diff --git a/CMakeLists.txt b/CMakeLists.txt index 50b8e39a..976ad8f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ apply_project_settings() include_directories( "${ENGINE_SOURCE_DIR}" ) include_directories( "${ENGINE_SOURCE_DIR}/public" ) -include_directories( "${ENGINE_SOURCE_DIR}/thirdparty" ) +include_directories( "${THIRDPARTY_SOURCE_DIR}" ) # Include the subdirectories that contain the individual projects add_subdirectory( "${ENGINE_SOURCE_DIR}" ) diff --git a/README.md b/README.md index ed1493c0..313e9e70 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,6 @@ which are located in `\platform\cfg\startup_*.cfg`. This is not a cheat or hack; attempting to use the SDK on the live version of the game could result in a permanent account ban.
The supported game versions are: - * S0 `R5pc_r5launch_J1557_CL387233_2019_01_28_07_43_PM`. - * S0 `R5pc_r5launch_J1624A_CL394493_2019_02_24_09_29_PM`. - * S1 `R5pc_r5launch_N52A_CL399039_2019_03_12_03_21_PM`. - * S2 `R5pc_r5launch_N428_CL436418_2019_08_07_09_35_PM`. * S3 `R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM`. ## Pylon [DISCLAIMER] diff --git a/license/thirdpartylegalnotices.txt b/license/thirdpartylegalnotices.txt index 38f43ec8..3f00d2c7 100644 --- a/license/thirdpartylegalnotices.txt +++ b/license/thirdpartylegalnotices.txt @@ -29,6 +29,47 @@ Microsoft Detours // SOFTWARE. //////////////////////////////////////////////////////////////////////////// +************************************************************************************ +NVIDIA NvAPI +************************************************************************************ + + // Copyright © 2012 NVIDIA Corporation. All rights reserved. + // + // NOTICE TO USER: + // + // This software is subject to NVIDIA ownership rights under U.S. + // and international Copyright laws. + // + // This software and the information contained herein are PROPRIETARY and + // CONFIDENTIAL to NVIDIA and are being provided solely under the terms and + // conditions of an NVIDIA software license agreement. + // Otherwise, you have no rights to use or access this software in any manner. + // + // If not covered by the applicable NVIDIA software license agreement: + // NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY + // PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY + // KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + // INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, AND + // FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY + // SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. + // + // U.S. Government End Users. + // This software is a "commercial item" as that term is defined at + // 48 C.F.R. 2.101 (OCT 1995), consisting of "commercial computer software" and + // "commercial computer software documentation" as such terms are used in + // 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Government only as a + // commercial end item. Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 + // through 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the + // software with only those rights set forth herein. + // + // Any use of this software in individual and commercial software must include, + // in the user documentation and internal comments to the code, + // the above Disclaimer (as applicable) and U.S. Government End Users Notice. + //////////////////////////////////////////////////////////////////////////// + ************************************************************************************ Recast & Detour ************************************************************************************ @@ -96,7 +137,7 @@ Dear ImGui // The MIT License (MIT) // - // Copyright (c) 2014-2022 Omar Cornut + // Copyright (c) 2014-2024 Omar Cornut // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -286,30 +327,22 @@ VDF Parser //////////////////////////////////////////////////////////////////////////// ************************************************************************************ -Nlohmann JSON +RapidJSON ************************************************************************************ - // MIT License + // Tencent is pleased to support the open source community by making RapidJSON available. // - // Copyright (c) 2013-2022 Niels Lohmann + // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. // - // Permission is hereby granted, free of charge, to any person obtaining a copy - // of this software and associated documentation files (the "Software"), to deal - // in the Software without restriction, including without limitation the rights - // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - // copies of the Software, and to permit persons to whom the Software is - // furnished to do so, subject to the following conditions: + // Licensed under the MIT License (the "License"); you may not use this file except + // in compliance with the License. You may obtain a copy of the License at // - // The above copyright notice and this permission notice shall be included in all - // copies or substantial portions of the Software. + // http://opensource.org/licenses/MIT // - // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - // SOFTWARE. + // Unless required by applicable law or agreed to in writing, software distributed + // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + // CONDITIONS OF ANY KIND, either express or implied. See the License for the + // specific language governing permissions and limitations under the License. //////////////////////////////////////////////////////////////////////////// ************************************************************************************ @@ -340,6 +373,396 @@ Curl // in this Software without prior written authorization of the copyright holder. //////////////////////////////////////////////////////////////////////////// +************************************************************************************ +Mbed TLS & l8w8jwt +************************************************************************************ + + // Apache License + // Version 2.0, January 2004 + // http://www.apache.org/licenses/ + // + // TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + // + // 1. Definitions. + // + // "License" shall mean the terms and conditions for use, reproduction, + // and distribution as defined by Sections 1 through 9 of this document. + // + // "Licensor" shall mean the copyright owner or entity authorized by + // the copyright owner that is granting the License. + // + // "Legal Entity" shall mean the union of the acting entity and all + // other entities that control, are controlled by, or are under common + // control with that entity. For the purposes of this definition, + // "control" means (i) the power, direct or indirect, to cause the + // direction or management of such entity, whether by contract or + // otherwise, or (ii) ownership of fifty percent (50%) or more of the + // outstanding shares, or (iii) beneficial ownership of such entity. + // + // "You" (or "Your") shall mean an individual or Legal Entity + // exercising permissions granted by this License. + // + // "Source" form shall mean the preferred form for making modifications, + // including but not limited to software source code, documentation + // source, and configuration files. + // + // "Object" form shall mean any form resulting from mechanical + // transformation or translation of a Source form, including but + // not limited to compiled object code, generated documentation, + // and conversions to other media types. + // + // "Work" shall mean the work of authorship, whether in Source or + // Object form, made available under the License, as indicated by a + // copyright notice that is included in or attached to the work + // (an example is provided in the Appendix below). + // + // "Derivative Works" shall mean any work, whether in Source or Object + // form, that is based on (or derived from) the Work and for which the + // editorial revisions, annotations, elaborations, or other modifications + // represent, as a whole, an original work of authorship. For the purposes + // of this License, Derivative Works shall not include works that remain + // separable from, or merely link (or bind by name) to the interfaces of, + // the Work and Derivative Works thereof. + // + // "Contribution" shall mean any work of authorship, including + // the original version of the Work and any modifications or additions + // to that Work or Derivative Works thereof, that is intentionally + // submitted to Licensor for inclusion in the Work by the copyright owner + // or by an individual or Legal Entity authorized to submit on behalf of + // the copyright owner. For the purposes of this definition, "submitted" + // means any form of electronic, verbal, or written communication sent + // to the Licensor or its representatives, including but not limited to + // communication on electronic mailing lists, source code control systems, + // and issue tracking systems that are managed by, or on behalf of, the + // Licensor for the purpose of discussing and improving the Work, but + // excluding communication that is conspicuously marked or otherwise + // designated in writing by the copyright owner as "Not a Contribution." + // + // "Contributor" shall mean Licensor and any individual or Legal Entity + // on behalf of whom a Contribution has been received by Licensor and + // subsequently incorporated within the Work. + // + // 2. Grant of Copyright License. Subject to the terms and conditions of + // this License, each Contributor hereby grants to You a perpetual, + // worldwide, non-exclusive, no-charge, royalty-free, irrevocable + // copyright license to reproduce, prepare Derivative Works of, + // publicly display, publicly perform, sublicense, and distribute the + // Work and such Derivative Works in Source or Object form. + // + // 3. Grant of Patent License. Subject to the terms and conditions of + // this License, each Contributor hereby grants to You a perpetual, + // worldwide, non-exclusive, no-charge, royalty-free, irrevocable + // (except as stated in this section) patent license to make, have made, + // use, offer to sell, sell, import, and otherwise transfer the Work, + // where such license applies only to those patent claims licensable + // by such Contributor that are necessarily infringed by their + // Contribution(s) alone or by combination of their Contribution(s) + // with the Work to which such Contribution(s) was submitted. If You + // institute patent litigation against any entity (including a + // cross-claim or counterclaim in a lawsuit) alleging that the Work + // or a Contribution incorporated within the Work constitutes direct + // or contributory patent infringement, then any patent licenses + // granted to You under this License for that Work shall terminate + // as of the date such litigation is filed. + // + // 4. Redistribution. You may reproduce and distribute copies of the + // Work or Derivative Works thereof in any medium, with or without + // modifications, and in Source or Object form, provided that You + // meet the following conditions: + // + // (a) You must give any other recipients of the Work or + // Derivative Works a copy of this License; and + // + // (b) You must cause any modified files to carry prominent notices + // stating that You changed the files; and + // + // (c) You must retain, in the Source form of any Derivative Works + // that You distribute, all copyright, patent, trademark, and + // attribution notices from the Source form of the Work, + // excluding those notices that do not pertain to any part of + // the Derivative Works; and + // + // (d) If the Work includes a "NOTICE" text file as part of its + // distribution, then any Derivative Works that You distribute must + // include a readable copy of the attribution notices contained + // within such NOTICE file, excluding those notices that do not + // pertain to any part of the Derivative Works, in at least one + // of the following places: within a NOTICE text file distributed + // as part of the Derivative Works; within the Source form or + // documentation, if provided along with the Derivative Works; or, + // within a display generated by the Derivative Works, if and + // wherever such third-party notices normally appear. The contents + // of the NOTICE file are for informational purposes only and + // do not modify the License. You may add Your own attribution + // notices within Derivative Works that You distribute, alongside + // or as an addendum to the NOTICE text from the Work, provided + // that such additional attribution notices cannot be construed + // as modifying the License. + // + // You may add Your own copyright statement to Your modifications and + // may provide additional or different license terms and conditions + // for use, reproduction, or distribution of Your modifications, or + // for any such Derivative Works as a whole, provided Your use, + // reproduction, and distribution of the Work otherwise complies with + // the conditions stated in this License. + // + // 5. Submission of Contributions. Unless You explicitly state otherwise, + // any Contribution intentionally submitted for inclusion in the Work + // by You to the Licensor shall be under the terms and conditions of + // this License, without any additional terms or conditions. + // Notwithstanding the above, nothing herein shall supersede or modify + // the terms of any separate license agreement you may have executed + // with Licensor regarding such Contributions. + // + // 6. Trademarks. This License does not grant permission to use the trade + // names, trademarks, service marks, or product names of the Licensor, + // except as required for reasonable and customary use in describing the + // origin of the Work and reproducing the content of the NOTICE file. + // + // 7. Disclaimer of Warranty. Unless required by applicable law or + // agreed to in writing, Licensor provides the Work (and each + // Contributor provides its Contributions) on an "AS IS" BASIS, + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + // implied, including, without limitation, any warranties or conditions + // of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + // PARTICULAR PURPOSE. You are solely responsible for determining the + // appropriateness of using or redistributing the Work and assume any + // risks associated with Your exercise of permissions under this License. + // + // 8. Limitation of Liability. In no event and under no legal theory, + // whether in tort (including negligence), contract, or otherwise, + // unless required by applicable law (such as deliberate and grossly + // negligent acts) or agreed to in writing, shall any Contributor be + // liable to You for damages, including any direct, indirect, special, + // incidental, or consequential damages of any character arising as a + // result of this License or out of the use or inability to use the + // Work (including but not limited to damages for loss of goodwill, + // work stoppage, computer failure or malfunction, or any and all + // other commercial damages or losses), even if such Contributor + // has been advised of the possibility of such damages. + // + // 9. Accepting Warranty or Additional Liability. While redistributing + // the Work or Derivative Works thereof, You may choose to offer, + // and charge a fee for, acceptance of support, warranty, indemnity, + // or other liability obligations and/or rights consistent with this + // License. However, in accepting such obligations, You may act only + // on Your own behalf and on Your sole responsibility, not on behalf + // of any other Contributor, and only if You agree to indemnify, + // defend, and hold each Contributor harmless for any liability + // incurred by, or claims asserted against, such Contributor by reason + // of your accepting any such warranty or additional liability. + // + // END OF TERMS AND CONDITIONS + // + // APPENDIX: How to apply the Apache License to your work. + // + // To apply the Apache License to your work, attach the following + // boilerplate notice, with the fields enclosed by brackets "[]" + // replaced with your own identifying information. (Don't include + // the brackets!) The text should be enclosed in the appropriate + // comment syntax for the file format. We also recommend that a + // file or class name and description of purpose be included on the + // same "printed page" as the copyright notice for easier + // identification within third-party archives. + // + // Copyright [yyyy] [name of copyright owner] + // + // Licensed under the Apache License, Version 2.0 (the "License"); + // you may not use this file except in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, software + // distributed under the License is distributed on an "AS IS" BASIS, + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + // See the License for the specific language governing permissions and + // limitations under the License. + //////////////////////////////////////////////////////////////////////////// + +************************************************************************************ +DirtySDK (EA WebKit) +************************************************************************************ + + // Copyright (C) 1999-2007, 2009-2010, 2012-2013 Electronic Arts Inc + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions + // are met: + // + // 1. Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // 2. Redistributions in binary form must reproduce the above copyright + // notice, this list of conditions and the following disclaimer in the + // documentation and/or other materials provided with the distribution. + // 3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of + // its contributors may be used to endorse or promote products derived + // from this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY + // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + // DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY + // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + //////////////////////////////////////////////////////////////////////////// + +************************************************************************************ +EAThread (EA WebKit) +************************************************************************************ + + //-------------------------------------------------------------------------- + // Copyright (C) 2017 Electronic Arts Inc. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions + // are met: + // + // 1. Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // 2. Redistributions in binary form must reproduce the above copyright + // notice, this list of conditions and the following disclaimer in the + // documentation and/or other materials provided with the distribution. + // 3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of + // its contributors may be used to endorse or promote products derived + // from this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY + // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + // DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY + // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + //-------------------------------------------------------------------------- + // + // Additional licenses also apply to this software package as detailed below. + // + //-------------------------------------------------------------------------- + // Copyright (c) 2015 Jeff Preshing + // + // This software is provided 'as-is', without any express or implied + // warranty. In no event will the authors be held liable for any damages + // arising from the use of this software. + // + // Permission is granted to anyone to use this software for any purpose, + // including commercial applications, and to alter it and redistribute it + // freely, subject to the following restrictions: + // + // 1. The origin of this software must not be misrepresented; you must not + // claim that you wrote the original software. If you use this software + // in a product, an acknowledgement in the product documentation would be + // appreciated but is not required. + // 2. Altered source versions must be plainly marked as such, and must not be + // misrepresented as being the original software. + // 3. This notice may not be removed or altered from any source distribution. + //-------------------------------------------------------------------------- + //////////////////////////////////////////////////////////////////////////// + +************************************************************************************ +EABase (EA WebKit) +************************************************************************************ + + // Copyright (C) 2002-2013 Electronic Arts Inc + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions + // are met: + // + // 1. Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // 2. Redistributions in binary form must reproduce the above copyright + // notice, this list of conditions and the following disclaimer in the + // documentation and/or other materials provided with the distribution. + // 3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of + // its contributors may be used to endorse or promote products derived + // from this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY + // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + // DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY + // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + //////////////////////////////////////////////////////////////////////////// + +************************************************************************************ +Zstandard +************************************************************************************ + + // BSD License + // + // For Zstandard software + // + // Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistributions of source code must retain the above copyright notice, this + // list of conditions and the following disclaimer. + // + // * Redistributions in binary form must reproduce the above copyright notice, + // this list of conditions and the following disclaimer in the documentation + // and/or other materials provided with the distribution. + // + // * Neither the name Facebook, nor Meta, nor the names of its contributors may + // be used to endorse or promote products derived from this software without + // specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + //////////////////////////////////////////////////////////////////////////// + +************************************************************************************ +LZ4 +************************************************************************************ + + // LZ4 Library + // Copyright (c) 2011-2020, Yann Collet + // All rights reserved. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistributions of source code must retain the above copyright notice, this + // list of conditions and the following disclaimer. + // + // * Redistributions in binary form must reproduce the above copyright notice, this + // list of conditions and the following disclaimer in the documentation and/or + // other materials provided with the distribution. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + //////////////////////////////////////////////////////////////////////////// + ************************************************************************************ LZHAM ************************************************************************************ @@ -365,46 +788,6 @@ LZHAM // THE SOFTWARE. //////////////////////////////////////////////////////////////////////////// -************************************************************************************ -SHA256 -************************************************************************************ - - // Updated to C++, zedwood.com 2012 - // Based on Olivier Gay's version - // See Modified BSD License below: - // - // FIPS 180-2 SHA-224/256/384/512 implementation - // Issue date: 04/30/2005 - // http://www.ouah.org/ogay/sha2/ - // - // Copyright (C) 2005, 2007 Olivier Gay - // All rights reserved. - // - // Redistribution and use in source and binary forms, with or without - // modification, are permitted provided that the following conditions - // are met: - // 1. Redistributions of source code must retain the above copyright - // notice, this list of conditions and the following disclaimer. - // 2. Redistributions in binary form must reproduce the above copyright - // notice, this list of conditions and the following disclaimer in the - // documentation and/or other materials provided with the distribution. - // 3. Neither the name of the project nor the names of its contributors - // may be used to endorse or promote products derived from this software - // without specific prior written permission. - // - // THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND - // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - // ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE - // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - // SUCH DAMAGE. - //////////////////////////////////////////////////////////////////////////// - ************************************************************************************ CRC32 ************************************************************************************ diff --git a/r5dev/CMakeLists.txt b/r5dev/CMakeLists.txt index 9fac8202..e129ac9e 100644 --- a/r5dev/CMakeLists.txt +++ b/r5dev/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory( vstdlib ) add_subdirectory( vphysics ) add_subdirectory( ebisusdk ) add_subdirectory( codecs ) +add_subdirectory( geforce ) set( FOLDER_CONTEXT "Protocols" ) add_subdirectory( protoc ) @@ -23,31 +24,43 @@ set( FOLDER_CONTEXT "Respawn" ) add_subdirectory( rtech ) set( FOLDER_CONTEXT "Thirdparty" ) -add_subdirectory( thirdparty/cppnet ) -add_subdirectory( thirdparty/curl ) -add_subdirectory( thirdparty/sdl ) -add_subdirectory( thirdparty/imgui ) add_subdirectory( thirdparty/spdlog ) + +set( FOLDER_CONTEXT "Thirdparty/Recast" ) +add_subdirectory( thirdparty/recast ) +add_subdirectory( thirdparty/detours ) + +set( FOLDER_CONTEXT "Thirdparty/Compression" ) add_subdirectory( thirdparty/lzham ) add_subdirectory( thirdparty/fastlz ) add_subdirectory( thirdparty/bzip2 ) add_subdirectory( thirdparty/zlib ) -add_subdirectory( thirdparty/lzma ) +add_subdirectory( thirdparty/zstd ) +add_subdirectory( thirdparty/lz4 ) add_subdirectory( thirdparty/zip ) -set( FOLDER_CONTEXT "Thirdparty/Recast" ) -add_subdirectory( thirdparty/recast ) +set( FOLDER_CONTEXT "Thirdparty/Security" ) +add_subdirectory( thirdparty/mbedtls ) +add_subdirectory( thirdparty/jwt ) -set( FOLDER_CONTEXT "Thirdparty/Microsoft" ) -add_subdirectory( thirdparty/detours ) +set( FOLDER_CONTEXT "Thirdparty/Multimedia" ) +add_subdirectory( thirdparty/sdl ) +add_subdirectory( thirdparty/imgui ) +add_subdirectory( thirdparty/cppnet ) -set( FOLDER_CONTEXT "Thirdparty/Google" ) +set( FOLDER_CONTEXT "Thirdparty/Networking" ) add_subdirectory( thirdparty/protobuf ) +add_subdirectory( thirdparty/curl ) +add_subdirectory( thirdparty/dirtysdk ) + +set( FOLDER_CONTEXT "Thirdparty/Threading" ) +add_subdirectory( thirdparty/ea/EAThread ) set( FOLDER_CONTEXT "Tools" ) add_subdirectory( sdklauncher ) add_subdirectory( netconsole ) add_subdirectory( naveditor ) +add_subdirectory( revpk ) set( FOLDER_CONTEXT "System" ) add_subdirectory( networksystem ) diff --git a/r5dev/appframework/IAppSystemGroup.cpp b/r5dev/appframework/IAppSystemGroup.cpp index d5b3e1df..0158bf8f 100644 --- a/r5dev/appframework/IAppSystemGroup.cpp +++ b/r5dev/appframework/IAppSystemGroup.cpp @@ -14,7 +14,7 @@ //----------------------------------------------------------------------------- void CAppSystemGroup::StaticDestroy(CAppSystemGroup* pModAppSystemGroup) { - CAppSystemGroup_Destroy(pModAppSystemGroup); + CAppSystemGroup__Destroy(pModAppSystemGroup); } //----------------------------------------------------------------------------- @@ -25,11 +25,48 @@ CAppSystemGroup::AppSystemGroupStage_t CAppSystemGroup::GetCurrentStage() const return m_nCurrentStage; } -void VAppSystemGroup::Attach(void) const +//----------------------------------------------------------------------------- +// Methods to find various global singleton systems +//----------------------------------------------------------------------------- +void* CAppSystemGroup::FindSystem(const char* pSystemName) { - DetourAttach(&CAppSystemGroup_Destroy, &CAppSystemGroup::StaticDestroy); + unsigned short i = m_SystemDict.Find(pSystemName); + if (i != m_SystemDict.InvalidIndex()) + return m_Systems[m_SystemDict[i]]; + + // If it's not an interface we know about, it could be an older + // version of an interface, or maybe something implemented by + // one of the instantiated interfaces... + + // QUESTION: What order should we iterate this in? + // It controls who wins if multiple ones implement the same interface + for (i = 0; i < m_Systems.Count(); ++i) + { + void* pInterface = m_Systems[i]->QueryInterface(pSystemName); + if (pInterface) + return pInterface; + } + + int nExternalCount = m_NonAppSystemFactories.Count(); + for (i = 0; i < nExternalCount; ++i) + { + void* pInterface = m_NonAppSystemFactories[i](pSystemName, NULL); + if (pInterface) + return pInterface; + } + + if (m_pParentAppSystem) + { + void* pInterface = m_pParentAppSystem->FindSystem(pSystemName); + if (pInterface) + return pInterface; + } + + // No dice.. + return NULL; } -void VAppSystemGroup::Detach(void) const + +void VAppSystemGroup::Detour(const bool bAttach) const { - DetourDetach(&CAppSystemGroup_Destroy, &CAppSystemGroup::StaticDestroy); -} \ No newline at end of file + DetourSetup(&CAppSystemGroup__Destroy, &CAppSystemGroup::StaticDestroy, bAttach); +} diff --git a/r5dev/cmake/Macros.cmake b/r5dev/cmake/Macros.cmake index 5b29f95a..0f395b36 100644 --- a/r5dev/cmake/Macros.cmake +++ b/r5dev/cmake/Macros.cmake @@ -51,14 +51,8 @@ macro( add_module MODULE_TYPE MODULE_NAME REUSE_PCH FOLDER_NAME WARNINGS_AS_ERRO add_library( ${PROJECT_NAME} ) elseif( ${MODULE_TYPE} STREQUAL "shared_lib" ) add_library( ${PROJECT_NAME} SHARED ) - target_link_options( ${PROJECT_NAME} PRIVATE - "$<$:/LTCG>" - ) elseif( ${MODULE_TYPE} STREQUAL "exe" ) add_executable( ${PROJECT_NAME} ) - target_link_options( ${PROJECT_NAME} PRIVATE - "$<$:/LTCG>" - ) else() message( FATAL_ERROR "Invalid module type: ${MODULE_TYPE}; expected 'lib', 'shared_lib', or 'exe'." ) endif() @@ -69,7 +63,7 @@ macro( add_module MODULE_TYPE MODULE_NAME REUSE_PCH FOLDER_NAME WARNINGS_AS_ERRO set_target_properties( ${MODULE_NAME} PROPERTIES FOLDER ${FOLDER_NAME} ) - if( ${GLOBAL_WARNINGS_AS_ERRORS} ) + if( ${OPTION_WARNINGS_AS_ERRORS} ) warnings_as_errors( ${PROJECT_NAME} ${WARNINGS_AS_ERRORS} ) endif() @@ -80,6 +74,7 @@ macro( add_module MODULE_TYPE MODULE_NAME REUSE_PCH FOLDER_NAME WARNINGS_AS_ERRO $<$,$>:/Ot> $<$,$>:/GS-> $<$,$>:/Gy> + $<$,$>:/GT> $<$,$>:/fp:fast> ) endif() @@ -101,12 +96,13 @@ macro( define_compiler_variables ) endmacro() # ----------------------------------------------------------------------------- -# Apply whole program optimization for this target in release ( !slow! ) +# Apply whole program optimization for this target in release and profile ( !slow! ) # ----------------------------------------------------------------------------- macro( whole_program_optimization ) - target_compile_options( ${PROJECT_NAME} PRIVATE - $<$:/GL> - ) + if( ${OPTION_LTCG_MODE} STREQUAL "ON" ) + set_property( TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE) + set_property( TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION_PROFILE TRUE) + endif() endmacro() # ----------------------------------------------------------------------------- @@ -134,18 +130,25 @@ endmacro() macro( thirdparty_suppress_warnings ) if( MSVC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) target_compile_options( ${PROJECT_NAME} PRIVATE + /wd4057 # 'function': 'int *' differs in indirection to slightly different base types from 'unsigned int [4]' /wd4100 # Unreferenced formal parameter. - /wd4131 # Using old-style declarations + /wd4131 # Using old-style declarations. /wd4152 # Function/data pointer conversion in expression. /wd4200 # Zero-sized array in union; SDL2 uses this for compiler compatibility. /wd4201 # Nameless struct/union. + /wd4204 # nonstandard extension used: non-constant aggregate initializer. + /wd4221 # nonstandard extension used: 'value': cannot be initialized using address of automatic variable 'symbol' /wd4244 # Type conversion truncation; protobuf has many, but this appears intentional. + /wd4245 # 'return': conversion signed/unsigned mismatch /wd4267 # Type conversion truncation; protobuf has many, but this appears intentional. + /wd4295 # Array is too small to include terminating null character. /wd4307 # Integral constant overflow. /wd4389 # Signed/unsigned mismatch. /wd4456 # Declaration hides previous local declaration. /wd4457 # Declaration hides function parameter. /wd4505 # Unreferenced local function has been removed. + /wd4701 # potentially uninitialized local variable. + /wd4702 # Unreachable code. ) endif() warnings_as_errors( ${PROJECT_NAME} FALSE ) diff --git a/r5dev/cmake/Options.cmake b/r5dev/cmake/Options.cmake index 3570c20f..ada74d78 100644 --- a/r5dev/cmake/Options.cmake +++ b/r5dev/cmake/Options.cmake @@ -20,27 +20,52 @@ macro( apply_project_settings ) ) # Some thirdparty code have Warnings as Errors disabled; this option won't override those. - option( GLOBAL_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON ) - option( ENABLE_LTCG "Enable link-time code generation (significantly increases compile times)" OFF ) + option( OPTION_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" ON ) - set( GAMEDLL_OPTION "GAMEDLL_S3" CACHE STRING "Game DLL version" ) - set_property( CACHE GAMEDLL_OPTION PROPERTY STRINGS - "GAMEDLL_S0" - "GAMEDLL_S1" - "GAMEDLL_S2" - "GAMEDLL_S3" + set( OPTION_LTCG_MODE "OFF" CACHE STRING "Enables link-time code generation (significantly increases compile times)" ) + set_property( CACHE OPTION_LTCG_MODE PROPERTY STRINGS + "OFF" + "ON" # Only on projects that specified LTCG + "ALL" # All projects, whether or not LTCG was specified ) + option( OPTION_CERTAIN "This build is certain; debug statements (such as DevMsg(...)) will NOT be compiled" OFF ) + option( OPTION_RETAIL "This build is retail; enable this among with 'OPTION_CERTAIN' to form a release build" OFF ) + # Set common defines add_compile_definitions( "_CRT_SECURE_NO_WARNINGS" "SPDLOG_COMPILED_LIB" "SPDLOG_NO_EXCEPTIONS" "CURL_STATICLIB" - "PLATFORM_64BITS" # Target is 64bits only. - "${GAMEDLL_OPTION}" + + # Must be explicitly defined to toggle SIMD optimizations for RapidJSON. + # Don't set this to anything higher than SSE2, as the game supports from + # SSE3 and higher, and the next level of optimizations in RapidJSON is SSE4.2. + "RAPIDJSON_SSE2" + + # Use iterative parsing to protect against stack overflows in rare cases; see: + # https://rapidjson.org/md_doc_features.html + # https://github.com/Tencent/rapidjson/issues/1227 + # https://github.com/Tencent/rapidjson/issues/2260 + "RAPIDJSON_PARSE_DEFAULT_FLAGS=kParseIterativeFlag|kParseValidateEncodingFlag" + + # Target is 64bits only. + "PLATFORM_64BITS" ) + if( ${OPTION_CERTAIN} ) + add_compile_definitions( + "_CERT" + ) + endif() + + if( ${OPTION_RETAIL} ) + add_compile_definitions( + "_RETAIL" + ) + endif() + # Set settings for Debug configuration add_compile_options( $<$,$>:/MTd> @@ -61,11 +86,9 @@ macro( apply_project_settings ) $<$,$>:/EHsc> ) - if( ${ENABLE_LTCG} ) - add_compile_options( - $<$,$>:/GL> - $<$,$>:/GL> - ) + if( ${OPTION_LTCG_MODE} STREQUAL "ALL" ) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_PROFILE ON) endif() set( CMAKE_EXE_LINKER_FLAGS_RELEASE @@ -81,9 +104,9 @@ macro( apply_project_settings ) include_directories( "${ENGINE_SOURCE_DIR}/" "${ENGINE_SOURCE_DIR}/public/" - "${ENGINE_SOURCE_DIR}/thirdparty/" - "${ENGINE_SOURCE_DIR}/thirdparty/imgui/" - "${ENGINE_SOURCE_DIR}/thirdparty/recast/" + "${THIRDPARTY_SOURCE_DIR}/" + "${THIRDPARTY_SOURCE_DIR}/imgui/" + "${THIRDPARTY_SOURCE_DIR}/recast/" ) endmacro() diff --git a/r5dev/codecs/Miles/miles_impl.cpp b/r5dev/codecs/Miles/miles_impl.cpp index 08796c48..9de48fa9 100644 --- a/r5dev/codecs/Miles/miles_impl.cpp +++ b/r5dev/codecs/Miles/miles_impl.cpp @@ -1,7 +1,11 @@ #include "core/stdafx.h" #include "miles_impl.h" #include "tier0/fasttimer.h" +#include "tier0/commandline.h" #include "tier1/cvar.h" +#include "filesystem/filesystem.h" + +static ConVar miles_debug("miles_debug", "0", FCVAR_RELEASE, "Enables debug prints for the Miles Sound System", "1 = print; 0 (zero) = no print"); //----------------------------------------------------------------------------- // Purpose: logs debug output emitted from the Miles Sound System @@ -10,7 +14,7 @@ //----------------------------------------------------------------------------- void AIL_LogFunc(int64_t nLogLevel, const char* pszMessage) { - DevMsg(eDLL_T::AUDIO, "%s\n", pszMessage); + Msg(eDLL_T::AUDIO, "%s\n", pszMessage); v_AIL_LogFunc(nLogLevel, pszMessage); } @@ -22,48 +26,93 @@ bool Miles_Initialize() { const char* pszLanguage = miles_language->GetString(); if (!pszLanguage[0]) - { pszLanguage = MILES_DEFAULT_LANGUAGE; + + const bool isEnglishLanguage = _stricmp(pszLanguage, "english") == 0; + + if (!isEnglishLanguage) + { + const bool useShipSound = !CommandLine()->FindParm("-devsound") || CommandLine()->FindParm("-shipsound"); + + const std::string baseStreamFilePath = Format("%s/general_%s.mstr", useShipSound ? "audio/ship" : "audio/dev", pszLanguage); + + // if the requested language for miles does not have a MSTR file present, throw a non-fatal error and force english as a fallback + // if we are loading english and the file is still not found, we can let it hit the regular engine error, since that is not recoverable + if (!FileSystem()->FileExists(baseStreamFilePath.c_str())) + { + Error(eDLL_T::AUDIO, NO_ERROR, "%s: attempted to load language '%s' but the required streaming source file (%s) was not found. falling back to english...\n", __FUNCTION__, pszLanguage, baseStreamFilePath.c_str()); + + pszLanguage = MILES_DEFAULT_LANGUAGE; + miles_language->SetValue(pszLanguage); + } } - DevMsg(eDLL_T::AUDIO, "%s: initializing MSS with language: '%s'\n", __FUNCTION__, pszLanguage); + Msg(eDLL_T::AUDIO, "%s: initializing MSS with language: '%s'\n", __FUNCTION__, pszLanguage); CFastTimer initTimer; initTimer.Start(); bool bResult = v_Miles_Initialize(); initTimer.End(); - DevMsg(eDLL_T::AUDIO, "%s: %s ('%f' seconds)\n", __FUNCTION__, bResult ? "success" : "failure", initTimer.GetDuration().GetSeconds()); + Msg(eDLL_T::AUDIO, "%s: %s ('%f' seconds)\n", __FUNCTION__, bResult ? "success" : "failure", initTimer.GetDuration().GetSeconds()); return bResult; } void MilesQueueEventRun(Miles::Queue* queue, const char* eventName) { - if(miles_debug->GetBool()) - DevMsg(eDLL_T::AUDIO, "%s: running event: '%s'\n", __FUNCTION__, eventName); + if(miles_debug.GetBool()) + Msg(eDLL_T::AUDIO, "%s: running event: '%s'\n", __FUNCTION__, eventName); v_MilesQueueEventRun(queue, eventName); } void MilesBankPatch(Miles::Bank* bank, char* streamPatch, char* localizedStreamPatch) { - // TODO [REXX]: add print for patch loading when Miles::Bank struct is mapped out a bit better with file name + if (miles_debug.GetBool()) + { + Msg(eDLL_T::AUDIO, + "%s: patching bank \"%s\". stream patches: \"%s\", \"%s\"\n", + __FUNCTION__, + bank->GetBankName(), + V_UnqualifiedFileName(streamPatch), V_UnqualifiedFileName(localizedStreamPatch) + ); + } + + const Miles::BankHeader_t* header = bank->GetHeader(); + + if (header->bankIndex >= header->project->bankCount) + Error(eDLL_T::AUDIO, EXIT_FAILURE, + "%s: Attempted to patch bank '%s' that identified itself as bank idx %i.\nProject expects a highest index of %i\n", + __FUNCTION__, + bank->GetBankName(), + header->bankIndex, + header->project->bankCount - 1 + ); + v_MilesBankPatch(bank, streamPatch, localizedStreamPatch); } -/////////////////////////////////////////////////////////////////////////////// -void MilesCore::Attach() const +void CSOM_AddEventToQueue(const char* eventName) { - DetourAttach(&v_AIL_LogFunc, &AIL_LogFunc); - DetourAttach(&v_Miles_Initialize, &Miles_Initialize); - DetourAttach(&v_MilesQueueEventRun, &MilesQueueEventRun); - DetourAttach(&v_MilesBankPatch, &MilesBankPatch); -} + if (miles_debug.GetBool()) + Msg(eDLL_T::AUDIO, "%s: queuing audio event '%s'\n", __FUNCTION__, eventName); -void MilesCore::Detach() const + v_CSOM_AddEventToQueue(eventName); + + if (g_milesGlobals->queuedEventHash == 1) + Warning(eDLL_T::AUDIO, "%s: failed to add event to queue; invalid event name '%s'\n", __FUNCTION__, eventName); + + if (g_milesGlobals->queuedEventHash == 2) + Warning(eDLL_T::AUDIO, "%s: failed to add event to queue; event '%s' not found.\n", __FUNCTION__, eventName); +}; + + +/////////////////////////////////////////////////////////////////////////////// +void MilesCore::Detour(const bool bAttach) const { - DetourDetach(&v_AIL_LogFunc, &AIL_LogFunc); - DetourDetach(&v_Miles_Initialize, &Miles_Initialize); - DetourDetach(&v_MilesQueueEventRun, &MilesQueueEventRun); - DetourDetach(&v_MilesBankPatch, &MilesBankPatch); -} \ No newline at end of file + DetourSetup(&v_AIL_LogFunc, &AIL_LogFunc, bAttach); + DetourSetup(&v_Miles_Initialize, &Miles_Initialize, bAttach); + DetourSetup(&v_MilesQueueEventRun, &MilesQueueEventRun, bAttach); + DetourSetup(&v_MilesBankPatch, &MilesBankPatch, bAttach); + DetourSetup(&v_CSOM_AddEventToQueue, &CSOM_AddEventToQueue, bAttach); +} diff --git a/r5dev/codecs/Miles/miles_impl.h b/r5dev/codecs/Miles/miles_impl.h index 1a34ac37..74c4483c 100644 --- a/r5dev/codecs/Miles/miles_impl.h +++ b/r5dev/codecs/Miles/miles_impl.h @@ -2,48 +2,76 @@ #include "miles_types.h" /* ==== WASAPI THREAD SERVICE =========================================================================================================================================== */ -inline CMemory p_AIL_LogFunc; inline void(*v_AIL_LogFunc)(int64_t nLogLevel, const char* pszMessage); - -inline CMemory p_Miles_Initialize; inline bool(*v_Miles_Initialize)(); - -inline CMemory p_MilesQueueEventRun; inline void(*v_MilesQueueEventRun)(Miles::Queue*, const char*); - -inline CMemory p_MilesBankPatch; inline void(*v_MilesBankPatch)(Miles::Bank*, char*, char*); +inline void(*v_CSOM_AddEventToQueue)(const char* eventName); + +struct MilesBankList_t +{ + char banks[64][16]; + int bankCount; +}; + +struct MilesGlobalState_t +{ + char gap0[24]; + bool mismatchedBuildTag; + char gap19[63]; + uintptr_t queuedEventHash; + char gap60[4]; + Vector3D queuedSoundPosition; + char gap70[24]; + float soundMasterVolume; + char gap8c[28]; + void* samplesXlogType; + char gapB0[8]; + void* dumpXlogType; + char gapC0[48]; + void* driver; + void* queue; + char gap100[40]; + MilesBankList_t bankList; + char gap52c[4]; + void* loadedBanks[16]; + char gap5b0[290448]; + HANDLE milesInitializedEvent; + HANDLE milesThread; + char gap47450[272]; + char milesOutputBuffer[1024]; + char unk[96]; +}; + +inline MilesGlobalState_t* g_milesGlobals; /////////////////////////////////////////////////////////////////////////////// class MilesCore : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("AIL_LogFunc", p_AIL_LogFunc.GetPtr()); - LogFunAdr("Miles_Initialize", p_Miles_Initialize.GetPtr()); + LogFunAdr("AIL_LogFunc", v_AIL_LogFunc); + LogFunAdr("Miles_Initialize", v_Miles_Initialize); + LogFunAdr("MilesQueueEventRun", v_MilesQueueEventRun); + LogFunAdr("MilesBankPatch", v_MilesBankPatch); + LogFunAdr("CSOM_AddEventToQueue", v_CSOM_AddEventToQueue); + LogVarAdr("g_milesGlobals", g_milesGlobals); } virtual void GetFun(void) const { - p_AIL_LogFunc = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B DA 48 8D 15 ?? ?? ?? ??"); - v_AIL_LogFunc = p_AIL_LogFunc.RCast(); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B DA 48 8D 15 ?? ?? ?? ??").GetPtr(v_AIL_LogFunc); + g_GameDll.FindPatternSIMD("0F B6 11 4C 8B C1").GetPtr(v_CSOM_AddEventToQueue); + + CMemory milesInitializeFunc = g_GameDll.FindPatternSIMD("40 53 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??"); + milesInitializeFunc.GetPtr(v_Miles_Initialize); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - p_Miles_Initialize = g_GameDll.FindPatternSIMD("40 53 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??"); -#else - p_Miles_Initialize = g_GameDll.FindPatternSIMD("40 55 53 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??"); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) - v_Miles_Initialize = p_Miles_Initialize.RCast(); - - p_MilesQueueEventRun = g_RadAudioSystemDll.GetExportedSymbol("MilesQueueEventRun"); - v_MilesQueueEventRun = p_MilesQueueEventRun.RCast(); - - p_MilesBankPatch = g_RadAudioSystemDll.GetExportedSymbol("MilesBankPatch"); - v_MilesBankPatch = p_MilesBankPatch.RCast(); + g_milesGlobals = milesInitializeFunc.FindPatternSelf("48 8D", CMemory::Direction::DOWN, 0x50).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_RadAudioSystemDll.GetExportedSymbol("MilesQueueEventRun").GetPtr(v_MilesQueueEventRun); + g_RadAudioSystemDll.GetExportedSymbol("MilesBankPatch").GetPtr(v_MilesBankPatch); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/codecs/Miles/miles_types.h b/r5dev/codecs/Miles/miles_types.h index bae8703a..3ac735e1 100644 --- a/r5dev/codecs/Miles/miles_types.h +++ b/r5dev/codecs/Miles/miles_types.h @@ -4,6 +4,9 @@ constexpr char MILES_DEFAULT_LANGUAGE[] = "english"; namespace Miles { + constexpr int TEMPLATEID_FLAG_SOURCE = 0x40000000; + + struct Queue { char gap0[0x8]; @@ -11,8 +14,107 @@ namespace Miles char gap10[0x20]; }; + struct BankHeader_t; + + struct Source_t + { + BankHeader_t* bank; // reserved on disk - written at runtime + char gap8[80]; + }; + + struct Event_t + { + int nameOffset; + int unkOffset; // offset into BankHeader_t::unk_68 data - some sort of event metadata? + }; + + // internal project data structure + struct IntProjectData_t + { + char gap0[0xCF0]; + int bankCount; + BankHeader_t** loadedBanks; + }; + + struct BankHeader_t + { + int magic; // 'CBNK' + int version; // 32 + uint32_t dataSize; + + int bankMagic; // 'BANK' + const char* bankName; + + void* unk_18; + IntProjectData_t* project; + + void* unk_28; + void* unk_30; + void* unk_38; + void* unk_40; // used to index into both sources and localised sources + + Source_t* sources; + Source_t* localizedSources; + + void* unk_58; + Event_t* events; + + void* unk_68; + const char* stringTable; + + void* unk_78; + void* unk_80; + void* unk_88; + + uint8_t bankIndex; + // 3 byte padding + + uint32_t localizedSourceCount; + uint32_t sourceCount; + + uint32_t patchCount; + uint32_t eventCount; + + uint32_t count_A4; + uint32_t count_A8; + uint32_t count_AC; + + uint32_t buildTag; + + uint32_t unk_B4; + uint32_t someDataSize; + + const char* GetBankName() const + { + return bankName; + } + + }; + static_assert(offsetof(BankHeader_t, project) == 0x20); + static_assert(offsetof(BankHeader_t, stringTable) == 0x70); + static_assert(offsetof(BankHeader_t, unk_B4) == 0xB4); + + struct Bank { - // TODO [REXX]: map out this struct and its internal counterpart + void* internalData; + void* unk_8; + + char* fileData; + + int unk_18; + char gap_1c[4]; + + const Miles::BankHeader_t* GetHeader() const + { + return reinterpret_cast(fileData); + } + + const char* GetBankName() const + { + return GetHeader()->GetBankName(); + } }; + + static_assert(sizeof(Bank) == 0x20); } \ No newline at end of file diff --git a/r5dev/codecs/Miles/radshal_wasapi.h b/r5dev/codecs/Miles/radshal_wasapi.h index a3578b59..d8056d02 100644 --- a/r5dev/codecs/Miles/radshal_wasapi.h +++ b/r5dev/codecs/Miles/radshal_wasapi.h @@ -8,7 +8,7 @@ class VRadShal : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("WASAPI_GetAudioDevice", p_WASAPI_GetAudioDevice.GetPtr()); + LogFunAdr("WASAPI_GetAudioDevice", (void*)p_WASAPI_GetAudioDevice.GetPtr()); } virtual void GetFun(void) const { @@ -17,7 +17,6 @@ class VRadShal : public IDetour } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/codecs/bink/bink_impl.cpp b/r5dev/codecs/bink/bink_impl.cpp index 64985a53..fba75138 100644 --- a/r5dev/codecs/bink/bink_impl.cpp +++ b/r5dev/codecs/bink/bink_impl.cpp @@ -20,12 +20,7 @@ void* BinkOpen(HANDLE hBinkFile, UINT32 nFlags) } /////////////////////////////////////////////////////////////////////////////// -void BinkCore::Attach() const +void BinkCore::Detour(const bool bAttach) const { - DetourAttach(&v_BinkOpen, &BinkOpen); + DetourSetup(&v_BinkOpen, &BinkOpen, bAttach); } - -void BinkCore::Detach() const -{ - DetourDetach(&v_BinkOpen, &BinkOpen); -} \ No newline at end of file diff --git a/r5dev/codecs/bink/bink_impl.h b/r5dev/codecs/bink/bink_impl.h index f122d494..ea103a4a 100644 --- a/r5dev/codecs/bink/bink_impl.h +++ b/r5dev/codecs/bink/bink_impl.h @@ -1,12 +1,7 @@ #pragma once -inline CMemory p_BinkOpen; inline void*(*v_BinkOpen)(HANDLE hBinkFile, UINT32 nFlags); - -inline CMemory p_BinkClose; inline void(*v_BinkClose)(HANDLE hBinkFile); - -inline CMemory p_BinkGetError; inline const char*(*v_BinkGetError)(void); /////////////////////////////////////////////////////////////////////////////// @@ -14,23 +9,18 @@ class BinkCore : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("BinkOpen", p_BinkOpen.GetPtr()); - LogFunAdr("BinkClose", p_BinkClose.GetPtr()); - LogFunAdr("BinkGetError", p_BinkGetError.GetPtr()); + LogFunAdr("BinkOpen", v_BinkOpen); + LogFunAdr("BinkClose", v_BinkClose); + LogFunAdr("BinkGetError", v_BinkGetError); } virtual void GetFun(void) const { - p_BinkOpen = g_RadVideoToolsDll.GetExportedSymbol("BinkOpen"); - v_BinkOpen = p_BinkOpen.RCast(); - p_BinkClose = g_RadVideoToolsDll.GetExportedSymbol("BinkClose"); - v_BinkClose = p_BinkClose.RCast(); - p_BinkGetError = g_RadVideoToolsDll.GetExportedSymbol("BinkGetError"); - v_BinkGetError = p_BinkGetError.RCast(); + g_RadVideoToolsDll.GetExportedSymbol("BinkOpen").GetPtr(v_BinkOpen); + g_RadVideoToolsDll.GetExportedSymbol("BinkClose").GetPtr(v_BinkClose); + g_RadVideoToolsDll.GetExportedSymbol("BinkGetError").GetPtr(v_BinkGetError); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// - diff --git a/r5dev/common/callback.cpp b/r5dev/common/callback.cpp index fa70fa75..8ea1e366 100644 --- a/r5dev/common/callback.cpp +++ b/r5dev/common/callback.cpp @@ -9,12 +9,14 @@ #include "windows/id3dx.h" #include "tier0/fasttimer.h" #include "tier1/cvar.h" +#include "tier1/fmtstr.h" #ifndef CLIENT_DLL #include "engine/server/sv_rcon.h" #endif // !CLIENT_DLL #ifndef DEDICATED #include "engine/client/cl_rcon.h" #include "engine/client/cdll_engine_int.h" +#include "engine/client/clientstate.h" #endif // !DEDICATED #include "engine/client/client.h" #include "engine/net.h" @@ -24,14 +26,23 @@ #ifndef CLIENT_DLL #include "engine/server/server.h" #endif // !CLIENT_DLL -#include "rtech/rtech_game.h" -#include "rtech/rtech_utils.h" + +#include "rtech/pak/pakencode.h" +#include "rtech/pak/pakdecode.h" +#include "rtech/pak/pakparse.h" +#include "rtech/pak/pakstate.h" +#include "rtech/pak/paktools.h" + +#include "rtech/playlists/playlists.h" + #include "filesystem/basefilesystem.h" #include "filesystem/filesystem.h" #include "vpklib/packedstore.h" #include "vscript/vscript.h" +#include "localize/localize.h" #include "ebisusdk/EbisuSDK.h" #ifndef DEDICATED +#include "geforce/reflex.h" #include "gameui/IBrowser.h" #include "gameui/IConsole.h" #endif // !DEDICATED @@ -50,6 +61,7 @@ #include "public/bspflags.h" #include "public/cmodel.h" #include "public/idebugoverlay.h" +#include "public/localize/ilocalize.h" #ifndef CLIENT_DLL #include "game/server/detour_impl.h" #include "game/server/gameinterface.h" @@ -65,171 +77,12 @@ MP_GameMode_Changed_f ===================== */ -void MP_GameMode_Changed_f(IConVar* pConVar, const char* pOldString, float flOldValue) +void MP_GameMode_Changed_f(IConVar* pConVar, const char* pOldString) { - SetupGamemode(mp_gamemode->GetString()); + v_SetupGamemode(mp_gamemode->GetString()); } -/* -===================== -MP_HostName_Changed_f -===================== -*/ -void MP_HostName_Changed_f(IConVar* pConVar, const char* pOldString, float flOldValue) -{ -#ifndef DEDICATED - g_pBrowser->SetHostName(pylon_matchmaking_hostname->GetString()); -#endif // !DEDICATED -} - -#ifndef DEDICATED -/* -===================== -ToggleConsole_f -===================== -*/ -void ToggleConsole_f(const CCommand& args) -{ - g_pConsole->m_bActivate ^= true; - ResetInput(); // Disable input to game when console is drawn. -} - -/* -===================== -ToggleBrowser_f -===================== -*/ -void ToggleBrowser_f(const CCommand& args) -{ - g_pBrowser->m_bActivate ^= true; - ResetInput(); // Disable input to game when browser is drawn. -} -#endif // !DEDICATED #ifndef CLIENT_DLL -/* -===================== -Host_Kick_f - - helper function for - bansystem -===================== -*/ -void _Author_Client_f(const CCommand& args, EKickType type) -{ - if (args.ArgC() < 2) - { - return; - } - - const char* szReason = args.ArgC() > 2 ? args.Arg(2) : nullptr; - - switch(type) - { - case KICK_NAME: - { - g_pBanSystem->KickPlayerByName(args.Arg(1), szReason); - break; - } - case KICK_ID: - { - g_pBanSystem->KickPlayerById(args.Arg(1), szReason); - break; - } - case BAN_NAME: - { - g_pBanSystem->BanPlayerByName(args.Arg(1), szReason); - break; - } - case BAN_ID: - { - g_pBanSystem->BanPlayerById(args.Arg(1), szReason); - break; - } - default: - { - // Code bug. - Assert(0); - } - } -} - - -/* -===================== -Host_Kick_f -===================== -*/ -void Host_Kick_f(const CCommand& args) -{ - _Author_Client_f(args, EKickType::KICK_NAME); -} - -/* -===================== -Host_KickID_f -===================== -*/ -void Host_KickID_f(const CCommand& args) -{ - _Author_Client_f(args, EKickType::KICK_ID); -} - -/* -===================== -Host_Ban_f -===================== -*/ -void Host_Ban_f(const CCommand& args) -{ - _Author_Client_f(args, EKickType::BAN_NAME); -} - -/* -===================== -Host_BanID_f -===================== -*/ -void Host_BanID_f(const CCommand& args) -{ - _Author_Client_f(args, EKickType::BAN_ID); -} - -/* -===================== -Host_Unban_f -===================== -*/ -void Host_Unban_f(const CCommand& args) -{ - if (args.ArgC() < 2) - { - return; - } - - g_pBanSystem->UnbanPlayer(args.Arg(1)); -} - -/* -===================== -Host_ReloadBanList_f -===================== -*/ -void Host_ReloadBanList_f(const CCommand& args) -{ - g_pBanSystem->Load(); // Reload banned list. -} - -/* -===================== -Host_ReloadPlaylists_f -===================== -*/ -void Host_ReloadPlaylists_f(const CCommand& args) -{ - _DownloadPlaylists_f(); - KeyValues::InitPlaylists(); // Re-Init playlist. -} - /* ===================== Host_Changelevel_f @@ -240,384 +93,25 @@ Host_Changelevel_f */ void Host_Changelevel_f(const CCommand& args) { - if (args.ArgC() >= 2 + const int argCount = args.ArgC(); + + if (argCount >= 2 && IsOriginInitialized() && g_pServer->IsActive()) { + const char* levelName = args[1]; + const char* landMarkName = argCount > 2 ? args[2] : ""; + v_SetLaunchOptions(args); - v_HostState_ChangeLevelMP(args[1], args[2]); + v_HostState_ChangeLevelMP(levelName, landMarkName); } } - -/* -===================== -Detour_HotSwap_f - - Hot swaps the NavMesh - while the game is running -===================== -*/ -void Detour_HotSwap_f(const CCommand& args) -{ - if (!g_pServer->IsActive()) - return; // Only execute if server is initialized and active. - - DevMsg(eDLL_T::SERVER, "Executing NavMesh hot swap for level '%s'\n", - g_ServerGlobalVariables->m_pszMapName); - - CFastTimer timer; - - timer.Start(); - Detour_HotSwap(); - - timer.End(); - DevMsg(eDLL_T::SERVER, "Hot swap took '%lf' seconds\n", timer.GetDuration().GetSeconds()); -} #endif // !CLIENT_DLL -/* -===================== -Pak_ListPaks_f -===================== -*/ -void Pak_ListPaks_f(const CCommand& args) -{ - DevMsg(eDLL_T::RTECH, "| id | name | status | asset count |\n"); - DevMsg(eDLL_T::RTECH, "|------|----------------------------------------------------|--------------------------------------|-------------|\n"); - uint32_t nTotalLoaded = 0; - - for (int16_t i = 0, n = *g_pLoadedPakCount; i < n; ++i) - { - const RPakLoadedInfo_t& info = g_pLoadedPakInfo[i]; - - if (info.m_nStatus == RPakStatus_t::PAK_STATUS_FREED) - continue; - - const char* szRpakStatus = g_pRTech->PakStatusToString(info.m_nStatus); - - // todo: make status into a string from an array/vector - DevMsg(eDLL_T::RTECH, "| %04i | %-50s | %-36s | %11i |\n", info.m_nHandle, info.m_pszFileName, szRpakStatus, info.m_nAssetCount); - nTotalLoaded++; - } - DevMsg(eDLL_T::RTECH, "|------|----------------------------------------------------|--------------------------------------|-------------|\n"); - DevMsg(eDLL_T::RTECH, "| %18i loaded paks. |\n", nTotalLoaded); - DevMsg(eDLL_T::RTECH, "|------|----------------------------------------------------|--------------------------------------|-------------|\n"); -} - -/* -===================== -Pak_ListTypes_f -===================== -*/ -void Pak_ListTypes_f(const CCommand& args) -{ - DevMsg(eDLL_T::RTECH, "| ext | description | version | header size | native size |\n"); - DevMsg(eDLL_T::RTECH, "|------|---------------------------|---------|-------------|-------------|\n"); - - uint32_t nRegistered = 0; - - for (int8_t i = 0; i < PAK_MAX_TYPES; ++i) - { - RPakAssetBinding_t* type = &g_pPakGlobals->m_nAssetBindings[i]; - - if (!type->m_szDescription) - continue; - - DevMsg(eDLL_T::RTECH, "| %-4s | %-25s | %7i | %11i | %11i |\n", FourCCToString(type->m_nExtension).c_str(), type->m_szDescription, type->m_iVersion, type->m_iSubHeaderSize, type->m_iNativeClassSize); - nRegistered++; - } - DevMsg(eDLL_T::RTECH, "|------|---------------------------|---------|-------------|-------------|\n"); - DevMsg(eDLL_T::RTECH, "| %18i registered types. |\n", nRegistered); - DevMsg(eDLL_T::RTECH, "|------|---------------------------|---------|-------------|-------------|\n"); -} - -/* -===================== -Pak_RequestUnload_f -===================== -*/ -void Pak_RequestUnload_f(const CCommand& args) -{ - if (args.ArgC() < 2) - { - return; - } - - try - { - if (args.HasOnlyDigits(1)) - { - const RPakHandle_t pakHandle = atoi(args.Arg(1)); - const RPakLoadedInfo_t* pakInfo = g_pRTech->GetPakLoadedInfo(pakHandle); - if (!pakInfo) - { - throw std::exception("Found no pak entry for specified handle."); - } - - const string pakName = pakInfo->m_pszFileName; - !pakName.empty() ? DevMsg(eDLL_T::RTECH, "Requested pak unload for file '%s'\n", pakName.c_str()) : DevMsg(eDLL_T::RTECH, "Requested pak unload for handle '%d'\n", pakHandle); - g_pakLoadApi->UnloadPak(pakHandle); - } - else - { - const RPakLoadedInfo_t* pakInfo = g_pRTech->GetPakLoadedInfo(args.Arg(1)); - if (!pakInfo) - { - throw std::exception("Found no pak entry for specified name."); - } - - DevMsg(eDLL_T::RTECH, "Requested pak unload for file '%s'\n", args.Arg(1)); - g_pakLoadApi->UnloadPak(pakInfo->m_nHandle); - } - } - catch (const std::exception& e) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - %s\n", __FUNCTION__, e.what()); - return; - } -} - -/* -===================== -Pak_RequestLoad_f -===================== -*/ -void Pak_RequestLoad_f(const CCommand& args) -{ - g_pakLoadApi->LoadAsync(args.Arg(1)); -} - - -/* -===================== -Pak_Swap_f -===================== -*/ -void Pak_Swap_f(const CCommand& args) -{ - try - { - string pakName; - RPakHandle_t pakHandle = 0; - RPakLoadedInfo_t* pakInfo = nullptr; - - if (args.HasOnlyDigits(1)) - { - pakHandle = atoi(args.Arg(1)); - pakInfo = g_pRTech->GetPakLoadedInfo(pakHandle); - if (!pakInfo) - { - throw std::exception("Found no pak entry for specified handle."); - } - - pakName = pakInfo->m_pszFileName; - } - else - { - pakName = args.Arg(1); - pakInfo = g_pRTech->GetPakLoadedInfo(args.Arg(1)); - if (!pakInfo) - { - throw std::exception("Found no pak entry for specified name."); - } - - pakHandle = pakInfo->m_nHandle; - } - - !pakName.empty() ? DevMsg(eDLL_T::RTECH, "Requested pak swap for file '%s'\n", pakName.c_str()) : DevMsg(eDLL_T::RTECH, "Requested pak swap for handle '%d'\n", pakHandle); - - g_pakLoadApi->UnloadPak(pakHandle); - - while (pakInfo->m_nStatus != RPakStatus_t::PAK_STATUS_FREED) // Wait till this slot gets free'd. - std::this_thread::sleep_for(std::chrono::seconds(1)); - - g_pakLoadApi->LoadAsync(pakName.c_str()); - } - catch (const std::exception& e) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - %s\n", __FUNCTION__, e.what()); - return; - } -} - -/* -===================== -RTech_StringToGUID_f -===================== -*/ -void RTech_StringToGUID_f(const CCommand& args) -{ - if (args.ArgC() < 2) - { - return; - } - - unsigned long long guid = g_pRTech->StringToGuid(args.Arg(1)); - - DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); - DevMsg(eDLL_T::RTECH, "] RTECH_HASH ]------------------------------------------------\n"); - DevMsg(eDLL_T::RTECH, "] GUID: '0x%llX'\n", guid); -} - -/* -===================== -RTech_Decompress_f - - Decompresses input RPak file and - dumps results to 'paks\Win32\*.rpak' -===================== -*/ -void RTech_Decompress_f(const CCommand& args) -{ - if (args.ArgC() < 2) - { - return; - } - - CUtlString inPakFile; - CUtlString outPakFile; - - inPakFile.Format(PLATFORM_PAK_PATH "%s", args.Arg(1)); - outPakFile.Format(PLATFORM_PAK_OVERRIDE_PATH "%s", args.Arg(1)); - - DevMsg(eDLL_T::RTECH, "______________________________________________________________\n"); - DevMsg(eDLL_T::RTECH, "-+ RTech decompress ------------------------------------------\n"); - - if (!FileSystem()->FileExists(inPakFile.String(), "GAME")) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' does not exist!\n", - __FUNCTION__, inPakFile.String()); - return; - } - - DevMsg(eDLL_T::RTECH, " |-+ Processing: '%s'\n", inPakFile.String()); - FileHandle_t hPakFile = FileSystem()->Open(inPakFile.String(), "rb", "GAME"); - - if (!hPakFile) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", - __FUNCTION__, inPakFile.String()); - return; - } - - uint32_t nPakLen = FileSystem()->Size(hPakFile); - - std::unique_ptr pPakBufContainer(new uint8_t[nPakLen]); - uint8_t* pPakBuf = pPakBufContainer.get(); - - FileSystem()->Read(pPakBuf, nPakLen, hPakFile); - FileSystem()->Close(hPakFile); - - RPakHeader_t* pHeader = reinterpret_cast(pPakBuf); - uint16_t flags = (pHeader->m_nFlags[0] << 8) | pHeader->m_nFlags[1]; - - SYSTEMTIME systemTime; - FileTimeToSystemTime(&pHeader->m_nFileTime, &systemTime); - - DevMsg(eDLL_T::RTECH, " | |-+ Header ------------------------------------------------\n"); - DevMsg(eDLL_T::RTECH, " | |-- Magic : '0x%08X'\n", pHeader->m_nMagic); - DevMsg(eDLL_T::RTECH, " | |-- Version : '%hu'\n", pHeader->m_nVersion); - DevMsg(eDLL_T::RTECH, " | |-- Flags : '0x%04hX'\n", flags); - DevMsg(eDLL_T::RTECH, " | |-- Time : '%hu-%hu-%hu/%hu %hu:%hu:%hu.%hu'\n", - systemTime.wYear,systemTime.wMonth,systemTime.wDay, systemTime.wDayOfWeek, - systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds); - DevMsg(eDLL_T::RTECH, " | |-- Hash : '0x%08llX'\n", pHeader->m_nHash); - DevMsg(eDLL_T::RTECH, " | |-- Entries : '%u'\n", pHeader->m_nAssetEntryCount); - DevMsg(eDLL_T::RTECH, " | |-+ Compression -----------------------------------------\n"); - DevMsg(eDLL_T::RTECH, " | |-- Size comp: '%llu'\n", pHeader->m_nSizeDisk); - DevMsg(eDLL_T::RTECH, " | |-- Size decp: '%llu'\n", pHeader->m_nSizeMemory); - - if (pHeader->m_nMagic != PAK_HEADER_MAGIC) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' has invalid magic!\n", - __FUNCTION__, inPakFile.String()); - - return; - } - if ((pHeader->m_nFlags[1] & 1) != 1) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' already decompressed!\n", - __FUNCTION__, inPakFile.String()); - - return; - } - if (pHeader->m_nSizeDisk != nPakLen) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - pak file '%s' decompressed size '%llu' doesn't match expected size '%llu'!\n", - __FUNCTION__, inPakFile.String(), nPakLen, pHeader->m_nSizeMemory); - - return; - } - - RPakDecompState_t decompState; - uint64_t nDecompSize = g_pRTech->DecompressPakFileInit(&decompState, pPakBuf, nPakLen, NULL, sizeof(RPakHeader_t)); - - if (nDecompSize == pHeader->m_nSizeDisk) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - calculated size: '%llu' expected: '%llu'!\n", - __FUNCTION__, nDecompSize, pHeader->m_nSizeMemory); - - return; - } - else - { - DevMsg(eDLL_T::RTECH, " | |-- Size calc: '%llu'\n", nDecompSize); - } - - DevMsg(eDLL_T::RTECH, " | |-- Ratio : '%.02f'\n", (pHeader->m_nSizeDisk * 100.f) / pHeader->m_nSizeMemory); - - - std::unique_ptr pDecompBufContainer(new uint8_t[nPakLen]); - uint8_t* pDecompBuf = pDecompBufContainer.get(); - - decompState.m_nOutMask = UINT64_MAX; - decompState.m_nOut = uint64_t(pDecompBuf); - - uint8_t nDecompResult = g_pRTech->DecompressPakFile(&decompState, nPakLen, pHeader->m_nSizeMemory); - if (nDecompResult != 1) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - decompression failed for '%s' return value: '%hu'!\n", - __FUNCTION__, inPakFile.String(), nDecompResult); - } - - pHeader->m_nFlags[1] = 0x0; // Set compressed flag to false for the decompressed pak file. - pHeader->m_nSizeDisk = pHeader->m_nSizeMemory; // Equal compressed size with decompressed. - - FileSystem()->CreateDirHierarchy(PLATFORM_PAK_OVERRIDE_PATH, "GAME"); - FileHandle_t hDecompFile = FileSystem()->Open(outPakFile.String(), "wb", "GAME"); - - if (!hDecompFile) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s - Unable to write to '%s' (read-only?)\n", - __FUNCTION__, outPakFile.String()); - - return; - } - - if (pHeader->m_nPatchIndex > 0) // Check if its an patch rpak. - { - // Loop through all the structs and patch their compress size. - for (uint32_t i = 1, nPatchOffset = (sizeof(RPakHeader_t) + sizeof(uint64_t)); - i <= pHeader->m_nPatchIndex; i++, nPatchOffset += sizeof(RPakPatchCompressedHeader_t)) - { - RPakPatchCompressedHeader_t* pPatchHeader = reinterpret_cast(pDecompBuf + nPatchOffset); - DevMsg(eDLL_T::RTECH, " | |-+ Patch #%02u -----------------------------------------\n", i); - DevMsg(eDLL_T::RTECH, " | %s |-- Size comp: '%llu'\n", i < pHeader->m_nPatchIndex ? "|" : " ", pPatchHeader->m_nSizeDisk); - DevMsg(eDLL_T::RTECH, " | %s |-- Size decp: '%llu'\n", i < pHeader->m_nPatchIndex ? "|" : " ", pPatchHeader->m_nSizeMemory); - - pPatchHeader->m_nSizeDisk = pPatchHeader->m_nSizeMemory; // Fix size for decompress. - } - } - - memcpy_s(pDecompBuf, sizeof(RPakHeader_t), pPakBuf, sizeof(RPakHeader_t));// Overwrite first 0x80 bytes which are NULL with the header data. - FileSystem()->Write(pDecompBuf, int(decompState.m_nDecompSize), hDecompFile); - - DevMsg(eDLL_T::RTECH, " |-- Checksum : '0x%08X'\n", crc32::update(NULL, pDecompBuf, decompState.m_nDecompSize)); - DevMsg(eDLL_T::RTECH, "-+ Decompressed pak file to: '%s'\n", outPakFile.String()); - DevMsg(eDLL_T::RTECH, "--------------------------------------------------------------\n"); - - FileSystem()->Close(hDecompFile); -} +// TODO: move this to 'packedstore.cpp' and move everything in that file to 'packetstorebuilder.cpp' +static ConVar fs_packedstore_workspace("fs_packedstore_workspace", "ship", FCVAR_DEVELOPMENTONLY, "Determines the current VPK workspace."); +static ConVar fs_packedstore_compression_level("fs_packedstore_compression_level", "default", FCVAR_DEVELOPMENTONLY, "Determines the VPK compression level.", "fastest faster default better uber"); +static ConVar fs_packedstore_max_helper_threads("fs_packedstore_max_helper_threads", "-1", FCVAR_DEVELOPMENTONLY, "Max # of additional \"helper\" threads to create during compression.", true, -1, true, LZHAM_MAX_HELPER_THREADS, "Must range between [-1,LZHAM_MAX_HELPER_THREADS], where -1=max practical"); /* ===================== @@ -634,18 +128,28 @@ void VPK_Pack_f(const CCommand& args) return; } - VPKPair_t pair(args.Arg(1), args.Arg(2), args.Arg(3), NULL); - CFastTimer timer; + const char* workspacePath = fs_packedstore_workspace.GetString(); - DevMsg(eDLL_T::FS, "*** Starting VPK build command for: '%s'\n", pair.m_DirName.Get()); + if (!FileSystem()->IsDirectory(workspacePath, "PLATFORM")) + { + Error(eDLL_T::FS, NO_ERROR, "Workspace path \"%s\" doesn't exist!\n", workspacePath); + return; + } + + VPKPair_t pair(args.Arg(1), args.Arg(2), args.Arg(3), NULL); + Msg(eDLL_T::FS, "*** Starting VPK build command for: '%s'\n", pair.m_DirName.String()); + + CFastTimer timer; timer.Start(); - g_pPackedStore->InitLzCompParams(); - g_pPackedStore->PackWorkspace(pair, fs_packedstore_workspace->GetString(), "vpk/"); + CPackedStoreBuilder builder; + + builder.InitLzEncoder(fs_packedstore_max_helper_threads.GetInt(), fs_packedstore_compression_level.GetString()); + builder.PackStore(pair, workspacePath, "vpk/"); timer.End(); - DevMsg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds()); - DevMsg(eDLL_T::FS, "\n"); + Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds()); + Msg(eDLL_T::FS, "\n"); } /* @@ -663,19 +167,28 @@ void VPK_Unpack_f(const CCommand& args) return; } - CUtlString arg = args.Arg(1); - VPKDir_t vpk(arg, (args.ArgC() > 2)); - CFastTimer timer; + CUtlString fileName = args.Arg(1); + VPKDir_t vpk(fileName, (args.ArgC() > 2)); - DevMsg(eDLL_T::FS, "*** Starting VPK extraction command for: '%s'\n", arg.Get()); + if (vpk.Failed()) + { + Error(eDLL_T::FS, NO_ERROR, "Failed to parse directory tree file \"%s\"!\n", fileName.String()); + return; + } + + Msg(eDLL_T::FS, "*** Starting VPK extraction command for: '%s'\n", fileName.String()); + + CFastTimer timer; timer.Start(); - g_pPackedStore->InitLzDecompParams(); - g_pPackedStore->UnpackWorkspace(vpk, fs_packedstore_workspace->GetString()); + CPackedStoreBuilder builder; + + builder.InitLzDecoder(); + builder.UnpackStore(vpk, fs_packedstore_workspace.GetString()); timer.End(); - DevMsg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds()); - DevMsg(eDLL_T::FS, "\n"); + Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds()); + Msg(eDLL_T::FS, "\n"); } /* @@ -714,57 +227,6 @@ void VPK_Unmount_f(const CCommand& args) FileSystem()->UnmountVPKFile(args.Arg(1)); } -/* -===================== -NET_SetKey_f - - Sets the input netchannel encryption key -===================== -*/ -void NET_SetKey_f(const CCommand& args) -{ - if (args.ArgC() < 2) - { - return; - } - - NET_SetKey(args.Arg(1)); -} - -/* -===================== -NET_GenerateKey_f - - Sets a random netchannel encryption key -===================== -*/ -void NET_GenerateKey_f(const CCommand& args) -{ - NET_GenerateKey(); -} - -/* -===================== -NET_UseRandomKeyChanged_f - - Use random AES encryption - key for game packets -===================== -*/ -void NET_UseRandomKeyChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue) -{ - if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetCommandName())) - { - if (strcmp(pOldString, pConVarRef->GetString()) == NULL) - return; // Same value. - - if (pConVarRef->GetBool()) - NET_GenerateKey(); - else - NET_SetKey(DEFAULT_NET_ENCRYPTION_KEY); - } -} - /* ===================== NET_UseSocketsForLoopbackChanged_f @@ -773,9 +235,9 @@ NET_UseSocketsForLoopbackChanged_f key for game packets ===================== */ -void NET_UseSocketsForLoopbackChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue) +void NET_UseSocketsForLoopbackChanged_f(IConVar* pConVar, const char* pOldString) { - if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetCommandName())) + if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName())) { if (strcmp(pOldString, pConVarRef->GetString()) == NULL) return; // Same value. @@ -784,7 +246,7 @@ void NET_UseSocketsForLoopbackChanged_f(IConVar* pConVar, const char* pOldString // Reboot the RCON server to switch address type. if (RCONServer()->IsInitialized()) { - DevMsg(eDLL_T::SERVER, "Rebooting RCON server...\n"); + Msg(eDLL_T::SERVER, "Rebooting RCON server...\n"); RCONServer()->Shutdown(); RCONServer()->Init(); } @@ -792,414 +254,34 @@ void NET_UseSocketsForLoopbackChanged_f(IConVar* pConVar, const char* pOldString } } -/* -===================== -SIG_GetAdr_f - - Logs the sigscan - results to the console. -===================== -*/ -void SIG_GetAdr_f(const CCommand& args) +void LanguageChanged_f(IConVar* pConVar, const char* pOldString) { - DetourAddress(); -} - -/* -===================== -CON_Help_f - - Shows the colors and - description of each - context. -===================== -*/ -void CON_Help_f(const CCommand& args) -{ - DevMsg(eDLL_T::COMMON, "Contexts:\n"); - SQVM_PrintFunc(reinterpret_cast(SQCONTEXT::SERVER), (SQChar*)(" = Server DLL (Script)\n")); - SQVM_PrintFunc(reinterpret_cast(SQCONTEXT::CLIENT), (SQChar*)(" = Client DLL (Script)\n")); - SQVM_PrintFunc(reinterpret_cast(SQCONTEXT::UI), (SQChar*)(" = UI DLL (Script)\n")); - - DevMsg(eDLL_T::SERVER, " = Server DLL (Code)\n"); - DevMsg(eDLL_T::CLIENT, " = Client DLL (Code)\n"); - DevMsg(eDLL_T::UI, " = UI DLL (Code)\n"); - DevMsg(eDLL_T::ENGINE, " = Engine DLL (Code)\n"); - DevMsg(eDLL_T::FS, " = FileSystem (Code)\n"); - DevMsg(eDLL_T::RTECH, " = PakLoad API (Code)\n"); - DevMsg(eDLL_T::MS, " = MaterialSystem (Code)\n"); - DevMsg(eDLL_T::AUDIO, " = Audio DLL (Code)\n"); - DevMsg(eDLL_T::VIDEO, " = Video DLL (Code)\n"); - DevMsg(eDLL_T::NETCON, " = NetConsole (Code)\n"); -} - -#ifndef DEDICATED -/* -===================== -CON_LogHistory_f - - Shows the game console - submission history. -===================== -*/ -void CON_LogHistory_f(const CCommand& args) -{ - const vector vHistory = g_pConsole->GetHistory(); - for (size_t i = 0, nh = vHistory.size(); i < nh; i++) - { - DevMsg(eDLL_T::COMMON, "%3d: %s\n", i, vHistory[i].c_str()); - } -} - -/* -===================== -CON_RemoveLine_f - - Removes a range of lines - from the console. -===================== -*/ -void CON_RemoveLine_f(const CCommand& args) -{ - if (args.ArgC() < 3) - { - DevMsg(eDLL_T::CLIENT, "Usage 'con_removeline': start(int) end(int)\n"); - return; - } - - int start = atoi(args[1]); - int end = atoi(args[2]); - - g_pConsole->RemoveLog(start, end); -} - -/* -===================== -CON_ClearLines_f - - Clears all lines from - the developer console. -===================== -*/ -void CON_ClearLines_f(const CCommand& args) -{ - g_pConsole->ClearLog(); -} - -/* -===================== -CON_ClearHistory_f - - Clears all submissions from the - developer console history. -===================== -*/ -void CON_ClearHistory_f(const CCommand& args) -{ - g_pConsole->ClearHistory(); -} - -/* -===================== -RCON_CmdQuery_f - - Issues an RCON command to the - RCON server. -===================== -*/ -void RCON_CmdQuery_f(const CCommand& args) -{ - const int64_t argCount = args.ArgC(); - - if (argCount < 2) - { - const char* pszAddress = rcon_address->GetString(); - - if (RCONClient()->IsInitialized() - && !RCONClient()->IsConnected() - && pszAddress[0]) - { - RCONClient()->Connect(pszAddress); - } - } - else - { - if (!RCONClient()->IsInitialized()) - { - Warning(eDLL_T::CLIENT, "Failed to issue command to RCON server: %s\n", "uninitialized"); - return; - } - else if (RCONClient()->IsConnected()) - { - vector vecMsg; - bool bSuccess = false; - const SocketHandle_t hSocket = RCONClient()->GetSocket(); - - if (strcmp(args.Arg(1), "PASS") == 0) // Auth with RCON server using rcon_password ConVar value. - { - if (argCount > 2) - { - bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(2), "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH); - } - else // Use 'rcon_password' ConVar as password. - { - bSuccess = RCONClient()->Serialize(vecMsg, rcon_password->GetString(), "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH); - } - - if (bSuccess) - { - RCONClient()->Send(hSocket, vecMsg.data(), int(vecMsg.size())); - } - - return; - } - else if (strcmp(args.Arg(1), "disconnect") == 0) // Disconnect from RCON server. - { - RCONClient()->Disconnect("issued by user"); - return; - } - - bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(1), args.ArgS(), cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND); - if (bSuccess) - { - RCONClient()->Send(hSocket, vecMsg.data(), int(vecMsg.size())); - } - return; - } - else - { - Warning(eDLL_T::CLIENT, "Failed to issue command to RCON server: %s\n", "unconnected"); - return; - } - } -} - -/* -===================== -RCON_Disconnect_f - - Disconnect from RCON server -===================== -*/ -void RCON_Disconnect_f(const CCommand& args) -{ - const bool bIsConnected = RCONClient()->IsConnected(); - RCONClient()->Disconnect("issued by user"); - - if (bIsConnected) // Log if client was indeed connected. - { - DevMsg(eDLL_T::CLIENT, "User closed RCON connection\n"); - } -} - -/* -===================== -RCON_SendLogs_f - - request logs from RCON server -===================== -*/ -void RCON_InputOnlyChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue) -{ - RCONClient()->RequestConsoleLog(RCONClient()->ShouldReceive()); -} -#endif // !DEDICATED - -#ifndef CLIENT_DLL - -static const char* s_LanguageNames[] = { - "english", - "french", - "german", - "italian", - "japanese", - "polish", - "russian", - "spanish", - "schinese", - "tchinese", - "korean" -}; - -static bool IsValidTextLanguage(const char* pLocaleName) -{ - for (int i = 0; i < SDK_ARRAYSIZE(s_LanguageNames); ++i) - { - if (strcmp(pLocaleName, s_LanguageNames[i]) == NULL) - return true; - } - - return false; -} - -void SV_LanguageChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue) -{ - if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetCommandName())) + if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName())) { const char* pNewString = pConVarRef->GetString(); - if (IsValidTextLanguage(pNewString)) - return; - - // if new text isn't valid but the old value is, reset the value - if (IsValidTextLanguage(pOldString)) - { - pConVarRef->SetValue(pOldString); - return; - } - else // this shouldn't really happen, but if neither the old nor new values are valid, set to english - pConVarRef->SetValue("english"); - - } -} - -#endif - -/* -===================== -RCON_PasswordChanged_f - - Change RCON password - on server and client -===================== -*/ -void RCON_PasswordChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue) -{ - if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetCommandName())) - { if (strcmp(pOldString, pConVarRef->GetString()) == NULL) - return; // Same password. + return; // Same language. -#ifndef DEDICATED - if (!RCONClient()->IsInitialized()) - RCONClient()->Init(); // Initialize first. -#endif // !DEDICATED -#ifndef CLIENT_DLL - if (RCONServer()->IsInitialized()) - RCONServer()->SetPassword(pConVarRef->GetString()); - else - RCONServer()->Init(); // Initialize first. -#endif // !CLIENT_DLL - } -} - -#ifndef CLIENT_DLL -/* -===================== -RCON_WhiteListAddresChanged_f - - Change whitelist address - on RCON server -===================== -*/ -void RCON_WhiteListAddresChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue) -{ - if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetCommandName())) - { - if (strcmp(pOldString, pConVarRef->GetString()) == NULL) - return; // Same address. - - if (!RCONServer()->SetWhiteListAddress(pConVarRef->GetString())) + if (!Localize_IsLanguageSupported(pNewString)) { - Warning(eDLL_T::SERVER, "Failed to set RCON whitelist address: %s\n", pConVarRef->GetString()); - } - } -} - -/* -===================== -RCON_ConnectionCountChanged_f - - Change max connection - count on RCON server -===================== -*/ -void RCON_ConnectionCountChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue) -{ - if (!RCONServer()->IsInitialized()) - return; // Not initialized; no sockets at this point. - - if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetCommandName())) - { - if (strcmp(pOldString, pConVarRef->GetString()) == NULL) - return; // Same count. - - const int maxCount = pConVarRef->GetInt(); - - int count = RCONServer()->GetAuthenticatedCount(); - CSocketCreator* pCreator = RCONServer()->GetSocketCreator(); - - if (count < maxCount) - { - if (!pCreator->IsListening()) + // if new text isn't valid but the old value is, reset the value + if (Localize_IsLanguageSupported(pOldString)) + pNewString = pOldString; + else { - pCreator->CreateListenSocket(*RCONServer()->GetNetAddress()); + // this shouldn't really happen, but if neither the old nor new values are valid, set to english + Assert(0); + pNewString = g_LanguageNames[0]; } } - else - { - while (count > maxCount) - { - RCONServer()->Disconnect(count-1, "too many authenticated sockets"); - count = RCONServer()->GetAuthenticatedCount(); - } - pCreator->CloseListenSocket(); - RCONServer()->CloseNonAuthConnection(); - } - } -} -#endif // !CLIENT_DLL - -/* -===================== -SQVM_ServerScript_f - - Executes input on the - VM in SERVER context. -===================== -*/ -void SQVM_ServerScript_f(const CCommand& args) -{ - if (args.ArgC() >= 2) - { - Script_Execute(args.ArgS(), SQCONTEXT::SERVER); + pConVarRef->SetValue(pNewString); + g_MasterServer.SetLanguage(pNewString); } } #ifndef DEDICATED -/* -===================== -SQVM_ClientScript_f - - Executes input on the - VM in CLIENT context. -===================== -*/ -void SQVM_ClientScript_f(const CCommand& args) -{ - if (args.ArgC() >= 2) - { - Script_Execute(args.ArgS(), SQCONTEXT::CLIENT); - } -} - -/* -===================== -SQVM_UIScript_f - - Executes input on the - VM in UI context. -===================== -*/ -void SQVM_UIScript_f(const CCommand& args) -{ - if (args.ArgC() >= 2) - { - Script_Execute(args.ArgS(), SQCONTEXT::UI); - } -} - /* ===================== Mat_CrossHair_f @@ -1209,58 +291,58 @@ Mat_CrossHair_f */ void Mat_CrossHair_f(const CCommand& args) { - CMaterialGlue* material = GetMaterialAtCrossHair(); + CMaterialGlue* material = v_GetMaterialAtCrossHair(); if (material) { - DevMsg(eDLL_T::MS, "______________________________________________________________\n"); - DevMsg(eDLL_T::MS, "-+ Material --------------------------------------------------\n"); - DevMsg(eDLL_T::MS, " |-- ADDR: '%llX'\n", material); - DevMsg(eDLL_T::MS, " |-- GUID: '%llX'\n", material->m_GUID); - DevMsg(eDLL_T::MS, " |-- Streaming texture count: '%d'\n", material->m_nStreamableTextureCount); - DevMsg(eDLL_T::MS, " |-- Material width: '%d'\n", material->m_iWidth); - DevMsg(eDLL_T::MS, " |-- Material height: '%d'\n", material->m_iHeight); - DevMsg(eDLL_T::MS, " |-- Flags: '%llX'\n", material->m_iFlags); + Msg(eDLL_T::MS, "______________________________________________________________\n"); + Msg(eDLL_T::MS, "-+ Material --------------------------------------------------\n"); + Msg(eDLL_T::MS, " |-- ADDR: '%llX'\n", material); + Msg(eDLL_T::MS, " |-- GUID: '%llX'\n", material->assetGuid); + Msg(eDLL_T::MS, " |-- Num Streaming Textures: '%d'\n", material->numStreamingTextureHandles); + Msg(eDLL_T::MS, " |-- Material width: '%d'\n", material->width); + Msg(eDLL_T::MS, " |-- Material height: '%d'\n", material->height); + Msg(eDLL_T::MS, " |-- Samplers: '%08X'\n", material->samplers); std::function fnPrintChild = [](CMaterialGlue* material, const char* print) { - DevMsg(eDLL_T::MS, " |-+\n"); - DevMsg(eDLL_T::MS, " | |-+ Child material ----------------------------------------\n"); - DevMsg(eDLL_T::MS, print, material); - DevMsg(eDLL_T::MS, " | |-- GUID: '%llX'\n", material->m_GUID); - DevMsg(eDLL_T::MS, " | |-- Material name: '%s'\n", material->m_pszName); + Msg(eDLL_T::MS, " |-+\n"); + Msg(eDLL_T::MS, " | |-+ Child material ----------------------------------------\n"); + Msg(eDLL_T::MS, print, material); + Msg(eDLL_T::MS, " | |-- GUID: '%llX'\n", material->assetGuid); + Msg(eDLL_T::MS, " | |-- Material name: '%s'\n", material->name); }; - DevMsg(eDLL_T::MS, " |-- Material name: '%s'\n", material->m_pszName); - DevMsg(eDLL_T::MS, " |-- Material surface name 1: '%s'\n", material->m_pszSurfaceProp); - DevMsg(eDLL_T::MS, " |-- Material surface name 2: '%s'\n", material->m_pszSurfaceProp2); - DevMsg(eDLL_T::MS, " |-- DX buffer: '%llX'\n", material->m_pDXBuffer); - DevMsg(eDLL_T::MS, " |-- DX buffer VFTable: '%llX'\n", material->m_pID3D11BufferVTable); + Msg(eDLL_T::MS, " |-- Material name: '%s'\n", material->name); + Msg(eDLL_T::MS, " |-- Material surface name 1: '%s'\n", material->surfaceProp); + Msg(eDLL_T::MS, " |-- Material surface name 2: '%s'\n", material->surfaceProp2); + Msg(eDLL_T::MS, " |-- DX buffer: '%llX'\n", material->dxBuffer); + Msg(eDLL_T::MS, " |-- DX buffer VFTable: '%llX'\n", material->unkD3DPointer); - material->m_pDepthShadow - ? fnPrintChild(material->m_pDepthShadow, " | |-+ DepthShadow: '%llX'\n") - : DevMsg(eDLL_T::MS, " | |-+ DepthShadow: 'NULL'\n"); - material->m_pDepthPrepass - ? fnPrintChild(material->m_pDepthPrepass, " | |-+ DepthPrepass: '%llX'\n") - : DevMsg(eDLL_T::MS, " | |-+ DepthPrepass: 'NULL'\n"); - material->m_pDepthVSM - ? fnPrintChild(material->m_pDepthVSM, " | |-+ DepthVSM: '%llX'\n") - : DevMsg(eDLL_T::MS, " | |-+ DepthVSM: 'NULL'\n"); - material->m_pDepthShadow - ? fnPrintChild(material->m_pDepthShadow, " | |-+ DepthShadowTight: '%llX'\n") - : DevMsg(eDLL_T::MS, " | |-+ DepthShadowTight: 'NULL'\n"); - material->m_pColPass - ? fnPrintChild(material->m_pColPass, " | |-+ ColPass: '%llX'\n") - : DevMsg(eDLL_T::MS, " | |-+ ColPass: 'NULL'\n"); + material->depthShadowMaterial + ? fnPrintChild(material->depthShadowMaterial, " | |-+ DepthShadow: '%llX'\n") + : Msg(eDLL_T::MS, " | |-+ DepthShadow: 'NULL'\n"); + material->depthPrepassMaterial + ? fnPrintChild(material->depthPrepassMaterial, " | |-+ DepthPrepass: '%llX'\n") + : Msg(eDLL_T::MS, " | |-+ DepthPrepass: 'NULL'\n"); + material->depthVSMMaterial + ? fnPrintChild(material->depthVSMMaterial, " | |-+ DepthVSM: '%llX'\n") + : Msg(eDLL_T::MS, " | |-+ DepthVSM: 'NULL'\n"); + material->depthShadowTightMaterial + ? fnPrintChild(material->depthShadowTightMaterial, " | |-+ DepthShadowTight: '%llX'\n") + : Msg(eDLL_T::MS, " | |-+ DepthShadowTight: 'NULL'\n"); + material->colpassMaterial + ? fnPrintChild(material->colpassMaterial, " | |-+ ColPass: '%llX'\n") + : Msg(eDLL_T::MS, " | |-+ ColPass: 'NULL'\n"); - DevMsg(eDLL_T::MS, "-+ Texture GUID map ------------------------------------------\n"); - DevMsg(eDLL_T::MS, " |-- Texture handles: '%llX'\n", material->m_pTextureHandles); - DevMsg(eDLL_T::MS, " |-- Streaming texture handles: '%llX'\n", material->m_pStreamableTextureHandles); + Msg(eDLL_T::MS, "-+ Texture GUID map ------------------------------------------\n"); + Msg(eDLL_T::MS, " |-- Texture handles: '%llX'\n", material->textureHandles); + Msg(eDLL_T::MS, " |-- Streaming texture handles: '%llX'\n", material->streamingTextureHandles); - DevMsg(eDLL_T::MS, "--------------------------------------------------------------\n"); + Msg(eDLL_T::MS, "--------------------------------------------------------------\n"); } else { - DevMsg(eDLL_T::MS, "%s: No material found >:(\n", __FUNCTION__); + Msg(eDLL_T::MS, "%s: No material found >:(\n", __FUNCTION__); } } @@ -1276,7 +358,7 @@ void Line_f(const CCommand& args) { if (args.ArgC() != 7) { - DevMsg(eDLL_T::CLIENT, "Usage 'line': start(vector) end(vector)\n"); + Msg(eDLL_T::CLIENT, "Usage 'line': start(vector) end(vector)\n"); return; } @@ -1287,7 +369,7 @@ void Line_f(const CCommand& args) end[i] = float(atof(args[i + 4])); } - g_pDebugOverlay->AddLineOverlay(start, end, 255, 255, 0, !r_debug_draw_depth_test->GetBool(), 100); + g_pDebugOverlay->AddLineOverlay(start, end, 255, 255, 0, !r_debug_draw_depth_test.GetBool(), 100); } /* @@ -1302,7 +384,7 @@ void Sphere_f(const CCommand& args) { if (args.ArgC() != 7) { - DevMsg(eDLL_T::CLIENT, "Usage 'sphere': origin(vector) radius(float) theta(int) phi(int)\n"); + Msg(eDLL_T::CLIENT, "Usage 'sphere': origin(vector) radius(float) theta(int) phi(int)\n"); return; } @@ -1331,7 +413,7 @@ void Capsule_f(const CCommand& args) { if (args.ArgC() != 10) { - DevMsg(eDLL_T::CLIENT, "Usage 'capsule': start(vector) end(vector) radius(vector)\n"); + Msg(eDLL_T::CLIENT, "Usage 'capsule': start(vector) end(vector) radius(vector)\n"); return; } @@ -1345,7 +427,10 @@ void Capsule_f(const CCommand& args) g_pDebugOverlay->AddCapsuleOverlay(start, end, radius, { 0,0,0 }, { 0,0,0 }, 141, 233, 135, 0, 100); } #endif // !DEDICATED -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) + +// TODO: move to other file? +static ConVar bhit_depth_test("bhit_depth_test", "0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Use depth test for bullet ray trace overlay"); +static ConVar bhit_abs_origin("bhit_abs_origin", "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Draw entity's predicted abs origin upon bullet impact for trajectory debugging (requires 'r_visualizetraces' to be set!)"); /* ===================== BHit_f @@ -1385,13 +470,13 @@ void BHit_f(const CCommand& args) g_pEngineTraceServer->TraceRay(ray, TRACE_MASK_NPCWORLDSTATIC, &trace); - g_pDebugOverlay->AddLineOverlay(trace.startpos, trace.endpos, 0, 255, 0, !bhit_depth_test->GetBool(), sv_visualizetraces_duration->GetFloat()); - g_pDebugOverlay->AddLineOverlay(trace.endpos, vecAbsEnd, 255, 0, 0, !bhit_depth_test->GetBool(), sv_visualizetraces_duration->GetFloat()); + g_pDebugOverlay->AddLineOverlay(trace.startpos, trace.endpos, 0, 255, 0, !bhit_depth_test.GetBool(), sv_visualizetraces_duration->GetFloat()); + g_pDebugOverlay->AddLineOverlay(trace.endpos, vecAbsEnd, 255, 0, 0, !bhit_depth_test.GetBool(), sv_visualizetraces_duration->GetFloat()); } #endif // !CLIENT_DLL #ifndef DEDICATED - if (bhit_abs_origin->GetBool() && r_visualizetraces->GetBool()) + if (bhit_abs_origin.GetBool() && r_visualizetraces->GetBool()) { const int iEnt = atoi(args[2]); if (const IClientEntity* pEntity = g_pClientEntityList->GetClientEntity(iEnt)) @@ -1402,7 +487,7 @@ void BHit_f(const CCommand& args) } #endif // !DEDICATED } -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 + /* ===================== CVHelp_f @@ -1455,45 +540,60 @@ void CVFlag_f(const CCommand& args) cv->CvarFindFlags_f(args); } +#ifndef DEDICATED +static double s_flScriptExecTimeBase = 0.0f; +static int s_nScriptExecCount = 0; +#endif // !DEDICATED /* ===================== -CC_CreateFakePlayer_f +Cmd_Exec_f - Creates a fake player - on the server + executes a cfg file ===================== */ -#ifndef CLIENT_DLL -void CC_CreateFakePlayer_f(const CCommand& args) +#ifndef DEDICATED +static ConVar sv_quota_scriptExecsPerSecond("sv_quota_scriptExecsPerSecond", "3", FCVAR_REPLICATED | FCVAR_RELEASE, + "How many script executions per second clients are allowed to submit, 0 to disable the limitation thereof.", true, 0.f, false, 0.f); +#endif // !DEDICATED + +void Cmd_Exec_f(const CCommand& args) { - if (args.ArgC() < 3) +#ifndef DEDICATED + // Prevent users from running neo strafe commands and other quick hacks. + // TODO: when reBar becomes a thing, we should verify this function and + // flag users that patch them out. + if (g_pClientState->IsActive() && !ThreadInServerFrameThread()) { - DevMsg(eDLL_T::SERVER, "usage 'sv_addbot': name(string) teamid(int)\n"); - return; + const int execQuota = sv_quota_scriptExecsPerSecond.GetInt(); + + if (execQuota > 0) + { + const double flCurrentTime = Plat_FloatTime(); + + // Reset every second. + if ((flCurrentTime - s_flScriptExecTimeBase) > 1.0) + { + s_flScriptExecTimeBase = flCurrentTime; + s_nScriptExecCount = 0; + } + + if (s_nScriptExecCount >= execQuota) + { + DevWarning(eDLL_T::ENGINE, "Client is simulating and exec count = %d of %d; dropped exec command: %s\n", + s_nScriptExecCount, execQuota, args.ArgS()); + + return; + } + + s_nScriptExecCount++; + } } - - int numPlayers = g_pServer->GetNumClients(); - - // Already at max, don't create. - if (numPlayers >= g_ServerGlobalVariables->m_nMaxClients) - return; - - const char* playerName = args.Arg(1); - - int teamNum = atoi(args.Arg(2)); - int maxTeams = int(g_pServer->GetMaxTeams()) + 1; - - // Clamp team count, going above the limit will - // cause a crash. Going below 0 means that the - // engine will assign the bot to the last team. - if (teamNum > maxTeams) - teamNum = maxTeams; - - g_pEngineServer->LockNetworkStringTables(true); - - edict_t nHandle = g_pEngineServer->CreateFakeClient(playerName, teamNum); - g_pServerGameClients->ClientFullyConnect(nHandle, false); - - g_pEngineServer->LockNetworkStringTables(false); +#endif // !DEDICATED + v__Cmd_Exec_f(args); +} + + +void VCallback::Detour(const bool bAttach) const +{ + DetourSetup(&v__Cmd_Exec_f, &Cmd_Exec_f, bAttach); } -#endif // !CLIENT_DLL \ No newline at end of file diff --git a/r5dev/common/callback.h b/r5dev/common/callback.h index 050627cd..07426375 100644 --- a/r5dev/common/callback.h +++ b/r5dev/common/callback.h @@ -1,102 +1,52 @@ #pragma once -inline CMemory p_SetupGamemode; -inline bool(*SetupGamemode)(const char* pszPlayList); +inline bool(*v_SetupGamemode)(const char* pszPlayList); /* ==== CONCOMMANDCALLBACK ============================================================================================================================================== */ -inline CMemory p_DownloadPlaylists_f; -inline void(*_DownloadPlaylists_f)(void); +inline void(*v__Cmd_Exec_f)(const CCommand& args); /////////////////////////////////////////////////////////////////////////////// -void MP_GameMode_Changed_f(IConVar* pConVar, const char* pOldString, float flOldValue); -void MP_HostName_Changed_f(IConVar* pConVar, const char* pOldString, float flOldValue); -#ifndef DEDICATED -void ToggleConsole_f(const CCommand& args); -void ToggleBrowser_f(const CCommand& args); -#endif // !DEDICATED +void MP_GameMode_Changed_f(IConVar* pConVar, const char* pOldString); #ifndef CLIENT_DLL -void Host_Kick_f(const CCommand& args); -void Host_KickID_f(const CCommand& args); -void Host_Ban_f(const CCommand& args); -void Host_BanID_f(const CCommand& args); -void Host_Unban_f(const CCommand& args); -void Host_ReloadBanList_f(const CCommand& args); -void Host_ReloadPlaylists_f(const CCommand& args); void Host_Changelevel_f(const CCommand& args); -void Detour_HotSwap_f(const CCommand& args); #endif // !CLIENT_DLL -void Pak_ListPaks_f(const CCommand& args); -void Pak_ListTypes_f(const CCommand& args); -void Pak_RequestUnload_f(const CCommand& args); -void Pak_RequestLoad_f(const CCommand& args); -void Pak_Swap_f(const CCommand& args); -void RTech_StringToGUID_f(const CCommand& args); -void RTech_Decompress_f(const CCommand& args); void VPK_Pack_f(const CCommand& args); void VPK_Unpack_f(const CCommand& args); void VPK_Mount_f(const CCommand& args); void VPK_Unmount_f(const CCommand& args); -void NET_SetKey_f(const CCommand& args); -void NET_GenerateKey_f(const CCommand& args); -void NET_UseRandomKeyChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue); -void NET_UseSocketsForLoopbackChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue); -void SIG_GetAdr_f(const CCommand& args); -void CON_Help_f(const CCommand& args); +void NET_UseSocketsForLoopbackChanged_f(IConVar* pConVar, const char* pOldString); #ifndef DEDICATED -void CON_LogHistory_f(const CCommand& args); -void CON_RemoveLine_f(const CCommand& args); -void CON_ClearLines_f(const CCommand& args); -void CON_ClearHistory_f(const CCommand& args); -void RCON_CmdQuery_f(const CCommand& args); -void RCON_Disconnect_f(const CCommand& args); -void RCON_InputOnlyChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue); +void GFX_NVN_Changed_f(IConVar* pConVar, const char* pOldString); #endif // !DEDICATED -void RCON_PasswordChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue); -#ifndef CLIENT_DLL -void SV_LanguageChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue); -void RCON_WhiteListAddresChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue); -void RCON_ConnectionCountChanged_f(IConVar* pConVar, const char* pOldString, float flOldValue); -void SQVM_ServerScript_f(const CCommand& args); -#endif // !CLIENT_DLL +void LanguageChanged_f(IConVar* pConVar, const char* pOldString); #ifndef DEDICATED -void SQVM_ClientScript_f(const CCommand& args); -void SQVM_UIScript_f(const CCommand& args); void Mat_CrossHair_f(const CCommand& args); void Line_f(const CCommand& args); void Sphere_f(const CCommand& args); void Capsule_f(const CCommand& args); #endif // !DEDICATED -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) void BHit_f(const CCommand& args); -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 void CVHelp_f(const CCommand& args); void CVList_f(const CCommand& args); void CVDiff_f(const CCommand& args); void CVFlag_f(const CCommand& args); -#ifndef CLIENT_DLL -void CC_CreateFakePlayer_f(const CCommand& args); -#endif // !CLIENT_DLL /////////////////////////////////////////////////////////////////////////////// class VCallback : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("SetupGamemode", p_SetupGamemode.GetPtr()); - LogFunAdr("DownloadPlaylist_f", p_DownloadPlaylists_f.GetPtr()); + LogFunAdr("SetupGamemode", v_SetupGamemode); + LogFunAdr("Cmd_Exec_f", v__Cmd_Exec_f); } virtual void GetFun(void) const { - p_SetupGamemode = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 C7 C0 ?? ?? ?? ??"); - p_DownloadPlaylists_f = g_GameDll.FindPatternSIMD("33 C9 C6 05 ?? ?? ?? ?? ?? E9 ?? ?? ?? ??"); - - SetupGamemode = p_SetupGamemode.RCast(); /*40 53 48 83 EC 20 48 8B D9 48 C7 C0 ?? ?? ?? ??*/ - _DownloadPlaylists_f = p_DownloadPlaylists_f.RCast(); /*33 C9 C6 05 ?? ?? ?? ?? ?? E9 ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 C7 C0 ?? ?? ?? ??").GetPtr(v_SetupGamemode); + g_GameDll.FindPatternSIMD("40 55 53 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B D9").GetPtr(v__Cmd_Exec_f); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/common/completion.cpp b/r5dev/common/completion.cpp index a94918b9..fb5487d7 100644 --- a/r5dev/common/completion.cpp +++ b/r5dev/common/completion.cpp @@ -27,7 +27,7 @@ int _Host_Map_f_CompletionFunc(char const* cmdname, char const* partial, char co substring = (char*)partial + strlen(cmdname); } - const int mapcount = (int)g_InstalledMaps.size(); + const int mapcount = g_InstalledMaps.Count(); const int longest = COMMAND_COMPLETION_ITEM_LENGTH; const int count = MIN(mapcount, COMMAND_COMPLETION_MAXITEMS); @@ -36,9 +36,9 @@ int _Host_Map_f_CompletionFunc(char const* cmdname, char const* partial, char co { for (int i = 0; i < count; i++) { - if (strstr(g_InstalledMaps[i].c_str(), substring)) + if (strstr(g_InstalledMaps[i].String(), substring)) { - strncpy(commands[filtered_count], g_InstalledMaps[i].c_str(), longest); + strncpy(commands[filtered_count], g_InstalledMaps[i].String(), longest); char old[COMMAND_COMPLETION_ITEM_LENGTH]; strncpy(old, commands[filtered_count], sizeof(old)); @@ -170,6 +170,18 @@ int RTech_PakUnload_f_CompletionFunc(char const* partial, char commands[COMMAND_ return _Host_Pak_f_CompletionFunc(&s_PakUnloadAutoFileList, partial, commands); } +static CBaseAutoCompleteFileList s_PakCompress("pak_compress", "paks/Win64_override", "rpak"); +//----------------------------------------------------------------------------- +// Purpose: +// Input : *partial - +// **commands - +// Output : int +//----------------------------------------------------------------------------- +int RTech_PakCompress_f_CompletionFunc(char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]) +{ + return _Host_Pak_f_CompletionFunc(&s_PakCompress, partial, commands); +} + static CBaseAutoCompleteFileList s_PakDecompress("pak_decompress", "paks/Win64", "rpak"); //----------------------------------------------------------------------------- // Purpose: diff --git a/r5dev/common/completion.h b/r5dev/common/completion.h index 66683236..583394f1 100644 --- a/r5dev/common/completion.h +++ b/r5dev/common/completion.h @@ -11,10 +11,10 @@ int Game_Give_f_CompletionFunc(char const* partial, char commands[COMMAND_COMPLE int RTech_PakLoad_f_CompletionFunc(char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); int RTech_PakUnload_f_CompletionFunc(char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); +int RTech_PakCompress_f_CompletionFunc(char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); int RTech_PakDecompress_f_CompletionFunc(char const* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); -inline CMemory p_CBaseAutoCompleteFileList_AutoCompletionFunc; -inline int(*v_CBaseAutoCompleteFileList_AutoCompletionFunc) +inline int(*CBaseAutoCompleteFileList__AutoCompletionFunc) (CBaseAutoCompleteFileList* thisp, const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH]); /////////////////////////////////////////////////////////////////////////////// @@ -22,21 +22,14 @@ class VCompletion : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CBaseAutoCompleteFileList::AutoCompletionFunc", p_CBaseAutoCompleteFileList_AutoCompletionFunc.GetPtr()); + LogFunAdr("CBaseAutoCompleteFileList::AutoCompletionFunc", CBaseAutoCompleteFileList__AutoCompletionFunc); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CBaseAutoCompleteFileList_AutoCompletionFunc = g_GameDll.FindPatternSIMD("40 55 53 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B 39"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CBaseAutoCompleteFileList_AutoCompletionFunc = g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 55 41 54"); -#endif - v_CBaseAutoCompleteFileList_AutoCompletionFunc = p_CBaseAutoCompleteFileList_AutoCompletionFunc.RCast(); + g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 55 41 54").GetPtr(CBaseAutoCompleteFileList__AutoCompletionFunc); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/common/global.cpp b/r5dev/common/global.cpp index 101b9c86..d18725d6 100644 --- a/r5dev/common/global.cpp +++ b/r5dev/common/global.cpp @@ -10,9 +10,13 @@ #include "callback.h" #include "global.h" + +ConVar curl_debug("curl_debug", "0", FCVAR_DEVELOPMENTONLY, "Determines whether or not to enable curl debug logging.", "1 = curl logs; 0 (zero) = no logs"); +ConVar curl_timeout("curl_timeout", "15", FCVAR_DEVELOPMENTONLY, "Maximum time in seconds a curl transfer operation could take."); +ConVar ssl_verify_peer("ssl_verify_peer", "1", FCVAR_DEVELOPMENTONLY, "Verify the authenticity of the peer's SSL certificate.", "1 = curl verifies; 0 (zero) = no verification"); + //----------------------------------------------------------------------------- // ENGINE | -ConVar* sdk_fixedframe_tickinterval = nullptr; ConVar* single_frame_shutdown_for_reload = nullptr; ConVar* old_gather_props = nullptr; @@ -21,12 +25,14 @@ ConVar* debug_draw_box_depth_test = nullptr; ConVar* developer = nullptr; ConVar* fps_max = nullptr; +ConVar* fps_max_vsync = nullptr; -// Taken from S15: -ConVar* usercmd_frametime_max = nullptr; -ConVar* usercmd_frametime_min = nullptr; +#ifndef DEDICATED +ConVar* in_syncRT = nullptr; +#endif // !DEDICATED -ConVar* usercmd_dualwield_enable = nullptr; +ConVar* base_tickinterval_sp = nullptr; +ConVar* base_tickinterval_mp = nullptr; ConVar* staticProp_no_fade_scalar = nullptr; ConVar* staticProp_gather_size_weight = nullptr; @@ -36,7 +42,6 @@ ConVar* model_defaultFadeDistMin = nullptr; ConVar* ip_cvar = nullptr; ConVar* hostname = nullptr; -ConVar* hostdesc = nullptr; ConVar* hostip = nullptr; ConVar* hostport = nullptr; @@ -45,17 +50,6 @@ ConVar* host_timescale = nullptr; ConVar* mp_gamemode = nullptr; -ConVar* rcon_address = nullptr; -ConVar* rcon_password = nullptr; - -ConVar* r_debug_overlay_nodecay = nullptr; -ConVar* r_debug_overlay_invisible = nullptr; -ConVar* r_debug_overlay_wireframe = nullptr; -ConVar* r_debug_draw_depth_test = nullptr; -ConVar* r_drawWorldMeshes = nullptr; -ConVar* r_drawWorldMeshesDepthOnly = nullptr; -ConVar* r_drawWorldMeshesDepthAtTheEnd = nullptr; - #ifndef DEDICATED ConVar* r_visualizetraces = nullptr; ConVar* r_visualizetraces_duration = nullptr; @@ -63,399 +57,75 @@ ConVar* r_visualizetraces_duration = nullptr; ConVar* stream_overlay = nullptr; ConVar* stream_overlay_mode = nullptr; -//----------------------------------------------------------------------------- -// SHARED | -ConVar* modsystem_enable = nullptr; -ConVar* modsystem_debug = nullptr; + +ConVar* eula_version = nullptr; +ConVar* eula_version_accepted = nullptr; + +ConVar* language_cvar = nullptr; //----------------------------------------------------------------------------- // SERVER | #ifndef CLIENT_DLL -ConVar* ai_ainDumpOnLoad = nullptr; -ConVar* ai_ainDebugConnect = nullptr; ConVar* ai_script_nodes_draw = nullptr; -ConVar* ai_script_nodes_draw_range = nullptr; -ConVar* ai_script_nodes_draw_nearest = nullptr; -ConVar* navmesh_always_reachable = nullptr; -ConVar* navmesh_debug_type = nullptr; -ConVar* navmesh_debug_tile_range = nullptr; -ConVar* navmesh_debug_camera_range = nullptr; -#ifndef DEDICATED -ConVar* navmesh_draw_bvtree = nullptr; -ConVar* navmesh_draw_portal = nullptr; -ConVar* navmesh_draw_polys = nullptr; -ConVar* navmesh_draw_poly_bounds = nullptr; -ConVar* navmesh_draw_poly_bounds_inner = nullptr; -#endif // !DEDICATED - -ConVar* sv_language = nullptr; - -ConVar* sv_showconnecting = nullptr; -ConVar* sv_globalBanlist = nullptr; -ConVar* sv_pylonVisibility = nullptr; -ConVar* sv_pylonRefreshRate = nullptr; -ConVar* sv_banlistRefreshRate = nullptr; -ConVar* sv_statusRefreshRate = nullptr; ConVar* sv_forceChatToTeamOnly = nullptr; ConVar* sv_single_core_dedi = nullptr; -ConVar* sv_updaterate_mp = nullptr; -ConVar* sv_updaterate_sp = nullptr; -ConVar* sv_autoReloadRate = nullptr; +ConVar* sv_maxunlag = nullptr; +ConVar* sv_clockcorrection_msecs = nullptr; + +ConVar* sv_updaterate_sp = nullptr; +ConVar* sv_updaterate_mp = nullptr; -ConVar* sv_simulateBots = nullptr; ConVar* sv_showhitboxes = nullptr; ConVar* sv_stats = nullptr; -ConVar* sv_quota_stringCmdsPerSecond = nullptr; - -ConVar* sv_validatePersonaName = nullptr; -ConVar* sv_minPersonaNameLength = nullptr; -ConVar* sv_maxPersonaNameLength = nullptr; - ConVar* sv_voiceEcho = nullptr; ConVar* sv_voiceenable = nullptr; ConVar* sv_alltalk = nullptr; ConVar* player_userCmdsQueueWarning = nullptr; -//#ifdef DEDICATED -ConVar* sv_rcon_debug = nullptr; -ConVar* sv_rcon_sendlogs = nullptr; -//ConVar* sv_rcon_banpenalty = nullptr; // TODO -ConVar* sv_rcon_maxfailures = nullptr; -ConVar* sv_rcon_maxignores = nullptr; -ConVar* sv_rcon_maxsockets = nullptr; -ConVar* sv_rcon_maxconnections = nullptr; -ConVar* sv_rcon_maxpacketsize = nullptr; -ConVar* sv_rcon_whitelist_address = nullptr; -//#endif // DEDICATED #endif // !CLIENT_DLL ConVar* sv_cheats = nullptr; ConVar* sv_visualizetraces = nullptr; ConVar* sv_visualizetraces_duration = nullptr; -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) ConVar* bhit_enable = nullptr; -ConVar* bhit_depth_test = nullptr; -ConVar* bhit_abs_origin = nullptr; -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 //----------------------------------------------------------------------------- // CLIENT | #ifndef DEDICATED -ConVar* cl_rcon_inputonly = nullptr; -ConVar* cl_quota_stringCmdsPerSecond = nullptr; - -ConVar* cl_cmdrate = nullptr; -ConVar* cl_move_use_dt = nullptr; - -ConVar* cl_notify_invert_x = nullptr; -ConVar* cl_notify_invert_y = nullptr; -ConVar* cl_notify_offset_x = nullptr; -ConVar* cl_notify_offset_y = nullptr; - -ConVar* cl_showsimstats = nullptr; -ConVar* cl_simstats_invert_x = nullptr; -ConVar* cl_simstats_invert_y = nullptr; -ConVar* cl_simstats_offset_x = nullptr; -ConVar* cl_simstats_offset_y = nullptr; - -ConVar* cl_showgpustats = nullptr; -ConVar* cl_gpustats_invert_x = nullptr; -ConVar* cl_gpustats_invert_y = nullptr; -ConVar* cl_gpustats_offset_x = nullptr; -ConVar* cl_gpustats_offset_y = nullptr; - -ConVar* cl_showmaterialinfo = nullptr; -ConVar* cl_materialinfo_offset_x = nullptr; -ConVar* cl_materialinfo_offset_y = nullptr; +ConVar* cl_updaterate_mp = nullptr; ConVar* cl_threaded_bone_setup = nullptr; -ConVar* con_drawnotify = nullptr; -ConVar* con_notifylines = nullptr; -ConVar* con_notifytime = nullptr; - -ConVar* con_notify_invert_x = nullptr; -ConVar* con_notify_invert_y = nullptr; -ConVar* con_notify_offset_x = nullptr; -ConVar* con_notify_offset_y = nullptr; - -ConVar* con_notify_script_server_clr = nullptr; -ConVar* con_notify_script_client_clr = nullptr; -ConVar* con_notify_script_ui_clr = nullptr; -ConVar* con_notify_native_server_clr = nullptr; -ConVar* con_notify_native_client_clr = nullptr; -ConVar* con_notify_native_ui_clr = nullptr; -ConVar* con_notify_native_engine_clr = nullptr; -ConVar* con_notify_native_fs_clr = nullptr; -ConVar* con_notify_native_rtech_clr = nullptr; -ConVar* con_notify_native_ms_clr = nullptr; -ConVar* con_notify_native_audio_clr = nullptr; -ConVar* con_notify_native_video_clr = nullptr; -ConVar* con_notify_netcon_clr = nullptr; -ConVar* con_notify_common_clr = nullptr; -ConVar* con_notify_warning_clr = nullptr; -ConVar* con_notify_error_clr = nullptr; - -ConVar* con_max_lines = nullptr; -ConVar* con_max_history = nullptr; -ConVar* con_suggest_limit = nullptr; -ConVar* con_suggest_showhelptext = nullptr; -ConVar* con_suggest_showflags = nullptr; - ConVar* origin_disconnectWhenOffline = nullptr; ConVar* discord_updatePresence = nullptr; - -ConVar* serverbrowser_hideEmptyServers = nullptr; -ConVar* serverbrowser_mapFilter = nullptr; -ConVar* serverbrowser_gamemodeFilter = nullptr; #endif // !DEDICATED //----------------------------------------------------------------------------- // FILESYSTEM | -ConVar* fs_showWarnings = nullptr; ConVar* fs_showAllReads = nullptr; -ConVar* fs_packedstore_entryblock_stats = nullptr; -ConVar* fs_packedstore_workspace = nullptr; -ConVar* fs_packedstore_compression_level = nullptr; -ConVar* fs_packedstore_max_helper_threads = nullptr; -//----------------------------------------------------------------------------- -// MATERIALSYSTEM | -#ifndef DEDICATED -ConVar* mat_alwaysComplain = nullptr; -#endif // !DEDICATED -//----------------------------------------------------------------------------- -// SQUIRREL | -ConVar* script_show_output = nullptr; -ConVar* script_show_warning = nullptr; //----------------------------------------------------------------------------- // NETCHANNEL | -ConVar* net_tracePayload = nullptr; -ConVar* net_encryptionEnable = nullptr; -ConVar* net_useRandomKey = nullptr; -ConVar* net_usesocketsforloopback = nullptr; -ConVar* net_processTimeBudget = nullptr; - +ConVar* net_usesocketsforloopback; +ConVar* net_data_block_enabled = nullptr; ConVar* net_datablock_networkLossForSlowSpeed = nullptr; +ConVar* net_compressDataBlock = nullptr; -ConVar* pylon_matchmaking_hostname = nullptr; -ConVar* pylon_host_update_interval = nullptr; -ConVar* pylon_showdebuginfo = nullptr; - -ConVar* ssl_verify_peer = nullptr; -ConVar* curl_timeout = nullptr; -ConVar* curl_debug = nullptr; -//----------------------------------------------------------------------------- -// RTECH API | -ConVar* rtech_debug = nullptr; +ConVar* net_showmsg = nullptr; +ConVar* net_blockmsg = nullptr; +ConVar* net_showpeaks = nullptr; //----------------------------------------------------------------------------- // RUI | #ifndef DEDICATED -ConVar* rui_drawEnable = nullptr; ConVar* rui_defaultDebugFontFace = nullptr; #endif // !DEDICATED //----------------------------------------------------------------------------- // MILES | #ifndef DEDICATED -ConVar* miles_debug = nullptr; ConVar* miles_language = nullptr; #endif -//----------------------------------------------------------------------------- -// Purpose: initialize ConVar's -//----------------------------------------------------------------------------- -void ConVar_StaticInit(void) -{ - //------------------------------------------------------------------------- - // ENGINE | - hostdesc = ConVar::StaticCreate("hostdesc", "", FCVAR_RELEASE, "Host game server description.", false, 0.f, false, 0.f, nullptr, nullptr); - sdk_fixedframe_tickinterval = ConVar::StaticCreate("sdk_fixedframe_tickinterval", "0.01", FCVAR_RELEASE, "The tick interval used by the SDK fixed frame.", false, 0.f, false, 0.f, nullptr, nullptr); - - curl_debug = ConVar::StaticCreate("curl_debug" , "0" , FCVAR_DEVELOPMENTONLY, "Determines whether or not to enable curl debug logging.", false, 0.f, false, 0.f, nullptr, "1 = curl logs; 0 (zero) = no logs"); - curl_timeout = ConVar::StaticCreate("curl_timeout" , "15", FCVAR_DEVELOPMENTONLY, "Maximum time in seconds a curl transfer operation could take.", false, 0.f, false, 0.f, nullptr, nullptr); - ssl_verify_peer = ConVar::StaticCreate("ssl_verify_peer", "1" , FCVAR_DEVELOPMENTONLY, "Verify the authenticity of the peer's SSL certificate.", false, 0.f, false, 0.f, nullptr, "1 = curl verifies; 0 (zero) = no verification"); - - rcon_address = ConVar::StaticCreate("rcon_address", "[loopback]:37015", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote server access address.", false, 0.f, false, 0.f, nullptr, nullptr); - rcon_password = ConVar::StaticCreate("rcon_password", "" , FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote server access password (rcon is disabled if empty).", false, 0.f, false, 0.f, &RCON_PasswordChanged_f, nullptr); - - r_debug_overlay_nodecay = ConVar::StaticCreate("r_debug_overlay_nodecay" , "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Keeps all debug overlays alive regardless of their lifetime. Use command 'clear_debug_overlays' to clear everything.", false, 0.f, false, 0.f, nullptr, nullptr); - r_debug_overlay_invisible = ConVar::StaticCreate("r_debug_overlay_invisible" , "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Show invisible debug overlays (alpha < 1 = 255).", false, 0.f, false, 0.f, nullptr, nullptr); - r_debug_overlay_wireframe = ConVar::StaticCreate("r_debug_overlay_wireframe" , "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Use wireframe in debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - r_debug_draw_depth_test = ConVar::StaticCreate("r_debug_draw_depth_test" , "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Toggle depth test for other debug draw functionality.", false, 0.f, false, 0.f, nullptr, nullptr); - r_drawWorldMeshes = ConVar::StaticCreate("r_drawWorldMeshes" , "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Render world meshes.", false, 0.f, false, 0.f, nullptr, nullptr); - r_drawWorldMeshesDepthOnly = ConVar::StaticCreate("r_drawWorldMeshesDepthOnly" , "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Render world meshes (depth only).", false, 0.f, false, 0.f, nullptr, nullptr); - r_drawWorldMeshesDepthAtTheEnd = ConVar::StaticCreate("r_drawWorldMeshesDepthAtTheEnd", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Render world meshes (depth at the end).", false, 0.f, false, 0.f, nullptr, nullptr); - - //------------------------------------------------------------------------- - // SHARED | - modsystem_enable = ConVar::StaticCreate("modsystem_enable", "1", FCVAR_RELEASE, "Enable the modsystem.", false, 0.f, false, 0.f, nullptr, nullptr); - modsystem_debug = ConVar::StaticCreate("modsystem_debug" , "0", FCVAR_RELEASE, "Debug the modsystem." , false, 0.f, false, 0.f, nullptr, nullptr); - - //------------------------------------------------------------------------- - // SERVER | -#ifndef CLIENT_DLL - ai_ainDumpOnLoad = ConVar::StaticCreate("ai_ainDumpOnLoad" , "0", FCVAR_DEVELOPMENTONLY, "Dumps AIN data from node graphs loaded from the disk on load.", false, 0.f, false, 0.f, nullptr, nullptr); - ai_ainDebugConnect = ConVar::StaticCreate("ai_ainDebugConnect" , "0", FCVAR_DEVELOPMENTONLY, "Debug AIN node connections.", false, 0.f, false, 0.f, nullptr, nullptr); - ai_script_nodes_draw_range = ConVar::StaticCreate("ai_script_nodes_draw_range" , "0", FCVAR_DEVELOPMENTONLY, "Debug draw AIN script nodes ranging from shift index to this cvar.", false, 0.f, false, 0.f, nullptr, nullptr); - ai_script_nodes_draw_nearest = ConVar::StaticCreate("ai_script_nodes_draw_nearest", "1", FCVAR_DEVELOPMENTONLY, "Debug draw AIN script node links to nearest node (build order is used if null).", false, 0.f, false, 0.f, nullptr, nullptr); - - navmesh_always_reachable = ConVar::StaticCreate("navmesh_always_reachable" , "0" , FCVAR_DEVELOPMENTONLY, "Marks goal poly from agent poly as reachable regardless of table data ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); - navmesh_debug_type = ConVar::StaticCreate("navmesh_debug_type" , "0" , FCVAR_DEVELOPMENTONLY, "NavMesh debug draw hull index.", true, 0.f, true, 4.f, nullptr, "0 = small, 1 = med_short, 2 = medium, 3 = large, 4 = extra large"); - navmesh_debug_tile_range = ConVar::StaticCreate("navmesh_debug_tile_range" , "0" , FCVAR_DEVELOPMENTONLY, "NavMesh debug draw tiles ranging from shift index to this cvar.", true, 0.f, false, 0.f, nullptr, nullptr); - navmesh_debug_camera_range = ConVar::StaticCreate("navmesh_debug_camera_range" , "2000" , FCVAR_DEVELOPMENTONLY, "Only debug draw tiles within this distance from camera origin.", true, 0.f, false, 0.f, nullptr, nullptr); -#ifndef DEDICATED - navmesh_draw_bvtree = ConVar::StaticCreate("navmesh_draw_bvtree" , "-1", FCVAR_DEVELOPMENTONLY, "Draws the BVTree of the NavMesh tiles.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount"); - navmesh_draw_portal = ConVar::StaticCreate("navmesh_draw_portal" , "-1", FCVAR_DEVELOPMENTONLY, "Draws the portal of the NavMesh tiles.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount"); - navmesh_draw_polys = ConVar::StaticCreate("navmesh_draw_polys" , "-1", FCVAR_DEVELOPMENTONLY, "Draws the polys of the NavMesh tiles.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount"); - navmesh_draw_poly_bounds = ConVar::StaticCreate("navmesh_draw_poly_bounds" , "-1", FCVAR_DEVELOPMENTONLY, "Draws the bounds of the NavMesh polys.", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount"); - navmesh_draw_poly_bounds_inner = ConVar::StaticCreate("navmesh_draw_poly_bounds_inner" , "0" , FCVAR_DEVELOPMENTONLY, "Draws the inner bounds of the NavMesh polys (requires navmesh_draw_poly_bounds).", false, 0.f, false, 0.f, nullptr, "Index: > 0 && < mesh->m_tileCount"); -#endif // !DEDICATED - - sv_language = ConVar::StaticCreate("sv_language", "english", FCVAR_RELEASE, "Language of the server. Sent to MasterServer for localising error messages.", false, 0.f, false, 0.f, SV_LanguageChanged_f, nullptr); - sv_showconnecting = ConVar::StaticCreate("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr); - sv_globalBanlist = ConVar::StaticCreate("sv_globalBanlist" , "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, nullptr, "0 = Disable, 1 = Enable."); - sv_pylonVisibility = ConVar::StaticCreate("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", false, 0.f, false, 0.f, nullptr, "0 = Offline, 1 = Hidden, 2 = Public."); - sv_pylonRefreshRate = ConVar::StaticCreate("sv_pylonRefreshRate" , "5.0" , FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds).", true, 2.f, true, 8.f, nullptr, nullptr); - sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "30.0", FCVAR_DEVELOPMENTONLY, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr); - sv_statusRefreshRate = ConVar::StaticCreate("sv_statusRefreshRate" , "0.5", FCVAR_RELEASE, "Server status refresh rate (seconds).", true, 0.f, false, 0.f, nullptr, nullptr); - sv_autoReloadRate = ConVar::StaticCreate("sv_autoReloadRate" , "0" , FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null).", true, 0.f, false, 0.f, nullptr, nullptr); - sv_simulateBots = ConVar::StaticCreate("sv_simulateBots", "1", FCVAR_RELEASE, "Simulate user commands for bots on the server.", true, 0.f, false, 0.f, nullptr, nullptr); - - sv_rcon_debug = ConVar::StaticCreate("sv_rcon_debug" , "0" , FCVAR_RELEASE, "Show rcon debug information ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); - sv_rcon_sendlogs = ConVar::StaticCreate("sv_rcon_sendlogs" , "0" , FCVAR_RELEASE, "Network console logs to connected and authenticated sockets.", false, 0.f, false, 0.f, nullptr, nullptr); - //sv_rcon_banpenalty = ConVar::StaticCreate("sv_rcon_banpenalty" , "10", FCVAR_RELEASE, "Number of minutes to ban users who fail rcon authentication.", false, 0.f, false, 0.f, nullptr, nullptr); - sv_rcon_maxfailures = ConVar::StaticCreate("sv_rcon_maxfailures", "10", FCVAR_RELEASE, "Max number of times an user can fail rcon authentication before being banned.", true, 1.f, false, 0.f, nullptr, nullptr); - sv_rcon_maxignores = ConVar::StaticCreate("sv_rcon_maxignores" , "15", FCVAR_RELEASE, "Max number of times an user can ignore the instruction message before being banned.", true, 1.f, false, 0.f, nullptr, nullptr); - sv_rcon_maxsockets = ConVar::StaticCreate("sv_rcon_maxsockets" , "32", FCVAR_RELEASE, "Max number of accepted sockets before the server starts closing redundant sockets.", true, 1.f, true, MAX_PLAYERS, nullptr, nullptr); - sv_rcon_maxconnections = ConVar::StaticCreate("sv_rcon_maxconnections" , "1" , FCVAR_RELEASE, "Max number of authenticated connections before the server closes the listen socket.", true, 1.f, true, MAX_PLAYERS, &RCON_ConnectionCountChanged_f, nullptr); - sv_rcon_maxpacketsize = ConVar::StaticCreate("sv_rcon_maxpacketsize" , "1024", FCVAR_RELEASE, "Max number of bytes allowed in a command packet from a non-authenticated netconsole.", true, 0.f, false, 0.f, nullptr, nullptr); - sv_rcon_whitelist_address = ConVar::StaticCreate("sv_rcon_whitelist_address", "" , FCVAR_RELEASE, "This address is not considered a 'redundant' socket and will never be banned for failed authentication attempts.", false, 0.f, false, 0.f, &RCON_WhiteListAddresChanged_f, "Format: '::ffff:127.0.0.1'"); - - sv_quota_stringCmdsPerSecond = ConVar::StaticCreate("sv_quota_stringCmdsPerSecond", "16", FCVAR_RELEASE, "How many string commands per second clients are allowed to submit, 0 to disallow all string commands.", true, 0.f, false, 0.f, nullptr, nullptr); - sv_validatePersonaName = ConVar::StaticCreate("sv_validatePersonaName" , "1" , FCVAR_RELEASE, "Validate the client's textual persona name on connect.", true, 0.f, false, 0.f, nullptr, nullptr); - sv_minPersonaNameLength = ConVar::StaticCreate("sv_minPersonaNameLength", "4" , FCVAR_RELEASE, "The minimum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr); - sv_maxPersonaNameLength = ConVar::StaticCreate("sv_maxPersonaNameLength", "16", FCVAR_RELEASE, "The maximum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr); -#endif // !CLIENT_DLL -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) - bhit_depth_test = ConVar::StaticCreate("bhit_depth_test", "0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Use depth test for bullet ray trace overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - bhit_abs_origin = ConVar::StaticCreate("bhit_abs_origin", "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Draw entity's predicted abs origin upon bullet impact for trajectory debugging (requires 'r_visualizetraces' to be set!).", false, 0.f, false, 0.f, nullptr, nullptr); -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 - //------------------------------------------------------------------------- - // CLIENT | -#ifndef DEDICATED - cl_rcon_inputonly = ConVar::StaticCreate("cl_rcon_inputonly", "0" , FCVAR_RELEASE, "Tells the rcon server whether or not we are input only.", false, 0.f, false, 0.f, RCON_InputOnlyChanged_f, nullptr); - cl_quota_stringCmdsPerSecond = ConVar::StaticCreate("cl_quota_stringCmdsPerSecond", "16" , FCVAR_RELEASE, "How many string commands per second user is allowed to submit, 0 to allow all submissions.", true, 0.f, false, 0.f, nullptr, nullptr); - - cl_notify_invert_x = ConVar::StaticCreate("cl_notify_invert_x", "0", FCVAR_DEVELOPMENTONLY, "Inverts the X offset for console notify debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_notify_invert_y = ConVar::StaticCreate("cl_notify_invert_y", "0", FCVAR_DEVELOPMENTONLY, "Inverts the Y offset for console notify debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_notify_offset_x = ConVar::StaticCreate("cl_notify_offset_x", "10", FCVAR_DEVELOPMENTONLY, "X offset for console notify debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_notify_offset_y = ConVar::StaticCreate("cl_notify_offset_y", "10", FCVAR_DEVELOPMENTONLY, "Y offset for console notify debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - - cl_showsimstats = ConVar::StaticCreate("cl_showsimstats" , "0" , FCVAR_DEVELOPMENTONLY, "Shows the tick counter for the server/client simulation and the render frame.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_simstats_invert_x = ConVar::StaticCreate("cl_simstats_invert_x", "1" , FCVAR_DEVELOPMENTONLY, "Inverts the X offset for simulation debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_simstats_invert_y = ConVar::StaticCreate("cl_simstats_invert_y", "1" , FCVAR_DEVELOPMENTONLY, "Inverts the Y offset for simulation debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_simstats_offset_x = ConVar::StaticCreate("cl_simstats_offset_x", "650", FCVAR_DEVELOPMENTONLY, "X offset for simulation debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_simstats_offset_y = ConVar::StaticCreate("cl_simstats_offset_y", "120", FCVAR_DEVELOPMENTONLY, "Y offset for simulation debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - - cl_showgpustats = ConVar::StaticCreate("cl_showgpustats" , "0", FCVAR_DEVELOPMENTONLY, "Texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_gpustats_invert_x = ConVar::StaticCreate("cl_gpustats_invert_x", "1", FCVAR_DEVELOPMENTONLY, "Inverts the X offset for texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_gpustats_invert_y = ConVar::StaticCreate("cl_gpustats_invert_y", "1", FCVAR_DEVELOPMENTONLY, "Inverts the Y offset for texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_gpustats_offset_x = ConVar::StaticCreate("cl_gpustats_offset_x", "650", FCVAR_DEVELOPMENTONLY, "X offset for texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_gpustats_offset_y = ConVar::StaticCreate("cl_gpustats_offset_y", "105", FCVAR_DEVELOPMENTONLY, "Y offset for texture streaming debug overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - - cl_showmaterialinfo = ConVar::StaticCreate("cl_showmaterialinfo" , "0" , FCVAR_DEVELOPMENTONLY, "Draw info for the material under the crosshair on screen.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_materialinfo_offset_x = ConVar::StaticCreate("cl_materialinfo_offset_x", "0" , FCVAR_DEVELOPMENTONLY, "X offset for material debug info overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - cl_materialinfo_offset_y = ConVar::StaticCreate("cl_materialinfo_offset_y", "420", FCVAR_DEVELOPMENTONLY, "Y offset for material debug info overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - - con_drawnotify = ConVar::StaticCreate("con_drawnotify", "0", FCVAR_RELEASE, "Draws the RUI console to the hud.", false, 0.f, false, 0.f, nullptr, nullptr); - con_notifylines = ConVar::StaticCreate("con_notifylines" , "3" , FCVAR_MATERIAL_SYSTEM_THREAD, "Number of console lines to overlay for debugging.", true, 1.f, false, 0.f, nullptr, nullptr); - con_notifytime = ConVar::StaticCreate("con_notifytime" , "6" , FCVAR_MATERIAL_SYSTEM_THREAD, "How long to display recent console text to the upper part of the game window.", false, 1.f, false, 50.f, nullptr, nullptr); - - con_notify_invert_x = ConVar::StaticCreate("con_notify_invert_x", "0" , FCVAR_MATERIAL_SYSTEM_THREAD, "Inverts the X offset for RUI console overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - con_notify_invert_y = ConVar::StaticCreate("con_notify_invert_y", "0" , FCVAR_MATERIAL_SYSTEM_THREAD, "Inverts the Y offset for RUI console overlay.", false, 0.f, false, 0.f, nullptr, nullptr); - con_notify_offset_x = ConVar::StaticCreate("con_notify_offset_x", "10", FCVAR_MATERIAL_SYSTEM_THREAD, "X offset for RUI console overlay.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_offset_y = ConVar::StaticCreate("con_notify_offset_y", "10", FCVAR_MATERIAL_SYSTEM_THREAD, "Y offset for RUI console overlay.", false, 1.f, false, 50.f, nullptr, nullptr); - - con_notify_script_server_clr = ConVar::StaticCreate("con_notify_script_server_clr", "130 120 245 255", FCVAR_MATERIAL_SYSTEM_THREAD, "Script SERVER VM RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_script_client_clr = ConVar::StaticCreate("con_notify_script_client_clr", "117 116 139 255", FCVAR_MATERIAL_SYSTEM_THREAD, "Script CLIENT VM RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_script_ui_clr = ConVar::StaticCreate("con_notify_script_ui_clr" , "200 110 110 255", FCVAR_MATERIAL_SYSTEM_THREAD, "Script UI VM RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - - con_notify_native_server_clr = ConVar::StaticCreate("con_notify_native_server_clr", "20 50 248 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native SERVER RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_client_clr = ConVar::StaticCreate("con_notify_native_client_clr", "70 70 70 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native CLIENT RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_ui_clr = ConVar::StaticCreate("con_notify_native_ui_clr" , "200 60 60 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native UI RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_engine_clr = ConVar::StaticCreate("con_notify_native_engine_clr", "255 255 255 255", FCVAR_MATERIAL_SYSTEM_THREAD, "Native engine RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_fs_clr = ConVar::StaticCreate("con_notify_native_fs_clr" , "0 100 225 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native FileSystem RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_rtech_clr = ConVar::StaticCreate("con_notify_native_rtech_clr" , "25 120 20 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native RTech RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_ms_clr = ConVar::StaticCreate("con_notify_native_ms_clr" , "200 20 180 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native MaterialSystem RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_audio_clr = ConVar::StaticCreate("con_notify_native_audio_clr" , "238 43 10 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native AudioSystem RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_native_video_clr = ConVar::StaticCreate("con_notify_native_video_clr" , "115 0 235 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Native VideoSystem RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - - con_notify_netcon_clr = ConVar::StaticCreate("con_notify_netcon_clr" , "255 255 255 255", FCVAR_MATERIAL_SYSTEM_THREAD, "Netconsole RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_common_clr = ConVar::StaticCreate("con_notify_common_clr" , "255 140 80 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Common RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - - con_notify_warning_clr = ConVar::StaticCreate("con_notify_warning_clr", "180 180 20 255", FCVAR_MATERIAL_SYSTEM_THREAD, "Warning RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - con_notify_error_clr = ConVar::StaticCreate("con_notify_error_clr" , "225 20 20 255" , FCVAR_MATERIAL_SYSTEM_THREAD, "Error RUI console overlay log color.", false, 1.f, false, 50.f, nullptr, nullptr); - - con_max_lines = ConVar::StaticCreate("con_max_lines" , "1024", FCVAR_DEVELOPMENTONLY, "Maximum number of lines in the console before cleanup starts.", true, 1.f, false, 0.f, nullptr, nullptr); - con_max_history = ConVar::StaticCreate("con_max_history" , "512" , FCVAR_DEVELOPMENTONLY, "Maximum number of command submission items before history cleanup starts.", true, 0.f, false, 0.f, nullptr, nullptr); - con_suggest_limit = ConVar::StaticCreate("con_suggest_limit" , "128" , FCVAR_DEVELOPMENTONLY, "Maximum number of suggestions the autocomplete window will show for the console.", true, 0.f, false, 0.f, nullptr, nullptr); - con_suggest_showhelptext = ConVar::StaticCreate("con_suggest_showhelptext" , "1" , FCVAR_DEVELOPMENTONLY, "Show CommandBase help text in autocomplete window.", false, 0.f, false, 0.f, nullptr, nullptr); - con_suggest_showflags = ConVar::StaticCreate("con_suggest_showflags" , "1" , FCVAR_DEVELOPMENTONLY, "Show CommandBase flags in autocomplete window.", false, 0.f, false, 0.f, nullptr, nullptr); - - serverbrowser_hideEmptyServers = ConVar::StaticCreate("serverbrowser_hideEmptyServers", "0", FCVAR_RELEASE, "Hide empty servers in the server browser.", false, 0.f, false, 0.f, nullptr, nullptr); - serverbrowser_mapFilter = ConVar::StaticCreate("serverbrowser_mapFilter", "0", FCVAR_RELEASE, "Filter servers by map in the server browser.", false, 0.f, false, 0.f, nullptr, nullptr); - serverbrowser_gamemodeFilter = ConVar::StaticCreate("serverbrowser_gamemodeFilter", "0", FCVAR_RELEASE, "Filter servers by gamemode in the server browser.", false, 0.f, false, 0.f, nullptr, nullptr); - -#endif // !DEDICATED - // Taken from S15: - usercmd_frametime_max = ConVar::StaticCreate("usercmd_frametime_max", "0.100", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The largest amount of simulation seconds a UserCmd can have.", false, 0.f, false, 0.f, nullptr, nullptr); - usercmd_frametime_min = ConVar::StaticCreate("usercmd_frametime_min", "0.002857", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The smallest amount of simulation seconds a UserCmd can have.", false, 0.f, false, 0.f, nullptr, nullptr); - - usercmd_dualwield_enable = ConVar::StaticCreate("usercmd_dualwield_enable", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Allows setting dual wield cycle slots, and activating multiple inventory weapons from UserCmd.", false, 0.f, false, 0.f, nullptr, nullptr); - //------------------------------------------------------------------------- - // FILESYSTEM | - fs_showWarnings = ConVar::StaticCreate("fs_showWarnings" , "0", FCVAR_DEVELOPMENTONLY, "Logs the FileSystem warnings to the console, filtered by 'fs_warning_level' ( !slower! ).", true, 0.f, true, 2.f, nullptr, "0 = log to file. 1 = 0 + log to console. 2 = 1 + log to notify"); - fs_packedstore_entryblock_stats = ConVar::StaticCreate("fs_packedstore_entryblock_stats" , "0", FCVAR_DEVELOPMENTONLY, "Logs the stats of each file entry in the VPK during decompression ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); - fs_packedstore_workspace = ConVar::StaticCreate("fs_packedstore_workspace" , "ship", FCVAR_DEVELOPMENTONLY, "Determines the current VPK workspace.", false, 0.f, false, 0.f, nullptr, nullptr); - fs_packedstore_compression_level = ConVar::StaticCreate("fs_packedstore_compression_level", "default", FCVAR_DEVELOPMENTONLY, "Determines the VPK compression level.", false, 0.f, false, 0.f, nullptr, "fastest faster default better uber"); - fs_packedstore_max_helper_threads = ConVar::StaticCreate("fs_packedstore_max_helper_threads" , "-1", FCVAR_DEVELOPMENTONLY, "Max # of additional \"helper\" threads to create during compression.", true, -1, true, LZHAM_MAX_HELPER_THREADS, nullptr, "Must range between [-1,LZHAM_MAX_HELPER_THREADS], where -1=max practical"); - //------------------------------------------------------------------------- - // MATERIALSYSTEM | -#ifndef DEDICATED - mat_alwaysComplain = ConVar::StaticCreate("mat_alwaysComplain", "0", FCVAR_RELEASE | FCVAR_MATERIAL_SYSTEM_THREAD, "Always complain when a material is missing.", false, 0.f, false, 0.f, nullptr, nullptr); -#endif // !DEDICATED - //------------------------------------------------------------------------- - // SQUIRREL | - script_show_output = ConVar::StaticCreate("script_show_output" , "0", FCVAR_RELEASE, "Prints the VM output to the console ( !slower! ).", true, 0.f, true, 2.f, nullptr, "0 = log to file. 1 = 0 + log to console. 2 = 1 + log to notify"); - script_show_warning = ConVar::StaticCreate("script_show_warning", "0", FCVAR_RELEASE, "Prints the VM warning output to the console ( !slower! ).", true, 0.f, true, 2.f, nullptr, "0 = log to file. 1 = 0 + log to console. 2 = 1 + log to notify"); - //------------------------------------------------------------------------- - // NETCHANNEL | - net_tracePayload = ConVar::StaticCreate("net_tracePayload" , "0", FCVAR_DEVELOPMENTONLY , "Log the payload of the send/recv datagram to a file on the disk.", false, 0.f, false, 0.f, nullptr, nullptr); - net_encryptionEnable = ConVar::StaticCreate("net_encryptionEnable" , "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED , "Use AES encryption on game packets.", false, 0.f, false, 0.f, nullptr, nullptr); - net_useRandomKey = ConVar::StaticCreate("net_useRandomKey" , "1" , FCVAR_RELEASE , "Use random AES encryption key for game packets.", false, 0.f, false, 0.f, &NET_UseRandomKeyChanged_f, nullptr); - net_processTimeBudget = ConVar::StaticCreate("net_processTimeBudget" ,"200" , FCVAR_RELEASE , "Net message process time budget in milliseconds (removing netchannel if exceeded).", true, 0.f, false, 0.f, nullptr, "0 = disabled"); - //------------------------------------------------------------------------- - // NETWORKSYSTEM | - pylon_matchmaking_hostname = ConVar::StaticCreate("pylon_matchmaking_hostname", "ms.r5reloaded.com", FCVAR_RELEASE, "Holds the pylon matchmaking hostname.", false, 0.f, false, 0.f, &MP_HostName_Changed_f, nullptr); - pylon_host_update_interval = ConVar::StaticCreate("pylon_host_update_interval", "5" , FCVAR_RELEASE, "Length of time in seconds between each status update interval to master server.", true, 5.f, false, 0.f, nullptr, nullptr); - pylon_showdebuginfo = ConVar::StaticCreate("pylon_showdebuginfo" , "0" , FCVAR_RELEASE, "Shows debug output for pylon.", false, 0.f, false, 0.f, nullptr, nullptr); - //------------------------------------------------------------------------- - // RTECH API | - rtech_debug = ConVar::StaticCreate("rtech_debug", "0", FCVAR_DEVELOPMENTONLY, "Shows debug output for the RTech system.", false, 0.f, false, 0.f, nullptr, nullptr); - //------------------------------------------------------------------------- - // RUI | -#ifndef DEDICATED - rui_drawEnable = ConVar::StaticCreate("rui_drawEnable", "1", FCVAR_RELEASE, "Draws the RUI if set.", false, 0.f, false, 0.f, nullptr, "1 = draw; 0 (zero) = no draw"); -#endif // !DEDICATED - //------------------------------------------------------------------------- - // MILES | -#ifndef DEDICATED - miles_debug = ConVar::StaticCreate("miles_debug", "0", FCVAR_RELEASE, "Enables debug prints for the Miles Sound System.", false, 0.f, false, 0.f, nullptr, "1 = print; 0 (zero) = no print"); -#endif // !DEDICATED - //------------------------------------------------------------------------- -} - //----------------------------------------------------------------------------- // Purpose: initialize shipped ConVar's //----------------------------------------------------------------------------- @@ -463,16 +133,21 @@ void ConVar_InitShipped(void) { #ifndef CLIENT_DLL ai_script_nodes_draw = g_pCVar->FindVar("ai_script_nodes_draw"); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) bhit_enable = g_pCVar->FindVar("bhit_enable"); -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 #endif // !CLIENT_DLL developer = g_pCVar->FindVar("developer"); fps_max = g_pCVar->FindVar("fps_max"); + fps_max_vsync = g_pCVar->FindVar("fps_max_vsync"); + base_tickinterval_sp = g_pCVar->FindVar("base_tickinterval_sp"); + base_tickinterval_mp = g_pCVar->FindVar("base_tickinterval_mp"); fs_showAllReads = g_pCVar->FindVar("fs_showAllReads"); + + eula_version = g_pCVar->FindVar("eula_version"); + eula_version_accepted = g_pCVar->FindVar("eula_version_accepted"); + + language_cvar = g_pCVar->FindVar("language"); #ifndef DEDICATED - cl_cmdrate = g_pCVar->FindVar("cl_cmdrate"); - cl_move_use_dt = g_pCVar->FindVar("cl_move_use_dt"); + cl_updaterate_mp = g_pCVar->FindVar("cl_updaterate_mp"); cl_threaded_bone_setup = g_pCVar->FindVar("cl_threaded_bone_setup"); #endif // !DEDICATED single_frame_shutdown_for_reload = g_pCVar->FindVar("single_frame_shutdown_for_reload"); @@ -483,6 +158,7 @@ void ConVar_InitShipped(void) #ifndef DEDICATED miles_language = g_pCVar->FindVar("miles_language"); rui_defaultDebugFontFace = g_pCVar->FindVar("rui_defaultDebugFontFace"); + in_syncRT = g_pCVar->FindVar("in_syncRT"); r_visualizetraces = g_pCVar->FindVar("r_visualizetraces"); r_visualizetraces_duration = g_pCVar->FindVar("r_visualizetraces_duration"); #endif // !DEDICATED @@ -496,7 +172,7 @@ void ConVar_InitShipped(void) old_gather_props = g_pCVar->FindVar("old_gather_props"); #ifndef DEDICATED origin_disconnectWhenOffline = g_pCVar->FindVar("origin_disconnectWhenOffline"); - discord_updatePresence = g_pCVar->FindVar("discord_updatePresence"); + discord_updatePresence = g_pCVar->FindVar("discord_updatePresence"); #endif // !DEDICATED mp_gamemode = g_pCVar->FindVar("mp_gamemode"); ip_cvar = g_pCVar->FindVar("ip"); @@ -505,13 +181,24 @@ void ConVar_InitShipped(void) hostport = g_pCVar->FindVar("hostport"); host_hasIrreversibleShutdown = g_pCVar->FindVar("host_hasIrreversibleShutdown"); host_timescale = g_pCVar->FindVar("host_timescale"); + + net_data_block_enabled = g_pCVar->FindVar("net_data_block_enabled"); + net_compressDataBlock = g_pCVar->FindVar("net_compressDataBlock"); net_datablock_networkLossForSlowSpeed = g_pCVar->FindVar("net_datablock_networkLossForSlowSpeed"); + net_usesocketsforloopback = g_pCVar->FindVar("net_usesocketsforloopback"); + + net_showmsg = g_pCVar->FindVar("net_showmsg"); + net_blockmsg = g_pCVar->FindVar("net_blockmsg"); + net_showpeaks = g_pCVar->FindVar("net_showpeaks"); #ifndef CLIENT_DLL sv_stats = g_pCVar->FindVar("sv_stats"); - sv_updaterate_mp = g_pCVar->FindVar("sv_updaterate_mp"); + sv_maxunlag = g_pCVar->FindVar("sv_maxunlag"); + sv_clockcorrection_msecs = g_pCVar->FindVar("sv_clockcorrection_msecs"); + sv_updaterate_sp = g_pCVar->FindVar("sv_updaterate_sp"); + sv_updaterate_mp = g_pCVar->FindVar("sv_updaterate_mp"); sv_showhitboxes = g_pCVar->FindVar("sv_showhitboxes"); sv_forceChatToTeamOnly = g_pCVar->FindVar("sv_forceChatToTeamOnly"); @@ -523,6 +210,9 @@ void ConVar_InitShipped(void) sv_alltalk = g_pCVar->FindVar("sv_alltalk"); player_userCmdsQueueWarning = g_pCVar->FindVar("player_userCmdsQueueWarning"); + sv_updaterate_sp->RemoveFlags(FCVAR_DEVELOPMENTONLY); + sv_updaterate_mp->RemoveFlags(FCVAR_DEVELOPMENTONLY); + sv_showhitboxes->SetMin(-1); // Allow user to go over each entity manually without going out of bounds. sv_showhitboxes->SetMax(NUM_ENT_ENTRIES - 1); @@ -532,21 +222,29 @@ void ConVar_InitShipped(void) sv_single_core_dedi->RemoveFlags(FCVAR_DEVELOPMENTONLY); ai_script_nodes_draw->SetValue(-1); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) bhit_enable->SetValue(0); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) #endif // !CLIENT_DLL #ifndef DEDICATED + cl_updaterate_mp->RemoveFlags(FCVAR_DEVELOPMENTONLY); + cl_threaded_bone_setup->RemoveFlags(FCVAR_DEVELOPMENTONLY); rui_defaultDebugFontFace->RemoveFlags(FCVAR_DEVELOPMENTONLY); origin_disconnectWhenOffline->RemoveFlags(FCVAR_DEVELOPMENTONLY); discord_updatePresence->RemoveFlags(FCVAR_DEVELOPMENTONLY); #endif // !DEDICATED + fps_max_vsync->RemoveFlags(FCVAR_DEVELOPMENTONLY); + + base_tickinterval_sp->RemoveFlags(FCVAR_DEVELOPMENTONLY); + base_tickinterval_mp->RemoveFlags(FCVAR_DEVELOPMENTONLY); + mp_gamemode->RemoveFlags(FCVAR_DEVELOPMENTONLY); mp_gamemode->RemoveChangeCallback(mp_gamemode->m_fnChangeCallbacks[0]); mp_gamemode->InstallChangeCallback(MP_GameMode_Changed_f, false); net_usesocketsforloopback->RemoveFlags(FCVAR_DEVELOPMENTONLY); net_usesocketsforloopback->InstallChangeCallback(NET_UseSocketsForLoopbackChanged_f, false); +#ifndef DEDICATED + language_cvar->InstallChangeCallback(LanguageChanged_f, false); +#endif // !DEDICATED } //----------------------------------------------------------------------------- @@ -610,79 +308,19 @@ void ConVar_PurgeHostNames(void) } } -//----------------------------------------------------------------------------- -// Purpose: ConCommand registration -//----------------------------------------------------------------------------- -void ConCommand_StaticInit(void) -{ - //------------------------------------------------------------------------- - // ENGINE DLL | -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) - ConCommand::StaticCreate("bhit", "Bullet-hit trajectory debug.", nullptr, FCVAR_DEVELOPMENTONLY | FCVAR_GAMEDLL, BHit_f, nullptr); -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 +static ConCommand bhit("bhit", BHit_f, "Bullet-hit trajectory debug", FCVAR_DEVELOPMENTONLY | FCVAR_GAMEDLL); + #ifndef DEDICATED - ConCommand::StaticCreate("line", "Draw a debug line.", nullptr, FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, Line_f, nullptr); - ConCommand::StaticCreate("sphere", "Draw a debug sphere.", nullptr, FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, Sphere_f, nullptr); - ConCommand::StaticCreate("capsule", "Draw a debug capsule.", nullptr, FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, Capsule_f, nullptr); +static ConCommand line("line", Line_f, "Draw a debug line", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT); +static ConCommand sphere("sphere", Sphere_f, "Draw a debug sphere", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT); +static ConCommand capsule("capsule", Capsule_f, "Draw a debug capsule", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT); #endif //!DEDICATED - ConCommand::StaticCreate("con_help", "Shows the colors and description of each context.", nullptr, FCVAR_RELEASE, CON_Help_f, nullptr); -#ifndef CLIENT_DLL - ConCommand::StaticCreate("reload_playlists", "Reloads the playlists file.", nullptr, FCVAR_RELEASE, Host_ReloadPlaylists_f, nullptr); -#endif // !CLIENT_DLL - //------------------------------------------------------------------------- - // SERVER DLL | -#ifndef CLIENT_DLL - ConCommand::StaticCreate("script", "Run input code as SERVER script on the VM.", nullptr, FCVAR_GAMEDLL | FCVAR_CHEAT, SQVM_ServerScript_f, nullptr); - ConCommand::StaticCreate("kick", "Kick a client from the server by user name.", "kick \"\"", FCVAR_RELEASE, Host_Kick_f, nullptr); - ConCommand::StaticCreate("kickid", "Kick a client from the server by handle, nucleus id or ip address.", "kickid \"\"/\"/\"", FCVAR_RELEASE, Host_KickID_f, nullptr); - ConCommand::StaticCreate("ban", "Bans a client from the server by user name.", "ban ", FCVAR_RELEASE, Host_Ban_f, nullptr); - ConCommand::StaticCreate("banid", "Bans a client from the server by handle, nucleus id or ip address.", "banid \"\"/\"/\"", FCVAR_RELEASE, Host_BanID_f, nullptr); - ConCommand::StaticCreate("unban", "Unbans a client from the server by nucleus id or ip address.", "unban \"\"/\"\"", FCVAR_RELEASE, Host_Unban_f, nullptr); - ConCommand::StaticCreate("sv_reloadbanlist", "Reloads the banned list.", nullptr, FCVAR_RELEASE, Host_ReloadBanList_f, nullptr); - ConCommand::StaticCreate("sv_addbot", "Creates a bot on the server.", nullptr, FCVAR_RELEASE, CC_CreateFakePlayer_f, nullptr); - ConCommand::StaticCreate("navmesh_hotswap", "Hot swap the NavMesh for all hulls.", nullptr, FCVAR_DEVELOPMENTONLY, Detour_HotSwap_f, nullptr); -#endif // !CLIENT_DLL -#ifndef DEDICATED - //------------------------------------------------------------------------- - // CLIENT DLL | - ConCommand::StaticCreate("script_client", "Run input code as CLIENT script on the VM.", nullptr, FCVAR_CLIENTDLL | FCVAR_CHEAT, SQVM_ClientScript_f, nullptr); - ConCommand::StaticCreate("rcon", "Forward RCON query to remote server.", "rcon \"\"", FCVAR_CLIENTDLL | FCVAR_RELEASE, RCON_CmdQuery_f, nullptr); - ConCommand::StaticCreate("rcon_disconnect", "Disconnect from RCON server.", nullptr, FCVAR_CLIENTDLL | FCVAR_RELEASE, RCON_Disconnect_f, nullptr); - ConCommand::StaticCreate("con_history", "Shows the developer console submission history.", nullptr, FCVAR_CLIENTDLL | FCVAR_RELEASE, CON_LogHistory_f, nullptr); - ConCommand::StaticCreate("con_removeline", "Removes a range of lines from the developer console.", nullptr, FCVAR_CLIENTDLL | FCVAR_RELEASE, CON_RemoveLine_f, nullptr); - ConCommand::StaticCreate("con_clearlines", "Clears all lines from the developer console.", nullptr, FCVAR_CLIENTDLL | FCVAR_RELEASE, CON_ClearLines_f, nullptr); - ConCommand::StaticCreate("con_clearhistory", "Clears all submissions from the developer console history.", nullptr, FCVAR_CLIENTDLL | FCVAR_RELEASE, CON_ClearHistory_f, nullptr); - - ConCommand::StaticCreate("toggleconsole", "Show/hide the developer console.", nullptr, FCVAR_CLIENTDLL | FCVAR_RELEASE, ToggleConsole_f, nullptr); - ConCommand::StaticCreate("togglebrowser", "Show/hide the server browser.", nullptr, FCVAR_CLIENTDLL | FCVAR_RELEASE, ToggleBrowser_f, nullptr); - //------------------------------------------------------------------------- - // UI DLL | - ConCommand::StaticCreate("script_ui", "Run input code as UI script on the VM.", nullptr, FCVAR_CLIENTDLL | FCVAR_CHEAT, SQVM_UIScript_f, nullptr); -#endif // !DEDICATED - //------------------------------------------------------------------------- - // FILESYSTEM API | - ConCommand::StaticCreate("fs_vpk_mount", "Mount a VPK file for FileSystem usage.", nullptr, FCVAR_DEVELOPMENTONLY, VPK_Mount_f, nullptr); - ConCommand::StaticCreate("fs_vpk_unmount", "Unmount a VPK file and clear its cache.", nullptr, FCVAR_DEVELOPMENTONLY, VPK_Unmount_f, nullptr); - ConCommand::StaticCreate("fs_vpk_pack", "Pack a VPK file from current workspace.", nullptr, FCVAR_DEVELOPMENTONLY, VPK_Pack_f, nullptr); - ConCommand::StaticCreate("fs_vpk_unpack", "Unpack all files from a VPK file.", nullptr, FCVAR_DEVELOPMENTONLY, VPK_Unpack_f, nullptr); - //------------------------------------------------------------------------- - // RTECH API | - ConCommand::StaticCreate("rtech_strtoguid", "Calculates the GUID from input data.", nullptr, FCVAR_DEVELOPMENTONLY, RTech_StringToGUID_f, nullptr); - ConCommand::StaticCreate("pak_decompress", "Decompresses specified RPAK file.", nullptr, FCVAR_DEVELOPMENTONLY, RTech_Decompress_f, RTech_PakDecompress_f_CompletionFunc); - ConCommand::StaticCreate("pak_requestload", "Requests asynchronous load for specified RPAK file.", nullptr, FCVAR_DEVELOPMENTONLY, Pak_RequestLoad_f, RTech_PakLoad_f_CompletionFunc); - ConCommand::StaticCreate("pak_requestunload", "Requests unload for specified RPAK file or ID.", nullptr, FCVAR_DEVELOPMENTONLY, Pak_RequestUnload_f, RTech_PakUnload_f_CompletionFunc); - ConCommand::StaticCreate("pak_swap", "Requests swap for specified RPAK file or ID", nullptr, FCVAR_DEVELOPMENTONLY, Pak_Swap_f, nullptr); - ConCommand::StaticCreate("pak_listpaks", "Display a list of the loaded Pak files.", nullptr, FCVAR_RELEASE, Pak_ListPaks_f, nullptr); - ConCommand::StaticCreate("pak_listtypes", "Display a list of the registered asset types.", nullptr, FCVAR_RELEASE, Pak_ListTypes_f, nullptr); - //------------------------------------------------------------------------- - // NETCHANNEL | - ConCommand::StaticCreate("net_setkey", "Sets user specified base64 net key.", nullptr, FCVAR_RELEASE, NET_SetKey_f, nullptr); - ConCommand::StaticCreate("net_generatekey", "Generates and sets a random base64 net key.", nullptr, FCVAR_RELEASE, NET_GenerateKey_f, nullptr); - //------------------------------------------------------------------------- - // TIER0 | - ConCommand::StaticCreate("sig_getadr", "Logs the sigscan results to the console.", nullptr, FCVAR_DEVELOPMENTONLY | FCVAR_HIDDEN, SIG_GetAdr_f, nullptr); -} +// TODO: move VPK building code to separate file and place this in 'packedstore.cpp' +static ConCommand fs_vpk_mount("fs_vpk_mount", VPK_Mount_f, "Mount a VPK file for FileSystem usage", FCVAR_DEVELOPMENTONLY); +static ConCommand fs_vpk_unmount("fs_vpk_unmount", VPK_Unmount_f, "Unmount a VPK file and clear its cache", FCVAR_DEVELOPMENTONLY); +static ConCommand fs_vpk_pack("fs_vpk_pack", VPK_Pack_f, "Pack a VPK file from current workspace", FCVAR_DEVELOPMENTONLY); +static ConCommand fs_vpk_unpack("fs_vpk_unpack", VPK_Unpack_f, "Unpack all files from a VPK file", FCVAR_DEVELOPMENTONLY); //----------------------------------------------------------------------------- // Purpose: shipped ConCommand initialization diff --git a/r5dev/common/global.h b/r5dev/common/global.h index 2fa7d671..e958199b 100644 --- a/r5dev/common/global.h +++ b/r5dev/common/global.h @@ -3,7 +3,6 @@ //------------------------------------------------------------------------- // ENGINE | -extern ConVar* sdk_fixedframe_tickinterval; extern ConVar* single_frame_shutdown_for_reload; extern ConVar* old_gather_props; @@ -12,12 +11,14 @@ extern ConVar* debug_draw_box_depth_test; extern ConVar* developer; extern ConVar* fps_max; +extern ConVar* fps_max_vsync; -// taken from S15: -extern ConVar* usercmd_frametime_max; -extern ConVar* usercmd_frametime_min; +#ifndef DEDICATED +extern ConVar* in_syncRT; +#endif // !DEDICATED -extern ConVar* usercmd_dualwield_enable; +extern ConVar* base_tickinterval_sp; +extern ConVar* base_tickinterval_mp; extern ConVar* staticProp_no_fade_scalar; extern ConVar* staticProp_gather_size_weight; @@ -27,7 +28,6 @@ extern ConVar* model_defaultFadeDistMin; extern ConVar* ip_cvar; extern ConVar* hostname; -extern ConVar* hostdesc; extern ConVar* hostip; extern ConVar* hostport; @@ -36,17 +36,6 @@ extern ConVar* host_timescale; extern ConVar* mp_gamemode; -extern ConVar* rcon_address; -extern ConVar* rcon_password; - -extern ConVar* r_debug_overlay_nodecay; -extern ConVar* r_debug_overlay_invisible; -extern ConVar* r_debug_overlay_wireframe; -extern ConVar* r_debug_draw_depth_test; -extern ConVar* r_drawWorldMeshes; -extern ConVar* r_drawWorldMeshesDepthOnly; -extern ConVar* r_drawWorldMeshesDepthAtTheEnd; - #ifndef DEDICATED extern ConVar* r_visualizetraces; extern ConVar* r_visualizetraces_duration; @@ -56,202 +45,80 @@ extern ConVar* stream_overlay; extern ConVar* stream_overlay_mode; //------------------------------------------------------------------------- // SHARED | -extern ConVar* modsystem_enable; -extern ConVar* modsystem_debug; +extern ConVar* eula_version; +extern ConVar* eula_version_accepted; + +extern ConVar* language_cvar; //------------------------------------------------------------------------- // SERVER | #ifndef CLIENT_DLL -extern ConVar* ai_ainDumpOnLoad; -extern ConVar* ai_ainDebugConnect; extern ConVar* ai_script_nodes_draw; -extern ConVar* ai_script_nodes_draw_range; -extern ConVar* ai_script_nodes_draw_nearest; -extern ConVar* navmesh_always_reachable; -extern ConVar* navmesh_debug_type; -extern ConVar* navmesh_debug_tile_range; -extern ConVar* navmesh_debug_camera_range; -#ifndef DEDICATED -extern ConVar* navmesh_draw_bvtree; -extern ConVar* navmesh_draw_portal; -extern ConVar* navmesh_draw_polys; -extern ConVar* navmesh_draw_poly_bounds; -extern ConVar* navmesh_draw_poly_bounds_inner; -#endif // DEDICATED -extern ConVar* sv_language; -extern ConVar* sv_showconnecting; -extern ConVar* sv_globalBanlist; -extern ConVar* sv_pylonVisibility; -extern ConVar* sv_pylonRefreshRate; -extern ConVar* sv_banlistRefreshRate; -extern ConVar* sv_statusRefreshRate; extern ConVar* sv_forceChatToTeamOnly; extern ConVar* sv_single_core_dedi; -extern ConVar* sv_updaterate_mp; -extern ConVar* sv_updaterate_sp; -extern ConVar* sv_autoReloadRate; +extern ConVar* sv_maxunlag; +extern ConVar* sv_clockcorrection_msecs; + +extern ConVar* sv_updaterate_sp; +extern ConVar* sv_updaterate_mp; -extern ConVar* sv_simulateBots; extern ConVar* sv_showhitboxes; extern ConVar* sv_stats; -extern ConVar* sv_quota_stringCmdsPerSecond; - -extern ConVar* sv_validatePersonaName; -extern ConVar* sv_minPersonaNameLength; -extern ConVar* sv_maxPersonaNameLength; - extern ConVar* sv_voiceEcho; extern ConVar* sv_voiceenable; extern ConVar* sv_alltalk; extern ConVar* player_userCmdsQueueWarning; -//#ifdef DEDICATED -extern ConVar* sv_rcon_debug; -extern ConVar* sv_rcon_sendlogs; -//extern ConVar* sv_rcon_banpenalty; -extern ConVar* sv_rcon_maxfailures; -extern ConVar* sv_rcon_maxignores; -extern ConVar* sv_rcon_maxsockets; -extern ConVar* sv_rcon_maxconnections; -extern ConVar* sv_rcon_maxpacketsize; -extern ConVar* sv_rcon_whitelist_address; -//#endif // DEDICATED #endif // CLIENT_DLL extern ConVar* sv_cheats; extern ConVar* sv_visualizetraces; extern ConVar* sv_visualizetraces_duration; -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) extern ConVar* bhit_enable; -extern ConVar* bhit_depth_test; -extern ConVar* bhit_abs_origin; -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 //------------------------------------------------------------------------- // CLIENT | #ifndef DEDICATED -extern ConVar* cl_rcon_inputonly; -extern ConVar* cl_quota_stringCmdsPerSecond; - -extern ConVar* cl_cmdrate; -extern ConVar* cl_move_use_dt; - -extern ConVar* cl_notify_invert_x; -extern ConVar* cl_notify_invert_y; -extern ConVar* cl_notify_offset_x; -extern ConVar* cl_notify_offset_y; - -extern ConVar* cl_showsimstats; -extern ConVar* cl_simstats_invert_x; -extern ConVar* cl_simstats_invert_y; -extern ConVar* cl_simstats_offset_x; -extern ConVar* cl_simstats_offset_y; - -extern ConVar* cl_showgpustats; -extern ConVar* cl_gpustats_invert_x; -extern ConVar* cl_gpustats_invert_y; -extern ConVar* cl_gpustats_offset_x; -extern ConVar* cl_gpustats_offset_y; - -extern ConVar* cl_showmaterialinfo; -extern ConVar* cl_materialinfo_offset_x; -extern ConVar* cl_materialinfo_offset_y; - extern ConVar* cl_threaded_bone_setup; -extern ConVar* con_drawnotify; -extern ConVar* con_notifylines; -extern ConVar* con_notifytime; - -extern ConVar* con_notify_invert_x; -extern ConVar* con_notify_invert_y; -extern ConVar* con_notify_offset_x; -extern ConVar* con_notify_offset_y; - -extern ConVar* con_notify_script_server_clr; -extern ConVar* con_notify_script_client_clr; -extern ConVar* con_notify_script_ui_clr; -extern ConVar* con_notify_native_server_clr; -extern ConVar* con_notify_native_client_clr; -extern ConVar* con_notify_native_ui_clr; -extern ConVar* con_notify_native_engine_clr; -extern ConVar* con_notify_native_fs_clr; -extern ConVar* con_notify_native_rtech_clr; -extern ConVar* con_notify_native_ms_clr; -extern ConVar* con_notify_native_audio_clr; -extern ConVar* con_notify_native_video_clr; -extern ConVar* con_notify_netcon_clr; -extern ConVar* con_notify_common_clr; -extern ConVar* con_notify_warning_clr; -extern ConVar* con_notify_error_clr; - -extern ConVar* con_max_lines; -extern ConVar* con_max_history; -extern ConVar* con_suggest_limit; -extern ConVar* con_suggest_showhelptext; -extern ConVar* con_suggest_showflags; - extern ConVar* origin_disconnectWhenOffline; extern ConVar* discord_updatePresence; #endif // !DEDICATED //------------------------------------------------------------------------- // FILESYSTEM | -extern ConVar* fs_showWarnings; extern ConVar* fs_showAllReads; -extern ConVar* fs_packedstore_entryblock_stats; -extern ConVar* fs_packedstore_workspace; -extern ConVar* fs_packedstore_compression_level; -extern ConVar* fs_packedstore_max_helper_threads; -//------------------------------------------------------------------------- -// MATERIALSYSTEM | -#ifndef DEDICATED -extern ConVar* mat_alwaysComplain; -#endif // !DEDICATED -//------------------------------------------------------------------------- -// SQUIRREL | -extern ConVar* script_show_output; -extern ConVar* script_show_warning; //------------------------------------------------------------------------- // NETCHANNEL | -extern ConVar* net_tracePayload; -extern ConVar* net_encryptionEnable; -extern ConVar* net_useRandomKey; extern ConVar* net_usesocketsforloopback; -extern ConVar* net_processTimeBudget; +extern ConVar* net_data_block_enabled; extern ConVar* net_datablock_networkLossForSlowSpeed; +extern ConVar* net_compressDataBlock; -extern ConVar* pylon_matchmaking_hostname; -extern ConVar* pylon_host_update_interval; -extern ConVar* pylon_showdebuginfo; +extern ConVar* net_showmsg; +extern ConVar* net_blockmsg; +extern ConVar* net_showpeaks; -extern ConVar* ssl_verify_peer; -extern ConVar* curl_timeout; -extern ConVar* curl_debug; -//------------------------------------------------------------------------- -// RTECH API | -extern ConVar* rtech_debug; +extern ConVar ssl_verify_peer; +extern ConVar curl_timeout; +extern ConVar curl_debug; //------------------------------------------------------------------------- // RUI | #ifndef DEDICATED -extern ConVar* rui_drawEnable; extern ConVar* rui_defaultDebugFontFace; #endif // !DEDICATED //------------------------------------------------------------------------- // MILES | #ifndef DEDICATED -extern ConVar* miles_debug; extern ConVar* miles_language; #endif -void ConVar_StaticInit(void); void ConVar_InitShipped(void); void ConVar_PurgeShipped(void); void ConVar_PurgeHostNames(void); -void ConCommand_StaticInit(void); void ConCommand_InitShipped(void); void ConCommand_PurgeShipped(void); diff --git a/r5dev/common/netmessages.cpp b/r5dev/common/netmessages.cpp index 8b5074b3..2e76f72f 100644 --- a/r5dev/common/netmessages.cpp +++ b/r5dev/common/netmessages.cpp @@ -28,7 +28,7 @@ bool SVC_Print::ProcessImpl() if (len < sizeof(m_szTextBuffer)) { - DevMsg(eDLL_T::SERVER, m_szText[len-1] == '\n' ? "%s" : "%s\n", m_szText); + Msg(eDLL_T::SERVER, m_szText[len-1] == '\n' ? "%s" : "%s\n", m_szText); } } @@ -56,7 +56,7 @@ bool SVC_UserMessage::ProcessImpl() if (len && len < sizeof(text)) { - DevMsg(eDLL_T::SERVER, text[len - 1] == '\n' ? "%s" : "%s\n", text); + Msg(eDLL_T::SERVER, text[len - 1] == '\n' ? "%s" : "%s\n", text); } } } @@ -90,6 +90,7 @@ bool CLC_SetPlaylistVarOverride::WriteToBufferImpl(CLC_SetPlaylistVarOverride* t return CLC_SetPlaylistVarOverride_WriteToBuffer(thisptr, buffer); } +static ConVar enable_CmdKeyValues("enable_CmdKeyValues", "0", FCVAR_DEVELOPMENTONLY, "Toggle CmdKeyValues transmit and receive."); /////////////////////////////////////////////////////////////////////////////////// // below functions are hooked as 'CmdKeyValues' isn't really used in this game, but @@ -98,8 +99,8 @@ bool CLC_SetPlaylistVarOverride::WriteToBufferImpl(CLC_SetPlaylistVarOverride* t /////////////////////////////////////////////////////////////////////////////////// bool Base_CmdKeyValues::ReadFromBufferImpl(Base_CmdKeyValues* thisptr, bf_read* buffer) { - // Abusable netmsg; only allow if cheats are enabled. - if (!sv_cheats->GetBool()) + // Abusable netmsg; only allow if explicitly enabled by the client. + if (!enable_CmdKeyValues.GetBool()) { return false; } @@ -108,8 +109,8 @@ bool Base_CmdKeyValues::ReadFromBufferImpl(Base_CmdKeyValues* thisptr, bf_read* } bool Base_CmdKeyValues::WriteToBufferImpl(Base_CmdKeyValues* thisptr, bf_write* buffer) { - // Abusable netmsg; only allow if cheats are enabled. - if (!sv_cheats->GetBool()) + // Abusable netmsg; only allow if explicitly enabled by the client. + if (!enable_CmdKeyValues.GetBool()) { return false; } @@ -160,26 +161,28 @@ bool ShouldReplayMessage(const CNetMessage* msg) } } -void V_NetMessages::Attach() const +void V_NetMessages::Detour(const bool bAttach) const { - auto hk_SVCPrint_Process = &SVC_Print::ProcessImpl; - auto hk_SVCUserMessage_Process = &SVC_UserMessage::ProcessImpl; + if (bAttach) + { + auto hk_SVCPrint_Process = &SVC_Print::ProcessImpl; + auto hk_SVCUserMessage_Process = &SVC_UserMessage::ProcessImpl; - CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID&)hk_SVCPrint_Process, NetMessageVtbl::Process, (LPVOID*)&SVC_Print_Process); - CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID&)hk_SVCUserMessage_Process, NetMessageVtbl::Process, (LPVOID*)&SVC_UserMessage_Process); - CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID*)&Base_CmdKeyValues::ReadFromBufferImpl, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&Base_CmdKeyValues_ReadFromBuffer); - CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID*)&Base_CmdKeyValues::WriteToBufferImpl, NetMessageVtbl::WriteToBuffer, (LPVOID*)&Base_CmdKeyValues_WriteToBuffer); - CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID*)&CLC_SetPlaylistVarOverride::ReadFromBufferImpl, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&CLC_SetPlaylistVarOverride_ReadFromBuffer); - CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID*)&CLC_SetPlaylistVarOverride::WriteToBufferImpl, NetMessageVtbl::WriteToBuffer, (LPVOID*)&CLC_SetPlaylistVarOverride_WriteToBuffer); -} - -void V_NetMessages::Detach() const -{ - void* hkRestore = nullptr; - CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID)SVC_Print_Process, NetMessageVtbl::Process, (LPVOID*)&hkRestore); - CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID)SVC_UserMessage_Process, NetMessageVtbl::Process, (LPVOID*)&hkRestore); - CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID)Base_CmdKeyValues_ReadFromBuffer, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&hkRestore); - CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID)Base_CmdKeyValues_WriteToBuffer, NetMessageVtbl::WriteToBuffer, (LPVOID*)&hkRestore); - CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID)CLC_SetPlaylistVarOverride_ReadFromBuffer, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&hkRestore); - CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID)CLC_SetPlaylistVarOverride_WriteToBuffer, NetMessageVtbl::WriteToBuffer, (LPVOID*)&hkRestore); + CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID&)hk_SVCPrint_Process, NetMessageVtbl::Process, (LPVOID*)&SVC_Print_Process); + CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID&)hk_SVCUserMessage_Process, NetMessageVtbl::Process, (LPVOID*)&SVC_UserMessage_Process); + CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID*)&Base_CmdKeyValues::ReadFromBufferImpl, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&Base_CmdKeyValues_ReadFromBuffer); + CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID*)&Base_CmdKeyValues::WriteToBufferImpl, NetMessageVtbl::WriteToBuffer, (LPVOID*)&Base_CmdKeyValues_WriteToBuffer); + CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID*)&CLC_SetPlaylistVarOverride::ReadFromBufferImpl, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&CLC_SetPlaylistVarOverride_ReadFromBuffer); + CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID*)&CLC_SetPlaylistVarOverride::WriteToBufferImpl, NetMessageVtbl::WriteToBuffer, (LPVOID*)&CLC_SetPlaylistVarOverride_WriteToBuffer); + } + else + { + void* hkRestore = nullptr; + CMemory::HookVirtualMethod((uintptr_t)g_pSVC_Print_VFTable, (LPVOID)SVC_Print_Process, NetMessageVtbl::Process, (LPVOID*)&hkRestore); + CMemory::HookVirtualMethod((uintptr_t)g_pSVC_UserMessage_VFTable, (LPVOID)SVC_UserMessage_Process, NetMessageVtbl::Process, (LPVOID*)&hkRestore); + CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID)Base_CmdKeyValues_ReadFromBuffer, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&hkRestore); + CMemory::HookVirtualMethod((uintptr_t)g_pBase_CmdKeyValues_VFTable, (LPVOID)Base_CmdKeyValues_WriteToBuffer, NetMessageVtbl::WriteToBuffer, (LPVOID*)&hkRestore); + CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID)CLC_SetPlaylistVarOverride_ReadFromBuffer, NetMessageVtbl::ReadFromBuffer, (LPVOID*)&hkRestore); + CMemory::HookVirtualMethod((uintptr_t)g_pCLC_SetPlaylistVarOverride_VFTable, (LPVOID)CLC_SetPlaylistVarOverride_WriteToBuffer, NetMessageVtbl::WriteToBuffer, (LPVOID*)&hkRestore); + } } diff --git a/r5dev/common/netmessages.h b/r5dev/common/netmessages.h index a6865227..e94ed440 100644 --- a/r5dev/common/netmessages.h +++ b/r5dev/common/netmessages.h @@ -7,6 +7,7 @@ #pragma once #include "tier1/bitbuf.h" +#include "common/qlimits.h" #include "public/inetchannel.h" #include "public/inetmessage.h" #include "public/inetmsghandler.h" @@ -184,6 +185,24 @@ public: /////////////////////////////////////////////////////////////////////////////////////// // server messages: /////////////////////////////////////////////////////////////////////////////////////// +class SVC_CreateStringTable : public CNetMessage +{ +public: + const char* m_szTableName; + int m_nMaxEntries; + int m_nNumEntries; + char m_bUserDataFixedSize; + char _padding0[3]; + int m_nUserDataSize; + int m_nUserDataSizeBits; + int m_nDictFlags; + int m_nLength; + bf_read m_DataIn; + bf_write m_DataOut; + char m_bDataCompressed; + char m_szTableNameBuffer[260]; +}; + class SVC_Print : public CNetMessage { public: @@ -362,6 +381,10 @@ private: bf_write m_DataOut; }; +/////////////////////////////////////////////////////////////////////////////////////// +// Client messages: +/////////////////////////////////////////////////////////////////////////////////////// + class CLC_ClientTick : public CNetMessage { public: @@ -427,7 +450,7 @@ private: /////////////////////////////////////////////////////////////////////////////////////// -// Client messages: +// Shared messages: /////////////////////////////////////////////////////////////////////////////////////// struct NET_StringCmd : CNetMessage @@ -436,6 +459,19 @@ struct NET_StringCmd : CNetMessage char buffer[1024]; }; +class NET_SetConVar : public CNetMessage +{ +public: + + typedef struct cvar_s + { + char name[MAX_OSPATH]; + char value[MAX_OSPATH]; + } cvar_t; + + CUtlVector m_ConVars; +}; + /////////////////////////////////////////////////////////////////////////////////////// // This message is subclassed by 'SVC_CmdKeyValues' and 'CLC_CmdKeyValues' class Base_CmdKeyValues : public CNetMessage @@ -476,15 +512,15 @@ class V_NetMessages : public IDetour { virtual void GetAdr(void) const { - LogConAdr("SVC_Print::`vftable'", reinterpret_cast(g_pSVC_Print_VFTable)); - LogConAdr("SVC_UserMessage::`vftable'", reinterpret_cast(g_pSVC_UserMessage_VFTable)); - LogConAdr("SVC_ServerTick::`vftable'", reinterpret_cast(g_pSVC_ServerTick_VFTable)); - LogConAdr("SVC_VoiceData::`vftable'", reinterpret_cast(g_pSVC_VoiceData_VFTable)); - LogConAdr("SVC_PlaylistOverrides::`vftable'", reinterpret_cast(g_pSVC_PlaylistOverrides_VFTable)); - LogConAdr("CLC_ClientTick::`vftable'", reinterpret_cast(g_pCLC_ClientTick_VFTable)); - LogConAdr("CLC_SetPlaylistVarOverride::`vftable'", reinterpret_cast(g_pCLC_SetPlaylistVarOverride_VFTable)); - LogConAdr("Base_CmdKeyValues::`vftable'", reinterpret_cast(g_pBase_CmdKeyValues_VFTable)); - //LogFunAdr("MM_Heartbeat::ToString", MM_Heartbeat__ToString.GetPtr()); + LogConAdr("SVC_Print::`vftable'", g_pSVC_Print_VFTable); + LogConAdr("SVC_UserMessage::`vftable'", g_pSVC_UserMessage_VFTable); + LogConAdr("SVC_ServerTick::`vftable'", g_pSVC_ServerTick_VFTable); + LogConAdr("SVC_VoiceData::`vftable'", g_pSVC_VoiceData_VFTable); + LogConAdr("SVC_PlaylistOverrides::`vftable'", g_pSVC_PlaylistOverrides_VFTable); + LogConAdr("CLC_ClientTick::`vftable'", g_pCLC_ClientTick_VFTable); + LogConAdr("CLC_SetPlaylistVarOverride::`vftable'", g_pCLC_SetPlaylistVarOverride_VFTable); + LogConAdr("Base_CmdKeyValues::`vftable'", g_pBase_CmdKeyValues_VFTable); + //LogFunAdr("MM_Heartbeat::ToString", MM_Heartbeat__ToString); } virtual void GetFun(void) const { @@ -504,8 +540,7 @@ class V_NetMessages : public IDetour g_pCLC_SetPlaylistVarOverride_VFTable = g_GameDll.GetVirtualMethodTable(".?AVCLC_SetPlaylistVarOverride@@"); g_pBase_CmdKeyValues_VFTable = g_GameDll.GetVirtualMethodTable(".?AVBase_CmdKeyValues@@"); } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/common/opcodes.cpp b/r5dev/common/opcodes.cpp index 95452066..e34b446b 100644 --- a/r5dev/common/opcodes.cpp +++ b/r5dev/common/opcodes.cpp @@ -23,7 +23,6 @@ #include "game/server/ai_networkmanager.h" #include "game/server/detour_impl.h" #endif // !CLIENT_DLL -#include "rtech/rtech_game.h" //#include "rtech/rui/rui.h" //#include "materialsystem/cmaterialsystem.h" //#include "studiorender/studiorendercontext.h" @@ -345,47 +344,9 @@ void RuntimePtc_Init() /* .TEXT */ #ifndef DEDICATED p_WASAPI_GetAudioDevice.Offset(0x410).FindPatternSelf("FF 15 ?? ?? 01 00", CMemory::Direction::DOWN, 100).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xEB }); // CAL --> NOP | Disable debugger check when miles searches for audio device to allow attaching the debugger to the game upon launch. - p_SQVM_CompileError.Offset(0x0).FindPatternSelf("41 B0 01", CMemory::Direction::DOWN, 400).Patch({ 0x41, 0xB0, 0x00 }); // MOV --> MOV | Set script error level to 0 (not severe): 'mov r8b, 0'. - p_SQVM_CompileError.Offset(0xE0).FindPatternSelf("E8", CMemory::Direction::DOWN, 200).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | TODO: causes errors on client script error. Research required (same function as soft error but that one doesn't crash). + CMemory(v_SQVM_CompileError).Offset(0x0).FindPatternSelf("41 B0 01", CMemory::Direction::DOWN, 400).Patch({ 0x41, 0xB0, 0x00 }); // MOV --> MOV | Set script error level to 0 (not severe): 'mov r8b, 0'. + CMemory(v_SQVM_CompileError).Offset(0xE0).FindPatternSelf("E8", CMemory::Direction::DOWN, 200).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | TODO: causes errors on client script error. Research required (same function as soft error but that one doesn't crash). #else - p_SQVM_CompileError.Offset(0xE0).FindPatternSelf("E8", CMemory::Direction::DOWN, 200).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | For dedicated we should not perform post-error events such as telemetry / showing 'COM_ExplainDisconnection' UI etc. + CMemory(v_SQVM_CompileError).Offset(0xE0).FindPatternSelf("E8", CMemory::Direction::DOWN, 200).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | For dedicated we should not perform post-error events such as telemetry / showing 'COM_ExplainDisconnection' UI etc. #endif // !DEDICATED - -#if defined (GAMEDLL_S2) || defined (GAMEDLL_S3) -#ifndef CLIENT_DLL - //p_CAI_NetworkManager__ShouldRebuild.Offset(0xA0).FindPatternSelf("FF ?? ?? ?? 00 00", CMemory::Direction::DOWN, 200).Patch({ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); // CAL --> NOP | Virtual call to restart when building AIN (which clears the AIN memory). Remove this once writing to file works. - //p_Detour_LevelInit.Offset(0x100).FindPatternSelf("74", CMemory::Direction::DOWN, 600).Patch({ 0xEB }); // JE --> JMP | Do while loop setting fields to -1 in navmesh is writing out of bounds (!TODO). -#endif // !CLIENT_DLL -#endif -#ifndef CLIENT_DLL - // !!!TODO!!! HACK: this needs to be removed asap! fix the entitlements file formatting on git itself. - Server_S2C_CONNECT_1.Offset(0x7).Patch({ 0xEB }); // JZ --> JMP | Prevent entitlement check to kick player from server on S2C_CONNECT Packet if it does not match the servers one. -#endif // !CLIENT_DLL - - vector starPakOpenFile = { - 0x4D, 0x31, 0xC0, // xor, r8, r8 - 0x48, 0x8D, 0x8C, 0x24, 0x90, 0x00, 0x00, 0x00, // lea rcx, [rsp+378h+90h] FileName - - // call RTech::OpenFile [RIP+RVA] - #if defined (GAMEDLL_S0) - 0xE8, 0x87, 0x96, 0xFF, 0xFF, - #elif defined (GAMEDLL_S1) - 0xE8, 0x27, 0x95, 0xFF, 0xFF, - #elif defined (GAMEDLL_S2) - 0xE8, 0x87, 0x95, 0xFF, 0xFF, - #elif defined (GAMEDLL_S3) - 0xE8, 0x77, 0x8F, 0xFF, 0xFF, - #endif - - 0x8B, 0xF8, // mov edi, eax - - // jmp [RIP+RVA] - #if defined (GAMEDLL_S0) || defined(GAMEDLL_S1) - 0xE9, 0xDC, 0x00, 0x00, 0x00 - #elif defined (GAMEDLL_S2) || defined(GAMEDLL_S3) - 0xE9, 0xDA, 0x00, 0x00, 0x00 - #endif - }; - - p_CPakFile_OpenFileOffset.Patch(starPakOpenFile); } diff --git a/r5dev/common/opcodes.h b/r5dev/common/opcodes.h index c6e8126b..c7e0a9cd 100644 --- a/r5dev/common/opcodes.h +++ b/r5dev/common/opcodes.h @@ -58,13 +58,6 @@ inline CMemory Host_Shutdown; //------------------------------------------------------------------------- inline CMemory Host_Disconnect; -//------------------------------------------------------------------------- -// RUNTIME: S2C_CHALLENGE -//------------------------------------------------------------------------- -#ifndef CLIENT_DLL -inline CMemory Server_S2C_CONNECT_1; -#endif // !CLIENT_DLL - //------------------------------------------------------------------------- // RUNTIME: //------------------------------------------------------------------------- @@ -88,9 +81,6 @@ class VOpcodes : public IDetour // LogFunAdr("Sys_InitGame", Sys_InitGame.GetPtr()); // LogFunAdr("Host_Init_1", gHost_Init_1.GetPtr()); // LogFunAdr("Host_Init_2", gHost_Init_2.GetPtr()); -#ifndef CLIENT_DLL - LogFunAdr("Server_S2C_CONNECT", Server_S2C_CONNECT_1.GetPtr()); -#endif // !CLIENT_DLL // LogFunAdr("GetEngineClientThread", GetEngineClientThread.GetPtr()); // LogFunAdr("MatchMaking_Frame", MatchMaking_Frame.GetPtr()); //#if !defined (GAMEDLL_S0) || !defined (GAMEDLL_S1) @@ -148,9 +138,6 @@ class VOpcodes : public IDetour // // 0x140236640 // 88 4C 24 08 53 55 56 57 48 83 EC 68 // // // //------------------------------------------------------------------------- -#ifndef CLIENT_DLL - Server_S2C_CONNECT_1 = g_GameDll.FindPatternSIMD("48 3B 05 ?? ?? ?? ?? 74 0C"); -#endif // !CLIENT_DLL // // //------------------------------------------------------------------------- //#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) @@ -174,7 +161,6 @@ class VOpcodes : public IDetour //#endif //48 83 EC 28 33 C9 FF 15 ? ? ? ? 48 8D 0D ? ? ? ? } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/common/proto_oob.h b/r5dev/common/proto_oob.h new file mode 100644 index 00000000..b9d922a1 --- /dev/null +++ b/r5dev/common/proto_oob.h @@ -0,0 +1,45 @@ +//============ Copyright Valve Corporation, All rights reserved. ==============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#if !defined( PROTO_OOB_H ) +#define PROTO_OOB_H +#ifdef _WIN32 +#pragma once +#endif + +#include "proto_version.h" + +#define PORT_RCON 37015 // default RCON port, TCP +#define PORT_SERVER 37015 // Default server port, UDP/TCP +#define PORT_CLIENT 37005 // Default client port, UDP/TCP + +// Out of band message id bytes +// Prefixes: S = server, C = client, A = any + +#define C2S_CONNECT 'A' // client requests to connect +#define S2C_DISCONNECT 'B' // server requests to disconnect + +#define C2S_CHALLENGE 'H' // + challenge value +#define S2C_CHALLENGE 'I' // + challenge value + +#define S2C_CONNACCEPT 'J' +#define S2C_CONNREJECT 'K' // special protocol for rejected connections + +// Generic Ping Request +#define A2A_PING 'L' // respond with an A2A_ACK +#define A2A_ACK 'M' // general acknowledgment without info + +#define S2C_UNKNOWN_UISCRIPT 'N' // TODO: figure out what this does, see [r5apex + 0x288880] + +// Data Block Request +#define S2C_DATABLOCK_FRAGMENT 'O' // data block fragment +#define C2S_DATABLOCK_ACK 'P' // data block fragment acknowledgment + +// All OOB packet start with this sequence +#define CONNECTIONLESS_HEADER 0xFFFFFFFF + +#endif diff --git a/r5dev/common/proto_version.h b/r5dev/common/proto_version.h new file mode 100644 index 00000000..cfc0a46f --- /dev/null +++ b/r5dev/common/proto_version.h @@ -0,0 +1,17 @@ +//============ Copyright Valve Corporation, All rights reserved. ==============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#if !defined( PROTO_VERSION_H ) +#define PROTO_VERSION_H +#ifdef _WIN32 +#pragma once +#endif + +// The current network protocol version. Changing this makes clients and servers incompatible +#define PROTOCOL_VERSION 529 + +#endif diff --git a/r5dev/common/protocol.h b/r5dev/common/protocol.h index 7205a635..3095821d 100644 --- a/r5dev/common/protocol.h +++ b/r5dev/common/protocol.h @@ -4,13 +4,20 @@ * _protocol.h *-----------------------------------------------------------------------------*/ + // Largest # of commands to send in a packet +#define NUM_NEW_COMMAND_BITS 4 +#define MAX_NEW_COMMANDS ((1 << NUM_NEW_COMMAND_BITS)-1) + // Max number of history commands to send ( 2 by default ) in case of dropped packets -#define NUM_BACKUP_COMMAND_BITS 4 // Originally 3 bits. -#define MAX_BACKUP_COMMANDS ((1 << NUM_BACKUP_COMMAND_BITS)-1) // 15 in R5; see 'CL_Move'. +#define NUM_BACKUP_COMMAND_BITS 3 +#define MAX_BACKUP_COMMANDS ((1 << NUM_BACKUP_COMMAND_BITS)-1) // Maximum amount of backup commands to process on the server. -#define MAX_BACKUP_COMMANDS_PROCESS (MAX_BACKUP_COMMANDS+1) * NUM_BACKUP_COMMAND_BITS -#define MAX_QUEUED_COMMANDS_PROCESS 0x1B0 +#define MAX_BACKUP_COMMANDS_PROCESS 64 +#define MAX_QUEUED_COMMANDS_PROCESS 432 + +// The size of the snapshot scratch buffer, which also applies to data block packets +#define SNAPSHOT_SCRATCH_BUFFER_SIZE 786432 enum class SIGNONSTATE : int { @@ -31,9 +38,17 @@ enum class PERSISTENCE : int PERSISTENCE_NONE = 0, // no persistence data for this client yet. PERSISTENCE_PENDING = 1, // pending or processing persistence data. PERSISTENCE_AVAILABLE = 2, // persistence is available for this client. -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - PERSISTENCE_READY = 3 // persistence is ready for this client. -#else PERSISTENCE_READY = 5 // persistence is ready for this client. -#endif }; + +#define net_NOP 0 // nop command used for padding. +#define net_Disconnect 1 // disconnect, last message in connection. + +// each channel packet has 1 byte of FLAG bits +#define PACKET_FLAG_RELIABLE (1<<0) // packet contains subchannel stream data +#define PACKET_FLAG_COMPRESSED (1<<1) // packet is compressed +#define PACKET_FLAG_ENCRYPTED (1<<2) // packet is encrypted +#define PACKET_FLAG_SPLIT (1<<3) // packet is split +#define PACKET_FLAG_CHOKED (1<<4) // packet was choked by sender +#define PACKET_FLAG_PRESCALED (1<<5) // packet was sent by sender with prescaled frame time +#define PACKET_FLAG_LOOPBACK (1<<6) // packet was sent from loopback connection diff --git a/r5dev/common/sdkdefs.h b/r5dev/common/sdkdefs.h index 5dc3473d..e7bb95c6 100644 --- a/r5dev/common/sdkdefs.h +++ b/r5dev/common/sdkdefs.h @@ -21,6 +21,27 @@ typedef uintptr_t uintp; typedef intptr_t intp; typedef const unsigned char* rsig_t; + +// 32bit and 64bit wide boolean type +typedef int32_t b32; +typedef int64_t b64; + +// signed size types typedef std::make_signed_t ssize_t; +#ifndef SSIZE_MAX +#ifdef _WIN64 +#define SSIZE_MAX 9223372036854775807i64 +#define SSIZE_MIN (-9223372036854775807i64 - 1) +#else +#define SSIZE_MAX 2147483647 +#define SSIZE_MIN (-2147483647 - 1) +#endif +#endif + +// unsigned size types +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t) -1) +#endif + #endif // SDKDEFS_H diff --git a/r5dev/common/xbox/xboxstubs.h b/r5dev/common/xbox/xboxstubs.h new file mode 100644 index 00000000..c86f5ab6 --- /dev/null +++ b/r5dev/common/xbox/xboxstubs.h @@ -0,0 +1,52 @@ +//========= Copyright 1996-2004, Valve LLC, All rights reserved. ============ +// +// Purpose: Win32 replacements for XBox. +// +//============================================================================= +#if !defined( XBOXSTUBS_H ) && !defined( _X360 ) +#define XBOXSTUBS_H + +typedef enum +{ + XK_BUTTON_UP, + XK_BUTTON_DOWN, + XK_BUTTON_LEFT, + XK_BUTTON_RIGHT, + + XK_BUTTON_START, + XK_BUTTON_BACK, + + XK_BUTTON_STICK1, + XK_BUTTON_STICK2, + + XK_BUTTON_A, + XK_BUTTON_B, + XK_BUTTON_X, + XK_BUTTON_Y, + + XK_BUTTON_LEFT_SHOULDER, + XK_BUTTON_RIGHT_SHOULDER, + + XK_XBUTTON_LTRIGGER_PARTIAL, + XK_XBUTTON_LTRIGGER_FULL, + + XK_XBUTTON_RTRIGGER_PARTIAL, + XK_XBUTTON_RTRIGGER_FULL, + + XK_STICK1_UP, + XK_STICK1_DOWN, + XK_STICK1_LEFT, + XK_STICK1_RIGHT, + + XK_STICK2_UP, + XK_STICK2_DOWN, + XK_STICK2_LEFT, + XK_STICK2_RIGHT, + + XK_UP_DOWN, + XK_LEFT_RIGHT, + + XK_MAX_KEYS, +} xKey_t; + +#endif // XBOXSTUBS_H \ No newline at end of file diff --git a/r5dev/core/CMakeLists.txt b/r5dev/core/CMakeLists.txt index 75fec9ab..d8f575bf 100644 --- a/r5dev/core/CMakeLists.txt +++ b/r5dev/core/CMakeLists.txt @@ -41,6 +41,8 @@ target_link_libraries( ${PROJECT_NAME} PRIVATE "wldap32.lib" "ws2_32.lib" "Rpcrt4.lib" + "iphlpapi.lib" + "Winmm.lib" "vpc" "memoverride" @@ -56,26 +58,35 @@ target_link_libraries( ${PROJECT_NAME} PRIVATE "vphysics" "SigCache_Pb" + "LiveAPI_Pb" "SV_RCon_Pb" "CL_RCon_Pb" - "rtech_tools" + "rson" "rtech_game" + "playlists" "stryder" - "libdetours" "liblzham" + "libzstd" + "liblz4" + + "libdetours" "libcurl" "libprotobuf" "libspdlog" "libdetour" "navdebugutils" + "EAThread" + "DirtySDK" + "networksystem" "pluginsystem" "filesystem" "datacache" "EbisuSDK" + "GFSDK" "localize" @@ -98,6 +109,16 @@ target_link_libraries( ${PROJECT_NAME} PRIVATE "rui" "d3d11.lib" + "${THIRDPARTY_SOURCE_DIR}/nvapi/amd64/nvapi64.lib" +) +endif() + +if( NOT ${PROJECT_NAME} STREQUAL "client" ) +target_link_libraries( ${PROJECT_NAME} PRIVATE + "libmbedcrypto" + "libmbedtls" + "libmbedx509" + "libjwt" ) endif() @@ -141,6 +162,11 @@ target_compile_definitions( ${PROJECT_NAME} PRIVATE endif() +target_include_directories( ${PROJECT_NAME} PRIVATE + "${THIRDPARTY_SOURCE_DIR}/dirtysdk/include/" + "${THIRDPARTY_SOURCE_DIR}/ea/" +) + target_link_options( ${PROJECT_NAME} PRIVATE "/STACK:8000000" # Match game executable stack reserve size diff --git a/r5dev/core/assert.h b/r5dev/core/assert.h index 226321f0..50ce39ae 100644 --- a/r5dev/core/assert.h +++ b/r5dev/core/assert.h @@ -10,4 +10,5 @@ // } while (false) //#else # define Assert(condition, ...) assert(condition) +# define AssertFatalMsg Assert //#endif diff --git a/r5dev/core/dllmain.cpp b/r5dev/core/dllmain.cpp index da05d31b..5415390a 100644 --- a/r5dev/core/dllmain.cpp +++ b/r5dev/core/dllmain.cpp @@ -3,6 +3,7 @@ #include "core/init.h" #include "core/logdef.h" #include "core/logger.h" +#include "tier0/cpu.h" #include "tier0/basetypes.h" #include "tier0/crashhandler.h" #include "tier0/commandline.h" @@ -15,6 +16,7 @@ #include "windows/system.h" #include "mathlib/mathlib.h" #include "launcher/launcher.h" +#include "protobuf/stubs/common.h" #ifndef DEDICATED #define SDK_DEFAULT_CFG "cfg/system/startup_default.cfg" @@ -24,6 +26,12 @@ bool g_bSdkInitialized = false; +bool g_bSdkInitCallInitiated = false; +bool g_bSdkShutdownCallInitiated = false; + +bool g_bSdkShutdownInitiatedFromConsoleHandler = false; +HMODULE s_hModuleHandle = NULL; + //############################################################################# // UTILITY //############################################################################# @@ -41,14 +49,14 @@ void Show_Emblem() // Logged as 'SYSTEM_ERROR' for its red color. for (size_t i = 0; i < SDK_ARRAYSIZE(R5R_EMBLEM); i++) { - DevMsg(eDLL_T::SYSTEM_ERROR, "%s\n", R5R_EMBLEM[i]); + Msg(eDLL_T::SYSTEM_ERROR, "%s\n", R5R_EMBLEM[i]); } // Log the SDK's 'build_id' under the emblem. - DevMsg(eDLL_T::SYSTEM_ERROR, + Msg(eDLL_T::SYSTEM_ERROR, "+------------------------------------------------[%s%010d%s]-+\n", g_svYellowF, g_SDKDll.GetNTHeaders()->FileHeader.TimeDateStamp, g_svRedF); - DevMsg(eDLL_T::SYSTEM_ERROR, "\n"); + Msg(eDLL_T::SYSTEM_ERROR, "\n"); } //############################################################################# @@ -62,12 +70,10 @@ void Tier0_Init() g_RadAudioDecoderDll.InitFromName("binkawin64.dll"); g_RadAudioSystemDll.InitFromName("mileswin64.dll"); #endif // !DEDICATED - - g_pCmdLine = g_GameDll.GetExportedSymbol("g_pCmdLine").RCast(); g_CoreMsgVCallback = &EngineLoggerSink; // Setup logger callback sink. g_pCmdLine->CreateCmdLine(GetCommandLineA()); - g_CrashHandler->SetCrashCallback(&Crash_Callback); + g_CrashHandler.SetCrashCallback(&Crash_Callback); // This prevents the game from recreating it, // see 'CCommandLine::StaticCreateCmdLine' for @@ -77,6 +83,28 @@ void Tier0_Init() void SDK_Init() { + assert(!g_bSdkInitialized); + + CheckSystemCPU(); // Check CPU as early as possible; error out if CPU isn't supported. + + if (g_bSdkInitCallInitiated) + { + spdlog::error("Recursive initialization!\n"); + return; + } + + // Set after checking cpu and initializing MathLib since we check CPU + // features there. Else we crash on the recursive initialization error as + // SpdLog uses SSE features. + g_bSdkInitCallInitiated = true; + + MathLib_Init(); // Initialize Mathlib. + + PEB64* pEnv = CModule::GetProcessEnvironmentBlock(); + + g_GameDll.InitFromBase(pEnv->ImageBaseAddress); + g_SDKDll.InitFromBase((QWORD)s_hModuleHandle); + Tier0_Init(); if (!CommandLine()->CheckParm("-launcher")) @@ -96,7 +124,9 @@ void SDK_Init() SpdLog_Init(bAnsiColor); Show_Emblem(); - Winsock_Init(); // Initialize Winsock. + Winsock_Startup(); // Initialize Winsock. + DirtySDK_Startup(); + Systems_Init(); WinSys_Init(); @@ -104,6 +134,7 @@ void SDK_Init() Input_Init(); #endif // !DEDICATED + GOOGLE_PROTOBUF_VERIFY_VERSION; curl_global_init(CURL_GLOBAL_ALL); lzham_enable_fail_exceptions(true); @@ -118,14 +149,26 @@ void SDK_Shutdown() { assert(g_bSdkInitialized); - if (!g_bSdkInitialized) + // Also check CPU in shutdown, since this function is exported, if they + // call this with an unsupported CPU we should let them know rather than + // crashing the process. + CheckSystemCPU(); + + if (g_bSdkShutdownCallInitiated) { spdlog::error("Recursive shutdown!\n"); return; } - g_bSdkInitialized = false; - DevMsg(eDLL_T::NONE, "GameSDK shutdown initiated\n"); + g_bSdkShutdownCallInitiated = true; + + if (!g_bSdkInitialized) + { + spdlog::error("Not initialized!\n"); + return; + } + + Msg(eDLL_T::NONE, "GameSDK shutdown initiated\n"); curl_global_cleanup(); @@ -135,10 +178,18 @@ void SDK_Shutdown() WinSys_Shutdown(); Systems_Shutdown(); + + DirtySDK_Shutdown(); Winsock_Shutdown(); SpdLog_Shutdown(); - Console_Shutdown(); + + // If the shutdown was initiated from the console window itself, don't + // shutdown the console as it would otherwise deadlock in FreeConsole! + if (!g_bSdkShutdownInitiatedFromConsoleHandler) + Console_Shutdown(); + + g_bSdkInitialized = false; } //############################################################################# @@ -147,27 +198,18 @@ void SDK_Shutdown() BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { - CheckCPU(); // Check CPU as early as possible; error out if CPU isn't supported. - MathLib_Init(); // Initialize Mathlib. - NOTE_UNUSED(lpReserved); switch (dwReason) { case DLL_PROCESS_ATTACH: { - PEB64* pEnv = CModule::GetProcessEnvironmentBlock(); - - g_GameDll.InitFromBase(pEnv->ImageBaseAddress); - g_SDKDll.InitFromBase((QWORD)hModule); - - SDK_Init(); + s_hModuleHandle = hModule; break; } - case DLL_PROCESS_DETACH: { - SDK_Shutdown(); + s_hModuleHandle = NULL; break; } } diff --git a/r5dev/core/init.cpp b/r5dev/core/init.cpp index 3e843127..3677676d 100644 --- a/r5dev/core/init.cpp +++ b/r5dev/core/init.cpp @@ -18,9 +18,8 @@ #include "tier0/sigcache.h" #include "tier1/cmd.h" #include "tier1/cvar.h" +#include "tier1/keyvalues_iface.h" #include "vpc/IAppSystem.h" -#include "vpc/keyvalues.h" -#include "vpc/rson.h" #include "vpc/interfaces.h" #include "common/callback.h" #include "common/completion.h" @@ -38,6 +37,7 @@ #include "codecs/miles/miles_impl.h" #include "codecs/miles/radshal_wasapi.h" #endif // !DEDICATED +#include "vphysics/physics_collide.h" #include "vphysics/QHull.h" #include "engine/staticpropmgr.h" #include "materialsystem/cmaterialsystem.h" @@ -46,6 +46,7 @@ #include "vgui/vgui_baseui_interface.h" #include "vgui/vgui_debugpanel.h" #include "vgui/vgui_fpspanel.h" +#include "vgui/vgui_controls/RichText.h" #include "vguimatsurface/MatSystemSurface.h" #include "engine/client/vengineclient_impl.h" #include "engine/client/cdll_engine_int.h" @@ -58,13 +59,23 @@ #include "engine/server/datablock_sender.h" #endif // !CLIENT_DLL #include "studiorender/studiorendercontext.h" -#include "rtech/rtech_game.h" -#include "rtech/rtech_utils.h" +#ifndef CLIENT_DLL +#include "rtech/liveapi/liveapi.h" +#endif // !CLIENT_DLL +#include "rtech/rstdlib.h" +#include "rtech/rson.h" +#include "rtech/async/asyncio.h" +#include "rtech/pak/pakalloc.h" +#include "rtech/pak/pakparse.h" +#include "rtech/pak/pakstate.h" +#include "rtech/pak/pakstream.h" #include "rtech/stryder/stryder.h" +#include "rtech/playlists/playlists.h" #ifndef DEDICATED #include "rtech/rui/rui.h" #include "engine/client/cl_ents_parse.h" #include "engine/client/cl_main.h" +#include "engine/client/cl_rcon.h" #include "engine/client/cl_splitscreen.h" #endif // !DEDICATED #include "engine/client/client.h" @@ -87,6 +98,7 @@ #include "engine/networkstringtable.h" #ifndef CLIENT_DLL #include "engine/server/sv_main.h" +#include "engine/server/sv_rcon.h" #endif // !CLIENT_DLL #include "engine/sdk_dll.h" #include "engine/sys_dll.h" @@ -95,13 +107,15 @@ #include "engine/sys_utils.h" #ifndef DEDICATED #include "engine/sys_getmodes.h" -#include "engine/gl_rmain.h" #include "engine/sys_mainwind.h" #include "engine/matsys_interface.h" +#include "engine/gl_rmain.h" #include "engine/gl_matsysiface.h" +#include "engine/gl_drawlights.h" #include "engine/gl_screen.h" #include "engine/gl_rsurf.h" #include "engine/debugoverlay.h" +#include "engine/keys.h" #endif // !DEDICATED #include "vscript/languages/squirrel_re/include/squirrel.h" #include "vscript/languages/squirrel_re/include/sqvm.h" @@ -137,6 +151,11 @@ #include "windows/id3dx.h" #endif // !DEDICATED +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protowebsocket.h" + ///////////////////////////////////////////////////////////////////////////////////////////////// // @@ -176,7 +195,7 @@ void ScriptConstantRegistrationCallback(CSquirrelVM* s) void Systems_Init() { - DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); + Msg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); QuerySystemInfo(); DetourRegister(); @@ -186,8 +205,8 @@ void Systems_Init() DetourInit(); initTimer.End(); - DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); - DevMsg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->InitDB()", + Msg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); + Msg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->InitDB()", initTimer.GetDuration().GetSeconds(), initTimer.GetDuration().GetCycles()); initTimer.Start(); @@ -197,9 +216,9 @@ void Systems_Init() DetourUpdateThread(GetCurrentThread()); // Hook functions - for (const IDetour* Detour : g_DetourVec) + for (const IDetour* pd : g_DetourVec) { - Detour->Attach(); + pd->Detour(true); } // Patch instructions @@ -210,16 +229,15 @@ void Systems_Init() if (hr != NO_ERROR) { // Failed to hook into the process, terminate + Assert(0); Error(eDLL_T::COMMON, 0xBAD0C0DE, "Failed to detour process: error code = %08x\n", hr); } initTimer.End(); - DevMsg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->Attach()", + Msg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->Attach()", initTimer.GetDuration().GetSeconds(), initTimer.GetDuration().GetCycles()); - DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); - DevMsg(eDLL_T::NONE, "\n"); - - ConVar_StaticInit(); + Msg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); + Msg(eDLL_T::NONE, "\n"); #ifdef DEDICATED InitCommandLineParameters(); @@ -232,6 +250,8 @@ void Systems_Init() ServerScriptRegister_Callback = Script_RegisterServerFunctions; CoreServerScriptRegister_Callback = Script_RegisterCoreServerFunctions; AdminPanelScriptRegister_Callback = Script_RegisterAdminPanelFunctions; + + ServerScriptRegisterEnum_Callback = Script_RegisterServerEnums; #endif// !CLIENT_DLL #ifndef SERVER_DLL @@ -257,6 +277,18 @@ void Systems_Init() void Systems_Shutdown() { + // Shutdown RCON (closes all open sockets) +#ifndef CLIENT_DLL + RCONServer()->Shutdown(); +#endif// !CLIENT_DLL +#ifndef SERVER_DLL + RCONClient()->Shutdown(); +#endif // !SERVER_DLL + +#ifndef CLIENT_DLL + LiveAPISystem()->Shutdown(); +#endif// !CLIENT_DLL + CFastTimer shutdownTimer; shutdownTimer.Start(); @@ -265,19 +297,19 @@ void Systems_Shutdown() DetourUpdateThread(GetCurrentThread()); // Unhook functions - for (const IDetour* Detour : g_DetourVec) + for (const IDetour* pd : g_DetourVec) { - Detour->Detach(); + pd->Detour(false); } // Commit the transaction DetourTransactionCommit(); shutdownTimer.End(); - DevMsg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->Detach()", + Msg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->Detach()", shutdownTimer.GetDuration().GetSeconds(), shutdownTimer.GetDuration().GetCycles()); - DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); - DevMsg(eDLL_T::NONE, "\n"); + Msg(eDLL_T::NONE, "+-------------------------------------------------------------+\n"); + Msg(eDLL_T::NONE, "\n"); } ///////////////////////////////////////////////////// @@ -291,25 +323,51 @@ void Systems_Shutdown() // ///////////////////////////////////////////////////// -void Winsock_Init() +void Winsock_Startup() { WSAData wsaData{}; - int nError = ::WSAStartup(MAKEWORD(2, 2), &wsaData); + const int nError = ::WSAStartup(MAKEWORD(2, 2), &wsaData); + if (nError != 0) { - Error(eDLL_T::COMMON, NO_ERROR, "%s: Failed to start Winsock: (%s)\n", + Error(eDLL_T::COMMON, 0, "%s: Windows Sockets API startup failure: (%s)\n", __FUNCTION__, NET_ErrorString(WSAGetLastError())); } } + void Winsock_Shutdown() { - int nError = ::WSACleanup(); + const int nError = ::WSACleanup(); + if (nError != 0) { - Error(eDLL_T::COMMON, NO_ERROR, "%s: Failed to stop Winsock: (%s)\n", + Error(eDLL_T::COMMON, 0, "%s: Windows Sockets API shutdown failure: (%s)\n", __FUNCTION__, NET_ErrorString(WSAGetLastError())); } } + +void DirtySDK_Startup() +{ + const int32_t netConStartupRet = NetConnStartup("-servicename=sourcesdk"); + + if (netConStartupRet < 0) + { + Error(eDLL_T::COMMON, 0, "%s: Network connection module startup failure: (%i)\n", + __FUNCTION__, netConStartupRet); + } +} + +void DirtySDK_Shutdown() +{ + const int32_t netConShutdownRet = NetConnShutdown(0); + + if (netConShutdownRet < 0) + { + Error(eDLL_T::COMMON, 0, "%s: Network connection module shutdown failure: (%i)\n", + __FUNCTION__, netConShutdownRet); + } +} + void QuerySystemInfo() { #ifndef DEDICATED @@ -324,20 +382,20 @@ void QuerySystemInfo() if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) // Only log the primary device. { - DevMsg(eDLL_T::NONE, "%-25s: '%s'\n", "GPU model identifier", dd.DeviceString); + Msg(eDLL_T::NONE, "%-25s: '%s'\n", "GPU model identifier", dd.DeviceString); } } #endif // !DEDICATED const CPUInformation& pi = GetCPUInformation(); - DevMsg(eDLL_T::NONE, "%-25s: '%s'\n","CPU model identifier", pi.m_szProcessorBrand); - DevMsg(eDLL_T::NONE, "%-25s: '%s'\n","CPU vendor tag", pi.m_szProcessorID); - DevMsg(eDLL_T::NONE, "%-25s: '%12hhu' ('%2hhu' %s)\n", "CPU core count", pi.m_nPhysicalProcessors, pi.m_nLogicalProcessors, "logical"); - DevMsg(eDLL_T::NONE, "%-25s: '%12lld' ('%6.1f' %s)\n", "CPU core speed", pi.m_Speed, float(pi.m_Speed / 1000000), "MHz"); - DevMsg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L1 cache", "(KiB)", pi.m_nL1CacheSizeKb, pi.m_nL1CacheDesc); - DevMsg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L2 cache", "(KiB)", pi.m_nL2CacheSizeKb, pi.m_nL2CacheDesc); - DevMsg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L3 cache", "(KiB)", pi.m_nL3CacheSizeKb, pi.m_nL3CacheDesc); + Msg(eDLL_T::NONE, "%-25s: '%s'\n","CPU model identifier", pi.m_szProcessorBrand); + Msg(eDLL_T::NONE, "%-25s: '%s'\n","CPU vendor tag", pi.m_szProcessorID); + Msg(eDLL_T::NONE, "%-25s: '%12hhu' ('%2hhu' %s)\n", "CPU core count", pi.m_nPhysicalProcessors, pi.m_nLogicalProcessors, "logical"); + Msg(eDLL_T::NONE, "%-25s: '%12lld' ('%6.1f' %s)\n", "CPU core speed", pi.m_Speed, float(pi.m_Speed / 1000000), "MHz"); + Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L1 cache", "(KiB)", pi.m_nL1CacheSizeKb, pi.m_nL1CacheDesc); + Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L2 cache", "(KiB)", pi.m_nL2CacheSizeKb, pi.m_nL2CacheDesc); + Msg(eDLL_T::NONE, "%-20s%s: '%12lu' ('0x%-8X')\n", "L3 cache", "(KiB)", pi.m_nL3CacheSizeKb, pi.m_nL3CacheDesc); MEMORYSTATUSEX statex{}; statex.dwLength = sizeof(statex); @@ -350,8 +408,8 @@ void QuerySystemInfo() DWORDLONG availPhysical = (statex.ullAvailPhys / 1024) / 1024; DWORDLONG availVirtual = (statex.ullAvailVirtual / 1024) / 1024; - DevMsg(eDLL_T::NONE, "%-20s%s: '%12llu' ('%9llu' %s)\n", "Total system memory", "(MiB)", totalPhysical, totalVirtual, "virtual"); - DevMsg(eDLL_T::NONE, "%-20s%s: '%12llu' ('%9llu' %s)\n", "Avail system memory", "(MiB)", availPhysical, availVirtual, "virtual"); + Msg(eDLL_T::NONE, "%-20s%s: '%12llu' ('%9llu' %s)\n", "Total system memory", "(MiB)", totalPhysical, totalVirtual, "virtual"); + Msg(eDLL_T::NONE, "%-20s%s: '%12llu' ('%9llu' %s)\n", "Avail system memory", "(MiB)", availPhysical, availVirtual, "virtual"); } else { @@ -360,30 +418,6 @@ void QuerySystemInfo() } } -void CheckCPU() // Respawn's engine and our SDK utilize POPCNT, SSE3 and SSSE3 (Supplemental SSE 3 Instructions). -{ - const CPUInformation& pi = GetCPUInformation(); - static char szBuf[1024]; - if (!pi.m_bSSE3) - { - V_snprintf(szBuf, sizeof(szBuf), "CPU does not have %s!\n", "SSE 3"); - MessageBoxA(NULL, szBuf, "Unsupported CPU", MB_ICONERROR | MB_OK); - ExitProcess(0xFFFFFFFF); - } - if (!pi.m_bSSSE3) - { - V_snprintf(szBuf, sizeof(szBuf), "CPU does not have %s!\n", "SSSE 3 (Supplemental SSE 3 Instructions)"); - MessageBoxA(NULL, szBuf, "Unsupported CPU", MB_ICONERROR | MB_OK); - ExitProcess(0xFFFFFFFF); - } - if (!pi.m_bPOPCNT) - { - V_snprintf(szBuf, sizeof(szBuf), "CPU does not have %s!\n", "POPCNT"); - MessageBoxA(NULL, szBuf, "Unsupported CPU", MB_ICONERROR | MB_OK); - ExitProcess(0xFFFFFFFF); - } -} - #if defined (DEDICATED) #define SIGDB_FILE "cfg/server/startup.bin" #elif defined (CLIENT_DLL) @@ -394,27 +428,30 @@ void CheckCPU() // Respawn's engine and our SDK utilize POPCNT, SSE3 and SSSE3 ( void DetourInit() // Run the sigscan { - const bool bLogAdr = CommandLine()->CheckParm("-sig_toconsole") ? true : false; const bool bNoSmap = CommandLine()->CheckParm("-nosmap") ? true : false; + const bool bLogAdr = CommandLine()->CheckParm("-sig_toconsole") ? true : false; bool bInitDivider = false; g_SigCache.SetDisabled(bNoSmap); - g_SigCache.LoadCache(SIGDB_FILE); + g_SigCache.ReadCache(SIGDB_FILE); - for (const IDetour* Detour : g_DetourVec) + // No debug logging in non dev builds. + const bool bDevMode = !IsCert() && !IsRetail(); + + for (const IDetour* pd : g_DetourVec) { - Detour->GetCon(); // Constants. - Detour->GetFun(); // Functions. - Detour->GetVar(); // Variables. + pd->GetCon(); // Constants. + pd->GetFun(); // Functions. + pd->GetVar(); // Variables. - if (bLogAdr) + if (bDevMode && bLogAdr) { if (!bInitDivider) { bInitDivider = true; spdlog::debug("+---------------------------------------------------------------------+\n"); } - Detour->GetAdr(); + pd->GetAdr(); spdlog::debug("+---------------------------------------------------------------------+\n"); } } @@ -426,14 +463,14 @@ void DetourInit() // Run the sigscan g_SigCache.WriteCache(SIGDB_FILE); g_SigCache.InvalidateMap(); -} + } void DetourAddress() // Test the sigscan results { spdlog::debug("+---------------------------------------------------------------------+\n"); - for (const IDetour* Detour : g_DetourVec) + for (const IDetour* pd : g_DetourVec) { - Detour->GetAdr(); + pd->GetAdr(); spdlog::debug("+---------------------------------------------------------------------+\n"); } } @@ -448,7 +485,6 @@ void DetourRegister() // Register detour classes to be searched and hooked. // Tier1 REGISTER(VCommandLine); - REGISTER(VConVar); REGISTER(VCVar); // VPC @@ -492,6 +528,7 @@ void DetourRegister() // Register detour classes to be searched and hooked. #endif // !DEDICATED // VPhysics + REGISTER(VPhysicsCollide); REGISTER(VQHull); // StaticPropMgr @@ -509,6 +546,7 @@ void DetourRegister() // Register detour classes to be searched and hooked. // VGui REGISTER(VEngineVGui); // REGISTER CLIENT ONLY! REGISTER(VFPSPanel); // REGISTER CLIENT ONLY! + REGISTER(VVGUIRichText); // REGISTER CLIENT ONLY! REGISTER(VMatSystemSurface); // Client @@ -536,9 +574,17 @@ void DetourRegister() // Register detour classes to be searched and hooked. #endif // !DEDICATED // RTech - REGISTER(V_RTechGame); - REGISTER(V_RTechUtils); + REGISTER(V_ReSTD); + + REGISTER(V_AsyncIO); + + REGISTER(V_PakAlloc); + REGISTER(V_PakParse); + REGISTER(V_PakState); + REGISTER(V_PakStream); + REGISTER(VStryder); + REGISTER(VPlaylists); #ifndef DEDICATED REGISTER(V_Rui); @@ -573,6 +619,7 @@ void DetourRegister() // Register detour classes to be searched and hooked. REGISTER(VGL_RMain); REGISTER(VMatSys_Interface); REGISTER(VGL_MatSysIFace); + REGISTER(VGL_DrawLights); REGISTER(VGL_Screen); #endif // !DEDICATED @@ -583,6 +630,7 @@ void DetourRegister() // Register detour classes to be searched and hooked. REGISTER(VGL_RSurf); REGISTER(VDebugOverlay); // !TODO: This also needs to be exposed to server dll!!! + REGISTER(VKeys); #endif // !DEDICATED // VScript @@ -631,4 +679,12 @@ void DetourRegister() // Register detour classes to be searched and hooked. REGISTER(VInputSystem); REGISTER(VDXGI); #endif // !DEDICATED -} \ No newline at end of file +} + +//----------------------------------------------------------------------------- +// Singleton accessors: +//----------------------------------------------------------------------------- +IKeyValuesSystem* KeyValuesSystem() +{ + return g_pKeyValuesSystem; +} diff --git a/r5dev/core/init.h b/r5dev/core/init.h index 0647794e..7b35872c 100644 --- a/r5dev/core/init.h +++ b/r5dev/core/init.h @@ -1,18 +1,20 @@ #pragma once -void SDK_Init(); -void SDK_Shutdown(); +PLATFORM_INTERFACE void SDK_Init(); +PLATFORM_INTERFACE void SDK_Shutdown(); void Systems_Init(); void Systems_Shutdown(); -void Winsock_Init(); +void Winsock_Startup(); void Winsock_Shutdown(); +void DirtySDK_Startup(); +void DirtySDK_Shutdown(); void QuerySystemInfo(); -void CheckCPU(); void DetourInit(); void DetourAddress(); void DetourRegister(); extern bool g_bSdkInitialized; +extern bool g_bSdkShutdownInitiatedFromConsoleHandler; diff --git a/r5dev/core/logdef.cpp b/r5dev/core/logdef.cpp index 27986967..d7441c8f 100644 --- a/r5dev/core/logdef.cpp +++ b/r5dev/core/logdef.cpp @@ -4,9 +4,47 @@ std::shared_ptr g_TermLogger; std::shared_ptr g_ImGuiLogger; +std::shared_ptr g_SuppementalToolsLogger; + std::ostringstream g_LogStream; std::shared_ptr g_LogSink; +#ifndef _TOOLS +static void SpdLog_CreateRotatingLoggers() +{ + /************************ + * ROTATE LOGGER SETUP * + ************************/ + spdlog::rotating_logger_mt("squirrel_re(warning)" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "script_warning.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); + spdlog::rotating_logger_mt("squirrel_re" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "script.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); + spdlog::rotating_logger_mt("sdk" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "message.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); + spdlog::rotating_logger_mt("sdk(warning)" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "warning.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); + spdlog::rotating_logger_mt("sdk(error)" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "error.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); + spdlog::rotating_logger_mt("net_trace" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "net_trace.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); +#ifndef DEDICATED + spdlog::rotating_logger_mt("netconsole" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "netconsole.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); +#endif // !DEDICATED + spdlog::rotating_logger_mt("filesystem" + , fmt::format("{:s}/{:s}", g_LogSessionDirectory, "filesystem.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); +} +#endif // !_TOOLS + +#ifdef _TOOLS +// NOTE: used for tools as additional file logger on top of the existing terminal logger. +void SpdLog_InstallSupplementalLogger(const char* pszLoggerName, const char* pszLogFileName, const char* pszPattern, const bool bTruncate) +{ + g_SuppementalToolsLogger = spdlog::basic_logger_mt(pszLoggerName, pszLogFileName, bTruncate); + g_SuppementalToolsLogger->set_pattern(pszPattern); +} +#endif // _TOOLS + //############################################################################# // SPDLOG INIT //############################################################################# @@ -20,9 +58,9 @@ void SpdLog_Init(const bool bAnsiColor) return; } -#ifndef NETCONSOLE +#ifndef _TOOLS g_LogSessionUUID = CreateUUID(); - g_LogSessionDirectory = fmt::format("platform\\logs\\{:s}", g_LogSessionUUID); + g_LogSessionDirectory = fmt::format("platform/logs/{:s}", g_LogSessionUUID); /************************ * IMGUI LOGGER SETUP * ************************/ @@ -33,16 +71,16 @@ void SpdLog_Init(const bool bAnsiColor) g_ImGuiLogger->set_pattern("%v"); g_ImGuiLogger->set_level(spdlog::level::trace); } -#endif // !NETCONSOLE +#endif // !_TOOLS /************************ * WINDOWS LOGGER SETUP * ************************/ { -#ifdef NETCONSOLE +#ifdef _TOOLS g_TermLogger = spdlog::default_logger(); #else g_TermLogger = spdlog::stdout_logger_mt("win_console"); -#endif // NETCONSOLE +#endif // _TOOLS // Determine if user wants ansi-color logging in the terminal. if (bAnsiColor) @@ -57,38 +95,15 @@ void SpdLog_Init(const bool bAnsiColor) //g_TermLogger->set_level(spdlog::level::trace); } -#ifndef NETCONSOLE +#ifndef _TOOLS spdlog::set_default_logger(g_TermLogger); // Set as default. - SpdLog_Create(); -#endif // !NETCONSOLE + SpdLog_CreateRotatingLoggers(); +#endif // !_TOOLS spdlog::set_level(spdlog::level::trace); - bInitialized = true; -} + spdlog::flush_every(std::chrono::seconds(5)); -void SpdLog_Create() -{ - /************************ - * ROTATE LOGGER SETUP * - ************************/ - spdlog::rotating_logger_mt("squirrel_re(warning)" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "script_warning.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); - spdlog::rotating_logger_mt("squirrel_re" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "script.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); - spdlog::rotating_logger_mt("sdk" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "message.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); - spdlog::rotating_logger_mt("sdk(warning)" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "warning.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); - spdlog::rotating_logger_mt("sdk(error)" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "error.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); - spdlog::rotating_logger_mt("net_trace" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "net_trace.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); -#ifndef DEDICATED - spdlog::rotating_logger_mt("netconsole" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "netconsole.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); -#endif // !DEDICATED - spdlog::rotating_logger_mt("filesystem" - , fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "filesystem.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v"); + bInitialized = true; } //############################################################################# @@ -97,4 +112,8 @@ void SpdLog_Create() void SpdLog_Shutdown() { spdlog::shutdown(); +#ifdef _TOOLS + // Destroy the tools logger to flush it. + g_SuppementalToolsLogger.reset(); +#endif // !_TOOLS } diff --git a/r5dev/core/logdef.h b/r5dev/core/logdef.h index b43c8006..0da40a4e 100644 --- a/r5dev/core/logdef.h +++ b/r5dev/core/logdef.h @@ -18,11 +18,19 @@ inline bool g_bSpdLog_UseAnsiClr = false; extern std::shared_ptr g_TermLogger; extern std::shared_ptr g_ImGuiLogger; +#ifdef _TOOLS +extern std::shared_ptr g_SuppementalToolsLogger; +#endif // _TOOLS + //------------------------------------------------------------------------- // IMGUI CONSOLE SINK | extern std::ostringstream g_LogStream; extern std::shared_ptr g_LogSink; void SpdLog_Init(const bool bAnsiColor); -void SpdLog_Create(void); void SpdLog_Shutdown(void); + +#ifdef _TOOLS +void SpdLog_InstallSupplementalLogger(const char* pszLoggerName, const char* pszLogFileName, + const char* pszPattern = "[%Y-%m-%d %H:%M:%S.%e] %v", const bool bTruncate = true); +#endif // _TOOLS diff --git a/r5dev/core/logger.cpp b/r5dev/core/logger.cpp index 765ccef9..ebc1ba08 100644 --- a/r5dev/core/logger.cpp +++ b/r5dev/core/logger.cpp @@ -9,13 +9,13 @@ #ifndef CLIENT_DLL #include "engine/server/sv_rcon.h" #endif // !CLIENT_DLL -#ifndef NETCONSOLE +#ifndef _TOOLS #include "vscript/languages/squirrel_re/include/sqstdaux.h" -#endif // !NETCONSOLE +#endif // !_TOOLS static const std::regex s_AnsiRowRegex("\\\033\\[.*?m"); std::mutex g_LogMutex; -#if !defined (DEDICATED) && !defined (NETCONSOLE) +#if !defined (DEDICATED) && !defined (_TOOLS) ImVec4 CheckForWarnings(LogType_t type, eDLL_T context, const ImVec4& defaultCol) { ImVec4 color = defaultCol; @@ -67,7 +67,7 @@ ImVec4 GetColorForContext(LogType_t type, eDLL_T context) return CheckForWarnings(type, context, ImVec4(0.81f, 0.81f, 0.81f, 1.00f)); } } -#endif // !DEDICATED && !NETCONSOLE +#endif // !DEDICATED && !_TOOLS const char* GetContextNameByIndex(eDLL_T context, const bool ansiColor = false) { @@ -150,18 +150,18 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, const char* pszContext = GetContextNameByIndex(context, bUseColor); message.append(pszContext); -#if !defined (DEDICATED) && !defined (NETCONSOLE) +#if !defined (DEDICATED) && !defined (_TOOLS) ImVec4 overlayColor = GetColorForContext(logType, context); eDLL_T overlayContext = context; -#endif // !DEDICATED && !NETCONSOLE +#endif // !DEDICATED && !_TOOLS -#if !defined (NETCONSOLE) +#if !defined (_TOOLS) bool bSquirrel = false; bool bWarning = false; bool bError = false; #else NOTE_UNUSED(pszLogger); -#endif // !NETCONSOLE +#endif // !_TOOLS //------------------------------------------------------------------------- // Setup logger and context @@ -169,24 +169,24 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, switch (logType) { case LogType_t::LOG_WARNING: -#if !defined (DEDICATED) && !defined (NETCONSOLE) +#if !defined (DEDICATED) && !defined (_TOOLS) overlayContext = eDLL_T::SYSTEM_WARNING; -#endif // !DEDICATED && !NETCONSOLE +#endif // !DEDICATED && !_TOOLS if (bUseColor) { message.append(g_svYellowF); } break; case LogType_t::LOG_ERROR: -#if !defined (DEDICATED) && !defined (NETCONSOLE) +#if !defined (DEDICATED) && !defined (_TOOLS) overlayContext = eDLL_T::SYSTEM_ERROR; -#endif // !DEDICATED && !NETCONSOLE +#endif // !DEDICATED && !_TOOLS if (bUseColor) { message.append(g_svRedF); } break; -#ifndef NETCONSOLE +#ifndef _TOOLS case LogType_t::SQ_INFO: bSquirrel = true; break; @@ -198,7 +198,7 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, bSquirrel = true; bWarning = true; break; -#endif // !NETCONSOLE +#endif // !_TOOLS default: break; } @@ -211,7 +211,7 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, const string formatted = FormatV(pszFormat, argsCopy); va_end(argsCopy); -#ifndef NETCONSOLE +#ifndef _TOOLS //------------------------------------------------------------------------- // Colorize script warnings and errors //------------------------------------------------------------------------- @@ -253,7 +253,7 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, message.append(g_svYellowF); } } -#endif // !NETCONSOLE +#endif // !_TOOLS message.append(formatted); //------------------------------------------------------------------------- @@ -271,7 +271,11 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, } } -#ifndef NETCONSOLE + // If a debugger is attached, emit the text there too + if (Plat_IsInDebugSession()) + Plat_DebugString(message.c_str()); + +#ifndef _TOOLS // Output is always logged to the file. std::shared_ptr ntlogger = spdlog::get(pszLogger); // <-- Obtain by 'pszLogger'. assert(ntlogger.get() != nullptr); @@ -290,14 +294,14 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, g_ImGuiLogger->debug(message); const string logStreamBuf = g_LogStream.str(); - g_pConsole->AddLog(ConLog_t(logStreamBuf, overlayColor)); + g_Console.AddLog(logStreamBuf.c_str(), ImGui::ColorConvertFloat4ToU32(overlayColor)); // We can only log to the in-game overlay console when the SDK has // been fully initialized, due to the use of ConVar's. if (g_bSdkInitialized && logLevel >= LogLevel_t::LEVEL_NOTIFY) { // Draw to mini console. - g_pOverlay->AddLog(overlayContext, logStreamBuf.c_str()); + g_TextOverlay.AddLog(overlayContext, logStreamBuf.c_str()); } #endif // !DEDICATED } @@ -307,7 +311,12 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context, g_LogStream.clear(); #endif // !DEDICATED -#endif // !NETCONSOLE +#else + if (g_SuppementalToolsLogger) + { + g_SuppementalToolsLogger->debug(message); + } +#endif if (exitCode) // Terminate the process if an exit code was passed. { diff --git a/r5dev/core/r5dev.h b/r5dev/core/r5dev.h index fe65d82e..54797c6f 100644 --- a/r5dev/core/r5dev.h +++ b/r5dev/core/r5dev.h @@ -8,10 +8,10 @@ __declspec(dllexport) void DummyExport() static const char* const R5R_EMBLEM[] = { R"(+-------------------------------------------------------------+)", - R"(| ___ ___ ___ _ _ _ ___ ___ |)", - R"(| | _ \ __| _ \___| |___ __ _ __| |___ __| | __ _|_ ) |_ ) |)", - R"(| | /__ \ / -_) / _ \/ _` / _` / -_) _` | \ V // / _ / / |)", - R"(| |_|_\___/_|_\___|_\___/\__,_\__,_\___\__,_| \_//___(_)___| |)", + R"(| ___ ___ ___ _ _ _ ___ _ _ |)", + R"(| | _ \ __| _ \___| |___ __ _ __| |___ __| | __ _|_ )| | | |)", + R"(| | /__ \ / -_) / _ \/ _` / _` / -_) _` | \ V // / |_ _| |)", + R"(| |_|_\___/_|_\___|_\___/\__,_\__,_\___\__,_| \_//___(_)|_| |)", R"(| |)"/*, R"(+-------------------------------------------------------------+)"*/ }; diff --git a/r5dev/core/shared_pch.h b/r5dev/core/shared_pch.h index bdfa5fe3..736212cd 100644 --- a/r5dev/core/shared_pch.h +++ b/r5dev/core/shared_pch.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/r5dev/core/stdafx.h b/r5dev/core/stdafx.h index bcb3b0c8..108f4f6d 100644 --- a/r5dev/core/stdafx.h +++ b/r5dev/core/stdafx.h @@ -18,8 +18,18 @@ #include "thirdparty/lzham/include/lzham_types.h" #include "thirdparty/lzham/include/lzham.h" +#include "thirdparty/zstd/zstd.h" +#include "thirdparty/zstd/decompress/zstd_decompress_internal.h" + +#include "thirdparty/lz4/lz4.h" + #include "thirdparty/curl/include/curl/curl.h" -#include "thirdparty/nlohmann/json.hpp" + +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/error/en.h" #if !defined(DEDICATED) && !defined(PLUGINSDK) #include "thirdparty/imgui/imgui.h" @@ -30,6 +40,10 @@ #include "thirdparty/imgui/misc/cpp/imgui_stdlib.h" #include "thirdparty/imgui/backends/imgui_impl_dx11.h" #include "thirdparty/imgui/backends/imgui_impl_win32.h" + +#include "thirdparty/nvapi/pclstats.h" +#include "thirdparty/nvapi/nvapi.h" +#include "thirdparty/nvapi/nvapi_lite_common.h" #endif // !DEDICATED && !PLUGINSDK @@ -46,11 +60,13 @@ #pragma warning(pop) // Tier0 includes. +#include "tier0/basetypes.h" +#include "tier0/wchartypes.h" #include "tier0/memaddr.h" #include "tier0/utility.h" #include "tier0/module.h" -#include "tier0/basetypes.h" #include "tier0/platform.h" +#include "tier0/platwindow.h" #include "tier0/annotations.h" #include "tier0/commonmacros.h" #include "tier0/memalloc.h" @@ -58,6 +74,7 @@ #include "tier0/dbg.h" // Tier1 includes. +#include "tier1/tier1.h" #include "tier1/cvar.h" #include "tier1/cmd.h" #include "common/global.h" diff --git a/r5dev/datacache/mdlcache.cpp b/r5dev/datacache/mdlcache.cpp index 856cedae..6d8f0c1f 100644 --- a/r5dev/datacache/mdlcache.cpp +++ b/r5dev/datacache/mdlcache.cpp @@ -12,12 +12,11 @@ #include "datacache/mdlcache.h" #include "datacache/imdlcache.h" #include "datacache/idatacache.h" -#include "rtech/rtech_utils.h" +#include "rtech/pak/paktools.h" #include "public/studio.h" -RMDLFallBack_t* g_pMDLFallback = new RMDLFallBack_t(); -std::unordered_set g_vBadMDLHandles; - +CStudioFallbackHandler g_StudioMdlFallbackHandler; +#define IS_VALID_DATACACHE_HANDLE(cacheHandle) (cacheHandle && cacheHandle != DC_INVALID_HANDLE) //----------------------------------------------------------------------------- // Purpose: finds an MDL @@ -26,33 +25,13 @@ std::unordered_set g_vBadMDLHandles; // *a3 - // Output : a pointer to the studiohdr_t object //----------------------------------------------------------------------------- -studiohdr_t* CMDLCache::FindMDL(CMDLCache* cache, MDLHandle_t handle, void* a3) +studiohdr_t* CMDLCache::FindMDL(CMDLCache* const cache, const MDLHandle_t handle, void* a3) { - studiodata_t* pStudioData = cache->GetStudioData(handle); - studiohdr_t* pStudioHdr; + studiodata_t* const studioData = cache->GetStudioData(handle); - if (pStudioData) + if (!studioData) { - if (pStudioData->m_MDLCache && - pStudioData->m_MDLCache != DC_INVALID_HANDLE) - { - studiohdr_t* pStudioHDR = **reinterpret_cast(pStudioData); - - if (!g_pMDLFallback->m_hErrorMDL && V_ComparePath(pStudioHDR->name, ERROR_MODEL)) - { - g_pMDLFallback->m_pErrorHDR = pStudioHDR; - g_pMDLFallback->m_hErrorMDL = handle; - } - else if (!g_pMDLFallback->m_hEmptyMDL && V_ComparePath(pStudioHDR->name, EMPTY_MODEL)) - { - g_pMDLFallback->m_pEmptyHDR = pStudioHDR; - g_pMDLFallback->m_hEmptyMDL = handle; - } - } - } - else - { - pStudioHdr = GetErrorModel(); + studiohdr_t* const pStudioHdr = GetErrorModel(); if (!IsKnownBadModel(handle)) { @@ -65,33 +44,56 @@ studiohdr_t* CMDLCache::FindMDL(CMDLCache* cache, MDLHandle_t handle, void* a3) return pStudioHdr; } - int nFlags = STUDIOHDR_FLAGS_NEEDS_DEFERRED_ADDITIVE | STUDIOHDR_FLAGS_OBSOLETE; - if ((pStudioData->m_nFlags & nFlags)) + studiomodelcache_t* modelCache = studioData->GetModelCache(); + + // Store error and empty fallback models. + if (IS_VALID_DATACACHE_HANDLE(modelCache)) { - void* pMDLCache = *reinterpret_cast(pStudioData); - if (pStudioData->m_MDLCache) + studiohdr_t* const studioHdr = studioData->GetModelCache()->GetStudioHdr(); + + if (studioHdr) + { + // Typically, you would only check for '(m_nFlags & STUDIODATA_ERROR_MODEL)', + // but for some reason this game doesn't have this flag set on that model. + if (!HasErrorModel() && ((studioData->flags & STUDIODATA_ERROR_MODEL) + || V_ComparePath(studioHdr->name, ERROR_MODEL))) + { + g_StudioMdlFallbackHandler.SetFallbackModel(studioHdr, handle); + } + } + } + + const int nFlags = STUDIOHDR_FLAGS_NEEDS_DEFERRED_ADDITIVE | STUDIOHDR_FLAGS_OBSOLETE; + + if ((studioData->flags & nFlags)) + { + if (IS_VALID_DATACACHE_HANDLE(modelCache)) { if (a3) { - FindCachedMDL(cache, pStudioData, a3); - pMDLCache = *reinterpret_cast(pStudioData); + FindCachedMDL(cache, studioData, a3); + modelCache = studioData->GetModelCache(); } - pStudioHdr = *reinterpret_cast(pMDLCache); + studiohdr_t* const pStudioHdr = modelCache->GetStudioHdr(); + if (pStudioHdr) return pStudioHdr; - return FindUncachedMDL(cache, handle, pStudioData, a3); + return FindUncachedMDL(cache, handle, studioData, a3); } - pMDLCache = pStudioData->m_pAnimData; - if (pMDLCache) + + studioanimcache_t* const animCache = studioData->GetAnimCache(); + + if (IS_VALID_DATACACHE_HANDLE(animCache)) { - pStudioHdr = *reinterpret_cast(pMDLCache); + studiohdr_t* const pStudioHdr = animCache->GetStudioHdr(); + if (pStudioHdr) return pStudioHdr; } } - return FindUncachedMDL(cache, handle, pStudioData, a3); + return FindUncachedMDL(cache, handle, studioData, a3); } //----------------------------------------------------------------------------- @@ -100,11 +102,11 @@ studiohdr_t* CMDLCache::FindMDL(CMDLCache* cache, MDLHandle_t handle, void* a3) // *pStudioData - // *a3 - //----------------------------------------------------------------------------- -void CMDLCache::FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* a3) +void CMDLCache::FindCachedMDL(CMDLCache* const cache, studiodata_t* const pStudioData, void* a3) { if (a3) { - AUTO_LOCK(pStudioData->m_Mutex); + AUTO_LOCK(pStudioData->mutex); *(_QWORD*)((int64_t)a3 + 0x880) = *(_QWORD*)&pStudioData->pad[0x24]; int64_t v6 = *(_QWORD*)&pStudioData->pad[0x24]; @@ -112,7 +114,7 @@ void CMDLCache::FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* *(_QWORD*)(v6 + 0x878) = (int64_t)a3; *(_QWORD*)&pStudioData->pad[0x24] = (int64_t)a3; *(_QWORD*)((int64_t)a3 + 0x870) = (int64_t)cache; - *(_WORD*)((int64_t)a3 + 0x888) = pStudioData->m_Handle; + *(_WORD*)((int64_t)a3 + 0x888) = pStudioData->modelHandle; } } @@ -124,124 +126,145 @@ void CMDLCache::FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* // *a4 - // Output : a pointer to the studiohdr_t object //----------------------------------------------------------------------------- -studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, studiodata_t* pStudioData, void* a4) +studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* const cache, const MDLHandle_t handle, studiodata_t* pStudioData, void* a4) { - AUTO_LOCK(pStudioData->m_Mutex); + AUTO_LOCK(pStudioData->mutex); - const char* szModelName = cache->GetModelName(handle); - size_t nFileNameLen = strlen(szModelName); + const char* modelName = cache->GetModelName(handle); + const size_t fileNameLen = strlen(modelName); - studiohdr_t* pStudioHdr; + studiohdr_t* studioHdr = nullptr; - if (nFileNameLen < 5 || - (Q_stricmp(&szModelName[nFileNameLen - 5], ".rmdl") != 0) && - (Q_stricmp(&szModelName[nFileNameLen - 5], ".rrig") != 0) && - (Q_stricmp(&szModelName[nFileNameLen - 5], ".rpak") != 0)) + if (fileNameLen < 5 || + (Q_stricmp(&modelName[fileNameLen - 5], ".rmdl") != 0) && + (Q_stricmp(&modelName[fileNameLen - 5], ".rrig") != 0) && + (Q_stricmp(&modelName[fileNameLen - 5], ".rpak") != 0)) { - pStudioHdr = GetErrorModel(); + studioHdr = GetErrorModel(); if (!IsKnownBadModel(handle)) { - if (!pStudioHdr) - Error(eDLL_T::ENGINE, EXIT_FAILURE, "Attempted to load old model \"%s\" and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL); + if (!studioHdr) + Error(eDLL_T::ENGINE, EXIT_FAILURE, "Attempted to load old model \"%s\" and \"%s\" couldn't be loaded.\n", modelName, ERROR_MODEL); else - Error(eDLL_T::ENGINE, NO_ERROR, "Attempted to load old model \"%s\"; replacing with \"%s\".\n", szModelName, ERROR_MODEL); + Error(eDLL_T::ENGINE, NO_ERROR, "Attempted to load old model \"%s\"; replacing with \"%s\".\n", modelName, ERROR_MODEL); } - return pStudioHdr; + return studioHdr; } - LOBYTE(pStudioData->m_nGuidLock) = 1; - g_pRTech->StringToGuid(szModelName); - LOBYTE(pStudioData->m_nGuidLock) = 0; + pStudioData->processing = true; + Pak_StringToGuid(modelName); + pStudioData->processing = false; - if (!pStudioData->m_MDLCache) + studiomodelcache_t* const modelCache = pStudioData->GetModelCache(); + + if (IS_VALID_DATACACHE_HANDLE(modelCache)) { - studiohdr_t** pAnimData = (studiohdr_t**)pStudioData->m_pAnimData; - if (pAnimData) - { - pStudioHdr = *pAnimData; - } - else - { - pStudioHdr = GetErrorModel(); - if (!IsKnownBadModel(handle)) - { - if (!pStudioHdr) - Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model \"%s\" not found and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL); - else - Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" not found; replacing with \"%s\".\n", szModelName, ERROR_MODEL); - } - - return pStudioHdr; - } + FindCachedMDL(cache, pStudioData, a4); + studioHdr = modelCache->GetStudioHdr(); } else { - FindCachedMDL(cache, pStudioData, a4); - DataCacheHandle_t dataHandle = pStudioData->m_MDLCache; + // Attempt to get studio header from anim cache. + studioanimcache_t* const animCache = pStudioData->GetAnimCache(); - if (dataHandle) + if (IS_VALID_DATACACHE_HANDLE(animCache)) { - if (dataHandle == DC_INVALID_HANDLE) - { - pStudioHdr = GetErrorModel(); - if (!IsKnownBadModel(handle)) - { - if (!pStudioHdr) - Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model \"%s\" has bad studio data and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL); - else - Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" has bad studio data; replacing with \"%s\".\n", szModelName, ERROR_MODEL); - } - } - else - pStudioHdr = *(studiohdr_t**)dataHandle; + studioHdr = animCache->GetStudioHdr(); } else { - pStudioHdr = GetErrorModel(); + studioHdr = GetErrorModel(); + if (!IsKnownBadModel(handle)) { - if (!pStudioHdr) - Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model \"%s\" has no studio data and \"%s\" couldn't be loaded.\n", szModelName, ERROR_MODEL); + if (!studioHdr) + Error(eDLL_T::ENGINE, EXIT_FAILURE, "Model \"%s\" not found and \"%s\" couldn't be loaded.\n", modelName, ERROR_MODEL); else - Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" has no studio data; replacing with \"%s\".\n", szModelName, ERROR_MODEL); + Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" not found; replacing with \"%s\".\n", modelName, ERROR_MODEL); } } } - return pStudioHdr; + assert(studioHdr); + return studioHdr; } //----------------------------------------------------------------------------- -// Purpose: gets the studiohdr from cache pool by handle +// Purpose: gets the model cache by handle +// Input : handle - +// Output : a pointer to the studiomodelcache_t object +//----------------------------------------------------------------------------- +studiomodelcache_t* CMDLCache::GetModelCache(const MDLHandle_t handle) +{ + if (handle == MDLHANDLE_INVALID) + return nullptr; + + const studiodata_t* const studioData = GetStudioData(handle); + + if (!studioData) + return nullptr; + + studiomodelcache_t* const modelCache = studioData->GetModelCache(); + return modelCache; +} + +//----------------------------------------------------------------------------- +// Purpose: gets the vcollide data from cache pool by handle // Input : *this - // handle - -// Output : a pointer to the studiohdr_t object +// Output : a pointer to the vcollide_t object //----------------------------------------------------------------------------- -studiohdr_t* CMDLCache::GetStudioHDR(CMDLCache* cache, MDLHandle_t handle) +vcollide_t* CMDLCache::GetVCollide(CMDLCache* const cache, const MDLHandle_t handle) { - studiohdr_t* pStudioHdr = nullptr; // rax + studiomodelcache_t* const modelCache = cache->GetModelCache(handle); - if (!handle) + if (!IS_VALID_DATACACHE_HANDLE(modelCache)) { - pStudioHdr = GetErrorModel(); - if (!pStudioHdr) - Error(eDLL_T::ENGINE, EXIT_FAILURE, "Attempted to load model with no handle and \"%s\" couldn't be loaded.\n", ERROR_MODEL); - - return pStudioHdr; + Warning(eDLL_T::ENGINE, "Attempted to load collision data on model \"%s\" with invalid studio data!\n", cache->GetModelName(handle)); + return nullptr; } - studiodata_t* pStudioData = cache->GetStudioData(handle); - DataCacheHandle_t dataCache = pStudioData->m_MDLCache; + studiophysicsref_t* const physicsCache = modelCache->GetPhysicsCache(); - if (dataCache && - dataCache != DC_INVALID_HANDLE) + if (!physicsCache) + return nullptr; + + CStudioVCollide* const pVCollide = physicsCache->GetStudioVCollide(); + + if (!pVCollide) + return nullptr; + + return pVCollide->GetVCollide(); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the physics geometry data from cache pool by handle +// Input : *this - +// handle - +// Output : a pointer to the physics geometry descriptor +//----------------------------------------------------------------------------- +void* CMDLCache::GetPhysicsGeometry(CMDLCache* const cache, const MDLHandle_t handle) +{ + studiomodelcache_t* const modelCache = cache->GetModelCache(handle); + + if (!IS_VALID_DATACACHE_HANDLE(modelCache)) { - void* v4 = *(void**)(*((_QWORD*)dataCache + 1) + 24i64); - if (v4) - pStudioHdr = (studiohdr_t*)((char*)v4 + 0x10); + Warning(eDLL_T::ENGINE, "Attempted to load physics geometry on model \"%s\" with invalid studio data!\n", cache->GetModelName(handle)); + return nullptr; } - return pStudioHdr; + + studiophysicsref_t* const physicsRef = modelCache->GetPhysicsCache(); + + if (!physicsRef) + return nullptr; + + CStudioPhysicsGeoms* const physicsGeoms = physicsRef->GetPhysicsGeoms(); + + if (!physicsGeoms) + return nullptr; + + return physicsGeoms->GetGeometryData(); } //----------------------------------------------------------------------------- @@ -250,52 +273,72 @@ studiohdr_t* CMDLCache::GetStudioHDR(CMDLCache* cache, MDLHandle_t handle) // handle - // Output : a pointer to the studiohwdata_t object //----------------------------------------------------------------------------- -studiohwdata_t* CMDLCache::GetHardwareData(CMDLCache* cache, MDLHandle_t handle) +studiohwdata_t* CMDLCache::GetHardwareData(CMDLCache* const cache, const MDLHandle_t handle) { - studiodata_t* pStudioData = cache->GetStudioData(handle); + const studiodata_t* studioData = nullptr; cache->GetStudioData(handle); + const studiomodelcache_t* modelCache = cache->GetModelCache(handle); - if (!pStudioData) + if (!IS_VALID_DATACACHE_HANDLE(modelCache)) { - if (!g_pMDLFallback->m_hErrorMDL) + if (!HasErrorModel()) { - Error(eDLL_T::ENGINE, EXIT_FAILURE, "Studio hardware with handle \"%hu\" not found and \"%s\" couldn't be loaded.\n", handle, ERROR_MODEL); + Error(eDLL_T::ENGINE, NO_ERROR, "Studio hardware for model \"%s\" not found and \"%s\" couldn't be loaded!\n", + cache->GetModelName(handle), ERROR_MODEL); + + assert(0); // Should never be hit! return nullptr; } - pStudioData = cache->GetStudioData(g_pMDLFallback->m_hErrorMDL); + else + { + // Only spew the message once. + if (g_StudioMdlFallbackHandler.AddToSuppressionList(handle)) + { + Warning(eDLL_T::ENGINE, "Studio hardware for model \"%s\" not found; replacing with \"%s\".\n", + cache->GetModelName(handle), ERROR_MODEL); + } + } + + studioData = cache->GetStudioData(GetErrorModelHandle()); + modelCache = studioData->GetModelCache(); } - - DataCacheHandle_t dataCache = pStudioData->m_MDLCache; - - if (dataCache) - { - if (dataCache == DC_INVALID_HANDLE) - return nullptr; - - void* pAnimData = (void*)*((_QWORD*)dataCache + 1); - - AcquireSRWLockExclusive(g_pMDLLock); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - v_CStudioHWDataRef__SetFlags(reinterpret_cast(pAnimData), 1i64); // !!! DECLARED INLINE IN < S3 !!! -#endif - ReleaseSRWLockExclusive(g_pMDLLock); - } - if ((pStudioData->m_nFlags & STUDIODATA_FLAGS_STUDIOMESH_LOADED)) - return &pStudioData->m_pHardwareRef->m_HardwareData; else - return nullptr; + { + studioData = cache->GetStudioData(handle); + } + + studiophysicsref_t* const physicsRef = modelCache->GetPhysicsCache(); + + AcquireSRWLockExclusive(g_pMDLLock); + CMDLCache__CheckData(physicsRef, 1i64); // !!! DECLARED INLINE IN < S3 !!! + ReleaseSRWLockExclusive(g_pMDLLock); + + if ((studioData->flags & STUDIODATA_FLAGS_STUDIOMESH_LOADED)) + return studioData->GetHardwareDataRef()->GetHardwareData(); + + return nullptr; } //----------------------------------------------------------------------------- // Purpose: gets the error model -// Output : *studiohdr_t //----------------------------------------------------------------------------- studiohdr_t* CMDLCache::GetErrorModel(void) { - // !TODO [AMOS]: mdl/error.rmdl fallback is not supported (yet) in the new GatherProps solution! - if (!old_gather_props->GetBool()) - old_gather_props->SetValue(true); + return g_StudioMdlFallbackHandler.GetFallbackModelHeader(); +} +const char* CMDLCache::GetErrorModelName(void) +{ + const studiohdr_t* const errorStudioHdr = g_StudioMdlFallbackHandler.GetFallbackModelHeader(); + assert(errorStudioHdr); - return g_pMDLFallback->m_pErrorHDR; + return errorStudioHdr ? errorStudioHdr->name : "(invalid)"; +} +MDLHandle_t CMDLCache::GetErrorModelHandle(void) +{ + return g_StudioMdlFallbackHandler.GetFallbackModelHandle(); +} +bool CMDLCache::HasErrorModel(void) +{ + return g_StudioMdlFallbackHandler.HasFallbackModel(); } //----------------------------------------------------------------------------- @@ -303,34 +346,20 @@ studiohdr_t* CMDLCache::GetErrorModel(void) // Input : handle - // Output : true if exist, false otherwise //----------------------------------------------------------------------------- -bool CMDLCache::IsKnownBadModel(MDLHandle_t handle) +bool CMDLCache::IsKnownBadModel(const MDLHandle_t handle) { - auto p = g_vBadMDLHandles.insert(handle); - return !p.second; + // Only adds if it didn't exist yet, else it returns false. + return g_StudioMdlFallbackHandler.AddBadModelHandle(handle); } -void VMDLCache::Attach() const +void VMDLCache::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_CMDLCache__FindMDL, &CMDLCache::FindMDL); -#ifdef GAMEDLL_S3 // !!! DECLARED INLINE WITH FINDMDL IN < S3 !!! - DetourAttach((LPVOID*)&v_CMDLCache__FindCachedMDL, &CMDLCache::FindCachedMDL); - DetourAttach((LPVOID*)&v_CMDLCache__FindUncachedMDL, &CMDLCache::FindUncachedMDL); -#endif // GAMEDLL_S3 -#ifdef GAMEDLL_S3 // !TODO: - DetourAttach((LPVOID*)&v_CMDLCache__GetHardwareData, &CMDLCache::GetHardwareData); - DetourAttach((LPVOID*)&v_CMDLCache__GetStudioHDR, &CMDLCache::GetStudioHDR); -#endif -} + DetourSetup(&CMDLCache__FindMDL, &CMDLCache::FindMDL, bAttach); + DetourSetup(&CMDLCache__FindCachedMDL, &CMDLCache::FindCachedMDL, bAttach); + DetourSetup(&CMDLCache__FindUncachedMDL, &CMDLCache::FindUncachedMDL, bAttach); -void VMDLCache::Detach() const -{ - DetourDetach((LPVOID*)&v_CMDLCache__FindMDL, &CMDLCache::FindMDL); -#ifdef GAMEDLL_S3 // !!! DECLARED INLINE WITH FINDMDL IN < S3 !!! - DetourDetach((LPVOID*)&v_CMDLCache__FindCachedMDL, &CMDLCache::FindCachedMDL); - DetourDetach((LPVOID*)&v_CMDLCache__FindUncachedMDL, &CMDLCache::FindUncachedMDL); -#endif // GAMEDLL_S3 -#ifdef GAMEDLL_S3 // !TODO: - DetourDetach((LPVOID*)&v_CMDLCache__GetHardwareData, &CMDLCache::GetHardwareData); - DetourDetach((LPVOID*)&v_CMDLCache__GetStudioHDR, &CMDLCache::GetStudioHDR); -#endif -} \ No newline at end of file + DetourSetup(&CMDLCache__GetVCollide, &CMDLCache::GetVCollide, bAttach); + DetourSetup(&CMDLCache__GetPhysicsGeometry, &CMDLCache::GetPhysicsGeometry, bAttach); + + DetourSetup(&CMDLCache__GetHardwareData, &CMDLCache::GetHardwareData, bAttach); +} diff --git a/r5dev/datacache/mdlcache.h b/r5dev/datacache/mdlcache.h index 4eb5f7b9..407fa68a 100644 --- a/r5dev/datacache/mdlcache.h +++ b/r5dev/datacache/mdlcache.h @@ -2,104 +2,254 @@ #define MDLCACHE_H #include "tier0/threadtools.h" #include "tier1/utldict.h" +#include "tier1/refcount.h" #include "datacache/idatacache.h" #include "datacache/imdlcache.h" #include "public/studio.h" +#include "public/vphysics/phyfile.h" +#include "vphysics/physics_collide.h" +#include "public/rtech/ipakfile.h" -struct RStaticProp_t +class CStudioFallbackHandler { - studiohdr_t* m_pStudioHDR{}; - CStudioHWDataRef* m_pHardWareData{}; - const char* m_szPropName{}; - uint8_t m_pUnknown[0x62]{}; +public: + CStudioFallbackHandler(void) + : m_pFallbackHDR(nullptr) + , m_hFallbackMDL(NULL) + {} + + // This must be cleared if 'common.rpak' is getting unloaded, as this pak + // contains the default fallback models!!! + inline void Clear(void) + { + m_pFallbackHDR = nullptr; + m_hFallbackMDL = NULL; + + m_BadMdlHandles.clear(); + } + + inline bool HasFallbackModel() const + { + return !!m_hFallbackMDL; + } + + inline void SetFallbackModel(studiohdr_t* const studioHdr, const MDLHandle_t handle) + { + m_pFallbackHDR = studioHdr; + m_hFallbackMDL = handle; + } + + inline studiohdr_t* GetFallbackModelHeader() const + { + return m_pFallbackHDR; + } + + inline MDLHandle_t GetFallbackModelHandle() const + { + return m_hFallbackMDL; + } + + inline void EnableLegacyGatherProps() + { + if (!old_gather_props->GetBool()) + old_gather_props->SetValue(true); + } + + inline void DisableLegacyGatherProps() + { + if (old_gather_props->GetBool()) + old_gather_props->SetValue(false); + } + + inline bool AddBadModelHandle(const MDLHandle_t handle) + { + auto p = m_BadMdlHandles.insert(handle); + return !p.second; + } + + inline void ClearBadModelHandleCache() + { + m_BadMdlHandles.clear(); + } + + inline bool HasInvalidModelHandles() + { + return !m_BadMdlHandles.empty(); + } + + inline bool AddToSuppressionList(const MDLHandle_t handle) + { + auto p = m_SuppressedHandles.insert(handle); + return p.second; + } + + inline void ClearSuppresionList() + { + m_SuppressedHandles.clear(); + } + +private: + studiohdr_t* m_pFallbackHDR; + MDLHandle_t m_hFallbackMDL; + + // Keep track of bad model handles so we don't log the + // same one twice or more to the console and cause a + // significant performance impact. + std::unordered_set m_BadMdlHandles; + + // Don't spam on these handles when trying to get + // cache data. + std::unordered_set m_SuppressedHandles; }; -struct RMDLFallBack_t + +struct CStudioVCollide : public CRefCounted<> { - studiohdr_t* m_pErrorHDR; - studiohdr_t* m_pEmptyHDR; - MDLHandle_t m_hErrorMDL; - MDLHandle_t m_hEmptyMDL; - - RMDLFallBack_t(void) - : m_pErrorHDR(nullptr) - , m_pEmptyHDR(nullptr) - , m_hErrorMDL(NULL) - , m_hEmptyMDL(NULL) +public: + ~CStudioVCollide() { + PhysicsCollision()->VCollideUnload(&m_vcollide); } - - // This must be cleared if 'common.rpak' is getting unloaded! - void Clear(void) + vcollide_t* GetVCollide() { - m_pErrorHDR = nullptr; - m_pEmptyHDR = nullptr; - m_hErrorMDL = NULL; - m_hEmptyMDL = NULL; + return &m_vcollide; } +private: + vcollide_t m_vcollide; +}; + +class CStudioPhysicsGeoms : public CRefCounted<> +{ +public: + void* GetGeometryData() { return m_pGeomDataDesc; } + +private: + // TODO: ptr to another ptr to geometry data; requires reversing. + void* m_pGeomDataDesc; + int unk2; + short unk3; + short unk4; +}; + +struct studiophysicsref_t +{ + inline CStudioVCollide* GetStudioVCollide() const { return vCollide; } + inline CStudioPhysicsGeoms* GetPhysicsGeoms() const { return physicsGeoms; } + + int unk0; + int unk1; + int unk2; + int unk3; + int unk4; + int unk5; + CStudioVCollide* vCollide; + CStudioPhysicsGeoms* physicsGeoms; +}; + +struct studiomodelcache_t +{ + inline studiohdr_t* GetStudioHdr() const { return studioHeader; } + inline studiophysicsref_t* GetPhysicsCache() const { return physicsCache; } + + studiohdr_t* studioHeader; + studiophysicsref_t* physicsCache; + const char* modelName; + char gap_18[8]; + phyheader_t* physicsHeader; + void* unk_28; + void* staticPropData; + void* animRigs; + int numAnimRigs; + int unk_44; + int streamedDataSize; + char gap_4C[8]; + int numAnimSeqs; + void* m_pAnimSeqs; + char gap_60[24]; +}; + +struct studioanimcache_t +{ + inline studiohdr_t* GetStudioHdr() const { return studioHdr; } + + studiohdr_t* studioHdr; + const char* rigName; + int unk0; + int numSequences; + PakPage_t sequences; + int unk1; + int unk2; }; // only models with type "mod_studio" have this data struct studiodata_t { - DataCacheHandle_t m_MDLCache; - void* m_pAnimData; // !TODO: reverse struct. - unsigned short m_nRefCount; - unsigned short m_nFlags; - MDLHandle_t m_Handle; -#ifndef GAMEDLL_S3 - void* Unk1; // TODO: unverified! - void* Unk2; // TODO: unverified! -#endif // !GAMEDLL_S3 - void* Unk3; // ptr to flags and model string. - CStudioHWDataRef* m_pHardwareRef; - void* m_pMaterialTable; // contains a large table of CMaterialGlue objects. + inline studiomodelcache_t* GetModelCache() const { return modelCache; } + inline studioanimcache_t* GetAnimCache() const { return animCache; } + inline CStudioHWDataRef* GetHardwareDataRef() const { return hardwareRef; } + + studiomodelcache_t* modelCache; + studioanimcache_t* animCache; + unsigned short refCount; + unsigned short flags; + MDLHandle_t modelHandle; + void* unkStruct; // TODO: reverse structure + CStudioHWDataRef* hardwareRef; + void* materialTable; // contains a large table of CMaterialGlue objects. int Unk5; char pad[72]; - CThreadFastMutex m_Mutex; - int m_nGuidLock; // always -1, set to 1 and 0 in CMDLCache::FindUncachedMDL. + CThreadFastMutex mutex; + bool processing; + PakHandle_t pakHandle; }; -extern RMDLFallBack_t* g_pMDLFallback; -extern std::unordered_set g_vBadMDLHandles; +extern CStudioFallbackHandler g_StudioMdlFallbackHandler; -class CMDLCache : public IMDLCache +class CMDLCache : public CTier1AppSystem { public: - static studiohdr_t* FindMDL(CMDLCache* cache, MDLHandle_t handle, void* a3); - static void FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* a3); - static studiohdr_t* FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, studiodata_t* pStudioData, void* a4); - static studiohdr_t* GetStudioHDR(CMDLCache* cache, MDLHandle_t handle); - static studiohwdata_t* GetHardwareData(CMDLCache* cache, MDLHandle_t handle); + static studiohdr_t* FindMDL(CMDLCache* const cache, const MDLHandle_t handle, void* a3); + static void FindCachedMDL(CMDLCache* const cache, studiodata_t* const pStudioData, void* a3); + static studiohdr_t* FindUncachedMDL(CMDLCache* const cache, const MDLHandle_t handle, studiodata_t* const pStudioData, void* a4); + + studiomodelcache_t* GetModelCache(const MDLHandle_t handle); + static vcollide_t* GetVCollide(CMDLCache* const cache, const MDLHandle_t handle); + static void* GetPhysicsGeometry(CMDLCache* const cache, const MDLHandle_t handle); + + static studiohwdata_t* GetHardwareData(CMDLCache* const cache, const MDLHandle_t handle); + static studiohdr_t* GetErrorModel(void); - static bool IsKnownBadModel(MDLHandle_t handle); + static const char* GetErrorModelName(void); + static MDLHandle_t GetErrorModelHandle(void); + static bool HasErrorModel(void); + static bool IsKnownBadModel(const MDLHandle_t handle); - studiodata_t* GetStudioData(MDLHandle_t handle) + inline studiodata_t* GetStudioData(const MDLHandle_t handle) { EnterCriticalSection(&m_MDLMutex); - studiodata_t* pStudioData = m_MDLDict.Element(handle); + studiodata_t* const studioData = m_MDLDict.Element(handle); LeaveCriticalSection(&m_MDLMutex); - return pStudioData; + return studioData; } - const char* GetModelName(MDLHandle_t handle) + inline const char* GetModelName(const MDLHandle_t handle) { EnterCriticalSection(&m_MDLMutex); - const char* szModelName = m_MDLDict.GetElementName(handle); + const char* const modelName = m_MDLDict.GetElementName(handle); LeaveCriticalSection(&m_MDLMutex); - return szModelName; + return modelName; } - void* GetMaterialTable(MDLHandle_t handle) + inline void* GetMaterialTable(const MDLHandle_t handle) { EnterCriticalSection(&m_MDLMutex); - studiodata_t* pStudioData = m_MDLDict.Element(handle); + studiodata_t* const studioData = m_MDLDict.Element(handle); LeaveCriticalSection(&m_MDLMutex); - return &pStudioData->m_pMaterialTable; + return &studioData->materialTable; } private: @@ -108,24 +258,16 @@ private: // !TODO: reverse the rest }; -inline CMemory p_CMDLCache__FindMDL; -inline studiohdr_t*(*v_CMDLCache__FindMDL)(CMDLCache* pCache, void* a2, void* a3); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) -inline CMemory p_CMDLCache__FindCachedMDL; -inline void(*v_CMDLCache__FindCachedMDL)(CMDLCache* pCache, void* a2, void* a3); +inline studiohdr_t*(*CMDLCache__FindMDL)(CMDLCache* const pCache, const MDLHandle_t handle, void* a3); +inline void(*CMDLCache__FindCachedMDL)(CMDLCache* const pCache, studiodata_t* const pStudioData, void* a3); +inline studiohdr_t*(*CMDLCache__FindUncachedMDL)(CMDLCache* const pCache, MDLHandle_t handle, studiodata_t* const pStudioData, void* a4); -inline CMemory p_CMDLCache__FindUncachedMDL; -inline studiohdr_t*(*v_CMDLCache__FindUncachedMDL)(CMDLCache* pCache, MDLHandle_t handle, void* a3, void* a4); -#endif -inline CMemory p_CMDLCache__GetStudioHDR; -inline studiohdr_t*(*v_CMDLCache__GetStudioHDR)(CMDLCache* pCache, MDLHandle_t handle); +inline vcollide_t*(*CMDLCache__GetVCollide)(CMDLCache* const pCache, const MDLHandle_t handle); +inline void* (*CMDLCache__GetPhysicsGeometry)(CMDLCache* const pCache, const MDLHandle_t handle); + +inline studiohwdata_t* (*CMDLCache__GetHardwareData)(CMDLCache* const pCache, const MDLHandle_t handle); +inline bool(*CMDLCache__CheckData)(void* const ref, const int64_t type); // Probably incorrect name. -inline CMemory p_CMDLCache__GetHardwareData; -inline studiohwdata_t*(*v_CMDLCache__GetHardwareData)(CMDLCache* pCache, MDLHandle_t handle); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) -inline CMemory p_CStudioHWDataRef__SetFlags; // Probably incorrect. -inline bool(*v_CStudioHWDataRef__SetFlags)(CStudioHWDataRef* ref, int64_t flags); -#endif inline CMDLCache* g_pMDLCache = nullptr; inline PSRWLOCK g_pMDLLock = nullptr; // Possibly a member? research required. @@ -134,54 +276,26 @@ class VMDLCache : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CMDLCache::FindMDL", p_CMDLCache__FindMDL.GetPtr()); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - LogFunAdr("CMDLCache::FindCachedMDL", p_CMDLCache__FindCachedMDL.GetPtr()); - LogFunAdr("CMDLCache::FindUncachedMDL", p_CMDLCache__FindUncachedMDL.GetPtr()); -#endif - LogFunAdr("CMDLCache::GetStudioHDR", p_CMDLCache__GetStudioHDR.GetPtr()); - LogFunAdr("CMDLCache::GetHardwareData", p_CMDLCache__GetHardwareData.GetPtr()); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - LogFunAdr("CStudioHWDataRef::SetFlags", p_CStudioHWDataRef__SetFlags.GetPtr()); -#endif - LogVarAdr("g_MDLCache", reinterpret_cast(g_pMDLCache)); - LogVarAdr("g_MDLLock", reinterpret_cast(g_pMDLLock)); + LogFunAdr("CMDLCache::FindMDL", CMDLCache__FindMDL); + LogFunAdr("CMDLCache::FindCachedMDL", CMDLCache__FindCachedMDL); + LogFunAdr("CMDLCache::FindUncachedMDL", CMDLCache__FindUncachedMDL); + LogFunAdr("CMDLCache::GetVCollide", CMDLCache__GetVCollide); + LogFunAdr("CMDLCache::GetPhysicsGeometry", CMDLCache__GetPhysicsGeometry); + LogFunAdr("CMDLCache::GetHardwareData", CMDLCache__GetHardwareData); + LogFunAdr("CMDLCache::CheckData", CMDLCache__CheckData); + + LogVarAdr("g_MDLCache", g_pMDLCache); + LogVarAdr("g_MDLLock", g_pMDLLock); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - p_CMDLCache__FindMDL = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 4C 8B F1 0F B7 DA"); - v_CMDLCache__FindMDL = p_CMDLCache__FindMDL.RCast(); /*48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC 20 4C 8B F1 0F B7 DA*/ - - p_CMDLCache__GetStudioHDR = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F1 0F B7 FA 48 8D 0D ?? ?? ?? ??"); - v_CMDLCache__GetStudioHDR = p_CMDLCache__GetStudioHDR.RCast(); /*48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B F1 0F B7 FA 48 8D 0D ? ? ? ?*/ - -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CMDLCache__GetHardwareData = g_GameDll.FindPatternSIMD("40 56 48 83 EC 20 48 89 5C 24 ?? 48 8D 0D ?? ?? ?? ??"); - v_CMDLCache__GetHardwareData = p_CMDLCache__GetHardwareData.RCast(); /*40 56 48 83 EC 20 48 89 5C 24 ? 48 8D 0D ? ? ? ?*/ -#else - p_CMDLCache__GetHardwareData = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 0F B7 DA FF 15 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 8D 14 5B 48 8D 0D ?? ?? ?? ?? 48 8B 7C D0 ?? FF 15 ?? ?? ?? ?? 48 8B 1F"); - v_CMDLCache__GetHardwareData = p_CMDLCache__GetHardwareData.RCast(); /*48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 0F B7 DA FF 15 ? ? ? ? 48 8B 05 ? ? ? ? 48 8D 14 5B 48 8D 0D ? ? ? ? 48 8B 7C D0 ? FF 15 ? ? ? ? 48 8B 1F*/ -#endif -#else - p_CMDLCache__FindMDL = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F1 0F B7 EA"); - v_CMDLCache__FindMDL = p_CMDLCache__FindMDL.RCast(); /*48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B F1 0F B7 EA*/ - - p_CMDLCache__FindCachedMDL = g_GameDll.FindPatternSIMD("4D 85 C0 74 7A 48 89 6C 24 ??"); - v_CMDLCache__FindCachedMDL = p_CMDLCache__FindCachedMDL.RCast(); /*4D 85 C0 74 7A 48 89 6C 24 ?*/ - - p_CMDLCache__FindUncachedMDL = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 48 8B E9 0F B7 FA"); - v_CMDLCache__FindUncachedMDL = p_CMDLCache__FindUncachedMDL.RCast(); /*48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC 20 48 8B E9 0F B7 FA*/ - - p_CMDLCache__GetStudioHDR = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 0F B7 DA FF 15 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 8D 14 5B 48 8D 0D ?? ?? ?? ?? 48 8B 5C D0 ?? FF 15 ?? ?? ?? ?? 48 8B 03 48 8B 48 08"); - v_CMDLCache__GetStudioHDR = p_CMDLCache__GetStudioHDR.RCast(); /*40 53 48 83 EC 20 48 8D 0D ? ? ? ? 0F B7 DA FF 15 ? ? ? ? 48 8B 05 ? ? ? ? 48 8D 14 5B 48 8D 0D ? ? ? ? 48 8B 5C D0 ? FF 15 ? ? ? ? 48 8B 03 48 8B 48 08*/ - - p_CMDLCache__GetHardwareData = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 0F B7 DA FF 15 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 8D 14 5B 48 8D 0D ?? ?? ?? ?? 48 8B 7C D0 ?? FF 15 ?? ?? ?? ?? 48 8B 1F"); - v_CMDLCache__GetHardwareData = p_CMDLCache__GetHardwareData.RCast(); /*48 89 5C 24 ? 57 48 83 EC 20 48 8D 0D ? ? ? ? 0F B7 DA FF 15 ? ? ? ? 48 8B 05 ? ? ? ? 48 8D 14 5B 48 8D 0D ? ? ? ? 48 8B 7C D0 ? FF 15 ? ? ? ? 48 8B 1F*/ - - p_CStudioHWDataRef__SetFlags = g_GameDll.FindPatternSIMD("48 83 EC 08 4C 8D 14 12"); - v_CStudioHWDataRef__SetFlags = p_CStudioHWDataRef__SetFlags.RCast(); /*48 83 EC 08 4C 8D 14 12*/ -#endif + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F1 0F B7 EA").GetPtr(CMDLCache__FindMDL); + g_GameDll.FindPatternSIMD("4D 85 C0 74 7A 48 89 6C 24 ??").GetPtr(CMDLCache__FindCachedMDL); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 48 8B E9 0F B7 FA").GetPtr(CMDLCache__FindUncachedMDL); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 0F B7 DA FF 15 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 8D 14 5B 48 8D 0D ?? ?? ?? ?? 48 8B 7C D0 ?? FF 15 ?? ?? ?? ?? 48 8B 1F").GetPtr(CMDLCache__GetHardwareData); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8D 0D ?? ?? ?? ?? 0F B7 DA FF 15 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 8D 14 5B 48 8D 0D ?? ?? ?? ?? 48 8B 5C D0 ?? FF 15 ?? ?? ?? ?? 48 8B 03 48 8B 48 08").GetPtr(CMDLCache__GetVCollide); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 B8 ?? ?? ?? ?? 0F B7 DA").GetPtr(CMDLCache__GetPhysicsGeometry); + g_GameDll.FindPatternSIMD("48 83 EC 08 4C 8D 14 12").GetPtr(CMDLCache__CheckData); } virtual void GetVar(void) const { @@ -189,11 +303,10 @@ class VMDLCache : public IDetour g_pMDLCache = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 0F 45 C8 FF 05 ?? ?? ?? ?? 48 83 3D ?? ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ??") .FindPatternSelf("48 8D 05").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pMDLLock = p_CMDLCache__GetHardwareData.Offset(0x35).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pMDLLock = CMemory(CMDLCache__GetHardwareData).Offset(0x35).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/ebisusdk/EbisuSDK.h b/r5dev/ebisusdk/EbisuSDK.h index e1f91265..894f8bac 100644 --- a/r5dev/ebisusdk/EbisuSDK.h +++ b/r5dev/ebisusdk/EbisuSDK.h @@ -1,12 +1,7 @@ #pragma once -inline CMemory p_EbisuSDK_Tier0_Init; inline void(*EbisuSDK_Tier0_Init)(void); - -inline CMemory p_EbisuSDK_CVar_Init; inline void(*EbisuSDK_CVar_Init)(void); - -inline CMemory p_EbisuSDK_SetState; inline void(*EbisuSDK_SetState)(void); inline uint64_t* g_NucleusID = nullptr; @@ -26,42 +21,32 @@ class VEbisuSDK : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("EbisuSDK_Tier0_Init", p_EbisuSDK_Tier0_Init.GetPtr()); - LogFunAdr("EbisuSDK_CVar_Init", p_EbisuSDK_CVar_Init.GetPtr()); - LogFunAdr("EbisuSDK_SetState", p_EbisuSDK_SetState.GetPtr()); - LogVarAdr("g_NucleusID", reinterpret_cast(g_NucleusID)); - LogVarAdr("g_NucleusToken", reinterpret_cast(g_NucleusToken)); - LogVarAdr("g_OriginAuthCode", reinterpret_cast(g_OriginAuthCode)); - LogVarAdr("g_OriginErrorLevel", reinterpret_cast(g_OriginErrorLevel)); - LogVarAdr("g_EbisuProfileInit", reinterpret_cast(g_EbisuProfileInit)); - LogVarAdr("g_EbisuSDKInit", reinterpret_cast(g_EbisuSDKInit)); + LogFunAdr("EbisuSDK_Tier0_Init", EbisuSDK_Tier0_Init); + LogFunAdr("EbisuSDK_CVar_Init", EbisuSDK_CVar_Init); + LogFunAdr("EbisuSDK_SetState", EbisuSDK_SetState); + LogVarAdr("g_NucleusID", g_NucleusID); + LogVarAdr("g_NucleusToken", g_NucleusToken); + LogVarAdr("g_OriginAuthCode", g_OriginAuthCode); + LogVarAdr("g_OriginErrorLevel", g_OriginErrorLevel); + LogVarAdr("g_EbisuProfileInit", g_EbisuProfileInit); + LogVarAdr("g_EbisuSDKInit", g_EbisuSDKInit); } virtual void GetFun(void) const { - p_EbisuSDK_Tier0_Init = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 85 ?? 02 ?? ?? 48 89 5C 24 20"); - EbisuSDK_Tier0_Init = p_EbisuSDK_Tier0_Init.RCast(); /*48 83 EC 28 80 3D ?? ?? ?? ?? 00 0F 85 ?? 02 00 00 48 89 5C 24 20*/ - - p_EbisuSDK_CVar_Init = g_GameDll.FindPatternSIMD("40 57 48 83 EC 40 83 3D"); - EbisuSDK_CVar_Init = p_EbisuSDK_CVar_Init.RCast(); /*40 57 48 83 EC 40 83 3D*/ - - p_EbisuSDK_SetState = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 5B"); - EbisuSDK_SetState = p_EbisuSDK_SetState.RCast(); /*48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 5B*/ + g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 85 ?? 02 ?? ?? 48 89 5C 24 20").GetPtr(EbisuSDK_Tier0_Init); + g_GameDll.FindPatternSIMD("40 57 48 83 EC 40 83 3D").GetPtr(EbisuSDK_CVar_Init); + g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 5B").GetPtr(EbisuSDK_SetState); } virtual void GetVar(void) const { - g_NucleusID = p_EbisuSDK_CVar_Init.Offset(0x20).FindPatternSelf("4C 89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_NucleusToken = p_EbisuSDK_SetState.Offset(0x1EF).FindPatternSelf("38 1D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); // !TODO: TEST! -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - g_NucleusToken = p_EbisuSDK_SetState.Offset(0x1EF).FindPatternSelf("80 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); -#endif - g_OriginAuthCode = p_EbisuSDK_SetState.Offset(0x1BF).FindPatternSelf("0F B6", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_OriginErrorLevel = p_EbisuSDK_SetState.Offset(0x20).FindPatternSelf("89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - g_EbisuProfileInit = p_EbisuSDK_CVar_Init.Offset(0x12A).FindPatternSelf("C6 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); - g_EbisuSDKInit = p_EbisuSDK_Tier0_Init.Offset(0x0).FindPatternSelf("80 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + g_NucleusID = CMemory(EbisuSDK_CVar_Init).Offset(0x20).FindPatternSelf("4C 89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_NucleusToken = CMemory(EbisuSDK_SetState).Offset(0x1EF).FindPatternSelf("80 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + g_OriginAuthCode = CMemory(EbisuSDK_SetState).Offset(0x1BF).FindPatternSelf("0F B6", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_OriginErrorLevel = CMemory(EbisuSDK_SetState).Offset(0x20).FindPatternSelf("89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); + g_EbisuProfileInit = CMemory(EbisuSDK_CVar_Init).Offset(0x12A).FindPatternSelf("C6 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + g_EbisuSDKInit = CMemory(EbisuSDK_Tier0_Init).Offset(0x0).FindPatternSelf("80 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/CMakeLists.txt b/r5dev/engine/CMakeLists.txt index 3fc1bd82..55f800b8 100644 --- a/r5dev/engine/CMakeLists.txt +++ b/r5dev/engine/CMakeLists.txt @@ -26,7 +26,16 @@ add_sources( SOURCE_GROUP "Debug" "debugoverlay.h" ) +add_sources( SOURCE_GROUP "Input" + "keys.cpp" + "keys.h" +) + add_sources( SOURCE_GROUP "Render" + "framelimit.cpp" + "framelimit.h" + "gl_drawlights.cpp" + "gl_drawlights.h" "gl_matsysiface.h" "gl_model_private.h" "gl_rmain.cpp" @@ -127,6 +136,8 @@ add_sources( SOURCE_GROUP "Shared" "shared/base_rcon.h" "shared/shared_rcon.cpp" "shared/shared_rcon.h" + "shared/datablock.cpp" + "shared/datablock.h" ) if( NOT ${PROJECT_NAME} STREQUAL "engine_ds" ) @@ -150,6 +161,11 @@ add_sources( SOURCE_GROUP "GameUI" "${ENGINE_SOURCE_DIR}/gameui/IBrowser.h" "${ENGINE_SOURCE_DIR}/gameui/IConsole.cpp" "${ENGINE_SOURCE_DIR}/gameui/IConsole.h" + + "${ENGINE_SOURCE_DIR}/gameui/imgui_system.cpp" + "${ENGINE_SOURCE_DIR}/gameui/imgui_system.h" + "${ENGINE_SOURCE_DIR}/gameui/imgui_surface.cpp" + "${ENGINE_SOURCE_DIR}/gameui/imgui_surface.h" ) endif() @@ -185,6 +201,7 @@ add_sources( SOURCE_GROUP "Common" "${ENGINE_SOURCE_DIR}/common/qlimits.h" "${ENGINE_SOURCE_DIR}/common/sdkdefs.h" "${ENGINE_SOURCE_DIR}/common/x86defs.h" + "${ENGINE_SOURCE_DIR}/common/xbox/xboxstubs.h" ) file( GLOB ENGINE_PUBLIC_HEADERS @@ -197,10 +214,8 @@ add_sources( SOURCE_GROUP "Public" "${ENGINE_SOURCE_DIR}/public/dt_send.h" "${ENGINE_SOURCE_DIR}/public/dt_recv.h" "${ENGINE_SOURCE_DIR}/public/datamap.h" - "${ENGINE_SOURCE_DIR}/public/idatablock.h" "${ENGINE_SOURCE_DIR}/public/idebugoverlay.h" "${ENGINE_SOURCE_DIR}/public/iengine.h" - "${ENGINE_SOURCE_DIR}/public/igame.h" "${ENGINE_SOURCE_DIR}/public/iserver.h" "${ENGINE_SOURCE_DIR}/public/isnapshotmgr.h" "${ENGINE_SOURCE_DIR}/public/inetchannel.h" @@ -220,7 +235,6 @@ if( NOT ${PROJECT_NAME} STREQUAL "engine_ds" ) add_sources( SOURCE_GROUP "Public" "${ENGINE_SOURCE_DIR}/public/client_class.h" "${ENGINE_SOURCE_DIR}/public/ivrenderview.h" - "${ENGINE_SOURCE_DIR}/public/isurfacesystem.h" # ImGui surface ) endif() @@ -236,6 +250,11 @@ target_compile_definitions( ${PROJECT_NAME} PRIVATE ) endif() +target_include_directories( ${PROJECT_NAME} PRIVATE + "${THIRDPARTY_SOURCE_DIR}/recast/" + "${THIRDPARTY_SOURCE_DIR}/mbedtls/include" +) + endmacro() add_engine_project( "engine" ) diff --git a/r5dev/engine/client/cdll_engine_int.cpp b/r5dev/engine/client/cdll_engine_int.cpp index 5c771b2f..14c6c5b9 100644 --- a/r5dev/engine/client/cdll_engine_int.cpp +++ b/r5dev/engine/client/cdll_engine_int.cpp @@ -10,18 +10,78 @@ #include "engine/net_chan.h" #include "engine/client/cl_rcon.h" #include "networksystem/bansystem.h" -#include "vpc/keyvalues.h" +#include "tier1/keyvalues.h" +#include "windows/id3dx.h" +#include "geforce/reflex.h" #include "vengineclient_impl.h" #include "cdll_engine_int.h" +#ifndef DEDICATED +#include "materialsystem/cmaterialsystem.h" +#endif // !DEDICATED /*****************************************************************************/ #ifndef DEDICATED +//----------------------------------------------------------------------------- +// Purpose: pre frame stage notify hook +//----------------------------------------------------------------------------- +void FrameStageNotify_Pre(const ClientFrameStage_t frameStage) +{ + switch (frameStage) + { + case ClientFrameStage_t::FRAME_START: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_START: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_POSTDATAUPDATE_START: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_POSTDATAUPDATE_END: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_END: + break; + case ClientFrameStage_t::FRAME_RENDER_START: + break; + case ClientFrameStage_t::FRAME_RENDER_END: + break; + case ClientFrameStage_t::FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE: + break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: post frame stage notify hook +//----------------------------------------------------------------------------- +void FrameStageNotify_Post(const ClientFrameStage_t frameStage) +{ + switch (frameStage) + { + case ClientFrameStage_t::FRAME_START: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_START: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_POSTDATAUPDATE_START: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_POSTDATAUPDATE_END: + break; + case ClientFrameStage_t::FRAME_NET_UPDATE_END: + break; + case ClientFrameStage_t::FRAME_RENDER_START: + break; + case ClientFrameStage_t::FRAME_RENDER_END: + GFX_SetLatencyMarker(D3D11Device(), SIMULATION_END, MaterialSystem()->GetCurrentFrameCount()); + break; + case ClientFrameStage_t::FRAME_NET_FULL_FRAME_UPDATE_ON_REMOVE: + break; + } +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CHLClient::FrameStageNotify(CHLClient* pHLClient, ClientFrameStage_t frameStage) { - CHLClient_FrameStageNotify(pHLClient, frameStage); + FrameStageNotify_Pre(frameStage); + CHLClient__FrameStageNotify(pHLClient, frameStage); + FrameStageNotify_Post(frameStage); } //----------------------------------------------------------------------------- @@ -31,21 +91,14 @@ void CHLClient::FrameStageNotify(CHLClient* pHLClient, ClientFrameStage_t frameS //----------------------------------------------------------------------------- ClientClass* CHLClient::GetAllClasses() { - return CHLClient_GetAllClasses(); + return CHLClient__GetAllClasses(); } #endif // !DEDICATED /////////////////////////////////////////////////////////////////////////////// -void VDll_Engine_Int::Attach() const +void VDll_Engine_Int::Detour(const bool bAttach) const { #ifndef DEDICATED - DetourAttach((LPVOID*)&CHLClient_FrameStageNotify, &CHLClient::FrameStageNotify); -#endif // !DEDICATED -} - -void VDll_Engine_Int::Detach() const -{ -#ifndef DEDICATED - DetourDetach((LPVOID*)&CHLClient_FrameStageNotify, &CHLClient::FrameStageNotify); + DetourSetup(&CHLClient__FrameStageNotify, &CHLClient::FrameStageNotify, bAttach); #endif // !DEDICATED } diff --git a/r5dev/engine/client/cdll_engine_int.h b/r5dev/engine/client/cdll_engine_int.h index 18455204..7952fb9c 100644 --- a/r5dev/engine/client/cdll_engine_int.h +++ b/r5dev/engine/client/cdll_engine_int.h @@ -10,7 +10,7 @@ enum class ClientFrameStage_t : int FRAME_UNDEFINED = -1, // (haven't run any frames yet) FRAME_START, - // A network packet is being recieved + // A network packet is being received FRAME_NET_UPDATE_START, // Data has been received and we're going to start calling PostDataUpdate FRAME_NET_UPDATE_POSTDATAUPDATE_START, @@ -51,20 +51,11 @@ public: /* ==== CHLCLIENT ======================================================================================================================================================= */ #ifndef DEDICATED -inline CMemory p_CHLClient_PostInit; -inline void*(*CHLClient_PostInit)(void); - -inline CMemory p_CHLClient_LevelShutdown; -inline void*(*CHLClient_LevelShutdown)(CHLClient* thisptr); - -inline CMemory p_CHLClient_HudProcessInput; -inline void(*CHLClient_HudProcessInput)(CHLClient* thisptr, bool bActive); - -inline CMemory p_CHLClient_FrameStageNotify; -inline void(*CHLClient_FrameStageNotify)(CHLClient* thisptr, ClientFrameStage_t frameStage); - -inline CMemory p_CHLClient_GetAllClasses; -inline ClientClass*(*CHLClient_GetAllClasses)(); +inline void*(*CHLClient__PostInit)(void); +inline void*(*CHLClient__LevelShutdown)(CHLClient* thisptr); +inline void(*CHLClient__HudProcessInput)(CHLClient* thisptr, bool bActive); +inline void(*CHLClient__FrameStageNotify)(CHLClient* thisptr, ClientFrameStage_t frameStage); +inline ClientClass*(*CHLClient__GetAllClasses)(); #endif // !DEDICATED inline CHLClient* g_pHLClient = nullptr; @@ -76,39 +67,25 @@ class VDll_Engine_Int : public IDetour virtual void GetAdr(void) const { #ifndef DEDICATED - LogFunAdr("CHLClient::PostInit", p_CHLClient_PostInit.GetPtr()); - LogFunAdr("CHLClient::LevelShutdown", p_CHLClient_LevelShutdown.GetPtr()); - LogFunAdr("CHLClient::HudProcessInput", p_CHLClient_HudProcessInput.GetPtr()); - LogFunAdr("CHLClient::FrameStageNotify", p_CHLClient_FrameStageNotify.GetPtr()); - LogFunAdr("CHLClient::GetAllClasses", p_CHLClient_GetAllClasses.GetPtr()); + LogFunAdr("CHLClient::PostInit", CHLClient__PostInit); + LogFunAdr("CHLClient::LevelShutdown", CHLClient__LevelShutdown); + LogFunAdr("CHLClient::HudProcessInput", CHLClient__HudProcessInput); + LogFunAdr("CHLClient::FrameStageNotify", CHLClient__FrameStageNotify); + LogFunAdr("CHLClient::GetAllClasses", CHLClient__GetAllClasses); #endif // !DEDICATED - LogVarAdr("g_HLClient", reinterpret_cast(g_pHLClient)); - LogVarAdr("g_pHLClient", reinterpret_cast(g_ppHLClient)); + LogVarAdr("g_HLClient", g_pHLClient); + LogVarAdr("g_pHLClient", g_ppHLClient); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CHLClient_LevelShutdown = g_GameDll.FindPatternSIMD("40 53 56 41 54 41 56 48 83 EC 28 48 8B F1"); #ifndef DEDICATED - p_CHLClient_PostInit = g_GameDll.FindPatternSIMD("48 83 3D ?? ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ??"); - p_CHLClient_FrameStageNotify = g_GameDll.FindPatternSIMD("48 83 EC 38 89 15 ?? ?? ?? ??"); - p_CHLClient_GetAllClasses = g_GameDll.FindPatternSIMD("48 8B 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 89 74 24 ??"); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 48 8D 0D ?? ?? ?? ??").GetPtr(CHLClient__LevelShutdown); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 83 3D ?? ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ??").GetPtr(CHLClient__PostInit); + g_GameDll.FindPatternSIMD("48 83 EC 28 89 15 ?? ?? ?? ??").GetPtr(CHLClient__FrameStageNotify); + g_GameDll.FindPatternSIMD("48 8B 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ??").GetPtr(CHLClient__GetAllClasses); #endif // !DEDICATED -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) #ifndef DEDICATED - p_CHLClient_LevelShutdown = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 48 8D 0D ?? ?? ?? ??"); - p_CHLClient_PostInit = g_GameDll.FindPatternSIMD("48 83 EC 28 48 83 3D ?? ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ??"); - p_CHLClient_FrameStageNotify = g_GameDll.FindPatternSIMD("48 83 EC 28 89 15 ?? ?? ?? ??"); - p_CHLClient_GetAllClasses = g_GameDll.FindPatternSIMD("48 8B 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ??"); -#endif // !DEDICATED -#endif -#ifndef DEDICATED - p_CHLClient_HudProcessInput = g_GameDll.FindPatternSIMD("48 83 EC 28 0F B6 0D ?? ?? ?? ?? 88 15 ?? ?? ?? ??"); - CHLClient_LevelShutdown = p_CHLClient_LevelShutdown.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 48 8D 0D ?? ?? ?? ??*/ - CHLClient_PostInit = p_CHLClient_PostInit.RCast(); /*48 83 EC 28 48 83 3D ?? ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ??*/ - CHLClient_FrameStageNotify = p_CHLClient_FrameStageNotify.RCast(); /*48 83 EC 28 89 15 ?? ?? ?? ??*/ - CHLClient_HudProcessInput = p_CHLClient_HudProcessInput.RCast(); /*48 83 EC 28 0F B6 0D ?? ?? ?? ?? 88 15 ?? ?? ?? ??*/ - CHLClient_GetAllClasses = p_CHLClient_GetAllClasses.RCast(); /*48 8B 05 ? ? ? ? C3 CC CC CC CC CC CC CC CC 48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ?*/ + g_GameDll.FindPatternSIMD("48 83 EC 28 0F B6 0D ?? ?? ?? ?? 88 15 ?? ?? ?? ??").GetPtr(CHLClient__HudProcessInput); #endif // !DEDICATED } virtual void GetVar(void) const @@ -120,7 +97,6 @@ class VDll_Engine_Int : public IDetour .FindPatternSelf("4C 8B", CMemory::Direction::DOWN, 512, 2).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/client/cl_ents_parse.h b/r5dev/engine/client/cl_ents_parse.h index ca95f693..1dea4fad 100644 --- a/r5dev/engine/client/cl_ents_parse.h +++ b/r5dev/engine/client/cl_ents_parse.h @@ -1,7 +1,6 @@ #ifndef CL_ENTS_PARSE_H #define CL_ENTS_PARSE_H -inline CMemory p_CL_CopyExistingEntity; inline bool(*v_CL_CopyExistingEntity)(__int64 a1, unsigned int* a2, char* a3); bool CL_CopyExistingEntity(__int64 a1, unsigned int* a2, char* a3); @@ -10,22 +9,17 @@ class V_CL_Ents_Parse : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CL_CopyExistingEntity", p_CL_CopyExistingEntity.GetPtr()); + LogFunAdr("CL_CopyExistingEntity", v_CL_CopyExistingEntity); } virtual void GetFun(void) const { - p_CL_CopyExistingEntity = g_GameDll.FindPatternSIMD("40 53 48 83 EC 70 4C 63 51 28"); - v_CL_CopyExistingEntity = p_CL_CopyExistingEntity.RCast(); /*40 53 48 83 EC 70 4C 63 51 28*/ + g_GameDll.FindPatternSIMD("40 53 48 83 EC 70 4C 63 51 28").GetPtr(v_CL_CopyExistingEntity); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const + virtual void Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_CL_CopyExistingEntity, &CL_CopyExistingEntity); - } - virtual void Detach(void) const - { - DetourDetach((LPVOID*)&v_CL_CopyExistingEntity, &CL_CopyExistingEntity); + DetourSetup(&v_CL_CopyExistingEntity, &CL_CopyExistingEntity, bAttach); } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/client/cl_main.cpp b/r5dev/engine/client/cl_main.cpp index 8a54e389..49e013a6 100644 --- a/r5dev/engine/client/cl_main.cpp +++ b/r5dev/engine/client/cl_main.cpp @@ -10,6 +10,8 @@ #include "cl_main.h" #include "engine/net.h" #include "cdll_engine_int.h" +#include "windows/id3dx.h" +#include "geforce/reflex.h" static float s_lastMovementCall = 0.0; static float s_LastFrameTime = 0.0; @@ -17,9 +19,9 @@ static float s_LastFrameTime = 0.0; //----------------------------------------------------------------------------- // Purpose: run client's movement frame //----------------------------------------------------------------------------- -void H_CL_Move() +void CL_MoveEx() { - CClientState* cl = GetBaseLocalClient(); + CClientState* const cl = GetBaseLocalClient(); if (!cl->IsConnected()) return; @@ -40,15 +42,15 @@ void H_CL_Move() const float hostTimeScale = host_timescale->GetFloat(); const bool isTimeScaleDefault = hostTimeScale == 1.0; - const float minFrameTime = usercmd_frametime_min->GetFloat(); - const float maxFrameTime = usercmd_frametime_max->GetFloat(); + const float minFrameTime = usercmd_frametime_min.GetFloat(); + const float maxFrameTime = usercmd_frametime_max.GetFloat(); const float netTime = float(*g_pNetTime); if (cl->m_flNextCmdTime <= (maxFrameTime * 0.5f) + netTime) sendPacket = chan->CanPacket(); - else if (cl->m_nOutgoingCommandNr - (commandTick+1) < MAX_BACKUP_COMMANDS || isTimeScaleDefault) + else if (cl->m_nOutgoingCommandNr - (commandTick+1) < MAX_NEW_COMMANDS || isTimeScaleDefault) sendPacket = false; const bool isActive = cl->IsActive(); @@ -68,53 +70,48 @@ void H_CL_Move() float frameTime = 0.0f; - if (cl_move_use_dt->GetBool()) + float timeScale; + float deltaTime; + + if (isPaused) { - float timeScale; - float deltaTime; - - if (isPaused) - { - timeScale = 1.0f; - frameTime = movementCallTime - s_lastMovementCall; - deltaTime = frameTime; - } - else - { - timeScale = hostTimeScale; - frameTime = cl->m_flFrameTime + s_LastFrameTime; - deltaTime = frameTime / timeScale; - } - - // Clamp the frame time to the maximum. - if (deltaTime > maxFrameTime) - frameTime = timeScale * maxFrameTime; - - // Drop this frame if delta time is below the minimum. - const bool dropFrame = (isTimeScaleDefault && deltaTime < minFrameTime); - - // This check originally was 'time < 0.0049999999', but - // that caused problems when the framerate was above 190. - if (dropFrame) - { - s_LastFrameTime = frameTime; - return; - } - - s_LastFrameTime = 0.0; + timeScale = 1.0f; + frameTime = movementCallTime - s_lastMovementCall; + deltaTime = frameTime; } - //else if (isPaused) - // // This hlClient virtual call just returns false. + else + { + timeScale = hostTimeScale; + frameTime = cl->m_flFrameTime + s_LastFrameTime; + deltaTime = frameTime / timeScale; + } + + // Clamp the frame time to the maximum. + if (deltaTime > maxFrameTime) + frameTime = timeScale * maxFrameTime; + + // Drop this frame if delta time is below the minimum. + const bool dropFrame = (isTimeScaleDefault && deltaTime < minFrameTime); + + // This check originally was 'time < 0.0049999999', but + // that caused problems when the framerate was above 190. + if (dropFrame) + { + s_LastFrameTime = frameTime; + return; + } + + s_LastFrameTime = 0.0; // Create and store usercmd structure. g_pHLClient->CreateMove(nextCommandNr, frameTime, !isPaused); cl->m_nOutgoingCommandNr = nextCommandNr; } - CL_RunPrediction(); + v_CL_RunPrediction(); if (sendPacket) - CL_SendMove(); + v_CL_SendMove(); else chan->SetChoked(); // Choke the packet... @@ -132,19 +129,14 @@ void H_CL_Move() chan->SendDatagram(nullptr); // Use full update rate when active. - float delta = netTime - float(cl->m_flNextCmdTime); - float maxDelta = fminf(fmaxf(delta, 0.0f), minFrameTime); + const float delta = netTime - float(cl->m_flNextCmdTime); + const float maxDelta = fminf(fmaxf(delta, 0.0f), minFrameTime); cl->m_flNextCmdTime = double(minFrameTime + netTime - maxDelta); } } -void VCL_Main::Attach() const +void VCL_Main::Detour(const bool bAttach) const { - DetourAttach(&CL_Move, &H_CL_Move); -} - -void VCL_Main::Detach() const -{ - DetourDetach(&CL_Move, &H_CL_Move); + DetourSetup(&v_CL_Move, &CL_MoveEx, bAttach); } diff --git a/r5dev/engine/client/cl_main.h b/r5dev/engine/client/cl_main.h index efa44118..bd12eceb 100644 --- a/r5dev/engine/client/cl_main.h +++ b/r5dev/engine/client/cl_main.h @@ -1,19 +1,10 @@ #pragma once -inline CMemory p_CL_Move; -inline void(*CL_Move)(void); - -inline CMemory p_CL_SendMove; -inline void(*CL_SendMove)(void); - -inline CMemory p_CL_EndMovie; -inline int(*CL_EndMovie)(void); - -inline CMemory p_CL_ClearState; -inline int(*CL_ClearState)(void); - -inline CMemory p_CL_RunPrediction; -inline void(*CL_RunPrediction)(void); +inline void(*v_CL_Move)(void); +inline void(*v_CL_SendMove)(void); +inline int(*v_CL_EndMovie)(void); +inline int(*v_CL_ClearState)(void); +inline void(*v_CL_RunPrediction)(void); inline bool g_bClientDLL = false; @@ -28,36 +19,22 @@ class VCL_Main : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CL_Move", p_CL_Move.GetPtr()); - LogFunAdr("CL_SendMove", p_CL_SendMove.GetPtr()); - LogFunAdr("CL_EndMovie", p_CL_EndMovie.GetPtr()); - LogFunAdr("CL_ClearState", p_CL_ClearState.GetPtr()); - LogFunAdr("CL_RunPrediction", p_CL_RunPrediction.GetPtr()); + LogFunAdr("CL_Move", v_CL_Move); + LogFunAdr("CL_SendMove", v_CL_SendMove); + LogFunAdr("CL_EndMovie", v_CL_EndMovie); + LogFunAdr("CL_ClearState", v_CL_ClearState); + LogFunAdr("CL_RunPrediction", v_CL_RunPrediction); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CL_Move = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 83 3D ?? ?? ?? ?? ?? 0F B6 DA"); - p_CL_SendMove = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ??"); - p_CL_EndMovie = g_GameDll.FindPatternSIMD("48 8B C4 48 83 EC 68 80 3D ?? ?? ?? ?? ??"); - p_CL_ClearState = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 1D ?? ?? ?? ??"); - p_CL_RunPrediction = g_GameDll.FindPatternSIMD("4C 8B DC 48 83 EC 58 83 3D ?? ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CL_Move = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 83 3D ?? ?? ?? ?? ?? 44 0F 29 5C 24 ??"); - p_CL_SendMove = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ??"); - p_CL_EndMovie = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 74 7B"); - p_CL_ClearState = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01"); - p_CL_RunPrediction = g_GameDll.FindPatternSIMD("48 83 EC 48 83 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??"); -#endif - CL_Move = p_CL_Move.RCast(); - CL_SendMove = p_CL_SendMove.RCast(); - CL_EndMovie = p_CL_EndMovie.RCast(); - CL_ClearState = p_CL_ClearState.RCast(); - CL_RunPrediction = p_CL_RunPrediction.RCast(); + g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 83 3D ?? ?? ?? ?? ?? 44 0F 29 5C 24 ??").GetPtr(v_CL_Move); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ??").GetPtr(v_CL_SendMove); + g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 74 7B").GetPtr(v_CL_EndMovie); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01").GetPtr(v_CL_ClearState); + g_GameDll.FindPatternSIMD("48 83 EC 48 83 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??").GetPtr(v_CL_RunPrediction); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/client/cl_rcon.cpp b/r5dev/engine/client/cl_rcon.cpp index 8f2dabd4..5ff53e41 100644 --- a/r5dev/engine/client/cl_rcon.cpp +++ b/r5dev/engine/client/cl_rcon.cpp @@ -16,6 +16,20 @@ #include "common/igameserverdata.h" +//----------------------------------------------------------------------------- +// Purpose: console variables +//----------------------------------------------------------------------------- +static ConVar rcon_address("rcon_address", "[loopback]:37015", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote server access address"); + +//----------------------------------------------------------------------------- +// Purpose: console commands +//----------------------------------------------------------------------------- +static void RCON_Disconnect_f(); +static void RCON_CmdQuery_f(const CCommand& args); + +static ConCommand rcon("rcon", RCON_CmdQuery_f, "Forward RCON query to remote server", FCVAR_CLIENTDLL | FCVAR_RELEASE, nullptr, "rcon \"\""); +static ConCommand rcon_disconnect("rcon_disconnect", RCON_Disconnect_f, "Disconnect from RCON server", FCVAR_CLIENTDLL | FCVAR_RELEASE); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -29,6 +43,9 @@ CRConClient::CRConClient() //----------------------------------------------------------------------------- CRConClient::~CRConClient(void) { + // NOTE: do not call Shutdown() from the destructor as the OS's socket + // system would be shutdown by now, call Shutdown() in application + // shutdown code instead } //----------------------------------------------------------------------------- @@ -76,7 +93,7 @@ void CRConClient::Disconnect(const char* szReason) szReason = "unknown reason"; } - DevMsg(eDLL_T::CLIENT, "Disconnect: (%s)\n", szReason); + Msg(eDLL_T::CLIENT, "RCON disconnect: (%s)\n", szReason); m_Socket.CloseAcceptedSocket(0); } } @@ -112,7 +129,7 @@ bool CRConClient::ProcessMessage(const char* pMsgBuf, const int nMsgLen) } } - DevMsg(eDLL_T::NETCON, "%s", response.responsemsg().c_str()); + Msg(eDLL_T::NETCON, "%s", response.responsemsg().c_str()); break; } case sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG: @@ -187,13 +204,23 @@ SocketHandle_t CRConClient::GetSocket(void) return SH_GetNetConSocketHandle(this, 0); } +//----------------------------------------------------------------------------- +// Purpose: request whether to recv logs from RCON server when cvar changes +//----------------------------------------------------------------------------- +static void RCON_InputOnlyChanged_f(IConVar* pConVar, const char* pOldString) +{ + RCONClient()->RequestConsoleLog(RCONClient()->ShouldReceive()); +} + +static ConVar cl_rcon_inputonly("cl_rcon_inputonly", "0", FCVAR_RELEASE, "Tells the rcon server whether or not we are input only.", + false, 0.f, false, 0.f, RCON_InputOnlyChanged_f); + //----------------------------------------------------------------------------- // Purpose: returns whether or not we should receive logs from the server -// Output : SOCKET_ERROR (-1) on failure //----------------------------------------------------------------------------- bool CRConClient::ShouldReceive(void) { - return (!IsRemoteLocal() && !cl_rcon_inputonly->GetBool()); + return (!IsRemoteLocal() && !cl_rcon_inputonly.GetBool()); } //----------------------------------------------------------------------------- @@ -221,8 +248,102 @@ bool CRConClient::IsConnected(void) } /////////////////////////////////////////////////////////////////////////////// -CRConClient* g_RCONClient(new CRConClient()); +static CRConClient s_RCONClient; CRConClient* RCONClient() // Singleton RCON Client. { - return g_RCONClient; + return &s_RCONClient; +} + +/* +===================== +RCON_CmdQuery_f + + Issues an RCON command to the + RCON server. +===================== +*/ +static void RCON_CmdQuery_f(const CCommand& args) +{ + const int64_t argCount = args.ArgC(); + + if (argCount < 2) + { + const char* pszAddress = rcon_address.GetString(); + + if (RCONClient()->IsInitialized() + && !RCONClient()->IsConnected() + && pszAddress[0]) + { + RCONClient()->Connect(pszAddress); + } + } + else + { + if (!RCONClient()->IsInitialized()) + { + Warning(eDLL_T::CLIENT, "Failed to issue command to RCON server: %s\n", "uninitialized"); + return; + } + else if (RCONClient()->IsConnected()) + { + vector vecMsg; + bool bSuccess = false; + const SocketHandle_t hSocket = RCONClient()->GetSocket(); + + if (strcmp(args.Arg(1), "PASS") == 0) // Auth with RCON server using rcon_password ConVar value. + { + if (argCount > 2) + { + bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(2), "", cl_rcon::request_t::SERVERDATA_REQUEST_AUTH); + } + else + { + Warning(eDLL_T::CLIENT, "Failed to issue command to RCON server: %s\n", "no password given"); + return; + } + + if (bSuccess) + { + RCONClient()->Send(hSocket, vecMsg.data(), int(vecMsg.size())); + } + + return; + } + else if (strcmp(args.Arg(1), "disconnect") == 0) // Disconnect from RCON server. + { + RCONClient()->Disconnect("issued by user"); + return; + } + + bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(1), args.ArgS(), cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND); + if (bSuccess) + { + RCONClient()->Send(hSocket, vecMsg.data(), int(vecMsg.size())); + } + return; + } + else + { + Warning(eDLL_T::CLIENT, "Failed to issue command to RCON server: %s\n", "unconnected"); + return; + } + } +} + +/* +===================== +RCON_Disconnect_f + + Disconnect from RCON server +===================== +*/ +static void RCON_Disconnect_f() +{ + const bool bIsConnected = RCONClient()->IsConnected(); + RCONClient()->Disconnect("issued by user"); + + if (bIsConnected) // Log if client was indeed connected. + { + Msg(eDLL_T::CLIENT, "User closed RCON connection\n"); + } } diff --git a/r5dev/engine/client/cl_splitscreen.h b/r5dev/engine/client/cl_splitscreen.h index 7fbebb44..eb3e8d02 100644 --- a/r5dev/engine/client/cl_splitscreen.h +++ b/r5dev/engine/client/cl_splitscreen.h @@ -128,26 +128,18 @@ class VSplitScreen : public IDetour { virtual void GetAdr(void) const { - LogVarAdr("g_SplitScreenMgr", reinterpret_cast(g_pSplitScreenMgr)); + LogVarAdr("g_SplitScreenMgr", g_pSplitScreenMgr); } virtual void GetFun(void) const { } virtual void GetVar(void) const { - const char* pszPattern; - const char* pszInstruction; + const char* const pszPattern = "40 53 48 83 EC 20 48 8D 1D ?? ?? ?? ?? 83 FA FF 75 12 48 8B 05 ?? ?? ?? ?? 48 8B CB FF 50 28 48 63 C8 EB 03 48 63 CA 48 69 C1 ?? ?? ?? ?? 66 C7 84 18 ?? ?? ?? ?? ?? ??";; + const char* const pszInstruction = "48 8D"; -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - pszPattern = "83 FA FF 75 22 48 8D 05 ?? ?? ?? ??"; - pszInstruction = "4C 8D"; -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - pszPattern = "40 53 48 83 EC 20 48 8D 1D ?? ?? ?? ?? 83 FA FF 75 12 48 8B 05 ?? ?? ?? ?? 48 8B CB FF 50 28 48 63 C8 EB 03 48 63 CA 48 69 C1 ?? ?? ?? ?? 66 C7 84 18 ?? ?? ?? ?? ?? ??"; - pszInstruction = "48 8D"; -#endif g_pSplitScreenMgr = g_GameDll.FindPatternSIMD(pszPattern).FindPatternSelf(pszInstruction).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { }; - virtual void Detach(void) const { }; + virtual void Detour(const bool bAttach) const { }; }; #endif // CL_SPLITSCREEN_H diff --git a/r5dev/engine/client/client.cpp b/r5dev/engine/client/client.cpp index 85315e74..496e7afe 100644 --- a/r5dev/engine/client/client.cpp +++ b/r5dev/engine/client/client.cpp @@ -13,9 +13,14 @@ #include "tier1/strtools.h" #include "engine/server/server.h" #include "engine/client/client.h" +#ifndef CLIENT_DLL +#include "networksystem/hostmanager.h" +#include "jwt/include/decode.h" +#include "mbedtls/include/mbedtls/sha256.h" +#endif -// 128+1 so that the client still receives the 'console command too long' message. -#define STRINGCMD_MAX_LEN 129 +// Absolute max string cmd length, any character past this will be NULLED. +#define STRINGCMD_MAX_LEN 512 //--------------------------------------------------------------------------------- // Purpose: throw away any residual garbage in the channel @@ -23,9 +28,9 @@ void CClient::Clear(void) { #ifndef CLIENT_DLL - g_ServerPlayer[GetUserID()].Reset(); // Reset ServerPlayer slot. + GetClientExtended()->Reset(); // Reset extended data. #endif // !CLIENT_DLL - v_CClient_Clear(this); + CClient__Clear(this); } //--------------------------------------------------------------------------------- @@ -37,39 +42,224 @@ void CClient::VClear(CClient* pClient) pClient->Clear(); } +#ifndef CLIENT_DLL +//--------------------------------------------------------------------------------- +// Purpose: gets the extended client data +// Output : CClientExtended* - +//--------------------------------------------------------------------------------- +CClientExtended* CClient::GetClientExtended(void) const +{ + return m_pServer->GetClientExtended(m_nUserID); +} +#endif // !CLIENT_DLL + + +static const char JWT_PUBLIC_KEY[] = +"-----BEGIN PUBLIC KEY-----\n" +"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2/335exIZ6LE8pYi6e50\n" +"7tH19tXaeeEJVF5XXpTCXpndXIIWVimvg6xQ381eajySDw93wvG1DzW3U/6LHzyt\n" +"Q++N8w7N+FwnXyoDUD5Y8hheTZv6jjLoYT8ZtsMl20k9UosrbFBTMUhgmIT2dVth\n" +"LH+rT9ohpUNwQXHJvTOs9eY74GyfFw93+32LANBPZ8b+S8S3oZnKFVeCxRkYKsV0\n" +"b34POHVBbXNw6Kt163gR5zaiCfJJtRto9AA7MV2t9pfy8CChs3uJ+Xn7QVHD5cqt\n" +"Msg9MBac2Pvs2j+8wJ/igAVL5L81z3FXVt04id59TfPMUbYhRfY8pk7FB0MCigOH\n" +"dwIDAQAB\n" +"-----END PUBLIC KEY-----\n"; + +static ConVar sv_onlineAuthEnable("sv_onlineAuthEnable", "1", FCVAR_RELEASE, "Enables the server-side online authentication system"); + +static ConVar sv_onlineAuthValidateExpiry("sv_onlineAuthValidateExpiry", "1", FCVAR_RELEASE, "Validate the online authentication token 'expiry' claim"); +static ConVar sv_onlineAuthValidateIssuedAt("sv_onlineAuthValidateIssuedAt", "1", FCVAR_RELEASE, "Validate the online authentication token 'issued at' claim"); + +static ConVar sv_onlineAuthExpiryTolerance("sv_onlineAuthExpiryTolerance", "1", FCVAR_DEVELOPMENTONLY, "The online authentication token 'expiry' claim tolerance in seconds", true, 0.f, true, float(UINT8_MAX), "Must range between [0,255]"); +static ConVar sv_onlineAuthIssuedAtTolerance("sv_onlineAuthIssuedAtTolerance", "30", FCVAR_DEVELOPMENTONLY, "The online authentication token 'issued at' claim tolerance in seconds", true, 0.f, true, float(UINT8_MAX), "Must range between [0,255]"); + +static ConVar sv_quota_stringCmdsPerSecond("sv_quota_stringCmdsPerSecond", "16", FCVAR_RELEASE, "How many string commands per second clients are allowed to submit, 0 to disallow all string commands", true, 0.f, false, 0.f); + +//--------------------------------------------------------------------------------- +// Purpose: check whether this client is authorized to join this server +// Input : *playerName - +// *reasonBuf - +// reasonBufLen - +// Output : true if authorized, false otherwise +//--------------------------------------------------------------------------------- +bool CClient::Authenticate(const char* const playerName, char* const reasonBuf, const size_t reasonBufLen) +{ +#ifndef CLIENT_DLL + // don't bother checking origin auth on bots or local clients + if (IsFakeClient() || GetNetChan()->GetRemoteAddress().IsLoopback()) + return true; + + l8w8jwt_claim* claims = nullptr; + size_t numClaims = 0; + + // formats the error reason, and frees the claims and returns +#define ERROR_AND_RETURN(fmt, ...) \ + do {\ + V_snprintf(reasonBuf, reasonBufLen, fmt, ##__VA_ARGS__); \ + if (claims) {\ + l8w8jwt_free_claims(claims, numClaims); \ + }\ + return false; \ + } while(0)\ + + KeyValues* const cl_onlineAuthTokenKv = this->m_ConVars->FindKey("cl_onlineAuthToken"); + KeyValues* const cl_onlineAuthTokenSignature1Kv = this->m_ConVars->FindKey("cl_onlineAuthTokenSignature1"); + KeyValues* const cl_onlineAuthTokenSignature2Kv = this->m_ConVars->FindKey("cl_onlineAuthTokenSignature2"); + + if (!cl_onlineAuthTokenKv || !cl_onlineAuthTokenSignature1Kv) + ERROR_AND_RETURN("Missing token"); + + const char* const onlineAuthToken = cl_onlineAuthTokenKv->GetString(); + const char* const onlineAuthTokenSignature1 = cl_onlineAuthTokenSignature1Kv->GetString(); + const char* const onlineAuthTokenSignature2 = cl_onlineAuthTokenSignature2Kv->GetString(); + + char fullToken[1024]; // enough buffer for 3x255, which is cvar count * userinfo str limit. + const int tokenLen = snprintf(fullToken, sizeof(fullToken), "%s.%s%s", + onlineAuthToken, onlineAuthTokenSignature1, onlineAuthTokenSignature2); + + if (tokenLen < 0) + ERROR_AND_RETURN("Token stitching failed"); + + struct l8w8jwt_decoding_params params; + l8w8jwt_decoding_params_init(¶ms); + + params.alg = L8W8JWT_ALG_RS256; + + params.jwt = (char*)fullToken; + params.jwt_length = tokenLen; + + params.verification_key = (unsigned char*)JWT_PUBLIC_KEY; + params.verification_key_length = sizeof(JWT_PUBLIC_KEY); + + params.validate_exp = sv_onlineAuthValidateExpiry.GetBool(); + params.exp_tolerance_seconds = (uint8_t)sv_onlineAuthExpiryTolerance.GetInt(); + + params.validate_iat = sv_onlineAuthValidateIssuedAt.GetBool(); + params.iat_tolerance_seconds = (uint8_t)sv_onlineAuthIssuedAtTolerance.GetInt(); + + enum l8w8jwt_validation_result validation_result; + const int r = l8w8jwt_decode(¶ms, &validation_result, &claims, &numClaims); + + if (r != L8W8JWT_SUCCESS) + ERROR_AND_RETURN("Code %i", r); + + if (validation_result != L8W8JWT_VALID) + { + char reasonBuffer[64]; + l8w8jwt_get_validation_result_desc(validation_result, reasonBuffer, sizeof(reasonBuffer)); + + ERROR_AND_RETURN("%s", reasonBuffer); + } + + bool foundSessionId = false; + for (size_t i = 0; i < numClaims; ++i) + { + // session id + if (!strcmp(claims[i].key, "sessionId")) + { + const char* const sessionId = claims[i].value; + + char newId[256]; + const int idLen = snprintf(newId, sizeof(newId), "%llu-%s-%s", + (NucleusID_t)this->m_DataBlock.userData, + playerName, + g_ServerHostManager.GetHostIP().c_str()); + + if (idLen < 0) + ERROR_AND_RETURN("Session ID stitching failed"); + + uint8_t sessionHash[32]; // hash decoded from JWT token + V_hextobinary(sessionId, strlen(sessionId), sessionHash, sizeof(sessionHash)); + + uint8_t oobHash[32]; // hash of data collected from out of band packet + const int shRet = mbedtls_sha256((const uint8_t*)newId, idLen, oobHash, NULL); + + if (shRet != NULL) + ERROR_AND_RETURN("Session ID hashing failed"); + + if (memcmp(oobHash, sessionHash, sizeof(sessionHash)) != 0) + ERROR_AND_RETURN("Token is not authorized for the connecting client"); + + foundSessionId = true; + } + } + + if (!foundSessionId) + ERROR_AND_RETURN("No session ID"); + + l8w8jwt_free_claims(claims, numClaims); + +#undef ERROR_AND_RETURN +#endif // !CLIENT_DLL + + return true; +} + //--------------------------------------------------------------------------------- // Purpose: connect new client // Input : *szName - -// *pNetChannel - +// *pNetChan - // bFakePlayer - -// *a5 - +// *conVars - // *szMessage - // nMessageSize - // Output : true if connection was successful, false otherwise //--------------------------------------------------------------------------------- -bool CClient::Connect(const char* szName, void* pNetChannel, bool bFakePlayer, void* a5, char* szMessage, int nMessageSize) +bool CClient::Connect(const char* szName, CNetChan* pNetChan, bool bFakePlayer, + CUtlVector* conVars, char* szMessage, int nMessageSize) { - return v_CClient_Connect(this, szName, pNetChannel, bFakePlayer, a5, szMessage, nMessageSize); +#ifndef CLIENT_DLL + GetClientExtended()->Reset(); // Reset extended data. +#endif + + if (!CClient__Connect(this, szName, pNetChan, bFakePlayer, conVars, szMessage, nMessageSize)) + return false; + +#ifndef CLIENT_DLL + +#define REJECT_CONNECTION(fmt, ...) V_snprintf(szMessage, nMessageSize, fmt, ##__VA_ARGS__); + + if (sv_onlineAuthEnable.GetBool()) + { + char authFailReason[512]; + if (!Authenticate(szName, authFailReason, sizeof(authFailReason))) + { + REJECT_CONNECTION("Failed to verify authentication token [%s]", authFailReason); + + const bool bEnableLogging = sv_showconnecting.GetBool(); + if (bEnableLogging) + { + const char* const netAdr = pNetChan ? pNetChan->GetAddress() : ""; + + Warning(eDLL_T::SERVER, "Client '%s' ('%llu') failed online authentication! [%s]\n", + netAdr, (NucleusID_t)m_DataBlock.userData, authFailReason); + } + + return false; + } + } + +#undef REJECT_CONNECTION +#endif // !CLIENT_DLL + + return true; } //--------------------------------------------------------------------------------- // Purpose: connect new client // Input : *pClient - // *szName - -// *pNetChannel - +// *pNetChan - // bFakePlayer - // *a5 - // *szMessage - // nMessageSize - // Output : true if connection was successful, false otherwise //--------------------------------------------------------------------------------- -bool CClient::VConnect(CClient* pClient, const char* szName, void* pNetChannel, bool bFakePlayer, void* a5, char* szMessage, int nMessageSize) +bool CClient::VConnect(CClient* pClient, const char* szName, CNetChan* pNetChan, bool bFakePlayer, + CUtlVector* conVars, char* szMessage, int nMessageSize) { - bool bResult = v_CClient_Connect(pClient, szName, pNetChannel, bFakePlayer, a5, szMessage, nMessageSize); -#ifndef CLIENT_DLL - g_ServerPlayer[pClient->GetUserID()].Reset(); // Reset ServerPlayer slot. -#endif // !CLIENT_DLL - return bResult; + return pClient->Connect(szName, pNetChan, bFakePlayer, conVars, szMessage, nMessageSize);; } //--------------------------------------------------------------------------------- @@ -92,7 +282,7 @@ void CClient::Disconnect(const Reputation_t nRepLvl, const char* szReason, ...) szBuf[sizeof(szBuf) - 1] = '\0'; va_end(vArgs); }///////////////////////////// - v_CClient_Disconnect(this, nRepLvl, szBuf); + CClient__Disconnect(this, nRepLvl, szBuf); } } @@ -104,14 +294,14 @@ void CClient::VActivatePlayer(CClient* pClient) { // Set the client instance to 'ready' before calling ActivatePlayer. pClient->SetPersistenceState(PERSISTENCE::PERSISTENCE_READY); - v_CClient_ActivatePlayer(pClient); + CClient__ActivatePlayer(pClient); #ifndef CLIENT_DLL const CNetChan* pNetChan = pClient->GetNetChan(); - if (pNetChan && sv_showconnecting->GetBool()) + if (pNetChan && sv_showconnecting.GetBool()) { - DevMsg(eDLL_T::SERVER, "Activated player #%d; channel %s(%s) ('%llu')\n", + Msg(eDLL_T::SERVER, "Activated player #%d; channel %s(%s) ('%llu')\n", pClient->GetUserID(), pNetChan->GetName(), pNetChan->GetAddress(), pClient->GetNucleusID()); } #endif // !CLIENT_DLL @@ -125,7 +315,7 @@ void CClient::VActivatePlayer(CClient* pClient) // bForceReliable - // bVoice - //--------------------------------------------------------------------------------- -bool CClient::SendNetMsgEx(CNetMessage* pMsg, char bLocal, bool bForceReliable, bool bVoice) +bool CClient::SendNetMsgEx(CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice) { if (!ShouldReplayMessage(pMsg)) { @@ -133,7 +323,7 @@ bool CClient::SendNetMsgEx(CNetMessage* pMsg, char bLocal, bool bForceReliable, pMsg->m_nGroup = NetMessageGroup::NoReplay; } - return v_CClient_SendNetMsgEx(this, pMsg, bLocal, bForceReliable, bVoice); + return CClient__SendNetMsgEx(this, pMsg, bLocal, bForceReliable, bVoice); } //--------------------------------------------------------------------------------- @@ -145,7 +335,64 @@ bool CClient::SendNetMsgEx(CNetMessage* pMsg, char bLocal, bool bForceReliable, //--------------------------------------------------------------------------------- void* CClient::VSendSnapshot(CClient* pClient, CClientFrame* pFrame, int nTick, int nTickAck) { - return v_CClient_SendSnapshot(pClient, pFrame, nTick, nTickAck); + return CClient__SendSnapshot(pClient, pFrame, nTick, nTickAck); +} + +//--------------------------------------------------------------------------------- +// Purpose: internal hook to 'CClient::SendNetMsgEx' +// Input : *pClient - +// *pMsg - +// bLocal - +// bForceReliable - +// bVoice - +//--------------------------------------------------------------------------------- +bool CClient::VSendNetMsgEx(CClient* pClient, CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice) +{ + return pClient->SendNetMsgEx(pMsg, bLocal, bForceReliable, bVoice); +} + +//--------------------------------------------------------------------------------- +// Purpose: write data into data blocks to send to the client +// Input : &buf +//--------------------------------------------------------------------------------- +void CClient::WriteDataBlock(CClient* pClient, bf_write& buf) +{ +#ifndef CLIENT_DLL + if (net_data_block_enabled->GetBool()) + { + buf.WriteUBitLong(net_NOP, NETMSG_TYPE_BITS); + + const int remainingBits = buf.GetNumBitsWritten() % 8; + + if (remainingBits && (8 - remainingBits) > 0) + { + // fill the last bits in the last byte with NOP + buf.WriteUBitLong(net_NOP, 8 - remainingBits); + } + + const bool isMultiplayer = g_ServerGlobalVariables->m_nGameMode < GameMode_t::PVE_MODE; + pClient->m_DataBlock.sender.WriteDataBlock(buf.GetData(), buf.GetNumBytesWritten(), isMultiplayer, buf.GetDebugName()); + } + else + { + pClient->m_NetChannel->SendData(buf, true); + } +#endif // !CLIENT_DLL +} + +//--------------------------------------------------------------------------------- +// Purpose: some versions of the binary have an optimization that shifts the 'this' +// pointer of the CClient structure by 8 bytes to avoid having to cache the vftable +// pointer if it never get used. Here we shift it back so it aligns again. +//--------------------------------------------------------------------------------- +CClient* AdjustShiftedThisPointer(CClient* shiftedPointer) +{ + /* Original function called method "CClient::ExecuteStringCommand" with an optimization + * that shifted the 'this' pointer with 8 bytes. + * Since this has been inlined with "CClient::ProcessStringCmd" as of S2, the shifting + * happens directly to anything calling this function. */ + char* pShifted = reinterpret_cast(shiftedPointer) - 8; + return reinterpret_cast(pShifted); } //--------------------------------------------------------------------------------- @@ -157,21 +404,16 @@ void* CClient::VSendSnapshot(CClient* pClient, CClientFrame* pFrame, int nTick, bool CClient::VProcessStringCmd(CClient* pClient, NET_StringCmd* pMsg) { #ifndef CLIENT_DLL -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - CClient* pClient_Adj = pClient; -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - /* Original function called method "CClient::ExecuteStringCommand" with an optimization - * that shifted the 'this' pointer with 8 bytes. - * Since this has been inlined with "CClient::ProcessStringCmd" as of S2, the shifting - * happens directly to anything calling this function. */ - char* pShifted = reinterpret_cast(pClient) - 8; - CClient* pClient_Adj = reinterpret_cast(pShifted); -#endif // !GAMEDLL_S0 || !GAMEDLL_S1 - int nUserID = pClient_Adj->GetUserID(); - ServerPlayer_t* pSlot = &g_ServerPlayer[nUserID]; + CClient* const pClient_Adj = AdjustShiftedThisPointer(pClient); - double flStartTime = Plat_FloatTime(); - int nCmdQuotaLimit = sv_quota_stringCmdsPerSecond->GetInt(); + // Jettison the cmd if the client isn't active. + if (!pClient_Adj->IsActive()) + return true; + + CClientExtended* const pSlot = pClient_Adj->GetClientExtended(); + + const double flStartTime = Plat_FloatTime(); + const int nCmdQuotaLimit = sv_quota_stringCmdsPerSecond.GetInt(); if (!nCmdQuotaLimit) return true; @@ -182,16 +424,13 @@ bool CClient::VProcessStringCmd(CClient* pClient, NET_StringCmd* pMsg) // The internal function discards the command if it's null. if (pCmd) { - // If the string length exceeds 128, the will engine return a 'command - // string too long' message back to the client that issued it and - // subsequently jettison the string cmd. Before this routine gets hit, - // the entire string gets parsed (up to 512 bytes). There is an issue - // in CUtlBuffer::ParseToken() that causes it to read past its buffer; - // mostly seems to happen on 32bit, but a carefully crafted string - // should work on 64bit too). The fix is to just null everything past - // the maximum allowed length. The second 'theoretical' fix would be to - // properly fix CUtlBuffer::ParseToken() by computing the UTF8 character - // size each iteration and check if it still doesn't exceed bounds. + // There is an issue in CUtlBuffer::ParseToken() that causes it to read + // past its buffer; mostly seems to happen on 32bit, but a carefully + // crafted string should work on 64bit too). The fix is to just null + // everything past the maximum allowed length. The second 'theoretical' + // fix would be to properly fix CUtlBuffer::ParseToken() by computing + // the UTF8 character size each iteration and check if it still doesn't + // exceed bounds. memset(&pMsg->buffer[STRINGCMD_MAX_LEN], '\0', sizeof(pMsg->buffer) - (STRINGCMD_MAX_LEN)); @@ -222,41 +461,80 @@ bool CClient::VProcessStringCmd(CClient* pClient, NET_StringCmd* pMsg) } #endif // !CLIENT_DLL - return v_CClient_ProcessStringCmd(pClient, pMsg); + return CClient__ProcessStringCmd(pClient, pMsg); } //--------------------------------------------------------------------------------- -// Purpose: internal hook to 'CClient::SendNetMsgEx' -// Input : *pClient - +// Purpose: process set convar +// Input : *pClient - (ADJ) // *pMsg - -// bLocal - -// bForceReliable - -// bVoice - +// Output : //--------------------------------------------------------------------------------- -bool CClient::VSendNetMsgEx(CClient* pClient, CNetMessage* pMsg, char bLocal, bool bForceReliable, bool bVoice) +bool CClient::VProcessSetConVar(CClient* pClient, NET_SetConVar* pMsg) { - return pClient->SendNetMsgEx(pMsg, bLocal, bForceReliable, bVoice); +#ifndef CLIENT_DLL + CClient* const pAdj = AdjustShiftedThisPointer(pClient); + CClientExtended* const pSlot = pAdj->GetClientExtended(); + + // This loop never exceeds 255 iterations, NET_SetConVar::ReadFromBuffer(...) + // reads and inserts up to 255 entries in the vector (reads a byte for size). + FOR_EACH_VEC(pMsg->m_ConVars, i) + { + const NET_SetConVar::cvar_t& entry = pMsg->m_ConVars[i]; + const char* const name = entry.name; + const char* const value = entry.value; + + // Discard any ConVar change request if it contains funky characters. + bool bFunky = false; + for (const char* s = name; *s != '\0'; ++s) + { + if (!V_isalnum(*s) && *s != '_') + { + bFunky = true; + break; + } + } + if (bFunky) + { + DevWarning(eDLL_T::SERVER, "Ignoring ConVar change request for variable '%s' from client '%s'; invalid characters in the variable name\n", + name, pAdj->GetClientName()); + continue; + } + + // The initial set of ConVars must contain all client ConVars that are + // flagged UserInfo. This is a simple fix to exploits that send bogus + // data later, and catches bugs, such as new UserInfo ConVars appearing + // later, which shouldn't happen. + if (pSlot->m_bInitialConVarsSet && !pAdj->m_ConVars->FindKey(name)) + { + DevWarning(eDLL_T::SERVER, "UserInfo update from \"%s\" ignored: %s = %s\n", + pAdj->GetClientName(), name, value); + continue; + } + + // Add ConVar to list and set string. + pAdj->m_ConVars->SetString(name, value); + DevMsg(eDLL_T::SERVER, "UserInfo update from \"%s\": %s = %s\n", pAdj->GetClientName(), name, value); + } + + pSlot->m_bInitialConVarsSet = true; + pAdj->m_bConVarsChanged = true; +#endif // !CLIENT_DLL + + return true; } -void VClient::Attach(void) const +void VClient::Detour(const bool bAttach) const { #ifndef CLIENT_DLL - DetourAttach((LPVOID*)&v_CClient_Clear, &CClient::VClear); - DetourAttach((LPVOID*)&v_CClient_Connect, &CClient::VConnect); - DetourAttach((LPVOID*)&v_CClient_ActivatePlayer, &CClient::VActivatePlayer); - DetourAttach((LPVOID*)&v_CClient_ProcessStringCmd, &CClient::VProcessStringCmd); - DetourAttach((LPVOID*)&v_CClient_SendNetMsgEx, &CClient::VSendNetMsgEx); - //DetourAttach((LPVOID*)&p_CClient_SendSnapshot, &CClient::VSendSnapshot); -#endif // !CLIENT_DLL -} -void VClient::Detach(void) const -{ -#ifndef CLIENT_DLL - DetourDetach((LPVOID*)&v_CClient_Clear, &CClient::VClear); - DetourDetach((LPVOID*)&v_CClient_Connect, &CClient::VConnect); - DetourDetach((LPVOID*)&v_CClient_ActivatePlayer, &CClient::VActivatePlayer); - DetourDetach((LPVOID*)&v_CClient_ProcessStringCmd, &CClient::VProcessStringCmd); - DetourDetach((LPVOID*)&v_CClient_SendNetMsgEx, &CClient::VSendNetMsgEx); - //DetourDetach((LPVOID*)&p_CClient_SendSnapshot, &CClient::VSendSnapshot); + DetourSetup(&CClient__Clear, &CClient::VClear, bAttach); + DetourSetup(&CClient__Connect, &CClient::VConnect, bAttach); + DetourSetup(&CClient__ActivatePlayer, &CClient::VActivatePlayer, bAttach); + DetourSetup(&CClient__SendNetMsgEx, &CClient::VSendNetMsgEx, bAttach); + //DetourSetup(&CClient__SendSnapshot, &CClient::VSendSnapshot, bAttach); + DetourSetup(&CClient__WriteDataBlock, &CClient::WriteDataBlock, bAttach); + + DetourSetup(&CClient__ProcessStringCmd, &CClient::VProcessStringCmd, bAttach); + DetourSetup(&CClient__ProcessSetConVar, &CClient::VProcessSetConVar, bAttach); #endif // !CLIENT_DLL } diff --git a/r5dev/engine/client/client.h b/r5dev/engine/client/client.h index 21e12ebe..a40456af 100644 --- a/r5dev/engine/client/client.h +++ b/r5dev/engine/client/client.h @@ -1,6 +1,8 @@ #pragma once -#include "vpc/keyvalues.h" +#include "tier1/keyvalues.h" #include "common/protocol.h" +#include "ebisusdk/EbisuTypes.h" +#include "engine/net.h" #include "engine/net_chan.h" #include "public/edict.h" #include "engine/server/datablock_sender.h" @@ -20,6 +22,7 @@ enum Reputation_t //----------------------------------------------------------------------------- class CServer; class CClient; +class CClientExtended; struct Spike_t { @@ -65,20 +68,24 @@ public: inline int64_t GetTeamNum() const { return m_iTeamNum; } inline edict_t GetHandle(void) const { return m_nHandle; } inline int GetUserID(void) const { return m_nUserID; } - inline uint64_t GetNucleusID(void) const { return m_nNucleusID; } + inline NucleusID_t GetNucleusID(void) const { return m_nNucleusID; } inline SIGNONSTATE GetSignonState(void) const { return m_nSignonState; } inline PERSISTENCE GetPersistenceState(void) const { return m_nPersistenceState; } inline CNetChan* GetNetChan(void) const { return m_NetChannel; } inline CServer* GetServer(void) const { return m_pServer; } +#ifndef CLIENT_DLL + CClientExtended* GetClientExtended(void) const; +#endif // !CLIENT_DLL + inline int GetCommandTick(void) const { return m_nCommandTick; } inline const char* GetServerName(void) const { return m_szServerName; } inline const char* GetClientName(void) const { return m_szClientName; } inline void SetHandle(edict_t nHandle) { m_nHandle = nHandle; } inline void SetUserID(uint32_t nUserID) { m_nUserID = nUserID; } - inline void SetNucleusID(uint64_t nNucleusID) { m_nNucleusID = nNucleusID; } + inline void SetNucleusID(NucleusID_t nNucleusID) { m_nNucleusID = nNucleusID; } inline void SetSignonState(SIGNONSTATE nSignonState) { m_nSignonState = nSignonState; } inline void SetPersistenceState(PERSISTENCE nPersistenceState) { m_nPersistenceState = nPersistenceState; } @@ -92,18 +99,27 @@ public: inline bool IsFakeClient(void) const { return m_bFakePlayer; } inline bool IsHumanPlayer(void) const { if (!IsConnected() || IsFakeClient()) { return false; } return true; } - bool SendNetMsgEx(CNetMessage* pMsg, char bLocal, bool bForceReliable, bool bVoice); - bool Connect(const char* szName, void* pNetChannel, bool bFakePlayer, void* a5, char* szMessage, int nMessageSize); + bool SendNetMsgEx(CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice); + + bool Authenticate(const char* const playerName, char* const reasonBuf, const size_t reasonBufLen); + bool Connect(const char* szName, CNetChan* pNetChan, bool bFakePlayer, + CUtlVector* conVars, char* szMessage, int nMessageSize); void Disconnect(const Reputation_t nRepLvl, const char* szReason, ...); - static bool VConnect(CClient* pClient, const char* szName, void* pNetChannel, bool bFakePlayer, void* a5, char* szMessage, int nMessageSize); void Clear(void); public: // Hook statics: static void VClear(CClient* pClient); + static bool VConnect(CClient* pClient, const char* szName, CNetChan* pNetChan, bool bFakePlayer, + CUtlVector* conVars, char* szMessage, int nMessageSize); + static void VActivatePlayer(CClient* pClient); - static bool VProcessStringCmd(CClient* pClient, NET_StringCmd* pMsg); static void* VSendSnapshot(CClient* pClient, CClientFrame* pFrame, int nTick, int nTickAck); - static bool VSendNetMsgEx(CClient* pClient, CNetMessage* pMsg, char bLocal, bool bForceReliable, bool bVoice); + static bool VSendNetMsgEx(CClient* pClient, CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice); + + static void WriteDataBlock(CClient* pClient, bf_write& buf); + + static bool VProcessStringCmd(CClient* pClient, NET_StringCmd* pMsg); + static bool VProcessSetConVar(CClient* pClient, NET_SetConVar* pMsg); private: // Stub reimplementation to avoid the 'no overrider' compiler errors in the @@ -146,24 +162,28 @@ private: char m_szClientName[256]; char m_szMachineName[256]; int m_nCommandTick; - char m_bUsePersistence_MAYBE; + bool m_bUsePersistence_MAYBE; char pad_0016[59]; int64_t m_iTeamNum; KeyValues* m_ConVars; - char m_bInitialConVarsSet; - char m_bSendServerInfo; - char m_bSendSignonData; - char m_bFullStateAchieved; + bool m_bConVarsChanged; + bool m_bSendServerInfo; + bool m_bSendSignonData; + bool m_bFullStateAchieved; char pad_0368[4]; CServer* m_pServer; - char pad_0378[24]; + char pad_0378[20]; + int m_nDisconnectTick; bool m_bKickedByFairFight_MAYBE; - char pad_0398[14]; + char pad_0398[3]; + int m_nSendtableCRC; + int m_nMmDev; + char pad_039C[4]; CNetChan* m_NetChannel; char pad_03A8[8]; SIGNONSTATE m_nSignonState; int unk0; - uint64_t m_nNucleusID; + NucleusID_t m_nNucleusID; int unk1; int unk2; int m_nDeltaTick; @@ -171,10 +191,8 @@ private: int m_nSignonTick; int m_nBaselineUpdateTick_MAYBE; char pad_03C0[448]; -#if defined (GAMEDLL_S2) || defined (GAMEDLL_S3) int unk3; int m_nForceWaitForTick; -#endif bool m_bFakePlayer; bool m_bReceivedPacket; bool m_bLowViolence; @@ -183,92 +201,108 @@ private: PERSISTENCE m_nPersistenceState; char pad_05C0[48]; ServerDataBlock m_DataBlock; - char pad_4A3D8[16]; + char pad_4A3D8[60]; int m_LastMovementTick; -#if defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - char pad_4A418[130]; -#endif - char pad_4A49A[80]; + char pad_4A418[86]; + char pad_4A46E[80]; }; -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -static_assert(sizeof(CClient) == 0x4A440); -#else static_assert(sizeof(CClient) == 0x4A4C0); -#endif + +//----------------------------------------------------------------------------- +// Extended CClient class +//----------------------------------------------------------------------------- +// NOTE: since we interface directly with the engine, we cannot modify the +// client structure. In order to add new data to each client instance, we +// need to use this new class which we link directly to the corresponding +// client instance through its UserID. +//----------------------------------------------------------------------------- +class CClientExtended +{ + friend class CClient; +public: + CClientExtended(void) + { + Reset(); + } + inline void Reset(void) + { + m_flNetProcessingTimeMsecs = 0.0; + m_flNetProcessTimeBase = 0.0; + m_flStringCommandQuotaTimeStart = 0.0; + m_nStringCommandQuotaCount = NULL; + m_bInitialConVarsSet = false; + } + +public: // Inlines: + inline void SetNetProcessingTimeMsecs(const double flStartTime, const double flCurrentTime) + { m_flNetProcessingTimeMsecs = (flCurrentTime * 1000) - (flStartTime * 1000); } + inline double GetNetProcessingTimeMsecs(void) const { return m_flNetProcessingTimeMsecs; } + + inline void SetNetProcessingTimeBase(const double flTime) { m_flNetProcessTimeBase = flTime; } + inline double GetNetProcessingTimeBase(void) const { return m_flNetProcessTimeBase; } + + inline void SetStringCommandQuotaTimeStart(const double flTime) { m_flStringCommandQuotaTimeStart = flTime; } + inline double GetStringCommandQuotaTimeStart(void) const { return m_flStringCommandQuotaTimeStart; } + + inline void SetStringCommandQuotaCount(const int iCount) { m_nStringCommandQuotaCount = iCount; } + inline int GetStringCommandQuotaCount(void) const { return m_nStringCommandQuotaCount; } + +private: + // Measure how long this client's packets took to process. + double m_flNetProcessingTimeMsecs; + double m_flNetProcessTimeBase; + + // The start time of the first stringcmd since reset. + double m_flStringCommandQuotaTimeStart; + int m_nStringCommandQuotaCount; + + bool m_bInitialConVarsSet; // Whether or not the initial ConVar KV's are set +}; /* ==== CBASECLIENT ===================================================================================================================================================== */ -inline CMemory p_CClient_Connect; -inline bool(*v_CClient_Connect)(CClient* pClient, const char* szName, void* pNetChannel, bool bFakePlayer, void* a5, char* szMessage, int nMessageSize); - -inline CMemory p_CClient_Disconnect; -inline bool(*v_CClient_Disconnect)(CClient* pClient, const Reputation_t nRepLvl, const char* szReason, ...); - -inline CMemory p_CClient_Clear; -inline void(*v_CClient_Clear)(CClient* pClient); - -inline CMemory p_CClient_ActivatePlayer; -inline void(*v_CClient_ActivatePlayer)(CClient* pClient); - -inline CMemory p_CClient_ProcessStringCmd; -inline bool(*v_CClient_ProcessStringCmd)(CClient* pClient, NET_StringCmd* pMsg); - -inline CMemory p_CClient_SetSignonState; -inline bool(*v_CClient_SetSignonState)(CClient* pClient, SIGNONSTATE signon); - -inline CMemory p_CClient_SendNetMsgEx; -inline bool(*v_CClient_SendNetMsgEx)(CClient* pClient, CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice); - -inline CMemory p_CClient_SendSnapshot; -inline void*(*v_CClient_SendSnapshot)(CClient* pClient, CClientFrame* pFrame, int nTick, int nTickAck); +inline bool(*CClient__Connect)(CClient* pClient, const char* szName, CNetChan* pNetChan, bool bFakePlayer, CUtlVector* conVars, char* szMessage, int nMessageSize); +inline bool(*CClient__Disconnect)(CClient* pClient, const Reputation_t nRepLvl, const char* szReason, ...); +inline void(*CClient__Clear)(CClient* pClient); +inline void(*CClient__ActivatePlayer)(CClient* pClient); +inline bool(*CClient__SetSignonState)(CClient* pClient, SIGNONSTATE signon); +inline bool(*CClient__SendNetMsgEx)(CClient* pClient, CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice); +inline void*(*CClient__SendSnapshot)(CClient* pClient, CClientFrame* pFrame, int nTick, int nTickAck); +inline void(*CClient__WriteDataBlock)(CClient* pClient, bf_write& buf); +inline bool(*CClient__ProcessStringCmd)(CClient* pClient, NET_StringCmd* pMsg); +inline bool(*CClient__ProcessSetConVar)(CClient* pClient, NET_SetConVar* pMsg); /////////////////////////////////////////////////////////////////////////////// class VClient : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CClient::Connect", p_CClient_Connect.GetPtr()); - LogFunAdr("CClient::Disconnect", p_CClient_Disconnect.GetPtr()); - LogFunAdr("CClient::Clear", p_CClient_Clear.GetPtr()); - LogFunAdr("CClient::ActivatePlayer", p_CClient_ActivatePlayer.GetPtr()); - LogFunAdr("CClient::ProcessStringCmd", p_CClient_ProcessStringCmd.GetPtr()); - LogFunAdr("CClient::SetSignonState", p_CClient_SetSignonState.GetPtr()); - LogFunAdr("CClient::SendNetMsgEx", p_CClient_SendNetMsgEx.GetPtr()); - LogFunAdr("CClient::SendSnapshot", p_CClient_SendSnapshot.GetPtr()); + LogFunAdr("CClient::Connect", CClient__Connect); + LogFunAdr("CClient::Disconnect", CClient__Disconnect); + LogFunAdr("CClient::Clear", CClient__Clear); + LogFunAdr("CClient::ActivatePlayer", CClient__ActivatePlayer); + LogFunAdr("CClient::SetSignonState", CClient__SetSignonState); + LogFunAdr("CClient::SendNetMsgEx", CClient__SendNetMsgEx); + LogFunAdr("CClient::SendSnapshot", CClient__SendSnapshot); + LogFunAdr("CClient::WriteDataBlock", CClient__WriteDataBlock); + LogFunAdr("CClient::ProcessStringCmd", CClient__ProcessStringCmd); + LogFunAdr("CClient::ProcessSetConVar", CClient__ProcessSetConVar); } virtual void GetFun(void) const { - p_CClient_Connect = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 20 41 0F B6 E9"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - p_CClient_Disconnect = g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 4C 89 48 20 53 56 57 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ?? 49 8B F8 0F B6 F2"); -#else // !GAMEDLL_S0 || !GAMEDLL_S1 || !GAMEDLL_S2 - p_CClient_Disconnect = g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 4C 89 48 20 53 56 57 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ?? 49 8B F8 8B F2"); -#endif - p_CClient_Clear = g_GameDll.FindPatternSIMD("40 53 41 56 41 57 48 83 EC 20 48 8B D9 48 89 74"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CClient_ActivatePlayer = g_GameDll.FindPatternSIMD("40 53 57 41 57 48 83 EC 30 8B 81 ?? ?? ?? ??"); - p_CClient_ProcessStringCmd = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 81 EC ?? ?? ?? ?? 49 8B D8"); - p_CClient_SendNetMsg = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 30 48 8B 05 ?? ?? ?? ?? 45 0F B6 F1"); - p_CClient_SendSnapshot = g_GameDll.FindPatternSIMD("44 89 44 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 55"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CClient_ActivatePlayer = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 8B 81 B0 03 ?? ?? 48 8B D9 C6"); - p_CClient_ProcessStringCmd = g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 7A 20"); - p_CClient_SendNetMsgEx = g_GameDll.FindPatternSIMD("40 53 55 56 57 41 56 48 83 EC 40 48 8B 05 ?? ?? ?? ??"); - p_CClient_SendSnapshot = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 41 55 41 56 41 57 48 8D 6C 24 ??"); -#endif // !GAMEDLL_S0 || !GAMEDLL_S1 - p_CClient_SetSignonState = g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 10 48 89 70 18 57 48 81 EC ?? ?? ?? ?? 0F 29 70 E8 8B F2"); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 20 41 0F B6 E9").GetPtr(CClient__Connect); + g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 4C 89 48 20 53 56 57 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ?? 49 8B F8 8B F2").GetPtr(CClient__Disconnect); + g_GameDll.FindPatternSIMD("40 53 41 56 41 57 48 83 EC 20 48 8B D9 48 89 74").GetPtr(CClient__Clear); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 8B 81 B0 03 ?? ?? 48 8B D9 C6").GetPtr(CClient__ActivatePlayer); + g_GameDll.FindPatternSIMD("40 53 55 56 57 41 56 48 83 EC 40 48 8B 05 ?? ?? ?? ??").GetPtr(CClient__SendNetMsgEx); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 41 55 41 56 41 57 48 8D 6C 24 ??").GetPtr(CClient__SendSnapshot); + g_GameDll.FindPatternSIMD("40 53 57 48 83 EC 38 48 8B 05 ?? ?? ?? ??").GetPtr(CClient__WriteDataBlock); + g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 7A 20").GetPtr(CClient__ProcessStringCmd); - v_CClient_Connect = p_CClient_Connect.RCast(); - v_CClient_Disconnect = p_CClient_Disconnect.RCast(); - v_CClient_Clear = p_CClient_Clear.RCast(); - v_CClient_ActivatePlayer = p_CClient_ActivatePlayer.RCast(); - v_CClient_ProcessStringCmd = p_CClient_ProcessStringCmd.RCast(); - v_CClient_SetSignonState = p_CClient_SetSignonState.RCast(); - v_CClient_SendNetMsgEx = p_CClient_SendNetMsgEx.RCast(); - v_CClient_SendSnapshot = p_CClient_SendSnapshot.RCast(); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 83 C2 20").GetPtr(CClient__ProcessSetConVar); + g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 10 48 89 70 18 57 48 81 EC ?? ?? ?? ?? 0F 29 70 E8 8B F2").GetPtr(CClient__SetSignonState); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/client/clientstate.cpp b/r5dev/engine/client/clientstate.cpp index 35c528ac..f7c42b5a 100644 --- a/r5dev/engine/client/clientstate.cpp +++ b/r5dev/engine/client/clientstate.cpp @@ -9,13 +9,20 @@ // ///////////////////////////////////////////////////////////////////////////////// #include "core/stdafx.h" -#include "vpc/keyvalues.h" #include "tier0/frametask.h" +#include "engine/common.h" #include "engine/host.h" +#include "engine/host_cmd.h" +#ifndef CLIENT_DLL +#include "engine/server/server.h" +#endif // !CLIENT_DLL #include "clientstate.h" #include "common/callback.h" #include "cdll_engine_int.h" #include "vgui/vgui_baseui_interface.h" +#include "rtech/playlists/playlists.h" +#include +#include //------------------------------------------------------------------------------ @@ -58,7 +65,7 @@ float CClientState::GetClientTime() const { if (m_bClockCorrectionEnabled) { - return (float)m_ClockDriftMgr.m_nSimulationTick * (*(float*)&interval_per_tick); // VERIFY DEREF + return (float)m_ClockDriftMgr.m_nSimulationTick * g_pCommonHostState->interval_per_tick; } else { @@ -128,12 +135,12 @@ void CClientState::VConnectionClosing(CClientState* thisptr, const char* szReaso // Delay execution to the next frame; this is required to avoid a rare crash. // Cannot reload playlists while still disconnecting. - g_TaskScheduler->Dispatch([]() + g_TaskQueue.Dispatch([]() { // Reload the local playlist to override the cached // one from the server we got disconnected from. - _DownloadPlaylists_f(); - KeyValues::InitPlaylists(); + v_Playlists_Download_f(); + Playlists_SDKInit(); }, 0); } @@ -144,35 +151,258 @@ void CClientState::VConnectionClosing(CClientState* thisptr, const char* szReaso // no longer can process server ticks every frame unlike previous games. // Without this, the server CPU and frame time don't get updated to the client. //------------------------------------------------------------------------------ -bool CClientState::VProcessServerTick(CClientState* pClientState, SVC_ServerTick* pServerTick) +bool CClientState::VProcessServerTick(CClientState* thisptr, SVC_ServerTick* msg) { - if (pServerTick->m_NetTick.m_nCommandTick != -1) + if (msg->m_NetTick.m_nCommandTick != -1) { - return CClientState__ProcessServerTick(pClientState, pServerTick); + // Updates statistics and updates clockdrift. + return CClientState__ProcessServerTick(thisptr, msg); } else // Statistics only. { - char* pShifted = reinterpret_cast(pClientState) - 0x10; // Shifted due to compiler optimizations. - CClientState* pClient_Adj = reinterpret_cast(pShifted); + CClientState* const thisptr_ADJ = thisptr->GetShiftedBasePointer(); - CNetChan* pChan = pClient_Adj->m_NetChannel; - pChan->SetRemoteFramerate(pServerTick->m_NetTick.m_flHostFrameTime, pServerTick->m_NetTick.m_flHostFrameTimeStdDeviation); - pChan->SetRemoteCPUStatistics(pServerTick->m_NetTick.m_nServerCPU); + if (thisptr_ADJ->IsConnected()) + { + CNetChan* const pChan = thisptr_ADJ->m_NetChannel; + + pChan->SetRemoteFramerate(msg->m_NetTick.m_flHostFrameTime, msg->m_NetTick.m_flHostFrameTimeStdDeviation); + pChan->SetRemoteCPUStatistics(msg->m_NetTick.m_nServerCPU); + } return true; } } -void VClientState::Attach() const +//------------------------------------------------------------------------------ +// Purpose: processes string commands sent from server +// Input : *thisptr - +// *msg - +// Output : true on success, false otherwise +//------------------------------------------------------------------------------ +bool CClientState::_ProcessStringCmd(CClientState* thisptr, NET_StringCmd* msg) { - DetourAttach(&CClientState__ConnectionClosing, &CClientState::VConnectionClosing); - DetourAttach(&CClientState__ProcessServerTick, &CClientState::VProcessServerTick); + CClientState* const thisptr_ADJ = thisptr->GetShiftedBasePointer(); + + if (thisptr_ADJ->m_bRestrictServerCommands +#ifndef CLIENT_DLL + && !g_pServer->IsActive() +#endif // !CLIENT_DLL + ) + { + CCommand args; + args.Tokenize(msg->cmd, cmd_source_t::kCommandSrcInvalid); + + if (args.ArgC() > 0) + { + if (!Cbuf_AddTextWithMarkers(msg->cmd, + eCmdExecutionMarker_Enable_FCVAR_SERVER_CAN_EXECUTE, + eCmdExecutionMarker_Disable_FCVAR_SERVER_CAN_EXECUTE)) + { + DevWarning(eDLL_T::CLIENT, "%s: No room for %i execution markers; command \"%s\" ignored\n", + __FUNCTION__, 2, msg->cmd); + } + + return true; + } + } + else + { + Cbuf_AddText(Cbuf_GetCurrentPlayer(), msg->cmd, cmd_source_t::kCommandSrcCode); + } + + return true; } -void VClientState::Detach() const +//------------------------------------------------------------------------------ +// Purpose: create's string tables from string table data sent from server +// Input : *thisptr - +// *msg - +// Output : true on success, false otherwise +//------------------------------------------------------------------------------ +bool CClientState::_ProcessCreateStringTable(CClientState* thisptr, SVC_CreateStringTable* msg) { - DetourDetach(&CClientState__ConnectionClosing, &CClientState::VConnectionClosing); - DetourDetach(&CClientState__ProcessServerTick, &CClientState::VProcessServerTick); + CClientState* const cl = thisptr->GetShiftedBasePointer(); + + if (!cl->IsConnected()) + return false; + + CNetworkStringTableContainer* const container = cl->m_StringTableContainer; + + // Must have a string table container at this point! + if (!container) + { + Assert(0); + + COM_ExplainDisconnection(true, "String table container missing.\n"); + v_Host_Disconnect(true); + + return false; + } + + container->AllowCreation(true); + const ssize_t startbit = msg->m_DataIn.GetNumBitsRead(); + + CNetworkStringTable* const table = (CNetworkStringTable*)container->CreateStringTable(false, msg->m_szTableName, + msg->m_nMaxEntries, msg->m_nUserDataSize, msg->m_nUserDataSizeBits, msg->m_nDictFlags); + + table->SetTick(cl->GetServerTickCount()); + CClientState__HookClientStringTable(cl, msg->m_szTableName); + + if (msg->m_bDataCompressed) + { + // TODO[ AMOS ]: check sizes before proceeding to decode + // the string tables + unsigned int msgUncompressedSize = msg->m_DataIn.ReadLong(); + unsigned int msgCompressedSize = msg->m_DataIn.ReadLong(); + + size_t uncompressedSize = msgUncompressedSize; + size_t compressedSize = msgCompressedSize; + + bool bSuccess = false; + + // TODO[ AMOS ]: this could do better. The engine does UINT_MAX-3 + // which doesn't look very great. Clamp to more reasonable values + // than UINT_MAX-3 or UINT_MAX/2? The largest string tables sent + // are settings layout string tables which are roughly 256KiB + // compressed with LZSS. perhaps clamp this to something like 16MiB? + if (msg->m_DataIn.TotalBytesAvailable() > 0 && + msgCompressedSize <= (unsigned int)msg->m_DataIn.TotalBytesAvailable() && + msgCompressedSize < UINT_MAX / 2 && msgUncompressedSize < UINT_MAX / 2) + { + // allocate buffer for uncompressed data, align to 4 bytes boundary + uint8_t* const uncompressedBuffer = new uint8_t[PAD_NUMBER(msgUncompressedSize, 4)]; + uint8_t* const compressedBuffer = new uint8_t[PAD_NUMBER(msgCompressedSize, 4)]; + + msg->m_DataIn.ReadBytes(compressedBuffer, msgCompressedSize); + + // uncompress data + bSuccess = NET_BufferToBufferDecompress(compressedBuffer, compressedSize, uncompressedBuffer, uncompressedSize); + bSuccess &= (uncompressedSize == msgUncompressedSize); + + if (bSuccess) + { + bf_read data(uncompressedBuffer, (int)uncompressedSize); + table->ParseUpdate(data, msg->m_nNumEntries); + } + + delete[] uncompressedBuffer; + delete[] compressedBuffer; + } + + if (!bSuccess) + { + Assert(false); + DevWarning(eDLL_T::CLIENT, "%s: Received malformed string table message!\n", __FUNCTION__); + } + } + else + { + table->ParseUpdate(msg->m_DataIn, msg->m_nNumEntries); + } + + container->AllowCreation(false); + const ssize_t endbit = msg->m_DataIn.GetNumBitsRead(); + + return (endbit - startbit) == msg->m_nLength; +} + +static ConVar cl_onlineAuthEnable("cl_onlineAuthEnable", "1", FCVAR_RELEASE, "Enables the client-side online authentication system"); + +static ConVar cl_onlineAuthToken("cl_onlineAuthToken", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "The client's online authentication token"); +static ConVar cl_onlineAuthTokenSignature1("cl_onlineAuthTokenSignature1", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "The client's online authentication token signature", false, 0.f, false, 0.f, "Primary"); +static ConVar cl_onlineAuthTokenSignature2("cl_onlineAuthTokenSignature2", "", FCVAR_HIDDEN | FCVAR_USERINFO | FCVAR_DONTRECORD | FCVAR_SERVER_CANNOT_QUERY | FCVAR_PLATFORM_SYSTEM, "The client's online authentication token signature", false, 0.f, false, 0.f, "Secondary"); + +//------------------------------------------------------------------------------ +// Purpose: get authentication token for current connection context +// Input : *connectParams - +// *reasonBuf - +// reasonBufLen - +// Output : true on success, false otherwise +//------------------------------------------------------------------------------ +bool CClientState::Authenticate(connectparams_t* connectParams, char* const reasonBuf, const size_t reasonBufLen) const +{ +#define FORMAT_ERROR_REASON(fmt, ...) V_snprintf(reasonBuf, reasonBufLen, fmt, ##__VA_ARGS__); + + string msToken; // token returned by the masterserver authorising the client to play online + string message; // message returned by the masterserver about the result of the auth + + // verify that the client is not lying about their account identity + // code is immediately discarded upon verification + + const bool ret = g_MasterServer.AuthForConnection(*g_NucleusID, connectParams->netAdr, g_OriginAuthCode, msToken, message); + if (!ret) + { + FORMAT_ERROR_REASON("%s", message.c_str()); + return false; + } + + // get full token + const char* token = msToken.c_str(); + + // get a pointer to the delimiter that begins the token's signature + const char* tokenSignatureDelim = strrchr(token, '.'); + + if (!tokenSignatureDelim) + { + FORMAT_ERROR_REASON("Invalid token returned by MS"); + return false; + } + + // replace the delimiter with a null char so the first cvar only takes the header and payload data + *(char*)tokenSignatureDelim = '\0'; + const size_t sigLength = strlen(tokenSignatureDelim) - 1; + + cl_onlineAuthToken.SetValue(token); + + if (sigLength > 0) + { + // get a pointer to the first part of the token signature to store in cl_onlineAuthTokenSignature1 + const char* tokenSignaturePart1 = tokenSignatureDelim + 1; + + cl_onlineAuthTokenSignature1.SetValue(tokenSignaturePart1); + + if (sigLength > 255) + { + // get a pointer to the rest of the token signature to store in cl_onlineAuthTokenSignature2 + const char* tokenSignaturePart2 = tokenSignaturePart1 + 255; + + cl_onlineAuthTokenSignature2.SetValue(tokenSignaturePart2); + } + } + + return true; +#undef REJECT_CONNECTION +} + +bool IsLocalHost(connectparams_t* connectParams) +{ + return (strstr(connectParams->netAdr, "localhost") || strstr(connectParams->netAdr, "127.0.0.1")); +} + +void CClientState::VConnect(CClientState* thisptr, connectparams_t* connectParams) +{ + if (cl_onlineAuthEnable.GetBool() && !IsLocalHost(connectParams)) + { + char authFailReason[512]; + + if (!thisptr->Authenticate(connectParams, authFailReason, sizeof(authFailReason))) + { + COM_ExplainDisconnection(true, "Failed to authenticate for online play: %s", authFailReason); + return; + } + } + + CClientState__Connect(thisptr, connectParams); +} + +void VClientState::Detour(const bool bAttach) const +{ + DetourSetup(&CClientState__ConnectionClosing, &CClientState::VConnectionClosing, bAttach); + DetourSetup(&CClientState__ProcessStringCmd, &CClientState::_ProcessStringCmd, bAttach); + DetourSetup(&CClientState__ProcessServerTick, &CClientState::VProcessServerTick, bAttach); + DetourSetup(&CClientState__ProcessCreateStringTable, &CClientState::_ProcessCreateStringTable, bAttach); + DetourSetup(&CClientState__Connect, &CClientState::VConnect, bAttach); } ///////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/client/clientstate.h b/r5dev/engine/client/clientstate.h index 7af0efbc..106272ac 100644 --- a/r5dev/engine/client/clientstate.h +++ b/r5dev/engine/client/clientstate.h @@ -5,12 +5,23 @@ #include "public/inetmsghandler.h" #include "public/isnapshotmgr.h" #include "engine/net_chan.h" +#include "engine/networkstringtable.h" #include "engine/debugoverlay.h" #include "engine/clockdriftmgr.h" #include "engine/framesnapshot.h" #include "engine/packed_entity.h" #include "datablock_receiver.h" +struct connectparams_t +{ + const char* netAdr; + const char* netKey; + int unkReconnect; + int unk; + bool challengeRequest; + bool asSpectator_MAYBE; +}; + class CClientSnapshotManager : public IClientSnapshotManager { public: @@ -26,7 +37,11 @@ class CClientState : CS_INetChannelHandler, IConnectionlessPacketHandler, IServe friend class ClientDataBlockReceiver; public: // Hook statics. static void VConnectionClosing(CClientState* thisptr, const char* szReason); + static bool _ProcessStringCmd(CClientState* thisptr, NET_StringCmd* msg); static bool VProcessServerTick(CClientState* thisptr, SVC_ServerTick* msg); + static bool _ProcessCreateStringTable(CClientState* thisptr, SVC_CreateStringTable* msg); + static void VConnect(CClientState* thisptr, connectparams_t* connectParams); + public: bool IsPaused() const; @@ -46,6 +61,21 @@ public: float GetFrameTime(void) const; + bool Authenticate(connectparams_t* connectParams, char* const reasonBuf, const size_t reasonBufLen) const; + +protected: + FORCEINLINE CClientState* GetShiftedBasePointer(void) + { + // NOTE: you must check in the disassembler if the CClientState method + // you detour is shifting the 'this' pointer with 16 bytes forward, if + // so, you need to call this and use this pointer instead! The shifting + // happens as part of a compiler optimization that truncated the vtable + // pointers off so the 'this' pointer points directly to the class data + char* const pShifted = reinterpret_cast(this) - 0x10; + return reinterpret_cast(pShifted); + } + +public: int m_Socket; int _padding_maybe; CNetChan* m_NetChannel; @@ -84,9 +114,9 @@ public: int m_nPlayerSlot; char m_szLevelFileName[64]; char m_szLevelBaseName[64]; - char field_1F0[64]; - char field_230[64]; - _BYTE m_szServerAddresString[128]; + char m_szLastLevelBaseName[64]; + char m_szSkyBoxBaseName[64]; + char m_szServerAddresString[128]; int m_bInMpLobbyMenu; int m_nTeam; _DWORD m_nMaxClients; @@ -97,22 +127,19 @@ public: _BYTE m_bSignonChallengeReceived; _DWORD challenge; netadr_t challengeAddr; - _BYTE byte33C; + bool m_bUseLocalSendTableFile; _QWORD m_pServerClasses; int m_nServerClasses; int m_nServerClassBits; - __int64 m_StringTableContainer; + CNetworkStringTableContainer* m_StringTableContainer; char m_PersistenceData[98304]; -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - char pads0[8]; -#endif _BYTE m_bPersistenceBaselineRecvd; int m_nPersistenceBaselineEntries; int field_18350; bool m_bRestrictServerCommands; bool m_bRestrictClientCommands; char buffer_0x400[1024]; - ClientDataBlockReceiver blockReceiver; + ClientDataBlockReceiver m_DataBlockReceiver; char client_requested_disconnect; char error_message[512]; _BYTE gap18CA1[3]; @@ -148,9 +175,7 @@ public: __int64 qword18D20; int dword18D28; int dword18D2C; - float field_18D30; - float m_flUnk1; - float m_flUnk2; + Vector3D field_18D30; int dword18D3C; int dword18D40; char gap18D44[4]; @@ -160,7 +185,6 @@ public: int dword18D60; char gap18D64[4]; __int64 qword18D68; - char gap18D70[8]; char buffer_47128[47128]; char entitlements_bitfield[16]; __int64 maybe_some_ll_stuff; @@ -176,9 +200,7 @@ public: __int64 qword245F0; int dword245F8; char gap245FC[1024]; - int dword249EC;//249EC - int dword249F0; - char gap24A04[4]; + int dword249EC; __int64 m_pModelPrecacheTable; __int64 qword24A10; __int64 m_pInstanceBaselineTable; @@ -189,11 +211,7 @@ public: char byte34A38; char field_34A39[7]; }; -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -static_assert(sizeof(CClientState) == 0x34A38); -#else -static_assert(sizeof(CClientState) == 0x34A30); -#endif +static_assert(sizeof(CClientState) == 0x34A20); #ifndef DEDICATED extern CClientState* g_pClientState; @@ -201,48 +219,42 @@ extern CClientState** g_pClientState_Shifted; // Shifted by 0x10 forward! #endif // DEDICATED /* ==== CCLIENTSTATE ==================================================================================================================================================== */ -inline CMemory p_CClientState__RunFrame; inline void(*CClientState__RunFrame)(CClientState* thisptr); - -inline CMemory p_CClientState__Disconnect; +inline void(*CClientState__Connect)(CClientState* thisptr, connectparams_t* connectParams); inline void(*CClientState__Disconnect)(CClientState* thisptr, bool bSendTrackingContext); - -inline CMemory p_CClientState__ConnectionClosing; inline void(*CClientState__ConnectionClosing)(CClientState* thisptr, const char* szReason); +inline bool(*CClientState__HookClientStringTable)(CClientState* thisptr, const char* tableName); -inline CMemory p_CClientState__ProcessServerTick; +inline bool(*CClientState__ProcessStringCmd)(CClientState* thisptr, NET_StringCmd* msg); inline bool(*CClientState__ProcessServerTick)(CClientState* thisptr, SVC_ServerTick* msg); +inline bool(*CClientState__ProcessCreateStringTable)(CClientState* thisptr, SVC_CreateStringTable* msg); /////////////////////////////////////////////////////////////////////////////// class VClientState : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CClientState::RunFrame", p_CClientState__RunFrame.GetPtr()); - LogFunAdr("CClientState::Disconnect", p_CClientState__Disconnect.GetPtr()); - LogFunAdr("CClientState::ConnectionClosing", p_CClientState__ConnectionClosing.GetPtr()); - LogFunAdr("CClientState::ProcessServerTick", p_CClientState__ProcessServerTick.GetPtr()); - LogVarAdr("g_ClientState", reinterpret_cast(g_pClientState)); - LogVarAdr("g_ClientState_Shifted", reinterpret_cast(g_pClientState_Shifted)); + LogFunAdr("CClientState::RunFrame", CClientState__RunFrame); + LogFunAdr("CClientState::Connect", CClientState__Connect); + LogFunAdr("CClientState::Disconnect", CClientState__Disconnect); + LogFunAdr("CClientState::ConnectionClosing", CClientState__ConnectionClosing); + LogFunAdr("CClientState::HookClientStringTable", CClientState__HookClientStringTable); + LogFunAdr("CClientState::ProcessStringCmd", CClientState__ProcessStringCmd); + LogFunAdr("CClientState::ProcessServerTick", CClientState__ProcessServerTick); + LogFunAdr("CClientState::ProcessCreateStringTable", CClientState__ProcessCreateStringTable); + LogVarAdr("g_ClientState", g_pClientState); + LogVarAdr("g_ClientState_Shifted", g_pClientState_Shifted); } virtual void GetFun(void) const { - -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CClientState__RunFrame = g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 57 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ??"); - p_CClientState__Disconnect = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 57 41 56 48 83 EC 30 0F B6 EA"); - p_CClientState__ConnectionClosing = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B DA 7E 6E"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CClientState__RunFrame = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ?? 48 8B D9 7D 0B"); - p_CClientState__Disconnect = g_GameDll.FindPatternSIMD("40 56 57 41 54 41 55 41 57 48 83 EC 30 44 0F B6 FA"); - p_CClientState__ConnectionClosing = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B DA 0F 8E ?? ?? ?? ??"); -#endif - p_CClientState__ProcessServerTick = g_GameDll.FindPatternSIMD("40 57 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B F9 7C 66"); - - CClientState__RunFrame = p_CClientState__RunFrame.RCast(); - CClientState__Disconnect = p_CClientState__Disconnect.RCast(); - CClientState__ConnectionClosing = p_CClientState__ConnectionClosing.RCast(); - CClientState__ProcessServerTick = p_CClientState__ProcessServerTick.RCast(); + g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ?? 48 8B D9 7D 0B").GetPtr(CClientState__RunFrame); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 48 8B 32").GetPtr(CClientState__Connect); + g_GameDll.FindPatternSIMD("40 56 57 41 54 41 55 41 57 48 83 EC 30 44 0F B6 FA").GetPtr(CClientState__Disconnect); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B DA 0F 8E ?? ?? ?? ??").GetPtr(CClientState__ConnectionClosing); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B D9 48 8B FA 48 8B 89 ?? ?? ?? ?? 48 85 C9 0F 84 ?? ?? ?? ??").GetPtr(CClientState__HookClientStringTable); + g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 80 B9 ?? ?? ?? ?? ?? 48 8B DA").GetPtr(CClientState__ProcessStringCmd); + g_GameDll.FindPatternSIMD("40 57 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B F9 7C 66").GetPtr(CClientState__ProcessServerTick); + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 53 56 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ??").GetPtr(CClientState__ProcessCreateStringTable); } virtual void GetVar(void) const { @@ -250,7 +262,6 @@ class VClientState : public IDetour g_pClientState_Shifted = reinterpret_cast(reinterpret_cast(g_pClientState)+1); // Shift by 8 bytes. } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/client/datablock_receiver.cpp b/r5dev/engine/client/datablock_receiver.cpp index c3917ee7..d8ae7e12 100644 --- a/r5dev/engine/client/datablock_receiver.cpp +++ b/r5dev/engine/client/datablock_receiver.cpp @@ -1,24 +1,162 @@ //===========================================================================// // -// Purpose: client side datablock receiver +// Purpose: client side data block receiver // //===========================================================================// #include "engine/client/clientstate.h" #include "datablock_receiver.h" - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ClientDataBlockReceiver::~ClientDataBlockReceiver() -{ - v_ClientDataBlockReceiver__Destructor(this); -} +#include "common/proto_oob.h" +#include "engine/common.h" +#include "engine/host_cmd.h" //----------------------------------------------------------------------------- // Purpose: send an ack back to the server to let them know -// we received the datablock +// we received the data block //----------------------------------------------------------------------------- void ClientDataBlockReceiver::AcknowledgeTransmission() { - v_ClientDataBlockReceiver__AcknowledgeTransmission(this); + const CClientState* const cl = m_pClientState; + + if (!cl) + { + Assert(0, "ClientDataBlockReceiver::AcknowledgeTransmission() called without a valid client handle!"); + return; + } + + const CNetChan* const chan = cl->m_NetChannel; + + if (!chan) + { + Assert(0, "ClientDataBlockReceiver::AcknowledgeTransmission() called without a net channel!"); + return; + } + + char dataBuf[DATABLOCK_FRAGMENT_PACKET_SIZE]; + bf_write buf(&dataBuf, sizeof(dataBuf)); + + buf.WriteLong(CONNECTIONLESS_HEADER); + buf.WriteByte(C2S_DATABLOCK_ACK); + + buf.WriteShort(m_TransferId); + buf.WriteShort(m_nTransferNr); + + for (int i = m_nTotalBlocks; (i--) > 0;) + { + if (m_BlockStatus[i]) + { + // ack the last blockNr we recv'd and processed + buf.WriteShort(i); + break; + } + } + + // send the data block ack packet + v_NET_SendPacket(NULL, + chan->GetSocket(), + chan->GetRemoteAddress(), + buf.GetData(), + buf.GetNumBytesWritten(), + NULL, false, NULL, true); +} + +//----------------------------------------------------------------------------- +// Purpose: process the recv'd data block and reconstruct the fragmented data +//----------------------------------------------------------------------------- +bool ClientDataBlockReceiver::ProcessDataBlock(const double startTime, const short transferId, const int transferSize, + const short transferNr, const short currentBlockId, const void* const blockBuffer, const int blockBufferBytes) +{ + // should we process a new transfer? + if (transferNr != m_nTransferNr) + ResetBlockReceiver(transferNr); + + m_bInitialized = true; + + // make sure we always receive fragments in order + if (transferId != m_TransferId || m_bCompletedRecv) + return false; + + // initialize the receiver if this is the firs fragment + if (!m_bStartedRecv) + StartBlockReceiver(transferSize, startTime); + + // received more blocks than the total # expected? + if (currentBlockId >= m_nTotalBlocks) + return false; + + // check if we have already copied the data block + if (!m_BlockStatus[currentBlockId]) + { + const int scratchBufferOffset = currentBlockId * MAX_DATABLOCK_FRAGMENT_SIZE; + + if (blockBufferBytes + scratchBufferOffset <= m_nTransferSize) + memcpy(m_pScratchBuffer + scratchBufferOffset + (sizeof(ClientDataBlockHeader_s) -1), blockBuffer, blockBufferBytes); + + ++m_nBlockAckTick; + m_BlockStatus[currentBlockId] = true; + } + + // check if we have recv'd enough fragments to decode the data + if (m_nBlockAckTick != m_nTotalBlocks) + return true; + + AcknowledgeTransmission(); + m_bCompletedRecv = true; + + const ClientDataBlockHeader_s* const pHeader = reinterpret_cast(m_pScratchBuffer); + + if (pHeader->isCompressed) + { + // NOTE: the engine's implementation of this function does NOT free + // this buffer when a malformed/corrupt LZ4 packet is sent to the + // receiver; wrapped buffer in unique_ptr to make sure it never leaks! + std::unique_ptr encodedDataBuf(new char[SNAPSHOT_SCRATCH_BUFFER_SIZE]); + + char* const pEncodedDataBuf = encodedDataBuf.get(); + char* const dataLocation = m_pScratchBuffer + sizeof(ClientDataBlockHeader_s); + + // copy the encoded data in the newly allocated buffer so we can decode back + // into the data block buffer we copied the encoded data from + const int compressedSize = m_nTransferSize -1; + + memcpy(pEncodedDataBuf, dataLocation, compressedSize); + const int numDecode = LZ4_decompress_safe(pEncodedDataBuf, dataLocation, compressedSize, SNAPSHOT_SCRATCH_BUFFER_SIZE); + + if (numDecode < 0) + { + Assert(0); + + COM_ExplainDisconnection(true, "LZ4 error decompressing data block from server.\n"); + v_Host_Disconnect(true); + + return false; + } + + m_nTransferSize = numDecode; + } + else + { + // truncate the byte that determines whether the data was compressed + m_nTransferSize--; + } + + return true; +} + +//----------------------------------------------------------------------------- +// NOTE: detoured for 2 reasons: +// 1: when a corrupt or malformed compress packet is sent, the code never freed +// the temporary copy buffer it made to decode the data into the scratch buf +// 2: exploring other compression algorithms for potential optimizations +//----------------------------------------------------------------------------- +static bool HK_ProcessDataBlock(ClientDataBlockReceiver* receiver, const double startTime, + const short transferId, const int transferSize, const short transferNr, + const short currentBlockId, const void* const blockBuffer, const int blockBufferBytes) +{ + return receiver->ProcessDataBlock(startTime, transferId, transferSize, transferNr, + currentBlockId, blockBuffer, blockBufferBytes); +} + +void VClientDataBlockReceiver::Detour(const bool bAttach) const +{ + DetourAttach(&ClientDataBlockReceiver__ProcessDataBlock, HK_ProcessDataBlock); } diff --git a/r5dev/engine/client/datablock_receiver.h b/r5dev/engine/client/datablock_receiver.h index cc6ee455..cd1df700 100644 --- a/r5dev/engine/client/datablock_receiver.h +++ b/r5dev/engine/client/datablock_receiver.h @@ -1,60 +1,55 @@ +//===========================================================================// +// +// Purpose: client side data block receiver +// +//===========================================================================// #ifndef DATABLOCK_RECEIVER_H #define DATABLOCK_RECEIVER_H -#include "idatablock.h" +#include "engine/shared/datablock.h" class CClientState; class ClientDataBlockReceiver : public NetDataBlockReceiver { - friend class CClientState; public: - virtual ~ClientDataBlockReceiver(); virtual void AcknowledgeTransmission() override; -protected: - CClientState* m_pClientState; - bool m_bStartedRecv; - bool m_bCompletedRecv; - bool byte12; - short m_TransferId; - short m_Counter; - bool m_bInitialized; - int m_nTransferSize; - int m_nTotalBlocks; - int m_nBlockAckTick; - double m_flStartTime; - bool m_BlockStatus[DATABLOCK_STATUS_SIZE]; - void* m_pBigBuffer; + bool ProcessDataBlock(const double startTime, const short transferId, const int transferSize, + const short counter, const short currentBlockId, const void* const blockBuffer, const int blockBufferBytes); }; -inline CMemory p_ClientDataBlockReceiver__Destructor; -inline void*(*v_ClientDataBlockReceiver__Destructor)(ClientDataBlockReceiver* thisptr); +struct ClientDataBlockHeader_s +{ + char reserved[3]; // unused in retail + bool isCompressed; +}; -inline CMemory p_ClientDataBlockReceiver__AcknowledgeTransmission; -inline void*(*v_ClientDataBlockReceiver__AcknowledgeTransmission)(ClientDataBlockReceiver* thisptr); +// virtual methods +inline void*(*ClientDataBlockReceiver__AcknowledgeTransmission)(ClientDataBlockReceiver* thisptr); + +// non-virtual methods +inline bool (*ClientDataBlockReceiver__ProcessDataBlock)(ClientDataBlockReceiver* thisptr, const double time, + const short transferId, const int transferSize, const short counter, const short currentBlockId, + const void* const blockBuffer, const int blockBufferBytes); /////////////////////////////////////////////////////////////////////////////// class VClientDataBlockReceiver : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("ClientDataBlockReceiver::~ClientDataBlockReceiver", p_ClientDataBlockReceiver__Destructor.GetPtr()); - LogFunAdr("ClientDataBlockReceiver::AcknowledgeTransmission", p_ClientDataBlockReceiver__AcknowledgeTransmission.GetPtr()); + LogFunAdr("ClientDataBlockReceiver::AcknowledgeTransmission", ClientDataBlockReceiver__AcknowledgeTransmission); + LogFunAdr("ClientDataBlockReceiver::ProcessDataBlock", ClientDataBlockReceiver__ProcessDataBlock); } virtual void GetFun(void) const { - p_ClientDataBlockReceiver__Destructor = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74" - " 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24" - " ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8D 05 ?? ?? ?? ?? C6 41 12 00"); - v_ClientDataBlockReceiver__Destructor = p_ClientDataBlockReceiver__Destructor.RCast(); + g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 4C 8B 51 08").GetPtr(ClientDataBlockReceiver__AcknowledgeTransmission); - p_ClientDataBlockReceiver__AcknowledgeTransmission = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 4C 8B 51 08"); - v_ClientDataBlockReceiver__AcknowledgeTransmission = p_ClientDataBlockReceiver__AcknowledgeTransmission.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 30 0F B7 44 24 ??") + .GetPtr(ClientDataBlockReceiver__ProcessDataBlock); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/client/vengineclient_impl.cpp b/r5dev/engine/client/vengineclient_impl.cpp index 21a8e397..efd64053 100644 --- a/r5dev/engine/client/vengineclient_impl.cpp +++ b/r5dev/engine/client/vengineclient_impl.cpp @@ -5,6 +5,7 @@ //=============================================================================// #include "core/stdafx.h" +#include "engine/cmd.h" #include "clientstate.h" #include "vengineclient_impl.h" @@ -55,10 +56,43 @@ bool CEngineClient::GetRestrictClientCommands() const //--------------------------------------------------------------------------------- int CEngineClient::GetLocalPlayer() { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - const static int index = 35; -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) const static int index = 36; -#endif return CallVFunc(index, this); -} \ No newline at end of file +} + +//--------------------------------------------------------------------------------- +// Purpose: execute client command +// Input : *thisptr - +// *szCmdString - +// Output : +//--------------------------------------------------------------------------------- +void CEngineClient::_ClientCmd(CEngineClient* thisptr, const char* const szCmdString) +{ + const bool restrictClientCommands = g_pClientState->m_bRestrictClientCommands; + const int numMarkers = 2; + + if (restrictClientCommands && !Cbuf_HasRoomForExecutionMarkers(numMarkers)) + { + DevWarning(eDLL_T::CLIENT, "%s: No room for %i execution markers; command \"%s\" ignored\n", + __FUNCTION__, numMarkers, szCmdString); + return; + } + + if (restrictClientCommands) + { + Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), eCmdExecutionMarker_Enable_FCVAR_CLIENTCMD_CAN_EXECUTE); + } + + Cbuf_AddText(Cbuf_GetCurrentPlayer(), szCmdString, cmd_source_t::kCommandSrcCode); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "\n", cmd_source_t::kCommandSrcCode); + + if (restrictClientCommands) + { + Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), eCmdExecutionMarker_Disable_FCVAR_CLIENTCMD_CAN_EXECUTE); + } +} + +void HVEngineClient::Detour(const bool bAttach) const +{ + DetourSetup(&CEngineClient__ClientCmd, &CEngineClient::_ClientCmd, bAttach); +} diff --git a/r5dev/engine/client/vengineclient_impl.h b/r5dev/engine/client/vengineclient_impl.h index 4872861e..d0a2091c 100644 --- a/r5dev/engine/client/vengineclient_impl.h +++ b/r5dev/engine/client/vengineclient_impl.h @@ -8,10 +8,15 @@ public: void SetRestrictClientCommands(bool bRestrict); bool GetRestrictClientCommands() const; int GetLocalPlayer(); // Local player index. + + // Hook statics: + static void _ClientCmd(CEngineClient* thisptr, const char* const szCmdString); }; /* ==== CVENGINECLIENT ================================================================================================================================================== */ /////////////////////////////////////////////////////////////////////////////// +inline void(*CEngineClient__ClientCmd)(CEngineClient* thisptr, const char* const szCmdString); + inline CMemory g_pEngineClientVFTable = nullptr; inline CEngineClient* g_pEngineClient = nullptr; @@ -20,16 +25,19 @@ class HVEngineClient : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CEngineClient::`vftable'", g_pEngineClientVFTable.GetPtr()); + LogConAdr("CEngineClient::`vftable'", (void*)g_pEngineClientVFTable.GetPtr()); + LogFunAdr("CEngineClient::ClientCmd", CEngineClient__ClientCmd); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 80 3D ?? ?? ?? ?? ?? 48 8B DA 74 0C").GetPtr(CEngineClient__ClientCmd); } - virtual void GetFun(void) const { } virtual void GetVar(void) const { } virtual void GetCon(void) const { g_pEngineClientVFTable = g_GameDll.GetVirtualMethodTable(".?AVCEngineClient@@"); g_pEngineClient = g_pEngineClientVFTable.RCast(); } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/cmd.cpp b/r5dev/engine/cmd.cpp index 527343c6..6b78bc74 100644 --- a/r5dev/engine/cmd.cpp +++ b/r5dev/engine/cmd.cpp @@ -1,13 +1,69 @@ #include "core/stdafx.h" #include "tier1/cmd.h" #include "tier1/cvar.h" +#include "tier1/commandbuffer.h" #include "engine/cmd.h" +CCommandBuffer** s_pCommandBuffer = nullptr; // array size = ECommandTarget_t::CBUF_COUNT. +LPCRITICAL_SECTION s_pCommandBufferMutex = nullptr; + +//============================================================================= +// List of execution markers +//============================================================================= +CUtlVector* g_pExecutionMarkers = nullptr; + +//----------------------------------------------------------------------------- +// Purpose: checks if there's room left for execution markers +// Input : cExecutionMarkers - +// Output : true if there's room for execution markers, false otherwise +//----------------------------------------------------------------------------- +bool Cbuf_HasRoomForExecutionMarkers(const int cExecutionMarkers) +{ + return (g_pExecutionMarkers->Count() + cExecutionMarkers) < MAX_EXECUTION_MARKERS; +} + +//----------------------------------------------------------------------------- +// Purpose: adds command text at the end of the command buffer with execution markers +// Input : *pText - +// markerLeft - +// markerRight - +// Output : true if there's room for execution markers, false otherwise +//----------------------------------------------------------------------------- +bool Cbuf_AddTextWithMarkers(const char* const pText, const ECmdExecutionMarker markerLeft, const ECmdExecutionMarker markerRight) +{ + if (Cbuf_HasRoomForExecutionMarkers(2)) + { + Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), markerLeft); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), pText, cmd_source_t::kCommandSrcCode); + Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), markerRight); + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: adds command text at the end of the buffer +//----------------------------------------------------------------------------- +//void Cbuf_AddText(ECommandTarget_t eTarget, const char* pText, int nTickDelay) +//{ +// LOCK_COMMAND_BUFFER(); +// if (!s_pCommandBuffer[(int)eTarget]->AddText(pText, nTickDelay, cmd_source_t::kCommandSrcInvalid)) +// { +// Error(eDLL_T::ENGINE, NO_ERROR, "%s: buffer overflow\n", __FUNCTION__); +// } +//} + //----------------------------------------------------------------------------- // Purpose: Sends the entire command line over to the server // Input : *args - // Output : true on success, false otherwise //----------------------------------------------------------------------------- +#ifndef DEDICATED +ConVar cl_quota_stringCmdsPerSecond("cl_quota_stringCmdsPerSecond", "16", FCVAR_RELEASE, "How many string commands per second user is allowed to submit, 0 to allow all submissions.", true, 0.f, false, 0.f); +#endif // DEDICATED + bool Cmd_ForwardToServer(const CCommand* args) { #ifndef DEDICATED @@ -19,8 +75,8 @@ bool Cmd_ForwardToServer(const CCommand* args) if (args->ArgC() == 0) return false; - double flStartTime = Plat_FloatTime(); - int nCmdQuotaLimit = cl_quota_stringCmdsPerSecond->GetInt(); + const double flStartTime = Plat_FloatTime(); + const int nCmdQuotaLimit = cl_quota_stringCmdsPerSecond.GetInt(); const char* pszCmdString = nullptr; // Special case: "cmd whatever args..." is forwarded as "whatever args..."; @@ -49,16 +105,13 @@ bool Cmd_ForwardToServer(const CCommand* args) } return v_Cmd_ForwardToServer(args); #else // !DEDICATED + Assert(0); return false; // Client only. #endif // DEDICATED } /////////////////////////////////////////////////////////////////////////////// -void VCmd::Attach() const +void VCmd::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_Cmd_ForwardToServer, &Cmd_ForwardToServer); -} -void VCmd::Detach() const -{ - DetourDetach((LPVOID*)&v_Cmd_ForwardToServer, &Cmd_ForwardToServer); + DetourSetup(&v_Cmd_ForwardToServer, &Cmd_ForwardToServer, bAttach); } diff --git a/r5dev/engine/cmd.h b/r5dev/engine/cmd.h index 0840ebc1..da1caa37 100644 --- a/r5dev/engine/cmd.h +++ b/r5dev/engine/cmd.h @@ -1,5 +1,17 @@ #ifndef CMD_H #define CMD_H +#include "tier1/commandbuffer.h" + +#define MAX_EXECUTION_MARKERS 2048 + +typedef enum +{ + eCmdExecutionMarker_Enable_FCVAR_SERVER_CAN_EXECUTE = 'a', + eCmdExecutionMarker_Disable_FCVAR_SERVER_CAN_EXECUTE = 'b', + + eCmdExecutionMarker_Enable_FCVAR_CLIENTCMD_CAN_EXECUTE = 'c', + eCmdExecutionMarker_Disable_FCVAR_CLIENTCMD_CAN_EXECUTE = 'd' +} ECmdExecutionMarker; //----------------------------------------------------------------------------- // Purpose: Returns current player calling this function @@ -11,46 +23,53 @@ FORCEINLINE ECommandTarget_t Cbuf_GetCurrentPlayer(void) return ECommandTarget_t::CBUF_FIRST_PLAYER; } +extern bool Cbuf_HasRoomForExecutionMarkers(const int cExecutionMarkers); +extern bool Cbuf_AddTextWithMarkers(const char* text, const ECmdExecutionMarker markerLeft, const ECmdExecutionMarker markerRight); + /* ==== COMMAND_BUFFER ================================================================================================================================================== */ -inline CMemory p_Cbuf_AddText; inline void(*Cbuf_AddText)(ECommandTarget_t eTarget, const char* pText, cmd_source_t cmdSource); - -inline CMemory p_Cbuf_Execute; +inline void(*Cbuf_AddExecutionMarker)(ECommandTarget_t target, ECmdExecutionMarker marker); inline void(*Cbuf_Execute)(void); - -inline CMemory p_Cmd_Dispatch; inline void(*v_Cmd_Dispatch)(ECommandTarget_t eTarget, const ConCommandBase* pCmdBase, const CCommand* pCommand, bool bCallBackupCallback); - -inline CMemory p_Cmd_ForwardToServer; inline bool(*v_Cmd_ForwardToServer)(const CCommand* pCommand); +extern CCommandBuffer** s_pCommandBuffer; +extern LPCRITICAL_SECTION s_pCommandBufferMutex; + +extern CUtlVector* g_pExecutionMarkers; + + /////////////////////////////////////////////////////////////////////////////// class VCmd : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Cbuf_AddText", p_Cbuf_AddText.GetPtr()); - LogFunAdr("Cbuf_Execute", p_Cbuf_Execute.GetPtr()); - LogFunAdr("Cmd_Dispatch", p_Cmd_Dispatch.GetPtr()); - LogFunAdr("Cmd_ForwardToServer", p_Cmd_ForwardToServer.GetPtr()); + LogFunAdr("Cbuf_AddText", Cbuf_AddText); + LogFunAdr("Cbuf_AddExecutionMarker", Cbuf_AddExecutionMarker); + LogFunAdr("Cbuf_Execute", Cbuf_Execute); + LogFunAdr("Cmd_Dispatch", v_Cmd_Dispatch); + LogFunAdr("Cmd_ForwardToServer", v_Cmd_ForwardToServer); + LogVarAdr("s_CommandBuffer", s_pCommandBuffer); + LogVarAdr("s_CommandBufferMutex", s_pCommandBufferMutex); + LogVarAdr("g_ExecutionMarkers", g_pExecutionMarkers); } virtual void GetFun(void) const { - p_Cbuf_AddText = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 63 D9 41 8B F8 48 8D 0D ?? ?? ?? ?? 48 8B F2 FF 15 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 41 B9 ?? ?? ?? ??"); - p_Cbuf_Execute = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 FF 15 ?? ?? ?? ??"); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 63 D9 41 8B F8 48 8D 0D ?? ?? ?? ?? 48 8B F2 FF 15 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 41 B9 ?? ?? ?? ??").GetPtr(Cbuf_AddText); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 05 ?? ?? ?? ??").GetPtr(Cbuf_AddExecutionMarker); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 FF 15 ?? ?? ?? ??").GetPtr(Cbuf_Execute); - p_Cmd_Dispatch = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B ?? 0C 49 FF C7").FollowNearCallSelf(); - p_Cmd_ForwardToServer = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 59 04"); - - Cbuf_AddText = p_Cbuf_AddText.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 63 D9 41 8B F8 48 8D 0D ?? ?? ?? ?? 48 8B F2 FF 15 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 41 B9 ?? ?? ?? ??*/ - Cbuf_Execute = p_Cbuf_Execute.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 FF 15 ?? ?? ?? ??*/ - v_Cmd_Dispatch = p_Cmd_Dispatch.RCast(); - v_Cmd_ForwardToServer = p_Cmd_ForwardToServer.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 59 04*/ + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B ?? 0C 49 FF C7").FollowNearCallSelf().GetPtr(v_Cmd_Dispatch); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 59 04").GetPtr(v_Cmd_ForwardToServer); + } + virtual void GetVar(void) const + { + s_pCommandBuffer = CMemory(Cbuf_AddText).FindPattern("48 8D 05").ResolveRelativeAddressSelf(3, 7).RCast(); + s_pCommandBufferMutex = CMemory(Cbuf_AddText).FindPattern("48 8D 0D").ResolveRelativeAddressSelf(3, 7).RCast(); + g_pExecutionMarkers = CMemory(Cbuf_AddExecutionMarker).FindPattern("48 8B 0D").ResolveRelativeAddressSelf(3, 7).RCast*>(); } - virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; #endif // CMD_H diff --git a/r5dev/engine/cmodel_bsp.cpp b/r5dev/engine/cmodel_bsp.cpp index 19562068..07033291 100644 --- a/r5dev/engine/cmodel_bsp.cpp +++ b/r5dev/engine/cmodel_bsp.cpp @@ -8,37 +8,110 @@ #include "core/stdafx.h" #include "tier0/memstd.h" #include "tier0/jobthread.h" +#include "tier1/fmtstr.h" #include "tier2/fileutils.h" #include "engine/sys_dll2.h" #include "engine/host_cmd.h" #include "engine/cmodel_bsp.h" -#include "rtech/rtech_utils.h" -#include "rtech/rtech_game.h" -#include "vpc/keyvalues.h" + +#include "rtech/pak/pakstate.h" +#include "rtech/pak/pakparse.h" +#include "rtech/pak/paktools.h" +#include "rtech/pak/pakstream.h" + +#include "tier1/keyvalues.h" #include "datacache/mdlcache.h" #include "filesystem/filesystem.h" #ifndef DEDICATED #include "client/clientstate.h" #endif // !DEDICATED -vector g_InstalledMaps; -string s_LevelName; +CUtlVector g_InstalledMaps; +CFmtStrN s_CurrentLevelName; -std::regex s_ArchiveRegex{ R"([^_]*_(.*)(.bsp.pak000_dir).*)" }; +static std::regex s_ArchiveRegex{ R"([^_]*_(.*)(.bsp.pak000_dir).*)" }; -bool s_bLevelResourceInitialized = false; -bool s_bBasePaksInitialized = false; -KeyValues* s_pLevelSetKV = nullptr; +static CustomPakData_t s_customPakData; +static KeyValues* s_pLevelSetKV = nullptr; +//----------------------------------------------------------------------------- +// Purpose: load a custom pak and add it to the list +//----------------------------------------------------------------------------- +PakHandle_t CustomPakData_t::LoadAndAddPak(const char* const pakFile) +{ + if (numHandles >= MAX_CUSTOM_PAKS) + { + Error(eDLL_T::ENGINE, NO_ERROR, "Tried to load pak '%s', but already reached the SDK's limit of %d!\n", pakFile, MAX_CUSTOM_PAKS); + return INVALID_PAK_HANDLE; + } + + const PakHandle_t pakId = g_pakLoadApi->LoadAsync(pakFile, AlignedMemAlloc(), 4, 0); + + // failure, don't add and return the invalid handle. + if (pakId == INVALID_PAK_HANDLE) + return pakId; + + handles[numHandles++] = pakId; + return pakId; +} + +//----------------------------------------------------------------------------- +// Purpose: unloads all active custom pak handles +//----------------------------------------------------------------------------- +void CustomPakData_t::UnloadAndRemoveAll() +{ + for (; numHandles-1 >= CustomPakData_t::PAK_TYPE_COUNT; numHandles--) + { + const PakHandle_t pakId = handles[numHandles-1]; + + if (pakId == INVALID_PAK_HANDLE) + { + assert(0); // invalid handles should not be inserted + return; + } + + g_pakLoadApi->UnloadAsync(pakId); + handles[numHandles-1] = INVALID_PAK_HANDLE; + } +} + +//----------------------------------------------------------------------------- +// Purpose: loads the base SDK pak file by type +//----------------------------------------------------------------------------- +PakHandle_t CustomPakData_t::LoadBasePak(const char* const pakFile, const EPakType type) +{ + const PakHandle_t pakId = g_pakLoadApi->LoadAsync(pakFile, AlignedMemAlloc(), 4, 0); + + // the file is most likely missing + assert(pakId != INVALID_PAK_HANDLE); + handles[type] = pakId; + + return pakId; +} + +//----------------------------------------------------------------------------- +// Purpose: unload the SDK base pak file by type +//----------------------------------------------------------------------------- +void CustomPakData_t::UnloadBasePak(const EPakType type) +{ + const PakHandle_t pakId = handles[type]; + + // only unload if it was actually successfully loaded + if (pakId != INVALID_PAK_HANDLE) + { + g_pakLoadApi->UnloadAsync(pakId); + handles[type] = INVALID_PAK_HANDLE; + } +} //----------------------------------------------------------------------------- // Purpose: checks if level has changed // Input : *pszLevelName - // Output : true if level name deviates from previous level //----------------------------------------------------------------------------- -bool Mod_LevelHasChanged(const char* pszLevelName) +bool Mod_LevelHasChanged(const char* const pszLevelName) { - return (s_LevelName.compare(pszLevelName) != 0); + return (V_strcmp(pszLevelName, s_CurrentLevelName.String()) != NULL); } //----------------------------------------------------------------------------- @@ -52,7 +125,7 @@ void Mod_GetAllInstalledMaps() std::cmatch regexMatches; std::lock_guard l(g_InstalledMapsMutex); - g_InstalledMaps.clear(); // Clear current list. + g_InstalledMaps.Purge(); // Clear current list. FOR_EACH_VEC(fileList, i) { @@ -67,326 +140,299 @@ void Mod_GetAllInstalledMaps() if (!regexMatches.empty()) { - if (regexMatches[1].str().compare("frontend") == 0) + const std::sub_match& match = regexMatches[1]; + + if (match.compare("frontend") == 0) continue; // Frontend contains no BSP's. - else if (regexMatches[1].str().compare("mp_common") == 0) + else if (match.compare("mp_common") == 0) { - if (std::find(g_InstalledMaps.begin(), g_InstalledMaps.end(), "mp_lobby") == g_InstalledMaps.end()) - g_InstalledMaps.push_back("mp_lobby"); + if (!g_InstalledMaps.HasElement("mp_lobby")) + g_InstalledMaps.AddToTail("mp_lobby"); + continue; // Common contains mp_lobby. } - - if (std::find(g_InstalledMaps.begin(), g_InstalledMaps.end(), regexMatches[1].str()) == g_InstalledMaps.end()) - g_InstalledMaps.push_back(regexMatches[1].str()); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: gets the queued pak handles -// Input : *a1 - -// *a2 - -// a3 - -// Output : __int64 -//----------------------------------------------------------------------------- -__int64 __fastcall Mod_GetQueuedPakHandle(char* a1, char* a2, __int64 a3) -{ - char v3; // al - signed int v4; // er11 - __int64 v5; // r10 - char* v6; // r9 - signed __int64 v7; // rdx - char v8; // al - char* v10; // r8 - char* v11; // r8 - - v3 = *a2; - v4 = 0; - *a1 = *a2; - v5 = 0i64; - if (v3) - { - v6 = a1; - v7 = a2 - a1; - while (1) - { - ++v5; - ++v6; - if (v5 == a3) - break; - v8 = v6[v7]; - *v6 = v8; - if (!v8) - return v5; - } - *(v6 - 1) = 0; - if (--v5) - { - v10 = &a1[v5 - 1]; - if ((*v10 & 0xC0) == 0x80) + else { - do - ++v4; - while ((v10[-v4] & 0xC0) == 0x80); - } - v11 = &v10[-v4]; - if (v4 != (signed int)((0xE5000000 >> (((unsigned __int8)*v11 >> 3) & 0x1E)) & 3)) - { - *v11 = 0; - v5 -= v4; + const string mapName = match.str(); + if (!g_InstalledMaps.HasElement(mapName.c_str())) + g_InstalledMaps.AddToTail(mapName.c_str()); } } } - return v5; } //----------------------------------------------------------------------------- // Purpose: processes queued pak files //----------------------------------------------------------------------------- -void Mod_ProcessPakQueue() +void Mod_QueuedPakCacheFrame() { - char v0; // bl - char** v1; // r10 - __int64 i; // er9 - char* v3; // rcx - signed __int64 v4; // r8 - int v5; // eax - int v6; // edx - __int64 v7; // eax - __int64 v8; // rbp - __int64 v9; // rsi - char* v10; // rbx - unsigned int v11; // ecx - __int64 v12; // rax - int v13; // edi - char v14; // al - char* v15; // rbx - __int64 v16; // edi - char* v17; // rsi - char* v18; // rax - int v19; // ecx - int v20; // er8 - int v21; // ecx - __int64 v22; // rdx - __int64 v24{}; // rdx - __int64 v25{}; // rcx - - v0 = 0; #ifndef DEDICATED bool bUnconnected = !(*g_pClientState_Shifted)->IsConnected(); #else // !DEDICATED bool bUnconnected = true; // Always true for dedicated. #endif - if (*(float*)&*dword_14B383420 == 1.0 && *qword_167ED7BB8 && bUnconnected) + bool startFromFirst = false; + + if (Pak_StreamingDownloadFinished() && Pak_GetNumStreamableAssets() && bUnconnected) { - *byte_16709DDDF = 0; - v0 = 1; + *g_pPakPrecacheJobFinished = false; + startFromFirst = true; } - else if (*byte_16709DDDF) + else if (*g_pPakPrecacheJobFinished) { return; } - if (FileSystem()->ResetItemCache() && !*dword_1634F445C) + + if (!FileSystem()->ResetItemCache() || *g_pNumPrecacheItemsMTVTF) { - v1 = &*off_141874660; - for (i = 0; i < 5; ++i) + return; + } + + const char** pPakName = &g_commonPakData[0].basePakName; + int i; + + for (i = 0; i < 5; ++i) + { + if (*((_BYTE*)pPakName - 268)) + break; + + const char* pakName = g_commonPakData[i].pakName; + const int64_t v4 = *pPakName - pakName; + + int v5; + int v6; + + do { - if (*((_BYTE*)v1 - 268)) - break; - v3 = (char*)&*unk_141874555 + 280 * i; - v4 = *v1 - v3; - do - { - v5 = (unsigned __int8)v3[v4]; - v6 = (unsigned __int8)*v3 - v5; - if (v6) - break; - ++v3; - } while (v5); + v5 = (unsigned __int8)pakName[v4]; + v6 = (unsigned __int8)*pakName - v5; if (v6) break; - v1 += 35; - } - v7 = 0; - if (!v0) - v7 = i; - v8 = v7; - if (v7 <= 4i64) - { - v9 = 4i64; - v10 = (char*)&*unk_1418749B0; - do - { - if (v10[5]) - { - v11 = *(_DWORD*)v10; - v12 = *(_DWORD*)v10 & 0x1FF; - v10[4] = 1; - if (*((_DWORD*)&*g_pLoadedPakInfo + 46 * v12) == v11) - { - v13 = *((_DWORD*)&*g_pLoadedPakInfo + 46 * v12 + 1); - v14 = v10[4]; - } - else - { - v13 = 14; - v14 = 1; - } - if (!v14 || v13 == 9) - { - // SDK pak files must be unloaded before the engine pak files, - // as we reference assets within engine pak files. - const RPakLoadedInfo_t* pLoadedPakInfo = g_pRTech->GetPakLoadedInfo(*(RPakHandle_t*)v10); - if (pLoadedPakInfo) - { - const char* pszLoadedPakName = pLoadedPakInfo->m_pszFileName; - if (strcmp(pszLoadedPakName, "common_mp.rpak") == 0 || - strcmp(pszLoadedPakName, "common_sp.rpak") == 0 || - strcmp(pszLoadedPakName, "common_pve.rpak") == 0) - { - const RPakLoadedInfo_t* pLoadedSdkPak = g_pRTech->GetPakLoadedInfo("common_sdk.rpak"); + ++pakName; + } while (v5); - if (pLoadedSdkPak) // Only unload if sdk pak file is loaded. - g_pakLoadApi->UnloadPak(pLoadedSdkPak->m_nHandle); + if (v6) + break; - } -#ifndef DEDICATED - else if (strcmp(pszLoadedPakName, "ui_mp.rpak") == 0) - { - const RPakLoadedInfo_t* pLoadedSdkPak = g_pRTech->GetPakLoadedInfo("ui_sdk.rpak"); - - if (pLoadedSdkPak) // Only unload if sdk pak file is loaded. - g_pakLoadApi->UnloadPak(pLoadedSdkPak->m_nHandle); - } -#endif // !DEDICATED - } - - // The old gather props is set if a model couldn't be - // loaded properly. If we unload level assets, we just - // enable the new implementation again and re-evaluate - // on the next level load. If we load a missing/bad - // model again, we toggle the old implementation as - // the helper functions for this implementation have - // been restored in the SDK, and modified to support - // stubbing missing model assets. See the function - // 'CMDLCache::GetErrorModel' for more information. - if (old_gather_props->GetBool()) - old_gather_props->SetValue(false); - - g_pakLoadApi->UnloadPak(*(RPakHandle_t*)v10); - Mod_UnloadPakFile(); // Unload mod pak files. - - if (s_pLevelSetKV) - { - // Delete current level settings if we drop all paks.. - s_pLevelSetKV->DeleteThis(); - s_pLevelSetKV = nullptr; - } - } - if (v13 && (unsigned int)(v13 - 13) > 1) - return; - *((_WORD*)v10 + 2) = 0; - *(_DWORD*)v10 = 0xFFFFFFFF; - } - --v9; - v10 -= 280; - } while (v9 >= v8); - } - *byte_16709DDDF = 1; - v15 = (char*)&*unk_141874550; - v16 = 0; - while (1) - { - v17 = (char*)&*unk_141874550 + 280 * v16 + 5; - v18 = v17; - do - { - v19 = (unsigned __int8)v18[*((_QWORD*)v15 + 34) - (_QWORD)v17]; - v20 = (unsigned __int8)*v18 - v19; - if (v20) - break; - ++v18; - } while (v19); - if (!v20) - goto LABEL_37; - Mod_GetQueuedPakHandle(v17, *((char**)v15 + 34), 260i64); - if (v15[5]) - break; - *(_DWORD*)v15 = 0xFFFFFFFF; - LABEL_40: - ++v16; - v15 += 280; - if (v16 >= 5) - { - if (*byte_16709DDDF) - { - if (*g_pMTVFTaskItem) - { - if (!*(_BYTE*)(*g_pMTVFTaskItem + 4)) - { - if (*qword_167ED7BC0 || WORD2(*g_pPakLoadJobID) != HIWORD(*g_pPakLoadJobID)) - { - if (!JT_AcquireFifoLock(g_pPakFifoLock) - && !(unsigned __int8)sub_14045BAC0((__int64(__fastcall*)(__int64, _DWORD*, __int64, _QWORD*))g_pPakFifoLockWrapper, g_pPakFifoLock, -1i64, 0i64)) - { - sub_14045A1D0((unsigned __int8(__fastcall*)(_QWORD))g_pPakFifoLockWrapper, g_pPakFifoLock, -1i64, 0i64, 0i64, 1); - } - - sub_140441220(v25, v24); - if (ThreadInMainThread()) - { - if (*g_bPakFifoLockAcquired) - { - *g_bPakFifoLockAcquired = 0; - JT_ReleaseFifoLock(g_pPakFifoLock); - } - } - JT_ReleaseFifoLock(g_pPakFifoLock); - } - FileSystem()->ResetItemCacheSize(256); - FileSystem()->PrecacheTaskItem(*g_pMTVFTaskItem); - } - } - } - return; - } - } - if (strcmp(v17, "mp_lobby.rpak") == 0) - s_bBasePaksInitialized = true; - - if (s_bBasePaksInitialized && !s_bLevelResourceInitialized) - { - Mod_PreloadLevelPaks(s_LevelName.c_str()); - s_bLevelResourceInitialized = true; - } - *(_DWORD*)v15 = g_pakLoadApi->LoadAsync(v17, AlignedMemAlloc(), 4, 0); - - if (strcmp(v17, "common_mp.rpak") == 0 || strcmp(v17, "common_sp.rpak") == 0 || strcmp(v17, "common_pve.rpak") == 0) - g_pakLoadApi->LoadAsync("common_sdk.rpak", AlignedMemAlloc(), 4, 0); -#ifndef DEDICATED - if (strcmp(v17, "ui_mp.rpak") == 0) - g_pakLoadApi->LoadAsync("ui_sdk.rpak", AlignedMemAlloc(), 4, 0); -#endif // !DEDICATED - - LABEL_37: - v21 = *(_DWORD*)v15; - if (*(_DWORD*)v15 != INVALID_PAK_HANDLE) - { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - v22 = 232i64 * (v21 & 0x1FF); -#else - v22 = 184i64 * (v21 & 0x1FF); -#endif - if (*(_DWORD*)((char*)&*g_pLoadedPakInfo + v22) != _DWORD(v21) || ((*(_DWORD*)((char*)&*g_pLoadedPakInfo + v22 + 4) - 9) & 0xFFFFFFFB) != 0) - { - *byte_16709DDDF = 0; return; - } - } - goto LABEL_40; + pPakName += 35; } + + int startIndex = 0; + + if (!startFromFirst) + startIndex = i; // start from last pre-cached + + const int numToProcess = startIndex; + + if (startIndex <= 4) + { + int numLeftToProcess = 4; + CommonPakData_t* data = &g_commonPakData[4]; + + do + { + if (*data->pakName) + { + PakLoadedInfo_t* const pakInfo = Pak_GetPakInfo(data->pakId); + EPakStatus status; + + // TODO: revisit this, this appears incorrect but also the way + // respawn does this. it this always supposed to be true on + // retail builds? + bool keepLoaded = true; + data->keepLoaded = true; + + if (pakInfo->handle == data->pakId) + { + status = pakInfo->status; + keepLoaded = data->keepLoaded; + } + else + { + status = PAK_STATUS_INVALID_PAKHANDLE; + keepLoaded = true; + } + + if (!keepLoaded || status == PAK_STATUS_LOADED) + { + // SDK pak files must be unloaded before the engine pak files, + // as we use assets within engine pak files. + switch (numLeftToProcess) + { +#ifndef DEDICATED + case CommonPakData_t::PAK_TYPE_UI_GM: + s_customPakData.UnloadBasePak(CustomPakData_t::PAK_TYPE_UI_SDK); + break; +#endif // !DEDICATED + + case CommonPakData_t::PAK_TYPE_COMMON: + g_StudioMdlFallbackHandler.Clear(); + break; + + case CommonPakData_t::PAK_TYPE_COMMON_GM: + s_customPakData.UnloadBasePak(CustomPakData_t::PAK_TYPE_COMMON_SDK); + break; + + case CommonPakData_t::PAK_TYPE_LOBBY: + s_customPakData.basePaksLoaded = false; + break; + + default: + break; + } + + // The old gather props is set if a model couldn't be + // loaded properly. If we unload level assets, we just + // enable the new implementation again and re-evaluate + // on the next level load. If we load a missing/bad + // model again, we toggle the old implementation as + // otherwise the fallback models won't render; the new + // gather props solution does not attempt to obtain + // studio hardware data on bad mdl handles. See + // 'GatherStaticPropsSecondPass_PreInit()' for details. + g_StudioMdlFallbackHandler.DisableLegacyGatherProps(); + + g_pakLoadApi->UnloadAsync(data->pakId); + + Mod_UnloadPakFile(); // Unload mod pak files. + + if (s_pLevelSetKV) + { + // Delete current level settings if we drop all paks.. + s_pLevelSetKV->DeleteThis(); + s_pLevelSetKV = nullptr; + } + } + + if (status && (unsigned int)(status - 13) > 1) + return; + + data->keepLoaded = false; + data->pakName[0] = '\0'; + + data->pakId = INVALID_PAK_HANDLE; + } + --numLeftToProcess; + --data; + } while (numLeftToProcess >= numToProcess); + } + + *g_pPakPrecacheJobFinished = true; + CommonPakData_t* commonData = g_commonPakData; + + int it = 0; + + char* name; + char* nameIt; + + while (true) + { + name = g_commonPakData[it].pakName; + nameIt = name; + char c; + int v20; + do + { + c = (unsigned __int8)nameIt[(unsigned __int64)(commonData->basePakName - (const char*)name)]; + v20 = (unsigned __int8)*nameIt - c; + if (v20) + break; + + ++nameIt; + } while (c); + + if (!v20) + goto CHECK_FOR_FAILURE; + + V_strncpy(name, commonData->basePakName, MAX_PATH); + + if (*commonData->pakName) + break; + + commonData->pakId = INVALID_PAK_HANDLE; + LOOP_AGAIN_OR_FINISH: + + ++it; + ++commonData; + if (it >= 5) + { + if (*g_pPakPrecacheJobFinished) + { + __int64 pMTVFTaskItem = *g_pMTVFTaskItem; + if (pMTVFTaskItem) + { + if (!*(_BYTE*)(pMTVFTaskItem + 4)) + { + JobFifoLock_s* const pakFifoLock = &g_pakGlobals->fifoLock; + + if (g_pakGlobals->hasPendingUnloadJobs || g_pakGlobals->loadedPakCount != g_pakGlobals->requestedPakCount) + { + if (!JT_AcquireFifoLockOrHelp(pakFifoLock) + && !JT_HelpWithJobTypes(g_pPakFifoLockWrapper, pakFifoLock, -1i64, 0i64)) + { + JT_HelpWithJobTypesOrSleep(g_pPakFifoLockWrapper, pakFifoLock, -1i64, 0i64, 0i64, 1); + } + + Mod_UnloadPendingAndPrecacheRequestedPaks(); + + if (ThreadInMainThread()) + { + if (*g_bPakFifoLockAcquired) + { + *g_bPakFifoLockAcquired = 0; + JT_ReleaseFifoLock(pakFifoLock); + } + } + + JT_ReleaseFifoLock(pakFifoLock); + + pMTVFTaskItem = *g_pMTVFTaskItem; + } + + FileSystem()->ResetItemCacheSize(256); + FileSystem()->PrecacheTaskItem(pMTVFTaskItem); + } + } + } + return; + } + } + + if (it == CommonPakData_t::PAK_TYPE_LOBBY) + s_customPakData.basePaksLoaded = true; + + if (s_customPakData.basePaksLoaded && !s_customPakData.levelResourcesLoaded) + { + Mod_PreloadLevelPaks(s_CurrentLevelName.String()); + s_customPakData.levelResourcesLoaded = true; + } + + commonData->pakId = g_pakLoadApi->LoadAsync(name, AlignedMemAlloc(), 4, 0); + +#ifndef DEDICATED + if (it == CommonPakData_t::PAK_TYPE_UI_GM) + s_customPakData.LoadBasePak("ui_sdk.rpak", CustomPakData_t::PAK_TYPE_UI_SDK); +#endif // !DEDICATED + if (it == CommonPakData_t::PAK_TYPE_COMMON_GM) + s_customPakData.LoadBasePak("common_sdk.rpak", CustomPakData_t::PAK_TYPE_COMMON_SDK); + +CHECK_FOR_FAILURE: + + if (commonData->pakId != INVALID_PAK_HANDLE) + { + const PakLoadedInfo_t* const pli = Pak_GetPakInfo(commonData->pakId); + + if (pli->handle != commonData->pakId || ((pli->status - 9) & 0xFFFFFFFB) != 0) + { + *g_pPakPrecacheJobFinished = false; + return; + } + } + + goto LOOP_AGAIN_OR_FINISH; } //----------------------------------------------------------------------------- @@ -394,12 +440,12 @@ void Mod_ProcessPakQueue() // Input : *szLevelName - // Output : true on success, false on failure //----------------------------------------------------------------------------- -void Mod_LoadPakForMap(const char* pszLevelName) +void Mod_LoadPakForMap(const char* const pszLevelName) { if (Mod_LevelHasChanged(pszLevelName)) - s_bLevelResourceInitialized = false; + s_customPakData.levelResourcesLoaded = false; - s_LevelName = pszLevelName; + s_CurrentLevelName = pszLevelName; // Dedicated should not load loadscreens. #ifndef DEDICATED @@ -412,11 +458,12 @@ void Mod_LoadPakForMap(const char* pszLevelName) // Input : *pszLevelName - // Output : KeyValues* //----------------------------------------------------------------------------- -KeyValues* Mod_GetLevelSettings(const char* pszLevelName) +KeyValues* Mod_GetLevelSettings(const char* const pszLevelName) { if (s_pLevelSetKV) { - if (s_bLevelResourceInitialized) + // If we didn't change the level, return the current one + if (s_customPakData.levelResourcesLoaded) return s_pLevelSetKV; s_pLevelSetKV->DeleteThis(); @@ -433,14 +480,14 @@ KeyValues* Mod_GetLevelSettings(const char* pszLevelName) // Purpose: loads required pakfile assets for specified BSP level // Input : &svSetFile - //----------------------------------------------------------------------------- -void Mod_PreloadLevelPaks(const char* pszLevelName) +void Mod_PreloadLevelPaks(const char* const pszLevelName) { - KeyValues* pSettingsKV = Mod_GetLevelSettings(pszLevelName); + KeyValues* const pSettingsKV = Mod_GetLevelSettings(pszLevelName); if (!pSettingsKV) return; - KeyValues* pPakListKV = pSettingsKV->FindKey("PakList"); + KeyValues* const pPakListKV = pSettingsKV->FindKey("PakList"); if (!pPakListKV) return; @@ -453,12 +500,10 @@ void Mod_PreloadLevelPaks(const char* pszLevelName) continue; snprintf(szPathBuffer, sizeof(szPathBuffer), "%s.rpak", pSubKey->GetName()); - RPakHandle_t nPakId = g_pakLoadApi->LoadAsync(szPathBuffer, AlignedMemAlloc(), 4, 0); + const PakHandle_t nPakId = s_customPakData.LoadAndAddPak(szPathBuffer); if (nPakId == INVALID_PAK_HANDLE) Error(eDLL_T::ENGINE, NO_ERROR, "%s: unable to load pak '%s' results '%d'\n", __FUNCTION__, szPathBuffer, nPakId); - else - g_vLoadedPakHandle.AddToTail(nPakId); } } @@ -467,25 +512,14 @@ void Mod_PreloadLevelPaks(const char* pszLevelName) //----------------------------------------------------------------------------- void Mod_UnloadPakFile(void) { - for (const RPakHandle_t& it : g_vLoadedPakHandle) - { - if (it >= 0) - { - g_pakLoadApi->UnloadPak(it); - } - } - g_vLoadedPakHandle.Purge(); - g_vBadMDLHandles.clear(); + s_customPakData.UnloadAndRemoveAll(); + + g_StudioMdlFallbackHandler.ClearBadModelHandleCache(); + g_StudioMdlFallbackHandler.ClearSuppresionList(); } -void VModel_BSP::Attach() const +void VModel_BSP::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_Mod_LoadPakForMap, &Mod_LoadPakForMap); - DetourAttach((LPVOID*)&v_Mod_ProcessPakQueue, &Mod_ProcessPakQueue); + DetourSetup(&v_Mod_LoadPakForMap, &Mod_LoadPakForMap, bAttach); + DetourSetup(&v_Mod_QueuedPakCacheFrame, &Mod_QueuedPakCacheFrame, bAttach); } - -void VModel_BSP::Detach() const -{ - DetourDetach((LPVOID*)&v_Mod_LoadPakForMap, &Mod_LoadPakForMap); - DetourDetach((LPVOID*)&v_Mod_ProcessPakQueue, &Mod_ProcessPakQueue); -} \ No newline at end of file diff --git a/r5dev/engine/cmodel_bsp.h b/r5dev/engine/cmodel_bsp.h index fb94a996..da8eaec5 100644 --- a/r5dev/engine/cmodel_bsp.h +++ b/r5dev/engine/cmodel_bsp.h @@ -1,33 +1,135 @@ #pragma once #include "tier0/jobthread.h" +#include "rtech/ipakfile.h" //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- class KeyValues; -inline CMemory p_Mod_LoadPakForMap; +//----------------------------------------------------------------------------- +// this structure contains handles and names to the base pak files the engine +// loads for a level, this is used for load/unload management during level +// changes or engine shutdown +//----------------------------------------------------------------------------- +struct CommonPakData_t +{ + enum EPakType + { + // the UI pak assigned to the current gamemode (range in GameMode_t) + PAK_TYPE_UI_GM = 0, + PAK_TYPE_COMMON, + + // the base pak assigned to the current gamemode (range in GameMode_t) + PAK_TYPE_COMMON_GM, + PAK_TYPE_LOBBY, + + // NOTE: this one is assigned to the name of the level, the prior ones are + // all static! + PAK_TYPE_LEVEL, + + // the total number of pak files to watch and manage + PAK_TYPE_COUNT + }; + + CommonPakData_t() + { + Reset(); + } + + void Reset() + { + pakId = INVALID_PAK_HANDLE; + keepLoaded = false; + basePakName = nullptr; + + memset(pakName, '\0', sizeof(pakName)); + } + + PakHandle_t pakId; + bool keepLoaded; + + // the pak name that's being requested to be loaded for this particular slot + char pakName[MAX_PATH]; + + // the actual base pak name, like "common_pve.rpak" as set when this array is + // being initialized + const char* basePakName; +}; + +//----------------------------------------------------------------------------- +// this structure contains handles and names to the custom pak files that are +// loaded with the settings KV for that level, these paks are loaded after the +// common paks are loaded, but unloaded before the common paks are unloaded +//----------------------------------------------------------------------------- +struct CustomPakData_t +{ + enum EPakType + { + // the pak that loads after CommonPakData_t::PAK_TYPE_UI_GM has loaded, and + // unloads before CommonPakData_t::PAK_TYPE_UI_GM gets unloaded + PAK_TYPE_UI_SDK = 0, + + // the pak that loads after CommonPakData_t::PAK_TYPE_COMMON_GM has loaded, + // and unloads before CommonPakData_t::PAK_TYPE_COMMON_GM gets unloaded + PAK_TYPE_COMMON_SDK, + + // the total number of base SDK pak files + PAK_TYPE_COUNT + }; + + enum + { + // the absolute max number of custom paks, note that the engine's limit + // could still be reached before this number as game scripts and other + // code still loads paks such as gladiator cards or load screens + MAX_CUSTOM_PAKS = (PAK_MAX_HANDLES - CommonPakData_t::PAK_TYPE_COUNT) + }; + + CustomPakData_t() + { + for (size_t i = 0; i < V_ARRAYSIZE(handles); i++) + { + handles[i] = INVALID_PAK_HANDLE; + } + + // the first # handles are reserved for base SDK paks + numHandles = PAK_TYPE_COUNT; + + levelResourcesLoaded = false; + basePaksLoaded = false; + } + + PakHandle_t LoadAndAddPak(const char* const pakFile); + void UnloadAndRemoveAll(); + + PakHandle_t LoadBasePak(const char* const pakFile, const EPakType type); + void UnloadBasePak(const EPakType type); + + // Pak handles that have been loaded with the level + // from within the level settings KV (located in + // scripts/levels/settings/*.kv). On level unload, + // each pak listed in this vector gets unloaded. + PakHandle_t handles[MAX_CUSTOM_PAKS]; + size_t numHandles; + + bool levelResourcesLoaded; + bool basePaksLoaded; +}; + +// array size = CommonPakData_t::PAK_TYPE_COUNT +inline CommonPakData_t* g_commonPakData; + inline void(*v_Mod_LoadPakForMap)(const char* szLevelName); +inline void(*v_Mod_QueuedPakCacheFrame)(void); -inline CMemory p_Mod_ProcessPakQueue; -inline void(*v_Mod_ProcessPakQueue)(void); +inline int32_t * g_pNumPrecacheItemsMTVTF; +inline bool* g_pPakPrecacheJobFinished; -inline float* dword_14B383420; -inline int32_t * dword_1634F445C; -inline void** qword_167ED7BB8; -inline bool* byte_16709DDDF; -inline char** off_141874660; -inline void** unk_141874555; -inline void** unk_1418749B0; -inline void** unk_141874550; -inline int64_t* qword_167ED7BC0; +inline void(*Mod_UnloadPendingAndPrecacheRequestedPaks)(void); -inline __int64(*sub_14045BAC0)(__int64(__fastcall* a1)(__int64, _DWORD*, __int64, _QWORD*), JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4); -inline __int64(*sub_14045A1D0)(unsigned __int8(__fastcall* a1)(_QWORD), JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4, volatile signed __int64* a5, char a6); -inline void(*sub_140441220)(__int64 a1, __int64 a2); - -extern bool s_bBasePaksInitialized; -extern vector g_InstalledMaps; +extern CUtlVector g_InstalledMaps; +extern std::mutex g_InstalledMapsMutex; bool Mod_LevelHasChanged(const char* pszLevelName); void Mod_GetAllInstalledMaps(); @@ -41,55 +143,30 @@ class VModel_BSP : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Mod_LoadPakForMap", p_Mod_LoadPakForMap.GetPtr()); - LogFunAdr("Mod_ProcessPakQueue", p_Mod_ProcessPakQueue.GetPtr()); - LogFunAdr("sub_14045BAC0", reinterpret_cast(sub_14045BAC0)); - LogFunAdr("sub_14045A1D0", reinterpret_cast(sub_14045A1D0)); - LogFunAdr("sub_140441220", reinterpret_cast(sub_140441220)); - LogVarAdr("dword_14B383420", reinterpret_cast(dword_14B383420)); - LogVarAdr("dword_1634F445C", reinterpret_cast(dword_1634F445C)); - LogVarAdr("qword_167ED7BB8", reinterpret_cast(qword_167ED7BB8)); - LogVarAdr("byte_16709DDDF", reinterpret_cast(byte_16709DDDF)); - LogVarAdr("off_141874660", reinterpret_cast(off_141874660)); - LogVarAdr("unk_141874555", reinterpret_cast(unk_141874555)); - LogVarAdr("unk_1418749B0", reinterpret_cast(unk_1418749B0)); - LogVarAdr("unk_141874550", reinterpret_cast(unk_141874550)); - LogVarAdr("qword_167ED7BC0", reinterpret_cast(qword_167ED7BC0)); + LogFunAdr("Mod_LoadPakForMap", v_Mod_LoadPakForMap); + LogFunAdr("Mod_QueuedPakCacheFrame", v_Mod_QueuedPakCacheFrame); + + LogFunAdr("Mod_UnloadPendingAndPrecacheRequestedPaks", Mod_UnloadPendingAndPrecacheRequestedPaks); + + LogVarAdr("g_numPrecacheItemsMTVTF", g_pNumPrecacheItemsMTVTF); + LogVarAdr("g_pakPrecacheJobFinished", g_pPakPrecacheJobFinished); + + LogVarAdr("g_commonPakData", g_commonPakData); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_Mod_LoadPakForMap = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 4C 8B C1 48 8D 15 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 4C 8D 0D ?? ?? ?? ??"); - v_Mod_LoadPakForMap = p_Mod_LoadPakForMap.RCast(); /*48 81 EC ? ? ? ? 4C 8B C1 48 8D 15 ? ? ? ? 48 8D 4C 24 ? E8 ? ? ? ? 4C 8D 0D ? ? ? ?*/ -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_Mod_LoadPakForMap = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 0F B6 05 ?? ?? ?? ?? 4C 8D 05 ?? ?? ?? ?? 84 C0"); - v_Mod_LoadPakForMap = p_Mod_LoadPakForMap.RCast(); /*48 81 EC ? ? ? ? 0F B6 05 ? ? ? ? 4C 8D 05 ? ? ? ? 84 C0*/ -#endif - p_Mod_ProcessPakQueue = g_GameDll.FindPatternSIMD("40 53 48 83 EC ?? F3 0F 10 05 ?? ?? ?? ?? 32 DB"); - v_Mod_ProcessPakQueue = p_Mod_ProcessPakQueue.RCast(); /*40 53 48 83 EC ?? F3 0F 10 05 ? ? ? ? 32 DB*/ - - sub_14045BAC0 = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 4C 89 4C 24 ?? 4C 89 44 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 60").RCast<__int64(*)(__int64(__fastcall* a1)(__int64, _DWORD*, __int64, _QWORD*), JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4)>(); - sub_14045A1D0 = g_GameDll.FindPatternSIMD("4C 89 4C 24 ?? 4C 89 44 24 ?? 48 89 54 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ??").RCast<__int64(*)(unsigned __int8(__fastcall* a1)(_QWORD), JobFifoLock_s* pFifoLock, __int64 a3, __int64 a4, volatile signed __int64* a5, char a6)>(); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - sub_140441220 = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 83 EC 20 33 ED 48 39 2D ?? ?? ?? ??").RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - sub_140441220 = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 33 ED 48 8D 35 ?? ?? ?? ?? 48 39 2D ?? ?? ?? ??").RCast(); -#endif + g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 0F B6 05 ?? ?? ?? ?? 4C 8D 05 ?? ?? ?? ?? 84 C0").GetPtr(v_Mod_LoadPakForMap); + g_GameDll.FindPatternSIMD("40 53 48 83 EC ?? F3 0F 10 05 ?? ?? ?? ?? 32 DB").GetPtr(v_Mod_QueuedPakCacheFrame); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 33 ED 48 8D 35 ?? ?? ?? ?? 48 39 2D ?? ?? ?? ??").GetPtr(Mod_UnloadPendingAndPrecacheRequestedPaks); } virtual void GetVar(void) const { - dword_14B383420 = p_Mod_ProcessPakQueue.FindPattern("F3 0F 10").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); - dword_1634F445C = p_Mod_ProcessPakQueue.FindPattern("8B 05").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - qword_167ED7BB8 = p_Mod_ProcessPakQueue.Offset(0x10).FindPatternSelf("48 83").ResolveRelativeAddressSelf(0x3, 0x8).RCast(); - byte_16709DDDF = p_Mod_ProcessPakQueue.Offset(0x20).FindPatternSelf("88 1D").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - off_141874660 = p_Mod_ProcessPakQueue.Offset(0x40).FindPatternSelf("4C 8D 15").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - unk_141874555 = p_Mod_ProcessPakQueue.Offset(0x40).FindPatternSelf("4C 8D 1D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - unk_1418749B0 = p_Mod_ProcessPakQueue.Offset(0xA0).FindPatternSelf("48 8D 1D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - unk_141874550 = p_Mod_ProcessPakQueue.Offset(0x150).FindPatternSelf("48 8D 2D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - qword_167ED7BC0 = p_Mod_ProcessPakQueue.Offset(0x200).FindPatternSelf("48 83 3D").ResolveRelativeAddressSelf(0x3, 0x8).RCast(); + g_pNumPrecacheItemsMTVTF = CMemory(v_Mod_QueuedPakCacheFrame).FindPattern("8B 05").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); + g_pPakPrecacheJobFinished = CMemory(v_Mod_QueuedPakCacheFrame).Offset(0x20).FindPatternSelf("88 1D").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); + + CMemory(v_Mod_QueuedPakCacheFrame).Offset(0xA0).FindPatternSelf("48 8D 2D").ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(g_commonPakData); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/common.cpp b/r5dev/engine/common.cpp index c43aaf9e..f569156d 100644 --- a/r5dev/engine/common.cpp +++ b/r5dev/engine/common.cpp @@ -6,6 +6,7 @@ #include #include +#include #include /* @@ -42,4 +43,54 @@ const char* COM_FormatSeconds(int seconds) } return string; -} \ No newline at end of file +} + +/* +============================== +COM_ExplainDisconnection + +============================== +*/ +void COM_ExplainDisconnection(bool bPrint, const char* fmt, ...) +{ + char szBuf[1024]; + {///////////////////////////// + va_list vArgs; + va_start(vArgs, fmt); + + vsnprintf(szBuf, sizeof(szBuf), fmt, vArgs); + + szBuf[sizeof(szBuf) - 1] = '\0'; + va_end(vArgs); + }///////////////////////////// + + if (bPrint) + { + if (szBuf[0] == '#') + { + wchar_t formatStr[1024]; + const wchar_t* wpchReason = (*g_ppVGuiLocalize) ? (*g_ppVGuiLocalize)->Find(szBuf) : nullptr; + if (wpchReason) + { + wcsncpy(formatStr, wpchReason, sizeof(formatStr) / sizeof(wchar_t)); + + char conStr[256]; + (*g_ppVGuiLocalize)->ConvertUnicodeToANSI(formatStr, conStr, sizeof(conStr)); + Error(eDLL_T::CLIENT, NO_ERROR, "%s\n", conStr); + } + else + Error(eDLL_T::CLIENT, NO_ERROR, "%s\n", szBuf); + } + else + { + Error(eDLL_T::CLIENT, NO_ERROR, "%s\n", szBuf); + } + } + + v_COM_ExplainDisconnection(bPrint, szBuf); +} + +void VCommon::Detour(const bool bAttach) const +{ + DetourSetup(&v_COM_ExplainDisconnection, COM_ExplainDisconnection, bAttach); +} diff --git a/r5dev/engine/common.h b/r5dev/engine/common.h index 7ef14682..715c3a2c 100644 --- a/r5dev/engine/common.h +++ b/r5dev/engine/common.h @@ -1,32 +1,29 @@ #pragma once /* ==== COMMON ========================================================================================================================================================== */ -inline CMemory p_COM_InitFilesystem; -inline void*(*COM_InitFilesystem)(const char* pFullModPath); - -inline CMemory p_COM_ExplainDisconnection; -inline void*(*COM_ExplainDisconnection)(uint64_t level, const char* fmt, ...); +inline void*(*v_COM_InitFilesystem)(const char* pFullModPath); +inline char* const(*v_COM_GetPrintMessageBuffer)(void); +inline void(*v_COM_ExplainDisconnection)(bool bPrint, const char* fmt, ...); const char* COM_FormatSeconds(int seconds); +void COM_ExplainDisconnection(bool bPrint, const char* fmt, ...); /////////////////////////////////////////////////////////////////////////////// class VCommon : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("COM_InitFilesystem", p_COM_InitFilesystem.GetPtr()); - LogFunAdr("COM_ExplainDisconnection", p_COM_ExplainDisconnection.GetPtr()); + LogFunAdr("COM_InitFilesystem", v_COM_InitFilesystem); + LogFunAdr("COM_GetPrintMessageBuffer", v_COM_GetPrintMessageBuffer); + LogFunAdr("COM_ExplainDisconnection", v_COM_ExplainDisconnection); } virtual void GetFun(void) const { - p_COM_InitFilesystem = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 48 C7 44 24 ?? ?? ?? ?? ??"); - p_COM_ExplainDisconnection = g_GameDll.FindPatternSIMD("48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 48 81 EC ?? ?? ?? ??"); - - COM_InitFilesystem = p_COM_InitFilesystem.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 48 C7 44 24 ?? ?? ?? ?? ??*/ - COM_ExplainDisconnection = p_COM_ExplainDisconnection.RCast(); /*48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 48 81 EC ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 48 C7 44 24 ?? ?? ?? ?? ??").GetPtr(v_COM_InitFilesystem); + g_GameDll.FindPatternSIMD("48 8D 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 4C 89 44 24 ??").GetPtr(v_COM_GetPrintMessageBuffer); + g_GameDll.FindPatternSIMD("48 8B C4 48 89 50 10 4C 89 40 18 4C 89 48 20 48 81 EC ?? ?? ?? ??").GetPtr(v_COM_ExplainDisconnection); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/debugoverlay.cpp b/r5dev/engine/debugoverlay.cpp index 4bfa3b78..57f525f4 100644 --- a/r5dev/engine/debugoverlay.cpp +++ b/r5dev/engine/debugoverlay.cpp @@ -20,6 +20,11 @@ #include "game/server/ai_network.h" #endif // !CLIENT_DLL +ConVar r_debug_draw_depth_test("r_debug_draw_depth_test", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Toggle depth test for other debug draw functionality"); + +static ConVar r_debug_overlay_nodecay("r_debug_overlay_nodecay", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Keeps all debug overlays alive regardless of their lifetime. Use command 'clear_debug_overlays' to clear everything"); +static ConVar r_debug_overlay_invisible("r_debug_overlay_invisible", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Show invisible debug overlays (alpha < 1 = 255)"); +static ConVar r_debug_overlay_wireframe("r_debug_overlay_wireframe", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Use wireframe in debug overlay"); //------------------------------------------------------------------------------ // Purpose: checks if overlay should be decayed @@ -27,7 +32,7 @@ //------------------------------------------------------------------------------ bool OverlayBase_t::IsDead() const { - if (r_debug_overlay_nodecay->GetBool()) + if (r_debug_overlay_nodecay.GetBool()) { // Keep rendering the overlay if no-decay is set. return false; @@ -107,7 +112,7 @@ void DrawOverlay(OverlayBase_t* pOverlay) OverlayBox_t* pBox = static_cast(pOverlay); if (pBox->a < 1) { - if (r_debug_overlay_invisible->GetBool()) + if (r_debug_overlay_invisible.GetBool()) { pBox->a = 255; } @@ -126,7 +131,7 @@ void DrawOverlay(OverlayBase_t* pOverlay) OverlaySphere_t* pSphere = static_cast(pOverlay); if (pSphere->a < 1) { - if (r_debug_overlay_invisible->GetBool()) + if (r_debug_overlay_invisible.GetBool()) { pSphere->a = 255; } @@ -137,14 +142,14 @@ void DrawOverlay(OverlayBase_t* pOverlay) } } - if (r_debug_overlay_wireframe->GetBool()) + if (r_debug_overlay_wireframe.GetBool()) { v_RenderWireframeSphere(pSphere->vOrigin, pSphere->flRadius, pSphere->nTheta, pSphere->nPhi, - Color(pSphere->r, pSphere->g, pSphere->b, pSphere->a), r_debug_draw_depth_test->GetBool()); + Color(pSphere->r, pSphere->g, pSphere->b, pSphere->a), r_debug_draw_depth_test.GetBool()); } else { - DebugDrawSphere(pSphere->vOrigin, pSphere->flRadius, Color(pSphere->r, pSphere->g, pSphere->b, pSphere->a), 16, r_debug_draw_depth_test->GetBool()); + DebugDrawSphere(pSphere->vOrigin, pSphere->flRadius, Color(pSphere->r, pSphere->g, pSphere->b, pSphere->a), 16, r_debug_draw_depth_test.GetBool()); } break; } @@ -153,7 +158,7 @@ void DrawOverlay(OverlayBase_t* pOverlay) OverlayLine_t* pLine = static_cast(pOverlay); if (pLine->a < 1) { - if (r_debug_overlay_invisible->GetBool()) + if (r_debug_overlay_invisible.GetBool()) { pLine->a = 255; } @@ -188,7 +193,7 @@ void DrawOverlay(OverlayBase_t* pOverlay) OverlayCapsule_t* pCapsule = static_cast(pOverlay); if (pCapsule->a < 1) { - if (r_debug_overlay_invisible->GetBool()) + if (r_debug_overlay_invisible.GetBool()) { pCapsule->a = 255; } @@ -205,7 +210,7 @@ void DrawOverlay(OverlayBase_t* pOverlay) AngleInverse(angles, angles); DebugDrawCapsule(pCapsule->start, angles, pCapsule->radius, pCapsule->start.DistTo(pCapsule->end), - Color(pCapsule->r, pCapsule->g, pCapsule->b, pCapsule->a), r_debug_draw_depth_test->GetBool()); + Color(pCapsule->r, pCapsule->g, pCapsule->b, pCapsule->a), r_debug_draw_depth_test.GetBool()); break; } case OverlayType_t::OVERLAY_UNK0: @@ -292,16 +297,7 @@ void DrawAllOverlays(bool bRender) #ifndef CLIENT_DLL if (bOverlayEnabled) { - if (ai_script_nodes_draw->GetInt() > -1) - g_pAIUtility->DrawAIScriptNetwork(*g_pAINetwork); - if (navmesh_draw_bvtree->GetInt() > -1) - g_pAIUtility->DrawNavMeshBVTree(); - if (navmesh_draw_portal->GetInt() > -1) - g_pAIUtility->DrawNavMeshPortals(); - if (navmesh_draw_polys->GetInt() > -1) - g_pAIUtility->DrawNavMeshPolys(); - if (navmesh_draw_poly_bounds->GetInt() > -1) - g_pAIUtility->DrawNavMeshPolyBoundaries(); + g_pAIUtility->RunRenderFrame(); } #endif // !CLIENT_DLL @@ -309,12 +305,7 @@ void DrawAllOverlays(bool bRender) } /////////////////////////////////////////////////////////////////////////////// -void VDebugOverlay::Attach() const +void VDebugOverlay::Detour(const bool bAttach) const { - DetourAttach(&v_DrawAllOverlays, &DrawAllOverlays); -} - -void VDebugOverlay::Detach() const -{ - DetourDetach(&v_DrawAllOverlays, &DrawAllOverlays); + DetourSetup(&v_DrawAllOverlays, &DrawAllOverlays, bAttach); } diff --git a/r5dev/engine/debugoverlay.h b/r5dev/engine/debugoverlay.h index 5f38e5ca..1f68259f 100644 --- a/r5dev/engine/debugoverlay.h +++ b/r5dev/engine/debugoverlay.h @@ -4,22 +4,8 @@ #include "mathlib/color.h" #include "mathlib/ssemath.h" -// Something has to be hardcoded.. -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - -constexpr auto MATERIALSYSTEM_VCALL_OFF_0 = 0x3F8; -constexpr auto CMATQUEUEDRENDERCONTEXT_VCALL_OFS_0 = 0x278; -constexpr auto CMATQUEUEDRENDERCONTEXT_VCALL_OFS_1 = 0x280; - -#elif defined (GAMEDLL_S3) - -constexpr auto MATERIALSYSTEM_VCALL_OFF_0 = 0x3F0; -constexpr auto CMATQUEUEDRENDERCONTEXT_VCALL_OFS_0 = 0x288; -constexpr auto CMATQUEUEDRENDERCONTEXT_VCALL_OFS_1 = 0x290; - -#endif -constexpr auto CMATQUEUEDRENDERCONTEXT_VCALL_OFS_2 = 0x8; constexpr auto NDEBUG_PERSIST_TILL_NEXT_SERVER = (0.01023f); +extern ConVar r_debug_draw_depth_test; enum class OverlayType_t { @@ -159,19 +145,10 @@ struct OverlayCapsule_t : public OverlayBase_t void DestroyOverlay(OverlayBase_t* pOverlay); void DrawOverlay(OverlayBase_t* pOverlay); -inline CMemory p_DrawAllOverlays; inline void(*v_DrawAllOverlays)(bool bDraw); - -inline CMemory p_DestroyOverlay; inline void(*v_DestroyOverlay)(OverlayBase_t* pOverlay); - -inline CMemory p_RenderLine; inline void*(*v_RenderLine)(const Vector3D& vOrigin, const Vector3D& vDest, Color color, bool bZBuffer); - -inline CMemory p_RenderBox; inline void*(*v_RenderBox)(const matrix3x4_t& vTransforms, const Vector3D& vMins, const Vector3D& vMaxs, Color color, bool bZBuffer); - -inline CMemory p_RenderWireframeSphere; inline void*(*v_RenderWireframeSphere)(const Vector3D& vCenter, float flRadius, int nTheta, int nPhi, Color color, bool bZBuffer); inline OverlayBase_t** s_pOverlays = nullptr; @@ -185,50 +162,33 @@ class VDebugOverlay : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("DrawAllOverlays", p_DrawAllOverlays.GetPtr()); - LogFunAdr("DestroyOverlay", p_DestroyOverlay.GetPtr()); - LogFunAdr("RenderLine", p_RenderLine.GetPtr()); - LogFunAdr("RenderBox", p_RenderBox.GetPtr()); - LogFunAdr("RenderWireframeSphere", p_RenderWireframeSphere.GetPtr()); - LogVarAdr("s_Overlays", reinterpret_cast(s_pOverlays)); - LogVarAdr("s_OverlayMutex", reinterpret_cast(s_OverlayMutex)); - LogVarAdr("g_nOverlayTickCount", reinterpret_cast(g_nOverlayTickCount)); - LogVarAdr("g_nRenderTickCount", reinterpret_cast(g_nRenderTickCount)); + LogFunAdr("DrawAllOverlays", v_DrawAllOverlays); + LogFunAdr("DestroyOverlay", v_DestroyOverlay); + LogFunAdr("RenderLine", v_RenderLine); + LogFunAdr("RenderBox", v_RenderBox); + LogFunAdr("RenderWireframeSphere", v_RenderWireframeSphere); + LogVarAdr("s_Overlays", s_pOverlays); + LogVarAdr("s_OverlayMutex", s_OverlayMutex); + LogVarAdr("g_nOverlayTickCount", g_nOverlayTickCount); + LogVarAdr("g_nRenderTickCount", g_nRenderTickCount); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_DrawAllOverlays = g_GameDll.FindPatternSIMD("40 55 48 83 EC 50 48 8B 05 ?? ?? ?? ??"); - p_RenderBox = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 44 89 4C 24 ?? 55 41 56"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_DrawAllOverlays = g_GameDll.FindPatternSIMD("40 55 48 83 EC 30 48 8B 05 ?? ?? ?? ?? 0F B6 E9"); - p_RenderBox = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 44 89 4C 24 ??"); -#endif - p_DestroyOverlay = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 8D 0D ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 63 03"); - p_RenderWireframeSphere = g_GameDll.FindPatternSIMD("40 56 41 54 41 55 48 81 EC ?? ?? ?? ??"); - p_RenderLine = g_GameDll.FindPatternSIMD("48 89 74 24 ?? 44 89 44 24 ?? 57 41 56"); - - v_DrawAllOverlays = p_DrawAllOverlays.RCast(); /*40 55 48 83 EC 30 48 8B 05 ?? ?? ?? ?? 0F B6 E9*/ - v_DestroyOverlay = p_DestroyOverlay.RCast(); /*40 53 48 83 EC 20 48 8B D9 48 8D 0D ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 63 03 */ - v_RenderBox = p_RenderBox.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 44 89 4C 24 ??*/ - v_RenderWireframeSphere = p_RenderWireframeSphere.RCast(); /*40 56 41 54 41 55 48 81 EC ?? ?? ?? ??*/ - v_RenderLine = p_RenderLine.RCast(); /*48 89 74 24 ?? 44 89 44 24 ?? 57 41 56*/ + g_GameDll.FindPatternSIMD("40 55 48 83 EC 30 48 8B 05 ?? ?? ?? ?? 0F B6 E9").GetPtr(v_DrawAllOverlays); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 44 89 4C 24 ??").GetPtr(v_RenderBox); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 8D 0D ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 63 03").GetPtr(v_DestroyOverlay); + g_GameDll.FindPatternSIMD("40 56 41 54 41 55 48 81 EC ?? ?? ?? ??").GetPtr(v_RenderWireframeSphere); + g_GameDll.FindPatternSIMD("48 89 74 24 ?? 44 89 44 24 ?? 57 41 56").GetPtr(v_RenderLine); } virtual void GetVar(void) const { - s_pOverlays = p_DrawAllOverlays.Offset(0x10).FindPatternSelf("48 8B 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - s_OverlayMutex = p_DrawAllOverlays.Offset(0x10).FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + s_pOverlays = CMemory(v_DrawAllOverlays).Offset(0x10).FindPatternSelf("48 8B 3D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + s_OverlayMutex = CMemory(v_DrawAllOverlays).Offset(0x10).FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_nRenderTickCount = p_DrawAllOverlays.Offset(0x80).FindPatternSelf("3B 0D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - g_nOverlayTickCount = p_DrawAllOverlays.Offset(0x70).FindPatternSelf("3B 0D", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - g_nRenderTickCount = p_DrawAllOverlays.Offset(0x50).FindPatternSelf("3B 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - g_nOverlayTickCount = p_DrawAllOverlays.Offset(0x70).FindPatternSelf("3B 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); -#endif + g_nRenderTickCount = CMemory(v_DrawAllOverlays).Offset(0x50).FindPatternSelf("3B 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); + g_nOverlayTickCount = CMemory(v_DrawAllOverlays).Offset(0x70).FindPatternSelf("3B 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/enginetrace.cpp b/r5dev/engine/enginetrace.cpp index 39e947ef..d12666e8 100644 --- a/r5dev/engine/enginetrace.cpp +++ b/r5dev/engine/enginetrace.cpp @@ -6,13 +6,3 @@ #include "core/stdafx.h" #include "engine/enginetrace.h" - -void CEngineTrace_Attach() -{ - -} - -void CEngineTrace_Detach() -{ - -} diff --git a/r5dev/engine/enginetrace.h b/r5dev/engine/enginetrace.h index 033138ec..d294ac4e 100644 --- a/r5dev/engine/enginetrace.h +++ b/r5dev/engine/enginetrace.h @@ -22,20 +22,16 @@ class CEngineTraceClient : public CEngineTrace inline CEngineTraceClient* g_pEngineTraceClient = nullptr; #endif // DEDICATED -/////////////////////////////////////////////////////////////////////////////// -void CEngineTrace_Attach(); -void CEngineTrace_Detach(); - /////////////////////////////////////////////////////////////////////////////// class VEngineTrace : public IDetour { virtual void GetAdr(void) const { #ifndef CLIENT_DLL - LogVarAdr("g_pEngineTraceServer", reinterpret_cast(g_pEngineTraceServer)); + LogVarAdr("g_pEngineTraceServer", g_pEngineTraceServer); #endif // CLIENT_DLL #ifndef DEDICATED - LogVarAdr("g_pEngineTraceClient", reinterpret_cast(g_pEngineTraceClient)); + LogVarAdr("g_pEngineTraceClient", g_pEngineTraceClient); #endif // DEDICATED } virtual void GetFun(void) const { } @@ -47,7 +43,6 @@ class VEngineTrace : public IDetour g_pEngineTraceServer = reinterpret_cast(&g_pEngineTraceServerVFTable); // Must be done for virtual calls. #endif // CLIENT_DLL } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/framelimit.cpp b/r5dev/engine/framelimit.cpp new file mode 100644 index 00000000..ceced332 --- /dev/null +++ b/r5dev/engine/framelimit.cpp @@ -0,0 +1,93 @@ +//===========================================================================// +// +// Purpose: High-precision frame rate limiter +// +//===========================================================================// +#include +#include "tier0/platform_internal.h" +#include "windows/id3dx.h" +#include "sys_mainwind.h" +#include "framelimit.h" + +//----------------------------------------------------------------------------- +// Purpose: constructor +//----------------------------------------------------------------------------- +CFrameLimit::CFrameLimit(void) +{ + m_MilliSeconds = 0.0; + m_FramesPerSecond = 0.0; + + m_Start.QuadPart = 0; + m_Next.QuadPart = 0; + m_Time.QuadPart = 0; + + m_Frames = 0; + m_bRestart = false; +} + +//----------------------------------------------------------------------------- +// Purpose: initializer +// Input : targetFps - +//----------------------------------------------------------------------------- +void CFrameLimit::Reset(double targetFps) +{ + m_MilliSeconds = 1000.0 / targetFps; + m_FramesPerSecond = targetFps; + + QueryPerformanceCounter(&m_Start); + m_Next.QuadPart = 0ULL; + m_Time.QuadPart = 0ULL; + + //m_Last.QuadPart = m_Start.QuadPart - (LONGLONG)((m_MilliSeconds / 1000.0) * g_pPerformanceFrequency->QuadPart); + m_Next.QuadPart = m_Start.QuadPart + (LONGLONG)((m_MilliSeconds / 1000.0) * g_pPerformanceFrequency->QuadPart); + + m_Frames = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: runs the frame limiter logic +//----------------------------------------------------------------------------- +void CFrameLimit::Run(const double targetFps, const double sleepThreshold, const double maxTolerance) +{ + if (m_FramesPerSecond != targetFps) + Reset(targetFps); + + m_Frames++; + QueryPerformanceCounter(&m_Time); + + // Actual frametime before we forced a delay + //m_EffectiveMilliSeconds = 1000.0 * ((double)(m_Time.QuadPart - m_Last.QuadPart) / (double)g_pPerformanceFrequency->QuadPart); + + if ((double)(m_Time.QuadPart - m_Next.QuadPart) / (double)g_pPerformanceFrequency->QuadPart / (m_MilliSeconds / 1000.0) > (maxTolerance * m_FramesPerSecond)) + { + DevMsg(eDLL_T::ENGINE, "%s: Frame time too long (expected: %3.01fx); restarting...\n", + __FUNCTION__, (double)(m_Time.QuadPart - m_Next.QuadPart) / (double)g_pPerformanceFrequency->QuadPart / (m_MilliSeconds / 1000.0) / m_FramesPerSecond ); + m_bRestart = true; + } + + if (m_bRestart) + { + m_Frames = 0; + m_Start.QuadPart = m_Time.QuadPart + (LONGLONG)((m_MilliSeconds / 1000.0) * (double)g_pPerformanceFrequency->QuadPart); + m_bRestart = false; + //Reset (targetFps); + //return; + } + + m_Next.QuadPart = (LONGLONG)((m_Start.QuadPart + (double)m_Frames * (m_MilliSeconds / 1000.0) * (double)g_pPerformanceFrequency->QuadPart)); + + if (m_Next.QuadPart > 0ULL) + { + while (m_Time.QuadPart < m_Next.QuadPart) + { + if ((double)(m_Next.QuadPart - m_Time.QuadPart) > (sleepThreshold * (double)g_pPerformanceFrequency->QuadPart)) + { + Sleep(10); + } + + QueryPerformanceCounter(&m_Time); + } + } + + //m_Last.QuadPart = m_Time.QuadPart; +} diff --git a/r5dev/engine/framelimit.h b/r5dev/engine/framelimit.h new file mode 100644 index 00000000..be4b8233 --- /dev/null +++ b/r5dev/engine/framelimit.h @@ -0,0 +1,26 @@ +#ifndef FRAMELIMIT_H +#define FRAMELIMIT_H + +//----------------------------------------------------------------------------- +// RenderThread frame limiter +//----------------------------------------------------------------------------- +class CFrameLimit +{ +public: + CFrameLimit(void); + + void Reset(const double target); + void Run(const double targetFps, const double sleepThreshold, const double maxTolerance); + +private: + double m_MilliSeconds; + double m_FramesPerSecond; + + LARGE_INTEGER m_Start; + LARGE_INTEGER m_Next; + LARGE_INTEGER m_Time; + uint32_t m_Frames; + bool m_bRestart; +}; + +#endif // FRAMELIMIT_H diff --git a/r5dev/engine/gl_drawlights.cpp b/r5dev/engine/gl_drawlights.cpp new file mode 100644 index 00000000..40b69635 --- /dev/null +++ b/r5dev/engine/gl_drawlights.cpp @@ -0,0 +1,13 @@ +#include "gl_drawlights.h" + + +void DrawLightSprites(void* unkType) +{ + v_DrawLightSprites(unkType); +} + +void VGL_DrawLights::Detour(const bool bAttach) const +{ + // Enable if needed. + //DetourSetup(&v_DrawLightSprites, &DrawLightSprites, bAttach); +} diff --git a/r5dev/engine/gl_drawlights.h b/r5dev/engine/gl_drawlights.h new file mode 100644 index 00000000..2822be2c --- /dev/null +++ b/r5dev/engine/gl_drawlights.h @@ -0,0 +1,20 @@ +#pragma once + +inline void(*v_DrawLightSprites)(void*); + +/////////////////////////////////////////////////////////////////////////////// +class VGL_DrawLights : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("DrawLightSprites", v_DrawLightSprites); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("48 8B C4 55 57 48 8D 68 A1 48 81 EC ?? ?? ?? ?? 48 8B 15 ?? ?? ?? ??").GetPtr(v_DrawLightSprites); + } + virtual void GetVar(void) const { } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const; +}; +/////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/gl_matsysiface.h b/r5dev/engine/gl_matsysiface.h index 242fb2e6..78a1bc5a 100644 --- a/r5dev/engine/gl_matsysiface.h +++ b/r5dev/engine/gl_matsysiface.h @@ -1,7 +1,6 @@ #pragma once /* ==== MATSYSIFACE ===================================================================================================================================================== */ -inline CMemory p_InitMaterialSystem; inline void*(*v_InitMaterialSystem)(void); /////////////////////////////////////////////////////////////////////////////// @@ -9,20 +8,14 @@ class VGL_MatSysIFace : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("InitMaterialSystem", p_InitMaterialSystem.GetPtr()); + LogFunAdr("InitMaterialSystem", v_InitMaterialSystem); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_InitMaterialSystem = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8D 1D ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ??"); -#else - p_InitMaterialSystem = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ??"); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) - v_InitMaterialSystem = p_InitMaterialSystem.RCast(); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ??").GetPtr(v_InitMaterialSystem); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/gl_model_private.h b/r5dev/engine/gl_model_private.h index 5b1f7ac6..31db1b84 100644 --- a/r5dev/engine/gl_model_private.h +++ b/r5dev/engine/gl_model_private.h @@ -9,20 +9,57 @@ #ifndef GL_MODEL_PRIVATE_H #define GL_MODEL_PRIVATE_H -#include "vpc/keyvalues.h" +#include "tier1/keyvalues.h" #include "mathlib/vector.h" #include "common/qlimits.h" #include "datacache/imdlcache.h" #include "public/model_types.h" +#include "public/bspfile.h" #ifndef DEDICATED #include "game/client/enginesprite.h" #endif // !DEDICATED typedef int ModelFileNameHandle_t; // 4 bytes in r5, void* originally. +struct worldbrushdata_t +{ + char unk[64]; + Vector3D* vertpositions; + int numvertices; + char unk_4C[4]; + Vector3D* vertnormals; + int numvertnormals; + int numtexdata; + dtexdata_t* texdata; + char* surfacenames; + char unk_60[4]; + int nummeshes; + char unk_78[72]; + int nummaterialsorts; + char unk_C4[4]; + char* lmapTypes; + int* lmapSizes; + dlightmapheader_t* lmapHeaders; + char* rtlData; + char* rtlPageData; + int numRtlPages; + int numLightmapHeaders; + bool externalLightmaps; + int numlightprobeindices; + int* lightprobeindices; + int numlightprobes; + dlightprobe_t* lightprobes; + char* lightproberefs; + char* lightprobetrees; + char* lightprobeparentinfos; + char unk_130[16]; + char* worldlights; + int numworldlights; +}; + struct brushdata_t // !! UNCONFIRMED !! { - void* pShared; // worldbrushdata_t + worldbrushdata_t* pShared; // worldbrushdata_t int firstmodelsurface; int nummodelsurfaces; diff --git a/r5dev/engine/gl_rmain.h b/r5dev/engine/gl_rmain.h index 3f0103a9..4881691e 100644 --- a/r5dev/engine/gl_rmain.h +++ b/r5dev/engine/gl_rmain.h @@ -24,8 +24,7 @@ class VGL_RMain : public IDetour virtual void GetFun(void) const { } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/gl_rsurf.cpp b/r5dev/engine/gl_rsurf.cpp index 8a1a0af6..7d9f0c0a 100644 --- a/r5dev/engine/gl_rsurf.cpp +++ b/r5dev/engine/gl_rsurf.cpp @@ -6,11 +6,24 @@ //===========================================================================// #include "core/stdafx.h" #include "tier1/cvar.h" +#include "windows/id3dx.h" +#include "geforce/reflex.h" #include "engine/gl_rsurf.h" +#include + +static ConVar r_drawWorldMeshes("r_drawWorldMeshes", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Render world meshes."); +static ConVar r_drawWorldMeshesDepthOnly("r_drawWorldMeshesDepthOnly", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Render world meshes (depth only)."); +static ConVar r_drawWorldMeshesDepthAtTheEnd("r_drawWorldMeshesDepthAtTheEnd", "1", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Render world meshes (depth at the end)."); + +void* R_DrawDepthOfField(const float scalar) +{ + GFX_SetLatencyMarker(D3D11Device(), RENDERSUBMIT_START, MaterialSystem()->GetCurrentFrameCount()); + return V_DrawDepthOfField(scalar); +} void* R_DrawWorldMeshes(void* baseEntity, void* renderContext, DrawWorldLists_t worldLists) { - if (r_drawWorldMeshes->GetBool()) + if (r_drawWorldMeshes.GetBool()) return V_DrawWorldMeshes(baseEntity, renderContext, worldLists); else return nullptr; @@ -18,7 +31,7 @@ void* R_DrawWorldMeshes(void* baseEntity, void* renderContext, DrawWorldLists_t void* R_DrawWorldMeshesDepthOnly(void* renderContext, DrawWorldLists_t worldLists) { - if (r_drawWorldMeshesDepthOnly->GetBool()) + if (r_drawWorldMeshesDepthOnly.GetBool()) return V_DrawWorldMeshesDepthOnly(renderContext, worldLists); else return nullptr; @@ -26,22 +39,16 @@ void* R_DrawWorldMeshesDepthOnly(void* renderContext, DrawWorldLists_t worldList void* R_DrawWorldMeshesDepthAtTheEnd(void* ptr1, void* ptr2, void* ptr3, DrawWorldLists_t worldLists) { - if (r_drawWorldMeshesDepthAtTheEnd->GetBool()) + if (r_drawWorldMeshesDepthAtTheEnd.GetBool()) return V_DrawWorldMeshesDepthAtTheEnd(ptr1, ptr2, ptr3, worldLists); else return nullptr; } -void VGL_RSurf::Attach() const +void VGL_RSurf::Detour(const bool bAttach) const { - DetourAttach(&V_DrawWorldMeshes, &R_DrawWorldMeshes); - DetourAttach(&V_DrawWorldMeshesDepthOnly, &R_DrawWorldMeshesDepthOnly); - DetourAttach(&V_DrawWorldMeshesDepthAtTheEnd, &R_DrawWorldMeshesDepthAtTheEnd); + DetourSetup(&V_DrawDepthOfField, &R_DrawDepthOfField, bAttach); + DetourSetup(&V_DrawWorldMeshes, &R_DrawWorldMeshes, bAttach); + DetourSetup(&V_DrawWorldMeshesDepthOnly, &R_DrawWorldMeshesDepthOnly, bAttach); + DetourSetup(&V_DrawWorldMeshesDepthAtTheEnd, &R_DrawWorldMeshesDepthAtTheEnd, bAttach); } - -void VGL_RSurf::Detach() const -{ - DetourDetach(&V_DrawWorldMeshes, &R_DrawWorldMeshes); - DetourDetach(&V_DrawWorldMeshesDepthOnly, &R_DrawWorldMeshesDepthOnly); - DetourDetach(&V_DrawWorldMeshesDepthAtTheEnd, &R_DrawWorldMeshesDepthAtTheEnd); -} \ No newline at end of file diff --git a/r5dev/engine/gl_rsurf.h b/r5dev/engine/gl_rsurf.h index 0c235ec6..8e64106c 100644 --- a/r5dev/engine/gl_rsurf.h +++ b/r5dev/engine/gl_rsurf.h @@ -1,13 +1,9 @@ #pragma once #include "public/ivrenderview.h" -inline CMemory P_DrawWorldMeshes; +inline void*(*V_DrawDepthOfField)(const float scalar); inline void*(*V_DrawWorldMeshes)(void* baseEntity, void* renderContext, DrawWorldLists_t worldLists); - -inline CMemory P_DrawWorldMeshesDepthOnly; inline void*(*V_DrawWorldMeshesDepthOnly)(void* renderContext, DrawWorldLists_t worldLists); - -inline CMemory P_DrawWorldMeshesDepthAtTheEnd; inline void*(*V_DrawWorldMeshesDepthAtTheEnd)(void* ptr1, void* ptr2, void* ptr3, DrawWorldLists_t worldLists); /////////////////////////////////////////////////////////////////////////////// @@ -15,27 +11,20 @@ class VGL_RSurf : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("R_DrawWorldMeshes", P_DrawWorldMeshes.GetPtr()); - LogFunAdr("R_DrawWorldMeshesDepthOnly", P_DrawWorldMeshesDepthOnly.GetPtr()); - LogFunAdr("R_DrawWorldMeshesDepthAtTheEnd", P_DrawWorldMeshesDepthAtTheEnd.GetPtr()); + LogFunAdr("R_DrawDepthOfField", V_DrawDepthOfField); + LogFunAdr("R_DrawWorldMeshes", V_DrawWorldMeshes); + LogFunAdr("R_DrawWorldMeshesDepthOnly", V_DrawWorldMeshesDepthOnly); + LogFunAdr("R_DrawWorldMeshesDepthAtTheEnd", V_DrawWorldMeshesDepthAtTheEnd); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - P_DrawWorldMeshes = g_GameDll.FindPatternSIMD("48 8B C4 48 89 48 08 53 48 83 EC 70"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - P_DrawWorldMeshes = g_GameDll.FindPatternSIMD("48 8B C4 48 89 48 08 53 57 41 55"); -#endif - P_DrawWorldMeshesDepthOnly = g_GameDll.FindPatternSIMD("40 56 57 B8 ?? ?? ?? ??"); - P_DrawWorldMeshesDepthAtTheEnd = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 41 8B F9"); - - V_DrawWorldMeshes = P_DrawWorldMeshes.RCast(); /*48 8B C4 48 89 48 08 53 57 41 55*/ - V_DrawWorldMeshesDepthOnly = P_DrawWorldMeshesDepthOnly.RCast(); /*40 56 57 B8 ?? ?? ?? ??*/ - V_DrawWorldMeshesDepthAtTheEnd = P_DrawWorldMeshesDepthAtTheEnd.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ? 57 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 41 8B F9*/ + g_GameDll.FindPatternSIMD("48 83 EC 48 0F 28 E8").GetPtr(V_DrawDepthOfField); + g_GameDll.FindPatternSIMD("48 8B C4 48 89 48 08 53 57 41 55").GetPtr(V_DrawWorldMeshes); + g_GameDll.FindPatternSIMD("40 56 57 B8 ?? ?? ?? ??").GetPtr(V_DrawWorldMeshesDepthOnly); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 41 8B F9").GetPtr(V_DrawWorldMeshesDepthAtTheEnd); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/gl_screen.h b/r5dev/engine/gl_screen.h index cd116d64..76af1cdb 100644 --- a/r5dev/engine/gl_screen.h +++ b/r5dev/engine/gl_screen.h @@ -1,7 +1,7 @@ #pragma once /////////////////////////////////////////////////////////////////////////////// -inline CMemory SCR_BeginLoadingPlaque; +inline __int64(*v_SCR_BeginLoadingPlaque)(void* a1); /////////////////////////////////////////////////////////////////////////////// inline bool* scr_drawloading = nullptr; @@ -14,32 +14,20 @@ class VGL_Screen : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("SCR_BeginLoadingPlaque", SCR_BeginLoadingPlaque.GetPtr()); - LogVarAdr("scr_drawloading", reinterpret_cast(scr_drawloading)); - LogVarAdr("scr_engineevent_loadingstarted", reinterpret_cast(scr_engineevent_loadingstarted)); + LogFunAdr("SCR_BeginLoadingPlaque", v_SCR_BeginLoadingPlaque); + LogVarAdr("scr_drawloading", scr_drawloading); + LogVarAdr("scr_engineevent_loadingstarted", scr_engineevent_loadingstarted); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - SCR_BeginLoadingPlaque = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 0F 29 74 24 ?? 48 8B F9"); - // 0x14022A4A0 // 48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 30 0F 29 74 24 ? 48 8B F9 // -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - SCR_BeginLoadingPlaque = g_GameDll.FindPatternSIMD("48 83 EC 38 0F 29 74 24 ?? 48 89 5C 24 ??"); - // 0x14022A4A0 // 48 83 EC 38 0F 29 74 24 ? 48 89 5C 24 ? // -#endif + g_GameDll.FindPatternSIMD("48 83 EC 38 0F 29 74 24 ?? 48 89 5C 24 ??").GetPtr(v_SCR_BeginLoadingPlaque); } virtual void GetVar(void) const { scr_drawloading = g_GameDll.FindPatternSIMD("0F B6 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 48 83 EC 28").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - scr_engineevent_loadingstarted = SCR_BeginLoadingPlaque.Offset(0x130).FindPatternSelf("C6 05 ?? ?? ?? ?? 01", CMemory::Direction::DOWN).ResolveRelativeAddress(0x2, 0x7).RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - scr_engineevent_loadingstarted = SCR_BeginLoadingPlaque.Offset(0x60).FindPatternSelf("C6 05 ?? ?? ?? ?? 01", CMemory::Direction::DOWN).ResolveRelativeAddress(0x2, 0x7).RCast(); -#endif - + scr_engineevent_loadingstarted = CMemory(v_SCR_BeginLoadingPlaque).Offset(0x60).FindPatternSelf("C6 05 ?? ?? ?? ?? 01", CMemory::Direction::DOWN).ResolveRelativeAddress(0x2, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/host.cpp b/r5dev/engine/host.cpp index b878cdeb..0ffb69b5 100644 --- a/r5dev/engine/host.cpp +++ b/r5dev/engine/host.cpp @@ -9,9 +9,49 @@ #include "tier0/frametask.h" #include "engine/host.h" #ifndef DEDICATED +#include "windows/id3dx.h" +#include "geforce/reflex.h" #include "vgui/vgui_debugpanel.h" +#include #endif // !DEDICATED +CCommonHostState* g_pCommonHostState = nullptr; + +void CCommonHostState::SetWorldModel(model_t* pModel) +{ + if (worldmodel == pModel) + return; + + worldmodel = pModel; + if (pModel) + { + worldbrush = pModel->brush.pShared; + } + else + { + worldbrush = NULL; + } +} + +/* +================== +Host_CountRealTimePackets + +Counts the number of +packets in non-prescaled +clock frames (does not +count for bots or Terminal +Services environments) +================== +*/ +void Host_CountRealTimePackets() +{ + v_Host_CountRealTimePackets(); +#ifndef DEDICATED + GFX_SetLatencyMarker(D3D11Device(), SIMULATION_START, MaterialSystem()->GetCurrentFrameCount()); +#endif // !DEDICATED +} + /* ================== _Host_RunFrame @@ -21,24 +61,24 @@ Runs all active servers */ void _Host_RunFrame(void* unused, float time) { - for (IFrameTask* const& task : g_FrameTasks) + for (IFrameTask* const& task : g_TaskQueueList) { task->RunFrame(); } - g_FrameTasks.erase(std::remove_if(g_FrameTasks.begin(), g_FrameTasks.end(), [](const IFrameTask* task) + g_TaskQueueList.erase(std::remove_if(g_TaskQueueList.begin(), g_TaskQueueList.end(), [](const IFrameTask* task) { return task->IsFinished(); - }), g_FrameTasks.end()); + }), g_TaskQueueList.end()); #ifndef DEDICATED - g_pOverlay->ShouldDraw(time); + g_TextOverlay.ShouldDraw(time); #endif // !DEDICATED return v_Host_RunFrame(unused, time); } -void _Host_Error(char* error, ...) +void _Host_Error(const char* error, ...) { char buf[1024]; {///////////////////////////// @@ -56,20 +96,12 @@ void _Host_Error(char* error, ...) } /////////////////////////////////////////////////////////////////////////////// -void VHost::Attach() const +void VHost::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_Host_RunFrame, &_Host_RunFrame); + DetourSetup(&v_Host_RunFrame, &_Host_RunFrame, bAttach); + DetourSetup(&v_Host_CountRealTimePackets, &Host_CountRealTimePackets, bAttach); #ifndef DEDICATED // Dedicated already logs this! - DetourAttach((LPVOID*)&v_Host_Error, &_Host_Error); + DetourSetup(&v_Host_Error, &_Host_Error, bAttach); #endif // !DEDICATED } - -void VHost::Detach() const -{ - DetourDetach((LPVOID*)&v_Host_RunFrame, &_Host_RunFrame); - -#ifndef DEDICATED // Dedicated already logs this! - DetourDetach((LPVOID*)&v_Host_Error, &_Host_Error); -#endif // !DEDICATED -} \ No newline at end of file diff --git a/r5dev/engine/host.h b/r5dev/engine/host.h index 68f7ab36..345cffcc 100644 --- a/r5dev/engine/host.h +++ b/r5dev/engine/host.h @@ -1,86 +1,93 @@ #pragma once +#include "engine/gl_model_private.h" -inline CMemory p_Host_RunFrame; inline void(*v_Host_RunFrame)(void* unused, float time); - -//inline CMemory p_Host_RunFrame_Render; // DEDICATED PATCH! -//inline void(*v_Host_RunFrame_Render)(void); - -inline CMemory p_Host_ShouldRun; +inline void(*v_Host_RunFrame_Render)(void); +inline void(*v_Host_CountRealTimePackets)(void); inline bool(*v_Host_ShouldRun)(); - -inline CMemory p_Host_Error; inline void(*v_Host_Error)(const char* error, ...); - -//inline CMemory p_VCR_EnterPausedState; // DEDICATED PATCH! //inline void(*v_VCR_EnterPausedState)(void); inline bool* g_bAbortServerSet = nullptr; -inline float* interval_per_tick = nullptr; inline jmp_buf* host_abortserver = nullptr; inline bool* host_initialized = nullptr; inline float* host_frametime_unbounded = nullptr; inline float* host_frametime_stddeviation = nullptr; +class CCommonHostState +{ +public: + CCommonHostState() + : worldmodel(NULL) + , worldbrush(NULL) + , interval_per_tick(0.0f) + , max_splitscreen_players(1) + , max_splitscreen_players_clientdll(1) + {} + + // cl_entitites[0].model + model_t* worldmodel; + struct worldbrushdata_t* worldbrush; + // Tick interval for game + float interval_per_tick; + // 1, unless a game supports split screen, then probably 2 or 4 (4 is the max allowable) + int max_splitscreen_players; + // This is the # the client .dll thinks is the max, it might be > max_splitscreen_players in -tools mode, etc. + int max_splitscreen_players_clientdll; + void SetWorldModel(model_t* pModel); +}; + +extern CCommonHostState* g_pCommonHostState; + /////////////////////////////////////////////////////////////////////////////// class VHost : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("_Host_RunFrame", p_Host_RunFrame.GetPtr()); - //LogFunAdr("_Host_RunFrame_Render", p_Host_RunFrame_Render.GetPtr()); - LogFunAdr("Host_ShouldRun", p_Host_ShouldRun.GetPtr()); - LogFunAdr("Host_Error", p_Host_Error.GetPtr()); - //LogFunAdr("VCR_EnterPausedState", p_VCR_EnterPausedState.GetPtr()); - LogVarAdr("g_bAbortServerSet", reinterpret_cast(g_bAbortServerSet)); - LogVarAdr("interval_per_tick", reinterpret_cast(interval_per_tick)); - LogVarAdr("host_abortserver", reinterpret_cast(host_abortserver)); - LogVarAdr("host_initialized", reinterpret_cast(host_initialized)); - LogVarAdr("host_frametime_unbounded", reinterpret_cast(host_frametime_unbounded)); - LogVarAdr("host_frametime_stddeviation", reinterpret_cast(host_frametime_stddeviation)); + LogFunAdr("_Host_RunFrame", v_Host_RunFrame); + LogFunAdr("_Host_RunFrame_Render", v_Host_RunFrame_Render); + LogFunAdr("Host_CountRealTimePackets", v_Host_CountRealTimePackets); + LogFunAdr("Host_ShouldRun", v_Host_ShouldRun); + LogFunAdr("Host_Error", v_Host_Error); + //LogFunAdr("VCR_EnterPausedState", v_VCR_EnterPausedState); + LogVarAdr("g_CommonHostState", g_pCommonHostState); + LogVarAdr("g_bAbortServerSet", g_bAbortServerSet); + LogVarAdr("host_abortserver", host_abortserver); + LogVarAdr("host_initialized", host_initialized); + LogVarAdr("host_frametime_unbounded", host_frametime_unbounded); + LogVarAdr("host_frametime_stddeviation", host_frametime_stddeviation); } virtual void GetFun(void) const { - p_Host_RunFrame = g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 18 48 89 70 20 F3 0F 11 48 ??"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - //p_Host_RunFrame_Render = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B 1D ?? ?? ?? ?? 33 FF"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - //p_Host_RunFrame_Render = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 34"); -#endif - p_Host_ShouldRun = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 83 78 6C 00 75 07 B0 01"); - p_Host_Error = g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 48 89 54 24 ?? 4C 89 44 24 ?? 4C 89 4C 24 ?? 53 57 48 81 EC ?? ?? ?? ??"); - //p_VCR_EnterPausedState = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 65 48 8B 04 25 ?? ?? ?? ?? BB ?? ?? ?? ?? C6 05 ?? ?? ?? ?? ??"); - - v_Host_RunFrame = p_Host_RunFrame.RCast(); - //v_Host_RunFrame_Render = p_Host_Error.RCast(); - v_Host_ShouldRun = p_Host_ShouldRun.RCast(); - v_Host_Error = p_Host_Error.RCast(); - //v_VCR_EnterPausedState = p_VCR_EnterPausedState.RCast(); + g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 18 48 89 70 20 F3 0F 11 48 ??").GetPtr(v_Host_RunFrame); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 34").GetPtr(v_Host_RunFrame_Render); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 65 48 8B 04 25 ?? ?? ?? ?? 33 DB").GetPtr(v_Host_CountRealTimePackets); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 83 78 6C 00 75 07 B0 01").GetPtr(v_Host_ShouldRun); + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 48 89 54 24 ?? 4C 89 44 24 ?? 4C 89 4C 24 ?? 53 57 48 81 EC ?? ?? ?? ??").GetPtr(v_Host_Error); + //g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 65 48 8B 04 25 ?? ?? ?? ?? BB ?? ?? ?? ?? C6 05 ?? ?? ?? ?? ??").GetPtr(v_VCR_EnterPausedState); } virtual void GetVar(void) const { - interval_per_tick = g_GameDll.FindPatternSIMD("4C 8B DC 4D 89 4B 20 55 56 41 54").FindPatternSelf("F3 0F 5E", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x4, 0x8).RCast(); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_bAbortServerSet = p_Host_Error.FindPattern("40 38 3D", CMemory::Direction::DOWN, 512, 2).ResolveRelativeAddress(3, 7).RCast(); - host_abortserver = p_Host_Error.FindPattern("48 8D 0D", CMemory::Direction::DOWN, 512, 3).ResolveRelativeAddress(3, 7).RCast(); + g_pCommonHostState = g_GameDll.FindPatternSIMD("48 83 EC 28 84 C9 75 0B") + .FindPatternSelf("48 8B 15").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - static const int n_host_frametime_unbounded_search_offset = 0x380; - static const int n_host_frametime_stddeviation_search_offset = 0x1200; -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - g_bAbortServerSet = p_Host_Error.FindPattern("40 38 3D", CMemory::Direction::DOWN, 512, 4).ResolveRelativeAddress(3, 7).RCast(); - host_abortserver = p_Host_Error.FindPattern("48 8D 0D", CMemory::Direction::DOWN, 512, 5).ResolveRelativeAddress(3, 7).RCast(); + const CMemory hostErrorBase(v_Host_Error); - static const int n_host_initialized_search_offset = 0x500; // TODO: S1!!! + g_bAbortServerSet = hostErrorBase.FindPattern("40 38 3D", CMemory::Direction::DOWN, 512, 4).ResolveRelativeAddress(3, 7).RCast(); + host_abortserver = hostErrorBase.FindPattern("48 8D 0D", CMemory::Direction::DOWN, 512, 5).ResolveRelativeAddress(3, 7).RCast(); + + static const int n_host_initialized_search_offset = 0x500; static const int n_host_frametime_unbounded_search_offset = 0x330; static const int n_host_frametime_stddeviation_search_offset = 0xFAA; -#endif - host_initialized = p_Host_RunFrame.Offset(n_host_initialized_search_offset).FindPatternSelf("44 38").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - host_frametime_unbounded = p_Host_RunFrame.Offset(n_host_frametime_unbounded_search_offset).FindPatternSelf("F3 0F 11").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); - host_frametime_stddeviation = p_Host_RunFrame.Offset(n_host_frametime_stddeviation_search_offset).FindPatternSelf("F3 0F 11").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); + + const CMemory hostRunFrameBase(v_Host_RunFrame); + + host_initialized = hostRunFrameBase.Offset(n_host_initialized_search_offset).FindPatternSelf("44 38").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + host_frametime_unbounded = hostRunFrameBase.Offset(n_host_frametime_unbounded_search_offset).FindPatternSelf("F3 0F 11").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); + host_frametime_stddeviation = hostRunFrameBase.Offset(n_host_frametime_stddeviation_search_offset).FindPatternSelf("F3 0F 11").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/host_cmd.cpp b/r5dev/engine/host_cmd.cpp index ceca0964..9845dfe9 100644 --- a/r5dev/engine/host_cmd.cpp +++ b/r5dev/engine/host_cmd.cpp @@ -62,7 +62,6 @@ void Host_Status_PrintClient(CClient* client, bool bShowAddress, void (*print) ( //print("\n"); } -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) /* ================== DFS_InitializeFeatureFlagDefinitions @@ -78,25 +77,13 @@ bool DFS_InitializeFeatureFlagDefinitions(const char* pszFeatureFlags) return v_DFS_InitializeFeatureFlagDefinitions(pszFeatureFlags); } -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) /////////////////////////////////////////////////////////////////////////////// -void VHostCmd::Attach() const +void VHostCmd::Detour(const bool bAttach) const { - DetourAttach(&v_Host_Shutdown, &Host_Shutdown); - DetourAttach(&v_Host_Status_PrintClient, &Host_Status_PrintClient); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - DetourAttach(&v_DFS_InitializeFeatureFlagDefinitions, &DFS_InitializeFeatureFlagDefinitions); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) -} - -void VHostCmd::Detach() const -{ - DetourDetach(&v_Host_Shutdown, &Host_Shutdown); - DetourDetach(&v_Host_Status_PrintClient, &Host_Status_PrintClient); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - DetourDetach(&v_DFS_InitializeFeatureFlagDefinitions, &DFS_InitializeFeatureFlagDefinitions); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) + DetourSetup(&v_Host_Shutdown, &Host_Shutdown, bAttach); + DetourSetup(&v_Host_Status_PrintClient, &Host_Status_PrintClient, bAttach); + DetourSetup(&v_DFS_InitializeFeatureFlagDefinitions, &DFS_InitializeFeatureFlagDefinitions, bAttach); } /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/host_cmd.h b/r5dev/engine/host_cmd.h index aafdce52..6cb0786b 100644 --- a/r5dev/engine/host_cmd.h +++ b/r5dev/engine/host_cmd.h @@ -18,31 +18,17 @@ struct EngineParms_t extern EngineParms_t* g_pEngineParms; /* ==== HOST ============================================================================================================================================================ */ -inline CMemory p_Host_Init; -inline void*(*v_Host_Init)(bool* bDedicated); - -inline CMemory p_Host_Shutdown; +inline void(*v_Host_Init)(); +inline void(*v_Host_Init_DuringVideo)(bool* bDedicated); +inline void(*v_Host_Init_PostVideo)(bool* bDedicated); inline void(*v_Host_Shutdown)(); - -inline CMemory p_Host_NewGame; inline bool(*v_Host_NewGame)(char* pszMapName, char* pszMapGroup, bool bLoadGame, char bBackground, LARGE_INTEGER PerformanceCount); - -inline CMemory p_Host_Disconnect; inline void(*v_Host_Disconnect)(bool bShowMainMenu); - -inline CMemory p_Host_ChangeLevel; inline bool(*v_Host_ChangeLevel)(bool bLoadFromSavedGame, const char* pszMapName, const char* pszMapGroup); - -inline CMemory p_Host_Status_PrintClient; inline void (*v_Host_Status_PrintClient)(CClient* client, bool bShowAddress, void (*print) (const char* fmt, ...)); -inline CMemory p_SetLaunchOptions; inline int(*v_SetLaunchOptions)(const CCommand& args); - -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) -inline CMemory p_DFS_InitializeFeatureFlagDefinitions; inline bool(*v_DFS_InitializeFeatureFlagDefinitions)(const char* pszFeatureFlags); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) extern EngineParms_t* g_pEngineParms; @@ -51,57 +37,39 @@ class VHostCmd : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Host_Init", p_Host_Init.GetPtr()); - LogFunAdr("Host_Shutdown", p_Host_Shutdown.GetPtr()); - LogFunAdr("Host_Disconnect", p_Host_Disconnect.GetPtr()); - LogFunAdr("Host_NewGame", p_Host_NewGame.GetPtr()); - LogFunAdr("Host_ChangeLevel", p_Host_ChangeLevel.GetPtr()); - LogFunAdr("Host_Status_PrintClient", p_Host_Status_PrintClient.GetPtr()); - LogFunAdr("SetLaunchOptions", p_SetLaunchOptions.GetPtr()); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - LogFunAdr("DFS_InitializeFeatureFlagDefinitions", p_DFS_InitializeFeatureFlagDefinitions.GetPtr()); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) - LogVarAdr("g_pEngineParms", reinterpret_cast(g_pEngineParms)); + LogFunAdr("Host_Init", v_Host_Init); + LogFunAdr("Host_Init_DuringVideo", v_Host_Init_DuringVideo); + LogFunAdr("Host_Init_PostVideo", v_Host_Init_PostVideo); + LogFunAdr("Host_Shutdown", v_Host_Shutdown); + LogFunAdr("Host_Disconnect", v_Host_Disconnect); + LogFunAdr("Host_NewGame", v_Host_NewGame); + LogFunAdr("Host_ChangeLevel", v_Host_ChangeLevel); + LogFunAdr("Host_Status_PrintClient", v_Host_Status_PrintClient); + LogFunAdr("SetLaunchOptions", v_SetLaunchOptions); + + LogFunAdr("DFS_InitializeFeatureFlagDefinitions", v_DFS_InitializeFeatureFlagDefinitions); + LogVarAdr("g_pEngineParms", g_pEngineParms); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_Host_Init = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8B D9 FF 15 ?? ?? ?? ??"); - p_Host_NewGame = g_GameDll.FindPatternSIMD("48 8B C4 56 41 54 41 57 48 81 EC ?? ?? ?? ?? F2 0F 10 05 ?? ?? ?? ??"); - p_Host_Disconnect = g_GameDll.FindPatternSIMD("48 83 EC 38 48 89 7C 24 ?? 0F B6 F9"); - p_Host_ChangeLevel = g_GameDll.FindPatternSIMD("40 53 56 41 56 48 81 EC ?? ?? ?? ?? 49 8B D8"); - p_SetLaunchOptions = g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 57 48 83 EC 20 48 8B E9 48 8B 0D ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_Host_Init = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B D9"); - p_Host_NewGame = g_GameDll.FindPatternSIMD("48 8B C4 ?? 41 54 41 55 48 81 EC 70 04 ?? ?? F2 0F 10 05 ?? ?? ?? 0B"); - p_Host_Disconnect = g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 0F B6 D9"); - p_Host_ChangeLevel = g_GameDll.FindPatternSIMD("40 56 57 41 56 48 81 EC ?? ?? ?? ??"); - p_SetLaunchOptions = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 83 EC 20 48 8B 1D ?? ?? ?? ?? 48 8B E9 48 85 DB"); -#endif - p_Host_Shutdown = g_GameDll.FindPatternSIMD("48 8B C4 48 83 EC ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 8B 15 ?? ?? ?? ??"); - p_Host_Status_PrintClient = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 60 48 8B A9 ?? ?? ?? ??"); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) - p_DFS_InitializeFeatureFlagDefinitions = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 40 38 3D ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B CE").FollowNearCallSelf(); - v_DFS_InitializeFeatureFlagDefinitions = p_DFS_InitializeFeatureFlagDefinitions.RCast(); /*48 8B C4 55 53 48 8D 68 E8*/ -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) - v_Host_Init = p_Host_Init.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B D9*/ - v_Host_Shutdown = p_Host_Shutdown.RCast(); - v_Host_NewGame = p_Host_NewGame.RCast(); /*48 8B C4 ?? 41 54 41 55 48 81 EC 70 04 00 00 F2 0F 10 05 ?? ?? ?? 0B*/ - v_Host_Disconnect = p_Host_Disconnect.RCast(); - v_Host_ChangeLevel = p_Host_ChangeLevel.RCast(); /*40 56 57 41 56 48 81 EC ?? ?? ?? ??*/ - v_Host_Status_PrintClient = p_Host_Status_PrintClient.RCast(); - v_SetLaunchOptions = p_SetLaunchOptions.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 83 EC 20 48 8B 1D ?? ?? ?? ?? 48 8B E9 48 85 DB*/ + g_GameDll.FindPatternSIMD("88 4C 24 08 53 55 56 57 48 83 EC 68").GetPtr(v_Host_Init); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B D9").GetPtr(v_Host_Init_DuringVideo); + g_GameDll.FindPatternSIMD("48 8B C4 ?? 41 54 41 55 48 81 EC 70 04 ?? ?? F2 0F 10 05 ?? ?? ?? 0B").GetPtr(v_Host_NewGame); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 0F B6 D9").GetPtr(v_Host_Disconnect); + g_GameDll.FindPatternSIMD("40 56 57 41 56 48 81 EC ?? ?? ?? ??").GetPtr(v_Host_ChangeLevel); + g_GameDll.FindPatternSIMD("48 8B C4 41 56 48 81 EC ?? ?? ?? ?? 45 33 F6").GetPtr(v_Host_Init_PostVideo); + g_GameDll.FindPatternSIMD("48 8B C4 48 83 EC ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 8B 15 ?? ?? ?? ??").GetPtr(v_Host_Shutdown); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 60 48 8B A9 ?? ?? ?? ??").GetPtr(v_Host_Status_PrintClient); + + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 83 EC 20 48 8B 1D ?? ?? ?? ?? 48 8B E9 48 85 DB").GetPtr(v_SetLaunchOptions); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 40 38 3D ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B CE").FollowNearCallSelf().GetPtr(v_DFS_InitializeFeatureFlagDefinitions); } virtual void GetVar(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_pEngineParms = p_CModAppSystemGroup_Main.FindPattern("48 8B", CMemory::Direction::DOWN, 100).ResolveRelativeAddress(0x3, 0x7).RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - g_pEngineParms = p_CModAppSystemGroup_Main.FindPattern("4C 8B", CMemory::Direction::DOWN, 100).ResolveRelativeAddress(0x3, 0x7).RCast(); -#endif + g_pEngineParms = CMemory(CModAppSystemGroup__Main).FindPattern("48 8B", CMemory::Direction::DOWN, 100).ResolveRelativeAddress(0x3, 0x7).RCast(); + g_pEngineParms = CMemory(CModAppSystemGroup__Main).FindPattern("4C 8B", CMemory::Direction::DOWN, 100).ResolveRelativeAddress(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp index f896730f..09213da5 100644 --- a/r5dev/engine/host_state.cpp +++ b/r5dev/engine/host_state.cpp @@ -10,10 +10,10 @@ #include "tier0/jobthread.h" #include "tier0/commandline.h" #include "tier0/fasttimer.h" +#include "tier0/frametask.h" #include "tier1/cvar.h" #include "tier1/NetAdr.h" #include "tier2/socketcreator.h" -#include "vpc/keyvalues.h" #include "datacache/mdlcache.h" #ifndef CLIENT_DLL #include "engine/server/sv_rcon.h" @@ -35,17 +35,19 @@ #include "engine/cmodel_bsp.h" #ifndef CLIENT_DLL #include "engine/server/server.h" +#include "rtech/liveapi/liveapi.h" #endif // !CLIENT_DLL -#include "rtech/rtech_game.h" -#include "rtech/rtech_utils.h" #include "rtech/stryder/stryder.h" +#include "rtech/playlists/playlists.h" #ifndef DEDICATED #include "vgui/vgui_baseui_interface.h" #include "client/vengineclient_impl.h" +#include "gameui/imgui_system.h" #endif // DEDICATED #include "networksystem/pylon.h" #ifndef CLIENT_DLL #include "networksystem/bansystem.h" +#include "networksystem/hostmanager.h" #endif // !CLIENT_DLL #include "networksystem/listmanager.h" #include "public/edict.h" @@ -55,44 +57,84 @@ #include "game/shared/vscript_shared.h" #ifndef CLIENT_DLL +static ConVar sv_pylonVisibility("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", "0 = Offline, 1 = Hidden, 2 = Public."); +static ConVar sv_pylonRefreshRate("sv_pylonRefreshRate", "5.0", FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds)."); + +static ConVar sv_autoReloadRate("sv_autoReloadRate", "0", FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null)."); +#endif // !CLIENT_DLL + +#ifdef DEDICATED +static ConVar hostdesc("hostdesc", "", FCVAR_RELEASE, "Host game server description."); //----------------------------------------------------------------------------- // Purpose: Send keep alive request to Pylon Master Server. -// Input : &netGameServer - // Output : Returns true on success, false otherwise. //----------------------------------------------------------------------------- -bool HostState_KeepAlive(const NetGameServer_t& netGameServer) +static void HostState_KeepAlive() { - if (!g_pServer->IsActive() || !sv_pylonVisibility->GetBool()) // Check for active game. + if (!g_pServer->IsActive() || !sv_pylonVisibility.GetBool()) // Check for active game. { - return false; + return; } - string errorMsg; - string hostToken; - - const bool result = g_pMasterServer->PostServerHost(errorMsg, hostToken, netGameServer); - if (!result) + const NetGameServer_t gameServer { - if (!errorMsg.empty() && g_pMasterServer->GetCurrentError().compare(errorMsg) != NULL) + hostname->GetString(), + hostdesc.GetString(), + sv_pylonVisibility.GetInt() == ServerVisibility_e::HIDDEN, + g_pHostState->m_levelName, + v_Playlists_GetCurrent(), + hostip->GetString(), + hostport->GetInt(), + g_pNetKey->GetBase64NetKey(), + *g_nServerRemoteChecksum, + SDK_VERSION, + g_pServer->GetNumClients(), + g_ServerGlobalVariables->m_nMaxClients, + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ).count() + }; + + std::thread request([&, gameServer] { - g_pMasterServer->SetCurrentError(errorMsg); - Error(eDLL_T::SERVER, NO_ERROR, "%s\n", errorMsg.c_str()); - } - } - else // Attempt to log the token, if there is one. - { - if (!hostToken.empty() && g_pMasterServer->GetCurrentToken().compare(hostToken) != NULL) - { - g_pMasterServer->SetCurrentToken(hostToken); - DevMsg(eDLL_T::SERVER, "Published server with token: %s'%s%s%s'\n", - g_svReset, g_svGreyB, - hostToken.c_str(), g_svReset); - } - } + string errorMsg; + string hostToken; + string hostIp; - return result; + const bool result = g_MasterServer.PostServerHost(errorMsg, hostToken, hostIp, gameServer); + + // Apply the data the next frame + g_TaskQueue.Dispatch([result, errorMsg, hostToken, hostIp] + { + if (!result) + { + if (!errorMsg.empty() && g_ServerHostManager.GetCurrentError().compare(errorMsg) != NULL) + { + g_ServerHostManager.SetCurrentError(errorMsg); + Error(eDLL_T::SERVER, NO_ERROR, "%s\n", errorMsg.c_str()); + } + } + else // Attempt to log the token, if there is one. + { + if (!hostToken.empty() && g_ServerHostManager.GetCurrentToken().compare(hostToken) != NULL) + { + g_ServerHostManager.SetCurrentToken(hostToken); + Msg(eDLL_T::SERVER, "Published server with token: %s'%s%s%s'\n", + g_svReset, g_svGreyB, + hostToken.c_str(), g_svReset); + } + } + + if (hostIp.length() != 0) + g_ServerHostManager.SetHostIP(hostIp); + + }, 0); + } + ); + + request.detach(); } -#endif // !CLIENT_DLL +#endif // DEDICATED //----------------------------------------------------------------------------- // Purpose: state machine's main processing loop @@ -115,8 +157,6 @@ void CHostState::FrameUpdate(CHostState* pHostState, double flCurrentTime, float RCONClient()->RunFrame(); #endif // !DEDICATED - HostStates_t oldState{}; - // Disable "warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable" #pragma warning(push) #pragma warning(disable : 4611) @@ -131,11 +171,11 @@ void CHostState::FrameUpdate(CHostState* pHostState, double flCurrentTime, float #ifndef CLIENT_DLL *g_bAbortServerSet = true; #endif // !CLIENT_DLL - do + while (true) { Cbuf_Execute(); - oldState = g_pHostState->m_iCurrentState; + const HostStates_t oldState = g_pHostState->m_iCurrentState; switch (g_pHostState->m_iCurrentState) { case HostStates_t::HS_NEW_GAME: @@ -168,32 +208,32 @@ void CHostState::FrameUpdate(CHostState* pHostState, double flCurrentTime, float bResetIdleName = true; } - CHostState_State_Run(&g_pHostState->m_iCurrentState, flCurrentTime, flFrameTime); + CHostState__State_Run(&g_pHostState->m_iCurrentState, flCurrentTime, flFrameTime); break; } case HostStates_t::HS_GAME_SHUTDOWN: { - DevMsg(eDLL_T::ENGINE, "%s: Shutdown host game\n", __FUNCTION__); - CHostState_State_GameShutDown(g_pHostState); + Msg(eDLL_T::ENGINE, "%s: Shutdown host game\n", __FUNCTION__); + CHostState__State_GameShutDown(g_pHostState); break; } case HostStates_t::HS_RESTART: { - DevMsg(eDLL_T::ENGINE, "%s: Restarting state machine\n", __FUNCTION__); + Msg(eDLL_T::ENGINE, "%s: Restarting state machine\n", __FUNCTION__); #ifndef DEDICATED - CL_EndMovie(); + v_CL_EndMovie(); #endif // !DEDICATED - Stryder_SendOfflineRequest(); // We have hostnames nulled anyway. + v_Stryder_SendOfflineRequest(); // We have hostnames nulled anyway. g_pEngine->SetNextState(IEngine::DLL_RESTART); break; } case HostStates_t::HS_SHUTDOWN: { - DevMsg(eDLL_T::ENGINE, "%s: Shutdown state machine\n", __FUNCTION__); + Msg(eDLL_T::ENGINE, "%s: Shutdown state machine\n", __FUNCTION__); #ifndef DEDICATED - CL_EndMovie(); + v_CL_EndMovie(); #endif // !DEDICATED - Stryder_SendOfflineRequest(); // We have hostnames nulled anyway. + v_Stryder_SendOfflineRequest(); // We have hostnames nulled anyway. g_pEngine->SetNextState(IEngine::DLL_CLOSE); break; } @@ -203,10 +243,15 @@ void CHostState::FrameUpdate(CHostState* pHostState, double flCurrentTime, float } } - } while ( - (oldState != HostStates_t::HS_RUN || g_pHostState->m_iNextState == HostStates_t::HS_LOAD_GAME && single_frame_shutdown_for_reload->GetBool()) - && oldState != HostStates_t::HS_SHUTDOWN - && oldState != HostStates_t::HS_RESTART); + // only do a single pass at HS_RUN per frame. All other states loop until they reach HS_RUN + if (oldState == HostStates_t::HS_RUN && (g_pHostState->m_iNextState != HostStates_t::HS_LOAD_GAME || !single_frame_shutdown_for_reload->GetBool())) + break; + + // shutting down + if (oldState == HostStates_t::HS_SHUTDOWN || + oldState == HostStates_t::HS_RESTART) + break; + } } } @@ -219,7 +264,7 @@ void CHostState::Init(void) { if (m_iNextState == HostStates_t::HS_GAME_SHUTDOWN) { - CHostState_State_GameShutDown(this); + CHostState__State_GameShutDown(this); } else { @@ -250,7 +295,7 @@ void CHostState::Setup(void) { g_pHostState->LoadConfig(); #ifndef CLIENT_DLL - g_pBanSystem->Load(); + g_BanSystem.LoadList(); #endif // !CLIENT_DLL ConVar_PurgeHostNames(); @@ -261,7 +306,11 @@ void CHostState::Setup(void) RCONClient()->Init(); #endif // !DEDICATED - if (net_useRandomKey->GetBool()) +#ifndef CLIENT_DLL + LiveAPISystem()->Init(); +#endif // !CLIENT_DLL + + if (net_useRandomKey.GetBool()) { NET_GenerateKey(); } @@ -301,50 +350,33 @@ void CHostState::Think(void) const #endif // DEDICATED bInitialized = true; } - if (sv_autoReloadRate->GetBool()) + if (sv_autoReloadRate.GetBool()) { - if (g_ServerGlobalVariables->m_flCurTime > sv_autoReloadRate->GetDouble()) + if (g_ServerGlobalVariables->m_flCurTime > sv_autoReloadRate.GetFloat()) { Cbuf_AddText(Cbuf_GetCurrentPlayer(), "reload\n", cmd_source_t::kCommandSrcCode); } } - if (statsTimer.GetDurationInProgress().GetSeconds() > sv_statusRefreshRate->GetDouble()) + if (statsTimer.GetDurationInProgress().GetSeconds() > sv_statusRefreshRate.GetFloat()) { - SetConsoleTitleA(Format("%s - %d/%d Players (%s on %s)", + SetConsoleTitleA(Format("%s - %d/%d Players (%s on %s) - %d%% Server CPU (%.3f msec on frame %d)", hostname->GetString(), g_pServer->GetNumClients(), - g_ServerGlobalVariables->m_nMaxClients, KeyValues_GetCurrentPlaylist(), m_levelName).c_str()); + g_ServerGlobalVariables->m_nMaxClients, v_Playlists_GetCurrent(), m_levelName, + static_cast(g_pServer->GetCPUUsage() * 100.0f), (g_pEngine->GetFrameTime() * 1000.0f), + g_pServer->GetTick()).c_str()); statsTimer.Start(); } - if (sv_globalBanlist->GetBool() && - banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate->GetDouble()) + if (sv_globalBanlist.GetBool() && + banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate.GetFloat()) { - SV_CheckForBan(); + SV_CheckClientsForBan(); banListTimer.Start(); } #ifdef DEDICATED - if (pylonTimer.GetDurationInProgress().GetSeconds() > sv_pylonRefreshRate->GetDouble()) + if (pylonTimer.GetDurationInProgress().GetSeconds() > sv_pylonRefreshRate.GetFloat()) { - const NetGameServer_t netGameServer - { - hostname->GetString(), - hostdesc->GetString(), - sv_pylonVisibility->GetInt() == EServerVisibility_t::HIDDEN, - g_pHostState->m_levelName, - KeyValues_GetCurrentPlaylist(), - hostip->GetString(), - hostport->GetString(), - g_pNetKey->GetBase64NetKey(), - std::to_string(*g_nServerRemoteChecksum), - SDK_VERSION, - std::to_string(g_pServer->GetNumClients()), - std::to_string(g_ServerGlobalVariables->m_nMaxClients), - std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch() - ).count() - }; - - std::thread(&HostState_KeepAlive, netGameServer).detach(); + HostState_KeepAlive(); pylonTimer.Start(); } #endif // DEDICATED @@ -388,6 +420,24 @@ void CHostState::LoadConfig(void) const } } +//----------------------------------------------------------------------------- +// Purpose: set state machine +// Input : newState - +// clearNext - +//----------------------------------------------------------------------------- +void CHostState::SetState(const HostStates_t newState) +{ + m_iCurrentState = newState; + + // If our next state isn't a shutdown, or its a forced shutdown then set + // next state to run. + if (m_iNextState != HostStates_t::HS_SHUTDOWN || + !host_hasIrreversibleShutdown->GetBool()) + { + m_iNextState = newState; + } +} + //----------------------------------------------------------------------------- // Purpose: shutdown active game //----------------------------------------------------------------------------- @@ -408,13 +458,13 @@ void CHostState::GameShutDown(void) //----------------------------------------------------------------------------- void CHostState::State_NewGame(void) { - DevMsg(eDLL_T::ENGINE, "%s: Loading level: '%s'\n", __FUNCTION__, g_pHostState->m_levelName); + Msg(eDLL_T::ENGINE, "%s: Loading level: '%s'\n", __FUNCTION__, g_pHostState->m_levelName); LARGE_INTEGER time{}; #ifndef CLIENT_DLL - bool bSplitScreenConnect = m_bSplitScreenConnect; - m_bSplitScreenConnect = 0; + const bool bSplitScreenConnect = m_bSplitScreenConnect; + m_bSplitScreenConnect = false; if (!g_pServerGameClients) // Init Game if it ain't valid. { @@ -434,13 +484,7 @@ void CHostState::State_NewGame(void) } #endif // !CLIENT_DLL - m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (m_iNextState != HostStates_t::HS_SHUTDOWN || !host_hasIrreversibleShutdown->GetBool()) - { - m_iNextState = HostStates_t::HS_RUN; - } + SetState(HostStates_t::HS_RUN); } //----------------------------------------------------------------------------- @@ -448,7 +492,7 @@ void CHostState::State_NewGame(void) //----------------------------------------------------------------------------- void CHostState::State_ChangeLevelSP(void) { - DevMsg(eDLL_T::ENGINE, "%s: Changing singleplayer level to: '%s'\n", __FUNCTION__, m_levelName); + Msg(eDLL_T::ENGINE, "%s: Changing singleplayer level to: '%s'\n", __FUNCTION__, m_levelName); m_flShortFrameTime = 1.5; // Set frame time. if (CModelLoader__Map_IsValid(g_pModelLoader, m_levelName)) // Check if map is valid and if we can start a new game. @@ -460,13 +504,8 @@ void CHostState::State_ChangeLevelSP(void) Error(eDLL_T::ENGINE, NO_ERROR, "%s: Unable to find level: '%s'\n", __FUNCTION__, m_levelName); } - m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (m_iNextState != HostStates_t::HS_SHUTDOWN || !host_hasIrreversibleShutdown->GetBool()) - { - m_iNextState = HostStates_t::HS_RUN; - } + // Set current state to run. + SetState(HostStates_t::HS_RUN); } //----------------------------------------------------------------------------- @@ -474,7 +513,7 @@ void CHostState::State_ChangeLevelSP(void) //----------------------------------------------------------------------------- void CHostState::State_ChangeLevelMP(void) { - DevMsg(eDLL_T::ENGINE, "%s: Changing multiplayer level to: '%s'\n", __FUNCTION__, m_levelName); + Msg(eDLL_T::ENGINE, "%s: Changing multiplayer level to: '%s'\n", __FUNCTION__, m_levelName); m_flShortFrameTime = 0.5; // Set frame time. #ifndef CLIENT_DLL @@ -492,13 +531,8 @@ void CHostState::State_ChangeLevelMP(void) Error(eDLL_T::ENGINE, NO_ERROR, "%s: Unable to find level: '%s'\n", __FUNCTION__, m_levelName); } - m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run. - - // If our next state isn't a shutdown or its a forced shutdown then set next state to run. - if (m_iNextState != HostStates_t::HS_SHUTDOWN || !host_hasIrreversibleShutdown->GetBool()) - { - m_iNextState = HostStates_t::HS_RUN; - } + // Set current state to run. + SetState(HostStates_t::HS_RUN); } //----------------------------------------------------------------------------- @@ -510,13 +544,9 @@ void CHostState::ResetLevelName(void) Q_snprintf(const_cast(m_levelName), sizeof(m_levelName), "%s", szNoMap); } -void VHostState::Attach(void) const +void VHostState::Detour(const bool bAttach) const { - DetourAttach(&CHostState_FrameUpdate, &CHostState::FrameUpdate); -} -void VHostState::Detach(void) const -{ - DetourDetach(&CHostState_FrameUpdate, &CHostState::FrameUpdate); + DetourSetup(&CHostState__FrameUpdate, &CHostState::FrameUpdate, bAttach); } /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/host_state.h b/r5dev/engine/host_state.h index 01089ec0..be1dbc0f 100644 --- a/r5dev/engine/host_state.h +++ b/r5dev/engine/host_state.h @@ -23,9 +23,10 @@ public: void Setup(void); void Think(void) const; + void SetState(const HostStates_t newState); void GameShutDown(void); - void State_NewGame(void); + void State_NewGame(void); void State_ChangeLevelSP(void); void State_ChangeLevelMP(void); @@ -50,16 +51,9 @@ public: }; /* ==== CHOSTSTATE ====================================================================================================================================================== */ -inline CMemory p_CHostState_FrameUpdate; -inline void(*CHostState_FrameUpdate)(CHostState* pHostState, double flCurrentTime, float flFrameTime); - -inline CMemory p_CHostState_State_Run; -inline void(*CHostState_State_Run)(HostStates_t* pState, double flCurrentTime, float flFrameTime); - -inline CMemory p_CHostState_State_GameShutDown; -inline void(*CHostState_State_GameShutDown)(CHostState* thisptr); - -inline CMemory p_HostState_ChangeLevelMP; +inline void(*CHostState__FrameUpdate)(CHostState* pHostState, double flCurrentTime, float flFrameTime); +inline void(*CHostState__State_Run)(HostStates_t* pState, double flCurrentTime, float flFrameTime); +inline void(*CHostState__State_GameShutDown)(CHostState* thisptr); inline void(*v_HostState_ChangeLevelMP)(char const* pNewLevel, char const* pLandmarkName); /////////////////////////////////////////////////////////////////////////////// @@ -70,36 +64,24 @@ class VHostState : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CHostState::FrameUpdate", p_CHostState_FrameUpdate.GetPtr()); - LogFunAdr("CHostState::State_Run", p_CHostState_State_Run.GetPtr()); - LogFunAdr("CHostState::State_GameShutDown", p_CHostState_State_GameShutDown.GetPtr()); - LogFunAdr("HostState_ChangeLevelMP", p_HostState_ChangeLevelMP.GetPtr()); - LogVarAdr("g_pHostState", reinterpret_cast(g_pHostState)); + LogFunAdr("CHostState::FrameUpdate", CHostState__FrameUpdate); + LogFunAdr("CHostState::State_Run", CHostState__State_Run); + LogFunAdr("CHostState::State_GameShutDown", CHostState__State_GameShutDown); + LogFunAdr("HostState_ChangeLevelMP", v_HostState_ChangeLevelMP); + LogVarAdr("g_pHostState", g_pHostState); } virtual void GetFun(void) const { - p_CHostState_FrameUpdate = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 20 F3 0F 11 54 24 18"); - p_CHostState_State_Run = g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 10 48 89 70 18 48 89 78 20 55 41 54 41 55 41 56 41 57 48 8D A8 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 0F 29 70 C8 45 33 E4"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CHostState_State_GameShutDown = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 56 48 83 EC 20 8B 05 ?? ?? ?? ?? 48 8B F1"); -#elif defined (GAMEDLL_S2) - p_CHostState_State_GameShutDown = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B 05 ?? ?? ?? ?? 33 FF 48 8B F1"); -#elif defined (GAMEDLL_S3) - p_CHostState_State_GameShutDown = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B D9 E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ??"); -#endif - p_HostState_ChangeLevelMP = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 48 8B F2 8B 0D ?? ?? ?? ??"); - - CHostState_FrameUpdate = p_CHostState_FrameUpdate.RCast(); /*48 89 5C 24 08 48 89 6C 24 20 F3 0F 11 54 24 18*/ - CHostState_State_Run = p_CHostState_State_Run.RCast(); /*48 8B C4 48 89 58 10 48 89 70 18 48 89 78 20 55 41 54 41 55 41 56 41 57 48 8D A8 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 0F 29 70 C8 45 33 E4*/ - CHostState_State_GameShutDown = p_CHostState_State_GameShutDown.RCast(); /*48 89 5C 24 ?? 57 48 83 EC 20 48 8B D9 E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ??*/ - v_HostState_ChangeLevelMP = p_HostState_ChangeLevelMP.RCast(); /*48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B F9 48 8B F2 8B 0D ? ? ? ?*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 20 F3 0F 11 54 24 18").GetPtr(CHostState__FrameUpdate); + g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 10 48 89 70 18 48 89 78 20 55 41 54 41 55 41 56 41 57 48 8D A8 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 0F 29 70 C8 45 33 E4").GetPtr(CHostState__State_Run); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B D9 E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ??").GetPtr(CHostState__State_GameShutDown); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 48 8B F2 8B 0D ?? ?? ?? ??").GetPtr(v_HostState_ChangeLevelMP); } virtual void GetVar(void) const { - g_pHostState = p_CHostState_FrameUpdate.FindPattern("48 8D ?? ?? ?? ?? 01", CMemory::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pHostState = CMemory(CHostState__FrameUpdate).FindPattern("48 8D ?? ?? ?? ?? 01", CMemory::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/keys.cpp b/r5dev/engine/keys.cpp new file mode 100644 index 00000000..92d4b43e --- /dev/null +++ b/r5dev/engine/keys.cpp @@ -0,0 +1,36 @@ +#include "keys.h" +#include "windows/id3dx.h" +#include "geforce/reflex.h" +#include + +KeyInfo_t* g_pKeyInfo = nullptr; +ButtonCode_t* g_pKeyEventTicks = nullptr; +short* g_nKeyEventCount = nullptr; + + +bool Input_Event(const InputEvent_t& inputEvent, const int noKeyUpCheck) +{ + bool runTriggerMarker = inputEvent.m_nData == ButtonCode_t::MOUSE_LEFT; + const KeyInfo_t& keyInfo = g_pKeyInfo[inputEvent.m_nData]; + + if (noKeyUpCheck) + { + const int v = (inputEvent.m_nType & 0xFFFFFFFD) == 0; + + if (keyInfo.m_nKeyDownTarget == v) + runTriggerMarker = false; + } + + if (runTriggerMarker && (inputEvent.m_nType != IE_ButtonReleased || keyInfo.m_bTrapKeyUp)) + { + GFX_SetLatencyMarker(D3D11Device(), TRIGGER_FLASH, MaterialSystem()->GetCurrentFrameCount()); + } + + return v_Input_Event(inputEvent, noKeyUpCheck); +} + +/////////////////////////////////////////////////////////////////////////////// +void VKeys::Detour(const bool bAttach) const +{ + DetourSetup(&v_Input_Event, &Input_Event, bAttach); +} diff --git a/r5dev/engine/keys.h b/r5dev/engine/keys.h new file mode 100644 index 00000000..094ed4b6 --- /dev/null +++ b/r5dev/engine/keys.h @@ -0,0 +1,81 @@ +#ifndef ENGINE_KEYS_H +#define ENGINE_KEYS_H +#include "inputsystem/ButtonCode.h" + +//----------------------------------------------------------------------------- +// Keypress event +//----------------------------------------------------------------------------- +struct KeyEvent_t +{ + const char* m_pCommand; + int m_nTick; + bool m_bDown; +}; + +//----------------------------------------------------------------------------- +// Current keypress state +//----------------------------------------------------------------------------- +struct KeyInfo_t +{ + enum + { + KEY_TAPPED_BIND = 0, + KEY_HELD_BIND, + + KEY_BIND_COUNT + }; + + const char* m_pKeyBinding[KEY_BIND_COUNT]; + int m_nKeyUpTarget; + int m_nKeyDownTarget; + + uint32_t m_nEventTick; // When was the event issued? + int unknown; + short m_nEventNumber; // The event number. + + bool m_bKeyDown; + bool m_bEventIsButtonKey; // Is the event a button key (< ButtonCode_t::KEY_LAST) + bool m_bTrapKeyUp; + bool m_bBoundSecondKey; // Is the key bound to the second row? + + short paddingMaybe; +}; + +inline bool (*v_Input_Event)(const InputEvent_t& inputEvent, const int noKeyUpCheck); +inline bool(*v_Key_Event)(const KeyEvent_t& keyEvent); + +extern KeyInfo_t* g_pKeyInfo; // ARRAYSIZE = ButtonCode_t::BUTTON_CODE_LAST +extern ButtonCode_t* g_pKeyEventTicks; // ARRAYSIZE = ButtonCode_t::BUTTON_CODE_LAST +extern short* g_nKeyEventCount; + +class VKeys : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("Input_Event", v_Input_Event); + LogFunAdr("Key_Event", v_Key_Event); + LogVarAdr("g_pKeyInfo", g_pKeyInfo); + LogVarAdr("g_pKeyEventTicks", g_pKeyEventTicks); + LogVarAdr("g_nKeyEventCount", g_nKeyEventCount); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 56").GetPtr(v_Input_Event); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 4C 63 41 08").GetPtr(v_Key_Event); + } + virtual void GetVar(void) const + { + g_pKeyInfo = g_GameDll.FindPatternSIMD("48 83 EC 28 33 D2 48 8D 0D ?? ?? ?? ?? 41 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 33 C0 C6 05 ?? ?? ?? ?? ??") + .FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 40).ResolveRelativeAddressSelf(3, 7).RCast(); + + CMemory l_EngineApi_PumpMessages = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 81 EC ?? ?? ?? ?? 45 33 C9"); + + // NOTE: g_nKeyEventCount's pattern is found earlier, thus searched for earlier to offset base for g_pKeyEventTicks. + g_nKeyEventCount = l_EngineApi_PumpMessages.FindPatternSelf("0F B7 15").ResolveRelativeAddressSelf(3, 7).RCast(); + g_pKeyEventTicks = l_EngineApi_PumpMessages.FindPatternSelf("48 8D 35").ResolveRelativeAddressSelf(3, 7).RCast(); + } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const; +}; + +#endif // ENGINE_KEYS_H diff --git a/r5dev/engine/matsys_interface.cpp b/r5dev/engine/matsys_interface.cpp index bb752c49..9d774f7a 100644 --- a/r5dev/engine/matsys_interface.cpp +++ b/r5dev/engine/matsys_interface.cpp @@ -26,12 +26,7 @@ bool UpdateCurrentVideoConfig(MaterialSystem_Config_t* pConfig) */ /////////////////////////////////////////////////////////////////////////////// -void VMatSys_Interface::Attach() const +void VMatSys_Interface::Detour(const bool bAttach) const { - //DetourAttach(&v_UpdateCurrentVideoConfig, &UpdateCurrentVideoConfig); + //DetourSetup(&v_UpdateCurrentVideoConfig, &UpdateCurrentVideoConfig, bAttach); } - -void VMatSys_Interface::Detach() const -{ - //DetourDetach(&v_UpdateCurrentVideoConfig, &UpdateCurrentVideoConfig); -} \ No newline at end of file diff --git a/r5dev/engine/matsys_interface.h b/r5dev/engine/matsys_interface.h index 12432969..9712560f 100644 --- a/r5dev/engine/matsys_interface.h +++ b/r5dev/engine/matsys_interface.h @@ -2,17 +2,16 @@ #define MATSYS_INTERFACE_H #include "public/imaterialsystem.h" +#include "public/inputsystem/ButtonCode.h" //------------------------------------------------------------------------- // RUNTIME: GAME_CFG //------------------------------------------------------------------------- -inline CMemory p_UpdateCurrentVideoConfig; -inline CMemory p_UpdateMaterialSystemConfig; -inline CMemory p_HandleConfigFile; -inline CMemory p_ResetPreviousGameState; -inline CMemory p_LoadPlayerConfig; - -inline bool(*v_UpdateCurrentVideoConfig)(MaterialSystem_Config_t* pConfig); +inline void(*v_UpdateMaterialSystemConfig)(void); +inline bool(*v_UpdateCurrentVideoConfig)(MaterialSystem_Config_t* const pConfig); +inline bool(*v_HandleConfigFile)(const int configType); //(saved games cfg) 0 = local, 1 = profile. +inline void(*v_ResetPreviousGameState)(void); +inline void(*v_LoadPlayerConfig)(ButtonCode_t buttonCode, void* unused); /////////////////////////////////////////////////////////////////////////////// @@ -20,30 +19,23 @@ class VMatSys_Interface : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("UpdateMaterialSystemConfig", p_UpdateMaterialSystemConfig.GetPtr()); - LogFunAdr("UpdateCurrentVideoConfig", p_UpdateCurrentVideoConfig.GetPtr()); - LogFunAdr("HandleConfigFile", p_HandleConfigFile.GetPtr()); - LogFunAdr("ResetPreviousGameState", p_ResetPreviousGameState.GetPtr()); - LogFunAdr("LoadPlayerConfig", p_LoadPlayerConfig.GetPtr()); + LogFunAdr("UpdateMaterialSystemConfig", v_UpdateMaterialSystemConfig); + LogFunAdr("UpdateCurrentVideoConfig", v_UpdateCurrentVideoConfig); + LogFunAdr("HandleConfigFile", v_HandleConfigFile); + LogFunAdr("ResetPreviousGameState", v_ResetPreviousGameState); + LogFunAdr("LoadPlayerConfig", v_LoadPlayerConfig); } virtual void GetFun(void) const { - p_UpdateMaterialSystemConfig = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 80 3D ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ??"); - p_UpdateCurrentVideoConfig = g_GameDll.FindPatternSIMD("40 55 ?? 41 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 4C 8B F1"); - p_HandleConfigFile = g_GameDll.FindPatternSIMD("40 56 48 81 EC ?? ?? ?? ?? 8B F1"); - p_ResetPreviousGameState = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 44 89 3D ?? ?? ?? ?? ?? 8B ?? 24 ??").ResolveRelativeAddressSelf(0x1, 0x5); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - p_LoadPlayerConfig = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 48 83 3D ?? ?? ?? ?? ?? 75 0C"); -#elif defined (GAMEDLL_S3) - p_LoadPlayerConfig = g_GameDll.FindPatternSIMD("E9 ?? ?? ?? ?? CC CC CC CC CC CC CC CC CC CC CC 40 53 48 83 EC 30 4D 8B D1").FollowNearCallSelf(); -#endif - - v_UpdateCurrentVideoConfig = p_UpdateCurrentVideoConfig.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 80 3D ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ??").GetPtr(v_UpdateMaterialSystemConfig); + g_GameDll.FindPatternSIMD("40 55 ?? 41 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 4C 8B F1").GetPtr(v_UpdateCurrentVideoConfig); + g_GameDll.FindPatternSIMD("40 56 48 81 EC ?? ?? ?? ?? 8B F1").GetPtr(v_HandleConfigFile); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 44 89 3D ?? ?? ?? ?? ?? 8B ?? 24 ??").ResolveRelativeAddressSelf(0x1, 0x5).GetPtr(v_ResetPreviousGameState); + g_GameDll.FindPatternSIMD("E9 ?? ?? ?? ?? CC CC CC CC CC CC CC CC CC CC CC 40 53 48 83 EC 30 4D 8B D1").FollowNearCallSelf().GetPtr(v_LoadPlayerConfig); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/modelinfo.h b/r5dev/engine/modelinfo.h index 5a5bbea6..3a2723f9 100644 --- a/r5dev/engine/modelinfo.h +++ b/r5dev/engine/modelinfo.h @@ -41,10 +41,10 @@ class VModelInfo : public IDetour virtual void GetAdr(void) const { #ifndef CLIENT_DLL - LogFunAdr("g_pModelInfoServer", reinterpret_cast(g_pModelInfoServer)); + LogFunAdr("g_pModelInfoServer", g_pModelInfoServer); #endif // CLIENT_DLL #ifndef DEDICATED - LogFunAdr("g_pModelInfoClient", reinterpret_cast(g_pModelInfoClient)); + LogFunAdr("g_pModelInfoClient", g_pModelInfoClient); #endif // DEDICATED } virtual void GetFun(void) const { } @@ -60,8 +60,7 @@ class VModelInfo : public IDetour #endif // DEDICATED } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/modelloader.cpp b/r5dev/engine/modelloader.cpp index b80e22b4..817127a3 100644 --- a/r5dev/engine/modelloader.cpp +++ b/r5dev/engine/modelloader.cpp @@ -280,7 +280,7 @@ void CMapLoadHelper::Constructor(CMapLoadHelper* loader, int lumpToLoad) FileHandle_t hLumpFile = FileSystem()->Open(lumpPathBuf, "rb"); if (hLumpFile != FILESYSTEM_INVALID_HANDLE) { - //DevMsg(eDLL_T::ENGINE, "Loading lump %.4x from file. Buffer: %p\n", lumpToLoad, loader->m_pRawData); + DevMsg(eDLL_T::ENGINE, "Loading lump %.4x from file. Buffer: %p\n", lumpToLoad, loader->m_pRawData); FileSystem()->ReadEx(loader->m_pRawData, lumpSize, lumpSize, hLumpFile); FileSystem()->Close(hLumpFile); @@ -345,20 +345,11 @@ void AddGameLump() } /////////////////////////////////////////////////////////////////////////////// -void VModelLoader::Attach() const +void VModelLoader::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&CModelLoader__LoadModel, &CModelLoader::LoadModel); - DetourAttach((LPVOID*)&CModelLoader__Map_LoadModelGuts, &CModelLoader::Map_LoadModelGuts); + DetourSetup(&CModelLoader__LoadModel, &CModelLoader::LoadModel, bAttach); + DetourSetup(&CModelLoader__Map_LoadModelGuts, &CModelLoader::Map_LoadModelGuts, bAttach); - DetourAttach((LPVOID*)&CMapLoadHelper__CMapLoadHelper, &CMapLoadHelper::Constructor); - DetourAttach((LPVOID*)&v_AddGameLump, &AddGameLump); -} - -void VModelLoader::Detach() const -{ - DetourDetach((LPVOID*)&CModelLoader__LoadModel, &CModelLoader::LoadModel); - DetourDetach((LPVOID*)&CModelLoader__Map_LoadModelGuts, &CModelLoader::Map_LoadModelGuts); - - DetourDetach((LPVOID*)&CMapLoadHelper__CMapLoadHelper, &CMapLoadHelper::Constructor); - DetourDetach((LPVOID*)&v_AddGameLump, &AddGameLump); + DetourSetup(&CMapLoadHelper__CMapLoadHelper, &CMapLoadHelper::Constructor, bAttach); + DetourSetup(&v_AddGameLump, &AddGameLump, bAttach); } diff --git a/r5dev/engine/modelloader.h b/r5dev/engine/modelloader.h index dec12e0c..b2604007 100644 --- a/r5dev/engine/modelloader.h +++ b/r5dev/engine/modelloader.h @@ -71,38 +71,21 @@ public: char m_szLumpFilename[260]; }; -inline CMemory p_CModelLoader__FindModel; inline void*(*CModelLoader__FindModel)(CModelLoader* loader, const char* pszModelName); - -inline CMemory p_CModelLoader__LoadModel; inline void(*CModelLoader__LoadModel)(CModelLoader* loader, model_t* model); - -inline CMemory p_CModelLoader__UnloadModel; inline uint64_t(*CModelLoader__UnloadModel)(CModelLoader* loader, model_t* model); - -inline CMemory p_CModelLoader__Studio_LoadModel; inline void*(*CModelLoader__Studio_LoadModel)(CModelLoader* loader); - -inline CMemory p_CModelLoader__Map_LoadModelGuts; inline uint64_t(*CModelLoader__Map_LoadModelGuts)(CModelLoader* loader, model_t* model); - -inline CMemory p_CModelLoader__Map_IsValid; inline bool(*CModelLoader__Map_IsValid)(CModelLoader* loader, const char* pszMapName); - -inline CMemory p_CMapLoadHelper__CMapLoadHelper; inline void(*CMapLoadHelper__CMapLoadHelper)(CMapLoadHelper * helper, int lumpToLoad); -inline CMemory p_AddGameLump; inline void(*v_AddGameLump)(void); - -inline CMemory p_Map_LoadModel; inline void(*v_Map_LoadModel)(void); -//inline CMemory p_GetSpriteInfo; // DEDICATED PATCH! -//inline void*(*GetSpriteInfo)(const char* pName, bool bIsAVI, bool bIsBIK, int& nWidth, int& nHeight, int& nFrameCount, void* a7); - -//inline CMemory p_BuildSpriteLoadName; // DEDICATED PATCH! -//inline void*(*BuildSpriteLoadName)(const char* pName, char* pOut, int outLen, bool& bIsAVI, bool& bIsBIK); +#ifndef DEDICATED +inline void*(*v_GetSpriteInfo)(const char* pName, bool bIsAVI, bool bIsBIK, int& nWidth, int& nHeight, int& nFrameCount, void* a7); +inline void*(*v_BuildSpriteLoadName)(const char* pName, char* pOut, int outLen, bool& bIsAVI, bool& bIsBIK); +#endif // !DEDICATED inline CModelLoader* g_pModelLoader; inline FileHandle_t* s_MapFileHandle; @@ -114,72 +97,56 @@ class VModelLoader : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CModelLoader::FindModel", p_CModelLoader__FindModel.GetPtr()); - LogFunAdr("CModelLoader::LoadModel", p_CModelLoader__LoadModel.GetPtr()); - LogFunAdr("CModelLoader::UnloadModel", p_CModelLoader__UnloadModel.GetPtr()); - LogFunAdr("CModelLoader::Map_LoadModelGuts", p_CModelLoader__Map_LoadModelGuts.GetPtr()); - LogFunAdr("CModelLoader::Map_IsValid", p_CModelLoader__Map_IsValid.GetPtr()); - LogFunAdr("CModelLoader::Studio_LoadModel", p_CModelLoader__Studio_LoadModel.GetPtr()); - LogFunAdr("CMapLoadHelper::CMapLoadHelper", p_CMapLoadHelper__CMapLoadHelper.GetPtr()); - LogFunAdr("AddGameLump", p_AddGameLump.GetPtr()); - LogFunAdr("Map_LoadModel", p_Map_LoadModel.GetPtr()); - //LogFunAdr("GetSpriteInfo", p_GetSpriteInfo.GetPtr()); - //LogFunAdr("BuildSpriteLoadName", p_BuildSpriteLoadName.GetPtr()); - LogVarAdr("g_pModelLoader", reinterpret_cast(g_pModelLoader)); - LogVarAdr("s_MapFileHandle", reinterpret_cast(s_MapFileHandle)); - LogVarAdr("s_MapHeader", reinterpret_cast(s_MapHeader)); - LogVarAdr("s_szMapPathName", reinterpret_cast(s_szMapPathName)); + LogFunAdr("CModelLoader::FindModel", CModelLoader__FindModel); + LogFunAdr("CModelLoader::LoadModel", CModelLoader__LoadModel); + LogFunAdr("CModelLoader::UnloadModel", CModelLoader__UnloadModel); + LogFunAdr("CModelLoader::Map_LoadModelGuts", CModelLoader__Map_LoadModelGuts); + LogFunAdr("CModelLoader::Map_IsValid", CModelLoader__Map_IsValid); + LogFunAdr("CModelLoader::Studio_LoadModel", CModelLoader__Studio_LoadModel); + + LogFunAdr("CMapLoadHelper::CMapLoadHelper", CMapLoadHelper__CMapLoadHelper); + + LogFunAdr("AddGameLump", v_AddGameLump); + LogFunAdr("Map_LoadModel", v_Map_LoadModel); + +#ifndef DEDICATED + LogFunAdr("GetSpriteInfo", v_GetSpriteInfo); + LogFunAdr("BuildSpriteLoadName", v_BuildSpriteLoadName); +#endif // !DEDICATED + + LogVarAdr("g_pModelLoader", g_pModelLoader); + LogVarAdr("s_MapFileHandle", s_MapFileHandle); + LogVarAdr("s_MapHeader", s_MapHeader); + LogVarAdr("s_szMapPathName", s_szMapPathName); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CModelLoader__FindModel = g_GameDll.FindPatternSIMD("40 55 41 55 41 56 48 8D AC 24 ?? ?? ?? ??"); - p_CModelLoader__LoadModel = g_GameDll.FindPatternSIMD("40 53 57 41 56 48 81 EC ?? ?? ?? ?? 48 8B FA"); - p_CModelLoader__UnloadModel = g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 18 55 48 81 EC ?? ?? ?? ?? 48 8B DA"); - p_CModelLoader__Studio_LoadModel = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 56 48 8D AC 24 ?? ?? ?? ??"); - p_CModelLoader__Map_LoadModelGuts = g_GameDll.FindPatternSIMD("48 89 54 24 ?? 48 89 4C 24 ?? 55 53 41 54 41 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? FF 05 ?? ?? ?? ??"); // BSP. - p_CModelLoader__Map_IsValid = g_GameDll.FindPatternSIMD("48 8B C4 53 48 81 EC ?? ?? ?? ?? 48 8B DA"); - //p_GetSpriteInfo = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 30 4C 8B AC 24 ?? ?? ?? ?? BE ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CModelLoader__FindModel = g_GameDll.FindPatternSIMD("40 55 41 57 48 83 EC 48 80 3A 2A"); - p_CModelLoader__LoadModel = g_GameDll.FindPatternSIMD("40 53 57 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ??"); - p_CModelLoader__UnloadModel = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 33 ED"); - p_CModelLoader__Studio_LoadModel = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 57 48 81 EC ?? ?? ?? ??"); - p_CModelLoader__Map_LoadModelGuts = g_GameDll.FindPatternSIMD("48 89 54 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 57"); // BSP. - p_CModelLoader__Map_IsValid = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 48 8B DA 48 85 D2 0F 84 ?? ?? ?? ?? 80 3A ?? 0F 84 ?? ?? ?? ?? 4C 8B CA"); - //p_GetSpriteInfo = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 30 4C 8B BC 24 ?? ?? ?? ??"); -#endif - //p_BuildSpriteLoadName = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 4D 8B F1 48 8B F2"); + g_GameDll.FindPatternSIMD("40 55 41 57 48 83 EC 48 80 3A 2A").GetPtr(CModelLoader__FindModel); + g_GameDll.FindPatternSIMD("40 53 57 41 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ??").GetPtr(CModelLoader__LoadModel); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 33 ED").GetPtr(CModelLoader__UnloadModel); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 57 48 81 EC ?? ?? ?? ??").GetPtr(CModelLoader__Studio_LoadModel); + g_GameDll.FindPatternSIMD("48 89 54 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 57").GetPtr(CModelLoader__Map_LoadModelGuts); // BSP. + g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 48 8B DA 48 85 D2 0F 84 ?? ?? ?? ?? 80 3A ?? 0F 84 ?? ?? ?? ?? 4C 8B CA").GetPtr(CModelLoader__Map_IsValid); + +#ifndef DEDICATED + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 30 4C 8B BC 24 ?? ?? ?? ??").GetPtr(v_GetSpriteInfo); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 4D 8B F1 48 8B F2").GetPtr(v_BuildSpriteLoadName); +#endif // !DEDICATED - p_CMapLoadHelper__CMapLoadHelper = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC 60"); - p_AddGameLump = g_GameDll.FindPatternSIMD("40 ?? 57 48 83 EC 48 33 ?? 48 8D"); - p_Map_LoadModel = g_GameDll.FindPatternSIMD("48 83 EC 28 8B 05 ?? ?? ?? ?? FF C8"); - - CModelLoader__FindModel = p_CModelLoader__FindModel.RCast(); - CModelLoader__LoadModel = p_CModelLoader__LoadModel.RCast(); - CModelLoader__UnloadModel = p_CModelLoader__UnloadModel.RCast(); - CModelLoader__Studio_LoadModel = p_CModelLoader__Studio_LoadModel.RCast(); - CModelLoader__Map_LoadModelGuts = p_CModelLoader__Map_LoadModelGuts.RCast(); - CModelLoader__Map_IsValid = p_CModelLoader__Map_IsValid.RCast(); - - CMapLoadHelper__CMapLoadHelper = p_CMapLoadHelper__CMapLoadHelper.RCast(); - v_AddGameLump = p_AddGameLump.RCast(); - v_Map_LoadModel = p_Map_LoadModel.RCast(); - - //GetSpriteInfo = p_GetSpriteInfo.RCast(); - //BuildSpriteLoadName = p_BuildSpriteLoadName.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC 60").GetPtr(CMapLoadHelper__CMapLoadHelper); + g_GameDll.FindPatternSIMD("40 ?? 57 48 83 EC 48 33 ?? 48 8D").GetPtr(v_AddGameLump); + g_GameDll.FindPatternSIMD("48 83 EC 28 8B 05 ?? ?? ?? ?? FF C8").GetPtr(v_Map_LoadModel); } virtual void GetVar(void) const { g_pModelLoader = g_GameDll.FindPatternSIMD( "48 89 4C 24 ?? 53 55 56 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??").FindPatternSelf("48 ?? 0D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(3, 7).RCast(); - s_MapFileHandle = p_Map_LoadModel.FindPattern("48 8B").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - s_MapHeader = p_Map_LoadModel.FindPattern("48 8D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - s_szMapPathName = p_CMapLoadHelper__CMapLoadHelper.FindPattern("4C 8D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + s_MapFileHandle = CMemory(v_Map_LoadModel).FindPattern("48 8B").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + s_MapHeader = CMemory(v_Map_LoadModel).FindPattern("48 8D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + s_szMapPathName = CMemory(CMapLoadHelper__CMapLoadHelper).FindPattern("4C 8D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/net.cpp b/r5dev/engine/net.cpp index b483cd08..0734c769 100644 --- a/r5dev/engine/net.cpp +++ b/r5dev/engine/net.cpp @@ -6,7 +6,7 @@ #include "core/stdafx.h" #include "engine/net.h" -#ifndef NETCONSOLE +#ifndef _TOOLS #include "tier1/cvar.h" #include "mathlib/color.h" #include "net.h" @@ -15,9 +15,45 @@ #include "server/server.h" #include "client/client.h" #endif // !CLIENT_DLL -#endif // !NETCONSOLE +#endif // !_TOOLS + +#ifndef _TOOLS +static void NET_SetKey_f(const CCommand& args) +{ + if (args.ArgC() < 2) + { + return; + } + + NET_SetKey(args.Arg(1)); +} +static void NET_GenerateKey_f() +{ + NET_GenerateKey(); +} + +void NET_UseRandomKeyChanged_f(IConVar* pConVar, const char* pOldString) +{ + if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName())) + { + if (strcmp(pOldString, pConVarRef->GetString()) == NULL) + return; // Same value. + + if (pConVarRef->GetBool()) + NET_GenerateKey(); + else + NET_SetKey(DEFAULT_NET_ENCRYPTION_KEY); + } +} + +ConVar net_useRandomKey("net_useRandomKey", "1", FCVAR_RELEASE, "Use random AES encryption key for game packets.", false, 0.f, false, 0.f, &NET_UseRandomKeyChanged_f, nullptr); + +static ConVar net_tracePayload("net_tracePayload", "0", FCVAR_DEVELOPMENTONLY, "Log the payload of the send/recv datagram to a file on the disk."); +static ConVar net_encryptionEnable("net_encryptionEnable", "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Use AES encryption on game packets."); + +static ConCommand net_setkey("net_setkey", NET_SetKey_f, "Sets user specified base64 net key", FCVAR_RELEASE); +static ConCommand net_generatekey("net_generatekey", NET_GenerateKey_f, "Generates and sets a random base64 net key", FCVAR_RELEASE); -#ifndef NETCONSOLE //----------------------------------------------------------------------------- // Purpose: hook and log the receive datagram // Input : iSocket - @@ -27,13 +63,16 @@ //----------------------------------------------------------------------------- bool NET_ReceiveDatagram(int iSocket, netpacket_s* pInpacket, bool bEncrypted) { - bool result = v_NET_ReceiveDatagram(iSocket, pInpacket, net_encryptionEnable->GetBool()); - if (result && net_tracePayload->GetBool()) + const bool decryptPacket = (bEncrypted && net_encryptionEnable.GetBool()); + const bool result = v_NET_ReceiveDatagram(iSocket, pInpacket, decryptPacket); + + if (result && net_tracePayload.GetBool()) { // Log received packet data. - HexDump("[+] NET_ReceiveDatagram ", "net_trace", + HexDump("[+] NET_ReceiveDatagram ", "net_trace", pInpacket->pData, size_t(pInpacket->wiresize)); } + return result; } @@ -48,15 +87,66 @@ bool NET_ReceiveDatagram(int iSocket, netpacket_s* pInpacket, bool bEncrypted) //----------------------------------------------------------------------------- int NET_SendDatagram(SOCKET s, void* pPayload, int iLenght, netadr_t* pAdr, bool bEncrypt) { - int result = v_NET_SendDatagram(s, pPayload, iLenght, pAdr, net_encryptionEnable->GetBool()); - if (result && net_tracePayload->GetBool()) + const bool encryptPacket = (bEncrypt && net_encryptionEnable.GetBool()); + const int result = v_NET_SendDatagram(s, pPayload, iLenght, pAdr, encryptPacket); + + if (result && net_tracePayload.GetBool()) { // Log transmitted packet data. HexDump("[+] NET_SendDatagram ", "net_trace", pPayload, size_t(iLenght)); } + return result; } +//----------------------------------------------------------------------------- +// Purpose: compresses the input buffer into the output buffer +// Input : *dest - +// *destLen - +// *source - +// sourceLen - +// Output : true on success, false otherwise +//----------------------------------------------------------------------------- +bool NET_BufferToBufferCompress(uint8_t* const dest, size_t* const destLen, uint8_t* const source, const size_t sourceLen) +{ + CLZSS lzss; + uint32_t compLen = (uint32_t)sourceLen; + + if (!lzss.CompressNoAlloc(source, (uint32_t)sourceLen, dest, &compLen)) + { + memcpy(dest, source, sourceLen); + + *destLen = sourceLen; + return false; + } + + *destLen = compLen; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: decompresses the input buffer into the output buffer +// Input : *source - +// &sourceLen - +// *dest - +// destLen - +// Output : true on success, false otherwise +//----------------------------------------------------------------------------- +unsigned int NET_BufferToBufferDecompress(uint8_t* const source, size_t& sourceLen, uint8_t* const dest, const size_t destLen) +{ + Assert(source); + Assert(sourceLen); + + CLZSS lzss; + + if (lzss.IsCompressed(source)) + { + return lzss.SafeUncompress(source, dest, (unsigned int)destLen); + } + + return 0; +} + //----------------------------------------------------------------------------- // Purpose: safely decompresses the input buffer into the output buffer // Input : *lzss - @@ -65,7 +155,7 @@ int NET_SendDatagram(SOCKET s, void* pPayload, int iLenght, netadr_t* pAdr, bool // unBufSize - // Output : total decompressed bytes //----------------------------------------------------------------------------- -unsigned int NET_Decompress(CLZSS* lzss, unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize) +unsigned int NET_BufferToBufferDecompress_LZSS(CLZSS* lzss, unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize) { return lzss->SafeUncompress(pInput, pOutput, unBufSize); } @@ -92,7 +182,7 @@ void NET_SetKey(const string& svNetKey) { v_NET_SetKey(g_pNetKey, svTokenizedKey.c_str()); - DevMsg(eDLL_T::ENGINE, "Installed NetKey: %s'%s%s%s'\n", + Msg(eDLL_T::ENGINE, "Installed NetKey: %s'%s%s%s'\n", g_svReset, g_svGreyB, g_pNetKey->GetBase64NetKey(), g_svReset); } else @@ -106,9 +196,9 @@ void NET_SetKey(const string& svNetKey) //----------------------------------------------------------------------------- void NET_GenerateKey() { - if (!net_useRandomKey->GetBool()) + if (!net_useRandomKey.GetBool()) { - net_useRandomKey->SetValue(1); + net_useRandomKey.SetValue(1); return; // Change callback will handle this. } @@ -154,7 +244,7 @@ void NET_PrintFunc(const char* fmt, ...) result.push_back('\n'); } - DevMsg(context, "%s", result.c_str()); + Msg(context, "%s", result.c_str()); } //----------------------------------------------------------------------------- @@ -175,10 +265,22 @@ void NET_RemoveChannel(CClient* pClient, int nIndex, const char* szReason, uint8 pClient->GetNetChan()->Shutdown(szReason, bBadRep, bRemoveNow); // Shutdown NetChannel. pClient->Clear(); // Reset CClient slot. - g_ServerPlayer[nIndex].Reset(); // Reset ServerPlayer slot. #endif // !CLIENT_DLL } -#endif // !NETCONSOLE + +//----------------------------------------------------------------------------- +// Purpose: reads the net message type from buffer +// Input : &outType - +// &buffer - +// Output : true on success, false otherwise +//----------------------------------------------------------------------------- +bool NET_ReadMessageType(int* outType, bf_read* buffer) +{ + *outType = buffer->ReadUBitLong(NETMSG_TYPE_BITS); + return !buffer->IsOverflowed(); +} + +#endif // !_TOOLS //----------------------------------------------------------------------------- // Purpose: returns the WSA error code @@ -282,24 +384,17 @@ const char* NET_ErrorString(int iCode) } } -#ifndef NETCONSOLE +#ifndef _TOOLS /////////////////////////////////////////////////////////////////////////////// -void VNet::Attach() const +void VNet::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_NET_Config, &NET_Config); - DetourAttach((LPVOID*)&v_NET_ReceiveDatagram, &NET_ReceiveDatagram); - DetourAttach((LPVOID*)&v_NET_SendDatagram, &NET_SendDatagram); - DetourAttach((LPVOID*)&v_NET_Decompress, &NET_Decompress); - DetourAttach((LPVOID*)&v_NET_PrintFunc, &NET_PrintFunc); -} + DetourSetup(&v_NET_Config, &NET_Config, bAttach); + DetourSetup(&v_NET_ReceiveDatagram, &NET_ReceiveDatagram, bAttach); + DetourSetup(&v_NET_SendDatagram, &NET_SendDatagram, bAttach); -void VNet::Detach() const -{ - DetourDetach((LPVOID*)&v_NET_Config, &NET_Config); - DetourDetach((LPVOID*)&v_NET_ReceiveDatagram, &NET_ReceiveDatagram); - DetourDetach((LPVOID*)&v_NET_SendDatagram, &NET_SendDatagram); - DetourDetach((LPVOID*)&v_NET_Decompress, &NET_Decompress); - DetourDetach((LPVOID*)&v_NET_PrintFunc, &NET_PrintFunc); + DetourSetup(&v_NET_BufferToBufferCompress, &NET_BufferToBufferCompress, bAttach); + DetourSetup(&v_NET_BufferToBufferDecompress_LZSS, &NET_BufferToBufferDecompress_LZSS, bAttach); + DetourSetup(&v_NET_PrintFunc, &NET_PrintFunc, bAttach); } /////////////////////////////////////////////////////////////////////////////// @@ -307,4 +402,4 @@ netadr_t* g_pNetAdr = nullptr; netkey_t* g_pNetKey = nullptr; double* g_pNetTime = nullptr; -#endif // !NETCONSOLE +#endif // !_TOOLS diff --git a/r5dev/engine/net.h b/r5dev/engine/net.h index 616b4727..70e60aab 100644 --- a/r5dev/engine/net.h +++ b/r5dev/engine/net.h @@ -1,6 +1,6 @@ #pragma once -#ifndef NETCONSOLE +#ifndef _TOOLS #include "engine/net_chan.h" #include "tier1/lzss.h" #define MAX_STREAMS 2 @@ -19,26 +19,20 @@ constexpr unsigned int AES_128_B64_ENCODED_SIZE = 24; constexpr const char* DEFAULT_NET_ENCRYPTION_KEY = "WDNWLmJYQ2ZlM0VoTid3Yg=="; /* ==== CNETCHAN ======================================================================================================================================================== */ -inline CMemory p_NET_Init; inline void*(*v_NET_Init)(bool bDeveloper); - -inline CMemory p_NET_SetKey; inline void(*v_NET_SetKey)(netkey_t* pKey, const char* szHash); - -inline CMemory p_NET_Config; inline void(*v_NET_Config)(void); -inline CMemory p_NET_ReceiveDatagram; -inline bool(*v_NET_ReceiveDatagram)(int iSocket, netpacket_s* pInpacket, bool bRaw); +inline int(*v_NET_GetPacket)(int iSocket, uint8_t* pScratch, bool bEncrypted); +inline int(*v_NET_SendPacket)(CNetChan* pChan, int iSocket, const netadr_t& toAdr, const uint8_t* pData, unsigned int nLen, void* unused0, bool bCompress, void* unused1, bool bEncrypt); -inline CMemory p_NET_SendDatagram; +inline bool(*v_NET_ReceiveDatagram)(int iSocket, netpacket_s* pInpacket, bool bRaw); inline int(*v_NET_SendDatagram)(SOCKET s, void* pPayload, int iLenght, netadr_t* pAdr, bool bEncrypted); -inline CMemory p_NET_Decompress; -inline int(*v_NET_Decompress)(CLZSS* lzss, unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize); +inline bool(*v_NET_BufferToBufferCompress)(uint8_t* const dest, size_t* const destLen, uint8_t* const source, const size_t sourceLen); +inline unsigned int(*v_NET_BufferToBufferDecompress_LZSS)(CLZSS* lzss, unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize); -inline CMemory p_NET_PrintFunc; -inline void(*v_NET_PrintFunc)(const char* fmt); +inline void(*v_NET_PrintFunc)(const char* fmt, ...); /////////////////////////////////////////////////////////////////////////////// bool NET_ReceiveDatagram(int iSocket, netpacket_s* pInpacket, bool bRaw); @@ -48,61 +42,72 @@ void NET_GenerateKey(); void NET_PrintFunc(const char* fmt, ...); void NET_RemoveChannel(CClient* pClient, int nIndex, const char* szReason, uint8_t bBadRep, bool bRemoveNow); +bool NET_BufferToBufferCompress(uint8_t* const dest, size_t* const destLen, uint8_t* const source, const size_t sourceLen); +unsigned int NET_BufferToBufferDecompress(uint8_t* pInput, size_t& coBufsize, uint8_t* pOutput, const size_t unBufSize); + +unsigned int NET_BufferToBufferDecompress_LZSS(CLZSS* lzss, unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize); + +bool NET_ReadMessageType(int* outType, bf_read* buffer); + /////////////////////////////////////////////////////////////////////////////// extern netadr_t* g_pNetAdr; extern netkey_t* g_pNetKey; extern double* g_pNetTime; +extern ConVar net_useRandomKey; + /////////////////////////////////////////////////////////////////////////////// class VNet : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("NET_Init", p_NET_Init.GetPtr()); - LogFunAdr("NET_Config", p_NET_Config.GetPtr()); - LogFunAdr("NET_SetKey", p_NET_SetKey.GetPtr()); - LogFunAdr("NET_ReceiveDatagram", p_NET_ReceiveDatagram.GetPtr()); - LogFunAdr("NET_SendDatagram", p_NET_SendDatagram.GetPtr()); - LogFunAdr("NET_Decompress", p_NET_Decompress.GetPtr()); - LogFunAdr("NET_PrintFunc", p_NET_PrintFunc.GetPtr()); - LogVarAdr("g_NetAdr", reinterpret_cast(g_pNetAdr)); - LogVarAdr("g_NetKey", reinterpret_cast(g_pNetKey)); - LogVarAdr("g_NetTime", reinterpret_cast(g_pNetTime)); + LogFunAdr("NET_Init", v_NET_Init); + LogFunAdr("NET_Config", v_NET_Config); + LogFunAdr("NET_SetKey", v_NET_SetKey); + + LogFunAdr("NET_GetPacket", v_NET_GetPacket); + LogFunAdr("NET_SendPacket", v_NET_SendPacket); + + LogFunAdr("NET_ReceiveDatagram", v_NET_ReceiveDatagram); + LogFunAdr("NET_SendDatagram", v_NET_SendDatagram); + + LogFunAdr("NET_BufferToBufferCompress", v_NET_BufferToBufferCompress); + LogFunAdr("NET_BufferToBufferDecompress_LZSS", v_NET_BufferToBufferDecompress_LZSS); + + LogFunAdr("NET_PrintFunc", v_NET_PrintFunc); + LogVarAdr("g_NetAdr", g_pNetAdr); + LogVarAdr("g_NetKey", g_pNetKey); + LogVarAdr("g_NetTime", g_pNetTime); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - p_NET_Init = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC C0 01 ??"); -#elif defined (GAMEDLL_S3) - p_NET_Init = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC F0 01 ??"); -#endif - p_NET_Config = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 57 C0"); - p_NET_SetKey = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 41 B8"); - p_NET_ReceiveDatagram = g_GameDll.FindPatternSIMD("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB"); - p_NET_SendDatagram = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ?? 05 ?? ??"); - p_NET_Decompress = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 41 56 45 33 F6"); - p_NET_PrintFunc = g_GameDll.FindPatternSIMD("48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 C3 48"); + g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC F0 01 ??").GetPtr(v_NET_Init); + g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 57 C0").GetPtr(v_NET_Config); + g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 41 B8").GetPtr(v_NET_SetKey); - v_NET_Init = p_NET_Init.RCast(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC F0 01 00*/ - v_NET_Config = p_NET_Config.RCast(); - v_NET_SetKey = p_NET_SetKey.RCast(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 41 B8*/ - v_NET_ReceiveDatagram = p_NET_ReceiveDatagram.RCast(); /*E8 ?? ?? ?? ?? 84 C0 75 35 48 8B D3*/ - v_NET_SendDatagram = p_NET_SendDatagram.RCast(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ?? 05 00 00*/ - v_NET_Decompress = p_NET_Decompress.RCast(); - v_NET_PrintFunc = p_NET_PrintFunc.RCast(); /*48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 C3 48*/ + + g_GameDll.FindPatternSIMD("48 8B C4 44 88 40 18 48 89 50 10 41 55").GetPtr(v_NET_GetPacket); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 4C 63 F2").GetPtr(v_NET_SendPacket); + + g_GameDll.FindPatternSIMD("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr(v_NET_ReceiveDatagram); + g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ?? 05 ?? ??").GetPtr(v_NET_SendDatagram); + + g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 50 48 8B 05 ?? ?? ?? ?? 49 8B E9").GetPtr(v_NET_BufferToBufferCompress); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 41 56 45 33 F6").GetPtr(v_NET_BufferToBufferDecompress_LZSS); + + g_GameDll.FindPatternSIMD("48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 C3 48").GetPtr(v_NET_PrintFunc); } virtual void GetVar(void) const { g_pNetAdr = g_GameDll.FindPatternSIMD("C7 05 ?? ?? ?? ?? ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 66 89 05 ?? ?? ?? ?? 88 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC 33 C0").ResolveRelativeAddressSelf(0x2, 0xA).RCast(); g_pNetKey = g_GameDll.FindString("client:NetEncryption_NewKey").FindPatternSelf("48 8D ?? ?? ?? ?? ?? 48 3B", CMemory::Direction::UP, 300).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pNetTime = p_NET_Init.Offset(0xA).FindPatternSelf("F2 0F").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); + g_pNetTime = CMemory(v_NET_Init).Offset(0xA).FindPatternSelf("F2 0F").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// -#endif // !NETCONSOLE +#endif // !_TOOLS const char* NET_ErrorString(int iCode); diff --git a/r5dev/engine/net_chan.cpp b/r5dev/engine/net_chan.cpp index af019ebc..bef2d6c6 100644 --- a/r5dev/engine/net_chan.cpp +++ b/r5dev/engine/net_chan.cpp @@ -7,7 +7,7 @@ #include "core/stdafx.h" #include "tier0/frametask.h" #include "tier1/cvar.h" -#include "vpc/keyvalues.h" +#include "tier1/keyvalues.h" #include "common/callback.h" #include "engine/net.h" #include "engine/net_chan.h" @@ -17,22 +17,28 @@ #include "server/vengineserver_impl.h" #endif // !CLIENT_DLL +//----------------------------------------------------------------------------- +// Console variables +//----------------------------------------------------------------------------- +static ConVar net_processTimeBudget("net_processTimeBudget", "200", FCVAR_RELEASE, "Net message process time budget in milliseconds (removing netchannel if exceeded).", true, 0.f, false, 0.f, "0 = disabled"); //----------------------------------------------------------------------------- -// Purpose: gets the netchannel network loss +// Purpose: gets the netchannel resend rate // Output : float //----------------------------------------------------------------------------- -float CNetChan::GetNetworkLoss() const +float CNetChan::GetResendRate() const { - float v1 = *&m_DataFlow[1].frames[0].one; - if (!v1 && !m_nSequencesSkipped_MAYBE) + const int64_t totalupdates = this->m_DataFlow[FLOW_INCOMING].totalupdates; + + if (!totalupdates && !this->m_nSequencesSkipped_MAYBE) return 0.0f; - float v4 = (v1 + m_nSequencesSkipped_MAYBE); - if (v1 + m_nSequencesSkipped_MAYBE < 0) - v4 = v4 + float(2 ^ 64); + float lossRate = (float)(totalupdates + m_nSequencesSkipped_MAYBE); - return m_nSequencesSkipped_MAYBE / v4; + if (totalupdates + m_nSequencesSkipped_MAYBE < 0.0f) + lossRate += float(2 ^ 64); + + return m_nSequencesSkipped_MAYBE / lossRate; } //----------------------------------------------------------------------------- @@ -64,6 +70,287 @@ double CNetChan::GetTimeConnected(void) const return (t > 0.0) ? t : 0.0; } +//----------------------------------------------------------------------------- +// Purpose: gets the number of bits written in selected stream +//----------------------------------------------------------------------------- +int CNetChan::GetNumBitsWritten(const bool bReliable) +{ + bf_write* pStream = &m_StreamUnreliable; + + if (bReliable) + { + pStream = &m_StreamReliable; + } + + return pStream->GetNumBitsWritten(); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the number of bits written in selected stream +//----------------------------------------------------------------------------- +int CNetChan::GetNumBitsLeft(const bool bReliable) +{ + bf_write* pStream = &m_StreamUnreliable; + + if (bReliable) + { + pStream = &m_StreamReliable; + } + + return pStream->GetNumBitsLeft(); +} + +//----------------------------------------------------------------------------- +// Purpose: flows a new packet +// Input : *pChan - +// outSeqNr - +// acknr - +// inSeqNr - +// nChoked - +// nDropped - +// nSize - +//----------------------------------------------------------------------------- +void CNetChan::_FlowNewPacket(CNetChan* pChan, int flow, int outSeqNr, int inSeqNr, int nChoked, int nDropped, int nSize) +{ + float netTime; // xmm4_8 (was double) + int v8; // r13d + int v9; // r14d + int v12; // r12d + int currentindex; // eax + int nextIndex; // r15d + int v17; // r8d + int v18; // ebp + unsigned int v19; // eax + int v20; // r9 (was char) + int v21; // r8d + __int64 v22; // r14 + float time; // xmm0_4 + __int64 v24; // rdx + __int64 v25; // rcx + __int64 v26; // rdx + __int64 v27; // rcx + __int64 v28; // rdx + __int64 v29; // rcx + int v30; // edx + int v31; // r8 (was char) + float v32; // xmm0_4 + __int64 v33; // r9 + __int64 v34; // rax + __int64 v35; // rdx + int v36; // r8d + float v37; // xmm3_4 + __int64 result; // rax + float v39; // xmm1_4 + float v40; // xmm0_4 + float v41; // xmm1_4 + netframe_header_t* v42; // rdx + float v43; // xmm0_4 + float v44; // xmm2_4 + float v45; // xmm0_4 + + netTime = (float)*g_pNetTime; + v8 = flow; + v9 = inSeqNr; + netflow_t* pFlow = &pChan->m_DataFlow[flow]; + v12 = outSeqNr; + + netframe_header_t* pFrameHeader = nullptr; + netframe_t* pFrame = nullptr; + + currentindex = pFlow->currentindex; + if (outSeqNr > currentindex) + { + nextIndex = currentindex + 1; + if (currentindex + 1 <= outSeqNr) + { + // This variable makes sure the loops below do not execute more + // than NET_FRAMES_BACKUP times. This has to be done as the + // headers and frame arrays in the netflow_t structure is as + // large as NET_FRAMES_BACKUP. Any execution past it is futile + // and only wastes CPU time. Sending an outSeqNr that is higher + // than the current index by something like a million or more will + // hang the engine for several milliseconds to several seconds. + int numPacketFrames = 0; + + v17 = outSeqNr - nextIndex; + + if (v17 + 1 >= 4) + { + v18 = nChoked + nDropped; + v19 = ((unsigned int)(v12 - nextIndex - 3) >> 2) + 1; + v20 = nextIndex + 2; + v21 = v17 - 2; + v22 = v19; + time = (float)*g_pNetTime; + nextIndex += 4 * v19; + + do + { + v24 = (v20 - 2) & NET_FRAMES_MASK; + v25 = v24; + pFlow->frame_headers[v25].time = time; + pFlow->frame_headers[v25].valid = 0; + pFlow->frame_headers[v25].size = 0; + pFlow->frame_headers[v25].latency = -1.0; + pFlow->frames[v24].avg_latency = pChan->m_DataFlow[FLOW_OUTGOING].avglatency; + pFlow->frame_headers[v25].choked = 0; + pFlow->frames[v24].dropped = 0; + if (v21 + 2 < v18) + { + if (v21 + 2 >= nChoked) + pFlow->frames[v24].dropped = 1; + else + pFlow->frame_headers[(v20 - 2) & NET_FRAMES_MASK].choked = 1; + } + v26 = (v20 - 1) & NET_FRAMES_MASK; + v27 = v26; + pFlow->frame_headers[v27].time = time; + pFlow->frame_headers[v27].valid = 0; + pFlow->frame_headers[v27].size = 0; + pFlow->frame_headers[v27].latency = -1.0; + pFlow->frames[v26].avg_latency = pChan->m_DataFlow[FLOW_OUTGOING].avglatency; + pFlow->frame_headers[v27].choked = 0; + pFlow->frames[v26].dropped = 0; + if (v21 + 1 < v18) + { + if (v21 + 1 >= nChoked) + pFlow->frames[v26].dropped = 1; + else + pFlow->frame_headers[(v20 - 1) & NET_FRAMES_MASK].choked = 1; + } + v28 = v20 & NET_FRAMES_MASK; + v29 = v28; + pFlow->frame_headers[v29].time = time; + pFlow->frame_headers[v29].valid = 0; + pFlow->frame_headers[v29].size = 0; + pFlow->frame_headers[v29].latency = -1.0; + pFlow->frames[v28].avg_latency = pChan->m_DataFlow[FLOW_OUTGOING].avglatency; + pFlow->frame_headers[v29].choked = 0; + pFlow->frames[v28].dropped = 0; + if (v21 < v18) + { + if (v21 >= nChoked) + pFlow->frames[v28].dropped = 1; + else + pFlow->frame_headers[v20 & NET_FRAMES_MASK].choked = 1; + } + pFrame = &pFlow->frames[(v20 + 1) & NET_FRAMES_MASK]; + pFrameHeader = &pFlow->frame_headers[(v20 + 1) & NET_FRAMES_MASK]; + pFrameHeader->time = time; + pFrameHeader->valid = 0; + pFrameHeader->size = 0; + pFrameHeader->latency = -1.0; + pFrame->avg_latency = pChan->m_DataFlow[FLOW_OUTGOING].avglatency; + pFrameHeader->choked = 0; + pFrame->dropped = 0; + if (v21 - 1 < v18) + { + if (v21 - 1 >= nChoked) + pFrame->dropped = 1; + else + pFrameHeader->choked = 1; + } + + // Incremented by four since this loop does four frames + // per iteration. + numPacketFrames += 4; + v21 -= 4; + v20 += 4; + --v22; + } while (v22 && numPacketFrames < NET_FRAMES_BACKUP); + v12 = outSeqNr; + v8 = flow; + v9 = inSeqNr; + } + + // Check if we did not reach NET_FRAMES_BACKUP, else we will + // execute the 129'th iteration as well. Also check if the next + // index doesn't exceed the outSeqNr. + if (numPacketFrames < NET_FRAMES_BACKUP && nextIndex <= v12) + { + v30 = v12 - nextIndex; + v31 = nextIndex; + v33 = v12 - nextIndex + 1; + do + { + pFrame = &pFlow->frames[v31 & NET_FRAMES_MASK]; + pFrameHeader = &pFlow->frame_headers[v31 & NET_FRAMES_MASK]; + v32 = netTime; + pFrameHeader->time = v32; + pFrameHeader->valid = 0; + pFrameHeader->size = 0; + pFrameHeader->latency = -1.0; + pFrame->avg_latency = pChan->m_DataFlow[FLOW_OUTGOING].avglatency; + pFrameHeader->choked = 0; + pFrame->dropped = 0; + if (v30 < nChoked + nDropped) + { + if (v30 >= nChoked) + pFrame->dropped = 1; + else + pFrameHeader->choked = 1; + } + --v30; + ++v31; + --v33; + ++numPacketFrames; + } while (v33 && numPacketFrames < NET_FRAMES_BACKUP); + v9 = inSeqNr; + } + } + pFrame->dropped = nDropped; + pFrameHeader->choked = (short)nChoked; + pFrameHeader->size = nSize; + pFrameHeader->valid = 1; + pFrame->avg_latency = pChan->m_DataFlow[FLOW_OUTGOING].avglatency; + } + ++pFlow->totalpackets; + pFlow->currentindex = v12; + v34 = 544i64; + + if (!v8) + v34 = 3688i64; + + pFlow->current_frame = pFrame; + v35 = 548i64; + v36 = *(_DWORD*)(&pChan->m_bProcessingMessages + v34); + if (v9 > v36 - NET_FRAMES_BACKUP) + { + if (!v8) + v35 = 3692i64; + result = (__int64)pChan + 16 * (v9 & NET_FRAMES_MASK); + v42 = (netframe_header_t*)(result + v35); + if (v42->valid && v42->latency == -1.0) + { + v43 = 0.0; + v44 = fmax(0.0f, netTime - v42->time); + v42->latency = v44; + if (v44 >= 0.0) + v43 = v44; + else + v42->latency = 0.0; + v45 = v43 + pFlow->latency; + ++pFlow->totalupdates; + pFlow->latency = v45; + pFlow->maxlatency = fmaxf(pFlow->maxlatency, v42->latency); + } + } + else + { + if (!v8) + v35 = 3692i64; + + v37 = *(float*)(&pChan->m_bProcessingMessages + 16 * (v36 & NET_FRAMES_MASK) + v35); + result = v35 + 16i64 * (((_BYTE)v36 + 1) & NET_FRAMES_MASK); + v39 = v37 - *(float*)(&pChan->m_bProcessingMessages + result); + ++pFlow->totalupdates; + v40 = (float)((float)(v39 / 127.0) * (float)(v36 - v9)) + netTime - v37; + v41 = fmaxf(pFlow->maxlatency, v40); + pFlow->latency = v40 + pFlow->latency; + pFlow->maxlatency = v41; + } +} + //----------------------------------------------------------------------------- // Purpose: shutdown netchannel // Input : *this - @@ -73,7 +360,7 @@ double CNetChan::GetTimeConnected(void) const //----------------------------------------------------------------------------- void CNetChan::_Shutdown(CNetChan* pChan, const char* szReason, uint8_t bBadRep, bool bRemoveNow) { - v_NetChan_Shutdown(pChan, szReason, bBadRep, bRemoveNow); + CNetChan__Shutdown(pChan, szReason, bBadRep, bRemoveNow); } //----------------------------------------------------------------------------- @@ -82,46 +369,193 @@ void CNetChan::_Shutdown(CNetChan* pChan, const char* szReason, uint8_t bBadRep, // *pMsg - // Output : true on success, false on failure //----------------------------------------------------------------------------- -bool CNetChan::_ProcessMessages(CNetChan* pChan, bf_read* pMsg) +bool CNetChan::_ProcessMessages(CNetChan* pChan, bf_read* pBuf) { #ifndef CLIENT_DLL - if (!ThreadInServerFrameThread() || !net_processTimeBudget->GetInt()) - return v_NetChan_ProcessMessages(pChan, pMsg); + if (!net_processTimeBudget.GetInt() || !ThreadInServerFrameThread()) + return pChan->ProcessMessages(pBuf); - const double flStartTime = Plat_FloatTime(); - const bool bResult = v_NetChan_ProcessMessages(pChan, pMsg); + const double flStartTime = Plat_FloatTime(); + const bool bResult = pChan->ProcessMessages(pBuf); - if (!pChan->m_MessageHandler) // NetChannel removed? - return bResult; + if (!pChan->m_MessageHandler) // NetChannel removed? + return bResult; - CClient* pClient = reinterpret_cast(pChan->m_MessageHandler); - ServerPlayer_t* pSlot = &g_ServerPlayer[pClient->GetUserID()]; + CClient* const pClient = reinterpret_cast(pChan->m_MessageHandler); + CClientExtended* const pExtended = pClient->GetClientExtended(); - if (flStartTime - pSlot->m_flLastNetProcessTime >= 1.0 || - pSlot->m_flLastNetProcessTime == -1.0) - { - pSlot->m_flLastNetProcessTime = flStartTime; - pSlot->m_flCurrentNetProcessTime = 0.0; - } - pSlot->m_flCurrentNetProcessTime += - (Plat_FloatTime() * 1000) - (flStartTime * 1000); + // Reset every second. + if ((flStartTime - pExtended->GetNetProcessingTimeBase()) > 1.0) + { + pExtended->SetNetProcessingTimeBase(flStartTime); + pExtended->SetNetProcessingTimeMsecs(0.0, 0.0); + } - if (pSlot->m_flCurrentNetProcessTime > - net_processTimeBudget->GetDouble()) - { - Warning(eDLL_T::SERVER, "Removing netchannel '%s' ('%s' exceeded frame budget by '%3.1f'ms!)\n", - pChan->GetName(), pChan->GetAddress(), (pSlot->m_flCurrentNetProcessTime - net_processTimeBudget->GetDouble())); - pClient->Disconnect(Reputation_t::REP_MARK_BAD, "#DISCONNECT_NETCHAN_OVERFLOW"); + const double flCurrentTime = Plat_FloatTime(); + pExtended->SetNetProcessingTimeMsecs(flStartTime, flCurrentTime); - return false; - } + if (pExtended->GetNetProcessingTimeMsecs() > net_processTimeBudget.GetFloat()) + { + Warning(eDLL_T::SERVER, "Removing netchannel '%s' ('%s' exceeded time budget by '%3.1f'ms!)\n", + pChan->GetName(), pChan->GetAddress(), (pExtended->GetNetProcessingTimeMsecs() - net_processTimeBudget.GetFloat())); + pClient->Disconnect(Reputation_t::REP_MARK_BAD, "#DISCONNECT_NETCHAN_OVERFLOW"); - return bResult; + return false; + } + + return bResult; #else // !CLIENT_DLL - return v_NetChan_ProcessMessages(pChan, pMsg); + return pChan->ProcessMessages(pBuf); #endif } +//----------------------------------------------------------------------------- +// Purpose: process message +// Input : *buf - +// Output : true on success, false on failure +//----------------------------------------------------------------------------- +bool CNetChan::ProcessMessages(bf_read* buf) +{ + m_bStopProcessing = false; + + const char* showMsgName = net_showmsg->GetString(); + const char* blockMsgName = net_blockmsg->GetString(); + const int netPeak = net_showpeaks->GetInt(); + + if (*showMsgName == '0') + { + showMsgName = NULL; // dont do strcmp all the time + } + + if (*blockMsgName == '0') + { + blockMsgName = NULL; // dont do strcmp all the time + } + + if (netPeak > 0 && netPeak < buf->GetNumBytesLeft()) + { + showMsgName = "1"; // show messages for this packet only + } + + while (true) + { + int cmd = net_NOP; + + while (true) + { + if (buf->GetNumBitsLeft() < NETMSG_TYPE_BITS) + return true; // Reached the end. + + if (!NET_ReadMessageType(&cmd, buf) && buf->m_bOverflow) + { + Error(eDLL_T::ENGINE, 0, "%s(%s): Incoming buffer overflow!\n", __FUNCTION__, GetAddress()); + m_MessageHandler->ConnectionCrashed("Buffer overflow in net message"); + + return false; + } + + if (cmd <= net_Disconnect) + break; // Either a Disconnect or NOP packet; process it below. + + INetMessage* netMsg = FindMessage(cmd); + + if (!netMsg) + { + DevWarning(eDLL_T::ENGINE, "%s(%s): Received unknown net message (%i)!\n", + __FUNCTION__, GetAddress(), cmd); + Assert(0); + return false; + } + + if (!netMsg->ReadFromBuffer(buf)) + { + DevWarning(eDLL_T::ENGINE, "%s(%s): Failed reading message '%s'!\n", + __FUNCTION__, GetAddress(), netMsg->GetName()); + Assert(0); + return false; + } + + if (showMsgName) + { + if ((*showMsgName == '1') || !Q_stricmp(showMsgName, netMsg->GetName())) + { + Msg(eDLL_T::ENGINE, "%s(%s): Received: %s\n", + __FUNCTION__, GetAddress(), netMsg->ToString()); + } + } + + if (blockMsgName) + { + if ((*blockMsgName == '1') || !Q_stricmp(blockMsgName, netMsg->GetName())) + { + Msg(eDLL_T::ENGINE, "%s(%s): Blocked: %s\n", + __FUNCTION__, GetAddress(), netMsg->ToString()); + + continue; + } + } + + // Netmessage calls the Process function that was registered by + // it's MessageHandler. + m_bProcessingMessages = true; + const bool bRet = netMsg->Process(); + m_bProcessingMessages = false; + + // This means we were deleted during the processing of that message. + if (m_bShouldDelete) + { + delete this; + return false; + } + + // This means our message buffer was freed or invalidated during + // the processing of that message. + if (m_bStopProcessing) + return false; + + if (!bRet) + { + DevWarning(eDLL_T::ENGINE, "%s(%s): Failed processing message '%s'!\n", + __FUNCTION__, GetAddress(), netMsg->GetName()); + Assert(0); + return false; + } + + if (IsOverflowed()) + return false; + } + + m_bProcessingMessages = true; + + if (cmd == net_NOP) // NOP; continue to next packet. + { + m_bProcessingMessages = false; + continue; + } + else if (cmd == net_Disconnect) // Disconnect request. + { + char reason[1024]; + buf->ReadString(reason, sizeof(reason), false); + + m_MessageHandler->ConnectionClosing(reason, 1); + m_bProcessingMessages = false; + } + + m_bProcessingMessages = false; + + if (m_bShouldDelete) + delete this; + + return false; + } +} + +bool CNetChan::ReadSubChannelData(bf_read& buf) +{ + // TODO: rebuild this and hook + return false; +} + //----------------------------------------------------------------------------- // Purpose: send message // Input : &msg - @@ -129,10 +563,10 @@ bool CNetChan::_ProcessMessages(CNetChan* pChan, bf_read* pMsg) // bVoice - // Output : true on success, false on failure //----------------------------------------------------------------------------- -bool CNetChan::SendNetMsg(INetMessage& msg, bool bForceReliable, bool bVoice) +bool CNetChan::SendNetMsg(INetMessage& msg, const bool bForceReliable, const bool bVoice) { if (remote_address.GetType() == netadrtype_t::NA_NULL) - return false; + return true; bf_write* pStream = &m_StreamUnreliable; @@ -142,29 +576,127 @@ bool CNetChan::SendNetMsg(INetMessage& msg, bool bForceReliable, bool bVoice) if (bVoice) pStream = &m_StreamVoice; - if (pStream != &m_StreamUnreliable || - pStream->GetNumBytesLeft() >= NET_UNRELIABLE_STREAM_MINSIZE) - { - AcquireSRWLockExclusive(&LOCK); + if (pStream == &m_StreamUnreliable && pStream->GetNumBytesLeft() < NET_UNRELIABLE_STREAM_MINSIZE) + return true; - pStream->WriteUBitLong(msg.GetType(), NETMSG_TYPE_BITS); - if (!pStream->IsOverflowed()) - msg.WriteToBuffer(pStream); + AcquireSRWLockExclusive(&m_Lock); - ReleaseSRWLockExclusive(&LOCK); - } + pStream->WriteUBitLong(msg.GetType(), NETMSG_TYPE_BITS); + const bool ret = msg.WriteToBuffer(pStream); - return true; + ReleaseSRWLockExclusive(&m_Lock); + + return !pStream->IsOverflowed() && ret; +} + +//----------------------------------------------------------------------------- +// Purpose: send data +// Input : &msg - +// bReliable - +// Output : true on success, false on failure +//----------------------------------------------------------------------------- +bool CNetChan::SendData(bf_write& msg, const bool bReliable) +{ + // Always queue any pending reliable data ahead of the fragmentation buffer + + if (remote_address.GetType() == netadrtype_t::NA_NULL) + return true; + + if (msg.GetNumBitsWritten() <= 0) + return true; + + if (msg.IsOverflowed() && !bReliable) + return true; + + bf_write& buf = bReliable + ? m_StreamReliable + : m_StreamUnreliable; + + const int dataBits = msg.GetNumBitsWritten(); + const int bitsLeft = buf.GetNumBitsLeft(); + + if (dataBits > bitsLeft) + { + if (bReliable) + { + Error(eDLL_T::ENGINE, 0, "%s(%s): Data too large for reliable buffer (%i > %i)!\n", + __FUNCTION__, GetAddress(), msg.GetNumBytesWritten(), buf.GetNumBytesLeft()); + + m_MessageHandler->ChannelDisconnect("reliable buffer is full"); + } + + return false; + } + + return buf.WriteBits(msg.GetData(), dataBits); +} + +//----------------------------------------------------------------------------- +// Purpose: finds a registered net message by type +// Input : type - +// Output : net message pointer on success, NULL otherwise +//----------------------------------------------------------------------------- +INetMessage* CNetChan::FindMessage(int type) +{ + int numtypes = m_NetMessages.Count(); + + for (int i = 0; i < numtypes; i++) + { + if (m_NetMessages[i]->GetType() == type) + return m_NetMessages[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: registers a net message +// Input : *msg +// Output : true on success, false otherwise +//----------------------------------------------------------------------------- +bool CNetChan::RegisterMessage(INetMessage* msg) +{ + Assert(msg); + + if (FindMessage(msg->GetType())) + { + Assert(0); // Duplicate registration! + return false; + } + + m_NetMessages.AddToTail(msg); + msg->SetNetChannel(this); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: free's the receive data fragment list +//----------------------------------------------------------------------------- +void CNetChan::FreeReceiveList() +{ + m_ReceiveList.blockSize = NULL; + m_ReceiveList.transferSize = NULL; + if (m_ReceiveList.buffer) + { + delete m_ReceiveList.buffer; + m_ReceiveList.buffer = nullptr; + } +} + +//----------------------------------------------------------------------------- +// Purpose: check if there is still data in the reliable waiting buffers +//----------------------------------------------------------------------------- +bool CNetChan::HasPendingReliableData(void) +{ + return (m_StreamReliable.GetNumBitsWritten() > 0) + || (m_WaitingList.Count() > 0); } /////////////////////////////////////////////////////////////////////////////// -void VNetChan::Attach() const +void VNetChan::Detour(const bool bAttach) const { - DetourAttach((PVOID*)&v_NetChan_Shutdown, &CNetChan::_Shutdown); - DetourAttach((PVOID*)&v_NetChan_ProcessMessages, &CNetChan::_ProcessMessages); -} -void VNetChan::Detach() const -{ - DetourDetach((PVOID*)&v_NetChan_Shutdown, &CNetChan::_Shutdown); - DetourDetach((PVOID*)&v_NetChan_ProcessMessages, &CNetChan::_ProcessMessages); + DetourSetup(&CNetChan__Shutdown, &CNetChan::_Shutdown, bAttach); + DetourSetup(&CNetChan__FlowNewPacket, &CNetChan::_FlowNewPacket, bAttach); + DetourSetup(&CNetChan__ProcessMessages, &CNetChan::_ProcessMessages, bAttach); } diff --git a/r5dev/engine/net_chan.h b/r5dev/engine/net_chan.h index af07c23a..1a032bbe 100644 --- a/r5dev/engine/net_chan.h +++ b/r5dev/engine/net_chan.h @@ -28,18 +28,23 @@ class CClient; class CNetChan; //----------------------------------------------------------------------------- -struct netframe_t +typedef struct netframe_header_s { - float one; - float two; - float three; - float four; - float five; - float six; -}; + float time; + int size; + short choked; + bool valid; + float latency; +} netframe_header_t; + +typedef struct netframe_s +{ + int dropped; + float avg_latency; +} netframe_t; //----------------------------------------------------------------------------- -struct netflow_t +typedef struct netflow_s { float nextcompute; float avgbytespersec; @@ -48,26 +53,30 @@ struct netflow_t float avgchoke; float avglatency; float latency; + float maxlatency; int64_t totalpackets; int64_t totalbytes; + int64_t totalupdates; + int currentindex; + netframe_header_t frame_headers[NET_FRAMES_BACKUP]; netframe_t frames[NET_FRAMES_BACKUP]; - netframe_t current_frame; -}; + netframe_t* current_frame; +} netflow_t; //----------------------------------------------------------------------------- struct dataFragments_t { - char* data; - int64_t block_size; - bool m_bIsCompressed; + char* buffer; + int64_t blockSize; + bool isCompressed; uint8_t gap11[7]; - int64_t m_nRawSize; - bool m_bFirstFragment; - bool m_bLastFragment; - bool m_bIsOutbound; + int64_t uncompressedSize; + bool firstFragment; + bool lastFragment; + bool isOutbound; int transferID; - int m_nTransferSize; - int m_nCurrentOffset; + int transferSize; + int currentOffset; }; //----------------------------------------------------------------------------- @@ -78,41 +87,39 @@ enum EBufType BUF_VOICE }; -inline CMemory p_NetChan_Clear; -inline void(*v_NetChan_Clear)(CNetChan* pChan, bool bStopProcessing); - -inline CMemory p_NetChan_Shutdown; -inline void(*v_NetChan_Shutdown)(CNetChan* pChan, const char* szReason, uint8_t bBadRep, bool bRemoveNow); - -inline CMemory p_NetChan_CanPacket; -inline bool(*v_NetChan_CanPacket)(const CNetChan* pChan); - -inline CMemory p_NetChan_SendDatagram; -inline int(*v_NetChan_SendDatagram)(CNetChan* pChan, bf_write* pMsg); - -inline CMemory p_NetChan_ProcessMessages; -inline bool(*v_NetChan_ProcessMessages)(CNetChan* pChan, bf_read* pMsg); +inline void(*CNetChan__Clear)(CNetChan* pChan, bool bStopProcessing); +inline void(*CNetChan__Shutdown)(CNetChan* pChan, const char* szReason, uint8_t bBadRep, bool bRemoveNow); +inline bool(*CNetChan__CanPacket)(const CNetChan* pChan); +inline void(*CNetChan__FlowNewPacket)(CNetChan* pChan, int flow, int outSeqNr, int inSeqNr, int nChoked, int nDropped, int nSize); +inline int(*CNetChan__SendDatagram)(CNetChan* pChan, bf_write* pMsg); +inline bool(*CNetChan__ProcessMessages)(CNetChan* pChan, bf_read* pMsg); //----------------------------------------------------------------------------- class CNetChan { public: + ~CNetChan() + { + Shutdown("NetChannel removed.", 1, false); + FreeReceiveList(); + } + inline const char* GetName(void) const { return m_Name; } inline const char* GetAddress(bool onlyBase = false) const { return remote_address.ToString(onlyBase); } inline int GetPort(void) const { return int(ntohs(remote_address.GetPort())); } inline int GetDataRate(void) const { return m_Rate; } inline int GetBufferSize(void) const { return NET_FRAMES_BACKUP; } - float GetNetworkLoss() const; + float GetResendRate() const; - inline float GetLatency(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].latency; } - inline float GetAvgChoke(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgchoke; } - inline float GetAvgLatency(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avglatency; } - inline float GetAvgLoss(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgloss; } - inline float GetAvgPackets(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgpacketspersec; } - inline float GetAvgData(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgbytespersec; } - inline int64_t GetTotalData(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].totalbytes; } - inline int64_t GetTotalPackets(int flow) const { Assert(flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].totalpackets; } + inline float GetLatency(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].latency; } + inline float GetAvgChoke(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgchoke; } + inline float GetAvgLatency(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avglatency; } + inline float GetAvgLoss(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgloss; } + inline float GetAvgPackets(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgpacketspersec; } + inline float GetAvgData(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].avgbytespersec; } + inline int64_t GetTotalData(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].totalbytes; } + inline int64_t GetTotalPackets(int flow) const { Assert(flow >= 0 && flow < SDK_ARRAYSIZE(m_DataFlow)); return m_DataFlow[flow].totalpackets; } int GetSequenceNr(int flow) const; double GetTimeConnected(void) const; @@ -121,19 +128,33 @@ public: inline int GetSocket(void) const { return m_Socket; } inline const bf_write& GetStreamVoice(void) const { return m_StreamVoice; } inline const netadr_t& GetRemoteAddress(void) const { return remote_address; } + + int GetNumBitsWritten(const bool bReliable); + int GetNumBitsLeft(const bool bReliable); inline bool IsOverflowed(void) const { return m_StreamReliable.IsOverflowed(); } - inline bool CanPacket(void) const { return v_NetChan_CanPacket(this); } - inline int SendDatagram(bf_write* pDatagram) { return v_NetChan_SendDatagram(this, pDatagram); } - bool SendNetMsg(INetMessage& msg, bool bForceReliable, bool bVoice); + bool HasPendingReliableData(void); - inline void Clear(bool bStopProcessing) { v_NetChan_Clear(this, bStopProcessing); } - inline void Shutdown(const char* szReason, uint8_t bBadRep, bool bRemoveNow) - { v_NetChan_Shutdown(this, szReason, bBadRep, bRemoveNow); } + inline bool CanPacket(void) const { return CNetChan__CanPacket(this); } + inline int SendDatagram(bf_write* pDatagram) { return CNetChan__SendDatagram(this, pDatagram); } + bool SendNetMsg(INetMessage& msg, const bool bForceReliable, const bool bVoice); + bool SendData(bf_write& msg, const bool bReliable); + + INetMessage* FindMessage(int type); + bool RegisterMessage(INetMessage* msg); + + inline void Clear(bool bStopProcessing) { CNetChan__Clear(this, bStopProcessing); } + inline void Shutdown(const char* szReason, uint8_t bBadRep, bool bRemoveNow) { CNetChan__Shutdown(this, szReason, bBadRep, bRemoveNow); } + void FreeReceiveList(); + bool ProcessMessages(bf_read* pMsg); + + bool ReadSubChannelData(bf_read& buf); static void _Shutdown(CNetChan* pChan, const char* szReason, uint8_t bBadRep, bool bRemoveNow); static bool _ProcessMessages(CNetChan* pChan, bf_read* pMsg); + static void _FlowNewPacket(CNetChan* pChan, int flow, int outSeqNr, int inSeqNr, int nChoked, int nDropped, int nSize); + void SetChoked(); void SetRemoteFramerate(float flFrameTime, float flFrameTimeStdDeviation); inline void SetRemoteCPUStatistics(uint8_t nStats) { m_nServerCPU = nStats; } @@ -143,19 +164,16 @@ public: bool m_bProcessingMessages; bool m_bShouldDelete; bool m_bStopProcessing; - bool shutting_down; + bool m_bShuttingDown; int m_nOutSequenceNr; int m_nInSequenceNr; int m_nOutSequenceNrAck; int m_nChokedPackets; - int unknown_challenge_var; + int m_nRealTimePackets; // Number of packets without pre-scaled frame times. private: -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) - char pad[8]; -#endif int m_nLastRecvFlags; - RTL_SRWLOCK LOCK; + RTL_SRWLOCK m_Lock; bf_write m_StreamReliable; CUtlMemory m_ReliableDataBuffer; bf_write m_StreamUnreliable; @@ -181,11 +199,11 @@ private: uint32_t m_nSubOutSequenceNr; int m_nLastRecvNonce; bool m_bUseCompression; - uint32_t dword168; + uint32_t m_ChallengeNr; float m_Timeout; INetChannelHandler* m_MessageHandler; CUtlVector m_NetMessages; - uint64_t qword198; + void* m_UnusedInterfacePointer; // Previously: IDemoRecorder* m_DemoRecorder. int m_nQueuedPackets; float m_flRemoteFrameTime; float m_flRemoteFrameTimeStdDeviation; @@ -195,7 +213,7 @@ private: int64_t m_StreamSendBuffer; bf_write m_StreamSend; uint8_t m_bInMatch_maybe; - netflow_t m_DataFlow[2]; + netflow_t m_DataFlow[MAX_FLOWS]; int m_nLifetimePacketsDropped; int m_nSessionPacketsDropped; int m_nSequencesSkipped_MAYBE; @@ -205,11 +223,7 @@ private: char m_Name[NET_CHANNELNAME_MAXLEN]; netadr_t remote_address; }; -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) || defined (GAMEDLL_S2) -static_assert(sizeof(CNetChan) == 0x1AD0); -#else static_assert(sizeof(CNetChan) == 0x1AC8); -#endif //----------------------------------------------------------------------------- // Purpose: sets the remote frame times @@ -237,33 +251,25 @@ class VNetChan : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CNetChan::Clear", p_NetChan_Clear.GetPtr()); - LogFunAdr("CNetChan::Shutdown", p_NetChan_Shutdown.GetPtr()); - LogFunAdr("CNetChan::CanPacket", p_NetChan_CanPacket.GetPtr()); - LogFunAdr("CNetChan::SendDatagram", p_NetChan_SendDatagram.GetPtr()); - LogFunAdr("CNetChan::ProcessMessages", p_NetChan_ProcessMessages.GetPtr()); + LogFunAdr("CNetChan::Clear", CNetChan__Clear); + LogFunAdr("CNetChan::Shutdown", CNetChan__Shutdown); + LogFunAdr("CNetChan::CanPacket", CNetChan__CanPacket); + LogFunAdr("CNetChan::FlowNewPacket", CNetChan__FlowNewPacket); + LogFunAdr("CNetChan::SendDatagram", CNetChan__SendDatagram); + LogFunAdr("CNetChan::ProcessMessages", CNetChan__ProcessMessages); } virtual void GetFun(void) const { - p_NetChan_Clear = g_GameDll.FindPatternSIMD("88 54 24 10 53 55 57"); - v_NetChan_Clear = p_NetChan_Clear.RCast(); - - p_NetChan_Shutdown = g_GameDll.FindPatternSIMD("48 89 6C 24 18 56 57 41 56 48 83 EC 30 83 B9"); - v_NetChan_Shutdown = p_NetChan_Shutdown.RCast(); - - p_NetChan_CanPacket = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B D9 75 15 48 8B 05 ?? ?? ?? ??"); - v_NetChan_CanPacket = p_NetChan_CanPacket.RCast(); - - p_NetChan_SendDatagram = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 56 41 57 48 83 EC 70"); - v_NetChan_SendDatagram = p_NetChan_SendDatagram.RCast(); - - p_NetChan_ProcessMessages = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B FA"); - v_NetChan_ProcessMessages = p_NetChan_ProcessMessages.RCast(); + g_GameDll.FindPatternSIMD("88 54 24 10 53 55 57").GetPtr(CNetChan__Clear); + g_GameDll.FindPatternSIMD("48 89 6C 24 18 56 57 41 56 48 83 EC 30 83 B9").GetPtr(CNetChan__Shutdown); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B D9 75 15 48 8B 05 ?? ?? ?? ??").GetPtr(CNetChan__CanPacket); + g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 44 89 44 24 ?? 89 54 24 10 56").GetPtr(CNetChan__FlowNewPacket); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 56 41 57 48 83 EC 70").GetPtr(CNetChan__SendDatagram); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B FA").GetPtr(CNetChan__ProcessMessages); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/networkstringtable.cpp b/r5dev/engine/networkstringtable.cpp index fd9c478b..5c222016 100644 --- a/r5dev/engine/networkstringtable.cpp +++ b/r5dev/engine/networkstringtable.cpp @@ -95,33 +95,29 @@ void CNetworkStringTableContainer::WriteUpdateMessage(CNetworkStringTableContain #ifndef CLIENT_DLL if (sv_stats->GetBool()) { - uint8_t nCPUPercentage = static_cast(g_pServer->GetCPUUsage() * 100.0f); + const uint8_t nCPUPercentage = static_cast(g_pServer->GetCPUUsage() * 100.0f); SVC_ServerTick serverTick(g_pServer->GetTick(), *host_frametime_unbounded, *host_frametime_stddeviation, nCPUPercentage); serverTick.m_nGroup = 0; serverTick.m_bReliable = true; - serverTick.m_NetTick.m_nCommandTick = -1; // Statistics only, see 'CClientState::VProcessServerTick'. + + // -1 means we update statistics only; see 'CClientState::VProcessServerTick()'. + serverTick.m_NetTick.m_nCommandTick = -1; pMsg->WriteUBitLong(serverTick.GetType(), NETMSG_TYPE_BITS); + if (!pMsg->IsOverflowed()) { serverTick.WriteToBuffer(pMsg); } } #endif // !CLIENT_DLL - v_CNetworkStringTableContainer__WriteUpdateMessage(thisp, pClient, nTickAck, pMsg); + + Assert(!pMsg->IsOverflowed(), "Snapshot buffer overflowed before string table update!"); + CNetworkStringTableContainer__WriteUpdateMessage(thisp, pClient, nTickAck, pMsg); } -void VNetworkStringTableContainer::Attach() const +void VNetworkStringTableContainer::Detour(const bool bAttach) const { -#if !defined (CLIENT_DLL) && !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) // TODO: doesn't work properly for S0/S1 yet. - DetourAttach(&v_CNetworkStringTableContainer__WriteUpdateMessage, &CNetworkStringTableContainer::WriteUpdateMessage); -#endif // !CLIENT_DLL && !GAMEDLL_S0 && !GAMEDLL_S1 + DetourSetup(&CNetworkStringTableContainer__WriteUpdateMessage, &CNetworkStringTableContainer::WriteUpdateMessage, bAttach); } - -void VNetworkStringTableContainer::Detach() const -{ -#if !defined (CLIENT_DLL) && !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) // TODO: doesn't work properly for S0/S1 yet. - DetourDetach(&v_CNetworkStringTableContainer__WriteUpdateMessage, &CNetworkStringTableContainer::WriteUpdateMessage); -#endif // !CLIENT_DLL && !GAMEDLL_S0 && !GAMEDLL_S1 -} \ No newline at end of file diff --git a/r5dev/engine/networkstringtable.h b/r5dev/engine/networkstringtable.h index 6dc26128..91c68993 100644 --- a/r5dev/engine/networkstringtable.h +++ b/r5dev/engine/networkstringtable.h @@ -9,19 +9,32 @@ #define NETWORKSTRINGTABLE_H #include "tier0/fasttimer.h" #include "tier1/utlvector.h" +#include "tier1/utlhashtable.h" #include "tier1/bitbuf.h" +#include "public/networkstringtabledefs.h" #include "client/client.h" -typedef int TABLEID; - -class INetworkStringTable -{ - INetworkStringTable* m_pVTable; -}; - class CNetworkStringTable : public INetworkStringTable { public: + // Updating/Writing + virtual bool WriteStringTable(bf_write& buf) = 0; + virtual bool ReadStringTable(bf_read& buf) = 0; + + virtual void ParseUpdate(bf_read& buf, int numStrings) = 0; + + virtual pfnStringChanged GetCallback(void) = 0; + + virtual int WriteUpdate(CClient* const client, bf_write& buf, int tickAck) = 0; + virtual bool WriteBaselines(SVC_CreateStringTable& msg, char* msgBuffer, int msgBufferSize) = 0; + + virtual void PurgeAllClientSide(void) = 0; + virtual void EnableRollback(bool bState) = 0; + + // TODO[ AMOS ]: there are a few more entries below in the vftable that + // need to be mapped out, most of them set/get bit fields; + // see [ r5apex_ds + 0x1329888 ] + TABLEID GetTableId(void) const; int GetMaxStrings(void) const; const char* GetTableName(void) const; @@ -41,42 +54,39 @@ private: // !TODO }; -class CNetworkStringTableContainer : public INetworkStringTable +class CNetworkStringTableContainer : public INetworkStringTableContainer { public: static void WriteUpdateMessage(CNetworkStringTableContainer* thisp, CClient* client, unsigned int tick_ack, bf_write* msg); + // Guards so game .dll can't create tables at the wrong time + inline void AllowCreation(bool state) { m_bAllowCreation = state; } + private: bool m_bAllowCreation; // create guard int m_nTickCount; // current tick bool m_bLocked; // currently locked? bool m_bEnableRollback; // enables rollback feature + CUtlVector < CNetworkStringTable* > m_Tables; // the string tables }; -inline CMemory p_CNetworkStringTableContainer__WriteUpdateMessage; -inline void (*v_CNetworkStringTableContainer__WriteUpdateMessage)(CNetworkStringTableContainer* thisp, CClient* client, unsigned int tick_ack, bf_write* msg); +inline void (*CNetworkStringTableContainer__WriteUpdateMessage)(CNetworkStringTableContainer* thisp, CClient* client, unsigned int tick_ack, bf_write* msg); class VNetworkStringTableContainer : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CNetworkStringTableContainer::WriteUpdateMessage", p_CNetworkStringTableContainer__WriteUpdateMessage.GetPtr()); + LogFunAdr("CNetworkStringTableContainer::WriteUpdateMessage", CNetworkStringTableContainer__WriteUpdateMessage); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CNetworkStringTableContainer__WriteUpdateMessage = g_GameDll.FindPatternSIMD("48 89 74 24 ?? 55 57 41 54 41 55 41 56 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CNetworkStringTableContainer__WriteUpdateMessage = g_GameDll.FindPatternSIMD("48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 45 33 ED"); -#endif - v_CNetworkStringTableContainer__WriteUpdateMessage = - p_CNetworkStringTableContainer__WriteUpdateMessage.RCast(); + g_GameDll.FindPatternSIMD("48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 45 33 ED") + .GetPtr(CNetworkStringTableContainer__WriteUpdateMessage); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/sdk_dll.cpp b/r5dev/engine/sdk_dll.cpp index 7d858922..8297fe38 100644 --- a/r5dev/engine/sdk_dll.cpp +++ b/r5dev/engine/sdk_dll.cpp @@ -7,24 +7,5 @@ #include "core/stdafx.h" #include "tier1/cvar.h" #include "engine/sdk_dll.h" -#ifndef DEDICATED -#include "gameui/IBrowser.h" -#include "gameui/IConsole.h" -#endif // !DEDICATED -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CEngineSDK::FixedFrame() -{ - for (;;) - { -#ifndef DEDICATED - g_pBrowser->Think(); - g_pConsole->Think(); -#endif // !DEDICATED - std::this_thread::sleep_for(IntervalToDuration(sdk_fixedframe_tickinterval->GetFloat())); - } -} - -CEngineSDK* g_EngineSDK = new CEngineSDK(); +CEngineSDK g_EngineSDK; diff --git a/r5dev/engine/sdk_dll.h b/r5dev/engine/sdk_dll.h index 3830f242..03c73d03 100644 --- a/r5dev/engine/sdk_dll.h +++ b/r5dev/engine/sdk_dll.h @@ -4,9 +4,8 @@ class CEngineSDK { public: - void FixedFrame(); }; -extern CEngineSDK* g_EngineSDK; +extern CEngineSDK g_EngineSDK; #endif // SDK_DLL_H diff --git a/r5dev/engine/server/datablock_sender.cpp b/r5dev/engine/server/datablock_sender.cpp index bc81d981..aa230e51 100644 --- a/r5dev/engine/server/datablock_sender.cpp +++ b/r5dev/engine/server/datablock_sender.cpp @@ -1,27 +1,62 @@ //===========================================================================// // -// Purpose: server side datablock sender +// Purpose: server side data block sender // //===========================================================================// #include "engine/client/client.h" +#include "common/proto_oob.h" #include "datablock_sender.h" -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ServerDataBlockSender::~ServerDataBlockSender() -{ - v_ServerDataBlockSender__Destructor(this); -} +static ConVar net_compressDataBlockLzAcceleration("net_compressDataBlockLzAcceleration", "1", FCVAR_DEVELOPMENTONLY, "The acceleration value for LZ4 data block compression"); //----------------------------------------------------------------------------- -// Purpose: sends the datablock +// Purpose: sends the data block //----------------------------------------------------------------------------- -void ServerDataBlockSender::SendDataBlock(short unk0, int unk1, - short unk2, short unk3, const void* buffer, int length) +void ServerDataBlockSender::SendDataBlock(const short transferId, const int transferSize, + const short transferNr, const short blockNr, const uint8_t* const blockData, const int blockSize) { - v_ServerDataBlockSender__SendDataBlock(this, unk0, unk1, - unk2, unk3, buffer, length); + const CClient* const cl = m_pClient; + + if (!cl) + { + Assert(0, "ServerDataBlockSender::SendDataBlock() called without a valid client handle!"); + return; + } + + const CNetChan* const chan = cl->m_NetChannel; + + if (!chan) + { + Assert(0, "ServerDataBlockSender::SendDataBlock() called without a valid net channel!"); + return; + } + + char dataBuf[DATABLOCK_FRAGMENT_PACKET_SIZE]; + bf_write buf(&dataBuf, sizeof(dataBuf)); + + // msg data (gets processed on client's out of band packet handler) + buf.WriteLong(CONNECTIONLESS_HEADER); + buf.WriteByte(S2C_DATABLOCK_FRAGMENT); + + // transfer info + buf.WriteByte(transferId); + buf.WriteLong(transferSize); + buf.WriteByte(transferNr); + + // block info + buf.WriteByte(blockNr); + buf.WriteLong(blockSize); + + // block data + buf.WriteBytes(blockData, blockSize); + + // send the data block packet + v_NET_SendPacket(NULL, + chan->GetSocket(), + chan->GetRemoteAddress(), + buf.GetData(), + buf.GetNumBytesWritten(), + NULL, false, NULL, true); } //----------------------------------------------------------------------------- @@ -29,26 +64,25 @@ void ServerDataBlockSender::SendDataBlock(short unk0, int unk1, //----------------------------------------------------------------------------- float ServerDataBlockSender::GetResendRate() const { - float flRet = 0.0f; + const CClient* const pClient = m_pClient; - if (!m_pClient) - return flRet; + if (!pClient) + return 0.0f; + + const CNetChan* const pChan = pClient->GetNetChan(); - CNetChan* pChan = m_pClient->GetNetChan(); if (!pChan) - return flRet; + return 0.0f; - if (!m_bStartedTransfer) - { - flRet = pChan->GetNetworkLoss(); + if (m_bStartedTransfer) + return 0.0f; - if (flRet < net_datablock_networkLossForSlowSpeed->GetFloat()) - { - return m_flResendRate; - } - } + const float netResendRate = pChan->GetResendRate(); - return flRet; + if (netResendRate < net_datablock_networkLossForSlowSpeed->GetFloat()) + return m_flResendRate; + + return netResendRate; } //----------------------------------------------------------------------------- @@ -58,3 +92,57 @@ const char* ServerDataBlockSender::GetReceiverName() const { return m_pClient->m_szServerName; } + +//----------------------------------------------------------------------------- +// Purpose: write the whole data in the data block scratch buffer +//----------------------------------------------------------------------------- +void ServerDataBlockSender::WriteDataBlock(const uint8_t* const sourceData, const int dataSize, + const bool isMultiplayer, const char* const debugName) +{ + AcquireSRWLockExclusive(&m_Lock); + + ServerDataBlockHeader_s* const pHeader = reinterpret_cast(m_pScratchBuffer); + bool copyRaw = true; + + int actualDataSize = dataSize; + + if (net_compressDataBlock->GetBool()) + { + const int encodedSize = LZ4_compress_fast((const char*)sourceData, (char*)m_pScratchBuffer + sizeof(ServerDataBlockHeader_s), + dataSize, SNAPSHOT_SCRATCH_BUFFER_SIZE, net_compressDataBlockLzAcceleration.GetInt()); + + // this shouldn't happen at all + if (!encodedSize) + { + Assert(0); + Error(eDLL_T::SERVER, 0, "LZ4 error compressing data block for client.\n"); + } + + // make sure the encoded data is smaller than the raw data, in some cases + // this might turn larger which means we should just send raw data + else if (encodedSize < dataSize) + { + actualDataSize = encodedSize; + + pHeader->isCompressed = true; + copyRaw = false; + } + } + + // in case no compression was performed, we send the raw data + if (copyRaw) + { + // this should equal the dataSize at this point, even if compression failed + Assert(actualDataSize == dataSize); + + pHeader->isCompressed = false; + memcpy(m_pScratchBuffer + sizeof(ServerDataBlockHeader_s), sourceData, actualDataSize); + } + + // NOTE: we copy data in the scratch buffer with an offset of + // sizeof(ServerDataBlockHeader_s), the header gets send up as well so we + // have to take this into account !!! + StartBlockSender(actualDataSize + sizeof(ServerDataBlockHeader_s), isMultiplayer, debugName); + + ReleaseSRWLockExclusive(&m_Lock); +} diff --git a/r5dev/engine/server/datablock_sender.h b/r5dev/engine/server/datablock_sender.h index c6bab5be..29f505a9 100644 --- a/r5dev/engine/server/datablock_sender.h +++ b/r5dev/engine/server/datablock_sender.h @@ -1,87 +1,57 @@ +//===========================================================================// +// +// Purpose: server side data block sender +// +//===========================================================================// #ifndef DATABLOCK_SENDER_H #define DATABLOCK_SENDER_H -#include "idatablock.h" +#include "engine/shared/datablock.h" class CClient; class ServerDataBlockSender : public NetDataBlockSender { - friend class CClient; public: - virtual ~ServerDataBlockSender() override; - virtual void SendDataBlock(short unk0, int unk1, - short unk2, short unk3, const void* buffer, int length) override; + virtual void SendDataBlock(const short transferId, const int transferSize, const short transferNr, + const short blockNr, const uint8_t* const blockData, const int blockSize) override; virtual float GetResendRate() const override; virtual const char* GetReceiverName() const override; -protected: - char pad_0008[56]; - RTL_SRWLOCK LOCK; - char pad_0048[56]; - CClient* m_pClient; - char m_bInitialized; - char m_bStartedTransfer; - char m_bMultiplayer; - char field_8B; - short m_nTransferId; - short m_nCounter; - int m_nTransferSize; - int m_nTotalBlocks; - int m_nBlockAckTick; - int m_nCurrentBlock; - int m_nUnkA0; - int m_nBlockSendsAttempted; - float m_flResendRate; - char pad_00AC[4]; - double m_TimeCurrentSend; - double m_TimeFirstSend; - double m_TimeLastSend; - double m_flBlockTimesArray[DATABLOCK_STATUS_SIZE]; - char m_szDebugName[64]; - bool m_bBlockStatusArray[DATABLOCK_STATUS_SIZE]; - void* m_pData; - bool m_bAbnormalSending_Maybe; + void WriteDataBlock(const uint8_t* const sourceData, const int dataSize, const bool isMultiplayer, const char* const debugName); }; struct ServerDataBlock { - char SnapshotBuffer[295312]; // this might be wrong !!! - void* pUnkBlockStruct; + char blockBuffer[295312]; // this might be wrong !!! + void* userData; char gapC0008[56]; ServerDataBlockSender sender; }; -inline CMemory p_ServerDataBlockSender__Destructor; -inline void*(*v_ServerDataBlockSender__Destructor)(ServerDataBlockSender* thisptr); +struct ServerDataBlockHeader_s +{ + bool isCompressed; +}; -inline CMemory p_ServerDataBlockSender__SendDataBlock; -inline void* (*v_ServerDataBlockSender__SendDataBlock)(ServerDataBlockSender* thisptr, - short unk0, int unk1, short unk2, short unk3, const void* buffer, int length); +inline void* (*ServerDataBlockSender__SendDataBlock)(ServerDataBlockSender* thisptr, + const short transferId, const int transferSize, const short transferNr, + const short blockNr, const uint8_t* const blockData, const int blockSize); /////////////////////////////////////////////////////////////////////////////// class VServerDataBlockSender : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("ServerDataBlockSender::~ServerDataBlockSender", p_ServerDataBlockSender__Destructor.GetPtr()); - LogFunAdr("ServerDataBlockSender::SendDataBlock", p_ServerDataBlockSender__SendDataBlock.GetPtr()); + LogFunAdr("ServerDataBlockSender::SendDataBlock", ServerDataBlockSender__SendDataBlock); } virtual void GetFun(void) const { - p_ServerDataBlockSender__Destructor = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74" - " 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24" - " ?? 48 89 74 24 ?? 57 48 83 EC 20 33 F6 66 C7 81 ?? ?? ?? ?? ?? ??"); - v_ServerDataBlockSender__Destructor = p_ServerDataBlockSender__Destructor.RCast(); - - p_ServerDataBlockSender__SendDataBlock = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 99 ?? ?? ?? ??"); - v_ServerDataBlockSender__SendDataBlock = p_ServerDataBlockSender__SendDataBlock.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 99 ?? ?? ?? ??").GetPtr(ServerDataBlockSender__SendDataBlock); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/server/persistence.cpp b/r5dev/engine/server/persistence.cpp index 93f52b49..16c8d9b4 100644 --- a/r5dev/engine/server/persistence.cpp +++ b/r5dev/engine/server/persistence.cpp @@ -2,23 +2,6 @@ #include "vengineserver_impl.h" #include "persistence.h" -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -bool Persistence_SetXP(int a1, int* a2) +void VPersistence::Detour(const bool bAttach) const { - g_pEngineServer->PersistenceAvailable(nullptr, a1); - return v_Persistence_SetXP(a1, a2); } -#endif - -void VPersistence::Attach() const -{ -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - DetourAttach((LPVOID*)&v_Persistence_SetXP, &Persistence_SetXP); -#endif -} -void VPersistence::Detach() const -{ -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - DetourDetach((LPVOID*)&v_Persistence_SetXP, &Persistence_SetXP); -#endif -} \ No newline at end of file diff --git a/r5dev/engine/server/persistence.h b/r5dev/engine/server/persistence.h index 9abf3817..7c3ad9f4 100644 --- a/r5dev/engine/server/persistence.h +++ b/r5dev/engine/server/persistence.h @@ -1,31 +1,14 @@ #ifndef PERSISTENCE_H #define PERSISTENCE_H -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -inline CMemory p_Persistence_SetXP; -inline bool(*v_Persistence_SetXP)(int a1, int* a2); -#endif - /////////////////////////////////////////////////////////////////////////////// class VPersistence : public IDetour { - virtual void GetAdr(void) const - { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - LogFunAdr("Persistence_SetXP", p_Persistence_SetXP.GetPtr()); -#endif - } - virtual void GetFun(void) const - { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_Persistence_SetXP = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 33 FF 48 8B F2 3B 0D ?? ?? ?? ??"); - v_Persistence_SetXP = p_Persistence_SetXP.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 33 FF 48 8B F2 3B 0D ?? ?? ?? ??*/ -#endif - } + virtual void GetAdr(void) const { } + virtual void GetFun(void) const { } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/server/server.cpp b/r5dev/engine/server/server.cpp index f29e5435..b5efc531 100644 --- a/r5dev/engine/server/server.cpp +++ b/r5dev/engine/server/server.cpp @@ -19,6 +19,20 @@ #include "ebisusdk/EbisuSDK.h" #include "public/edict.h" #include "pluginsystem/pluginsystem.h" +#include "rtech/liveapi/liveapi.h" + +//--------------------------------------------------------------------------------- +// Console variables +//--------------------------------------------------------------------------------- +ConVar sv_showconnecting("sv_showconnecting", "1", FCVAR_RELEASE, "Logs information about the connecting client to the console"); +ConVar sv_globalBanlist("sv_globalBanlist", "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, "0 = Disable, 1 = Enable."); + +ConVar sv_banlistRefreshRate("sv_banlistRefreshRate", "30.0", FCVAR_DEVELOPMENTONLY, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f); +ConVar sv_statusRefreshRate("sv_statusRefreshRate", "0.5", FCVAR_RELEASE, "Server status refresh rate (seconds).", true, 0.f, false, 0.f); + +static ConVar sv_validatePersonaName("sv_validatePersonaName", "1", FCVAR_RELEASE, "Validate the client's textual persona name on connect."); +static ConVar sv_minPersonaNameLength("sv_minPersonaNameLength", "4", FCVAR_RELEASE, "The minimum length of the client's textual persona name.", true, 0.f, false, 0.f); +static ConVar sv_maxPersonaNameLength("sv_maxPersonaNameLength", "16", FCVAR_RELEASE, "The maximum length of the client's textual persona name.", true, 0.f, false, 0.f); //--------------------------------------------------------------------------------- // Purpose: Gets the number of human players on the server @@ -80,6 +94,17 @@ int CServer::GetNumClients(void) const return nClients; } +//--------------------------------------------------------------------------------- +// Purpose: Rejects connection request and sends back a message +// Input : iSocket - +// *pChallenge - +// *szMessage - +//--------------------------------------------------------------------------------- +void CServer::RejectConnection(int iSocket, netadr_t* pNetAdr, const char* szMessage) +{ + CServer__RejectConnection(this, iSocket, pNetAdr, szMessage); +} + //--------------------------------------------------------------------------------- // Purpose: Initializes a CSVClient for a new net connection. This will only be called // once for a player each game, not once for each level change. @@ -98,11 +123,11 @@ CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) char pszAddresBuffer[128]; // Render the client's address. pChallenge->netAdr.ToString(pszAddresBuffer, sizeof(pszAddresBuffer), true); - const bool bEnableLogging = sv_showconnecting->GetBool(); + const bool bEnableLogging = sv_showconnecting.GetBool(); const int nPort = int(ntohs(pChallenge->netAdr.GetPort())); if (bEnableLogging) - DevMsg(eDLL_T::SERVER, "Processing connectionless challenge for '[%s]:%i' ('%llu')\n", + Msg(eDLL_T::SERVER, "Processing connectionless challenge for '[%s]:%i' ('%llu')\n", pszAddresBuffer, nPort, nNucleusID); bool bValidName = false; @@ -110,8 +135,8 @@ CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) if (VALID_CHARSTAR(pszPersonaName) && V_IsValidUTF8(pszPersonaName)) { - if (sv_validatePersonaName->GetBool() && - !IsValidPersonaName(pszPersonaName, sv_minPersonaNameLength->GetInt(), sv_maxPersonaNameLength->GetInt())) + if (sv_validatePersonaName.GetBool() && + !IsValidPersonaName(pszPersonaName, sv_minPersonaNameLength.GetInt(), sv_maxPersonaNameLength.GetInt())) { bValidName = false; } @@ -132,9 +157,9 @@ CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) return nullptr; } - if (g_pBanSystem->IsBanListValid()) + if (g_BanSystem.IsBanListValid()) { - if (g_pBanSystem->IsBanned(pszAddresBuffer, nNucleusID)) + if (g_BanSystem.IsBanned(pszAddresBuffer, nNucleusID)) { pServer->RejectConnection(pServer->m_Socket, &pChallenge->netAdr, "#Valve_Reject_Banned"); if (bEnableLogging) @@ -145,9 +170,9 @@ CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) } } - CClient* pClient = v_CServer_ConnectClient(pServer, pChallenge); + CClient* pClient = CServer__ConnectClient(pServer, pChallenge); - for (auto& callback : !g_pPluginSystem->GetConnectClientCallbacks()) + for (auto& callback : !g_PluginSystem.GetConnectClientCallbacks()) { if (!callback(pServer, pClient, pChallenge)) { @@ -156,11 +181,14 @@ CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) } } - if (pClient && sv_globalBanlist->GetBool()) + if (pClient && sv_globalBanlist.GetBool()) { if (!pClient->GetNetChan()->GetRemoteAddress().IsLoopback()) { - std::thread th(SV_IsClientBanned, pClient, string(pszAddresBuffer), nNucleusID, string(pszPersonaName), nPort); + const string addressBufferCopy(pszAddresBuffer); + const string personaNameCopy(pszPersonaName); + + std::thread th(SV_CheckForBanAndDisconnect, pClient, addressBufferCopy, nNucleusID, personaNameCopy, nPort); th.detach(); } } @@ -169,25 +197,26 @@ CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge) } //--------------------------------------------------------------------------------- -// Purpose: Rejects connection request and sends back a message -// Input : iSocket - -// *pChallenge - -// *szMessage - +// Purpose: Sends netmessage to all active clients +// Input : *msg - +// onlyActive - +// reliable - //--------------------------------------------------------------------------------- -void CServer::RejectConnection(int iSocket, netadr_t* pNetAdr, const char* szMessage) +void CServer::BroadcastMessage(CNetMessage* const msg, const bool onlyActive, const bool reliable) { - v_CServer_RejectConnection(this, iSocket, pNetAdr, szMessage); + CServer__BroadcastMessage(this, msg, onlyActive, reliable); } //--------------------------------------------------------------------------------- // Purpose: Runs the server frame job // Input : flFrameTime - // bRunOverlays - -// bUniformSnapshotInterval - +// bUpdateFrame - //--------------------------------------------------------------------------------- -void CServer::FrameJob(double flFrameTime, bool bRunOverlays, bool bUniformSnapshotInterval) +void CServer::FrameJob(double flFrameTime, bool bRunOverlays, bool bUpdateFrame) { - v_CServer_FrameJob(flFrameTime, bRunOverlays, bUniformSnapshotInterval); + CServer__FrameJob(flFrameTime, bRunOverlays, bUpdateFrame); + LiveAPISystem()->RunFrame(); } //--------------------------------------------------------------------------------- @@ -196,27 +225,18 @@ void CServer::FrameJob(double flFrameTime, bool bRunOverlays, bool bUniformSnaps //--------------------------------------------------------------------------------- void CServer::RunFrame(CServer* pServer) { - v_CServer_RunFrame(pServer); + CServer__RunFrame(pServer); } /////////////////////////////////////////////////////////////////////////////// -void VServer::Attach() const +void VServer::Detour(const bool bAttach) const { - DetourAttach(&v_CServer_RunFrame, &CServer::RunFrame); -#if defined(GAMEDLL_S3) - DetourAttach((LPVOID*)&v_CServer_ConnectClient, &CServer::ConnectClient); - DetourAttach((LPVOID*)&v_CServer_FrameJob, &CServer::FrameJob); -#endif // !TODO: S1 and S2 CServer functions require work. + DetourSetup(&CServer__RunFrame, &CServer::RunFrame, bAttach); + DetourSetup(&CServer__ConnectClient, &CServer::ConnectClient, bAttach); + DetourSetup(&CServer__FrameJob, &CServer::FrameJob, bAttach); } -void VServer::Detach() const -{ - DetourDetach(&v_CServer_RunFrame, &CServer::RunFrame); -#if defined(GAMEDLL_S3) - DetourDetach((LPVOID*)&v_CServer_ConnectClient, &CServer::ConnectClient); - DetourDetach((LPVOID*)&v_CServer_FrameJob, &CServer::FrameJob); -#endif // !TODO: S1 and S2 CServer functions require work. -} /////////////////////////////////////////////////////////////////////////////// -CServer* g_pServer = nullptr; \ No newline at end of file +CServer* g_pServer = nullptr; +CClientExtended CServer::sm_ClientsExtended[MAX_PLAYERS]; diff --git a/r5dev/engine/server/server.h b/r5dev/engine/server/server.h index 951c4969..cc05ac46 100644 --- a/r5dev/engine/server/server.h +++ b/r5dev/engine/server/server.h @@ -45,9 +45,10 @@ public: inline int GetMaxClients(void) const { return m_nMaxClients; } inline int64_t GetMaxTeams(void) const { return m_iMaxTeams; } + inline CClient* GetClient(const int nIndex) { Assert(nIndex >= NULL && nIndex < MAX_PLAYERS); return &m_Clients[nIndex]; } + inline CClientExtended* GetClientExtended(const int nIndex) { Assert(nIndex >= NULL && nIndex < MAX_PLAYERS); return &sm_ClientsExtended[nIndex]; } - inline CClient* GetClient(int nIndex) { Assert(nIndex < MAX_PLAYERS); return &m_Clients[nIndex]; } - + inline float GetTime(void) const { return m_nTickCount * m_flTickInterval; } inline float GetCPUUsage(void) const { return m_fCPUPercent; } inline bool IsActive(void) const { return m_State >= server_state_t::ss_active; } @@ -56,8 +57,10 @@ public: void RejectConnection(int iSocket, netadr_t* pNetAdr, const char* szMessage); static CClient* ConnectClient(CServer* pServer, user_creds_s* pChallenge); + + void BroadcastMessage(CNetMessage* const msg, const bool onlyActive, const bool reliable); static void RunFrame(CServer* pServer); - static void FrameJob(double flFrameTime, bool bRunOverlays, bool bUniformSnapshotInterval); + static void FrameJob(double flFrameTime, bool bRunOverlays, bool bUpdateFrame); #endif // !CLIENT_DLL private: @@ -98,28 +101,29 @@ private: float m_fStartTime; float m_fLastCPUCheckTime; bool m_bTeams[MAX_TEAMS]; // Something with teams, unclear what this does; see '[r5apex_ds.exe + 0x30CE40]' + + // Maps directly to m_Clients, contains extended client data which we + // cannot add to the CClient class as it would otherwise mismatch the + // structure in the engine. + static CClientExtended sm_ClientsExtended[MAX_PLAYERS]; }; -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -// !TODO: check if struct size is correct for S1! -static_assert(sizeof(CServer) == 0x25220C0); -#else static_assert(sizeof(CServer) == 0x25264C0); -#endif extern CServer* g_pServer; +extern ConVar sv_globalBanlist; +extern ConVar sv_banlistRefreshRate; + +extern ConVar sv_statusRefreshRate; + +extern ConVar sv_showconnecting; + /* ==== CSERVER ========================================================================================================================================================= */ -inline CMemory p_CServer_FrameJob; -inline void(*v_CServer_FrameJob)(double flFrameTime, bool bRunOverlays, bool bUniformSnapshotInterval); - -inline CMemory p_CServer_RunFrame; -inline void(*v_CServer_RunFrame)(CServer* pServer); - -inline CMemory p_CServer_ConnectClient; -inline CClient*(*v_CServer_ConnectClient)(CServer* pServer, user_creds_s* pCreds); - -inline CMemory p_CServer_RejectConnection; -inline void*(*v_CServer_RejectConnection)(CServer* pServer, int iSocket, netadr_t* pNetAdr, const char* szMessage); +inline void(*CServer__FrameJob)(double flFrameTime, bool bRunOverlays, bool bUpdateFrame); +inline void(*CServer__RunFrame)(CServer* pServer); +inline CClient*(*CServer__ConnectClient)(CServer* pServer, user_creds_s* pCreds); +inline void*(*CServer__RejectConnection)(CServer* pServer, int iSocket, netadr_t* pNetAdr, const char* szMessage); +inline void (*CServer__BroadcastMessage)(CServer* pServer, CNetMessage* const msg, const bool onlyActive, const bool reliable); /////////////////////////////////////////////////////////////////////////////// class VServer : public IDetour @@ -127,36 +131,23 @@ class VServer : public IDetour virtual void GetAdr(void) const { #ifndef CLIENT_DLL - LogFunAdr("CServer::FrameJob", p_CServer_FrameJob.GetPtr()); - LogFunAdr("CServer::RunFrame", p_CServer_RunFrame.GetPtr()); - LogFunAdr("CServer::ConnectClient", p_CServer_ConnectClient.GetPtr()); - LogFunAdr("CServer::RejectConnection", p_CServer_RejectConnection.GetPtr()); - LogVarAdr("g_Server", reinterpret_cast(g_pServer)); + LogFunAdr("CServer::FrameJob", CServer__FrameJob); + LogFunAdr("CServer::RunFrame", CServer__RunFrame); + LogFunAdr("CServer::ConnectClient", CServer__ConnectClient); + LogFunAdr("CServer::RejectConnection", CServer__RejectConnection); + LogFunAdr("CServer::BroadcastMessage", CServer__BroadcastMessage); + LogVarAdr("g_Server", g_pServer); #endif // !CLIENT_DLL } virtual void GetFun(void) const { #ifndef CLIENT_DLL - p_CServer_FrameJob = g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 56 41 54 41 56"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CServer_ConnectClient = g_GameDll.FindPatternSIMD("44 89 44 24 ?? 55 56 57 48 8D AC 24 ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) - p_CServer_ConnectClient = g_GameDll.FindPatternSIMD("44 89 44 24 ?? 56 57 48 81 EC ?? ?? ?? ??"); -#else - p_CServer_ConnectClient = g_GameDll.FindPatternSIMD("40 55 57 41 55 41 57 48 8D AC 24 ?? ?? ?? ??"); -#endif + g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 56 41 54 41 56").GetPtr(CServer__FrameJob); + g_GameDll.FindPatternSIMD("40 55 57 41 55 41 57 48 8D AC 24 ?? ?? ?? ??").GetPtr(CServer__ConnectClient); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CServer_RunFrame = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 48 81 EC ?? ?? ?? ?? 0F 29 B4 24 ?? ?? ?? ??"); -#else - p_CServer_RunFrame = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 88 05 ?? ?? ?? ??").FollowNearCallSelf(); -#endif - p_CServer_RejectConnection = g_GameDll.FindPatternSIMD("4C 89 4C 24 ?? 53 55 56 57 48 81 EC ?? ?? ?? ?? 49 8B D9"); - - v_CServer_FrameJob = p_CServer_FrameJob.RCast(); /*48 89 6C 24 ?? 56 41 54 41 56*/ - v_CServer_RunFrame = p_CServer_RunFrame.RCast(); - v_CServer_ConnectClient = p_CServer_ConnectClient.RCast(); /*40 55 57 41 55 41 57 48 8D AC 24 ?? ?? ?? ??*/ - v_CServer_RejectConnection = p_CServer_RejectConnection.RCast(); /*4C 89 4C 24 ?? 53 55 56 57 48 81 EC ?? ?? ?? ?? 49 8B D9*/ + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 88 05 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(CServer__RunFrame); + g_GameDll.FindPatternSIMD("4C 89 4C 24 ?? 53 55 56 57 48 81 EC ?? ?? ?? ?? 49 8B D9").GetPtr(CServer__RejectConnection); + g_GameDll.FindPatternSIMD("4C 8B DC 45 88 43 18 56").GetPtr(CServer__BroadcastMessage); #endif // !CLIENT_DLL } virtual void GetVar(void) const @@ -166,7 +157,6 @@ class VServer : public IDetour #endif // !CLIENT_DLL } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index 5dcce362..9ad96112 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -12,77 +12,76 @@ #include "networksystem/pylon.h" #include "networksystem/bansystem.h" #include "engine/client/client.h" -#include "tier1/cvar.h" #include "server.h" //----------------------------------------------------------------------------- // Purpose: checks if particular client is banned on the comp server //----------------------------------------------------------------------------- -void SV_IsClientBanned(CClient* pClient, const string& svIPAddr, - const uint64_t nNucleusID, const string& svPersonaName, const int nPort) +void SV_CheckForBanAndDisconnect(CClient* const pClient, const string& svIPAddr, + const NucleusID_t nNucleusID, const string& svPersonaName, const int nPort) { Assert(pClient != nullptr); string svError; - bool bCompBanned = g_pMasterServer->CheckForBan(svIPAddr, nNucleusID, svPersonaName, svError); + const bool bCompBanned = g_MasterServer.CheckForBan(svIPAddr, nNucleusID, svPersonaName, svError); if (bCompBanned) { - if (!ThreadInMainThread()) - { - g_TaskScheduler->Dispatch([pClient, svError, svIPAddr, nNucleusID, nPort] + g_TaskQueue.Dispatch([pClient, svError, svIPAddr, nNucleusID, nPort] + { + // Make sure client isn't already disconnected, + // and that if there is a valid netchannel, that + // it hasn't been taken by a different client by + // the time this task is getting executed. + const CNetChan* const pChan = pClient->GetNetChan(); + if (pChan && pClient->GetNucleusID() == nNucleusID) { - // Make sure client isn't already disconnected, - // and that if there is a valid netchannel, that - // it hasn't been taken by a different client by - // the time this task is getting executed. - CNetChan* pChan = pClient->GetNetChan(); - if (pChan && pClient->GetNucleusID() == nNucleusID) - { - int nUserID = pClient->GetUserID(); + const int nUserID = pClient->GetUserID(); - pClient->Disconnect(Reputation_t::REP_MARK_BAD, svError.c_str()); - Warning(eDLL_T::SERVER, "Removed client '[%s]:%i' from slot #%i ('%llu' is banned globally!)\n", - svIPAddr.c_str(), nPort, nUserID, nNucleusID); - } - }, 0); - } + pClient->Disconnect(Reputation_t::REP_MARK_BAD, svError.c_str()); + Warning(eDLL_T::SERVER, "Removed client '[%s]:%i' from slot #%i ('%llu' is banned globally!)\n", + svIPAddr.c_str(), nPort, nUserID, nNucleusID); + } + }, 0); } } //----------------------------------------------------------------------------- // Purpose: checks if particular client is banned on the master server //----------------------------------------------------------------------------- -void SV_ProcessBulkCheck(const BannedVec_t& bannedVec) +void SV_ProcessBulkCheck(const CBanSystem::BannedList_t* const pBannedVec) { - BannedVec_t outBannedVec; - g_pMasterServer->GetBannedList(bannedVec, outBannedVec); + CBanSystem::BannedList_t* const outBannedVec = new CBanSystem::BannedList_t(); + g_MasterServer.GetBannedList(*pBannedVec, *outBannedVec); - if (!ThreadInMainThread()) - { - g_TaskScheduler->Dispatch([outBannedVec] - { - SV_CheckForBan(&outBannedVec); - }, 0); - } + g_TaskQueue.Dispatch([outBannedVec] + { + SV_CheckClientsForBan(outBannedVec); + delete outBannedVec; + }, 0); } //----------------------------------------------------------------------------- // Purpose: creates a snapshot of the currently connected clients // Input : *pBannedVec - if passed, will check for bans and kick the clients //----------------------------------------------------------------------------- -void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) +void SV_CheckClientsForBan(const CBanSystem::BannedList_t* const pBannedVec /*= nullptr*/) { Assert(ThreadInMainThread()); - BannedVec_t bannedVec; + + CBanSystem::BannedList_t* bannedVec = !pBannedVec + ? new CBanSystem::BannedList_t + : nullptr; for (int c = 0; c < g_ServerGlobalVariables->m_nMaxClients; c++) // Loop through all possible client instances. { - CClient* pClient = g_pServer->GetClient(c); + CClient* const pClient = g_pServer->GetClient(c); + if (!pClient) continue; - CNetChan* pNetChan = pClient->GetNetChan(); + const CNetChan* const pNetChan = pClient->GetNetChan(); + if (!pNetChan) continue; @@ -92,26 +91,28 @@ void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) if (pNetChan->GetRemoteAddress().IsLoopback()) continue; - const char* szIPAddr = pNetChan->GetAddress(true); - const uint64_t nNucleusID = pClient->GetNucleusID(); + const char* const szIPAddr = pNetChan->GetAddress(true); + const NucleusID_t nNucleusID = pClient->GetNucleusID(); // If no banned list was provided, build one with all clients // on the server. This will be used for bulk checking so live // bans could be performed, as this function is called periodically. - if (!pBannedVec) - bannedVec.push_back(std::make_pair(string(szIPAddr), nNucleusID)); + if (bannedVec) + bannedVec->AddToTail(CBanSystem::Banned_t(szIPAddr, nNucleusID)); else { // Check if current client is within provided banned list, and // prune if so... - for (auto& it : *pBannedVec) + FOR_EACH_VEC(*pBannedVec, i) { - if (it.second == pClient->GetNucleusID()) + const CBanSystem::Banned_t& banned = (*pBannedVec)[i]; + + if (banned.m_NucleusID == pClient->GetNucleusID()) { const int nUserID = pClient->GetUserID(); const int nPort = pNetChan->GetPort(); - pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", it.first.c_str()); + pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", banned.m_Address.String()); Warning(eDLL_T::SERVER, "Removed client '[%s]:%i' from slot #%i ('%llu' is banned globally!)\n", szIPAddr, nPort, nUserID, nNucleusID); } @@ -119,9 +120,20 @@ void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/) } } - if (!pBannedVec && !bannedVec.empty()) + if (bannedVec && !bannedVec->IsEmpty()) { - std::thread(&SV_ProcessBulkCheck, bannedVec).detach(); + std::thread bulkCheck([bannedVec]() + { + SV_ProcessBulkCheck(bannedVec); + delete bannedVec; + }); + + bulkCheck.detach(); + } + else if (bannedVec) + { + delete bannedVec; + bannedVec = nullptr; } } @@ -150,7 +162,7 @@ bool SV_ActivateServer() return v_SV_ActivateServer(); } -void SV_BroadcastVoiceData(CClient* cl, int nBytes, char* data) +void SV_BroadcastVoiceData(CClient* const cl, const int nBytes, char* const data) { if (!sv_voiceenable->GetBool()) return; diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 12cf7f4a..1c2e75d2 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -8,22 +8,11 @@ class CClient; class CClient; /* ==== SV_MAIN ======================================================================================================================================================= */ -inline CMemory p_SV_InitGameDLL; -inline void(*v_SV_InitGameDLL)(void); - -inline CMemory p_SV_ShutdownGameDLL; -inline void(*v_SV_ShutdownGameDLL)(void); - -inline CMemory p_SV_ActivateServer; -inline bool(*v_SV_ActivateServer)(void); - -inline CMemory p_SV_CreateBaseline; -inline bool(*v_SV_CreateBaseline)(void); - -inline CMemory p_CGameServer__SpawnServer; inline bool(*CGameServer__SpawnServer)(void* thisptr, const char* pszMapName, const char* pszMapGroupName); - -inline CMemory p_SV_BroadcastVoiceData; +inline void(*v_SV_InitGameDLL)(void); +inline void(*v_SV_ShutdownGameDLL)(void); +inline bool(*v_SV_ActivateServer)(void); +inline bool(*v_SV_CreateBaseline)(void); inline void(*v_SV_BroadcastVoiceData)(CClient* cl, int nBytes, char* data); inline bool* s_bIsDedicated = nullptr; @@ -39,9 +28,9 @@ inline bool IsDedicated() void SV_InitGameDLL(); void SV_ShutdownGameDLL(); bool SV_ActivateServer(); -void SV_BroadcastVoiceData(CClient* cl, int nBytes, char* data); -void SV_IsClientBanned(CClient* pClient, const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName, const int nPort); -void SV_CheckForBan(const BannedVec_t* pBannedVec = nullptr); +void SV_BroadcastVoiceData(CClient* const cl, const int nBytes, char* const data); +void SV_CheckForBanAndDisconnect(CClient* const pClient, const string& svIPAddr, const NucleusID_t nNucleusID, const string& svPersonaName, const int nPort); +void SV_CheckClientsForBan(const CBanSystem::BannedList_t* const pBannedVec = nullptr); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -49,34 +38,22 @@ class HSV_Main : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CGameServer::SpawnServer", p_CGameServer__SpawnServer.GetPtr()); - LogFunAdr("SV_InitGameDLL", p_SV_InitGameDLL.GetPtr()); - LogFunAdr("SV_ShutdownGameDLL", p_SV_ShutdownGameDLL.GetPtr()); - LogFunAdr("SV_ActivateServer", p_SV_ActivateServer.GetPtr()); - LogFunAdr("SV_CreateBaseline", p_SV_CreateBaseline.GetPtr()); - LogFunAdr("SV_BroadcastVoiceData", p_SV_BroadcastVoiceData.GetPtr()); - LogVarAdr("s_bIsDedicated", reinterpret_cast(s_bIsDedicated)); + LogFunAdr("CGameServer::SpawnServer", CGameServer__SpawnServer); + LogFunAdr("SV_InitGameDLL", v_SV_InitGameDLL); + LogFunAdr("SV_ShutdownGameDLL", v_SV_ShutdownGameDLL); + LogFunAdr("SV_ActivateServer", v_SV_ActivateServer); + LogFunAdr("SV_CreateBaseline", v_SV_CreateBaseline); + LogFunAdr("SV_BroadcastVoiceData", v_SV_BroadcastVoiceData); + LogVarAdr("s_bIsDedicated", s_bIsDedicated); } virtual void GetFun(void) const { - p_SV_InitGameDLL = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ??"); - p_SV_ShutdownGameDLL = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48"); - p_SV_ActivateServer = g_GameDll.FindPatternSIMD("48 8B C4 56 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 8D"); - p_SV_CreateBaseline = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 07"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CGameServer__SpawnServer = g_GameDll.FindPatternSIMD("40 53 55 56 57 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CGameServer__SpawnServer = g_GameDll.FindPatternSIMD("48 8B C4 53 55 56 57 41 54 41 55 41 57"); -#endif - p_SV_BroadcastVoiceData = g_GameDll.FindPatternSIMD("4C 8B DC 56 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??"); - - v_SV_InitGameDLL = p_SV_InitGameDLL.RCast(); - v_SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast(); - v_SV_ActivateServer = p_SV_ActivateServer.RCast(); - v_SV_CreateBaseline = p_SV_CreateBaseline.RCast(); - v_SV_BroadcastVoiceData = p_SV_BroadcastVoiceData.RCast(); - - CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast(); + g_GameDll.FindPatternSIMD("48 8B C4 53 55 56 57 41 54 41 55 41 57").GetPtr(CGameServer__SpawnServer); + g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ??").GetPtr(v_SV_InitGameDLL); + g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48").GetPtr(v_SV_ShutdownGameDLL); + g_GameDll.FindPatternSIMD("48 8B C4 56 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 8D").GetPtr(v_SV_ActivateServer); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 07").GetPtr(v_SV_CreateBaseline); + g_GameDll.FindPatternSIMD("4C 8B DC 56 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??").GetPtr(v_SV_BroadcastVoiceData); } virtual void GetVar(void) const { @@ -85,23 +62,13 @@ class HSV_Main : public IDetour } virtual void GetCon(void) const { } /////////////////////////////////////////////////////////////////////////////// - virtual void Attach(void) const + virtual void Detour(const bool bAttach) const { - //DetourAttach(&v_SV_InitGameDLL, SV_InitGameDLL); - //DetourAttach(&v_SV_ShutdownGameDLL, SV_ShutdownGameDLL); - //DetourAttach(&v_SV_ActivateServer, SV_ActivateServer); + //DetourSetup(&v_SV_InitGameDLL, SV_InitGameDLL, bAttach); + //DetourSetup(&v_SV_ShutdownGameDLL, SV_ShutdownGameDLL, bAttach); + //DetourSetup(&v_SV_ActivateServer, SV_ActivateServer, bAttach); #ifndef CLIENT_DLL - DetourAttach(&v_SV_BroadcastVoiceData, SV_BroadcastVoiceData); -#endif // !CLIENT_DLL - } - - virtual void Detach(void) const - { - //DetourDetach(&v_SV_InitGameDLL, SV_InitGameDLL); - //DetourDetach(&v_SV_ShutdownGameDLL, SV_ShutdownGameDLL); - //DetourDetach(&v_SV_ActivateServer, SV_ActivateServer); -#ifndef CLIENT_DLL - DetourDetach(&v_SV_BroadcastVoiceData, SV_BroadcastVoiceData); + DetourSetup(&v_SV_BroadcastVoiceData, SV_BroadcastVoiceData, bAttach); #endif // !CLIENT_DLL } }; diff --git a/r5dev/engine/server/sv_rcon.cpp b/r5dev/engine/server/sv_rcon.cpp index f331425d..b1acbf3d 100644 --- a/r5dev/engine/server/sv_rcon.cpp +++ b/r5dev/engine/server/sv_rcon.cpp @@ -13,17 +13,39 @@ #include "engine/server/sv_rcon.h" #include "protoc/sv_rcon.pb.h" #include "protoc/cl_rcon.pb.h" -#include "mathlib/sha256.h" #include "common/igameserverdata.h" +#include "mbedtls/include/mbedtls/sha512.h" //----------------------------------------------------------------------------- -// Purpose: +// Purpose: constants //----------------------------------------------------------------------------- static const char s_NoAuthMessage[] = "This server is password protected for console access; authenticate with 'PASS ' command.\n"; static const char s_WrongPwMessage[] = "Admin password incorrect.\n"; static const char s_AuthMessage[] = "Authentication successful.\n"; static const char s_BannedMessage[] = "Go away.\n"; +//----------------------------------------------------------------------------- +// Purpose: console variables +//----------------------------------------------------------------------------- +static void RCON_WhiteListAddresChanged_f(IConVar* pConVar, const char* pOldString); +static void RCON_ConnectionCountChanged_f(IConVar* pConVar, const char* pOldString); +static void RCON_PasswordChanged_f(IConVar* pConVar, const char* pOldString); + +static ConVar rcon_password("rcon_password", "", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote server access password (rcon is disabled if empty)", false, 0.f, false, 0.f, &RCON_PasswordChanged_f); + +static ConVar sv_rcon_debug("sv_rcon_debug", "0", FCVAR_RELEASE, "Show rcon debug information ( !slower! )"); +static ConVar sv_rcon_sendlogs("sv_rcon_sendlogs", "0", FCVAR_RELEASE, "Network console logs to connected and authenticated sockets"); + +//static ConVar sv_rcon_banpenalty("sv_rcon_banpenalty" , "10", FCVAR_RELEASE, "Number of minutes to ban users who fail rcon authentication"); + +static ConVar sv_rcon_maxfailures("sv_rcon_maxfailures", "10", FCVAR_RELEASE, "Max number of times an user can fail rcon authentication before being banned", true, 1.f, false, 0.f); +static ConVar sv_rcon_maxignores("sv_rcon_maxignores", "15", FCVAR_RELEASE, "Max number of times an user can ignore the instruction message before being banned", true, 1.f, false, 0.f); +static ConVar sv_rcon_maxsockets("sv_rcon_maxsockets", "32", FCVAR_RELEASE, "Max number of accepted sockets before the server starts closing redundant sockets", true, 1.f, true, MAX_PLAYERS); + +static ConVar sv_rcon_maxconnections("sv_rcon_maxconnections", "1", FCVAR_RELEASE, "Max number of authenticated connections before the server closes the listen socket", true, 1.f, true, MAX_PLAYERS, &RCON_ConnectionCountChanged_f); +static ConVar sv_rcon_maxpacketsize("sv_rcon_maxpacketsize", "1024", FCVAR_RELEASE, "Max number of bytes allowed in a command packet from a non-authenticated netconsole", true, 0.f, false, 0.f); +static ConVar sv_rcon_whitelist_address("sv_rcon_whitelist_address", "", FCVAR_RELEASE, "This address is not considered a 'redundant' socket and will never be banned for failed authentication attempts", &RCON_WhiteListAddresChanged_f, "Format: '::ffff:127.0.0.1'"); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -39,6 +61,9 @@ CRConServer::CRConServer(void) //----------------------------------------------------------------------------- CRConServer::~CRConServer(void) { + // NOTE: do not call Shutdown() from the destructor as the OS's socket + // system would be shutdown by now, call Shutdown() in application + // shutdown code instead } //----------------------------------------------------------------------------- @@ -48,7 +73,7 @@ void CRConServer::Init(void) { if (!m_bInitialized) { - if (!SetPassword(rcon_password->GetString())) + if (!SetPassword(rcon_password.GetString())) { return; } @@ -64,7 +89,7 @@ void CRConServer::Init(void) m_Address.SetFromString(Format("[%s]:%i", pszAddress, hostport->GetInt()).c_str(), true); m_Socket.CreateListenSocket(m_Address); - DevMsg(eDLL_T::SERVER, "Remote server access initialized ('%s')\n", m_Address.ToString()); + Msg(eDLL_T::SERVER, "Remote server access initialized ('%s')\n", m_Address.ToString()); m_bInitialized = true; } @@ -73,13 +98,26 @@ void CRConServer::Init(void) //----------------------------------------------------------------------------- void CRConServer::Shutdown(void) { + if (!m_bInitialized) + { + // If we aren't initialized, we shouldn't have any connections at all. + Assert(!m_Socket.GetAcceptedSocketCount(), "Accepted connections while RCON server isn't initialized!"); + Assert(!m_Socket.IsListening(), "Listen socket active while RCON server isn't initialized!"); + + return; + } + + m_bInitialized = false; + + const int nConnCount = m_Socket.GetAcceptedSocketCount(); m_Socket.CloseAllAcceptedSockets(); + if (m_Socket.IsListening()) { m_Socket.CloseListenSocket(); } - m_bInitialized = false; + Msg(eDLL_T::SERVER, "Remote server access deinitialized ('%i' accepted sockets closed)\n", nConnCount); } //----------------------------------------------------------------------------- @@ -90,7 +128,7 @@ void CRConServer::Think(void) const int nCount = m_Socket.GetAcceptedSocketCount(); // Close redundant sockets if there are too many except for whitelisted and authenticated. - if (nCount > sv_rcon_maxsockets->GetInt()) + if (nCount > sv_rcon_maxsockets.GetInt()) { for (m_nConnIndex = nCount - 1; m_nConnIndex >= 0; m_nConnIndex--) { @@ -123,9 +161,6 @@ void CRConServer::Think(void) //----------------------------------------------------------------------------- bool CRConServer::SetPassword(const char* pszPassword) { - m_bInitialized = false; - m_Socket.CloseAllAcceptedSockets(); - const size_t nLen = strlen(pszPassword); if (nLen < RCON_MIN_PASSWORD_LEN) { @@ -139,8 +174,32 @@ bool CRConServer::SetPassword(const char* pszPassword) return false; } - m_svPasswordHash = sha256(pszPassword); - DevMsg(eDLL_T::SERVER, "Password hash ('%s')\n", m_svPasswordHash.c_str()); + // This is here so we only print the confirmation message if the user + // actually requested to change the password rather than initializing + // the RCON server + const bool wasInitialized = m_bInitialized; + + m_bInitialized = false; + m_Socket.CloseAllAcceptedSockets(); + + const int nHashRet = mbedtls_sha512(reinterpret_cast(pszPassword), nLen, m_PasswordHash, NULL); + + if (nHashRet != 0) + { + Error(eDLL_T::SERVER, 0, "SHA-512 algorithm failed on RCON password [%i]\n", nHashRet); + + if (m_Socket.IsListening()) + { + m_Socket.CloseListenSocket(); + } + + return false; + } + + if (wasInitialized) + { + Msg(eDLL_T::SERVER, "Successfully changed RCON server password\n"); + } m_bInitialized = true; return true; @@ -180,7 +239,7 @@ void CRConServer::RunFrame(void) continue; } - Recv(data, sv_rcon_maxpacketsize->GetInt()); + Recv(data, sv_rcon_maxpacketsize.GetInt()); } } } @@ -323,59 +382,55 @@ void CRConServer::Authenticate(const cl_rcon::request& request, CConnectedNetCon { return; } - else // Authorize. + + // Authorize. + if (Comparator(request.requestmsg())) { - if (Comparator(request.requestmsg())) + data.m_bAuthorized = true; + if (++m_nAuthConnections >= sv_rcon_maxconnections.GetInt()) { - data.m_bAuthorized = true; - if (++m_nAuthConnections >= sv_rcon_maxconnections->GetInt()) - { - m_Socket.CloseListenSocket(); - CloseNonAuthConnection(); - } - - const char* pSendLogs = (!sv_rcon_sendlogs->GetBool() || data.m_bInputOnly) ? "0" : "1"; - - SendEncode(data.m_hSocket, s_AuthMessage, pSendLogs, - sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH, static_cast(eDLL_T::NETCON)); + m_Socket.CloseListenSocket(); + CloseNonAuthConnection(); } - else // Bad password. + + const char* pSendLogs = (!sv_rcon_sendlogs.GetBool() || data.m_bInputOnly) ? "0" : "1"; + + SendEncode(data.m_hSocket, s_AuthMessage, pSendLogs, + sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH, static_cast(eDLL_T::NETCON)); + } + else // Bad password. + { + const netadr_t& netAdr = m_Socket.GetAcceptedSocketAddress(m_nConnIndex); + if (sv_rcon_debug.GetBool()) { - const netadr_t netAdr = m_Socket.GetAcceptedSocketAddress(m_nConnIndex); - if (sv_rcon_debug->GetBool()) - { - DevMsg(eDLL_T::SERVER, "Bad RCON password attempt from '%s'\n", netAdr.ToString()); - } - - SendEncode(data.m_hSocket, s_WrongPwMessage, "", - sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH, static_cast(eDLL_T::NETCON)); - - data.m_bAuthorized = false; - data.m_bValidated = false; - data.m_nFailedAttempts++; + Msg(eDLL_T::SERVER, "Bad RCON password attempt from '%s'\n", netAdr.ToString()); } + + SendEncode(data.m_hSocket, s_WrongPwMessage, "", + sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH, static_cast(eDLL_T::NETCON)); + + data.m_bAuthorized = false; + data.m_bValidated = false; + data.m_nFailedAttempts++; } } //----------------------------------------------------------------------------- -// Purpose: sha256 hashed password comparison +// Purpose: sha512 hashed password comparison // Input : &svPassword - // Output : true if matches, false otherwise //----------------------------------------------------------------------------- bool CRConServer::Comparator(const string& svPassword) const { - string passwordHash = sha256(svPassword); - if (sv_rcon_debug->GetBool()) - { - DevMsg(eDLL_T::SERVER, "+---------------------------------------------------------------------------+\n"); - DevMsg(eDLL_T::SERVER, "[ Server: '%s']\n", m_svPasswordHash.c_str()); - DevMsg(eDLL_T::SERVER, "[ Client: '%s']\n", passwordHash.c_str()); - DevMsg(eDLL_T::SERVER, "+---------------------------------------------------------------------------+\n"); - } - if (memcmp(passwordHash.data(), m_svPasswordHash.data(), SHA256::DIGEST_SIZE) == 0) + uint8_t clientPasswordHash[RCON_SHA512_HASH_SIZE]; + mbedtls_sha512(reinterpret_cast(svPassword.c_str()), svPassword.length(), + clientPasswordHash, NULL); + + if (memcmp(clientPasswordHash, m_PasswordHash, RCON_SHA512_HASH_SIZE) == 0) { return true; } + return false; } @@ -430,11 +485,11 @@ bool CRConServer::ProcessMessage(const char* pMsgBuf, const int nMsgLen) const bool bWantLog = atoi(request.requestval().c_str()) != NULL; data.m_bInputOnly = !bWantLog; - if (bWantLog && !sv_rcon_sendlogs->GetBool()) + if (bWantLog && !sv_rcon_sendlogs.GetBool()) { // Toggle it on since there's at least 1 netconsole that // wants to receive logs. - sv_rcon_sendlogs->SetValue(bWantLog); + sv_rcon_sendlogs.SetValue(bWantLog); } } break; @@ -454,7 +509,7 @@ bool CRConServer::ProcessMessage(const char* pMsgBuf, const int nMsgLen) //----------------------------------------------------------------------------- void CRConServer::Execute(const cl_rcon::request& request) const { - const string& commandString = request.requestmsg().c_str(); + const string& commandString = request.requestmsg(); const char* const pCommandString = commandString.c_str(); ConCommandBase* pCommandBase = g_pCVar->FindCommandBase(pCommandString); @@ -467,6 +522,9 @@ void CRConServer::Execute(const cl_rcon::request& request) const const char* const pValueString = request.requestval().c_str(); + if (pCommandBase->IsFlagSet(FCVAR_SERVER_FRAME_THREAD)) + ThreadJoinServerJob(); + if (!pCommandBase->IsCommand()) { // Here we want to skip over the command string in the value buffer. @@ -492,7 +550,13 @@ void CRConServer::Execute(const cl_rcon::request& request) const else // Invoke command callback directly. { CCommand cmd; - cmd.Tokenize(pValueString, cmd_source_t::kCommandSrcCode); + + // Only tokenize if we actually have strings in the value buffer, some + // commands (like 'status') don't need any additional parameters. + if (VALID_CHARSTAR(pValueString)) + { + cmd.Tokenize(pValueString, cmd_source_t::kCommandSrcCode); + } v_Cmd_Dispatch(ECommandTarget_t::CBUF_SERVER, pCommandBase, &cmd, false); } @@ -509,12 +573,12 @@ bool CRConServer::CheckForBan(CConnectedNetConsoleData& data) return false; } - const netadr_t netAdr = m_Socket.GetAcceptedSocketAddress(m_nConnIndex); + const netadr_t& netAdr = m_Socket.GetAcceptedSocketAddress(m_nConnIndex); const char* szNetAdr = netAdr.ToString(true); if (m_BannedList.size() >= RCON_MAX_BANNEDLIST_SIZE) { - const char* pszWhiteListAddress = sv_rcon_whitelist_address->GetString(); + const char* pszWhiteListAddress = sv_rcon_whitelist_address.GetString(); if (!pszWhiteListAddress[0]) { Warning(eDLL_T::SERVER, "Banned list overflowed; please use a whitelist address. RCON shutting down...\n"); @@ -526,9 +590,9 @@ bool CRConServer::CheckForBan(CConnectedNetConsoleData& data) // Only allow whitelisted at this point. if (!m_WhiteListAddress.CompareAdr(netAdr)) { - if (sv_rcon_debug->GetBool()) + if (sv_rcon_debug.GetBool()) { - DevMsg(eDLL_T::SERVER, "Banned list is full; dropping '%s'\n", szNetAdr); + Msg(eDLL_T::SERVER, "Banned list is full; dropping '%s'\n", szNetAdr); } return true; @@ -544,8 +608,8 @@ bool CRConServer::CheckForBan(CConnectedNetConsoleData& data) } // Check if netconsole has reached maximum number of attempts > add to banned list. - if (data.m_nFailedAttempts >= sv_rcon_maxfailures->GetInt() - || data.m_nIgnoredMessage >= sv_rcon_maxignores->GetInt()) + if (data.m_nFailedAttempts >= sv_rcon_maxfailures.GetInt() + || data.m_nIgnoredMessage >= sv_rcon_maxignores.GetInt()) { // Don't add white listed address to banned list. if (m_WhiteListAddress.CompareAdr(netAdr)) @@ -578,16 +642,16 @@ void CRConServer::Disconnect(const char* szReason) // NETMGR void CRConServer::Disconnect(const int nIndex, const char* szReason) // NETMGR { CConnectedNetConsoleData& data = m_Socket.GetAcceptedSocketData(nIndex); - if (data.m_bAuthorized || sv_rcon_debug->GetBool()) + if (data.m_bAuthorized) { // Inform server owner when authenticated connection has been closed. - netadr_t netAdr = m_Socket.GetAcceptedSocketAddress(nIndex); + const netadr_t& netAdr = m_Socket.GetAcceptedSocketAddress(nIndex); if (!szReason) { szReason = "unknown reason"; } - DevMsg(eDLL_T::SERVER, "Connection to '%s' lost (%s)\n", netAdr.ToString(), szReason); + Msg(eDLL_T::SERVER, "Connection to '%s' lost (%s)\n", netAdr.ToString(), szReason); m_nAuthConnections--; } @@ -626,7 +690,7 @@ bool CRConServer::ShouldSend(const sv_rcon::response_t responseType) const if (responseType == sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG) { - if (!sv_rcon_sendlogs->GetBool() || !m_Socket.GetAuthorizedSocketCount()) + if (!sv_rcon_sendlogs.GetBool() || !m_Socket.GetAuthorizedSocketCount()) { // Disabled or no authorized clients to send to... return false; @@ -652,9 +716,82 @@ int CRConServer::GetAuthenticatedCount(void) const return m_nAuthConnections; } +//----------------------------------------------------------------------------- +// Purpose: change whitelist address on RCON server +//----------------------------------------------------------------------------- +static void RCON_WhiteListAddresChanged_f(IConVar* pConVar, const char* pOldString) +{ + if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName())) + { + if (strcmp(pOldString, pConVarRef->GetString()) == NULL) + return; // Same address. + + if (!RCONServer()->SetWhiteListAddress(pConVarRef->GetString())) + { + Warning(eDLL_T::SERVER, "Failed to set RCON whitelist address: %s\n", pConVarRef->GetString()); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: change max connection count on RCON server +//----------------------------------------------------------------------------- +static void RCON_ConnectionCountChanged_f(IConVar* pConVar, const char* pOldString) +{ + if (!RCONServer()->IsInitialized()) + return; // Not initialized; no sockets at this point. + + if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName())) + { + if (strcmp(pOldString, pConVarRef->GetString()) == NULL) + return; // Same count. + + const int maxCount = pConVarRef->GetInt(); + + int count = RCONServer()->GetAuthenticatedCount(); + CSocketCreator* pCreator = RCONServer()->GetSocketCreator(); + + if (count < maxCount) + { + if (!pCreator->IsListening()) + { + pCreator->CreateListenSocket(*RCONServer()->GetNetAddress()); + } + } + else + { + while (count > maxCount) + { + RCONServer()->Disconnect(count - 1, "too many authenticated sockets"); + count = RCONServer()->GetAuthenticatedCount(); + } + + pCreator->CloseListenSocket(); + RCONServer()->CloseNonAuthConnection(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: change RCON password on server and drop all connections +//----------------------------------------------------------------------------- +void RCON_PasswordChanged_f(IConVar* pConVar, const char* pOldString) +{ + if (ConVar* pConVarRef = g_pCVar->FindVar(pConVar->GetName())) + { + if (strcmp(pOldString, pConVarRef->GetString()) == NULL) + return; // Same password. + + if (RCONServer()->IsInitialized()) + RCONServer()->SetPassword(pConVarRef->GetString()); + else + RCONServer()->Init(); // Initialize first. + } +} + /////////////////////////////////////////////////////////////////////////////// -CRConServer* g_RCONServer(new CRConServer()); +static CRConServer s_RCONServer; CRConServer* RCONServer() // Singleton RCON Server. { - return g_RCONServer; + return &s_RCONServer; } diff --git a/r5dev/engine/server/sv_rcon.h b/r5dev/engine/server/sv_rcon.h index 75465527..a557181c 100644 --- a/r5dev/engine/server/sv_rcon.h +++ b/r5dev/engine/server/sv_rcon.h @@ -7,6 +7,7 @@ #define RCON_MIN_PASSWORD_LEN 8 #define RCON_MAX_BANNEDLIST_SIZE 512 +#define RCON_SHA512_HASH_SIZE 64 class CRConServer : public CNetConBase { @@ -59,7 +60,7 @@ private: int m_nAuthConnections; bool m_bInitialized; std::unordered_set m_BannedList; - std::string m_svPasswordHash; + uint8_t m_PasswordHash[RCON_SHA512_HASH_SIZE]; netadr_t m_WhiteListAddress; }; diff --git a/r5dev/engine/server/vengineserver_impl.cpp b/r5dev/engine/server/vengineserver_impl.cpp index 2865686e..6ee4bf96 100644 --- a/r5dev/engine/server/vengineserver_impl.cpp +++ b/r5dev/engine/server/vengineserver_impl.cpp @@ -12,21 +12,13 @@ bool CVEngineServer::PersistenceAvailable(void* entidx, int clientidx) { /////////////////////////////////////////////////////////////////////////// - return IVEngineServer__PersistenceAvailable(entidx, clientidx); + return CVEngineServer__PersistenceAvailable(entidx, clientidx); } -void HVEngineServer::Attach() const +void HVEngineServer::Detour(const bool bAttach) const { - DetourAttach(&IVEngineServer__PersistenceAvailable, &CVEngineServer::PersistenceAvailable); + DetourSetup(&CVEngineServer__PersistenceAvailable, &CVEngineServer::PersistenceAvailable, bAttach); } -void HVEngineServer::Detach() const -{ - DetourDetach(&IVEngineServer__PersistenceAvailable, &CVEngineServer::PersistenceAvailable); -} - -/////////////////////////////////////////////////////////////////////////////// -ServerPlayer_t g_ServerPlayer[MAX_PLAYERS]; - IVEngineServer* g_pEngineServerVFTable = nullptr; -CVEngineServer* g_pEngineServer = reinterpret_cast(&g_pEngineServerVFTable); \ No newline at end of file +CVEngineServer* g_pEngineServer = reinterpret_cast(&g_pEngineServerVFTable); diff --git a/r5dev/engine/server/vengineserver_impl.h b/r5dev/engine/server/vengineserver_impl.h index 605ecc67..66f3008f 100644 --- a/r5dev/engine/server/vengineserver_impl.h +++ b/r5dev/engine/server/vengineserver_impl.h @@ -3,37 +3,12 @@ #include "public/eiface.h" /* ==== CVENGINESERVER ================================================================================================================================================== */ -inline CMemory p_IVEngineServer__PersistenceAvailable; -inline bool(*IVEngineServer__PersistenceAvailable)(void* entidx, int clientidx); +inline bool(*CVEngineServer__PersistenceAvailable)(void* entidx, int clientidx); inline bool* m_bIsDedicated = nullptr; /////////////////////////////////////////////////////////////////////////////// -struct ServerPlayer_t -{ - ServerPlayer_t(void) - : m_flCurrentNetProcessTime(0.0) - , m_flLastNetProcessTime(0.0) - , m_flStringCommandQuotaTimeStart(0.0) - , m_nStringCommandQuotaCount(0) - {} - inline void Reset(void) - { - m_flCurrentNetProcessTime = 0.0; - m_flLastNetProcessTime = 0.0; - m_flStringCommandQuotaTimeStart = 0.0; - m_nStringCommandQuotaCount = 0; - } - - double m_flCurrentNetProcessTime; - double m_flLastNetProcessTime; - double m_flStringCommandQuotaTimeStart; - int m_nStringCommandQuotaCount; -}; - -extern ServerPlayer_t g_ServerPlayer[MAX_PLAYERS]; - class CVEngineServer : public IVEngineServer { public: @@ -48,14 +23,13 @@ class HVEngineServer : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CVEngineServer::`vftable'", reinterpret_cast(g_pEngineServerVFTable)); - LogFunAdr("CVEngineServer::PersistenceAvailable", p_IVEngineServer__PersistenceAvailable.GetPtr()); - LogVarAdr("m_bIsDedicated", reinterpret_cast(m_bIsDedicated)); // !TODO: part of CServer! + LogConAdr("CVEngineServer::`vftable'", g_pEngineServerVFTable); + LogFunAdr("CVEngineServer::PersistenceAvailable", CVEngineServer__PersistenceAvailable); + LogVarAdr("m_bIsDedicated", m_bIsDedicated); // !TODO: part of CServer! } virtual void GetFun(void) const { - p_IVEngineServer__PersistenceAvailable = g_GameDll.FindPatternSIMD("3B 15 ?? ?? ?? ?? 7D 33"); - IVEngineServer__PersistenceAvailable = p_IVEngineServer__PersistenceAvailable.RCast(); + g_GameDll.FindPatternSIMD("3B 15 ?? ?? ?? ?? 7D 33").GetPtr(CVEngineServer__PersistenceAvailable); } virtual void GetVar(void) const { @@ -65,7 +39,6 @@ class HVEngineServer : public IDetour m_bIsDedicated = pEngineServerVFTable.WalkVTableSelf(3).DerefSelf().ResolveRelativeAddress(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/shared/base_rcon.h b/r5dev/engine/shared/base_rcon.h index f46ad20e..5ce54733 100644 --- a/r5dev/engine/shared/base_rcon.h +++ b/r5dev/engine/shared/base_rcon.h @@ -11,7 +11,7 @@ public: CNetConBase(void) {} - virtual bool Connect(const char* pHostAdr, const int nHostPort = SOCKET_ERROR); + virtual bool Connect(const char* pHostName, const int nHostPort = SOCKET_ERROR); virtual void Disconnect(const char* szReason = nullptr) { NOTE_UNUSED(szReason); }; virtual bool ProcessBuffer(CConnectedNetConsoleData& data, const char* pRecvBuf, int nRecvLen, const int nMaxLen = SOCKET_ERROR); diff --git a/r5dev/engine/shared/datablock.cpp b/r5dev/engine/shared/datablock.cpp new file mode 100644 index 00000000..52c3dfc0 --- /dev/null +++ b/r5dev/engine/shared/datablock.cpp @@ -0,0 +1,112 @@ +//===========================================================================// +// +// Purpose: data block sender & receiver +// +//===========================================================================// +#include "datablock.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +NetDataBlockSender::~NetDataBlockSender() +{ + ResetBlockSender(); + + delete[] m_pScratchBuffer; + m_pScratchBuffer = nullptr; + + m_pClient = nullptr; +} + +//----------------------------------------------------------------------------- +// Purpose: reset the data block sender context +//----------------------------------------------------------------------------- +void NetDataBlockSender::ResetBlockSender(void) +{ + m_bInitialized = false; + m_bStartedTransfer = false; + + m_nTransferId = 0; + m_nTransferSize = 0; + m_nTotalBlocks = 0; + m_nBlockAckTick = 0; + + m_TimeCurrentSend = 0.0; + m_TimeFirstSend = 0.0; + + m_nTotalSizeRemaining = 0; + + m_TimeLastSend = 0.0; + m_szDebugName[0] = '\0'; + m_bDumbDataBlockInfo = false; + m_nCurrentBlock = DATABLOCK_INVALID_BLOCK_NR; + m_nBlockSendsAttempted = 0; + + memset(m_bBlockAckStatus, 0, sizeof(m_bBlockAckStatus)); + memset(m_flBlockSendTimes, 0, sizeof(m_flBlockSendTimes)); +} + +//----------------------------------------------------------------------------- +// Purpose: initialize the data block sender context +//----------------------------------------------------------------------------- +void NetDataBlockSender::StartBlockSender(const int transferSize, const bool isMultiplayer, const char* const debugName) +{ + m_bMultiplayer = isMultiplayer; + m_nBlockAckTick = 0; + m_nTransferSize = transferSize; + + // calculate the number of data blocks we have, which get sent individually + // to the receiver + m_nTotalBlocks = m_nTransferSize / MAX_DATABLOCK_FRAGMENT_SIZE + (m_nTransferSize % MAX_DATABLOCK_FRAGMENT_SIZE != 0); + + strncpy(m_szDebugName, debugName, sizeof(m_szDebugName)); + m_szDebugName[sizeof(m_szDebugName) - 1] = '\0'; + + // null status memory + memset(m_bBlockAckStatus, 0, sizeof(m_bBlockAckStatus)); + memset(m_flBlockSendTimes, 0, sizeof(m_flBlockSendTimes)); + + m_bInitialized = true; + m_bStartedTransfer = false; + + const double currentTime = Plat_FloatTime(); + + m_TimeLastSend = currentTime; + m_TimeCurrentSend = currentTime; + m_TimeFirstSend = currentTime; + + m_nTotalSizeRemaining = 4096; + m_nBlockSendsAttempted = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: initialize the data block receiver context +//----------------------------------------------------------------------------- +void NetDataBlockReceiver::StartBlockReceiver(const int transferSize, const double startTime) +{ + m_bStartedRecv = true; + m_nTransferSize = transferSize; + m_nTotalBlocks = transferSize / MAX_DATABLOCK_FRAGMENT_SIZE + (transferSize % MAX_DATABLOCK_FRAGMENT_SIZE != 0); + m_nBlockAckTick = 0; + m_flStartTime = startTime; + + memset(m_BlockStatus, 0, sizeof(m_BlockStatus)); +} + +//----------------------------------------------------------------------------- +// Purpose: reset the data block receiver context +//----------------------------------------------------------------------------- +void NetDataBlockReceiver::ResetBlockReceiver(const short transferNr) +{ + m_nTransferNr = transferNr; + + m_bStartedRecv = false; + m_bCompletedRecv = false; + + m_TransferId = 0; + m_nTotalBlocks = 0; + m_nBlockAckTick = 0; + m_flStartTime = 0.0; + + memset(m_BlockStatus, 0, sizeof(m_BlockStatus)); +} diff --git a/r5dev/engine/shared/datablock.h b/r5dev/engine/shared/datablock.h new file mode 100644 index 00000000..14be3b81 --- /dev/null +++ b/r5dev/engine/shared/datablock.h @@ -0,0 +1,173 @@ +//===========================================================================// +// +// Purpose: data block sender & receiver +// +//===========================================================================// +#ifndef IDATABLOCK_H +#define IDATABLOCK_H + +// the maximum size of each data block fragment +#define MAX_DATABLOCK_FRAGMENT_SIZE 1024 + +// the maximum amount of fragments per data block transfer +#define MAX_DATABLOCK_FRAGMENTS 768 + +#define DATABLOCK_DEBUG_NAME_LEN 64 +#define DATABLOCK_INVALID_BLOCK_NR -1 + +// the maximum size of a data block fragment packet (encoded header + actual data) +#define DATABLOCK_FRAGMENT_PACKET_SIZE (MAX_DATABLOCK_FRAGMENT_SIZE + 176) + +//----------------------------------------------------------------------------- +// Forward decelerations +//----------------------------------------------------------------------------- +class CClient; +class CClientState; + +abstract_class NetDataBlockSender +{ +public: + virtual ~NetDataBlockSender(); + + + virtual void SendDataBlock(const short transferId, const int transferSize, + const short transferNr, const short blockNr, const uint8_t* const blockData, const int blockSize) = 0; + virtual float GetResendRate() const = 0; + virtual const char* GetReceiverName() const = 0; + + void StartBlockSender(const int transferSize, const bool isMultiplayer, const char* const debugName); + void ResetBlockSender(); + +protected: + char pad_0008[56]; + RTL_SRWLOCK m_Lock; + char pad_0048[56]; + + // the server side client handle that is our 'receiving' end + CClient* m_pClient; + + char m_bInitialized; + char m_bStartedTransfer; + + char m_bMultiplayer; + char field_8B; + + // the current transfer id, and the global transfer count for this + // particular client. the transfer nr keeps getting incremented on + // each new context setup + short m_nTransferId; + short m_nTransferNr; + + // the total transfer size for the data, and the number of blocks this data + // has been carved up to + int m_nTransferSize; + int m_nTotalBlocks; + + // last block that has been ack'd, and the current block that is pending to + // be sent to the receiver + int m_nBlockAckTick; + int m_nCurrentBlock; + + // the total number of bytes remaining to be sent, and the number of times + // we attempted to send data blocks + int m_nTotalSizeRemaining; + int m_nBlockSendsAttempted; + + // the resend rate for this connection, which depends of the stability/loss + // and other factors computed from the netchan + float m_flResendRate; + char pad_00AC[4]; // padding, in case we want to stuff our own vars in here + + // times used to determine when a data block has been sent, and how long it + // took to get this out and acknowledged + double m_TimeCurrentSend; + double m_TimeFirstSend; + double m_TimeLastSend; + + // the last time we attempted to send this block, this gets updated when + // a data block hasn't been acknowledged in time and is being resent + double m_flBlockSendTimes[MAX_DATABLOCK_FRAGMENTS]; + + // the debug name used when details get dumped to the console + char m_szDebugName[DATABLOCK_DEBUG_NAME_LEN]; + + // if a data block has been acknowledged by the receiver, we mark that + // particular block as acknowledged + bool m_bBlockAckStatus[MAX_DATABLOCK_FRAGMENTS]; + uint8_t* m_pScratchBuffer; + bool m_bDumbDataBlockInfo; +}; + +abstract_class NetDataBlockReceiver +{ +public: + virtual ~NetDataBlockReceiver() {}; + // Called when cvar 'net_debugDataBlockReceiver' is set; + // currently a nullsub in the engine. + virtual void DebugDataBlockReceiver() {}; + virtual void AcknowledgeTransmission() = 0; + + void StartBlockReceiver(const int transferSize, const double startTime); + void ResetBlockReceiver(const short transferNr); + +protected: + // the client side 'client' handle + CClientState* m_pClientState; + + // whether the transfer has been started and completed + bool m_bStartedRecv; + bool m_bCompletedRecv; + + bool byte12; + + // the current transfer id, and the global transfer count for this + // particular client. the transfer nr keeps getting incremented on + // each new context setup + short m_TransferId; + short m_nTransferNr; + + bool m_bInitialized; + + // the total transfer size, and the # amount of blocks the data has been + // carved into + int m_nTransferSize; + int m_nTotalBlocks; + + int m_nBlockAckTick; + + // the time the data block receiver was started for this transfer + double m_flStartTime; + + // if we successfully processed a data block, we mark it as processed + bool m_BlockStatus[MAX_DATABLOCK_FRAGMENTS]; + char* m_pScratchBuffer; +}; + +inline void* (*NetDataBlockSender__Destructor)(NetDataBlockSender* thisptr); +inline void* (*NetDataBlockReceiver__Destructor)(NetDataBlockReceiver* thisptr); + +/////////////////////////////////////////////////////////////////////////////// +class VNetDataBlock : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("NetDataBlockSender::~NetDataBlockSender", NetDataBlockSender__Destructor); + LogFunAdr("NetDataBlockReceiver::~NetDataBlockReceiver", NetDataBlockReceiver__Destructor); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74" + " 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24" + " ?? 48 89 74 24 ?? 57 48 83 EC 20 33 F6 66 C7 81 ?? ?? ?? ?? ?? ??").GetPtr(NetDataBlockSender__Destructor); + + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74" + " 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24" + " ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8D 05 ?? ?? ?? ?? C6 41 12 00").GetPtr(NetDataBlockReceiver__Destructor); + } + virtual void GetVar(void) const { } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const { } +}; +/////////////////////////////////////////////////////////////////////////////// + +#endif // IDATABLOCK_H diff --git a/r5dev/engine/shared/shared_rcon.cpp b/r5dev/engine/shared/shared_rcon.cpp index 3aba56c9..1ead017f 100644 --- a/r5dev/engine/shared/shared_rcon.cpp +++ b/r5dev/engine/shared/shared_rcon.cpp @@ -79,7 +79,7 @@ bool CL_NetConConnect(CNetConBase* pBase, const char* pHostAdr, const int nHostP return false; } - DevMsg(eDLL_T::CLIENT, "Connected to: %s\n", pNetAdr->ToString()); + Msg(eDLL_T::CLIENT, "Connected to: %s\n", pNetAdr->ToString()); return true; } diff --git a/r5dev/engine/staticpropmgr.cpp b/r5dev/engine/staticpropmgr.cpp index 363931e5..fe58e1a2 100644 --- a/r5dev/engine/staticpropmgr.cpp +++ b/r5dev/engine/staticpropmgr.cpp @@ -5,7 +5,7 @@ //----------------------------------------------------------------------------- // Purpose: initialises static props from the static prop gamelump //----------------------------------------------------------------------------- -void* __fastcall CStaticProp_Init(int64_t thisptr, int64_t a2, unsigned int idx, unsigned int a4, StaticPropLump_t* lump, int64_t a6, int64_t a7) +void* CStaticProp::Init(CStaticProp* thisptr, int64_t a2, unsigned int idx, unsigned int a4, StaticPropLump_t* lump, int64_t a6, int64_t a7) { MDLHandle_t handle = *reinterpret_cast(a7 + 0x140); studiohdr_t* pStudioHdr = g_pMDLCache->FindMDL(g_pMDLCache, handle, nullptr); @@ -19,19 +19,35 @@ void* __fastcall CStaticProp_Init(int64_t thisptr, int64_t a2, unsigned int idx, lump->m_Skin = 0; } - return v_CStaticProp_Init(thisptr, a2, idx, a4, lump, a6, a7); + return CStaticProp__Init(thisptr, a2, idx, a4, lump, a6, a7); } -void VStaticPropMgr::Attach() const +//----------------------------------------------------------------------------- +// NOTE: the following gather props functions have been hooked as we must +// enable the old gather props logic for fall back models to draw !!! The +// new solution won't call CMDLCache::GetHardwareData() on bad model handles. +//----------------------------------------------------------------------------- +void* GatherStaticPropsSecondPass_PreInit(GatherProps_t* gather) { -#ifndef DEDICATED - DetourAttach((LPVOID*)&v_CStaticProp_Init, &CStaticProp_Init); -#endif // !DEDICATED + if (g_StudioMdlFallbackHandler.HasInvalidModelHandles()) + g_StudioMdlFallbackHandler.EnableLegacyGatherProps(); + + return v_GatherStaticPropsSecondPass_PreInit(gather); +} +void* GatherStaticPropsSecondPass_PostInit(GatherProps_t* gather) +{ + if (g_StudioMdlFallbackHandler.HasInvalidModelHandles()) + g_StudioMdlFallbackHandler.EnableLegacyGatherProps(); + + return v_GatherStaticPropsSecondPass_PostInit(gather); } -void VStaticPropMgr::Detach() const +void VStaticPropMgr::Detour(const bool bAttach) const { #ifndef DEDICATED - DetourDetach((LPVOID*)&v_CStaticProp_Init, &CStaticProp_Init); + DetourSetup(&CStaticProp__Init, &CStaticProp::Init, bAttach); #endif // !DEDICATED + + DetourSetup(&v_GatherStaticPropsSecondPass_PreInit, &GatherStaticPropsSecondPass_PreInit, bAttach); + DetourSetup(&v_GatherStaticPropsSecondPass_PostInit, &GatherStaticPropsSecondPass_PostInit, bAttach); } diff --git a/r5dev/engine/staticpropmgr.h b/r5dev/engine/staticpropmgr.h index f486a962..b7ee2ab8 100644 --- a/r5dev/engine/staticpropmgr.h +++ b/r5dev/engine/staticpropmgr.h @@ -1,30 +1,56 @@ #pragma once #include "public/gamebspfile.h" -inline CMemory p_CStaticProp_Init; -inline void*(*v_CStaticProp_Init)(int64_t thisptr, int64_t a2, unsigned int idx, unsigned int a4, StaticPropLump_t* lump, int64_t a6, int64_t a7); +struct GatherProps_t +{ + // TODO: reverse structure. + int field_0; + int field_4; + int field_8; + int field_C; + _BYTE gap10[8]; + int field_18; + int field_1C; + int field_20; + int field_24; + int field_28; + __int64 field_30; + int field_38; + int field_3C; + __int64 field_40; +}; -void* __fastcall CStaticProp_Init(int64_t thisptr, int64_t a2, unsigned int idx, unsigned int a4, StaticPropLump_t* lump, int64_t a6, int64_t a7); +class CStaticProp +{ +public: + static void* Init(CStaticProp* thisptr, int64_t a2, unsigned int idx, unsigned int a4, StaticPropLump_t* lump, int64_t a6, int64_t a7); + +private: // TODO: reverse structure. +}; + +inline void*(*CStaticProp__Init)(CStaticProp* thisptr, int64_t a2, unsigned int idx, unsigned int a4, StaticPropLump_t* lump, int64_t a6, int64_t a7); +inline void*(*v_GatherStaticPropsSecondPass_PreInit)(GatherProps_t* gather); +inline void* (*v_GatherStaticPropsSecondPass_PostInit)(GatherProps_t* gather); /////////////////////////////////////////////////////////////////////////////// class VStaticPropMgr : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CStaticProp::Init", p_CStaticProp_Init.GetPtr()); + LogFunAdr("CStaticProp::Init", CStaticProp__Init); } virtual void GetFun(void) const { - p_CStaticProp_Init = g_GameDll.FindPatternSIMD("48 8B C4 44 89 40 18 48 89 50 10 55"); /*48 8B C4 44 89 40 18 48 89 50 10 55*/ - v_CStaticProp_Init = p_CStaticProp_Init.RCast(); + g_GameDll.FindPatternSIMD("48 8B C4 44 89 40 18 48 89 50 10 55").GetPtr(CStaticProp__Init); + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8D 6C 24 ?? 48 8B 05 ?? ?? ?? ?? 4C 8B F9").GetPtr(v_GatherStaticPropsSecondPass_PreInit); + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8D 6C 24 ?? 48 8B 05 ?? ?? ?? ?? 4C 8B F1").GetPtr(v_GatherStaticPropsSecondPass_PostInit); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/sys_dll.cpp b/r5dev/engine/sys_dll.cpp index 7d4222fc..5a3d21a2 100644 --- a/r5dev/engine/sys_dll.cpp +++ b/r5dev/engine/sys_dll.cpp @@ -40,19 +40,9 @@ //----------------------------------------------------------------------------- bool CSourceAppSystemGroup::StaticPreInit(CSourceAppSystemGroup* pSourceAppSystemGroup) { - if (pSourceAppSystemGroup->GetCurrentStage() == CSourceAppSystemGroup::CREATION) - { - ConVar_InitShipped(); - ConVar_PurgeShipped(); - ConCommand_StaticInit(); - ConCommand_InitShipped(); - ConCommand_PurgeShipped(); - } - return CSourceAppSystemGroup__PreInit(pSourceAppSystemGroup); } - //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -66,20 +56,13 @@ bool CSourceAppSystemGroup::StaticCreate(CSourceAppSystemGroup* pSourceAppSystem //----------------------------------------------------------------------------- int CModAppSystemGroup::StaticMain(CModAppSystemGroup* pModAppSystemGroup) { - std::thread fixed(&CEngineSDK::FixedFrame, g_EngineSDK); - fixed.detach(); - int nRunResult = RUN_OK; HEbisuSDK_Init(); // Not here in retail. We init EbisuSDK here though. -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) // !TODO: rebuild does not work for S1 (CModAppSystemGroup and CEngine member offsets do align with all other builds). - return CModAppSystemGroup_Main(pModAppSystemGroup); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - g_pEngine->SetQuitting(IEngine::QUIT_NOTQUITTING); if (g_pEngine->Load(pModAppSystemGroup->IsServerOnly(), g_pEngineParms->baseDirectory)) { - if (CEngineAPI_MainLoop()) + if (CEngineAPI::MainLoop()) { nRunResult = RUN_RESTART; } @@ -90,7 +73,6 @@ int CModAppSystemGroup::StaticMain(CModAppSystemGroup* pModAppSystemGroup) #endif // !CLIENT_DLL } return nRunResult; -#endif } //----------------------------------------------------------------------------- @@ -102,30 +84,29 @@ bool CModAppSystemGroup::StaticCreate(CModAppSystemGroup* pModAppSystemGroup) pModAppSystemGroup->SetServerOnly(); *m_bIsDedicated = true; #endif // DEDICATED - g_pFactory->GetFactoriesFromRegister(); - g_pFactory->AddFactory(FACTORY_INTERFACE_VERSION, g_pFactory); - g_pFactory->AddFactory(INTERFACEVERSION_PLUGINSYSTEM, g_pPluginSystem); - g_pFactory->AddFactory(KEYVALUESSYSTEM_INTERFACE_VERSION, g_pKeyValuesSystem); + + EXPOSE_INTERFACE_FN((InstantiateInterfaceFn)PluginSystem, CPluginSystem, INTERFACEVERSION_PLUGINSYSTEM); + EXPOSE_INTERFACE_FN((InstantiateInterfaceFn)KeyValuesSystem, CKeyValuesSystem, KEYVALUESSYSTEM_INTERFACE_VERSION); InitPluginSystem(pModAppSystemGroup); - CALL_PLUGIN_CALLBACKS(g_pPluginSystem->GetCreateCallbacks(), pModAppSystemGroup); + CALL_PLUGIN_CALLBACKS(g_PluginSystem.GetCreateCallbacks(), pModAppSystemGroup); - g_pModSystem->Init(); + ModSystem()->Init(); - g_pDebugOverlay = g_pFactory->GetFactoryPtr(VDEBUG_OVERLAY_INTERFACE_VERSION, false).RCast(); + g_pDebugOverlay = (CIVDebugOverlay*)g_FactorySystem.GetFactory(VDEBUG_OVERLAY_INTERFACE_VERSION); #ifndef CLIENT_DLL - g_pServerGameDLL = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMEDLL, false).RCast(); - g_pServerGameClients = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMECLIENTS_NEW, false).RCast(); + g_pServerGameDLL = (CServerGameDLL*)g_FactorySystem.GetFactory(INTERFACEVERSION_SERVERGAMEDLL); + g_pServerGameClients = (CServerGameClients*)g_FactorySystem.GetFactory(INTERFACEVERSION_SERVERGAMECLIENTS_NEW); if (!g_pServerGameClients) - g_pServerGameClients = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMECLIENTS, false).RCast(); - g_pServerGameEntities = g_pFactory->GetFactoryPtr(INTERFACEVERSION_SERVERGAMEENTS, false).RCast(); + g_pServerGameClients = (CServerGameClients*)g_FactorySystem.GetFactory(INTERFACEVERSION_SERVERGAMECLIENTS); + g_pServerGameEntities = (CServerGameEnts*)g_FactorySystem.GetFactory(INTERFACEVERSION_SERVERGAMEENTS); #endif // !CLIENT_DLL #ifndef DEDICATED - g_pClientEntityList = g_pFactory->GetFactoryPtr(VCLIENTENTITYLIST_INTERFACE_VERSION, false).RCast(); - g_pEngineTraceClient = g_pFactory->GetFactoryPtr(INTERFACEVERSION_ENGINETRACE_CLIENT, false).RCast(); + g_pClientEntityList = (CClientEntityList*)g_FactorySystem.GetFactory(VCLIENTENTITYLIST_INTERFACE_VERSION); + g_pEngineTraceClient = (CEngineTraceClient*)g_FactorySystem.GetFactory(INTERFACEVERSION_ENGINETRACE_CLIENT); - g_pImGuiConfig->Load(); // Load ImGui configs. + g_ImGuiConfig.Load(); // Load ImGui configs. DirectX_Init(); #endif // !DEDICATED @@ -134,10 +115,10 @@ bool CModAppSystemGroup::StaticCreate(CModAppSystemGroup* pModAppSystemGroup) cv->EnableDevCvars(); } - g_FrameTasks.push_back(std::move(g_TaskScheduler)); + g_TaskQueueList.push_back(&g_TaskQueue); g_bAppSystemInit = true; - return CModAppSystemGroup_Create(pModAppSystemGroup); + return CModAppSystemGroup__Create(pModAppSystemGroup); } //----------------------------------------------------------------------------- @@ -145,14 +126,14 @@ bool CModAppSystemGroup::StaticCreate(CModAppSystemGroup* pModAppSystemGroup) //----------------------------------------------------------------------------- void CModAppSystemGroup::InitPluginSystem(CModAppSystemGroup* pModAppSystemGroup) { - // DEBUG CODE FOR PLUGINS - g_pPluginSystem->PluginSystem_Init(); - for (auto& it : g_pPluginSystem->GetPluginInstances()) + g_PluginSystem.Init(); + + for (auto& it : g_PluginSystem.GetInstances()) { - if (g_pPluginSystem->LoadPluginInstance(it)) - DevMsg(eDLL_T::ENGINE, "Loaded plugin: '%s'\n", it.m_svPluginName.c_str()); + if (g_PluginSystem.LoadInstance(it)) + Msg(eDLL_T::ENGINE, "Loaded plugin: '%s'\n", it.m_Name.String()); else - Warning(eDLL_T::ENGINE, "Failed loading plugin: '%s'\n", it.m_svPluginName.c_str()); + Warning(eDLL_T::ENGINE, "Failed loading plugin: '%s'\n", it.m_Name.String()); } } @@ -178,24 +159,13 @@ int HSys_Error_Internal(char* fmt, va_list args) return Sys_Error_Internal(fmt, args); } -void VSys_Dll::Attach() const +void VSys_Dll::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&CSourceAppSystemGroup__PreInit, &CSourceAppSystemGroup::StaticPreInit); - DetourAttach((LPVOID*)&CSourceAppSystemGroup__Create, &CSourceAppSystemGroup::StaticCreate); + DetourSetup(&CSourceAppSystemGroup__PreInit, &CSourceAppSystemGroup::StaticPreInit, bAttach); + DetourSetup(&CSourceAppSystemGroup__Create, &CSourceAppSystemGroup::StaticCreate, bAttach); - DetourAttach((LPVOID*)&CModAppSystemGroup_Main, &CModAppSystemGroup::StaticMain); - DetourAttach((LPVOID*)&CModAppSystemGroup_Create, &CModAppSystemGroup::StaticCreate); + DetourSetup(&CModAppSystemGroup__Main, &CModAppSystemGroup::StaticMain, bAttach); + DetourSetup(&CModAppSystemGroup__Create, &CModAppSystemGroup::StaticCreate, bAttach); - DetourAttach(&Sys_Error_Internal, &HSys_Error_Internal); -} - -void VSys_Dll::Detach() const -{ - DetourDetach((LPVOID*)&CSourceAppSystemGroup__PreInit, &CSourceAppSystemGroup::StaticPreInit); - DetourDetach((LPVOID*)&CSourceAppSystemGroup__Create, &CSourceAppSystemGroup::StaticCreate); - - DetourDetach((LPVOID*)&CModAppSystemGroup_Main, &CModAppSystemGroup::StaticMain); - DetourDetach((LPVOID*)&CModAppSystemGroup_Create, &CModAppSystemGroup::StaticCreate); - - DetourDetach(&Sys_Error_Internal, &HSys_Error_Internal); + DetourSetup(&Sys_Error_Internal, &HSys_Error_Internal, bAttach); } diff --git a/r5dev/engine/sys_dll.h b/r5dev/engine/sys_dll.h index ba8e43c3..41f155ea 100644 --- a/r5dev/engine/sys_dll.h +++ b/r5dev/engine/sys_dll.h @@ -40,22 +40,14 @@ private: }; /* ==== CAPPSYSTEMGROUP ================================================================================================================================================= */ -inline CMemory p_CModAppSystemGroup_Main; -inline int(*CModAppSystemGroup_Main)(CModAppSystemGroup* pModAppSystemGroup); - -inline CMemory p_CModAppSystemGroup_Create; -inline bool(*CModAppSystemGroup_Create)(CModAppSystemGroup* pModAppSystemGroup); - -inline CMemory p_CSourceAppSystemGroup__PreInit; +inline int(*CModAppSystemGroup__Main)(CModAppSystemGroup* pModAppSystemGroup); +inline bool(*CModAppSystemGroup__Create)(CModAppSystemGroup* pModAppSystemGroup); inline bool(*CSourceAppSystemGroup__PreInit)(CSourceAppSystemGroup* pModAppSystemGroup); - -inline CMemory p_CSourceAppSystemGroup__Create; inline bool(*CSourceAppSystemGroup__Create)(CSourceAppSystemGroup* pModAppSystemGroup); inline bool g_bAppSystemInit = false; /* ==== UTILITY ========================================================================================================================================================= */ -inline CMemory p_Sys_Error_Internal; inline int(*Sys_Error_Internal)(char* fmt, va_list args); inline bool* gfExtendedError = nullptr; @@ -68,42 +60,29 @@ class VSys_Dll : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CModAppSystemGroup::Main", p_CModAppSystemGroup_Main.GetPtr()); - LogFunAdr("CModAppSystemGroup::Create", p_CModAppSystemGroup_Create.GetPtr()); - LogFunAdr("CSourceAppSystemGroup::PreInit", p_CSourceAppSystemGroup__PreInit.GetPtr()); - LogFunAdr("CSourceAppSystemGroup::Create", p_CSourceAppSystemGroup__Create.GetPtr()); - LogFunAdr("Sys_Error_Internal", p_Sys_Error_Internal.GetPtr()); - LogVarAdr("gfExtendedError", reinterpret_cast(gfExtendedError)); + LogFunAdr("CModAppSystemGroup::Main", CModAppSystemGroup__Main); + LogFunAdr("CModAppSystemGroup::Create", CModAppSystemGroup__Create); + LogFunAdr("CSourceAppSystemGroup::PreInit", CSourceAppSystemGroup__PreInit); + LogFunAdr("CSourceAppSystemGroup::Create", CSourceAppSystemGroup__Create); + LogFunAdr("Sys_Error_Internal", Sys_Error_Internal); + LogVarAdr("gfExtendedError", gfExtendedError); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CModAppSystemGroup_Main = g_GameDll.FindPatternSIMD("48 83 EC 28 80 B9 ?? ?? ?? ?? ?? 48 8B 15 ?? ?? ?? ??"); - p_CModAppSystemGroup_Create = g_GameDll.FindPatternSIMD("48 8B C4 57 41 54 41 55 41 56 41 57 48 83 EC 60 48 C7 40 ?? ?? ?? ?? ?? 48 89 58 08"); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 80 B9 ?? ?? ?? ?? ?? BB ?? ?? ?? ??").GetPtr(CModAppSystemGroup__Main); + g_GameDll.FindPatternSIMD("48 8B C4 55 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 60").GetPtr(CModAppSystemGroup__Create); - p_CSourceAppSystemGroup__Create = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 E8 ?? ?? ?? ?? 33 C9"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CModAppSystemGroup_Main = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 80 B9 ?? ?? ?? ?? ?? BB ?? ?? ?? ??"); - p_CModAppSystemGroup_Create = g_GameDll.FindPatternSIMD("48 8B C4 55 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 60"); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 E8 ?? ?? ?? ?? 33 C9").GetPtr(CSourceAppSystemGroup__Create); + g_GameDll.FindPatternSIMD("48 89 74 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ??").GetPtr(CSourceAppSystemGroup__PreInit); - p_CSourceAppSystemGroup__Create = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 E8 ?? ?? ?? ?? 33 C9"); -#endif - p_CSourceAppSystemGroup__PreInit = g_GameDll.FindPatternSIMD("48 89 74 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ??"); - - CModAppSystemGroup_Main = p_CModAppSystemGroup_Main.RCast(); - CModAppSystemGroup_Create = p_CModAppSystemGroup_Create.RCast(); - CSourceAppSystemGroup__PreInit = p_CSourceAppSystemGroup__PreInit.RCast(); - CSourceAppSystemGroup__Create = p_CSourceAppSystemGroup__Create.RCast(); - - p_Sys_Error_Internal = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 ?? ?? 48 8B DA 48 8B F9 E8 ?? ?? ?? FF 33 F6 48"); - Sys_Error_Internal = p_Sys_Error_Internal.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 74 24 10 57 48 81 EC 30 08 ?? ?? 48 8B DA 48 8B F9 E8 ?? ?? ?? FF 33 F6 48").GetPtr(Sys_Error_Internal); } virtual void GetVar(void) const { - gfExtendedError = p_COM_ExplainDisconnection.Offset(0x0).FindPatternSelf("C6 05", CMemory::Direction::DOWN, 300).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + gfExtendedError = CMemory(v_COM_ExplainDisconnection).Offset(0x0) + .FindPatternSelf("C6 05", CMemory::Direction::DOWN, 300).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/sys_dll2.cpp b/r5dev/engine/sys_dll2.cpp index dd724580..a635cb27 100644 --- a/r5dev/engine/sys_dll2.cpp +++ b/r5dev/engine/sys_dll2.cpp @@ -1,4 +1,4 @@ -//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // @@ -10,13 +10,19 @@ #include "tier1/cmd.h" #include "tier1/cvar.h" #include "tier1/strtools.h" +#include "engine/sys_engine.h" #include "engine/sys_dll.h" #include "engine/sys_dll2.h" #include "engine/host_cmd.h" #include "engine/traceinit.h" -#include "rtech/rtech_utils.h" #ifndef DEDICATED +#include "engine/sys_mainwind.h" +#include "inputsystem/inputsystem.h" +#include "vgui/vgui_baseui_interface.h" +#include "materialsystem/cmaterialsystem.h" +#include "windows/id3dx.h" #include "client/vengineclient_impl.h" +#include "geforce/reflex.h" #endif // !DEDICATED #include "filesystem/filesystem.h" constexpr char DFS_ENABLE_PATH[] = "/vpk/enable.txt"; @@ -79,7 +85,7 @@ static void InitVPKSystem() InitReturnVal_t CEngineAPI::VInit(CEngineAPI* pEngineAPI) { - return CEngineAPI_Init(pEngineAPI); + return CEngineAPI__Init(pEngineAPI); } //----------------------------------------------------------------------------- @@ -90,7 +96,7 @@ bool CEngineAPI::VModInit(CEngineAPI* pEngineAPI, const char* pModName, const ch // Register new Pak Assets here! //RTech_RegisterAsset(0, 1, "", nullptr, nullptr, nullptr, CMemory(0x1660AD0A8).RCast(), 8, 8, 8, 0, 0xFFFFFFC); - bool results = CEngineAPI_ModInit(pEngineAPI, pModName, pGameDir); + bool results = CEngineAPI__ModInit(pEngineAPI, pModName, pGameDir); if (!IsValveMod(pModName) && !IsRespawnMod(pModName)) { #ifndef DEDICATED @@ -107,7 +113,6 @@ bool CEngineAPI::VModInit(CEngineAPI* pEngineAPI, const char* pModName, const ch //----------------------------------------------------------------------------- void CEngineAPI::VSetStartupInfo(CEngineAPI* pEngineAPI, StartupInfo_t* pStartupInfo) { -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) if (*g_bTextMode) { return; @@ -142,26 +147,138 @@ void CEngineAPI::VSetStartupInfo(CEngineAPI* pEngineAPI, StartupInfo_t* pStartup InitVPKSystem(); v_TRACEINIT(NULL, "COM_InitFilesystem( m_StartupInfo.m_szInitialMod )", "COM_ShutdownFileSystem()"); - COM_InitFilesystem(pEngineAPI->m_StartupInfo.m_szInitialMod); + v_COM_InitFilesystem(pEngineAPI->m_StartupInfo.m_szInitialMod); *g_bTextMode = true; -#else - // !TODO: 'TRACEINIT' needs to be reimplemented in S0/S1 (inline). - v_CEngineAPI_SetStartupInfo(pEngineAPI, pStartupInfo); -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CEngineAPI::PumpMessages() +{ +#ifndef DEDICATED + MSG msg; + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + if (in_syncRT->GetBool()) + (*g_fnSyncRTWithIn)(); + + g_pInputSystem->PollInputState(v_UIEventDispatcher); + g_pGame->DispatchAllStoredGameMessages(); +#endif // !DEDICATED +} + +#ifndef DEDICATED +//----------------------------------------------------------------------------- +// Purpose: force update NVIDIA Reflex Low Latency parameters +//----------------------------------------------------------------------------- +static void GFX_NVN_Changed_f(IConVar* pConVar, const char* pOldString) +{ + GFX_MarkLowLatencyParametersOutOfDate(); +} + +static ConVar fps_max_gfx("fps_max_gfx", "0", FCVAR_RELEASE, "Frame rate limiter using NVIDIA Reflex Low Latency SDK. -1 indicates the use of desktop refresh. 0 is disabled.", true, -1.f, true, 295.f, GFX_NVN_Changed_f); +static ConVar gfx_nvnUseLowLatency("gfx_nvnUseLowLatency", "1", FCVAR_RELEASE | FCVAR_ARCHIVE, "Enables NVIDIA Reflex Low Latency SDK.", GFX_NVN_Changed_f); +static ConVar gfx_nvnUseLowLatencyBoost("gfx_nvnUseLowLatencyBoost", "0", FCVAR_RELEASE | FCVAR_ARCHIVE, "Enables NVIDIA Reflex Low Latency Boost.", GFX_NVN_Changed_f); + +// NOTE: defaulted to 0 as it causes rubber banding on some hardware. +static ConVar gfx_nvnUseMarkersToOptimize("gfx_nvnUseMarkersToOptimize", "0", FCVAR_RELEASE, "Use NVIDIA Reflex Low Latency markers to optimize (requires Low Latency Boost to be enabled).", GFX_NVN_Changed_f); +#endif // !DEDICATED + +void CEngineAPI::UpdateLowLatencyParameters() +{ +#ifndef DEDICATED + const bool bUseLowLatencyMode = gfx_nvnUseLowLatency.GetBool(); + const bool bUseLowLatencyBoost = gfx_nvnUseLowLatencyBoost.GetBool(); + const bool bUseMarkersToOptimize = gfx_nvnUseMarkersToOptimize.GetBool(); + + float fpsMax = fps_max_gfx.GetFloat(); + + if (fpsMax == -1.0f) + { + const float globalFps = fps_max->GetFloat(); + + // Make sure the global fps limiter is 'unlimited' + // before we let the gfx frame limiter cap it to + // the desktop's refresh rate; not adhering to + // this will result in a major performance drop. + if (globalFps == 0.0f) + fpsMax = g_pGame->GetTVRefreshRate(); + else + fpsMax = 0.0f; // Don't let NVIDIA limit the frame rate. + } + + GFX_UpdateLowLatencyParameters(D3D11Device(), bUseLowLatencyMode, + bUseLowLatencyBoost, bUseMarkersToOptimize, fpsMax); +#endif // !DEDICATED +} + +void CEngineAPI::RunLowLatencyFrame() +{ +#ifndef DEDICATED + if (GFX_IsLowLatencySDKEnabled()) + { + if (GFX_HasPendingLowLatencyParameterUpdates()) + { + UpdateLowLatencyParameters(); + } + + GFX_RunLowLatencyFrame(D3D11Device()); + } +#endif // !DEDICATED +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CEngineAPI::MainLoop() +{ +#ifndef DEDICATED + bool bRunLowLatency = false; +#endif // !DEDICATED + + // Main message pump + while (true) + { + // Pump messages unless someone wants to quit + if (g_pEngine->GetQuitting() != IEngine::QUIT_NOTQUITTING) + { + if (g_pEngine->GetQuitting() != IEngine::QUIT_TODESKTOP) { + return true; + } + + return false; + } + +#ifndef DEDICATED + if (bRunLowLatency) { + CEngineAPI::RunLowLatencyFrame(); + bRunLowLatency = false; + } + CEngineAPI::PumpMessages(); +#endif // !DEDICATED + + if (g_pEngine->Frame()) + { +#ifndef DEDICATED + // Only run reflex if we ran an actual engine frame. + bRunLowLatency = true; +#endif // !DEDICATED + } + } } /////////////////////////////////////////////////////////////////////////////// -void VSys_Dll2::Attach() const +void VSys_Dll2::Detour(const bool bAttach) const { - DetourAttach(&CEngineAPI_Init, &CEngineAPI::VInit); - DetourAttach(&CEngineAPI_ModInit, &CEngineAPI::VModInit); - DetourAttach(&v_CEngineAPI_SetStartupInfo, &CEngineAPI::VSetStartupInfo); + DetourSetup(&CEngineAPI__Init, &CEngineAPI::VInit, bAttach); + DetourSetup(&CEngineAPI__ModInit, &CEngineAPI::VModInit, bAttach); + DetourSetup(&CEngineAPI__PumpMessages, &CEngineAPI::PumpMessages, bAttach); + DetourSetup(&CEngineAPI__MainLoop, &CEngineAPI::MainLoop, bAttach); + DetourSetup(&CEngineAPI__SetStartupInfo, &CEngineAPI::VSetStartupInfo, bAttach); } - -void VSys_Dll2::Detach() const -{ - DetourDetach(&CEngineAPI_Init, &CEngineAPI::VInit); - DetourDetach(&CEngineAPI_ModInit, &CEngineAPI::VModInit); - DetourDetach(&v_CEngineAPI_SetStartupInfo, &CEngineAPI::VSetStartupInfo); -} \ No newline at end of file diff --git a/r5dev/engine/sys_dll2.h b/r5dev/engine/sys_dll2.h index 6f76c014..4049d411 100644 --- a/r5dev/engine/sys_dll2.h +++ b/r5dev/engine/sys_dll2.h @@ -5,13 +5,13 @@ class CEngineAPI : public IEngineAPI { public: - virtual bool Connect(CreateInterfaceFn factory) = 0; + virtual bool Connect(const CreateInterfaceFn factory) = 0; virtual void Disconnect() = 0; - virtual void* QueryInterface(const char* pInterfaceName) = 0; + virtual void* QueryInterface(const char* const pInterfaceName) = 0; virtual InitReturnVal_t Init() = 0; virtual void Shutdown() = 0; virtual AppSystemTier_t GetTier() = 0; - virtual void Reconnect(CreateInterfaceFn factory, const char* pInterfaceName) = 0; + virtual void Reconnect(const CreateInterfaceFn factory, const char* const pInterfaceName) = 0; // This function must be called before init virtual bool SetStartupInfo(StartupInfo_t& info) = 0; @@ -33,35 +33,27 @@ public: static InitReturnVal_t VInit(CEngineAPI* thisp); static bool VModInit(CEngineAPI* pEngineAPI, const char* pModName, const char* pGameDir); static void VSetStartupInfo(CEngineAPI* pEngineAPI, StartupInfo_t* pStartupInfo); + + static void PumpMessages(); + static void RunLowLatencyFrame(); + static void UpdateLowLatencyParameters(); + + static bool MainLoop(); //private: void* m_hEditorHWnd; bool m_bRunningSimulation; StartupInfo_t m_StartupInfo; }; -inline CMemory p_CEngineAPI_Init; -inline InitReturnVal_t(*CEngineAPI_Init)(CEngineAPI* thisp); - -inline CMemory p_CEngineAPI_Shutdown; -inline void(*CEngineAPI_Shutdown)(void); - -inline CMemory p_CEngineAPI_Connect; -inline bool(*CEngineAPI_Connect)(CEngineAPI* thisptr, CreateInterfaceFn factory); - -inline CMemory p_CEngineAPI_ModInit; -inline bool(*CEngineAPI_ModInit)(CEngineAPI* pEngineAPI, const char* pModName, const char* pGameDir); - -inline CMemory p_CEngineAPI_MainLoop; -inline bool(*CEngineAPI_MainLoop)(void); - -inline CMemory p_CEngineAPI_SetStartupInfo; -inline void(*v_CEngineAPI_SetStartupInfo)(CEngineAPI* pEngineAPI, StartupInfo_t* pStartupInfo); - -inline CMemory p_ResetMTVFTaskItem; +inline InitReturnVal_t(*CEngineAPI__Init)(CEngineAPI* thisp); +inline void(*CEngineAPI__Shutdown)(void); +inline bool(*CEngineAPI__Connect)(CEngineAPI* thisptr, CreateInterfaceFn factory); +inline bool(*CEngineAPI__ModInit)(CEngineAPI* pEngineAPI, const char* pModName, const char* pGameDir); +inline bool(*CEngineAPI__MainLoop)(void); +inline void(*CEngineAPI__PumpMessages)(void); +inline void(*CEngineAPI__SetStartupInfo)(CEngineAPI* pEngineAPI, StartupInfo_t* pStartupInfo); inline void*(*v_ResetMTVFTaskItem)(void); - -inline CMemory p_PakFile_Init; -inline void(*PakFile_Init)(char* buffer, char* source, char vpk_file); +inline void(*v_PakFile_Init)(char* buffer, char* source, char vpk_file); inline bool* g_bTextMode = nullptr; inline char* g_szBaseDir = nullptr; // static size = 260 @@ -73,57 +65,41 @@ class VSys_Dll2 : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CEngineAPI::Init", p_CEngineAPI_Init.GetPtr()); - LogFunAdr("CEngineAPI::Shutdown", p_CEngineAPI_Shutdown.GetPtr()); - LogFunAdr("CEngineAPI::Connect", p_CEngineAPI_Connect.GetPtr()); - LogFunAdr("CEngineAPI::ModInit", p_CEngineAPI_ModInit.GetPtr()); - LogFunAdr("CEngineAPI::MainLoop", p_CEngineAPI_MainLoop.GetPtr()); -#if defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - LogFunAdr("CEngineAPI::SetStartupInfo", p_CEngineAPI_SetStartupInfo.GetPtr()); -#endif - LogFunAdr("ResetMTVFTaskItem", p_ResetMTVFTaskItem.GetPtr()); - LogFunAdr("PakFile_Init", p_PakFile_Init.GetPtr()); - LogVarAdr("g_bTextMode", reinterpret_cast(g_bTextMode)); - LogVarAdr("g_szBaseDir", reinterpret_cast(g_szBaseDir)); - LogVarAdr("g_pMTVFTaskItem", reinterpret_cast(g_pMTVFTaskItem)); - LogVarAdr("g_szMTVFItemName", reinterpret_cast(g_szMTVFItemName)); + LogFunAdr("CEngineAPI::Init", CEngineAPI__Init); + LogFunAdr("CEngineAPI::Shutdown", CEngineAPI__Shutdown); + LogFunAdr("CEngineAPI::Connect", CEngineAPI__Connect); + LogFunAdr("CEngineAPI::ModInit", CEngineAPI__ModInit); + LogFunAdr("CEngineAPI::MainLoop", CEngineAPI__MainLoop); + LogFunAdr("CEngineAPI::PumpMessages", CEngineAPI__PumpMessages); + LogFunAdr("CEngineAPI::SetStartupInfo", CEngineAPI__SetStartupInfo); + LogFunAdr("ResetMTVFTaskItem", v_ResetMTVFTaskItem); + LogFunAdr("PakFile_Init", v_PakFile_Init); + LogVarAdr("g_bTextMode", g_bTextMode); + LogVarAdr("g_szBaseDir", g_szBaseDir); + LogVarAdr("g_pMTVFTaskItem", g_pMTVFTaskItem); + LogVarAdr("g_szMTVFItemName", g_szMTVFItemName); } virtual void GetFun(void) const { - p_CEngineAPI_Init = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F1 48 8D 3D ?? ?? ?? ?? 33 DB 48 8D 15 ?? ?? ?? ??"); - p_CEngineAPI_Connect = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 89 15"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CEngineAPI_Shutdown = g_GameDll.FindPatternSIMD("41 54 41 56 48 83 EC 38 48 8B 0D ?? ?? ?? ??"); - p_CEngineAPI_ModInit = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 4D 8B F0"); - p_CEngineAPI_MainLoop = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 81 EC ?? ?? ?? ?? 45 33 C9"); - p_PakFile_Init = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 44 88 44 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 20"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CEngineAPI_Shutdown = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 33 D2 48 8B 01 FF 90 ?? ?? ?? ?? B1 01"); - p_CEngineAPI_ModInit = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 4D 8B F8"); - p_CEngineAPI_MainLoop = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 8B 15 ?? ?? ?? ?? 84 C0 B9 ?? ?? ?? ??").FollowNearCallSelf(); - p_PakFile_Init = g_GameDll.FindPatternSIMD("44 88 44 24 ?? 53 55 56 57"); -#endif - p_CEngineAPI_SetStartupInfo = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? ?? 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 48 8B DA"); - p_ResetMTVFTaskItem = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 15 ?? ?? ?? ?? 48 85 D2 0F 84 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 33 C9 E8 ?? ?? ?? ?? 0F 28 05 ?? ?? ?? ?? 0F 28 0D ?? ?? ?? ?? 0F 11 05 ?? ?? ?? ?? 0F 28 05 ?? ?? ?? ?? 0F 11 0D ?? ?? ?? ?? 0F 28 0D ?? ?? ?? ?? 0F 11 05 ?? ?? ?? ?? 0F 11 0D ?? ?? ?? ?? 48 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? FF 15 ?? ?? ?? ??"); - - CEngineAPI_Init = p_CEngineAPI_Init.RCast(); - CEngineAPI_Shutdown = p_CEngineAPI_Shutdown.RCast(); - CEngineAPI_Connect = p_CEngineAPI_Connect.RCast(); /*48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 89 15 ?? ?? ?? ??*/ - CEngineAPI_ModInit = p_CEngineAPI_ModInit.RCast(); /*48 89 5C 24 ?? 48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 4D 8B F8*/ - CEngineAPI_MainLoop = p_CEngineAPI_MainLoop.RCast(); /*E8 ?? ?? ?? ?? 48 8B 15 ?? ?? ?? ?? 84 C0 B9 ?? ?? ?? ??*/ - v_CEngineAPI_SetStartupInfo = p_CEngineAPI_SetStartupInfo.RCast(); /*48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 48 8B DA*/ - PakFile_Init = p_PakFile_Init.RCast(); /*44 88 44 24 ?? 53 55 56 57*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F1 48 8D 3D ?? ?? ?? ?? 33 DB 48 8D 15 ?? ?? ?? ??").GetPtr(CEngineAPI__Init); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 89 15").GetPtr(CEngineAPI__Connect); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 33 D2 48 8B 01 FF 90 ?? ?? ?? ?? B1 01").GetPtr(CEngineAPI__Shutdown); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 4D 8B F8").GetPtr(CEngineAPI__ModInit); + g_GameDll.FindPatternSIMD("4C 8B DC 49 89 4B 08 48 81 EC ?? ?? ?? ?? 8B 05 ?? ?? ?? ??").GetPtr(CEngineAPI__MainLoop); + g_GameDll.FindPatternSIMD("44 88 44 24 ?? 53 55 56 57").GetPtr(v_PakFile_Init); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 81 EC ?? ?? ?? ?? 45 33 C9").GetPtr(CEngineAPI__PumpMessages); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? ?? 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 48 8B DA").GetPtr(CEngineAPI__SetStartupInfo); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 15 ?? ?? ?? ?? 48 85 D2 0F 84 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 33 C9 E8 ?? ?? ?? ?? 0F 28 05 ?? ?? ?? ?? 0F 28 0D ?? ?? ?? ?? 0F 11 05 ?? ?? ?? ?? 0F 28 05 ?? ?? ?? ?? 0F 11 0D ?? ?? ?? ?? 0F 28 0D ?? ?? ?? ?? 0F 11 05 ?? ?? ?? ?? 0F 11 0D ?? ?? ?? ?? 48 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? FF 15 ?? ?? ?? ??").GetPtr(v_ResetMTVFTaskItem); } virtual void GetVar(void) const { - g_bTextMode = p_CEngineAPI_SetStartupInfo.FindPattern("80 3D", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); - g_szBaseDir = p_CEngineAPI_SetStartupInfo.FindPattern("48 8D", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_bTextMode = CMemory(CEngineAPI__SetStartupInfo).FindPattern("80 3D", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + g_szBaseDir = CMemory(CEngineAPI__SetStartupInfo).FindPattern("48 8D", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pMTVFTaskItem = p_ResetMTVFTaskItem.FindPattern("48 8B", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_szMTVFItemName = p_ResetMTVFTaskItem.FindPattern("C6 05", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + g_pMTVFTaskItem = CMemory(v_ResetMTVFTaskItem).FindPattern("48 8B", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_szMTVFItemName = CMemory(v_ResetMTVFTaskItem).FindPattern("C6 05", CMemory::Direction::DOWN, 250).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/sys_engine.cpp b/r5dev/engine/sys_engine.cpp index d553a6c4..959e0805 100644 --- a/r5dev/engine/sys_engine.cpp +++ b/r5dev/engine/sys_engine.cpp @@ -7,6 +7,7 @@ /////////////////////////////////////////////////////////////////////////////// CEngine* g_pEngine = nullptr; +IEngine::QuitState_t* gsm_Quitting = nullptr; bool CEngine::_Frame(CEngine* thisp) { @@ -27,95 +28,10 @@ bool CEngine::_Frame(CEngine* thisp) } #endif // DEDICATED - return v_CEngine_Frame(thisp); + return CEngine__Frame(thisp); } -/* -//----------------------------------------------------------------------------- -// Purpose: Start initializing the engine. -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool CEngine::Load(bool dedicated, const char* rootDir) +void VEngine::Detour(const bool bAttach) const { - const static int index = 1; - return CallVFunc(index, this, dedicated, rootDir); + DetourSetup(&CEngine__Frame, &CEngine::_Frame, bAttach); } - -//----------------------------------------------------------------------------- -// Purpose: Start to shutdown the engine. -//----------------------------------------------------------------------------- -void CEngine::Unload(void) -{ - const static int index = 2; - CallVFunc(index, this); -} - -//----------------------------------------------------------------------------- -// Purpose: Set the next dll engine state. -//----------------------------------------------------------------------------- -void CEngine::SetNextState(EngineState_t iNextState) -{ - m_nNextDLLState = iNextState; -} - -//----------------------------------------------------------------------------- -// Purpose: Get the dll engine state. -//----------------------------------------------------------------------------- -EngineState_t CEngine::GetState(void) const -{ - return m_nDLLState; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void CEngine::Frame(void) -{ - const static int index = 5; - CallVFunc(index, this); -} - -//----------------------------------------------------------------------------- -// Purpose: Get engine frame time. -//----------------------------------------------------------------------------- -float CEngine::GetFrameTime(void) const -{ - return m_flFrameTime; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -float CEngine::GetPreviousTime(void) // I'm not sure if this is right, should double check. -{ - const static int index = 7; - return CallVFunc(index, this); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -__m128 __fastcall CEngine::GetCurTime(CEngine *thisPtr) const -{ - return _mm_cvtpd_ps(_mm_cvtepi32_pd(_mm_cvtsi64_si128(thisPtr->m_flCurrentTime))); -} - -//----------------------------------------------------------------------------- -// Purpose: Set dll state. -//----------------------------------------------------------------------------- -void CEngine::SetQuitting(EngineDllQuitting_t quitDllState) -{ - const static int index = 9; - CallVFunc(index, this, quitDllState); -} -*/ - -void VEngine::Attach() const -{ - DetourAttach((LPVOID*)&v_CEngine_Frame, &CEngine::_Frame); -} - -void VEngine::Detach() const -{ - DetourDetach((LPVOID*)&v_CEngine_Frame, &CEngine::_Frame); -} \ No newline at end of file diff --git a/r5dev/engine/sys_engine.h b/r5dev/engine/sys_engine.h index 15b84a48..8d0ee35f 100644 --- a/r5dev/engine/sys_engine.h +++ b/r5dev/engine/sys_engine.h @@ -1,10 +1,19 @@ #pragma once #include +class CEngine; + +/* ==== CENGINE ======================================================================================================================================================= */ +inline bool(*CEngine__Frame)(CEngine* thisp); + +extern CEngine* g_pEngine; +extern IEngine::QuitState_t* gsm_Quitting; + class CEngine : public IEngine { public: - static bool _Frame(CEngine* thisp); + static bool _Frame(CEngine* const thisp); + inline IEngine::QuitState_t GetQuitting() const { return *gsm_Quitting; } private: EngineState_t m_nDLLState; @@ -14,17 +23,12 @@ private: float m_flFrameTime; float m_flPreviousFrameTime; float m_flFilteredTime; - uint8_t gap2C[4]; - int64_t field_30; - char field_38; - char field_39; + char padding[4]; // <- free data + double m_flBenchmarkTime; + bool m_bShouldPause; + bool m_bPaused; }; - -/* ==== CENGINE ======================================================================================================================================================= */ -inline CMemory p_CEngine_Frame; -inline bool(*v_CEngine_Frame)(CEngine* thisp); - -extern CEngine* g_pEngine; +static_assert(sizeof(CEngine) == 0x40); /////////////////////////////////////////////////////////////////////////////// @@ -35,30 +39,20 @@ class VEngine : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CEngine::Frame", p_CEngine_Frame.GetPtr()); - LogVarAdr("g_Engine", reinterpret_cast(g_pEngine)); + LogFunAdr("CEngine::Frame", CEngine__Frame); + LogVarAdr("g_Engine", g_pEngine); + LogVarAdr("sm_Quitting", gsm_Quitting); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CEngine_Frame = g_GameDll.FindPatternSIMD("40 55 53 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 8B F1"); -#elif defined (GAMEDLL_S2) - p_CEngine_Frame = g_GameDll.FindPatternSIMD("48 8B C4 56 48 81 EC ?? ?? ?? ?? 0F 29 70 B8"); -#else - p_CEngine_Frame = g_GameDll.FindPatternSIMD("48 8B C4 55 56 48 8D A8 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 0F 29 70 B8"); -#endif - v_CEngine_Frame = p_CEngine_Frame.RCast(); + g_GameDll.FindPatternSIMD("48 8B C4 55 56 48 8D A8 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 0F 29 70 B8").GetPtr(CEngine__Frame); } virtual void GetVar(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_pEngine = g_GameDll.FindPatternSIMD("48 83 EC 28 80 B9 ?? ?? ?? ?? ?? 48 8B 15 ?? ?? ?? ??").FindPatternSelf("48 8D ?? ?? ?? ?? 01", CMemory::Direction::DOWN, 300).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) g_pEngine = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 80 B9 ?? ?? ?? ?? ?? BB ?? ?? ?? ??").FindPatternSelf("48 8B ?? ?? ?? ?? 01", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#endif + gsm_Quitting = g_GameDll.FindPatternSIMD("89 15 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC 83 C1 F4").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/sys_getmodes.cpp b/r5dev/engine/sys_getmodes.cpp index 37211f3a..f38de0e0 100644 --- a/r5dev/engine/sys_getmodes.cpp +++ b/r5dev/engine/sys_getmodes.cpp @@ -6,6 +6,7 @@ #include "core/stdafx.h" #include "windows/id3dx.h" #include "engine/sys_getmodes.h" +#include "gameui/imgui_system.h" //----------------------------------------------------------------------------- // Purpose: creates the game window, obtains the rect and plays the startup movie. @@ -14,15 +15,12 @@ bool HCVideoMode_Common__CreateGameWindow(int* pnRect) { g_nWindowRect[0] = pnRect[0]; g_nWindowRect[1] = pnRect[1]; - return CVideoMode_Common__CreateGameWindow(pnRect); + + const bool ret = CVideoMode_Common__CreateGameWindow(pnRect); + return ret; } -void HVideoMode_Common::Attach() const +void HVideoMode_Common::Detour(const bool bAttach) const { - DetourAttach(&CVideoMode_Common__CreateGameWindow, &HCVideoMode_Common__CreateGameWindow); -} - -void HVideoMode_Common::Detach() const -{ - DetourDetach(&CVideoMode_Common__CreateGameWindow, &HCVideoMode_Common__CreateGameWindow); + DetourSetup(&CVideoMode_Common__CreateGameWindow, &HCVideoMode_Common__CreateGameWindow, bAttach); } diff --git a/r5dev/engine/sys_getmodes.h b/r5dev/engine/sys_getmodes.h index 6a7cd1d4..cded8167 100644 --- a/r5dev/engine/sys_getmodes.h +++ b/r5dev/engine/sys_getmodes.h @@ -3,10 +3,7 @@ //------------------------------------------------------------------------- // CGAME //------------------------------------------------------------------------- -inline CMemory p_CVideoMode_Common__CreateGameWindow; inline bool(*CVideoMode_Common__CreateGameWindow)(int* pnRect); - -inline CMemory p_CVideoMode_Common__CreateWindowClass; inline HWND(*CVideoMode_Common__CreateWindowClass)(vrect_t* pnRect); /////////////////////////////////////////////////////////////////////////////// @@ -14,24 +11,16 @@ class HVideoMode_Common : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CVideoMode_Common::CreateGameWindow", p_CVideoMode_Common__CreateGameWindow.GetPtr()); - LogFunAdr("CVideoMode_Common::CreateWindowClass", p_CVideoMode_Common__CreateWindowClass.GetPtr()); + LogFunAdr("CVideoMode_Common::CreateGameWindow", CVideoMode_Common__CreateGameWindow); + LogFunAdr("CVideoMode_Common::CreateWindowClass", CVideoMode_Common__CreateWindowClass); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CVideoMode_Common__CreateGameWindow = g_GameDll.FindPatternSIMD("40 56 57 48 83 EC 38 48 8B F9 E8 ?? ?? ?? ??"); - p_CVideoMode_Common__CreateWindowClass = g_GameDll.FindPatternSIMD("40 55 53 57 41 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 4C 8B F1"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CVideoMode_Common__CreateGameWindow = g_GameDll.FindPatternSIMD("40 56 57 48 83 EC 28 48 8B F9 E8 ?? ?? ?? ?? 48 8B F0"); - p_CVideoMode_Common__CreateWindowClass = g_GameDll.FindPatternSIMD("40 55 53 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B F9 FF 15 ?? ?? ?? ??"); -#endif - CVideoMode_Common__CreateGameWindow = p_CVideoMode_Common__CreateGameWindow.RCast(); /*40 56 57 48 83 EC 28 48 8B F9 E8 ?? ?? ?? ?? 48 8B F0*/ - CVideoMode_Common__CreateWindowClass = p_CVideoMode_Common__CreateWindowClass.RCast(); /*40 55 53 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B F9 FF 15 ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("40 56 57 48 83 EC 28 48 8B F9 E8 ?? ?? ?? ?? 48 8B F0").GetPtr(CVideoMode_Common__CreateGameWindow); + g_GameDll.FindPatternSIMD("40 55 53 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B F9 FF 15 ?? ?? ?? ??").GetPtr(CVideoMode_Common__CreateWindowClass); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/sys_mainwind.cpp b/r5dev/engine/sys_mainwind.cpp index bca3375e..ca4dbdc5 100644 --- a/r5dev/engine/sys_mainwind.cpp +++ b/r5dev/engine/sys_mainwind.cpp @@ -9,8 +9,10 @@ #include "windows/input.h" #include "engine/sys_mainwind.h" #include "engine/sys_engine.h" +#include "engine/keys.h" #include "gameui/IConsole.h" #include "gameui/IBrowser.h" +#include "gameui/imgui_system.h" //----------------------------------------------------------------------------- // Purpose: plays the startup video's @@ -19,44 +21,38 @@ void CGame::PlayStartupVideos(void) { if (!CommandLine()->CheckParm("-novid")) { - v_CGame__PlayStartupVideos(); + CGame__PlayStartupVideos(); } } //----------------------------------------------------------------------------- // Purpose: main windows procedure //----------------------------------------------------------------------------- -int CGame::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +LRESULT CGame::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - if (!g_bImGuiInitialized) - return v_CGame__WindowProc(hWnd, uMsg, wParam, lParam); + if (!ImguiSystem()->IsInitialized()) + return CGame__WindowProc(hWnd, uMsg, wParam, lParam); - const IEngine::EngineState_t state = g_pEngine->GetState(); - - if (state == IEngine::DLL_CLOSE || - state == IEngine::DLL_RESTART) - return v_CGame__WindowProc(hWnd, uMsg, wParam, lParam); - - ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam); + ImguiSystem()->MessageHandler(hWnd, uMsg, wParam, lParam); if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) { - if (wParam == g_pImGuiConfig->m_ConsoleConfig.m_nBind0 || - wParam == g_pImGuiConfig->m_ConsoleConfig.m_nBind1) + if (wParam == g_ImGuiConfig.m_ConsoleConfig.m_nBind0 || + wParam == g_ImGuiConfig.m_ConsoleConfig.m_nBind1) { - g_pConsole->m_bActivate ^= true; + g_Console.ToggleActive(); ResetInput(); // Disable input to game when console is drawn. } - if (wParam == g_pImGuiConfig->m_BrowserConfig.m_nBind0 || - wParam == g_pImGuiConfig->m_BrowserConfig.m_nBind1) + if (wParam == g_ImGuiConfig.m_BrowserConfig.m_nBind0 || + wParam == g_ImGuiConfig.m_BrowserConfig.m_nBind1) { - g_pBrowser->m_bActivate ^= true; + g_Browser.ToggleActive(); ResetInput(); // Disable input to game when browser is drawn. } } - if (g_pConsole->m_bActivate || g_pBrowser->m_bActivate) + if (g_Console.IsActivated() || g_Browser.IsActivated()) {////////////////////////////////////////////////////////////////////////////// g_bBlockInput = true; @@ -91,18 +87,72 @@ int CGame::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) g_bBlockInput = false; } - return v_CGame__WindowProc(hWnd, uMsg, wParam, lParam); + return CGame__WindowProc(hWnd, uMsg, wParam, lParam); +} + +//----------------------------------------------------------------------------- +// Purpose: gets the window rect +//----------------------------------------------------------------------------- +void CGame::GetWindowRect(int* const x, int* const y, int* const w, int* const h) const +{ + if (x) + { + *x = m_x; + } + if (y) + { + *y = m_y; + } + if (w) + { + *w = m_width; + } + if (h) + { + *h = m_height; + } +} + +//----------------------------------------------------------------------------- +// Purpose: dispatch key event +//----------------------------------------------------------------------------- +void CGame::DispatchKeyEvent(const uint64_t currentTick, const ButtonCode_t buttonCode) const +{ + // Controller 'hold' keys are delayed longer. + // TODO[ AMOS ]: use ConVar's instead? + const float delay = buttonCode == KEY_XBUTTON_BACK ? 1.0f : 0.2f; + KeyInfo_t& keyInfo = g_pKeyInfo[buttonCode]; + + if (!keyInfo.m_bKeyDown && ((currentTick - keyInfo.m_nEventTick) * 0.001f) >= delay) + { + KeyEvent_t keyEvent; + + keyEvent.m_pCommand = keyInfo.m_pKeyBinding[KeyInfo_t::KEY_HELD_BIND]; + keyEvent.m_nTick = buttonCode; + keyEvent.m_bDown = true; + + v_Key_Event(keyEvent); + keyInfo.m_bKeyDown = true; + } +} + +//----------------------------------------------------------------------------- +// Purpose: dispatch all the queued up messages +//----------------------------------------------------------------------------- +void CGame::DispatchAllStoredGameMessages() const +{ + const uint64_t ticks = Plat_MSTime(); + const short eventCount = *g_nKeyEventCount; + + for (short i = 0; i < eventCount; i++) + { + DispatchKeyEvent(ticks, g_pKeyEventTicks[i]); + } } /////////////////////////////////////////////////////////////////////////////// -void VGame::Attach() const +void VGame::Detour(const bool bAttach) const { - DetourAttach(&v_CGame__PlayStartupVideos, &CGame::PlayStartupVideos); - DetourAttach(&v_CGame__WindowProc, &CGame::WindowProc); + DetourSetup(&CGame__PlayStartupVideos, &CGame::PlayStartupVideos, bAttach); + DetourSetup(&CGame__WindowProc, &CGame::WindowProc, bAttach); } - -void VGame::Detach() const -{ - DetourDetach(&v_CGame__PlayStartupVideos, &CGame::PlayStartupVideos); - DetourDetach(&v_CGame__WindowProc, &CGame::WindowProc); -} \ No newline at end of file diff --git a/r5dev/engine/sys_mainwind.h b/r5dev/engine/sys_mainwind.h index 20a9b3ec..b2b4a160 100644 --- a/r5dev/engine/sys_mainwind.h +++ b/r5dev/engine/sys_mainwind.h @@ -5,60 +5,73 @@ //===========================================================================// #ifndef SYS_MAINWIND_H #define SYS_MAINWIND_H -#include "public/igame.h" +#include "inputsystem/iinputsystem.h" -inline CMemory p_CGame__AttachToWindow; -inline void (*v_CGame__AttachToWindow)(void); - -inline CMemory p_CGame__PlayStartupVideos; -inline void(*v_CGame__PlayStartupVideos)(void); - -inline CMemory p_CGame__WindowProc; -inline int(*v_CGame__WindowProc)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +inline void (*CGame__AttachToWindow)(void); +inline void(*CGame__PlayStartupVideos)(void); +inline LRESULT (*CGame__WindowProc)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); //----------------------------------------------------------------------------- // Purpose: Main game interface, including message pump and window creation //----------------------------------------------------------------------------- -class CGame : public IGame +class CGame { public: static void PlayStartupVideos(void); - static int WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -}; + static LRESULT WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); -inline HWND* g_pGameWindow = nullptr; + + inline HWND GetWindow() const { return m_hWindow; } + void GetWindowRect(int* const x, int* const y, int* const w, int* const h) const; + + inline int GetDesktopWidth() const { return m_iDesktopWidth; } + inline int GetDesktopHeight() const { return m_iDesktopHeight; } + inline int GetDesktopRefreshRate() const { return m_iDesktopRefreshRate; } + inline float GetTVRefreshRate() const // Avoid stutter on TV's running on broadcast frame rates. + { return ((float)m_iDesktopRefreshRate == 59.0f || (float)m_iDesktopRefreshRate == 60.0f) ? 59.939999f : (float)m_iDesktopRefreshRate; } + + void DispatchKeyEvent(const uint64_t currentTick, const ButtonCode_t buttonCode) const; + void DispatchAllStoredGameMessages() const; + +private: + HWND m_hWindow; + HINSTANCE m_hInstance; + WNDPROC m_ChainedWindowProc; + int m_x; + int m_y; + int m_width; + int m_height; + bool m_bPostedFirstAppEvent; + bool m_bPostFirstAppEvent; + bool m_bExternallySuppliedWindow; + int m_iDesktopWidth; + int m_iDesktopHeight; + int m_iDesktopRefreshRate; + void* m_pInputContext_Maybe; +}; static_assert(sizeof(CGame) == 64); + +inline CGame* g_pGame = nullptr; /////////////////////////////////////////////////////////////////////////////// class VGame : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CGame::AttachToWindow", p_CGame__AttachToWindow.GetPtr()); - LogFunAdr("CGame::PlayStartupVideos", p_CGame__PlayStartupVideos.GetPtr()); - LogVarAdr("g_GameWindow", reinterpret_cast(g_pGameWindow)); + LogFunAdr("CGame::AttachToWindow", CGame__AttachToWindow); + LogFunAdr("CGame::PlayStartupVideos", CGame__PlayStartupVideos); + LogVarAdr("g_Game", g_pGame); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CGame__AttachToWindow = g_GameDll.FindPatternSIMD("48 83 EC 38 48 8B 0D ?? ?? ?? ?? 48 85 C9 0F 84 ?? ?? ?? ??"); - p_CGame__PlayStartupVideos = g_GameDll.FindPatternSIMD("48 8B C4 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ??"); - p_CGame__WindowProc = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 41 54 41 56 48 81 EC ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CGame__AttachToWindow = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 0F 84 ?? ?? ?? ?? BA ?? ?? ?? ??"); - p_CGame__PlayStartupVideos = g_GameDll.FindPatternSIMD("48 8B C4 55 48 8D A8 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??"); - p_CGame__WindowProc = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 33 F6"); -#endif - - v_CGame__AttachToWindow = p_CGame__AttachToWindow.RCast(); - v_CGame__PlayStartupVideos = p_CGame__PlayStartupVideos.RCast(); - v_CGame__WindowProc = p_CGame__WindowProc.RCast(); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 0F 84 ?? ?? ?? ?? BA ?? ?? ?? ??").GetPtr(CGame__AttachToWindow); + g_GameDll.FindPatternSIMD("48 8B C4 55 48 8D A8 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??").GetPtr(CGame__PlayStartupVideos); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 33 F6").GetPtr(CGame__WindowProc); } virtual void GetVar(void) const { - g_pGameWindow = p_CGame__AttachToWindow.FindPattern("48 8B 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pGame = CMemory(CGame__AttachToWindow).FindPattern("48 8B 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/sys_utils.cpp b/r5dev/engine/sys_utils.cpp index fc5157a9..89f70815 100644 --- a/r5dev/engine/sys_utils.cpp +++ b/r5dev/engine/sys_utils.cpp @@ -26,7 +26,7 @@ // ... - // Output : void _Error //----------------------------------------------------------------------------- -void _Error(char* fmt, ...) +void _Error(const char* fmt, ...) { char buf[4096]; bool shouldNewline = true; @@ -53,7 +53,7 @@ void _Error(char* fmt, ...) // *error - ... - // Output : void* _Warning //----------------------------------------------------------------------------- -void _Warning(int level, char* fmt, ...) +void _Warning(int level, const char* fmt, ...) { char buf[10000]; bool shouldNewline = true; @@ -98,9 +98,9 @@ void _Con_NPrintf(int pos, const char* fmt, ...) va_end(args); }///////////////////////////// - g_pOverlay->m_nCon_NPrintf_Idx = pos; - snprintf(g_pOverlay->m_szCon_NPrintf_Buf, - sizeof(g_pOverlay->m_szCon_NPrintf_Buf), "%s", buf); + g_TextOverlay.m_nCon_NPrintf_Idx = pos; + snprintf(g_TextOverlay.m_szCon_NPrintf_Buf, + sizeof(g_TextOverlay.m_szCon_NPrintf_Buf), "%s", buf); } #endif // !DEDICATED @@ -114,20 +114,28 @@ int Sys_GetProcessUpTime(char* szBuffer) return v_Sys_GetProcessUpTime(szBuffer); } -void VSys_Utils::Attach() const +//----------------------------------------------------------------------------- +// Purpose: Gets the build string of the game (defined in build.txt), if the file +// is absent, the changelist # will be returned instead +//----------------------------------------------------------------------------- +const char* Sys_GetBuildString(void) { - DetourAttach((LPVOID*)&v_Error, &_Error); - DetourAttach((LPVOID*)&v_Warning, &_Warning); -#ifndef DEDICATED - DetourAttach((LPVOID*)&v_Con_NPrintf, &_Con_NPrintf); -#endif // !DEDICATED + return v_Sys_GetBuildString(); } -void VSys_Utils::Detach() const +//----------------------------------------------------------------------------- +// Purpose: Gets the platform string +//----------------------------------------------------------------------------- +const char* Sys_GetPlatformString(void) { - DetourDetach((LPVOID*)&v_Error, &_Error); - DetourDetach((LPVOID*)&v_Warning, &_Warning); + return "PC"; +} + +void VSys_Utils::Detour(const bool bAttach) const +{ + DetourSetup(&v_Error, &_Error, bAttach); + DetourSetup(&v_Warning, &_Warning, bAttach); #ifndef DEDICATED - DetourDetach((LPVOID*)&v_Con_NPrintf, &_Con_NPrintf); + DetourSetup(&v_Con_NPrintf, &_Con_NPrintf, bAttach); #endif // !DEDICATED } diff --git a/r5dev/engine/sys_utils.h b/r5dev/engine/sys_utils.h index 9ad52cda..cf0feefd 100644 --- a/r5dev/engine/sys_utils.h +++ b/r5dev/engine/sys_utils.h @@ -1,53 +1,45 @@ #pragma once //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -inline CMemory p_Error; inline void(*v_Error)(const char* fmt, ...); - -inline CMemory p_Warning; -inline void(*v_Warning)(int, const char* fmt, ...); - -inline CMemory p_Sys_GetProcessUpTime; +inline void(*v_Warning)(int, const char* fmt, ...); inline int(*v_Sys_GetProcessUpTime)(char* szBuffer); +inline const char* (*v_Sys_GetBuildString)(void); #ifndef DEDICATED -inline CMemory p_Con_NPrintf; inline void(*v_Con_NPrintf)(int pos, const char* fmt, ...); #endif // !DEDICATED /* ==== ------- ========================================================================================================================================================= */ /////////////////////////////////////////////////////////////////////////////// int Sys_GetProcessUpTime(char* szBuffer); +const char* Sys_GetBuildString(void); +const char* Sys_GetPlatformString(void); /////////////////////////////////////////////////////////////////////////////// class VSys_Utils : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Error", p_Error.GetPtr()); - LogFunAdr("Warning", p_Warning.GetPtr()); - LogFunAdr("Sys_GetProcessUpTime", p_Sys_GetProcessUpTime.GetPtr()); + LogFunAdr("Error", v_Error); + LogFunAdr("Warning", v_Warning); + LogFunAdr("Sys_GetProcessUpTime", v_Sys_GetProcessUpTime); + LogFunAdr("Sys_GetProcessUpTime", v_Sys_GetBuildString); #ifndef DEDICATED - LogFunAdr("Con_NPrintf", p_Con_NPrintf.GetPtr()); + LogFunAdr("Con_NPrintf", v_Con_NPrintf); #endif // !DEDICATED } virtual void GetFun(void) const { - p_Error = g_GameDll.FindPatternSIMD("48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 55 41 54 41 56 B8 58 10 ?? ?? E8"); - p_Warning = g_GameDll.FindPatternSIMD("48 89 54 24 ?? 4C 89 44 24 ?? 4C 89 4C 24 ?? 48 83 EC 28 4C 8D 44 24 ?? E8 ?? ?? ?? ?? 48 83 C4 28 C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 8B 05 ?? ?? ?? ??"); - p_Sys_GetProcessUpTime = g_GameDll.FindPatternSIMD("40 57 48 83 EC 30 48 8B F9 8B 0D ?? ?? ?? ??"); + g_GameDll.FindPatternSIMD("48 89 4C 24 08 48 89 54 24 10 4C 89 44 24 18 4C 89 4C 24 20 53 55 41 54 41 56 B8 58 10 ?? ?? E8").GetPtr(v_Error); + g_GameDll.FindPatternSIMD("48 89 54 24 ?? 4C 89 44 24 ?? 4C 89 4C 24 ?? 48 83 EC 28 4C 8D 44 24 ?? E8 ?? ?? ?? ?? 48 83 C4 28 C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 8B 05 ?? ?? ?? ??").GetPtr(v_Warning); + g_GameDll.FindPatternSIMD("40 57 48 83 EC 30 48 8B F9 8B 0D ?? ?? ?? ??").GetPtr(v_Sys_GetProcessUpTime); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B E8").FollowNearCallSelf().GetPtr(v_Sys_GetBuildString); #ifndef DEDICATED - p_Con_NPrintf = g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 48 89 54 24 ?? 4C 89 44 24 ?? 4C 89 4C 24 ?? C3"); -#endif // !DEDICATED - v_Error = p_Error.RCast(); - v_Warning = p_Warning.RCast(); - v_Sys_GetProcessUpTime = p_Sys_GetProcessUpTime.RCast(); -#ifndef DEDICATED - v_Con_NPrintf = p_Con_NPrintf.RCast(); + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 48 89 54 24 ?? 4C 89 44 24 ?? 4C 89 4C 24 ?? C3").GetPtr(v_Con_NPrintf); #endif // !DEDICATED } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/engine/traceinit.h b/r5dev/engine/traceinit.h index aeee65f9..9e4df1fb 100644 --- a/r5dev/engine/traceinit.h +++ b/r5dev/engine/traceinit.h @@ -1,7 +1,6 @@ #ifndef TRACEINIT_H #define TRACEINIT_H -inline CMemory p_TRACEINIT; inline void(*v_TRACEINIT)(void* undef, const char* initfunc, const char* shutdownfunc); /////////////////////////////////////////////////////////////////////////////// @@ -9,17 +8,15 @@ class VTraceInit : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("TRACEINIT", p_TRACEINIT.GetPtr()); + LogFunAdr("TRACEINIT", v_TRACEINIT); } virtual void GetFun(void) const { - p_TRACEINIT = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 05 ?? ?? ?? ?? 49 8B F8 48 8B F2 48 85 C0"); - v_TRACEINIT = p_TRACEINIT.RCast(); /*48 89 5C 24 ? 48 89 74 24 ? 57 48 83 EC 20 48 8B 05 ? ? ? ? 49 8B F8 48 8B F2 48 85 C0*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 05 ?? ?? ?? ?? 49 8B F8 48 8B F2 48 85 C0").GetPtr(v_TRACEINIT); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/filesystem/CMakeLists.txt b/r5dev/filesystem/CMakeLists.txt index a226c117..2c131e6d 100644 --- a/r5dev/filesystem/CMakeLists.txt +++ b/r5dev/filesystem/CMakeLists.txt @@ -18,3 +18,20 @@ add_sources( SOURCE_GROUP "Public" end_sources() target_include_directories( ${PROJECT_NAME} PRIVATE "${ENGINE_SOURCE_DIR}/tier0/" "${ENGINE_SOURCE_DIR}/tier1/" ) + +add_module( "lib" "filesystem_std" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "Private" + "filesystem_std.cpp" + "filesystem_std.h" +) + +add_sources( SOURCE_GROUP "Public" + "${ENGINE_SOURCE_DIR}/public/ifilesystem.h" +) + +end_sources() + +target_include_directories( ${PROJECT_NAME} PRIVATE "${ENGINE_SOURCE_DIR}/tier0/" "${ENGINE_SOURCE_DIR}/tier1/" ) diff --git a/r5dev/filesystem/basefilesystem.cpp b/r5dev/filesystem/basefilesystem.cpp index d6a21fb6..412f112b 100644 --- a/r5dev/filesystem/basefilesystem.cpp +++ b/r5dev/filesystem/basefilesystem.cpp @@ -6,6 +6,8 @@ #include "bspfile.h" #include "engine/modelloader.h" +static ConVar fs_showWarnings("fs_showWarnings", "0", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "Logs the FileSystem warnings to the console, filtered by 'fs_warning_level' ( !slower! ).", true, 0.f, true, 2.f, "0 = log to file. 1 = 0 + log to console. 2 = 1 + log to notify"); + //--------------------------------------------------------------------------------- // Purpose: prints the output of the filesystem based on the warning level // Input : *this - @@ -25,7 +27,7 @@ void CBaseFileSystem::Warning(CBaseFileSystem* pFileSystem, FileWarningLevel_t l va_list args; va_start(args, pFmt); - CoreMsgV(LogType_t::LOG_WARNING, static_cast(fs_showWarnings->GetInt()), eDLL_T::FS, "filesystem", pFmt, args); + CoreMsgV(LogType_t::LOG_WARNING, static_cast(fs_showWarnings.GetInt()), eDLL_T::FS, "filesystem", pFmt, args); va_end(args); } @@ -69,7 +71,7 @@ bool CBaseFileSystem::VCheckDisk(const char* pszFilePath) // *pszFilePath - // Output : handle to file on success, NULL on failure //--------------------------------------------------------------------------------- -FileHandle_t CBaseFileSystem::VReadFromVPK(CBaseFileSystem* pFileSystem, FileHandle_t pResults, char* pszFilePath) +FileHandle_t CBaseFileSystem::VReadFromVPK(CBaseFileSystem* pFileSystem, FileHandle_t pResults, const char* pszFilePath) { if (VCheckDisk(pszFilePath)) { @@ -77,7 +79,7 @@ FileHandle_t CBaseFileSystem::VReadFromVPK(CBaseFileSystem* pFileSystem, FileHan return pResults; } - return v_CBaseFileSystem_LoadFromVPK(pFileSystem, pResults, pszFilePath); + return CBaseFileSystem__LoadFromVPK(pFileSystem, pResults, pszFilePath); } //--------------------------------------------------------------------------------- @@ -87,14 +89,14 @@ FileHandle_t CBaseFileSystem::VReadFromVPK(CBaseFileSystem* pFileSystem, FileHan // *pCache - // Output : true if file exists, false otherwise //--------------------------------------------------------------------------------- -bool CBaseFileSystem::VReadFromCache(CBaseFileSystem* pFileSystem, char* pszFilePath, FileSystemCache* pCache) +bool CBaseFileSystem::VReadFromCache(CBaseFileSystem* pFileSystem, const char* pszFilePath, FileSystemCache* pCache) { if (VCheckDisk(pszFilePath)) { return false; } - bool result = v_CBaseFileSystem_LoadFromCache(pFileSystem, pszFilePath, pCache); + bool result = CBaseFileSystem__LoadFromCache(pFileSystem, pszFilePath, pCache); return result; } @@ -131,7 +133,7 @@ void CBaseFileSystem::VAddMapPackFile(CBaseFileSystem* pFileSystem, const char* pPath = lumpPathBuf; } - v_CBaseFileSystem_AddMapPackFile(pFileSystem, pPath, pPathID, addType); + CBaseFileSystem__AddMapPackFile(pFileSystem, pPath, pPathID, addType); } //--------------------------------------------------------------------------------- @@ -142,14 +144,14 @@ void CBaseFileSystem::VAddMapPackFile(CBaseFileSystem* pFileSystem, const char* //--------------------------------------------------------------------------------- VPKData_t* CBaseFileSystem::VMountVPKFile(CBaseFileSystem* pFileSystem, const char* pszVpkPath) { - int nHandle = v_CBaseFileSystem_GetMountedVPKHandle(pFileSystem, pszVpkPath); - VPKData_t* pPakData = v_CBaseFileSystem_MountVPKFile(pFileSystem, pszVpkPath); + int nHandle = CBaseFileSystem__GetMountedVPKHandle(pFileSystem, pszVpkPath); + VPKData_t* pPakData = CBaseFileSystem__MountVPKFile(pFileSystem, pszVpkPath); if (pPakData) { if (nHandle < 0) // Only log if VPK hasn't been mounted yet. { - ::DevMsg(eDLL_T::FS, "Mounted vpk file: '%s' with handle: '%i'\n", pszVpkPath, pPakData->m_nHandle); + ::Msg(eDLL_T::FS, "Mounted vpk file: '%s' with handle: '%i'\n", pszVpkPath, pPakData->m_nHandle); } } else // VPK failed to load or does not exist... @@ -168,12 +170,12 @@ VPKData_t* CBaseFileSystem::VMountVPKFile(CBaseFileSystem* pFileSystem, const ch //--------------------------------------------------------------------------------- const char* CBaseFileSystem::VUnmountVPKFile(CBaseFileSystem* pFileSystem, const char* pszVpkPath) { - int nHandle = v_CBaseFileSystem_GetMountedVPKHandle(pFileSystem, pszVpkPath); - const char* pRet = v_CBaseFileSystem_UnmountVPKFile(pFileSystem, pszVpkPath); + int nHandle = CBaseFileSystem__GetMountedVPKHandle(pFileSystem, pszVpkPath); + const char* pRet = CBaseFileSystem__UnmountVPKFile(pFileSystem, pszVpkPath); if (nHandle >= 0) { - ::DevMsg(eDLL_T::FS, "Unmounted vpk file: '%s' with handle: '%i'\n", pszVpkPath, nHandle); + ::Msg(eDLL_T::FS, "Unmounted vpk file: '%s' with handle: '%i'\n", pszVpkPath, nHandle); } return pRet; @@ -201,23 +203,14 @@ CUtlString CBaseFileSystem::ReadString(FileHandle_t pFile) return result; } -void VBaseFileSystem::Attach() const +void VBaseFileSystem::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_CBaseFileSystem_Warning, &CBaseFileSystem::Warning); - DetourAttach((LPVOID*)&v_CBaseFileSystem_LoadFromVPK, &CBaseFileSystem::VReadFromVPK); - DetourAttach((LPVOID*)&v_CBaseFileSystem_LoadFromCache, &CBaseFileSystem::VReadFromCache); - DetourAttach((LPVOID*)&v_CBaseFileSystem_AddMapPackFile, &CBaseFileSystem::VAddMapPackFile); - DetourAttach((LPVOID*)&v_CBaseFileSystem_MountVPKFile, &CBaseFileSystem::VMountVPKFile); - DetourAttach((LPVOID*)&v_CBaseFileSystem_UnmountVPKFile, &CBaseFileSystem::VUnmountVPKFile); + DetourSetup(&CBaseFileSystem__Warning, &CBaseFileSystem::Warning, bAttach); + DetourSetup(&CBaseFileSystem__LoadFromVPK, &CBaseFileSystem::VReadFromVPK, bAttach); + DetourSetup(&CBaseFileSystem__LoadFromCache, &CBaseFileSystem::VReadFromCache, bAttach); + DetourSetup(&CBaseFileSystem__AddMapPackFile, &CBaseFileSystem::VAddMapPackFile, bAttach); + DetourSetup(&CBaseFileSystem__MountVPKFile, &CBaseFileSystem::VMountVPKFile, bAttach); + DetourSetup(&CBaseFileSystem__UnmountVPKFile, &CBaseFileSystem::VUnmountVPKFile, bAttach); } -void VBaseFileSystem::Detach() const -{ - DetourDetach((LPVOID*)&v_CBaseFileSystem_Warning, &CBaseFileSystem::Warning); - DetourDetach((LPVOID*)&v_CBaseFileSystem_LoadFromVPK, &CBaseFileSystem::VReadFromVPK); - DetourDetach((LPVOID*)&v_CBaseFileSystem_LoadFromCache, &CBaseFileSystem::VReadFromCache); - DetourDetach((LPVOID*)&v_CBaseFileSystem_AddMapPackFile, &CBaseFileSystem::VAddMapPackFile); - DetourDetach((LPVOID*)&v_CBaseFileSystem_MountVPKFile, &CBaseFileSystem::VMountVPKFile); - DetourDetach((LPVOID*)&v_CBaseFileSystem_UnmountVPKFile, &CBaseFileSystem::VUnmountVPKFile); -} CBaseFileSystem* g_pFileSystem = nullptr; \ No newline at end of file diff --git a/r5dev/filesystem/basefilesystem.h b/r5dev/filesystem/basefilesystem.h index b7397ca9..6563315c 100644 --- a/r5dev/filesystem/basefilesystem.h +++ b/r5dev/filesystem/basefilesystem.h @@ -1,7 +1,7 @@ #pragma once #include "public/ifilesystem.h" -class CBaseFileSystem : public IFileSystem +class CBaseFileSystem : public CTier1AppSystem { public: //-------------------------------------------------------- @@ -9,8 +9,8 @@ public: //-------------------------------------------------------- static void Warning(CBaseFileSystem* pFileSystem, FileWarningLevel_t level, const char* fmt, ...); static bool VCheckDisk(const char* pszFilePath); - static FileHandle_t VReadFromVPK(CBaseFileSystem* pFileSystem, FileHandle_t pResults, char* pszFilePath); - static bool VReadFromCache(CBaseFileSystem* pFileSystem, char* pszFilePath, FileSystemCache* pCache); + static FileHandle_t VReadFromVPK(CBaseFileSystem* pFileSystem, FileHandle_t pResults, const char* pszFilePath); + static bool VReadFromCache(CBaseFileSystem* pFileSystem, const char* pszFilePath, FileSystemCache* pCache); static void VAddMapPackFile(CBaseFileSystem* pFileSystem, const char* pPath, const char* pPathID, SearchPathAdd_t addType); static VPKData_t* VMountVPKFile(CBaseFileSystem* pFileSystem, const char* pszVpkPath); static const char* VUnmountVPKFile(CBaseFileSystem* pFileSystem, const char* pszVpkPath); @@ -43,26 +43,13 @@ protected: }; /* ==== CBASEFILESYSTEM ================================================================================================================================================= */ -inline CMemory p_CBaseFileSystem_Warning; -inline void(*v_CBaseFileSystem_Warning)(CBaseFileSystem* pFileSystem, FileWarningLevel_t level, const char* fmt, ...); - -inline CMemory p_CBaseFileSystem_LoadFromVPK; -inline FileHandle_t(*v_CBaseFileSystem_LoadFromVPK)(CBaseFileSystem* pFileSystem, FileHandle_t pResults, const char* pszAssetName); - -inline CMemory p_CBaseFileSystem_LoadFromCache; -inline bool(*v_CBaseFileSystem_LoadFromCache)(CBaseFileSystem* pFileSystem, const char* pszAssetName, FileSystemCache* pCache); - -inline CMemory p_CBaseFileSystem_AddMapPackFile; -inline void(*v_CBaseFileSystem_AddMapPackFile)(CBaseFileSystem* pFileSystem, const char* pPath, const char* pPathID, SearchPathAdd_t addType); - -inline CMemory p_CBaseFileSystem_MountVPKFile; -inline VPKData_t*(*v_CBaseFileSystem_MountVPKFile)(CBaseFileSystem* pFileSystem, const char* pszVpkPath); - -inline CMemory p_CBaseFileSystem_UnmountVPKFile; -inline const char* (*v_CBaseFileSystem_UnmountVPKFile)(CBaseFileSystem* pFileSystem, const char* pszVpkPath); - -inline CMemory p_CBaseFileSystem_GetMountedVPKHandle; -inline int(*v_CBaseFileSystem_GetMountedVPKHandle)(CBaseFileSystem* pFileSystem, const char* pszVpkPath); +inline void(*CBaseFileSystem__Warning)(CBaseFileSystem* pFileSystem, FileWarningLevel_t level, const char* fmt, ...); +inline FileHandle_t(*CBaseFileSystem__LoadFromVPK)(CBaseFileSystem* pFileSystem, FileHandle_t pResults, const char* pszAssetName); +inline bool(*CBaseFileSystem__LoadFromCache)(CBaseFileSystem* pFileSystem, const char* pszAssetName, FileSystemCache* pCache); +inline void(*CBaseFileSystem__AddMapPackFile)(CBaseFileSystem* pFileSystem, const char* pPath, const char* pPathID, SearchPathAdd_t addType); +inline VPKData_t*(*CBaseFileSystem__MountVPKFile)(CBaseFileSystem* pFileSystem, const char* pszVpkPath); +inline const char* (*CBaseFileSystem__UnmountVPKFile)(CBaseFileSystem* pFileSystem, const char* pszVpkPath); +inline int(*CBaseFileSystem__GetMountedVPKHandle)(CBaseFileSystem* pFileSystem, const char* pszVpkPath); extern CBaseFileSystem* g_pFileSystem; @@ -71,32 +58,24 @@ class VBaseFileSystem : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CBaseFileSystem::Warning", p_CBaseFileSystem_Warning.GetPtr()); - LogFunAdr("CBaseFileSystem::LoadFromVPK", p_CBaseFileSystem_LoadFromVPK.GetPtr()); - LogFunAdr("CBaseFileSystem::LoadFromCache", p_CBaseFileSystem_LoadFromCache.GetPtr()); - LogFunAdr("CBaseFileSystem::AddMapPackFile", p_CBaseFileSystem_AddMapPackFile.GetPtr()); - LogFunAdr("CBaseFileSystem::MountVPKFile", p_CBaseFileSystem_MountVPKFile.GetPtr()); - LogFunAdr("CBaseFileSystem::UnmountVPKFile", p_CBaseFileSystem_UnmountVPKFile.GetPtr()); - LogFunAdr("CBaseFileSystem::GetMountedVPKHandle", p_CBaseFileSystem_GetMountedVPKHandle.GetPtr()); - LogVarAdr("g_pFileSystem", reinterpret_cast(g_pFileSystem)); + LogFunAdr("CBaseFileSystem::Warning", CBaseFileSystem__Warning); + LogFunAdr("CBaseFileSystem::LoadFromVPK", CBaseFileSystem__LoadFromVPK); + LogFunAdr("CBaseFileSystem::LoadFromCache", CBaseFileSystem__LoadFromCache); + LogFunAdr("CBaseFileSystem::AddMapPackFile", CBaseFileSystem__AddMapPackFile); + LogFunAdr("CBaseFileSystem::MountVPKFile", CBaseFileSystem__MountVPKFile); + LogFunAdr("CBaseFileSystem::UnmountVPKFile", CBaseFileSystem__UnmountVPKFile); + LogFunAdr("CBaseFileSystem::GetMountedVPKHandle", CBaseFileSystem__GetMountedVPKHandle); + LogVarAdr("g_pFileSystem", g_pFileSystem); } virtual void GetFun(void) const { - p_CBaseFileSystem_Warning = g_GameDll.FindPatternSIMD("4C 89 4C 24 20 C3 CC CC CC CC CC CC CC CC CC CC 48"); - p_CBaseFileSystem_LoadFromVPK = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 49 8B C0 4C 8D 8C 24 ?? ?? ?? ??"); - p_CBaseFileSystem_LoadFromCache = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 49 8B D8"); - p_CBaseFileSystem_AddMapPackFile = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 54 24 ?? 55 ?? 41 54 41 55 48 8D AC 24 ?? ?? ?? ??"); - p_CBaseFileSystem_MountVPKFile = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??"); - p_CBaseFileSystem_UnmountVPKFile = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 48 8B CB 48 8D 15 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0"); - p_CBaseFileSystem_GetMountedVPKHandle = g_GameDll.FindPatternSIMD("48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??"); - - v_CBaseFileSystem_Warning = p_CBaseFileSystem_Warning.RCast(); /*4C 89 4C 24 20 C3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48*/ - v_CBaseFileSystem_LoadFromVPK = p_CBaseFileSystem_LoadFromVPK.RCast(); /*48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 49 8B C0 4C 8D 8C 24 ?? ?? ?? ??*/ - v_CBaseFileSystem_LoadFromCache = p_CBaseFileSystem_LoadFromCache.RCast(); /*40 53 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 49 8B D8*/ - v_CBaseFileSystem_AddMapPackFile = p_CBaseFileSystem_AddMapPackFile.RCast(); - v_CBaseFileSystem_MountVPKFile = p_CBaseFileSystem_MountVPKFile.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??*/ - v_CBaseFileSystem_UnmountVPKFile = p_CBaseFileSystem_UnmountVPKFile.RCast(); /*48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 48 8B CB 48 8D 15 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0*/ - v_CBaseFileSystem_GetMountedVPKHandle = p_CBaseFileSystem_GetMountedVPKHandle.RCast(); /*48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("4C 89 4C 24 20 C3 CC CC CC CC CC CC CC CC CC CC 48").GetPtr(CBaseFileSystem__Warning); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 49 8B C0 4C 8D 8C 24 ?? ?? ?? ??").GetPtr(CBaseFileSystem__LoadFromVPK); + g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 49 8B D8").GetPtr(CBaseFileSystem__LoadFromCache); + g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 54 24 ?? 55 ?? 41 54 41 55 48 8D AC 24 ?? ?? ?? ??").GetPtr(CBaseFileSystem__AddMapPackFile); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??").GetPtr(CBaseFileSystem__MountVPKFile); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 48 8B CB 48 8D 15 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0").GetPtr(CBaseFileSystem__UnmountVPKFile); + g_GameDll.FindPatternSIMD("48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B F9 4C 8D 05 ?? ?? ?? ??").GetPtr(CBaseFileSystem__GetMountedVPKHandle); } virtual void GetVar(void) const { @@ -104,7 +83,6 @@ class VBaseFileSystem : public IDetour .FindPattern("48 89", CMemory::Direction::DOWN, 512, 2).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/filesystem/filesystem.cpp b/r5dev/filesystem/filesystem.cpp index 43e85eb8..b48a491f 100644 --- a/r5dev/filesystem/filesystem.cpp +++ b/r5dev/filesystem/filesystem.cpp @@ -4,4 +4,9 @@ /////////////////////////////////////////////////////////////////////////////// CFileSystem_Stdio** g_pFullFileSystem = nullptr; -CFileSystem_Stdio* g_pFileSystem_Stdio = nullptr; \ No newline at end of file +CFileSystem_Stdio* g_pFileSystem_Stdio = nullptr; + +CFileSystem_Stdio* FileSystem() +{ + return (*g_pFullFileSystem); +} diff --git a/r5dev/filesystem/filesystem.h b/r5dev/filesystem/filesystem.h index 79a245ee..db78171d 100644 --- a/r5dev/filesystem/filesystem.h +++ b/r5dev/filesystem/filesystem.h @@ -21,7 +21,7 @@ protected: virtual size_t FS_vfprintf(FILE* fp, const char* fmt, va_list list) = 0; virtual int FS_ferror(FILE* fp) = 0; virtual int FS_fflush(FILE* fp) = 0; - virtual char* FS_fgets(char* dest, int destSize, FILE* fp) = 0; + virtual char* FS_fgets(char* dest, unsigned int destSize) = 0; virtual int FS_stat(const char* path, struct _stat* buf, bool* pbLoadedFromSteamCache = NULL) = 0; virtual int FS_chmod(const char* path, int pmode) = 0; virtual HANDLE FS_FindFirstFile(const char* findname, WIN32_FIND_DATA* dat) = 0; @@ -36,18 +36,15 @@ extern CFileSystem_Stdio* g_pFileSystem_Stdio; //----------------------------------------------------------------------------- // Singleton FileSystem //----------------------------------------------------------------------------- -inline CFileSystem_Stdio* FileSystem() -{ - return (*g_pFullFileSystem); -} +extern CFileSystem_Stdio* FileSystem(); /////////////////////////////////////////////////////////////////////////////// class VFileSystem_Stdio : public IDetour { virtual void GetAdr(void) const { - LogVarAdr("g_pFullFileSystem", reinterpret_cast(g_pFullFileSystem)); - LogVarAdr("g_pFileSystem_Stdio", reinterpret_cast(g_pFileSystem_Stdio)); + LogVarAdr("g_pFullFileSystem", g_pFullFileSystem); + LogVarAdr("g_pFileSystem_Stdio", g_pFileSystem_Stdio); } virtual void GetFun(void) const { } virtual void GetVar(void) const @@ -58,8 +55,7 @@ class VFileSystem_Stdio : public IDetour .FindPattern("48 89", CMemory::Direction::DOWN, 512, 1).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/filesystem/filesystem_std.cpp b/r5dev/filesystem/filesystem_std.cpp new file mode 100644 index 00000000..8fad1707 --- /dev/null +++ b/r5dev/filesystem/filesystem_std.cpp @@ -0,0 +1,336 @@ +//=============================================================================// +// +// Purpose: FileSystem class utilizing standard libraries. +// ---------------------------------------------------------------------------- +// NOTE: use this for standalone/tools projects +//=============================================================================// +#include "tier0/utility.h" +#include "filesystem_std.h" + +ssize_t CBaseFileSystem::Read(void* pOutput, ssize_t size, FileHandle_t file) +{ + return fread(pOutput, sizeof(uint8_t), size, (FILE*)file); +} + +ssize_t CBaseFileSystem::Write(void const* pInput, ssize_t size, FileHandle_t file) +{ + return fwrite(pInput, sizeof(uint8_t), size, (FILE*)file); +} + +FileHandle_t CBaseFileSystem::Open(const char* pFileName, const char* pOptions, const char* pPathID, int64_t unknown) +{ + NOTE_UNUSED(unknown); + + char fullPath[1024]; + snprintf(fullPath, sizeof(fullPath), "%s", pFileName); + + V_FixSlashes(fullPath); + + return (FileHandle_t)fopen(fullPath, pOptions); +} + +void CBaseFileSystem::Close(FileHandle_t file) +{ + fclose((FILE*)file); +} + +void CBaseFileSystem::Seek(FileHandle_t file, ssize_t pos, FileSystemSeek_t seekType) +{ + fseek((FILE*)file, (long)pos, seekType); +} + +ptrdiff_t CBaseFileSystem::Tell(FileHandle_t file) +{ + return ftell((FILE*)file); +} + +ssize_t CBaseFileSystem::FSize(const char* pFileName, const char* pPathID) +{ + char fullPath[1024]; + snprintf(fullPath, sizeof(fullPath), "%s", pFileName); + + V_FixSlashes(fullPath); + + FILE* fp = fopen(fullPath, "rb"); + if (!fp) + return 0; + + fseek(fp, 0, SEEK_END); + ptrdiff_t size = Tell(fp); + + fclose(fp); + return size; +} + +ssize_t CBaseFileSystem::Size(FileHandle_t file) +{ + fseek((FILE*)file, 0, SEEK_END); + ptrdiff_t size = Tell((FILE*)file); + + fseek((FILE*)file, 0, SEEK_SET); + return size; +} + +void CBaseFileSystem::Flush(FileHandle_t file) +{ + fflush((FILE*)file); +} + +bool CBaseFileSystem::Precache(const char* pFileName, const char* pPathID) +{ + NOTE_UNUSED(pPathID); + NOTE_UNUSED(pFileName); + return true; +} + +bool CBaseFileSystem::FileExists(const char* pFileName, const char* pPathID) +{ + char fullPath[1024]; + snprintf(fullPath, sizeof(fullPath), "%s", pFileName); + + V_FixSlashes(fullPath); + + FILE* fp = fopen(fullPath, "rb"); + if (!fp) + return false; + + fclose(fp); + return true; +} + +bool CBaseFileSystem::IsFileWritable(char const* pFileName, const char* pPathID) +{ + char fullPath[1024]; + snprintf(fullPath, sizeof(fullPath), "%s", pFileName); + + V_FixSlashes(fullPath); + + FILE* fp = fopen(fullPath, "a"); + if (!fp) + return false; + + fclose(fp); + return true; +} + +bool CBaseFileSystem::SetFileWritable(char const* pFileName, bool writable, const char* pPathID) +{ + NOTE_UNUSED(pPathID); + NOTE_UNUSED(writable); + NOTE_UNUSED(pFileName); + return true; +} + +long long CBaseFileSystem::GetFileTime(const char* pFileName, const char* pPathID) +{ + char fullPath[1024]; + snprintf(fullPath, sizeof(fullPath), "%s", pFileName); + + V_FixSlashes(fullPath); + + struct stat result; + + // On POSIX systems, st_mtime is the time of last + // modification in seconds since the epoch. + if (stat(fullPath, &result) == NULL) + return (long)result.st_mtime; + else + return -1; +} + +bool CBaseFileSystem::GetOptimalIOConstraints(FileHandle_t hFile, uint64_t* pOffsetAlign, uint64_t* pSizeAlign, uint64_t* pBufferAlign) +{ + if (pOffsetAlign) + *pOffsetAlign = 1; + if (pSizeAlign) + *pSizeAlign = 1; + if (pBufferAlign) + *pBufferAlign = 1; + return false; +} + +bool CBaseFileSystem::ReadToBuffer(FileHandle_t hFile, CUtlBuffer& buf, ssize_t nMaxBytes, FSAllocFunc_t pfnAlloc) +{ + ssize_t nBytesToRead = Size(hFile); + if (nBytesToRead == 0) + { + // no data in file + return true; + } + + if (nMaxBytes > 0) + { + // can't read more than file has + nBytesToRead = MIN(nMaxBytes, nBytesToRead); + } + + ssize_t nBytesRead = 0; + ptrdiff_t nBytesOffset = 0; + + ptrdiff_t iStartPos = Tell(hFile); + + if (nBytesToRead != 0) + { + ssize_t nBytesDestBuffer = nBytesToRead; + uint64_t nSizeAlign = 0, nBufferAlign = 0, nOffsetAlign = 0; + + bool bBinary = !(buf.IsText() && !buf.ContainsCRLF()); + + if (bBinary && !IsPosix() && !buf.IsExternallyAllocated() && !pfnAlloc && + (buf.TellPut() == 0) && (buf.TellGet() == 0) && (iStartPos % 4 == 0) && + GetOptimalIOConstraints(hFile, &nOffsetAlign, &nSizeAlign, &nBufferAlign)) + { + // correct conditions to allow an optimal read + if (iStartPos % nOffsetAlign != 0) + { + // move starting position back to nearest alignment + nBytesOffset = (iStartPos % nOffsetAlign); + Assert((iStartPos - nBytesOffset) % nOffsetAlign == 0); + Seek(hFile, -nBytesOffset, FILESYSTEM_SEEK_CURRENT); + + // going to read from aligned start, increase target buffer size by offset alignment + nBytesDestBuffer += nBytesOffset; + } + + // snap target buffer size to its size alignment + // add additional alignment slop for target pointer adjustment + nBytesDestBuffer = AlignValue(nBytesDestBuffer, nSizeAlign) + nBufferAlign; + } + + AssertMsg(!pfnAlloc, "Custom allocators not yet supported!"); + + //if (!pfnAlloc) + { + buf.EnsureCapacity(nBytesDestBuffer + buf.TellPut()); + } + //else + //{ + // // caller provided allocator + // void* pMemory = (*pfnAlloc)(g_pszReadFilename, nBytesDestBuffer); + // buf.SetExternalBuffer(pMemory, nBytesDestBuffer, 0, buf.GetFlags() & ~CUtlBuffer::EXTERNAL_GROWABLE); + //} + + ssize_t seekGet = -1; + if (nBytesDestBuffer != nBytesToRead) + { + // doing optimal read, align target pointer + ssize_t nAlignedBase = AlignValue((byte*)buf.Base(), nBufferAlign) - (byte*)buf.Base(); + buf.SeekPut(CUtlBuffer::SEEK_HEAD, nAlignedBase); + + // the buffer read position is slid forward to ignore the addtional + // starting offset alignment + seekGet = nAlignedBase + nBytesOffset; + } + + nBytesRead = ReadEx(buf.PeekPut(), nBytesDestBuffer - nBufferAlign, nBytesToRead + nBytesOffset, hFile); + buf.SeekPut(CUtlBuffer::SEEK_CURRENT, nBytesRead); + + if (seekGet != -1) + { + // can only seek the get after data has been put, otherwise buffer sets overflow error + buf.SeekGet(CUtlBuffer::SEEK_HEAD, seekGet); + } + + Seek(hFile, iStartPos + (nBytesRead - nBytesOffset), FILESYSTEM_SEEK_HEAD); + } + + return (nBytesRead != 0); +} + +bool CBaseFileSystem::ReadFile(const char* pFileName, const char* pPath, CUtlBuffer& buf, ssize_t nMaxBytes, ptrdiff_t nStartingByte, FSAllocFunc_t pfnAlloc) +{ + //CHECK_DOUBLE_SLASHES(pFileName); + + bool bBinary = !(buf.IsText() && !buf.ContainsCRLF()); + + FileHandle_t fp = Open(pFileName, (bBinary) ? "rb" : "rt", pPath); + if (!fp) + return false; + + if (nStartingByte != 0) + { + Seek(fp, nStartingByte, FILESYSTEM_SEEK_HEAD); + } + + AssertMsg(!pfnAlloc, "Custom allocators not yet supported!"); + + //if (pfnAlloc) + //{ + // g_pszReadFilename = (char*)pFileName; + //} + + bool bSuccess = ReadToBuffer(fp, buf, nMaxBytes, pfnAlloc); + + Close(fp); + + return bSuccess; +} + +bool CBaseFileSystem::WriteFile(const char* pFileName, const char* pPath, CUtlBuffer& buf) +{ + const char* pWriteFlags = "wb"; + if (buf.IsText() && !buf.ContainsCRLF()) + { + pWriteFlags = "wt"; + } + + FileHandle_t fp = Open(pFileName, pWriteFlags, pPath); + if (!fp) + return false; + + ssize_t nBytesWritten = Write(buf.Base(), buf.TellMaxPut(), fp); + + Close(fp); + return (nBytesWritten != 0); +} + +ssize_t CBaseFileSystem::ReadEx(void* pOutput, ssize_t /*destSize*/, ssize_t size, FileHandle_t file) +{ + if (!file) + { + assert(0); // Tried to Read NULL file handle! + return 0; + } + if (size < 0) + { + return 0; + } + + const ssize_t nRet = fread(pOutput, sizeof(uint8_t), size, (FILE*)file); + return nRet; + +} + +int CBaseFileSystem::CreateDirHierarchy(const char* pPath, const char* pPathID) +{ + NOTE_UNUSED(pPathID); + return ::CreateDirHierarchy(pPath); +} + +bool CBaseFileSystem::IsDirectory(const char* pPath, const char* pPathID) +{ + NOTE_UNUSED(pPathID); + return ::IsDirectory(pPath); +} + +char* CBaseFileSystem::ReadLine(char* maxChars, ssize_t maxOutputLength, FileHandle_t file) +{ + return fgets(maxChars, (int)maxOutputLength, (FILE*)file); +} + +CUtlString CBaseFileSystem::ReadString(FileHandle_t pFile) +{ + CUtlString result; + char c = '\0'; + + do + { + Read(&c, sizeof(char), pFile); + + if (c) + result += c; + + } while (c); + + return result; +} diff --git a/r5dev/filesystem/filesystem_std.h b/r5dev/filesystem/filesystem_std.h new file mode 100644 index 00000000..37262d01 --- /dev/null +++ b/r5dev/filesystem/filesystem_std.h @@ -0,0 +1,294 @@ +#ifndef FILESYSTEM_H +#define FILESYSTEM_H +#include +#include "ifilesystem.h" + +class CBaseFileSystem : public CTier1AppSystem +{ +public: + // Stub implementation of IAppSystem. + //virtual ~CBaseFileSystem() {}; + virtual bool Connect(const CreateInterfaceFn factory) { return false; }; + virtual void Disconnect() {}; + virtual void* QueryInterface(const char* const pInterfaceName) { return nullptr; }; + virtual InitReturnVal_t Init() { return InitReturnVal_t::INIT_FAILED; }; + virtual void Shutdown() {}; + virtual AppSystemTier_t GetTier() { return AppSystemTier_t::APP_SYSTEM_TIER_OTHER; }; + virtual void Reconnect(const CreateInterfaceFn factory, const char* const pInterfaceName) {}; + + + //-------------------------------------------------------- + virtual bool IsSteam() const { return false; }; + virtual FilesystemMountRetval_t MountSteamContent(int nExtraAppId = -1) { return FilesystemMountRetval_t::FILESYSTEM_MOUNT_FAILED; }; + + virtual bool InitFeatureFlags() { return false; }; + virtual bool InitFeatureFlags(const char* pszFlagSetFile) { return false; }; + + virtual void AddSearchPath(const char* pPath, const char* pPathID, SearchPathAdd_t addType) {}; + virtual bool RemoveSearchPath(const char* pPath, const char* pPathID) { return false; }; + virtual void RemoveAllSearchPaths(void) {}; + virtual void RemoveSearchPaths(const char* szPathID) {}; + virtual void MarkPathIDByRequestOnly(const char* pPathID, bool bRequestOnly) {} + virtual const char* RelativePathToFullPath(const char* pFileName, const char* pPathID, char* pLocalPath, ssize_t localPathBufferSize, PathTypeFilter_t pathFilter = FILTER_NONE, PathTypeQuery_t* pPathType = NULL) { return nullptr; }; +#if IsGameConsole() + virtual bool GetPackFileInfoFromRelativePath(const char* pFileName, const char* pPathID, char* pPackPath, ssize_t nPackPathBufferSize, ptrdiff_t& nPosition, ssize_t& nLength) { return false; }; +#endif + virtual ssize_t GetSearchPath(const char* pathID, bool bGetPackFiles, char* pPath, ssize_t nMaxLen) { return NULL; }; + virtual bool AddPackFile(const char* fullpath, const char* pathID) { return false; }; + + //-------------------------------------------------------- + // File manipulation operations + //-------------------------------------------------------- + virtual void RemoveFile(char const* pRelativePath, const char* pathID = 0) {}; // Deletes a file (on the WritePath) + virtual bool RenameFile(char const* pOldPath, char const* pNewPath, const char* pathID = 0) { return false; }; // Renames a file (on the WritePath) + virtual int CreateDirHierarchy(const char* path, const char* pathID = 0); // create a local directory structure + virtual bool IsDirectory(const char* pFileName, const char* pathID = 0); // File I/O and info + virtual ssize_t FileTimeToString(char* pStrip, ssize_t maxCharsIncludingTerminator, long fileTime) { return NULL; }; // Returns the string size + + //-------------------------------------------------------- + // Open file operations + //-------------------------------------------------------- + + virtual void SetBufferSize(FileHandle_t file/*, ssize_t nBytes*/) {}; + virtual bool IsOk(FileHandle_t file) { return false; }; + virtual bool EndOfFile(FileHandle_t file) { return false; }; + virtual char* ReadLine(char* pOutput, size_t maxChars, FileHandle_t file) { return nullptr; }; +#if ! defined(SWIG) + // Don't let SWIG see the PRINTF_FORMAT_STRING attribute or it will complain. + virtual ssize_t FPrintf(FileHandle_t file, PRINTF_FORMAT_STRING const char* pFormat, ...) FMTFUNCTION(3, 4) { return NULL; }; +#else + virtual ssize_t FPrintf(FileHandle_t file, const char* pFormat, ...) FMTFUNCTION(3, 4) { return NULL; }; +#endif + + //-------------------------------------------------------- + // Dynamic library operations + //-------------------------------------------------------- + // load/unload modules + virtual CSysModule* LoadModule(const char* pFileName, const char* pPathID = 0, bool bValidatedDllOnly = true) { return nullptr; }; + virtual void UnloadModule(CSysModule* pModule) {}; + + //-------------------------------------------------------- + // File searching operations + //-------------------------------------------------------- + // FindFirst/FindNext. Also see FindFirstEx. + virtual const char* FindFirst(const char* pWildCard, FileFindHandle_t* pHandle) { return nullptr; }; + virtual const char* FindNext(FileFindHandle_t handle) { return nullptr; }; + virtual bool FindIsDirectory(FileFindHandle_t handle) { return false; }; + virtual void FindClose(FileFindHandle_t handle) {}; + + // Same as FindFirst, but you can filter by path ID, which can make it faster. + virtual const char* FindFirstEx( + const char* pWildCard, + const char* pPathID, + FileFindHandle_t* pHandle + ) { return nullptr; }; + + // Searches for a file in all paths and results absolute path names for the file, works in pack files (zip and vpk) too + // Lets you search for something like sound/sound.cache and get a list of every sound cache + virtual void FindFileAbsoluteList(CUtlVector& outAbsolutePathNames, const char* pWildCard, const char* pPathID) {}; + + //-------------------------------------------------------- + // File name and directory operations + //-------------------------------------------------------- + + // FIXME: This method is obsolete! Use RelativePathToFullPath instead! + // converts a partial path into a full path + virtual const char* GetLocalPath(const char* pFileName, char* pLocalPath, ssize_t localPathBufferSize) { return nullptr; }; + + // Returns true on success ( based on current list of search paths, otherwise false if + // it can't be resolved ) + virtual bool FullPathToRelativePath(const char* pFullpath, char* pRelative, ssize_t maxlen) { return false; }; + + // Gets the current working directory + virtual bool GetCurrentDirectory(char* pDirectory, unsigned int maxlen) { return false; }; // Last parameter is a DWORD passed to 'GetCurrentDirectoryA()' internally. + + //-------------------------------------------------------- + // Filename dictionary operations + //-------------------------------------------------------- + + virtual FileNameHandle_t FindOrAddFileName(char const* pFileName) { return nullptr; }; + virtual bool String(const FileNameHandle_t& handle, char* buf, ssize_t buflen) { return NULL; }; + + //-------------------------------------------------------- + // Asynchronous file operations + //-------------------------------------------------------- + +//--------------- [ !!! AMOS: !!! ALL ASYNC METHODS ARE UNIMPLEMENTED !!! PURECALL !!! ] ---------------// + //------------------------------------ + // Global operations + //------------------------------------ + virtual void PureCall0() {}; + virtual void PureCall1() {}; + virtual void PureCall2() {}; + virtual void PureCall3() {}; + virtual void PureCall4() {}; + virtual void PureCall5() {}; + + //-------------------------------------------------------- + // Debugging operations + //-------------------------------------------------------- + + // Dump to printf/OutputDebugString the list of files that have not been closed + virtual void PrintOpenedFiles(void) {}; + virtual void PrintSearchPaths(void) {}; + + // output + virtual void SetWarningFunc(void (*pfnWarning)(const char* fmt, ...)) {}; + virtual void SetWarningLevel(FileWarningLevel_t level) {}; + virtual void AddLoggingFunc(void (*pfnLogFunc)(const char* fileName, const char* accessType)) {}; + virtual void RemoveLoggingFunc(FileSystemLoggingFunc_t logFunc) {}; + + virtual __int64 __fastcall sub_14038C240(__int64 a1) { return NULL; }; + virtual __int64 __fastcall sub_14038C380(__int64 a1) { return NULL; }; + virtual __int64 __fastcall sub_14038C400(__int64 a1, __int64 a2) { return NULL; }; + + // Returns the file system statistics retrieved by the implementation. Returns NULL if not supported. + virtual const FileSystemStatistics* GetFilesystemStatistics() { return nullptr; }; + + //-------------------------------------------------------- + // Start of new functions after Lost Coast release (7/05) + //-------------------------------------------------------- + + virtual FileHandle_t OpenEx(const char* pFileName, const char* pOptions, unsigned flags = 0, const char* pathID = 0/*, char** ppszResolvedFilename = NULL*/) { return nullptr; }; + + // Extended version of read provides more context to allow for more optimal reading + virtual ssize_t ReadEx(void* pOutput, ssize_t sizeDest, ssize_t size, FileHandle_t file); + virtual ssize_t ReadFileEx(const char* pFileName, const char* pPath, void** ppBuf, bool bNullTerminate = false, bool bOptimalAlloc = false, ssize_t nMaxBytes = 0, ptrdiff_t nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL) { return NULL; }; + + virtual FileNameHandle_t FindFileName(char const* pFileName) { return nullptr; }; + +#if defined( TRACK_BLOCKING_IO ) + virtual void EnableBlockingFileAccessTracking(bool state) {}; + virtual bool IsBlockingFileAccessEnabled() { return false; }; + + virtual IBlockingFileItemList* RetrieveBlockingFileAccessInfo() { return nullptr; }; +#endif + + virtual void SetupPreloadData() {}; + virtual void DiscardPreloadData() {}; + + // If the "PreloadedData" hasn't been purged, then this'll try and instance the KeyValues using the fast path of compiled keyvalues loaded during startup. + // Otherwise, it'll just fall through to the regular KeyValues loading routines + virtual KeyValues* LoadKeyValues(KeyValuesPreloadType_t type, char const* filename, char const* pPathID = 0) { return nullptr; }; + virtual bool LoadKeyValues(KeyValues& head, KeyValuesPreloadType_t type, char const* filename, char const* pPathID = 0) { return false; }; + + virtual bool GetFileTypeForFullPath(char const* pFullPath, wchar_t* buf, size_t bufSizeInBytes) { return false; }; + + //-------------------------------------------------------- + //-------------------------------------------------------- + virtual bool ReadToBuffer(FileHandle_t hFile, CUtlBuffer& buf, ssize_t nMaxBytes = 0, FSAllocFunc_t pfnAlloc = NULL); + + //-------------------------------------------------------- + // Optimal IO operations + //-------------------------------------------------------- + virtual bool GetOptimalIOConstraints(FileHandle_t hFile, uint64_t* pOffsetAlign, uint64_t* pSizeAlign, uint64_t* pBufferAlign); + virtual void* AllocOptimalReadBuffer(ptrdiff_t nOffset = 0/*!!! UNUSED !!!*/, ssize_t nSize = 0) { return nullptr; }; + virtual void FreeOptimalReadBuffer(void*) {}; + + + virtual bool __fastcall sub_140383E00(__int64 a2) { return false; }; + virtual bool sub_1403836A0() { return false; }; + virtual __int64 __fastcall sub_140384310(int a1) { return NULL; }; + virtual __int64 __fastcall CheckVPKMode(int nMode) { return NULL; }; // Checks if the VPK mode equals the mode input. + + + //-------------------------------------------------------- + // Cache/VPK operations + //-------------------------------------------------------- + virtual bool ReadFromCache(const char* pPath, FileSystemCache* pCache) { return false; }; + + virtual bool __fastcall sub_14037FFA0(__int64 a1, unsigned int a2, __int64 a3) { return false; }; + + virtual void SetVPKCacheModeClient() {}; // g_nVPKCacheMode = 1; + virtual void SetVPKCacheModeServer() {}; // g_nVPKCacheMode = 2; + virtual bool IsVPKCacheEnabled() { return false; }; // g_nVPKCacheMode != 0; + + virtual __int64 __fastcall PrecacheTaskItem(__int64 a1) { return NULL; }; + + virtual void ResetItemCacheSize(int edx) {}; + virtual void __fastcall sub_140380100(__int64 a1) {}; + virtual void __fastcall sub_140380230(char a2) {}; + virtual void* __fastcall sub_1403801F0(const void* a1, unsigned int a2) { return nullptr; }; + virtual void __fastcall sub_140380220(__int64 a1) {}; + virtual bool ResetItemCache() { return false; }; + virtual char __fastcall sub_1403836D0(int a1, char* a2, unsigned int a3) { return false; }; + virtual __int64 __fastcall sub_140383840(unsigned int a1, __int64 a2, char* a3, unsigned int BufferCount) { return NULL; }; + virtual const char** __fastcall sub_140383760(unsigned int a1) { return nullptr; }; + virtual __int64 __fastcall sub_140383A20(const char* a1) { return NULL; }; + + virtual VPKData_t* MountVPKFile(const char* pVpkPath) { return nullptr; }; + virtual const char* UnmountVPKFile(const char* pBasename) { return nullptr; }; + + virtual void __fastcall sub_140383370() {}; + virtual void __fastcall sub_140383560() {}; + + virtual unsigned __int64 __fastcall PnpCtxRegQueryInfoKey(__int64 a1, char* a2, unsigned int* a3, unsigned __int64 a4, unsigned __int64 a5, void(__fastcall* a6)(unsigned __int64)) { return NULL; }; + virtual char* sub_1403842B0() { return nullptr; }; + virtual __int64 __fastcall sub_1403842C0(__int64 a1, unsigned int a2, __int64 a3) { return NULL; }; + + virtual char __fastcall LoadMainVPK(const char* pszVPKFile) { return NULL; }; + + virtual __int64 sub_140380080() { return NULL; }; + virtual char __fastcall sub_14038B530(const char* a1, unsigned __int8* a2, char* a3, __int64 Count) { return NULL; }; + virtual __int64 __fastcall sub_14038C830(unsigned __int16* a1) { return NULL; }; + virtual __int64 __fastcall sub_140388360(char* a1, __int64 a2) { return NULL; }; + virtual __int64 __fastcall sub_140384C60(char* a1, unsigned __int64 a2) { return NULL; }; + virtual void __fastcall sub_140382A80() { }; + virtual __int64 __fastcall sub_14038CC90(int a1, unsigned int a2, __int64 a3, __int64 a4) { return NULL; }; + virtual __int64 __fastcall UserMathErrorFunction() { return NULL; }; + + virtual ssize_t Read(void* pOutput, ssize_t size, FileHandle_t file); + virtual ssize_t Write(void const* pInput, ssize_t size, FileHandle_t file); + + // if pathID is NULL, all paths will be searched for the file + virtual FileHandle_t Open(const char* pFileName, const char* pOptions, const char* pPathID = 0, int64_t unknown = 0); + virtual void Close(FileHandle_t file); + + virtual void Seek(FileHandle_t file, ptrdiff_t pos, FileSystemSeek_t seekType); + virtual ptrdiff_t Tell(FileHandle_t file); + virtual ssize_t FSize(const char* pFileName, const char* pPathID = 0); // Gets optimized away if it isn't named differently or used. + virtual ssize_t Size(FileHandle_t file); + + virtual void Flush(FileHandle_t file); + virtual bool Precache(const char* pFileName, const char* pPathID = 0); + + virtual bool FileExists(const char* pFileName, const char* pPathID = 0); + virtual bool IsFileWritable(char const* pFileName, const char* pPathID = 0); + virtual bool SetFileWritable(char const* pFileName, bool writable, const char* pPathID = 0); + + virtual long long GetFileTime(const char* pFileName, const char* pPathID = 0); + + virtual bool ReadFile(const char* pFileName, const char* pPath, CUtlBuffer& buf, ssize_t nMaxBytes = 0, ptrdiff_t nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL); + virtual bool WriteFile(const char* pFileName, const char* pPath, CUtlBuffer& buf); + virtual bool UnzipFile(const char* pFileName, const char* pPath, const char* pDestination) { return false; }; + + char* ReadLine(char* maxChars, ssize_t maxOutputLength, FileHandle_t file); + CUtlString ReadString(FileHandle_t pFile); +}; + +class CFileSystem_Stdio : public CBaseFileSystem +{ +protected: + // implementation of CBaseFileSystem virtual functions + virtual FILE* FS_fopen(const char* filename, const char* options, unsigned flags, int64* size) { return nullptr; }; + virtual void FS_setbufsize(FILE* fp, unsigned nBytes) {}; + virtual void FS_fclose(FILE* fp) {}; + virtual void FS_fseek(FILE* fp, int64 pos, int seekType) {}; + virtual long FS_ftell(FILE* fp) { return NULL; }; + virtual int FS_feof(FILE* fp) { return NULL; }; + virtual size_t FS_fread(void* dest, size_t destSize, size_t size, FILE* fp) { return NULL; }; + virtual size_t FS_fwrite(const void* src, size_t size, FILE* fp) { return NULL; }; + virtual bool FS_setmode(FILE* fp, FileMode_t mode) { return false; }; + virtual size_t FS_vfprintf(FILE* fp, const char* fmt, va_list list) { return NULL; }; + virtual int FS_ferror(FILE* fp) { return NULL; }; + virtual int FS_fflush(FILE* fp) { return NULL; }; + virtual char* FS_fgets(char* dest, unsigned int destSize) { return nullptr; }; + virtual int FS_stat(const char* path, struct _stat* buf, bool* pbLoadedFromSteamCache = NULL) { return NULL; }; + virtual int FS_chmod(const char* path, int pmode) { return NULL; }; + virtual HANDLE FS_FindFirstFile(const char* findname, WIN32_FIND_DATA* dat) { return NULL; }; + virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA* dat) { return false; }; + virtual bool FS_FindClose(HANDLE handle) { return false; }; + virtual int FS_GetSectorSize(FILE*) { return NULL; }; +}; + +#endif // FILESYSTEM_H diff --git a/r5dev/game/CMakeLists.txt b/r5dev/game/CMakeLists.txt index 6984ca98..b24557d3 100644 --- a/r5dev/game/CMakeLists.txt +++ b/r5dev/game/CMakeLists.txt @@ -41,7 +41,7 @@ endif() if( ${PROJECT_NAME} STREQUAL "server_static" ) -add_sources( SOURCE_GROUP "Server" +add_sources( SOURCE_GROUP "AI" "server/ai_network.cpp" "server/ai_network.h" "server/ai_networkmanager.cpp" @@ -49,30 +49,59 @@ add_sources( SOURCE_GROUP "Server" "server/ai_node.h" "server/ai_utility.cpp" "server/ai_utility.h" + "server/detour_impl.h" +) + +add_sources( SOURCE_GROUP "Entity" "server/baseanimating.cpp" "server/baseanimating.h" "server/baseanimatingoverlay.h" "server/basecombatcharacter.h" "server/baseentity.cpp" "server/baseentity.h" - "server/detour_impl.h" "server/entitylist.cpp" "server/entitylist.h" + "server/entityoutput.cpp" + "server/entityoutput.h" +) + +add_sources( SOURCE_GROUP "Network" + "server/networkproperty.cpp" + "server/networkproperty.h" +) + +add_sources( SOURCE_GROUP "Player" + "server/player.cpp" + "server/player.h" + "server/playerlocaldata.h" +) + +add_sources( SOURCE_GROUP "Script" + "server/vscript_server.cpp" + "server/vscript_server.h" +) + +add_sources( SOURCE_GROUP "Physics" + "server/physics_main.cpp" + "server/physics_main.h" +) + +add_sources( SOURCE_GROUP "Utility" + "server/cbase.cpp" + "server/cbase.h" "server/gameinterface.cpp" "server/gameinterface.h" "server/movehelper_server.cpp" "server/movehelper_server.h" - "server/networkproperty.cpp" - "server/networkproperty.h" - "server/physics_main.cpp" - "server/physics_main.h" - "server/player.cpp" - "server/player.h" - "server/playerlocaldata.h" "server/util_server.cpp" "server/util_server.h" - "server/vscript_server.cpp" - "server/vscript_server.h" + "server/variant_t.cpp" + "server/variant_t.h" +) + +add_sources( SOURCE_GROUP "LiveAPI" + "server/liveapi/liveapi.cpp" + "server/liveapi/liveapi.h" ) add_sources( SOURCE_GROUP "Public" @@ -146,6 +175,7 @@ endif() target_include_directories( ${PROJECT_NAME} PRIVATE "${ENGINE_SOURCE_DIR}/tier0/" "${ENGINE_SOURCE_DIR}/tier1/" + "${THIRDPARTY_SOURCE_DIR}/mbedtls/include" ) endmacro() diff --git a/r5dev/game/client/input.cpp b/r5dev/game/client/input.cpp index 9b3ce9d7..6a2f4ced 100644 --- a/r5dev/game/client/input.cpp +++ b/r5dev/game/client/input.cpp @@ -18,12 +18,7 @@ void CInput::SetCustomWeaponActivity(CInput* pInput, int weaponActivity) v_CInput__SetCustomWeaponActivity(pInput, weaponActivity); } -void VInput::Attach(void) const +void VInput::Detour(const bool bAttach) const { - DetourAttach(&v_CInput__SetCustomWeaponActivity, CInput::SetCustomWeaponActivity); -} - -void VInput::Detach(void) const -{ - DetourDetach(&v_CInput__SetCustomWeaponActivity, CInput::SetCustomWeaponActivity); + DetourSetup(&v_CInput__SetCustomWeaponActivity, CInput::SetCustomWeaponActivity, bAttach); } diff --git a/r5dev/game/client/input.h b/r5dev/game/client/input.h index 21fab76e..c3206bf8 100644 --- a/r5dev/game/client/input.h +++ b/r5dev/game/client/input.h @@ -9,7 +9,6 @@ public: private: }; -inline CMemory p_CInput__SetCustomWeaponActivity; inline void(*v_CInput__SetCustomWeaponActivity)(CInput* pInput, int weaponActivity); inline IInput* g_pInput_VFTable = nullptr; @@ -20,15 +19,14 @@ class VInput : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CInput::`vftable'", reinterpret_cast(g_pInput_VFTable)); - LogFunAdr("CInput::SetCustomWeaponActivity", p_CInput__SetCustomWeaponActivity.GetPtr()); - LogVarAdr("g_Input", reinterpret_cast(g_pInput)); + LogConAdr("CInput::`vftable'", g_pInput_VFTable); + LogFunAdr("CInput::SetCustomWeaponActivity", v_CInput__SetCustomWeaponActivity); + LogVarAdr("g_Input", g_pInput); } virtual void GetFun(void) const { - p_CInput__SetCustomWeaponActivity = g_GameDll. - FindPatternSIMD("89 91 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC F3 0F 11 89 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC F3 0F 10 81 ?? ?? ?? ??"); - v_CInput__SetCustomWeaponActivity = p_CInput__SetCustomWeaponActivity.RCast(); + g_GameDll.FindPatternSIMD("89 91 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC F3 0F 11 89 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC F3 0F 10 81 ?? ?? ?? ??") + .GetPtr(v_CInput__SetCustomWeaponActivity); } virtual void GetVar(void) const { @@ -39,8 +37,7 @@ class VInput : public IDetour { g_pInput_VFTable = g_GameDll.GetVirtualMethodTable(".?AVCInput@@").RCast(); } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/client/movehelper_client.h b/r5dev/game/client/movehelper_client.h index 03a18071..91fc222c 100644 --- a/r5dev/game/client/movehelper_client.h +++ b/r5dev/game/client/movehelper_client.h @@ -35,17 +35,16 @@ class VMoveHelperClient : public IDetour { virtual void GetAdr(void) const { - LogVarAdr("s_MoveHelperClient", reinterpret_cast(s_MoveHelperClient)); + LogVarAdr("s_MoveHelperClient", s_MoveHelperClient); } virtual void GetFun(void) const { } virtual void GetVar(void) const { - CMemory pFunc = g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 80 3D ?? ?? ?? ?? ?? 48 8B D9 74 1A"); + const CMemory pFunc = g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 80 3D ?? ?? ?? ?? ?? 48 8B D9 74 1A"); s_MoveHelperClient = pFunc.FindPattern("4C 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/client/viewrender.cpp b/r5dev/game/client/viewrender.cpp index b09d43d4..88245599 100644 --- a/r5dev/game/client/viewrender.cpp +++ b/r5dev/game/client/viewrender.cpp @@ -10,7 +10,7 @@ VMatrix* CViewRender::GetWorldMatrixForView(int8_t slot) { - return CViewRender_GetWorldMatrixForView(this, slot); + return CViewRender__GetWorldMatrixForView(this, slot); } const Vector3D& MainViewOrigin() diff --git a/r5dev/game/client/viewrender.h b/r5dev/game/client/viewrender.h index 13a862f7..5afdc7d2 100644 --- a/r5dev/game/client/viewrender.h +++ b/r5dev/game/client/viewrender.h @@ -15,30 +15,28 @@ public: const Vector3D& MainViewOrigin(); const QAngle& MainViewAngles(); -inline CMemory p_CViewRender_GetWorldMatrixForView; -inline VMatrix*(*CViewRender_GetWorldMatrixForView)(CViewRender*, int8_t); +inline VMatrix*(*CViewRender__GetWorldMatrixForView)(CViewRender*, int8_t); inline Vector3D* g_vecRenderOrigin = nullptr; inline QAngle* g_vecRenderAngles = nullptr; inline CViewRender* g_pViewRender = nullptr; -inline CMemory g_pViewRender_VFTable; +inline void* g_pViewRender_VFTable; /////////////////////////////////////////////////////////////////////////////// class V_ViewRender : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CViewRender::`vftable'", g_pViewRender_VFTable.GetPtr()); - LogFunAdr("CViewRender::GetWorldMatrixForView", p_CViewRender_GetWorldMatrixForView.GetPtr()); - LogVarAdr("g_ViewRender", reinterpret_cast(g_pViewRender)); - LogVarAdr("g_vecRenderOrigin", reinterpret_cast(g_vecRenderOrigin)); - LogVarAdr("g_vecRenderAngles", reinterpret_cast(g_vecRenderAngles)); + LogConAdr("CViewRender::`vftable'", g_pViewRender_VFTable); + LogFunAdr("CViewRender::GetWorldMatrixForView", CViewRender__GetWorldMatrixForView); + LogVarAdr("g_ViewRender", g_pViewRender); + LogVarAdr("g_vecRenderOrigin", g_vecRenderOrigin); + LogVarAdr("g_vecRenderAngles", g_vecRenderAngles); } virtual void GetFun(void) const { - p_CViewRender_GetWorldMatrixForView = g_pViewRender_VFTable.WalkVTable(16).Deref(); // 16th vfunc. - CViewRender_GetWorldMatrixForView = p_CViewRender_GetWorldMatrixForView.RCast(); + CMemory(g_pViewRender_VFTable).WalkVTable(16).Deref().GetPtr(CViewRender__GetWorldMatrixForView); // 16th vfunc. } virtual void GetVar(void) const { @@ -53,7 +51,6 @@ class V_ViewRender : public IDetour { g_pViewRender_VFTable = g_GameDll.GetVirtualMethodTable(".?AVCViewRender@@"); } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/client/vscript_client.cpp b/r5dev/game/client/vscript_client.cpp index 507b4523..81e451b8 100644 --- a/r5dev/game/client/vscript_client.cpp +++ b/r5dev/game/client/vscript_client.cpp @@ -1,4 +1,4 @@ -//=============================================================================// + //=============================================================================// // // Purpose: Expose native code to VScript API // @@ -9,29 +9,71 @@ //=============================================================================// #include "core/stdafx.h" -#include "vpc/keyvalues.h" +#include "tier1/keyvalues.h" #include "engine/cmodel_bsp.h" #include "engine/host_state.h" #include "engine/client/cl_main.h" #include "networksystem/pylon.h" #include "networksystem/listmanager.h" #include "game/shared/vscript_shared.h" + +#include "vscript/vscript.h" #include "vscript/languages/squirrel_re/include/sqvm.h" #include "vscript_client.h" +/* +===================== +SQVM_ClientScript_f + + Executes input on the + VM in CLIENT context. +===================== +*/ +static void SQVM_ClientScript_f(const CCommand& args) +{ + if (args.ArgC() >= 2) + { + Script_Execute(args.ArgS(), SQCONTEXT::CLIENT); + } +} + +/* +===================== +SQVM_UIScript_f + + Executes input on the + VM in UI context. +===================== +*/ +static void SQVM_UIScript_f(const CCommand& args) +{ + if (args.ArgC() >= 2) + { + Script_Execute(args.ArgS(), SQCONTEXT::UI); + } +} + +static ConCommand script_client("script_client", SQVM_ClientScript_f, "Run input code as CLIENT script on the VM", FCVAR_DEVELOPMENTONLY | FCVAR_CLIENTDLL | FCVAR_CHEAT); +static ConCommand script_ui("script_ui", SQVM_UIScript_f, "Run input code as UI script on the VM", FCVAR_DEVELOPMENTONLY | FCVAR_CLIENTDLL | FCVAR_CHEAT); + //----------------------------------------------------------------------------- // Purpose: checks if the server index is valid, raises an error if not //----------------------------------------------------------------------------- -static SQBool Script_CheckServerIndex(HSQUIRRELVM v, SQInteger iServer) +static SQBool Script_CheckServerIndexAndFailure(HSQUIRRELVM v, SQInteger iServer) { - SQInteger iCount = static_cast(g_pServerListManager->m_vServerList.size()); + SQInteger iCount = static_cast(g_ServerListManager.m_vServerList.size()); if (iServer >= iCount) { v_SQVM_RaiseError(v, "Index must be less than %i.\n", iCount); return false; } + else if (iServer == -1) // If its still -1, then 'sq_getinteger' failed + { + v_SQVM_RaiseError(v, "Invalid argument type provided.\n"); + return false; + } return true; } @@ -46,11 +88,13 @@ namespace VScriptCode SQRESULT RefreshServerList(HSQUIRRELVM v) { string serverMessage; // Refresh list. - size_t iCount = g_pServerListManager->RefreshServerList(serverMessage); + size_t iCount; + // TODO: return error string on failure? + g_ServerListManager.RefreshServerList(serverMessage, iCount); sq_pushinteger(v, static_cast(iCount)); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -58,10 +102,10 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerCount(HSQUIRRELVM v) { - size_t iCount = g_pServerListManager->m_vServerList.size(); + size_t iCount = g_ServerListManager.m_vServerList.size(); sq_pushinteger(v, static_cast(iCount)); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -69,50 +113,47 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetHiddenServerName(HSQUIRRELVM v) { - SQChar* privateToken = sq_getstring(v, 1); + const SQChar* privateToken = nullptr; - if (!VALID_CHARSTAR(privateToken)) - return SQ_OK; + if (SQ_FAILED(sq_getstring(v, 2, &privateToken)) || VALID_CHARSTAR(privateToken)) + { + v_SQVM_ScriptError("Empty or null private token"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } string hiddenServerRequestMessage; NetGameServer_t serverListing; - bool result = g_pMasterServer->GetServerByToken(serverListing, hiddenServerRequestMessage, privateToken); // Send token connect request. + bool result = g_MasterServer.GetServerByToken(serverListing, hiddenServerRequestMessage, privateToken); // Send token connect request. if (!result) { if (hiddenServerRequestMessage.empty()) - { sq_pushstring(v, "Request failed", -1); - } else { hiddenServerRequestMessage = Format("Request failed: %s", hiddenServerRequestMessage.c_str()); sq_pushstring(v, hiddenServerRequestMessage.c_str(), -1); } - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } - if (serverListing.m_svHostName.empty()) + if (serverListing.name.empty()) { if (hiddenServerRequestMessage.empty()) - { hiddenServerRequestMessage = Format("Server listing empty"); - } else - { hiddenServerRequestMessage = Format("Server listing empty: %s", hiddenServerRequestMessage.c_str()); - } sq_pushstring(v, hiddenServerRequestMessage.c_str(), -1); } else { - hiddenServerRequestMessage = Format("Found server: %s", serverListing.m_svHostName.c_str()); + hiddenServerRequestMessage = Format("Found server: %s", serverListing.name.c_str()); sq_pushstring(v, hiddenServerRequestMessage.c_str(), -1); } - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -120,18 +161,18 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerName(HSQUIRRELVM v) { - std::lock_guard l(g_pServerListManager->m_Mutex); - SQInteger iServer = sq_getinteger(v, 1); + AUTO_LOCK(g_ServerListManager.m_Mutex); - if (!Script_CheckServerIndex(v, iServer)) - { - return SQ_ERROR; - } + SQInteger iServer = -1; + sq_getinteger(v, 2, &iServer); - const string& serverName = g_pServerListManager->m_vServerList[iServer].m_svHostName; + if (!Script_CheckServerIndexAndFailure(v, iServer)) + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + + const string& serverName = g_ServerListManager.m_vServerList[iServer].name; sq_pushstring(v, serverName.c_str(), -1); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -139,18 +180,18 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerDescription(HSQUIRRELVM v) { - std::lock_guard l(g_pServerListManager->m_Mutex); - SQInteger iServer = sq_getinteger(v, 1); + AUTO_LOCK(g_ServerListManager.m_Mutex); - if (!Script_CheckServerIndex(v, iServer)) - { - return SQ_ERROR; - } + SQInteger iServer = -1; + sq_getinteger(v, 2, &iServer); - const string& serverDescription = g_pServerListManager->m_vServerList[iServer].m_svDescription; + if (!Script_CheckServerIndexAndFailure(v, iServer)) + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + + const string& serverDescription = g_ServerListManager.m_vServerList[iServer].description; sq_pushstring(v, serverDescription.c_str(), -1); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -158,18 +199,18 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerMap(HSQUIRRELVM v) { - std::lock_guard l(g_pServerListManager->m_Mutex); - SQInteger iServer = sq_getinteger(v, 1); + AUTO_LOCK(g_ServerListManager.m_Mutex); - if (!Script_CheckServerIndex(v, iServer)) - { - return SQ_ERROR; - } + SQInteger iServer = -1; + sq_getinteger(v, 2, &iServer); - const string& svServerMapName = g_pServerListManager->m_vServerList[iServer].m_svHostMap; + if (!Script_CheckServerIndexAndFailure(v, iServer)) + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + + const string& svServerMapName = g_ServerListManager.m_vServerList[iServer].map; sq_pushstring(v, svServerMapName.c_str(), -1); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -177,18 +218,18 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerPlaylist(HSQUIRRELVM v) { - std::lock_guard l(g_pServerListManager->m_Mutex); - SQInteger iServer = sq_getinteger(v, 1); + AUTO_LOCK(g_ServerListManager.m_Mutex); - if (!Script_CheckServerIndex(v, iServer)) - { - return SQ_ERROR; - } + SQInteger iServer = -1; + sq_getinteger(v, 2, &iServer); - const string& serverPlaylist = g_pServerListManager->m_vServerList[iServer].m_svPlaylist; + if (!Script_CheckServerIndexAndFailure(v, iServer)) + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + + const string& serverPlaylist = g_ServerListManager.m_vServerList[iServer].playlist; sq_pushstring(v, serverPlaylist.c_str(), -1); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -196,18 +237,18 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerCurrentPlayers(HSQUIRRELVM v) { - std::lock_guard l(g_pServerListManager->m_Mutex); - SQInteger iServer = sq_getinteger(v, 1); + AUTO_LOCK(g_ServerListManager.m_Mutex); - if (!Script_CheckServerIndex(v, iServer)) - { - return SQ_ERROR; - } + SQInteger iServer = -1; + sq_getinteger(v, 2, &iServer); - const string& playerCount = g_pServerListManager->m_vServerList[iServer].m_svPlayerCount.c_str(); - sq_pushinteger(v, strtol(playerCount.c_str(), NULL, NULL)); + if (!Script_CheckServerIndexAndFailure(v, iServer)) + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); - return SQ_OK; + const SQInteger playerCount = g_ServerListManager.m_vServerList[iServer].numPlayers; + sq_pushinteger(v, playerCount); + + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -215,18 +256,18 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerMaxPlayers(HSQUIRRELVM v) { - std::lock_guard l(g_pServerListManager->m_Mutex); - SQInteger iServer = sq_getinteger(v, 1); + AUTO_LOCK(g_ServerListManager.m_Mutex); - if (!Script_CheckServerIndex(v, iServer)) - { - return SQ_ERROR; - } + SQInteger iServer = -1; + sq_getinteger(v, 2, &iServer); - const string& maxPlayers = g_pServerListManager->m_vServerList[iServer].m_svMaxPlayers; - sq_pushinteger(v, strtol(maxPlayers.c_str(), NULL, NULL)); + if (!Script_CheckServerIndexAndFailure(v, iServer)) + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); - return SQ_OK; + const SQInteger maxPlayers = g_ServerListManager.m_vServerList[iServer].maxPlayers; + sq_pushinteger(v, maxPlayers); + + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -244,7 +285,10 @@ namespace VScriptCode PromoRightDesc }; - R5RPromoData ePromoIndex = static_cast(sq_getinteger(v, 1)); + SQInteger idx = 0; + sq_getinteger(v, 2, &idx); + + R5RPromoData ePromoIndex = static_cast(idx); const char* pszPromoKey; switch (ePromoIndex) @@ -287,7 +331,30 @@ namespace VScriptCode } sq_pushstring(v, pszPromoKey, -1); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); + } + + SQRESULT GetEULAContents(HSQUIRRELVM v) + { + MSEulaData_t eulaData; + string eulaRequestMessage; + + if (g_MasterServer.GetEULA(eulaData, eulaRequestMessage)) + { + // set EULA version cvar to the newly fetched EULA version + eula_version->SetValue(eulaData.version); + + sq_pushstring(v, eulaData.contents.c_str(), -1); + } + else + { + string error = Format("Failed to load EULA Data: %s", eulaRequestMessage.c_str()); + + Warning(eDLL_T::UI, "%s\n", error.c_str()); + sq_pushstring(v, error.c_str(), -1); + } + + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -295,16 +362,24 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT ConnectToServer(HSQUIRRELVM v) { - SQChar* ipAddress = sq_getstring(v, 1); - SQChar* cryptoKey = sq_getstring(v, 2); + const SQChar* ipAddress = nullptr; + if (SQ_FAILED(sq_getstring(v, 2, &ipAddress))) + { + v_SQVM_ScriptError("Missing ip address"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } - if (!VALID_CHARSTAR(ipAddress) || VALID_CHARSTAR(cryptoKey)) - return SQ_OK; + const SQChar* cryptoKey = nullptr; + if (SQ_FAILED(sq_getstring(v, 3, &cryptoKey))) + { + v_SQVM_ScriptError("Missing encryption key"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } - DevMsg(eDLL_T::UI, "Connecting to server with ip address '%s' and encryption key '%s'\n", ipAddress, cryptoKey); - g_pServerListManager->ConnectToServer(ipAddress, cryptoKey); + Msg(eDLL_T::UI, "Connecting to server with ip address '%s' and encryption key '%s'\n", ipAddress, cryptoKey); + g_ServerListManager.ConnectToServer(ipAddress, cryptoKey); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -312,20 +387,22 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT ConnectToListedServer(HSQUIRRELVM v) { - std::lock_guard l(g_pServerListManager->m_Mutex); - SQInteger iServer = sq_getinteger(v, 1); + AUTO_LOCK(g_ServerListManager.m_Mutex); - if (!Script_CheckServerIndex(v, iServer)) + SQInteger iServer = -1; + sq_getinteger(v, 2, &iServer); + + if (!Script_CheckServerIndexAndFailure(v, iServer)) { - return SQ_ERROR; + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); } - const NetGameServer_t& gameServer = g_pServerListManager->m_vServerList[iServer]; + const NetGameServer_t& gameServer = g_ServerListManager.m_vServerList[iServer]; - g_pServerListManager->ConnectToServer(gameServer.m_svIpAddress, gameServer.m_svGamePort, - gameServer.m_svEncryptionKey); + g_ServerListManager.ConnectToServer(gameServer.address, gameServer.port, + gameServer.netKey); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -333,25 +410,29 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT ConnectToHiddenServer(HSQUIRRELVM v) { - SQChar* privateToken = sq_getstring(v, 1); + const SQChar* privateToken = nullptr; + const SQRESULT strRet = sq_getstring(v, 2, &privateToken); - if (!VALID_CHARSTAR(privateToken)) - return SQ_OK; + if (SQ_FAILED(strRet) || VALID_CHARSTAR(privateToken)) + { + v_SQVM_ScriptError("Empty or null private token"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } string hiddenServerRequestMessage; NetGameServer_t netListing; - bool result = g_pMasterServer->GetServerByToken(netListing, hiddenServerRequestMessage, privateToken); // Send token connect request. + const bool result = g_MasterServer.GetServerByToken(netListing, hiddenServerRequestMessage, privateToken); // Send token connect request. if (result) { - g_pServerListManager->ConnectToServer(netListing.m_svIpAddress, netListing.m_svGamePort, netListing.m_svEncryptionKey); + g_ServerListManager.ConnectToServer(netListing.address, netListing.port, netListing.netKey); } else { Warning(eDLL_T::UI, "Failed to connect to private server: %s\n", hiddenServerRequestMessage.c_str()); } - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -360,7 +441,7 @@ namespace VScriptCode SQRESULT IsClientDLL(HSQUIRRELVM v) { sq_pushbool(v, ::IsClientDLL()); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } } } @@ -400,6 +481,7 @@ void Script_RegisterUIFunctions(CSquirrelVM* s) // Misc main menu functions DEFINE_CLIENT_SCRIPTFUNC_NAMED(s, GetPromoData, "Gets promo data for specified slot type", "string", "int"); + DEFINE_CLIENT_SCRIPTFUNC_NAMED(s, GetEULAContents, "Gets EULA contents from masterserver", "string", ""); // Functions for connecting to servers DEFINE_CLIENT_SCRIPTFUNC_NAMED(s, ConnectToServer, "Joins server by ip address and encryption key", "void", "string, string"); @@ -415,3 +497,14 @@ void Script_RegisterCoreClientFunctions(CSquirrelVM* s) { DEFINE_CLIENT_SCRIPTFUNC_NAMED(s, IsClientDLL, "Returns whether this build is client only", "bool", ""); } + +//--------------------------------------------------------------------------------- +// Purpose: console variables for scripts, these should not be used in engine/sdk code !!! +//--------------------------------------------------------------------------------- +static ConVar settings_reflex("settings_reflex", "1", FCVAR_RELEASE, "Selected NVIDIA Reflex mode.", "0 = Off. 1 = On. 2 = On + Boost."); +static ConVar serverbrowser_hideEmptyServers("serverbrowser_hideEmptyServers", "0", FCVAR_RELEASE, "Hide empty servers in the server browser."); +static ConVar serverbrowser_mapFilter("serverbrowser_mapFilter", "0", FCVAR_RELEASE, "Filter servers by map in the server browser."); +static ConVar serverbrowser_gamemodeFilter("serverbrowser_gamemodeFilter", "0", FCVAR_RELEASE, "Filter servers by gamemode in the server browser."); + +// NOTE: if we want to make a certain promo only show once, add the playerprofile flag to the cvar below. Current behavior = always show after game restart. +static ConVar promo_version_accepted("promo_version_accepted", "0", FCVAR_RELEASE, "The accepted promo version."); diff --git a/r5dev/game/server/ai_network.cpp b/r5dev/game/server/ai_network.cpp index be14ea2d..3604cbd5 100644 --- a/r5dev/game/server/ai_network.cpp +++ b/r5dev/game/server/ai_network.cpp @@ -11,6 +11,8 @@ int g_DebugConnectNode1 = -1; int g_DebugConnectNode2 = -1; #define DebuggingConnect( node1, node2 ) ( ( node1 == g_DebugConnectNode1 && node2 == g_DebugConnectNode2 ) || ( node1 == g_DebugConnectNode2 && node2 == g_DebugConnectNode1 ) ) +static ConVar ai_ainDebugConnect("ai_ainDebugConnect", "0", FCVAR_DEVELOPMENTONLY, "Debug AIN node connections"); + //----------------------------------------------------------------------------- // Purpose: debug logs node connections // Input : node1 - @@ -20,7 +22,7 @@ int g_DebugConnectNode2 = -1; //----------------------------------------------------------------------------- void CAI_Network::DebugConnectMsg(int node1, int node2, const char* pszFormat, ...) { - if (ai_ainDebugConnect->GetBool()) + if (ai_ainDebugConnect.GetBool()) { if (DebuggingConnect(node1, node2)) { @@ -36,7 +38,7 @@ void CAI_Network::DebugConnectMsg(int node1, int node2, const char* pszFormat, . }///////////////////////////// - DevMsg(eDLL_T::SERVER, "%s", buf); + Msg(eDLL_T::SERVER, "%s", buf); } } } @@ -54,7 +56,7 @@ void* CAI_Network::GetVTable(void) const // Purpose: gets the number of node links // Output : int //----------------------------------------------------------------------------- -int CAI_Network::GetNumLinks(void) const +int CAI_Network::NumLinks(void) const { return m_iNumLinks; } @@ -63,7 +65,7 @@ int CAI_Network::GetNumLinks(void) const // Purpose: gets the number of zones // Output : int //----------------------------------------------------------------------------- -int CAI_Network::GetNumZones(void) const +int CAI_Network::NumZones(void) const { return m_iNumZones; } @@ -72,7 +74,7 @@ int CAI_Network::GetNumZones(void) const // Purpose: gets the number of hints // Output : int //----------------------------------------------------------------------------- -int CAI_Network::GetNumHints(void) const +int CAI_Network::NumHints(void) const { return m_iNumHints; } @@ -81,16 +83,16 @@ int CAI_Network::GetNumHints(void) const // Purpose: gets the number of script nodes // Output : int //----------------------------------------------------------------------------- -int CAI_Network::GetNumScriptNodes(void) const +int CAI_Network::NumScriptNodes(void) const { return m_iNumScriptNodes; } //----------------------------------------------------------------------------- // Purpose: gets the path nodes -// Output : int64_t +// Output : int //----------------------------------------------------------------------------- -int64_t CAI_Network::GetNumPathNodes(void) const +int CAI_Network::NumPathNodes(void) const { return m_iNumNodes; } @@ -115,21 +117,46 @@ CAI_ScriptNode* CAI_Network::GetScriptNodes(void) const } //----------------------------------------------------------------------------- -// Purpose: gets the pointer to path nodes +// Purpose: gets the pointer to path node +// Input : id - // Output : CAI_Node** //----------------------------------------------------------------------------- -CAI_Node** CAI_Network::GetPathNodes(void) const +CAI_Node* CAI_Network::GetPathNode(int id) const { - return m_pAInode; + if (id >= 0 && + id < m_iNumNodes) + { + return m_pAInode[id]; + } + + Assert(0); + return NULL; } //----------------------------------------------------------------------------- -void VAI_Network::Attach() const +// Purpose: adds a path node +// Input : *origin - +// jaw - +// Output : CAI_Node* +//----------------------------------------------------------------------------- +CAI_Node* CAI_Network::AddPathNode(const Vector3D* origin, const float jaw) { - DetourAttach(&v_CAI_Network__DebugConnectMsg, &CAI_Network::DebugConnectMsg); + return CAI_Network__AddPathNode(this, origin, jaw); } -void VAI_Network::Detach() const +//----------------------------------------------------------------------------- +// Purpose: creates a node link +// Input : srcID - +// destID - +// Output : CAI_NodeLink* +//----------------------------------------------------------------------------- +CAI_NodeLink* CAI_Network::CreateNodeLink(int srcID, int destID) { - DetourDetach(&v_CAI_Network__DebugConnectMsg, &CAI_Network::DebugConnectMsg); + return CAI_Network__CreateNodeLink(this, srcID, destID); +} + +//----------------------------------------------------------------------------- +void VAI_Network::Detour(const bool bAttach) const +{ + DetourSetup(&CAI_Network__DebugConnectMsg, &CAI_Network::DebugConnectMsg, bAttach); } diff --git a/r5dev/game/server/ai_network.h b/r5dev/game/server/ai_network.h index 51d25919..ee71feb4 100644 --- a/r5dev/game/server/ai_network.h +++ b/r5dev/game/server/ai_network.h @@ -11,23 +11,33 @@ class CAI_Network public: static void DebugConnectMsg(int node1, int node2, const char* pszFormat, ...); void* GetVTable(void) const; - int GetNumLinks(void) const; - int GetNumZones(void) const; - int GetNumHints(void) const; - int GetNumScriptNodes(void) const; - int64_t GetNumPathNodes(void) const; + int NumLinks(void) const; + int NumZones(void) const; + int NumHints(void) const; + int NumScriptNodes(void) const; + int NumPathNodes(void) const; short GetHint(int nIndex) const; CAI_ScriptNode* GetScriptNodes(void) const; - CAI_Node** GetPathNodes(void) const; + + CAI_Node* AddPathNode(const Vector3D* origin, const float jaw); + CAI_Node* GetPathNode(int id) const; + + CAI_NodeLink* CreateNodeLink(int srcID, int destID); public: void* m_pVTable; // <-- 'this'. int m_iNumLinks; // +0x0008 - char unk1[0x7C]; // +0x000C + int m_nUnk0; + + CAI_HullData m_HullData[MAX_HULLS]; + int m_iNumZones; // +0x0088 - char unk2[0x10]; // +0x008C + int m_iUnkCount0; + int m_iUnkCount1; + int m_iUnkCount2; + int m_iUnkCount4; // unk8 on disk int unk5; // +0x009C @@ -45,28 +55,31 @@ public: }; inline CAI_Network** g_pAINetwork = nullptr; -inline CMemory p_CAI_Network__DebugConnectMsg; -inline void(*v_CAI_Network__DebugConnectMsg)(int node1, int node2, const char* pszformat, ...); +inline CAI_Node*(*CAI_Network__AddPathNode)(CAI_Network* pNetwork, const Vector3D* origin, float yaw); +inline CAI_NodeLink* (*CAI_Network__CreateNodeLink)(CAI_Network* pNetwork, int srcID, int destID); +inline void(*CAI_Network__DebugConnectMsg)(int node1, int node2, const char* pszformat, ...); /////////////////////////////////////////////////////////////////////////////// class VAI_Network : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CAI_Network::DebugConnectMsg", p_CAI_Network__DebugConnectMsg.GetPtr()); - LogVarAdr("g_pAINetwork", reinterpret_cast(g_pAINetwork)); + LogFunAdr("CAI_Network::AddPathNode", CAI_Network__AddPathNode); + LogFunAdr("CAI_Network::CreateNodeLink", CAI_Network__CreateNodeLink); + LogFunAdr("CAI_Network::DebugConnectMsg", CAI_Network__DebugConnectMsg); + LogVarAdr("g_pAINetwork", g_pAINetwork); } virtual void GetFun(void) const { - p_CAI_Network__DebugConnectMsg = g_GameDll.FindPatternSIMD("4C 89 4C 24 ?? 48 83 EC 18"); - v_CAI_Network__DebugConnectMsg = p_CAI_Network__DebugConnectMsg.RCast(); /*4C 89 4C 24 ?? 48 83 EC 18*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 48 8B B9 ?? ?? ?? ?? 48 8B F2 0F 29 74 24 ??").GetPtr(CAI_Network__AddPathNode); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 57 41 56 48 83 EC 20 49 63 E8").GetPtr(CAI_Network__CreateNodeLink); + g_GameDll.FindPatternSIMD("4C 89 4C 24 ?? 48 83 EC 18").GetPtr(CAI_Network__DebugConnectMsg); } virtual void GetVar(void) const { g_pAINetwork = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 4C 63 91 ?? ?? ?? ??").FindPatternSelf("48 8B").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/ai_networkmanager.cpp b/r5dev/game/server/ai_networkmanager.cpp index 3591d0a2..4b8f6abc 100644 --- a/r5dev/game/server/ai_networkmanager.cpp +++ b/r5dev/game/server/ai_networkmanager.cpp @@ -14,13 +14,17 @@ #include "game/server/ai_node.h" #include "game/server/ai_network.h" #include "game/server/ai_networkmanager.h" +#include constexpr int AINET_SCRIPT_VERSION_NUMBER = 21; constexpr int AINET_VERSION_NUMBER = 57; -constexpr int AINET_MIN_FILE_SIZE = 82; +constexpr int AINET_MINIMUM_SIZE = 82; // The file is at least this large when all required fields are written constexpr const char* AINETWORK_EXT = ".ain"; constexpr const char* AINETWORK_PATH = "maps/graphs/"; + +static ConVar ai_ainDumpOnLoad("ai_ainDumpOnLoad", "0", FCVAR_DEVELOPMENTONLY, "Dumps AIN data from node graphs loaded from the disk on load"); + /* ============================== CAI_NetworkBuilder::BuildFile @@ -42,13 +46,14 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork) CFastTimer timer; // Build from memory. - DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); - DevMsg(eDLL_T::SERVER, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AI NETWORK GRAPH FILE CONSTRUCTION STARTED <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); + Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); + Msg(eDLL_T::SERVER, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> AI NETWORK GRAPH FILE CONSTRUCTION STARTED <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); masterTimer.Start(); timer.Start(); + FileSystem()->CreateDirHierarchy(AINETWORK_PATH, "GAME"); FileHandle_t pAIGraph = FileSystem()->Open(szGraphPath, "wb", "GAME"); if (!pAIGraph) { @@ -57,240 +62,346 @@ void CAI_NetworkBuilder::SaveNetworkGraph(CAI_Network* pNetwork) } DevMsg(eDLL_T::SERVER, "+- Writing header...\n"); - DevMsg(eDLL_T::SERVER, " |-- AINet version: '%d'\n", AINET_VERSION_NUMBER); - FileSystem()->Write(&AINET_VERSION_NUMBER, sizeof(int), pAIGraph); - DevMsg(eDLL_T::SERVER, " |-- Map version: '%d'\n", g_ServerGlobalVariables->m_nMapVersion); - FileSystem()->Write(&g_ServerGlobalVariables->m_nMapVersion, sizeof(int), pAIGraph); - - FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME"); - uint32_t nNavMeshHash = NULL; - - if (!pNavMesh) - { - Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n", __FUNCTION__, S_HULL_TYPE[E_HULL_TYPE::LARGE]); - } - else - { - uint32_t nLen = FileSystem()->Size(pNavMesh); - std::unique_ptr pBuf(new uint8_t[nLen]); - - FileSystem()->Read(pBuf.get(), nLen, pNavMesh); - FileSystem()->Close(pNavMesh); - - nNavMeshHash = crc32::update(NULL, pBuf.get(), nLen); - } + // Must be computed at this point. + Assert((*g_ppAINetworkManager)->IsRuntimeCRCCalculated()); // Large NavMesh CRC. - DevMsg(eDLL_T::SERVER, " |-- NavMesh CRC: '0x%lX'\n", nNavMeshHash); - FileSystem()->Write(&nNavMeshHash, sizeof(uint32_t), pAIGraph); + DevMsg(eDLL_T::SERVER, " |-- AINet version: '%d'\n", AINET_VERSION_NUMBER); + DevMsg(eDLL_T::SERVER, " |-- Map version: '%d'\n", g_ServerGlobalVariables->m_nMapVersion); + DevMsg(eDLL_T::SERVER, " |-- Runtime CRC: '0x%lX'\n", (*g_ppAINetworkManager)->GetRuntimeCRC()); - // Path nodes. - DevMsg(eDLL_T::SERVER, " |-- Node count: '%d'\n", pNetwork->m_iNumNodes); - FileSystem()->Write(&pNetwork->m_iNumNodes, sizeof(int), pAIGraph); + CUtlBuffer buf; + + // --------------------------- + // Save the version numbers + // --------------------------- + buf.PutInt(AINET_VERSION_NUMBER); + buf.PutInt(g_ServerGlobalVariables->m_nMapVersion); + buf.PutInt((*g_ppAINetworkManager)->GetRuntimeCRC()); timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing header. %lf seconds\n", timer.GetDuration().GetSeconds()); + Msg(eDLL_T::SERVER, "...done writing header. %lf seconds\n", timer.GetDuration().GetSeconds()); timer.Start(); - DevMsg(eDLL_T::SERVER, "+- Writing node positions...\n"); + DevMsg(eDLL_T::SERVER, "+- Writing path nodes...\n"); - int nCalculatedLinkcount = 0; + // ------------------------------- + // Dump all the nodes to the file + // ------------------------------- + buf.PutInt(pNetwork->NumPathNodes()); + int totalNumLinks = 0; - if (pNetwork->m_pAInode) + for (int node = 0; node < pNetwork->NumPathNodes(); node++) { - for (int i = 0; i < pNetwork->m_iNumNodes; i++) + const CAI_Node* aiNode = pNetwork->GetPathNode(node); + + DevMsg(eDLL_T::SERVER, " |-- Writing node '#%d' at '0x%zX'\n", aiNode->m_iID, buf.TellPut()); + + buf.Put(&aiNode->m_vOrigin, sizeof(Vector3D)); + buf.PutFloat(aiNode->GetYaw()); + buf.Put(&aiNode->m_flVOffset, sizeof(aiNode->m_flVOffset)); + buf.PutChar((char)aiNode->GetType()); + buf.PutInt(aiNode->GetInfo()); + + for (int j = 0; j < MAX_HULLS; j++) { - // Construct on-disk node struct. - CAI_NodeDisk diskNode{}; - diskNode.m_vOrigin.x = pNetwork->m_pAInode[i]->m_vOrigin.x; - diskNode.m_vOrigin.y = pNetwork->m_pAInode[i]->m_vOrigin.y; - diskNode.m_vOrigin.z = pNetwork->m_pAInode[i]->m_vOrigin.z; - diskNode.m_flYaw = pNetwork->m_pAInode[i]->m_flYaw; - memcpy(diskNode.hulls, pNetwork->m_pAInode[i]->m_fHulls, sizeof(diskNode.hulls)); - diskNode.unk0 = static_cast(pNetwork->m_pAInode[i]->unk0); - diskNode.unk1 = pNetwork->m_pAInode[i]->unk1; - - for (int j = 0; j < MAX_HULLS; j++) - { - diskNode.unk2[j] = static_cast(pNetwork->m_pAInode[i]->unk2[j]); - } - - memcpy(diskNode.unk3, pNetwork->m_pAInode[i]->unk3, sizeof(diskNode.unk3)); - diskNode.unk4 = pNetwork->m_pAInode[i]->unk6; - diskNode.unk5 = -1; // aiNetwork->nodes[i]->unk8; // This field is wrong, however it's always -1 in original navmeshes anyway. - memcpy(diskNode.unk6, pNetwork->m_pAInode[i]->unk10, sizeof(diskNode.unk6)); - - - DevMsg(eDLL_T::SERVER, " |-- Copying node '#%d' from '0x%p' to '0x%zX'\n", pNetwork->m_pAInode[i]->m_nIndex, reinterpret_cast(pNetwork->m_pAInode[i]), FileSystem()->Tell(pAIGraph)); - FileSystem()->Write(&diskNode, sizeof(CAI_NodeDisk), pAIGraph); - - nCalculatedLinkcount += pNetwork->m_pAInode[i]->m_nNumLinks; + buf.PutShort((short)aiNode->unk2[j]); } + + buf.Put(&aiNode->unk3, sizeof(aiNode->unk3)); + buf.PutShort(aiNode->unk6); + buf.PutShort(aiNode->unk9); // Always -1; + buf.Put(&aiNode->unk11, sizeof(aiNode->unk11)); + + totalNumLinks += aiNode->NumLinks(); } + + pNetwork->m_iNumLinks = totalNumLinks; + timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing node positions. %lf seconds\n", timer.GetDuration().GetSeconds()); + Msg(eDLL_T::SERVER, "...done writing path nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumNodes); timer.Start(); - DevMsg(eDLL_T::SERVER, "+- Writing links...\n"); + DevMsg(eDLL_T::SERVER, "+- Writing node links...\n"); + DevMsg(eDLL_T::SERVER, " |-- Cached node link count: '%d'\n", totalNumLinks); - DevMsg(eDLL_T::SERVER, " |-- Cached link count: '%d'\n", pNetwork->m_iNumLinks); - DevMsg(eDLL_T::SERVER, " |-- Calculated link count: '%d'\n", nCalculatedLinkcount); + // ------------------------------- + // Dump all the links to the file + // ------------------------------- + int packedLinks = totalNumLinks / 2; + buf.PutInt(packedLinks); - nCalculatedLinkcount /= 2; - if (ai_ainDumpOnLoad->GetBool()) + for (int node = 0; node < pNetwork->NumPathNodes(); node++) { - if (pNetwork->m_iNumLinks != nCalculatedLinkcount) - { - Warning(eDLL_T::SERVER, "%s - Calculated link count '%d' doesn't match cached link count '%d' (expected on build!)\n", __FUNCTION__, nCalculatedLinkcount, pNetwork->m_iNumLinks); - } - } + const CAI_Node* aiNode = pNetwork->GetPathNode(node); - FileSystem()->Write(&nCalculatedLinkcount, sizeof(int), pAIGraph); - - if (pNetwork->m_pAInode) - { - for (int i = 0; i < pNetwork->m_iNumNodes; i++) + for (int link = 0; link < aiNode->NumLinks(); link++) { - for (int j = 0; j < pNetwork->m_pAInode[i]->m_nNumLinks; j++) + const CAI_NodeLink* nodeLink = aiNode->GetLinkByIndex(link); + + // Skip links that don't originate from current node. + if (nodeLink->m_iSrcID == aiNode->m_iID) { - // Skip links that don't originate from current node. - if (pNetwork->m_pAInode[i]->links[j]->m_iSrcID != pNetwork->m_pAInode[i]->m_nIndex) - { - continue; - } + DevMsg(eDLL_T::SERVER, " |-- Writing link (%hd <--> %hd) at '0x%zX'\n", nodeLink->m_iSrcID, nodeLink->m_iDestID, buf.TellPut()); - CAI_NodeLinkDisk diskLink{}; - diskLink.m_iSrcID = pNetwork->m_pAInode[i]->links[j]->m_iSrcID; - diskLink.m_iDestID = pNetwork->m_pAInode[i]->links[j]->m_iDestID; - diskLink.unk0 = pNetwork->m_pAInode[i]->links[j]->unk1; - memcpy(diskLink.m_bHulls, pNetwork->m_pAInode[i]->links[j]->m_bHulls, sizeof(diskLink.m_bHulls)); + buf.PutShort(nodeLink->m_iSrcID); + buf.PutShort(nodeLink->m_iDestID); - DevMsg(eDLL_T::SERVER, " |-- Writing link '%h' => '%h' to '0x%zX'\n", diskLink.m_iSrcID, diskLink.m_iDestID, FileSystem()->Tell(pAIGraph)); - FileSystem()->Write(&diskLink, sizeof(CAI_NodeLinkDisk), pAIGraph); + buf.PutChar(nodeLink->unk1); + buf.Put(nodeLink->m_iAcceptedMoveTypes, sizeof(nodeLink->m_iAcceptedMoveTypes)); } } } timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing links. %lf seconds (%d links)\n", timer.GetDuration().GetSeconds(), nCalculatedLinkcount); + Msg(eDLL_T::SERVER, "...done writing node links. %lf seconds (%d links)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumLinks); timer.Start(); - DevMsg(eDLL_T::SERVER, "+- Writing hull data...\n"); - // Don't know what this is, it's likely a block from tf1 that got deprecated? should just be 1 int per node. - DevMsg(eDLL_T::SERVER, " |-- Writing '%d' bytes for node block at '0x%zX'\n", pNetwork->m_iNumNodes * sizeof(uint32_t), FileSystem()->Tell(pAIGraph)); + DevMsg(eDLL_T::SERVER, "+- Writing wc lookup table...\n"); - if (pNetwork->m_iNumNodes > 0) + // ------------------------------- + // Dump the WC lookup table + // ------------------------------- + CUtlMap wcIDs; + SetDefLessFunc(wcIDs); + bool bCheckForProblems = false; + + const CAI_NetworkEditTools* const pEditOps = (*g_ppAINetworkManager)->GetEditOps(); + + for (int node = 0; node < pNetwork->m_iNumNodes; node++) { - std::unique_ptr unkNodeBlock(new uint32_t[pNetwork->m_iNumNodes * sizeof(uint32_t)]); - memset(&unkNodeBlock, '\0', pNetwork->m_iNumNodes * sizeof(uint32_t)); + const int nIndex = pEditOps->m_pNodeIndexTable[node]; + const int iPreviousNodeBinding = wcIDs.Find(nIndex); - FileSystem()->Write(unkNodeBlock.get(), pNetwork->m_iNumNodes * sizeof(uint32_t), pAIGraph); - } + if (iPreviousNodeBinding != wcIDs.InvalidIndex()) + { + if (!bCheckForProblems) + { + DevWarning(eDLL_T::SERVER, "******* MAP CONTAINS DUPLICATE HAMMER NODE IDS! CHECK FOR PROBLEMS IN HAMMER TO CORRECT *******\n"); + bCheckForProblems = true; + } + DevWarning(eDLL_T::SERVER, " AI node %d is associated with Hammer node %d, but %d is already bound to node %d\n", + node, nIndex, nIndex, wcIDs[(unsigned short)nIndex]); + } + else + { + wcIDs.Insert(nIndex, node); + } - // TODO: This is traverse nodes i think? these aren't used in r2 ains so we can get away with just writing count=0 and skipping - // but ideally should actually dump these. - DevMsg(eDLL_T::SERVER, " |-- Writing '%d' traversal nodes at '0x%zX'\n", 0, FileSystem()->Tell(pAIGraph)); - short traverseNodeCount = 0; // Only write count since count=0 means we don't have to actually do anything here. - FileSystem()->Write(&traverseNodeCount, sizeof(short), pAIGraph); - - // TODO: Ideally these should be actually dumped, but they're always 0 in r2 from what i can tell. - DevMsg(eDLL_T::SERVER, " |-- Writing '%d' bytes for hull data block at '0x%zX'\n", (MAX_HULLS * 8), FileSystem()->Tell(pAIGraph)); - for (int i = 0; i < (MAX_HULLS * 8); i++) - { - FileSystem()->Write("\0", sizeof(char), pAIGraph); + DevMsg(eDLL_T::SERVER, " |-- Writing Hammer node (%d <--> %d) at '0x%zX'\n", nIndex, wcIDs.Element((unsigned short)nIndex), buf.TellPut()); + buf.PutInt(nIndex); } timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing hull data. %lf seconds\n", timer.GetDuration().GetSeconds()); + Msg(eDLL_T::SERVER, "...done writing wc lookup table. %lf seconds (%d indices)\n", timer.GetDuration().GetSeconds(), wcIDs.Count()); timer.Start(); - DevMsg(eDLL_T::SERVER, "+- Writing clusters...\n"); + DevMsg(eDLL_T::SERVER, "+- Writing traverse ex nodes...\n"); - FileSystem()->Write(&*g_nAiNodeClusters, sizeof(*g_nAiNodeClusters), pAIGraph); - for (int i = 0; i < *g_nAiNodeClusters; i++) + // ------------------------------- + // Dump the traverse ex nodes + // ------------------------------- + const int traverseExNodeCount = g_pAITraverseNodes->Count(); + buf.PutShort((short)traverseExNodeCount); + + FOR_EACH_VEC(*g_pAITraverseNodes, i) { - DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph)); - AINodeClusters* nodeClusters = (*g_pppAiNodeClusters)[i]; + DevMsg(eDLL_T::SERVER, " |-- Writing traverse ex node '%d' at '0x%zX'\n", i, buf.TellPut()); - FileSystem()->Write(&nodeClusters->m_nIndex, sizeof(nodeClusters->m_nIndex), pAIGraph); - FileSystem()->Write(&nodeClusters->unk1, sizeof(nodeClusters->unk1), pAIGraph); - - FileSystem()->Write(&nodeClusters->m_vOrigin.x, sizeof(nodeClusters->m_vOrigin.x), pAIGraph); - FileSystem()->Write(&nodeClusters->m_vOrigin.y, sizeof(nodeClusters->m_vOrigin.y), pAIGraph); - FileSystem()->Write(&nodeClusters->m_vOrigin.z, sizeof(nodeClusters->m_vOrigin.z), pAIGraph); - - FileSystem()->Write(&nodeClusters->unkcount0, sizeof(nodeClusters->unkcount0), pAIGraph); - for (int j = 0; j < nodeClusters->unkcount0; j++) - { - short unk2Short = static_cast(nodeClusters->unk2[j]); - FileSystem()->Write(&unk2Short, sizeof(unk2Short), pAIGraph); - } - - FileSystem()->Write(&nodeClusters->unkcount1, sizeof(nodeClusters->unkcount1), pAIGraph); - for (int j = 0; j < nodeClusters->unkcount1; j++) - { - short unk3Short = static_cast(nodeClusters->unk3[j]); - FileSystem()->Write(&unk3Short, sizeof(unk3Short), pAIGraph); - } - - FileSystem()->Write(&nodeClusters->unk5, sizeof(nodeClusters->unk5), pAIGraph); + const CAI_TraverseNode& traverseExNode = (*g_pAITraverseNodes)[i]; + buf.Put(&traverseExNode.m_Quat, sizeof(Quaternion)); + buf.PutInt(traverseExNode.m_Index_MAYBE); } timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing clusters. %lf seconds (%d clusters)\n", timer.GetDuration().GetSeconds(), *g_nAiNodeClusters); + Msg(eDLL_T::SERVER, "...done writing traverse ex nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), traverseExNodeCount); + + + timer.Start(); + DevMsg(eDLL_T::SERVER, "+- Writing hull data blocks...\n"); + + // ------------------------------- + // Dump the hull data blocks + // ------------------------------- + + // Pointer to numZones counter, incremented up and until + // the last counter field for the hull data block. + int* countPtr = &pNetwork->m_iNumZones; + + for (int i = 0; i < MAX_HULLS; i++, countPtr++) + { + const CAI_HullData& hullData = pNetwork->m_HullData[i]; + const int bufferSize = sizeof(int) * hullData.unk1; + + buf.PutInt(*countPtr); + buf.PutShort(hullData.m_Count); + buf.PutShort(hullData.unk1); + buf.Put(hullData.pBuffer, bufferSize); + } + + timer.End(); + Msg(eDLL_T::SERVER, "...done writing hull data blocks. %lf seconds (%d blocks)\n", timer.GetDuration().GetSeconds(), MAX_HULLS); + + + timer.Start(); + DevMsg(eDLL_T::SERVER, "+- Writing path clusters...\n"); + + // ------------------------------- + // Dump the path clusters + // ------------------------------- + const int numClusters = g_pAIPathClusters->Count(); + buf.PutInt(numClusters); + + FOR_EACH_VEC(*g_pAIPathClusters, i) + { + DevMsg(eDLL_T::SERVER, " |-- Writing cluster '#%d' at '0x%zX'\n", i, buf.TellPut()); + + const CAI_Cluster* pathClusters = (*g_pAIPathClusters)[i]; + + buf.PutInt(pathClusters->m_nIndex); + buf.PutChar(pathClusters->unk1); + + buf.PutFloat(pathClusters->GetOrigin().x); + buf.PutFloat(pathClusters->GetOrigin().y); + buf.PutFloat(pathClusters->GetOrigin().z); + + const int unkVec0Size = pathClusters->unkVec0.Count(); + buf.PutInt(unkVec0Size); + + FOR_EACH_VEC(pathClusters->unkVec0, j) + { + short unkShort = static_cast(pathClusters->unkVec0[j]); + buf.PutShort(unkShort); + } + + const int unkVec1Size = pathClusters->unkVec1.Count(); + buf.PutInt(unkVec1Size); + + FOR_EACH_VEC(pathClusters->unkVec1, j) + { + short unkShort = static_cast(pathClusters->unkVec1[j]); + buf.PutShort(unkShort); + } + + buf.PutChar(pathClusters->unk5); + } + + timer.End(); + Msg(eDLL_T::SERVER, "...done writing path clusters. %lf seconds (%d clusters)\n", timer.GetDuration().GetSeconds(), numClusters); timer.Start(); DevMsg(eDLL_T::SERVER, "+- Writing cluster links...\n"); - FileSystem()->Write(&*g_nAiNodeClusterLinks, sizeof(*g_nAiNodeClusterLinks), pAIGraph); - for (int i = 0; i < *g_nAiNodeClusterLinks; i++) + // ------------------------------- + // Dump the cluster links + // ------------------------------- + const int numClusterLinks = g_pAIClusterLinks->Count(); + buf.PutInt(numClusterLinks); + + FOR_EACH_VEC(*g_pAIClusterLinks, i) { // Disk and memory structs are literally identical here so just directly write. - DevMsg(eDLL_T::SERVER, " |-- Writing cluster link '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph)); - FileSystem()->Write(&*g_pppAiNodeClusterLinks[i], sizeof(*(*g_pppAiNodeClusterLinks)[i]), pAIGraph); + const CAI_ClusterLink* clusterLink = (*g_pAIClusterLinks)[i]; + + DevMsg(eDLL_T::SERVER, " |-- Writing link (%hd <--> %hd) at '0x%zX'\n", clusterLink->m_iSrcID, clusterLink->m_iDestID, buf.TellPut()); + + buf.PutShort(clusterLink->m_iSrcID); + buf.PutShort(clusterLink->m_iDestID); + + buf.PutInt(clusterLink->unk2); + buf.PutChar(clusterLink->flags); + + buf.PutChar(clusterLink->unkFlags4); + buf.PutChar(clusterLink->unkFlags5); } timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing cluster links. %lf seconds (%d cluster links)\n", timer.GetDuration().GetSeconds(), *g_nAiNodeClusterLinks); + Msg(eDLL_T::SERVER, "...done writing cluster links. %lf seconds (%d links)\n", timer.GetDuration().GetSeconds(), numClusterLinks); // This is always set to '-1'. Likely a field for maintaining compatibility. - FileSystem()->Write(&pNetwork->unk5, sizeof(pNetwork->unk5), pAIGraph); + buf.PutInt(pNetwork->unk5); // AIN v57 and above only (not present in r1, static array in r2, pointer to dynamic array in r5). timer.Start(); DevMsg(eDLL_T::SERVER, "+- Writing script nodes...\n"); - FileSystem()->Write(&pNetwork->m_iNumScriptNodes, sizeof(pNetwork->m_iNumScriptNodes), pAIGraph); - for (int i = 0; i < pNetwork->m_iNumScriptNodes; i++) + // ------------------------------- + // Dump all the script nodes + // ------------------------------- + const int numScriptNodes = pNetwork->m_iNumScriptNodes; + buf.PutInt(numScriptNodes); + + for (int node = 0; node < numScriptNodes; node++) { // Disk and memory structs for script nodes are identical. - DevMsg(eDLL_T::SERVER, " |-- Writing script node '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph)); - FileSystem()->Write(&pNetwork->m_ScriptNode[i], sizeof(CAI_ScriptNode), pAIGraph); + DevMsg(eDLL_T::SERVER, " |-- Writing script node '#%d' at '0x%zX'\n", node, buf.TellPut()); + buf.Put(&pNetwork->m_ScriptNode[node], sizeof(CAI_ScriptNode)); } timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing script nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumScriptNodes); + Msg(eDLL_T::SERVER, "...done writing script nodes. %lf seconds (%d nodes)\n", timer.GetDuration().GetSeconds(), numScriptNodes); timer.Start(); DevMsg(eDLL_T::SERVER, "+- Writing hint data...\n"); - FileSystem()->Write(&pNetwork->m_iNumHints, sizeof(pNetwork->m_iNumHints), pAIGraph); - for (int i = 0; i < pNetwork->m_iNumHints; i++) + // ------------------------------- + // Dump the hint data + // ------------------------------- + const int numHinst = pNetwork->m_iNumHints; + buf.PutInt(numHinst); + + for (int hint = 0; hint < numHinst; hint++) { - DevMsg(eDLL_T::SERVER, " |-- Writing hint data '#%d' at '0x%zX'\n", i, FileSystem()->Tell(pAIGraph)); - FileSystem()->Write(&pNetwork->m_Hints[i], sizeof(pNetwork->m_Hints[i]), pAIGraph); + DevMsg(eDLL_T::SERVER, " |-- Writing hint data '#%d' at '0x%zX'\n", hint, buf.TellPut()); + buf.PutShort(pNetwork->m_Hints[hint]); } timer.End(); - DevMsg(eDLL_T::SERVER, "...done writing hint data. %lf seconds (%d hints)\n", timer.GetDuration().GetSeconds(), pNetwork->m_iNumHints); + Msg(eDLL_T::SERVER, "...done writing hint data. %lf seconds (%d hints)\n", timer.GetDuration().GetSeconds(), numHinst); + timer.Start(); + DevMsg(eDLL_T::SERVER, "+- Calculating navmesh crc...\n"); + + // ------------------------------- + // Dump NavMesh CRC + // ------------------------------- + FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME"); + uint32_t nNavMeshCRC = NULL; + + if (!pNavMesh) + { + Warning(eDLL_T::SERVER, "%s - No %s NavMesh found. Unable to calculate CRC for AI Network\n", + __FUNCTION__, S_HULL_TYPE[E_HULL_TYPE::LARGE]); + } + else + { + const ssize_t nLen = FileSystem()->Size(pNavMesh); + std::unique_ptr pBuf(new uint8_t[nLen]); + + FileSystem()->Read(pBuf.get(), nLen, pNavMesh); + FileSystem()->Close(pNavMesh); + + nNavMeshCRC = crc32::update(NULL, pBuf.get(), nLen); + } + + // Note: the NavMesh checksum is written at the END of the file + // to maintain compatibility with r1 and r2 AIN's. + DevMsg(eDLL_T::SERVER, " |-- Writing navmesh crc '%x' at '0x%zX'\n", nNavMeshCRC, buf.TellPut()); + buf.PutInt(nNavMeshCRC); + + timer.End(); + Msg(eDLL_T::SERVER, "...done calculating navmesh crc. %lf seconds (%x)\n", timer.GetDuration().GetSeconds(), nNavMeshCRC); + + // Write the entire buffer to the disk. + FileSystem()->Write(buf.Base(), buf.TellPut(), pAIGraph); FileSystem()->Close(pAIGraph); masterTimer.End(); - DevMsg(eDLL_T::SERVER, "...done writing AI node graph. %lf seconds\n", masterTimer.GetDuration().GetSeconds()); - DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); - DevMsg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); + + Msg(eDLL_T::SERVER, "...done writing AI node graph. %lf seconds\n", masterTimer.GetDuration().GetSeconds()); + Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); + Msg(eDLL_T::SERVER, "++++--------------------------------------------------------------------------------------------------------------------------++++\n"); } /* @@ -301,7 +412,7 @@ CAI_NetworkManager::LoadNetworkGraph and validate status ============================== */ -void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile) +void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pManager, CUtlBuffer* pBuffer, const char* szAIGraphFile) { bool bNavMeshAvailable = true; @@ -314,8 +425,10 @@ void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, int nAiNetVersion = NULL; int nAiMapVersion = NULL; - uint32_t nAiGraphHash = NULL; - uint32_t nNavMeshHash = NULL; + uint32_t nAiGraphCRC = NULL; // AIN CRC from AIN file. + uint32_t nAiNavMeshCRC = NULL; // NavMesh CRC from AIN file. + uint32_t nNavMeshCRC = NULL; // NavMesh CRC from local NM file. + uint32_t nAiRuntimeCRC = pManager->GetRuntimeCRC(); FileHandle_t pNavMesh = FileSystem()->Open(szMeshPath, "rb", "GAME"); if (!pNavMesh) @@ -325,53 +438,78 @@ void CAI_NetworkManager::LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, } else { - uint32_t nLen = FileSystem()->Size(pNavMesh); + const ssize_t nLen = FileSystem()->Size(pNavMesh); std::unique_ptr pBuf(new uint8_t[nLen]); FileSystem()->Read(pBuf.get(), nLen, pNavMesh); FileSystem()->Close(pNavMesh); - nNavMeshHash = crc32::update(NULL, pBuf.get(), nLen); + nNavMeshCRC = crc32::update(NULL, pBuf.get(), nLen); } - FileHandle_t pAIGraph = FileSystem()->Open(szGraphPath, "rb", "GAME"); - if (!pAIGraph) + const ssize_t nFileSize = pBuffer->TellPut(); + const ssize_t nOldOffset = pBuffer->TellGet(); + + // Seek to the start of the buffer so we can validate the header. + pBuffer->SeekGet(CUtlBuffer::SEEK_HEAD, 0); + + // If we have a NavMesh, then the minimum size is + // 'AINET_MINIMUM_SIZE' + CRC32 as the AIN needs + // a NavMesh checksum field for validation. + const int nMinimumFileSize = bNavMeshAvailable + ? AINET_MINIMUM_SIZE + sizeof(nNavMeshCRC) + : AINET_MINIMUM_SIZE; + + if (nFileSize >= nMinimumFileSize) { - Error(eDLL_T::SERVER, NO_ERROR, "%s - Unable to open '%s' (insufficient rights?)\n", __FUNCTION__, szGraphPath); - LoadNetworkGraphEx(pAINetworkManager, pBuffer, szAIGraphFile); - - return; - } - - if (FileSystem()->Size(pAIGraph) >= AINET_MIN_FILE_SIZE) - { - FileSystem()->Read(&nAiNetVersion, sizeof(int), pAIGraph); - FileSystem()->Read(&nAiMapVersion, sizeof(int), pAIGraph); - FileSystem()->Read(&nAiGraphHash, sizeof(int), pAIGraph); + nAiNetVersion = pBuffer->GetInt(); + nAiMapVersion = pBuffer->GetInt(); + nAiGraphCRC = pBuffer->GetInt(); + // Too old; build with a different game??? if (nAiNetVersion > AINET_VERSION_NUMBER) { Warning(eDLL_T::SERVER, "AI node graph '%s' is unsupported (net version: '%d' expected: '%d')\n", szGraphPath, nAiNetVersion, AINET_VERSION_NUMBER); } + // AIN file was build with a different version of the map, therefore, + // the path node positions might be invalid. else if (nAiMapVersion != g_ServerGlobalVariables->m_nMapVersion) { Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (map version: '%d' expected: '%d')\n", szGraphPath, nAiMapVersion, g_ServerGlobalVariables->m_nMapVersion); } - else if (bNavMeshAvailable && nAiGraphHash != nNavMeshHash) + // Data checksum is now what the runtime expects. + else if (nAiGraphCRC != nAiRuntimeCRC) { - Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (checksum: '%x' expected: '%x')\n", - szGraphPath, nAiGraphHash, nNavMeshHash); + Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (ain checksum: '%x' expected: '%x')\n", + szGraphPath, nAiGraphCRC, nAiRuntimeCRC); + } + else if (bNavMeshAvailable) + { + // Seek to the end of the file, minus the size of the CRC field. + // The NavMesh CRC is written at the end of the file to maintain + // compatibility with r1 and r2 AIN files. + pBuffer->SeekGet(CUtlBuffer::SEEK_HEAD, nFileSize - sizeof(nAiNavMeshCRC)); + nAiNavMeshCRC = pBuffer->GetInt(); + + // The AIN file was build with a different NavMesh, therefore, + // the script node positions might be incorrect. + if (nAiNavMeshCRC != nNavMeshCRC) + { + Warning(eDLL_T::SERVER, "AI node graph '%s' is out of date (nav checksum: '%x' expected: '%x')\n", + szGraphPath, nAiGraphCRC, nNavMeshCRC); + } } } else { - Error(eDLL_T::SERVER, NO_ERROR, "%s - AI node graph '%s' is corrupt\n", __FUNCTION__, szGraphPath); + Error(eDLL_T::SERVER, NO_ERROR, "%s - AI node graph '%s' appears truncated\n", __FUNCTION__, szGraphPath); } - FileSystem()->Close(pAIGraph); - LoadNetworkGraphEx(pAINetworkManager, pBuffer, szAIGraphFile); + // Recover old buffer position before we call LoadNetworkGraph. + pBuffer->SeekGet(CUtlBuffer::SEEK_HEAD, nOldOffset); + LoadNetworkGraphEx(pManager, pBuffer, szAIGraphFile); } /* @@ -382,18 +520,14 @@ CAI_NetworkManager::LoadNetworkGraphEx (internal) ============================== */ -void CAI_NetworkManager::LoadNetworkGraphEx(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile) +void CAI_NetworkManager::LoadNetworkGraphEx(CAI_NetworkManager* pManager, CUtlBuffer* pBuffer, const char* szAIGraphFile) { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - CAI_NetworkManager__LoadNetworkGraph(pAINetworkManager, pBuffer, szAIGraphFile, NULL); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - CAI_NetworkManager__LoadNetworkGraph(pAINetworkManager, pBuffer, szAIGraphFile); -#endif + CAI_NetworkManager__LoadNetworkGraph(pManager, pBuffer, szAIGraphFile); - if (ai_ainDumpOnLoad->GetBool()) + if (ai_ainDumpOnLoad.GetBool()) { - DevMsg(eDLL_T::SERVER, "Reparsing AI Network '%s'\n", szAIGraphFile); - CAI_NetworkBuilder::SaveNetworkGraph(*(CAI_Network**)(reinterpret_cast(pAINetworkManager) + AINETWORK_OFFSET)); + Msg(eDLL_T::SERVER, "Dumping AI Network '%s'\n", szAIGraphFile); + CAI_NetworkBuilder::SaveNetworkGraph(pManager->m_pNetwork); } } @@ -405,20 +539,14 @@ CAI_NetworkBuilder::Build during level load ============================== */ -void CAI_NetworkBuilder::Build(CAI_NetworkBuilder* pBuilder, CAI_Network* pAINetwork, void* a3, int a4) +void CAI_NetworkBuilder::Build(CAI_NetworkBuilder* pBuilder, CAI_Network* pAINetwork) { - CAI_NetworkBuilder__Build(pBuilder, pAINetwork, a3, a4); + CAI_NetworkBuilder__Build(pBuilder, pAINetwork); CAI_NetworkBuilder::SaveNetworkGraph(pAINetwork); } -void VAI_NetworkManager::Attach() const +void VAI_NetworkManager::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &CAI_NetworkManager::LoadNetworkGraph); - DetourAttach((LPVOID*)&CAI_NetworkBuilder__Build, &CAI_NetworkBuilder::Build); -} - -void VAI_NetworkManager::Detach() const -{ - DetourDetach((LPVOID*)&CAI_NetworkManager__LoadNetworkGraph, &CAI_NetworkManager::LoadNetworkGraph); - DetourDetach((LPVOID*)&CAI_NetworkBuilder__Build, &CAI_NetworkBuilder::Build); + DetourSetup(&CAI_NetworkManager__LoadNetworkGraph, &CAI_NetworkManager::LoadNetworkGraph, bAttach); + DetourSetup(&CAI_NetworkBuilder__Build, &CAI_NetworkBuilder::Build, bAttach); } diff --git a/r5dev/game/server/ai_networkmanager.h b/r5dev/game/server/ai_networkmanager.h index 214464c9..d2890078 100644 --- a/r5dev/game/server/ai_networkmanager.h +++ b/r5dev/game/server/ai_networkmanager.h @@ -6,34 +6,63 @@ #pragma once #include "game/server/ai_network.h" #include "game/server/detour_impl.h" +#include "baseentity.h" -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -const int AINETWORK_OFFSET = 2808; -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) -const int AINETWORK_OFFSET = 2840; -#endif +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CAI_NetworkBuilder; +class CAI_NetworkManager; /* ==== CAI_NETWORKMANAGER ============================================================================================================================================== */ -inline CMemory p_CAI_NetworkManager__ShouldRebuild = nullptr; -inline void*(*CAI_NetworkManager__ShouldRebuild)(void* thisptr, CAI_Network* pNetwork, void* a3, int a4); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -inline CMemory p_CAI_NetworkManager__LoadNetworkGraph = nullptr; -inline void*(*CAI_NetworkManager__LoadNetworkGraph)(void* thisptr, void* pBuffer, const char* pszFileName, int a4); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) -inline CMemory p_CAI_NetworkManager__LoadNetworkGraph = nullptr; -inline void*(*CAI_NetworkManager__LoadNetworkGraph)(void* thisptr, void* pBuffer, const char* pszFileName); -#endif -/* ==== CAI_NETWORKBUILDER ============================================================================================================================================== */ -inline CMemory p_CAI_NetworkBuilder__Build; -inline void*(*CAI_NetworkBuilder__Build)(void* thisptr, CAI_Network* pNetwork, void* a3, int a4); +inline void (*CAI_NetworkManager__InitializeAINetworks)(void); // Static +inline void (*CAI_NetworkManager__DelayedInit)(CAI_NetworkManager* thisptr, CAI_Network* pNetwork); +inline void (*CAI_NetworkManager__LoadNetworkGraph)(CAI_NetworkManager* thisptr, CUtlBuffer* pBuffer, const char* pszFileName); -inline int * g_nAiNodeClusters = nullptr; -inline AINodeClusters *** g_pppAiNodeClusters = nullptr; -inline int * g_nAiNodeClusterLinks = nullptr; -inline AINodeClusterLinks*** g_pppAiNodeClusterLinks = nullptr; +/* ==== CAI_NETWORKBUILDER ============================================================================================================================================== */ +inline void (*CAI_NetworkBuilder__Build)(CAI_NetworkBuilder* thisptr, CAI_Network* pNetwork); + +inline CAI_NetworkManager** g_ppAINetworkManager = nullptr; + +inline CUtlVector* g_pAIPathClusters = nullptr; +inline CUtlVector* g_pAIClusterLinks = nullptr; +inline CUtlVector* g_pAITraverseNodes = nullptr; + +//----------------------------------------------------------------------------- +// CAI_NetworkEditTools +// +// Purpose: Bridge class to Hammer node editing functionality +// +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +class CAI_NetworkEditTools +{ +public: + //----------------- + // WC Editing + //----------------- + int m_nNextWCIndex; + Vector3D* m_pWCPosition; + + //----------------- + // Debugging Tools + //----------------- + int m_debugNetOverlays; + int* m_pNodeIndexTable; + + //----------------- + // Network pointers + //----------------- + CAI_NetworkManager* m_pManager; + CAI_Network* m_pNetwork; +}; //----------------------------------------------------------------------------- // CAI_NetworkBuilder +// +// Purpose: Wrapper class for building and saving network graphs // //----------------------------------------------------------------------------- @@ -42,22 +71,50 @@ inline AINodeClusterLinks*** g_pppAiNodeClusterLinks = nullptr; class CAI_NetworkBuilder { public: - static void Build(CAI_NetworkBuilder* pBuilder, CAI_Network* pAINetwork, void* a3, int a4); + static void Build(CAI_NetworkBuilder* pBuilder, CAI_Network* pAINetwork); static void SaveNetworkGraph(CAI_Network* pNetwork); }; //----------------------------------------------------------------------------- // CAI_NetworkManager +// +// Purpose: The entity in the level responsible for building the network if it +// isn't there, saving & loading of the network, and holding the +// CAI_Network instance. // //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -class CAI_NetworkManager +class CAI_NetworkManager : public CBaseEntity { public: - static void LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile); - static void LoadNetworkGraphEx(CAI_NetworkManager* pAINetworkManager, void* pBuffer, const char* szAIGraphFile); + static void LoadNetworkGraph(CAI_NetworkManager* pAINetworkManager, CUtlBuffer* pBuffer, const char* szAIGraphFile); + static void LoadNetworkGraphEx(CAI_NetworkManager* pAINetworkManager, CUtlBuffer* pBuffer, const char* szAIGraphFile); + + CAI_NetworkEditTools* GetEditOps() { return m_pEditOps; } + CAI_Network* GetNetwork() { /*Assert(!m_ThreadedBuild.pBuildingNetwork);*/ return m_pNetwork; } + + inline uint32 GetFileCRC() const { m_ainMapFilesCRC; } + inline uint32 GetRuntimeCRC() const { return m_runtimeCreatedAINMapFilesCRC; } + inline bool IsRuntimeCRCCalculated() const { return m_calculatedRuntimeAINMapFilesCRC; } + +private: + // !TODO[ AMOS ]: If found, change to ptr and hook up to engine! + //static bool gm_fNetworksLoaded; // Have AINetworks been loaded + + void* _vftable; + CAI_NetworkEditTools* m_pEditOps; + CAI_Network* m_pNetwork; + bool m_fInitalized; + bool m_bDontSaveGraph; + char gap_b22[2]; + int m_ainVersion; + uint32 m_ainMapFilesCRC; + uint32 m_runtimeCreatedAINMapFilesCRC; + bool m_calculatedRuntimeAINMapFilesCRC; + char gap_b31[7]; + /*ThreadedGraphBuildData*/ char m_ThreadedBuild[72]; }; /////////////////////////////////////////////////////////////////////////////// @@ -65,45 +122,34 @@ class VAI_NetworkManager : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CAI_NetworkManager::LoadNetworkGraph", p_CAI_NetworkManager__LoadNetworkGraph.GetPtr()); - LogFunAdr("CAI_NetworkManager::ShouldRebuild", p_CAI_NetworkManager__ShouldRebuild.GetPtr()); - LogFunAdr("CAI_NetworkBuilder::Build", p_CAI_NetworkBuilder__Build.GetPtr()); - LogVarAdr("g_nAiNodeClusters", reinterpret_cast(g_nAiNodeClusters)); - LogVarAdr("g_pAiNodeClusters", reinterpret_cast(g_pppAiNodeClusters)); - LogVarAdr("g_nAiNodeClusterLinks", reinterpret_cast(g_nAiNodeClusterLinks)); - LogVarAdr("g_pAiNodeClusterLinks", reinterpret_cast(g_pppAiNodeClusterLinks)); + LogFunAdr("CAI_NetworkManager::InitializeAINetworks", CAI_NetworkManager__InitializeAINetworks); + LogFunAdr("CAI_NetworkManager::LoadNetworkGraph", CAI_NetworkManager__LoadNetworkGraph); + LogFunAdr("CAI_NetworkManager::DelayedInit", CAI_NetworkManager__DelayedInit); + LogFunAdr("CAI_NetworkBuilder::Build", CAI_NetworkBuilder__Build); + LogVarAdr("g_pAINetworkManager", g_ppAINetworkManager); + LogVarAdr("g_AIPathClusters< CAI_Cluster* >", g_pAIPathClusters); + LogVarAdr("g_AIClusterLinks< CAI_ClusterLink* >", g_pAIClusterLinks); + LogVarAdr("g_AITraverseNodes< CAI_TraverseNode >", g_pAITraverseNodes); } virtual void GetFun(void) const { - p_CAI_NetworkManager__ShouldRebuild = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 8B 0D ?? ?? ?? ?? 8B 41 6C"); -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CAI_NetworkManager__LoadNetworkGraph = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 4C 24 ?? 55 53 57 41 54 41 55 41 56"); - CAI_NetworkManager__LoadNetworkGraph = p_CAI_NetworkManager__LoadNetworkGraph.RCast(); /*4C 89 44 24 ?? 48 89 4C 24 ?? 55 53 57 41 54 41 55 41 56*/ -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CAI_NetworkManager__LoadNetworkGraph = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B FA"); - CAI_NetworkManager__LoadNetworkGraph = p_CAI_NetworkManager__LoadNetworkGraph.RCast(); /*4C 89 44 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B FA*/ -#endif -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CAI_NetworkBuilder__Build = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 4C 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 30 48 63 BA ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CAI_NetworkBuilder__Build = g_GameDll.FindPatternSIMD("48 89 54 24 ?? 48 89 4C 24 ?? 53 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 38 8B B2 ?? ?? ?? ??"); -#endif - CAI_NetworkManager__ShouldRebuild = p_CAI_NetworkManager__ShouldRebuild.RCast(); /*40 53 48 83 EC 20 48 8B D9 48 8B 0D ?? ?? ?? ?? 8B 41 6C*/ - CAI_NetworkBuilder__Build = p_CAI_NetworkBuilder__Build.RCast(); /*48 89 54 24 ?? 48 89 4C 24 ?? 53 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 38 8B B2 ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 4C 89 74 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ??").GetPtr(CAI_NetworkManager__InitializeAINetworks); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 8B 0D ?? ?? ?? ?? 8B 41 6C").GetPtr(CAI_NetworkManager__DelayedInit); + g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B FA").GetPtr(CAI_NetworkManager__LoadNetworkGraph); + g_GameDll.FindPatternSIMD("48 89 54 24 ?? 48 89 4C 24 ?? 53 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 38 8B B2 ?? ?? ?? ??").GetPtr(CAI_NetworkBuilder__Build); } virtual void GetVar(void) const { - g_nAiNodeClusters = g_GameDll.FindPatternSIMD("4C 0F BF 12") - .FindPatternSelf("83 3D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x2, 0x7).RCast(); - g_pppAiNodeClusters = g_GameDll.FindPatternSIMD("F3 0F 10 52 ?? 4C 8B CA") - .FindPatternSelf("48 8B 35", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_nAiNodeClusterLinks = g_GameDll.FindPatternSIMD("49 FF C0 48 83 C2 04 4D 3B C2 7C D4") - .FindPatternSelf("8B 3D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - g_pppAiNodeClusterLinks = g_GameDll.FindPatternSIMD("F3 0F 10 52 ?? 4C 8B CA") - .FindPatternSelf("4C 8B 1D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_ppAINetworkManager = CMemory(CAI_NetworkManager__InitializeAINetworks).FindPattern("48 89 05", CMemory::Direction::DOWN) + .ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pAIPathClusters = g_GameDll.FindPatternSIMD("F3 0F 10 52 ?? 4C 8B CA") + .FindPatternSelf("48 8B 35", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast*>(); + g_pAIClusterLinks = g_GameDll.FindPatternSIMD("F3 0F 10 52 ?? 4C 8B CA") + .FindPatternSelf("4C 8B 1D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast*>(); + g_pAITraverseNodes = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 56 48 8B EC 48 81 EC ?? ?? ?? ??").OffsetSelf(0x2EF) + .FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast*>(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/ai_node.h b/r5dev/game/server/ai_node.h index cd041392..96a455ea 100644 --- a/r5dev/game/server/ai_node.h +++ b/r5dev/game/server/ai_node.h @@ -10,7 +10,19 @@ constexpr int MAX_HULLS = 5; constexpr int NOT_CACHED = -2; // Returned if data not in cache constexpr int NO_NODE = -1; // Returned when no node meets the qualification -#pragma pack(push, 1) +//========================================================= +// >> The type of node +//========================================================= +enum NodeType_e // !TODO: unconfirmed for r1/r2/r5. +{ + NODE_ANY, // Used to specify any type of node (for search) + NODE_DELETED, // Used in wc_edit mode to remove nodes during runtime + NODE_GROUND, + NODE_AIR, + NODE_CLIMB, + NODE_WATER +}; + //============================================================================= // >> CAI_NodeLink //============================================================================= @@ -18,36 +30,41 @@ struct CAI_NodeLink { short m_iSrcID; short m_iDestID; - bool m_bHulls[MAX_HULLS]; - char unk0; + byte m_iAcceptedMoveTypes[MAX_HULLS]; + byte m_LinkInfo; char unk1; // maps => unk0 on disk char unk2[5]; int64_t m_nFlags; }; -//============================================================================= -// >> CAI_NodeLinkDisk -//============================================================================= -struct CAI_NodeLinkDisk -{ - short m_iSrcID; - short m_iDestID; - char unk0; - bool m_bHulls[MAX_HULLS]; -}; - //============================================================================= // >> CAI_Node //============================================================================= -struct CAI_Node +class CAI_Node { - int m_nIndex; // Not present on disk - Vector3D m_vOrigin; - float m_fHulls[MAX_HULLS]; - float m_flYaw; +public: + const Vector3D& GetOrigin() const { return m_vOrigin; } + Vector3D& AccessOrigin() { return m_vOrigin; } + float GetYaw() const { return m_flYaw; } + + int NumLinks() const { return m_Links.Count(); } + void ClearLinks() { m_Links.Purge(); } + CAI_NodeLink* GetLinkByIndex(int i) const { return m_Links[i]; } + + NodeType_e SetType(NodeType_e type) { return (m_eNodeType = type); } + NodeType_e GetType() const { return m_eNodeType; } + + int SetInfo(int info) { return m_eNodeInfo = info; } + int GetInfo() const { return m_eNodeInfo; } + + int m_iID; // ID for this node + Vector3D m_vOrigin; // location of this node in space + float m_flVOffset[MAX_HULLS]; // vertical offset for each hull type, assuming ground node, 0 otherwise + float m_flYaw; // NPC on this node should face this yaw to face the hint, or climb a ladder + + NodeType_e m_eNodeType; // The type of node; always 2 in buildainfile. + int m_eNodeInfo; // bits that tell us more about this nodes - int unk0; // Always 2 in buildainfile, maps directly to unk0 in disk struct - int unk1; // Maps directly to unk1 in disk struct int unk2[MAX_HULLS]; // Maps directly to unk2 in disk struct, despite being ints rather than shorts // View server.dll+393672 for context @@ -55,35 +72,72 @@ struct CAI_Node char pad[3]; // Aligns next bytes float unk4[MAX_HULLS]; // I have no clue, calculated using some kind float function magic - CAI_NodeLink** links; - char unk5[16]; - int m_nNumLinks; - int unk11; // Bad name lmao + CUtlVector m_Links; short unk6; // Should match up to unk4 on disk char unk7[16]; // Padding until next bit - short unk8; // Should match up to unk5 on disk - char unk9[8]; // Padding until next bit - char unk10[8]; // Should match up to unk6 on disk + short unk8; + short unk9; // Should match up to unk5 on disk + char unk10[6]; // Padding until next bit + char unk11[8]; // Should match up to unk6 on disk }; //============================================================================= -// >> CAI_NodeDisk +// >> CAI_Cluster //============================================================================= -struct CAI_NodeDisk // The way CAI_Nodes are represented in on-disk ain files +class CAI_Cluster { - Vector3D m_vOrigin; - - float m_flYaw; - float hulls[MAX_HULLS]; +public: + const Vector3D& GetOrigin() const { return m_vOrigin; } + Vector3D& AccessOrigin() { return m_vOrigin; } + int m_nIndex; char unk0; - int unk1; - short unk2[MAX_HULLS]; - char unk3[MAX_HULLS]; - short unk4; - short unk5; - char unk6[8]; -}; // Total size of 68 bytes + char unk1; // Maps to unk1 on disk + + Vector3D m_vOrigin; + char unkC; // idk, might be a 4 bytes type or just padding. + + // These are utlvectors in engine, but its + // unknown what they do yet. + CUtlVector unkVec0; + CUtlVector unkVec1; + + // This is an array of floats that is indexed + // into by teamNum at [r5apex_ds + EC84DC]; + // Seems to be used along with the cvar: + // 'ai_path_dangerous_cluster_min_time'. + float clusterTime[MAX_TEAMS]; + + float field_0250; + float field_0254; + float field_0258; + char unk5; +}; +static_assert(sizeof(CAI_Cluster) == 608); + +//============================================================================= +// >> CAI_ClusterLink +//============================================================================= +struct CAI_ClusterLink +{ + short m_iSrcID; + short m_iDestID; + int unk2; + char flags; + char unkFlags4; + char unkFlags5; +}; +static_assert(sizeof(CAI_ClusterLink) == 12); + +//============================================================================= +// >> CAI_ScriptNode +//============================================================================= +struct CAI_TraverseNode +{ + Quaternion m_Quat; + int m_Index_MAYBE; +}; +static_assert(sizeof(CAI_TraverseNode) == 20); //============================================================================= // >> CAI_ScriptNode @@ -91,39 +145,21 @@ struct CAI_NodeDisk // The way CAI_Nodes are represented in on-disk ain files struct CAI_ScriptNode { Vector3D m_vOrigin; - uint64_t scriptdata; + + // Might be wrong; seems to be used for clamping. + // See [r5apex_ds + 0xF28A6E] + int m_nMin; + int m_nMax; }; -struct AINodeClusters +//============================================================================= +// >> CAI_ScriptNode +//============================================================================= +struct CAI_HullData { - int m_nIndex; - char unk0; - char unk1; // Maps to unk1 on disk - char pad0[2]; // Padding to +8 - - Vector3D m_vOrigin; - - char pad5[4]; - int* unk2; // Maps to unk5 on disk; - char pad1[16]; // Pad to +48 - int unkcount0; // Maps to unkcount0 on disk - - char pad2[4]; // Pad to +56 - int* unk3; - char pad3[16]; // Pad to +80 - int unkcount1; - - char pad4[132]; - char unk5; -}; - -struct AINodeClusterLinks -{ - short unk0; + short m_Count; // Multiplied by 4; probably total buffer size. short unk1; int unk2; - char unk3; - char unk4; - char unk5; + void* pBuffer; // Hull data buffer. + char unk3[8]; }; -#pragma pack(pop) diff --git a/r5dev/game/server/ai_utility.cpp b/r5dev/game/server/ai_utility.cpp index e85bb44b..98de8a73 100644 --- a/r5dev/game/server/ai_utility.cpp +++ b/r5dev/game/server/ai_utility.cpp @@ -5,11 +5,17 @@ //=============================================================================// #include "core/stdafx.h" +#include "tier0/fasttimer.h" #include "tier1/cvar.h" +#include "engine/server/server.h" #include "public/edict.h" #include "game/server/detour_impl.h" #include "game/server/ai_networkmanager.h" +#include "vscript/languages/squirrel_re/vsquirrel.h" + +static ConVar navmesh_always_reachable("navmesh_always_reachable", "0", FCVAR_DEVELOPMENTONLY, "Marks goal poly from agent poly as reachable regardless of table data ( !slower! )"); + inline uint32_t g_HullMasks[10] = // Hull mask table [r5apex_ds.exe + 131a2f8]. { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, @@ -42,7 +48,7 @@ void ClearNavMeshForHull(int hullSize) // Frees tiles, polys, tris, anything dynamically // allocated for this navmesh, and the navmesh itself. v_Detour_FreeNavMesh(nav); - delete nav; + free(nav); g_pNavMesh[hullSize] = nullptr; } @@ -69,10 +75,10 @@ uint32_t GetHullMaskById(int hullId) //----------------------------------------------------------------------------- uint8_t IsGoalPolyReachable(dtNavMesh* nav, dtPolyRef fromRef, dtPolyRef goalRef, int hullId) { - if (navmesh_always_reachable->GetBool()) + if (navmesh_always_reachable.GetBool()) return true; - return v_dtNavMesh__isPolyReachable(nav, fromRef, goalRef, hullId); + return dtNavMesh__isPolyReachable(nav, fromRef, goalRef, hullId); } //----------------------------------------------------------------------------- @@ -124,23 +130,49 @@ bool Detour_IsLoaded() //----------------------------------------------------------------------------- void Detour_HotSwap() { + Assert(ThreadInMainOrServerFrameThread()); + g_pServerScript->ExecuteCodeCallback("CodeCallback_OnNavMeshHotSwapBegin"); + // Free and re-init NavMesh. Detour_LevelShutdown(); v_Detour_LevelInit(); if (!Detour_IsLoaded()) Error(eDLL_T::SERVER, NOERROR, "%s - Failed to hot swap NavMesh\n", __FUNCTION__); + + g_pServerScript->ExecuteCodeCallback("CodeCallback_OnNavMeshHotSwapEnd"); } +/* +===================== +Detour_HotSwap_f + + Hot swaps the NavMesh + while the game is running +===================== +*/ +static void Detour_HotSwap_f() +{ + if (!g_pServer->IsActive()) + return; // Only execute if server is initialized and active. + + Msg(eDLL_T::SERVER, "Executing NavMesh hot swap for level '%s'\n", + g_ServerGlobalVariables->m_pszMapName); + + CFastTimer timer; + + timer.Start(); + Detour_HotSwap(); + + timer.End(); + Msg(eDLL_T::SERVER, "Hot swap took '%lf' seconds\n", timer.GetDuration().GetSeconds()); +} + +static ConCommand navmesh_hotswap("navmesh_hotswap", Detour_HotSwap_f, "Hot swap the NavMesh for all hulls", FCVAR_DEVELOPMENTONLY | FCVAR_SERVER_FRAME_THREAD); + /////////////////////////////////////////////////////////////////////////////// -void VRecast::Attach() const +void VRecast::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_dtNavMesh__isPolyReachable, &IsGoalPolyReachable); - DetourAttach((LPVOID*)&v_Detour_LevelInit, &Detour_LevelInit); + DetourSetup(&dtNavMesh__isPolyReachable, &IsGoalPolyReachable, bAttach); + DetourSetup(&v_Detour_LevelInit, &Detour_LevelInit, bAttach); } - -void VRecast::Detach() const -{ - DetourDetach((LPVOID*)&v_dtNavMesh__isPolyReachable, &IsGoalPolyReachable); - DetourDetach((LPVOID*)&v_Detour_LevelInit, &Detour_LevelInit); -} \ No newline at end of file diff --git a/r5dev/game/server/baseanimating.cpp b/r5dev/game/server/baseanimating.cpp index 353c9d6c..3c76f249 100644 --- a/r5dev/game/server/baseanimating.cpp +++ b/r5dev/game/server/baseanimating.cpp @@ -70,7 +70,7 @@ void CBaseAnimating::HitboxToWorldTransforms(uint32_t iBone, matrix3x4_t* transf void CBaseAnimating::LockStudioHdr() { // Populates the 'm_pStudioHdr' field. - v_CBaseAnimating__LockStudioHdr(this); + CBaseAnimating__LockStudioHdr(this); } CStudioHdr* CBaseAnimating::GetModelPtr(void) @@ -81,10 +81,3 @@ CStudioHdr* CBaseAnimating::GetModelPtr(void) } return (m_pStudioHdr && m_pStudioHdr->IsValid()) ? m_pStudioHdr : nullptr; } - -void BaseAnimating_Attach() -{ -} -void BaseAnimating_Detach() -{ -} \ No newline at end of file diff --git a/r5dev/game/server/baseanimating.h b/r5dev/game/server/baseanimating.h index 044426a3..f5f436b8 100644 --- a/r5dev/game/server/baseanimating.h +++ b/r5dev/game/server/baseanimating.h @@ -26,7 +26,7 @@ public: float GetModelScale() const { return m_flModelScale; } protected: - char gap_b04[8]; // Aligns properly in IDA and generated code after setting from 12 to 8. + void* __vftable; bool m_markedForServerInterpolation; bool m_animRemoveFromServerInterpolationNextFrame; char gap_b12[2]; @@ -135,28 +135,22 @@ protected: int m_numAnimSyncScriptProps; }; -inline CMemory p_CBaseAnimating__LockStudioHdr; -inline CBaseAnimating*(*v_CBaseAnimating__LockStudioHdr)(CBaseAnimating* thisp); - -void BaseAnimating_Attach(); -void BaseAnimating_Detach(); +inline CBaseAnimating*(*CBaseAnimating__LockStudioHdr)(CBaseAnimating* thisp); /////////////////////////////////////////////////////////////////////////////// class VBaseAnimating : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CBaseAnimating::LockStudioHdr", p_CBaseAnimating__LockStudioHdr.GetPtr()); + LogFunAdr("CBaseAnimating::LockStudioHdr", CBaseAnimating__LockStudioHdr); } virtual void GetFun(void) const { - p_CBaseAnimating__LockStudioHdr = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 41 56 48 83 EC 20 0F BF 41 58"); - v_CBaseAnimating__LockStudioHdr = p_CBaseAnimating__LockStudioHdr.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 41 56 48 83 EC 20 0F BF 41 58").GetPtr(CBaseAnimating__LockStudioHdr); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/baseanimatingoverlay.h b/r5dev/game/server/baseanimatingoverlay.h index 2b694939..b5e492ea 100644 --- a/r5dev/game/server/baseanimatingoverlay.h +++ b/r5dev/game/server/baseanimatingoverlay.h @@ -14,7 +14,7 @@ class CBaseAnimatingOverlay : public CBaseAnimating { - char gap_11E4[8]; + void* __vftable; int m_maxOverlays; char gap_11f4[4]; CAnimationLayer m_AnimOverlay; diff --git a/r5dev/game/server/baseentity.h b/r5dev/game/server/baseentity.h index 25cae346..88cd720e 100644 --- a/r5dev/game/server/baseentity.h +++ b/r5dev/game/server/baseentity.h @@ -17,9 +17,28 @@ #include "public/iserverentity.h" #include "engine/gl_model_private.h" #include "game/shared/collisionproperty.h" +#include "game/shared/shareddefs.h" #include "networkproperty.h" #include "entitylist.h" +#include "entityoutput.h" +//----------------------------------------------------------------------------- + +typedef void (CBaseEntity::* BASEPTR)(void); +typedef void (CBaseEntity::* ENTITYFUNCPTR)(CBaseEntity* pOther); +typedef void (CBaseEntity::* USEPTR)(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value); + +//----------------------------------------------------------------------------- +// Purpose: think contexts +//----------------------------------------------------------------------------- +struct thinkfunc_t +{ + BASEPTR m_pfnThink; + bool m_fireBeforeBaseThink; + string_t m_iszContext; + int m_nNextThinkTick; + int m_nLastThinkTick; +}; class CBaseEntity : public IServerEntity { @@ -34,8 +53,11 @@ public: int GetModelIndex(void) const; // Virtual in-engine! string_t GetModelName(void) const; // Virtual in-engine! + inline edict_t GetEdict(void) { return NetworkProp()->GetEdict(); } + inline string_t GetEntityName(void) const { return m_iName; } + protected: - char m_RefEHandle[4]; + CBaseHandle m_RefEHandle; char gap_c[4]; void* m_collideable; void* m_networkable; @@ -48,14 +70,14 @@ protected: string_t m_ModelName; int m_entIndex; char gap_74[8]; // Aligns properly in IDA and generated code after setting from 4 to 8. - const char* m_iClassname; + string_t* m_iClassname; float m_flAnimTime; float m_flSimulationTime; int m_creationTick; int m_nLastThinkTick; int m_PredictableID; int touchStamp; - char m_aThinkFunctions[32]; + CUtlVector m_aThinkFunctions; float m_entitySpawnTime; int m_spawner; bool m_wantsDamageCallbacks; @@ -65,7 +87,7 @@ protected: int m_fEffects; bool m_thinkNextFrame; char gap_cd[3]; - __int64 m_target; + string_t m_target; int m_networkedFlags; char m_nRenderFX; char m_nRenderMode; @@ -101,7 +123,7 @@ protected: int m_fDataObjectTypes; int m_iEFlags; int m_fFlags; - __int64 m_iName; + string_t m_iName; int m_scriptNameIndex; int m_instanceNameIndex; char m_scriptName[64]; @@ -145,7 +167,7 @@ protected: Vector3D m_vecAbsVelocity; Vector3D m_vecAngVelocity; char gap_3f4[12]; - float m_rgflCoordinateFrame[12]; + matrix3x4_t m_rgflCoordinateFrame; float m_flFriction; float m_flLocalTime; float m_flVPhysicsUpdateLocalTime; @@ -156,7 +178,7 @@ protected: Vector3D m_angAbsRotation; Vector3D m_vecVelocity; char gap_474[4]; - __int64 m_iParent; + string_t m_iParent; int m_iHammerID; float m_flSpeed; int m_iMaxHealth; @@ -176,9 +198,9 @@ protected: float m_lastTitanFootstepDamageTime; float m_flMaxspeed; int m_visibilityFlags; - char m_OnUser1[40]; - char m_OnDeath[40]; - char m_OnDestroy[40]; + COutputEvent m_OnUser1; + COutputEvent m_OnDeath; + COutputEvent m_OnDestroy; int m_cellWidth; int m_cellBits; int m_cellX; @@ -205,7 +227,7 @@ protected: float m_entityFadeDist; int m_dissolveEffectEntityHandle; float m_fadeDist; - __int64 m_iSignifierName; + string_t m_iSignifierName; int m_collectedInvalidateFlags; bool m_collectingInvalidateFlags; char gap_5d5[3]; @@ -219,7 +241,7 @@ protected: void* m_pTimedOverlay; char m_ScriptScope[32]; char m_hScriptInstance[8]; - __int64 m_iszScriptId; + string_t m_iszScriptId; int m_bossPlayer; int m_usableType; int m_usablePriority; @@ -252,30 +274,28 @@ protected: char m_realmsTransmitMaskCached[16]; int m_realmsTransmitMaskCachedSerialNumber; }; +static_assert(sizeof(CBaseEntity) == 2824); -inline CMemory p_CBaseEntity__GetBaseEntity; -inline CBaseEntity*(*v_CBaseEntity__GetBaseEntity)(CBaseEntity* thisp); +inline CBaseEntity*(*CBaseEntity__GetBaseEntity)(CBaseEntity* thisp); /////////////////////////////////////////////////////////////////////////////// class VBaseEntity : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CBaseEntity::GetBaseEntity", p_CBaseEntity__GetBaseEntity.GetPtr()); - LogVarAdr("g_pEntityList", reinterpret_cast(g_pEntityList)); + LogFunAdr("CBaseEntity::GetBaseEntity", CBaseEntity__GetBaseEntity); + LogVarAdr("g_pEntityList", g_pEntityList); } virtual void GetFun(void) const { - p_CBaseEntity__GetBaseEntity = g_GameDll.FindPatternSIMD("8B 91 ?? ?? ?? ?? 83 FA FF 74 1F 0F B7 C2 48 8D 0D ?? ?? ?? ?? C1 EA 10 48 8D 04 40 48 03 C0 39 54 C1 08 75 05 48 8B 04 C1 C3 33 C0 C3 CC CC CC 48 8B 41 30"); - v_CBaseEntity__GetBaseEntity = p_CBaseEntity__GetBaseEntity.RCast(); + g_GameDll.FindPatternSIMD("8B 91 ?? ?? ?? ?? 83 FA FF 74 1F 0F B7 C2 48 8D 0D ?? ?? ?? ?? C1 EA 10 48 8D 04 40 48 03 C0 39 54 C1 08 75 05 48 8B 04 C1 C3 33 C0 C3 CC CC CC 48 8B 41 30").GetPtr(CBaseEntity__GetBaseEntity); } virtual void GetVar(void) const { - g_pEntityList = p_CBaseEntity__GetBaseEntity.FindPattern("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pEntityList = CMemory(CBaseEntity__GetBaseEntity).FindPattern("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/cbase.cpp b/r5dev/game/server/cbase.cpp new file mode 100644 index 00000000..5e3f63c0 --- /dev/null +++ b/r5dev/game/server/cbase.cpp @@ -0,0 +1,6 @@ +//=============================================================================// +// +// Purpose: +// +//=============================================================================// +#include "cbase.h" diff --git a/r5dev/game/server/cbase.h b/r5dev/game/server/cbase.h new file mode 100644 index 00000000..99f0b645 --- /dev/null +++ b/r5dev/game/server/cbase.h @@ -0,0 +1,5 @@ +#ifndef CBASE_H +#define CBASE_H + + +#endif // CBASE_H diff --git a/r5dev/game/server/detour_impl.h b/r5dev/game/server/detour_impl.h index 0c888350..97d213fe 100644 --- a/r5dev/game/server/detour_impl.h +++ b/r5dev/game/server/detour_impl.h @@ -6,20 +6,11 @@ //------------------------------------------------------------------------- // RUNTIME: DETOUR //------------------------------------------------------------------------- -inline CMemory p_Detour_LevelInit; inline void(*v_Detour_LevelInit)(void); - -inline CMemory p_Detour_FreeNavMesh; inline void(*v_Detour_FreeNavMesh)(dtNavMesh* mesh); - -inline CMemory p_dtNavMesh__Init; -inline dtStatus(*v_dtNavMesh__Init)(dtNavMesh* thisptr, unsigned char* data, int flags); - -inline CMemory p_dtNavMesh__addTile; -inline dtStatus(*v_dtNavMesh__addTile)(dtNavMesh* thisptr, unsigned char* data, dtMeshHeader* header, int dataSize, int flags, dtTileRef lastRef); - -inline CMemory p_dtNavMesh__isPolyReachable; -inline bool(*v_dtNavMesh__isPolyReachable)(dtNavMesh* thisptr, dtPolyRef poly_1, dtPolyRef poly_2, int hull_type); +inline dtStatus(*dtNavMesh__Init)(dtNavMesh* thisptr, unsigned char* data, int flags); +inline dtStatus(*dtNavMesh__addTile)(dtNavMesh* thisptr, unsigned char* data, dtMeshHeader* header, int dataSize, int flags, dtTileRef lastRef); +inline uint8_t(*dtNavMesh__isPolyReachable)(dtNavMesh* thisptr, dtPolyRef poly_1, dtPolyRef poly_2, int hull_type); constexpr const char* NAVMESH_PATH = "maps/navmesh/"; @@ -58,33 +49,21 @@ class VRecast : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Detour_LevelInit", p_Detour_LevelInit.GetPtr()); - LogFunAdr("Detour_FreeNavMesh", p_Detour_FreeNavMesh.GetPtr()); - LogFunAdr("dtNavMesh::Init", p_dtNavMesh__Init.GetPtr()); - LogFunAdr("dtNavMesh::addTile", p_dtNavMesh__addTile.GetPtr()); - LogFunAdr("dtNavMesh::isPolyReachable", p_dtNavMesh__isPolyReachable.GetPtr()); - LogVarAdr("g_pNavMesh[5]", reinterpret_cast(g_pNavMesh)); - LogVarAdr("g_pNavMeshQuery", reinterpret_cast(g_pNavMeshQuery)); + LogFunAdr("Detour_LevelInit", v_Detour_LevelInit); + LogFunAdr("Detour_FreeNavMesh", v_Detour_FreeNavMesh); + LogFunAdr("dtNavMesh::Init", dtNavMesh__Init); + LogFunAdr("dtNavMesh::addTile", dtNavMesh__addTile); + LogFunAdr("dtNavMesh::isPolyReachable", dtNavMesh__isPolyReachable); + LogVarAdr("g_pNavMesh[ MAX_HULLS ]", g_pNavMesh); + LogVarAdr("g_pNavMeshQuery", g_pNavMeshQuery); } virtual void GetFun(void) const { - -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_Detour_LevelInit = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 45 33 F6 48 8D 3D ?? ?? ?? ??"); - p_Detour_FreeNavMesh = g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 48 89 6C 24 ?? 48 89 74 24 ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_Detour_LevelInit = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 45 33 E4"); - p_Detour_FreeNavMesh = g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 48 89 6C 24 ?? 48 8B D9"); -#endif - p_dtNavMesh__Init = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 53 41 56 48 81 EC ?? ?? ?? ?? 0F 10 11"); - p_dtNavMesh__addTile = g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 41 55"); - p_dtNavMesh__isPolyReachable = g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 49 63 F1"); - - v_Detour_LevelInit = p_Detour_LevelInit.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 45 33 E4*/ - v_Detour_FreeNavMesh = p_Detour_FreeNavMesh.RCast(); - v_dtNavMesh__Init = p_dtNavMesh__Init.RCast(); /*4C 89 44 24 ?? 53 41 56 48 81 EC ?? ?? ?? ?? 0F 10 11*/ - v_dtNavMesh__addTile = p_dtNavMesh__addTile.RCast(); /*44 89 4C 24 ?? 41 55*/ - v_dtNavMesh__isPolyReachable = p_dtNavMesh__isPolyReachable.RCast(); /*48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 49 63 F1*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 45 33 E4").GetPtr(v_Detour_LevelInit); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 48 89 6C 24 ?? 48 8B D9").GetPtr(v_Detour_FreeNavMesh); + g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 53 41 56 48 81 EC ?? ?? ?? ?? 0F 10 11").GetPtr(dtNavMesh__Init); + g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 41 55").GetPtr(dtNavMesh__addTile); + g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 49 63 F1").GetPtr(dtNavMesh__isPolyReachable); } virtual void GetVar(void) const { @@ -94,7 +73,6 @@ class VRecast : public IDetour .FindPatternSelf("48 89 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/entityoutput.cpp b/r5dev/game/server/entityoutput.cpp new file mode 100644 index 00000000..c6526e68 --- /dev/null +++ b/r5dev/game/server/entityoutput.cpp @@ -0,0 +1,6 @@ +//=============================================================================// +// +// Purpose: +// +//=============================================================================// +#include "entityoutput.h" diff --git a/r5dev/game/server/entityoutput.h b/r5dev/game/server/entityoutput.h new file mode 100644 index 00000000..22b3abce --- /dev/null +++ b/r5dev/game/server/entityoutput.h @@ -0,0 +1,70 @@ +#ifndef ENTITYOUTPUT_H +#define ENTITYOUTPUT_H +#include "variant_t.h" + +#define EVENT_FIRE_ALWAYS -1 + +class CBaseEntity; + + +//----------------------------------------------------------------------------- +// Purpose: A COutputEvent consists of an array of these CEventActions. +// Each CEventAction holds the information to fire a single input in +// a target entity, after a specific delay. +//----------------------------------------------------------------------------- +class CEventAction +{ +public: + int m_type; + char gap_4[4]; + + string_t m_iTarget; // name of the entity(s) to cause the action in + string_t m_iTargetInput; // the name of the action to fire + string_t m_iParameter; // parameter to send, 0 if none + + int m_scriptEnt; + char gap_24[4]; + char m_scriptFunc[16]; + + float m_flDelay; // the number of seconds to wait before firing the action + int m_nTimesToFire; // The number of times to fire this event, or EVENT_FIRE_ALWAYS. + + int m_iIDStamp; // unique identifier stamp + + //static int s_iNextIDStamp; !TODO[ AMOS ]: If found, make this a ptr and link it to the one in the game engine! + + CEventAction* m_pNext; +}; + +//----------------------------------------------------------------------------- +// Purpose: Stores a list of connections to other entities, for data/commands to be +// communicated along. +//----------------------------------------------------------------------------- +class CBaseEntityOutput +{ +public: + virtual ~CBaseEntityOutput() {}; + virtual int Save(/*ISave*/ __int64 /*save*/) {return 1; /*!!! IMPLEMENTATION IN ENGINE !!!*/} + virtual int Restore(/*IRestore*/ __int64 /*restore*/, int /*elementCount*/) { return 1; /*!!! IMPLEMENTATION IN ENGINE !!!*/ } + +protected: + variant_t m_Value; + CEventAction* m_ActionList; + //DECLARE_SIMPLE_DATADESC(); + + CBaseEntityOutput() {} // this class cannot be created, only it's children + +private: + CBaseEntityOutput(CBaseEntityOutput&); // protect from accidental copying +}; + + +//----------------------------------------------------------------------------- +// Purpose: parameterless entity event +//----------------------------------------------------------------------------- +class COutputEvent : public CBaseEntityOutput +{ +public: +}; + +#endif // ENTITYOUTPUT_H diff --git a/r5dev/game/server/gameinterface.cpp b/r5dev/game/server/gameinterface.cpp index 1273ef65..8b16f760 100644 --- a/r5dev/game/server/gameinterface.cpp +++ b/r5dev/game/server/gameinterface.cpp @@ -15,6 +15,7 @@ #include "gameinterface.h" #include "entitylist.h" #include "baseanimating.h" +#include "engine/server/server.h" #include "game/shared/usercmd.h" #include "game/server/util_server.h" @@ -78,11 +79,20 @@ ServerClass* CServerGameDLL::GetAllServerClasses(void) void __fastcall CServerGameDLL::OnReceivedSayTextMessage(void* thisptr, int senderId, const char* text, bool isTeamChat) { -#if defined(GAMEDLL_S3) // set isTeamChat to false so that we can let the convar sv_forceChatToTeamOnly decide whether team chat should be enforced // this isn't a great way of doing it but it works so meh CServerGameDLL__OnReceivedSayTextMessage(thisptr, senderId, text, false); -#endif +} + +void DrawServerHitbox(int iEntity) +{ + IHandleEntity* pEntity = LookupEntityByIndex(iEntity); + CBaseAnimating* pAnimating = dynamic_cast(pEntity); + + if (pAnimating) + { + pAnimating->DrawServerHitboxes(); + } } void DrawServerHitboxes(bool bRunOverlays) @@ -93,27 +103,16 @@ void DrawServerHitboxes(bool bRunOverlays) if (nVal == -1) return; - std::function fnLookupAndDraw = [&](int iEntity) - { - IHandleEntity* pEntity = LookupEntityByIndex(iEntity); - CBaseAnimating* pAnimating = dynamic_cast(pEntity); - - if (pAnimating) - { - pAnimating->DrawServerHitboxes(); - } - }; - if (nVal == 0) { for (int i = 0; i < NUM_ENT_ENTRIES; i++) { - fnLookupAndDraw(i); + DrawServerHitbox(i); } } else // Lookup entity manually by index from 'sv_showhitboxes'. { - fnLookupAndDraw(nVal); + DrawServerHitbox(nVal); } } @@ -137,16 +136,11 @@ void CServerGameClients::ProcessUserCmds(CServerGameClients* thisp, edict_t edic if (totalCmds < 0 || totalCmds >= (MAX_BACKUP_COMMANDS_PROCESS - 1) || numCmds < 0 || numCmds > totalCmds) { - //const char* name = "unknown"; - //if (pPlayer) - //{ - // name = pPlayer->GetPlayerName(); - //} - - //Warning(eDLL_T::SERVER, "%s: too many cmds %i sent for player %s\n", __FUNCTION__, totalCmds, name); - // !TODO: Drop the client from here. + CClient* pClient = g_pServer->GetClient(edict-1); + Warning(eDLL_T::SERVER, "%s: Player '%s' sent too many cmds (%i)\n", __FUNCTION__, pClient->GetServerName(), totalCmds); buf->SetOverflowFlag(); + return; } @@ -158,7 +152,7 @@ void CServerGameClients::ProcessUserCmds(CServerGameClients* thisp, edict_t edic from = to; } - // Client not fully connected or server has gone inactive or is paused, just ignore + // Client not fully connected or server has gone inactive or is paused, just ignore if (ignore || !pPlayer) { return; @@ -173,22 +167,11 @@ void RunFrameServer(double flFrameTime, bool bRunOverlays, bool bUniformUpdate) v_RunFrameServer(flFrameTime, bRunOverlays, bUniformUpdate); } -void VServerGameDLL::Attach() const +void VServerGameDLL::Detour(const bool bAttach) const { -#if defined(GAMEDLL_S3) - DetourAttach((LPVOID*)&CServerGameDLL__OnReceivedSayTextMessage, &CServerGameDLL::OnReceivedSayTextMessage); - DetourAttach(&v_CServerGameClients__ProcessUserCmds, CServerGameClients::ProcessUserCmds); -#endif - DetourAttach(&v_RunFrameServer, &RunFrameServer); -} - -void VServerGameDLL::Detach() const -{ -#if defined(GAMEDLL_S3) - DetourDetach((LPVOID*)&CServerGameDLL__OnReceivedSayTextMessage, &CServerGameDLL::OnReceivedSayTextMessage); - DetourDetach(&v_CServerGameClients__ProcessUserCmds, CServerGameClients::ProcessUserCmds); -#endif - DetourDetach(&v_RunFrameServer, &RunFrameServer); + DetourSetup(&CServerGameDLL__OnReceivedSayTextMessage, &CServerGameDLL::OnReceivedSayTextMessage, bAttach); + DetourSetup(&CServerGameClients__ProcessUserCmds, CServerGameClients::ProcessUserCmds, bAttach); + DetourSetup(&v_RunFrameServer, &RunFrameServer, bAttach); } CServerGameDLL* g_pServerGameDLL = nullptr; diff --git a/r5dev/game/server/gameinterface.h b/r5dev/game/server/gameinterface.h index 68861596..8b92c280 100644 --- a/r5dev/game/server/gameinterface.h +++ b/r5dev/game/server/gameinterface.h @@ -47,14 +47,10 @@ class CServerGameEnts : public IServerGameEnts { }; -inline CMemory p_CServerGameDLL__OnReceivedSayTextMessage; inline void(*CServerGameDLL__OnReceivedSayTextMessage)(void* thisptr, int senderId, const char* text, bool isTeamChat); - -inline CMemory p_CServerGameClients__ProcessUserCmds; -inline void(*v_CServerGameClients__ProcessUserCmds)(CServerGameClients* thisp, edict_t edict, bf_read* buf, +inline void(*CServerGameClients__ProcessUserCmds)(CServerGameClients* thisp, edict_t edict, bf_read* buf, int numCmds, int totalCmds, int droppedPackets, bool ignore, bool paused); -inline CMemory p_RunFrameServer; inline void(*v_RunFrameServer)(double flFrameTime, bool bRunOverlays, bool bUniformUpdate); extern CServerGameDLL* g_pServerGameDLL; @@ -68,35 +64,26 @@ class VServerGameDLL : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CServerGameDLL::OnReceivedSayTextMessage", p_CServerGameDLL__OnReceivedSayTextMessage.GetPtr()); - LogFunAdr("CServerGameClients::ProcessUserCmds", p_CServerGameClients__ProcessUserCmds.GetPtr()); - LogFunAdr("RunFrameServer", p_RunFrameServer.GetPtr()); - LogVarAdr("g_pServerGameDLL", reinterpret_cast(g_pServerGameDLL)); - LogVarAdr("g_pServerGameClients", reinterpret_cast(g_pServerGameClients)); - LogVarAdr("g_pServerGameEntities", reinterpret_cast(g_pServerGameEntities)); - LogVarAdr("g_pGlobals", reinterpret_cast(g_pGlobals)); + LogFunAdr("CServerGameDLL::OnReceivedSayTextMessage", CServerGameDLL__OnReceivedSayTextMessage); + LogFunAdr("CServerGameClients::ProcessUserCmds", CServerGameClients__ProcessUserCmds); + LogFunAdr("RunFrameServer", v_RunFrameServer); + LogVarAdr("g_pServerGameDLL", g_pServerGameDLL); + LogVarAdr("g_pServerGameClients", g_pServerGameClients); + LogVarAdr("g_pServerGameEntities", g_pServerGameEntities); + LogVarAdr("g_pGlobals", g_pGlobals); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CServerGameDLL__OnReceivedSayTextMessage = g_GameDll.FindPatternSIMD("40 55 57 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 4C 8B 15 ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CServerGameDLL__OnReceivedSayTextMessage = g_GameDll.FindPatternSIMD("85 D2 0F 8E ?? ?? ?? ?? 4C 8B DC"); -#endif - p_CServerGameClients__ProcessUserCmds = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 55 41 57"); - p_RunFrameServer = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 30 0F 29 74 24 ?? 48 8D 0D ?? ?? ?? ??"); - - CServerGameDLL__OnReceivedSayTextMessage = p_CServerGameDLL__OnReceivedSayTextMessage.RCast(); - v_CServerGameClients__ProcessUserCmds = p_CServerGameClients__ProcessUserCmds.RCast(); - v_RunFrameServer = p_RunFrameServer.RCast(); + g_GameDll.FindPatternSIMD("85 D2 0F 8E ?? ?? ?? ?? 4C 8B DC").GetPtr(CServerGameDLL__OnReceivedSayTextMessage); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 55 41 57").GetPtr(CServerGameClients__ProcessUserCmds); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 30 0F 29 74 24 ?? 48 8D 0D ?? ?? ?? ??").GetPtr(v_RunFrameServer); } virtual void GetVar(void) const { g_pGlobals = g_GameDll.FindPatternSIMD("4C 8B 0D ?? ?? ?? ?? 48 8B D1").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/liveapi/liveapi.cpp b/r5dev/game/server/liveapi/liveapi.cpp new file mode 100644 index 00000000..41ff6b92 --- /dev/null +++ b/r5dev/game/server/liveapi/liveapi.cpp @@ -0,0 +1,2321 @@ +//=============================================================================// +// +// Purpose: ServerGameDLL LiveAPI implementation +// +// ---------------------------------------------------------------------------- +// TODO: +// - Add code callback for observer target changed ( event ObserverSwitched ) +// - Add code callback for player weapon switched ( event WeaponSwitched ) +// +//=============================================================================// +#include "tier1/depthcounter.h" +#include "mbedtls/include/mbedtls/sha512.h" +#include "rtech/liveapi/liveapi.h" +#include "engine/sys_utils.h" +#include "vscript/languages/squirrel_re/include/sqtable.h" +#include "vscript/languages/squirrel_re/include/sqarray.h" +#include "game/server/vscript_server.h" +#include "liveapi.h" + +#pragma warning(push) +#pragma warning(disable : 4505) +#include "protoc/events.pb.h" +#pragma warning(pop) + +#define LIVEAPI_MAX_ITEM_DEPTH 64 // The total nesting depth cannot exceed this number +#define LIVEAPI_SHA512_HASH_SIZE 64 + + +/* + ███████╗██╗ ██╗███████╗███╗ ██╗████████╗ ███╗ ███╗███████╗███████╗███████╗ █████╗ ██████╗ ███████╗███████╗ + ██╔════╝██║ ██║██╔════╝████╗ ██║╚══██╔══╝ ████╗ ████║██╔════╝██╔════╝██╔════╝██╔══██╗██╔════╝ ██╔════╝██╔════╝ + █████╗ ██║ ██║█████╗ ██╔██╗ ██║ ██║ ██╔████╔██║█████╗ ███████╗███████╗███████║██║ ███╗█████╗ ███████╗ + ██╔══╝ ╚██╗ ██╔╝██╔══╝ ██║╚██╗██║ ██║ ██║╚██╔╝██║██╔══╝ ╚════██║╚════██║██╔══██║██║ ██║██╔══╝ ╚════██║ + ███████╗ ╚████╔╝ ███████╗██║ ╚████║ ██║ ██║ ╚═╝ ██║███████╗███████║███████║██║ ██║╚██████╔╝███████╗███████║ + ╚══════╝ ╚═══╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝ + NOTE: messages are statically allocated to save on runtime overhead, each message is cleared after usage. +*/ + +static rtech::liveapi::AmmoUsed s_ammoUsed; +static rtech::liveapi::ArenasItemDeselected s_arenasItemDeselected; +static rtech::liveapi::ArenasItemSelected s_arenasItemSelected; +static rtech::liveapi::BannerCollected s_bannerCollected; +static rtech::liveapi::BlackMarketAction s_blackMarketAction; +//static rtech::liveapi::ChangeCamera s_changeCamera; +static rtech::liveapi::CharacterSelected s_characterSelected; +//static rtech::liveapi::CustomMatch_CreateLobby s_customMatch_CreateLobby; +//static rtech::liveapi::CustomMatch_GetLobbyPlayers s_customMatch_GetLobbyPlayers; +//static rtech::liveapi::CustomMatch_GetSettings s_customMatch_GetSettings; +//static rtech::liveapi::CustomMatch_JoinLobby s_customMatch_JoinLobby; +//static rtech::liveapi::CustomMatch_KickPlayer s_customMatch_KickPlayer; +//static rtech::liveapi::CustomMatch_LeaveLobby s_customMatch_LeaveLobby; +//static rtech::liveapi::CustomMatch_LobbyPlayers s_customMatch_LobbyPlayers; +//static rtech::liveapi::CustomMatch_SendChat s_customMatch_SendChat; +//static rtech::liveapi::CustomMatch_SetMatchmaking s_customMatch_SetMatchmaking; +//static rtech::liveapi::CustomMatch_SetReady s_customMatch_SetReady; +//static rtech::liveapi::CustomMatch_SetSettings s_customMatch_SetSettings; +//static rtech::liveapi::CustomMatch_SetTeam s_customMatch_SetTeam; +//static rtech::liveapi::CustomMatch_SetTeamName s_customMatch_SetTeamName; +static rtech::liveapi::CustomEvent s_customEvent; +static rtech::liveapi::GameStateChanged s_gameStateChanged; +static rtech::liveapi::GibraltarShieldAbsorbed s_gibraltarShieldAbsorbed; +static rtech::liveapi::GrenadeThrown s_grenadeThrown; +static rtech::liveapi::Init s_init; +static rtech::liveapi::InventoryDrop s_inventoryDrop; +static rtech::liveapi::InventoryPickUp s_inventoryPickUp; +static rtech::liveapi::InventoryUse s_inventoryUse; +static rtech::liveapi::LegendUpgradeSelected s_legendUpgradeSelected; +static rtech::liveapi::LiveAPIEvent s_liveAPIEvent; +static rtech::liveapi::MatchSetup s_matchSetup; +static rtech::liveapi::MatchStateEnd s_matchStateEnd; +static rtech::liveapi::ObserverAnnotation s_observerAnnotation; +static rtech::liveapi::ObserverSwitched s_observerSwitched; +//static rtech::liveapi::PauseToggle s_pauseToggle; +static rtech::liveapi::PlayerAbilityUsed s_playerAbilityUsed; +static rtech::liveapi::PlayerAssist s_playerAssist; +static rtech::liveapi::PlayerConnected s_playerConnected; +static rtech::liveapi::PlayerDamaged s_playerDamaged; +static rtech::liveapi::PlayerDisconnected s_playerDisconnected; +static rtech::liveapi::PlayerDowned s_playerDowned; +static rtech::liveapi::PlayerKilled s_playerKilled; +static rtech::liveapi::PlayerRespawnTeam s_playerRespawnTeam; +static rtech::liveapi::PlayerRevive s_playerRevive; +static rtech::liveapi::PlayerStatChanged s_playerStatChanged; +static rtech::liveapi::PlayerUpgradeTierChanged s_playerUpgradeTierChanged; +//static rtech::liveapi::Request s_request; +//static rtech::liveapi::RequestStatus s_requestStatus; +//static rtech::liveapi::Response s_response; +static rtech::liveapi::RevenantForgedShadowDamaged s_revenantForgedShadowDamaged; +static rtech::liveapi::RingFinishedClosing s_ringFinishedClosing; +static rtech::liveapi::RingStartClosing s_ringStartClosing; +static rtech::liveapi::SquadEliminated s_squadEliminated; +static rtech::liveapi::WarpGateUsed s_warpGateUsed; +static rtech::liveapi::WeaponSwitched s_weaponSwitched; +static rtech::liveapi::WraithPortal s_wraithPortal; +static rtech::liveapi::ZiplineUsed s_ziplineUsed; + + +/* + ███████╗███╗ ██╗██╗ ██╗███╗ ███╗███████╗██████╗ █████╗ ████████╗██╗ ██████╗ ███╗ ██╗███████╗ + ██╔════╝████╗ ██║██║ ██║████╗ ████║██╔════╝██╔══██╗██╔══██╗╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝ + █████╗ ██╔██╗ ██║██║ ██║██╔████╔██║█████╗ ██████╔╝███████║ ██║ ██║██║ ██║██╔██╗ ██║███████╗ + ██╔══╝ ██║╚██╗██║██║ ██║██║╚██╔╝██║██╔══╝ ██╔══██╗██╔══██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║ + ███████╗██║ ╚████║╚██████╔╝██║ ╚═╝ ██║███████╗██║ ██║██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║███████║ + ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ + NOTE: the values here must align with the enumeration exposed to scripts; see section "abstractions"! +*/ + +enum class eLiveAPI_EventTypes +{ + ammoUsed, + arenasItemDeselected, + arenasItemSelected, + + bannerCollected, + blackMarketAction, + //changeCamera, + characterSelected, + //checkedState, + //clientState, + + //customMatch_CreateLobby, + //customMatch_GetLobbyPlayers, + //customMatch_GetSettings, + //customMatch_JoinLobby, + //customMatch_KickPlayer, + //customMatch_LeaveLobby, + //customMatch_LobbyPlayer, + //customMatch_LobbyPlayers, + //customMatch_SendChat, + //customMatch_SetMatchmaking, + //customMatch_SetReady, + //customMatch_SetSettings, + //customMatch_SetTeam, + //customMatch_SetTeamName, + customEvent, + datacenter, + //gameConVar, + gameStateChanged, + gibraltarShieldAbsorbed, + //globalVars, + grenadeThrown, + init, + + inventoryDrop, + inventoryItem, + inventoryPickUp, + inventoryUse, + + legendUpgradeSelected, + liveAPIEvent, + loadoutConfiguration, + matchSetup, + matchStateEnd, + observerAnnotation, + observerSwitched, + //pauseToggle, + + player, + playerAbilityUsed, + playerAssist, + playerConnected, + playerDamaged, + playerDisconnected, + playerDowned, + playerKilled, + playerRespawnTeam, + playerRevive, + playerStatChanged, + playerUpgradeTierChanged, + + //request, + //requestStatus, + //response, + + revenantForgedShadowDamaged, + + ringFinishedClosing, + ringStartClosing, + + //runCommand, + //scriptCall, + squadEliminated, + //stateCheck, + //svcMsgOverflow, + //svcMsgRemoteScript, + vector3, + version, + warpGateUsed, + weaponSwitched, + wraithPortal, + ziplineUsed, +}; + + +/* + ██╗ ██╗████████╗██╗██╗ ██╗████████╗██╗ ██╗ ██╗ ███╗ ███╗ █████╗ ██████╗██████╗ ██████╗ ███████╗ + ██║ ██║╚══██╔══╝██║██║ ██║╚══██╔══╝╚██╗ ██╔╝ ██║ ████╗ ████║██╔══██╗██╔════╝██╔══██╗██╔═══██╗██╔════╝ + ██║ ██║ ██║ ██║██║ ██║ ██║ ╚████╔╝ ████████╗ ██╔████╔██║███████║██║ ██████╔╝██║ ██║███████╗ + ██║ ██║ ██║ ██║██║ ██║ ██║ ╚██╔╝ ██╔═██╔═╝ ██║╚██╔╝██║██╔══██║██║ ██╔══██╗██║ ██║╚════██║ + ╚██████╔╝ ██║ ██║███████╗██║ ██║ ██║ ██████║ ██║ ╚═╝ ██║██║ ██║╚██████╗██║ ██║╚██████╔╝███████║ + ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝ +*/ + +static const char* LiveAPI_EventTypeToString(const eLiveAPI_EventTypes eventType) +{ + switch (eventType) + { + case eLiveAPI_EventTypes::ammoUsed: return "ammoUsed"; + case eLiveAPI_EventTypes::arenasItemDeselected: return "arenasItemDeselected"; + case eLiveAPI_EventTypes::arenasItemSelected: return "arenasItemSelected"; + case eLiveAPI_EventTypes::bannerCollected: return "bannerCollected"; + case eLiveAPI_EventTypes::blackMarketAction: return "blackMarketAction"; + //case eLiveAPI_EventTypes::changeCamera: return "changeCamera"; + case eLiveAPI_EventTypes::characterSelected: return "characterSelected"; + //case eLiveAPI_EventTypes::checkedState: return "checkedState"; + //case eLiveAPI_EventTypes::clientState: return "clientState"; + //case eLiveAPI_EventTypes::customMatch_CreateLobby: return "customMatch_CreateLobby"; + //case eLiveAPI_EventTypes::customMatch_GetLobbyPlayers: return "customMatch_GetLobbyPlayers"; + //case eLiveAPI_EventTypes::customMatch_GetSettings: return "customMatch_GetSettings"; + //case eLiveAPI_EventTypes::customMatch_JoinLobby: return "customMatch_JoinLobby"; + //case eLiveAPI_EventTypes::customMatch_KickPlayer: return "customMatch_KickPlayer"; + //case eLiveAPI_EventTypes::customMatch_LeaveLobby: return "customMatch_LeaveLobby"; + //case eLiveAPI_EventTypes::customMatch_LobbyPlayer: return "customMatch_LobbyPlayer"; + //case eLiveAPI_EventTypes::customMatch_LobbyPlayers: return "customMatch_LobbyPlayers"; + //case eLiveAPI_EventTypes::customMatch_SendChat: return "customMatch_SendChat"; + //case eLiveAPI_EventTypes::customMatch_SetMatchmaking: return "customMatch_SetMatchmaking"; + //case eLiveAPI_EventTypes::customMatch_SetReady: return "customMatch_SetReady"; + //case eLiveAPI_EventTypes::customMatch_SetSettings: return "customMatch_SetSettings"; + //case eLiveAPI_EventTypes::customMatch_SetTeam: return "customMatch_SetTeam"; + //case eLiveAPI_EventTypes::customMatch_SetTeamName: return "customMatch_SetTeamName"; + case eLiveAPI_EventTypes::customEvent: return "customEvent"; + case eLiveAPI_EventTypes::datacenter: return "datacenter"; + //case eLiveAPI_EventTypes::gameConVar: return "gameConVar"; + case eLiveAPI_EventTypes::gameStateChanged: return "gameStateChanged"; + case eLiveAPI_EventTypes::gibraltarShieldAbsorbed: return "gibraltarShieldAbsorbed"; + //case eLiveAPI_EventTypes::globalVars: return "globalVars"; + case eLiveAPI_EventTypes::grenadeThrown: return "grenadeThrown"; + case eLiveAPI_EventTypes::init: return "init"; + case eLiveAPI_EventTypes::inventoryDrop: return "inventoryDrop"; + case eLiveAPI_EventTypes::inventoryItem: return "inventoryItem"; + case eLiveAPI_EventTypes::inventoryPickUp: return "inventoryPickUp"; + case eLiveAPI_EventTypes::inventoryUse: return "inventoryUse"; + case eLiveAPI_EventTypes::legendUpgradeSelected: return "legendUpgradeSelected"; + case eLiveAPI_EventTypes::liveAPIEvent: return "liveAPIEvent"; + case eLiveAPI_EventTypes::loadoutConfiguration: return "loadoutConfiguration"; + case eLiveAPI_EventTypes::matchSetup: return "matchSetup"; + case eLiveAPI_EventTypes::matchStateEnd: return "matchStateEnd"; + case eLiveAPI_EventTypes::observerAnnotation: return "observerAnnotation"; + case eLiveAPI_EventTypes::observerSwitched: return "observerSwitched"; + //case eLiveAPI_EventTypes::pauseToggle: return "pauseToggle"; + case eLiveAPI_EventTypes::player: return "player"; + case eLiveAPI_EventTypes::playerAbilityUsed: return "playerAbilityUsed"; + case eLiveAPI_EventTypes::playerAssist: return "playerAssist"; + case eLiveAPI_EventTypes::playerConnected: return "playerConnected"; + case eLiveAPI_EventTypes::playerDamaged: return "playerDamaged"; + case eLiveAPI_EventTypes::playerDisconnected: return "playerDisconnected"; + case eLiveAPI_EventTypes::playerDowned: return "playerDowned"; + case eLiveAPI_EventTypes::playerKilled: return "playerKilled"; + case eLiveAPI_EventTypes::playerRespawnTeam: return "playerRespawnTeam"; + case eLiveAPI_EventTypes::playerRevive: return "playerRevive"; + case eLiveAPI_EventTypes::playerStatChanged: return "playerStatChanged"; + case eLiveAPI_EventTypes::playerUpgradeTierChanged: return "playerUpgradeTierChanged"; + //case eLiveAPI_EventTypes::request: return "request"; + //case eLiveAPI_EventTypes::requestStatus: return "requestStatus"; + //case eLiveAPI_EventTypes::response: return "response"; + case eLiveAPI_EventTypes::revenantForgedShadowDamaged: return "revenantForgedShadowDamaged"; + case eLiveAPI_EventTypes::ringFinishedClosing: return "ringFinishedClosing"; + case eLiveAPI_EventTypes::ringStartClosing: return "ringStartClosing"; + //case eLiveAPI_EventTypes::runCommand: return "runCommand"; + //case eLiveAPI_EventTypes::scriptCall: return "scriptCall"; + case eLiveAPI_EventTypes::squadEliminated: return "squadEliminated"; + //case eLiveAPI_EventTypes::stateCheck: return "stateCheck"; + //case eLiveAPI_EventTypes::svcMsgOverflow: return "svcMsgOverflow"; + //case eLiveAPI_EventTypes::svcMsgRemoteScript: return "svcMsgRemoteScript"; + case eLiveAPI_EventTypes::vector3: return "vector3"; + case eLiveAPI_EventTypes::version: return "version"; + case eLiveAPI_EventTypes::warpGateUsed: return "warpGateUsed"; + case eLiveAPI_EventTypes::weaponSwitched: return "weaponSwitched"; + case eLiveAPI_EventTypes::wraithPortal: return "wraithPortal"; + case eLiveAPI_EventTypes::ziplineUsed: return "ziplineUsed"; + + default: Assert(0); return "(unknown)"; + }; +} + +static bool LiveAPI_EnsureType(HSQUIRRELVM const v, const SQObjectPtr& obj, const SQObjectType expectType, + const google::protobuf::Message* const eventMsg, const SQInteger fieldNum) +{ + if (sq_type(obj) == expectType) + return true; + + const char* const expectTypeName = IdType2Name(expectType); + const char* const gotTypeName = IdType2Name(sq_type(obj)); + + if (eventMsg) + { + const google::protobuf::Descriptor* const descriptor = eventMsg->GetDescriptor(); + + if (fieldNum == -1) + { + v_SQVM_RaiseError(v, "Expected type \"%s\", got type \"%s\" for message \"%s\".", expectTypeName, gotTypeName, + descriptor->name().c_str()); + } + else + { + const google::protobuf::FieldDescriptor* const fieldDescriptor = descriptor->field(fieldNum); + + v_SQVM_RaiseError(v, "Expected type \"%s\", got type \"%s\" for field \"%s.%s\".", expectTypeName, gotTypeName, + descriptor->name().c_str(), fieldDescriptor->name().c_str()); + } + } + else + { + v_SQVM_RaiseError(v, "Expected type \"%s\", got type \"%s\".", expectTypeName, gotTypeName); + } + + return false; +} + +static bool LiveAPI_CheckSwitchType(HSQUIRRELVM const v, const SQObjectPtr& obj) +{ + if (sq_type(obj) == OT_INTEGER) + return true; + + v_SQVM_RaiseError(v, "Cannot switch on type \"%s\".", IdType2Name(sq_type(obj))); + return false; +} + +#define LIVEAPI_ENSURE_TYPE(v, obj, expectType, eventMsg, fieldNum) { if (!LiveAPI_EnsureType(v, obj, expectType, eventMsg, fieldNum)) return false; } +#define LIVEAPI_EMPTY_TABLE_ERROR(v, eventMsg) { v_SQVM_RaiseError(v, "Empty iterable on message \"%s\".", eventMsg->GetTypeName().c_str()); return false; } +#define LIVEAPI_FIELD_ERROR(v, fieldNum, eventMsg) { v_SQVM_RaiseError(v, "Field \"%d\" doesn't exist in message \"%s\".", fieldNum, eventMsg->GetTypeName().c_str()); return false; } +#define LIVEAPI_ONEOF_FIELD_ERROR(v, fieldNum, eventMsg) { v_SQVM_RaiseError(v, "Tried to set member \"%d\" of oneof field in message \"%s\" while another has already been set.", fieldNum, eventMsg->GetTypeName().c_str()); return false; } +#define LIVEAPI_UNSUPPORTED_TYPE_ERROR(v, gotType, eventMsg) {v_SQVM_RaiseError(v, "Value type \"%s\" is not supported for message \"%s\".\n", IdType2Name(gotType), eventMsg->GetTypeName().c_str()); return false; } +#define LIVEAPI_CHECK_RECURSION_DEPTH(v, currDepth) { if (currDepth > LIVEAPI_MAX_ITEM_DEPTH) { v_SQVM_RaiseError(v, "Exceeded nesting depth limit of \"%i\".", LIVEAPI_MAX_ITEM_DEPTH); return false; }} + + +/* + ██╗███╗ ██╗████████╗███████╗██████╗ ███╗ ███╗███████╗██████╗ ██╗ █████╗ ██████╗ ██╗ ██╗ + ██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔════╝██╔══██╗██║██╔══██╗██╔══██╗╚██╗ ██╔╝ + ██║██╔██╗ ██║ ██║ █████╗ ██████╔╝██╔████╔██║█████╗ ██║ ██║██║███████║██████╔╝ ╚████╔╝ + ██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██║╚██╔╝██║██╔══╝ ██║ ██║██║██╔══██║██╔══██╗ ╚██╔╝ + ██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ╚═╝ ██║███████╗██████╔╝██║██║ ██║██║ ██║ ██║ + ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ + Not used directly, but as part of other messages +*/ + +template +static void LiveAPI_SetCommonMessageFields(T const msg, const eLiveAPI_EventTypes eventType) +{ + if (msg->timestamp() == 0) + msg->set_timestamp(GetUnixTimeStamp()); + if (msg->category().empty()) + msg->set_category(LiveAPI_EventTypeToString(eventType)); +} + +static void LiveAPI_SetVector3D(rtech::liveapi::Vector3* const msgVec, const Vector3D* const dataVec) +{ + msgVec->set_x(dataVec->x); + msgVec->set_y(dataVec->y); + msgVec->set_z(dataVec->z); +} + +static void LiveAPI_SetDataCenter(rtech::liveapi::Datacenter* const msg, const char* const name) +{ + LiveAPI_SetCommonMessageFields(msg, eLiveAPI_EventTypes::datacenter); + msg->set_name(name); +} + +static void LiveAPI_SetVersion(rtech::liveapi::Version* const msg) +{ + msg->set_major_num(LIVEAPI_MAJOR_VERSION); + msg->set_minor_num(LIVEAPI_MINOR_VERSION); + msg->set_build_stamp(g_SDKDll.GetNTHeaders()->FileHeader.TimeDateStamp); + msg->set_revision(LIVEAPI_REVISION); +} + +static void LiveAPI_SetNucleusHash(std::string* const msg, const SQString* const nucleusId) +{ + static const char hexChars[] = "0123456789abcdef"; + uint8_t nucleusIdHash[LIVEAPI_SHA512_HASH_SIZE]; + + mbedtls_sha512(reinterpret_cast(nucleusId->_val), nucleusId->_len, nucleusIdHash, NULL); + + const size_t hashSize = liveapi_truncate_hash_fields.GetBool() + ? LIVEAPI_SHA512_HASH_SIZE / 4 + : LIVEAPI_SHA512_HASH_SIZE; + + msg->reserve((hashSize * 2) + 1); + msg->resize((hashSize * 2)); + + for (size_t i = 0; i < hashSize; i++) + { + (*msg)[i * 2] = hexChars[(nucleusIdHash[i] >> 4) & 0xf]; + (*msg)[i * 2 + 1] = hexChars[nucleusIdHash[i] & 0xf]; + } +} + +static bool LiveAPI_SetPlayerIdentityFields(HSQUIRRELVM const v, const SQTable* const table, rtech::liveapi::Player* const playerMsg) +{ + bool ranLoop = false; + + SQ_FOR_EACH_TABLE(table, i) + { + const SQTable::_HashNode& node = table->_nodes[i]; + + if (sq_isnull(node.key)) + continue; + + if (!ranLoop) + ranLoop = true; + + if (!LiveAPI_CheckSwitchType(v, node.key)) + return false; + + const SQInteger fieldNum = _integer(node.key); + const SQObjectPtr& obj = node.val; + + switch (fieldNum) + { + case rtech::liveapi::Player::kNameFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, playerMsg, fieldNum); + playerMsg->set_name(_stringval(obj)); + + break; + } + case rtech::liveapi::Player::kTeamIdFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, playerMsg, fieldNum); + playerMsg->set_teamid(_integer(obj)); + + break; + } + case rtech::liveapi::Player::kPosFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_VECTOR, playerMsg, fieldNum); + LiveAPI_SetVector3D(playerMsg->mutable_pos(), _vector3d(obj)); + + break; + } + case rtech::liveapi::Player::kAnglesFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_VECTOR, playerMsg, fieldNum); + LiveAPI_SetVector3D(playerMsg->mutable_angles(), _vector3d(obj)); + + break; + } + case rtech::liveapi::Player::kCurrentHealthFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, playerMsg, fieldNum); + playerMsg->set_currenthealth(_integer(obj)); + + break; + } + case rtech::liveapi::Player::kMaxHealthFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, playerMsg, fieldNum); + playerMsg->set_maxhealth(_integer(obj)); + + break; + } + case rtech::liveapi::Player::kShieldHealthFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, playerMsg, fieldNum); + playerMsg->set_shieldhealth(_integer(obj)); + + break; + } + case rtech::liveapi::Player::kShieldMaxHealthFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, playerMsg, fieldNum); + playerMsg->set_shieldmaxhealth(_integer(obj)); + + break; + } + case rtech::liveapi::Player::kNucleusHashFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, playerMsg, fieldNum); + LiveAPI_SetNucleusHash(playerMsg->mutable_nucleushash(), _string(obj)); + + break; + } + case rtech::liveapi::Player::kHardwareNameFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, playerMsg, fieldNum); + playerMsg->set_hardwarename(_stringval(obj)); + + break; + } + case rtech::liveapi::Player::kTeamNameFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, playerMsg, fieldNum); + playerMsg->set_teamname(_stringval(obj)); + + break; + } + case rtech::liveapi::Player::kSquadIndexFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, playerMsg, fieldNum); + playerMsg->set_squadindex(_integer(obj)); + + break; + } + case rtech::liveapi::Player::kCharacterFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, playerMsg, fieldNum); + playerMsg->set_character(_stringval(obj)); + + break; + } + case rtech::liveapi::Player::kSkinFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, playerMsg, fieldNum); + playerMsg->set_skin(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, playerMsg); + } + } + + if (!ranLoop) + LIVEAPI_EMPTY_TABLE_ERROR(v, playerMsg); + + return true; +} + +static bool LiveAPI_SetInventoryItem(HSQUIRRELVM const v, const SQTable* const table, rtech::liveapi::InventoryItem* const event) +{ + bool ranLoop = false; + + SQ_FOR_EACH_TABLE(table, i) + { + const SQTable::_HashNode& node = table->_nodes[i]; + + if (sq_isnull(node.key)) + continue; + + if (!ranLoop) + ranLoop = true; + + if (!LiveAPI_CheckSwitchType(v, node.key)) + return false; + + const SQInteger fieldNum = _integer(node.key); + const SQObjectPtr& obj = node.val; + + switch (fieldNum) + { + case rtech::liveapi::InventoryItem::kQuantityFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_quantity(_integer(obj)); + + break; + } + case rtech::liveapi::InventoryItem::kItemFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_item(_stringval(obj)); + + break; + } + case rtech::liveapi::InventoryItem::kExtraDataFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_extradata(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + } + + if (!ranLoop) + LIVEAPI_EMPTY_TABLE_ERROR(v, event); + + return true; +} + +static bool LiveAPI_SetLoadoutConfiguration(HSQUIRRELVM const v, rtech::liveapi::LoadoutConfiguration* const msg, const SQTable* const table) +{ + bool ranOuterLoop = false; + bool ranInnerLoop = false; + + SQ_FOR_EACH_TABLE(table, i) + { + const SQTable::_HashNode& node = table->_nodes[i]; + + if (sq_isnull(node.key)) + continue; + + if (!ranOuterLoop) + ranOuterLoop = true; + + if (!LiveAPI_CheckSwitchType(v, node.key)) + return false; + + const SQInteger fieldNum = _integer(node.key); + const SQObjectPtr& obj = node.val; + + rtech::liveapi::InventoryItem* (rtech::liveapi::LoadoutConfiguration:: * addFunctor)(); + + switch (fieldNum) + { + case rtech::liveapi::LoadoutConfiguration::kWeaponsFieldNumber: + { + addFunctor = &rtech::liveapi::LoadoutConfiguration::add_weapons; + break; + } + case rtech::liveapi::LoadoutConfiguration::kEquipmentFieldNumber: + { + addFunctor = &rtech::liveapi::LoadoutConfiguration::add_equipment; + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, msg); + } + + LIVEAPI_ENSURE_TYPE(v, obj, OT_ARRAY, msg, fieldNum); + const SQArray* const fieldArray = _array(obj); + + for (SQInteger j = 0; j < fieldArray->Size(); j++) + { + const SQObject& fieldObj = fieldArray->_values[j]; + + if (sq_isnull(fieldObj)) + continue; + + if (!ranInnerLoop) + ranInnerLoop = true; + + LIVEAPI_ENSURE_TYPE(v, fieldObj, OT_TABLE, msg, fieldNum); + + if (!LiveAPI_SetInventoryItem(v, _table(fieldObj), (msg->*addFunctor)())) + return false; + } + } + + if (!ranOuterLoop || !ranInnerLoop) + LIVEAPI_EMPTY_TABLE_ERROR(v, msg); + + return true; +} + + +/* + ███████╗██╗ ██╗███████╗███╗ ██╗████████╗ ██╗ ██╗ █████╗ ███╗ ██╗██████╗ ██╗ ███████╗██████╗ ███████╗ + ██╔════╝██║ ██║██╔════╝████╗ ██║╚══██╔══╝ ██║ ██║██╔══██╗████╗ ██║██╔══██╗██║ ██╔════╝██╔══██╗██╔════╝ + █████╗ ██║ ██║█████╗ ██╔██╗ ██║ ██║ ███████║███████║██╔██╗ ██║██║ ██║██║ █████╗ ██████╔╝███████╗ + ██╔══╝ ╚██╗ ██╔╝██╔══╝ ██║╚██╗██║ ██║ ██╔══██║██╔══██║██║╚██╗██║██║ ██║██║ ██╔══╝ ██╔══██╗╚════██║ + ███████╗ ╚████╔╝ ███████╗██║ ╚████║ ██║ ██║ ██║██║ ██║██║ ╚████║██████╔╝███████╗███████╗██║ ██║███████║ + ╚══════╝ ╚═══╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝ + NOTE: one handler may be used for multiple events to cut down on boilerplate code, this is possible if a message + has the same structure layout as another message; the actual field numbers don't matter, but the field names do! +*/ + +static bool LiveAPI_HandleInitEvent(rtech::liveapi::Init* const event, const eLiveAPI_EventTypes eventType) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + event->set_gameversion(Sys_GetBuildString()); + LiveAPI_SetVersion(event->mutable_apiversion()); + event->set_platform(Sys_GetPlatformString()); + event->set_name(liveapi_session_name.GetString()); + + return true; +} + +static bool LiveAPI_HandleMatchSetup(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::MatchSetup* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::MatchSetup::kMapFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_map(_stringval(obj)); + + break; + } + case rtech::liveapi::MatchSetup::kPlaylistNameFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_playlistname(_stringval(obj)); + + break; + } + case rtech::liveapi::MatchSetup::kPlaylistDescFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_playlistdesc(_stringval(obj)); + + break; + } + case rtech::liveapi::MatchSetup::kDatacenterFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + LiveAPI_SetDataCenter(event->mutable_datacenter(), _stringval(obj)); + + break; + } + case rtech::liveapi::MatchSetup::kAimAssistOnFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_BOOL, event, fieldNum); + event->set_aimassiston(_bool(obj)); + + break; + } + case rtech::liveapi::MatchSetup::kAnonymousModeFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_BOOL, event, fieldNum); + event->set_anonymousmode(_bool(obj)); + + break; + } + case rtech::liveapi::MatchSetup::kServerIdFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_serverid(_stringval(obj)); + + break; + } + case rtech::liveapi::MatchSetup::kStartingLoadoutFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + + if (!LiveAPI_SetLoadoutConfiguration(v, event->mutable_startingloadout(), _table(obj))) + return false; + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleAmmoUsed(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::AmmoUsed* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::AmmoUsed::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::AmmoUsed::kAmmoTypeFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_ammotype(_stringval(obj)); + + break; + } + case rtech::liveapi::AmmoUsed::kAmountUsedFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_amountused(_integer(obj)); + + break; + } + case rtech::liveapi::AmmoUsed::kOldAmmoCountFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_oldammocount(_integer(obj)); + + break; + } + case rtech::liveapi::AmmoUsed::kNewAmmoCountFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_newammocount(_integer(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +template +static bool LiveAPI_HandleInventoryChange(HSQUIRRELVM const v, const SQObject& obj, T const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case event->kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case event->kItemFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_item(_stringval(obj)); + + break; + } + case event->kQuantityFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_quantity(_integer(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleInventoryDrop(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::InventoryDrop* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::InventoryDrop::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::InventoryDrop::kItemFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_item(_stringval(obj)); + + break; + } + case rtech::liveapi::InventoryDrop::kQuantityFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_quantity(_integer(obj)); + + break; + } + case rtech::liveapi::InventoryDrop::kExtraDataFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_ARRAY, event, fieldNum); + const SQArray* const fieldArray = _array(obj); + + for (SQInteger j = 0; j < fieldArray->Size(); j++) + { + const SQObject& fieldObj = fieldArray->_values[j]; + + if (sq_isnull(fieldObj)) + continue; + + LIVEAPI_ENSURE_TYPE(v, fieldObj, OT_STRING, event, fieldNum); + event->add_extradata(_stringval(fieldObj)); + } + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleGameStateChanged(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::GameStateChanged* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::GameStateChanged::kStateFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_state(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleMatchStateEnd(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::MatchStateEnd* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::MatchStateEnd::kStateFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_state(_stringval(obj)); + + break; + } + case rtech::liveapi::MatchStateEnd::kWinnersFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_ARRAY, event, fieldNum); + const SQArray* const fieldArray = _array(obj); + + for (SQInteger j = 0; j < fieldArray->Size(); j++) + { + const SQObject& fieldObj = fieldArray->_values[j]; + + if (sq_isnull(fieldObj)) + continue; + + LIVEAPI_ENSURE_TYPE(v, fieldObj, OT_TABLE, event, fieldNum); + + if (!LiveAPI_SetPlayerIdentityFields(v, _table(fieldObj), event->add_winners())) + return false; // failure + } + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleObserverSwitched(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::ObserverSwitched* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::ObserverSwitched::kObserverFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_observer())) + return false; + + break; + } + case rtech::liveapi::ObserverSwitched::kTargetFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_target())) + return false; + + break; + } + case rtech::liveapi::ObserverSwitched::kTargetTeamFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_ARRAY, event, fieldNum); + const SQArray* const fieldArray = _array(obj); + + for (SQInteger j = 0; j < fieldArray->Size(); j++) + { + const SQObject& fieldObj = fieldArray->_values[j]; + + if (sq_isnull(fieldObj)) + continue; + + LIVEAPI_ENSURE_TYPE(v, fieldObj, OT_TABLE, event, fieldNum); + + if (!LiveAPI_SetPlayerIdentityFields(v, _table(fieldObj), event->add_targetteam())) + return false; // failure + } + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleObserverAnnotation(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::ObserverAnnotation* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::ObserverAnnotation::kAnnotationSerialFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_annotationserial(_integer(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleBannerCollected(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::BannerCollected* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::BannerCollected::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::BannerCollected::kCollectedFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_collected())) + return false; + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerRevive(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerRevive* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerRevive::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::PlayerRevive::kRevivedFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_revived())) + return false; + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerDisconnected(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerDisconnected* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerDisconnected::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::PlayerDisconnected::kCanReconnectFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_BOOL, event, fieldNum); + event->set_canreconnect(_bool(obj)); + + break; + } + case rtech::liveapi::PlayerDisconnected::kIsAliveFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_BOOL, event, fieldNum); + event->set_isalive(_bool(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +template +static bool LiveAPI_HandlePlayerAttackCommon(HSQUIRRELVM const v, const SQObject& obj, T const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case event->kAttackerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_attacker())) + return false; + + break; + } + case event->kVictimFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_victim())) + return false; + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerDowned(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerDowned* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerDowned::kAttackerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_attacker())) + return false; + + break; + } + case rtech::liveapi::PlayerDowned::kVictimFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_victim())) + return false; + + break; + } + case rtech::liveapi::PlayerDowned::kWeaponFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_weapon(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerKilled(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerKilled* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerKilled::kAttackerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_attacker())) + return false; + + break; + } + case rtech::liveapi::PlayerKilled::kVictimFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_victim())) + return false; + + break; + } + case rtech::liveapi::PlayerKilled::kAwardedToFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_awardedto())) + return false; + + break; + } + case rtech::liveapi::PlayerKilled::kWeaponFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_weapon(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +template +static bool LiveAPI_HandleAbilityDamaged(HSQUIRRELVM const v, const SQObject& obj, T const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case event->kAttackerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_attacker())) + return false; + + break; + } + case event->kVictimFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_victim())) + return false; + + break; + } + case event->kDamageInflictedFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_damageinflicted(_integer(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerDamaged(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerDamaged* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerDamaged::kAttackerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_attacker())) + return false; + + break; + } + case rtech::liveapi::PlayerDamaged::kVictimFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_victim())) + return false; + + break; + } + case rtech::liveapi::PlayerDamaged::kWeaponFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_weapon(_stringval(obj)); + + break; + } + case rtech::liveapi::PlayerDamaged::kDamageInflictedFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_damageinflicted(_integer(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerAssist(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerAssist* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerAssist::kAssistantFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_assistant())) + return false; + + break; + } + case rtech::liveapi::PlayerAssist::kVictimFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_victim())) + return false; + + break; + } + case rtech::liveapi::PlayerAssist::kWeaponFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_weapon(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +// CharacterSelected +// PlayerConnected +// WraithPortal +// WarpGateUsed +template +static bool LiveAPI_HandleSimplePlayerMessage(HSQUIRRELVM const v, const SQObject& obj, T const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case event->kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleRingFinishedClosing(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::RingFinishedClosing* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case event->kStageFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_stage(_integer(obj)); + + break; + } + case event->kCenterFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_VECTOR, event, fieldNum); + LiveAPI_SetVector3D(event->mutable_center(), _vector3d(obj)); + + break; + } + case event->kCurrentRadiusFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_FLOAT, event, fieldNum); + event->set_currentradius(_float(obj)); + + break; + } + case event->kShrinkDurationFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_FLOAT, event, fieldNum); + event->set_shrinkduration(_float(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleDeathFieldStartClosing(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::RingStartClosing* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::RingStartClosing::kStageFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_stage(_integer(obj)); + + break; + } + case rtech::liveapi::RingStartClosing::kCenterFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_VECTOR, event, fieldNum); + LiveAPI_SetVector3D(event->mutable_center(), _vector3d(obj)); + + break; + } + case rtech::liveapi::RingStartClosing::kCurrentRadiusFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_FLOAT, event, fieldNum); + event->set_currentradius(_float(obj)); + + break; + } + case rtech::liveapi::RingStartClosing::kEndRadiusFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_FLOAT, event, fieldNum); + event->set_endradius(_float(obj)); + + break; + } + case rtech::liveapi::RingStartClosing::kShrinkDurationFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_FLOAT, event, fieldNum); + event->set_shrinkduration(_float(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleSquadEliminated(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::SquadEliminated* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::SquadEliminated::kPlayersFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_ARRAY, event, fieldNum); + const SQArray* const fieldArray = _array(obj); + + for (SQInteger j = 0; j < fieldArray->Size(); j++) + { + const SQObject& fieldObj = fieldArray->_values[j]; + + if (sq_isnull(fieldObj)) + continue; + + LIVEAPI_ENSURE_TYPE(v, fieldObj, OT_TABLE, event, fieldNum); + rtech::liveapi::Player* const player = event->add_players(); + + if (!LiveAPI_SetPlayerIdentityFields(v, _table(fieldObj), player)) + return false; // failure + } + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +template +static bool LiveAPI_HandleLinkedEntityEvent(HSQUIRRELVM const v, const SQObject& obj, T const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case event->kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case event->kLinkedEntityFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_linkedentity(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleWeaponSwitchedEvent(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::WeaponSwitched* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::WeaponSwitched::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::WeaponSwitched::kOldWeaponFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_oldweapon(_stringval(obj)); + + break; + } + case rtech::liveapi::WeaponSwitched::kNewWeaponFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_newweapon(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleBlackMarketActionEvent(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::BlackMarketAction* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::BlackMarketAction::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::BlackMarketAction::kItemFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_item(_stringval(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerUpgradeTierChanged(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerUpgradeTierChanged* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerUpgradeTierChanged::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::PlayerUpgradeTierChanged::kLevelFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_level(_integer(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandleLegendUpgradeSelected(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::LegendUpgradeSelected* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::LegendUpgradeSelected::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::LegendUpgradeSelected::kUpgradeNameFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_upgradename(_stringval(obj)); + + break; + } + case rtech::liveapi::LegendUpgradeSelected::kUpgradeDescFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_upgradedesc(_stringval(obj)); + + break; + } + case rtech::liveapi::LegendUpgradeSelected::kLevelFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_level(_integer(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerRespawnTeam(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerRespawnTeam* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerRespawnTeam::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::PlayerRespawnTeam::kRespawnedFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_ARRAY, event, fieldNum); + const SQArray* const fieldArray = _array(obj); + + for (SQInteger j = 0; j < fieldArray->Size(); j++) + { + const SQObject& fieldObj = fieldArray->_values[j]; + + if (sq_isnull(fieldObj)) + continue; + + LIVEAPI_ENSURE_TYPE(v, fieldObj, OT_TABLE, event, fieldNum); + rtech::liveapi::Player* const player = event->add_respawned(); + + if (!LiveAPI_SetPlayerIdentityFields(v, _table(fieldObj), player)) + return false; // failure + } + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static bool LiveAPI_HandlePlayerStatChanged(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::PlayerStatChanged* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::PlayerStatChanged::kPlayerFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetPlayerIdentityFields(v, _table(obj), event->mutable_player())) + return false; + + break; + } + case rtech::liveapi::PlayerStatChanged::kStatNameFieldNumber: + { + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_statname(_stringval(obj)); + + break; + } + case rtech::liveapi::PlayerStatChanged::kIntValueFieldNumber: + { + if (event->newValue_case() != rtech::liveapi::PlayerStatChanged::NEWVALUE_NOT_SET) + LIVEAPI_ONEOF_FIELD_ERROR(v, fieldNum, event); + + LIVEAPI_ENSURE_TYPE(v, obj, OT_INTEGER, event, fieldNum); + event->set_intvalue(_integer(obj)); + + break; + } + case rtech::liveapi::PlayerStatChanged::kFloatValueFieldNumber: + { + if (event->newValue_case() != rtech::liveapi::PlayerStatChanged::NEWVALUE_NOT_SET) + LIVEAPI_ONEOF_FIELD_ERROR(v, fieldNum, event); + + LIVEAPI_ENSURE_TYPE(v, obj, OT_FLOAT, event, fieldNum); + event->set_floatvalue(_float(obj)); + + break; + } + case rtech::liveapi::PlayerStatChanged::kBoolValueFieldNumber: + { + if (event->newValue_case() != rtech::liveapi::PlayerStatChanged::NEWVALUE_NOT_SET) + LIVEAPI_ONEOF_FIELD_ERROR(v, fieldNum, event); + + LIVEAPI_ENSURE_TYPE(v, obj, OT_BOOL, event, fieldNum); + event->set_boolvalue(_bool(obj)); + + break; + } + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +static void LiveAPI_SetCustomVectorField(google::protobuf::Struct* const structData, const Vector3D* const vecData) +{ + google::protobuf::Map* const fieldData = structData->mutable_fields(); + + (*fieldData)["x"].set_number_value(vecData->x); + (*fieldData)["y"].set_number_value(vecData->y); + (*fieldData)["z"].set_number_value(vecData->z); +} + +static bool LiveAPI_SetCustomTableFields(HSQUIRRELVM const v, google::protobuf::Struct* const structData, const SQTable* const tableData); +static bool LiveAPI_SetCustomArrayFields(HSQUIRRELVM const v, google::protobuf::ListValue* const listData, const SQArray* const arrayData); + +static int s_currentDepth = 0; + +static bool LiveAPI_SetCustomArrayFields(HSQUIRRELVM const v, google::protobuf::ListValue* const listData, const SQArray* const arrayData) +{ + CDepthCounter counter(s_currentDepth); + bool ranLoop = false; + + for (SQInteger i = 0; i < arrayData->Size(); i++) + { + const SQObject& valueObj = arrayData->_values[i]; + + if (sq_isnull(valueObj)) + continue; + + if (!ranLoop) + ranLoop = true; + + const SQObjectType valueType = sq_type(valueObj); + + switch (valueType) + { + case OT_BOOL: + listData->add_values()->set_bool_value(_bool(valueObj)); + break; + case OT_INTEGER: + listData->add_values()->set_number_value(_integer(valueObj)); + break; + case OT_FLOAT: + listData->add_values()->set_number_value(_float(valueObj)); + break; + case OT_STRING: + listData->add_values()->set_string_value(_stringval(valueObj)); + break; + case OT_VECTOR: + LiveAPI_SetCustomVectorField(listData->add_values()->mutable_struct_value(), _vector3d(valueObj)); + break; + case OT_ARRAY: + LIVEAPI_CHECK_RECURSION_DEPTH(v, counter.Get()); + + if (arrayData == _array(valueObj)) + { + v_SQVM_RaiseError(v, "Attempted to nest array at depth \"%i\" into itself at index \"%i\".", counter.Get(), i); + return false; + } + + if (!LiveAPI_SetCustomArrayFields(v, listData->add_values()->mutable_list_value(), _array(valueObj))) + return false; + + break; + case OT_TABLE: + LIVEAPI_CHECK_RECURSION_DEPTH(v, counter.Get()); + + if (!LiveAPI_SetCustomTableFields(v, listData->add_values()->mutable_struct_value(), _table(valueObj))) + return false; + + break; + default: + LIVEAPI_UNSUPPORTED_TYPE_ERROR(v, valueType, listData); + } + } + + if (!ranLoop) + LIVEAPI_EMPTY_TABLE_ERROR(v, listData); + + return true; +} + +static bool LiveAPI_SetCustomTableFields(HSQUIRRELVM const v, google::protobuf::Struct* const structData, const SQTable* const tableData) +{ + CDepthCounter counter(s_currentDepth); + bool ranLoop = false; + + SQ_FOR_EACH_TABLE(tableData, i) + { + const SQTable::_HashNode& node = tableData->_nodes[i]; + + if (sq_isnull(node.key)) + continue; + + if (!ranLoop) + ranLoop = true; + + const SQObjectType keyType = sq_type(node.key); + + if (keyType != OT_STRING) + { + v_SQVM_RaiseError(v, "Key in table must be a \"%s\", got \"%s\" for message \"%s\" at depth \"%i\" at index \"%i\".", + IdType2Name(OT_STRING), IdType2Name(keyType), structData->GetTypeName().c_str(), counter.Get(), i); + + return false; + } + + const SQObjectType valueType = sq_type(node.val); + + switch (valueType) + { + case OT_BOOL: + (*structData->mutable_fields())[_stringval(node.key)].set_bool_value(_bool(node.val)); + break; + case OT_INTEGER: + (*structData->mutable_fields())[_stringval(node.key)].set_number_value(_integer(node.val)); + break; + case OT_FLOAT: + (*structData->mutable_fields())[_stringval(node.key)].set_number_value(_float(node.val)); + break; + case OT_STRING: + (*structData->mutable_fields())[_stringval(node.key)].set_string_value(_stringval(node.val)); + break; + case OT_VECTOR: + LiveAPI_SetCustomVectorField((*structData->mutable_fields())[_stringval(node.key)].mutable_struct_value(), _vector3d(node.val)); + break; + case OT_ARRAY: + LIVEAPI_CHECK_RECURSION_DEPTH(v, counter.Get()); + + if (!LiveAPI_SetCustomArrayFields(v, (*structData->mutable_fields())[_stringval(node.key)].mutable_list_value(), _array(node.val))) + return false; + + break; + case OT_TABLE: + LIVEAPI_CHECK_RECURSION_DEPTH(v, counter.Get()); + + if (tableData == _table(node.val)) + { + v_SQVM_RaiseError(v, "Attempted to nest table at depth \"%i\" into itself at index \"%i\".", counter.Get(), i); + return false; + } + + if (!LiveAPI_SetCustomTableFields(v, (*structData->mutable_fields())[_stringval(node.key)].mutable_struct_value(), _table(node.val))) + return false; + + break; + default: + LIVEAPI_UNSUPPORTED_TYPE_ERROR(v, valueType, structData); + } + } + + if (!ranLoop) + LIVEAPI_EMPTY_TABLE_ERROR(v, structData); + + return true; +} + +static bool LiveAPI_HandleCustomEvent(HSQUIRRELVM const v, const SQObject& obj, rtech::liveapi::CustomEvent* const event, + const eLiveAPI_EventTypes eventType, const SQInteger fieldNum) +{ + LiveAPI_SetCommonMessageFields(event, eventType); + + switch (fieldNum) + { + case rtech::liveapi::CustomEvent::kNameFieldNumber: + LIVEAPI_ENSURE_TYPE(v, obj, OT_STRING, event, fieldNum); + event->set_name(_stringval(obj)); + + break; + case rtech::liveapi::CustomEvent::kDataFieldNumber: + LIVEAPI_ENSURE_TYPE(v, obj, OT_TABLE, event, fieldNum); + if (!LiveAPI_SetCustomTableFields(v, event->mutable_data(), _table(obj))) + return false; + + break; + default: + LIVEAPI_FIELD_ERROR(v, fieldNum, event); + } + + return true; +} + +/* + ███████╗██╗ ██╗███████╗███╗ ██╗████████╗ ██████╗ ██╗███████╗██████╗ █████╗ ████████╗ ██████╗██╗ ██╗███████╗██████╗ + ██╔════╝██║ ██║██╔════╝████╗ ██║╚══██╔══╝ ██╔══██╗██║██╔════╝██╔══██╗██╔══██╗╚══██╔══╝██╔════╝██║ ██║██╔════╝██╔══██╗ + █████╗ ██║ ██║█████╗ ██╔██╗ ██║ ██║ ██║ ██║██║███████╗██████╔╝███████║ ██║ ██║ ███████║█████╗ ██████╔╝ + ██╔══╝ ╚██╗ ██╔╝██╔══╝ ██║╚██╗██║ ██║ ██║ ██║██║╚════██║██╔═══╝ ██╔══██║ ██║ ██║ ██╔══██║██╔══╝ ██╔══██╗ + ███████╗ ╚████╔╝ ███████╗██║ ╚████║ ██║ ██████╔╝██║███████║██║ ██║ ██║ ██║ ╚██████╗██║ ██║███████╗██║ ██║ + ╚══════╝ ╚═══╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ +*/ + +static void LiveAPI_SendEvent(const google::protobuf::Message* const msg) +{ + s_liveAPIEvent.set_event_size(msg->ByteSize()); + s_liveAPIEvent.mutable_gamemessage()->PackFrom(*msg); + + LiveAPISystem()->LogEvent(&s_liveAPIEvent, &s_liveAPIEvent.gamemessage()); + s_liveAPIEvent.Clear(); +} + +static bool LiveAPI_HandleEventByCategory(HSQUIRRELVM const v, const SQTable* const table, const eLiveAPI_EventTypes eventType) +{ + google::protobuf::Message* msg = nullptr; + + SQ_FOR_EACH_TABLE(table, i) + { + const SQTable::_HashNode& node = table->_nodes[i]; + + if (sq_isnull(node.key)) + continue; + + if (!LiveAPI_CheckSwitchType(v, node.key)) + return false; + + const SQInteger fieldNum = _integer(node.key); + const SQObjectPtr& obj = node.val; + + bool ret = false; + + switch (eventType) + { + case eLiveAPI_EventTypes::init: + msg = &s_init; + ret = LiveAPI_HandleInitEvent(&s_init, eventType); + break; + case eLiveAPI_EventTypes::matchSetup: + msg = &s_matchSetup; + ret = LiveAPI_HandleMatchSetup(v, obj, &s_matchSetup, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::ammoUsed: + msg = &s_ammoUsed; + ret = LiveAPI_HandleAmmoUsed(v, obj, &s_ammoUsed, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::arenasItemDeselected: + msg = &s_arenasItemDeselected; + ret = LiveAPI_HandleInventoryChange(v, obj, &s_arenasItemDeselected, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::arenasItemSelected: + msg = &s_arenasItemSelected; + ret = LiveAPI_HandleInventoryChange(v, obj, &s_arenasItemSelected, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::bannerCollected: + msg = &s_bannerCollected; + ret = LiveAPI_HandleBannerCollected(v, obj, &s_bannerCollected, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::customEvent: + msg = &s_customEvent; + ret = LiveAPI_HandleCustomEvent(v, obj, &s_customEvent, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::inventoryPickUp: + msg = &s_inventoryPickUp; + ret = LiveAPI_HandleInventoryChange(v, obj, &s_inventoryPickUp, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::inventoryDrop: + msg = &s_inventoryDrop; + ret = LiveAPI_HandleInventoryDrop(v, obj, &s_inventoryDrop, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::inventoryUse: + msg = &s_inventoryUse; + ret = LiveAPI_HandleInventoryChange(v, obj, &s_inventoryUse, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::gameStateChanged: + msg = &s_gameStateChanged; + ret = LiveAPI_HandleGameStateChanged(v, obj, &s_gameStateChanged, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::matchStateEnd: + msg = &s_matchStateEnd; + ret = LiveAPI_HandleMatchStateEnd(v, obj, &s_matchStateEnd, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::characterSelected: + msg = &s_characterSelected; + ret = LiveAPI_HandleSimplePlayerMessage(v, obj, &s_characterSelected, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::warpGateUsed: + msg = &s_warpGateUsed; + ret = LiveAPI_HandleSimplePlayerMessage(v, obj, &s_warpGateUsed, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::wraithPortal: + msg = &s_wraithPortal; + ret = LiveAPI_HandleSimplePlayerMessage(v, obj, &s_wraithPortal, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerConnected: + msg = &s_playerConnected; + ret = LiveAPI_HandleSimplePlayerMessage(v, obj, &s_playerConnected, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerRevive: + msg = &s_playerRevive; + ret = LiveAPI_HandlePlayerRevive(v, obj, &s_playerRevive, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerDisconnected: + msg = &s_playerDisconnected; + ret = LiveAPI_HandlePlayerDisconnected(v, obj, &s_playerDisconnected, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerDamaged: + msg = &s_playerDamaged; + ret = LiveAPI_HandlePlayerDamaged(v, obj, &s_playerDamaged, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerDowned: + msg = &s_playerDowned; + ret = LiveAPI_HandlePlayerDowned(v, obj, &s_playerDowned, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerKilled: + msg = &s_playerKilled; + ret = LiveAPI_HandlePlayerKilled(v, obj, &s_playerKilled, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerAssist: + msg = &s_playerAssist; + ret = LiveAPI_HandlePlayerAssist(v, obj, &s_playerAssist, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerRespawnTeam: + msg = &s_playerRespawnTeam; + ret = LiveAPI_HandlePlayerRespawnTeam(v, obj, &s_playerRespawnTeam, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerStatChanged: + msg = &s_playerStatChanged; + ret = LiveAPI_HandlePlayerStatChanged(v, obj, &s_playerStatChanged, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerUpgradeTierChanged: + msg = &s_playerUpgradeTierChanged; + ret = LiveAPI_HandlePlayerUpgradeTierChanged(v, obj, &s_playerUpgradeTierChanged, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::legendUpgradeSelected: + msg = &s_legendUpgradeSelected; + ret = LiveAPI_HandleLegendUpgradeSelected(v, obj, &s_legendUpgradeSelected, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::gibraltarShieldAbsorbed: + msg = &s_gibraltarShieldAbsorbed; + ret = LiveAPI_HandleAbilityDamaged(v, obj, &s_gibraltarShieldAbsorbed, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::revenantForgedShadowDamaged: + msg = &s_revenantForgedShadowDamaged; + ret = LiveAPI_HandleAbilityDamaged(v, obj, &s_revenantForgedShadowDamaged, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::ringStartClosing: + msg = &s_ringStartClosing; + ret = LiveAPI_HandleDeathFieldStartClosing(v, obj, &s_ringStartClosing, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::ringFinishedClosing: + msg = &s_ringFinishedClosing; + ret = LiveAPI_HandleRingFinishedClosing(v, obj, &s_ringFinishedClosing, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::squadEliminated: + msg = &s_squadEliminated; + ret = LiveAPI_HandleSquadEliminated(v, obj, &s_squadEliminated, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::ziplineUsed: + msg = &s_ziplineUsed; + ret = LiveAPI_HandleLinkedEntityEvent(v, obj, &s_ziplineUsed, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::grenadeThrown: + msg = &s_grenadeThrown; + ret = LiveAPI_HandleLinkedEntityEvent(v, obj, &s_grenadeThrown, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::playerAbilityUsed: + msg = &s_playerAbilityUsed; + ret = LiveAPI_HandleLinkedEntityEvent(v, obj, &s_playerAbilityUsed, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::weaponSwitched: + msg = &s_weaponSwitched; + ret = LiveAPI_HandleWeaponSwitchedEvent(v, obj, &s_weaponSwitched, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::blackMarketAction: + msg = &s_blackMarketAction; + ret = LiveAPI_HandleBlackMarketActionEvent(v, obj, &s_blackMarketAction, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::observerSwitched: + msg = &s_observerSwitched; + ret = LiveAPI_HandleObserverSwitched(v, obj, &s_observerSwitched, eventType, fieldNum); + break; + case eLiveAPI_EventTypes::observerAnnotation: + msg = &s_observerAnnotation; + ret = LiveAPI_HandleObserverAnnotation(v, obj, &s_observerAnnotation, eventType, fieldNum); + break; + default: + v_SQVM_RaiseError(v, "Event type \"%d\" not found.", eventType); + return false; + } + + if (!ret) + { + Assert(msg); + + msg->Clear(); + return false; + } + } + + if (!msg) // Script bug, e.g. giving an empty table (either completely empty or filled with null) + { + v_SQVM_RaiseError(v, "Empty table on event type \"%d\".", eventType); + return false; + } + + LiveAPI_SendEvent(msg); + msg->Clear(); + + return true; +} + +/* + █████╗ ██████╗ ███████╗████████╗██████╗ █████╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗ + ██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝ + ███████║██████╔╝███████╗ ██║ ██████╔╝███████║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗ + ██╔══██║██╔══██╗╚════██║ ██║ ██╔══██╗██╔══██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║ + ██║ ██║██████╔╝███████║ ██║ ██║ ██║██║ ██║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║ + ╚═╝ ╚═╝╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ + Code exposed to scripts +*/ + +namespace VScriptCode +{ + namespace Server + { + SQRESULT LiveAPI_IsValidToRun(HSQUIRRELVM v); + SQRESULT LiveAPI_LogRaw(HSQUIRRELVM v); + + SQRESULT LiveAPI_StartLogging(HSQUIRRELVM v); + SQRESULT LiveAPI_StopLogging(HSQUIRRELVM v); + } +} + +SQRESULT VScriptCode::Server::LiveAPI_IsValidToRun(HSQUIRRELVM v) +{ + sq_pushbool(v, LiveAPISystem()->IsValidToRun()); + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); +} + +SQRESULT VScriptCode::Server::LiveAPI_LogRaw(HSQUIRRELVM v) +{ + if (!LiveAPISystem()->IsEnabled()) + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); + + SQRESULT result = SQ_OK; + const SQObjectPtr& object = v->GetUp(-2); + + if (sq_istable(object)) + { + const SQTable* const table = _table(object); + SQInteger eventType = 0; + + if (SQ_FAILED(sq_getinteger(v, 3, &eventType))) + { + v_SQVM_ScriptError("Second argument must be an integer."); + result = SQ_FAIL; + } + else if (!LiveAPI_HandleEventByCategory(v, table, eLiveAPI_EventTypes(eventType))) + result = SQ_ERROR; + } + else + { + v_SQVM_ScriptError("First argument must be a table."); + result = SQ_FAIL; + } + + SCRIPT_CHECK_AND_RETURN(v, result); +} + +SQRESULT VScriptCode::Server::LiveAPI_StartLogging(HSQUIRRELVM v) +{ + LiveAPISystem()->CreateLogger(); + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); +} + +SQRESULT VScriptCode::Server::LiveAPI_StopLogging(HSQUIRRELVM v) +{ + LiveAPISystem()->DestroyLogger(); + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); +} + +void Script_RegisterLiveAPIFunctions(CSquirrelVM* const s) +{ + DEFINE_SERVER_SCRIPTFUNC_NAMED(s, LiveAPI_IsValidToRun, "Whether the LiveAPI system is enabled and able to run", "bool", ""); + DEFINE_SERVER_SCRIPTFUNC_NAMED(s, LiveAPI_LogRaw, "VM bridge to the LiveAPI logger from scripts", "void", "table< int, var > data, int eventType"); + + DEFINE_SERVER_SCRIPTFUNC_NAMED(s, LiveAPI_StartLogging, "Start the LiveAPI session logger", "void", ""); + DEFINE_SERVER_SCRIPTFUNC_NAMED(s, LiveAPI_StopLogging, "Stop the LiveAPI session logger", "void", ""); +} + +void Script_RegisterLiveAPIEnums(CSquirrelVM* const s) +{ + DEFINE_SCRIPTENUM_NAMED(s, "eLiveAPI_EventTypes", 0, + "ammoUsed", + "arenasItemDeselected", + "arenasItemSelected", + "bannerCollected", + "blackMarketAction", + //"changeCamera", + "characterSelected", + //"checkedState", + //"clientState", + //"customMatch_CreateLobby", + //"customMatch_GetLobbyPlayers", + //"customMatch_GetSettings", + //"customMatch_JoinLobby", + //"customMatch_KickPlayer", + //"customMatch_LeaveLobby", + //"customMatch_LobbyPlayer", + //"customMatch_LobbyPlayers", + //"customMatch_SendChat", + //"customMatch_SetMatchmaking", + //"customMatch_SetReady", + //"customMatch_SetSettings", + //"customMatch_SetTeam", + //"customMatch_SetTeamName", + "customEvent", + "datacenter", + //"gameConVar", + "gameStateChanged", + "gibraltarShieldAbsorbed", + //"globalVars", + "grenadeThrown", + "init", + "inventoryDrop", + "inventoryItem", + "inventoryPickUp", + "inventoryUse", + "legendUpgradeSelected", + "liveAPIEvent", + "loadoutConfiguration", + "matchSetup", + "matchStateEnd", + "observerAnnotation", + "observerSwitched", + //"pauseToggle", + "player", + "playerAbilityUsed", + "playerAssist", + "playerConnected", + "playerDamaged", + "playerDisconnected", + "playerDowned", + "playerKilled", + "playerRespawnTeam", + "playerRevive", + "playerStatChanged", + "playerUpgradeTierChanged", + //"request", + //"requestStatus", + //"response", + "revenantForgedShadowDamaged", + "ringFinishedClosing", + "ringStartClosing", + //"runCommand", + //"scriptCall", + "squadEliminated", + //"stateCheck", + //"svcMsgOverflow", + //"svcMsgRemoteScript", + "vector3", + "version", + "warpGateUsed", + "weaponSwitched", + "wraithPortal", + "ziplineUsed" + ); +} diff --git a/r5dev/game/server/liveapi/liveapi.h b/r5dev/game/server/liveapi/liveapi.h new file mode 100644 index 00000000..df589e9f --- /dev/null +++ b/r5dev/game/server/liveapi/liveapi.h @@ -0,0 +1,11 @@ +#ifndef SERVER_LIVEAPI_H +#define SERVER_LIVEAPI_H + +#define LIVEAPI_MAJOR_VERSION 0 +#define LIVEAPI_MINOR_VERSION 1 +#define LIVEAPI_REVISION "Rev: " MKSTRING(LIVEAPI_MAJOR_VERSION) "." MKSTRING(LIVEAPI_MINOR_VERSION) + +extern void Script_RegisterLiveAPIFunctions(CSquirrelVM* const s); +extern void Script_RegisterLiveAPIEnums(CSquirrelVM* const s); + +#endif // SERVER_LIVEAPI_H diff --git a/r5dev/game/server/movehelper_server.h b/r5dev/game/server/movehelper_server.h index 83604fe8..83b22e52 100644 --- a/r5dev/game/server/movehelper_server.h +++ b/r5dev/game/server/movehelper_server.h @@ -34,17 +34,16 @@ class VMoveHelperServer : public IDetour { virtual void GetAdr(void) const { - LogVarAdr("s_MoveHelperServer", reinterpret_cast(s_MoveHelperServer)); + LogVarAdr("s_MoveHelperServer", s_MoveHelperServer); } virtual void GetFun(void) const { } virtual void GetVar(void) const { - CMemory pFunc = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 85 C0 0F 84 ?? ?? ?? ?? 48 8B 47 10").FollowNearCallSelf(); + const CMemory pFunc = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 85 C0 0F 84 ?? ?? ?? ?? 48 8B 47 10").FollowNearCallSelf(); s_MoveHelperServer = pFunc.FindPattern("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/networkproperty.cpp b/r5dev/game/server/networkproperty.cpp index 1bf9d6d0..528c06d6 100644 --- a/r5dev/game/server/networkproperty.cpp +++ b/r5dev/game/server/networkproperty.cpp @@ -9,8 +9,3 @@ #include "public/basehandle.h" #include "baseentity.h" #include "networkproperty.h" - -edict_t CServerNetworkProperty::GetEdict(void) const -{ - return m_edict; -} diff --git a/r5dev/game/server/networkproperty.h b/r5dev/game/server/networkproperty.h index 5ee5c7d5..ba11e949 100644 --- a/r5dev/game/server/networkproperty.h +++ b/r5dev/game/server/networkproperty.h @@ -19,7 +19,7 @@ class CServerNetworkProperty : IServerNetworkable { public: - edict_t GetEdict() const; + inline edict_t GetEdict(void) const { return m_edict; } private: CBaseEntity* m_pOuter; diff --git a/r5dev/game/server/physics_main.cpp b/r5dev/game/server/physics_main.cpp index f3ee0e49..761da69f 100644 --- a/r5dev/game/server/physics_main.cpp +++ b/r5dev/game/server/physics_main.cpp @@ -12,12 +12,14 @@ #include "engine/client/client.h" #include "game/server/util_server.h" +static ConVar sv_simulateBots("sv_simulateBots", "1", FCVAR_RELEASE, "Simulate user commands for bots on the server."); + //----------------------------------------------------------------------------- // Purpose: Runs the command simulation for fake players //----------------------------------------------------------------------------- void Physics_RunBotSimulation(bool bSimulating) { - if (!sv_simulateBots->GetBool()) + if (!sv_simulateBots.GetBool()) return; for (int i = 0; i < g_ServerGlobalVariables->m_nMaxClients; i++) @@ -45,12 +47,7 @@ void Physics_RunThinkFunctions(bool bSimulating) } /////////////////////////////////////////////////////////////////////////////// -void VPhysics_Main::Attach() const +void VPhysics_Main::Detour(const bool bAttach) const { - DetourAttach(&v_Physics_RunThinkFunctions, &Physics_RunThinkFunctions); + DetourSetup(&v_Physics_RunThinkFunctions, &Physics_RunThinkFunctions, bAttach); } - -void VPhysics_Main::Detach() const -{ - DetourDetach(&v_Physics_RunThinkFunctions, &Physics_RunThinkFunctions); -} \ No newline at end of file diff --git a/r5dev/game/server/physics_main.h b/r5dev/game/server/physics_main.h index 091d38e6..86bc82bb 100644 --- a/r5dev/game/server/physics_main.h +++ b/r5dev/game/server/physics_main.h @@ -7,7 +7,6 @@ #ifndef PHYSICS_MAIN_H #define PHYSICS_MAIN_H -inline CMemory p_Physics_RunThinkFunctions; inline void(*v_Physics_RunThinkFunctions)(bool bSimulating); /////////////////////////////////////////////////////////////////////////////// @@ -15,17 +14,15 @@ class VPhysics_Main : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Physics_RunThinkFunctions", p_Physics_RunThinkFunctions.GetPtr()); + LogFunAdr("Physics_RunThinkFunctions", v_Physics_RunThinkFunctions); } virtual void GetFun(void) const { - p_Physics_RunThinkFunctions = g_GameDll.FindPatternSIMD("88 4C 24 08 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??"); - v_Physics_RunThinkFunctions = p_Physics_RunThinkFunctions.RCast(); + g_GameDll.FindPatternSIMD("88 4C 24 08 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??").GetPtr(v_Physics_RunThinkFunctions); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/player.cpp b/r5dev/game/server/player.cpp index 2c896611..8bb00679 100644 --- a/r5dev/game/server/player.cpp +++ b/r5dev/game/server/player.cpp @@ -11,6 +11,8 @@ #include "gameinterface.h" #include "player.h" +#include "engine/server/server.h" + //------------------------------------------------------------------------------ // Purpose: executes a null command for this player //------------------------------------------------------------------------------ @@ -21,6 +23,9 @@ void CPlayer::RunNullCommand(void) float flOldFrameTime = (*g_pGlobals)->m_flFrameTime; float flOldCurTime = (*g_pGlobals)->m_flCurTime; + cmd.frametime = flOldFrameTime; + cmd.command_time = flOldCurTime; + pl.fixangle = FIXANGLE_NONE; EyeAngles(&cmd.viewangles); @@ -43,7 +48,7 @@ void CPlayer::RunNullCommand(void) //------------------------------------------------------------------------------ QAngle* CPlayer::EyeAngles(QAngle* pAngles) { - return v_CPlayer__EyeAngles(this, pAngles); + return CPlayer__EyeAngles(this, pAngles); } //------------------------------------------------------------------------------ @@ -59,10 +64,10 @@ inline void CPlayer::SetTimeBase(float flTimeBase) SetLastUCmdSimulationRemainderTime(flTime); - float flSomeTime = flTimeBase - m_lastUCmdSimulationRemainderTime * (*g_pGlobals)->m_flTickInterval; - if (flSomeTime >= 0.0) + float flSimulationTime = flTimeBase - m_lastUCmdSimulationRemainderTime * (*g_pGlobals)->m_flTickInterval; + if (flSimulationTime >= 0.0f) { - flTime = flSomeTime; + flTime = flSimulationTime; } SetTotalExtraClientCmdTimeAttempted(flTime); @@ -104,6 +109,65 @@ void CPlayer::SetTotalExtraClientCmdTimeAttempted(float flAttemptedTime) } } +//------------------------------------------------------------------------------ +// Purpose: clamps the unlag amount to sv_unlag + clockdrift +// Input : *cmd - +//------------------------------------------------------------------------------ +void CPlayer::ClampUnlag(CUserCmd* cmd) +{ + const CClient* client = g_pServer->GetClient(GetEdict() - 1); + const CNetChan* chan = client->GetNetChan(); + + const float clockDriftMsecs = sv_clockcorrection_msecs->GetFloat() / 1000.0f; + const float maxUnlag = sv_maxunlag->GetFloat(); + const float latencyAmount = Clamp(chan->GetLatency(FLOW_OUTGOING), 0.0f, maxUnlag); + const float serverTime = (*g_pGlobals)->m_flCurTime; + + // Command issue time from client, note that this value can be altered + // from the client, and therefore be used to exploit lag compensation. + const float commandTime = cmd->command_time; + const float lastCommandTime = m_LastCmd.command_time; + const float commandDelta = fabs(commandTime - serverTime); + + bool recomputeUnlag = false; + + // Check delta first, otherwise player could set commandTime to a fixed + // time and circumvent the system, as commandTime < lastCommandTime or + // commandTime > localCurTime will always fail. + if (commandDelta > maxUnlag) + { + // Too much to unlag, clamp to max !!! + recomputeUnlag = true; + DevWarning(eDLL_T::SERVER, "%s: commandDelta( %f ) > maxUnlag( %f ) !!!\n", + __FUNCTION__, commandDelta, maxUnlag); + } + else if (commandTime < (lastCommandTime - clockDriftMsecs)) + { + // Can never be lower than last !!! + recomputeUnlag = true; + DevWarning(eDLL_T::SERVER, "%s: cmd->command_time( %f ) < (m_LastCmd.command_time( %f ) - clockDriftMsecs( %f )) !!!\n", + __FUNCTION__, commandTime, lastCommandTime, clockDriftMsecs); + } + else if (commandTime > (serverTime + clockDriftMsecs)) + { + // Too far in the future, clamp to max !!! + recomputeUnlag = true; + DevWarning(eDLL_T::SERVER, "%s: cmd->command_time( %f ) > (g_pGlobals->m_flCurTime( %f ) + clockDriftMsecs( %f )) !!!\n", + __FUNCTION__, commandTime, serverTime, clockDriftMsecs); + } + + if (recomputeUnlag) + { + // Clamp it to server time minus latency. Note that it could still + // be lower than previous, hence the clamp on the recomputation. + float newCommandTime = Clamp(serverTime - latencyAmount, lastCommandTime, serverTime); + cmd->command_time = newCommandTime; + + DevWarning(eDLL_T::SERVER, "%s: Clamped cmd->command_time( %f ) to %f !!!\n", + __FUNCTION__, commandTime, newCommandTime); + } +} + //------------------------------------------------------------------------------ // Purpose: processes user cmd's for this player // Input : *cmds - @@ -112,6 +176,11 @@ void CPlayer::SetTotalExtraClientCmdTimeAttempted(float flAttemptedTime) // droppedPackets - // paused - //------------------------------------------------------------------------------ +// TODO: this code is experimental and has reported problems from players with +// high latency, needs to be debugged or a different approach needs to be taken! +// Defaulted to OFF for now +static ConVar sv_unlag_clamp("sv_unlag_clamp", "0", FCVAR_RELEASE, "Clamp the difference between the current time and received command time to sv_maxunlag + sv_clockcorrection_msecs."); + void CPlayer::ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds, int droppedPackets, bool paused) { @@ -125,24 +194,27 @@ void CPlayer::ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds, CUserCmd* cmd = &cmds[i]; const int commandNumber = cmd->command_number; - if (commandNumber > m_latestCommandQueued) + if (commandNumber <= m_latestCommandQueued) + continue; + + m_latestCommandQueued = commandNumber; + const int lastCommandNumber = lastCmd->command_number; + + if (lastCommandNumber == MAX_QUEUED_COMMANDS_PROCESS) + return; + + if (sv_unlag_clamp.GetBool()) + ClampUnlag(cmd); + + CUserCmd* queuedCmd = &m_Commands[lastCommandNumber]; + queuedCmd->Copy(cmd); + + if (++lastCmd->command_number > player_userCmdsQueueWarning->GetInt()) { - m_latestCommandQueued = commandNumber; - const int lastCommandNumber = lastCmd->command_number; + const float curTime = float(Plat_FloatTime()); - if (lastCommandNumber == MAX_QUEUED_COMMANDS_PROCESS) - return; - - CUserCmd* queuedCmd = &m_Commands[lastCommandNumber]; - queuedCmd->Copy(cmd); - - if (++lastCmd->command_number > player_userCmdsQueueWarning->GetInt()) - { - const float curTime = float(Plat_FloatTime()); - - if ((curTime - m_lastCommandCountWarnTime) > 0.5f) - m_lastCommandCountWarnTime = curTime; - } + if ((curTime - m_lastCommandCountWarnTime) > 0.5f) + m_lastCommandCountWarnTime = curTime; } } @@ -157,7 +229,7 @@ void CPlayer::ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds, //------------------------------------------------------------------------------ void CPlayer::PlayerRunCommand(CUserCmd* pUserCmd, IMoveHelper* pMover) { - v_CPlayer__PlayerRunCommand(this, pUserCmd, pMover); + CPlayer__PlayerRunCommand(this, pUserCmd, pMover); } //------------------------------------------------------------------------------ @@ -167,4 +239,50 @@ void CPlayer::PlayerRunCommand(CUserCmd* pUserCmd, IMoveHelper* pMover) void CPlayer::SetLastUserCommand(CUserCmd* pUserCmd) { m_LastCmd.Copy(pUserCmd); -} \ No newline at end of file +} + +/* +===================== +CC_CreateFakePlayer_f + + Creates a fake player + on the server +===================== +*/ +static void CC_CreateFakePlayer_f(const CCommand& args) +{ + if (!g_pServer->IsActive()) + return; + + if (args.ArgC() < 3) + { + Msg(eDLL_T::SERVER, "usage 'sv_addbot': name(string) teamid(int)\n"); + return; + } + + const int numPlayers = g_pServer->GetNumClients(); + + // Already at max, don't create. + if (numPlayers >= g_ServerGlobalVariables->m_nMaxClients) + return; + + const char* playerName = args.Arg(1); + + int teamNum = atoi(args.Arg(2)); + const int maxTeams = int(g_pServer->GetMaxTeams()) + 1; + + // Clamp team count, going above the limit will + // cause a crash. Going below 0 means that the + // engine will assign the bot to the last team. + if (teamNum > maxTeams) + teamNum = maxTeams; + + g_pEngineServer->LockNetworkStringTables(true); + + const edict_t nHandle = g_pEngineServer->CreateFakeClient(playerName, teamNum); + g_pServerGameClients->ClientFullyConnect(nHandle, false); + + g_pEngineServer->LockNetworkStringTables(false); +} + +static ConCommand sv_addbot("sv_addbot", CC_CreateFakePlayer_f, "Creates a bot on the server", FCVAR_RELEASE); diff --git a/r5dev/game/server/player.h b/r5dev/game/server/player.h index 5e463963..1b6d05bf 100644 --- a/r5dev/game/server/player.h +++ b/r5dev/game/server/player.h @@ -251,6 +251,9 @@ public: void ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds, int droppedPackets, bool paused); + + void ClampUnlag(CUserCmd* cmd); + void PlayerRunCommand(CUserCmd* pUserCmd, IMoveHelper* pMover); void SetLastUserCommand(CUserCmd* pUserCmd); @@ -791,32 +794,25 @@ private: }; static_assert(sizeof(CPlayer) == 0x7EF0); // !TODO: backwards compatibility. -inline CMemory p_CPlayer__EyeAngles; -inline QAngle*(*v_CPlayer__EyeAngles)(CPlayer* pPlayer, QAngle* pAngles); - -inline CMemory p_CPlayer__PlayerRunCommand; -inline void(*v_CPlayer__PlayerRunCommand)(CPlayer* pPlayer, CUserCmd* pUserCmd, IMoveHelper* pMover); +inline QAngle*(*CPlayer__EyeAngles)(CPlayer* pPlayer, QAngle* pAngles); +inline void(*CPlayer__PlayerRunCommand)(CPlayer* pPlayer, CUserCmd* pUserCmd, IMoveHelper* pMover); /////////////////////////////////////////////////////////////////////////////// class VPlayer : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CPlayer::EyeAngles", p_CPlayer__EyeAngles.GetPtr()); - LogFunAdr("CPlayer::PlayerRunCommand", p_CPlayer__PlayerRunCommand.GetPtr()); + LogFunAdr("CPlayer::EyeAngles", CPlayer__EyeAngles); + LogFunAdr("CPlayer::PlayerRunCommand", CPlayer__PlayerRunCommand); } virtual void GetFun(void) const { - p_CPlayer__EyeAngles = g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 F2 0F 10 05 ?? ?? ?? ??"); - v_CPlayer__EyeAngles = p_CPlayer__EyeAngles.RCast(); - - p_CPlayer__PlayerRunCommand = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B 03 49 81 C6 ?? ?? ?? ??").FollowNearCallSelf(); - v_CPlayer__PlayerRunCommand = p_CPlayer__PlayerRunCommand.RCast(); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 30 F2 0F 10 05 ?? ?? ?? ??").GetPtr(CPlayer__EyeAngles); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B 03 49 81 C6 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(CPlayer__PlayerRunCommand); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/server/variant_t.cpp b/r5dev/game/server/variant_t.cpp new file mode 100644 index 00000000..32db0c9d --- /dev/null +++ b/r5dev/game/server/variant_t.cpp @@ -0,0 +1,332 @@ + ////////////////////////// variant_t implementation ////////////////////////// + +#include "entityoutput.h" +#include "baseentity.h" + +//----------------------------------------------------------------------------- +// Purpose: All types must be able to display as strings for debugging purposes. +// Output : Returns a pointer to the string that represents this value. +// +// NOTE: The returned pointer should not be stored by the caller as +// subsequent calls to this function will overwrite the contents +// of the buffer! +//----------------------------------------------------------------------------- +const char* variant_t::ToString(void) const +{ + static char szBuf[512]; + + switch (fieldType) + { + case FIELD_STRING: + { + return(STRING(iszVal)); + } + + case FIELD_BOOLEAN: + { + if (bVal == 0) + { + Q_strncpy(szBuf, "false", sizeof(szBuf)); + } + else + { + Q_strncpy(szBuf, "true", sizeof(szBuf)); + } + return(szBuf); + } + + case FIELD_INTEGER: + { + Q_snprintf(szBuf, sizeof(szBuf), "%i", iVal); + return(szBuf); + } + + case FIELD_FLOAT: + { + Q_snprintf(szBuf, sizeof(szBuf), "%g", flVal); + return(szBuf); + } + + case FIELD_COLOR32: + { + Q_snprintf(szBuf, sizeof(szBuf), "%d %d %d %d", (int)rgbaVal.r, (int)rgbaVal.g, (int)rgbaVal.b, (int)rgbaVal.a); + return(szBuf); + } + + case FIELD_VECTOR: + { + Q_snprintf(szBuf, sizeof(szBuf), "[%g %g %g]", (double)vecVal[0], (double)vecVal[1], (double)vecVal[2]); + return(szBuf); + } + + case FIELD_VOID: + { + szBuf[0] = '\0'; + return(szBuf); + } + + case FIELD_EHANDLE: + { + const char* pszName = (Entity()) ? STRING(Entity()->GetEntityName()) : "<>"; + Q_strncpy(szBuf, pszName, 512); + return (szBuf); + } + } + + return("No conversion to string"); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the entity +//----------------------------------------------------------------------------- +void variant_t::SetEntity(CBaseEntity* val) +{ + eVal = val; + fieldType = FIELD_EHANDLE; +} + +// BUGBUG: Add support for function pointer save/restore to variants +// BUGBUG: Must pass datamap_t to read/write fields +//void variant_t::Set(fieldtype_t ftype, void* data) +//{ +// fieldType = ftype; +// +// switch (ftype) +// { +// case FIELD_BOOLEAN: bVal = *((bool*)data); break; +// case FIELD_CHARACTER: iVal = *((char*)data); break; +// case FIELD_SHORT: iVal = *((short*)data); break; +// case FIELD_INTEGER: iVal = *((int*)data); break; +// case FIELD_STRING: iszVal = *((string_t*)data); break; +// case FIELD_FLOAT: flVal = *((float*)data); break; +// case FIELD_COLOR32: rgbaVal = *((color32*)data); break; +// +// case FIELD_VECTOR: +// case FIELD_POSITION_VECTOR: +// { +// vecVal[0] = ((float*)data)[0]; +// vecVal[1] = ((float*)data)[1]; +// vecVal[2] = ((float*)data)[2]; +// break; +// } +// +// case FIELD_EHANDLE: eVal = *((EHANDLE*)data); break; +// case FIELD_CLASSPTR: eVal = *((CBaseEntity**)data); break; +// case FIELD_VOID: +// default: +// iVal = 0; fieldType = FIELD_VOID; +// break; +// } +//} + +//----------------------------------------------------------------------------- +// Purpose: Copies the value in the variant into a block of memory +// Input : *data - the block to write into +//----------------------------------------------------------------------------- +void variant_t::SetOther(void* data) +{ + switch (fieldType) + { + case FIELD_BOOLEAN: *((bool*)data) = bVal != 0; break; + case FIELD_CHARACTER: *((char*)data) = (char)iVal; break; + case FIELD_SHORT: *((short*)data) = (short)iVal; break; + case FIELD_INTEGER: *((int*)data) = iVal; break; + case FIELD_STRING: *((string_t*)data) = iszVal; break; + case FIELD_FLOAT: *((float*)data) = flVal; break; + case FIELD_COLOR32: *((color32*)data) = rgbaVal; break; + + case FIELD_VECTOR: + case FIELD_POSITION_VECTOR: + { + ((float*)data)[0] = vecVal[0]; + ((float*)data)[1] = vecVal[1]; + ((float*)data)[2] = vecVal[2]; + break; + } + + case FIELD_EHANDLE: *((EHANDLE*)data) = eVal; break; + case FIELD_CLASSPTR: *((CBaseEntity**)data) = eVal; break; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Converts the variant to a new type. This function defines which I/O +// types can be automatically converted between. Connections that require +// an unsupported conversion will cause an error message at runtime. +// Input : newType - the type to convert to +// Output : Returns true on success, false if the conversion is not legal +//----------------------------------------------------------------------------- +//bool variant_t::Convert(fieldtype_t newType) +//{ +// if (newType == fieldType) +// { +// return true; +// } +// +// // +// // Converting to a null value is easy. +// // +// if (newType == FIELD_VOID) +// { +// Set(FIELD_VOID, NULL); +// return true; +// } +// +// // +// // FIELD_INPUT accepts the variant type directly. +// // +// if (newType == FIELD_INPUT) +// { +// return true; +// } +// +// switch (fieldType) +// { +// case FIELD_INTEGER: +// { +// switch (newType) +// { +// case FIELD_FLOAT: +// { +// SetFloat((float)iVal); +// return true; +// } +// +// case FIELD_BOOLEAN: +// { +// SetBool(iVal != 0); +// return true; +// } +// } +// break; +// } +// +// case FIELD_FLOAT: +// { +// switch (newType) +// { +// case FIELD_INTEGER: +// { +// SetInt((int)flVal); +// return true; +// } +// +// case FIELD_BOOLEAN: +// { +// SetBool(flVal != 0); +// return true; +// } +// } +// break; +// } +// +// // +// // Everyone must convert from FIELD_STRING if possible, since +// // parameter overrides are always passed as strings. +// // +// case FIELD_STRING: +// { +// switch (newType) +// { +// case FIELD_INTEGER: +// { +// if (iszVal != NULL_STRING) +// { +// SetInt(atoi(STRING(iszVal))); +// } +// else +// { +// SetInt(0); +// } +// return true; +// } +// +// case FIELD_FLOAT: +// { +// if (iszVal != NULL_STRING) +// { +// SetFloat(atof(STRING(iszVal))); +// } +// else +// { +// SetFloat(0); +// } +// return true; +// } +// +// case FIELD_BOOLEAN: +// { +// if (iszVal != NULL_STRING) +// { +// SetBool(atoi(STRING(iszVal)) != 0); +// } +// else +// { +// SetBool(false); +// } +// return true; +// } +// +// case FIELD_VECTOR: +// { +// ::Vector3D tmpVec = vec3_origin; +// if (sscanf(STRING(iszVal), "[%f %f %f]", &tmpVec[0], &tmpVec[1], &tmpVec[2]) == 0) +// { +// // Try sucking out 3 floats with no []s +// sscanf(STRING(iszVal), "%f %f %f", &tmpVec[0], &tmpVec[1], &tmpVec[2]); +// } +// SetVector3D(tmpVec); +// return true; +// } +// +// case FIELD_COLOR32: +// { +// int nRed = 0; +// int nGreen = 0; +// int nBlue = 0; +// int nAlpha = 255; +// +// sscanf(STRING(iszVal), "%d %d %d %d", &nRed, &nGreen, &nBlue, &nAlpha); +// SetColor32(nRed, nGreen, nBlue, nAlpha); +// return true; +// } +// +// case FIELD_EHANDLE: +// { +// // convert the string to an entity by locating it by classname +// CBaseEntity* ent = NULL; +// if (iszVal != NULL_STRING) +// { +// // FIXME: do we need to pass an activator in here? +// ent = gEntList.FindEntityByName(NULL, iszVal); +// } +// SetEntity(ent); +// return true; +// } +// } +// +// break; +// } +// +// case FIELD_EHANDLE: +// { +// switch (newType) +// { +// case FIELD_STRING: +// { +// // take the entities targetname as the string +// string_t iszStr = NULL_STRING; +// if (eVal != NULL) +// { +// SetString(eVal->GetEntityName()); +// } +// return true; +// } +// } +// break; +// } +// } +// +// // invalid conversion +// return false; +//} diff --git a/r5dev/game/server/variant_t.h b/r5dev/game/server/variant_t.h new file mode 100644 index 00000000..a6269e2f --- /dev/null +++ b/r5dev/game/server/variant_t.h @@ -0,0 +1,127 @@ +//====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef VARIANT_T_H +#define VARIANT_T_H +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/string_t.h" +#include "mathlib/vmatrix.h" +#include "mathlib/color.h" +#include "game/shared/ehandle.h" +#include "datamap.h" + +// Microsoft headers define these for '_variant_t' +// undefine them to prevent clashes with our struct. +#if defined variant_t +#undef variant_t +#endif variant_t + +class CBaseEntity; + +//----------------------------------------------------------------------------- +// A variant class for passing data in entity input/output connections. +//----------------------------------------------------------------------------- +class variant_t +{ +public: + // constructor + variant_t() : fieldType(FIELD_VOID), iVal(0) {} + + inline bool Bool( void ) const { return( fieldType == FIELD_BOOLEAN ) ? bVal : false; } + inline const char *String( void ) const { return( fieldType == FIELD_STRING ) ? STRING(iszVal) : ToString(); } + inline string_t StringID( void ) const { return( fieldType == FIELD_STRING ) ? iszVal : NULL_STRING; } + inline int Int( void ) const { return( fieldType == FIELD_INTEGER ) ? iVal : 0; } + inline float Float( void ) const { return( fieldType == FIELD_FLOAT ) ? flVal : 0; } + inline const CHandle &Entity( void ) const; + inline color32 Color32( void ) const { return rgbaVal; } + inline void Vector3D( ::Vector3D &vec ) const; + + fieldtype_t FieldType( void ) { return fieldType; } + + void SetBool( bool b ) { bVal = b; fieldType = FIELD_BOOLEAN; } + void SetString( string_t str ) { iszVal = str, fieldType = FIELD_STRING; } + void SetInt( int val ) { iVal = val, fieldType = FIELD_INTEGER; } + void SetFloat( float val ) { flVal = val, fieldType = FIELD_FLOAT; } + void SetEntity( CBaseEntity *val ); + void SetVector3D( const ::Vector3D &val ) { vecVal[0] = val[ 0 ]; vecVal[ 1 ] = val[ 1 ]; vecVal[ 2 ] = val[ 2 ]; fieldType = FIELD_VECTOR; } + void SetPositionVector3D( const ::Vector3D &val ) { vecVal[ 0 ] = val[ 0 ]; vecVal[ 1 ] = val[ 1 ]; vecVal[ 2 ] = val[ 2 ]; fieldType = FIELD_POSITION_VECTOR; } + void SetColor32( color32 val ) { rgbaVal = val; fieldType = FIELD_COLOR32; } + void SetColor32( int r, int g, int b, int a ) { rgbaVal.r = (byte)r; rgbaVal.g = (byte)g; rgbaVal.b = (byte)b; rgbaVal.a = (byte)a; fieldType = FIELD_COLOR32; } + void Set( fieldtype_t ftype, void *data ); + void SetOther( void *data ); + bool Convert( fieldtype_t newType ); + + // !TODO[AMOS]: If found, make this a ptrand link it to the one in the game engine! + //static typedescription_t m_SaveBool[]; + //static typedescription_t m_SaveInt[]; + //static typedescription_t m_SaveFloat[]; + //static typedescription_t m_SaveEHandle[]; + //static typedescription_t m_SaveString[]; + //static typedescription_t m_SaveColor[]; + //static typedescription_t m_SaveVector[]; + //static typedescription_t m_SavePositionVector[]; + //static typedescription_t m_SaveVMatrix[]; + //static typedescription_t m_SaveVMatrixWorldspace[]; + //static typedescription_t m_SaveMatrix3x4Worldspace[]; + +protected: + //------------------------------------------------------------------------- + // Returns a string representation of the value without modifying the variant. + //------------------------------------------------------------------------- + const char *ToString( void ) const; + + friend class CVariantSaveDataOps; + +private: + union + { + bool bVal; + string_t iszVal; + int iVal; + float flVal; + float vecVal[3]; + color32 rgbaVal; + }; + CHandle eVal; // this can't be in the union because it has a constructor. + + fieldtype_t fieldType; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Returns this variant as a vector. +//----------------------------------------------------------------------------- +inline void variant_t::Vector3D( ::Vector3D &vec ) const +{ + if (( fieldType == FIELD_VECTOR ) || ( fieldType == FIELD_POSITION_VECTOR )) + { + vec[ 0 ] = vecVal[ 0 ]; + vec[ 1 ] = vecVal[ 1 ]; + vec[ 2 ] = vecVal[ 2 ]; + } + else + { + vec = vec3_origin; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Returns this variant as an EHANDLE. +//----------------------------------------------------------------------------- +inline const CHandle &variant_t::Entity( void ) const +{ + if ( fieldType == FIELD_EHANDLE ) + return eVal; + + static const CHandle hNull( INVALID_EHANDLE ); + return( hNull ); +} + +#endif // VARIANT_T_H diff --git a/r5dev/game/server/vscript_server.cpp b/r5dev/game/server/vscript_server.cpp index a3e0bada..2123d4d7 100644 --- a/r5dev/game/server/vscript_server.cpp +++ b/r5dev/game/server/vscript_server.cpp @@ -11,11 +11,30 @@ #include "core/stdafx.h" #include "engine/server/server.h" #include "game/shared/vscript_shared.h" +#include "vscript/vscript.h" #include "vscript/languages/squirrel_re/include/sqvm.h" +#include "liveapi/liveapi.h" #include "vscript_server.h" #include -#include +#include + +/* +===================== +SQVM_ServerScript_f + + Executes input on the + VM in SERVER context. +===================== +*/ +static void SQVM_ServerScript_f(const CCommand& args) +{ + if (args.ArgC() >= 2) + { + Script_Execute(args.ArgS(), SQCONTEXT::SERVER); + } +} +static ConCommand script("script", SQVM_ServerScript_f, "Run input code as SERVER script on the VM", FCVAR_DEVELOPMENTONLY | FCVAR_GAMEDLL | FCVAR_CHEAT | FCVAR_SERVER_FRAME_THREAD); namespace VScriptCode { @@ -29,35 +48,40 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT CreateServer(HSQUIRRELVM v) { - SQChar* serverName = sq_getstring(v, 1); - SQChar* serverDescription = sq_getstring(v, 2); - SQChar* serverMapName = sq_getstring(v, 3); - SQChar* serverPlaylist = sq_getstring(v, 4); - EServerVisibility_t eServerVisibility = static_cast(sq_getinteger(v, 5)); + const SQChar* serverName = nullptr; + const SQChar* serverDescription = nullptr; + const SQChar* serverMapName = nullptr; + const SQChar* serverPlaylist = nullptr; + + sq_getstring(v, 2, &serverName); + sq_getstring(v, 3, &serverDescription); + sq_getstring(v, 4, &serverMapName); + sq_getstring(v, 5, &serverPlaylist); + + SQInteger serverVisibility = 0; + sq_getinteger(v, 6, &serverVisibility); if (!VALID_CHARSTAR(serverName) || !VALID_CHARSTAR(serverMapName) || !VALID_CHARSTAR(serverPlaylist)) { - return SQ_OK; + v_SQVM_ScriptError("Empty or null server criteria"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); } // Adjust browser settings. - std::lock_guard l(g_pServerListManager->m_Mutex); + NetGameServer_t& details = g_ServerHostManager.GetDetails(); - g_pServerListManager->m_Server.m_svHostName = serverName; - g_pServerListManager->m_Server.m_svDescription = serverDescription; - g_pServerListManager->m_Server.m_svHostMap = serverMapName; - g_pServerListManager->m_Server.m_svPlaylist = serverPlaylist; - g_pServerListManager->m_ServerVisibility = eServerVisibility; + details.name = serverName; + details.description = serverDescription; + details.map = serverMapName; + details.playlist = serverPlaylist; // Launch server. - g_pServerListManager->LaunchServer(g_pServer->IsActive()); + g_ServerHostManager.SetVisibility(ServerVisibility_e(serverVisibility)); + g_ServerHostManager.LaunchServer(g_pServer->IsActive()); - return SQ_OK; - - //v_SQVM_RaiseError(v, "\"%s\" is not supported on client builds.\n", "CreateServer"); - //return SQ_ERROR; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- // Purpose: shuts the server down and disconnects all clients @@ -67,7 +91,7 @@ namespace VScriptCode if (g_pHostState->m_bActiveGame) g_pHostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -75,16 +99,24 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT KickPlayerByName(HSQUIRRELVM v) { - SQChar* playerName = sq_getstring(v, 1); - SQChar* reason = sq_getstring(v, 2); + const SQChar* playerName = nullptr; + const SQChar* reason = nullptr; + + sq_getstring(v, 2, &playerName); + sq_getstring(v, 3, &reason); + + if (!VALID_CHARSTAR(playerName)) + { + v_SQVM_ScriptError("Empty or null player name"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } // Discard empty strings, this will use the default message instead. if (!VALID_CHARSTAR(reason)) reason = nullptr; - g_pBanSystem->KickPlayerByName(playerName, reason); - - return SQ_OK; + g_BanSystem.KickPlayerByName(playerName, reason); + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -92,16 +124,24 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT KickPlayerById(HSQUIRRELVM v) { - SQChar* playerHandle = sq_getstring(v, 1); - SQChar* reason = sq_getstring(v, 2); + const SQChar* playerHandle = nullptr; + const SQChar* reason = nullptr; + + sq_getstring(v, 2, &playerHandle); + sq_getstring(v, 3, &reason); + + if (!VALID_CHARSTAR(playerHandle)) + { + v_SQVM_ScriptError("Empty or null player handle"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } // Discard empty strings, this will use the default message instead. if (!VALID_CHARSTAR(reason)) reason = nullptr; - g_pBanSystem->KickPlayerById(playerHandle, reason); - - return SQ_OK; + g_BanSystem.KickPlayerById(playerHandle, reason); + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -109,16 +149,24 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT BanPlayerByName(HSQUIRRELVM v) { - SQChar* playerName = sq_getstring(v, 1); - SQChar* reason = sq_getstring(v, 2); + const SQChar* playerName = nullptr; + const SQChar* reason = nullptr; + + sq_getstring(v, 2, &playerName); + sq_getstring(v, 3, &reason); + + if (!VALID_CHARSTAR(playerName)) + { + v_SQVM_ScriptError("Empty or null player name"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } // Discard empty strings, this will use the default message instead. if (!VALID_CHARSTAR(reason)) reason = nullptr; - g_pBanSystem->BanPlayerByName(playerName, reason); - - return SQ_OK; + g_BanSystem.BanPlayerByName(playerName, reason); + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -126,16 +174,24 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT BanPlayerById(HSQUIRRELVM v) { - SQChar* playerHandle = sq_getstring(v, 1); - SQChar* reason = sq_getstring(v, 2); + const SQChar* playerHandle = nullptr; + const SQChar* reason = nullptr; + + sq_getstring(v, 2, &playerHandle); + sq_getstring(v, 3, &reason); + + if (!VALID_CHARSTAR(playerHandle)) + { + v_SQVM_ScriptError("Empty or null player handle"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } // Discard empty strings, this will use the default message instead. if (!VALID_CHARSTAR(reason)) reason = nullptr; - g_pBanSystem->BanPlayerById(playerHandle, reason); - - return SQ_OK; + g_BanSystem.BanPlayerById(playerHandle, reason); + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -143,10 +199,18 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT UnbanPlayer(HSQUIRRELVM v) { - SQChar* szCriteria = sq_getstring(v, 1); - g_pBanSystem->UnbanPlayer(szCriteria); + const SQChar* szCriteria = nullptr; + sq_getstring(v, 2, &szCriteria); - return SQ_OK; + if (!VALID_CHARSTAR(szCriteria)) + { + v_SQVM_ScriptError("Empty or null player criteria"); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + } + + g_BanSystem.UnbanPlayer(szCriteria); + + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -155,7 +219,7 @@ namespace VScriptCode SQRESULT GetNumHumanPlayers(HSQUIRRELVM v) { sq_pushinteger(v, g_pServer->GetNumHumanPlayers()); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -164,7 +228,7 @@ namespace VScriptCode SQRESULT GetNumFakeClients(HSQUIRRELVM v) { sq_pushinteger(v, g_pServer->GetNumFakeClients()); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -173,9 +237,9 @@ namespace VScriptCode SQRESULT IsServerActive(HSQUIRRELVM v) { bool isActive = g_pServer->IsActive(); - sq_pushbool(v, isActive); - return SQ_OK; + + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -184,7 +248,7 @@ namespace VScriptCode SQRESULT IsDedicated(HSQUIRRELVM v) { sq_pushbool(v, ::IsDedicated()); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } } } @@ -198,6 +262,13 @@ void Script_RegisterServerFunctions(CSquirrelVM* s) Script_RegisterCommonAbstractions(s); Script_RegisterCoreServerFunctions(s); Script_RegisterAdminPanelFunctions(s); + + Script_RegisterLiveAPIFunctions(s); +} + +void Script_RegisterServerEnums(CSquirrelVM* const s) +{ + Script_RegisterLiveAPIEnums(s); } //--------------------------------------------------------------------------------- diff --git a/r5dev/game/server/vscript_server.h b/r5dev/game/server/vscript_server.h index fc22dc32..d75cd394 100644 --- a/r5dev/game/server/vscript_server.h +++ b/r5dev/game/server/vscript_server.h @@ -1,5 +1,6 @@ #ifndef VSCRIPT_SERVER_H #define VSCRIPT_SERVER_H +#include "vscript/languages/squirrel_re/vsquirrel.h" namespace VScriptCode { @@ -26,6 +27,8 @@ void Script_RegisterServerFunctions(CSquirrelVM* s); void Script_RegisterCoreServerFunctions(CSquirrelVM* s); void Script_RegisterAdminPanelFunctions(CSquirrelVM* s); +void Script_RegisterServerEnums(CSquirrelVM* const s); + #define DEFINE_SERVER_SCRIPTFUNC_NAMED(s, functionName, helpString, \ returnType, parameters) \ s->RegisterFunction(#functionName, MKSTRING(Script_##functionName), \ diff --git a/r5dev/game/shared/ai_utility_shared.cpp b/r5dev/game/shared/ai_utility_shared.cpp index b34fb073..6650029d 100644 --- a/r5dev/game/shared/ai_utility_shared.cpp +++ b/r5dev/game/shared/ai_utility_shared.cpp @@ -21,6 +21,19 @@ #include "thirdparty/recast/Detour/Include/DetourCommon.h" #include "thirdparty/recast/Detour/Include/DetourNavMesh.h" +static ConVar ai_script_nodes_draw_range("ai_script_nodes_draw_range", "0", FCVAR_DEVELOPMENTONLY, "Debug draw AIN script nodes ranging from shift index to this cvar"); +static ConVar ai_script_nodes_draw_nearest("ai_script_nodes_draw_nearest", "1", FCVAR_DEVELOPMENTONLY, "Debug draw AIN script node links to nearest node (build order is used if null)"); + +static ConVar navmesh_debug_type("navmesh_debug_type", "0", FCVAR_DEVELOPMENTONLY, "NavMesh debug draw hull index", true, 0.f, true, 4.f, nullptr, "0 = small, 1 = med_short, 2 = medium, 3 = large, 4 = extra large"); +static ConVar navmesh_debug_tile_range("navmesh_debug_tile_range", "0", FCVAR_DEVELOPMENTONLY, "NavMesh debug draw tiles ranging from shift index to this cvar", true, 0.f, false, 0.f); +static ConVar navmesh_debug_camera_range("navmesh_debug_camera_range", "2000", FCVAR_DEVELOPMENTONLY, "Only debug draw tiles within this distance from camera origin", true, 0.f, false, 0.f); + +static ConVar navmesh_draw_bvtree("navmesh_draw_bvtree", "-1", FCVAR_DEVELOPMENTONLY, "Draws the BVTree of the NavMesh tiles", false, 0.f, false, 0.f, nullptr, "Index: >= 0 && < mesh->m_tileCount"); +static ConVar navmesh_draw_portal("navmesh_draw_portal", "-1", FCVAR_DEVELOPMENTONLY, "Draws the portal of the NavMesh tiles", false, 0.f, false, 0.f, nullptr, "Index: >= 0 && < mesh->m_tileCount"); +static ConVar navmesh_draw_polys("navmesh_draw_polys", "-1", FCVAR_DEVELOPMENTONLY, "Draws the polys of the NavMesh tiles", false, 0.f, false, 0.f, nullptr, "Index: >= 0 && < mesh->m_tileCount"); +static ConVar navmesh_draw_poly_bounds("navmesh_draw_poly_bounds", "-1", FCVAR_DEVELOPMENTONLY, "Draws the bounds of the NavMesh polys", false, 0.f, false, 0.f, nullptr, "Index: >= 0 && < mesh->m_tileCount"); +static ConVar navmesh_draw_poly_bounds_inner("navmesh_draw_poly_bounds_inner", "0", FCVAR_DEVELOPMENTONLY, "Draws the inner bounds of the NavMesh polys (requires navmesh_draw_poly_bounds)"); + //------------------------------------------------------------------------------ // Purpose: //------------------------------------------------------------------------------ @@ -38,39 +51,88 @@ static const fltx4 s_xMaxs = LoadAlignedSIMD(s_vMaxs); static const fltx4 s_xSubMask = LoadAlignedSIMD(s_vSubMask); //------------------------------------------------------------------------------ -// Purpose: draw AI script network -// Input : *pAINetwork - +// Purpose: run the NavMesh renderer //------------------------------------------------------------------------------ -void CAI_Utility::DrawAIScriptNetwork(const CAI_Network* pNetwork) const +void CAI_Utility::RunRenderFrame(void) +{ + const int iScriptNodeIndex = ai_script_nodes_draw->GetInt(); + const int iNavMeshBVTreeIndex = navmesh_draw_bvtree.GetInt(); + const int iNavMeshPortalIndex = navmesh_draw_portal.GetInt(); + const int iNavMeshPolyIndex = navmesh_draw_polys.GetInt(); + const int iNavMeshPolyBoundIndex = navmesh_draw_poly_bounds.GetInt(); + + if (iScriptNodeIndex <= -1 && + iNavMeshBVTreeIndex <= -1 && + iNavMeshPortalIndex <= -1 && + iNavMeshPolyIndex <= -1 && + iNavMeshPolyBoundIndex <= -1) + { + // Nothing to render. + return; + } + + const Vector3D& vCamera = MainViewOrigin(); + const QAngle& aCamera = MainViewAngles(); + + const Vector3D vNormal = vCamera - aCamera.GetNormal() * 256.0f; + const VPlane vCullPlane(vNormal, aCamera); + + const float flCameraRange = navmesh_debug_camera_range.GetFloat(); + const int nTileRange = navmesh_debug_tile_range.GetInt(); + const bool bUseDepthBuffer = r_debug_draw_depth_test.GetBool(); + + if (iScriptNodeIndex > -1) + g_pAIUtility->DrawAIScriptNetwork(*g_pAINetwork, vCamera, iScriptNodeIndex, flCameraRange, bUseDepthBuffer); + if (iNavMeshBVTreeIndex > -1) + g_pAIUtility->DrawNavMeshBVTree(nullptr, vCamera, vCullPlane, iNavMeshBVTreeIndex, flCameraRange, nTileRange, bUseDepthBuffer); + if (iNavMeshPortalIndex > -1) + g_pAIUtility->DrawNavMeshPortals(nullptr, vCamera, vCullPlane, iNavMeshPortalIndex, flCameraRange, nTileRange, bUseDepthBuffer); + if (iNavMeshPolyIndex > -1) + g_pAIUtility->DrawNavMeshPolys(nullptr, vCamera, vCullPlane, iNavMeshPolyIndex, flCameraRange, nTileRange, bUseDepthBuffer); + if (iNavMeshPolyBoundIndex > -1) + g_pAIUtility->DrawNavMeshPolyBoundaries(nullptr, vCamera, vCullPlane, iNavMeshPolyBoundIndex, flCameraRange, nTileRange, bUseDepthBuffer); +} + +//------------------------------------------------------------------------------ +// Purpose: draw AI script network +// Input : *pNetwork - +// &vCameraPos - +// iNodeIndex - +// flCameraRange - +// bUseDepthBuffer - +//------------------------------------------------------------------------------ +void CAI_Utility::DrawAIScriptNetwork( + const CAI_Network* pNetwork, + const Vector3D& vCameraPos, + const int iNodeIndex, + const float flCameraRange, + const bool bUseDepthBuffer) const { if (!pNetwork) return; // AI Network not build or loaded. - const bool bUseDepthBuffer = r_debug_draw_depth_test->GetBool(); - const bool bDrawNearest = ai_script_nodes_draw_nearest->GetBool(); - const int nNodeRange = ai_script_nodes_draw_range->GetInt(); - const float flCameraRange = navmesh_debug_camera_range->GetFloat(); - const Vector3D vCamera = MainViewOrigin(); + const bool bDrawNearest = ai_script_nodes_draw_nearest.GetBool(); + const int nNodeRange = ai_script_nodes_draw_range.GetInt(); OverlayBox_t::Transforms vTransforms; std::unordered_set uLinkSet; - for (int i = ai_script_nodes_draw->GetInt(), ns = pNetwork->GetNumScriptNodes(); i < ns; i++) + for (int i = iNodeIndex, ns = pNetwork->NumScriptNodes(); i < ns; i++) { if (nNodeRange && i > nNodeRange) break; const CAI_ScriptNode* pScriptNode = &pNetwork->m_ScriptNode[i]; - fltx4 xOrigin = LoadUnaligned3SIMD(&pScriptNode->m_vOrigin); - xOrigin = SubSIMD(xOrigin, s_xSubMask); // Subtract 25.f from our scalars to align box with node. + const fltx4 xOrigin = SubSIMD(// Subtract 25.f from our scalars to align box with node. + LoadUnaligned3SIMD(&pScriptNode->m_vOrigin), s_xSubMask); if (flCameraRange > 0.0f) { // Flip the script node Z axis with that of the camera, so that it won't be used for // the final distance computation. This allows for viewing the AI Network from above. - const fltx4 xOriginCamZ = SetComponentSIMD(xOrigin, 2, vCamera.z); + const fltx4 xOriginCamZ = SetComponentSIMD(xOrigin, 2, vCameraPos.z); - if (vCamera.DistTo(*reinterpret_cast(&xOriginCamZ)) > flCameraRange) + if (vCameraPos.DistTo(*reinterpret_cast(&xOriginCamZ)) > flCameraRange) continue; // Do not render if node is not within range set by cvar 'navmesh_debug_camera_range'. } @@ -79,7 +141,7 @@ void CAI_Utility::DrawAIScriptNetwork(const CAI_Network* pNetwork) const { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, - *reinterpret_cast(&xOrigin)); + *reinterpret_cast(&xOrigin)); v_RenderBox(vTransforms.mat, *reinterpret_cast(&s_xMins), *reinterpret_cast(&s_xMaxs), m_BoxColor, bUseDepthBuffer); @@ -89,7 +151,10 @@ void CAI_Utility::DrawAIScriptNetwork(const CAI_Network* pNetwork) const int nNearest = GetNearestNodeToPos(pNetwork, &pScriptNode->m_vOrigin); if (nNearest != NO_NODE) // NO_NODE = -1 { - auto p = uLinkSet.insert(_mm_extract_epi64(PackNodeLink(i, nNearest), 1)); + shortx8 packedLinks = PackNodeLink(i, nNearest); + packedLinks = _mm_srli_si128(packedLinks, 8); // Only the upper 64bits are used. + + auto p = uLinkSet.insert(reinterpret_cast(packedLinks)); if (p.second) // Only render if link hasn't already been rendered. { const CAI_ScriptNode* pNearestNode = &pNetwork->m_ScriptNode[nNearest]; @@ -104,22 +169,30 @@ void CAI_Utility::DrawAIScriptNetwork(const CAI_Network* pNetwork) const //------------------------------------------------------------------------------ // Purpose: draw NavMesh BVTree -// Input : *pMesh - +// Input : *pMesh - +// &vCameraPos - +// &vCullPlane - +// iBVTreeIndex - +// flCameraRange - +// nTileRange - +// bDepthBuffer - //------------------------------------------------------------------------------ -void CAI_Utility::DrawNavMeshBVTree(dtNavMesh* pMesh) const +void CAI_Utility::DrawNavMeshBVTree( + const dtNavMesh* pMesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iBVTreeIndex, + const float flCameraRange, + const int nTileRange, + const bool bDepthBuffer) const { if (!pMesh) - pMesh = GetNavMeshForHull(navmesh_debug_type->GetInt()); + pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt()); if (!pMesh) return; // NavMesh for hull not loaded. - const Vector3D vCamera = MainViewOrigin(); - const bool bDepthBuffer = r_debug_draw_depth_test->GetBool(); - const int nTileRange = navmesh_debug_tile_range->GetInt(); - const float flCameraRange = navmesh_debug_camera_range->GetFloat(); - OverlayBox_t::Transforms vTransforms; - for (int i = navmesh_draw_bvtree->GetInt(), nt = pMesh->getTileCount(); i < nt; ++i) + for (int i = iBVTreeIndex, nt = pMesh->getTileCount(); i < nt; ++i) { if (nTileRange > 0 && i > nTileRange) break; @@ -128,7 +201,7 @@ void CAI_Utility::DrawNavMeshBVTree(dtNavMesh* pMesh) const if (!pTile->header) continue; - if (!IsTileWithinRange(pTile, vCamera, flCameraRange)) + if (!IsTileWithinRange(pTile, vCullPlane, vCameraPos, flCameraRange)) continue; const float flCellSize = 1.0f / pTile->header->bvQuantFactor; @@ -158,21 +231,28 @@ void CAI_Utility::DrawNavMeshBVTree(dtNavMesh* pMesh) const //------------------------------------------------------------------------------ // Purpose: draw NavMesh portals -// Input : *pMesh - +// Input : *pMesh - +// &vCameraPos - +// &vCullPlane - +// iPortalIndex - +// flCameraRange - +// nTileRange - +// bDepthBuffer - //------------------------------------------------------------------------------ -void CAI_Utility::DrawNavMeshPortals(dtNavMesh* pMesh) const +void CAI_Utility::DrawNavMeshPortals(const dtNavMesh* pMesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iPortalIndex, + const float flCameraRange, + const int nTileRange, + const bool bDepthBuffer) const { if (!pMesh) - pMesh = GetNavMeshForHull(navmesh_debug_type->GetInt()); + pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt()); if (!pMesh) return; // NavMesh for hull not loaded. - const Vector3D vCamera = MainViewOrigin(); - const bool bDepthBuffer = r_debug_draw_depth_test->GetBool(); - const int nTileRange = navmesh_debug_tile_range->GetInt(); - const float flCameraRange = navmesh_debug_camera_range->GetFloat(); - - for (int i = navmesh_draw_portal->GetInt(), nt = pMesh->getTileCount(); i < nt; ++i) + for (int i = iPortalIndex, nt = pMesh->getTileCount(); i < nt; ++i) { if (nTileRange > 0 && i > nTileRange) break; @@ -181,7 +261,7 @@ void CAI_Utility::DrawNavMeshPortals(dtNavMesh* pMesh) const if (!pTile->header) continue; - if (!IsTileWithinRange(pTile, vCamera, flCameraRange)) + if (!IsTileWithinRange(pTile, vCullPlane, vCameraPos, flCameraRange)) continue; // Draw portals @@ -271,21 +351,28 @@ void CAI_Utility::DrawNavMeshPortals(dtNavMesh* pMesh) const //------------------------------------------------------------------------------ // Purpose: draw NavMesh polys -// Input : *pMesh - +// Input : *pMesh - +// &vCameraPos - +// &vCullPlane - +// iPolyIndex - +// flCameraRange - +// nTileRange - +// bDepthBuffer - //------------------------------------------------------------------------------ -void CAI_Utility::DrawNavMeshPolys(dtNavMesh* pMesh) const +void CAI_Utility::DrawNavMeshPolys(const dtNavMesh* pMesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iPolyIndex, + const float flCameraRange, + const int nTileRange, + const bool bDepthBuffer) const { if (!pMesh) - pMesh = GetNavMeshForHull(navmesh_debug_type->GetInt()); + pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt()); if (!pMesh) return; // NavMesh for hull not loaded. - const Vector3D vCamera = MainViewOrigin(); - const bool bDepthBuffer = r_debug_draw_depth_test->GetBool(); - const int nTileRange = navmesh_debug_tile_range->GetInt(); - const float flCameraRange = navmesh_debug_camera_range->GetFloat(); - - for (int i = navmesh_draw_polys->GetInt(); i < pMesh->getTileCount(); ++i) + for (int i = iPolyIndex; i < pMesh->getTileCount(); ++i) { if (nTileRange > 0 && i > nTileRange) break; @@ -294,7 +381,7 @@ void CAI_Utility::DrawNavMeshPolys(dtNavMesh* pMesh) const if (!pTile->header) continue; - if (!IsTileWithinRange(pTile, vCamera, flCameraRange)) + if (!IsTileWithinRange(pTile, vCullPlane, vCameraPos, flCameraRange)) continue; for (int j = 0; j < pTile->header->polyCount; j++) @@ -345,25 +432,33 @@ void CAI_Utility::DrawNavMeshPolys(dtNavMesh* pMesh) const //------------------------------------------------------------------------------ // Purpose : draw NavMesh poly boundaries -// Input : *pMesh - +// Input : *pMesh - +// &vCameraPos - +// &vCullPlane - +// iBoundaryIndex - +// flCameraRange - +// nTileRange - +// bDepthBuffer - //------------------------------------------------------------------------------ -void CAI_Utility::DrawNavMeshPolyBoundaries(dtNavMesh* pMesh) const +void CAI_Utility::DrawNavMeshPolyBoundaries(const dtNavMesh* pMesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iBoundaryIndex, + const float flCameraRange, + const int nTileRange, + const bool bDepthBuffer) const { static const float thr = 0.01f * 0.01f; Color col{ 20, 140, 255, 255 }; if (!pMesh) - pMesh = GetNavMeshForHull(navmesh_debug_type->GetInt()); + pMesh = GetNavMeshForHull(navmesh_debug_type.GetInt()); if (!pMesh) return; // NavMesh for hull not loaded. - const Vector3D vCamera = MainViewOrigin(); - const bool bDepthBuffer = r_debug_draw_depth_test->GetBool(); - const bool bDrawInner = navmesh_draw_poly_bounds_inner->GetBool(); - const int nTileRange = navmesh_debug_tile_range->GetInt(); - const float flCameraRange = navmesh_debug_camera_range->GetFloat(); + const bool bDrawInner = navmesh_draw_poly_bounds_inner.GetBool(); - for (int i = navmesh_draw_poly_bounds->GetInt(), nt = pMesh->getTileCount(); i < nt; ++i) + for (int i = iBoundaryIndex, nt = pMesh->getTileCount(); i < nt; ++i) { if (nTileRange > 0 && i > nTileRange) break; @@ -372,7 +467,7 @@ void CAI_Utility::DrawNavMeshPolyBoundaries(dtNavMesh* pMesh) const if (!pTile->header) continue; - if (!IsTileWithinRange(pTile, vCamera, flCameraRange)) + if (!IsTileWithinRange(pTile, vCullPlane, vCameraPos, flCameraRange)) continue; for (int j = 0; j < pTile->header->polyCount; ++j) @@ -457,9 +552,9 @@ void CAI_Utility::DrawNavMeshPolyBoundaries(dtNavMesh* pMesh) const // d - // Output : packed node set as i64x2 //------------------------------------------------------------------------------ -__m128i CAI_Utility::PackNodeLink(int32_t a, int32_t b, int32_t c, int32_t d) const +shortx8 CAI_Utility::PackNodeLink(int32_t a, int32_t b, int32_t c, int32_t d) const { - __m128i xResult = _mm_set_epi32(a, b, c, d); + shortx8 xResult = _mm_set_epi32(a, b, c, d); // We shuffle a b and c d if following condition is met, this is to // ensure we always end up with one possible combination of indices. @@ -478,16 +573,25 @@ __m128i CAI_Utility::PackNodeLink(int32_t a, int32_t b, int32_t c, int32_t d) co // flCameraRadius - // Output : true if within radius, false otherwise //------------------------------------------------------------------------------ -bool CAI_Utility::IsTileWithinRange(const dtMeshTile* pTile, const Vector3D& vCamera, const float flCameraRadius) const +bool CAI_Utility::IsTileWithinRange(const dtMeshTile* pTile, const VPlane& vPlane, const Vector3D& vCamera, const float flCameraRadius) const { - if (flCameraRadius <= 0.0f) - return false; - const fltx4 xMinBound = LoadGatherSIMD(pTile->header->bmin[0], pTile->header->bmin[1], vCamera.z, 0.0f); const fltx4 xMaxBound = LoadGatherSIMD(pTile->header->bmax[0], pTile->header->bmax[1], vCamera.z, 0.0f); - if (vCamera.DistTo(*reinterpret_cast(&xMinBound)) > flCameraRadius || - vCamera.DistTo(*reinterpret_cast(&xMaxBound)) > flCameraRadius) + const Vector3D* vecMinBound = reinterpret_cast(&xMinBound); + const Vector3D* vecMaxBound = reinterpret_cast(&xMaxBound); + + if (flCameraRadius > 0.0f) + { + // Too far from camera, do not render. + if (vCamera.DistTo(*vecMinBound) > flCameraRadius || + vCamera.DistTo(*vecMaxBound) > flCameraRadius) + return false; + } + + // Behind the camera, do not render. + if (vPlane.GetPointSide(*vecMinBound) != SIDE_FRONT || + vPlane.GetPointSide(*vecMaxBound) != SIDE_FRONT) return false; return true; diff --git a/r5dev/game/shared/ai_utility_shared.h b/r5dev/game/shared/ai_utility_shared.h index f381653e..d8e32b81 100644 --- a/r5dev/game/shared/ai_utility_shared.h +++ b/r5dev/game/shared/ai_utility_shared.h @@ -17,14 +17,49 @@ class CAI_Utility { public: CAI_Utility(void); - void DrawAIScriptNetwork(const CAI_Network* pAINetwork) const; - void DrawNavMeshBVTree(dtNavMesh* mesh = nullptr) const; - void DrawNavMeshPortals(dtNavMesh* mesh = nullptr) const; - void DrawNavMeshPolys(dtNavMesh* mesh = nullptr) const; - void DrawNavMeshPolyBoundaries(dtNavMesh* mesh = nullptr) const; - __m128i PackNodeLink(int32_t a, int32_t b, int32_t c = 0, int32_t d = 0) const; + void RunRenderFrame(void); + + void DrawAIScriptNetwork(const CAI_Network* pNetwork, + const Vector3D& vCameraPos, + const int iNodeIndex, + const float flCameraRange, + const bool bUseDepthBuffer) const; + + void DrawNavMeshBVTree(const dtNavMesh* mesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iBVTreeIndex, + const float flCameraRange, + const int nTileRange, + const bool bUseDepthBuffer) const; + + void DrawNavMeshPortals(const dtNavMesh* mesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iPortalIndex, + const float flCameraRange, + const int nTileRange, + const bool bUseDepthBuffer) const; + + void DrawNavMeshPolys(const dtNavMesh* mesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iPolyIndex, + const float flCameraRange, + const int nTileRange, + const bool bDepthBuffer) const; + + void DrawNavMeshPolyBoundaries(const dtNavMesh* mesh, + const Vector3D& vCameraPos, + const VPlane& vCullPlane, + const int iBoundaryIndex, + const float flCameraRange, + const int nTileRange, + const bool bDepthBuffer) const; + + shortx8 PackNodeLink(int32_t a, int32_t b, int32_t c = 0, int32_t d = 0) const; int GetNearestNodeToPos(const CAI_Network* pAINetwork, const Vector3D* vec) const; - bool IsTileWithinRange(const dtMeshTile* pTile, const Vector3D& vCamera, const float flCameraRadius) const; + bool IsTileWithinRange(const dtMeshTile* pTile, const VPlane& vPlane, const Vector3D& vCamera, const float flCameraRadius) const; private: Color m_BoxColor; diff --git a/r5dev/game/shared/animation.cpp b/r5dev/game/shared/animation.cpp index d69d2a54..6f368ba2 100644 --- a/r5dev/game/shared/animation.cpp +++ b/r5dev/game/shared/animation.cpp @@ -16,15 +16,10 @@ int CStudioHdr::LookupSequence(CStudioHdr* pStudio, const char* pszName) if (!pStudio->m_pMDLCache) return -1; // animations are unavailable for missing dynamic props! (mdl/error.rmdl). - return v_CStudioHdr__LookupSequence(pStudio, pszName); + return CStudioHdr__LookupSequence(pStudio, pszName); } -void VAnimation::Attach() const +void VAnimation::Detour(const bool bAttach) const { - DetourAttach(&v_CStudioHdr__LookupSequence, &CStudioHdr::LookupSequence); -} - -void VAnimation::Detach() const -{ - DetourDetach(&v_CStudioHdr__LookupSequence, &CStudioHdr::LookupSequence); + DetourSetup(&CStudioHdr__LookupSequence, &CStudioHdr::LookupSequence, bAttach); } diff --git a/r5dev/game/shared/animation.h b/r5dev/game/shared/animation.h index 0b9c0e13..70dee004 100644 --- a/r5dev/game/shared/animation.h +++ b/r5dev/game/shared/animation.h @@ -81,26 +81,22 @@ struct Player_AnimViewEntityData Vector3D animViewEntityBlendStartEyeAngles; }; - -inline CMemory p_CStudioHdr__LookupSequence; -inline int(*v_CStudioHdr__LookupSequence)(CStudioHdr* pStudio, const char* pszName); +inline int(*CStudioHdr__LookupSequence)(CStudioHdr* pStudio, const char* pszName); /////////////////////////////////////////////////////////////////////////////// class VAnimation : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CStudioHdr::LookupSequence", p_CStudioHdr__LookupSequence.GetPtr()); + LogFunAdr("CStudioHdr::LookupSequence", CStudioHdr__LookupSequence); } virtual void GetFun(void) const { - p_CStudioHdr__LookupSequence = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 4C 8B C2 48 8B 89 ?? ?? ?? ??"); - v_CStudioHdr__LookupSequence = p_CStudioHdr__LookupSequence.RCast(); /*40 53 48 83 EC 20 48 8B D9 4C 8B C2 48 8B 89 ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 4C 8B C2 48 8B 89 ?? ?? ?? ??").GetPtr(CStudioHdr__LookupSequence); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/shared/r1/weapon_bolt.cpp b/r5dev/game/shared/r1/weapon_bolt.cpp index 963544ef..74985793 100644 --- a/r5dev/game/shared/r1/weapon_bolt.cpp +++ b/r5dev/game/shared/r1/weapon_bolt.cpp @@ -13,19 +13,21 @@ CBaseEntity* CreateWeaponBolt(Vector3D* origin, Vector3D* end, __int64 unused, f // Code does NOT check for null, and performing inline assembly is kind of a waste. // This only happens when 'EntityFactoryDictionary' fails, which only happens when // there are no edict slots available anymore (usually a bug in scripts). - Error(eDLL_T::SERVER, EXIT_FAILURE, "Unable to create bolt for %s", UTIL_GetEntityScriptInfo(player)); + //Error(eDLL_T::SERVER, EXIT_FAILURE, "Unable to create bolt for %s", UTIL_GetEntityScriptInfo(player)); + + // Amos: The engine code has been modified on assembly level, function + // 'FireWeaponBolt' now checks the pointer returned by this function, + // and returns null if null (script should be able to handle this error + // or cause a soft crash if the entity handle is being used). + + Assert(0); } return weaponBolt; } //----------------------------------------------------------------------------- -void V_Weapon_Bolt::Attach() const +void V_Weapon_Bolt::Detour(const bool bAttach) const { - DetourAttach(&v_CreateWeaponBolt, &CreateWeaponBolt); -} - -void V_Weapon_Bolt::Detach() const -{ - DetourDetach(&v_CreateWeaponBolt, &CreateWeaponBolt); + DetourSetup(&v_CreateWeaponBolt, &CreateWeaponBolt, bAttach); } diff --git a/r5dev/game/shared/r1/weapon_bolt.h b/r5dev/game/shared/r1/weapon_bolt.h index 777d2c5d..7daf8eb6 100644 --- a/r5dev/game/shared/r1/weapon_bolt.h +++ b/r5dev/game/shared/r1/weapon_bolt.h @@ -13,7 +13,6 @@ CBaseEntity* CreateWeaponBolt(Vector3D* origin, Vector3D* end, __int64 unused, float scale, CPlayer* unkEnt, int a6, int modelindex, int a8, unsigned __int8 a9, unsigned int a10, CBaseEntity* weaponEnt); -inline CMemory p_CreateWeaponBolt; inline CBaseEntity*(*v_CreateWeaponBolt)( Vector3D* origin, Vector3D* end, __int64 unused, float scale, CPlayer* player, int a6, int modelindex, int a8, unsigned __int8 a9, unsigned int a10, CBaseEntity* weapon); @@ -24,23 +23,15 @@ class V_Weapon_Bolt : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CreateWeaponBolt", p_CreateWeaponBolt.GetPtr()); + LogFunAdr("CreateWeaponBolt", v_CreateWeaponBolt); } virtual void GetFun(void) const { } virtual void GetVar(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CreateWeaponBolt = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 55 41 56 48 81 EC ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CreateWeaponBolt = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 70"); -#endif - v_CreateWeaponBolt = p_CreateWeaponBolt.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 70").GetPtr(v_CreateWeaponBolt); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/shared/shareddefs.h b/r5dev/game/shared/shareddefs.h index f3119134..45078bbe 100644 --- a/r5dev/game/shared/shareddefs.h +++ b/r5dev/game/shared/shareddefs.h @@ -39,5 +39,12 @@ #define HITGROUP_GEAR 8 // alerts NPC, but doesn't do damage or bleed (1/100th damage) #define HITGROUP_COUNT 9 +typedef enum // !TODO[ AMOS ]: Confirm this! +{ + USE_OFF = 0, + USE_ON = 1, + USE_SET = 2, + USE_TOGGLE = 3 +} USE_TYPE; #endif // SHAREDDEFS_H \ No newline at end of file diff --git a/r5dev/game/shared/usercmd.cpp b/r5dev/game/shared/usercmd.cpp index d540ece3..ef1403ea 100644 --- a/r5dev/game/shared/usercmd.cpp +++ b/r5dev/game/shared/usercmd.cpp @@ -10,6 +10,11 @@ #include "game/shared/in_buttons.h" #include "game/shared/weapon_types.h" +ConVar usercmd_frametime_max("usercmd_frametime_max", "0.100" , FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The largest amount of simulation seconds a UserCmd can have." ); +ConVar usercmd_frametime_min("usercmd_frametime_min", "0.002857", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The smallest amount of simulation seconds a UserCmd can have."); + +ConVar usercmd_dualwield_enable("usercmd_dualwield_enable", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Allows setting dual wield cycle slots, and activating multiple inventory weapons from UserCmd."); + //----------------------------------------------------------------------------- // Purpose: Read in a delta compressed usercommand. // Input : *buf - @@ -30,6 +35,7 @@ int ReadUserCmd(bf_read* buf, CUserCmd* move, CUserCmd* from) // Viewangles must be normalized; applying invalid angles on the client // will result in undefined behavior, or a crash. move->viewangles.Normalize(); + move->pitchangles.Normalize(); // Some players abused a feature of the engine which allows you to perform // custom weapon activities. After some research, it appears that only the @@ -46,13 +52,13 @@ int ReadUserCmd(bf_read* buf, CUserCmd* move, CUserCmd* from) // it couldn't be circumvented by busting out the client side clamps. if (host_timescale->GetFloat() == 1.0f) move->frametime = clamp(move->frametime, - usercmd_frametime_min->GetFloat(), - usercmd_frametime_max->GetFloat()); + usercmd_frametime_min.GetFloat(), + usercmd_frametime_max.GetFloat()); // Checks are only required if cycleslot is valid; see 'CPlayer::UpdateWeaponSlots'. if (move->cycleslot != WEAPON_INVENTORY_SLOT_INVALID) { - const bool dualWieldEnabled = usercmd_dualwield_enable->GetBool(); + const bool dualWieldEnabled = usercmd_dualwield_enable.GetBool(); // Client could instruct the server to switch cycle slots for inventory // weapons, however, the client could also cycle to the dual wield slots. @@ -76,12 +82,7 @@ int ReadUserCmd(bf_read* buf, CUserCmd* move, CUserCmd* from) } //----------------------------------------------------------------------------- -void VUserCmd::Attach() const +void VUserCmd::Detour(const bool bAttach) const { - DetourAttach(&v_ReadUserCmd, &ReadUserCmd); -} - -void VUserCmd::Detach() const -{ - DetourDetach(&v_ReadUserCmd, &ReadUserCmd); + DetourSetup(&v_ReadUserCmd, &ReadUserCmd, bAttach); } diff --git a/r5dev/game/shared/usercmd.h b/r5dev/game/shared/usercmd.h index c8a43623..ae357700 100644 --- a/r5dev/game/shared/usercmd.h +++ b/r5dev/game/shared/usercmd.h @@ -13,21 +13,22 @@ #include "tier1/bitbuf.h" #include "mathlib/vector.h" +//------------------------------------------------------------------------------------- +// Console variables +//------------------------------------------------------------------------------------- +extern ConVar usercmd_frametime_max; +extern ConVar usercmd_frametime_min; + +extern ConVar usercmd_dualwield_enable; + //------------------------------------------------------------------------------------- // Forward declarations //------------------------------------------------------------------------------------- class CUserCmd; -inline CMemory p_CUserCmd__CUserCmd; -inline CUserCmd*(*v_CUserCmd__CUserCmd)(CUserCmd* pUserCmd); - -inline CMemory p_CUserCmd__Reset; -inline void(*v_CUserCmd__Reset)(CUserCmd* pUserCmd); - -inline CMemory p_CUserCmd__Copy; -inline CUserCmd*(*v_CUserCmd__Copy)(CUserCmd* pDest, CUserCmd* pSource); - -inline CMemory p_ReadUserCmd; +inline CUserCmd*(*CUserCmd__CUserCmd)(CUserCmd* pUserCmd); +inline void(*CUserCmd__Reset)(CUserCmd* pUserCmd); +inline CUserCmd*(*CUserCmd__Copy)(CUserCmd* pDest, CUserCmd* pSource); inline int(*v_ReadUserCmd)(bf_read* buf, CUserCmd* move, CUserCmd* from); //------------------------------------------------------------------------------------- @@ -38,17 +39,17 @@ class CUserCmd public: CUserCmd() // Cannot be constructed during DLL init. { - v_CUserCmd__CUserCmd(this); + CUserCmd__CUserCmd(this); } - inline CUserCmd* Copy(CUserCmd* pSource) { return v_CUserCmd__Copy(this, pSource); } - inline void Reset() { v_CUserCmd__Reset(this); } + inline CUserCmd* Copy(CUserCmd* pSource) { return CUserCmd__Copy(this, pSource); } + inline void Reset() { CUserCmd__Reset(this); } int32_t command_number; int32_t tick_count; float_t command_time; QAngle viewangles; - char pad_0x0018[12]; + QAngle pitchangles; // Pitch angles? See [r5apex_ds+705D80]. float_t forwardmove; float_t sidemove; float_t upmove; @@ -56,9 +57,9 @@ public: byte impulse; byte cycleslot; byte weaponindex; - __int16 weaponselect; + short weaponselect; bool bUnk39; - __int16 weaponactivity; + short weaponactivity; int nUnk3C; bool controllermode; bool fixangles; @@ -99,29 +100,21 @@ class VUserCmd : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CUserCmd::CUserCmd", p_CUserCmd__CUserCmd.GetPtr()); - LogFunAdr("CUserCmd::Reset", p_CUserCmd__Reset.GetPtr()); - LogFunAdr("CUserCmd::Copy", p_CUserCmd__Copy.GetPtr()); - LogFunAdr("ReadUserCmd", p_ReadUserCmd.GetPtr()); + LogFunAdr("CUserCmd::CUserCmd", CUserCmd__CUserCmd); + LogFunAdr("CUserCmd::Reset", CUserCmd__Reset); + LogFunAdr("CUserCmd::Copy", CUserCmd__Copy); + LogFunAdr("ReadUserCmd", v_ReadUserCmd); } virtual void GetFun(void) const { - p_CUserCmd__CUserCmd = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 81 C3 ?? ?? ?? ?? 48 83 EF 01 75 EB 33 C0").FollowNearCallSelf(); - v_CUserCmd__CUserCmd = p_CUserCmd__CUserCmd.RCast(); - - p_CUserCmd__Reset = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 8B DF 66 83 FE FF").FollowNearCallSelf(); - v_CUserCmd__Reset = p_CUserCmd__Reset.RCast(); - - p_CUserCmd__Copy = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 4C 8B 9B ?? ?? ?? ??").FollowNearCallSelf(); - v_CUserCmd__Copy = p_CUserCmd__Copy.RCast(); - - p_ReadUserCmd = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 4C 8B C6 48 81 C6 ?? ?? ?? ??").FollowNearCallSelf(); - v_ReadUserCmd = p_ReadUserCmd.RCast(); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 81 C3 ?? ?? ?? ?? 48 83 EF 01 75 EB 33 C0").FollowNearCallSelf().GetPtr(CUserCmd__CUserCmd); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 8B DF 66 83 FE FF").FollowNearCallSelf().GetPtr(CUserCmd__Reset); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 4C 8B 9B ?? ?? ?? ??").FollowNearCallSelf().GetPtr(CUserCmd__Copy); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 4C 8B C6 48 81 C6 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(v_ReadUserCmd); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/shared/util_shared.h b/r5dev/game/shared/util_shared.h index 0b854aaf..8c06bc8a 100644 --- a/r5dev/game/shared/util_shared.h +++ b/r5dev/game/shared/util_shared.h @@ -10,7 +10,6 @@ class CTraceFilterSimple; const char* UTIL_GetEntityScriptInfo(CBaseEntity* pEnt); -inline CMemory p_UTIL_GetEntityScriptInfo; inline const char*(*v_UTIL_GetEntityScriptInfo)(CBaseEntity* pEnt); inline CTraceFilterSimple* g_pTraceFilterSimpleVFTable = nullptr; @@ -44,21 +43,19 @@ class VUtil_Shared : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CTraceFilterSimple::`vftable'", reinterpret_cast(g_pTraceFilterSimpleVFTable)); - LogFunAdr("UTIL_GetEntityScriptInfo", p_UTIL_GetEntityScriptInfo.GetPtr()); + LogConAdr("CTraceFilterSimple::`vftable'", g_pTraceFilterSimpleVFTable); + LogFunAdr("UTIL_GetEntityScriptInfo", v_UTIL_GetEntityScriptInfo); } virtual void GetFun(void) const { } virtual void GetVar(void) const { - p_UTIL_GetEntityScriptInfo = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 4C 8B 5E ??").FollowNearCallSelf(); - v_UTIL_GetEntityScriptInfo = p_UTIL_GetEntityScriptInfo.RCast(); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 4C 8B 5E ??").FollowNearCallSelf().GetPtr(v_UTIL_GetEntityScriptInfo); } virtual void GetCon(void) const { g_pTraceFilterSimpleVFTable = g_GameDll.GetVirtualMethodTable(".?AVCTraceFilterSimple@@").RCast(); } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/game/shared/vscript_shared.cpp b/r5dev/game/shared/vscript_shared.cpp index 493169cb..2ac26e93 100644 --- a/r5dev/game/shared/vscript_shared.cpp +++ b/r5dev/game/shared/vscript_shared.cpp @@ -12,7 +12,7 @@ //=============================================================================// #include "core/stdafx.h" -#include "vpc/keyvalues.h" +#include "rtech/playlists/playlists.h" #include "engine/client/cl_main.h" #include "engine/cmodel_bsp.h" #include "vscript/languages/squirrel_re/include/sqvm.h" @@ -28,7 +28,7 @@ namespace VScriptCode SQRESULT GetSDKVersion(HSQUIRRELVM v) { sq_pushstring(v, SDK_VERSION, -1); - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -38,17 +38,20 @@ namespace VScriptCode { std::lock_guard l(g_InstalledMapsMutex); - if (g_InstalledMaps.empty()) - return SQ_OK; + if (g_InstalledMaps.IsEmpty()) + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); sq_newarray(v, 0); - for (const string& it : g_InstalledMaps) + + FOR_EACH_VEC(g_InstalledMaps, i) { - sq_pushstring(v, it.c_str(), -1); + const CUtlString& mapName = g_InstalledMaps[i]; + + sq_pushstring(v, mapName.String(), -1); sq_arrayappend(v, -2); } - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } //----------------------------------------------------------------------------- @@ -59,7 +62,7 @@ namespace VScriptCode std::lock_guard l(g_PlaylistsVecMutex); if (g_vAllPlaylists.empty()) - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); sq_newarray(v, 0); for (const string& it : g_vAllPlaylists) @@ -68,7 +71,19 @@ namespace VScriptCode sq_arrayappend(v, -2); } - return SQ_OK; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); + } + + SQRESULT ScriptError(HSQUIRRELVM v) + { + SQChar* pString = NULL; + SQInteger a4 = 0; + + if (SQVM_sprintf(v, 0, 1, &a4, &pString) < 0) + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); + + v_SQVM_ScriptError("%s", pString); + SCRIPT_CHECK_AND_RETURN(v, SQ_ERROR); } } } @@ -83,10 +98,12 @@ void Script_RegisterCommonAbstractions(CSquirrelVM* s) DEFINE_SHARED_SCRIPTFUNC_NAMED(s, GetAvailableMaps, "Gets an array of all available maps", "array< string >", ""); DEFINE_SHARED_SCRIPTFUNC_NAMED(s, GetAvailablePlaylists, "Gets an array of all available playlists", "array< string >", ""); + + DEFINE_SHARED_SCRIPTFUNC_NAMED(s, ScriptError, "", "void", "string format, ...") } //--------------------------------------------------------------------------------- -// Purpose: listen server constants (!!! only call on builds containing a listen server !!!) +// Purpose: listen server constants // Input : *s - //--------------------------------------------------------------------------------- void Script_RegisterListenServerConstants(CSquirrelVM* s) @@ -94,3 +111,37 @@ void Script_RegisterListenServerConstants(CSquirrelVM* s) const SQBool hasListenServer = !IsClientDLL(); s->RegisterConstant("LISTEN_SERVER", hasListenServer); } + +//--------------------------------------------------------------------------------- +// Purpose: server enums +// Input : *s - +//--------------------------------------------------------------------------------- +void Script_RegisterCommonEnums_Server(CSquirrelVM* const s) +{ + v_Script_RegisterCommonEnums_Server(s); + + if (ServerScriptRegisterEnum_Callback) + ServerScriptRegisterEnum_Callback(s); +} + +//--------------------------------------------------------------------------------- +// Purpose: client/ui enums +// Input : *s - +//--------------------------------------------------------------------------------- +void Script_RegisterCommonEnums_Client(CSquirrelVM* const s) +{ + v_Script_RegisterCommonEnums_Client(s); + + const SQCONTEXT context = s->GetContext(); + + if (context == SQCONTEXT::CLIENT && ClientScriptRegisterEnum_Callback) + ClientScriptRegisterEnum_Callback(s); + else if (context == SQCONTEXT::UI && UIScriptRegisterEnum_Callback) + UIScriptRegisterEnum_Callback(s); +} + +void VScriptShared::Detour(const bool bAttach) const +{ + DetourSetup(&v_Script_RegisterCommonEnums_Server, &Script_RegisterCommonEnums_Server, bAttach); + DetourSetup(&v_Script_RegisterCommonEnums_Client, &Script_RegisterCommonEnums_Client, bAttach); +} diff --git a/r5dev/game/shared/vscript_shared.h b/r5dev/game/shared/vscript_shared.h index ba0224fe..dd57ef08 100644 --- a/r5dev/game/shared/vscript_shared.h +++ b/r5dev/game/shared/vscript_shared.h @@ -3,11 +3,11 @@ #include "vscript/languages/squirrel_re/include/squirrel.h" #include "vscript/languages/squirrel_re/vsquirrel.h" -inline CMemory p_Script_Remote_BeginRegisteringFunctions; -inline void*(*Script_Remote_BeginRegisteringFunctions)(void); +inline void (*v_Script_RegisterCommonEnums_Server)(CSquirrelVM* const s); +inline void (*v_Script_RegisterCommonEnums_Client)(CSquirrelVM* const s); -inline CMemory p_RestoreRemoteChecksumsFromSaveGame; -inline void*(*RestoreRemoteChecksumsFromSaveGame)(void* a1, void* a2); +inline void*(*v_Script_Remote_BeginRegisteringFunctions)(void); +inline void*(*v_RestoreRemoteChecksumsFromSaveGame)(void* a1, void* a2); inline uint32_t* g_nServerRemoteChecksum = nullptr; inline uint32_t* g_nClientRemoteChecksum = nullptr; @@ -35,27 +35,32 @@ class VScriptShared : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Remote_BeginRegisteringFunctions", p_Script_Remote_BeginRegisteringFunctions.GetPtr()); - LogFunAdr("RestoreRemoteChecksumsFromSaveGame", p_RestoreRemoteChecksumsFromSaveGame.GetPtr()); - LogVarAdr("g_nServerRemoteChecksum", reinterpret_cast(g_nServerRemoteChecksum)); - LogVarAdr("g_nClientRemoteChecksum", reinterpret_cast(g_nClientRemoteChecksum)); + LogFunAdr("Script_RegisterCommonEnums_Server", v_Script_RegisterCommonEnums_Server); + LogFunAdr("Script_RegisterCommonEnums_Client", v_Script_RegisterCommonEnums_Client); + + LogFunAdr("Remote_BeginRegisteringFunctions", v_Script_Remote_BeginRegisteringFunctions); + LogFunAdr("RestoreRemoteChecksumsFromSaveGame", v_RestoreRemoteChecksumsFromSaveGame); + + LogVarAdr("g_nServerRemoteChecksum", g_nServerRemoteChecksum); + LogVarAdr("g_nClientRemoteChecksum", g_nClientRemoteChecksum); } virtual void GetFun(void) const { - p_Script_Remote_BeginRegisteringFunctions = g_GameDll.FindPatternSIMD("48 83 EC 28 83 3D ?? ?? ?? ?? ?? 74 10"); - p_RestoreRemoteChecksumsFromSaveGame = g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 41 54 48 83 EC 40"); + g_GameDll.FindPatternSIMD("E8 ? ? ? ? 48 8B CB E8 ? ? ? ? 48 8B 43 08 BF ? ? ? ? 48 8B 50 60 48 8D 05 ? ? ? ? 48 89 82 ? ? ? ? 65 48 8B 04 25 ? ? ? ? 48 03 38 8B 07 39 05 ? ? ? ? 0F 8F ? ? ? ? 48 8D 05 ? ? ? ? 48 8D 0D ? ? ? ? 48 89 05 ? ? ? ? E8 ? ? ? ? 48 8D 05 ? ? ? ? 89 35 ? ? ? ? 48 89 05 ? ? ? ? 4C 8D 35 ? ? ? ?") + .FollowNearCallSelf().GetPtr(v_Script_RegisterCommonEnums_Server); + g_GameDll.FindPatternSIMD("E8 ? ? ? ? 48 8B CB E8 ? ? ? ? 48 8B 43 08 BF ? ? ? ? 48 8B 50 60 48 8D 05 ? ? ? ? 48 89 82 ? ? ? ? 65 48 8B 04 25 ? ? ? ? 48 03 38 8B 07 39 05 ? ? ? ? 0F 8F ? ? ? ? 48 8D 05 ? ? ? ? 48 8D 0D ? ? ? ? 48 89 05 ? ? ? ? E8 ? ? ? ? 48 8D 05 ? ? ? ? 89 35 ? ? ? ? 48 89 05 ? ? ? ? 4C 8D 3D ? ? ? ?") + .FollowNearCallSelf().GetPtr(v_Script_RegisterCommonEnums_Client); - Script_Remote_BeginRegisteringFunctions = p_Script_Remote_BeginRegisteringFunctions.RCast(); /*48 83 EC 28 83 3D ?? ?? ?? ?? ?? 74 10*/ - RestoreRemoteChecksumsFromSaveGame = p_RestoreRemoteChecksumsFromSaveGame.RCast(); /*48 89 4C 24 ?? 41 54 48 83 EC 40*/ + g_GameDll.FindPatternSIMD("48 83 EC 28 83 3D ?? ?? ?? ?? ?? 74 10").GetPtr(v_Script_Remote_BeginRegisteringFunctions); + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 41 54 48 83 EC 40").GetPtr(v_RestoreRemoteChecksumsFromSaveGame); } virtual void GetVar(void) const { - g_nServerRemoteChecksum = p_RestoreRemoteChecksumsFromSaveGame.Offset(0x1C0).FindPatternSelf("48 8D 15", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_nClientRemoteChecksum = p_Script_Remote_BeginRegisteringFunctions.Offset(0x0).FindPatternSelf("89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); + g_nServerRemoteChecksum = CMemory(v_RestoreRemoteChecksumsFromSaveGame).Offset(0x1C0).FindPatternSelf("48 8D 15", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_nClientRemoteChecksum = CMemory(v_Script_Remote_BeginRegisteringFunctions).Offset(0x0).FindPatternSelf("89 05", CMemory::Direction::DOWN, 150).ResolveRelativeAddressSelf(0x2, 0x6).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/gameui/IBrowser.cpp b/r5dev/gameui/IBrowser.cpp index 4af5163e..b68b43a3 100644 --- a/r5dev/gameui/IBrowser.cpp +++ b/r5dev/gameui/IBrowser.cpp @@ -30,33 +30,32 @@ History: #include "engine/client/clientstate.h" #include "networksystem/serverlisting.h" #include "networksystem/pylon.h" +#include "networksystem/hostmanager.h" #include "networksystem/listmanager.h" -#include "vpc/keyvalues.h" +#include "rtech/playlists/playlists.h" #include "common/callback.h" #include "gameui/IBrowser.h" #include "public/edict.h" #include "game/shared/vscript_shared.h" +static ConCommand togglebrowser("togglebrowser", CBrowser::ToggleBrowser_f, "Show/hide the server browser", FCVAR_CLIENTDLL | FCVAR_RELEASE); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBrowser::CBrowser(void) - : m_pszBrowserLabel("Server Browser") - , m_bActivate(false) - , m_bInitialized(false) - , m_bReclaimFocus(false) - , m_bReclaimFocusTokenField(false) - , m_bQueryListNonRecursive(false) - , m_bQueryGlobalBanList(true) - , m_flFadeAlpha(0.f) - , m_HostRequestMessageColor(1.00f, 1.00f, 1.00f, 1.00f) - , m_ivHiddenServerMessageColor(0.00f, 1.00f, 0.00f, 1.00f) - , m_Style(ImGuiStyle_t::NONE) + : m_reclaimFocusOnTokenField(false) + , m_queryNewListNonRecursive(false) + , m_queryGlobalBanList(true) + , m_hostMessageColor(1.00f, 1.00f, 1.00f, 1.00f) + , m_hiddenServerMessageColor(0.00f, 1.00f, 0.00f, 1.00f) { - memset(m_szServerAddressBuffer, '\0', sizeof(m_szServerAddressBuffer)); - memset(m_szServerEncKeyBuffer, '\0', sizeof(m_szServerEncKeyBuffer)); + m_surfaceLabel = "Server Browser"; - m_rLockedIconBlob = GetModuleResource(IDB_PNG2); + memset(m_serverAddressTextBuf, '\0', sizeof(m_serverAddressTextBuf)); + memset(m_serverNetKeyTextBuf, '\0', sizeof(m_serverNetKeyTextBuf)); + + m_lockedIconDataResource = GetModuleResource(IDB_PNG2); } //----------------------------------------------------------------------------- @@ -64,10 +63,7 @@ CBrowser::CBrowser(void) //----------------------------------------------------------------------------- CBrowser::~CBrowser(void) { - if (m_idLockedIcon) - { - m_idLockedIcon->Release(); - } + Shutdown(); } //----------------------------------------------------------------------------- @@ -75,16 +71,26 @@ CBrowser::~CBrowser(void) //----------------------------------------------------------------------------- bool CBrowser::Init(void) { - SetStyleVar(); - m_szMatchmakingHostName = pylon_matchmaking_hostname->GetString(); + SetStyleVar(928.f, 524.f, -500.f, 50.f); - bool ret = LoadTextureBuffer(reinterpret_cast(m_rLockedIconBlob.m_pData), int(m_rLockedIconBlob.m_nSize), - &m_idLockedIcon, &m_rLockedIconBlob.m_nWidth, &m_rLockedIconBlob.m_nHeight); + bool ret = LoadTextureBuffer(reinterpret_cast(m_lockedIconDataResource.m_pData), int(m_lockedIconDataResource.m_nSize), + &m_lockedIconShaderResource, &m_lockedIconDataResource.m_nWidth, &m_lockedIconDataResource.m_nHeight); - IM_ASSERT(ret && m_idLockedIcon); + IM_ASSERT(ret && m_lockedIconShaderResource); return ret; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBrowser::Shutdown(void) +{ + if (m_lockedIconShaderResource) + { + m_lockedIconShaderResource->Release(); + } +} + //----------------------------------------------------------------------------- // Purpose: draws the main browser front-end //----------------------------------------------------------------------------- @@ -96,65 +102,51 @@ void CBrowser::RunFrame(void) //ImGui::ShowDemoWindow(); } - if (!m_bInitialized) + if (!m_initialized) { Init(); - m_bInitialized = true; + m_initialized = true; } - int nVars = 0; - float flWidth; - float flHeight; - if (m_Style == ImGuiStyle_t::MODERN) - { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_flFadeAlpha); nVars++; + Animate(); + RunTask(); - flWidth = 621.f; - flHeight = 532.f; + int baseWindowStyleVars = 0; + ImVec2 minBaseWindowRect; + + if (m_surfaceStyle == ImGuiStyle_t::MODERN) + { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); baseWindowStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++; + + minBaseWindowRect = ImVec2(621.f, 532.f); } else { - if (m_Style == ImGuiStyle_t::LEGACY) - { - flWidth = 619.f; - flHeight = 526.f; - } - else - { - flWidth = 618.f; - flHeight = 524.f; - } + minBaseWindowRect = m_surfaceStyle == ImGuiStyle_t::LEGACY + ? ImVec2(619.f, 526.f) + : ImVec2(618.f, 524.f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 6.f, 6.f }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_flFadeAlpha); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 6.f, 6.f }); baseWindowStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++; - ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f); baseWindowStyleVars++; } - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(flWidth, flHeight)); nVars++; - if (m_bActivate && m_bReclaimFocus) // Reclaim focus on window apparition. + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, minBaseWindowRect); baseWindowStyleVars++; + + if (m_activated && m_reclaimFocus) // Reclaim focus on window apparition. { ImGui::SetNextWindowFocus(); - m_bReclaimFocus = false; - } - - if (!ImGui::Begin(m_pszBrowserLabel, &m_bActivate, ImGuiWindowFlags_NoScrollbar, &ResetInput)) - { - ImGui::End(); - ImGui::PopStyleVar(nVars); - return; + m_reclaimFocus = false; } DrawSurface(); - - ImGui::End(); - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(baseWindowStyleVars); } //----------------------------------------------------------------------------- // Purpose: runs tasks for the browser while not being drawn -// (!!! RunTask and RunFrame must be called from the same thread !!!) //----------------------------------------------------------------------------- void CBrowser::RunTask() { @@ -167,118 +159,94 @@ void CBrowser::RunTask() bInitialized = true; } - if (timer.GetDurationInProgress().GetSeconds() > pylon_host_update_interval->GetDouble()) + if (timer.GetDurationInProgress().GetSeconds() > pylon_host_update_interval.GetFloat()) { UpdateHostingStatus(); timer.Start(); } - if (m_bActivate) + if (m_activated) { - if (m_bQueryListNonRecursive) + if (m_queryNewListNonRecursive) { - std::thread refresh(&CBrowser::RefreshServerList, g_pBrowser); - refresh.detach(); - - m_bQueryListNonRecursive = false; + m_queryNewListNonRecursive = false; + RefreshServerList(); } } - else // Refresh server list the next time 'm_bActivate' evaluates to true. + else // Refresh server list the next time 'm_activated' evaluates to true. { - m_bReclaimFocus = true; - m_bReclaimFocusTokenField = true; - m_bQueryListNonRecursive = true; + m_reclaimFocusOnTokenField = true; + m_queryNewListNonRecursive = true; } } //----------------------------------------------------------------------------- -// Purpose: think +// Purpose: draws the server browser's main surface +// Output : true if a frame has been drawn, false otherwise //----------------------------------------------------------------------------- -void CBrowser::Think(void) +bool CBrowser::DrawSurface(void) { - if (m_bActivate) + if (!ImGui::Begin(m_surfaceLabel, &m_activated, ImGuiWindowFlags_None, &ResetInput)) { - if (m_flFadeAlpha < 1.f) - { - m_flFadeAlpha += .05f; - m_flFadeAlpha = (std::min)(m_flFadeAlpha, 1.f); - } + ImGui::End(); + return false; } - else // Reset to full transparent. - { - if (m_flFadeAlpha > 0.f) - { - m_flFadeAlpha -= .05f; - m_flFadeAlpha = (std::max)(m_flFadeAlpha, 0.f); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: draws the compmenu -//----------------------------------------------------------------------------- -void CBrowser::DrawSurface(void) -{ - std::lock_guard l(m_Mutex); ImGui::BeginTabBar("CompMenu"); if (ImGui::BeginTabItem("Browsing")) { - BrowserPanel(); + DrawBrowserPanel(); ImGui::EndTabItem(); } #ifndef CLIENT_DLL if (ImGui::BeginTabItem("Hosting")) { - HostPanel(); + DrawHostPanel(); ImGui::EndTabItem(); } #endif // !CLIENT_DLL - if (ImGui::BeginTabItem("Settings")) - { - SettingsPanel(); - ImGui::EndTabItem(); - } + ImGui::EndTabBar(); + ImGui::End(); + + return true; } //----------------------------------------------------------------------------- // Purpose: draws the server browser section //----------------------------------------------------------------------------- -void CBrowser::BrowserPanel(void) +void CBrowser::DrawBrowserPanel(void) { ImGui::BeginGroup(); - m_imServerBrowserFilter.Draw(); + m_serverBrowserTextFilter.Draw(); ImGui::SameLine(); if (ImGui::Button("Refresh")) { - m_svServerListMessage.clear(); - - std::thread refresh(&CBrowser::RefreshServerList, this); - refresh.detach(); + m_serverListMessage.clear(); + RefreshServerList(); } ImGui::EndGroup(); - ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), "%s", m_svServerListMessage.c_str()); + ImGui::TextColored(ImVec4(1.00f, 0.00f, 0.00f, 1.00f), "%s", m_serverListMessage.c_str()); ImGui::Separator(); - int iVars = 0; // Eliminate borders around server list table. - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 0.f }); iVars++; + int windowStyleVars = 0; // Eliminate borders around server list table. + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 0.f }); windowStyleVars++; const float fFooterHeight = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); ImGui::BeginChild("##ServerBrowser_ServerList", { 0, -fFooterHeight }, true, ImGuiWindowFlags_AlwaysVerticalScrollbar); if (ImGui::BeginTable("##ServerBrowser_ServerListTable", 6, ImGuiTableFlags_Resizable)) { - int nVars = 0; - if (m_Style == ImGuiStyle_t::MODERN) + int frameStyleVars = 0; + if (m_surfaceStyle == ImGuiStyle_t::MODERN) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 8.f, 0.f }); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 8.f, 0.f }); frameStyleVars++; } else { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4.f, 0.f }); nVars++; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{ 4.f, 0.f }); frameStyleVars++; } ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch, 25); @@ -289,18 +257,44 @@ void CBrowser::BrowserPanel(void) ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 5); ImGui::TableHeadersRow(); - g_pServerListManager->m_Mutex.lock(); - for (const NetGameServer_t& server : g_pServerListManager->m_vServerList) - { - const char* pszHostName = server.m_svHostName.c_str(); - const char* pszHostMap = server.m_svHostMap.c_str(); - const char* pszPlaylist = server.m_svPlaylist.c_str(); - const char* pszHostPort = server.m_svGamePort.c_str(); + g_ServerListManager.m_Mutex.Lock(); + vector filteredServers; - if (m_imServerBrowserFilter.PassFilter(pszHostName) - || m_imServerBrowserFilter.PassFilter(pszHostMap) - || m_imServerBrowserFilter.PassFilter(pszHostPort)) + // Filter the server list first before running it over the ImGui list + // clipper, if we do this within the clipper, clipper.Step() will fail + // as the calculation for the remainder will be off. + for (int i = 0; i < g_ServerListManager.m_vServerList.size(); i++) + { + const NetGameServer_t& server = g_ServerListManager.m_vServerList[i]; + + const char* pszHostName = server.name.c_str(); + const char* pszHostMap = server.map.c_str(); + const char* pszPlaylist = server.playlist.c_str(); + + if (m_serverBrowserTextFilter.PassFilter(pszHostName) + || m_serverBrowserTextFilter.PassFilter(pszHostMap) + || m_serverBrowserTextFilter.PassFilter(pszPlaylist)) { + filteredServers.push_back(&server); + } + } + + ImGuiListClipper clipper; + clipper.Begin(static_cast(filteredServers.size())); + + while (clipper.Step()) + { + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const NetGameServer_t* const server = filteredServers[i]; + + const char* pszHostName = server->name.c_str(); + const char* pszHostMap = server->map.c_str(); + const char* pszPlaylist = server->playlist.c_str(); + + char pszHostPort[32]; + sprintf(pszHostPort, "%d", server->port); + ImGui::TableNextColumn(); ImGui::Text("%s", pszHostName); @@ -311,54 +305,71 @@ void CBrowser::BrowserPanel(void) ImGui::Text("%s", pszPlaylist); ImGui::TableNextColumn(); - ImGui::Text("%s", Format("%3d/%3d", strtol(server.m_svPlayerCount.c_str(), NULL, NULL), strtol(server.m_svMaxPlayers.c_str(), NULL, NULL)).c_str()); + ImGui::Text("%s", Format("%3d/%3d", server->numPlayers, server->maxPlayers).c_str()); ImGui::TableNextColumn(); ImGui::Text("%s", pszHostPort); ImGui::TableNextColumn(); string svConnectBtn = "Connect##"; - svConnectBtn.append(server.m_svHostName + server.m_svIpAddress + server.m_svHostMap); + svConnectBtn.append(server->name + server->address + server->map); if (ImGui::Button(svConnectBtn.c_str())) { - g_pServerListManager->ConnectToServer(server.m_svIpAddress, pszHostPort, server.m_svEncryptionKey); + g_ServerListManager.ConnectToServer(server->address, server->port, server->netKey); } } } - g_pServerListManager->m_Mutex.unlock(); + + filteredServers.clear(); + g_ServerListManager.m_Mutex.Unlock(); ImGui::EndTable(); - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(frameStyleVars); } ImGui::EndChild(); - ImGui::PopStyleVar(iVars); + ImGui::PopStyleVar(windowStyleVars); ImGui::Separator(); - ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth() / 4); + + const ImVec2 regionAvail = ImGui::GetContentRegionAvail(); + const ImGuiStyle& style = ImGui::GetStyle(); + + // 4 elements means 3 spacings between items, this has to be subtracted + // from the remaining available region to get correct results on all + // window padding values!!! + const float itemWidth = (regionAvail.x - (3 * style.ItemSpacing.x)) / 4; + + ImGui::PushItemWidth(itemWidth); { - ImGui::InputTextWithHint("##ServerBrowser_ServerCon", "Server address and port", m_szServerAddressBuffer, IM_ARRAYSIZE(m_szServerAddressBuffer)); + ImGui::InputTextWithHint("##ServerBrowser_ServerCon", "Server address and port", m_serverAddressTextBuf, IM_ARRAYSIZE(m_serverAddressTextBuf)); ImGui::SameLine(); - ImGui::InputTextWithHint("##ServerBrowser_ServerKey", "Encryption key", m_szServerEncKeyBuffer, IM_ARRAYSIZE(m_szServerEncKeyBuffer)); + ImGui::InputTextWithHint("##ServerBrowser_ServerKey", "Encryption key", m_serverNetKeyTextBuf, IM_ARRAYSIZE(m_serverNetKeyTextBuf)); ImGui::SameLine(); - if (ImGui::Button("Connect", ImVec2(ImGui::GetWindowContentRegionWidth() / 4.3f, ImGui::GetFrameHeight()))) + if (ImGui::Button("Connect", ImVec2(itemWidth, ImGui::GetFrameHeight()))) { - if (m_szServerAddressBuffer[0]) + if (m_serverAddressTextBuf[0]) { - g_pServerListManager->ConnectToServer(m_szServerAddressBuffer, m_szServerEncKeyBuffer); + g_ServerListManager.ConnectToServer(m_serverAddressTextBuf, m_serverNetKeyTextBuf); } } ImGui::SameLine(); - if (ImGui::Button("Private servers", ImVec2(ImGui::GetWindowContentRegionWidth() / 4.3f, ImGui::GetFrameHeight()))) + + // NOTE: -9 to prevent the last button from clipping/colliding with the + // window drag handle! -9 makes the distance between the handle and the + // last button equal as that of the developer console. + if (ImGui::Button("Private servers", ImVec2(itemWidth - 9, ImGui::GetFrameHeight()))) { ImGui::OpenPopup("Private Server"); } + HiddenServersModal(); } + ImGui::PopItemWidth(); } @@ -367,13 +378,23 @@ void CBrowser::BrowserPanel(void) //----------------------------------------------------------------------------- void CBrowser::RefreshServerList(void) { - DevMsg(eDLL_T::CLIENT, "Refreshing server list with matchmaking host '%s'\n", pylon_matchmaking_hostname->GetString()); + Msg(eDLL_T::CLIENT, "Refreshing server list with matchmaking host '%s'\n", pylon_matchmaking_hostname.GetString()); - std::string svServerListMessage; - g_pServerListManager->RefreshServerList(svServerListMessage); + // Thread the request, and let the main thread assign status message back + std::thread request([&] + { + std::string serverListMessage; + size_t numServers; + g_ServerListManager.RefreshServerList(serverListMessage, numServers); - std::lock_guard l(m_Mutex); - m_svServerListMessage = svServerListMessage; + g_TaskQueue.Dispatch([&, serverListMessage] + { + SetServerListMessage(serverListMessage.c_str()); + }, 0); + } + ); + + request.detach(); } //----------------------------------------------------------------------------- @@ -381,32 +402,31 @@ void CBrowser::RefreshServerList(void) //----------------------------------------------------------------------------- void CBrowser::HiddenServersModal(void) { - float flHeight; // Set the padding accordingly for each theme. - switch (m_Style) + float modalWindowHeight; // Set the padding accordingly for each theme. + switch (m_surfaceStyle) { case ImGuiStyle_t::LEGACY: - flHeight = 207.f; + modalWindowHeight = 207.f; break; case ImGuiStyle_t::MODERN: - flHeight = 214.f; + modalWindowHeight = 214.f; break; default: - flHeight = 206.f; + modalWindowHeight = 206.f; break; } - int nVars = 0; - bool bModalOpen = true; + int modalStyleVars = 0; + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(408.f, modalWindowHeight)); modalStyleVars++; - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(408.f, flHeight)); nVars++; - - if (ImGui::BeginPopupModal("Private Server", &bModalOpen, ImGuiWindowFlags_NoResize)) + bool isModalStillOpen = true; + if (ImGui::BeginPopupModal("Private Server", &isModalStillOpen, ImGuiWindowFlags_NoResize)) { - ImGui::SetWindowSize(ImVec2(408.f, flHeight), ImGuiCond_Always); + ImGui::SetWindowSize(ImVec2(408.f, modalWindowHeight), ImGuiCond_Always); ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); // Override the style color for child bg. - ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(float(m_rLockedIconBlob.m_nWidth), float(m_rLockedIconBlob.m_nHeight))); - ImGui::Image(m_idLockedIcon, ImVec2(float(m_rLockedIconBlob.m_nWidth), float(m_rLockedIconBlob.m_nHeight))); // Display texture. + ImGui::BeginChild("##HiddenServersConnectModal_IconParent", ImVec2(float(m_lockedIconDataResource.m_nWidth), float(m_lockedIconDataResource.m_nHeight))); + ImGui::Image(m_lockedIconShaderResource, ImVec2(float(m_lockedIconDataResource.m_nWidth), float(m_lockedIconDataResource.m_nHeight))); // Display texture. ImGui::EndChild(); ImGui::PopStyleColor(); // Pop the override for the child bg. @@ -414,101 +434,106 @@ void CBrowser::HiddenServersModal(void) ImGui::SameLine(); ImGui::Text("Enter token to connect"); - ImGui::PushItemWidth(ImGui::GetWindowContentRegionWidth()); // Override item width. - ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token (required)", &m_svHiddenServerToken); + const ImVec2 contentRegionMax = ImGui::GetContentRegionAvail(); + ImGui::PushItemWidth(contentRegionMax.x); // Override item width. + + string hiddenServerToken; + ImGui::InputTextWithHint("##HiddenServersConnectModal_TokenInput", "Token (required)", &hiddenServerToken); + ImGui::PopItemWidth(); - if (m_bReclaimFocusTokenField) + if (m_reclaimFocusOnTokenField) { ImGui::SetKeyboardFocusHere(-1); // -1 means previous widget. - m_bReclaimFocusTokenField = false; + m_reclaimFocusOnTokenField = false; } - ImGui::Dummy(ImVec2(ImGui::GetWindowContentRegionWidth(), 19.f)); // Place a dummy, basically making space inserting a blank element. + ImGui::Dummy(ImVec2(contentRegionMax.x, 19.f)); // Place a dummy, basically making space inserting a blank element. - ImGui::TextColored(m_ivHiddenServerMessageColor, "%s", m_svHiddenServerRequestMessage.c_str()); + ImGui::TextColored(m_hiddenServerMessageColor, "%s", m_hiddenServerRequestMessage.c_str()); ImGui::Separator(); - if (ImGui::Button("Connect", ImVec2(ImGui::GetWindowContentRegionWidth(), 24))) + if (ImGui::Button("Connect", ImVec2(contentRegionMax.x, 24))) { - m_svHiddenServerRequestMessage.clear(); - m_bReclaimFocusTokenField = true; + m_hiddenServerRequestMessage.clear(); + m_reclaimFocusOnTokenField = true; - if (!m_svHiddenServerToken.empty()) + if (!hiddenServerToken.empty()) { NetGameServer_t server; - bool result = g_pMasterServer->GetServerByToken(server, m_svHiddenServerRequestMessage, m_svHiddenServerToken); // Send token connect request. + const bool result = g_MasterServer.GetServerByToken(server, m_hiddenServerRequestMessage, hiddenServerToken); // Send token connect request. - if (result && !server.m_svHostName.empty()) + if (result && !server.name.empty()) { - g_pServerListManager->ConnectToServer(server.m_svIpAddress, server.m_svGamePort, server.m_svEncryptionKey); // Connect to the server - m_svHiddenServerRequestMessage = Format("Found server: %s", server.m_svHostName.c_str()); - m_ivHiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + g_ServerListManager.ConnectToServer(server.address, server.port, server.netKey); // Connect to the server + m_hiddenServerRequestMessage = Format("Found server: %s", server.name.c_str()); + m_hiddenServerMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); ImGui::CloseCurrentPopup(); } else { - if (m_svHiddenServerRequestMessage.empty()) + if (m_hiddenServerRequestMessage.empty()) { - m_svHiddenServerRequestMessage = "Unknown error."; + m_hiddenServerRequestMessage = "Unknown error."; } else // Display error message. { - m_svHiddenServerRequestMessage = Format("Error: %s", m_svHiddenServerRequestMessage.c_str()); + m_hiddenServerRequestMessage = Format("Error: %s", m_hiddenServerRequestMessage.c_str()); } - m_ivHiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } else { - m_svHiddenServerRequestMessage = "Token is required."; - m_ivHiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hiddenServerRequestMessage = "Token is required."; + m_hiddenServerMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } - if (ImGui::Button("Close", ImVec2(ImGui::GetWindowContentRegionWidth(), 24))) + + if (ImGui::Button("Close", ImVec2(contentRegionMax.x, 24))) { - m_svHiddenServerRequestMessage.clear(); - m_bReclaimFocusTokenField = true; + m_hiddenServerRequestMessage.clear(); + m_reclaimFocusOnTokenField = true; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(modalStyleVars); } - else if (!bModalOpen) + else if (!isModalStillOpen) { - m_svHiddenServerRequestMessage.clear(); - m_bReclaimFocusTokenField = true; + m_hiddenServerRequestMessage.clear(); + m_reclaimFocusOnTokenField = true; - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(modalStyleVars); } else { - ImGui::PopStyleVar(nVars); + ImGui::PopStyleVar(modalStyleVars); } } //----------------------------------------------------------------------------- // Purpose: draws the host section //----------------------------------------------------------------------------- -void CBrowser::HostPanel(void) +void CBrowser::DrawHostPanel(void) { #ifndef CLIENT_DLL - std::lock_guard l(g_pServerListManager->m_Mutex); + NetGameServer_t& details = g_ServerHostManager.GetDetails(); - ImGui::InputTextWithHint("##ServerHost_ServerName", "Server name (required)", &g_pServerListManager->m_Server.m_svHostName); - ImGui::InputTextWithHint("##ServerHost_ServerDesc", "Server description (optional)", &g_pServerListManager->m_Server.m_svDescription); + ImGui::InputTextWithHint("##ServerHost_ServerName", "Server name (required)", &details.name); + ImGui::InputTextWithHint("##ServerHost_ServerDesc", "Server description (optional)", &details.description); ImGui::Spacing(); - if (ImGui::BeginCombo("Mode", g_pServerListManager->m_Server.m_svPlaylist.c_str())) + if (ImGui::BeginCombo("Mode", details.playlist.c_str())) { g_PlaylistsVecMutex.lock(); for (const string& svPlaylist : g_vAllPlaylists) { - if (ImGui::Selectable(svPlaylist.c_str(), svPlaylist == g_pServerListManager->m_Server.m_svPlaylist)) + if (ImGui::Selectable(svPlaylist.c_str(), svPlaylist == details.playlist)) { - g_pServerListManager->m_Server.m_svPlaylist = svPlaylist; + details.playlist = svPlaylist; } } @@ -516,14 +541,18 @@ void CBrowser::HostPanel(void) ImGui::EndCombo(); } - if (ImGui::BeginCombo("Map", g_pServerListManager->m_Server.m_svHostMap.c_str())) + if (ImGui::BeginCombo("Map", details.map.c_str())) { g_InstalledMapsMutex.lock(); - for (const string& svMap : g_InstalledMaps) + + FOR_EACH_VEC(g_InstalledMaps, i) { - if (ImGui::Selectable(svMap.c_str(), svMap == g_pServerListManager->m_Server.m_svHostMap)) + const CUtlString& mapName = g_InstalledMaps[i]; + + if (ImGui::Selectable(mapName.String(), + mapName.IsEqual_CaseInsensitive(details.map.c_str()))) { - g_pServerListManager->m_Server.m_svHostMap = svMap; + details.map = mapName.String(); } } @@ -531,121 +560,116 @@ void CBrowser::HostPanel(void) ImGui::EndCombo(); } - m_bQueryGlobalBanList = sv_globalBanlist->GetBool(); // Sync toggle with 'sv_globalBanlist'. - if (ImGui::Checkbox("Load global banned list", &m_bQueryGlobalBanList)) + m_queryGlobalBanList = sv_globalBanlist.GetBool(); // Sync toggle with 'sv_globalBanlist'. + if (ImGui::Checkbox("Load global banned list", &m_queryGlobalBanList)) { - sv_globalBanlist->SetValue(m_bQueryGlobalBanList); + sv_globalBanlist.SetValue(m_queryGlobalBanList); } ImGui::Text("Server visibility"); - if (ImGui::SameLine(); ImGui::RadioButton("offline", g_pServerListManager->m_ServerVisibility == EServerVisibility_t::OFFLINE)) + if (ImGui::SameLine(); ImGui::RadioButton("offline", g_ServerHostManager.GetVisibility() == ServerVisibility_e::OFFLINE)) { - g_pServerListManager->m_ServerVisibility = EServerVisibility_t::OFFLINE; + g_ServerHostManager.SetVisibility(ServerVisibility_e::OFFLINE); } - if (ImGui::SameLine(); ImGui::RadioButton("hidden", g_pServerListManager->m_ServerVisibility == EServerVisibility_t::HIDDEN)) + if (ImGui::SameLine(); ImGui::RadioButton("hidden", g_ServerHostManager.GetVisibility() == ServerVisibility_e::HIDDEN)) { - g_pServerListManager->m_ServerVisibility = EServerVisibility_t::HIDDEN; + g_ServerHostManager.SetVisibility(ServerVisibility_e::HIDDEN); } - if (ImGui::SameLine(); ImGui::RadioButton("public", g_pServerListManager->m_ServerVisibility == EServerVisibility_t::PUBLIC)) + if (ImGui::SameLine(); ImGui::RadioButton("public", g_ServerHostManager.GetVisibility() == ServerVisibility_e::PUBLIC)) { - g_pServerListManager->m_ServerVisibility = EServerVisibility_t::PUBLIC; + g_ServerHostManager.SetVisibility(ServerVisibility_e::PUBLIC); } - ImGui::TextColored(m_HostRequestMessageColor, "%s", m_svHostRequestMessage.c_str()); - if (!m_svHostToken.empty()) + ImGui::TextColored(m_hostMessageColor, "%s", m_hostMessage.c_str()); + if (!m_hostToken.empty()) { - ImGui::InputText("##ServerHost_HostToken", &m_svHostToken, ImGuiInputTextFlags_ReadOnly); + ImGui::InputText("##ServerHost_HostToken", &m_hostToken, ImGuiInputTextFlags_ReadOnly); } ImGui::Spacing(); - const bool bServerActive = g_pServer->IsActive(); + const ImVec2 contentRegionMax = ImGui::GetContentRegionAvail(); + const bool serverActive = g_pServer->IsActive(); if (!g_pHostState->m_bActiveGame) { - if (ImGui::Button("Start server", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("Start server", ImVec2(contentRegionMax.x, 32))) { - m_svHostRequestMessage.clear(); + m_hostMessage.clear(); - bool bEnforceField = g_pServerListManager->m_ServerVisibility == EServerVisibility_t::OFFLINE ? true : !g_pServerListManager->m_Server.m_svHostName.empty(); - if (bEnforceField && !g_pServerListManager->m_Server.m_svPlaylist.empty() && !g_pServerListManager->m_Server.m_svHostMap.empty()) + const bool enforceField = g_ServerHostManager.GetVisibility() == ServerVisibility_e::OFFLINE + ? true + : !details.name.empty(); + + if (enforceField && !details.playlist.empty() && !details.map.empty()) { - g_pServerListManager->LaunchServer(bServerActive); // Launch server. + g_ServerHostManager.LaunchServer(serverActive); // Launch server. } else { - if (g_pServerListManager->m_Server.m_svHostName.empty()) + if (details.name.empty()) { - m_svHostRequestMessage = "Server name is required."; - m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Server name is required."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } - else if (g_pServerListManager->m_Server.m_svPlaylist.empty()) + else if (details.playlist.empty()) { - m_svHostRequestMessage = "Playlist is required."; - m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Playlist is required."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } - else if (g_pServerListManager->m_Server.m_svHostMap.empty()) + else if (details.map.empty()) { - m_svHostRequestMessage = "Level name is required."; - m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Level name is required."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } } - if (ImGui::Button("Reload playlist", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + + if (ImGui::Button("Reload playlist", ImVec2(contentRegionMax.x, 32))) { - g_TaskScheduler->Dispatch([]() - { - _DownloadPlaylists_f(); - KeyValues::InitPlaylists(); // Re-Init playlist. - }, 0); + v_Playlists_Download_f(); + Playlists_SDKInit(); // Re-Init playlist. } - if (ImGui::Button("Reload banlist", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("Reload banlist", ImVec2(contentRegionMax.x, 32))) { - g_TaskScheduler->Dispatch([]() - { - g_pBanSystem->Load(); - }, 0); + g_BanSystem.LoadList(); } } else { - if (ImGui::Button("Stop server", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("Stop server", ImVec2(contentRegionMax.x, 32))) { ProcessCommand("LeaveMatch"); // TODO: use script callback instead. - g_TaskScheduler->Dispatch([]() - { - // Force CHostState::FrameUpdate to shutdown the server for dedicated. - g_pHostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; - }, 0); + g_pHostState->m_iNextState = HostStates_t::HS_GAME_SHUTDOWN; } - if (ImGui::Button("Change level", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("Change level", ImVec2(contentRegionMax.x, 32))) { - if (!g_pServerListManager->m_Server.m_svHostMap.empty()) + if (!details.map.empty()) { - g_pServerListManager->LaunchServer(bServerActive); + g_ServerHostManager.LaunchServer(serverActive); } else { - m_svHostRequestMessage = "Failed to change level: 'levelname' was empty."; - m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessage = "Failed to change level: 'levelname' was empty."; + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } } - if (bServerActive) + if (serverActive) { ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); - if (ImGui::Button("AI network rebuild", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("AI network rebuild", ImVec2(contentRegionMax.x, 32))) { ProcessCommand("BuildAINFile"); } - if (ImGui::Button("NavMesh hot swap", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("NavMesh hot swap", ImVec2(contentRegionMax.x, 32))) { ProcessCommand("navmesh_hotswap"); } @@ -654,9 +678,9 @@ void CBrowser::HostPanel(void) ImGui::Separator(); ImGui::Spacing(); - if (ImGui::Button("AI settings reparse", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("AI settings reparse", ImVec2(contentRegionMax.x, 32))) { - DevMsg(eDLL_T::ENGINE, "Reparsing AI data on %s\n", g_pClientState->IsActive() ? "server and client" : "server"); + Msg(eDLL_T::ENGINE, "Reparsing AI data on %s\n", g_pClientState->IsActive() ? "server and client" : "server"); ProcessCommand("aisettings_reparse"); if (g_pClientState->IsActive()) @@ -665,9 +689,9 @@ void CBrowser::HostPanel(void) } } - if (ImGui::Button("Weapon settings reparse", ImVec2(ImGui::GetWindowContentRegionWidth(), 32))) + if (ImGui::Button("Weapon settings reparse", ImVec2(contentRegionMax.x, 32))) { - DevMsg(eDLL_T::ENGINE, "Reparsing weapon data on %s\n", g_pClientState->IsActive() ? "server and client" : "server"); + Msg(eDLL_T::ENGINE, "Reparsing weapon data on %s\n", g_pClientState->IsActive() ? "server and client" : "server"); ProcessCommand("weapon_reparse"); } } @@ -683,31 +707,36 @@ void CBrowser::UpdateHostingStatus(void) #ifndef CLIENT_DLL assert(g_pHostState && g_pCVar); - std::lock_guard l(g_pServerListManager->m_Mutex); - g_pServerListManager->m_HostingStatus = g_pServer->IsActive() ? EHostStatus_t::HOSTING : EHostStatus_t::NOT_HOSTING; // Are we hosting a server? + const HostStatus_e hostStatus = (g_ServerHostManager.GetVisibility() != ServerVisibility_e::OFFLINE && g_pServer->IsActive()) + ? HostStatus_e::HOSTING + : HostStatus_e::NOT_HOSTING; - switch (g_pServerListManager->m_HostingStatus) + g_ServerHostManager.SetHostStatus(hostStatus); // Are we hosting a server? + + switch (hostStatus) { - case EHostStatus_t::NOT_HOSTING: + case HostStatus_e::NOT_HOSTING: { - std::lock_guard g(m_Mutex); - if (!m_svHostToken.empty()) + if (!m_hostToken.empty()) { - m_svHostToken.clear(); + m_hostToken.clear(); } - if (ImGui::ColorConvertFloat4ToU32(m_HostRequestMessageColor) == // Only clear if this is green (a valid hosting message). + if (ImGui::ColorConvertFloat4ToU32(m_hostMessageColor) == // Only clear if this is green (a valid hosting message). ImGui::ColorConvertFloat4ToU32(ImVec4(0.00f, 1.00f, 0.00f, 1.00f))) { - m_svHostRequestMessage.clear(); - m_HostRequestMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + m_hostMessage.clear(); + m_hostMessageColor = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); } break; } - case EHostStatus_t::HOSTING: + case HostStatus_e::HOSTING: { - if (g_pServerListManager->m_ServerVisibility == EServerVisibility_t::OFFLINE) + const ServerVisibility_e serverVisibility = g_ServerHostManager.GetVisibility(); + NetGameServer_t& details = g_ServerHostManager.GetDetails(); + + if (serverVisibility == ServerVisibility_e::OFFLINE) { break; } @@ -717,46 +746,39 @@ void CBrowser::UpdateHostingStatus(void) break; } - switch (g_pServerListManager->m_ServerVisibility) + switch (serverVisibility) { - case EServerVisibility_t::HIDDEN: - g_pServerListManager->m_Server.m_bHidden = true; + case ServerVisibility_e::HIDDEN: + details.hidden = true; break; - case EServerVisibility_t::PUBLIC: - g_pServerListManager->m_Server.m_bHidden = false; + case ServerVisibility_e::PUBLIC: + details.hidden = false; break; default: break; } - g_TaskScheduler->Dispatch([this]() + const NetGameServer_t netGameServer { - std::lock_guard f(g_pServerListManager->m_Mutex); - NetGameServer_t netGameServer - { - g_pServerListManager->m_Server.m_svHostName, - g_pServerListManager->m_Server.m_svDescription, - g_pServerListManager->m_Server.m_bHidden, - g_pHostState->m_levelName, - KeyValues_GetCurrentPlaylist(), - hostip->GetString(), - hostport->GetString(), - g_pNetKey->GetBase64NetKey(), - std::to_string(*g_nServerRemoteChecksum), - SDK_VERSION, - std::to_string(g_pServer->GetNumClients()), - std::to_string(g_ServerGlobalVariables->m_nMaxClients), - std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch() - ).count() - }; - - std::thread post(&CBrowser::SendHostingPostRequest, this, netGameServer); - post.detach(); - - }, 0); + details.name, + details.description, + details.hidden, + g_pHostState->m_levelName, + v_Playlists_GetCurrent(), + hostip->GetString(), + hostport->GetInt(), + g_pNetKey->GetBase64NetKey(), + *g_nServerRemoteChecksum, + SDK_VERSION, + g_pServer->GetNumClients(), + g_ServerGlobalVariables->m_nMaxClients, + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch() + ).count() + }; + SendHostingPostRequest(netGameServer); break; } default: @@ -766,89 +788,84 @@ void CBrowser::UpdateHostingStatus(void) } //----------------------------------------------------------------------------- -// Purpose: sends the hosting POST request to the comp server +// Purpose: sends the hosting POST request to the comp server and installs the +// host data on the server browser // Input : &gameServer - //----------------------------------------------------------------------------- void CBrowser::SendHostingPostRequest(const NetGameServer_t& gameServer) { #ifndef CLIENT_DLL - string svHostRequestMessage; - string svHostToken; - bool result = g_pMasterServer->PostServerHost(svHostRequestMessage, svHostToken, gameServer); - - std::lock_guard l(m_Mutex); - - m_svHostRequestMessage = svHostRequestMessage; - m_svHostToken = svHostToken; - - if (result) - { - m_HostRequestMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); - stringstream ssMessage; - ssMessage << "Broadcasting: "; - if (!m_svHostToken.empty()) + std::thread request([&, gameServer] { - ssMessage << "share the following token for clients to connect: "; + string hostRequestMessage; + string hostToken; + string hostIp; + + const bool result = g_MasterServer.PostServerHost(hostRequestMessage, hostToken, hostIp, gameServer); + + g_TaskQueue.Dispatch([&, result, hostRequestMessage, hostToken, hostIp] + { + InstallHostingDetails(result, hostRequestMessage.c_str(), hostToken.c_str(), hostIp); + }, 0); } - m_svHostRequestMessage = ssMessage.str(); + ); + request.detach(); +#endif // !CLIENT_DLL +} + +//----------------------------------------------------------------------------- +// Purpose: installs the host data on the server browser +// Input : postFailed - +// *hostMessage - +// *hostToken - +// &hostIp - +//----------------------------------------------------------------------------- +void CBrowser::InstallHostingDetails(const bool postFailed, const char* const hostMessage, const char* const hostToken, const string& hostIp) +{ +#ifndef CLIENT_DLL + m_hostMessage = hostMessage; + m_hostToken = hostToken; + + if (!hostIp.empty()) + { + g_ServerHostManager.SetHostIP(hostIp); + } + + if (postFailed) + { + m_hostMessageColor = ImVec4(0.00f, 1.00f, 0.00f, 1.00f); + stringstream ssMessage; + ssMessage << "Broadcasting"; + if (!m_hostToken.empty()) + { + ssMessage << ": share the following token for clients to connect: "; + } + + m_hostMessage = ssMessage.str(); } else { - m_HostRequestMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); } #endif // !CLIENT_DLL } //----------------------------------------------------------------------------- -// Purpose: processes submitted commands for the main thread +// Purpose: processes submitted commands // Input : *pszCommand - //----------------------------------------------------------------------------- void CBrowser::ProcessCommand(const char* pszCommand) const { Cbuf_AddText(Cbuf_GetCurrentPlayer(), pszCommand, cmd_source_t::kCommandSrcCode); - //g_TaskScheduler->Dispatch(Cbuf_Execute, 0); // Run in main thread. } //----------------------------------------------------------------------------- -// Purpose: draws the settings section +// Purpose: toggles the server browser //----------------------------------------------------------------------------- -void CBrowser::SettingsPanel(void) +void CBrowser::ToggleBrowser_f() { - ImGui::InputTextWithHint("Hostname", "Matchmaking host name", &m_szMatchmakingHostName); - if (ImGui::Button("Update hostname")) - { - ProcessCommand(Format("%s \"%s\"", pylon_matchmaking_hostname->GetName(), m_szMatchmakingHostName.c_str()).c_str()); - } - - // The 'const' qualifier has been casted away, however the readonly flag is set. - // Still a hack, but better than modifying the Dear ImGui lib even more.. - ImGui::InputTextWithHint("Netkey", "Network encryption key", const_cast(g_pNetKey->GetBase64NetKey()), ImGuiInputTextFlags_ReadOnly); - if (ImGui::Button("Regenerate encryption key")) - { - g_TaskScheduler->Dispatch(NET_GenerateKey, 0); - } + g_Browser.m_activated ^= true; + ResetInput(); // Disable input to game when browser is drawn. } -//----------------------------------------------------------------------------- -// Purpose: hooked to 'MP_HostName_Changed_f' to sync hostname field with -// the 'pylon_matchmaking_hostname' ConVar (!!! DO NOT USE !!!). -// Input : *pszHostName - -//----------------------------------------------------------------------------- -void CBrowser::SetHostName(const char* pszHostName) -{ - std::lock_guard l(m_Mutex); - m_szMatchmakingHostName = pszHostName; -} - -//----------------------------------------------------------------------------- -// Purpose: sets the browser front-end style -//----------------------------------------------------------------------------- -void CBrowser::SetStyleVar(void) -{ - m_Style = g_pImGuiConfig->InitStyle(); - - ImGui::SetNextWindowSize(ImVec2(928.f, 524.f), ImGuiCond_FirstUseEver); - ImGui::SetWindowPos(ImVec2(-500.f, 50.f), ImGuiCond_FirstUseEver); -} - -CBrowser* g_pBrowser = new CBrowser(); \ No newline at end of file +CBrowser g_Browser; diff --git a/r5dev/gameui/IBrowser.h b/r5dev/gameui/IBrowser.h index 6bf195da..9591892d 100644 --- a/r5dev/gameui/IBrowser.h +++ b/r5dev/gameui/IBrowser.h @@ -4,80 +4,74 @@ #include "windows/resource.h" #include "networksystem/serverlisting.h" #include "networksystem/pylon.h" -#include "public/isurfacesystem.h" #include "thirdparty/imgui/misc/imgui_utility.h" -class CBrowser : public ISurface +#include "imgui_surface.h" + +class CBrowser : public CImguiSurface { public: CBrowser(void); virtual ~CBrowser(void); virtual bool Init(void); - virtual void Think(void); + virtual void Shutdown(void); virtual void RunFrame(void); - virtual void RunTask(void); + void RunTask(void); - virtual void DrawSurface(void); + virtual bool DrawSurface(void); - void BrowserPanel(void); + void DrawBrowserPanel(void); void RefreshServerList(void); void HiddenServersModal(void); - void HostPanel(void); + void DrawHostPanel(void); void UpdateHostingStatus(void); + void InstallHostingDetails(const bool postFailed, const char* const hostMessage, const char* const hostToken, const string& hostIp); void SendHostingPostRequest(const NetGameServer_t& gameServer); void ProcessCommand(const char* pszCommand) const; - void SettingsPanel(void); - void SetHostName(const char* pszHostName); - virtual void SetStyleVar(void); - - inline bool IsVisible() { return m_flFadeAlpha > 0.0f; } - - const char* m_pszBrowserLabel; - bool m_bActivate; +public: + // Command callbacks + static void ToggleBrowser_f(); private: - bool m_bInitialized; - bool m_bReclaimFocus; - bool m_bReclaimFocusTokenField; - bool m_bQueryListNonRecursive; // When set, refreshes the server list once the next frame. - bool m_bQueryGlobalBanList; - char m_szServerAddressBuffer[128]; - char m_szServerEncKeyBuffer[30]; - float m_flFadeAlpha; + inline void SetServerListMessage(const char* const message) { m_serverListMessage = message; }; + inline void SetHostMessage(const char* const message) { m_hostMessage = message; } + inline void SetHostToken(const char* const message) { m_hostToken = message; } - ID3D11ShaderResourceView* m_idLockedIcon; - MODULERESOURCE m_rLockedIconBlob; - mutable std::mutex m_Mutex; +private: + bool m_reclaimFocusOnTokenField; + bool m_queryNewListNonRecursive; // When set, refreshes the server list once the next frame. + bool m_queryGlobalBanList; + char m_serverAddressTextBuf[128]; + char m_serverNetKeyTextBuf[30]; + + ID3D11ShaderResourceView* m_lockedIconShaderResource; + MODULERESOURCE m_lockedIconDataResource; //////////////////// // Server List // //////////////////// - ImGuiTextFilter m_imServerBrowserFilter; - string m_svServerListMessage; - string m_szMatchmakingHostName; + ImGuiTextFilter m_serverBrowserTextFilter; + string m_serverListMessage; //////////////////// // Host Server // //////////////////// - string m_svHostRequestMessage; - string m_svHostToken; - ImVec4 m_HostRequestMessageColor; + string m_hostMessage; + string m_hostToken; + ImVec4 m_hostMessageColor; //////////////////// // Private Server // //////////////////// - string m_svHiddenServerToken; - string m_svHiddenServerRequestMessage; - ImVec4 m_ivHiddenServerMessageColor; - - ImGuiStyle_t m_Style; + string m_hiddenServerRequestMessage; + ImVec4 m_hiddenServerMessageColor; }; -extern CBrowser* g_pBrowser; +extern CBrowser g_Browser; #endif \ No newline at end of file diff --git a/r5dev/gameui/IConsole.cpp b/r5dev/gameui/IConsole.cpp index 4370a583..9bf9fb05 100644 --- a/r5dev/gameui/IConsole.cpp +++ b/r5dev/gameui/IConsole.cpp @@ -1,7 +1,7 @@ /****************************************************************************** ------------------------------------------------------------------------------- File : IConsole.cpp -Date : 18:07:2021 +Date : 15:06:2021 Author : Kawe Mazidjatari Purpose: Implements the in-game console front-end ------------------------------------------------------------------------------- @@ -23,56 +23,49 @@ History: #include "engine/cmd.h" #include "gameui/IConsole.h" +//----------------------------------------------------------------------------- +// Console variables +//----------------------------------------------------------------------------- +static ConVar con_max_lines("con_max_lines", "1024", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "Maximum number of lines in the console before cleanup starts", true, 1.f, false, 0.f); +static ConVar con_max_history("con_max_history", "512", FCVAR_DEVELOPMENTONLY, "Maximum number of command submission items before history cleanup starts", true, 0.f, false, 0.f); + +static ConVar con_suggest_limit("con_suggest_limit", "128", FCVAR_DEVELOPMENTONLY, "Maximum number of suggestions the autocomplete window will show for the console", true, 0.f, false, 0.f); +static ConVar con_suggest_helptext("con_suggest_helptext", "1", FCVAR_RELEASE, "Show CommandBase help text in autocomplete window"); + +static ConVar con_autocomplete_window_textures("con_autocomplete_window_textures", "1", FCVAR_RELEASE, "Show help textures in autocomplete window"); +static ConVar con_autocomplete_window_width("con_autocomplete_window_width", "0", FCVAR_RELEASE, "The maximum width of the console's autocomplete window", true, 0.f, false, 0.f); +static ConVar con_autocomplete_window_height("con_autocomplete_window_height", "217.5", FCVAR_RELEASE, "The maximum height of the console's autocomplete window", true, 0.f, false, 0.f); + +//----------------------------------------------------------------------------- +// Console commands +//----------------------------------------------------------------------------- +static ConCommand toggleconsole("toggleconsole", CConsole::ToggleConsole_f, "Show/hide the developer console.", FCVAR_CLIENTDLL | FCVAR_RELEASE); + +static ConCommand con_history("con_history", CConsole::LogHistory_f, "Shows the developer console submission history", FCVAR_CLIENTDLL | FCVAR_RELEASE); +static ConCommand con_removeline("con_removeline", CConsole::RemoveLine_f, "Removes a range of lines from the developer console", FCVAR_CLIENTDLL | FCVAR_RELEASE); +static ConCommand con_clearlines("con_clearlines", CConsole::ClearLines_f, "Clears all lines from the developer console", FCVAR_CLIENTDLL | FCVAR_RELEASE); +static ConCommand con_clearhistory("con_clearhistory", CConsole::ClearHistory_f, "Clears all submissions from the developer console history", FCVAR_CLIENTDLL | FCVAR_RELEASE); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CConsole::CConsole(void) - : m_pszConsoleLabel("Console") - , m_pszLoggingLabel("LoggingRegion") - , m_nHistoryPos(-1) - , m_nSuggestPos(-1) - , m_nScrollBack(0) - , m_nSelectBack(0) - , m_nInputTextLen(0) - , m_flScrollX(0.f) - , m_flScrollY(0.f) - , m_flFadeAlpha(0.f) - , m_bInitialized(false) - , m_bReclaimFocus(false) - , m_bCopyToClipBoard(false) - , m_bModifyInput(false) - , m_bCanAutoComplete(false) - , m_bSuggestActive(false) - , m_bSuggestMoved(false) - , m_bSuggestUpdate(false) - , m_Style(ImGuiStyle_t::NONE) - , m_bActivate(false) + : m_loggerLabel("LoggingRegion") + , m_historyPos(ConAutoCompletePos_e::kPark) + , m_suggestPos(ConAutoCompletePos_e::kPark) + , m_scrollBackAmount(0) + , m_selectBackAmount(0) + , m_selectedSuggestionTextLen(0) + , m_lastFrameScrollPos(0.f, 0.f) + , m_inputTextBufModified(false) + , m_canAutoComplete(false) + , m_autoCompleteActive(false) + , m_autoCompletePosMoved(false) { - m_nInputFlags = - ImGuiInputTextFlags_EnterReturnsTrue | - ImGuiInputTextFlags_CallbackCompletion | - ImGuiInputTextFlags_CallbackHistory | - ImGuiInputTextFlags_CallbackAlways | - ImGuiInputTextFlags_CallbackCharFilter | - ImGuiInputTextFlags_CallbackEdit | - ImGuiInputTextFlags_AutoCaretEnd; + m_surfaceLabel = "Console"; - m_nSuggestFlags = - ImGuiWindowFlags_NoTitleBar | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoFocusOnAppearing | - ImGuiWindowFlags_AlwaysVerticalScrollbar | - ImGuiWindowFlags_AlwaysHorizontalScrollbar; - - m_nLoggingFlags = - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_HorizontalScrollbar | - ImGuiWindowFlags_AlwaysVerticalScrollbar; - - memset(m_szInputBuf, '\0', sizeof(m_szInputBuf)); - memset(m_szWindowLabel, '\0', sizeof(m_szWindowLabel)); - snprintf(m_szSummary, sizeof(m_szSummary), "%zu history items", m_vHistory.size()); + memset(m_inputTextBuf, '\0', sizeof(m_inputTextBuf)); + snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "%zu history items", m_vecHistory.size()); } //----------------------------------------------------------------------------- @@ -80,7 +73,25 @@ CConsole::CConsole(void) //----------------------------------------------------------------------------- CConsole::~CConsole(void) { - for (MODULERESOURCE& flagIcon : m_vFlagIcons) + Shutdown(); +} + +//----------------------------------------------------------------------------- +// Purpose: game console initialization +// Output : true on success, false otherwise +//----------------------------------------------------------------------------- +bool CConsole::Init(void) +{ + SetStyleVar(1200, 524, -1000, 50); + return LoadFlagIcons(); +} + +//----------------------------------------------------------------------------- +// Purpose: game console shutdown +//----------------------------------------------------------------------------- +void CConsole::Shutdown(void) +{ + for (MODULERESOURCE& flagIcon : m_vecFlagIcons) { if (flagIcon.m_idIcon) { @@ -89,16 +100,6 @@ CConsole::~CConsole(void) } } -//----------------------------------------------------------------------------- -// Purpose: game console setup -// Output : true on success, false otherwise -//----------------------------------------------------------------------------- -bool CConsole::Init(void) -{ - SetStyleVar(); - return LoadFlagIcons(); -} - //----------------------------------------------------------------------------- // Purpose: game console main render loop //----------------------------------------------------------------------------- @@ -113,140 +114,95 @@ void CConsole::RunFrame(void) /************************** * BASE PANEL SETUP * **************************/ + if (!m_initialized) { - if (!m_bInitialized) - { - Init(); - m_bInitialized = true; - } - - int nVars = 0; - float flWidth; - float flHeight; - if (m_Style == ImGuiStyle_t::MODERN) - { - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_flFadeAlpha); nVars++; - - flWidth = 621.f; - flHeight = 532.f; - } - else - { - if (m_Style == ImGuiStyle_t::LEGACY) - { - flWidth = 619.f; - flHeight = 526.f; - } - else - { - flWidth = 618.f; - flHeight = 524.f; - } - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 6.f, 6.f }); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_flFadeAlpha); nVars++; - } - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(flWidth, flHeight)); nVars++; - - DrawSurface(); - ImGui::PopStyleVar(nVars); + Init(); + m_initialized = true; } + Animate(); + + int baseWindowStyleVars = 0; + ImVec2 minBaseWindowRect; + + if (m_surfaceStyle == ImGuiStyle_t::MODERN) + { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 8.f, 10.f }); baseWindowStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++; + + minBaseWindowRect = ImVec2(621.f, 532.f); + } + else + { + minBaseWindowRect = m_surfaceStyle == ImGuiStyle_t::LEGACY + ? ImVec2(619.f, 526.f) + : ImVec2(618.f, 524.f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 6.f, 6.f }); baseWindowStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); baseWindowStyleVars++; + } + + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, minBaseWindowRect); baseWindowStyleVars++; + + const bool drawn = DrawSurface(); + ImGui::PopStyleVar(baseWindowStyleVars); + + // If we didn't draw the console, don't draw the suggest panel + if (!drawn) + return; + /************************** * SUGGESTION PANEL SETUP * **************************/ + if (RunAutoComplete()) { - int nVars = 0; - if (AutoComplete()) + if (m_surfaceStyle == ImGuiStyle_t::MODERN) { - if (m_Style == ImGuiStyle_t::MODERN) - { - const ImGuiStyle& style = ImGui::GetStyle(); - m_ivSuggestWindowPos.y = m_ivSuggestWindowPos.y + style.WindowPadding.y + 1.5f; - } - - ImGui::SetNextWindowPos(m_ivSuggestWindowPos); - ImGui::SetNextWindowSize(m_ivSuggestWindowSize); - if (m_bSuggestUpdate) - { - ImGui::SetNextWindowScroll(ImVec2(0.f, 0.f)); - m_bSuggestUpdate = false; - } - - ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(500, 37)); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); nVars++; - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_flFadeAlpha); nVars++; - - SuggestPanel(); - - ImGui::PopStyleVar(nVars); - } - } -} - -//----------------------------------------------------------------------------- -// Purpose: runs tasks for the console while not being drawn -// (!!! RunTask and RunFrame must be called from the same thread !!!) -//----------------------------------------------------------------------------- -void CConsole::RunTask(void) -{ - // m_Logger and m_vHistory are modified. - std::lock_guard l(m_Mutex); - - ClampLogSize(); - ClampHistorySize(); -} - -//----------------------------------------------------------------------------- -// Purpose: think -//----------------------------------------------------------------------------- -void CConsole::Think(void) -{ - if (m_bActivate) - { - if (m_flFadeAlpha < 1.f) - { - m_flFadeAlpha += .05f; - m_flFadeAlpha = (std::min)(m_flFadeAlpha, 1.f); - } - } - else // Reset to full transparent. - { - if (m_flFadeAlpha > 0.f) - { - m_flFadeAlpha -= .05f; - m_flFadeAlpha = (std::max)(m_flFadeAlpha, 0.f); + const ImGuiStyle& style = ImGui::GetStyle(); + m_autoCompleteWindowPos.y = m_autoCompleteWindowPos.y + style.WindowPadding.y + 1.5f; } - m_bReclaimFocus = true; + ImGui::SetNextWindowPos(m_autoCompleteWindowPos); + ImGui::SetNextWindowSize(m_autoCompleteWindowRect); + + int autoCompleteStyleVars = 0; + + // NOTE: 68 is the minimum width of the autocomplete window as this + // leaves enough space to show the flag and the first 4 characters + // of the suggestion. 37 is the minimum height as anything lower + // will truncate the first element in the autocomplete window. + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(68, 37)); autoCompleteStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); autoCompleteStyleVars++; + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, m_fadeAlpha); autoCompleteStyleVars++; + + DrawAutoCompletePanel(); + + ImGui::PopStyleVar(autoCompleteStyleVars); } } //----------------------------------------------------------------------------- // Purpose: draws the console's main surface -// Input : *bDraw - +// Output : true if a frame has been drawn, false otherwise //----------------------------------------------------------------------------- -void CConsole::DrawSurface(void) +bool CConsole::DrawSurface(void) { - if (!ImGui::Begin(m_pszConsoleLabel, &m_bActivate, ImGuiWindowFlags_None, &ResetInput)) + if (!ImGui::Begin(m_surfaceLabel, &m_activated, ImGuiWindowFlags_None, &ResetInput)) { ImGui::End(); - return; + return false; } + m_mainWindow = ImGui::GetCurrentWindow(); + const ImGuiStyle& style = ImGui::GetStyle(); + const ImVec2 fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr); - // Reserve enough left-over height and width for 1 separator + 1 input text - const float flFooterHeightReserve = style.ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - const float flFooterWidthReserve = style.ItemSpacing.y + ImGui::GetWindowWidth(); - - ImVec2 fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr); /////////////////////////////////////////////////////////////////////// ImGui::Separator(); if (ImGui::BeginPopup("Options")) { - OptionsPanel(); + DrawOptionsPanel(); } if (ImGui::Button("Options")) { @@ -254,73 +210,110 @@ void CConsole::DrawSurface(void) } ImGui::SameLine(); - m_Logger.GetFilter().Draw("Filter | ", flFooterWidthReserve - 500); - ImGui::SameLine(); - ImGui::Text("%s", m_szSummary); + // Reserve enough left-over height and width for 1 separator + 1 input text + const float footerWidthReserve = style.ItemSpacing.y + ImGui::GetWindowWidth(); + m_colorTextLogger.GetFilter().Draw("Filter (inc,-exc)", footerWidthReserve - 350); ImGui::Separator(); /////////////////////////////////////////////////////////////////////// - if (!m_Logger.IsScrolledToBottom() && m_nScrollBack > 0) + if (!m_colorTextLogger.IsScrolledToBottom() && m_scrollBackAmount > 0) { - ImGuiWindow* pWindow = ImGui::GetCurrentWindow(); - ImGuiID nID = pWindow->GetID(m_pszLoggingLabel); + const ImGuiID windowId = m_mainWindow->GetID(m_loggerLabel); - snprintf(m_szWindowLabel, sizeof(m_szWindowLabel), "%s/%s_%08X", m_pszConsoleLabel, m_pszLoggingLabel, nID); - ImGui::SetWindowScrollY(m_szWindowLabel, m_flScrollY - m_nScrollBack * fontSize.y); + char windowName[128]; + snprintf(windowName, sizeof(windowName), "%s/%s_%08X", m_surfaceLabel, m_loggerLabel, windowId); + + ImGui::SetWindowScrollY(windowName, m_lastFrameScrollPos.y - m_scrollBackAmount * fontSize.y); } - m_nScrollBack = 0; + m_scrollBackAmount = 0; - /////////////////////////////////////////////////////////////////////// - int iVars = 0; // Eliminate borders around log window. - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 1.f }); iVars++; - ImGui::BeginChild(m_pszLoggingLabel, ImVec2(0, -flFooterHeightReserve), true, m_nLoggingFlags); + // Reserve enough left-over height for 2 text elements. + float footerHeightReserve = ImGui::GetFrameHeight() * 2; + ImGuiChildFlags loggerFlags = ImGuiChildFlags_None; - // Mutex is locked here, as we start using/modifying - // non-atomic members that are used from several threads. - std::lock_guard l(m_Mutex); - m_Logger.Render(); + const bool isLegacyStyle = m_surfaceStyle == ImGuiStyle_t::LEGACY; + int numLoggerStyleVars = 0; - if (m_bCopyToClipBoard) + if (isLegacyStyle) { - m_Logger.Copy(true); - m_bCopyToClipBoard = false; + loggerFlags |= ImGuiChildFlags_Border; + + // Eliminate padding around logger child. This padding gets added when + // ImGuiChildFlags_Border flag gets set. + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 1.f, 1.f }); numLoggerStyleVars++; + + // if we use the legacy theme, also account for one extra space as the + // legacy theme has an extra separator at the bottom of the logger. + footerHeightReserve += style.ItemSpacing.y; } - m_flScrollX = ImGui::GetScrollX(); - m_flScrollY = ImGui::GetScrollY(); + const static int colorLoggerWindowFlags = + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_HorizontalScrollbar | + ImGuiWindowFlags_NoNavInputs | + ImGuiWindowFlags_OverlayHorizontalScrollbar; + + ImGui::BeginChild(m_loggerLabel, ImVec2(0, -footerHeightReserve), loggerFlags, colorLoggerWindowFlags); + + // NOTE: scoped so the mutex releases after we have rendered. + // this is currently the only place the color logger is used + // during the drawing of the base panel. + { + AUTO_LOCK(m_colorTextLoggerMutex); + m_colorTextLogger.Render(); + } + + m_lastFrameScrollPos = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY()); ImGui::EndChild(); - ImGui::PopStyleVar(iVars); - ImGui::Separator(); + if (numLoggerStyleVars) + ImGui::PopStyleVar(numLoggerStyleVars); - std::function fnHandleInput = [&](void) + // The legacy theme also has a spacer here. + if (isLegacyStyle) + ImGui::Separator(); + + ImGui::Text("%s", m_summaryTextBuf); + + const std::function fnHandleInput = [&](void) { - if (m_szInputBuf[0]) + if (m_inputTextBuf[0]) { - ProcessCommand(m_szInputBuf); - ClearAutoComplete(); + ProcessCommand(m_inputTextBuf); + ResetAutoCompleteData(); - m_bModifyInput = true; + m_inputTextBufModified = true; } - BuildSummary(); - m_bReclaimFocus = true; + BuildSummaryText(""); + m_reclaimFocus = true; }; /////////////////////////////////////////////////////////////////////// - ImGui::PushItemWidth(flFooterWidthReserve - 80); - if (ImGui::InputText("##input", m_szInputBuf, IM_ARRAYSIZE(m_szInputBuf), m_nInputFlags, &TextEditCallbackStub, reinterpret_cast(this))) - { - if (m_nSuggestPos > -1) - { - BuildInputFromSelected(m_vSuggest[m_nSuggestPos], m_svInputConVar); - BuildSummary(m_svInputConVar); + const static int inputTextFieldFlags = + ImGuiInputTextFlags_EnterReturnsTrue | + ImGuiInputTextFlags_CallbackCompletion | + ImGuiInputTextFlags_CallbackHistory | + ImGuiInputTextFlags_CallbackAlways | + ImGuiInputTextFlags_CallbackCharFilter | + ImGuiInputTextFlags_CallbackEdit | + ImGuiInputTextFlags_AutoCaretEnd; - m_bModifyInput = true; - m_bReclaimFocus = true; + ImGui::PushItemWidth(footerWidthReserve - 80); + if (ImGui::InputText("##input", m_inputTextBuf, IM_ARRAYSIZE(m_inputTextBuf), inputTextFieldFlags, &TextEditCallbackStub, reinterpret_cast(this))) + { + // If we selected something in the suggestions window, create the + // command from that instead + if (m_suggestPos > ConAutoCompletePos_e::kPark) + { + DetermineInputTextFromSelectedSuggestion(m_vecSuggest[m_suggestPos], m_selectedSuggestionText); + BuildSummaryText(m_selectedSuggestionText.c_str()); + + m_inputTextBufModified = true; + m_reclaimFocus = true; } else { @@ -332,28 +325,30 @@ void CConsole::DrawSurface(void) ImGui::SetItemDefaultFocus(); // Auto-focus input field if reclaim is demanded. - if (m_bReclaimFocus) + if (m_reclaimFocus) { ImGui::SetKeyboardFocusHere(-1); // -1 means previous widget. - m_bReclaimFocus = false; + m_reclaimFocus = false; } - BuildSuggestPanelRect(); + DetermineAutoCompleteWindowRect(); ImGui::SameLine(); if (ImGui::Button("Submit")) { fnHandleInput(); } + ImGui::End(); + return true; } //----------------------------------------------------------------------------- // Purpose: draws the options panel //----------------------------------------------------------------------------- -void CConsole::OptionsPanel(void) +void CConsole::DrawOptionsPanel(void) { - ImGui::Checkbox("Auto-scroll", &m_Logger.m_bAutoScroll); + ImGui::Checkbox("Auto-scroll", &m_colorTextLogger.m_bAutoScroll); ImGui::SameLine(); ImGui::PushItemWidth(100); @@ -366,57 +361,81 @@ void CConsole::OptionsPanel(void) } ImGui::SameLine(); - m_bCopyToClipBoard = ImGui::SmallButton("Copy"); + + // Copies all logged text to the clip board + if (ImGui::SmallButton("Copy")) + { + AUTO_LOCK(m_colorTextLoggerMutex); + m_colorTextLogger.Copy(true); + } ImGui::Text("Console hotkey:"); ImGui::SameLine(); - if (ImGui::Hotkey("##ToggleConsole", &g_pImGuiConfig->m_ConsoleConfig.m_nBind0, ImVec2(80, 80))) + if (ImGui::Hotkey("##ToggleConsole", &g_ImGuiConfig.m_ConsoleConfig.m_nBind0, ImVec2(80, 80))) { - g_pImGuiConfig->Save(); + g_ImGuiConfig.Save(); } ImGui::Text("Browser hotkey:"); ImGui::SameLine(); - if (ImGui::Hotkey("##ToggleBrowser", &g_pImGuiConfig->m_BrowserConfig.m_nBind0, ImVec2(80, 80))) + if (ImGui::Hotkey("##ToggleBrowser", &g_ImGuiConfig.m_BrowserConfig.m_nBind0, ImVec2(80, 80))) { - g_pImGuiConfig->Save(); + g_ImGuiConfig.Save(); } ImGui::EndPopup(); } //----------------------------------------------------------------------------- -// Purpose: draws the suggestion panel with results based on user input +// Purpose: draws the autocomplete panel with results based on user input //----------------------------------------------------------------------------- -void CConsole::SuggestPanel(void) +void CConsole::DrawAutoCompletePanel(void) { - ImGui::Begin("##suggest", nullptr, m_nSuggestFlags); + const static int autoCompleteWindowFlags = + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_AlwaysVerticalScrollbar | + ImGuiWindowFlags_AlwaysHorizontalScrollbar; + + ImGui::Begin("##suggest", nullptr, autoCompleteWindowFlags); ImGui::PushAllowKeyboardFocus(false); - for (size_t i = 0, ns = m_vSuggest.size(); i < ns; i++) + ImGuiWindow* const autocompleteWindow = ImGui::GetCurrentWindow(); + + // NOTE: this makes sure we always draw this window behind the main console + // window, this is necessary as otherwise if you were to drag another + // window above the console, and then focus on the console again, that + // window will now be in between the console window and the autocomplete + // suggest window. + ImGui::BringWindowToDisplayBehind(autocompleteWindow, m_mainWindow); + + for (size_t i = 0, ns = m_vecSuggest.size(); i < ns; i++) { - const CSuggest& suggest = m_vSuggest[i]; - const bool bIsIndexActive = m_nSuggestPos == ssize_t(i); + const ConAutoCompleteSuggest_s& suggest = m_vecSuggest[i]; + const bool isIndexActive = m_suggestPos == ssize_t(i); ImGui::PushID(static_cast(i)); - if (con_suggest_showflags->GetBool()) + if (m_autoCompleteTexturesLoaded && con_autocomplete_window_textures.GetBool()) { // Show the flag texture before the cvar name. - const int mainTexIdx = GetFlagTextureIndex(suggest.m_nFlags); - const MODULERESOURCE& mainRes = m_vFlagIcons[mainTexIdx]; + const int mainTexIdx = GetFlagTextureIndex(suggest.flags); + const MODULERESOURCE& mainRes = m_vecFlagIcons[mainTexIdx]; + ImGui::Image(mainRes.m_idIcon, ImVec2(float(mainRes.m_nWidth), float(mainRes.m_nHeight))); // Show a more detailed description of the flag when user hovers over the texture. if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly) && - suggest.m_nFlags != COMMAND_COMPLETION_MARKER) + suggest.flags != COMMAND_COMPLETION_MARKER) { - std::function fnAddHint = [&](const ConVarFlags::FlagDesc_t& cvarInfo) + const std::function fnAddHint = [&](const ConVarFlags::FlagDesc_t& cvarInfo) { const int hintTexIdx = GetFlagTextureIndex(cvarInfo.bit); - const MODULERESOURCE& hintRes = m_vFlagIcons[hintTexIdx]; + const MODULERESOURCE& hintRes = m_vecFlagIcons[hintTexIdx]; ImGui::Image(hintRes.m_idIcon, ImVec2(float(hintRes.m_nWidth), float(hintRes.m_nHeight))); ImGui::SameLine(); @@ -424,19 +443,19 @@ void CConsole::SuggestPanel(void) }; ImGui::BeginTooltip(); - bool bFlagSet = false; + bool isFlagSet = false; // Reverse loop to display the most significant flag first. for (int j = IM_ARRAYSIZE(g_ConVarFlags.m_FlagsToDesc); (j--) > 0;) { const ConVarFlags::FlagDesc_t& info = g_ConVarFlags.m_FlagsToDesc[j]; - if (suggest.m_nFlags & info.bit) + if (suggest.flags & info.bit) { - bFlagSet = true; + isFlagSet = true; fnAddHint(info); } } - if (!bFlagSet) // Display the FCVAR_NONE flag if no flags are set. + if (!isFlagSet) // Display the FCVAR_NONE flag if no flags are set. { fnAddHint(g_ConVarFlags.m_FlagsToDesc[0]); } @@ -447,37 +466,48 @@ void CConsole::SuggestPanel(void) ImGui::SameLine(); } - if (ImGui::Selectable(suggest.m_svName.c_str(), bIsIndexActive)) + if (ImGui::Selectable(suggest.text.c_str(), isIndexActive)) { ImGui::Separator(); - string svConVar; + string newInputText; - BuildInputFromSelected(suggest, svConVar); - memmove(m_szInputBuf, svConVar.data(), svConVar.size() + 1); + DetermineInputTextFromSelectedSuggestion(suggest, newInputText); + memmove(m_inputTextBuf, newInputText.data(), newInputText.size() + 1); - m_bCanAutoComplete = true; - m_bReclaimFocus = true; + m_canAutoComplete = true; + m_reclaimFocus = true; - BuildSummary(svConVar); + BuildSummaryText(newInputText.c_str()); } + ImGui::PopID(); - // Make sure we bring the currently 'active' item into view. - if (m_bSuggestMoved && bIsIndexActive) + // Update the suggest position + if (m_autoCompletePosMoved) { - ImGuiWindow* pWindow = ImGui::GetCurrentWindow(); - ImRect imRect = ImGui::GetCurrentContext()->LastItemData.Rect; + if (isIndexActive) // Bring the 'active' element into view + { + ImRect imRect = ImGui::GetCurrentContext()->LastItemData.Rect; - // Reset to keep flag in display. - imRect.Min.x = pWindow->InnerRect.Min.x; - imRect.Max.x = pWindow->InnerRect.Min.x; // Set to Min.x on purpose! + // Reset to keep flag icon in display. + imRect.Min.x = autocompleteWindow->InnerRect.Min.x; + imRect.Max.x = autocompleteWindow->InnerRect.Max.x; - // Eliminate jiggle when going up/down in the menu. - imRect.Min.y += 1; - imRect.Max.y -= 1; + // Eliminate jiggle when going up/down in the menu. + imRect.Min.y += 1; + imRect.Max.y -= 1; - ImGui::ScrollToRect(pWindow, imRect); - m_bSuggestMoved = false; + ImGui::ScrollToRect(autocompleteWindow, imRect); + m_autoCompletePosMoved = false; + } + else if (m_suggestPos == ConAutoCompletePos_e::kPark) + { + // Reset position; kPark = no active element. + ImGui::SetScrollX(0.0f); + ImGui::SetScrollY(0.0f); + + m_autoCompletePosMoved = false; + } } } @@ -489,57 +519,63 @@ void CConsole::SuggestPanel(void) // Purpose: runs the auto complete for the console // Output : true if auto complete is performed, false otherwise //----------------------------------------------------------------------------- -bool CConsole::AutoComplete(void) +bool CConsole::RunAutoComplete(void) { // Don't suggest if user tries to assign value to ConVar or execute ConCommand. - if (!m_szInputBuf[0] || strstr(m_szInputBuf, ";")) + if (!m_inputTextBuf[0] || strstr(m_inputTextBuf, ";")) { - if (m_bSuggestActive) + if (m_autoCompleteActive) { - ClearAutoComplete(); - m_bCanAutoComplete = false; + ResetAutoCompleteData(); } + return false; } - if (!strstr(m_szInputBuf, " ")) + if (!strstr(m_inputTextBuf, " ")) { - if (m_bCanAutoComplete) + if (m_canAutoComplete) { - FindFromPartial(); + CreateSuggestionsFromPartial(); } } - else if (m_bCanAutoComplete) // Command completion callback. + else if (m_canAutoComplete) // Command completion callback. { - ClearAutoComplete(); - m_bCanAutoComplete = false; + ResetAutoCompleteData(); - string svCommand; + char szCommand[sizeof(m_inputTextBuf)]; + size_t i = 0; - for (size_t i = 0; i < sizeof(m_szInputBuf); i++) + // Truncate everything past (and including) the space to get the + // command string. + for (; i < sizeof(m_inputTextBuf); i++) { - if (isspace(m_szInputBuf[i])) + const char c = m_inputTextBuf[i]; + + if (c == '\0' || isspace(c)) { break; } - svCommand += m_szInputBuf[i]; + + szCommand[i] = c; } - ConCommand* pCommand = g_pCVar->FindCommand(svCommand.c_str()); + szCommand[i] = '\0'; + ConCommand* const pCommand = g_pCVar->FindCommand(szCommand); if (pCommand && pCommand->CanAutoComplete()) { CUtlVector< CUtlString > commands; - int iret = pCommand->AutoCompleteSuggest(m_szInputBuf, commands); + const int iret = pCommand->AutoCompleteSuggest(m_inputTextBuf, commands); if (!iret) { return false; } - for (int i = 0; i < iret; ++i) + for (int j = 0; j < iret; ++j) { - m_vSuggest.push_back(CSuggest(commands[i].String(), COMMAND_COMPLETION_MARKER)); + m_vecSuggest.push_back(ConAutoCompleteSuggest_s(commands[j].String(), COMMAND_COMPLETION_MARKER)); } } else @@ -548,221 +584,202 @@ bool CConsole::AutoComplete(void) } } - if (m_vSuggest.empty()) + if (m_vecSuggest.empty()) { - ResetAutoComplete(); - m_bCanAutoComplete = false; - return false; } - m_bSuggestActive = true; + m_autoCompleteActive = true; return true; } //----------------------------------------------------------------------------- // Purpose: resets the auto complete window //----------------------------------------------------------------------------- -void CConsole::ResetAutoComplete(void) +void CConsole::ResetAutoCompleteData(void) { - m_bSuggestActive = false; - m_nSuggestPos = -1; -} - -//----------------------------------------------------------------------------- -// Purpose: clears the auto complete window -//----------------------------------------------------------------------------- -void CConsole::ClearAutoComplete(void) -{ - ResetAutoComplete(); - m_vSuggest.clear(); + m_suggestPos = ConAutoCompletePos_e::kPark; + m_canAutoComplete = false; + m_autoCompleteActive = false; + m_autoCompletePosMoved = true; + m_vecSuggest.clear(); } //----------------------------------------------------------------------------- // Purpose: find ConVars/ConCommands from user input and add to vector // - Ignores ConVars marked FCVAR_HIDDEN //----------------------------------------------------------------------------- -void CConsole::FindFromPartial(void) +void CConsole::CreateSuggestionsFromPartial(void) { - ClearAutoComplete(); - m_bCanAutoComplete = false; + ResetAutoCompleteData(); ICvar::Iterator iter(g_pCVar); for (iter.SetFirst(); iter.IsValid(); iter.Next()) { - if (m_vSuggest.size() >= size_t(con_suggest_limit->GetInt())) + if (m_vecSuggest.size() >= con_suggest_limit.GetInt()) { break; } - const ConCommandBase* pCommandBase = iter.Get(); - if (pCommandBase->IsFlagSet(FCVAR_HIDDEN)) + const ConCommandBase* const commandBase = iter.Get(); + + if (commandBase->IsFlagSet(FCVAR_HIDDEN)) { continue; } - const char* pCommandName = pCommandBase->GetName(); - if (!V_stristr(pCommandName, m_szInputBuf)) + const char* const commandName = commandBase->GetName(); + + if (!V_stristr(commandName, m_inputTextBuf)) { continue; } - if (std::find(m_vSuggest.begin(), m_vSuggest.end(), - pCommandName) == m_vSuggest.end()) + if (std::find(m_vecSuggest.begin(), m_vecSuggest.end(), + commandName) == m_vecSuggest.end()) { - string svValue; + string docString; - if (!pCommandBase->IsCommand()) + if (!commandBase->IsCommand()) { - const ConVar* pConVar = reinterpret_cast(pCommandBase); + const ConVar* conVar = reinterpret_cast(commandBase); - svValue = " = ["; // Assign default value to string if its a ConVar. - svValue.append(pConVar->GetString()); - svValue.append("]"); + docString = " = ["; // Assign current value to string if its a ConVar. + docString.append(conVar->GetString()); + docString.append("]"); } - if (con_suggest_showhelptext->GetBool()) + if (con_suggest_helptext.GetBool()) { - if (pCommandBase->GetHelpText()) + std::function fnAppendDocString = [&](string& targetString, const char* toAppend) { - string svHelpText = pCommandBase->GetHelpText(); - if (!svHelpText.empty()) + if (VALID_CHARSTAR(toAppend)) { - svValue.append(" - \"" + svHelpText + "\""); + targetString.append(" - \""); + targetString.append(toAppend); + targetString.append("\""); } - } - if (pCommandBase->GetUsageText()) - { - string svUsageText = pCommandBase->GetUsageText(); - if (!svUsageText.empty()) - { - svValue.append(" - \"" + svUsageText + "\""); - } - } + }; + + fnAppendDocString(docString, commandBase->GetHelpText()); + fnAppendDocString(docString, commandBase->GetUsageText()); } - m_vSuggest.push_back(CSuggest(pCommandName + svValue, pCommandBase->GetFlags())); + m_vecSuggest.push_back(ConAutoCompleteSuggest_s(commandName + docString, commandBase->GetFlags())); + } + else + { + // Trying to push a duplicate ConCommandBase in the vector; code bug. + Assert(0); } - else { break; } } - std::sort(m_vSuggest.begin(), m_vSuggest.end()); + std::sort(m_vecSuggest.begin(), m_vecSuggest.end()); } //----------------------------------------------------------------------------- // Purpose: processes submitted commands for the main thread -// Input : svCommand - +// Input : inputText - //----------------------------------------------------------------------------- -void CConsole::ProcessCommand(string svCommand) +void CConsole::ProcessCommand(const char* const inputText) { - StringRTrim(svCommand, " "); // Remove trailing white space characters to prevent history duplication. - AddLog(ImVec4(1.00f, 0.80f, 0.60f, 1.00f), "%s] %s\n", Plat_GetProcessUpTime(), svCommand.c_str()); + string commandFormatted(inputText); + StringRTrim(commandFormatted, " "); // Remove trailing white space characters to prevent history duplication. - Cbuf_AddText(Cbuf_GetCurrentPlayer(), svCommand.c_str(), cmd_source_t::kCommandSrcCode); - m_nHistoryPos = -1; + const ImU32 commandColor = ImGui::ColorConvertFloat4ToU32(ImVec4(1.00f, 0.80f, 0.60f, 1.00f)); + AddLog(commandColor, "%s] %s\n", Plat_GetProcessUpTime(), commandFormatted.c_str()); - for (size_t i = m_vHistory.size(); i-- > 0;) - { - if (m_vHistory[i].compare(svCommand) == 0) - { - m_vHistory.erase(m_vHistory.begin() + i); - break; - } - } + Cbuf_AddText(Cbuf_GetCurrentPlayer(), commandFormatted.c_str(), cmd_source_t::kCommandSrcCode); + m_historyPos = ConAutoCompletePos_e::kPark; - m_vHistory.push_back(svCommand); - m_Logger.ShouldScrollToBottom(true); + AddHistory(commandFormatted.c_str()); + + m_colorTextLogger.ShouldScrollToStart(true); + m_colorTextLogger.ShouldScrollToBottom(true); } //----------------------------------------------------------------------------- -// Purpose: builds the console summary -// Input : svConVar - +// Purpose: builds the console summary, this function will attempt to search +// for a ConVar first, from which it formats the current and default +// value. If the string is empty, or no ConVar is found, the function +// formats the number of history items instead +// Input : inputText - //----------------------------------------------------------------------------- -void CConsole::BuildSummary(string svConVar) +void CConsole::BuildSummaryText(const char* const inputText) { - if (!svConVar.empty()) + if (*inputText) { - // Remove trailing space and/or semicolon before we call 'g_pCVar->FindVar(..)'. - StringRTrim(svConVar, " ;", true); + string conVarFormatted(inputText); - if (const ConVar* pConVar = g_pCVar->FindVar(svConVar.c_str())) + // Remove trailing space and/or semicolon before we call 'g_pCVar->FindVar(..)'. + StringRTrim(conVarFormatted, " ;", true); + const ConVar* const conVar = g_pCVar->FindVar(conVarFormatted.c_str()); + + if (conVar && !conVar->IsFlagSet(FCVAR_HIDDEN)) { // Display the current and default value of ConVar if found. - snprintf(m_szSummary, sizeof(m_szSummary), "(\"%s\", default \"%s\")", pConVar->GetString(), pConVar->GetDefault()); + snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "(\"%s\", default \"%s\")", + conVar->GetString(), conVar->GetDefault()); + return; } } - snprintf(m_szSummary, sizeof(m_szSummary), "%zu history items", m_vHistory.size()); + snprintf(m_summaryTextBuf, sizeof(m_summaryTextBuf), "%zu history items", m_vecHistory.size()); } //----------------------------------------------------------------------------- -// Purpose: builds the selected suggestion for input field +// Purpose: creates the selected suggestion for input field // Input : &suggest - //----------------------------------------------------------------------------- -void CConsole::BuildInputFromSelected(const CSuggest& suggest, string& svInput) +void CConsole::DetermineInputTextFromSelectedSuggestion(const ConAutoCompleteSuggest_s& suggest, string& svInput) { - if (suggest.m_nFlags == COMMAND_COMPLETION_MARKER) + if (suggest.flags == COMMAND_COMPLETION_MARKER) { - svInput = suggest.m_svName + ' '; + svInput = suggest.text + ' '; } else // Remove the default value from ConVar before assigning it to the input buffer. { - svInput = suggest.m_svName.substr(0, suggest.m_svName.find(' ')) + ' '; + svInput = suggest.text.substr(0, suggest.text.find(' ')) + ' '; } } //----------------------------------------------------------------------------- -// Purpose: builds the suggestion panel rect +// Purpose: determines the autocomplete window rect //----------------------------------------------------------------------------- -void CConsole::BuildSuggestPanelRect(void) +void CConsole::DetermineAutoCompleteWindowRect(void) { float flSinglePadding = 0.f; const float flItemHeight = ImGui::GetTextLineHeightWithSpacing() + 1.0f; - if (m_vSuggest.size() > 1) + if (m_vecSuggest.size() > 1) { // Pad with 18 to keep all items in view. flSinglePadding = flItemHeight; } - m_ivSuggestWindowPos = ImGui::GetItemRectMin(); - m_ivSuggestWindowPos.y += ImGui::GetItemRectSize().y; + // NOTE: last item rect = the input text box, the idea here is to set the + // pos to that of the input text bar, whilst also clamping the width to it. + const ImVec2 lastItemRectMin = ImGui::GetItemRectMin(); + const ImVec2 lastItemRectSize = ImGui::GetItemRectSize(); - const float flWindowHeight = (flSinglePadding + std::clamp( - static_cast(m_vSuggest.size()) * (flItemHeight), 37.0f, 127.5f)); + m_autoCompleteWindowPos = lastItemRectMin; + m_autoCompleteWindowPos.y += lastItemRectSize.y; - m_ivSuggestWindowSize = ImVec2(600, flWindowHeight); -} + const float maxWindowWidth = con_autocomplete_window_width.GetFloat(); -//----------------------------------------------------------------------------- -// Purpose: clamps the size of the log vector -//----------------------------------------------------------------------------- -void CConsole::ClampLogSize(void) -{ - const int nMaxLines = con_max_lines->GetInt(); + const float flWindowWidth = maxWindowWidth > 0 + ? ImMin(con_autocomplete_window_width.GetFloat(), lastItemRectSize.x) + : lastItemRectSize.x; - if (m_Logger.GetTotalLines() > nMaxLines) - { - while (m_Logger.GetTotalLines() > nMaxLines) - { - m_Logger.RemoveLine(0); - m_nScrollBack++; - m_nSelectBack++; - } - m_Logger.MoveSelection(m_nSelectBack, false); - m_Logger.MoveCursor(m_nSelectBack, false); - m_nSelectBack = 0; - } -} + // NOTE: minimum vertical size of the window, going below this will + // truncate the first element in the window making it looked bugged. + const static float minWindowHeight = 37.0f; -//----------------------------------------------------------------------------- -// Purpose: clamps the size of the history vector -//----------------------------------------------------------------------------- -void CConsole::ClampHistorySize(void) -{ - while (m_vHistory.size() > size_t(con_max_history->GetInt())) - { - m_vHistory.erase(m_vHistory.begin()); - } + const float flWindowHeight = flSinglePadding + ImClamp( + static_cast(m_vecSuggest.size() * flItemHeight), + minWindowHeight, + con_autocomplete_window_height.GetFloat()); + + m_autoCompleteWindowRect = ImVec2(flWindowWidth, flWindowHeight); } //----------------------------------------------------------------------------- @@ -776,18 +793,20 @@ bool CConsole::LoadFlagIcons(void) // Get all flag image resources for displaying flags. for (int i = IDB_PNG3, k = NULL; i <= IDB_PNG32; i++, k++) { - m_vFlagIcons.push_back(MODULERESOURCE(GetModuleResource(i))); - MODULERESOURCE& rFlagIcon = m_vFlagIcons[k]; + m_vecFlagIcons.push_back(MODULERESOURCE(GetModuleResource(i))); + MODULERESOURCE& rFlagIcon = m_vecFlagIcons[k]; ret = LoadTextureBuffer(reinterpret_cast(rFlagIcon.m_pData), // !TODO: Fall-back texture. static_cast(rFlagIcon.m_nSize), &rFlagIcon.m_idIcon, &rFlagIcon.m_nWidth, &rFlagIcon.m_nHeight); - IM_ASSERT(ret); if (!ret) { + Assert(0, "Texture flags load failed for %i", i); break; } } + + m_autoCompleteTexturesLoaded = ret; return ret; } @@ -796,9 +815,9 @@ bool CConsole::LoadFlagIcons(void) // in the future we should build the texture procedurally with use of popcnt. // Input : nFlags - //----------------------------------------------------------------------------- -int CConsole::GetFlagTextureIndex(int nFlags) const +int CConsole::GetFlagTextureIndex(const int flags) const { - switch (nFlags) // All indices for single/dual flag textures. + switch (flags) // All indices for single/dual flag textures. { case FCVAR_DEVELOPMENTONLY: return 9; @@ -845,7 +864,7 @@ int CConsole::GetFlagTextureIndex(int nFlags) const default: // Hit when flag is zero/non-indexed or 3+ bits are set. - int v = __popcnt(nFlags); + const unsigned int v = __popcnt(flags); switch (v) { case 0: @@ -858,17 +877,17 @@ int CConsole::GetFlagTextureIndex(int nFlags) const // and display the appropriate checker texture. bool mul = v > 2; - if (nFlags & FCVAR_DEVELOPMENTONLY) + if (flags & FCVAR_DEVELOPMENTONLY) { return mul ? 4 : 3; } - else if (nFlags & FCVAR_CHEAT) + else if (flags & FCVAR_CHEAT) { return mul ? 6 : 5; } - else if (nFlags & FCVAR_RELEASE && // RELEASE command but no context restriction. - !(nFlags & FCVAR_SERVER_CAN_EXECUTE) && - !(nFlags & FCVAR_CLIENTCMD_CAN_EXECUTE)) + else if (flags & FCVAR_RELEASE && // RELEASE command but no context restriction. + !(flags & FCVAR_SERVER_CAN_EXECUTE) && + !(flags & FCVAR_CLIENTCMD_CAN_EXECUTE)) { return mul ? 8 : 7; } @@ -907,89 +926,99 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) } case ImGuiInputTextFlags_CallbackHistory: { - if (m_bSuggestActive) + if (m_autoCompleteActive) { - if (iData->EventKey == ImGuiKey_UpArrow && m_nSuggestPos > - 1) + if (iData->EventKey == ImGuiKey_UpArrow && m_suggestPos > - 1) { - m_nSuggestPos--; - m_bSuggestMoved = true; + m_suggestPos--; + m_autoCompletePosMoved = true; } else if (iData->EventKey == ImGuiKey_DownArrow) { - if (m_nSuggestPos < static_cast(m_vSuggest.size()) - 1) + if (m_suggestPos < static_cast(m_vecSuggest.size()) - 1) { - m_nSuggestPos++; - m_bSuggestMoved = true; + m_suggestPos++; + m_autoCompletePosMoved = true; } } } else // Allow user to navigate through the history if suggest panel isn't drawn. { - const ssize_t nPrevHistoryPos = m_nHistoryPos; + const int prevHistoryPos = m_historyPos; + if (iData->EventKey == ImGuiKey_UpArrow) { - if (m_nHistoryPos == -1) + if (m_historyPos == ConAutoCompletePos_e::kPark) { - m_nHistoryPos = static_cast(m_vHistory.size()) - 1; + m_historyPos = static_cast(m_vecHistory.size()) - 1; } - else if (m_nHistoryPos > 0) + else if (m_historyPos > 0) { - m_nHistoryPos--; + m_historyPos--; } } else if (iData->EventKey == ImGuiKey_DownArrow) { - if (m_nHistoryPos != -1) + if (m_historyPos != ConAutoCompletePos_e::kPark) { - if (++m_nHistoryPos >= static_cast(m_vHistory.size())) + if (++m_historyPos >= static_cast(m_vecHistory.size())) { - m_nHistoryPos = -1; + m_historyPos = ConAutoCompletePos_e::kPark; } } } - if (nPrevHistoryPos != m_nHistoryPos) + + if (prevHistoryPos != m_historyPos) { - string svHistory = (m_nHistoryPos >= 0) ? m_vHistory[m_nHistoryPos] : ""; - if (!svHistory.empty()) + string historyText = (m_historyPos >= 0) ? m_vecHistory[m_historyPos] : ""; + + if (!historyText.empty()) { - if (m_vHistory[m_nHistoryPos].find(' ') == string::npos) + if (historyText.find(' ') == string::npos) { - // Append whitespace to previous entered command if absent or no parameters where passed. - svHistory.append(" "); + // Append whitespace to previous entered command if + // absent or no parameters where passed. This is to + // the user could directly start adding their own + // params without needing to hit the space bar first. + historyText.append(" "); } } iData->DeleteChars(0, iData->BufTextLen); - iData->InsertChars(0, svHistory.c_str()); + iData->InsertChars(0, historyText.c_str()); } } - BuildSummary(iData->Buf); + + BuildSummaryText(iData->Buf); break; } case ImGuiInputTextFlags_CallbackAlways: { - m_nInputTextLen = iData->BufTextLen; - if (m_bModifyInput) // User entered a value in the input field. + m_selectedSuggestionTextLen = iData->BufTextLen; + + if (m_inputTextBufModified) // User entered a value in the input field. { - iData->DeleteChars(0, m_nInputTextLen); - m_bSuggestActive = false; + iData->DeleteChars(0, m_selectedSuggestionTextLen); - if (!m_svInputConVar.empty()) // User selected a ConVar from the suggestion window, copy it to the buffer. + if (!m_selectedSuggestionText.empty()) // User selected a ConVar from the suggestion window, copy it to the buffer. { - iData->InsertChars(0, m_svInputConVar.c_str()); - m_svInputConVar.clear(); + iData->InsertChars(0, m_selectedSuggestionText.c_str()); + m_selectedSuggestionText.clear(); - m_bCanAutoComplete = true; - m_bReclaimFocus = true; + m_canAutoComplete = true; + m_reclaimFocus = true; } - m_bModifyInput = false; + + m_inputTextBufModified = false; } + break; } case ImGuiInputTextFlags_CallbackCharFilter: { const ImWchar c = iData->EventChar; - if (!m_nInputTextLen) + + if (!m_selectedSuggestionTextLen) { if (c == '~') // Discard tilde character as first input. { @@ -997,6 +1026,7 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) return 1; } } + if (c == '`') // Discard back quote character (default console invoke key). { iData->EventChar = 0; @@ -1015,19 +1045,20 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) iData->DeleteChars(0, 1); } - if (iData->BufTextLen) // Attempt to build a summary.. + if (iData->BufTextLen) { - BuildSummary(iData->Buf); - m_bCanAutoComplete = true; + m_canAutoComplete = true; } else // Reset state and enable history scrolling when buffer is empty. { - m_bCanAutoComplete = false; + ResetAutoCompleteData(); } + BuildSummaryText(iData->Buf); break; } } + return 0; } @@ -1038,37 +1069,39 @@ int CConsole::TextEditCallback(ImGuiInputTextCallbackData* iData) //----------------------------------------------------------------------------- int CConsole::TextEditCallbackStub(ImGuiInputTextCallbackData* iData) { - CConsole* pConsole = reinterpret_cast(iData->UserData); + CConsole* const pConsole = reinterpret_cast(iData->UserData); return pConsole->TextEditCallback(iData); } //----------------------------------------------------------------------------- -// Purpose: adds logs to the vector +// Purpose: adds logs to the console; this is the only place text is added to +// the vector, do not call 'm_Logger.InsertText' elsewhere as we also manage +// the size of the vector here !!! // Input : &conLog - //----------------------------------------------------------------------------- -void CConsole::AddLog(const ConLog_t& conLog) +void CConsole::AddLog(const char* const text, const ImU32 color) { - std::lock_guard l(m_Mutex); - m_Logger.InsertText(conLog); + AUTO_LOCK(m_colorTextLoggerMutex); + + m_colorTextLogger.InsertText(text, color); + ClampLogSize(); } //----------------------------------------------------------------------------- -// Purpose: adds logs to the vector (internal) -// Only call when mutex lock is obtained! +// Purpose: adds logs to the console (internal) // Input : &color - // *fmt - // ... - //----------------------------------------------------------------------------- -void CConsole::AddLog(const ImVec4& color, const char* fmt, ...) /*IM_FMTARGS(2)*/ +void CConsole::AddLog(const ImU32 color, const char* fmt, ...) /*IM_FMTARGS(2)*/ { - string result; va_list args; - va_start(args, fmt); - result = FormatV(fmt, args); + + string result = FormatV(fmt, args); va_end(args); - m_Logger.InsertText(ConLog_t(result, color)); + AddLog(result.c_str(), color); } //----------------------------------------------------------------------------- @@ -1078,13 +1111,14 @@ void CConsole::AddLog(const ImVec4& color, const char* fmt, ...) /*IM_FMTARGS(2) //----------------------------------------------------------------------------- void CConsole::RemoveLog(int nStart, int nEnd) { - std::lock_guard l(m_Mutex); - int nLines = m_Logger.GetTotalLines(); + AUTO_LOCK(m_colorTextLoggerMutex); - if (nEnd >= nLines) + const int numLines = m_colorTextLogger.GetTotalLines(); + + if (nEnd >= numLines) { // Sanitize for last array elem. - nEnd = (nLines - 1); + nEnd = (numLines - 1); } if (nStart >= nEnd) @@ -1105,13 +1139,13 @@ void CConsole::RemoveLog(int nStart, int nEnd) } // User wants to remove everything. - if (nLines <= (nStart - nEnd)) + if (numLines <= (nStart - nEnd)) { ClearLog(); return; } - m_Logger.RemoveLine(nStart, nEnd); + m_colorTextLogger.RemoveLine(nStart, nEnd); } //----------------------------------------------------------------------------- @@ -1119,18 +1153,64 @@ void CConsole::RemoveLog(int nStart, int nEnd) //----------------------------------------------------------------------------- void CConsole::ClearLog(void) { - std::lock_guard l(m_Mutex); - m_Logger.RemoveLine(0, (m_Logger.GetTotalLines() - 1)); + AUTO_LOCK(m_colorTextLoggerMutex); + m_colorTextLogger.RemoveLine(0, (m_colorTextLogger.GetTotalLines() - 1)); +} + +//----------------------------------------------------------------------------- +// Purpose: clamps the size of the log vector +//----------------------------------------------------------------------------- +void CConsole::ClampLogSize(void) +{ + // +1 since the first row is a dummy + const int maxLines = con_max_lines.GetInt() + 1; + + if (m_colorTextLogger.GetTotalLines() > maxLines) + { + while (m_colorTextLogger.GetTotalLines() > maxLines) + { + m_colorTextLogger.RemoveLine(0); + + m_scrollBackAmount++; + m_selectBackAmount++; + } + + m_colorTextLogger.MoveSelection(m_selectBackAmount, false); + m_colorTextLogger.MoveCursor(m_selectBackAmount, false); + + m_selectBackAmount = 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: adds a command to the history vector; this is the only place text +// is added to the vector, do not call 'm_History.push_back' elsewhere as we +// also manage the size of the vector here !!! +//----------------------------------------------------------------------------- +void CConsole::AddHistory(const char* const command) +{ + // If this command was already in the history, remove it so when we push it + // in, it would appear all the way at the top of the list + for (size_t i = m_vecHistory.size(); i-- > 0;) + { + if (m_vecHistory[i].compare(command) == 0) + { + m_vecHistory.erase(m_vecHistory.begin() + i); + break; + } + } + + m_vecHistory.push_back(command); + ClampHistorySize(); } //----------------------------------------------------------------------------- // Purpose: gets all console submissions // Output : vector of strings //----------------------------------------------------------------------------- -vector CConsole::GetHistory(void) const +const vector& CConsole::GetHistory(void) const { - std::lock_guard l(m_Mutex); - return m_vHistory; + return m_vecHistory; } //----------------------------------------------------------------------------- @@ -1138,20 +1218,73 @@ vector CConsole::GetHistory(void) const //----------------------------------------------------------------------------- void CConsole::ClearHistory(void) { - std::lock_guard l(m_Mutex); - m_vHistory.clear(); - BuildSummary(); + m_vecHistory.clear(); + BuildSummaryText(""); } //----------------------------------------------------------------------------- -// Purpose: sets the console front-end style +// Purpose: clamps the size of the history vector //----------------------------------------------------------------------------- -void CConsole::SetStyleVar(void) +void CConsole::ClampHistorySize(void) { - m_Style = g_pImGuiConfig->InitStyle(); - - ImGui::SetNextWindowSize(ImVec2(1200, 524), ImGuiCond_FirstUseEver); - ImGui::SetWindowPos(ImVec2(-1000, 50), ImGuiCond_FirstUseEver); + while (m_vecHistory.size() > con_max_history.GetInt()) + { + m_vecHistory.erase(m_vecHistory.begin()); + } } -CConsole* g_pConsole = new CConsole(); +//----------------------------------------------------------------------------- +// Purpose: toggles the console +//----------------------------------------------------------------------------- +void CConsole::ToggleConsole_f() +{ + g_Console.m_activated ^= true; + ResetInput(); // Disable input to game when console is drawn. +} + +//----------------------------------------------------------------------------- +// Purpose: shows the game console submission history. +//----------------------------------------------------------------------------- +void CConsole::LogHistory_f() +{ + const vector& vHistory = g_Console.GetHistory(); + for (size_t i = 0, nh = vHistory.size(); i < nh; i++) + { + Msg(eDLL_T::COMMON, "%3d: %s\n", i, vHistory[i].c_str()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: removes a range of lines from the console. +//----------------------------------------------------------------------------- +void CConsole::RemoveLine_f(const CCommand& args) +{ + if (args.ArgC() < 3) + { + Msg(eDLL_T::CLIENT, "Usage 'con_removeline': start(int) end(int)\n"); + return; + } + + const int start = atoi(args[1]); + const int end = atoi(args[2]); + + g_Console.RemoveLog(start, end); +} + +//----------------------------------------------------------------------------- +// Purpose: clears all lines from the developer console. +//----------------------------------------------------------------------------- +void CConsole::ClearLines_f() +{ + g_Console.ClearLog(); +} + +//----------------------------------------------------------------------------- +// Purpose: clears all submissions from the developer console history. +//----------------------------------------------------------------------------- +void CConsole::ClearHistory_f() +{ + g_Console.ClearHistory(); +} + +CConsole g_Console; diff --git a/r5dev/gameui/IConsole.h b/r5dev/gameui/IConsole.h index e48dba35..7066098f 100644 --- a/r5dev/gameui/IConsole.h +++ b/r5dev/gameui/IConsole.h @@ -2,11 +2,12 @@ #ifndef DEDICATED #include "common/sdkdefs.h" #include "windows/resource.h" -#include "public/isurfacesystem.h" -#include "thirdparty/imgui/misc/imgui_logger.h" -#include "thirdparty/imgui/misc/imgui_utility.h" +#include "imgui/misc/imgui_logger.h" +#include "imgui/misc/imgui_utility.h" -class CConsole : public ISurface +#include "imgui_surface.h" + +class CConsole : public CImguiSurface { public: /////////////////////////////////////////////////////////////////////////// @@ -14,100 +15,162 @@ public: virtual ~CConsole(void); virtual bool Init(void); - virtual void Think(void); + virtual void Shutdown(void); virtual void RunFrame(void); - virtual void RunTask(void); - - virtual void DrawSurface(void); + virtual bool DrawSurface(void); private: - void OptionsPanel(void); - void SuggestPanel(void); + void DrawOptionsPanel(void); + void DrawAutoCompletePanel(void); - bool AutoComplete(void); - void ResetAutoComplete(void); - void ClearAutoComplete(void); + bool RunAutoComplete(void); + void ResetAutoCompleteData(void); - void FindFromPartial(void); - void ProcessCommand(string svCommand); + void CreateSuggestionsFromPartial(void); + void ProcessCommand(const char* const inputText); - void BuildSummary(string svConVar = ""); - void BuildInputFromSelected(const CSuggest& suggest, string& svInput); - void BuildSuggestPanelRect(void); + void BuildSummaryText(const char* const inputText); - void ClampLogSize(void); - void ClampHistorySize(void); + struct ConAutoCompleteSuggest_s; + void DetermineInputTextFromSelectedSuggestion(const ConAutoCompleteSuggest_s& suggest, string& svInput); + void DetermineAutoCompleteWindowRect(void); bool LoadFlagIcons(void); - int GetFlagTextureIndex(int nFlags) const; + int GetFlagTextureIndex(const int flags) const; int TextEditCallback(ImGuiInputTextCallbackData* pData); static int TextEditCallbackStub(ImGuiInputTextCallbackData* pData); /////////////////////////////////////////////////////////////////////////// public: - void AddLog(const ConLog_t& conLog); + void AddLog(const char* const text, const ImU32 color); void RemoveLog(int nStart, int nEnd); void ClearLog(void); - vector GetHistory(void) const; + void AddHistory(const char* const command); + const vector& GetHistory(void) const; void ClearHistory(void); - inline bool IsVisible() { return m_flFadeAlpha > 0.0f; } +public: + // Console command callbacks + static void ToggleConsole_f(); + static void LogHistory_f(); + static void RemoveLine_f(const CCommand& args); + static void ClearLines_f(); + static void ClearHistory_f(); -private: // Internal only. - void AddLog(const ImVec4& color, const char* fmt, ...) /*IM_FMTARGS(2)*/; +private: // Internals. + void AddLog(const ImU32 color, const char* fmt, ...) /*IM_FMTARGS(2)*/; - /////////////////////////////////////////////////////////////////////////// - virtual void SetStyleVar(void); + void ClampLogSize(void); + void ClampHistorySize(void); + +private: + enum ConAutoCompletePos_e + { + // Park means the position is out of screen. + kPark = -1, + }; + + struct ConAutoCompleteSuggest_s + { + ConAutoCompleteSuggest_s(const string& inText, const int inFlags) + { + text = inText; + flags = inFlags; + } + bool operator==(const string& a) const + { + return text.compare(a) == 0; + } + bool operator<(const ConAutoCompleteSuggest_s& a) const + { + return text < a.text; + } + + string text; + int flags; + }; private: /////////////////////////////////////////////////////////////////////////// - const char* m_pszConsoleLabel; - const char* m_pszLoggingLabel; - char m_szInputBuf[512]; - char m_szSummary[512]; - char m_szWindowLabel[512]; + ImGuiWindow* m_mainWindow; + const char* m_loggerLabel; + char m_inputTextBuf[512]; + char m_summaryTextBuf[256]; - string m_svInputConVar; - ssize_t m_nHistoryPos; - ssize_t m_nSuggestPos; - int m_nScrollBack; - int m_nSelectBack; - int m_nInputTextLen; - float m_flScrollX; - float m_flScrollY; - float m_flFadeAlpha; + // The selected ConVar from the suggestions in the autocomplete window gets + // copied into this buffer. + string m_selectedSuggestionText; + int m_selectedSuggestionTextLen; - bool m_bInitialized; - bool m_bReclaimFocus; - bool m_bCopyToClipBoard; - bool m_bModifyInput; + // The positions in the history buffer; when there is nothing in the input + // text field, the arrow keys would instead iterate over the previously + // submitted commands and show the currently selected one right in the + // input text field. + int m_historyPos; - bool m_bCanAutoComplete; - bool m_bSuggestActive; - bool m_bSuggestMoved; - bool m_bSuggestUpdate; + // The position in the autocomplete window; this dictates the current + // (highlighted) suggestion, and copies that one into m_selectedSuggestion + // once selected. + int m_suggestPos; - vector m_vSuggest; - vector m_vFlagIcons; - vector m_vHistory; + // Scroll and select back amount; if text lines are getting removed to + // clamp the size to the maximum allowed, we need to scroll back (and + // if text has been selected, select back this amount) to prevent the + // view or selection from drifting. + int m_scrollBackAmount; + int m_selectBackAmount; - ImGuiStyle_t m_Style; - ImVec2 m_ivSuggestWindowPos; - ImVec2 m_ivSuggestWindowSize; - CTextLogger m_Logger; - mutable std::mutex m_Mutex; + // Scroll position of the last drawn frame, after any scroll back has been + // applied, this is used as a base for scrolling back when entries are + // getting removed. + ImVec2 m_lastFrameScrollPos; - ImGuiInputTextFlags m_nInputFlags; - ImGuiWindowFlags m_nSuggestFlags; - ImGuiWindowFlags m_nLoggingFlags; + // Set when the input text has been modified, used to rebuild suggestions + // shown in the autocomplete window. + bool m_inputTextBufModified; -public: - bool m_bActivate = false; + // Determines whether we can autocomplete and build a list of suggestions, + // e.g. when you type "map " (with a trailing white space ' '), and "map" + // happens to be a ConCommand with an autocomplete function, this gets set + // and the autocomplete function of that ConCommand will be called to + // create a list of suggestions to be shown in the autocomplete window. + // This member is always false when the input text is empty. + bool m_canAutoComplete; + + // Whether the autocomplete window is active. If this is set, the arrow up + // and down keys will be used for the auto complete window instead of the + // history (previously submitted commands) scroller. + bool m_autoCompleteActive; + + // If the position in the autocomplete window had moved, this var will be + // set. This is used to check if we need to adjust the scroll position in + // the autocomplete window to keep the current selection visible. + bool m_autoCompletePosMoved; + + // If the textures failed to load, this will remain false and no textures + // will be drawn in the autocomplete window. This is because if one fails + // to load, the indices will be incorrect. + bool m_autoCompleteTexturesLoaded; + + // The position and rect of the autocomplete window, the pos is set to that + // of the input text field + an offset to move it under the item. + ImVec2 m_autoCompleteWindowPos; + ImVec2 m_autoCompleteWindowRect; + + vector m_vecSuggest; + vector m_vecFlagIcons; + vector m_vecHistory; + + // The color logger in which text gets added, note that the mutex lock + // should always be acquired when using this as this is accessed from + // multiple threads! + CTextLogger m_colorTextLogger; + mutable CThreadFastMutex m_colorTextLoggerMutex; }; /////////////////////////////////////////////////////////////////////////////// -extern CConsole* g_pConsole; +extern CConsole g_Console; #endif // !DEDICATED diff --git a/r5dev/gameui/imgui_surface.cpp b/r5dev/gameui/imgui_surface.cpp new file mode 100644 index 00000000..da0778c8 --- /dev/null +++ b/r5dev/gameui/imgui_surface.cpp @@ -0,0 +1,43 @@ +#include "imgui_surface.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CImguiSurface::CImguiSurface() + : m_surfaceLabel("Surface") + , m_fadeAlpha(0.0f) + , m_surfaceStyle(ImGuiStyle_t::NONE) + , m_initialized(false) + , m_activated(false) + , m_reclaimFocus(false) +{} + +//----------------------------------------------------------------------------- +// Purpose: fade surface in and out smoothly +//----------------------------------------------------------------------------- +void CImguiSurface::Animate() +{ + const float deltaTime = ImGui::GetIO().DeltaTime; + const float animSpeed = m_activated + ? IMGUI_SURFACE_FADE_ANIM_SPEED + : -IMGUI_SURFACE_FADE_ANIM_SPEED; + + m_fadeAlpha += animSpeed * deltaTime; + m_fadeAlpha = ImClamp(m_fadeAlpha, 0.0f, 1.0f); + + // Reclaim focus the next time we activate the panel + if (!m_activated) + m_reclaimFocus = true; +} + +//----------------------------------------------------------------------------- +// Purpose: sets the surface front-end style and initial positions/sizes +//----------------------------------------------------------------------------- +void CImguiSurface::SetStyleVar(const float width, const float height, + const float x, const float y) +{ + m_surfaceStyle = g_ImGuiConfig.InitStyle(); + + ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_FirstUseEver); + ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_FirstUseEver); +} diff --git a/r5dev/gameui/imgui_surface.h b/r5dev/gameui/imgui_surface.h new file mode 100644 index 00000000..9220ba9e --- /dev/null +++ b/r5dev/gameui/imgui_surface.h @@ -0,0 +1,40 @@ +#ifndef IMGUI_SURFACE_H +#define IMGUI_SURFACE_H + +#define IMGUI_SURFACE_FADE_ANIM_SPEED 5.0f + +#include "imgui/misc/imgui_utility.h" + +class CImguiSurface +{ +public: + CImguiSurface(); + virtual ~CImguiSurface() { }; + + virtual bool Init() = 0; + virtual void Shutdown() = 0; + + virtual void Animate(); + virtual void RunFrame() = 0; + + virtual bool DrawSurface() = 0; + virtual void SetStyleVar(const float width, const float height, const float x, const float y); + + // inlines: + inline void ToggleActive() { m_activated ^= true; } + + inline bool IsActivated() { return m_activated; } + inline bool IsVisible() { return m_fadeAlpha > 0.0f; } + +protected: + const char* m_surfaceLabel; + + float m_fadeAlpha; + ImGuiStyle_t m_surfaceStyle; + + bool m_initialized; + bool m_activated; + bool m_reclaimFocus; +}; + +#endif // IMGUI_SURFACE_H diff --git a/r5dev/gameui/imgui_system.cpp b/r5dev/gameui/imgui_system.cpp new file mode 100644 index 00000000..4d9343b0 --- /dev/null +++ b/r5dev/gameui/imgui_system.cpp @@ -0,0 +1,177 @@ +//=============================================================================// +// +// Purpose: Dear ImGui engine implementation +// +//=============================================================================// + +#include "imgui/misc/imgui_snapshot.h" +#include "engine/sys_mainwind.h" +#include "windows/id3dx.h" + +#include "IBrowser.h" +#include "IConsole.h" + +#include "imgui_system.h" + +//----------------------------------------------------------------------------- +// Constructors/Destructors. +//----------------------------------------------------------------------------- +CImguiSystem::CImguiSystem() +{ + m_systemInitState = ImguiSystemInitStage_e::IM_PENDING_INIT; +} + +//----------------------------------------------------------------------------- +// Initializes the imgui system. If this fails, false would be returned and the +// implementation won't run. +//----------------------------------------------------------------------------- +bool CImguiSystem::Init() +{ + Assert(ThreadInMainThread(), "CImguiSystem::Init() should only be called from the main thread!"); + Assert(m_systemInitState == ImguiSystemInitStage_e::IM_PENDING_INIT, "CImguiSystem::Init() called recursively?"); + + /////////////////////////////////////////////////////////////////////////// + IMGUI_CHECKVERSION(); + ImGuiContext* const context = ImGui::CreateContext(); + + if (!context) + return false; + + AUTO_LOCK(m_snapshotBufferMutex); + AUTO_LOCK(m_inputEventQueueMutex); + + // This is required to disable the ctrl+tab menu as some users use this + // shortcut for other things in-game. See: https://github.com/ocornut/imgui/issues/4828 + context->ConfigNavWindowingKeyNext = 0; + context->ConfigNavWindowingKeyPrev = 0; + + ImGuiViewport* const vp = ImGui::GetMainViewport(); + vp->PlatformHandleRaw = g_pGame->GetWindow(); + + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_IsSRGB; + + if (!ImGui_ImplWin32_Init(g_pGame->GetWindow()) || + !ImGui_ImplDX11_Init(D3D11Device(), D3D11DeviceContext())) + { + Assert(0); + + m_systemInitState = ImguiSystemInitStage_e::IM_INIT_FAILURE; + return false; + } + + m_systemInitState = ImguiSystemInitStage_e::IM_SYSTEM_INIT; + return true; +} + +//----------------------------------------------------------------------------- +// Shuts the imgui system down, frees all allocated buffers. +//----------------------------------------------------------------------------- +void CImguiSystem::Shutdown() +{ + Assert(ThreadInMainThread(), "CImguiSystem::Shutdown() should only be called from the main thread!"); + Assert(m_systemInitState != ImguiSystemInitStage_e::IM_PENDING_INIT, "CImguiSystem::Shutdown() called recursively?"); + + // Nothing to shutdown. + if (m_systemInitState == ImguiSystemInitStage_e::IM_PENDING_INIT) + return; + + AUTO_LOCK(m_snapshotBufferMutex); + AUTO_LOCK(m_inputEventQueueMutex); + + m_systemInitState = ImguiSystemInitStage_e::IM_PENDING_INIT; + + ImGui_ImplDX11_Shutdown(); + ImGui_ImplWin32_Shutdown(); + + ImGui::DestroyContext(); + + m_snapshotData.Clear(); +} + +//----------------------------------------------------------------------------- +// Copies currently drawn data into the snapshot buffer which is queued to be +// rendered in the render thread. This should only be called from the same +// thread SampleFrame() is being called from. +//----------------------------------------------------------------------------- +void CImguiSystem::SwapBuffers() +{ + Assert(ThreadInMainThread(), "CImguiSystem::SwapBuffers() should only be called from the main thread!"); + + if (m_systemInitState < ImguiSystemInitStage_e::IM_FRAME_SAMPLED) + return; + + AUTO_LOCK(m_snapshotBufferMutex); + ImDrawData* const drawData = ImGui::GetDrawData(); + + // Nothing has been drawn, nothing to swap + if (!drawData) + return; + + m_snapshotData.SnapUsingSwap(drawData, ImGui::GetTime()); + + if (m_systemInitState == ImguiSystemInitStage_e::IM_FRAME_SAMPLED) + m_systemInitState = ImguiSystemInitStage_e::IM_FRAME_SWAPPED; +} + +//----------------------------------------------------------------------------- +// Draws the ImGui panels and applies all queued input events. +//----------------------------------------------------------------------------- +void CImguiSystem::SampleFrame() +{ + Assert(ThreadInMainThread(), "CImguiSystem::SampleFrame() should only be called from the main thread!"); + + if (m_systemInitState == ImguiSystemInitStage_e::IM_PENDING_INIT) + return; + + AUTO_LOCK(m_inputEventQueueMutex); + + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + + ImGui::NewFrame(); + + g_Browser.RunFrame(); + g_Console.RunFrame(); + + ImGui::EndFrame(); + ImGui::Render(); + + if (m_systemInitState == ImguiSystemInitStage_e::IM_SYSTEM_INIT) + m_systemInitState = ImguiSystemInitStage_e::IM_FRAME_SAMPLED; +} + +//----------------------------------------------------------------------------- +// Renders the drawn frame out which has been swapped to the snapshot buffer. +//----------------------------------------------------------------------------- +void CImguiSystem::RenderFrame() +{ + if (m_systemInitState < ImguiSystemInitStage_e::IM_FRAME_SWAPPED) + return; + + { + AUTO_LOCK(m_snapshotBufferMutex); + ImGui_ImplDX11_RenderDrawData(&m_snapshotData.DrawData); + } + + if (m_systemInitState == ImguiSystemInitStage_e::IM_FRAME_SAMPLED) + m_systemInitState = ImguiSystemInitStage_e::IM_FRAME_RENDERED; +} + +//----------------------------------------------------------------------------- +// Window procedure handler. +//----------------------------------------------------------------------------- +LRESULT CImguiSystem::MessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + AUTO_LOCK(ImguiSystem()->m_inputEventQueueMutex); + + extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wParam, lParam); +} + +static CImguiSystem s_imguiSystem; + +CImguiSystem* ImguiSystem() +{ + return &s_imguiSystem; +} diff --git a/r5dev/gameui/imgui_system.h b/r5dev/gameui/imgui_system.h new file mode 100644 index 00000000..aa8121fd --- /dev/null +++ b/r5dev/gameui/imgui_system.h @@ -0,0 +1,70 @@ +//=============================================================================// +// +// Purpose: Dear ImGui engine implementation +// +//=============================================================================// +#ifndef IMGUI_SYSTEM_H +#define IMGUI_SYSTEM_H +#include "imgui/misc/imgui_snapshot.h" + +class CImguiSystem +{ +public: + CImguiSystem(); + + bool Init(); + void Shutdown(); + + void SwapBuffers(); + + void SampleFrame(); + void RenderFrame(); + + // statics: + static LRESULT MessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + + // inlines: + inline bool IsInitialized() const + { + return m_systemInitState >= ImguiSystemInitStage_e::IM_SYSTEM_INIT; + }; + +private: + enum class ImguiSystemInitStage_e + { + // When the system failed to initialize, the stage would be set to + // this. + IM_INIT_FAILURE = -1, + + IM_PENDING_INIT, + IM_SYSTEM_INIT, + + // State gets set to this when the first frame has been sampled. + IM_FRAME_SAMPLED, + + // State gets set to this then buffers have been swapped for the first + // time. + IM_FRAME_SWAPPED, + + // Rendered for the first time. + IM_FRAME_RENDERED + }; + + ImguiSystemInitStage_e m_systemInitState; + ImDrawDataSnapshot m_snapshotData; + + // Mutex used during swapping and rendering, we draw the windows in the + // main thread, and render it in the render thread. The only place this + // mutex is used is during snapshot swapping and during rendering + mutable CThreadFastMutex m_snapshotBufferMutex; + + // Mutex used between ImGui window procedure handling and drawing, see + // https://github.com/ocornut/imgui/issues/6895. In this engine the window + // is ran in thread separate from the main thread, therefore it needs a + // lock to control access as main calls SampleFrame(). + mutable CThreadFastMutex m_inputEventQueueMutex; +}; + +CImguiSystem* ImguiSystem(); + +#endif // IMGUI_SYSTEM_H diff --git a/r5dev/gameui/CMakeLists.txt b/r5dev/geforce/CMakeLists.txt similarity index 52% rename from r5dev/gameui/CMakeLists.txt rename to r5dev/geforce/CMakeLists.txt index 6b282605..264e90c3 100644 --- a/r5dev/gameui/CMakeLists.txt +++ b/r5dev/geforce/CMakeLists.txt @@ -1,13 +1,11 @@ cmake_minimum_required( VERSION 3.16 ) -add_module( "lib" "gameui" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) +add_module( "lib" "GFSDK" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) start_sources() -add_sources( SOURCE_GROUP "Core" - "IBrowser.cpp" - "IBrowser.h" - "IConsole.cpp" - "IConsole.h" +add_sources( SOURCE_GROUP "Runtime" + "reflex.cpp" + "reflex.h" ) end_sources() diff --git a/r5dev/geforce/reflex.cpp b/r5dev/geforce/reflex.cpp new file mode 100644 index 00000000..0c7b88cb --- /dev/null +++ b/r5dev/geforce/reflex.cpp @@ -0,0 +1,138 @@ +//===========================================================================// +// +// Purpose: NVIDIA Reflex utilities +// +//===========================================================================// +#include "reflex.h" +#include "mathlib/mathlib.h" +#include "materialsystem/cmaterialsystem.h" + +static bool s_LowLatencySDKEnabled = false; + +// If false, the system will call 'NvAPI_D3D_SetSleepMode' to update the parameters. +static bool s_ReflexModeInfoUpToDate = false; + +// This is 'NVAPI_OK' If the call to 'NvAPI_D3D_SetSleepMode' was successful. +// If not, the Low Latency SDK will not run. +static NvAPI_Status s_ReflexModeUpdateStatus = NvAPI_Status::NVAPI_OK; + +//----------------------------------------------------------------------------- +// Purpose: enable/disable low latency SDK +// Input : enable - +//----------------------------------------------------------------------------- +void GFX_EnableLowLatencySDK(const bool enable) +{ + s_LowLatencySDKEnabled = enable; +} + +//----------------------------------------------------------------------------- +// Purpose: whether we should run the low latency SDK +//----------------------------------------------------------------------------- +bool GFX_IsLowLatencySDKEnabled(void) +{ + if (!s_LowLatencySDKEnabled) + return false; + + const MaterialAdapterInfo_t& adapterInfo = g_pMaterialAdapterMgr->GetAdapterInfo(); + // Only run on NVIDIA display drivers; AMD and Intel are not + // supported by NVIDIA Reflex. + return adapterInfo.m_VendorID == NVIDIA_VENDOR_ID; +} + +//----------------------------------------------------------------------------- +// Purpose: mark the parameters as out-of-date; force update next frame +//----------------------------------------------------------------------------- +void GFX_MarkLowLatencyParametersOutOfDate(void) +{ + s_ReflexModeInfoUpToDate = false; +} + +//----------------------------------------------------------------------------- +// Purpose: mark the parameters as up-to-date +//----------------------------------------------------------------------------- +void GFX_MarkLowLatencyParametersUpToDate(void) +{ + s_ReflexModeInfoUpToDate = true; +} + +//----------------------------------------------------------------------------- +// Purpose: has the user requested any changes to the low latency parameters? +//----------------------------------------------------------------------------- +bool GFX_HasPendingLowLatencyParameterUpdates(void) +{ + return s_ReflexModeInfoUpToDate == false; +} + +//----------------------------------------------------------------------------- +// Purpose: returns whether the call to 'NvAPI_D3D_SetSleepMode' was successful +//----------------------------------------------------------------------------- +bool GFX_ParameterUpdateWasSuccessful(void) +{ + return s_ReflexModeUpdateStatus == NvAPI_Status::NVAPI_OK; +} + +//----------------------------------------------------------------------------- +// Purpose: updates the low latency parameters +// Input : *device - +// useLowLatencyMode - +// useLowLatencyBoost - +// useMarkersToOptimize - +// maxFramesPerSecond - +//----------------------------------------------------------------------------- +void GFX_UpdateLowLatencyParameters(IUnknown* const device, const bool useLowLatencyMode, + const bool useLowLatencyBoost, const bool useMarkersToOptimize, + const float maxFramesPerSecond) +{ + Assert(device); + Assert(IsFinite(maxFramesPerSecond)); + + NV_SET_SLEEP_MODE_PARAMS params = {}; + params.version = NV_SET_SLEEP_MODE_PARAMS_VER1; + + params.bLowLatencyMode = useLowLatencyMode; + params.bLowLatencyBoost = useLowLatencyMode && useLowLatencyBoost; + params.minimumIntervalUs = maxFramesPerSecond > 0 + ? (NvU32)((1000.0f / maxFramesPerSecond) * 1000.0f) + : 0; + params.bUseMarkersToOptimize = useMarkersToOptimize; + + s_ReflexModeUpdateStatus = NvAPI_D3D_SetSleepMode(device, ¶ms); + GFX_MarkLowLatencyParametersUpToDate(); +} + +//----------------------------------------------------------------------------- +// Purpose: runs a frame of the low latency sdk +// Input : *device - +//----------------------------------------------------------------------------- +void GFX_RunLowLatencyFrame(IUnknown* const device) +{ + Assert(device); + + if (GFX_ParameterUpdateWasSuccessful()) + NvAPI_D3D_Sleep(device); +} + +//----------------------------------------------------------------------------- +// Purpose: sets the latency marker +// Input : *device - +// frameNumber - +// markerType - +//----------------------------------------------------------------------------- +void GFX_SetLatencyMarker(IUnknown* const device, + const NV_LATENCY_MARKER_TYPE markerType, const NvU64 frameID) +{ + Assert(device); + + if (GFX_ParameterUpdateWasSuccessful() && GFX_IsLowLatencySDKEnabled()) + { + NV_LATENCY_MARKER_PARAMS params = {}; + params.version = NV_LATENCY_MARKER_PARAMS_VER1; + params.frameID = frameID; + params.markerType = markerType; + + NvAPI_D3D_SetLatencyMarker(device, ¶ms); + } + + // PCLStats runs separately and is supported on non-NVIDIA hardware. + PCLSTATS_MARKER(markerType, frameID); +} diff --git a/r5dev/geforce/reflex.h b/r5dev/geforce/reflex.h new file mode 100644 index 00000000..acef73b4 --- /dev/null +++ b/r5dev/geforce/reflex.h @@ -0,0 +1,19 @@ +#ifndef GFSDK_REFLEX_H +#define GFSDK_REFLEX_H + +void GFX_EnableLowLatencySDK(const bool enable); +bool GFX_IsLowLatencySDKEnabled(void); + +void GFX_MarkLowLatencyParametersOutOfDate(void); +bool GFX_HasPendingLowLatencyParameterUpdates(void); + +void GFX_UpdateLowLatencyParameters(IUnknown* device, const bool useLowLatencyMode, + const bool useLowLatencyBoost, const bool useMarkersToOptimize, + const float maxFramesPerSecond); + +void GFX_RunLowLatencyFrame(IUnknown* device); + +void GFX_SetLatencyMarker(IUnknown* device, + const NV_LATENCY_MARKER_TYPE markerType, const NvU64 frameID); + +#endif // GFSDK_REFLEX_H diff --git a/r5dev/inputsystem/CMakeLists.txt b/r5dev/inputsystem/CMakeLists.txt index c22fca6f..5d6ea714 100644 --- a/r5dev/inputsystem/CMakeLists.txt +++ b/r5dev/inputsystem/CMakeLists.txt @@ -9,7 +9,10 @@ add_sources( SOURCE_GROUP "Private" ) add_sources( SOURCE_GROUP "Public" + "${ENGINE_SOURCE_DIR}/public/inputsystem/iinputsystem.h" + "${ENGINE_SOURCE_DIR}/public/inputsystem/AnalogCode.h" "${ENGINE_SOURCE_DIR}/public/inputsystem/ButtonCode.h" + "${ENGINE_SOURCE_DIR}/public/inputsystem/InputEnums.h" ) end_sources() diff --git a/r5dev/inputsystem/inputsystem.cpp b/r5dev/inputsystem/inputsystem.cpp index d78ed76c..a49bcf4a 100644 --- a/r5dev/inputsystem/inputsystem.cpp +++ b/r5dev/inputsystem/inputsystem.cpp @@ -6,48 +6,73 @@ #include "core/stdafx.h" #include "vpc/IAppSystem.h" +#include "windows/id3dx.h" +#include "geforce/reflex.h" #include "inputsystem/inputsystem.h" +#include -//----------------------------------------------------------------------------- -// Enables/disables input -//----------------------------------------------------------------------------- -void CInputSystem::EnableInput(bool bEnabled) +////----------------------------------------------------------------------------- +//// Enables/disables input +////----------------------------------------------------------------------------- +//void CInputSystem::EnableInput(bool bEnabled) +//{ +// const static int index = 10; +// CallVFunc(index, this, bEnabled); +//} +// +////----------------------------------------------------------------------------- +//// Enables/disables the inputsystem windows message pump +////----------------------------------------------------------------------------- +//void CInputSystem::EnableMessagePump(bool bEnabled) +//{ +// const static int index = 11; +// CallVFunc(index, this, bEnabled); +//} +// +////----------------------------------------------------------------------------- +//// Poll current state +////----------------------------------------------------------------------------- +//bool CInputSystem::IsButtonDown(ButtonCode_t Button) +//{ +// const static int index = 13; +// return CallVFunc(index, this, Button); +//} +// +////----------------------------------------------------------------------------- +//// Convert back + forth between ButtonCode/AnalogCode + strings +////----------------------------------------------------------------------------- +//bool CInputSystem::ButtonCodeToString(ButtonCode_t Button) +//{ +// const static int index = 25; +// return CallVFunc(index, this, Button); +//} +//ButtonCode_t CInputSystem::StringToButtonCode(const char* pString) +//{ +// const static int index = 26; +// return CallVFunc(index, this, pString); +//} + +LRESULT CInputSystem::WindowProc(void* unused, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - const static int index = 10; - CallVFunc(index, this, bEnabled); + if (g_pInputSystem->m_bEnabled && + ((hwnd == g_pInputSystem->m_hAttachedHWnd) || (uMsg == WM_ACTIVATEAPP)) && + (uMsg != WM_CLOSE)) + { + if (PCLSTATS_IS_PING_MSG_ID(uMsg)) + { + GFX_SetLatencyMarker(D3D11Device(), PC_LATENCY_PING, MaterialSystem()->GetCurrentFrameCount()); + } + } + + return CInputSystem__WindowProc(unused, hwnd, uMsg, wParam, lParam); } -//----------------------------------------------------------------------------- -// Enables/disables the inputsystem windows message pump -//----------------------------------------------------------------------------- -void CInputSystem::EnableMessagePump(bool bEnabled) -{ - const static int index = 11; - CallVFunc(index, this, bEnabled); -} -//----------------------------------------------------------------------------- -// Poll current state -//----------------------------------------------------------------------------- -bool CInputSystem::IsButtonDown(ButtonCode_t Button) +void VInputSystem::Detour(const bool bAttach) const { - const static int index = 13; - return CallVFunc(index, this, Button); -} - -//----------------------------------------------------------------------------- -// Convert back + forth between ButtonCode/AnalogCode + strings -//----------------------------------------------------------------------------- -bool CInputSystem::ButtonCodeToString(ButtonCode_t Button) -{ - const static int index = 25; - return CallVFunc(index, this, Button); -} -ButtonCode_t CInputSystem::StringToButtonCode(const char* pString) -{ - const static int index = 26; - return CallVFunc(index, this, pString); + DetourSetup(&CInputSystem__WindowProc, &CInputSystem::WindowProc, bAttach); } /////////////////////////////////////////////////////////////////////////////// -CInputSystem* g_pInputSystem = nullptr; \ No newline at end of file +CInputSystem* g_pInputSystem = nullptr; +bool(**g_fnSyncRTWithIn)(void) = nullptr; diff --git a/r5dev/inputsystem/inputsystem.h b/r5dev/inputsystem/inputsystem.h index 60833501..e4697301 100644 --- a/r5dev/inputsystem/inputsystem.h +++ b/r5dev/inputsystem/inputsystem.h @@ -1,41 +1,197 @@ #pragma once -#include "core/stdafx.h" -#include "inputsystem/ButtonCode.h" +#include "inputsystem/iinputsystem.h" +#include "mathlib/bitvec.h" +#include "tier1/utlstringmap.h" +#include -class CInputSystem +//----------------------------------------------------------------------------- +// Implementation of the input system +//----------------------------------------------------------------------------- +class CInputSystem : public CTier1AppSystem< IInputSystem > { public: - void EnableInput(bool bEnabled); // @0x14039F100 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - void EnableMessagePump(bool bEnabled); // @0x14039F110 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - bool IsButtonDown(ButtonCode_t Button); // @0x1403A0140 in R5pc_r5launch_N1094_CL456479_2019_10_30_05_20_PM - bool ButtonCodeToString(ButtonCode_t Button); - ButtonCode_t StringToButtonCode(const char* pString); + // !!!interface implemented in engine!!! +public: + // Hook statics: + static LRESULT WindowProc(void* unused, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); private: - char pad_0000[16]; //0x0000 -public: - bool m_bEnabled; //0x0010 IsInputEnabled variable. - bool m_bPumpEnabled; //0x0011 EnabledMessagePump variable. + enum + { + INPUT_STATE_QUEUED = 0, + INPUT_STATE_CURRENT, + + INPUT_STATE_COUNT, + + BUTTON_EVENT_COUNT = 128 + }; + + struct xdevice_t + { + struct xvibration_t + { + float leftMainMotor; + float rightMainMotor; + float leftTriggerMotor; + float rightTriggerMotor; + }; + + struct unkownhiddevice_t + { + struct state_t + { + SRWLOCK lock; + char unk0[56]; + xvibration_t vibration; + char unk1[48]; + }; + + // Name might be incorrect! + state_t states[INPUT_STATE_COUNT]; + HANDLE hThread0; + HANDLE hthread1; + }; + + int userId; + char active; + XINPUT_STATE states[INPUT_STATE_COUNT]; + int newState; + xKey_t lastStickKeys[MAX_JOYSTICK_AXES-2]; // -2 as U and V aren't polled. + int unk0; + bool pendingRumbleUpdate; + _BYTE gap41[3]; + xvibration_t vibration; + bool isXbox360Gamepad; + bool nonXboxDevice; // uses unknownHidDevice when set + _BYTE gap56[42]; + unkownhiddevice_t unknownHidDevice; + _BYTE gap190[42]; + }; + static_assert(sizeof(xdevice_t) == 0x1C0); + + struct appKey_t + { + int repeats; + int sample; + }; + + struct InputState_t + { + // Analog states + CBitVec m_ButtonState; + int m_pAnalogValue[ANALOG_CODE_LAST]; + }; + + + HWND m_ChainedWndProc; + HWND m_hAttachedHWnd; + bool m_bEnabled; + bool m_bPumpEnabled; + bool m_bIsPolling; + bool m_bIMEComposing; + bool m_bMouseCursorVisible; + bool m_bJoystickCursorVisible; + bool m_bIsInGame; // Delay joystick polling if in-game. + + // Current button state + InputState_t m_InputState[INPUT_STATE_COUNT]; + + // Current button state mutex + CRITICAL_SECTION m_InputStateMutex; + int unknown0; + short unknown1; + bool unknown2; + + // Analog event mutex + CRITICAL_SECTION m_AnalogEventMutex; + int unknown3; + short unknown4; + bool unknown5; + + // Analog events + InputEvent_t m_AnalogEvents[JOYSTICK_AXIS_BUTTON_COUNT]; + int m_AnalogEventTypes[JOYSTICK_AXIS_BUTTON_COUNT]; + + // Button events + InputEvent_t m_Events[BUTTON_EVENT_COUNT]; + + // Current event + InputEvent_t m_CurrentEvent; + + DWORD m_StartupTimeTick; + int m_nLastPollTick; + int m_nLastSampleTick; + int m_nLastAnalogPollTick; + int m_nLastAnalogSampleTick; + + // Mouse wheel hack + UINT m_uiMouseWheel; + + // Xbox controller info + int m_nJoystickCount; + appKey_t m_appXKeys[XUSER_MAX_COUNT][XK_MAX_KEYS]; + char pad_unk[16]; + xdevice_t m_XDevices[XUSER_MAX_COUNT]; + + // Used to determine whether to generate UI events + int m_nUIEventClientCount; + + // Raw mouse input + bool m_bRawInputSupported; + CRITICAL_SECTION m_MouseAccumMutex; + int m_mouseRawAccumX; + int m_mouseRawAccumY; + + _BYTE gap1785[8]; + + // Current mouse capture window + PlatWindow_t m_hCurrentCaptureWnd; + + // For the 'SleepUntilInput' feature + HANDLE m_hEvent; + + InputCursorHandle_t m_pDefaultCursors[INPUT_CURSOR_COUNT]; + CUtlStringMap m_UserCursors; + + CSysModule* m_pXInputDLL; + CSysModule* m_pRawInputDLL; + + // NVNT falcon module + CSysModule* m_pNovintDLL; // Unused in R5? + + bool m_bIgnoreLocalJoystick; + InputCursorHandle_t m_hCursor; }; +static_assert(sizeof(CInputSystem) == 0x18E8); /////////////////////////////////////////////////////////////////////////////// +inline LRESULT (*CInputSystem__WindowProc)(void* thisptr, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + extern CInputSystem* g_pInputSystem; +extern bool(**g_fnSyncRTWithIn)(void); // Belongs to an array of functions, see CMaterialSystem::MatsysMode_Init(). /////////////////////////////////////////////////////////////////////////////// class VInputSystem : public IDetour { virtual void GetAdr(void) const { - LogVarAdr("g_pInputSystem", reinterpret_cast(g_pInputSystem)); + LogFunAdr("CInputSystem::WindowProc", CInputSystem__WindowProc); + LogVarAdr("g_pInputSystem", g_pInputSystem); + LogVarAdr("g_fnSyncRTWithIn", g_fnSyncRTWithIn); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 55 56 41 54 41 55 48 83 EC 48").GetPtr(CInputSystem__WindowProc); } - virtual void GetFun(void) const { } virtual void GetVar(void) const { g_pInputSystem = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 05 ?? ?? ?? ?? 48 85 C9 74 11") .FindPatternSelf("48 89 05", CMemory::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + + const CMemory l_EngineApi_PumpMessages = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 81 EC ?? ?? ?? ?? 45 33 C9"); + g_fnSyncRTWithIn = l_EngineApi_PumpMessages.FindPattern("74 06").FindPatternSelf("FF 15").ResolveRelativeAddressSelf(2, 6).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/launcher/launcher.cpp b/r5dev/launcher/launcher.cpp index 0aa38bfc..4f7763dd 100644 --- a/r5dev/launcher/launcher.cpp +++ b/r5dev/launcher/launcher.cpp @@ -6,26 +6,13 @@ //===========================================================================// #include "core/stdafx.h" #include "core/logdef.h" +#include "core/init.h" #include "tier0/crashhandler.h" #include "tier0/commandline.h" #include "tier1/strtools.h" #include "launcher/launcher.h" #include -int LauncherMain(HINSTANCE hInstance) -{ - // Flush buffers every 5 seconds for every logger. - // Has to be done here, don't move this to SpdLog - // init, as this could cause deadlocks on certain - // compilers (VS2017)!!! - spdlog::flush_every(std::chrono::seconds(5)); - - int results = v_LauncherMain(hInstance); - DevMsg(eDLL_T::NONE, "%s returned: %s\n", __FUNCTION__, ExitCodeToString(results)); - return results; -} - -#if !defined (GAMEDLL_S0) || !defined (GAMEDLL_S1) // Remove all but the last -game parameter. // This is for mods based off something other than Half-Life 2 (like HL2MP mods). // The Steam UI does 'steam -applaunch 320 -game c:\steam\steamapps\sourcemods\modname', but applaunch inserts @@ -52,26 +39,12 @@ void RemoveSpuriousGameParameters() CommandLine()->AppendParm("-game", lastGameArg); } } -#endif - -const char* ExitCodeToString(int nCode) -{ - switch (nCode) - { - case EXIT_SUCCESS: - return "EXIT_SUCCESS"; - case EXIT_FAILURE: - return "EXIT_FAILURE"; - default: - return "UNKNOWN_EXIT_CODE"; - } -} LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) { // Don't run the unhandled exception filter from the // game if we have a valid vectored exception filter. - if (g_CrashHandler) + if (g_CrashHandler.IsValid()) { return NULL; } @@ -79,19 +52,8 @@ LONG WINAPI TopLevelExceptionFilter(EXCEPTION_POINTERS* pExceptionPointers) return v_TopLevelExceptionFilter(pExceptionPointers); } -void VLauncher::Attach(void) const +void VLauncher::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&v_LauncherMain, &LauncherMain); - DetourAttach((LPVOID*)&v_TopLevelExceptionFilter, &TopLevelExceptionFilter); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) - DetourAttach((LPVOID*)&v_RemoveSpuriousGameParameters, &RemoveSpuriousGameParameters); -#endif + DetourSetup(&v_TopLevelExceptionFilter, &TopLevelExceptionFilter, bAttach); + DetourSetup(&v_RemoveSpuriousGameParameters, &RemoveSpuriousGameParameters, bAttach); } -void VLauncher::Detach(void) const -{ - DetourDetach((LPVOID*)&v_LauncherMain, &LauncherMain); - DetourDetach((LPVOID*)&v_TopLevelExceptionFilter, &TopLevelExceptionFilter); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) - DetourDetach((LPVOID*)&v_RemoveSpuriousGameParameters, &RemoveSpuriousGameParameters); -#endif -} \ No newline at end of file diff --git a/r5dev/launcher/launcher.h b/r5dev/launcher/launcher.h index 5037b532..c398e89d 100644 --- a/r5dev/launcher/launcher.h +++ b/r5dev/launcher/launcher.h @@ -1,47 +1,31 @@ #ifndef LAUNCHER_H #define LAUNCHER_H -inline CMemory p_LauncherMain; +// NOTE: cannot hook this! this function is hooked by loader.dll and is also +// used to gracefully shutdown the SDK. inline int(*v_LauncherMain)(HINSTANCE hInstance); -inline CMemory p_TopLevelExceptionFilter; inline LONG(*v_TopLevelExceptionFilter)(EXCEPTION_POINTERS* pExceptionPointer); - -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) -inline CMemory p_RemoveSpuriousGameParameters; -inline void*(*v_RemoveSpuriousGameParameters)(void); -#endif // !GAMEDLL_S0 || !GAMEDLL_S1 - -const char* ExitCodeToString(int nCode); +inline void(*v_RemoveSpuriousGameParameters)(void); /////////////////////////////////////////////////////////////////////////////// class VLauncher : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("LauncherMain", p_LauncherMain.GetPtr()); - LogFunAdr("TopLevelExceptionFilter", p_TopLevelExceptionFilter.GetPtr()); -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) - LogFunAdr("RemoveSpuriousGameParameters", p_RemoveSpuriousGameParameters.GetPtr()); -#endif // !GAMEDLL_S0 || !GAMEDLL_S1 + LogFunAdr("LauncherMain", v_LauncherMain); + LogFunAdr("TopLevelExceptionFilter", v_TopLevelExceptionFilter); + LogFunAdr("RemoveSpuriousGameParameters", v_RemoveSpuriousGameParameters); } virtual void GetFun(void) const { - p_LauncherMain = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B C8 E8 ?? ?? ?? ?? CC").FollowNearCallSelf(); - v_LauncherMain = p_LauncherMain.RCast(); - - p_TopLevelExceptionFilter = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B 05 ?? ?? ?? ?? 48 8B D9 48 85 C0 74 06"); - v_TopLevelExceptionFilter = p_TopLevelExceptionFilter.RCast(); - -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) - p_RemoveSpuriousGameParameters = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 33 ED 48 8D 3D ?? ?? ?? ??"); - v_RemoveSpuriousGameParameters = p_RemoveSpuriousGameParameters.RCast(); -#endif // !GAMEDLL_S0 || !GAMEDLL_S1 + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B C8 E8 ?? ?? ?? ?? CC").FollowNearCallSelf().GetPtr(v_LauncherMain); + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B 05 ?? ?? ?? ?? 48 8B D9 48 85 C0 74 06").GetPtr(v_TopLevelExceptionFilter); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 33 ED 48 8D 3D ?? ?? ?? ??").GetPtr(v_RemoveSpuriousGameParameters); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/launcher/prx.cpp b/r5dev/launcher/prx.cpp index 1706020c..e8e42d2d 100644 --- a/r5dev/launcher/prx.cpp +++ b/r5dev/launcher/prx.cpp @@ -13,12 +13,7 @@ void h_exit_or_terminate_process(UINT uExitCode) TerminateProcess(h, uExitCode); } -void VPRX::Attach() const +void VPRX::Detour(const bool bAttach) const { - //DetourAttach(&v_exit_or_terminate_process, &h_exit_or_terminate_process); + //DetourSetup(&v_exit_or_terminate_process, &h_exit_or_terminate_process, bAttach); } - -void VPRX::Detach() const -{ - //DetourDetach(&v_exit_or_terminate_process, &h_exit_or_terminate_process); -} \ No newline at end of file diff --git a/r5dev/launcher/prx.h b/r5dev/launcher/prx.h index 3786691e..24918690 100644 --- a/r5dev/launcher/prx.h +++ b/r5dev/launcher/prx.h @@ -1,7 +1,6 @@ #pragma once /* ==== PRX ============================================================================================================================================================= */ -inline CMemory p_exit_or_terminate_process; inline void(*v_exit_or_terminate_process)(UINT uExitCode); /////////////////////////////////////////////////////////////////////////////// @@ -9,16 +8,14 @@ class VPRX : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("exit_or_terminate_process", p_exit_or_terminate_process.GetPtr()); + LogFunAdr("exit_or_terminate_process", v_exit_or_terminate_process); } virtual void GetFun(void) const { - p_exit_or_terminate_process = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 8B D9 E8 ?? ?? ?? ?? 84 C0"); - v_exit_or_terminate_process = p_exit_or_terminate_process.RCast(); /*40 53 48 83 EC 20 8B D9 E8 ? ? ? ? 84 C0 */ + g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 8B D9 E8 ?? ?? ?? ?? 84 C0").GetPtr(v_exit_or_terminate_process); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/loader/CMakeLists.txt b/r5dev/loader/CMakeLists.txt index dab69fab..b1b95fcf 100644 --- a/r5dev/loader/CMakeLists.txt +++ b/r5dev/loader/CMakeLists.txt @@ -19,6 +19,7 @@ target_link_libraries( ${PROJECT_NAME} PRIVATE "vpc" "tier0" + "tier1" "libdetours" ) diff --git a/r5dev/loader/loader.cpp b/r5dev/loader/loader.cpp index b24b6a26..39bc1beb 100644 --- a/r5dev/loader/loader.cpp +++ b/r5dev/loader/loader.cpp @@ -39,10 +39,14 @@ static const IMAGE_DOS_HEADER* s_DosHeader = nullptr; static const IMAGE_NT_HEADERS64* s_NtHeaders = nullptr; static HMODULE s_SdkModule = NULL; +typedef void (*InitFunc)(void); +static InitFunc s_SdkInitFunc = NULL; +static InitFunc s_SdkShutdownFunc = NULL; + //----------------------------------------------------------------------------- -// WinMain function pointer +// LauncherMain function pointer //----------------------------------------------------------------------------- -static int (*v_WinMain)(HINSTANCE, HINSTANCE, LPSTR, int) = nullptr; +static int (*v_LauncherMain)(HINSTANCE, HINSTANCE, LPSTR, int) = nullptr; //----------------------------------------------------------------------------- // Purpose: Terminates the process with an error when called @@ -100,16 +104,49 @@ static void InitGameSDK(const LPSTR lpCmdLine) { Assert(0); FatalError("Failed to load SDK: error code = %08x\n", GetLastError()); + + return; + } + + s_SdkInitFunc = (InitFunc)GetProcAddress(s_SdkModule, "SDK_Init"); + if (s_SdkInitFunc) + s_SdkShutdownFunc = (InitFunc)GetProcAddress(s_SdkModule, "SDK_Shutdown"); + + if (!s_SdkInitFunc || !s_SdkShutdownFunc) + { + Assert(0); + FatalError("Loaded SDK is invalid: error code = %08x\n", GetLastError()); + + return; + } + + s_SdkInitFunc(); +} + +//----------------------------------------------------------------------------- +// Purpose: Unloads the SDK module +//----------------------------------------------------------------------------- +static void ShutdownGameSDK() +{ + if (s_SdkModule) + { + s_SdkShutdownFunc(); + + FreeLibrary(s_SdkModule); + s_SdkModule = NULL; } } //----------------------------------------------------------------------------- -// Purpose: WinMain hook; loads the SDK before LauncherMain +// Purpose: LauncherMain hook; loads the SDK before the game inits //----------------------------------------------------------------------------- -int WINAPI hWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +int WINAPI hLauncherMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { InitGameSDK(lpCmdLine); // Init GameSDK, internal function calls LauncherMain. - return v_WinMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd); + const int ret = v_LauncherMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd); + ShutdownGameSDK(); + + return ret; } //----------------------------------------------------------------------------- @@ -120,7 +157,7 @@ static void AttachEP() DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); - DetourAttach(&v_WinMain, &hWinMain); + DetourAttach(&v_LauncherMain, &hLauncherMain); HRESULT hr = DetourTransactionCommit(); if (hr != NO_ERROR) // Failed to hook into the process, terminate... @@ -138,7 +175,7 @@ static void DetachEP() DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); - DetourDetach(&v_WinMain, &hWinMain); + DetourDetach(&v_LauncherMain, &hLauncherMain); HRESULT hr = DetourTransactionCommit(); Assert(hr != NO_ERROR); @@ -159,7 +196,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) s_NtHeaders = (IMAGE_NT_HEADERS64*)((uintptr_t)s_DosHeader + (uintptr_t)s_DosHeader->e_lfanew); - v_WinMain = CModule::GetExportedSymbol((QWORD)s_DosHeader, "WinMain") + v_LauncherMain = CModule::GetExportedSymbol((QWORD)s_DosHeader, "LauncherMain") .RCast(); AttachEP(); @@ -168,9 +205,6 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) case DLL_PROCESS_DETACH: { - if (s_SdkModule) - FreeLibrary(s_SdkModule); - DetachEP(); break; } diff --git a/r5dev/localize/CMakeLists.txt b/r5dev/localize/CMakeLists.txt index 8f1a26a3..7035532c 100644 --- a/r5dev/localize/CMakeLists.txt +++ b/r5dev/localize/CMakeLists.txt @@ -8,4 +8,8 @@ add_sources( SOURCE_GROUP "Runtime" "localize.h" ) +add_sources( SOURCE_GROUP "Public" + "${ENGINE_SOURCE_DIR}/public/localize/ilocalize.h" +) + end_sources() diff --git a/r5dev/localize/localize.cpp b/r5dev/localize/localize.cpp index f1823b12..cae020eb 100644 --- a/r5dev/localize/localize.cpp +++ b/r5dev/localize/localize.cpp @@ -5,10 +5,10 @@ bool Localize_LoadLocalizationFileLists(CLocalize* thisptr) { - v_CLocalize__LoadLocalizationFileLists(thisptr); + CLocalize__LoadLocalizationFileLists(thisptr); const CUtlVector& - modList = g_pModSystem->GetModList(); + modList = ModSystem()->GetModList(); FOR_EACH_VEC(modList, i) { @@ -22,7 +22,7 @@ bool Localize_LoadLocalizationFileLists(CLocalize* thisptr) { const char* localizationFile = mod->m_LocalizationFiles.Element(j).Get(); - if (!v_CLocalize__AddFile(thisptr, localizationFile, "PLATFORM")) + if (!CLocalize__AddFile(thisptr, localizationFile, "PLATFORM")) Warning(eDLL_T::ENGINE, "Failed to add localization file '%s'\n", localizationFile); } } @@ -30,12 +30,18 @@ bool Localize_LoadLocalizationFileLists(CLocalize* thisptr) return true; } -void VLocalize::Attach() const +bool Localize_IsLanguageSupported(const char* pLocaleName) { - DetourAttach((LPVOID*)&v_CLocalize__LoadLocalizationFileLists, &Localize_LoadLocalizationFileLists); + for (int i = 0; i < SDK_ARRAYSIZE(g_LanguageNames); ++i) + { + if (strcmp(pLocaleName, g_LanguageNames[i]) == NULL) + return true; + } + + return false; } -void VLocalize::Detach() const +void VLocalize::Detour(const bool bAttach) const { - DetourDetach((LPVOID*)&v_CLocalize__LoadLocalizationFileLists, &Localize_LoadLocalizationFileLists); -} \ No newline at end of file + DetourSetup(&CLocalize__LoadLocalizationFileLists, &Localize_LoadLocalizationFileLists, bAttach); +} diff --git a/r5dev/localize/localize.h b/r5dev/localize/localize.h index be9ca839..b414c68e 100644 --- a/r5dev/localize/localize.h +++ b/r5dev/localize/localize.h @@ -1,43 +1,44 @@ #pragma once +#include "localize/ilocalize.h" -class CLocalize +bool Localize_IsLanguageSupported(const char* pLocaleName); + +class CLocalize : public CBaseAppSystem< ILocalize > { - // todo + char unk[400]; + unsigned __int16 m_CurrentFile; + char unk_19A; + bool m_bUseOnlyLongestLanguageString; + bool m_bSuppressChangeCallbacks; + bool m_bQueuedChangeCallback; }; -inline CMemory p_CLocalize__AddFile; -inline bool(*v_CLocalize__AddFile)(CLocalize * thisptr, const char* szFileName, const char* pPathID); +inline bool(*CLocalize__AddFile)(CLocalize * thisptr, const char* szFileName, const char* pPathID); +inline bool(*CLocalize__LoadLocalizationFileLists)(CLocalize * thisptr); -inline CMemory p_CLocalize__LoadLocalizationFileLists; -inline bool(*v_CLocalize__LoadLocalizationFileLists)(CLocalize * thisptr); - - -inline CLocalize* g_pVGuiLocalize; -inline CLocalize* g_pLocalize; +inline CLocalize** g_ppVGuiLocalize; +inline CLocalize** g_ppLocalize; /////////////////////////////////////////////////////////////////////////////// class VLocalize : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CLocalize::AddFile", p_CLocalize__AddFile.GetPtr()); - LogFunAdr("CLocalize::LoadLocalizationFileLists", p_CLocalize__LoadLocalizationFileLists.GetPtr()); + LogFunAdr("CLocalize::AddFile", CLocalize__AddFile); + LogFunAdr("CLocalize::LoadLocalizationFileLists", CLocalize__LoadLocalizationFileLists); + LogFunAdr("g_Localize", g_ppLocalize); } virtual void GetFun(void) const { - p_CLocalize__AddFile = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 49 FF C4").FollowNearCallSelf(); - v_CLocalize__AddFile = p_CLocalize__AddFile.RCast(); - - p_CLocalize__LoadLocalizationFileLists = g_GameDll.FindPatternSIMD("4C 8B DC 53 48 81 EC ?? ?? ?? ?? 33 C0"); - v_CLocalize__LoadLocalizationFileLists = p_CLocalize__LoadLocalizationFileLists.RCast(); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 49 FF C4").FollowNearCallSelf().GetPtr(CLocalize__AddFile); + g_GameDll.FindPatternSIMD("4C 8B DC 53 48 81 EC ?? ?? ?? ?? 33 C0").GetPtr(CLocalize__LoadLocalizationFileLists); } virtual void GetVar(void) const { - g_pVGuiLocalize = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 50 40 40 38 2D ?? ?? ?? ??").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pLocalize = g_pVGuiLocalize; // these are set to the same thing in CSourceAppSystemGroup::Create + g_ppVGuiLocalize = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 50 40 40 38 2D ?? ?? ?? ??").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_ppLocalize = g_ppVGuiLocalize; // these are set to the same thing in CSourceAppSystemGroup::Create } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/materialsystem/cmaterialglue.h b/r5dev/materialsystem/cmaterialglue.h index 02fc4104..cbdcd971 100644 --- a/r5dev/materialsystem/cmaterialglue.h +++ b/r5dev/materialsystem/cmaterialglue.h @@ -5,59 +5,72 @@ #include "public/materialsystem/shader_vcs_version.h" #include "public/rendersystem/schema/texture.g.h" -struct CMaterialGlue_Unknown +struct MaterialDXState_t { - __m128i unk1; - __m128i unk2; - __m128i unk3; + uint32_t blendState[8]; + unsigned int unkFlags; + unsigned __int16 depthStencilFlags; + unsigned __int16 rasterizerFlags; + char pad[8]; }; -#pragma pack(push, 1) // Without this MSVC might get the idea to align our members to completely fuck the offsets up. -// [ PIXIE ]: The texture GUID's aren't in a specific order, gonna leave them as ptr's so an individual can check them in any memory searcher. -// [ PIXIE ]: Verification needed for earlier seasons, if the struct is the same. -// [ PIXIE ]: Class seems mostly right, a few members are still missing though. +#pragma pack(push, 1) class CMaterialGlue : public IMaterialInternal { public: uint8_t pad_0008[8]; //0x0008 - uint64_t m_GUID; //0x0010 - const char* m_pszName; //0x0018 - const char* m_pszSurfaceProp; //0x0020 - const char* m_pszSurfaceProp2; //0x0028 - CMaterialGlue* m_pDepthShadow; //0x0030 - CMaterialGlue* m_pDepthPrepass; //0x0038 - CMaterialGlue* m_pDepthVSM; //0x0040 - CMaterialGlue* m_pDepthShadowTight; //0x0048 - CMaterialGlue* m_pColPass; //0x0050 - CShaderGlue* m_pShaderGlue; //0x0058 - TextureHeader_t** m_pTextureHandles; //0x0060 - TextureHeader_t** m_pStreamableTextureHandles; //0x0068 - int16_t m_nStreamableTextureCount; //0x0070 - int16_t m_iWidth; //0x0072 - int16_t m_iHeight; //0x0074 - int16_t m_iDepth; //0x0076 - uint32_t m_iFlags; //0x0078 [ PIXIE ]: I'm pretty sure those are VTF Image Flags, If you set them to NULL they cause Texture stretching. - int32_t m_unused2; //0x007C - uint8_t pad_0080[8]; //0x0080 - uint32_t m_iUnknownFlags1; //0x0088 - char pad_008C[4]; //0x008C - CMaterialGlue_Unknown m_UnkSections[2]; - uint8_t bytef0; - uint8_t bytef1; - uint8_t m_iMaterialType; + uint64_t assetGuid; //0x0010 + const char* name; //0x0018 + + const char* surfaceProp; //0x0020 + const char* surfaceProp2; //0x0028 + + CMaterialGlue* depthShadowMaterial; //0x0030 + CMaterialGlue* depthPrepassMaterial; //0x0038 + CMaterialGlue* depthVSMMaterial; //0x0040 + CMaterialGlue* depthShadowTightMaterial; //0x0048 + CMaterialGlue* colpassMaterial; //0x0050 + + CShaderGlue* shaderset; //0x0058 + + TextureHeader_t** textureHandles; //0x0060 + TextureHeader_t** streamingTextureHandles; //0x0068 + + int16_t numStreamingTextureHandles; //0x0070 + + int16_t width; //0x0072 + int16_t height; //0x0074 + int16_t depth; //0x0076 + + uint32_t samplers; //0x0078 + + char padding_7C[4]; //0x007C + + uint32_t unk_80; + uint32_t unk_84; + + uint64_t flags; // 0x0088 + + MaterialDXState_t dxStates[2]; + + uint16_t numAnimationFrames; // used in CMaterialGlue::GetNumAnimationFrames (0x1403B4250), which is called from GetSpriteInfo @ 0x1402561FC + uint8_t materialType; uint8_t bytef3; - int dwordf4; - void* m_pTextureAnim; - void** m_pDXBuffer; - void** m_pID3D11BufferVTable; - void* m_pViewsBuffer; - uint32_t m_iUnknown3; //0x0118 - uint16_t m_iUnknown4; //0x011C - uint16_t m_iUnknown5; //0x011E - uint16_t m_iUnknown6; //0x0120 - uint64_t m_Unknown7; //0x0122 - uint32_t m_iUnknown8; //0x012A - uint16_t m_iUnknown9; //0x012E + + char padding_F4[4]; + + void* textureAnim; + void** dxBuffer; + void** unkD3DPointer; // was m_pID3D11BufferVTable + void* viewsBuffer; + + uint32_t unknown3; //0x0118 + uint16_t unknown4; //0x011C + uint16_t unknown5; //0x011E + uint16_t unknown6; //0x0120 + uint64_t unknown7; //0x0122 + uint32_t unknown8; //0x012A + uint16_t unknown9; //0x012E }; //Size: 0x0130 confirmed end size. static_assert(sizeof(CMaterialGlue) == 0x130); #pragma pack(pop) @@ -68,8 +81,7 @@ inline void* g_pMaterialGlueVFTable = nullptr; /* ==== CMATERIALGLUE ================================================================================================================================================== */ #ifndef DEDICATED -inline CMemory p_GetMaterialAtCrossHair; -inline CMaterialGlue*(*GetMaterialAtCrossHair)(void); +inline CMaterialGlue*(*v_GetMaterialAtCrossHair)(void); #endif // !DEDICATED /////////////////////////////////////////////////////////////////////////////// @@ -77,16 +89,15 @@ class VMaterialGlue : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CMaterialGlue::`vftable'", reinterpret_cast(g_pMaterialGlueVFTable)); + LogConAdr("CMaterialGlue::`vftable'", g_pMaterialGlueVFTable); #ifndef DEDICATED - LogFunAdr("CMaterialGlue::GetMaterialAtCrossHair", p_GetMaterialAtCrossHair.GetPtr()); + LogFunAdr("CMaterialGlue::GetMaterialAtCrossHair", v_GetMaterialAtCrossHair); #endif // !DEDICATED } virtual void GetFun(void) const { #ifndef DEDICATED - p_GetMaterialAtCrossHair = g_GameDll.FindPatternSIMD("48 8B C4 48 83 EC 58 48 83 3D ?? ?? ?? ?? ??"); - GetMaterialAtCrossHair = p_GetMaterialAtCrossHair.RCast(); /*48 8B C4 48 83 EC 58 48 83 3D ? ? ? ? ?*/ + g_GameDll.FindPatternSIMD("48 8B C4 48 83 EC 58 48 83 3D ?? ?? ?? ?? ??").GetPtr(v_GetMaterialAtCrossHair); #endif // !DEDICATED } virtual void GetVar(void) const { } @@ -94,7 +105,6 @@ class VMaterialGlue : public IDetour { g_pMaterialGlueVFTable = g_GameDll.GetVirtualMethodTable(".?AVCMaterialGlue@@"); } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/materialsystem/cmaterialsystem.cpp b/r5dev/materialsystem/cmaterialsystem.cpp index 23356536..de2ef6aa 100644 --- a/r5dev/materialsystem/cmaterialsystem.cpp +++ b/r5dev/materialsystem/cmaterialsystem.cpp @@ -5,15 +5,34 @@ //===========================================================================// #include "core/stdafx.h" #include "tier0/crashhandler.h" +#include "tier0/commandline.h" #include "tier1/cvar.h" -#include "vpc/keyvalues.h" -#include "rtech/rtech_utils.h" +#include "tier1/keyvalues.h" +#include "rtech/pak/pakstate.h" #include "engine/cmodel_bsp.h" +#include "engine/sys_engine.h" +#include "geforce/reflex.h" #ifndef MATERIALSYSTEM_NODX +#include "gameui/imgui_system.h" #include "materialsystem/cmaterialglue.h" #endif // !MATERIALSYSTEM_NODX #include "materialsystem/cmaterialsystem.h" +#ifndef MATERIALSYSTEM_NODX +PCLSTATS_DEFINE() +#endif // MATERIALSYSTEM_NODX + +bool CMaterialSystem::Connect(CMaterialSystem* thisptr, const CreateInterfaceFn factory) +{ + const bool result = CMaterialSystem__Connect(thisptr, factory); + return result; +} + +void CMaterialSystem::Disconnect(CMaterialSystem* thisptr) +{ + CMaterialSystem__Disconnect(thisptr); +} + //----------------------------------------------------------------------------- // Purpose: initialization of the material system //----------------------------------------------------------------------------- @@ -22,18 +41,40 @@ InitReturnVal_t CMaterialSystem::Init(CMaterialSystem* thisptr) #ifdef MATERIALSYSTEM_NODX // Only load the 'startup.rpak' file, as 'common_early.rpak' has assets // that references assets in 'startup.rpak'. - RPakHandle_t pakHandle = g_pakLoadApi->LoadAsync("startup.rpak", AlignedMemAlloc(), 5); - g_pakLoadApi->WaitAsync(pakHandle); + PakHandle_t pakHandle = g_pakLoadApi->LoadAsync("startup.rpak", AlignedMemAlloc(), 5, 0); + g_pakLoadApi->WaitAsync(pakHandle, nullptr); // Trick: return INIT_FAILED to disable the loading of hardware // configuration data, since we don't need it on the dedi. return INIT_FAILED; #else // Initialize as usual. + GFX_EnableLowLatencySDK(!CommandLine()->CheckParm("-gfx_nvnDisableLowLatency")); + + if (GFX_IsLowLatencySDKEnabled()) + { + PCLSTATS_INIT(0); + } + return CMaterialSystem__Init(thisptr); #endif } +//----------------------------------------------------------------------------- +// Purpose: shutdown of the material system +//----------------------------------------------------------------------------- +int CMaterialSystem::Shutdown(CMaterialSystem* thisptr) +{ +#ifndef MATERIALSYSTEM_NODX + if (GFX_IsLowLatencySDKEnabled()) + { + PCLSTATS_SHUTDOWN(); + } +#endif + + return CMaterialSystem__Shutdown(thisptr); +} + #ifndef MATERIALSYSTEM_NODX //--------------------------------------------------------------------------------- // Purpose: loads and processes STBSP files @@ -51,40 +92,50 @@ void StreamDB_Init(const char* pszLevelName) if (pStreamKV) { const char* pszColumnName = pStreamKV->GetString(); - DevMsg(eDLL_T::MS, "StreamDB_Init: Loading override STBSP file '%s.stbsp'\n", pszColumnName); + Msg(eDLL_T::MS, "StreamDB_Init: Loading override STBSP file '%s.stbsp'\n", pszColumnName); v_StreamDB_Init(pszColumnName); return; } } - DevMsg(eDLL_T::MS, "StreamDB_Init: Loading STBSP file '%s.stbsp'\n", pszLevelName); + Msg(eDLL_T::MS, "StreamDB_Init: Loading STBSP file '%s.stbsp'\n", pszLevelName); v_StreamDB_Init(pszLevelName); } //--------------------------------------------------------------------------------- // Purpose: draw frame //--------------------------------------------------------------------------------- -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -void* __fastcall DispatchDrawCall(int64_t a1, uint64_t a2, int a3, int a4, char a5, int a6, uint8_t a7, int64_t a8, uint32_t a9, uint32_t a10, __m128* a11, int a12) -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) void* __fastcall DispatchDrawCall(int64_t a1, uint64_t a2, int a3, int a4, int64_t a5, int a6, uint8_t a7, int64_t a8, uint32_t a9, uint32_t a10, int a11, __m128* a12, int a13, int64_t a14) -#endif { // This only happens when the BSP is in a horrible condition (bad depth buffer draw calls!) // but allows you to load BSP's with virtually all missing shaders/materials and models // being replaced with 'material_for_aspect/error.rpak' and 'mdl/error.rmdl'. - if (!s_pRenderContext.GetValue()) + if (!*s_pRenderContext) return nullptr; -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - return v_DispatchDrawCall(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - return v_DispatchDrawCall(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); -#endif -} -#endif // !MATERIALSYSTEM_NODX -#ifndef MATERIALSYSTEM_NODX + return v_DispatchDrawCall(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14); +} + +//--------------------------------------------------------------------------------- +// Purpose: run IDXGISwapChain::Present +//--------------------------------------------------------------------------------- +ssize_t SpinPresent(void) +{ + ImguiSystem()->RenderFrame(); + + const ssize_t val = v_SpinPresent(); + return val; +} + +void* CMaterialSystem::SwapBuffers(CMaterialSystem* pMatSys) +{ + ImguiSystem()->SampleFrame(); + ImguiSystem()->SwapBuffers(); + + return CMaterialSystem__SwapBuffers(pMatSys); +} + //----------------------------------------------------------------------------- // Purpose: finds a material // Input : *pMatSys - @@ -94,11 +145,13 @@ void* __fastcall DispatchDrawCall(int64_t a1, uint64_t a2, int a3, int a4, int64 // bComplain - // Output : pointer to material //----------------------------------------------------------------------------- +static ConVar mat_alwaysComplain("mat_alwaysComplain", "0", FCVAR_RELEASE | FCVAR_MATERIAL_SYSTEM_THREAD, "Always complain when a material is missing"); + CMaterialGlue* CMaterialSystem::FindMaterialEx(CMaterialSystem* pMatSys, const char* pMaterialName, uint8_t nMaterialType, int nUnk, bool bComplain) { CMaterialGlue* pMaterial = CMaterialSystem__FindMaterialEx(pMatSys, pMaterialName, nMaterialType, nUnk, bComplain); - if ((bComplain || mat_alwaysComplain->GetBool()) && pMaterial->IsErrorMaterial()) + if ((bComplain || mat_alwaysComplain.GetBool()) && pMaterial->IsErrorMaterial()) { Error(eDLL_T::MS, NO_ERROR, "Material \"%s\" not found; replacing with \"%s\".\n", pMaterialName, pMaterial->GetName()); } @@ -114,29 +167,27 @@ Vector2D CMaterialSystem::GetScreenSize(CMaterialSystem* pMatSys) { Vector2D vecScreenSize; - CMaterialSystem_GetScreenSize(pMatSys, &vecScreenSize.x, &vecScreenSize.y); + CMaterialSystem__GetScreenSize(pMatSys, &vecScreenSize.x, &vecScreenSize.y); return vecScreenSize; } #endif // !MATERIALSYSTEM_NODX /////////////////////////////////////////////////////////////////////////////// -void VMaterialSystem::Attach() const +void VMaterialSystem::Detour(const bool bAttach) const { - DetourAttach((LPVOID*)&CMaterialSystem__Init, &CMaterialSystem::Init); + DetourSetup(&CMaterialSystem__Init, &CMaterialSystem::Init, bAttach); + DetourSetup(&CMaterialSystem__Shutdown, &CMaterialSystem::Shutdown, bAttach); + + DetourSetup(&CMaterialSystem__Connect, &CMaterialSystem::Connect, bAttach); + DetourSetup(&CMaterialSystem__Disconnect, &CMaterialSystem::Disconnect, bAttach); + #ifndef MATERIALSYSTEM_NODX - DetourAttach((LPVOID*)&v_StreamDB_Init, &StreamDB_Init); - DetourAttach((LPVOID*)&v_DispatchDrawCall, &DispatchDrawCall); - DetourAttach((LPVOID*)&CMaterialSystem__FindMaterialEx, &CMaterialSystem::FindMaterialEx); + DetourSetup(&CMaterialSystem__SwapBuffers, &CMaterialSystem::SwapBuffers, bAttach); + DetourSetup(&CMaterialSystem__FindMaterialEx, &CMaterialSystem::FindMaterialEx, bAttach); + + DetourSetup(&v_StreamDB_Init, &StreamDB_Init, bAttach); + DetourSetup(&v_DispatchDrawCall, &DispatchDrawCall, bAttach); + DetourSetup(&v_SpinPresent, &SpinPresent, bAttach); #endif // !MATERIALSYSTEM_NODX } - -void VMaterialSystem::Detach() const -{ - DetourDetach((LPVOID*)&CMaterialSystem__Init, &CMaterialSystem::Init); -#ifndef MATERIALSYSTEM_NODX - DetourDetach((LPVOID*)&v_StreamDB_Init, &StreamDB_Init); - DetourDetach((LPVOID*)&v_DispatchDrawCall, &DispatchDrawCall); - DetourDetach((LPVOID*)&CMaterialSystem__FindMaterialEx, &CMaterialSystem::FindMaterialEx); -#endif // !MATERIALSYSTEM_NODX -} \ No newline at end of file diff --git a/r5dev/materialsystem/cmaterialsystem.h b/r5dev/materialsystem/cmaterialsystem.h index 185b75e1..78718d6d 100644 --- a/r5dev/materialsystem/cmaterialsystem.h +++ b/r5dev/materialsystem/cmaterialsystem.h @@ -1,113 +1,171 @@ #ifndef MATERIALSYSTEM_H #define MATERIALSYSTEM_H #include "cmaterialglue.h" +#include "public/imaterialsystem.h" #define STREAM_DB_EXT "stbsp" class CMaterialSystem { public: + static bool Connect(CMaterialSystem* thisptr, const CreateInterfaceFn factory); + static void Disconnect(CMaterialSystem* thisptr); + static InitReturnVal_t Init(CMaterialSystem* thisptr); + static int Shutdown(CMaterialSystem* thisptr); #ifndef MATERIALSYSTEM_NODX + static void* SwapBuffers(CMaterialSystem* pMatSys); static CMaterialGlue* FindMaterialEx(CMaterialSystem* pMatSys, const char* pMaterialName, uint8_t nMaterialType, int nUnk, bool bComplain); static Vector2D GetScreenSize(CMaterialSystem* pMatSys = nullptr); #endif // !MATERIALSYSTEM_NODX + + // TODO: reverse the vftable! + inline int GetCurrentFrameCount() + { + const static int index = 74; + return CallVFunc(index, this); + } }; -/* ==== MATERIALSYSTEM ================================================================================================================================================== */ -inline CMemory p_CMaterialSystem__Init; -inline InitReturnVal_t(*CMaterialSystem__Init)(CMaterialSystem* thisptr); +#ifndef MATERIALSYSTEM_NODX +class CMaterialDeviceMgr +{ +public: + inline const MaterialAdapterInfo_t& GetAdapterInfo(int nIndex) const + { + Assert(nIndex >= 0 && nIndex < SDK_ARRAYSIZE(m_AdapterInfo)); + return m_AdapterInfo[nIndex]; + } + inline const MaterialAdapterInfo_t& GetAdapterInfo() const + { + // Retrieve info of the selected adapter. + return GetAdapterInfo(m_SelectedAdapter); + } -inline void* g_pMaterialSystem = nullptr; +private: + enum + { + MAX_ADAPTER_COUNT = 4 + }; + + IDXGIAdapter* m_Adapters[MAX_ADAPTER_COUNT]; + void* m_pUnknown1[MAX_ADAPTER_COUNT]; + MaterialAdapterInfo_t m_AdapterInfo[MAX_ADAPTER_COUNT]; + size_t m_AdapterMemorySize[MAX_ADAPTER_COUNT]; + int m_NumDisplayAdaptersProcessed; + int m_SelectedAdapter; + int m_NumDisplayAdapters; +}; + +inline CMaterialDeviceMgr* g_pMaterialAdapterMgr = nullptr; +#endif // !MATERIALSYSTEM_NODX + +/* ==== MATERIALSYSTEM ================================================================================================================================================== */ +inline InitReturnVal_t(*CMaterialSystem__Init)(CMaterialSystem* thisptr); +inline int(*CMaterialSystem__Shutdown)(CMaterialSystem* thisptr); + +inline bool(*CMaterialSystem__Connect)(CMaterialSystem*, const CreateInterfaceFn); +inline void(*CMaterialSystem__Disconnect)(CMaterialSystem*); + +inline CMaterialSystem* g_pMaterialSystem = nullptr; inline void* g_pMaterialVFTable = nullptr; #ifndef MATERIALSYSTEM_NODX -inline CMemory p_CMaterialSystem__FindMaterialEx; +inline void*(*CMaterialSystem__SwapBuffers)(CMaterialSystem* pMatSys); + inline CMaterialGlue*(*CMaterialSystem__FindMaterialEx)(CMaterialSystem* pMatSys, const char* pMaterialName, uint8_t nMaterialType, int nUnk, bool bComplain); +inline void(*CMaterialSystem__GetScreenSize)(CMaterialSystem* pMatSys, float* outX, float* outY); -inline CMemory p_CMaterialSystem_GetScreenSize; -inline void(*CMaterialSystem_GetScreenSize)(CMaterialSystem* pMatSys, float* outX, float* outY); - -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) -inline CMemory p_DispatchDrawCall; -inline void*(*v_DispatchDrawCall)(int64_t a1, uint64_t a2, int a3, int a4, char a5, int a6, uint8_t a7, int64_t a8, uint32_t a9, uint32_t a10, __m128* a11, int a12); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) -inline CMemory p_DispatchDrawCall; inline void*(*v_DispatchDrawCall)(int64_t a1, uint64_t a2, int a3, int a4, int64_t a5, int a6, uint8_t a7, int64_t a8, uint32_t a9, uint32_t a10, int a11, __m128* a12, int a13, int64_t a14); -#endif -inline CMemory p_GetStreamOverlay; -inline void(*v_GetStreamOverlay)(const char* mode, char* buf, size_t bufSize); - -inline CMemory p_DrawStreamOverlay; -inline const char*(*v_DrawStreamOverlay)(void* thisptr, uint8_t* a2, void* unused, void* a4); - -inline CMemory s_pRenderContext; - -inline int* g_nTotalStreamingTextureMemory = nullptr; -inline int* g_nUnfreeStreamingTextureMemory = nullptr; -inline int* g_nUnusableStreamingTextureMemory = nullptr; +inline ssize_t(*v_SpinPresent)(void); +inline void(*CMaterialSystem__GetStreamOverlay)(const char* mode, char* buf, size_t bufSize); +inline const char*(*CMaterialSystem__DrawStreamOverlay)(void* thisptr, uint8_t* a2, void* unused, void* a4); #endif // !MATERIALSYSTEM_NODX +inline void(*v_StreamDB_Init)(const char* const pszLevelName); + +#ifndef MATERIALSYSTEM_NODX +inline void** s_pRenderContext; // NOTE: This is some CMaterial instance or array. + +inline ssize_t* g_nTotalStreamingTextureMemory = nullptr; +inline ssize_t* g_nUnfreeStreamingTextureMemory = nullptr; +inline ssize_t* g_nUnusableStreamingTextureMemory = nullptr; +#endif // !MATERIALSYSTEM_NODX + +// TODO: move to materialsystem_global.h! +// TODO: reverse the vftable! +inline CMaterialSystem* MaterialSystem() +{ + return g_pMaterialSystem; +} + /////////////////////////////////////////////////////////////////////////////// class VMaterialSystem : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CMaterial::`vftable'", reinterpret_cast(g_pMaterialVFTable)); - LogFunAdr("CMaterialSystem::Init", p_CMaterialSystem__Init.GetPtr()); + LogConAdr("CMaterial::`vftable'", g_pMaterialVFTable); + LogFunAdr("CMaterialSystem::Init", CMaterialSystem__Init); + LogFunAdr("CMaterialSystem::Shutdown", CMaterialSystem__Shutdown); + LogFunAdr("CMaterialSystem::Connect", CMaterialSystem__Connect); + LogFunAdr("CMaterialSystem::Disconnect", CMaterialSystem__Disconnect); #ifndef MATERIALSYSTEM_NODX - LogFunAdr("CMaterialSystem::FindMaterialEx", p_CMaterialSystem__FindMaterialEx.GetPtr()); - LogFunAdr("CMaterialSystem::GetScreenSize", p_CMaterialSystem_GetScreenSize.GetPtr()); - LogFunAdr("CMaterialSystem::DispatchDrawCall", p_DispatchDrawCall.GetPtr()); - LogFunAdr("CMaterialSystem::GetStreamOverlay", p_GetStreamOverlay.GetPtr()); - LogFunAdr("CMaterialSystem::DrawStreamOverlay", p_DrawStreamOverlay.GetPtr()); - LogVarAdr("g_nTotalStreamingTextureMemory", reinterpret_cast(g_nTotalStreamingTextureMemory)); - LogVarAdr("g_nUnfreeStreamingTextureMemory", reinterpret_cast(g_nUnfreeStreamingTextureMemory)); - LogVarAdr("g_nUnusableStreamingTextureMemory", reinterpret_cast(g_nUnusableStreamingTextureMemory)); - LogVarAdr("s_pRenderContext", s_pRenderContext.GetPtr()); + LogFunAdr("CMaterialSystem::SwapBuffers", CMaterialSystem__SwapBuffers); + LogFunAdr("CMaterialSystem::FindMaterialEx", CMaterialSystem__FindMaterialEx); + LogFunAdr("CMaterialSystem::GetScreenSize", CMaterialSystem__GetScreenSize); + LogFunAdr("CMaterialSystem::GetStreamOverlay", CMaterialSystem__GetStreamOverlay); + LogFunAdr("CMaterialSystem::DrawStreamOverlay", CMaterialSystem__DrawStreamOverlay); + LogFunAdr("DispatchDrawCall", v_DispatchDrawCall); + LogFunAdr("SpinPresent", v_SpinPresent); #endif // !MATERIALSYSTEM_NODX - LogVarAdr("g_pMaterialSystem", reinterpret_cast(g_pMaterialSystem)); + LogFunAdr("StreamDB_Init", v_StreamDB_Init); + +#ifndef MATERIALSYSTEM_NODX + LogVarAdr("g_nTotalStreamingTextureMemory", g_nTotalStreamingTextureMemory); + LogVarAdr("g_nUnfreeStreamingTextureMemory", g_nUnfreeStreamingTextureMemory); + LogVarAdr("g_nUnusableStreamingTextureMemory", g_nUnusableStreamingTextureMemory); + LogVarAdr("s_pRenderContext", s_pRenderContext); + LogVarAdr("g_MaterialAdapterMgr", g_pMaterialAdapterMgr); +#endif // !MATERIALSYSTEM_NODX + LogVarAdr("g_pMaterialSystem", g_pMaterialSystem); } virtual void GetFun(void) const { - p_CMaterialSystem__Init = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 70 48 83 3D ?? ?? ?? ?? ??"); - CMaterialSystem__Init = p_CMaterialSystem__Init.RCast(); /*48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 70 48 83 3D ?? ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 70 48 83 3D ?? ?? ?? ?? ??").GetPtr(CMaterialSystem__Init); + g_GameDll.FindPatternSIMD("48 83 EC 58 48 89 6C 24 ??").GetPtr(CMaterialSystem__Shutdown); + + g_GameDll.FindPatternSIMD("48 89 54 24 ?? 56 48 83 EC 50").GetPtr(CMaterialSystem__Connect); + g_GameDll.FindPatternSIMD("48 83 EC 28 8B 0D ?? ?? ?? ?? 48 89 6C 24 ??").GetPtr(CMaterialSystem__Disconnect); #ifndef MATERIALSYSTEM_NODX - p_CMaterialSystem__FindMaterialEx = g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 44 88 44 24 ?? 48 89 4C 24 ??"); - CMaterialSystem__FindMaterialEx = p_CMaterialSystem__FindMaterialEx.RCast(); /*44 89 4C 24 ?? 44 88 44 24 ?? 48 89 4C 24 ??*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 56 41 57 48 83 EC 40 65 48 8B 04 25 ?? ?? ?? ??").GetPtr(CMaterialSystem__SwapBuffers); - p_CMaterialSystem_GetScreenSize = g_GameDll.FindPatternSIMD("8B 05 ?? ?? ?? ?? 89 02 8B 05 ?? ?? ?? ?? 41 89 ?? C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC 8B 05 ?? ?? ?? ??"); - CMaterialSystem_GetScreenSize = p_CMaterialSystem_GetScreenSize.RCast(); /*8B 05 ? ? ? ? 89 02 8B 05 ? ? ? ? 41 89 00 C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC 8B 05 ? ? ? ?*/ -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_DispatchDrawCall = g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 44 89 44 24 ?? 48 89 4C 24 ?? 55 53"); - v_DispatchDrawCall = p_DispatchDrawCall.RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_DispatchDrawCall = g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 44 89 44 24 ?? 48 89 4C 24 ?? 55 53 56"); - v_DispatchDrawCall = p_DispatchDrawCall.RCast(); -#endif - p_GetStreamOverlay = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 0F 84 ?? ?? ?? ?? 48 89 9C 24 ?? ?? ?? ??").FollowNearCallSelf(); - v_GetStreamOverlay = p_GetStreamOverlay.RCast(); /*E8 ? ? ? ? 80 7C 24 ? ? 0F 84 ? ? ? ? 48 89 9C 24 ? ? ? ?*/ + g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 44 88 44 24 ?? 48 89 4C 24 ??").GetPtr(CMaterialSystem__FindMaterialEx); + g_GameDll.FindPatternSIMD("8B 05 ?? ?? ?? ?? 89 02 8B 05 ?? ?? ?? ?? 41 89 ?? C3 CC CC CC CC CC CC CC CC CC CC CC CC CC CC 8B 05 ?? ?? ?? ??").GetPtr(CMaterialSystem__GetScreenSize); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 80 7C 24 ?? ?? 0F 84 ?? ?? ?? ?? 48 89 9C 24 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(CMaterialSystem__GetStreamOverlay); + g_GameDll.FindPatternSIMD("41 56 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 C6 02 ??").GetPtr(CMaterialSystem__DrawStreamOverlay); - p_DrawStreamOverlay = g_GameDll.FindPatternSIMD("41 56 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 C6 02 ??"); - v_DrawStreamOverlay = p_DrawStreamOverlay.RCast(); // 41 56 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 C6 02 00 // + g_GameDll.FindPatternSIMD("44 89 4C 24 ?? 44 89 44 24 ?? 48 89 4C 24 ?? 55 53 56").GetPtr(v_DispatchDrawCall); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 8B 15 ?? ?? ?? ??").GetPtr(v_SpinPresent); #endif // !MATERIALSYSTEM_NODX + + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 54 41 56 41 57 48 83 EC 40 48 8B E9").GetPtr(v_StreamDB_Init); } virtual void GetVar(void) const { #ifndef MATERIALSYSTEM_NODX - g_nTotalStreamingTextureMemory = p_DrawStreamOverlay.Offset(0x1C).FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_nUnfreeStreamingTextureMemory = p_DrawStreamOverlay.Offset(0x2D).FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_nUnusableStreamingTextureMemory = p_DrawStreamOverlay.Offset(0x50).FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + CMemory(CMaterialSystem__DrawStreamOverlay).Offset(0x1C).FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(g_nTotalStreamingTextureMemory); + CMemory(CMaterialSystem__DrawStreamOverlay).Offset(0x2D).FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(g_nUnfreeStreamingTextureMemory); + CMemory(CMaterialSystem__DrawStreamOverlay).Offset(0x50).FindPatternSelf("48 8B 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(g_nUnusableStreamingTextureMemory); - s_pRenderContext = p_DispatchDrawCall.FindPattern("48 8B ?? ?? ?? ?? 01").ResolveRelativeAddressSelf(0x3, 0x7); + CMemory(v_DispatchDrawCall).FindPattern("48 8B ?? ?? ?? ?? 01").ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(s_pRenderContext); + CMemory(CMaterialSystem__Disconnect).FindPattern("48 8D").ResolveRelativeAddressSelf(0x3, 0x7).GetPtr(g_pMaterialAdapterMgr); #endif // !MATERIALSYSTEM_NODX - g_pMaterialSystem = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 85 C9 74 11 48 8B 01 48 8D 15 ?? ?? ?? ??").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pMaterialSystem = g_GameDll.FindPatternSIMD("8B 41 28 85 C0 7F 18").FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(3, 7).RCast(); } virtual void GetCon(void) const { g_pMaterialVFTable = g_GameDll.GetVirtualMethodTable(".?AVCMaterial@@").RCast(); } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/materialsystem/cshaderglue.cpp b/r5dev/materialsystem/cshaderglue.cpp index 297dbd42..fe158458 100644 --- a/r5dev/materialsystem/cshaderglue.cpp +++ b/r5dev/materialsystem/cshaderglue.cpp @@ -10,14 +10,3 @@ int CShaderGlue::SetupShader(uint64_t nCount, uint64_t a3, void* pRawMaterialGlu { return CShaderGlue_SetupShader(this, nCount, a3, pRawMaterialGlueWithoutVTable); } - -/////////////////////////////////////////////////////////////////////////////// -void CShaderGlue_Attach() -{ - -} - -void CShaderGlue_Detach() -{ - -} \ No newline at end of file diff --git a/r5dev/materialsystem/cshaderglue.h b/r5dev/materialsystem/cshaderglue.h index d77fa290..eb1e8e35 100644 --- a/r5dev/materialsystem/cshaderglue.h +++ b/r5dev/materialsystem/cshaderglue.h @@ -33,30 +33,25 @@ static_assert(sizeof(CShaderGlue) == 0x40); // [ PIXIE ]: All vars have proper d /* ==== CSHADERGLUE ================================================================================================================================================== */ inline int(*CShaderGlue_SetupShader)(CShaderGlue* thisptr, uint64_t nCount, uint64_t a3, void* pRawMaterialGlueWithoutVTable); -inline CMemory CShaderGlue_VTable; inline void* g_pShaderGlueVFTable = nullptr; -void CShaderGlue_Attach(); -void CShaderGlue_Detach(); /////////////////////////////////////////////////////////////////////////////// class VShaderGlue : public IDetour { virtual void GetAdr(void) const { - LogConAdr("CShaderGlue::`vftable'", reinterpret_cast(g_pShaderGlueVFTable)); - LogFunAdr("CShaderGlue::SetupShader", reinterpret_cast(CShaderGlue_SetupShader)); + LogConAdr("CShaderGlue::`vftable'", g_pShaderGlueVFTable); + LogFunAdr("CShaderGlue::SetupShader", CShaderGlue_SetupShader); } virtual void GetFun(void) const { - CShaderGlue_SetupShader = CShaderGlue_VTable.WalkVTable(4).Deref().RCast(); + CShaderGlue_SetupShader = CMemory(g_pShaderGlueVFTable).WalkVTable(4).Deref().RCast(); } virtual void GetVar(void) const { } virtual void GetCon(void) const { - CShaderGlue_VTable = g_GameDll.GetVirtualMethodTable(".?AVCShaderGlue@@"); - g_pShaderGlueVFTable = CShaderGlue_VTable.RCast(); + g_pShaderGlueVFTable = g_GameDll.GetVirtualMethodTable(".?AVCShaderGlue@@"); } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/mathlib/CMakeLists.txt b/r5dev/mathlib/CMakeLists.txt index 8c638ed4..027e689a 100644 --- a/r5dev/mathlib/CMakeLists.txt +++ b/r5dev/mathlib/CMakeLists.txt @@ -12,8 +12,6 @@ add_sources( SOURCE_GROUP "Hash" "IceKey.H" "sha1.cpp" "sha1.h" - "sha256.cpp" - "sha256.h" ) add_sources( SOURCE_GROUP "RNG" @@ -64,5 +62,6 @@ target_precompile_headers( ${PROJECT_NAME} PRIVATE ) target_compile_definitions( ${PROJECT_NAME} PRIVATE "BUILDING_MATHLIB" + "ALLOW_SIMD_QUATERNION_MATH" $<$,$>:DEBUG_MATHLIB> ) diff --git a/r5dev/mathlib/mathlib_base.cpp b/r5dev/mathlib/mathlib_base.cpp index d1331455..1a87be64 100644 --- a/r5dev/mathlib/mathlib_base.cpp +++ b/r5dev/mathlib/mathlib_base.cpp @@ -4059,21 +4059,6 @@ void MathLib_Init(float gamma, float texGamma, float brightness, int overbright) // FIXME: Hook SSE into VectorAligned + Vector4DAligned -#if !defined( _GAMECONSOLE ) - // Grab the processor information: - const CPUInformation& pi = GetCPUInformation(); - - if (!(pi.m_bSSE && pi.m_bSSE2)) - { - Assert(0); - if (MessageBoxA(NULL, "SSE and SSE2 are required.", "Unsupported CPU", MB_ICONERROR | MB_OK)) - { - TerminateProcess(GetCurrentProcess(), EXIT_FAILURE); - } - } -#endif //!360 - - s_bMathlibInitialized = true; InitSinCosTable(); diff --git a/r5dev/mathlib/sha256.cpp b/r5dev/mathlib/sha256.cpp deleted file mode 100644 index b49a3588..00000000 --- a/r5dev/mathlib/sha256.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "mathlib/sha256.h" - -const uint32 SHA256::sha256_k[64] = //UL = uint32 - {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; - -void SHA256::transform(const uint8* message, uint64 block_nb) -{ - uint32 w[64]{}; - uint32 wv[8]{}; - uint32 t1, t2; - uint64 i; - int j; - const uint8* sub_block; - for (i = 0; i < block_nb; i++) { - sub_block = message + (i << 6); - for (j = 0; j < 16; j++) { - SHA2_PACK32(&sub_block[j << 2], &w[j]); - } - for (j = 16; j < 64; j++) { - w[j] = SHA256_F4(w[j - 2]) + w[j - 7] + SHA256_F3(w[j - 15]) + w[j - 16]; - } - for (j = 0; j < 8; j++) { - wv[j] = m_h[j]; - } - for (j = 0; j < 64; j++) { - t1 = wv[7] + SHA256_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6]) - + sha256_k[j] + w[j]; - t2 = SHA256_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - for (j = 0; j < 8; j++) { - m_h[j] += wv[j]; - } - } -} - -void SHA256::init() -{ - m_h[0] = 0x6a09e667; - m_h[1] = 0xbb67ae85; - m_h[2] = 0x3c6ef372; - m_h[3] = 0xa54ff53a; - m_h[4] = 0x510e527f; - m_h[5] = 0x9b05688c; - m_h[6] = 0x1f83d9ab; - m_h[7] = 0x5be0cd19; - m_len = 0; - m_tot_len = 0; -} - -void SHA256::update(const uint8* message, uint64 len) -{ - uint64 block_nb; - uint64 new_len, rem_len, tmp_len; - const uint8 *shifted_message; - tmp_len = SHA224_256_BLOCK_SIZE - m_len; - rem_len = len < tmp_len ? len : tmp_len; - memcpy(&m_block[m_len], message, rem_len); - if (m_len + len < SHA224_256_BLOCK_SIZE) { - m_len += len; - return; - } - new_len = len - rem_len; - block_nb = new_len / SHA224_256_BLOCK_SIZE; - shifted_message = message + rem_len; - transform(m_block, 1); - transform(shifted_message, block_nb); - rem_len = new_len % SHA224_256_BLOCK_SIZE; - memcpy(m_block, &shifted_message[block_nb << 6], rem_len); - m_len = rem_len; - m_tot_len += (block_nb + 1) << 6; -} - -void SHA256::final(uint8* digest) -{ - uint64 block_nb; - uint64 pm_len; - uint64 len_b; - int i; - block_nb = (1 + ((SHA224_256_BLOCK_SIZE - 9) - < (m_len % SHA224_256_BLOCK_SIZE))); - len_b = (m_tot_len + m_len) << 3; - pm_len = block_nb << 6; - memset(m_block + m_len, '\0', pm_len - m_len); - m_block[m_len] = 0x80; - SHA2_UNPACK32(len_b, m_block + pm_len - 4); - transform(m_block, block_nb); - for (i = 0 ; i < 8; i++) { - SHA2_UNPACK32(m_h[i], &digest[i << 2]); - } -} - -string sha256(const string& input) -{ - uint8 digest[SHA256::DIGEST_SIZE]; - memset(digest, '\0', SHA256::DIGEST_SIZE); - - SHA256 ctx = SHA256(); - ctx.init(); - ctx.update(reinterpret_cast(input.c_str()), input.length()); - ctx.final(digest); - - char buf[2*SHA256::DIGEST_SIZE+1]; - memset(buf, '\0', 2*SHA256::DIGEST_SIZE+1); - - for (uint32 i = 0; i < SHA256::DIGEST_SIZE; i++) { - sprintf(buf + i * 2, "%02x", digest[i]); - } - - return buf; -} diff --git a/r5dev/mathlib/sha256.h b/r5dev/mathlib/sha256.h deleted file mode 100644 index b233df7c..00000000 --- a/r5dev/mathlib/sha256.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef SHA256_H -#define SHA256_H - -class SHA256 -{ -protected: - typedef unsigned char uint8; - typedef unsigned int uint32; - typedef unsigned long long uint64; - - static const uint32 sha256_k[]; - static const uint32 SHA224_256_BLOCK_SIZE = (512/8); -public: - void init(); - void update(const uint8* message, uint64 len); - void final(uint8* digest); - static const uint32 DIGEST_SIZE = ( 256 / 8); - -protected: - void transform(const uint8* message, uint64 block_nb); - uint64 m_tot_len; - uint64 m_len; - uint8 m_block[2*SHA224_256_BLOCK_SIZE]; - uint32 m_h[8]; -}; - -string sha256(const string& input); - -#define SHA2_SHFR(x, n) (x >> n) -#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) -#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) -#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z)) -#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) -#define SHA256_F1(x) (SHA2_ROTR(x, 2) ^ SHA2_ROTR(x, 13) ^ SHA2_ROTR(x, 22)) -#define SHA256_F2(x) (SHA2_ROTR(x, 6) ^ SHA2_ROTR(x, 11) ^ SHA2_ROTR(x, 25)) -#define SHA256_F3(x) (SHA2_ROTR(x, 7) ^ SHA2_ROTR(x, 18) ^ SHA2_SHFR(x, 3)) -#define SHA256_F4(x) (SHA2_ROTR(x, 17) ^ SHA2_ROTR(x, 19) ^ SHA2_SHFR(x, 10)) -#define SHA2_UNPACK32(x, str) \ -{ \ - *((str) + 3) = (uint8) ((x) ); \ - *((str) + 2) = (uint8) ((x) >> 8); \ - *((str) + 1) = (uint8) ((x) >> 16); \ - *((str) + 0) = (uint8) ((x) >> 24); \ -} -#define SHA2_PACK32(str, x) \ -{ \ - *(x) = ((uint32) *((str) + 3) ) \ - | ((uint32) *((str) + 2) << 8) \ - | ((uint32) *((str) + 1) << 16) \ - | ((uint32) *((str) + 0) << 24); \ -} -#endif diff --git a/r5dev/mathlib/ssequaternion.h b/r5dev/mathlib/ssequaternion.h index 5cb9bc33..5c024cbe 100644 --- a/r5dev/mathlib/ssequaternion.h +++ b/r5dev/mathlib/ssequaternion.h @@ -421,8 +421,8 @@ FORCEINLINE fltx4 QuaternionSlerpNoAlignSIMD(const fltx4& p, const fltx4& q, flo SubFloat(result, 1) = SubFloat(q, 0); SubFloat(result, 2) = -SubFloat(q, 3); SubFloat(result, 3) = SubFloat(q, 2); - sclp = sin((1.0f - t) * (0.5f * M_PI)); - sclq = sin(t * (0.5f * M_PI)); + sclp = (float)sin((1.0f - t) * (0.5f * M_PI)); + sclq = (float)sin(t * (0.5f * M_PI)); SubFloat(result, 0) = sclp * SubFloat(p, 0) + sclq * SubFloat(result, 0); SubFloat(result, 1) = sclp * SubFloat(p, 1) + sclq * SubFloat(result, 1); SubFloat(result, 2) = sclp * SubFloat(p, 2) + sclq * SubFloat(result, 2); diff --git a/r5dev/mathlib/swap.h b/r5dev/mathlib/swap.h index eb817536..afd47a77 100644 --- a/r5dev/mathlib/swap.h +++ b/r5dev/mathlib/swap.h @@ -11,6 +11,10 @@ #define LittleDWord( val ) ( val ) #define LittleQWord( val ) ( val ) +// If a swapped float passes through the fpu, the bytes may get changed. +// Prevent this by swapping floats as DWORDs. +#define SafeSwapFloat( pOut, pIn ) (*((uint*)pOut) = DWordSwap( *((uint*)pIn) )) + template inline T WordSwapC(T w) { @@ -98,6 +102,12 @@ inline void StoreLittleInt64(uint64* base, unsigned int nWordIndex, uint64 nWord __storedoublewordbytereverse(nWord, nWordIndex << 2, base); } #endif + +// Pass floats by pointer for swapping to avoid truncation in the fpu +#define BigFloat( pOut, pIn ) ( *pOut = *pIn ) +#define LittleFloat( pOut, pIn ) SafeSwapFloat( pOut, pIn ) +#define SwapFloat( pOut, pIn ) LittleFloat( pOut, pIn ) + #else inline uint32 LoadLittleDWord(uint32* base, unsigned int dwordIndex) { @@ -108,4 +118,9 @@ inline void StoreLittleDWord(uint32* base, unsigned int dwordIndex, uint32 dword { base[dwordIndex] = LittleDWord(dword); } + +// Pass floats by pointer for swapping to avoid truncation in the fpu +#define BigFloat( pOut, pIn ) SafeSwapFloat( pOut, pIn ) +#define LittleFloat( pOut, pIn ) ( *pOut = *pIn ) +#define SwapFloat( pOut, pIn ) BigFloat( pOut, pIn ) #endif diff --git a/r5dev/mathlib/vector.h b/r5dev/mathlib/vector.h index b7a94c4f..8b5470be 100644 --- a/r5dev/mathlib/vector.h +++ b/r5dev/mathlib/vector.h @@ -1958,7 +1958,7 @@ void Quaternion::Print() const { #ifndef _CERT #if !defined(__SPU__) - DevMsg(eDLL_T::COMMON, "q{ %.3fi + %.3fj + %.3fk + %.3f }", x, y, z, w); + Msg(eDLL_T::COMMON, "q{ %.3fi + %.3fj + %.3fk + %.3f }", x, y, z, w); #endif #endif } @@ -2428,6 +2428,7 @@ public: void Normalize(); void NormalizePositive(); + inline Vector3D GetNormal() const; inline struct matrix3x4_t ToMatrix() const; inline Quaternion ToQuaternion() const; @@ -2533,6 +2534,11 @@ inline void QAngle::NormalizePositive() z = AngleNormalizePositive(z); } +inline Vector3D QAngle::GetNormal() const +{ + Vector3D ret(cos(VEC_DEG2RAD(y)), sin(VEC_DEG2RAD(y)), -sin(VEC_DEG2RAD(x))); + return ret; +} #if !defined(__SPU__) inline void QAngle::Random(vec_t minVal, vec_t maxVal) diff --git a/r5dev/mathlib/vplane.h b/r5dev/mathlib/vplane.h index 48f52a0f..a3fefed4 100644 --- a/r5dev/mathlib/vplane.h +++ b/r5dev/mathlib/vplane.h @@ -30,6 +30,7 @@ class VPlane public: VPlane(); VPlane(const Vector3D& vNormal, vec_t dist); + VPlane(const Vector3D& vPoint, const QAngle& ang); void Init(const Vector3D& vNormal, vec_t dist); @@ -86,6 +87,12 @@ inline VPlane::VPlane(const Vector3D& vNormal, vec_t dist) m_Dist = dist; } +inline VPlane::VPlane(const Vector3D& vPoint, const QAngle& ang) +{ + m_Normal = ang.GetNormal(); + m_Dist = vPoint.x * m_Normal.x + vPoint.y * m_Normal.y + vPoint.z * m_Normal.z; +} + inline void VPlane::Init(const Vector3D& vNormal, vec_t dist) { m_Normal = vNormal; diff --git a/r5dev/naveditor/GameUtils.cpp b/r5dev/naveditor/GameUtils.cpp index 297e04a8..4011cf65 100644 --- a/r5dev/naveditor/GameUtils.cpp +++ b/r5dev/naveditor/GameUtils.cpp @@ -142,7 +142,7 @@ void buildLinkTable(dtNavMesh* mesh, LinkTableData& data) { auto l = *nlabels.begin(); poly.disjointSetId = (unsigned short)l; - for (auto nl : nlabels) + for (const int nl : nlabels) data.set_union(l, nl); } nlabels.clear(); diff --git a/r5dev/netconsole/CMakeLists.txt b/r5dev/netconsole/CMakeLists.txt index 69d3a74f..c18e4c43 100644 --- a/r5dev/netconsole/CMakeLists.txt +++ b/r5dev/netconsole/CMakeLists.txt @@ -40,7 +40,7 @@ set_target_properties( ${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(ProjectDir)../../../${BUILD_OUTPUT_DIR}/bin/" ) target_compile_definitions( ${PROJECT_NAME} PRIVATE - "NETCONSOLE" + "_TOOLS" ) target_link_libraries( ${PROJECT_NAME} PRIVATE "tier0" diff --git a/r5dev/netconsole/netconsole.cpp b/r5dev/netconsole/netconsole.cpp index 6e843210..7620fd28 100644 --- a/r5dev/netconsole/netconsole.cpp +++ b/r5dev/netconsole/netconsole.cpp @@ -7,6 +7,7 @@ #include "core/stdafx.h" #include "core/logdef.h" #include "core/logger.h" +#include "tier0/cpu.h" #include "tier0/utility.h" #include "tier1/NetAdr.h" #include "tier2/socketcreator.h" @@ -22,7 +23,7 @@ //----------------------------------------------------------------------------- CNetCon::CNetCon(void) : m_bInitialized(false) - , m_bQuitApplication(false) + , m_bQuitting(false) , m_bPromptConnect(true) , m_flTickInterval(0.05f) { @@ -43,9 +44,12 @@ CNetCon::~CNetCon(void) // Purpose: WSA and NETCON systems init // Output : true on success, false otherwise //----------------------------------------------------------------------------- -bool CNetCon::Init(const bool bAnsiColor) +bool CNetCon::Init(const bool bAnsiColor, const char* pHostName, const int nPort) { + std::lock_guard l(m_Mutex); + g_CoreMsgVCallback = &EngineLoggerSink; + TermSetup(bAnsiColor); WSAData wsaData; const int nError = ::WSAStartup(MAKEWORD(2, 2), &wsaData); @@ -56,20 +60,42 @@ bool CNetCon::Init(const bool bAnsiColor) return false; } - m_bInitialized = true; + // Try to connect from given parameters, these are passed in from the + // command line. If we fail, return out as this allows the user to + // quickly retry again. + if (pHostName && nPort != SOCKET_ERROR) + { + if (!Connect(pHostName, nPort)) + { + return false; + } + } - TermSetup(bAnsiColor); - DevMsg(eDLL_T::NONE, "R5 TCP net console [Version %s]\n", NETCON_VERSION); + m_bInitialized = true; + Msg(eDLL_T::NONE, "R5 TCP net console [Version %s]\n", NETCON_VERSION); static std::thread frame([this]() { - for (;;) - { - RunFrame(); - } + while (RunFrame()) + {} }); frame.detach(); + + static std::thread input([this]() + { + while (!NetConsole()->GetQuitting()) + { + std::string lineInput; + + if (std::getline(std::cin, lineInput)) + { + NetConsole()->RunInput(lineInput); + } + } + }); + input.detach(); + return true; } @@ -79,9 +105,19 @@ bool CNetCon::Init(const bool bAnsiColor) //----------------------------------------------------------------------------- bool CNetCon::Shutdown(void) { + if (!m_bInitialized) + { + // Called twice! + Assert(0); + return false; + } + + m_bInitialized = false; + + std::lock_guard l(m_Mutex); bool bResult = false; - m_Socket.CloseAllAcceptedSockets(); + GetSocketCreator()->CloseAllAcceptedSockets(); const int nError = ::WSACleanup(); if (nError == 0) @@ -95,11 +131,33 @@ bool CNetCon::Shutdown(void) } SpdLog_Shutdown(); - Console_Shutdown(); - return bResult; } +//----------------------------------------------------------------------------- +// Purpose: handle close events outside application routines +//----------------------------------------------------------------------------- +BOOL WINAPI CNetCon::CloseHandler(DWORD eventCode) +{ + switch (eventCode) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + + NetConsole()->SetQuitting(true); + + // Give it time to shutdown properly, value is set to the max possible + // of SPI_GETWAITTOKILLSERVICETIMEOUT, which is 20000ms by default. + Sleep(20000); + return TRUE; + } + + return FALSE; +} + //----------------------------------------------------------------------------- // Purpose: terminal setup //----------------------------------------------------------------------------- @@ -107,136 +165,171 @@ void CNetCon::TermSetup(const bool bAnsiColor) { SpdLog_Init(bAnsiColor); Console_Init(bAnsiColor); + + // Handle ctrl+x or X close events, give the application time to shutdown + // properly and flush all logging buffers. + SetConsoleCtrlHandler(CloseHandler, true); + + // Write console output to a file, this includes everything from local logs + // to messages over the wire. Note that logs emitted locally are prefixed + // with timestamps in () while messages over the wire are in []. + SpdLog_InstallSupplementalLogger("supplemental_logger_mt", "netconsole.log"); } //----------------------------------------------------------------------------- // Purpose: gets input IP and port for initialization //----------------------------------------------------------------------------- -void CNetCon::UserInput(void) +void CNetCon::RunInput(const string& lineInput) { - if (std::getline(std::cin, m_Input)) + if (lineInput.empty()) { - if (m_Input.compare("nquit") == 0) + // Empty string given, don't process it. + return; + } + + if (lineInput.compare("nquit") == 0) + { + SetQuitting(true); + return; + } + + std::lock_guard l(m_Mutex); + + if (IsConnected()) + { + CCommand cmd; + cmd.Tokenize(lineInput.c_str()); + + if (V_strcmp(cmd.Arg(0), "disconnect") == 0) { - m_bQuitApplication = true; + Disconnect("user closed connection"); return; } - std::lock_guard l(m_Mutex); + vector vecMsg; - if (IsConnected()) + const SocketHandle_t hSocket = GetSocket(); + bool bSend = false; + + if (cmd.ArgC() > 1) { - CCommand cmd; - cmd.Tokenize(m_Input.c_str()); - - if (V_strcmp(cmd.Arg(0), "disconnect") == 0) + if (V_strcmp(cmd.Arg(0), "PASS") == 0) // Auth with RCON server. { - Disconnect("user closed connection"); - return; + bSend = Serialize(vecMsg, cmd.Arg(1), "", + cl_rcon::request_t::SERVERDATA_REQUEST_AUTH); } - - vector vecMsg; - - const SocketHandle_t hSocket = GetSocket(); - bool bSend = false; - - if (cmd.ArgC() > 1) + else // Execute command query. { - if (V_strcmp(cmd.Arg(0), "PASS") == 0) // Auth with RCON server. - { - bSend = Serialize(vecMsg, cmd.Arg(1), "", - cl_rcon::request_t::SERVERDATA_REQUEST_AUTH); - } - else // Execute command query. - { - bSend = Serialize(vecMsg, cmd.Arg(0), cmd.GetCommandString(), - cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND); - } - } - else if (!m_Input.empty()) // Single arg command query. - { - bSend = Serialize(vecMsg, m_Input.c_str(), "", cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND); - } - - if (bSend) // Only send if serialization process was successful. - { - if (!Send(hSocket, vecMsg.data(), int(vecMsg.size()))) - { - Error(eDLL_T::CLIENT, NO_ERROR, "Failed to send RCON message: (%s)\n", "SOCKET_ERROR"); - } + bSend = Serialize(vecMsg, cmd.Arg(0), cmd.GetCommandString(), + cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND); } } - else // Setup connection from input. + else // Single arg command query. { - CCommand cmd; - cmd.Tokenize(m_Input.c_str(), cmd_source_t::kCommandSrcCode, &m_CharacterSet); + bSend = Serialize(vecMsg, lineInput.c_str(), "", cl_rcon::request_t::SERVERDATA_REQUEST_EXECCOMMAND); + } - if (cmd.ArgC() > 1) + if (bSend) // Only send if serialization process was successful. + { + if (!Send(hSocket, vecMsg.data(), int(vecMsg.size()))) { - const char* inAddr = cmd.Arg(0); - const char* inPort = cmd.Arg(1); - - if (!*inAddr || !*inPort) - { - Warning(eDLL_T::CLIENT, "No IP address or port provided\n"); - m_bPromptConnect = true; - return; - } - - if (!Connect(inAddr, atoi(inPort))) - { - m_bPromptConnect = true; - return; - } - } - else - { - if (!Connect(cmd.GetCommandString())) - { - m_bPromptConnect = true; - return; - } + Error(eDLL_T::CLIENT, NO_ERROR, "Failed to send RCON message: (%s)\n", "SOCKET_ERROR"); } } } -} + else // Setup connection from input. + { + CCommand cmd; + cmd.Tokenize(lineInput.c_str(), cmd_source_t::kCommandSrcCode, &m_CharacterSet); -//----------------------------------------------------------------------------- -// Purpose: clears the input buffer -//----------------------------------------------------------------------------- -void CNetCon::ClearInput(void) -{ - m_Input.clear(); + if (cmd.ArgC() > 1) + { + const char* inAddr = cmd.Arg(0); + const char* inPort = cmd.Arg(1); + + if (!*inAddr || !*inPort) + { + Warning(eDLL_T::CLIENT, "No IP address or port provided\n"); + SetPrompting(true); + return; + } + + if (!Connect(inAddr, atoi(inPort))) + { + SetPrompting(true); + return; + } + } + else + { + if (!Connect(cmd.GetCommandString())) + { + SetPrompting(true); + return; + } + } + } } //----------------------------------------------------------------------------- // Purpose: client's main processing loop //----------------------------------------------------------------------------- -void CNetCon::RunFrame(void) +bool CNetCon::RunFrame(void) { - if (IsInitialized() && IsConnected()) + if (IsInitialized()) { std::lock_guard l(m_Mutex); - CConnectedNetConsoleData& pData = m_Socket.GetAcceptedSocketData(0); - Recv(pData); - } - else if (m_bPromptConnect) - { - DevMsg(eDLL_T::NONE, "Enter []: or : "); - m_bPromptConnect = false; + if (IsConnected()) + { + CConnectedNetConsoleData& pData = GetSocketCreator()->GetAcceptedSocketData(0); + Recv(pData); + } + else if (GetPrompting()) + { + Msg(eDLL_T::NONE, "Enter []: or : "); + SetPrompting(false); + } } std::this_thread::sleep_for(IntervalToDuration(m_flTickInterval)); + return true; } //----------------------------------------------------------------------------- // Purpose: checks if application should be terminated // Output : true for termination, false otherwise //----------------------------------------------------------------------------- -bool CNetCon::ShouldQuit(void) const +bool CNetCon::GetQuitting(void) const { - return m_bQuitApplication; + return m_bQuitting; +} + +//----------------------------------------------------------------------------- +// Purpose: set whether we should quit +// input : bQuit +//----------------------------------------------------------------------------- +void CNetCon::SetQuitting(const bool bQuit) +{ + m_bQuitting = bQuit; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if we should prompt the connect message +// Output : true for prompting, false otherwise +//----------------------------------------------------------------------------- +bool CNetCon::GetPrompting(void) const +{ + return m_bPromptConnect; +} + +//----------------------------------------------------------------------------- +// Purpose: set whether we should prompt the connect message +// input : bPrompt +//----------------------------------------------------------------------------- +void CNetCon::SetPrompting(const bool bPrompt) +{ + m_bPromptConnect = bPrompt; } //----------------------------------------------------------------------------- @@ -252,11 +345,11 @@ void CNetCon::Disconnect(const char* szReason) szReason = "unknown reason"; } - DevMsg(eDLL_T::CLIENT, "Disconnect: (%s)\n", szReason); - m_Socket.CloseAcceptedSocket(0); + Msg(eDLL_T::CLIENT, "Disconnect: (%s)\n", szReason); + GetSocketCreator()->CloseAcceptedSocket(0); } - m_bPromptConnect = true; + SetPrompting(true); } //----------------------------------------------------------------------------- @@ -295,7 +388,7 @@ bool CNetCon::ProcessMessage(const char* pMsgBuf, const int nMsgLen) } } - DevMsg(eDLL_T::NETCON, "%s", response.responsemsg().c_str()); + Msg(eDLL_T::NETCON, "%s", response.responsemsg().c_str()); break; } case sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG: @@ -360,6 +453,8 @@ bool CNetCon::IsConnected(void) //----------------------------------------------------------------------------- int main(int argc, char* argv[]) { + CheckCPUforSSE2(); + bool bEnableColor = false; for (int i = 0; i < argc; i++) @@ -371,23 +466,25 @@ int main(int argc, char* argv[]) } } - if (!NetConsole()->Init(bEnableColor)) + const char* pHostName = nullptr; + int nPort = SOCKET_ERROR; + + if (argc >= 3) // Get IP and Port from command line. + { + pHostName = argv[1]; + nPort = atoi(argv[2]); + } + + if (!NetConsole()->Init(bEnableColor, pHostName, nPort)) { return EXIT_FAILURE; } - if (argc >= 3) // Get IP and Port from command line. + while (!NetConsole()->GetQuitting()) { - if (!NetConsole()->Connect(argv[1], atoi(argv[2]))) - { - return EXIT_FAILURE; - } - } - - while (!NetConsole()->ShouldQuit()) - { - NetConsole()->UserInput(); - NetConsole()->ClearInput(); + // Run with a reasonable tick rate so we don't eat all the CPU. + std::this_thread::sleep_for( + IntervalToDuration(NetConsole()->GetTickInterval())); } if (!NetConsole()->Shutdown()) diff --git a/r5dev/netconsole/netconsole.h b/r5dev/netconsole/netconsole.h index 2a0926a6..0d77ab48 100644 --- a/r5dev/netconsole/netconsole.h +++ b/r5dev/netconsole/netconsole.h @@ -17,15 +17,21 @@ public: CNetCon(void); ~CNetCon(void); - bool Init(const bool bAnsiColor); + bool Init(const bool bAnsiColor, const char* pHostName = nullptr, const int nPort = SOCKET_ERROR); bool Shutdown(void); - void TermSetup(const bool bAnsiColor); - void UserInput(void); - void ClearInput(void); - void RunFrame(void); - bool ShouldQuit(void) const; + void RunInput(const string& lineInput); + bool RunFrame(void); + + bool GetQuitting(void) const; + void SetQuitting(const bool bQuit); + + bool GetPrompting(void) const; + void SetPrompting(const bool bPrompt); + + inline float GetTickInterval() const { return m_flTickInterval; } + static BOOL WINAPI CloseHandler(DWORD eventCode); virtual void Disconnect(const char* szReason = nullptr); virtual bool ProcessMessage(const char* pMsgBuf, const int nMsgLen) override; @@ -39,13 +45,11 @@ public: private: bool m_bInitialized; - bool m_bQuitApplication; + bool m_bQuitting; bool m_bPromptConnect; float m_flTickInterval; characterset_t m_CharacterSet; - - std::string m_Input; mutable std::mutex m_Mutex; }; diff --git a/r5dev/networksystem/CMakeLists.txt b/r5dev/networksystem/CMakeLists.txt index c379fbe6..70e4a4f4 100644 --- a/r5dev/networksystem/CMakeLists.txt +++ b/r5dev/networksystem/CMakeLists.txt @@ -6,6 +6,8 @@ start_sources() add_sources( SOURCE_GROUP "Private" "bansystem.cpp" "bansystem.h" + "hostmanager.cpp" + "hostmanager.h" "listmanager.cpp" "listmanager.h" "pylon.cpp" diff --git a/r5dev/networksystem/bansystem.cpp b/r5dev/networksystem/bansystem.cpp index 9bd12bf0..2714dd77 100644 --- a/r5dev/networksystem/bansystem.cpp +++ b/r5dev/networksystem/bansystem.cpp @@ -6,6 +6,7 @@ //=====================================================================================// #include "core/stdafx.h" +#include "tier1/strtools.h" #include "engine/net.h" #include "engine/server/server.h" #include "engine/client/client.h" @@ -15,56 +16,67 @@ //----------------------------------------------------------------------------- // Purpose: loads and parses the banned list //----------------------------------------------------------------------------- -void CBanSystem::Load(void) +void CBanSystem::LoadList(void) { if (IsBanListValid()) - m_vBanList.clear(); + m_BannedList.Purge(); - FileHandle_t pFile = FileSystem()->Open("banlist.json", "rt"); + FileHandle_t pFile = FileSystem()->Open("banlist.json", "rt", "PLATFORM"); if (!pFile) return; - uint32_t nLen = FileSystem()->Size(pFile); + const ssize_t nLen = FileSystem()->Size(pFile); std::unique_ptr pBuf(new char[nLen + 1]); - int nRead = FileSystem()->Read(pBuf.get(), nLen, pFile); + const ssize_t nRead = FileSystem()->Read(pBuf.get(), nLen, pFile); FileSystem()->Close(pFile); - pBuf.get()[nRead] = '\0'; // Null terminate the string buffer containing our banned list. + pBuf[nRead] = '\0'; // Null terminate the string buffer containing our banned list. - try + rapidjson::Document document; + if (document.Parse(pBuf.get()).HasParseError()) { - nlohmann::json jsIn = nlohmann::json::parse(pBuf.get()); + Warning(eDLL_T::SERVER, "%s: JSON parse error at position %zu: %s\n", + __FUNCTION__, document.GetErrorOffset(), rapidjson::GetParseError_En(document.GetParseError())); + return; + } - size_t nTotalBans = 0; - if (!jsIn.is_null()) - { - if (!jsIn["totalBans"].is_null()) - nTotalBans = jsIn["totalBans"].get(); - } + if (!document.IsObject()) + { + Warning(eDLL_T::SERVER, "%s: JSON root was not an object\n", __FUNCTION__); + return; + } - for (size_t i = 0; i < nTotalBans; i++) + uint64_t nTotalBans = 0; + if (document.HasMember("totalBans") && document["totalBans"].IsUint64()) + { + nTotalBans = document["totalBans"].GetUint64(); + } + + for (uint64_t i = 0; i < nTotalBans; i++) + { + char idx[64]; _ui64toa(i, idx, 10); + + if (document.HasMember(idx) && document[idx].IsObject()) { - nlohmann::json jsEntry = jsIn[std::to_string(i)]; - if (!jsEntry.is_null()) + const rapidjson::Value& entry = document[idx]; + if (entry.HasMember("ipAddress") && entry["ipAddress"].IsString() && + entry.HasMember("nucleusId") && entry["nucleusId"].IsUint64()) { - string svIpAddress = jsEntry["ipAddress"].get(); - uint64_t nNucleusID = jsEntry["nucleusId"].get(); + Banned_t banned; + banned.m_Address = entry["ipAddress"].GetString(); + banned.m_NucleusID = entry["nucleusId"].GetUint64(); - m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID)); + m_BannedList.AddToTail(banned); } } } - catch (const std::exception& ex) - { - Warning(eDLL_T::SERVER, "%s: Exception while parsing banned list:\n%s\n", __FUNCTION__, ex.what()); - } } //----------------------------------------------------------------------------- // Purpose: saves the banned list //----------------------------------------------------------------------------- -void CBanSystem::Save(void) const +void CBanSystem::SaveList(void) const { FileHandle_t pFile = FileSystem()->Open("banlist.json", "wt", "PLATFORM"); if (!pFile) @@ -73,25 +85,29 @@ void CBanSystem::Save(void) const return; } - try - { - nlohmann::json jsOut; - for (size_t i = 0; i < m_vBanList.size(); i++) - { - jsOut[std::to_string(i)]["ipAddress"] = m_vBanList[i].first; - jsOut[std::to_string(i)]["nucleusId"] = m_vBanList[i].second; - } + rapidjson::Document document; + document.SetObject(); - jsOut["totalBans"] = m_vBanList.size(); - string svJsOut = jsOut.dump(4); + rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); - FileSystem()->Write(svJsOut.data(), int(svJsOut.size()), pFile); - } - catch (const std::exception& ex) + FOR_EACH_VEC(m_BannedList, i) { - Warning(eDLL_T::SERVER, "%s: Exception while parsing banned list:\n%s\n", __FUNCTION__, ex.what()); + const Banned_t& banned = m_BannedList[i]; + char idx[64]; _ui64toa(i, idx, 10); + + rapidjson::Value obj(rapidjson::kObjectType); + obj.AddMember("ipAddress", rapidjson::Value(banned.m_Address.String(), allocator), allocator); + obj.AddMember("nucleusId", banned.m_NucleusID, allocator); + + document.AddMember(rapidjson::Value(idx, allocator), obj, allocator); } + document.AddMember("totalBans", m_BannedList.Count(), allocator); + + rapidjson::StringBuffer buffer; + JSON_DocumentToBufferDeserialize(document, buffer); + + FileSystem()->Write(buffer.GetString(), buffer.GetSize(), pFile); FileSystem()->Close(pFile); } @@ -100,24 +116,22 @@ void CBanSystem::Save(void) const // Input : *ipAddress - // nucleusId - //----------------------------------------------------------------------------- -bool CBanSystem::AddEntry(const char* ipAddress, const uint64_t nucleusId) +bool CBanSystem::AddEntry(const char* ipAddress, const NucleusID_t nucleusId) { Assert(VALID_CHARSTAR(ipAddress)); - const auto idPair = std::make_pair(string(ipAddress), nucleusId); + const Banned_t banned(ipAddress, nucleusId); if (IsBanListValid()) { - auto it = std::find(m_vBanList.begin(), m_vBanList.end(), idPair); - - if (it == m_vBanList.end()) + if (m_BannedList.Find(banned) == m_BannedList.InvalidIndex()) { - m_vBanList.push_back(idPair); + m_BannedList.AddToTail(banned); return true; } } else { - m_vBanList.push_back(idPair); + m_BannedList.AddToTail(banned); return true; } @@ -129,23 +143,22 @@ bool CBanSystem::AddEntry(const char* ipAddress, const uint64_t nucleusId) // Input : *ipAddress - // nucleusId - //----------------------------------------------------------------------------- -bool CBanSystem::DeleteEntry(const char* ipAddress, const uint64_t nucleusId) +bool CBanSystem::DeleteEntry(const char* ipAddress, const NucleusID_t nucleusId) { Assert(VALID_CHARSTAR(ipAddress)); if (IsBanListValid()) { - auto it = std::find_if(m_vBanList.begin(), m_vBanList.end(), - [&](const pair& element) - { - return (strcmp(ipAddress, element.first.c_str()) == NULL - || element.second == nucleusId); - }); - - if (it != m_vBanList.end()) + FOR_EACH_VEC(m_BannedList, i) { - m_vBanList.erase(it); - return true; + const Banned_t& banned = m_BannedList[i]; + + if (banned.m_NucleusID == nucleusId || + banned.m_Address.IsEqual_CaseInsensitive(ipAddress)) + { + m_BannedList.Remove(i); + return true; + } } } @@ -158,21 +171,21 @@ bool CBanSystem::DeleteEntry(const char* ipAddress, const uint64_t nucleusId) // nucleusId - // Output : true if banned, false if not banned //----------------------------------------------------------------------------- -bool CBanSystem::IsBanned(const char* ipAddress, const uint64_t nucleusId) const +bool CBanSystem::IsBanned(const char* ipAddress, const NucleusID_t nucleusId) const { - for (size_t i = 0; i < m_vBanList.size(); i++) + FOR_EACH_VEC(m_BannedList, i) { - const string& bannedIpAddress = m_vBanList[i].first; - const uint64_t bannedNucleusID = m_vBanList[i].second; + const Banned_t& banned = m_BannedList[i]; - if (bannedIpAddress.empty() - || !bannedNucleusID) // Cannot be null. + if (banned.m_NucleusID == NULL || + banned.m_Address.IsEmpty()) { + // Cannot be NULL. continue; } - if (bannedIpAddress.compare(ipAddress) == NULL - || nucleusId == bannedNucleusID) + if (banned.m_NucleusID == nucleusId || + banned.m_Address.IsEqual_CaseInsensitive(ipAddress)) { return true; } @@ -186,7 +199,7 @@ bool CBanSystem::IsBanned(const char* ipAddress, const uint64_t nucleusId) const //----------------------------------------------------------------------------- bool CBanSystem::IsBanListValid(void) const { - return !m_vBanList.empty(); + return !m_BannedList.IsEmpty(); } //----------------------------------------------------------------------------- @@ -247,34 +260,29 @@ void CBanSystem::BanPlayerById(const char* playerHandle, const char* reason) //----------------------------------------------------------------------------- void CBanSystem::UnbanPlayer(const char* criteria) { - try + bool bSave = false; + if (V_IsAllDigit(criteria)) // Check if we have an ip address or nucleus id. { - bool bSave = false; - if (StringIsDigit(criteria)) // Check if we have an ip address or nucleus id. - { - if (DeleteEntry("<>", std::stoll(criteria))) // Delete ban entry. - { - bSave = true; - } - } - else - { - if (DeleteEntry(criteria, 0)) // Delete ban entry. - { - bSave = true; - } - } + char* pEnd = nullptr; + const uint64_t nTargetID = strtoull(criteria, &pEnd, 10); - if (bSave) + if (DeleteEntry("-<[InVaLiD]>-", nTargetID)) // Delete ban entry. { - Save(); // Save modified vector to file. - DevMsg(eDLL_T::SERVER, "Removed '%s' from banned list\n", criteria); + bSave = true; } } - catch (const std::exception& e) + else { - Error(eDLL_T::SERVER, NO_ERROR, "%s - %s\n", __FUNCTION__, e.what()); - return; + if (DeleteEntry(criteria, 0)) // Delete ban entry. + { + bSave = true; + } + } + + if (bSave) + { + SaveList(); // Save modified vector to file. + Msg(eDLL_T::SERVER, "Removed '%s' from banned list\n", criteria); } } @@ -282,7 +290,7 @@ void CBanSystem::UnbanPlayer(const char* criteria) // Purpose: authors player by given name // Input : *playerName - // shouldBan - (only kicks if false) -// *reason - +// *reason - //----------------------------------------------------------------------------- void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan, const char* reason) { @@ -299,7 +307,7 @@ void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan if (!pClient) continue; - CNetChan* pNetChan = pClient->GetNetChan(); + const CNetChan* pNetChan = pClient->GetNetChan(); if (!pNetChan) continue; @@ -318,12 +326,12 @@ void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan if (bSave) { - Save(); - DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerName); + SaveList(); + Msg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerName); } else if (bDisconnect) { - DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerName); + Msg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerName); } } @@ -331,82 +339,157 @@ void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan // Purpose: authors player by given nucleus id or ip address // Input : *playerHandle - // shouldBan - (only kicks if false) -// *reason - +// *reason - //----------------------------------------------------------------------------- void CBanSystem::AuthorPlayerById(const char* playerHandle, const bool shouldBan, const char* reason) { Assert(VALID_CHARSTAR(playerHandle)); - try + bool bOnlyDigits = V_IsAllDigit(playerHandle); + bool bDisconnect = false; + bool bSave = false; + + if (!reason) + reason = shouldBan ? "Banned from server" : "Kicked from server"; + + for (int i = 0; i < g_ServerGlobalVariables->m_nMaxClients; i++) { - bool bOnlyDigits = StringIsDigit(playerHandle); - bool bDisconnect = false; - bool bSave = false; + CClient* pClient = g_pServer->GetClient(i); + if (!pClient) + continue; - if (!reason) - reason = shouldBan ? "Banned from server" : "Kicked from server"; + const CNetChan* pNetChan = pClient->GetNetChan(); + if (!pNetChan) + continue; - for (int i = 0; i < g_ServerGlobalVariables->m_nMaxClients; i++) + if (bOnlyDigits) { - CClient* pClient = g_pServer->GetClient(i); - if (!pClient) - continue; + char* pEnd = nullptr; + const uint64_t nTargetID = strtoull(playerHandle, &pEnd, 10); - CNetChan* pNetChan = pClient->GetNetChan(); - if (!pNetChan) - continue; - - if (bOnlyDigits) + if (nTargetID >= MAX_PLAYERS) // Is it a possible nucleusID? { - uint64_t nTargetID = static_cast(std::stoll(playerHandle)); - if (nTargetID > static_cast(MAX_PLAYERS)) // Is it a possible nucleusID? - { - uint64_t nNucleusID = pClient->GetNucleusID(); - if (nNucleusID != nTargetID) - continue; - } - else // If its not try by handle. - { - uint64_t nClientID = static_cast(pClient->GetHandle()); - if (nClientID != nTargetID) - continue; - } + const NucleusID_t nNucleusID = pClient->GetNucleusID(); - if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) - bSave = true; - - pClient->Disconnect(REP_MARK_BAD, reason); - bDisconnect = true; - } - else - { - if (strcmp(playerHandle, pNetChan->GetAddress()) != NULL) + if (nNucleusID != nTargetID) continue; - - if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) - bSave = true; - - pClient->Disconnect(REP_MARK_BAD, reason); - bDisconnect = true; } - } + else // If its not try by handle. + { + const edict_t nClientID = pClient->GetHandle(); - if (bSave) - { - Save(); - DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerHandle); + if (nClientID != nTargetID) + continue; + } + + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + bSave = true; + + pClient->Disconnect(REP_MARK_BAD, reason); + bDisconnect = true; } - else if (bDisconnect) + else { - DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerHandle); + if (strcmp(playerHandle, pNetChan->GetAddress()) != NULL) + continue; + + if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave) + bSave = true; + + pClient->Disconnect(REP_MARK_BAD, reason); + bDisconnect = true; } } - catch (const std::exception& e) + + if (bSave) { - Error(eDLL_T::SERVER, NO_ERROR, "%s - %s\n", __FUNCTION__, e.what()); - return; + SaveList(); + Msg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerHandle); + } + else if (bDisconnect) + { + Msg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerHandle); } } /////////////////////////////////////////////////////////////////////////////// -CBanSystem* g_pBanSystem = new CBanSystem(); +// Console command handlers +/////////////////////////////////////////////////////////////////////////////// + +static void _Author_Client_f(const CCommand& args, EKickType type) +{ + if (args.ArgC() < 2) + { + return; + } + + const char* szReason = args.ArgC() > 2 ? args.Arg(2) : nullptr; + + switch (type) + { + case KICK_NAME: + { + g_BanSystem.KickPlayerByName(args.Arg(1), szReason); + break; + } + case KICK_ID: + { + g_BanSystem.KickPlayerById(args.Arg(1), szReason); + break; + } + case BAN_NAME: + { + g_BanSystem.BanPlayerByName(args.Arg(1), szReason); + break; + } + case BAN_ID: + { + g_BanSystem.BanPlayerById(args.Arg(1), szReason); + break; + } + default: + { + // Code bug. + Assert(0); + } + } +} +static void Host_Kick_f(const CCommand& args) +{ + _Author_Client_f(args, EKickType::KICK_NAME); +} +static void Host_KickID_f(const CCommand& args) +{ + _Author_Client_f(args, EKickType::KICK_ID); +} +static void Host_Ban_f(const CCommand& args) +{ + _Author_Client_f(args, EKickType::BAN_NAME); +} +static void Host_BanID_f(const CCommand& args) +{ + _Author_Client_f(args, EKickType::BAN_ID); +} +static void Host_Unban_f(const CCommand& args) +{ + if (args.ArgC() < 2) + { + return; + } + + g_BanSystem.UnbanPlayer(args.Arg(1)); +} +static void Host_ReloadBanList_f() +{ + g_BanSystem.LoadList(); // Reload banned list. +} + +static ConCommand kick("kick", Host_Kick_f, "Kick a client from the server by user name", FCVAR_RELEASE, nullptr, "kick \"\""); +static ConCommand kickid("kickid", Host_KickID_f, "Kick a client from the server by handle, nucleus id or ip address", FCVAR_RELEASE, nullptr, "kickid \"\"/\"/\""); +static ConCommand ban("ban", Host_Ban_f, "Bans a client from the server by user name", FCVAR_RELEASE, nullptr, "ban "); +static ConCommand banid("banid", Host_BanID_f, "Bans a client from the server by handle, nucleus id or ip address", FCVAR_RELEASE, nullptr, "banid \"\"/\"/\""); +static ConCommand unban("unban", Host_Unban_f, "Unbans a client from the server by nucleus id or ip address", FCVAR_RELEASE, nullptr, "unban \"\"/\"\""); +static ConCommand reload_banlist("banlist_reload", Host_ReloadBanList_f, "Reloads the banned list", FCVAR_RELEASE); + +/////////////////////////////////////////////////////////////////////////////// +CBanSystem g_BanSystem; diff --git a/r5dev/networksystem/bansystem.h b/r5dev/networksystem/bansystem.h index aad59916..54390093 100644 --- a/r5dev/networksystem/bansystem.h +++ b/r5dev/networksystem/bansystem.h @@ -1,6 +1,5 @@ #pragma once - -typedef vector> BannedVec_t; +#include "ebisusdk/EbisuTypes.h" enum EKickType { @@ -13,13 +12,33 @@ enum EKickType class CBanSystem { public: - void Load(void); - void Save(void) const; + struct Banned_t + { + Banned_t(const char* ipAddress = "", NucleusID_t nucleusId = NULL) + : m_Address(ipAddress) + , m_NucleusID(nucleusId) + {} - bool AddEntry(const char* ipAddress, const uint64_t nucleusId); - bool DeleteEntry(const char* ipAddress, const uint64_t nucleusId); + inline bool operator==(const Banned_t& other) const + { + return m_NucleusID == other.m_NucleusID + && m_Address.IsEqual_CaseInsensitive(other.m_Address); + } - bool IsBanned(const char* ipAddress, const uint64_t nucleusId) const; + NucleusID_t m_NucleusID; + CUtlString m_Address; + }; + + typedef CUtlVector BannedList_t; + +public: + void LoadList(void); + void SaveList(void) const; + + bool AddEntry(const char* ipAddress, const NucleusID_t nucleusId); + bool DeleteEntry(const char* ipAddress, const NucleusID_t nucleusId); + + bool IsBanned(const char* ipAddress, const NucleusID_t nucleusId) const; bool IsBanListValid(void) const; void KickPlayerByName(const char* playerName, const char* reason = nullptr); @@ -34,7 +53,7 @@ private: void AuthorPlayerByName(const char* playerName, const bool bBan, const char* reason = nullptr); void AuthorPlayerById(const char* playerHandle, const bool bBan, const char* reason = nullptr); - BannedVec_t m_vBanList; + BannedList_t m_BannedList; }; -extern CBanSystem* g_pBanSystem; +extern CBanSystem g_BanSystem; diff --git a/r5dev/networksystem/hostmanager.cpp b/r5dev/networksystem/hostmanager.cpp new file mode 100644 index 00000000..94125933 --- /dev/null +++ b/r5dev/networksystem/hostmanager.cpp @@ -0,0 +1,51 @@ +//=============================================================================// +// +// Purpose: server host manager +// +//----------------------------------------------------------------------------- +// +//=============================================================================// +#include "tier0/frametask.h" +#include "rtech/playlists/playlists.h" +#include "engine/cmd.h" +#include "hostmanager.h" + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CServerHostManager::CServerHostManager(void) + : m_HostingStatus(HostStatus_e::NOT_HOSTING) + , m_ServerVisibility(ServerVisibility_e::OFFLINE) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: Launch server with given parameters +//----------------------------------------------------------------------------- +void CServerHostManager::LaunchServer(const bool changeLevel) const +{ + if (!ThreadInMainThread()) + { + g_TaskQueue.Dispatch([this, changeLevel]() + { + this->LaunchServer(changeLevel); + }, 0); + return; + } + + Msg(eDLL_T::ENGINE, "Starting server with name: \"%s\" map: \"%s\" playlist: \"%s\"\n", + m_Server.name.c_str(), m_Server.map.c_str(), m_Server.playlist.c_str()); + + /* + * Playlist gets parsed in two instances, first in Playlists_Parse() with all the necessary + * values. Then when you would normally call launchplaylist which calls StartPlaylist it would cmd + * call mp_gamemode which parses the gamemode specific part of the playlist.. + */ + v_Playlists_Parse(m_Server.playlist.c_str()); + mp_gamemode->SetValue(m_Server.playlist.c_str()); + + const string command = Format("%s \"%s\"", changeLevel ? "changelevel" : "map", m_Server.map.c_str()); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), command.c_str(), cmd_source_t::kCommandSrcCode); +} + +CServerHostManager g_ServerHostManager; diff --git a/r5dev/networksystem/hostmanager.h b/r5dev/networksystem/hostmanager.h new file mode 100644 index 00000000..330cad61 --- /dev/null +++ b/r5dev/networksystem/hostmanager.h @@ -0,0 +1,55 @@ +#ifndef HOSTMANAGER_H +#define HOSTMANAGER_H +#include + +enum HostStatus_e +{ + NOT_HOSTING, + HOSTING +}; + +enum ServerVisibility_e +{ + OFFLINE, + HIDDEN, + PUBLIC +}; + +class CServerHostManager +{ +public: + CServerHostManager(); + + void LaunchServer(const bool changeLevel) const; + + inline HostStatus_e GetHostStatus(void) const { return m_HostingStatus; } + inline void SetHostStatus(const HostStatus_e hostStatus) { m_HostingStatus = hostStatus; } + + inline ServerVisibility_e GetVisibility(void) const { return m_ServerVisibility; } + inline void SetVisibility(const ServerVisibility_e visibility) { m_ServerVisibility = visibility; } + + inline NetGameServer_t& GetDetails() { return m_Server; } + + inline void SetCurrentToken(const string& token) { m_Token = token; } + inline const string& GetCurrentToken() const { return m_Token; } + + inline void SetCurrentError(const string& error) { m_ErrorMsg = error; } + inline const string& GetCurrentError() const { return m_ErrorMsg; } + + inline void SetHostIP(const string& ip) { m_HostIP = ip; }; + inline const string& GetHostIP() const { return m_HostIP; }; + +private: + HostStatus_e m_HostingStatus; + ServerVisibility_e m_ServerVisibility; + + NetGameServer_t m_Server; + + string m_Token; + string m_ErrorMsg; + string m_HostIP; +}; + +extern CServerHostManager g_ServerHostManager; + +#endif // HOSTMANAGER_H diff --git a/r5dev/networksystem/listmanager.cpp b/r5dev/networksystem/listmanager.cpp index 8a508990..14f4dcba 100644 --- a/r5dev/networksystem/listmanager.cpp +++ b/r5dev/networksystem/listmanager.cpp @@ -1,6 +1,6 @@ //=============================================================================// -// -// Purpose: +// +// Purpose: server list manager // //----------------------------------------------------------------------------- // @@ -14,7 +14,7 @@ #include "engine/net.h" #include "engine/host_state.h" #include "engine/server/server.h" -#include "vpc/keyvalues.h" +#include "rtech/playlists/playlists.h" #include "pylon.h" #include "listmanager.h" @@ -22,25 +22,30 @@ // Purpose: //----------------------------------------------------------------------------- CServerListManager::CServerListManager(void) - : m_HostingStatus(EHostStatus_t::NOT_HOSTING) - , m_ServerVisibility(EServerVisibility_t::OFFLINE) { } //----------------------------------------------------------------------------- // Purpose: get server list from pylon -// Input : &svMessage - -// Output : amount of servers found +// Input : &outMessage - +// &numServers - +// Output : true on success, false otherwise //----------------------------------------------------------------------------- -size_t CServerListManager::RefreshServerList(string& svMessage) +bool CServerListManager::RefreshServerList(string& outMessage, size_t& numServers) { ClearServerList(); - vector vServerList = g_pMasterServer->GetServerList(svMessage); - std::lock_guard l(m_Mutex); - m_vServerList = vServerList; + vector serverList; + const bool success = g_MasterServer.GetServerList(serverList, outMessage); - return m_vServerList.size(); + if (!success) + return false; + + AUTO_LOCK(m_Mutex); + m_vServerList = serverList; + + numServers = m_vServerList.size(); + return true; } //----------------------------------------------------------------------------- @@ -48,51 +53,23 @@ size_t CServerListManager::RefreshServerList(string& svMessage) //----------------------------------------------------------------------------- void CServerListManager::ClearServerList(void) { - std::lock_guard l(m_Mutex); + AUTO_LOCK(m_Mutex); m_vServerList.clear(); } -//----------------------------------------------------------------------------- -// Purpose: Launch server with given parameters -//----------------------------------------------------------------------------- -void CServerListManager::LaunchServer(const bool bChangeLevel) const -{ - if (!ThreadInMainThread()) - { - g_TaskScheduler->Dispatch([this, bChangeLevel]() - { - this->LaunchServer(bChangeLevel); - }, 0); - return; - } - - DevMsg(eDLL_T::ENGINE, "Starting server with name: \"%s\" map: \"%s\" playlist: \"%s\"\n", - m_Server.m_svHostName.c_str(), m_Server.m_svHostMap.c_str(), m_Server.m_svPlaylist.c_str()); - - /* - * Playlist gets parsed in two instances, first in KeyValues::ParsePlaylists with all the necessary - * values. Then when you would normally call launchplaylist which calls StartPlaylist it would cmd - * call mp_gamemode which parses the gamemode specific part of the playlist.. - */ - KeyValues::ParsePlaylists(m_Server.m_svPlaylist.c_str()); - mp_gamemode->SetValue(m_Server.m_svPlaylist.c_str()); - - ProcessCommand(Format("%s \"%s\"", bChangeLevel ? "changelevel" : "map", m_Server.m_svHostMap.c_str()).c_str()); -} - //----------------------------------------------------------------------------- // Purpose: connects to specified server // Input : &svIp - -// &svPort - +// nPort - // &svNetKey - //----------------------------------------------------------------------------- -void CServerListManager::ConnectToServer(const string& svIp, const string& svPort, const string& svNetKey) const +void CServerListManager::ConnectToServer(const string& svIp, const int nPort, const string& svNetKey) const { if (!ThreadInMainThread()) { - g_TaskScheduler->Dispatch([this, svIp, svPort, svNetKey]() + g_TaskQueue.Dispatch([this, svIp, nPort, svNetKey]() { - this->ConnectToServer(svIp, svPort, svNetKey); + this->ConnectToServer(svIp, nPort, svNetKey); }, 0); return; } @@ -101,7 +78,9 @@ void CServerListManager::ConnectToServer(const string& svIp, const string& svPor { NET_SetKey(svNetKey); } - ProcessCommand(Format("%s \"[%s]:%s\"", "connect", svIp.c_str(), svPort.c_str()).c_str()); + + const string command = Format("%s \"[%s]:%i\"", "connect", svIp.c_str(), nPort); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), command.c_str(), cmd_source_t::kCommandSrcCode); } //----------------------------------------------------------------------------- @@ -113,7 +92,7 @@ void CServerListManager::ConnectToServer(const string& svServer, const string& s { if (!ThreadInMainThread()) { - g_TaskScheduler->Dispatch([this, svServer, svNetKey]() + g_TaskQueue.Dispatch([this, svServer, svNetKey]() { this->ConnectToServer(svServer, svNetKey); }, 0); @@ -124,17 +103,9 @@ void CServerListManager::ConnectToServer(const string& svServer, const string& s { NET_SetKey(svNetKey); } - ProcessCommand(Format("%s \"%s\"", "connect", svServer.c_str()).c_str()); + + const string command = Format("%s \"%s\"", "connect", svServer.c_str()).c_str(); + Cbuf_AddText(Cbuf_GetCurrentPlayer(), command.c_str(), cmd_source_t::kCommandSrcCode); } -//----------------------------------------------------------------------------- -// Purpose: executes submitted commands in a separate thread -// Input : *pszCommand - -//----------------------------------------------------------------------------- -void CServerListManager::ProcessCommand(const char* pszCommand) const -{ - Cbuf_AddText(Cbuf_GetCurrentPlayer(), pszCommand, cmd_source_t::kCommandSrcCode); - //g_TaskScheduler->Dispatch(Cbuf_Execute, 0); // Run in main thread. -} - -CServerListManager* g_pServerListManager = new CServerListManager(); \ No newline at end of file +CServerListManager g_ServerListManager; diff --git a/r5dev/networksystem/listmanager.h b/r5dev/networksystem/listmanager.h index 73fc78bc..843564fb 100644 --- a/r5dev/networksystem/listmanager.h +++ b/r5dev/networksystem/listmanager.h @@ -2,41 +2,21 @@ #define LISTMANAGER_H #include -enum EHostStatus_t -{ - NOT_HOSTING, - HOSTING -}; - -enum EServerVisibility_t -{ - OFFLINE, - HIDDEN, - PUBLIC -}; - class CServerListManager { public: CServerListManager(); - size_t RefreshServerList(string& svMessage); + bool RefreshServerList(string& outMessage, size_t& numServers); void ClearServerList(void); - void LaunchServer(const bool bChangeLevel) const; - void ConnectToServer(const string& svIp, const string& svPort, const string& svNetKey) const; + void ConnectToServer(const string& svIp, const int nPort, const string& svNetKey) const; void ConnectToServer(const string& svServer, const string& svNetKey) const; - void ProcessCommand(const char* pszCommand) const; - - EHostStatus_t m_HostingStatus; - EServerVisibility_t m_ServerVisibility; - - NetGameServer_t m_Server; + // TODO: make private! vector m_vServerList; - - mutable std::mutex m_Mutex; + mutable CThreadFastMutex m_Mutex; }; -extern CServerListManager* g_pServerListManager; +extern CServerListManager g_ServerListManager; #endif // LISTMANAGER_H diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 56825a25..57a53940 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -1,9 +1,9 @@ -//=====================================================================================// +//=============================================================================// // -// Purpose: Implementation of the pylon server backend. +// Purpose: Implementation of the pylon client. // // $NoKeywords: $ -//=====================================================================================// +//=============================================================================// #include #include @@ -11,66 +11,101 @@ #include #include +//----------------------------------------------------------------------------- +// Console variables +//----------------------------------------------------------------------------- +ConVar pylon_matchmaking_hostname("pylon_matchmaking_hostname", "ms.r5reloaded.com", FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS, "Holds the pylon matchmaking hostname"); +ConVar pylon_host_update_interval("pylon_host_update_interval", "5", FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS, "Length of time in seconds between each status update interval to master server", true, 5.f, false, 0.f); +ConVar pylon_showdebuginfo("pylon_showdebuginfo", "0", FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS, "Shows debug output for pylon"); + +//----------------------------------------------------------------------------- +// Purpose: checks if the server listing fields are valid. +// Input : &value - +// Output : true on success, false on failure. +//----------------------------------------------------------------------------- +static bool IsServerListingValid(const rapidjson::Value& value) +{ + if (value.HasMember("name") && value["name"].IsString() && + value.HasMember("description") && value["description"].IsString() && + value.HasMember("hidden") && value["hidden"].IsBool() && + value.HasMember("map") && value["map"].IsString() && + value.HasMember("playlist") && value["playlist"].IsString() && + value.HasMember("ip") && value["ip"].IsString() && + value.HasMember("port") && value["port"].IsInt() && + value.HasMember("key") && value["key"].IsString() && + value.HasMember("checksum") && value["checksum"].IsUint() && + value.HasMember("numPlayers") && value["numPlayers"].IsInt() && + value.HasMember("maxPlayers") && value["maxPlayers"].IsInt()) + { + return true; + } + + return false; +} + //----------------------------------------------------------------------------- // Purpose: gets a vector of hosted servers. // Input : &outMessage - -// Output : vector +// Output : true on success, false on failure. //----------------------------------------------------------------------------- -vector CPylon::GetServerList(string& outMessage) const +bool CPylon::GetServerList(vector& outServerList, string& outMessage) const { - vector vecServers; + rapidjson::Document requestJson; + requestJson.SetObject(); + requestJson.AddMember("version", SDK_VERSION, requestJson.GetAllocator()); - nlohmann::json requestJson = nlohmann::json::object(); - requestJson["version"] = SDK_VERSION; + rapidjson::StringBuffer stringBuffer; + JSON_DocumentToBufferDeserialize(requestJson, stringBuffer); - nlohmann::json responseJson; + rapidjson::Document responseJson; CURLINFO status; if (!SendRequest("/servers", requestJson, responseJson, outMessage, status, "server list error")) { - return vecServers; + return false; } - if (!responseJson.contains("servers")) + if (!responseJson.HasMember("servers")) { outMessage = Format("Invalid response with status: %d", int(status)); - return vecServers; + return false; } - try + const rapidjson::Value& servers = responseJson["servers"]; + + for (rapidjson::Value::ConstValueIterator itr = servers.Begin(); + itr != servers.End(); ++itr) { - for (auto& obj : responseJson["servers"]) + const rapidjson::Value& obj = *itr; + + if (!IsServerListingValid(obj)) { - vecServers.push_back( - NetGameServer_t - { - obj.value("name",""), - obj.value("description",""), - obj.value("hidden","false") == "true", - obj.value("map",""), - obj.value("playlist",""), - obj.value("ip",""), - obj.value("port", ""), - obj.value("key",""), - obj.value("checksum",""), - obj.value("version", SDK_VERSION), - obj.value("playerCount", ""), - obj.value("maxPlayers", ""), - obj.value("timeStamp", 0), - obj.value("publicRef", ""), - obj.value("cachedId", ""), - } - ); + // Missing details; skip this server listing. + continue; } - } - catch (const std::exception& ex) - { - Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); - vecServers.clear(); // Clear as the vector may be partially filled. + + outServerList.push_back( + NetGameServer_t + { + obj["name"].GetString(), + obj["description"].GetString(), + obj["hidden"].GetBool(), + obj["map"].GetString(), + obj["playlist"].GetString(), + obj["ip"].GetString(), + obj["port"].GetInt(), + obj["key"].GetString(), + obj["checksum"].GetUint(), + SDK_VERSION, + obj["numPlayers"].GetInt(), + obj["maxPlayers"].GetInt(), + -1, + } + ); } - return vecServers; + return true; } //----------------------------------------------------------------------------- @@ -83,10 +118,14 @@ vector CPylon::GetServerList(string& outMessage) const bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, string& outMessage, const string& token) const { - nlohmann::json requestJson = nlohmann::json::object(); - requestJson["token"] = token; + rapidjson::Document requestJson; + requestJson.SetObject(); - nlohmann::json responseJson; + rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator(); + requestJson.AddMember("version", rapidjson::Value(SDK_VERSION, requestJson.GetAllocator()), allocator); + requestJson.AddMember("token", rapidjson::Value(token.c_str(), requestJson.GetAllocator()), allocator); + + rapidjson::Document responseJson; CURLINFO status; if (!SendRequest("/server/byToken", requestJson, responseJson, @@ -95,42 +134,38 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, return false; } - if (!responseJson.contains("server")) + if (!responseJson.HasMember("server")) { outMessage = Format("Invalid response with status: %d", int(status)); return false; } - try - { - nlohmann::json& serverJson = responseJson["server"]; - outGameServer = NetGameServer_t - { - serverJson.value("name",""), - serverJson.value("description",""), - serverJson.value("hidden","false") == "true", - serverJson.value("map",""), - serverJson.value("playlist",""), - serverJson.value("ip",""), - serverJson.value("port", ""), - serverJson.value("key",""), - serverJson.value("checksum",""), - serverJson.value("version", SDK_VERSION), - serverJson.value("playerCount", ""), - serverJson.value("maxPlayers", ""), - serverJson.value("timeStamp", 0), - serverJson.value("publicRef", ""), - serverJson.value("cachedId", ""), - }; + const rapidjson::Value& serverJson = responseJson["server"]; - return true; - } - catch (const std::exception& ex) + if (!IsServerListingValid(serverJson)) { - Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); + outMessage = Format("Invalid server listing data!"); + return false; } - return false; + outGameServer = NetGameServer_t + { + serverJson["name"].GetString(), + serverJson["description"].GetString(), + serverJson["hidden"].GetBool(), + serverJson["map"].GetString(), + serverJson["playlist"].GetString(), + serverJson["ip"].GetString(), + serverJson["port"].GetInt(), + serverJson["key"].GetString(), + serverJson["checksum"].GetUint(), + SDK_VERSION, + serverJson["numPlayers"].GetInt(), + serverJson["maxPlayers"].GetInt(), + -1, + }; + + return true; } //----------------------------------------------------------------------------- @@ -140,48 +175,52 @@ bool CPylon::GetServerByToken(NetGameServer_t& outGameServer, // &netGameServer - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- -bool CPylon::PostServerHost(string& outMessage, string& outToken, - const NetGameServer_t& netGameServer) const +bool CPylon::PostServerHost(string& outMessage, string& outToken, string& outHostIp, const NetGameServer_t& netGameServer) const { - nlohmann::json requestJson = nlohmann::json::object(); - requestJson["name"] = netGameServer.m_svHostName; - requestJson["description"] = netGameServer.m_svDescription; - requestJson["hidden"] = netGameServer.m_bHidden; - requestJson["map"] = netGameServer.m_svHostMap; - requestJson["playlist"] = netGameServer.m_svPlaylist; - requestJson["ip"] = netGameServer.m_svIpAddress; - requestJson["port"] = netGameServer.m_svGamePort; - requestJson["key"] = netGameServer.m_svEncryptionKey; - requestJson["checksum"] = netGameServer.m_svRemoteChecksum; - requestJson["version"] = netGameServer.m_svSDKVersion; - requestJson["playerCount"] = netGameServer.m_svPlayerCount; - requestJson["maxPlayers"] = netGameServer.m_svMaxPlayers; - requestJson["timeStamp"] = netGameServer.m_nTimeStamp; - requestJson["publicRef"] = netGameServer.m_svPublicRef; - requestJson["cachedId"] = netGameServer.m_svCachedId; + rapidjson::Document requestJson; + requestJson.SetObject(); - nlohmann::json responseJson; + rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator(); + + requestJson.AddMember("name", rapidjson::Value(netGameServer.name.c_str(), allocator), allocator); + requestJson.AddMember("description", rapidjson::Value(netGameServer.description.c_str(), allocator), allocator); + requestJson.AddMember("hidden", netGameServer.hidden, allocator); + requestJson.AddMember("map", rapidjson::Value(netGameServer.map.c_str(), allocator), allocator); + requestJson.AddMember("playlist", rapidjson::Value(netGameServer.playlist.c_str(), allocator), allocator); + requestJson.AddMember("ip", rapidjson::Value(netGameServer.address.c_str(), allocator), allocator); + requestJson.AddMember("port", netGameServer.port, allocator); + requestJson.AddMember("key", rapidjson::Value(netGameServer.netKey.c_str(), allocator), allocator); + requestJson.AddMember("checksum", netGameServer.checksum, allocator); + requestJson.AddMember("version", rapidjson::Value(netGameServer.versionId.c_str(), allocator), allocator); + requestJson.AddMember("numPlayers", netGameServer.numPlayers, allocator); + requestJson.AddMember("maxPlayers", netGameServer.maxPlayers, allocator); + requestJson.AddMember("timeStamp", netGameServer.timeStamp, allocator); + + rapidjson::Document responseJson; CURLINFO status; - if (!SendRequest("/servers/add", requestJson, responseJson, - outMessage, status, "server host error")) + if (!SendRequest("/servers/add", requestJson, responseJson, outMessage, status, "server host error")) { return false; } - if (netGameServer.m_bHidden) + if (netGameServer.hidden) { - nlohmann::json& tokenJson = responseJson["token"]; - - if (!tokenJson.is_string()) + if (!responseJson.HasMember("token") || !responseJson["token"].IsString()) { outMessage = Format("Invalid response with status: %d", int(status)); outToken.clear(); - return false; } - outToken = tokenJson.get(); + outToken = responseJson["token"].GetString(); + } + + if (responseJson.HasMember("ip") && responseJson["ip"].IsString() && + responseJson.HasMember("port") && responseJson["port"].IsInt()) + { + outHostIp = Format("[%s]:%i", + responseJson["ip"].GetString(), responseJson["port"].GetInt()); } return true; @@ -193,56 +232,54 @@ bool CPylon::PostServerHost(string& outMessage, string& outToken, // &outBannedVec - // Output : True on success, false otherwise. //----------------------------------------------------------------------------- -bool CPylon::GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const +bool CPylon::GetBannedList(const CBanSystem::BannedList_t& inBannedVec, CBanSystem::BannedList_t& outBannedVec) const { - nlohmann::json arrayJson = nlohmann::json::array(); + rapidjson::Document requestJson; + requestJson.SetObject(); - for (const auto& bannedPair : inBannedVec) + rapidjson::Value playersArray(rapidjson::kArrayType); + + rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator(); + + FOR_EACH_VEC(inBannedVec, i) { - nlohmann::json player; - player["id"] = bannedPair.second; - player["ip"] = bannedPair.first; - arrayJson.push_back(player); + const CBanSystem::Banned_t& banned = inBannedVec[i]; + + rapidjson::Value player(rapidjson::kObjectType); + player.AddMember("id", banned.m_NucleusID, allocator); + player.AddMember("ip", rapidjson::Value(banned.m_Address.String(), allocator), allocator); + playersArray.PushBack(player, allocator); } - nlohmann::json playerArray; - playerArray["players"] = arrayJson; + requestJson.AddMember("players", playersArray, allocator); + + rapidjson::Document responseJson; string outMessage; CURLINFO status; - if (!SendRequest("/banlist/bulkCheck", playerArray, - arrayJson, outMessage, status, "banned bulk check error")) + if (!SendRequest("/banlist/bulkCheck", requestJson, responseJson, outMessage, status, "banned bulk check error")) { return false; } - if (!arrayJson.contains("bannedPlayers")) + if (!responseJson.HasMember("bannedPlayers") || !responseJson["bannedPlayers"].IsArray()) { outMessage = Format("Invalid response with status: %d", int(status)); return false; } - try + const rapidjson::Value& bannedPlayers = responseJson["bannedPlayers"]; + for (const rapidjson::Value& obj : bannedPlayers.GetArray()) { - for (auto& obj : arrayJson["bannedPlayers"]) - { - outBannedVec.push_back( - std::make_pair( - obj.value("reason", "#DISCONNECT_BANNED"), - obj.value("id", uint64_t(0)) - ) - ); - } - - return true; - } - catch (const std::exception& ex) - { - Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); + CBanSystem::Banned_t banned( + obj.HasMember("reason") ? obj["reason"].GetString() : "#DISCONNECT_BANNED", + obj.HasMember("id") && obj["id"].IsUint64() ? obj["id"].GetUint64() : NucleusID_t(NULL) + ); + outBannedVec.AddToTail(banned); } - return false; + return true; } //----------------------------------------------------------------------------- @@ -252,41 +289,143 @@ bool CPylon::GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBanne // &outReason - <- contains banned reason if any. // Output : True if banned, false if not banned. //----------------------------------------------------------------------------- -bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, - const string& personaName, string& outReason) const +bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, const string& personaName, string& outReason) const { - nlohmann::json requestJson = nlohmann::json::object(); - requestJson["name"] = personaName; - requestJson["id"] = nucleusId; - requestJson["ip"] = ipAddress; + rapidjson::Document requestJson; + requestJson.SetObject(); - nlohmann::json responseJson; + rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator(); + + requestJson.AddMember("name", rapidjson::Value(personaName.c_str(), allocator), allocator); + requestJson.AddMember("id", nucleusId, allocator); + requestJson.AddMember("ip", rapidjson::Value(ipAddress.c_str(), allocator), allocator); + + rapidjson::Document responseJson; string outMessage; CURLINFO status; - if (!SendRequest("/banlist/isBanned", requestJson, - responseJson, outMessage, status, "banned check error")) + if (!SendRequest("/banlist/isBanned", requestJson, responseJson, outMessage, status, "banned check error")) { return false; } - try + if (responseJson.HasMember("banned") && responseJson["banned"].IsBool()) { - if (responseJson["banned"].is_boolean() && - responseJson["banned"].get()) + if (responseJson["banned"].GetBool()) { - outReason = responseJson.value("reason", "#DISCONNECT_BANNED"); + outReason = responseJson.HasMember("reason") ? responseJson["reason"].GetString() : "#DISCONNECT_BANNED"; return true; } } - catch (const std::exception& ex) + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: authenticate for 'this' particular connection. +// Input : nucleusId - +// *ipAddress - +// *authCode - +// &outToken - +// &outMessage - +// Output : true on success, false on failure. +//----------------------------------------------------------------------------- +bool CPylon::AuthForConnection(const uint64_t nucleusId, const char* ipAddress, + const char* authCode, string& outToken, string& outMessage) const +{ + rapidjson::Document requestJson; + requestJson.SetObject(); + + rapidjson::Document::AllocatorType& allocator = requestJson.GetAllocator(); + + requestJson.AddMember("id", nucleusId, allocator); + requestJson.AddMember("ip", rapidjson::Value(ipAddress, allocator), allocator); + requestJson.AddMember("code", rapidjson::Value(authCode, allocator), allocator); + + rapidjson::Document responseJson; + + CURLINFO status; + + if (!SendRequest("/client/authenticate", requestJson, responseJson, outMessage, status, "origin auth error")) { - Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what()); + return false; + } + + if (responseJson.HasMember("token") && responseJson["token"].IsString()) + { + outToken = responseJson["token"].GetString(); + return true; } return false; } +//----------------------------------------------------------------------------- +// Purpose: checks if the EULA response fields are valid. +// Input : &doc - +// Output : true on success, false on failure. +//----------------------------------------------------------------------------- +static bool ValidateEULAData(const rapidjson::Document& doc) +{ + if (!doc.HasMember("data") || !doc["data"].IsObject()) + return false; + + const rapidjson::Value& data = doc["data"]; + + if (!data.HasMember("version") || !data["version"].IsInt()) + return false; + + if (!data.HasMember("lang") || !data["lang"].IsString()) + return false; + + if (!data.HasMember("contents") || !data["contents"].IsString()) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: checks if the accepted EULA is up to date. +// Output : true on success, false on failure. +//----------------------------------------------------------------------------- +static bool IsEULAUpToDate() +{ + return (eula_version_accepted->GetInt() == eula_version->GetInt()); +} + +//----------------------------------------------------------------------------- +// Purpose: Gets the EULA from master server. +// Input : &outData - +// &outMessage - +// Output : True on success, false on failure. +//----------------------------------------------------------------------------- +bool CPylon::GetEULA(MSEulaData_t& outData, string& outMessage) const +{ + rapidjson::Document requestJson; + requestJson.SetObject(); + + rapidjson::Document responseJson; + CURLINFO status; + + if (!SendRequest("/eula", requestJson, responseJson, outMessage, status, "eula fetch error", false)) + { + return false; + } + + if (!ValidateEULAData(responseJson)) + { + return false; + } + + const rapidjson::Value& data = responseJson["data"]; + + outData.version = data["version"].GetInt(); + outData.language = data["lang"].GetString(); + outData.contents = data["contents"].GetString(); + + return true; +} + //----------------------------------------------------------------------------- // Purpose: Sends request to Pylon Master Server. // Input : *endpoint - @@ -294,45 +433,66 @@ bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId, // &responseJson - // &outMessage - // &status - +// checkEula - // Output : True on success, false on failure. //----------------------------------------------------------------------------- -bool CPylon::SendRequest(const char* endpoint, const nlohmann::json& requestJson, - nlohmann::json& responseJson, string& outMessage, CURLINFO& status, const char* errorText) const +bool CPylon::SendRequest(const char* endpoint, const rapidjson::Document& requestJson, + rapidjson::Document& responseJson, string& outMessage, CURLINFO& status, + const char* errorText, const bool checkEula) const { - string responseBody; + if (!IsDedicated() && !IsEULAUpToDate() && checkEula) + { + outMessage = "EULA not accepted"; + return false; + } - if (!QueryServer(endpoint, requestJson.dump(4).c_str(), responseBody, outMessage, status)) + rapidjson::StringBuffer stringBuffer; + JSON_DocumentToBufferDeserialize(requestJson, stringBuffer); + + string responseBody; + if (!QueryServer(endpoint, stringBuffer.GetString(), responseBody, outMessage, status)) { return false; } - try + if (status == 200) // STATUS_OK { - if (status == 200) // STATUS_OK - { - responseJson = nlohmann::json::parse(responseBody); - LogBody(responseJson); + responseJson.Parse(responseBody.c_str()); - if (responseJson["success"].is_boolean() && - responseJson["success"].get()) - { - return true; - } - else - { - ExtractError(responseJson, outMessage, status); - return false; - } + if (responseJson.HasParseError()) + { + Warning(eDLL_T::ENGINE, "%s: JSON parse error at position %zu: %s\n", __FUNCTION__, + responseJson.GetErrorOffset(), rapidjson::GetParseError_En(responseJson.GetParseError())); + + return false; + } + + if (!responseJson.IsObject()) + { + Warning(eDLL_T::ENGINE, "%s: JSON root was not an object\n", __FUNCTION__); + return false; + } + + if (pylon_showdebuginfo.GetBool()) + { + LogBody(responseJson); + } + + if (responseJson.HasMember("success") && + responseJson["success"].IsBool() && + responseJson["success"].GetBool()) + { + return true; } else { - ExtractError(responseBody, outMessage, status, errorText); + ExtractError(responseJson, outMessage, status); return false; } } - catch (const std::exception& ex) + else { - Warning(eDLL_T::ENGINE, "%s - Exception while parsing response:\n%s\n", __FUNCTION__, ex.what()); + ExtractError(responseBody, outMessage, status, errorText); return false; } } @@ -349,24 +509,25 @@ bool CPylon::SendRequest(const char* endpoint, const nlohmann::json& requestJson bool CPylon::QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const { - const bool showDebug = pylon_showdebuginfo->GetBool(); - const char* hostName = pylon_matchmaking_hostname->GetString(); + const bool showDebug = pylon_showdebuginfo.GetBool(); + const char* hostName = pylon_matchmaking_hostname.GetString(); if (showDebug) { - DevMsg(eDLL_T::ENGINE, "Sending request to '%s' with endpoint '%s':\n%s\n", + Msg(eDLL_T::ENGINE, "Sending request to '%s' with endpoint '%s':\n%s\n", hostName, endpoint, request); } string finalUrl; CURLFormatUrl(finalUrl, hostName, endpoint); + finalUrl += Format("?language=%s", this->GetLanguage().c_str()); CURLParams params; params.writeFunction = CURLWriteStringCallback; - params.timeout = curl_timeout->GetInt(); - params.verifyPeer = ssl_verify_peer->GetBool(); - params.verbose = curl_debug->GetBool(); + params.timeout = curl_timeout.GetInt(); + params.verifyPeer = ssl_verify_peer.GetBool(); + params.verbose = curl_debug.GetBool(); curl_slist* sList = nullptr; CURL* curl = CURLInitRequest(finalUrl.c_str(), request, outResponse, sList, params); @@ -386,7 +547,7 @@ bool CPylon::QueryServer(const char* endpoint, const char* request, if (showDebug) { - DevMsg(eDLL_T::ENGINE, "Host '%s' replied with status: '%d'\n", + Msg(eDLL_T::ENGINE, "Host '%s' replied with status: '%d'\n", hostName, outStatus); } @@ -400,12 +561,13 @@ bool CPylon::QueryServer(const char* endpoint, const char* request, // status - // *errorText - //----------------------------------------------------------------------------- -void CPylon::ExtractError(const nlohmann::json& resultJson, string& outMessage, +void CPylon::ExtractError(const rapidjson::Document& resultJson, string& outMessage, CURLINFO status, const char* errorText) const { - if (resultJson["error"].is_string()) + if (resultJson.IsObject() && resultJson.HasMember("error") && + resultJson["error"].IsString()) { - outMessage = resultJson["error"].get(); + outMessage = resultJson["error"].GetString(); } else { @@ -431,7 +593,9 @@ void CPylon::ExtractError(const string& response, string& outMessage, { if (!response.empty()) { - nlohmann::json resultBody = nlohmann::json::parse(response); + rapidjson::Document resultBody; + resultBody.Parse(response.c_str()); + ExtractError(resultBody, outMessage, status, errorText); } else if (status) @@ -449,14 +613,13 @@ void CPylon::ExtractError(const string& response, string& outMessage, // Purpose: Logs the response body if debug is enabled. // Input : &responseJson - //----------------------------------------------------------------------------- -void CPylon::LogBody(const nlohmann::json& responseJson) const +void CPylon::LogBody(const rapidjson::Document& responseJson) const { - if (pylon_showdebuginfo->GetBool()) - { - const string responseBody = responseJson.dump(4); - DevMsg(eDLL_T::ENGINE, "\n%s\n", responseBody.c_str()); - } + rapidjson::StringBuffer stringBuffer; + + JSON_DocumentToBufferDeserialize(responseJson, stringBuffer); + Msg(eDLL_T::ENGINE, "\n%s\n", stringBuffer.GetString()); } /////////////////////////////////////////////////////////////////////////////// -CPylon* g_pMasterServer(new CPylon()); +CPylon g_MasterServer; diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index b7b7d1c9..e208bf75 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -2,32 +2,55 @@ #include "thirdparty/curl/include/curl/curl.h" #include "bansystem.h" #include "serverlisting.h" +#include "localize/ilocalize.h" + +extern ConVar pylon_matchmaking_hostname; +extern ConVar pylon_host_update_interval; +extern ConVar pylon_showdebuginfo; + +struct MSEulaData_t +{ + int version; + string language; + string contents; +}; class CPylon { public: - vector GetServerList(string& outMessage) const; - bool GetServerByToken(NetGameServer_t& slOutServer, string& outMessage, const string& svToken) const; - bool PostServerHost(string& outMessage, string& svOutToken, const NetGameServer_t& netGameServer) const; + CPylon() { SetLanguage(g_LanguageNames[0]); } - bool GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const; + bool GetServerList(vector& outServerList, string& outMessage) const; + bool GetServerByToken(NetGameServer_t& slOutServer, string& outMessage, const string& svToken) const; + bool PostServerHost(string& outMessage, string& svOutToken, string& outHostIp, const NetGameServer_t& netGameServer) const; + + bool GetBannedList(const CBanSystem::BannedList_t& inBannedVec, CBanSystem::BannedList_t& outBannedVec) const; bool CheckForBan(const string& ipAddress, const uint64_t nucleusId, const string& personaName, string& outReason) const; - void ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText = nullptr) const; + bool AuthForConnection(const uint64_t nucleusId, const char* ipAddress, const char* authCode, string& outToken, string& outMessage) const; + + bool GetEULA(MSEulaData_t& outData, string& outMessage) const; + + void ExtractError(const rapidjson::Document& resultBody, string& outMessage, CURLINFO status, const char* errorText = nullptr) const; void ExtractError(const string& response, string& outMessage, CURLINFO status, const char* messageText = nullptr) const; - void LogBody(const nlohmann::json& responseJson) const; - bool SendRequest(const char* endpoint, const nlohmann::json& requestJson, nlohmann::json& responseJson, string& outMessage, CURLINFO& status, const char* errorText = nullptr) const; + void LogBody(const rapidjson::Document& responseJson) const; + bool SendRequest(const char* endpoint, const rapidjson::Document& requestJson, rapidjson::Document& responseJson, string& outMessage, CURLINFO& status, const char* errorText = nullptr, const bool checkEula = true) const; bool QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const; - inline const string& GetCurrentToken() const { return m_Token; } - inline const string& GetCurrentError() const { return m_ErrorMsg; } - - inline void SetCurrentToken(const string& token) { m_Token = token; } - inline void SetCurrentError(const string& error) { m_ErrorMsg = error; } + inline void SetLanguage(const char* lang) + { + AUTO_LOCK(m_StringMutex); + m_Language = lang; + }; + inline const string& GetLanguage() const + { + AUTO_LOCK(m_StringMutex); + return m_Language; + }; private: - string m_Token; - string m_ErrorMsg; + string m_Language; + mutable CThreadFastMutex m_StringMutex; }; -extern CPylon* g_pMasterServer; +extern CPylon g_MasterServer; diff --git a/r5dev/networksystem/serverlisting.h b/r5dev/networksystem/serverlisting.h index c08b385c..eab8e1ce 100644 --- a/r5dev/networksystem/serverlisting.h +++ b/r5dev/networksystem/serverlisting.h @@ -1,38 +1,40 @@ #pragma once - -struct NetGameMod_t -{ - string m_svPackage; - int m_nNumber; - bool m_bRequired; - string m_svDownloadLink; - - //NLOHMANN_DEFINE_TYPE_INTRUSIVE(NetGameMod_t, m_svPackage, m_nNumber, m_bRequired, m_svDownloadLink) -}; - struct NetGameServer_t { - string m_svHostName; - string m_svDescription; - bool m_bHidden; + // the name and description of this listing, which will be display to the + // client's server browser + string name; + string description; - string m_svHostMap = "mp_lobby"; - string m_svPlaylist = "dev_default"; + // whether or not this is a visible 'public' gameserver; this is only used + // on the masterserver to determine whether or not to broadcast your + // listing from there + bool hidden = true; - string m_svIpAddress; - string m_svGamePort; - string m_svEncryptionKey; + // the level and playlist of the server, which will be display to the + // client's server browser + string map = "mp_lobby"; + string playlist = "dev_default"; - string m_svRemoteChecksum; - string m_svSDKVersion; + // the address and port of the server, validated and set from the + // masterserver + string address; + int port = NULL; - string m_svPlayerCount; - string m_svMaxPlayers; - int64_t m_nTimeStamp = -1; + // the base64 net key used to decrypt game packets, the client has to + // install this before issuing a connectionless packet + string netKey; - string m_svPublicRef; - string m_svCachedId; + // version identifiers used to check if the gameserver and gameclient are + // compatible with each other + unsigned int checksum = NULL; + string versionId; - //vector m_vMods; -}; \ No newline at end of file + // current amount of players, and the maximum allowed for this gameserver + int numPlayers = NULL; + int maxPlayers = NULL; + + // the issue time of this listing + int64_t timeStamp = -1; +}; diff --git a/r5dev/pluginsdk/CMakeLists.txt b/r5dev/pluginsdk/CMakeLists.txt index 8e45621c..47e85e8e 100644 --- a/r5dev/pluginsdk/CMakeLists.txt +++ b/r5dev/pluginsdk/CMakeLists.txt @@ -15,6 +15,7 @@ end_sources( "${BUILD_OUTPUT_DIR}/bin/x64_retail/plugins/" ) target_link_libraries( ${PROJECT_NAME} PRIVATE "memoverride" "tier0" + "tier1" "libdetours" "libprotobuf" diff --git a/r5dev/pluginsdk/ifactory.h b/r5dev/pluginsdk/ifactory.h index 4acc4833..862cbc30 100644 --- a/r5dev/pluginsdk/ifactory.h +++ b/r5dev/pluginsdk/ifactory.h @@ -1,16 +1,10 @@ #pragma once +#include "tier1/interface.h" -struct FactoryInfo_t; -class CMemory; - -// TODO: Make this abstract and make it base class of CFactory. -class IFactory +abstract_class IFactorySystem { public: - virtual void AddFactory(const string& svFactoryName, void* pFactory) = 0; - virtual void AddFactory(FactoryInfo_t factoryInfo) = 0; - virtual size_t GetVersionIndex(const string& svInterfaceName) const = 0; - virtual void GetFactoriesFromRegister(void) = 0; - virtual CMemory GetFactoryPtr(const string& svFactoryName, bool versionLess = true) const = 0; - virtual const char* GetFactoryFullName(const string& svFactoryName) const = 0; + virtual void AddFactory(InstantiateInterfaceFn createFn, const char* pName) const = 0; + virtual void* GetFactory(const char* pName) const = 0; + virtual const char* GetVersion(void) const = 0; }; diff --git a/r5dev/pluginsdk/pluginsdk.cpp b/r5dev/pluginsdk/pluginsdk.cpp index ab541d44..cd677130 100644 --- a/r5dev/pluginsdk/pluginsdk.cpp +++ b/r5dev/pluginsdk/pluginsdk.cpp @@ -20,9 +20,7 @@ CPluginSDK::CPluginSDK(const char* pszSelfModule) : m_FactoryInstance(nullptr), m_PluginSystem(nullptr) { m_SelfModule.InitFromName(pszSelfModule); - - // !TODO: Use PEB! - m_GameModule.InitFromName("r5apex.exe"); + m_GameModule.InitFromBase(CModule::GetProcessEnvironmentBlock()->ImageBaseAddress); } //--------------------------------------------------------------------------------- @@ -37,33 +35,30 @@ CPluginSDK::~CPluginSDK() //--------------------------------------------------------------------------------- bool CPluginSDK::InitSDK() { - auto getFactorySystemFn = m_SDKModule.GetExportedSymbol("GetFactorySystem").RCast(); - - Assert(getFactorySystemFn, "Could not find GetFactorySystem export from gamesdk.dll"); - if (!getFactorySystemFn) + InstantiateInterfaceFn factorySystem = m_SDKModule.GetExportedSymbol("GetFactorySystem").RCast(); + if (!factorySystem) + { + Assert(factorySystem, "factorySystem == NULL; symbol renamed???"); return false; + } - m_FactoryInstance = reinterpret_cast(getFactorySystemFn()); - Assert(getFactorySystemFn, "m_FactoryInstace was nullptr."); - if (!m_FactoryInstance) - return false; + m_FactoryInstance = (IFactorySystem*)factorySystem(); // Let's make sure the factory version matches, else we unload. - bool isFactoryVersionOk = strcmp(m_FactoryInstance->GetFactoryFullName("VFactorySystem"), FACTORY_INTERFACE_VERSION) == 0; - Assert(isFactoryVersionOk, "Version mismatch between IFactory and CFactory."); + bool isFactoryVersionOk = strcmp(m_FactoryInstance->GetVersion(), FACTORY_INTERFACE_VERSION) == 0; if (!isFactoryVersionOk) + { + Assert(isFactoryVersionOk, "Version mismatch!"); return false; + } - // Let's make sure the SDK version matches with the PluginSystem, else we unload - bool isPluginVersionOk = strcmp(m_FactoryInstance->GetFactoryFullName("VPluginSystem"), INTERFACEVERSION_PLUGINSYSTEM) == 0; - Assert(isPluginVersionOk, "Version mismatch between CPluginSDK and CPluginSystem."); - if (!isPluginVersionOk) - return false; - - m_PluginSystem = m_FactoryInstance->GetFactoryPtr(INTERFACEVERSION_PLUGINSYSTEM, false).RCast(); - Assert(m_PluginSystem, "m_PluginSystem was nullptr."); + // Unload if + m_PluginSystem = (IPluginSystem*)m_FactoryInstance->GetFactory(INTERFACEVERSION_PLUGINSYSTEM); if (!m_PluginSystem) + { + Assert(m_PluginSystem, "CPluginSDK::m_PluginSystem == NULL"); return false; + } return true; } diff --git a/r5dev/pluginsdk/pluginsdk.h b/r5dev/pluginsdk/pluginsdk.h index fa6a0e22..5ef7ebc8 100644 --- a/r5dev/pluginsdk/pluginsdk.h +++ b/r5dev/pluginsdk/pluginsdk.h @@ -1,6 +1,6 @@ #pragma once -class IFactory; +class IFactorySystem; class IPluginSystem; //-----------------------------------------------------------------------------// @@ -15,7 +15,7 @@ public: inline void SetSDKModule(const CModule& sdkModule) { m_SDKModule = sdkModule; }; private: - IFactory* m_FactoryInstance; + IFactorySystem* m_FactoryInstance; IPluginSystem* m_PluginSystem; CModule m_SelfModule; CModule m_GameModule; diff --git a/r5dev/pluginsystem/modsystem.cpp b/r5dev/pluginsystem/modsystem.cpp index bd12ab8d..3ab6e7b1 100644 --- a/r5dev/pluginsystem/modsystem.cpp +++ b/r5dev/pluginsystem/modsystem.cpp @@ -7,13 +7,19 @@ //=============================================================================// #include "core/stdafx.h" -#include "vpc/rson.h" #include "tier0/commandline.h" #include "tier1/cvar.h" #include "tier2/fileutils.h" +#include "rtech/rson.h" #include "localize/localize.h" #include "modsystem.h" +//----------------------------------------------------------------------------- +// Console variables +//----------------------------------------------------------------------------- +static ConVar modsystem_enable("modsystem_enable", "1", FCVAR_RELEASE, "Enable the modsystem"); +static ConVar modsystem_debug("modsystem_debug", "0", FCVAR_RELEASE, "Debug the modsystem"); + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -32,7 +38,7 @@ CModSystem::~CModSystem() //----------------------------------------------------------------------------- void CModSystem::Init() { - if (!modsystem_enable->GetBool()) + if (!modsystem_enable.GetBool()) return; // no mods installed, no point in initializing. @@ -43,7 +49,7 @@ void CModSystem::Init() // executes commands/convars over the command line. we check for an // explicit modsystem debug flag, and set the convar from here. if (CommandLine()->CheckParm("-modsystem_debug")) - modsystem_debug->SetValue(true); + modsystem_debug.SetValue(true); CUtlVector modFileList; RecursiveFindFilesMatchingName(modFileList, @@ -88,8 +94,8 @@ void CModSystem::UpdateModStatusList() if (!enabledList.HasElement(mod->m_ModID)) { - if (modsystem_debug->GetBool()) - DevMsg(eDLL_T::ENGINE, "Mod '%s' does not exist in '%s'; enabling...\n", + if (modsystem_debug.GetBool()) + Msg(eDLL_T::ENGINE, "Mod '%s' does not exist in '%s'; enabling...\n", mod->m_ModID.Get(), MOD_STATUS_LIST_FILE); mod->SetState(eModState::ENABLED); @@ -99,8 +105,8 @@ void CModSystem::UpdateModStatusList() const bool bEnable = enabledList.FindElement(mod->m_ModID, false); mod->SetState(bEnable ? eModState::ENABLED : eModState::DISABLED); - if (modsystem_debug->GetBool()) - DevMsg(eDLL_T::ENGINE, "Mod '%s' exists in '%s' and is %s.\n", + if (modsystem_debug.GetBool()) + Msg(eDLL_T::ENGINE, "Mod '%s' exists in '%s' and is %s.\n", mod->m_ModID.Get(), MOD_STATUS_LIST_FILE, bEnable ? "enabled" : "disabled"); } } @@ -146,7 +152,7 @@ void CModSystem::WriteModStatusList() pModListKV->SetBool(mod->m_ModID.Get(), enabled); } - CUtlBuffer buf = CUtlBuffer(int64_t(0), 0, CUtlBuffer::TEXT_BUFFER); + CUtlBuffer buf = CUtlBuffer(ssize_t(0), 0, CUtlBuffer::TEXT_BUFFER); kv.RecursiveSaveToFile(buf, 0); if (!FileSystem()->WriteFile(MOD_STATUS_LIST_FILE, "PLATFORM", buf)) @@ -174,7 +180,7 @@ CModSystem::ModInstance_t::ModInstance_t(const CUtlString& basePath) } // parse any additional info from mod.vdf - ParseConVars(); + //ParseConVars(); ParseLocalizationFiles(); // add mod folder to search paths so files can be easily loaded from here @@ -273,56 +279,56 @@ bool CModSystem::ModInstance_t::ParseSettings() //----------------------------------------------------------------------------- // Purpose: parses and registers convars listed in settings KV //----------------------------------------------------------------------------- -void CModSystem::ModInstance_t::ParseConVars() -{ - Assert(m_SettingsKV); - KeyValues* pConVars = m_SettingsKV->FindKey("ConVars"); - - if (pConVars) - { - for (KeyValues* pSubKey = pConVars->GetFirstSubKey(); - pSubKey != nullptr; pSubKey = pSubKey->GetNextKey()) - { - const char* pszName = pSubKey->GetName(); - const char* pszFlagsString = pSubKey->GetString("flags", "NONE"); - const char* pszHelpString = pSubKey->GetString("helpText"); - const char* pszUsageString = pSubKey->GetString("usageText"); - - KeyValues* pValues = pSubKey->FindKey("Values"); - - const char* pszDefaultValue = "0"; - bool bMin = false; - bool bMax = false; - float fMin = 0.f; - float fMax = 0.f; - - if (pValues) - { - pszDefaultValue = pValues->GetString("default", "0"); - - // minimum cvar value - if (pValues->FindKey("min")) - { - bMin = true; // has min value - fMin = pValues->GetFloat("min", 0.f); - } - - // maximum cvar value - if (pValues->FindKey("max")) - { - bMax = true; // has max value - fMax = pValues->GetFloat("max", 1.f); - } - } - - int flags = FCVAR_NONE; - - if (ConVar_ParseFlagString(pszFlagsString, flags, pszName)) - ConVar::StaticCreate(pszName, pszDefaultValue, flags, - pszHelpString, bMin, fMin, bMax, fMax, nullptr, pszUsageString); - } - } -} +//void CModSystem::ModInstance_t::ParseConVars() +//{ +// Assert(m_SettingsKV); +// KeyValues* pConVars = m_SettingsKV->FindKey("ConVars"); +// +// if (pConVars) +// { +// for (KeyValues* pSubKey = pConVars->GetFirstSubKey(); +// pSubKey != nullptr; pSubKey = pSubKey->GetNextKey()) +// { +// const char* pszName = pSubKey->GetName(); +// const char* pszFlagsString = pSubKey->GetString("flags", "NONE"); +// const char* pszHelpString = pSubKey->GetString("helpText"); +// const char* pszUsageString = pSubKey->GetString("usageText"); +// +// KeyValues* pValues = pSubKey->FindKey("Values"); +// +// const char* pszDefaultValue = "0"; +// bool bMin = false; +// bool bMax = false; +// float fMin = 0.f; +// float fMax = 0.f; +// +// if (pValues) +// { +// pszDefaultValue = pValues->GetString("default", "0"); +// +// // minimum cvar value +// if (pValues->FindKey("min")) +// { +// bMin = true; // has min value +// fMin = pValues->GetFloat("min", 0.f); +// } +// +// // maximum cvar value +// if (pValues->FindKey("max")) +// { +// bMax = true; // has max value +// fMax = pValues->GetFloat("max", 1.f); +// } +// } +// +// int flags = FCVAR_NONE; +// +// if (ConVar_ParseFlagString(pszFlagsString, flags, pszName)) +// ConVar::StaticCreate(pszName, pszDefaultValue, flags, +// pszHelpString, bMin, fMin, bMax, fMax, nullptr, pszUsageString); +// } +// } +//} //----------------------------------------------------------------------------- // Purpose: parses and stores localization file paths in a vector @@ -343,4 +349,4 @@ void CModSystem::ModInstance_t::ParseLocalizationFiles() } } -CModSystem* g_pModSystem = new CModSystem(); +CModSystem g_ModSystem; diff --git a/r5dev/pluginsystem/modsystem.h b/r5dev/pluginsystem/modsystem.h index a81c4cff..2a336dc1 100644 --- a/r5dev/pluginsystem/modsystem.h +++ b/r5dev/pluginsystem/modsystem.h @@ -1,9 +1,9 @@ #pragma once -#include "vpc/keyvalues.h" -#include "vpc/rson.h" +#include "tier1/keyvalues.h" +#include "rtech/rson.h" #include "filesystem/filesystem.h" -#include "public/vscript/ivscript.h" +#include "vscript/ivscript.h" #define MOD_STATUS_LIST_FILE "mods.vdf" #define MOD_SETTINGS_FILE "mod.vdf" @@ -29,7 +29,7 @@ public: ~ModInstance_t(); bool ParseSettings(); - void ParseConVars(); + //void ParseConVars(); void ParseLocalizationFiles(); inline void SetState(eModState state) { m_iState = state; }; @@ -76,4 +76,9 @@ private: CUtlVector m_ModList; }; -extern CModSystem* g_pModSystem; +extern CModSystem g_ModSystem; + +FORCEINLINE CModSystem* ModSystem() +{ + return &g_ModSystem; +} diff --git a/r5dev/pluginsystem/pluginsystem.cpp b/r5dev/pluginsystem/pluginsystem.cpp index fc886167..8f5b2e0a 100644 --- a/r5dev/pluginsystem/pluginsystem.cpp +++ b/r5dev/pluginsystem/pluginsystem.cpp @@ -7,36 +7,40 @@ //=============================================================================// #include "core/stdafx.h" +#include "tier2/fileutils.h" +#include "filesystem/filesystem.h" #include "pluginsystem.h" -#include -#include //----------------------------------------------------------------------------- // Purpose: initialize the plugin system // Input : //----------------------------------------------------------------------------- -void CPluginSystem::PluginSystem_Init() +void CPluginSystem::Init() { - FileSystem()->CreateDirHierarchy(PLUGIN_INSTALL_DIR); + if (!FileSystem()->IsDirectory(PLUGIN_INSTALL_DIR, "GAME")) + return; // No plugins to load. CUtlVector< CUtlString > pluginPaths; - AddFilesToList(pluginPaths, PLUGIN_INSTALL_DIR, "dll"); + AddFilesToList(pluginPaths, PLUGIN_INSTALL_DIR, "dll", "GAME"); for (int i = 0; i < pluginPaths.Count(); ++i) { CUtlString& path = pluginPaths[i]; bool addInstance = true; - for (auto& inst : pluginInstances) + FOR_EACH_VEC(m_Instances, j) { - if (inst.m_svPluginFullPath.compare(path.Get()) == 0) + const PluginInstance_t& instance = m_Instances[j]; + + if (instance.m_Path.IsEqual_CaseInsensitive(path.String()) == 0) addInstance = false; } - const char* baseFileName = V_UnqualifiedFileName(path.Get()); - if (addInstance) - pluginInstances.push_back(PluginInstance_t(baseFileName, path.Get())); + { + const char* baseFileName = V_UnqualifiedFileName(path.String()); + m_Instances.AddToTail(PluginInstance_t(baseFileName, path.String())); + } } } @@ -45,16 +49,16 @@ void CPluginSystem::PluginSystem_Init() // Input : pluginInst* - // Output : bool //----------------------------------------------------------------------------- -bool CPluginSystem::LoadPluginInstance(PluginInstance_t& pluginInst) +bool CPluginSystem::LoadInstance(PluginInstance_t& pluginInst) { if (pluginInst.m_bIsLoaded) return false; - HMODULE loadedPlugin = LoadLibraryA(pluginInst.m_svPluginFullPath.c_str()); + HMODULE loadedPlugin = LoadLibraryA(pluginInst.m_Path.String()); if (loadedPlugin == INVALID_HANDLE_VALUE || loadedPlugin == 0) return false; - CModule pluginModule(pluginInst.m_svPluginName.c_str()); + CModule pluginModule(pluginInst.m_Name.String()); // Pass selfModule here on load function, we have to do // this because local listen/dedi/client dll's are called @@ -64,7 +68,7 @@ bool CPluginSystem::LoadPluginInstance(PluginInstance_t& pluginInst) Assert(onLoadFn); - if (!onLoadFn(pluginInst.m_svPluginName.c_str(), g_SDKDll.GetModuleName().c_str())) + if (!onLoadFn(pluginInst.m_Name.String(), g_SDKDll.GetModuleName().c_str())) { FreeLibrary(loadedPlugin); return false; @@ -79,7 +83,7 @@ bool CPluginSystem::LoadPluginInstance(PluginInstance_t& pluginInst) // Input : pluginInst* - // Output : bool //----------------------------------------------------------------------------- -bool CPluginSystem::UnloadPluginInstance(PluginInstance_t& pluginInst) +bool CPluginSystem::UnloadInstance(PluginInstance_t& pluginInst) { if (!pluginInst.m_bIsLoaded) return false; @@ -106,27 +110,26 @@ bool CPluginSystem::UnloadPluginInstance(PluginInstance_t& pluginInst) // Input : pluginInst* - // Output : bool //----------------------------------------------------------------------------- -bool CPluginSystem::ReloadPluginInstance(PluginInstance_t& pluginInst) +bool CPluginSystem::ReloadInstance(PluginInstance_t& pluginInst) { - return UnloadPluginInstance(pluginInst) ? LoadPluginInstance(pluginInst) : false; + return UnloadInstance(pluginInst) ? LoadInstance(pluginInst) : false; } //----------------------------------------------------------------------------- // Purpose: get all plugin instances // Input : -// Output : vector& +// Output : CUtlVector& //----------------------------------------------------------------------------- -vector& CPluginSystem::GetPluginInstances() +CUtlVector& CPluginSystem::GetInstances() { - return pluginInstances; + return m_Instances; } //----------------------------------------------------------------------------- // Purpose: add plugin callback for function // Input : *help -// Output : void //----------------------------------------------------------------------------- -void CPluginSystem::AddPluginCallback(PluginHelpWithAnything_t* help) +void CPluginSystem::AddCallback(PluginHelpWithAnything_t* help) { #define ADD_PLUGIN_CALLBACK(fn, callback, function) callback += reinterpret_cast(function) @@ -152,9 +155,8 @@ void CPluginSystem::AddPluginCallback(PluginHelpWithAnything_t* help) //----------------------------------------------------------------------------- // Purpose: remove plugin callback for function // Input : *help -// Output : void //----------------------------------------------------------------------------- -void CPluginSystem::RemovePluginCallback(PluginHelpWithAnything_t* help) +void CPluginSystem::RemoveCallback(PluginHelpWithAnything_t* help) { #define REMOVE_PLUGIN_CALLBACK(fn, callback, function) callback -= reinterpret_cast(function) @@ -192,12 +194,12 @@ void* CPluginSystem::HelpWithAnything(PluginHelpWithAnything_t* help) } case PluginHelpWithAnything_t::ePluginHelp::PLUGIN_REGISTER_CALLBACK: { - AddPluginCallback(help); + AddCallback(help); break; } case PluginHelpWithAnything_t::ePluginHelp::PLUGIN_UNREGISTER_CALLBACK: { - RemovePluginCallback(help); + RemoveCallback(help); break; } default: @@ -207,4 +209,4 @@ void* CPluginSystem::HelpWithAnything(PluginHelpWithAnything_t* help) return nullptr; } -CPluginSystem* g_pPluginSystem = new CPluginSystem(); \ No newline at end of file +CPluginSystem g_PluginSystem; diff --git a/r5dev/pluginsystem/pluginsystem.h b/r5dev/pluginsystem/pluginsystem.h index 943d0660..6b60e848 100644 --- a/r5dev/pluginsystem/pluginsystem.h +++ b/r5dev/pluginsystem/pluginsystem.h @@ -12,17 +12,21 @@ template class CPluginCallbackList { public: - CPluginCallbackList() : m_vCallbacks() {} - CPluginCallbackList(const vector& cbs) : m_vCallbacks(cbs) {} + CPluginCallbackList() {} + CPluginCallbackList(const CUtlVector& cbs) + { + for (auto it : cbs) + m_vCallbacks.AddToTail(it); + } - vector& GetCallbacks() { return m_vCallbacks; } + CUtlVector& GetCallbacks() { return m_vCallbacks; } operator bool() { - return !this->m_vCallbacks.empty; + return !this->m_vCallbacks.IsEmpty(); } - vector& operator!() + CUtlVector& operator!() { return this->m_vCallbacks; } @@ -30,17 +34,17 @@ public: CPluginCallbackList& operator+=(const T& rhs) { if (rhs) - this->m_vCallbacks.push_back(rhs); + this->m_vCallbacks.AddToTail(rhs); return *this; } - CPluginCallbackList& operator+=(const vector& rhs) + CPluginCallbackList& operator+=(const CUtlVector& rhs) { for (auto it : rhs) { if (it) - this->m_vCallbacks.push_back(it); + this->m_vCallbacks.AddToTail(it); } return *this; @@ -48,23 +52,26 @@ public: CPluginCallbackList& operator-=(const T& rhs) { - if (rhs) { - auto it = std::find(m_vCallbacks.begin(), m_vCallbacks.end(), rhs); - if (it != m_vCallbacks.end()) - m_vCallbacks.erase(it); + if (rhs) + { + const int fnd = m_vCallbacks.Find(rhs); + + if (fnd != m_vCallbacks.InvalidIndex()) + m_vCallbacks.Remove(fnd); } return *this; } - CPluginCallbackList& operator-=(const vector& rhs) + CPluginCallbackList& operator-=(const CUtlVector& rhs) { for (auto itc : rhs) { if (itc) { - auto it = std::find(m_vCallbacks.begin(), m_vCallbacks.end(), itc); - if (it != m_vCallbacks.end()) - m_vCallbacks.erase(it); + const int fnd = m_vCallbacks.Find(itc); + + if (fnd != m_vCallbacks.InvalidIndex()) + m_vCallbacks.Remove(fnd); } } @@ -72,7 +79,7 @@ public: } private: - vector m_vCallbacks; + CUtlVector m_vCallbacks; }; class CPluginSystem : IPluginSystem @@ -80,28 +87,36 @@ class CPluginSystem : IPluginSystem public: struct PluginInstance_t { - PluginInstance_t(string svPluginName, string svPluginFullPath) : m_svPluginName(svPluginName), m_svPluginFullPath(svPluginFullPath), m_svDescription(std::string()), m_bIsLoaded(false) {}; + PluginInstance_t(const char* pName, const char* pPath, const char* pDescription = "") + : m_Name(pName) + , m_Path(pPath) + , m_Description(pDescription) + , m_bIsLoaded(false) + { + }; // Might wanna make a status code system. typedef bool(*OnLoad)(const char*, const char*); typedef void(*OnUnload)(); CModule m_hModule; - string m_svPluginName; - string m_svPluginFullPath; - string m_svDescription; + CUtlString m_Name; + CUtlString m_Path; + CUtlString m_Description; bool m_bIsLoaded; // [ PIXIE ]: I don't like this and it's bad. // I will make a module manager later which will grab all modules from the process and adds each module / removes module that passes through DLLMain. }; - void PluginSystem_Init(); - bool ReloadPluginInstance(PluginInstance_t& pluginInst); - bool LoadPluginInstance(PluginInstance_t& pluginInst); - bool UnloadPluginInstance(PluginInstance_t& pluginInst); - void AddPluginCallback(PluginHelpWithAnything_t* help); - void RemovePluginCallback(PluginHelpWithAnything_t* help); + void Init(); - vector& GetPluginInstances(); + bool LoadInstance(PluginInstance_t& pluginInst); + bool UnloadInstance(PluginInstance_t& pluginInst); + bool ReloadInstance(PluginInstance_t& pluginInst); + + void AddCallback(PluginHelpWithAnything_t* help); + void RemoveCallback(PluginHelpWithAnything_t* help); + + CUtlVector& GetInstances(); virtual void* HelpWithAnything(PluginHelpWithAnything_t* help); @@ -113,11 +128,16 @@ public: #undef CREATE_PLUGIN_CALLBACK private: - vector pluginInstances; + CUtlVector m_Instances; }; -extern CPluginSystem* g_pPluginSystem; +extern CPluginSystem g_PluginSystem; + +FORCEINLINE CPluginSystem* PluginSystem() +{ + return &g_PluginSystem; +} // Monitor this and performance profile this if fps drops are detected. #define CALL_PLUGIN_CALLBACKS(callback, ...) \ for (auto& cb : !callback) \ - cb(__VA_ARGS__) \ No newline at end of file + cb(__VA_ARGS__) diff --git a/r5dev/protoc/CMakeLists.txt b/r5dev/protoc/CMakeLists.txt index 88ff5c7f..8e78d772 100644 --- a/r5dev/protoc/CMakeLists.txt +++ b/r5dev/protoc/CMakeLists.txt @@ -1,4 +1,16 @@ cmake_minimum_required( VERSION 3.16 ) +add_module( "lib" "LiveAPI_Pb" "vpc" ${FOLDER_CONTEXT} FALSE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "Runtime" + "events.pb.cc" + "events.pb.h" +) + +end_sources() +thirdparty_suppress_warnings() + add_module( "lib" "SigCache_Pb" "vpc" ${FOLDER_CONTEXT} FALSE TRUE ) start_sources() diff --git a/r5dev/protoc/events.pb.cc b/r5dev/protoc/events.pb.cc new file mode 100644 index 00000000..a64f40cb --- /dev/null +++ b/r5dev/protoc/events.pb.cc @@ -0,0 +1,22396 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: events.proto + +#include "events.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include + +PROTOBUF_PRAGMA_INIT_SEG + +namespace _pb = ::PROTOBUF_NAMESPACE_ID; +namespace _pbi = _pb::internal; + +namespace rtech { +namespace liveapi { +PROTOBUF_CONSTEXPR Vector3::Vector3( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.x_)*/0 + , /*decltype(_impl_.y_)*/0 + , /*decltype(_impl_.z_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct Vector3DefaultTypeInternal { + PROTOBUF_CONSTEXPR Vector3DefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~Vector3DefaultTypeInternal() {} + union { + Vector3 _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 Vector3DefaultTypeInternal _Vector3_default_instance_; +PROTOBUF_CONSTEXPR Player::Player( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.nucleushash_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.hardwarename_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.teamname_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.character_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.skin_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.pos_)*/nullptr + , /*decltype(_impl_.angles_)*/nullptr + , /*decltype(_impl_.teamid_)*/0u + , /*decltype(_impl_.currenthealth_)*/0u + , /*decltype(_impl_.maxhealth_)*/0u + , /*decltype(_impl_.shieldhealth_)*/0u + , /*decltype(_impl_.shieldmaxhealth_)*/0u + , /*decltype(_impl_.squadindex_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerDefaultTypeInternal() {} + union { + Player _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerDefaultTypeInternal _Player_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_LobbyPlayer::CustomMatch_LobbyPlayer( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.nucleushash_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.hardwarename_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.teamid_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_LobbyPlayerDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_LobbyPlayerDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_LobbyPlayerDefaultTypeInternal() {} + union { + CustomMatch_LobbyPlayer _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_LobbyPlayerDefaultTypeInternal _CustomMatch_LobbyPlayer_default_instance_; +PROTOBUF_CONSTEXPR Datacenter::Datacenter( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct DatacenterDefaultTypeInternal { + PROTOBUF_CONSTEXPR DatacenterDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~DatacenterDefaultTypeInternal() {} + union { + Datacenter _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 DatacenterDefaultTypeInternal _Datacenter_default_instance_; +PROTOBUF_CONSTEXPR Version::Version( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.revision_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.major_num_)*/0u + , /*decltype(_impl_.minor_num_)*/0u + , /*decltype(_impl_.build_stamp_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct VersionDefaultTypeInternal { + PROTOBUF_CONSTEXPR VersionDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~VersionDefaultTypeInternal() {} + union { + Version _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 VersionDefaultTypeInternal _Version_default_instance_; +PROTOBUF_CONSTEXPR InventoryItem::InventoryItem( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.item_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.extradata_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.quantity_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct InventoryItemDefaultTypeInternal { + PROTOBUF_CONSTEXPR InventoryItemDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~InventoryItemDefaultTypeInternal() {} + union { + InventoryItem _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 InventoryItemDefaultTypeInternal _InventoryItem_default_instance_; +PROTOBUF_CONSTEXPR LoadoutConfiguration::LoadoutConfiguration( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.weapons_)*/{} + , /*decltype(_impl_.equipment_)*/{} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct LoadoutConfigurationDefaultTypeInternal { + PROTOBUF_CONSTEXPR LoadoutConfigurationDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~LoadoutConfigurationDefaultTypeInternal() {} + union { + LoadoutConfiguration _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 LoadoutConfigurationDefaultTypeInternal _LoadoutConfiguration_default_instance_; +PROTOBUF_CONSTEXPR Init::Init( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.gameversion_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.platform_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.apiversion_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct InitDefaultTypeInternal { + PROTOBUF_CONSTEXPR InitDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~InitDefaultTypeInternal() {} + union { + Init _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 InitDefaultTypeInternal _Init_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_LobbyPlayers::CustomMatch_LobbyPlayers( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.players_)*/{} + , /*decltype(_impl_.playertoken_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_LobbyPlayersDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_LobbyPlayersDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_LobbyPlayersDefaultTypeInternal() {} + union { + CustomMatch_LobbyPlayers _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_LobbyPlayersDefaultTypeInternal _CustomMatch_LobbyPlayers_default_instance_; +PROTOBUF_CONSTEXPR ObserverSwitched::ObserverSwitched( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.targetteam_)*/{} + , /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.observer_)*/nullptr + , /*decltype(_impl_.target_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct ObserverSwitchedDefaultTypeInternal { + PROTOBUF_CONSTEXPR ObserverSwitchedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~ObserverSwitchedDefaultTypeInternal() {} + union { + ObserverSwitched _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ObserverSwitchedDefaultTypeInternal _ObserverSwitched_default_instance_; +PROTOBUF_CONSTEXPR ObserverAnnotation::ObserverAnnotation( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.annotationserial_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct ObserverAnnotationDefaultTypeInternal { + PROTOBUF_CONSTEXPR ObserverAnnotationDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~ObserverAnnotationDefaultTypeInternal() {} + union { + ObserverAnnotation _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ObserverAnnotationDefaultTypeInternal _ObserverAnnotation_default_instance_; +PROTOBUF_CONSTEXPR MatchSetup::MatchSetup( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.map_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.playlistname_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.playlistdesc_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.serverid_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.datacenter_)*/nullptr + , /*decltype(_impl_.startingloadout_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.aimassiston_)*/false + , /*decltype(_impl_.anonymousmode_)*/false + , /*decltype(_impl_._cached_size_)*/{}} {} +struct MatchSetupDefaultTypeInternal { + PROTOBUF_CONSTEXPR MatchSetupDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~MatchSetupDefaultTypeInternal() {} + union { + MatchSetup _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 MatchSetupDefaultTypeInternal _MatchSetup_default_instance_; +PROTOBUF_CONSTEXPR GameStateChanged::GameStateChanged( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.state_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct GameStateChangedDefaultTypeInternal { + PROTOBUF_CONSTEXPR GameStateChangedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~GameStateChangedDefaultTypeInternal() {} + union { + GameStateChanged _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GameStateChangedDefaultTypeInternal _GameStateChanged_default_instance_; +PROTOBUF_CONSTEXPR CharacterSelected::CharacterSelected( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CharacterSelectedDefaultTypeInternal { + PROTOBUF_CONSTEXPR CharacterSelectedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CharacterSelectedDefaultTypeInternal() {} + union { + CharacterSelected _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CharacterSelectedDefaultTypeInternal _CharacterSelected_default_instance_; +PROTOBUF_CONSTEXPR MatchStateEnd::MatchStateEnd( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.winners_)*/{} + , /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.state_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct MatchStateEndDefaultTypeInternal { + PROTOBUF_CONSTEXPR MatchStateEndDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~MatchStateEndDefaultTypeInternal() {} + union { + MatchStateEnd _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 MatchStateEndDefaultTypeInternal _MatchStateEnd_default_instance_; +PROTOBUF_CONSTEXPR RingStartClosing::RingStartClosing( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.center_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.stage_)*/0u + , /*decltype(_impl_.currentradius_)*/0 + , /*decltype(_impl_.endradius_)*/0 + , /*decltype(_impl_.shrinkduration_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct RingStartClosingDefaultTypeInternal { + PROTOBUF_CONSTEXPR RingStartClosingDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~RingStartClosingDefaultTypeInternal() {} + union { + RingStartClosing _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RingStartClosingDefaultTypeInternal _RingStartClosing_default_instance_; +PROTOBUF_CONSTEXPR RingFinishedClosing::RingFinishedClosing( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.center_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.stage_)*/0u + , /*decltype(_impl_.currentradius_)*/0 + , /*decltype(_impl_.shrinkduration_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct RingFinishedClosingDefaultTypeInternal { + PROTOBUF_CONSTEXPR RingFinishedClosingDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~RingFinishedClosingDefaultTypeInternal() {} + union { + RingFinishedClosing _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RingFinishedClosingDefaultTypeInternal _RingFinishedClosing_default_instance_; +PROTOBUF_CONSTEXPR PlayerConnected::PlayerConnected( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerConnectedDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerConnectedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerConnectedDefaultTypeInternal() {} + union { + PlayerConnected _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerConnectedDefaultTypeInternal _PlayerConnected_default_instance_; +PROTOBUF_CONSTEXPR PlayerDisconnected::PlayerDisconnected( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.canreconnect_)*/false + , /*decltype(_impl_.isalive_)*/false + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerDisconnectedDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerDisconnectedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerDisconnectedDefaultTypeInternal() {} + union { + PlayerDisconnected _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerDisconnectedDefaultTypeInternal _PlayerDisconnected_default_instance_; +PROTOBUF_CONSTEXPR PlayerStatChanged::PlayerStatChanged( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.statname_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.newValue_)*/{} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{}} {} +struct PlayerStatChangedDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerStatChangedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerStatChangedDefaultTypeInternal() {} + union { + PlayerStatChanged _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerStatChangedDefaultTypeInternal _PlayerStatChanged_default_instance_; +PROTOBUF_CONSTEXPR PlayerUpgradeTierChanged::PlayerUpgradeTierChanged( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.level_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerUpgradeTierChangedDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerUpgradeTierChangedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerUpgradeTierChangedDefaultTypeInternal() {} + union { + PlayerUpgradeTierChanged _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerUpgradeTierChangedDefaultTypeInternal _PlayerUpgradeTierChanged_default_instance_; +PROTOBUF_CONSTEXPR PlayerDamaged::PlayerDamaged( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.weapon_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.attacker_)*/nullptr + , /*decltype(_impl_.victim_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.damageinflicted_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerDamagedDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerDamagedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerDamagedDefaultTypeInternal() {} + union { + PlayerDamaged _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerDamagedDefaultTypeInternal _PlayerDamaged_default_instance_; +PROTOBUF_CONSTEXPR PlayerKilled::PlayerKilled( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.weapon_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.attacker_)*/nullptr + , /*decltype(_impl_.victim_)*/nullptr + , /*decltype(_impl_.awardedto_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerKilledDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerKilledDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerKilledDefaultTypeInternal() {} + union { + PlayerKilled _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerKilledDefaultTypeInternal _PlayerKilled_default_instance_; +PROTOBUF_CONSTEXPR PlayerDowned::PlayerDowned( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.weapon_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.attacker_)*/nullptr + , /*decltype(_impl_.victim_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerDownedDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerDownedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerDownedDefaultTypeInternal() {} + union { + PlayerDowned _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerDownedDefaultTypeInternal _PlayerDowned_default_instance_; +PROTOBUF_CONSTEXPR PlayerAssist::PlayerAssist( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.weapon_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.assistant_)*/nullptr + , /*decltype(_impl_.victim_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerAssistDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerAssistDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerAssistDefaultTypeInternal() {} + union { + PlayerAssist _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerAssistDefaultTypeInternal _PlayerAssist_default_instance_; +PROTOBUF_CONSTEXPR SquadEliminated::SquadEliminated( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.players_)*/{} + , /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct SquadEliminatedDefaultTypeInternal { + PROTOBUF_CONSTEXPR SquadEliminatedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~SquadEliminatedDefaultTypeInternal() {} + union { + SquadEliminated _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 SquadEliminatedDefaultTypeInternal _SquadEliminated_default_instance_; +PROTOBUF_CONSTEXPR GibraltarShieldAbsorbed::GibraltarShieldAbsorbed( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.attacker_)*/nullptr + , /*decltype(_impl_.victim_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.damageinflicted_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct GibraltarShieldAbsorbedDefaultTypeInternal { + PROTOBUF_CONSTEXPR GibraltarShieldAbsorbedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~GibraltarShieldAbsorbedDefaultTypeInternal() {} + union { + GibraltarShieldAbsorbed _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GibraltarShieldAbsorbedDefaultTypeInternal _GibraltarShieldAbsorbed_default_instance_; +PROTOBUF_CONSTEXPR RevenantForgedShadowDamaged::RevenantForgedShadowDamaged( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.attacker_)*/nullptr + , /*decltype(_impl_.victim_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.damageinflicted_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct RevenantForgedShadowDamagedDefaultTypeInternal { + PROTOBUF_CONSTEXPR RevenantForgedShadowDamagedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~RevenantForgedShadowDamagedDefaultTypeInternal() {} + union { + RevenantForgedShadowDamaged _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RevenantForgedShadowDamagedDefaultTypeInternal _RevenantForgedShadowDamaged_default_instance_; +PROTOBUF_CONSTEXPR PlayerRespawnTeam::PlayerRespawnTeam( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.respawned_)*/{} + , /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerRespawnTeamDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerRespawnTeamDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerRespawnTeamDefaultTypeInternal() {} + union { + PlayerRespawnTeam _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerRespawnTeamDefaultTypeInternal _PlayerRespawnTeam_default_instance_; +PROTOBUF_CONSTEXPR PlayerRevive::PlayerRevive( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.revived_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerReviveDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerReviveDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerReviveDefaultTypeInternal() {} + union { + PlayerRevive _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerReviveDefaultTypeInternal _PlayerRevive_default_instance_; +PROTOBUF_CONSTEXPR ArenasItemSelected::ArenasItemSelected( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.item_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.quantity_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct ArenasItemSelectedDefaultTypeInternal { + PROTOBUF_CONSTEXPR ArenasItemSelectedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~ArenasItemSelectedDefaultTypeInternal() {} + union { + ArenasItemSelected _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ArenasItemSelectedDefaultTypeInternal _ArenasItemSelected_default_instance_; +PROTOBUF_CONSTEXPR ArenasItemDeselected::ArenasItemDeselected( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.item_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.quantity_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct ArenasItemDeselectedDefaultTypeInternal { + PROTOBUF_CONSTEXPR ArenasItemDeselectedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~ArenasItemDeselectedDefaultTypeInternal() {} + union { + ArenasItemDeselected _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ArenasItemDeselectedDefaultTypeInternal _ArenasItemDeselected_default_instance_; +PROTOBUF_CONSTEXPR InventoryPickUp::InventoryPickUp( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.item_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.quantity_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct InventoryPickUpDefaultTypeInternal { + PROTOBUF_CONSTEXPR InventoryPickUpDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~InventoryPickUpDefaultTypeInternal() {} + union { + InventoryPickUp _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 InventoryPickUpDefaultTypeInternal _InventoryPickUp_default_instance_; +PROTOBUF_CONSTEXPR InventoryDrop::InventoryDrop( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.extradata_)*/{} + , /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.item_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.quantity_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct InventoryDropDefaultTypeInternal { + PROTOBUF_CONSTEXPR InventoryDropDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~InventoryDropDefaultTypeInternal() {} + union { + InventoryDrop _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 InventoryDropDefaultTypeInternal _InventoryDrop_default_instance_; +PROTOBUF_CONSTEXPR InventoryUse::InventoryUse( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.item_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.quantity_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct InventoryUseDefaultTypeInternal { + PROTOBUF_CONSTEXPR InventoryUseDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~InventoryUseDefaultTypeInternal() {} + union { + InventoryUse _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 InventoryUseDefaultTypeInternal _InventoryUse_default_instance_; +PROTOBUF_CONSTEXPR BannerCollected::BannerCollected( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.collected_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct BannerCollectedDefaultTypeInternal { + PROTOBUF_CONSTEXPR BannerCollectedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~BannerCollectedDefaultTypeInternal() {} + union { + BannerCollected _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 BannerCollectedDefaultTypeInternal _BannerCollected_default_instance_; +PROTOBUF_CONSTEXPR PlayerAbilityUsed::PlayerAbilityUsed( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.linkedentity_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PlayerAbilityUsedDefaultTypeInternal { + PROTOBUF_CONSTEXPR PlayerAbilityUsedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PlayerAbilityUsedDefaultTypeInternal() {} + union { + PlayerAbilityUsed _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PlayerAbilityUsedDefaultTypeInternal _PlayerAbilityUsed_default_instance_; +PROTOBUF_CONSTEXPR LegendUpgradeSelected::LegendUpgradeSelected( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.upgradename_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.upgradedesc_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.level_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct LegendUpgradeSelectedDefaultTypeInternal { + PROTOBUF_CONSTEXPR LegendUpgradeSelectedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~LegendUpgradeSelectedDefaultTypeInternal() {} + union { + LegendUpgradeSelected _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 LegendUpgradeSelectedDefaultTypeInternal _LegendUpgradeSelected_default_instance_; +PROTOBUF_CONSTEXPR ZiplineUsed::ZiplineUsed( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.linkedentity_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct ZiplineUsedDefaultTypeInternal { + PROTOBUF_CONSTEXPR ZiplineUsedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~ZiplineUsedDefaultTypeInternal() {} + union { + ZiplineUsed _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ZiplineUsedDefaultTypeInternal _ZiplineUsed_default_instance_; +PROTOBUF_CONSTEXPR GrenadeThrown::GrenadeThrown( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.linkedentity_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct GrenadeThrownDefaultTypeInternal { + PROTOBUF_CONSTEXPR GrenadeThrownDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~GrenadeThrownDefaultTypeInternal() {} + union { + GrenadeThrown _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 GrenadeThrownDefaultTypeInternal _GrenadeThrown_default_instance_; +PROTOBUF_CONSTEXPR BlackMarketAction::BlackMarketAction( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.item_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct BlackMarketActionDefaultTypeInternal { + PROTOBUF_CONSTEXPR BlackMarketActionDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~BlackMarketActionDefaultTypeInternal() {} + union { + BlackMarketAction _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 BlackMarketActionDefaultTypeInternal _BlackMarketAction_default_instance_; +PROTOBUF_CONSTEXPR WraithPortal::WraithPortal( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct WraithPortalDefaultTypeInternal { + PROTOBUF_CONSTEXPR WraithPortalDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~WraithPortalDefaultTypeInternal() {} + union { + WraithPortal _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 WraithPortalDefaultTypeInternal _WraithPortal_default_instance_; +PROTOBUF_CONSTEXPR WarpGateUsed::WarpGateUsed( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct WarpGateUsedDefaultTypeInternal { + PROTOBUF_CONSTEXPR WarpGateUsedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~WarpGateUsedDefaultTypeInternal() {} + union { + WarpGateUsed _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 WarpGateUsedDefaultTypeInternal _WarpGateUsed_default_instance_; +PROTOBUF_CONSTEXPR AmmoUsed::AmmoUsed( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.ammotype_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_.amountused_)*/0u + , /*decltype(_impl_.oldammocount_)*/0u + , /*decltype(_impl_.newammocount_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct AmmoUsedDefaultTypeInternal { + PROTOBUF_CONSTEXPR AmmoUsedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~AmmoUsedDefaultTypeInternal() {} + union { + AmmoUsed _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 AmmoUsedDefaultTypeInternal _AmmoUsed_default_instance_; +PROTOBUF_CONSTEXPR WeaponSwitched::WeaponSwitched( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.oldweapon_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.newweapon_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.player_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct WeaponSwitchedDefaultTypeInternal { + PROTOBUF_CONSTEXPR WeaponSwitchedDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~WeaponSwitchedDefaultTypeInternal() {} + union { + WeaponSwitched _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 WeaponSwitchedDefaultTypeInternal _WeaponSwitched_default_instance_; +PROTOBUF_CONSTEXPR CustomEvent::CustomEvent( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.category_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.name_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.data_)*/nullptr + , /*decltype(_impl_.timestamp_)*/uint64_t{0u} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomEventDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomEventDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomEventDefaultTypeInternal() {} + union { + CustomEvent _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomEventDefaultTypeInternal _CustomEvent_default_instance_; +PROTOBUF_CONSTEXPR ChangeCamera::ChangeCamera( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.target_)*/{} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{}} {} +struct ChangeCameraDefaultTypeInternal { + PROTOBUF_CONSTEXPR ChangeCameraDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~ChangeCameraDefaultTypeInternal() {} + union { + ChangeCamera _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ChangeCameraDefaultTypeInternal _ChangeCamera_default_instance_; +PROTOBUF_CONSTEXPR PauseToggle::PauseToggle( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.pretimer_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct PauseToggleDefaultTypeInternal { + PROTOBUF_CONSTEXPR PauseToggleDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~PauseToggleDefaultTypeInternal() {} + union { + PauseToggle _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 PauseToggleDefaultTypeInternal _PauseToggle_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_CreateLobby::CustomMatch_CreateLobby( + ::_pbi::ConstantInitialized) {} +struct CustomMatch_CreateLobbyDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_CreateLobbyDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_CreateLobbyDefaultTypeInternal() {} + union { + CustomMatch_CreateLobby _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_CreateLobbyDefaultTypeInternal _CustomMatch_CreateLobby_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_JoinLobby::CustomMatch_JoinLobby( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.roletoken_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_JoinLobbyDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_JoinLobbyDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_JoinLobbyDefaultTypeInternal() {} + union { + CustomMatch_JoinLobby _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_JoinLobbyDefaultTypeInternal _CustomMatch_JoinLobby_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_LeaveLobby::CustomMatch_LeaveLobby( + ::_pbi::ConstantInitialized) {} +struct CustomMatch_LeaveLobbyDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_LeaveLobbyDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_LeaveLobbyDefaultTypeInternal() {} + union { + CustomMatch_LeaveLobby _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_LeaveLobbyDefaultTypeInternal _CustomMatch_LeaveLobby_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_SetReady::CustomMatch_SetReady( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.isready_)*/false + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_SetReadyDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_SetReadyDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_SetReadyDefaultTypeInternal() {} + union { + CustomMatch_SetReady _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_SetReadyDefaultTypeInternal _CustomMatch_SetReady_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_GetLobbyPlayers::CustomMatch_GetLobbyPlayers( + ::_pbi::ConstantInitialized) {} +struct CustomMatch_GetLobbyPlayersDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_GetLobbyPlayersDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_GetLobbyPlayersDefaultTypeInternal() {} + union { + CustomMatch_GetLobbyPlayers _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_GetLobbyPlayersDefaultTypeInternal _CustomMatch_GetLobbyPlayers_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_SetMatchmaking::CustomMatch_SetMatchmaking( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.enabled_)*/false + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_SetMatchmakingDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_SetMatchmakingDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_SetMatchmakingDefaultTypeInternal() {} + union { + CustomMatch_SetMatchmaking _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_SetMatchmakingDefaultTypeInternal _CustomMatch_SetMatchmaking_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_SetTeam::CustomMatch_SetTeam( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.targethardwarename_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.targetnucleushash_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.teamid_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_SetTeamDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_SetTeamDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_SetTeamDefaultTypeInternal() {} + union { + CustomMatch_SetTeam _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_SetTeamDefaultTypeInternal _CustomMatch_SetTeam_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_KickPlayer::CustomMatch_KickPlayer( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.targethardwarename_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.targetnucleushash_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_KickPlayerDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_KickPlayerDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_KickPlayerDefaultTypeInternal() {} + union { + CustomMatch_KickPlayer _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_KickPlayerDefaultTypeInternal _CustomMatch_KickPlayer_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_SetSettings::CustomMatch_SetSettings( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.playlistname_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.adminchat_)*/false + , /*decltype(_impl_.teamrename_)*/false + , /*decltype(_impl_.selfassign_)*/false + , /*decltype(_impl_.aimassist_)*/false + , /*decltype(_impl_.anonmode_)*/false + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_SetSettingsDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_SetSettingsDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_SetSettingsDefaultTypeInternal() {} + union { + CustomMatch_SetSettings _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_SetSettingsDefaultTypeInternal _CustomMatch_SetSettings_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_GetSettings::CustomMatch_GetSettings( + ::_pbi::ConstantInitialized) {} +struct CustomMatch_GetSettingsDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_GetSettingsDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_GetSettingsDefaultTypeInternal() {} + union { + CustomMatch_GetSettings _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_GetSettingsDefaultTypeInternal _CustomMatch_GetSettings_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_SetTeamName::CustomMatch_SetTeamName( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.teamname_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.teamid_)*/0 + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_SetTeamNameDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_SetTeamNameDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_SetTeamNameDefaultTypeInternal() {} + union { + CustomMatch_SetTeamName _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_SetTeamNameDefaultTypeInternal _CustomMatch_SetTeamName_default_instance_; +PROTOBUF_CONSTEXPR CustomMatch_SendChat::CustomMatch_SendChat( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.text_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct CustomMatch_SendChatDefaultTypeInternal { + PROTOBUF_CONSTEXPR CustomMatch_SendChatDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~CustomMatch_SendChatDefaultTypeInternal() {} + union { + CustomMatch_SendChat _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 CustomMatch_SendChatDefaultTypeInternal _CustomMatch_SendChat_default_instance_; +PROTOBUF_CONSTEXPR Request::Request( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.presharedkey_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_.withack_)*/false + , /*decltype(_impl_.actions_)*/{} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{}} {} +struct RequestDefaultTypeInternal { + PROTOBUF_CONSTEXPR RequestDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~RequestDefaultTypeInternal() {} + union { + Request _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RequestDefaultTypeInternal _Request_default_instance_; +PROTOBUF_CONSTEXPR RequestStatus::RequestStatus( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.status_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}} + , /*decltype(_impl_._cached_size_)*/{}} {} +struct RequestStatusDefaultTypeInternal { + PROTOBUF_CONSTEXPR RequestStatusDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~RequestStatusDefaultTypeInternal() {} + union { + RequestStatus _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RequestStatusDefaultTypeInternal _RequestStatus_default_instance_; +PROTOBUF_CONSTEXPR Response::Response( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.result_)*/nullptr + , /*decltype(_impl_.success_)*/false + , /*decltype(_impl_._cached_size_)*/{}} {} +struct ResponseDefaultTypeInternal { + PROTOBUF_CONSTEXPR ResponseDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~ResponseDefaultTypeInternal() {} + union { + Response _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ResponseDefaultTypeInternal _Response_default_instance_; +PROTOBUF_CONSTEXPR LiveAPIEvent::LiveAPIEvent( + ::_pbi::ConstantInitialized): _impl_{ + /*decltype(_impl_.gamemessage_)*/nullptr + , /*decltype(_impl_.event_size_)*/0u + , /*decltype(_impl_._cached_size_)*/{}} {} +struct LiveAPIEventDefaultTypeInternal { + PROTOBUF_CONSTEXPR LiveAPIEventDefaultTypeInternal() + : _instance(::_pbi::ConstantInitialized{}) {} + ~LiveAPIEventDefaultTypeInternal() {} + union { + LiveAPIEvent _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 LiveAPIEventDefaultTypeInternal _LiveAPIEvent_default_instance_; +} // namespace liveapi +} // namespace rtech +static ::_pb::Metadata file_level_metadata_events_2eproto[64]; +static const ::_pb::EnumDescriptor* file_level_enum_descriptors_events_2eproto[1]; +static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_events_2eproto = nullptr; + +const uint32_t TableStruct_events_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Vector3, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Vector3, _impl_.x_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Vector3, _impl_.y_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Vector3, _impl_.z_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.name_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.teamid_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.pos_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.angles_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.currenthealth_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.maxhealth_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.shieldhealth_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.shieldmaxhealth_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.nucleushash_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.hardwarename_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.teamname_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.squadindex_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.character_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Player, _impl_.skin_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayer, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayer, _impl_.name_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayer, _impl_.teamid_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayer, _impl_.nucleushash_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayer, _impl_.hardwarename_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Datacenter, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Datacenter, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Datacenter, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Datacenter, _impl_.name_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Version, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Version, _impl_.major_num_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Version, _impl_.minor_num_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Version, _impl_.build_stamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Version, _impl_.revision_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryItem, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryItem, _impl_.quantity_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryItem, _impl_.item_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryItem, _impl_.extradata_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LoadoutConfiguration, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LoadoutConfiguration, _impl_.weapons_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LoadoutConfiguration, _impl_.equipment_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Init, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Init, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Init, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Init, _impl_.gameversion_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Init, _impl_.apiversion_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Init, _impl_.platform_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Init, _impl_.name_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayers, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayers, _impl_.playertoken_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LobbyPlayers, _impl_.players_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverSwitched, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverSwitched, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverSwitched, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverSwitched, _impl_.observer_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverSwitched, _impl_.target_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverSwitched, _impl_.targetteam_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverAnnotation, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverAnnotation, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverAnnotation, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ObserverAnnotation, _impl_.annotationserial_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.map_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.playlistname_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.playlistdesc_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.datacenter_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.aimassiston_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.anonymousmode_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.serverid_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchSetup, _impl_.startingloadout_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GameStateChanged, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GameStateChanged, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GameStateChanged, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GameStateChanged, _impl_.state_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CharacterSelected, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CharacterSelected, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CharacterSelected, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CharacterSelected, _impl_.player_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchStateEnd, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchStateEnd, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchStateEnd, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchStateEnd, _impl_.state_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::MatchStateEnd, _impl_.winners_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _impl_.stage_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _impl_.center_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _impl_.currentradius_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _impl_.endradius_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingStartClosing, _impl_.shrinkduration_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingFinishedClosing, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingFinishedClosing, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingFinishedClosing, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingFinishedClosing, _impl_.stage_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingFinishedClosing, _impl_.center_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingFinishedClosing, _impl_.currentradius_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RingFinishedClosing, _impl_.shrinkduration_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerConnected, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerConnected, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerConnected, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerConnected, _impl_.player_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDisconnected, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDisconnected, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDisconnected, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDisconnected, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDisconnected, _impl_.canreconnect_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDisconnected, _impl_.isalive_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerStatChanged, _internal_metadata_), + ~0u, // no _extensions_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerStatChanged, _impl_._oneof_case_[0]), + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerStatChanged, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerStatChanged, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerStatChanged, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerStatChanged, _impl_.statname_), + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerStatChanged, _impl_.newValue_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerUpgradeTierChanged, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerUpgradeTierChanged, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerUpgradeTierChanged, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerUpgradeTierChanged, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerUpgradeTierChanged, _impl_.level_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDamaged, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDamaged, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDamaged, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDamaged, _impl_.attacker_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDamaged, _impl_.victim_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDamaged, _impl_.weapon_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDamaged, _impl_.damageinflicted_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerKilled, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerKilled, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerKilled, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerKilled, _impl_.attacker_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerKilled, _impl_.victim_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerKilled, _impl_.awardedto_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerKilled, _impl_.weapon_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDowned, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDowned, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDowned, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDowned, _impl_.attacker_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDowned, _impl_.victim_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerDowned, _impl_.weapon_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAssist, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAssist, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAssist, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAssist, _impl_.assistant_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAssist, _impl_.victim_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAssist, _impl_.weapon_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::SquadEliminated, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::SquadEliminated, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::SquadEliminated, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::SquadEliminated, _impl_.players_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GibraltarShieldAbsorbed, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GibraltarShieldAbsorbed, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GibraltarShieldAbsorbed, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GibraltarShieldAbsorbed, _impl_.attacker_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GibraltarShieldAbsorbed, _impl_.victim_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GibraltarShieldAbsorbed, _impl_.damageinflicted_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RevenantForgedShadowDamaged, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RevenantForgedShadowDamaged, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RevenantForgedShadowDamaged, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RevenantForgedShadowDamaged, _impl_.attacker_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RevenantForgedShadowDamaged, _impl_.victim_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RevenantForgedShadowDamaged, _impl_.damageinflicted_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRespawnTeam, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRespawnTeam, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRespawnTeam, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRespawnTeam, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRespawnTeam, _impl_.respawned_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRevive, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRevive, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRevive, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRevive, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerRevive, _impl_.revived_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemSelected, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemSelected, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemSelected, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemSelected, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemSelected, _impl_.item_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemSelected, _impl_.quantity_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemDeselected, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemDeselected, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemDeselected, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemDeselected, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemDeselected, _impl_.item_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ArenasItemDeselected, _impl_.quantity_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryPickUp, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryPickUp, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryPickUp, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryPickUp, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryPickUp, _impl_.item_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryPickUp, _impl_.quantity_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryDrop, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryDrop, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryDrop, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryDrop, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryDrop, _impl_.item_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryDrop, _impl_.quantity_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryDrop, _impl_.extradata_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryUse, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryUse, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryUse, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryUse, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryUse, _impl_.item_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::InventoryUse, _impl_.quantity_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BannerCollected, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BannerCollected, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BannerCollected, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BannerCollected, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BannerCollected, _impl_.collected_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAbilityUsed, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAbilityUsed, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAbilityUsed, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAbilityUsed, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PlayerAbilityUsed, _impl_.linkedentity_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LegendUpgradeSelected, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LegendUpgradeSelected, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LegendUpgradeSelected, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LegendUpgradeSelected, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LegendUpgradeSelected, _impl_.upgradename_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LegendUpgradeSelected, _impl_.upgradedesc_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LegendUpgradeSelected, _impl_.level_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ZiplineUsed, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ZiplineUsed, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ZiplineUsed, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ZiplineUsed, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ZiplineUsed, _impl_.linkedentity_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GrenadeThrown, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GrenadeThrown, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GrenadeThrown, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GrenadeThrown, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::GrenadeThrown, _impl_.linkedentity_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BlackMarketAction, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BlackMarketAction, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BlackMarketAction, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BlackMarketAction, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::BlackMarketAction, _impl_.item_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WraithPortal, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WraithPortal, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WraithPortal, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WraithPortal, _impl_.player_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WarpGateUsed, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WarpGateUsed, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WarpGateUsed, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WarpGateUsed, _impl_.player_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _impl_.ammotype_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _impl_.amountused_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _impl_.oldammocount_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::AmmoUsed, _impl_.newammocount_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WeaponSwitched, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WeaponSwitched, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WeaponSwitched, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WeaponSwitched, _impl_.player_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WeaponSwitched, _impl_.oldweapon_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::WeaponSwitched, _impl_.newweapon_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomEvent, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomEvent, _impl_.timestamp_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomEvent, _impl_.category_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomEvent, _impl_.name_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomEvent, _impl_.data_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ChangeCamera, _internal_metadata_), + ~0u, // no _extensions_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ChangeCamera, _impl_._oneof_case_[0]), + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::ChangeCamera, _impl_.target_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PauseToggle, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::PauseToggle, _impl_.pretimer_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_CreateLobby, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_JoinLobby, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_JoinLobby, _impl_.roletoken_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_LeaveLobby, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetReady, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetReady, _impl_.isready_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_GetLobbyPlayers, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetMatchmaking, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetMatchmaking, _impl_.enabled_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetTeam, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetTeam, _impl_.teamid_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetTeam, _impl_.targethardwarename_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetTeam, _impl_.targetnucleushash_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_KickPlayer, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_KickPlayer, _impl_.targethardwarename_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_KickPlayer, _impl_.targetnucleushash_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetSettings, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetSettings, _impl_.playlistname_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetSettings, _impl_.adminchat_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetSettings, _impl_.teamrename_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetSettings, _impl_.selfassign_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetSettings, _impl_.aimassist_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetSettings, _impl_.anonmode_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_GetSettings, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetTeamName, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetTeamName, _impl_.teamid_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SetTeamName, _impl_.teamname_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SendChat, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::CustomMatch_SendChat, _impl_.text_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Request, _internal_metadata_), + ~0u, // no _extensions_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Request, _impl_._oneof_case_[0]), + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Request, _impl_.withack_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Request, _impl_.presharedkey_), + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + ::_pbi::kInvalidFieldOffsetTag, + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Request, _impl_.actions_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RequestStatus, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::RequestStatus, _impl_.status_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Response, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Response, _impl_.success_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::Response, _impl_.result_), + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LiveAPIEvent, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LiveAPIEvent, _impl_.event_size_), + PROTOBUF_FIELD_OFFSET(::rtech::liveapi::LiveAPIEvent, _impl_.gamemessage_), +}; +static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, -1, sizeof(::rtech::liveapi::Vector3)}, + { 9, -1, -1, sizeof(::rtech::liveapi::Player)}, + { 29, -1, -1, sizeof(::rtech::liveapi::CustomMatch_LobbyPlayer)}, + { 39, -1, -1, sizeof(::rtech::liveapi::Datacenter)}, + { 48, -1, -1, sizeof(::rtech::liveapi::Version)}, + { 58, -1, -1, sizeof(::rtech::liveapi::InventoryItem)}, + { 67, -1, -1, sizeof(::rtech::liveapi::LoadoutConfiguration)}, + { 75, -1, -1, sizeof(::rtech::liveapi::Init)}, + { 87, -1, -1, sizeof(::rtech::liveapi::CustomMatch_LobbyPlayers)}, + { 95, -1, -1, sizeof(::rtech::liveapi::ObserverSwitched)}, + { 106, -1, -1, sizeof(::rtech::liveapi::ObserverAnnotation)}, + { 115, -1, -1, sizeof(::rtech::liveapi::MatchSetup)}, + { 131, -1, -1, sizeof(::rtech::liveapi::GameStateChanged)}, + { 140, -1, -1, sizeof(::rtech::liveapi::CharacterSelected)}, + { 149, -1, -1, sizeof(::rtech::liveapi::MatchStateEnd)}, + { 159, -1, -1, sizeof(::rtech::liveapi::RingStartClosing)}, + { 172, -1, -1, sizeof(::rtech::liveapi::RingFinishedClosing)}, + { 184, -1, -1, sizeof(::rtech::liveapi::PlayerConnected)}, + { 193, -1, -1, sizeof(::rtech::liveapi::PlayerDisconnected)}, + { 204, -1, -1, sizeof(::rtech::liveapi::PlayerStatChanged)}, + { 218, -1, -1, sizeof(::rtech::liveapi::PlayerUpgradeTierChanged)}, + { 228, -1, -1, sizeof(::rtech::liveapi::PlayerDamaged)}, + { 240, -1, -1, sizeof(::rtech::liveapi::PlayerKilled)}, + { 252, -1, -1, sizeof(::rtech::liveapi::PlayerDowned)}, + { 263, -1, -1, sizeof(::rtech::liveapi::PlayerAssist)}, + { 274, -1, -1, sizeof(::rtech::liveapi::SquadEliminated)}, + { 283, -1, -1, sizeof(::rtech::liveapi::GibraltarShieldAbsorbed)}, + { 294, -1, -1, sizeof(::rtech::liveapi::RevenantForgedShadowDamaged)}, + { 305, -1, -1, sizeof(::rtech::liveapi::PlayerRespawnTeam)}, + { 315, -1, -1, sizeof(::rtech::liveapi::PlayerRevive)}, + { 325, -1, -1, sizeof(::rtech::liveapi::ArenasItemSelected)}, + { 336, -1, -1, sizeof(::rtech::liveapi::ArenasItemDeselected)}, + { 347, -1, -1, sizeof(::rtech::liveapi::InventoryPickUp)}, + { 358, -1, -1, sizeof(::rtech::liveapi::InventoryDrop)}, + { 370, -1, -1, sizeof(::rtech::liveapi::InventoryUse)}, + { 381, -1, -1, sizeof(::rtech::liveapi::BannerCollected)}, + { 391, -1, -1, sizeof(::rtech::liveapi::PlayerAbilityUsed)}, + { 401, -1, -1, sizeof(::rtech::liveapi::LegendUpgradeSelected)}, + { 413, -1, -1, sizeof(::rtech::liveapi::ZiplineUsed)}, + { 423, -1, -1, sizeof(::rtech::liveapi::GrenadeThrown)}, + { 433, -1, -1, sizeof(::rtech::liveapi::BlackMarketAction)}, + { 443, -1, -1, sizeof(::rtech::liveapi::WraithPortal)}, + { 452, -1, -1, sizeof(::rtech::liveapi::WarpGateUsed)}, + { 461, -1, -1, sizeof(::rtech::liveapi::AmmoUsed)}, + { 474, -1, -1, sizeof(::rtech::liveapi::WeaponSwitched)}, + { 485, -1, -1, sizeof(::rtech::liveapi::CustomEvent)}, + { 495, -1, -1, sizeof(::rtech::liveapi::ChangeCamera)}, + { 504, -1, -1, sizeof(::rtech::liveapi::PauseToggle)}, + { 511, -1, -1, sizeof(::rtech::liveapi::CustomMatch_CreateLobby)}, + { 517, -1, -1, sizeof(::rtech::liveapi::CustomMatch_JoinLobby)}, + { 524, -1, -1, sizeof(::rtech::liveapi::CustomMatch_LeaveLobby)}, + { 530, -1, -1, sizeof(::rtech::liveapi::CustomMatch_SetReady)}, + { 537, -1, -1, sizeof(::rtech::liveapi::CustomMatch_GetLobbyPlayers)}, + { 543, -1, -1, sizeof(::rtech::liveapi::CustomMatch_SetMatchmaking)}, + { 550, -1, -1, sizeof(::rtech::liveapi::CustomMatch_SetTeam)}, + { 559, -1, -1, sizeof(::rtech::liveapi::CustomMatch_KickPlayer)}, + { 567, -1, -1, sizeof(::rtech::liveapi::CustomMatch_SetSettings)}, + { 579, -1, -1, sizeof(::rtech::liveapi::CustomMatch_GetSettings)}, + { 585, -1, -1, sizeof(::rtech::liveapi::CustomMatch_SetTeamName)}, + { 593, -1, -1, sizeof(::rtech::liveapi::CustomMatch_SendChat)}, + { 600, -1, -1, sizeof(::rtech::liveapi::Request)}, + { 623, -1, -1, sizeof(::rtech::liveapi::RequestStatus)}, + { 630, -1, -1, sizeof(::rtech::liveapi::Response)}, + { 638, -1, -1, sizeof(::rtech::liveapi::LiveAPIEvent)}, +}; + +static const ::_pb::Message* const file_default_instances[] = { + &::rtech::liveapi::_Vector3_default_instance_._instance, + &::rtech::liveapi::_Player_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_LobbyPlayer_default_instance_._instance, + &::rtech::liveapi::_Datacenter_default_instance_._instance, + &::rtech::liveapi::_Version_default_instance_._instance, + &::rtech::liveapi::_InventoryItem_default_instance_._instance, + &::rtech::liveapi::_LoadoutConfiguration_default_instance_._instance, + &::rtech::liveapi::_Init_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_LobbyPlayers_default_instance_._instance, + &::rtech::liveapi::_ObserverSwitched_default_instance_._instance, + &::rtech::liveapi::_ObserverAnnotation_default_instance_._instance, + &::rtech::liveapi::_MatchSetup_default_instance_._instance, + &::rtech::liveapi::_GameStateChanged_default_instance_._instance, + &::rtech::liveapi::_CharacterSelected_default_instance_._instance, + &::rtech::liveapi::_MatchStateEnd_default_instance_._instance, + &::rtech::liveapi::_RingStartClosing_default_instance_._instance, + &::rtech::liveapi::_RingFinishedClosing_default_instance_._instance, + &::rtech::liveapi::_PlayerConnected_default_instance_._instance, + &::rtech::liveapi::_PlayerDisconnected_default_instance_._instance, + &::rtech::liveapi::_PlayerStatChanged_default_instance_._instance, + &::rtech::liveapi::_PlayerUpgradeTierChanged_default_instance_._instance, + &::rtech::liveapi::_PlayerDamaged_default_instance_._instance, + &::rtech::liveapi::_PlayerKilled_default_instance_._instance, + &::rtech::liveapi::_PlayerDowned_default_instance_._instance, + &::rtech::liveapi::_PlayerAssist_default_instance_._instance, + &::rtech::liveapi::_SquadEliminated_default_instance_._instance, + &::rtech::liveapi::_GibraltarShieldAbsorbed_default_instance_._instance, + &::rtech::liveapi::_RevenantForgedShadowDamaged_default_instance_._instance, + &::rtech::liveapi::_PlayerRespawnTeam_default_instance_._instance, + &::rtech::liveapi::_PlayerRevive_default_instance_._instance, + &::rtech::liveapi::_ArenasItemSelected_default_instance_._instance, + &::rtech::liveapi::_ArenasItemDeselected_default_instance_._instance, + &::rtech::liveapi::_InventoryPickUp_default_instance_._instance, + &::rtech::liveapi::_InventoryDrop_default_instance_._instance, + &::rtech::liveapi::_InventoryUse_default_instance_._instance, + &::rtech::liveapi::_BannerCollected_default_instance_._instance, + &::rtech::liveapi::_PlayerAbilityUsed_default_instance_._instance, + &::rtech::liveapi::_LegendUpgradeSelected_default_instance_._instance, + &::rtech::liveapi::_ZiplineUsed_default_instance_._instance, + &::rtech::liveapi::_GrenadeThrown_default_instance_._instance, + &::rtech::liveapi::_BlackMarketAction_default_instance_._instance, + &::rtech::liveapi::_WraithPortal_default_instance_._instance, + &::rtech::liveapi::_WarpGateUsed_default_instance_._instance, + &::rtech::liveapi::_AmmoUsed_default_instance_._instance, + &::rtech::liveapi::_WeaponSwitched_default_instance_._instance, + &::rtech::liveapi::_CustomEvent_default_instance_._instance, + &::rtech::liveapi::_ChangeCamera_default_instance_._instance, + &::rtech::liveapi::_PauseToggle_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_CreateLobby_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_JoinLobby_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_LeaveLobby_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_SetReady_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_GetLobbyPlayers_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_SetMatchmaking_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_SetTeam_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_KickPlayer_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_SetSettings_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_GetSettings_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_SetTeamName_default_instance_._instance, + &::rtech::liveapi::_CustomMatch_SendChat_default_instance_._instance, + &::rtech::liveapi::_Request_default_instance_._instance, + &::rtech::liveapi::_RequestStatus_default_instance_._instance, + &::rtech::liveapi::_Response_default_instance_._instance, + &::rtech::liveapi::_LiveAPIEvent_default_instance_._instance, +}; + +const char descriptor_table_protodef_events_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\014events.proto\022\rrtech.liveapi\032\034google/pr" + "otobuf/struct.proto\032\031google/protobuf/any" + ".proto\"*\n\007Vector3\022\t\n\001x\030\001 \001(\002\022\t\n\001y\030\002 \001(\002\022" + "\t\n\001z\030\003 \001(\002\"\276\002\n\006Player\022\014\n\004name\030\001 \001(\t\022\016\n\006t" + "eamId\030\002 \001(\r\022#\n\003pos\030\003 \001(\0132\026.rtech.liveapi" + ".Vector3\022&\n\006angles\030\004 \001(\0132\026.rtech.liveapi" + ".Vector3\022\025\n\rcurrentHealth\030\005 \001(\r\022\021\n\tmaxHe" + "alth\030\006 \001(\r\022\024\n\014shieldHealth\030\007 \001(\r\022\027\n\017shie" + "ldMaxHealth\030\010 \001(\r\022\023\n\013nucleusHash\030\t \001(\t\022\024" + "\n\014hardwareName\030\n \001(\t\022\020\n\010teamName\030\013 \001(\t\022\022" + "\n\nsquadIndex\030\014 \001(\r\022\021\n\tcharacter\030\r \001(\t\022\014\n" + "\004skin\030\016 \001(\t\"b\n\027CustomMatch_LobbyPlayer\022\014" + "\n\004name\030\001 \001(\t\022\016\n\006teamId\030\002 \001(\r\022\023\n\013nucleusH" + "ash\030\003 \001(\t\022\024\n\014hardwareName\030\004 \001(\t\"\?\n\nDatac" + "enter\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001" + "(\t\022\014\n\004name\030\003 \001(\t\"V\n\007Version\022\021\n\tmajor_num" + "\030\001 \001(\r\022\021\n\tminor_num\030\002 \001(\r\022\023\n\013build_stamp" + "\030\003 \001(\r\022\020\n\010revision\030\004 \001(\t\"B\n\rInventoryIte" + "m\022\020\n\010quantity\030\001 \001(\005\022\014\n\004item\030\002 \001(\t\022\021\n\text" + "raData\030\003 \001(\t\"v\n\024LoadoutConfiguration\022-\n\007" + "weapons\030\001 \003(\0132\034.rtech.liveapi.InventoryI" + "tem\022/\n\tequipment\030\002 \003(\0132\034.rtech.liveapi.I" + "nventoryItem\"\214\001\n\004Init\022\021\n\ttimestamp\030\001 \001(\004" + "\022\020\n\010category\030\002 \001(\t\022\023\n\013gameVersion\030\003 \001(\t\022" + "*\n\napiVersion\030\004 \001(\0132\026.rtech.liveapi.Vers" + "ion\022\020\n\010platform\030\005 \001(\t\022\014\n\004name\030\006 \001(\t\"h\n\030C" + "ustomMatch_LobbyPlayers\022\023\n\013playerToken\030\001" + " \001(\t\0227\n\007players\030\002 \003(\0132&.rtech.liveapi.Cu" + "stomMatch_LobbyPlayer\"\262\001\n\020ObserverSwitch" + "ed\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022" + "\'\n\010observer\030\003 \001(\0132\025.rtech.liveapi.Player" + "\022%\n\006target\030\004 \001(\0132\025.rtech.liveapi.Player\022" + ")\n\ntargetTeam\030\005 \003(\0132\025.rtech.liveapi.Play" + "er\"S\n\022ObserverAnnotation\022\021\n\ttimestamp\030\001 " + "\001(\004\022\020\n\010category\030\002 \001(\t\022\030\n\020annotationSeria" + "l\030\003 \001(\005\"\225\002\n\nMatchSetup\022\021\n\ttimestamp\030\001 \001(" + "\004\022\020\n\010category\030\002 \001(\t\022\013\n\003map\030\003 \001(\t\022\024\n\014play" + "listName\030\004 \001(\t\022\024\n\014playlistDesc\030\005 \001(\t\022-\n\n" + "datacenter\030\006 \001(\0132\031.rtech.liveapi.Datacen" + "ter\022\023\n\013aimAssistOn\030\007 \001(\010\022\025\n\ranonymousMod" + "e\030\010 \001(\010\022\020\n\010serverId\030\t \001(\t\022<\n\017startingLoa" + "dout\030\n \001(\0132#.rtech.liveapi.LoadoutConfig" + "uration\"F\n\020GameStateChanged\022\021\n\ttimestamp" + "\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022\r\n\005state\030\003 \001(\t\"" + "_\n\021CharacterSelected\022\021\n\ttimestamp\030\001 \001(\004\022" + "\020\n\010category\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025.rtec" + "h.liveapi.Player\"k\n\rMatchStateEnd\022\021\n\ttim" + "estamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022\r\n\005state\030" + "\003 \001(\t\022&\n\007winners\030\004 \003(\0132\025.rtech.liveapi.P" + "layer\"\260\001\n\020RingStartClosing\022\021\n\ttimestamp\030" + "\001 \001(\004\022\020\n\010category\030\002 \001(\t\022\r\n\005stage\030\003 \001(\r\022&" + "\n\006center\030\004 \001(\0132\026.rtech.liveapi.Vector3\022\025" + "\n\rcurrentRadius\030\005 \001(\002\022\021\n\tendRadius\030\006 \001(\002" + "\022\026\n\016shrinkDuration\030\007 \001(\002\"\240\001\n\023RingFinishe" + "dClosing\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030" + "\002 \001(\t\022\r\n\005stage\030\003 \001(\r\022&\n\006center\030\004 \001(\0132\026.r" + "tech.liveapi.Vector3\022\025\n\rcurrentRadius\030\005 " + "\001(\002\022\026\n\016shrinkDuration\030\007 \001(\002\"]\n\017PlayerCon" + "nected\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 " + "\001(\t\022%\n\006player\030\003 \001(\0132\025.rtech.liveapi.Play" + "er\"\207\001\n\022PlayerDisconnected\022\021\n\ttimestamp\030\001" + " \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025" + ".rtech.liveapi.Player\022\024\n\014canReconnect\030\004 " + "\001(\010\022\017\n\007isAlive\030\005 \001(\010\"\274\001\n\021PlayerStatChang" + "ed\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022" + "%\n\006player\030\003 \001(\0132\025.rtech.liveapi.Player\022\020" + "\n\010statName\030\004 \001(\t\022\022\n\010intValue\030\005 \001(\rH\000\022\024\n\n" + "floatValue\030\006 \001(\002H\000\022\023\n\tboolValue\030\007 \001(\010H\000B" + "\n\n\010newValue\"u\n\030PlayerUpgradeTierChanged\022" + "\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006" + "player\030\003 \001(\0132\025.rtech.liveapi.Player\022\r\n\005l" + "evel\030\004 \001(\005\"\255\001\n\rPlayerDamaged\022\021\n\ttimestam" + "p\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022\'\n\010attacker\030\003 " + "\001(\0132\025.rtech.liveapi.Player\022%\n\006victim\030\004 \001" + "(\0132\025.rtech.liveapi.Player\022\016\n\006weapon\030\005 \001(" + "\t\022\027\n\017damageInflicted\030\006 \001(\r\"\275\001\n\014PlayerKil" + "led\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t" + "\022\'\n\010attacker\030\003 \001(\0132\025.rtech.liveapi.Playe" + "r\022%\n\006victim\030\004 \001(\0132\025.rtech.liveapi.Player" + "\022(\n\tawardedTo\030\005 \001(\0132\025.rtech.liveapi.Play" + "er\022\016\n\006weapon\030\006 \001(\t\"\223\001\n\014PlayerDowned\022\021\n\tt" + "imestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022\'\n\010atta" + "cker\030\003 \001(\0132\025.rtech.liveapi.Player\022%\n\006vic" + "tim\030\004 \001(\0132\025.rtech.liveapi.Player\022\016\n\006weap" + "on\030\005 \001(\t\"\224\001\n\014PlayerAssist\022\021\n\ttimestamp\030\001" + " \001(\004\022\020\n\010category\030\002 \001(\t\022(\n\tassistant\030\003 \001(" + "\0132\025.rtech.liveapi.Player\022%\n\006victim\030\004 \001(\013" + "2\025.rtech.liveapi.Player\022\016\n\006weapon\030\005 \001(\t\"" + "^\n\017SquadEliminated\022\021\n\ttimestamp\030\001 \001(\004\022\020\n" + "\010category\030\002 \001(\t\022&\n\007players\030\003 \003(\0132\025.rtech" + ".liveapi.Player\"\247\001\n\027GibraltarShieldAbsor" + "bed\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t" + "\022\'\n\010attacker\030\003 \001(\0132\025.rtech.liveapi.Playe" + "r\022%\n\006victim\030\004 \001(\0132\025.rtech.liveapi.Player" + "\022\027\n\017damageInflicted\030\006 \001(\r\"\253\001\n\033RevenantFo" + "rgedShadowDamaged\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010" + "category\030\002 \001(\t\022\'\n\010attacker\030\003 \001(\0132\025.rtech" + ".liveapi.Player\022%\n\006victim\030\004 \001(\0132\025.rtech." + "liveapi.Player\022\027\n\017damageInflicted\030\006 \001(\r\"" + "\211\001\n\021PlayerRespawnTeam\022\021\n\ttimestamp\030\001 \001(\004" + "\022\020\n\010category\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025.rte" + "ch.liveapi.Player\022(\n\trespawned\030\004 \003(\0132\025.r" + "tech.liveapi.Player\"\202\001\n\014PlayerRevive\022\021\n\t" + "timestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006pla" + "yer\030\003 \001(\0132\025.rtech.liveapi.Player\022&\n\007revi" + "ved\030\004 \001(\0132\025.rtech.liveapi.Player\"\200\001\n\022Are" + "nasItemSelected\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010ca" + "tegory\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025.rtech.liv" + "eapi.Player\022\014\n\004item\030\004 \001(\t\022\020\n\010quantity\030\005 " + "\001(\005\"\202\001\n\024ArenasItemDeselected\022\021\n\ttimestam" + "p\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006player\030\003 \001(" + "\0132\025.rtech.liveapi.Player\022\014\n\004item\030\004 \001(\t\022\020" + "\n\010quantity\030\005 \001(\005\"}\n\017InventoryPickUp\022\021\n\tt" + "imestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006play" + "er\030\003 \001(\0132\025.rtech.liveapi.Player\022\014\n\004item\030" + "\004 \001(\t\022\020\n\010quantity\030\005 \001(\005\"\216\001\n\rInventoryDro" + "p\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%" + "\n\006player\030\003 \001(\0132\025.rtech.liveapi.Player\022\014\n" + "\004item\030\004 \001(\t\022\020\n\010quantity\030\005 \001(\005\022\021\n\textraDa" + "ta\030\006 \003(\t\"z\n\014InventoryUse\022\021\n\ttimestamp\030\001 " + "\001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025." + "rtech.liveapi.Player\022\014\n\004item\030\004 \001(\t\022\020\n\010qu" + "antity\030\005 \001(\005\"\207\001\n\017BannerCollected\022\021\n\ttime" + "stamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006player\030" + "\003 \001(\0132\025.rtech.liveapi.Player\022(\n\tcollecte" + "d\030\004 \001(\0132\025.rtech.liveapi.Player\"u\n\021Player" + "AbilityUsed\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010catego" + "ry\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025.rtech.liveapi" + ".Player\022\024\n\014linkedEntity\030\004 \001(\t\"\234\001\n\025Legend" + "UpgradeSelected\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010ca" + "tegory\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025.rtech.liv" + "eapi.Player\022\023\n\013upgradeName\030\004 \001(\t\022\023\n\013upgr" + "adeDesc\030\005 \001(\t\022\r\n\005level\030\006 \001(\005\"o\n\013ZiplineU" + "sed\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t" + "\022%\n\006player\030\003 \001(\0132\025.rtech.liveapi.Player\022" + "\024\n\014linkedEntity\030\004 \001(\t\"q\n\rGrenadeThrown\022\021" + "\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006p" + "layer\030\003 \001(\0132\025.rtech.liveapi.Player\022\024\n\014li" + "nkedEntity\030\004 \001(\t\"m\n\021BlackMarketAction\022\021\n" + "\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006pl" + "ayer\030\003 \001(\0132\025.rtech.liveapi.Player\022\014\n\004ite" + "m\030\004 \001(\t\"Z\n\014WraithPortal\022\021\n\ttimestamp\030\001 \001" + "(\004\022\020\n\010category\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025.r" + "tech.liveapi.Player\"Z\n\014WarpGateUsed\022\021\n\tt" + "imestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022%\n\006play" + "er\030\003 \001(\0132\025.rtech.liveapi.Player\"\250\001\n\010Ammo" + "Used\022\021\n\ttimestamp\030\001 \001(\004\022\020\n\010category\030\002 \001(" + "\t\022%\n\006player\030\003 \001(\0132\025.rtech.liveapi.Player" + "\022\020\n\010ammoType\030\004 \001(\t\022\022\n\namountUsed\030\005 \001(\r\022\024" + "\n\014oldAmmoCount\030\006 \001(\r\022\024\n\014newAmmoCount\030\007 \001" + "(\r\"\202\001\n\016WeaponSwitched\022\021\n\ttimestamp\030\001 \001(\004" + "\022\020\n\010category\030\002 \001(\t\022%\n\006player\030\003 \001(\0132\025.rte" + "ch.liveapi.Player\022\021\n\toldWeapon\030\004 \001(\t\022\021\n\t" + "newWeapon\030\005 \001(\t\"g\n\013CustomEvent\022\021\n\ttimest" + "amp\030\001 \001(\004\022\020\n\010category\030\002 \001(\t\022\014\n\004name\030\003 \001(" + "\t\022%\n\004data\030\004 \001(\0132\027.google.protobuf.Struct" + "\"X\n\014ChangeCamera\022.\n\003poi\030\001 \001(\0162\037.rtech.li" + "veapi.PlayerOfInterestH\000\022\016\n\004name\030\002 \001(\tH\000" + "B\010\n\006target\"\037\n\013PauseToggle\022\020\n\010preTimer\030\001 " + "\001(\002\"\031\n\027CustomMatch_CreateLobby\"*\n\025Custom" + "Match_JoinLobby\022\021\n\troleToken\030\001 \001(\t\"\030\n\026Cu" + "stomMatch_LeaveLobby\"\'\n\024CustomMatch_SetR" + "eady\022\017\n\007isReady\030\001 \001(\010\"\035\n\033CustomMatch_Get" + "LobbyPlayers\"-\n\032CustomMatch_SetMatchmaki" + "ng\022\017\n\007enabled\030\001 \001(\010\"\\\n\023CustomMatch_SetTe" + "am\022\016\n\006teamId\030\001 \001(\005\022\032\n\022targetHardwareName" + "\030\002 \001(\t\022\031\n\021targetNucleusHash\030\003 \001(\t\"O\n\026Cus" + "tomMatch_KickPlayer\022\032\n\022targetHardwareNam" + "e\030\001 \001(\t\022\031\n\021targetNucleusHash\030\002 \001(\t\"\217\001\n\027C" + "ustomMatch_SetSettings\022\024\n\014playlistName\030\001" + " \001(\t\022\021\n\tadminChat\030\002 \001(\010\022\022\n\nteamRename\030\003 " + "\001(\010\022\022\n\nselfAssign\030\004 \001(\010\022\021\n\taimAssist\030\005 \001" + "(\010\022\020\n\010anonMode\030\006 \001(\010\"\031\n\027CustomMatch_GetS" + "ettings\";\n\027CustomMatch_SetTeamName\022\016\n\006te" + "amId\030\001 \001(\005\022\020\n\010teamName\030\002 \001(\t\"$\n\024CustomMa" + "tch_SendChat\022\014\n\004text\030\001 \001(\t\"\226\010\n\007Request\022\017" + "\n\007withAck\030\001 \001(\010\022\024\n\014preSharedKey\030\002 \001(\t\0220\n" + "\tchangeCam\030\004 \001(\0132\033.rtech.liveapi.ChangeC" + "ameraH\000\0221\n\013pauseToggle\030\005 \001(\0132\032.rtech.liv" + "eapi.PauseToggleH\000\022I\n\027customMatch_Create" + "Lobby\030\n \001(\0132&.rtech.liveapi.CustomMatch_" + "CreateLobbyH\000\022E\n\025customMatch_JoinLobby\030\013" + " \001(\0132$.rtech.liveapi.CustomMatch_JoinLob" + "byH\000\022G\n\026customMatch_LeaveLobby\030\014 \001(\0132%.r" + "tech.liveapi.CustomMatch_LeaveLobbyH\000\022C\n" + "\024customMatch_SetReady\030\r \001(\0132#.rtech.live" + "api.CustomMatch_SetReadyH\000\022O\n\032customMatc" + "h_SetMatchmaking\030\016 \001(\0132).rtech.liveapi.C" + "ustomMatch_SetMatchmakingH\000\022A\n\023customMat" + "ch_SetTeam\030\017 \001(\0132\".rtech.liveapi.CustomM" + "atch_SetTeamH\000\022G\n\026customMatch_KickPlayer" + "\030\020 \001(\0132%.rtech.liveapi.CustomMatch_KickP" + "layerH\000\022I\n\027customMatch_SetSettings\030\021 \001(\013" + "2&.rtech.liveapi.CustomMatch_SetSettings" + "H\000\022C\n\024customMatch_SendChat\030\022 \001(\0132#.rtech" + ".liveapi.CustomMatch_SendChatH\000\022Q\n\033custo" + "mMatch_GetLobbyPlayers\030\023 \001(\0132*.rtech.liv" + "eapi.CustomMatch_GetLobbyPlayersH\000\022I\n\027cu" + "stomMatch_SetTeamName\030\024 \001(\0132&.rtech.live" + "api.CustomMatch_SetTeamNameH\000\022I\n\027customM" + "atch_GetSettings\030\025 \001(\0132&.rtech.liveapi.C" + "ustomMatch_GetSettingsH\000B\t\n\007actions\"\037\n\rR" + "equestStatus\022\016\n\006status\030\001 \001(\t\"A\n\010Response" + "\022\017\n\007success\030\001 \001(\010\022$\n\006result\030\002 \001(\0132\024.goog" + "le.protobuf.Any\"M\n\014LiveAPIEvent\022\022\n\nevent" + "_size\030\001 \001(\007\022)\n\013gameMessage\030\003 \001(\0132\024.googl" + "e.protobuf.Any*\210\001\n\020PlayerOfInterest\022\017\n\013U" + "NSPECIFIED\020\000\022\010\n\004NEXT\020\001\022\014\n\010PREVIOUS\020\002\022\017\n\013" + "KILL_LEADER\020\003\022\021\n\rCLOSEST_ENEMY\020\004\022\022\n\016CLOS" + "EST_PLAYER\020\005\022\023\n\017LATEST_ATTACKER\020\006b\006proto" + "3" + ; +static const ::_pbi::DescriptorTable* const descriptor_table_events_2eproto_deps[2] = { + &::descriptor_table_google_2fprotobuf_2fany_2eproto, + &::descriptor_table_google_2fprotobuf_2fstruct_2eproto, +}; +static ::_pbi::once_flag descriptor_table_events_2eproto_once; +const ::_pbi::DescriptorTable descriptor_table_events_2eproto = { + false, false, 8401, descriptor_table_protodef_events_2eproto, + "events.proto", + &descriptor_table_events_2eproto_once, descriptor_table_events_2eproto_deps, 2, 64, + schemas, file_default_instances, TableStruct_events_2eproto::offsets, + file_level_metadata_events_2eproto, file_level_enum_descriptors_events_2eproto, + file_level_service_descriptors_events_2eproto, +}; +PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_events_2eproto_getter() { + return &descriptor_table_events_2eproto; +} + +// Force running AddDescriptors() at dynamic initialization time. +PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_events_2eproto(&descriptor_table_events_2eproto); +namespace rtech { +namespace liveapi { +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PlayerOfInterest_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_events_2eproto); + return file_level_enum_descriptors_events_2eproto[0]; +} +bool PlayerOfInterest_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + default: + return false; + } +} + + +// =================================================================== + +class Vector3::_Internal { + public: +}; + +Vector3::Vector3(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.Vector3) +} +Vector3::Vector3(const Vector3& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + Vector3* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.x_){} + , decltype(_impl_.y_){} + , decltype(_impl_.z_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::memcpy(&_impl_.x_, &from._impl_.x_, + static_cast(reinterpret_cast(&_impl_.z_) - + reinterpret_cast(&_impl_.x_)) + sizeof(_impl_.z_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.Vector3) +} + +inline void Vector3::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.x_){0} + , decltype(_impl_.y_){0} + , decltype(_impl_.z_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; +} + +Vector3::~Vector3() { + // @@protoc_insertion_point(destructor:rtech.liveapi.Vector3) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void Vector3::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); +} + +void Vector3::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void Vector3::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.Vector3) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + ::memset(&_impl_.x_, 0, static_cast( + reinterpret_cast(&_impl_.z_) - + reinterpret_cast(&_impl_.x_)) + sizeof(_impl_.z_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* Vector3::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // float x = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 13)) { + _impl_.x_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + // float y = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 21)) { + _impl_.y_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + // float z = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 29)) { + _impl_.z_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* Vector3::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.Vector3) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // float x = 1; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_x = this->_internal_x(); + uint32_t raw_x; + memcpy(&raw_x, &tmp_x, sizeof(tmp_x)); + if (raw_x != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(1, this->_internal_x(), target); + } + + // float y = 2; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_y = this->_internal_y(); + uint32_t raw_y; + memcpy(&raw_y, &tmp_y, sizeof(tmp_y)); + if (raw_y != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(2, this->_internal_y(), target); + } + + // float z = 3; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_z = this->_internal_z(); + uint32_t raw_z; + memcpy(&raw_z, &tmp_z, sizeof(tmp_z)); + if (raw_z != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(3, this->_internal_z(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.Vector3) + return target; +} + +size_t Vector3::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.Vector3) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // float x = 1; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_x = this->_internal_x(); + uint32_t raw_x; + memcpy(&raw_x, &tmp_x, sizeof(tmp_x)); + if (raw_x != 0) { + total_size += 1 + 4; + } + + // float y = 2; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_y = this->_internal_y(); + uint32_t raw_y; + memcpy(&raw_y, &tmp_y, sizeof(tmp_y)); + if (raw_y != 0) { + total_size += 1 + 4; + } + + // float z = 3; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_z = this->_internal_z(); + uint32_t raw_z; + memcpy(&raw_z, &tmp_z, sizeof(tmp_z)); + if (raw_z != 0) { + total_size += 1 + 4; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Vector3::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + Vector3::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Vector3::GetClassData() const { return &_class_data_; } + + +void Vector3::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.Vector3) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_x = from._internal_x(); + uint32_t raw_x; + memcpy(&raw_x, &tmp_x, sizeof(tmp_x)); + if (raw_x != 0) { + _this->_internal_set_x(from._internal_x()); + } + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_y = from._internal_y(); + uint32_t raw_y; + memcpy(&raw_y, &tmp_y, sizeof(tmp_y)); + if (raw_y != 0) { + _this->_internal_set_y(from._internal_y()); + } + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_z = from._internal_z(); + uint32_t raw_z; + memcpy(&raw_z, &tmp_z, sizeof(tmp_z)); + if (raw_z != 0) { + _this->_internal_set_z(from._internal_z()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void Vector3::CopyFrom(const Vector3& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.Vector3) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Vector3::IsInitialized() const { + return true; +} + +void Vector3::InternalSwap(Vector3* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(Vector3, _impl_.z_) + + sizeof(Vector3::_impl_.z_) + - PROTOBUF_FIELD_OFFSET(Vector3, _impl_.x_)>( + reinterpret_cast(&_impl_.x_), + reinterpret_cast(&other->_impl_.x_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata Vector3::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[0]); +} + +// =================================================================== + +class Player::_Internal { + public: + static const ::rtech::liveapi::Vector3& pos(const Player* msg); + static const ::rtech::liveapi::Vector3& angles(const Player* msg); +}; + +const ::rtech::liveapi::Vector3& +Player::_Internal::pos(const Player* msg) { + return *msg->_impl_.pos_; +} +const ::rtech::liveapi::Vector3& +Player::_Internal::angles(const Player* msg) { + return *msg->_impl_.angles_; +} +Player::Player(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.Player) +} +Player::Player(const Player& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + Player* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.name_){} + , decltype(_impl_.nucleushash_){} + , decltype(_impl_.hardwarename_){} + , decltype(_impl_.teamname_){} + , decltype(_impl_.character_){} + , decltype(_impl_.skin_){} + , decltype(_impl_.pos_){nullptr} + , decltype(_impl_.angles_){nullptr} + , decltype(_impl_.teamid_){} + , decltype(_impl_.currenthealth_){} + , decltype(_impl_.maxhealth_){} + , decltype(_impl_.shieldhealth_){} + , decltype(_impl_.shieldmaxhealth_){} + , decltype(_impl_.squadindex_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_name().empty()) { + _this->_impl_.name_.Set(from._internal_name(), + _this->GetArenaForAllocation()); + } + _impl_.nucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.nucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_nucleushash().empty()) { + _this->_impl_.nucleushash_.Set(from._internal_nucleushash(), + _this->GetArenaForAllocation()); + } + _impl_.hardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.hardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_hardwarename().empty()) { + _this->_impl_.hardwarename_.Set(from._internal_hardwarename(), + _this->GetArenaForAllocation()); + } + _impl_.teamname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.teamname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_teamname().empty()) { + _this->_impl_.teamname_.Set(from._internal_teamname(), + _this->GetArenaForAllocation()); + } + _impl_.character_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.character_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_character().empty()) { + _this->_impl_.character_.Set(from._internal_character(), + _this->GetArenaForAllocation()); + } + _impl_.skin_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.skin_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_skin().empty()) { + _this->_impl_.skin_.Set(from._internal_skin(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_pos()) { + _this->_impl_.pos_ = new ::rtech::liveapi::Vector3(*from._impl_.pos_); + } + if (from._internal_has_angles()) { + _this->_impl_.angles_ = new ::rtech::liveapi::Vector3(*from._impl_.angles_); + } + ::memcpy(&_impl_.teamid_, &from._impl_.teamid_, + static_cast(reinterpret_cast(&_impl_.squadindex_) - + reinterpret_cast(&_impl_.teamid_)) + sizeof(_impl_.squadindex_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.Player) +} + +inline void Player::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.name_){} + , decltype(_impl_.nucleushash_){} + , decltype(_impl_.hardwarename_){} + , decltype(_impl_.teamname_){} + , decltype(_impl_.character_){} + , decltype(_impl_.skin_){} + , decltype(_impl_.pos_){nullptr} + , decltype(_impl_.angles_){nullptr} + , decltype(_impl_.teamid_){0u} + , decltype(_impl_.currenthealth_){0u} + , decltype(_impl_.maxhealth_){0u} + , decltype(_impl_.shieldhealth_){0u} + , decltype(_impl_.shieldmaxhealth_){0u} + , decltype(_impl_.squadindex_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.nucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.nucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.hardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.hardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.teamname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.teamname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.character_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.character_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.skin_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.skin_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +Player::~Player() { + // @@protoc_insertion_point(destructor:rtech.liveapi.Player) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void Player::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.name_.Destroy(); + _impl_.nucleushash_.Destroy(); + _impl_.hardwarename_.Destroy(); + _impl_.teamname_.Destroy(); + _impl_.character_.Destroy(); + _impl_.skin_.Destroy(); + if (this != internal_default_instance()) delete _impl_.pos_; + if (this != internal_default_instance()) delete _impl_.angles_; +} + +void Player::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void Player::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.Player) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.name_.ClearToEmpty(); + _impl_.nucleushash_.ClearToEmpty(); + _impl_.hardwarename_.ClearToEmpty(); + _impl_.teamname_.ClearToEmpty(); + _impl_.character_.ClearToEmpty(); + _impl_.skin_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.pos_ != nullptr) { + delete _impl_.pos_; + } + _impl_.pos_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.angles_ != nullptr) { + delete _impl_.angles_; + } + _impl_.angles_ = nullptr; + ::memset(&_impl_.teamid_, 0, static_cast( + reinterpret_cast(&_impl_.squadindex_) - + reinterpret_cast(&_impl_.teamid_)) + sizeof(_impl_.squadindex_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* Player::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string name = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_name(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Player.name")); + } else + goto handle_unusual; + continue; + // uint32 teamId = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _impl_.teamid_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Vector3 pos = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_pos(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Vector3 angles = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_angles(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 currentHealth = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.currenthealth_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 maxHealth = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _impl_.maxhealth_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 shieldHealth = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _impl_.shieldhealth_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 shieldMaxHealth = 8; + case 8: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 64)) { + _impl_.shieldmaxhealth_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string nucleusHash = 9; + case 9: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 74)) { + auto str = _internal_mutable_nucleushash(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Player.nucleusHash")); + } else + goto handle_unusual; + continue; + // string hardwareName = 10; + case 10: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 82)) { + auto str = _internal_mutable_hardwarename(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Player.hardwareName")); + } else + goto handle_unusual; + continue; + // string teamName = 11; + case 11: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 90)) { + auto str = _internal_mutable_teamname(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Player.teamName")); + } else + goto handle_unusual; + continue; + // uint32 squadIndex = 12; + case 12: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 96)) { + _impl_.squadindex_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string character = 13; + case 13: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 106)) { + auto str = _internal_mutable_character(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Player.character")); + } else + goto handle_unusual; + continue; + // string skin = 14; + case 14: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 114)) { + auto str = _internal_mutable_skin(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Player.skin")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* Player::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.Player) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string name = 1; + if (!this->_internal_name().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_name().data(), static_cast(this->_internal_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Player.name"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_name(), target); + } + + // uint32 teamId = 2; + if (this->_internal_teamid() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(2, this->_internal_teamid(), target); + } + + // .rtech.liveapi.Vector3 pos = 3; + if (this->_internal_has_pos()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::pos(this), + _Internal::pos(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Vector3 angles = 4; + if (this->_internal_has_angles()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::angles(this), + _Internal::angles(this).GetCachedSize(), target, stream); + } + + // uint32 currentHealth = 5; + if (this->_internal_currenthealth() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(5, this->_internal_currenthealth(), target); + } + + // uint32 maxHealth = 6; + if (this->_internal_maxhealth() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(6, this->_internal_maxhealth(), target); + } + + // uint32 shieldHealth = 7; + if (this->_internal_shieldhealth() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(7, this->_internal_shieldhealth(), target); + } + + // uint32 shieldMaxHealth = 8; + if (this->_internal_shieldmaxhealth() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(8, this->_internal_shieldmaxhealth(), target); + } + + // string nucleusHash = 9; + if (!this->_internal_nucleushash().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_nucleushash().data(), static_cast(this->_internal_nucleushash().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Player.nucleusHash"); + target = stream->WriteStringMaybeAliased( + 9, this->_internal_nucleushash(), target); + } + + // string hardwareName = 10; + if (!this->_internal_hardwarename().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_hardwarename().data(), static_cast(this->_internal_hardwarename().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Player.hardwareName"); + target = stream->WriteStringMaybeAliased( + 10, this->_internal_hardwarename(), target); + } + + // string teamName = 11; + if (!this->_internal_teamname().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_teamname().data(), static_cast(this->_internal_teamname().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Player.teamName"); + target = stream->WriteStringMaybeAliased( + 11, this->_internal_teamname(), target); + } + + // uint32 squadIndex = 12; + if (this->_internal_squadindex() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(12, this->_internal_squadindex(), target); + } + + // string character = 13; + if (!this->_internal_character().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_character().data(), static_cast(this->_internal_character().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Player.character"); + target = stream->WriteStringMaybeAliased( + 13, this->_internal_character(), target); + } + + // string skin = 14; + if (!this->_internal_skin().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_skin().data(), static_cast(this->_internal_skin().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Player.skin"); + target = stream->WriteStringMaybeAliased( + 14, this->_internal_skin(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.Player) + return target; +} + +size_t Player::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.Player) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string name = 1; + if (!this->_internal_name().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_name()); + } + + // string nucleusHash = 9; + if (!this->_internal_nucleushash().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_nucleushash()); + } + + // string hardwareName = 10; + if (!this->_internal_hardwarename().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_hardwarename()); + } + + // string teamName = 11; + if (!this->_internal_teamname().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_teamname()); + } + + // string character = 13; + if (!this->_internal_character().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_character()); + } + + // string skin = 14; + if (!this->_internal_skin().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_skin()); + } + + // .rtech.liveapi.Vector3 pos = 3; + if (this->_internal_has_pos()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.pos_); + } + + // .rtech.liveapi.Vector3 angles = 4; + if (this->_internal_has_angles()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.angles_); + } + + // uint32 teamId = 2; + if (this->_internal_teamid() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_teamid()); + } + + // uint32 currentHealth = 5; + if (this->_internal_currenthealth() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_currenthealth()); + } + + // uint32 maxHealth = 6; + if (this->_internal_maxhealth() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_maxhealth()); + } + + // uint32 shieldHealth = 7; + if (this->_internal_shieldhealth() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_shieldhealth()); + } + + // uint32 shieldMaxHealth = 8; + if (this->_internal_shieldmaxhealth() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_shieldmaxhealth()); + } + + // uint32 squadIndex = 12; + if (this->_internal_squadindex() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_squadindex()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Player::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + Player::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Player::GetClassData() const { return &_class_data_; } + + +void Player::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.Player) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_name().empty()) { + _this->_internal_set_name(from._internal_name()); + } + if (!from._internal_nucleushash().empty()) { + _this->_internal_set_nucleushash(from._internal_nucleushash()); + } + if (!from._internal_hardwarename().empty()) { + _this->_internal_set_hardwarename(from._internal_hardwarename()); + } + if (!from._internal_teamname().empty()) { + _this->_internal_set_teamname(from._internal_teamname()); + } + if (!from._internal_character().empty()) { + _this->_internal_set_character(from._internal_character()); + } + if (!from._internal_skin().empty()) { + _this->_internal_set_skin(from._internal_skin()); + } + if (from._internal_has_pos()) { + _this->_internal_mutable_pos()->::rtech::liveapi::Vector3::MergeFrom( + from._internal_pos()); + } + if (from._internal_has_angles()) { + _this->_internal_mutable_angles()->::rtech::liveapi::Vector3::MergeFrom( + from._internal_angles()); + } + if (from._internal_teamid() != 0) { + _this->_internal_set_teamid(from._internal_teamid()); + } + if (from._internal_currenthealth() != 0) { + _this->_internal_set_currenthealth(from._internal_currenthealth()); + } + if (from._internal_maxhealth() != 0) { + _this->_internal_set_maxhealth(from._internal_maxhealth()); + } + if (from._internal_shieldhealth() != 0) { + _this->_internal_set_shieldhealth(from._internal_shieldhealth()); + } + if (from._internal_shieldmaxhealth() != 0) { + _this->_internal_set_shieldmaxhealth(from._internal_shieldmaxhealth()); + } + if (from._internal_squadindex() != 0) { + _this->_internal_set_squadindex(from._internal_squadindex()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void Player::CopyFrom(const Player& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.Player) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Player::IsInitialized() const { + return true; +} + +void Player::InternalSwap(Player* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.name_, lhs_arena, + &other->_impl_.name_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.nucleushash_, lhs_arena, + &other->_impl_.nucleushash_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.hardwarename_, lhs_arena, + &other->_impl_.hardwarename_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.teamname_, lhs_arena, + &other->_impl_.teamname_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.character_, lhs_arena, + &other->_impl_.character_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.skin_, lhs_arena, + &other->_impl_.skin_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(Player, _impl_.squadindex_) + + sizeof(Player::_impl_.squadindex_) + - PROTOBUF_FIELD_OFFSET(Player, _impl_.pos_)>( + reinterpret_cast(&_impl_.pos_), + reinterpret_cast(&other->_impl_.pos_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata Player::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[1]); +} + +// =================================================================== + +class CustomMatch_LobbyPlayer::_Internal { + public: +}; + +CustomMatch_LobbyPlayer::CustomMatch_LobbyPlayer(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_LobbyPlayer) +} +CustomMatch_LobbyPlayer::CustomMatch_LobbyPlayer(const CustomMatch_LobbyPlayer& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_LobbyPlayer* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.name_){} + , decltype(_impl_.nucleushash_){} + , decltype(_impl_.hardwarename_){} + , decltype(_impl_.teamid_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_name().empty()) { + _this->_impl_.name_.Set(from._internal_name(), + _this->GetArenaForAllocation()); + } + _impl_.nucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.nucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_nucleushash().empty()) { + _this->_impl_.nucleushash_.Set(from._internal_nucleushash(), + _this->GetArenaForAllocation()); + } + _impl_.hardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.hardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_hardwarename().empty()) { + _this->_impl_.hardwarename_.Set(from._internal_hardwarename(), + _this->GetArenaForAllocation()); + } + _this->_impl_.teamid_ = from._impl_.teamid_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_LobbyPlayer) +} + +inline void CustomMatch_LobbyPlayer::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.name_){} + , decltype(_impl_.nucleushash_){} + , decltype(_impl_.hardwarename_){} + , decltype(_impl_.teamid_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.nucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.nucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.hardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.hardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_LobbyPlayer::~CustomMatch_LobbyPlayer() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_LobbyPlayer) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_LobbyPlayer::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.name_.Destroy(); + _impl_.nucleushash_.Destroy(); + _impl_.hardwarename_.Destroy(); +} + +void CustomMatch_LobbyPlayer::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_LobbyPlayer::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_LobbyPlayer) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.name_.ClearToEmpty(); + _impl_.nucleushash_.ClearToEmpty(); + _impl_.hardwarename_.ClearToEmpty(); + _impl_.teamid_ = 0u; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_LobbyPlayer::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string name = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_name(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_LobbyPlayer.name")); + } else + goto handle_unusual; + continue; + // uint32 teamId = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _impl_.teamid_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string nucleusHash = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_nucleushash(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_LobbyPlayer.nucleusHash")); + } else + goto handle_unusual; + continue; + // string hardwareName = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_hardwarename(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_LobbyPlayer.hardwareName")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_LobbyPlayer::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_LobbyPlayer) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string name = 1; + if (!this->_internal_name().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_name().data(), static_cast(this->_internal_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_LobbyPlayer.name"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_name(), target); + } + + // uint32 teamId = 2; + if (this->_internal_teamid() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(2, this->_internal_teamid(), target); + } + + // string nucleusHash = 3; + if (!this->_internal_nucleushash().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_nucleushash().data(), static_cast(this->_internal_nucleushash().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_LobbyPlayer.nucleusHash"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_nucleushash(), target); + } + + // string hardwareName = 4; + if (!this->_internal_hardwarename().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_hardwarename().data(), static_cast(this->_internal_hardwarename().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_LobbyPlayer.hardwareName"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_hardwarename(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_LobbyPlayer) + return target; +} + +size_t CustomMatch_LobbyPlayer::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_LobbyPlayer) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string name = 1; + if (!this->_internal_name().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_name()); + } + + // string nucleusHash = 3; + if (!this->_internal_nucleushash().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_nucleushash()); + } + + // string hardwareName = 4; + if (!this->_internal_hardwarename().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_hardwarename()); + } + + // uint32 teamId = 2; + if (this->_internal_teamid() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_teamid()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_LobbyPlayer::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_LobbyPlayer::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_LobbyPlayer::GetClassData() const { return &_class_data_; } + + +void CustomMatch_LobbyPlayer::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_LobbyPlayer) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_name().empty()) { + _this->_internal_set_name(from._internal_name()); + } + if (!from._internal_nucleushash().empty()) { + _this->_internal_set_nucleushash(from._internal_nucleushash()); + } + if (!from._internal_hardwarename().empty()) { + _this->_internal_set_hardwarename(from._internal_hardwarename()); + } + if (from._internal_teamid() != 0) { + _this->_internal_set_teamid(from._internal_teamid()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_LobbyPlayer::CopyFrom(const CustomMatch_LobbyPlayer& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_LobbyPlayer) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_LobbyPlayer::IsInitialized() const { + return true; +} + +void CustomMatch_LobbyPlayer::InternalSwap(CustomMatch_LobbyPlayer* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.name_, lhs_arena, + &other->_impl_.name_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.nucleushash_, lhs_arena, + &other->_impl_.nucleushash_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.hardwarename_, lhs_arena, + &other->_impl_.hardwarename_, rhs_arena + ); + swap(_impl_.teamid_, other->_impl_.teamid_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_LobbyPlayer::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[2]); +} + +// =================================================================== + +class Datacenter::_Internal { + public: +}; + +Datacenter::Datacenter(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.Datacenter) +} +Datacenter::Datacenter(const Datacenter& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + Datacenter* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.name_){} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_name().empty()) { + _this->_impl_.name_.Set(from._internal_name(), + _this->GetArenaForAllocation()); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.Datacenter) +} + +inline void Datacenter::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.name_){} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +Datacenter::~Datacenter() { + // @@protoc_insertion_point(destructor:rtech.liveapi.Datacenter) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void Datacenter::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.name_.Destroy(); +} + +void Datacenter::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void Datacenter::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.Datacenter) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.name_.ClearToEmpty(); + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* Datacenter::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Datacenter.category")); + } else + goto handle_unusual; + continue; + // string name = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_name(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Datacenter.name")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* Datacenter::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.Datacenter) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Datacenter.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // string name = 3; + if (!this->_internal_name().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_name().data(), static_cast(this->_internal_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Datacenter.name"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_name(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.Datacenter) + return target; +} + +size_t Datacenter::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.Datacenter) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string name = 3; + if (!this->_internal_name().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_name()); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Datacenter::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + Datacenter::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Datacenter::GetClassData() const { return &_class_data_; } + + +void Datacenter::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.Datacenter) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_name().empty()) { + _this->_internal_set_name(from._internal_name()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void Datacenter::CopyFrom(const Datacenter& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.Datacenter) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Datacenter::IsInitialized() const { + return true; +} + +void Datacenter::InternalSwap(Datacenter* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.name_, lhs_arena, + &other->_impl_.name_, rhs_arena + ); + swap(_impl_.timestamp_, other->_impl_.timestamp_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata Datacenter::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[3]); +} + +// =================================================================== + +class Version::_Internal { + public: +}; + +Version::Version(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.Version) +} +Version::Version(const Version& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + Version* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.revision_){} + , decltype(_impl_.major_num_){} + , decltype(_impl_.minor_num_){} + , decltype(_impl_.build_stamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.revision_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.revision_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_revision().empty()) { + _this->_impl_.revision_.Set(from._internal_revision(), + _this->GetArenaForAllocation()); + } + ::memcpy(&_impl_.major_num_, &from._impl_.major_num_, + static_cast(reinterpret_cast(&_impl_.build_stamp_) - + reinterpret_cast(&_impl_.major_num_)) + sizeof(_impl_.build_stamp_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.Version) +} + +inline void Version::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.revision_){} + , decltype(_impl_.major_num_){0u} + , decltype(_impl_.minor_num_){0u} + , decltype(_impl_.build_stamp_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.revision_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.revision_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +Version::~Version() { + // @@protoc_insertion_point(destructor:rtech.liveapi.Version) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void Version::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.revision_.Destroy(); +} + +void Version::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void Version::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.Version) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.revision_.ClearToEmpty(); + ::memset(&_impl_.major_num_, 0, static_cast( + reinterpret_cast(&_impl_.build_stamp_) - + reinterpret_cast(&_impl_.major_num_)) + sizeof(_impl_.build_stamp_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* Version::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint32 major_num = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.major_num_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 minor_num = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _impl_.minor_num_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 build_stamp = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _impl_.build_stamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string revision = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_revision(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Version.revision")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* Version::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.Version) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint32 major_num = 1; + if (this->_internal_major_num() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(1, this->_internal_major_num(), target); + } + + // uint32 minor_num = 2; + if (this->_internal_minor_num() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(2, this->_internal_minor_num(), target); + } + + // uint32 build_stamp = 3; + if (this->_internal_build_stamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(3, this->_internal_build_stamp(), target); + } + + // string revision = 4; + if (!this->_internal_revision().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_revision().data(), static_cast(this->_internal_revision().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Version.revision"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_revision(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.Version) + return target; +} + +size_t Version::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.Version) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string revision = 4; + if (!this->_internal_revision().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_revision()); + } + + // uint32 major_num = 1; + if (this->_internal_major_num() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_major_num()); + } + + // uint32 minor_num = 2; + if (this->_internal_minor_num() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_minor_num()); + } + + // uint32 build_stamp = 3; + if (this->_internal_build_stamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_build_stamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Version::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + Version::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Version::GetClassData() const { return &_class_data_; } + + +void Version::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.Version) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_revision().empty()) { + _this->_internal_set_revision(from._internal_revision()); + } + if (from._internal_major_num() != 0) { + _this->_internal_set_major_num(from._internal_major_num()); + } + if (from._internal_minor_num() != 0) { + _this->_internal_set_minor_num(from._internal_minor_num()); + } + if (from._internal_build_stamp() != 0) { + _this->_internal_set_build_stamp(from._internal_build_stamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void Version::CopyFrom(const Version& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.Version) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Version::IsInitialized() const { + return true; +} + +void Version::InternalSwap(Version* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.revision_, lhs_arena, + &other->_impl_.revision_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(Version, _impl_.build_stamp_) + + sizeof(Version::_impl_.build_stamp_) + - PROTOBUF_FIELD_OFFSET(Version, _impl_.major_num_)>( + reinterpret_cast(&_impl_.major_num_), + reinterpret_cast(&other->_impl_.major_num_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata Version::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[4]); +} + +// =================================================================== + +class InventoryItem::_Internal { + public: +}; + +InventoryItem::InventoryItem(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.InventoryItem) +} +InventoryItem::InventoryItem(const InventoryItem& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + InventoryItem* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.item_){} + , decltype(_impl_.extradata_){} + , decltype(_impl_.quantity_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_item().empty()) { + _this->_impl_.item_.Set(from._internal_item(), + _this->GetArenaForAllocation()); + } + _impl_.extradata_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.extradata_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_extradata().empty()) { + _this->_impl_.extradata_.Set(from._internal_extradata(), + _this->GetArenaForAllocation()); + } + _this->_impl_.quantity_ = from._impl_.quantity_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.InventoryItem) +} + +inline void InventoryItem::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.item_){} + , decltype(_impl_.extradata_){} + , decltype(_impl_.quantity_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.extradata_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.extradata_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +InventoryItem::~InventoryItem() { + // @@protoc_insertion_point(destructor:rtech.liveapi.InventoryItem) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void InventoryItem::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.item_.Destroy(); + _impl_.extradata_.Destroy(); +} + +void InventoryItem::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void InventoryItem::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.InventoryItem) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.item_.ClearToEmpty(); + _impl_.extradata_.ClearToEmpty(); + _impl_.quantity_ = 0; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* InventoryItem::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // int32 quantity = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.quantity_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string item = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_item(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryItem.item")); + } else + goto handle_unusual; + continue; + // string extraData = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_extradata(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryItem.extraData")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* InventoryItem::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.InventoryItem) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // int32 quantity = 1; + if (this->_internal_quantity() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_quantity(), target); + } + + // string item = 2; + if (!this->_internal_item().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_item().data(), static_cast(this->_internal_item().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryItem.item"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_item(), target); + } + + // string extraData = 3; + if (!this->_internal_extradata().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_extradata().data(), static_cast(this->_internal_extradata().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryItem.extraData"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_extradata(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.InventoryItem) + return target; +} + +size_t InventoryItem::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.InventoryItem) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string item = 2; + if (!this->_internal_item().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_item()); + } + + // string extraData = 3; + if (!this->_internal_extradata().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_extradata()); + } + + // int32 quantity = 1; + if (this->_internal_quantity() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_quantity()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData InventoryItem::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + InventoryItem::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*InventoryItem::GetClassData() const { return &_class_data_; } + + +void InventoryItem::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.InventoryItem) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_item().empty()) { + _this->_internal_set_item(from._internal_item()); + } + if (!from._internal_extradata().empty()) { + _this->_internal_set_extradata(from._internal_extradata()); + } + if (from._internal_quantity() != 0) { + _this->_internal_set_quantity(from._internal_quantity()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void InventoryItem::CopyFrom(const InventoryItem& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.InventoryItem) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool InventoryItem::IsInitialized() const { + return true; +} + +void InventoryItem::InternalSwap(InventoryItem* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.item_, lhs_arena, + &other->_impl_.item_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.extradata_, lhs_arena, + &other->_impl_.extradata_, rhs_arena + ); + swap(_impl_.quantity_, other->_impl_.quantity_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata InventoryItem::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[5]); +} + +// =================================================================== + +class LoadoutConfiguration::_Internal { + public: +}; + +LoadoutConfiguration::LoadoutConfiguration(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.LoadoutConfiguration) +} +LoadoutConfiguration::LoadoutConfiguration(const LoadoutConfiguration& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + LoadoutConfiguration* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.weapons_){from._impl_.weapons_} + , decltype(_impl_.equipment_){from._impl_.equipment_} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.LoadoutConfiguration) +} + +inline void LoadoutConfiguration::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.weapons_){arena} + , decltype(_impl_.equipment_){arena} + , /*decltype(_impl_._cached_size_)*/{} + }; +} + +LoadoutConfiguration::~LoadoutConfiguration() { + // @@protoc_insertion_point(destructor:rtech.liveapi.LoadoutConfiguration) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void LoadoutConfiguration::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.weapons_.~RepeatedPtrField(); + _impl_.equipment_.~RepeatedPtrField(); +} + +void LoadoutConfiguration::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void LoadoutConfiguration::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.LoadoutConfiguration) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.weapons_.Clear(); + _impl_.equipment_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* LoadoutConfiguration::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // repeated .rtech.liveapi.InventoryItem weapons = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + ptr -= 1; + do { + ptr += 1; + ptr = ctx->ParseMessage(_internal_add_weapons(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr)); + } else + goto handle_unusual; + continue; + // repeated .rtech.liveapi.InventoryItem equipment = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + ptr -= 1; + do { + ptr += 1; + ptr = ctx->ParseMessage(_internal_add_equipment(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* LoadoutConfiguration::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.LoadoutConfiguration) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // repeated .rtech.liveapi.InventoryItem weapons = 1; + for (unsigned i = 0, + n = static_cast(this->_internal_weapons_size()); i < n; i++) { + const auto& repfield = this->_internal_weapons(i); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream); + } + + // repeated .rtech.liveapi.InventoryItem equipment = 2; + for (unsigned i = 0, + n = static_cast(this->_internal_equipment_size()); i < n; i++) { + const auto& repfield = this->_internal_equipment(i); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.LoadoutConfiguration) + return target; +} + +size_t LoadoutConfiguration::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.LoadoutConfiguration) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated .rtech.liveapi.InventoryItem weapons = 1; + total_size += 1UL * this->_internal_weapons_size(); + for (const auto& msg : this->_impl_.weapons_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + // repeated .rtech.liveapi.InventoryItem equipment = 2; + total_size += 1UL * this->_internal_equipment_size(); + for (const auto& msg : this->_impl_.equipment_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData LoadoutConfiguration::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + LoadoutConfiguration::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*LoadoutConfiguration::GetClassData() const { return &_class_data_; } + + +void LoadoutConfiguration::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.LoadoutConfiguration) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + _this->_impl_.weapons_.MergeFrom(from._impl_.weapons_); + _this->_impl_.equipment_.MergeFrom(from._impl_.equipment_); + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void LoadoutConfiguration::CopyFrom(const LoadoutConfiguration& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.LoadoutConfiguration) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool LoadoutConfiguration::IsInitialized() const { + return true; +} + +void LoadoutConfiguration::InternalSwap(LoadoutConfiguration* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + _impl_.weapons_.InternalSwap(&other->_impl_.weapons_); + _impl_.equipment_.InternalSwap(&other->_impl_.equipment_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata LoadoutConfiguration::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[6]); +} + +// =================================================================== + +class Init::_Internal { + public: + static const ::rtech::liveapi::Version& apiversion(const Init* msg); +}; + +const ::rtech::liveapi::Version& +Init::_Internal::apiversion(const Init* msg) { + return *msg->_impl_.apiversion_; +} +Init::Init(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.Init) +} +Init::Init(const Init& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + Init* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.gameversion_){} + , decltype(_impl_.platform_){} + , decltype(_impl_.name_){} + , decltype(_impl_.apiversion_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.gameversion_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.gameversion_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_gameversion().empty()) { + _this->_impl_.gameversion_.Set(from._internal_gameversion(), + _this->GetArenaForAllocation()); + } + _impl_.platform_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.platform_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_platform().empty()) { + _this->_impl_.platform_.Set(from._internal_platform(), + _this->GetArenaForAllocation()); + } + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_name().empty()) { + _this->_impl_.name_.Set(from._internal_name(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_apiversion()) { + _this->_impl_.apiversion_ = new ::rtech::liveapi::Version(*from._impl_.apiversion_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.Init) +} + +inline void Init::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.gameversion_){} + , decltype(_impl_.platform_){} + , decltype(_impl_.name_){} + , decltype(_impl_.apiversion_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.gameversion_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.gameversion_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.platform_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.platform_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +Init::~Init() { + // @@protoc_insertion_point(destructor:rtech.liveapi.Init) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void Init::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.gameversion_.Destroy(); + _impl_.platform_.Destroy(); + _impl_.name_.Destroy(); + if (this != internal_default_instance()) delete _impl_.apiversion_; +} + +void Init::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void Init::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.Init) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.gameversion_.ClearToEmpty(); + _impl_.platform_.ClearToEmpty(); + _impl_.name_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.apiversion_ != nullptr) { + delete _impl_.apiversion_; + } + _impl_.apiversion_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* Init::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Init.category")); + } else + goto handle_unusual; + continue; + // string gameVersion = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_gameversion(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Init.gameVersion")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Version apiVersion = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_apiversion(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string platform = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_platform(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Init.platform")); + } else + goto handle_unusual; + continue; + // string name = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 50)) { + auto str = _internal_mutable_name(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Init.name")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* Init::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.Init) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Init.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // string gameVersion = 3; + if (!this->_internal_gameversion().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_gameversion().data(), static_cast(this->_internal_gameversion().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Init.gameVersion"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_gameversion(), target); + } + + // .rtech.liveapi.Version apiVersion = 4; + if (this->_internal_has_apiversion()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::apiversion(this), + _Internal::apiversion(this).GetCachedSize(), target, stream); + } + + // string platform = 5; + if (!this->_internal_platform().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_platform().data(), static_cast(this->_internal_platform().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Init.platform"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_platform(), target); + } + + // string name = 6; + if (!this->_internal_name().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_name().data(), static_cast(this->_internal_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Init.name"); + target = stream->WriteStringMaybeAliased( + 6, this->_internal_name(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.Init) + return target; +} + +size_t Init::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.Init) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string gameVersion = 3; + if (!this->_internal_gameversion().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_gameversion()); + } + + // string platform = 5; + if (!this->_internal_platform().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_platform()); + } + + // string name = 6; + if (!this->_internal_name().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_name()); + } + + // .rtech.liveapi.Version apiVersion = 4; + if (this->_internal_has_apiversion()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.apiversion_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Init::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + Init::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Init::GetClassData() const { return &_class_data_; } + + +void Init::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.Init) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_gameversion().empty()) { + _this->_internal_set_gameversion(from._internal_gameversion()); + } + if (!from._internal_platform().empty()) { + _this->_internal_set_platform(from._internal_platform()); + } + if (!from._internal_name().empty()) { + _this->_internal_set_name(from._internal_name()); + } + if (from._internal_has_apiversion()) { + _this->_internal_mutable_apiversion()->::rtech::liveapi::Version::MergeFrom( + from._internal_apiversion()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void Init::CopyFrom(const Init& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.Init) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Init::IsInitialized() const { + return true; +} + +void Init::InternalSwap(Init* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.gameversion_, lhs_arena, + &other->_impl_.gameversion_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.platform_, lhs_arena, + &other->_impl_.platform_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.name_, lhs_arena, + &other->_impl_.name_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(Init, _impl_.timestamp_) + + sizeof(Init::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(Init, _impl_.apiversion_)>( + reinterpret_cast(&_impl_.apiversion_), + reinterpret_cast(&other->_impl_.apiversion_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata Init::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[7]); +} + +// =================================================================== + +class CustomMatch_LobbyPlayers::_Internal { + public: +}; + +CustomMatch_LobbyPlayers::CustomMatch_LobbyPlayers(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_LobbyPlayers) +} +CustomMatch_LobbyPlayers::CustomMatch_LobbyPlayers(const CustomMatch_LobbyPlayers& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_LobbyPlayers* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.players_){from._impl_.players_} + , decltype(_impl_.playertoken_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.playertoken_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playertoken_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_playertoken().empty()) { + _this->_impl_.playertoken_.Set(from._internal_playertoken(), + _this->GetArenaForAllocation()); + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_LobbyPlayers) +} + +inline void CustomMatch_LobbyPlayers::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.players_){arena} + , decltype(_impl_.playertoken_){} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.playertoken_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playertoken_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_LobbyPlayers::~CustomMatch_LobbyPlayers() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_LobbyPlayers) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_LobbyPlayers::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.players_.~RepeatedPtrField(); + _impl_.playertoken_.Destroy(); +} + +void CustomMatch_LobbyPlayers::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_LobbyPlayers::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_LobbyPlayers) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.players_.Clear(); + _impl_.playertoken_.ClearToEmpty(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_LobbyPlayers::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string playerToken = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_playertoken(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_LobbyPlayers.playerToken")); + } else + goto handle_unusual; + continue; + // repeated .rtech.liveapi.CustomMatch_LobbyPlayer players = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + ptr -= 1; + do { + ptr += 1; + ptr = ctx->ParseMessage(_internal_add_players(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_LobbyPlayers::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_LobbyPlayers) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string playerToken = 1; + if (!this->_internal_playertoken().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_playertoken().data(), static_cast(this->_internal_playertoken().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_LobbyPlayers.playerToken"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_playertoken(), target); + } + + // repeated .rtech.liveapi.CustomMatch_LobbyPlayer players = 2; + for (unsigned i = 0, + n = static_cast(this->_internal_players_size()); i < n; i++) { + const auto& repfield = this->_internal_players(i); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(2, repfield, repfield.GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_LobbyPlayers) + return target; +} + +size_t CustomMatch_LobbyPlayers::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_LobbyPlayers) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated .rtech.liveapi.CustomMatch_LobbyPlayer players = 2; + total_size += 1UL * this->_internal_players_size(); + for (const auto& msg : this->_impl_.players_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + // string playerToken = 1; + if (!this->_internal_playertoken().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_playertoken()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_LobbyPlayers::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_LobbyPlayers::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_LobbyPlayers::GetClassData() const { return &_class_data_; } + + +void CustomMatch_LobbyPlayers::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_LobbyPlayers) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + _this->_impl_.players_.MergeFrom(from._impl_.players_); + if (!from._internal_playertoken().empty()) { + _this->_internal_set_playertoken(from._internal_playertoken()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_LobbyPlayers::CopyFrom(const CustomMatch_LobbyPlayers& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_LobbyPlayers) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_LobbyPlayers::IsInitialized() const { + return true; +} + +void CustomMatch_LobbyPlayers::InternalSwap(CustomMatch_LobbyPlayers* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + _impl_.players_.InternalSwap(&other->_impl_.players_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.playertoken_, lhs_arena, + &other->_impl_.playertoken_, rhs_arena + ); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_LobbyPlayers::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[8]); +} + +// =================================================================== + +class ObserverSwitched::_Internal { + public: + static const ::rtech::liveapi::Player& observer(const ObserverSwitched* msg); + static const ::rtech::liveapi::Player& target(const ObserverSwitched* msg); +}; + +const ::rtech::liveapi::Player& +ObserverSwitched::_Internal::observer(const ObserverSwitched* msg) { + return *msg->_impl_.observer_; +} +const ::rtech::liveapi::Player& +ObserverSwitched::_Internal::target(const ObserverSwitched* msg) { + return *msg->_impl_.target_; +} +ObserverSwitched::ObserverSwitched(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.ObserverSwitched) +} +ObserverSwitched::ObserverSwitched(const ObserverSwitched& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + ObserverSwitched* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.targetteam_){from._impl_.targetteam_} + , decltype(_impl_.category_){} + , decltype(_impl_.observer_){nullptr} + , decltype(_impl_.target_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_observer()) { + _this->_impl_.observer_ = new ::rtech::liveapi::Player(*from._impl_.observer_); + } + if (from._internal_has_target()) { + _this->_impl_.target_ = new ::rtech::liveapi::Player(*from._impl_.target_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.ObserverSwitched) +} + +inline void ObserverSwitched::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.targetteam_){arena} + , decltype(_impl_.category_){} + , decltype(_impl_.observer_){nullptr} + , decltype(_impl_.target_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +ObserverSwitched::~ObserverSwitched() { + // @@protoc_insertion_point(destructor:rtech.liveapi.ObserverSwitched) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void ObserverSwitched::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.targetteam_.~RepeatedPtrField(); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.observer_; + if (this != internal_default_instance()) delete _impl_.target_; +} + +void ObserverSwitched::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void ObserverSwitched::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.ObserverSwitched) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.targetteam_.Clear(); + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.observer_ != nullptr) { + delete _impl_.observer_; + } + _impl_.observer_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.target_ != nullptr) { + delete _impl_.target_; + } + _impl_.target_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ObserverSwitched::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ObserverSwitched.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player observer = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_observer(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player target = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_target(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated .rtech.liveapi.Player targetTeam = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + ptr -= 1; + do { + ptr += 1; + ptr = ctx->ParseMessage(_internal_add_targetteam(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<42>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ObserverSwitched::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.ObserverSwitched) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ObserverSwitched.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player observer = 3; + if (this->_internal_has_observer()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::observer(this), + _Internal::observer(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player target = 4; + if (this->_internal_has_target()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::target(this), + _Internal::target(this).GetCachedSize(), target, stream); + } + + // repeated .rtech.liveapi.Player targetTeam = 5; + for (unsigned i = 0, + n = static_cast(this->_internal_targetteam_size()); i < n; i++) { + const auto& repfield = this->_internal_targetteam(i); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(5, repfield, repfield.GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.ObserverSwitched) + return target; +} + +size_t ObserverSwitched::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.ObserverSwitched) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated .rtech.liveapi.Player targetTeam = 5; + total_size += 1UL * this->_internal_targetteam_size(); + for (const auto& msg : this->_impl_.targetteam_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player observer = 3; + if (this->_internal_has_observer()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.observer_); + } + + // .rtech.liveapi.Player target = 4; + if (this->_internal_has_target()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.target_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ObserverSwitched::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + ObserverSwitched::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ObserverSwitched::GetClassData() const { return &_class_data_; } + + +void ObserverSwitched::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.ObserverSwitched) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + _this->_impl_.targetteam_.MergeFrom(from._impl_.targetteam_); + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_observer()) { + _this->_internal_mutable_observer()->::rtech::liveapi::Player::MergeFrom( + from._internal_observer()); + } + if (from._internal_has_target()) { + _this->_internal_mutable_target()->::rtech::liveapi::Player::MergeFrom( + from._internal_target()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ObserverSwitched::CopyFrom(const ObserverSwitched& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.ObserverSwitched) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ObserverSwitched::IsInitialized() const { + return true; +} + +void ObserverSwitched::InternalSwap(ObserverSwitched* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + _impl_.targetteam_.InternalSwap(&other->_impl_.targetteam_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(ObserverSwitched, _impl_.timestamp_) + + sizeof(ObserverSwitched::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(ObserverSwitched, _impl_.observer_)>( + reinterpret_cast(&_impl_.observer_), + reinterpret_cast(&other->_impl_.observer_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ObserverSwitched::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[9]); +} + +// =================================================================== + +class ObserverAnnotation::_Internal { + public: +}; + +ObserverAnnotation::ObserverAnnotation(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.ObserverAnnotation) +} +ObserverAnnotation::ObserverAnnotation(const ObserverAnnotation& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + ObserverAnnotation* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.annotationserial_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.annotationserial_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.annotationserial_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.ObserverAnnotation) +} + +inline void ObserverAnnotation::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.annotationserial_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +ObserverAnnotation::~ObserverAnnotation() { + // @@protoc_insertion_point(destructor:rtech.liveapi.ObserverAnnotation) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void ObserverAnnotation::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); +} + +void ObserverAnnotation::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void ObserverAnnotation::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.ObserverAnnotation) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.annotationserial_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.annotationserial_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ObserverAnnotation::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ObserverAnnotation.category")); + } else + goto handle_unusual; + continue; + // int32 annotationSerial = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _impl_.annotationserial_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ObserverAnnotation::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.ObserverAnnotation) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ObserverAnnotation.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // int32 annotationSerial = 3; + if (this->_internal_annotationserial() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_annotationserial(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.ObserverAnnotation) + return target; +} + +size_t ObserverAnnotation::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.ObserverAnnotation) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 annotationSerial = 3; + if (this->_internal_annotationserial() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_annotationserial()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ObserverAnnotation::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + ObserverAnnotation::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ObserverAnnotation::GetClassData() const { return &_class_data_; } + + +void ObserverAnnotation::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.ObserverAnnotation) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_annotationserial() != 0) { + _this->_internal_set_annotationserial(from._internal_annotationserial()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ObserverAnnotation::CopyFrom(const ObserverAnnotation& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.ObserverAnnotation) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ObserverAnnotation::IsInitialized() const { + return true; +} + +void ObserverAnnotation::InternalSwap(ObserverAnnotation* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(ObserverAnnotation, _impl_.annotationserial_) + + sizeof(ObserverAnnotation::_impl_.annotationserial_) + - PROTOBUF_FIELD_OFFSET(ObserverAnnotation, _impl_.timestamp_)>( + reinterpret_cast(&_impl_.timestamp_), + reinterpret_cast(&other->_impl_.timestamp_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ObserverAnnotation::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[10]); +} + +// =================================================================== + +class MatchSetup::_Internal { + public: + static const ::rtech::liveapi::Datacenter& datacenter(const MatchSetup* msg); + static const ::rtech::liveapi::LoadoutConfiguration& startingloadout(const MatchSetup* msg); +}; + +const ::rtech::liveapi::Datacenter& +MatchSetup::_Internal::datacenter(const MatchSetup* msg) { + return *msg->_impl_.datacenter_; +} +const ::rtech::liveapi::LoadoutConfiguration& +MatchSetup::_Internal::startingloadout(const MatchSetup* msg) { + return *msg->_impl_.startingloadout_; +} +MatchSetup::MatchSetup(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.MatchSetup) +} +MatchSetup::MatchSetup(const MatchSetup& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + MatchSetup* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.map_){} + , decltype(_impl_.playlistname_){} + , decltype(_impl_.playlistdesc_){} + , decltype(_impl_.serverid_){} + , decltype(_impl_.datacenter_){nullptr} + , decltype(_impl_.startingloadout_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.aimassiston_){} + , decltype(_impl_.anonymousmode_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.map_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.map_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_map().empty()) { + _this->_impl_.map_.Set(from._internal_map(), + _this->GetArenaForAllocation()); + } + _impl_.playlistname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_playlistname().empty()) { + _this->_impl_.playlistname_.Set(from._internal_playlistname(), + _this->GetArenaForAllocation()); + } + _impl_.playlistdesc_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistdesc_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_playlistdesc().empty()) { + _this->_impl_.playlistdesc_.Set(from._internal_playlistdesc(), + _this->GetArenaForAllocation()); + } + _impl_.serverid_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.serverid_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_serverid().empty()) { + _this->_impl_.serverid_.Set(from._internal_serverid(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_datacenter()) { + _this->_impl_.datacenter_ = new ::rtech::liveapi::Datacenter(*from._impl_.datacenter_); + } + if (from._internal_has_startingloadout()) { + _this->_impl_.startingloadout_ = new ::rtech::liveapi::LoadoutConfiguration(*from._impl_.startingloadout_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.anonymousmode_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.anonymousmode_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.MatchSetup) +} + +inline void MatchSetup::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.map_){} + , decltype(_impl_.playlistname_){} + , decltype(_impl_.playlistdesc_){} + , decltype(_impl_.serverid_){} + , decltype(_impl_.datacenter_){nullptr} + , decltype(_impl_.startingloadout_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.aimassiston_){false} + , decltype(_impl_.anonymousmode_){false} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.map_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.map_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistdesc_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistdesc_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.serverid_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.serverid_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +MatchSetup::~MatchSetup() { + // @@protoc_insertion_point(destructor:rtech.liveapi.MatchSetup) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void MatchSetup::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.map_.Destroy(); + _impl_.playlistname_.Destroy(); + _impl_.playlistdesc_.Destroy(); + _impl_.serverid_.Destroy(); + if (this != internal_default_instance()) delete _impl_.datacenter_; + if (this != internal_default_instance()) delete _impl_.startingloadout_; +} + +void MatchSetup::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void MatchSetup::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.MatchSetup) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.map_.ClearToEmpty(); + _impl_.playlistname_.ClearToEmpty(); + _impl_.playlistdesc_.ClearToEmpty(); + _impl_.serverid_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.datacenter_ != nullptr) { + delete _impl_.datacenter_; + } + _impl_.datacenter_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.startingloadout_ != nullptr) { + delete _impl_.startingloadout_; + } + _impl_.startingloadout_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.anonymousmode_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.anonymousmode_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* MatchSetup::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.MatchSetup.category")); + } else + goto handle_unusual; + continue; + // string map = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_map(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.MatchSetup.map")); + } else + goto handle_unusual; + continue; + // string playlistName = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_playlistname(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.MatchSetup.playlistName")); + } else + goto handle_unusual; + continue; + // string playlistDesc = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_playlistdesc(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.MatchSetup.playlistDesc")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Datacenter datacenter = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 50)) { + ptr = ctx->ParseMessage(_internal_mutable_datacenter(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool aimAssistOn = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _impl_.aimassiston_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool anonymousMode = 8; + case 8: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 64)) { + _impl_.anonymousmode_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string serverId = 9; + case 9: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 74)) { + auto str = _internal_mutable_serverid(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.MatchSetup.serverId")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.LoadoutConfiguration startingLoadout = 10; + case 10: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 82)) { + ptr = ctx->ParseMessage(_internal_mutable_startingloadout(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* MatchSetup::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.MatchSetup) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.MatchSetup.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // string map = 3; + if (!this->_internal_map().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_map().data(), static_cast(this->_internal_map().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.MatchSetup.map"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_map(), target); + } + + // string playlistName = 4; + if (!this->_internal_playlistname().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_playlistname().data(), static_cast(this->_internal_playlistname().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.MatchSetup.playlistName"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_playlistname(), target); + } + + // string playlistDesc = 5; + if (!this->_internal_playlistdesc().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_playlistdesc().data(), static_cast(this->_internal_playlistdesc().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.MatchSetup.playlistDesc"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_playlistdesc(), target); + } + + // .rtech.liveapi.Datacenter datacenter = 6; + if (this->_internal_has_datacenter()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(6, _Internal::datacenter(this), + _Internal::datacenter(this).GetCachedSize(), target, stream); + } + + // bool aimAssistOn = 7; + if (this->_internal_aimassiston() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(7, this->_internal_aimassiston(), target); + } + + // bool anonymousMode = 8; + if (this->_internal_anonymousmode() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(8, this->_internal_anonymousmode(), target); + } + + // string serverId = 9; + if (!this->_internal_serverid().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_serverid().data(), static_cast(this->_internal_serverid().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.MatchSetup.serverId"); + target = stream->WriteStringMaybeAliased( + 9, this->_internal_serverid(), target); + } + + // .rtech.liveapi.LoadoutConfiguration startingLoadout = 10; + if (this->_internal_has_startingloadout()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(10, _Internal::startingloadout(this), + _Internal::startingloadout(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.MatchSetup) + return target; +} + +size_t MatchSetup::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.MatchSetup) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string map = 3; + if (!this->_internal_map().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_map()); + } + + // string playlistName = 4; + if (!this->_internal_playlistname().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_playlistname()); + } + + // string playlistDesc = 5; + if (!this->_internal_playlistdesc().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_playlistdesc()); + } + + // string serverId = 9; + if (!this->_internal_serverid().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_serverid()); + } + + // .rtech.liveapi.Datacenter datacenter = 6; + if (this->_internal_has_datacenter()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.datacenter_); + } + + // .rtech.liveapi.LoadoutConfiguration startingLoadout = 10; + if (this->_internal_has_startingloadout()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.startingloadout_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // bool aimAssistOn = 7; + if (this->_internal_aimassiston() != 0) { + total_size += 1 + 1; + } + + // bool anonymousMode = 8; + if (this->_internal_anonymousmode() != 0) { + total_size += 1 + 1; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MatchSetup::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + MatchSetup::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MatchSetup::GetClassData() const { return &_class_data_; } + + +void MatchSetup::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.MatchSetup) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_map().empty()) { + _this->_internal_set_map(from._internal_map()); + } + if (!from._internal_playlistname().empty()) { + _this->_internal_set_playlistname(from._internal_playlistname()); + } + if (!from._internal_playlistdesc().empty()) { + _this->_internal_set_playlistdesc(from._internal_playlistdesc()); + } + if (!from._internal_serverid().empty()) { + _this->_internal_set_serverid(from._internal_serverid()); + } + if (from._internal_has_datacenter()) { + _this->_internal_mutable_datacenter()->::rtech::liveapi::Datacenter::MergeFrom( + from._internal_datacenter()); + } + if (from._internal_has_startingloadout()) { + _this->_internal_mutable_startingloadout()->::rtech::liveapi::LoadoutConfiguration::MergeFrom( + from._internal_startingloadout()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_aimassiston() != 0) { + _this->_internal_set_aimassiston(from._internal_aimassiston()); + } + if (from._internal_anonymousmode() != 0) { + _this->_internal_set_anonymousmode(from._internal_anonymousmode()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void MatchSetup::CopyFrom(const MatchSetup& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.MatchSetup) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MatchSetup::IsInitialized() const { + return true; +} + +void MatchSetup::InternalSwap(MatchSetup* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.map_, lhs_arena, + &other->_impl_.map_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.playlistname_, lhs_arena, + &other->_impl_.playlistname_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.playlistdesc_, lhs_arena, + &other->_impl_.playlistdesc_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.serverid_, lhs_arena, + &other->_impl_.serverid_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(MatchSetup, _impl_.anonymousmode_) + + sizeof(MatchSetup::_impl_.anonymousmode_) + - PROTOBUF_FIELD_OFFSET(MatchSetup, _impl_.datacenter_)>( + reinterpret_cast(&_impl_.datacenter_), + reinterpret_cast(&other->_impl_.datacenter_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata MatchSetup::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[11]); +} + +// =================================================================== + +class GameStateChanged::_Internal { + public: +}; + +GameStateChanged::GameStateChanged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.GameStateChanged) +} +GameStateChanged::GameStateChanged(const GameStateChanged& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + GameStateChanged* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.state_){} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.state_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.state_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_state().empty()) { + _this->_impl_.state_.Set(from._internal_state(), + _this->GetArenaForAllocation()); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.GameStateChanged) +} + +inline void GameStateChanged::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.state_){} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.state_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.state_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +GameStateChanged::~GameStateChanged() { + // @@protoc_insertion_point(destructor:rtech.liveapi.GameStateChanged) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void GameStateChanged::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.state_.Destroy(); +} + +void GameStateChanged::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void GameStateChanged::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.GameStateChanged) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.state_.ClearToEmpty(); + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* GameStateChanged::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.GameStateChanged.category")); + } else + goto handle_unusual; + continue; + // string state = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_state(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.GameStateChanged.state")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* GameStateChanged::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.GameStateChanged) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.GameStateChanged.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // string state = 3; + if (!this->_internal_state().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_state().data(), static_cast(this->_internal_state().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.GameStateChanged.state"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_state(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.GameStateChanged) + return target; +} + +size_t GameStateChanged::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.GameStateChanged) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string state = 3; + if (!this->_internal_state().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_state()); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData GameStateChanged::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + GameStateChanged::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GameStateChanged::GetClassData() const { return &_class_data_; } + + +void GameStateChanged::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.GameStateChanged) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_state().empty()) { + _this->_internal_set_state(from._internal_state()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void GameStateChanged::CopyFrom(const GameStateChanged& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.GameStateChanged) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool GameStateChanged::IsInitialized() const { + return true; +} + +void GameStateChanged::InternalSwap(GameStateChanged* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.state_, lhs_arena, + &other->_impl_.state_, rhs_arena + ); + swap(_impl_.timestamp_, other->_impl_.timestamp_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata GameStateChanged::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[12]); +} + +// =================================================================== + +class CharacterSelected::_Internal { + public: + static const ::rtech::liveapi::Player& player(const CharacterSelected* msg); +}; + +const ::rtech::liveapi::Player& +CharacterSelected::_Internal::player(const CharacterSelected* msg) { + return *msg->_impl_.player_; +} +CharacterSelected::CharacterSelected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CharacterSelected) +} +CharacterSelected::CharacterSelected(const CharacterSelected& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CharacterSelected* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CharacterSelected) +} + +inline void CharacterSelected::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CharacterSelected::~CharacterSelected() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CharacterSelected) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CharacterSelected::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void CharacterSelected::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CharacterSelected::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CharacterSelected) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CharacterSelected::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CharacterSelected.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CharacterSelected::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CharacterSelected) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CharacterSelected.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CharacterSelected) + return target; +} + +size_t CharacterSelected::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CharacterSelected) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CharacterSelected::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CharacterSelected::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CharacterSelected::GetClassData() const { return &_class_data_; } + + +void CharacterSelected::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CharacterSelected) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CharacterSelected::CopyFrom(const CharacterSelected& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CharacterSelected) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CharacterSelected::IsInitialized() const { + return true; +} + +void CharacterSelected::InternalSwap(CharacterSelected* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(CharacterSelected, _impl_.timestamp_) + + sizeof(CharacterSelected::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(CharacterSelected, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CharacterSelected::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[13]); +} + +// =================================================================== + +class MatchStateEnd::_Internal { + public: +}; + +MatchStateEnd::MatchStateEnd(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.MatchStateEnd) +} +MatchStateEnd::MatchStateEnd(const MatchStateEnd& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + MatchStateEnd* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.winners_){from._impl_.winners_} + , decltype(_impl_.category_){} + , decltype(_impl_.state_){} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.state_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.state_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_state().empty()) { + _this->_impl_.state_.Set(from._internal_state(), + _this->GetArenaForAllocation()); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.MatchStateEnd) +} + +inline void MatchStateEnd::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.winners_){arena} + , decltype(_impl_.category_){} + , decltype(_impl_.state_){} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.state_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.state_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +MatchStateEnd::~MatchStateEnd() { + // @@protoc_insertion_point(destructor:rtech.liveapi.MatchStateEnd) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void MatchStateEnd::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.winners_.~RepeatedPtrField(); + _impl_.category_.Destroy(); + _impl_.state_.Destroy(); +} + +void MatchStateEnd::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void MatchStateEnd::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.MatchStateEnd) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.winners_.Clear(); + _impl_.category_.ClearToEmpty(); + _impl_.state_.ClearToEmpty(); + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* MatchStateEnd::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.MatchStateEnd.category")); + } else + goto handle_unusual; + continue; + // string state = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_state(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.MatchStateEnd.state")); + } else + goto handle_unusual; + continue; + // repeated .rtech.liveapi.Player winners = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr -= 1; + do { + ptr += 1; + ptr = ctx->ParseMessage(_internal_add_winners(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* MatchStateEnd::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.MatchStateEnd) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.MatchStateEnd.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // string state = 3; + if (!this->_internal_state().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_state().data(), static_cast(this->_internal_state().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.MatchStateEnd.state"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_state(), target); + } + + // repeated .rtech.liveapi.Player winners = 4; + for (unsigned i = 0, + n = static_cast(this->_internal_winners_size()); i < n; i++) { + const auto& repfield = this->_internal_winners(i); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, repfield, repfield.GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.MatchStateEnd) + return target; +} + +size_t MatchStateEnd::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.MatchStateEnd) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated .rtech.liveapi.Player winners = 4; + total_size += 1UL * this->_internal_winners_size(); + for (const auto& msg : this->_impl_.winners_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string state = 3; + if (!this->_internal_state().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_state()); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData MatchStateEnd::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + MatchStateEnd::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*MatchStateEnd::GetClassData() const { return &_class_data_; } + + +void MatchStateEnd::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.MatchStateEnd) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + _this->_impl_.winners_.MergeFrom(from._impl_.winners_); + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_state().empty()) { + _this->_internal_set_state(from._internal_state()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void MatchStateEnd::CopyFrom(const MatchStateEnd& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.MatchStateEnd) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MatchStateEnd::IsInitialized() const { + return true; +} + +void MatchStateEnd::InternalSwap(MatchStateEnd* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + _impl_.winners_.InternalSwap(&other->_impl_.winners_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.state_, lhs_arena, + &other->_impl_.state_, rhs_arena + ); + swap(_impl_.timestamp_, other->_impl_.timestamp_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata MatchStateEnd::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[14]); +} + +// =================================================================== + +class RingStartClosing::_Internal { + public: + static const ::rtech::liveapi::Vector3& center(const RingStartClosing* msg); +}; + +const ::rtech::liveapi::Vector3& +RingStartClosing::_Internal::center(const RingStartClosing* msg) { + return *msg->_impl_.center_; +} +RingStartClosing::RingStartClosing(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.RingStartClosing) +} +RingStartClosing::RingStartClosing(const RingStartClosing& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + RingStartClosing* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.center_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.stage_){} + , decltype(_impl_.currentradius_){} + , decltype(_impl_.endradius_){} + , decltype(_impl_.shrinkduration_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_center()) { + _this->_impl_.center_ = new ::rtech::liveapi::Vector3(*from._impl_.center_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.shrinkduration_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.shrinkduration_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.RingStartClosing) +} + +inline void RingStartClosing::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.center_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.stage_){0u} + , decltype(_impl_.currentradius_){0} + , decltype(_impl_.endradius_){0} + , decltype(_impl_.shrinkduration_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +RingStartClosing::~RingStartClosing() { + // @@protoc_insertion_point(destructor:rtech.liveapi.RingStartClosing) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void RingStartClosing::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.center_; +} + +void RingStartClosing::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void RingStartClosing::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.RingStartClosing) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.center_ != nullptr) { + delete _impl_.center_; + } + _impl_.center_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.shrinkduration_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.shrinkduration_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* RingStartClosing::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.RingStartClosing.category")); + } else + goto handle_unusual; + continue; + // uint32 stage = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _impl_.stage_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Vector3 center = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_center(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // float currentRadius = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 45)) { + _impl_.currentradius_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + // float endRadius = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 53)) { + _impl_.endradius_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + // float shrinkDuration = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 61)) { + _impl_.shrinkduration_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* RingStartClosing::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.RingStartClosing) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.RingStartClosing.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // uint32 stage = 3; + if (this->_internal_stage() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(3, this->_internal_stage(), target); + } + + // .rtech.liveapi.Vector3 center = 4; + if (this->_internal_has_center()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::center(this), + _Internal::center(this).GetCachedSize(), target, stream); + } + + // float currentRadius = 5; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_currentradius = this->_internal_currentradius(); + uint32_t raw_currentradius; + memcpy(&raw_currentradius, &tmp_currentradius, sizeof(tmp_currentradius)); + if (raw_currentradius != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(5, this->_internal_currentradius(), target); + } + + // float endRadius = 6; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_endradius = this->_internal_endradius(); + uint32_t raw_endradius; + memcpy(&raw_endradius, &tmp_endradius, sizeof(tmp_endradius)); + if (raw_endradius != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(6, this->_internal_endradius(), target); + } + + // float shrinkDuration = 7; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_shrinkduration = this->_internal_shrinkduration(); + uint32_t raw_shrinkduration; + memcpy(&raw_shrinkduration, &tmp_shrinkduration, sizeof(tmp_shrinkduration)); + if (raw_shrinkduration != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(7, this->_internal_shrinkduration(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.RingStartClosing) + return target; +} + +size_t RingStartClosing::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.RingStartClosing) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Vector3 center = 4; + if (this->_internal_has_center()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.center_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // uint32 stage = 3; + if (this->_internal_stage() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_stage()); + } + + // float currentRadius = 5; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_currentradius = this->_internal_currentradius(); + uint32_t raw_currentradius; + memcpy(&raw_currentradius, &tmp_currentradius, sizeof(tmp_currentradius)); + if (raw_currentradius != 0) { + total_size += 1 + 4; + } + + // float endRadius = 6; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_endradius = this->_internal_endradius(); + uint32_t raw_endradius; + memcpy(&raw_endradius, &tmp_endradius, sizeof(tmp_endradius)); + if (raw_endradius != 0) { + total_size += 1 + 4; + } + + // float shrinkDuration = 7; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_shrinkduration = this->_internal_shrinkduration(); + uint32_t raw_shrinkduration; + memcpy(&raw_shrinkduration, &tmp_shrinkduration, sizeof(tmp_shrinkduration)); + if (raw_shrinkduration != 0) { + total_size += 1 + 4; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData RingStartClosing::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + RingStartClosing::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*RingStartClosing::GetClassData() const { return &_class_data_; } + + +void RingStartClosing::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.RingStartClosing) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_center()) { + _this->_internal_mutable_center()->::rtech::liveapi::Vector3::MergeFrom( + from._internal_center()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_stage() != 0) { + _this->_internal_set_stage(from._internal_stage()); + } + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_currentradius = from._internal_currentradius(); + uint32_t raw_currentradius; + memcpy(&raw_currentradius, &tmp_currentradius, sizeof(tmp_currentradius)); + if (raw_currentradius != 0) { + _this->_internal_set_currentradius(from._internal_currentradius()); + } + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_endradius = from._internal_endradius(); + uint32_t raw_endradius; + memcpy(&raw_endradius, &tmp_endradius, sizeof(tmp_endradius)); + if (raw_endradius != 0) { + _this->_internal_set_endradius(from._internal_endradius()); + } + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_shrinkduration = from._internal_shrinkduration(); + uint32_t raw_shrinkduration; + memcpy(&raw_shrinkduration, &tmp_shrinkduration, sizeof(tmp_shrinkduration)); + if (raw_shrinkduration != 0) { + _this->_internal_set_shrinkduration(from._internal_shrinkduration()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void RingStartClosing::CopyFrom(const RingStartClosing& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.RingStartClosing) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool RingStartClosing::IsInitialized() const { + return true; +} + +void RingStartClosing::InternalSwap(RingStartClosing* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(RingStartClosing, _impl_.shrinkduration_) + + sizeof(RingStartClosing::_impl_.shrinkduration_) + - PROTOBUF_FIELD_OFFSET(RingStartClosing, _impl_.center_)>( + reinterpret_cast(&_impl_.center_), + reinterpret_cast(&other->_impl_.center_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata RingStartClosing::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[15]); +} + +// =================================================================== + +class RingFinishedClosing::_Internal { + public: + static const ::rtech::liveapi::Vector3& center(const RingFinishedClosing* msg); +}; + +const ::rtech::liveapi::Vector3& +RingFinishedClosing::_Internal::center(const RingFinishedClosing* msg) { + return *msg->_impl_.center_; +} +RingFinishedClosing::RingFinishedClosing(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.RingFinishedClosing) +} +RingFinishedClosing::RingFinishedClosing(const RingFinishedClosing& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + RingFinishedClosing* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.center_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.stage_){} + , decltype(_impl_.currentradius_){} + , decltype(_impl_.shrinkduration_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_center()) { + _this->_impl_.center_ = new ::rtech::liveapi::Vector3(*from._impl_.center_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.shrinkduration_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.shrinkduration_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.RingFinishedClosing) +} + +inline void RingFinishedClosing::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.center_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.stage_){0u} + , decltype(_impl_.currentradius_){0} + , decltype(_impl_.shrinkduration_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +RingFinishedClosing::~RingFinishedClosing() { + // @@protoc_insertion_point(destructor:rtech.liveapi.RingFinishedClosing) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void RingFinishedClosing::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.center_; +} + +void RingFinishedClosing::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void RingFinishedClosing::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.RingFinishedClosing) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.center_ != nullptr) { + delete _impl_.center_; + } + _impl_.center_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.shrinkduration_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.shrinkduration_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* RingFinishedClosing::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.RingFinishedClosing.category")); + } else + goto handle_unusual; + continue; + // uint32 stage = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _impl_.stage_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Vector3 center = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_center(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // float currentRadius = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 45)) { + _impl_.currentradius_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + // float shrinkDuration = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 61)) { + _impl_.shrinkduration_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* RingFinishedClosing::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.RingFinishedClosing) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.RingFinishedClosing.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // uint32 stage = 3; + if (this->_internal_stage() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(3, this->_internal_stage(), target); + } + + // .rtech.liveapi.Vector3 center = 4; + if (this->_internal_has_center()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::center(this), + _Internal::center(this).GetCachedSize(), target, stream); + } + + // float currentRadius = 5; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_currentradius = this->_internal_currentradius(); + uint32_t raw_currentradius; + memcpy(&raw_currentradius, &tmp_currentradius, sizeof(tmp_currentradius)); + if (raw_currentradius != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(5, this->_internal_currentradius(), target); + } + + // float shrinkDuration = 7; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_shrinkduration = this->_internal_shrinkduration(); + uint32_t raw_shrinkduration; + memcpy(&raw_shrinkduration, &tmp_shrinkduration, sizeof(tmp_shrinkduration)); + if (raw_shrinkduration != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(7, this->_internal_shrinkduration(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.RingFinishedClosing) + return target; +} + +size_t RingFinishedClosing::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.RingFinishedClosing) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Vector3 center = 4; + if (this->_internal_has_center()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.center_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // uint32 stage = 3; + if (this->_internal_stage() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_stage()); + } + + // float currentRadius = 5; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_currentradius = this->_internal_currentradius(); + uint32_t raw_currentradius; + memcpy(&raw_currentradius, &tmp_currentradius, sizeof(tmp_currentradius)); + if (raw_currentradius != 0) { + total_size += 1 + 4; + } + + // float shrinkDuration = 7; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_shrinkduration = this->_internal_shrinkduration(); + uint32_t raw_shrinkduration; + memcpy(&raw_shrinkduration, &tmp_shrinkduration, sizeof(tmp_shrinkduration)); + if (raw_shrinkduration != 0) { + total_size += 1 + 4; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData RingFinishedClosing::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + RingFinishedClosing::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*RingFinishedClosing::GetClassData() const { return &_class_data_; } + + +void RingFinishedClosing::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.RingFinishedClosing) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_center()) { + _this->_internal_mutable_center()->::rtech::liveapi::Vector3::MergeFrom( + from._internal_center()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_stage() != 0) { + _this->_internal_set_stage(from._internal_stage()); + } + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_currentradius = from._internal_currentradius(); + uint32_t raw_currentradius; + memcpy(&raw_currentradius, &tmp_currentradius, sizeof(tmp_currentradius)); + if (raw_currentradius != 0) { + _this->_internal_set_currentradius(from._internal_currentradius()); + } + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_shrinkduration = from._internal_shrinkduration(); + uint32_t raw_shrinkduration; + memcpy(&raw_shrinkduration, &tmp_shrinkduration, sizeof(tmp_shrinkduration)); + if (raw_shrinkduration != 0) { + _this->_internal_set_shrinkduration(from._internal_shrinkduration()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void RingFinishedClosing::CopyFrom(const RingFinishedClosing& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.RingFinishedClosing) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool RingFinishedClosing::IsInitialized() const { + return true; +} + +void RingFinishedClosing::InternalSwap(RingFinishedClosing* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(RingFinishedClosing, _impl_.shrinkduration_) + + sizeof(RingFinishedClosing::_impl_.shrinkduration_) + - PROTOBUF_FIELD_OFFSET(RingFinishedClosing, _impl_.center_)>( + reinterpret_cast(&_impl_.center_), + reinterpret_cast(&other->_impl_.center_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata RingFinishedClosing::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[16]); +} + +// =================================================================== + +class PlayerConnected::_Internal { + public: + static const ::rtech::liveapi::Player& player(const PlayerConnected* msg); +}; + +const ::rtech::liveapi::Player& +PlayerConnected::_Internal::player(const PlayerConnected* msg) { + return *msg->_impl_.player_; +} +PlayerConnected::PlayerConnected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerConnected) +} +PlayerConnected::PlayerConnected(const PlayerConnected& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerConnected* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerConnected) +} + +inline void PlayerConnected::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerConnected::~PlayerConnected() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerConnected) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerConnected::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void PlayerConnected::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerConnected::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerConnected) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerConnected::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerConnected.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerConnected::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerConnected) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerConnected.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerConnected) + return target; +} + +size_t PlayerConnected::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerConnected) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerConnected::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerConnected::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerConnected::GetClassData() const { return &_class_data_; } + + +void PlayerConnected::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerConnected) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerConnected::CopyFrom(const PlayerConnected& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerConnected) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerConnected::IsInitialized() const { + return true; +} + +void PlayerConnected::InternalSwap(PlayerConnected* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerConnected, _impl_.timestamp_) + + sizeof(PlayerConnected::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerConnected, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerConnected::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[17]); +} + +// =================================================================== + +class PlayerDisconnected::_Internal { + public: + static const ::rtech::liveapi::Player& player(const PlayerDisconnected* msg); +}; + +const ::rtech::liveapi::Player& +PlayerDisconnected::_Internal::player(const PlayerDisconnected* msg) { + return *msg->_impl_.player_; +} +PlayerDisconnected::PlayerDisconnected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerDisconnected) +} +PlayerDisconnected::PlayerDisconnected(const PlayerDisconnected& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerDisconnected* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.canreconnect_){} + , decltype(_impl_.isalive_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.isalive_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.isalive_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerDisconnected) +} + +inline void PlayerDisconnected::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.canreconnect_){false} + , decltype(_impl_.isalive_){false} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerDisconnected::~PlayerDisconnected() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerDisconnected) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerDisconnected::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void PlayerDisconnected::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerDisconnected::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerDisconnected) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.isalive_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.isalive_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerDisconnected::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerDisconnected.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool canReconnect = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 32)) { + _impl_.canreconnect_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool isAlive = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.isalive_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerDisconnected::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerDisconnected) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerDisconnected.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // bool canReconnect = 4; + if (this->_internal_canreconnect() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(4, this->_internal_canreconnect(), target); + } + + // bool isAlive = 5; + if (this->_internal_isalive() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(5, this->_internal_isalive(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerDisconnected) + return target; +} + +size_t PlayerDisconnected::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerDisconnected) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // bool canReconnect = 4; + if (this->_internal_canreconnect() != 0) { + total_size += 1 + 1; + } + + // bool isAlive = 5; + if (this->_internal_isalive() != 0) { + total_size += 1 + 1; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerDisconnected::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerDisconnected::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerDisconnected::GetClassData() const { return &_class_data_; } + + +void PlayerDisconnected::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerDisconnected) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_canreconnect() != 0) { + _this->_internal_set_canreconnect(from._internal_canreconnect()); + } + if (from._internal_isalive() != 0) { + _this->_internal_set_isalive(from._internal_isalive()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerDisconnected::CopyFrom(const PlayerDisconnected& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerDisconnected) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerDisconnected::IsInitialized() const { + return true; +} + +void PlayerDisconnected::InternalSwap(PlayerDisconnected* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerDisconnected, _impl_.isalive_) + + sizeof(PlayerDisconnected::_impl_.isalive_) + - PROTOBUF_FIELD_OFFSET(PlayerDisconnected, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerDisconnected::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[18]); +} + +// =================================================================== + +class PlayerStatChanged::_Internal { + public: + static const ::rtech::liveapi::Player& player(const PlayerStatChanged* msg); +}; + +const ::rtech::liveapi::Player& +PlayerStatChanged::_Internal::player(const PlayerStatChanged* msg) { + return *msg->_impl_.player_; +} +PlayerStatChanged::PlayerStatChanged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerStatChanged) +} +PlayerStatChanged::PlayerStatChanged(const PlayerStatChanged& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerStatChanged* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.statname_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.newValue_){} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.statname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.statname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_statname().empty()) { + _this->_impl_.statname_.Set(from._internal_statname(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + clear_has_newValue(); + switch (from.newValue_case()) { + case kIntValue: { + _this->_internal_set_intvalue(from._internal_intvalue()); + break; + } + case kFloatValue: { + _this->_internal_set_floatvalue(from._internal_floatvalue()); + break; + } + case kBoolValue: { + _this->_internal_set_boolvalue(from._internal_boolvalue()); + break; + } + case NEWVALUE_NOT_SET: { + break; + } + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerStatChanged) +} + +inline void PlayerStatChanged::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.statname_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.newValue_){} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.statname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.statname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + clear_has_newValue(); +} + +PlayerStatChanged::~PlayerStatChanged() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerStatChanged) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerStatChanged::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.statname_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; + if (has_newValue()) { + clear_newValue(); + } +} + +void PlayerStatChanged::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerStatChanged::clear_newValue() { +// @@protoc_insertion_point(one_of_clear_start:rtech.liveapi.PlayerStatChanged) + switch (newValue_case()) { + case kIntValue: { + // No need to clear + break; + } + case kFloatValue: { + // No need to clear + break; + } + case kBoolValue: { + // No need to clear + break; + } + case NEWVALUE_NOT_SET: { + break; + } + } + _impl_._oneof_case_[0] = NEWVALUE_NOT_SET; +} + + +void PlayerStatChanged::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerStatChanged) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.statname_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + clear_newValue(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerStatChanged::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerStatChanged.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string statName = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_statname(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerStatChanged.statName")); + } else + goto handle_unusual; + continue; + // uint32 intValue = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _internal_set_intvalue(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // float floatValue = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 53)) { + _internal_set_floatvalue(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + // bool boolValue = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _internal_set_boolvalue(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerStatChanged::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerStatChanged) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerStatChanged.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string statName = 4; + if (!this->_internal_statname().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_statname().data(), static_cast(this->_internal_statname().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerStatChanged.statName"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_statname(), target); + } + + // uint32 intValue = 5; + if (_internal_has_intvalue()) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(5, this->_internal_intvalue(), target); + } + + // float floatValue = 6; + if (_internal_has_floatvalue()) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(6, this->_internal_floatvalue(), target); + } + + // bool boolValue = 7; + if (_internal_has_boolvalue()) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(7, this->_internal_boolvalue(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerStatChanged) + return target; +} + +size_t PlayerStatChanged::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerStatChanged) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string statName = 4; + if (!this->_internal_statname().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_statname()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + switch (newValue_case()) { + // uint32 intValue = 5; + case kIntValue: { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_intvalue()); + break; + } + // float floatValue = 6; + case kFloatValue: { + total_size += 1 + 4; + break; + } + // bool boolValue = 7; + case kBoolValue: { + total_size += 1 + 1; + break; + } + case NEWVALUE_NOT_SET: { + break; + } + } + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerStatChanged::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerStatChanged::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerStatChanged::GetClassData() const { return &_class_data_; } + + +void PlayerStatChanged::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerStatChanged) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_statname().empty()) { + _this->_internal_set_statname(from._internal_statname()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + switch (from.newValue_case()) { + case kIntValue: { + _this->_internal_set_intvalue(from._internal_intvalue()); + break; + } + case kFloatValue: { + _this->_internal_set_floatvalue(from._internal_floatvalue()); + break; + } + case kBoolValue: { + _this->_internal_set_boolvalue(from._internal_boolvalue()); + break; + } + case NEWVALUE_NOT_SET: { + break; + } + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerStatChanged::CopyFrom(const PlayerStatChanged& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerStatChanged) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerStatChanged::IsInitialized() const { + return true; +} + +void PlayerStatChanged::InternalSwap(PlayerStatChanged* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.statname_, lhs_arena, + &other->_impl_.statname_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerStatChanged, _impl_.timestamp_) + + sizeof(PlayerStatChanged::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerStatChanged, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); + swap(_impl_.newValue_, other->_impl_.newValue_); + swap(_impl_._oneof_case_[0], other->_impl_._oneof_case_[0]); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerStatChanged::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[19]); +} + +// =================================================================== + +class PlayerUpgradeTierChanged::_Internal { + public: + static const ::rtech::liveapi::Player& player(const PlayerUpgradeTierChanged* msg); +}; + +const ::rtech::liveapi::Player& +PlayerUpgradeTierChanged::_Internal::player(const PlayerUpgradeTierChanged* msg) { + return *msg->_impl_.player_; +} +PlayerUpgradeTierChanged::PlayerUpgradeTierChanged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerUpgradeTierChanged) +} +PlayerUpgradeTierChanged::PlayerUpgradeTierChanged(const PlayerUpgradeTierChanged& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerUpgradeTierChanged* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.level_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.level_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.level_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerUpgradeTierChanged) +} + +inline void PlayerUpgradeTierChanged::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.level_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerUpgradeTierChanged::~PlayerUpgradeTierChanged() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerUpgradeTierChanged) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerUpgradeTierChanged::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void PlayerUpgradeTierChanged::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerUpgradeTierChanged::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerUpgradeTierChanged) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.level_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.level_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerUpgradeTierChanged::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerUpgradeTierChanged.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // int32 level = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 32)) { + _impl_.level_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerUpgradeTierChanged::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerUpgradeTierChanged) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerUpgradeTierChanged.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // int32 level = 4; + if (this->_internal_level() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(4, this->_internal_level(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerUpgradeTierChanged) + return target; +} + +size_t PlayerUpgradeTierChanged::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerUpgradeTierChanged) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 level = 4; + if (this->_internal_level() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_level()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerUpgradeTierChanged::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerUpgradeTierChanged::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerUpgradeTierChanged::GetClassData() const { return &_class_data_; } + + +void PlayerUpgradeTierChanged::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerUpgradeTierChanged) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_level() != 0) { + _this->_internal_set_level(from._internal_level()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerUpgradeTierChanged::CopyFrom(const PlayerUpgradeTierChanged& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerUpgradeTierChanged) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerUpgradeTierChanged::IsInitialized() const { + return true; +} + +void PlayerUpgradeTierChanged::InternalSwap(PlayerUpgradeTierChanged* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerUpgradeTierChanged, _impl_.level_) + + sizeof(PlayerUpgradeTierChanged::_impl_.level_) + - PROTOBUF_FIELD_OFFSET(PlayerUpgradeTierChanged, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerUpgradeTierChanged::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[20]); +} + +// =================================================================== + +class PlayerDamaged::_Internal { + public: + static const ::rtech::liveapi::Player& attacker(const PlayerDamaged* msg); + static const ::rtech::liveapi::Player& victim(const PlayerDamaged* msg); +}; + +const ::rtech::liveapi::Player& +PlayerDamaged::_Internal::attacker(const PlayerDamaged* msg) { + return *msg->_impl_.attacker_; +} +const ::rtech::liveapi::Player& +PlayerDamaged::_Internal::victim(const PlayerDamaged* msg) { + return *msg->_impl_.victim_; +} +PlayerDamaged::PlayerDamaged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerDamaged) +} +PlayerDamaged::PlayerDamaged(const PlayerDamaged& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerDamaged* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.damageinflicted_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_weapon().empty()) { + _this->_impl_.weapon_.Set(from._internal_weapon(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_attacker()) { + _this->_impl_.attacker_ = new ::rtech::liveapi::Player(*from._impl_.attacker_); + } + if (from._internal_has_victim()) { + _this->_impl_.victim_ = new ::rtech::liveapi::Player(*from._impl_.victim_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.damageinflicted_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.damageinflicted_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerDamaged) +} + +inline void PlayerDamaged::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.damageinflicted_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerDamaged::~PlayerDamaged() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerDamaged) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerDamaged::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.weapon_.Destroy(); + if (this != internal_default_instance()) delete _impl_.attacker_; + if (this != internal_default_instance()) delete _impl_.victim_; +} + +void PlayerDamaged::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerDamaged::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerDamaged) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.weapon_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.damageinflicted_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.damageinflicted_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerDamaged::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerDamaged.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player attacker = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_attacker(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player victim = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_victim(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string weapon = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_weapon(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerDamaged.weapon")); + } else + goto handle_unusual; + continue; + // uint32 damageInflicted = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _impl_.damageinflicted_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerDamaged::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerDamaged) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerDamaged.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::attacker(this), + _Internal::attacker(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::victim(this), + _Internal::victim(this).GetCachedSize(), target, stream); + } + + // string weapon = 5; + if (!this->_internal_weapon().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_weapon().data(), static_cast(this->_internal_weapon().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerDamaged.weapon"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_weapon(), target); + } + + // uint32 damageInflicted = 6; + if (this->_internal_damageinflicted() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(6, this->_internal_damageinflicted(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerDamaged) + return target; +} + +size_t PlayerDamaged::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerDamaged) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string weapon = 5; + if (!this->_internal_weapon().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_weapon()); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.attacker_); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.victim_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // uint32 damageInflicted = 6; + if (this->_internal_damageinflicted() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_damageinflicted()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerDamaged::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerDamaged::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerDamaged::GetClassData() const { return &_class_data_; } + + +void PlayerDamaged::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerDamaged) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_weapon().empty()) { + _this->_internal_set_weapon(from._internal_weapon()); + } + if (from._internal_has_attacker()) { + _this->_internal_mutable_attacker()->::rtech::liveapi::Player::MergeFrom( + from._internal_attacker()); + } + if (from._internal_has_victim()) { + _this->_internal_mutable_victim()->::rtech::liveapi::Player::MergeFrom( + from._internal_victim()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_damageinflicted() != 0) { + _this->_internal_set_damageinflicted(from._internal_damageinflicted()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerDamaged::CopyFrom(const PlayerDamaged& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerDamaged) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerDamaged::IsInitialized() const { + return true; +} + +void PlayerDamaged::InternalSwap(PlayerDamaged* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.weapon_, lhs_arena, + &other->_impl_.weapon_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerDamaged, _impl_.damageinflicted_) + + sizeof(PlayerDamaged::_impl_.damageinflicted_) + - PROTOBUF_FIELD_OFFSET(PlayerDamaged, _impl_.attacker_)>( + reinterpret_cast(&_impl_.attacker_), + reinterpret_cast(&other->_impl_.attacker_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerDamaged::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[21]); +} + +// =================================================================== + +class PlayerKilled::_Internal { + public: + static const ::rtech::liveapi::Player& attacker(const PlayerKilled* msg); + static const ::rtech::liveapi::Player& victim(const PlayerKilled* msg); + static const ::rtech::liveapi::Player& awardedto(const PlayerKilled* msg); +}; + +const ::rtech::liveapi::Player& +PlayerKilled::_Internal::attacker(const PlayerKilled* msg) { + return *msg->_impl_.attacker_; +} +const ::rtech::liveapi::Player& +PlayerKilled::_Internal::victim(const PlayerKilled* msg) { + return *msg->_impl_.victim_; +} +const ::rtech::liveapi::Player& +PlayerKilled::_Internal::awardedto(const PlayerKilled* msg) { + return *msg->_impl_.awardedto_; +} +PlayerKilled::PlayerKilled(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerKilled) +} +PlayerKilled::PlayerKilled(const PlayerKilled& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerKilled* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.awardedto_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_weapon().empty()) { + _this->_impl_.weapon_.Set(from._internal_weapon(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_attacker()) { + _this->_impl_.attacker_ = new ::rtech::liveapi::Player(*from._impl_.attacker_); + } + if (from._internal_has_victim()) { + _this->_impl_.victim_ = new ::rtech::liveapi::Player(*from._impl_.victim_); + } + if (from._internal_has_awardedto()) { + _this->_impl_.awardedto_ = new ::rtech::liveapi::Player(*from._impl_.awardedto_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerKilled) +} + +inline void PlayerKilled::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.awardedto_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerKilled::~PlayerKilled() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerKilled) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerKilled::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.weapon_.Destroy(); + if (this != internal_default_instance()) delete _impl_.attacker_; + if (this != internal_default_instance()) delete _impl_.victim_; + if (this != internal_default_instance()) delete _impl_.awardedto_; +} + +void PlayerKilled::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerKilled::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerKilled) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.weapon_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.awardedto_ != nullptr) { + delete _impl_.awardedto_; + } + _impl_.awardedto_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerKilled::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerKilled.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player attacker = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_attacker(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player victim = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_victim(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player awardedTo = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + ptr = ctx->ParseMessage(_internal_mutable_awardedto(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string weapon = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 50)) { + auto str = _internal_mutable_weapon(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerKilled.weapon")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerKilled::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerKilled) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerKilled.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::attacker(this), + _Internal::attacker(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::victim(this), + _Internal::victim(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player awardedTo = 5; + if (this->_internal_has_awardedto()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(5, _Internal::awardedto(this), + _Internal::awardedto(this).GetCachedSize(), target, stream); + } + + // string weapon = 6; + if (!this->_internal_weapon().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_weapon().data(), static_cast(this->_internal_weapon().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerKilled.weapon"); + target = stream->WriteStringMaybeAliased( + 6, this->_internal_weapon(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerKilled) + return target; +} + +size_t PlayerKilled::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerKilled) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string weapon = 6; + if (!this->_internal_weapon().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_weapon()); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.attacker_); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.victim_); + } + + // .rtech.liveapi.Player awardedTo = 5; + if (this->_internal_has_awardedto()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.awardedto_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerKilled::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerKilled::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerKilled::GetClassData() const { return &_class_data_; } + + +void PlayerKilled::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerKilled) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_weapon().empty()) { + _this->_internal_set_weapon(from._internal_weapon()); + } + if (from._internal_has_attacker()) { + _this->_internal_mutable_attacker()->::rtech::liveapi::Player::MergeFrom( + from._internal_attacker()); + } + if (from._internal_has_victim()) { + _this->_internal_mutable_victim()->::rtech::liveapi::Player::MergeFrom( + from._internal_victim()); + } + if (from._internal_has_awardedto()) { + _this->_internal_mutable_awardedto()->::rtech::liveapi::Player::MergeFrom( + from._internal_awardedto()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerKilled::CopyFrom(const PlayerKilled& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerKilled) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerKilled::IsInitialized() const { + return true; +} + +void PlayerKilled::InternalSwap(PlayerKilled* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.weapon_, lhs_arena, + &other->_impl_.weapon_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerKilled, _impl_.timestamp_) + + sizeof(PlayerKilled::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerKilled, _impl_.attacker_)>( + reinterpret_cast(&_impl_.attacker_), + reinterpret_cast(&other->_impl_.attacker_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerKilled::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[22]); +} + +// =================================================================== + +class PlayerDowned::_Internal { + public: + static const ::rtech::liveapi::Player& attacker(const PlayerDowned* msg); + static const ::rtech::liveapi::Player& victim(const PlayerDowned* msg); +}; + +const ::rtech::liveapi::Player& +PlayerDowned::_Internal::attacker(const PlayerDowned* msg) { + return *msg->_impl_.attacker_; +} +const ::rtech::liveapi::Player& +PlayerDowned::_Internal::victim(const PlayerDowned* msg) { + return *msg->_impl_.victim_; +} +PlayerDowned::PlayerDowned(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerDowned) +} +PlayerDowned::PlayerDowned(const PlayerDowned& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerDowned* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_weapon().empty()) { + _this->_impl_.weapon_.Set(from._internal_weapon(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_attacker()) { + _this->_impl_.attacker_ = new ::rtech::liveapi::Player(*from._impl_.attacker_); + } + if (from._internal_has_victim()) { + _this->_impl_.victim_ = new ::rtech::liveapi::Player(*from._impl_.victim_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerDowned) +} + +inline void PlayerDowned::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerDowned::~PlayerDowned() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerDowned) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerDowned::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.weapon_.Destroy(); + if (this != internal_default_instance()) delete _impl_.attacker_; + if (this != internal_default_instance()) delete _impl_.victim_; +} + +void PlayerDowned::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerDowned::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerDowned) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.weapon_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerDowned::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerDowned.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player attacker = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_attacker(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player victim = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_victim(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string weapon = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_weapon(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerDowned.weapon")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerDowned::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerDowned) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerDowned.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::attacker(this), + _Internal::attacker(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::victim(this), + _Internal::victim(this).GetCachedSize(), target, stream); + } + + // string weapon = 5; + if (!this->_internal_weapon().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_weapon().data(), static_cast(this->_internal_weapon().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerDowned.weapon"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_weapon(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerDowned) + return target; +} + +size_t PlayerDowned::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerDowned) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string weapon = 5; + if (!this->_internal_weapon().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_weapon()); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.attacker_); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.victim_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerDowned::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerDowned::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerDowned::GetClassData() const { return &_class_data_; } + + +void PlayerDowned::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerDowned) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_weapon().empty()) { + _this->_internal_set_weapon(from._internal_weapon()); + } + if (from._internal_has_attacker()) { + _this->_internal_mutable_attacker()->::rtech::liveapi::Player::MergeFrom( + from._internal_attacker()); + } + if (from._internal_has_victim()) { + _this->_internal_mutable_victim()->::rtech::liveapi::Player::MergeFrom( + from._internal_victim()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerDowned::CopyFrom(const PlayerDowned& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerDowned) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerDowned::IsInitialized() const { + return true; +} + +void PlayerDowned::InternalSwap(PlayerDowned* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.weapon_, lhs_arena, + &other->_impl_.weapon_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerDowned, _impl_.timestamp_) + + sizeof(PlayerDowned::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerDowned, _impl_.attacker_)>( + reinterpret_cast(&_impl_.attacker_), + reinterpret_cast(&other->_impl_.attacker_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerDowned::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[23]); +} + +// =================================================================== + +class PlayerAssist::_Internal { + public: + static const ::rtech::liveapi::Player& assistant(const PlayerAssist* msg); + static const ::rtech::liveapi::Player& victim(const PlayerAssist* msg); +}; + +const ::rtech::liveapi::Player& +PlayerAssist::_Internal::assistant(const PlayerAssist* msg) { + return *msg->_impl_.assistant_; +} +const ::rtech::liveapi::Player& +PlayerAssist::_Internal::victim(const PlayerAssist* msg) { + return *msg->_impl_.victim_; +} +PlayerAssist::PlayerAssist(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerAssist) +} +PlayerAssist::PlayerAssist(const PlayerAssist& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerAssist* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.assistant_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_weapon().empty()) { + _this->_impl_.weapon_.Set(from._internal_weapon(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_assistant()) { + _this->_impl_.assistant_ = new ::rtech::liveapi::Player(*from._impl_.assistant_); + } + if (from._internal_has_victim()) { + _this->_impl_.victim_ = new ::rtech::liveapi::Player(*from._impl_.victim_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerAssist) +} + +inline void PlayerAssist::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.weapon_){} + , decltype(_impl_.assistant_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.weapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerAssist::~PlayerAssist() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerAssist) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerAssist::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.weapon_.Destroy(); + if (this != internal_default_instance()) delete _impl_.assistant_; + if (this != internal_default_instance()) delete _impl_.victim_; +} + +void PlayerAssist::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerAssist::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerAssist) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.weapon_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.assistant_ != nullptr) { + delete _impl_.assistant_; + } + _impl_.assistant_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerAssist::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerAssist.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player assistant = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_assistant(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player victim = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_victim(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string weapon = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_weapon(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerAssist.weapon")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerAssist::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerAssist) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerAssist.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player assistant = 3; + if (this->_internal_has_assistant()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::assistant(this), + _Internal::assistant(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::victim(this), + _Internal::victim(this).GetCachedSize(), target, stream); + } + + // string weapon = 5; + if (!this->_internal_weapon().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_weapon().data(), static_cast(this->_internal_weapon().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerAssist.weapon"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_weapon(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerAssist) + return target; +} + +size_t PlayerAssist::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerAssist) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string weapon = 5; + if (!this->_internal_weapon().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_weapon()); + } + + // .rtech.liveapi.Player assistant = 3; + if (this->_internal_has_assistant()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.assistant_); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.victim_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerAssist::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerAssist::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerAssist::GetClassData() const { return &_class_data_; } + + +void PlayerAssist::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerAssist) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_weapon().empty()) { + _this->_internal_set_weapon(from._internal_weapon()); + } + if (from._internal_has_assistant()) { + _this->_internal_mutable_assistant()->::rtech::liveapi::Player::MergeFrom( + from._internal_assistant()); + } + if (from._internal_has_victim()) { + _this->_internal_mutable_victim()->::rtech::liveapi::Player::MergeFrom( + from._internal_victim()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerAssist::CopyFrom(const PlayerAssist& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerAssist) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerAssist::IsInitialized() const { + return true; +} + +void PlayerAssist::InternalSwap(PlayerAssist* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.weapon_, lhs_arena, + &other->_impl_.weapon_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerAssist, _impl_.timestamp_) + + sizeof(PlayerAssist::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerAssist, _impl_.assistant_)>( + reinterpret_cast(&_impl_.assistant_), + reinterpret_cast(&other->_impl_.assistant_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerAssist::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[24]); +} + +// =================================================================== + +class SquadEliminated::_Internal { + public: +}; + +SquadEliminated::SquadEliminated(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.SquadEliminated) +} +SquadEliminated::SquadEliminated(const SquadEliminated& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + SquadEliminated* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.players_){from._impl_.players_} + , decltype(_impl_.category_){} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.SquadEliminated) +} + +inline void SquadEliminated::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.players_){arena} + , decltype(_impl_.category_){} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +SquadEliminated::~SquadEliminated() { + // @@protoc_insertion_point(destructor:rtech.liveapi.SquadEliminated) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void SquadEliminated::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.players_.~RepeatedPtrField(); + _impl_.category_.Destroy(); +} + +void SquadEliminated::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void SquadEliminated::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.SquadEliminated) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.players_.Clear(); + _impl_.category_.ClearToEmpty(); + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* SquadEliminated::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.SquadEliminated.category")); + } else + goto handle_unusual; + continue; + // repeated .rtech.liveapi.Player players = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr -= 1; + do { + ptr += 1; + ptr = ctx->ParseMessage(_internal_add_players(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* SquadEliminated::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.SquadEliminated) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.SquadEliminated.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // repeated .rtech.liveapi.Player players = 3; + for (unsigned i = 0, + n = static_cast(this->_internal_players_size()); i < n; i++) { + const auto& repfield = this->_internal_players(i); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, repfield, repfield.GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.SquadEliminated) + return target; +} + +size_t SquadEliminated::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.SquadEliminated) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated .rtech.liveapi.Player players = 3; + total_size += 1UL * this->_internal_players_size(); + for (const auto& msg : this->_impl_.players_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData SquadEliminated::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + SquadEliminated::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*SquadEliminated::GetClassData() const { return &_class_data_; } + + +void SquadEliminated::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.SquadEliminated) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + _this->_impl_.players_.MergeFrom(from._impl_.players_); + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void SquadEliminated::CopyFrom(const SquadEliminated& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.SquadEliminated) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool SquadEliminated::IsInitialized() const { + return true; +} + +void SquadEliminated::InternalSwap(SquadEliminated* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + _impl_.players_.InternalSwap(&other->_impl_.players_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + swap(_impl_.timestamp_, other->_impl_.timestamp_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata SquadEliminated::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[25]); +} + +// =================================================================== + +class GibraltarShieldAbsorbed::_Internal { + public: + static const ::rtech::liveapi::Player& attacker(const GibraltarShieldAbsorbed* msg); + static const ::rtech::liveapi::Player& victim(const GibraltarShieldAbsorbed* msg); +}; + +const ::rtech::liveapi::Player& +GibraltarShieldAbsorbed::_Internal::attacker(const GibraltarShieldAbsorbed* msg) { + return *msg->_impl_.attacker_; +} +const ::rtech::liveapi::Player& +GibraltarShieldAbsorbed::_Internal::victim(const GibraltarShieldAbsorbed* msg) { + return *msg->_impl_.victim_; +} +GibraltarShieldAbsorbed::GibraltarShieldAbsorbed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.GibraltarShieldAbsorbed) +} +GibraltarShieldAbsorbed::GibraltarShieldAbsorbed(const GibraltarShieldAbsorbed& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + GibraltarShieldAbsorbed* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.damageinflicted_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_attacker()) { + _this->_impl_.attacker_ = new ::rtech::liveapi::Player(*from._impl_.attacker_); + } + if (from._internal_has_victim()) { + _this->_impl_.victim_ = new ::rtech::liveapi::Player(*from._impl_.victim_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.damageinflicted_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.damageinflicted_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.GibraltarShieldAbsorbed) +} + +inline void GibraltarShieldAbsorbed::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.damageinflicted_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +GibraltarShieldAbsorbed::~GibraltarShieldAbsorbed() { + // @@protoc_insertion_point(destructor:rtech.liveapi.GibraltarShieldAbsorbed) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void GibraltarShieldAbsorbed::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.attacker_; + if (this != internal_default_instance()) delete _impl_.victim_; +} + +void GibraltarShieldAbsorbed::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void GibraltarShieldAbsorbed::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.GibraltarShieldAbsorbed) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.damageinflicted_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.damageinflicted_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* GibraltarShieldAbsorbed::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.GibraltarShieldAbsorbed.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player attacker = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_attacker(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player victim = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_victim(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 damageInflicted = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _impl_.damageinflicted_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* GibraltarShieldAbsorbed::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.GibraltarShieldAbsorbed) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.GibraltarShieldAbsorbed.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::attacker(this), + _Internal::attacker(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::victim(this), + _Internal::victim(this).GetCachedSize(), target, stream); + } + + // uint32 damageInflicted = 6; + if (this->_internal_damageinflicted() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(6, this->_internal_damageinflicted(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.GibraltarShieldAbsorbed) + return target; +} + +size_t GibraltarShieldAbsorbed::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.GibraltarShieldAbsorbed) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.attacker_); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.victim_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // uint32 damageInflicted = 6; + if (this->_internal_damageinflicted() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_damageinflicted()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData GibraltarShieldAbsorbed::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + GibraltarShieldAbsorbed::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GibraltarShieldAbsorbed::GetClassData() const { return &_class_data_; } + + +void GibraltarShieldAbsorbed::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.GibraltarShieldAbsorbed) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_attacker()) { + _this->_internal_mutable_attacker()->::rtech::liveapi::Player::MergeFrom( + from._internal_attacker()); + } + if (from._internal_has_victim()) { + _this->_internal_mutable_victim()->::rtech::liveapi::Player::MergeFrom( + from._internal_victim()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_damageinflicted() != 0) { + _this->_internal_set_damageinflicted(from._internal_damageinflicted()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void GibraltarShieldAbsorbed::CopyFrom(const GibraltarShieldAbsorbed& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.GibraltarShieldAbsorbed) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool GibraltarShieldAbsorbed::IsInitialized() const { + return true; +} + +void GibraltarShieldAbsorbed::InternalSwap(GibraltarShieldAbsorbed* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(GibraltarShieldAbsorbed, _impl_.damageinflicted_) + + sizeof(GibraltarShieldAbsorbed::_impl_.damageinflicted_) + - PROTOBUF_FIELD_OFFSET(GibraltarShieldAbsorbed, _impl_.attacker_)>( + reinterpret_cast(&_impl_.attacker_), + reinterpret_cast(&other->_impl_.attacker_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata GibraltarShieldAbsorbed::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[26]); +} + +// =================================================================== + +class RevenantForgedShadowDamaged::_Internal { + public: + static const ::rtech::liveapi::Player& attacker(const RevenantForgedShadowDamaged* msg); + static const ::rtech::liveapi::Player& victim(const RevenantForgedShadowDamaged* msg); +}; + +const ::rtech::liveapi::Player& +RevenantForgedShadowDamaged::_Internal::attacker(const RevenantForgedShadowDamaged* msg) { + return *msg->_impl_.attacker_; +} +const ::rtech::liveapi::Player& +RevenantForgedShadowDamaged::_Internal::victim(const RevenantForgedShadowDamaged* msg) { + return *msg->_impl_.victim_; +} +RevenantForgedShadowDamaged::RevenantForgedShadowDamaged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.RevenantForgedShadowDamaged) +} +RevenantForgedShadowDamaged::RevenantForgedShadowDamaged(const RevenantForgedShadowDamaged& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + RevenantForgedShadowDamaged* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.damageinflicted_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_attacker()) { + _this->_impl_.attacker_ = new ::rtech::liveapi::Player(*from._impl_.attacker_); + } + if (from._internal_has_victim()) { + _this->_impl_.victim_ = new ::rtech::liveapi::Player(*from._impl_.victim_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.damageinflicted_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.damageinflicted_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.RevenantForgedShadowDamaged) +} + +inline void RevenantForgedShadowDamaged::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.attacker_){nullptr} + , decltype(_impl_.victim_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.damageinflicted_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +RevenantForgedShadowDamaged::~RevenantForgedShadowDamaged() { + // @@protoc_insertion_point(destructor:rtech.liveapi.RevenantForgedShadowDamaged) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void RevenantForgedShadowDamaged::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.attacker_; + if (this != internal_default_instance()) delete _impl_.victim_; +} + +void RevenantForgedShadowDamaged::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void RevenantForgedShadowDamaged::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.RevenantForgedShadowDamaged) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.damageinflicted_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.damageinflicted_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* RevenantForgedShadowDamaged::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.RevenantForgedShadowDamaged.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player attacker = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_attacker(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player victim = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_victim(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 damageInflicted = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _impl_.damageinflicted_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* RevenantForgedShadowDamaged::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.RevenantForgedShadowDamaged) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.RevenantForgedShadowDamaged.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::attacker(this), + _Internal::attacker(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::victim(this), + _Internal::victim(this).GetCachedSize(), target, stream); + } + + // uint32 damageInflicted = 6; + if (this->_internal_damageinflicted() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(6, this->_internal_damageinflicted(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.RevenantForgedShadowDamaged) + return target; +} + +size_t RevenantForgedShadowDamaged::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.RevenantForgedShadowDamaged) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player attacker = 3; + if (this->_internal_has_attacker()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.attacker_); + } + + // .rtech.liveapi.Player victim = 4; + if (this->_internal_has_victim()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.victim_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // uint32 damageInflicted = 6; + if (this->_internal_damageinflicted() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_damageinflicted()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData RevenantForgedShadowDamaged::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + RevenantForgedShadowDamaged::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*RevenantForgedShadowDamaged::GetClassData() const { return &_class_data_; } + + +void RevenantForgedShadowDamaged::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.RevenantForgedShadowDamaged) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_attacker()) { + _this->_internal_mutable_attacker()->::rtech::liveapi::Player::MergeFrom( + from._internal_attacker()); + } + if (from._internal_has_victim()) { + _this->_internal_mutable_victim()->::rtech::liveapi::Player::MergeFrom( + from._internal_victim()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_damageinflicted() != 0) { + _this->_internal_set_damageinflicted(from._internal_damageinflicted()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void RevenantForgedShadowDamaged::CopyFrom(const RevenantForgedShadowDamaged& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.RevenantForgedShadowDamaged) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool RevenantForgedShadowDamaged::IsInitialized() const { + return true; +} + +void RevenantForgedShadowDamaged::InternalSwap(RevenantForgedShadowDamaged* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(RevenantForgedShadowDamaged, _impl_.damageinflicted_) + + sizeof(RevenantForgedShadowDamaged::_impl_.damageinflicted_) + - PROTOBUF_FIELD_OFFSET(RevenantForgedShadowDamaged, _impl_.attacker_)>( + reinterpret_cast(&_impl_.attacker_), + reinterpret_cast(&other->_impl_.attacker_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata RevenantForgedShadowDamaged::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[27]); +} + +// =================================================================== + +class PlayerRespawnTeam::_Internal { + public: + static const ::rtech::liveapi::Player& player(const PlayerRespawnTeam* msg); +}; + +const ::rtech::liveapi::Player& +PlayerRespawnTeam::_Internal::player(const PlayerRespawnTeam* msg) { + return *msg->_impl_.player_; +} +PlayerRespawnTeam::PlayerRespawnTeam(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerRespawnTeam) +} +PlayerRespawnTeam::PlayerRespawnTeam(const PlayerRespawnTeam& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerRespawnTeam* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.respawned_){from._impl_.respawned_} + , decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerRespawnTeam) +} + +inline void PlayerRespawnTeam::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.respawned_){arena} + , decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerRespawnTeam::~PlayerRespawnTeam() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerRespawnTeam) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerRespawnTeam::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.respawned_.~RepeatedPtrField(); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void PlayerRespawnTeam::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerRespawnTeam::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerRespawnTeam) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.respawned_.Clear(); + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerRespawnTeam::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerRespawnTeam.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated .rtech.liveapi.Player respawned = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr -= 1; + do { + ptr += 1; + ptr = ctx->ParseMessage(_internal_add_respawned(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerRespawnTeam::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerRespawnTeam) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerRespawnTeam.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // repeated .rtech.liveapi.Player respawned = 4; + for (unsigned i = 0, + n = static_cast(this->_internal_respawned_size()); i < n; i++) { + const auto& repfield = this->_internal_respawned(i); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, repfield, repfield.GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerRespawnTeam) + return target; +} + +size_t PlayerRespawnTeam::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerRespawnTeam) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated .rtech.liveapi.Player respawned = 4; + total_size += 1UL * this->_internal_respawned_size(); + for (const auto& msg : this->_impl_.respawned_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerRespawnTeam::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerRespawnTeam::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerRespawnTeam::GetClassData() const { return &_class_data_; } + + +void PlayerRespawnTeam::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerRespawnTeam) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + _this->_impl_.respawned_.MergeFrom(from._impl_.respawned_); + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerRespawnTeam::CopyFrom(const PlayerRespawnTeam& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerRespawnTeam) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerRespawnTeam::IsInitialized() const { + return true; +} + +void PlayerRespawnTeam::InternalSwap(PlayerRespawnTeam* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + _impl_.respawned_.InternalSwap(&other->_impl_.respawned_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerRespawnTeam, _impl_.timestamp_) + + sizeof(PlayerRespawnTeam::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerRespawnTeam, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerRespawnTeam::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[28]); +} + +// =================================================================== + +class PlayerRevive::_Internal { + public: + static const ::rtech::liveapi::Player& player(const PlayerRevive* msg); + static const ::rtech::liveapi::Player& revived(const PlayerRevive* msg); +}; + +const ::rtech::liveapi::Player& +PlayerRevive::_Internal::player(const PlayerRevive* msg) { + return *msg->_impl_.player_; +} +const ::rtech::liveapi::Player& +PlayerRevive::_Internal::revived(const PlayerRevive* msg) { + return *msg->_impl_.revived_; +} +PlayerRevive::PlayerRevive(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerRevive) +} +PlayerRevive::PlayerRevive(const PlayerRevive& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerRevive* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.revived_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + if (from._internal_has_revived()) { + _this->_impl_.revived_ = new ::rtech::liveapi::Player(*from._impl_.revived_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerRevive) +} + +inline void PlayerRevive::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.revived_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerRevive::~PlayerRevive() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerRevive) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerRevive::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; + if (this != internal_default_instance()) delete _impl_.revived_; +} + +void PlayerRevive::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerRevive::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerRevive) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.revived_ != nullptr) { + delete _impl_.revived_; + } + _impl_.revived_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerRevive::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerRevive.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player revived = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_revived(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerRevive::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerRevive) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerRevive.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player revived = 4; + if (this->_internal_has_revived()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::revived(this), + _Internal::revived(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerRevive) + return target; +} + +size_t PlayerRevive::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerRevive) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // .rtech.liveapi.Player revived = 4; + if (this->_internal_has_revived()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.revived_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerRevive::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerRevive::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerRevive::GetClassData() const { return &_class_data_; } + + +void PlayerRevive::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerRevive) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_has_revived()) { + _this->_internal_mutable_revived()->::rtech::liveapi::Player::MergeFrom( + from._internal_revived()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerRevive::CopyFrom(const PlayerRevive& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerRevive) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerRevive::IsInitialized() const { + return true; +} + +void PlayerRevive::InternalSwap(PlayerRevive* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerRevive, _impl_.timestamp_) + + sizeof(PlayerRevive::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerRevive, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerRevive::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[29]); +} + +// =================================================================== + +class ArenasItemSelected::_Internal { + public: + static const ::rtech::liveapi::Player& player(const ArenasItemSelected* msg); +}; + +const ::rtech::liveapi::Player& +ArenasItemSelected::_Internal::player(const ArenasItemSelected* msg) { + return *msg->_impl_.player_; +} +ArenasItemSelected::ArenasItemSelected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.ArenasItemSelected) +} +ArenasItemSelected::ArenasItemSelected(const ArenasItemSelected& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + ArenasItemSelected* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.quantity_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_item().empty()) { + _this->_impl_.item_.Set(from._internal_item(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.ArenasItemSelected) +} + +inline void ArenasItemSelected::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.quantity_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +ArenasItemSelected::~ArenasItemSelected() { + // @@protoc_insertion_point(destructor:rtech.liveapi.ArenasItemSelected) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void ArenasItemSelected::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.item_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void ArenasItemSelected::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void ArenasItemSelected::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.ArenasItemSelected) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.item_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ArenasItemSelected::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ArenasItemSelected.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string item = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_item(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ArenasItemSelected.item")); + } else + goto handle_unusual; + continue; + // int32 quantity = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.quantity_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ArenasItemSelected::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.ArenasItemSelected) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ArenasItemSelected.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_item().data(), static_cast(this->_internal_item().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ArenasItemSelected.item"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_item(), target); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(5, this->_internal_quantity(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.ArenasItemSelected) + return target; +} + +size_t ArenasItemSelected::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.ArenasItemSelected) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_item()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_quantity()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ArenasItemSelected::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + ArenasItemSelected::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ArenasItemSelected::GetClassData() const { return &_class_data_; } + + +void ArenasItemSelected::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.ArenasItemSelected) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_item().empty()) { + _this->_internal_set_item(from._internal_item()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_quantity() != 0) { + _this->_internal_set_quantity(from._internal_quantity()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ArenasItemSelected::CopyFrom(const ArenasItemSelected& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.ArenasItemSelected) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ArenasItemSelected::IsInitialized() const { + return true; +} + +void ArenasItemSelected::InternalSwap(ArenasItemSelected* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.item_, lhs_arena, + &other->_impl_.item_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(ArenasItemSelected, _impl_.quantity_) + + sizeof(ArenasItemSelected::_impl_.quantity_) + - PROTOBUF_FIELD_OFFSET(ArenasItemSelected, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ArenasItemSelected::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[30]); +} + +// =================================================================== + +class ArenasItemDeselected::_Internal { + public: + static const ::rtech::liveapi::Player& player(const ArenasItemDeselected* msg); +}; + +const ::rtech::liveapi::Player& +ArenasItemDeselected::_Internal::player(const ArenasItemDeselected* msg) { + return *msg->_impl_.player_; +} +ArenasItemDeselected::ArenasItemDeselected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.ArenasItemDeselected) +} +ArenasItemDeselected::ArenasItemDeselected(const ArenasItemDeselected& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + ArenasItemDeselected* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.quantity_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_item().empty()) { + _this->_impl_.item_.Set(from._internal_item(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.ArenasItemDeselected) +} + +inline void ArenasItemDeselected::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.quantity_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +ArenasItemDeselected::~ArenasItemDeselected() { + // @@protoc_insertion_point(destructor:rtech.liveapi.ArenasItemDeselected) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void ArenasItemDeselected::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.item_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void ArenasItemDeselected::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void ArenasItemDeselected::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.ArenasItemDeselected) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.item_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ArenasItemDeselected::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ArenasItemDeselected.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string item = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_item(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ArenasItemDeselected.item")); + } else + goto handle_unusual; + continue; + // int32 quantity = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.quantity_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ArenasItemDeselected::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.ArenasItemDeselected) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ArenasItemDeselected.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_item().data(), static_cast(this->_internal_item().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ArenasItemDeselected.item"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_item(), target); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(5, this->_internal_quantity(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.ArenasItemDeselected) + return target; +} + +size_t ArenasItemDeselected::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.ArenasItemDeselected) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_item()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_quantity()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ArenasItemDeselected::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + ArenasItemDeselected::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ArenasItemDeselected::GetClassData() const { return &_class_data_; } + + +void ArenasItemDeselected::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.ArenasItemDeselected) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_item().empty()) { + _this->_internal_set_item(from._internal_item()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_quantity() != 0) { + _this->_internal_set_quantity(from._internal_quantity()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ArenasItemDeselected::CopyFrom(const ArenasItemDeselected& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.ArenasItemDeselected) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ArenasItemDeselected::IsInitialized() const { + return true; +} + +void ArenasItemDeselected::InternalSwap(ArenasItemDeselected* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.item_, lhs_arena, + &other->_impl_.item_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(ArenasItemDeselected, _impl_.quantity_) + + sizeof(ArenasItemDeselected::_impl_.quantity_) + - PROTOBUF_FIELD_OFFSET(ArenasItemDeselected, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ArenasItemDeselected::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[31]); +} + +// =================================================================== + +class InventoryPickUp::_Internal { + public: + static const ::rtech::liveapi::Player& player(const InventoryPickUp* msg); +}; + +const ::rtech::liveapi::Player& +InventoryPickUp::_Internal::player(const InventoryPickUp* msg) { + return *msg->_impl_.player_; +} +InventoryPickUp::InventoryPickUp(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.InventoryPickUp) +} +InventoryPickUp::InventoryPickUp(const InventoryPickUp& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + InventoryPickUp* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.quantity_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_item().empty()) { + _this->_impl_.item_.Set(from._internal_item(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.InventoryPickUp) +} + +inline void InventoryPickUp::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.quantity_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +InventoryPickUp::~InventoryPickUp() { + // @@protoc_insertion_point(destructor:rtech.liveapi.InventoryPickUp) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void InventoryPickUp::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.item_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void InventoryPickUp::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void InventoryPickUp::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.InventoryPickUp) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.item_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* InventoryPickUp::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryPickUp.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string item = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_item(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryPickUp.item")); + } else + goto handle_unusual; + continue; + // int32 quantity = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.quantity_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* InventoryPickUp::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.InventoryPickUp) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryPickUp.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_item().data(), static_cast(this->_internal_item().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryPickUp.item"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_item(), target); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(5, this->_internal_quantity(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.InventoryPickUp) + return target; +} + +size_t InventoryPickUp::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.InventoryPickUp) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_item()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_quantity()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData InventoryPickUp::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + InventoryPickUp::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*InventoryPickUp::GetClassData() const { return &_class_data_; } + + +void InventoryPickUp::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.InventoryPickUp) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_item().empty()) { + _this->_internal_set_item(from._internal_item()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_quantity() != 0) { + _this->_internal_set_quantity(from._internal_quantity()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void InventoryPickUp::CopyFrom(const InventoryPickUp& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.InventoryPickUp) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool InventoryPickUp::IsInitialized() const { + return true; +} + +void InventoryPickUp::InternalSwap(InventoryPickUp* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.item_, lhs_arena, + &other->_impl_.item_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(InventoryPickUp, _impl_.quantity_) + + sizeof(InventoryPickUp::_impl_.quantity_) + - PROTOBUF_FIELD_OFFSET(InventoryPickUp, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata InventoryPickUp::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[32]); +} + +// =================================================================== + +class InventoryDrop::_Internal { + public: + static const ::rtech::liveapi::Player& player(const InventoryDrop* msg); +}; + +const ::rtech::liveapi::Player& +InventoryDrop::_Internal::player(const InventoryDrop* msg) { + return *msg->_impl_.player_; +} +InventoryDrop::InventoryDrop(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.InventoryDrop) +} +InventoryDrop::InventoryDrop(const InventoryDrop& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + InventoryDrop* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.extradata_){from._impl_.extradata_} + , decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.quantity_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_item().empty()) { + _this->_impl_.item_.Set(from._internal_item(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.InventoryDrop) +} + +inline void InventoryDrop::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.extradata_){arena} + , decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.quantity_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +InventoryDrop::~InventoryDrop() { + // @@protoc_insertion_point(destructor:rtech.liveapi.InventoryDrop) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void InventoryDrop::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.extradata_.~RepeatedPtrField(); + _impl_.category_.Destroy(); + _impl_.item_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void InventoryDrop::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void InventoryDrop::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.InventoryDrop) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.extradata_.Clear(); + _impl_.category_.ClearToEmpty(); + _impl_.item_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* InventoryDrop::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryDrop.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string item = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_item(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryDrop.item")); + } else + goto handle_unusual; + continue; + // int32 quantity = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.quantity_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated string extraData = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 50)) { + ptr -= 1; + do { + ptr += 1; + auto str = _internal_add_extradata(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryDrop.extraData")); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<50>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* InventoryDrop::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.InventoryDrop) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryDrop.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_item().data(), static_cast(this->_internal_item().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryDrop.item"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_item(), target); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(5, this->_internal_quantity(), target); + } + + // repeated string extraData = 6; + for (int i = 0, n = this->_internal_extradata_size(); i < n; i++) { + const auto& s = this->_internal_extradata(i); + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + s.data(), static_cast(s.length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryDrop.extraData"); + target = stream->WriteString(6, s, target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.InventoryDrop) + return target; +} + +size_t InventoryDrop::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.InventoryDrop) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated string extraData = 6; + total_size += 1 * + ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.extradata_.size()); + for (int i = 0, n = _impl_.extradata_.size(); i < n; i++) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + _impl_.extradata_.Get(i)); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_item()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_quantity()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData InventoryDrop::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + InventoryDrop::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*InventoryDrop::GetClassData() const { return &_class_data_; } + + +void InventoryDrop::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.InventoryDrop) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + _this->_impl_.extradata_.MergeFrom(from._impl_.extradata_); + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_item().empty()) { + _this->_internal_set_item(from._internal_item()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_quantity() != 0) { + _this->_internal_set_quantity(from._internal_quantity()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void InventoryDrop::CopyFrom(const InventoryDrop& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.InventoryDrop) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool InventoryDrop::IsInitialized() const { + return true; +} + +void InventoryDrop::InternalSwap(InventoryDrop* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + _impl_.extradata_.InternalSwap(&other->_impl_.extradata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.item_, lhs_arena, + &other->_impl_.item_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(InventoryDrop, _impl_.quantity_) + + sizeof(InventoryDrop::_impl_.quantity_) + - PROTOBUF_FIELD_OFFSET(InventoryDrop, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata InventoryDrop::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[33]); +} + +// =================================================================== + +class InventoryUse::_Internal { + public: + static const ::rtech::liveapi::Player& player(const InventoryUse* msg); +}; + +const ::rtech::liveapi::Player& +InventoryUse::_Internal::player(const InventoryUse* msg) { + return *msg->_impl_.player_; +} +InventoryUse::InventoryUse(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.InventoryUse) +} +InventoryUse::InventoryUse(const InventoryUse& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + InventoryUse* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.quantity_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_item().empty()) { + _this->_impl_.item_.Set(from._internal_item(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.InventoryUse) +} + +inline void InventoryUse::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.quantity_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +InventoryUse::~InventoryUse() { + // @@protoc_insertion_point(destructor:rtech.liveapi.InventoryUse) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void InventoryUse::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.item_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void InventoryUse::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void InventoryUse::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.InventoryUse) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.item_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.quantity_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.quantity_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* InventoryUse::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryUse.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string item = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_item(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.InventoryUse.item")); + } else + goto handle_unusual; + continue; + // int32 quantity = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.quantity_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* InventoryUse::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.InventoryUse) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryUse.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_item().data(), static_cast(this->_internal_item().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.InventoryUse.item"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_item(), target); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(5, this->_internal_quantity(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.InventoryUse) + return target; +} + +size_t InventoryUse::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.InventoryUse) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_item()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 quantity = 5; + if (this->_internal_quantity() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_quantity()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData InventoryUse::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + InventoryUse::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*InventoryUse::GetClassData() const { return &_class_data_; } + + +void InventoryUse::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.InventoryUse) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_item().empty()) { + _this->_internal_set_item(from._internal_item()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_quantity() != 0) { + _this->_internal_set_quantity(from._internal_quantity()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void InventoryUse::CopyFrom(const InventoryUse& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.InventoryUse) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool InventoryUse::IsInitialized() const { + return true; +} + +void InventoryUse::InternalSwap(InventoryUse* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.item_, lhs_arena, + &other->_impl_.item_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(InventoryUse, _impl_.quantity_) + + sizeof(InventoryUse::_impl_.quantity_) + - PROTOBUF_FIELD_OFFSET(InventoryUse, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata InventoryUse::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[34]); +} + +// =================================================================== + +class BannerCollected::_Internal { + public: + static const ::rtech::liveapi::Player& player(const BannerCollected* msg); + static const ::rtech::liveapi::Player& collected(const BannerCollected* msg); +}; + +const ::rtech::liveapi::Player& +BannerCollected::_Internal::player(const BannerCollected* msg) { + return *msg->_impl_.player_; +} +const ::rtech::liveapi::Player& +BannerCollected::_Internal::collected(const BannerCollected* msg) { + return *msg->_impl_.collected_; +} +BannerCollected::BannerCollected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.BannerCollected) +} +BannerCollected::BannerCollected(const BannerCollected& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + BannerCollected* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.collected_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + if (from._internal_has_collected()) { + _this->_impl_.collected_ = new ::rtech::liveapi::Player(*from._impl_.collected_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.BannerCollected) +} + +inline void BannerCollected::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.collected_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +BannerCollected::~BannerCollected() { + // @@protoc_insertion_point(destructor:rtech.liveapi.BannerCollected) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void BannerCollected::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; + if (this != internal_default_instance()) delete _impl_.collected_; +} + +void BannerCollected::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void BannerCollected::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.BannerCollected) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + if (GetArenaForAllocation() == nullptr && _impl_.collected_ != nullptr) { + delete _impl_.collected_; + } + _impl_.collected_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* BannerCollected::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.BannerCollected.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player collected = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_collected(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* BannerCollected::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.BannerCollected) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.BannerCollected.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.Player collected = 4; + if (this->_internal_has_collected()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::collected(this), + _Internal::collected(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.BannerCollected) + return target; +} + +size_t BannerCollected::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.BannerCollected) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // .rtech.liveapi.Player collected = 4; + if (this->_internal_has_collected()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.collected_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData BannerCollected::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + BannerCollected::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*BannerCollected::GetClassData() const { return &_class_data_; } + + +void BannerCollected::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.BannerCollected) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_has_collected()) { + _this->_internal_mutable_collected()->::rtech::liveapi::Player::MergeFrom( + from._internal_collected()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void BannerCollected::CopyFrom(const BannerCollected& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.BannerCollected) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool BannerCollected::IsInitialized() const { + return true; +} + +void BannerCollected::InternalSwap(BannerCollected* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(BannerCollected, _impl_.timestamp_) + + sizeof(BannerCollected::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(BannerCollected, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata BannerCollected::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[35]); +} + +// =================================================================== + +class PlayerAbilityUsed::_Internal { + public: + static const ::rtech::liveapi::Player& player(const PlayerAbilityUsed* msg); +}; + +const ::rtech::liveapi::Player& +PlayerAbilityUsed::_Internal::player(const PlayerAbilityUsed* msg) { + return *msg->_impl_.player_; +} +PlayerAbilityUsed::PlayerAbilityUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PlayerAbilityUsed) +} +PlayerAbilityUsed::PlayerAbilityUsed(const PlayerAbilityUsed& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PlayerAbilityUsed* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.linkedentity_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.linkedentity_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_linkedentity().empty()) { + _this->_impl_.linkedentity_.Set(from._internal_linkedentity(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PlayerAbilityUsed) +} + +inline void PlayerAbilityUsed::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.linkedentity_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +PlayerAbilityUsed::~PlayerAbilityUsed() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PlayerAbilityUsed) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PlayerAbilityUsed::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.linkedentity_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void PlayerAbilityUsed::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PlayerAbilityUsed::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PlayerAbilityUsed) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.linkedentity_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PlayerAbilityUsed::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerAbilityUsed.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string linkedEntity = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_linkedentity(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.PlayerAbilityUsed.linkedEntity")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PlayerAbilityUsed::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PlayerAbilityUsed) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerAbilityUsed.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string linkedEntity = 4; + if (!this->_internal_linkedentity().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_linkedentity().data(), static_cast(this->_internal_linkedentity().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.PlayerAbilityUsed.linkedEntity"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_linkedentity(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PlayerAbilityUsed) + return target; +} + +size_t PlayerAbilityUsed::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PlayerAbilityUsed) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string linkedEntity = 4; + if (!this->_internal_linkedentity().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_linkedentity()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PlayerAbilityUsed::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PlayerAbilityUsed::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PlayerAbilityUsed::GetClassData() const { return &_class_data_; } + + +void PlayerAbilityUsed::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PlayerAbilityUsed) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_linkedentity().empty()) { + _this->_internal_set_linkedentity(from._internal_linkedentity()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PlayerAbilityUsed::CopyFrom(const PlayerAbilityUsed& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PlayerAbilityUsed) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PlayerAbilityUsed::IsInitialized() const { + return true; +} + +void PlayerAbilityUsed::InternalSwap(PlayerAbilityUsed* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.linkedentity_, lhs_arena, + &other->_impl_.linkedentity_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(PlayerAbilityUsed, _impl_.timestamp_) + + sizeof(PlayerAbilityUsed::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(PlayerAbilityUsed, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PlayerAbilityUsed::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[36]); +} + +// =================================================================== + +class LegendUpgradeSelected::_Internal { + public: + static const ::rtech::liveapi::Player& player(const LegendUpgradeSelected* msg); +}; + +const ::rtech::liveapi::Player& +LegendUpgradeSelected::_Internal::player(const LegendUpgradeSelected* msg) { + return *msg->_impl_.player_; +} +LegendUpgradeSelected::LegendUpgradeSelected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.LegendUpgradeSelected) +} +LegendUpgradeSelected::LegendUpgradeSelected(const LegendUpgradeSelected& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + LegendUpgradeSelected* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.upgradename_){} + , decltype(_impl_.upgradedesc_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.level_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.upgradename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.upgradename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_upgradename().empty()) { + _this->_impl_.upgradename_.Set(from._internal_upgradename(), + _this->GetArenaForAllocation()); + } + _impl_.upgradedesc_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.upgradedesc_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_upgradedesc().empty()) { + _this->_impl_.upgradedesc_.Set(from._internal_upgradedesc(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.level_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.level_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.LegendUpgradeSelected) +} + +inline void LegendUpgradeSelected::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.upgradename_){} + , decltype(_impl_.upgradedesc_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.level_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.upgradename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.upgradename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.upgradedesc_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.upgradedesc_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +LegendUpgradeSelected::~LegendUpgradeSelected() { + // @@protoc_insertion_point(destructor:rtech.liveapi.LegendUpgradeSelected) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void LegendUpgradeSelected::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.upgradename_.Destroy(); + _impl_.upgradedesc_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void LegendUpgradeSelected::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void LegendUpgradeSelected::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.LegendUpgradeSelected) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.upgradename_.ClearToEmpty(); + _impl_.upgradedesc_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.level_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.level_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* LegendUpgradeSelected::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.LegendUpgradeSelected.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string upgradeName = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_upgradename(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.LegendUpgradeSelected.upgradeName")); + } else + goto handle_unusual; + continue; + // string upgradeDesc = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_upgradedesc(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.LegendUpgradeSelected.upgradeDesc")); + } else + goto handle_unusual; + continue; + // int32 level = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _impl_.level_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* LegendUpgradeSelected::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.LegendUpgradeSelected) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.LegendUpgradeSelected.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string upgradeName = 4; + if (!this->_internal_upgradename().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_upgradename().data(), static_cast(this->_internal_upgradename().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.LegendUpgradeSelected.upgradeName"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_upgradename(), target); + } + + // string upgradeDesc = 5; + if (!this->_internal_upgradedesc().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_upgradedesc().data(), static_cast(this->_internal_upgradedesc().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.LegendUpgradeSelected.upgradeDesc"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_upgradedesc(), target); + } + + // int32 level = 6; + if (this->_internal_level() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(6, this->_internal_level(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.LegendUpgradeSelected) + return target; +} + +size_t LegendUpgradeSelected::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.LegendUpgradeSelected) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string upgradeName = 4; + if (!this->_internal_upgradename().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_upgradename()); + } + + // string upgradeDesc = 5; + if (!this->_internal_upgradedesc().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_upgradedesc()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // int32 level = 6; + if (this->_internal_level() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_level()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData LegendUpgradeSelected::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + LegendUpgradeSelected::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*LegendUpgradeSelected::GetClassData() const { return &_class_data_; } + + +void LegendUpgradeSelected::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.LegendUpgradeSelected) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_upgradename().empty()) { + _this->_internal_set_upgradename(from._internal_upgradename()); + } + if (!from._internal_upgradedesc().empty()) { + _this->_internal_set_upgradedesc(from._internal_upgradedesc()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_level() != 0) { + _this->_internal_set_level(from._internal_level()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void LegendUpgradeSelected::CopyFrom(const LegendUpgradeSelected& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.LegendUpgradeSelected) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool LegendUpgradeSelected::IsInitialized() const { + return true; +} + +void LegendUpgradeSelected::InternalSwap(LegendUpgradeSelected* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.upgradename_, lhs_arena, + &other->_impl_.upgradename_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.upgradedesc_, lhs_arena, + &other->_impl_.upgradedesc_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(LegendUpgradeSelected, _impl_.level_) + + sizeof(LegendUpgradeSelected::_impl_.level_) + - PROTOBUF_FIELD_OFFSET(LegendUpgradeSelected, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata LegendUpgradeSelected::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[37]); +} + +// =================================================================== + +class ZiplineUsed::_Internal { + public: + static const ::rtech::liveapi::Player& player(const ZiplineUsed* msg); +}; + +const ::rtech::liveapi::Player& +ZiplineUsed::_Internal::player(const ZiplineUsed* msg) { + return *msg->_impl_.player_; +} +ZiplineUsed::ZiplineUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.ZiplineUsed) +} +ZiplineUsed::ZiplineUsed(const ZiplineUsed& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + ZiplineUsed* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.linkedentity_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.linkedentity_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_linkedentity().empty()) { + _this->_impl_.linkedentity_.Set(from._internal_linkedentity(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.ZiplineUsed) +} + +inline void ZiplineUsed::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.linkedentity_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +ZiplineUsed::~ZiplineUsed() { + // @@protoc_insertion_point(destructor:rtech.liveapi.ZiplineUsed) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void ZiplineUsed::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.linkedentity_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void ZiplineUsed::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void ZiplineUsed::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.ZiplineUsed) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.linkedentity_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ZiplineUsed::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ZiplineUsed.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string linkedEntity = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_linkedentity(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ZiplineUsed.linkedEntity")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ZiplineUsed::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.ZiplineUsed) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ZiplineUsed.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string linkedEntity = 4; + if (!this->_internal_linkedentity().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_linkedentity().data(), static_cast(this->_internal_linkedentity().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ZiplineUsed.linkedEntity"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_linkedentity(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.ZiplineUsed) + return target; +} + +size_t ZiplineUsed::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.ZiplineUsed) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string linkedEntity = 4; + if (!this->_internal_linkedentity().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_linkedentity()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ZiplineUsed::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + ZiplineUsed::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ZiplineUsed::GetClassData() const { return &_class_data_; } + + +void ZiplineUsed::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.ZiplineUsed) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_linkedentity().empty()) { + _this->_internal_set_linkedentity(from._internal_linkedentity()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ZiplineUsed::CopyFrom(const ZiplineUsed& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.ZiplineUsed) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ZiplineUsed::IsInitialized() const { + return true; +} + +void ZiplineUsed::InternalSwap(ZiplineUsed* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.linkedentity_, lhs_arena, + &other->_impl_.linkedentity_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(ZiplineUsed, _impl_.timestamp_) + + sizeof(ZiplineUsed::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(ZiplineUsed, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ZiplineUsed::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[38]); +} + +// =================================================================== + +class GrenadeThrown::_Internal { + public: + static const ::rtech::liveapi::Player& player(const GrenadeThrown* msg); +}; + +const ::rtech::liveapi::Player& +GrenadeThrown::_Internal::player(const GrenadeThrown* msg) { + return *msg->_impl_.player_; +} +GrenadeThrown::GrenadeThrown(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.GrenadeThrown) +} +GrenadeThrown::GrenadeThrown(const GrenadeThrown& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + GrenadeThrown* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.linkedentity_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.linkedentity_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_linkedentity().empty()) { + _this->_impl_.linkedentity_.Set(from._internal_linkedentity(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.GrenadeThrown) +} + +inline void GrenadeThrown::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.linkedentity_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +GrenadeThrown::~GrenadeThrown() { + // @@protoc_insertion_point(destructor:rtech.liveapi.GrenadeThrown) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void GrenadeThrown::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.linkedentity_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void GrenadeThrown::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void GrenadeThrown::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.GrenadeThrown) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.linkedentity_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* GrenadeThrown::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.GrenadeThrown.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string linkedEntity = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_linkedentity(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.GrenadeThrown.linkedEntity")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* GrenadeThrown::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.GrenadeThrown) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.GrenadeThrown.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string linkedEntity = 4; + if (!this->_internal_linkedentity().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_linkedentity().data(), static_cast(this->_internal_linkedentity().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.GrenadeThrown.linkedEntity"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_linkedentity(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.GrenadeThrown) + return target; +} + +size_t GrenadeThrown::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.GrenadeThrown) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string linkedEntity = 4; + if (!this->_internal_linkedentity().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_linkedentity()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData GrenadeThrown::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + GrenadeThrown::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GrenadeThrown::GetClassData() const { return &_class_data_; } + + +void GrenadeThrown::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.GrenadeThrown) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_linkedentity().empty()) { + _this->_internal_set_linkedentity(from._internal_linkedentity()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void GrenadeThrown::CopyFrom(const GrenadeThrown& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.GrenadeThrown) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool GrenadeThrown::IsInitialized() const { + return true; +} + +void GrenadeThrown::InternalSwap(GrenadeThrown* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.linkedentity_, lhs_arena, + &other->_impl_.linkedentity_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(GrenadeThrown, _impl_.timestamp_) + + sizeof(GrenadeThrown::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(GrenadeThrown, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata GrenadeThrown::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[39]); +} + +// =================================================================== + +class BlackMarketAction::_Internal { + public: + static const ::rtech::liveapi::Player& player(const BlackMarketAction* msg); +}; + +const ::rtech::liveapi::Player& +BlackMarketAction::_Internal::player(const BlackMarketAction* msg) { + return *msg->_impl_.player_; +} +BlackMarketAction::BlackMarketAction(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.BlackMarketAction) +} +BlackMarketAction::BlackMarketAction(const BlackMarketAction& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + BlackMarketAction* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_item().empty()) { + _this->_impl_.item_.Set(from._internal_item(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.BlackMarketAction) +} + +inline void BlackMarketAction::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.item_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.item_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +BlackMarketAction::~BlackMarketAction() { + // @@protoc_insertion_point(destructor:rtech.liveapi.BlackMarketAction) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void BlackMarketAction::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.item_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void BlackMarketAction::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void BlackMarketAction::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.BlackMarketAction) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.item_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* BlackMarketAction::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.BlackMarketAction.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string item = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_item(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.BlackMarketAction.item")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* BlackMarketAction::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.BlackMarketAction) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.BlackMarketAction.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_item().data(), static_cast(this->_internal_item().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.BlackMarketAction.item"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_item(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.BlackMarketAction) + return target; +} + +size_t BlackMarketAction::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.BlackMarketAction) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string item = 4; + if (!this->_internal_item().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_item()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData BlackMarketAction::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + BlackMarketAction::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*BlackMarketAction::GetClassData() const { return &_class_data_; } + + +void BlackMarketAction::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.BlackMarketAction) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_item().empty()) { + _this->_internal_set_item(from._internal_item()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void BlackMarketAction::CopyFrom(const BlackMarketAction& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.BlackMarketAction) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool BlackMarketAction::IsInitialized() const { + return true; +} + +void BlackMarketAction::InternalSwap(BlackMarketAction* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.item_, lhs_arena, + &other->_impl_.item_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(BlackMarketAction, _impl_.timestamp_) + + sizeof(BlackMarketAction::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(BlackMarketAction, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata BlackMarketAction::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[40]); +} + +// =================================================================== + +class WraithPortal::_Internal { + public: + static const ::rtech::liveapi::Player& player(const WraithPortal* msg); +}; + +const ::rtech::liveapi::Player& +WraithPortal::_Internal::player(const WraithPortal* msg) { + return *msg->_impl_.player_; +} +WraithPortal::WraithPortal(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.WraithPortal) +} +WraithPortal::WraithPortal(const WraithPortal& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + WraithPortal* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.WraithPortal) +} + +inline void WraithPortal::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +WraithPortal::~WraithPortal() { + // @@protoc_insertion_point(destructor:rtech.liveapi.WraithPortal) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void WraithPortal::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void WraithPortal::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void WraithPortal::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.WraithPortal) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* WraithPortal::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.WraithPortal.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* WraithPortal::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.WraithPortal) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.WraithPortal.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.WraithPortal) + return target; +} + +size_t WraithPortal::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.WraithPortal) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData WraithPortal::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + WraithPortal::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*WraithPortal::GetClassData() const { return &_class_data_; } + + +void WraithPortal::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.WraithPortal) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void WraithPortal::CopyFrom(const WraithPortal& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.WraithPortal) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool WraithPortal::IsInitialized() const { + return true; +} + +void WraithPortal::InternalSwap(WraithPortal* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(WraithPortal, _impl_.timestamp_) + + sizeof(WraithPortal::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(WraithPortal, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata WraithPortal::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[41]); +} + +// =================================================================== + +class WarpGateUsed::_Internal { + public: + static const ::rtech::liveapi::Player& player(const WarpGateUsed* msg); +}; + +const ::rtech::liveapi::Player& +WarpGateUsed::_Internal::player(const WarpGateUsed* msg) { + return *msg->_impl_.player_; +} +WarpGateUsed::WarpGateUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.WarpGateUsed) +} +WarpGateUsed::WarpGateUsed(const WarpGateUsed& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + WarpGateUsed* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.WarpGateUsed) +} + +inline void WarpGateUsed::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +WarpGateUsed::~WarpGateUsed() { + // @@protoc_insertion_point(destructor:rtech.liveapi.WarpGateUsed) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void WarpGateUsed::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void WarpGateUsed::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void WarpGateUsed::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.WarpGateUsed) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* WarpGateUsed::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.WarpGateUsed.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* WarpGateUsed::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.WarpGateUsed) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.WarpGateUsed.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.WarpGateUsed) + return target; +} + +size_t WarpGateUsed::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.WarpGateUsed) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData WarpGateUsed::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + WarpGateUsed::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*WarpGateUsed::GetClassData() const { return &_class_data_; } + + +void WarpGateUsed::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.WarpGateUsed) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void WarpGateUsed::CopyFrom(const WarpGateUsed& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.WarpGateUsed) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool WarpGateUsed::IsInitialized() const { + return true; +} + +void WarpGateUsed::InternalSwap(WarpGateUsed* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(WarpGateUsed, _impl_.timestamp_) + + sizeof(WarpGateUsed::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(WarpGateUsed, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata WarpGateUsed::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[42]); +} + +// =================================================================== + +class AmmoUsed::_Internal { + public: + static const ::rtech::liveapi::Player& player(const AmmoUsed* msg); +}; + +const ::rtech::liveapi::Player& +AmmoUsed::_Internal::player(const AmmoUsed* msg) { + return *msg->_impl_.player_; +} +AmmoUsed::AmmoUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.AmmoUsed) +} +AmmoUsed::AmmoUsed(const AmmoUsed& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + AmmoUsed* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.ammotype_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , decltype(_impl_.amountused_){} + , decltype(_impl_.oldammocount_){} + , decltype(_impl_.newammocount_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.ammotype_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.ammotype_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_ammotype().empty()) { + _this->_impl_.ammotype_.Set(from._internal_ammotype(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + ::memcpy(&_impl_.timestamp_, &from._impl_.timestamp_, + static_cast(reinterpret_cast(&_impl_.newammocount_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.newammocount_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.AmmoUsed) +} + +inline void AmmoUsed::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.ammotype_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , decltype(_impl_.amountused_){0u} + , decltype(_impl_.oldammocount_){0u} + , decltype(_impl_.newammocount_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.ammotype_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.ammotype_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +AmmoUsed::~AmmoUsed() { + // @@protoc_insertion_point(destructor:rtech.liveapi.AmmoUsed) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void AmmoUsed::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.ammotype_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void AmmoUsed::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void AmmoUsed::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.AmmoUsed) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.ammotype_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + ::memset(&_impl_.timestamp_, 0, static_cast( + reinterpret_cast(&_impl_.newammocount_) - + reinterpret_cast(&_impl_.timestamp_)) + sizeof(_impl_.newammocount_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* AmmoUsed::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.AmmoUsed.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string ammoType = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_ammotype(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.AmmoUsed.ammoType")); + } else + goto handle_unusual; + continue; + // uint32 amountUsed = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.amountused_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 oldAmmoCount = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _impl_.oldammocount_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // uint32 newAmmoCount = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _impl_.newammocount_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* AmmoUsed::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.AmmoUsed) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.AmmoUsed.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string ammoType = 4; + if (!this->_internal_ammotype().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_ammotype().data(), static_cast(this->_internal_ammotype().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.AmmoUsed.ammoType"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_ammotype(), target); + } + + // uint32 amountUsed = 5; + if (this->_internal_amountused() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(5, this->_internal_amountused(), target); + } + + // uint32 oldAmmoCount = 6; + if (this->_internal_oldammocount() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(6, this->_internal_oldammocount(), target); + } + + // uint32 newAmmoCount = 7; + if (this->_internal_newammocount() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt32ToArray(7, this->_internal_newammocount(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.AmmoUsed) + return target; +} + +size_t AmmoUsed::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.AmmoUsed) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string ammoType = 4; + if (!this->_internal_ammotype().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_ammotype()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + // uint32 amountUsed = 5; + if (this->_internal_amountused() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_amountused()); + } + + // uint32 oldAmmoCount = 6; + if (this->_internal_oldammocount() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_oldammocount()); + } + + // uint32 newAmmoCount = 7; + if (this->_internal_newammocount() != 0) { + total_size += ::_pbi::WireFormatLite::UInt32SizePlusOne(this->_internal_newammocount()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData AmmoUsed::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + AmmoUsed::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*AmmoUsed::GetClassData() const { return &_class_data_; } + + +void AmmoUsed::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.AmmoUsed) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_ammotype().empty()) { + _this->_internal_set_ammotype(from._internal_ammotype()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + if (from._internal_amountused() != 0) { + _this->_internal_set_amountused(from._internal_amountused()); + } + if (from._internal_oldammocount() != 0) { + _this->_internal_set_oldammocount(from._internal_oldammocount()); + } + if (from._internal_newammocount() != 0) { + _this->_internal_set_newammocount(from._internal_newammocount()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void AmmoUsed::CopyFrom(const AmmoUsed& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.AmmoUsed) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool AmmoUsed::IsInitialized() const { + return true; +} + +void AmmoUsed::InternalSwap(AmmoUsed* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.ammotype_, lhs_arena, + &other->_impl_.ammotype_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(AmmoUsed, _impl_.newammocount_) + + sizeof(AmmoUsed::_impl_.newammocount_) + - PROTOBUF_FIELD_OFFSET(AmmoUsed, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata AmmoUsed::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[43]); +} + +// =================================================================== + +class WeaponSwitched::_Internal { + public: + static const ::rtech::liveapi::Player& player(const WeaponSwitched* msg); +}; + +const ::rtech::liveapi::Player& +WeaponSwitched::_Internal::player(const WeaponSwitched* msg) { + return *msg->_impl_.player_; +} +WeaponSwitched::WeaponSwitched(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.WeaponSwitched) +} +WeaponSwitched::WeaponSwitched(const WeaponSwitched& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + WeaponSwitched* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.oldweapon_){} + , decltype(_impl_.newweapon_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.oldweapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.oldweapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_oldweapon().empty()) { + _this->_impl_.oldweapon_.Set(from._internal_oldweapon(), + _this->GetArenaForAllocation()); + } + _impl_.newweapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.newweapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_newweapon().empty()) { + _this->_impl_.newweapon_.Set(from._internal_newweapon(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_player()) { + _this->_impl_.player_ = new ::rtech::liveapi::Player(*from._impl_.player_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.WeaponSwitched) +} + +inline void WeaponSwitched::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.oldweapon_){} + , decltype(_impl_.newweapon_){} + , decltype(_impl_.player_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.oldweapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.oldweapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.newweapon_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.newweapon_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +WeaponSwitched::~WeaponSwitched() { + // @@protoc_insertion_point(destructor:rtech.liveapi.WeaponSwitched) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void WeaponSwitched::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.oldweapon_.Destroy(); + _impl_.newweapon_.Destroy(); + if (this != internal_default_instance()) delete _impl_.player_; +} + +void WeaponSwitched::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void WeaponSwitched::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.WeaponSwitched) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.oldweapon_.ClearToEmpty(); + _impl_.newweapon_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* WeaponSwitched::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.WeaponSwitched.category")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.Player player = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_player(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string oldWeapon = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_oldweapon(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.WeaponSwitched.oldWeapon")); + } else + goto handle_unusual; + continue; + // string newWeapon = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_newweapon(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.WeaponSwitched.newWeapon")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* WeaponSwitched::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.WeaponSwitched) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.WeaponSwitched.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::player(this), + _Internal::player(this).GetCachedSize(), target, stream); + } + + // string oldWeapon = 4; + if (!this->_internal_oldweapon().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_oldweapon().data(), static_cast(this->_internal_oldweapon().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.WeaponSwitched.oldWeapon"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_oldweapon(), target); + } + + // string newWeapon = 5; + if (!this->_internal_newweapon().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_newweapon().data(), static_cast(this->_internal_newweapon().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.WeaponSwitched.newWeapon"); + target = stream->WriteStringMaybeAliased( + 5, this->_internal_newweapon(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.WeaponSwitched) + return target; +} + +size_t WeaponSwitched::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.WeaponSwitched) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string oldWeapon = 4; + if (!this->_internal_oldweapon().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_oldweapon()); + } + + // string newWeapon = 5; + if (!this->_internal_newweapon().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_newweapon()); + } + + // .rtech.liveapi.Player player = 3; + if (this->_internal_has_player()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.player_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData WeaponSwitched::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + WeaponSwitched::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*WeaponSwitched::GetClassData() const { return &_class_data_; } + + +void WeaponSwitched::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.WeaponSwitched) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_oldweapon().empty()) { + _this->_internal_set_oldweapon(from._internal_oldweapon()); + } + if (!from._internal_newweapon().empty()) { + _this->_internal_set_newweapon(from._internal_newweapon()); + } + if (from._internal_has_player()) { + _this->_internal_mutable_player()->::rtech::liveapi::Player::MergeFrom( + from._internal_player()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void WeaponSwitched::CopyFrom(const WeaponSwitched& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.WeaponSwitched) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool WeaponSwitched::IsInitialized() const { + return true; +} + +void WeaponSwitched::InternalSwap(WeaponSwitched* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.oldweapon_, lhs_arena, + &other->_impl_.oldweapon_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.newweapon_, lhs_arena, + &other->_impl_.newweapon_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(WeaponSwitched, _impl_.timestamp_) + + sizeof(WeaponSwitched::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(WeaponSwitched, _impl_.player_)>( + reinterpret_cast(&_impl_.player_), + reinterpret_cast(&other->_impl_.player_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata WeaponSwitched::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[44]); +} + +// =================================================================== + +class CustomEvent::_Internal { + public: + static const ::PROTOBUF_NAMESPACE_ID::Struct& data(const CustomEvent* msg); +}; + +const ::PROTOBUF_NAMESPACE_ID::Struct& +CustomEvent::_Internal::data(const CustomEvent* msg) { + return *msg->_impl_.data_; +} +void CustomEvent::clear_data() { + if (GetArenaForAllocation() == nullptr && _impl_.data_ != nullptr) { + delete _impl_.data_; + } + _impl_.data_ = nullptr; +} +CustomEvent::CustomEvent(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomEvent) +} +CustomEvent::CustomEvent(const CustomEvent& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomEvent* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.name_){} + , decltype(_impl_.data_){nullptr} + , decltype(_impl_.timestamp_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_category().empty()) { + _this->_impl_.category_.Set(from._internal_category(), + _this->GetArenaForAllocation()); + } + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_name().empty()) { + _this->_impl_.name_.Set(from._internal_name(), + _this->GetArenaForAllocation()); + } + if (from._internal_has_data()) { + _this->_impl_.data_ = new ::PROTOBUF_NAMESPACE_ID::Struct(*from._impl_.data_); + } + _this->_impl_.timestamp_ = from._impl_.timestamp_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomEvent) +} + +inline void CustomEvent::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.category_){} + , decltype(_impl_.name_){} + , decltype(_impl_.data_){nullptr} + , decltype(_impl_.timestamp_){uint64_t{0u}} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.category_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.category_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.name_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomEvent::~CustomEvent() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomEvent) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomEvent::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.category_.Destroy(); + _impl_.name_.Destroy(); + if (this != internal_default_instance()) delete _impl_.data_; +} + +void CustomEvent::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomEvent::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomEvent) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.category_.ClearToEmpty(); + _impl_.name_.ClearToEmpty(); + if (GetArenaForAllocation() == nullptr && _impl_.data_ != nullptr) { + delete _impl_.data_; + } + _impl_.data_ = nullptr; + _impl_.timestamp_ = uint64_t{0u}; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomEvent::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // uint64 timestamp = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.timestamp_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string category = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_category(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomEvent.category")); + } else + goto handle_unusual; + continue; + // string name = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_name(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomEvent.name")); + } else + goto handle_unusual; + continue; + // .google.protobuf.Struct data = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_data(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomEvent::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomEvent) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteUInt64ToArray(1, this->_internal_timestamp(), target); + } + + // string category = 2; + if (!this->_internal_category().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_category().data(), static_cast(this->_internal_category().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomEvent.category"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_category(), target); + } + + // string name = 3; + if (!this->_internal_name().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_name().data(), static_cast(this->_internal_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomEvent.name"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_name(), target); + } + + // .google.protobuf.Struct data = 4; + if (this->_internal_has_data()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::data(this), + _Internal::data(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomEvent) + return target; +} + +size_t CustomEvent::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomEvent) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string category = 2; + if (!this->_internal_category().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_category()); + } + + // string name = 3; + if (!this->_internal_name().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_name()); + } + + // .google.protobuf.Struct data = 4; + if (this->_internal_has_data()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.data_); + } + + // uint64 timestamp = 1; + if (this->_internal_timestamp() != 0) { + total_size += ::_pbi::WireFormatLite::UInt64SizePlusOne(this->_internal_timestamp()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomEvent::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomEvent::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomEvent::GetClassData() const { return &_class_data_; } + + +void CustomEvent::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomEvent) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_category().empty()) { + _this->_internal_set_category(from._internal_category()); + } + if (!from._internal_name().empty()) { + _this->_internal_set_name(from._internal_name()); + } + if (from._internal_has_data()) { + _this->_internal_mutable_data()->::PROTOBUF_NAMESPACE_ID::Struct::MergeFrom( + from._internal_data()); + } + if (from._internal_timestamp() != 0) { + _this->_internal_set_timestamp(from._internal_timestamp()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomEvent::CopyFrom(const CustomEvent& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomEvent) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomEvent::IsInitialized() const { + return true; +} + +void CustomEvent::InternalSwap(CustomEvent* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.category_, lhs_arena, + &other->_impl_.category_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.name_, lhs_arena, + &other->_impl_.name_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(CustomEvent, _impl_.timestamp_) + + sizeof(CustomEvent::_impl_.timestamp_) + - PROTOBUF_FIELD_OFFSET(CustomEvent, _impl_.data_)>( + reinterpret_cast(&_impl_.data_), + reinterpret_cast(&other->_impl_.data_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomEvent::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[45]); +} + +// =================================================================== + +class ChangeCamera::_Internal { + public: +}; + +ChangeCamera::ChangeCamera(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.ChangeCamera) +} +ChangeCamera::ChangeCamera(const ChangeCamera& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + ChangeCamera* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.target_){} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + clear_has_target(); + switch (from.target_case()) { + case kPoi: { + _this->_internal_set_poi(from._internal_poi()); + break; + } + case kName: { + _this->_internal_set_name(from._internal_name()); + break; + } + case TARGET_NOT_SET: { + break; + } + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.ChangeCamera) +} + +inline void ChangeCamera::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.target_){} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{} + }; + clear_has_target(); +} + +ChangeCamera::~ChangeCamera() { + // @@protoc_insertion_point(destructor:rtech.liveapi.ChangeCamera) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void ChangeCamera::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + if (has_target()) { + clear_target(); + } +} + +void ChangeCamera::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void ChangeCamera::clear_target() { +// @@protoc_insertion_point(one_of_clear_start:rtech.liveapi.ChangeCamera) + switch (target_case()) { + case kPoi: { + // No need to clear + break; + } + case kName: { + _impl_.target_.name_.Destroy(); + break; + } + case TARGET_NOT_SET: { + break; + } + } + _impl_._oneof_case_[0] = TARGET_NOT_SET; +} + + +void ChangeCamera::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.ChangeCamera) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + clear_target(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ChangeCamera::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // .rtech.liveapi.PlayerOfInterest poi = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_poi(static_cast<::rtech::liveapi::PlayerOfInterest>(val)); + } else + goto handle_unusual; + continue; + // string name = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_name(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.ChangeCamera.name")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ChangeCamera::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.ChangeCamera) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // .rtech.liveapi.PlayerOfInterest poi = 1; + if (_internal_has_poi()) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteEnumToArray( + 1, this->_internal_poi(), target); + } + + // string name = 2; + if (_internal_has_name()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_name().data(), static_cast(this->_internal_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.ChangeCamera.name"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_name(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.ChangeCamera) + return target; +} + +size_t ChangeCamera::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.ChangeCamera) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + switch (target_case()) { + // .rtech.liveapi.PlayerOfInterest poi = 1; + case kPoi: { + total_size += 1 + + ::_pbi::WireFormatLite::EnumSize(this->_internal_poi()); + break; + } + // string name = 2; + case kName: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_name()); + break; + } + case TARGET_NOT_SET: { + break; + } + } + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ChangeCamera::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + ChangeCamera::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ChangeCamera::GetClassData() const { return &_class_data_; } + + +void ChangeCamera::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.ChangeCamera) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + switch (from.target_case()) { + case kPoi: { + _this->_internal_set_poi(from._internal_poi()); + break; + } + case kName: { + _this->_internal_set_name(from._internal_name()); + break; + } + case TARGET_NOT_SET: { + break; + } + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ChangeCamera::CopyFrom(const ChangeCamera& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.ChangeCamera) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ChangeCamera::IsInitialized() const { + return true; +} + +void ChangeCamera::InternalSwap(ChangeCamera* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_impl_.target_, other->_impl_.target_); + swap(_impl_._oneof_case_[0], other->_impl_._oneof_case_[0]); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ChangeCamera::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[46]); +} + +// =================================================================== + +class PauseToggle::_Internal { + public: +}; + +PauseToggle::PauseToggle(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.PauseToggle) +} +PauseToggle::PauseToggle(const PauseToggle& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + PauseToggle* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.pretimer_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _this->_impl_.pretimer_ = from._impl_.pretimer_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.PauseToggle) +} + +inline void PauseToggle::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.pretimer_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; +} + +PauseToggle::~PauseToggle() { + // @@protoc_insertion_point(destructor:rtech.liveapi.PauseToggle) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void PauseToggle::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); +} + +void PauseToggle::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void PauseToggle::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.PauseToggle) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.pretimer_ = 0; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PauseToggle::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // float preTimer = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 13)) { + _impl_.pretimer_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PauseToggle::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.PauseToggle) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // float preTimer = 1; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_pretimer = this->_internal_pretimer(); + uint32_t raw_pretimer; + memcpy(&raw_pretimer, &tmp_pretimer, sizeof(tmp_pretimer)); + if (raw_pretimer != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFloatToArray(1, this->_internal_pretimer(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.PauseToggle) + return target; +} + +size_t PauseToggle::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.PauseToggle) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // float preTimer = 1; + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_pretimer = this->_internal_pretimer(); + uint32_t raw_pretimer; + memcpy(&raw_pretimer, &tmp_pretimer, sizeof(tmp_pretimer)); + if (raw_pretimer != 0) { + total_size += 1 + 4; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PauseToggle::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + PauseToggle::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PauseToggle::GetClassData() const { return &_class_data_; } + + +void PauseToggle::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.PauseToggle) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + static_assert(sizeof(uint32_t) == sizeof(float), "Code assumes uint32_t and float are the same size."); + float tmp_pretimer = from._internal_pretimer(); + uint32_t raw_pretimer; + memcpy(&raw_pretimer, &tmp_pretimer, sizeof(tmp_pretimer)); + if (raw_pretimer != 0) { + _this->_internal_set_pretimer(from._internal_pretimer()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PauseToggle::CopyFrom(const PauseToggle& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.PauseToggle) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PauseToggle::IsInitialized() const { + return true; +} + +void PauseToggle::InternalSwap(PauseToggle* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_impl_.pretimer_, other->_impl_.pretimer_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PauseToggle::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[47]); +} + +// =================================================================== + +class CustomMatch_CreateLobby::_Internal { + public: +}; + +CustomMatch_CreateLobby::CustomMatch_CreateLobby(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase(arena, is_message_owned) { + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_CreateLobby) +} +CustomMatch_CreateLobby::CustomMatch_CreateLobby(const CustomMatch_CreateLobby& from) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase() { + CustomMatch_CreateLobby* const _this = this; (void)_this; + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_CreateLobby) +} + + + + + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_CreateLobby::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl, + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl, +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_CreateLobby::GetClassData() const { return &_class_data_; } + + + + + + + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_CreateLobby::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[48]); +} + +// =================================================================== + +class CustomMatch_JoinLobby::_Internal { + public: +}; + +CustomMatch_JoinLobby::CustomMatch_JoinLobby(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_JoinLobby) +} +CustomMatch_JoinLobby::CustomMatch_JoinLobby(const CustomMatch_JoinLobby& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_JoinLobby* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.roletoken_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.roletoken_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.roletoken_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_roletoken().empty()) { + _this->_impl_.roletoken_.Set(from._internal_roletoken(), + _this->GetArenaForAllocation()); + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_JoinLobby) +} + +inline void CustomMatch_JoinLobby::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.roletoken_){} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.roletoken_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.roletoken_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_JoinLobby::~CustomMatch_JoinLobby() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_JoinLobby) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_JoinLobby::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.roletoken_.Destroy(); +} + +void CustomMatch_JoinLobby::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_JoinLobby::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_JoinLobby) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.roletoken_.ClearToEmpty(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_JoinLobby::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string roleToken = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_roletoken(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_JoinLobby.roleToken")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_JoinLobby::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_JoinLobby) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string roleToken = 1; + if (!this->_internal_roletoken().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_roletoken().data(), static_cast(this->_internal_roletoken().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_JoinLobby.roleToken"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_roletoken(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_JoinLobby) + return target; +} + +size_t CustomMatch_JoinLobby::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_JoinLobby) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string roleToken = 1; + if (!this->_internal_roletoken().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_roletoken()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_JoinLobby::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_JoinLobby::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_JoinLobby::GetClassData() const { return &_class_data_; } + + +void CustomMatch_JoinLobby::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_JoinLobby) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_roletoken().empty()) { + _this->_internal_set_roletoken(from._internal_roletoken()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_JoinLobby::CopyFrom(const CustomMatch_JoinLobby& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_JoinLobby) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_JoinLobby::IsInitialized() const { + return true; +} + +void CustomMatch_JoinLobby::InternalSwap(CustomMatch_JoinLobby* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.roletoken_, lhs_arena, + &other->_impl_.roletoken_, rhs_arena + ); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_JoinLobby::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[49]); +} + +// =================================================================== + +class CustomMatch_LeaveLobby::_Internal { + public: +}; + +CustomMatch_LeaveLobby::CustomMatch_LeaveLobby(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase(arena, is_message_owned) { + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_LeaveLobby) +} +CustomMatch_LeaveLobby::CustomMatch_LeaveLobby(const CustomMatch_LeaveLobby& from) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase() { + CustomMatch_LeaveLobby* const _this = this; (void)_this; + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_LeaveLobby) +} + + + + + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_LeaveLobby::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl, + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl, +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_LeaveLobby::GetClassData() const { return &_class_data_; } + + + + + + + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_LeaveLobby::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[50]); +} + +// =================================================================== + +class CustomMatch_SetReady::_Internal { + public: +}; + +CustomMatch_SetReady::CustomMatch_SetReady(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_SetReady) +} +CustomMatch_SetReady::CustomMatch_SetReady(const CustomMatch_SetReady& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_SetReady* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.isready_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _this->_impl_.isready_ = from._impl_.isready_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_SetReady) +} + +inline void CustomMatch_SetReady::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.isready_){false} + , /*decltype(_impl_._cached_size_)*/{} + }; +} + +CustomMatch_SetReady::~CustomMatch_SetReady() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_SetReady) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_SetReady::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); +} + +void CustomMatch_SetReady::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_SetReady::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_SetReady) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.isready_ = false; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_SetReady::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // bool isReady = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.isready_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_SetReady::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_SetReady) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // bool isReady = 1; + if (this->_internal_isready() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(1, this->_internal_isready(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_SetReady) + return target; +} + +size_t CustomMatch_SetReady::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_SetReady) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // bool isReady = 1; + if (this->_internal_isready() != 0) { + total_size += 1 + 1; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_SetReady::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_SetReady::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_SetReady::GetClassData() const { return &_class_data_; } + + +void CustomMatch_SetReady::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_SetReady) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (from._internal_isready() != 0) { + _this->_internal_set_isready(from._internal_isready()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_SetReady::CopyFrom(const CustomMatch_SetReady& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_SetReady) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_SetReady::IsInitialized() const { + return true; +} + +void CustomMatch_SetReady::InternalSwap(CustomMatch_SetReady* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_impl_.isready_, other->_impl_.isready_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_SetReady::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[51]); +} + +// =================================================================== + +class CustomMatch_GetLobbyPlayers::_Internal { + public: +}; + +CustomMatch_GetLobbyPlayers::CustomMatch_GetLobbyPlayers(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase(arena, is_message_owned) { + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_GetLobbyPlayers) +} +CustomMatch_GetLobbyPlayers::CustomMatch_GetLobbyPlayers(const CustomMatch_GetLobbyPlayers& from) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase() { + CustomMatch_GetLobbyPlayers* const _this = this; (void)_this; + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_GetLobbyPlayers) +} + + + + + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_GetLobbyPlayers::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl, + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl, +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_GetLobbyPlayers::GetClassData() const { return &_class_data_; } + + + + + + + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_GetLobbyPlayers::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[52]); +} + +// =================================================================== + +class CustomMatch_SetMatchmaking::_Internal { + public: +}; + +CustomMatch_SetMatchmaking::CustomMatch_SetMatchmaking(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_SetMatchmaking) +} +CustomMatch_SetMatchmaking::CustomMatch_SetMatchmaking(const CustomMatch_SetMatchmaking& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_SetMatchmaking* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.enabled_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _this->_impl_.enabled_ = from._impl_.enabled_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_SetMatchmaking) +} + +inline void CustomMatch_SetMatchmaking::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.enabled_){false} + , /*decltype(_impl_._cached_size_)*/{} + }; +} + +CustomMatch_SetMatchmaking::~CustomMatch_SetMatchmaking() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_SetMatchmaking) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_SetMatchmaking::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); +} + +void CustomMatch_SetMatchmaking::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_SetMatchmaking::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_SetMatchmaking) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.enabled_ = false; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_SetMatchmaking::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // bool enabled = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.enabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_SetMatchmaking::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_SetMatchmaking) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // bool enabled = 1; + if (this->_internal_enabled() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(1, this->_internal_enabled(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_SetMatchmaking) + return target; +} + +size_t CustomMatch_SetMatchmaking::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_SetMatchmaking) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // bool enabled = 1; + if (this->_internal_enabled() != 0) { + total_size += 1 + 1; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_SetMatchmaking::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_SetMatchmaking::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_SetMatchmaking::GetClassData() const { return &_class_data_; } + + +void CustomMatch_SetMatchmaking::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_SetMatchmaking) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (from._internal_enabled() != 0) { + _this->_internal_set_enabled(from._internal_enabled()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_SetMatchmaking::CopyFrom(const CustomMatch_SetMatchmaking& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_SetMatchmaking) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_SetMatchmaking::IsInitialized() const { + return true; +} + +void CustomMatch_SetMatchmaking::InternalSwap(CustomMatch_SetMatchmaking* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_impl_.enabled_, other->_impl_.enabled_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_SetMatchmaking::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[53]); +} + +// =================================================================== + +class CustomMatch_SetTeam::_Internal { + public: +}; + +CustomMatch_SetTeam::CustomMatch_SetTeam(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_SetTeam) +} +CustomMatch_SetTeam::CustomMatch_SetTeam(const CustomMatch_SetTeam& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_SetTeam* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.targethardwarename_){} + , decltype(_impl_.targetnucleushash_){} + , decltype(_impl_.teamid_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.targethardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targethardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_targethardwarename().empty()) { + _this->_impl_.targethardwarename_.Set(from._internal_targethardwarename(), + _this->GetArenaForAllocation()); + } + _impl_.targetnucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targetnucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_targetnucleushash().empty()) { + _this->_impl_.targetnucleushash_.Set(from._internal_targetnucleushash(), + _this->GetArenaForAllocation()); + } + _this->_impl_.teamid_ = from._impl_.teamid_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_SetTeam) +} + +inline void CustomMatch_SetTeam::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.targethardwarename_){} + , decltype(_impl_.targetnucleushash_){} + , decltype(_impl_.teamid_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.targethardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targethardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targetnucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targetnucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_SetTeam::~CustomMatch_SetTeam() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_SetTeam) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_SetTeam::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.targethardwarename_.Destroy(); + _impl_.targetnucleushash_.Destroy(); +} + +void CustomMatch_SetTeam::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_SetTeam::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_SetTeam) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.targethardwarename_.ClearToEmpty(); + _impl_.targetnucleushash_.ClearToEmpty(); + _impl_.teamid_ = 0; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_SetTeam::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // int32 teamId = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.teamid_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string targetHardwareName = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_targethardwarename(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_SetTeam.targetHardwareName")); + } else + goto handle_unusual; + continue; + // string targetNucleusHash = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_targetnucleushash(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_SetTeam.targetNucleusHash")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_SetTeam::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_SetTeam) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // int32 teamId = 1; + if (this->_internal_teamid() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_teamid(), target); + } + + // string targetHardwareName = 2; + if (!this->_internal_targethardwarename().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_targethardwarename().data(), static_cast(this->_internal_targethardwarename().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_SetTeam.targetHardwareName"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_targethardwarename(), target); + } + + // string targetNucleusHash = 3; + if (!this->_internal_targetnucleushash().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_targetnucleushash().data(), static_cast(this->_internal_targetnucleushash().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_SetTeam.targetNucleusHash"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_targetnucleushash(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_SetTeam) + return target; +} + +size_t CustomMatch_SetTeam::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_SetTeam) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string targetHardwareName = 2; + if (!this->_internal_targethardwarename().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_targethardwarename()); + } + + // string targetNucleusHash = 3; + if (!this->_internal_targetnucleushash().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_targetnucleushash()); + } + + // int32 teamId = 1; + if (this->_internal_teamid() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_teamid()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_SetTeam::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_SetTeam::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_SetTeam::GetClassData() const { return &_class_data_; } + + +void CustomMatch_SetTeam::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_SetTeam) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_targethardwarename().empty()) { + _this->_internal_set_targethardwarename(from._internal_targethardwarename()); + } + if (!from._internal_targetnucleushash().empty()) { + _this->_internal_set_targetnucleushash(from._internal_targetnucleushash()); + } + if (from._internal_teamid() != 0) { + _this->_internal_set_teamid(from._internal_teamid()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_SetTeam::CopyFrom(const CustomMatch_SetTeam& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_SetTeam) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_SetTeam::IsInitialized() const { + return true; +} + +void CustomMatch_SetTeam::InternalSwap(CustomMatch_SetTeam* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.targethardwarename_, lhs_arena, + &other->_impl_.targethardwarename_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.targetnucleushash_, lhs_arena, + &other->_impl_.targetnucleushash_, rhs_arena + ); + swap(_impl_.teamid_, other->_impl_.teamid_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_SetTeam::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[54]); +} + +// =================================================================== + +class CustomMatch_KickPlayer::_Internal { + public: +}; + +CustomMatch_KickPlayer::CustomMatch_KickPlayer(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_KickPlayer) +} +CustomMatch_KickPlayer::CustomMatch_KickPlayer(const CustomMatch_KickPlayer& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_KickPlayer* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.targethardwarename_){} + , decltype(_impl_.targetnucleushash_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.targethardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targethardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_targethardwarename().empty()) { + _this->_impl_.targethardwarename_.Set(from._internal_targethardwarename(), + _this->GetArenaForAllocation()); + } + _impl_.targetnucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targetnucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_targetnucleushash().empty()) { + _this->_impl_.targetnucleushash_.Set(from._internal_targetnucleushash(), + _this->GetArenaForAllocation()); + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_KickPlayer) +} + +inline void CustomMatch_KickPlayer::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.targethardwarename_){} + , decltype(_impl_.targetnucleushash_){} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.targethardwarename_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targethardwarename_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targetnucleushash_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.targetnucleushash_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_KickPlayer::~CustomMatch_KickPlayer() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_KickPlayer) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_KickPlayer::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.targethardwarename_.Destroy(); + _impl_.targetnucleushash_.Destroy(); +} + +void CustomMatch_KickPlayer::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_KickPlayer::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_KickPlayer) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.targethardwarename_.ClearToEmpty(); + _impl_.targetnucleushash_.ClearToEmpty(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_KickPlayer::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string targetHardwareName = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_targethardwarename(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_KickPlayer.targetHardwareName")); + } else + goto handle_unusual; + continue; + // string targetNucleusHash = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_targetnucleushash(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_KickPlayer.targetNucleusHash")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_KickPlayer::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_KickPlayer) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string targetHardwareName = 1; + if (!this->_internal_targethardwarename().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_targethardwarename().data(), static_cast(this->_internal_targethardwarename().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_KickPlayer.targetHardwareName"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_targethardwarename(), target); + } + + // string targetNucleusHash = 2; + if (!this->_internal_targetnucleushash().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_targetnucleushash().data(), static_cast(this->_internal_targetnucleushash().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_KickPlayer.targetNucleusHash"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_targetnucleushash(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_KickPlayer) + return target; +} + +size_t CustomMatch_KickPlayer::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_KickPlayer) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string targetHardwareName = 1; + if (!this->_internal_targethardwarename().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_targethardwarename()); + } + + // string targetNucleusHash = 2; + if (!this->_internal_targetnucleushash().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_targetnucleushash()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_KickPlayer::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_KickPlayer::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_KickPlayer::GetClassData() const { return &_class_data_; } + + +void CustomMatch_KickPlayer::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_KickPlayer) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_targethardwarename().empty()) { + _this->_internal_set_targethardwarename(from._internal_targethardwarename()); + } + if (!from._internal_targetnucleushash().empty()) { + _this->_internal_set_targetnucleushash(from._internal_targetnucleushash()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_KickPlayer::CopyFrom(const CustomMatch_KickPlayer& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_KickPlayer) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_KickPlayer::IsInitialized() const { + return true; +} + +void CustomMatch_KickPlayer::InternalSwap(CustomMatch_KickPlayer* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.targethardwarename_, lhs_arena, + &other->_impl_.targethardwarename_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.targetnucleushash_, lhs_arena, + &other->_impl_.targetnucleushash_, rhs_arena + ); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_KickPlayer::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[55]); +} + +// =================================================================== + +class CustomMatch_SetSettings::_Internal { + public: +}; + +CustomMatch_SetSettings::CustomMatch_SetSettings(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_SetSettings) +} +CustomMatch_SetSettings::CustomMatch_SetSettings(const CustomMatch_SetSettings& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_SetSettings* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.playlistname_){} + , decltype(_impl_.adminchat_){} + , decltype(_impl_.teamrename_){} + , decltype(_impl_.selfassign_){} + , decltype(_impl_.aimassist_){} + , decltype(_impl_.anonmode_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.playlistname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_playlistname().empty()) { + _this->_impl_.playlistname_.Set(from._internal_playlistname(), + _this->GetArenaForAllocation()); + } + ::memcpy(&_impl_.adminchat_, &from._impl_.adminchat_, + static_cast(reinterpret_cast(&_impl_.anonmode_) - + reinterpret_cast(&_impl_.adminchat_)) + sizeof(_impl_.anonmode_)); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_SetSettings) +} + +inline void CustomMatch_SetSettings::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.playlistname_){} + , decltype(_impl_.adminchat_){false} + , decltype(_impl_.teamrename_){false} + , decltype(_impl_.selfassign_){false} + , decltype(_impl_.aimassist_){false} + , decltype(_impl_.anonmode_){false} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.playlistname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.playlistname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_SetSettings::~CustomMatch_SetSettings() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_SetSettings) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_SetSettings::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.playlistname_.Destroy(); +} + +void CustomMatch_SetSettings::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_SetSettings::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_SetSettings) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.playlistname_.ClearToEmpty(); + ::memset(&_impl_.adminchat_, 0, static_cast( + reinterpret_cast(&_impl_.anonmode_) - + reinterpret_cast(&_impl_.adminchat_)) + sizeof(_impl_.anonmode_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_SetSettings::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string playlistName = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_playlistname(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_SetSettings.playlistName")); + } else + goto handle_unusual; + continue; + // bool adminChat = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _impl_.adminchat_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool teamRename = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _impl_.teamrename_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool selfAssign = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 32)) { + _impl_.selfassign_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool aimAssist = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _impl_.aimassist_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // bool anonMode = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _impl_.anonmode_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_SetSettings::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_SetSettings) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string playlistName = 1; + if (!this->_internal_playlistname().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_playlistname().data(), static_cast(this->_internal_playlistname().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_SetSettings.playlistName"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_playlistname(), target); + } + + // bool adminChat = 2; + if (this->_internal_adminchat() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(2, this->_internal_adminchat(), target); + } + + // bool teamRename = 3; + if (this->_internal_teamrename() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(3, this->_internal_teamrename(), target); + } + + // bool selfAssign = 4; + if (this->_internal_selfassign() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(4, this->_internal_selfassign(), target); + } + + // bool aimAssist = 5; + if (this->_internal_aimassist() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(5, this->_internal_aimassist(), target); + } + + // bool anonMode = 6; + if (this->_internal_anonmode() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(6, this->_internal_anonmode(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_SetSettings) + return target; +} + +size_t CustomMatch_SetSettings::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_SetSettings) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string playlistName = 1; + if (!this->_internal_playlistname().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_playlistname()); + } + + // bool adminChat = 2; + if (this->_internal_adminchat() != 0) { + total_size += 1 + 1; + } + + // bool teamRename = 3; + if (this->_internal_teamrename() != 0) { + total_size += 1 + 1; + } + + // bool selfAssign = 4; + if (this->_internal_selfassign() != 0) { + total_size += 1 + 1; + } + + // bool aimAssist = 5; + if (this->_internal_aimassist() != 0) { + total_size += 1 + 1; + } + + // bool anonMode = 6; + if (this->_internal_anonmode() != 0) { + total_size += 1 + 1; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_SetSettings::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_SetSettings::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_SetSettings::GetClassData() const { return &_class_data_; } + + +void CustomMatch_SetSettings::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_SetSettings) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_playlistname().empty()) { + _this->_internal_set_playlistname(from._internal_playlistname()); + } + if (from._internal_adminchat() != 0) { + _this->_internal_set_adminchat(from._internal_adminchat()); + } + if (from._internal_teamrename() != 0) { + _this->_internal_set_teamrename(from._internal_teamrename()); + } + if (from._internal_selfassign() != 0) { + _this->_internal_set_selfassign(from._internal_selfassign()); + } + if (from._internal_aimassist() != 0) { + _this->_internal_set_aimassist(from._internal_aimassist()); + } + if (from._internal_anonmode() != 0) { + _this->_internal_set_anonmode(from._internal_anonmode()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_SetSettings::CopyFrom(const CustomMatch_SetSettings& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_SetSettings) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_SetSettings::IsInitialized() const { + return true; +} + +void CustomMatch_SetSettings::InternalSwap(CustomMatch_SetSettings* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.playlistname_, lhs_arena, + &other->_impl_.playlistname_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(CustomMatch_SetSettings, _impl_.anonmode_) + + sizeof(CustomMatch_SetSettings::_impl_.anonmode_) + - PROTOBUF_FIELD_OFFSET(CustomMatch_SetSettings, _impl_.adminchat_)>( + reinterpret_cast(&_impl_.adminchat_), + reinterpret_cast(&other->_impl_.adminchat_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_SetSettings::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[56]); +} + +// =================================================================== + +class CustomMatch_GetSettings::_Internal { + public: +}; + +CustomMatch_GetSettings::CustomMatch_GetSettings(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase(arena, is_message_owned) { + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_GetSettings) +} +CustomMatch_GetSettings::CustomMatch_GetSettings(const CustomMatch_GetSettings& from) + : ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase() { + CustomMatch_GetSettings* const _this = this; (void)_this; + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_GetSettings) +} + + + + + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_GetSettings::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl, + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl, +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_GetSettings::GetClassData() const { return &_class_data_; } + + + + + + + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_GetSettings::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[57]); +} + +// =================================================================== + +class CustomMatch_SetTeamName::_Internal { + public: +}; + +CustomMatch_SetTeamName::CustomMatch_SetTeamName(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_SetTeamName) +} +CustomMatch_SetTeamName::CustomMatch_SetTeamName(const CustomMatch_SetTeamName& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_SetTeamName* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.teamname_){} + , decltype(_impl_.teamid_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.teamname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.teamname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_teamname().empty()) { + _this->_impl_.teamname_.Set(from._internal_teamname(), + _this->GetArenaForAllocation()); + } + _this->_impl_.teamid_ = from._impl_.teamid_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_SetTeamName) +} + +inline void CustomMatch_SetTeamName::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.teamname_){} + , decltype(_impl_.teamid_){0} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.teamname_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.teamname_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_SetTeamName::~CustomMatch_SetTeamName() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_SetTeamName) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_SetTeamName::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.teamname_.Destroy(); +} + +void CustomMatch_SetTeamName::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_SetTeamName::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_SetTeamName) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.teamname_.ClearToEmpty(); + _impl_.teamid_ = 0; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_SetTeamName::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // int32 teamId = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.teamid_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string teamName = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_teamname(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_SetTeamName.teamName")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_SetTeamName::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_SetTeamName) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // int32 teamId = 1; + if (this->_internal_teamid() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_teamid(), target); + } + + // string teamName = 2; + if (!this->_internal_teamname().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_teamname().data(), static_cast(this->_internal_teamname().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_SetTeamName.teamName"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_teamname(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_SetTeamName) + return target; +} + +size_t CustomMatch_SetTeamName::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_SetTeamName) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string teamName = 2; + if (!this->_internal_teamname().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_teamname()); + } + + // int32 teamId = 1; + if (this->_internal_teamid() != 0) { + total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_teamid()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_SetTeamName::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_SetTeamName::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_SetTeamName::GetClassData() const { return &_class_data_; } + + +void CustomMatch_SetTeamName::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_SetTeamName) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_teamname().empty()) { + _this->_internal_set_teamname(from._internal_teamname()); + } + if (from._internal_teamid() != 0) { + _this->_internal_set_teamid(from._internal_teamid()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_SetTeamName::CopyFrom(const CustomMatch_SetTeamName& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_SetTeamName) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_SetTeamName::IsInitialized() const { + return true; +} + +void CustomMatch_SetTeamName::InternalSwap(CustomMatch_SetTeamName* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.teamname_, lhs_arena, + &other->_impl_.teamname_, rhs_arena + ); + swap(_impl_.teamid_, other->_impl_.teamid_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_SetTeamName::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[58]); +} + +// =================================================================== + +class CustomMatch_SendChat::_Internal { + public: +}; + +CustomMatch_SendChat::CustomMatch_SendChat(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.CustomMatch_SendChat) +} +CustomMatch_SendChat::CustomMatch_SendChat(const CustomMatch_SendChat& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + CustomMatch_SendChat* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.text_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.text_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.text_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_text().empty()) { + _this->_impl_.text_.Set(from._internal_text(), + _this->GetArenaForAllocation()); + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.CustomMatch_SendChat) +} + +inline void CustomMatch_SendChat::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.text_){} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.text_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.text_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +CustomMatch_SendChat::~CustomMatch_SendChat() { + // @@protoc_insertion_point(destructor:rtech.liveapi.CustomMatch_SendChat) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void CustomMatch_SendChat::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.text_.Destroy(); +} + +void CustomMatch_SendChat::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void CustomMatch_SendChat::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.CustomMatch_SendChat) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.text_.ClearToEmpty(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* CustomMatch_SendChat::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string text = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_text(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.CustomMatch_SendChat.text")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* CustomMatch_SendChat::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.CustomMatch_SendChat) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string text = 1; + if (!this->_internal_text().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_text().data(), static_cast(this->_internal_text().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.CustomMatch_SendChat.text"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_text(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.CustomMatch_SendChat) + return target; +} + +size_t CustomMatch_SendChat::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.CustomMatch_SendChat) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string text = 1; + if (!this->_internal_text().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_text()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData CustomMatch_SendChat::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + CustomMatch_SendChat::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*CustomMatch_SendChat::GetClassData() const { return &_class_data_; } + + +void CustomMatch_SendChat::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.CustomMatch_SendChat) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_text().empty()) { + _this->_internal_set_text(from._internal_text()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void CustomMatch_SendChat::CopyFrom(const CustomMatch_SendChat& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.CustomMatch_SendChat) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool CustomMatch_SendChat::IsInitialized() const { + return true; +} + +void CustomMatch_SendChat::InternalSwap(CustomMatch_SendChat* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.text_, lhs_arena, + &other->_impl_.text_, rhs_arena + ); +} + +::PROTOBUF_NAMESPACE_ID::Metadata CustomMatch_SendChat::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[59]); +} + +// =================================================================== + +class Request::_Internal { + public: + static const ::rtech::liveapi::ChangeCamera& changecam(const Request* msg); + static const ::rtech::liveapi::PauseToggle& pausetoggle(const Request* msg); + static const ::rtech::liveapi::CustomMatch_CreateLobby& custommatch_createlobby(const Request* msg); + static const ::rtech::liveapi::CustomMatch_JoinLobby& custommatch_joinlobby(const Request* msg); + static const ::rtech::liveapi::CustomMatch_LeaveLobby& custommatch_leavelobby(const Request* msg); + static const ::rtech::liveapi::CustomMatch_SetReady& custommatch_setready(const Request* msg); + static const ::rtech::liveapi::CustomMatch_SetMatchmaking& custommatch_setmatchmaking(const Request* msg); + static const ::rtech::liveapi::CustomMatch_SetTeam& custommatch_setteam(const Request* msg); + static const ::rtech::liveapi::CustomMatch_KickPlayer& custommatch_kickplayer(const Request* msg); + static const ::rtech::liveapi::CustomMatch_SetSettings& custommatch_setsettings(const Request* msg); + static const ::rtech::liveapi::CustomMatch_SendChat& custommatch_sendchat(const Request* msg); + static const ::rtech::liveapi::CustomMatch_GetLobbyPlayers& custommatch_getlobbyplayers(const Request* msg); + static const ::rtech::liveapi::CustomMatch_SetTeamName& custommatch_setteamname(const Request* msg); + static const ::rtech::liveapi::CustomMatch_GetSettings& custommatch_getsettings(const Request* msg); +}; + +const ::rtech::liveapi::ChangeCamera& +Request::_Internal::changecam(const Request* msg) { + return *msg->_impl_.actions_.changecam_; +} +const ::rtech::liveapi::PauseToggle& +Request::_Internal::pausetoggle(const Request* msg) { + return *msg->_impl_.actions_.pausetoggle_; +} +const ::rtech::liveapi::CustomMatch_CreateLobby& +Request::_Internal::custommatch_createlobby(const Request* msg) { + return *msg->_impl_.actions_.custommatch_createlobby_; +} +const ::rtech::liveapi::CustomMatch_JoinLobby& +Request::_Internal::custommatch_joinlobby(const Request* msg) { + return *msg->_impl_.actions_.custommatch_joinlobby_; +} +const ::rtech::liveapi::CustomMatch_LeaveLobby& +Request::_Internal::custommatch_leavelobby(const Request* msg) { + return *msg->_impl_.actions_.custommatch_leavelobby_; +} +const ::rtech::liveapi::CustomMatch_SetReady& +Request::_Internal::custommatch_setready(const Request* msg) { + return *msg->_impl_.actions_.custommatch_setready_; +} +const ::rtech::liveapi::CustomMatch_SetMatchmaking& +Request::_Internal::custommatch_setmatchmaking(const Request* msg) { + return *msg->_impl_.actions_.custommatch_setmatchmaking_; +} +const ::rtech::liveapi::CustomMatch_SetTeam& +Request::_Internal::custommatch_setteam(const Request* msg) { + return *msg->_impl_.actions_.custommatch_setteam_; +} +const ::rtech::liveapi::CustomMatch_KickPlayer& +Request::_Internal::custommatch_kickplayer(const Request* msg) { + return *msg->_impl_.actions_.custommatch_kickplayer_; +} +const ::rtech::liveapi::CustomMatch_SetSettings& +Request::_Internal::custommatch_setsettings(const Request* msg) { + return *msg->_impl_.actions_.custommatch_setsettings_; +} +const ::rtech::liveapi::CustomMatch_SendChat& +Request::_Internal::custommatch_sendchat(const Request* msg) { + return *msg->_impl_.actions_.custommatch_sendchat_; +} +const ::rtech::liveapi::CustomMatch_GetLobbyPlayers& +Request::_Internal::custommatch_getlobbyplayers(const Request* msg) { + return *msg->_impl_.actions_.custommatch_getlobbyplayers_; +} +const ::rtech::liveapi::CustomMatch_SetTeamName& +Request::_Internal::custommatch_setteamname(const Request* msg) { + return *msg->_impl_.actions_.custommatch_setteamname_; +} +const ::rtech::liveapi::CustomMatch_GetSettings& +Request::_Internal::custommatch_getsettings(const Request* msg) { + return *msg->_impl_.actions_.custommatch_getsettings_; +} +void Request::set_allocated_changecam(::rtech::liveapi::ChangeCamera* changecam) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (changecam) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(changecam); + if (message_arena != submessage_arena) { + changecam = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, changecam, submessage_arena); + } + set_has_changecam(); + _impl_.actions_.changecam_ = changecam; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.changeCam) +} +void Request::set_allocated_pausetoggle(::rtech::liveapi::PauseToggle* pausetoggle) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (pausetoggle) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(pausetoggle); + if (message_arena != submessage_arena) { + pausetoggle = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, pausetoggle, submessage_arena); + } + set_has_pausetoggle(); + _impl_.actions_.pausetoggle_ = pausetoggle; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.pauseToggle) +} +void Request::set_allocated_custommatch_createlobby(::rtech::liveapi::CustomMatch_CreateLobby* custommatch_createlobby) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_createlobby) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_createlobby); + if (message_arena != submessage_arena) { + custommatch_createlobby = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_createlobby, submessage_arena); + } + set_has_custommatch_createlobby(); + _impl_.actions_.custommatch_createlobby_ = custommatch_createlobby; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_CreateLobby) +} +void Request::set_allocated_custommatch_joinlobby(::rtech::liveapi::CustomMatch_JoinLobby* custommatch_joinlobby) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_joinlobby) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_joinlobby); + if (message_arena != submessage_arena) { + custommatch_joinlobby = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_joinlobby, submessage_arena); + } + set_has_custommatch_joinlobby(); + _impl_.actions_.custommatch_joinlobby_ = custommatch_joinlobby; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_JoinLobby) +} +void Request::set_allocated_custommatch_leavelobby(::rtech::liveapi::CustomMatch_LeaveLobby* custommatch_leavelobby) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_leavelobby) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_leavelobby); + if (message_arena != submessage_arena) { + custommatch_leavelobby = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_leavelobby, submessage_arena); + } + set_has_custommatch_leavelobby(); + _impl_.actions_.custommatch_leavelobby_ = custommatch_leavelobby; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_LeaveLobby) +} +void Request::set_allocated_custommatch_setready(::rtech::liveapi::CustomMatch_SetReady* custommatch_setready) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_setready) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_setready); + if (message_arena != submessage_arena) { + custommatch_setready = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_setready, submessage_arena); + } + set_has_custommatch_setready(); + _impl_.actions_.custommatch_setready_ = custommatch_setready; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_SetReady) +} +void Request::set_allocated_custommatch_setmatchmaking(::rtech::liveapi::CustomMatch_SetMatchmaking* custommatch_setmatchmaking) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_setmatchmaking) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_setmatchmaking); + if (message_arena != submessage_arena) { + custommatch_setmatchmaking = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_setmatchmaking, submessage_arena); + } + set_has_custommatch_setmatchmaking(); + _impl_.actions_.custommatch_setmatchmaking_ = custommatch_setmatchmaking; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_SetMatchmaking) +} +void Request::set_allocated_custommatch_setteam(::rtech::liveapi::CustomMatch_SetTeam* custommatch_setteam) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_setteam) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_setteam); + if (message_arena != submessage_arena) { + custommatch_setteam = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_setteam, submessage_arena); + } + set_has_custommatch_setteam(); + _impl_.actions_.custommatch_setteam_ = custommatch_setteam; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_SetTeam) +} +void Request::set_allocated_custommatch_kickplayer(::rtech::liveapi::CustomMatch_KickPlayer* custommatch_kickplayer) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_kickplayer) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_kickplayer); + if (message_arena != submessage_arena) { + custommatch_kickplayer = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_kickplayer, submessage_arena); + } + set_has_custommatch_kickplayer(); + _impl_.actions_.custommatch_kickplayer_ = custommatch_kickplayer; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_KickPlayer) +} +void Request::set_allocated_custommatch_setsettings(::rtech::liveapi::CustomMatch_SetSettings* custommatch_setsettings) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_setsettings) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_setsettings); + if (message_arena != submessage_arena) { + custommatch_setsettings = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_setsettings, submessage_arena); + } + set_has_custommatch_setsettings(); + _impl_.actions_.custommatch_setsettings_ = custommatch_setsettings; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_SetSettings) +} +void Request::set_allocated_custommatch_sendchat(::rtech::liveapi::CustomMatch_SendChat* custommatch_sendchat) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_sendchat) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_sendchat); + if (message_arena != submessage_arena) { + custommatch_sendchat = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_sendchat, submessage_arena); + } + set_has_custommatch_sendchat(); + _impl_.actions_.custommatch_sendchat_ = custommatch_sendchat; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_SendChat) +} +void Request::set_allocated_custommatch_getlobbyplayers(::rtech::liveapi::CustomMatch_GetLobbyPlayers* custommatch_getlobbyplayers) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_getlobbyplayers) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_getlobbyplayers); + if (message_arena != submessage_arena) { + custommatch_getlobbyplayers = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_getlobbyplayers, submessage_arena); + } + set_has_custommatch_getlobbyplayers(); + _impl_.actions_.custommatch_getlobbyplayers_ = custommatch_getlobbyplayers; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_GetLobbyPlayers) +} +void Request::set_allocated_custommatch_setteamname(::rtech::liveapi::CustomMatch_SetTeamName* custommatch_setteamname) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_setteamname) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_setteamname); + if (message_arena != submessage_arena) { + custommatch_setteamname = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_setteamname, submessage_arena); + } + set_has_custommatch_setteamname(); + _impl_.actions_.custommatch_setteamname_ = custommatch_setteamname; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_SetTeamName) +} +void Request::set_allocated_custommatch_getsettings(::rtech::liveapi::CustomMatch_GetSettings* custommatch_getsettings) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_actions(); + if (custommatch_getsettings) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(custommatch_getsettings); + if (message_arena != submessage_arena) { + custommatch_getsettings = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, custommatch_getsettings, submessage_arena); + } + set_has_custommatch_getsettings(); + _impl_.actions_.custommatch_getsettings_ = custommatch_getsettings; + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.customMatch_GetSettings) +} +Request::Request(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.Request) +} +Request::Request(const Request& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + Request* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.presharedkey_){} + , decltype(_impl_.withack_){} + , decltype(_impl_.actions_){} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.presharedkey_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.presharedkey_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_presharedkey().empty()) { + _this->_impl_.presharedkey_.Set(from._internal_presharedkey(), + _this->GetArenaForAllocation()); + } + _this->_impl_.withack_ = from._impl_.withack_; + clear_has_actions(); + switch (from.actions_case()) { + case kChangeCam: { + _this->_internal_mutable_changecam()->::rtech::liveapi::ChangeCamera::MergeFrom( + from._internal_changecam()); + break; + } + case kPauseToggle: { + _this->_internal_mutable_pausetoggle()->::rtech::liveapi::PauseToggle::MergeFrom( + from._internal_pausetoggle()); + break; + } + case kCustomMatchCreateLobby: { + _this->_internal_mutable_custommatch_createlobby()->::rtech::liveapi::CustomMatch_CreateLobby::MergeFrom( + from._internal_custommatch_createlobby()); + break; + } + case kCustomMatchJoinLobby: { + _this->_internal_mutable_custommatch_joinlobby()->::rtech::liveapi::CustomMatch_JoinLobby::MergeFrom( + from._internal_custommatch_joinlobby()); + break; + } + case kCustomMatchLeaveLobby: { + _this->_internal_mutable_custommatch_leavelobby()->::rtech::liveapi::CustomMatch_LeaveLobby::MergeFrom( + from._internal_custommatch_leavelobby()); + break; + } + case kCustomMatchSetReady: { + _this->_internal_mutable_custommatch_setready()->::rtech::liveapi::CustomMatch_SetReady::MergeFrom( + from._internal_custommatch_setready()); + break; + } + case kCustomMatchSetMatchmaking: { + _this->_internal_mutable_custommatch_setmatchmaking()->::rtech::liveapi::CustomMatch_SetMatchmaking::MergeFrom( + from._internal_custommatch_setmatchmaking()); + break; + } + case kCustomMatchSetTeam: { + _this->_internal_mutable_custommatch_setteam()->::rtech::liveapi::CustomMatch_SetTeam::MergeFrom( + from._internal_custommatch_setteam()); + break; + } + case kCustomMatchKickPlayer: { + _this->_internal_mutable_custommatch_kickplayer()->::rtech::liveapi::CustomMatch_KickPlayer::MergeFrom( + from._internal_custommatch_kickplayer()); + break; + } + case kCustomMatchSetSettings: { + _this->_internal_mutable_custommatch_setsettings()->::rtech::liveapi::CustomMatch_SetSettings::MergeFrom( + from._internal_custommatch_setsettings()); + break; + } + case kCustomMatchSendChat: { + _this->_internal_mutable_custommatch_sendchat()->::rtech::liveapi::CustomMatch_SendChat::MergeFrom( + from._internal_custommatch_sendchat()); + break; + } + case kCustomMatchGetLobbyPlayers: { + _this->_internal_mutable_custommatch_getlobbyplayers()->::rtech::liveapi::CustomMatch_GetLobbyPlayers::MergeFrom( + from._internal_custommatch_getlobbyplayers()); + break; + } + case kCustomMatchSetTeamName: { + _this->_internal_mutable_custommatch_setteamname()->::rtech::liveapi::CustomMatch_SetTeamName::MergeFrom( + from._internal_custommatch_setteamname()); + break; + } + case kCustomMatchGetSettings: { + _this->_internal_mutable_custommatch_getsettings()->::rtech::liveapi::CustomMatch_GetSettings::MergeFrom( + from._internal_custommatch_getsettings()); + break; + } + case ACTIONS_NOT_SET: { + break; + } + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.Request) +} + +inline void Request::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.presharedkey_){} + , decltype(_impl_.withack_){false} + , decltype(_impl_.actions_){} + , /*decltype(_impl_._cached_size_)*/{} + , /*decltype(_impl_._oneof_case_)*/{} + }; + _impl_.presharedkey_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.presharedkey_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + clear_has_actions(); +} + +Request::~Request() { + // @@protoc_insertion_point(destructor:rtech.liveapi.Request) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void Request::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.presharedkey_.Destroy(); + if (has_actions()) { + clear_actions(); + } +} + +void Request::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void Request::clear_actions() { +// @@protoc_insertion_point(one_of_clear_start:rtech.liveapi.Request) + switch (actions_case()) { + case kChangeCam: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.changecam_; + } + break; + } + case kPauseToggle: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.pausetoggle_; + } + break; + } + case kCustomMatchCreateLobby: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_createlobby_; + } + break; + } + case kCustomMatchJoinLobby: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_joinlobby_; + } + break; + } + case kCustomMatchLeaveLobby: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_leavelobby_; + } + break; + } + case kCustomMatchSetReady: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setready_; + } + break; + } + case kCustomMatchSetMatchmaking: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setmatchmaking_; + } + break; + } + case kCustomMatchSetTeam: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setteam_; + } + break; + } + case kCustomMatchKickPlayer: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_kickplayer_; + } + break; + } + case kCustomMatchSetSettings: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setsettings_; + } + break; + } + case kCustomMatchSendChat: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_sendchat_; + } + break; + } + case kCustomMatchGetLobbyPlayers: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_getlobbyplayers_; + } + break; + } + case kCustomMatchSetTeamName: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setteamname_; + } + break; + } + case kCustomMatchGetSettings: { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_getsettings_; + } + break; + } + case ACTIONS_NOT_SET: { + break; + } + } + _impl_._oneof_case_[0] = ACTIONS_NOT_SET; +} + + +void Request::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.Request) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.presharedkey_.ClearToEmpty(); + _impl_.withack_ = false; + clear_actions(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* Request::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // bool withAck = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.withack_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // string preSharedKey = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_presharedkey(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.Request.preSharedKey")); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.ChangeCamera changeCam = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_changecam(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.PauseToggle pauseToggle = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + ptr = ctx->ParseMessage(_internal_mutable_pausetoggle(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_CreateLobby customMatch_CreateLobby = 10; + case 10: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 82)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_createlobby(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_JoinLobby customMatch_JoinLobby = 11; + case 11: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 90)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_joinlobby(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_LeaveLobby customMatch_LeaveLobby = 12; + case 12: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 98)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_leavelobby(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_SetReady customMatch_SetReady = 13; + case 13: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 106)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_setready(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_SetMatchmaking customMatch_SetMatchmaking = 14; + case 14: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 114)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_setmatchmaking(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_SetTeam customMatch_SetTeam = 15; + case 15: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 122)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_setteam(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_KickPlayer customMatch_KickPlayer = 16; + case 16: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 130)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_kickplayer(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_SetSettings customMatch_SetSettings = 17; + case 17: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 138)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_setsettings(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_SendChat customMatch_SendChat = 18; + case 18: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 146)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_sendchat(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_GetLobbyPlayers customMatch_GetLobbyPlayers = 19; + case 19: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 154)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_getlobbyplayers(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_SetTeamName customMatch_SetTeamName = 20; + case 20: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 162)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_setteamname(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .rtech.liveapi.CustomMatch_GetSettings customMatch_GetSettings = 21; + case 21: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 170)) { + ptr = ctx->ParseMessage(_internal_mutable_custommatch_getsettings(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* Request::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.Request) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // bool withAck = 1; + if (this->_internal_withack() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(1, this->_internal_withack(), target); + } + + // string preSharedKey = 2; + if (!this->_internal_presharedkey().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_presharedkey().data(), static_cast(this->_internal_presharedkey().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.Request.preSharedKey"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_presharedkey(), target); + } + + // .rtech.liveapi.ChangeCamera changeCam = 4; + if (_internal_has_changecam()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(4, _Internal::changecam(this), + _Internal::changecam(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.PauseToggle pauseToggle = 5; + if (_internal_has_pausetoggle()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(5, _Internal::pausetoggle(this), + _Internal::pausetoggle(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_CreateLobby customMatch_CreateLobby = 10; + if (_internal_has_custommatch_createlobby()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(10, _Internal::custommatch_createlobby(this), + _Internal::custommatch_createlobby(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_JoinLobby customMatch_JoinLobby = 11; + if (_internal_has_custommatch_joinlobby()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(11, _Internal::custommatch_joinlobby(this), + _Internal::custommatch_joinlobby(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_LeaveLobby customMatch_LeaveLobby = 12; + if (_internal_has_custommatch_leavelobby()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(12, _Internal::custommatch_leavelobby(this), + _Internal::custommatch_leavelobby(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_SetReady customMatch_SetReady = 13; + if (_internal_has_custommatch_setready()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(13, _Internal::custommatch_setready(this), + _Internal::custommatch_setready(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_SetMatchmaking customMatch_SetMatchmaking = 14; + if (_internal_has_custommatch_setmatchmaking()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(14, _Internal::custommatch_setmatchmaking(this), + _Internal::custommatch_setmatchmaking(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_SetTeam customMatch_SetTeam = 15; + if (_internal_has_custommatch_setteam()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(15, _Internal::custommatch_setteam(this), + _Internal::custommatch_setteam(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_KickPlayer customMatch_KickPlayer = 16; + if (_internal_has_custommatch_kickplayer()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(16, _Internal::custommatch_kickplayer(this), + _Internal::custommatch_kickplayer(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_SetSettings customMatch_SetSettings = 17; + if (_internal_has_custommatch_setsettings()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(17, _Internal::custommatch_setsettings(this), + _Internal::custommatch_setsettings(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_SendChat customMatch_SendChat = 18; + if (_internal_has_custommatch_sendchat()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(18, _Internal::custommatch_sendchat(this), + _Internal::custommatch_sendchat(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_GetLobbyPlayers customMatch_GetLobbyPlayers = 19; + if (_internal_has_custommatch_getlobbyplayers()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(19, _Internal::custommatch_getlobbyplayers(this), + _Internal::custommatch_getlobbyplayers(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_SetTeamName customMatch_SetTeamName = 20; + if (_internal_has_custommatch_setteamname()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(20, _Internal::custommatch_setteamname(this), + _Internal::custommatch_setteamname(this).GetCachedSize(), target, stream); + } + + // .rtech.liveapi.CustomMatch_GetSettings customMatch_GetSettings = 21; + if (_internal_has_custommatch_getsettings()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(21, _Internal::custommatch_getsettings(this), + _Internal::custommatch_getsettings(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.Request) + return target; +} + +size_t Request::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.Request) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string preSharedKey = 2; + if (!this->_internal_presharedkey().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_presharedkey()); + } + + // bool withAck = 1; + if (this->_internal_withack() != 0) { + total_size += 1 + 1; + } + + switch (actions_case()) { + // .rtech.liveapi.ChangeCamera changeCam = 4; + case kChangeCam: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.changecam_); + break; + } + // .rtech.liveapi.PauseToggle pauseToggle = 5; + case kPauseToggle: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.pausetoggle_); + break; + } + // .rtech.liveapi.CustomMatch_CreateLobby customMatch_CreateLobby = 10; + case kCustomMatchCreateLobby: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_createlobby_); + break; + } + // .rtech.liveapi.CustomMatch_JoinLobby customMatch_JoinLobby = 11; + case kCustomMatchJoinLobby: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_joinlobby_); + break; + } + // .rtech.liveapi.CustomMatch_LeaveLobby customMatch_LeaveLobby = 12; + case kCustomMatchLeaveLobby: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_leavelobby_); + break; + } + // .rtech.liveapi.CustomMatch_SetReady customMatch_SetReady = 13; + case kCustomMatchSetReady: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_setready_); + break; + } + // .rtech.liveapi.CustomMatch_SetMatchmaking customMatch_SetMatchmaking = 14; + case kCustomMatchSetMatchmaking: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_setmatchmaking_); + break; + } + // .rtech.liveapi.CustomMatch_SetTeam customMatch_SetTeam = 15; + case kCustomMatchSetTeam: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_setteam_); + break; + } + // .rtech.liveapi.CustomMatch_KickPlayer customMatch_KickPlayer = 16; + case kCustomMatchKickPlayer: { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_kickplayer_); + break; + } + // .rtech.liveapi.CustomMatch_SetSettings customMatch_SetSettings = 17; + case kCustomMatchSetSettings: { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_setsettings_); + break; + } + // .rtech.liveapi.CustomMatch_SendChat customMatch_SendChat = 18; + case kCustomMatchSendChat: { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_sendchat_); + break; + } + // .rtech.liveapi.CustomMatch_GetLobbyPlayers customMatch_GetLobbyPlayers = 19; + case kCustomMatchGetLobbyPlayers: { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_getlobbyplayers_); + break; + } + // .rtech.liveapi.CustomMatch_SetTeamName customMatch_SetTeamName = 20; + case kCustomMatchSetTeamName: { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_setteamname_); + break; + } + // .rtech.liveapi.CustomMatch_GetSettings customMatch_GetSettings = 21; + case kCustomMatchGetSettings: { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.actions_.custommatch_getsettings_); + break; + } + case ACTIONS_NOT_SET: { + break; + } + } + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Request::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + Request::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Request::GetClassData() const { return &_class_data_; } + + +void Request::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.Request) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_presharedkey().empty()) { + _this->_internal_set_presharedkey(from._internal_presharedkey()); + } + if (from._internal_withack() != 0) { + _this->_internal_set_withack(from._internal_withack()); + } + switch (from.actions_case()) { + case kChangeCam: { + _this->_internal_mutable_changecam()->::rtech::liveapi::ChangeCamera::MergeFrom( + from._internal_changecam()); + break; + } + case kPauseToggle: { + _this->_internal_mutable_pausetoggle()->::rtech::liveapi::PauseToggle::MergeFrom( + from._internal_pausetoggle()); + break; + } + case kCustomMatchCreateLobby: { + _this->_internal_mutable_custommatch_createlobby()->::rtech::liveapi::CustomMatch_CreateLobby::MergeFrom( + from._internal_custommatch_createlobby()); + break; + } + case kCustomMatchJoinLobby: { + _this->_internal_mutable_custommatch_joinlobby()->::rtech::liveapi::CustomMatch_JoinLobby::MergeFrom( + from._internal_custommatch_joinlobby()); + break; + } + case kCustomMatchLeaveLobby: { + _this->_internal_mutable_custommatch_leavelobby()->::rtech::liveapi::CustomMatch_LeaveLobby::MergeFrom( + from._internal_custommatch_leavelobby()); + break; + } + case kCustomMatchSetReady: { + _this->_internal_mutable_custommatch_setready()->::rtech::liveapi::CustomMatch_SetReady::MergeFrom( + from._internal_custommatch_setready()); + break; + } + case kCustomMatchSetMatchmaking: { + _this->_internal_mutable_custommatch_setmatchmaking()->::rtech::liveapi::CustomMatch_SetMatchmaking::MergeFrom( + from._internal_custommatch_setmatchmaking()); + break; + } + case kCustomMatchSetTeam: { + _this->_internal_mutable_custommatch_setteam()->::rtech::liveapi::CustomMatch_SetTeam::MergeFrom( + from._internal_custommatch_setteam()); + break; + } + case kCustomMatchKickPlayer: { + _this->_internal_mutable_custommatch_kickplayer()->::rtech::liveapi::CustomMatch_KickPlayer::MergeFrom( + from._internal_custommatch_kickplayer()); + break; + } + case kCustomMatchSetSettings: { + _this->_internal_mutable_custommatch_setsettings()->::rtech::liveapi::CustomMatch_SetSettings::MergeFrom( + from._internal_custommatch_setsettings()); + break; + } + case kCustomMatchSendChat: { + _this->_internal_mutable_custommatch_sendchat()->::rtech::liveapi::CustomMatch_SendChat::MergeFrom( + from._internal_custommatch_sendchat()); + break; + } + case kCustomMatchGetLobbyPlayers: { + _this->_internal_mutable_custommatch_getlobbyplayers()->::rtech::liveapi::CustomMatch_GetLobbyPlayers::MergeFrom( + from._internal_custommatch_getlobbyplayers()); + break; + } + case kCustomMatchSetTeamName: { + _this->_internal_mutable_custommatch_setteamname()->::rtech::liveapi::CustomMatch_SetTeamName::MergeFrom( + from._internal_custommatch_setteamname()); + break; + } + case kCustomMatchGetSettings: { + _this->_internal_mutable_custommatch_getsettings()->::rtech::liveapi::CustomMatch_GetSettings::MergeFrom( + from._internal_custommatch_getsettings()); + break; + } + case ACTIONS_NOT_SET: { + break; + } + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void Request::CopyFrom(const Request& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.Request) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Request::IsInitialized() const { + return true; +} + +void Request::InternalSwap(Request* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.presharedkey_, lhs_arena, + &other->_impl_.presharedkey_, rhs_arena + ); + swap(_impl_.withack_, other->_impl_.withack_); + swap(_impl_.actions_, other->_impl_.actions_); + swap(_impl_._oneof_case_[0], other->_impl_._oneof_case_[0]); +} + +::PROTOBUF_NAMESPACE_ID::Metadata Request::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[60]); +} + +// =================================================================== + +class RequestStatus::_Internal { + public: +}; + +RequestStatus::RequestStatus(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.RequestStatus) +} +RequestStatus::RequestStatus(const RequestStatus& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + RequestStatus* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.status_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + _impl_.status_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.status_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (!from._internal_status().empty()) { + _this->_impl_.status_.Set(from._internal_status(), + _this->GetArenaForAllocation()); + } + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.RequestStatus) +} + +inline void RequestStatus::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.status_){} + , /*decltype(_impl_._cached_size_)*/{} + }; + _impl_.status_.InitDefault(); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + _impl_.status_.Set("", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +RequestStatus::~RequestStatus() { + // @@protoc_insertion_point(destructor:rtech.liveapi.RequestStatus) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void RequestStatus::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + _impl_.status_.Destroy(); +} + +void RequestStatus::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void RequestStatus::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.RequestStatus) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + _impl_.status_.ClearToEmpty(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* RequestStatus::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // string status = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_status(); + ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + CHK_(::_pbi::VerifyUTF8(str, "rtech.liveapi.RequestStatus.status")); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* RequestStatus::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.RequestStatus) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // string status = 1; + if (!this->_internal_status().empty()) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_status().data(), static_cast(this->_internal_status().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "rtech.liveapi.RequestStatus.status"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_status(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.RequestStatus) + return target; +} + +size_t RequestStatus::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.RequestStatus) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string status = 1; + if (!this->_internal_status().empty()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_status()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData RequestStatus::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + RequestStatus::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*RequestStatus::GetClassData() const { return &_class_data_; } + + +void RequestStatus::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.RequestStatus) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (!from._internal_status().empty()) { + _this->_internal_set_status(from._internal_status()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void RequestStatus::CopyFrom(const RequestStatus& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.RequestStatus) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool RequestStatus::IsInitialized() const { + return true; +} + +void RequestStatus::InternalSwap(RequestStatus* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &_impl_.status_, lhs_arena, + &other->_impl_.status_, rhs_arena + ); +} + +::PROTOBUF_NAMESPACE_ID::Metadata RequestStatus::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[61]); +} + +// =================================================================== + +class Response::_Internal { + public: + static const ::PROTOBUF_NAMESPACE_ID::Any& result(const Response* msg); +}; + +const ::PROTOBUF_NAMESPACE_ID::Any& +Response::_Internal::result(const Response* msg) { + return *msg->_impl_.result_; +} +void Response::clear_result() { + if (GetArenaForAllocation() == nullptr && _impl_.result_ != nullptr) { + delete _impl_.result_; + } + _impl_.result_ = nullptr; +} +Response::Response(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.Response) +} +Response::Response(const Response& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + Response* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.result_){nullptr} + , decltype(_impl_.success_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + if (from._internal_has_result()) { + _this->_impl_.result_ = new ::PROTOBUF_NAMESPACE_ID::Any(*from._impl_.result_); + } + _this->_impl_.success_ = from._impl_.success_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.Response) +} + +inline void Response::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.result_){nullptr} + , decltype(_impl_.success_){false} + , /*decltype(_impl_._cached_size_)*/{} + }; +} + +Response::~Response() { + // @@protoc_insertion_point(destructor:rtech.liveapi.Response) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void Response::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + if (this != internal_default_instance()) delete _impl_.result_; +} + +void Response::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void Response::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.Response) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + if (GetArenaForAllocation() == nullptr && _impl_.result_ != nullptr) { + delete _impl_.result_; + } + _impl_.result_ = nullptr; + _impl_.success_ = false; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* Response::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // bool success = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _impl_.success_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .google.protobuf.Any result = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + ptr = ctx->ParseMessage(_internal_mutable_result(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* Response::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.Response) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // bool success = 1; + if (this->_internal_success() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray(1, this->_internal_success(), target); + } + + // .google.protobuf.Any result = 2; + if (this->_internal_has_result()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(2, _Internal::result(this), + _Internal::result(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.Response) + return target; +} + +size_t Response::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.Response) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // .google.protobuf.Any result = 2; + if (this->_internal_has_result()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.result_); + } + + // bool success = 1; + if (this->_internal_success() != 0) { + total_size += 1 + 1; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Response::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + Response::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Response::GetClassData() const { return &_class_data_; } + + +void Response::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.Response) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (from._internal_has_result()) { + _this->_internal_mutable_result()->::PROTOBUF_NAMESPACE_ID::Any::MergeFrom( + from._internal_result()); + } + if (from._internal_success() != 0) { + _this->_internal_set_success(from._internal_success()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void Response::CopyFrom(const Response& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.Response) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool Response::IsInitialized() const { + return true; +} + +void Response::InternalSwap(Response* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(Response, _impl_.success_) + + sizeof(Response::_impl_.success_) + - PROTOBUF_FIELD_OFFSET(Response, _impl_.result_)>( + reinterpret_cast(&_impl_.result_), + reinterpret_cast(&other->_impl_.result_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata Response::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[62]); +} + +// =================================================================== + +class LiveAPIEvent::_Internal { + public: + static const ::PROTOBUF_NAMESPACE_ID::Any& gamemessage(const LiveAPIEvent* msg); +}; + +const ::PROTOBUF_NAMESPACE_ID::Any& +LiveAPIEvent::_Internal::gamemessage(const LiveAPIEvent* msg) { + return *msg->_impl_.gamemessage_; +} +void LiveAPIEvent::clear_gamemessage() { + if (GetArenaForAllocation() == nullptr && _impl_.gamemessage_ != nullptr) { + delete _impl_.gamemessage_; + } + _impl_.gamemessage_ = nullptr; +} +LiveAPIEvent::LiveAPIEvent(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(arena, is_message_owned); + // @@protoc_insertion_point(arena_constructor:rtech.liveapi.LiveAPIEvent) +} +LiveAPIEvent::LiveAPIEvent(const LiveAPIEvent& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + LiveAPIEvent* const _this = this; (void)_this; + new (&_impl_) Impl_{ + decltype(_impl_.gamemessage_){nullptr} + , decltype(_impl_.event_size_){} + , /*decltype(_impl_._cached_size_)*/{}}; + + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + if (from._internal_has_gamemessage()) { + _this->_impl_.gamemessage_ = new ::PROTOBUF_NAMESPACE_ID::Any(*from._impl_.gamemessage_); + } + _this->_impl_.event_size_ = from._impl_.event_size_; + // @@protoc_insertion_point(copy_constructor:rtech.liveapi.LiveAPIEvent) +} + +inline void LiveAPIEvent::SharedCtor( + ::_pb::Arena* arena, bool is_message_owned) { + (void)arena; + (void)is_message_owned; + new (&_impl_) Impl_{ + decltype(_impl_.gamemessage_){nullptr} + , decltype(_impl_.event_size_){0u} + , /*decltype(_impl_._cached_size_)*/{} + }; +} + +LiveAPIEvent::~LiveAPIEvent() { + // @@protoc_insertion_point(destructor:rtech.liveapi.LiveAPIEvent) + if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) { + (void)arena; + return; + } + SharedDtor(); +} + +inline void LiveAPIEvent::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + if (this != internal_default_instance()) delete _impl_.gamemessage_; +} + +void LiveAPIEvent::SetCachedSize(int size) const { + _impl_._cached_size_.Set(size); +} + +void LiveAPIEvent::Clear() { +// @@protoc_insertion_point(message_clear_start:rtech.liveapi.LiveAPIEvent) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + if (GetArenaForAllocation() == nullptr && _impl_.gamemessage_ != nullptr) { + delete _impl_.gamemessage_; + } + _impl_.gamemessage_ = nullptr; + _impl_.event_size_ = 0u; + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* LiveAPIEvent::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::_pbi::ReadTag(ptr, &tag); + switch (tag >> 3) { + // fixed32 event_size = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 13)) { + _impl_.event_size_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(uint32_t); + } else + goto handle_unusual; + continue; + // .google.protobuf.Any gameMessage = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_gamemessage(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* LiveAPIEvent::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:rtech.liveapi.LiveAPIEvent) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // fixed32 event_size = 1; + if (this->_internal_event_size() != 0) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteFixed32ToArray(1, this->_internal_event_size(), target); + } + + // .google.protobuf.Any gameMessage = 3; + if (this->_internal_has_gamemessage()) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(3, _Internal::gamemessage(this), + _Internal::gamemessage(this).GetCachedSize(), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:rtech.liveapi.LiveAPIEvent) + return target; +} + +size_t LiveAPIEvent::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:rtech.liveapi.LiveAPIEvent) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // .google.protobuf.Any gameMessage = 3; + if (this->_internal_has_gamemessage()) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *_impl_.gamemessage_); + } + + // fixed32 event_size = 1; + if (this->_internal_event_size() != 0) { + total_size += 1 + 4; + } + + return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData LiveAPIEvent::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck, + LiveAPIEvent::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*LiveAPIEvent::GetClassData() const { return &_class_data_; } + + +void LiveAPIEvent::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) { + auto* const _this = static_cast(&to_msg); + auto& from = static_cast(from_msg); + // @@protoc_insertion_point(class_specific_merge_from_start:rtech.liveapi.LiveAPIEvent) + GOOGLE_DCHECK_NE(&from, _this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (from._internal_has_gamemessage()) { + _this->_internal_mutable_gamemessage()->::PROTOBUF_NAMESPACE_ID::Any::MergeFrom( + from._internal_gamemessage()); + } + if (from._internal_event_size() != 0) { + _this->_internal_set_event_size(from._internal_event_size()); + } + _this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void LiveAPIEvent::CopyFrom(const LiveAPIEvent& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:rtech.liveapi.LiveAPIEvent) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool LiveAPIEvent::IsInitialized() const { + return true; +} + +void LiveAPIEvent::InternalSwap(LiveAPIEvent* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(LiveAPIEvent, _impl_.event_size_) + + sizeof(LiveAPIEvent::_impl_.event_size_) + - PROTOBUF_FIELD_OFFSET(LiveAPIEvent, _impl_.gamemessage_)>( + reinterpret_cast(&_impl_.gamemessage_), + reinterpret_cast(&other->_impl_.gamemessage_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata LiveAPIEvent::GetMetadata() const { + return ::_pbi::AssignDescriptors( + &descriptor_table_events_2eproto_getter, &descriptor_table_events_2eproto_once, + file_level_metadata_events_2eproto[63]); +} + +// @@protoc_insertion_point(namespace_scope) +} // namespace liveapi +} // namespace rtech +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::rtech::liveapi::Vector3* +Arena::CreateMaybeMessage< ::rtech::liveapi::Vector3 >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::Vector3 >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::Player* +Arena::CreateMaybeMessage< ::rtech::liveapi::Player >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::Player >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_LobbyPlayer* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_LobbyPlayer >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_LobbyPlayer >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::Datacenter* +Arena::CreateMaybeMessage< ::rtech::liveapi::Datacenter >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::Datacenter >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::Version* +Arena::CreateMaybeMessage< ::rtech::liveapi::Version >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::Version >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::InventoryItem* +Arena::CreateMaybeMessage< ::rtech::liveapi::InventoryItem >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::InventoryItem >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::LoadoutConfiguration* +Arena::CreateMaybeMessage< ::rtech::liveapi::LoadoutConfiguration >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::LoadoutConfiguration >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::Init* +Arena::CreateMaybeMessage< ::rtech::liveapi::Init >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::Init >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_LobbyPlayers* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_LobbyPlayers >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_LobbyPlayers >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::ObserverSwitched* +Arena::CreateMaybeMessage< ::rtech::liveapi::ObserverSwitched >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::ObserverSwitched >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::ObserverAnnotation* +Arena::CreateMaybeMessage< ::rtech::liveapi::ObserverAnnotation >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::ObserverAnnotation >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::MatchSetup* +Arena::CreateMaybeMessage< ::rtech::liveapi::MatchSetup >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::MatchSetup >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::GameStateChanged* +Arena::CreateMaybeMessage< ::rtech::liveapi::GameStateChanged >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::GameStateChanged >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CharacterSelected* +Arena::CreateMaybeMessage< ::rtech::liveapi::CharacterSelected >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CharacterSelected >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::MatchStateEnd* +Arena::CreateMaybeMessage< ::rtech::liveapi::MatchStateEnd >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::MatchStateEnd >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::RingStartClosing* +Arena::CreateMaybeMessage< ::rtech::liveapi::RingStartClosing >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::RingStartClosing >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::RingFinishedClosing* +Arena::CreateMaybeMessage< ::rtech::liveapi::RingFinishedClosing >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::RingFinishedClosing >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerConnected* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerConnected >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerConnected >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerDisconnected* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerDisconnected >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerDisconnected >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerStatChanged* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerStatChanged >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerStatChanged >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerUpgradeTierChanged* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerUpgradeTierChanged >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerUpgradeTierChanged >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerDamaged* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerDamaged >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerDamaged >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerKilled* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerKilled >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerKilled >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerDowned* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerDowned >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerDowned >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerAssist* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerAssist >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerAssist >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::SquadEliminated* +Arena::CreateMaybeMessage< ::rtech::liveapi::SquadEliminated >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::SquadEliminated >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::GibraltarShieldAbsorbed* +Arena::CreateMaybeMessage< ::rtech::liveapi::GibraltarShieldAbsorbed >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::GibraltarShieldAbsorbed >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::RevenantForgedShadowDamaged* +Arena::CreateMaybeMessage< ::rtech::liveapi::RevenantForgedShadowDamaged >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::RevenantForgedShadowDamaged >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerRespawnTeam* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerRespawnTeam >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerRespawnTeam >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerRevive* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerRevive >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerRevive >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::ArenasItemSelected* +Arena::CreateMaybeMessage< ::rtech::liveapi::ArenasItemSelected >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::ArenasItemSelected >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::ArenasItemDeselected* +Arena::CreateMaybeMessage< ::rtech::liveapi::ArenasItemDeselected >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::ArenasItemDeselected >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::InventoryPickUp* +Arena::CreateMaybeMessage< ::rtech::liveapi::InventoryPickUp >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::InventoryPickUp >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::InventoryDrop* +Arena::CreateMaybeMessage< ::rtech::liveapi::InventoryDrop >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::InventoryDrop >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::InventoryUse* +Arena::CreateMaybeMessage< ::rtech::liveapi::InventoryUse >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::InventoryUse >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::BannerCollected* +Arena::CreateMaybeMessage< ::rtech::liveapi::BannerCollected >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::BannerCollected >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PlayerAbilityUsed* +Arena::CreateMaybeMessage< ::rtech::liveapi::PlayerAbilityUsed >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PlayerAbilityUsed >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::LegendUpgradeSelected* +Arena::CreateMaybeMessage< ::rtech::liveapi::LegendUpgradeSelected >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::LegendUpgradeSelected >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::ZiplineUsed* +Arena::CreateMaybeMessage< ::rtech::liveapi::ZiplineUsed >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::ZiplineUsed >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::GrenadeThrown* +Arena::CreateMaybeMessage< ::rtech::liveapi::GrenadeThrown >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::GrenadeThrown >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::BlackMarketAction* +Arena::CreateMaybeMessage< ::rtech::liveapi::BlackMarketAction >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::BlackMarketAction >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::WraithPortal* +Arena::CreateMaybeMessage< ::rtech::liveapi::WraithPortal >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::WraithPortal >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::WarpGateUsed* +Arena::CreateMaybeMessage< ::rtech::liveapi::WarpGateUsed >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::WarpGateUsed >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::AmmoUsed* +Arena::CreateMaybeMessage< ::rtech::liveapi::AmmoUsed >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::AmmoUsed >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::WeaponSwitched* +Arena::CreateMaybeMessage< ::rtech::liveapi::WeaponSwitched >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::WeaponSwitched >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomEvent* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomEvent >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomEvent >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::ChangeCamera* +Arena::CreateMaybeMessage< ::rtech::liveapi::ChangeCamera >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::ChangeCamera >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::PauseToggle* +Arena::CreateMaybeMessage< ::rtech::liveapi::PauseToggle >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::PauseToggle >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_CreateLobby* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_CreateLobby >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_CreateLobby >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_JoinLobby* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_JoinLobby >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_JoinLobby >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_LeaveLobby* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_LeaveLobby >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_LeaveLobby >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_SetReady* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetReady >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_SetReady >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_GetLobbyPlayers* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_GetLobbyPlayers >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_GetLobbyPlayers >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_SetMatchmaking* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetMatchmaking >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_SetMatchmaking >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_SetTeam* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetTeam >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_SetTeam >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_KickPlayer* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_KickPlayer >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_KickPlayer >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_SetSettings* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetSettings >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_SetSettings >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_GetSettings* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_GetSettings >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_GetSettings >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_SetTeamName* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetTeamName >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_SetTeamName >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::CustomMatch_SendChat* +Arena::CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SendChat >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::CustomMatch_SendChat >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::Request* +Arena::CreateMaybeMessage< ::rtech::liveapi::Request >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::Request >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::RequestStatus* +Arena::CreateMaybeMessage< ::rtech::liveapi::RequestStatus >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::RequestStatus >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::Response* +Arena::CreateMaybeMessage< ::rtech::liveapi::Response >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::Response >(arena); +} +template<> PROTOBUF_NOINLINE ::rtech::liveapi::LiveAPIEvent* +Arena::CreateMaybeMessage< ::rtech::liveapi::LiveAPIEvent >(Arena* arena) { + return Arena::CreateMessageInternal< ::rtech::liveapi::LiveAPIEvent >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/r5dev/protoc/events.pb.h b/r5dev/protoc/events.pb.h new file mode 100644 index 00000000..e59b7327 --- /dev/null +++ b/r5dev/protoc/events.pb.h @@ -0,0 +1,25973 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: events.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_events_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_events_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3021000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3021012 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_events_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_events_2eproto { + static const uint32_t offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_events_2eproto; +namespace rtech { +namespace liveapi { +class AmmoUsed; +struct AmmoUsedDefaultTypeInternal; +extern AmmoUsedDefaultTypeInternal _AmmoUsed_default_instance_; +class ArenasItemDeselected; +struct ArenasItemDeselectedDefaultTypeInternal; +extern ArenasItemDeselectedDefaultTypeInternal _ArenasItemDeselected_default_instance_; +class ArenasItemSelected; +struct ArenasItemSelectedDefaultTypeInternal; +extern ArenasItemSelectedDefaultTypeInternal _ArenasItemSelected_default_instance_; +class BannerCollected; +struct BannerCollectedDefaultTypeInternal; +extern BannerCollectedDefaultTypeInternal _BannerCollected_default_instance_; +class BlackMarketAction; +struct BlackMarketActionDefaultTypeInternal; +extern BlackMarketActionDefaultTypeInternal _BlackMarketAction_default_instance_; +class ChangeCamera; +struct ChangeCameraDefaultTypeInternal; +extern ChangeCameraDefaultTypeInternal _ChangeCamera_default_instance_; +class CharacterSelected; +struct CharacterSelectedDefaultTypeInternal; +extern CharacterSelectedDefaultTypeInternal _CharacterSelected_default_instance_; +class CustomEvent; +struct CustomEventDefaultTypeInternal; +extern CustomEventDefaultTypeInternal _CustomEvent_default_instance_; +class CustomMatch_CreateLobby; +struct CustomMatch_CreateLobbyDefaultTypeInternal; +extern CustomMatch_CreateLobbyDefaultTypeInternal _CustomMatch_CreateLobby_default_instance_; +class CustomMatch_GetLobbyPlayers; +struct CustomMatch_GetLobbyPlayersDefaultTypeInternal; +extern CustomMatch_GetLobbyPlayersDefaultTypeInternal _CustomMatch_GetLobbyPlayers_default_instance_; +class CustomMatch_GetSettings; +struct CustomMatch_GetSettingsDefaultTypeInternal; +extern CustomMatch_GetSettingsDefaultTypeInternal _CustomMatch_GetSettings_default_instance_; +class CustomMatch_JoinLobby; +struct CustomMatch_JoinLobbyDefaultTypeInternal; +extern CustomMatch_JoinLobbyDefaultTypeInternal _CustomMatch_JoinLobby_default_instance_; +class CustomMatch_KickPlayer; +struct CustomMatch_KickPlayerDefaultTypeInternal; +extern CustomMatch_KickPlayerDefaultTypeInternal _CustomMatch_KickPlayer_default_instance_; +class CustomMatch_LeaveLobby; +struct CustomMatch_LeaveLobbyDefaultTypeInternal; +extern CustomMatch_LeaveLobbyDefaultTypeInternal _CustomMatch_LeaveLobby_default_instance_; +class CustomMatch_LobbyPlayer; +struct CustomMatch_LobbyPlayerDefaultTypeInternal; +extern CustomMatch_LobbyPlayerDefaultTypeInternal _CustomMatch_LobbyPlayer_default_instance_; +class CustomMatch_LobbyPlayers; +struct CustomMatch_LobbyPlayersDefaultTypeInternal; +extern CustomMatch_LobbyPlayersDefaultTypeInternal _CustomMatch_LobbyPlayers_default_instance_; +class CustomMatch_SendChat; +struct CustomMatch_SendChatDefaultTypeInternal; +extern CustomMatch_SendChatDefaultTypeInternal _CustomMatch_SendChat_default_instance_; +class CustomMatch_SetMatchmaking; +struct CustomMatch_SetMatchmakingDefaultTypeInternal; +extern CustomMatch_SetMatchmakingDefaultTypeInternal _CustomMatch_SetMatchmaking_default_instance_; +class CustomMatch_SetReady; +struct CustomMatch_SetReadyDefaultTypeInternal; +extern CustomMatch_SetReadyDefaultTypeInternal _CustomMatch_SetReady_default_instance_; +class CustomMatch_SetSettings; +struct CustomMatch_SetSettingsDefaultTypeInternal; +extern CustomMatch_SetSettingsDefaultTypeInternal _CustomMatch_SetSettings_default_instance_; +class CustomMatch_SetTeam; +struct CustomMatch_SetTeamDefaultTypeInternal; +extern CustomMatch_SetTeamDefaultTypeInternal _CustomMatch_SetTeam_default_instance_; +class CustomMatch_SetTeamName; +struct CustomMatch_SetTeamNameDefaultTypeInternal; +extern CustomMatch_SetTeamNameDefaultTypeInternal _CustomMatch_SetTeamName_default_instance_; +class Datacenter; +struct DatacenterDefaultTypeInternal; +extern DatacenterDefaultTypeInternal _Datacenter_default_instance_; +class GameStateChanged; +struct GameStateChangedDefaultTypeInternal; +extern GameStateChangedDefaultTypeInternal _GameStateChanged_default_instance_; +class GibraltarShieldAbsorbed; +struct GibraltarShieldAbsorbedDefaultTypeInternal; +extern GibraltarShieldAbsorbedDefaultTypeInternal _GibraltarShieldAbsorbed_default_instance_; +class GrenadeThrown; +struct GrenadeThrownDefaultTypeInternal; +extern GrenadeThrownDefaultTypeInternal _GrenadeThrown_default_instance_; +class Init; +struct InitDefaultTypeInternal; +extern InitDefaultTypeInternal _Init_default_instance_; +class InventoryDrop; +struct InventoryDropDefaultTypeInternal; +extern InventoryDropDefaultTypeInternal _InventoryDrop_default_instance_; +class InventoryItem; +struct InventoryItemDefaultTypeInternal; +extern InventoryItemDefaultTypeInternal _InventoryItem_default_instance_; +class InventoryPickUp; +struct InventoryPickUpDefaultTypeInternal; +extern InventoryPickUpDefaultTypeInternal _InventoryPickUp_default_instance_; +class InventoryUse; +struct InventoryUseDefaultTypeInternal; +extern InventoryUseDefaultTypeInternal _InventoryUse_default_instance_; +class LegendUpgradeSelected; +struct LegendUpgradeSelectedDefaultTypeInternal; +extern LegendUpgradeSelectedDefaultTypeInternal _LegendUpgradeSelected_default_instance_; +class LiveAPIEvent; +struct LiveAPIEventDefaultTypeInternal; +extern LiveAPIEventDefaultTypeInternal _LiveAPIEvent_default_instance_; +class LoadoutConfiguration; +struct LoadoutConfigurationDefaultTypeInternal; +extern LoadoutConfigurationDefaultTypeInternal _LoadoutConfiguration_default_instance_; +class MatchSetup; +struct MatchSetupDefaultTypeInternal; +extern MatchSetupDefaultTypeInternal _MatchSetup_default_instance_; +class MatchStateEnd; +struct MatchStateEndDefaultTypeInternal; +extern MatchStateEndDefaultTypeInternal _MatchStateEnd_default_instance_; +class ObserverAnnotation; +struct ObserverAnnotationDefaultTypeInternal; +extern ObserverAnnotationDefaultTypeInternal _ObserverAnnotation_default_instance_; +class ObserverSwitched; +struct ObserverSwitchedDefaultTypeInternal; +extern ObserverSwitchedDefaultTypeInternal _ObserverSwitched_default_instance_; +class PauseToggle; +struct PauseToggleDefaultTypeInternal; +extern PauseToggleDefaultTypeInternal _PauseToggle_default_instance_; +class Player; +struct PlayerDefaultTypeInternal; +extern PlayerDefaultTypeInternal _Player_default_instance_; +class PlayerAbilityUsed; +struct PlayerAbilityUsedDefaultTypeInternal; +extern PlayerAbilityUsedDefaultTypeInternal _PlayerAbilityUsed_default_instance_; +class PlayerAssist; +struct PlayerAssistDefaultTypeInternal; +extern PlayerAssistDefaultTypeInternal _PlayerAssist_default_instance_; +class PlayerConnected; +struct PlayerConnectedDefaultTypeInternal; +extern PlayerConnectedDefaultTypeInternal _PlayerConnected_default_instance_; +class PlayerDamaged; +struct PlayerDamagedDefaultTypeInternal; +extern PlayerDamagedDefaultTypeInternal _PlayerDamaged_default_instance_; +class PlayerDisconnected; +struct PlayerDisconnectedDefaultTypeInternal; +extern PlayerDisconnectedDefaultTypeInternal _PlayerDisconnected_default_instance_; +class PlayerDowned; +struct PlayerDownedDefaultTypeInternal; +extern PlayerDownedDefaultTypeInternal _PlayerDowned_default_instance_; +class PlayerKilled; +struct PlayerKilledDefaultTypeInternal; +extern PlayerKilledDefaultTypeInternal _PlayerKilled_default_instance_; +class PlayerRespawnTeam; +struct PlayerRespawnTeamDefaultTypeInternal; +extern PlayerRespawnTeamDefaultTypeInternal _PlayerRespawnTeam_default_instance_; +class PlayerRevive; +struct PlayerReviveDefaultTypeInternal; +extern PlayerReviveDefaultTypeInternal _PlayerRevive_default_instance_; +class PlayerStatChanged; +struct PlayerStatChangedDefaultTypeInternal; +extern PlayerStatChangedDefaultTypeInternal _PlayerStatChanged_default_instance_; +class PlayerUpgradeTierChanged; +struct PlayerUpgradeTierChangedDefaultTypeInternal; +extern PlayerUpgradeTierChangedDefaultTypeInternal _PlayerUpgradeTierChanged_default_instance_; +class Request; +struct RequestDefaultTypeInternal; +extern RequestDefaultTypeInternal _Request_default_instance_; +class RequestStatus; +struct RequestStatusDefaultTypeInternal; +extern RequestStatusDefaultTypeInternal _RequestStatus_default_instance_; +class Response; +struct ResponseDefaultTypeInternal; +extern ResponseDefaultTypeInternal _Response_default_instance_; +class RevenantForgedShadowDamaged; +struct RevenantForgedShadowDamagedDefaultTypeInternal; +extern RevenantForgedShadowDamagedDefaultTypeInternal _RevenantForgedShadowDamaged_default_instance_; +class RingFinishedClosing; +struct RingFinishedClosingDefaultTypeInternal; +extern RingFinishedClosingDefaultTypeInternal _RingFinishedClosing_default_instance_; +class RingStartClosing; +struct RingStartClosingDefaultTypeInternal; +extern RingStartClosingDefaultTypeInternal _RingStartClosing_default_instance_; +class SquadEliminated; +struct SquadEliminatedDefaultTypeInternal; +extern SquadEliminatedDefaultTypeInternal _SquadEliminated_default_instance_; +class Vector3; +struct Vector3DefaultTypeInternal; +extern Vector3DefaultTypeInternal _Vector3_default_instance_; +class Version; +struct VersionDefaultTypeInternal; +extern VersionDefaultTypeInternal _Version_default_instance_; +class WarpGateUsed; +struct WarpGateUsedDefaultTypeInternal; +extern WarpGateUsedDefaultTypeInternal _WarpGateUsed_default_instance_; +class WeaponSwitched; +struct WeaponSwitchedDefaultTypeInternal; +extern WeaponSwitchedDefaultTypeInternal _WeaponSwitched_default_instance_; +class WraithPortal; +struct WraithPortalDefaultTypeInternal; +extern WraithPortalDefaultTypeInternal _WraithPortal_default_instance_; +class ZiplineUsed; +struct ZiplineUsedDefaultTypeInternal; +extern ZiplineUsedDefaultTypeInternal _ZiplineUsed_default_instance_; +} // namespace liveapi +} // namespace rtech +PROTOBUF_NAMESPACE_OPEN +template<> ::rtech::liveapi::AmmoUsed* Arena::CreateMaybeMessage<::rtech::liveapi::AmmoUsed>(Arena*); +template<> ::rtech::liveapi::ArenasItemDeselected* Arena::CreateMaybeMessage<::rtech::liveapi::ArenasItemDeselected>(Arena*); +template<> ::rtech::liveapi::ArenasItemSelected* Arena::CreateMaybeMessage<::rtech::liveapi::ArenasItemSelected>(Arena*); +template<> ::rtech::liveapi::BannerCollected* Arena::CreateMaybeMessage<::rtech::liveapi::BannerCollected>(Arena*); +template<> ::rtech::liveapi::BlackMarketAction* Arena::CreateMaybeMessage<::rtech::liveapi::BlackMarketAction>(Arena*); +template<> ::rtech::liveapi::ChangeCamera* Arena::CreateMaybeMessage<::rtech::liveapi::ChangeCamera>(Arena*); +template<> ::rtech::liveapi::CharacterSelected* Arena::CreateMaybeMessage<::rtech::liveapi::CharacterSelected>(Arena*); +template<> ::rtech::liveapi::CustomEvent* Arena::CreateMaybeMessage<::rtech::liveapi::CustomEvent>(Arena*); +template<> ::rtech::liveapi::CustomMatch_CreateLobby* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_CreateLobby>(Arena*); +template<> ::rtech::liveapi::CustomMatch_GetLobbyPlayers* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_GetLobbyPlayers>(Arena*); +template<> ::rtech::liveapi::CustomMatch_GetSettings* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_GetSettings>(Arena*); +template<> ::rtech::liveapi::CustomMatch_JoinLobby* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_JoinLobby>(Arena*); +template<> ::rtech::liveapi::CustomMatch_KickPlayer* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_KickPlayer>(Arena*); +template<> ::rtech::liveapi::CustomMatch_LeaveLobby* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_LeaveLobby>(Arena*); +template<> ::rtech::liveapi::CustomMatch_LobbyPlayer* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_LobbyPlayer>(Arena*); +template<> ::rtech::liveapi::CustomMatch_LobbyPlayers* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_LobbyPlayers>(Arena*); +template<> ::rtech::liveapi::CustomMatch_SendChat* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_SendChat>(Arena*); +template<> ::rtech::liveapi::CustomMatch_SetMatchmaking* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_SetMatchmaking>(Arena*); +template<> ::rtech::liveapi::CustomMatch_SetReady* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_SetReady>(Arena*); +template<> ::rtech::liveapi::CustomMatch_SetSettings* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_SetSettings>(Arena*); +template<> ::rtech::liveapi::CustomMatch_SetTeam* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_SetTeam>(Arena*); +template<> ::rtech::liveapi::CustomMatch_SetTeamName* Arena::CreateMaybeMessage<::rtech::liveapi::CustomMatch_SetTeamName>(Arena*); +template<> ::rtech::liveapi::Datacenter* Arena::CreateMaybeMessage<::rtech::liveapi::Datacenter>(Arena*); +template<> ::rtech::liveapi::GameStateChanged* Arena::CreateMaybeMessage<::rtech::liveapi::GameStateChanged>(Arena*); +template<> ::rtech::liveapi::GibraltarShieldAbsorbed* Arena::CreateMaybeMessage<::rtech::liveapi::GibraltarShieldAbsorbed>(Arena*); +template<> ::rtech::liveapi::GrenadeThrown* Arena::CreateMaybeMessage<::rtech::liveapi::GrenadeThrown>(Arena*); +template<> ::rtech::liveapi::Init* Arena::CreateMaybeMessage<::rtech::liveapi::Init>(Arena*); +template<> ::rtech::liveapi::InventoryDrop* Arena::CreateMaybeMessage<::rtech::liveapi::InventoryDrop>(Arena*); +template<> ::rtech::liveapi::InventoryItem* Arena::CreateMaybeMessage<::rtech::liveapi::InventoryItem>(Arena*); +template<> ::rtech::liveapi::InventoryPickUp* Arena::CreateMaybeMessage<::rtech::liveapi::InventoryPickUp>(Arena*); +template<> ::rtech::liveapi::InventoryUse* Arena::CreateMaybeMessage<::rtech::liveapi::InventoryUse>(Arena*); +template<> ::rtech::liveapi::LegendUpgradeSelected* Arena::CreateMaybeMessage<::rtech::liveapi::LegendUpgradeSelected>(Arena*); +template<> ::rtech::liveapi::LiveAPIEvent* Arena::CreateMaybeMessage<::rtech::liveapi::LiveAPIEvent>(Arena*); +template<> ::rtech::liveapi::LoadoutConfiguration* Arena::CreateMaybeMessage<::rtech::liveapi::LoadoutConfiguration>(Arena*); +template<> ::rtech::liveapi::MatchSetup* Arena::CreateMaybeMessage<::rtech::liveapi::MatchSetup>(Arena*); +template<> ::rtech::liveapi::MatchStateEnd* Arena::CreateMaybeMessage<::rtech::liveapi::MatchStateEnd>(Arena*); +template<> ::rtech::liveapi::ObserverAnnotation* Arena::CreateMaybeMessage<::rtech::liveapi::ObserverAnnotation>(Arena*); +template<> ::rtech::liveapi::ObserverSwitched* Arena::CreateMaybeMessage<::rtech::liveapi::ObserverSwitched>(Arena*); +template<> ::rtech::liveapi::PauseToggle* Arena::CreateMaybeMessage<::rtech::liveapi::PauseToggle>(Arena*); +template<> ::rtech::liveapi::Player* Arena::CreateMaybeMessage<::rtech::liveapi::Player>(Arena*); +template<> ::rtech::liveapi::PlayerAbilityUsed* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerAbilityUsed>(Arena*); +template<> ::rtech::liveapi::PlayerAssist* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerAssist>(Arena*); +template<> ::rtech::liveapi::PlayerConnected* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerConnected>(Arena*); +template<> ::rtech::liveapi::PlayerDamaged* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerDamaged>(Arena*); +template<> ::rtech::liveapi::PlayerDisconnected* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerDisconnected>(Arena*); +template<> ::rtech::liveapi::PlayerDowned* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerDowned>(Arena*); +template<> ::rtech::liveapi::PlayerKilled* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerKilled>(Arena*); +template<> ::rtech::liveapi::PlayerRespawnTeam* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerRespawnTeam>(Arena*); +template<> ::rtech::liveapi::PlayerRevive* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerRevive>(Arena*); +template<> ::rtech::liveapi::PlayerStatChanged* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerStatChanged>(Arena*); +template<> ::rtech::liveapi::PlayerUpgradeTierChanged* Arena::CreateMaybeMessage<::rtech::liveapi::PlayerUpgradeTierChanged>(Arena*); +template<> ::rtech::liveapi::Request* Arena::CreateMaybeMessage<::rtech::liveapi::Request>(Arena*); +template<> ::rtech::liveapi::RequestStatus* Arena::CreateMaybeMessage<::rtech::liveapi::RequestStatus>(Arena*); +template<> ::rtech::liveapi::Response* Arena::CreateMaybeMessage<::rtech::liveapi::Response>(Arena*); +template<> ::rtech::liveapi::RevenantForgedShadowDamaged* Arena::CreateMaybeMessage<::rtech::liveapi::RevenantForgedShadowDamaged>(Arena*); +template<> ::rtech::liveapi::RingFinishedClosing* Arena::CreateMaybeMessage<::rtech::liveapi::RingFinishedClosing>(Arena*); +template<> ::rtech::liveapi::RingStartClosing* Arena::CreateMaybeMessage<::rtech::liveapi::RingStartClosing>(Arena*); +template<> ::rtech::liveapi::SquadEliminated* Arena::CreateMaybeMessage<::rtech::liveapi::SquadEliminated>(Arena*); +template<> ::rtech::liveapi::Vector3* Arena::CreateMaybeMessage<::rtech::liveapi::Vector3>(Arena*); +template<> ::rtech::liveapi::Version* Arena::CreateMaybeMessage<::rtech::liveapi::Version>(Arena*); +template<> ::rtech::liveapi::WarpGateUsed* Arena::CreateMaybeMessage<::rtech::liveapi::WarpGateUsed>(Arena*); +template<> ::rtech::liveapi::WeaponSwitched* Arena::CreateMaybeMessage<::rtech::liveapi::WeaponSwitched>(Arena*); +template<> ::rtech::liveapi::WraithPortal* Arena::CreateMaybeMessage<::rtech::liveapi::WraithPortal>(Arena*); +template<> ::rtech::liveapi::ZiplineUsed* Arena::CreateMaybeMessage<::rtech::liveapi::ZiplineUsed>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace rtech { +namespace liveapi { + +enum PlayerOfInterest : int { + UNSPECIFIED = 0, + NEXT = 1, + PREVIOUS = 2, + KILL_LEADER = 3, + CLOSEST_ENEMY = 4, + CLOSEST_PLAYER = 5, + LATEST_ATTACKER = 6, + PlayerOfInterest_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits::min(), + PlayerOfInterest_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits::max() +}; +bool PlayerOfInterest_IsValid(int value); +constexpr PlayerOfInterest PlayerOfInterest_MIN = UNSPECIFIED; +constexpr PlayerOfInterest PlayerOfInterest_MAX = LATEST_ATTACKER; +constexpr int PlayerOfInterest_ARRAYSIZE = PlayerOfInterest_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PlayerOfInterest_descriptor(); +template +inline const std::string& PlayerOfInterest_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function PlayerOfInterest_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + PlayerOfInterest_descriptor(), enum_t_value); +} +inline bool PlayerOfInterest_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PlayerOfInterest* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + PlayerOfInterest_descriptor(), name, value); +} +// =================================================================== + +class Vector3 final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.Vector3) */ { + public: + inline Vector3() : Vector3(nullptr) {} + ~Vector3() override; + explicit PROTOBUF_CONSTEXPR Vector3(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + Vector3(const Vector3& from); + Vector3(Vector3&& from) noexcept + : Vector3() { + *this = ::std::move(from); + } + + inline Vector3& operator=(const Vector3& from) { + CopyFrom(from); + return *this; + } + inline Vector3& operator=(Vector3&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const Vector3& default_instance() { + return *internal_default_instance(); + } + static inline const Vector3* internal_default_instance() { + return reinterpret_cast( + &_Vector3_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(Vector3& a, Vector3& b) { + a.Swap(&b); + } + inline void Swap(Vector3* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(Vector3* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + Vector3* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const Vector3& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const Vector3& from) { + Vector3::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(Vector3* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.Vector3"; + } + protected: + explicit Vector3(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kXFieldNumber = 1, + kYFieldNumber = 2, + kZFieldNumber = 3, + }; + // float x = 1; + void clear_x(); + float x() const; + void set_x(float value); + private: + float _internal_x() const; + void _internal_set_x(float value); + public: + + // float y = 2; + void clear_y(); + float y() const; + void set_y(float value); + private: + float _internal_y() const; + void _internal_set_y(float value); + public: + + // float z = 3; + void clear_z(); + float z() const; + void set_z(float value); + private: + float _internal_z() const; + void _internal_set_z(float value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.Vector3) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + float x_; + float y_; + float z_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class Player final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.Player) */ { + public: + inline Player() : Player(nullptr) {} + ~Player() override; + explicit PROTOBUF_CONSTEXPR Player(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + Player(const Player& from); + Player(Player&& from) noexcept + : Player() { + *this = ::std::move(from); + } + + inline Player& operator=(const Player& from) { + CopyFrom(from); + return *this; + } + inline Player& operator=(Player&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const Player& default_instance() { + return *internal_default_instance(); + } + static inline const Player* internal_default_instance() { + return reinterpret_cast( + &_Player_default_instance_); + } + static constexpr int kIndexInFileMessages = + 1; + + friend void swap(Player& a, Player& b) { + a.Swap(&b); + } + inline void Swap(Player* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(Player* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + Player* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const Player& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const Player& from) { + Player::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(Player* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.Player"; + } + protected: + explicit Player(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kNameFieldNumber = 1, + kNucleusHashFieldNumber = 9, + kHardwareNameFieldNumber = 10, + kTeamNameFieldNumber = 11, + kCharacterFieldNumber = 13, + kSkinFieldNumber = 14, + kPosFieldNumber = 3, + kAnglesFieldNumber = 4, + kTeamIdFieldNumber = 2, + kCurrentHealthFieldNumber = 5, + kMaxHealthFieldNumber = 6, + kShieldHealthFieldNumber = 7, + kShieldMaxHealthFieldNumber = 8, + kSquadIndexFieldNumber = 12, + }; + // string name = 1; + void clear_name(); + const std::string& name() const; + template + void set_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_name(); + PROTOBUF_NODISCARD std::string* release_name(); + void set_allocated_name(std::string* name); + private: + const std::string& _internal_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value); + std::string* _internal_mutable_name(); + public: + + // string nucleusHash = 9; + void clear_nucleushash(); + const std::string& nucleushash() const; + template + void set_nucleushash(ArgT0&& arg0, ArgT... args); + std::string* mutable_nucleushash(); + PROTOBUF_NODISCARD std::string* release_nucleushash(); + void set_allocated_nucleushash(std::string* nucleushash); + private: + const std::string& _internal_nucleushash() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_nucleushash(const std::string& value); + std::string* _internal_mutable_nucleushash(); + public: + + // string hardwareName = 10; + void clear_hardwarename(); + const std::string& hardwarename() const; + template + void set_hardwarename(ArgT0&& arg0, ArgT... args); + std::string* mutable_hardwarename(); + PROTOBUF_NODISCARD std::string* release_hardwarename(); + void set_allocated_hardwarename(std::string* hardwarename); + private: + const std::string& _internal_hardwarename() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_hardwarename(const std::string& value); + std::string* _internal_mutable_hardwarename(); + public: + + // string teamName = 11; + void clear_teamname(); + const std::string& teamname() const; + template + void set_teamname(ArgT0&& arg0, ArgT... args); + std::string* mutable_teamname(); + PROTOBUF_NODISCARD std::string* release_teamname(); + void set_allocated_teamname(std::string* teamname); + private: + const std::string& _internal_teamname() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_teamname(const std::string& value); + std::string* _internal_mutable_teamname(); + public: + + // string character = 13; + void clear_character(); + const std::string& character() const; + template + void set_character(ArgT0&& arg0, ArgT... args); + std::string* mutable_character(); + PROTOBUF_NODISCARD std::string* release_character(); + void set_allocated_character(std::string* character); + private: + const std::string& _internal_character() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_character(const std::string& value); + std::string* _internal_mutable_character(); + public: + + // string skin = 14; + void clear_skin(); + const std::string& skin() const; + template + void set_skin(ArgT0&& arg0, ArgT... args); + std::string* mutable_skin(); + PROTOBUF_NODISCARD std::string* release_skin(); + void set_allocated_skin(std::string* skin); + private: + const std::string& _internal_skin() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_skin(const std::string& value); + std::string* _internal_mutable_skin(); + public: + + // .rtech.liveapi.Vector3 pos = 3; + bool has_pos() const; + private: + bool _internal_has_pos() const; + public: + void clear_pos(); + const ::rtech::liveapi::Vector3& pos() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Vector3* release_pos(); + ::rtech::liveapi::Vector3* mutable_pos(); + void set_allocated_pos(::rtech::liveapi::Vector3* pos); + private: + const ::rtech::liveapi::Vector3& _internal_pos() const; + ::rtech::liveapi::Vector3* _internal_mutable_pos(); + public: + void unsafe_arena_set_allocated_pos( + ::rtech::liveapi::Vector3* pos); + ::rtech::liveapi::Vector3* unsafe_arena_release_pos(); + + // .rtech.liveapi.Vector3 angles = 4; + bool has_angles() const; + private: + bool _internal_has_angles() const; + public: + void clear_angles(); + const ::rtech::liveapi::Vector3& angles() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Vector3* release_angles(); + ::rtech::liveapi::Vector3* mutable_angles(); + void set_allocated_angles(::rtech::liveapi::Vector3* angles); + private: + const ::rtech::liveapi::Vector3& _internal_angles() const; + ::rtech::liveapi::Vector3* _internal_mutable_angles(); + public: + void unsafe_arena_set_allocated_angles( + ::rtech::liveapi::Vector3* angles); + ::rtech::liveapi::Vector3* unsafe_arena_release_angles(); + + // uint32 teamId = 2; + void clear_teamid(); + uint32_t teamid() const; + void set_teamid(uint32_t value); + private: + uint32_t _internal_teamid() const; + void _internal_set_teamid(uint32_t value); + public: + + // uint32 currentHealth = 5; + void clear_currenthealth(); + uint32_t currenthealth() const; + void set_currenthealth(uint32_t value); + private: + uint32_t _internal_currenthealth() const; + void _internal_set_currenthealth(uint32_t value); + public: + + // uint32 maxHealth = 6; + void clear_maxhealth(); + uint32_t maxhealth() const; + void set_maxhealth(uint32_t value); + private: + uint32_t _internal_maxhealth() const; + void _internal_set_maxhealth(uint32_t value); + public: + + // uint32 shieldHealth = 7; + void clear_shieldhealth(); + uint32_t shieldhealth() const; + void set_shieldhealth(uint32_t value); + private: + uint32_t _internal_shieldhealth() const; + void _internal_set_shieldhealth(uint32_t value); + public: + + // uint32 shieldMaxHealth = 8; + void clear_shieldmaxhealth(); + uint32_t shieldmaxhealth() const; + void set_shieldmaxhealth(uint32_t value); + private: + uint32_t _internal_shieldmaxhealth() const; + void _internal_set_shieldmaxhealth(uint32_t value); + public: + + // uint32 squadIndex = 12; + void clear_squadindex(); + uint32_t squadindex() const; + void set_squadindex(uint32_t value); + private: + uint32_t _internal_squadindex() const; + void _internal_set_squadindex(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.Player) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr nucleushash_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr hardwarename_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr teamname_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr character_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr skin_; + ::rtech::liveapi::Vector3* pos_; + ::rtech::liveapi::Vector3* angles_; + uint32_t teamid_; + uint32_t currenthealth_; + uint32_t maxhealth_; + uint32_t shieldhealth_; + uint32_t shieldmaxhealth_; + uint32_t squadindex_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_LobbyPlayer final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_LobbyPlayer) */ { + public: + inline CustomMatch_LobbyPlayer() : CustomMatch_LobbyPlayer(nullptr) {} + ~CustomMatch_LobbyPlayer() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_LobbyPlayer(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_LobbyPlayer(const CustomMatch_LobbyPlayer& from); + CustomMatch_LobbyPlayer(CustomMatch_LobbyPlayer&& from) noexcept + : CustomMatch_LobbyPlayer() { + *this = ::std::move(from); + } + + inline CustomMatch_LobbyPlayer& operator=(const CustomMatch_LobbyPlayer& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_LobbyPlayer& operator=(CustomMatch_LobbyPlayer&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_LobbyPlayer& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_LobbyPlayer* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_LobbyPlayer_default_instance_); + } + static constexpr int kIndexInFileMessages = + 2; + + friend void swap(CustomMatch_LobbyPlayer& a, CustomMatch_LobbyPlayer& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_LobbyPlayer* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_LobbyPlayer* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_LobbyPlayer* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_LobbyPlayer& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_LobbyPlayer& from) { + CustomMatch_LobbyPlayer::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_LobbyPlayer* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_LobbyPlayer"; + } + protected: + explicit CustomMatch_LobbyPlayer(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kNameFieldNumber = 1, + kNucleusHashFieldNumber = 3, + kHardwareNameFieldNumber = 4, + kTeamIdFieldNumber = 2, + }; + // string name = 1; + void clear_name(); + const std::string& name() const; + template + void set_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_name(); + PROTOBUF_NODISCARD std::string* release_name(); + void set_allocated_name(std::string* name); + private: + const std::string& _internal_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value); + std::string* _internal_mutable_name(); + public: + + // string nucleusHash = 3; + void clear_nucleushash(); + const std::string& nucleushash() const; + template + void set_nucleushash(ArgT0&& arg0, ArgT... args); + std::string* mutable_nucleushash(); + PROTOBUF_NODISCARD std::string* release_nucleushash(); + void set_allocated_nucleushash(std::string* nucleushash); + private: + const std::string& _internal_nucleushash() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_nucleushash(const std::string& value); + std::string* _internal_mutable_nucleushash(); + public: + + // string hardwareName = 4; + void clear_hardwarename(); + const std::string& hardwarename() const; + template + void set_hardwarename(ArgT0&& arg0, ArgT... args); + std::string* mutable_hardwarename(); + PROTOBUF_NODISCARD std::string* release_hardwarename(); + void set_allocated_hardwarename(std::string* hardwarename); + private: + const std::string& _internal_hardwarename() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_hardwarename(const std::string& value); + std::string* _internal_mutable_hardwarename(); + public: + + // uint32 teamId = 2; + void clear_teamid(); + uint32_t teamid() const; + void set_teamid(uint32_t value); + private: + uint32_t _internal_teamid() const; + void _internal_set_teamid(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_LobbyPlayer) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr nucleushash_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr hardwarename_; + uint32_t teamid_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class Datacenter final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.Datacenter) */ { + public: + inline Datacenter() : Datacenter(nullptr) {} + ~Datacenter() override; + explicit PROTOBUF_CONSTEXPR Datacenter(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + Datacenter(const Datacenter& from); + Datacenter(Datacenter&& from) noexcept + : Datacenter() { + *this = ::std::move(from); + } + + inline Datacenter& operator=(const Datacenter& from) { + CopyFrom(from); + return *this; + } + inline Datacenter& operator=(Datacenter&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const Datacenter& default_instance() { + return *internal_default_instance(); + } + static inline const Datacenter* internal_default_instance() { + return reinterpret_cast( + &_Datacenter_default_instance_); + } + static constexpr int kIndexInFileMessages = + 3; + + friend void swap(Datacenter& a, Datacenter& b) { + a.Swap(&b); + } + inline void Swap(Datacenter* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(Datacenter* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + Datacenter* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const Datacenter& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const Datacenter& from) { + Datacenter::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(Datacenter* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.Datacenter"; + } + protected: + explicit Datacenter(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kNameFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string name = 3; + void clear_name(); + const std::string& name() const; + template + void set_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_name(); + PROTOBUF_NODISCARD std::string* release_name(); + void set_allocated_name(std::string* name); + private: + const std::string& _internal_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value); + std::string* _internal_mutable_name(); + public: + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.Datacenter) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class Version final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.Version) */ { + public: + inline Version() : Version(nullptr) {} + ~Version() override; + explicit PROTOBUF_CONSTEXPR Version(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + Version(const Version& from); + Version(Version&& from) noexcept + : Version() { + *this = ::std::move(from); + } + + inline Version& operator=(const Version& from) { + CopyFrom(from); + return *this; + } + inline Version& operator=(Version&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const Version& default_instance() { + return *internal_default_instance(); + } + static inline const Version* internal_default_instance() { + return reinterpret_cast( + &_Version_default_instance_); + } + static constexpr int kIndexInFileMessages = + 4; + + friend void swap(Version& a, Version& b) { + a.Swap(&b); + } + inline void Swap(Version* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(Version* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + Version* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const Version& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const Version& from) { + Version::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(Version* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.Version"; + } + protected: + explicit Version(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kRevisionFieldNumber = 4, + kMajorNumFieldNumber = 1, + kMinorNumFieldNumber = 2, + kBuildStampFieldNumber = 3, + }; + // string revision = 4; + void clear_revision(); + const std::string& revision() const; + template + void set_revision(ArgT0&& arg0, ArgT... args); + std::string* mutable_revision(); + PROTOBUF_NODISCARD std::string* release_revision(); + void set_allocated_revision(std::string* revision); + private: + const std::string& _internal_revision() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_revision(const std::string& value); + std::string* _internal_mutable_revision(); + public: + + // uint32 major_num = 1; + void clear_major_num(); + uint32_t major_num() const; + void set_major_num(uint32_t value); + private: + uint32_t _internal_major_num() const; + void _internal_set_major_num(uint32_t value); + public: + + // uint32 minor_num = 2; + void clear_minor_num(); + uint32_t minor_num() const; + void set_minor_num(uint32_t value); + private: + uint32_t _internal_minor_num() const; + void _internal_set_minor_num(uint32_t value); + public: + + // uint32 build_stamp = 3; + void clear_build_stamp(); + uint32_t build_stamp() const; + void set_build_stamp(uint32_t value); + private: + uint32_t _internal_build_stamp() const; + void _internal_set_build_stamp(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.Version) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr revision_; + uint32_t major_num_; + uint32_t minor_num_; + uint32_t build_stamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class InventoryItem final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.InventoryItem) */ { + public: + inline InventoryItem() : InventoryItem(nullptr) {} + ~InventoryItem() override; + explicit PROTOBUF_CONSTEXPR InventoryItem(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + InventoryItem(const InventoryItem& from); + InventoryItem(InventoryItem&& from) noexcept + : InventoryItem() { + *this = ::std::move(from); + } + + inline InventoryItem& operator=(const InventoryItem& from) { + CopyFrom(from); + return *this; + } + inline InventoryItem& operator=(InventoryItem&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const InventoryItem& default_instance() { + return *internal_default_instance(); + } + static inline const InventoryItem* internal_default_instance() { + return reinterpret_cast( + &_InventoryItem_default_instance_); + } + static constexpr int kIndexInFileMessages = + 5; + + friend void swap(InventoryItem& a, InventoryItem& b) { + a.Swap(&b); + } + inline void Swap(InventoryItem* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(InventoryItem* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + InventoryItem* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const InventoryItem& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const InventoryItem& from) { + InventoryItem::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(InventoryItem* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.InventoryItem"; + } + protected: + explicit InventoryItem(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kItemFieldNumber = 2, + kExtraDataFieldNumber = 3, + kQuantityFieldNumber = 1, + }; + // string item = 2; + void clear_item(); + const std::string& item() const; + template + void set_item(ArgT0&& arg0, ArgT... args); + std::string* mutable_item(); + PROTOBUF_NODISCARD std::string* release_item(); + void set_allocated_item(std::string* item); + private: + const std::string& _internal_item() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_item(const std::string& value); + std::string* _internal_mutable_item(); + public: + + // string extraData = 3; + void clear_extradata(); + const std::string& extradata() const; + template + void set_extradata(ArgT0&& arg0, ArgT... args); + std::string* mutable_extradata(); + PROTOBUF_NODISCARD std::string* release_extradata(); + void set_allocated_extradata(std::string* extradata); + private: + const std::string& _internal_extradata() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_extradata(const std::string& value); + std::string* _internal_mutable_extradata(); + public: + + // int32 quantity = 1; + void clear_quantity(); + int32_t quantity() const; + void set_quantity(int32_t value); + private: + int32_t _internal_quantity() const; + void _internal_set_quantity(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.InventoryItem) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr item_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr extradata_; + int32_t quantity_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class LoadoutConfiguration final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.LoadoutConfiguration) */ { + public: + inline LoadoutConfiguration() : LoadoutConfiguration(nullptr) {} + ~LoadoutConfiguration() override; + explicit PROTOBUF_CONSTEXPR LoadoutConfiguration(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + LoadoutConfiguration(const LoadoutConfiguration& from); + LoadoutConfiguration(LoadoutConfiguration&& from) noexcept + : LoadoutConfiguration() { + *this = ::std::move(from); + } + + inline LoadoutConfiguration& operator=(const LoadoutConfiguration& from) { + CopyFrom(from); + return *this; + } + inline LoadoutConfiguration& operator=(LoadoutConfiguration&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const LoadoutConfiguration& default_instance() { + return *internal_default_instance(); + } + static inline const LoadoutConfiguration* internal_default_instance() { + return reinterpret_cast( + &_LoadoutConfiguration_default_instance_); + } + static constexpr int kIndexInFileMessages = + 6; + + friend void swap(LoadoutConfiguration& a, LoadoutConfiguration& b) { + a.Swap(&b); + } + inline void Swap(LoadoutConfiguration* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(LoadoutConfiguration* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + LoadoutConfiguration* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const LoadoutConfiguration& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const LoadoutConfiguration& from) { + LoadoutConfiguration::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(LoadoutConfiguration* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.LoadoutConfiguration"; + } + protected: + explicit LoadoutConfiguration(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kWeaponsFieldNumber = 1, + kEquipmentFieldNumber = 2, + }; + // repeated .rtech.liveapi.InventoryItem weapons = 1; + int weapons_size() const; + private: + int _internal_weapons_size() const; + public: + void clear_weapons(); + ::rtech::liveapi::InventoryItem* mutable_weapons(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >* + mutable_weapons(); + private: + const ::rtech::liveapi::InventoryItem& _internal_weapons(int index) const; + ::rtech::liveapi::InventoryItem* _internal_add_weapons(); + public: + const ::rtech::liveapi::InventoryItem& weapons(int index) const; + ::rtech::liveapi::InventoryItem* add_weapons(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >& + weapons() const; + + // repeated .rtech.liveapi.InventoryItem equipment = 2; + int equipment_size() const; + private: + int _internal_equipment_size() const; + public: + void clear_equipment(); + ::rtech::liveapi::InventoryItem* mutable_equipment(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >* + mutable_equipment(); + private: + const ::rtech::liveapi::InventoryItem& _internal_equipment(int index) const; + ::rtech::liveapi::InventoryItem* _internal_add_equipment(); + public: + const ::rtech::liveapi::InventoryItem& equipment(int index) const; + ::rtech::liveapi::InventoryItem* add_equipment(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >& + equipment() const; + + // @@protoc_insertion_point(class_scope:rtech.liveapi.LoadoutConfiguration) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem > weapons_; + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem > equipment_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class Init final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.Init) */ { + public: + inline Init() : Init(nullptr) {} + ~Init() override; + explicit PROTOBUF_CONSTEXPR Init(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + Init(const Init& from); + Init(Init&& from) noexcept + : Init() { + *this = ::std::move(from); + } + + inline Init& operator=(const Init& from) { + CopyFrom(from); + return *this; + } + inline Init& operator=(Init&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const Init& default_instance() { + return *internal_default_instance(); + } + static inline const Init* internal_default_instance() { + return reinterpret_cast( + &_Init_default_instance_); + } + static constexpr int kIndexInFileMessages = + 7; + + friend void swap(Init& a, Init& b) { + a.Swap(&b); + } + inline void Swap(Init* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(Init* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + Init* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const Init& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const Init& from) { + Init::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(Init* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.Init"; + } + protected: + explicit Init(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kGameVersionFieldNumber = 3, + kPlatformFieldNumber = 5, + kNameFieldNumber = 6, + kApiVersionFieldNumber = 4, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string gameVersion = 3; + void clear_gameversion(); + const std::string& gameversion() const; + template + void set_gameversion(ArgT0&& arg0, ArgT... args); + std::string* mutable_gameversion(); + PROTOBUF_NODISCARD std::string* release_gameversion(); + void set_allocated_gameversion(std::string* gameversion); + private: + const std::string& _internal_gameversion() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_gameversion(const std::string& value); + std::string* _internal_mutable_gameversion(); + public: + + // string platform = 5; + void clear_platform(); + const std::string& platform() const; + template + void set_platform(ArgT0&& arg0, ArgT... args); + std::string* mutable_platform(); + PROTOBUF_NODISCARD std::string* release_platform(); + void set_allocated_platform(std::string* platform); + private: + const std::string& _internal_platform() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_platform(const std::string& value); + std::string* _internal_mutable_platform(); + public: + + // string name = 6; + void clear_name(); + const std::string& name() const; + template + void set_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_name(); + PROTOBUF_NODISCARD std::string* release_name(); + void set_allocated_name(std::string* name); + private: + const std::string& _internal_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value); + std::string* _internal_mutable_name(); + public: + + // .rtech.liveapi.Version apiVersion = 4; + bool has_apiversion() const; + private: + bool _internal_has_apiversion() const; + public: + void clear_apiversion(); + const ::rtech::liveapi::Version& apiversion() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Version* release_apiversion(); + ::rtech::liveapi::Version* mutable_apiversion(); + void set_allocated_apiversion(::rtech::liveapi::Version* apiversion); + private: + const ::rtech::liveapi::Version& _internal_apiversion() const; + ::rtech::liveapi::Version* _internal_mutable_apiversion(); + public: + void unsafe_arena_set_allocated_apiversion( + ::rtech::liveapi::Version* apiversion); + ::rtech::liveapi::Version* unsafe_arena_release_apiversion(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.Init) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr gameversion_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr platform_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; + ::rtech::liveapi::Version* apiversion_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_LobbyPlayers final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_LobbyPlayers) */ { + public: + inline CustomMatch_LobbyPlayers() : CustomMatch_LobbyPlayers(nullptr) {} + ~CustomMatch_LobbyPlayers() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_LobbyPlayers(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_LobbyPlayers(const CustomMatch_LobbyPlayers& from); + CustomMatch_LobbyPlayers(CustomMatch_LobbyPlayers&& from) noexcept + : CustomMatch_LobbyPlayers() { + *this = ::std::move(from); + } + + inline CustomMatch_LobbyPlayers& operator=(const CustomMatch_LobbyPlayers& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_LobbyPlayers& operator=(CustomMatch_LobbyPlayers&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_LobbyPlayers& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_LobbyPlayers* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_LobbyPlayers_default_instance_); + } + static constexpr int kIndexInFileMessages = + 8; + + friend void swap(CustomMatch_LobbyPlayers& a, CustomMatch_LobbyPlayers& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_LobbyPlayers* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_LobbyPlayers* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_LobbyPlayers* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_LobbyPlayers& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_LobbyPlayers& from) { + CustomMatch_LobbyPlayers::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_LobbyPlayers* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_LobbyPlayers"; + } + protected: + explicit CustomMatch_LobbyPlayers(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPlayersFieldNumber = 2, + kPlayerTokenFieldNumber = 1, + }; + // repeated .rtech.liveapi.CustomMatch_LobbyPlayer players = 2; + int players_size() const; + private: + int _internal_players_size() const; + public: + void clear_players(); + ::rtech::liveapi::CustomMatch_LobbyPlayer* mutable_players(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::CustomMatch_LobbyPlayer >* + mutable_players(); + private: + const ::rtech::liveapi::CustomMatch_LobbyPlayer& _internal_players(int index) const; + ::rtech::liveapi::CustomMatch_LobbyPlayer* _internal_add_players(); + public: + const ::rtech::liveapi::CustomMatch_LobbyPlayer& players(int index) const; + ::rtech::liveapi::CustomMatch_LobbyPlayer* add_players(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::CustomMatch_LobbyPlayer >& + players() const; + + // string playerToken = 1; + void clear_playertoken(); + const std::string& playertoken() const; + template + void set_playertoken(ArgT0&& arg0, ArgT... args); + std::string* mutable_playertoken(); + PROTOBUF_NODISCARD std::string* release_playertoken(); + void set_allocated_playertoken(std::string* playertoken); + private: + const std::string& _internal_playertoken() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_playertoken(const std::string& value); + std::string* _internal_mutable_playertoken(); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_LobbyPlayers) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::CustomMatch_LobbyPlayer > players_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr playertoken_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class ObserverSwitched final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.ObserverSwitched) */ { + public: + inline ObserverSwitched() : ObserverSwitched(nullptr) {} + ~ObserverSwitched() override; + explicit PROTOBUF_CONSTEXPR ObserverSwitched(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ObserverSwitched(const ObserverSwitched& from); + ObserverSwitched(ObserverSwitched&& from) noexcept + : ObserverSwitched() { + *this = ::std::move(from); + } + + inline ObserverSwitched& operator=(const ObserverSwitched& from) { + CopyFrom(from); + return *this; + } + inline ObserverSwitched& operator=(ObserverSwitched&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ObserverSwitched& default_instance() { + return *internal_default_instance(); + } + static inline const ObserverSwitched* internal_default_instance() { + return reinterpret_cast( + &_ObserverSwitched_default_instance_); + } + static constexpr int kIndexInFileMessages = + 9; + + friend void swap(ObserverSwitched& a, ObserverSwitched& b) { + a.Swap(&b); + } + inline void Swap(ObserverSwitched* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ObserverSwitched* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ObserverSwitched* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ObserverSwitched& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const ObserverSwitched& from) { + ObserverSwitched::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ObserverSwitched* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.ObserverSwitched"; + } + protected: + explicit ObserverSwitched(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kTargetTeamFieldNumber = 5, + kCategoryFieldNumber = 2, + kObserverFieldNumber = 3, + kTargetFieldNumber = 4, + kTimestampFieldNumber = 1, + }; + // repeated .rtech.liveapi.Player targetTeam = 5; + int targetteam_size() const; + private: + int _internal_targetteam_size() const; + public: + void clear_targetteam(); + ::rtech::liveapi::Player* mutable_targetteam(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* + mutable_targetteam(); + private: + const ::rtech::liveapi::Player& _internal_targetteam(int index) const; + ::rtech::liveapi::Player* _internal_add_targetteam(); + public: + const ::rtech::liveapi::Player& targetteam(int index) const; + ::rtech::liveapi::Player* add_targetteam(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& + targetteam() const; + + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player observer = 3; + bool has_observer() const; + private: + bool _internal_has_observer() const; + public: + void clear_observer(); + const ::rtech::liveapi::Player& observer() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_observer(); + ::rtech::liveapi::Player* mutable_observer(); + void set_allocated_observer(::rtech::liveapi::Player* observer); + private: + const ::rtech::liveapi::Player& _internal_observer() const; + ::rtech::liveapi::Player* _internal_mutable_observer(); + public: + void unsafe_arena_set_allocated_observer( + ::rtech::liveapi::Player* observer); + ::rtech::liveapi::Player* unsafe_arena_release_observer(); + + // .rtech.liveapi.Player target = 4; + bool has_target() const; + private: + bool _internal_has_target() const; + public: + void clear_target(); + const ::rtech::liveapi::Player& target() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_target(); + ::rtech::liveapi::Player* mutable_target(); + void set_allocated_target(::rtech::liveapi::Player* target); + private: + const ::rtech::liveapi::Player& _internal_target() const; + ::rtech::liveapi::Player* _internal_mutable_target(); + public: + void unsafe_arena_set_allocated_target( + ::rtech::liveapi::Player* target); + ::rtech::liveapi::Player* unsafe_arena_release_target(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.ObserverSwitched) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player > targetteam_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* observer_; + ::rtech::liveapi::Player* target_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class ObserverAnnotation final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.ObserverAnnotation) */ { + public: + inline ObserverAnnotation() : ObserverAnnotation(nullptr) {} + ~ObserverAnnotation() override; + explicit PROTOBUF_CONSTEXPR ObserverAnnotation(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ObserverAnnotation(const ObserverAnnotation& from); + ObserverAnnotation(ObserverAnnotation&& from) noexcept + : ObserverAnnotation() { + *this = ::std::move(from); + } + + inline ObserverAnnotation& operator=(const ObserverAnnotation& from) { + CopyFrom(from); + return *this; + } + inline ObserverAnnotation& operator=(ObserverAnnotation&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ObserverAnnotation& default_instance() { + return *internal_default_instance(); + } + static inline const ObserverAnnotation* internal_default_instance() { + return reinterpret_cast( + &_ObserverAnnotation_default_instance_); + } + static constexpr int kIndexInFileMessages = + 10; + + friend void swap(ObserverAnnotation& a, ObserverAnnotation& b) { + a.Swap(&b); + } + inline void Swap(ObserverAnnotation* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ObserverAnnotation* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ObserverAnnotation* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ObserverAnnotation& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const ObserverAnnotation& from) { + ObserverAnnotation::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ObserverAnnotation* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.ObserverAnnotation"; + } + protected: + explicit ObserverAnnotation(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kTimestampFieldNumber = 1, + kAnnotationSerialFieldNumber = 3, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 annotationSerial = 3; + void clear_annotationserial(); + int32_t annotationserial() const; + void set_annotationserial(int32_t value); + private: + int32_t _internal_annotationserial() const; + void _internal_set_annotationserial(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.ObserverAnnotation) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + uint64_t timestamp_; + int32_t annotationserial_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class MatchSetup final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.MatchSetup) */ { + public: + inline MatchSetup() : MatchSetup(nullptr) {} + ~MatchSetup() override; + explicit PROTOBUF_CONSTEXPR MatchSetup(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + MatchSetup(const MatchSetup& from); + MatchSetup(MatchSetup&& from) noexcept + : MatchSetup() { + *this = ::std::move(from); + } + + inline MatchSetup& operator=(const MatchSetup& from) { + CopyFrom(from); + return *this; + } + inline MatchSetup& operator=(MatchSetup&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const MatchSetup& default_instance() { + return *internal_default_instance(); + } + static inline const MatchSetup* internal_default_instance() { + return reinterpret_cast( + &_MatchSetup_default_instance_); + } + static constexpr int kIndexInFileMessages = + 11; + + friend void swap(MatchSetup& a, MatchSetup& b) { + a.Swap(&b); + } + inline void Swap(MatchSetup* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(MatchSetup* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + MatchSetup* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const MatchSetup& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const MatchSetup& from) { + MatchSetup::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(MatchSetup* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.MatchSetup"; + } + protected: + explicit MatchSetup(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kMapFieldNumber = 3, + kPlaylistNameFieldNumber = 4, + kPlaylistDescFieldNumber = 5, + kServerIdFieldNumber = 9, + kDatacenterFieldNumber = 6, + kStartingLoadoutFieldNumber = 10, + kTimestampFieldNumber = 1, + kAimAssistOnFieldNumber = 7, + kAnonymousModeFieldNumber = 8, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string map = 3; + void clear_map(); + const std::string& map() const; + template + void set_map(ArgT0&& arg0, ArgT... args); + std::string* mutable_map(); + PROTOBUF_NODISCARD std::string* release_map(); + void set_allocated_map(std::string* map); + private: + const std::string& _internal_map() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_map(const std::string& value); + std::string* _internal_mutable_map(); + public: + + // string playlistName = 4; + void clear_playlistname(); + const std::string& playlistname() const; + template + void set_playlistname(ArgT0&& arg0, ArgT... args); + std::string* mutable_playlistname(); + PROTOBUF_NODISCARD std::string* release_playlistname(); + void set_allocated_playlistname(std::string* playlistname); + private: + const std::string& _internal_playlistname() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_playlistname(const std::string& value); + std::string* _internal_mutable_playlistname(); + public: + + // string playlistDesc = 5; + void clear_playlistdesc(); + const std::string& playlistdesc() const; + template + void set_playlistdesc(ArgT0&& arg0, ArgT... args); + std::string* mutable_playlistdesc(); + PROTOBUF_NODISCARD std::string* release_playlistdesc(); + void set_allocated_playlistdesc(std::string* playlistdesc); + private: + const std::string& _internal_playlistdesc() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_playlistdesc(const std::string& value); + std::string* _internal_mutable_playlistdesc(); + public: + + // string serverId = 9; + void clear_serverid(); + const std::string& serverid() const; + template + void set_serverid(ArgT0&& arg0, ArgT... args); + std::string* mutable_serverid(); + PROTOBUF_NODISCARD std::string* release_serverid(); + void set_allocated_serverid(std::string* serverid); + private: + const std::string& _internal_serverid() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_serverid(const std::string& value); + std::string* _internal_mutable_serverid(); + public: + + // .rtech.liveapi.Datacenter datacenter = 6; + bool has_datacenter() const; + private: + bool _internal_has_datacenter() const; + public: + void clear_datacenter(); + const ::rtech::liveapi::Datacenter& datacenter() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Datacenter* release_datacenter(); + ::rtech::liveapi::Datacenter* mutable_datacenter(); + void set_allocated_datacenter(::rtech::liveapi::Datacenter* datacenter); + private: + const ::rtech::liveapi::Datacenter& _internal_datacenter() const; + ::rtech::liveapi::Datacenter* _internal_mutable_datacenter(); + public: + void unsafe_arena_set_allocated_datacenter( + ::rtech::liveapi::Datacenter* datacenter); + ::rtech::liveapi::Datacenter* unsafe_arena_release_datacenter(); + + // .rtech.liveapi.LoadoutConfiguration startingLoadout = 10; + bool has_startingloadout() const; + private: + bool _internal_has_startingloadout() const; + public: + void clear_startingloadout(); + const ::rtech::liveapi::LoadoutConfiguration& startingloadout() const; + PROTOBUF_NODISCARD ::rtech::liveapi::LoadoutConfiguration* release_startingloadout(); + ::rtech::liveapi::LoadoutConfiguration* mutable_startingloadout(); + void set_allocated_startingloadout(::rtech::liveapi::LoadoutConfiguration* startingloadout); + private: + const ::rtech::liveapi::LoadoutConfiguration& _internal_startingloadout() const; + ::rtech::liveapi::LoadoutConfiguration* _internal_mutable_startingloadout(); + public: + void unsafe_arena_set_allocated_startingloadout( + ::rtech::liveapi::LoadoutConfiguration* startingloadout); + ::rtech::liveapi::LoadoutConfiguration* unsafe_arena_release_startingloadout(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // bool aimAssistOn = 7; + void clear_aimassiston(); + bool aimassiston() const; + void set_aimassiston(bool value); + private: + bool _internal_aimassiston() const; + void _internal_set_aimassiston(bool value); + public: + + // bool anonymousMode = 8; + void clear_anonymousmode(); + bool anonymousmode() const; + void set_anonymousmode(bool value); + private: + bool _internal_anonymousmode() const; + void _internal_set_anonymousmode(bool value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.MatchSetup) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr map_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr playlistname_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr playlistdesc_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr serverid_; + ::rtech::liveapi::Datacenter* datacenter_; + ::rtech::liveapi::LoadoutConfiguration* startingloadout_; + uint64_t timestamp_; + bool aimassiston_; + bool anonymousmode_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class GameStateChanged final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.GameStateChanged) */ { + public: + inline GameStateChanged() : GameStateChanged(nullptr) {} + ~GameStateChanged() override; + explicit PROTOBUF_CONSTEXPR GameStateChanged(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + GameStateChanged(const GameStateChanged& from); + GameStateChanged(GameStateChanged&& from) noexcept + : GameStateChanged() { + *this = ::std::move(from); + } + + inline GameStateChanged& operator=(const GameStateChanged& from) { + CopyFrom(from); + return *this; + } + inline GameStateChanged& operator=(GameStateChanged&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const GameStateChanged& default_instance() { + return *internal_default_instance(); + } + static inline const GameStateChanged* internal_default_instance() { + return reinterpret_cast( + &_GameStateChanged_default_instance_); + } + static constexpr int kIndexInFileMessages = + 12; + + friend void swap(GameStateChanged& a, GameStateChanged& b) { + a.Swap(&b); + } + inline void Swap(GameStateChanged* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(GameStateChanged* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + GameStateChanged* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const GameStateChanged& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const GameStateChanged& from) { + GameStateChanged::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(GameStateChanged* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.GameStateChanged"; + } + protected: + explicit GameStateChanged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kStateFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string state = 3; + void clear_state(); + const std::string& state() const; + template + void set_state(ArgT0&& arg0, ArgT... args); + std::string* mutable_state(); + PROTOBUF_NODISCARD std::string* release_state(); + void set_allocated_state(std::string* state); + private: + const std::string& _internal_state() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_state(const std::string& value); + std::string* _internal_mutable_state(); + public: + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.GameStateChanged) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr state_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CharacterSelected final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CharacterSelected) */ { + public: + inline CharacterSelected() : CharacterSelected(nullptr) {} + ~CharacterSelected() override; + explicit PROTOBUF_CONSTEXPR CharacterSelected(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CharacterSelected(const CharacterSelected& from); + CharacterSelected(CharacterSelected&& from) noexcept + : CharacterSelected() { + *this = ::std::move(from); + } + + inline CharacterSelected& operator=(const CharacterSelected& from) { + CopyFrom(from); + return *this; + } + inline CharacterSelected& operator=(CharacterSelected&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CharacterSelected& default_instance() { + return *internal_default_instance(); + } + static inline const CharacterSelected* internal_default_instance() { + return reinterpret_cast( + &_CharacterSelected_default_instance_); + } + static constexpr int kIndexInFileMessages = + 13; + + friend void swap(CharacterSelected& a, CharacterSelected& b) { + a.Swap(&b); + } + inline void Swap(CharacterSelected* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CharacterSelected* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CharacterSelected* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CharacterSelected& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CharacterSelected& from) { + CharacterSelected::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CharacterSelected* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CharacterSelected"; + } + protected: + explicit CharacterSelected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CharacterSelected) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class MatchStateEnd final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.MatchStateEnd) */ { + public: + inline MatchStateEnd() : MatchStateEnd(nullptr) {} + ~MatchStateEnd() override; + explicit PROTOBUF_CONSTEXPR MatchStateEnd(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + MatchStateEnd(const MatchStateEnd& from); + MatchStateEnd(MatchStateEnd&& from) noexcept + : MatchStateEnd() { + *this = ::std::move(from); + } + + inline MatchStateEnd& operator=(const MatchStateEnd& from) { + CopyFrom(from); + return *this; + } + inline MatchStateEnd& operator=(MatchStateEnd&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const MatchStateEnd& default_instance() { + return *internal_default_instance(); + } + static inline const MatchStateEnd* internal_default_instance() { + return reinterpret_cast( + &_MatchStateEnd_default_instance_); + } + static constexpr int kIndexInFileMessages = + 14; + + friend void swap(MatchStateEnd& a, MatchStateEnd& b) { + a.Swap(&b); + } + inline void Swap(MatchStateEnd* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(MatchStateEnd* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + MatchStateEnd* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const MatchStateEnd& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const MatchStateEnd& from) { + MatchStateEnd::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(MatchStateEnd* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.MatchStateEnd"; + } + protected: + explicit MatchStateEnd(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kWinnersFieldNumber = 4, + kCategoryFieldNumber = 2, + kStateFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // repeated .rtech.liveapi.Player winners = 4; + int winners_size() const; + private: + int _internal_winners_size() const; + public: + void clear_winners(); + ::rtech::liveapi::Player* mutable_winners(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* + mutable_winners(); + private: + const ::rtech::liveapi::Player& _internal_winners(int index) const; + ::rtech::liveapi::Player* _internal_add_winners(); + public: + const ::rtech::liveapi::Player& winners(int index) const; + ::rtech::liveapi::Player* add_winners(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& + winners() const; + + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string state = 3; + void clear_state(); + const std::string& state() const; + template + void set_state(ArgT0&& arg0, ArgT... args); + std::string* mutable_state(); + PROTOBUF_NODISCARD std::string* release_state(); + void set_allocated_state(std::string* state); + private: + const std::string& _internal_state() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_state(const std::string& value); + std::string* _internal_mutable_state(); + public: + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.MatchStateEnd) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player > winners_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr state_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class RingStartClosing final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.RingStartClosing) */ { + public: + inline RingStartClosing() : RingStartClosing(nullptr) {} + ~RingStartClosing() override; + explicit PROTOBUF_CONSTEXPR RingStartClosing(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + RingStartClosing(const RingStartClosing& from); + RingStartClosing(RingStartClosing&& from) noexcept + : RingStartClosing() { + *this = ::std::move(from); + } + + inline RingStartClosing& operator=(const RingStartClosing& from) { + CopyFrom(from); + return *this; + } + inline RingStartClosing& operator=(RingStartClosing&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const RingStartClosing& default_instance() { + return *internal_default_instance(); + } + static inline const RingStartClosing* internal_default_instance() { + return reinterpret_cast( + &_RingStartClosing_default_instance_); + } + static constexpr int kIndexInFileMessages = + 15; + + friend void swap(RingStartClosing& a, RingStartClosing& b) { + a.Swap(&b); + } + inline void Swap(RingStartClosing* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(RingStartClosing* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + RingStartClosing* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const RingStartClosing& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const RingStartClosing& from) { + RingStartClosing::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(RingStartClosing* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.RingStartClosing"; + } + protected: + explicit RingStartClosing(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kCenterFieldNumber = 4, + kTimestampFieldNumber = 1, + kStageFieldNumber = 3, + kCurrentRadiusFieldNumber = 5, + kEndRadiusFieldNumber = 6, + kShrinkDurationFieldNumber = 7, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Vector3 center = 4; + bool has_center() const; + private: + bool _internal_has_center() const; + public: + void clear_center(); + const ::rtech::liveapi::Vector3& center() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Vector3* release_center(); + ::rtech::liveapi::Vector3* mutable_center(); + void set_allocated_center(::rtech::liveapi::Vector3* center); + private: + const ::rtech::liveapi::Vector3& _internal_center() const; + ::rtech::liveapi::Vector3* _internal_mutable_center(); + public: + void unsafe_arena_set_allocated_center( + ::rtech::liveapi::Vector3* center); + ::rtech::liveapi::Vector3* unsafe_arena_release_center(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // uint32 stage = 3; + void clear_stage(); + uint32_t stage() const; + void set_stage(uint32_t value); + private: + uint32_t _internal_stage() const; + void _internal_set_stage(uint32_t value); + public: + + // float currentRadius = 5; + void clear_currentradius(); + float currentradius() const; + void set_currentradius(float value); + private: + float _internal_currentradius() const; + void _internal_set_currentradius(float value); + public: + + // float endRadius = 6; + void clear_endradius(); + float endradius() const; + void set_endradius(float value); + private: + float _internal_endradius() const; + void _internal_set_endradius(float value); + public: + + // float shrinkDuration = 7; + void clear_shrinkduration(); + float shrinkduration() const; + void set_shrinkduration(float value); + private: + float _internal_shrinkduration() const; + void _internal_set_shrinkduration(float value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.RingStartClosing) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Vector3* center_; + uint64_t timestamp_; + uint32_t stage_; + float currentradius_; + float endradius_; + float shrinkduration_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class RingFinishedClosing final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.RingFinishedClosing) */ { + public: + inline RingFinishedClosing() : RingFinishedClosing(nullptr) {} + ~RingFinishedClosing() override; + explicit PROTOBUF_CONSTEXPR RingFinishedClosing(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + RingFinishedClosing(const RingFinishedClosing& from); + RingFinishedClosing(RingFinishedClosing&& from) noexcept + : RingFinishedClosing() { + *this = ::std::move(from); + } + + inline RingFinishedClosing& operator=(const RingFinishedClosing& from) { + CopyFrom(from); + return *this; + } + inline RingFinishedClosing& operator=(RingFinishedClosing&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const RingFinishedClosing& default_instance() { + return *internal_default_instance(); + } + static inline const RingFinishedClosing* internal_default_instance() { + return reinterpret_cast( + &_RingFinishedClosing_default_instance_); + } + static constexpr int kIndexInFileMessages = + 16; + + friend void swap(RingFinishedClosing& a, RingFinishedClosing& b) { + a.Swap(&b); + } + inline void Swap(RingFinishedClosing* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(RingFinishedClosing* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + RingFinishedClosing* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const RingFinishedClosing& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const RingFinishedClosing& from) { + RingFinishedClosing::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(RingFinishedClosing* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.RingFinishedClosing"; + } + protected: + explicit RingFinishedClosing(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kCenterFieldNumber = 4, + kTimestampFieldNumber = 1, + kStageFieldNumber = 3, + kCurrentRadiusFieldNumber = 5, + kShrinkDurationFieldNumber = 7, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Vector3 center = 4; + bool has_center() const; + private: + bool _internal_has_center() const; + public: + void clear_center(); + const ::rtech::liveapi::Vector3& center() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Vector3* release_center(); + ::rtech::liveapi::Vector3* mutable_center(); + void set_allocated_center(::rtech::liveapi::Vector3* center); + private: + const ::rtech::liveapi::Vector3& _internal_center() const; + ::rtech::liveapi::Vector3* _internal_mutable_center(); + public: + void unsafe_arena_set_allocated_center( + ::rtech::liveapi::Vector3* center); + ::rtech::liveapi::Vector3* unsafe_arena_release_center(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // uint32 stage = 3; + void clear_stage(); + uint32_t stage() const; + void set_stage(uint32_t value); + private: + uint32_t _internal_stage() const; + void _internal_set_stage(uint32_t value); + public: + + // float currentRadius = 5; + void clear_currentradius(); + float currentradius() const; + void set_currentradius(float value); + private: + float _internal_currentradius() const; + void _internal_set_currentradius(float value); + public: + + // float shrinkDuration = 7; + void clear_shrinkduration(); + float shrinkduration() const; + void set_shrinkduration(float value); + private: + float _internal_shrinkduration() const; + void _internal_set_shrinkduration(float value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.RingFinishedClosing) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Vector3* center_; + uint64_t timestamp_; + uint32_t stage_; + float currentradius_; + float shrinkduration_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerConnected final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerConnected) */ { + public: + inline PlayerConnected() : PlayerConnected(nullptr) {} + ~PlayerConnected() override; + explicit PROTOBUF_CONSTEXPR PlayerConnected(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerConnected(const PlayerConnected& from); + PlayerConnected(PlayerConnected&& from) noexcept + : PlayerConnected() { + *this = ::std::move(from); + } + + inline PlayerConnected& operator=(const PlayerConnected& from) { + CopyFrom(from); + return *this; + } + inline PlayerConnected& operator=(PlayerConnected&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerConnected& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerConnected* internal_default_instance() { + return reinterpret_cast( + &_PlayerConnected_default_instance_); + } + static constexpr int kIndexInFileMessages = + 17; + + friend void swap(PlayerConnected& a, PlayerConnected& b) { + a.Swap(&b); + } + inline void Swap(PlayerConnected* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerConnected* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerConnected* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerConnected& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerConnected& from) { + PlayerConnected::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerConnected* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerConnected"; + } + protected: + explicit PlayerConnected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerConnected) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerDisconnected final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerDisconnected) */ { + public: + inline PlayerDisconnected() : PlayerDisconnected(nullptr) {} + ~PlayerDisconnected() override; + explicit PROTOBUF_CONSTEXPR PlayerDisconnected(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerDisconnected(const PlayerDisconnected& from); + PlayerDisconnected(PlayerDisconnected&& from) noexcept + : PlayerDisconnected() { + *this = ::std::move(from); + } + + inline PlayerDisconnected& operator=(const PlayerDisconnected& from) { + CopyFrom(from); + return *this; + } + inline PlayerDisconnected& operator=(PlayerDisconnected&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerDisconnected& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerDisconnected* internal_default_instance() { + return reinterpret_cast( + &_PlayerDisconnected_default_instance_); + } + static constexpr int kIndexInFileMessages = + 18; + + friend void swap(PlayerDisconnected& a, PlayerDisconnected& b) { + a.Swap(&b); + } + inline void Swap(PlayerDisconnected* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerDisconnected* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerDisconnected* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerDisconnected& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerDisconnected& from) { + PlayerDisconnected::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerDisconnected* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerDisconnected"; + } + protected: + explicit PlayerDisconnected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kCanReconnectFieldNumber = 4, + kIsAliveFieldNumber = 5, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // bool canReconnect = 4; + void clear_canreconnect(); + bool canreconnect() const; + void set_canreconnect(bool value); + private: + bool _internal_canreconnect() const; + void _internal_set_canreconnect(bool value); + public: + + // bool isAlive = 5; + void clear_isalive(); + bool isalive() const; + void set_isalive(bool value); + private: + bool _internal_isalive() const; + void _internal_set_isalive(bool value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerDisconnected) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + bool canreconnect_; + bool isalive_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerStatChanged final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerStatChanged) */ { + public: + inline PlayerStatChanged() : PlayerStatChanged(nullptr) {} + ~PlayerStatChanged() override; + explicit PROTOBUF_CONSTEXPR PlayerStatChanged(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerStatChanged(const PlayerStatChanged& from); + PlayerStatChanged(PlayerStatChanged&& from) noexcept + : PlayerStatChanged() { + *this = ::std::move(from); + } + + inline PlayerStatChanged& operator=(const PlayerStatChanged& from) { + CopyFrom(from); + return *this; + } + inline PlayerStatChanged& operator=(PlayerStatChanged&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerStatChanged& default_instance() { + return *internal_default_instance(); + } + enum NewValueCase { + kIntValue = 5, + kFloatValue = 6, + kBoolValue = 7, + NEWVALUE_NOT_SET = 0, + }; + + static inline const PlayerStatChanged* internal_default_instance() { + return reinterpret_cast( + &_PlayerStatChanged_default_instance_); + } + static constexpr int kIndexInFileMessages = + 19; + + friend void swap(PlayerStatChanged& a, PlayerStatChanged& b) { + a.Swap(&b); + } + inline void Swap(PlayerStatChanged* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerStatChanged* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerStatChanged* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerStatChanged& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerStatChanged& from) { + PlayerStatChanged::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerStatChanged* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerStatChanged"; + } + protected: + explicit PlayerStatChanged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kStatNameFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kIntValueFieldNumber = 5, + kFloatValueFieldNumber = 6, + kBoolValueFieldNumber = 7, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string statName = 4; + void clear_statname(); + const std::string& statname() const; + template + void set_statname(ArgT0&& arg0, ArgT... args); + std::string* mutable_statname(); + PROTOBUF_NODISCARD std::string* release_statname(); + void set_allocated_statname(std::string* statname); + private: + const std::string& _internal_statname() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_statname(const std::string& value); + std::string* _internal_mutable_statname(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // uint32 intValue = 5; + bool has_intvalue() const; + private: + bool _internal_has_intvalue() const; + public: + void clear_intvalue(); + uint32_t intvalue() const; + void set_intvalue(uint32_t value); + private: + uint32_t _internal_intvalue() const; + void _internal_set_intvalue(uint32_t value); + public: + + // float floatValue = 6; + bool has_floatvalue() const; + private: + bool _internal_has_floatvalue() const; + public: + void clear_floatvalue(); + float floatvalue() const; + void set_floatvalue(float value); + private: + float _internal_floatvalue() const; + void _internal_set_floatvalue(float value); + public: + + // bool boolValue = 7; + bool has_boolvalue() const; + private: + bool _internal_has_boolvalue() const; + public: + void clear_boolvalue(); + bool boolvalue() const; + void set_boolvalue(bool value); + private: + bool _internal_boolvalue() const; + void _internal_set_boolvalue(bool value); + public: + + void clear_newValue(); + NewValueCase newValue_case() const; + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerStatChanged) + private: + class _Internal; + void set_has_intvalue(); + void set_has_floatvalue(); + void set_has_boolvalue(); + + inline bool has_newValue() const; + inline void clear_has_newValue(); + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr statname_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + union NewValueUnion { + constexpr NewValueUnion() : _constinit_{} {} + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized _constinit_; + uint32_t intvalue_; + float floatvalue_; + bool boolvalue_; + } newValue_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + uint32_t _oneof_case_[1]; + + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerUpgradeTierChanged final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerUpgradeTierChanged) */ { + public: + inline PlayerUpgradeTierChanged() : PlayerUpgradeTierChanged(nullptr) {} + ~PlayerUpgradeTierChanged() override; + explicit PROTOBUF_CONSTEXPR PlayerUpgradeTierChanged(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerUpgradeTierChanged(const PlayerUpgradeTierChanged& from); + PlayerUpgradeTierChanged(PlayerUpgradeTierChanged&& from) noexcept + : PlayerUpgradeTierChanged() { + *this = ::std::move(from); + } + + inline PlayerUpgradeTierChanged& operator=(const PlayerUpgradeTierChanged& from) { + CopyFrom(from); + return *this; + } + inline PlayerUpgradeTierChanged& operator=(PlayerUpgradeTierChanged&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerUpgradeTierChanged& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerUpgradeTierChanged* internal_default_instance() { + return reinterpret_cast( + &_PlayerUpgradeTierChanged_default_instance_); + } + static constexpr int kIndexInFileMessages = + 20; + + friend void swap(PlayerUpgradeTierChanged& a, PlayerUpgradeTierChanged& b) { + a.Swap(&b); + } + inline void Swap(PlayerUpgradeTierChanged* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerUpgradeTierChanged* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerUpgradeTierChanged* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerUpgradeTierChanged& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerUpgradeTierChanged& from) { + PlayerUpgradeTierChanged::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerUpgradeTierChanged* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerUpgradeTierChanged"; + } + protected: + explicit PlayerUpgradeTierChanged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kLevelFieldNumber = 4, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 level = 4; + void clear_level(); + int32_t level() const; + void set_level(int32_t value); + private: + int32_t _internal_level() const; + void _internal_set_level(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerUpgradeTierChanged) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + int32_t level_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerDamaged final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerDamaged) */ { + public: + inline PlayerDamaged() : PlayerDamaged(nullptr) {} + ~PlayerDamaged() override; + explicit PROTOBUF_CONSTEXPR PlayerDamaged(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerDamaged(const PlayerDamaged& from); + PlayerDamaged(PlayerDamaged&& from) noexcept + : PlayerDamaged() { + *this = ::std::move(from); + } + + inline PlayerDamaged& operator=(const PlayerDamaged& from) { + CopyFrom(from); + return *this; + } + inline PlayerDamaged& operator=(PlayerDamaged&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerDamaged& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerDamaged* internal_default_instance() { + return reinterpret_cast( + &_PlayerDamaged_default_instance_); + } + static constexpr int kIndexInFileMessages = + 21; + + friend void swap(PlayerDamaged& a, PlayerDamaged& b) { + a.Swap(&b); + } + inline void Swap(PlayerDamaged* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerDamaged* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerDamaged* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerDamaged& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerDamaged& from) { + PlayerDamaged::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerDamaged* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerDamaged"; + } + protected: + explicit PlayerDamaged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kWeaponFieldNumber = 5, + kAttackerFieldNumber = 3, + kVictimFieldNumber = 4, + kTimestampFieldNumber = 1, + kDamageInflictedFieldNumber = 6, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string weapon = 5; + void clear_weapon(); + const std::string& weapon() const; + template + void set_weapon(ArgT0&& arg0, ArgT... args); + std::string* mutable_weapon(); + PROTOBUF_NODISCARD std::string* release_weapon(); + void set_allocated_weapon(std::string* weapon); + private: + const std::string& _internal_weapon() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_weapon(const std::string& value); + std::string* _internal_mutable_weapon(); + public: + + // .rtech.liveapi.Player attacker = 3; + bool has_attacker() const; + private: + bool _internal_has_attacker() const; + public: + void clear_attacker(); + const ::rtech::liveapi::Player& attacker() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_attacker(); + ::rtech::liveapi::Player* mutable_attacker(); + void set_allocated_attacker(::rtech::liveapi::Player* attacker); + private: + const ::rtech::liveapi::Player& _internal_attacker() const; + ::rtech::liveapi::Player* _internal_mutable_attacker(); + public: + void unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker); + ::rtech::liveapi::Player* unsafe_arena_release_attacker(); + + // .rtech.liveapi.Player victim = 4; + bool has_victim() const; + private: + bool _internal_has_victim() const; + public: + void clear_victim(); + const ::rtech::liveapi::Player& victim() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_victim(); + ::rtech::liveapi::Player* mutable_victim(); + void set_allocated_victim(::rtech::liveapi::Player* victim); + private: + const ::rtech::liveapi::Player& _internal_victim() const; + ::rtech::liveapi::Player* _internal_mutable_victim(); + public: + void unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim); + ::rtech::liveapi::Player* unsafe_arena_release_victim(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // uint32 damageInflicted = 6; + void clear_damageinflicted(); + uint32_t damageinflicted() const; + void set_damageinflicted(uint32_t value); + private: + uint32_t _internal_damageinflicted() const; + void _internal_set_damageinflicted(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerDamaged) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr weapon_; + ::rtech::liveapi::Player* attacker_; + ::rtech::liveapi::Player* victim_; + uint64_t timestamp_; + uint32_t damageinflicted_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerKilled final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerKilled) */ { + public: + inline PlayerKilled() : PlayerKilled(nullptr) {} + ~PlayerKilled() override; + explicit PROTOBUF_CONSTEXPR PlayerKilled(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerKilled(const PlayerKilled& from); + PlayerKilled(PlayerKilled&& from) noexcept + : PlayerKilled() { + *this = ::std::move(from); + } + + inline PlayerKilled& operator=(const PlayerKilled& from) { + CopyFrom(from); + return *this; + } + inline PlayerKilled& operator=(PlayerKilled&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerKilled& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerKilled* internal_default_instance() { + return reinterpret_cast( + &_PlayerKilled_default_instance_); + } + static constexpr int kIndexInFileMessages = + 22; + + friend void swap(PlayerKilled& a, PlayerKilled& b) { + a.Swap(&b); + } + inline void Swap(PlayerKilled* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerKilled* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerKilled* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerKilled& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerKilled& from) { + PlayerKilled::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerKilled* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerKilled"; + } + protected: + explicit PlayerKilled(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kWeaponFieldNumber = 6, + kAttackerFieldNumber = 3, + kVictimFieldNumber = 4, + kAwardedToFieldNumber = 5, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string weapon = 6; + void clear_weapon(); + const std::string& weapon() const; + template + void set_weapon(ArgT0&& arg0, ArgT... args); + std::string* mutable_weapon(); + PROTOBUF_NODISCARD std::string* release_weapon(); + void set_allocated_weapon(std::string* weapon); + private: + const std::string& _internal_weapon() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_weapon(const std::string& value); + std::string* _internal_mutable_weapon(); + public: + + // .rtech.liveapi.Player attacker = 3; + bool has_attacker() const; + private: + bool _internal_has_attacker() const; + public: + void clear_attacker(); + const ::rtech::liveapi::Player& attacker() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_attacker(); + ::rtech::liveapi::Player* mutable_attacker(); + void set_allocated_attacker(::rtech::liveapi::Player* attacker); + private: + const ::rtech::liveapi::Player& _internal_attacker() const; + ::rtech::liveapi::Player* _internal_mutable_attacker(); + public: + void unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker); + ::rtech::liveapi::Player* unsafe_arena_release_attacker(); + + // .rtech.liveapi.Player victim = 4; + bool has_victim() const; + private: + bool _internal_has_victim() const; + public: + void clear_victim(); + const ::rtech::liveapi::Player& victim() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_victim(); + ::rtech::liveapi::Player* mutable_victim(); + void set_allocated_victim(::rtech::liveapi::Player* victim); + private: + const ::rtech::liveapi::Player& _internal_victim() const; + ::rtech::liveapi::Player* _internal_mutable_victim(); + public: + void unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim); + ::rtech::liveapi::Player* unsafe_arena_release_victim(); + + // .rtech.liveapi.Player awardedTo = 5; + bool has_awardedto() const; + private: + bool _internal_has_awardedto() const; + public: + void clear_awardedto(); + const ::rtech::liveapi::Player& awardedto() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_awardedto(); + ::rtech::liveapi::Player* mutable_awardedto(); + void set_allocated_awardedto(::rtech::liveapi::Player* awardedto); + private: + const ::rtech::liveapi::Player& _internal_awardedto() const; + ::rtech::liveapi::Player* _internal_mutable_awardedto(); + public: + void unsafe_arena_set_allocated_awardedto( + ::rtech::liveapi::Player* awardedto); + ::rtech::liveapi::Player* unsafe_arena_release_awardedto(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerKilled) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr weapon_; + ::rtech::liveapi::Player* attacker_; + ::rtech::liveapi::Player* victim_; + ::rtech::liveapi::Player* awardedto_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerDowned final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerDowned) */ { + public: + inline PlayerDowned() : PlayerDowned(nullptr) {} + ~PlayerDowned() override; + explicit PROTOBUF_CONSTEXPR PlayerDowned(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerDowned(const PlayerDowned& from); + PlayerDowned(PlayerDowned&& from) noexcept + : PlayerDowned() { + *this = ::std::move(from); + } + + inline PlayerDowned& operator=(const PlayerDowned& from) { + CopyFrom(from); + return *this; + } + inline PlayerDowned& operator=(PlayerDowned&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerDowned& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerDowned* internal_default_instance() { + return reinterpret_cast( + &_PlayerDowned_default_instance_); + } + static constexpr int kIndexInFileMessages = + 23; + + friend void swap(PlayerDowned& a, PlayerDowned& b) { + a.Swap(&b); + } + inline void Swap(PlayerDowned* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerDowned* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerDowned* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerDowned& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerDowned& from) { + PlayerDowned::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerDowned* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerDowned"; + } + protected: + explicit PlayerDowned(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kWeaponFieldNumber = 5, + kAttackerFieldNumber = 3, + kVictimFieldNumber = 4, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string weapon = 5; + void clear_weapon(); + const std::string& weapon() const; + template + void set_weapon(ArgT0&& arg0, ArgT... args); + std::string* mutable_weapon(); + PROTOBUF_NODISCARD std::string* release_weapon(); + void set_allocated_weapon(std::string* weapon); + private: + const std::string& _internal_weapon() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_weapon(const std::string& value); + std::string* _internal_mutable_weapon(); + public: + + // .rtech.liveapi.Player attacker = 3; + bool has_attacker() const; + private: + bool _internal_has_attacker() const; + public: + void clear_attacker(); + const ::rtech::liveapi::Player& attacker() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_attacker(); + ::rtech::liveapi::Player* mutable_attacker(); + void set_allocated_attacker(::rtech::liveapi::Player* attacker); + private: + const ::rtech::liveapi::Player& _internal_attacker() const; + ::rtech::liveapi::Player* _internal_mutable_attacker(); + public: + void unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker); + ::rtech::liveapi::Player* unsafe_arena_release_attacker(); + + // .rtech.liveapi.Player victim = 4; + bool has_victim() const; + private: + bool _internal_has_victim() const; + public: + void clear_victim(); + const ::rtech::liveapi::Player& victim() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_victim(); + ::rtech::liveapi::Player* mutable_victim(); + void set_allocated_victim(::rtech::liveapi::Player* victim); + private: + const ::rtech::liveapi::Player& _internal_victim() const; + ::rtech::liveapi::Player* _internal_mutable_victim(); + public: + void unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim); + ::rtech::liveapi::Player* unsafe_arena_release_victim(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerDowned) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr weapon_; + ::rtech::liveapi::Player* attacker_; + ::rtech::liveapi::Player* victim_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerAssist final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerAssist) */ { + public: + inline PlayerAssist() : PlayerAssist(nullptr) {} + ~PlayerAssist() override; + explicit PROTOBUF_CONSTEXPR PlayerAssist(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerAssist(const PlayerAssist& from); + PlayerAssist(PlayerAssist&& from) noexcept + : PlayerAssist() { + *this = ::std::move(from); + } + + inline PlayerAssist& operator=(const PlayerAssist& from) { + CopyFrom(from); + return *this; + } + inline PlayerAssist& operator=(PlayerAssist&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerAssist& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerAssist* internal_default_instance() { + return reinterpret_cast( + &_PlayerAssist_default_instance_); + } + static constexpr int kIndexInFileMessages = + 24; + + friend void swap(PlayerAssist& a, PlayerAssist& b) { + a.Swap(&b); + } + inline void Swap(PlayerAssist* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerAssist* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerAssist* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerAssist& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerAssist& from) { + PlayerAssist::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerAssist* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerAssist"; + } + protected: + explicit PlayerAssist(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kWeaponFieldNumber = 5, + kAssistantFieldNumber = 3, + kVictimFieldNumber = 4, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string weapon = 5; + void clear_weapon(); + const std::string& weapon() const; + template + void set_weapon(ArgT0&& arg0, ArgT... args); + std::string* mutable_weapon(); + PROTOBUF_NODISCARD std::string* release_weapon(); + void set_allocated_weapon(std::string* weapon); + private: + const std::string& _internal_weapon() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_weapon(const std::string& value); + std::string* _internal_mutable_weapon(); + public: + + // .rtech.liveapi.Player assistant = 3; + bool has_assistant() const; + private: + bool _internal_has_assistant() const; + public: + void clear_assistant(); + const ::rtech::liveapi::Player& assistant() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_assistant(); + ::rtech::liveapi::Player* mutable_assistant(); + void set_allocated_assistant(::rtech::liveapi::Player* assistant); + private: + const ::rtech::liveapi::Player& _internal_assistant() const; + ::rtech::liveapi::Player* _internal_mutable_assistant(); + public: + void unsafe_arena_set_allocated_assistant( + ::rtech::liveapi::Player* assistant); + ::rtech::liveapi::Player* unsafe_arena_release_assistant(); + + // .rtech.liveapi.Player victim = 4; + bool has_victim() const; + private: + bool _internal_has_victim() const; + public: + void clear_victim(); + const ::rtech::liveapi::Player& victim() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_victim(); + ::rtech::liveapi::Player* mutable_victim(); + void set_allocated_victim(::rtech::liveapi::Player* victim); + private: + const ::rtech::liveapi::Player& _internal_victim() const; + ::rtech::liveapi::Player* _internal_mutable_victim(); + public: + void unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim); + ::rtech::liveapi::Player* unsafe_arena_release_victim(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerAssist) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr weapon_; + ::rtech::liveapi::Player* assistant_; + ::rtech::liveapi::Player* victim_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class SquadEliminated final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.SquadEliminated) */ { + public: + inline SquadEliminated() : SquadEliminated(nullptr) {} + ~SquadEliminated() override; + explicit PROTOBUF_CONSTEXPR SquadEliminated(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + SquadEliminated(const SquadEliminated& from); + SquadEliminated(SquadEliminated&& from) noexcept + : SquadEliminated() { + *this = ::std::move(from); + } + + inline SquadEliminated& operator=(const SquadEliminated& from) { + CopyFrom(from); + return *this; + } + inline SquadEliminated& operator=(SquadEliminated&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const SquadEliminated& default_instance() { + return *internal_default_instance(); + } + static inline const SquadEliminated* internal_default_instance() { + return reinterpret_cast( + &_SquadEliminated_default_instance_); + } + static constexpr int kIndexInFileMessages = + 25; + + friend void swap(SquadEliminated& a, SquadEliminated& b) { + a.Swap(&b); + } + inline void Swap(SquadEliminated* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(SquadEliminated* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + SquadEliminated* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const SquadEliminated& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const SquadEliminated& from) { + SquadEliminated::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(SquadEliminated* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.SquadEliminated"; + } + protected: + explicit SquadEliminated(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPlayersFieldNumber = 3, + kCategoryFieldNumber = 2, + kTimestampFieldNumber = 1, + }; + // repeated .rtech.liveapi.Player players = 3; + int players_size() const; + private: + int _internal_players_size() const; + public: + void clear_players(); + ::rtech::liveapi::Player* mutable_players(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* + mutable_players(); + private: + const ::rtech::liveapi::Player& _internal_players(int index) const; + ::rtech::liveapi::Player* _internal_add_players(); + public: + const ::rtech::liveapi::Player& players(int index) const; + ::rtech::liveapi::Player* add_players(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& + players() const; + + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.SquadEliminated) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player > players_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class GibraltarShieldAbsorbed final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.GibraltarShieldAbsorbed) */ { + public: + inline GibraltarShieldAbsorbed() : GibraltarShieldAbsorbed(nullptr) {} + ~GibraltarShieldAbsorbed() override; + explicit PROTOBUF_CONSTEXPR GibraltarShieldAbsorbed(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + GibraltarShieldAbsorbed(const GibraltarShieldAbsorbed& from); + GibraltarShieldAbsorbed(GibraltarShieldAbsorbed&& from) noexcept + : GibraltarShieldAbsorbed() { + *this = ::std::move(from); + } + + inline GibraltarShieldAbsorbed& operator=(const GibraltarShieldAbsorbed& from) { + CopyFrom(from); + return *this; + } + inline GibraltarShieldAbsorbed& operator=(GibraltarShieldAbsorbed&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const GibraltarShieldAbsorbed& default_instance() { + return *internal_default_instance(); + } + static inline const GibraltarShieldAbsorbed* internal_default_instance() { + return reinterpret_cast( + &_GibraltarShieldAbsorbed_default_instance_); + } + static constexpr int kIndexInFileMessages = + 26; + + friend void swap(GibraltarShieldAbsorbed& a, GibraltarShieldAbsorbed& b) { + a.Swap(&b); + } + inline void Swap(GibraltarShieldAbsorbed* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(GibraltarShieldAbsorbed* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + GibraltarShieldAbsorbed* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const GibraltarShieldAbsorbed& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const GibraltarShieldAbsorbed& from) { + GibraltarShieldAbsorbed::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(GibraltarShieldAbsorbed* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.GibraltarShieldAbsorbed"; + } + protected: + explicit GibraltarShieldAbsorbed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kAttackerFieldNumber = 3, + kVictimFieldNumber = 4, + kTimestampFieldNumber = 1, + kDamageInflictedFieldNumber = 6, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player attacker = 3; + bool has_attacker() const; + private: + bool _internal_has_attacker() const; + public: + void clear_attacker(); + const ::rtech::liveapi::Player& attacker() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_attacker(); + ::rtech::liveapi::Player* mutable_attacker(); + void set_allocated_attacker(::rtech::liveapi::Player* attacker); + private: + const ::rtech::liveapi::Player& _internal_attacker() const; + ::rtech::liveapi::Player* _internal_mutable_attacker(); + public: + void unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker); + ::rtech::liveapi::Player* unsafe_arena_release_attacker(); + + // .rtech.liveapi.Player victim = 4; + bool has_victim() const; + private: + bool _internal_has_victim() const; + public: + void clear_victim(); + const ::rtech::liveapi::Player& victim() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_victim(); + ::rtech::liveapi::Player* mutable_victim(); + void set_allocated_victim(::rtech::liveapi::Player* victim); + private: + const ::rtech::liveapi::Player& _internal_victim() const; + ::rtech::liveapi::Player* _internal_mutable_victim(); + public: + void unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim); + ::rtech::liveapi::Player* unsafe_arena_release_victim(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // uint32 damageInflicted = 6; + void clear_damageinflicted(); + uint32_t damageinflicted() const; + void set_damageinflicted(uint32_t value); + private: + uint32_t _internal_damageinflicted() const; + void _internal_set_damageinflicted(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.GibraltarShieldAbsorbed) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* attacker_; + ::rtech::liveapi::Player* victim_; + uint64_t timestamp_; + uint32_t damageinflicted_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class RevenantForgedShadowDamaged final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.RevenantForgedShadowDamaged) */ { + public: + inline RevenantForgedShadowDamaged() : RevenantForgedShadowDamaged(nullptr) {} + ~RevenantForgedShadowDamaged() override; + explicit PROTOBUF_CONSTEXPR RevenantForgedShadowDamaged(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + RevenantForgedShadowDamaged(const RevenantForgedShadowDamaged& from); + RevenantForgedShadowDamaged(RevenantForgedShadowDamaged&& from) noexcept + : RevenantForgedShadowDamaged() { + *this = ::std::move(from); + } + + inline RevenantForgedShadowDamaged& operator=(const RevenantForgedShadowDamaged& from) { + CopyFrom(from); + return *this; + } + inline RevenantForgedShadowDamaged& operator=(RevenantForgedShadowDamaged&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const RevenantForgedShadowDamaged& default_instance() { + return *internal_default_instance(); + } + static inline const RevenantForgedShadowDamaged* internal_default_instance() { + return reinterpret_cast( + &_RevenantForgedShadowDamaged_default_instance_); + } + static constexpr int kIndexInFileMessages = + 27; + + friend void swap(RevenantForgedShadowDamaged& a, RevenantForgedShadowDamaged& b) { + a.Swap(&b); + } + inline void Swap(RevenantForgedShadowDamaged* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(RevenantForgedShadowDamaged* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + RevenantForgedShadowDamaged* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const RevenantForgedShadowDamaged& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const RevenantForgedShadowDamaged& from) { + RevenantForgedShadowDamaged::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(RevenantForgedShadowDamaged* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.RevenantForgedShadowDamaged"; + } + protected: + explicit RevenantForgedShadowDamaged(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kAttackerFieldNumber = 3, + kVictimFieldNumber = 4, + kTimestampFieldNumber = 1, + kDamageInflictedFieldNumber = 6, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player attacker = 3; + bool has_attacker() const; + private: + bool _internal_has_attacker() const; + public: + void clear_attacker(); + const ::rtech::liveapi::Player& attacker() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_attacker(); + ::rtech::liveapi::Player* mutable_attacker(); + void set_allocated_attacker(::rtech::liveapi::Player* attacker); + private: + const ::rtech::liveapi::Player& _internal_attacker() const; + ::rtech::liveapi::Player* _internal_mutable_attacker(); + public: + void unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker); + ::rtech::liveapi::Player* unsafe_arena_release_attacker(); + + // .rtech.liveapi.Player victim = 4; + bool has_victim() const; + private: + bool _internal_has_victim() const; + public: + void clear_victim(); + const ::rtech::liveapi::Player& victim() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_victim(); + ::rtech::liveapi::Player* mutable_victim(); + void set_allocated_victim(::rtech::liveapi::Player* victim); + private: + const ::rtech::liveapi::Player& _internal_victim() const; + ::rtech::liveapi::Player* _internal_mutable_victim(); + public: + void unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim); + ::rtech::liveapi::Player* unsafe_arena_release_victim(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // uint32 damageInflicted = 6; + void clear_damageinflicted(); + uint32_t damageinflicted() const; + void set_damageinflicted(uint32_t value); + private: + uint32_t _internal_damageinflicted() const; + void _internal_set_damageinflicted(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.RevenantForgedShadowDamaged) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* attacker_; + ::rtech::liveapi::Player* victim_; + uint64_t timestamp_; + uint32_t damageinflicted_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerRespawnTeam final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerRespawnTeam) */ { + public: + inline PlayerRespawnTeam() : PlayerRespawnTeam(nullptr) {} + ~PlayerRespawnTeam() override; + explicit PROTOBUF_CONSTEXPR PlayerRespawnTeam(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerRespawnTeam(const PlayerRespawnTeam& from); + PlayerRespawnTeam(PlayerRespawnTeam&& from) noexcept + : PlayerRespawnTeam() { + *this = ::std::move(from); + } + + inline PlayerRespawnTeam& operator=(const PlayerRespawnTeam& from) { + CopyFrom(from); + return *this; + } + inline PlayerRespawnTeam& operator=(PlayerRespawnTeam&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerRespawnTeam& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerRespawnTeam* internal_default_instance() { + return reinterpret_cast( + &_PlayerRespawnTeam_default_instance_); + } + static constexpr int kIndexInFileMessages = + 28; + + friend void swap(PlayerRespawnTeam& a, PlayerRespawnTeam& b) { + a.Swap(&b); + } + inline void Swap(PlayerRespawnTeam* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerRespawnTeam* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerRespawnTeam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerRespawnTeam& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerRespawnTeam& from) { + PlayerRespawnTeam::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerRespawnTeam* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerRespawnTeam"; + } + protected: + explicit PlayerRespawnTeam(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kRespawnedFieldNumber = 4, + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // repeated .rtech.liveapi.Player respawned = 4; + int respawned_size() const; + private: + int _internal_respawned_size() const; + public: + void clear_respawned(); + ::rtech::liveapi::Player* mutable_respawned(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* + mutable_respawned(); + private: + const ::rtech::liveapi::Player& _internal_respawned(int index) const; + ::rtech::liveapi::Player* _internal_add_respawned(); + public: + const ::rtech::liveapi::Player& respawned(int index) const; + ::rtech::liveapi::Player* add_respawned(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& + respawned() const; + + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerRespawnTeam) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player > respawned_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerRevive final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerRevive) */ { + public: + inline PlayerRevive() : PlayerRevive(nullptr) {} + ~PlayerRevive() override; + explicit PROTOBUF_CONSTEXPR PlayerRevive(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerRevive(const PlayerRevive& from); + PlayerRevive(PlayerRevive&& from) noexcept + : PlayerRevive() { + *this = ::std::move(from); + } + + inline PlayerRevive& operator=(const PlayerRevive& from) { + CopyFrom(from); + return *this; + } + inline PlayerRevive& operator=(PlayerRevive&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerRevive& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerRevive* internal_default_instance() { + return reinterpret_cast( + &_PlayerRevive_default_instance_); + } + static constexpr int kIndexInFileMessages = + 29; + + friend void swap(PlayerRevive& a, PlayerRevive& b) { + a.Swap(&b); + } + inline void Swap(PlayerRevive* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerRevive* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerRevive* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerRevive& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerRevive& from) { + PlayerRevive::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerRevive* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerRevive"; + } + protected: + explicit PlayerRevive(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kRevivedFieldNumber = 4, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // .rtech.liveapi.Player revived = 4; + bool has_revived() const; + private: + bool _internal_has_revived() const; + public: + void clear_revived(); + const ::rtech::liveapi::Player& revived() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_revived(); + ::rtech::liveapi::Player* mutable_revived(); + void set_allocated_revived(::rtech::liveapi::Player* revived); + private: + const ::rtech::liveapi::Player& _internal_revived() const; + ::rtech::liveapi::Player* _internal_mutable_revived(); + public: + void unsafe_arena_set_allocated_revived( + ::rtech::liveapi::Player* revived); + ::rtech::liveapi::Player* unsafe_arena_release_revived(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerRevive) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + ::rtech::liveapi::Player* revived_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class ArenasItemSelected final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.ArenasItemSelected) */ { + public: + inline ArenasItemSelected() : ArenasItemSelected(nullptr) {} + ~ArenasItemSelected() override; + explicit PROTOBUF_CONSTEXPR ArenasItemSelected(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ArenasItemSelected(const ArenasItemSelected& from); + ArenasItemSelected(ArenasItemSelected&& from) noexcept + : ArenasItemSelected() { + *this = ::std::move(from); + } + + inline ArenasItemSelected& operator=(const ArenasItemSelected& from) { + CopyFrom(from); + return *this; + } + inline ArenasItemSelected& operator=(ArenasItemSelected&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ArenasItemSelected& default_instance() { + return *internal_default_instance(); + } + static inline const ArenasItemSelected* internal_default_instance() { + return reinterpret_cast( + &_ArenasItemSelected_default_instance_); + } + static constexpr int kIndexInFileMessages = + 30; + + friend void swap(ArenasItemSelected& a, ArenasItemSelected& b) { + a.Swap(&b); + } + inline void Swap(ArenasItemSelected* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ArenasItemSelected* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ArenasItemSelected* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ArenasItemSelected& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const ArenasItemSelected& from) { + ArenasItemSelected::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ArenasItemSelected* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.ArenasItemSelected"; + } + protected: + explicit ArenasItemSelected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kItemFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kQuantityFieldNumber = 5, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string item = 4; + void clear_item(); + const std::string& item() const; + template + void set_item(ArgT0&& arg0, ArgT... args); + std::string* mutable_item(); + PROTOBUF_NODISCARD std::string* release_item(); + void set_allocated_item(std::string* item); + private: + const std::string& _internal_item() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_item(const std::string& value); + std::string* _internal_mutable_item(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 quantity = 5; + void clear_quantity(); + int32_t quantity() const; + void set_quantity(int32_t value); + private: + int32_t _internal_quantity() const; + void _internal_set_quantity(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.ArenasItemSelected) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr item_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + int32_t quantity_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class ArenasItemDeselected final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.ArenasItemDeselected) */ { + public: + inline ArenasItemDeselected() : ArenasItemDeselected(nullptr) {} + ~ArenasItemDeselected() override; + explicit PROTOBUF_CONSTEXPR ArenasItemDeselected(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ArenasItemDeselected(const ArenasItemDeselected& from); + ArenasItemDeselected(ArenasItemDeselected&& from) noexcept + : ArenasItemDeselected() { + *this = ::std::move(from); + } + + inline ArenasItemDeselected& operator=(const ArenasItemDeselected& from) { + CopyFrom(from); + return *this; + } + inline ArenasItemDeselected& operator=(ArenasItemDeselected&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ArenasItemDeselected& default_instance() { + return *internal_default_instance(); + } + static inline const ArenasItemDeselected* internal_default_instance() { + return reinterpret_cast( + &_ArenasItemDeselected_default_instance_); + } + static constexpr int kIndexInFileMessages = + 31; + + friend void swap(ArenasItemDeselected& a, ArenasItemDeselected& b) { + a.Swap(&b); + } + inline void Swap(ArenasItemDeselected* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ArenasItemDeselected* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ArenasItemDeselected* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ArenasItemDeselected& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const ArenasItemDeselected& from) { + ArenasItemDeselected::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ArenasItemDeselected* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.ArenasItemDeselected"; + } + protected: + explicit ArenasItemDeselected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kItemFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kQuantityFieldNumber = 5, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string item = 4; + void clear_item(); + const std::string& item() const; + template + void set_item(ArgT0&& arg0, ArgT... args); + std::string* mutable_item(); + PROTOBUF_NODISCARD std::string* release_item(); + void set_allocated_item(std::string* item); + private: + const std::string& _internal_item() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_item(const std::string& value); + std::string* _internal_mutable_item(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 quantity = 5; + void clear_quantity(); + int32_t quantity() const; + void set_quantity(int32_t value); + private: + int32_t _internal_quantity() const; + void _internal_set_quantity(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.ArenasItemDeselected) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr item_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + int32_t quantity_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class InventoryPickUp final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.InventoryPickUp) */ { + public: + inline InventoryPickUp() : InventoryPickUp(nullptr) {} + ~InventoryPickUp() override; + explicit PROTOBUF_CONSTEXPR InventoryPickUp(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + InventoryPickUp(const InventoryPickUp& from); + InventoryPickUp(InventoryPickUp&& from) noexcept + : InventoryPickUp() { + *this = ::std::move(from); + } + + inline InventoryPickUp& operator=(const InventoryPickUp& from) { + CopyFrom(from); + return *this; + } + inline InventoryPickUp& operator=(InventoryPickUp&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const InventoryPickUp& default_instance() { + return *internal_default_instance(); + } + static inline const InventoryPickUp* internal_default_instance() { + return reinterpret_cast( + &_InventoryPickUp_default_instance_); + } + static constexpr int kIndexInFileMessages = + 32; + + friend void swap(InventoryPickUp& a, InventoryPickUp& b) { + a.Swap(&b); + } + inline void Swap(InventoryPickUp* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(InventoryPickUp* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + InventoryPickUp* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const InventoryPickUp& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const InventoryPickUp& from) { + InventoryPickUp::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(InventoryPickUp* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.InventoryPickUp"; + } + protected: + explicit InventoryPickUp(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kItemFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kQuantityFieldNumber = 5, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string item = 4; + void clear_item(); + const std::string& item() const; + template + void set_item(ArgT0&& arg0, ArgT... args); + std::string* mutable_item(); + PROTOBUF_NODISCARD std::string* release_item(); + void set_allocated_item(std::string* item); + private: + const std::string& _internal_item() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_item(const std::string& value); + std::string* _internal_mutable_item(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 quantity = 5; + void clear_quantity(); + int32_t quantity() const; + void set_quantity(int32_t value); + private: + int32_t _internal_quantity() const; + void _internal_set_quantity(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.InventoryPickUp) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr item_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + int32_t quantity_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class InventoryDrop final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.InventoryDrop) */ { + public: + inline InventoryDrop() : InventoryDrop(nullptr) {} + ~InventoryDrop() override; + explicit PROTOBUF_CONSTEXPR InventoryDrop(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + InventoryDrop(const InventoryDrop& from); + InventoryDrop(InventoryDrop&& from) noexcept + : InventoryDrop() { + *this = ::std::move(from); + } + + inline InventoryDrop& operator=(const InventoryDrop& from) { + CopyFrom(from); + return *this; + } + inline InventoryDrop& operator=(InventoryDrop&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const InventoryDrop& default_instance() { + return *internal_default_instance(); + } + static inline const InventoryDrop* internal_default_instance() { + return reinterpret_cast( + &_InventoryDrop_default_instance_); + } + static constexpr int kIndexInFileMessages = + 33; + + friend void swap(InventoryDrop& a, InventoryDrop& b) { + a.Swap(&b); + } + inline void Swap(InventoryDrop* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(InventoryDrop* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + InventoryDrop* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const InventoryDrop& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const InventoryDrop& from) { + InventoryDrop::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(InventoryDrop* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.InventoryDrop"; + } + protected: + explicit InventoryDrop(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kExtraDataFieldNumber = 6, + kCategoryFieldNumber = 2, + kItemFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kQuantityFieldNumber = 5, + }; + // repeated string extraData = 6; + int extradata_size() const; + private: + int _internal_extradata_size() const; + public: + void clear_extradata(); + const std::string& extradata(int index) const; + std::string* mutable_extradata(int index); + void set_extradata(int index, const std::string& value); + void set_extradata(int index, std::string&& value); + void set_extradata(int index, const char* value); + void set_extradata(int index, const char* value, size_t size); + std::string* add_extradata(); + void add_extradata(const std::string& value); + void add_extradata(std::string&& value); + void add_extradata(const char* value); + void add_extradata(const char* value, size_t size); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& extradata() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_extradata(); + private: + const std::string& _internal_extradata(int index) const; + std::string* _internal_add_extradata(); + public: + + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string item = 4; + void clear_item(); + const std::string& item() const; + template + void set_item(ArgT0&& arg0, ArgT... args); + std::string* mutable_item(); + PROTOBUF_NODISCARD std::string* release_item(); + void set_allocated_item(std::string* item); + private: + const std::string& _internal_item() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_item(const std::string& value); + std::string* _internal_mutable_item(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 quantity = 5; + void clear_quantity(); + int32_t quantity() const; + void set_quantity(int32_t value); + private: + int32_t _internal_quantity() const; + void _internal_set_quantity(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.InventoryDrop) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField extradata_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr item_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + int32_t quantity_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class InventoryUse final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.InventoryUse) */ { + public: + inline InventoryUse() : InventoryUse(nullptr) {} + ~InventoryUse() override; + explicit PROTOBUF_CONSTEXPR InventoryUse(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + InventoryUse(const InventoryUse& from); + InventoryUse(InventoryUse&& from) noexcept + : InventoryUse() { + *this = ::std::move(from); + } + + inline InventoryUse& operator=(const InventoryUse& from) { + CopyFrom(from); + return *this; + } + inline InventoryUse& operator=(InventoryUse&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const InventoryUse& default_instance() { + return *internal_default_instance(); + } + static inline const InventoryUse* internal_default_instance() { + return reinterpret_cast( + &_InventoryUse_default_instance_); + } + static constexpr int kIndexInFileMessages = + 34; + + friend void swap(InventoryUse& a, InventoryUse& b) { + a.Swap(&b); + } + inline void Swap(InventoryUse* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(InventoryUse* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + InventoryUse* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const InventoryUse& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const InventoryUse& from) { + InventoryUse::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(InventoryUse* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.InventoryUse"; + } + protected: + explicit InventoryUse(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kItemFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kQuantityFieldNumber = 5, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string item = 4; + void clear_item(); + const std::string& item() const; + template + void set_item(ArgT0&& arg0, ArgT... args); + std::string* mutable_item(); + PROTOBUF_NODISCARD std::string* release_item(); + void set_allocated_item(std::string* item); + private: + const std::string& _internal_item() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_item(const std::string& value); + std::string* _internal_mutable_item(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 quantity = 5; + void clear_quantity(); + int32_t quantity() const; + void set_quantity(int32_t value); + private: + int32_t _internal_quantity() const; + void _internal_set_quantity(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.InventoryUse) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr item_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + int32_t quantity_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class BannerCollected final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.BannerCollected) */ { + public: + inline BannerCollected() : BannerCollected(nullptr) {} + ~BannerCollected() override; + explicit PROTOBUF_CONSTEXPR BannerCollected(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + BannerCollected(const BannerCollected& from); + BannerCollected(BannerCollected&& from) noexcept + : BannerCollected() { + *this = ::std::move(from); + } + + inline BannerCollected& operator=(const BannerCollected& from) { + CopyFrom(from); + return *this; + } + inline BannerCollected& operator=(BannerCollected&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const BannerCollected& default_instance() { + return *internal_default_instance(); + } + static inline const BannerCollected* internal_default_instance() { + return reinterpret_cast( + &_BannerCollected_default_instance_); + } + static constexpr int kIndexInFileMessages = + 35; + + friend void swap(BannerCollected& a, BannerCollected& b) { + a.Swap(&b); + } + inline void Swap(BannerCollected* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(BannerCollected* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + BannerCollected* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const BannerCollected& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const BannerCollected& from) { + BannerCollected::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(BannerCollected* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.BannerCollected"; + } + protected: + explicit BannerCollected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kCollectedFieldNumber = 4, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // .rtech.liveapi.Player collected = 4; + bool has_collected() const; + private: + bool _internal_has_collected() const; + public: + void clear_collected(); + const ::rtech::liveapi::Player& collected() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_collected(); + ::rtech::liveapi::Player* mutable_collected(); + void set_allocated_collected(::rtech::liveapi::Player* collected); + private: + const ::rtech::liveapi::Player& _internal_collected() const; + ::rtech::liveapi::Player* _internal_mutable_collected(); + public: + void unsafe_arena_set_allocated_collected( + ::rtech::liveapi::Player* collected); + ::rtech::liveapi::Player* unsafe_arena_release_collected(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.BannerCollected) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + ::rtech::liveapi::Player* collected_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PlayerAbilityUsed final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PlayerAbilityUsed) */ { + public: + inline PlayerAbilityUsed() : PlayerAbilityUsed(nullptr) {} + ~PlayerAbilityUsed() override; + explicit PROTOBUF_CONSTEXPR PlayerAbilityUsed(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PlayerAbilityUsed(const PlayerAbilityUsed& from); + PlayerAbilityUsed(PlayerAbilityUsed&& from) noexcept + : PlayerAbilityUsed() { + *this = ::std::move(from); + } + + inline PlayerAbilityUsed& operator=(const PlayerAbilityUsed& from) { + CopyFrom(from); + return *this; + } + inline PlayerAbilityUsed& operator=(PlayerAbilityUsed&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PlayerAbilityUsed& default_instance() { + return *internal_default_instance(); + } + static inline const PlayerAbilityUsed* internal_default_instance() { + return reinterpret_cast( + &_PlayerAbilityUsed_default_instance_); + } + static constexpr int kIndexInFileMessages = + 36; + + friend void swap(PlayerAbilityUsed& a, PlayerAbilityUsed& b) { + a.Swap(&b); + } + inline void Swap(PlayerAbilityUsed* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PlayerAbilityUsed* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PlayerAbilityUsed* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PlayerAbilityUsed& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PlayerAbilityUsed& from) { + PlayerAbilityUsed::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PlayerAbilityUsed* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PlayerAbilityUsed"; + } + protected: + explicit PlayerAbilityUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kLinkedEntityFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string linkedEntity = 4; + void clear_linkedentity(); + const std::string& linkedentity() const; + template + void set_linkedentity(ArgT0&& arg0, ArgT... args); + std::string* mutable_linkedentity(); + PROTOBUF_NODISCARD std::string* release_linkedentity(); + void set_allocated_linkedentity(std::string* linkedentity); + private: + const std::string& _internal_linkedentity() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_linkedentity(const std::string& value); + std::string* _internal_mutable_linkedentity(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PlayerAbilityUsed) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr linkedentity_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class LegendUpgradeSelected final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.LegendUpgradeSelected) */ { + public: + inline LegendUpgradeSelected() : LegendUpgradeSelected(nullptr) {} + ~LegendUpgradeSelected() override; + explicit PROTOBUF_CONSTEXPR LegendUpgradeSelected(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + LegendUpgradeSelected(const LegendUpgradeSelected& from); + LegendUpgradeSelected(LegendUpgradeSelected&& from) noexcept + : LegendUpgradeSelected() { + *this = ::std::move(from); + } + + inline LegendUpgradeSelected& operator=(const LegendUpgradeSelected& from) { + CopyFrom(from); + return *this; + } + inline LegendUpgradeSelected& operator=(LegendUpgradeSelected&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const LegendUpgradeSelected& default_instance() { + return *internal_default_instance(); + } + static inline const LegendUpgradeSelected* internal_default_instance() { + return reinterpret_cast( + &_LegendUpgradeSelected_default_instance_); + } + static constexpr int kIndexInFileMessages = + 37; + + friend void swap(LegendUpgradeSelected& a, LegendUpgradeSelected& b) { + a.Swap(&b); + } + inline void Swap(LegendUpgradeSelected* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(LegendUpgradeSelected* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + LegendUpgradeSelected* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const LegendUpgradeSelected& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const LegendUpgradeSelected& from) { + LegendUpgradeSelected::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(LegendUpgradeSelected* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.LegendUpgradeSelected"; + } + protected: + explicit LegendUpgradeSelected(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kUpgradeNameFieldNumber = 4, + kUpgradeDescFieldNumber = 5, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kLevelFieldNumber = 6, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string upgradeName = 4; + void clear_upgradename(); + const std::string& upgradename() const; + template + void set_upgradename(ArgT0&& arg0, ArgT... args); + std::string* mutable_upgradename(); + PROTOBUF_NODISCARD std::string* release_upgradename(); + void set_allocated_upgradename(std::string* upgradename); + private: + const std::string& _internal_upgradename() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_upgradename(const std::string& value); + std::string* _internal_mutable_upgradename(); + public: + + // string upgradeDesc = 5; + void clear_upgradedesc(); + const std::string& upgradedesc() const; + template + void set_upgradedesc(ArgT0&& arg0, ArgT... args); + std::string* mutable_upgradedesc(); + PROTOBUF_NODISCARD std::string* release_upgradedesc(); + void set_allocated_upgradedesc(std::string* upgradedesc); + private: + const std::string& _internal_upgradedesc() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_upgradedesc(const std::string& value); + std::string* _internal_mutable_upgradedesc(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // int32 level = 6; + void clear_level(); + int32_t level() const; + void set_level(int32_t value); + private: + int32_t _internal_level() const; + void _internal_set_level(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.LegendUpgradeSelected) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr upgradename_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr upgradedesc_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + int32_t level_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class ZiplineUsed final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.ZiplineUsed) */ { + public: + inline ZiplineUsed() : ZiplineUsed(nullptr) {} + ~ZiplineUsed() override; + explicit PROTOBUF_CONSTEXPR ZiplineUsed(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ZiplineUsed(const ZiplineUsed& from); + ZiplineUsed(ZiplineUsed&& from) noexcept + : ZiplineUsed() { + *this = ::std::move(from); + } + + inline ZiplineUsed& operator=(const ZiplineUsed& from) { + CopyFrom(from); + return *this; + } + inline ZiplineUsed& operator=(ZiplineUsed&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ZiplineUsed& default_instance() { + return *internal_default_instance(); + } + static inline const ZiplineUsed* internal_default_instance() { + return reinterpret_cast( + &_ZiplineUsed_default_instance_); + } + static constexpr int kIndexInFileMessages = + 38; + + friend void swap(ZiplineUsed& a, ZiplineUsed& b) { + a.Swap(&b); + } + inline void Swap(ZiplineUsed* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ZiplineUsed* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ZiplineUsed* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ZiplineUsed& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const ZiplineUsed& from) { + ZiplineUsed::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ZiplineUsed* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.ZiplineUsed"; + } + protected: + explicit ZiplineUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kLinkedEntityFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string linkedEntity = 4; + void clear_linkedentity(); + const std::string& linkedentity() const; + template + void set_linkedentity(ArgT0&& arg0, ArgT... args); + std::string* mutable_linkedentity(); + PROTOBUF_NODISCARD std::string* release_linkedentity(); + void set_allocated_linkedentity(std::string* linkedentity); + private: + const std::string& _internal_linkedentity() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_linkedentity(const std::string& value); + std::string* _internal_mutable_linkedentity(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.ZiplineUsed) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr linkedentity_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class GrenadeThrown final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.GrenadeThrown) */ { + public: + inline GrenadeThrown() : GrenadeThrown(nullptr) {} + ~GrenadeThrown() override; + explicit PROTOBUF_CONSTEXPR GrenadeThrown(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + GrenadeThrown(const GrenadeThrown& from); + GrenadeThrown(GrenadeThrown&& from) noexcept + : GrenadeThrown() { + *this = ::std::move(from); + } + + inline GrenadeThrown& operator=(const GrenadeThrown& from) { + CopyFrom(from); + return *this; + } + inline GrenadeThrown& operator=(GrenadeThrown&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const GrenadeThrown& default_instance() { + return *internal_default_instance(); + } + static inline const GrenadeThrown* internal_default_instance() { + return reinterpret_cast( + &_GrenadeThrown_default_instance_); + } + static constexpr int kIndexInFileMessages = + 39; + + friend void swap(GrenadeThrown& a, GrenadeThrown& b) { + a.Swap(&b); + } + inline void Swap(GrenadeThrown* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(GrenadeThrown* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + GrenadeThrown* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const GrenadeThrown& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const GrenadeThrown& from) { + GrenadeThrown::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(GrenadeThrown* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.GrenadeThrown"; + } + protected: + explicit GrenadeThrown(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kLinkedEntityFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string linkedEntity = 4; + void clear_linkedentity(); + const std::string& linkedentity() const; + template + void set_linkedentity(ArgT0&& arg0, ArgT... args); + std::string* mutable_linkedentity(); + PROTOBUF_NODISCARD std::string* release_linkedentity(); + void set_allocated_linkedentity(std::string* linkedentity); + private: + const std::string& _internal_linkedentity() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_linkedentity(const std::string& value); + std::string* _internal_mutable_linkedentity(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.GrenadeThrown) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr linkedentity_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class BlackMarketAction final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.BlackMarketAction) */ { + public: + inline BlackMarketAction() : BlackMarketAction(nullptr) {} + ~BlackMarketAction() override; + explicit PROTOBUF_CONSTEXPR BlackMarketAction(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + BlackMarketAction(const BlackMarketAction& from); + BlackMarketAction(BlackMarketAction&& from) noexcept + : BlackMarketAction() { + *this = ::std::move(from); + } + + inline BlackMarketAction& operator=(const BlackMarketAction& from) { + CopyFrom(from); + return *this; + } + inline BlackMarketAction& operator=(BlackMarketAction&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const BlackMarketAction& default_instance() { + return *internal_default_instance(); + } + static inline const BlackMarketAction* internal_default_instance() { + return reinterpret_cast( + &_BlackMarketAction_default_instance_); + } + static constexpr int kIndexInFileMessages = + 40; + + friend void swap(BlackMarketAction& a, BlackMarketAction& b) { + a.Swap(&b); + } + inline void Swap(BlackMarketAction* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(BlackMarketAction* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + BlackMarketAction* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const BlackMarketAction& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const BlackMarketAction& from) { + BlackMarketAction::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(BlackMarketAction* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.BlackMarketAction"; + } + protected: + explicit BlackMarketAction(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kItemFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string item = 4; + void clear_item(); + const std::string& item() const; + template + void set_item(ArgT0&& arg0, ArgT... args); + std::string* mutable_item(); + PROTOBUF_NODISCARD std::string* release_item(); + void set_allocated_item(std::string* item); + private: + const std::string& _internal_item() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_item(const std::string& value); + std::string* _internal_mutable_item(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.BlackMarketAction) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr item_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class WraithPortal final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.WraithPortal) */ { + public: + inline WraithPortal() : WraithPortal(nullptr) {} + ~WraithPortal() override; + explicit PROTOBUF_CONSTEXPR WraithPortal(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + WraithPortal(const WraithPortal& from); + WraithPortal(WraithPortal&& from) noexcept + : WraithPortal() { + *this = ::std::move(from); + } + + inline WraithPortal& operator=(const WraithPortal& from) { + CopyFrom(from); + return *this; + } + inline WraithPortal& operator=(WraithPortal&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const WraithPortal& default_instance() { + return *internal_default_instance(); + } + static inline const WraithPortal* internal_default_instance() { + return reinterpret_cast( + &_WraithPortal_default_instance_); + } + static constexpr int kIndexInFileMessages = + 41; + + friend void swap(WraithPortal& a, WraithPortal& b) { + a.Swap(&b); + } + inline void Swap(WraithPortal* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(WraithPortal* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + WraithPortal* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const WraithPortal& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const WraithPortal& from) { + WraithPortal::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(WraithPortal* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.WraithPortal"; + } + protected: + explicit WraithPortal(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.WraithPortal) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class WarpGateUsed final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.WarpGateUsed) */ { + public: + inline WarpGateUsed() : WarpGateUsed(nullptr) {} + ~WarpGateUsed() override; + explicit PROTOBUF_CONSTEXPR WarpGateUsed(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + WarpGateUsed(const WarpGateUsed& from); + WarpGateUsed(WarpGateUsed&& from) noexcept + : WarpGateUsed() { + *this = ::std::move(from); + } + + inline WarpGateUsed& operator=(const WarpGateUsed& from) { + CopyFrom(from); + return *this; + } + inline WarpGateUsed& operator=(WarpGateUsed&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const WarpGateUsed& default_instance() { + return *internal_default_instance(); + } + static inline const WarpGateUsed* internal_default_instance() { + return reinterpret_cast( + &_WarpGateUsed_default_instance_); + } + static constexpr int kIndexInFileMessages = + 42; + + friend void swap(WarpGateUsed& a, WarpGateUsed& b) { + a.Swap(&b); + } + inline void Swap(WarpGateUsed* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(WarpGateUsed* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + WarpGateUsed* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const WarpGateUsed& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const WarpGateUsed& from) { + WarpGateUsed::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(WarpGateUsed* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.WarpGateUsed"; + } + protected: + explicit WarpGateUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.WarpGateUsed) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class AmmoUsed final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.AmmoUsed) */ { + public: + inline AmmoUsed() : AmmoUsed(nullptr) {} + ~AmmoUsed() override; + explicit PROTOBUF_CONSTEXPR AmmoUsed(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + AmmoUsed(const AmmoUsed& from); + AmmoUsed(AmmoUsed&& from) noexcept + : AmmoUsed() { + *this = ::std::move(from); + } + + inline AmmoUsed& operator=(const AmmoUsed& from) { + CopyFrom(from); + return *this; + } + inline AmmoUsed& operator=(AmmoUsed&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const AmmoUsed& default_instance() { + return *internal_default_instance(); + } + static inline const AmmoUsed* internal_default_instance() { + return reinterpret_cast( + &_AmmoUsed_default_instance_); + } + static constexpr int kIndexInFileMessages = + 43; + + friend void swap(AmmoUsed& a, AmmoUsed& b) { + a.Swap(&b); + } + inline void Swap(AmmoUsed* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(AmmoUsed* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + AmmoUsed* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const AmmoUsed& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const AmmoUsed& from) { + AmmoUsed::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(AmmoUsed* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.AmmoUsed"; + } + protected: + explicit AmmoUsed(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kAmmoTypeFieldNumber = 4, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + kAmountUsedFieldNumber = 5, + kOldAmmoCountFieldNumber = 6, + kNewAmmoCountFieldNumber = 7, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string ammoType = 4; + void clear_ammotype(); + const std::string& ammotype() const; + template + void set_ammotype(ArgT0&& arg0, ArgT... args); + std::string* mutable_ammotype(); + PROTOBUF_NODISCARD std::string* release_ammotype(); + void set_allocated_ammotype(std::string* ammotype); + private: + const std::string& _internal_ammotype() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_ammotype(const std::string& value); + std::string* _internal_mutable_ammotype(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // uint32 amountUsed = 5; + void clear_amountused(); + uint32_t amountused() const; + void set_amountused(uint32_t value); + private: + uint32_t _internal_amountused() const; + void _internal_set_amountused(uint32_t value); + public: + + // uint32 oldAmmoCount = 6; + void clear_oldammocount(); + uint32_t oldammocount() const; + void set_oldammocount(uint32_t value); + private: + uint32_t _internal_oldammocount() const; + void _internal_set_oldammocount(uint32_t value); + public: + + // uint32 newAmmoCount = 7; + void clear_newammocount(); + uint32_t newammocount() const; + void set_newammocount(uint32_t value); + private: + uint32_t _internal_newammocount() const; + void _internal_set_newammocount(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.AmmoUsed) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr ammotype_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + uint32_t amountused_; + uint32_t oldammocount_; + uint32_t newammocount_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class WeaponSwitched final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.WeaponSwitched) */ { + public: + inline WeaponSwitched() : WeaponSwitched(nullptr) {} + ~WeaponSwitched() override; + explicit PROTOBUF_CONSTEXPR WeaponSwitched(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + WeaponSwitched(const WeaponSwitched& from); + WeaponSwitched(WeaponSwitched&& from) noexcept + : WeaponSwitched() { + *this = ::std::move(from); + } + + inline WeaponSwitched& operator=(const WeaponSwitched& from) { + CopyFrom(from); + return *this; + } + inline WeaponSwitched& operator=(WeaponSwitched&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const WeaponSwitched& default_instance() { + return *internal_default_instance(); + } + static inline const WeaponSwitched* internal_default_instance() { + return reinterpret_cast( + &_WeaponSwitched_default_instance_); + } + static constexpr int kIndexInFileMessages = + 44; + + friend void swap(WeaponSwitched& a, WeaponSwitched& b) { + a.Swap(&b); + } + inline void Swap(WeaponSwitched* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(WeaponSwitched* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + WeaponSwitched* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const WeaponSwitched& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const WeaponSwitched& from) { + WeaponSwitched::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(WeaponSwitched* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.WeaponSwitched"; + } + protected: + explicit WeaponSwitched(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kOldWeaponFieldNumber = 4, + kNewWeaponFieldNumber = 5, + kPlayerFieldNumber = 3, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string oldWeapon = 4; + void clear_oldweapon(); + const std::string& oldweapon() const; + template + void set_oldweapon(ArgT0&& arg0, ArgT... args); + std::string* mutable_oldweapon(); + PROTOBUF_NODISCARD std::string* release_oldweapon(); + void set_allocated_oldweapon(std::string* oldweapon); + private: + const std::string& _internal_oldweapon() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_oldweapon(const std::string& value); + std::string* _internal_mutable_oldweapon(); + public: + + // string newWeapon = 5; + void clear_newweapon(); + const std::string& newweapon() const; + template + void set_newweapon(ArgT0&& arg0, ArgT... args); + std::string* mutable_newweapon(); + PROTOBUF_NODISCARD std::string* release_newweapon(); + void set_allocated_newweapon(std::string* newweapon); + private: + const std::string& _internal_newweapon() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_newweapon(const std::string& value); + std::string* _internal_mutable_newweapon(); + public: + + // .rtech.liveapi.Player player = 3; + bool has_player() const; + private: + bool _internal_has_player() const; + public: + void clear_player(); + const ::rtech::liveapi::Player& player() const; + PROTOBUF_NODISCARD ::rtech::liveapi::Player* release_player(); + ::rtech::liveapi::Player* mutable_player(); + void set_allocated_player(::rtech::liveapi::Player* player); + private: + const ::rtech::liveapi::Player& _internal_player() const; + ::rtech::liveapi::Player* _internal_mutable_player(); + public: + void unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player); + ::rtech::liveapi::Player* unsafe_arena_release_player(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.WeaponSwitched) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr oldweapon_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr newweapon_; + ::rtech::liveapi::Player* player_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomEvent final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomEvent) */ { + public: + inline CustomEvent() : CustomEvent(nullptr) {} + ~CustomEvent() override; + explicit PROTOBUF_CONSTEXPR CustomEvent(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomEvent(const CustomEvent& from); + CustomEvent(CustomEvent&& from) noexcept + : CustomEvent() { + *this = ::std::move(from); + } + + inline CustomEvent& operator=(const CustomEvent& from) { + CopyFrom(from); + return *this; + } + inline CustomEvent& operator=(CustomEvent&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomEvent& default_instance() { + return *internal_default_instance(); + } + static inline const CustomEvent* internal_default_instance() { + return reinterpret_cast( + &_CustomEvent_default_instance_); + } + static constexpr int kIndexInFileMessages = + 45; + + friend void swap(CustomEvent& a, CustomEvent& b) { + a.Swap(&b); + } + inline void Swap(CustomEvent* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomEvent* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomEvent* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomEvent& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomEvent& from) { + CustomEvent::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomEvent* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomEvent"; + } + protected: + explicit CustomEvent(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kCategoryFieldNumber = 2, + kNameFieldNumber = 3, + kDataFieldNumber = 4, + kTimestampFieldNumber = 1, + }; + // string category = 2; + void clear_category(); + const std::string& category() const; + template + void set_category(ArgT0&& arg0, ArgT... args); + std::string* mutable_category(); + PROTOBUF_NODISCARD std::string* release_category(); + void set_allocated_category(std::string* category); + private: + const std::string& _internal_category() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_category(const std::string& value); + std::string* _internal_mutable_category(); + public: + + // string name = 3; + void clear_name(); + const std::string& name() const; + template + void set_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_name(); + PROTOBUF_NODISCARD std::string* release_name(); + void set_allocated_name(std::string* name); + private: + const std::string& _internal_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value); + std::string* _internal_mutable_name(); + public: + + // .google.protobuf.Struct data = 4; + bool has_data() const; + private: + bool _internal_has_data() const; + public: + void clear_data(); + const ::PROTOBUF_NAMESPACE_ID::Struct& data() const; + PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Struct* release_data(); + ::PROTOBUF_NAMESPACE_ID::Struct* mutable_data(); + void set_allocated_data(::PROTOBUF_NAMESPACE_ID::Struct* data); + private: + const ::PROTOBUF_NAMESPACE_ID::Struct& _internal_data() const; + ::PROTOBUF_NAMESPACE_ID::Struct* _internal_mutable_data(); + public: + void unsafe_arena_set_allocated_data( + ::PROTOBUF_NAMESPACE_ID::Struct* data); + ::PROTOBUF_NAMESPACE_ID::Struct* unsafe_arena_release_data(); + + // uint64 timestamp = 1; + void clear_timestamp(); + uint64_t timestamp() const; + void set_timestamp(uint64_t value); + private: + uint64_t _internal_timestamp() const; + void _internal_set_timestamp(uint64_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomEvent) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr category_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; + ::PROTOBUF_NAMESPACE_ID::Struct* data_; + uint64_t timestamp_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class ChangeCamera final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.ChangeCamera) */ { + public: + inline ChangeCamera() : ChangeCamera(nullptr) {} + ~ChangeCamera() override; + explicit PROTOBUF_CONSTEXPR ChangeCamera(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ChangeCamera(const ChangeCamera& from); + ChangeCamera(ChangeCamera&& from) noexcept + : ChangeCamera() { + *this = ::std::move(from); + } + + inline ChangeCamera& operator=(const ChangeCamera& from) { + CopyFrom(from); + return *this; + } + inline ChangeCamera& operator=(ChangeCamera&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ChangeCamera& default_instance() { + return *internal_default_instance(); + } + enum TargetCase { + kPoi = 1, + kName = 2, + TARGET_NOT_SET = 0, + }; + + static inline const ChangeCamera* internal_default_instance() { + return reinterpret_cast( + &_ChangeCamera_default_instance_); + } + static constexpr int kIndexInFileMessages = + 46; + + friend void swap(ChangeCamera& a, ChangeCamera& b) { + a.Swap(&b); + } + inline void Swap(ChangeCamera* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ChangeCamera* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ChangeCamera* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ChangeCamera& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const ChangeCamera& from) { + ChangeCamera::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ChangeCamera* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.ChangeCamera"; + } + protected: + explicit ChangeCamera(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPoiFieldNumber = 1, + kNameFieldNumber = 2, + }; + // .rtech.liveapi.PlayerOfInterest poi = 1; + bool has_poi() const; + private: + bool _internal_has_poi() const; + public: + void clear_poi(); + ::rtech::liveapi::PlayerOfInterest poi() const; + void set_poi(::rtech::liveapi::PlayerOfInterest value); + private: + ::rtech::liveapi::PlayerOfInterest _internal_poi() const; + void _internal_set_poi(::rtech::liveapi::PlayerOfInterest value); + public: + + // string name = 2; + bool has_name() const; + private: + bool _internal_has_name() const; + public: + void clear_name(); + const std::string& name() const; + template + void set_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_name(); + PROTOBUF_NODISCARD std::string* release_name(); + void set_allocated_name(std::string* name); + private: + const std::string& _internal_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name(const std::string& value); + std::string* _internal_mutable_name(); + public: + + void clear_target(); + TargetCase target_case() const; + // @@protoc_insertion_point(class_scope:rtech.liveapi.ChangeCamera) + private: + class _Internal; + void set_has_poi(); + void set_has_name(); + + inline bool has_target() const; + inline void clear_has_target(); + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + union TargetUnion { + constexpr TargetUnion() : _constinit_{} {} + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized _constinit_; + int poi_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; + } target_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + uint32_t _oneof_case_[1]; + + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class PauseToggle final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.PauseToggle) */ { + public: + inline PauseToggle() : PauseToggle(nullptr) {} + ~PauseToggle() override; + explicit PROTOBUF_CONSTEXPR PauseToggle(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PauseToggle(const PauseToggle& from); + PauseToggle(PauseToggle&& from) noexcept + : PauseToggle() { + *this = ::std::move(from); + } + + inline PauseToggle& operator=(const PauseToggle& from) { + CopyFrom(from); + return *this; + } + inline PauseToggle& operator=(PauseToggle&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PauseToggle& default_instance() { + return *internal_default_instance(); + } + static inline const PauseToggle* internal_default_instance() { + return reinterpret_cast( + &_PauseToggle_default_instance_); + } + static constexpr int kIndexInFileMessages = + 47; + + friend void swap(PauseToggle& a, PauseToggle& b) { + a.Swap(&b); + } + inline void Swap(PauseToggle* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PauseToggle* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PauseToggle* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PauseToggle& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const PauseToggle& from) { + PauseToggle::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PauseToggle* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.PauseToggle"; + } + protected: + explicit PauseToggle(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPreTimerFieldNumber = 1, + }; + // float preTimer = 1; + void clear_pretimer(); + float pretimer() const; + void set_pretimer(float value); + private: + float _internal_pretimer() const; + void _internal_set_pretimer(float value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.PauseToggle) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + float pretimer_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_CreateLobby final : + public ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_CreateLobby) */ { + public: + inline CustomMatch_CreateLobby() : CustomMatch_CreateLobby(nullptr) {} + explicit PROTOBUF_CONSTEXPR CustomMatch_CreateLobby(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_CreateLobby(const CustomMatch_CreateLobby& from); + CustomMatch_CreateLobby(CustomMatch_CreateLobby&& from) noexcept + : CustomMatch_CreateLobby() { + *this = ::std::move(from); + } + + inline CustomMatch_CreateLobby& operator=(const CustomMatch_CreateLobby& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_CreateLobby& operator=(CustomMatch_CreateLobby&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_CreateLobby& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_CreateLobby* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_CreateLobby_default_instance_); + } + static constexpr int kIndexInFileMessages = + 48; + + friend void swap(CustomMatch_CreateLobby& a, CustomMatch_CreateLobby& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_CreateLobby* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_CreateLobby* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_CreateLobby* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyFrom; + inline void CopyFrom(const CustomMatch_CreateLobby& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl(*this, from); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeFrom; + void MergeFrom(const CustomMatch_CreateLobby& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl(*this, from); + } + public: + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_CreateLobby"; + } + protected: + explicit CustomMatch_CreateLobby(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_CreateLobby) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_JoinLobby final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_JoinLobby) */ { + public: + inline CustomMatch_JoinLobby() : CustomMatch_JoinLobby(nullptr) {} + ~CustomMatch_JoinLobby() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_JoinLobby(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_JoinLobby(const CustomMatch_JoinLobby& from); + CustomMatch_JoinLobby(CustomMatch_JoinLobby&& from) noexcept + : CustomMatch_JoinLobby() { + *this = ::std::move(from); + } + + inline CustomMatch_JoinLobby& operator=(const CustomMatch_JoinLobby& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_JoinLobby& operator=(CustomMatch_JoinLobby&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_JoinLobby& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_JoinLobby* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_JoinLobby_default_instance_); + } + static constexpr int kIndexInFileMessages = + 49; + + friend void swap(CustomMatch_JoinLobby& a, CustomMatch_JoinLobby& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_JoinLobby* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_JoinLobby* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_JoinLobby* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_JoinLobby& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_JoinLobby& from) { + CustomMatch_JoinLobby::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_JoinLobby* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_JoinLobby"; + } + protected: + explicit CustomMatch_JoinLobby(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kRoleTokenFieldNumber = 1, + }; + // string roleToken = 1; + void clear_roletoken(); + const std::string& roletoken() const; + template + void set_roletoken(ArgT0&& arg0, ArgT... args); + std::string* mutable_roletoken(); + PROTOBUF_NODISCARD std::string* release_roletoken(); + void set_allocated_roletoken(std::string* roletoken); + private: + const std::string& _internal_roletoken() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_roletoken(const std::string& value); + std::string* _internal_mutable_roletoken(); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_JoinLobby) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr roletoken_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_LeaveLobby final : + public ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_LeaveLobby) */ { + public: + inline CustomMatch_LeaveLobby() : CustomMatch_LeaveLobby(nullptr) {} + explicit PROTOBUF_CONSTEXPR CustomMatch_LeaveLobby(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_LeaveLobby(const CustomMatch_LeaveLobby& from); + CustomMatch_LeaveLobby(CustomMatch_LeaveLobby&& from) noexcept + : CustomMatch_LeaveLobby() { + *this = ::std::move(from); + } + + inline CustomMatch_LeaveLobby& operator=(const CustomMatch_LeaveLobby& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_LeaveLobby& operator=(CustomMatch_LeaveLobby&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_LeaveLobby& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_LeaveLobby* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_LeaveLobby_default_instance_); + } + static constexpr int kIndexInFileMessages = + 50; + + friend void swap(CustomMatch_LeaveLobby& a, CustomMatch_LeaveLobby& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_LeaveLobby* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_LeaveLobby* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_LeaveLobby* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyFrom; + inline void CopyFrom(const CustomMatch_LeaveLobby& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl(*this, from); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeFrom; + void MergeFrom(const CustomMatch_LeaveLobby& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl(*this, from); + } + public: + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_LeaveLobby"; + } + protected: + explicit CustomMatch_LeaveLobby(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_LeaveLobby) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_SetReady final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_SetReady) */ { + public: + inline CustomMatch_SetReady() : CustomMatch_SetReady(nullptr) {} + ~CustomMatch_SetReady() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_SetReady(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_SetReady(const CustomMatch_SetReady& from); + CustomMatch_SetReady(CustomMatch_SetReady&& from) noexcept + : CustomMatch_SetReady() { + *this = ::std::move(from); + } + + inline CustomMatch_SetReady& operator=(const CustomMatch_SetReady& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_SetReady& operator=(CustomMatch_SetReady&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_SetReady& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_SetReady* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_SetReady_default_instance_); + } + static constexpr int kIndexInFileMessages = + 51; + + friend void swap(CustomMatch_SetReady& a, CustomMatch_SetReady& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_SetReady* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_SetReady* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_SetReady* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_SetReady& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_SetReady& from) { + CustomMatch_SetReady::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_SetReady* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_SetReady"; + } + protected: + explicit CustomMatch_SetReady(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kIsReadyFieldNumber = 1, + }; + // bool isReady = 1; + void clear_isready(); + bool isready() const; + void set_isready(bool value); + private: + bool _internal_isready() const; + void _internal_set_isready(bool value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_SetReady) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + bool isready_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_GetLobbyPlayers final : + public ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_GetLobbyPlayers) */ { + public: + inline CustomMatch_GetLobbyPlayers() : CustomMatch_GetLobbyPlayers(nullptr) {} + explicit PROTOBUF_CONSTEXPR CustomMatch_GetLobbyPlayers(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_GetLobbyPlayers(const CustomMatch_GetLobbyPlayers& from); + CustomMatch_GetLobbyPlayers(CustomMatch_GetLobbyPlayers&& from) noexcept + : CustomMatch_GetLobbyPlayers() { + *this = ::std::move(from); + } + + inline CustomMatch_GetLobbyPlayers& operator=(const CustomMatch_GetLobbyPlayers& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_GetLobbyPlayers& operator=(CustomMatch_GetLobbyPlayers&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_GetLobbyPlayers& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_GetLobbyPlayers* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_GetLobbyPlayers_default_instance_); + } + static constexpr int kIndexInFileMessages = + 52; + + friend void swap(CustomMatch_GetLobbyPlayers& a, CustomMatch_GetLobbyPlayers& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_GetLobbyPlayers* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_GetLobbyPlayers* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_GetLobbyPlayers* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyFrom; + inline void CopyFrom(const CustomMatch_GetLobbyPlayers& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl(*this, from); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeFrom; + void MergeFrom(const CustomMatch_GetLobbyPlayers& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl(*this, from); + } + public: + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_GetLobbyPlayers"; + } + protected: + explicit CustomMatch_GetLobbyPlayers(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_GetLobbyPlayers) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_SetMatchmaking final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_SetMatchmaking) */ { + public: + inline CustomMatch_SetMatchmaking() : CustomMatch_SetMatchmaking(nullptr) {} + ~CustomMatch_SetMatchmaking() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_SetMatchmaking(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_SetMatchmaking(const CustomMatch_SetMatchmaking& from); + CustomMatch_SetMatchmaking(CustomMatch_SetMatchmaking&& from) noexcept + : CustomMatch_SetMatchmaking() { + *this = ::std::move(from); + } + + inline CustomMatch_SetMatchmaking& operator=(const CustomMatch_SetMatchmaking& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_SetMatchmaking& operator=(CustomMatch_SetMatchmaking&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_SetMatchmaking& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_SetMatchmaking* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_SetMatchmaking_default_instance_); + } + static constexpr int kIndexInFileMessages = + 53; + + friend void swap(CustomMatch_SetMatchmaking& a, CustomMatch_SetMatchmaking& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_SetMatchmaking* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_SetMatchmaking* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_SetMatchmaking* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_SetMatchmaking& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_SetMatchmaking& from) { + CustomMatch_SetMatchmaking::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_SetMatchmaking* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_SetMatchmaking"; + } + protected: + explicit CustomMatch_SetMatchmaking(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kEnabledFieldNumber = 1, + }; + // bool enabled = 1; + void clear_enabled(); + bool enabled() const; + void set_enabled(bool value); + private: + bool _internal_enabled() const; + void _internal_set_enabled(bool value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_SetMatchmaking) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + bool enabled_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_SetTeam final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_SetTeam) */ { + public: + inline CustomMatch_SetTeam() : CustomMatch_SetTeam(nullptr) {} + ~CustomMatch_SetTeam() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_SetTeam(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_SetTeam(const CustomMatch_SetTeam& from); + CustomMatch_SetTeam(CustomMatch_SetTeam&& from) noexcept + : CustomMatch_SetTeam() { + *this = ::std::move(from); + } + + inline CustomMatch_SetTeam& operator=(const CustomMatch_SetTeam& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_SetTeam& operator=(CustomMatch_SetTeam&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_SetTeam& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_SetTeam* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_SetTeam_default_instance_); + } + static constexpr int kIndexInFileMessages = + 54; + + friend void swap(CustomMatch_SetTeam& a, CustomMatch_SetTeam& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_SetTeam* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_SetTeam* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_SetTeam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_SetTeam& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_SetTeam& from) { + CustomMatch_SetTeam::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_SetTeam* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_SetTeam"; + } + protected: + explicit CustomMatch_SetTeam(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kTargetHardwareNameFieldNumber = 2, + kTargetNucleusHashFieldNumber = 3, + kTeamIdFieldNumber = 1, + }; + // string targetHardwareName = 2; + void clear_targethardwarename(); + const std::string& targethardwarename() const; + template + void set_targethardwarename(ArgT0&& arg0, ArgT... args); + std::string* mutable_targethardwarename(); + PROTOBUF_NODISCARD std::string* release_targethardwarename(); + void set_allocated_targethardwarename(std::string* targethardwarename); + private: + const std::string& _internal_targethardwarename() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_targethardwarename(const std::string& value); + std::string* _internal_mutable_targethardwarename(); + public: + + // string targetNucleusHash = 3; + void clear_targetnucleushash(); + const std::string& targetnucleushash() const; + template + void set_targetnucleushash(ArgT0&& arg0, ArgT... args); + std::string* mutable_targetnucleushash(); + PROTOBUF_NODISCARD std::string* release_targetnucleushash(); + void set_allocated_targetnucleushash(std::string* targetnucleushash); + private: + const std::string& _internal_targetnucleushash() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_targetnucleushash(const std::string& value); + std::string* _internal_mutable_targetnucleushash(); + public: + + // int32 teamId = 1; + void clear_teamid(); + int32_t teamid() const; + void set_teamid(int32_t value); + private: + int32_t _internal_teamid() const; + void _internal_set_teamid(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_SetTeam) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr targethardwarename_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr targetnucleushash_; + int32_t teamid_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_KickPlayer final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_KickPlayer) */ { + public: + inline CustomMatch_KickPlayer() : CustomMatch_KickPlayer(nullptr) {} + ~CustomMatch_KickPlayer() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_KickPlayer(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_KickPlayer(const CustomMatch_KickPlayer& from); + CustomMatch_KickPlayer(CustomMatch_KickPlayer&& from) noexcept + : CustomMatch_KickPlayer() { + *this = ::std::move(from); + } + + inline CustomMatch_KickPlayer& operator=(const CustomMatch_KickPlayer& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_KickPlayer& operator=(CustomMatch_KickPlayer&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_KickPlayer& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_KickPlayer* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_KickPlayer_default_instance_); + } + static constexpr int kIndexInFileMessages = + 55; + + friend void swap(CustomMatch_KickPlayer& a, CustomMatch_KickPlayer& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_KickPlayer* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_KickPlayer* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_KickPlayer* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_KickPlayer& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_KickPlayer& from) { + CustomMatch_KickPlayer::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_KickPlayer* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_KickPlayer"; + } + protected: + explicit CustomMatch_KickPlayer(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kTargetHardwareNameFieldNumber = 1, + kTargetNucleusHashFieldNumber = 2, + }; + // string targetHardwareName = 1; + void clear_targethardwarename(); + const std::string& targethardwarename() const; + template + void set_targethardwarename(ArgT0&& arg0, ArgT... args); + std::string* mutable_targethardwarename(); + PROTOBUF_NODISCARD std::string* release_targethardwarename(); + void set_allocated_targethardwarename(std::string* targethardwarename); + private: + const std::string& _internal_targethardwarename() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_targethardwarename(const std::string& value); + std::string* _internal_mutable_targethardwarename(); + public: + + // string targetNucleusHash = 2; + void clear_targetnucleushash(); + const std::string& targetnucleushash() const; + template + void set_targetnucleushash(ArgT0&& arg0, ArgT... args); + std::string* mutable_targetnucleushash(); + PROTOBUF_NODISCARD std::string* release_targetnucleushash(); + void set_allocated_targetnucleushash(std::string* targetnucleushash); + private: + const std::string& _internal_targetnucleushash() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_targetnucleushash(const std::string& value); + std::string* _internal_mutable_targetnucleushash(); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_KickPlayer) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr targethardwarename_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr targetnucleushash_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_SetSettings final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_SetSettings) */ { + public: + inline CustomMatch_SetSettings() : CustomMatch_SetSettings(nullptr) {} + ~CustomMatch_SetSettings() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_SetSettings(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_SetSettings(const CustomMatch_SetSettings& from); + CustomMatch_SetSettings(CustomMatch_SetSettings&& from) noexcept + : CustomMatch_SetSettings() { + *this = ::std::move(from); + } + + inline CustomMatch_SetSettings& operator=(const CustomMatch_SetSettings& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_SetSettings& operator=(CustomMatch_SetSettings&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_SetSettings& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_SetSettings* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_SetSettings_default_instance_); + } + static constexpr int kIndexInFileMessages = + 56; + + friend void swap(CustomMatch_SetSettings& a, CustomMatch_SetSettings& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_SetSettings* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_SetSettings* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_SetSettings* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_SetSettings& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_SetSettings& from) { + CustomMatch_SetSettings::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_SetSettings* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_SetSettings"; + } + protected: + explicit CustomMatch_SetSettings(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPlaylistNameFieldNumber = 1, + kAdminChatFieldNumber = 2, + kTeamRenameFieldNumber = 3, + kSelfAssignFieldNumber = 4, + kAimAssistFieldNumber = 5, + kAnonModeFieldNumber = 6, + }; + // string playlistName = 1; + void clear_playlistname(); + const std::string& playlistname() const; + template + void set_playlistname(ArgT0&& arg0, ArgT... args); + std::string* mutable_playlistname(); + PROTOBUF_NODISCARD std::string* release_playlistname(); + void set_allocated_playlistname(std::string* playlistname); + private: + const std::string& _internal_playlistname() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_playlistname(const std::string& value); + std::string* _internal_mutable_playlistname(); + public: + + // bool adminChat = 2; + void clear_adminchat(); + bool adminchat() const; + void set_adminchat(bool value); + private: + bool _internal_adminchat() const; + void _internal_set_adminchat(bool value); + public: + + // bool teamRename = 3; + void clear_teamrename(); + bool teamrename() const; + void set_teamrename(bool value); + private: + bool _internal_teamrename() const; + void _internal_set_teamrename(bool value); + public: + + // bool selfAssign = 4; + void clear_selfassign(); + bool selfassign() const; + void set_selfassign(bool value); + private: + bool _internal_selfassign() const; + void _internal_set_selfassign(bool value); + public: + + // bool aimAssist = 5; + void clear_aimassist(); + bool aimassist() const; + void set_aimassist(bool value); + private: + bool _internal_aimassist() const; + void _internal_set_aimassist(bool value); + public: + + // bool anonMode = 6; + void clear_anonmode(); + bool anonmode() const; + void set_anonmode(bool value); + private: + bool _internal_anonmode() const; + void _internal_set_anonmode(bool value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_SetSettings) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr playlistname_; + bool adminchat_; + bool teamrename_; + bool selfassign_; + bool aimassist_; + bool anonmode_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_GetSettings final : + public ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_GetSettings) */ { + public: + inline CustomMatch_GetSettings() : CustomMatch_GetSettings(nullptr) {} + explicit PROTOBUF_CONSTEXPR CustomMatch_GetSettings(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_GetSettings(const CustomMatch_GetSettings& from); + CustomMatch_GetSettings(CustomMatch_GetSettings&& from) noexcept + : CustomMatch_GetSettings() { + *this = ::std::move(from); + } + + inline CustomMatch_GetSettings& operator=(const CustomMatch_GetSettings& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_GetSettings& operator=(CustomMatch_GetSettings&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_GetSettings& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_GetSettings* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_GetSettings_default_instance_); + } + static constexpr int kIndexInFileMessages = + 57; + + friend void swap(CustomMatch_GetSettings& a, CustomMatch_GetSettings& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_GetSettings* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_GetSettings* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_GetSettings* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyFrom; + inline void CopyFrom(const CustomMatch_GetSettings& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::CopyImpl(*this, from); + } + using ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeFrom; + void MergeFrom(const CustomMatch_GetSettings& from) { + ::PROTOBUF_NAMESPACE_ID::internal::ZeroFieldsBase::MergeImpl(*this, from); + } + public: + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_GetSettings"; + } + protected: + explicit CustomMatch_GetSettings(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_GetSettings) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_SetTeamName final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_SetTeamName) */ { + public: + inline CustomMatch_SetTeamName() : CustomMatch_SetTeamName(nullptr) {} + ~CustomMatch_SetTeamName() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_SetTeamName(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_SetTeamName(const CustomMatch_SetTeamName& from); + CustomMatch_SetTeamName(CustomMatch_SetTeamName&& from) noexcept + : CustomMatch_SetTeamName() { + *this = ::std::move(from); + } + + inline CustomMatch_SetTeamName& operator=(const CustomMatch_SetTeamName& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_SetTeamName& operator=(CustomMatch_SetTeamName&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_SetTeamName& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_SetTeamName* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_SetTeamName_default_instance_); + } + static constexpr int kIndexInFileMessages = + 58; + + friend void swap(CustomMatch_SetTeamName& a, CustomMatch_SetTeamName& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_SetTeamName* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_SetTeamName* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_SetTeamName* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_SetTeamName& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_SetTeamName& from) { + CustomMatch_SetTeamName::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_SetTeamName* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_SetTeamName"; + } + protected: + explicit CustomMatch_SetTeamName(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kTeamNameFieldNumber = 2, + kTeamIdFieldNumber = 1, + }; + // string teamName = 2; + void clear_teamname(); + const std::string& teamname() const; + template + void set_teamname(ArgT0&& arg0, ArgT... args); + std::string* mutable_teamname(); + PROTOBUF_NODISCARD std::string* release_teamname(); + void set_allocated_teamname(std::string* teamname); + private: + const std::string& _internal_teamname() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_teamname(const std::string& value); + std::string* _internal_mutable_teamname(); + public: + + // int32 teamId = 1; + void clear_teamid(); + int32_t teamid() const; + void set_teamid(int32_t value); + private: + int32_t _internal_teamid() const; + void _internal_set_teamid(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_SetTeamName) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr teamname_; + int32_t teamid_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class CustomMatch_SendChat final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.CustomMatch_SendChat) */ { + public: + inline CustomMatch_SendChat() : CustomMatch_SendChat(nullptr) {} + ~CustomMatch_SendChat() override; + explicit PROTOBUF_CONSTEXPR CustomMatch_SendChat(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + CustomMatch_SendChat(const CustomMatch_SendChat& from); + CustomMatch_SendChat(CustomMatch_SendChat&& from) noexcept + : CustomMatch_SendChat() { + *this = ::std::move(from); + } + + inline CustomMatch_SendChat& operator=(const CustomMatch_SendChat& from) { + CopyFrom(from); + return *this; + } + inline CustomMatch_SendChat& operator=(CustomMatch_SendChat&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const CustomMatch_SendChat& default_instance() { + return *internal_default_instance(); + } + static inline const CustomMatch_SendChat* internal_default_instance() { + return reinterpret_cast( + &_CustomMatch_SendChat_default_instance_); + } + static constexpr int kIndexInFileMessages = + 59; + + friend void swap(CustomMatch_SendChat& a, CustomMatch_SendChat& b) { + a.Swap(&b); + } + inline void Swap(CustomMatch_SendChat* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(CustomMatch_SendChat* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + CustomMatch_SendChat* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const CustomMatch_SendChat& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const CustomMatch_SendChat& from) { + CustomMatch_SendChat::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(CustomMatch_SendChat* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.CustomMatch_SendChat"; + } + protected: + explicit CustomMatch_SendChat(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kTextFieldNumber = 1, + }; + // string text = 1; + void clear_text(); + const std::string& text() const; + template + void set_text(ArgT0&& arg0, ArgT... args); + std::string* mutable_text(); + PROTOBUF_NODISCARD std::string* release_text(); + void set_allocated_text(std::string* text); + private: + const std::string& _internal_text() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_text(const std::string& value); + std::string* _internal_mutable_text(); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.CustomMatch_SendChat) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr text_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class Request final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.Request) */ { + public: + inline Request() : Request(nullptr) {} + ~Request() override; + explicit PROTOBUF_CONSTEXPR Request(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + Request(const Request& from); + Request(Request&& from) noexcept + : Request() { + *this = ::std::move(from); + } + + inline Request& operator=(const Request& from) { + CopyFrom(from); + return *this; + } + inline Request& operator=(Request&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const Request& default_instance() { + return *internal_default_instance(); + } + enum ActionsCase { + kChangeCam = 4, + kPauseToggle = 5, + kCustomMatchCreateLobby = 10, + kCustomMatchJoinLobby = 11, + kCustomMatchLeaveLobby = 12, + kCustomMatchSetReady = 13, + kCustomMatchSetMatchmaking = 14, + kCustomMatchSetTeam = 15, + kCustomMatchKickPlayer = 16, + kCustomMatchSetSettings = 17, + kCustomMatchSendChat = 18, + kCustomMatchGetLobbyPlayers = 19, + kCustomMatchSetTeamName = 20, + kCustomMatchGetSettings = 21, + ACTIONS_NOT_SET = 0, + }; + + static inline const Request* internal_default_instance() { + return reinterpret_cast( + &_Request_default_instance_); + } + static constexpr int kIndexInFileMessages = + 60; + + friend void swap(Request& a, Request& b) { + a.Swap(&b); + } + inline void Swap(Request* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(Request* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + Request* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const Request& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const Request& from) { + Request::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(Request* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.Request"; + } + protected: + explicit Request(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPreSharedKeyFieldNumber = 2, + kWithAckFieldNumber = 1, + kChangeCamFieldNumber = 4, + kPauseToggleFieldNumber = 5, + kCustomMatchCreateLobbyFieldNumber = 10, + kCustomMatchJoinLobbyFieldNumber = 11, + kCustomMatchLeaveLobbyFieldNumber = 12, + kCustomMatchSetReadyFieldNumber = 13, + kCustomMatchSetMatchmakingFieldNumber = 14, + kCustomMatchSetTeamFieldNumber = 15, + kCustomMatchKickPlayerFieldNumber = 16, + kCustomMatchSetSettingsFieldNumber = 17, + kCustomMatchSendChatFieldNumber = 18, + kCustomMatchGetLobbyPlayersFieldNumber = 19, + kCustomMatchSetTeamNameFieldNumber = 20, + kCustomMatchGetSettingsFieldNumber = 21, + }; + // string preSharedKey = 2; + void clear_presharedkey(); + const std::string& presharedkey() const; + template + void set_presharedkey(ArgT0&& arg0, ArgT... args); + std::string* mutable_presharedkey(); + PROTOBUF_NODISCARD std::string* release_presharedkey(); + void set_allocated_presharedkey(std::string* presharedkey); + private: + const std::string& _internal_presharedkey() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_presharedkey(const std::string& value); + std::string* _internal_mutable_presharedkey(); + public: + + // bool withAck = 1; + void clear_withack(); + bool withack() const; + void set_withack(bool value); + private: + bool _internal_withack() const; + void _internal_set_withack(bool value); + public: + + // .rtech.liveapi.ChangeCamera changeCam = 4; + bool has_changecam() const; + private: + bool _internal_has_changecam() const; + public: + void clear_changecam(); + const ::rtech::liveapi::ChangeCamera& changecam() const; + PROTOBUF_NODISCARD ::rtech::liveapi::ChangeCamera* release_changecam(); + ::rtech::liveapi::ChangeCamera* mutable_changecam(); + void set_allocated_changecam(::rtech::liveapi::ChangeCamera* changecam); + private: + const ::rtech::liveapi::ChangeCamera& _internal_changecam() const; + ::rtech::liveapi::ChangeCamera* _internal_mutable_changecam(); + public: + void unsafe_arena_set_allocated_changecam( + ::rtech::liveapi::ChangeCamera* changecam); + ::rtech::liveapi::ChangeCamera* unsafe_arena_release_changecam(); + + // .rtech.liveapi.PauseToggle pauseToggle = 5; + bool has_pausetoggle() const; + private: + bool _internal_has_pausetoggle() const; + public: + void clear_pausetoggle(); + const ::rtech::liveapi::PauseToggle& pausetoggle() const; + PROTOBUF_NODISCARD ::rtech::liveapi::PauseToggle* release_pausetoggle(); + ::rtech::liveapi::PauseToggle* mutable_pausetoggle(); + void set_allocated_pausetoggle(::rtech::liveapi::PauseToggle* pausetoggle); + private: + const ::rtech::liveapi::PauseToggle& _internal_pausetoggle() const; + ::rtech::liveapi::PauseToggle* _internal_mutable_pausetoggle(); + public: + void unsafe_arena_set_allocated_pausetoggle( + ::rtech::liveapi::PauseToggle* pausetoggle); + ::rtech::liveapi::PauseToggle* unsafe_arena_release_pausetoggle(); + + // .rtech.liveapi.CustomMatch_CreateLobby customMatch_CreateLobby = 10; + bool has_custommatch_createlobby() const; + private: + bool _internal_has_custommatch_createlobby() const; + public: + void clear_custommatch_createlobby(); + const ::rtech::liveapi::CustomMatch_CreateLobby& custommatch_createlobby() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_CreateLobby* release_custommatch_createlobby(); + ::rtech::liveapi::CustomMatch_CreateLobby* mutable_custommatch_createlobby(); + void set_allocated_custommatch_createlobby(::rtech::liveapi::CustomMatch_CreateLobby* custommatch_createlobby); + private: + const ::rtech::liveapi::CustomMatch_CreateLobby& _internal_custommatch_createlobby() const; + ::rtech::liveapi::CustomMatch_CreateLobby* _internal_mutable_custommatch_createlobby(); + public: + void unsafe_arena_set_allocated_custommatch_createlobby( + ::rtech::liveapi::CustomMatch_CreateLobby* custommatch_createlobby); + ::rtech::liveapi::CustomMatch_CreateLobby* unsafe_arena_release_custommatch_createlobby(); + + // .rtech.liveapi.CustomMatch_JoinLobby customMatch_JoinLobby = 11; + bool has_custommatch_joinlobby() const; + private: + bool _internal_has_custommatch_joinlobby() const; + public: + void clear_custommatch_joinlobby(); + const ::rtech::liveapi::CustomMatch_JoinLobby& custommatch_joinlobby() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_JoinLobby* release_custommatch_joinlobby(); + ::rtech::liveapi::CustomMatch_JoinLobby* mutable_custommatch_joinlobby(); + void set_allocated_custommatch_joinlobby(::rtech::liveapi::CustomMatch_JoinLobby* custommatch_joinlobby); + private: + const ::rtech::liveapi::CustomMatch_JoinLobby& _internal_custommatch_joinlobby() const; + ::rtech::liveapi::CustomMatch_JoinLobby* _internal_mutable_custommatch_joinlobby(); + public: + void unsafe_arena_set_allocated_custommatch_joinlobby( + ::rtech::liveapi::CustomMatch_JoinLobby* custommatch_joinlobby); + ::rtech::liveapi::CustomMatch_JoinLobby* unsafe_arena_release_custommatch_joinlobby(); + + // .rtech.liveapi.CustomMatch_LeaveLobby customMatch_LeaveLobby = 12; + bool has_custommatch_leavelobby() const; + private: + bool _internal_has_custommatch_leavelobby() const; + public: + void clear_custommatch_leavelobby(); + const ::rtech::liveapi::CustomMatch_LeaveLobby& custommatch_leavelobby() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_LeaveLobby* release_custommatch_leavelobby(); + ::rtech::liveapi::CustomMatch_LeaveLobby* mutable_custommatch_leavelobby(); + void set_allocated_custommatch_leavelobby(::rtech::liveapi::CustomMatch_LeaveLobby* custommatch_leavelobby); + private: + const ::rtech::liveapi::CustomMatch_LeaveLobby& _internal_custommatch_leavelobby() const; + ::rtech::liveapi::CustomMatch_LeaveLobby* _internal_mutable_custommatch_leavelobby(); + public: + void unsafe_arena_set_allocated_custommatch_leavelobby( + ::rtech::liveapi::CustomMatch_LeaveLobby* custommatch_leavelobby); + ::rtech::liveapi::CustomMatch_LeaveLobby* unsafe_arena_release_custommatch_leavelobby(); + + // .rtech.liveapi.CustomMatch_SetReady customMatch_SetReady = 13; + bool has_custommatch_setready() const; + private: + bool _internal_has_custommatch_setready() const; + public: + void clear_custommatch_setready(); + const ::rtech::liveapi::CustomMatch_SetReady& custommatch_setready() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_SetReady* release_custommatch_setready(); + ::rtech::liveapi::CustomMatch_SetReady* mutable_custommatch_setready(); + void set_allocated_custommatch_setready(::rtech::liveapi::CustomMatch_SetReady* custommatch_setready); + private: + const ::rtech::liveapi::CustomMatch_SetReady& _internal_custommatch_setready() const; + ::rtech::liveapi::CustomMatch_SetReady* _internal_mutable_custommatch_setready(); + public: + void unsafe_arena_set_allocated_custommatch_setready( + ::rtech::liveapi::CustomMatch_SetReady* custommatch_setready); + ::rtech::liveapi::CustomMatch_SetReady* unsafe_arena_release_custommatch_setready(); + + // .rtech.liveapi.CustomMatch_SetMatchmaking customMatch_SetMatchmaking = 14; + bool has_custommatch_setmatchmaking() const; + private: + bool _internal_has_custommatch_setmatchmaking() const; + public: + void clear_custommatch_setmatchmaking(); + const ::rtech::liveapi::CustomMatch_SetMatchmaking& custommatch_setmatchmaking() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_SetMatchmaking* release_custommatch_setmatchmaking(); + ::rtech::liveapi::CustomMatch_SetMatchmaking* mutable_custommatch_setmatchmaking(); + void set_allocated_custommatch_setmatchmaking(::rtech::liveapi::CustomMatch_SetMatchmaking* custommatch_setmatchmaking); + private: + const ::rtech::liveapi::CustomMatch_SetMatchmaking& _internal_custommatch_setmatchmaking() const; + ::rtech::liveapi::CustomMatch_SetMatchmaking* _internal_mutable_custommatch_setmatchmaking(); + public: + void unsafe_arena_set_allocated_custommatch_setmatchmaking( + ::rtech::liveapi::CustomMatch_SetMatchmaking* custommatch_setmatchmaking); + ::rtech::liveapi::CustomMatch_SetMatchmaking* unsafe_arena_release_custommatch_setmatchmaking(); + + // .rtech.liveapi.CustomMatch_SetTeam customMatch_SetTeam = 15; + bool has_custommatch_setteam() const; + private: + bool _internal_has_custommatch_setteam() const; + public: + void clear_custommatch_setteam(); + const ::rtech::liveapi::CustomMatch_SetTeam& custommatch_setteam() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_SetTeam* release_custommatch_setteam(); + ::rtech::liveapi::CustomMatch_SetTeam* mutable_custommatch_setteam(); + void set_allocated_custommatch_setteam(::rtech::liveapi::CustomMatch_SetTeam* custommatch_setteam); + private: + const ::rtech::liveapi::CustomMatch_SetTeam& _internal_custommatch_setteam() const; + ::rtech::liveapi::CustomMatch_SetTeam* _internal_mutable_custommatch_setteam(); + public: + void unsafe_arena_set_allocated_custommatch_setteam( + ::rtech::liveapi::CustomMatch_SetTeam* custommatch_setteam); + ::rtech::liveapi::CustomMatch_SetTeam* unsafe_arena_release_custommatch_setteam(); + + // .rtech.liveapi.CustomMatch_KickPlayer customMatch_KickPlayer = 16; + bool has_custommatch_kickplayer() const; + private: + bool _internal_has_custommatch_kickplayer() const; + public: + void clear_custommatch_kickplayer(); + const ::rtech::liveapi::CustomMatch_KickPlayer& custommatch_kickplayer() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_KickPlayer* release_custommatch_kickplayer(); + ::rtech::liveapi::CustomMatch_KickPlayer* mutable_custommatch_kickplayer(); + void set_allocated_custommatch_kickplayer(::rtech::liveapi::CustomMatch_KickPlayer* custommatch_kickplayer); + private: + const ::rtech::liveapi::CustomMatch_KickPlayer& _internal_custommatch_kickplayer() const; + ::rtech::liveapi::CustomMatch_KickPlayer* _internal_mutable_custommatch_kickplayer(); + public: + void unsafe_arena_set_allocated_custommatch_kickplayer( + ::rtech::liveapi::CustomMatch_KickPlayer* custommatch_kickplayer); + ::rtech::liveapi::CustomMatch_KickPlayer* unsafe_arena_release_custommatch_kickplayer(); + + // .rtech.liveapi.CustomMatch_SetSettings customMatch_SetSettings = 17; + bool has_custommatch_setsettings() const; + private: + bool _internal_has_custommatch_setsettings() const; + public: + void clear_custommatch_setsettings(); + const ::rtech::liveapi::CustomMatch_SetSettings& custommatch_setsettings() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_SetSettings* release_custommatch_setsettings(); + ::rtech::liveapi::CustomMatch_SetSettings* mutable_custommatch_setsettings(); + void set_allocated_custommatch_setsettings(::rtech::liveapi::CustomMatch_SetSettings* custommatch_setsettings); + private: + const ::rtech::liveapi::CustomMatch_SetSettings& _internal_custommatch_setsettings() const; + ::rtech::liveapi::CustomMatch_SetSettings* _internal_mutable_custommatch_setsettings(); + public: + void unsafe_arena_set_allocated_custommatch_setsettings( + ::rtech::liveapi::CustomMatch_SetSettings* custommatch_setsettings); + ::rtech::liveapi::CustomMatch_SetSettings* unsafe_arena_release_custommatch_setsettings(); + + // .rtech.liveapi.CustomMatch_SendChat customMatch_SendChat = 18; + bool has_custommatch_sendchat() const; + private: + bool _internal_has_custommatch_sendchat() const; + public: + void clear_custommatch_sendchat(); + const ::rtech::liveapi::CustomMatch_SendChat& custommatch_sendchat() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_SendChat* release_custommatch_sendchat(); + ::rtech::liveapi::CustomMatch_SendChat* mutable_custommatch_sendchat(); + void set_allocated_custommatch_sendchat(::rtech::liveapi::CustomMatch_SendChat* custommatch_sendchat); + private: + const ::rtech::liveapi::CustomMatch_SendChat& _internal_custommatch_sendchat() const; + ::rtech::liveapi::CustomMatch_SendChat* _internal_mutable_custommatch_sendchat(); + public: + void unsafe_arena_set_allocated_custommatch_sendchat( + ::rtech::liveapi::CustomMatch_SendChat* custommatch_sendchat); + ::rtech::liveapi::CustomMatch_SendChat* unsafe_arena_release_custommatch_sendchat(); + + // .rtech.liveapi.CustomMatch_GetLobbyPlayers customMatch_GetLobbyPlayers = 19; + bool has_custommatch_getlobbyplayers() const; + private: + bool _internal_has_custommatch_getlobbyplayers() const; + public: + void clear_custommatch_getlobbyplayers(); + const ::rtech::liveapi::CustomMatch_GetLobbyPlayers& custommatch_getlobbyplayers() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_GetLobbyPlayers* release_custommatch_getlobbyplayers(); + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* mutable_custommatch_getlobbyplayers(); + void set_allocated_custommatch_getlobbyplayers(::rtech::liveapi::CustomMatch_GetLobbyPlayers* custommatch_getlobbyplayers); + private: + const ::rtech::liveapi::CustomMatch_GetLobbyPlayers& _internal_custommatch_getlobbyplayers() const; + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* _internal_mutable_custommatch_getlobbyplayers(); + public: + void unsafe_arena_set_allocated_custommatch_getlobbyplayers( + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* custommatch_getlobbyplayers); + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* unsafe_arena_release_custommatch_getlobbyplayers(); + + // .rtech.liveapi.CustomMatch_SetTeamName customMatch_SetTeamName = 20; + bool has_custommatch_setteamname() const; + private: + bool _internal_has_custommatch_setteamname() const; + public: + void clear_custommatch_setteamname(); + const ::rtech::liveapi::CustomMatch_SetTeamName& custommatch_setteamname() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_SetTeamName* release_custommatch_setteamname(); + ::rtech::liveapi::CustomMatch_SetTeamName* mutable_custommatch_setteamname(); + void set_allocated_custommatch_setteamname(::rtech::liveapi::CustomMatch_SetTeamName* custommatch_setteamname); + private: + const ::rtech::liveapi::CustomMatch_SetTeamName& _internal_custommatch_setteamname() const; + ::rtech::liveapi::CustomMatch_SetTeamName* _internal_mutable_custommatch_setteamname(); + public: + void unsafe_arena_set_allocated_custommatch_setteamname( + ::rtech::liveapi::CustomMatch_SetTeamName* custommatch_setteamname); + ::rtech::liveapi::CustomMatch_SetTeamName* unsafe_arena_release_custommatch_setteamname(); + + // .rtech.liveapi.CustomMatch_GetSettings customMatch_GetSettings = 21; + bool has_custommatch_getsettings() const; + private: + bool _internal_has_custommatch_getsettings() const; + public: + void clear_custommatch_getsettings(); + const ::rtech::liveapi::CustomMatch_GetSettings& custommatch_getsettings() const; + PROTOBUF_NODISCARD ::rtech::liveapi::CustomMatch_GetSettings* release_custommatch_getsettings(); + ::rtech::liveapi::CustomMatch_GetSettings* mutable_custommatch_getsettings(); + void set_allocated_custommatch_getsettings(::rtech::liveapi::CustomMatch_GetSettings* custommatch_getsettings); + private: + const ::rtech::liveapi::CustomMatch_GetSettings& _internal_custommatch_getsettings() const; + ::rtech::liveapi::CustomMatch_GetSettings* _internal_mutable_custommatch_getsettings(); + public: + void unsafe_arena_set_allocated_custommatch_getsettings( + ::rtech::liveapi::CustomMatch_GetSettings* custommatch_getsettings); + ::rtech::liveapi::CustomMatch_GetSettings* unsafe_arena_release_custommatch_getsettings(); + + void clear_actions(); + ActionsCase actions_case() const; + // @@protoc_insertion_point(class_scope:rtech.liveapi.Request) + private: + class _Internal; + void set_has_changecam(); + void set_has_pausetoggle(); + void set_has_custommatch_createlobby(); + void set_has_custommatch_joinlobby(); + void set_has_custommatch_leavelobby(); + void set_has_custommatch_setready(); + void set_has_custommatch_setmatchmaking(); + void set_has_custommatch_setteam(); + void set_has_custommatch_kickplayer(); + void set_has_custommatch_setsettings(); + void set_has_custommatch_sendchat(); + void set_has_custommatch_getlobbyplayers(); + void set_has_custommatch_setteamname(); + void set_has_custommatch_getsettings(); + + inline bool has_actions() const; + inline void clear_has_actions(); + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr presharedkey_; + bool withack_; + union ActionsUnion { + constexpr ActionsUnion() : _constinit_{} {} + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized _constinit_; + ::rtech::liveapi::ChangeCamera* changecam_; + ::rtech::liveapi::PauseToggle* pausetoggle_; + ::rtech::liveapi::CustomMatch_CreateLobby* custommatch_createlobby_; + ::rtech::liveapi::CustomMatch_JoinLobby* custommatch_joinlobby_; + ::rtech::liveapi::CustomMatch_LeaveLobby* custommatch_leavelobby_; + ::rtech::liveapi::CustomMatch_SetReady* custommatch_setready_; + ::rtech::liveapi::CustomMatch_SetMatchmaking* custommatch_setmatchmaking_; + ::rtech::liveapi::CustomMatch_SetTeam* custommatch_setteam_; + ::rtech::liveapi::CustomMatch_KickPlayer* custommatch_kickplayer_; + ::rtech::liveapi::CustomMatch_SetSettings* custommatch_setsettings_; + ::rtech::liveapi::CustomMatch_SendChat* custommatch_sendchat_; + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* custommatch_getlobbyplayers_; + ::rtech::liveapi::CustomMatch_SetTeamName* custommatch_setteamname_; + ::rtech::liveapi::CustomMatch_GetSettings* custommatch_getsettings_; + } actions_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + uint32_t _oneof_case_[1]; + + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class RequestStatus final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.RequestStatus) */ { + public: + inline RequestStatus() : RequestStatus(nullptr) {} + ~RequestStatus() override; + explicit PROTOBUF_CONSTEXPR RequestStatus(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + RequestStatus(const RequestStatus& from); + RequestStatus(RequestStatus&& from) noexcept + : RequestStatus() { + *this = ::std::move(from); + } + + inline RequestStatus& operator=(const RequestStatus& from) { + CopyFrom(from); + return *this; + } + inline RequestStatus& operator=(RequestStatus&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const RequestStatus& default_instance() { + return *internal_default_instance(); + } + static inline const RequestStatus* internal_default_instance() { + return reinterpret_cast( + &_RequestStatus_default_instance_); + } + static constexpr int kIndexInFileMessages = + 61; + + friend void swap(RequestStatus& a, RequestStatus& b) { + a.Swap(&b); + } + inline void Swap(RequestStatus* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(RequestStatus* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + RequestStatus* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const RequestStatus& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const RequestStatus& from) { + RequestStatus::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(RequestStatus* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.RequestStatus"; + } + protected: + explicit RequestStatus(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kStatusFieldNumber = 1, + }; + // string status = 1; + void clear_status(); + const std::string& status() const; + template + void set_status(ArgT0&& arg0, ArgT... args); + std::string* mutable_status(); + PROTOBUF_NODISCARD std::string* release_status(); + void set_allocated_status(std::string* status); + private: + const std::string& _internal_status() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_status(const std::string& value); + std::string* _internal_mutable_status(); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.RequestStatus) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr status_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class Response final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.Response) */ { + public: + inline Response() : Response(nullptr) {} + ~Response() override; + explicit PROTOBUF_CONSTEXPR Response(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + Response(const Response& from); + Response(Response&& from) noexcept + : Response() { + *this = ::std::move(from); + } + + inline Response& operator=(const Response& from) { + CopyFrom(from); + return *this; + } + inline Response& operator=(Response&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const Response& default_instance() { + return *internal_default_instance(); + } + static inline const Response* internal_default_instance() { + return reinterpret_cast( + &_Response_default_instance_); + } + static constexpr int kIndexInFileMessages = + 62; + + friend void swap(Response& a, Response& b) { + a.Swap(&b); + } + inline void Swap(Response* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(Response* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + Response* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const Response& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const Response& from) { + Response::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(Response* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.Response"; + } + protected: + explicit Response(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kResultFieldNumber = 2, + kSuccessFieldNumber = 1, + }; + // .google.protobuf.Any result = 2; + bool has_result() const; + private: + bool _internal_has_result() const; + public: + void clear_result(); + const ::PROTOBUF_NAMESPACE_ID::Any& result() const; + PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Any* release_result(); + ::PROTOBUF_NAMESPACE_ID::Any* mutable_result(); + void set_allocated_result(::PROTOBUF_NAMESPACE_ID::Any* result); + private: + const ::PROTOBUF_NAMESPACE_ID::Any& _internal_result() const; + ::PROTOBUF_NAMESPACE_ID::Any* _internal_mutable_result(); + public: + void unsafe_arena_set_allocated_result( + ::PROTOBUF_NAMESPACE_ID::Any* result); + ::PROTOBUF_NAMESPACE_ID::Any* unsafe_arena_release_result(); + + // bool success = 1; + void clear_success(); + bool success() const; + void set_success(bool value); + private: + bool _internal_success() const; + void _internal_set_success(bool value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.Response) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::Any* result_; + bool success_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// ------------------------------------------------------------------- + +class LiveAPIEvent final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:rtech.liveapi.LiveAPIEvent) */ { + public: + inline LiveAPIEvent() : LiveAPIEvent(nullptr) {} + ~LiveAPIEvent() override; + explicit PROTOBUF_CONSTEXPR LiveAPIEvent(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + LiveAPIEvent(const LiveAPIEvent& from); + LiveAPIEvent(LiveAPIEvent&& from) noexcept + : LiveAPIEvent() { + *this = ::std::move(from); + } + + inline LiveAPIEvent& operator=(const LiveAPIEvent& from) { + CopyFrom(from); + return *this; + } + inline LiveAPIEvent& operator=(LiveAPIEvent&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const LiveAPIEvent& default_instance() { + return *internal_default_instance(); + } + static inline const LiveAPIEvent* internal_default_instance() { + return reinterpret_cast( + &_LiveAPIEvent_default_instance_); + } + static constexpr int kIndexInFileMessages = + 63; + + friend void swap(LiveAPIEvent& a, LiveAPIEvent& b) { + a.Swap(&b); + } + inline void Swap(LiveAPIEvent* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(LiveAPIEvent* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + LiveAPIEvent* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const LiveAPIEvent& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom( const LiveAPIEvent& from) { + LiveAPIEvent::MergeImpl(*this, from); + } + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _impl_._cached_size_.Get(); } + + private: + void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(LiveAPIEvent* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "rtech.liveapi.LiveAPIEvent"; + } + protected: + explicit LiveAPIEvent(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kGameMessageFieldNumber = 3, + kEventSizeFieldNumber = 1, + }; + // .google.protobuf.Any gameMessage = 3; + bool has_gamemessage() const; + private: + bool _internal_has_gamemessage() const; + public: + void clear_gamemessage(); + const ::PROTOBUF_NAMESPACE_ID::Any& gamemessage() const; + PROTOBUF_NODISCARD ::PROTOBUF_NAMESPACE_ID::Any* release_gamemessage(); + ::PROTOBUF_NAMESPACE_ID::Any* mutable_gamemessage(); + void set_allocated_gamemessage(::PROTOBUF_NAMESPACE_ID::Any* gamemessage); + private: + const ::PROTOBUF_NAMESPACE_ID::Any& _internal_gamemessage() const; + ::PROTOBUF_NAMESPACE_ID::Any* _internal_mutable_gamemessage(); + public: + void unsafe_arena_set_allocated_gamemessage( + ::PROTOBUF_NAMESPACE_ID::Any* gamemessage); + ::PROTOBUF_NAMESPACE_ID::Any* unsafe_arena_release_gamemessage(); + + // fixed32 event_size = 1; + void clear_event_size(); + uint32_t event_size() const; + void set_event_size(uint32_t value); + private: + uint32_t _internal_event_size() const; + void _internal_set_event_size(uint32_t value); + public: + + // @@protoc_insertion_point(class_scope:rtech.liveapi.LiveAPIEvent) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + struct Impl_ { + ::PROTOBUF_NAMESPACE_ID::Any* gamemessage_; + uint32_t event_size_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + }; + union { Impl_ _impl_; }; + friend struct ::TableStruct_events_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// Vector3 + +// float x = 1; +inline void Vector3::clear_x() { + _impl_.x_ = 0; +} +inline float Vector3::_internal_x() const { + return _impl_.x_; +} +inline float Vector3::x() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Vector3.x) + return _internal_x(); +} +inline void Vector3::_internal_set_x(float value) { + + _impl_.x_ = value; +} +inline void Vector3::set_x(float value) { + _internal_set_x(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Vector3.x) +} + +// float y = 2; +inline void Vector3::clear_y() { + _impl_.y_ = 0; +} +inline float Vector3::_internal_y() const { + return _impl_.y_; +} +inline float Vector3::y() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Vector3.y) + return _internal_y(); +} +inline void Vector3::_internal_set_y(float value) { + + _impl_.y_ = value; +} +inline void Vector3::set_y(float value) { + _internal_set_y(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Vector3.y) +} + +// float z = 3; +inline void Vector3::clear_z() { + _impl_.z_ = 0; +} +inline float Vector3::_internal_z() const { + return _impl_.z_; +} +inline float Vector3::z() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Vector3.z) + return _internal_z(); +} +inline void Vector3::_internal_set_z(float value) { + + _impl_.z_ = value; +} +inline void Vector3::set_z(float value) { + _internal_set_z(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Vector3.z) +} + +// ------------------------------------------------------------------- + +// Player + +// string name = 1; +inline void Player::clear_name() { + _impl_.name_.ClearToEmpty(); +} +inline const std::string& Player::name() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.name) + return _internal_name(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Player::set_name(ArgT0&& arg0, ArgT... args) { + + _impl_.name_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.name) +} +inline std::string* Player::mutable_name() { + std::string* _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.name) + return _s; +} +inline const std::string& Player::_internal_name() const { + return _impl_.name_.Get(); +} +inline void Player::_internal_set_name(const std::string& value) { + + _impl_.name_.Set(value, GetArenaForAllocation()); +} +inline std::string* Player::_internal_mutable_name() { + + return _impl_.name_.Mutable(GetArenaForAllocation()); +} +inline std::string* Player::release_name() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.name) + return _impl_.name_.Release(); +} +inline void Player::set_allocated_name(std::string* name) { + if (name != nullptr) { + + } else { + + } + _impl_.name_.SetAllocated(name, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.name_.IsDefault()) { + _impl_.name_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.name) +} + +// uint32 teamId = 2; +inline void Player::clear_teamid() { + _impl_.teamid_ = 0u; +} +inline uint32_t Player::_internal_teamid() const { + return _impl_.teamid_; +} +inline uint32_t Player::teamid() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.teamId) + return _internal_teamid(); +} +inline void Player::_internal_set_teamid(uint32_t value) { + + _impl_.teamid_ = value; +} +inline void Player::set_teamid(uint32_t value) { + _internal_set_teamid(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.teamId) +} + +// .rtech.liveapi.Vector3 pos = 3; +inline bool Player::_internal_has_pos() const { + return this != internal_default_instance() && _impl_.pos_ != nullptr; +} +inline bool Player::has_pos() const { + return _internal_has_pos(); +} +inline void Player::clear_pos() { + if (GetArenaForAllocation() == nullptr && _impl_.pos_ != nullptr) { + delete _impl_.pos_; + } + _impl_.pos_ = nullptr; +} +inline const ::rtech::liveapi::Vector3& Player::_internal_pos() const { + const ::rtech::liveapi::Vector3* p = _impl_.pos_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Vector3_default_instance_); +} +inline const ::rtech::liveapi::Vector3& Player::pos() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.pos) + return _internal_pos(); +} +inline void Player::unsafe_arena_set_allocated_pos( + ::rtech::liveapi::Vector3* pos) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.pos_); + } + _impl_.pos_ = pos; + if (pos) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Player.pos) +} +inline ::rtech::liveapi::Vector3* Player::release_pos() { + + ::rtech::liveapi::Vector3* temp = _impl_.pos_; + _impl_.pos_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Vector3* Player::unsafe_arena_release_pos() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.pos) + + ::rtech::liveapi::Vector3* temp = _impl_.pos_; + _impl_.pos_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Vector3* Player::_internal_mutable_pos() { + + if (_impl_.pos_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Vector3>(GetArenaForAllocation()); + _impl_.pos_ = p; + } + return _impl_.pos_; +} +inline ::rtech::liveapi::Vector3* Player::mutable_pos() { + ::rtech::liveapi::Vector3* _msg = _internal_mutable_pos(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.pos) + return _msg; +} +inline void Player::set_allocated_pos(::rtech::liveapi::Vector3* pos) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.pos_; + } + if (pos) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(pos); + if (message_arena != submessage_arena) { + pos = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, pos, submessage_arena); + } + + } else { + + } + _impl_.pos_ = pos; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.pos) +} + +// .rtech.liveapi.Vector3 angles = 4; +inline bool Player::_internal_has_angles() const { + return this != internal_default_instance() && _impl_.angles_ != nullptr; +} +inline bool Player::has_angles() const { + return _internal_has_angles(); +} +inline void Player::clear_angles() { + if (GetArenaForAllocation() == nullptr && _impl_.angles_ != nullptr) { + delete _impl_.angles_; + } + _impl_.angles_ = nullptr; +} +inline const ::rtech::liveapi::Vector3& Player::_internal_angles() const { + const ::rtech::liveapi::Vector3* p = _impl_.angles_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Vector3_default_instance_); +} +inline const ::rtech::liveapi::Vector3& Player::angles() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.angles) + return _internal_angles(); +} +inline void Player::unsafe_arena_set_allocated_angles( + ::rtech::liveapi::Vector3* angles) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.angles_); + } + _impl_.angles_ = angles; + if (angles) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Player.angles) +} +inline ::rtech::liveapi::Vector3* Player::release_angles() { + + ::rtech::liveapi::Vector3* temp = _impl_.angles_; + _impl_.angles_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Vector3* Player::unsafe_arena_release_angles() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.angles) + + ::rtech::liveapi::Vector3* temp = _impl_.angles_; + _impl_.angles_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Vector3* Player::_internal_mutable_angles() { + + if (_impl_.angles_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Vector3>(GetArenaForAllocation()); + _impl_.angles_ = p; + } + return _impl_.angles_; +} +inline ::rtech::liveapi::Vector3* Player::mutable_angles() { + ::rtech::liveapi::Vector3* _msg = _internal_mutable_angles(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.angles) + return _msg; +} +inline void Player::set_allocated_angles(::rtech::liveapi::Vector3* angles) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.angles_; + } + if (angles) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(angles); + if (message_arena != submessage_arena) { + angles = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, angles, submessage_arena); + } + + } else { + + } + _impl_.angles_ = angles; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.angles) +} + +// uint32 currentHealth = 5; +inline void Player::clear_currenthealth() { + _impl_.currenthealth_ = 0u; +} +inline uint32_t Player::_internal_currenthealth() const { + return _impl_.currenthealth_; +} +inline uint32_t Player::currenthealth() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.currentHealth) + return _internal_currenthealth(); +} +inline void Player::_internal_set_currenthealth(uint32_t value) { + + _impl_.currenthealth_ = value; +} +inline void Player::set_currenthealth(uint32_t value) { + _internal_set_currenthealth(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.currentHealth) +} + +// uint32 maxHealth = 6; +inline void Player::clear_maxhealth() { + _impl_.maxhealth_ = 0u; +} +inline uint32_t Player::_internal_maxhealth() const { + return _impl_.maxhealth_; +} +inline uint32_t Player::maxhealth() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.maxHealth) + return _internal_maxhealth(); +} +inline void Player::_internal_set_maxhealth(uint32_t value) { + + _impl_.maxhealth_ = value; +} +inline void Player::set_maxhealth(uint32_t value) { + _internal_set_maxhealth(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.maxHealth) +} + +// uint32 shieldHealth = 7; +inline void Player::clear_shieldhealth() { + _impl_.shieldhealth_ = 0u; +} +inline uint32_t Player::_internal_shieldhealth() const { + return _impl_.shieldhealth_; +} +inline uint32_t Player::shieldhealth() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.shieldHealth) + return _internal_shieldhealth(); +} +inline void Player::_internal_set_shieldhealth(uint32_t value) { + + _impl_.shieldhealth_ = value; +} +inline void Player::set_shieldhealth(uint32_t value) { + _internal_set_shieldhealth(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.shieldHealth) +} + +// uint32 shieldMaxHealth = 8; +inline void Player::clear_shieldmaxhealth() { + _impl_.shieldmaxhealth_ = 0u; +} +inline uint32_t Player::_internal_shieldmaxhealth() const { + return _impl_.shieldmaxhealth_; +} +inline uint32_t Player::shieldmaxhealth() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.shieldMaxHealth) + return _internal_shieldmaxhealth(); +} +inline void Player::_internal_set_shieldmaxhealth(uint32_t value) { + + _impl_.shieldmaxhealth_ = value; +} +inline void Player::set_shieldmaxhealth(uint32_t value) { + _internal_set_shieldmaxhealth(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.shieldMaxHealth) +} + +// string nucleusHash = 9; +inline void Player::clear_nucleushash() { + _impl_.nucleushash_.ClearToEmpty(); +} +inline const std::string& Player::nucleushash() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.nucleusHash) + return _internal_nucleushash(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Player::set_nucleushash(ArgT0&& arg0, ArgT... args) { + + _impl_.nucleushash_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.nucleusHash) +} +inline std::string* Player::mutable_nucleushash() { + std::string* _s = _internal_mutable_nucleushash(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.nucleusHash) + return _s; +} +inline const std::string& Player::_internal_nucleushash() const { + return _impl_.nucleushash_.Get(); +} +inline void Player::_internal_set_nucleushash(const std::string& value) { + + _impl_.nucleushash_.Set(value, GetArenaForAllocation()); +} +inline std::string* Player::_internal_mutable_nucleushash() { + + return _impl_.nucleushash_.Mutable(GetArenaForAllocation()); +} +inline std::string* Player::release_nucleushash() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.nucleusHash) + return _impl_.nucleushash_.Release(); +} +inline void Player::set_allocated_nucleushash(std::string* nucleushash) { + if (nucleushash != nullptr) { + + } else { + + } + _impl_.nucleushash_.SetAllocated(nucleushash, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.nucleushash_.IsDefault()) { + _impl_.nucleushash_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.nucleusHash) +} + +// string hardwareName = 10; +inline void Player::clear_hardwarename() { + _impl_.hardwarename_.ClearToEmpty(); +} +inline const std::string& Player::hardwarename() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.hardwareName) + return _internal_hardwarename(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Player::set_hardwarename(ArgT0&& arg0, ArgT... args) { + + _impl_.hardwarename_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.hardwareName) +} +inline std::string* Player::mutable_hardwarename() { + std::string* _s = _internal_mutable_hardwarename(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.hardwareName) + return _s; +} +inline const std::string& Player::_internal_hardwarename() const { + return _impl_.hardwarename_.Get(); +} +inline void Player::_internal_set_hardwarename(const std::string& value) { + + _impl_.hardwarename_.Set(value, GetArenaForAllocation()); +} +inline std::string* Player::_internal_mutable_hardwarename() { + + return _impl_.hardwarename_.Mutable(GetArenaForAllocation()); +} +inline std::string* Player::release_hardwarename() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.hardwareName) + return _impl_.hardwarename_.Release(); +} +inline void Player::set_allocated_hardwarename(std::string* hardwarename) { + if (hardwarename != nullptr) { + + } else { + + } + _impl_.hardwarename_.SetAllocated(hardwarename, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.hardwarename_.IsDefault()) { + _impl_.hardwarename_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.hardwareName) +} + +// string teamName = 11; +inline void Player::clear_teamname() { + _impl_.teamname_.ClearToEmpty(); +} +inline const std::string& Player::teamname() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.teamName) + return _internal_teamname(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Player::set_teamname(ArgT0&& arg0, ArgT... args) { + + _impl_.teamname_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.teamName) +} +inline std::string* Player::mutable_teamname() { + std::string* _s = _internal_mutable_teamname(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.teamName) + return _s; +} +inline const std::string& Player::_internal_teamname() const { + return _impl_.teamname_.Get(); +} +inline void Player::_internal_set_teamname(const std::string& value) { + + _impl_.teamname_.Set(value, GetArenaForAllocation()); +} +inline std::string* Player::_internal_mutable_teamname() { + + return _impl_.teamname_.Mutable(GetArenaForAllocation()); +} +inline std::string* Player::release_teamname() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.teamName) + return _impl_.teamname_.Release(); +} +inline void Player::set_allocated_teamname(std::string* teamname) { + if (teamname != nullptr) { + + } else { + + } + _impl_.teamname_.SetAllocated(teamname, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.teamname_.IsDefault()) { + _impl_.teamname_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.teamName) +} + +// uint32 squadIndex = 12; +inline void Player::clear_squadindex() { + _impl_.squadindex_ = 0u; +} +inline uint32_t Player::_internal_squadindex() const { + return _impl_.squadindex_; +} +inline uint32_t Player::squadindex() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.squadIndex) + return _internal_squadindex(); +} +inline void Player::_internal_set_squadindex(uint32_t value) { + + _impl_.squadindex_ = value; +} +inline void Player::set_squadindex(uint32_t value) { + _internal_set_squadindex(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.squadIndex) +} + +// string character = 13; +inline void Player::clear_character() { + _impl_.character_.ClearToEmpty(); +} +inline const std::string& Player::character() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.character) + return _internal_character(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Player::set_character(ArgT0&& arg0, ArgT... args) { + + _impl_.character_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.character) +} +inline std::string* Player::mutable_character() { + std::string* _s = _internal_mutable_character(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.character) + return _s; +} +inline const std::string& Player::_internal_character() const { + return _impl_.character_.Get(); +} +inline void Player::_internal_set_character(const std::string& value) { + + _impl_.character_.Set(value, GetArenaForAllocation()); +} +inline std::string* Player::_internal_mutable_character() { + + return _impl_.character_.Mutable(GetArenaForAllocation()); +} +inline std::string* Player::release_character() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.character) + return _impl_.character_.Release(); +} +inline void Player::set_allocated_character(std::string* character) { + if (character != nullptr) { + + } else { + + } + _impl_.character_.SetAllocated(character, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.character_.IsDefault()) { + _impl_.character_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.character) +} + +// string skin = 14; +inline void Player::clear_skin() { + _impl_.skin_.ClearToEmpty(); +} +inline const std::string& Player::skin() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Player.skin) + return _internal_skin(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Player::set_skin(ArgT0&& arg0, ArgT... args) { + + _impl_.skin_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Player.skin) +} +inline std::string* Player::mutable_skin() { + std::string* _s = _internal_mutable_skin(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Player.skin) + return _s; +} +inline const std::string& Player::_internal_skin() const { + return _impl_.skin_.Get(); +} +inline void Player::_internal_set_skin(const std::string& value) { + + _impl_.skin_.Set(value, GetArenaForAllocation()); +} +inline std::string* Player::_internal_mutable_skin() { + + return _impl_.skin_.Mutable(GetArenaForAllocation()); +} +inline std::string* Player::release_skin() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Player.skin) + return _impl_.skin_.Release(); +} +inline void Player::set_allocated_skin(std::string* skin) { + if (skin != nullptr) { + + } else { + + } + _impl_.skin_.SetAllocated(skin, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.skin_.IsDefault()) { + _impl_.skin_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Player.skin) +} + +// ------------------------------------------------------------------- + +// CustomMatch_LobbyPlayer + +// string name = 1; +inline void CustomMatch_LobbyPlayer::clear_name() { + _impl_.name_.ClearToEmpty(); +} +inline const std::string& CustomMatch_LobbyPlayer::name() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_LobbyPlayer.name) + return _internal_name(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_LobbyPlayer::set_name(ArgT0&& arg0, ArgT... args) { + + _impl_.name_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_LobbyPlayer.name) +} +inline std::string* CustomMatch_LobbyPlayer::mutable_name() { + std::string* _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_LobbyPlayer.name) + return _s; +} +inline const std::string& CustomMatch_LobbyPlayer::_internal_name() const { + return _impl_.name_.Get(); +} +inline void CustomMatch_LobbyPlayer::_internal_set_name(const std::string& value) { + + _impl_.name_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayer::_internal_mutable_name() { + + return _impl_.name_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayer::release_name() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_LobbyPlayer.name) + return _impl_.name_.Release(); +} +inline void CustomMatch_LobbyPlayer::set_allocated_name(std::string* name) { + if (name != nullptr) { + + } else { + + } + _impl_.name_.SetAllocated(name, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.name_.IsDefault()) { + _impl_.name_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_LobbyPlayer.name) +} + +// uint32 teamId = 2; +inline void CustomMatch_LobbyPlayer::clear_teamid() { + _impl_.teamid_ = 0u; +} +inline uint32_t CustomMatch_LobbyPlayer::_internal_teamid() const { + return _impl_.teamid_; +} +inline uint32_t CustomMatch_LobbyPlayer::teamid() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_LobbyPlayer.teamId) + return _internal_teamid(); +} +inline void CustomMatch_LobbyPlayer::_internal_set_teamid(uint32_t value) { + + _impl_.teamid_ = value; +} +inline void CustomMatch_LobbyPlayer::set_teamid(uint32_t value) { + _internal_set_teamid(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_LobbyPlayer.teamId) +} + +// string nucleusHash = 3; +inline void CustomMatch_LobbyPlayer::clear_nucleushash() { + _impl_.nucleushash_.ClearToEmpty(); +} +inline const std::string& CustomMatch_LobbyPlayer::nucleushash() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_LobbyPlayer.nucleusHash) + return _internal_nucleushash(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_LobbyPlayer::set_nucleushash(ArgT0&& arg0, ArgT... args) { + + _impl_.nucleushash_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_LobbyPlayer.nucleusHash) +} +inline std::string* CustomMatch_LobbyPlayer::mutable_nucleushash() { + std::string* _s = _internal_mutable_nucleushash(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_LobbyPlayer.nucleusHash) + return _s; +} +inline const std::string& CustomMatch_LobbyPlayer::_internal_nucleushash() const { + return _impl_.nucleushash_.Get(); +} +inline void CustomMatch_LobbyPlayer::_internal_set_nucleushash(const std::string& value) { + + _impl_.nucleushash_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayer::_internal_mutable_nucleushash() { + + return _impl_.nucleushash_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayer::release_nucleushash() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_LobbyPlayer.nucleusHash) + return _impl_.nucleushash_.Release(); +} +inline void CustomMatch_LobbyPlayer::set_allocated_nucleushash(std::string* nucleushash) { + if (nucleushash != nullptr) { + + } else { + + } + _impl_.nucleushash_.SetAllocated(nucleushash, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.nucleushash_.IsDefault()) { + _impl_.nucleushash_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_LobbyPlayer.nucleusHash) +} + +// string hardwareName = 4; +inline void CustomMatch_LobbyPlayer::clear_hardwarename() { + _impl_.hardwarename_.ClearToEmpty(); +} +inline const std::string& CustomMatch_LobbyPlayer::hardwarename() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_LobbyPlayer.hardwareName) + return _internal_hardwarename(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_LobbyPlayer::set_hardwarename(ArgT0&& arg0, ArgT... args) { + + _impl_.hardwarename_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_LobbyPlayer.hardwareName) +} +inline std::string* CustomMatch_LobbyPlayer::mutable_hardwarename() { + std::string* _s = _internal_mutable_hardwarename(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_LobbyPlayer.hardwareName) + return _s; +} +inline const std::string& CustomMatch_LobbyPlayer::_internal_hardwarename() const { + return _impl_.hardwarename_.Get(); +} +inline void CustomMatch_LobbyPlayer::_internal_set_hardwarename(const std::string& value) { + + _impl_.hardwarename_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayer::_internal_mutable_hardwarename() { + + return _impl_.hardwarename_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayer::release_hardwarename() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_LobbyPlayer.hardwareName) + return _impl_.hardwarename_.Release(); +} +inline void CustomMatch_LobbyPlayer::set_allocated_hardwarename(std::string* hardwarename) { + if (hardwarename != nullptr) { + + } else { + + } + _impl_.hardwarename_.SetAllocated(hardwarename, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.hardwarename_.IsDefault()) { + _impl_.hardwarename_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_LobbyPlayer.hardwareName) +} + +// ------------------------------------------------------------------- + +// Datacenter + +// uint64 timestamp = 1; +inline void Datacenter::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t Datacenter::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t Datacenter::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Datacenter.timestamp) + return _internal_timestamp(); +} +inline void Datacenter::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void Datacenter::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Datacenter.timestamp) +} + +// string category = 2; +inline void Datacenter::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& Datacenter::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Datacenter.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Datacenter::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Datacenter.category) +} +inline std::string* Datacenter::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Datacenter.category) + return _s; +} +inline const std::string& Datacenter::_internal_category() const { + return _impl_.category_.Get(); +} +inline void Datacenter::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* Datacenter::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* Datacenter::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Datacenter.category) + return _impl_.category_.Release(); +} +inline void Datacenter::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Datacenter.category) +} + +// string name = 3; +inline void Datacenter::clear_name() { + _impl_.name_.ClearToEmpty(); +} +inline const std::string& Datacenter::name() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Datacenter.name) + return _internal_name(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Datacenter::set_name(ArgT0&& arg0, ArgT... args) { + + _impl_.name_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Datacenter.name) +} +inline std::string* Datacenter::mutable_name() { + std::string* _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Datacenter.name) + return _s; +} +inline const std::string& Datacenter::_internal_name() const { + return _impl_.name_.Get(); +} +inline void Datacenter::_internal_set_name(const std::string& value) { + + _impl_.name_.Set(value, GetArenaForAllocation()); +} +inline std::string* Datacenter::_internal_mutable_name() { + + return _impl_.name_.Mutable(GetArenaForAllocation()); +} +inline std::string* Datacenter::release_name() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Datacenter.name) + return _impl_.name_.Release(); +} +inline void Datacenter::set_allocated_name(std::string* name) { + if (name != nullptr) { + + } else { + + } + _impl_.name_.SetAllocated(name, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.name_.IsDefault()) { + _impl_.name_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Datacenter.name) +} + +// ------------------------------------------------------------------- + +// Version + +// uint32 major_num = 1; +inline void Version::clear_major_num() { + _impl_.major_num_ = 0u; +} +inline uint32_t Version::_internal_major_num() const { + return _impl_.major_num_; +} +inline uint32_t Version::major_num() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Version.major_num) + return _internal_major_num(); +} +inline void Version::_internal_set_major_num(uint32_t value) { + + _impl_.major_num_ = value; +} +inline void Version::set_major_num(uint32_t value) { + _internal_set_major_num(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Version.major_num) +} + +// uint32 minor_num = 2; +inline void Version::clear_minor_num() { + _impl_.minor_num_ = 0u; +} +inline uint32_t Version::_internal_minor_num() const { + return _impl_.minor_num_; +} +inline uint32_t Version::minor_num() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Version.minor_num) + return _internal_minor_num(); +} +inline void Version::_internal_set_minor_num(uint32_t value) { + + _impl_.minor_num_ = value; +} +inline void Version::set_minor_num(uint32_t value) { + _internal_set_minor_num(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Version.minor_num) +} + +// uint32 build_stamp = 3; +inline void Version::clear_build_stamp() { + _impl_.build_stamp_ = 0u; +} +inline uint32_t Version::_internal_build_stamp() const { + return _impl_.build_stamp_; +} +inline uint32_t Version::build_stamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Version.build_stamp) + return _internal_build_stamp(); +} +inline void Version::_internal_set_build_stamp(uint32_t value) { + + _impl_.build_stamp_ = value; +} +inline void Version::set_build_stamp(uint32_t value) { + _internal_set_build_stamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Version.build_stamp) +} + +// string revision = 4; +inline void Version::clear_revision() { + _impl_.revision_.ClearToEmpty(); +} +inline const std::string& Version::revision() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Version.revision) + return _internal_revision(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Version::set_revision(ArgT0&& arg0, ArgT... args) { + + _impl_.revision_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Version.revision) +} +inline std::string* Version::mutable_revision() { + std::string* _s = _internal_mutable_revision(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Version.revision) + return _s; +} +inline const std::string& Version::_internal_revision() const { + return _impl_.revision_.Get(); +} +inline void Version::_internal_set_revision(const std::string& value) { + + _impl_.revision_.Set(value, GetArenaForAllocation()); +} +inline std::string* Version::_internal_mutable_revision() { + + return _impl_.revision_.Mutable(GetArenaForAllocation()); +} +inline std::string* Version::release_revision() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Version.revision) + return _impl_.revision_.Release(); +} +inline void Version::set_allocated_revision(std::string* revision) { + if (revision != nullptr) { + + } else { + + } + _impl_.revision_.SetAllocated(revision, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.revision_.IsDefault()) { + _impl_.revision_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Version.revision) +} + +// ------------------------------------------------------------------- + +// InventoryItem + +// int32 quantity = 1; +inline void InventoryItem::clear_quantity() { + _impl_.quantity_ = 0; +} +inline int32_t InventoryItem::_internal_quantity() const { + return _impl_.quantity_; +} +inline int32_t InventoryItem::quantity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryItem.quantity) + return _internal_quantity(); +} +inline void InventoryItem::_internal_set_quantity(int32_t value) { + + _impl_.quantity_ = value; +} +inline void InventoryItem::set_quantity(int32_t value) { + _internal_set_quantity(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryItem.quantity) +} + +// string item = 2; +inline void InventoryItem::clear_item() { + _impl_.item_.ClearToEmpty(); +} +inline const std::string& InventoryItem::item() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryItem.item) + return _internal_item(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryItem::set_item(ArgT0&& arg0, ArgT... args) { + + _impl_.item_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryItem.item) +} +inline std::string* InventoryItem::mutable_item() { + std::string* _s = _internal_mutable_item(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryItem.item) + return _s; +} +inline const std::string& InventoryItem::_internal_item() const { + return _impl_.item_.Get(); +} +inline void InventoryItem::_internal_set_item(const std::string& value) { + + _impl_.item_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryItem::_internal_mutable_item() { + + return _impl_.item_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryItem::release_item() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryItem.item) + return _impl_.item_.Release(); +} +inline void InventoryItem::set_allocated_item(std::string* item) { + if (item != nullptr) { + + } else { + + } + _impl_.item_.SetAllocated(item, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.item_.IsDefault()) { + _impl_.item_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryItem.item) +} + +// string extraData = 3; +inline void InventoryItem::clear_extradata() { + _impl_.extradata_.ClearToEmpty(); +} +inline const std::string& InventoryItem::extradata() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryItem.extraData) + return _internal_extradata(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryItem::set_extradata(ArgT0&& arg0, ArgT... args) { + + _impl_.extradata_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryItem.extraData) +} +inline std::string* InventoryItem::mutable_extradata() { + std::string* _s = _internal_mutable_extradata(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryItem.extraData) + return _s; +} +inline const std::string& InventoryItem::_internal_extradata() const { + return _impl_.extradata_.Get(); +} +inline void InventoryItem::_internal_set_extradata(const std::string& value) { + + _impl_.extradata_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryItem::_internal_mutable_extradata() { + + return _impl_.extradata_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryItem::release_extradata() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryItem.extraData) + return _impl_.extradata_.Release(); +} +inline void InventoryItem::set_allocated_extradata(std::string* extradata) { + if (extradata != nullptr) { + + } else { + + } + _impl_.extradata_.SetAllocated(extradata, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.extradata_.IsDefault()) { + _impl_.extradata_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryItem.extraData) +} + +// ------------------------------------------------------------------- + +// LoadoutConfiguration + +// repeated .rtech.liveapi.InventoryItem weapons = 1; +inline int LoadoutConfiguration::_internal_weapons_size() const { + return _impl_.weapons_.size(); +} +inline int LoadoutConfiguration::weapons_size() const { + return _internal_weapons_size(); +} +inline void LoadoutConfiguration::clear_weapons() { + _impl_.weapons_.Clear(); +} +inline ::rtech::liveapi::InventoryItem* LoadoutConfiguration::mutable_weapons(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.LoadoutConfiguration.weapons) + return _impl_.weapons_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >* +LoadoutConfiguration::mutable_weapons() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.LoadoutConfiguration.weapons) + return &_impl_.weapons_; +} +inline const ::rtech::liveapi::InventoryItem& LoadoutConfiguration::_internal_weapons(int index) const { + return _impl_.weapons_.Get(index); +} +inline const ::rtech::liveapi::InventoryItem& LoadoutConfiguration::weapons(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LoadoutConfiguration.weapons) + return _internal_weapons(index); +} +inline ::rtech::liveapi::InventoryItem* LoadoutConfiguration::_internal_add_weapons() { + return _impl_.weapons_.Add(); +} +inline ::rtech::liveapi::InventoryItem* LoadoutConfiguration::add_weapons() { + ::rtech::liveapi::InventoryItem* _add = _internal_add_weapons(); + // @@protoc_insertion_point(field_add:rtech.liveapi.LoadoutConfiguration.weapons) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >& +LoadoutConfiguration::weapons() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.LoadoutConfiguration.weapons) + return _impl_.weapons_; +} + +// repeated .rtech.liveapi.InventoryItem equipment = 2; +inline int LoadoutConfiguration::_internal_equipment_size() const { + return _impl_.equipment_.size(); +} +inline int LoadoutConfiguration::equipment_size() const { + return _internal_equipment_size(); +} +inline void LoadoutConfiguration::clear_equipment() { + _impl_.equipment_.Clear(); +} +inline ::rtech::liveapi::InventoryItem* LoadoutConfiguration::mutable_equipment(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.LoadoutConfiguration.equipment) + return _impl_.equipment_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >* +LoadoutConfiguration::mutable_equipment() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.LoadoutConfiguration.equipment) + return &_impl_.equipment_; +} +inline const ::rtech::liveapi::InventoryItem& LoadoutConfiguration::_internal_equipment(int index) const { + return _impl_.equipment_.Get(index); +} +inline const ::rtech::liveapi::InventoryItem& LoadoutConfiguration::equipment(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LoadoutConfiguration.equipment) + return _internal_equipment(index); +} +inline ::rtech::liveapi::InventoryItem* LoadoutConfiguration::_internal_add_equipment() { + return _impl_.equipment_.Add(); +} +inline ::rtech::liveapi::InventoryItem* LoadoutConfiguration::add_equipment() { + ::rtech::liveapi::InventoryItem* _add = _internal_add_equipment(); + // @@protoc_insertion_point(field_add:rtech.liveapi.LoadoutConfiguration.equipment) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::InventoryItem >& +LoadoutConfiguration::equipment() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.LoadoutConfiguration.equipment) + return _impl_.equipment_; +} + +// ------------------------------------------------------------------- + +// Init + +// uint64 timestamp = 1; +inline void Init::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t Init::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t Init::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Init.timestamp) + return _internal_timestamp(); +} +inline void Init::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void Init::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Init.timestamp) +} + +// string category = 2; +inline void Init::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& Init::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Init.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Init::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Init.category) +} +inline std::string* Init::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Init.category) + return _s; +} +inline const std::string& Init::_internal_category() const { + return _impl_.category_.Get(); +} +inline void Init::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* Init::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* Init::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Init.category) + return _impl_.category_.Release(); +} +inline void Init::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Init.category) +} + +// string gameVersion = 3; +inline void Init::clear_gameversion() { + _impl_.gameversion_.ClearToEmpty(); +} +inline const std::string& Init::gameversion() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Init.gameVersion) + return _internal_gameversion(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Init::set_gameversion(ArgT0&& arg0, ArgT... args) { + + _impl_.gameversion_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Init.gameVersion) +} +inline std::string* Init::mutable_gameversion() { + std::string* _s = _internal_mutable_gameversion(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Init.gameVersion) + return _s; +} +inline const std::string& Init::_internal_gameversion() const { + return _impl_.gameversion_.Get(); +} +inline void Init::_internal_set_gameversion(const std::string& value) { + + _impl_.gameversion_.Set(value, GetArenaForAllocation()); +} +inline std::string* Init::_internal_mutable_gameversion() { + + return _impl_.gameversion_.Mutable(GetArenaForAllocation()); +} +inline std::string* Init::release_gameversion() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Init.gameVersion) + return _impl_.gameversion_.Release(); +} +inline void Init::set_allocated_gameversion(std::string* gameversion) { + if (gameversion != nullptr) { + + } else { + + } + _impl_.gameversion_.SetAllocated(gameversion, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.gameversion_.IsDefault()) { + _impl_.gameversion_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Init.gameVersion) +} + +// .rtech.liveapi.Version apiVersion = 4; +inline bool Init::_internal_has_apiversion() const { + return this != internal_default_instance() && _impl_.apiversion_ != nullptr; +} +inline bool Init::has_apiversion() const { + return _internal_has_apiversion(); +} +inline void Init::clear_apiversion() { + if (GetArenaForAllocation() == nullptr && _impl_.apiversion_ != nullptr) { + delete _impl_.apiversion_; + } + _impl_.apiversion_ = nullptr; +} +inline const ::rtech::liveapi::Version& Init::_internal_apiversion() const { + const ::rtech::liveapi::Version* p = _impl_.apiversion_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Version_default_instance_); +} +inline const ::rtech::liveapi::Version& Init::apiversion() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Init.apiVersion) + return _internal_apiversion(); +} +inline void Init::unsafe_arena_set_allocated_apiversion( + ::rtech::liveapi::Version* apiversion) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.apiversion_); + } + _impl_.apiversion_ = apiversion; + if (apiversion) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Init.apiVersion) +} +inline ::rtech::liveapi::Version* Init::release_apiversion() { + + ::rtech::liveapi::Version* temp = _impl_.apiversion_; + _impl_.apiversion_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Version* Init::unsafe_arena_release_apiversion() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Init.apiVersion) + + ::rtech::liveapi::Version* temp = _impl_.apiversion_; + _impl_.apiversion_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Version* Init::_internal_mutable_apiversion() { + + if (_impl_.apiversion_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Version>(GetArenaForAllocation()); + _impl_.apiversion_ = p; + } + return _impl_.apiversion_; +} +inline ::rtech::liveapi::Version* Init::mutable_apiversion() { + ::rtech::liveapi::Version* _msg = _internal_mutable_apiversion(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Init.apiVersion) + return _msg; +} +inline void Init::set_allocated_apiversion(::rtech::liveapi::Version* apiversion) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.apiversion_; + } + if (apiversion) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(apiversion); + if (message_arena != submessage_arena) { + apiversion = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, apiversion, submessage_arena); + } + + } else { + + } + _impl_.apiversion_ = apiversion; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Init.apiVersion) +} + +// string platform = 5; +inline void Init::clear_platform() { + _impl_.platform_.ClearToEmpty(); +} +inline const std::string& Init::platform() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Init.platform) + return _internal_platform(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Init::set_platform(ArgT0&& arg0, ArgT... args) { + + _impl_.platform_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Init.platform) +} +inline std::string* Init::mutable_platform() { + std::string* _s = _internal_mutable_platform(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Init.platform) + return _s; +} +inline const std::string& Init::_internal_platform() const { + return _impl_.platform_.Get(); +} +inline void Init::_internal_set_platform(const std::string& value) { + + _impl_.platform_.Set(value, GetArenaForAllocation()); +} +inline std::string* Init::_internal_mutable_platform() { + + return _impl_.platform_.Mutable(GetArenaForAllocation()); +} +inline std::string* Init::release_platform() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Init.platform) + return _impl_.platform_.Release(); +} +inline void Init::set_allocated_platform(std::string* platform) { + if (platform != nullptr) { + + } else { + + } + _impl_.platform_.SetAllocated(platform, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.platform_.IsDefault()) { + _impl_.platform_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Init.platform) +} + +// string name = 6; +inline void Init::clear_name() { + _impl_.name_.ClearToEmpty(); +} +inline const std::string& Init::name() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Init.name) + return _internal_name(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Init::set_name(ArgT0&& arg0, ArgT... args) { + + _impl_.name_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Init.name) +} +inline std::string* Init::mutable_name() { + std::string* _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Init.name) + return _s; +} +inline const std::string& Init::_internal_name() const { + return _impl_.name_.Get(); +} +inline void Init::_internal_set_name(const std::string& value) { + + _impl_.name_.Set(value, GetArenaForAllocation()); +} +inline std::string* Init::_internal_mutable_name() { + + return _impl_.name_.Mutable(GetArenaForAllocation()); +} +inline std::string* Init::release_name() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Init.name) + return _impl_.name_.Release(); +} +inline void Init::set_allocated_name(std::string* name) { + if (name != nullptr) { + + } else { + + } + _impl_.name_.SetAllocated(name, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.name_.IsDefault()) { + _impl_.name_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Init.name) +} + +// ------------------------------------------------------------------- + +// CustomMatch_LobbyPlayers + +// string playerToken = 1; +inline void CustomMatch_LobbyPlayers::clear_playertoken() { + _impl_.playertoken_.ClearToEmpty(); +} +inline const std::string& CustomMatch_LobbyPlayers::playertoken() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_LobbyPlayers.playerToken) + return _internal_playertoken(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_LobbyPlayers::set_playertoken(ArgT0&& arg0, ArgT... args) { + + _impl_.playertoken_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_LobbyPlayers.playerToken) +} +inline std::string* CustomMatch_LobbyPlayers::mutable_playertoken() { + std::string* _s = _internal_mutable_playertoken(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_LobbyPlayers.playerToken) + return _s; +} +inline const std::string& CustomMatch_LobbyPlayers::_internal_playertoken() const { + return _impl_.playertoken_.Get(); +} +inline void CustomMatch_LobbyPlayers::_internal_set_playertoken(const std::string& value) { + + _impl_.playertoken_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayers::_internal_mutable_playertoken() { + + return _impl_.playertoken_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_LobbyPlayers::release_playertoken() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_LobbyPlayers.playerToken) + return _impl_.playertoken_.Release(); +} +inline void CustomMatch_LobbyPlayers::set_allocated_playertoken(std::string* playertoken) { + if (playertoken != nullptr) { + + } else { + + } + _impl_.playertoken_.SetAllocated(playertoken, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.playertoken_.IsDefault()) { + _impl_.playertoken_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_LobbyPlayers.playerToken) +} + +// repeated .rtech.liveapi.CustomMatch_LobbyPlayer players = 2; +inline int CustomMatch_LobbyPlayers::_internal_players_size() const { + return _impl_.players_.size(); +} +inline int CustomMatch_LobbyPlayers::players_size() const { + return _internal_players_size(); +} +inline void CustomMatch_LobbyPlayers::clear_players() { + _impl_.players_.Clear(); +} +inline ::rtech::liveapi::CustomMatch_LobbyPlayer* CustomMatch_LobbyPlayers::mutable_players(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_LobbyPlayers.players) + return _impl_.players_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::CustomMatch_LobbyPlayer >* +CustomMatch_LobbyPlayers::mutable_players() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.CustomMatch_LobbyPlayers.players) + return &_impl_.players_; +} +inline const ::rtech::liveapi::CustomMatch_LobbyPlayer& CustomMatch_LobbyPlayers::_internal_players(int index) const { + return _impl_.players_.Get(index); +} +inline const ::rtech::liveapi::CustomMatch_LobbyPlayer& CustomMatch_LobbyPlayers::players(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_LobbyPlayers.players) + return _internal_players(index); +} +inline ::rtech::liveapi::CustomMatch_LobbyPlayer* CustomMatch_LobbyPlayers::_internal_add_players() { + return _impl_.players_.Add(); +} +inline ::rtech::liveapi::CustomMatch_LobbyPlayer* CustomMatch_LobbyPlayers::add_players() { + ::rtech::liveapi::CustomMatch_LobbyPlayer* _add = _internal_add_players(); + // @@protoc_insertion_point(field_add:rtech.liveapi.CustomMatch_LobbyPlayers.players) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::CustomMatch_LobbyPlayer >& +CustomMatch_LobbyPlayers::players() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.CustomMatch_LobbyPlayers.players) + return _impl_.players_; +} + +// ------------------------------------------------------------------- + +// ObserverSwitched + +// uint64 timestamp = 1; +inline void ObserverSwitched::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t ObserverSwitched::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t ObserverSwitched::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverSwitched.timestamp) + return _internal_timestamp(); +} +inline void ObserverSwitched::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void ObserverSwitched::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ObserverSwitched.timestamp) +} + +// string category = 2; +inline void ObserverSwitched::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& ObserverSwitched::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverSwitched.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ObserverSwitched::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ObserverSwitched.category) +} +inline std::string* ObserverSwitched::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ObserverSwitched.category) + return _s; +} +inline const std::string& ObserverSwitched::_internal_category() const { + return _impl_.category_.Get(); +} +inline void ObserverSwitched::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* ObserverSwitched::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* ObserverSwitched::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ObserverSwitched.category) + return _impl_.category_.Release(); +} +inline void ObserverSwitched::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ObserverSwitched.category) +} + +// .rtech.liveapi.Player observer = 3; +inline bool ObserverSwitched::_internal_has_observer() const { + return this != internal_default_instance() && _impl_.observer_ != nullptr; +} +inline bool ObserverSwitched::has_observer() const { + return _internal_has_observer(); +} +inline void ObserverSwitched::clear_observer() { + if (GetArenaForAllocation() == nullptr && _impl_.observer_ != nullptr) { + delete _impl_.observer_; + } + _impl_.observer_ = nullptr; +} +inline const ::rtech::liveapi::Player& ObserverSwitched::_internal_observer() const { + const ::rtech::liveapi::Player* p = _impl_.observer_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& ObserverSwitched::observer() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverSwitched.observer) + return _internal_observer(); +} +inline void ObserverSwitched::unsafe_arena_set_allocated_observer( + ::rtech::liveapi::Player* observer) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.observer_); + } + _impl_.observer_ = observer; + if (observer) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.ObserverSwitched.observer) +} +inline ::rtech::liveapi::Player* ObserverSwitched::release_observer() { + + ::rtech::liveapi::Player* temp = _impl_.observer_; + _impl_.observer_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* ObserverSwitched::unsafe_arena_release_observer() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ObserverSwitched.observer) + + ::rtech::liveapi::Player* temp = _impl_.observer_; + _impl_.observer_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* ObserverSwitched::_internal_mutable_observer() { + + if (_impl_.observer_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.observer_ = p; + } + return _impl_.observer_; +} +inline ::rtech::liveapi::Player* ObserverSwitched::mutable_observer() { + ::rtech::liveapi::Player* _msg = _internal_mutable_observer(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ObserverSwitched.observer) + return _msg; +} +inline void ObserverSwitched::set_allocated_observer(::rtech::liveapi::Player* observer) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.observer_; + } + if (observer) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(observer); + if (message_arena != submessage_arena) { + observer = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, observer, submessage_arena); + } + + } else { + + } + _impl_.observer_ = observer; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ObserverSwitched.observer) +} + +// .rtech.liveapi.Player target = 4; +inline bool ObserverSwitched::_internal_has_target() const { + return this != internal_default_instance() && _impl_.target_ != nullptr; +} +inline bool ObserverSwitched::has_target() const { + return _internal_has_target(); +} +inline void ObserverSwitched::clear_target() { + if (GetArenaForAllocation() == nullptr && _impl_.target_ != nullptr) { + delete _impl_.target_; + } + _impl_.target_ = nullptr; +} +inline const ::rtech::liveapi::Player& ObserverSwitched::_internal_target() const { + const ::rtech::liveapi::Player* p = _impl_.target_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& ObserverSwitched::target() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverSwitched.target) + return _internal_target(); +} +inline void ObserverSwitched::unsafe_arena_set_allocated_target( + ::rtech::liveapi::Player* target) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.target_); + } + _impl_.target_ = target; + if (target) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.ObserverSwitched.target) +} +inline ::rtech::liveapi::Player* ObserverSwitched::release_target() { + + ::rtech::liveapi::Player* temp = _impl_.target_; + _impl_.target_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* ObserverSwitched::unsafe_arena_release_target() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ObserverSwitched.target) + + ::rtech::liveapi::Player* temp = _impl_.target_; + _impl_.target_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* ObserverSwitched::_internal_mutable_target() { + + if (_impl_.target_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.target_ = p; + } + return _impl_.target_; +} +inline ::rtech::liveapi::Player* ObserverSwitched::mutable_target() { + ::rtech::liveapi::Player* _msg = _internal_mutable_target(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ObserverSwitched.target) + return _msg; +} +inline void ObserverSwitched::set_allocated_target(::rtech::liveapi::Player* target) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.target_; + } + if (target) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(target); + if (message_arena != submessage_arena) { + target = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, target, submessage_arena); + } + + } else { + + } + _impl_.target_ = target; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ObserverSwitched.target) +} + +// repeated .rtech.liveapi.Player targetTeam = 5; +inline int ObserverSwitched::_internal_targetteam_size() const { + return _impl_.targetteam_.size(); +} +inline int ObserverSwitched::targetteam_size() const { + return _internal_targetteam_size(); +} +inline void ObserverSwitched::clear_targetteam() { + _impl_.targetteam_.Clear(); +} +inline ::rtech::liveapi::Player* ObserverSwitched::mutable_targetteam(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ObserverSwitched.targetTeam) + return _impl_.targetteam_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* +ObserverSwitched::mutable_targetteam() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.ObserverSwitched.targetTeam) + return &_impl_.targetteam_; +} +inline const ::rtech::liveapi::Player& ObserverSwitched::_internal_targetteam(int index) const { + return _impl_.targetteam_.Get(index); +} +inline const ::rtech::liveapi::Player& ObserverSwitched::targetteam(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverSwitched.targetTeam) + return _internal_targetteam(index); +} +inline ::rtech::liveapi::Player* ObserverSwitched::_internal_add_targetteam() { + return _impl_.targetteam_.Add(); +} +inline ::rtech::liveapi::Player* ObserverSwitched::add_targetteam() { + ::rtech::liveapi::Player* _add = _internal_add_targetteam(); + // @@protoc_insertion_point(field_add:rtech.liveapi.ObserverSwitched.targetTeam) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& +ObserverSwitched::targetteam() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.ObserverSwitched.targetTeam) + return _impl_.targetteam_; +} + +// ------------------------------------------------------------------- + +// ObserverAnnotation + +// uint64 timestamp = 1; +inline void ObserverAnnotation::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t ObserverAnnotation::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t ObserverAnnotation::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverAnnotation.timestamp) + return _internal_timestamp(); +} +inline void ObserverAnnotation::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void ObserverAnnotation::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ObserverAnnotation.timestamp) +} + +// string category = 2; +inline void ObserverAnnotation::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& ObserverAnnotation::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverAnnotation.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ObserverAnnotation::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ObserverAnnotation.category) +} +inline std::string* ObserverAnnotation::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ObserverAnnotation.category) + return _s; +} +inline const std::string& ObserverAnnotation::_internal_category() const { + return _impl_.category_.Get(); +} +inline void ObserverAnnotation::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* ObserverAnnotation::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* ObserverAnnotation::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ObserverAnnotation.category) + return _impl_.category_.Release(); +} +inline void ObserverAnnotation::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ObserverAnnotation.category) +} + +// int32 annotationSerial = 3; +inline void ObserverAnnotation::clear_annotationserial() { + _impl_.annotationserial_ = 0; +} +inline int32_t ObserverAnnotation::_internal_annotationserial() const { + return _impl_.annotationserial_; +} +inline int32_t ObserverAnnotation::annotationserial() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ObserverAnnotation.annotationSerial) + return _internal_annotationserial(); +} +inline void ObserverAnnotation::_internal_set_annotationserial(int32_t value) { + + _impl_.annotationserial_ = value; +} +inline void ObserverAnnotation::set_annotationserial(int32_t value) { + _internal_set_annotationserial(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ObserverAnnotation.annotationSerial) +} + +// ------------------------------------------------------------------- + +// MatchSetup + +// uint64 timestamp = 1; +inline void MatchSetup::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t MatchSetup::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t MatchSetup::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.timestamp) + return _internal_timestamp(); +} +inline void MatchSetup::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void MatchSetup::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.timestamp) +} + +// string category = 2; +inline void MatchSetup::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& MatchSetup::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void MatchSetup::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.category) +} +inline std::string* MatchSetup::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchSetup.category) + return _s; +} +inline const std::string& MatchSetup::_internal_category() const { + return _impl_.category_.Get(); +} +inline void MatchSetup::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* MatchSetup::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* MatchSetup::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchSetup.category) + return _impl_.category_.Release(); +} +inline void MatchSetup::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchSetup.category) +} + +// string map = 3; +inline void MatchSetup::clear_map() { + _impl_.map_.ClearToEmpty(); +} +inline const std::string& MatchSetup::map() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.map) + return _internal_map(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void MatchSetup::set_map(ArgT0&& arg0, ArgT... args) { + + _impl_.map_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.map) +} +inline std::string* MatchSetup::mutable_map() { + std::string* _s = _internal_mutable_map(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchSetup.map) + return _s; +} +inline const std::string& MatchSetup::_internal_map() const { + return _impl_.map_.Get(); +} +inline void MatchSetup::_internal_set_map(const std::string& value) { + + _impl_.map_.Set(value, GetArenaForAllocation()); +} +inline std::string* MatchSetup::_internal_mutable_map() { + + return _impl_.map_.Mutable(GetArenaForAllocation()); +} +inline std::string* MatchSetup::release_map() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchSetup.map) + return _impl_.map_.Release(); +} +inline void MatchSetup::set_allocated_map(std::string* map) { + if (map != nullptr) { + + } else { + + } + _impl_.map_.SetAllocated(map, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.map_.IsDefault()) { + _impl_.map_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchSetup.map) +} + +// string playlistName = 4; +inline void MatchSetup::clear_playlistname() { + _impl_.playlistname_.ClearToEmpty(); +} +inline const std::string& MatchSetup::playlistname() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.playlistName) + return _internal_playlistname(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void MatchSetup::set_playlistname(ArgT0&& arg0, ArgT... args) { + + _impl_.playlistname_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.playlistName) +} +inline std::string* MatchSetup::mutable_playlistname() { + std::string* _s = _internal_mutable_playlistname(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchSetup.playlistName) + return _s; +} +inline const std::string& MatchSetup::_internal_playlistname() const { + return _impl_.playlistname_.Get(); +} +inline void MatchSetup::_internal_set_playlistname(const std::string& value) { + + _impl_.playlistname_.Set(value, GetArenaForAllocation()); +} +inline std::string* MatchSetup::_internal_mutable_playlistname() { + + return _impl_.playlistname_.Mutable(GetArenaForAllocation()); +} +inline std::string* MatchSetup::release_playlistname() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchSetup.playlistName) + return _impl_.playlistname_.Release(); +} +inline void MatchSetup::set_allocated_playlistname(std::string* playlistname) { + if (playlistname != nullptr) { + + } else { + + } + _impl_.playlistname_.SetAllocated(playlistname, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.playlistname_.IsDefault()) { + _impl_.playlistname_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchSetup.playlistName) +} + +// string playlistDesc = 5; +inline void MatchSetup::clear_playlistdesc() { + _impl_.playlistdesc_.ClearToEmpty(); +} +inline const std::string& MatchSetup::playlistdesc() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.playlistDesc) + return _internal_playlistdesc(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void MatchSetup::set_playlistdesc(ArgT0&& arg0, ArgT... args) { + + _impl_.playlistdesc_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.playlistDesc) +} +inline std::string* MatchSetup::mutable_playlistdesc() { + std::string* _s = _internal_mutable_playlistdesc(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchSetup.playlistDesc) + return _s; +} +inline const std::string& MatchSetup::_internal_playlistdesc() const { + return _impl_.playlistdesc_.Get(); +} +inline void MatchSetup::_internal_set_playlistdesc(const std::string& value) { + + _impl_.playlistdesc_.Set(value, GetArenaForAllocation()); +} +inline std::string* MatchSetup::_internal_mutable_playlistdesc() { + + return _impl_.playlistdesc_.Mutable(GetArenaForAllocation()); +} +inline std::string* MatchSetup::release_playlistdesc() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchSetup.playlistDesc) + return _impl_.playlistdesc_.Release(); +} +inline void MatchSetup::set_allocated_playlistdesc(std::string* playlistdesc) { + if (playlistdesc != nullptr) { + + } else { + + } + _impl_.playlistdesc_.SetAllocated(playlistdesc, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.playlistdesc_.IsDefault()) { + _impl_.playlistdesc_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchSetup.playlistDesc) +} + +// .rtech.liveapi.Datacenter datacenter = 6; +inline bool MatchSetup::_internal_has_datacenter() const { + return this != internal_default_instance() && _impl_.datacenter_ != nullptr; +} +inline bool MatchSetup::has_datacenter() const { + return _internal_has_datacenter(); +} +inline void MatchSetup::clear_datacenter() { + if (GetArenaForAllocation() == nullptr && _impl_.datacenter_ != nullptr) { + delete _impl_.datacenter_; + } + _impl_.datacenter_ = nullptr; +} +inline const ::rtech::liveapi::Datacenter& MatchSetup::_internal_datacenter() const { + const ::rtech::liveapi::Datacenter* p = _impl_.datacenter_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Datacenter_default_instance_); +} +inline const ::rtech::liveapi::Datacenter& MatchSetup::datacenter() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.datacenter) + return _internal_datacenter(); +} +inline void MatchSetup::unsafe_arena_set_allocated_datacenter( + ::rtech::liveapi::Datacenter* datacenter) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.datacenter_); + } + _impl_.datacenter_ = datacenter; + if (datacenter) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.MatchSetup.datacenter) +} +inline ::rtech::liveapi::Datacenter* MatchSetup::release_datacenter() { + + ::rtech::liveapi::Datacenter* temp = _impl_.datacenter_; + _impl_.datacenter_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Datacenter* MatchSetup::unsafe_arena_release_datacenter() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchSetup.datacenter) + + ::rtech::liveapi::Datacenter* temp = _impl_.datacenter_; + _impl_.datacenter_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Datacenter* MatchSetup::_internal_mutable_datacenter() { + + if (_impl_.datacenter_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Datacenter>(GetArenaForAllocation()); + _impl_.datacenter_ = p; + } + return _impl_.datacenter_; +} +inline ::rtech::liveapi::Datacenter* MatchSetup::mutable_datacenter() { + ::rtech::liveapi::Datacenter* _msg = _internal_mutable_datacenter(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchSetup.datacenter) + return _msg; +} +inline void MatchSetup::set_allocated_datacenter(::rtech::liveapi::Datacenter* datacenter) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.datacenter_; + } + if (datacenter) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(datacenter); + if (message_arena != submessage_arena) { + datacenter = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, datacenter, submessage_arena); + } + + } else { + + } + _impl_.datacenter_ = datacenter; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchSetup.datacenter) +} + +// bool aimAssistOn = 7; +inline void MatchSetup::clear_aimassiston() { + _impl_.aimassiston_ = false; +} +inline bool MatchSetup::_internal_aimassiston() const { + return _impl_.aimassiston_; +} +inline bool MatchSetup::aimassiston() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.aimAssistOn) + return _internal_aimassiston(); +} +inline void MatchSetup::_internal_set_aimassiston(bool value) { + + _impl_.aimassiston_ = value; +} +inline void MatchSetup::set_aimassiston(bool value) { + _internal_set_aimassiston(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.aimAssistOn) +} + +// bool anonymousMode = 8; +inline void MatchSetup::clear_anonymousmode() { + _impl_.anonymousmode_ = false; +} +inline bool MatchSetup::_internal_anonymousmode() const { + return _impl_.anonymousmode_; +} +inline bool MatchSetup::anonymousmode() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.anonymousMode) + return _internal_anonymousmode(); +} +inline void MatchSetup::_internal_set_anonymousmode(bool value) { + + _impl_.anonymousmode_ = value; +} +inline void MatchSetup::set_anonymousmode(bool value) { + _internal_set_anonymousmode(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.anonymousMode) +} + +// string serverId = 9; +inline void MatchSetup::clear_serverid() { + _impl_.serverid_.ClearToEmpty(); +} +inline const std::string& MatchSetup::serverid() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.serverId) + return _internal_serverid(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void MatchSetup::set_serverid(ArgT0&& arg0, ArgT... args) { + + _impl_.serverid_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchSetup.serverId) +} +inline std::string* MatchSetup::mutable_serverid() { + std::string* _s = _internal_mutable_serverid(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchSetup.serverId) + return _s; +} +inline const std::string& MatchSetup::_internal_serverid() const { + return _impl_.serverid_.Get(); +} +inline void MatchSetup::_internal_set_serverid(const std::string& value) { + + _impl_.serverid_.Set(value, GetArenaForAllocation()); +} +inline std::string* MatchSetup::_internal_mutable_serverid() { + + return _impl_.serverid_.Mutable(GetArenaForAllocation()); +} +inline std::string* MatchSetup::release_serverid() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchSetup.serverId) + return _impl_.serverid_.Release(); +} +inline void MatchSetup::set_allocated_serverid(std::string* serverid) { + if (serverid != nullptr) { + + } else { + + } + _impl_.serverid_.SetAllocated(serverid, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.serverid_.IsDefault()) { + _impl_.serverid_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchSetup.serverId) +} + +// .rtech.liveapi.LoadoutConfiguration startingLoadout = 10; +inline bool MatchSetup::_internal_has_startingloadout() const { + return this != internal_default_instance() && _impl_.startingloadout_ != nullptr; +} +inline bool MatchSetup::has_startingloadout() const { + return _internal_has_startingloadout(); +} +inline void MatchSetup::clear_startingloadout() { + if (GetArenaForAllocation() == nullptr && _impl_.startingloadout_ != nullptr) { + delete _impl_.startingloadout_; + } + _impl_.startingloadout_ = nullptr; +} +inline const ::rtech::liveapi::LoadoutConfiguration& MatchSetup::_internal_startingloadout() const { + const ::rtech::liveapi::LoadoutConfiguration* p = _impl_.startingloadout_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_LoadoutConfiguration_default_instance_); +} +inline const ::rtech::liveapi::LoadoutConfiguration& MatchSetup::startingloadout() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchSetup.startingLoadout) + return _internal_startingloadout(); +} +inline void MatchSetup::unsafe_arena_set_allocated_startingloadout( + ::rtech::liveapi::LoadoutConfiguration* startingloadout) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.startingloadout_); + } + _impl_.startingloadout_ = startingloadout; + if (startingloadout) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.MatchSetup.startingLoadout) +} +inline ::rtech::liveapi::LoadoutConfiguration* MatchSetup::release_startingloadout() { + + ::rtech::liveapi::LoadoutConfiguration* temp = _impl_.startingloadout_; + _impl_.startingloadout_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::LoadoutConfiguration* MatchSetup::unsafe_arena_release_startingloadout() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchSetup.startingLoadout) + + ::rtech::liveapi::LoadoutConfiguration* temp = _impl_.startingloadout_; + _impl_.startingloadout_ = nullptr; + return temp; +} +inline ::rtech::liveapi::LoadoutConfiguration* MatchSetup::_internal_mutable_startingloadout() { + + if (_impl_.startingloadout_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::LoadoutConfiguration>(GetArenaForAllocation()); + _impl_.startingloadout_ = p; + } + return _impl_.startingloadout_; +} +inline ::rtech::liveapi::LoadoutConfiguration* MatchSetup::mutable_startingloadout() { + ::rtech::liveapi::LoadoutConfiguration* _msg = _internal_mutable_startingloadout(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchSetup.startingLoadout) + return _msg; +} +inline void MatchSetup::set_allocated_startingloadout(::rtech::liveapi::LoadoutConfiguration* startingloadout) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.startingloadout_; + } + if (startingloadout) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(startingloadout); + if (message_arena != submessage_arena) { + startingloadout = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, startingloadout, submessage_arena); + } + + } else { + + } + _impl_.startingloadout_ = startingloadout; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchSetup.startingLoadout) +} + +// ------------------------------------------------------------------- + +// GameStateChanged + +// uint64 timestamp = 1; +inline void GameStateChanged::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t GameStateChanged::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t GameStateChanged::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GameStateChanged.timestamp) + return _internal_timestamp(); +} +inline void GameStateChanged::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void GameStateChanged::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.GameStateChanged.timestamp) +} + +// string category = 2; +inline void GameStateChanged::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& GameStateChanged::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GameStateChanged.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void GameStateChanged::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.GameStateChanged.category) +} +inline std::string* GameStateChanged::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GameStateChanged.category) + return _s; +} +inline const std::string& GameStateChanged::_internal_category() const { + return _impl_.category_.Get(); +} +inline void GameStateChanged::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* GameStateChanged::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* GameStateChanged::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GameStateChanged.category) + return _impl_.category_.Release(); +} +inline void GameStateChanged::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GameStateChanged.category) +} + +// string state = 3; +inline void GameStateChanged::clear_state() { + _impl_.state_.ClearToEmpty(); +} +inline const std::string& GameStateChanged::state() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GameStateChanged.state) + return _internal_state(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void GameStateChanged::set_state(ArgT0&& arg0, ArgT... args) { + + _impl_.state_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.GameStateChanged.state) +} +inline std::string* GameStateChanged::mutable_state() { + std::string* _s = _internal_mutable_state(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GameStateChanged.state) + return _s; +} +inline const std::string& GameStateChanged::_internal_state() const { + return _impl_.state_.Get(); +} +inline void GameStateChanged::_internal_set_state(const std::string& value) { + + _impl_.state_.Set(value, GetArenaForAllocation()); +} +inline std::string* GameStateChanged::_internal_mutable_state() { + + return _impl_.state_.Mutable(GetArenaForAllocation()); +} +inline std::string* GameStateChanged::release_state() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GameStateChanged.state) + return _impl_.state_.Release(); +} +inline void GameStateChanged::set_allocated_state(std::string* state) { + if (state != nullptr) { + + } else { + + } + _impl_.state_.SetAllocated(state, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.state_.IsDefault()) { + _impl_.state_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GameStateChanged.state) +} + +// ------------------------------------------------------------------- + +// CharacterSelected + +// uint64 timestamp = 1; +inline void CharacterSelected::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t CharacterSelected::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t CharacterSelected::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CharacterSelected.timestamp) + return _internal_timestamp(); +} +inline void CharacterSelected::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void CharacterSelected::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CharacterSelected.timestamp) +} + +// string category = 2; +inline void CharacterSelected::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& CharacterSelected::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CharacterSelected.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CharacterSelected::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CharacterSelected.category) +} +inline std::string* CharacterSelected::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CharacterSelected.category) + return _s; +} +inline const std::string& CharacterSelected::_internal_category() const { + return _impl_.category_.Get(); +} +inline void CharacterSelected::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* CharacterSelected::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* CharacterSelected::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CharacterSelected.category) + return _impl_.category_.Release(); +} +inline void CharacterSelected::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CharacterSelected.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool CharacterSelected::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool CharacterSelected::has_player() const { + return _internal_has_player(); +} +inline void CharacterSelected::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& CharacterSelected::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& CharacterSelected::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CharacterSelected.player) + return _internal_player(); +} +inline void CharacterSelected::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.CharacterSelected.player) +} +inline ::rtech::liveapi::Player* CharacterSelected::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* CharacterSelected::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CharacterSelected.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* CharacterSelected::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* CharacterSelected::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CharacterSelected.player) + return _msg; +} +inline void CharacterSelected::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CharacterSelected.player) +} + +// ------------------------------------------------------------------- + +// MatchStateEnd + +// uint64 timestamp = 1; +inline void MatchStateEnd::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t MatchStateEnd::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t MatchStateEnd::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchStateEnd.timestamp) + return _internal_timestamp(); +} +inline void MatchStateEnd::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void MatchStateEnd::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchStateEnd.timestamp) +} + +// string category = 2; +inline void MatchStateEnd::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& MatchStateEnd::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchStateEnd.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void MatchStateEnd::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchStateEnd.category) +} +inline std::string* MatchStateEnd::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchStateEnd.category) + return _s; +} +inline const std::string& MatchStateEnd::_internal_category() const { + return _impl_.category_.Get(); +} +inline void MatchStateEnd::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* MatchStateEnd::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* MatchStateEnd::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchStateEnd.category) + return _impl_.category_.Release(); +} +inline void MatchStateEnd::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchStateEnd.category) +} + +// string state = 3; +inline void MatchStateEnd::clear_state() { + _impl_.state_.ClearToEmpty(); +} +inline const std::string& MatchStateEnd::state() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchStateEnd.state) + return _internal_state(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void MatchStateEnd::set_state(ArgT0&& arg0, ArgT... args) { + + _impl_.state_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.MatchStateEnd.state) +} +inline std::string* MatchStateEnd::mutable_state() { + std::string* _s = _internal_mutable_state(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchStateEnd.state) + return _s; +} +inline const std::string& MatchStateEnd::_internal_state() const { + return _impl_.state_.Get(); +} +inline void MatchStateEnd::_internal_set_state(const std::string& value) { + + _impl_.state_.Set(value, GetArenaForAllocation()); +} +inline std::string* MatchStateEnd::_internal_mutable_state() { + + return _impl_.state_.Mutable(GetArenaForAllocation()); +} +inline std::string* MatchStateEnd::release_state() { + // @@protoc_insertion_point(field_release:rtech.liveapi.MatchStateEnd.state) + return _impl_.state_.Release(); +} +inline void MatchStateEnd::set_allocated_state(std::string* state) { + if (state != nullptr) { + + } else { + + } + _impl_.state_.SetAllocated(state, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.state_.IsDefault()) { + _impl_.state_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.MatchStateEnd.state) +} + +// repeated .rtech.liveapi.Player winners = 4; +inline int MatchStateEnd::_internal_winners_size() const { + return _impl_.winners_.size(); +} +inline int MatchStateEnd::winners_size() const { + return _internal_winners_size(); +} +inline void MatchStateEnd::clear_winners() { + _impl_.winners_.Clear(); +} +inline ::rtech::liveapi::Player* MatchStateEnd::mutable_winners(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.MatchStateEnd.winners) + return _impl_.winners_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* +MatchStateEnd::mutable_winners() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.MatchStateEnd.winners) + return &_impl_.winners_; +} +inline const ::rtech::liveapi::Player& MatchStateEnd::_internal_winners(int index) const { + return _impl_.winners_.Get(index); +} +inline const ::rtech::liveapi::Player& MatchStateEnd::winners(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.MatchStateEnd.winners) + return _internal_winners(index); +} +inline ::rtech::liveapi::Player* MatchStateEnd::_internal_add_winners() { + return _impl_.winners_.Add(); +} +inline ::rtech::liveapi::Player* MatchStateEnd::add_winners() { + ::rtech::liveapi::Player* _add = _internal_add_winners(); + // @@protoc_insertion_point(field_add:rtech.liveapi.MatchStateEnd.winners) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& +MatchStateEnd::winners() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.MatchStateEnd.winners) + return _impl_.winners_; +} + +// ------------------------------------------------------------------- + +// RingStartClosing + +// uint64 timestamp = 1; +inline void RingStartClosing::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t RingStartClosing::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t RingStartClosing::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingStartClosing.timestamp) + return _internal_timestamp(); +} +inline void RingStartClosing::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void RingStartClosing::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingStartClosing.timestamp) +} + +// string category = 2; +inline void RingStartClosing::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& RingStartClosing::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingStartClosing.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void RingStartClosing::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingStartClosing.category) +} +inline std::string* RingStartClosing::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RingStartClosing.category) + return _s; +} +inline const std::string& RingStartClosing::_internal_category() const { + return _impl_.category_.Get(); +} +inline void RingStartClosing::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* RingStartClosing::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* RingStartClosing::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RingStartClosing.category) + return _impl_.category_.Release(); +} +inline void RingStartClosing::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RingStartClosing.category) +} + +// uint32 stage = 3; +inline void RingStartClosing::clear_stage() { + _impl_.stage_ = 0u; +} +inline uint32_t RingStartClosing::_internal_stage() const { + return _impl_.stage_; +} +inline uint32_t RingStartClosing::stage() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingStartClosing.stage) + return _internal_stage(); +} +inline void RingStartClosing::_internal_set_stage(uint32_t value) { + + _impl_.stage_ = value; +} +inline void RingStartClosing::set_stage(uint32_t value) { + _internal_set_stage(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingStartClosing.stage) +} + +// .rtech.liveapi.Vector3 center = 4; +inline bool RingStartClosing::_internal_has_center() const { + return this != internal_default_instance() && _impl_.center_ != nullptr; +} +inline bool RingStartClosing::has_center() const { + return _internal_has_center(); +} +inline void RingStartClosing::clear_center() { + if (GetArenaForAllocation() == nullptr && _impl_.center_ != nullptr) { + delete _impl_.center_; + } + _impl_.center_ = nullptr; +} +inline const ::rtech::liveapi::Vector3& RingStartClosing::_internal_center() const { + const ::rtech::liveapi::Vector3* p = _impl_.center_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Vector3_default_instance_); +} +inline const ::rtech::liveapi::Vector3& RingStartClosing::center() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingStartClosing.center) + return _internal_center(); +} +inline void RingStartClosing::unsafe_arena_set_allocated_center( + ::rtech::liveapi::Vector3* center) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.center_); + } + _impl_.center_ = center; + if (center) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.RingStartClosing.center) +} +inline ::rtech::liveapi::Vector3* RingStartClosing::release_center() { + + ::rtech::liveapi::Vector3* temp = _impl_.center_; + _impl_.center_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Vector3* RingStartClosing::unsafe_arena_release_center() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RingStartClosing.center) + + ::rtech::liveapi::Vector3* temp = _impl_.center_; + _impl_.center_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Vector3* RingStartClosing::_internal_mutable_center() { + + if (_impl_.center_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Vector3>(GetArenaForAllocation()); + _impl_.center_ = p; + } + return _impl_.center_; +} +inline ::rtech::liveapi::Vector3* RingStartClosing::mutable_center() { + ::rtech::liveapi::Vector3* _msg = _internal_mutable_center(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RingStartClosing.center) + return _msg; +} +inline void RingStartClosing::set_allocated_center(::rtech::liveapi::Vector3* center) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.center_; + } + if (center) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(center); + if (message_arena != submessage_arena) { + center = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, center, submessage_arena); + } + + } else { + + } + _impl_.center_ = center; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RingStartClosing.center) +} + +// float currentRadius = 5; +inline void RingStartClosing::clear_currentradius() { + _impl_.currentradius_ = 0; +} +inline float RingStartClosing::_internal_currentradius() const { + return _impl_.currentradius_; +} +inline float RingStartClosing::currentradius() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingStartClosing.currentRadius) + return _internal_currentradius(); +} +inline void RingStartClosing::_internal_set_currentradius(float value) { + + _impl_.currentradius_ = value; +} +inline void RingStartClosing::set_currentradius(float value) { + _internal_set_currentradius(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingStartClosing.currentRadius) +} + +// float endRadius = 6; +inline void RingStartClosing::clear_endradius() { + _impl_.endradius_ = 0; +} +inline float RingStartClosing::_internal_endradius() const { + return _impl_.endradius_; +} +inline float RingStartClosing::endradius() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingStartClosing.endRadius) + return _internal_endradius(); +} +inline void RingStartClosing::_internal_set_endradius(float value) { + + _impl_.endradius_ = value; +} +inline void RingStartClosing::set_endradius(float value) { + _internal_set_endradius(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingStartClosing.endRadius) +} + +// float shrinkDuration = 7; +inline void RingStartClosing::clear_shrinkduration() { + _impl_.shrinkduration_ = 0; +} +inline float RingStartClosing::_internal_shrinkduration() const { + return _impl_.shrinkduration_; +} +inline float RingStartClosing::shrinkduration() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingStartClosing.shrinkDuration) + return _internal_shrinkduration(); +} +inline void RingStartClosing::_internal_set_shrinkduration(float value) { + + _impl_.shrinkduration_ = value; +} +inline void RingStartClosing::set_shrinkduration(float value) { + _internal_set_shrinkduration(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingStartClosing.shrinkDuration) +} + +// ------------------------------------------------------------------- + +// RingFinishedClosing + +// uint64 timestamp = 1; +inline void RingFinishedClosing::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t RingFinishedClosing::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t RingFinishedClosing::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingFinishedClosing.timestamp) + return _internal_timestamp(); +} +inline void RingFinishedClosing::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void RingFinishedClosing::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingFinishedClosing.timestamp) +} + +// string category = 2; +inline void RingFinishedClosing::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& RingFinishedClosing::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingFinishedClosing.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void RingFinishedClosing::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingFinishedClosing.category) +} +inline std::string* RingFinishedClosing::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RingFinishedClosing.category) + return _s; +} +inline const std::string& RingFinishedClosing::_internal_category() const { + return _impl_.category_.Get(); +} +inline void RingFinishedClosing::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* RingFinishedClosing::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* RingFinishedClosing::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RingFinishedClosing.category) + return _impl_.category_.Release(); +} +inline void RingFinishedClosing::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RingFinishedClosing.category) +} + +// uint32 stage = 3; +inline void RingFinishedClosing::clear_stage() { + _impl_.stage_ = 0u; +} +inline uint32_t RingFinishedClosing::_internal_stage() const { + return _impl_.stage_; +} +inline uint32_t RingFinishedClosing::stage() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingFinishedClosing.stage) + return _internal_stage(); +} +inline void RingFinishedClosing::_internal_set_stage(uint32_t value) { + + _impl_.stage_ = value; +} +inline void RingFinishedClosing::set_stage(uint32_t value) { + _internal_set_stage(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingFinishedClosing.stage) +} + +// .rtech.liveapi.Vector3 center = 4; +inline bool RingFinishedClosing::_internal_has_center() const { + return this != internal_default_instance() && _impl_.center_ != nullptr; +} +inline bool RingFinishedClosing::has_center() const { + return _internal_has_center(); +} +inline void RingFinishedClosing::clear_center() { + if (GetArenaForAllocation() == nullptr && _impl_.center_ != nullptr) { + delete _impl_.center_; + } + _impl_.center_ = nullptr; +} +inline const ::rtech::liveapi::Vector3& RingFinishedClosing::_internal_center() const { + const ::rtech::liveapi::Vector3* p = _impl_.center_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Vector3_default_instance_); +} +inline const ::rtech::liveapi::Vector3& RingFinishedClosing::center() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingFinishedClosing.center) + return _internal_center(); +} +inline void RingFinishedClosing::unsafe_arena_set_allocated_center( + ::rtech::liveapi::Vector3* center) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.center_); + } + _impl_.center_ = center; + if (center) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.RingFinishedClosing.center) +} +inline ::rtech::liveapi::Vector3* RingFinishedClosing::release_center() { + + ::rtech::liveapi::Vector3* temp = _impl_.center_; + _impl_.center_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Vector3* RingFinishedClosing::unsafe_arena_release_center() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RingFinishedClosing.center) + + ::rtech::liveapi::Vector3* temp = _impl_.center_; + _impl_.center_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Vector3* RingFinishedClosing::_internal_mutable_center() { + + if (_impl_.center_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Vector3>(GetArenaForAllocation()); + _impl_.center_ = p; + } + return _impl_.center_; +} +inline ::rtech::liveapi::Vector3* RingFinishedClosing::mutable_center() { + ::rtech::liveapi::Vector3* _msg = _internal_mutable_center(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RingFinishedClosing.center) + return _msg; +} +inline void RingFinishedClosing::set_allocated_center(::rtech::liveapi::Vector3* center) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.center_; + } + if (center) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(center); + if (message_arena != submessage_arena) { + center = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, center, submessage_arena); + } + + } else { + + } + _impl_.center_ = center; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RingFinishedClosing.center) +} + +// float currentRadius = 5; +inline void RingFinishedClosing::clear_currentradius() { + _impl_.currentradius_ = 0; +} +inline float RingFinishedClosing::_internal_currentradius() const { + return _impl_.currentradius_; +} +inline float RingFinishedClosing::currentradius() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingFinishedClosing.currentRadius) + return _internal_currentradius(); +} +inline void RingFinishedClosing::_internal_set_currentradius(float value) { + + _impl_.currentradius_ = value; +} +inline void RingFinishedClosing::set_currentradius(float value) { + _internal_set_currentradius(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingFinishedClosing.currentRadius) +} + +// float shrinkDuration = 7; +inline void RingFinishedClosing::clear_shrinkduration() { + _impl_.shrinkduration_ = 0; +} +inline float RingFinishedClosing::_internal_shrinkduration() const { + return _impl_.shrinkduration_; +} +inline float RingFinishedClosing::shrinkduration() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RingFinishedClosing.shrinkDuration) + return _internal_shrinkduration(); +} +inline void RingFinishedClosing::_internal_set_shrinkduration(float value) { + + _impl_.shrinkduration_ = value; +} +inline void RingFinishedClosing::set_shrinkduration(float value) { + _internal_set_shrinkduration(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RingFinishedClosing.shrinkDuration) +} + +// ------------------------------------------------------------------- + +// PlayerConnected + +// uint64 timestamp = 1; +inline void PlayerConnected::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerConnected::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerConnected::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerConnected.timestamp) + return _internal_timestamp(); +} +inline void PlayerConnected::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerConnected::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerConnected.timestamp) +} + +// string category = 2; +inline void PlayerConnected::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerConnected::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerConnected.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerConnected::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerConnected.category) +} +inline std::string* PlayerConnected::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerConnected.category) + return _s; +} +inline const std::string& PlayerConnected::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerConnected::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerConnected::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerConnected::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerConnected.category) + return _impl_.category_.Release(); +} +inline void PlayerConnected::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerConnected.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool PlayerConnected::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool PlayerConnected::has_player() const { + return _internal_has_player(); +} +inline void PlayerConnected::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerConnected::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerConnected::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerConnected.player) + return _internal_player(); +} +inline void PlayerConnected::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerConnected.player) +} +inline ::rtech::liveapi::Player* PlayerConnected::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerConnected::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerConnected.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerConnected::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* PlayerConnected::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerConnected.player) + return _msg; +} +inline void PlayerConnected::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerConnected.player) +} + +// ------------------------------------------------------------------- + +// PlayerDisconnected + +// uint64 timestamp = 1; +inline void PlayerDisconnected::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerDisconnected::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerDisconnected::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDisconnected.timestamp) + return _internal_timestamp(); +} +inline void PlayerDisconnected::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerDisconnected::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDisconnected.timestamp) +} + +// string category = 2; +inline void PlayerDisconnected::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerDisconnected::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDisconnected.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerDisconnected::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDisconnected.category) +} +inline std::string* PlayerDisconnected::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDisconnected.category) + return _s; +} +inline const std::string& PlayerDisconnected::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerDisconnected::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerDisconnected::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerDisconnected::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDisconnected.category) + return _impl_.category_.Release(); +} +inline void PlayerDisconnected::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDisconnected.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool PlayerDisconnected::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool PlayerDisconnected::has_player() const { + return _internal_has_player(); +} +inline void PlayerDisconnected::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerDisconnected::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerDisconnected::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDisconnected.player) + return _internal_player(); +} +inline void PlayerDisconnected::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerDisconnected.player) +} +inline ::rtech::liveapi::Player* PlayerDisconnected::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerDisconnected::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDisconnected.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerDisconnected::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* PlayerDisconnected::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDisconnected.player) + return _msg; +} +inline void PlayerDisconnected::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDisconnected.player) +} + +// bool canReconnect = 4; +inline void PlayerDisconnected::clear_canreconnect() { + _impl_.canreconnect_ = false; +} +inline bool PlayerDisconnected::_internal_canreconnect() const { + return _impl_.canreconnect_; +} +inline bool PlayerDisconnected::canreconnect() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDisconnected.canReconnect) + return _internal_canreconnect(); +} +inline void PlayerDisconnected::_internal_set_canreconnect(bool value) { + + _impl_.canreconnect_ = value; +} +inline void PlayerDisconnected::set_canreconnect(bool value) { + _internal_set_canreconnect(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDisconnected.canReconnect) +} + +// bool isAlive = 5; +inline void PlayerDisconnected::clear_isalive() { + _impl_.isalive_ = false; +} +inline bool PlayerDisconnected::_internal_isalive() const { + return _impl_.isalive_; +} +inline bool PlayerDisconnected::isalive() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDisconnected.isAlive) + return _internal_isalive(); +} +inline void PlayerDisconnected::_internal_set_isalive(bool value) { + + _impl_.isalive_ = value; +} +inline void PlayerDisconnected::set_isalive(bool value) { + _internal_set_isalive(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDisconnected.isAlive) +} + +// ------------------------------------------------------------------- + +// PlayerStatChanged + +// uint64 timestamp = 1; +inline void PlayerStatChanged::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerStatChanged::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerStatChanged::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerStatChanged.timestamp) + return _internal_timestamp(); +} +inline void PlayerStatChanged::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerStatChanged::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerStatChanged.timestamp) +} + +// string category = 2; +inline void PlayerStatChanged::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerStatChanged::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerStatChanged.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerStatChanged::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerStatChanged.category) +} +inline std::string* PlayerStatChanged::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerStatChanged.category) + return _s; +} +inline const std::string& PlayerStatChanged::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerStatChanged::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerStatChanged::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerStatChanged::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerStatChanged.category) + return _impl_.category_.Release(); +} +inline void PlayerStatChanged::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerStatChanged.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool PlayerStatChanged::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool PlayerStatChanged::has_player() const { + return _internal_has_player(); +} +inline void PlayerStatChanged::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerStatChanged::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerStatChanged::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerStatChanged.player) + return _internal_player(); +} +inline void PlayerStatChanged::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerStatChanged.player) +} +inline ::rtech::liveapi::Player* PlayerStatChanged::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerStatChanged::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerStatChanged.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerStatChanged::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* PlayerStatChanged::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerStatChanged.player) + return _msg; +} +inline void PlayerStatChanged::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerStatChanged.player) +} + +// string statName = 4; +inline void PlayerStatChanged::clear_statname() { + _impl_.statname_.ClearToEmpty(); +} +inline const std::string& PlayerStatChanged::statname() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerStatChanged.statName) + return _internal_statname(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerStatChanged::set_statname(ArgT0&& arg0, ArgT... args) { + + _impl_.statname_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerStatChanged.statName) +} +inline std::string* PlayerStatChanged::mutable_statname() { + std::string* _s = _internal_mutable_statname(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerStatChanged.statName) + return _s; +} +inline const std::string& PlayerStatChanged::_internal_statname() const { + return _impl_.statname_.Get(); +} +inline void PlayerStatChanged::_internal_set_statname(const std::string& value) { + + _impl_.statname_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerStatChanged::_internal_mutable_statname() { + + return _impl_.statname_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerStatChanged::release_statname() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerStatChanged.statName) + return _impl_.statname_.Release(); +} +inline void PlayerStatChanged::set_allocated_statname(std::string* statname) { + if (statname != nullptr) { + + } else { + + } + _impl_.statname_.SetAllocated(statname, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.statname_.IsDefault()) { + _impl_.statname_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerStatChanged.statName) +} + +// uint32 intValue = 5; +inline bool PlayerStatChanged::_internal_has_intvalue() const { + return newValue_case() == kIntValue; +} +inline bool PlayerStatChanged::has_intvalue() const { + return _internal_has_intvalue(); +} +inline void PlayerStatChanged::set_has_intvalue() { + _impl_._oneof_case_[0] = kIntValue; +} +inline void PlayerStatChanged::clear_intvalue() { + if (_internal_has_intvalue()) { + _impl_.newValue_.intvalue_ = 0u; + clear_has_newValue(); + } +} +inline uint32_t PlayerStatChanged::_internal_intvalue() const { + if (_internal_has_intvalue()) { + return _impl_.newValue_.intvalue_; + } + return 0u; +} +inline void PlayerStatChanged::_internal_set_intvalue(uint32_t value) { + if (!_internal_has_intvalue()) { + clear_newValue(); + set_has_intvalue(); + } + _impl_.newValue_.intvalue_ = value; +} +inline uint32_t PlayerStatChanged::intvalue() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerStatChanged.intValue) + return _internal_intvalue(); +} +inline void PlayerStatChanged::set_intvalue(uint32_t value) { + _internal_set_intvalue(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerStatChanged.intValue) +} + +// float floatValue = 6; +inline bool PlayerStatChanged::_internal_has_floatvalue() const { + return newValue_case() == kFloatValue; +} +inline bool PlayerStatChanged::has_floatvalue() const { + return _internal_has_floatvalue(); +} +inline void PlayerStatChanged::set_has_floatvalue() { + _impl_._oneof_case_[0] = kFloatValue; +} +inline void PlayerStatChanged::clear_floatvalue() { + if (_internal_has_floatvalue()) { + _impl_.newValue_.floatvalue_ = 0; + clear_has_newValue(); + } +} +inline float PlayerStatChanged::_internal_floatvalue() const { + if (_internal_has_floatvalue()) { + return _impl_.newValue_.floatvalue_; + } + return 0; +} +inline void PlayerStatChanged::_internal_set_floatvalue(float value) { + if (!_internal_has_floatvalue()) { + clear_newValue(); + set_has_floatvalue(); + } + _impl_.newValue_.floatvalue_ = value; +} +inline float PlayerStatChanged::floatvalue() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerStatChanged.floatValue) + return _internal_floatvalue(); +} +inline void PlayerStatChanged::set_floatvalue(float value) { + _internal_set_floatvalue(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerStatChanged.floatValue) +} + +// bool boolValue = 7; +inline bool PlayerStatChanged::_internal_has_boolvalue() const { + return newValue_case() == kBoolValue; +} +inline bool PlayerStatChanged::has_boolvalue() const { + return _internal_has_boolvalue(); +} +inline void PlayerStatChanged::set_has_boolvalue() { + _impl_._oneof_case_[0] = kBoolValue; +} +inline void PlayerStatChanged::clear_boolvalue() { + if (_internal_has_boolvalue()) { + _impl_.newValue_.boolvalue_ = false; + clear_has_newValue(); + } +} +inline bool PlayerStatChanged::_internal_boolvalue() const { + if (_internal_has_boolvalue()) { + return _impl_.newValue_.boolvalue_; + } + return false; +} +inline void PlayerStatChanged::_internal_set_boolvalue(bool value) { + if (!_internal_has_boolvalue()) { + clear_newValue(); + set_has_boolvalue(); + } + _impl_.newValue_.boolvalue_ = value; +} +inline bool PlayerStatChanged::boolvalue() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerStatChanged.boolValue) + return _internal_boolvalue(); +} +inline void PlayerStatChanged::set_boolvalue(bool value) { + _internal_set_boolvalue(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerStatChanged.boolValue) +} + +inline bool PlayerStatChanged::has_newValue() const { + return newValue_case() != NEWVALUE_NOT_SET; +} +inline void PlayerStatChanged::clear_has_newValue() { + _impl_._oneof_case_[0] = NEWVALUE_NOT_SET; +} +inline PlayerStatChanged::NewValueCase PlayerStatChanged::newValue_case() const { + return PlayerStatChanged::NewValueCase(_impl_._oneof_case_[0]); +} +// ------------------------------------------------------------------- + +// PlayerUpgradeTierChanged + +// uint64 timestamp = 1; +inline void PlayerUpgradeTierChanged::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerUpgradeTierChanged::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerUpgradeTierChanged::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerUpgradeTierChanged.timestamp) + return _internal_timestamp(); +} +inline void PlayerUpgradeTierChanged::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerUpgradeTierChanged::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerUpgradeTierChanged.timestamp) +} + +// string category = 2; +inline void PlayerUpgradeTierChanged::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerUpgradeTierChanged::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerUpgradeTierChanged.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerUpgradeTierChanged::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerUpgradeTierChanged.category) +} +inline std::string* PlayerUpgradeTierChanged::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerUpgradeTierChanged.category) + return _s; +} +inline const std::string& PlayerUpgradeTierChanged::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerUpgradeTierChanged::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerUpgradeTierChanged::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerUpgradeTierChanged::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerUpgradeTierChanged.category) + return _impl_.category_.Release(); +} +inline void PlayerUpgradeTierChanged::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerUpgradeTierChanged.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool PlayerUpgradeTierChanged::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool PlayerUpgradeTierChanged::has_player() const { + return _internal_has_player(); +} +inline void PlayerUpgradeTierChanged::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerUpgradeTierChanged::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerUpgradeTierChanged::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerUpgradeTierChanged.player) + return _internal_player(); +} +inline void PlayerUpgradeTierChanged::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerUpgradeTierChanged.player) +} +inline ::rtech::liveapi::Player* PlayerUpgradeTierChanged::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerUpgradeTierChanged::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerUpgradeTierChanged.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerUpgradeTierChanged::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* PlayerUpgradeTierChanged::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerUpgradeTierChanged.player) + return _msg; +} +inline void PlayerUpgradeTierChanged::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerUpgradeTierChanged.player) +} + +// int32 level = 4; +inline void PlayerUpgradeTierChanged::clear_level() { + _impl_.level_ = 0; +} +inline int32_t PlayerUpgradeTierChanged::_internal_level() const { + return _impl_.level_; +} +inline int32_t PlayerUpgradeTierChanged::level() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerUpgradeTierChanged.level) + return _internal_level(); +} +inline void PlayerUpgradeTierChanged::_internal_set_level(int32_t value) { + + _impl_.level_ = value; +} +inline void PlayerUpgradeTierChanged::set_level(int32_t value) { + _internal_set_level(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerUpgradeTierChanged.level) +} + +// ------------------------------------------------------------------- + +// PlayerDamaged + +// uint64 timestamp = 1; +inline void PlayerDamaged::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerDamaged::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerDamaged::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDamaged.timestamp) + return _internal_timestamp(); +} +inline void PlayerDamaged::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerDamaged::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDamaged.timestamp) +} + +// string category = 2; +inline void PlayerDamaged::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerDamaged::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDamaged.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerDamaged::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDamaged.category) +} +inline std::string* PlayerDamaged::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDamaged.category) + return _s; +} +inline const std::string& PlayerDamaged::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerDamaged::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerDamaged::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerDamaged::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDamaged.category) + return _impl_.category_.Release(); +} +inline void PlayerDamaged::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDamaged.category) +} + +// .rtech.liveapi.Player attacker = 3; +inline bool PlayerDamaged::_internal_has_attacker() const { + return this != internal_default_instance() && _impl_.attacker_ != nullptr; +} +inline bool PlayerDamaged::has_attacker() const { + return _internal_has_attacker(); +} +inline void PlayerDamaged::clear_attacker() { + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerDamaged::_internal_attacker() const { + const ::rtech::liveapi::Player* p = _impl_.attacker_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerDamaged::attacker() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDamaged.attacker) + return _internal_attacker(); +} +inline void PlayerDamaged::unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.attacker_); + } + _impl_.attacker_ = attacker; + if (attacker) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerDamaged.attacker) +} +inline ::rtech::liveapi::Player* PlayerDamaged::release_attacker() { + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerDamaged::unsafe_arena_release_attacker() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDamaged.attacker) + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerDamaged::_internal_mutable_attacker() { + + if (_impl_.attacker_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.attacker_ = p; + } + return _impl_.attacker_; +} +inline ::rtech::liveapi::Player* PlayerDamaged::mutable_attacker() { + ::rtech::liveapi::Player* _msg = _internal_mutable_attacker(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDamaged.attacker) + return _msg; +} +inline void PlayerDamaged::set_allocated_attacker(::rtech::liveapi::Player* attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.attacker_; + } + if (attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(attacker); + if (message_arena != submessage_arena) { + attacker = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, attacker, submessage_arena); + } + + } else { + + } + _impl_.attacker_ = attacker; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDamaged.attacker) +} + +// .rtech.liveapi.Player victim = 4; +inline bool PlayerDamaged::_internal_has_victim() const { + return this != internal_default_instance() && _impl_.victim_ != nullptr; +} +inline bool PlayerDamaged::has_victim() const { + return _internal_has_victim(); +} +inline void PlayerDamaged::clear_victim() { + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerDamaged::_internal_victim() const { + const ::rtech::liveapi::Player* p = _impl_.victim_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerDamaged::victim() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDamaged.victim) + return _internal_victim(); +} +inline void PlayerDamaged::unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.victim_); + } + _impl_.victim_ = victim; + if (victim) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerDamaged.victim) +} +inline ::rtech::liveapi::Player* PlayerDamaged::release_victim() { + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerDamaged::unsafe_arena_release_victim() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDamaged.victim) + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerDamaged::_internal_mutable_victim() { + + if (_impl_.victim_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.victim_ = p; + } + return _impl_.victim_; +} +inline ::rtech::liveapi::Player* PlayerDamaged::mutable_victim() { + ::rtech::liveapi::Player* _msg = _internal_mutable_victim(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDamaged.victim) + return _msg; +} +inline void PlayerDamaged::set_allocated_victim(::rtech::liveapi::Player* victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.victim_; + } + if (victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(victim); + if (message_arena != submessage_arena) { + victim = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, victim, submessage_arena); + } + + } else { + + } + _impl_.victim_ = victim; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDamaged.victim) +} + +// string weapon = 5; +inline void PlayerDamaged::clear_weapon() { + _impl_.weapon_.ClearToEmpty(); +} +inline const std::string& PlayerDamaged::weapon() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDamaged.weapon) + return _internal_weapon(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerDamaged::set_weapon(ArgT0&& arg0, ArgT... args) { + + _impl_.weapon_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDamaged.weapon) +} +inline std::string* PlayerDamaged::mutable_weapon() { + std::string* _s = _internal_mutable_weapon(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDamaged.weapon) + return _s; +} +inline const std::string& PlayerDamaged::_internal_weapon() const { + return _impl_.weapon_.Get(); +} +inline void PlayerDamaged::_internal_set_weapon(const std::string& value) { + + _impl_.weapon_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerDamaged::_internal_mutable_weapon() { + + return _impl_.weapon_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerDamaged::release_weapon() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDamaged.weapon) + return _impl_.weapon_.Release(); +} +inline void PlayerDamaged::set_allocated_weapon(std::string* weapon) { + if (weapon != nullptr) { + + } else { + + } + _impl_.weapon_.SetAllocated(weapon, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.weapon_.IsDefault()) { + _impl_.weapon_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDamaged.weapon) +} + +// uint32 damageInflicted = 6; +inline void PlayerDamaged::clear_damageinflicted() { + _impl_.damageinflicted_ = 0u; +} +inline uint32_t PlayerDamaged::_internal_damageinflicted() const { + return _impl_.damageinflicted_; +} +inline uint32_t PlayerDamaged::damageinflicted() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDamaged.damageInflicted) + return _internal_damageinflicted(); +} +inline void PlayerDamaged::_internal_set_damageinflicted(uint32_t value) { + + _impl_.damageinflicted_ = value; +} +inline void PlayerDamaged::set_damageinflicted(uint32_t value) { + _internal_set_damageinflicted(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDamaged.damageInflicted) +} + +// ------------------------------------------------------------------- + +// PlayerKilled + +// uint64 timestamp = 1; +inline void PlayerKilled::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerKilled::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerKilled::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerKilled.timestamp) + return _internal_timestamp(); +} +inline void PlayerKilled::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerKilled::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerKilled.timestamp) +} + +// string category = 2; +inline void PlayerKilled::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerKilled::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerKilled.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerKilled::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerKilled.category) +} +inline std::string* PlayerKilled::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerKilled.category) + return _s; +} +inline const std::string& PlayerKilled::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerKilled::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerKilled::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerKilled::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerKilled.category) + return _impl_.category_.Release(); +} +inline void PlayerKilled::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerKilled.category) +} + +// .rtech.liveapi.Player attacker = 3; +inline bool PlayerKilled::_internal_has_attacker() const { + return this != internal_default_instance() && _impl_.attacker_ != nullptr; +} +inline bool PlayerKilled::has_attacker() const { + return _internal_has_attacker(); +} +inline void PlayerKilled::clear_attacker() { + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerKilled::_internal_attacker() const { + const ::rtech::liveapi::Player* p = _impl_.attacker_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerKilled::attacker() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerKilled.attacker) + return _internal_attacker(); +} +inline void PlayerKilled::unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.attacker_); + } + _impl_.attacker_ = attacker; + if (attacker) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerKilled.attacker) +} +inline ::rtech::liveapi::Player* PlayerKilled::release_attacker() { + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerKilled::unsafe_arena_release_attacker() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerKilled.attacker) + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerKilled::_internal_mutable_attacker() { + + if (_impl_.attacker_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.attacker_ = p; + } + return _impl_.attacker_; +} +inline ::rtech::liveapi::Player* PlayerKilled::mutable_attacker() { + ::rtech::liveapi::Player* _msg = _internal_mutable_attacker(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerKilled.attacker) + return _msg; +} +inline void PlayerKilled::set_allocated_attacker(::rtech::liveapi::Player* attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.attacker_; + } + if (attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(attacker); + if (message_arena != submessage_arena) { + attacker = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, attacker, submessage_arena); + } + + } else { + + } + _impl_.attacker_ = attacker; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerKilled.attacker) +} + +// .rtech.liveapi.Player victim = 4; +inline bool PlayerKilled::_internal_has_victim() const { + return this != internal_default_instance() && _impl_.victim_ != nullptr; +} +inline bool PlayerKilled::has_victim() const { + return _internal_has_victim(); +} +inline void PlayerKilled::clear_victim() { + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerKilled::_internal_victim() const { + const ::rtech::liveapi::Player* p = _impl_.victim_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerKilled::victim() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerKilled.victim) + return _internal_victim(); +} +inline void PlayerKilled::unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.victim_); + } + _impl_.victim_ = victim; + if (victim) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerKilled.victim) +} +inline ::rtech::liveapi::Player* PlayerKilled::release_victim() { + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerKilled::unsafe_arena_release_victim() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerKilled.victim) + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerKilled::_internal_mutable_victim() { + + if (_impl_.victim_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.victim_ = p; + } + return _impl_.victim_; +} +inline ::rtech::liveapi::Player* PlayerKilled::mutable_victim() { + ::rtech::liveapi::Player* _msg = _internal_mutable_victim(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerKilled.victim) + return _msg; +} +inline void PlayerKilled::set_allocated_victim(::rtech::liveapi::Player* victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.victim_; + } + if (victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(victim); + if (message_arena != submessage_arena) { + victim = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, victim, submessage_arena); + } + + } else { + + } + _impl_.victim_ = victim; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerKilled.victim) +} + +// .rtech.liveapi.Player awardedTo = 5; +inline bool PlayerKilled::_internal_has_awardedto() const { + return this != internal_default_instance() && _impl_.awardedto_ != nullptr; +} +inline bool PlayerKilled::has_awardedto() const { + return _internal_has_awardedto(); +} +inline void PlayerKilled::clear_awardedto() { + if (GetArenaForAllocation() == nullptr && _impl_.awardedto_ != nullptr) { + delete _impl_.awardedto_; + } + _impl_.awardedto_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerKilled::_internal_awardedto() const { + const ::rtech::liveapi::Player* p = _impl_.awardedto_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerKilled::awardedto() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerKilled.awardedTo) + return _internal_awardedto(); +} +inline void PlayerKilled::unsafe_arena_set_allocated_awardedto( + ::rtech::liveapi::Player* awardedto) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.awardedto_); + } + _impl_.awardedto_ = awardedto; + if (awardedto) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerKilled.awardedTo) +} +inline ::rtech::liveapi::Player* PlayerKilled::release_awardedto() { + + ::rtech::liveapi::Player* temp = _impl_.awardedto_; + _impl_.awardedto_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerKilled::unsafe_arena_release_awardedto() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerKilled.awardedTo) + + ::rtech::liveapi::Player* temp = _impl_.awardedto_; + _impl_.awardedto_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerKilled::_internal_mutable_awardedto() { + + if (_impl_.awardedto_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.awardedto_ = p; + } + return _impl_.awardedto_; +} +inline ::rtech::liveapi::Player* PlayerKilled::mutable_awardedto() { + ::rtech::liveapi::Player* _msg = _internal_mutable_awardedto(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerKilled.awardedTo) + return _msg; +} +inline void PlayerKilled::set_allocated_awardedto(::rtech::liveapi::Player* awardedto) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.awardedto_; + } + if (awardedto) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(awardedto); + if (message_arena != submessage_arena) { + awardedto = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, awardedto, submessage_arena); + } + + } else { + + } + _impl_.awardedto_ = awardedto; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerKilled.awardedTo) +} + +// string weapon = 6; +inline void PlayerKilled::clear_weapon() { + _impl_.weapon_.ClearToEmpty(); +} +inline const std::string& PlayerKilled::weapon() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerKilled.weapon) + return _internal_weapon(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerKilled::set_weapon(ArgT0&& arg0, ArgT... args) { + + _impl_.weapon_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerKilled.weapon) +} +inline std::string* PlayerKilled::mutable_weapon() { + std::string* _s = _internal_mutable_weapon(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerKilled.weapon) + return _s; +} +inline const std::string& PlayerKilled::_internal_weapon() const { + return _impl_.weapon_.Get(); +} +inline void PlayerKilled::_internal_set_weapon(const std::string& value) { + + _impl_.weapon_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerKilled::_internal_mutable_weapon() { + + return _impl_.weapon_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerKilled::release_weapon() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerKilled.weapon) + return _impl_.weapon_.Release(); +} +inline void PlayerKilled::set_allocated_weapon(std::string* weapon) { + if (weapon != nullptr) { + + } else { + + } + _impl_.weapon_.SetAllocated(weapon, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.weapon_.IsDefault()) { + _impl_.weapon_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerKilled.weapon) +} + +// ------------------------------------------------------------------- + +// PlayerDowned + +// uint64 timestamp = 1; +inline void PlayerDowned::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerDowned::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerDowned::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDowned.timestamp) + return _internal_timestamp(); +} +inline void PlayerDowned::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerDowned::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDowned.timestamp) +} + +// string category = 2; +inline void PlayerDowned::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerDowned::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDowned.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerDowned::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDowned.category) +} +inline std::string* PlayerDowned::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDowned.category) + return _s; +} +inline const std::string& PlayerDowned::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerDowned::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerDowned::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerDowned::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDowned.category) + return _impl_.category_.Release(); +} +inline void PlayerDowned::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDowned.category) +} + +// .rtech.liveapi.Player attacker = 3; +inline bool PlayerDowned::_internal_has_attacker() const { + return this != internal_default_instance() && _impl_.attacker_ != nullptr; +} +inline bool PlayerDowned::has_attacker() const { + return _internal_has_attacker(); +} +inline void PlayerDowned::clear_attacker() { + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerDowned::_internal_attacker() const { + const ::rtech::liveapi::Player* p = _impl_.attacker_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerDowned::attacker() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDowned.attacker) + return _internal_attacker(); +} +inline void PlayerDowned::unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.attacker_); + } + _impl_.attacker_ = attacker; + if (attacker) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerDowned.attacker) +} +inline ::rtech::liveapi::Player* PlayerDowned::release_attacker() { + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerDowned::unsafe_arena_release_attacker() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDowned.attacker) + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerDowned::_internal_mutable_attacker() { + + if (_impl_.attacker_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.attacker_ = p; + } + return _impl_.attacker_; +} +inline ::rtech::liveapi::Player* PlayerDowned::mutable_attacker() { + ::rtech::liveapi::Player* _msg = _internal_mutable_attacker(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDowned.attacker) + return _msg; +} +inline void PlayerDowned::set_allocated_attacker(::rtech::liveapi::Player* attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.attacker_; + } + if (attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(attacker); + if (message_arena != submessage_arena) { + attacker = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, attacker, submessage_arena); + } + + } else { + + } + _impl_.attacker_ = attacker; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDowned.attacker) +} + +// .rtech.liveapi.Player victim = 4; +inline bool PlayerDowned::_internal_has_victim() const { + return this != internal_default_instance() && _impl_.victim_ != nullptr; +} +inline bool PlayerDowned::has_victim() const { + return _internal_has_victim(); +} +inline void PlayerDowned::clear_victim() { + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerDowned::_internal_victim() const { + const ::rtech::liveapi::Player* p = _impl_.victim_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerDowned::victim() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDowned.victim) + return _internal_victim(); +} +inline void PlayerDowned::unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.victim_); + } + _impl_.victim_ = victim; + if (victim) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerDowned.victim) +} +inline ::rtech::liveapi::Player* PlayerDowned::release_victim() { + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerDowned::unsafe_arena_release_victim() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDowned.victim) + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerDowned::_internal_mutable_victim() { + + if (_impl_.victim_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.victim_ = p; + } + return _impl_.victim_; +} +inline ::rtech::liveapi::Player* PlayerDowned::mutable_victim() { + ::rtech::liveapi::Player* _msg = _internal_mutable_victim(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDowned.victim) + return _msg; +} +inline void PlayerDowned::set_allocated_victim(::rtech::liveapi::Player* victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.victim_; + } + if (victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(victim); + if (message_arena != submessage_arena) { + victim = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, victim, submessage_arena); + } + + } else { + + } + _impl_.victim_ = victim; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDowned.victim) +} + +// string weapon = 5; +inline void PlayerDowned::clear_weapon() { + _impl_.weapon_.ClearToEmpty(); +} +inline const std::string& PlayerDowned::weapon() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerDowned.weapon) + return _internal_weapon(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerDowned::set_weapon(ArgT0&& arg0, ArgT... args) { + + _impl_.weapon_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerDowned.weapon) +} +inline std::string* PlayerDowned::mutable_weapon() { + std::string* _s = _internal_mutable_weapon(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerDowned.weapon) + return _s; +} +inline const std::string& PlayerDowned::_internal_weapon() const { + return _impl_.weapon_.Get(); +} +inline void PlayerDowned::_internal_set_weapon(const std::string& value) { + + _impl_.weapon_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerDowned::_internal_mutable_weapon() { + + return _impl_.weapon_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerDowned::release_weapon() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerDowned.weapon) + return _impl_.weapon_.Release(); +} +inline void PlayerDowned::set_allocated_weapon(std::string* weapon) { + if (weapon != nullptr) { + + } else { + + } + _impl_.weapon_.SetAllocated(weapon, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.weapon_.IsDefault()) { + _impl_.weapon_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerDowned.weapon) +} + +// ------------------------------------------------------------------- + +// PlayerAssist + +// uint64 timestamp = 1; +inline void PlayerAssist::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerAssist::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerAssist::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAssist.timestamp) + return _internal_timestamp(); +} +inline void PlayerAssist::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerAssist::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerAssist.timestamp) +} + +// string category = 2; +inline void PlayerAssist::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerAssist::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAssist.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerAssist::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerAssist.category) +} +inline std::string* PlayerAssist::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerAssist.category) + return _s; +} +inline const std::string& PlayerAssist::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerAssist::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerAssist::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerAssist::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerAssist.category) + return _impl_.category_.Release(); +} +inline void PlayerAssist::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerAssist.category) +} + +// .rtech.liveapi.Player assistant = 3; +inline bool PlayerAssist::_internal_has_assistant() const { + return this != internal_default_instance() && _impl_.assistant_ != nullptr; +} +inline bool PlayerAssist::has_assistant() const { + return _internal_has_assistant(); +} +inline void PlayerAssist::clear_assistant() { + if (GetArenaForAllocation() == nullptr && _impl_.assistant_ != nullptr) { + delete _impl_.assistant_; + } + _impl_.assistant_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerAssist::_internal_assistant() const { + const ::rtech::liveapi::Player* p = _impl_.assistant_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerAssist::assistant() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAssist.assistant) + return _internal_assistant(); +} +inline void PlayerAssist::unsafe_arena_set_allocated_assistant( + ::rtech::liveapi::Player* assistant) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.assistant_); + } + _impl_.assistant_ = assistant; + if (assistant) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerAssist.assistant) +} +inline ::rtech::liveapi::Player* PlayerAssist::release_assistant() { + + ::rtech::liveapi::Player* temp = _impl_.assistant_; + _impl_.assistant_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerAssist::unsafe_arena_release_assistant() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerAssist.assistant) + + ::rtech::liveapi::Player* temp = _impl_.assistant_; + _impl_.assistant_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerAssist::_internal_mutable_assistant() { + + if (_impl_.assistant_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.assistant_ = p; + } + return _impl_.assistant_; +} +inline ::rtech::liveapi::Player* PlayerAssist::mutable_assistant() { + ::rtech::liveapi::Player* _msg = _internal_mutable_assistant(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerAssist.assistant) + return _msg; +} +inline void PlayerAssist::set_allocated_assistant(::rtech::liveapi::Player* assistant) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.assistant_; + } + if (assistant) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(assistant); + if (message_arena != submessage_arena) { + assistant = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, assistant, submessage_arena); + } + + } else { + + } + _impl_.assistant_ = assistant; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerAssist.assistant) +} + +// .rtech.liveapi.Player victim = 4; +inline bool PlayerAssist::_internal_has_victim() const { + return this != internal_default_instance() && _impl_.victim_ != nullptr; +} +inline bool PlayerAssist::has_victim() const { + return _internal_has_victim(); +} +inline void PlayerAssist::clear_victim() { + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerAssist::_internal_victim() const { + const ::rtech::liveapi::Player* p = _impl_.victim_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerAssist::victim() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAssist.victim) + return _internal_victim(); +} +inline void PlayerAssist::unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.victim_); + } + _impl_.victim_ = victim; + if (victim) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerAssist.victim) +} +inline ::rtech::liveapi::Player* PlayerAssist::release_victim() { + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerAssist::unsafe_arena_release_victim() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerAssist.victim) + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerAssist::_internal_mutable_victim() { + + if (_impl_.victim_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.victim_ = p; + } + return _impl_.victim_; +} +inline ::rtech::liveapi::Player* PlayerAssist::mutable_victim() { + ::rtech::liveapi::Player* _msg = _internal_mutable_victim(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerAssist.victim) + return _msg; +} +inline void PlayerAssist::set_allocated_victim(::rtech::liveapi::Player* victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.victim_; + } + if (victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(victim); + if (message_arena != submessage_arena) { + victim = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, victim, submessage_arena); + } + + } else { + + } + _impl_.victim_ = victim; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerAssist.victim) +} + +// string weapon = 5; +inline void PlayerAssist::clear_weapon() { + _impl_.weapon_.ClearToEmpty(); +} +inline const std::string& PlayerAssist::weapon() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAssist.weapon) + return _internal_weapon(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerAssist::set_weapon(ArgT0&& arg0, ArgT... args) { + + _impl_.weapon_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerAssist.weapon) +} +inline std::string* PlayerAssist::mutable_weapon() { + std::string* _s = _internal_mutable_weapon(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerAssist.weapon) + return _s; +} +inline const std::string& PlayerAssist::_internal_weapon() const { + return _impl_.weapon_.Get(); +} +inline void PlayerAssist::_internal_set_weapon(const std::string& value) { + + _impl_.weapon_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerAssist::_internal_mutable_weapon() { + + return _impl_.weapon_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerAssist::release_weapon() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerAssist.weapon) + return _impl_.weapon_.Release(); +} +inline void PlayerAssist::set_allocated_weapon(std::string* weapon) { + if (weapon != nullptr) { + + } else { + + } + _impl_.weapon_.SetAllocated(weapon, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.weapon_.IsDefault()) { + _impl_.weapon_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerAssist.weapon) +} + +// ------------------------------------------------------------------- + +// SquadEliminated + +// uint64 timestamp = 1; +inline void SquadEliminated::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t SquadEliminated::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t SquadEliminated::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.SquadEliminated.timestamp) + return _internal_timestamp(); +} +inline void SquadEliminated::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void SquadEliminated::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.SquadEliminated.timestamp) +} + +// string category = 2; +inline void SquadEliminated::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& SquadEliminated::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.SquadEliminated.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void SquadEliminated::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.SquadEliminated.category) +} +inline std::string* SquadEliminated::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.SquadEliminated.category) + return _s; +} +inline const std::string& SquadEliminated::_internal_category() const { + return _impl_.category_.Get(); +} +inline void SquadEliminated::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* SquadEliminated::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* SquadEliminated::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.SquadEliminated.category) + return _impl_.category_.Release(); +} +inline void SquadEliminated::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.SquadEliminated.category) +} + +// repeated .rtech.liveapi.Player players = 3; +inline int SquadEliminated::_internal_players_size() const { + return _impl_.players_.size(); +} +inline int SquadEliminated::players_size() const { + return _internal_players_size(); +} +inline void SquadEliminated::clear_players() { + _impl_.players_.Clear(); +} +inline ::rtech::liveapi::Player* SquadEliminated::mutable_players(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.SquadEliminated.players) + return _impl_.players_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* +SquadEliminated::mutable_players() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.SquadEliminated.players) + return &_impl_.players_; +} +inline const ::rtech::liveapi::Player& SquadEliminated::_internal_players(int index) const { + return _impl_.players_.Get(index); +} +inline const ::rtech::liveapi::Player& SquadEliminated::players(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.SquadEliminated.players) + return _internal_players(index); +} +inline ::rtech::liveapi::Player* SquadEliminated::_internal_add_players() { + return _impl_.players_.Add(); +} +inline ::rtech::liveapi::Player* SquadEliminated::add_players() { + ::rtech::liveapi::Player* _add = _internal_add_players(); + // @@protoc_insertion_point(field_add:rtech.liveapi.SquadEliminated.players) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& +SquadEliminated::players() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.SquadEliminated.players) + return _impl_.players_; +} + +// ------------------------------------------------------------------- + +// GibraltarShieldAbsorbed + +// uint64 timestamp = 1; +inline void GibraltarShieldAbsorbed::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t GibraltarShieldAbsorbed::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t GibraltarShieldAbsorbed::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GibraltarShieldAbsorbed.timestamp) + return _internal_timestamp(); +} +inline void GibraltarShieldAbsorbed::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void GibraltarShieldAbsorbed::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.GibraltarShieldAbsorbed.timestamp) +} + +// string category = 2; +inline void GibraltarShieldAbsorbed::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& GibraltarShieldAbsorbed::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GibraltarShieldAbsorbed.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void GibraltarShieldAbsorbed::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.GibraltarShieldAbsorbed.category) +} +inline std::string* GibraltarShieldAbsorbed::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GibraltarShieldAbsorbed.category) + return _s; +} +inline const std::string& GibraltarShieldAbsorbed::_internal_category() const { + return _impl_.category_.Get(); +} +inline void GibraltarShieldAbsorbed::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* GibraltarShieldAbsorbed::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* GibraltarShieldAbsorbed::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GibraltarShieldAbsorbed.category) + return _impl_.category_.Release(); +} +inline void GibraltarShieldAbsorbed::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GibraltarShieldAbsorbed.category) +} + +// .rtech.liveapi.Player attacker = 3; +inline bool GibraltarShieldAbsorbed::_internal_has_attacker() const { + return this != internal_default_instance() && _impl_.attacker_ != nullptr; +} +inline bool GibraltarShieldAbsorbed::has_attacker() const { + return _internal_has_attacker(); +} +inline void GibraltarShieldAbsorbed::clear_attacker() { + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; +} +inline const ::rtech::liveapi::Player& GibraltarShieldAbsorbed::_internal_attacker() const { + const ::rtech::liveapi::Player* p = _impl_.attacker_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& GibraltarShieldAbsorbed::attacker() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GibraltarShieldAbsorbed.attacker) + return _internal_attacker(); +} +inline void GibraltarShieldAbsorbed::unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.attacker_); + } + _impl_.attacker_ = attacker; + if (attacker) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.GibraltarShieldAbsorbed.attacker) +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::release_attacker() { + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::unsafe_arena_release_attacker() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GibraltarShieldAbsorbed.attacker) + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::_internal_mutable_attacker() { + + if (_impl_.attacker_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.attacker_ = p; + } + return _impl_.attacker_; +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::mutable_attacker() { + ::rtech::liveapi::Player* _msg = _internal_mutable_attacker(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GibraltarShieldAbsorbed.attacker) + return _msg; +} +inline void GibraltarShieldAbsorbed::set_allocated_attacker(::rtech::liveapi::Player* attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.attacker_; + } + if (attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(attacker); + if (message_arena != submessage_arena) { + attacker = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, attacker, submessage_arena); + } + + } else { + + } + _impl_.attacker_ = attacker; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GibraltarShieldAbsorbed.attacker) +} + +// .rtech.liveapi.Player victim = 4; +inline bool GibraltarShieldAbsorbed::_internal_has_victim() const { + return this != internal_default_instance() && _impl_.victim_ != nullptr; +} +inline bool GibraltarShieldAbsorbed::has_victim() const { + return _internal_has_victim(); +} +inline void GibraltarShieldAbsorbed::clear_victim() { + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; +} +inline const ::rtech::liveapi::Player& GibraltarShieldAbsorbed::_internal_victim() const { + const ::rtech::liveapi::Player* p = _impl_.victim_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& GibraltarShieldAbsorbed::victim() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GibraltarShieldAbsorbed.victim) + return _internal_victim(); +} +inline void GibraltarShieldAbsorbed::unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.victim_); + } + _impl_.victim_ = victim; + if (victim) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.GibraltarShieldAbsorbed.victim) +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::release_victim() { + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::unsafe_arena_release_victim() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GibraltarShieldAbsorbed.victim) + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::_internal_mutable_victim() { + + if (_impl_.victim_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.victim_ = p; + } + return _impl_.victim_; +} +inline ::rtech::liveapi::Player* GibraltarShieldAbsorbed::mutable_victim() { + ::rtech::liveapi::Player* _msg = _internal_mutable_victim(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GibraltarShieldAbsorbed.victim) + return _msg; +} +inline void GibraltarShieldAbsorbed::set_allocated_victim(::rtech::liveapi::Player* victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.victim_; + } + if (victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(victim); + if (message_arena != submessage_arena) { + victim = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, victim, submessage_arena); + } + + } else { + + } + _impl_.victim_ = victim; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GibraltarShieldAbsorbed.victim) +} + +// uint32 damageInflicted = 6; +inline void GibraltarShieldAbsorbed::clear_damageinflicted() { + _impl_.damageinflicted_ = 0u; +} +inline uint32_t GibraltarShieldAbsorbed::_internal_damageinflicted() const { + return _impl_.damageinflicted_; +} +inline uint32_t GibraltarShieldAbsorbed::damageinflicted() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GibraltarShieldAbsorbed.damageInflicted) + return _internal_damageinflicted(); +} +inline void GibraltarShieldAbsorbed::_internal_set_damageinflicted(uint32_t value) { + + _impl_.damageinflicted_ = value; +} +inline void GibraltarShieldAbsorbed::set_damageinflicted(uint32_t value) { + _internal_set_damageinflicted(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.GibraltarShieldAbsorbed.damageInflicted) +} + +// ------------------------------------------------------------------- + +// RevenantForgedShadowDamaged + +// uint64 timestamp = 1; +inline void RevenantForgedShadowDamaged::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t RevenantForgedShadowDamaged::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t RevenantForgedShadowDamaged::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RevenantForgedShadowDamaged.timestamp) + return _internal_timestamp(); +} +inline void RevenantForgedShadowDamaged::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void RevenantForgedShadowDamaged::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RevenantForgedShadowDamaged.timestamp) +} + +// string category = 2; +inline void RevenantForgedShadowDamaged::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& RevenantForgedShadowDamaged::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RevenantForgedShadowDamaged.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void RevenantForgedShadowDamaged::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.RevenantForgedShadowDamaged.category) +} +inline std::string* RevenantForgedShadowDamaged::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RevenantForgedShadowDamaged.category) + return _s; +} +inline const std::string& RevenantForgedShadowDamaged::_internal_category() const { + return _impl_.category_.Get(); +} +inline void RevenantForgedShadowDamaged::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* RevenantForgedShadowDamaged::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* RevenantForgedShadowDamaged::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RevenantForgedShadowDamaged.category) + return _impl_.category_.Release(); +} +inline void RevenantForgedShadowDamaged::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RevenantForgedShadowDamaged.category) +} + +// .rtech.liveapi.Player attacker = 3; +inline bool RevenantForgedShadowDamaged::_internal_has_attacker() const { + return this != internal_default_instance() && _impl_.attacker_ != nullptr; +} +inline bool RevenantForgedShadowDamaged::has_attacker() const { + return _internal_has_attacker(); +} +inline void RevenantForgedShadowDamaged::clear_attacker() { + if (GetArenaForAllocation() == nullptr && _impl_.attacker_ != nullptr) { + delete _impl_.attacker_; + } + _impl_.attacker_ = nullptr; +} +inline const ::rtech::liveapi::Player& RevenantForgedShadowDamaged::_internal_attacker() const { + const ::rtech::liveapi::Player* p = _impl_.attacker_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& RevenantForgedShadowDamaged::attacker() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RevenantForgedShadowDamaged.attacker) + return _internal_attacker(); +} +inline void RevenantForgedShadowDamaged::unsafe_arena_set_allocated_attacker( + ::rtech::liveapi::Player* attacker) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.attacker_); + } + _impl_.attacker_ = attacker; + if (attacker) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.RevenantForgedShadowDamaged.attacker) +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::release_attacker() { + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::unsafe_arena_release_attacker() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RevenantForgedShadowDamaged.attacker) + + ::rtech::liveapi::Player* temp = _impl_.attacker_; + _impl_.attacker_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::_internal_mutable_attacker() { + + if (_impl_.attacker_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.attacker_ = p; + } + return _impl_.attacker_; +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::mutable_attacker() { + ::rtech::liveapi::Player* _msg = _internal_mutable_attacker(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RevenantForgedShadowDamaged.attacker) + return _msg; +} +inline void RevenantForgedShadowDamaged::set_allocated_attacker(::rtech::liveapi::Player* attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.attacker_; + } + if (attacker) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(attacker); + if (message_arena != submessage_arena) { + attacker = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, attacker, submessage_arena); + } + + } else { + + } + _impl_.attacker_ = attacker; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RevenantForgedShadowDamaged.attacker) +} + +// .rtech.liveapi.Player victim = 4; +inline bool RevenantForgedShadowDamaged::_internal_has_victim() const { + return this != internal_default_instance() && _impl_.victim_ != nullptr; +} +inline bool RevenantForgedShadowDamaged::has_victim() const { + return _internal_has_victim(); +} +inline void RevenantForgedShadowDamaged::clear_victim() { + if (GetArenaForAllocation() == nullptr && _impl_.victim_ != nullptr) { + delete _impl_.victim_; + } + _impl_.victim_ = nullptr; +} +inline const ::rtech::liveapi::Player& RevenantForgedShadowDamaged::_internal_victim() const { + const ::rtech::liveapi::Player* p = _impl_.victim_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& RevenantForgedShadowDamaged::victim() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RevenantForgedShadowDamaged.victim) + return _internal_victim(); +} +inline void RevenantForgedShadowDamaged::unsafe_arena_set_allocated_victim( + ::rtech::liveapi::Player* victim) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.victim_); + } + _impl_.victim_ = victim; + if (victim) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.RevenantForgedShadowDamaged.victim) +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::release_victim() { + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::unsafe_arena_release_victim() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RevenantForgedShadowDamaged.victim) + + ::rtech::liveapi::Player* temp = _impl_.victim_; + _impl_.victim_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::_internal_mutable_victim() { + + if (_impl_.victim_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.victim_ = p; + } + return _impl_.victim_; +} +inline ::rtech::liveapi::Player* RevenantForgedShadowDamaged::mutable_victim() { + ::rtech::liveapi::Player* _msg = _internal_mutable_victim(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RevenantForgedShadowDamaged.victim) + return _msg; +} +inline void RevenantForgedShadowDamaged::set_allocated_victim(::rtech::liveapi::Player* victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.victim_; + } + if (victim) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(victim); + if (message_arena != submessage_arena) { + victim = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, victim, submessage_arena); + } + + } else { + + } + _impl_.victim_ = victim; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RevenantForgedShadowDamaged.victim) +} + +// uint32 damageInflicted = 6; +inline void RevenantForgedShadowDamaged::clear_damageinflicted() { + _impl_.damageinflicted_ = 0u; +} +inline uint32_t RevenantForgedShadowDamaged::_internal_damageinflicted() const { + return _impl_.damageinflicted_; +} +inline uint32_t RevenantForgedShadowDamaged::damageinflicted() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RevenantForgedShadowDamaged.damageInflicted) + return _internal_damageinflicted(); +} +inline void RevenantForgedShadowDamaged::_internal_set_damageinflicted(uint32_t value) { + + _impl_.damageinflicted_ = value; +} +inline void RevenantForgedShadowDamaged::set_damageinflicted(uint32_t value) { + _internal_set_damageinflicted(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.RevenantForgedShadowDamaged.damageInflicted) +} + +// ------------------------------------------------------------------- + +// PlayerRespawnTeam + +// uint64 timestamp = 1; +inline void PlayerRespawnTeam::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerRespawnTeam::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerRespawnTeam::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRespawnTeam.timestamp) + return _internal_timestamp(); +} +inline void PlayerRespawnTeam::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerRespawnTeam::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerRespawnTeam.timestamp) +} + +// string category = 2; +inline void PlayerRespawnTeam::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerRespawnTeam::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRespawnTeam.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerRespawnTeam::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerRespawnTeam.category) +} +inline std::string* PlayerRespawnTeam::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerRespawnTeam.category) + return _s; +} +inline const std::string& PlayerRespawnTeam::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerRespawnTeam::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerRespawnTeam::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerRespawnTeam::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerRespawnTeam.category) + return _impl_.category_.Release(); +} +inline void PlayerRespawnTeam::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerRespawnTeam.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool PlayerRespawnTeam::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool PlayerRespawnTeam::has_player() const { + return _internal_has_player(); +} +inline void PlayerRespawnTeam::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerRespawnTeam::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerRespawnTeam::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRespawnTeam.player) + return _internal_player(); +} +inline void PlayerRespawnTeam::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerRespawnTeam.player) +} +inline ::rtech::liveapi::Player* PlayerRespawnTeam::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerRespawnTeam::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerRespawnTeam.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerRespawnTeam::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* PlayerRespawnTeam::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerRespawnTeam.player) + return _msg; +} +inline void PlayerRespawnTeam::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerRespawnTeam.player) +} + +// repeated .rtech.liveapi.Player respawned = 4; +inline int PlayerRespawnTeam::_internal_respawned_size() const { + return _impl_.respawned_.size(); +} +inline int PlayerRespawnTeam::respawned_size() const { + return _internal_respawned_size(); +} +inline void PlayerRespawnTeam::clear_respawned() { + _impl_.respawned_.Clear(); +} +inline ::rtech::liveapi::Player* PlayerRespawnTeam::mutable_respawned(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerRespawnTeam.respawned) + return _impl_.respawned_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >* +PlayerRespawnTeam::mutable_respawned() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.PlayerRespawnTeam.respawned) + return &_impl_.respawned_; +} +inline const ::rtech::liveapi::Player& PlayerRespawnTeam::_internal_respawned(int index) const { + return _impl_.respawned_.Get(index); +} +inline const ::rtech::liveapi::Player& PlayerRespawnTeam::respawned(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRespawnTeam.respawned) + return _internal_respawned(index); +} +inline ::rtech::liveapi::Player* PlayerRespawnTeam::_internal_add_respawned() { + return _impl_.respawned_.Add(); +} +inline ::rtech::liveapi::Player* PlayerRespawnTeam::add_respawned() { + ::rtech::liveapi::Player* _add = _internal_add_respawned(); + // @@protoc_insertion_point(field_add:rtech.liveapi.PlayerRespawnTeam.respawned) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::rtech::liveapi::Player >& +PlayerRespawnTeam::respawned() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.PlayerRespawnTeam.respawned) + return _impl_.respawned_; +} + +// ------------------------------------------------------------------- + +// PlayerRevive + +// uint64 timestamp = 1; +inline void PlayerRevive::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerRevive::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerRevive::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRevive.timestamp) + return _internal_timestamp(); +} +inline void PlayerRevive::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerRevive::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerRevive.timestamp) +} + +// string category = 2; +inline void PlayerRevive::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerRevive::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRevive.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerRevive::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerRevive.category) +} +inline std::string* PlayerRevive::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerRevive.category) + return _s; +} +inline const std::string& PlayerRevive::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerRevive::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerRevive::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerRevive::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerRevive.category) + return _impl_.category_.Release(); +} +inline void PlayerRevive::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerRevive.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool PlayerRevive::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool PlayerRevive::has_player() const { + return _internal_has_player(); +} +inline void PlayerRevive::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerRevive::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerRevive::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRevive.player) + return _internal_player(); +} +inline void PlayerRevive::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerRevive.player) +} +inline ::rtech::liveapi::Player* PlayerRevive::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerRevive::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerRevive.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerRevive::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* PlayerRevive::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerRevive.player) + return _msg; +} +inline void PlayerRevive::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerRevive.player) +} + +// .rtech.liveapi.Player revived = 4; +inline bool PlayerRevive::_internal_has_revived() const { + return this != internal_default_instance() && _impl_.revived_ != nullptr; +} +inline bool PlayerRevive::has_revived() const { + return _internal_has_revived(); +} +inline void PlayerRevive::clear_revived() { + if (GetArenaForAllocation() == nullptr && _impl_.revived_ != nullptr) { + delete _impl_.revived_; + } + _impl_.revived_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerRevive::_internal_revived() const { + const ::rtech::liveapi::Player* p = _impl_.revived_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerRevive::revived() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerRevive.revived) + return _internal_revived(); +} +inline void PlayerRevive::unsafe_arena_set_allocated_revived( + ::rtech::liveapi::Player* revived) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.revived_); + } + _impl_.revived_ = revived; + if (revived) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerRevive.revived) +} +inline ::rtech::liveapi::Player* PlayerRevive::release_revived() { + + ::rtech::liveapi::Player* temp = _impl_.revived_; + _impl_.revived_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerRevive::unsafe_arena_release_revived() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerRevive.revived) + + ::rtech::liveapi::Player* temp = _impl_.revived_; + _impl_.revived_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerRevive::_internal_mutable_revived() { + + if (_impl_.revived_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.revived_ = p; + } + return _impl_.revived_; +} +inline ::rtech::liveapi::Player* PlayerRevive::mutable_revived() { + ::rtech::liveapi::Player* _msg = _internal_mutable_revived(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerRevive.revived) + return _msg; +} +inline void PlayerRevive::set_allocated_revived(::rtech::liveapi::Player* revived) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.revived_; + } + if (revived) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(revived); + if (message_arena != submessage_arena) { + revived = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, revived, submessage_arena); + } + + } else { + + } + _impl_.revived_ = revived; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerRevive.revived) +} + +// ------------------------------------------------------------------- + +// ArenasItemSelected + +// uint64 timestamp = 1; +inline void ArenasItemSelected::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t ArenasItemSelected::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t ArenasItemSelected::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemSelected.timestamp) + return _internal_timestamp(); +} +inline void ArenasItemSelected::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void ArenasItemSelected::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemSelected.timestamp) +} + +// string category = 2; +inline void ArenasItemSelected::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& ArenasItemSelected::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemSelected.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ArenasItemSelected::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemSelected.category) +} +inline std::string* ArenasItemSelected::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ArenasItemSelected.category) + return _s; +} +inline const std::string& ArenasItemSelected::_internal_category() const { + return _impl_.category_.Get(); +} +inline void ArenasItemSelected::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* ArenasItemSelected::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* ArenasItemSelected::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ArenasItemSelected.category) + return _impl_.category_.Release(); +} +inline void ArenasItemSelected::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ArenasItemSelected.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool ArenasItemSelected::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool ArenasItemSelected::has_player() const { + return _internal_has_player(); +} +inline void ArenasItemSelected::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& ArenasItemSelected::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& ArenasItemSelected::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemSelected.player) + return _internal_player(); +} +inline void ArenasItemSelected::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.ArenasItemSelected.player) +} +inline ::rtech::liveapi::Player* ArenasItemSelected::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* ArenasItemSelected::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ArenasItemSelected.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* ArenasItemSelected::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* ArenasItemSelected::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ArenasItemSelected.player) + return _msg; +} +inline void ArenasItemSelected::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ArenasItemSelected.player) +} + +// string item = 4; +inline void ArenasItemSelected::clear_item() { + _impl_.item_.ClearToEmpty(); +} +inline const std::string& ArenasItemSelected::item() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemSelected.item) + return _internal_item(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ArenasItemSelected::set_item(ArgT0&& arg0, ArgT... args) { + + _impl_.item_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemSelected.item) +} +inline std::string* ArenasItemSelected::mutable_item() { + std::string* _s = _internal_mutable_item(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ArenasItemSelected.item) + return _s; +} +inline const std::string& ArenasItemSelected::_internal_item() const { + return _impl_.item_.Get(); +} +inline void ArenasItemSelected::_internal_set_item(const std::string& value) { + + _impl_.item_.Set(value, GetArenaForAllocation()); +} +inline std::string* ArenasItemSelected::_internal_mutable_item() { + + return _impl_.item_.Mutable(GetArenaForAllocation()); +} +inline std::string* ArenasItemSelected::release_item() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ArenasItemSelected.item) + return _impl_.item_.Release(); +} +inline void ArenasItemSelected::set_allocated_item(std::string* item) { + if (item != nullptr) { + + } else { + + } + _impl_.item_.SetAllocated(item, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.item_.IsDefault()) { + _impl_.item_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ArenasItemSelected.item) +} + +// int32 quantity = 5; +inline void ArenasItemSelected::clear_quantity() { + _impl_.quantity_ = 0; +} +inline int32_t ArenasItemSelected::_internal_quantity() const { + return _impl_.quantity_; +} +inline int32_t ArenasItemSelected::quantity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemSelected.quantity) + return _internal_quantity(); +} +inline void ArenasItemSelected::_internal_set_quantity(int32_t value) { + + _impl_.quantity_ = value; +} +inline void ArenasItemSelected::set_quantity(int32_t value) { + _internal_set_quantity(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemSelected.quantity) +} + +// ------------------------------------------------------------------- + +// ArenasItemDeselected + +// uint64 timestamp = 1; +inline void ArenasItemDeselected::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t ArenasItemDeselected::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t ArenasItemDeselected::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemDeselected.timestamp) + return _internal_timestamp(); +} +inline void ArenasItemDeselected::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void ArenasItemDeselected::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemDeselected.timestamp) +} + +// string category = 2; +inline void ArenasItemDeselected::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& ArenasItemDeselected::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemDeselected.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ArenasItemDeselected::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemDeselected.category) +} +inline std::string* ArenasItemDeselected::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ArenasItemDeselected.category) + return _s; +} +inline const std::string& ArenasItemDeselected::_internal_category() const { + return _impl_.category_.Get(); +} +inline void ArenasItemDeselected::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* ArenasItemDeselected::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* ArenasItemDeselected::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ArenasItemDeselected.category) + return _impl_.category_.Release(); +} +inline void ArenasItemDeselected::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ArenasItemDeselected.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool ArenasItemDeselected::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool ArenasItemDeselected::has_player() const { + return _internal_has_player(); +} +inline void ArenasItemDeselected::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& ArenasItemDeselected::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& ArenasItemDeselected::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemDeselected.player) + return _internal_player(); +} +inline void ArenasItemDeselected::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.ArenasItemDeselected.player) +} +inline ::rtech::liveapi::Player* ArenasItemDeselected::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* ArenasItemDeselected::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ArenasItemDeselected.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* ArenasItemDeselected::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* ArenasItemDeselected::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ArenasItemDeselected.player) + return _msg; +} +inline void ArenasItemDeselected::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ArenasItemDeselected.player) +} + +// string item = 4; +inline void ArenasItemDeselected::clear_item() { + _impl_.item_.ClearToEmpty(); +} +inline const std::string& ArenasItemDeselected::item() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemDeselected.item) + return _internal_item(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ArenasItemDeselected::set_item(ArgT0&& arg0, ArgT... args) { + + _impl_.item_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemDeselected.item) +} +inline std::string* ArenasItemDeselected::mutable_item() { + std::string* _s = _internal_mutable_item(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ArenasItemDeselected.item) + return _s; +} +inline const std::string& ArenasItemDeselected::_internal_item() const { + return _impl_.item_.Get(); +} +inline void ArenasItemDeselected::_internal_set_item(const std::string& value) { + + _impl_.item_.Set(value, GetArenaForAllocation()); +} +inline std::string* ArenasItemDeselected::_internal_mutable_item() { + + return _impl_.item_.Mutable(GetArenaForAllocation()); +} +inline std::string* ArenasItemDeselected::release_item() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ArenasItemDeselected.item) + return _impl_.item_.Release(); +} +inline void ArenasItemDeselected::set_allocated_item(std::string* item) { + if (item != nullptr) { + + } else { + + } + _impl_.item_.SetAllocated(item, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.item_.IsDefault()) { + _impl_.item_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ArenasItemDeselected.item) +} + +// int32 quantity = 5; +inline void ArenasItemDeselected::clear_quantity() { + _impl_.quantity_ = 0; +} +inline int32_t ArenasItemDeselected::_internal_quantity() const { + return _impl_.quantity_; +} +inline int32_t ArenasItemDeselected::quantity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ArenasItemDeselected.quantity) + return _internal_quantity(); +} +inline void ArenasItemDeselected::_internal_set_quantity(int32_t value) { + + _impl_.quantity_ = value; +} +inline void ArenasItemDeselected::set_quantity(int32_t value) { + _internal_set_quantity(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ArenasItemDeselected.quantity) +} + +// ------------------------------------------------------------------- + +// InventoryPickUp + +// uint64 timestamp = 1; +inline void InventoryPickUp::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t InventoryPickUp::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t InventoryPickUp::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryPickUp.timestamp) + return _internal_timestamp(); +} +inline void InventoryPickUp::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void InventoryPickUp::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryPickUp.timestamp) +} + +// string category = 2; +inline void InventoryPickUp::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& InventoryPickUp::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryPickUp.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryPickUp::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryPickUp.category) +} +inline std::string* InventoryPickUp::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryPickUp.category) + return _s; +} +inline const std::string& InventoryPickUp::_internal_category() const { + return _impl_.category_.Get(); +} +inline void InventoryPickUp::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryPickUp::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryPickUp::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryPickUp.category) + return _impl_.category_.Release(); +} +inline void InventoryPickUp::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryPickUp.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool InventoryPickUp::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool InventoryPickUp::has_player() const { + return _internal_has_player(); +} +inline void InventoryPickUp::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& InventoryPickUp::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& InventoryPickUp::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryPickUp.player) + return _internal_player(); +} +inline void InventoryPickUp::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.InventoryPickUp.player) +} +inline ::rtech::liveapi::Player* InventoryPickUp::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* InventoryPickUp::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryPickUp.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* InventoryPickUp::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* InventoryPickUp::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryPickUp.player) + return _msg; +} +inline void InventoryPickUp::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryPickUp.player) +} + +// string item = 4; +inline void InventoryPickUp::clear_item() { + _impl_.item_.ClearToEmpty(); +} +inline const std::string& InventoryPickUp::item() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryPickUp.item) + return _internal_item(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryPickUp::set_item(ArgT0&& arg0, ArgT... args) { + + _impl_.item_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryPickUp.item) +} +inline std::string* InventoryPickUp::mutable_item() { + std::string* _s = _internal_mutable_item(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryPickUp.item) + return _s; +} +inline const std::string& InventoryPickUp::_internal_item() const { + return _impl_.item_.Get(); +} +inline void InventoryPickUp::_internal_set_item(const std::string& value) { + + _impl_.item_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryPickUp::_internal_mutable_item() { + + return _impl_.item_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryPickUp::release_item() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryPickUp.item) + return _impl_.item_.Release(); +} +inline void InventoryPickUp::set_allocated_item(std::string* item) { + if (item != nullptr) { + + } else { + + } + _impl_.item_.SetAllocated(item, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.item_.IsDefault()) { + _impl_.item_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryPickUp.item) +} + +// int32 quantity = 5; +inline void InventoryPickUp::clear_quantity() { + _impl_.quantity_ = 0; +} +inline int32_t InventoryPickUp::_internal_quantity() const { + return _impl_.quantity_; +} +inline int32_t InventoryPickUp::quantity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryPickUp.quantity) + return _internal_quantity(); +} +inline void InventoryPickUp::_internal_set_quantity(int32_t value) { + + _impl_.quantity_ = value; +} +inline void InventoryPickUp::set_quantity(int32_t value) { + _internal_set_quantity(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryPickUp.quantity) +} + +// ------------------------------------------------------------------- + +// InventoryDrop + +// uint64 timestamp = 1; +inline void InventoryDrop::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t InventoryDrop::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t InventoryDrop::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryDrop.timestamp) + return _internal_timestamp(); +} +inline void InventoryDrop::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void InventoryDrop::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryDrop.timestamp) +} + +// string category = 2; +inline void InventoryDrop::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& InventoryDrop::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryDrop.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryDrop::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryDrop.category) +} +inline std::string* InventoryDrop::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryDrop.category) + return _s; +} +inline const std::string& InventoryDrop::_internal_category() const { + return _impl_.category_.Get(); +} +inline void InventoryDrop::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryDrop::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryDrop::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryDrop.category) + return _impl_.category_.Release(); +} +inline void InventoryDrop::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryDrop.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool InventoryDrop::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool InventoryDrop::has_player() const { + return _internal_has_player(); +} +inline void InventoryDrop::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& InventoryDrop::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& InventoryDrop::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryDrop.player) + return _internal_player(); +} +inline void InventoryDrop::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.InventoryDrop.player) +} +inline ::rtech::liveapi::Player* InventoryDrop::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* InventoryDrop::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryDrop.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* InventoryDrop::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* InventoryDrop::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryDrop.player) + return _msg; +} +inline void InventoryDrop::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryDrop.player) +} + +// string item = 4; +inline void InventoryDrop::clear_item() { + _impl_.item_.ClearToEmpty(); +} +inline const std::string& InventoryDrop::item() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryDrop.item) + return _internal_item(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryDrop::set_item(ArgT0&& arg0, ArgT... args) { + + _impl_.item_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryDrop.item) +} +inline std::string* InventoryDrop::mutable_item() { + std::string* _s = _internal_mutable_item(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryDrop.item) + return _s; +} +inline const std::string& InventoryDrop::_internal_item() const { + return _impl_.item_.Get(); +} +inline void InventoryDrop::_internal_set_item(const std::string& value) { + + _impl_.item_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryDrop::_internal_mutable_item() { + + return _impl_.item_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryDrop::release_item() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryDrop.item) + return _impl_.item_.Release(); +} +inline void InventoryDrop::set_allocated_item(std::string* item) { + if (item != nullptr) { + + } else { + + } + _impl_.item_.SetAllocated(item, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.item_.IsDefault()) { + _impl_.item_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryDrop.item) +} + +// int32 quantity = 5; +inline void InventoryDrop::clear_quantity() { + _impl_.quantity_ = 0; +} +inline int32_t InventoryDrop::_internal_quantity() const { + return _impl_.quantity_; +} +inline int32_t InventoryDrop::quantity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryDrop.quantity) + return _internal_quantity(); +} +inline void InventoryDrop::_internal_set_quantity(int32_t value) { + + _impl_.quantity_ = value; +} +inline void InventoryDrop::set_quantity(int32_t value) { + _internal_set_quantity(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryDrop.quantity) +} + +// repeated string extraData = 6; +inline int InventoryDrop::_internal_extradata_size() const { + return _impl_.extradata_.size(); +} +inline int InventoryDrop::extradata_size() const { + return _internal_extradata_size(); +} +inline void InventoryDrop::clear_extradata() { + _impl_.extradata_.Clear(); +} +inline std::string* InventoryDrop::add_extradata() { + std::string* _s = _internal_add_extradata(); + // @@protoc_insertion_point(field_add_mutable:rtech.liveapi.InventoryDrop.extraData) + return _s; +} +inline const std::string& InventoryDrop::_internal_extradata(int index) const { + return _impl_.extradata_.Get(index); +} +inline const std::string& InventoryDrop::extradata(int index) const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryDrop.extraData) + return _internal_extradata(index); +} +inline std::string* InventoryDrop::mutable_extradata(int index) { + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryDrop.extraData) + return _impl_.extradata_.Mutable(index); +} +inline void InventoryDrop::set_extradata(int index, const std::string& value) { + _impl_.extradata_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryDrop.extraData) +} +inline void InventoryDrop::set_extradata(int index, std::string&& value) { + _impl_.extradata_.Mutable(index)->assign(std::move(value)); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryDrop.extraData) +} +inline void InventoryDrop::set_extradata(int index, const char* value) { + GOOGLE_DCHECK(value != nullptr); + _impl_.extradata_.Mutable(index)->assign(value); + // @@protoc_insertion_point(field_set_char:rtech.liveapi.InventoryDrop.extraData) +} +inline void InventoryDrop::set_extradata(int index, const char* value, size_t size) { + _impl_.extradata_.Mutable(index)->assign( + reinterpret_cast(value), size); + // @@protoc_insertion_point(field_set_pointer:rtech.liveapi.InventoryDrop.extraData) +} +inline std::string* InventoryDrop::_internal_add_extradata() { + return _impl_.extradata_.Add(); +} +inline void InventoryDrop::add_extradata(const std::string& value) { + _impl_.extradata_.Add()->assign(value); + // @@protoc_insertion_point(field_add:rtech.liveapi.InventoryDrop.extraData) +} +inline void InventoryDrop::add_extradata(std::string&& value) { + _impl_.extradata_.Add(std::move(value)); + // @@protoc_insertion_point(field_add:rtech.liveapi.InventoryDrop.extraData) +} +inline void InventoryDrop::add_extradata(const char* value) { + GOOGLE_DCHECK(value != nullptr); + _impl_.extradata_.Add()->assign(value); + // @@protoc_insertion_point(field_add_char:rtech.liveapi.InventoryDrop.extraData) +} +inline void InventoryDrop::add_extradata(const char* value, size_t size) { + _impl_.extradata_.Add()->assign(reinterpret_cast(value), size); + // @@protoc_insertion_point(field_add_pointer:rtech.liveapi.InventoryDrop.extraData) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& +InventoryDrop::extradata() const { + // @@protoc_insertion_point(field_list:rtech.liveapi.InventoryDrop.extraData) + return _impl_.extradata_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* +InventoryDrop::mutable_extradata() { + // @@protoc_insertion_point(field_mutable_list:rtech.liveapi.InventoryDrop.extraData) + return &_impl_.extradata_; +} + +// ------------------------------------------------------------------- + +// InventoryUse + +// uint64 timestamp = 1; +inline void InventoryUse::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t InventoryUse::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t InventoryUse::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryUse.timestamp) + return _internal_timestamp(); +} +inline void InventoryUse::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void InventoryUse::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryUse.timestamp) +} + +// string category = 2; +inline void InventoryUse::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& InventoryUse::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryUse.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryUse::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryUse.category) +} +inline std::string* InventoryUse::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryUse.category) + return _s; +} +inline const std::string& InventoryUse::_internal_category() const { + return _impl_.category_.Get(); +} +inline void InventoryUse::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryUse::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryUse::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryUse.category) + return _impl_.category_.Release(); +} +inline void InventoryUse::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryUse.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool InventoryUse::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool InventoryUse::has_player() const { + return _internal_has_player(); +} +inline void InventoryUse::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& InventoryUse::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& InventoryUse::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryUse.player) + return _internal_player(); +} +inline void InventoryUse::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.InventoryUse.player) +} +inline ::rtech::liveapi::Player* InventoryUse::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* InventoryUse::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryUse.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* InventoryUse::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* InventoryUse::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryUse.player) + return _msg; +} +inline void InventoryUse::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryUse.player) +} + +// string item = 4; +inline void InventoryUse::clear_item() { + _impl_.item_.ClearToEmpty(); +} +inline const std::string& InventoryUse::item() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryUse.item) + return _internal_item(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void InventoryUse::set_item(ArgT0&& arg0, ArgT... args) { + + _impl_.item_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryUse.item) +} +inline std::string* InventoryUse::mutable_item() { + std::string* _s = _internal_mutable_item(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.InventoryUse.item) + return _s; +} +inline const std::string& InventoryUse::_internal_item() const { + return _impl_.item_.Get(); +} +inline void InventoryUse::_internal_set_item(const std::string& value) { + + _impl_.item_.Set(value, GetArenaForAllocation()); +} +inline std::string* InventoryUse::_internal_mutable_item() { + + return _impl_.item_.Mutable(GetArenaForAllocation()); +} +inline std::string* InventoryUse::release_item() { + // @@protoc_insertion_point(field_release:rtech.liveapi.InventoryUse.item) + return _impl_.item_.Release(); +} +inline void InventoryUse::set_allocated_item(std::string* item) { + if (item != nullptr) { + + } else { + + } + _impl_.item_.SetAllocated(item, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.item_.IsDefault()) { + _impl_.item_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.InventoryUse.item) +} + +// int32 quantity = 5; +inline void InventoryUse::clear_quantity() { + _impl_.quantity_ = 0; +} +inline int32_t InventoryUse::_internal_quantity() const { + return _impl_.quantity_; +} +inline int32_t InventoryUse::quantity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.InventoryUse.quantity) + return _internal_quantity(); +} +inline void InventoryUse::_internal_set_quantity(int32_t value) { + + _impl_.quantity_ = value; +} +inline void InventoryUse::set_quantity(int32_t value) { + _internal_set_quantity(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.InventoryUse.quantity) +} + +// ------------------------------------------------------------------- + +// BannerCollected + +// uint64 timestamp = 1; +inline void BannerCollected::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t BannerCollected::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t BannerCollected::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BannerCollected.timestamp) + return _internal_timestamp(); +} +inline void BannerCollected::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void BannerCollected::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.BannerCollected.timestamp) +} + +// string category = 2; +inline void BannerCollected::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& BannerCollected::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BannerCollected.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void BannerCollected::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.BannerCollected.category) +} +inline std::string* BannerCollected::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.BannerCollected.category) + return _s; +} +inline const std::string& BannerCollected::_internal_category() const { + return _impl_.category_.Get(); +} +inline void BannerCollected::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* BannerCollected::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* BannerCollected::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.BannerCollected.category) + return _impl_.category_.Release(); +} +inline void BannerCollected::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.BannerCollected.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool BannerCollected::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool BannerCollected::has_player() const { + return _internal_has_player(); +} +inline void BannerCollected::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& BannerCollected::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& BannerCollected::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BannerCollected.player) + return _internal_player(); +} +inline void BannerCollected::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.BannerCollected.player) +} +inline ::rtech::liveapi::Player* BannerCollected::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* BannerCollected::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.BannerCollected.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* BannerCollected::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* BannerCollected::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.BannerCollected.player) + return _msg; +} +inline void BannerCollected::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.BannerCollected.player) +} + +// .rtech.liveapi.Player collected = 4; +inline bool BannerCollected::_internal_has_collected() const { + return this != internal_default_instance() && _impl_.collected_ != nullptr; +} +inline bool BannerCollected::has_collected() const { + return _internal_has_collected(); +} +inline void BannerCollected::clear_collected() { + if (GetArenaForAllocation() == nullptr && _impl_.collected_ != nullptr) { + delete _impl_.collected_; + } + _impl_.collected_ = nullptr; +} +inline const ::rtech::liveapi::Player& BannerCollected::_internal_collected() const { + const ::rtech::liveapi::Player* p = _impl_.collected_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& BannerCollected::collected() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BannerCollected.collected) + return _internal_collected(); +} +inline void BannerCollected::unsafe_arena_set_allocated_collected( + ::rtech::liveapi::Player* collected) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.collected_); + } + _impl_.collected_ = collected; + if (collected) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.BannerCollected.collected) +} +inline ::rtech::liveapi::Player* BannerCollected::release_collected() { + + ::rtech::liveapi::Player* temp = _impl_.collected_; + _impl_.collected_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* BannerCollected::unsafe_arena_release_collected() { + // @@protoc_insertion_point(field_release:rtech.liveapi.BannerCollected.collected) + + ::rtech::liveapi::Player* temp = _impl_.collected_; + _impl_.collected_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* BannerCollected::_internal_mutable_collected() { + + if (_impl_.collected_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.collected_ = p; + } + return _impl_.collected_; +} +inline ::rtech::liveapi::Player* BannerCollected::mutable_collected() { + ::rtech::liveapi::Player* _msg = _internal_mutable_collected(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.BannerCollected.collected) + return _msg; +} +inline void BannerCollected::set_allocated_collected(::rtech::liveapi::Player* collected) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.collected_; + } + if (collected) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(collected); + if (message_arena != submessage_arena) { + collected = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, collected, submessage_arena); + } + + } else { + + } + _impl_.collected_ = collected; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.BannerCollected.collected) +} + +// ------------------------------------------------------------------- + +// PlayerAbilityUsed + +// uint64 timestamp = 1; +inline void PlayerAbilityUsed::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t PlayerAbilityUsed::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t PlayerAbilityUsed::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAbilityUsed.timestamp) + return _internal_timestamp(); +} +inline void PlayerAbilityUsed::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void PlayerAbilityUsed::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerAbilityUsed.timestamp) +} + +// string category = 2; +inline void PlayerAbilityUsed::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& PlayerAbilityUsed::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAbilityUsed.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerAbilityUsed::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerAbilityUsed.category) +} +inline std::string* PlayerAbilityUsed::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerAbilityUsed.category) + return _s; +} +inline const std::string& PlayerAbilityUsed::_internal_category() const { + return _impl_.category_.Get(); +} +inline void PlayerAbilityUsed::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerAbilityUsed::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerAbilityUsed::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerAbilityUsed.category) + return _impl_.category_.Release(); +} +inline void PlayerAbilityUsed::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerAbilityUsed.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool PlayerAbilityUsed::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool PlayerAbilityUsed::has_player() const { + return _internal_has_player(); +} +inline void PlayerAbilityUsed::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& PlayerAbilityUsed::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& PlayerAbilityUsed::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAbilityUsed.player) + return _internal_player(); +} +inline void PlayerAbilityUsed::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.PlayerAbilityUsed.player) +} +inline ::rtech::liveapi::Player* PlayerAbilityUsed::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* PlayerAbilityUsed::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerAbilityUsed.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* PlayerAbilityUsed::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* PlayerAbilityUsed::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerAbilityUsed.player) + return _msg; +} +inline void PlayerAbilityUsed::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerAbilityUsed.player) +} + +// string linkedEntity = 4; +inline void PlayerAbilityUsed::clear_linkedentity() { + _impl_.linkedentity_.ClearToEmpty(); +} +inline const std::string& PlayerAbilityUsed::linkedentity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PlayerAbilityUsed.linkedEntity) + return _internal_linkedentity(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PlayerAbilityUsed::set_linkedentity(ArgT0&& arg0, ArgT... args) { + + _impl_.linkedentity_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.PlayerAbilityUsed.linkedEntity) +} +inline std::string* PlayerAbilityUsed::mutable_linkedentity() { + std::string* _s = _internal_mutable_linkedentity(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.PlayerAbilityUsed.linkedEntity) + return _s; +} +inline const std::string& PlayerAbilityUsed::_internal_linkedentity() const { + return _impl_.linkedentity_.Get(); +} +inline void PlayerAbilityUsed::_internal_set_linkedentity(const std::string& value) { + + _impl_.linkedentity_.Set(value, GetArenaForAllocation()); +} +inline std::string* PlayerAbilityUsed::_internal_mutable_linkedentity() { + + return _impl_.linkedentity_.Mutable(GetArenaForAllocation()); +} +inline std::string* PlayerAbilityUsed::release_linkedentity() { + // @@protoc_insertion_point(field_release:rtech.liveapi.PlayerAbilityUsed.linkedEntity) + return _impl_.linkedentity_.Release(); +} +inline void PlayerAbilityUsed::set_allocated_linkedentity(std::string* linkedentity) { + if (linkedentity != nullptr) { + + } else { + + } + _impl_.linkedentity_.SetAllocated(linkedentity, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.linkedentity_.IsDefault()) { + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.PlayerAbilityUsed.linkedEntity) +} + +// ------------------------------------------------------------------- + +// LegendUpgradeSelected + +// uint64 timestamp = 1; +inline void LegendUpgradeSelected::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t LegendUpgradeSelected::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t LegendUpgradeSelected::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LegendUpgradeSelected.timestamp) + return _internal_timestamp(); +} +inline void LegendUpgradeSelected::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void LegendUpgradeSelected::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.LegendUpgradeSelected.timestamp) +} + +// string category = 2; +inline void LegendUpgradeSelected::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& LegendUpgradeSelected::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LegendUpgradeSelected.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void LegendUpgradeSelected::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.LegendUpgradeSelected.category) +} +inline std::string* LegendUpgradeSelected::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.LegendUpgradeSelected.category) + return _s; +} +inline const std::string& LegendUpgradeSelected::_internal_category() const { + return _impl_.category_.Get(); +} +inline void LegendUpgradeSelected::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* LegendUpgradeSelected::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* LegendUpgradeSelected::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.LegendUpgradeSelected.category) + return _impl_.category_.Release(); +} +inline void LegendUpgradeSelected::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.LegendUpgradeSelected.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool LegendUpgradeSelected::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool LegendUpgradeSelected::has_player() const { + return _internal_has_player(); +} +inline void LegendUpgradeSelected::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& LegendUpgradeSelected::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& LegendUpgradeSelected::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LegendUpgradeSelected.player) + return _internal_player(); +} +inline void LegendUpgradeSelected::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.LegendUpgradeSelected.player) +} +inline ::rtech::liveapi::Player* LegendUpgradeSelected::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* LegendUpgradeSelected::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.LegendUpgradeSelected.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* LegendUpgradeSelected::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* LegendUpgradeSelected::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.LegendUpgradeSelected.player) + return _msg; +} +inline void LegendUpgradeSelected::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.LegendUpgradeSelected.player) +} + +// string upgradeName = 4; +inline void LegendUpgradeSelected::clear_upgradename() { + _impl_.upgradename_.ClearToEmpty(); +} +inline const std::string& LegendUpgradeSelected::upgradename() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LegendUpgradeSelected.upgradeName) + return _internal_upgradename(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void LegendUpgradeSelected::set_upgradename(ArgT0&& arg0, ArgT... args) { + + _impl_.upgradename_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.LegendUpgradeSelected.upgradeName) +} +inline std::string* LegendUpgradeSelected::mutable_upgradename() { + std::string* _s = _internal_mutable_upgradename(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.LegendUpgradeSelected.upgradeName) + return _s; +} +inline const std::string& LegendUpgradeSelected::_internal_upgradename() const { + return _impl_.upgradename_.Get(); +} +inline void LegendUpgradeSelected::_internal_set_upgradename(const std::string& value) { + + _impl_.upgradename_.Set(value, GetArenaForAllocation()); +} +inline std::string* LegendUpgradeSelected::_internal_mutable_upgradename() { + + return _impl_.upgradename_.Mutable(GetArenaForAllocation()); +} +inline std::string* LegendUpgradeSelected::release_upgradename() { + // @@protoc_insertion_point(field_release:rtech.liveapi.LegendUpgradeSelected.upgradeName) + return _impl_.upgradename_.Release(); +} +inline void LegendUpgradeSelected::set_allocated_upgradename(std::string* upgradename) { + if (upgradename != nullptr) { + + } else { + + } + _impl_.upgradename_.SetAllocated(upgradename, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.upgradename_.IsDefault()) { + _impl_.upgradename_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.LegendUpgradeSelected.upgradeName) +} + +// string upgradeDesc = 5; +inline void LegendUpgradeSelected::clear_upgradedesc() { + _impl_.upgradedesc_.ClearToEmpty(); +} +inline const std::string& LegendUpgradeSelected::upgradedesc() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LegendUpgradeSelected.upgradeDesc) + return _internal_upgradedesc(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void LegendUpgradeSelected::set_upgradedesc(ArgT0&& arg0, ArgT... args) { + + _impl_.upgradedesc_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.LegendUpgradeSelected.upgradeDesc) +} +inline std::string* LegendUpgradeSelected::mutable_upgradedesc() { + std::string* _s = _internal_mutable_upgradedesc(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.LegendUpgradeSelected.upgradeDesc) + return _s; +} +inline const std::string& LegendUpgradeSelected::_internal_upgradedesc() const { + return _impl_.upgradedesc_.Get(); +} +inline void LegendUpgradeSelected::_internal_set_upgradedesc(const std::string& value) { + + _impl_.upgradedesc_.Set(value, GetArenaForAllocation()); +} +inline std::string* LegendUpgradeSelected::_internal_mutable_upgradedesc() { + + return _impl_.upgradedesc_.Mutable(GetArenaForAllocation()); +} +inline std::string* LegendUpgradeSelected::release_upgradedesc() { + // @@protoc_insertion_point(field_release:rtech.liveapi.LegendUpgradeSelected.upgradeDesc) + return _impl_.upgradedesc_.Release(); +} +inline void LegendUpgradeSelected::set_allocated_upgradedesc(std::string* upgradedesc) { + if (upgradedesc != nullptr) { + + } else { + + } + _impl_.upgradedesc_.SetAllocated(upgradedesc, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.upgradedesc_.IsDefault()) { + _impl_.upgradedesc_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.LegendUpgradeSelected.upgradeDesc) +} + +// int32 level = 6; +inline void LegendUpgradeSelected::clear_level() { + _impl_.level_ = 0; +} +inline int32_t LegendUpgradeSelected::_internal_level() const { + return _impl_.level_; +} +inline int32_t LegendUpgradeSelected::level() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LegendUpgradeSelected.level) + return _internal_level(); +} +inline void LegendUpgradeSelected::_internal_set_level(int32_t value) { + + _impl_.level_ = value; +} +inline void LegendUpgradeSelected::set_level(int32_t value) { + _internal_set_level(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.LegendUpgradeSelected.level) +} + +// ------------------------------------------------------------------- + +// ZiplineUsed + +// uint64 timestamp = 1; +inline void ZiplineUsed::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t ZiplineUsed::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t ZiplineUsed::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ZiplineUsed.timestamp) + return _internal_timestamp(); +} +inline void ZiplineUsed::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void ZiplineUsed::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ZiplineUsed.timestamp) +} + +// string category = 2; +inline void ZiplineUsed::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& ZiplineUsed::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ZiplineUsed.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ZiplineUsed::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ZiplineUsed.category) +} +inline std::string* ZiplineUsed::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ZiplineUsed.category) + return _s; +} +inline const std::string& ZiplineUsed::_internal_category() const { + return _impl_.category_.Get(); +} +inline void ZiplineUsed::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* ZiplineUsed::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* ZiplineUsed::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ZiplineUsed.category) + return _impl_.category_.Release(); +} +inline void ZiplineUsed::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ZiplineUsed.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool ZiplineUsed::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool ZiplineUsed::has_player() const { + return _internal_has_player(); +} +inline void ZiplineUsed::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& ZiplineUsed::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& ZiplineUsed::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ZiplineUsed.player) + return _internal_player(); +} +inline void ZiplineUsed::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.ZiplineUsed.player) +} +inline ::rtech::liveapi::Player* ZiplineUsed::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* ZiplineUsed::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ZiplineUsed.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* ZiplineUsed::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* ZiplineUsed::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ZiplineUsed.player) + return _msg; +} +inline void ZiplineUsed::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ZiplineUsed.player) +} + +// string linkedEntity = 4; +inline void ZiplineUsed::clear_linkedentity() { + _impl_.linkedentity_.ClearToEmpty(); +} +inline const std::string& ZiplineUsed::linkedentity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ZiplineUsed.linkedEntity) + return _internal_linkedentity(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void ZiplineUsed::set_linkedentity(ArgT0&& arg0, ArgT... args) { + + _impl_.linkedentity_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ZiplineUsed.linkedEntity) +} +inline std::string* ZiplineUsed::mutable_linkedentity() { + std::string* _s = _internal_mutable_linkedentity(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ZiplineUsed.linkedEntity) + return _s; +} +inline const std::string& ZiplineUsed::_internal_linkedentity() const { + return _impl_.linkedentity_.Get(); +} +inline void ZiplineUsed::_internal_set_linkedentity(const std::string& value) { + + _impl_.linkedentity_.Set(value, GetArenaForAllocation()); +} +inline std::string* ZiplineUsed::_internal_mutable_linkedentity() { + + return _impl_.linkedentity_.Mutable(GetArenaForAllocation()); +} +inline std::string* ZiplineUsed::release_linkedentity() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ZiplineUsed.linkedEntity) + return _impl_.linkedentity_.Release(); +} +inline void ZiplineUsed::set_allocated_linkedentity(std::string* linkedentity) { + if (linkedentity != nullptr) { + + } else { + + } + _impl_.linkedentity_.SetAllocated(linkedentity, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.linkedentity_.IsDefault()) { + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ZiplineUsed.linkedEntity) +} + +// ------------------------------------------------------------------- + +// GrenadeThrown + +// uint64 timestamp = 1; +inline void GrenadeThrown::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t GrenadeThrown::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t GrenadeThrown::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GrenadeThrown.timestamp) + return _internal_timestamp(); +} +inline void GrenadeThrown::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void GrenadeThrown::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.GrenadeThrown.timestamp) +} + +// string category = 2; +inline void GrenadeThrown::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& GrenadeThrown::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GrenadeThrown.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void GrenadeThrown::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.GrenadeThrown.category) +} +inline std::string* GrenadeThrown::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GrenadeThrown.category) + return _s; +} +inline const std::string& GrenadeThrown::_internal_category() const { + return _impl_.category_.Get(); +} +inline void GrenadeThrown::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* GrenadeThrown::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* GrenadeThrown::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GrenadeThrown.category) + return _impl_.category_.Release(); +} +inline void GrenadeThrown::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GrenadeThrown.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool GrenadeThrown::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool GrenadeThrown::has_player() const { + return _internal_has_player(); +} +inline void GrenadeThrown::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& GrenadeThrown::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& GrenadeThrown::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GrenadeThrown.player) + return _internal_player(); +} +inline void GrenadeThrown::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.GrenadeThrown.player) +} +inline ::rtech::liveapi::Player* GrenadeThrown::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* GrenadeThrown::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GrenadeThrown.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* GrenadeThrown::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* GrenadeThrown::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GrenadeThrown.player) + return _msg; +} +inline void GrenadeThrown::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GrenadeThrown.player) +} + +// string linkedEntity = 4; +inline void GrenadeThrown::clear_linkedentity() { + _impl_.linkedentity_.ClearToEmpty(); +} +inline const std::string& GrenadeThrown::linkedentity() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.GrenadeThrown.linkedEntity) + return _internal_linkedentity(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void GrenadeThrown::set_linkedentity(ArgT0&& arg0, ArgT... args) { + + _impl_.linkedentity_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.GrenadeThrown.linkedEntity) +} +inline std::string* GrenadeThrown::mutable_linkedentity() { + std::string* _s = _internal_mutable_linkedentity(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.GrenadeThrown.linkedEntity) + return _s; +} +inline const std::string& GrenadeThrown::_internal_linkedentity() const { + return _impl_.linkedentity_.Get(); +} +inline void GrenadeThrown::_internal_set_linkedentity(const std::string& value) { + + _impl_.linkedentity_.Set(value, GetArenaForAllocation()); +} +inline std::string* GrenadeThrown::_internal_mutable_linkedentity() { + + return _impl_.linkedentity_.Mutable(GetArenaForAllocation()); +} +inline std::string* GrenadeThrown::release_linkedentity() { + // @@protoc_insertion_point(field_release:rtech.liveapi.GrenadeThrown.linkedEntity) + return _impl_.linkedentity_.Release(); +} +inline void GrenadeThrown::set_allocated_linkedentity(std::string* linkedentity) { + if (linkedentity != nullptr) { + + } else { + + } + _impl_.linkedentity_.SetAllocated(linkedentity, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.linkedentity_.IsDefault()) { + _impl_.linkedentity_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.GrenadeThrown.linkedEntity) +} + +// ------------------------------------------------------------------- + +// BlackMarketAction + +// uint64 timestamp = 1; +inline void BlackMarketAction::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t BlackMarketAction::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t BlackMarketAction::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BlackMarketAction.timestamp) + return _internal_timestamp(); +} +inline void BlackMarketAction::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void BlackMarketAction::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.BlackMarketAction.timestamp) +} + +// string category = 2; +inline void BlackMarketAction::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& BlackMarketAction::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BlackMarketAction.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void BlackMarketAction::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.BlackMarketAction.category) +} +inline std::string* BlackMarketAction::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.BlackMarketAction.category) + return _s; +} +inline const std::string& BlackMarketAction::_internal_category() const { + return _impl_.category_.Get(); +} +inline void BlackMarketAction::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* BlackMarketAction::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* BlackMarketAction::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.BlackMarketAction.category) + return _impl_.category_.Release(); +} +inline void BlackMarketAction::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.BlackMarketAction.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool BlackMarketAction::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool BlackMarketAction::has_player() const { + return _internal_has_player(); +} +inline void BlackMarketAction::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& BlackMarketAction::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& BlackMarketAction::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BlackMarketAction.player) + return _internal_player(); +} +inline void BlackMarketAction::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.BlackMarketAction.player) +} +inline ::rtech::liveapi::Player* BlackMarketAction::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* BlackMarketAction::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.BlackMarketAction.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* BlackMarketAction::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* BlackMarketAction::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.BlackMarketAction.player) + return _msg; +} +inline void BlackMarketAction::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.BlackMarketAction.player) +} + +// string item = 4; +inline void BlackMarketAction::clear_item() { + _impl_.item_.ClearToEmpty(); +} +inline const std::string& BlackMarketAction::item() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.BlackMarketAction.item) + return _internal_item(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void BlackMarketAction::set_item(ArgT0&& arg0, ArgT... args) { + + _impl_.item_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.BlackMarketAction.item) +} +inline std::string* BlackMarketAction::mutable_item() { + std::string* _s = _internal_mutable_item(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.BlackMarketAction.item) + return _s; +} +inline const std::string& BlackMarketAction::_internal_item() const { + return _impl_.item_.Get(); +} +inline void BlackMarketAction::_internal_set_item(const std::string& value) { + + _impl_.item_.Set(value, GetArenaForAllocation()); +} +inline std::string* BlackMarketAction::_internal_mutable_item() { + + return _impl_.item_.Mutable(GetArenaForAllocation()); +} +inline std::string* BlackMarketAction::release_item() { + // @@protoc_insertion_point(field_release:rtech.liveapi.BlackMarketAction.item) + return _impl_.item_.Release(); +} +inline void BlackMarketAction::set_allocated_item(std::string* item) { + if (item != nullptr) { + + } else { + + } + _impl_.item_.SetAllocated(item, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.item_.IsDefault()) { + _impl_.item_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.BlackMarketAction.item) +} + +// ------------------------------------------------------------------- + +// WraithPortal + +// uint64 timestamp = 1; +inline void WraithPortal::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t WraithPortal::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t WraithPortal::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WraithPortal.timestamp) + return _internal_timestamp(); +} +inline void WraithPortal::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void WraithPortal::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.WraithPortal.timestamp) +} + +// string category = 2; +inline void WraithPortal::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& WraithPortal::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WraithPortal.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void WraithPortal::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.WraithPortal.category) +} +inline std::string* WraithPortal::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WraithPortal.category) + return _s; +} +inline const std::string& WraithPortal::_internal_category() const { + return _impl_.category_.Get(); +} +inline void WraithPortal::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* WraithPortal::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* WraithPortal::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WraithPortal.category) + return _impl_.category_.Release(); +} +inline void WraithPortal::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WraithPortal.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool WraithPortal::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool WraithPortal::has_player() const { + return _internal_has_player(); +} +inline void WraithPortal::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& WraithPortal::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& WraithPortal::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WraithPortal.player) + return _internal_player(); +} +inline void WraithPortal::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.WraithPortal.player) +} +inline ::rtech::liveapi::Player* WraithPortal::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* WraithPortal::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WraithPortal.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* WraithPortal::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* WraithPortal::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WraithPortal.player) + return _msg; +} +inline void WraithPortal::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WraithPortal.player) +} + +// ------------------------------------------------------------------- + +// WarpGateUsed + +// uint64 timestamp = 1; +inline void WarpGateUsed::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t WarpGateUsed::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t WarpGateUsed::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WarpGateUsed.timestamp) + return _internal_timestamp(); +} +inline void WarpGateUsed::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void WarpGateUsed::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.WarpGateUsed.timestamp) +} + +// string category = 2; +inline void WarpGateUsed::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& WarpGateUsed::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WarpGateUsed.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void WarpGateUsed::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.WarpGateUsed.category) +} +inline std::string* WarpGateUsed::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WarpGateUsed.category) + return _s; +} +inline const std::string& WarpGateUsed::_internal_category() const { + return _impl_.category_.Get(); +} +inline void WarpGateUsed::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* WarpGateUsed::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* WarpGateUsed::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WarpGateUsed.category) + return _impl_.category_.Release(); +} +inline void WarpGateUsed::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WarpGateUsed.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool WarpGateUsed::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool WarpGateUsed::has_player() const { + return _internal_has_player(); +} +inline void WarpGateUsed::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& WarpGateUsed::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& WarpGateUsed::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WarpGateUsed.player) + return _internal_player(); +} +inline void WarpGateUsed::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.WarpGateUsed.player) +} +inline ::rtech::liveapi::Player* WarpGateUsed::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* WarpGateUsed::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WarpGateUsed.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* WarpGateUsed::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* WarpGateUsed::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WarpGateUsed.player) + return _msg; +} +inline void WarpGateUsed::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WarpGateUsed.player) +} + +// ------------------------------------------------------------------- + +// AmmoUsed + +// uint64 timestamp = 1; +inline void AmmoUsed::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t AmmoUsed::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t AmmoUsed::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.AmmoUsed.timestamp) + return _internal_timestamp(); +} +inline void AmmoUsed::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void AmmoUsed::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.AmmoUsed.timestamp) +} + +// string category = 2; +inline void AmmoUsed::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& AmmoUsed::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.AmmoUsed.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void AmmoUsed::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.AmmoUsed.category) +} +inline std::string* AmmoUsed::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.AmmoUsed.category) + return _s; +} +inline const std::string& AmmoUsed::_internal_category() const { + return _impl_.category_.Get(); +} +inline void AmmoUsed::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* AmmoUsed::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* AmmoUsed::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.AmmoUsed.category) + return _impl_.category_.Release(); +} +inline void AmmoUsed::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.AmmoUsed.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool AmmoUsed::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool AmmoUsed::has_player() const { + return _internal_has_player(); +} +inline void AmmoUsed::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& AmmoUsed::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& AmmoUsed::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.AmmoUsed.player) + return _internal_player(); +} +inline void AmmoUsed::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.AmmoUsed.player) +} +inline ::rtech::liveapi::Player* AmmoUsed::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* AmmoUsed::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.AmmoUsed.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* AmmoUsed::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* AmmoUsed::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.AmmoUsed.player) + return _msg; +} +inline void AmmoUsed::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.AmmoUsed.player) +} + +// string ammoType = 4; +inline void AmmoUsed::clear_ammotype() { + _impl_.ammotype_.ClearToEmpty(); +} +inline const std::string& AmmoUsed::ammotype() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.AmmoUsed.ammoType) + return _internal_ammotype(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void AmmoUsed::set_ammotype(ArgT0&& arg0, ArgT... args) { + + _impl_.ammotype_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.AmmoUsed.ammoType) +} +inline std::string* AmmoUsed::mutable_ammotype() { + std::string* _s = _internal_mutable_ammotype(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.AmmoUsed.ammoType) + return _s; +} +inline const std::string& AmmoUsed::_internal_ammotype() const { + return _impl_.ammotype_.Get(); +} +inline void AmmoUsed::_internal_set_ammotype(const std::string& value) { + + _impl_.ammotype_.Set(value, GetArenaForAllocation()); +} +inline std::string* AmmoUsed::_internal_mutable_ammotype() { + + return _impl_.ammotype_.Mutable(GetArenaForAllocation()); +} +inline std::string* AmmoUsed::release_ammotype() { + // @@protoc_insertion_point(field_release:rtech.liveapi.AmmoUsed.ammoType) + return _impl_.ammotype_.Release(); +} +inline void AmmoUsed::set_allocated_ammotype(std::string* ammotype) { + if (ammotype != nullptr) { + + } else { + + } + _impl_.ammotype_.SetAllocated(ammotype, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.ammotype_.IsDefault()) { + _impl_.ammotype_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.AmmoUsed.ammoType) +} + +// uint32 amountUsed = 5; +inline void AmmoUsed::clear_amountused() { + _impl_.amountused_ = 0u; +} +inline uint32_t AmmoUsed::_internal_amountused() const { + return _impl_.amountused_; +} +inline uint32_t AmmoUsed::amountused() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.AmmoUsed.amountUsed) + return _internal_amountused(); +} +inline void AmmoUsed::_internal_set_amountused(uint32_t value) { + + _impl_.amountused_ = value; +} +inline void AmmoUsed::set_amountused(uint32_t value) { + _internal_set_amountused(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.AmmoUsed.amountUsed) +} + +// uint32 oldAmmoCount = 6; +inline void AmmoUsed::clear_oldammocount() { + _impl_.oldammocount_ = 0u; +} +inline uint32_t AmmoUsed::_internal_oldammocount() const { + return _impl_.oldammocount_; +} +inline uint32_t AmmoUsed::oldammocount() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.AmmoUsed.oldAmmoCount) + return _internal_oldammocount(); +} +inline void AmmoUsed::_internal_set_oldammocount(uint32_t value) { + + _impl_.oldammocount_ = value; +} +inline void AmmoUsed::set_oldammocount(uint32_t value) { + _internal_set_oldammocount(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.AmmoUsed.oldAmmoCount) +} + +// uint32 newAmmoCount = 7; +inline void AmmoUsed::clear_newammocount() { + _impl_.newammocount_ = 0u; +} +inline uint32_t AmmoUsed::_internal_newammocount() const { + return _impl_.newammocount_; +} +inline uint32_t AmmoUsed::newammocount() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.AmmoUsed.newAmmoCount) + return _internal_newammocount(); +} +inline void AmmoUsed::_internal_set_newammocount(uint32_t value) { + + _impl_.newammocount_ = value; +} +inline void AmmoUsed::set_newammocount(uint32_t value) { + _internal_set_newammocount(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.AmmoUsed.newAmmoCount) +} + +// ------------------------------------------------------------------- + +// WeaponSwitched + +// uint64 timestamp = 1; +inline void WeaponSwitched::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t WeaponSwitched::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t WeaponSwitched::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WeaponSwitched.timestamp) + return _internal_timestamp(); +} +inline void WeaponSwitched::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void WeaponSwitched::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.WeaponSwitched.timestamp) +} + +// string category = 2; +inline void WeaponSwitched::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& WeaponSwitched::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WeaponSwitched.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void WeaponSwitched::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.WeaponSwitched.category) +} +inline std::string* WeaponSwitched::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WeaponSwitched.category) + return _s; +} +inline const std::string& WeaponSwitched::_internal_category() const { + return _impl_.category_.Get(); +} +inline void WeaponSwitched::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* WeaponSwitched::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* WeaponSwitched::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WeaponSwitched.category) + return _impl_.category_.Release(); +} +inline void WeaponSwitched::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WeaponSwitched.category) +} + +// .rtech.liveapi.Player player = 3; +inline bool WeaponSwitched::_internal_has_player() const { + return this != internal_default_instance() && _impl_.player_ != nullptr; +} +inline bool WeaponSwitched::has_player() const { + return _internal_has_player(); +} +inline void WeaponSwitched::clear_player() { + if (GetArenaForAllocation() == nullptr && _impl_.player_ != nullptr) { + delete _impl_.player_; + } + _impl_.player_ = nullptr; +} +inline const ::rtech::liveapi::Player& WeaponSwitched::_internal_player() const { + const ::rtech::liveapi::Player* p = _impl_.player_; + return p != nullptr ? *p : reinterpret_cast( + ::rtech::liveapi::_Player_default_instance_); +} +inline const ::rtech::liveapi::Player& WeaponSwitched::player() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WeaponSwitched.player) + return _internal_player(); +} +inline void WeaponSwitched::unsafe_arena_set_allocated_player( + ::rtech::liveapi::Player* player) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.player_); + } + _impl_.player_ = player; + if (player) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.WeaponSwitched.player) +} +inline ::rtech::liveapi::Player* WeaponSwitched::release_player() { + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::rtech::liveapi::Player* WeaponSwitched::unsafe_arena_release_player() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WeaponSwitched.player) + + ::rtech::liveapi::Player* temp = _impl_.player_; + _impl_.player_ = nullptr; + return temp; +} +inline ::rtech::liveapi::Player* WeaponSwitched::_internal_mutable_player() { + + if (_impl_.player_ == nullptr) { + auto* p = CreateMaybeMessage<::rtech::liveapi::Player>(GetArenaForAllocation()); + _impl_.player_ = p; + } + return _impl_.player_; +} +inline ::rtech::liveapi::Player* WeaponSwitched::mutable_player() { + ::rtech::liveapi::Player* _msg = _internal_mutable_player(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WeaponSwitched.player) + return _msg; +} +inline void WeaponSwitched::set_allocated_player(::rtech::liveapi::Player* player) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete _impl_.player_; + } + if (player) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(player); + if (message_arena != submessage_arena) { + player = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, player, submessage_arena); + } + + } else { + + } + _impl_.player_ = player; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WeaponSwitched.player) +} + +// string oldWeapon = 4; +inline void WeaponSwitched::clear_oldweapon() { + _impl_.oldweapon_.ClearToEmpty(); +} +inline const std::string& WeaponSwitched::oldweapon() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WeaponSwitched.oldWeapon) + return _internal_oldweapon(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void WeaponSwitched::set_oldweapon(ArgT0&& arg0, ArgT... args) { + + _impl_.oldweapon_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.WeaponSwitched.oldWeapon) +} +inline std::string* WeaponSwitched::mutable_oldweapon() { + std::string* _s = _internal_mutable_oldweapon(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WeaponSwitched.oldWeapon) + return _s; +} +inline const std::string& WeaponSwitched::_internal_oldweapon() const { + return _impl_.oldweapon_.Get(); +} +inline void WeaponSwitched::_internal_set_oldweapon(const std::string& value) { + + _impl_.oldweapon_.Set(value, GetArenaForAllocation()); +} +inline std::string* WeaponSwitched::_internal_mutable_oldweapon() { + + return _impl_.oldweapon_.Mutable(GetArenaForAllocation()); +} +inline std::string* WeaponSwitched::release_oldweapon() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WeaponSwitched.oldWeapon) + return _impl_.oldweapon_.Release(); +} +inline void WeaponSwitched::set_allocated_oldweapon(std::string* oldweapon) { + if (oldweapon != nullptr) { + + } else { + + } + _impl_.oldweapon_.SetAllocated(oldweapon, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.oldweapon_.IsDefault()) { + _impl_.oldweapon_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WeaponSwitched.oldWeapon) +} + +// string newWeapon = 5; +inline void WeaponSwitched::clear_newweapon() { + _impl_.newweapon_.ClearToEmpty(); +} +inline const std::string& WeaponSwitched::newweapon() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.WeaponSwitched.newWeapon) + return _internal_newweapon(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void WeaponSwitched::set_newweapon(ArgT0&& arg0, ArgT... args) { + + _impl_.newweapon_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.WeaponSwitched.newWeapon) +} +inline std::string* WeaponSwitched::mutable_newweapon() { + std::string* _s = _internal_mutable_newweapon(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.WeaponSwitched.newWeapon) + return _s; +} +inline const std::string& WeaponSwitched::_internal_newweapon() const { + return _impl_.newweapon_.Get(); +} +inline void WeaponSwitched::_internal_set_newweapon(const std::string& value) { + + _impl_.newweapon_.Set(value, GetArenaForAllocation()); +} +inline std::string* WeaponSwitched::_internal_mutable_newweapon() { + + return _impl_.newweapon_.Mutable(GetArenaForAllocation()); +} +inline std::string* WeaponSwitched::release_newweapon() { + // @@protoc_insertion_point(field_release:rtech.liveapi.WeaponSwitched.newWeapon) + return _impl_.newweapon_.Release(); +} +inline void WeaponSwitched::set_allocated_newweapon(std::string* newweapon) { + if (newweapon != nullptr) { + + } else { + + } + _impl_.newweapon_.SetAllocated(newweapon, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.newweapon_.IsDefault()) { + _impl_.newweapon_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.WeaponSwitched.newWeapon) +} + +// ------------------------------------------------------------------- + +// CustomEvent + +// uint64 timestamp = 1; +inline void CustomEvent::clear_timestamp() { + _impl_.timestamp_ = uint64_t{0u}; +} +inline uint64_t CustomEvent::_internal_timestamp() const { + return _impl_.timestamp_; +} +inline uint64_t CustomEvent::timestamp() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomEvent.timestamp) + return _internal_timestamp(); +} +inline void CustomEvent::_internal_set_timestamp(uint64_t value) { + + _impl_.timestamp_ = value; +} +inline void CustomEvent::set_timestamp(uint64_t value) { + _internal_set_timestamp(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomEvent.timestamp) +} + +// string category = 2; +inline void CustomEvent::clear_category() { + _impl_.category_.ClearToEmpty(); +} +inline const std::string& CustomEvent::category() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomEvent.category) + return _internal_category(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomEvent::set_category(ArgT0&& arg0, ArgT... args) { + + _impl_.category_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomEvent.category) +} +inline std::string* CustomEvent::mutable_category() { + std::string* _s = _internal_mutable_category(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomEvent.category) + return _s; +} +inline const std::string& CustomEvent::_internal_category() const { + return _impl_.category_.Get(); +} +inline void CustomEvent::_internal_set_category(const std::string& value) { + + _impl_.category_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomEvent::_internal_mutable_category() { + + return _impl_.category_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomEvent::release_category() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomEvent.category) + return _impl_.category_.Release(); +} +inline void CustomEvent::set_allocated_category(std::string* category) { + if (category != nullptr) { + + } else { + + } + _impl_.category_.SetAllocated(category, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.category_.IsDefault()) { + _impl_.category_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomEvent.category) +} + +// string name = 3; +inline void CustomEvent::clear_name() { + _impl_.name_.ClearToEmpty(); +} +inline const std::string& CustomEvent::name() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomEvent.name) + return _internal_name(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomEvent::set_name(ArgT0&& arg0, ArgT... args) { + + _impl_.name_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomEvent.name) +} +inline std::string* CustomEvent::mutable_name() { + std::string* _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomEvent.name) + return _s; +} +inline const std::string& CustomEvent::_internal_name() const { + return _impl_.name_.Get(); +} +inline void CustomEvent::_internal_set_name(const std::string& value) { + + _impl_.name_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomEvent::_internal_mutable_name() { + + return _impl_.name_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomEvent::release_name() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomEvent.name) + return _impl_.name_.Release(); +} +inline void CustomEvent::set_allocated_name(std::string* name) { + if (name != nullptr) { + + } else { + + } + _impl_.name_.SetAllocated(name, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.name_.IsDefault()) { + _impl_.name_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomEvent.name) +} + +// .google.protobuf.Struct data = 4; +inline bool CustomEvent::_internal_has_data() const { + return this != internal_default_instance() && _impl_.data_ != nullptr; +} +inline bool CustomEvent::has_data() const { + return _internal_has_data(); +} +inline const ::PROTOBUF_NAMESPACE_ID::Struct& CustomEvent::_internal_data() const { + const ::PROTOBUF_NAMESPACE_ID::Struct* p = _impl_.data_; + return p != nullptr ? *p : reinterpret_cast( + ::PROTOBUF_NAMESPACE_ID::_Struct_default_instance_); +} +inline const ::PROTOBUF_NAMESPACE_ID::Struct& CustomEvent::data() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomEvent.data) + return _internal_data(); +} +inline void CustomEvent::unsafe_arena_set_allocated_data( + ::PROTOBUF_NAMESPACE_ID::Struct* data) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.data_); + } + _impl_.data_ = data; + if (data) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.CustomEvent.data) +} +inline ::PROTOBUF_NAMESPACE_ID::Struct* CustomEvent::release_data() { + + ::PROTOBUF_NAMESPACE_ID::Struct* temp = _impl_.data_; + _impl_.data_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::PROTOBUF_NAMESPACE_ID::Struct* CustomEvent::unsafe_arena_release_data() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomEvent.data) + + ::PROTOBUF_NAMESPACE_ID::Struct* temp = _impl_.data_; + _impl_.data_ = nullptr; + return temp; +} +inline ::PROTOBUF_NAMESPACE_ID::Struct* CustomEvent::_internal_mutable_data() { + + if (_impl_.data_ == nullptr) { + auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Struct>(GetArenaForAllocation()); + _impl_.data_ = p; + } + return _impl_.data_; +} +inline ::PROTOBUF_NAMESPACE_ID::Struct* CustomEvent::mutable_data() { + ::PROTOBUF_NAMESPACE_ID::Struct* _msg = _internal_mutable_data(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomEvent.data) + return _msg; +} +inline void CustomEvent::set_allocated_data(::PROTOBUF_NAMESPACE_ID::Struct* data) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.data_); + } + if (data) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena( + reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(data)); + if (message_arena != submessage_arena) { + data = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, data, submessage_arena); + } + + } else { + + } + _impl_.data_ = data; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomEvent.data) +} + +// ------------------------------------------------------------------- + +// ChangeCamera + +// .rtech.liveapi.PlayerOfInterest poi = 1; +inline bool ChangeCamera::_internal_has_poi() const { + return target_case() == kPoi; +} +inline bool ChangeCamera::has_poi() const { + return _internal_has_poi(); +} +inline void ChangeCamera::set_has_poi() { + _impl_._oneof_case_[0] = kPoi; +} +inline void ChangeCamera::clear_poi() { + if (_internal_has_poi()) { + _impl_.target_.poi_ = 0; + clear_has_target(); + } +} +inline ::rtech::liveapi::PlayerOfInterest ChangeCamera::_internal_poi() const { + if (_internal_has_poi()) { + return static_cast< ::rtech::liveapi::PlayerOfInterest >(_impl_.target_.poi_); + } + return static_cast< ::rtech::liveapi::PlayerOfInterest >(0); +} +inline ::rtech::liveapi::PlayerOfInterest ChangeCamera::poi() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ChangeCamera.poi) + return _internal_poi(); +} +inline void ChangeCamera::_internal_set_poi(::rtech::liveapi::PlayerOfInterest value) { + if (!_internal_has_poi()) { + clear_target(); + set_has_poi(); + } + _impl_.target_.poi_ = value; +} +inline void ChangeCamera::set_poi(::rtech::liveapi::PlayerOfInterest value) { + _internal_set_poi(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.ChangeCamera.poi) +} + +// string name = 2; +inline bool ChangeCamera::_internal_has_name() const { + return target_case() == kName; +} +inline bool ChangeCamera::has_name() const { + return _internal_has_name(); +} +inline void ChangeCamera::set_has_name() { + _impl_._oneof_case_[0] = kName; +} +inline void ChangeCamera::clear_name() { + if (_internal_has_name()) { + _impl_.target_.name_.Destroy(); + clear_has_target(); + } +} +inline const std::string& ChangeCamera::name() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.ChangeCamera.name) + return _internal_name(); +} +template +inline void ChangeCamera::set_name(ArgT0&& arg0, ArgT... args) { + if (!_internal_has_name()) { + clear_target(); + set_has_name(); + _impl_.target_.name_.InitDefault(); + } + _impl_.target_.name_.Set( static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.ChangeCamera.name) +} +inline std::string* ChangeCamera::mutable_name() { + std::string* _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.ChangeCamera.name) + return _s; +} +inline const std::string& ChangeCamera::_internal_name() const { + if (_internal_has_name()) { + return _impl_.target_.name_.Get(); + } + return ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(); +} +inline void ChangeCamera::_internal_set_name(const std::string& value) { + if (!_internal_has_name()) { + clear_target(); + set_has_name(); + _impl_.target_.name_.InitDefault(); + } + _impl_.target_.name_.Set(value, GetArenaForAllocation()); +} +inline std::string* ChangeCamera::_internal_mutable_name() { + if (!_internal_has_name()) { + clear_target(); + set_has_name(); + _impl_.target_.name_.InitDefault(); + } + return _impl_.target_.name_.Mutable( GetArenaForAllocation()); +} +inline std::string* ChangeCamera::release_name() { + // @@protoc_insertion_point(field_release:rtech.liveapi.ChangeCamera.name) + if (_internal_has_name()) { + clear_has_target(); + return _impl_.target_.name_.Release(); + } else { + return nullptr; + } +} +inline void ChangeCamera::set_allocated_name(std::string* name) { + if (has_target()) { + clear_target(); + } + if (name != nullptr) { + set_has_name(); + _impl_.target_.name_.InitAllocated(name, GetArenaForAllocation()); + } + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.ChangeCamera.name) +} + +inline bool ChangeCamera::has_target() const { + return target_case() != TARGET_NOT_SET; +} +inline void ChangeCamera::clear_has_target() { + _impl_._oneof_case_[0] = TARGET_NOT_SET; +} +inline ChangeCamera::TargetCase ChangeCamera::target_case() const { + return ChangeCamera::TargetCase(_impl_._oneof_case_[0]); +} +// ------------------------------------------------------------------- + +// PauseToggle + +// float preTimer = 1; +inline void PauseToggle::clear_pretimer() { + _impl_.pretimer_ = 0; +} +inline float PauseToggle::_internal_pretimer() const { + return _impl_.pretimer_; +} +inline float PauseToggle::pretimer() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.PauseToggle.preTimer) + return _internal_pretimer(); +} +inline void PauseToggle::_internal_set_pretimer(float value) { + + _impl_.pretimer_ = value; +} +inline void PauseToggle::set_pretimer(float value) { + _internal_set_pretimer(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.PauseToggle.preTimer) +} + +// ------------------------------------------------------------------- + +// CustomMatch_CreateLobby + +// ------------------------------------------------------------------- + +// CustomMatch_JoinLobby + +// string roleToken = 1; +inline void CustomMatch_JoinLobby::clear_roletoken() { + _impl_.roletoken_.ClearToEmpty(); +} +inline const std::string& CustomMatch_JoinLobby::roletoken() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_JoinLobby.roleToken) + return _internal_roletoken(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_JoinLobby::set_roletoken(ArgT0&& arg0, ArgT... args) { + + _impl_.roletoken_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_JoinLobby.roleToken) +} +inline std::string* CustomMatch_JoinLobby::mutable_roletoken() { + std::string* _s = _internal_mutable_roletoken(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_JoinLobby.roleToken) + return _s; +} +inline const std::string& CustomMatch_JoinLobby::_internal_roletoken() const { + return _impl_.roletoken_.Get(); +} +inline void CustomMatch_JoinLobby::_internal_set_roletoken(const std::string& value) { + + _impl_.roletoken_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_JoinLobby::_internal_mutable_roletoken() { + + return _impl_.roletoken_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_JoinLobby::release_roletoken() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_JoinLobby.roleToken) + return _impl_.roletoken_.Release(); +} +inline void CustomMatch_JoinLobby::set_allocated_roletoken(std::string* roletoken) { + if (roletoken != nullptr) { + + } else { + + } + _impl_.roletoken_.SetAllocated(roletoken, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.roletoken_.IsDefault()) { + _impl_.roletoken_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_JoinLobby.roleToken) +} + +// ------------------------------------------------------------------- + +// CustomMatch_LeaveLobby + +// ------------------------------------------------------------------- + +// CustomMatch_SetReady + +// bool isReady = 1; +inline void CustomMatch_SetReady::clear_isready() { + _impl_.isready_ = false; +} +inline bool CustomMatch_SetReady::_internal_isready() const { + return _impl_.isready_; +} +inline bool CustomMatch_SetReady::isready() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetReady.isReady) + return _internal_isready(); +} +inline void CustomMatch_SetReady::_internal_set_isready(bool value) { + + _impl_.isready_ = value; +} +inline void CustomMatch_SetReady::set_isready(bool value) { + _internal_set_isready(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetReady.isReady) +} + +// ------------------------------------------------------------------- + +// CustomMatch_GetLobbyPlayers + +// ------------------------------------------------------------------- + +// CustomMatch_SetMatchmaking + +// bool enabled = 1; +inline void CustomMatch_SetMatchmaking::clear_enabled() { + _impl_.enabled_ = false; +} +inline bool CustomMatch_SetMatchmaking::_internal_enabled() const { + return _impl_.enabled_; +} +inline bool CustomMatch_SetMatchmaking::enabled() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetMatchmaking.enabled) + return _internal_enabled(); +} +inline void CustomMatch_SetMatchmaking::_internal_set_enabled(bool value) { + + _impl_.enabled_ = value; +} +inline void CustomMatch_SetMatchmaking::set_enabled(bool value) { + _internal_set_enabled(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetMatchmaking.enabled) +} + +// ------------------------------------------------------------------- + +// CustomMatch_SetTeam + +// int32 teamId = 1; +inline void CustomMatch_SetTeam::clear_teamid() { + _impl_.teamid_ = 0; +} +inline int32_t CustomMatch_SetTeam::_internal_teamid() const { + return _impl_.teamid_; +} +inline int32_t CustomMatch_SetTeam::teamid() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetTeam.teamId) + return _internal_teamid(); +} +inline void CustomMatch_SetTeam::_internal_set_teamid(int32_t value) { + + _impl_.teamid_ = value; +} +inline void CustomMatch_SetTeam::set_teamid(int32_t value) { + _internal_set_teamid(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetTeam.teamId) +} + +// string targetHardwareName = 2; +inline void CustomMatch_SetTeam::clear_targethardwarename() { + _impl_.targethardwarename_.ClearToEmpty(); +} +inline const std::string& CustomMatch_SetTeam::targethardwarename() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetTeam.targetHardwareName) + return _internal_targethardwarename(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_SetTeam::set_targethardwarename(ArgT0&& arg0, ArgT... args) { + + _impl_.targethardwarename_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetTeam.targetHardwareName) +} +inline std::string* CustomMatch_SetTeam::mutable_targethardwarename() { + std::string* _s = _internal_mutable_targethardwarename(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_SetTeam.targetHardwareName) + return _s; +} +inline const std::string& CustomMatch_SetTeam::_internal_targethardwarename() const { + return _impl_.targethardwarename_.Get(); +} +inline void CustomMatch_SetTeam::_internal_set_targethardwarename(const std::string& value) { + + _impl_.targethardwarename_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetTeam::_internal_mutable_targethardwarename() { + + return _impl_.targethardwarename_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetTeam::release_targethardwarename() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_SetTeam.targetHardwareName) + return _impl_.targethardwarename_.Release(); +} +inline void CustomMatch_SetTeam::set_allocated_targethardwarename(std::string* targethardwarename) { + if (targethardwarename != nullptr) { + + } else { + + } + _impl_.targethardwarename_.SetAllocated(targethardwarename, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.targethardwarename_.IsDefault()) { + _impl_.targethardwarename_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_SetTeam.targetHardwareName) +} + +// string targetNucleusHash = 3; +inline void CustomMatch_SetTeam::clear_targetnucleushash() { + _impl_.targetnucleushash_.ClearToEmpty(); +} +inline const std::string& CustomMatch_SetTeam::targetnucleushash() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetTeam.targetNucleusHash) + return _internal_targetnucleushash(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_SetTeam::set_targetnucleushash(ArgT0&& arg0, ArgT... args) { + + _impl_.targetnucleushash_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetTeam.targetNucleusHash) +} +inline std::string* CustomMatch_SetTeam::mutable_targetnucleushash() { + std::string* _s = _internal_mutable_targetnucleushash(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_SetTeam.targetNucleusHash) + return _s; +} +inline const std::string& CustomMatch_SetTeam::_internal_targetnucleushash() const { + return _impl_.targetnucleushash_.Get(); +} +inline void CustomMatch_SetTeam::_internal_set_targetnucleushash(const std::string& value) { + + _impl_.targetnucleushash_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetTeam::_internal_mutable_targetnucleushash() { + + return _impl_.targetnucleushash_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetTeam::release_targetnucleushash() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_SetTeam.targetNucleusHash) + return _impl_.targetnucleushash_.Release(); +} +inline void CustomMatch_SetTeam::set_allocated_targetnucleushash(std::string* targetnucleushash) { + if (targetnucleushash != nullptr) { + + } else { + + } + _impl_.targetnucleushash_.SetAllocated(targetnucleushash, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.targetnucleushash_.IsDefault()) { + _impl_.targetnucleushash_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_SetTeam.targetNucleusHash) +} + +// ------------------------------------------------------------------- + +// CustomMatch_KickPlayer + +// string targetHardwareName = 1; +inline void CustomMatch_KickPlayer::clear_targethardwarename() { + _impl_.targethardwarename_.ClearToEmpty(); +} +inline const std::string& CustomMatch_KickPlayer::targethardwarename() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_KickPlayer.targetHardwareName) + return _internal_targethardwarename(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_KickPlayer::set_targethardwarename(ArgT0&& arg0, ArgT... args) { + + _impl_.targethardwarename_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_KickPlayer.targetHardwareName) +} +inline std::string* CustomMatch_KickPlayer::mutable_targethardwarename() { + std::string* _s = _internal_mutable_targethardwarename(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_KickPlayer.targetHardwareName) + return _s; +} +inline const std::string& CustomMatch_KickPlayer::_internal_targethardwarename() const { + return _impl_.targethardwarename_.Get(); +} +inline void CustomMatch_KickPlayer::_internal_set_targethardwarename(const std::string& value) { + + _impl_.targethardwarename_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_KickPlayer::_internal_mutable_targethardwarename() { + + return _impl_.targethardwarename_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_KickPlayer::release_targethardwarename() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_KickPlayer.targetHardwareName) + return _impl_.targethardwarename_.Release(); +} +inline void CustomMatch_KickPlayer::set_allocated_targethardwarename(std::string* targethardwarename) { + if (targethardwarename != nullptr) { + + } else { + + } + _impl_.targethardwarename_.SetAllocated(targethardwarename, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.targethardwarename_.IsDefault()) { + _impl_.targethardwarename_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_KickPlayer.targetHardwareName) +} + +// string targetNucleusHash = 2; +inline void CustomMatch_KickPlayer::clear_targetnucleushash() { + _impl_.targetnucleushash_.ClearToEmpty(); +} +inline const std::string& CustomMatch_KickPlayer::targetnucleushash() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_KickPlayer.targetNucleusHash) + return _internal_targetnucleushash(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_KickPlayer::set_targetnucleushash(ArgT0&& arg0, ArgT... args) { + + _impl_.targetnucleushash_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_KickPlayer.targetNucleusHash) +} +inline std::string* CustomMatch_KickPlayer::mutable_targetnucleushash() { + std::string* _s = _internal_mutable_targetnucleushash(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_KickPlayer.targetNucleusHash) + return _s; +} +inline const std::string& CustomMatch_KickPlayer::_internal_targetnucleushash() const { + return _impl_.targetnucleushash_.Get(); +} +inline void CustomMatch_KickPlayer::_internal_set_targetnucleushash(const std::string& value) { + + _impl_.targetnucleushash_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_KickPlayer::_internal_mutable_targetnucleushash() { + + return _impl_.targetnucleushash_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_KickPlayer::release_targetnucleushash() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_KickPlayer.targetNucleusHash) + return _impl_.targetnucleushash_.Release(); +} +inline void CustomMatch_KickPlayer::set_allocated_targetnucleushash(std::string* targetnucleushash) { + if (targetnucleushash != nullptr) { + + } else { + + } + _impl_.targetnucleushash_.SetAllocated(targetnucleushash, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.targetnucleushash_.IsDefault()) { + _impl_.targetnucleushash_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_KickPlayer.targetNucleusHash) +} + +// ------------------------------------------------------------------- + +// CustomMatch_SetSettings + +// string playlistName = 1; +inline void CustomMatch_SetSettings::clear_playlistname() { + _impl_.playlistname_.ClearToEmpty(); +} +inline const std::string& CustomMatch_SetSettings::playlistname() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetSettings.playlistName) + return _internal_playlistname(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_SetSettings::set_playlistname(ArgT0&& arg0, ArgT... args) { + + _impl_.playlistname_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetSettings.playlistName) +} +inline std::string* CustomMatch_SetSettings::mutable_playlistname() { + std::string* _s = _internal_mutable_playlistname(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_SetSettings.playlistName) + return _s; +} +inline const std::string& CustomMatch_SetSettings::_internal_playlistname() const { + return _impl_.playlistname_.Get(); +} +inline void CustomMatch_SetSettings::_internal_set_playlistname(const std::string& value) { + + _impl_.playlistname_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetSettings::_internal_mutable_playlistname() { + + return _impl_.playlistname_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetSettings::release_playlistname() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_SetSettings.playlistName) + return _impl_.playlistname_.Release(); +} +inline void CustomMatch_SetSettings::set_allocated_playlistname(std::string* playlistname) { + if (playlistname != nullptr) { + + } else { + + } + _impl_.playlistname_.SetAllocated(playlistname, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.playlistname_.IsDefault()) { + _impl_.playlistname_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_SetSettings.playlistName) +} + +// bool adminChat = 2; +inline void CustomMatch_SetSettings::clear_adminchat() { + _impl_.adminchat_ = false; +} +inline bool CustomMatch_SetSettings::_internal_adminchat() const { + return _impl_.adminchat_; +} +inline bool CustomMatch_SetSettings::adminchat() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetSettings.adminChat) + return _internal_adminchat(); +} +inline void CustomMatch_SetSettings::_internal_set_adminchat(bool value) { + + _impl_.adminchat_ = value; +} +inline void CustomMatch_SetSettings::set_adminchat(bool value) { + _internal_set_adminchat(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetSettings.adminChat) +} + +// bool teamRename = 3; +inline void CustomMatch_SetSettings::clear_teamrename() { + _impl_.teamrename_ = false; +} +inline bool CustomMatch_SetSettings::_internal_teamrename() const { + return _impl_.teamrename_; +} +inline bool CustomMatch_SetSettings::teamrename() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetSettings.teamRename) + return _internal_teamrename(); +} +inline void CustomMatch_SetSettings::_internal_set_teamrename(bool value) { + + _impl_.teamrename_ = value; +} +inline void CustomMatch_SetSettings::set_teamrename(bool value) { + _internal_set_teamrename(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetSettings.teamRename) +} + +// bool selfAssign = 4; +inline void CustomMatch_SetSettings::clear_selfassign() { + _impl_.selfassign_ = false; +} +inline bool CustomMatch_SetSettings::_internal_selfassign() const { + return _impl_.selfassign_; +} +inline bool CustomMatch_SetSettings::selfassign() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetSettings.selfAssign) + return _internal_selfassign(); +} +inline void CustomMatch_SetSettings::_internal_set_selfassign(bool value) { + + _impl_.selfassign_ = value; +} +inline void CustomMatch_SetSettings::set_selfassign(bool value) { + _internal_set_selfassign(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetSettings.selfAssign) +} + +// bool aimAssist = 5; +inline void CustomMatch_SetSettings::clear_aimassist() { + _impl_.aimassist_ = false; +} +inline bool CustomMatch_SetSettings::_internal_aimassist() const { + return _impl_.aimassist_; +} +inline bool CustomMatch_SetSettings::aimassist() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetSettings.aimAssist) + return _internal_aimassist(); +} +inline void CustomMatch_SetSettings::_internal_set_aimassist(bool value) { + + _impl_.aimassist_ = value; +} +inline void CustomMatch_SetSettings::set_aimassist(bool value) { + _internal_set_aimassist(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetSettings.aimAssist) +} + +// bool anonMode = 6; +inline void CustomMatch_SetSettings::clear_anonmode() { + _impl_.anonmode_ = false; +} +inline bool CustomMatch_SetSettings::_internal_anonmode() const { + return _impl_.anonmode_; +} +inline bool CustomMatch_SetSettings::anonmode() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetSettings.anonMode) + return _internal_anonmode(); +} +inline void CustomMatch_SetSettings::_internal_set_anonmode(bool value) { + + _impl_.anonmode_ = value; +} +inline void CustomMatch_SetSettings::set_anonmode(bool value) { + _internal_set_anonmode(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetSettings.anonMode) +} + +// ------------------------------------------------------------------- + +// CustomMatch_GetSettings + +// ------------------------------------------------------------------- + +// CustomMatch_SetTeamName + +// int32 teamId = 1; +inline void CustomMatch_SetTeamName::clear_teamid() { + _impl_.teamid_ = 0; +} +inline int32_t CustomMatch_SetTeamName::_internal_teamid() const { + return _impl_.teamid_; +} +inline int32_t CustomMatch_SetTeamName::teamid() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetTeamName.teamId) + return _internal_teamid(); +} +inline void CustomMatch_SetTeamName::_internal_set_teamid(int32_t value) { + + _impl_.teamid_ = value; +} +inline void CustomMatch_SetTeamName::set_teamid(int32_t value) { + _internal_set_teamid(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetTeamName.teamId) +} + +// string teamName = 2; +inline void CustomMatch_SetTeamName::clear_teamname() { + _impl_.teamname_.ClearToEmpty(); +} +inline const std::string& CustomMatch_SetTeamName::teamname() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SetTeamName.teamName) + return _internal_teamname(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_SetTeamName::set_teamname(ArgT0&& arg0, ArgT... args) { + + _impl_.teamname_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SetTeamName.teamName) +} +inline std::string* CustomMatch_SetTeamName::mutable_teamname() { + std::string* _s = _internal_mutable_teamname(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_SetTeamName.teamName) + return _s; +} +inline const std::string& CustomMatch_SetTeamName::_internal_teamname() const { + return _impl_.teamname_.Get(); +} +inline void CustomMatch_SetTeamName::_internal_set_teamname(const std::string& value) { + + _impl_.teamname_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetTeamName::_internal_mutable_teamname() { + + return _impl_.teamname_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_SetTeamName::release_teamname() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_SetTeamName.teamName) + return _impl_.teamname_.Release(); +} +inline void CustomMatch_SetTeamName::set_allocated_teamname(std::string* teamname) { + if (teamname != nullptr) { + + } else { + + } + _impl_.teamname_.SetAllocated(teamname, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.teamname_.IsDefault()) { + _impl_.teamname_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_SetTeamName.teamName) +} + +// ------------------------------------------------------------------- + +// CustomMatch_SendChat + +// string text = 1; +inline void CustomMatch_SendChat::clear_text() { + _impl_.text_.ClearToEmpty(); +} +inline const std::string& CustomMatch_SendChat::text() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.CustomMatch_SendChat.text) + return _internal_text(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void CustomMatch_SendChat::set_text(ArgT0&& arg0, ArgT... args) { + + _impl_.text_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.CustomMatch_SendChat.text) +} +inline std::string* CustomMatch_SendChat::mutable_text() { + std::string* _s = _internal_mutable_text(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.CustomMatch_SendChat.text) + return _s; +} +inline const std::string& CustomMatch_SendChat::_internal_text() const { + return _impl_.text_.Get(); +} +inline void CustomMatch_SendChat::_internal_set_text(const std::string& value) { + + _impl_.text_.Set(value, GetArenaForAllocation()); +} +inline std::string* CustomMatch_SendChat::_internal_mutable_text() { + + return _impl_.text_.Mutable(GetArenaForAllocation()); +} +inline std::string* CustomMatch_SendChat::release_text() { + // @@protoc_insertion_point(field_release:rtech.liveapi.CustomMatch_SendChat.text) + return _impl_.text_.Release(); +} +inline void CustomMatch_SendChat::set_allocated_text(std::string* text) { + if (text != nullptr) { + + } else { + + } + _impl_.text_.SetAllocated(text, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.text_.IsDefault()) { + _impl_.text_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.CustomMatch_SendChat.text) +} + +// ------------------------------------------------------------------- + +// Request + +// bool withAck = 1; +inline void Request::clear_withack() { + _impl_.withack_ = false; +} +inline bool Request::_internal_withack() const { + return _impl_.withack_; +} +inline bool Request::withack() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.withAck) + return _internal_withack(); +} +inline void Request::_internal_set_withack(bool value) { + + _impl_.withack_ = value; +} +inline void Request::set_withack(bool value) { + _internal_set_withack(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Request.withAck) +} + +// string preSharedKey = 2; +inline void Request::clear_presharedkey() { + _impl_.presharedkey_.ClearToEmpty(); +} +inline const std::string& Request::presharedkey() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.preSharedKey) + return _internal_presharedkey(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void Request::set_presharedkey(ArgT0&& arg0, ArgT... args) { + + _impl_.presharedkey_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.Request.preSharedKey) +} +inline std::string* Request::mutable_presharedkey() { + std::string* _s = _internal_mutable_presharedkey(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.preSharedKey) + return _s; +} +inline const std::string& Request::_internal_presharedkey() const { + return _impl_.presharedkey_.Get(); +} +inline void Request::_internal_set_presharedkey(const std::string& value) { + + _impl_.presharedkey_.Set(value, GetArenaForAllocation()); +} +inline std::string* Request::_internal_mutable_presharedkey() { + + return _impl_.presharedkey_.Mutable(GetArenaForAllocation()); +} +inline std::string* Request::release_presharedkey() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.preSharedKey) + return _impl_.presharedkey_.Release(); +} +inline void Request::set_allocated_presharedkey(std::string* presharedkey) { + if (presharedkey != nullptr) { + + } else { + + } + _impl_.presharedkey_.SetAllocated(presharedkey, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.presharedkey_.IsDefault()) { + _impl_.presharedkey_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Request.preSharedKey) +} + +// .rtech.liveapi.ChangeCamera changeCam = 4; +inline bool Request::_internal_has_changecam() const { + return actions_case() == kChangeCam; +} +inline bool Request::has_changecam() const { + return _internal_has_changecam(); +} +inline void Request::set_has_changecam() { + _impl_._oneof_case_[0] = kChangeCam; +} +inline void Request::clear_changecam() { + if (_internal_has_changecam()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.changecam_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::ChangeCamera* Request::release_changecam() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.changeCam) + if (_internal_has_changecam()) { + clear_has_actions(); + ::rtech::liveapi::ChangeCamera* temp = _impl_.actions_.changecam_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.changecam_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::ChangeCamera& Request::_internal_changecam() const { + return _internal_has_changecam() + ? *_impl_.actions_.changecam_ + : reinterpret_cast< ::rtech::liveapi::ChangeCamera&>(::rtech::liveapi::_ChangeCamera_default_instance_); +} +inline const ::rtech::liveapi::ChangeCamera& Request::changecam() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.changeCam) + return _internal_changecam(); +} +inline ::rtech::liveapi::ChangeCamera* Request::unsafe_arena_release_changecam() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.changeCam) + if (_internal_has_changecam()) { + clear_has_actions(); + ::rtech::liveapi::ChangeCamera* temp = _impl_.actions_.changecam_; + _impl_.actions_.changecam_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_changecam(::rtech::liveapi::ChangeCamera* changecam) { + clear_actions(); + if (changecam) { + set_has_changecam(); + _impl_.actions_.changecam_ = changecam; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.changeCam) +} +inline ::rtech::liveapi::ChangeCamera* Request::_internal_mutable_changecam() { + if (!_internal_has_changecam()) { + clear_actions(); + set_has_changecam(); + _impl_.actions_.changecam_ = CreateMaybeMessage< ::rtech::liveapi::ChangeCamera >(GetArenaForAllocation()); + } + return _impl_.actions_.changecam_; +} +inline ::rtech::liveapi::ChangeCamera* Request::mutable_changecam() { + ::rtech::liveapi::ChangeCamera* _msg = _internal_mutable_changecam(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.changeCam) + return _msg; +} + +// .rtech.liveapi.PauseToggle pauseToggle = 5; +inline bool Request::_internal_has_pausetoggle() const { + return actions_case() == kPauseToggle; +} +inline bool Request::has_pausetoggle() const { + return _internal_has_pausetoggle(); +} +inline void Request::set_has_pausetoggle() { + _impl_._oneof_case_[0] = kPauseToggle; +} +inline void Request::clear_pausetoggle() { + if (_internal_has_pausetoggle()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.pausetoggle_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::PauseToggle* Request::release_pausetoggle() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.pauseToggle) + if (_internal_has_pausetoggle()) { + clear_has_actions(); + ::rtech::liveapi::PauseToggle* temp = _impl_.actions_.pausetoggle_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.pausetoggle_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::PauseToggle& Request::_internal_pausetoggle() const { + return _internal_has_pausetoggle() + ? *_impl_.actions_.pausetoggle_ + : reinterpret_cast< ::rtech::liveapi::PauseToggle&>(::rtech::liveapi::_PauseToggle_default_instance_); +} +inline const ::rtech::liveapi::PauseToggle& Request::pausetoggle() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.pauseToggle) + return _internal_pausetoggle(); +} +inline ::rtech::liveapi::PauseToggle* Request::unsafe_arena_release_pausetoggle() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.pauseToggle) + if (_internal_has_pausetoggle()) { + clear_has_actions(); + ::rtech::liveapi::PauseToggle* temp = _impl_.actions_.pausetoggle_; + _impl_.actions_.pausetoggle_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_pausetoggle(::rtech::liveapi::PauseToggle* pausetoggle) { + clear_actions(); + if (pausetoggle) { + set_has_pausetoggle(); + _impl_.actions_.pausetoggle_ = pausetoggle; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.pauseToggle) +} +inline ::rtech::liveapi::PauseToggle* Request::_internal_mutable_pausetoggle() { + if (!_internal_has_pausetoggle()) { + clear_actions(); + set_has_pausetoggle(); + _impl_.actions_.pausetoggle_ = CreateMaybeMessage< ::rtech::liveapi::PauseToggle >(GetArenaForAllocation()); + } + return _impl_.actions_.pausetoggle_; +} +inline ::rtech::liveapi::PauseToggle* Request::mutable_pausetoggle() { + ::rtech::liveapi::PauseToggle* _msg = _internal_mutable_pausetoggle(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.pauseToggle) + return _msg; +} + +// .rtech.liveapi.CustomMatch_CreateLobby customMatch_CreateLobby = 10; +inline bool Request::_internal_has_custommatch_createlobby() const { + return actions_case() == kCustomMatchCreateLobby; +} +inline bool Request::has_custommatch_createlobby() const { + return _internal_has_custommatch_createlobby(); +} +inline void Request::set_has_custommatch_createlobby() { + _impl_._oneof_case_[0] = kCustomMatchCreateLobby; +} +inline void Request::clear_custommatch_createlobby() { + if (_internal_has_custommatch_createlobby()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_createlobby_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_CreateLobby* Request::release_custommatch_createlobby() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_CreateLobby) + if (_internal_has_custommatch_createlobby()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_CreateLobby* temp = _impl_.actions_.custommatch_createlobby_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_createlobby_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_CreateLobby& Request::_internal_custommatch_createlobby() const { + return _internal_has_custommatch_createlobby() + ? *_impl_.actions_.custommatch_createlobby_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_CreateLobby&>(::rtech::liveapi::_CustomMatch_CreateLobby_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_CreateLobby& Request::custommatch_createlobby() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_CreateLobby) + return _internal_custommatch_createlobby(); +} +inline ::rtech::liveapi::CustomMatch_CreateLobby* Request::unsafe_arena_release_custommatch_createlobby() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_CreateLobby) + if (_internal_has_custommatch_createlobby()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_CreateLobby* temp = _impl_.actions_.custommatch_createlobby_; + _impl_.actions_.custommatch_createlobby_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_createlobby(::rtech::liveapi::CustomMatch_CreateLobby* custommatch_createlobby) { + clear_actions(); + if (custommatch_createlobby) { + set_has_custommatch_createlobby(); + _impl_.actions_.custommatch_createlobby_ = custommatch_createlobby; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_CreateLobby) +} +inline ::rtech::liveapi::CustomMatch_CreateLobby* Request::_internal_mutable_custommatch_createlobby() { + if (!_internal_has_custommatch_createlobby()) { + clear_actions(); + set_has_custommatch_createlobby(); + _impl_.actions_.custommatch_createlobby_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_CreateLobby >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_createlobby_; +} +inline ::rtech::liveapi::CustomMatch_CreateLobby* Request::mutable_custommatch_createlobby() { + ::rtech::liveapi::CustomMatch_CreateLobby* _msg = _internal_mutable_custommatch_createlobby(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_CreateLobby) + return _msg; +} + +// .rtech.liveapi.CustomMatch_JoinLobby customMatch_JoinLobby = 11; +inline bool Request::_internal_has_custommatch_joinlobby() const { + return actions_case() == kCustomMatchJoinLobby; +} +inline bool Request::has_custommatch_joinlobby() const { + return _internal_has_custommatch_joinlobby(); +} +inline void Request::set_has_custommatch_joinlobby() { + _impl_._oneof_case_[0] = kCustomMatchJoinLobby; +} +inline void Request::clear_custommatch_joinlobby() { + if (_internal_has_custommatch_joinlobby()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_joinlobby_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_JoinLobby* Request::release_custommatch_joinlobby() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_JoinLobby) + if (_internal_has_custommatch_joinlobby()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_JoinLobby* temp = _impl_.actions_.custommatch_joinlobby_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_joinlobby_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_JoinLobby& Request::_internal_custommatch_joinlobby() const { + return _internal_has_custommatch_joinlobby() + ? *_impl_.actions_.custommatch_joinlobby_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_JoinLobby&>(::rtech::liveapi::_CustomMatch_JoinLobby_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_JoinLobby& Request::custommatch_joinlobby() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_JoinLobby) + return _internal_custommatch_joinlobby(); +} +inline ::rtech::liveapi::CustomMatch_JoinLobby* Request::unsafe_arena_release_custommatch_joinlobby() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_JoinLobby) + if (_internal_has_custommatch_joinlobby()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_JoinLobby* temp = _impl_.actions_.custommatch_joinlobby_; + _impl_.actions_.custommatch_joinlobby_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_joinlobby(::rtech::liveapi::CustomMatch_JoinLobby* custommatch_joinlobby) { + clear_actions(); + if (custommatch_joinlobby) { + set_has_custommatch_joinlobby(); + _impl_.actions_.custommatch_joinlobby_ = custommatch_joinlobby; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_JoinLobby) +} +inline ::rtech::liveapi::CustomMatch_JoinLobby* Request::_internal_mutable_custommatch_joinlobby() { + if (!_internal_has_custommatch_joinlobby()) { + clear_actions(); + set_has_custommatch_joinlobby(); + _impl_.actions_.custommatch_joinlobby_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_JoinLobby >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_joinlobby_; +} +inline ::rtech::liveapi::CustomMatch_JoinLobby* Request::mutable_custommatch_joinlobby() { + ::rtech::liveapi::CustomMatch_JoinLobby* _msg = _internal_mutable_custommatch_joinlobby(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_JoinLobby) + return _msg; +} + +// .rtech.liveapi.CustomMatch_LeaveLobby customMatch_LeaveLobby = 12; +inline bool Request::_internal_has_custommatch_leavelobby() const { + return actions_case() == kCustomMatchLeaveLobby; +} +inline bool Request::has_custommatch_leavelobby() const { + return _internal_has_custommatch_leavelobby(); +} +inline void Request::set_has_custommatch_leavelobby() { + _impl_._oneof_case_[0] = kCustomMatchLeaveLobby; +} +inline void Request::clear_custommatch_leavelobby() { + if (_internal_has_custommatch_leavelobby()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_leavelobby_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_LeaveLobby* Request::release_custommatch_leavelobby() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_LeaveLobby) + if (_internal_has_custommatch_leavelobby()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_LeaveLobby* temp = _impl_.actions_.custommatch_leavelobby_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_leavelobby_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_LeaveLobby& Request::_internal_custommatch_leavelobby() const { + return _internal_has_custommatch_leavelobby() + ? *_impl_.actions_.custommatch_leavelobby_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_LeaveLobby&>(::rtech::liveapi::_CustomMatch_LeaveLobby_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_LeaveLobby& Request::custommatch_leavelobby() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_LeaveLobby) + return _internal_custommatch_leavelobby(); +} +inline ::rtech::liveapi::CustomMatch_LeaveLobby* Request::unsafe_arena_release_custommatch_leavelobby() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_LeaveLobby) + if (_internal_has_custommatch_leavelobby()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_LeaveLobby* temp = _impl_.actions_.custommatch_leavelobby_; + _impl_.actions_.custommatch_leavelobby_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_leavelobby(::rtech::liveapi::CustomMatch_LeaveLobby* custommatch_leavelobby) { + clear_actions(); + if (custommatch_leavelobby) { + set_has_custommatch_leavelobby(); + _impl_.actions_.custommatch_leavelobby_ = custommatch_leavelobby; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_LeaveLobby) +} +inline ::rtech::liveapi::CustomMatch_LeaveLobby* Request::_internal_mutable_custommatch_leavelobby() { + if (!_internal_has_custommatch_leavelobby()) { + clear_actions(); + set_has_custommatch_leavelobby(); + _impl_.actions_.custommatch_leavelobby_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_LeaveLobby >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_leavelobby_; +} +inline ::rtech::liveapi::CustomMatch_LeaveLobby* Request::mutable_custommatch_leavelobby() { + ::rtech::liveapi::CustomMatch_LeaveLobby* _msg = _internal_mutable_custommatch_leavelobby(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_LeaveLobby) + return _msg; +} + +// .rtech.liveapi.CustomMatch_SetReady customMatch_SetReady = 13; +inline bool Request::_internal_has_custommatch_setready() const { + return actions_case() == kCustomMatchSetReady; +} +inline bool Request::has_custommatch_setready() const { + return _internal_has_custommatch_setready(); +} +inline void Request::set_has_custommatch_setready() { + _impl_._oneof_case_[0] = kCustomMatchSetReady; +} +inline void Request::clear_custommatch_setready() { + if (_internal_has_custommatch_setready()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setready_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_SetReady* Request::release_custommatch_setready() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_SetReady) + if (_internal_has_custommatch_setready()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetReady* temp = _impl_.actions_.custommatch_setready_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_setready_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_SetReady& Request::_internal_custommatch_setready() const { + return _internal_has_custommatch_setready() + ? *_impl_.actions_.custommatch_setready_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_SetReady&>(::rtech::liveapi::_CustomMatch_SetReady_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_SetReady& Request::custommatch_setready() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_SetReady) + return _internal_custommatch_setready(); +} +inline ::rtech::liveapi::CustomMatch_SetReady* Request::unsafe_arena_release_custommatch_setready() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_SetReady) + if (_internal_has_custommatch_setready()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetReady* temp = _impl_.actions_.custommatch_setready_; + _impl_.actions_.custommatch_setready_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_setready(::rtech::liveapi::CustomMatch_SetReady* custommatch_setready) { + clear_actions(); + if (custommatch_setready) { + set_has_custommatch_setready(); + _impl_.actions_.custommatch_setready_ = custommatch_setready; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_SetReady) +} +inline ::rtech::liveapi::CustomMatch_SetReady* Request::_internal_mutable_custommatch_setready() { + if (!_internal_has_custommatch_setready()) { + clear_actions(); + set_has_custommatch_setready(); + _impl_.actions_.custommatch_setready_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetReady >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_setready_; +} +inline ::rtech::liveapi::CustomMatch_SetReady* Request::mutable_custommatch_setready() { + ::rtech::liveapi::CustomMatch_SetReady* _msg = _internal_mutable_custommatch_setready(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_SetReady) + return _msg; +} + +// .rtech.liveapi.CustomMatch_SetMatchmaking customMatch_SetMatchmaking = 14; +inline bool Request::_internal_has_custommatch_setmatchmaking() const { + return actions_case() == kCustomMatchSetMatchmaking; +} +inline bool Request::has_custommatch_setmatchmaking() const { + return _internal_has_custommatch_setmatchmaking(); +} +inline void Request::set_has_custommatch_setmatchmaking() { + _impl_._oneof_case_[0] = kCustomMatchSetMatchmaking; +} +inline void Request::clear_custommatch_setmatchmaking() { + if (_internal_has_custommatch_setmatchmaking()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setmatchmaking_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_SetMatchmaking* Request::release_custommatch_setmatchmaking() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_SetMatchmaking) + if (_internal_has_custommatch_setmatchmaking()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetMatchmaking* temp = _impl_.actions_.custommatch_setmatchmaking_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_setmatchmaking_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_SetMatchmaking& Request::_internal_custommatch_setmatchmaking() const { + return _internal_has_custommatch_setmatchmaking() + ? *_impl_.actions_.custommatch_setmatchmaking_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_SetMatchmaking&>(::rtech::liveapi::_CustomMatch_SetMatchmaking_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_SetMatchmaking& Request::custommatch_setmatchmaking() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_SetMatchmaking) + return _internal_custommatch_setmatchmaking(); +} +inline ::rtech::liveapi::CustomMatch_SetMatchmaking* Request::unsafe_arena_release_custommatch_setmatchmaking() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_SetMatchmaking) + if (_internal_has_custommatch_setmatchmaking()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetMatchmaking* temp = _impl_.actions_.custommatch_setmatchmaking_; + _impl_.actions_.custommatch_setmatchmaking_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_setmatchmaking(::rtech::liveapi::CustomMatch_SetMatchmaking* custommatch_setmatchmaking) { + clear_actions(); + if (custommatch_setmatchmaking) { + set_has_custommatch_setmatchmaking(); + _impl_.actions_.custommatch_setmatchmaking_ = custommatch_setmatchmaking; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_SetMatchmaking) +} +inline ::rtech::liveapi::CustomMatch_SetMatchmaking* Request::_internal_mutable_custommatch_setmatchmaking() { + if (!_internal_has_custommatch_setmatchmaking()) { + clear_actions(); + set_has_custommatch_setmatchmaking(); + _impl_.actions_.custommatch_setmatchmaking_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetMatchmaking >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_setmatchmaking_; +} +inline ::rtech::liveapi::CustomMatch_SetMatchmaking* Request::mutable_custommatch_setmatchmaking() { + ::rtech::liveapi::CustomMatch_SetMatchmaking* _msg = _internal_mutable_custommatch_setmatchmaking(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_SetMatchmaking) + return _msg; +} + +// .rtech.liveapi.CustomMatch_SetTeam customMatch_SetTeam = 15; +inline bool Request::_internal_has_custommatch_setteam() const { + return actions_case() == kCustomMatchSetTeam; +} +inline bool Request::has_custommatch_setteam() const { + return _internal_has_custommatch_setteam(); +} +inline void Request::set_has_custommatch_setteam() { + _impl_._oneof_case_[0] = kCustomMatchSetTeam; +} +inline void Request::clear_custommatch_setteam() { + if (_internal_has_custommatch_setteam()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setteam_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_SetTeam* Request::release_custommatch_setteam() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_SetTeam) + if (_internal_has_custommatch_setteam()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetTeam* temp = _impl_.actions_.custommatch_setteam_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_setteam_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_SetTeam& Request::_internal_custommatch_setteam() const { + return _internal_has_custommatch_setteam() + ? *_impl_.actions_.custommatch_setteam_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_SetTeam&>(::rtech::liveapi::_CustomMatch_SetTeam_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_SetTeam& Request::custommatch_setteam() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_SetTeam) + return _internal_custommatch_setteam(); +} +inline ::rtech::liveapi::CustomMatch_SetTeam* Request::unsafe_arena_release_custommatch_setteam() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_SetTeam) + if (_internal_has_custommatch_setteam()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetTeam* temp = _impl_.actions_.custommatch_setteam_; + _impl_.actions_.custommatch_setteam_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_setteam(::rtech::liveapi::CustomMatch_SetTeam* custommatch_setteam) { + clear_actions(); + if (custommatch_setteam) { + set_has_custommatch_setteam(); + _impl_.actions_.custommatch_setteam_ = custommatch_setteam; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_SetTeam) +} +inline ::rtech::liveapi::CustomMatch_SetTeam* Request::_internal_mutable_custommatch_setteam() { + if (!_internal_has_custommatch_setteam()) { + clear_actions(); + set_has_custommatch_setteam(); + _impl_.actions_.custommatch_setteam_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetTeam >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_setteam_; +} +inline ::rtech::liveapi::CustomMatch_SetTeam* Request::mutable_custommatch_setteam() { + ::rtech::liveapi::CustomMatch_SetTeam* _msg = _internal_mutable_custommatch_setteam(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_SetTeam) + return _msg; +} + +// .rtech.liveapi.CustomMatch_KickPlayer customMatch_KickPlayer = 16; +inline bool Request::_internal_has_custommatch_kickplayer() const { + return actions_case() == kCustomMatchKickPlayer; +} +inline bool Request::has_custommatch_kickplayer() const { + return _internal_has_custommatch_kickplayer(); +} +inline void Request::set_has_custommatch_kickplayer() { + _impl_._oneof_case_[0] = kCustomMatchKickPlayer; +} +inline void Request::clear_custommatch_kickplayer() { + if (_internal_has_custommatch_kickplayer()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_kickplayer_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_KickPlayer* Request::release_custommatch_kickplayer() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_KickPlayer) + if (_internal_has_custommatch_kickplayer()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_KickPlayer* temp = _impl_.actions_.custommatch_kickplayer_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_kickplayer_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_KickPlayer& Request::_internal_custommatch_kickplayer() const { + return _internal_has_custommatch_kickplayer() + ? *_impl_.actions_.custommatch_kickplayer_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_KickPlayer&>(::rtech::liveapi::_CustomMatch_KickPlayer_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_KickPlayer& Request::custommatch_kickplayer() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_KickPlayer) + return _internal_custommatch_kickplayer(); +} +inline ::rtech::liveapi::CustomMatch_KickPlayer* Request::unsafe_arena_release_custommatch_kickplayer() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_KickPlayer) + if (_internal_has_custommatch_kickplayer()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_KickPlayer* temp = _impl_.actions_.custommatch_kickplayer_; + _impl_.actions_.custommatch_kickplayer_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_kickplayer(::rtech::liveapi::CustomMatch_KickPlayer* custommatch_kickplayer) { + clear_actions(); + if (custommatch_kickplayer) { + set_has_custommatch_kickplayer(); + _impl_.actions_.custommatch_kickplayer_ = custommatch_kickplayer; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_KickPlayer) +} +inline ::rtech::liveapi::CustomMatch_KickPlayer* Request::_internal_mutable_custommatch_kickplayer() { + if (!_internal_has_custommatch_kickplayer()) { + clear_actions(); + set_has_custommatch_kickplayer(); + _impl_.actions_.custommatch_kickplayer_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_KickPlayer >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_kickplayer_; +} +inline ::rtech::liveapi::CustomMatch_KickPlayer* Request::mutable_custommatch_kickplayer() { + ::rtech::liveapi::CustomMatch_KickPlayer* _msg = _internal_mutable_custommatch_kickplayer(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_KickPlayer) + return _msg; +} + +// .rtech.liveapi.CustomMatch_SetSettings customMatch_SetSettings = 17; +inline bool Request::_internal_has_custommatch_setsettings() const { + return actions_case() == kCustomMatchSetSettings; +} +inline bool Request::has_custommatch_setsettings() const { + return _internal_has_custommatch_setsettings(); +} +inline void Request::set_has_custommatch_setsettings() { + _impl_._oneof_case_[0] = kCustomMatchSetSettings; +} +inline void Request::clear_custommatch_setsettings() { + if (_internal_has_custommatch_setsettings()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setsettings_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_SetSettings* Request::release_custommatch_setsettings() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_SetSettings) + if (_internal_has_custommatch_setsettings()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetSettings* temp = _impl_.actions_.custommatch_setsettings_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_setsettings_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_SetSettings& Request::_internal_custommatch_setsettings() const { + return _internal_has_custommatch_setsettings() + ? *_impl_.actions_.custommatch_setsettings_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_SetSettings&>(::rtech::liveapi::_CustomMatch_SetSettings_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_SetSettings& Request::custommatch_setsettings() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_SetSettings) + return _internal_custommatch_setsettings(); +} +inline ::rtech::liveapi::CustomMatch_SetSettings* Request::unsafe_arena_release_custommatch_setsettings() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_SetSettings) + if (_internal_has_custommatch_setsettings()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetSettings* temp = _impl_.actions_.custommatch_setsettings_; + _impl_.actions_.custommatch_setsettings_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_setsettings(::rtech::liveapi::CustomMatch_SetSettings* custommatch_setsettings) { + clear_actions(); + if (custommatch_setsettings) { + set_has_custommatch_setsettings(); + _impl_.actions_.custommatch_setsettings_ = custommatch_setsettings; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_SetSettings) +} +inline ::rtech::liveapi::CustomMatch_SetSettings* Request::_internal_mutable_custommatch_setsettings() { + if (!_internal_has_custommatch_setsettings()) { + clear_actions(); + set_has_custommatch_setsettings(); + _impl_.actions_.custommatch_setsettings_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetSettings >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_setsettings_; +} +inline ::rtech::liveapi::CustomMatch_SetSettings* Request::mutable_custommatch_setsettings() { + ::rtech::liveapi::CustomMatch_SetSettings* _msg = _internal_mutable_custommatch_setsettings(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_SetSettings) + return _msg; +} + +// .rtech.liveapi.CustomMatch_SendChat customMatch_SendChat = 18; +inline bool Request::_internal_has_custommatch_sendchat() const { + return actions_case() == kCustomMatchSendChat; +} +inline bool Request::has_custommatch_sendchat() const { + return _internal_has_custommatch_sendchat(); +} +inline void Request::set_has_custommatch_sendchat() { + _impl_._oneof_case_[0] = kCustomMatchSendChat; +} +inline void Request::clear_custommatch_sendchat() { + if (_internal_has_custommatch_sendchat()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_sendchat_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_SendChat* Request::release_custommatch_sendchat() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_SendChat) + if (_internal_has_custommatch_sendchat()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SendChat* temp = _impl_.actions_.custommatch_sendchat_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_sendchat_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_SendChat& Request::_internal_custommatch_sendchat() const { + return _internal_has_custommatch_sendchat() + ? *_impl_.actions_.custommatch_sendchat_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_SendChat&>(::rtech::liveapi::_CustomMatch_SendChat_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_SendChat& Request::custommatch_sendchat() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_SendChat) + return _internal_custommatch_sendchat(); +} +inline ::rtech::liveapi::CustomMatch_SendChat* Request::unsafe_arena_release_custommatch_sendchat() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_SendChat) + if (_internal_has_custommatch_sendchat()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SendChat* temp = _impl_.actions_.custommatch_sendchat_; + _impl_.actions_.custommatch_sendchat_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_sendchat(::rtech::liveapi::CustomMatch_SendChat* custommatch_sendchat) { + clear_actions(); + if (custommatch_sendchat) { + set_has_custommatch_sendchat(); + _impl_.actions_.custommatch_sendchat_ = custommatch_sendchat; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_SendChat) +} +inline ::rtech::liveapi::CustomMatch_SendChat* Request::_internal_mutable_custommatch_sendchat() { + if (!_internal_has_custommatch_sendchat()) { + clear_actions(); + set_has_custommatch_sendchat(); + _impl_.actions_.custommatch_sendchat_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SendChat >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_sendchat_; +} +inline ::rtech::liveapi::CustomMatch_SendChat* Request::mutable_custommatch_sendchat() { + ::rtech::liveapi::CustomMatch_SendChat* _msg = _internal_mutable_custommatch_sendchat(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_SendChat) + return _msg; +} + +// .rtech.liveapi.CustomMatch_GetLobbyPlayers customMatch_GetLobbyPlayers = 19; +inline bool Request::_internal_has_custommatch_getlobbyplayers() const { + return actions_case() == kCustomMatchGetLobbyPlayers; +} +inline bool Request::has_custommatch_getlobbyplayers() const { + return _internal_has_custommatch_getlobbyplayers(); +} +inline void Request::set_has_custommatch_getlobbyplayers() { + _impl_._oneof_case_[0] = kCustomMatchGetLobbyPlayers; +} +inline void Request::clear_custommatch_getlobbyplayers() { + if (_internal_has_custommatch_getlobbyplayers()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_getlobbyplayers_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_GetLobbyPlayers* Request::release_custommatch_getlobbyplayers() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_GetLobbyPlayers) + if (_internal_has_custommatch_getlobbyplayers()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* temp = _impl_.actions_.custommatch_getlobbyplayers_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_getlobbyplayers_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_GetLobbyPlayers& Request::_internal_custommatch_getlobbyplayers() const { + return _internal_has_custommatch_getlobbyplayers() + ? *_impl_.actions_.custommatch_getlobbyplayers_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_GetLobbyPlayers&>(::rtech::liveapi::_CustomMatch_GetLobbyPlayers_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_GetLobbyPlayers& Request::custommatch_getlobbyplayers() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_GetLobbyPlayers) + return _internal_custommatch_getlobbyplayers(); +} +inline ::rtech::liveapi::CustomMatch_GetLobbyPlayers* Request::unsafe_arena_release_custommatch_getlobbyplayers() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_GetLobbyPlayers) + if (_internal_has_custommatch_getlobbyplayers()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* temp = _impl_.actions_.custommatch_getlobbyplayers_; + _impl_.actions_.custommatch_getlobbyplayers_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_getlobbyplayers(::rtech::liveapi::CustomMatch_GetLobbyPlayers* custommatch_getlobbyplayers) { + clear_actions(); + if (custommatch_getlobbyplayers) { + set_has_custommatch_getlobbyplayers(); + _impl_.actions_.custommatch_getlobbyplayers_ = custommatch_getlobbyplayers; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_GetLobbyPlayers) +} +inline ::rtech::liveapi::CustomMatch_GetLobbyPlayers* Request::_internal_mutable_custommatch_getlobbyplayers() { + if (!_internal_has_custommatch_getlobbyplayers()) { + clear_actions(); + set_has_custommatch_getlobbyplayers(); + _impl_.actions_.custommatch_getlobbyplayers_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_GetLobbyPlayers >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_getlobbyplayers_; +} +inline ::rtech::liveapi::CustomMatch_GetLobbyPlayers* Request::mutable_custommatch_getlobbyplayers() { + ::rtech::liveapi::CustomMatch_GetLobbyPlayers* _msg = _internal_mutable_custommatch_getlobbyplayers(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_GetLobbyPlayers) + return _msg; +} + +// .rtech.liveapi.CustomMatch_SetTeamName customMatch_SetTeamName = 20; +inline bool Request::_internal_has_custommatch_setteamname() const { + return actions_case() == kCustomMatchSetTeamName; +} +inline bool Request::has_custommatch_setteamname() const { + return _internal_has_custommatch_setteamname(); +} +inline void Request::set_has_custommatch_setteamname() { + _impl_._oneof_case_[0] = kCustomMatchSetTeamName; +} +inline void Request::clear_custommatch_setteamname() { + if (_internal_has_custommatch_setteamname()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_setteamname_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_SetTeamName* Request::release_custommatch_setteamname() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_SetTeamName) + if (_internal_has_custommatch_setteamname()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetTeamName* temp = _impl_.actions_.custommatch_setteamname_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_setteamname_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_SetTeamName& Request::_internal_custommatch_setteamname() const { + return _internal_has_custommatch_setteamname() + ? *_impl_.actions_.custommatch_setteamname_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_SetTeamName&>(::rtech::liveapi::_CustomMatch_SetTeamName_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_SetTeamName& Request::custommatch_setteamname() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_SetTeamName) + return _internal_custommatch_setteamname(); +} +inline ::rtech::liveapi::CustomMatch_SetTeamName* Request::unsafe_arena_release_custommatch_setteamname() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_SetTeamName) + if (_internal_has_custommatch_setteamname()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_SetTeamName* temp = _impl_.actions_.custommatch_setteamname_; + _impl_.actions_.custommatch_setteamname_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_setteamname(::rtech::liveapi::CustomMatch_SetTeamName* custommatch_setteamname) { + clear_actions(); + if (custommatch_setteamname) { + set_has_custommatch_setteamname(); + _impl_.actions_.custommatch_setteamname_ = custommatch_setteamname; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_SetTeamName) +} +inline ::rtech::liveapi::CustomMatch_SetTeamName* Request::_internal_mutable_custommatch_setteamname() { + if (!_internal_has_custommatch_setteamname()) { + clear_actions(); + set_has_custommatch_setteamname(); + _impl_.actions_.custommatch_setteamname_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_SetTeamName >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_setteamname_; +} +inline ::rtech::liveapi::CustomMatch_SetTeamName* Request::mutable_custommatch_setteamname() { + ::rtech::liveapi::CustomMatch_SetTeamName* _msg = _internal_mutable_custommatch_setteamname(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_SetTeamName) + return _msg; +} + +// .rtech.liveapi.CustomMatch_GetSettings customMatch_GetSettings = 21; +inline bool Request::_internal_has_custommatch_getsettings() const { + return actions_case() == kCustomMatchGetSettings; +} +inline bool Request::has_custommatch_getsettings() const { + return _internal_has_custommatch_getsettings(); +} +inline void Request::set_has_custommatch_getsettings() { + _impl_._oneof_case_[0] = kCustomMatchGetSettings; +} +inline void Request::clear_custommatch_getsettings() { + if (_internal_has_custommatch_getsettings()) { + if (GetArenaForAllocation() == nullptr) { + delete _impl_.actions_.custommatch_getsettings_; + } + clear_has_actions(); + } +} +inline ::rtech::liveapi::CustomMatch_GetSettings* Request::release_custommatch_getsettings() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Request.customMatch_GetSettings) + if (_internal_has_custommatch_getsettings()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_GetSettings* temp = _impl_.actions_.custommatch_getsettings_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + _impl_.actions_.custommatch_getsettings_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::rtech::liveapi::CustomMatch_GetSettings& Request::_internal_custommatch_getsettings() const { + return _internal_has_custommatch_getsettings() + ? *_impl_.actions_.custommatch_getsettings_ + : reinterpret_cast< ::rtech::liveapi::CustomMatch_GetSettings&>(::rtech::liveapi::_CustomMatch_GetSettings_default_instance_); +} +inline const ::rtech::liveapi::CustomMatch_GetSettings& Request::custommatch_getsettings() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Request.customMatch_GetSettings) + return _internal_custommatch_getsettings(); +} +inline ::rtech::liveapi::CustomMatch_GetSettings* Request::unsafe_arena_release_custommatch_getsettings() { + // @@protoc_insertion_point(field_unsafe_arena_release:rtech.liveapi.Request.customMatch_GetSettings) + if (_internal_has_custommatch_getsettings()) { + clear_has_actions(); + ::rtech::liveapi::CustomMatch_GetSettings* temp = _impl_.actions_.custommatch_getsettings_; + _impl_.actions_.custommatch_getsettings_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void Request::unsafe_arena_set_allocated_custommatch_getsettings(::rtech::liveapi::CustomMatch_GetSettings* custommatch_getsettings) { + clear_actions(); + if (custommatch_getsettings) { + set_has_custommatch_getsettings(); + _impl_.actions_.custommatch_getsettings_ = custommatch_getsettings; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Request.customMatch_GetSettings) +} +inline ::rtech::liveapi::CustomMatch_GetSettings* Request::_internal_mutable_custommatch_getsettings() { + if (!_internal_has_custommatch_getsettings()) { + clear_actions(); + set_has_custommatch_getsettings(); + _impl_.actions_.custommatch_getsettings_ = CreateMaybeMessage< ::rtech::liveapi::CustomMatch_GetSettings >(GetArenaForAllocation()); + } + return _impl_.actions_.custommatch_getsettings_; +} +inline ::rtech::liveapi::CustomMatch_GetSettings* Request::mutable_custommatch_getsettings() { + ::rtech::liveapi::CustomMatch_GetSettings* _msg = _internal_mutable_custommatch_getsettings(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Request.customMatch_GetSettings) + return _msg; +} + +inline bool Request::has_actions() const { + return actions_case() != ACTIONS_NOT_SET; +} +inline void Request::clear_has_actions() { + _impl_._oneof_case_[0] = ACTIONS_NOT_SET; +} +inline Request::ActionsCase Request::actions_case() const { + return Request::ActionsCase(_impl_._oneof_case_[0]); +} +// ------------------------------------------------------------------- + +// RequestStatus + +// string status = 1; +inline void RequestStatus::clear_status() { + _impl_.status_.ClearToEmpty(); +} +inline const std::string& RequestStatus::status() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.RequestStatus.status) + return _internal_status(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void RequestStatus::set_status(ArgT0&& arg0, ArgT... args) { + + _impl_.status_.Set(static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:rtech.liveapi.RequestStatus.status) +} +inline std::string* RequestStatus::mutable_status() { + std::string* _s = _internal_mutable_status(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.RequestStatus.status) + return _s; +} +inline const std::string& RequestStatus::_internal_status() const { + return _impl_.status_.Get(); +} +inline void RequestStatus::_internal_set_status(const std::string& value) { + + _impl_.status_.Set(value, GetArenaForAllocation()); +} +inline std::string* RequestStatus::_internal_mutable_status() { + + return _impl_.status_.Mutable(GetArenaForAllocation()); +} +inline std::string* RequestStatus::release_status() { + // @@protoc_insertion_point(field_release:rtech.liveapi.RequestStatus.status) + return _impl_.status_.Release(); +} +inline void RequestStatus::set_allocated_status(std::string* status) { + if (status != nullptr) { + + } else { + + } + _impl_.status_.SetAllocated(status, GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (_impl_.status_.IsDefault()) { + _impl_.status_.Set("", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.RequestStatus.status) +} + +// ------------------------------------------------------------------- + +// Response + +// bool success = 1; +inline void Response::clear_success() { + _impl_.success_ = false; +} +inline bool Response::_internal_success() const { + return _impl_.success_; +} +inline bool Response::success() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Response.success) + return _internal_success(); +} +inline void Response::_internal_set_success(bool value) { + + _impl_.success_ = value; +} +inline void Response::set_success(bool value) { + _internal_set_success(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.Response.success) +} + +// .google.protobuf.Any result = 2; +inline bool Response::_internal_has_result() const { + return this != internal_default_instance() && _impl_.result_ != nullptr; +} +inline bool Response::has_result() const { + return _internal_has_result(); +} +inline const ::PROTOBUF_NAMESPACE_ID::Any& Response::_internal_result() const { + const ::PROTOBUF_NAMESPACE_ID::Any* p = _impl_.result_; + return p != nullptr ? *p : reinterpret_cast( + ::PROTOBUF_NAMESPACE_ID::_Any_default_instance_); +} +inline const ::PROTOBUF_NAMESPACE_ID::Any& Response::result() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.Response.result) + return _internal_result(); +} +inline void Response::unsafe_arena_set_allocated_result( + ::PROTOBUF_NAMESPACE_ID::Any* result) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.result_); + } + _impl_.result_ = result; + if (result) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.Response.result) +} +inline ::PROTOBUF_NAMESPACE_ID::Any* Response::release_result() { + + ::PROTOBUF_NAMESPACE_ID::Any* temp = _impl_.result_; + _impl_.result_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::PROTOBUF_NAMESPACE_ID::Any* Response::unsafe_arena_release_result() { + // @@protoc_insertion_point(field_release:rtech.liveapi.Response.result) + + ::PROTOBUF_NAMESPACE_ID::Any* temp = _impl_.result_; + _impl_.result_ = nullptr; + return temp; +} +inline ::PROTOBUF_NAMESPACE_ID::Any* Response::_internal_mutable_result() { + + if (_impl_.result_ == nullptr) { + auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Any>(GetArenaForAllocation()); + _impl_.result_ = p; + } + return _impl_.result_; +} +inline ::PROTOBUF_NAMESPACE_ID::Any* Response::mutable_result() { + ::PROTOBUF_NAMESPACE_ID::Any* _msg = _internal_mutable_result(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.Response.result) + return _msg; +} +inline void Response::set_allocated_result(::PROTOBUF_NAMESPACE_ID::Any* result) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.result_); + } + if (result) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena( + reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(result)); + if (message_arena != submessage_arena) { + result = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, result, submessage_arena); + } + + } else { + + } + _impl_.result_ = result; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.Response.result) +} + +// ------------------------------------------------------------------- + +// LiveAPIEvent + +// fixed32 event_size = 1; +inline void LiveAPIEvent::clear_event_size() { + _impl_.event_size_ = 0u; +} +inline uint32_t LiveAPIEvent::_internal_event_size() const { + return _impl_.event_size_; +} +inline uint32_t LiveAPIEvent::event_size() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LiveAPIEvent.event_size) + return _internal_event_size(); +} +inline void LiveAPIEvent::_internal_set_event_size(uint32_t value) { + + _impl_.event_size_ = value; +} +inline void LiveAPIEvent::set_event_size(uint32_t value) { + _internal_set_event_size(value); + // @@protoc_insertion_point(field_set:rtech.liveapi.LiveAPIEvent.event_size) +} + +// .google.protobuf.Any gameMessage = 3; +inline bool LiveAPIEvent::_internal_has_gamemessage() const { + return this != internal_default_instance() && _impl_.gamemessage_ != nullptr; +} +inline bool LiveAPIEvent::has_gamemessage() const { + return _internal_has_gamemessage(); +} +inline const ::PROTOBUF_NAMESPACE_ID::Any& LiveAPIEvent::_internal_gamemessage() const { + const ::PROTOBUF_NAMESPACE_ID::Any* p = _impl_.gamemessage_; + return p != nullptr ? *p : reinterpret_cast( + ::PROTOBUF_NAMESPACE_ID::_Any_default_instance_); +} +inline const ::PROTOBUF_NAMESPACE_ID::Any& LiveAPIEvent::gamemessage() const { + // @@protoc_insertion_point(field_get:rtech.liveapi.LiveAPIEvent.gameMessage) + return _internal_gamemessage(); +} +inline void LiveAPIEvent::unsafe_arena_set_allocated_gamemessage( + ::PROTOBUF_NAMESPACE_ID::Any* gamemessage) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.gamemessage_); + } + _impl_.gamemessage_ = gamemessage; + if (gamemessage) { + + } else { + + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:rtech.liveapi.LiveAPIEvent.gameMessage) +} +inline ::PROTOBUF_NAMESPACE_ID::Any* LiveAPIEvent::release_gamemessage() { + + ::PROTOBUF_NAMESPACE_ID::Any* temp = _impl_.gamemessage_; + _impl_.gamemessage_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::PROTOBUF_NAMESPACE_ID::Any* LiveAPIEvent::unsafe_arena_release_gamemessage() { + // @@protoc_insertion_point(field_release:rtech.liveapi.LiveAPIEvent.gameMessage) + + ::PROTOBUF_NAMESPACE_ID::Any* temp = _impl_.gamemessage_; + _impl_.gamemessage_ = nullptr; + return temp; +} +inline ::PROTOBUF_NAMESPACE_ID::Any* LiveAPIEvent::_internal_mutable_gamemessage() { + + if (_impl_.gamemessage_ == nullptr) { + auto* p = CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Any>(GetArenaForAllocation()); + _impl_.gamemessage_ = p; + } + return _impl_.gamemessage_; +} +inline ::PROTOBUF_NAMESPACE_ID::Any* LiveAPIEvent::mutable_gamemessage() { + ::PROTOBUF_NAMESPACE_ID::Any* _msg = _internal_mutable_gamemessage(); + // @@protoc_insertion_point(field_mutable:rtech.liveapi.LiveAPIEvent.gameMessage) + return _msg; +} +inline void LiveAPIEvent::set_allocated_gamemessage(::PROTOBUF_NAMESPACE_ID::Any* gamemessage) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.gamemessage_); + } + if (gamemessage) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena( + reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(gamemessage)); + if (message_arena != submessage_arena) { + gamemessage = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, gamemessage, submessage_arena); + } + + } else { + + } + _impl_.gamemessage_ = gamemessage; + // @@protoc_insertion_point(field_set_allocated:rtech.liveapi.LiveAPIEvent.gameMessage) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace liveapi +} // namespace rtech + +PROTOBUF_NAMESPACE_OPEN + +template <> struct is_proto_enum< ::rtech::liveapi::PlayerOfInterest> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::rtech::liveapi::PlayerOfInterest>() { + return ::rtech::liveapi::PlayerOfInterest_descriptor(); +} + +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_events_2eproto diff --git a/r5dev/public/appframework/IAppSystem.h b/r5dev/public/appframework/IAppSystem.h index 25f2ff19..3b573c23 100644 --- a/r5dev/public/appframework/IAppSystem.h +++ b/r5dev/public/appframework/IAppSystem.h @@ -54,15 +54,15 @@ enum AppSystemTier_t abstract_class IAppSystem { public: - virtual ~IAppSystem() = 0; // Prepended on each class derived class in assembly. + virtual ~IAppSystem() {}; // Prepended on each class derived class in assembly. // Here's where the app systems get to learn about each other - virtual bool Connect(CreateInterfaceFn factory) = 0; + virtual bool Connect(const CreateInterfaceFn factory) = 0; virtual void Disconnect() = 0; // Here's where systems can access other interfaces implemented by this object // Returns NULL if it doesn't implement the requested interface - virtual void* QueryInterface(const char* pInterfaceName) = 0; + virtual void* QueryInterface(const char* const pInterfaceName) = 0; // Init, shutdown virtual InitReturnVal_t Init() = 0; @@ -75,7 +75,7 @@ public: virtual AppSystemTier_t GetTier() = 0; // Reconnect to a particular interface - virtual void Reconnect(CreateInterfaceFn factory, const char* pInterfaceName) = 0; + virtual void Reconnect(const CreateInterfaceFn factory, const char* const pInterfaceName) = 0; }; //----------------------------------------------------------------------------- @@ -85,15 +85,15 @@ template< class IInterface > class CBaseAppSystem : public IInterface { public: - virtual ~CBaseAppSystem() = 0; // Prepended on each class derived class in assembly. + virtual ~CBaseAppSystem() {}; // Prepended on each class derived class in assembly. // Here's where the app systems get to learn about each other - virtual bool Connect(CreateInterfaceFn factory) = 0; + virtual bool Connect(const CreateInterfaceFn factory) = 0; virtual void Disconnect() = 0; // Here's where systems can access other interfaces implemented by this object // Returns NULL if it doesn't implement the requested interface - virtual void* QueryInterface(const char* pInterfaceName) = 0; + virtual void* QueryInterface(const char* const pInterfaceName) = 0; // Init, shutdown virtual InitReturnVal_t Init() = 0; @@ -106,7 +106,15 @@ public: virtual AppSystemTier_t GetTier() = 0; // Reconnect to a particular interface - virtual void Reconnect(CreateInterfaceFn factory, const char* pInterfaceName) = 0; + virtual void Reconnect(const CreateInterfaceFn factory, const char* const pInterfaceName) = 0; +}; + +//----------------------------------------------------------------------------- +// Helper implementation of an IAppSystem for tier0 +//----------------------------------------------------------------------------- +template< class IInterface > +class CTier0AppSystem : public CBaseAppSystem< IInterface > +{ }; #endif // IAPPSYSTEM_H \ No newline at end of file diff --git a/r5dev/public/appframework/IAppSystemGroup.h b/r5dev/public/appframework/IAppSystemGroup.h index 52b7d48a..1f34c346 100644 --- a/r5dev/public/appframework/IAppSystemGroup.h +++ b/r5dev/public/appframework/IAppSystemGroup.h @@ -3,6 +3,7 @@ #include "tier1/interface.h" #include "tier1/utlvector.h" +#include "tier1/utldict.h" #include "filesystem/filesystem.h" //----------------------------------------------------------------------------- @@ -76,10 +77,13 @@ public: // Returns the stage at which the app system group ran into an error AppSystemGroupStage_t GetCurrentStage() const; + // Method to look up a particular named system... + void* FindSystem(const char* pInterfaceName); + private: struct Module_t { - void /*CSysModule*/* m_pModule; + CSysModule* m_pModule; CreateInterfaceFn m_Factory; char* m_pModuleName; }; @@ -88,34 +92,28 @@ protected: CUtlVector m_Modules; CUtlVector m_Systems; CUtlVector m_NonAppSystemFactories; - char m_Pad[56]; // <-- unknown + CUtlDict m_SystemDict; + CAppSystemGroup* m_pParentAppSystem; AppSystemGroupStage_t m_nCurrentStage; }; static_assert(sizeof(CAppSystemGroup) == 0xA8); -inline CMemory p_CAppSystemGroup_Destroy; -inline void(*CAppSystemGroup_Destroy)(CAppSystemGroup* pAppSystemGroup); +inline void(*CAppSystemGroup__Destroy)(CAppSystemGroup* pAppSystemGroup); /////////////////////////////////////////////////////////////////////////////// class VAppSystemGroup : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CAppSystemGroup::Destroy", p_CAppSystemGroup_Destroy.GetPtr()); + LogFunAdr("CAppSystemGroup::Destroy", CAppSystemGroup__Destroy); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_CAppSystemGroup_Destroy = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 20 8B 81 ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_CAppSystemGroup_Destroy = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B 81 ?? ?? ?? ?? 48 8B F9"); -#endif - CAppSystemGroup_Destroy = p_CAppSystemGroup_Destroy.RCast(); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B 81 ?? ?? ?? ?? 48 8B F9").GetPtr(CAppSystemGroup__Destroy); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; #endif // APPSYSTEMGROUP_H diff --git a/r5dev/public/bspfile.h b/r5dev/public/bspfile.h index 2e4a1c32..a78170c3 100644 --- a/r5dev/public/bspfile.h +++ b/r5dev/public/bspfile.h @@ -331,6 +331,13 @@ struct dgamelump_t int filelen; }; +struct dlightmapheader_t +{ + char type; + short width; + short height; +}; + struct dlightprobe_t { short ambientSH[12]; // Ambient spherical harmonics coefficients @@ -340,4 +347,12 @@ struct dlightprobe_t char pad[4]; // Padding has been removed as of S14 }; +struct dtexdata_t +{ + int nameStringTableID; + int width; + int height; + int flags; +}; + #endif // BSPFILE_H diff --git a/r5dev/public/coordsize.h b/r5dev/public/coordsize.h new file mode 100644 index 00000000..b21d9eee --- /dev/null +++ b/r5dev/public/coordsize.h @@ -0,0 +1,98 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef COORDSIZE_H +#define COORDSIZE_H +#pragma once + +#include "worldsize.h" + +// OVERALL Coordinate Size Limits used in COMMON.C MSG_*BitCoord() Routines (and someday the HUD) +#define COORD_INTEGER_BITS 16 +#define COORD_FRACTIONAL_BITS 5 +#define COORD_DENOMINATOR (1<<(COORD_FRACTIONAL_BITS)) +#define COORD_RESOLUTION (1.0f/(COORD_DENOMINATOR)) + +// Special threshold for networking multiplayer origins +#define COORD_INTEGER_BITS_MP 11 +#define COORD_FRACTIONAL_BITS_MP_LOWPRECISION 3 +#define COORD_DENOMINATOR_LOWPRECISION (1<<(COORD_FRACTIONAL_BITS_MP_LOWPRECISION)) +#define COORD_RESOLUTION_LOWPRECISION (1.0f/(COORD_DENOMINATOR_LOWPRECISION)) + +#define NORMAL_FRACTIONAL_BITS 11 +#define NORMAL_DENOMINATOR ((1<<(NORMAL_FRACTIONAL_BITS))-1) +#define NORMAL_RESOLUTION (1.0f/(NORMAL_DENOMINATOR)) + +// this is limited by the network fractional bits used for coords +// because net coords will only be accurate to 5 bits fractional +// Standard collision test epsilon +// 1/32nd inch collision epsilon +#define DIST_EPSILON (0.03125) + +// Verify that coordsize.h and worldsize.h are consistently defined +#if (MAX_COORD_INTEGER != (1<(g_ServerGlobalVariables)); + LogVarAdr("g_ServerGlobalVariables", g_ServerGlobalVariables); #endif // !CLIENT_DLL #ifndef DEDICATED - LogVarAdr("g_ClientGlobalVariables", reinterpret_cast(g_ClientGlobalVariables)); + LogVarAdr("g_ClientGlobalVariables", g_ClientGlobalVariables); #endif // !DEDICATED } virtual void GetFun(void) const { } @@ -64,17 +64,11 @@ class VEdict : public IDetour .FindPatternSelf("48 8D ?? ?? ?? ?? 01", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); #endif // !CLIENT_DLL #ifndef DEDICATED -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_ClientGlobalVariables = g_GameDll.FindPatternSIMD("48 8B C4 57 41 54 41 55 41 56 41 57 48 83 EC 60 48 C7 40 ?? ?? ?? ?? ?? 48 89 58 08") - .FindPatternSelf("4C 8D ?? ?? ?? ?? 01", CMemory::Direction::DOWN, 8000).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) g_ClientGlobalVariables = g_GameDll.FindPatternSIMD("48 8B C4 55 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC 60") .FindPatternSelf("4C 8D ?? ?? ?? ?? 01", CMemory::Direction::DOWN, 8000).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#endif // GAME_DLL #endif // !DEDICATED } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/eiface.h b/r5dev/public/eiface.h index efc6f6a9..0ecffbb1 100644 --- a/r5dev/public/eiface.h +++ b/r5dev/public/eiface.h @@ -9,7 +9,7 @@ #define EIFACE_H #include "edict.h" #include "tier1/bitbuf.h" -#include "vpc/keyvalues.h" +#include "tier1/keyvalues.h" //----------------------------------------------------------------------------- // Forward declarations @@ -154,11 +154,6 @@ public: // Get a convar keyvalue for specified client virtual const char* GetClientConVarValue(int nClientIndex, const char* szConVarName) = 0; - -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - virtual void NullSub1(void) = 0; // Additional nullsub only present in s0 and s1 gamedll's -#endif // GAMEDLL_S0 || GAMEDLL_S1 - // Returns the name as represented on the server of specified client virtual const char* GetClientServerName(int nClientIndex) = 0; // Returns the network address of specified client diff --git a/r5dev/public/iconcommand.h b/r5dev/public/iconcommand.h index af3bcb78..03805d4a 100644 --- a/r5dev/public/iconcommand.h +++ b/r5dev/public/iconcommand.h @@ -17,37 +17,7 @@ abstract_class IConCommandBaseAccessor public: // Flags is a combination of FCVAR flags in cvar.h. // hOut is filled in with a handle to the variable. - virtual bool RegisterConCommandBase(ConCommandBase* pVar) = 0; + virtual bool RegisterConCommandBase(ConCommandBase* const pVar) = 0; }; -//----------------------------------------------------------------------------- -// Abstract interface for ConVars -//----------------------------------------------------------------------------- -abstract_class IConCommandBase -{ -public: - virtual ~IConCommandBase(void) = 0; - - virtual bool IsCommand(void) const = 0; - - virtual bool IsFlagSet(int flag) const = 0; // Check flag - virtual void AddFlags(int flags) = 0; // Set flag - virtual void RemoveFlags(int flags) = 0; // Clear flag - virtual int GetFlags() const = 0; // Get flag - - virtual const char* GetName(void) const = 0; // Return name of cvar - virtual const char* GetHelpText(void) const = 0; // Return help text for cvar - virtual const char* GetUsageText(void) const = 0; // Return usage text for cvar - virtual void SetAccessor(char* bAccessors) const = 0; - - virtual bool IsRegistered(void) const = 0; - virtual CVarDLLIdentifier_t GetDLLIdentifier() const = 0; // Returns the DLL identifier - -protected: - virtual void Create(const char* pName, const char* pHelpString = 0, - int flags = 0) = 0; - virtual void Init() = 0; // Used internally by OneTimeInit to initialize/shutdown -}; - - -#endif // ICONCOMMAND_H \ No newline at end of file +#endif // ICONCOMMAND_H diff --git a/r5dev/public/iconvar.h b/r5dev/public/iconvar.h index 7added89..b492396d 100644 --- a/r5dev/public/iconvar.h +++ b/r5dev/public/iconvar.h @@ -6,6 +6,7 @@ //----------------------------------------------------------------------------- class IConVar; class CCommand; +class CUtlString; //----------------------------------------------------------------------------- // Command to ConVars and ConCommands @@ -66,6 +67,9 @@ class CCommand; typedef void (*FnCommandCallbackV1_t)(void); typedef void (*FnCommandCallback_t)(const CCommand& command); +typedef void (*FnCommandSupplementalFinishCallback_t)(); +typedef void (*FnCommandSupplementalCallback_t)(const CCommand& command, FnCommandSupplementalFinishCallback_t); + #define COMMAND_COMPLETION_MAXITEMS 128 #define COMMAND_COMPLETION_ITEM_LENGTH 128 @@ -88,14 +92,14 @@ public: class ICommandCompletionCallback { public: - //virtual int CommandCompletionCallback(const char* pPartial, CUtlVector< CUtlString > &commands) = 0; + virtual int CommandCompletionCallback(const char* pPartial, CUtlVector< CUtlString > &commands) = 0; }; //----------------------------------------------------------------------------- // Called when a ConVar changes value // NOTE: For FCVAR_NEVER_AS_STRING ConVars, pOldValue == NULL //----------------------------------------------------------------------------- -typedef void (*FnChangeCallback_t)(IConVar* var, const char* pOldValue, float flOldValue); +typedef void (*FnChangeCallback_t)(IConVar* var, const char* pOldValue); //----------------------------------------------------------------------------- @@ -111,17 +115,15 @@ public: virtual void SetValue(float flValue) = 0; virtual void SetValue(int nValue) = 0; - // Original name 'GetName'. Renamed due to name ambiguity - // as we are not implementing it in ConVar, we are just - // interfacing it with the game executable. - virtual const char* GetCommandName(void) const = 0; + // Return name of command + virtual const char* GetName(void) const = 0; // Return name of command (usually == GetName(), except in case of FCVAR_SS_ADDED vars virtual const char* GetBaseName(void) const = 0; // Accessors.. not as efficient as using GetState()/GetInfo() // if you call these methods multiple times on the same IConVar - virtual bool IsConVarFlagSet(int nFlag) const = 0; // Original name 'IsFlagSet'. Renamed for same reason as 'GetName'. + virtual bool IsFlagSet(const int nFlag) const = 0; virtual int GetSplitScreenPlayerSlot() const = 0; }; diff --git a/r5dev/public/idatablock.h b/r5dev/public/idatablock.h deleted file mode 100644 index 84b49d75..00000000 --- a/r5dev/public/idatablock.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef IDATABLOCK_H -#define IDATABLOCK_H - -#define DATABLOCK_STATUS_SIZE 0x300 - -abstract_class NetDataBlockSender -{ -public: - virtual ~NetDataBlockSender() {}; - virtual void SendDataBlock(short unk0, int unk1, - short unk2, short unk3, const void* buffer, int length) = 0; - virtual float GetResendRate() const = 0; - virtual const char* GetReceiverName() const = 0; -}; - -abstract_class NetDataBlockReceiver -{ -public: - virtual ~NetDataBlockReceiver() {}; - virtual void NotImplemented() {}; - virtual void AcknowledgeTransmission() = 0; -}; - -#endif // IDATABLOCK_H diff --git a/r5dev/public/iengine.h b/r5dev/public/iengine.h index f9fe9c8e..36f084ff 100644 --- a/r5dev/public/iengine.h +++ b/r5dev/public/iengine.h @@ -20,7 +20,7 @@ abstract_class IEngine { public: - enum + enum QuitState_t { QUIT_NOTQUITTING = 0, QUIT_TODESKTOP, @@ -45,7 +45,7 @@ public: virtual void SetNextState(EngineState_t iNextState) = 0; virtual EngineState_t GetState(void) = 0; - virtual void Frame(void) = 0; + virtual bool Frame(void) = 0; // Returns true if an engine frame is being ran. virtual float GetFrameTime(void) = 0; virtual float GetPreviousTime(void) = 0; diff --git a/r5dev/public/ifilesystem.h b/r5dev/public/ifilesystem.h index 69cbf5af..2462f4d0 100644 --- a/r5dev/public/ifilesystem.h +++ b/r5dev/public/ifilesystem.h @@ -185,17 +185,17 @@ enum abstract_class IBaseFileSystem { public: - virtual int Read(void* pOutput, int size, FileHandle_t file) = 0; - virtual int Write(void const* pInput, int size, FileHandle_t file) = 0; + virtual ssize_t Read(void* pOutput, ssize_t size, FileHandle_t file) = 0; + virtual ssize_t Write(void const* pInput, ssize_t size, FileHandle_t file) = 0; // if pathID is NULL, all paths will be searched for the file virtual FileHandle_t Open(const char* pFileName, const char* pOptions, const char* pPathID = 0, int64_t unknown = 0) = 0; virtual void Close(FileHandle_t file) = 0; - virtual void Seek(FileHandle_t file, int pos, FileSystemSeek_t seekType) = 0; - virtual unsigned int Tell(FileHandle_t file) = 0; - virtual unsigned int FSize(const char* pFileName, const char* pPathID = 0) = 0; // Gets optimized away if it isn't named differently or used. - virtual unsigned int Size(FileHandle_t file) = 0; + virtual void Seek(FileHandle_t file, ptrdiff_t pos, FileSystemSeek_t seekType) = 0; + virtual ptrdiff_t Tell(FileHandle_t file) = 0; + virtual ssize_t FSize(const char* pFileName, const char* pPathID = 0) = 0; // Gets optimized away if it isn't named differently or used. + virtual ssize_t Size(FileHandle_t file) = 0; virtual void Flush(FileHandle_t file) = 0; virtual bool Precache(const char* pFileName, const char* pPathID = 0) = 0; @@ -204,12 +204,12 @@ public: virtual bool IsFileWritable(char const* pFileName, const char* pPathID = 0) = 0; virtual bool SetFileWritable(char const* pFileName, bool writable, const char* pPathID = 0) = 0; - virtual long GetFileTime(const char* pFileName, const char* pPathID = 0) = 0; + virtual long long GetFileTime(const char* pFileName, const char* pPathID = 0) = 0; //-------------------------------------------------------- // Reads/writes files to utlbuffers. Use this for optimal read performance when doing open/read/close. //-------------------------------------------------------- - virtual bool ReadFile(const char* pFileName, const char* pPath, CUtlBuffer& buf, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL) = 0; + virtual bool ReadFile(const char* pFileName, const char* pPath, CUtlBuffer& buf, ssize_t nMaxBytes = 0, ptrdiff_t nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL) = 0; virtual bool WriteFile(const char* pFileName, const char* pPath, CUtlBuffer& buf) = 0; virtual bool UnzipFile(const char* pFileName, const char* pPath, const char* pDestination) = 0; }; @@ -231,10 +231,8 @@ public: // at load time, so the dedicated couldn't pass it in that way). virtual FilesystemMountRetval_t MountSteamContent(int nExtraAppId = -1) = 0; -#if !defined(GAMEDLL_S0) && !defined(GAMEDLL_S1) && !defined (GAMEDLL_S2) virtual bool InitFeatureFlags() = 0; virtual bool InitFeatureFlags(const char* pszFlagSetFile) = 0; -#endif // !GAMEDLL_S0 || !GAMEDLL_S1 || GAMEDLL_S2 virtual void AddSearchPath(const char* pPath, const char* pPathID, SearchPathAdd_t addType) = 0; virtual bool RemoveSearchPath(const char* pPath, const char* pPathID) = 0; @@ -246,13 +244,13 @@ public: // remember it in case you add search paths with this path ID. virtual void MarkPathIDByRequestOnly(const char* pPathID, bool bRequestOnly) = 0; // converts a partial path into a full path - virtual const char* RelativePathToFullPath(const char* pFileName, const char* pPathID, char* pLocalPath, size_t localPathBufferSize, PathTypeFilter_t pathFilter = FILTER_NONE, PathTypeQuery_t* pPathType = NULL) = 0; + virtual const char* RelativePathToFullPath(const char* pFileName, const char* pPathID, char* pLocalPath, ssize_t localPathBufferSize, PathTypeFilter_t pathFilter = FILTER_NONE, PathTypeQuery_t* pPathType = NULL) = 0; #if IsGameConsole() // Given a relative path, gets the PACK file that contained this file and its offset and size. Can be used to prefetch a file to a HDD for caching reason. - virtual bool GetPackFileInfoFromRelativePath(const char* pFileName, const char* pPathID, char* pPackPath, int nPackPathBufferSize, int64& nPosition, int64& nLength) = 0; + virtual bool GetPackFileInfoFromRelativePath(const char* pFileName, const char* pPathID, char* pPackPath, ssize_t nPackPathBufferSize, ptrdiff_t& nPosition, ssize_t& nLength) = 0; #endif // Returns the search path, each path is separated by ;s. Returns the length of the string returned - virtual int GetSearchPath(const char* pathID, bool bGetPackFiles, char* pPath, int nMaxLen) = 0; + virtual ssize_t GetSearchPath(const char* pathID, bool bGetPackFiles, char* pPath, ssize_t nMaxLen) = 0; virtual bool AddPackFile(const char* fullpath, const char* pathID) = 0; // interface for custom pack files > 4Gb //-------------------------------------------------------- @@ -262,21 +260,21 @@ public: virtual bool RenameFile(char const* pOldPath, char const* pNewPath, const char* pathID = 0) = 0; // Renames a file (on the WritePath) virtual int CreateDirHierarchy(const char* path, const char* pathID = 0) = 0; // create a local directory structure virtual bool IsDirectory(const char* pFileName, const char* pathID = 0) = 0; // File I/O and info - virtual void FileTimeToString(char* pStrip, int maxCharsIncludingTerminator, long fileTime) = 0; + virtual ssize_t FileTimeToString(char* pStrip, ssize_t maxCharsIncludingTerminator, long fileTime) = 0; // Returns the string size //-------------------------------------------------------- // Open file operations //-------------------------------------------------------- - virtual void SetBufferSize(FileHandle_t file, unsigned nBytes) = 0; + virtual void SetBufferSize(FileHandle_t file/*, ssize_t nBytes*/) = 0; // Amos: unsure if this is accurate, no longer takes the second argument? virtual bool IsOk(FileHandle_t file) = 0; virtual bool EndOfFile(FileHandle_t file) = 0; - virtual char* ReadLine(char* pOutput, int maxChars, FileHandle_t file) = 0; + virtual char* ReadLine(char* pOutput, size_t maxChars, FileHandle_t file) = 0; #if ! defined(SWIG) // Don't let SWIG see the PRINTF_FORMAT_STRING attribute or it will complain. - virtual int FPrintf(FileHandle_t file, PRINTF_FORMAT_STRING const char* pFormat, ...) FMTFUNCTION(3, 4) = 0; + virtual ssize_t FPrintf(FileHandle_t file, PRINTF_FORMAT_STRING const char* pFormat, ...) FMTFUNCTION(3, 4) = 0; #else - virtual int FPrintf(FileHandle_t file, const char* pFormat, ...) FMTFUNCTION(3, 4) = 0; + virtual ssize_t FPrintf(FileHandle_t file, const char* pFormat, ...) FMTFUNCTION(3, 4) = 0; #endif //-------------------------------------------------------- @@ -312,21 +310,21 @@ public: // FIXME: This method is obsolete! Use RelativePathToFullPath instead! // converts a partial path into a full path - virtual const char* GetLocalPath(const char* pFileName, char* pLocalPath, int localPathBufferSize) = 0; + virtual const char* GetLocalPath(const char* pFileName, char* pLocalPath, ssize_t localPathBufferSize) = 0; // Returns true on success ( based on current list of search paths, otherwise false if // it can't be resolved ) - virtual bool FullPathToRelativePath(const char* pFullpath, char* pRelative, int maxlen) = 0; + virtual bool FullPathToRelativePath(const char* pFullpath, char* pRelative, ssize_t maxlen) = 0; // Gets the current working directory - virtual bool GetCurrentDirectory(char* pDirectory, int maxlen) = 0; + virtual bool GetCurrentDirectory(char* pDirectory, unsigned int maxlen) = 0; // Last parameter is a DWORD passed to 'GetCurrentDirectoryA()' internally. //-------------------------------------------------------- // Filename dictionary operations //-------------------------------------------------------- virtual FileNameHandle_t FindOrAddFileName(char const* pFileName) = 0; - virtual bool String(const FileNameHandle_t& handle, char* buf, int buflen) = 0; + virtual bool String(const FileNameHandle_t& handle, char* buf, ssize_t buflen) = 0; //-------------------------------------------------------- // Asynchronous file operations @@ -368,11 +366,11 @@ public: // Start of new functions after Lost Coast release (7/05) //-------------------------------------------------------- - virtual FileHandle_t OpenEx(const char* pFileName, const char* pOptions, unsigned flags = 0, const char* pathID = 0, char** ppszResolvedFilename = NULL) = 0; + virtual FileHandle_t OpenEx(const char* pFileName, const char* pOptions, unsigned flags = 0, const char* pathID = 0/*, char** ppszResolvedFilename = NULL*/) = 0; // Extended version of read provides more context to allow for more optimal reading - virtual int ReadEx(void* pOutput, int sizeDest, int size, FileHandle_t file) = 0; - virtual int ReadFileEx(const char* pFileName, const char* pPath, void** ppBuf, bool bNullTerminate = false, bool bOptimalAlloc = false, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL) = 0; + virtual ssize_t ReadEx(void* pOutput, ssize_t sizeDest, ssize_t size, FileHandle_t file) = 0; + virtual ssize_t ReadFileEx(const char* pFileName, const char* pPath, void** ppBuf, bool bNullTerminate = false, bool bOptimalAlloc = false, ssize_t nMaxBytes = 0, ptrdiff_t nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL) = 0; virtual FileNameHandle_t FindFileName(char const* pFileName) = 0; @@ -403,17 +401,17 @@ public: virtual KeyValues* LoadKeyValues(KeyValuesPreloadType_t type, char const* filename, char const* pPathID = 0) = 0; virtual bool LoadKeyValues(KeyValues& head, KeyValuesPreloadType_t type, char const* filename, char const* pPathID = 0) = 0; - virtual bool GetFileTypeForFullPath(char const* pFullPath, wchar_t* buf, size_t bufSizeInBytes) = 0; + virtual bool GetFileTypeForFullPath(char const* pFullPath, wchar_t* buf, size_t bufSizeInBytes) = 0; //-------------------------------------------------------- //-------------------------------------------------------- - virtual bool ReadToBuffer(FileHandle_t hFile, CUtlBuffer& buf, int nMaxBytes = 0, FSAllocFunc_t pfnAlloc = NULL) = 0; + virtual bool ReadToBuffer(FileHandle_t hFile, CUtlBuffer& buf, ssize_t nMaxBytes = 0, FSAllocFunc_t pfnAlloc = NULL) = 0; //-------------------------------------------------------- // Optimal IO operations //-------------------------------------------------------- - virtual bool GetOptimalIOConstraints(FileHandle_t hFile, unsigned* pOffsetAlign, unsigned* pSizeAlign, unsigned* pBufferAlign) = 0; - virtual void* AllocOptimalReadBuffer(FileHandle_t hFile, unsigned nSize = 0, unsigned nOffset = 0) = 0; + virtual bool GetOptimalIOConstraints(FileHandle_t hFile, uint64_t* pOffsetAlign, uint64_t* pSizeAlign, uint64_t* pBufferAlign) = 0; + virtual void* AllocOptimalReadBuffer(ptrdiff_t nOffset = 0/*!!! UNUSED !!!*/, ssize_t nSize = 0) = 0; virtual void FreeOptimalReadBuffer(void*) = 0; diff --git a/r5dev/public/iframetask.h b/r5dev/public/iframetask.h index cd901a1d..248c86f9 100644 --- a/r5dev/public/iframetask.h +++ b/r5dev/public/iframetask.h @@ -1,11 +1,12 @@ #ifndef TIER0_IFRAMETASK_H #define TIER0_IFRAMETASK_H -struct ScheduledTasks_s +struct QueuedTasks_s { - int m_nDelayedFrames; + unsigned int m_nDelayedFrames; std::function m_rFunctor; - ScheduledTasks_s(int frames, std::function functor) + + QueuedTasks_s(unsigned int frames, std::function functor) { m_nDelayedFrames = frames; m_rFunctor = functor; @@ -16,6 +17,7 @@ abstract_class IFrameTask { public: virtual ~IFrameTask() {} + virtual void RunFrame() = 0; virtual bool IsFinished() const = 0; }; diff --git a/r5dev/public/igame.h b/r5dev/public/igame.h deleted file mode 100644 index c7dfaaf7..00000000 --- a/r5dev/public/igame.h +++ /dev/null @@ -1,19 +0,0 @@ -//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// -// -// Purpose: -// -// $Workfile: $ -// $Date: $ -// $NoKeywords: $ -//===========================================================================// -#ifndef IGAME_H -#define IGAME_H - -class IGame -{ -public: - virtual ~IGame(void) { } - // TODO // -}; - -#endif // IGAME_H \ No newline at end of file diff --git a/r5dev/public/ikeyvaluessystem.h b/r5dev/public/ikeyvaluessystem.h index 11108d33..6099ab4f 100644 --- a/r5dev/public/ikeyvaluessystem.h +++ b/r5dev/public/ikeyvaluessystem.h @@ -26,29 +26,31 @@ public: // registers the size of the KeyValues in the specified instance // so it can build a properly sized memory pool for the KeyValues objects // the sizes will usually never differ but this is for versioning safety - virtual void RegisterSizeofKeyValues( int64 nSize ) = 0; + virtual void RegisterSizeofKeyValues( const ssize_t nSize ) = 0; // allocates/frees a KeyValues object from the shared mempool - virtual void *AllocKeyValuesMemory( int64 nSize ) = 0; - virtual void FreeKeyValuesMemory( void *pMem ) = 0; + virtual void *AllocKeyValuesMemory( const ssize_t nSize ) = 0; + virtual void FreeKeyValuesMemory( void *const pMem ) = 0; // symbol table access (used for key names) - virtual HKeySymbol GetSymbolForString( const char *pName, bool bCreate = true ) = 0; - virtual const char *GetStringForSymbol( HKeySymbol symbol ) = 0; + virtual HKeySymbol GetSymbolForString( const char *const pName, const bool bCreate = true ) = 0; + virtual const char *GetStringForSymbol( const HKeySymbol symbol ) = 0; // for debugging, adds KeyValues record into global list so we can track memory leaks - virtual void AddKeyValuesToMemoryLeakList( void *pMem, HKeySymbol name ) = 0; - virtual void RemoveKeyValuesFromMemoryLeakList( void *pMem ) = 0; + virtual void AddKeyValuesToMemoryLeakList( const void *const pMem, const HKeySymbol name ) = 0; + virtual void RemoveKeyValuesFromMemoryLeakList( const void *const pMem ) = 0; // Returns the globally allocated keyvalues memory pool. virtual void *GetKeyValuesMemory( void ) = 0; // set/get a value for keyvalues resolution symbol // e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE] - virtual void SetKeyValuesExpressionSymbol( const char *pName, bool bValue ) = 0; - virtual bool GetKeyValuesExpressionSymbol( const char *pName ) = 0; + virtual void SetKeyValuesExpressionSymbol( const char *const pName, const bool bValue ) = 0; + virtual bool GetKeyValuesExpressionSymbol( const char *const pName ) = 0; // symbol table access from code with case-preserving requirements (used for key names) - virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *pName, bool bCreate = true ) = 0; + virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *const pName, const bool bCreate = true ) = 0; }; +IKeyValuesSystem* KeyValuesSystem(); + #endif // VSTDLIB_IKEYVALUESSYSTEM_H diff --git a/r5dev/public/imaterial.h b/r5dev/public/imaterial.h index 44c74244..2f7c6e79 100644 --- a/r5dev/public/imaterial.h +++ b/r5dev/public/imaterial.h @@ -95,11 +95,8 @@ private: virtual void stub_74() const = 0; virtual void stub_75() const = 0; virtual void stub_76() const = 0; - // s0 and s1 builds have a smaller vtable size (2 methods less). -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) virtual void stub_77() const = 0; virtual void stub_78() const = 0; -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 // STUB_138 should be GetShaderGlue. }; diff --git a/r5dev/public/imaterialsystem.h b/r5dev/public/imaterialsystem.h index ce26761c..3fa72fc7 100644 --- a/r5dev/public/imaterialsystem.h +++ b/r5dev/public/imaterialsystem.h @@ -1,6 +1,30 @@ #ifndef IMATERIALSYSTEM_H #define IMATERIALSYSTEM_H +#define NVIDIA_VENDOR_ID 0x10DE + +//----------------------------------------------------------------------------- +// Material adapter info.. +//----------------------------------------------------------------------------- +struct MaterialAdapterInfo_t +{ + enum + { + MATERIAL_ADAPTER_NAME_LENGTH = 512 + }; + + char m_pDriverName[MATERIAL_ADAPTER_NAME_LENGTH]; + unsigned int m_VendorID; + unsigned int m_DeviceID; + unsigned int m_SubSysID; + unsigned int m_Revision; + int m_nDXSupportLevel; // This is the *preferred* dx support level + int m_nMinDXSupportLevel; + int m_nMaxDXSupportLevel; + unsigned int m_nDriverVersionHigh; + unsigned int m_nDriverVersionLow; +}; + //----------------------------------------------------------------------------- // Video mode info.. //----------------------------------------------------------------------------- diff --git a/r5dev/public/inetchannel.h b/r5dev/public/inetchannel.h index bf308731..6878e004 100644 --- a/r5dev/public/inetchannel.h +++ b/r5dev/public/inetchannel.h @@ -51,6 +51,22 @@ public: struct CS_INetChannelHandler : INetChannelHandler {}; +enum netsocket_e +{ + NS_CLIENT = 0, // client socket + NS_SERVER, // server socket + + // unknown as this seems unused in R5, but if the socket equals to this in + // CServer::ConnectionlessPacketHandler() in case C2S_Challenge, the packet + // sent back won't be encrypted + NS_UNK0, + + // used for chat room, communities, discord presence, EA/Origin, etc + NS_PRESENCE, + + MAX_SOCKETS // 4 in R5 +}; + typedef struct netpacket_s { netadr_t from; diff --git a/r5dev/public/inputsystem/AnalogCode.h b/r5dev/public/inputsystem/AnalogCode.h new file mode 100644 index 00000000..ce59433e --- /dev/null +++ b/r5dev/public/inputsystem/AnalogCode.h @@ -0,0 +1,36 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef ANALOGCODE_H +#define ANALOGCODE_H +#include "inputsystem/InputEnums.h" + + +//----------------------------------------------------------------------------- +// Macro to get at joystick codes +//----------------------------------------------------------------------------- +#define JOYSTICK_AXIS_INTERNAL( _joystick, _axis ) ( JOYSTICK_FIRST_AXIS + ((_joystick) * MAX_JOYSTICK_AXES) + (_axis) ) +#define JOYSTICK_AXIS( _joystick, _axis ) ( (AnalogCode_t)JOYSTICK_AXIS_INTERNAL( _joystick, _axis ) ) + + +//----------------------------------------------------------------------------- +// Enumeration for analog input devices. Includes joysticks, mousewheel, mouse +//----------------------------------------------------------------------------- +enum AnalogCode_t +{ + ANALOG_CODE_INVALID = -1, + MOUSE_XY = 0, // Invoked when either x or y changes + MOUSE_WHEEL, + + JOYSTICK_FIRST_AXIS, + JOYSTICK_LAST_AXIS = JOYSTICK_AXIS_INTERNAL(MAX_JOYSTICKS - 1, MAX_JOYSTICK_AXES - 1), + + ANALOG_CODE_LAST, +}; + + +#endif // ANALOGCODE_H diff --git a/r5dev/public/inputsystem/ButtonCode.h b/r5dev/public/inputsystem/ButtonCode.h index 7b47cfa5..9d9f188b 100644 --- a/r5dev/public/inputsystem/ButtonCode.h +++ b/r5dev/public/inputsystem/ButtonCode.h @@ -1,4 +1,6 @@ #pragma once +#include "InputEnums.h" +#include "common/xbox/xboxstubs.h" #define JOYSTICK_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_BUTTON + ((_joystick) * JOYSTICK_MAX_BUTTON_COUNT) + (_button) ) #define JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ( JOYSTICK_FIRST_POV_BUTTON + ((_joystick) * JOYSTICK_POV_BUTTON_COUNT) + (_button) ) @@ -8,24 +10,6 @@ #define JOYSTICK_POV_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL( _joystick, _button ) ) #define JOYSTICK_AXIS_BUTTON( _joystick, _button ) ( (ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL( _joystick, _button ) ) -// Buttons are not confirmed to be the same. They have been always the same throughout the source engine. Lets hope they did not change them. -enum -{ - MAX_JOYSTICKS = MAX_SPLITSCREEN_CLIENTS, - MOUSE_BUTTON_COUNT = 5, -}; - -enum JoystickAxis_t -{ - JOY_AXIS_X = 0, - JOY_AXIS_Y, - JOY_AXIS_Z, - JOY_AXIS_R, - JOY_AXIS_U, - JOY_AXIS_V, - MAX_JOYSTICK_AXES, -}; - enum { JOYSTICK_MAX_BUTTON_COUNT = 32, @@ -150,8 +134,9 @@ enum ButtonCode_t KEY_CAPSLOCKTOGGLE, KEY_NUMLOCKTOGGLE, KEY_SCROLLLOCKTOGGLE, + KEY_CREDITSIGN, - KEY_LAST = KEY_SCROLLLOCKTOGGLE, + KEY_LAST = KEY_CREDITSIGN, KEY_COUNT = KEY_LAST - KEY_FIRST + 1, // Mouse @@ -178,18 +163,17 @@ enum ButtonCode_t JOYSTICK_FIRST_AXIS_BUTTON, JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), - JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, + // New in R5 + JOYSTICK_UP_DOWN, + JOYSTICK_LEFT_RIGHT, + + JOYSTICK_LAST = JOYSTICK_LEFT_RIGHT, BUTTON_CODE_LAST, BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, // Helpers for XBox 360 - KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons - KEY_XBUTTON_RIGHT, - KEY_XBUTTON_DOWN, - KEY_XBUTTON_LEFT, - - KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons + KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons KEY_XBUTTON_B, KEY_XBUTTON_X, KEY_XBUTTON_Y, @@ -200,15 +184,162 @@ enum ButtonCode_t KEY_XBUTTON_STICK1, KEY_XBUTTON_STICK2, KEY_XBUTTON_INACTIVE_START, + KEY_XBUTTON_LTRIGGER_FULL, + KEY_XBUTTON_RTRIGGER_FULL, + KEY_XBUTTON_RELOAD, + KEY_XBUTTON_TRIGGER, + KEY_XBUTTON_PUMP_ACTION, + KEY_XBUTTON_ROLL_RIGHT, + KEY_XBUTTON_ROLL_LEFT, - KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE - KEY_XSTICK1_LEFT, // XAXIS NEGATIVE - KEY_XSTICK1_DOWN, // YAXIS POSITIVE - KEY_XSTICK1_UP, // YAXIS NEGATIVE - KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE - KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE - KEY_XSTICK2_RIGHT, // UAXIS POSITIVE - KEY_XSTICK2_LEFT, // UAXIS NEGATIVE - KEY_XSTICK2_DOWN, // VAXIS POSITIVE - KEY_XSTICK2_UP, // VAXIS NEGATIVE -}; \ No newline at end of file + KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons + KEY_XBUTTON_RIGHT, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + + KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE + KEY_XSTICK1_LEFT, // XAXIS NEGATIVE + KEY_XSTICK1_DOWN, // YAXIS POSITIVE + KEY_XSTICK1_UP, // YAXIS NEGATIVE + KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE + KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE + KEY_XSTICK2_RIGHT, // UAXIS POSITIVE + KEY_XSTICK2_LEFT, // UAXIS NEGATIVE + KEY_XSTICK2_DOWN, // VAXIS POSITIVE + KEY_XSTICK2_UP, // VAXIS NEGATIVE +}; + +//----------------------------------------------------------------------------- +// Inline helpers +//----------------------------------------------------------------------------- +inline bool IsAlpha( const ButtonCode_t code ) +{ + return ( code >= KEY_A ) && ( code <= KEY_Z ); +} + +inline bool IsAlphaNumeric( const ButtonCode_t code ) +{ + return ( code >= KEY_0 ) && ( code <= KEY_Z ); +} + +inline bool IsSpace( const ButtonCode_t code ) +{ + return ( code == KEY_ENTER ) || ( code == KEY_TAB ) || ( code == KEY_SPACE ); +} + +inline bool IsKeypad( const ButtonCode_t code ) +{ + return ( code >= MOUSE_FIRST ) && ( code <= KEY_PAD_DECIMAL ); +} + +inline bool IsPunctuation( const ButtonCode_t code ) +{ + return ( code >= KEY_0 ) && ( code <= KEY_SPACE ) && !IsAlphaNumeric( code ) && !IsSpace( code ) && !IsKeypad( code ); +} + +inline bool IsKeyCode( const ButtonCode_t code ) +{ + return ( code >= KEY_FIRST ) && ( code <= KEY_LAST ); +} + +inline bool IsMouseCode( const ButtonCode_t code ) +{ + return ( code >= MOUSE_FIRST ) && ( code <= MOUSE_LAST ); +} + +inline bool IsJoystickCode( const ButtonCode_t code ) +{ + return ( ( code >= JOYSTICK_FIRST ) && ( code <= JOYSTICK_LAST ) ); +} + +inline bool IsJoystickButtonCode( const ButtonCode_t code ) +{ + return ( code >= JOYSTICK_FIRST_BUTTON ) && ( code <= JOYSTICK_LAST_BUTTON ); +} + +inline bool IsJoystickPOVCode( const ButtonCode_t code ) +{ + return ( code >= JOYSTICK_FIRST_POV_BUTTON ) && ( code <= JOYSTICK_LAST_POV_BUTTON ); +} + +inline bool IsJoystickAxisCode( const ButtonCode_t code ) +{ + return ( code >= JOYSTICK_FIRST_AXIS_BUTTON ) && ( code <= JOYSTICK_LAST_AXIS_BUTTON ); +} + +inline ButtonCode_t GetBaseButtonCode( const ButtonCode_t code ) +{ + if ( IsJoystickButtonCode( code ) ) + { + const int offset = ( code - JOYSTICK_FIRST_BUTTON ) % JOYSTICK_MAX_BUTTON_COUNT; + return (ButtonCode_t)( JOYSTICK_FIRST_BUTTON + offset ); + } + + if ( IsJoystickPOVCode( code ) ) + { + const int offset = ( code - JOYSTICK_FIRST_POV_BUTTON ) % JOYSTICK_POV_BUTTON_COUNT; + return (ButtonCode_t)( JOYSTICK_FIRST_POV_BUTTON + offset ); + } + + if ( IsJoystickAxisCode( code ) ) + { + const int offset = ( code - JOYSTICK_FIRST_AXIS_BUTTON ) % JOYSTICK_AXIS_BUTTON_COUNT; + return (ButtonCode_t)( JOYSTICK_FIRST_AXIS_BUTTON + offset ); + } + + return code; +} + +inline int GetJoystickForCode( const ButtonCode_t code ) +{ + if ( !IsJoystickCode( code ) ) + return 0; + + if ( IsJoystickButtonCode( code ) ) + { + const int offset = ( code - JOYSTICK_FIRST_BUTTON ) / JOYSTICK_MAX_BUTTON_COUNT; + return offset; + } + if ( IsJoystickPOVCode( code ) ) + { + const int offset = ( code - JOYSTICK_FIRST_POV_BUTTON ) / JOYSTICK_POV_BUTTON_COUNT; + return offset; + } + if ( IsJoystickAxisCode( code ) ) + { + const int offset = ( code - JOYSTICK_FIRST_AXIS_BUTTON ) / JOYSTICK_AXIS_BUTTON_COUNT; + return offset; + } + + return 0; +} + +inline ButtonCode_t ButtonCodeToJoystickButtonCode( ButtonCode_t code, int nDesiredJoystick ) +{ + if ( !IsJoystickCode( code ) || nDesiredJoystick == 0 ) + return code; + + nDesiredJoystick = clamp( nDesiredJoystick, 0, MAX_JOYSTICKS - 1 ); + code = GetBaseButtonCode( code ); + + // Now upsample it + if ( IsJoystickButtonCode( code ) ) + { + const int nOffset = code - JOYSTICK_FIRST_BUTTON; + return JOYSTICK_BUTTON( nDesiredJoystick, nOffset ); + } + + if ( IsJoystickPOVCode( code ) ) + { + const int nOffset = code - JOYSTICK_FIRST_POV_BUTTON; + return JOYSTICK_POV_BUTTON( nDesiredJoystick, nOffset ); + } + + if ( IsJoystickAxisCode( code ) ) + { + const int nOffset = code - JOYSTICK_FIRST_AXIS_BUTTON; + return JOYSTICK_AXIS_BUTTON( nDesiredJoystick, nOffset ); + } + + return code; +} diff --git a/r5dev/public/inputsystem/InputEnums.h b/r5dev/public/inputsystem/InputEnums.h new file mode 100644 index 00000000..dcc2f21b --- /dev/null +++ b/r5dev/public/inputsystem/InputEnums.h @@ -0,0 +1,98 @@ +//===========================================================================// +// +// Purpose: +// +//===========================================================================// +#ifndef INPUTENUMS_H +#define INPUTENUMS_H + +// Standard maximum +/- value of a joystick axis +#define MAX_BUTTONSAMPLE 32768 + +#if !defined( _X360 ) +#define INVALID_USER_ID -1 +#else +#define INVALID_USER_ID XBX_INVALID_USER_ID +#endif + +enum +{ +#ifdef _PS3 + MAX_JOYSTICKS = 7, +#else + MAX_JOYSTICKS = 4, +#endif + MOUSE_BUTTON_COUNT = 5, + MAX_NOVINT_DEVICES = 2, +}; + +enum JoystickAxis_t +{ + JOY_AXIS_X = 0, + JOY_AXIS_Y, + JOY_AXIS_Z, + JOY_AXIS_R, + JOY_AXIS_U, + JOY_AXIS_V, + MAX_JOYSTICK_AXES, +}; + +enum JoystickDeadzoneIndex_t +{ + JOYSTICK_DEADZONE_NONE = 0, + JOYSTICK_DEADZONE_XBOX360, + JOYSTICK_DEADZONE_XBOX1, + JOYSTICK_DEADZONE_OTHER +}; + +//----------------------------------------------------------------------------- +// Events +//----------------------------------------------------------------------------- +enum InputEventType_t +{ + IE_ButtonPressed = 0, // m_nData contains a ButtonCode_t + IE_ButtonReleased, // m_nData contains a ButtonCode_t + IE_ButtonDoubleClicked, // m_nData contains a ButtonCode_t + IE_AnalogValueChanged, // m_nData contains an AnalogCode_t, m_nData2 contains the value + + IE_Unknown8 = 8, // Unknown what this is/does, its used in [r5apex.exe+0x297722] and [r5apex.exe+0x297ACD] + + IE_FirstSystemEvent = 100, + IE_Quit = IE_FirstSystemEvent, + IE_ControllerInserted, // m_nData contains the controller ID + IE_ControllerUnplugged, // m_nData contains the controller ID + IE_Close, + IE_WindowSizeChanged, // m_nData contains width, m_nData2 contains height, m_nData3 = 0 if not minimized, 1 if minimized + IE_PS_CameraUnplugged, // m_nData contains code for type of disconnect. + IE_PS_Move_OutOfView, // m_nData contains bool (0, 1) for whether the move is now out of view (1) or in view (0) + + IE_FirstUIEvent = 200, + IE_LocateMouseClick = IE_FirstUIEvent, + IE_SetCursor, + IE_KeyTyped, + IE_KeyCodeTyped, + IE_InputLanguageChanged, + IE_IMESetWindow, + IE_IMEStartComposition, + IE_IMEComposition, + IE_IMEEndComposition, + IE_IMEShowCandidates, + IE_IMEChangeCandidates, + IE_IMECloseCandidates, + IE_IMERecomputeModes, + IE_OverlayEvent, + + IE_FirstVguiEvent = 1000, // Assign ranges for other systems that post user events here + IE_FirstAppEvent = 2000, +}; + +struct InputEvent_t +{ + int m_nType; // Type of the event (see InputEventType_t) + int m_nTick; // Tick on which the event occurred + int m_nData; // Generic 32-bit data, what it contains depends on the event + int m_nData2; // Generic 32-bit data, what it contains depends on the event + int m_nData3; // Generic 32-bit data, what it contains depends on the event +}; + +#endif // INPUTENUMS_H diff --git a/r5dev/public/inputsystem/iinputsystem.h b/r5dev/public/inputsystem/iinputsystem.h new file mode 100644 index 00000000..857808e8 --- /dev/null +++ b/r5dev/public/inputsystem/iinputsystem.h @@ -0,0 +1,153 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// +#ifndef IINPUTSYSTEM_H +#define IINPUTSYSTEM_H + +#include "inputsystem/ButtonCode.h" +#include "inputsystem/AnalogCode.h" +#include "inputsystem/InputEnums.h" + +///----------------------------------------------------------------------------- +/// A handle to a cursor icon +///----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( InputCursorHandle_t ); +#define INPUT_CURSOR_HANDLE_INVALID ( (InputCursorHandle_t)0 ) + +///----------------------------------------------------------------------------- +/// Input event callback +///----------------------------------------------------------------------------- +typedef bool ( *InputEventCallback_t ) ( const InputEvent_t& eventCallback ); + +///----------------------------------------------------------------------------- +/// An enumeration describing well-known cursor icons +///----------------------------------------------------------------------------- +enum InputStandardCursor_t +{ + INPUT_CURSOR_NONE = 0, + INPUT_CURSOR_ARROW, + INPUT_CURSOR_IBEAM, + INPUT_CURSOR_HOURGLASS, + INPUT_CURSOR_CROSSHAIR, + INPUT_CURSOR_WAITARROW, + INPUT_CURSOR_UP, + INPUT_CURSOR_SIZE_NW_SE, + INPUT_CURSOR_SIZE_NE_SW, + INPUT_CURSOR_SIZE_W_E, + INPUT_CURSOR_SIZE_N_S, + INPUT_CURSOR_SIZE_ALL, + INPUT_CURSOR_NO, + INPUT_CURSOR_HAND, + + INPUT_CURSOR_COUNT +}; + +//----------------------------------------------------------------------------- +// Main interface for input. This is a low-level interface +//----------------------------------------------------------------------------- +#define INPUTSYSTEM_INTERFACE_VERSION "InputSystemVersion001" +abstract_class IInputSystem : public IAppSystem +{ +public: + /// Attach, detach input system from a particular window + /// This window should be the root window for the application + /// Only 1 window should be attached at any given time. + virtual void AttachToWindow( const void* const hWnd ) = 0; + virtual void DetachFromWindow( ) = 0; + + /// Enables/disables input. PollInputState will not update current + /// button/analog states when it is called if the system is disabled. + virtual void EnableInput( const bool bEnable ) = 0; + + /// Enables/disables the windows message pump. PollInputState will not. + /// Peek/Dispatch messages if this is disabled. + virtual void EnableMessagePump( const bool bEnable ) = 0; + + /// Gets the time of the last polling in ms. + virtual int GetPollTick() const = 0; + + /// Is a button down? "Buttons" are binary-state input devices (mouse buttons, keyboard keys). + virtual bool IsButtonDown( const ButtonCode_t code ) const = 0; + + /// Returns the tick at which the button was pressed and released. + virtual int GetButtonPressedTick( const ButtonCode_t code ) const = 0; + + /// Returns the joystick deadzone index for connected hardware. + virtual JoystickDeadzoneIndex_t GetJoystickDeadzoneIndex( ) const = 0; + + /// DoNothing; VFTable padding. + virtual bool ReturnFalse( ) const = 0; + + /// Polls the current input state. + virtual void PollInputState( const InputEventCallback_t eventCallback ) = 0; + + /// Posts a user-defined event into the event queue; this is expected + /// to be called in overridden wndprocs connected to the root panel. + virtual void PostUserEvent( const InputEvent_t &event ) = 0; + virtual void PostUserEvent( const InputEventType_t type ) = 0; + + /// Returns the number of joysticks + virtual int GetJoystickCount( ) const = 0; + + /// Sample the joystick and append events to the input queue. + virtual void SampleDevices( void ) = 0; + + virtual void SetRumble( const float fLeftMainMotor, const float fRightMainMotor, const float fLeftTriggerMotor, const float fRightTriggerMotor, const int userId = INVALID_USER_ID ) = 0; + virtual void StopRumble( const int userId = INVALID_USER_ID ) = 0; + + /// Resets the input state. + virtual void ResetInputState() = 0; + + /// Convert back + forth between ButtonCode/AnalogCode + strings. + virtual const char* ButtonCodeToString( const ButtonCode_t code ) const = 0; + virtual ButtonCode_t StringToButtonCode( const char* const pString ) const = 0; + + /// Sleeps until input happens. Pass a negative number to sleep infinitely. + virtual void SleepUntilInput( const int nMaxSleepTimeMS = -1 ) = 0; + + /// Convert back + forth between virtual codes + button codes + virtual ButtonCode_t VirtualKeyToButtonCode( const int nVirtualKey ) const = 0; + virtual int ButtonCodeToVirtualKey( const ButtonCode_t code ) const = 0; + + /// Sets the cursor position. + virtual void SetCursorPosition( const int x, const int y ) = 0; + + /// Tells the input system to generate UI-related events, defined + /// in inputsystem/inputenums.h (see IE_FirstUIEvent) + /// We could have multiple clients that care about UI-related events + /// so we refcount the clients with an Add/Remove strategy. If there + /// are no interested clients, the UI events are not generated. + virtual void AddUIEventListener() = 0; + virtual void RemoveUIEventListener() = 0; + + /// Creates a cursor using one of the well-known cursor icons. + virtual InputCursorHandle_t GetStandardCursor( const InputStandardCursor_t id ) = 0; + + /// Loads a cursor defined in a file. + virtual InputCursorHandle_t LoadCursorFromFile( const char* const pFileName, const char* const pPathID = NULL ) = 0; + + /// Sets the cursor icon. + virtual void SetCursorIcon( const InputCursorHandle_t hCursor ) = 0; + + /// Gets the cursor position. + virtual void GetCursorPosition( const int* const pX, const int* const pY ) = 0; + + /// Mouse capture. + virtual void EnableMouseCapture( const PlatWindow_t hWnd ) = 0; + virtual void DisableMouseCapture( ) = 0; + + // Mouse/Joystick cursor visibility, tell inputsystem when we hide stuff rather than querying the OS which is expensive on OSX. + virtual void SetMouseCursorVisible( const bool bVisible ) = 0; + virtual void SetJoystickCursorVisible( const bool bVisible ) = 0; + + /// Reset the current cursor icon. Used to reset the icon in the case of alt+tabs where the cursor has been forced to a different + /// icon because it was outside of the client rect during the reload. + virtual void ResetCursorIcon() = 0; + + // read and clear accumulated raw input values. + virtual void GetRawMouseAccumulators( int& accumX, int& accumY ) = 0; +}; + +#endif // IINPUTSYSTEM_H diff --git a/r5dev/public/ipackedstore.h b/r5dev/public/ipackedstore.h index 4e26ea44..13fef2f0 100644 --- a/r5dev/public/ipackedstore.h +++ b/r5dev/public/ipackedstore.h @@ -18,16 +18,10 @@ enum EPackedTextureFlags TEXTURE_ENVIRONMENT_MAP = 1 << 10, }; -struct FileHandleTracker_t +enum EPackedStoreTargets { - int m_nFileNumber; - int m_nCurOfs; - HANDLE m_hFileHandle; -}; - -struct pFileHandleTracker_t -{ - FileHandleTracker_t self[1024]; + STORE_TARGET_SERVER, + STORE_TARGET_CLIENT }; #pragma pack(push, 1) diff --git a/r5dev/public/isurfacesystem.h b/r5dev/public/isurfacesystem.h deleted file mode 100644 index 83fd18ab..00000000 --- a/r5dev/public/isurfacesystem.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef ISURFACESYSTEM_H -#define ISURFACESYSTEM_H - -class ISurface -{ -public: - virtual ~ISurface() { }; - virtual bool Init() = 0; - virtual void Think() = 0; - virtual void RunFrame() = 0; - virtual void RunTask() = 0; - virtual void DrawSurface() = 0; - virtual void SetStyleVar() = 0; -}; - -struct CSuggest -{ - CSuggest(const string& svName, int nFlags) - { - m_svName = svName; - m_nFlags = nFlags; - } - bool operator==(const string& a) const - { - return m_svName.compare(a) == 0; - } - bool operator<(const CSuggest& a) const - { - return m_svName < a.m_svName; - } - string m_svName; - int m_nFlags; -}; - -#endif // ISURFACESYSTEM_H diff --git a/r5dev/public/localize/ilocalize.h b/r5dev/public/localize/ilocalize.h new file mode 100644 index 00000000..2c145557 --- /dev/null +++ b/r5dev/public/localize/ilocalize.h @@ -0,0 +1,76 @@ +#ifndef LOCALIZE_H +#define LOCALIZE_H + +// direct references to localized strings +typedef unsigned long StringIndex_t; +const unsigned long INVALID_LOCALIZE_STRING_INDEX = (StringIndex_t)-1; + +abstract_class ILocalize : public IAppSystem +{ +public: + virtual bool LoadLocalizationFileLists() = 0; + + // adds the contents of a file to the localization table + virtual bool AddFile(const char* fileName, const char* pPathID = NULL) = 0; + + // Remove all strings from the table + virtual void RemoveAll() = 0; + + // Finds the localized text for tokenName + virtual wchar_t* Find(char const* tokenName) = 0; + + virtual void* FindIndex_Unknown(StringIndex_t index) = 0; + + // converts an english string to unicode + // returns the number of wchar_t in resulting string, including null terminator + virtual int ConvertANSIToUnicode(const char* ansi, OUT_Z_BYTECAP(unicodeBufferSizeInBytes) wchar_t* unicode, ssize_t unicodeBufferSizeInBytes) = 0; + + // converts an unicode string to an english string + // unrepresentable characters are converted to system default + // returns the number of characters in resulting string, including null terminator + virtual int ConvertUnicodeToANSI(const wchar_t* unicode, OUT_Z_BYTECAP(ansiBufferSize) char* ansi, ssize_t ansiBufferSize) = 0; + + + virtual StringIndex_t FindIndex(StringIndex_t index) = 0; + + //!!! TODO !!! + //void* func_80[3]; + + + //virtual void ConstructString(CLocalize*, char*, __int64, __int64, ...); + + //__int64(__fastcall* GetNameByIndex)(CLocalize*, int); + //__int64(__fastcall* GetValueByIndex)(CLocalize*, int); + //__int64(__fastcall* GetFirstStringIndex)(CLocalize*); + //__int64(__fastcall* GetNextStringIndex)(CLocalize*, int); + + + //void* func_C0[6]; + + + //__int64(__fastcall* ReloadLocalizationFiles)(CLocalize*); + + + //void* func_F8[6]; + + + //__int64(__fastcall* ConvertANSIToUCS2)(CLocalize*, const char*, void*, __int64); + //void(__fastcall* ConvertUCS2ToANSI)(CLocalize*, __int16*, char*, int); +}; + +inline const char* const g_LanguageNames[] = { + "english", + "german", + "french", + "italian", + "korean", + "spanish", + "schinese", + "tchinese", + "russian", + "japanese", + "portuguese", + "polish", +}; + +#endif // LOCALIZE_H diff --git a/r5dev/public/networkstringtabledefs.h b/r5dev/public/networkstringtabledefs.h new file mode 100644 index 00000000..afc0c432 --- /dev/null +++ b/r5dev/public/networkstringtabledefs.h @@ -0,0 +1,76 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef NETWORKSTRINGTABLEDEFS_H +#define NETWORKSTRINGTABLEDEFS_H + +typedef int TABLEID; + +#define INVALID_STRING_TABLE -1 +const unsigned short INVALID_STRING_INDEX = ( unsigned short )-1; + +// table index is sent in log2( MAX_TABLES ) bits +#define MAX_TABLES 32 // Table id is 4 bits + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class INetworkStringTable; + +typedef void ( *pfnStringChanged )( void* object, INetworkStringTable* stringTable, int stringNumber, char const* newString, void const* newData ); + +//----------------------------------------------------------------------------- +// Purpose: Game .dll shared string table interfaces +//----------------------------------------------------------------------------- +class INetworkStringTable +{ +public: + virtual ~INetworkStringTable( void ) {}; + + // Table Info + virtual const char *GetTableName( void ) const = 0; + virtual TABLEID GetTableId( void ) const = 0; + virtual int GetNumStrings( void ) const = 0; + virtual int GetMaxStrings( void ) const = 0; + virtual int GetEntryBits( void ) const = 0; + + // Networking + virtual void SetTick( int tick ) = 0; + virtual bool ChangedSinceTick( int tick ) const = 0; + + // Accessors (length -1 means don't change user data if string already exits) + virtual int AddString( bool bIsServer, const char *value, int length = -1, const void *userdata = 0 ) = 0; + virtual int AddString( const char* value ) = 0; + + virtual const char* GetString( int stringNumber ) = 0; + + virtual int Unused0( void ) = 0; // returns -1 + virtual int Unused1( void ) const = 0; // returns -1 + virtual int Unused2( void ) const = 0; // returns 0 + + virtual void SetStringUserData( int stringNumber, int length, const void *userdata ) = 0; + virtual const void* GetStringUserData( int stringNumber, int* length ) = 0; +}; + +class INetworkStringTableContainer +{ +public: + virtual ~INetworkStringTableContainer( void ) {}; + + // table creation/destruction + virtual INetworkStringTable *CreateStringTable( bool createTableMemory, const char *tableName, int maxentries, int userdatafixedsize = 0, int userdatanetworkbits = 0, int dictFlags = 0 ) = 0; + virtual void RemoveAllTables( void ) = 0; + + // table infos + virtual INetworkStringTable *FindTable( const char *tableName ) const = 0; + virtual INetworkStringTable *GetTable( TABLEID stringTable ) const = 0; + virtual int GetNumTables( void ) const = 0; + + virtual void SetAllowClientSideAddString( INetworkStringTable *table, bool bAllowClientSideAddString ) = 0; +}; + +#endif // NETWORKSTRINGTABLEDEFS_H diff --git a/r5dev/public/rendersystem/schema/texture.g.h b/r5dev/public/rendersystem/schema/texture.g.h index 46f4348a..91fb01a3 100644 --- a/r5dev/public/rendersystem/schema/texture.g.h +++ b/r5dev/public/rendersystem/schema/texture.g.h @@ -29,9 +29,7 @@ uint8_t unknown_5[3]; uint8_t m_nTotalStreamedMipCount; // Does not get set until after RTech::CreateDXTexture. uint8_t unk4[228]; -#ifdef GAMEDLL_S3 uint8_t unk5[57]; -#endif // GAMEDLL_S3 ID3D11Texture2D* m_ppTexture; ID3D11ShaderResourceView* m_ppShaderResourceView; uint8_t m_nTextureMipLevels; diff --git a/r5dev/public/rtech/iasync.h b/r5dev/public/rtech/iasync.h new file mode 100644 index 00000000..a736e489 --- /dev/null +++ b/r5dev/public/rtech/iasync.h @@ -0,0 +1,13 @@ +#ifndef RTECH_IASYNC_H +#define RTECH_IASYNC_H + +#define ASYNC_MAX_FILE_HANDLES 1024 +#define ASYNC_MAX_FILE_HANDLES_MASK (ASYNC_MAX_FILE_HANDLES-1) + +// invalid async request +#define FS_ASYNC_REQ_INVALID -2 +// invalid async file +#define FS_ASYNC_FILE_INVALID -1 + + +#endif // RTECH_IASYNC_H diff --git a/r5dev/public/rtech/ipakfile.h b/r5dev/public/rtech/ipakfile.h index f3ea8073..e2c81a10 100644 --- a/r5dev/public/rtech/ipakfile.h +++ b/r5dev/public/rtech/ipakfile.h @@ -1,17 +1,152 @@ #ifndef RTECH_IPACKFILE_H #define RTECH_IPACKFILE_H +#include "tier0/jobthread.h" +#include "tier0/tslist.h" -#define PLATFORM_PAK_PATH "paks\\Win64\\" -#define PLATFORM_PAK_OVERRIDE_PATH "paks\\Win32\\" +#include "rtech/iasync.h" +#include "rtech/rstdlib.h" -#define INVALID_PAK_HANDLE -1 - -#define PAK_MAX_TYPES 64 +// pak header versions #define PAK_HEADER_MAGIC (('k'<<24)+('a'<<16)+('P'<<8)+'R') +#define PAK_HEADER_VERSION 8 -typedef int RPakHandle_t; +// pak header flags +#define PAK_HEADER_FLAGS_HAS_MODULE (1<<0) +#define PAK_HEADER_FLAGS_HAS_MODULE_EXTENDED PAK_HEADER_FLAGS_HAS_MODULE | (1<<1) -enum class RPakStatus_t : int32_t +#define PAK_HEADER_FLAGS_COMPRESSED (1<<8) + +// use the ZStd decoder instead of the RTech one +#define PAK_HEADER_FLAGS_ZSTREAM_ENCODED (1<<9) + +// max amount of types at runtime in which assets will be tracked +#define PAK_MAX_TYPES 64 +#define PAK_MAX_TYPES_MASK (PAK_MAX_TYPES-1) + +// max amount of global pak assets at runtime +#define PAK_MAX_ASSETS 0x40000 // TODO: rename to PAK_MAX_LOADED_ASSETS +#define PAK_MAX_ASSETS_MASK (PAK_MAX_ASSETS-1) + +// max amount of global pak assets tracked at runtime +#define PAK_MAX_TRACKED_ASSETS (PAK_MAX_ASSETS/2) +#define PAK_MAX_TRACKED_ASSETS_MASK (PAK_MAX_TRACKED_ASSETS-1) + +// max amount of segments a pak file could have +#define PAK_MAX_SEGMENTS 20 + +// max amount of buffers in which segments get copied in +#define PAK_SEGMENT_BUFFER_TYPES 4 + +// max amount of streaming files that could be opened per set for a pak, so if a +// pak uses more than one set, this number would be used per set +#define PAK_MAX_STREAMING_FILE_HANDLES_PER_SET 4 + +// max amount of paks that could be loaded at runtime +#define PAK_MAX_HANDLES 512 // TODO: rename to PAK_MAX_LOADED_PAKS +#define PAK_MAX_HANDLES_MASK (PAK_MAX_HANDLES-1) + +// max amount of async streaming requests that could be made per pak file, if a +// pak file has more patches than this number, and is already trying to stream +// this amount in, all remainder patches would have to wait until one slot +// becomes available again +#define PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS 8 +#define PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK (PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS-1) + +// the minimum stream ring buffer size when a pak is loaded who's compressed +// size is below PAK_DECODE_IN_RING_BUFFER_SIZE, the system allocates a +// buffer with the size of the compressed pak + the value of this define +// the system should still use the default ring buffer input size for decoding +// as these pak files get decoded in one-go; there is no input buffer wrapping +// going on +#define PAK_DECODE_IN_RING_BUFFER_SMALL_SIZE 0x1000 +#define PAK_DECODE_IN_RING_BUFFER_SMALL_MASK (PAK_DECODE_IN_RING_BUFFER_SMALL_SIZE-1) + +// the input stream ring buffer size for pak decoder before wrapping around +#define PAK_DECODE_IN_RING_BUFFER_SIZE 0x1000000 +#define PAK_DECODE_IN_RING_BUFFER_MASK (PAK_DECODE_IN_RING_BUFFER_SIZE-1) + +// the output stream ring buffer size in which input buffer gets decoded to, we +// can only decode up to this many bytes before we have to wrap around +#define PAK_DECODE_OUT_RING_BUFFER_SIZE 0x400000 +#define PAK_DECODE_OUT_RING_BUFFER_MASK (PAK_DECODE_OUT_RING_BUFFER_SIZE-1) + +// max amount to read per async fs read request +#define PAK_READ_DATA_CHUNK_SIZE (1ull << 19) + +// base pak directory containing paks sorted in platform specific subdirectories +#define PAK_BASE_PATH "paks\\" +#define PLATFORM_PAK_PATH PAK_BASE_PATH"Win64\\" + +// pak override directory; the system will looks for pak files in this directory +// first before falling back to PLATFORM_PAK_PATH +#define PLATFORM_PAK_OVERRIDE_PATH PAK_BASE_PATH"Win64_override\\" + +// the handle that should be returned when a pak failed to load or process +#define INVALID_PAK_HANDLE -1 // TODO: rename to PAK_INVALID_HANDLE + +#define PAK_MAX_DISPATCH_LOAD_JOBS 4 +#define PAK_DEFAULT_JOB_GROUP_ID 0x3000 + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +struct PakFile_t; + +//----------------------------------------------------------------------------- +// Handle types +//----------------------------------------------------------------------------- +typedef int PakHandle_t; +typedef uint64_t PakGuid_t; + +//----------------------------------------------------------------------------- +// Page handle types +//----------------------------------------------------------------------------- +struct PakPageHeader_t +{ + uint32_t segmentIdx; + uint32_t pageAlignment; + uint32_t dataSize; +}; + +// ptr.index != UINT32_MAX +// ptr.offset != UINT32_MAX +// ptr.index < pak->GetPageCount() +// ptr.offset <= pak->GetPageSize(ptr.index) +#define IS_PAKPTR_VALID(pak, ptr) ((ptr)->index != UINT32_MAX && (ptr)->offset != UINT32_MAX && (ptr)->index < (pak)->GetPageCount() && (ptr)->offset <= (pak)->GetPageSize((ptr)->index)) +#define ASSERT_PAKPTR_VALID(pak, ptr) Assert(IS_PAKPTR_VALID(pak, ptr), "Invalid pak page pointer") + +union PakPage_t +{ + struct + { + uint32_t index; + uint32_t offset; + }; + void* ptr; +}; + +//----------------------------------------------------------------------------- +// Enumerations +//----------------------------------------------------------------------------- +enum EPakDecodeMode : int32_t +{ + MODE_DISABLED = -1, + + // the default decoder + MODE_RTECH, + MODE_ZSTD +}; + +enum EPakStreamSet +{ + STREAMING_SET_MANDATORY = 0, + STREAMING_SET_OPTIONAL, + + // number of streaming sets + STREAMING_SET_COUNT +}; + +enum EPakStatus : int32_t { PAK_STATUS_FREED = 0, PAK_STATUS_LOAD_PENDING = 1, @@ -31,230 +166,661 @@ enum class RPakStatus_t : int32_t PAK_STATUS_BUSY = 15 }; -struct RPakAssetBinding_t +struct PakAssetBinding_t { - uint32_t m_nExtension; // For example '0x6C74616D' for the material asset. - int m_iVersion; - const char* m_szDescription; // Description/Name of asset. - void* m_pLoadAssetFunction; - void* m_pUnloadAssetFunction; - void* m_pReplaceAssetFunction; - void* m_pAllocAssetFunctions; - int m_iSubHeaderSize; - int m_iNativeClassSize; // Native class size, for 'material' it would be CMaterialGlue full size. - uint32_t m_HeaderAlignment; - int unk3; + enum EType + { + NONE = 0, + + // not registered, assets with type 'NONE' + // will be stubbed with this value! + STUB, + + // explicitly registered by pak or code + REGISTERED + }; + + // for example '0x6C74616D' for the material asset. + uint32_t extension; + uint32_t version; + + // description or name of asset. + const char* description; + + // asset specific load callbacks + void* loadAssetFunc; + void* unloadAssetFunc; + void* replaceAssetFunc; + + CAlignedMemAlloc* allocator; + + unsigned int headerSize; + unsigned int nativeClassSize; // Native class size, for 'material' it would be CMaterialGlue full size. + unsigned int headerAlignment; + + // the type of this asset bind + // NOTE: the asset bind will be stubbed if its 'NONE' in runtime! + EType type; + // [ PIXIE ]: Should be the full size across Season 0-3. }; -struct RPakAssetEntry_t +struct PakAsset_t { - uint64_t m_Guid; - uint64_t m_Padding; - uint32_t m_nHeadPageIdx; - uint32_t m_nHeadPageOffset; - uint32_t m_nCpuPageIdx; - uint32_t m_nCpuPageOffset; - uint64_t m_nStarpakOffset; - uint64_t m_nStarpakOptOffset; - uint16_t m_nPageEnd; - uint16_t unk1; - uint32_t m_nRelationsStartIdx; - uint32_t m_nUsesStartIdx; - uint32_t m_nRelationsCount; - uint32_t m_nUsesCount; - uint32_t m_nAssetHeaderSize; - uint32_t m_nVersion; - uint32_t m_nMagic; + // the guid of this asset, which will be used to index into, and retrieve + // this asset from the hash table + PakGuid_t guid; + uint64_t padding; // Unknown. + + PakPage_t headPtr; + PakPage_t dataPtr; + + // offset to the streaming data in the streaming set + uint64_t streamingDataFileOffset[STREAMING_SET_COUNT]; + uint16_t pageEnd; + + // the number of remaining dependencies that are yet to be resolved + uint16_t numRemainingDependencies; + uint32_t dependentsIndex; + uint32_t dependenciesIndex; + uint32_t dependentsCount; + uint32_t dependenciesCount; + + // size of the asset's header + uint32_t headerSize; + + // versions of the asset + uint32_t version; + uint32_t magic; + + FORCEINLINE uint8_t HashTableIndexForAssetType() const + { + return (((0x1020601 * magic) >> 24) & PAK_MAX_TYPES_MASK); + } }; -struct RPakAssetEntryShort +struct PakAssetShort_t { - uint64_t m_Guid; - uint64_t m_Padding; - void* m_pHead; - void* m_pCpu; + PakGuid_t guid; + uint32_t trackerIndex; + uint32_t unk_C; + void* head; + void* cpu; }; -struct RPakGlobals_t +struct PakAssetTracker_s { - RPakAssetBinding_t m_nAssetBindings[64]; // [ PIXIE ]: Max possible registered assets on Season 3, 0-2 I did not check yet. - RPakAssetEntryShort m_Assets[0x40000]; - // End size unknown. + void* memPage; + int trackerIndex; + int loadedPakIndex; + uint8_t assetTypeHashIdx; + char unk_11[3]; + char unk_10[4]; }; -struct RPakHeader_t +struct PakTracker_s { - uint32_t m_nMagic; // 'RPak' - uint16_t m_nVersion; // R2 = '7' R5 = '8' - uint8_t m_nFlags[0x2]; // - FILETIME m_nFileTime; // - uint64_t m_nHash; // - uint64_t m_nSizeDisk; // Compressed size - uint64_t m_nEmbeddedStarpakOffset; // - uint8_t unk0[0x8]; // - uint64_t m_nSizeMemory; // Decompressed size - uint64_t m_nEmbeddedStarpakSize; // - uint8_t unk1[0x8]; // - - uint16_t m_nStarpakReferenceSize; // - uint16_t m_nStarpakOptReferenceSize; // - uint16_t m_nVirtualSegmentCount; // * 0x10 - uint16_t m_nMemPageCount; // * 0xC - - uint32_t m_nPatchIndex; // - - uint32_t m_nDescriptorCount; // - uint32_t m_nAssetEntryCount; // File entry count - uint32_t m_nGuidDescriptorCount; // - uint32_t m_nRelationsCounts; // - - uint8_t unk2[0x10]; // - uint32_t m_nMemPageOffset; // Size not verified. Offsets every page by x amount, if not 0 start of first page has data corresponding for 'patching some page' - uint8_t unk3[0x8]; // + uint32_t numPaksTracked; + int unk_4; + int unk_8; + char gap_C[644100]; + int loadedAssetIndices[PAK_MAX_HANDLES]; + char gap_9DC04[522240]; }; -struct RPakPatchCompressedHeader_t -{ - uint64_t m_nSizeDisk; - uint64_t m_nSizeMemory; -}; - -struct RPakDecompState_t -{ - uint64_t m_nInputBuf; - uint64_t m_nOut; - uint64_t m_nMask; - uint64_t m_nOutMask; - uint64_t m_nTotalFileLen; - uint64_t m_nDecompSize; - uint64_t m_nInvMaskIn; - uint64_t m_nInvMaskOut; - uint32_t m_nHeaderOffset; - uint32_t dword44; - uint64_t m_nInputBytePos; - uint64_t m_nDecompPosition; - uint64_t m_nLengthNeeded; - uint64_t byte; - uint32_t m_nByteBitOffset; - uint32_t dword6C; - uint64_t qword70; - uint64_t m_nCompressedStreamSize; - uint64_t m_nDecompStreamSize; -}; - -class RPakLoadedInfo_t +class PakLoadedInfo_t { public: - RPakHandle_t m_nHandle; //0x0000 - RPakStatus_t m_nStatus; //0x0004 - uint64_t m_nUnk1; //0x0008 - uint32_t m_nUnk2; //0x0010 - uint32_t m_nAssetCount; //0x0014 - char* m_pszFileName; //0x0018 - void* m_pMalloc; //0x0020 - uint64_t* m_pAssetGuids; //0x0028 size of the array is m_nAssetCount -#if defined (GAMEDLL_S3) - void* m_pVSegBuffers[4]; //0x0030 - char pad_0050[16]; //0x0050 - void* m_pPakInfo; //0x0060 - RPakLoadedInfo_t* m_pUnknownLoadedPakInfo; //0x0068 - char pad_0070[4]; //0x0070 - int8_t m_nUnk3; //0x0074 - char pad_0075[51]; //0x0075 - uint32_t m_nUnk4; //0x00A8 - uint8_t m_nUnk5; //0x00AC -#endif -#if !defined (GAMEDLL_S3) - char pad_0030[128]; //0x0030 - char pad_00B0[48]; //0x00B0 -#endif // !GAMEDLL_S3 - uint64_t m_nUnkEnd; //0x00B0/0x00E8 + struct StreamingInfo_t + { + inline void Reset() + { + embeddedStarpakName = nullptr; + streamFileNumber[0] = FS_ASYNC_FILE_INVALID; + streamFileNumber[1] = FS_ASYNC_FILE_INVALID; + streamFileNumber[2] = FS_ASYNC_FILE_INVALID; + streamFileNumber[3] = FS_ASYNC_FILE_INVALID; + streamFileCount = NULL; + streamingDisabled = true; + } + + char* embeddedStarpakName; + int streamFileNumber[PAK_MAX_STREAMING_FILE_HANDLES_PER_SET]; + int streamFileCount; + bool streamingDisabled; + char padding_maybe[3]; + }; + + PakHandle_t handle; + EPakStatus status; + JobID_t loadJobId; + uint32_t padding_maybe; + + // the log level of the pak, this is also used for errors if a pak failed + // to load; the higher the level, the more important this pak file is + int logLevel; + + uint32_t assetCount; + const char* fileName; + CAlignedMemAlloc* allocator; + PakGuid_t* assetGuids; //size of the array is m_nAssetCount + void* segmentBuffers[PAK_SEGMENT_BUFFER_TYPES]; + _QWORD qword50; + FILETIME fileTime; + PakFile_t* pakFile; + StreamingInfo_t streamInfo[STREAMING_SET_COUNT]; + uint32_t fileHandle; + uint8_t m_nUnk5; + HMODULE hModule; + }; //Size: 0x00B8/0x00E8 -struct RPakDescriptor_t +struct PakGlobals_s { - uint32_t m_Index; - uint32_t m_Offset; + // [ PIXIE ]: Max possible registered assets on Season 3, 0-2 I did not check yet. + PakAssetBinding_t assetBindings[PAK_MAX_TYPES]; + PakAssetShort_t loadedAssets[PAK_MAX_ASSETS]; + + // assets that are tracked across all asset types + PakAssetTracker_s trackedAssets[PAK_MAX_TRACKED_ASSETS]; + + RHashMap trackedAssetMap; // links to 'trackedAssets' + RHashMap loadedPakMap; // links to 'loadedPaks' + + // all currently loaded pak handles + PakLoadedInfo_t loadedPaks[PAK_MAX_HANDLES]; + + RMultiHashMap unkMap2; // links to 'unkIntArray' and 'unkIntArray2' + int unkIntArray[PAK_MAX_TRACKED_ASSETS]; + int unkIntArray2[PAK_MAX_ASSETS]; + + // whether asset streaming (mandatory & optional) is enabled + b64 useStreamingSystem; + + // whether we should emulate (fake) our streaming install for debugging + b64 emulateStreamingInstallInit; + b64 emulateStreamingInstall; + + // mounted # streamable assets (globally across all paks) + int64_t numStreamableAssets; + b64 hasPendingUnloadJobs; + + // paks that contain tracked assets + PakTracker_s* pakTracker; + + // called when threads have to be synced (e.g. syncing the render thread + // when we execute the unloading of paks and assets) + void* threadSyncFunc; + + // the index to the asset in the trackedAssets array of the last asset we + // tracked + int lastAssetTrackerIndex; + bool updateSplitScreenAnims; + + // the current # pending asset loading jobs + int16_t numAssetLoadJobs; + JobFifoLock_s fifoLock; + JobID_t pakLoadJobId; + + int16_t loadedPakCount; + int16_t requestedPakCount; + + PakHandle_t loadedPakHandles[PAK_MAX_HANDLES]; + + JobTypeID_t assetBindJobTypes[PAK_MAX_TYPES]; + JobTypeID_t jobTypeSlots_Unused[PAK_MAX_TYPES]; + + JobTypeID_t dispatchLoadJobTypes[PAK_MAX_DISPATCH_LOAD_JOBS]; + uint8_t dispathLoadJobPriorities[PAK_MAX_DISPATCH_LOAD_JOBS]; // promoted to JobPriority_e + uint32_t dispatchLoadJobAffinities[PAK_MAX_DISPATCH_LOAD_JOBS]; + + RTL_SRWLOCK cpuDataLock; + char unknown_or_unused[32]; + void* addToMapFunc; + void* removeFromMapFunc; + __int64 qword_167ED8540; + int dword_167ED8548; + int dword_167ED854C; + __int64 qword_167ED8550; + int dword_167ED8558; + int unknown_dword_or_nothing; + int dword_167ED8560; + int numPatchedPaks; + const char** patchedPakFiles; + uint8_t* patchNumbers; }; -struct RPakMemPageInfo_t +struct PakPatchFileHeader_t { - uint32_t m_nVirtualSegmentIndex; - uint32_t m_nPageAlignment; - uint32_t m_nDataSize; + uint64_t compressedSize; + uint64_t decompressedSize; }; -struct RPakVirtualSegment_t +struct PakPatchDataHeader_t { - uint32_t m_nFlags; - uint32_t m_nAlignment; - uint64_t m_nDataSize; + uint32_t editStreamSize; + uint32_t pageCount; }; -struct PakFile_t // !TODO: Map out on S1 and S2! +struct PakFileHeader_t { - int m_nDescCount; - int m_nProcessedAssetCount; - int m_nPageEnd; - int m_nPageStart; - uint32_t m_nPatchIndex_maybe; + inline uint32_t GetTotalStreamingNamesBufferSize() const + { + return( + streamingFilesBufSize[STREAMING_SET_MANDATORY] + + streamingFilesBufSize[STREAMING_SET_OPTIONAL]); + } + + inline uint64_t GetTotalEmbeddedStreamingDataSize() const + { + return ( + embeddedStreamingDataSize[STREAMING_SET_MANDATORY] + + embeddedStreamingDataSize[STREAMING_SET_OPTIONAL]); + } + + inline uint64_t GetTotalHeaderSize() const + { + uint64_t headerSize = sizeof(PakFileHeader_t); + + // if we have patches, we should include the patch header as well + if (patchIndex > 0) + headerSize += sizeof(PakPatchDataHeader_t); + + // the streaming file paths belong to the header as well + headerSize += GetTotalStreamingNamesBufferSize(); + return headerSize; + } + + inline EPakDecodeMode GetCompressionMode() const + { + if (flags & PAK_HEADER_FLAGS_ZSTREAM_ENCODED) + return EPakDecodeMode::MODE_ZSTD; + + // NOTE: this should be the first check once we rebuilt function + // 14043F300 and alloc ring buffer for the flags individually instead + // instead of having to define the main compress flag (which really + // just means that the pak is using the RTech decoder) + else if (flags & PAK_HEADER_FLAGS_COMPRESSED) + return EPakDecodeMode::MODE_RTECH; + + return EPakDecodeMode::MODE_DISABLED; + } + + // file versions + uint32_t magic; // 'RPak' + uint16_t version; // R2 = '7' R5 = '8' + + // pak file flags + uint16_t flags; + + // when this pak file was built + FILETIME fileTime; + uint64_t checksum; + + // compressed size of the pak file, this includes the header + uint64_t compressedSize; + + // offset to the embedded mandatory and optional streaming data + // NOTE: this should be NULL if external streaming sets are used + uint64_t embeddedStreamingDataOffset[STREAMING_SET_COUNT]; + + // decompressed size of this pak, this includes the header + // NOTE: if the pak is uncompressed, this will equal compressedSize + uint64_t decompressedSize; + + // size of the embedded mandatory and optional streaming data + // NOTE: this should be NULL if external streaming sets are used + uint64_t embeddedStreamingDataSize[STREAMING_SET_COUNT]; + + // size of the string array containing paths to external streaming files + uint16_t streamingFilesBufSize[STREAMING_SET_COUNT]; + + // number of segments in this pak; absolute max = PAK_MAX_SEGMENTS + uint16_t virtualSegmentCount; + + // number of memory pages to allocate for this pak + uint16_t memPageCount; + + uint16_t patchIndex; + + uint32_t descriptorCount; + + // number of assets in this pak + uint32_t assetCount; + uint32_t guidDescriptorCount; + uint32_t relationsCounts; + + uint8_t unk2[0x10]; + + // size not verified. offsets every page by x amount, if not 0 start of first page has data corresponding for 'patching some page' + uint32_t memPageOffset; + + uint8_t unk3[0x8]; +}; static_assert(sizeof(PakFileHeader_t) == 0x80); + +// segment flags +#define SF_HEAD (0) +#define SF_TEMP (1 << 0) // 0x1 +#define SF_CPU (1 << 1) // 0x2 +#define SF_DEV (1 << 8) // 0x80 + +struct PakSegmentHeader_t +{ + int typeFlags; + int dataAlignment; + size_t dataSize; +}; + +struct PakSegmentDescriptor_t +{ + size_t assetTypeCount[PAK_MAX_TYPES]; + int64_t segmentSizes[PAK_MAX_SEGMENTS]; + + size_t segmentSizeForType[PAK_SEGMENT_BUFFER_TYPES]; + int segmentAlignmentForType[PAK_SEGMENT_BUFFER_TYPES]; +}; + +struct PakDecoder_t +{ + const uint8_t* inputBuf; + uint8_t* outputBuf; + + uint64_t inputMask; + uint64_t outputMask; + + size_t fileSize; + size_t decompSize; + + uint64_t inputInvMask; + uint64_t outputInvMask; + + uint32_t headerOffset; + + // this field was unused, it now contains the decoder mode + EPakDecodeMode decodeMode; + + // NOTE: unless you are in the RTech decoder, use the getter if you need to + // get the current pos!!! + uint64_t inBufBytePos; + // NOTE: unless you are in the RTech decoder, use the getter if you need to + // get the current pos!!! + uint64_t outBufBytePos; + + size_t bufferSizeNeeded; + + // current byte and current bit of byte + uint64_t currentByte; + uint32_t currentBit; + + uint32_t dword6C; + uint64_t qword70; + + union + { + size_t compressedStreamSize; + + // compressedStreamSize isn't used on ZStd paks, instead, we need to + // store the frame header size + size_t frameHeaderSize; + }; + + union + { + size_t decompressedStreamSize; + + // decompressedStreamSize isn't used on ZStd paks; use this space for + // the decoder + ZSTD_DStream* zstreamContext; + }; +}; + +struct PakRingBufferFrame_t +{ + size_t bufIndex; + size_t frameLen; +}; + +struct PakFileStream_t +{ + struct Descriptor + { + size_t dataOffset; + size_t compressedSize; + size_t decompressedSize; + + // NOTE: if this is set, the game sets 'PakMemoryData_t::processedPatchedDataSize' + // to 'dataOffset'; else its getting set to 'sizeof(PakFileHeader_t)'. + EPakDecodeMode compressionMode; + }; + + _QWORD qword0; + _QWORD fileSize; + int fileHandle; + int asyncRequestHandles[32]; + _BYTE gap94[32]; + unsigned int numDataChunksProcessed; + _DWORD numDataChunks; + _BYTE fileReadStatus; + bool finishedLoadingPatches; + _BYTE gapBE; + _BYTE numLoadedFiles; + Descriptor descriptors[PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS]; + uint8_t* buffer; + _QWORD bufferMask; + _QWORD bytesStreamed; +}; + +typedef struct PakPatchFuncs_s +{ + typedef bool (*PatchFunc_t)(PakFile_t* const pak, size_t* const numAvailableBytes); + + enum EPatchCommands + { + PATCH_CMD0, + PATCH_CMD1, + PATCH_CMD2, + PATCH_CMD3, + PATCH_CMD4, + PATCH_CMD5, // Same as cmd4. + PATCH_CMD6, + + // !!! NOT A CMD !!! + PATCH_CMD_COUNT + }; + + inline PatchFunc_t operator[](ssize_t i) const + { + Assert((i >= 0) && (i < SDK_ARRAYSIZE(patchFuncs))); + return patchFuncs[i]; + } + + PatchFunc_t patchFuncs[PATCH_CMD_COUNT]; + +} PakPatchFuncs_t; + +struct PakMemoryData_t +{ + uint64_t processedPatchedDataSize; + char* patchData; // pointer to patch stream data + + char* patchDataPtr; + RBitRead bitBuf; + uint32_t patchDataOffset; + + _BYTE patchCommands[64]; + + _BYTE PATCH_field_68[64]; + _BYTE PATCH_unk2[256]; + _BYTE PATCH_unk3[256]; + + _QWORD field_2A8; + + // number of bytes remaining in the patch stream data + size_t patchSrcSize; + + // pointer to the location in the pak that a patch command is writing to + char* patchDstPtr; + + size_t numBytesToProcess_maybe; + PakPatchFuncs_s::PatchFunc_t patchFunc; + + uint64_t qword2D0; + PakHandle_t pakId; + JobID_t assetLoadJobId; + int* loadedAssetIndices; + uint8_t** memPageBuffers; + + PakPatchFileHeader_t* patchHeaders; + unsigned short* patchIndices; + + char* streamingFilePaths[STREAMING_SET_COUNT]; + + PakSegmentHeader_t* segmentHeaders; + PakPageHeader_t* pageHeaders; + + PakPage_t* virtualPointers; + PakAsset_t* assetEntries; + + PakPage_t* guidDescriptors; + uint32_t* fileRelations; + + char gap5E0[32]; + + PakPatchDataHeader_t* patchDataHeader; + PakAsset_t** ppAssetEntries; + + int someAssetCount; + int numShiftedPointers; + + // array of sizes/offsets in the SF_HEAD segment buffer + __int64 unkAssetTypeBindingSizes[PAK_MAX_TYPES]; + + const char* fileName; + PakFileHeader_t pakHeader; + PakPatchFileHeader_t patchHeader; +}; + +struct PakFile_t +{ + int numProcessedPointers; + uint32_t processedAssetCount; + int processedPageCount; + int firstPageIdx; + + uint32_t patchCount; uint32_t dword14; - char gap18[184]; - uint32_t unsigned_intD0; - char gapD4[284]; - uint64_t m_nInputBytePos; + + PakFileStream_t fileStream; + uint64_t inputBytePos; uint8_t byte1F8; char gap1F9[4]; uint8_t byte1FD; - uint8_t byte1FE; - uint8_t byte200; - RPakDecompState_t m_PakDecompState; - uint64_t qword288; - uint64_t qword290; + bool isOffsetted_MAYBE; + bool isCompressed; + PakDecoder_t pakDecoder; + uint8_t* decompBuffer; + size_t maxCopySize; uint64_t qword298; - uint64_t qword2A0; - char* m_PatchData; - char gap2B0[696]; - unsigned __int8(__fastcall* pfunc568)(__int64, LARGE_INTEGER*, unsigned __int64); - uint64_t qword570; - uint64_t qword578; - int* qword580; - uint8_t** m_ppPagePointers; - void* m_pPatchCompressPairs; - uint64_t qword598; - char* m_pszStreamingFilePaths; - char* m_pszOptStreamingFilePaths; - void* m_pVirtualSegments; - RPakMemPageInfo_t* m_pMemPages; - RPakDescriptor_t* m_pVirtualPointers; - RPakAssetEntry_t* m_pAssetEntries; - RPakDescriptor_t* m_pGuidDescriptors; - uint32_t* m_pFileRelations; - char gap5E0[40]; - RPakAssetEntry_t** m_ppAssetEntries; - char gap610[256]; -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) // TODO: needs to be checked. - char gap710[256]; -#if !defined (GAMEDLL_S2) - char gap810[8]; -#endif // !(GAMEDLL_S0) || !(GAMEDLL_S1) || !(GAMEDLL_S2) -#endif - const char* m_pszFileName; - RPakHeader_t m_PakHdr; + + PakMemoryData_t memoryData; + + inline const char* GetName() const + { + return memoryData.fileName; + } + + inline const PakFileHeader_t& GetHeader() const + { + return memoryData.pakHeader; + } + + inline uint32_t GetAssetCount() const + { + return GetHeader().assetCount; + } + + inline uint32_t GetPointerCount() const + { + return GetHeader().descriptorCount; + } + + // --- pages --- + inline uint16_t GetPageCount() const + { + return GetHeader().memPageCount; + } + + inline bool IsPageOffsetValid(uint32_t index, uint32_t offset) const + { + // validate page index + if (index == UINT32_MAX || index > GetPageCount()) + return false; + + // !TODO! find some other way of validating offset within page + // commented because otherwise we incorrectly return false a lot. + // because of pointer shifting, the offset can't be easily validated like this + // + //if (offset == UINT32_MAX || && offset > GetPageSize(index)) + // return false; + + return true; + } + + inline const PakPageHeader_t* GetPageHeader(const uint32_t i) const + { + assert(i != UINT32_MAX && i < GetPageCount()); + + return &memoryData.pageHeaders[i]; + } + + inline uint32_t GetPageSize(const uint32_t i) const + { + assert(i != UINT32_MAX && i < GetPageCount()); + return memoryData.pageHeaders[i].dataSize; + } + + inline void* GetPointerForPageOffset(const uint32_t index, const uint32_t offset) const + { + assert(IsPageOffsetValid(index, offset)); + + return memoryData.memPageBuffers[index] + offset; + } + + inline void* GetPointerForPageOffset(const PakPage_t& ptr) const + { + assert(IsPageOffsetValid(ptr.index, ptr.offset)); + + return memoryData.memPageBuffers[ptr.index] + ptr.offset; + } + + inline void* GetPointerForPageOffset(const PakPage_t* ptr) const + { + assert(IsPageOffsetValid(ptr->index, ptr->offset)); + + return memoryData.memPageBuffers[ptr->index] + ptr->offset; + } + + // --- segments --- + inline uint16_t GetSegmentCount() const + { + return GetHeader().virtualSegmentCount; + } + + inline const PakSegmentHeader_t* GetSegmentHeader(const uint32_t i) const + { + assert(i < GetSegmentCount()); + + return &memoryData.segmentHeaders[i]; + } }; -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) -#if !defined (GAMEDLL_S2) -static_assert(sizeof(PakFile_t) == 2208); // S3+ -#else -static_assert(sizeof(PakFile_t) == 2200); // S2 -#endif // !GAMEDLL_S2 -#else -static_assert(sizeof(PakFile_t) == 1944); // S0/S1 -#endif // !GAMEDLL_S0 && !GAMEDLL_S1 -static_assert(sizeof(RPakDecompState_t) == 136); -static_assert(sizeof(RPakPatchCompressedHeader_t) == 16); +static_assert(sizeof(PakTracker_s) == 0x11D410); +static_assert(sizeof(PakFile_t) == 2224); // S3+ +static_assert(sizeof(PakLoadedInfo_t) == 184); +static_assert(sizeof(PakDecoder_t) == 136); +static_assert(sizeof(PakPatchFileHeader_t) == 16); +static_assert(sizeof(PakPatchDataHeader_t) == 8); +static_assert(sizeof(PakGlobals_s) == 0xC98A48); #endif // RTECH_IPACKFILE_H diff --git a/r5dev/public/rtech/rson.h b/r5dev/public/rtech/rson.h new file mode 100644 index 00000000..ad7db46d --- /dev/null +++ b/r5dev/public/rtech/rson.h @@ -0,0 +1,135 @@ +#pragma once +#include "mathlib/color.h" +#include "tier0/tslist.h" +#include "tier1/utlbuffer.h" +#include "public/ifilesystem.h" +#include "filesystem/filesystem.h" + +class RSON +{ +public: + enum eFieldType + { + RSON_NULL = 0x1, + RSON_STRING = 0x2, + RSON_VALUE = 0x4, + RSON_OBJECT = 0x8, + RSON_BOOLEAN = 0x10, + RSON_INTEGER = 0x20, + RSON_SIGNED_INTEGER = 0x40, + RSON_UNSIGNED_INTEGER = 0x80, + RSON_DOUBLE = 0x100, + RSON_ARRAY = 0x1000, + }; + + + //------------------------------------------------------------------------- + // + //------------------------------------------------------------------------- + struct Field_t; + + + //------------------------------------------------------------------------- + // + //------------------------------------------------------------------------- + union Value_t + { + inline Field_t* GetSubKey() const { return pSubKey; }; + inline const char* GetString() const { return pszString; }; + inline int64_t GetInt() const { return integerValue; }; + + Field_t* pSubKey; + char* pszString; + int64_t integerValue; + }; + + //------------------------------------------------------------------------- + // used for the root node of rson tree + //------------------------------------------------------------------------- + struct Node_t + { + eFieldType m_Type; + int m_nValueCount; + Value_t m_Value; + + inline Field_t* GetFirstSubKey() const; + + // does not support finding a key in a different level of the tree + inline Field_t* FindKey(const char* const pszKeyName) const; + }; + + //------------------------------------------------------------------------- + // used for every other field of the rson tree + //------------------------------------------------------------------------- + struct Field_t + { + char* m_pszName; + Node_t m_Node; + Field_t* m_pNext; + Field_t* m_pPrev; + + // Inlines + inline const char* GetString() const { return (m_Node.m_Type == RSON_STRING) ? m_Node.m_Value.GetString() : NULL; }; + inline Field_t* GetNextKey() const { return m_pNext; }; + inline Field_t* GetLastKey() const { return m_pPrev; }; + + inline Field_t* GetFirstSubKey() const { return m_Node.GetFirstSubKey(); }; + inline Field_t* FindKey(const char* pszKeyName) const { return m_Node.FindKey(pszKeyName); }; + }; + +public: + static Node_t* LoadFromBuffer(const char* pszBufferName, char* pBuffer, eFieldType rootType); + static Node_t* LoadFromFile(const char* pszFilePath, const char* pPathID = nullptr); +}; + + +/////////////////////////////////////////////////////////////////////////////// + +RSON::Field_t* RSON::Node_t::GetFirstSubKey() const +{ + if (m_Type & eFieldType::RSON_OBJECT) + return m_Value.pSubKey; + + return NULL; +}; + +RSON::Field_t* RSON::Node_t::FindKey(const char* const pszKeyName) const +{ + if ((m_Type & eFieldType::RSON_OBJECT) == 0) + return NULL; + + for (Field_t* pKey = GetFirstSubKey(); pKey != nullptr; pKey = pKey->GetNextKey()) + { + if (!_stricmp(pKey->m_pszName, pszKeyName)) + return pKey; + } + + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +inline RSON::Node_t* (*RSON_LoadFromBuffer)(const char* bufName, char* buf, RSON::eFieldType rootType, __int64 a4, void* a5); +inline void (*RSON_Free)(RSON::Node_t* rson, CAlignedMemAlloc* allocator); + +/////////////////////////////////////////////////////////////////////////////// +class VRSON : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("RSON_LoadFromBuffer", RSON_LoadFromBuffer); + LogFunAdr("RSON_Free", RSON_Free); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 89 45 60 48 8B D8").FollowNearCallSelf().GetPtr(RSON_LoadFromBuffer); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 83 EF 01 75 E7").FollowNearCallSelf().GetPtr(RSON_Free); + } + virtual void GetVar(void) const { } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const { } +}; +/////////////////////////////////////////////////////////////////////////////// + diff --git a/r5dev/public/rtech/rstdlib.h b/r5dev/public/rtech/rstdlib.h new file mode 100644 index 00000000..5de507fc --- /dev/null +++ b/r5dev/public/rtech/rstdlib.h @@ -0,0 +1,136 @@ +#pragma once + +class RHashMap; + +/* ==== RTECH =========================================================================================================================================================== */ +// [ PIXIE ]: I'm very unsure about this, but it really seems like it +inline int(*v_RHashMap_FindSlot)(RHashMap* const thisptr); +inline void(*v_RHashMap_FreeSlot)(RHashMap* const thisptr, const int slotNum); + + +class RHashMap +{ +public: + inline int FindSlot(void) + { + return v_RHashMap_FindSlot(this); + } + + inline void FreeSlot(const unsigned int slotNum) + { + v_RHashMap_FreeSlot(this, slotNum); + } + +private: + int m_index; + int m_slotsLeft; + int m_structSize; + int m_searchMask; + void* m_buffer; + int m_slotsUsed; + int padding_perhaps; +}; + +class RHashMap_MT +{ +public: + inline int FindSlot(void) + { + AcquireSRWLockExclusive(&m_lock); + const int slot = m_mgr.FindSlot(); + ReleaseSRWLockExclusive(&m_lock); + + return slot; + } + + inline void FreeSlot(const unsigned int slotNum) + { + AcquireSRWLockExclusive(&m_lock); + m_mgr.FreeSlot(slotNum); + ReleaseSRWLockExclusive(&m_lock); + } + +private: + RHashMap m_mgr; + SRWLOCK m_lock; +}; + +struct RMultiHashMap +{ + int globalIndex; + int firstBufSize; + void* firstBuffer; + void* secondBuffer; + int firstSlotsUsed; + int secondSlotsUsed; + int firstIndex; + int secondIndex; + int secondBufMask; +}; + +#pragma pack(push, 4) + +struct RBitRead +{ + unsigned __int64 m_dataBuf; + unsigned int m_bitsAvailable; + + RBitRead() : m_dataBuf(0), m_bitsAvailable(64) {}; + + FORCEINLINE void ConsumeData(unsigned __int64 input, unsigned int numBits = 64) + { + if (numBits > m_bitsAvailable) + { + assert(false && "RBitRead::ConsumeData: numBits must be less than or equal to m_bitsAvailable."); + return; + } + + m_dataBuf |= input << (64 - numBits); + } + + FORCEINLINE void ConsumeData(void* input, unsigned int numBits = 64) + { + if (numBits > m_bitsAvailable) + { + assert(false && "RBitRead::ConsumeData: numBits must be less than or equal to m_bitsAvailable."); + return; + } + + m_dataBuf |= *reinterpret_cast(input) << (64 - numBits); + } + + FORCEINLINE int BitsAvailable() const { return m_bitsAvailable; }; + + FORCEINLINE unsigned __int64 ReadBits(unsigned int numBits) + { + assert(numBits <= 64 && "RBitRead::ReadBits: numBits must be less than or equal to 64."); + return m_dataBuf & ((1ull << numBits) - 1); + } + + FORCEINLINE void DiscardBits(unsigned int numBits) + { + assert(numBits <= 64 && "RBitRead::DiscardBits: numBits must be less than or equal to 64."); + this->m_dataBuf >>= numBits; + this->m_bitsAvailable += numBits; + } +}; +#pragma pack(pop) + +/////////////////////////////////////////////////////////////////////////////// +class V_ReSTD : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("RHashMap::FindSlot", v_RHashMap_FindSlot); + LogFunAdr("RHashMap::FreeSlot", v_RHashMap_FreeSlot); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("44 8B 51 0C 4C 8B C1").GetPtr(v_RHashMap_FindSlot); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 44 8B 59 0C").GetPtr(v_RHashMap_FreeSlot); + } + virtual void GetVar(void) const { } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const { }; +}; +/////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/studio.h b/r5dev/public/studio.h index e75146d3..aef2a812 100644 --- a/r5dev/public/studio.h +++ b/r5dev/public/studio.h @@ -1,5 +1,6 @@ #ifndef STUDIO_H #define STUDIO_H +#include "tier1/refcount.h" #include "mathlib/mathlib.h" #define MAX_NUM_LODS 8 @@ -1066,14 +1067,17 @@ private: CMDLCache* m_pMDLCache; }; -class CStudioHWDataRef +template +class CStudioAssetEmbeddedRef : public CRefCounted<> +{ +protected: + T m_Embedded; +}; + +class CStudioHWDataRef : public CStudioAssetEmbeddedRef { public: - bool IsDataRef(void) const { return true; } - - CStudioHWDataRef* m_pVTable; - uint8_t pad0[0x8]; - studiohwdata_t m_HardwareData; + studiohwdata_t* GetHardwareData() { return &m_Embedded; } }; #endif // STUDIO_H diff --git a/r5dev/public/tier0/basetypes.h b/r5dev/public/tier0/basetypes.h index 0ab346ef..a918d71b 100644 --- a/r5dev/public/tier0/basetypes.h +++ b/r5dev/public/tier0/basetypes.h @@ -4,15 +4,6 @@ * _basetypes *-----------------------------------------------------------------------------*/ -// These are set from CMake now. -//#define GAMEDLL_S0 /*[r]*/ -//#define GAMEDLL_S1 /*[r]*/ -//#define GAMEDLL_S2 /*[i]*/ -//#define GAMEDLL_S3 /*[r]*/ -//#define GAMEDLL_S4 /*[i]*/ -//#define GAMEDLL_S5 /*[i]*/ -//#define GAMEDLL_S7 /*[i]*/ - //----------------------------------------------------------------------------- // Set up platform defines. //----------------------------------------------------------------------------- @@ -140,14 +131,10 @@ #define MAX_PLAYERS 128 // Absolute max R5 players. #define MAX_TEAMS 126 // Absolute max R5 teams. -#if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) && !defined (GAMEDLL_S2) -#define MAX_MAP_NAME_HOST 64 -#else -#define MAX_MAP_NAME_HOST 32 -#endif // Max BSP file name len. -#define MAX_MAP_NAME 64 +#define MAX_MAP_NAME_HOST 64 // Max host BSP file name len. +#define MAX_MAP_NAME 64 // Max BSP file name len. -#define SDK_VERSION "VGameSDK008" // Increment this with every /breaking/ SDK change (i.e. security/backend changes breaking compatibility). +#define SDK_VERSION "VGameSDK010" // Increment this with every /breaking/ SDK change (i.e. security/backend changes breaking compatibility). #define SDK_ARRAYSIZE(arr) ((sizeof(arr) / sizeof(*arr))) // Name due to IMGUI implementation and NT implementation that we shouldn't share across everywhere. #define SDK_SYSTEM_CFG_PATH "cfg/system/" diff --git a/r5dev/public/tier0/binstream.h b/r5dev/public/tier0/binstream.h index 6c01f660..f13a44cc 100644 --- a/r5dev/public/tier0/binstream.h +++ b/r5dev/public/tier0/binstream.h @@ -12,17 +12,18 @@ public: }; CIOStream(); - CIOStream(const fs::path& fsFileFullPath, int nFlags); ~CIOStream(); - bool Open(const fs::path& fsFileFullPath, int nFlags); + bool Open(const char* const pFilePath, const int nFlags); void Close(); void Flush(); - void ComputeFileSize(); + std::streampos TellGet(); + std::streampos TellPut(); - std::streampos GetPosition(Mode_t mode); - void SetPosition(std::streampos nOffset, Mode_t mode); + void SeekGet(const std::streampos nOffset); + void SeekPut(const std::streampos nOffset); + void Seek(const std::streampos nOffset); const std::filebuf* GetData() const; const std::streampos GetSize() const; @@ -46,7 +47,13 @@ public: // Purpose: reads any value from the file with specified size //----------------------------------------------------------------------------- template - void Read(T& tValue, size_t nSize) + void Read(T* tValue, const size_t nSize) + { + if (IsReadable()) + m_Stream.read(reinterpret_cast(tValue), nSize); + } + template + void Read(T& tValue, const size_t nSize) { if (IsReadable()) m_Stream.read(reinterpret_cast(&tValue), nSize); @@ -65,7 +72,8 @@ public: m_Stream.read(reinterpret_cast(&value), sizeof(value)); return value; } - bool ReadString(string& svOut); + bool ReadString(std::string& svOut); + bool ReadString(char* const pBuf, const size_t nLen); //----------------------------------------------------------------------------- // Purpose: writes any value to the file @@ -84,7 +92,7 @@ public: // Purpose: writes any value to the file with specified size //----------------------------------------------------------------------------- template - void Write(T tValue, size_t nSize) + void Write(T* tValue, size_t nSize) { if (!IsWritable()) return; @@ -92,10 +100,10 @@ public: m_Stream.write(reinterpret_cast(tValue), nSize); m_nSize += nSize; } - bool WriteString(const string& svInput); + bool WriteString(const std::string& svInput); private: std::streampos m_nSize; // File size. int m_nFlags; // Stream flags. - fstream m_Stream; // I/O stream. + std::fstream m_Stream; // I/O stream. }; diff --git a/r5dev/public/tier0/crashhandler.h b/r5dev/public/tier0/crashhandler.h index 744313e4..c07527bb 100644 --- a/r5dev/public/tier0/crashhandler.h +++ b/r5dev/public/tier0/crashhandler.h @@ -1,7 +1,7 @@ #ifndef CRASHHANDLER_H #define CRASHHANDLER_H +#include "tier1/fmtstr.h" -#define CRASHHANDLER_MAX_MODULES 4096 #define CRASHMESSAGE_MSG_EXECUTABLE "bin\\crashmsg.exe" //----------------------------------------------------------------------------- @@ -16,20 +16,18 @@ public: void Init(); void Shutdown(); + void Reset(); + //------------------------------------------------------------------------- // Inlines: //------------------------------------------------------------------------- void Start(); void End(); - inline void Lock() const { m_Mutex.lock(); }; - inline void Unlock() const { m_Mutex.unlock(); }; - - inline void SetState(bool bState) { m_bCallState = bState; }; - inline bool GetState() const { return m_bCallState; }; + inline void SetExit(const bool bExit) { m_bExit = bExit; }; + inline bool GetExit() const { return m_bExit; }; inline bool IsValid() const { return m_hExceptionHandler != nullptr; }; - inline bool Handling() const { return m_bExceptionHandling; }; //------------------------------------------------------------------------- // Formatters: @@ -44,17 +42,13 @@ public: //------------------------------------------------------------------------- // Utility: //------------------------------------------------------------------------- - const CHAR* ExceptionToString() const; - const CHAR* ExceptionToString(DWORD nExceptionCode) const; + const char* ExceptionToString() const; + const char* ExceptionToString(const DWORD nExceptionCode) const; - void SetExceptionPointers(EXCEPTION_POINTERS* pExceptionPointers) { m_pExceptionPointers = pExceptionPointers; } + void SetExceptionPointers(EXCEPTION_POINTERS* const pExceptionPointers) { m_pExceptionPointers = pExceptionPointers; } void SetCrashCallback(PVOID pCrashCallback) { m_pCrashCallback = pCrashCallback; } - void AddWhitelist(void* pWhitelist); - void RemoveWhitelist(void* pWhitelist); - bool HasWhitelist(); - - void GetCallStack(); + void CaptureCallStack(); void WriteFile(); void CreateMessageProcess(); @@ -66,39 +60,59 @@ private: // Internals: //------------------------------------------------------------------------- void FormatExceptionAddress(); - void FormatExceptionAddress(LPCSTR pExceptionAddress); + void FormatExceptionAddress(const LPCSTR pExceptionAddress); void FormatExceptionCode(); - void FormatALU(const CHAR* pszRegister, DWORD64 nContent); - void FormatFPU(const CHAR* pszRegister, M128A* pxContent); + void FormatALU(const char* const pszRegister, const DWORD64 nContent); + void FormatFPU(const char* const pszRegister, const M128A* const pxContent); bool IsPageAccessible() const; private: enum { - NUM_FRAMES_TO_SEARCH = 7, - NUM_FRAMES_TO_CAPTURE = 128 + NUM_FRAMES_TO_CAPTURE = 128, + MAX_MODULE_HANDLES = 4096 }; + // Captured callstack frames. PVOID m_ppStackTrace[NUM_FRAMES_TO_CAPTURE]; - PVOID m_pCrashCallback; - PVOID m_hExceptionHandler; - EXCEPTION_POINTERS* m_pExceptionPointers; WORD m_nCapturedFrames; - string m_svBuffer; // Buffer containing the entire crash log. - string m_svCrashMsgInfo; + // The loaded module handles that are being tracked. + HMODULE m_ppModuleHandles[MAX_MODULE_HANDLES]; + + // Custom crash callback that's called after the logs have been written. + PVOID m_pCrashCallback; + + // Current exception handler, only kept here for tracking as we need the + // handle if we want to remove this handler. + PVOID m_hExceptionHandler; + + EXCEPTION_POINTERS* m_pExceptionPointers; + + // 32KiB buffer containing the entire crash log, static as we shouldn't + // allocate any dynamic memory when writing the log files as that is + // unsafe during a crash. + CFmtStrQuietTruncationN<32768> m_Buffer; + + // Buffer containing the module name we crashed in. + CFmtStrQuietTruncationN<256> m_CrashingModule; + + // Buffer containing cmd line arguments for the external crash message + // process. + CFmtStrQuietTruncationN<256> m_MessageCmdLine; uint8_t m_nCrashMsgFlags; - bool m_bCallState; // Set when called to prevent recursive calls. - bool m_bCrashMsgCreated; // Set when crashmsg.exe is created to prevent recursive messages. - bool m_bExceptionHandling; // Set on filter entry, unset within the same lock if exception was not handled, never unset if handled. + // Set when called to prevent recursive calls. + bool m_bExit; - std::set m_WhiteList; - mutable std::mutex m_Mutex; + // Set when crashmsg.exe is created to prevent recursive messages. + bool m_bMessageCreated; + + mutable RTL_SRWLOCK m_Lock; }; -extern CCrashHandler* g_CrashHandler; +extern CCrashHandler g_CrashHandler; #endif // CRASHHANDLER_H \ No newline at end of file diff --git a/r5dev/public/tier0/dbg.h b/r5dev/public/tier0/dbg.h index bc60a332..14e8fe50 100644 --- a/r5dev/public/tier0/dbg.h +++ b/r5dev/public/tier0/dbg.h @@ -15,7 +15,6 @@ // Used for the 'Error' function, this tells the function to only log, not quit. //#define NO_ERROR 0 -bool HushAsserts(); //----------------------------------------------------------------------------- enum class eDLL_T : int { @@ -108,11 +107,22 @@ void CoreMsg(LogType_t logType, LogLevel_t logLevel, eDLL_T context, const UINT exitCode, const char* pszLogger, const char* pszFormat, ...); // These functions do not return. -PLATFORM_INTERFACE void DevMsg(eDLL_T context, const char* fmt, ...) FMTFUNCTION(2, 3); +PLATFORM_INTERFACE void Msg(eDLL_T context, const char* fmt, ...) FMTFUNCTION(2, 3); PLATFORM_INTERFACE void NetMsg(LogType_t logType, eDLL_T context, const char* uptime, const char* fmt, ...) FMTFUNCTION(4, 5); PLATFORM_INTERFACE void Warning(eDLL_T context, const char* fmt, ...) FMTFUNCTION(2, 3); PLATFORM_INTERFACE void Error(eDLL_T context, const UINT code, const char* fmt, ...) FMTFUNCTION(3, 4); +// TODO[ AMOS ]: export to DLL? +void Plat_FatalError(eDLL_T context, const char* fmt, ...); + +#if defined DBGFLAG_STRINGS_STRIP +#define DevMsg( ... ) ((void)0) +#define DevWarning( ... ) ((void)0) +#else // DBGFLAG_STRINGS_STRIP +PLATFORM_INTERFACE void DevMsg(eDLL_T context, const char* fmt, ...) FMTFUNCTION(2, 3); +PLATFORM_INTERFACE void DevWarning(eDLL_T context, const char* fmt, ...) FMTFUNCTION(2, 3); +#endif + // You can use this macro like a runtime assert macro. // If the condition fails, then Error is called with the message. This macro is called // like AssertMsg, where msg must be enclosed in parenthesis: @@ -151,6 +161,38 @@ template inline void AssertValidReadWritePtr(T* /*ptr*/, int count = 1) #define AssertValidThis() #endif +//----------------------------------------------------------------------------- +// Macro to protect functions that are not reentrant + +#ifdef _DEBUG +class CReentryGuard +{ +public: + CReentryGuard(int* pSemaphore) + : m_pSemaphore(pSemaphore) + { + ++(*m_pSemaphore); + } + + ~CReentryGuard() + { + --(*m_pSemaphore); + } + +private: + int* m_pSemaphore; +}; + +#define ASSERT_NO_REENTRY() \ + static int fSemaphore##__LINE__; \ + Assert( !fSemaphore##__LINE__ ); \ + CReentryGuard ReentryGuard##__LINE__( &fSemaphore##__LINE__ ) +#else +#define ASSERT_NO_REENTRY() +#endif + +#define AssertMsg(condition, ...) assert(condition) + typedef void (*CoreMsgVCallbackSink_t)(LogType_t logType, LogLevel_t logLevel, eDLL_T context, const char* pszLogger, const char* pszFormat, va_list args, const UINT exitCode, const char* pszUptimeOverride); diff --git a/r5dev/public/tier0/frametask.h b/r5dev/public/tier0/frametask.h index 126e76df..01e7c2f9 100644 --- a/r5dev/public/tier0/frametask.h +++ b/r5dev/public/tier0/frametask.h @@ -8,7 +8,9 @@ // Committed tasks are scheduled to execute after 'i' frames. // ---------------------------------------------------------------------------- // A use case for scheduling tasks in the main thread would be (for example) -// calling 'KeyValues::ParsePlaylists(...)' from the render thread. +// performing a web request in a separate thread, and apply the results (such as +// server lists in the browser) onto the imgui panels which are created/drawn in +// the main thread //=============================================================================// class CFrameTask : public IFrameTask { @@ -17,14 +19,14 @@ public: virtual void RunFrame(); virtual bool IsFinished() const; - void Dispatch(std::function functor, int frames); + void Dispatch(std::function functor, unsigned int frames); private: mutable std::mutex m_Mutex; - std::list m_ScheduledTasks; + std::list m_QueuedTasks; }; -extern std::list g_FrameTasks; -extern CFrameTask* g_TaskScheduler; +extern std::list g_TaskQueueList; +extern CFrameTask g_TaskQueue; #endif // TIER0_FRAMETASK_H diff --git a/r5dev/public/tier0/imemalloc.h b/r5dev/public/tier0/imemalloc.h index a33b72e9..5caa290c 100644 --- a/r5dev/public/tier0/imemalloc.h +++ b/r5dev/public/tier0/imemalloc.h @@ -10,6 +10,15 @@ #ifndef TIER0_IMEMALLOC_H #define TIER0_IMEMALLOC_H +inline void* MemAlloc_Alloc(size_t nSize, const char* pFileName = NULL, int nLine = 0) { return malloc(nSize); } +inline void MemAlloc_Free(void* ptr, const char* pFileName = NULL, int nLine = 0) { free(ptr); } + +inline void* MemAlloc_AllocAligned(size_t size, size_t align) { return _aligned_malloc(size, align); } +inline void* MemAlloc_AllocAlignedFileLine(size_t size, size_t align, const char* pszFile = NULL, int nLine = 0) { return _aligned_malloc(size, align); } +inline void MemAlloc_FreeAligned(void* pMemBlock, const char* pszFile = NULL, int nLine = 0) { free(pMemBlock); } + +//----------------------------------------------------------------------------- + /// This interface class is used to let the mem_dump command retrieve /// information about memory allocations outside of the heap. It is currently /// used by CMemoryStack to report on its allocations. diff --git a/r5dev/public/tier0/jobthread.h b/r5dev/public/tier0/jobthread.h index faa1b204..8ddb6c4a 100644 --- a/r5dev/public/tier0/jobthread.h +++ b/r5dev/public/tier0/jobthread.h @@ -1,57 +1,133 @@ #ifndef JOBTHREAD_H #define JOBTHREAD_H +typedef uint32_t JobID_t; +typedef uint8_t JobTypeID_t; + +typedef bool(*JobHelpCallback_t)(__int64, _DWORD*, __int64, _QWORD*); + struct JobFifoLock_s { + int id; + int depth; + short tls[64]; }; -typedef uint32_t JobID_t; +struct JobContext_s +{ + void* callbackArg; // Argument to job callback function. + void* callbackFunc; // Job callback function. + JobTypeID_t jobTypeId; + bool field_11; + __int16 field_12; + int field_14; + JobID_t jobId; + int field_1C; + int field_20; + int field_24; // Bit fields? + __int64 field_28; + __int64 unknownMask; + __int64 unknownInt; +}; + +typedef struct JobUserData_s +{ + JobUserData_s(int32_t si) + { + data.sint = si; + } + JobUserData_s(uint32_t ui) + { + data.uint = ui; + } + JobUserData_s(int64_t si) + { + data.sint = si; + } + JobUserData_s(uint64_t ui) + { + data.uint = ui; + } + JobUserData_s(double sa) + { + data.scal = sa; + } + JobUserData_s(void* pt) + { + data.ptr = pt; + } + + union + { + int64_t sint; + uint64_t uint; + double scal; + void* ptr; + } data; +} JobUserData_t; + +// Array size = 2048*sizeof(JobContext_s) +inline JobContext_s* job_JT_Context = nullptr; + +extern bool JT_IsJobDone(const JobID_t jobId); +extern JobID_t JTGuts_AddJob(JobTypeID_t jobTypeId, JobID_t jobId, void* callbackFunc, void* callbackArg); +extern JobID_t JT_GetCurrentJob(); + -inline CMemory p_JT_ParallelCall; inline void(*JT_ParallelCall)(void); - -inline CMemory p_JT_HelpWithAnything; inline void*(*JT_HelpWithAnything)(bool bShouldLoadPak); -inline CMemory p_JT_AcquireFifoLock; -inline bool(*JT_AcquireFifoLock)(struct JobFifoLock_s* pFifo); +inline bool(*JT_HelpWithJobTypes)(JobHelpCallback_t, JobUserData_t userData, __int64 a3, __int64 a4); +inline __int64(*JT_HelpWithJobTypesOrSleep)(JobHelpCallback_t, JobUserData_t userData, __int64 a3, __int64 a4, volatile signed __int64* a5, char a6); +inline __int64(*JT_WaitForJobAndOnlyHelpWithJobTypes)(JobID_t, uint64_t unkMask1, uint64_t unkMask2); -inline CMemory p_JT_ReleaseFifoLock; +inline bool(*JT_AcquireFifoLockOrHelp)(struct JobFifoLock_s* pFifo); inline void(*JT_ReleaseFifoLock)(struct JobFifoLock_s* pFifo); -void JT_Attach(); -void JT_Detach(); +inline void(*JT_EndJobGroup)(const JobID_t jobId); + +inline unsigned int (*JT_AllocateJob)(); // Returns an index to the 'job_JT_Context' array +inline JobID_t(*JTGuts_AddJob_Internal)(JobTypeID_t jobTypeId, JobID_t jobId, void* callbackfunc, void* callbackArg, int jobIndex, JobContext_s* context); + /////////////////////////////////////////////////////////////////////////////// class VJobThread : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("JT_ParallelCall", p_JT_ParallelCall.GetPtr()); - LogFunAdr("JT_HelpWithAnything", p_JT_HelpWithAnything.GetPtr()); - LogFunAdr("JT_AcquireFifoLock", p_JT_AcquireFifoLock.GetPtr()); - LogFunAdr("JT_ReleaseFifoLock", p_JT_ReleaseFifoLock.GetPtr()); + LogFunAdr("JT_ParallelCall", JT_ParallelCall); + + LogFunAdr("JT_HelpWithAnything", JT_HelpWithAnything); + LogFunAdr("JT_HelpWithJobTypes", JT_HelpWithJobTypes); + LogFunAdr("JT_HelpWithJobTypesOrSleep", JT_HelpWithJobTypesOrSleep); + LogFunAdr("JT_WaitForJobAndOnlyHelpWithJobTypes", JT_WaitForJobAndOnlyHelpWithJobTypes); + + LogFunAdr("JT_AcquireFifoLockOrHelp", JT_AcquireFifoLockOrHelp); + LogFunAdr("JT_ReleaseFifoLock", JT_ReleaseFifoLock); + + LogFunAdr("JT_EndJobGroup", JT_EndJobGroup); + LogFunAdr("JT_AllocateJob", JT_AllocateJob); + LogFunAdr("JTGuts_AddJob_Internal", JTGuts_AddJob_Internal); + LogVarAdr("job_JT_Context", job_JT_Context); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_JT_ParallelCall = g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 10 48 89 70 18 55 57 41 57"); - p_JT_HelpWithAnything = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 30 80 3D ?? ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_JT_ParallelCall = g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 08 48 89 78 10 55 48 8D 68 A1 48 81 EC ?? ?? ?? ?? 0F 29 70 E8 48 8D 1D ?? ?? ?? ??"); - p_JT_HelpWithAnything = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 80 3D ?? ?? ?? ?? ??"); -#endif - p_JT_AcquireFifoLock = g_GameDll.FindPatternSIMD("48 83 EC 08 65 48 8B 04 25 ?? ?? ?? ?? 4C 8B C1"); - p_JT_ReleaseFifoLock = g_GameDll.FindPatternSIMD("48 83 EC 28 44 8B 11"); - - JT_ParallelCall = p_JT_ParallelCall.RCast(); /*48 8B C4 48 89 58 08 48 89 78 10 55 48 8D 68 A1 48 81 EC ?? ?? ?? ?? 0F 29 70 E8 48 8D 1D ?? ?? ?? ??*/ - JT_HelpWithAnything = p_JT_HelpWithAnything.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 80 3D ?? ?? ?? ?? ??*/ - JT_AcquireFifoLock = p_JT_AcquireFifoLock.RCast(); /*48 83 EC 08 65 48 8B 04 25 ?? ?? ?? ?? 4C 8B C1*/ - JT_ReleaseFifoLock = p_JT_ReleaseFifoLock.RCast(); /*48 83 EC 28 44 8B 11*/ + g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 08 48 89 78 10 55 48 8D 68 A1 48 81 EC ?? ?? ?? ?? 0F 29 70 E8 48 8D 1D ?? ?? ?? ??").GetPtr(JT_ParallelCall); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 80 3D ?? ?? ?? ?? ??").GetPtr(JT_HelpWithAnything); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 4C 89 4C 24 ?? 4C 89 44 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 83 EC 60").GetPtr(JT_HelpWithJobTypes); + g_GameDll.FindPatternSIMD("4C 89 4C 24 ?? 4C 89 44 24 ?? 48 89 54 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ??").GetPtr(JT_HelpWithJobTypesOrSleep); + g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 30 8B F9").GetPtr(JT_WaitForJobAndOnlyHelpWithJobTypes); + g_GameDll.FindPatternSIMD("48 83 EC 08 65 48 8B 04 25 ?? ?? ?? ?? 4C 8B C1").GetPtr(JT_AcquireFifoLockOrHelp); + g_GameDll.FindPatternSIMD("48 83 EC 28 44 8B 11").GetPtr(JT_ReleaseFifoLock); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 30 65 48 8B 04 25 ?? ?? ?? ?? BA ?? ?? ?? ??").GetPtr(JT_AllocateJob); + g_GameDll.FindPatternSIMD("8B D1 48 8D 05 ?? ?? ?? ?? 81 E2 ?? ?? ?? ?? 48 C1 E2 06 48 03 D0 E9 ?? ?? ?? ??").GetPtr(JT_EndJobGroup); + g_GameDll.FindPatternSIMD("48 89 74 24 ? 57 48 83 EC 20 0F B6 F1").GetPtr(JTGuts_AddJob_Internal); + } + virtual void GetVar(void) const + { + CMemory(JT_EndJobGroup).FindPattern("48 8D").ResolveRelativeAddressSelf(3, 7).GetPtr(job_JT_Context); } - virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier0/memaddr.h b/r5dev/public/tier0/memaddr.h index 6223fe73..97ffa46b 100644 --- a/r5dev/public/tier0/memaddr.h +++ b/r5dev/public/tier0/memaddr.h @@ -48,6 +48,12 @@ public: return ptr; } + template + inline void GetPtr(T*& outPtr) const + { + outPtr = reinterpret_cast(ptr); + } + template inline T GetValue(void) const { return *reinterpret_cast(ptr); diff --git a/r5dev/public/tier0/module.h b/r5dev/public/tier0/module.h index ac5e1d0d..299b3662 100644 --- a/r5dev/public/tier0/module.h +++ b/r5dev/public/tier0/module.h @@ -64,6 +64,9 @@ public: inline static PEB64* GetProcessEnvironmentBlock() { return reinterpret_cast(__readgsqword(0x60)); } + inline static TEB64* GetThreadEnvironmentBlock() + { return reinterpret_cast(NtCurrentTeb()); } + void UnlinkFromPEB(void) const; private: diff --git a/r5dev/public/tier0/platform.h b/r5dev/public/tier0/platform.h index 15a8ac37..31bb5279 100644 --- a/r5dev/public/tier0/platform.h +++ b/r5dev/public/tier0/platform.h @@ -406,6 +406,32 @@ uint64_t Plat_MSTime(); const char* Plat_GetProcessUpTime(); void Plat_GetProcessUpTime(char* szBuf, size_t nSize); +inline bool Plat_IsInDebugSession() +{ +#if defined( _X360 ) + return (XBX_IsDebuggerPresent() != 0); +#elif defined( _WIN32 ) + return (IsDebuggerPresent() != 0); +#elif defined( _PS3 ) && !defined(_CERT) + return snIsDebuggerPresent(); +#else + return false; +#endif +} + +inline void Plat_DebugString(const char* psz) +{ +#if defined( _X360 ) + XBX_OutputDebugString(psz); +#elif defined( _WIN32 ) + ::OutputDebugStringA(psz); +#elif defined(_PS3) + printf("%s", psz); +#else + // do nothing? +#endif +} + #if defined( _X360 ) #define Plat_FastMemset XMemSet #define Plat_FastMemcpy XMemCpy diff --git a/r5dev/public/tier0/platform_internal.h b/r5dev/public/tier0/platform_internal.h index 88c62a87..409c4a2a 100644 --- a/r5dev/public/tier0/platform_internal.h +++ b/r5dev/public/tier0/platform_internal.h @@ -1,37 +1,47 @@ #ifndef PLATFORM_INTERNAL_H #define PLATFORM_INTERNAL_H -inline CMemory p_Plat_FloatTime; +inline void(*v_InitTime)(void); inline double(*v_Plat_FloatTime)(void); - -inline CMemory p_Plat_MSTime; inline uint64_t(*v_Plat_MSTime)(void); +inline bool* s_pbTimeInitted = nullptr; +inline double* g_pPerformanceCounterToMS = nullptr; +inline LARGE_INTEGER* g_pPerformanceFrequency = nullptr; +inline LARGE_INTEGER* g_pClockStart = nullptr; + inline double* g_flErrorTimeStamp = nullptr; /////////////////////////////////////////////////////////////////////////////// class VPlatform : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Plat_FloatTime", p_Plat_FloatTime.GetPtr()); - LogFunAdr("Plat_MSTime", p_Plat_MSTime.GetPtr()); - LogVarAdr("g_flErrorTimeStamp", reinterpret_cast(g_flErrorTimeStamp)); + LogFunAdr("InitTime", v_InitTime); + LogFunAdr("Plat_FloatTime", v_Plat_FloatTime); + LogFunAdr("Plat_MSTime", v_Plat_MSTime); + LogVarAdr("s_bTimeInitted", s_pbTimeInitted); + LogVarAdr("g_PerformanceCounterToMS", g_pPerformanceCounterToMS); + LogVarAdr("g_PerformanceFrequency", g_pPerformanceFrequency); + LogVarAdr("g_ClockStart", g_pClockStart); + LogVarAdr("g_flErrorTimeStamp", g_flErrorTimeStamp); } virtual void GetFun(void) const { - p_Plat_FloatTime = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 75 05 E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 1D"); - p_Plat_MSTime = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 75 05 E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 2A"); - - v_Plat_FloatTime = p_Plat_FloatTime.RCast(); /*48 83 EC 28 80 3D ? ? ? ? ? 75 05 E8 ? ? ? ? 80 3D ? ? ? ? ? 74 1D*/ - v_Plat_MSTime = p_Plat_MSTime.RCast(); /*48 83 EC 28 80 3D ? ? ? ? ? 75 05 E8 ? ? ? ? 80 3D ? ? ? ? ? 74 2A*/ + g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 75 4C").GetPtr(v_InitTime); + g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 75 05 E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 1D").GetPtr(v_Plat_FloatTime); + g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 75 05 E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 2A").GetPtr(v_Plat_MSTime); } virtual void GetVar(void) const { + s_pbTimeInitted = CMemory(v_InitTime).FindPattern("80 3D").ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + g_pPerformanceCounterToMS = CMemory(v_InitTime).FindPattern("48 89").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pPerformanceFrequency = CMemory(v_InitTime).FindPattern("48 F7").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pClockStart = CMemory(v_InitTime).FindPattern("48 8D", CMemory::Direction::DOWN, 512, 2).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_flErrorTimeStamp = g_GameDll.FindPatternSIMD("0F 57 C0 F2 0F 11 05 ?? ?? ?? ?? C3").FindPatternSelf("F2 0F").ResolveRelativeAddressSelf(0x4, 0x8).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier0/platwindow.h b/r5dev/public/tier0/platwindow.h new file mode 100644 index 00000000..8a93ca03 --- /dev/null +++ b/r5dev/public/tier0/platwindow.h @@ -0,0 +1,82 @@ +//===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +// +//===========================================================================// + +#ifndef PLATWINDOW_H +#define PLATWINDOW_H + +#ifdef COMPILER_MSVC32 +#pragma once +#endif + +#include "tier0/platform.h" +#include "tier0/basetypes.h" + +//----------------------------------------------------------------------------- +// Window handle +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( PlatWindow_t ); +#define PLAT_WINDOW_INVALID ( (PlatWindow_t)0 ) + + +//----------------------------------------------------------------------------- +// Window creation +//----------------------------------------------------------------------------- +enum WindowCreateFlags_t +{ + WINDOW_CREATE_FULLSCREEN = 0x1, + WINDOW_CREATE_RESIZING = 0x2, + +}; + +PLATFORM_INTERFACE PlatWindow_t Plat_CreateWindow( void *hInstance, const char *pTitle, int nWidth, int nHeight, int nFlags ); + + +//----------------------------------------------------------------------------- +// Window title +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_SetWindowTitle( PlatWindow_t hWindow, const char *pTitle ); + + +//----------------------------------------------------------------------------- +// Window movement +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_SetWindowPos( PlatWindow_t hWindow, int x, int y ); + + +//----------------------------------------------------------------------------- +// Gets the desktop resolution +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_GetDesktopResolution( int *pWidth, int *pHeight ); + + +//----------------------------------------------------------------------------- +// Gets a window size +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_GetWindowClientSize( PlatWindow_t hWindow, int *pWidth, int *pHeight ); + + +//----------------------------------------------------------------------------- +// Is the window minimized? +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE bool Plat_IsWindowMinimized( PlatWindow_t hWindow ); + + +//----------------------------------------------------------------------------- +// Gets the shell window in a console app +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE PlatWindow_t Plat_GetShellWindow( ); + + +//----------------------------------------------------------------------------- +// Convert window -> Screen coordinates and back +//----------------------------------------------------------------------------- +PLATFORM_INTERFACE void Plat_WindowToScreenCoords( PlatWindow_t hWnd, int &x, int &y ); +PLATFORM_INTERFACE void Plat_ScreenToWindowCoords( PlatWindow_t hWnd, int &x, int &y ); + + +#endif // PLATWINDOW_H diff --git a/r5dev/public/tier0/sigcache.h b/r5dev/public/tier0/sigcache.h index 91431c4a..67ff711a 100644 --- a/r5dev/public/tier0/sigcache.h +++ b/r5dev/public/tier0/sigcache.h @@ -7,7 +7,7 @@ #define SIGDB_DICT_SIZE 20 #define SIGDB_MAJOR_VERSION 0x2 // Increment when library changes are made. -#define SIGDB_MINOR_VERSION 0x6 // Increment when SDK updates are released. +#define SIGDB_MINOR_VERSION 0xA // Increment when SDK updates are released. class CSigCache { @@ -23,7 +23,7 @@ public: void AddEntry(const char* szPattern, const uint64_t nRVA); bool FindEntry(const char* szPattern, uint64_t& nRVA); - bool LoadCache(const char* szCacheFile); + bool ReadCache(const char* szCacheFile); bool WriteCache(const char* szCacheFile) const; private: diff --git a/r5dev/public/tier0/threadtools.h b/r5dev/public/tier0/threadtools.h index 9bb258bf..e47e2379 100644 --- a/r5dev/public/tier0/threadtools.h +++ b/r5dev/public/tier0/threadtools.h @@ -1,5 +1,9 @@ #ifndef THREADTOOLS_H #define THREADTOOLS_H +#include "dbg.h" +#ifndef BUILDING_MATHLIB +#include "jobthread.h" +#endif // BUILDING_MATHLIB inline void ThreadSleep(unsigned nMilliseconds) { @@ -60,6 +64,14 @@ inline void ThreadPause() // //----------------------------------------------------------------------------- +FORCEINLINE int32 ThreadInterlockedIncrement(int32 volatile* p) { Assert((size_t)p % 4 == 0); return _InterlockedIncrement((volatile long*)p); } +FORCEINLINE int32 ThreadInterlockedDecrement(int32 volatile* p) { Assert((size_t)p % 4 == 0); return _InterlockedDecrement((volatile long*)p); } + +FORCEINLINE int64 ThreadInterlockedIncrement64(int64 volatile* p) { Assert((size_t)p % 8 == 0); return _InterlockedIncrement64((volatile int64*)p); } +FORCEINLINE int64 ThreadInterlockedDecrement64(int64 volatile* p) { Assert((size_t)p % 8 == 0); return _InterlockedDecrement64((volatile int64*)p); } + +FORCEINLINE int32 ThreadInterlockedExchangeAdd(int32 volatile* p, int32 value) { Assert((size_t)p % 4 == 0); return _InterlockedExchangeAdd((volatile long*)p, value); } + FORCEINLINE int32 ThreadInterlockedCompareExchange(LONG volatile* pDest, int32 value, int32 comperand) { return _InterlockedCompareExchange(pDest, comperand, value); @@ -86,12 +98,6 @@ FORCEINLINE bool ThreadInterlockedAssignIf64(int64 volatile* pDest, int64 value, // Thread checking methods. // //----------------------------------------------------------------------------- -#ifndef BUILDING_MATHLIB - -inline ThreadId_t* g_ThreadMainThreadID = nullptr; -inline ThreadId_t g_ThreadRenderThreadID = NULL; -inline ThreadId_t* g_ThreadServerFrameThreadID = nullptr; - FORCEINLINE ThreadId_t ThreadGetCurrentId() { #ifdef _WIN32 @@ -109,20 +115,18 @@ FORCEINLINE ThreadId_t ThreadGetCurrentId() #endif } -FORCEINLINE bool ThreadInMainThread() -{ - return (ThreadGetCurrentId() == (*g_ThreadMainThreadID)); -} +#ifndef BUILDING_MATHLIB -FORCEINLINE bool ThreadInRenderThread() -{ - return (ThreadGetCurrentId() == g_ThreadRenderThreadID); -} +extern ThreadId_t* g_ThreadMainThreadID; +extern ThreadId_t* g_ThreadServerFrameThreadID; +inline JobID_t* g_CurrentServerFrameJobID; +inline JobID_t* g_AllocatedServerFrameJobID; -FORCEINLINE bool ThreadInServerFrameThread() -{ - return (ThreadGetCurrentId() == (*g_ThreadServerFrameThreadID)); -} +PLATFORM_INTERFACE bool ThreadInMainThread(); +PLATFORM_INTERFACE bool ThreadInServerFrameThread(); +PLATFORM_INTERFACE bool ThreadInMainOrServerFrameThread(); +PLATFORM_INTERFACE bool ThreadCouldDoServerWork(); +PLATFORM_INTERFACE void ThreadJoinServerJob(); #endif // !BUILDING_MATHLIB @@ -260,8 +264,7 @@ typedef CInterlockedIntT CInterlockedUInt; #ifndef BUILDING_MATHLIB //============================================================================= -inline CMemory p_DeclareCurrentThreadIsMainThread; -inline ThreadId_t(*v_DeclareCurrentThreadIsMainThread)(void); + #endif // !BUILDING_MATHLIB @@ -269,6 +272,14 @@ inline ThreadId_t(*v_DeclareCurrentThreadIsMainThread)(void); class CThreadFastMutex { public: + CThreadFastMutex() + : m_nOwnerID(NULL) + , m_nDepth(NULL) + , m_lAddend(NULL) + , m_hSemaphore(NULL) + { + } + int Lock(void); int Unlock(void); @@ -316,7 +327,7 @@ private: }; /////////////////////////////////////////////////////////////////////////////// -template struct CAutoLockTypeDeducer {}; +template struct CAutoLockTypeDeducer {}; template <> struct CAutoLockTypeDeducer { typedef CThreadFastMutex Type_t; }; #define AUTO_LOCK_( type, mutex ) \ @@ -330,29 +341,244 @@ template <> struct CAutoLockTypeDeducer { typedef CThr AUTO_LOCK_(CAutoLockTypeDeducer::Type_t, mutex) #endif +//----------------------------------------------------------------------------- +// +// CThreadSpinRWLock inline functions +// +//----------------------------------------------------------------------------- + +class ALIGN8 PLATFORM_CLASS CThreadSpinRWLock +{ +public: + CThreadSpinRWLock() + { + m_lockInfo.m_i32 = 0; + m_writerId = 0; +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + m_iWriteDepth = 0; +#endif + } + + bool IsLockedForWrite(); + bool IsLockedForRead(); + + FORCEINLINE bool TryLockForWrite(); + bool TryLockForWrite_UnforcedInline(); + + void LockForWrite(); + void SpinLockForWrite(); + + FORCEINLINE bool TryLockForRead(); + bool TryLockForRead_UnforcedInline(); + + void LockForRead(); + void SpinLockForRead(); + + void UnlockWrite(); + void UnlockRead(); + + bool TryLockForWrite() const { return const_cast(this)->TryLockForWrite(); } + bool TryLockForRead() const { return const_cast(this)->TryLockForRead(); } + void LockForRead() const { const_cast(this)->LockForRead(); } + void UnlockRead() const { const_cast(this)->UnlockRead(); } + void LockForWrite() const { const_cast(this)->LockForWrite(); } + void UnlockWrite() const { const_cast(this)->UnlockWrite(); } + +private: + enum + { + THREAD_SPIN = (8 * 1024) + }; + +#pragma warning(push) +#pragma warning(disable : 4201) + union LockInfo_t + { + struct + { +#if PLAT_LITTLE_ENDIAN + uint16 m_nReaders; + uint16 m_fWriting; +#else + uint16 m_fWriting; + uint16 m_nReaders; +#endif + }; + uint32 m_i32; + }; +#pragma warning(pop) + + LockInfo_t m_lockInfo; + ThreadId_t m_writerId; +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + int m_iWriteDepth; + uint32 pad; +#endif +} ALIGN8_POST; + +inline bool CThreadSpinRWLock::IsLockedForWrite() +{ + return (m_lockInfo.m_fWriting == 1); +} + +inline bool CThreadSpinRWLock::IsLockedForRead() +{ + return (m_lockInfo.m_nReaders > 0); +} + +FORCEINLINE bool CThreadSpinRWLock::TryLockForWrite() +{ + volatile LockInfo_t& curValue = m_lockInfo; + if (!(curValue.m_i32 & 0x00010000) && ThreadInterlockedAssignIf((LONG*)&curValue.m_i32, 0x00010000, 0)) + { + ThreadMemoryBarrier(); + Assert(m_writerId == 0); + m_writerId = ThreadGetCurrentId(); +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + Assert(m_iWriteDepth == 0); + m_iWriteDepth++; +#endif + return true; + } + + return false; +} + +inline bool CThreadSpinRWLock::TryLockForWrite_UnforcedInline() +{ + if (TryLockForWrite()) + { + return true; + } + +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if (m_writerId != ThreadGetCurrentId()) + { + return false; + } + m_iWriteDepth++; + return true; +#else + return false; +#endif +} + +FORCEINLINE void CThreadSpinRWLock::LockForWrite() +{ + if (!TryLockForWrite()) + { + SpinLockForWrite(); + } +} + +FORCEINLINE bool CThreadSpinRWLock::TryLockForRead() +{ + volatile LockInfo_t& curValue = m_lockInfo; + if (!(curValue.m_i32 & 0x00010000)) // !m_lockInfo.m_fWriting + { + LockInfo_t oldValue; + LockInfo_t newValue; + oldValue.m_i32 = (curValue.m_i32 & 0xffff); + newValue.m_i32 = oldValue.m_i32 + 1; + + if (ThreadInterlockedAssignIf((LONG*)&m_lockInfo.m_i32, newValue.m_i32, oldValue.m_i32)) + { + ThreadMemoryBarrier(); + Assert(m_lockInfo.m_fWriting == 0); + return true; + } + } + return false; +} + +inline bool CThreadSpinRWLock::TryLockForRead_UnforcedInline() +{ +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if (m_lockInfo.m_i32 & 0x00010000) // m_lockInfo.m_fWriting + { + if (m_writerId == ThreadGetCurrentId()) + { + m_lockInfo.m_nReaders++; + return true; + } + + return false; + } +#endif + return TryLockForRead(); +} + +FORCEINLINE void CThreadSpinRWLock::LockForRead() +{ + if (!TryLockForRead()) + { + SpinLockForRead(); + } +} + +FORCEINLINE void CThreadSpinRWLock::UnlockWrite() +{ + Assert(m_writerId == ThreadGetCurrentId()); +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if (--m_iWriteDepth == 0) +#endif + { + m_writerId = 0; + ThreadMemoryBarrier(); + m_lockInfo.m_i32 = 0; + } +} + +#ifndef REENTRANT_THREAD_SPIN_RW_LOCK +FORCEINLINE +#else +inline +#endif +void CThreadSpinRWLock::UnlockRead() +{ + Assert(m_writerId == 0 || (m_writerId == ThreadGetCurrentId() && m_lockInfo.m_fWriting)); +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if (!(m_lockInfo.m_i32 & 0x00010000)) // !m_lockInfo.m_fWriting +#endif + { + ThreadMemoryBarrier(); + ThreadInterlockedDecrement((int32*)&m_lockInfo.m_i32); + Assert(m_writerId == 0 && !m_lockInfo.m_fWriting); + } +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + else if (m_writerId == ThreadGetCurrentId()) + { + m_lockInfo.m_nReaders--; + } + else + { + RWLAssert(0); + } +#endif +} + #ifndef BUILDING_MATHLIB /////////////////////////////////////////////////////////////////////////////// class VThreadTools : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("DeclareCurrentThreadIsMainThread", p_DeclareCurrentThreadIsMainThread.GetPtr()); - LogVarAdr("g_ThreadMainThreadID", reinterpret_cast(g_ThreadMainThreadID)); - LogVarAdr("g_ThreadServerFrameThreadID", reinterpret_cast(g_ThreadServerFrameThreadID)); - } - virtual void GetFun(void) const - { - p_DeclareCurrentThreadIsMainThread = g_GameDll.FindPatternSIMD("48 83 EC 28 FF 15 ?? ?? ?? ?? 89 05 ?? ?? ?? ?? 48 83 C4 28"); - v_DeclareCurrentThreadIsMainThread = p_DeclareCurrentThreadIsMainThread.RCast(); /*48 83 EC 28 FF 15 ?? ?? ?? ?? 89 05 ?? ?? ?? ?? 48 83 C4 28 */ + LogVarAdr("g_ThreadMainThreadID", g_ThreadMainThreadID); + LogVarAdr("g_ThreadServerFrameThreadID", g_ThreadServerFrameThreadID); + LogVarAdr("g_CurrentServerFrameJobID", g_CurrentServerFrameJobID); + LogVarAdr("g_AllocatedServerFrameJobID", g_AllocatedServerFrameJobID); } + virtual void GetFun(void) const { } virtual void GetVar(void) const { - g_ThreadMainThreadID = p_DeclareCurrentThreadIsMainThread.FindPattern("89 05").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); - g_ThreadServerFrameThreadID = g_GameDll.FindPatternSIMD("83 79 ?? ?? 75 28 8B").FindPatternSelf("8B 05").ResolveRelativeAddressSelf(0x2, 0x6).RCast(); + g_GameDll.FindPatternSIMD("66 89 54 24 ?? 53 55 56 57 41 54 48 81 EC ?? ?? ?? ??") + .FindPatternSelf("39 05").ResolveRelativeAddressSelf(2, 6).GetPtr(g_CurrentServerFrameJobID); + + g_GameDll.FindPatternSIMD("48 83 EC 28 FF 15 ?? ?? ?? ?? 8B 0D ?? ?? ?? ??") + .FindPatternSelf("8B 0D").ResolveRelativeAddressSelf(2, 6).GetPtr(g_AllocatedServerFrameJobID); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool /*bAttach*/) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier0/tier0_iface.h b/r5dev/public/tier0/tier0_iface.h index 0884b8a9..0165d864 100644 --- a/r5dev/public/tier0/tier0_iface.h +++ b/r5dev/public/tier0/tier0_iface.h @@ -32,8 +32,8 @@ ReturnType CallVFunc(int index, void* thisPtr, Args... args) return (*reinterpret_cast(thisPtr))[index](thisPtr, args...); } -void LogFunAdr(const char* szFun, uintptr_t nAdr); // Logging function addresses. -void LogVarAdr(const char* szVar, uintptr_t nAdr); // Logging variable addresses. -void LogConAdr(const char* szCon, uintptr_t nAdr); // Logging constant addresses. +void LogFunAdr(const char* szFun, const void* const pAdr); // Logging function addresses. +void LogVarAdr(const char* szVar, const void* const pAdr); // Logging variable addresses. +void LogConAdr(const char* szCon, const void* const pAdr); // Logging constant addresses. #endif // TIER0_IFACE_H diff --git a/r5dev/public/tier0/tslist.h b/r5dev/public/tier0/tslist.h index 058e5a82..fa7d25f6 100644 --- a/r5dev/public/tier0/tslist.h +++ b/r5dev/public/tier0/tslist.h @@ -43,22 +43,16 @@ class VTSListBase : public IDetour { virtual void GetAdr(void) const { - LogVarAdr("g_AlignedMemAlloc", reinterpret_cast(g_pAlignedMemAlloc)); + LogVarAdr("g_AlignedMemAlloc", g_pAlignedMemAlloc); } virtual void GetFun(void) const { } virtual void GetVar(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_pAlignedMemAlloc = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8B D9 FF 15 ?? ?? ?? ??") - .Offset(0x600).FindPatternSelf("48 8D 15 ?? ?? ?? 01", CMemory::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) g_pAlignedMemAlloc = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B D9") .Offset(0x130).FindPatternSelf("48 8D 15 ?? ?? ?? 01", CMemory::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#endif } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier0/utility.h b/r5dev/public/tier0/utility.h index 8c983f12..f245e34e 100644 --- a/r5dev/public/tier0/utility.h +++ b/r5dev/public/tier0/utility.h @@ -4,7 +4,9 @@ // Internals BOOL IsBadReadPtrV2(void* ptr); BOOL FileExists(LPCTSTR szPath); -BOOL FileEmpty(ifstream& pFile); +int CreateDirHierarchy(const char* filePath); +bool IsDirectory(const char* path); +bool FileEmpty(ifstream& pFile); MODULEINFO GetModuleInfo(const char* szModule); ///////////////////////////////////////////////////////////////////////////// @@ -62,7 +64,8 @@ string& StringLTrim(string& svInput, const char* pszToTrim, bool bTrimBefore = f string& StringRTrim(string& svInput, const char* pszToTrim, bool bTrimAfter = false); string& StringTrim(string& svInput, const char* pszToTrim, bool bTrimAll = false); -string FourCCToString(int n); +typedef char FourCCString_t[5]; +void FourCCToString(FourCCString_t& buf, const int n); ///////////////////////////////////////////////////////////////////////////// // Bytes @@ -86,6 +89,8 @@ string FormatBytes(size_t nBytes); string FormatV(const char* szFormat, va_list args); string Format(const char* szFormat, ...); +void JSON_DocumentToBufferDeserialize(const rapidjson::Document& document, rapidjson::StringBuffer& buffer, unsigned int indent = 4); + ///////////////////////////////////////////////////////////////////////////// // Array template @@ -117,6 +122,7 @@ int CompareIPv6(const IN6_ADDR& ipA, const IN6_ADDR& ipB); ///////////////////////////////////////////////////////////////////////////// // Time +uint64_t GetUnixTimeStamp(); std::chrono::nanoseconds IntervalToDuration(const float flInterval); ///////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier1/bitbuf.h b/r5dev/public/tier1/bitbuf.h index f43745c5..8581da14 100644 --- a/r5dev/public/tier1/bitbuf.h +++ b/r5dev/public/tier1/bitbuf.h @@ -8,6 +8,7 @@ #ifndef BITBUF_H #define BITBUF_H +#include "mathlib/swap.h" //----------------------------------------------------------------------------- // You can define a handler function that will be called in case of @@ -97,50 +98,142 @@ namespace bitbuf return(n >> 1) ^ -static_cast(n & 1); } - const int kMaxVarintBytes = 10; const int kMaxVarint32Bytes = 5; + const int kMaxVarint64Bytes = 10; } //----------------------------------------------------------------------------- -// Used for serialization +enum EBitCoordType +{ + kCW_None, + kCW_LowPrecision, + kCW_Integral +}; + //----------------------------------------------------------------------------- class CBitBuffer { public: CBitBuffer(void); - inline void SetDebugName(const char* pName) { m_pDebugName = pName; } - inline const char* GetDebugName() const { return m_pDebugName; } - inline bool IsOverflowed() const { return m_bOverflow; } - inline void SetOverflowFlag() { m_bOverflow = true; } + + FORCEINLINE void SetDebugName(const char* pName) { m_pDebugName = pName; } + FORCEINLINE const char* GetDebugName() const { return m_pDebugName; } + FORCEINLINE bool IsOverflowed() const { return m_bOverflow; } + FORCEINLINE void SetOverflowFlag() { m_bOverflow = true; } //////////////////////////////////// const char* m_pDebugName; - uint8_t m_bOverflow; - int64_t m_nDataBits; + bool m_bOverflow; + ssize_t m_nDataBits; size_t m_nDataBytes; + + static const uint32 s_nMaskTable[33]; // 0 1 3 7 15 .. }; +//----------------------------------------------------------------------------- class CBitRead : public CBitBuffer { public: - CBitRead(const void* pData, int nBytes, int nBits = -1); - CBitRead(const char* pDebugName, const void* pData, int nBytes, int nBits = -1); + CBitRead(const void* pData, size_t nBytes, ssize_t nBits = -1); + CBitRead(const char* pDebugName, const void* pData, size_t nBytes, ssize_t nBits = -1); CBitRead(void) : CBitBuffer() { } - void StartReading(const void* pData, size_t nBytes, int64 iStartBit = 0, int64 nBits = -1); - bool Seek(int64 nPosition); + void StartReading(const void* pData, size_t nBytes, ssize_t iStartBit = 0, ssize_t nBits = -1); - void GrabNextDWord(bool bOverFlowImmediately = false); - void FetchNext(); + FORCEINLINE ssize_t Tell(void) const; + bool Seek(ssize_t nPosition); - int ReadSBitLong(int numbits); - uint32 ReadUBitLong(int numbits); + FORCEINLINE bool SeekRelative(ssize_t nOffset) + { + return Seek(GetNumBitsRead() + nOffset); + } - inline int ReadByte() { return ReadSBitLong(sizeof(unsigned char) << 3); } - inline int ReadChar() { return ReadSBitLong(sizeof(char) << 3); } - bool ReadString(char* pStr, int bufLen, bool bLine = false, int* pOutNumChars = nullptr); + FORCEINLINE unsigned char const* GetBasePointer() + { + return reinterpret_cast(m_pData); + } + + FORCEINLINE ssize_t GetNumBitsRead(void) const { return Tell(); }; + FORCEINLINE ssize_t GetNumBytesRead(void) const { return ((GetNumBitsRead() + 7) >> 3); } + + FORCEINLINE ssize_t GetNumBitsLeft(void) const { return m_nDataBits - GetNumBitsRead(); } + FORCEINLINE ssize_t GetNumBytesLeft(void) const { return GetNumBitsLeft() >> 3; } + + FORCEINLINE size_t TotalBytesAvailable(void) const { return m_nDataBytes; } + + FORCEINLINE void GrabNextDWord(bool bOverFlowImmediately = false); + FORCEINLINE void FetchNext(void); + + FORCEINLINE unsigned int ReadUBitLong(int numbits); + FORCEINLINE int ReadSBitLong(int numbits); + FORCEINLINE unsigned int PeekUBitLong(int numbits); + + FORCEINLINE unsigned int ReadUBitVar(void); + + FORCEINLINE float ReadBitFloat(void) + { + uint32 nvalue = ReadUBitLong(32); + return *((float*)&nvalue); + } + + + float ReadBitCoord(); + float ReadBitCoordMP(EBitCoordType coordType); + float ReadBitCellCoord(int bits, EBitCoordType coordType); + float ReadBitNormal(); + void ReadBitVec3Coord(Vector3D& fa); + void ReadBitVec3Normal(Vector3D& fa); + void ReadBitAngles(QAngle& fa); + float ReadBitAngle(int numbits); + + // returns 0 or 1. + FORCEINLINE int ReadOneBit(void); + + FORCEINLINE int ReadChar(void) { return ReadSBitLong(sizeof(char) << 3); } + FORCEINLINE int ReadByte(void) { return ReadSBitLong(sizeof(unsigned char) << 3); } + FORCEINLINE int ReadShort(void) { return ReadUBitLong(sizeof(short) << 3); } + FORCEINLINE int ReadWord(void) { return ReadUBitLong(sizeof(unsigned short) << 3); } + FORCEINLINE int ReadLong(void) { return ReadUBitLong(sizeof(int32) << 3); } + FORCEINLINE float ReadFloat(void) + { + uint32 nUval = ReadUBitLong(sizeof(int32) << 3); + return *((float*)&nUval); + } + + // reads a signed 64-bit integer from the buffer + int64 ReadLongLong(void); + + // reads a varint encoded integer + uint32 ReadVarInt32(); + uint64 ReadVarInt64(); + int32 ReadSignedVarInt32() { return bitbuf::ZigZagDecode32(ReadVarInt32()); } + int64 ReadSignedVarInt64() { return bitbuf::ZigZagDecode64(ReadVarInt64()); } + + void ReadBits(void* pOutData, int nBits); + bool ReadBytes(void* pOut, int nBytes); + + + // Returns false if bufLen isn't large enough to hold the + // string in the buffer. + // + // Always reads to the end of the string (so you can read the + // next piece of data waiting). + // + // If bLine is true, it stops when it reaches a '\n' or a null-terminator. + // + // pStr is always null-terminated (unless bufLen is 0). + // + // pOutNumChars is set to the number of characters left in pStr when the routine is + // complete (this will never exceed bufLen-1). + // + bool ReadString(char* pStr, int bufLen, bool bLine = false, int* pOutNumChars = NULL); + bool ReadWString(wchar_t* pStr, int bufLen, bool bLine = false, int* pOutNumChars = NULL); + + // Reads a string and allocates memory for it. If the string in the buffer + // is > 2048 bytes, then pOverflow is set to true (if it's not NULL). + char* ReadAndAllocateString(bool *pOverflow = NULL); //////////////////////////////////// uint32 m_nInBufWord; @@ -150,61 +243,100 @@ public: const uint32* m_pData; }; -class bf_read : public CBitRead +//----------------------------------------------------------------------------- +class CBitWrite { public: - -}; - -struct bf_write -{ -public: - bf_write(); + CBitWrite(); // nMaxBits can be used as the number of bits in the buffer. // It must be <= nBytes*8. If you leave it at -1, then it's set to nBytes * 8. - bf_write(void* pData, int nBytes, int nMaxBits = -1); - bf_write(const char* pDebugName, void* pData, int nBytes, int nMaxBits = -1); + CBitWrite(void* pData, int nBytes, int nMaxBits = -1); + CBitWrite(const char* pDebugName, void* pData, int nBytes, int nMaxBits = -1); // Restart buffer writing. - inline void Reset() { m_iCurBit = 0; m_bOverflow = false; } - inline void SeekToBit(int bitPos) { m_iCurBit = bitPos; } + inline void Reset() { m_iCurBit = 0; m_bOverflow = false; } + inline void SeekToBit(int bitPos) { m_iCurBit = bitPos; } void StartWriting(void* pData, int nBytes, int iStartBit = 0, int nMaxBits = -1); // Bit functions. - void WriteOneBit(int nValue); - void WriteOneBitNoCheck(int nValue); - void WriteOneBitAt(int iBit, int nValue); + void WriteOneBit(int nValue); + void WriteOneBitNoCheck(int nValue); + void WriteOneBitAt(int iBit, int nValue); // Write signed or unsigned. Range is only checked in debug. - void WriteUBitLong(unsigned int curData, int numbits, bool bCheckRange = false); - void WriteSBitLong(int data, int numbits); + void WriteUBitLong(unsigned int curData, int numbits, bool bCheckRange = false); + void WriteSBitLong(int data, int numbits); // Tell it whether or not the data is unsigned. If it's signed, // cast to unsigned before passing in (it will cast back inside). - void WriteBitLong(unsigned int data, int numbits, bool bSigned); + void WriteBitLong(unsigned int data, int numbits, bool bSigned); // Write a list of bits in. - bool WriteBits(const void* pIn, int nBits); - inline bool WriteBytes(const void* pIn, int nBytes) { return WriteBits(pIn, nBytes << 3); } + bool WriteBits(const void* pIn, int nBits); + + // copy the bits straight out of pIn. This seeks pIn forward by nBits, + // returns false if this buffer or the read buffer overflows + bool WriteBitsFromBuffer(class bf_read *pIn, int nBits); + + // writes an unsigned integer with variable bit length + void WriteUBitVar(unsigned int data); + + // writes a varint encoded integer + void WriteVarInt32(uint32 data); + void WriteVarInt64(uint64 data); + void WriteSignedVarInt32(int32 data); + void WriteSignedVarInt64(int64 data); + int ByteSizeVarInt32(uint32 data); + int ByteSizeVarInt64(uint64 data); + int ByteSizeSignedVarInt32(int32 data); + int ByteSizeSignedVarInt64(int64 data); + + // writes an angle or vector encoded + void WriteBitAngle(float fAngle, int numbits); + void WriteBitCoord(const float f); + void WriteBitCoordMP(const float f, bool bIntegral, bool bLowPrecision); + void WriteBitCellCoord(const float f, int bits, EBitCoordType coordType); + void WriteBitFloat(float val); + void WriteBitVec3Coord(const Vector3D& fa); + void WriteBitNormal(float f); + void WriteBitVec3Normal(const Vector3D& fa); + void WriteBitAngles(const QAngle& fa); + + // byte functions + void WriteChar(int val); + void WriteByte(int val); + void WriteShort(int val); + void WriteWord(int val); + void WriteLong(long val); + void WriteLongLong(int64 val); + void WriteFloat(float val); + bool WriteBytes(const void* pBuf, int nBytes); + + // Returns false if it overflows the buffer. + bool WriteString(const char* pStr); + bool WriteWString(const wchar_t* pStr); // How many bytes are filled in? - inline int GetNumBytesWritten() const { return BitByte(this->m_iCurBit); } - inline int GetNumBitsWritten() const { return this->m_iCurBit; } - inline int GetMaxNumBits() const { return this->m_nDataBits; } - inline int GetNumBitsLeft() const { return this->m_nDataBits - m_iCurBit; } - inline int GetNumBytesLeft() const { return this->GetNumBitsLeft() >> 3; } - inline unsigned char* GetData() const { return this->m_pData; } + FORCEINLINE int GetNumBytesWritten() const { return BitByte(this->m_iCurBit); } + FORCEINLINE int GetNumBitsWritten() const { return this->m_iCurBit; } + FORCEINLINE int GetMaxNumBits() const { return this->m_nDataBits; } + FORCEINLINE int GetNumBitsLeft() const { return this->m_nDataBits - m_iCurBit; } + FORCEINLINE int GetNumBytesLeft() const { return this->GetNumBitsLeft() >> 3; } - inline const char* GetDebugName() const { return this->m_pDebugName; } - inline void SetDebugName(const char* pDebugName) { m_pDebugName = pDebugName; } + FORCEINLINE unsigned char* GetData() { return this->m_pData; } + FORCEINLINE const unsigned char* GetData() const { return this->m_pData; } + + FORCEINLINE const char* GetDebugName() const { return this->m_pDebugName; } + FORCEINLINE void SetDebugName(const char* pDebugName) { m_pDebugName = pDebugName; } // Has the buffer overflowed? bool CheckForOverflow(int nBits); void SetOverflowFlag(); - inline bool IsOverflowed() const { return this->m_bOverflow; } + FORCEINLINE bool IsOverflowed() const { return this->m_bOverflow; } + private: // The current buffer. unsigned char* m_pData; @@ -219,4 +351,287 @@ private: bool m_bAssertOnOverflow; const char* m_pDebugName; }; + +//----------------------------------------------------------------------------- +// Used for unserialization +//----------------------------------------------------------------------------- +class bf_read : public CBitRead +{ +public: + bf_read(const void* pData, int nBytes, int nBits = -1) + : CBitRead(pData, nBytes, nBits) {} + + bf_read(const char* pDebugName, void* pData, int nBytes, int nMaxBits = -1) + : CBitRead(pDebugName, pData, nMaxBits) {} + + bf_read(void) : CBitRead() + {} +}; + +//----------------------------------------------------------------------------- +// Used for serialization +//----------------------------------------------------------------------------- +class bf_write : public CBitWrite +{ +public: + bf_write(void* pData, int nBytes, int nMaxBits = -1) + : CBitWrite(pData, nBytes, nMaxBits) {} + + bf_write(const char* pDebugName, void* pData, int nBytes, int nMaxBits = -1) + : CBitWrite(pDebugName, pData, nBytes, nMaxBits) {} + + bf_write(void) : CBitWrite() + {} +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for getting positions and grabbing next data in the read buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +FORCEINLINE ssize_t CBitRead::Tell(void) const +{ + if (!m_pData) // pesky null ptr bitbufs. these happen. + { + Assert(m_pData); + return 0; + } + + ssize_t nCurOfs = int64(((intp(m_pDataIn) - intp(m_pData)) / 4) - 1); + nCurOfs *= 32; + nCurOfs += (32 - m_nBitsAvail); + ssize_t nAdjust = 8 * (m_nDataBytes & 3); + + return MIN(nCurOfs + nAdjust, m_nDataBits); +} + +//----------------------------------------------------------------------------- +FORCEINLINE void CBitRead::GrabNextDWord(bool bOverFlowImmediately) +{ + if (m_pDataIn == m_pBufferEnd) + { + m_nBitsAvail = 1; + m_nInBufWord = 0; + + m_pDataIn++; + + if (bOverFlowImmediately) + SetOverflowFlag(); + } + else + { + if (m_pDataIn > m_pBufferEnd) + { + SetOverflowFlag(); + m_nInBufWord = 0; + } + else + { + assert(reinterpret_cast(m_pDataIn) + 3 < reinterpret_cast(m_pBufferEnd)); + m_nInBufWord = LittleDWord(*(m_pDataIn++)); + } + } +} + +//----------------------------------------------------------------------------- +FORCEINLINE void CBitRead::FetchNext() +{ + m_nBitsAvail = 32; + GrabNextDWord(false); +} + +//----------------------------------------------------------------------------- +FORCEINLINE int CBitRead::ReadOneBit(void) +{ + int nRet = m_nInBufWord & 1; + if (--m_nBitsAvail == 0) + { + FetchNext(); + } + else + m_nInBufWord >>= 1; + return nRet; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for reading encoded integers from the buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// reads an unsigned integer from the buffer +FORCEINLINE unsigned int CBitRead::ReadUBitLong(int numbits) +{ + if (m_nBitsAvail >= numbits) + { + unsigned int nRet = m_nInBufWord & s_nMaskTable[numbits]; + m_nBitsAvail -= numbits; + if (m_nBitsAvail) + { + m_nInBufWord >>= numbits; + } + else + { + FetchNext(); + } + return nRet; + } + else + { + uint32 nRet = m_nInBufWord; + numbits -= m_nBitsAvail; + GrabNextDWord(true); + + if (IsOverflowed()) + return 0; + + nRet |= ((m_nInBufWord & s_nMaskTable[numbits]) << m_nBitsAvail); + m_nBitsAvail = 32 - numbits; + m_nInBufWord >>= numbits; + + return nRet; + } +} + +//----------------------------------------------------------------------------- +// reads a signed integer from the buffer +FORCEINLINE int CBitRead::ReadSBitLong(int numbits) +{ + int nRet = ReadUBitLong(numbits); + return (nRet << (32 - numbits)) >> (32 - numbits); +} + +//----------------------------------------------------------------------------- +// peeks an unsigned integer from the buffer +FORCEINLINE unsigned int CBitRead::PeekUBitLong(int numbits) +{ + int nSaveBA = m_nBitsAvail; + uint32_t nSaveW = m_nInBufWord; + uint32 const* pSaveP = m_pDataIn; + unsigned int nRet = ReadUBitLong(numbits); + m_nBitsAvail = nSaveBA; + m_nInBufWord = nSaveW; + m_pDataIn = pSaveP; + return nRet; +} + +#ifdef _WIN32 +#pragma warning(push) +#pragma warning(disable : 4715) // disable warning on not all cases + // returning a value. throwing default: + // in measurably reduces perf in bit + // packing benchmark +#endif +//----------------------------------------------------------------------------- +// reads an unsigned integer with variable bit length +FORCEINLINE unsigned int CBitRead::ReadUBitVar(void) +{ + unsigned int ret = ReadUBitLong(6); + switch (ret & (16 | 32)) + { + case 16: + ret = (ret & 15) | (ReadUBitLong(4) << 4); + Assert(ret >= 16); + break; + + case 32: + ret = (ret & 15) | (ReadUBitLong(8) << 4); + Assert(ret >= 256); + break; + case 48: + ret = (ret & 15) | (ReadUBitLong(32 - 4) << 4); + Assert(ret >= 4096); + break; + } + return ret; +} +#ifdef _WIN32 +#pragma warning(pop) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for bit level writing operations +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// writes a bit to the buffer without checking for overflow +FORCEINLINE void CBitWrite::WriteOneBitNoCheck(int nValue) +{ + if (nValue) + m_pData[m_iCurBit >> 3] |= (1 << (m_iCurBit & 7)); + else + m_pData[m_iCurBit >> 3] &= ~(1 << (m_iCurBit & 7)); + + ++m_iCurBit; +} + +//----------------------------------------------------------------------------- +// writes a bit to the buffer +FORCEINLINE void CBitWrite::WriteOneBit(int nValue) +{ + if (!CheckForOverflow(1)) + WriteOneBitNoCheck(nValue); +} + +//----------------------------------------------------------------------------- +// writes a bit to the buffer at a specific bit index +FORCEINLINE void CBitWrite::WriteOneBitAt(int iBit, int nValue) +{ + if (iBit + 1 > m_nDataBits) + { + SetOverflowFlag(); + CallErrorHandler(BITBUFERROR_BUFFER_OVERRUN, GetDebugName()); + return; + } + + if (nValue) + m_pData[iBit >> 3] |= (1 << (iBit & 7)); + else + m_pData[iBit >> 3] &= ~(1 << (iBit & 7)); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Routines for writing integers into the buffer +/////////////////////////////////////////////////////////////////////////////// + +//----------------------------------------------------------------------------- +// writes an unsigned integer with variable bit length +FORCEINLINE void CBitWrite::WriteUBitVar(unsigned int data) +{ + /* Reference: + if ( data < 0x10u ) + WriteUBitLong( 0, 2 ), WriteUBitLong( data, 4 ); + else if ( data < 0x100u ) + WriteUBitLong( 1, 2 ), WriteUBitLong( data, 8 ); + else if ( data < 0x1000u ) + WriteUBitLong( 2, 2 ), WriteUBitLong( data, 12 ); + else + WriteUBitLong( 3, 2 ), WriteUBitLong( data, 32 ); + */ + // a < b ? -1 : 0 translates into a CMP, SBB instruction pair + // with no flow control. should also be branchless on consoles. + int n = (data < 0x10u ? -1 : 0) + (data < 0x100u ? -1 : 0) + (data < 0x1000u ? -1 : 0); + WriteUBitLong(data * 4 + n + 3, 6 + n * 4 + 12); + if (data >= 0x1000u) + { + WriteUBitLong(data >> 16, 16); + } +} + +//----------------------------------------------------------------------------- +// write raw IEEE float bits in little endian form +FORCEINLINE void CBitWrite::WriteBitFloat(float val) +{ + long intVal; + + Assert(sizeof(long) == sizeof(float)); + Assert(sizeof(float) == 4); + + intVal = *((long*)&val); + WriteUBitLong(intVal, 32); +} + #endif // BITBUF_H diff --git a/r5dev/public/tier1/cmd.h b/r5dev/public/tier1/cmd.h index 9b9eff5b..df51c698 100644 --- a/r5dev/public/tier1/cmd.h +++ b/r5dev/public/tier1/cmd.h @@ -57,24 +57,92 @@ public: CCommand(int nArgC, const char** ppArgV, cmd_source_t source); bool Tokenize(const char* pCommand, cmd_source_t source = cmd_source_t::kCommandSrcCode, characterset_t* pBreakSet = nullptr); - int64_t ArgC(void) const; + int ArgC(void) const; const char** ArgV(void) const; const char* ArgS(void) const; const char* GetCommandString(void) const; const char* Arg(int nIndex) const; const char* operator[](int nIndex) const; - void Reset(); - int MaxCommandLength(void) const; bool HasOnlyDigits(int nIndex) const; + void Reset(); + + static int MaxCommandLength(void); + static characterset_t* DefaultBreakSet(); private: cmd_source_t m_nQueuedVal; int m_nArgc; - int64_t m_nArgv0Size; + ssize_t m_nArgv0Size; char m_pArgSBuffer[COMMAND_MAX_LENGTH]; char m_pArgvBuffer[COMMAND_MAX_LENGTH]; const char* m_ppArgv[COMMAND_MAX_ARGC]; }; +//----------------------------------------------------------------------------- +// Purpose: returns max command length +//----------------------------------------------------------------------------- +inline int CCommand::MaxCommandLength(void) +{ + return COMMAND_MAX_LENGTH - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: returns argument count +//----------------------------------------------------------------------------- +inline int CCommand::ArgC(void) const +{ + return m_nArgc; +} + +//----------------------------------------------------------------------------- +// Purpose: returns argument vector +//----------------------------------------------------------------------------- +inline const char** CCommand::ArgV(void) const +{ + return m_nArgc ? (const char**)m_ppArgv : NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: returns all args that occur after the 0th arg, in string form +//----------------------------------------------------------------------------- +inline const char* CCommand::ArgS(void) const +{ + return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; +} + +//----------------------------------------------------------------------------- +// Purpose: returns the entire command in string form, including the 0th arg +//----------------------------------------------------------------------------- +inline const char* CCommand::GetCommandString(void) const +{ + return m_nArgc ? m_pArgSBuffer : ""; +} + +//----------------------------------------------------------------------------- +// Purpose: returns argument from index as string +// Input : nIndex - +//----------------------------------------------------------------------------- +inline const char* CCommand::Arg(int nIndex) const +{ + // FIXME: Many command handlers appear to not be particularly careful + // about checking for valid argc range. For now, we're going to + // do the extra check and return an empty string if it's out of range + if (nIndex < 0 || nIndex >= m_nArgc) + { + Assert(0); + return ""; + } + return m_ppArgv[nIndex]; +} + +//----------------------------------------------------------------------------- +// Purpose: gets at arguments +// Input : nInput - +//----------------------------------------------------------------------------- +inline const char* CCommand::operator[](int nIndex) const +{ + return Arg(nIndex); +} + #endif // TIER1_CMD_H diff --git a/r5dev/public/tier1/commandbuffer.h b/r5dev/public/tier1/commandbuffer.h new file mode 100644 index 00000000..de4614ed --- /dev/null +++ b/r5dev/public/tier1/commandbuffer.h @@ -0,0 +1,167 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: command buffer class implementation +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//===========================================================================// + + +#ifndef COMMANDBUFFER_H +#define COMMANDBUFFER_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "tier1/utllinkedlist.h" +#include "tier1/convar.h" + + +//----------------------------------------------------------------------------- +// Forward declarations +//----------------------------------------------------------------------------- +class CUtlBuffer; + + +//----------------------------------------------------------------------------- +// Command handle +//----------------------------------------------------------------------------- +typedef intptr_t CommandHandle_t; +enum +{ + COMMAND_BUFFER_INVALID_COMMAND_HANDLE = 0 +}; + + +//----------------------------------------------------------------------------- +// A command buffer class- a queue of argc/argv based commands associated +// with a particular time +//----------------------------------------------------------------------------- +class CCommandBuffer +{ +public: + // Constructor, destructor + CCommandBuffer(); + ~CCommandBuffer(); + + // Inserts text into the command buffer + bool AddText( const char *const pText, const int nTickDelay = 0, const cmd_source_t cmdSource = cmd_source_t::kCommandSrcInvalid ); + + // Used to iterate over all commands appropriate for the current time + void BeginProcessingCommands( const int nDeltaTicks ); + bool DequeueNextCommand( ); + int DequeueNextCommand( const char **& ppArgv ); + int ArgC() const; + const char **ArgV() const; + const char *ArgS() const; // All args that occur after the 0th arg, in string form + const char *GetCommandString() const; // The entire command in string form, including the 0th arg + const CCommand& GetCommand() const; + void EndProcessingCommands(); + + // Are we in the middle of processing commands? + bool IsProcessingCommands() const; + + // Delays all queued commands to execute at a later time + void DelayAllQueuedCommands( const int nTickDelay ); + + // Indicates how long to delay when encountering a 'wait' command + void SetWaitDelayTime( const int nTickDelay ); + + // Returns a handle to the next command to process + // (useful when inserting commands into the buffer during processing + // of commands to force immediate execution of those commands, + // most relevantly, to implement a feature where you stream a file + // worth of commands into the buffer, where the file size is too large + // to entirely contain in the buffer). + CommandHandle_t GetNextCommandHandle() const; + + // Specifies a max limit of the args buffer. For unit testing. Size == 0 means use default + void LimitArgumentBufferSize( ssize_t nSize ); + + void SetWaitEnabled( const bool bEnable ) { m_bWaitEnabled = bEnable; } + bool IsWaitEnabled( void ) const { return m_bWaitEnabled; } + + ssize_t GetArgumentBufferSize() const { return m_nArgSBufferSize; } + ssize_t GetMaxArgumentBufferSize() const { return m_nMaxArgSBufferLength; } + +private: + enum + { + ARGS_BUFFER_LENGTH = 8192, + }; + + struct Command_t + { + int m_nTick; + ssize_t m_nFirstArgS; + ssize_t m_nBufferSize; + cmd_source_t m_Source; + }; + + // Insert a command into the command queue at the appropriate time + void InsertCommandAtAppropriateTime( const intptr_t hCommand ); + + // Insert a command into the command queue + // Only happens if it's inserted while processing other commands + void InsertImmediateCommand( const intptr_t hCommand ); + + // Insert a command into the command queue + bool InsertCommand( const char *const pArgS, ssize_t nCommandSize, const int nTick, const cmd_source_t cmdSource ); + + // Returns the length of the next command, as well as the offset to the next command + void GetNextCommandLength( const char *const pText, const ssize_t nMaxLen, ssize_t *const pCommandLength, ssize_t *const pNextCommandOffset ) const; + + // Compacts the command buffer + void Compact(); + + // Parses argv0 out of the buffer + bool ParseArgV0( CUtlBuffer &buf, char *const pArgv0, const ssize_t nMaxLen, const char **const pArgs ) const; + + char m_pArgSBuffer[ ARGS_BUFFER_LENGTH ]; + ssize_t m_nArgSBufferSize; + CUtlFixedLinkedList< Command_t > m_Commands; + int m_nCurrentTick; + int m_nLastTickToProcess; + int m_nWaitDelayTicks; + intptr_t m_hNextCommand; + ssize_t m_nMaxArgSBufferLength; + bool m_bIsProcessingCommands; + bool m_bWaitEnabled; + + // NOTE: This is here to avoid the pointers returned by DequeueNextCommand + // to become invalid by calling AddText. Is there a way we can avoid the memcpy? + CCommand m_CurrentCommand; +}; + + +//----------------------------------------------------------------------------- +// Returns the next command +//----------------------------------------------------------------------------- +inline int CCommandBuffer::ArgC() const +{ + return m_CurrentCommand.ArgC(); +} + +inline const char **CCommandBuffer::ArgV() const +{ + return m_CurrentCommand.ArgV(); +} + +inline const char *CCommandBuffer::ArgS() const +{ + return m_CurrentCommand.ArgS(); +} + +inline const char *CCommandBuffer::GetCommandString() const +{ + return m_CurrentCommand.GetCommandString(); +} + +inline const CCommand& CCommandBuffer::GetCommand() const +{ + return m_CurrentCommand; +} + +#endif // COMMANDBUFFER_H diff --git a/r5dev/public/tier1/convar.h b/r5dev/public/tier1/convar.h index 34a26775..a234ec4f 100644 --- a/r5dev/public/tier1/convar.h +++ b/r5dev/public/tier1/convar.h @@ -15,6 +15,7 @@ #include "mathlib/color.h" #include "public/iconvar.h" #include "public/iconcommand.h" +#include "tier1/cmd.h" #include "tier1/utlvector.h" #include "tier1/utlstring.h" @@ -24,40 +25,60 @@ class ConCommandBase { public: - virtual ~ConCommandBase(void) { }; + virtual ~ConCommandBase(void); - virtual bool IsCommand(void) const = 0; - virtual bool IsFlagSet(int nFlags) const = 0; + virtual bool IsCommand(void) const; + virtual bool IsFlagSet(const int nFlags) const; - virtual void AddFlags(int nFlags) = 0; - virtual void RemoveFlags(int nFlags) = 0; + virtual void AddFlags(const int nFlags); + virtual void RemoveFlags(const int nFlags); - virtual int GetFlags(void) const = 0; - virtual const char* GetName(void) const = 0; - virtual const char* GetHelpText(void) const = 0; - virtual const char* GetUsageText(void) const = 0; + virtual int GetFlags(void) const; + virtual const char* GetName(void) const; + virtual const char* GetHelpText(void) const; - virtual void SetAccessor(IConCommandBaseAccessor* pAccessor) = 0; - virtual bool IsRegistered(void) const = 0; + virtual const char* GetUsageText(void) const; + virtual void SetUsageText(const char* const usageText); - virtual int GetDLLIdentifier() const = 0; + virtual bool IsRegistered(void) const; + + virtual int GetDLLIdentifier() const; virtual ConCommandBase* Create(const char* szName, const char* szHelpString, - int nFlags, const char* pszUsageString) = 0; + int nFlags, const char* pszUsageString); - virtual void Init() = 0; - - bool HasFlags(int nFlags) const; + virtual void Init(); + void Shutdown(); ConCommandBase* GetNext(void) const; char* CopyString(const char* szFrom) const; +//private: + // Next ConVar in chain + // Prior to register, it points to the next convar in the DLL. + // Once registered, though, m_pNext is reset to point to the next + // convar in the global list ConCommandBase* m_pNext; //0x0008 + + // Has the cvar been added to the global list? bool m_bRegistered; //0x0010 + + // Static data. const char* m_pszName; //0x0018 const char* m_pszHelpString; //0x0020 - const char* m_pszUsageString; //0x0028 - IConCommandBaseAccessor* s_pAccessor; //0x0030 <-- unused since executable is monolithic. + + const char* m_pszStaticUsageString; //0x0028 + const char* m_pszCustomUsageString; //0x0030 + + // ConVar flags int m_nFlags; //0x0038 + + // ConVars add themselves to this list for the executable. + // Then ConVar_Register runs through all the console variables + // and registers them into a global list stored in vstdlib.dll + static ConCommandBase* s_pConCommandBases; + + // ConVars in this executable use this 'global' to access values. + static IConCommandBaseAccessor* s_pAccessor; }; static_assert(sizeof(ConCommandBase) == 0x40); @@ -70,16 +91,37 @@ class ConCommand : public ConCommandBase { friend class CCvar; public: - ConCommand(void); + typedef ConCommandBase BaseClass; - static ConCommand* StaticCreate(const char* szName, const char* szHelpString, const char* pszUsageString, - int nFlags, FnCommandCallback_t pCallback, FnCommandCompletionCallback pCommandCompletionCallback); + ConCommand(const char* pName, FnCommandCallbackV1_t callback, + const char* pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0, const char* pszUsageString = 0); + ConCommand(const char* pName, FnCommandCallback_t callback, + const char* pHelpString = 0, int flags = 0, FnCommandCompletionCallback completionFunc = 0, const char* pszUsageString = 0); + ConCommand(const char* pName, ICommandCallback* pCallback, + const char* pHelpString = 0, int flags = 0, ICommandCompletionCallback* pCommandCompletionCallback = 0, const char* pszUsageString = 0); - virtual int AutoCompleteSuggest(const char* partial, CUtlVector< CUtlString >& commands) = 0; - virtual bool CanAutoComplete(void) const = 0; + virtual ~ConCommand(void); + + virtual bool IsCommand(void) const; + + virtual int AutoCompleteSuggest(const char* partial, CUtlVector< CUtlString >& commands); + virtual bool CanAutoComplete(void) const; + + // Invoke the function + virtual void Dispatch(const ECommandTarget_t target, const CCommand& command, const bool bCallSupplemental); + +//private: + FnCommandSupplementalFinishCallback_t m_fnSupplementalFinishCallBack; //0x0040 + FnCommandSupplementalCallback_t m_fnSupplementalCallback; //0x0048 + + // NOTE: To maintain backward compatibility, we have to be very careful: + // All public virtual methods must appear in the same order always + // since engine code will be calling into this code, which *does not match* + // in the mod code; it's using slightly different, but compatible versions + // of this class. Also: Be very careful about adding new fields to this class. + // Those fields will not exist in the version of this class that is instanced + // in mod code. - void* m_nNullCallBack; //0x0040 - void* m_pSubCallback; //0x0048 // Call this function when executing the command union { @@ -111,56 +153,109 @@ class ConVar : public ConCommandBase, public IConVar friend class ConVarRef; public: - static ConVar* StaticCreate(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, - bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t pCallback, const char* pszUsageString); - void Destroy(void); + typedef ConCommandBase BaseClass; - ConVar(void); - virtual ~ConVar(void) { }; + ConVar(const char* pName, const char* pDefaultValue, int flags = 0); - FORCEINLINE bool GetBool(void) const; - FORCEINLINE float GetFloat(void) const; - FORCEINLINE double GetDouble(void) const; - FORCEINLINE int GetInt(void) const; - FORCEINLINE Color GetColor(void) const; - FORCEINLINE const char* GetString(void) const; + ConVar(const char* pName, const char* pDefaultValue, int flags, + const char* pHelpString, const char* pUsageString = 0); + ConVar(const char* pName, const char* pDefaultValue, int flags, + const char* pHelpString, bool bMin, float fMin, bool bMax, float fMax, const char* pUsageString = 0); + ConVar(const char* pName, const char* pDefaultValue, int flags, + const char* pHelpString, FnChangeCallback_t callback, const char* pUsageString = 0); + ConVar(const char* pName, const char* pDefaultValue, int flags, + const char* pHelpString, bool bMin, float fMin, bool bMax, float fMax, + FnChangeCallback_t callback, const char* pUsageString = 0); - void SetMax(float flMaxValue); - void SetMin(float flMinValue); - bool GetMin(float& flMinValue) const; - bool GetMax(float& flMaxValue) const; - float GetMinValue(void) const; - float GetMaxValue(void) const; - bool HasMin(void) const; - bool HasMax(void) const; + virtual ~ConVar(void); - void SetValue(int nValue); - void SetValue(float flValue); - void SetValue(const char* pszValue); - void SetValue(Color clValue); + virtual bool IsCommand(void) const; + virtual bool IsFlagSet(const int flag) const; - virtual void InternalSetValue(const char* pszValue) = 0; - virtual void InternalSetFloatValue(float flValue) = 0; - virtual void InternalSetIntValue(int nValue) = 0; - void InternalSetColorValue(Color value); + virtual void AddFlags(const int flags); + virtual int GetFlags() const; - virtual __int64 Unknown0(unsigned int a2) = 0; - virtual __int64 Unknown1(const char* a2) = 0; + // Return name of cvar + virtual const char* GetName(void) const; - void Revert(void); - virtual bool ClampValue(float& flValue) = 0; + // Return name of command (usually == GetName(), except in case of FCVAR_SS_ADDED vars + virtual const char* GetBaseName(void) const; + virtual int GetSplitScreenPlayerSlot() const; - const char* GetDefault(void) const; - void SetDefault(const char* pszDefault); - bool SetColorFromString(const char* pszValue); + // Return help text for cvar + virtual const char* GetHelpText(void) const; - virtual void ChangeStringValue(const char* pszTempValue) = 0; - virtual void CreateInternal(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, - bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t pCallback, const char* pszUsageString) = 0; + virtual const char* GetUsageText(void) const; + virtual void SetUsageText(const char* const usageText); - void InstallChangeCallback(FnChangeCallback_t callback, bool bInvoke); - void RemoveChangeCallback(FnChangeCallback_t callback); + virtual bool IsRegistered(void) const; + // Install a change callback (there shouldn't already be one....) + void InstallChangeCallback(FnChangeCallback_t callback, bool bInvoke); + void RemoveChangeCallback(FnChangeCallback_t callback); + + int GetChangeCallbackCount() const { return m_pParent->m_fnChangeCallbacks.Count(); } + FnChangeCallback_t GetChangeCallback(int slot) const { return m_pParent->m_fnChangeCallbacks[slot]; } + + FORCEINLINE bool GetBool(void) const; + FORCEINLINE float GetFloat(void) const; + FORCEINLINE int GetInt(void) const; + FORCEINLINE Color GetColor(void) const; + FORCEINLINE const char* GetString(void) const; + + // Any function that allocates/frees memory needs to be virtual or else you'll have crashes + // from alloc/free across dll/exe boundaries. + + // These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue). + virtual void SetValue(const char *value); + virtual void SetValue(float value); + virtual void SetValue(int value); + void SetValue(Color value); + + // Reset to default value + void Revert(void); + + // True if it has a min/max setting + bool HasMin(void) const; + bool HasMax(void) const; + + void SetMax(float flMaxValue); + void SetMin(float flMinValue); + + bool GetMin(float& flMinValue) const; + bool GetMax(float& flMaxValue) const; + + float GetMinValue(void) const; + float GetMaxValue(void) const; + + const char* GetDefault(void) const; + void SetDefault(const char* pszDefault); + +private: + bool InternalSetColorFromString(const char* value); + + // Called by CCvar when the value of a var is changing. + virtual void InternalSetValue(const char* pszValue); + + // For CVARs marked FCVAR_NEVER_AS_STRING + virtual void InternalSetFloatValue(float flValue); + virtual void InternalSetIntValue(int nValue); + + virtual void InternalSetColorValue(Color value); + + // DoNothing in the engine, probably for tracking/debugging cvar strings in debug. + virtual void TrackDefaultValue(const char* value) { }; + + virtual bool ClampValue(float& flValue); + + virtual void ChangeStringValue(const char* pszTempValue); + + virtual void Create(const char* pName, const char* pDefaultValue, int flags = 0, + const char* pHelpString = 0, bool bMin = false, float fMin = 0.0, bool bMax = false, float fMax = false, + FnChangeCallback_t callback = 0, const char* pszUsageString = 0); + +//protected: +public: // TODO: make protected! struct CVValue_t { char* m_pszString; @@ -200,15 +295,6 @@ FORCEINLINE float ConVar::GetFloat(void) const return m_pParent->m_Value.m_fValue; } -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a double. -// Output : double -//----------------------------------------------------------------------------- -FORCEINLINE double ConVar::GetDouble(void) const -{ - return static_cast(m_pParent->m_Value.m_fValue); -} - //----------------------------------------------------------------------------- // Purpose: Return ConVar value as an integer. // Output : int @@ -243,60 +329,10 @@ FORCEINLINE const char* ConVar::GetString(void) const return str ? str : ""; } -/* ==== CONVAR ========================================================================================================================================================== */ -inline CMemory p_ConVar_Register; -inline void*(*v_ConVar_Register)(ConVar* thisptr, const char* szName, const char* szDefaultValue, int nFlags, const char* szHelpString, bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t pCallback, const char* pszUsageString); - -inline CMemory p_ConVar_Unregister; -inline void(*v_ConVar_Unregister)(ConVar* thisptr); - -inline CMemory p_ConVar_IsFlagSet; -inline bool(*v_ConVar_IsFlagSet)(ConVar* pConVar, int nFlag); - -inline ConCommandBase* g_pConCommandBaseVFTable; -inline ConCommand* g_pConCommandVFTable; -inline ConVar* g_pConVarVBTable; -inline IConVar* g_pConVarVFTable; - -/////////////////////////////////////////////////////////////////////////////// -class VConVar : public IDetour -{ - virtual void GetAdr(void) const - { - LogConAdr("ConCommandBase::`vftable'", reinterpret_cast(g_pConCommandBaseVFTable)); - LogConAdr("ConCommand::`vftable'", reinterpret_cast(g_pConCommandVFTable)); - LogConAdr("ConVar::`vbtable'", reinterpret_cast(g_pConVarVBTable)); - LogConAdr("ConVar::`vftable'", reinterpret_cast(g_pConVarVFTable)); - LogFunAdr("ConVar::Register", p_ConVar_Register.GetPtr()); - LogFunAdr("ConVar::Unregister", p_ConVar_Unregister.GetPtr()); - LogFunAdr("ConVar::IsFlagSet", p_ConVar_IsFlagSet.GetPtr()); - } - virtual void GetFun(void) const - { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_ConVar_Register = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 30 F3 0F 10 44 24 ??"); - p_ConVar_Unregister = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 48 8B 59 58 48 8D 05 ?? ?? ?? ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_ConVar_Register = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 40 F3 0F 10 84 24 ?? ?? ?? ??"); - p_ConVar_Unregister = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 79 58"); -#endif - p_ConVar_IsFlagSet = g_GameDll.FindPatternSIMD("48 8B 41 48 85 50 38"); - - v_ConVar_IsFlagSet = p_ConVar_IsFlagSet.RCast(); - v_ConVar_Register = p_ConVar_Register.RCast(); - v_ConVar_Unregister = p_ConVar_Unregister.RCast(); - } - virtual void GetVar(void) const { } - virtual void GetCon(void) const - { - g_pConCommandBaseVFTable = g_GameDll.GetVirtualMethodTable(".?AVConCommandBase@@").RCast(); - g_pConCommandVFTable = g_GameDll.GetVirtualMethodTable(".?AVConCommand@@").RCast(); - g_pConVarVBTable = g_GameDll.GetVirtualMethodTable(".?AVConVar@@", 0).RCast(); - g_pConVarVFTable = g_GameDll.GetVirtualMethodTable(".?AVConVar@@", 1).RCast(); - } - virtual void Attach(void) const { } - virtual void Detach(void) const { } -}; -/////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- +// Called by the framework to register ConCommands with the ICVar +//----------------------------------------------------------------------------- +void ConVar_Register(int nCVarFlag = 0, IConCommandBaseAccessor* pAccessor = NULL); +void ConVar_Unregister(); #endif // CONVAR_H diff --git a/r5dev/public/tier1/cvar.h b/r5dev/public/tier1/cvar.h index 22d67a5e..c1eefe2d 100644 --- a/r5dev/public/tier1/cvar.h +++ b/r5dev/public/tier1/cvar.h @@ -18,6 +18,7 @@ #include "tier1/utlmap.h" #include "tier1/utlvector.h" #include "tier1/utlstring.h" +#include "tier1/utlbuffer.h" //----------------------------------------------------------------------------- // Forward declarations @@ -68,19 +69,19 @@ protected: friend class CCvarUtilities; private: - CUtlVector< FnChangeCallback_t > m_GlobalChangeCallbacks; - char pad0[30]; //!TODO: - int m_nNextDLLIdentifier; - ConCommandBase* m_pConCommandList; - CConCommandHash m_CommandHash; - CUtlVector m_Unknown; - char pad2[32]; - void* m_pCallbackStub; - void* m_pAllocFunc; - char pad3[16]; - CUtlVector< QueuedConVarSet_t > m_QueuedConVarSets; - bool m_bMaterialSystemThreadSetAllowed; + CUtlVector< FnChangeCallback_t > m_GlobalChangeCallbacks; + CUtlVector< IConsoleDisplayFunc* > m_DisplayFuncs; + int m_nNextDLLIdentifier; + + ConCommandBase* m_pConCommandList; + CConCommandHash m_CommandHash; + + // temporary console area so we can store prints before console display funs are installed + mutable CUtlBuffer m_TempConsoleBuffer; + CUtlVector< QueuedConVarSet_t > m_QueuedConVarSets; + bool m_bMaterialSystemThreadSetAllowed; }; +static_assert(sizeof(CCvar) == 360); extern CCvar* g_pCVar; /////////////////////////////////////////////////////////////////////////////// @@ -161,33 +162,36 @@ extern ConVarFlags g_ConVarFlags; bool ConVar_ParseFlagString(const char* pszFlags, int& nFlags, const char* pszConVarName = "<>"); void ConVar_PrintDescription(ConCommandBase* pVar); -inline CMemory p_ConVar_PrintDescription; -inline void* (*v_ConVar_PrintDescription)(ConCommandBase* pVar); +inline bool (*CCvar__Connect)(CCvar* thisptr, CreateInterfaceFn factory); +inline void (*CCvar__Disconnect)(CCvar* thisptr); + +inline void (*v_ConVar_PrintDescription)(ConCommandBase* pVar); /////////////////////////////////////////////////////////////////////////////// class VCVar : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("ConVar_PrintDescription", p_ConVar_PrintDescription.GetPtr()); - LogVarAdr("g_pCVar", reinterpret_cast(g_pCVar)); + LogFunAdr("CCvar::Connect", CCvar__Connect); + LogFunAdr("CCvar::Disconnect", CCvar__Disconnect); + LogFunAdr("ConVar_PrintDescription", v_ConVar_PrintDescription); + LogVarAdr("g_pCVar", g_pCVar); } virtual void GetFun(void) const { - p_ConVar_PrintDescription = g_GameDll.FindPatternSIMD("B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 01 48 89 9C 24 ?? ?? ?? ??"); - v_ConVar_PrintDescription = p_ConVar_PrintDescription.RCast(); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 0F 45 C8 FF 05 ?? ?? ?? ?? 48 89 0D ?? ?? ?? ??").GetPtr(CCvar__Connect); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 26 80 3D ?? ?? ?? ?? ?? 74 1D 48 8B 01 8B 15 ?? " + "?? ?? ?? FF 50 58 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? C6 05 ?? ?? ?? ?? ?? 48 C7 05 ?? ?? ?? ?? ?? ?? ?? ??").GetPtr(CCvar__Disconnect); + + g_GameDll.FindPatternSIMD("B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 01 48 89 9C 24 ?? ?? ?? ??").GetPtr(v_ConVar_PrintDescription); } virtual void GetVar(void) const { - g_pCVar = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 89 15") - .FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 40).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - - //g_pCVar = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 83 3D ?? ?? ?? ?? ?? 48 8B D9 74 09") // Actual CCvar, above is the vtable ptr. - //.FindPatternSelf("48 83 3D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x8).RCast(); + g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 85 C0 48 0F 45 C8 FF 05 ?? ?? ?? ?? 48 89 0D ?? ?? ?? ??") + .FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(3, 7).GetPtr(g_pCVar); } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier1/depthcounter.h b/r5dev/public/tier1/depthcounter.h new file mode 100644 index 00000000..269d5ff0 --- /dev/null +++ b/r5dev/public/tier1/depthcounter.h @@ -0,0 +1,31 @@ +//=============================================================================// +// +// Purpose: simple class that could be used to manage depths of nested elements +// +//=============================================================================// +#ifndef TIER1_DEPTHCOUNTER_H +#define TIER1_DEPTHCOUNTER_H + +template +class CDepthCounter +{ +public: + CDepthCounter(T& counter) : ref(counter) + { + ref++; + } + ~CDepthCounter() + { + ref--; + } + + T Get() + { + return ref; + } + +private: + T& ref; +}; + +#endif // TIER1_DEPTHCOUNTER_H diff --git a/r5dev/public/tier1/exprevaluator.h b/r5dev/public/tier1/exprevaluator.h new file mode 100644 index 00000000..89dffc69 --- /dev/null +++ b/r5dev/public/tier1/exprevaluator.h @@ -0,0 +1,74 @@ +//===== Copyright 1996-2006, Valve Corporation, All rights reserved. ======// +// +// Purpose: ExprSimplifier builds a binary tree from an infix expression (in the +// form of a character array). +// +//===========================================================================// + +#ifndef EXPREVALUATOR_H +#define EXPREVALUATOR_H + +#if defined( _WIN32 ) +#pragma once +#endif + +static const char OR_OP = '|'; +static const char AND_OP = '&'; +static const char NOT_OP = '!'; + +#define MAX_IDENTIFIER_LEN 128 +enum Kind {CONDITIONAL, NOT, LITERAL}; + +struct ExprNode +{ + ExprNode *left; // left sub-expression + ExprNode *right; // right sub-expression + Kind kind; // kind of node this is + union + { + char cond; // the conditional + bool value; // the value + } data; +}; + +typedef ExprNode *ExprTree; + +// callback to evaluate a $ during evaluation, return true or false +typedef bool (*GetSymbolProc_t)( const char *pKey ); +typedef void (*SyntaxErrorProc_t)( const char *pReason ); + +class CExpressionEvaluator +{ +public: + CExpressionEvaluator(); + ~CExpressionEvaluator(); + bool Evaluate( bool &result, const char *pInfixExpression, GetSymbolProc_t pGetSymbolProc = 0, SyntaxErrorProc_t pSyntaxErrorProc = 0 ); + +private: + CExpressionEvaluator( CExpressionEvaluator& ); // prevent copy constructor being used + + char GetNextToken( void ); + void FreeNode( ExprNode *pNode ); + ExprNode *AllocateNode( void ); + void FreeTree( ExprTree &node ); + bool IsConditional( bool &bCondition, const char token ); + bool IsNotOp( const char token ); + bool IsIdentifierOrConstant( const char token ); + bool MakeExprNode( ExprTree &tree, char token, Kind kind, ExprTree left, ExprTree right ); + bool MakeFactor( ExprTree &tree ); + bool MakeTerm( ExprTree &tree ); + bool MakeExpression( ExprTree &tree ); + bool BuildExpression( void ); + bool SimplifyNode( ExprTree &node ); + + ExprTree m_ExprTree; // Tree representation of the expression + char m_CurToken; // Current token read from the input expression + const char *m_pExpression; // Array of the expression characters + int m_CurPosition; // Current position in the input expression + char m_Identifier[MAX_IDENTIFIER_LEN]; // Stores the identifier string + GetSymbolProc_t m_pGetSymbolProc; + SyntaxErrorProc_t m_pSyntaxErrorProc; + bool m_bSetup; +}; + +#endif diff --git a/r5dev/public/tier1/fmtstr.h b/r5dev/public/tier1/fmtstr.h new file mode 100644 index 00000000..2ef3f43d --- /dev/null +++ b/r5dev/public/tier1/fmtstr.h @@ -0,0 +1,369 @@ +//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// +// +// Purpose: A simple class for performing safe and in-expression sprintf-style +// string formatting +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef FMTSTR_H +#define FMTSTR_H + +#include +#include +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "tier1/strtools.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +//============================================================================= + +// using macro to be compatible with GCC +#define FmtStrVSNPrintf( szBuf, nBufSize, bQuietTruncation, ppszFormat, nPrevLen, lastArg ) \ + do \ + { \ + ssize_t result; \ + va_list arg_ptr; \ + bool bTruncated = false; \ + static int scAsserted = 0; \ + \ + va_start(arg_ptr, lastArg); \ + result = V_vsnprintfRet( (szBuf), (nBufSize)-1, (*(ppszFormat)), arg_ptr, &bTruncated ); \ + va_end(arg_ptr); \ + \ + (szBuf)[(nBufSize)-1] = 0; \ + if ( bTruncated && !(bQuietTruncation) && scAsserted < 5 ) \ + { \ + Warning( eDLL_T::COMMON, "FmtStrVSNPrintf truncated to %zd without QUIET_TRUNCATION specified!\n", ( nBufSize ) ); \ + AssertMsg( 0, "FmtStrVSNPrintf truncated without QUIET_TRUNCATION specified!\n" ); \ + scAsserted++; \ + } \ + m_nLength = nPrevLen + result; \ + } \ + while (0) + +// using macro to be compatable with GCC +#define FmtStrVSNPrintfNoLengthFixup( szBuf, nBufSize, bQuietTruncation, ppszFormat, nPrevLen, lastArg ) \ + do \ + { \ + int result; \ + va_list arg_ptr; \ + bool bTruncated = false; \ + static int scAsserted = 0; \ + \ + va_start(arg_ptr, lastArg); \ + result = V_vsnprintfRet( (szBuf), (nBufSize)-1, (*(ppszFormat)), arg_ptr, &bTruncated ); \ + va_end(arg_ptr); \ + \ + (szBuf)[(nBufSize)-1] = 0; \ + if ( bTruncated && !(bQuietTruncation) && scAsserted < 5 ) \ + { \ + Warning( eDLL_T::COMMON, "FmtStrVSNPrintf truncated to %zd without QUIET_TRUNCATION specified!\n", ( nBufSize ) ); \ + AssertMsg( 0, "FmtStrVSNPrintf truncated without QUIET_TRUNCATION specified!\n" ); \ + scAsserted++; \ + } \ + } \ + while (0) + +//----------------------------------------------------------------------------- +// +// Purpose: String formatter with specified size +// + +template < ssize_t SIZE_BUF, bool QUIET_TRUNCATION = false > +class CFmtStrN +{ +public: + CFmtStrN() + { + InitQuietTruncation(); + m_szBuf[0] = 0; + m_nLength = 0; + } + + // Standard C formatting + CFmtStrN(PRINTF_FORMAT_STRING const char *pszFormat, ...) FMTFUNCTION( 2, 3 ) + { + InitQuietTruncation(); + FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat ); + } + + // Use this for pass-through formatting + CFmtStrN(const char ** ppszFormat, ...) + { + InitQuietTruncation(); + FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, ppszFormat, 0, ppszFormat ); + } + + // Explicit reformat + const char *sprintf(PRINTF_FORMAT_STRING const char *pszFormat, ...) FMTFUNCTION( 2, 3 ) + { + InitQuietTruncation(); + FmtStrVSNPrintf(m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat ); + return m_szBuf; + } + + // Same as sprintf above, but for compatibility with Steam interface + const char *Format( PRINTF_FORMAT_STRING const char *pszFormat, ... ) FMTFUNCTION( 2, 3 ) + { + InitQuietTruncation(); + FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, &pszFormat, 0, pszFormat ); + return m_szBuf; + } + + // Use this for va_list formatting + const char *sprintf_argv(const char *pszFormat, va_list arg_ptr) + { + int result; + bool bTruncated = false; + static int s_nWarned = 0; + + InitQuietTruncation(); + result = V_vsnprintfRet( m_szBuf, SIZE_BUF - 1, pszFormat, arg_ptr, &bTruncated ); + m_szBuf[SIZE_BUF - 1] = 0; + if ( bTruncated && !m_bQuietTruncation && ( s_nWarned < 5 ) ) + { + Warning( eDLL_T::COMMON, "CFmtStr truncated to %zd without QUIET_TRUNCATION specified!\n", SIZE_BUF ); + AssertMsg( 0, "CFmtStr truncated without QUIET_TRUNCATION specified!\n" ); + s_nWarned++; + } + m_nLength = V_strlen( m_szBuf ); + return m_szBuf; + } + + // Use this for pass-through formatting + void VSprintf(const char **ppszFormat, ...) + { + InitQuietTruncation(); + FmtStrVSNPrintf( m_szBuf, SIZE_BUF, m_bQuietTruncation, ppszFormat, 0, ppszFormat ); + } + + // Compatible API with CUtlString for converting to const char* + const char *Get( ) const { return m_szBuf; } + const char *String( ) const { return m_szBuf; } + // Use for access + operator const char *() const { return m_szBuf; } + char *Access() { return m_szBuf; } + + // Access template argument + static inline ssize_t GetMaxLength() { return SIZE_BUF-1; } + + CFmtStrN & operator=( const char *pchValue ) + { + V_strncpy( m_szBuf, pchValue, SIZE_BUF ); + m_nLength = V_strlen( m_szBuf ); + return *this; + } + + CFmtStrN & operator+=( const char *pchValue ) + { + Append( pchValue ); + return *this; + } + + ssize_t Length() const { return m_nLength; } + + void SetLength( ssize_t nLength ) + { + m_nLength = Min( nLength, SIZE_BUF - 1 ); + m_szBuf[m_nLength] = '\0'; + } + + void Clear() + { + m_szBuf[0] = 0; + m_nLength = 0; + } + + void AppendFormat( PRINTF_FORMAT_STRING const char *pchFormat, ... ) FMTFUNCTION( 2, 3 ) + { + char *pchEnd = m_szBuf + m_nLength; + FmtStrVSNPrintf( pchEnd, SIZE_BUF - m_nLength, m_bQuietTruncation, &pchFormat, m_nLength, pchFormat ); + } + + void AppendFormatV( const char *pchFormat, va_list args ); + + void Append( const char *pchValue ) + { + // This function is close to the metal to cut down on the CPU cost + // of the previous incantation of Append which was implemented as + // AppendFormat( "%s", pchValue ). This implementation, though not + // as easy to read, instead does a strcpy from the existing end + // point of the CFmtStrN. This brings something like a 10-20x speedup + // in my rudimentary tests. It isn't using V_strncpy because that + // function doesn't return the number of characters copied, which + // we need to adjust m_nLength. Doing the V_strncpy with a V_strlen + // afterwards took twice as long as this implementations in tests, + // so V_strncpy's implementation was used to write this method. + char *pDest = m_szBuf + m_nLength; + const ssize_t maxLen = SIZE_BUF - m_nLength; + char *pLast = pDest + maxLen - 1; + while ( (pDest < pLast) && (*pchValue != 0) ) + { + *pDest = *pchValue; + ++pDest; ++pchValue; + } + *pDest = 0; + m_nLength = pDest - m_szBuf; + } + + //optimized version of append for just adding a single character + void Append( char ch ) + { + if( m_nLength < SIZE_BUF - 1 ) + { + m_szBuf[ m_nLength ] = ch; + m_nLength++; + m_szBuf[ m_nLength ] = '\0'; + } + } + + void AppendIndent( uint64 unCount, char chIndent = '\t' ); + + void SetQuietTruncation( bool bQuiet ) { m_bQuietTruncation = bQuiet; } + +protected: + virtual void InitQuietTruncation() + { + m_bQuietTruncation = QUIET_TRUNCATION; + } + + bool m_bQuietTruncation; + +private: + char m_szBuf[SIZE_BUF]; + ssize_t m_nLength; +}; + + +// Version which will not assert if strings are truncated + +template < ssize_t SIZE_BUF > +class CFmtStrQuietTruncationN : public CFmtStrN +{ +}; + + +template< ssize_t SIZE_BUF, bool QUIET_TRUNCATION > +void CFmtStrN< SIZE_BUF, QUIET_TRUNCATION >::AppendIndent( uint64 unCount, char chIndent ) +{ + Assert( Length() + unCount < SIZE_BUF ); + if( Length() + unCount >= SIZE_BUF ) + unCount = SIZE_BUF - (1+Length()); + for ( uint64 x = 0; x < unCount; x++ ) + { + m_szBuf[ m_nLength++ ] = chIndent; + } + m_szBuf[ m_nLength ] = '\0'; +} + +template< ssize_t SIZE_BUF, bool QUIET_TRUNCATION > +void CFmtStrN< SIZE_BUF, QUIET_TRUNCATION >::AppendFormatV( const char *pchFormat, va_list args ) +{ + int cubPrinted = V_vsnprintf( m_szBuf+Length(), SIZE_BUF - Length(), pchFormat, args ); + m_nLength += cubPrinted; +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Default-sized string formatter +// + +#define FMTSTR_STD_LEN 256 + +typedef CFmtStrN CFmtStr; +typedef CFmtStrQuietTruncationN CFmtStrQuietTruncation; +typedef CFmtStrN<32> CFmtStr32; +typedef CFmtStrN<1024> CFmtStr1024; +typedef CFmtStrN<2048> CFmtStr2048; +typedef CFmtStrN<8192> CFmtStrMax; + + +//----------------------------------------------------------------------------- +// Purpose: Fast-path number-to-string helper (with optional quoting) +// Derived off of the Steam CNumStr but with a few tweaks, such as +// trimming off the in-our-cases-unnecessary strlen calls (by not +// storing the length in the class). +//----------------------------------------------------------------------------- + +class CNumStr +{ +public: + CNumStr() { m_szBuf[0] = 0; } + + explicit CNumStr( bool b ) { SetBool( b ); } + + explicit CNumStr( int8 n8 ) { SetInt8( n8 ); } + explicit CNumStr( uint8 un8 ) { SetUint8( un8 ); } + explicit CNumStr( int16 n16 ) { SetInt16( n16 ); } + explicit CNumStr( uint16 un16 ) { SetUint16( un16 ); } + explicit CNumStr( int32 n32 ) { SetInt32( n32 ); } + explicit CNumStr( uint32 un32 ) { SetUint32( un32 ); } + explicit CNumStr( int64 n64 ) { SetInt64( n64 ); } + explicit CNumStr( uint64 un64 ) { SetUint64( un64 ); } + +#if defined(COMPILER_GCC) && defined(PLATFORM_64BITS) + explicit CNumStr( lint64 n64 ) { SetInt64( (int64)n64 ); } + explicit CNumStr( ulint64 un64 ) { SetUint64( (uint64)un64 ); } +#endif + + explicit CNumStr( double f ) { SetDouble( f ); } + explicit CNumStr( float f ) { SetFloat( f ); } + + inline void SetBool( bool b ) { memcpy( m_szBuf, b ? "1" : "0", 2 ); } + +#ifdef _WIN32 + inline void SetInt8( int8 n8 ) { _itoa( (int32)n8, m_szBuf, 10 ); } + inline void SetUint8( uint8 un8 ) { _itoa( (int32)un8, m_szBuf, 10 ); } + inline void SetInt16( int16 n16 ) { _itoa( (int32)n16, m_szBuf, 10 ); } + inline void SetUint16( uint16 un16 ) { _itoa( (int32)un16, m_szBuf, 10 ); } + inline void SetInt32( int32 n32 ) { _itoa( n32, m_szBuf, 10 ); } + inline void SetUint32( uint32 un32 ) { _i64toa( (int64)un32, m_szBuf, 10 ); } + inline void SetInt64( int64 n64 ) { _i64toa( n64, m_szBuf, 10 ); } + inline void SetUint64( uint64 un64 ) { _ui64toa( un64, m_szBuf, 10 ); } +#else + inline void SetInt8( int8 n8 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)n8 ); } + inline void SetUint8( uint8 un8 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)un8 ); } + inline void SetInt16( int16 n16 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)n16 ); } + inline void SetUint16( uint16 un16 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", (int32)un16 ); } + inline void SetInt32( int32 n32 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%d", n32 ); } + inline void SetUint32( uint32 un32 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%u", un32 ); } + inline void SetInt64( int64 n64 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%lld", n64 ); } + inline void SetUint64( uint64 un64 ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%llu", un64 ); } +#endif + + inline void SetDouble( double f ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%.18g", f ); } + inline void SetFloat( float f ) { Q_snprintf( m_szBuf, sizeof(m_szBuf), "%.18g", f ); } + + inline void SetHexUint64( uint64 un64 ) { V_binarytohex( (byte *)&un64, sizeof( un64 ), m_szBuf, sizeof( m_szBuf ) ); } + + operator const char *() const { return m_szBuf; } + const char* String() const { return m_szBuf; } + + void AddQuotes() + { + Assert( m_szBuf[0] != '"' ); + const size_t nLength = Q_strlen( m_szBuf ); + memmove( m_szBuf + 1, m_szBuf, nLength ); + m_szBuf[0] = '"'; + m_szBuf[nLength + 1] = '"'; + m_szBuf[nLength + 2] = 0; + } + +protected: + char m_szBuf[28]; // long enough to hold 18 digits of precision, a decimal, a - sign, e+### suffix, and quotes + +}; + + +//============================================================================= + +const int k_cchFormattedDate = 64; +const int k_cchFormattedTime = 32; +bool BGetLocalFormattedTime( time_t timeVal, char *pchDate, int cubDate, char *pchTime, int cubTime ); + +#endif // FMTSTR_H diff --git a/r5dev/public/tier1/interface.h b/r5dev/public/tier1/interface.h index 91418523..70fe4359 100644 --- a/r5dev/public/tier1/interface.h +++ b/r5dev/public/tier1/interface.h @@ -14,26 +14,53 @@ typedef void* (*CreateInterfaceFn)(const char* pName, int* pReturnCode); typedef void* (*InstantiateInterfaceFn)(); typedef HINSTANCE CSysModule; -struct InterfaceGlobals_t +class InterfaceReg { - InstantiateInterfaceFn m_pInterfacePtr; - const char* m_pInterfaceName; - InterfaceGlobals_t* m_pNextInterfacePtr; +public: + InterfaceReg(InstantiateInterfaceFn fn, const char* pName); + +public: + InstantiateInterfaceFn m_CreateFn; + const char* m_pName; + InterfaceReg* m_pNext; }; +extern InterfaceReg** s_ppInterfaceRegs; + //----------------------------------------------------------------------------- +// Use this to expose an interface that can have multiple instances. +// e.g.: +// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) +// This will expose a class called CInterfaceImp that implements IInterface (a pure class) +// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) +// +// In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") +// so that each component can use these names/vtables to communicate +// +// A single class can support multiple interfaces through multiple inheritance +// +// Use this if you want to write the factory function. +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ + { \ + static InterfaceReg __g_Create##interfaceName##_reg(functionName, versionName); \ + } +#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ + { \ + static void* __Create##className##_interface() {return static_cast( new className );} \ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); \ + } -struct FactoryInfo_t -{ - CMemory m_pFactoryPtr; - string m_szFactoryFullName; - string m_szFactoryName; - string m_szFactoryVersion; +// Use this to expose a singleton interface with a global variable you've created. +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ + { \ + static void* __Create##className##interfaceName##_interface() {return static_cast( &globalVarName );} \ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); \ + } - FactoryInfo_t() : m_pFactoryPtr(nullptr) {} - FactoryInfo_t(const uintptr_t factoryPtr, const string& factoryFullName, const string& factoryName, const string& factoryVersion) : - m_pFactoryPtr(factoryPtr), m_szFactoryFullName(factoryFullName), m_szFactoryName(factoryName), m_szFactoryVersion(factoryVersion) {} - FactoryInfo_t(const uintptr_t factoryPtr, const string& factoryFullName) : - m_pFactoryPtr(factoryPtr), m_szFactoryFullName(factoryFullName) {} -}; +// Use this to expose a singleton interface. This creates the global variable for you automatically. +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ + { \ + static className __g_##className##_singleton; \ + } \ + EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) #endif // INTERFACE_H diff --git a/r5dev/vpc/keyvalues.h b/r5dev/public/tier1/keyvalues.h similarity index 50% rename from r5dev/vpc/keyvalues.h rename to r5dev/public/tier1/keyvalues.h index a86001e2..aed290eb 100644 --- a/r5dev/vpc/keyvalues.h +++ b/r5dev/public/tier1/keyvalues.h @@ -1,16 +1,14 @@ #pragma once #include "mathlib/color.h" #include "tier1/utlbuffer.h" +#include "tier1/exprevaluator.h" #include "public/ifilesystem.h" -#define MAKE_3_BYTES_FROM_1_AND_2( x1, x2 ) (( (( uint16_t )x2) << 8 ) | (uint8_t)(x1)) -#define SPLIT_3_BYTES_INTO_1_AND_2( x1, x2, x3 ) do { x1 = (uint8)(x3); x2 = (uint16)( (x3) >> 8 ); } while( 0 ) +#define KEYVALUES_TOKEN_SIZE (1024 * 32) -extern vector g_vAllPlaylists; -extern vector g_vGameInfoPaths; - -extern std::mutex g_InstalledMapsMutex; -extern std::mutex g_PlaylistsVecMutex; +// single byte identifies a xbox kv file in binary format +// strings are pooled from a searchpath/zip mounted symbol table +#define KV_BINARY_POOLED_FORMAT 0xAA //--------------------------------------------------------------------------------- // Purpose: Forward declarations @@ -18,28 +16,7 @@ extern std::mutex g_PlaylistsVecMutex; class KeyValues; class CFileSystem_Stdio; class IBaseFileSystem; - -/* ==== KEYVALUES ======================================================================================================================================================= */ -inline CMemory p_KeyValues_FindKey; -inline void*(*KeyValues_FindKey)(KeyValues* thisptr, const char* pkeyName, bool bCreate); - -inline CMemory p_KeyValues_LoadPlaylists; -inline bool(*KeyValues_LoadPlaylists)(const char* pszPlaylist); - -inline CMemory p_KeyValues_ParsePlaylists; -inline bool(*KeyValues_ParsePlaylists)(const char* pszPlaylist); - -inline CMemory p_KeyValues_GetCurrentPlaylist; -inline const char* (*KeyValues_GetCurrentPlaylist)(void); - -inline CMemory p_KeyValues_ReadKeyValuesFile; -inline KeyValues*(*KeyValues_ReadKeyValuesFile)(CFileSystem_Stdio* pFileSystem, const char* pFileName); - -inline CMemory p_KeyValues_RecursiveSaveToFile; -inline void(*KeyValues_RecursiveSaveToFile)(KeyValues* thisptr, IBaseFileSystem* pFileSystem, FileHandle_t pHandle, CUtlBuffer* pBuf, int nIndentLevel); - -inline CMemory p_KeyValues_LoadFromFile; -inline KeyValues*(*KeyValues_LoadFromFile)(KeyValues* thisptr, IBaseFileSystem* pFileSystem, const char* pszResourceName, const char* pszPathID, void* pfnEvaluateSymbolProc); +class CKeyValuesTokenReader; enum KeyValuesTypes_t : char { @@ -64,6 +41,14 @@ enum MergeKeyValuesOp_t MERGE_KV_BORROW, // update values only update existing keys in storage, keys in update that do not exist in storage are discarded }; +#define FOR_EACH_SUBKEY( kvRoot, kvSubKey ) \ + for ( KeyValues * kvSubKey = kvRoot->GetFirstSubKey(); kvSubKey != NULL; kvSubKey = kvSubKey->GetNextKey() ) + +#define FOR_EACH_TRUE_SUBKEY( kvRoot, kvSubKey ) \ + for ( KeyValues * kvSubKey = kvRoot->GetFirstTrueSubKey(); kvSubKey != NULL; kvSubKey = kvSubKey->GetNextTrueSubKey() ) + +#define FOR_EACH_VALUE( kvRoot, kvValue ) \ + for ( KeyValues * kvValue = kvRoot->GetFirstValue(); kvValue != NULL; kvValue = kvValue->GetNextValue() ) //----------------------------------------------------------------------------- // Purpose: Simple recursive data access class @@ -103,6 +88,7 @@ public: void DeleteThis(void); void RemoveEverything(); + KeyValues* FindKey(int keySymbol) const; KeyValues* FindKey(const char* pKeyName, bool bCreate = false); KeyValues* FindLastSubKey(void) const; @@ -122,6 +108,8 @@ public: KeyValues* GetFirstSubKey() const; KeyValues* GetNextKey() const; const char* GetName(void) const; + int GetNameSymbol() const; + int GetNameSymbolCaseSensitive() const; int GetInt(const char* pszKeyName, int iDefaultValue); uint64_t GetUint64(const char* pszKeyName, uint64_t nDefaultValue); void* GetPtr(const char* pszKeyName, void* pDefaultValue); @@ -147,20 +135,41 @@ public: void SetBool(const char* pszKeyName, bool bValue) { SetInt(pszKeyName, bValue ? 1 : 0); } void UsesEscapeSequences(bool bState); - void RecursiveCopyKeyValues(KeyValues& src); void RecursiveSaveToFile(CUtlBuffer& buf, int nIndentLevel); - void RecursiveSaveToFile(IBaseFileSystem* pFileSystem, FileHandle_t pHandle, CUtlBuffer* pBuf, int nIndentLevel); - KeyValues* LoadFromFile(IBaseFileSystem* pFileSystem, const char* pszResourceName, const char* pszPathID, void* pfnEvaluateSymbolProc); + + bool LoadFromFile(IBaseFileSystem* filesystem, const char* resourceName, const char* pathID, GetSymbolProc_t pfnEvaluateSymbolProc = nullptr); + + bool LoadFromBuffer(char const* resourceName, CUtlBuffer& buf, IBaseFileSystem* pFileSystem, const char* pPathID, GetSymbolProc_t pfnEvaluateSymbolProc = nullptr); + bool LoadFromBuffer(char const* resourceName, const char* pBuffer, IBaseFileSystem* pFileSystem, const char* pPathID, GetSymbolProc_t pfnEvaluateSymbolProc = nullptr); + + // for handling #include "filename" + void AppendIncludedKeys(CUtlVector< KeyValues* >& includedKeys); + void ParseIncludedKeys(char const* resourceName, const char* filetoinclude, + IBaseFileSystem* pFileSystem, const char* pPathID, CUtlVector< KeyValues* >& includedKeys, GetSymbolProc_t pfnEvaluateSymbolProc); + + // For handling #base "filename" + void MergeBaseKeys(CUtlVector< KeyValues* >& baseKeys); + void RecursiveMergeKeyValues(KeyValues* baseKV); void CopySubkeys(KeyValues* pParent) const; KeyValues* MakeCopy(void) const; - // Initialization - static void InitPlaylists(void); - static void InitFileSystem(void); - static bool LoadPlaylists(const char* szPlaylist); - static bool ParsePlaylists(const char* szPlaylist); - static KeyValues* ReadKeyValuesFile(CFileSystem_Stdio* pFileSystem, const char* pFileName); + KeyValues* CreateKeyUsingKnownLastChild(const char* keyName, KeyValues* pLastChild); + void AddSubkeyUsingKnownLastChild(KeyValues* pSubKey, KeyValues* pLastChild); + +private: + void RecursiveSaveToFile(IBaseFileSystem* pFileSystem, FileHandle_t pHandle, CUtlBuffer* pBuf, int nIndentLevel); + void RecursiveLoadFromBuffer(char const* resourceName, CKeyValuesTokenReader& tokenReader, GetSymbolProc_t pfnEvaluateSymbolProc); + + void RecursiveCopyKeyValues(KeyValues& src); + + // NOTE: If both filesystem and pBuf are non-null, it'll save to both of them. + // If filesystem is null, it'll ignore f. + void InternalWrite(IBaseFileSystem* filesystem, FileHandle_t f, CUtlBuffer* pBuf, const void* pData, ssize_t len); + void WriteIndents(IBaseFileSystem* filesystem, FileHandle_t f, CUtlBuffer* pBuf, int indentLevel); + void WriteConvertedString(IBaseFileSystem* pFileSystem, FileHandle_t pHandle, CUtlBuffer* pBuf, const char* pszString); + + bool EvaluateConditional(const char* pExpressionString, GetSymbolProc_t pfnEvaluateSymbolProc); public: uint32_t m_iKeyName : 24;// 0x0000 @@ -182,61 +191,3 @@ public: KeyValues* m_pSub; // 0x0038 KeyValues* m_pChain; // 0x0040 }; - -/////////////////////////////////////////////////////////////////////////////// -extern KeyValues** g_pPlaylistKeyValues; - -/////////////////////////////////////////////////////////////////////////////// -class VKeyValues : public IDetour -{ - virtual void GetAdr(void) const - { - LogFunAdr("KeyValues::FindKey", p_KeyValues_FindKey.GetPtr()); - LogFunAdr("KeyValues::LoadPlaylists", p_KeyValues_LoadPlaylists.GetPtr()); - LogFunAdr("KeyValues::ParsePlaylists", p_KeyValues_ParsePlaylists.GetPtr()); - LogFunAdr("KeyValues::GetCurrentPlaylist", p_KeyValues_GetCurrentPlaylist.GetPtr()); - LogFunAdr("KeyValues::ReadKeyValuesFile", p_KeyValues_ReadKeyValuesFile.GetPtr()); - LogFunAdr("KeyValues::RecursiveSaveToFile", p_KeyValues_RecursiveSaveToFile.GetPtr()); - LogFunAdr("KeyValues::LoadFromFile", p_KeyValues_LoadFromFile.GetPtr()); - LogVarAdr("g_pPlaylistKeyValues", reinterpret_cast(g_pPlaylistKeyValues)); - } - virtual void GetFun(void) const - { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_KeyValues_FindKey = g_GameDll.FindPatternSIMD("48 89 5C 24 10 48 89 6C 24 18 48 89 74 24 20 57 41 54 41 55 41 56 41 57 48 81 EC 20 01 ?? ?? 45"); - p_KeyValues_GetCurrentPlaylist = g_GameDll.FindPatternSIMD("48 8B 0D ?? ?? ?? ?? 48 85 C9 75 08 48 8D 05 ?? ?? ?? ??"); - p_KeyValues_ReadKeyValuesFile = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 57 41 54 41 57 48 8D 6C 24 ??"); - p_KeyValues_LoadFromFile = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 4C 89 4C 24 ?? 4C 89 44 24 ?? 48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ??"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_KeyValues_FindKey = g_GameDll.FindPatternSIMD("40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45"); - p_KeyValues_GetCurrentPlaylist = g_GameDll.FindPatternSIMD("48 8B 05 ?? ?? ?? ?? 48 85 C0 75 08 48 8D 05 ?? ?? ?? ?? C3 0F B7 50 2A"); - p_KeyValues_ReadKeyValuesFile = g_GameDll.FindPatternSIMD("48 8B C4 55 53 57 41 54 48 8D 68 A1"); - p_KeyValues_LoadFromFile = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 4C 89 4C 24 ?? 48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ??"); -#endif - p_KeyValues_LoadPlaylists = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 40 48 8B F1"); - p_KeyValues_ParsePlaylists = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 0C").FollowNearCallSelf(); - p_KeyValues_RecursiveSaveToFile = g_GameDll.FindPatternSIMD("48 8B C4 53 ?? 57 41 55 41 ?? 48 83"); - - KeyValues_FindKey = p_KeyValues_FindKey.RCast(); /*40 56 57 41 57 48 81 EC 30 01 00 00 45 0F B6 F8*/ - KeyValues_LoadPlaylists = p_KeyValues_ParsePlaylists.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 40 48 8B F1*/ - KeyValues_ParsePlaylists = p_KeyValues_ParsePlaylists.RCast(); /*E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 0C*/ - KeyValues_GetCurrentPlaylist = p_KeyValues_GetCurrentPlaylist.RCast(); /*48 8B 05 ?? ?? ?? ?? 48 85 C0 75 08 48 8D 05 ?? ?? ?? ?? C3 0F B7 50 2A*/ - KeyValues_ReadKeyValuesFile = p_KeyValues_ReadKeyValuesFile.RCast(); /*48 8B C4 55 53 57 41 54 48 8D 68 A1*/ - KeyValues_RecursiveSaveToFile = p_KeyValues_RecursiveSaveToFile.RCast(); /*48 8B C4 53 ?? 57 41 55 41 ?? 48 83*/ - KeyValues_LoadFromFile = p_KeyValues_LoadFromFile.RCast(); - } - virtual void GetVar(void) const - { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - g_pPlaylistKeyValues = g_GameDll.FindPatternSIMD("48 8B C4 53 57 41 56 48 81 EC 20") - .FindPatternSelf("48 8B 2D", CMemory::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - g_pPlaylistKeyValues = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 E8 B4") - .FindPatternSelf("48 8B 0D", CMemory::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); -#endif - } - virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; -}; -/////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier1/keyvalues_iface.h b/r5dev/public/tier1/keyvalues_iface.h new file mode 100644 index 00000000..a89a86d8 --- /dev/null +++ b/r5dev/public/tier1/keyvalues_iface.h @@ -0,0 +1,42 @@ +#pragma once +#include "tier1/keyvalues.h" + +//--------------------------------------------------------------------------------- +// Purpose: Forward declarations +//--------------------------------------------------------------------------------- +class KeyValues; +class CFileSystem_Stdio; +class IBaseFileSystem; +class CKeyValuesTokenReader; + +/* ==== KEYVALUES ======================================================================================================================================================= */ +inline void*(*KeyValues__FindKey)(KeyValues* thisptr, const char* pkeyName, bool bCreate); +inline KeyValues*(*KeyValues__ReadKeyValuesFile)(CFileSystem_Stdio* pFileSystem, const char* pFileName); +inline void(*KeyValues__RecursiveSaveToFile)(KeyValues* thisptr, IBaseFileSystem* pFileSystem, FileHandle_t pHandle, CUtlBuffer* pBuf, int nIndentLevel); +inline KeyValues*(*KeyValues__LoadFromFile)(KeyValues* thisptr, IBaseFileSystem* pFileSystem, const char* pszResourceName, const char* pszPathID, void* pfnEvaluateSymbolProc); + +/////////////////////////////////////////////////////////////////////////////// +extern KeyValues** g_pPlaylistKeyValues; + +/////////////////////////////////////////////////////////////////////////////// +class VKeyValues : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("KeyValues::FindKey", KeyValues__FindKey); + LogFunAdr("KeyValues::ReadKeyValuesFile", KeyValues__ReadKeyValuesFile); + LogFunAdr("KeyValues::RecursiveSaveToFile", KeyValues__RecursiveSaveToFile); + LogFunAdr("KeyValues::LoadFromFile", KeyValues__LoadFromFile); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("40 56 57 41 57 48 81 EC ?? ?? ?? ?? 45").GetPtr(KeyValues__FindKey); + g_GameDll.FindPatternSIMD("48 8B C4 55 53 57 41 54 48 8D 68 A1").GetPtr(KeyValues__ReadKeyValuesFile); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 4C 89 4C 24 ?? 48 89 4C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 8D 6C 24 ??").GetPtr(KeyValues__LoadFromFile); + g_GameDll.FindPatternSIMD("48 8B C4 53 ?? 57 41 55 41 ?? 48 83").GetPtr(KeyValues__RecursiveSaveToFile); + } + virtual void GetVar(void) const { } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const; +}; +/////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier1/lzss.h b/r5dev/public/tier1/lzss.h index 86fbabeb..47f5adeb 100644 --- a/r5dev/public/tier1/lzss.h +++ b/r5dev/public/tier1/lzss.h @@ -65,6 +65,7 @@ private: FORCEINLINE CLZSS::CLZSS( int nWindowSize ) { + Assert( IsPowerOfTwo( nWindowSize ) ); m_nWindowSize = nWindowSize; } #endif diff --git a/r5dev/public/tier1/mempool.h b/r5dev/public/tier1/mempool.h index e8e2a94f..ae93f759 100644 --- a/r5dev/public/tier1/mempool.h +++ b/r5dev/public/tier1/mempool.h @@ -14,22 +14,644 @@ #ifndef MEMPOOL_H #define MEMPOOL_H -struct CUtlMemoryPool +#ifdef _WIN32 +#pragma once +#endif + +#include "tier0/memalloc.h" +#include "tier0/tslist.h" +#include "tier0/platform.h" +#include "tier1/utlvector.h" +#include "tier1/utlrbtree.h" + +//----------------------------------------------------------------------------- +// Purpose: Optimized pool memory allocator +//----------------------------------------------------------------------------- + +typedef void (*MemoryPoolReportFunc_t)(PRINTF_FORMAT_STRING char const* pMsg, ...); + +class CUtlMemoryPool { +public: + // Ways the memory pool can grow when it needs to make a new blob. + enum MemoryPoolGrowType_t + { + GROW_NONE = 0, // Don't allow new blobs. + GROW_FAST = 1, // New blob size is numElements * (i+1) (ie: the blocks it allocates + // get larger and larger each time it allocates one). + GROW_SLOW = 2 // New blob size is numElements. + }; + + CUtlMemoryPool(int blockSize, int numElements, int growMode = GROW_FAST, const char* pszAllocOwner = NULL, unsigned short nAlignment = 0); + ~CUtlMemoryPool(); + + void* Alloc(); // Allocate the element size you specified in the constructor. + void* Alloc(size_t amount); + void* AllocZero(); // Allocate the element size you specified in the constructor, zero the memory before construction + void* AllocZero(size_t amount); + void Free(void* pMem); + + // Frees everything + void Clear(); + + // Error reporting... + static void SetErrorReportFunc(MemoryPoolReportFunc_t func); + + // returns number of allocated blocks + int Count() const { return m_BlocksAllocated; } + int PeakCount() const { return m_PeakAlloc; } + int BlockSize() const { return m_BlockSize; } + int Size() const; + + bool IsAllocationWithinPool(void* pMem) const; + +protected: class CBlob { public: - short m_nAlignment; // to int align the struct. - short m_NumBlobs; // Number of blobs. - const char* m_pszAllocOwner; - CBlob* m_pPrev, * m_pNext; + CBlob* m_pPrev; + int m_NumBytes; // Number of bytes in this blob. + char m_Data[1]; + char m_Padding[3]; // to int align the struct }; - int m_BlockSize; - int m_BlocksPerBlob; - int m_GrowMode; - int m_BlocksAllocated; - CBlob m_BlobHead; + // Resets the pool + void Init(); + void AddNewBlob(); + void ReportLeaks(); + + int m_BlockSize; + int m_BlocksPerBlob; + + int m_GrowMode; // GROW_ enum. + + int m_BlocksAllocated; + int m_PeakAlloc; + unsigned short m_nAlignment; + unsigned short m_NumBlobs; + + const char* m_pszAllocOwner; + + CUtlMemoryPool::CBlob* m_pPrev; + CUtlMemoryPool::CBlob* m_pNext; + + static MemoryPoolReportFunc_t g_ReportFunc; }; -#endif // MEMPOOL_H \ No newline at end of file + +//----------------------------------------------------------------------------- +// Multi-thread/Thread Safe Memory Class +//----------------------------------------------------------------------------- +class CMemoryPoolMT : public CUtlMemoryPool +{ +public: + CMemoryPoolMT(int blockSize, int numElements, int growMode = GROW_FAST, const char* pszAllocOwner = NULL, unsigned short nAlignment = 0) : CUtlMemoryPool(blockSize, numElements, growMode, pszAllocOwner, nAlignment) {} + + + void* Alloc() { AUTO_LOCK(m_mutex); return CUtlMemoryPool::Alloc(); } + void* Alloc(size_t amount) { AUTO_LOCK(m_mutex); return CUtlMemoryPool::Alloc(amount); } + void* AllocZero() { AUTO_LOCK(m_mutex); return CUtlMemoryPool::AllocZero(); } + void* AllocZero(size_t amount) { AUTO_LOCK(m_mutex); return CUtlMemoryPool::AllocZero(amount); } + void Free(void* pMem) { AUTO_LOCK(m_mutex); CUtlMemoryPool::Free(pMem); } + + // Frees everything + void Clear() { AUTO_LOCK(m_mutex); return CUtlMemoryPool::Clear(); } +private: + CThreadFastMutex m_mutex; // @TODO: Rework to use tslist (toml 7/6/2007) +}; + + +//----------------------------------------------------------------------------- +// Wrapper macro to make an allocator that returns particular typed allocations +// and construction and destruction of objects. +//----------------------------------------------------------------------------- +template< class T > +class CClassMemoryPool : public CUtlMemoryPool +{ +public: + CClassMemoryPool(int numElements, int growMode = GROW_FAST, int nAlignment = 0) : + CUtlMemoryPool(sizeof(T), numElements, growMode, MEM_ALLOC_CLASSNAME(T), nAlignment) {} + + T* Alloc(); + T* AllocZero(); + void Free(T* pMem); + + void Clear(); +}; + +//----------------------------------------------------------------------------- +// Specialized pool for aligned data management (e.g., Xbox textures) +//----------------------------------------------------------------------------- +template +class CAlignedMemPool +{ + enum + { + BLOCK_SIZE = COMPILETIME_MAX(ALIGN_VALUE(ITEM_SIZE, ALIGNMENT), 8), + }; + +public: + CAlignedMemPool(); + + void* Alloc(); + void Free(void* p); + + static int __cdecl CompareChunk(void* const* ppLeft, void* const* ppRight); + void Compact(); + + int NumTotal() { AUTO_LOCK(m_mutex); return m_Chunks.Count() * (CHUNK_SIZE / BLOCK_SIZE); } + int NumAllocated() { AUTO_LOCK(m_mutex); return NumTotal() - m_nFree; } + int NumFree() { AUTO_LOCK(m_mutex); return m_nFree; } + + int BytesTotal() { AUTO_LOCK(m_mutex); return NumTotal() * BLOCK_SIZE; } + int BytesAllocated() { AUTO_LOCK(m_mutex); return NumAllocated() * BLOCK_SIZE; } + int BytesFree() { AUTO_LOCK(m_mutex); return NumFree() * BLOCK_SIZE; } + + int ItemSize() { return ITEM_SIZE; } + int BlockSize() { return BLOCK_SIZE; } + int ChunkSize() { return CHUNK_SIZE; } + +private: + struct FreeBlock_t + { + FreeBlock_t* pNext; + byte reserved[BLOCK_SIZE - sizeof(FreeBlock_t*)]; + }; + + CUtlVector m_Chunks; // Chunks are tracked outside blocks (unlike CUtlMemoryPool) to simplify alignment issues + FreeBlock_t* m_pFirstFree; + int m_nFree; + CAllocator m_Allocator; + double m_TimeLastCompact; + + CThreadFastMutex m_mutex; +}; + +//----------------------------------------------------------------------------- +// Pool variant using standard allocation +// TODO[ AMOS ]: commented from here as CTSList isn't implemented yet; +// structure and the size thereof are unknown. +//----------------------------------------------------------------------------- +//template +//class CObjectPool +//{ +//public: +// CObjectPool() +// { +// int i = nInitialCount; +// while (i-- > 0) +// { +// m_AvailableObjects.PushItem(new T); +// } +// } +// +// ~CObjectPool() +// { +// Purge(); +// } +// +// int NumAvailable() +// { +// return m_AvailableObjects.Count(); +// } +// +// void Purge() +// { +// T* p = NULL; +// while (m_AvailableObjects.PopItem(&p)) +// { +// delete p; +// } +// } +// +// T* GetObject(bool bCreateNewIfEmpty = bDefCreateNewIfEmpty) +// { +// T* p = NULL; +// if (!m_AvailableObjects.PopItem(&p)) +// { +// p = (bCreateNewIfEmpty) ? new T : NULL; +// } +// return p; +// } +// +// void PutObject(T* p) +// { +// m_AvailableObjects.PushItem(p); +// } +// +//private: +// CTSList m_AvailableObjects; +//}; + +////----------------------------------------------------------------------------- +//// Fixed budget pool with overflow to malloc +////----------------------------------------------------------------------------- +//template +//class CFixedBudgetMemoryPool +//{ +//public: +// CFixedBudgetMemoryPool() +// { +// m_pBase = m_pLimit = 0; +// COMPILE_TIME_ASSERT(ITEM_SIZE % 4 == 0); +// } +// +// bool Owns(void* p) +// { +// return (p >= m_pBase && p < m_pLimit); +// } +// +// void* Alloc() +// { +// MEM_ALLOC_CREDIT_CLASS(); +//#ifndef USE_MEM_DEBUG +// if (!m_pBase) +// { +// LOCAL_THREAD_LOCK(); +// if (!m_pBase) +// { +// byte* pMemory = m_pBase = (byte*)malloc(ITEM_COUNT * ITEM_SIZE); +// m_pLimit = m_pBase + (ITEM_COUNT * ITEM_SIZE); +// +// for (int i = 0; i < ITEM_COUNT; i++) +// { +// m_freeList.Push((TSLNodeBase_t*)pMemory); +// pMemory += ITEM_SIZE; +// } +// } +// } +// +// void* p = m_freeList.Pop(); +// if (p) +// return p; +//#endif +// return malloc(ITEM_SIZE); +// } +// +// void Free(void* p) +// { +//#ifndef USE_MEM_DEBUG +// if (Owns(p)) +// m_freeList.Push((TSLNodeBase_t*)p); +// else +//#endif +// free(p); +// } +// +// void Clear() +// { +//#ifndef USE_MEM_DEBUG +// if (m_pBase) +// { +// free(m_pBase); +// } +// m_pBase = m_pLimit = 0; +// Construct(&m_freeList); +//#endif +// } +// +// bool IsEmpty() +// { +//#ifndef USE_MEM_DEBUG +// if (m_pBase && m_freeList.Count() != ITEM_COUNT) +// return false; +//#endif +// return true; +// } +// +// enum +// { +// ITEM_SIZE = ALIGN_VALUE(PROVIDED_ITEM_SIZE, TSLIST_NODE_ALIGNMENT) +// }; +// +// CTSListBase m_freeList; +// byte* m_pBase; +// byte* m_pLimit; +//}; +// +//#define BIND_TO_FIXED_BUDGET_POOL( poolName ) \ +// inline void* operator new( size_t size ) { return poolName.Alloc(); } \ +// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { return poolName.Alloc(); } \ +// inline void operator delete( void* p ) { poolName.Free(p); } \ +// inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { poolName.Free(p); } +// +////----------------------------------------------------------------------------- +// +// +//template< class T > +//inline T* CClassMemoryPool::Alloc() +//{ +// T* pRet; +// +// { +// MEM_ALLOC_CREDIT_CLASS(); +// pRet = (T*)CUtlMemoryPool::Alloc(); +// } +// +// if (pRet) +// { +// Construct(pRet); +// } +// return pRet; +//} +// +//template< class T > +//inline T* CClassMemoryPool::AllocZero() +//{ +// T* pRet; +// +// { +// MEM_ALLOC_CREDIT_CLASS(); +// pRet = (T*)CUtlMemoryPool::AllocZero(); +// } +// +// if (pRet) +// { +// Construct(pRet); +// } +// return pRet; +//} +// +//template< class T > +//inline void CClassMemoryPool::Free(T* pMem) +//{ +// if (pMem) +// { +// Destruct(pMem); +// } +// +// CUtlMemoryPool::Free(pMem); +//} +// +//template< class T > +//inline void CClassMemoryPool::Clear() +//{ +// CUtlRBTree freeBlocks; +// SetDefLessFunc(freeBlocks); +// +// void* pCurFree = m_pHeadOfFreeList; +// while (pCurFree != NULL) +// { +// freeBlocks.Insert(pCurFree); +// pCurFree = *((void**)pCurFree); +// } +// +// for (CBlob* pCur = m_BlobHead.m_pNext; pCur != &m_BlobHead; pCur = pCur->m_pNext) +// { +// int nElements = pCur->m_NumBytes / this->m_BlockSize; +// T* p = (T*)AlignValue(pCur->m_Data, this->m_nAlignment); +// T* pLimit = p + nElements; +// while (p < pLimit) +// { +// if (freeBlocks.Find(p) == freeBlocks.InvalidIndex()) +// { +// Destruct(p); +// } +// p++; +// } +// } +// +// CUtlMemoryPool::Clear(); +//} +// +// +// +// +// +////----------------------------------------------------------------------------- +//// Macros that make it simple to make a class use a fixed-size allocator +//// Put DECLARE_FIXEDSIZE_ALLOCATOR in the private section of a class, +//// Put DEFINE_FIXEDSIZE_ALLOCATOR in the CPP file +////----------------------------------------------------------------------------- +//#define DECLARE_FIXEDSIZE_ALLOCATOR( _class ) \ +// public: \ +// inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ +// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ +// inline void operator delete( void* p ) { s_Allocator.Free(p); } \ +// inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \ +// private: \ +// static CUtlMemoryPool s_Allocator +// +//#define DEFINE_FIXEDSIZE_ALLOCATOR( _class, _initsize, _grow ) \ +// CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool") +// +//#define DEFINE_FIXEDSIZE_ALLOCATOR_ALIGNED( _class, _initsize, _grow, _alignment ) \ +// CUtlMemoryPool _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool", _alignment ) +// +//#define DECLARE_FIXEDSIZE_ALLOCATOR_MT( _class ) \ +// public: \ +// inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ +// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_Allocator.Alloc(size); } \ +// inline void operator delete( void* p ) { s_Allocator.Free(p); } \ +// inline void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine ) { s_Allocator.Free(p); } \ +// private: \ +// static CMemoryPoolMT s_Allocator +// +//#define DEFINE_FIXEDSIZE_ALLOCATOR_MT( _class, _initsize, _grow ) \ +// CMemoryPoolMT _class::s_Allocator(sizeof(_class), _initsize, _grow, #_class " pool") +// +////----------------------------------------------------------------------------- +//// Macros that make it simple to make a class use a fixed-size allocator +//// This version allows us to use a memory pool which is externally defined... +//// Put DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the private section of a class, +//// Put DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL in the CPP file +////----------------------------------------------------------------------------- +// +//#define DECLARE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class ) \ +// public: \ +// inline void* operator new( size_t size ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \ +// inline void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) { MEM_ALLOC_CREDIT_(#_class " pool"); return s_pAllocator->Alloc(size); } \ +// inline void operator delete( void* p ) { s_pAllocator->Free(p); } \ +// private: \ +// static CUtlMemoryPool* s_pAllocator +// +//#define DEFINE_FIXEDSIZE_ALLOCATOR_EXTERNAL( _class, _allocator ) \ +// CUtlMemoryPool* _class::s_pAllocator = _allocator +// +// +//template +//inline CAlignedMemPool::CAlignedMemPool() +// : m_pFirstFree(0), +// m_nFree(0), +// m_TimeLastCompact(0) +//{ +// // These COMPILE_TIME_ASSERT checks need to be in individual scopes to avoid build breaks +// // on MacOS and Linux due to a gcc bug. +// { COMPILE_TIME_ASSERT(sizeof(FreeBlock_t) >= BLOCK_SIZE); } +// { COMPILE_TIME_ASSERT(ALIGN_VALUE(sizeof(FreeBlock_t), ALIGNMENT) == sizeof(FreeBlock_t)); } +//} +// +//template +//inline void* CAlignedMemPool::Alloc() +//{ +// AUTO_LOCK(m_mutex); +// +// if (!m_pFirstFree) +// { +// if (!GROWMODE && m_Chunks.Count()) +// { +// return NULL; +// } +// +// FreeBlock_t* pNew = (FreeBlock_t*)m_Allocator.Alloc(CHUNK_SIZE); +// Assert((unsigned)pNew % ALIGNMENT == 0); +// m_Chunks.AddToTail(pNew); +// m_nFree = CHUNK_SIZE / BLOCK_SIZE; +// m_pFirstFree = pNew; +// for (int i = 0; i < m_nFree - 1; i++) +// { +// pNew->pNext = pNew + 1; +// pNew++; +// } +// pNew->pNext = NULL; +// } +// +// void* p = m_pFirstFree; +// m_pFirstFree = m_pFirstFree->pNext; +// m_nFree--; +// +// return p; +//} +// +//template +//inline void CAlignedMemPool::Free(void* p) +//{ +// AUTO_LOCK(m_mutex); +// +// // Insertion sort to encourage allocation clusters in chunks +// FreeBlock_t* pFree = ((FreeBlock_t*)p); +// FreeBlock_t* pCur = m_pFirstFree; +// FreeBlock_t* pPrev = NULL; +// +// while (pCur && pFree > pCur) +// { +// pPrev = pCur; +// pCur = pCur->pNext; +// } +// +// pFree->pNext = pCur; +// +// if (pPrev) +// { +// pPrev->pNext = pFree; +// } +// else +// { +// m_pFirstFree = pFree; +// } +// m_nFree++; +// +// if (m_nFree >= (CHUNK_SIZE / BLOCK_SIZE) * COMPACT_THRESHOLD) +// { +// double time = Plat_FloatTime(); +// double compactTime = (m_nFree >= (CHUNK_SIZE / BLOCK_SIZE) * COMPACT_THRESHOLD * 4) ? 15.0 : 30.0; +// if (m_TimeLastCompact > time || m_TimeLastCompact + compactTime < time) +// { +// Compact(); +// m_TimeLastCompact = time; +// } +// } +//} +// +//template +//inline int __cdecl CAlignedMemPool::CompareChunk(void* const* ppLeft, void* const* ppRight) +//{ +// return size_cast((intp)*ppLeft - (intp)*ppRight); +//} +// +//template +//inline void CAlignedMemPool::Compact() +//{ +// FreeBlock_t* pCur = m_pFirstFree; +// FreeBlock_t* pPrev = NULL; +// +// m_Chunks.Sort(CompareChunk); +// +//#ifdef VALIDATE_ALIGNED_MEM_POOL +// { +// FreeBlock_t* p = m_pFirstFree; +// while (p) +// { +// if (p->pNext && p > p->pNext) +// { +// __asm { int 3 } +// } +// p = p->pNext; +// } +// +// for (int i = 0; i < m_Chunks.Count(); i++) +// { +// if (i + 1 < m_Chunks.Count()) +// { +// if (m_Chunks[i] > m_Chunks[i + 1]) +// { +// __asm { int 3 } +// } +// } +// } +// } +//#endif +// +// int i; +// +// for (i = 0; i < m_Chunks.Count(); i++) +// { +// int nBlocksPerChunk = CHUNK_SIZE / BLOCK_SIZE; +// FreeBlock_t* pChunkLimit = ((FreeBlock_t*)m_Chunks[i]) + nBlocksPerChunk; +// int nFromChunk = 0; +// if (pCur == m_Chunks[i]) +// { +// FreeBlock_t* pFirst = pCur; +// while (pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit) +// { +// pCur = pCur->pNext; +// nFromChunk++; +// } +// pCur = pFirst; +// +// } +// +// while (pCur && pCur >= m_Chunks[i] && pCur < pChunkLimit) +// { +// if (nFromChunk != nBlocksPerChunk) +// { +// if (pPrev) +// { +// pPrev->pNext = pCur; +// } +// else +// { +// m_pFirstFree = pCur; +// } +// pPrev = pCur; +// } +// else if (pPrev) +// { +// pPrev->pNext = NULL; +// } +// else +// { +// m_pFirstFree = NULL; +// } +// +// pCur = pCur->pNext; +// } +// +// if (nFromChunk == nBlocksPerChunk) +// { +// m_Allocator.Free(m_Chunks[i]); +// m_nFree -= nBlocksPerChunk; +// m_Chunks[i] = 0; +// } +// } +// +// for (i = m_Chunks.Count() - 1; i >= 0; i--) +// { +// if (!m_Chunks[i]) +// { +// m_Chunks.FastRemove(i); +// } +// } +//} + +#endif // MEMPOOL_H diff --git a/r5dev/public/tier1/memstack.h b/r5dev/public/tier1/memstack.h index 7d6e0773..445da08c 100644 --- a/r5dev/public/tier1/memstack.h +++ b/r5dev/public/tier1/memstack.h @@ -20,7 +20,7 @@ //----------------------------------------------------------------------------- -typedef uint64 MemoryStackMark_t; +typedef size_t MemoryStackMark_t; class CMemoryStack //: private IMemoryInfo { @@ -28,27 +28,27 @@ public: CMemoryStack(); ~CMemoryStack(); - bool Init( const char *pszAllocOwner, uint64 maxSize = 0, uint64 commitIncrement = 0, uint64 initialCommit = 0, uint64 alignment = 16 ); + bool Init( const char *pszAllocOwner, size_t maxSize = 0, size_t commitIncrement = 0, size_t initialCommit = 0, size_t alignment = 16 ); void Term(); - uint64 GetSize() const; - uint64 GetMaxSize() const ; - uint64 GetUsed() const; + size_t GetSize() const; + size_t GetMaxSize() const ; + size_t GetUsed() const; - void *Alloc( uint64 bytes, bool bClear = false ) RESTRICT; + void *Alloc( size_t bytes, bool bClear = false ) RESTRICT; MemoryStackMark_t GetCurrentAllocPoint() const; void FreeToAllocPoint( MemoryStackMark_t mark, bool bDecommit = true ); void FreeAll( bool bDecommit = true ); - void Access( void **ppRegion, uint64 *pBytes ); + void Access( void **ppRegion, size_t *pBytes ); void PrintContents() const; void *GetBase(); const void *GetBase() const { return const_cast(this)->GetBase(); } - bool CommitSize( uint64 nBytes ); + bool CommitSize( size_t nBytes ); void SetAllocOwner( const char *pszAllocOwner ); @@ -78,12 +78,12 @@ private: bool m_bPhysical; char *m_pszAllocOwner; - uint64 m_unkSize; // Unknown field.. - uint64 m_maxSize; // m_maxSize stores how big the stack can grow. It measures the reservation size. - uint64 m_alignment; + size_t m_unkSize; // Unknown field.. + size_t m_maxSize; // m_maxSize stores how big the stack can grow. It measures the reservation size. + size_t m_alignment; #ifdef MEMSTACK_VIRTUAL_MEMORY_AVAILABLE - uint64 m_commitIncrement; - uint64 m_minCommit; + size_t m_commitIncrement; + size_t m_minCommit; #endif #if defined( MEMSTACK_VIRTUAL_MEMORY_AVAILABLE ) && defined( _PS3 ) IVirtualMemorySection *m_pVirtualMemorySection; @@ -97,7 +97,7 @@ private: //------------------------------------- -FORCEINLINE void *CMemoryStack::Alloc( uint64 bytes, bool bClear ) RESTRICT +FORCEINLINE void *CMemoryStack::Alloc( size_t bytes, bool bClear ) RESTRICT { sizeof(CMemoryStack); Assert( m_pBase ); @@ -129,7 +129,7 @@ FORCEINLINE void *CMemoryStack::Alloc( uint64 bytes, bool bClear ) RESTRICT //------------------------------------- -inline bool CMemoryStack::CommitSize( uint64 nBytes ) +inline bool CMemoryStack::CommitSize( size_t nBytes ) { if ( GetSize() != nBytes ) { @@ -142,14 +142,14 @@ inline bool CMemoryStack::CommitSize( uint64 nBytes ) // How big can this memory stack grow? This is equivalent to how many // bytes are reserved. -inline uint64 CMemoryStack::GetMaxSize() const +inline size_t CMemoryStack::GetMaxSize() const { return m_maxSize; } //------------------------------------- -inline uint64 CMemoryStack::GetUsed() const +inline size_t CMemoryStack::GetUsed() const { return ( m_pNextAlloc - m_pBase ); } diff --git a/r5dev/public/tier1/refcount.h b/r5dev/public/tier1/refcount.h new file mode 100644 index 00000000..ef6af222 --- /dev/null +++ b/r5dev/public/tier1/refcount.h @@ -0,0 +1,389 @@ +//========== Copyright � 2005, Valve Corporation, All rights reserved. ======== +// +// Purpose: Tools for correctly implementing & handling reference counted +// objects +// +//============================================================================= + +#ifndef REFCOUNT_H +#define REFCOUNT_H + +#include "tier0/threadtools.h" + +#if defined( _WIN32 ) +#pragma once +#endif + +//----------------------------------------------------------------------------- +// Purpose: Implement a standard reference counted interface. Use of this +// is optional insofar as all the concrete tools only require +// at compile time that the function signatures match. +//----------------------------------------------------------------------------- + +class IRefCounted +{ +public: + virtual int AddRef() = 0; + virtual int Release() = 0; +}; + + +//----------------------------------------------------------------------------- +// Purpose: Release a pointer and mark it NULL +//----------------------------------------------------------------------------- + +template +inline int SafeRelease( REFCOUNTED_ITEM_PTR &pRef ) +{ + // Use funny syntax so that this works on "auto pointers" + REFCOUNTED_ITEM_PTR *ppRef = &pRef; + if ( *ppRef ) + { + int result = (*ppRef)->Release(); + *ppRef = NULL; + return result; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Maintain a reference across a scope +//----------------------------------------------------------------------------- + +template +class CAutoRef +{ +public: + CAutoRef( T *pRef ) + : m_pRef( pRef ) + { + if ( m_pRef ) + m_pRef->AddRef(); + } + + ~CAutoRef() + { + if (m_pRef) + m_pRef->Release(); + } + +private: + T *m_pRef; +}; + +//----------------------------------------------------------------------------- +// Purpose: Do a an inline AddRef then return the pointer, useful when +// returning an object from a function +//----------------------------------------------------------------------------- + +#define RetAddRef( p ) ( (p)->AddRef(), (p) ) +#define InlineAddRef( p ) ( (p)->AddRef(), (p) ) + + +//----------------------------------------------------------------------------- +// Purpose: A class to both hold a pointer to an object and its reference. +// Base exists to support other cleanup models +//----------------------------------------------------------------------------- + +template +class CBaseAutoPtr +{ +public: + CBaseAutoPtr() : m_pObject(0) {} + CBaseAutoPtr(T *pFrom) : m_pObject(pFrom) {} + + operator const void *() const { return m_pObject; } + operator void *() { return m_pObject; } + + operator const T *() const { return m_pObject; } + operator const T *() { return m_pObject; } + operator T *() { return m_pObject; } + + int operator=( int i ) { AssertMsg( i == 0, "Only NULL allowed on integer assign" ); m_pObject = 0; return 0; } + T * operator=( T *p ) { m_pObject = p; return p; } + + bool operator !() const { return ( !m_pObject ); } + bool operator!=( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (m_pObject != NULL); } + bool operator==( const void *p ) const { return ( m_pObject == p ); } + bool operator!=( const void *p ) const { return ( m_pObject != p ); } + bool operator==( T *p ) const { return operator==( (void *)p ); } + bool operator!=( T *p ) const { return operator!=( (void *)p ); } + bool operator==( const CBaseAutoPtr &p ) const { return operator==( (const void *)p ); } + bool operator!=( const CBaseAutoPtr &p ) const { return operator!=( (const void *)p ); } + + T * operator->() { return m_pObject; } + T & operator *() { return *m_pObject; } + T ** operator &() { return &m_pObject; } + + const T * operator->() const { return m_pObject; } + const T & operator *() const { return *m_pObject; } + T * const * operator &() const { return &m_pObject; } + +protected: + CBaseAutoPtr( const CBaseAutoPtr &from ) : m_pObject( from.m_pObject ) {} + void operator=( const CBaseAutoPtr &from ) { m_pObject = from.m_pObject; } + + T *m_pObject; +}; + +//--------------------------------------------------------- + +template +class CRefPtr : public CBaseAutoPtr +{ + typedef CBaseAutoPtr BaseClass; +public: + CRefPtr() {} + CRefPtr( T *pInit ) : BaseClass( pInit ) {} + CRefPtr( const CRefPtr &from ) : BaseClass( from ) {} + ~CRefPtr() { if ( BaseClass::m_pObject ) BaseClass::m_pObject->Release(); } + + void operator=( const CRefPtr &from ) { BaseClass::operator=( from ); } + + int operator=( int i ) { return BaseClass::operator=( i ); } + T *operator=( T *p ) { return BaseClass::operator=( p ); } + + operator bool() const { return !BaseClass::operator!(); } + operator bool() { return !BaseClass::operator!(); } + + void SafeRelease() { if ( BaseClass::m_pObject ) BaseClass::m_pObject->Release(); BaseClass::m_pObject = 0; } + void AssignAddRef( T *pFrom ) { if (pFrom) pFrom->AddRef(); SafeRelease(); BaseClass::m_pObject = pFrom; } + void AddRefAssignTo( T *&pTo ) { if ( BaseClass::m_pObject ) BaseClass::m_pObject->AddRef(); ::SafeRelease( pTo ); pTo = BaseClass::m_pObject; } +}; + + +//----------------------------------------------------------------------------- +// Purpose: Traits classes defining reference count threading model +//----------------------------------------------------------------------------- + +class CRefMT +{ +public: + static int Increment( int *p) { return ThreadInterlockedIncrement( (int32 *)p ); } + static int Decrement( int *p) { return ThreadInterlockedDecrement( (int32 *)p ); } +}; + +class CRefST +{ +public: + static int Increment( int *p) { return ++(*p); } + static int Decrement( int *p) { return --(*p); } +}; + +//----------------------------------------------------------------------------- +// Purpose: Actual reference counting implementation. Pulled out to reduce +// code bloat. +//----------------------------------------------------------------------------- + +template +class NO_VTABLE CRefCountServiceBase +{ +protected: + CRefCountServiceBase() + : m_iRefs( 1 ) + { + } + + virtual ~CRefCountServiceBase() + { + } + + virtual bool OnFinalRelease() + { + return true; + } + + int GetRefCount() const + { + return m_iRefs; + } + + int DoAddRef() + { + return CRefThreading::Increment( &m_iRefs ); + } + + int DoRelease() + { + int result = CRefThreading::Decrement( &m_iRefs ); + if ( result ) + return result; + if ( OnFinalRelease() && bSelfDelete ) + delete this; + return 0; + } + +private: + int m_iRefs; +}; + +class CRefCountServiceNull +{ +protected: + static int DoAddRef() { return 1; } + static int DoRelease() { return 1; } +}; + +template +class NO_VTABLE CRefCountServiceDestruct +{ +protected: + CRefCountServiceDestruct() + : m_iRefs( 1 ) + { + } + + virtual ~CRefCountServiceDestruct() + { + } + + int GetRefCount() const + { + return m_iRefs; + } + + int DoAddRef() + { + return CRefThreading::Increment( &m_iRefs ); + } + + int DoRelease() + { + int result = CRefThreading::Decrement( &m_iRefs ); + if ( result ) + return result; + this->~CRefCountServiceDestruct(); + return 0; + } + +private: + int m_iRefs; +}; + + +typedef CRefCountServiceBase CRefCountServiceST; +typedef CRefCountServiceBase CRefCountServiceNoDeleteST; + +typedef CRefCountServiceBase CRefCountServiceMT; +typedef CRefCountServiceBase CRefCountServiceNoDeleteMT; + +// Default to threadsafe +typedef CRefCountServiceNoDeleteMT CRefCountServiceNoDelete; +typedef CRefCountServiceMT CRefCountService; + +//----------------------------------------------------------------------------- +// Purpose: Base classes to implement reference counting +//----------------------------------------------------------------------------- + +template < class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted : public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted1 : public BASE1, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted1() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted2 : public BASE1, public BASE2, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted2() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class BASE3, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted3 : public BASE1, public BASE2, public BASE3, + public REFCOUNT_SERVICE +{ + virtual ~CRefCounted3() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class BASE3, class BASE4, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted4 : public BASE1, public BASE2, public BASE3, public BASE4, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted4() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//------------------------------------- + +template < class BASE1, class BASE2, class BASE3, class BASE4, class BASE5, class REFCOUNT_SERVICE = CRefCountService > +class NO_VTABLE CRefCounted5 : public BASE1, public BASE2, public BASE3, public BASE4, public BASE5, + public REFCOUNT_SERVICE +{ +public: + virtual ~CRefCounted5() {} + int AddRef() { return REFCOUNT_SERVICE::DoAddRef(); } + int Release() { return REFCOUNT_SERVICE::DoRelease(); } +}; + +//----------------------------------------------------------------------------- +// Purpose: Class to throw around a reference counted item to debug +// referencing problems +//----------------------------------------------------------------------------- + +#if defined( __clang__ ) +template +#else +template +#endif +class CRefDebug : public BASE_REFCOUNTED +{ +public: +#ifdef _DEBUG + CRefDebug() + { + AssertMsg( this->GetRefCount() == 1, "Expected initial ref count of 1" ); + DevMsg( eDLL_T::COMMON, "%s:create 0x%x\n", ( pszName ) ? pszName : "", this ); + } + + virtual ~CRefDebug() + { + AssertDevMsg( this->GetRefCount() == FINAL_REFS, "Object still referenced on destroy?" ); + DevMsg( eDLL_T::COMMON, "%s:destroy 0x%x\n", ( pszName ) ? pszName : "", this ); + } + + int AddRef() + { + DevMsg( eDLL_T::COMMON, "%s:(0x%x)->AddRef() --> %d\n", ( pszName ) ? pszName : "", this, this->GetRefCount() + 1 ); + return BASE_REFCOUNTED::AddRef(); + } + + int Release() + { + DevMsg( eDLL_T::COMMON, "%s:(0x%x)->Release() --> %d\n", ( pszName ) ? pszName : "", this, this->GetRefCount() - 1 ); + Assert( this->GetRefCount() > 0 ); + return BASE_REFCOUNTED::Release(); + } +#endif +}; + +//----------------------------------------------------------------------------- + +#endif // REFCOUNT_H diff --git a/r5dev/public/tier1/stringpool.h b/r5dev/public/tier1/stringpool.h index 6b8bbd7e..7f753e1a 100644 --- a/r5dev/public/tier1/stringpool.h +++ b/r5dev/public/tier1/stringpool.h @@ -248,7 +248,7 @@ inline T CCountedStringPoolBase::ReferenceStringHandle( const char* pIntrinsi } else { - unsigned int newElement = m_Elements.AddToTail(); + T newElement = (T)m_Elements.AddToTail(); VerifyNotOverflowed( newElement ); nCurrentBucket = newElement; } @@ -260,7 +260,7 @@ inline T CCountedStringPoolBase::ReferenceStringHandle( const char* pIntrinsi m_HashTable[ nHashBucketIndex ] = nCurrentBucket; m_Elements[nCurrentBucket].pString = new char[Q_strlen( pIntrinsic ) + 1]; - Q_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic ); + strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic ); return nCurrentBucket; } @@ -342,12 +342,13 @@ inline void CCountedStringPoolBase::SpewStrings() int i; for ( i = 0; i < m_Elements.Count(); i++ ) { - char* string = m_Elements[i].pString; + char* pString = m_Elements[i].pString; + NOTE_UNUSED(pString); - Msg("String %d: ref:%d %s\n", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string); + DevMsg(eDLL_T::COMMON, "String %d: ref:%hhu %s\n", i, m_Elements[i].nReferenceCount, pString == NULL? "EMPTY - ok for slot zero only!" : pString); } - Msg("\n%d total counted strings.", m_Elements.Count()); + DevMsg(eDLL_T::COMMON, "\n%d total counted strings.", m_Elements.Count()); } #define STRING_POOL_VERSION MAKEID( 'C', 'S', 'P', '1' ) diff --git a/r5dev/public/tier1/strtools.h b/r5dev/public/tier1/strtools.h index b577260f..20508762 100644 --- a/r5dev/public/tier1/strtools.h +++ b/r5dev/public/tier1/strtools.h @@ -41,6 +41,8 @@ #define V_strdup _strdup #define V_strcat strcat +#define V_strcasecmp V_stricmp + #define Q_vsnprintf V_vsnprintf #define Q_snprintf V_snprintf #define Q_strlower V_strlower @@ -59,15 +61,38 @@ #define Q_strcat V_strcat template int V_vsprintf_safe(OUT_Z_ARRAY char(&pDest)[maxLenInCharacters], PRINTF_FORMAT_STRING const char* pFormat, va_list params) { return V_vsnprintf(pDest, maxLenInCharacters, pFormat, params); } - +int _V_stricmp_NegativeForUnequal(const char* s1, const char* s2); char const* V_stristr(char const* pStr, char const* pSearch); -const char* V_strnistr(const char* pStr, const char* pSearch, int64_t n); -const char* V_strnchr(const char* pStr, char c, int64_t n); +const char* V_strnistr(const char* pStr, const char* pSearch, ssize_t n); +const char* V_strnchr(const char* pStr, char c, ssize_t n); bool V_isspace(int c); +inline bool V_isalnum(char c) { return isalnum((unsigned char)c) != 0; } +bool V_IsAllDigit(const char* pString); + +// this is locale-unaware and therefore faster version of standard isdigit() +// It also avoids sign-extension errors. +inline bool V_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +inline bool V_iswdigit(int c) +{ + return (((uint)(c - '0')) < 10); +} + +void V_hextobinary(char const* in, size_t numchars, byte* out, size_t maxoutputbytes); +void V_binarytohex(const byte* in, size_t inputbytes, char* out, size_t outsize); +ssize_t V_vsnprintfRet(char* pDest, size_t maxLen, const char* pFormat, va_list params, bool* pbTruncated); + // Strip white space at the beginning and end of a string -int64_t V_StrTrim(char* pStr); +ssize_t V_StrTrim(char* pStr); + +class CUtlStringList; +void V_SplitString2(const char* pString, const char** pSeparators, ssize_t nSeparators, CUtlStringList& outStrings); +void V_SplitString(const char* pString, const char* pSeparator, CUtlStringList& outStrings); int V_UTF8ToUnicode(const char* pUTF8, wchar_t* pwchDest, int cubDestSizeInBytes); int V_UnicodeToUTF8(const wchar_t* pUnicode, char* pUTF8, int cubDestSizeInBytes); @@ -137,11 +162,12 @@ void V_ComposeFileName(const char* path, const char* filename, char* dest, size_ // Remove any extension from in and return resulting string in out void V_StripExtension(const char* in, char* out, size_t outLen); +// Returns a pointer to the file extension or NULL if one doesn't exist +const char* V_GetFileExtension(const char* path, const bool keepDot = false); + // Copy out the file extension into dest void V_ExtractFileExtension(const char* path, char* dest, size_t destSize); - -// Returns a pointer to the file extension or NULL if one doesn't exist -const char* V_GetFileExtension(const char* path); +bool V_ExtractFilePath(const char* path, char* dest, size_t destSize); // Extracts the base name of a file (no path, no extension, assumes '/' or '\' as path separator) void V_FileBase(const char* in, OUT_Z_CAP(maxlen) char* out, size_t maxlen); diff --git a/r5dev/public/tier1/tier1.h b/r5dev/public/tier1/tier1.h new file mode 100644 index 00000000..177e8de0 --- /dev/null +++ b/r5dev/public/tier1/tier1.h @@ -0,0 +1,26 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: A higher level link library for general use in the game and tools. +// +//===========================================================================// +#ifndef TIER1_H +#define TIER1_H +#include "appframework/IAppSystem.h" + +//----------------------------------------------------------------------------- +// Helper empty implementation of an IAppSystem for tier2 libraries +//----------------------------------------------------------------------------- +template< class IInterface, int ConVarFlag = 0 > +class CTier1AppSystem : public CTier0AppSystem< IInterface > +{ +public: + virtual bool Connect( const CreateInterfaceFn factory ) = 0; + virtual void Disconnect( ) = 0; + virtual void* QueryInterface( const char* const pInterfaceName ) = 0; + virtual InitReturnVal_t Init( ) = 0; + virtual void Shutdown( ) = 0; + virtual AppSystemTier_t GetTier( ) = 0; + virtual void Reconnect( const CreateInterfaceFn factory, const char* const pInterfaceName ) = 0; +}; + +#endif // TIER1_H diff --git a/r5dev/public/tier1/utlbuffer.h b/r5dev/public/tier1/utlbuffer.h index 426db5bd..aaa73981 100644 --- a/r5dev/public/tier1/utlbuffer.h +++ b/r5dev/public/tier1/utlbuffer.h @@ -41,33 +41,33 @@ public: struct ConversionArray_t { char m_nActualChar; - char* m_pReplacementString; + const char* m_pReplacementString; }; - CUtlCharConversion(char nEscapeChar, const char* pDelimiter, int64 nCount, ConversionArray_t* pArray); + CUtlCharConversion(char nEscapeChar, const char* pDelimiter, ssize_t nCount, ConversionArray_t* pArray); char GetEscapeChar() const; const char* GetDelimiter() const; - int64 GetDelimiterLength() const; + ssize_t GetDelimiterLength() const; const char* GetConversionString(char c) const; - int64 GetConversionLength(char c) const; - int64 MaxConversionLength() const; + ssize_t GetConversionLength(char c) const; + ssize_t MaxConversionLength() const; // Finds a conversion for the passed-in string, returns length - virtual char FindConversion(const char* pString, int64* pLength); + virtual char FindConversion(const char* pString, ssize_t* pLength); protected: struct ConversionInfo_t { - int64 m_nLength; - char* m_pReplacementString; + ssize_t m_nLength; + const char* m_pReplacementString; }; char m_nEscapeChar; const char* m_pDelimiter; - int64 m_nDelimiterLength; - int64 m_nCount; - int64 m_nMaxConversionLength; + ssize_t m_nDelimiterLength; + ssize_t m_nCount; + ssize_t m_nMaxConversionLength; char m_pList[256]; ConversionInfo_t m_pReplacements[256]; }; @@ -157,6 +157,7 @@ public: // flags enum BufferFlags_t { + FLAGS_NONE = 0x0, TEXT_BUFFER = 0x1, // Describes how get + put work (as strings, or binary) EXTERNAL_GROWABLE = 0x2, // This is used w/ external buffers and causes the utlbuf to switch to reallocatable memory if an overflow happens when Putting. CONTAINS_CRLF = 0x4, // For text buffers only, does this contain \n or \n\r? @@ -165,13 +166,13 @@ public: }; // Overflow functions when a get or put overflows - typedef bool (CUtlBuffer::* UtlBufferOverflowFunc_t)(int64 nSize); + typedef bool (CUtlBuffer::* UtlBufferOverflowFunc_t)(ssize_t nSize); // Constructors for growable + external buffers for serialization/unserialization - CUtlBuffer(int64 growSize = 0, int64 initSize = 0, int nFlags = 0); - CUtlBuffer(const void* pBuffer, int64 size, int nFlags = 0); - // This one isn't actually defined so that we catch contructors that are trying to pass a bool in as the third param. - CUtlBuffer(const void* pBuffer, int64 size, bool crap) = delete; + CUtlBuffer(ssize_t growSize = 0, ssize_t initSize = 0, int nFlags = FLAGS_NONE); + CUtlBuffer(const void* pBuffer, ssize_t size, int nFlags = FLAGS_NONE); + // This one isn't actually defined so that we catch constructors that are trying to pass a bool in as the third param. + CUtlBuffer(const void* pBuffer, ssize_t size, bool crap) = delete; // UtlBuffer objects should not be copyable; we do a slow copy if you use this but it asserts. // (REI: I'd like to delete these but we have some python bindings that currently rely on being able to copy these objects) @@ -192,21 +193,21 @@ public: void SetBufferType(bool bIsText, bool bContainsCRLF); // Makes sure we've got at least this much memory - void EnsureCapacity(int64 num); + void EnsureCapacity(ssize_t num); // Access for direct read into buffer - void* AccessForDirectRead(int64 nBytes); + void* AccessForDirectRead(ssize_t nBytes); // Attaches the buffer to external memory.... - void SetExternalBuffer(void* pMemory, int64 nSize, int64 nInitialPut, int nFlags = 0); + void SetExternalBuffer(void* pMemory, ssize_t nSize, ssize_t nInitialPut, int nFlags = 0); bool IsExternallyAllocated() const; - void AssumeMemory(void* pMemory, int64 nSize, int64 nInitialPut, int nFlags = 0); + void AssumeMemory(void* pMemory, ssize_t nSize, ssize_t nInitialPut, int nFlags = 0); void* Detach(); void* DetachMemory(); // copies data from another buffer void CopyBuffer(const CUtlBuffer& buffer); - void CopyBuffer(const void* pubData, int64 cubData); + void CopyBuffer(const void* pubData, ssize_t cubData); void Swap(CUtlBuffer& buf); void Swap(CUtlMemory& mem); @@ -250,27 +251,27 @@ public: float GetFloat(); double GetDouble(); void* GetPtr(); - void GetString(char* pString, int64 nMaxChars); - bool Get(void* pMem, int64 size); - void GetLine(char* pLine, int64 nMaxChars); + void GetString(char* pString, ssize_t nMaxChars); + bool Get(void* pMem, ssize_t size); + void GetLine(char* pLine, ssize_t nMaxChars); // Used for getting objects that have a byteswap datadesc defined - template void GetObjects(T* dest, int64 count = 1); + template void GetObjects(T* dest, ssize_t count = 1); // This will get at least 1 byte and up to nSize bytes. // It will return the number of bytes actually read. - int64 GetUpTo(void* pMem, int64 nSize); + ssize_t GetUpTo(void* pMem, ssize_t nSize); // This version of GetString converts \" to \\ and " to \, etc. // It also reads a " at the beginning and end of the string - void GetDelimitedString(CUtlCharConversion* pConv, char* pString, int64 nMaxChars = 0); + void GetDelimitedString(CUtlCharConversion* pConv, char* pString, ssize_t nMaxChars = 0); char GetDelimitedChar(CUtlCharConversion* pConv); // This will return the # of characters of the string about to be read out // NOTE: The count will *include* the terminating 0!! // In binary mode, it's the number of characters until the next 0 // In text mode, it's the number of characters until the next space. - int64 PeekStringLength(); + ssize_t PeekStringLength(); // This version of PeekStringLength converts \" to \\ and " to \, etc. // It also reads a " at the beginning and end of the string @@ -280,11 +281,11 @@ public: // Specifying false for bActualSize will return the pre-translated number of characters // including the delimiters and the escape characters. So, \n counts as 2 characters when bActualSize == false // and only 1 character when bActualSize == true - int64 PeekDelimitedStringLength(CUtlCharConversion* pConv, bool bActualSize = true); + ssize_t PeekDelimitedStringLength(CUtlCharConversion* pConv, bool bActualSize = true); // Just like scanf, but doesn't work in binary mode - int64 Scanf(SCANF_FORMAT_STRING const char* pFmt, ...); - int64 VaScanf(const char* pFmt, va_list list); + ssize_t Scanf(SCANF_FORMAT_STRING const char* pFmt, ...); + ssize_t VaScanf(const char* pFmt, va_list list); // Eats white space, advances Get index void EatWhiteSpace(); @@ -298,7 +299,7 @@ public: // (skipping whitespace that leads + trails both delimiters). // If successful, the get index is advanced and the function returns true, // otherwise the index is not advanced and the function returns false. - bool ParseToken(const char* pStartingDelim, const char* pEndingDelim, char* pString, int64 nMaxLen); + bool ParseToken(const char* pStartingDelim, const char* pEndingDelim, char* pString, ssize_t nMaxLen); // Advance the get index until after the particular string is found // Do not eat whitespace before starting. Return false if it failed @@ -307,7 +308,7 @@ public: // Parses the next token, given a set of character breaks to stop at // Returns the length of the token parsed in bytes (-1 if none parsed) - int64 ParseToken(characterset_t* pBreaks, char* pTokenBuf, int64 nMaxLen, bool bParseComments = true); + ssize_t ParseToken(characterset_t* pBreaks, char* pTokenBuf, ssize_t nMaxLen, bool bParseComments = true); // Write stuff in // Binary mode: it'll just write the bits directly in, and strings will be @@ -326,10 +327,10 @@ public: void PutDouble(double d); void PutPtr(void*); // Writes the pointer, not the pointed to void PutString(const char* pString); - void Put(const void* pMem, int64 size); + void Put(const void* pMem, ssize_t size); // Used for putting objects that have a byteswap datadesc defined - template void PutObjects(T* src, int64 count = 1); + template void PutObjects(T* src, ssize_t count = 1); // This version of PutString converts \ to \\ and " to \", etc. // It also places " at the beginning and end of the string @@ -341,24 +342,24 @@ public: void VaPrintf(const char* pFmt, va_list list); // What am I writing (put)/reading (get)? - void* PeekPut(int64 offset = 0); - const void* PeekGet(int64 offset = 0) const; - const void* PeekGet(int64 nMaxSize, int64 nOffset); + void* PeekPut(ssize_t offset = 0); + const void* PeekGet(ssize_t offset = 0) const; + const void* PeekGet(ssize_t nMaxSize, ssize_t nOffset); // Where am I writing (put)/reading (get)? - int64 TellPut() const; - int64 TellGet() const; + ssize_t TellPut() const; + ssize_t TellGet() const; // What's the most I've ever written? - int64 TellMaxPut() const; + ssize_t TellMaxPut() const; // How many bytes remain to be read? // NOTE: This is not accurate for streaming text files; it overshoots - int64 GetBytesRemaining() const; + ssize_t GetBytesRemaining() const; // Change where I'm writing (put)/reading (get) - void SeekPut(SeekType_t type, int64 offset); - void SeekGet(SeekType_t type, int64 offset); + void SeekPut(SeekType_t type, ssize_t offset); + void SeekGet(SeekType_t type, ssize_t offset); // Buffer base const void* Base() const; @@ -366,7 +367,7 @@ public: // memory allocation size, does *not* reflect size written or read, // use TellPut or TellGet for that - int64 Size() const; + ssize_t Size() const; // Am I a text buffer? bool IsText() const; @@ -412,17 +413,17 @@ protected: void SetOverflowFuncs(UtlBufferOverflowFunc_t getFunc, UtlBufferOverflowFunc_t putFunc); - bool OnPutOverflow(int64 nSize); - bool OnGetOverflow(int64 nSize); + bool OnPutOverflow(ssize_t nSize); + bool OnGetOverflow(ssize_t nSize); protected: // Checks if a get/put is ok - bool CheckPut(int64 size); - bool CheckGet(int64 size); + bool CheckPut(ssize_t size); + bool CheckGet(ssize_t size); // NOTE: Pass in nPut here even though it is just a copy of m_Put. This is almost always called immediately // after modifying m_Put and this lets it stay in a register - void AddNullTermination(int64 nPut); + void AddNullTermination(ssize_t nPut); // Methods to help with pretty-printing bool WasLastCharacterCR(); @@ -433,24 +434,24 @@ protected: void PutDelimitedCharInternal(CUtlCharConversion* pConv, char c); // Default overflow funcs - bool PutOverflow(int64 nSize); - bool GetOverflow(int64 nSize); + bool PutOverflow(ssize_t nSize); + bool GetOverflow(ssize_t nSize); // Does the next bytes of the buffer match a pattern? - bool PeekStringMatch(int64 nOffset, const char* pString, int64 nLen); + bool PeekStringMatch(ssize_t nOffset, const char* pString, ssize_t nLen); // Peek size of line to come, check memory bound - int64 PeekLineLength(); + ssize_t PeekLineLength(); // How much whitespace should I skip? - int64 PeekWhiteSpace(int64 nOffset); + ssize_t PeekWhiteSpace(ssize_t nOffset); // Checks if a peek get is ok - bool CheckPeekGet(int64 nOffset, int64 nSize); + bool CheckPeekGet(ssize_t nOffset, ssize_t nSize); // Call this to peek arbitrarily long into memory. It doesn't fail unless // it can't read *anything* new - bool CheckArbitraryPeekGet(int64 nOffset, int64& nIncrement); + bool CheckArbitraryPeekGet(ssize_t nOffset, ssize_t& nIncrement); template void GetType(T& dest); template void GetTypeBin(T& dest); @@ -464,8 +465,8 @@ protected: // be sure to also update the copy constructor // and SwapCopy() when adding members. CUtlMemory m_Memory; - int64 m_Get; - int64 m_Put; + ssize_t m_Get; + ssize_t m_Put; unsigned char m_Error; unsigned char m_Flags; @@ -475,8 +476,8 @@ protected: #endif int m_nTab; - int64 m_nMaxPut; - int64 m_nOffset; + ssize_t m_nMaxPut; + ssize_t m_nOffset; UtlBufferOverflowFunc_t m_GetOverflowFunc; UtlBufferOverflowFunc_t m_PutOverflowFunc; @@ -485,7 +486,7 @@ protected: void* m_pUnk; // Possibly padding? const char* m_pName; - int64 m_Count; // Unknown count. + ssize_t m_Count; // Unknown count. }; static_assert(sizeof(CUtlBuffer) == 0x70); @@ -560,7 +561,7 @@ inline CUtlBuffer& operator<<(CUtlBuffer& b, const Vector2D& v) class CUtlInplaceBuffer : public CUtlBuffer { public: - CUtlInplaceBuffer(int64 growSize = 0, int64 initSize = 0, int nFlags = 0); + CUtlInplaceBuffer(ssize_t growSize = 0, ssize_t initSize = 0, int nFlags = FLAGS_NONE); // // Routines returning buffer-inplace-pointers @@ -596,7 +597,7 @@ public: // @returns true if line was successfully read // false when EOF is reached or error occurs // - bool InplaceGetLinePtr( /* out */ char** ppszInBufferPtr, /* out */ int64* pnLineLength); + bool InplaceGetLinePtr( /* out */ char** ppszInBufferPtr, /* out */ ssize_t* pnLineLength); // // Determines the line length, advances the "get" pointer offset by the line length, @@ -627,7 +628,7 @@ public: //----------------------------------------------------------------------------- // Where am I reading? //----------------------------------------------------------------------------- -inline int64 CUtlBuffer::TellGet() const +inline ssize_t CUtlBuffer::TellGet() const { return m_Get; } @@ -636,7 +637,7 @@ inline int64 CUtlBuffer::TellGet() const //----------------------------------------------------------------------------- // How many bytes remain to be read? //----------------------------------------------------------------------------- -inline int64 CUtlBuffer::GetBytesRemaining() const +inline ssize_t CUtlBuffer::GetBytesRemaining() const { return m_nMaxPut - TellGet(); } @@ -645,7 +646,7 @@ inline int64 CUtlBuffer::GetBytesRemaining() const //----------------------------------------------------------------------------- // What am I reading? //----------------------------------------------------------------------------- -inline const void* CUtlBuffer::PeekGet(int64 offset) const +inline const void* CUtlBuffer::PeekGet(ssize_t offset) const { return &m_Memory[m_Get + offset - m_nOffset]; } @@ -678,9 +679,9 @@ inline void CUtlBuffer::GetObject(T* dest) template -inline void CUtlBuffer::GetObjects(T* dest, int64 count) +inline void CUtlBuffer::GetObjects(T* dest, ssize_t count) { - for (int64 i = 0; i < count; ++i, ++dest) + for (ssize_t i = 0; i < count; ++i, ++dest) { GetObject(dest); } @@ -846,7 +847,7 @@ template inline bool CUtlBuffer::GetTypeText(T& value, int nRadix /*= 10*/) { // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters - int64 nLength = 128; + ssize_t nLength = 128; if (!CheckArbitraryPeekGet(0, nLength)) { value = 0; @@ -857,7 +858,7 @@ inline bool CUtlBuffer::GetTypeText(T& value, int nRadix /*= 10*/) char* pEnd = pStart; value = StringToNumber< T >(pStart, &pEnd, nRadix); - int64 nBytesRead = (int64)(pEnd - pStart); + ssize_t nBytesRead = (ssize_t)(pEnd - pStart); if (nBytesRead == 0) return false; @@ -1005,7 +1006,7 @@ inline bool CUtlBuffer::IsExternallyAllocated() const //----------------------------------------------------------------------------- // Where am I writing? //----------------------------------------------------------------------------- -inline int64 CUtlBuffer::TellPut() const +inline ssize_t CUtlBuffer::TellPut() const { return m_Put; } @@ -1014,7 +1015,7 @@ inline int64 CUtlBuffer::TellPut() const //----------------------------------------------------------------------------- // What's the most I've ever written? //----------------------------------------------------------------------------- -inline int64 CUtlBuffer::TellMaxPut() const +inline ssize_t CUtlBuffer::TellMaxPut() const { return m_nMaxPut; } @@ -1023,7 +1024,7 @@ inline int64 CUtlBuffer::TellMaxPut() const //----------------------------------------------------------------------------- // What am I reading? //----------------------------------------------------------------------------- -inline void* CUtlBuffer::PeekPut(int64 offset) +inline void* CUtlBuffer::PeekPut(ssize_t offset) { return &m_Memory[m_Put + offset - m_nOffset]; } @@ -1053,9 +1054,9 @@ inline void CUtlBuffer::PutObject(T* src) template -inline void CUtlBuffer::PutObjects(T* src, int64 count) +inline void CUtlBuffer::PutObjects(T* src, ssize_t count) { - for (int64 i = 0; i < count; ++i, ++src) + for (ssize_t i = 0; i < count; ++i, ++src) { PutObject(src); } @@ -1353,7 +1354,7 @@ inline void* CUtlBuffer::Base() return m_Memory.Base(); } -inline int64 CUtlBuffer::Size() const +inline ssize_t CUtlBuffer::Size() const { return m_Memory.NumAllocated(); } @@ -1385,7 +1386,7 @@ inline void CUtlBuffer::Purge() //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- -inline void* CUtlBuffer::AccessForDirectRead(int64 nBytes) +inline void* CUtlBuffer::AccessForDirectRead(ssize_t nBytes) { Assert(m_Get == 0 && m_Put == 0 && m_nMaxPut == 0); EnsureCapacity(nBytes); @@ -1404,15 +1405,15 @@ inline void* CUtlBuffer::Detach() inline void CUtlBuffer::Spew() { - //SeekGet(CUtlBuffer::SEEK_HEAD, 0); + SeekGet(CUtlBuffer::SEEK_HEAD, 0); - //char pTmpLine[1024]; - //while (IsValid() && GetBytesRemaining()) - //{ - // memset(pTmpLine, 0, sizeof(pTmpLine)); - // Get(pTmpLine, MIN((size_t)GetBytesRemaining(), sizeof(pTmpLine) - 1)); - // DevMsg(eDLL_T::COMMON, _T("%s"), pTmpLine); - //} + char pTmpLine[1024]; + while (IsValid() && GetBytesRemaining()) + { + memset(pTmpLine, 0, sizeof(pTmpLine)); + Get(pTmpLine, MIN((size_t)GetBytesRemaining(), sizeof(pTmpLine) - 1)); + DevMsg(eDLL_T::COMMON, "%s", pTmpLine); + } } #if !defined(_GAMECONSOLE) @@ -1439,7 +1440,7 @@ inline void CUtlBuffer::CopyBuffer(const CUtlBuffer& buffer) CopyBuffer(buffer.Base(), buffer.TellPut()); } -inline void CUtlBuffer::CopyBuffer(const void* pubData, int64 cubData) +inline void CUtlBuffer::CopyBuffer(const void* pubData, ssize_t cubData) { Clear(); if (cubData) diff --git a/r5dev/public/tier1/utlcommon.h b/r5dev/public/tier1/utlcommon.h new file mode 100644 index 00000000..128ad110 --- /dev/null +++ b/r5dev/public/tier1/utlcommon.h @@ -0,0 +1,385 @@ +//========= Copyright � 2011, Valve Corporation, All rights reserved. ============// +// +// Purpose: common helpers for reuse among various Utl containers +// +// $NoKeywords: $ +// +//=============================================================================// + +#ifndef UTLCOMMON_H +#define UTLCOMMON_H +#pragma once + +#include "strtools.h" + +//----------------------------------------------------------------------------- +// Henry Goffin (henryg) was here. Questions? Bugs? Go slap him around a bit. +//----------------------------------------------------------------------------- + +// empty_t is the canonical "no-value" type which is fully defined but empty. +struct empty_t {}; + +// undefined_t is the canonical "undefined" type, used mostly for typedefs; +// parameters of type undefined_t will not compile, which is actually useful +// behavior when it comes to template programming. Google "SFINAE" for info. +struct undefined_t; + +// CTypeSelect::type is a typedef of A if sel is nonzero, else B +template +struct CTypeSelect { typedef A type; }; + +template +struct CTypeSelect<0, A, B> { typedef B type; }; + +// CTypeEquals::value is nonzero if A and B are the same type +template +struct CTypeEquals { enum { value = 0 }; }; + +template +struct CTypeEquals { enum { value = 1 }; }; + +template +struct CTypeEquals : CTypeEquals< const volatile A&, const volatile B& > {}; + +template +struct CTypeEquals : CTypeEquals< const volatile A, const volatile B > {}; + +template +struct CTypeEquals : CTypeEquals< A&, B& > {}; + +// CUtlKeyValuePair is intended for use with key-lookup containers. +// Because it is specialized for "empty_t" values, one container can +// function as either a set of keys OR a key-value dictionary while +// avoiding storage waste or padding for the empty_t value objects. +template +class CUtlKeyValuePair +{ +public: + typedef V ValueReturn_t; + K m_key; + V m_value; + + CUtlKeyValuePair() {} + + template < typename KInit > + explicit CUtlKeyValuePair( const KInit &k ) : m_key( k ) {} + + template < typename KInit, typename VInit > + CUtlKeyValuePair( const KInit &k, const VInit &v ) : m_key( k ), m_value( v ) {} + + V &GetValue() { return m_value; } + const V &GetValue() const { return m_value; } +}; + +template +class CUtlKeyValuePair +{ +public: + typedef const K ValueReturn_t; + K m_key; + + CUtlKeyValuePair() {} + + template < typename KInit > + explicit CUtlKeyValuePair( const KInit &k ) : m_key( k ) {} + + template < typename KInit > + CUtlKeyValuePair( const KInit &k, empty_t ) : m_key( k ) {} + + CUtlKeyValuePair( const K &k, const empty_t& ) : m_key( k ) {} + const K &GetValue() const { return m_key; } +}; + + +// Default functors. You can specialize these if your type does +// not implement operator== or operator< in an efficient way for +// some odd reason. +template struct DefaultLessFunctor; +template struct DefaultEqualFunctor; + +// Hashing functor used by hash tables. You can either specialize +// for types which are widely used, or plug a custom functor directly +// into the hash table. If you do roll your own, please read up on +// bit-mixing and the avalanche property; be sure that your values +// are reasonably well-distributed across the entire 32-bit range. +// http://en.wikipedia.org/wiki/Avalanche_effect +// http://home.comcast.net/~bretm/hash/5.html +// +template struct DefaultHashFunctor; + +// Argument type information. Struct currently contains one or two typedefs: +// typename Arg_t = primary argument type. Usually const T&, sometimes T. +// typename Alt_t = optional alternate type. Usually *undefined*. +// +// Any specializations should be implemented via simple inheritance +// from ArgumentTypeInfoImpl< BestArgType, [optional] AlternateArgType > +// +template struct ArgumentTypeInfo; + + +// Some fundamental building-block functors... +struct StringLessFunctor +{ + StringLessFunctor( int i ) {}; + StringLessFunctor( void ) {}; + inline bool operator!() const { return false; } + + bool operator()( const char *a, const char *b ) const + { + return V_strcmp( a, b ) < 0; + } +}; +struct StringEqualFunctor { bool operator()( const char *a, const char *b ) const { return V_strcmp( a, b ) == 0; } }; +struct CaselessStringLessFunctor { bool operator()( const char *a, const char *b ) const { return V_strcasecmp( a, b ) < 0; } }; +struct CaselessStringEqualFunctor { bool operator()( const char *a, const char *b ) const { return V_strcasecmp( a, b ) == 0; } }; + +struct IdentityHashFunctor { unsigned int operator() ( uint32 s ) const { return s; } }; +struct Mix32HashFunctor { unsigned int operator()( uint32 s ) const; }; +struct Mix64HashFunctor { unsigned int operator()( uint64 s ) const; }; +struct StringHashFunctor { unsigned int operator()( const char* s ) const; }; +struct CaselessStringHashFunctor { unsigned int operator()( const char* s ) const; }; + +struct PointerLessFunctor { bool operator()( const void *a, const void *b ) const { return a < b; } }; +struct PointerEqualFunctor { bool operator()( const void *a, const void *b ) const { return a == b; } }; +#if defined( PLATFORM_64BITS ) +struct PointerHashFunctor { unsigned int operator()( const void* s ) const { return Mix64HashFunctor()( ( uintp ) s ); } }; +#else +struct PointerHashFunctor { unsigned int operator()( const void* s ) const { return Mix32HashFunctor()( ( uintp ) s ); } }; +#endif + + +// Generic implementation of Less and Equal functors +template < typename T > +struct DefaultLessFunctor +{ + bool operator()( typename ArgumentTypeInfo< T >::Arg_t a, typename ArgumentTypeInfo< T >::Arg_t b ) const { return a < b; } + bool operator()( typename ArgumentTypeInfo< T >::Alt_t a, typename ArgumentTypeInfo< T >::Arg_t b ) const { return a < b; } + bool operator()( typename ArgumentTypeInfo< T >::Arg_t a, typename ArgumentTypeInfo< T >::Alt_t b ) const { return a < b; } +}; + +template < typename T > +struct DefaultEqualFunctor +{ + bool operator()( typename ArgumentTypeInfo< T >::Arg_t a, typename ArgumentTypeInfo< T >::Arg_t b ) const { return a == b; } + bool operator()( typename ArgumentTypeInfo< T >::Alt_t a, typename ArgumentTypeInfo< T >::Arg_t b ) const { return a == b; } + bool operator()( typename ArgumentTypeInfo< T >::Arg_t a, typename ArgumentTypeInfo< T >::Alt_t b ) const { return a == b; } +}; + +// Hashes for basic types +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +#if !defined(PLATFORM_64BITS) || defined(_WIN32) +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +#elif defined(POSIX) +template <> struct DefaultHashFunctor : Mix64HashFunctor { }; +template <> struct DefaultHashFunctor : Mix64HashFunctor { }; +#endif +template <> struct DefaultHashFunctor : Mix64HashFunctor { }; +template <> struct DefaultHashFunctor : Mix64HashFunctor { }; +template <> struct DefaultHashFunctor : PointerHashFunctor { }; +template <> struct DefaultHashFunctor : PointerHashFunctor { }; +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +template <> struct DefaultHashFunctor : Mix32HashFunctor { }; +#endif + +// String specializations. If you want to operate on raw values, use +// PointerLessFunctor and friends from the "building-block" section above +template <> struct DefaultLessFunctor : StringLessFunctor { }; +template <> struct DefaultLessFunctor : StringLessFunctor { }; +template <> struct DefaultEqualFunctor : StringEqualFunctor { }; +template <> struct DefaultEqualFunctor : StringEqualFunctor { }; +template <> struct DefaultHashFunctor : StringHashFunctor { }; +template <> struct DefaultHashFunctor : StringHashFunctor { }; + +// CUtlString/CUtlConstString are specialized here and not in utlstring.h +// because I consider string datatypes to be fundamental, and don't feel +// comfortable making that header file dependent on this one. (henryg) +class CUtlString; +template < typename T > class CUtlConstStringBase; + +template <> struct DefaultLessFunctor : StringLessFunctor { }; +template <> struct DefaultHashFunctor : StringHashFunctor { }; +template <> struct DefaultEqualFunctor : StringEqualFunctor {}; +template < typename T > struct DefaultLessFunctor< CUtlConstStringBase > : StringLessFunctor { }; +template < typename T > struct DefaultHashFunctor< CUtlConstStringBase > : StringHashFunctor { }; + + +// Helpers to deduce if a type defines a public AltArgumentType_t typedef: +template < typename T > +struct HasClassAltArgumentType +{ + template < typename X > static long Test( typename X::AltArgumentType_t* ); + template < typename X > static char Test( ... ); + enum { value = ( sizeof( Test< T >( NULL ) ) != sizeof( char ) ) }; +}; + +template < typename T, bool = HasClassAltArgumentType< T >::value > +struct GetClassAltArgumentType { typedef typename T::AltArgumentType_t Result_t; }; + +template < typename T > +struct GetClassAltArgumentType< T, false > { typedef undefined_t Result_t; }; + +// Unwrap references; reference types don't have member typedefs. +template < typename T > +struct GetClassAltArgumentType< T&, false > : GetClassAltArgumentType< T > { }; + +// ArgumentTypeInfoImpl is the base for all ArgumentTypeInfo specializations. +template < typename ArgT, typename AltT = typename GetClassAltArgumentType::Result_t > +struct ArgumentTypeInfoImpl +{ + enum { has_alt = 1 }; + typedef ArgT Arg_t; + typedef AltT Alt_t; +}; + +// Handle cases where AltArgumentType_t is typedef'd to undefined_t +template < typename ArgT > +struct ArgumentTypeInfoImpl< ArgT, undefined_t > +{ + enum { has_alt = 0 }; + typedef ArgT Arg_t; + typedef undefined_t Alt_t; +}; + +// Handle cases where AltArgumentType_t is typedef'd to the primary type +template < typename ArgT > +struct ArgumentTypeInfoImpl< ArgT, ArgT > +{ + enum { has_alt = 0 }; + typedef ArgT Arg_t; + typedef undefined_t Alt_t; +}; + + +// By default, everything is passed via const ref and doesn't define an alternate type. +template struct ArgumentTypeInfo : ArgumentTypeInfoImpl< const T& > { }; + +// Small native types are most efficiently passed by value. +template <> struct ArgumentTypeInfo< bool > : ArgumentTypeInfoImpl< bool > { }; +template <> struct ArgumentTypeInfo< char > : ArgumentTypeInfoImpl< char > { }; +template <> struct ArgumentTypeInfo< signed char > : ArgumentTypeInfoImpl< signed char > { }; +template <> struct ArgumentTypeInfo< unsigned char > : ArgumentTypeInfoImpl< unsigned char > { }; +template <> struct ArgumentTypeInfo< signed short > : ArgumentTypeInfoImpl< signed short > { }; +template <> struct ArgumentTypeInfo< unsigned short > : ArgumentTypeInfoImpl< unsigned short > { }; +template <> struct ArgumentTypeInfo< signed int > : ArgumentTypeInfoImpl< signed int > { }; +template <> struct ArgumentTypeInfo< unsigned int > : ArgumentTypeInfoImpl< unsigned int > { }; +template <> struct ArgumentTypeInfo< signed long > : ArgumentTypeInfoImpl< signed long > { }; +template <> struct ArgumentTypeInfo< unsigned long > : ArgumentTypeInfoImpl< unsigned long > { }; +template <> struct ArgumentTypeInfo< signed long long > : ArgumentTypeInfoImpl< signed long long > { }; +template <> struct ArgumentTypeInfo< unsigned long long > : ArgumentTypeInfoImpl< unsigned long long > { }; +template <> struct ArgumentTypeInfo< float > : ArgumentTypeInfoImpl< float > { }; +template <> struct ArgumentTypeInfo< double > : ArgumentTypeInfoImpl< double > { }; +template <> struct ArgumentTypeInfo< long double > : ArgumentTypeInfoImpl< long double > { }; +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +template <> struct ArgumentTypeInfo< wchar_t > : ArgumentTypeInfoImpl< wchar_t > { }; +#endif + +// Pointers are also most efficiently passed by value. +template < typename T > struct ArgumentTypeInfo< T* > : ArgumentTypeInfoImpl< T* > { }; + + +// Specializations to unwrap const-decorated types and references +template struct ArgumentTypeInfo : ArgumentTypeInfo { }; +template struct ArgumentTypeInfo : ArgumentTypeInfo { }; +template struct ArgumentTypeInfo : ArgumentTypeInfo { }; +template struct ArgumentTypeInfo : ArgumentTypeInfo { }; + +template struct DefaultLessFunctor : DefaultLessFunctor { }; +template struct DefaultLessFunctor : DefaultLessFunctor { }; +template struct DefaultLessFunctor : DefaultLessFunctor { }; +template struct DefaultLessFunctor : DefaultLessFunctor { }; + +template struct DefaultEqualFunctor : DefaultEqualFunctor { }; +template struct DefaultEqualFunctor : DefaultEqualFunctor { }; +template struct DefaultEqualFunctor : DefaultEqualFunctor { }; +template struct DefaultEqualFunctor : DefaultEqualFunctor { }; + +template struct DefaultHashFunctor : DefaultHashFunctor { }; +template struct DefaultHashFunctor : DefaultHashFunctor { }; +template struct DefaultHashFunctor : DefaultHashFunctor { }; +template struct DefaultHashFunctor : DefaultHashFunctor { }; + + +// Hash all pointer types as raw pointers by default +template struct DefaultHashFunctor< T * > : PointerHashFunctor { }; + + +// Here follow the useful implementations. + +// Bob Jenkins's 32-bit mix function. +inline unsigned int Mix32HashFunctor::operator()( uint32 n ) const +{ + // Perform a mixture of the bits in n, where each bit + // of the input value has an equal chance to affect each + // bit of the output. This turns tightly clustered input + // values into a smooth distribution. + // + // This takes 16-20 cycles on modern x86 architectures; + // that's roughly the same cost as a mispredicted branch. + // It's also reasonably efficient on PPC-based consoles. + // + // If you're still thinking, "too many instructions!", + // do keep in mind that reading one byte of uncached RAM + // is about 30x slower than executing this code. It pays + // to have a good hash function which minimizes collisions + // (and therefore long lookup chains). + n = ( n + 0x7ed55d16 ) + ( n << 12 ); + n = ( n ^ 0xc761c23c ) ^ ( n >> 19 ); + n = ( n + 0x165667b1 ) + ( n << 5 ); + n = ( n + 0xd3a2646c ) ^ ( n << 9 ); + n = ( n + 0xfd7046c5 ) + ( n << 3 ); + n = ( n ^ 0xb55a4f09 ) ^ ( n >> 16 ); + return n; +} + +inline unsigned int Mix64HashFunctor::operator()( uint64 s ) const +{ + // Thomas Wang hash, http://www.concentric.net/~ttwang/tech/inthash.htm + s = ( ~s ) + ( s << 21 ); // s = (s << 21) - s - 1; + s = s ^ ( s >> 24 ); + s = (s + ( s << 3 ) ) + ( s << 8 ); // s * 265 + s = s ^ ( s >> 14 ); + s = ( s + ( s << 2 ) ) + ( s << 4 ); // s * 21 + s = s ^ ( s >> 28 ); + s = s + ( s << 31 ); + return (unsigned int)s; +} + + +// Based on the widely-used FNV-1A string hash with a final +// mixing step to improve dispersion for very small and very +// large hash table sizes. +inline unsigned int StringHashFunctor::operator()( const char* s ) const +{ + uint32 h = 2166136261u; + for ( ; *s; ++s ) + { + uint32 c = (unsigned char) *s; + h = (h ^ c) * 16777619; + } + return (h ^ (h << 17)) + (h >> 21); +} + +// Equivalent to StringHashFunctor on lower-case strings. +inline unsigned int CaselessStringHashFunctor::operator()( const char* s ) const +{ + uint32 h = 2166136261u; + for ( ; *s; ++s ) + { + uint32 c = (unsigned char) *s; + // Brutally fast branchless ASCII tolower(): + // if ((c >= 'A') && (c <= 'Z')) c += ('a' - 'A'); + c += (((('A'-1) - c) & (c - ('Z'+1))) >> 26) & 32; + h = (h ^ c) * 16777619; + } + return (h ^ (h << 17)) + (h >> 21); +} + + +#endif // UTLCOMMON_H diff --git a/r5dev/public/tier1/utldict.h b/r5dev/public/tier1/utldict.h index 81680e87..9d237ca0 100644 --- a/r5dev/public/tier1/utldict.h +++ b/r5dev/public/tier1/utldict.h @@ -31,11 +31,11 @@ enum EDictCompareType // This is a useful macro to iterate from start to end in order in a map #define FOR_EACH_DICT( dictName, iteratorName ) \ - for( int iteratorName=dictName.First(); iteratorName != dictName.InvalidIndex(); iteratorName = dictName.Next( iteratorName ) ) + for( decltype(dictName)::IndexType_t iteratorName=dictName.First(); iteratorName != dictName.InvalidIndex(); iteratorName = dictName.Next( iteratorName ) ) // faster iteration, but in an unspecified order #define FOR_EACH_DICT_FAST( dictName, iteratorName ) \ - for ( int iteratorName = 0; iteratorName < dictName.MaxElement(); ++iteratorName ) if ( !dictName.IsValidIndex( iteratorName ) ) continue; else + for ( decltype(dictName)::IndexType_t iteratorName = 0; iteratorName < dictName.MaxElement(); ++iteratorName ) if ( !dictName.IsValidIndex( iteratorName ) ) continue; else //----------------------------------------------------------------------------- // A dictionary mapping from symbol to structure @@ -46,14 +46,15 @@ class CUtlDict public: typedef const char* KeyType_t; typedef T ElemType_t; + typedef I IndexType_t; // constructor, destructor // Left at growSize = 0, the memory will first allocate 1 element and double in size // at each increment. - CUtlDict( int compareType = k_eDictCompareTypeCaseInsensitive, int growSize = 0, int initSize = 0 ); + CUtlDict( EDictCompareType compareType = k_eDictCompareTypeCaseInsensitive, I growSize = 0, I initSize = 0 ); ~CUtlDict( ); - void EnsureCapacity( int ); + void EnsureCapacity( I ); // gets particular elements T& Element( I i ); @@ -68,7 +69,7 @@ public: void SetElementName( I i, char const *pName ); // Number of elements - unsigned int Count() const; + I Count() const; // Number of allocated slots I MaxElement() const; @@ -114,7 +115,7 @@ protected: // constructor, destructor //----------------------------------------------------------------------------- template -CUtlDict::CUtlDict( int compareType, int growSize, int initSize ) : m_Elements( growSize, initSize ) +CUtlDict::CUtlDict( EDictCompareType compareType, I growSize, I initSize ) : m_Elements( growSize, initSize ) { if ( compareType == k_eDictCompareTypeFilenames ) { @@ -137,7 +138,7 @@ CUtlDict::~CUtlDict() } template -inline void CUtlDict::EnsureCapacity( int num ) +inline void CUtlDict::EnsureCapacity( I num ) { return m_Elements.EnsureCapacity( num ); } @@ -199,7 +200,7 @@ inline void CUtlDict::SetElementName( I i, char const *pName ) // Num elements //----------------------------------------------------------------------------- template -inline unsigned int CUtlDict::Count() const +inline I CUtlDict::Count() const { return m_Elements.Count(); } diff --git a/r5dev/public/tier1/utlhash.h b/r5dev/public/tier1/utlhash.h new file mode 100644 index 00000000..014504e5 --- /dev/null +++ b/r5dev/public/tier1/utlhash.h @@ -0,0 +1,1299 @@ +//====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======// +// +// Purpose: +// +// $NoKeywords: $ +// +// Serialization/unserialization buffer +//=============================================================================// + +#ifndef UTLHASH_H +#define UTLHASH_H +#pragma once + +#include +#include "tier0/commonmacros.h" +#include "utlmemory.h" +#include "utlvector.h" +#include "utllinkedlist.h" +#include "utllinkedlist.h" +#include "generichash.h" + +typedef unsigned int UtlHashHandle_t; + +template +class CUtlHash +{ +public: + // compare and key functions - implemented by the + typedef C CompareFunc_t; + typedef K KeyFunc_t; + + // constructor/deconstructor + explicit CUtlHash( int bucketCount = 0, int growCount = 0, int initCount = 0, + CompareFunc_t compareFunc = 0, KeyFunc_t keyFunc = 0 ); + ~CUtlHash(); + + // invalid handle + static UtlHashHandle_t InvalidHandle( void ) { return ( UtlHashHandle_t )~0; } + bool IsValidHandle( UtlHashHandle_t handle ) const; + + // size + int Count( void ) const; + + // memory + void Purge( void ); + + // insertion methods + UtlHashHandle_t Insert( Data const &src ); + UtlHashHandle_t Insert( Data const &src, bool *pDidInsert ); + UtlHashHandle_t AllocEntryFromKey( Data const &src ); + + // removal methods + void Remove( UtlHashHandle_t handle ); + void RemoveAll(); + + // retrieval methods + UtlHashHandle_t Find( Data const &src ) const; + + Data &Element( UtlHashHandle_t handle ); + Data const &Element( UtlHashHandle_t handle ) const; + Data &operator[]( UtlHashHandle_t handle ); + Data const &operator[]( UtlHashHandle_t handle ) const; + + UtlHashHandle_t GetFirstHandle() const; + UtlHashHandle_t GetNextHandle( UtlHashHandle_t h ) const; + + // debugging!! + void Log( const char *filename ); + void Dump(); + +protected: + + int GetBucketIndex( UtlHashHandle_t handle ) const; + int GetKeyDataIndex( UtlHashHandle_t handle ) const; + UtlHashHandle_t BuildHandle( int ndxBucket, int ndxKeyData ) const; + + bool DoFind( Data const &src, unsigned int *pBucket, int *pIndex ) const; + +protected: + + // handle upper 16 bits = bucket index (bucket heads) + // handle lower 16 bits = key index (bucket list) + typedef CUtlVector HashBucketList_t; + CUtlVector m_Buckets; + + CompareFunc_t m_CompareFunc; // function used to handle unique compares on data + KeyFunc_t m_KeyFunc; // function used to generate the key value + + bool m_bPowerOfTwo; // if the bucket value is a power of two, + unsigned int m_ModMask; // use the mod mask to "mod" +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +CUtlHash::CUtlHash( int bucketCount, int growCount, int initCount, + CompareFunc_t compareFunc, KeyFunc_t keyFunc ) : + m_CompareFunc( compareFunc ), + m_KeyFunc( keyFunc ) +{ + bucketCount = Min(bucketCount, 65536); + m_Buckets.SetSize( bucketCount ); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].SetSize( initCount ); + m_Buckets[ndxBucket].SetGrowSize( growCount ); + } + + // check to see if the bucket count is a power of 2 and set up + // optimizations appropriately + m_bPowerOfTwo = IsPowerOfTwo( bucketCount ); + m_ModMask = m_bPowerOfTwo ? (bucketCount-1) : 0; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +CUtlHash::~CUtlHash() +{ + Purge(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CUtlHash::IsValidHandle( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + // ndxBucket and ndxKeyData can't possibly be less than zero -- take a + // look at the definition of the Get..Index functions for why. However, + // if you override those functions, you will need to override this one + // as well. + if( /*( ndxBucket >= 0 ) && */ ( ndxBucket < m_Buckets.Count() ) ) + { + if( /*( ndxKeyData >= 0 ) && */ ( ndxKeyData < m_Buckets[ndxBucket].Count() ) ) + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::Count( void ) const +{ + int count = 0; + + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + count += m_Buckets[ndxBucket].Count(); + } + + return count; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::GetBucketIndex( UtlHashHandle_t handle ) const +{ + return ( ( ( handle >> 16 ) & 0x0000ffff ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline int CUtlHash::GetKeyDataIndex( UtlHashHandle_t handle ) const +{ + return ( handle & 0x0000ffff ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::BuildHandle( int ndxBucket, int ndxKeyData ) const +{ + Assert( ( ndxBucket >= 0 ) && ( ndxBucket < 65536 ) ); + Assert( ( ndxKeyData >= 0 ) && ( ndxKeyData < 65536 ) ); + + UtlHashHandle_t handle = ndxKeyData; + handle |= ( ndxBucket << 16 ); + + return handle; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Purge( void ) +{ + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].Purge(); + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline bool CUtlHash::DoFind( Data const &src, unsigned int *pBucket, int *pIndex ) const +{ + // generate the data "key" + unsigned int key = m_KeyFunc( src ); + + // hash the "key" - get the correct hash table "bucket" + unsigned int ndxBucket; + if( m_bPowerOfTwo ) + { + *pBucket = ndxBucket = ( key & m_ModMask ); + } + else + { + int bucketCount = m_Buckets.Count(); + *pBucket = ndxBucket = key % bucketCount; + } + + int ndxKeyData = 0; + const CUtlVector &bucket = m_Buckets[ndxBucket]; + int keyDataCount = bucket.Count(); + for( ndxKeyData = 0; ndxKeyData < keyDataCount; ndxKeyData++ ) + { + if( m_CompareFunc( bucket.Element( ndxKeyData ), src ) ) + break; + } + + if( ndxKeyData == keyDataCount ) + return false; + + *pIndex = ndxKeyData; + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Find( Data const &src ) const +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + return ( InvalidHandle() ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Insert( Data const &src ) +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + ndxKeyData = m_Buckets[ndxBucket].AddToTail( src ); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::Insert( Data const &src, bool *pDidInsert ) +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + *pDidInsert = false; + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + *pDidInsert = true; + ndxKeyData = m_Buckets[ndxBucket].AddToTail( src ); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::AllocEntryFromKey( Data const &src ) +{ + unsigned int ndxBucket; + int ndxKeyData = 0; + + if ( DoFind( src, &ndxBucket, &ndxKeyData ) ) + { + return ( BuildHandle( ndxBucket, ndxKeyData ) ); + } + + ndxKeyData = m_Buckets[ndxBucket].AddToTail(); + + return ( BuildHandle( ndxBucket, ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Remove( UtlHashHandle_t handle ) +{ + Assert( IsValidHandle( handle ) ); + + // check to see if the bucket exists + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + if( m_Buckets[ndxBucket].IsValidIndex( ndxKeyData ) ) + { + m_Buckets[ndxBucket].FastRemove( ndxKeyData ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::RemoveAll() +{ + int bucketCount = m_Buckets.Count(); + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + m_Buckets[ndxBucket].RemoveAll(); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data &CUtlHash::Element( UtlHashHandle_t handle ) +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data const &CUtlHash::Element( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data &CUtlHash::operator[]( UtlHashHandle_t handle ) +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline Data const &CUtlHash::operator[]( UtlHashHandle_t handle ) const +{ + int ndxBucket = GetBucketIndex( handle ); + int ndxKeyData = GetKeyDataIndex( handle ); + + return ( m_Buckets[ndxBucket].Element( ndxKeyData ) ); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline UtlHashHandle_t CUtlHash::GetFirstHandle() const +{ + return GetNextHandle( ( UtlHashHandle_t )-1 ); +} + +template +inline UtlHashHandle_t CUtlHash::GetNextHandle( UtlHashHandle_t handle ) const +{ + ++handle; // start at the first possible handle after the one given + + int bi = GetBucketIndex( handle ); + int ki = GetKeyDataIndex( handle ); + + int nBuckets = m_Buckets.Count(); + for ( ; bi < nBuckets; ++bi ) + { + if ( ki < m_Buckets[ bi ].Count() ) + return BuildHandle( bi, ki ); + + ki = 0; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Log( const char *filename ) +{ + FILE *pDebugFp; + pDebugFp = fopen( filename, "w" ); + if( !pDebugFp ) + return; + + int maxBucketSize = 0; + int numBucketsEmpty = 0; + + int bucketCount = m_Buckets.Count(); + fprintf( pDebugFp, "\n%d Buckets\n", bucketCount ); + + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + int count = m_Buckets[ndxBucket].Count(); + + if( count > maxBucketSize ) { maxBucketSize = count; } + if( count == 0 ) + numBucketsEmpty++; + + fprintf( pDebugFp, "Bucket %d: %d\n", ndxBucket, count ); + } + + fprintf( pDebugFp, "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty ); + fprintf( pDebugFp, "Max Bucket Size: %d\n", maxBucketSize ); + + fclose( pDebugFp ); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +inline void CUtlHash::Dump( ) +{ + int maxBucketSize = 0; + int numBucketsEmpty = 0; + + int bucketCount = m_Buckets.Count(); + Msg( eDLL_T::COMMON, "\n%d Buckets\n", bucketCount ); + + for( int ndxBucket = 0; ndxBucket < bucketCount; ndxBucket++ ) + { + int count = m_Buckets[ndxBucket].Count(); + + if( count > maxBucketSize ) { maxBucketSize = count; } + if( count == 0 ) + numBucketsEmpty++; + + Msg( eDLL_T::COMMON, "Bucket %d: %d\n", ndxBucket, count ); + } + + Msg( eDLL_T::COMMON, "\nBucketHeads Used: %d\n", bucketCount - numBucketsEmpty ); + Msg( eDLL_T::COMMON, "Max Bucket Size: %d\n", maxBucketSize ); +} + +//============================================================================= +// +// Fast Hash +// +// Number of buckets must be a power of 2. +// Key must be 32-bits (unsigned int). +// +typedef intp UtlHashFastHandle_t; + +#define UTLHASH_POOL_SCALAR 2 + +class CUtlHashFastNoHash +{ +public: + static int Hash( int key, int bucketMask ) + { + return ( key & bucketMask ); + } +}; + +class CUtlHashFastGenericHash +{ +public: + static int Hash( int key, int bucketMask ) + { + return ( HashIntConventional( key ) & bucketMask ); + } +}; + +template +class CUtlHashFast +{ +public: + + // Constructor/Deconstructor. + CUtlHashFast(); + ~CUtlHashFast(); + + // Memory. + void Purge( void ); + + // Invalid handle. + static UtlHashFastHandle_t InvalidHandle( void ) { return ( UtlHashFastHandle_t )~0; } + inline bool IsValidHandle( UtlHashFastHandle_t hHash ) const; + + // Initialize. + bool Init( int nBucketCount ); + + // Size not available; count is meaningless for multilists. + // int Count( void ) const; + + // Insertion. + UtlHashFastHandle_t Insert( uintp uiKey, const Data &data ); + UtlHashFastHandle_t FastInsert( uintp uiKey, const Data &data ); + + // Removal. + void Remove( UtlHashFastHandle_t hHash ); + void RemoveAll( void ); + + // Retrieval. + UtlHashFastHandle_t Find( uintp uiKey ) const; + + Data &Element( UtlHashFastHandle_t hHash ); + Data const &Element( UtlHashFastHandle_t hHash ) const; + Data &operator[]( UtlHashFastHandle_t hHash ); + Data const &operator[]( UtlHashFastHandle_t hHash ) const; + + // Iteration + struct UtlHashFastIterator_t + { + int bucket; + UtlHashFastHandle_t handle; + + UtlHashFastIterator_t(int _bucket, const UtlHashFastHandle_t &_handle) + : bucket(_bucket), handle(_handle) {}; + // inline operator UtlHashFastHandle_t() const { return handle; }; + }; + inline UtlHashFastIterator_t First() const; + inline UtlHashFastIterator_t Next( const UtlHashFastIterator_t &hHash ) const; + inline bool IsValidIterator( const UtlHashFastIterator_t &iter ) const; + inline Data &operator[]( const UtlHashFastIterator_t &iter ) { return (*this)[iter.handle]; } + inline Data const &operator[]( const UtlHashFastIterator_t &iter ) const { return (*this)[iter.handle]; } + +//protected: + + // Templatized for memory tracking purposes + template + struct HashFastData_t_ + { + uintp m_uiKey; + HashData m_Data; + }; + + typedef HashFastData_t_ HashFastData_t; + + uintp m_uiBucketMask; + CUtlVector m_aBuckets; + CUtlFixedLinkedList m_aDataPool; + +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template CUtlHashFast::CUtlHashFast() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor +//----------------------------------------------------------------------------- +template CUtlHashFast::~CUtlHashFast() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy dynamically allocated hash data. +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::Purge( void ) +{ + m_aBuckets.Purge(); + m_aDataPool.Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Initialize the hash - set bucket count and hash grow amount. +//----------------------------------------------------------------------------- +template bool CUtlHashFast::Init( int nBucketCount ) +{ + // Verify the bucket count is power of 2. + if ( !IsPowerOfTwo( nBucketCount ) ) + return false; + + // Set the bucket size. + m_aBuckets.SetSize( nBucketCount ); + for ( int iBucket = 0; iBucket < nBucketCount; ++iBucket ) + { + m_aBuckets[iBucket] = m_aDataPool.InvalidIndex(); + } + + // Set the mod mask. + m_uiBucketMask = nBucketCount - 1; + + // Calculate the grow size. + int nGrowSize = UTLHASH_POOL_SCALAR * nBucketCount; + m_aDataPool.SetGrowSize( nGrowSize ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Return the number of elements in the hash. +// Not available because count isn't accurately maintained for multilists. +//----------------------------------------------------------------------------- +/* +template inline int CUtlHashFast::Count( void ) const +{ + return m_aDataPool.Count(); +} +*/ + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (uintp), with +// a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::Insert( uintp uiKey, const Data &data ) +{ + // Check to see if that key already exists in the buckets (should be unique). + UtlHashFastHandle_t hHash = Find( uiKey ); + if( hHash != InvalidHandle() ) + return hHash; + + return FastInsert( uiKey, data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (uintp), +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::FastInsert( uintp uiKey, const Data &data ) +{ + // Get a new element from the pool. + intp iHashData = m_aDataPool.Alloc( true ); + HashFastData_t *pHashData = &m_aDataPool[iHashData]; + if ( !pHashData ) + return InvalidHandle(); + + // Add data to new element. + pHashData->m_uiKey = uiKey; + pHashData->m_Data = data; + + // Link element. + int iBucket = HashFuncs::Hash( uiKey, m_uiBucketMask ); + m_aDataPool.LinkBefore( m_aBuckets[iBucket], iHashData ); + m_aBuckets[iBucket] = iHashData; + + return iHashData; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a given element from the hash. +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::Remove( UtlHashFastHandle_t hHash ) +{ + int iBucket = HashFuncs::Hash( m_aDataPool[hHash].m_uiKey, m_uiBucketMask ); + if ( m_aBuckets[iBucket] == hHash ) + { + // It is a bucket head. + m_aBuckets[iBucket] = m_aDataPool.Next( hHash ); + } + else + { + // Not a bucket head. + m_aDataPool.Unlink( hHash ); + } + + // Remove the element. + m_aDataPool.Remove( hHash ); +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +template inline void CUtlHashFast::RemoveAll( void ) +{ + m_aBuckets.RemoveAll(); + m_aDataPool.RemoveAll(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template inline UtlHashFastHandle_t CUtlHashFast::Find( uintp uiKey ) const +{ + // hash the "key" - get the correct hash table "bucket" + int iBucket = HashFuncs::Hash( uiKey, m_uiBucketMask ); + + for ( intp iElement = m_aBuckets[iBucket]; iElement != m_aDataPool.InvalidIndex(); iElement = m_aDataPool.Next( iElement ) ) + { + if ( m_aDataPool[iElement].m_uiKey == uiKey ) + return iElement; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFast::Element( UtlHashFastHandle_t hHash ) +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFast::Element( UtlHashFastHandle_t hHash ) const +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFast::operator[]( UtlHashFastHandle_t hHash ) +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFast::operator[]( UtlHashFastHandle_t hHash ) const +{ + return ( m_aDataPool[hHash].m_Data ); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the index of the first element +//----------------------------------------------------------------------------- +template + typename CUtlHashFast::UtlHashFastIterator_t + CUtlHashFast::First() const +{ + // walk through the buckets to find the first one that has some data + int bucketCount = m_aBuckets.Count(); + const UtlHashFastHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + for ( int bucket = 0 ; bucket < bucketCount ; ++bucket ) + { + UtlHashFastHandle_t iElement = m_aBuckets[bucket]; // get the head of the bucket + if (iElement != invalidIndex) + return UtlHashFastIterator_t(bucket,iElement); + } + + // if we are down here, the list is empty + return UtlHashFastIterator_t(-1, invalidIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: For iterating over the whole hash, return the next element after +// the param one. Or an invalid iterator. +//----------------------------------------------------------------------------- +template + typename CUtlHashFast::UtlHashFastIterator_t + CUtlHashFast::Next( const typename CUtlHashFast::UtlHashFastIterator_t &iter ) const +{ + // look for the next entry in the current bucket + UtlHashFastHandle_t next = m_aDataPool.Next(iter.handle); + const UtlHashFastHandle_t invalidIndex = m_aDataPool.InvalidIndex(); + if (next != invalidIndex) + { + // this bucket still has more elements in it + return UtlHashFastIterator_t(iter.bucket, next); + } + + // otherwise look for the next bucket with data + int bucketCount = m_aBuckets.Count(); + for ( int bucket = iter.bucket+1 ; bucket < bucketCount ; ++bucket ) + { + UtlHashFastHandle_t next = m_aBuckets[bucket]; // get the head of the bucket + if (next != invalidIndex) + return UtlHashFastIterator_t( bucket, next ); + } + + // if we're here, there's no more data to be had + return UtlHashFastIterator_t(-1, invalidIndex); +} + +template + bool CUtlHashFast::IsValidIterator( const typename CUtlHashFast::UtlHashFastIterator_t &iter ) const +{ + return ( (iter.bucket >= 0) && (m_aDataPool.IsValidIndex(iter.handle)) ); +} + + +template inline bool CUtlHashFast::IsValidHandle( UtlHashFastHandle_t hHash ) const +{ + return m_aDataPool.IsValidIndex(hHash); +} + +//============================================================================= +// +// Fixed Hash +// +// Number of buckets must be a power of 2. +// Key must be pointer-size (uintp). +// +typedef intp UtlHashFixedHandle_t; + +template +class CUtlHashFixedGenericHash +{ +public: + static int Hash( int key, int bucketMask ) + { + int hash = HashIntConventional( key ); + if ( NUM_BUCKETS <= USHRT_MAX ) + { + hash ^= ( hash >> 16 ); + } + if ( NUM_BUCKETS <= UCHAR_MAX ) + { + hash ^= ( hash >> 8 ); + } + return ( hash & bucketMask ); + } +}; + +template +class CUtlHashFixed +{ +public: + + // Constructor/Deconstructor. + CUtlHashFixed(); + ~CUtlHashFixed(); + + // Memory. + void Purge( void ); + + // Invalid handle. + static UtlHashFixedHandle_t InvalidHandle( void ) { return ( UtlHashFixedHandle_t )~0; } + + // Size. + int Count( void ); + + // Insertion. + UtlHashFixedHandle_t Insert( unsigned int uiKey, const Data &data ); + UtlHashFixedHandle_t FastInsert( unsigned int uiKey, const Data &data ); + + // Removal. + void Remove( UtlHashFixedHandle_t hHash ); + void RemoveAll( void ); + + // Retrieval. + UtlHashFixedHandle_t Find( unsigned int uiKey ); + + Data &Element( UtlHashFixedHandle_t hHash ); + Data const &Element( UtlHashFixedHandle_t hHash ) const; + Data &operator[]( UtlHashFixedHandle_t hHash ); + Data const &operator[]( UtlHashFixedHandle_t hHash ) const; + + //protected: + + // Templatized for memory tracking purposes + template + struct HashFixedData_t_ + { + unsigned int m_uiKey; + Data_t m_Data; + }; + + typedef HashFixedData_t_ HashFixedData_t; + + enum + { + BUCKET_MASK = NUM_BUCKETS - 1 + }; + CUtlPtrLinkedList m_aBuckets[NUM_BUCKETS]; + int m_nElements; +}; + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +template CUtlHashFixed::CUtlHashFixed() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Deconstructor +//----------------------------------------------------------------------------- +template CUtlHashFixed::~CUtlHashFixed() +{ + Purge(); +} + +//----------------------------------------------------------------------------- +// Purpose: Destroy dynamically allocated hash data. +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::Purge( void ) +{ + RemoveAll(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return the number of elements in the hash. +//----------------------------------------------------------------------------- +template inline int CUtlHashFixed::Count( void ) +{ + return m_nElements; +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), with +// a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::Insert( unsigned int uiKey, const Data &data ) +{ + // Check to see if that key already exists in the buckets (should be unique). + UtlHashFixedHandle_t hHash = Find( uiKey ); + if( hHash != InvalidHandle() ) + return hHash; + + return FastInsert( uiKey, data ); +} + +//----------------------------------------------------------------------------- +// Purpose: Insert data into the hash table given its key (unsigned int), +// without a check to see if the element already exists within the tree. +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::FastInsert( unsigned int uiKey, const Data &data ) +{ + int iBucket = HashFuncs::Hash( uiKey, NUM_BUCKETS - 1 ); + UtlPtrLinkedListIndex_t iElem = m_aBuckets[iBucket].AddToHead(); + + HashFixedData_t *pHashData = &m_aBuckets[iBucket][iElem]; + + Assert( (UtlPtrLinkedListIndex_t)pHashData == iElem ); + + // Add data to new element. + pHashData->m_uiKey = uiKey; + pHashData->m_Data = data; + + m_nElements++; + return (UtlHashFixedHandle_t)pHashData; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove a given element from the hash. +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::Remove( UtlHashFixedHandle_t hHash ) +{ + HashFixedData_t *pHashData = (HashFixedData_t *)hHash; + Assert( Find(pHashData->m_uiKey) != InvalidHandle() ); + int iBucket = HashFuncs::Hash( pHashData->m_uiKey, NUM_BUCKETS - 1 ); + m_aBuckets[iBucket].Remove( (UtlPtrLinkedListIndex_t)pHashData ); + m_nElements--; +} + +//----------------------------------------------------------------------------- +// Purpose: Remove all elements from the hash +//----------------------------------------------------------------------------- +template inline void CUtlHashFixed::RemoveAll( void ) +{ + for ( int i = 0; i < NUM_BUCKETS; i++ ) + { + m_aBuckets[i].RemoveAll(); + } + m_nElements = 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template inline UtlHashFixedHandle_t CUtlHashFixed::Find( unsigned int uiKey ) +{ + int iBucket = HashFuncs::Hash( uiKey, NUM_BUCKETS - 1 ); + CUtlPtrLinkedList &bucket = m_aBuckets[iBucket]; + + for ( UtlPtrLinkedListIndex_t iElement = bucket.Head(); iElement != bucket.InvalidIndex(); iElement = bucket.Next( iElement ) ) + { + if ( bucket[iElement].m_uiKey == uiKey ) + return (UtlHashFixedHandle_t)iElement; + } + + return InvalidHandle(); +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFixed::Element( UtlHashFixedHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFixed::Element( UtlHashFixedHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data &CUtlHashFixed::operator[]( UtlHashFixedHandle_t hHash ) +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +//----------------------------------------------------------------------------- +// Purpose: Return data given a hash handle. +//----------------------------------------------------------------------------- +template inline Data const &CUtlHashFixed::operator[]( UtlHashFixedHandle_t hHash ) const +{ + return ((HashFixedData_t *)hHash)->m_Data; +} + +class CDefaultHash32 +{ +public: + static inline uint32 HashKey32( uint32 nKey ) { return HashIntConventional(nKey); } +}; + +class CPassthroughHash32 +{ +public: + static inline uint32 HashKey32( uint32 nKey ) { return nKey; } +}; + + +// This is a simpler hash for scalar types that stores the entire hash + buckets in a single linear array +// This is much more cache friendly for small (e.g. 32-bit) types stored in the hash +template +class CUtlScalarHash +{ +public: + + // Constructor/Destructor. + CUtlScalarHash(); + ~CUtlScalarHash(); + + // Memory. + // void Purge( void ); + + // Invalid handle. + static const UtlHashFastHandle_t InvalidHandle( void ) { return (unsigned int)~0; } + + // Initialize. + bool Init( int nBucketCount ); + + // Size. + int Count( void ) const { return m_dataCount; } + + // Insertion. + UtlHashFastHandle_t Insert( unsigned int uiKey, const Data &data ); + + // Removal. + void FindAndRemove( unsigned int uiKey, const Data &dataRecord ); + void Remove( UtlHashFastHandle_t hHash ); + void RemoveAll( void ); + void Grow(); + + // Retrieval. Finds by uiKey and then by comparing dataRecord + UtlHashFastHandle_t Find( unsigned int uiKey, const Data &dataRecord ) const; + UtlHashFastHandle_t FindByUniqueKey( unsigned int uiKey ) const; + + Data &Element( UtlHashFastHandle_t hHash ) { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data const &Element( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data &operator[]( UtlHashFastHandle_t hHash ) { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + Data const &operator[]( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_Data; } + + unsigned int Key( UtlHashFastHandle_t hHash ) const { Assert(unsigned(hHash)<=m_uiBucketMask); return m_pData[hHash].m_uiKey; } + + UtlHashFastHandle_t FirstInorder() const + { + return NextInorder(-1); + } + UtlHashFastHandle_t NextInorder( UtlHashFastHandle_t nStart ) const + { + int nElementCount = m_maxData * 2; + unsigned int nUnusedListElement = (unsigned int)InvalidHandle(); + for ( int i = nStart+1; i < nElementCount; i++ ) + { + if ( m_pData[i].m_uiKey != nUnusedListElement ) + return i; + } + return nUnusedListElement; + } + + //protected: + + struct HashScalarData_t + { + unsigned int m_uiKey; + Data m_Data; + }; + + unsigned int m_uiBucketMask; + HashScalarData_t *m_pData; + int m_maxData; + int m_dataCount; +}; + +template CUtlScalarHash::CUtlScalarHash() +{ + m_pData = NULL; + m_uiBucketMask = 0; + m_maxData = 0; + m_dataCount = 0; +} + +template CUtlScalarHash::~CUtlScalarHash() +{ + delete[] m_pData; +} + +template bool CUtlScalarHash::Init( int nBucketCount ) +{ + Assert(m_dataCount==0); + m_maxData = SmallestPowerOfTwoGreaterOrEqual(nBucketCount); + int elementCount = m_maxData * 2; + m_pData = new HashScalarData_t[elementCount]; + m_uiBucketMask = elementCount - 1; + RemoveAll(); + return true; +} + +template void CUtlScalarHash::Grow() +{ + ASSERT_NO_REENTRY(); + int oldElementCount = m_maxData * 2; + HashScalarData_t *pOldData = m_pData; + + // Grow to a minimum size of 16 + m_maxData = Max( oldElementCount, 16 ); + int elementCount = m_maxData * 2; + m_pData = new HashScalarData_t[elementCount]; + m_uiBucketMask = elementCount-1; + m_dataCount = 0; + for ( int i = 0; i < elementCount; i++ ) + { + m_pData[i].m_uiKey = InvalidHandle(); + } + for ( int i = 0; i < oldElementCount; i++ ) + { + if ( pOldData[i].m_uiKey != (unsigned)InvalidHandle() ) + { + Insert( pOldData[i].m_uiKey, pOldData[i].m_Data ); + } + } + delete[] pOldData; +} + +template UtlHashFastHandle_t CUtlScalarHash::Insert( unsigned int uiKey, const Data &data ) +{ + if ( m_dataCount >= m_maxData ) + { + Grow(); + } + m_dataCount++; + Assert(uiKey != (uint)InvalidHandle()); // This hash stores less data by assuming uiKey != ~0 + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + index = (index+1) & m_uiBucketMask; + } + m_pData[index].m_uiKey = uiKey; + m_pData[index].m_Data = data; + + return index; +} + +// Removal. +template void CUtlScalarHash::Remove( UtlHashFastHandle_t hHash ) +{ + int mid = (m_uiBucketMask+1) / 2; + int lastRemoveIndex = hHash; + // remove the item + m_pData[lastRemoveIndex].m_uiKey = InvalidHandle(); + m_dataCount--; + + // now search for any items needing to be swapped down + unsigned int endOfList = (unsigned int)InvalidHandle(); + for ( int index = (hHash+1) & m_uiBucketMask; m_pData[index].m_uiKey != endOfList; index = (index+1) & m_uiBucketMask ) + { + int ideal = CHashFunction::HashKey32(m_pData[index].m_uiKey) & m_uiBucketMask; + + // is the ideal index for this element <= (in a wrapped buffer sense) the ideal index of the removed element? + // if so, swap + int diff = ideal - lastRemoveIndex; + if ( diff > mid ) + { + diff -= (m_uiBucketMask+1); + } + if ( diff < -mid ) + { + diff += (m_uiBucketMask+1); + } + + // should I swap this? + if ( diff <= 0 ) + { + m_pData[lastRemoveIndex] = m_pData[index]; + lastRemoveIndex = index; + m_pData[index].m_uiKey = InvalidHandle(); + } + } +} + +template void CUtlScalarHash::FindAndRemove( unsigned int uiKey, const Data &dataRecord ) +{ + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey && m_pData[index].m_Data == dataRecord ) + { + Remove(index); + return; + } + index = (index+1) & m_uiBucketMask; + } +} + +template void CUtlScalarHash::RemoveAll( void ) +{ + int elementCount = m_maxData * 2; + for ( int i = 0; i < elementCount; i++ ) + { + m_pData[i].m_uiKey = (unsigned)InvalidHandle(); + } + m_dataCount = 0; +} + +// Retrieval. +template UtlHashFastHandle_t CUtlScalarHash::Find( unsigned int uiKey, const Data &dataRecord ) const +{ + if ( m_pData == NULL ) + return InvalidHandle(); + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey && m_pData[index].m_Data == dataRecord ) + return index; + index = (index+1) & m_uiBucketMask; + } + return InvalidHandle(); +} + +template UtlHashFastHandle_t CUtlScalarHash::FindByUniqueKey( unsigned int uiKey ) const +{ + if ( m_pData == NULL ) + return InvalidHandle(); + int index = CHashFunction::HashKey32(uiKey) & m_uiBucketMask; + unsigned int endOfList = (unsigned int)InvalidHandle(); + while ( m_pData[index].m_uiKey != endOfList ) + { + if ( m_pData[index].m_uiKey == uiKey ) + return index; + index = (index+1) & m_uiBucketMask; + } + return InvalidHandle(); +} + + +#endif // UTLHASH_H diff --git a/r5dev/public/tier1/utlhashtable.h b/r5dev/public/tier1/utlhashtable.h new file mode 100644 index 00000000..44c6ecde --- /dev/null +++ b/r5dev/public/tier1/utlhashtable.h @@ -0,0 +1,997 @@ +//========= Copyright � 2011, Valve Corporation, All rights reserved. ============// +// +// Purpose: a fast growable hashtable with stored hashes, L2-friendly behavior. +// Useful as a string dictionary or a low-overhead set/map for small POD types. +// +// Usage notes: +// - handles are NOT STABLE across element removal! use RemoveAndAdvance() +// if you are removing elements while iterating through the hashtable. +// Use CUtlStableHashtable if you need stable handles (less efficient). +// - handles are also NOT STABLE across element insertion. The handle +// resulting from the insertion of an element may not retreive the +// same (or any!) element after further insertions. Again, use +// CUtlStableHashtable if you need stable handles +// - Insert() first searches for an existing match and returns it if found +// - a value type of "empty_t" can be used to eliminate value storage and +// switch Element() to return const Key references instead of values +// - an extra user flag bit is accessible via Get/SetUserFlag() +// - hash function pointer / functor is exposed via GetHashRef() +// - comparison function pointer / functor is exposed via GetEqualRef() +// - if your value type cannot be copy-constructed, use key-only Insert() +// to default-initialize the value and then manipulate it afterwards. +// - The reason that UtlHashtable permutes itself and invalidates +// iterators is to make it faster in the case where you are not +// tracking iterators. If you use it as a set or a map ("is this +// value a member?") as opposed to a long-term container, then you +// probably don't need stable iterators. Hashtable tries to place +// newly inserted data in the primary hash slot, making an +// assumption that if you inserted it recently, you're more likely +// to access it than if you inserted something a long time +// ago. It's effectively trying to minimize cache misses for hot +// data if you add and remove a lot. +// If you don't care too much about cache misses, UtlStableHashtable +// is what you're looking for +// +// Implementation notes: +// - overall hash table load is kept between .25 and .75 +// - items which would map to the same ideal slot are chained together +// - chained items are stored sequentially in adjacent free spaces +// - "root" entries are prioritized over chained entries; if a +// slot is not occupied by an item in its root position, the table +// is guaranteed to contain no keys which would hash to that slot. +// - new items go at the head of the chain (ie, in their root slot) +// and evict / "bump" any chained entries which occupy that slot +// - chain-following skips over unused holes and continues examining +// table entries until a chain entry with FLAG_LAST is encountered +// +// CUtlHashtable< uint32 > setOfIntegers; +// CUtlHashtable< const char* > setOfStringPointers; +// CUtlHashtable< int, CUtlVector > mapFromIntsToArrays; +// +// $NoKeywords: $ +// +// A closed-form (open addressing) hashtable with linear sequential probing. +//=============================================================================// + +#ifndef UTLHASHTABLE_H +#define UTLHASHTABLE_H +#pragma once + +#include "utlcommon.h" +#include "utlmemory.h" +#include "mathlib/mathlib.h" +#include "utllinkedlist.h" + +//----------------------------------------------------------------------------- +// Henry Goffin (henryg) was here. Questions? Bugs? Go slap him around a bit. +//----------------------------------------------------------------------------- + +typedef unsigned int UtlHashHandle_t; + +#define FOR_EACH_HASHTABLE( table, iter ) \ + for ( UtlHashHandle_t iter = (table).FirstHandle(); iter != (table).InvalidHandle(); iter = (table).NextHandle( iter ) ) + +// CUtlHashtableEntry selects between 16 and 32 bit storage backing +// for flags_and_hash depending on the size of the stored types. +template < typename KeyT, typename ValueT = empty_t > +class CUtlHashtableEntry +{ +public: + typedef CUtlKeyValuePair< KeyT, ValueT > KVPair; + + enum { INT16_STORAGE = ( sizeof( KVPair ) <= 2 ) }; + typedef typename CTypeSelect< INT16_STORAGE, int16, int32 >::type storage_t; + + enum + { + FLAG_FREE = INT16_STORAGE ? 0x8000 : 0x80000000, // must be high bit for IsValid and IdealIndex to work + FLAG_LAST = INT16_STORAGE ? 0x4000 : 0x40000000, + MASK_HASH = INT16_STORAGE ? 0x3FFF : 0x3FFFFFFF + }; + + storage_t flags_and_hash; + storage_t data[ ( sizeof(KVPair) + sizeof(storage_t) - 1 ) / sizeof(storage_t) ]; + + bool IsValid() const { return flags_and_hash >= 0; } + void MarkInvalid() { int32 flag = FLAG_FREE; flags_and_hash = (storage_t)flag; } + const KVPair *Raw() const { return reinterpret_cast< const KVPair * >( &data[0] ); } + const KVPair *operator->() const { Assert( IsValid() ); return reinterpret_cast< const KVPair * >( &data[0] ); } + KVPair *Raw() { return reinterpret_cast< KVPair * >( &data[0] ); } + KVPair *operator->() { Assert( IsValid() ); return reinterpret_cast< KVPair * >( &data[0] ); } + + // Returns the ideal index of the data in this slot, or all bits set if invalid + uint32 FORCEINLINE IdealIndex( uint32 slotmask ) const { return IdealIndex( flags_and_hash, slotmask ) | ( (int32)flags_and_hash >> 31 ); } + + // Use template tricks to fully define only one function that takes either 16 or 32 bits + // and performs different logic without using "if ( INT16_STORAGE )", because GCC and MSVC + // sometimes have trouble removing the constant branch, which is dumb... but whatever. + // 16-bit hashes are simply too narrow for large hashtables; more mask bits than hash bits! + // So we duplicate the hash bits. (Note: h *= MASK_HASH+2 is the same as h += h<::type uint32_if16BitStorage; + typedef typename CTypeSelect< INT16_STORAGE, undefined_t, int32 >::type uint32_if32BitStorage; + static FORCEINLINE uint32 IdealIndex( uint32_if16BitStorage h, uint32 m ) { h &= MASK_HASH; h *= MASK_HASH + 2; return h & m; } + static FORCEINLINE uint32 IdealIndex( uint32_if32BitStorage h, uint32 m ) { return h & m; } + + // More efficient than memcpy for the small types that are stored in a hashtable + void MoveDataFrom( CUtlHashtableEntry &src ) + { + storage_t * RESTRICT srcData = &src.data[0]; + for ( size_t i = 0; i < ARRAYSIZE( data ); ++i ) { data[i] = srcData[i]; } + } +}; + +template , typename KeyIsEqualT = DefaultEqualFunctor, typename AlternateKeyT = typename ArgumentTypeInfo::Alt_t > +class CUtlHashtable +{ +public: + typedef UtlHashHandle_t handle_t; + +protected: + typedef CUtlKeyValuePair KVPair; + typedef typename ArgumentTypeInfo::Arg_t KeyArg_t; + typedef typename ArgumentTypeInfo::Arg_t ValueArg_t; + typedef typename ArgumentTypeInfo::Arg_t KeyAlt_t; + typedef CUtlHashtableEntry< KeyT, ValueT > entry_t; + + enum { FLAG_FREE = entry_t::FLAG_FREE }; + enum { FLAG_LAST = entry_t::FLAG_LAST }; + enum { MASK_HASH = entry_t::MASK_HASH }; + + CUtlMemory< entry_t > m_table; + int m_nUsed; + int m_nMinSize; + bool m_bSizeLocked; + KeyIsEqualT m_eq; + KeyHashT m_hash; + + // Allocate an empty table and then re-insert all existing entries. + void DoRealloc( int size ); + + // Move an existing entry to a free slot, leaving a hole behind + void BumpEntry( unsigned int idx ); + + // Insert an unconstructed KVPair at the primary slot + int DoInsertUnconstructed( unsigned int h, bool allowGrow ); + + // Implementation for Insert functions, constructs a KVPair + // with either a default-construted or copy-constructed value + template handle_t DoInsert( KeyParamT k, unsigned int h, bool* pDidInsert ); + template handle_t DoInsert( KeyParamT k, typename ArgumentTypeInfo::Arg_t v, unsigned int h, bool* pDidInsert ); + template handle_t DoInsertNoCheck( KeyParamT k, typename ArgumentTypeInfo::Arg_t v, unsigned int h ); + + // Key lookup. Can also return previous-in-chain if result is chained. + template handle_t DoLookup( KeyParamT x, unsigned int h, handle_t *pPreviousInChain ) const; + + // Remove single element by key + hash. Returns the index of the new hole + // that was created. Returns InvalidHandle() if element was not found. + template int DoRemove( KeyParamT x, unsigned int h ); + + // Friend CUtlStableHashtable so that it can call our Do* functions directly + template < typename K, typename V, typename S, typename H, typename E, typename A > friend class CUtlStableHashtable; + +public: + explicit CUtlHashtable( int minimumSize = 32 ) + : m_nUsed(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(), m_hash() { } + + CUtlHashtable( int minimumSize, const KeyHashT &hash, KeyIsEqualT const &eq = KeyIsEqualT() ) + : m_nUsed(0), m_nMinSize(MAX(8, minimumSize)), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { } + + CUtlHashtable( entry_t* pMemory, unsigned int nCount, const KeyHashT &hash = KeyHashT(), KeyIsEqualT const &eq = KeyIsEqualT() ) + : m_nUsed(0), m_nMinSize(8), m_bSizeLocked(false), m_eq(eq), m_hash(hash) { SetExternalBuffer( pMemory, nCount ); } + + ~CUtlHashtable() { RemoveAll(); } + + CUtlHashtable &operator=( CUtlHashtable const &src ); + + // Set external memory + void SetExternalBuffer( byte* pRawBuffer, unsigned int nBytes, bool bAssumeOwnership = false, bool bGrowable = false ); + void SetExternalBuffer( entry_t* pBuffer, unsigned int nSize, bool bAssumeOwnership = false, bool bGrowable = false ); + + // Functor/function-pointer access + KeyHashT& GetHashRef() { return m_hash; } + KeyIsEqualT& GetEqualRef() { return m_eq; } + KeyHashT const &GetHashRef() const { return m_hash; } + KeyIsEqualT const &GetEqualRef() const { return m_eq; } + + // Handle validation + bool IsValidHandle( handle_t idx ) const { return (unsigned)idx < (unsigned)m_table.Count() && m_table[idx].IsValid(); } + static handle_t InvalidHandle() { return (handle_t) -1; } + + // Iteration functions + handle_t FirstHandle() const { return NextHandle( (handle_t) -1 ); } + handle_t NextHandle( handle_t start ) const; + + // Returns the number of unique keys in the table + int Count() const { return m_nUsed; } + + // Key lookup, returns InvalidHandle() if not found + handle_t Find( KeyArg_t k ) const { return DoLookup( k, m_hash(k), NULL ); } + handle_t Find( KeyArg_t k, unsigned int hash) const { Assert( hash == m_hash(k) ); return DoLookup( k, hash, NULL ); } + // Alternate-type key lookup, returns InvalidHandle() if not found + handle_t Find( KeyAlt_t k ) const { return DoLookup( k, m_hash(k), NULL ); } + handle_t Find( KeyAlt_t k, unsigned int hash) const { Assert( hash == m_hash(k) ); return DoLookup( k, hash, NULL ); } + + // True if the key is in the table + bool HasElement( KeyArg_t k ) const { return InvalidHandle() != Find( k ); } + bool HasElement( KeyAlt_t k ) const { return InvalidHandle() != Find( k ); } + + // Key insertion or lookup, always returns a valid handle + // Using a different prototype for InsertIfNotFound since it could be confused with Insert if the ValueArg_t is a bool* + handle_t Insert( KeyArg_t k ) { return DoInsert( k, m_hash(k), nullptr ); } + handle_t InsertIfNotFound( KeyArg_t k, bool* pDidInsert ) { return DoInsert( k, m_hash( k ), pDidInsert ); } + handle_t Insert( KeyArg_t k, ValueArg_t v, bool *pDidInsert = nullptr ) { return DoInsert( k, v, m_hash(k), pDidInsert ); } + handle_t Insert( KeyArg_t k, ValueArg_t v, unsigned int hash, bool *pDidInsert = nullptr ) { Assert( hash == m_hash(k) ); return DoInsert( k, v, hash, pDidInsert ); } + + // Alternate-type key insertion or lookup, always returns a valid handle + // Using a different prototype for InsertIfNotFound since it could be confused with Insert if the ValueArg_t is a bool* + handle_t Insert( KeyAlt_t k ) { return DoInsert( k, m_hash(k), nullptr ); } + handle_t InsertIfNotFound( KeyAlt_t k, bool* pDidInsert ) { return DoInsert( k, m_hash( k ), pDidInsert ); } + handle_t Insert( KeyAlt_t k, ValueArg_t v, bool *pDidInsert = NULL ) { return DoInsert( k, v, m_hash(k), pDidInsert ); } + handle_t Insert( KeyAlt_t k, ValueArg_t v, unsigned int hash, bool *pDidInsert = NULL ) { Assert( hash == m_hash(k) ); return DoInsert( k, v, hash, pDidInsert ); } + + // Key removal, returns false if not found + bool Remove( KeyArg_t k ) { return DoRemove( k, m_hash(k) ) >= 0; } + bool Remove( KeyArg_t k, unsigned int hash ) { Assert( hash == m_hash(k) ); return DoRemove( k, hash ) >= 0; } + // Alternate-type key removal, returns false if not found + bool Remove( KeyAlt_t k ) { return DoRemove( k, m_hash(k) ) >= 0; } + bool Remove( KeyAlt_t k, unsigned int hash ) { Assert( hash == m_hash(k) ); return DoRemove( k, hash ) >= 0; } + + // Remove while iterating, returns the next handle for forward iteration + // Note: aside from this, ALL handles are invalid if an element is removed + handle_t RemoveAndAdvance( handle_t idx ); + + // Remove by handle, convenient when you look up a handle and do something with it before removing the element + void RemoveByHandle( handle_t idx ); + + // Nuke contents + void RemoveAll(); + + // Nuke and release memory. + void Purge() { RemoveAll(); m_table.Purge(); } + + // Reserve table capacity up front to avoid reallocation during insertions + void Reserve( int expected ) { if ( expected > m_nUsed ) DoRealloc( expected * 4 / 3 ); } + + // Shrink to best-fit size, re-insert keys for optimal lookup + void Compact( bool bMinimal ) { DoRealloc( bMinimal ? m_nUsed : ( m_nUsed * 4 / 3 ) ); } + + // Access functions. Note: if ValueT is empty_t, all functions return const keys. + typedef typename KVPair::ValueReturn_t Element_t; + KeyT const &Key( handle_t idx ) const { return m_table[idx]->m_key; } + Element_t const &Element( handle_t idx ) const { return m_table[idx]->GetValue(); } + Element_t &Element(handle_t idx) { return m_table[idx]->GetValue(); } + Element_t const &operator[]( handle_t idx ) const { return m_table[idx]->GetValue(); } + Element_t &operator[]( handle_t idx ) { return m_table[idx]->GetValue(); } + + void ReplaceKey( handle_t idx, KeyArg_t k ) { Assert( m_eq( m_table[idx]->m_key, k ) && m_hash( k ) == m_hash( m_table[idx]->m_key ) ); m_table[idx]->m_key = k; } + void ReplaceKey( handle_t idx, KeyAlt_t k ) { Assert( m_eq( m_table[idx]->m_key, k ) && m_hash( k ) == m_hash( m_table[idx]->m_key ) ); m_table[idx]->m_key = k; } + + Element_t const &Get( KeyArg_t k, Element_t const &defaultValue ) const { handle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; } + Element_t const &Get( KeyAlt_t k, Element_t const &defaultValue ) const { handle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; } + + Element_t const *GetPtr( KeyArg_t k ) const { handle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + Element_t const *GetPtr( KeyAlt_t k ) const { handle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + Element_t *GetPtr( KeyArg_t k ) { handle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + Element_t *GetPtr( KeyAlt_t k ) { handle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + + // Swap memory and contents with another identical hashtable + // (NOTE: if using function pointers or functors with state, + // it is up to the caller to ensure that they are compatible!) + void Swap( CUtlHashtable &other ) { m_table.Swap(other.m_table); ::V_swap(m_nUsed, other.m_nUsed); } + + // GetMemoryUsage returns all memory held by this class + // and its held classes. It does not include sizeof(*this). + size_t GetMemoryUsage() const + { + return m_table.AllocSize(); + } + + size_t GetReserveCount( )const + { + return m_table.Count(); + } + + +#if _DEBUG + // Validate the integrity of the hashtable + void DbgCheckIntegrity() const; +#endif + +private: + CUtlHashtable(const CUtlHashtable& copyConstructorIsNotImplemented); +}; + + +// Set external memory (raw byte buffer, best-fit) +template +void CUtlHashtable::SetExternalBuffer( byte* pRawBuffer, unsigned int nBytes, bool bAssumeOwnership, bool bGrowable ) +{ + AssertDbg( ((uintptr_t)pRawBuffer % VALIGNOF(int)) == 0 ); + uint32 bestSize = LargestPowerOfTwoLessThanOrEqual( nBytes / sizeof(entry_t) ); + Assert( bestSize != 0 && bestSize*sizeof(entry_t) <= nBytes ); + + return SetExternalBuffer( (entry_t*) pRawBuffer, bestSize, bAssumeOwnership, bGrowable ); +} + +// Set external memory (typechecked, must be power of two) +template +void CUtlHashtable::SetExternalBuffer( entry_t* pBuffer, unsigned int nSize, bool bAssumeOwnership, bool bGrowable ) +{ + Assert( IsPowerOfTwo(nSize) ); + Assert( m_nUsed == 0 ); + for ( uint i = 0; i < nSize; ++i ) + pBuffer[i].MarkInvalid(); + if ( bAssumeOwnership ) + m_table.AssumeMemory( pBuffer, nSize ); + else + m_table.SetExternalBuffer( pBuffer, nSize ); + m_bSizeLocked = !bGrowable; +} + +// Allocate an empty table and then re-insert all existing entries. +template +void CUtlHashtable::DoRealloc( int size ) +{ + Assert( !m_bSizeLocked ); + + size = SmallestPowerOfTwoGreaterOrEqual( MAX( m_nMinSize, size ) ); + Assert( size > 0 && (uint)size <= entry_t::IdealIndex( ~0, 0x1FFFFFFF ) ); // reasonable power of 2 + Assert( size > m_nUsed ); + + CUtlMemory oldTable; + oldTable.Swap( m_table ); + entry_t * RESTRICT const pOldBase = oldTable.Base(); + + m_table.EnsureCapacity( size ); + entry_t * const pNewBase = m_table.Base(); + for ( int i = 0; i < size; ++i ) + pNewBase[i].MarkInvalid(); + + int nLeftToMove = m_nUsed; + m_nUsed = 0; + for ( int i = oldTable.Count() - 1; i >= 0; --i ) + { + if ( pOldBase[i].IsValid() ) + { + int newIdx = DoInsertUnconstructed( pOldBase[i].flags_and_hash, false ); + pNewBase[newIdx].MoveDataFrom( pOldBase[i] ); + if ( --nLeftToMove == 0 ) + break; + } + } + Assert( nLeftToMove == 0 ); +} + + +// Move an existing entry to a free slot, leaving a hole behind +template +void CUtlHashtable::BumpEntry( unsigned int idx ) +{ + Assert( m_table[idx].IsValid() ); + Assert( m_nUsed < m_table.Count() ); + + entry_t* table = m_table.Base(); + unsigned int slotmask = m_table.Count()-1; + unsigned int new_flags_and_hash = table[idx].flags_and_hash & (FLAG_LAST | MASK_HASH); + + unsigned int chainid = entry_t::IdealIndex( new_flags_and_hash, slotmask ); + + // Look for empty slots scanning forward, stripping FLAG_LAST as we go. + // Note: this potentially strips FLAG_LAST from table[idx] if we pass it + int newIdx = chainid; // start at ideal slot + for ( ; ; newIdx = (newIdx + 1) & slotmask ) + { + if ( table[newIdx].IdealIndex( slotmask ) == chainid ) + { + if ( table[newIdx].flags_and_hash & FLAG_LAST ) + { + table[newIdx].flags_and_hash &= ~FLAG_LAST; + new_flags_and_hash |= FLAG_LAST; + } + continue; + } + if ( table[newIdx].IsValid() ) + { + continue; + } + break; + } + + // Did we pick something closer to the ideal slot, leaving behind a + // FLAG_LAST bit on the current slot because we didn't scan past it? + if ( table[idx].flags_and_hash & FLAG_LAST ) + { +#ifdef _DEBUG + Assert( new_flags_and_hash & FLAG_LAST ); + // Verify logic: we must have moved to an earlier slot, right? + uint offset = ((uint)idx - chainid + slotmask + 1) & slotmask; + uint newOffset = ((uint)newIdx - chainid + slotmask + 1) & slotmask; + Assert( newOffset < offset ); +#endif + // Scan backwards from old to new location, depositing FLAG_LAST on + // the first match we find. (+slotmask) is the same as (-1) without + // having to make anyone think about two's complement shenanigans. + int scan = (idx + slotmask) & slotmask; + while ( scan != newIdx ) + { + if ( table[scan].IdealIndex( slotmask ) == chainid ) + { + table[scan].flags_and_hash |= FLAG_LAST; + new_flags_and_hash &= ~FLAG_LAST; + break; + } + scan = (scan + slotmask) & slotmask; + } + } + + // Move entry to the free slot we found, leaving a hole at idx + table[newIdx].flags_and_hash = new_flags_and_hash; + table[newIdx].MoveDataFrom( table[idx] ); + table[idx].MarkInvalid(); +} + + +// Insert a value at the root position for that value's hash chain. +template +int CUtlHashtable::DoInsertUnconstructed( unsigned int h, bool allowGrow ) +{ + if ( allowGrow && !m_bSizeLocked ) + { + // Keep the load factor between .25 and .75 + int newSize = m_nUsed + 1; + if ( ( newSize*4 < m_table.Count() && m_table.Count() > m_nMinSize*2 ) || newSize*4 > m_table.Count()*3 ) + { + DoRealloc( newSize * 4 / 3 ); + } + } + Assert( m_nUsed < m_table.Count() ); + ++m_nUsed; + + entry_t* table = m_table.Base(); + unsigned int slotmask = m_table.Count()-1; + unsigned int new_flags_and_hash = FLAG_LAST | (h & MASK_HASH); + unsigned int idx = entry_t::IdealIndex( h, slotmask ); + if ( table[idx].IdealIndex( slotmask ) == idx ) + { + // There is already an entry in this chain. + new_flags_and_hash &= ~FLAG_LAST; + BumpEntry(idx); + } + else if ( table[idx].IsValid() ) + { + // Somebody else is living in our ideal index but does not belong + // to our entry chain; move it out of the way, start a new chain. + BumpEntry(idx); + } + table[idx].flags_and_hash = new_flags_and_hash; + return idx; +} + + +// Key lookup. Can also return previous-in-chain if result is a chained slot. +template +template +UtlHashHandle_t CUtlHashtable::DoLookup( KeyParamT x, unsigned int h, handle_t *pPreviousInChain ) const +{ + if ( m_nUsed == 0 ) + { + // Empty table. + return (handle_t) -1; + } + + const entry_t* table = m_table.Base(); + unsigned int slotmask = m_table.Count()-1; + Assert( m_table.Count() > 0 && (slotmask & m_table.Count()) == 0 ); + unsigned int chainid = entry_t::IdealIndex( h, slotmask ); + + unsigned int idx = chainid; + if ( table[idx].IdealIndex( slotmask ) != chainid ) + { + // Nothing in root position? No match. + return (handle_t) -1; + } + + // Linear scan until found or end of chain + handle_t lastIdx = (handle_t) -1; + while (1) + { + // Only examine this slot if it is valid and belongs to our hash chain + if ( table[idx].IdealIndex( slotmask ) == chainid ) + { + // Test the full-width hash to avoid unnecessary calls to m_eq() + if ( ((table[idx].flags_and_hash ^ h) & MASK_HASH) == 0 && m_eq( table[idx]->m_key, x ) ) + { + // Found match! + if (pPreviousInChain) + *pPreviousInChain = lastIdx; + + return (handle_t) idx; + } + + if ( table[idx].flags_and_hash & FLAG_LAST ) + { + // End of chain. No match. + return (handle_t) -1; + } + + lastIdx = (handle_t) idx; + } + idx = (idx + 1) & slotmask; + } +} + + +// Key insertion, or return index of existing key if found +template +template +UtlHashHandle_t CUtlHashtable::DoInsert( KeyParamT k, unsigned int h, bool* pDidInsert ) +{ + handle_t idx = DoLookup( k, h, NULL ); + bool bShouldInsert = ( idx == (handle_t)-1 ); + if ( pDidInsert ) + { + *pDidInsert = bShouldInsert; + } + if ( bShouldInsert ) + { + idx = (handle_t) DoInsertUnconstructed( h, true ); + Construct( m_table[ idx ].Raw(), k ); + } + return idx; +} + +// Key insertion, or return index of existing key if found +template +template +UtlHashHandle_t CUtlHashtable::DoInsert( KeyParamT k, typename ArgumentTypeInfo::Arg_t v, unsigned int h, bool *pDidInsert ) +{ + handle_t idx = DoLookup( k, h, NULL ); + if ( idx == (handle_t) -1 ) + { + idx = (handle_t) DoInsertUnconstructed( h, true ); + Construct( m_table[ idx ].Raw(), k, v ); + if ( pDidInsert ) *pDidInsert = true; + } + else + { + if ( pDidInsert ) *pDidInsert = false; + } + return idx; +} + +// Key insertion +template +template +UtlHashHandle_t CUtlHashtable::DoInsertNoCheck( KeyParamT k, typename ArgumentTypeInfo::Arg_t v, unsigned int h ) +{ + Assert( DoLookup( k, h, NULL ) == (handle_t) -1 ); + handle_t idx = (handle_t) DoInsertUnconstructed( h, true ); + Construct( m_table[ idx ].Raw(), k, v ); + return idx; +} + + +// Remove single element by key + hash. Returns the location of the new empty hole. +template +template +int CUtlHashtable::DoRemove( KeyParamT x, unsigned int h ) +{ + unsigned int slotmask = m_table.Count()-1; + handle_t previous = (handle_t) -1; + int idx = (int) DoLookup( x, h, &previous ); + if (idx == -1) + { + return -1; + } + + enum { FAKEFLAG_ROOT = 1 }; + int nLastAndRootFlags = m_table[idx].flags_and_hash & FLAG_LAST; + nLastAndRootFlags |= ( (uint)idx == m_table[idx].IdealIndex( slotmask ) ); + + // Remove from table + m_table[idx].MarkInvalid(); + Destruct( m_table[idx].Raw() ); + --m_nUsed; + + if ( nLastAndRootFlags == FLAG_LAST ) // last only, not root + { + // This was the end of the chain - mark previous as last. + // (This isn't the root, so there must be a previous.) + Assert( previous != (handle_t) -1 ); + m_table[previous].flags_and_hash |= FLAG_LAST; + } + + if ( nLastAndRootFlags == FAKEFLAG_ROOT ) // root only, not last + { + // If we are removing the root and there is more to the chain, + // scan to find the next chain entry and move it to the root. + unsigned int chainid = entry_t::IdealIndex( h, slotmask ); + unsigned int nextIdx = idx; + while (1) + { + nextIdx = (nextIdx + 1) & slotmask; + if ( m_table[nextIdx].IdealIndex( slotmask ) == chainid ) + { + break; + } + } + Assert( !(m_table[nextIdx].flags_and_hash & FLAG_FREE) ); + + // Leave a hole where the next entry in the chain was. + m_table[idx].flags_and_hash = m_table[nextIdx].flags_and_hash; + m_table[idx].MoveDataFrom( m_table[nextIdx] ); + m_table[nextIdx].MarkInvalid(); + return nextIdx; + } + + // The hole is still where the element used to be. + return idx; +} + + +// Assignment operator. It's up to the user to make sure that the hash and equality functors match. +template +CUtlHashtable &CUtlHashtable::operator=( CUtlHashtable const &src ) +{ + if ( &src != this ) + { + Assert( !m_bSizeLocked || m_table.Count() >= src.m_nUsed ); + if ( !m_bSizeLocked ) + { + Purge(); + Reserve(src.m_nUsed); + } + else + { + RemoveAll(); + } + + const entry_t * srcTable = src.m_table.Base(); + for ( int i = src.m_table.Count() - 1; i >= 0; --i ) + { + if ( srcTable[i].IsValid() ) + { + // If this assert trips, double-check that both hashtables + // have the same hash function pointers or hash functor state! + Assert( m_hash(srcTable[i]->m_key) == src.m_hash(srcTable[i]->m_key) ); + int newIdx = DoInsertUnconstructed( srcTable[i].flags_and_hash , false ); + Construct( m_table[newIdx].Raw(), *srcTable[i].Raw() ); // copy construct KVPair + } + } + } + return *this; +} + +// Remove and return the next valid iterator for a forward iteration. +template +UtlHashHandle_t CUtlHashtable::RemoveAndAdvance( UtlHashHandle_t idx ) +{ + Assert( IsValidHandle( idx ) ); + + // TODO optimize, implement DoRemoveAt that does not need to re-evaluate equality in DoLookup + int hole = DoRemove< KeyArg_t >( m_table[idx]->m_key, m_table[idx].flags_and_hash & MASK_HASH ); + // DoRemove returns the index of the element that it moved to fill the hole, if any. + if ( hole <= (int) idx ) + { + // Didn't fill, or filled from a previously seen element. + return NextHandle( idx ); + } + else + { + // Do not advance; slot has a new un-iterated value. + Assert( IsValidHandle(idx) ); + return idx; + } +} + + +// Remove and return the next valid iterator for a forward iteration. +template +void CUtlHashtable::RemoveByHandle( UtlHashHandle_t idx ) +{ + AssertDbg( IsValidHandle( idx ) ); + + // Copied from RemoveAndAdvance(): TODO optimize, implement DoRemoveAt that does not need to re-evaluate equality in DoLookup + DoRemove< KeyArg_t >( m_table[idx]->m_key, m_table[idx].flags_and_hash & MASK_HASH ); +} + + +// Burn it with fire. +template +void CUtlHashtable::RemoveAll() +{ + int used = m_nUsed; + if ( used != 0 ) + { + entry_t* table = m_table.Base(); + for ( int i = m_table.Count() - 1; i >= 0; --i ) + { + if ( table[i].IsValid() ) + { + table[i].MarkInvalid(); + Destruct( table[i].Raw() ); + if ( --used == 0 ) + break; + } + } + m_nUsed = 0; + } +} + +template +UtlHashHandle_t CUtlHashtable::NextHandle( handle_t start ) const +{ + const entry_t *table = m_table.Base(); + for ( int i = (int)start + 1; i < m_table.Count(); ++i ) + { + if ( table[i].IsValid() ) + return (handle_t) i; + } + return (handle_t) -1; +} + + +#if _DEBUG +template +void CUtlHashtable::DbgCheckIntegrity() const +{ + // Stress test the hash table as a test of both container functionality + // and also the validity of the user's Hash and Equal function objects. + // NOTE: will fail if function objects require any sort of state! + CUtlHashtable clone; + unsigned int bytes = sizeof(entry_t)*max(16,m_table.Count()); + byte* tempbuf = (byte*) malloc(bytes); + clone.SetExternalBuffer( tempbuf, bytes, false, false ); + clone = *this; + + int count = 0, roots = 0, ends = 0; + int slotmask = m_table.Count() - 1; + for (int i = 0; i < m_table.Count(); ++i) + { + if (!(m_table[i].flags_and_hash & FLAG_FREE)) ++count; + if (m_table[i].IdealIndex(slotmask) == (uint)i) ++roots; + if (m_table[i].flags_and_hash & FLAG_LAST) ++ends; + if (m_table[i].IsValid()) + { + Assert( Find(m_table[i]->m_key) == (handle_t)i ); + Verify( clone.Remove(m_table[i]->m_key) ); + } + else + { + Assert( m_table[i].flags_and_hash == FLAG_FREE ); + } + } + Assert( count == Count() && count >= roots && roots == ends ); + Assert( clone.Count() == 0 ); + clone.Purge(); + free(tempbuf); +} +#endif + +//----------------------------------------------------------------------- +// CUtlStableHashtable +//----------------------------------------------------------------------- + +// Stable hashtables are less memory and cache efficient, but can be +// iterated quickly and their element handles are completely stable. +// Implemented as a hashtable which only stores indices, and a separate +// CUtlLinkedList data table which contains key-value pairs; this may +// change to a more efficient structure in the future if space becomes +// critical. I have some ideas about that but not the time to implement +// at the moment. -henryg + +// Note: RemoveAndAdvance is slower than in CUtlHashtable because the +// key needs to be re-hashed under the current implementation. + +template , typename KeyIsEqualT = DefaultEqualFunctor, typename IndexStorageT = uint16, typename AlternateKeyT = typename ArgumentTypeInfo::Alt_t > +class CUtlStableHashtable +{ +public: + typedef typename ArgumentTypeInfo::Arg_t KeyArg_t; + typedef typename ArgumentTypeInfo::Arg_t ValueArg_t; + typedef typename ArgumentTypeInfo::Arg_t KeyAlt_t; + typedef typename CTypeSelect< sizeof( IndexStorageT ) == 2, uint16, uint32 >::type IndexStorage_t; + +protected: + COMPILE_TIME_ASSERT( sizeof( IndexStorage_t ) == sizeof( IndexStorageT ) ); + + typedef CUtlKeyValuePair< KeyT, ValueT > KVPair; + struct HashProxy; + struct EqualProxy; + struct IndirectIndex; + + typedef CUtlHashtable< IndirectIndex, empty_t, HashProxy, EqualProxy, AlternateKeyT > Hashtable_t; + typedef CUtlLinkedList< KVPair, IndexStorage_t > LinkedList_t; + + template bool DoRemove( KeyArgumentT k ); + template UtlHashHandle_t DoFind( KeyArgumentT k ) const; + template UtlHashHandle_t DoInsert( KeyArgumentT k ); + template UtlHashHandle_t DoInsert( KeyArgumentT k, ValueArgumentT v ); + +public: + + KeyHashT &GetHashRef() { return m_table.GetHashRef().m_hash; } + KeyIsEqualT &GetEqualRef() { return m_table.GetEqualRef().m_eq; } + KeyHashT const &GetHashRef() const { return m_table.GetHashRef().m_hash; } + KeyIsEqualT const &GetEqualRef() const { return m_table.GetEqualRef().m_eq; } + + UtlHashHandle_t Insert( KeyArg_t k ) { return DoInsert( k ); } + UtlHashHandle_t Insert( KeyAlt_t k ) { return DoInsert( k ); } + UtlHashHandle_t Insert( KeyArg_t k, ValueArg_t v ) { return DoInsert( k, v ); } + UtlHashHandle_t Insert( KeyAlt_t k, ValueArg_t v ) { return DoInsert( k, v ); } + UtlHashHandle_t Find( KeyArg_t k ) const { return DoFind( k ); } + UtlHashHandle_t Find( KeyAlt_t k ) const { return DoFind( k ); } + bool Remove( KeyArg_t k ) { return DoRemove( k ); } + bool Remove( KeyAlt_t k ) { return DoRemove( k ); } + + void RemoveAll() { m_table.RemoveAll(); m_data.RemoveAll(); } + void Purge() { m_table.Purge(); m_data.Purge(); } + int Count() const { return m_table.Count(); } + + typedef typename KVPair::ValueReturn_t Element_t; + KeyT const &Key( UtlHashHandle_t idx ) const { return m_data[idx].m_key; } + Element_t const &Element( UtlHashHandle_t idx ) const { return m_data[idx].GetValue(); } + Element_t &Element( UtlHashHandle_t idx ) { return m_data[idx].GetValue(); } + Element_t const &operator[]( UtlHashHandle_t idx ) const { return m_data[idx].GetValue(); } + Element_t &operator[]( UtlHashHandle_t idx ) { return m_data[idx].GetValue(); } + + void ReplaceKey( UtlHashHandle_t idx, KeyArg_t k ) { Assert( GetEqualRef()( m_data[idx].m_key, k ) && GetHashRef()( k ) == GetHashRef()( m_data[idx].m_key ) ); m_data[idx].m_key = k; } + void ReplaceKey( UtlHashHandle_t idx, KeyAlt_t k ) { Assert( GetEqualRef()( m_data[idx].m_key, k ) && GetHashRef()( k ) == GetHashRef()( m_data[idx].m_key ) ); m_data[idx].m_key = k; } + + Element_t const &Get( KeyArg_t k, Element_t const &defaultValue ) const { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; } + Element_t const &Get( KeyAlt_t k, Element_t const &defaultValue ) const { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return Element( h ); return defaultValue; } + + Element_t const *GetPtr( KeyArg_t k ) const { UtlHashHandle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + Element_t const *GetPtr( KeyAlt_t k ) const { UtlHashHandle_t h = Find(k); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + Element_t *GetPtr( KeyArg_t k ) { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + Element_t *GetPtr( KeyAlt_t k ) { UtlHashHandle_t h = Find( k ); if ( h != InvalidHandle() ) return &Element( h ); return NULL; } + + UtlHashHandle_t FirstHandle() const { return ExtendInvalidHandle( m_data.Head() ); } + UtlHashHandle_t NextHandle( UtlHashHandle_t h ) const { return ExtendInvalidHandle( m_data.Next( h ) ); } + bool IsValidHandle( UtlHashHandle_t h ) const { return m_data.IsValidIndex( h ); } + UtlHashHandle_t InvalidHandle() const { return (UtlHashHandle_t)-1; } + + UtlHashHandle_t RemoveAndAdvance( UtlHashHandle_t h ) + { + Assert( m_data.IsValidIndex( h ) ); + m_table.Remove( IndirectIndex( h ) ); + IndexStorage_t next = m_data.Next( h ); + m_data.Remove( h ); + return ExtendInvalidHandle(next); + } + + void Compact( bool bMinimal ) { m_table.Compact( bMinimal ); /*m_data.Compact();*/ } + + void Swap( CUtlStableHashtable &other ) + { + m_table.Swap(other.m_table); + // XXX swapping CUtlLinkedList by block memory swap, ugh + char buf[ sizeof(m_data) ]; + memcpy( buf, &m_data, sizeof(m_data) ); + memcpy( &m_data, &other.m_data, sizeof(m_data) ); + memcpy( &other.m_data, buf, sizeof(m_data) ); + } + + +protected: + // Perform extension of 0xFFFF to 0xFFFFFFFF if necessary. Note: ( a < CONSTANT ) ? 0 : -1 is usually branchless + static UtlHashHandle_t ExtendInvalidHandle( uint32 x ) { return x; } + static UtlHashHandle_t ExtendInvalidHandle( uint16 x ) { uint32 a = x; return a | ( ( a < 0xFFFFu ) ? 0 : -1 ); } + + struct IndirectIndex + { + explicit IndirectIndex(IndexStorage_t i) : m_index(i) { } + IndexStorage_t m_index; + }; + + struct HashProxy + { + KeyHashT m_hash; + unsigned int operator()( IndirectIndex idx ) const + { + const ptrdiff_t tableoffset = (uintptr_t)(&((Hashtable_t*)1024)->GetHashRef()) - 1024; + const ptrdiff_t owneroffset = offsetof(CUtlStableHashtable, m_table) + tableoffset; + CUtlStableHashtable* pOwner = (CUtlStableHashtable*)((uintptr_t)this - owneroffset); + return m_hash( pOwner->m_data[ idx.m_index ].m_key ); + } + unsigned int operator()( KeyArg_t k ) const { return m_hash( k ); } + unsigned int operator()( KeyAlt_t k ) const { return m_hash( k ); } + }; + + struct EqualProxy + { + KeyIsEqualT m_eq; + unsigned int operator()( IndirectIndex lhs, IndirectIndex rhs ) const + { + return lhs.m_index == rhs.m_index; + } + unsigned int operator()( IndirectIndex lhs, KeyArg_t rhs ) const + { + const ptrdiff_t tableoffset = (uintptr_t)(&((Hashtable_t*)1024)->GetEqualRef()) - 1024; + const ptrdiff_t owneroffset = offsetof(CUtlStableHashtable, m_table) + tableoffset; + CUtlStableHashtable* pOwner = (CUtlStableHashtable*)((uintptr_t)this - owneroffset); + return m_eq( pOwner->m_data[ lhs.m_index ].m_key, rhs ); + } + unsigned int operator()( IndirectIndex lhs, KeyAlt_t rhs ) const + { + const ptrdiff_t tableoffset = (uintptr_t)(&((Hashtable_t*)1024)->GetEqualRef()) - 1024; + const ptrdiff_t owneroffset = offsetof(CUtlStableHashtable, m_table) + tableoffset; + CUtlStableHashtable* pOwner = (CUtlStableHashtable*)((uintptr_t)this - owneroffset); + return m_eq( pOwner->m_data[ lhs.m_index ].m_key, rhs ); + } + }; + + class CCustomLinkedList : public LinkedList_t + { + public: + int AddToTailUnconstructed() + { + IndexStorage_t newNode = this->AllocInternal(); + if ( newNode != this->InvalidIndex() ) + this->LinkToTail( newNode ); + return newNode; + } + }; + + Hashtable_t m_table; + CCustomLinkedList m_data; +}; + +template +template +inline bool CUtlStableHashtable::DoRemove( KeyArgumentT k ) +{ + unsigned int hash = m_table.GetHashRef()( k ); + UtlHashHandle_t h = m_table.template DoLookup( k, hash, NULL ); + if ( h == m_table.InvalidHandle() ) + return false; + + int idx = m_table[ h ].m_index; + m_table.template DoRemove( IndirectIndex( idx ), hash ); + m_data.Remove( idx ); + return true; +} + +template +template +inline UtlHashHandle_t CUtlStableHashtable::DoFind( KeyArgumentT k ) const +{ + unsigned int hash = m_table.GetHashRef()( k ); + UtlHashHandle_t h = m_table.template DoLookup( k, hash, NULL ); + if ( h != m_table.InvalidHandle() ) + return m_table[ h ].m_index; + + return (UtlHashHandle_t) -1; +} + +template +template +inline UtlHashHandle_t CUtlStableHashtable::DoInsert( KeyArgumentT k ) +{ + unsigned int hash = m_table.GetHashRef()( k ); + UtlHashHandle_t h = m_table.template DoLookup( k, hash, NULL ); + if ( h != m_table.InvalidHandle() ) + return m_table[ h ].m_index; + + int idx = m_data.AddToTailUnconstructed(); + Construct( &m_data[idx], k ); + m_table.template DoInsertNoCheck( IndirectIndex( idx ), empty_t(), hash ); + return idx; +} + +template +template +inline UtlHashHandle_t CUtlStableHashtable::DoInsert( KeyArgumentT k, ValueArgumentT v ) +{ + unsigned int hash = m_table.GetHashRef()( k ); + UtlHashHandle_t h = m_table.template DoLookup( k, hash, NULL ); + if ( h != m_table.InvalidHandle() ) + return m_table[ h ].m_index; + + int idx = m_data.AddToTailUnconstructed(); + Construct( &m_data[idx], k, v ); + m_table.template DoInsertNoCheck( IndirectIndex( idx ), empty_t(), hash ); + return idx; +} + +#endif // UTLHASHTABLE_H diff --git a/r5dev/public/tier1/utllinkedlist.h b/r5dev/public/tier1/utllinkedlist.h index 2f4a4c0d..76b6ad43 100644 --- a/r5dev/public/tier1/utllinkedlist.h +++ b/r5dev/public/tier1/utllinkedlist.h @@ -24,7 +24,7 @@ // This is a useful macro to iterate from head to tail in a linked list. #define FOR_EACH_LL( listName, iteratorName ) \ - for( int iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) ) + for( decltype(listName)::IndexLocalType_t iteratorName=listName.Head(); iteratorName != listName.InvalidIndex(); iteratorName = listName.Next( iteratorName ) ) //----------------------------------------------------------------------------- // class CUtlLinkedList: @@ -62,12 +62,12 @@ class CUtlLinkedList { public: typedef T ElemType_t; - typedef S IndexType_t; // should really be called IndexStorageType_t, but that would be a huge change + typedef S IndexStorageType_t; typedef I IndexLocalType_t; typedef M MemoryAllocator_t; // constructor, destructor - CUtlLinkedList(ssize_t growSize = 0, ssize_t initSize = 0); + CUtlLinkedList(IndexStorageType_t growSize = 0, IndexStorageType_t initSize = 0); ~CUtlLinkedList(); // gets particular elements @@ -77,9 +77,9 @@ public: T const& operator[](I i) const; // Make sure we have a particular amount of memory - void EnsureCapacity(int num); + void EnsureCapacity(IndexStorageType_t num); - void SetGrowSize(int growSize); + void SetGrowSize(IndexStorageType_t growSize); // Memory deallocation void Purge(); @@ -133,7 +133,7 @@ public: inline static size_t ElementSize() { return sizeof(ListElem_t); } // list statistics - int Count() const; + I Count() const; I MaxElementIndex() const; I NumAllocated(void) const { return m_NumAlloced; } @@ -192,7 +192,7 @@ template < class T > class CUtlFixedLinkedList : public CUtlLinkedList< T, intptr_t, true, intptr_t, CUtlFixedMemory< UtlLinkedListElem_t< T, intptr_t > > > { public: - CUtlFixedLinkedList(ssize_t growSize = 0, ssize_t initSize = 0) + CUtlFixedLinkedList(intptr_t growSize = 0, intptr_t initSize = 0) : CUtlLinkedList< T, intptr_t, true, intptr_t, CUtlFixedMemory< UtlLinkedListElem_t< T, intptr_t > > >(growSize, initSize) {} bool IsValidIndex(intptr_t i) const @@ -221,7 +221,7 @@ template < class T, class I = unsigned short > class CUtlBlockLinkedList : public CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > > { public: - CUtlBlockLinkedList(int growSize = 0, int initSize = 0) + CUtlBlockLinkedList(I growSize = 0, I initSize = 0) : CUtlLinkedList< T, I, true, I, CUtlBlockMemory< UtlLinkedListElem_t< T, I >, I > >(growSize, initSize) {} protected: void ResetDbgInfo() {} @@ -233,7 +233,7 @@ protected: //----------------------------------------------------------------------------- template -CUtlLinkedList::CUtlLinkedList(ssize_t growSize, ssize_t initSize) : +CUtlLinkedList::CUtlLinkedList(S growSize, S initSize) : m_Memory(growSize, initSize), m_LastAlloc(m_Memory.InvalidIterator()) { // Prevent signed non-int datatypes @@ -292,7 +292,7 @@ inline T const& CUtlLinkedList::operator[](I i) const //----------------------------------------------------------------------------- template -inline int CUtlLinkedList::Count() const +inline I CUtlLinkedList::Count() const { #ifdef MULTILIST_PEDANTIC_ASSERTS AssertMsg(!ML, "CUtlLinkedList::Count() is meaningless for linked lists."); @@ -398,7 +398,7 @@ inline bool CUtlFixedLinkedList::IsInList( int i ) const //----------------------------------------------------------------------------- template< class T, class S, bool ML, class I, class M > -void CUtlLinkedList::EnsureCapacity(int num) +void CUtlLinkedList::EnsureCapacity(S num) { MEM_ALLOC_CREDIT_CLASS(); m_Memory.EnsureCapacity(num); @@ -406,7 +406,7 @@ void CUtlLinkedList::EnsureCapacity(int num) } template< class T, class S, bool ML, class I, class M > -void CUtlLinkedList::SetGrowSize(int growSize) +void CUtlLinkedList::SetGrowSize(S growSize) { RemoveAll(); m_Memory.Init(growSize); @@ -998,7 +998,7 @@ public: m_nElems = 0; } - int Count() const + size_t Count() const { return m_nElems; } @@ -1078,7 +1078,7 @@ private: } Node_t* m_pFirst; - unsigned m_nElems; + size_t m_nElems; }; //----------------------------------------------------------------------------- diff --git a/r5dev/public/tier1/utlmap.h b/r5dev/public/tier1/utlmap.h index 522d926f..e279917c 100644 --- a/r5dev/public/tier1/utlmap.h +++ b/r5dev/public/tier1/utlmap.h @@ -24,11 +24,11 @@ // This is a useful macro to iterate from start to end in order in a map #define FOR_EACH_MAP( mapName, iteratorName ) \ - for ( unsigned short iteratorName = (mapName).FirstInorder(); (mapName).IsUtlMap && iteratorName != (mapName).InvalidIndex(); iteratorName = (mapName).NextInorder( iteratorName ) ) + for ( decltype(mapName)::IndexType_t iteratorName = (mapName).FirstInorder(); (mapName).IsUtlMap && iteratorName != (mapName).InvalidIndex(); iteratorName = (mapName).NextInorder( iteratorName ) ) // faster iteration, but in an unspecified order #define FOR_EACH_MAP_FAST( mapName, iteratorName ) \ - for ( unsigned short iteratorName = 0; (mapName).IsUtlMap && iteratorName < (mapName).MaxElement(); ++iteratorName ) if ( !(mapName).IsValidIndex( iteratorName ) ) continue; else + for ( decltype(mapName)::IndexType_t iteratorName = 0; (mapName).IsUtlMap && iteratorName < (mapName).MaxElement(); ++iteratorName ) if ( !(mapName).IsValidIndex( iteratorName ) ) continue; else struct base_utlmap_t @@ -54,7 +54,7 @@ public: // Left at growSize = 0, the memory will first allocate 1 element and double in size // at each increment. // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below - CUtlMap( int growSize = 0, int initSize = 0, const LessFunc_t &lessfunc = 0 ) + CUtlMap( IndexType_t growSize = 0, IndexType_t initSize = 0, const LessFunc_t &lessfunc = 0 ) : m_Tree( growSize, initSize, CKeyLess( lessfunc ) ) { } @@ -64,7 +64,7 @@ public: { } - void EnsureCapacity( int num ) { m_Tree.EnsureCapacity( num ); } + void EnsureCapacity( IndexType_t num ) { m_Tree.EnsureCapacity( num ); } // gets particular elements ElemType_t & Element( IndexType_t i ) { return m_Tree.Element( i ).elem; } @@ -76,7 +76,7 @@ public: // Num elements - unsigned int Count() const { return m_Tree.Count(); } + IndexType_t Count() const { return m_Tree.Count(); } // Max "size" of the vector IndexType_t MaxElement() const { return m_Tree.MaxElement(); } @@ -267,12 +267,12 @@ public: LessFunc_t m_LessFunc; }; - typedef CUtlRBTree CTree; + typedef CUtlRBTree CTree; CTree *AccessTree() { return &m_Tree; } protected: - CTree m_Tree; + CTree m_Tree; }; //----------------------------------------------------------------------------- diff --git a/r5dev/public/tier1/utlmemory.h b/r5dev/public/tier1/utlmemory.h index 822c5db7..8947f3d5 100644 --- a/r5dev/public/tier1/utlmemory.h +++ b/r5dev/public/tier1/utlmemory.h @@ -39,16 +39,16 @@ // The CUtlMemory class: // A growable memory class which doubles in size by default. //----------------------------------------------------------------------------- -template< class T, class I = int64 > +template< class T, class I = ssize_t > class CUtlMemory { template< class A, class B> friend class CUtlVector; template< class A, size_t B> friend class CUtlVectorFixedGrowableCompat; public: // constructor, destructor - CUtlMemory(int64 nGrowSize = 0, int64 nInitSize = 0); - CUtlMemory(T* pMemory, int64 numElements); - CUtlMemory(const T* pMemory, int64 numElements); + CUtlMemory(ssize_t nGrowSize = 0, ssize_t nInitSize = 0); + CUtlMemory(T* pMemory, ssize_t numElements); + CUtlMemory(const T* pMemory, ssize_t numElements); ~CUtlMemory(); CUtlMemory(const CUtlMemory&) = delete; @@ -58,7 +58,7 @@ public: CUtlMemory& operator=(CUtlMemory&& moveFrom); // Set the size by which the memory grows - void Init(int64 nGrowSize = 0, int64 nInitSize = 0); + void Init(ssize_t nGrowSize = 0, ssize_t nInitSize = 0); class Iterator_t { @@ -94,9 +94,9 @@ public: const T* Base() const; // Attaches the buffer to external memory.... - void SetExternalBuffer(T* pMemory, int64 numElements); - void SetExternalBuffer(const T* pMemory, int64 numElements); - void AssumeMemory(T* pMemory, int64 nSize); + void SetExternalBuffer(T* pMemory, ssize_t numElements); + void SetExternalBuffer(const T* pMemory, ssize_t numElements); + void AssumeMemory(T* pMemory, ssize_t nSize); T* Detach(); void* DetachMemory(); @@ -105,23 +105,23 @@ public: // Switches the buffer from an external memory buffer to a reallocatable buffer // Will copy the current contents of the external buffer to the reallocatable buffer - void ConvertToGrowableMemory(int64 nGrowSize); + void ConvertToGrowableMemory(ssize_t nGrowSize); // Size - int64 NumAllocated() const; - int64 Count() const; + ssize_t NumAllocated() const; + ssize_t Count() const; // Grows the memory, so that at least allocated + num elements are allocated - void Grow(int64 num = 1); + void Grow(ssize_t num = 1); // Makes sure we've got at least this much memory - void EnsureCapacity(int64 num); + void EnsureCapacity(ssize_t num); // Memory deallocation void Purge(); // Purge all but the given number of elements - void Purge(int64 numElements); + void Purge(ssize_t numElements); // is the memory externally allocated? bool IsExternallyAllocated() const; @@ -130,7 +130,7 @@ public: bool IsReadOnly() const; // Set the size by which the memory grows - void SetGrowSize(int64 size); + void SetGrowSize(ssize_t size); protected: void ValidateGrowSize() @@ -139,7 +139,7 @@ protected: if (m_nGrowSize && m_nGrowSize != EXTERNAL_BUFFER_MARKER) { // Max grow size at 128 bytes on XBOX - const int64 MAX_GROW = 128; + const ssize_t MAX_GROW = 128; if (m_nGrowSize * sizeof(T) > MAX_GROW) { m_nGrowSize = max(1, MAX_GROW / sizeof(T)); @@ -155,8 +155,8 @@ protected: }; T* m_pMemory; - int64 m_nAllocationCount; - int64 m_nGrowSize; + ssize_t m_nAllocationCount; + ssize_t m_nGrowSize; }; @@ -164,19 +164,19 @@ protected: // The CUtlMemory class: // A growable memory class which doubles in size by default. //----------------------------------------------------------------------------- -template< class T, size_t SIZE, class I = int64 > +template< class T, size_t SIZE, class I = ssize_t > class CUtlMemoryFixedGrowable : public CUtlMemory< T, I > { typedef CUtlMemory< T, I > BaseClass; public: - CUtlMemoryFixedGrowable(int64 nGrowSize = 0, int64 nInitSize = SIZE) : BaseClass(m_pFixedMemory, SIZE) + CUtlMemoryFixedGrowable(ssize_t nGrowSize = 0, ssize_t nInitSize = SIZE) : BaseClass(m_pFixedMemory, SIZE) { Assert(nInitSize == 0 || nInitSize == SIZE); m_nMallocGrowSize = nGrowSize; } - void Grow(int64 nCount = 1) + void Grow(ssize_t nCount = 1) { if (this->IsExternallyAllocated()) { @@ -185,7 +185,7 @@ public: BaseClass::Grow(nCount); } - void EnsureCapacity(int64 num) + void EnsureCapacity(ssize_t num) { if (CUtlMemory::m_nAllocationCount >= num) return; @@ -200,7 +200,7 @@ public: } private: - int64 m_nMallocGrowSize; + ssize_t m_nMallocGrowSize; T m_pFixedMemory[SIZE]; }; @@ -208,68 +208,68 @@ private: // The CUtlMemoryFixed class: // A fixed memory class //----------------------------------------------------------------------------- -template< typename T, size_t SIZE, int64 nAlignment = 0 > +template< typename T, size_t SIZE, ssize_t nAlignment = 0 > class CUtlMemoryFixed { public: // constructor, destructor - CUtlMemoryFixed(int64 nGrowSize = 0, int64 nInitSize = 0) { Assert(nInitSize == 0 || nInitSize == SIZE); } - CUtlMemoryFixed(T* pMemory, int64 numElements) { Assert(0); } + CUtlMemoryFixed(ssize_t nGrowSize = 0, ssize_t nInitSize = 0) { Assert(nInitSize == 0 || nInitSize == SIZE); } + CUtlMemoryFixed(T* pMemory, ssize_t numElements) { Assert(0); } // Can we use this index? - bool IsIdxValid(int64 i) const { return (i >= 0) && (i < SIZE); } + bool IsIdxValid(ssize_t i) const { return (i >= 0) && (i < SIZE); } // Specify the invalid ('null') index that we'll only return on failure - static const int64 INVALID_INDEX = -1; // For use with COMPILE_TIME_ASSERT - static int64 InvalidIndex() { return INVALID_INDEX; } + static const ssize_t INVALID_INDEX = -1; // For use with COMPILE_TIME_ASSERT + static ssize_t InvalidIndex() { return INVALID_INDEX; } // Gets the base address T* Base() { if (nAlignment == 0) return (T*)(&m_Memory[0]); else return (T*)AlignValue(&m_Memory[0], nAlignment); } const T* Base() const { if (nAlignment == 0) return (T*)(&m_Memory[0]); else return (T*)AlignValue(&m_Memory[0], nAlignment); } // element access - T& operator[](int64 i) { Assert(IsIdxValid(i)); return Base()[i]; } - const T& operator[](int64 i) const { Assert(IsIdxValid(i)); return Base()[i]; } - T& Element(int64 i) { Assert(IsIdxValid(i)); return Base()[i]; } - const T& Element(int64 i) const { Assert(IsIdxValid(i)); return Base()[i]; } + T& operator[](ssize_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& operator[](ssize_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } + T& Element(ssize_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& Element(ssize_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } // Attaches the buffer to external memory.... - void SetExternalBuffer(T* pMemory, int64 numElements) { Assert(0); } + void SetExternalBuffer(T* pMemory, ssize_t numElements) { Assert(0); } // Size - int64 NumAllocated() const { return SIZE; } - int64 Count() const { return SIZE; } + ssize_t NumAllocated() const { return SIZE; } + ssize_t Count() const { return SIZE; } // Grows the memory, so that at least allocated + num elements are allocated - void Grow(int64 num = 1) { Assert(0); } + void Grow(ssize_t num = 1) { Assert(0); } // Makes sure we've got at least this much memory - void EnsureCapacity(int64 num) { Assert(num <= SIZE); } + void EnsureCapacity(ssize_t num) { Assert(num <= SIZE); } // Memory deallocation void Purge() {} // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryFixed) - void Purge(int64 numElements) { Assert(0); } + void Purge(ssize_t numElements) { Assert(0); } // is the memory externally allocated? bool IsExternallyAllocated() const { return false; } // Set the size by which the memory grows - void SetGrowSize(int64 size) {} + void SetGrowSize(ssize_t size) {} class Iterator_t { public: - Iterator_t(int64 i) : index(i) {} - int64 index; + Iterator_t(ssize_t i) : index(i) {} + ssize_t index; bool operator==(const Iterator_t it) const { return index == it.index; } bool operator!=(const Iterator_t it) const { return index != it.index; } }; Iterator_t First() const { return Iterator_t(IsIdxValid(0) ? 0 : InvalidIndex()); } Iterator_t Next(const Iterator_t& it) const { return Iterator_t(IsIdxValid(it.index + 1) ? it.index + 1 : InvalidIndex()); } - int64 GetIndex(const Iterator_t& it) const { return it.index; } - bool IsIdxAfter(int64 i, const Iterator_t& it) const { return i > it.index; } + ssize_t GetIndex(const Iterator_t& it) const { return it.index; } + bool IsIdxAfter(ssize_t i, const Iterator_t& it) const { return i > it.index; } bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index); } Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex()); } @@ -291,32 +291,32 @@ class CUtlMemoryConservative public: // constructor, destructor - CUtlMemoryConservative(int64 nGrowSize = 0, int64 nInitSize = 0) : m_pMemory(NULL) + CUtlMemoryConservative(ssize_t nGrowSize = 0, ssize_t nInitSize = 0) : m_pMemory(NULL) { #ifdef REMEMBER_ALLOC_SIZE_FOR_VALGRIND m_nCurAllocSize = 0; #endif } - CUtlMemoryConservative(T* pMemory, int64 numElements) { Assert(0); } + CUtlMemoryConservative(T* pMemory, ssize_t numElements) { Assert(0); } ~CUtlMemoryConservative() { if (m_pMemory) free(m_pMemory); } // Can we use this index? - bool IsIdxValid(int64 i) const { return (IsDebug()) ? (i >= 0 && i < NumAllocated()) : (i >= 0); } - static int64 InvalidIndex() { return -1; } + bool IsIdxValid(ssize_t i) const { return (IsDebug()) ? (i >= 0 && i < NumAllocated()) : (i >= 0); } + static ssize_t InvalidIndex() { return -1; } // Gets the base address T* Base() { return m_pMemory; } const T* Base() const { return m_pMemory; } // element access - T& operator[](int64 i) { Assert(IsIdxValid(i)); return Base()[i]; } - const T& operator[](int64 i) const { Assert(IsIdxValid(i)); return Base()[i]; } - T& Element(int64 i) { Assert(IsIdxValid(i)); return Base()[i]; } - const T& Element(int64 i) const { Assert(IsIdxValid(i)); return Base()[i]; } + T& operator[](ssize_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& operator[](ssize_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } + T& Element(ssize_t i) { Assert(IsIdxValid(i)); return Base()[i]; } + const T& Element(ssize_t i) const { Assert(IsIdxValid(i)); return Base()[i]; } // Attaches the buffer to external memory.... - void SetExternalBuffer(T* pMemory, int64 numElements) { Assert(0); } + void SetExternalBuffer(T* pMemory, ssize_t numElements) { Assert(0); } // Size FORCEINLINE void RememberAllocSize(size_t sz) @@ -335,11 +335,11 @@ public: #endif } - int64 NumAllocated() const + ssize_t NumAllocated() const { return AllocSize() / sizeof(T); } - int64 Count() const + ssize_t Count() const { return NumAllocated(); } @@ -350,16 +350,16 @@ public: RememberAllocSize(sz); } // Grows the memory, so that at least allocated + num elements are allocated - void Grow(int64 num = 1) + void Grow(ssize_t num = 1) { - int64 nCurN = NumAllocated(); + ssize_t nCurN = NumAllocated(); ReAlloc((nCurN + num) * sizeof(T)); } // Makes sure we've got at least this much memory - void EnsureCapacity(int64 num) + void EnsureCapacity(ssize_t num) { - size_t nSize = sizeof(T) * MAX(num, Count()); + size_t nSize = sizeof(T) * Max(num, Count()); ReAlloc(nSize); } @@ -372,27 +372,27 @@ public: } // Purge all but the given number of elements - void Purge(int64 numElements) { ReAlloc(numElements * sizeof(T)); } + void Purge(ssize_t numElements) { ReAlloc(numElements * sizeof(T)); } // is the memory externally allocated? bool IsExternallyAllocated() const { return false; } // Set the size by which the memory grows - void SetGrowSize(int64 size) {} + void SetGrowSize(ssize_t size) {} class Iterator_t { public: - Iterator_t(int64 i, int64 _limit) : index(i), limit(_limit) {} - int64 index; - int64 limit; + Iterator_t(ssize_t i, ssize_t _limit) : index(i), limit(_limit) {} + ssize_t index; + ssize_t limit; bool operator==(const Iterator_t it) const { return index == it.index; } bool operator!=(const Iterator_t it) const { return index != it.index; } }; - Iterator_t First() const { int64 limit = NumAllocated(); return Iterator_t(limit ? 0 : InvalidIndex(), limit); } + Iterator_t First() const { ssize_t limit = NumAllocated(); return Iterator_t(limit ? 0 : InvalidIndex(), limit); } Iterator_t Next(const Iterator_t& it) const { return Iterator_t((it.index + 1 < it.limit) ? it.index + 1 : InvalidIndex(), it.limit); } - int64 GetIndex(const Iterator_t& it) const { return it.index; } - bool IsIdxAfter(int64 i, const Iterator_t& it) const { return i > it.index; } + ssize_t GetIndex(const Iterator_t& it) const { return it.index; } + bool IsIdxAfter(ssize_t i, const Iterator_t& it) const { return i > it.index; } bool IsValidIterator(const Iterator_t& it) const { return IsIdxValid(it.index) && (it.index < it.limit); } Iterator_t InvalidIterator() const { return Iterator_t(InvalidIndex(), 0); } @@ -410,7 +410,7 @@ private: //----------------------------------------------------------------------------- template< class T, class I > -CUtlMemory::CUtlMemory(int64 nGrowSize, int64 nInitAllocationCount) : m_pMemory(0), +CUtlMemory::CUtlMemory(ssize_t nGrowSize, ssize_t nInitAllocationCount) : m_pMemory(0), m_nAllocationCount(nInitAllocationCount), m_nGrowSize(nGrowSize) { ValidateGrowSize(); @@ -424,7 +424,7 @@ m_nAllocationCount(nInitAllocationCount), m_nGrowSize(nGrowSize) } template< class T, class I > -CUtlMemory::CUtlMemory(T* pMemory, int64 numElements) : m_pMemory(pMemory), +CUtlMemory::CUtlMemory(T* pMemory, ssize_t numElements) : m_pMemory(pMemory), m_nAllocationCount(numElements) { // Special marker indicating externally supplied modifiable memory @@ -432,7 +432,7 @@ m_nAllocationCount(numElements) } template< class T, class I > -CUtlMemory::CUtlMemory(const T* pMemory, int64 numElements) : m_pMemory((T*)pMemory), +CUtlMemory::CUtlMemory(const T* pMemory, ssize_t numElements) : m_pMemory((T*)pMemory), m_nAllocationCount(numElements) { // Special marker indicating externally supplied modifiable memory @@ -466,8 +466,8 @@ CUtlMemory& CUtlMemory::operator=(CUtlMemory&& moveFrom) { // Copy member variables to locals before purge to handle self-assignment T* pMemory = moveFrom.m_pMemory; - int64 nAllocationCount = moveFrom.m_nAllocationCount; - int64 nGrowSize = moveFrom.m_nGrowSize; + ssize_t nAllocationCount = moveFrom.m_nAllocationCount; + ssize_t nGrowSize = moveFrom.m_nGrowSize; moveFrom.m_pMemory = nullptr; moveFrom.m_nAllocationCount = 0; @@ -484,7 +484,7 @@ CUtlMemory& CUtlMemory::operator=(CUtlMemory&& moveFrom) } template< class T, class I > -void CUtlMemory::Init(int64 nGrowSize /*= 0*/, int64 nInitSize /*= 0*/) +void CUtlMemory::Init(ssize_t nGrowSize /*= 0*/, ssize_t nInitSize /*= 0*/) { Purge(); @@ -516,7 +516,7 @@ void CUtlMemory::Swap(CUtlMemory& mem) // Switches the buffer from an external memory buffer to a reallocatable buffer //----------------------------------------------------------------------------- template< class T, class I > -void CUtlMemory::ConvertToGrowableMemory(int64 nGrowSize) +void CUtlMemory::ConvertToGrowableMemory(ssize_t nGrowSize) { if (!IsExternallyAllocated()) return; @@ -527,7 +527,7 @@ void CUtlMemory::ConvertToGrowableMemory(int64 nGrowSize) UTLMEMORY_TRACK_ALLOC(); MEM_ALLOC_CREDIT_CLASS(); - int64 nNumBytes = m_nAllocationCount * sizeof(T); + ssize_t nNumBytes = m_nAllocationCount * sizeof(T); T* pMemory = (T*)malloc(nNumBytes); memcpy(pMemory, m_pMemory, nNumBytes); m_pMemory = pMemory; @@ -543,7 +543,7 @@ void CUtlMemory::ConvertToGrowableMemory(int64 nGrowSize) // Attaches the buffer to external memory.... //----------------------------------------------------------------------------- template< class T, class I > -void CUtlMemory::SetExternalBuffer(T* pMemory, int64 numElements) +void CUtlMemory::SetExternalBuffer(T* pMemory, ssize_t numElements) { // Blow away any existing allocated memory Purge(); @@ -556,7 +556,7 @@ void CUtlMemory::SetExternalBuffer(T* pMemory, int64 numElements) } template< class T, class I > -void CUtlMemory::SetExternalBuffer(const T* pMemory, int64 numElements) +void CUtlMemory::SetExternalBuffer(const T* pMemory, ssize_t numElements) { // Blow away any existing allocated memory Purge(); @@ -569,7 +569,7 @@ void CUtlMemory::SetExternalBuffer(const T* pMemory, int64 numElements) } template< class T, class I > -void CUtlMemory::AssumeMemory(T* pMemory, int64 numElements) +void CUtlMemory::AssumeMemory(T* pMemory, ssize_t numElements) { // Blow away any existing allocated memory Purge(); @@ -653,7 +653,7 @@ bool CUtlMemory::IsReadOnly() const template< class T, class I > -void CUtlMemory::SetGrowSize(int64 nSize) +void CUtlMemory::SetGrowSize(ssize_t nSize) { Assert(!IsExternallyAllocated()); Assert(nSize >= 0); @@ -683,13 +683,13 @@ inline const T* CUtlMemory::Base() const // Size //----------------------------------------------------------------------------- template< class T, class I > -inline int64 CUtlMemory::NumAllocated() const +inline ssize_t CUtlMemory::NumAllocated() const { return m_nAllocationCount; } template< class T, class I > -inline int64 CUtlMemory::Count() const +inline ssize_t CUtlMemory::Count() const { return m_nAllocationCount; } @@ -703,14 +703,14 @@ inline bool CUtlMemory::IsIdxValid(I i) const { // GCC warns if I is an unsigned type and we do a ">= 0" against it (since the comparison is always 0). // We get the warning even if we cast inside the expression. It only goes away if we assign to another variable. - int64 x = i; + ssize_t x = i; return (x >= 0) && (x < m_nAllocationCount); } //----------------------------------------------------------------------------- // Grows the memory //----------------------------------------------------------------------------- -inline int64 UtlMemory_CalcNewAllocationCount(int64 nAllocationCount, int64 nGrowSize, int64 nNewSize, int64 nBytesItem) +inline ssize_t UtlMemory_CalcNewAllocationCount(ssize_t nAllocationCount, ssize_t nGrowSize, ssize_t nNewSize, ssize_t nBytesItem) { if (nGrowSize) { @@ -734,7 +734,7 @@ inline int64 UtlMemory_CalcNewAllocationCount(int64 nAllocationCount, int64 nGro #ifndef _X360 nAllocationCount *= 2; #else - int64 nNewAllocationCount = (nAllocationCount * 9) / 8; // 12.5 % + ssize_t nNewAllocationCount = (nAllocationCount * 9) / 8; // 12.5 % if (nNewAllocationCount > nAllocationCount) nAllocationCount = nNewAllocationCount; else @@ -747,41 +747,41 @@ inline int64 UtlMemory_CalcNewAllocationCount(int64 nAllocationCount, int64 nGro } template< class T, class I > -void CUtlMemory::Grow(int64 num) +void CUtlMemory::Grow(ssize_t num) { Assert(num > 0); if (IsExternallyAllocated()) { - // Can't grow a buffer whose memory was externally allocated - Assert(0); + Assert(0); // Can't grow a buffer whose memory was externally allocated + Error(eDLL_T::COMMON, EXIT_FAILURE, "EnsureCapacity( %zd ) called for external buffer of size %zd.\n", num, m_nAllocationCount); return; } // Make sure we have at least numallocated + num allocations. // Use the grow rules specified for this memory (in m_nGrowSize) - int64 nAllocationRequested = m_nAllocationCount + num; + ssize_t nAllocationRequested = m_nAllocationCount + num; UTLMEMORY_TRACK_FREE(); - int64 nNewAllocationCount = UtlMemory_CalcNewAllocationCount(m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof(T)); + ssize_t nNewAllocationCount = UtlMemory_CalcNewAllocationCount(m_nAllocationCount, m_nGrowSize, nAllocationRequested, sizeof(T)); // if m_nAllocationRequested wraps index type I, recalculate - if ((int64)(I)nNewAllocationCount < nAllocationRequested) + if ((ssize_t)(I)nNewAllocationCount < nAllocationRequested) { - if ((int64)(I)nNewAllocationCount == 0 && (int64)(I)(nNewAllocationCount - 1) >= nAllocationRequested) + if ((ssize_t)(I)nNewAllocationCount == 0 && (ssize_t)(I)(nNewAllocationCount - 1) >= nAllocationRequested) { --nNewAllocationCount; // deal w/ the common case of m_nAllocationCount == MAX_USHORT + 1 } else { - if ((int64)(I)nAllocationRequested != nAllocationRequested) + if ((ssize_t)(I)nAllocationRequested != nAllocationRequested) { // we've been asked to grow memory to a size s.t. the index type can't address the requested amount of memory Assert(0); return; } - while ((int64)(I)nNewAllocationCount < nAllocationRequested) + while ((ssize_t)(I)nNewAllocationCount < nAllocationRequested) { nNewAllocationCount = (nNewAllocationCount + nAllocationRequested) / 2; } @@ -811,15 +811,15 @@ void CUtlMemory::Grow(int64 num) // Makes sure we've got at least this much memory //----------------------------------------------------------------------------- template< class T, class I > -inline void CUtlMemory::EnsureCapacity(int64 num) +inline void CUtlMemory::EnsureCapacity(ssize_t num) { if (m_nAllocationCount >= num) return; if (IsExternallyAllocated()) { - // Can't grow a buffer whose memory was externally allocated - Assert(0); + Assert(0); // Can't grow a buffer whose memory was externally allocated + Error(eDLL_T::COMMON, EXIT_FAILURE, "EnsureCapacity( %zd ) called for external buffer of size %zd.\n", num, m_nAllocationCount); return; } @@ -861,7 +861,7 @@ void CUtlMemory::Purge() } template< class T, class I > -void CUtlMemory::Purge(int64 numElements) +void CUtlMemory::Purge(ssize_t numElements) { Assert(numElements >= 0); @@ -914,31 +914,31 @@ void CUtlMemory::Purge(int64 numElements) // The CUtlMemory class: // A growable memory class which doubles in size by default. //----------------------------------------------------------------------------- -template< class T, int64 nAlignment > +template< class T, ssize_t nAlignment > class CUtlMemoryAligned : public CUtlMemory { public: // constructor, destructor - CUtlMemoryAligned(int64 nGrowSize = 0, int64 nInitSize = 0); - CUtlMemoryAligned(T* pMemory, int64 numElements); - CUtlMemoryAligned(const T* pMemory, int64 numElements); + CUtlMemoryAligned(ssize_t nGrowSize = 0, ssize_t nInitSize = 0); + CUtlMemoryAligned(T* pMemory, ssize_t numElements); + CUtlMemoryAligned(const T* pMemory, ssize_t numElements); ~CUtlMemoryAligned(); // Attaches the buffer to external memory.... - void SetExternalBuffer(T* pMemory, int64 numElements); - void SetExternalBuffer(const T* pMemory, int64 numElements); + void SetExternalBuffer(T* pMemory, ssize_t numElements); + void SetExternalBuffer(const T* pMemory, ssize_t numElements); // Grows the memory, so that at least allocated + num elements are allocated - void Grow(int64 num = 1); + void Grow(ssize_t num = 1); // Makes sure we've got at least this much memory - void EnsureCapacity(int64 num); + void EnsureCapacity(ssize_t num); // Memory deallocation void Purge(); // Purge all but the given number of elements (NOT IMPLEMENTED IN CUtlMemoryAligned) - void Purge(int64 numElements) { Assert(0); } + void Purge(ssize_t numElements) { Assert(0); } private: void* Align(const void* pAddr); @@ -948,7 +948,7 @@ private: //----------------------------------------------------------------------------- // Aligns a pointer //----------------------------------------------------------------------------- -template< class T, int64 nAlignment > +template< class T, ssize_t nAlignment > void* CUtlMemoryAligned::Align(const void* pAddr) { size_t nAlignmentMask = nAlignment - 1; @@ -959,8 +959,8 @@ void* CUtlMemoryAligned::Align(const void* pAddr) //----------------------------------------------------------------------------- // constructor, destructor //----------------------------------------------------------------------------- -template< class T, int64 nAlignment > -CUtlMemoryAligned::CUtlMemoryAligned(int64 nGrowSize, int64 nInitAllocationCount) +template< class T, ssize_t nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned(ssize_t nGrowSize, ssize_t nInitAllocationCount) { CUtlMemory::m_pMemory = 0; CUtlMemory::m_nAllocationCount = nInitAllocationCount; @@ -978,27 +978,27 @@ CUtlMemoryAligned::CUtlMemoryAligned(int64 nGrowSize, int64 nInit } } -template< class T, int64 nAlignment > -CUtlMemoryAligned::CUtlMemoryAligned(T* pMemory, int64 numElements) +template< class T, ssize_t nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned(T* pMemory, ssize_t numElements) { // Special marker indicating externally supplied memory CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_BUFFER_MARKER; CUtlMemory::m_pMemory = (T*)Align(pMemory); - CUtlMemory::m_nAllocationCount = ((int64)(pMemory + numElements) - (int64)CUtlMemory::m_pMemory) / sizeof(T); + CUtlMemory::m_nAllocationCount = ((ssize_t)(pMemory + numElements) - (ssize_t)CUtlMemory::m_pMemory) / sizeof(T); } -template< class T, int64 nAlignment > -CUtlMemoryAligned::CUtlMemoryAligned(const T* pMemory, int64 numElements) +template< class T, ssize_t nAlignment > +CUtlMemoryAligned::CUtlMemoryAligned(const T* pMemory, ssize_t numElements) { // Special marker indicating externally supplied memory CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_CONST_BUFFER_MARKER; CUtlMemory::m_pMemory = (T*)Align(pMemory); - CUtlMemory::m_nAllocationCount = ((int64)(pMemory + numElements) - (int64)CUtlMemory::m_pMemory) / sizeof(T); + CUtlMemory::m_nAllocationCount = ((ssize_t)(pMemory + numElements) - (ssize_t)CUtlMemory::m_pMemory) / sizeof(T); } -template< class T, int64 nAlignment > +template< class T, ssize_t nAlignment > CUtlMemoryAligned::~CUtlMemoryAligned() { Purge(); @@ -1008,27 +1008,27 @@ CUtlMemoryAligned::~CUtlMemoryAligned() //----------------------------------------------------------------------------- // Attaches the buffer to external memory.... //----------------------------------------------------------------------------- -template< class T, int64 nAlignment > -void CUtlMemoryAligned::SetExternalBuffer(T* pMemory, int64 numElements) +template< class T, ssize_t nAlignment > +void CUtlMemoryAligned::SetExternalBuffer(T* pMemory, ssize_t numElements) { // Blow away any existing allocated memory Purge(); CUtlMemory::m_pMemory = (T*)Align(pMemory); - CUtlMemory::m_nAllocationCount = ((int64)(pMemory + numElements) - (int64)CUtlMemory::m_pMemory) / sizeof(T); + CUtlMemory::m_nAllocationCount = ((ssize_t)(pMemory + numElements) - (ssize_t)CUtlMemory::m_pMemory) / sizeof(T); // Indicate that we don't own the memory CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_BUFFER_MARKER; } -template< class T, int64 nAlignment > -void CUtlMemoryAligned::SetExternalBuffer(const T* pMemory, int64 numElements) +template< class T, ssize_t nAlignment > +void CUtlMemoryAligned::SetExternalBuffer(const T* pMemory, ssize_t numElements) { // Blow away any existing allocated memory Purge(); CUtlMemory::m_pMemory = (T*)Align(pMemory); - CUtlMemory::m_nAllocationCount = ((int64)(pMemory + numElements) - (int64)CUtlMemory::m_pMemory) / sizeof(T); + CUtlMemory::m_nAllocationCount = ((ssize_t)(pMemory + numElements) - (ssize_t)CUtlMemory::m_pMemory) / sizeof(T); // Indicate that we don't own the memory CUtlMemory::m_nGrowSize = CUtlMemory::EXTERNAL_CONST_BUFFER_MARKER; @@ -1038,15 +1038,15 @@ void CUtlMemoryAligned::SetExternalBuffer(const T* pMemory, int64 //----------------------------------------------------------------------------- // Grows the memory //----------------------------------------------------------------------------- -template< class T, int64 nAlignment > -void CUtlMemoryAligned::Grow(int64 num) +template< class T, ssize_t nAlignment > +void CUtlMemoryAligned::Grow(ssize_t num) { Assert(num > 0); if (this->IsExternallyAllocated()) { - // Can't grow a buffer whose memory was externally allocated - Assert(0); + Assert(0); // Can't grow a buffer whose memory was externally allocated + Error(eDLL_T::COMMON, EXIT_FAILURE, "EnsureCapacity( %zd ) called for external buffer of size %zd.\n", num, CUtlMemory::m_nAllocationCount); return; } @@ -1054,7 +1054,7 @@ void CUtlMemoryAligned::Grow(int64 num) // Make sure we have at least numallocated + num allocations. // Use the grow rules specified for this memory (in m_nGrowSize) - int64 nAllocationRequested = CUtlMemory::m_nAllocationCount + num; + ssize_t nAllocationRequested = CUtlMemory::m_nAllocationCount + num; CUtlMemory::m_nAllocationCount = UtlMemory_CalcNewAllocationCount(CUtlMemory::m_nAllocationCount, CUtlMemory::m_nGrowSize, nAllocationRequested, sizeof(T)); @@ -1078,16 +1078,16 @@ void CUtlMemoryAligned::Grow(int64 num) //----------------------------------------------------------------------------- // Makes sure we've got at least this much memory //----------------------------------------------------------------------------- -template< class T, int64 nAlignment > -inline void CUtlMemoryAligned::EnsureCapacity(int64 num) +template< class T, ssize_t nAlignment > +inline void CUtlMemoryAligned::EnsureCapacity(ssize_t num) { if (CUtlMemory::m_nAllocationCount >= num) return; if (this->IsExternallyAllocated()) { - // Can't grow a buffer whose memory was externally allocated - Assert(0); + Assert(0); // Can't grow a buffer whose memory was externally allocated + Error(eDLL_T::COMMON, EXIT_FAILURE, "EnsureCapacity( %zd ) called for external buffer of size %zd.\n", num, CUtlMemory::m_nAllocationCount); return; } @@ -1113,7 +1113,7 @@ inline void CUtlMemoryAligned::EnsureCapacity(int64 num) //----------------------------------------------------------------------------- // Memory deallocation //----------------------------------------------------------------------------- -template< class T, int64 nAlignment > +template< class T, ssize_t nAlignment > void CUtlMemoryAligned::Purge() { if (!this->IsExternallyAllocated()) diff --git a/r5dev/public/tier1/utlrbtree.h b/r5dev/public/tier1/utlrbtree.h index d3db9602..2e4bae31 100644 --- a/r5dev/public/tier1/utlrbtree.h +++ b/r5dev/public/tier1/utlrbtree.h @@ -151,11 +151,11 @@ public: // Left at growSize = 0, the memory will first allocate 1 element and double in size // at each increment. // LessFunc_t is required, but may be set after the constructor using SetLessFunc() below - CUtlRBTree(int64 growSize = 0, int64 initSize = 0, const LessFunc_t& lessfunc = 0); + CUtlRBTree(IndexType_t growSize = 0, IndexType_t initSize = 0, const LessFunc_t& lessfunc = 0); CUtlRBTree(const LessFunc_t& lessfunc); ~CUtlRBTree(); - void EnsureCapacity(int64 num); + void EnsureCapacity(IndexType_t num); // NOTE: CopyFrom is fast but dangerous! It just memcpy's all nodes - it does NOT run copy constructors, so // it is not a true deep copy (i.e 'T' must be POD for this to work - e.g CUtlString will not work). @@ -171,7 +171,7 @@ public: I Root() const; // Num elements - unsigned int Count() const; + I Count() const; // Max "size" of the vector // it's not generally safe to iterate from index 0 to MaxElement()-1 (you could do this as a potential @@ -215,7 +215,7 @@ public: // NOTE: the returned 'index' will be valid as long as the element remains in the tree // (other elements being added/removed will not affect it) I Insert(T const& insert); - void Insert(const T* pArray, int64 nItems); + void Insert(const T* pArray, I nItems); I InsertIfNotFound(T const& insert); // Find method @@ -384,7 +384,7 @@ protected: //----------------------------------------------------------------------------- template < class T, class I, typename L, class M > -inline CUtlRBTree::CUtlRBTree(int64 growSize, int64 initSize, const LessFunc_t& lessfunc) : +inline CUtlRBTree::CUtlRBTree(IndexType_t growSize, IndexType_t initSize, const LessFunc_t& lessfunc) : m_LessFunc(lessfunc), m_Elements(growSize, initSize), m_Root(InvalidIndex()), @@ -397,7 +397,7 @@ inline CUtlRBTree::CUtlRBTree(int64 growSize, int64 initSize, const template < class T, class I, typename L, class M > inline CUtlRBTree::CUtlRBTree(const LessFunc_t& lessfunc) : - m_Elements((int64)0, (int64)0), + m_Elements((ssize_t)0, (ssize_t)0), m_LessFunc(lessfunc), m_Root(InvalidIndex()), m_NumElements(0), @@ -414,7 +414,7 @@ inline CUtlRBTree::~CUtlRBTree() } template < class T, class I, typename L, class M > -inline void CUtlRBTree::EnsureCapacity(int64 num) +inline void CUtlRBTree::EnsureCapacity(IndexType_t num) { m_Elements.EnsureCapacity(num); } @@ -482,9 +482,9 @@ inline I CUtlRBTree::Root() const //----------------------------------------------------------------------------- template < class T, class I, typename L, class M > -inline unsigned int CUtlRBTree::Count() const +inline I CUtlRBTree::Count() const { - return (unsigned int)m_NumElements; + return m_NumElements; } //----------------------------------------------------------------------------- @@ -1509,7 +1509,7 @@ I CUtlRBTree::Insert(T const& insert) template < class T, class I, typename L, class M > -void CUtlRBTree::Insert(const T* pArray, int64 nItems) +void CUtlRBTree::Insert(const T* pArray, I nItems) { while (nItems--) { diff --git a/r5dev/public/tier1/utlstring.h b/r5dev/public/tier1/utlstring.h index c1d86eef..e12e155a 100644 --- a/r5dev/public/tier1/utlstring.h +++ b/r5dev/public/tier1/utlstring.h @@ -14,17 +14,13 @@ #include "tier1/strtools.h" #include "limits.h" -#ifndef SIZE_MAX -#define SIZE_MAX ((size_t) -1) -#endif - //----------------------------------------------------------------------------- // Base class, containing simple memory management //----------------------------------------------------------------------------- class CUtlBinaryBlock { public: - CUtlBinaryBlock( int64 growSize = 0, int64 initSize = 0 ); + CUtlBinaryBlock( ssize_t growSize = 0, ssize_t initSize = 0 ); ~CUtlBinaryBlock() { #ifdef _DEBUG @@ -35,8 +31,8 @@ public: } // NOTE: nInitialLength indicates how much of the buffer starts full - CUtlBinaryBlock( void* pMemory, int64 nSizeInBytes, int64 nInitialLength ); - CUtlBinaryBlock( const void* pMemory, int64 nSizeInBytes ); + CUtlBinaryBlock( void* pMemory, ssize_t nSizeInBytes, ssize_t nInitialLength ); + CUtlBinaryBlock( const void* pMemory, ssize_t nSizeInBytes ); CUtlBinaryBlock( const CUtlBinaryBlock& src ); CUtlBinaryBlock &operator=( const CUtlBinaryBlock &src ); @@ -46,16 +42,16 @@ public: CUtlBinaryBlock &operator=( CUtlBinaryBlock&& src ); #endif - void Get( void *pValue, int64 nMaxLen ) const; - void Set( const void *pValue, int64 nLen ); + void Get( void *pValue, ssize_t nMaxLen ) const; + void Set( const void *pValue, ssize_t nLen ); const void *Get( ) const; void *Get( ); - unsigned char& operator[]( int64 i ); - const unsigned char& operator[]( int64 i ) const; + unsigned char& operator[]( ssize_t i ); + const unsigned char& operator[]( ssize_t i ) const; - int64 Length() const; - void SetLength( int64 nLength ); // Undefined memory will result + ssize_t Length() const; + void SetLength( ssize_t nLength ); // Undefined memory will result bool IsEmpty() const; void Clear(); void Purge(); @@ -68,7 +64,7 @@ public: private: CUtlMemory m_Memory; - int64 m_nActualLength; + ssize_t m_nActualLength; }; @@ -86,7 +82,7 @@ inline CUtlBinaryBlock::CUtlBinaryBlock( CUtlBinaryBlock&& src ) inline CUtlBinaryBlock& CUtlBinaryBlock::operator= ( CUtlBinaryBlock&& src ) { - int64 length = src.m_nActualLength; + ssize_t length = src.m_nActualLength; src.m_nActualLength = 0; m_Memory = Move( src.m_Memory ); @@ -106,17 +102,17 @@ inline void *CUtlBinaryBlock::Get( ) return m_Memory.Base(); } -inline int64 CUtlBinaryBlock::Length() const +inline ssize_t CUtlBinaryBlock::Length() const { return m_nActualLength; } -inline unsigned char& CUtlBinaryBlock::operator[]( int64 i ) +inline unsigned char& CUtlBinaryBlock::operator[]( ssize_t i ) { return m_Memory[i]; } -inline const unsigned char& CUtlBinaryBlock::operator[]( int64 i ) const +inline const unsigned char& CUtlBinaryBlock::operator[]( ssize_t i ) const { return m_Memory[i]; } @@ -154,8 +150,8 @@ public: CUtlString( const char *pString ); // initialize from c-style string // Attaches the string to external memory. Useful for avoiding a copy - CUtlString( void* pMemory, int64 nSizeInBytes, int64 nInitialLength ); - CUtlString( const void* pMemory, int64 nSizeInBytes ); + CUtlString( void* pMemory, ssize_t nSizeInBytes, ssize_t nInitialLength ); + CUtlString( const void* pMemory, ssize_t nSizeInBytes ); // Copy/move constructor/assignment // Moves are extremely efficient as the underlying memory is not copied, just the pointers. @@ -185,7 +181,7 @@ public: const char *String() const { return Get(); } // Returns strlen - int64 Length() const; + ssize_t Length() const; bool IsEmpty() const; // GS - Added for chromehtml @@ -197,7 +193,7 @@ public: // Sets the length (used to serialize into the buffer ) // Note: If nLen != 0, then this adds an extra byte for a null-terminator. void Set( const char *pValue ); - void SetLength( int64 nLen ); + void SetLength( ssize_t nLen ); void Purge(); void Swap(CUtlString &src); @@ -206,7 +202,7 @@ public: void ToUpper(); void ToLower( ); void Append( const char *pchAddition ); - void Append(const char *pchAddition, int64 nMaxChars); + void Append(const char *pchAddition, ssize_t nMaxChars); void Append(char chAddition) { char temp[2] = { chAddition, 0 }; Append(temp); @@ -215,8 +211,8 @@ public: // Strips the trailing slash void StripTrailingSlash(); - char operator[] ( int64 idx ) const; - char& operator[] ( int64 idx ); + char operator[] ( ssize_t idx ) const; + char& operator[] ( ssize_t idx ); // Test for equality bool operator==( const CUtlString &src ) const; @@ -232,11 +228,11 @@ public: CUtlString &operator+=( const CUtlString &rhs ); CUtlString &operator+=( const char *rhs ); CUtlString &operator+=( char c ); - CUtlString &operator+=( int64 rhs ); + CUtlString &operator+=( ssize_t rhs ); CUtlString &operator+=( double rhs ); CUtlString operator+( const char *pOther )const; - //CUtlString operator+( int64 rhs )const; + //CUtlString operator+( ssize_t rhs )const; bool MatchesPattern( const CUtlString &Pattern, int nFlags = 0 ); // case SENSITIVE, use * for wildcard in pattern string @@ -249,7 +245,7 @@ public: int FormatV( const char *pFormat, va_list marker ); #endif - void SetDirect( const char *pValue, int64 nChars ); + void SetDirect( const char *pValue, ssize_t nChars ); // Defining AltArgumentType_t hints that associative container classes should // also implement Find/Insert/Remove functions that take const char* params. @@ -260,11 +256,11 @@ public: // Take a piece out of the string. // If you only specify nStart, it'll go from nStart to the end. // You can use negative numbers and it'll wrap around to the start. - CUtlString Slice( int64 nStart=0, int64 nEnd=INT_MAX ); + CUtlString Slice( ssize_t nStart=0, ssize_t nEnd=SSIZE_MAX ); // Grab a substring starting from the left or the right side. - CUtlString Left( int64 nChars ); - CUtlString Right( int64 nChars ); + CUtlString Left( ssize_t nChars ); + CUtlString Right( ssize_t nChars ); CUtlString Remove( char const *pTextToRemove, bool bCaseSensitive ) const; @@ -319,14 +315,14 @@ public: static CUtlString PathJoin( const char *pStr1, const char *pStr2 ); // These can be used for utlvector sorts. - static int64 __cdecl SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ); - static int64 __cdecl SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ); + static ssize_t __cdecl SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ); + static ssize_t __cdecl SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ); // From Src2 void AppendSlash( char separator = CORRECT_PATH_SEPARATOR ) { - int64 nLength = Length() - 1; + ssize_t nLength = Length() - 1; if ( nLength > 0 && !PATHSEPARATOR( static_cast< char >( m_Storage[ nLength ] ) ) ) { Append( separator ); @@ -335,7 +331,7 @@ public: void FixSlashes( char cSeparator = CORRECT_PATH_SEPARATOR ) { - for ( int64 nLength = Length() - 1; nLength >= 0; nLength-- ) + for ( ssize_t nLength = Length() - 1; nLength >= 0; nLength-- ) { char *pname = (char*)&m_Storage[ nLength ]; if ( *pname == INCORRECT_PATH_SEPARATOR || *pname == CORRECT_PATH_SEPARATOR ) @@ -397,22 +393,22 @@ inline bool CUtlString::IsEmpty() const return Length() == 0; } -inline int64 __cdecl CUtlString::SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ) +inline ssize_t __cdecl CUtlString::SortCaseInsensitive( const CUtlString *pString1, const CUtlString *pString2 ) { return V_stricmp( pString1->String(), pString2->String() ); } -inline int64 __cdecl CUtlString::SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ) +inline ssize_t __cdecl CUtlString::SortCaseSensitive( const CUtlString *pString1, const CUtlString *pString2 ) { return V_strcmp( pString1->String(), pString2->String() ); } -inline char CUtlString::operator [] ( int64 index ) const +inline char CUtlString::operator [] ( ssize_t index ) const { return Get()[index]; } -inline char& CUtlString::operator[] ( int64 index ) +inline char& CUtlString::operator[] ( ssize_t index ) { return Access()[index]; } @@ -625,7 +621,7 @@ public: void Append(const CUtlStringBuilder &str) { Append(str.String(), str.Length()); } //void Append( IFillStringFunctor& func ); void AppendChar(char ch) { Append(&ch, 1); } - void AppendRepeat(char ch, int64 cCount); + void AppendRepeat(char ch, ssize_t cCount); // sets the string void SetValue(const char *pchString); @@ -1375,7 +1371,7 @@ inline void CUtlStringBuilder::Append(const char *pchAddition, size_t cbLen) //----------------------------------------------------------------------------- // Purpose: append a repeated series of a single character //----------------------------------------------------------------------------- -inline void CUtlStringBuilder::AppendRepeat(char ch, int64 cCount) +inline void CUtlStringBuilder::AppendRepeat(char ch, ssize_t cCount) { size_t cbOld = Length(); char *pstrNew = PrepareBuffer(cbOld + cCount, true); diff --git a/r5dev/public/tier1/utlstringmap.h b/r5dev/public/tier1/utlstringmap.h new file mode 100644 index 00000000..7bc46b16 --- /dev/null +++ b/r5dev/public/tier1/utlstringmap.h @@ -0,0 +1,185 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +//===========================================================================// + +#ifndef UTLSTRINGMAP_H +#define UTLSTRINGMAP_H +#ifdef _WIN32 +#pragma once +#endif + +#include "utlsymbol.h" + +template +class CUtlStringMap +{ +public: + typedef UtlSymId_t IndexType_t; + CUtlStringMap( bool caseInsensitive = true, int initsize = 32 ) : + m_SymbolTable( 0, 32, caseInsensitive ), + m_Vector( initsize ) + { + } + + // Get data by the string itself: + T& operator[]( const char *pString ) + { + CUtlSymbol symbol = m_SymbolTable.AddString( pString ); + int index = ( int )( UtlSymId_t )symbol; + if( m_Vector.Count() <= index ) + { + m_Vector.EnsureCount( index + 1 ); + } + return m_Vector[index]; + } + + // Get data by the string's symbol table ID - only used to retrieve a pre-existing symbol, not create a new one! + T& operator[]( UtlSymId_t n ) + { + Assert( n <= m_Vector.Count() ); + return m_Vector[n]; + } + + const T& operator[]( UtlSymId_t n ) const + { + Assert( n <= m_Vector.Count() ); + return m_Vector[n]; + } + + unsigned int Count() const + { + Assert( m_Vector.Count() == m_SymbolTable.GetNumStrings() ); + return m_Vector.Count(); + } + + bool Defined( const char *pString ) const + { + return m_SymbolTable.Find( pString ) != UTL_INVAL_SYMBOL; + } + + UtlSymId_t Find( const char *pString ) const + { + return m_SymbolTable.Find( pString ); + } + + UtlSymId_t AddString( const char *pString ) + { + CUtlSymbol symbol = m_SymbolTable.AddString( pString ); + int index = ( int )( UtlSymId_t )symbol; + if( m_Vector.Count() <= index ) + { + m_Vector.EnsureCount( index + 1 ); + } + return symbol; + } + + /// Add a string to the map and also insert an item at + /// its location in the same operation. Returns the + /// newly created index (or the one that was just + /// overwritten, if pString already existed.) + UtlSymId_t Insert( const char *pString, const T &item ) + { + CUtlSymbol symbol = m_SymbolTable.AddString( pString ); + UtlSymId_t index = symbol; // implicit coercion + if ( m_Vector.Count() > index ) + { + // this string is already in the dictionary. + + } + else if ( m_Vector.Count() == index ) + { + // this is the expected case when we've added one more to the tail. + m_Vector.AddToTail( item ); + } + else // ( m_Vector.Count() < index ) + { + // this is a strange shouldn't-happen case. + //AssertMsg( false, "CUtlStringMap insert unexpected entries." ); + m_Vector.EnsureCount( index + 1 ); + m_Vector[index] = item; + } + return index; + } + + /// iterate, not in any particular order. + IndexType_t First() const + { + if ( Count() > 0 ) + { + return 0; + } + else + { + return InvalidIndex(); + } + } + + static UtlSymId_t InvalidIndex() + { + return UTL_INVAL_SYMBOL; + } + + // iterators (for uniformity with other map types) + inline UtlSymId_t Head() const + { + return m_SymbolTable.GetNumStrings() > 0 ? 0 : InvalidIndex(); + } + + inline UtlSymId_t Next( const UtlSymId_t &i ) const + { + UtlSymId_t n = i+1; + return n < m_SymbolTable.GetNumStrings() ? n : InvalidIndex(); + } + + + int GetNumStrings( void ) const + { + return m_SymbolTable.GetNumStrings(); + } + + const char *String( int n ) const + { + return m_SymbolTable.String( n ); + } + + // Clear all of the data from the map + void Clear() + { + m_Vector.RemoveAll(); + m_SymbolTable.RemoveAll(); + } + + void Purge() + { + m_Vector.Purge(); + m_SymbolTable.RemoveAll(); + } + + void PurgeAndDeleteElements() + { + m_Vector.PurgeAndDeleteElements(); + m_SymbolTable.RemoveAll(); + } + + + +private: + CUtlVector m_Vector; + CUtlSymbolTable m_SymbolTable; +}; + + +template< class T > +class CUtlStringMapAutoPurge : public CUtlStringMap < T > +{ +public: + ~CUtlStringMapAutoPurge( void ) + { + this->PurgeAndDeleteElements(); + } + +}; + +#endif // UTLSTRINGMAP_H diff --git a/r5dev/public/tier1/utlsymbol.h b/r5dev/public/tier1/utlsymbol.h index 3133559d..a1bfb0bd 100644 --- a/r5dev/public/tier1/utlsymbol.h +++ b/r5dev/public/tier1/utlsymbol.h @@ -104,7 +104,7 @@ class CUtlSymbolTable { public: // constructor, destructor - CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false ); + CUtlSymbolTable( unsigned short growSize = 0, unsigned short initSize = 16, bool caseInsensitive = false ); ~CUtlSymbolTable(); // Finds and/or creates a symbol based on the string @@ -159,21 +159,21 @@ protected: public: CLess( int ignored = 0 ) {} // permits default initialization to NULL in CUtlRBTree bool operator!() const { return false; } - bool operator()( const CStringPoolIndex &left, const CStringPoolIndex &right ) const; + int operator()( const CStringPoolIndex &left, const CStringPoolIndex &right ) const; }; // Stores the symbol lookup class CTree : public CUtlRBTree { public: - CTree( int growSize, int initSize ) : CUtlRBTree( growSize, initSize ) {} + CTree( unsigned short growSize, unsigned short initSize ) : CUtlRBTree( growSize, initSize ) {} friend class CUtlSymbolTable::CLess; // Needed to allow CLess to calculate pointer to symbol table }; struct StringPool_t - { - int m_TotalLen; // How large is - int m_SpaceUsed; + { + size_t m_TotalLen; // How large is + size_t m_SpaceUsed; char m_Data[1]; }; @@ -187,19 +187,18 @@ protected: CUtlVector m_StringPools; private: - int FindPoolWithSpace( int len ) const; + int FindPoolWithSpace( size_t len ) const; const char* StringFromIndex( const CStringPoolIndex &index ) const; const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const; friend class CLess; friend class CSymbolHash; - }; -class CUtlSymbolTableMT : public CUtlSymbolTable +class CUtlSymbolTableMT : public CUtlSymbolTable { public: - CUtlSymbolTableMT( int growSize = 0, int initSize = 32, bool caseInsensitive = false ) + CUtlSymbolTableMT( unsigned short growSize = 0, unsigned short initSize = 32, bool caseInsensitive = false ) : CUtlSymbolTable( growSize, initSize, caseInsensitive ) { } diff --git a/r5dev/public/tier1/utlvector.h b/r5dev/public/tier1/utlvector.h index fb1642e1..0caf449b 100644 --- a/r5dev/public/tier1/utlvector.h +++ b/r5dev/public/tier1/utlvector.h @@ -61,14 +61,6 @@ public: // Copy the array. CUtlVector& operator=(const CUtlVector& other); - // NOTE: - // Do not call after initialization or after adding elements. - // This is added so it could be constructed nicely. Since the - // game executable in monolithic, we couldn't import the malloc - // functions, and thus not construct automatically when using - // the game's memalloc singleton. - void Init(); - // element access T& operator[](int i); const T& operator[](int i) const; @@ -664,15 +656,6 @@ inline CUtlVector& CUtlVector::operator=(const CUtlVector& oth return *this; } -template< typename T, class A > -void CUtlVector::Init() -{ - m_Memory.m_pMemory = nullptr; - m_Memory.m_nAllocationCount = 0; - m_Memory.m_nGrowSize = 0; - m_Size = 0; -} - //----------------------------------------------------------------------------- // element access //----------------------------------------------------------------------------- @@ -1417,24 +1400,23 @@ void CUtlVector::Validate(CValidator& validator, char* pchName) // A vector class for storing pointers, so that the elements pointed to by the pointers are deleted // on exit. -template class CUtlVectorAutoPurge : public CUtlVector< T, CUtlMemory< T, int> > +template class CUtlVectorAutoPurge : public CUtlVector< T > { public: ~CUtlVectorAutoPurge(void) { this->PurgeAndDeleteElements(); } - }; // easy string list class with dynamically allocated strings. For use with V_SplitString, etc. // Frees the dynamic strings in destructor. -class CUtlStringList : public CUtlVectorAutoPurge< char*> +class CUtlStringList : public CUtlVectorAutoPurge { public: void CopyAndAddToTail(char const* pString) // clone the string and add to the end { - char* pNewStr = new char[1 + strlen(pString)]; + char* const pNewStr = new char[strlen(pString) + 1]; strcpy(pNewStr, pString); AddToTail(pNewStr); } @@ -1446,27 +1428,25 @@ public: CUtlStringList() {} - // !TODO: + CUtlStringList(char const* pString, char const* pSeparator) + { + SplitString(pString, pSeparator); + } - //CUtlStringList(char const* pString, char const* pSeparator) - //{ - // SplitString(pString, pSeparator); - //} + CUtlStringList(char const* pString, const char** pSeparators, ssize_t nSeparators) + { + SplitString2(pString, pSeparators, nSeparators); + } - //CUtlStringList(char const* pString, const char** pSeparators, int nSeparators) - //{ - // SplitString2(pString, pSeparators, nSeparators); - //} + void SplitString(char const* pString, char const* pSeparator) + { + V_SplitString(pString, pSeparator, *this); + } - //void SplitString(char const* pString, char const* pSeparator) - //{ - // V_SplitString(pString, pSeparator, *this); - //} - - //void SplitString2(char const* pString, const char** pSeparators, int nSeparators) - //{ - // V_SplitString2(pString, pSeparators, nSeparators, *this); - //} + void SplitString2(char const* pString, const char** pSeparators, ssize_t nSeparators) + { + V_SplitString2(pString, pSeparators, nSeparators, *this); + } private: CUtlStringList(const CUtlStringList& other); // copying directly will cause double-release of the same strings; maybe we need to do a deep copy, but unless and until such need arises, this will guard against double-release }; diff --git a/r5dev/public/tier2/fileutils.h b/r5dev/public/tier2/fileutils.h index c05b220f..08ac3296 100644 --- a/r5dev/public/tier2/fileutils.h +++ b/r5dev/public/tier2/fileutils.h @@ -12,16 +12,16 @@ #include "filesystem/filesystem.h" // Builds a directory which is a subdirectory of the current mod. -void GetModSubdirectory( const char *pSubDir, char *pBuf, size_t nBufLen ); +void GetModSubdirectory( const char *pSubDir, char *pBuf, ssize_t nBufLen ); // Builds a directory which is a subdirectory of the current mod's *content*. -void GetModContentSubdirectory( const char *pSubDir, char *pBuf, size_t nBufLen ); +void GetModContentSubdirectory( const char *pSubDir, char *pBuf, ssize_t nBufLen ); // Generates a filename under the 'game' subdirectory given a subdirectory of 'content'. -void ComputeModFilename( const char *pContentFileName, char *pBuf, size_t nBufLen ); +void ComputeModFilename( const char *pContentFileName, char *pBuf, ssize_t nBufLen ); // Generates a filename under the 'content' subdirectory given a subdirectory of 'game'. -void ComputeModContentFilename( const char *pGameFileName, char *pBuf, size_t nBufLen ); +void ComputeModContentFilename( const char *pGameFileName, char *pBuf, ssize_t nBufLen ); // Finds all files matching the a name within a directory and its sub directories. Output entries are paths to found files (relative to and including szStartDirectory). void RecursiveFindFilesMatchingName( CUtlVector< CUtlString > &fileList, const char* szStartDirectory, const char* szTargetFileName, const char *pPathID, char separator = CORRECT_PATH_SEPARATOR); @@ -36,6 +36,6 @@ void GetSearchPath( CUtlVector< CUtlString > &pathList, const char *pPathID ); // 1. if its full path already return. // 2. if its a relative path try to find it under the path id. // 3. if find fails treat relative path as relative to the current dir. -bool GenerateFullPath( const char *pFileName, char const *pPathID, char *pBuf, size_t nBufLen ); +bool GenerateFullPath( const char *pFileName, char const *pPathID, char *pBuf, ssize_t nBufLen ); #endif // FILEUTILS_H diff --git a/r5dev/public/tier2/websocket.h b/r5dev/public/tier2/websocket.h new file mode 100644 index 00000000..0a38645f --- /dev/null +++ b/r5dev/public/tier2/websocket.h @@ -0,0 +1,144 @@ +//===========================================================================// +// +// Purpose: WebSocket implementation +// +//===========================================================================// +#ifndef TIER2_WEBSOCKET_H +#define TIER2_WEBSOCKET_H + +#define WEBSOCKET_DEFAULT_BUFFER_SIZE 1024 + +//----------------------------------------------------------------------------- +// forward declarations +//----------------------------------------------------------------------------- +struct ProtoWebSocketRefT; + +class CWebSocket +{ +public: + enum ConnState_e + { + // The socket has to be created and setup + CS_CREATE = 0, + + // The socket connection is established + CS_CONNECTED, + + // The socket is listening for data + CS_LISTENING, + + // The socket is destroyed and deallocated (if retries are set, the + // code will set the state to 'CS_RETRY' and reattempt to establish + // a connection up to ConnParams_s::maxRetries times + CS_DESTROYED, + + // The socket was destroyed and deallocated, and marked for a retry + // attempt + CS_RETRY, + + // The socket was destroyed and deallocated, and is marked unavailable. + // the code will remove this connection from the list and no further + // attempts will be made + CS_UNAVAIL + }; + + //------------------------------------------------------------------------- + // Connection parameters for the system & each individual connection, if + // these are changed, call CWebSocket::UpdateParams() to apply the new + // parameters on the system and each connection + //------------------------------------------------------------------------- + struct ConnParams_s + { + ConnParams_s() + { + bufSize = WEBSOCKET_DEFAULT_BUFFER_SIZE; + retryTime = 0.0f; + maxRetries = 0; + + timeOut = -1; + keepAlive = -1; + laxSSL = 0; + } + + // Total amount of buffer size that could be queued up and sent + int32_t bufSize; + + // Total amount of time between each connection attempt + float retryTime; + + // Maximum number of retries + // NOTE: the initial attempt is not counted as a retry attempt; if this + // field is set to 5, then the code will perform 1 connection attempt + + // 5 retries before giving up and marking this connection as unavailable + int32_t maxRetries; + + // Total amount of time in seconds before the connection is timing out + int32_t timeOut; + + // Time interval in seconds for the periodical keepalive pong message + int32_t keepAlive; + + // Whether to validate the clients certificate, if this is set, no + // validation is performed + int32_t laxSSL; + }; + + //------------------------------------------------------------------------- + // Represents an individual socket connection + //------------------------------------------------------------------------- + struct ConnContext_s + { + ConnContext_s(const char* const addr) + { + webSocket = nullptr; + address = addr; + + state = CS_CREATE; + + tryCount = 0; + lastQueryTime = 0; + } + + bool Connect(const double queryTime, const ConnParams_s& params); + bool Process(const double queryTime); + + void SetParams(const ConnParams_s& params); + + void Disconnect(); + void Reconnect(); + void Destroy(); + + ProtoWebSocketRefT* webSocket; + ConnState_e state; + + int tryCount; // Number of connection attempts + double lastQueryTime; + + CUtlString address; + }; + + CWebSocket(); + + bool Init(const char* const addressList, const ConnParams_s& params, const char*& initError); + void Shutdown(); + + bool UpdateAddressList(const char* const addressList); + void UpdateParams(const ConnParams_s& params); + + void Update(); + void DeleteUnavailable(); + + void DisconnectAll(); + void ReconnectAll(); + void ClearAll(); + + void SendData(const char* const dataBuf, const int32_t dataSize); + bool IsInitialized() const; + +private: + bool m_initialized; + ConnParams_s m_connParams; + CUtlVector m_addressList; +}; + +#endif // TIER2_WEBSOCKET_H diff --git a/r5dev/public/vphysics/phyfile.h b/r5dev/public/vphysics/phyfile.h new file mode 100644 index 00000000..d29c31d2 --- /dev/null +++ b/r5dev/public/vphysics/phyfile.h @@ -0,0 +1,22 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef PHYFILE_H +#define PHYFILE_H +#pragma once + +typedef struct phyheader_s +{ + int size; + int id; + short numsolids; + short align; + int checksum; // checksum of source .rmdl file + int keyvalueindex; +} phyheader_t; + +#endif // PHYFILE_H diff --git a/r5dev/public/vphysics/vcollide.h b/r5dev/public/vphysics/vcollide.h new file mode 100644 index 00000000..97a7d078 --- /dev/null +++ b/r5dev/public/vphysics/vcollide.h @@ -0,0 +1,26 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#ifndef VCOLLIDE_H +#define VCOLLIDE_H +#ifdef _WIN32 +#pragma once +#endif + +class CPhysCollide; + +struct vcollide_t +{ + unsigned short solidCount; // TODO: count num bits + unsigned short descSize; + // VPhysicsSolids + CPhysCollide** solids; + char* pKeyValues; + void* pUserData; +}; + +#endif // VCOLLIDE_H diff --git a/r5dev/public/vphysics/vphysics_interface.h b/r5dev/public/vphysics/vphysics_interface.h new file mode 100644 index 00000000..c5ab473c --- /dev/null +++ b/r5dev/public/vphysics/vphysics_interface.h @@ -0,0 +1,40 @@ +//=============================================================================// +// +// Purpose: Public interfaces to vphysics DLL +// +// $NoKeywords: $ +//=============================================================================// +#ifndef VPHYSICS_INTERFACE_H +#define VPHYSICS_INTERFACE_H +#include "public/vphysics/vcollide.h" + +#define VPHYSICS_COLLISION_INTERFACE_VERSION "VPhysicsCollision007" + +abstract_class IPhysicsCollision +{ +public: + virtual ~IPhysicsCollision(void) {} + +private: + // TODO: reverse these: + virtual void sub_14058C3B0() = 0; + virtual void sub_14058C3F0() = 0; + virtual void sub_14058CD80() = 0; + virtual void sub_14058C6E0() = 0; + virtual void sub_14058C6F0() = 0; + virtual void sub_14058CDD0() = 0; + virtual void sub_14058CB50() = 0; + virtual void sub_14058C980() = 0; + virtual void sub_14058D3D0() = 0; + virtual void sub_14058D400() = 0; + virtual void sub_14058C0D0() = 0; + virtual void sub_14058C060() = 0; + +public: + virtual void VCollideLoad(vcollide_t* const pOutput, const int numSolids, const char* const pBuffer) = 0; + virtual void VCollideUnload(vcollide_t* const pVCollide) = 0; + + // TODO: there is more past this, see r5apex.exe @1413A9420 +}; + +#endif // VPHYSICS_INTERFACE_H diff --git a/r5dev/public/vscript/ivscript.h b/r5dev/public/vscript/ivscript.h index 86a4f6b9..2d9960b8 100644 --- a/r5dev/public/vscript/ivscript.h +++ b/r5dev/public/vscript/ivscript.h @@ -15,17 +15,6 @@ DECLARE_POINTER_HANDLE(HSCRIPT); typedef int ScriptDataType_t; typedef void* ScriptFunctionBindingStorageType_t; -enum ScriptLanguage_t -{ - SL_NONE, - SL_GAMEMONKEY, - SL_SQUIRREL, - SL_LUA, - SL_PYTHON, - - SL_DEFAULT = SL_SQUIRREL -}; - //--------------------------------------------------------- enum ExtendedFieldType diff --git a/r5dev/resource/batch/clean_sdk.bat b/r5dev/resource/batch/clean_sdk.bat index a84700a3..4da73a6c 100644 --- a/r5dev/resource/batch/clean_sdk.bat +++ b/r5dev/resource/batch/clean_sdk.bat @@ -1,12 +1,13 @@ +REM Remove depot files. +rd /S /Q "%~dp0..\platform\depot" REM Remove log files ('log' is no longer used. 'logs' contains current logs, these get automatically cleaned if they exceed 10mb). -rd /S /Q "%~dp0log" -rd /S /Q "%~dp0logs" +rd /S /Q "%~dp0..\platform\log" +rd /S /Q "%~dp0..\platform\logs" REM Remove old NavMesh files which where included as an attempt to debug/suppress warnings. rd /S /Q "%~dp0..\maps" -rd /S /Q "%~dp0maps\graphs" -rd /S /Q "%~dp0maps\navmesh" +rd /S /Q "%~dp0..\platform\maps\graphs" +rd /S /Q "%~dp0..\platform\maps\navmesh" REM Remove deprecated binary and configuration files (these are no longer used). -del /Q "%~dp0..\gameinfo.txt" del /Q "%~dp0..\banlist.config" del /Q "%~dp0..\gui.config" del /Q "%~dp0..\Run R5 Reloaded.exe" diff --git a/r5dev/resource/cfg/system/autoexec.cfg b/r5dev/resource/cfg/system/autoexec.cfg index b562cd29..0bae6587 100644 --- a/r5dev/resource/cfg/system/autoexec.cfg +++ b/r5dev/resource/cfg/system/autoexec.cfg @@ -2,4 +2,3 @@ //// REPLICATED //// ////////////////////////// mp_allowed "1" // Whether multiplayer is allowed or not. -net_usesocketsforloopback "1" // Whether to use network sockets layer for packets. diff --git a/r5dev/resource/cfg/system/autoexec_dev.cfg b/r5dev/resource/cfg/system/autoexec_dev.cfg index 3939576c..2be6b217 100644 --- a/r5dev/resource/cfg/system/autoexec_dev.cfg +++ b/r5dev/resource/cfg/system/autoexec_dev.cfg @@ -11,7 +11,6 @@ developer "1" // Required for DEVELOPER script. ////////////////////////// net_useRandomKey "0" // Use a randomized netkey on launch. net_processTimeBudget "0" // Net message process budget in milliseconds (removing netchannel if exceeded). -net_usesocketsforloopback "1" // Whether to use network sockets layer for packets. ////////////////////////// //// SQUIRREL //// diff --git a/r5dev/resource/cfg/system/autoexec_server.cfg b/r5dev/resource/cfg/system/autoexec_server.cfg index 86eab247..01e59041 100644 --- a/r5dev/resource/cfg/system/autoexec_server.cfg +++ b/r5dev/resource/cfg/system/autoexec_server.cfg @@ -5,4 +5,3 @@ //// SERVER //// ////////////////////////// //hostname "R5" // Determines the name of the server displayed in the server browser. -sv_requireOriginToken "0" // Enables origin token verification on the server diff --git a/r5dev/resource/cfg/system/autoexec_server_dev.cfg b/r5dev/resource/cfg/system/autoexec_server_dev.cfg index 4095a935..415e17c2 100644 --- a/r5dev/resource/cfg/system/autoexec_server_dev.cfg +++ b/r5dev/resource/cfg/system/autoexec_server_dev.cfg @@ -24,6 +24,7 @@ sv_visualizetraces "1" // Show native and script related debug tr ////////////////////////// //// CAI //// ////////////////////////// +sv_spawnAIHintsInMP "1" // Must be set to cache the entire AI Network graph. ai_ainRebuildOnMapStart "0" // Whether to rebuild the AI Network graph on level load. ai_ainDumpOnLoad "0" // Whether to dump the parsed AI Network graph file loaded from disk. ai_ainDebugConnect "1" // Show AI Network graph connection debug. diff --git a/r5dev/resource/cfg/system/startup_client_retail.cfg b/r5dev/resource/cfg/system/startup_client_retail.cfg index f7251631..c72584ea 100644 --- a/r5dev/resource/cfg/system/startup_client_retail.cfg +++ b/r5dev/resource/cfg/system/startup_client_retail.cfg @@ -1,6 +1,3 @@ --ansicolor -fnf --multiple -showdevmenu --wconsole -playlistfile "playlists_r5_patch.txt" diff --git a/r5dev/resource/cfg/system/startup_default.cfg b/r5dev/resource/cfg/system/startup_default.cfg index cd1a6313..c72584ea 100644 --- a/r5dev/resource/cfg/system/startup_default.cfg +++ b/r5dev/resource/cfg/system/startup_default.cfg @@ -1,4 +1,3 @@ -fnf --multiple -showdevmenu -playlistfile "playlists_r5_patch.txt" diff --git a/r5dev/resource/cfg/system/startup_retail.cfg b/r5dev/resource/cfg/system/startup_retail.cfg index f7251631..c72584ea 100644 --- a/r5dev/resource/cfg/system/startup_retail.cfg +++ b/r5dev/resource/cfg/system/startup_retail.cfg @@ -1,6 +1,3 @@ --ansicolor -fnf --multiple -showdevmenu --wconsole -playlistfile "playlists_r5_patch.txt" diff --git a/r5dev/resource/cfg/tools/build_all_vpk.cfg b/r5dev/resource/cfg/tools/build_all_vpk.cfg index df572eae..7f0f56e4 100644 --- a/r5dev/resource/cfg/tools/build_all_vpk.cfg +++ b/r5dev/resource/cfg/tools/build_all_vpk.cfg @@ -1,2 +1,2 @@ -exec "tools/englishserver_build_vpk.cfg" -exec "tools/englishclient_build_vpk.cfg" +exec "tools/englishserver_pack_vpk.cfg" +exec "tools/englishclient_pack_vpk.cfg" diff --git a/r5dev/resource/cfg/tools/englishclient_build_vpk.cfg b/r5dev/resource/cfg/tools/englishclient_pack_vpk.cfg similarity index 100% rename from r5dev/resource/cfg/tools/englishclient_build_vpk.cfg rename to r5dev/resource/cfg/tools/englishclient_pack_vpk.cfg diff --git a/r5dev/resource/cfg/tools/englishclient_extract_vpk.cfg b/r5dev/resource/cfg/tools/englishclient_unpack_vpk.cfg similarity index 100% rename from r5dev/resource/cfg/tools/englishclient_extract_vpk.cfg rename to r5dev/resource/cfg/tools/englishclient_unpack_vpk.cfg diff --git a/r5dev/resource/cfg/tools/englishserver_build_vpk.cfg b/r5dev/resource/cfg/tools/englishserver_pack_vpk.cfg similarity index 100% rename from r5dev/resource/cfg/tools/englishserver_build_vpk.cfg rename to r5dev/resource/cfg/tools/englishserver_pack_vpk.cfg diff --git a/r5dev/resource/cfg/tools/englishserver_extract_vpk.cfg b/r5dev/resource/cfg/tools/englishserver_unpack_vpk.cfg similarity index 100% rename from r5dev/resource/cfg/tools/englishserver_extract_vpk.cfg rename to r5dev/resource/cfg/tools/englishserver_unpack_vpk.cfg diff --git a/r5dev/resource/patch/r5apex.patch b/r5dev/resource/patch/r5apex.patch index 3bcec091..ed57e30c 100644 --- a/r5dev/resource/patch/r5apex.patch +++ b/r5dev/resource/patch/r5apex.patch @@ -44,6 +44,15 @@ // screen. This satisfies the required memory size to render all 120 player models (active players on server). 0xE6530: "mov r8d, 200000h" +// This patch assigns the model and anim cache pointers to NULL instead of 0xDEADFEEDDEADFEED, +// if the asset failed to load. The 0xDEADFEEDDEADFEED (DC_INVALID_HANDLE) assignment was most +// likely done to catch errors in the asset loading system, since the pointer test won't fail +// while the pointer is invalid, this cause a hard crash. We however don't want this as this +// would cause issues loading BSP's with missing assets (the SDK handles the errors properly +// and also logs what was failed to load). +0x1E3CB6: "xor rax, rax" // NULL RAX instead of mov'ing '0xDEADFEEDDEADFEED' to cache ptr in 'Pak_UpdateModelAsset()' +0x1E3EE2: "xor rax, rax" // NULL RAX instead of mov'ing '0xDEADFEEDDEADFEED' to cache ptr in 'Pak_UpdateAnimRigAsset()' + ///////////////////////////// ///////////////////////////// //// Code defects //// diff --git a/r5dev/resource/patch/r5apex_ds.patch b/r5dev/resource/patch/r5apex_ds.patch index 95f156ee..43b5a3dc 100644 --- a/r5dev/resource/patch/r5apex_ds.patch +++ b/r5dev/resource/patch/r5apex_ds.patch @@ -22,3 +22,8 @@ // Prevent 'fps_max' from being reset in 'CEngine::Frame' if 'sv_cheats' isn't set; this is bound to the server's tickrate in sdk code to save CPU time. 0x296587: "jnz short loc_1402971B7" --> "jmp short loc_1402971B7" + +// Disable pak asset streaming system by calling Pak_Initialize() with mode 0, +// since the dedicated server doesn't use model or texture streaming, and as a +// result the files aren't shipped. +0x44BE87: "mov ecx, 0x1" --> "mov ecx, 0x0" diff --git a/r5dev/resource/protobuf/events.proto b/r5dev/resource/protobuf/events.proto new file mode 100644 index 00000000..d52d0231 --- /dev/null +++ b/r5dev/resource/protobuf/events.proto @@ -0,0 +1,781 @@ +////////////////////////////////////////////////////////////////////// +// Apex Legends Live API +// Copyright 2023 Respawn Entertainment +// +// Contains all messages used by LiveAPI with annotations as comments +// See readme.txt for more information on how to consume this file +////////////////////////////////////////////////////////////////////// + +syntax = "proto3"; + +package rtech.liveapi; + + +////////////////////////////////////////////////////////////////////// +// Intermediary messages: +// Not used directly, but as part of other messages +////////////////////////////////////////////////////////////////////// + +message Vector3 +{ + float x = 1; + float y = 2; + float z = 3; +} + +message Player +{ + string name = 1; + uint32 teamId = 2; + Vector3 pos = 3; + Vector3 angles = 4; + + uint32 currentHealth = 5; + uint32 maxHealth = 6; + uint32 shieldHealth = 7; + uint32 shieldMaxHealth = 8; + + string nucleusHash = 9; + string hardwareName = 10; + + string teamName = 11; + uint32 squadIndex = 12; + string character = 13; + string skin = 14; +} + +message CustomMatch_LobbyPlayer +{ + string name = 1; + uint32 teamId = 2; + + string nucleusHash = 3; + string hardwareName = 4; +} + +message Datacenter +{ + uint64 timestamp = 1; + string category = 2; + + string name = 3; +} + +message Version +{ + uint32 major_num = 1; + uint32 minor_num = 2; + uint32 build_stamp = 3; + string revision = 4; +} + +message InventoryItem +{ + int32 quantity = 1; + string item = 2; + + // any mods or additional info on the item + string extraData = 3; +} + +message LoadoutConfiguration +{ + repeated InventoryItem weapons = 1; + repeated InventoryItem equipment = 2; +} + +////////////////////////////////////////////////////////////////////// +// Output messages: +// Game events that describe the ongoing state of the match +// Every message will have a timestamp and category +////////////////////////////////////////////////////////////////////// + +// Traffic initialization +// This message is sent upon successfully connecting over WebSockets +message Init +{ + uint64 timestamp = 1; + string category = 2; + + string gameVersion = 3; + Version apiVersion = 4; + string platform = 5; + + // Named specified by `liveapi_session_name` + string name = 6; +} + +// Response to the CustomMatch_GetLobbyPlayers +// Contains the list of all players in the lobby +message CustomMatch_LobbyPlayers +{ + string playerToken = 1; + repeated CustomMatch_LobbyPlayer players = 2; +} + +///////////////////////////////////////// +// Observer Events +///////////////////////////////////////// + +// Event when the observer camera switches from viewing one player to another +message ObserverSwitched +{ + uint64 timestamp = 1; + string category = 2; + + Player observer = 3; + Player target = 4; + repeated Player targetTeam = 5; +} + +// Used by observers to annotate events uniquely +message ObserverAnnotation +{ + uint64 timestamp = 1; + string category = 2; + + int32 annotationSerial = 3; +} + + +///////////////////////////////////////// +// Match Information +///////////////////////////////////////// + +// Sent during the first phase of a match. This event gives a full description of what match is being played +message MatchSetup +{ + uint64 timestamp = 1; + string category = 2; + + string map = 3; + string playlistName = 4; + string playlistDesc = 5; + Datacenter datacenter = 6; + bool aimAssistOn = 7; + bool anonymousMode = 8; + string serverId = 9; + + LoadoutConfiguration startingLoadout = 10; +} + +// Sent whenever the match changes phases (e.g. prematch, playing) +message GameStateChanged +{ + uint64 timestamp = 1; + string category = 2; + + string state = 3; +} + +// Occurs when any player has locked in a character during legend select +message CharacterSelected +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; +} + +// Event to summarize the match after it has ended +message MatchStateEnd +{ + uint64 timestamp = 1; + string category = 2; + + string state = 3; + repeated Player winners = 4; +} + +// Fired whenever the ring begins moving in a match +message RingStartClosing +{ + uint64 timestamp = 1; + string category = 2; + + uint32 stage = 3; + Vector3 center = 4; + float currentRadius = 5; + float endRadius = 6; + float shrinkDuration= 7; +} + +// Used when the ring has finished moving and prior to it moving again +message RingFinishedClosing +{ + uint64 timestamp = 1; + string category = 2; + + uint32 stage = 3; + Vector3 center = 4; + float currentRadius = 5; + float shrinkDuration= 7; +} + +// Used when a player has connected to the match +message PlayerConnected +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; +} + +// Used when a player has disconnected, even temporarily +// `canReconnect` will indicate if the player is able to reconnect or has forfeited +message PlayerDisconnected +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + bool canReconnect = 4; + bool isAlive = 5; +} + +// Generic event for a change in the player stats +// Common stat names that can come with this event include "knockdowns", "revivesGiven", "kills" +message PlayerStatChanged +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string statName = 4; + + oneof newValue // R5R: formerly of type `uint32` + { + uint32 intValue = 5; + float floatValue= 6; + bool boolValue = 7; + } +} + +// Event used to notify when a player goes above their current tier level +// Tier levels start at 1. Following this event, players may have Upgrades to their legend +// Selection of upgrades will produce a separate `LegendUpgradeSelected` event +message PlayerUpgradeTierChanged +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + int32 level = 4; +} + +///////////////////////////////////////// +// Combat events +///////////////////////////////////////// + +// Event describing a player taking damage +// Details include the attacker, victim, the weapon used and the amount of damage +message PlayerDamaged +{ + uint64 timestamp = 1; + string category = 2; + + Player attacker = 3; + Player victim = 4; + string weapon = 5; + uint32 damageInflicted = 6; +} + +// Sent when a player is killed. Details are similar to PlayerDamaged event +// The `awardedTo` field describes the player that the kill is given to +message PlayerKilled +{ + uint64 timestamp = 1; + string category = 2; + + Player attacker = 3; + Player victim = 4; + Player awardedTo = 5; + string weapon = 6; +} + +// Event describing a player that has been downed after taking sufficient damage +// Similar to PlayerDamaged, but may not be sent in certain game modes (e.g. Control) +message PlayerDowned +{ + uint64 timestamp = 1; + string category = 2; + + Player attacker = 3; + Player victim = 4; + string weapon = 5; +} + +// Sent when a player is killed if there is an assist awarded +// This event may come in rapid succession to the PlayerKilled event with a corresponding `victim` field +message PlayerAssist +{ + uint64 timestamp = 1; + string category = 2; + + Player assistant = 3; + Player victim = 4; + string weapon = 5; +} + +// Occurs when the entire squad in a game has been eliminated +// The event contains all player in said squad. May not occur in certain game modes +message SquadEliminated +{ + uint64 timestamp = 1; + string category = 2; + + repeated Player players = 3; +} + +// Occurs when Gibraltars shield has taken any enemy damage +// The field `damageInflicted` will indicate how much was absorbed by the shield +message GibraltarShieldAbsorbed +{ + uint64 timestamp = 1; + string category = 2; + + Player attacker = 3; + Player victim = 4; + uint32 damageInflicted = 6; +} + +// Occurs when Revenant, while using his Forged Shadows ultimate, takes any enemy damage +// This event is distinct from `PlayerDamaged` since the player may receive no actual damage if the shadow is able to absorb it +// The field `damageInflicted` will indicate how much damage (in total) was dealt +// If there is any leftover damage that goes affects the player, that amount will be what is registered in a different `PlayerDamaged` event +message RevenantForgedShadowDamaged +{ + uint64 timestamp = 1; + string category = 2; + + Player attacker = 3; + Player victim = 4; + uint32 damageInflicted = 6; +} + +///////////////////////////////////////// +// Interaction events +///////////////////////////////////////// + +// Sent when a player is respawned and comes back into game +// For example, when using a respawn beacon +message PlayerRespawnTeam +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + repeated Player respawned = 4; // R5R: formerly of type `string` +} + +// Occurs when a player finishes assisting a downed player +// May not be sent in certain game modes (e.g. Control) +message PlayerRevive +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + Player revived = 4; +} + +// Specific Arenas-only event that occurs when players select an item +message ArenasItemSelected +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string item = 4; + int32 quantity = 5; +} + +// Specific Arenas-only event that occurs when players deselect an item +message ArenasItemDeselected +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string item = 4; + int32 quantity = 5; +} + +// Event that occurs when a player has picked up loot into their inventory +message InventoryPickUp +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string item = 4; + int32 quantity = 5; +} + +// Event that occurs when a player has dropped loot from their inventory +// The item itself may have attachments that will be described in the `extraData` field +message InventoryDrop +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string item = 4; + int32 quantity = 5; + repeated string extraData = 6; +} + +// Used to indicate the player has used a consumable item (e.g. syringe, shield cell) from their inventory +message InventoryUse +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string item = 4; + int32 quantity = 5; +} + +// Event used when a teammate banner has been picked up +message BannerCollected +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + Player collected = 4; +} + +// Used to indicate that the player has activated one of their legend's abilities +// The ability can be a Tactical or an Ultimate and is decribed in the `linkedEntity` field +// For example: `linkedEntity: "Tactical (Eye of the Allfather)"` for Bloodhound's tactical +message PlayerAbilityUsed +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string linkedEntity = 4; +} + +// Signals that a player has selected an upgrade at a particular tier level +// Updates to their tier level will be sent as a PlayerUpgradeTierChanged event +message LegendUpgradeSelected +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string upgradeName = 4; + string upgradeDesc = 5; + int32 level = 6; +} + +// Indicates that a player has started using the zipline +message ZiplineUsed +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string linkedEntity = 4; +} + +// Used to indicate that a player has tossed a grenade +// The `linkedEntity` will describe the grenade in further detail and it may be a legend's Ability +// For example: `linkedEntity: "Ultimate (Rolling Thunder)"` for Bangalore's Ultimate ability +message GrenadeThrown +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string linkedEntity = 4; +} + +// Event specifying that a player has picked up loot from Loba's Black Market +// This event may fire in quick succession to the InventoryPickUp event +message BlackMarketAction +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string item = 4; +} + +// Used to indicate a player has traversed a Wraith Portal +message WraithPortal +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; +} + +// Used to indicate a player has traversed a Warp Gate +message WarpGateUsed +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; +} + +// Used to indicate that a player has used ammo +// This event may not fire immediately and updates may be batched to save bandwidth +message AmmoUsed +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string ammoType = 4; + uint32 amountUsed = 5; + uint32 oldAmmoCount = 6; + uint32 newAmmoCount = 7; +} + +// Used to indicate that a player has switched weapons, either to a weapon in their inventory or swapped with a weapon on the ground +message WeaponSwitched +{ + uint64 timestamp = 1; + string category = 2; + + Player player = 3; + string oldWeapon = 4; + string newWeapon = 5; +} + +///////////////////////////////////////// +// Custom events +///////////////////////////////////////// + +import "google/protobuf/struct.proto"; + +// Event defining custom user data that is otherwise too specific to create dedicated messages for +message CustomEvent +{ + uint64 timestamp = 1; + string category = 2; + + string name = 3; + google.protobuf.Struct data = 4; +} + +////////////////////////////////////////////////////////////////////// +// Input messages: +// Used by observers to programmatically interact with the game +////////////////////////////////////////////////////////////////////// + + +// Enum used to quickly described the target of a ChangeCamera operation +enum PlayerOfInterest +{ + UNSPECIFIED = 0; + + // cycle through known Players in a team + NEXT = 1; + PREVIOUS = 2; + + // Go to an interesting player + KILL_LEADER = 3; + CLOSEST_ENEMY = 4; + CLOSEST_PLAYER = 5; + LATEST_ATTACKER = 6; +} + +// Request to change the observer camera +// If changing by a target's name, be aware that the +// - server may skip the request if the player is not actively in the game (i.e. waiting for reconnect, downed or killed) +// - If the string is longer than 256 characters, the request will fail +message ChangeCamera +{ + oneof target + { + // Set the camera to an interesting player (e.g. the Kill Leader) + PlayerOfInterest poi = 1; + + // Change camera to a player by name + string name = 2; + } +} + +// Request message to toggle pause in a match type that supports it +message PauseToggle +{ + float preTimer = 1; +} + +// Request to create a custom match lobby +message CustomMatch_CreateLobby +{ +} + +// Request to join an existing custom match lobby identified by the `roleToken` +message CustomMatch_JoinLobby +{ + string roleToken = 1; +} + +// Request to leave a custom match lobby +message CustomMatch_LeaveLobby +{ +} + +// Request to programatically change your player's ready state in a custom match lobby +message CustomMatch_SetReady +{ + bool isReady = 1; +} + +// Request to retrieve all connected players in a custom match lobby +message CustomMatch_GetLobbyPlayers +{ +} + +// Request to change the state of matchmaking in a custom match lobby +// When enabled is True, the lobby will attempt to being a match +message CustomMatch_SetMatchmaking +{ + bool enabled = 1; +} + +// Request to assign a particular player to a specific team +// Note that the `targetHardwareName` and `targetNucleusHash` can be obtained from a prior request to CustomMatch_GetLobbyPlayers +// If the parameters do not match any lobby player, the request is ignored +// The `teamId` is across the entire lobby. Meaning, observers have a teamId of 0 and match players will be teamId of 1 and upwards +message CustomMatch_SetTeam +{ + int32 teamId = 1; + string targetHardwareName = 2; + string targetNucleusHash = 3; +} + +// Request to remove a player from the currently connected custom match lobby +message CustomMatch_KickPlayer +{ + string targetHardwareName = 1; + string targetNucleusHash = 2; +} + +// Request to alter the settings of a custom match lobby +// Your request should specify all fields being set with the new value +// For convinience, call `CustomMatch_GetSettings` to get the full state of settings +message CustomMatch_SetSettings +{ + string playlistName = 1; + bool adminChat = 2; + bool teamRename = 3; + bool selfAssign = 4; + bool aimAssist = 5; + bool anonMode = 6; +} + +// Review all the current settings. This request will be replied to with +// `CustomMatch_SetSettings` from which you can modify and reply with any new values for your convenience +message CustomMatch_GetSettings +{ +} + +// Request to set the name of a team in custom match lobby +// Requires special access and is subject to text filtering +message CustomMatch_SetTeamName +{ + int32 teamId = 1; + string teamName = 2; +} + +// Request to programatically send a chat message to the entire custom match lobby +message CustomMatch_SendChat +{ + string text = 1; +} + +// Envelope message for any Live API request +// This allows a single uniform data structure for requests to be made and for the game to receive them +// Specifically, there is only one possible action per request. You can request an acknowledgement of your request by setting `withAck` to true +// Acknowledgements will come in the form of a Response message. More information can be found with that event +// +// A single example to create a CustomMatch_JoinLobby request in python is as follows +// ``` +// req = Request() +// req.customMatch_JoinLobby.roleToken = "" +// req.withAck = True +// ``` +// For more information, consult the Protobuf documentation for your language of choice and look at details regarding the `oneof` field (https://protobuf.dev/programming-guides/proto3/#oneof) +message Request +{ + // Receive an acknowledgement of the request having been received + bool withAck = 1; + + // Preshared key to use with the request. Only necessary if the connecting game has a preshared key specified through `cl_liveapi_requests_psk` + string preSharedKey = 2; + + oneof actions + { + ChangeCamera changeCam = 4; + PauseToggle pauseToggle = 5; + + // Custom Match specific requests (reserved 10 -> 30) + CustomMatch_CreateLobby customMatch_CreateLobby = 10; + CustomMatch_JoinLobby customMatch_JoinLobby = 11; + CustomMatch_LeaveLobby customMatch_LeaveLobby = 12; + CustomMatch_SetReady customMatch_SetReady = 13; + CustomMatch_SetMatchmaking customMatch_SetMatchmaking = 14; + CustomMatch_SetTeam customMatch_SetTeam = 15; + CustomMatch_KickPlayer customMatch_KickPlayer = 16; + CustomMatch_SetSettings customMatch_SetSettings = 17; + CustomMatch_SendChat customMatch_SendChat = 18; + CustomMatch_GetLobbyPlayers customMatch_GetLobbyPlayers = 19; + CustomMatch_SetTeamName customMatch_SetTeamName = 20; + CustomMatch_GetSettings customMatch_GetSettings = 21; + + } +} + +////////////////////////////////////////////////////////////////////// +// Reply messages: +// Used by the game to send data to any connected clients +////////////////////////////////////////////////////////////////////// + +import "google/protobuf/any.proto"; + +// Message used to indicate the status of a request +// Generally, it is used to provide a plain text, detailed response in case of failures or problems +message RequestStatus +{ + string status = 1; +} + +// Message used to indicate the response to a request made to the API +// Only the requesting part will receive this message and this message is only sent if the request required an acknowledgement by setting `withAck` to true in the Request object +// This message is always sent within a LiveAPIEvent and never on its own to allow any applications to have a uniform method of reading events over the wire +// If `success` is true, it does not mean that the Request has finished or that it was completed correctly. In this case, it means that it was successfully received and contains no issues (it is a well-formed request) +// The `result` field may sometimes be populated to give more context around the request, especially in the case of error +// Refer to the LiveAPIEvent message on how to the the Any field +message Response +{ + bool success = 1; + google.protobuf.Any result = 2; +} + +// Envelope for all LiveAPI Events +// Any game events or responses to requests will be sent using this message. The specific event or message is stored in the `gameMessage` field +// Before proceeding, familiarize yourself with the proto3 `Any` field type at: https://protobuf.dev/programming-guides/proto3/#any +// In order to read the message successfully, check the type contained in `gameMessage` and create an instance of that type where you can unpack the data to +// Protobuf has several ways of doing type to instance lookups that will allow you to do this after you've generated bindings +// For example, to read and unpack any LiveAPIEvent in Python, the following can be done (assume `pb_msg` contains the LiveAPIEvent object) +// ``` +// from events_pb2 import * +// from google.protobuf import symbol_database +// [ ... ] +// result_type = pb_msg.gameMessage.TypeName() +// msg_result = symbol_database.Default().GetSymbol(result_type)() +// pb_msg.gameMessage.Unpack(msg_result) # msg_result now holds the actual event you want to read +// ``` +message LiveAPIEvent +{ + fixed32 event_size = 1; + google.protobuf.Any gameMessage = 3; +} diff --git a/r5dev/resource/protobuf/generate.bat b/r5dev/resource/protobuf/generate.bat index 21d31785..e179f451 100644 --- a/r5dev/resource/protobuf/generate.bat +++ b/r5dev/resource/protobuf/generate.bat @@ -1,3 +1,4 @@ protoc64 --cpp_out=. sig_map.proto protoc64 --cpp_out=. sv_rcon.proto protoc64 --cpp_out=. cl_rcon.proto +protoc64 --cpp_out=. events.proto diff --git a/r5dev/revpk/CMakeLists.txt b/r5dev/revpk/CMakeLists.txt new file mode 100644 index 00000000..2ed543c9 --- /dev/null +++ b/r5dev/revpk/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required( VERSION 3.16 ) +add_module( "exe" "revpk" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "Private" + "revpk.cpp" + "${ENGINE_SOURCE_DIR}/core/logdef.cpp" + "${ENGINE_SOURCE_DIR}/core/logdef.h" + "${ENGINE_SOURCE_DIR}/core/logger.cpp" + "${ENGINE_SOURCE_DIR}/core/logger.h" + "${ENGINE_SOURCE_DIR}/core/termutil.cpp" + "${ENGINE_SOURCE_DIR}/core/termutil.h" + "${ENGINE_SOURCE_DIR}/tier0/plat_time.cpp" +) + +add_sources( SOURCE_GROUP "Windows" + "${ENGINE_SOURCE_DIR}/windows/console.cpp" + "${ENGINE_SOURCE_DIR}/windows/console.h" +) + +end_sources( "${BUILD_OUTPUT_DIR}/bin/" ) + +set_target_properties( ${PROJECT_NAME} PROPERTIES + VS_DEBUGGER_COMMAND "revpk.exe" + VS_DEBUGGER_WORKING_DIRECTORY "$(ProjectDir)../../../${BUILD_OUTPUT_DIR}/bin/" +) +target_compile_definitions( ${PROJECT_NAME} PRIVATE + "_TOOLS" +) + +target_link_libraries( ${PROJECT_NAME} PRIVATE + "vpc" + "tier0" + "tier1" + "filesystem_std" + "vstdlib" + "mathlib" + "vpklib" + + "libspdlog" + "liblzham" + "Rpcrt4.lib" +) diff --git a/r5dev/revpk/revpk.cpp b/r5dev/revpk/revpk.cpp new file mode 100644 index 00000000..bdf5c40c --- /dev/null +++ b/r5dev/revpk/revpk.cpp @@ -0,0 +1,282 @@ +//=============================================================================// +// +// Purpose: Standalone VPK tool +// +//=============================================================================// +#include "core/logdef.h" +#include "core/logger.h" +#include "tier0/fasttimer.h" +#include "tier0/cpu.h" +#include "tier1/cmd.h" +#include "tier1/fmtstr.h" +#include "tier1/keyvalues.h" +#include "windows/console.h" +#include "vpklib/packedstore.h" + +#include "vstdlib/keyvaluessystem.h" +#include "filesystem/filesystem_std.h" + +#define PACK_COMMAND "pack" +#define UNPACK_COMMAND "unpack" + +#define PACK_LOG_DIR "manifest/pack_logs/" +#define UNPACK_LOG_DIR "manifest/unpack_logs/" + +#define FRONTEND_ENABLE_FILE "enable.txt" + +static CKeyValuesSystem s_KeyValuesSystem; +static CFileSystem_Stdio g_FullFileSystem; +static bool s_bUseAnsiColors = true; + +//----------------------------------------------------------------------------- +// Purpose: keyvalues singleton accessor +//----------------------------------------------------------------------------- +IKeyValuesSystem* KeyValuesSystem() +{ + return &s_KeyValuesSystem; +} + +//----------------------------------------------------------------------------- +// Purpose: filesystem singleton accessor +//----------------------------------------------------------------------------- +CFileSystem_Stdio* FileSystem() +{ + return &g_FullFileSystem; +} + +//----------------------------------------------------------------------------- +// Purpose: init +//----------------------------------------------------------------------------- +static void ReVPK_Init() +{ + CheckCPUforSSE2(); + + g_CoreMsgVCallback = EngineLoggerSink; + lzham_enable_fail_exceptions(true); + + Console_Init(s_bUseAnsiColors); + SpdLog_Init(s_bUseAnsiColors); +} + +//----------------------------------------------------------------------------- +// Purpose: shutdown +//----------------------------------------------------------------------------- +static void ReVPK_Shutdown() +{ + // Must be done to flush all buffers. + SpdLog_Shutdown(); + Console_Shutdown(); +} + +//----------------------------------------------------------------------------- +// Purpose: logs tool's usage +//----------------------------------------------------------------------------- +static void ReVPK_Usage() +{ + CFmtStr1024 usage; + + usage.Format( + "ReVPK instructions and options:\n" + "For packing; run 'revpk %s' with the following parameters:\n" + "\t<%s>\t- locale prefix for the directory tree file\n" + "\t<%s>\t- context scope for the VPK files [\"server\", \"client\"]\n" + "\t<%s>\t- level name for the VPK files\n" + "\t<%s>\t- ( optional ) path to the workspace containing the manifest file\n" + "\t<%s>\t- ( optional ) path in which the VPK files will be built\n" + "\t<%s>\t- ( optional ) max LZHAM helper threads [\"%d\", \"%d\"] \"%d\" ( default ) for max practical\n" + "\t<%s>\t- ( optional ) the level of compression [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"]\n\n" + + "For unpacking; run 'revpk %s' with the following parameters:\n" + "\t<%s>\t- path and name of the target directory tree or data block file\n" + "\t<%s>\t- ( optional ) path in which the VPK files will be unpacked\n" + "\t<%s>\t- ( optional ) whether to parse the directory tree file name from the data block file name\n", + + PACK_COMMAND, // Pack parameters: + "locale", "context", "levelName", "workspacePath", "buildPath", + + "numThreads", // Num helper threads. + -1, LZHAM_MAX_HELPER_THREADS, -1, + + "compressLevel", // Compress level. + "fastest", "faster", "default", "better", "uber", + + UNPACK_COMMAND,// Unpack parameters: + "fileName", "inputDir", "sanitize" + ); + + Warning(eDLL_T::FS, "%s", usage.Get()); +} + +//----------------------------------------------------------------------------- +// Purpose: writes the VPK front-end enable file +//----------------------------------------------------------------------------- +static void ReVPK_WriteEnableFile(const char* containingPath) +{ + CFmtStr1024 textFileName; + textFileName.Format("%s%s", containingPath, FRONTEND_ENABLE_FILE); + + if (FileSystem()->FileExists(textFileName.String(), "PLATFORM")) + { + // Already exists. + return; + } + + FileHandle_t enableTxt = FileSystem()->Open(textFileName.String(), "wb", "PLATFORM"); + + if (enableTxt) + { + const char* textData = "1 \r\n"; + FileSystem()->Write(textData, strlen(textData), enableTxt); + FileSystem()->Close(enableTxt); + } + else + { + Error(eDLL_T::FS, NO_ERROR, "Failed to write front-end enable file \"%s\"; insufficient rights?\n", + textFileName.String()); + } +} + +//----------------------------------------------------------------------------- +// Purpose: packs VPK files into 'SHIP' VPK directory +//----------------------------------------------------------------------------- +static void ReVPK_Pack(const CCommand& args) +{ + const int argCount = args.ArgC(); + + if (argCount < 5) + { + ReVPK_Usage(); + return; + } + + CFmtStr1024 workspacePath = argCount > 5 ? args.Arg(5) : "ship/"; + CFmtStr1024 buildPath = argCount > 6 ? args.Arg(6) : "vpk/"; + + // Make sure there's always a trailing slash. + V_AppendSlash(workspacePath.Access(), workspacePath.GetMaxLength(), '/'); + V_AppendSlash(buildPath.Access(), buildPath.GetMaxLength(), '/'); + + if (!FileSystem()->IsDirectory(workspacePath, "PLATFORM")) + { + Error(eDLL_T::FS, NO_ERROR, "Workspace path \"%s\" doesn't exist!\n", workspacePath.String()); + return; + } + + const char* localeName = args.Arg(2); + const char* contextName = args.Arg(3); + const char* levelName = args.Arg(4); + + // For clients, we need an enable file which the engine uses to determine + // whether or not to mount the front-end VPK file. + if (V_strcmp(contextName, DIR_TARGET[EPackedStoreTargets::STORE_TARGET_CLIENT]) == NULL) + { + ReVPK_WriteEnableFile(buildPath); + } + + // Write the pack log to a file. + CFmtStr1024 textFileName("%s%s%s%s_%s.log", buildPath.String(), PACK_LOG_DIR, localeName, contextName, levelName); + SpdLog_InstallSupplementalLogger("supplemental_logger_mt", textFileName.String()); + + VPKPair_t pair(localeName, contextName, levelName, NULL); + Msg(eDLL_T::FS, "*** Starting VPK build command for: '%s'\n", pair.m_DirName.Get()); + + CFastTimer timer; + timer.Start(); + + CPackedStoreBuilder builder; + + builder.InitLzEncoder( + argCount > 7 ? (std::min)(atoi(args.Arg(7)), LZHAM_MAX_HELPER_THREADS) : -1, // Num threads. + argCount > 8 ? args.Arg(8) : "default"); // Compress level. + + builder.PackStore(pair, workspacePath.String(), buildPath.String()); + + timer.End(); + Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds()); + Msg(eDLL_T::FS, "\n"); +} + +//----------------------------------------------------------------------------- +// Purpose: unpacks VPK files into workspace directory +//----------------------------------------------------------------------------- +static void ReVPK_Unpack(const CCommand& args) +{ + const int argCount = args.ArgC(); + + if (argCount < 3) + { + ReVPK_Usage(); + return; + } + + const char* fileName = args.Arg(2); + const char* outPath = argCount > 3 ? args.Arg(3) : "ship/"; + const bool sanitize = argCount > 4 ? atoi(args.Arg(4)) != NULL : false; + + VPKDir_t vpk(fileName, sanitize); + + // Make sure the VPK directory tree was actually parsed correctly. + if (vpk.Failed()) + { + Error(eDLL_T::FS, NO_ERROR, "Failed to parse directory tree file \"%s\"!\n", fileName); + return; + } + + CUtlString baseName = PackedStore_GetDirBaseName(vpk.m_DirFilePath); + + // Write the unpack log to a file. + CFmtStr1024 textFileName("%s%s%s.log", outPath, UNPACK_LOG_DIR, baseName.String()); + SpdLog_InstallSupplementalLogger("supplemental_logger_mt", textFileName.String()); + + const char* actualDirFile = vpk.m_DirFilePath.String(); + Msg(eDLL_T::FS, "*** Starting VPK extraction command for: '%s'\n", actualDirFile); + + CFastTimer timer; + timer.Start(); + + CPackedStoreBuilder builder; + + builder.InitLzDecoder(); + builder.UnpackStore(vpk, argCount > 3 ? args.Arg(3) : "ship/"); + + timer.End(); + Msg(eDLL_T::FS, "*** Time elapsed: '%lf' seconds\n", timer.GetDuration().GetSeconds()); + Msg(eDLL_T::FS, "\n"); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + ReVPK_Init(); + + CCommand args; + CUtlString str; + + for (int i = 0; i < argc; i++) + { + str.Append(argv[i]); + str.Append(' '); + } + + args.Tokenize(str.Get(), cmd_source_t::kCommandSrcCode); + + if (!args.ArgC()) { + ReVPK_Usage(); + } + else + { + if (V_strcmp(args.Arg(1), PACK_COMMAND) == NULL) { + ReVPK_Pack(args); + } + else if (V_strcmp(args.Arg(1), UNPACK_COMMAND) == NULL) { + ReVPK_Unpack(args); + } + else { + ReVPK_Usage(); + } + } + + ReVPK_Shutdown(); +} diff --git a/r5dev/rtech/CMakeLists.txt b/r5dev/rtech/CMakeLists.txt index 9bde18bb..8be05513 100644 --- a/r5dev/rtech/CMakeLists.txt +++ b/r5dev/rtech/CMakeLists.txt @@ -3,28 +3,75 @@ add_module( "lib" "rtech_game" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) start_sources() -add_sources( SOURCE_GROUP "Private" - "rtech_game.cpp" - "rtech_game.h" +add_sources( SOURCE_GROUP "Async" + "async/asyncio.cpp" + "async/asyncio.h" +) + +add_sources( SOURCE_GROUP "Pak" + "pak/pakparse.cpp" + "pak/pakparse.h" + + "pak/pakalloc.cpp" + "pak/pakalloc.h" + + "pak/pakencode.cpp" + "pak/pakencode.h" + + "pak/pakdecode.cpp" + "pak/pakdecode.h" + + "pak/pakpatch.cpp" + "pak/pakpatch.h" + + "pak/pakstate.cpp" + "pak/pakstate.h" + + "pak/pakstream.cpp" + "pak/pakstream.h" + + "pak/paktools.cpp" + "pak/paktools.h" +) + +add_sources( SOURCE_GROUP "LiveAPI" + "liveapi/liveapi.cpp" + "liveapi/liveapi.h" ) add_sources( SOURCE_GROUP "Public" + "${ENGINE_SOURCE_DIR}/public/rtech/iasync.h" "${ENGINE_SOURCE_DIR}/public/rtech/ipakfile.h" ) end_sources() -add_module( "lib" "rtech_tools" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) +target_include_directories( ${PROJECT_NAME} PRIVATE + "${THIRDPARTY_SOURCE_DIR}/dirtysdk/include/" + "${THIRDPARTY_SOURCE_DIR}/ea/" +) + +add_module( "lib" "rson" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "RSON" + "rdf/rson.cpp" +) + +add_sources( SOURCE_GROUP "Public" + "${ENGINE_SOURCE_DIR}/public/rtech/rson.h" +) + +end_sources() + +add_module( "lib" "playlists" "vpc" ${FOLDER_CONTEXT} TRUE TRUE ) start_sources() add_sources( SOURCE_GROUP "Source" - "rtech_utils.cpp" - "rtech_utils.h" -) - -add_sources( SOURCE_GROUP "Public" - "${ENGINE_SOURCE_DIR}/public/rtech/ipakfile.h" + "playlists/playlists.cpp" + "playlists/playlists.h" ) end_sources() diff --git a/r5dev/rtech/async/asyncio.cpp b/r5dev/rtech/async/asyncio.cpp new file mode 100644 index 00000000..0c945233 --- /dev/null +++ b/r5dev/rtech/async/asyncio.cpp @@ -0,0 +1,88 @@ +//=============================================================================// +// +// Purpose: async file loading, unloading and the management thereof +// +//=============================================================================// +#include "rtech/ipakfile.h" +#include "rtech/pak/paktools.h" +#include "asyncio.h" + +ConVar async_debug_level("async_debug_level", "0", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "The debug level for async reads", false, 0.f, false, 0.f, "0 = disabled"); +ConVar async_debug_close("async_debug_close", "0", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "Debug async file closing", false, 0.f, false, 0.f, "0 = disabled"); + +//---------------------------------------------------------------------------------- +// open a file and add it to the async file handle array +//---------------------------------------------------------------------------------- +int FS_OpenAsyncFile(const char* const filePath, const int logLevel, size_t* const fileSizeOut) +{ + const CHAR* fileToLoad = filePath; + char overridePath[1024]; + + // function can be called with null strings, for example if optional + // streaming sets are missing; check for it + if (fileToLoad && *fileToLoad) + { + // is this a pak file and do we have an override + if (strstr(fileToLoad, PLATFORM_PAK_PATH) && + Pak_FileOverrideExists(fileToLoad, overridePath, sizeof(overridePath))) + { + fileToLoad = overridePath; + } + } + + const HANDLE hFile = CreateFileA(fileToLoad, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_SUPPORTS_GHOSTING, 0); + + if (hFile == INVALID_HANDLE_VALUE) + return FS_ASYNC_FILE_INVALID; + + if (fileSizeOut) + { + // get the size of the file we just opened + LARGE_INTEGER fileSize; + + if (GetFileSizeEx(hFile, &fileSize)) + *fileSizeOut = fileSize.QuadPart; + } + + const int fileIdx = g_pAsyncFileSlotMgr->FindSlot(); + const int slotNum = (fileIdx & ASYNC_MAX_FILE_HANDLES_MASK); + + AsyncHandleTracker_t& tracker = g_pAsyncFileSlots[slotNum]; + + tracker.slot = fileIdx; + tracker.handle = hFile; + tracker.state = 1; + + if (async_debug_level.GetInt() >= logLevel) + Msg(eDLL_T::RTECH, "%s: Opened file: '%s' to slot #%d\n", __FUNCTION__, fileToLoad, slotNum); + + return fileIdx; +} + +//---------------------------------------------------------------------------------- +// close a file and remove it from the async file handle array +//---------------------------------------------------------------------------------- +void FS_CloseAsyncFile(const int fileHandle) +{ + const int slotNum = fileHandle & ASYNC_MAX_FILE_HANDLES_MASK; + AsyncHandleTracker_t& tracker = g_pAsyncFileSlots[slotNum]; + + if (ThreadInterlockedExchangeAdd(&tracker.state, -1) <= 1) + { + CloseHandle(tracker.handle); + tracker.handle = INVALID_HANDLE_VALUE; + + g_pAsyncFileSlotMgr->FreeSlot(slotNum); + + if (async_debug_close.GetBool()) + Msg(eDLL_T::RTECH, "%s: Closed file from slot #%d\n", __FUNCTION__, slotNum); + } +} + +/////////////////////////////////////////////////////////////////////////////// +void V_AsyncIO::Detour(const bool bAttach) const +{ + DetourSetup(&v_FS_OpenAsyncFile, &FS_OpenAsyncFile, bAttach); + DetourSetup(&v_FS_CloseAsyncFile, &FS_CloseAsyncFile, bAttach); +} diff --git a/r5dev/rtech/async/asyncio.h b/r5dev/rtech/async/asyncio.h new file mode 100644 index 00000000..67db3c47 --- /dev/null +++ b/r5dev/rtech/async/asyncio.h @@ -0,0 +1,106 @@ +#ifndef RTECH_ASYNCIO_H +#define RTECH_ASYNCIO_H +#include "rtech/iasync.h" +#include "rtech/rstdlib.h" + +struct AsyncHandleTracker_t +{ + int slot; + int state; + HANDLE handle; +}; + +struct AsyncHandleStatus_t +{ + enum EStatus : uint8_t + { + // the file is still pending, or being read at this moment + FS_ASYNC_PENDING = 0, + + // the file is ready to be used + FS_ASYNC_READY, + + // there was an error while reading the file + FS_ASYNC_ERROR, + + // async read operations were canceled + FS_ASYNC_CANCELLED + }; + + int slot; + int unk2; + int unk3; + int unk4; + __int64 unk5; + + // pointer to user defined data + void* userData; + + void* unkFunctionPointer; + void* unkStatusPointer; + int unk6; + int unkFlag0; + __int16 unkFlag2; + __int16 unkFlag3; + __int16 unk8; + EStatus readStatus; +}; +static_assert(sizeof(AsyncHandleStatus_t) == 0x40); + +extern int FS_OpenAsyncFile(const char* const filePath, const int logLevel, size_t* const fileSizeOut); +extern void FS_CloseAsyncFile(const int fileHandle); + +inline int(*v_FS_OpenAsyncFile)(const char* const filePath, const int logLevel, size_t* const outFileSize); +inline void(*v_FS_CloseAsyncFile)(const int fileHandle); + +inline int(*v_FS_ReadAsyncFile)(const int fileHandle, __int64 readOffset, unsigned __int64 readSize, void* a4, void* a5, void* a6, int a7); +inline uint8_t(*v_FS_CheckAsyncRequest)(AsyncHandleStatus_t* pakStatus, size_t* bytesProcessed, const char** stateString); + +extern ConVar async_debug_level; + +inline AsyncHandleTracker_t* g_pAsyncFileSlots; // bufSize=1024*sizeof(FileHandleTracker_t). +inline RHashMap_MT* g_pAsyncFileSlotMgr; // Manages 'g_pakFileSlots'. +inline AsyncHandleStatus_t* g_pAsyncStatusSlots; // bufSize=256*sizeof(PakStatus_t). +inline RHashMap_MT* g_pAsyncStatusSlotMgr; // Manages 'g_pakStatusSlots'. + +class V_AsyncIO : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("FS_OpenAsyncFile", v_FS_OpenAsyncFile); + LogFunAdr("FS_CloseAsyncFile", v_FS_CloseAsyncFile); + + LogFunAdr("FS_ReadAsyncFile", v_FS_ReadAsyncFile); + LogFunAdr("FS_CheckAsyncRequest", v_FS_CheckAsyncRequest); + + LogVarAdr("g_asyncFileSlots", g_pAsyncFileSlots); + LogVarAdr("g_asyncFileSlotMgr", g_pAsyncFileSlotMgr); + + LogVarAdr("g_asyncStatusSlots", g_pAsyncStatusSlots); + LogVarAdr("g_asyncStatusSlotMgr", g_pAsyncStatusSlotMgr); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 89 85 08 01 ?? ??").FollowNearCallSelf().GetPtr(v_FS_OpenAsyncFile); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B D9 48 8D 35 ?? ?? ?? ??").GetPtr(v_FS_CloseAsyncFile); + + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 4C 89 64 24 ?? 41 55 41 56 41 57 48 83 EC 20 8B C1").GetPtr(v_FS_ReadAsyncFile); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 0F B6 79").GetPtr(v_FS_CheckAsyncRequest); + } + virtual void GetVar(void) const + { + extern void(*v_StreamDB_Init)(const char* const pszLevelName); + const CMemory streamDbBase(v_StreamDB_Init); + + g_pAsyncFileSlots = streamDbBase.Offset(0x70).FindPatternSelf("4C 8D", CMemory::Direction::DOWN, 512, 1).ResolveRelativeAddress(0x3, 0x7).RCast(); + g_pAsyncFileSlotMgr = streamDbBase.Offset(0x70).FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 512, 2).ResolveRelativeAddress(0x3, 0x7).RCast(); + + g_pAsyncStatusSlots = g_GameDll.FindPatternSIMD("0F B6 C9 48 8D 05 ?? ?? ?? ??").FindPatternSelf("48 8D 05").ResolveRelativeAddress(0x3, 0x7).RCast(); + g_pAsyncStatusSlotMgr = streamDbBase.Offset(0x190).FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 512, 2).ResolveRelativeAddress(0x3, 0x7).RCast(); + } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const; +}; + + +#endif // !ASYNCIO_H diff --git a/r5dev/rtech/liveapi/liveapi.cpp b/r5dev/rtech/liveapi/liveapi.cpp new file mode 100644 index 00000000..818b6e3a --- /dev/null +++ b/r5dev/rtech/liveapi/liveapi.cpp @@ -0,0 +1,299 @@ +//===========================================================================// +// +// Purpose: LiveAPI WebSocket implementation +// +//===========================================================================// +#include "liveapi.h" +#include "protobuf/util/json_util.h" + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protowebsocket.h" + +//----------------------------------------------------------------------------- +// change callbacks +//----------------------------------------------------------------------------- +static void LiveAPI_EnabledChangedCallback(IConVar* var, const char* pOldValue) +{ + LiveAPISystem()->ToggleInit(); +} +static void LiveAPI_WebSocketEnabledChangedCallback(IConVar* var, const char* pOldValue) +{ + LiveAPISystem()->ToggleInitWebSocket(); +} +static void LiveAPI_ParamsChangedCallback(IConVar* var, const char* pOldValue) +{ + LiveAPISystem()->UpdateParams(); +} +static void LiveAPI_AddressChangedCallback(IConVar* var, const char* pOldValue) +{ + LiveAPISystem()->RebootWebSocket(); +} + +//----------------------------------------------------------------------------- +// console variables +//----------------------------------------------------------------------------- +ConVar liveapi_enabled("liveapi_enabled", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Enable LiveAPI functionality", &LiveAPI_EnabledChangedCallback); +ConVar liveapi_session_name("liveapi_session_name", "liveapi_session", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "LiveAPI session name to identify this connection"); +ConVar liveapi_truncate_hash_fields("liveapi_truncate_hash_fields", "1", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to truncate hash fields in LiveAPI events to save on I/O"); + +// WebSocket core +static ConVar liveapi_websocket_enabled("liveapi_websocket_enabled", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to use WebSocket to transmit LiveAPI events", &LiveAPI_WebSocketEnabledChangedCallback); +static ConVar liveapi_servers("liveapi_servers", "", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Comma separated list of addresses to connect to", &LiveAPI_AddressChangedCallback, "ws://domain.suffix:port"); + +// WebSocket connection base parameters +static ConVar liveapi_retry_count("liveapi_retry_count", "5", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Amount of times to retry connecting before marking the connection as unavailable", &LiveAPI_ParamsChangedCallback); +static ConVar liveapi_retry_time("liveapi_retry_time", "30", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Amount of time between each retry", &LiveAPI_ParamsChangedCallback); + +// WebSocket connection context parameters +static ConVar liveapi_timeout("liveapi_timeout", "300", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "WebSocket connection timeout in seconds", &LiveAPI_ParamsChangedCallback); +static ConVar liveapi_keepalive("liveapi_keepalive", "30", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Interval of time to send Pong to any connected server", &LiveAPI_ParamsChangedCallback); +static ConVar liveapi_lax_ssl("liveapi_lax_ssl", "1", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Skip SSL certificate validation for all WSS connections (allows the use of self-signed certificates)", &LiveAPI_ParamsChangedCallback); + +// Print core +static ConVar liveapi_print_enabled("liveapi_print_enabled", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to enable the printing of all events to a LiveAPI JSON file"); + +// Print parameters +static ConVar liveapi_print_pretty("liveapi_print_pretty", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to print events in a formatted manner to the LiveAPI JSON file"); +static ConVar liveapi_print_primitive("liveapi_print_primitive", "0", FCVAR_RELEASE | FCVAR_SERVER_FRAME_THREAD, "Whether to print primitive event fields to the LiveAPI JSON file"); + +//----------------------------------------------------------------------------- +// constructors/destructors +//----------------------------------------------------------------------------- +LiveAPI::LiveAPI() +{ + matchLogCount = 0; + initialLog = false; + initialized = false; +} +LiveAPI::~LiveAPI() +{ +} + +//----------------------------------------------------------------------------- +// Initialization of the LiveAPI system +//----------------------------------------------------------------------------- +void LiveAPI::Init() +{ + if (!liveapi_enabled.GetBool()) + return; + + InitWebSocket(); + initialized = true; +} + +//----------------------------------------------------------------------------- +// Shutdown of the LiveAPI system +//----------------------------------------------------------------------------- +void LiveAPI::Shutdown() +{ + webSocketSystem.Shutdown(); + DestroyLogger(); + initialized = false; +} + +//----------------------------------------------------------------------------- +// Toggle between init or deinit depending on current init state and the value +// of the cvar 'liveapi_enabled' +//----------------------------------------------------------------------------- +void LiveAPI::ToggleInit() +{ + const bool enabled = liveapi_enabled.GetBool(); + + if (enabled && !initialized) + Init(); + else if (!enabled && initialized) + Shutdown(); +} + +//----------------------------------------------------------------------------- +// Populate the connection params structure +//----------------------------------------------------------------------------- +void LiveAPI::CreateParams(CWebSocket::ConnParams_s& params) +{ + params.bufSize = LIVE_API_MAX_FRAME_BUFFER_SIZE; + + params.retryTime = liveapi_retry_time.GetFloat(); + params.maxRetries = liveapi_retry_count.GetInt(); + + params.timeOut = liveapi_timeout.GetInt(); + params.keepAlive = liveapi_keepalive.GetInt(); + params.laxSSL = liveapi_lax_ssl.GetInt(); +} + +//----------------------------------------------------------------------------- +// Update the websocket parameters and apply them on all connections +//----------------------------------------------------------------------------- +void LiveAPI::UpdateParams() +{ + CWebSocket::ConnParams_s connParams; + CreateParams(connParams); + + webSocketSystem.UpdateParams(connParams); +} + +//----------------------------------------------------------------------------- +// Initialize the websocket system +//----------------------------------------------------------------------------- +void LiveAPI::InitWebSocket() +{ + if (!liveapi_websocket_enabled.GetBool()) + return; + + CWebSocket::ConnParams_s connParams; + CreateParams(connParams); + + const char* initError = nullptr; + + if (!webSocketSystem.Init(liveapi_servers.GetString(), connParams, initError)) + { + Error(eDLL_T::RTECH, 0, "LiveAPI: WebSocket initialization failed! [%s]\n", initError); + return; + } +} + +//----------------------------------------------------------------------------- +// Shutdown the websocket system +//----------------------------------------------------------------------------- +void LiveAPI::ShutdownWebSocket() +{ + webSocketSystem.Shutdown(); +} + +//----------------------------------------------------------------------------- +// Toggle between init or deinit depending on current init state and the value +// of the cvar 'liveapi_websocket_enabled' +//----------------------------------------------------------------------------- +void LiveAPI::ToggleInitWebSocket() +{ + const bool enabled = liveapi_websocket_enabled.GetBool(); + + if (enabled && !WebSocketInitialized()) + InitWebSocket(); + else if (!enabled && WebSocketInitialized()) + ShutdownWebSocket(); +} + +//----------------------------------------------------------------------------- +// Reboot the websocket system and reconnect to addresses specified in cvar +// 'liveapi_servers' +//----------------------------------------------------------------------------- +void LiveAPI::RebootWebSocket() +{ + ShutdownWebSocket(); + InitWebSocket(); +} + +//----------------------------------------------------------------------------- +// Create the file logger +//----------------------------------------------------------------------------- +void LiveAPI::CreateLogger() +{ + // Its possible that one was already created but never closed, this is + // possible if the game scripts crashed or something along those lines. + DestroyLogger(); + + if (!liveapi_print_enabled.GetBool()) + return; // Logging is disabled + + matchLogger = spdlog::basic_logger_mt("match_logger", + Format("platform/liveapi/logs/%s/match_%d.json", g_LogSessionUUID.c_str(), matchLogCount++)); + + matchLogger.get()->set_pattern("%v"); + matchLogger.get()->info("[\n"); +} + +//----------------------------------------------------------------------------- +// Destroy the file logger +//----------------------------------------------------------------------------- +void LiveAPI::DestroyLogger() +{ + if (initialLog) + initialLog = false; + + if (!matchLogger) + return; // Nothing to drop + + matchLogger.get()->info("\n]\n"); + matchLogger.reset(); + + spdlog::drop("match_logger"); +} + +//----------------------------------------------------------------------------- +// LiveAPI state machine +//----------------------------------------------------------------------------- +void LiveAPI::RunFrame() +{ + if (!IsEnabled()) + return; + + if (WebSocketInitialized()) + webSocketSystem.Update(); +} + +//----------------------------------------------------------------------------- +// Send an event to all sockets +//----------------------------------------------------------------------------- +void LiveAPI::LogEvent(const google::protobuf::Message* const toTransmit, const google::protobuf::Message* toPrint) +{ + if (!IsEnabled()) + return; + + if (WebSocketInitialized()) + { + const string data = toTransmit->SerializeAsString(); + webSocketSystem.SendData(data.c_str(), (int)data.size()); + } + + // NOTE: we don't check on the cvar 'liveapi_print_enabled' here because if + // this cvar gets disabled on the fly and we check it here, the output will + // be truncated and thus invalid! Log for as long as the SpdLog instance is + // valid. + if (matchLogger) + { + std::string jsonStr(initialLog ? ",\n" : ""); + google::protobuf::util::JsonPrintOptions options; + + options.add_whitespace = liveapi_print_pretty.GetBool(); + options.always_print_primitive_fields = liveapi_print_primitive.GetBool(); + + google::protobuf::util::MessageToJsonString(*toPrint, &jsonStr, options); + + // Remove the trailing newline character + if (options.add_whitespace && !jsonStr.empty()) + jsonStr.pop_back(); + + matchLogger.get()->info(jsonStr); + + if (!initialLog) + initialLog = true; + } +} + +//----------------------------------------------------------------------------- +// Returns whether the system is enabled +//----------------------------------------------------------------------------- +bool LiveAPI::IsEnabled() const +{ + return liveapi_enabled.GetBool(); +} + +//----------------------------------------------------------------------------- +// Returns whether the system is able to run +//----------------------------------------------------------------------------- +bool LiveAPI::IsValidToRun() const +{ + return (IsEnabled() && (WebSocketInitialized() || FileLoggerInitialized())); +} + +static LiveAPI s_liveApi; + +//----------------------------------------------------------------------------- +// Singleton accessor +//----------------------------------------------------------------------------- +LiveAPI* LiveAPISystem() +{ + return &s_liveApi; +} diff --git a/r5dev/rtech/liveapi/liveapi.h b/r5dev/rtech/liveapi/liveapi.h new file mode 100644 index 00000000..94b0f21d --- /dev/null +++ b/r5dev/rtech/liveapi/liveapi.h @@ -0,0 +1,59 @@ +#ifndef RTECH_LIVEAPI_H +#define RTECH_LIVEAPI_H +#include "tier2/websocket.h" +#include "thirdparty/protobuf/message.h" + +#define LIVE_API_MAX_FRAME_BUFFER_SIZE 0x8000 + +extern ConVar liveapi_enabled; +extern ConVar liveapi_session_name; +extern ConVar liveapi_truncate_hash_fields; + +struct ProtoWebSocketRefT; +typedef void (*LiveAPISendCallback_t)(ProtoWebSocketRefT* webSocket); + +class LiveAPI +{ +public: + LiveAPI(); + ~LiveAPI(); + + void Init(); + void Shutdown(); + + void ToggleInit(); + + void CreateParams(CWebSocket::ConnParams_s& params); + void UpdateParams(); + + void InitWebSocket(); + void ShutdownWebSocket(); + + void ToggleInitWebSocket(); + + void RebootWebSocket(); + + void CreateLogger(); + void DestroyLogger(); + + void RunFrame(); + void LogEvent(const google::protobuf::Message* const toTransmit, const google::protobuf::Message* toPrint); + + bool IsEnabled() const; + bool IsValidToRun() const; + + inline bool WebSocketInitialized() const { return webSocketSystem.IsInitialized(); } + inline bool FileLoggerInitialized() const { return matchLogger != nullptr; } + +private: + CWebSocket webSocketSystem; + + std::shared_ptr matchLogger; + int matchLogCount; + bool initialLog; + bool initialized; +}; + +LiveAPI* LiveAPISystem(); + +#endif // RTECH_LIVEAPI_H diff --git a/r5dev/rtech/pak/pakalloc.cpp b/r5dev/rtech/pak/pakalloc.cpp new file mode 100644 index 00000000..b3fb82b1 --- /dev/null +++ b/r5dev/rtech/pak/pakalloc.cpp @@ -0,0 +1,109 @@ +//=============================================================================// +// +// Purpose: pak page allocation and alignment +// +//=============================================================================// +#include "rtech/ipakfile.h" +#include "pakstate.h" +#include "pakalloc.h" + +//----------------------------------------------------------------------------- +// aligns the segment headers for each asset type +//----------------------------------------------------------------------------- +void Pak_AlignSegmentHeaders(PakFile_t* const pak, PakSegmentDescriptor_t* const desc) +{ + uint64_t headersSize = 0; + uint8_t headerSegmentAlignment = static_cast(desc->segmentAlignmentForType[SF_HEAD]); + + for (uint8_t i = 0; i < PAK_MAX_TYPES; ++i) + { + const PakAssetBinding_t& binding = g_pakGlobals->assetBindings[i]; + + if (desc->assetTypeCount[i]) + { + // asset header alignment really shouldn't be above 255 + // if this needs raising, headerSegmentAlignment should be made wider + assert(binding.headerAlignment <= UINT8_MAX); + + const size_t alignedSize = ALIGN_VALUE(headersSize, static_cast(binding.headerAlignment)); + + pak->memoryData.unkAssetTypeBindingSizes[i] = alignedSize; + headersSize = alignedSize + (desc->assetTypeCount[i] * binding.nativeClassSize); + + desc->segmentSizeForType[SF_HEAD] = headersSize; + + headerSegmentAlignment = Max(headerSegmentAlignment, static_cast(binding.headerAlignment)); + desc->segmentAlignmentForType[SF_HEAD] = headerSegmentAlignment; + } + } +} + +//----------------------------------------------------------------------------- +// aligns each individual non-header segment +//----------------------------------------------------------------------------- +void Pak_AlignSegments(PakFile_t* const pak, PakSegmentDescriptor_t* const desc) +{ + for (uint16_t i = 0; i < pak->GetSegmentCount(); ++i) + { + const PakSegmentHeader_t* const segHeader = pak->GetSegmentHeader(i); + + const uint8_t segmentType = segHeader->typeFlags & (SF_TEMP | SF_CPU); + + if (segmentType != SF_HEAD) // if not a header segment + { + // should this be a hard error on release? + // segment alignment must not be 0 and must be a power of two + assert(segHeader->dataAlignment > 0 && IsPowerOfTwo(segHeader->dataAlignment)); + + const size_t alignedSegmentSize = ALIGN_VALUE(desc->segmentSizeForType[segmentType], static_cast(segHeader->dataAlignment)); + //const size_t sizeAligned = ~(m_align - 1) & (m_align - 1 + segmentSizeForType[segmentType]); + + desc->segmentSizes[i] = alignedSegmentSize; + desc->segmentSizeForType[segmentType] = alignedSegmentSize + segHeader->dataSize; + + // check if this segment's alignment is higher than the previous highest for this type + // if so, increase the alignment to accommodate this segment + desc->segmentAlignmentForType[segmentType] = Max(desc->segmentAlignmentForType[segmentType], segHeader->dataAlignment); + } + } +} + +//----------------------------------------------------------------------------- +// copy's pages into pre-allocated and aligned segments +//----------------------------------------------------------------------------- +void Pak_CopyPagesToSegments(PakFile_t* const pak, PakLoadedInfo_t* const loadedInfo, PakSegmentDescriptor_t* const desc) +{ + for (uint32_t i = 0; i < pak->GetPageCount(); ++i) + { + const PakPageHeader_t* const pageHeader = pak->GetPageHeader(i); + const uint32_t segmentIndex = pageHeader->segmentIdx; + + const PakSegmentHeader_t* const segHeader = pak->GetSegmentHeader(segmentIndex); + const int typeFlags = segHeader->typeFlags; + + // check if header page + if ((typeFlags & (SF_TEMP | SF_CPU)) != 0) + { + // align the segment's current size to the alignment of the new page to get copied in + // this ensures that the location holding the page is aligned as required + // + // since the segment will always have alignment equal to or greater than the page, and that alignment will always be a power of 2 + // the page does not have to be aligned to the same alignment as the segment, as aligning it to its own alignment is sufficient as long as + // every subsequent page does the same thing + const size_t alignedSegmentSize = ALIGN_VALUE(desc->segmentSizes[segmentIndex], static_cast(pageHeader->pageAlignment)); + + // get a pointer to the newly aligned location within the segment for this page + pak->memoryData.memPageBuffers[i] = reinterpret_cast(loadedInfo->segmentBuffers[typeFlags & (SF_TEMP | SF_CPU)]) + alignedSegmentSize; + + // update the segment size to reflect the new alignment and page size + desc->segmentSizes[segmentIndex] = alignedSegmentSize + pak->memoryData.pageHeaders[i].dataSize; + } + else + { + // all headers go into one segment and are dealt with separately in Pak_ProcessPakFile + // since headers must be copied individually into a buffer that is big enough for the "native class" version of the header + // instead of just the file version + pak->memoryData.memPageBuffers[i] = reinterpret_cast(loadedInfo->segmentBuffers[SF_HEAD]); + } + } +} diff --git a/r5dev/rtech/pak/pakalloc.h b/r5dev/rtech/pak/pakalloc.h new file mode 100644 index 00000000..9d2b2a81 --- /dev/null +++ b/r5dev/rtech/pak/pakalloc.h @@ -0,0 +1,28 @@ +#ifndef RTECH_PAKALLOC_H +#define RTECH_PAKALLOC_H +#include "rtech/ipakfile.h" + +extern void Pak_AlignSegmentHeaders(PakFile_t* const pak, PakSegmentDescriptor_t* const desc); +extern void Pak_AlignSegments(PakFile_t* const pak, PakSegmentDescriptor_t* const desc); +extern void Pak_CopyPagesToSegments(PakFile_t* const pak, PakLoadedInfo_t* const loadedInfo, PakSegmentDescriptor_t* const desc); + +// something with sorting pages? +inline void (*sub_140442740)(PakAsset_t** assetEntries, PakAsset_t** assetEntry, __int64 idx, PakFile_t* pak); + +/////////////////////////////////////////////////////////////////////////////// +class V_PakAlloc : public IDetour +{ + virtual void GetAdr(void) const + { + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 54 41 56 41 57 48 83 EC 40 48 8B C2 49 8B D9").GetPtr(sub_140442740); + } + virtual void GetVar(void) const { } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const { }; +}; +/////////////////////////////////////////////////////////////////////////////// + +#endif // RTECH_PAKALLOC_H diff --git a/r5dev/rtech/pak/pakdecode.cpp b/r5dev/rtech/pak/pakdecode.cpp new file mode 100644 index 00000000..cbbf229c --- /dev/null +++ b/r5dev/rtech/pak/pakdecode.cpp @@ -0,0 +1,898 @@ +//=============================================================================// +// +// Purpose: streamed & buffered pak decoder +// +//=============================================================================// +#include "tier0/binstream.h" +#include "tier1/fmtstr.h" + +#include "rtech/ipakfile.h" + +#include "paktools.h" +#include "pakdecode.h" + +//----------------------------------------------------------------------------- +// lookup table for default pak decoder +//----------------------------------------------------------------------------- +static const unsigned char /*141313180*/ s_defaultDecoderLUT[] = +{ + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0B, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF3, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0E, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x09, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF1, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0D, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF2, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0F, + 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x0A, + 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF0, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0E, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0B, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x10, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0F, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0D, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, + 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0xFF, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, + 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, + 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x0B, + 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x08, 0x00, 0x03, 0x00, 0x08, 0x00, 0x0E, + 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x0D, + 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0F, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, + 0x4A, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, + 0xCA, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, + 0x4A, 0x01, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, 0xAA, 0x01, 0x00, 0x00, + 0xAA, 0x03, 0x00, 0x00, 0xAA, 0x05, 0x00, 0x00, 0xAA, 0x25, 0x00, 0x00, 0xAA, 0x25, 0x02, 0x00, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x09, 0x09, 0x0D, 0x11, 0x15, + 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x2A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, + 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, + 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, + 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, + 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, + 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, + 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xF1, 0x1D, 0xC1, 0xF6, 0x7F, 0x00, 0x00, + 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, + 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, + 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, + 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, + 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, + 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, + 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, + 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, + 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, + 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, + 0x4C, 0x39, 0x56, 0x75, 0x42, 0x52, 0x65, 0x75, 0x70, 0x35, 0x31, 0x77, 0x4C, 0x51, 0x64, 0x61, +}; + +//----------------------------------------------------------------------------- +// checks if we have enough output buffer room to decode the data stream +//----------------------------------------------------------------------------- +bool Pak_HasEnoughDecodeBufferAvailable(PakDecoder_t* const decoder, const size_t outLen) +{ + // make sure caller has copied all data out the ring buffer first before + // overwriting it with new decoded data + const uint64_t bytesWritten = (decoder->outBufBytePos & ~decoder->outputInvMask); + return (outLen >= decoder->outputInvMask + (bytesWritten +1) || outLen >= decoder->decompSize); +} + +//----------------------------------------------------------------------------- +// checks if we have enough source data streamed to decode the next block +//----------------------------------------------------------------------------- +bool Pak_HasEnoughStreamedDataForDecode(PakDecoder_t* const decoder, const size_t inLen) +{ + // the decoder needs at least this amount of input data streamed in order + // to decode the rest of the pak file, as this is where reading has stopped + // this value may equal the currently streamed input size, as its possible + // this function is getting called to flush the remainder decoded data into + // the out buffer which got truncated off on the call prior due to wrapping + return (inLen >= decoder->bufferSizeNeeded); +} + +//----------------------------------------------------------------------------- +// gets the frame for the data in the ring buffer, the frame returned is always +// ending to the end of the ring buffer, or the end of the data itself +//----------------------------------------------------------------------------- +PakRingBufferFrame_t Pak_DetermineRingBufferFrame(const uint64_t bufMask, const size_t seekPos, const size_t dataLen) +{ + PakRingBufferFrame_t ring; + ring.bufIndex = seekPos & bufMask; + + // the total amount of bytes used and available in this frame + const size_t bytesUsed = ring.bufIndex & bufMask; + const size_t totalAvail = bufMask +1 - bytesUsed; + + // the last part of the data might be smaller than the remainder of the ring + // buffer; clamp it + ring.frameLen = Min(dataLen - seekPos, totalAvail); + return ring; +} + +//----------------------------------------------------------------------------- +// initializes the RTech decoder +//----------------------------------------------------------------------------- +size_t Pak_RTechDecoderInit(PakDecoder_t* const decoder, const uint8_t* const fileBuffer, + const uint64_t inputMask, const size_t dataSize, const size_t dataOffset, const size_t headerSize) +{ + uint64_t frameHeader = *(_QWORD*)((inputMask & (dataOffset + headerSize)) + fileBuffer); + const int decompressedSizeBits = frameHeader & 0x3F; + + frameHeader >>= 6; + decoder->decompSize = (1i64 << decompressedSizeBits) | frameHeader & ((1i64 << decompressedSizeBits) - 1); + + const uint64_t bytePos = dataOffset + headerSize + 8; + const int64_t currByteLow = *(_QWORD*)((inputMask & bytePos) + fileBuffer) << (64 - ((uint8_t)decompressedSizeBits + 6)); + + decoder->inBufBytePos = bytePos + ((uint64_t)(uint32_t)(decompressedSizeBits + 6) >> 3); + const uint32_t bitPosFinal = ((decompressedSizeBits + 6) & 7) + 13; + + const uint64_t currByte = (0xFFFFFFFFFFFFFFFFui64 >> ((decompressedSizeBits + 6) & 7)) & ((frameHeader >> decompressedSizeBits) | currByteLow); + const uint32_t currbits = (((_BYTE)currByte - 1) & 0x3F) + 1; + + const uint64_t invMaskIn = 0xFFFFFFFFFFFFFFFFui64 >> (64 - (uint8_t)currbits); + decoder->inputInvMask = invMaskIn; + + const uint64_t invMaskOut = 0xFFFFFFFFFFFFFFFFui64 >> (64 - ((((currByte >> 6) - 1) & 0x3F) + 1)); + decoder->outputInvMask = invMaskOut; + + const uint64_t finalByteFull = (currByte >> 13) | (*(_QWORD*)((inputMask & decoder->inBufBytePos) + fileBuffer) << (64 - (uint8_t)bitPosFinal)); + const uint32_t finalBitOffset = bitPosFinal & 7; + + decoder->inBufBytePos += bitPosFinal >> 3; + const uint64_t finalByte = (0xFFFFFFFFFFFFFFFFui64 >> finalBitOffset) & finalByteFull; + + if (decoder->inputInvMask == 0xFFFFFFFFFFFFFFFFui64) + { + decoder->headerOffset = 0; + decoder->bufferSizeNeeded = dataSize; + } + else + { + const uint64_t finalPos = inputMask & decoder->inBufBytePos; + decoder->headerOffset = (currbits >> 3) + 1; + decoder->inBufBytePos += (currbits >> 3) + 1; + decoder->bufferSizeNeeded = *(_QWORD*)(finalPos + fileBuffer) & ((1i64 << (8 * ((uint8_t)(currbits >> 3) + 1))) - 1);; + } + + decoder->bufferSizeNeeded += dataOffset; + decoder->currentByte = finalByte; + decoder->currentBit = finalBitOffset; + decoder->qword70 = decoder->inputInvMask + dataOffset - 6; + decoder->dword6C = 0; + decoder->compressedStreamSize = decoder->bufferSizeNeeded; + decoder->decompressedStreamSize = decoder->decompSize; + + if ((((uint8_t)(currByte >> 6) - 1) & 0x3F) != -1i64 && decoder->decompSize - 1 > decoder->outputInvMask) + { + const uint64_t streamCompressedSize = decoder->bufferSizeNeeded - decoder->headerOffset; + decoder->compressedStreamSize = streamCompressedSize; + + decoder->decompressedStreamSize = decoder->outputInvMask + 1; + } + + return decoder->decompSize; +} + +//----------------------------------------------------------------------------- +// decodes the RTech data stream up to available buffer or data +//----------------------------------------------------------------------------- +bool Pak_RTechStreamDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen) +{ + bool result; // al + uint64_t outBufBytePos; // r15 + uint8_t* outputBuf; // r11 + uint32_t currentBit; // ebp + uint64_t currentByte; // rsi + uint64_t inBufBytePos; // rdi + size_t qword70; // r12 + const uint8_t* inputBuf; // r13 + uint32_t dword6C; // ecx + uint64_t v13; // rsi + unsigned __int64 i; // rax + unsigned __int64 v15; // r8 + __int64 v16; // r9 + int v17; // ecx + unsigned __int64 v18; // rax + uint64_t v19; // rsi + __int64 v20; // r14 + int v21; // ecx + unsigned __int64 v22; // r11 + int v23; // edx + uint64_t outputMask; // rax + int v25; // r8d + unsigned int v26; // r13d + uint64_t v27; // r10 + uint8_t* v28; // rax + uint8_t* v29; // r10 + size_t decompSize; // r9 + uint64_t inputInvMask; // r10 + uint64_t headerOffset; // r8 + uint64_t v33; // rax + uint64_t v34; // rax + uint64_t v35; // rax + size_t v36; // rcx + __int64 v37; // rdx + size_t v38; // r14 + size_t v39; // r11 + uint64_t v40; // cl + uint64_t v41; // rsi + __int64 v42; // rcx + uint64_t v43; // r8 + int v44; // r11d + unsigned __int8 v45; // r9 + uint64_t v46; // rcx + uint64_t v47; // rcx + __int64 v48; // r9 + __int64 m; // r8 + __int64 v50; // r9d + __int64 v51; // r8 + __int64 v52; // rdx + __int64 k; // r8 + signed __int64 v54; // r10 + __int64 v55; // rdx + unsigned int v56; // r14d + const uint8_t* v57; // rdx + uint8_t* v58; // r8 + uint64_t v59; // al + uint64_t v60; // rsi + __int64 v61; // rax + uint64_t v62; // r9 + int v63; // r10d + unsigned __int8 v64; // cl + uint64_t v65; // rax + unsigned int v66; // r14d + unsigned int j; // ecx + __int64 v68; // rax + uint64_t v69; // rcx + uint8_t* v70; // [rsp+0h] [rbp-58h] + uint32_t v71; // [rsp+60h] [rbp+8h] + const uint8_t* v74; // [rsp+78h] [rbp+20h] + + outBufBytePos = decoder->outBufBytePos; + + outputBuf = decoder->outputBuf; + currentBit = decoder->currentBit; + currentByte = decoder->currentByte; + inBufBytePos = decoder->inBufBytePos; + qword70 = decoder->qword70; + inputBuf = decoder->inputBuf; + + if (decoder->compressedStreamSize < qword70) + qword70 = decoder->compressedStreamSize; + + dword6C = decoder->dword6C; + v74 = inputBuf; + v70 = outputBuf; + v71 = dword6C; + if (!currentBit) + goto LABEL_11; + + v13 = (*(_QWORD*)&inputBuf[inBufBytePos & decoder->inputMask] << (64 - (unsigned __int8)currentBit)) | currentByte; + for (i = currentBit; ; i = currentBit) + { + currentBit &= 7u; + inBufBytePos += i >> 3; + dword6C = v71; + currentByte = (0xFFFFFFFFFFFFFFFFui64 >> currentBit) & v13; + LABEL_11: + v15 = (unsigned __int64)dword6C << 8; + v16 = dword6C; + v17 = s_defaultDecoderLUT[(unsigned __int8)currentByte + 512 + v15]; + v18 = (unsigned __int8)currentByte + v15; + currentBit += v17; + v19 = currentByte >> v17; + v20 = (unsigned int)(char)s_defaultDecoderLUT[v18]; + if ((s_defaultDecoderLUT[v18] & 0x80u) != 0) + { + v56 = -(int)v20; + v57 = &inputBuf[inBufBytePos & decoder->inputMask]; + v71 = 1; + v58 = &outputBuf[outBufBytePos & decoder->outputMask]; + if (v56 == s_defaultDecoderLUT[v16 + 1248]) + { + if ((~inBufBytePos & decoder->inputInvMask) < 0xF || (decoder->outputInvMask & ~outBufBytePos) < 0xF || decoder->decompSize - outBufBytePos < 0x10) + v56 = 1; + v59 = v19; + v60 = v19 >> 3; + v61 = v59 & 7; + v62 = v60; + if (v61) + { + v63 = s_defaultDecoderLUT[v61 + 1232]; + v64 = s_defaultDecoderLUT[v61 + 1240]; + } + else + { + v62 = v60 >> 4; + v65 = v60 & 0xF; + currentBit += 4; + v63 = *(_DWORD*)&s_defaultDecoderLUT[4 * v65 + 1152]; + v64 = s_defaultDecoderLUT[v65 + 1216]; + } + currentBit += v64 + 3; + v19 = v62 >> v64; + v66 = v63 + (v62 & ((1 << v64) - 1)) + v56; + for (j = v66 >> 3; j; --j) + { + v68 = *(_QWORD*)v57; + v57 += 8; + *(_QWORD*)v58 = v68; + v58 += 8; + } + if ((v66 & 4) != 0) + { + *(_DWORD*)v58 = *(_DWORD*)v57; + v58 += 4; + v57 += 4; + } + if ((v66 & 2) != 0) + { + *(_WORD*)v58 = *(_WORD*)v57; + v58 += 2; + v57 += 2; + } + if ((v66 & 1) != 0) + *v58 = *v57; + inBufBytePos += v66; + outBufBytePos += v66; + } + else + { + *(_QWORD*)v58 = *(_QWORD*)v57; + *((_QWORD*)v58 + 1) = *((_QWORD*)v57 + 1); + inBufBytePos += v56; + outBufBytePos += v56; + } + } + else + { + v21 = v19 & 0xF; + v71 = 0; + v22 = ((unsigned __int64)(unsigned int)v19 >> (((unsigned int)(v21 - 31) >> 3) & 6)) & 0x3F; + v23 = 1 << (v21 + ((v19 >> 4) & ((24 * (((unsigned int)(v21 - 31) >> 3) & 2)) >> 4))); + currentBit += (((unsigned int)(v21 - 31) >> 3) & 6) + s_defaultDecoderLUT[v22 + 1088] + v21 + ((v19 >> 4) & ((24 * (((unsigned int)(v21 - 31) >> 3) & 2)) >> 4)); + outputMask = decoder->outputMask; + v25 = 16 * (v23 + ((v23 - 1) & (v19 >> ((((unsigned int)(v21 - 31) >> 3) & 6) + s_defaultDecoderLUT[v22 + 1088])))); + v19 >>= (((unsigned int)(v21 - 31) >> 3) & 6) + s_defaultDecoderLUT[v22 + 1088] + v21 + ((v19 >> 4) & ((24 * (((unsigned int)(v21 - 31) >> 3) & 2)) >> 4)); + v26 = v25 + s_defaultDecoderLUT[v22 + 1024] - 16; + v27 = outputMask & (outBufBytePos - v26); + v28 = &v70[outBufBytePos & outputMask]; + v29 = &v70[v27]; + if ((_DWORD)v20 == 17) + { + v40 = v19; + v41 = v19 >> 3; + v42 = v40 & 7; + v43 = v41; + if (v42) + { + v44 = s_defaultDecoderLUT[v42 + 1232]; + v45 = s_defaultDecoderLUT[v42 + 1240]; + } + else + { + currentBit += 4; + v46 = v41 & 0xF; + v43 = v41 >> 4; + v44 = *(_DWORD*)&s_defaultDecoderLUT[4 * v46 + 1152]; + v45 = s_defaultDecoderLUT[v46 + 1216]; + if (v74 && currentBit + v45 >= 61) + { + v47 = inBufBytePos++ & decoder->inputMask; + v43 |= (unsigned __int64)v74[v47] << (61 - (unsigned __int8)currentBit); + currentBit -= 8; + } + } + currentBit += v45 + 3; + v19 = v43 >> v45; + v48 = ((unsigned int)v43 & ((1 << v45) - 1)) + v44 + 17; + outBufBytePos += v48; + if (v26 < 8) + { + v50 = v48 - 13; + outBufBytePos -= 13i64; + if (v26 == 1) + { + v51 = *v29; + //++dword_14D40B2BC; + v52 = 0i64; + for (k = 0x101010101010101i64 * v51; (unsigned int)v52 < v50; v52 = (unsigned int)(v52 + 8)) + *(_QWORD*)&v28[v52] = k; + } + else + { + //++dword_14D40B2B8; + if (v50) + { + v54 = v29 - v28; + v55 = v50; + do + { + *v28 = v28[v54]; + ++v28; + --v55; + } while (v55); + } + } + } + else + { + //++dword_14D40B2AC; + for (m = 0i64; (unsigned int)m < (unsigned int)v48; m = (unsigned int)(m + 8)) + *(_QWORD*)&v28[m] = *(_QWORD*)&v29[m]; + } + } + else + { + outBufBytePos += v20; + *(_QWORD*)v28 = *(_QWORD*)v29; + *((_QWORD*)v28 + 1) = *((_QWORD*)v29 + 1); + } + inputBuf = v74; + } + if (inBufBytePos >= qword70) + break; + LABEL_29: + outputBuf = v70; + v13 = (*(_QWORD*)&inputBuf[inBufBytePos & decoder->inputMask] << (64 - (unsigned __int8)currentBit)) | v19; + } + if (outBufBytePos != decoder->decompressedStreamSize) + goto LABEL_25; + decompSize = decoder->decompSize; + if (outBufBytePos == decompSize) + { + result = true; + goto LABEL_69; + } + inputInvMask = decoder->inputInvMask; + headerOffset = decoder->headerOffset; + v33 = inputInvMask & -(__int64)inBufBytePos; + v19 >>= 1; + ++currentBit; + if (headerOffset > v33) + { + inBufBytePos += v33; + v34 = decoder->qword70; + if (inBufBytePos > v34) + decoder->qword70 = inputInvMask + v34 + 1; + } + v35 = inBufBytePos & decoder->inputMask; + inBufBytePos += headerOffset; + v36 = outBufBytePos + decoder->outputInvMask + 1; + v37 = *(_QWORD*)&inputBuf[v35] & ((1i64 << (8 * (unsigned __int8)headerOffset)) - 1); + v38 = v37 + decoder->bufferSizeNeeded; + v39 = v37 + decoder->compressedStreamSize; + decoder->bufferSizeNeeded = v38; + decoder->compressedStreamSize = v39; + if (v36 >= decompSize) + { + v36 = decompSize; + decoder->compressedStreamSize = headerOffset + v39; + } + decoder->decompressedStreamSize = v36; + if (inLen >= v38 && outLen >= v36) + { + LABEL_25: + qword70 = decoder->qword70; + if (inBufBytePos >= qword70) + { + inBufBytePos = ~decoder->inputInvMask & (inBufBytePos + 7); + qword70 += decoder->inputInvMask + 1; + decoder->qword70 = qword70; + } + if (decoder->compressedStreamSize < qword70) + qword70 = decoder->compressedStreamSize; + goto LABEL_29; + } + v69 = decoder->qword70; + if (inBufBytePos >= v69) + { + inBufBytePos = ~inputInvMask & (inBufBytePos + 7); + decoder->qword70 = v69 + inputInvMask + 1; + } + decoder->dword6C = v71; + result = false; + decoder->currentByte = v19; + decoder->currentBit = currentBit; +LABEL_69: + decoder->outBufBytePos = outBufBytePos; + decoder->inBufBytePos = inBufBytePos; + return result; +} + +//----------------------------------------------------------------------------- +// initializes the ZStd decoder +//----------------------------------------------------------------------------- +size_t Pak_ZStdDecoderInit(PakDecoder_t* const decoder, const uint8_t* frameHeader, + const size_t dataSize, const size_t headerSize) +{ + ZSTD_DStream* const dctx = ZSTD_createDStream(); + assert(dctx); + + // failure + if (!dctx) + return NULL; + + decoder->zstreamContext = dctx; + + if (ZSTD_getFrameHeader(&dctx->fParams, frameHeader, dataSize) != 0) + { + ZSTD_freeDStream(decoder->zstreamContext); + decoder->zstreamContext = nullptr; + + return NULL; // content size error + } + + // ideally the frame header of the block gets parsed first, the length + // thereof is returned by initDStream and thus being processed first + // before moving on to actual data + decoder->frameHeaderSize = ZSTD_initDStream(dctx); + + // we need at least this many bytes of streamed data to process the frame + // header of the compressed block + decoder->bufferSizeNeeded = decoder->inBufBytePos + decoder->frameHeaderSize; + + // must include header size + decoder->decompSize = dctx->fParams.frameContentSize + headerSize; + return decoder->decompSize; +} + +//----------------------------------------------------------------------------- +// decodes the ZStd data stream up to available buffer or data, whichever ends +// first +//----------------------------------------------------------------------------- +bool Pak_ZStdStreamDecode(PakDecoder_t* const decoder, const PakRingBufferFrame_t& outFrame, const PakRingBufferFrame_t& inFrame) +{ + ZSTD_outBuffer outBuffer = { + &decoder->outputBuf[outFrame.bufIndex], + outFrame.frameLen, NULL + }; + + ZSTD_inBuffer inBuffer = { + &decoder->inputBuf[inFrame.bufIndex], + inFrame.frameLen, NULL + }; + + const size_t ret = ZSTD_decompressStream(decoder->zstreamContext, &outBuffer, &inBuffer); + + if (ZSTD_isError(ret)) + { + // NOTE: obtained here and not in the error formatter as we could check + // the error string during the assertion + const char* const decodeError = ZSTD_getErrorName(ret); + assert(0); + + Error(eDLL_T::RTECH, EXIT_FAILURE, "%s: decode error: %s\n", __FUNCTION__, decodeError); + return false; + } + + // advance buffer io positions, required so the main parser could already + // start parsing the headers while the rest is getting decoded still + decoder->outBufBytePos += outBuffer.pos; + decoder->inBufBytePos += inBuffer.pos; + + // on the next call, we need at least this amount of data streamed in order + // to decode the rest of the pak file, as this is where reading has stopped + // this value may equal the currently streamed input size, as its possible + // this function is getting called to flush the remainder decoded data into + // the out buffer which got truncated off on the call prior due to wrapping + // + // if the input stream has fully decoded, this should equal the size of the + // encoded pak file + decoder->bufferSizeNeeded = decoder->inBufBytePos; + + const bool decoded = ret == NULL; + + // zstd decoder no longer necessary at this point, deallocate + if (decoded) + { + ZSTD_freeDStream(decoder->zstreamContext); + decoder->zstreamContext = nullptr; + } + + return decoded; +} + +//----------------------------------------------------------------------------- +// initializes the decoder +//----------------------------------------------------------------------------- +size_t Pak_InitDecoder(PakDecoder_t* const decoder, const uint8_t* const inputBuf, uint8_t* const outputBuf, + const uint64_t inputMask, const uint64_t outputMask, const size_t dataSize, const size_t dataOffset, + const size_t headerSize, const EPakDecodeMode decodeMode) +{ + // buffer size must be power of two as we index into buffers using a bit + // mask rather than a modulo, the mask provided must be bufferSize-1 + assert(IsPowerOfTwo(inputMask + 1)); + assert(IsPowerOfTwo(outputMask + 1)); + + // the absolute start address of the input and output buffers + decoder->inputBuf = inputBuf; + decoder->outputBuf = outputBuf; + + // the actual file size, which consists of dataOffset (anything up to the + // frame header, like the file header) and the actual encoded data itself + decoder->fileSize = dataOffset + dataSize; + decoder->decodeMode = decodeMode; + + // buffer masks, which essentially gets used to index into the input and + // output buffers, similar to 'idx % bufSize', where bufSize = bufMask+1 + decoder->inputMask = inputMask; + decoder->outputMask = outputMask; + + // the current positions in the input and output buffers; if we deal with + // paks that are patched, the input buffer position during the init and + // decode call on subsequent patches may not be at the start of the buffer, + // they will end where the previous 'to patch' pak had finished streaming + // and decoding + decoder->inBufBytePos = dataOffset + headerSize; + decoder->outBufBytePos = headerSize; + + // if we use the default RTech decoder, return from here as the stuff below + // is handled by the RTech decoder internally + if (decodeMode == EPakDecodeMode::MODE_RTECH) + return Pak_RTechDecoderInit(decoder, inputBuf, inputMask, dataSize, dataOffset, headerSize); + + // NOTE: on RTech encoded paks this data is parsed out of the frame header, + // but for ZStd encoded paks we are always limiting this to the ring buffer + // size + decoder->outputInvMask = PAK_DECODE_OUT_RING_BUFFER_MASK; + + // this points to the first byte of the frame header, takes dataOffset + // into account which is the offset in the ring buffer to the patched + // data as we parse it contiguously after the base pak data, which + // might have ended somewhere in the middle of the ring buffer + const uint8_t* const frameHeaderData = &inputBuf[inputMask & (dataOffset + headerSize)]; + + const size_t decodeSize = Pak_ZStdDecoderInit(decoder, frameHeaderData, dataSize, headerSize); + assert(decodeSize); + + return decodeSize; +} + +//----------------------------------------------------------------------------- +// decodes streamed input pak data; on patched pak files, inLen will continue +// from where the base pak had ended as patch pak files are considered part of +// the pak file that's currently getting loaded +//----------------------------------------------------------------------------- +bool Pak_StreamToBufferDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen, const EPakDecodeMode decodeMode) +{ + if (!Pak_HasEnoughStreamedDataForDecode(decoder, inLen)) + return false; + + if (!Pak_HasEnoughDecodeBufferAvailable(decoder, outLen)) + return false; + + if (decodeMode == EPakDecodeMode::MODE_RTECH) + return Pak_RTechStreamDecode(decoder, inLen, outLen); + + // must have a decoder at this point + // + // also, input seek pos may not exceed inLen as we can't read past + // currently streamed data; this should've been checked before reaching + // this position in code + assert(decoder->zstreamContext && decoder->inBufBytePos <= inLen); + + const PakRingBufferFrame_t outFrame = Pak_DetermineRingBufferFrame(decoder->outputMask, decoder->outBufBytePos , outLen); + const PakRingBufferFrame_t inFrame = Pak_DetermineRingBufferFrame(decoder->inputMask, decoder->inBufBytePos, inLen); + + return Pak_ZStdStreamDecode(decoder, outFrame, inFrame); +} + +//----------------------------------------------------------------------------- +// decodes buffered input pak data +//----------------------------------------------------------------------------- +bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const size_t pakSize, const EPakDecodeMode decodeMode) +{ + assert(decodeMode != EPakDecodeMode::MODE_DISABLED); + + PakDecoder_t decoder{}; + const size_t decompressedSize = Pak_InitDecoder(&decoder, inBuf, outBuf, UINT64_MAX, UINT64_MAX, pakSize, NULL, sizeof(PakFileHeader_t), decodeMode); + + PakFileHeader_t* const inHeader = reinterpret_cast(inBuf); + + if (decompressedSize != inHeader->decompressedSize) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: decompressed size: '%zu' expected: '%zu'!\n", + __FUNCTION__, decompressedSize, inHeader->decompressedSize); + + return false; + } + + // we should always have enough buffer room at this point + if (!Pak_StreamToBufferDecode(&decoder, inHeader->compressedSize, inHeader->decompressedSize, decodeMode)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: decompression failed!\n", + __FUNCTION__); + + return false; + } + + PakFileHeader_t* const outHeader = reinterpret_cast(outBuf); + + // copy the header over to the decoded buffer + *outHeader = *inHeader; + + // remove compress flags + outHeader->flags &= ~PAK_HEADER_FLAGS_COMPRESSED; + outHeader->flags &= ~PAK_HEADER_FLAGS_ZSTREAM_ENCODED; + + // equal compressed size with decompressed + outHeader->compressedSize = outHeader->decompressedSize; + + return true; +} + +//----------------------------------------------------------------------------- +// decodes the pak file from file name +//----------------------------------------------------------------------------- +bool Pak_DecodePakFile(const char* const inPakFile, const char* const outPakFile) +{ + // if this path doesn't exist, we must create it first before trying to + // open the out file + if (!Pak_CreateOverridePath()) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to create output path for pak file '%s'!\n", + __FUNCTION__, outPakFile); + + return false; + } + + CIOStream inPakStream; + + if (!inPakStream.Open(inPakFile, CIOStream::READ | CIOStream::BINARY)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for read!\n", + __FUNCTION__, inPakFile); + + return false; + } + + CIOStream outPakStream; + + if (!outPakStream.Open(outPakFile, CIOStream::WRITE | CIOStream::BINARY)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for write!\n", + __FUNCTION__, outPakFile); + + return false; + } + + const size_t fileSize = inPakStream.GetSize(); + + if (fileSize <= sizeof(PakFileHeader_t)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' appears truncated!\n", + __FUNCTION__, inPakFile); + + return false; + } + + std::unique_ptr inPakBufContainer(new uint8_t[fileSize]); + uint8_t* const inPakBuf = inPakBufContainer.get(); + + inPakStream.Read(inPakBuf, fileSize); + inPakStream.Close(); + + PakFileHeader_t* const inHeader = reinterpret_cast(inPakBuf); + + if (inHeader->magic != PAK_HEADER_MAGIC || inHeader->version != PAK_HEADER_VERSION) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' has incompatible or invalid header!\n", + __FUNCTION__, inPakFile); + + return false; + } + + const EPakDecodeMode decodeMode = inHeader->GetCompressionMode(); + + if (decodeMode == EPakDecodeMode::MODE_DISABLED) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' is already decompressed!\n", + __FUNCTION__, inPakFile); + + return false; + } + + if (inHeader->compressedSize != fileSize) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' appears truncated or corrupt; compressed size: '%zu' expected: '%zu'!\n", + __FUNCTION__, inPakFile, fileSize, inHeader->compressedSize); + + return false; + } + + Pak_ShowHeaderDetails(inHeader); + + std::unique_ptr outPakBufContainer(new uint8_t[inHeader->decompressedSize]); + uint8_t* const outPakBuf = outPakBufContainer.get(); + + if (!Pak_BufferToBufferDecode(inPakBuf, outPakBuf, fileSize, decodeMode)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to decompress pak file '%s'!\n", + __FUNCTION__, inPakFile); + + return false; + } + + const PakFileHeader_t* const outHeader = reinterpret_cast(outPakBuf); + + // NOTE: if the paks this particular pak patches have different sizes than + // current sizes in the patch header, the runtime will crash! + if (outHeader->patchIndex && !Pak_UpdatePatchHeaders(outPakBuf, outPakFile)) + { + Warning(eDLL_T::RTECH, "%s: pak '%s' is a patch pak, but the pak(s) it patches weren't found; patch headers not updated!\n", + __FUNCTION__, inPakFile); + } + + outPakStream.Write(outPakBuf, outHeader->decompressedSize); + + Msg(eDLL_T::RTECH, "Decompressed pak file to: '%s'\n", outPakFile); + return true; +} diff --git a/r5dev/rtech/pak/pakdecode.h b/r5dev/rtech/pak/pakdecode.h new file mode 100644 index 00000000..2008b012 --- /dev/null +++ b/r5dev/rtech/pak/pakdecode.h @@ -0,0 +1,14 @@ +#ifndef RTECH_PAKDECODE_H +#define RTECH_PAKDECODE_H +#include "rtech/ipakfile.h" + +extern size_t Pak_InitDecoder(PakDecoder_t* const decoder, const uint8_t* const inputBuf, uint8_t* const outputBuf, + const uint64_t inputMask, const uint64_t outputMask, const size_t dataSize, const size_t dataOffset, + const size_t headerSize, const EPakDecodeMode decodeMode); + +extern bool Pak_StreamToBufferDecode(PakDecoder_t* const decoder, const size_t inLen, const size_t outLen, const EPakDecodeMode decodeMode); +extern bool Pak_BufferToBufferDecode(uint8_t* const inBuf, uint8_t* const outBuf, const size_t pakSize, const EPakDecodeMode decodeMode); + +extern bool Pak_DecodePakFile(const char* const inPakFile, const char* const outPakFile); + +#endif // RTECH_PAKDECODE_H diff --git a/r5dev/rtech/pak/pakencode.cpp b/r5dev/rtech/pak/pakencode.cpp new file mode 100644 index 00000000..99df0017 --- /dev/null +++ b/r5dev/rtech/pak/pakencode.cpp @@ -0,0 +1,184 @@ +//=============================================================================// +// +// Purpose: buffered pak encoder +// +//=============================================================================// +#include "tier0/binstream.h" +#include "rtech/ipakfile.h" +#include "paktools.h" +#include "pakencode.h" + +//----------------------------------------------------------------------------- +// determines whether encoding had failed +//----------------------------------------------------------------------------- +static bool Pak_HasEncodeFailed(const size_t result) +{ + return static_cast(ZSTD_isError(result)); +} + +//----------------------------------------------------------------------------- +// returns the encode error as string +//----------------------------------------------------------------------------- +static const char* Pak_GetEncodeError(const size_t result) +{ + return ZSTD_getErrorName(result); +} + +//----------------------------------------------------------------------------- +// encodes the pak file from buffer, we can't do streamed compression as we +// need to know the actual decompress size ahead of time, else the runtime will +// fail as we wouldn't be able to parse the decompressed size from the frame +// header +//----------------------------------------------------------------------------- +bool Pak_BufferToBufferEncode(const uint8_t* const inBuf, const uint64_t inLen, + uint8_t* const outBuf, const uint64_t outLen, const int level) +{ + // offset to the actual pak data, the main file header shouldn't be + // compressed + const size_t dataOffset = sizeof(PakFileHeader_t); + + uint8_t* const dstBuf = outBuf + dataOffset; + const size_t dstLen = outLen - dataOffset; + + const uint8_t* const srcBuf = inBuf + dataOffset; + const size_t srcLen = inLen - dataOffset; + + size_t compressSize = NULL; + + compressSize = ZSTD_compress(dstBuf, dstLen, srcBuf, srcLen, level); + + if (Pak_HasEncodeFailed(compressSize)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: compression failed! [%s]\n", + __FUNCTION__, Pak_GetEncodeError(compressSize)); + + return false; + } + + PakFileHeader_t* const outHeader = reinterpret_cast(outBuf); + + // the compressed size includes the entire buffer, even the data we didn't + // compress like the file header + outHeader->compressedSize = compressSize + dataOffset; + + // these flags are required for the game's runtime to decide whether or not to + // decompress the pak, and how; see Pak_ProcessPakFile() for more details + outHeader->flags |= PAK_HEADER_FLAGS_COMPRESSED; + outHeader->flags |= PAK_HEADER_FLAGS_ZSTREAM_ENCODED; + + return true; +} + +//----------------------------------------------------------------------------- +// encodes the pak file from file name +//----------------------------------------------------------------------------- +bool Pak_EncodePakFile(const char* const inPakFile, const char* const outPakFile, const int level) +{ + if (!Pak_CreateBasePath()) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to create output path for pak file '%s'!\n", + __FUNCTION__, outPakFile); + + return false; + } + + CIOStream inPakStream; + + if (!inPakStream.Open(inPakFile, CIOStream::READ | CIOStream::BINARY)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for read!\n", + __FUNCTION__, inPakFile); + + return false; + } + + CIOStream outPakStream; + + if (!outPakStream.Open(outPakFile, CIOStream::WRITE | CIOStream::BINARY)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to open pak file '%s' for write!\n", + __FUNCTION__, outPakFile); + + return false; + } + + const size_t fileSize = inPakStream.GetSize(); + + // file appears truncated + if (fileSize <= sizeof(PakFileHeader_t)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' appears truncated!\n", + __FUNCTION__, inPakFile); + + return false; + } + + std::unique_ptr inPakBufContainer(new uint8_t[fileSize]); + uint8_t* const inPakBuf = inPakBufContainer.get(); + + inPakStream.Read(inPakBuf, fileSize); + inPakStream.Close(); + + const PakFileHeader_t* const inHeader = reinterpret_cast(inPakBuf); + + if (inHeader->magic != PAK_HEADER_MAGIC || inHeader->version != PAK_HEADER_VERSION) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' has incompatible or invalid header!\n", + __FUNCTION__, inPakFile); + + return false; + } + + if (inHeader->GetCompressionMode() != EPakDecodeMode::MODE_DISABLED) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' is already compressed!\n", + __FUNCTION__, inPakFile); + + return false; + } + + if (inHeader->compressedSize != fileSize) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: pak '%s' appears truncated or corrupt; compressed size: '%zu' expected: '%zu'!\n", + __FUNCTION__, inPakFile, fileSize, inHeader->compressedSize); + + return false; + } + + // NOTE: if the paks this particular pak patches have different sizes than + // current sizes in the patch header, the runtime will crash! + if (inHeader->patchIndex && !Pak_UpdatePatchHeaders(inPakBuf, outPakFile)) + { + Warning(eDLL_T::RTECH, "%s: pak '%s' is a patch pak, but the pak(s) it patches weren't found; patch headers not updated!\n", + __FUNCTION__, inPakFile); + } + + const size_t outBufSize = inHeader->decompressedSize; + + std::unique_ptr outPakBufContainer(new uint8_t[outBufSize]); + uint8_t* const outPakBuf = outPakBufContainer.get(); + + PakFileHeader_t* const outHeader = reinterpret_cast(outPakBuf); + + // copy the header over + *outHeader = *inHeader; + + // encoding failed + if (!Pak_BufferToBufferEncode(inPakBuf, fileSize, outPakBuf, outBufSize, level)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: failed to compress pak file '%s'!\n", + __FUNCTION__, inPakFile); + + return false; + } + + const PakFileHeader_t* const outPakHeader = reinterpret_cast(outPakBuf); + + Pak_ShowHeaderDetails(outPakHeader); + + // this will be true if the entire buffer has been written + outPakStream.Write(outPakBuf, outPakHeader->compressedSize); + + Msg(eDLL_T::RTECH, "Compressed pak file to: '%s'\n", outPakFile); + return true; +} diff --git a/r5dev/rtech/pak/pakencode.h b/r5dev/rtech/pak/pakencode.h new file mode 100644 index 00000000..afe54992 --- /dev/null +++ b/r5dev/rtech/pak/pakencode.h @@ -0,0 +1,10 @@ +#ifndef RTECH_PAKENCODE_H +#define RTECH_PAKENCODE_H +#include "rtech/ipakfile.h" + +bool Pak_BufferToBufferEncode(const uint8_t* const inBuf, const uint64_t inLen, + uint8_t* const outBuf, const uint64_t outLen, const int level); + +bool Pak_EncodePakFile(const char* const inPakFile, const char* const outPakFile, const int level); + +#endif // RTECH_PAKENCODE_H diff --git a/r5dev/rtech/pak/pakparse.cpp b/r5dev/rtech/pak/pakparse.cpp new file mode 100644 index 00000000..cfad8e3d --- /dev/null +++ b/r5dev/rtech/pak/pakparse.cpp @@ -0,0 +1,1309 @@ +//=============================================================================// +// +// Purpose: pak file loading and unloading +// +//=============================================================================// +#include "rtech/ipakfile.h" +#include "rtech/async/asyncio.h" + +#include "paktools.h" +#include "pakstate.h" +#include "pakpatch.h" +#include "pakalloc.h" +#include "pakparse.h" +#include "pakdecode.h" +#include "pakstream.h" + +static ConVar pak_debugrelations("pak_debugrelations", "0", FCVAR_DEVELOPMENTONLY | FCVAR_ACCESSIBLE_FROM_THREADS, "Debug RPAK asset dependency resolving"); + +//----------------------------------------------------------------------------- +// resolve the target guid from lookuo table +//----------------------------------------------------------------------------- +static bool Pak_ResolveAssetDependency(const PakFile_t* const pak, PakGuid_t currentGuid, + const PakGuid_t targetGuid, int& currentIndex, const bool shouldCheckTwo) +{ + while (true) + { + if (shouldCheckTwo && currentGuid == 2) + { + if (pak->memoryData.pakHeader.assetCount) + return false; + } + + currentIndex++; + + if (currentIndex >= PAK_MAX_ASSETS) + return false; + + currentIndex &= PAK_MAX_ASSETS_MASK; + currentGuid = g_pakGlobals->loadedAssets[currentIndex].guid; + + if (currentGuid == targetGuid) + return true; + } + + UNREACHABLE(); +} + +//----------------------------------------------------------------------------- +// resolve guid relations for asset +//----------------------------------------------------------------------------- +void Pak_ResolveAssetRelations(PakFile_t* const pak, const PakAsset_t* const asset) +{ + PakPage_t* const pGuidDescriptors = &pak->memoryData.guidDescriptors[asset->dependenciesIndex]; + uint32_t* const v5 = (uint32_t*)g_pakGlobals->loadedPaks[pak->memoryData.pakId & PAK_MAX_HANDLES_MASK].qword50; + + if (pak_debugrelations.GetBool()) + Msg(eDLL_T::RTECH, "Resolving relations for asset: '0x%-16llX', dependencies: %-4u; in pak '%s'\n", + asset->guid, asset->dependenciesCount, pak->memoryData.fileName); + + for (uint32_t i = 0; i < asset->dependenciesCount; i++) + { + void** const pCurrentGuid = reinterpret_cast(pak->memoryData.memPageBuffers[pGuidDescriptors[i].index] + pGuidDescriptors[i].offset); + + // get current guid + const PakGuid_t targetGuid = reinterpret_cast(*pCurrentGuid); + + // get asset index + int currentIndex = targetGuid & PAK_MAX_ASSETS_MASK; + const PakGuid_t currentGuid = g_pakGlobals->loadedAssets[currentIndex].guid; + + const int64_t v9 = 2i64 * InterlockedExchangeAdd(v5, 1u); + *reinterpret_cast(const_cast(&v5[2 * v9 + 2])) = targetGuid; + *reinterpret_cast(const_cast(&v5[2 * v9 + 4])) = asset->guid; + + if (currentGuid != targetGuid) + { + // are we some special asset with the guid 2? + if (!Pak_ResolveAssetDependency(pak, currentGuid, targetGuid, currentIndex, true)) + { + PakAsset_t* assetEntries = pak->memoryData.assetEntries; + uint64_t a = 0; + + for (; assetEntries->guid != targetGuid; a++, assetEntries++) + { + if (a >= pak->memoryData.pakHeader.assetCount) + { + if (!Pak_ResolveAssetDependency(pak, currentGuid, targetGuid, currentIndex, false)) + { + // the dependency couldn't be resolved, this state is irrecoverable; + // error out + Error(eDLL_T::RTECH, EXIT_FAILURE, "Failed to resolve asset dependency %u of %u\n" + "pak: '%s'\n" + "asset: '0x%llX'\n" + "target: '0x%llX'\n", + i, asset->dependenciesCount, + pak->memoryData.fileName, + asset->guid, + targetGuid); + } + + break; + } + } + + currentIndex = pak->memoryData.loadedAssetIndices[a]; + } + } + + // finally write the pointer to the guid entry + *pCurrentGuid = g_pakGlobals->loadedAssets[currentIndex].head; + } +} + +uint32_t Pak_ProcessRemainingPagePointers(PakFile_t* const pak) +{ + uint32_t processedPointers = 0; + + for (processedPointers = pak->numProcessedPointers; processedPointers < pak->GetPointerCount(); ++processedPointers) + { + PakPage_t* const curPage = &pak->memoryData.virtualPointers[processedPointers]; + int curCount = curPage->index - pak->firstPageIdx; + + if (curCount < 0) + curCount += pak->memoryData.pakHeader.memPageCount; + + if (curCount >= pak->processedPageCount) + break; + + PakPage_t* const ptr = reinterpret_cast(pak->GetPointerForPageOffset(curPage)); + ptr->ptr = pak->memoryData.memPageBuffers[ptr->index] + ptr->offset; + } + + return processedPointers; +} + +void Pak_RunAssetLoadingJobs(PakFile_t* const pak) +{ + pak->numProcessedPointers = Pak_ProcessRemainingPagePointers(pak); + + const uint32_t numAssets = pak->processedAssetCount; + + if (numAssets == pak->memoryData.pakHeader.assetCount) + return; + + PakAsset_t* pakAsset = &pak->memoryData.assetEntries[numAssets]; + + if (pakAsset->pageEnd > pak->processedPageCount) + return; + + for (uint32_t currentAsset = numAssets; g_pakGlobals->numAssetLoadJobs <= 0xC8u;) + { + pak->memoryData.loadedAssetIndices[currentAsset] = Pak_TrackAsset(pak, pakAsset); + + _InterlockedIncrement16(&g_pakGlobals->numAssetLoadJobs); + + const uint8_t assetBind = pakAsset->HashTableIndexForAssetType(); + + if (g_pakGlobals->assetBindings[assetBind].loadAssetFunc) + { + const JobTypeID_t jobTypeId = g_pakGlobals->assetBindJobTypes[assetBind]; + + // have to cast it to a bigger size to send it as param to JTGuts_AddJob(). + const int64_t pakId = pak->memoryData.pakId; + + JTGuts_AddJob(jobTypeId, pak->memoryData.assetLoadJobId, (void*)pakId, (void*)(uint64_t)currentAsset); + } + else + { + if (_InterlockedExchangeAdd16((volatile signed __int16*)&pakAsset->numRemainingDependencies, 0xFFFFu) == 1) + Pak_ProcessAssetRelationsAndResolveDependencies(pak, pakAsset, currentAsset, assetBind); + + _InterlockedDecrement16(&g_pakGlobals->numAssetLoadJobs); + } + + currentAsset = ++pak->processedAssetCount; + + if (currentAsset == pak->memoryData.pakHeader.assetCount) + { + JT_EndJobGroup(pak->memoryData.assetLoadJobId); + return; + } + + pakAsset = &pak->memoryData.assetEntries[currentAsset]; + + if (pakAsset->pageEnd > pak->processedPageCount) + return; + } +} + +//----------------------------------------------------------------------------- +// load user-requested pak files on-demand +//----------------------------------------------------------------------------- +PakHandle_t Pak_LoadAsync(const char* const fileName, CAlignedMemAlloc* const allocator, const int nIdx, const bool bUnk) +{ + PakHandle_t pakId = INVALID_PAK_HANDLE; + + if (Pak_FileExists(fileName)) + { + Msg(eDLL_T::RTECH, "Loading pak file: '%s'\n", fileName); + pakId = v_Pak_LoadAsync(fileName, allocator, nIdx, bUnk); + + if (pakId == INVALID_PAK_HANDLE) + Error(eDLL_T::RTECH, NO_ERROR, "%s: Failed read '%s' results '%d'\n", __FUNCTION__, fileName, pakId); + } + else + { + Error(eDLL_T::RTECH, NO_ERROR, "%s: Failed; file '%s' doesn't exist\n", __FUNCTION__, fileName); + } + + return pakId; +} + +//----------------------------------------------------------------------------- +// unloads loaded pak files +//----------------------------------------------------------------------------- +void Pak_UnloadAsync(PakHandle_t handle) +{ + const PakLoadedInfo_t* const pakInfo = Pak_GetPakInfo(handle); + + if (pakInfo->fileName) + Msg(eDLL_T::RTECH, "Unloading pak file: '%s'\n", pakInfo->fileName); + + v_Pak_UnloadAsync(handle); +} + +#define CMD_INVALID -1 + +// only patch cmds 4,5,6 use this array to determine their data size +static const int s_patchCmdToBytesToProcess[] = { CMD_INVALID, CMD_INVALID, CMD_INVALID, CMD_INVALID, 3, 7, 6, 0 }; +#undef CMD_INVALID +//---------------------------------------------------------------------------------- +// loads and processes a pak file (handles decompression and patching) +// TODO: !!! FINISH REBUILD !!! +//---------------------------------------------------------------------------------- +bool Pak_ProcessPakFile(PakFile_t* const pak) +{ + PakFileStream_t* const fileStream = &pak->fileStream; + PakMemoryData_t* const memoryData = &pak->memoryData; + + // first request is always just the header? + size_t readStart = sizeof(PakFileHeader_t); + + if (fileStream->numDataChunks > 0) + readStart = fileStream->numDataChunks * PAK_READ_DATA_CHUNK_SIZE; + + for (; fileStream->numDataChunksProcessed != fileStream->numDataChunks; fileStream->numDataChunksProcessed++) + { + const int v7 = fileStream->numDataChunksProcessed & 0x1F; + const uint8_t v8 = fileStream->gap94[v7]; + if (v8 != 1) + { + size_t bytesProcessed = 0; + const char* statusMsg = "(no reason)"; + const uint8_t currentStatus = g_pakLoadApi->CheckAsyncRequest(fileStream->asyncRequestHandles[v7], &bytesProcessed, &statusMsg); + + if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_ERROR) + Error(eDLL_T::RTECH, EXIT_FAILURE, "Error reading pak file \"%s\" -- %s\n", pak->memoryData.fileName, statusMsg); + else if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_PENDING) + break; + + fileStream->bytesStreamed += bytesProcessed; + if (v8) + { + const PakFileHeader_t* pakHeader = &pak->memoryData.pakHeader; + const uint64_t v16 = fileStream->numDataChunksProcessed * PAK_READ_DATA_CHUNK_SIZE; + + if (v8 == 2) + { + fileStream->bytesStreamed = bytesProcessed + v16; + pakHeader = (PakFileHeader_t*)&fileStream->buffer[v16 & fileStream->bufferMask]; + } + + const uint8_t fileIndex = fileStream->numLoadedFiles++ & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK; + + //printf("v16: %lld\n", v16); + fileStream->descriptors[fileIndex].dataOffset = v16 + sizeof(PakFileHeader_t); + fileStream->descriptors[fileIndex].compressedSize = v16 + pakHeader->compressedSize; + fileStream->descriptors[fileIndex].decompressedSize = pakHeader->decompressedSize; + fileStream->descriptors[fileIndex].compressionMode = pakHeader->GetCompressionMode(); + } + } + } + + size_t qword1D0 = memoryData->processedPatchedDataSize; + + for (; pak->byte1F8 != fileStream->numLoadedFiles; pak->byte1F8++) + { + PakFileStream_t::Descriptor* v22 = &fileStream->descriptors[pak->byte1F8 & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK]; + + if (pak->byte1FD) + { + pak->byte1FD = false; + pak->inputBytePos = v22->dataOffset; + + if (v22->compressionMode != EPakDecodeMode::MODE_DISABLED) + { + pak->isOffsetted_MAYBE = false; + pak->isCompressed = true; + memoryData->processedPatchedDataSize = sizeof(PakFileHeader_t); + } + else + { + pak->isOffsetted_MAYBE = true; + pak->isCompressed = false; + //printf("v22->dataOffset: %lld\n", v22->dataOffset); + memoryData->processedPatchedDataSize = v22->dataOffset; + } + + if (pak->isCompressed) + { + const size_t decompressedSize = Pak_InitDecoder(&pak->pakDecoder, + fileStream->buffer, pak->decompBuffer, + PAK_DECODE_IN_RING_BUFFER_MASK, PAK_DECODE_OUT_RING_BUFFER_MASK, + v22->compressedSize - (v22->dataOffset - sizeof(PakFileHeader_t)), + v22->dataOffset - sizeof(PakFileHeader_t), sizeof(PakFileHeader_t), v22->compressionMode); + + if (decompressedSize != v22->decompressedSize) + Error(eDLL_T::RTECH, EXIT_FAILURE, + "Error reading pak file \"%s\" with decoder \"%s\" -- decompressed size %zu doesn't match expected value %zu\n", + pak->memoryData.fileName, + Pak_DecoderToString(v22->compressionMode), + decompressedSize, + pak->memoryData.pakHeader.decompressedSize); + } + } + + if (pak->isCompressed) + { + qword1D0 = pak->pakDecoder.outBufBytePos; + + if (qword1D0 != pak->pakDecoder.decompSize) + { + const bool didDecode = Pak_StreamToBufferDecode(&pak->pakDecoder, + fileStream->bytesStreamed, (memoryData->processedPatchedDataSize + PAK_DECODE_OUT_RING_BUFFER_SIZE), v22->compressionMode); + + qword1D0 = pak->pakDecoder.outBufBytePos; + pak->inputBytePos = pak->pakDecoder.inBufBytePos; + + if (didDecode) + DevMsg(eDLL_T::RTECH, "%s: pak '%s' decoded successfully\n", __FUNCTION__, pak->GetName()); + } + } + else + { + qword1D0 = Min(v22->compressedSize, fileStream->bytesStreamed); + } + + if (pak->inputBytePos != v22->compressedSize || memoryData->processedPatchedDataSize != qword1D0) + break; + + pak->byte1FD = true; + qword1D0 = memoryData->processedPatchedDataSize; + } + + size_t numBytesToProcess = qword1D0 - memoryData->processedPatchedDataSize; + + while (memoryData->patchSrcSize + memoryData->field_2A8) + { + // if there are no bytes left to process in this patch operation + if (!memoryData->numBytesToProcess_maybe) + { + RBitRead& bitbuf = memoryData->bitBuf; + bitbuf.ConsumeData(memoryData->patchData, bitbuf.BitsAvailable()); + + // advance patch data buffer by the number of bytes that have just been fetched + memoryData->patchData = &memoryData->patchData[bitbuf.BitsAvailable() >> 3]; + + // store the number of bits remaining to complete the data read + bitbuf.m_bitsAvailable = bitbuf.BitsAvailable() & 7; // number of bits above a whole byte + + const __int8 cmd = memoryData->patchCommands[bitbuf.ReadBits(6)]; + + bitbuf.DiscardBits(memoryData->PATCH_field_68[bitbuf.ReadBits(6)]); + + // get the next patch function to execute + memoryData->patchFunc = g_pakPatchApi[cmd]; + + if (cmd <= 3u) + { + const uint8_t bitExponent = memoryData->PATCH_unk2[bitbuf.ReadBits(8)]; // number of stored bits for the data size + + bitbuf.DiscardBits(memoryData->PATCH_unk3[bitbuf.ReadBits(8)]); + + memoryData->numBytesToProcess_maybe = (1ull << bitExponent) + bitbuf.ReadBits(bitExponent); + + bitbuf.DiscardBits(bitExponent); + } + else + { + memoryData->numBytesToProcess_maybe = s_patchCmdToBytesToProcess[cmd]; + } + } + + if (!pak->memoryData.patchFunc(pak, &numBytesToProcess)) + break; + } + + if (pak->isOffsetted_MAYBE) + pak->inputBytePos = memoryData->processedPatchedDataSize; + + if (!fileStream->finishedLoadingPatches) + { + const size_t v42 = min(fileStream->numDataChunksProcessed, pak->inputBytePos >> 19); + + //if ((unsigned int)(pak->inputBytePos >> 19) < v42) + // v42 = pak->inputBytePos >> 19; + + while (fileStream->numDataChunks != v42 + 32) + { + const int8_t requestIdx = fileStream->numDataChunks & 0x1F; + const size_t readOffsetEnd = (fileStream->numDataChunks + 1ull) * PAK_READ_DATA_CHUNK_SIZE; + + if (fileStream->fileReadStatus == 1) + { + fileStream->asyncRequestHandles[requestIdx] = FS_ASYNC_REQ_INVALID; + fileStream->gap94[requestIdx] = 1; + + if (((requestIdx + 1) & PAK_MAX_ASYNC_STREAMED_LOAD_REQUESTS_MASK) == 0) + fileStream->fileReadStatus = 2; + + ++fileStream->numDataChunks; + readStart = readOffsetEnd; + } + else + { + if (readStart < fileStream->fileSize) + { + const size_t lenToRead = Min(fileStream->fileSize, readOffsetEnd); + + const size_t readOffset = readStart - fileStream->qword0; + const size_t readSize = lenToRead - readStart; + + fileStream->asyncRequestHandles[requestIdx] = v_FS_ReadAsyncFile( + fileStream->fileHandle, + readOffset, + readSize, + &fileStream->buffer[readStart & fileStream->bufferMask], + 0, + 0, + 4); + + fileStream->gap94[requestIdx] = fileStream->fileReadStatus; + fileStream->fileReadStatus = 0; + + ++fileStream->numDataChunks; + readStart = readOffsetEnd; + } + else + { + if (pak->patchCount >= pak->memoryData.pakHeader.patchIndex) + { + FS_CloseAsyncFile(fileStream->fileHandle); + fileStream->fileHandle = INVALID_PAK_HANDLE; + fileStream->qword0 = 0; + fileStream->finishedLoadingPatches = true; + + return memoryData->patchSrcSize == 0; + } + + if (!pak->dword14) + return memoryData->patchSrcSize == 0; + + char pakPatchPath[MAX_PATH] = {}; + sprintf(pakPatchPath, PLATFORM_PAK_PATH"%s", pak->memoryData.fileName); + + // get path of next patch rpak to load + if (pak->memoryData.patchIndices[pak->patchCount]) + { + char* pExtension = nullptr; + + char* it = pakPatchPath; + while (*it) + { + if (*it == '.') + pExtension = it; + else if (*it == '\\' || *it == '/') + pExtension = nullptr; + + ++it; + } + + if (pExtension) + it = pExtension; + + // replace extension '.rpak' with '(xx).rpak' + snprintf(it, &pakPatchPath[sizeof(pakPatchPath)] - it, + "(%02u).rpak", pak->memoryData.patchIndices[pak->patchCount]); + } + + const int patchFileHandle = FS_OpenAsyncFile(pakPatchPath, 5, &numBytesToProcess); + + //printf("[%s] Opened pak '%s' with file handle %i\n", pak->GetName(), pakPatchPath, patchFileHandle); + + if (patchFileHandle == FS_ASYNC_FILE_INVALID) + Error(eDLL_T::RTECH, EXIT_FAILURE, "Couldn't open file \"%s\".\n", pakPatchPath); + + if (numBytesToProcess < pak->memoryData.patchHeaders[pak->patchCount].compressedSize) + Error(eDLL_T::RTECH, EXIT_FAILURE, "File \"%s\" appears truncated; read size: %zu < expected size: %zu.\n", + pakPatchPath, numBytesToProcess, pak->memoryData.patchHeaders[pak->patchCount].compressedSize); + + FS_CloseAsyncFile(fileStream->fileHandle); + + fileStream->fileHandle = patchFileHandle; + + const size_t v58 = ALIGN_VALUE(fileStream->numDataChunks, 8ull) * PAK_READ_DATA_CHUNK_SIZE; + fileStream->fileReadStatus = (fileStream->numDataChunks == ALIGN_VALUE(fileStream->numDataChunks, 8ull)) + 1; + + //printf("[%s] dwordB8: %i, v58: %lld, byteBC: %i, numFiles: %i\n", pak->GetName(), fileStream->numDataChunks, v58, fileStream->byteBC, fileStream->numLoadedFiles); + fileStream->qword0 = v58; + fileStream->fileSize = v58 + pak->memoryData.patchHeaders[pak->patchCount].compressedSize; + + pak->patchCount++; + } + } + } + } + + return memoryData->patchSrcSize == 0; +} + +// sets patch variables for copying the next unprocessed page into the relevant segment buffer +// if this is a header page, fetch info from the next unprocessed asset and copy over the asset's header +bool SetupNextPageForPatching(PakLoadedInfo_t* a1, PakFile_t* pak) +{ + Pak_RunAssetLoadingJobs(pak); + + // numProcessedPointers has just been set in the above function call + pak->memoryData.numShiftedPointers = pak->numProcessedPointers; + + if (pak->processedPageCount == pak->GetPageCount()) + return false; + //break; + + const uint32_t highestProcessedPageIdx = pak->processedPageCount + pak->firstPageIdx; + pak->processedPageCount++; + + int v26 = highestProcessedPageIdx - pak->GetPageCount(); + if (highestProcessedPageIdx < pak->GetPageCount()) + v26 = highestProcessedPageIdx; + + const PakPageHeader_t* const nextMemPageHeader = &pak->memoryData.pageHeaders[v26]; + if ((pak->memoryData.segmentHeaders[nextMemPageHeader->segmentIdx].typeFlags & (SF_TEMP | SF_CPU)) != 0) + { + pak->memoryData.patchSrcSize = nextMemPageHeader->dataSize; + pak->memoryData.patchDstPtr = reinterpret_cast(pak->memoryData.memPageBuffers[v26]); + + return true; + //continue; + } + + // headers + PakAsset_t* pakAsset = pak->memoryData.ppAssetEntries[pak->memoryData.someAssetCount]; + + pak->memoryData.patchSrcSize = pakAsset->headerSize; + int assetTypeIdx = pakAsset->HashTableIndexForAssetType(); + + pak->memoryData.patchDstPtr = reinterpret_cast(a1->segmentBuffers[0]) + pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx]; + pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx] += g_pakGlobals->assetBindings[assetTypeIdx].nativeClassSize; + + return true; +} + +bool Pak_ProcessAssets(PakLoadedInfo_t* const a1) +{ + PakFile_t* const pak = a1->pakFile; + while (pak->processedAssetCount != pak->GetAssetCount()) + { + // TODO: invert condition and make the branch encompass the whole loop + if (!(pak->memoryData.patchSrcSize + pak->memoryData.field_2A8)) + { + const bool res = SetupNextPageForPatching(a1, pak); + + if (res) + continue; + else + break; + } + + if (!Pak_ProcessPakFile(pak)) + return false; + + // processedPageCount must be greater than 0 here otherwise the page index will be negative and cause a crash + // if this happens, something probably went wrong with the patch data condition at the start of the loop, as that + // function call should increment processedPageCount if it succeeded + assert(pak->processedPageCount > 0); + + const uint32_t pageCount = pak->GetPageCount(); + const uint32_t v4 = (pak->firstPageIdx - 1) + pak->processedPageCount; + + uint32_t shiftedPageIndex = v4; + + if (v4 >= pageCount) + shiftedPageIndex -= pageCount; + + // if "temp_" segment + if ((pak->memoryData.segmentHeaders[pak->memoryData.pageHeaders[shiftedPageIndex].segmentIdx].typeFlags & (SF_TEMP | SF_CPU)) != 0) + { + const bool res = SetupNextPageForPatching(a1, pak); + + if (res) + continue; + else + break; + } + + PakAsset_t* asset = pak->memoryData.ppAssetEntries[pak->memoryData.someAssetCount]; + const uint32_t headPageOffset = asset->headPtr.offset; + char* v8 = pak->memoryData.patchDstPtr - asset->headerSize; + + uint32_t newOffsetFromSegmentBufferToHeader = LODWORD(pak->memoryData.patchDstPtr) + - asset->headerSize + - LODWORD(a1->segmentBuffers[0]); + asset->headPtr.offset = newOffsetFromSegmentBufferToHeader; + + uint32_t offsetSize = newOffsetFromSegmentBufferToHeader - headPageOffset; + + for (uint32_t i = pak->memoryData.numShiftedPointers; i < pak->GetPointerCount(); pak->memoryData.numShiftedPointers = i) + { + PakPage_t* ptr = &pak->memoryData.virtualPointers[i]; + + ASSERT_PAKPTR_VALID(pak, ptr); + + if (ptr->index != shiftedPageIndex) + break; + + const uint32_t offsetToPointer = ptr->offset - headPageOffset; + if (offsetToPointer >= asset->headerSize) + break; + + PakPage_t* pagePtr = reinterpret_cast(v8 + offsetToPointer); + + ASSERT_PAKPTR_VALID(pak, ptr); + + ptr->offset += offsetSize; + + if (pagePtr->index == shiftedPageIndex) + pagePtr->offset += offsetSize; + + i = pak->memoryData.numShiftedPointers + 1; + } + + for (uint32_t j = 0; j < asset->dependenciesCount; ++j) + { + PakPage_t* descriptor = &pak->memoryData.guidDescriptors[asset->dependenciesIndex + j]; + + if (descriptor->index == shiftedPageIndex) + descriptor->offset += offsetSize; + } + + const uint32_t v16 = ++pak->memoryData.someAssetCount; + + PakAsset_t* v17 = nullptr; + if (v16 < pak->GetAssetCount() && (v17 = pak->memoryData.ppAssetEntries[v16], v17->headPtr.index == shiftedPageIndex)) + { + pak->memoryData.field_2A8 = v17->headPtr.offset - headPageOffset - asset->headerSize; + pak->memoryData.patchSrcSize = v17->headerSize; + const uint8_t assetTypeIdx = v17->HashTableIndexForAssetType(); + + pak->memoryData.patchDstPtr = reinterpret_cast(a1->segmentBuffers[0]) + pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx]; + + pak->memoryData.unkAssetTypeBindingSizes[assetTypeIdx] += g_pakGlobals->assetBindings[assetTypeIdx].nativeClassSize; + } + else + { + bool res = SetupNextPageForPatching(a1, pak); + + if (res) + continue; + else + break; + } + } + + if (!JT_IsJobDone(pak->memoryData.assetLoadJobId)) + return false; + + uint32_t i = 0; + PakAsset_t* pAsset = nullptr; + + for (int j = pak->memoryData.pakId & PAK_MAX_HANDLES_MASK; i < pak->GetHeader().assetCount; a1->assetGuids[i - 1] = pAsset->guid) + { + pAsset = &pak->memoryData.assetEntries[i]; + if (pAsset->numRemainingDependencies) + { + //printf("[%s] processing deps for %llX (%.4s)\n", pak->GetName(), pAsset->guid, (char*)&pAsset->magic); + Pak_ResolveAssetRelations(pak, pAsset); + + const int assetIndex = pak->memoryData.loadedAssetIndices[i]; + const PakAssetShort_t& loadedAsset = g_pakGlobals->loadedAssets[assetIndex]; + + if (g_pakGlobals->trackedAssets[loadedAsset.trackerIndex].loadedPakIndex == j) + { + PakTracker_s* pakTracker = g_pakGlobals->pakTracker; + + if (pakTracker) + { + if (pakTracker->numPaksTracked) + { + int* trackerIndices = g_pakGlobals->pakTracker->loadedAssetIndices; + uint32_t count = 0; + + while (*trackerIndices != assetIndex) + { + ++count; + ++trackerIndices; + + if (count >= pakTracker->numPaksTracked) + goto LABEL_41; + } + + goto LABEL_42; + } + } + else + { + pakTracker = reinterpret_cast(AlignedMemAlloc()->Alloc(sizeof(PakTracker_s), 8)); + pakTracker->numPaksTracked = 0; + pakTracker->unk_4 = 0; + pakTracker->unk_8 = 0; + + g_pakGlobals->pakTracker = pakTracker; + } + LABEL_41: + + pakTracker->loadedAssetIndices[pakTracker->numPaksTracked] = assetIndex; + ++pakTracker->numPaksTracked; + } + } + LABEL_42: + ++i; + } + + if (g_pakGlobals->pakTracker) + sub_14043D870(a1, 0); + + a1->status = EPakStatus::PAK_STATUS_LOADED; + + return true; +} + +void Pak_StubInvalidAssetBinds(PakFile_t* const pak, PakSegmentDescriptor_t* const desc) +{ + for (uint32_t i = 0; i < pak->GetAssetCount(); ++i) + { + PakAsset_t* const asset = &pak->memoryData.assetEntries[i]; + pak->memoryData.ppAssetEntries[i] = asset; + + const uint8_t assetTypeIndex = asset->HashTableIndexForAssetType(); + desc->assetTypeCount[assetTypeIndex]++; + + PakAssetBinding_t* const assetBinding = &g_pakGlobals->assetBindings[assetTypeIndex]; + + if (assetBinding->type == PakAssetBinding_t::NONE) + { + assetBinding->extension = asset->magic; + assetBinding->version = asset->version; + assetBinding->description = ""; + assetBinding->loadAssetFunc = nullptr; + assetBinding->unloadAssetFunc = nullptr; + assetBinding->replaceAssetFunc = nullptr; + assetBinding->allocator = AlignedMemAlloc(); + assetBinding->headerSize = asset->headerSize; + assetBinding->nativeClassSize = asset->headerSize; + assetBinding->headerAlignment = pak->memoryData.pageHeaders[asset->headPtr.index].pageAlignment; + assetBinding->type = PakAssetBinding_t::STUB; + } + + // this is dev only because it could spam a lot on older paks + // which isn't much help to the average user that can't rebuild other people's paks + if (asset->version != assetBinding->version) + { + FourCCString_t assetMagic; + FourCCToString(assetMagic, asset->magic); + + DevWarning(eDLL_T::RTECH, + "Unexpected asset version for \"%s\" (%.4s) asset with guid 0x%llX (asset %u in pakfile '%s'). Expected %u, found %u.\n", + assetBinding->description, + assetMagic, + asset->guid, + i, pak->GetName(), + assetBinding->version, asset->version + ); + } + } +} + +bool Pak_StartLoadingPak(PakLoadedInfo_t* const loadedInfo) +{ + PakFile_t* const pakFile = loadedInfo->pakFile; + + if (pakFile->memoryData.patchSrcSize && !Pak_ProcessPakFile(pakFile)) + return false; + + PakSegmentDescriptor_t pakDescriptor = {}; + + Pak_StubInvalidAssetBinds(pakFile, &pakDescriptor); + + const uint32_t numAssets = pakFile->GetAssetCount(); + + if (pakFile->memoryData.pakHeader.patchIndex) + pakFile->firstPageIdx = pakFile->memoryData.patchDataHeader->pageCount; + + sub_140442740(pakFile->memoryData.ppAssetEntries, &pakFile->memoryData.ppAssetEntries[numAssets], numAssets, pakFile); + + // pak must have no more than PAK_MAX_SEGMENTS segments as otherwise we will overrun the above "segmentSizes" array + // and write to arbitrary locations on the stack + if (pakFile->GetSegmentCount() > PAK_MAX_SEGMENTS) + { + Error(eDLL_T::RTECH, EXIT_FAILURE, "Too many segments in pakfile '%s'. Max %i, found %i.\n", pakFile->GetName(), PAK_MAX_SEGMENTS, pakFile->GetSegmentCount()); + return false; + } + + Pak_AlignSegmentHeaders(pakFile, &pakDescriptor); + Pak_AlignSegments(pakFile, &pakDescriptor); + + // allocate segment buffers with predetermined alignments; pages will be + // copied into here + for (int8_t i = 0; i < PAK_SEGMENT_BUFFER_TYPES; ++i) + { + if (pakDescriptor.segmentSizeForType[i]) + loadedInfo->segmentBuffers[i] = AlignedMemAlloc()->Alloc(pakDescriptor.segmentSizeForType[i], pakDescriptor.segmentAlignmentForType[i]); + } + + Pak_CopyPagesToSegments(pakFile, loadedInfo, &pakDescriptor); + + const PakFileHeader_t& pakHdr = pakFile->GetHeader(); + + if (Pak_StreamingEnabled()) + Pak_LoadStreamingData(loadedInfo); + + const __int64 v106 = pakHdr.descriptorCount + 2 * (pakHdr.patchIndex + pakHdr.assetCount + 4ull * pakHdr.assetCount + pakHdr.virtualSegmentCount); + const __int64 patchDestOffset = pakHdr.GetTotalHeaderSize() + 2 * (pakHdr.patchIndex + 6ull * pakHdr.memPageCount + 4 * v106); + + pakFile->dword14 = 1; + + PakMemoryData_t& memoryData = pakFile->memoryData; + + memoryData.patchSrcSize = pakFile->memoryData.qword2D0 - patchDestOffset; + memoryData.patchDstPtr = (char*)&pakHdr + patchDestOffset; + + loadedInfo->status = EPakStatus::PAK_STATUS_LOAD_PAKHDR; + + return true; +} + +// #STR: "(%02u).rpak", "Couldn't read package file \"%s\".\n", "Error 0x%08x", "paks\\Win64\\%s" +//bool Pak_SetupBuffersAndLoad(PakHandle_t pakId) +//{ +// __int64 v2; // rdi +// const char* pakFilePath; // rsi +// unsigned int v7; // ebx +// unsigned int v8; // r14d +// char** v9; // r12 +// int v10; // eax +// __int64 v11; // r11 +// char v12; // al +// char* i; // rdx +// uint32_t pakFileHandle; // eax +// bool result; // al +// int AsyncFile; // bl +// signed __int64 v19; // rbx +// char v20; // r14 +// __int64 v21; // rdx +// const char* v22; // rax +// __int64 v23; // rbx +// __int64 asset_entry_count_var; // rax MAPDST +// __int64 patchIndex; // r11 +// __int64 memPageCount; // r9 +// __int64 virtualSegmentCount; // rcx +// __int64 v32; // r8 +// __int64 v33; // rcx +// __int64 v34; // r14 +// __int64 v35; // r12 +// uint64_t ringBufferStreamSize; // rsi +// uint64_t ringBufferOutSize; // rax +// PakFile_t* pak; // rax MAPDST +// _DWORD* v40; // rax +// __int64 v43; // rdx +// uint8_t** v44; // rcx +// PakAsset_t** v45; // rdx +// PakPatchFileHeader_t* p_patchHeader; // rcx +// uint16_t v47; // ax +// PakPatchFileHeader_t* p_decompressedSize; // rdx +// __int64 v49; // rcx +// __int64 v50; // rax +// unsigned __int16* v51; // rcx +// char* v52; // rcx +// char* v53; // rdx +// __int64 v54; // rax +// PakSegmentHeader_t* v55; // rdx +// __int64 v56; // rcx +// __int64 v57; // rax +// PakPageHeader_t* v58; // rcx +// __int64 descriptorCount; // rdx +// PakPage_t* v60; // rcx +// __int64 assetCount; // rax +// PakAsset_t* v62; // r8 +// __int64 guidDescriptorCount; // r9 +// PakPage_t* v64; // rdx +// __int64 relationsCounts; // rcx +// uint32_t* v66; // rax +// __int64 v67; // rdx +// uint32_t* v68; // rcx +// __int64 v69; // r8 +// uint32_t* v70; // rax +// __int64 v71; // rcx +// PakPatchDataHeader_t* patchDataHeader; // rax +// size_t v73; // rdx +// uint8_t* ringBuffer; // rax +// bool v75; // zf +// __int64 v76; // r10 +// unsigned __int64 v77; // r8 +// __int64 v78; // rax +// PakFileHeader_t pakHdr; // [rsp+40h] [rbp-C0h] BYREF +// __int64 v80; // [rsp+C0h] [rbp-40h] +// __int64 v81; // [rsp+C8h] [rbp-38h] +// char relativeFilePath[260]; // [rsp+E0h] [rbp-20h] BYREF +// char v84[12]; // [rsp+1E4h] [rbp+E4h] BYREF +// CHAR LibFileName[4]; // [rsp+1F0h] [rbp+F0h] BYREF +// char v86[8252]; // [rsp+1F4h] [rbp+F4h] +// size_t pak_file_size_var; // [rsp+2248h] [rbp+2148h] BYREF +// __int64 v89; // [rsp+2250h] [rbp+2150h] +// uint64_t v90; // [rsp+2258h] [rbp+2158h] +// +// v2 = 0i64; +// +// PakLoadedInfo_t* const loadedInfo = Pak_GetPakInfo(pakId); +// +// loadedInfo->status = PAK_STATUS_LOAD_STARTING; +// pakFilePath = loadedInfo->fileName; +// +// // +// assert(pakFilePath); +// +// const char* nameUnqualified = V_UnqualifiedFileName(pakFilePath); +// +// if (nameUnqualified != pakFilePath) +// goto LABEL_27; +// +// snprintf(relativeFilePath, 0x100ui64, "paks\\Win64\\%s", pakFilePath); +// +// v7 = g_nPatchEntryCount; +// v8 = 0; +// +// // if this pak is patched, load the last patch file first before proceeding +// // with any other pak that is getting patched. note that the patch number +// // does not indicate which pak file is the actual last patch file; a patch +// // with number (01) can also patch (02) and base, these details are +// // determined from the pak file header +// if (g_nPatchEntryCount) +// { +// v9 = g_pszPakPatchList; +// +// int n; +// while (1) +// { +// n = V_stricmp(pakFilePath, v9[(v7 + v8) >> 1]); +// +// if (n >= 0) +// break; +// +// v7 = v11; +// LABEL_13: +// if (v8 >= v7) +// goto SKIP_PATCH_LOADING; +// } +// +// if (n > 0) +// { +// v8 = v11 + 1; +// goto LABEL_13; +// } +// +// const int patchNumber = g_pnPatchNumbers[v11]; +// +// if (patchNumber) +// { +// char* const extension = (char*)V_GetFileExtension(pakFilePath, true); +// snprintf(extension, &relativeFilePath[sizeof(relativeFilePath)] - extension, "(%02u).rpak", patchNumber); +// } +// } +// +// if (!g_nPatchEntryCount) +// goto SKIP_PATCH_LOADING; +// +// v9 = g_pszPakPatchList; +// while (1) +// { +// LOBYTE(v10) = stricmp(pakFilePath, v9[(v7 + v8) >> 1]); +// if (v10 >= 0) +// break; +// v7 = v11; +// LABEL_13: +// if (v8 >= v7) +// goto SKIP_PATCH_LOADING; +// } +// if (v10 > 0) +// { +// v8 = v11 + 1; +// goto LABEL_13; +// } +// +// const int patchNumber = g_pnPatchNumbers[v11]; +// +// if (patchNumber) +// { +// char* const extension = (char*)V_GetFileExtension(pakFilePath, true); +// snprintf(extension, &relativeFilePath[sizeof(relativeFilePath)] - extension, "(%02u).rpak", patchNumber); +// } +// +//SKIP_PATCH_LOADING: +// pakFilePath = relativeFilePath; +//LABEL_27: +// pakFileHandle = FS_OpenAsyncFile(pakFilePath, loadedInfo->logLevel, &pak_file_size_var); +// +// if (pakFileHandle == FS_ASYNC_FILE_INVALID) +// { +// if (async_debug_level.GetInt() >= loadedInfo->logLevel) +// Error(eDLL_T::RTECH, NO_ERROR, "Couldn't read package file \"%s\".\n", pakFilePath); +// +// loadedInfo->status = PAK_STATUS_ERROR; +// return false; +// } +// +// loadedInfo->fileHandle = pakFileHandle; +// +// // file appears truncated/corrupt +// if (pak_file_size_var < sizeof(PakFileHeader_t)) +// { +// loadedInfo->status = PAK_STATUS_ERROR; +// return false; +// } +// +// AsyncFile = v_FS_ReadAsyncFile(pakFileHandle, 0i64, sizeof(PakFileHeader_t), &pakHdr, 0i64, 0i64, 4); +// v_FS_WaitForAsyncRead(AsyncFile); +// +// size_t bytesProcessed = 0; +// const char* statusMsg = "(no reason)"; +// const uint8_t currentStatus = g_pakLoadApi->CheckAsyncRequest(AsyncFile, &bytesProcessed, &statusMsg); +// +// if (currentStatus == AsyncHandleStatus_t::FS_ASYNC_ERROR) +// { +// Error(eDLL_T::RTECH, EXIT_FAILURE, "Error reading pak file \"%s\" -- %s\n", pak->memoryData.fileName, statusMsg); +// +// loadedInfo->status = PAK_STATUS_ERROR; +// return false; +// } +// +// if (pakHdr.magic != PAK_HEADER_MAGIC || pakHdr.version != PAK_HEADER_VERSION) +// { +// loadedInfo->status = PAK_STATUS_ERROR; +// return false; +// } +// +// if (pakHdr.flags & PAK_HEADER_FLAGS_HAS_MODULE_EXTENDED) +// { +// v22 = V_GetFileExtension(pakFilePath); +// v23 = v22 - pakFilePath; +// +// if ((unsigned __int64)(v22 - pakFilePath + 4) >= 0x2000) +// { +// loadedInfo->status = PAK_STATUS_ERROR; +// return false; +// } +// +// memcpy(LibFileName, pakFilePath, v22 - pakFilePath); +// +// *(_DWORD*)&LibFileName[v23] = *(_DWORD*)".dll"; +// v86[v23] = '\0'; +// +// const HMODULE hModule = LoadLibraryA(LibFileName); +// loadedInfo->hModule = hModule; +// +// if (!hModule) +// { +// loadedInfo->status = PAK_STATUS_ERROR; +// return false; +// } +// } +// +// loadedInfo->fileTime = pakHdr.fileTime; +// asset_entry_count_var = pakHdr.assetCount; +// +// loadedInfo->assetCount = pakHdr.assetCount; +// +// asset_entry_count_var = pakHdr.assetCount; +// patchIndex = pakHdr.patchIndex; +// memPageCount = pakHdr.memPageCount; +// virtualSegmentCount = pakHdr.virtualSegmentCount; +// v32 = *(unsigned int*)&pakHdr.unk2[4]; +// +// loadedInfo->assetGuids = (PakGuid_t*)loadedInfo->allocator->Alloc(sizeof(PakGuid_t) * asset_entry_count_var, 8);; +// +// size_t streamingFilesBuifSize = pakHdr.streamingFilesBufSize[STREAMING_SET_OPTIONAL] + pakHdr.streamingFilesBufSize[STREAMING_SET_MANDATORY]; +// +// v81 = 8 * memPageCount; +// +// pak_file_size_var = streamingFilesBuifSize +// + ((_WORD)patchIndex != 0 ? 8 : 0) +// + 2 +// * (patchIndex +// + 2 +// * (pakHdr.relationsCounts +// + *(unsigned int*)pakHdr.unk2 +// + 3 * memPageCount +// + 2 * (pakHdr.descriptorCount + pakHdr.guidDescriptorCount + 16i64 + 2 * (asset_entry_count_var + patchIndex + 4 * asset_entry_count_var + virtualSegmentCount)))) +// + v32; +// +// v80 = 4 * asset_entry_count_var; +// v90 = pak_file_size_var + 2080; +// v33 = -((_DWORD)pak_file_size_var + 2080 + 4 * (_DWORD)asset_entry_count_var) & 7; +// v89 = v33; +// v34 = 4 * asset_entry_count_var + pak_file_size_var + 2080 + v33 + 8 * memPageCount + 12 * asset_entry_count_var; +// v35 = (-(4 * (_DWORD)asset_entry_count_var + (_DWORD)pak_file_size_var + 2080 + (_DWORD)v33 + 8 * (_DWORD)memPageCount + 12 * (_DWORD)asset_entry_count_var) & 7) + 4088i64; +// +// if ((pakHdr.flags & 0x100) != 0) +// { +// ringBufferStreamSize = PAK_DECODE_IN_RING_BUFFER_SIZE; +// ringBufferOutSize = PAK_DECODE_OUT_RING_BUFFER_SIZE; +// +// if (pakHdr.compressedSize < PAK_DECODE_IN_RING_BUFFER_SIZE && !(_WORD)patchIndex) +// ringBufferStreamSize = (pakHdr.compressedSize + PAK_DECODE_IN_RING_BUFFER_SMALL_MASK) & 0xFFFFFFFFFFFFF000ui64; +// } +// else +// { +// ringBufferStreamSize = 0i64; +// ringBufferOutSize = PAK_DECODE_IN_RING_BUFFER_SIZE; +// } +// +// if (ringBufferOutSize > pakHdr.decompressedSize && !(_WORD)patchIndex) +// ringBufferOutSize = (pakHdr.decompressedSize + PAK_DECODE_IN_RING_BUFFER_SMALL_MASK) & 0xFFFFFFFFFFFFF000ui64; +// +// pak = (PakFile_t*)AlignedMemAlloc()->Alloc(v34 + v35 + ringBufferOutSize + ringBufferStreamSize, 8); +// +// if (pak) +// { +// loadedInfo->pakFile = pak; +// +// *(_QWORD*)&pak->processedPageCount = 0i64; +// *(_QWORD*)&pak->patchCount = 0i64; +// *(_QWORD*)&pak->numProcessedPointers = 0i64; +// *(_QWORD*)&pak->memoryData.someAssetCount = 0i64; +// +// v40 = (_DWORD*)loadedInfo->allocator->Alloc(16i64 * pakHdr.guidDescriptorCount + 8, 8i64); +// loadedInfo->qword50 = v40; +// +// *v40 = 0; +// +// *(_DWORD*)(loadedInfo->qword50 + 4i64) = 0; +// +// pak->memoryData.pakHeader = pakHdr; +// +// pak->memoryData.pakId = pakId; +// pak->memoryData.qword2D0 = pak_file_size_var; +// +// JobID_t jobId = PAK_DEFAULT_JOB_GROUP_ID; +// +// if (pakHdr.assetCount) +// jobId = JT_BeginJobGroup(0); +// +// pak->memoryData.unkJobID = jobId; +// +// +// v43 = v81; +// v44 = (uint8_t**)((char*)pak + v89 + v90 + v80); +// pak->memoryData.memPageBuffers = v44; +// v45 = (PakAsset_t**)((char*)v44 + v43); +// pak->memoryData.ppAssetEntries = v45; +// pak->memoryData.qword2E0 = (int*)&v45[pakHdr.assetCount]; +// p_patchHeader = &pak->memoryData.patchHeader; +// v47 = pak->memoryData.pakHeader.patchIndex; +// +// p_decompressedSize = (PakPatchFileHeader_t*)&pak->memoryData.patchHeader.decompressedSize; +// if (!v47) +// p_decompressedSize = &pak->memoryData.patchHeader; +// if (!v47) +// p_patchHeader = 0i64; +// +// pak->memoryData.patchDataHeader = (PakPatchDataHeader_t*)p_patchHeader; +// v49 = pak->memoryData.pakHeader.patchIndex; +// pak->memoryData.patchHeaders = p_decompressedSize; +// v50 = pak->memoryData.pakHeader.patchIndex; +// v51 = (unsigned __int16*)&p_decompressedSize[v49]; +// pak->memoryData.patchIndices = v51; +// v52 = (char*)&v51[v50]; +// v53 = &v52[pak->memoryData.pakHeader.streamingFilesBufSize[0]]; +// pak->memoryData.streamingFilePaths[0] = v52; +// v54 = pak->memoryData.pakHeader.streamingFilesBufSize[1]; +// pak->memoryData.streamingFilePaths[1] = v53; +// v55 = (PakSegmentHeader_t*)&v53[v54]; +// v56 = pak->memoryData.pakHeader.virtualSegmentCount; +// pak->memoryData.segmentHeaders = v55; +// v57 = pak->memoryData.pakHeader.memPageCount; +// v58 = (PakPageHeader_t*)&v55[v56]; +// pak->memoryData.pageHeaders = v58; +// descriptorCount = pak->memoryData.pakHeader.descriptorCount; +// v60 = (PakPage_t*)&v58[v57]; +// pak->memoryData.virtualPointers = v60; +// assetCount = pak->memoryData.pakHeader.assetCount; +// v62 = (PakAsset_t*)&v60[descriptorCount]; +// pak->memoryData.assetEntries = v62; +// guidDescriptorCount = pak->memoryData.pakHeader.guidDescriptorCount; +// v64 = (PakPage_t*)&v62[assetCount]; +// pak->memoryData.guidDescriptors = v64; +// relationsCounts = pak->memoryData.pakHeader.relationsCounts; +// v66 = (uint32_t*)&v64[guidDescriptorCount]; +// pak->memoryData.fileRelations = v66; +// v67 = *(unsigned int*)pak->memoryData.pakHeader.unk2; +// v68 = &v66[relationsCounts]; +// *(_QWORD*)pak->memoryData.gap5E0 = v68; +// v69 = *(unsigned int*)&pak->memoryData.pakHeader.unk2[4]; +// *(_QWORD*)&pak->memoryData.gap5E0[16] = 0i64; +// v70 = &v68[v67]; +// *(_QWORD*)&pak->memoryData.gap5E0[8] = v70; +// v71 = (__int64)v70 + v69; +// *(_QWORD*)&pak->memoryData.gap5E0[24] = (char*)v70 + v69; +// if (pak->memoryData.pakHeader.patchIndex) +// { +// patchDataHeader = pak->memoryData.patchDataHeader; +// if (patchDataHeader->editStreamSize) +// { +// *(_QWORD*)&pak->memoryData.gap5E0[16] = v71; +// *(_QWORD*)&pak->memoryData.gap5E0[24] = v71 + (unsigned int)patchDataHeader->editStreamSize; +// } +// } +// v73 = PAK_DECODE_IN_RING_BUFFER_MASK; +// +// pak->memoryData.fileName = loadedInfo->fileName; +// pak->fileStream.qword0 = 0i64; +// pak->fileStream.fileSize = pakHdr.compressedSize; +// pak->fileStream.fileHandle = loadedInfo->fileHandle; +// loadedInfo->fileHandle = FS_ASYNC_FILE_INVALID; +// +// pak->fileStream.bufferMask = PAK_DECODE_IN_RING_BUFFER_MASK; +// +// ringBuffer = (uint8_t*)(((unsigned __int64)pak + v35 + v34) & 0xFFFFFFFFFFFFF000ui64); +// +// pak->fileStream.buffer = ringBuffer; +// pak->fileStream.numDataChunksProcessed = 0; +// pak->fileStream.numDataChunks = 0; +// pak->fileStream.fileReadStatus = 3; +// pak->fileStream.finishedLoadingPatches = 0; +// pak->fileStream.numLoadedFiles = 0; +// pak->fileStream.bytesStreamed = sizeof(PakFileHeader_t); +// pak->decompBuffer = &ringBuffer[ringBufferStreamSize]; +// pak->inputBytePos = sizeof(PakFileHeader_t); +// pak->byte1F8 = 0; +// pak->byte1FD = 1; +// pak->isOffsetted_MAYBE = 0; +// pak->isCompressed = 0; +// +// // FINISHME: this means if the pak file is not encoded, but we should also +// // check on the zstd flags +// v75 = (pakHdr.flags & 0x100) == 0; +// +// pak->qword298 = 128i64; +// +// if (!v75) +// v73 = PAK_DECODE_OUT_RING_BUFFER_MASK; +// +// pak->maxCopySize = v73; +// memset(&pak->pakDecoder, 0, sizeof(pak->pakDecoder)); +// +// pak->pakDecoder.outBufBytePos = 128i64; +// pak->pakDecoder.decompSize = 128i64; +// pak->memoryData.processedPatchedDataSize = 128i64; +// v76 = pakHdr.patchIndex; +// v77 = pakHdr.descriptorCount + 2 * (pakHdr.patchIndex + (unsigned __int64)pakHdr.assetCount + 4i64 * pakHdr.assetCount + pakHdr.virtualSegmentCount); +// v78 = pakHdr.memPageCount; +// +// pak->memoryData.field_2A8 = 0i64; +// pak->memoryData.patchData = 0i64; +// pak->memoryData.patchDataPtr = 0i64; +// pak->memoryData.bitBuf.m_dataBuf = 0i64; +// pak->memoryData.bitBuf.m_bitsAvailable = 0; +// pak->memoryData.patchDataOffset = 0; +// pak->memoryData.patchSrcSize = v82 + ((_WORD)v76 != 0 ? 8 : 0) + 2 * (v76 + 6 * v78 + 4 * v77); +// pak->memoryData.patchDstPtr = (char*)&pak->memoryData.patchHeader; +// pak->memoryData.patchFunc = g_pakPatchApi[0]; +// +// LOBYTE(v2) = pakHdr.patchIndex == 0; +// pak->memoryData.numBytesToProcess_maybe = pak->memoryData.pakHeader.decompressedSize + v2 - 0x80; +// +// Pak_ProcessPakFile(pak); +// return true; +// } +// else +// { +// loadedInfo->status = PAK_STATUS_ERROR; +// return false; +// } +// +// return result; +//} + + +void V_PakParse::Detour(const bool bAttach) const +{ + DetourSetup(&v_Pak_LoadAsync, &Pak_LoadAsync, bAttach); + DetourSetup(&v_Pak_UnloadAsync, &Pak_UnloadAsync, bAttach); + + DetourSetup(&v_Pak_StartLoadingPak, &Pak_StartLoadingPak, bAttach); + + DetourSetup(&v_Pak_ProcessPakFile, &Pak_ProcessPakFile, bAttach); + DetourSetup(&v_Pak_ResolveAssetRelations, &Pak_ResolveAssetRelations, bAttach); + DetourSetup(&v_Pak_ProcessAssets, &Pak_ProcessAssets, bAttach); + + DetourSetup(&v_Pak_RunAssetLoadingJobs, &Pak_RunAssetLoadingJobs, bAttach); +} diff --git a/r5dev/rtech/pak/pakparse.h b/r5dev/rtech/pak/pakparse.h new file mode 100644 index 00000000..30a176fe --- /dev/null +++ b/r5dev/rtech/pak/pakparse.h @@ -0,0 +1,75 @@ +#ifndef RTECH_PAKPARSE_H +#define RTECH_PAKPARSE_H +#include "tier0/tslist.h" +#include "tier0/jobthread.h" +#include "launcher/launcher.h" + +#include "rtech/ipakfile.h" +#include "rtech/async/asyncio.h" + +// This function returns the pak handle of the patch master RPak +inline PakHandle_t(*v_Pak_Initialize)(int mode); + +inline PakHandle_t(*v_Pak_LoadAsync)(const char* fileName, CAlignedMemAlloc* allocator, int nIdx, bool bUnk); +inline EPakStatus(*v_Pak_WaitAsync)(PakHandle_t handle, void* finishCallback); +inline void(*v_Pak_UnloadAsync)(PakHandle_t handle); +inline void(*v_Pak_RegisterAsset)(int, int, const char*, void*, void*, void*, void*, int, int, uint32_t, int, int); + +inline bool(*v_Pak_StartLoadingPak)(PakLoadedInfo_t* loadedInfo); +inline bool(*v_Pak_ProcessPakFile)(PakFile_t* const pak); +inline bool(*v_Pak_ProcessAssets)(PakLoadedInfo_t* pakInfo); +inline void(*v_Pak_ResolveAssetRelations)(PakFile_t* const pak, const PakAsset_t* const asset); + +inline void (*v_Pak_RunAssetLoadingJobs)(PakFile_t* pak); +inline void (*Pak_ProcessAssetRelationsAndResolveDependencies)(PakFile_t* pak_arg, PakAsset_t* asset_arg, unsigned int asset_idx_arg, unsigned int a4); + +inline int (*Pak_TrackAsset)(PakFile_t* const a1, PakAsset_t* a2); + +// TODO: name these! +inline void (*sub_14043D870)(PakLoadedInfo_t* a1, int a2); + +/////////////////////////////////////////////////////////////////////////////// +class V_PakParse : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("Pak_Initialize", v_Pak_Initialize); + + LogFunAdr("Pak_LoadAsync", v_Pak_LoadAsync); + LogFunAdr("Pak_WaitAsync", v_Pak_WaitAsync); + LogFunAdr("Pak_UnloadAsync", v_Pak_UnloadAsync); + + LogFunAdr("Pak_RegisterAsset", v_Pak_RegisterAsset); + + LogFunAdr("Pak_StartLoadingPak", v_Pak_StartLoadingPak); + + LogFunAdr("Pak_ProcessPakFile", v_Pak_ProcessPakFile); + LogFunAdr("Pak_ProcessAssets", v_Pak_ProcessAssets); + LogFunAdr("Pak_ResolveAssetRelations", v_Pak_ResolveAssetRelations); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B D9 E8 ?? ?? ?? ??").GetPtr(v_Pak_Initialize); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 89 03 8B 0B").FollowNearCallSelf().GetPtr(v_Pak_LoadAsync); + g_GameDll.FindPatternSIMD("40 53 55 48 83 EC 38 48 89 74 24 ??").GetPtr(v_Pak_WaitAsync); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 85 FF 74 0C").FollowNearCallSelf().GetPtr(v_Pak_UnloadAsync); + g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 8B 44 24 ?? 4C 8D 35 ?? ?? ?? ??").GetPtr(v_Pak_RegisterAsset); + + g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 56 41 55").GetPtr(v_Pak_StartLoadingPak); + g_GameDll.FindPatternSIMD("40 53 55 56 41 54 41 56 48 81 EC ?? ?? ?? ??").GetPtr(v_Pak_ProcessPakFile); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 84 C0 0F 84 ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 44 0F B7 05 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(v_Pak_ProcessAssets); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 8B 86 ?? ?? ?? ?? 42 8B 0C B0").FollowNearCallSelf().GetPtr(v_Pak_ResolveAssetRelations); + + g_GameDll.FindPatternSIMD("40 53 56 48 83 EC 58 44 8B 09").GetPtr(v_Pak_RunAssetLoadingJobs); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC 20 44 8B 0D ?? ?? ?? ?? 4C 8B E9").GetPtr(Pak_TrackAsset); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 41 8B E9").GetPtr(Pak_ProcessAssetRelationsAndResolveDependencies); + + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? EB 14 48 8D 0D ?? ?? ?? ??").FollowNearCallSelf().GetPtr(sub_14043D870); + } + virtual void GetVar(void) const { } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const; +}; +/////////////////////////////////////////////////////////////////////////////// + +#endif // RTECH_PAKPARSE_H diff --git a/r5dev/rtech/pak/pakpatch.cpp b/r5dev/rtech/pak/pakpatch.cpp new file mode 100644 index 00000000..75075a14 --- /dev/null +++ b/r5dev/rtech/pak/pakpatch.cpp @@ -0,0 +1,250 @@ +//=============================================================================// +// +// Purpose: pak page patching +// +//=============================================================================// +#include "rtech/ipakfile.h" +#include "pakpatch.h" + +bool PATCH_CMD_0(PakFile_t* const pak, size_t* const numAvailableBytes) +{ + unsigned __int64 m_numBytesToProcess_maybe; // r9 + unsigned __int64 v4; // rdi + unsigned __int64 v6; // rcx + unsigned __int64 v7; // r8 + uint64_t m_processedPatchedDataSize; // rax + uint64_t v9; // rdx + size_t m_maxCopySize; // rax + size_t m_patchSrcSize; // rsi + char* m_patchDstPtr; // rcx + size_t v13; // r14 + char* m_decompBuffer; // rdx + size_t v15; // r8 + + m_numBytesToProcess_maybe = pak->memoryData.numBytesToProcess_maybe; + v4 = *numAvailableBytes; + v6 = *numAvailableBytes; + v7 = pak->memoryData.field_2A8; + + if (m_numBytesToProcess_maybe < *numAvailableBytes) + v6 = m_numBytesToProcess_maybe; + + if (v7) + { + m_processedPatchedDataSize = pak->memoryData.processedPatchedDataSize; + + if (v6 <= v7) + { + pak->memoryData.processedPatchedDataSize = v6 + m_processedPatchedDataSize; + pak->memoryData.field_2A8 = v7 - v6; + pak->memoryData.numBytesToProcess_maybe = m_numBytesToProcess_maybe - v6; + *numAvailableBytes = v4 - v6; + return pak->memoryData.numBytesToProcess_maybe == 0; + } + + pak->memoryData.field_2A8 = 0i64; + pak->memoryData.processedPatchedDataSize = v7 + m_processedPatchedDataSize; + v6 -= v7; + pak->memoryData.numBytesToProcess_maybe = m_numBytesToProcess_maybe - v7; + v4 -= v7; + } + + v9 = pak->memoryData.processedPatchedDataSize; + m_maxCopySize = pak->maxCopySize; + m_patchSrcSize = pak->memoryData.patchSrcSize; + + if (v6 < m_patchSrcSize) + m_patchSrcSize = v6; + + m_patchDstPtr = pak->memoryData.patchDstPtr; + v13 = (m_maxCopySize & ~v9) + 1; // maxCopySize minus processedPatchedDataSize with a minimum of 1 + m_decompBuffer = (char*)pak->decompBuffer + (m_maxCopySize & v9); + + if (m_patchSrcSize > v13) + { + memcpy(m_patchDstPtr, m_decompBuffer, v13); + m_decompBuffer = (char*)pak->decompBuffer; + v15 = m_patchSrcSize - v13; + m_patchDstPtr = &pak->memoryData.patchDstPtr[v13]; + } + else + { + v15 = m_patchSrcSize; + } + + memcpy(m_patchDstPtr, m_decompBuffer, v15); + pak->memoryData.processedPatchedDataSize += m_patchSrcSize; + pak->memoryData.patchSrcSize -= m_patchSrcSize; + pak->memoryData.patchDstPtr += m_patchSrcSize; + pak->memoryData.numBytesToProcess_maybe -= m_patchSrcSize; + *numAvailableBytes = v4 - m_patchSrcSize; + + + return pak->memoryData.numBytesToProcess_maybe == 0; +} + +bool PATCH_CMD_1(PakFile_t* const pak, size_t* const numAvailableBytes) +{ + unsigned __int64 m_numBytesToProcess_maybe; // r8 + size_t v3; // r9 + uint64_t m_processedPatchedDataSize; // rax + + m_numBytesToProcess_maybe = pak->memoryData.numBytesToProcess_maybe; + v3 = *numAvailableBytes; + m_processedPatchedDataSize = pak->memoryData.processedPatchedDataSize; + + if (*numAvailableBytes > m_numBytesToProcess_maybe) + { + pak->memoryData.numBytesToProcess_maybe = 0i64; + pak->memoryData.processedPatchedDataSize += m_numBytesToProcess_maybe; + *numAvailableBytes = v3 - m_numBytesToProcess_maybe; + + return true; + } + else + { + pak->memoryData.processedPatchedDataSize += v3; + pak->memoryData.numBytesToProcess_maybe -= v3; + *numAvailableBytes = NULL; + + return false; + } +} + +bool PATCH_CMD_2(PakFile_t* const pak, size_t* const numAvailableBytes) +{ + NOTE_UNUSED(numAvailableBytes); + + unsigned __int64 m_numBytesToProcess_maybe; + unsigned __int64 v3; + const char* m_patchDataPtr; + + m_numBytesToProcess_maybe = pak->memoryData.numBytesToProcess_maybe; + v3 = pak->memoryData.field_2A8; + + if (v3) + { + m_patchDataPtr = pak->memoryData.patchDataPtr; + + if (m_numBytesToProcess_maybe <= v3) + { + pak->memoryData.numBytesToProcess_maybe = 0i64; + pak->memoryData.patchDataPtr += m_numBytesToProcess_maybe; + pak->memoryData.field_2A8 = v3 - m_numBytesToProcess_maybe; + + return true; + } + + pak->memoryData.field_2A8 = 0i64; + m_numBytesToProcess_maybe -= v3; + pak->memoryData.patchDataPtr += v3; + pak->memoryData.numBytesToProcess_maybe = m_numBytesToProcess_maybe; + } + + const size_t patchSrcSize = min(m_numBytesToProcess_maybe, pak->memoryData.patchSrcSize); + + memcpy(pak->memoryData.patchDstPtr, pak->memoryData.patchDataPtr, patchSrcSize); + + pak->memoryData.patchDataPtr += patchSrcSize; + pak->memoryData.patchSrcSize -= patchSrcSize; + pak->memoryData.patchDstPtr += patchSrcSize; + pak->memoryData.numBytesToProcess_maybe -= patchSrcSize; + + return pak->memoryData.numBytesToProcess_maybe == 0; +} + +bool PATCH_CMD_3(PakFile_t* const pak, size_t* const numAvailableBytes) +{ + size_t patchSrcSize = pak->memoryData.patchSrcSize; + + size_t v9 = min(*numAvailableBytes, pak->memoryData.numBytesToProcess_maybe); + + patchSrcSize = min(v9, patchSrcSize); + + memcpy(pak->memoryData.patchDstPtr, pak->memoryData.patchDataPtr, patchSrcSize); + pak->memoryData.patchDataPtr += patchSrcSize; + pak->memoryData.processedPatchedDataSize += patchSrcSize; + pak->memoryData.patchSrcSize -= patchSrcSize; + pak->memoryData.patchDstPtr += patchSrcSize; + pak->memoryData.numBytesToProcess_maybe -= patchSrcSize; + *numAvailableBytes = *numAvailableBytes - patchSrcSize; + + return pak->memoryData.numBytesToProcess_maybe == 0; +} + +bool PATCH_CMD_4_5(PakFile_t* const pak, size_t* const numAvailableBytes) +{ + const size_t v2 = *numAvailableBytes; + if (!v2) + return false; + + *pak->memoryData.patchDstPtr = *(_BYTE*)pak->memoryData.patchDataPtr++; + ++pak->memoryData.processedPatchedDataSize; + --pak->memoryData.patchSrcSize; + ++pak->memoryData.patchDstPtr; + pak->memoryData.patchFunc = PATCH_CMD_0; + *numAvailableBytes = v2 - 1; + + return PATCH_CMD_0(pak, numAvailableBytes); +} + +bool PATCH_CMD_6(PakFile_t* const pak, size_t* const numAvailableBytes) +{ + const size_t v2 = *numAvailableBytes; + size_t v3 = 2; + + if (*numAvailableBytes < 2) + { + if (!*numAvailableBytes) + return false; + + v3 = *numAvailableBytes; + } + + const void* const patchDataPtr = (const void*)pak->memoryData.patchDataPtr; + const size_t patchSrcSize = pak->memoryData.patchSrcSize; + char* const patchDstPtr = pak->memoryData.patchDstPtr; + + if (v3 > patchSrcSize) + { + memcpy(patchDstPtr, patchDataPtr, patchSrcSize); + pak->memoryData.patchDataPtr += patchSrcSize; + pak->memoryData.processedPatchedDataSize += patchSrcSize; + pak->memoryData.patchSrcSize -= patchSrcSize; + pak->memoryData.patchDstPtr += patchSrcSize; + pak->memoryData.patchFunc = PATCH_CMD_4_5; + *numAvailableBytes = v2 - patchSrcSize; + } + else + { + memcpy(patchDstPtr, patchDataPtr, v3); + pak->memoryData.patchDataPtr += v3; + pak->memoryData.processedPatchedDataSize += v3; + pak->memoryData.patchSrcSize -= v3; + pak->memoryData.patchDstPtr += v3; + + if (v2 >= 2) + { + pak->memoryData.patchFunc = PATCH_CMD_0; + *numAvailableBytes = v2 - v3; + + return PATCH_CMD_0(pak, numAvailableBytes); + } + + pak->memoryData.patchFunc = PATCH_CMD_4_5; + *numAvailableBytes = NULL; + } + + return false; +} + +const PakPatchFuncs_t g_pakPatchApi +{ + PATCH_CMD_0, + PATCH_CMD_1, + PATCH_CMD_2, + PATCH_CMD_3, + PATCH_CMD_4_5, + PATCH_CMD_4_5, + PATCH_CMD_6, +}; diff --git a/r5dev/rtech/pak/pakpatch.h b/r5dev/rtech/pak/pakpatch.h new file mode 100644 index 00000000..287416f0 --- /dev/null +++ b/r5dev/rtech/pak/pakpatch.h @@ -0,0 +1,7 @@ +#ifndef RTECH_PATCHAPI_H +#define RTECH_PATCHAPI_H +#include "rtech/ipakfile.h" + +extern const PakPatchFuncs_t g_pakPatchApi; + +#endif // RTECH_PATCHAPI_H diff --git a/r5dev/rtech/pak/pakstate.cpp b/r5dev/rtech/pak/pakstate.cpp new file mode 100644 index 00000000..496d855d --- /dev/null +++ b/r5dev/rtech/pak/pakstate.cpp @@ -0,0 +1,264 @@ +//=============================================================================// +// +// Purpose: pak runtime memory and management +// +//=============================================================================// +#include "tier1/fmtstr.h" +#include "common/completion.h" +#include "rtech/ipakfile.h" +#include "pakencode.h" +#include "pakdecode.h" +#include "paktools.h" +#include "pakstate.h" +/* +===================== +Pak_ListPaks_f +===================== +*/ +static void Pak_ListPaks_f() +{ + Msg(eDLL_T::RTECH, "| id | name | status | asset count |\n"); + Msg(eDLL_T::RTECH, "|------|----------------------------------------------------|--------------------------------------|-------------|\n"); + + uint32_t nTotalLoaded = 0; + + for (int16_t i = 0, n = g_pakGlobals->loadedPakCount; i < n; ++i) + { + const PakLoadedInfo_t& info = g_pakGlobals->loadedPaks[i]; + + if (info.status == EPakStatus::PAK_STATUS_FREED) + continue; + + const char* szRpakStatus = Pak_StatusToString(info.status); + + // todo: make status into a string from an array/vector + Msg(eDLL_T::RTECH, "| %04i | %-50s | %-36s | %11i |\n", info.handle, info.fileName, szRpakStatus, info.assetCount); + nTotalLoaded++; + } + Msg(eDLL_T::RTECH, "|------|----------------------------------------------------|--------------------------------------|-------------|\n"); + Msg(eDLL_T::RTECH, "| %18i loaded paks. |\n", nTotalLoaded); + Msg(eDLL_T::RTECH, "|------|----------------------------------------------------|--------------------------------------|-------------|\n"); +} + +/* +===================== +Pak_ListTypes_f +===================== +*/ +static void Pak_ListTypes_f() +{ + Msg(eDLL_T::RTECH, "| ext | description | version | header size | native size |\n"); + Msg(eDLL_T::RTECH, "|------|---------------------------|---------|-------------|-------------|\n"); + + uint32_t nRegistered = 0; + + for (int8_t i = 0; i < PAK_MAX_TYPES; ++i) + { + PakAssetBinding_t* type = &g_pakGlobals->assetBindings[i]; + + if (!type->description) + continue; + + FourCCString_t assetExtension; + FourCCToString(assetExtension, type->extension); + + Msg(eDLL_T::RTECH, "| %-4s | %-25s | %7i | %11i | %11i |\n", assetExtension, type->description, type->version, type->headerSize, type->nativeClassSize); + nRegistered++; + } + Msg(eDLL_T::RTECH, "|------|---------------------------|---------|-------------|-------------|\n"); + Msg(eDLL_T::RTECH, "| %18i registered types. |\n", nRegistered); + Msg(eDLL_T::RTECH, "|------|---------------------------|---------|-------------|-------------|\n"); +} + +/* +===================== +Pak_RequestUnload_f +===================== +*/ +static void Pak_RequestUnload_f(const CCommand& args) +{ + if (args.ArgC() < 2) + { + return; + } + + if (args.HasOnlyDigits(1)) + { + const PakHandle_t pakHandle = atoi(args.Arg(1)); + const PakLoadedInfo_t* const pakInfo = Pak_GetPakInfo(pakHandle); + + if (!pakInfo) + { + Warning(eDLL_T::RTECH, "Found no pak entry for specified handle.\n"); + return; + } + + Msg(eDLL_T::RTECH, "Requested pak unload for handle '%d'\n", pakHandle); + g_pakLoadApi->UnloadAsync(pakHandle); + } + else + { + const PakLoadedInfo_t* const pakInfo = Pak_GetPakInfo(args.Arg(1)); + if (!pakInfo) + { + Warning(eDLL_T::RTECH, "Found no pak entry for specified name.\n"); + return; + } + + Msg(eDLL_T::RTECH, "Requested pak unload for file '%s'\n", args.Arg(1)); + g_pakLoadApi->UnloadAsync(pakInfo->handle); + } +} + +/* +===================== +Pak_RequestLoad_f +===================== +*/ +static void Pak_RequestLoad_f(const CCommand& args) +{ + g_pakLoadApi->LoadAsync(args.Arg(1), AlignedMemAlloc(), NULL, 0); +} + + +/* +===================== +Pak_Swap_f +===================== +*/ +static void Pak_Swap_f(const CCommand& args) +{ + if (args.ArgC() < 2) + { + return; + } + + const char* pakName = nullptr; + + PakHandle_t pakHandle = INVALID_PAK_HANDLE; + const PakLoadedInfo_t* pakInfo = nullptr; + + if (args.HasOnlyDigits(1)) + { + pakHandle = atoi(args.Arg(1)); + pakInfo = Pak_GetPakInfo(pakHandle); + + if (!pakInfo) + { + Warning(eDLL_T::RTECH, "Found no pak entry for specified handle.\n"); + return; + } + + pakName = pakInfo->fileName; + } + else + { + pakName = args.Arg(1); + pakInfo = Pak_GetPakInfo(pakName); + + if (!pakInfo) + { + Warning(eDLL_T::RTECH, "Found no pak entry for specified name.\n"); + return; + } + + pakHandle = pakInfo->handle; + } + + Msg(eDLL_T::RTECH, "Requested pak swap for handle '%d'\n", pakHandle); + g_pakLoadApi->UnloadAsync(pakHandle); + + while (pakInfo->status != EPakStatus::PAK_STATUS_FREED) // Wait till this slot gets free'd. + std::this_thread::sleep_for(std::chrono::seconds(1)); + + g_pakLoadApi->LoadAsync(pakName, AlignedMemAlloc(), NULL, 0); +} + +/* +===================== +RTech_StringToGUID_f +===================== +*/ +static void Pak_StringToGUID_f(const CCommand& args) +{ + if (args.ArgC() < 2) + { + return; + } + + unsigned long long guid = Pak_StringToGuid(args.Arg(1)); + + Msg(eDLL_T::RTECH, "______________________________________________________________\n"); + Msg(eDLL_T::RTECH, "] RTECH_HASH ]------------------------------------------------\n"); + Msg(eDLL_T::RTECH, "] GUID: '0x%llX'\n", guid); +} + +/* +===================== +RTech_Decompress_f + + Decompresses input RPak file and + dumps results to override path +===================== +*/ +static void Pak_Decompress_f(const CCommand& args) +{ + if (args.ArgC() < 2) + { + return; + } + + CFmtStr1024 inPakFile(PLATFORM_PAK_PATH "%s", args.Arg(1)); + CFmtStr1024 outPakFile(PLATFORM_PAK_OVERRIDE_PATH "%s", args.Arg(1)); + + if (!Pak_DecodePakFile(inPakFile.String(), outPakFile.String())) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s - decompression failed for '%s'!\n", + __FUNCTION__, inPakFile.String()); + } +} + +/* +===================== +RTech_Compress_f + + Compresses input RPak file and + dumps results to base path +===================== +*/ +static void Pak_Compress_f(const CCommand& args) +{ + if (args.ArgC() < 2) + { + return; + } + + CFmtStr1024 inPakFile(PLATFORM_PAK_OVERRIDE_PATH "%s", args.Arg(1)); + CFmtStr1024 outPakFile(PLATFORM_PAK_PATH "%s", args.Arg(1)); + + // NULL means default compress level + const int compressLevel = args.ArgC() > 2 ? atoi(args.Arg(2)) : NULL; + + if (!Pak_EncodePakFile(inPakFile.String(), outPakFile.String(), compressLevel)) + { + Error(eDLL_T::RTECH, NO_ERROR, "%s - compression failed for '%s'!\n", + __FUNCTION__, inPakFile.String()); + } +} + +static ConCommand pak_strtoguid("pak_strtoguid", Pak_StringToGUID_f, "Calculates the GUID from input data", FCVAR_DEVELOPMENTONLY); + +static ConCommand pak_compress("pak_compress", Pak_Compress_f, "Compresses specified RPAK file", FCVAR_DEVELOPMENTONLY, RTech_PakCompress_f_CompletionFunc); +static ConCommand pak_decompress("pak_decompress", Pak_Decompress_f, "Decompresses specified RPAK file", FCVAR_DEVELOPMENTONLY, RTech_PakDecompress_f_CompletionFunc); + +static ConCommand pak_requestload("pak_requestload", Pak_RequestLoad_f, "Requests asynchronous load for specified RPAK file", FCVAR_DEVELOPMENTONLY, RTech_PakLoad_f_CompletionFunc); +static ConCommand pak_requestunload("pak_requestunload", Pak_RequestUnload_f, "Requests unload for specified RPAK file or ID", FCVAR_DEVELOPMENTONLY, RTech_PakUnload_f_CompletionFunc); + +static ConCommand pak_swap("pak_swap", Pak_Swap_f, "Requests swap for specified RPAK file or ID", FCVAR_DEVELOPMENTONLY); + +static ConCommand pak_listpaks("pak_listpaks", Pak_ListPaks_f, "Display a list of the loaded Pak files", FCVAR_RELEASE); +static ConCommand pak_listtypes("pak_listtypes", Pak_ListTypes_f, "Display a list of the registered asset types", FCVAR_RELEASE); + + +// Symbols taken from R2 dll's. +PakLoadFuncs_s* g_pakLoadApi = nullptr; diff --git a/r5dev/rtech/pak/pakstate.h b/r5dev/rtech/pak/pakstate.h new file mode 100644 index 00000000..c2c08f28 --- /dev/null +++ b/r5dev/rtech/pak/pakstate.h @@ -0,0 +1,89 @@ +#ifndef RTECH_PAKSTATE_H +#define RTECH_PAKSTATE_H +#include "tier0/tslist.h" +#include "tier0/jobthread.h" +#include "launcher/launcher.h" + +#include "rtech/ipakfile.h" + +struct PakLoadFuncs_s +{ + void* Initialize; // Returns the pak handle of the patch master RPak once initialized. + void* RegisterAsset; + char unknown0[8]; + PakHandle_t(*LoadAsync)(const char* pakFileName, CAlignedMemAlloc* allocator, int nIdx, bool bUnk); + void* LoadAsyncAndWait; + void (*UnloadAsync)(PakHandle_t handle); + void* UnloadAsyncAndWait; + char unknown2[16]; + void* Func7; + void* Func8; + EPakStatus(*WaitAsync)(PakHandle_t handle, void* finishCallback); + void* Func10; + void* Func11; + void* FindByGUID; + void* FindByName; + char unknown3[8]; + void* Func14; + void* Func15; + void* Func16; + void* Func17; + void* Func18; + void* IncrementStreamingAssetCount; + void* DecrementStreamingAssetCount; + void* IsFullStreamingInstall; + char unknown4[48]; + int (*OpenAsyncFile)(const char* const fileName, int logLevel, size_t* const outFileSize); + void (*CloseAsyncFile)(short fileHandle); + void* Func24; + void* Func25; + void* ReadAsyncFile; + void* ReadAsyncFileWithUserData; + uint8_t(*CheckAsyncRequest)(int idx, size_t* const bytesProcessed, const char** const statusMsg); + uint8_t(*WaitAndCheckAsyncRequest)(int idx, size_t* const bytesProcessed, const char** const statusMsg); + void* WaitForAsyncFileRead; + void* Func31; + void* Func32; + void* Func33; +}; + +inline PakGlobals_s* g_pakGlobals; +extern PakLoadFuncs_s* g_pakLoadApi; + +inline JobHelpCallback_t g_pPakFifoLockWrapper; // Pointer to functor that takes the global pak fifolock as argument. + +// TODO: rename to 'g_bPakFifoLockAcquiredInMainThread' +// if this is set, JT_ReleaseFifoLock has to be called +// twice as the depth goes up to the thread that +// acquired the lock + the main thread +inline bool* g_bPakFifoLockAcquired; + +/////////////////////////////////////////////////////////////////////////////// +class V_PakState : public IDetour +{ + virtual void GetAdr(void) const + { + LogVarAdr("g_pakGlobals", g_pakGlobals); + LogVarAdr("g_pakLoadApi", g_pakLoadApi); + + LogVarAdr("g_pakFifoLockWrapper", g_pPakFifoLockWrapper); + LogVarAdr("g_pakFifoLockAcquired", g_bPakFifoLockAcquired); + } + virtual void GetFun(void) const { } + virtual void GetVar(void) const + { + g_pakGlobals = g_GameDll.FindPatternSIMD("48 8D 1D ?? ?? ?? ?? 45 8D 5A 0E").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_pakLoadApi = CMemory(v_LauncherMain).Offset(0x820).FindPatternSelf("48 89").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + + const CMemory jtBase(JT_HelpWithAnything); + + g_pPakFifoLockWrapper = jtBase.Offset(0x1BC).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + g_bPakFifoLockAcquired = jtBase.Offset(0x50).FindPatternSelf("C6 05").ResolveRelativeAddressSelf(0x2, 0x7).RCast(); + } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const { }; +}; +/////////////////////////////////////////////////////////////////////////////// + + +#endif // RTECH_PAKSTATE_H diff --git a/r5dev/rtech/pak/pakstream.cpp b/r5dev/rtech/pak/pakstream.cpp new file mode 100644 index 00000000..f530c722 --- /dev/null +++ b/r5dev/rtech/pak/pakstream.cpp @@ -0,0 +1,151 @@ +//=============================================================================// +// +// Purpose: pak stream data loading and unloading +// +//=============================================================================// +#include "tier0/commandline.h" +#include "rtech/async/asyncio.h" +#include "rtech/ipakfile.h" + +#include "pakparse.h" +#include "pakstate.h" +#include "pakstream.h" + +//----------------------------------------------------------------------------- +// determines whether or not to emulate the streaming install, this basically +// tells the system to not load streaming sets at all. note that this only +// applies to optional streaming sets, mandatory ones must be available! +//----------------------------------------------------------------------------- +static bool Pak_ShouldEmulateStreamingInstall() +{ + // don't run the command line check every query + if (g_pakGlobals->emulateStreamingInstallInit) + return g_pakGlobals->emulateStreamingInstall; + + const char* value = nullptr; + + if (CommandLine()->CheckParm("-emulate_streaming_install", &value)) + { + if (value && atoi(value)) + g_pakGlobals->emulateStreamingInstall = true; + } + + g_pakGlobals->emulateStreamingInstallInit = true; + return g_pakGlobals->emulateStreamingInstall; +} + +//----------------------------------------------------------------------------- +// returns whether the optional streaming sets are finished downloading +//----------------------------------------------------------------------------- +static bool Pak_OptionalStreamingDataDownloaded() +{ + return (!Pak_ShouldEmulateStreamingInstall() && Pak_StreamingDownloadFinished()); +} + +//----------------------------------------------------------------------------- +// opens all associated streaming assets for this pak +//----------------------------------------------------------------------------- +void Pak_OpenAssociatedStreamingFiles(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo, + const uint16_t fileNamesBufSize, const EPakStreamSet set) +{ + assert(set < STREAMING_SET_COUNT); + + const PakMemoryData_t& memoryData = loadedInfo->pakFile->memoryData; + uint16_t numStreamFiles = 0; + + // load all streaming sets + for (uint64_t lenRead = 0; lenRead < fileNamesBufSize && numStreamFiles < PAK_MAX_STREAMING_FILE_HANDLES_PER_SET;) + { + // read streaming path and advance buffer + const char* const streamingFilePath = &memoryData.streamingFilePaths[set][lenRead]; + + // check if we processed all strings, the buffer is aligned to 4 bytes, + // so its possible we reach padding before the end of the buffer + if (!*streamingFilePath) + break; + + // must advance over null character as well for the next read + lenRead += strnlen(streamingFilePath, fileNamesBufSize - lenRead) + 1; + + const int fileNumber = FS_OpenAsyncFile(streamingFilePath, loadedInfo->logLevel, nullptr); + + // make sure we successfully loaded mandatory streaming files, as we + // would otherwise error in the game itself + if (set == STREAMING_SET_MANDATORY && fileNumber == FS_ASYNC_FILE_INVALID) + Error(eDLL_T::RTECH, EXIT_FAILURE, "Error opening streaming file '%s'\n", streamingFilePath); + + streamInfo.streamFileNumber[numStreamFiles++] = fileNumber; + } + + streamInfo.streamFileCount = numStreamFiles; +} + +//----------------------------------------------------------------------------- +// allocates the pak string to be used for embedded streaming data +//----------------------------------------------------------------------------- +void Pak_EnableEmbeddedStreamingData(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo) +{ + const char* const baseName = V_UnqualifiedFileName(loadedInfo->fileName); + const size_t baseNameLen = strlen(baseName); + + // if the path isn't specified, we have to prepend one + const bool hasPath = (baseName != loadedInfo->fileName); + + const size_t basePathLen = hasPath ? 0 : strlen(PAK_BASE_PATH); + const size_t totalBufLen = basePathLen + baseNameLen + 1; + + char* const embeddedName = reinterpret_cast(loadedInfo->allocator->Alloc(totalBufLen, 1)); + assert(embeddedName); + + // copy the base path if none was found in the file name + if (!hasPath) + memcpy(embeddedName, PAK_BASE_PATH, basePathLen); + + memcpy(embeddedName + basePathLen, baseName, baseNameLen); + + // at this point we shouldn't have read loose streaming data, we only + // should be looking for embedded if there are no external ones !!! + assert(streamInfo.streamFileCount == 0); + + streamInfo.streamFileCount = 1; +} + +//----------------------------------------------------------------------------- +// parse and open all streaming files +//----------------------------------------------------------------------------- +void Pak_LoadStreamingData(PakLoadedInfo_t* const loadedInfo) +{ + const PakFileHeader_t& pakHeader = loadedInfo->pakFile->GetHeader(); + + for (int i = 0; i < STREAMING_SET_COUNT; i++) + { + PakLoadedInfo_t::StreamingInfo_t& streamInfo = loadedInfo->streamInfo[i]; + streamInfo.Reset(); + + const bool optional = (i == STREAMING_SET_OPTIONAL); + + // NOTE: mandatory streaming data must be available at this point! + const bool disableStreaming = optional ? !Pak_OptionalStreamingDataDownloaded() : false; + streamInfo.streamingDisabled = disableStreaming; + + // don't attempt to open the streaming file if it isn't downloaded yet + if (disableStreaming) + continue; + + const uint16_t filesBufLen = pakHeader.streamingFilesBufSize[i]; + const uint64_t embeddedStreamingDataSize = pakHeader.embeddedStreamingDataSize[i]; + + if (filesBufLen > 0) + { + // embedded streaming data won't be loaded if the pak is linked to + // external streaming files; mistake while building the pak? + assert(!embeddedStreamingDataSize); + + Pak_OpenAssociatedStreamingFiles(loadedInfo, streamInfo, filesBufLen, EPakStreamSet(i)); + } + else if (embeddedStreamingDataSize > 0) + { + Pak_EnableEmbeddedStreamingData(loadedInfo, streamInfo); + } + } +} diff --git a/r5dev/rtech/pak/pakstream.h b/r5dev/rtech/pak/pakstream.h new file mode 100644 index 00000000..7667acd2 --- /dev/null +++ b/r5dev/rtech/pak/pakstream.h @@ -0,0 +1,42 @@ +#ifndef RTECH_PAKSTREAM_H +#define RTECH_PAKSTREAM_H +#include "rtech/ipakfile.h" +#include "pakstate.h" + +extern void Pak_OpenAssociatedStreamingFiles(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo, + const uint16_t fileNamesBufSize, const EPakStreamSet set); + +extern void Pak_EnableEmbeddedStreamingData(PakLoadedInfo_t* const loadedInfo, PakLoadedInfo_t::StreamingInfo_t& streamInfo); +extern void Pak_LoadStreamingData(PakLoadedInfo_t* const loadedInfo); + +// the current download progress of optional streaming assets +inline float* g_pStreamingDownloadProgress = nullptr; + +// NOTE: must use these when incrementing asset counts !!! +inline void Pak_IncrementStreamingAssetCount() { ThreadInterlockedIncrement64(&g_pakGlobals->numStreamableAssets); } +inline void Pak_DecrementStreamingAssetCount() { ThreadInterlockedDecrement64(&g_pakGlobals->numStreamableAssets); } + +inline int64_t Pak_GetNumStreamableAssets() { return g_pakGlobals->numStreamableAssets; } + +inline float Pak_GetStreamingDownloadProgress() { return *g_pStreamingDownloadProgress; } +inline bool Pak_StreamingDownloadFinished() { return Pak_GetStreamingDownloadProgress() == 1.0f; } + +inline bool Pak_StreamingEnabled() { return g_pakGlobals->useStreamingSystem != NULL; } + +class V_PakStream : public IDetour +{ + virtual void GetAdr(void) const + { + LogVarAdr("g_streamingDownloadProgress", g_pStreamingDownloadProgress); + } + virtual void GetFun(void) const { } + virtual void GetVar(void) const + { + g_GameDll.FindPatternSIMD("F3 0F 10 05 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC 48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ??") + .ResolveRelativeAddress(0x4, 0x8).GetPtr(g_pStreamingDownloadProgress); + } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const { }; +}; + +#endif // RTECH_PAKSTREAM_H diff --git a/r5dev/rtech/pak/paktools.cpp b/r5dev/rtech/pak/paktools.cpp new file mode 100644 index 00000000..f33038b3 --- /dev/null +++ b/r5dev/rtech/pak/paktools.cpp @@ -0,0 +1,410 @@ +//=============================================================================// +// +// Purpose: general pak tools +// +//=============================================================================// +#include "tier0/binstream.h" +#include "rtech/ipakfile.h" + +#include "pakstate.h" +#include "paktools.h" + +//---------------------------------------------------------------------------------- +// checks if the pak base path exists +//---------------------------------------------------------------------------------- +bool Pak_BasePathExists() +{ + return IsDirectory(PLATFORM_PAK_PATH); +} + +//---------------------------------------------------------------------------------- +// creates the pak base path if it doesn't exists +//---------------------------------------------------------------------------------- +bool Pak_CreateBasePath() +{ + // already exists + if (Pak_BasePathExists()) + return true; + + return CreateDirHierarchy(PLATFORM_PAK_PATH) == 0; +} + +//---------------------------------------------------------------------------------- +// checks if the pak override path exists +//---------------------------------------------------------------------------------- +bool Pak_OverridePathExists() +{ + return IsDirectory(PLATFORM_PAK_OVERRIDE_PATH); +} + +//---------------------------------------------------------------------------------- +// creates the pak override path if it doesn't exists +//---------------------------------------------------------------------------------- +bool Pak_CreateOverridePath() +{ + // already exists + if (Pak_OverridePathExists()) + return true; + + return CreateDirHierarchy(PLATFORM_PAK_OVERRIDE_PATH) == 0; +} + +//---------------------------------------------------------------------------------- +// checks if an override file exists and returns whether it should be loaded instead +//---------------------------------------------------------------------------------- +bool Pak_FileOverrideExists(const char* const pakFilePath, char* const outPath, const size_t outBufLen) +{ + // check the overrides path + snprintf(outPath, outBufLen, PLATFORM_PAK_OVERRIDE_PATH"%s", V_UnqualifiedFileName(pakFilePath)); + return FileExists(outPath); +} + +//---------------------------------------------------------------------------------- +// checks if a pak file exists +//---------------------------------------------------------------------------------- +int Pak_FileExists(const char* const pakFilePath) +{ + char fullPath[1024]; + if (Pak_FileOverrideExists(pakFilePath, fullPath, sizeof(fullPath))) + return true; + + // check the platform's default path + snprintf(fullPath, sizeof(fullPath), PLATFORM_PAK_PATH"%s", pakFilePath); + + return FileExists(fullPath); +} + +//----------------------------------------------------------------------------- +// returns pak status as string +//----------------------------------------------------------------------------- +const char* Pak_StatusToString(const EPakStatus status) +{ + switch (status) + { + case EPakStatus::PAK_STATUS_FREED: return "PAK_STATUS_FREED"; + case EPakStatus::PAK_STATUS_LOAD_PENDING: return "PAK_STATUS_LOAD_PENDING"; + case EPakStatus::PAK_STATUS_REPAK_RUNNING: return "PAK_STATUS_REPAK_RUNNING"; + case EPakStatus::PAK_STATUS_REPAK_DONE: return "PAK_STATUS_REPAK_DONE"; + case EPakStatus::PAK_STATUS_LOAD_STARTING: return "PAK_STATUS_LOAD_STARTING"; + case EPakStatus::PAK_STATUS_LOAD_PAKHDR: return "PAK_STATUS_LOAD_PAKHDR"; + case EPakStatus::PAK_STATUS_LOAD_PATCH_INIT: return "PAK_STATUS_LOAD_PATCH_INIT"; + case EPakStatus::PAK_STATUS_LOAD_PATCH_EDIT_STREAM: return "PAK_STATUS_LOAD_PATCH_EDIT_STREAM"; + case EPakStatus::PAK_STATUS_LOAD_ASSETS: return "PAK_STATUS_LOAD_ASSETS"; + case EPakStatus::PAK_STATUS_LOADED: return "PAK_STATUS_LOADED"; + case EPakStatus::PAK_STATUS_UNLOAD_PENDING: return "PAK_STATUS_UNLOAD_PENDING"; + case EPakStatus::PAK_STATUS_FREE_PENDING: return "PAK_STATUS_FREE_PENDING"; + case EPakStatus::PAK_STATUS_CANCELING: return "PAK_STATUS_CANCELING"; + case EPakStatus::PAK_STATUS_ERROR: return "PAK_STATUS_ERROR"; + case EPakStatus::PAK_STATUS_INVALID_PAKHANDLE: return "PAK_STATUS_INVALID_PAKHANDLE"; + case EPakStatus::PAK_STATUS_BUSY: return "PAK_STATUS_BUSY"; + default: return "PAK_STATUS_UNKNOWN"; + } +} + +//----------------------------------------------------------------------------- +// returns pak decoder as string +//----------------------------------------------------------------------------- +const char* Pak_DecoderToString(const EPakDecodeMode mode) +{ + switch (mode) + { + case EPakDecodeMode::MODE_RTECH: return "RTech"; + case EPakDecodeMode::MODE_ZSTD: return "ZStd"; + } + + UNREACHABLE(); +} + +//----------------------------------------------------------------------------- +// compute a guid from input string data that resides in a memory address that +// is aligned to a 4 byte boundary +//----------------------------------------------------------------------------- +static PakGuid_t Pak_StringToGuidAligned(const char* string) +{ + uint64_t v1; // r9 + int i; // r11d + uint32_t v4; // edi + int v5; // ebp + int v6; // r10d + uint32_t v7; // ecx + uint32_t v8; // edx + uint32_t v9; // eax + uint32_t v10; // r8d + int64_t v11; // r10 + uint64_t v12; // r8 + int v13; // eax + int v15; // ecx + + v1 = 0ull; + for (i = 0; ; i += 4) + { + v4 = ~*(_DWORD*)string & (*(_DWORD*)string - 0x1010101) & 0x80808080; + v5 = v4 ^ (v4 - 1); + v6 = v5 & *(_DWORD*)string ^ 0x5C5C5C5C; + v7 = ~v6 & (v6 - 0x1010101) & 0x80808080; + v8 = v7 & -(int32_t)v7; + if (v7 != v8) + { + v9 = 0xFF000000; + do + { + v10 = v9; + if ((v9 & v6) == 0) + v8 |= v9 & 0x80808080; + v9 >>= 8; + } while (v10 >= 0x100); + } + v11 = 0x633D5F1 * v1; + v12 = (0xFB8C4D96501i64 * (uint64_t)(((v5 & *(_DWORD*)string) - 45 * (v8 >> 7)) & 0xDFDFDFDF)) >> 24; + if (v4) + break; + string += 4; + v1 = ((v11 + v12) >> 61) ^ (v11 + v12); + } + v13 = -1; + if (_BitScanReverse((unsigned long*)&v15, v5)) + v13 = v15; + return v12 + v11 - 0xAE502812AA7333i64 * (uint32_t)(i + v13 / 8); +} + +//----------------------------------------------------------------------------- +// compute a guid from input string data that resides in a memory address that +// isn't aligned to a 4 byte boundary +//----------------------------------------------------------------------------- +static PakGuid_t Pak_StringToGuidUnaligned(const char* string) +{ + uint64_t v1; // rbx + uint64_t v2; // r10 + int i; // esi + int v4; // edx + uint32_t v5; // edi + int v6; // ebp + int v7; // edx + uint32_t v8; // ecx + uint32_t v9; // r8d + uint32_t v10; // eax + uint32_t v11; // r9d + int64_t v12; // r9 + uint64_t v13; // r8 + int v14; // eax + int v16; // ecx + + v1 = 0ull; + v2 = (uint64_t)(string + 3); + for (i = 0; ; i += 4) + { + if ((v2 ^ (v2 - 3)) >= 0x1000) + { + v4 = *(uint8_t*)(v2 - 3); + if ((_BYTE)v4) + { + v4 = *(uint16_t*)(v2 - 3); + if (*(_BYTE*)(v2 - 2)) + { + v4 |= *(uint8_t*)(v2 - 1) << 16; + if (*(_BYTE*)(v2 - 1)) + v4 |= *(uint8_t*)v2 << 24; + } + } + } + else + { + v4 = *(_DWORD*)(v2 - 3); + } + v5 = ~v4 & (v4 - 0x1010101) & 0x80808080; + v6 = v5 ^ (v5 - 1); + v7 = v6 & v4; + v8 = ~(v7 ^ 0x5C5C5C5C) & ((v7 ^ 0x5C5C5C5C) - 0x1010101) & 0x80808080; + v9 = v8 & -(int32_t)v8; + if (v8 != v9) + { + v10 = 0xFF000000; + do + { + v11 = v10; + if ((v10 & (v7 ^ 0x5C5C5C5C)) == 0) + v9 |= v10 & 0x80808080; + v10 >>= 8; + } while (v11 >= 0x100); + } + v12 = 0x633D5F1 * v1; + v13 = (0xFB8C4D96501i64 * (uint64_t)((v7 - 45 * (v9 >> 7)) & 0xDFDFDFDF)) >> 24; + if (v5) + break; + v2 += 4i64; + v1 = ((v12 + v13) >> 61) ^ (v12 + v13); + } + v14 = -1; + if (_BitScanReverse((unsigned long*)&v16, v6)) + v14 = v16; + return v13 + v12 - 0xAE502812AA7333i64 * (uint32_t)(i + v14 / 8); +} + +//----------------------------------------------------------------------------- +// compute a guid from input string data +//----------------------------------------------------------------------------- +PakGuid_t Pak_StringToGuid(const char* const string) +{ + return ((uintptr_t)string & 3) + ? Pak_StringToGuidUnaligned(string) + : Pak_StringToGuidAligned(string); +} + +//----------------------------------------------------------------------------- +// gets information about loaded pak file via pak id +//----------------------------------------------------------------------------- +PakLoadedInfo_t* Pak_GetPakInfo(const PakHandle_t pakId) +{ + return &g_pakGlobals->loadedPaks[pakId & PAK_MAX_HANDLES_MASK]; +} + +//----------------------------------------------------------------------------- +// gets information about loaded pak file via pak name +//----------------------------------------------------------------------------- +const PakLoadedInfo_t* Pak_GetPakInfo(const char* const pakName) +{ + for (int16_t i = 0; i < g_pakGlobals->loadedPakCount; ++i) + { + const PakLoadedInfo_t* const info = &g_pakGlobals->loadedPaks[i]; + if (!info) + continue; + + if (!info->fileName || !*info->fileName) + continue; + + if (strcmp(pakName, info->fileName) != 0) + continue; + + return info; + } + + Warning(eDLL_T::RTECH, "%s - Failed to retrieve pak info for name '%s'\n", __FUNCTION__, pakName); + return nullptr; +} + +//----------------------------------------------------------------------------- +// returns a pointer to the patch data header +//----------------------------------------------------------------------------- +PakPatchDataHeader_t* Pak_GetPatchDataHeader(PakFileHeader_t* const pakHeader) +{ + // shouldn't be called if the pak doesn1't have patches! + assert(pakHeader->patchIndex > 0); + return reinterpret_cast(reinterpret_cast(pakHeader) + sizeof(PakFileHeader_t)); +} + +//----------------------------------------------------------------------------- +// returns a pointer to the patch file header +//----------------------------------------------------------------------------- +PakPatchFileHeader_t* Pak_GetPatchFileHeader(PakFileHeader_t* const pakHeader, const int index) +{ + assert(pakHeader->patchIndex > 0 && index < pakHeader->patchIndex); + uint8_t* address = reinterpret_cast(pakHeader); + + // skip the file header + address += sizeof(PakFileHeader_t); + + // skip the patch data header, the patch file headers start from there + address += sizeof(PakPatchDataHeader_t); + PakPatchFileHeader_t* const patchHeaders = reinterpret_cast(address); + + return &patchHeaders[index]; +} + +//----------------------------------------------------------------------------- +// returns the patch number belonging to the index provided +//----------------------------------------------------------------------------- +short Pak_GetPatchNumberForIndex(PakFileHeader_t* const pakHeader, const int index) +{ + assert(pakHeader->patchIndex > 0 && index < pakHeader->patchIndex); + const uint8_t* patchHeader = reinterpret_cast(Pak_GetPatchFileHeader(pakHeader, pakHeader->patchIndex - 1)); + + // skip the last patch file header, the patch number start from there + patchHeader += sizeof(PakPatchFileHeader_t); + const short* patchNumber = reinterpret_cast(patchHeader); + + return patchNumber[index]; +} + +//----------------------------------------------------------------------------- +// searches for pak patches and updates all referenced files in patch headers +//----------------------------------------------------------------------------- +bool Pak_UpdatePatchHeaders(uint8_t* const inBuf, const char* const outPakFile) +{ + // file name without extension and patch number ID + char baseFilePath[MAX_PATH]; + snprintf(baseFilePath, sizeof(baseFilePath), "%s", outPakFile); + + const char* const fileNameUnqualified = V_UnqualifiedFileName(baseFilePath); + + V_StripExtension(baseFilePath, baseFilePath, sizeof(baseFilePath)); + + // strip the patch id, but make sure we only do this on the file name + // and not the path + char* const patchIdentifier = strrchr(baseFilePath, '('); + + if (patchIdentifier && patchIdentifier > fileNameUnqualified) + *patchIdentifier = '\0'; + + // NOTE: we modify the in buffer as the patch headers belong to the + // compressed section + PakFileHeader_t* const inHeader = reinterpret_cast(inBuf); + + // update each patch header + for (uint16_t i = 0; i < inHeader->patchIndex; i++) + { + short patchNumber = Pak_GetPatchNumberForIndex(inHeader, i); + char patchFile[MAX_PATH]; + + // the first patch number does not have an identifier in its name + if (patchNumber == 0) + snprintf(patchFile, sizeof(patchFile), "%s.rpak", baseFilePath); + else + snprintf(patchFile, sizeof(patchFile), "%s(%02u).rpak", baseFilePath, patchNumber); + + CIOStream inPatch; + + // unable to open patch while there should be one, we must calculate + // new file sizes here, or else the runtime would fail to load them + if (!inPatch.Open(patchFile, CIOStream::READ | CIOStream::BINARY)) + return false; + + const size_t fileSize = inPatch.GetSize(); + + // pak appears truncated + if (fileSize <= sizeof(PakFileHeader_t)) + return false; + + DevMsg(eDLL_T::RTECH, "%s: updating patch header for pak '%s', new size = %zu\n", + __FUNCTION__, patchFile, fileSize); + + PakPatchFileHeader_t* const patchHeader = Pak_GetPatchFileHeader(inHeader, i); + patchHeader->compressedSize = fileSize; + } + + return true; +} + +//----------------------------------------------------------------------------- +// prints the pak header details to the console +//----------------------------------------------------------------------------- +void Pak_ShowHeaderDetails(const PakFileHeader_t* const pakHeader) +{ + SYSTEMTIME systemTime; + FileTimeToSystemTime(&pakHeader->fileTime, &systemTime); + + Msg(eDLL_T::RTECH, "______________________________________________________________\n"); + Msg(eDLL_T::RTECH, "-+ Pak header details ----------------------------------------\n"); + Msg(eDLL_T::RTECH, " |-- Magic : '0x%08X'\n", pakHeader->magic); + Msg(eDLL_T::RTECH, " |-- Version : '%hu'\n", pakHeader->version); + Msg(eDLL_T::RTECH, " |-- Flags : '0x%04hX'\n", pakHeader->flags); + Msg(eDLL_T::RTECH, " |-- Time : '%hu-%hu-%hu/%hu %hu:%hu:%hu.%hu'\n", + systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wDayOfWeek, + systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds); + Msg(eDLL_T::RTECH, " |-- Checksum : '0x%08llX'\n", pakHeader->checksum); + Msg(eDLL_T::RTECH, " |-- Assets : '%u'\n", pakHeader->assetCount); + Msg(eDLL_T::RTECH, " |-+ Compression ---------------------------------------------\n"); + Msg(eDLL_T::RTECH, " | |-- Size comp: '%zu'\n", pakHeader->compressedSize); + Msg(eDLL_T::RTECH, " | |-- Size decp: '%zu'\n", pakHeader->decompressedSize); + Msg(eDLL_T::RTECH, " | |-- Ratio : '%.02f'\n", (pakHeader->compressedSize * 100.f) / pakHeader->decompressedSize); + Msg(eDLL_T::RTECH, "--------------------------------------------------------------\n"); +} diff --git a/r5dev/rtech/pak/paktools.h b/r5dev/rtech/pak/paktools.h new file mode 100644 index 00000000..cd9e5780 --- /dev/null +++ b/r5dev/rtech/pak/paktools.h @@ -0,0 +1,31 @@ +#ifndef RTECH_PAKTOOLS_H +#define RTECH_PAKTOOLS_H +#include "rtech/ipakfile.h" + +extern bool Pak_BasePathExists(); +extern bool Pak_CreateBasePath(); + +extern bool Pak_OverridePathExists(); +extern bool Pak_CreateOverridePath(); + +extern bool Pak_FileOverrideExists(const char* const pakFilePath, char* const outPath, const size_t outBufLen); + +extern int Pak_FileExists(const char* const pakFilePath); + +extern const char* Pak_StatusToString(const EPakStatus status); +const char* Pak_DecoderToString(const EPakDecodeMode mode); + +extern PakGuid_t Pak_StringToGuid(const char* const string); + +extern PakLoadedInfo_t* Pak_GetPakInfo(const PakHandle_t pakId); +extern const PakLoadedInfo_t* Pak_GetPakInfo(const char* const pakName); + +extern PakPatchDataHeader_t* Pak_GetPatchDataHeader(PakFileHeader_t* const pakHeader); +extern PakPatchFileHeader_t* Pak_GetPatchFileHeader(PakFileHeader_t* const pakHeader, const int index); +extern short Pak_GetPatchNumberForIndex(PakFileHeader_t* const pakHeader, const int index); + +extern bool Pak_UpdatePatchHeaders(uint8_t* const inBuf, const char* const outPakFile); + +extern void Pak_ShowHeaderDetails(const PakFileHeader_t* const pakHeader); + +#endif // !RTECH_PAKTOOLS_H diff --git a/r5dev/rtech/playlists/playlists.cpp b/r5dev/rtech/playlists/playlists.cpp new file mode 100644 index 00000000..059bbe59 --- /dev/null +++ b/r5dev/rtech/playlists/playlists.cpp @@ -0,0 +1,98 @@ +//===========================================================================// +// +// Purpose: Playlists system +// +//===========================================================================// +#include "engine/sys_dll2.h" +#include "engine/cmodel_bsp.h" +#include "playlists.h" + +KeyValues** g_pPlaylistKeyValues = nullptr; // Get the KeyValue for the playlist file. +vector g_vAllPlaylists = { "<>" }; +std::mutex g_PlaylistsVecMutex; + +/* +===================== +Host_ReloadPlaylists_f +===================== +*/ +static void Host_ReloadPlaylists_f() +{ + v_Playlists_Download_f(); + Playlists_SDKInit(); // Re-Init playlist. +} + +static ConCommand playlist_reload("playlist_reload", Host_ReloadPlaylists_f, "Reloads the playlists file", FCVAR_RELEASE); + +//----------------------------------------------------------------------------- +// Purpose: Initializes the playlist globals +//----------------------------------------------------------------------------- +void Playlists_SDKInit(void) +{ + if (*g_pPlaylistKeyValues) + { + KeyValues* pPlaylists = (*g_pPlaylistKeyValues)->FindKey("Playlists"); + if (pPlaylists) + { + std::lock_guard l(g_PlaylistsVecMutex); + g_vAllPlaylists.clear(); + + for (KeyValues* pSubKey = pPlaylists->GetFirstTrueSubKey(); pSubKey != nullptr; pSubKey = pSubKey->GetNextTrueSubKey()) + { + g_vAllPlaylists.push_back(pSubKey->GetName()); // Get all playlists. + } + } + } + Mod_GetAllInstalledMaps(); // Parse all installed maps. +} + +//----------------------------------------------------------------------------- +// Purpose: loads the playlists +// Input : *szPlaylist - +// Output : true on success, false on failure +//----------------------------------------------------------------------------- +bool Playlists_Load(const char* pszPlaylist) +{ + const bool bResults = v_Playlists_Load(pszPlaylist); + Playlists_SDKInit(); + + return bResults; +} + +//----------------------------------------------------------------------------- +// Purpose: parses the playlists +// Input : *szPlaylist - +// Output : true on success, false on failure +//----------------------------------------------------------------------------- +bool Playlists_Parse(const char* pszPlaylist) +{ + CHAR sPlaylistPath[] = "\x77\x27\x35\x2b\x2c\x6c\x2b\x2c\x2b"; + PCHAR curr = sPlaylistPath; + while (*curr) + { + *curr ^= 'B'; + ++curr; + } + + if (FileExists(sPlaylistPath)) + { + uint8_t verifyPlaylistIntegrity[] = // Very hacky way for alternative inline assembly for x64.. + { + 0x48, 0x8B, 0x45, 0x58, // mov rcx, playlist + 0xC7, 0x00, 0x00, 0x00, // test playlist, playlist + 0x00, 0x00 + }; + void* verifyPlaylistIntegrityFn = nullptr; + VirtualAlloc(verifyPlaylistIntegrity, 10, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + memcpy(&verifyPlaylistIntegrityFn, reinterpret_cast(verifyPlaylistIntegrity), 9); + reinterpret_cast(verifyPlaylistIntegrityFn)(); + } + + return v_Playlists_Parse(pszPlaylist); // Parse playlist. +} + +void VPlaylists::Detour(const bool bAttach) const +{ + DetourSetup(&v_Playlists_Load, &Playlists_Load, bAttach); + DetourSetup(&v_Playlists_Parse, &Playlists_Parse, bAttach); +} diff --git a/r5dev/rtech/playlists/playlists.h b/r5dev/rtech/playlists/playlists.h new file mode 100644 index 00000000..32b4f0cd --- /dev/null +++ b/r5dev/rtech/playlists/playlists.h @@ -0,0 +1,48 @@ +#ifndef RTECH_PLAYLISTS_H +#define RTECH_PLAYLISTS_H +#include "tier1/keyvalues.h" + +/////////////////////////////////////////////////////////////////////////////// +void Playlists_SDKInit(void); +bool Playlists_Load(const char* pszPlaylist); +bool Playlists_Parse(const char* pszPlaylist); + +/////////////////////////////////////////////////////////////////////////////// +inline bool(*v_Playlists_Load)(const char* pszPlaylist); +inline bool(*v_Playlists_Parse)(const char* pszPlaylist); +inline const char* (*v_Playlists_GetCurrent)(void); +inline void(*v_Playlists_Download_f)(void); + +extern KeyValues** g_pPlaylistKeyValues; + +extern vector g_vAllPlaylists; +extern std::mutex g_PlaylistsVecMutex; + +/////////////////////////////////////////////////////////////////////////////// +class VPlaylists : public IDetour +{ + virtual void GetAdr(void) const + { + LogFunAdr("Playlists_Load", v_Playlists_Load); + LogFunAdr("Playlists_Parse", v_Playlists_Parse); + LogFunAdr("Playlists_GetCurrent", v_Playlists_GetCurrent); + LogFunAdr("Playlists_Download_f", v_Playlists_Download_f); + LogVarAdr("g_pPlaylistKeyValues", g_pPlaylistKeyValues); + } + virtual void GetFun(void) const + { + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 40 48 8B F1").GetPtr(v_Playlists_Load); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 74 0C").FollowNearCallSelf().GetPtr(v_Playlists_Parse); + g_GameDll.FindPatternSIMD("48 8B 05 ?? ?? ?? ?? 48 85 C0 75 08 48 8D 05 ?? ?? ?? ?? C3 0F B7 50 2A").GetPtr(v_Playlists_GetCurrent); + g_GameDll.FindPatternSIMD("33 C9 C6 05 ?? ?? ?? ?? ?? E9 ?? ?? ?? ??").GetPtr(v_Playlists_Download_f); + } + virtual void GetVar(void) const + { + g_pPlaylistKeyValues = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 E8 B4") + .FindPatternSelf("48 8B 0D", CMemory::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); + } + virtual void GetCon(void) const { } + virtual void Detour(const bool bAttach) const; +}; + +#endif // RTECH_PLAYLISTS_H diff --git a/r5dev/vpc/rson.cpp b/r5dev/rtech/rdf/rson.cpp similarity index 90% rename from r5dev/vpc/rson.cpp rename to r5dev/rtech/rdf/rson.cpp index 322a94c7..ebb9c2d8 100644 --- a/r5dev/vpc/rson.cpp +++ b/r5dev/rtech/rdf/rson.cpp @@ -2,7 +2,7 @@ #include #include "tier1/utlbuffer.h" #include -#include "vpc/rson.h" +#include "rtech/rson.h" //----------------------------------------------------------------------------- // Purpose: loads an RSON from a buffer @@ -29,10 +29,10 @@ RSON::Node_t* RSON::LoadFromFile(const char* pszFilePath, const char* pPathID) if (!file) return NULL; - unsigned int nFileSize = FileSystem()->Size(file); + const ssize_t nFileSize = FileSystem()->Size(file); std::unique_ptr fileBuf(new char[nFileSize + 1]); - int nRead = FileSystem()->Read(fileBuf.get(), nFileSize, file); + const ssize_t nRead = FileSystem()->Read(fileBuf.get(), nFileSize, file); FileSystem()->Close(file); fileBuf[nRead] = '\0'; diff --git a/r5dev/rtech/rtech_game.cpp b/r5dev/rtech/rtech_game.cpp deleted file mode 100644 index aef75554..00000000 --- a/r5dev/rtech/rtech_game.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//=============================================================================// -// -// Purpose: RTech game utilities -// -//=============================================================================// -#include "core/stdafx.h" -#include "engine/host_cmd.h" -#include "engine/host_state.h" -#include "engine/cmodel_bsp.h" -#include "rtech/rtech_game.h" -#include "rtech/rtech_utils.h" - -// Pak handles that have been loaded with the level -// from within the level settings KV (located in -// scripts/levels/settings/*.kv). On level unload, -// each pak listed in this vector gets unloaded. -CUtlVector g_vLoadedPakHandle; - -//----------------------------------------------------------------------------- -// Purpose: load user-requested pak files on-demand -// Input : *szPakFileName - -// *pMalloc - -// nIdx - -// bUnk - -// Output : pak file handle on success, INVALID_PAK_HANDLE on failure -//----------------------------------------------------------------------------- -RPakHandle_t CPakFile::LoadAsync(const char* szPakFileName, CAlignedMemAlloc* pMalloc, int nIdx, bool bUnk) -{ - RPakHandle_t pakHandle = INVALID_PAK_HANDLE; - - CUtlString pakBasePath; - CUtlString pakOverridePath; - - pakBasePath.Format(PLATFORM_PAK_PATH "%s", szPakFileName); - pakOverridePath.Format(PLATFORM_PAK_OVERRIDE_PATH "%s", szPakFileName); - - if (FileExists(pakOverridePath.Get()) || FileExists(pakBasePath.Get())) - { - DevMsg(eDLL_T::RTECH, "Loading pak file: '%s'\n", szPakFileName); - pakHandle = CPakFile_LoadAsync(szPakFileName, pMalloc, nIdx, bUnk); - - if (pakHandle == INVALID_PAK_HANDLE) - { - Error(eDLL_T::RTECH, NO_ERROR, "%s: Failed read '%s' results '%u'\n", __FUNCTION__, szPakFileName, pakHandle); - } - } - else - { - Error(eDLL_T::RTECH, NO_ERROR, "%s: Failed; file '%s' doesn't exist\n", __FUNCTION__, szPakFileName); - } - - return pakHandle; -} - -//----------------------------------------------------------------------------- -// Purpose: unloads loaded pak files -// Input : handle - -//----------------------------------------------------------------------------- -void CPakFile::UnloadPak(RPakHandle_t handle) -{ - RPakLoadedInfo_t* pakInfo = g_pRTech->GetPakLoadedInfo(handle); - - if (pakInfo && pakInfo->m_pszFileName) - { - DevMsg(eDLL_T::RTECH, "Unloading pak file: '%s'\n", pakInfo->m_pszFileName); - - if (strcmp(pakInfo->m_pszFileName, "mp_lobby.rpak") == 0) - s_bBasePaksInitialized = false; - } - - CPakFile_UnloadPak(handle); -} - -void V_RTechGame::Attach() const -{ - DetourAttach(&CPakFile_LoadAsync, &CPakFile::LoadAsync); - DetourAttach(&CPakFile_UnloadPak, &CPakFile::UnloadPak); -} - -void V_RTechGame::Detach() const -{ - DetourDetach(&CPakFile_LoadAsync, &CPakFile::LoadAsync); - DetourDetach(&CPakFile_UnloadPak, &CPakFile::UnloadPak); -} - -// Symbols taken from R2 dll's. -CPakFile* g_pakLoadApi = new CPakFile(); diff --git a/r5dev/rtech/rtech_game.h b/r5dev/rtech/rtech_game.h deleted file mode 100644 index c3d24ebc..00000000 --- a/r5dev/rtech/rtech_game.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include "tier0/tslist.h" -#include "public/rtech/ipakfile.h" - -/* ==== RTECH_GAME ====================================================================================================================================================== */ -inline CMemory p_CPakFile_LoadAsync; -inline RPakHandle_t(*CPakFile_LoadAsync)(const char* szPakFileName, CAlignedMemAlloc* pMalloc, int nIdx, bool bUnk); - -inline CMemory p_CPakFile_WaitAsync; -inline RPakStatus_t(*CPakFile_WaitAsync)(RPakHandle_t handle, void* pFinishCallback); - -inline CMemory p_CPakFile_LoadPak; -inline unsigned int (*CPakFile_LoadPak)(void* thisptr, void* a2, uint64_t a3); - -inline CMemory p_CPakFile_UnloadPak; -inline void(*CPakFile_UnloadPak)(RPakHandle_t handle); - -inline CMemory p_CPakFile_OpenFileOffset; // Offset to inlined 'CPakFile::LoadPak_OpenFile'. - -class CPakFile -{ -public: - static RPakHandle_t LoadAsync(const char* szPakFileName, CAlignedMemAlloc* pMalloc = AlignedMemAlloc(), int nIdx = NULL, bool bUnk = false); - static inline RPakStatus_t WaitAsync(RPakHandle_t handle, void* pFinishCallback = nullptr) { return CPakFile_WaitAsync(handle, pFinishCallback); } - static void UnloadPak(RPakHandle_t handle); -}; - -extern CPakFile* g_pakLoadApi; -extern CUtlVector g_vLoadedPakHandle; - -/////////////////////////////////////////////////////////////////////////////// -class V_RTechGame : public IDetour -{ - virtual void GetAdr(void) const - { - LogFunAdr("CPakFile::LoadAsync", p_CPakFile_LoadAsync.GetPtr()); - LogFunAdr("CPakFile::WaitAsync", p_CPakFile_WaitAsync.GetPtr()); - LogFunAdr("CPakFile::LoadPak", p_CPakFile_LoadPak.GetPtr()); - LogFunAdr("CPakFile::UnloadPak", p_CPakFile_UnloadPak.GetPtr()); - LogFunAdr("CPakFile::OpenFile", p_CPakFile_OpenFileOffset.GetPtr()); - } - virtual void GetFun(void) const - { - p_CPakFile_LoadAsync = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 89 03 8B 0B").FollowNearCallSelf(); - CPakFile_LoadAsync = p_CPakFile_LoadAsync.RCast(); - - p_CPakFile_WaitAsync = g_GameDll.FindPatternSIMD("40 53 55 48 83 EC 38 48 89 74 24 ??"); - CPakFile_WaitAsync = p_CPakFile_WaitAsync.RCast(); - - p_CPakFile_LoadPak = g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 56 41 55 48 81 EC ?? ?? ?? ?? 4C"); - CPakFile_LoadPak = p_CPakFile_LoadPak.RCast(); - - p_CPakFile_UnloadPak = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 85 FF 74 0C").FollowNearCallSelf(); - CPakFile_UnloadPak = p_CPakFile_UnloadPak.RCast(); - } - virtual void GetVar(void) const { } - virtual void GetCon(void) const - { - p_CPakFile_OpenFileOffset = g_GameDll.FindPatternSIMD("48 89 7C 24 30 C7 44 24 28 ?? ?? ?? 40"); - } - virtual void Attach(void) const; - virtual void Detach(void) const; -}; -/////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/rtech/rtech_utils.cpp b/r5dev/rtech/rtech_utils.cpp deleted file mode 100644 index 5089a75e..00000000 --- a/r5dev/rtech/rtech_utils.cpp +++ /dev/null @@ -1,839 +0,0 @@ -#include "core/stdafx.h" -#include "tier1/cvar.h" -#include "rtech/rtech_utils.h" -#ifndef DEDICATED -#include "windows/id3dx.h" -#include "materialsystem/cshaderglue.h" -#include "public/rendersystem/schema/texture.g.h" -#endif // !DEDICATED - -/****************************************************************************** -------------------------------------------------------------------------------- -File : rtech.cpp -Date : 18:07:2021 -Author : Kawe Mazidjatari -Purpose: Implements the 'rtech_game' core utilities -------------------------------------------------------------------------------- -History: -- 18:07:2021 | 13:02 : Created by Kawe Mazidjatari -- 10:09:2021 | 18:22 : Implement 'StringToGuid' method -- 12:11:2021 | 14:41 : Add decompression method to ConCommand callback -- 25:12:2021 | 23:20 : Made everything more readable thanks to bezdna5-rs -- 28:03:2022 | 18:00 : Added getting pak info by PakID. - -******************************************************************************/ - -//----------------------------------------------------------------------------- -// Purpose: calculate 'GUID' from input data -//----------------------------------------------------------------------------- -uint64_t __fastcall RTech::StringToGuid(const char* pData) -{ - uint32_t* v1; // r8 - uint64_t v2; // r10 - int32_t v3; // er11 - uint32_t v4; // er9 - uint32_t i; // edx - uint64_t v6; // rcx - int32_t v7; // er9 - int32_t v8; // edx - int32_t v9; // eax - uint32_t v10; // er8 - int32_t v12; // ecx - uint32_t* a1 = (uint32_t*)pData; - - v1 = a1; - v2 = 0i64; - v3 = 0; - v4 = (*a1 - 45 * ((~(*a1 ^ 0x5C5C5C5Cu) >> 7) & (((*a1 ^ 0x5C5C5C5Cu) - 0x1010101) >> 7) & 0x1010101)) & 0xDFDFDFDF; - for (i = ~*a1 & (*a1 - 0x1010101) & 0x80808080; !i; i = v8 & 0x80808080) - { - v6 = v4; - v7 = v1[1]; - ++v1; - v3 += 4; - v2 = ((((uint64_t)(0xFB8C4D96501i64 * v6) >> 24) + 0x633D5F1 * v2) >> 61) ^ (((uint64_t)(0xFB8C4D96501i64 * v6) >> 24) - + 0x633D5F1 * v2); - v8 = ~v7 & (v7 - 0x1010101); - v4 = (v7 - 45 * ((~(v7 ^ 0x5C5C5C5Cu) >> 7) & (((v7 ^ 0x5C5C5C5Cu) - 0x1010101) >> 7) & 0x1010101)) & 0xDFDFDFDF; - } - v9 = -1; - v10 = (i & -(signed)i) - 1; - if (_BitScanReverse((unsigned long*)&v12, v10)) - { - v9 = v12; - } - return 0x633D5F1 * v2 + ((0xFB8C4D96501i64 * (uint64_t)(v4 & v10)) >> 24) - 0xAE502812AA7333i64 * (uint32_t)(v3 + v9 / 8); -} - -//----------------------------------------------------------------------------- -// Purpose: calculate 'decompressed' size and commit parameters -//----------------------------------------------------------------------------- -uint64_t __fastcall RTech::DecompressPakFileInit(RPakDecompState_t* state, uint8_t* fileBuffer, uint64_t fileSize, uint64_t offNoHeader, uint64_t headerSize) -{ - int64_t input_byte_pos_init; // r9 - uint64_t byte_init; // r11 - int32_t decompressed_size_bits; // ecx - int64_t byte_1_low; // rdi - uint64_t input_byte_pos_1; // r10 - uint32_t bit_pos_final; // ebp - uint64_t byte_1; // rdi - uint32_t brih_bits; // er11 - uint64_t inv_mask_in; // r8 - uint64_t byte_final_full; // rbx - uint64_t bit_pos_final_1; // rax - int32_t byte_bit_offset_final; // ebp - uint64_t input_byte_pos_final; // r10 - uint64_t byte_final; // rbx - uint32_t brih_bytes; // er11 - uint64_t byte_tmp; // rdx - uint64_t stream_len_needed; // r14 - uint64_t result; // rax - uint64_t inv_mask_out; // r8 - uint64_t qw70; // rcx - uint64_t stream_compressed_size_new; // rdx - - const uintptr_t mask = UINT64_MAX; - const uintptr_t file_buf = uintptr_t(fileBuffer); - - state->m_nInputBuf = file_buf; - state->m_nOut = 0i64; - state->m_nOutMask = 0i64; - state->dword44 = 0; - state->m_nTotalFileLen = fileSize + offNoHeader; - state->m_nMask = mask; - input_byte_pos_init = offNoHeader + headerSize + 8; - byte_init = *(uint64_t*)((mask & (offNoHeader + headerSize)) + file_buf); - state->m_nDecompPosition = headerSize; - decompressed_size_bits = byte_init & 0x3F; - byte_init >>= 6; - state->m_nInputBytePos = input_byte_pos_init; - state->m_nDecompSize = (byte_init & ((1i64 << decompressed_size_bits) - 1)) | (1i64 << decompressed_size_bits); - byte_1_low = *(uint64_t*)((mask & input_byte_pos_init) + file_buf) << (64 - - ((uint8_t)decompressed_size_bits - + 6)); - input_byte_pos_1 = input_byte_pos_init + ((uint64_t)(uint32_t)(decompressed_size_bits + 6) >> 3); - state->m_nInputBytePos = input_byte_pos_1; - bit_pos_final = ((decompressed_size_bits + 6) & 7) + 13; - byte_1 = (0xFFFFFFFFFFFFFFFFui64 >> ((decompressed_size_bits + 6) & 7)) & ((byte_init >> decompressed_size_bits) | byte_1_low); - brih_bits = (((uint8_t)byte_1 - 1) & 0x3F) + 1; - inv_mask_in = 0xFFFFFFFFFFFFFFFFui64 >> (64 - (uint8_t)brih_bits); - state->m_nInvMaskIn = inv_mask_in; - state->m_nInvMaskOut = 0xFFFFFFFFFFFFFFFFui64 >> (63 - (((byte_1 >> 6) - 1) & 0x3F)); - byte_final_full = (byte_1 >> 13) | (*(uint64_t*)((mask & input_byte_pos_1) + file_buf) << (64 - - (uint8_t)bit_pos_final)); - bit_pos_final_1 = bit_pos_final; - byte_bit_offset_final = bit_pos_final & 7; - input_byte_pos_final = (bit_pos_final_1 >> 3) + input_byte_pos_1; - byte_final = (0xFFFFFFFFFFFFFFFFui64 >> byte_bit_offset_final) & byte_final_full; - state->m_nInputBytePos = input_byte_pos_final; - if (inv_mask_in == -1i64) - { - state->m_nHeaderOffset = 0; - stream_len_needed = fileSize; - } - else - { - brih_bytes = brih_bits >> 3; - state->m_nHeaderOffset = brih_bytes + 1; - byte_tmp = *(uint64_t*)((mask & input_byte_pos_final) + file_buf); - state->m_nInputBytePos = input_byte_pos_final + brih_bytes + 1; - stream_len_needed = byte_tmp & ((1i64 << (8 * ((uint8_t)brih_bytes + 1))) - 1); - } - result = state->m_nDecompSize; - inv_mask_out = state->m_nInvMaskOut; - qw70 = offNoHeader + state->m_nInvMaskIn - 6i64; - state->m_nLengthNeeded = stream_len_needed + offNoHeader; - state->qword70 = qw70; - state->byte = byte_final; - state->m_nByteBitOffset = byte_bit_offset_final; - state->dword6C = 0; - state->m_nCompressedStreamSize = stream_len_needed + offNoHeader; - state->m_nDecompStreamSize = result; - if (result - 1 > inv_mask_out) - { - stream_compressed_size_new = stream_len_needed + offNoHeader - state->m_nHeaderOffset; - state->m_nDecompStreamSize = inv_mask_out + 1; - state->m_nCompressedStreamSize = stream_compressed_size_new; - } - - return result; -} - -static const unsigned char /*unk_141313180*/ s_PakFileCompressionLUT[0x720] = -{ - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0B, - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF3, - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0E, - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x09, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF1, - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0D, - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0xF7, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF2, - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF9, 0x04, 0xFD, 0xFC, 0x07, 0x04, 0x05, 0xFF, 0xF4, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF6, 0x04, 0xFD, 0xFC, 0xFB, 0x04, 0x06, 0xFF, 0x0F, - 0x04, 0xFE, 0xFC, 0x08, 0x04, 0xEF, 0x11, 0xF8, 0x04, 0xFD, 0xFC, 0x0C, 0x04, 0x05, 0xFF, 0x0A, - 0x04, 0xFE, 0xFC, 0x10, 0x04, 0xEF, 0x11, 0xF5, 0x04, 0xFD, 0xFC, 0xFA, 0x04, 0x06, 0xFF, 0xF0, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0E, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0B, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x10, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0C, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x09, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0F, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x11, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0D, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x07, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x0A, - 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0x08, 0x04, 0x05, 0x04, 0x06, 0x04, 0x05, 0x04, 0xFF, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x07, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x06, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x02, 0x04, 0x03, 0x05, 0x02, 0x04, 0x04, 0x06, 0x02, 0x04, 0x03, 0x06, 0x02, 0x05, 0x04, 0x08, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x06, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x07, - 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x03, 0x01, 0x02, 0x01, 0x08, - 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x08, 0x00, 0x0B, - 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x09, 0x00, 0x08, 0x00, 0x03, 0x00, 0x08, 0x00, 0x0E, - 0x00, 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x07, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x0D, - 0x00, 0x08, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x05, 0x00, 0x08, 0x00, 0x0F, - 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, - 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, - 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, - 0x01, 0x02, 0x01, 0x05, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, 0x01, 0x02, 0x01, 0x06, - 0x4A, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, - 0xCA, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, - 0x4A, 0x01, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, 0xAA, 0x01, 0x00, 0x00, - 0xAA, 0x03, 0x00, 0x00, 0xAA, 0x05, 0x00, 0x00, 0xAA, 0x25, 0x00, 0x00, 0xAA, 0x25, 0x02, 0x00, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x09, 0x09, 0x0D, 0x11, 0x15, - 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x2A, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, - 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, - 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, - 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, - 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, - 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, - 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, - 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xF1, 0x1D, 0xC1, 0xF6, 0x7F, 0x00, 0x00, - 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, - 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3F, - 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, 0x02, 0x61, 0x4D, 0xB9, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, 0xC2, 0x14, 0xCF, 0x37, - 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, 0x9E, 0x4B, 0x6F, 0xB0, - 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, 0x22, 0x0B, 0xB6, 0xBA, - 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, 0x00, 0x70, 0x95, 0xB6, - 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, 0xA9, 0xAA, 0x2A, 0x3D, - 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, 0x00, 0x00, 0x80, 0x3F, - 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBF, - 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, 0xA8, 0xAA, 0x2A, 0xBE, - 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, 0xD2, 0x85, 0x08, 0x3C, - 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, 0x83, 0xF9, 0x22, 0x3F, - 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, 0x00, 0x10, 0xC9, 0x3F, - 0x4C, 0x39, 0x56, 0x75, 0x42, 0x52, 0x65, 0x75, 0x70, 0x35, 0x31, 0x77, 0x4C, 0x51, 0x64, 0x61, -}; - -//----------------------------------------------------------------------------- -// Purpose: decompress input data -//----------------------------------------------------------------------------- -uint8_t __fastcall RTech::DecompressPakFile(RPakDecompState_t* state, uint64_t inLen, uint64_t outLen) -{ - char result; // al - uint64_t v5; // r15 - uint64_t v6; // r11 - uint32_t v7; // ebp - uint64_t v8; // rsi - uint64_t v9; // rdi - uint64_t v10; // r12 - uint64_t v11; // r13 - uint32_t v12; // ecx - uint64_t v13; // rsi - uint64_t i; // rax - uint64_t v15; // r8 - int64_t v16; // r9 - int v17; // ecx - uint64_t v18; // rax - uint64_t v19; // rsi - int64_t v20; // r14 - int v21; // ecx - uint64_t v22; // r11 - int v23; // edx - uint64_t v24; // rax - int v25; // er8 - uint32_t v26; // er13 - uint64_t v27; // r10 - uint64_t v28; // rax - _QWORD* v29; // r10 - uint64_t v30; // r9 - uint64_t v31; // r10 - uint64_t v32; // r8 - uint64_t v33; // rax - uint64_t v34; // rax - uint64_t v35; // rax - uint64_t v36; // rcx - int64_t v37; // rdx - uint64_t v38; // r14 - uint64_t v39; // r11 - char v40; // cl - uint64_t v41; // rsi - int64_t v42; // rcx - uint64_t v43; // r8 - int v44; // er11 - uint8_t v45; // r9 - uint64_t v46; // rcx - uint64_t v47; // rcx - int64_t v48; // r9 - int64_t l; // r8 - uint32_t v50; // er9 - int64_t v51; // r8 - int64_t v52; // rdx - int64_t k; // r8 - char* v54; // r10 - int64_t v55; // rdx - uint32_t v56; // er14 - int64_t* v57; // rdx - int64_t* v58; // r8 - char v59; // al - uint64_t v60; // rsi - int64_t v61; // rax - uint64_t v62; // r9 - int v63; // er10 - uint8_t v64; // cl - uint64_t v65; // rax - uint32_t v66; // er14 - uint32_t j; // ecx - int64_t v68; // rax - uint64_t v69; // rcx - uint64_t v70; // [rsp+0h] [rbp-58h] - uint32_t v71; // [rsp+60h] [rbp+8h] - uint64_t v74; // [rsp+78h] [rbp+20h] - - if (inLen < state->m_nLengthNeeded) - return 0; - v5 = state->m_nDecompPosition; - if (outLen < state->m_nInvMaskOut + (v5 & ~state->m_nInvMaskOut) + 1 && outLen < state->m_nDecompSize) - return 0; - v6 = state->m_nOut; - v7 = state->m_nByteBitOffset; - v8 = state->byte; - v9 = state->m_nInputBytePos; - v10 = state->qword70; - v11 = state->m_nInputBuf; - if (state->m_nCompressedStreamSize < v10) - v10 = state->m_nCompressedStreamSize; - v12 = state->dword6C; - v74 = v11; - v70 = v6; - v71 = v12; - if (!v7) - goto LABEL_11; - v13 = (*(_QWORD*)((v9 & state->m_nMask) + v11) << (64 - (unsigned __int8)v7)) | v8; - for (i = v7; ; i = v7) - { - v7 &= 7u; - v9 += i >> 3; - v12 = v71; - v8 = (0xFFFFFFFFFFFFFFFFui64 >> v7) & v13; - LABEL_11: - v15 = (unsigned __int64)v12 << 8; - v16 = v12; - v17 = *((unsigned __int8*)&s_PakFileCompressionLUT + (unsigned __int8)v8 + v15 + 512); - v18 = (unsigned __int8)v8 + v15; - v7 += v17; - v19 = v8 >> v17; - v20 = (unsigned int)*((char*)&s_PakFileCompressionLUT + v18); - if (*((char*)&s_PakFileCompressionLUT + v18) < 0) - { - v56 = -(int)v20; - v57 = (__int64*)(v11 + (v9 & state->m_nMask)); - v71 = 1; - v58 = (__int64*)(v6 + (v5 & state->m_nOutMask)); - if (v56 == *((unsigned __int8*)&s_PakFileCompressionLUT + v16 + 1248)) - { - if ((~v9 & state->m_nInvMaskIn) < 0xF || (state->m_nInvMaskOut & ~v5) < 15 || state->m_nDecompSize - v5 < 0x10) - v56 = 1; - v59 = char(v19); - v60 = v19 >> 3; - v61 = v59 & 7; - v62 = v60; - if (v61) - { - v63 = *((unsigned __int8*)&s_PakFileCompressionLUT + v61 + 1232); - v64 = *((_BYTE*)&s_PakFileCompressionLUT + v61 + 1240); - } - else - { - v62 = v60 >> 4; - v65 = v60 & 0xF; - v7 += 4; - v63 = *((_DWORD*)&s_PakFileCompressionLUT + v65 + 288); - v64 = *((_BYTE*)&s_PakFileCompressionLUT + v65 + 1216); - } - v7 += v64 + 3; - v19 = v62 >> v64; - v66 = v63 + (v62 & ((1 << v64) - 1)) + v56; - for (j = v66 >> 3; j; --j) - { - v68 = *v57++; - *v58++ = v68; - } - if ((v66 & 4) != 0) - { - *(_DWORD*)v58 = *(_DWORD*)v57; - v58 = (__int64*)((char*)v58 + 4); - v57 = (__int64*)((char*)v57 + 4); - } - if ((v66 & 2) != 0) - { - *(_WORD*)v58 = *(_WORD*)v57; - v58 = (__int64*)((char*)v58 + 2); - v57 = (__int64*)((char*)v57 + 2); - } - if ((v66 & 1) != 0) - *(_BYTE*)v58 = *(_BYTE*)v57; - v9 += v66; - v5 += v66; - } - else - { - *v58 = *v57; - v58[1] = v57[1]; - v9 += v56; - v5 += v56; - } - } - else - { - v21 = v19 & 0xF; - v71 = 0; - v22 = ((unsigned __int64)(unsigned int)v19 >> (((unsigned int)(v21 - 31) >> 3) & 6)) & 0x3F; - v23 = 1 << (v21 + ((v19 >> 4) & ((24 * (((unsigned int)(v21 - 31) >> 3) & 2)) >> 4))); - v7 += (((unsigned int)(v21 - 31) >> 3) & 6) + *((unsigned __int8*)&s_PakFileCompressionLUT + v22 + 1088) + v21 + ((v19 >> 4) & ((24 * (((unsigned int)(v21 - 31) >> 3) & 2)) >> 4)); - v24 = state->m_nOutMask; - v25 = 16 * (v23 + ((v23 - 1) & (v19 >> ((((unsigned int)(v21 - 31) >> 3) & 6) + *((_BYTE*)&s_PakFileCompressionLUT + v22 + 1088))))); - v19 >>= (((unsigned int)(v21 - 31) >> 3) & 6) + *((_BYTE*)&s_PakFileCompressionLUT + v22 + 1088) + v21 + ((v19 >> 4) & ((24 * (((unsigned int)(v21 - 31) >> 3) & 2)) >> 4)); - v26 = v25 + *((unsigned __int8*)&s_PakFileCompressionLUT + v22 + 1024) - 16; - v27 = v24 & (v5 - v26); - v28 = v70 + (v5 & v24); - v29 = (_QWORD*)(v70 + v27); - if ((_DWORD)v20 == 17) - { - v40 = char(v19); - v41 = v19 >> 3; - v42 = v40 & 7; - v43 = v41; - if (v42) - { - v44 = *((unsigned __int8*)&s_PakFileCompressionLUT + v42 + 1232); - v45 = *((_BYTE*)&s_PakFileCompressionLUT + v42 + 1240); - } - else - { - v7 += 4; - v46 = v41 & 0xF; - v43 = v41 >> 4; - v44 = *((_DWORD*)&s_PakFileCompressionLUT + v46 + 288); - v45 = *((_BYTE*)&s_PakFileCompressionLUT + v46 + 1216); - if (v74 && v7 + v45 >= 61) - { - v47 = v9++ & state->m_nMask; - v43 |= (unsigned __int64)*(unsigned __int8*)(v47 + v74) << (61 - (unsigned __int8)v7); - v7 -= 8; - } - } - v7 += v45 + 3; - v19 = v43 >> v45; - v48 = ((unsigned int)v43 & ((1 << v45) - 1)) + v44 + 17; - v5 += v48; - if (v26 < 8) - { - v50 = uint32_t(v48 - 13); - v5 -= 13i64; - if (v26 == 1) - { - v51 = *(unsigned __int8*)v29; - //++dword_14D40B2BC; - v52 = 0i64; - for (k = 0x101010101010101i64 * v51; (unsigned int)v52 < v50; v52 = (unsigned int)(v52 + 8)) - *(_QWORD*)(v52 + v28) = k; - } - else - { - //++dword_14D40B2B8; - if (v50) - { - v54 = (char*)v29 - v28; - v55 = v50; - do - { - *(_BYTE*)v28 = v54[v28]; - ++v28; - --v55; - } while (v55); - } - } - } - else - { - //++dword_14D40B2AC; - for (l = 0i64; (unsigned int)l < (unsigned int)v48; l = (unsigned int)(l + 8)) - *(_QWORD*)(l + v28) = *(_QWORD*)((char*)v29 + l); - } - } - else - { - v5 += v20; - *(_QWORD*)v28 = *v29; - *(_QWORD*)(v28 + 8) = v29[1]; - } - v11 = v74; - } - if (v9 >= v10) - break; - LABEL_29: - v6 = v70; - v13 = (*(_QWORD*)((v9 & state->m_nMask) + v11) << (64 - (unsigned __int8)v7)) | v19; - } - if (v5 != state->m_nDecompStreamSize) - goto LABEL_25; - v30 = state->m_nDecompSize; - if (v5 == v30) - { - result = 1; - goto LABEL_69; - } - v31 = state->m_nInvMaskIn; - v32 = state->m_nHeaderOffset; - v33 = v31 & -(__int64)v9; - v19 >>= 1; - ++v7; - if (v32 > v33) - { - v9 += v33; - v34 = state->qword70; - if (v9 > v34) - state->qword70 = v31 + v34 + 1; - } - v35 = v9 & state->m_nMask; - v9 += v32; - v36 = v5 + state->m_nInvMaskOut + 1; - v37 = *(_QWORD*)(v35 + v11) & ((1i64 << (8 * (unsigned __int8)v32)) - 1); - v38 = v37 + state->m_nLengthNeeded; - v39 = v37 + state->m_nCompressedStreamSize; - state->m_nLengthNeeded = v38; - state->m_nCompressedStreamSize = v39; - if (v36 >= v30) - { - v36 = v30; - state->m_nCompressedStreamSize = v32 + v39; - } - state->m_nDecompStreamSize = v36; - if (inLen >= v38 && outLen >= v36) - { - LABEL_25: - v10 = state->qword70; - if (v9 >= v10) - { - v9 = ~state->m_nInvMaskIn & (v9 + 7); - v10 += state->m_nInvMaskIn + 1; - state->qword70 = v10; - } - if (state->m_nCompressedStreamSize < v10) - v10 = state->m_nCompressedStreamSize; - goto LABEL_29; - } - v69 = state->qword70; - if (v9 >= v69) - { - v9 = ~v31 & (v9 + 7); - state->qword70 = v69 + v31 + 1; - } - state->dword6C = v71; - result = 0; - state->byte = v19; - state->m_nByteBitOffset = v7; -LABEL_69: - state->m_nDecompPosition = v5; - state->m_nInputBytePos = v9; - return result; -} - -//---------------------------------------------------------------------------------- -// Purpose: start loading shader sets, assign vftable pointer -//---------------------------------------------------------------------------------- -void** RTech::LoadShaderSet(void** VTablePtr) -{ - *VTablePtr = &g_pShaderGlueVFTable; - return &g_pShaderGlueVFTable; -} - -//---------------------------------------------------------------------------------- -// Purpose: open a file and add it to m_FileHandles. -//---------------------------------------------------------------------------------- -int32_t RTech::OpenFile(const CHAR* szFilePath, void* unused, LONGLONG* fileSizeOut) -{ - const CHAR* szFileToLoad = szFilePath; - CUtlString pakBasePath(szFilePath); - - if (pakBasePath.Find(PLATFORM_PAK_PATH) != -1) - { - pakBasePath = pakBasePath.Replace(PLATFORM_PAK_PATH, PLATFORM_PAK_OVERRIDE_PATH); - - if (FileExists(pakBasePath.Get())) - { - szFileToLoad = pakBasePath.Get(); - } - } - - const HANDLE hFile = CreateFileA(szFileToLoad, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_SUPPORTS_GHOSTING, 0); - - if (hFile == INVALID_HANDLE_VALUE) - return -1; - - if (rtech_debug->GetBool()) - DevMsg(eDLL_T::RTECH, "Opened file: '%s'\n", szFileToLoad); - - if (fileSizeOut) - { - LARGE_INTEGER fileSize{}; - if (GetFileSizeEx(hFile, &fileSize)) - *fileSizeOut = fileSize.QuadPart; - } - - AcquireSRWLockExclusive(reinterpret_cast(&*s_pFileArrayMutex)); - const int32_t fileIdx = RTech_FindFreeSlotInFiles(s_pFileArray); - ReleaseSRWLockExclusive(reinterpret_cast(&*s_pFileArrayMutex)); - - const int32_t fileHandleIdx = (fileIdx & 0x3FF); // Something with ArraySize. - - s_pFileHandles->self[fileHandleIdx].m_nFileNumber = fileIdx; - s_pFileHandles->self[fileHandleIdx].m_hFileHandle = hFile; - s_pFileHandles->self[fileHandleIdx].m_nCurOfs = 1; - - return fileIdx; -} - -//----------------------------------------------------------------------------- -// Purpose: gets information about loaded pak file via pak ID -//----------------------------------------------------------------------------- -RPakLoadedInfo_t* RTech::GetPakLoadedInfo(RPakHandle_t nHandle) -{ - for (int16_t i = 0; i < *g_pLoadedPakCount; ++i) - { - RPakLoadedInfo_t* info = &g_pLoadedPakInfo[i]; - if (!info) - continue; - - if (info->m_nHandle != nHandle) - continue; - - return info; - } - - Warning(eDLL_T::RTECH, "%s - Failed to retrieve pak info for handle '%d'\n", __FUNCTION__, nHandle); - return nullptr; -} - -//----------------------------------------------------------------------------- -// Purpose: gets information about loaded pak file via pak name -//----------------------------------------------------------------------------- -RPakLoadedInfo_t* RTech::GetPakLoadedInfo(const char* szPakName) -{ - for (int16_t i = 0; i < *g_pLoadedPakCount; ++i) - { - RPakLoadedInfo_t* info = &g_pLoadedPakInfo[i]; - if (!info) - continue; - - if (!info->m_pszFileName || !*info->m_pszFileName) - continue; - - if (strcmp(szPakName, info->m_pszFileName) != 0) - continue; - - return info; - } - - Warning(eDLL_T::RTECH, "%s - Failed to retrieve pak info for name '%s'\n", __FUNCTION__, szPakName); - return nullptr; -} - -//----------------------------------------------------------------------------- -// Purpose: returns pak status as string -//----------------------------------------------------------------------------- -const char* RTech::PakStatusToString(RPakStatus_t status) -{ - switch (status) - { - case RPakStatus_t::PAK_STATUS_FREED: return "PAK_STATUS_FREED"; - case RPakStatus_t::PAK_STATUS_LOAD_PENDING: return "PAK_STATUS_LOAD_PENDING"; - case RPakStatus_t::PAK_STATUS_REPAK_RUNNING: return "PAK_STATUS_REPAK_RUNNING"; - case RPakStatus_t::PAK_STATUS_REPAK_DONE: return "PAK_STATUS_REPAK_DONE"; - case RPakStatus_t::PAK_STATUS_LOAD_STARTING: return "PAK_STATUS_LOAD_STARTING"; - case RPakStatus_t::PAK_STATUS_LOAD_PAKHDR: return "PAK_STATUS_LOAD_PAKHDR"; - case RPakStatus_t::PAK_STATUS_LOAD_PATCH_INIT: return "PAK_STATUS_LOAD_PATCH_INIT"; - case RPakStatus_t::PAK_STATUS_LOAD_PATCH_EDIT_STREAM: return "PAK_STATUS_LOAD_PATCH_EDIT_STREAM"; - case RPakStatus_t::PAK_STATUS_LOAD_ASSETS: return "PAK_STATUS_LOAD_ASSETS"; - case RPakStatus_t::PAK_STATUS_LOADED: return "PAK_STATUS_LOADED"; - case RPakStatus_t::PAK_STATUS_UNLOAD_PENDING: return "PAK_STATUS_UNLOAD_PENDING"; - case RPakStatus_t::PAK_STATUS_FREE_PENDING: return "PAK_STATUS_FREE_PENDING"; - case RPakStatus_t::PAK_STATUS_CANCELING: return "PAK_STATUS_CANCELING"; - case RPakStatus_t::PAK_STATUS_ERROR: return "PAK_STATUS_ERROR"; - case RPakStatus_t::PAK_STATUS_INVALID_PAKHANDLE: return "PAK_STATUS_INVALID_PAKHANDLE"; - case RPakStatus_t::PAK_STATUS_BUSY: return "PAK_STATUS_BUSY"; - default: return "PAK_STATUS_UNKNOWN"; - } -} -#ifdef GAMEDLL_S3 -//----------------------------------------------------------------------------- -// Purpose: process guid relations for asset -//----------------------------------------------------------------------------- -void RTech::PakProcessGuidRelationsForAsset(PakFile_t* pPak, RPakAssetEntry_t* pAsset) -{ -#if defined (GAMEDLL_S0) && defined (GAMEDLL_S1) && defined (GAMEDLL_S2) - static const int GLOBAL_MUL = 0x1D; -#else - static const int GLOBAL_MUL = 0x17; -#endif - - RPakDescriptor_t* pGuidDescriptors = &pPak->m_pGuidDescriptors[pAsset->m_nUsesStartIdx]; - volatile uint32_t* v5 = reinterpret_cast(*(reinterpret_cast(g_pPakGlobals) + GLOBAL_MUL * (pPak->qword578 & 0x1FF) + 0x160212)); - const bool bDebug = rtech_debug->GetBool(); - - if (bDebug) - DevMsg(eDLL_T::RTECH, "Processing GUID relations for asset '0x%-16llX' in pak '%-32s'. Uses: %-4i\n", pAsset->m_Guid, pPak->m_pszFileName, pAsset->m_nUsesCount); - - for (uint32_t i = 0; i < pAsset->m_nUsesCount; i++) - { - void** pCurrentGuid = reinterpret_cast(pPak->m_ppPagePointers[pGuidDescriptors[i].m_Index] + pGuidDescriptors[i].m_Offset); - - // Get current guid. - const uint64_t currentGuid = reinterpret_cast(*pCurrentGuid); - - // Get asset index. - int assetIdx = currentGuid & 0x3FFFF; - uint64_t assetIdxEntryGuid = g_pPakGlobals->m_Assets[assetIdx].m_Guid; - - const int64_t v9 = 2i64 * InterlockedExchangeAdd(v5, 1u); - *reinterpret_cast(const_cast(&v5[2 * v9 + 2])) = currentGuid; - *reinterpret_cast(const_cast(&v5[2 * v9 + 4])) = pAsset->m_Guid; - - std::function fnCheckAsset = [&](bool shouldCheckTwo) - { - while (true) - { - if (shouldCheckTwo && assetIdxEntryGuid == 2) - { - if (pPak->m_PakHdr.m_nAssetEntryCount) - return false; - } - - assetIdx++; - - // Check if we have a deadlock and report it if we have rtech_debug enabled. - if (bDebug && assetIdx >= 0x40000) - { - Warning(eDLL_T::RTECH, "Possible deadlock detected while processing asset '0x%-16llX' in pak '%-32s'. Uses: %-4i | assetIdxEntryGuid: '0x%-16llX' | currentGuid: '0x%-16llX'\n", pAsset->m_Guid, pPak->m_pszFileName, pAsset->m_nUsesCount, assetIdxEntryGuid, currentGuid); - if (IsDebuggerPresent()) - DebugBreak(); - } - - assetIdx &= 0x3FFFF; - assetIdxEntryGuid = g_pPakGlobals->m_Assets[assetIdx].m_Guid; - - if (assetIdxEntryGuid == currentGuid) - return true; - } - }; - - if (assetIdxEntryGuid != currentGuid) - { - // Are we some special asset with the guid 2? - if (!fnCheckAsset(true)) - { - RPakAssetEntry_t* assetEntries = pPak->m_pAssetEntries; - uint64_t a = 0; - - for (; assetEntries->m_Guid != currentGuid; a++, assetEntries++) - { - if (a >= pPak->m_PakHdr.m_nAssetEntryCount) - { - fnCheckAsset(false); - break; - } - } - - assetIdx = pPak->qword580[a]; - } - } - - // Finally write the pointer to the guid entry. - *pCurrentGuid = g_pPakGlobals->m_Assets[assetIdx].m_pHead; - } -} -#endif // GAMEDLL_S3 -void V_RTechUtils::Attach() const -{ - DetourAttach(&RTech_OpenFile, &RTech::OpenFile); - -#ifdef GAMEDLL_S3 - DetourAttach(&RTech_Pak_ProcessGuidRelationsForAsset, &RTech::PakProcessGuidRelationsForAsset); -#endif -} - -void V_RTechUtils::Detach() const -{ - DetourDetach(&RTech_OpenFile, &RTech::OpenFile); - -#ifdef GAMEDLL_S3 - DetourDetach(&RTech_Pak_ProcessGuidRelationsForAsset, &RTech::PakProcessGuidRelationsForAsset); -#endif -} - -/////////////////////////////////////////////////////////////////////////////// -RTech* g_pRTech = new RTech(); diff --git a/r5dev/rtech/rtech_utils.h b/r5dev/rtech/rtech_utils.h deleted file mode 100644 index 9ba79f0a..00000000 --- a/r5dev/rtech/rtech_utils.h +++ /dev/null @@ -1,126 +0,0 @@ -#pragma once -#include "tier0/jobthread.h" -#include "vpklib/packedstore.h" -#include "rtech/rtech_game.h" -#include "public/rendersystem/schema/texture.g.h" -#include "public/rtech/ipakfile.h" - -/* ==== RTECH =========================================================================================================================================================== */ -// [ PIXIE ]: I'm very unsure about this, but it really seems like it -inline CMemory p_RTech_FindFreeSlotInFiles; -inline int32_t(*RTech_FindFreeSlotInFiles)(int32_t*); - -inline CMemory p_RTech_OpenFile; -inline int32_t(*RTech_OpenFile)(const char*, void*, int64_t*); - -inline CMemory p_RTech_RegisterAsset; -inline void(*RTech_RegisterAsset)(int, int, const char*, void*, void*, void*, void*, int, int, uint32_t, int, int); - -#ifdef GAMEDLL_S3 -inline CMemory p_Pak_ProcessGuidRelationsForAsset; -inline void(*RTech_Pak_ProcessGuidRelationsForAsset)(PakFile_t*, RPakAssetEntry_t*); -#endif - -inline CMemory p_StreamDB_Init; -inline void(*v_StreamDB_Init)(const char* pszLevelName); - -inline RPakLoadedInfo_t* g_pLoadedPakInfo; -inline int16_t* g_pRequestedPakCount; -inline int16_t* g_pLoadedPakCount; -inline JobID_t* g_pPakLoadJobID; -inline RPakGlobals_t* g_pPakGlobals; - -inline int32_t* s_pFileArray; -inline PSRWLOCK* s_pFileArrayMutex; -inline pFileHandleTracker_t* s_pFileHandles; - -inline JobFifoLock_s* g_pPakFifoLock; -inline void* g_pPakFifoLockWrapper; // Pointer to functor that takes the global pak fifolock as argument. -inline bool* g_bPakFifoLockAcquired; - -class RTech -{ -public: - uint64_t __fastcall StringToGuid(const char* pData); - uint8_t __fastcall DecompressPakFile(RPakDecompState_t* state, uint64_t inLen, uint64_t outLen); - uint64_t __fastcall DecompressPakFileInit(RPakDecompState_t* state, uint8_t* fileBuffer, uint64_t fileSize, uint64_t offNoHeader, uint64_t headerSize); - RPakLoadedInfo_t* GetPakLoadedInfo(RPakHandle_t nPakId); - RPakLoadedInfo_t* GetPakLoadedInfo(const char* szPakName); - const char* PakStatusToString(RPakStatus_t status); - - static int32_t OpenFile(const CHAR* szFilePath, void* unused, LONGLONG* fileSizeOut); -#ifdef GAMEDLL_S3 - static void PakProcessGuidRelationsForAsset(PakFile_t* pak, RPakAssetEntry_t* asset); -#endif // GAMEDLL_S3 - - void** LoadShaderSet(void** VTablePtr); -}; - -/////////////////////////////////////////////////////////////////////////////// -extern RTech* g_pRTech; - -/////////////////////////////////////////////////////////////////////////////// -class V_RTechUtils : public IDetour -{ - virtual void GetAdr(void) const - { - LogFunAdr("RTech::FindFreeSlotInFiles", p_RTech_FindFreeSlotInFiles.GetPtr()); - LogFunAdr("RTech::OpenFile", p_RTech_OpenFile.GetPtr()); - - LogFunAdr("StreamDB_Init", p_StreamDB_Init.GetPtr()); - LogVarAdr("s_FileArray", reinterpret_cast(s_pFileArray)); - LogVarAdr("s_FileArrayMutex", reinterpret_cast(s_pFileArrayMutex)); - LogVarAdr("s_FileHandles", reinterpret_cast(s_pFileHandles)); - - LogVarAdr("g_loadedPakInfo", reinterpret_cast(g_pLoadedPakInfo)); - LogVarAdr("g_loadedPakCount", reinterpret_cast(g_pLoadedPakCount)); - LogVarAdr("g_requestedPakCount", reinterpret_cast(g_pRequestedPakCount)); - - LogVarAdr("g_pakGlobals", reinterpret_cast(g_pPakGlobals)); - LogVarAdr("g_pakLoadJobID", reinterpret_cast(g_pPakLoadJobID)); - - LogVarAdr("g_pakFifoLock", reinterpret_cast(g_pPakFifoLock)); - LogVarAdr("g_pakFifoLockWrapper", reinterpret_cast(g_pPakFifoLockWrapper)); - LogVarAdr("g_pakFifoLockAcquired", reinterpret_cast(g_bPakFifoLockAcquired)); - } - virtual void GetFun(void) const - { - p_StreamDB_Init = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 54 41 56 41 57 48 83 EC 40 48 8B E9"); - v_StreamDB_Init = p_StreamDB_Init.RCast(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 54 41 56 41 57 48 83 EC 40 48 8B E9*/ - - p_RTech_FindFreeSlotInFiles = g_GameDll.FindPatternSIMD("44 8B 51 0C 4C 8B C1"); - RTech_FindFreeSlotInFiles = p_RTech_FindFreeSlotInFiles.RCast(); /*44 8B 51 0C 4C 8B C1*/ - - p_RTech_OpenFile = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 89 85 08 01 ?? ??").FollowNearCallSelf(); - RTech_OpenFile = p_RTech_OpenFile.RCast(); /*E8 ? ? ? ? 89 85 08 01 00 00*/ - - p_RTech_RegisterAsset = g_GameDll.FindPatternSIMD("4D 89 42 08").FindPatternSelf("48 89 6C", CMemory::Direction::UP); - RTech_RegisterAsset = p_RTech_RegisterAsset.RCast(); /*4D 89 42 08*/ - -#ifdef GAMEDLL_S3 - p_Pak_ProcessGuidRelationsForAsset = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 48 8B 86 ?? ?? ?? ?? 42 8B 0C B0").FollowNearCallSelf(); - RTech_Pak_ProcessGuidRelationsForAsset = p_Pak_ProcessGuidRelationsForAsset.RCast(); /*E8 ? ? ? ? 48 8B 86 ? ? ? ? 42 8B 0C B0*/ -#endif - } - virtual void GetVar(void) const - { - s_pFileArray = p_StreamDB_Init.Offset(0x70).FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 512, 2).ResolveRelativeAddress(0x3, 0x7).RCast(); - s_pFileHandles = p_StreamDB_Init.Offset(0x70).FindPatternSelf("4C 8D", CMemory::Direction::DOWN, 512, 1).ResolveRelativeAddress(0x3, 0x7).RCast(); - s_pFileArrayMutex = p_StreamDB_Init.Offset(0x70).FindPatternSelf("48 8D 0D", CMemory::Direction::DOWN, 512, 1).ResolveRelativeAddress(0x3, 0x7).RCast(); - - g_pLoadedPakInfo = p_CPakFile_UnloadPak.FindPattern("48 8D 05", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pRequestedPakCount = p_CPakFile_UnloadPak.FindPattern("66 89", CMemory::Direction::DOWN, 450).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pLoadedPakCount = &*g_pRequestedPakCount - 1; // '-1' shifts it back with sizeof(int16_t). - - g_pPakGlobals = g_GameDll.FindPatternSIMD("48 8D 1D ?? ?? ?? ?? 45 8D 5A 0E").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); /*48 8D 1D ? ? ? ? 45 8D 5A 0E*/ - g_pPakLoadJobID = reinterpret_cast(&*g_pLoadedPakCount - 2); - - g_pPakFifoLock = p_JT_HelpWithAnything.Offset(0x155).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_pPakFifoLockWrapper = p_JT_HelpWithAnything.Offset(0x1BC).FindPatternSelf("48 8D 0D").ResolveRelativeAddressSelf(0x3, 0x7).RCast(); - g_bPakFifoLockAcquired = p_JT_HelpWithAnything.Offset(0x50).FindPatternSelf("C6 05").ResolveRelativeAddressSelf(0x2, 0x7).RCast(); - } - virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; -}; -/////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/rtech/rui/rui.cpp b/r5dev/rtech/rui/rui.cpp index d6d49a42..04c2b8b2 100644 --- a/r5dev/rtech/rui/rui.cpp +++ b/r5dev/rtech/rui/rui.cpp @@ -11,25 +11,22 @@ #include "rui.h" #include "tier1/cvar.h" +static ConVar rui_drawEnable("rui_drawEnable", "1", FCVAR_RELEASE, "Draws the RUI if set", false, 0.f, false, 0.f, "1 = draw; 0 (zero) = no draw"); + //----------------------------------------------------------------------------- // Purpose: draw RUI frame //----------------------------------------------------------------------------- bool __fastcall Rui_Draw(__int64* a1, __m128* a2, const __m128i* a3, __int64 a4, __m128* a5) { - if (!rui_drawEnable->GetBool()) + if (!rui_drawEnable.GetBool()) return false; return v_Rui_Draw(a1, a2, a3, a4, a5); } -void V_Rui::Attach() const +void V_Rui::Detour(const bool bAttach) const { - DetourAttach(&v_Rui_Draw, &Rui_Draw); -} - -void V_Rui::Detach() const -{ - DetourDetach(&v_Rui_Draw, &Rui_Draw); + DetourSetup(&v_Rui_Draw, &Rui_Draw, bAttach); } #endif // !DEDICATED \ No newline at end of file diff --git a/r5dev/rtech/rui/rui.h b/r5dev/rtech/rui/rui.h index 021625c8..7fb07f8e 100644 --- a/r5dev/rtech/rui/rui.h +++ b/r5dev/rtech/rui/rui.h @@ -1,13 +1,9 @@ -#pragma once +#ifndef RTECH_RUI_H +#define RTECH_RUI_H /* ==== RUI ====================================================================================================================================================== */ -inline CMemory p_Rui_Draw; inline bool(*v_Rui_Draw)(__int64* a1, __m128* a2, const __m128i* a3, __int64 a4, __m128* a5); - -inline CMemory p_Rui_LoadAsset; inline void*(*v_Rui_LoadAsset)(const char* szRuiAssetName); - -inline CMemory p_Rui_GetFontFace; inline int16_t(*v_Rui_GetFontFace)(void); /////////////////////////////////////////////////////////////////////////////// @@ -15,24 +11,20 @@ class V_Rui : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Rui_Draw", p_Rui_Draw.GetPtr()); - LogFunAdr("Rui_LoadAsset", p_Rui_LoadAsset.GetPtr()); - LogFunAdr("Rui_GetFontFace", p_Rui_GetFontFace.GetPtr()); + LogFunAdr("Rui_Draw", v_Rui_Draw); + LogFunAdr("Rui_LoadAsset", v_Rui_LoadAsset); + LogFunAdr("Rui_GetFontFace", v_Rui_GetFontFace); } virtual void GetFun(void) const { - p_Rui_Draw = g_GameDll.FindPatternSIMD("40 53 48 83 EC 40 4C 8B 5A 18"); - v_Rui_Draw = p_Rui_Draw.RCast(); /* 40 53 48 83 EC 40 4C 8B 5A 18 */ - - p_Rui_LoadAsset = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? EB 03 49 8B C6 48 89 86 ?? ?? ?? ?? 8B 86 ?? ?? ?? ??").FollowNearCallSelf(); - v_Rui_LoadAsset = p_Rui_LoadAsset.RCast(); /*E8 ?? ?? ?? ?? EB 03 49 8B C6 48 89 86 ?? ?? ?? ?? 8B 86 ?? ?? ?? ??*/ - - p_Rui_GetFontFace = g_GameDll.FindPatternSIMD("F7 05 ?? ?? ?? ?? ?? ?? ?? ?? 4C 8D 0D ?? ?? ?? ?? 74 05 49 8B D1 EB 19 48 8B 05 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B 48 58 48 85 C9 48 0F 45 D1 F7 05 ?? ?? ?? ?? ?? ?? ?? ?? 75 19 48 8B 05 ?? ?? ?? ?? 4C 8D 0D ?? ?? ?? ?? 4C 8B 40 58 4D 85 C0 4D 0F 45 C8 49 8B C9 48 FF 25 ?? ?? ?? ??"); - v_Rui_GetFontFace = p_Rui_GetFontFace.RCast();/*F7 05 ? ? ? ? ? ? ? ? 4C 8D 0D ? ? ? ? 74 05 49 8B D1 EB 19 48 8B 05 ? ? ? ? 48 8D 15 ? ? ? ? 48 8B 48 58 48 85 C9 48 0F 45 D1 F7 05 ? ? ? ? ? ? ? ? 75 19 48 8B 05 ? ? ? ? 4C 8D 0D ? ? ? ? 4C 8B 40 58 4D 85 C0 4D 0F 45 C8 49 8B C9 48 FF 25 ? ? ? ?*/ + g_GameDll.FindPatternSIMD("40 53 48 83 EC 40 4C 8B 5A 18").GetPtr(v_Rui_Draw); + g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? EB 03 49 8B C6 48 89 86 ?? ?? ?? ?? 8B 86 ?? ?? ?? ??").FollowNearCallSelf().GetPtr(v_Rui_LoadAsset); + g_GameDll.FindPatternSIMD("F7 05 ?? ?? ?? ?? ?? ?? ?? ?? 4C 8D 0D ?? ?? ?? ?? 74 05 49 8B D1 EB 19 48 8B 05 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B 48 58 48 85 C9 48 0F 45 D1 F7 05 ?? ?? ?? ?? ?? ?? ?? ?? 75 19 48 8B 05 ?? ?? ?? ?? 4C 8D 0D ?? ?? ?? ?? 4C 8B 40 58 4D 85 C0 4D 0F 45 C8 49 8B C9 48 FF 25 ?? ?? ?? ??").GetPtr(v_Rui_GetFontFace); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const; - virtual void Detach(void) const; + virtual void Detour(const bool bAttach) const; }; /////////////////////////////////////////////////////////////////////////////// + +#endif // RTECH_RUI_H diff --git a/r5dev/rtech/stryder/stryder.h b/r5dev/rtech/stryder/stryder.h index 9e683999..2ff39b1b 100644 --- a/r5dev/rtech/stryder/stryder.h +++ b/r5dev/rtech/stryder/stryder.h @@ -1,35 +1,27 @@ -#pragma once +#ifndef RTECH_STRYDER_H +#define RTECH_STRYDER_H /* ==== STRYDER ================================================================================================================================================ */ -inline CMemory p_Stryder_StitchRequest; -inline void*(*Stryder_StitchRequest)(void* a1); - -inline CMemory p_Stryder_SendOfflineRequest; -inline bool(*Stryder_SendOfflineRequest)(void); +inline void*(*v_Stryder_StitchRequest)(void* a1); +inline bool(*v_Stryder_SendOfflineRequest)(void); /////////////////////////////////////////////////////////////////////////////// class VStryder : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("Stryder_StitchRequest", p_Stryder_StitchRequest.GetPtr()); - LogFunAdr("Stryder_SendOfflineRequest", p_Stryder_SendOfflineRequest.GetPtr()); + LogFunAdr("Stryder_StitchRequest", v_Stryder_StitchRequest); + LogFunAdr("Stryder_SendOfflineRequest", v_Stryder_SendOfflineRequest); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - p_Stryder_StitchRequest = g_GameDll.FindPatternSIMD("48 8B C4 53 57 41 56 48 81 EC 20"); - p_Stryder_SendOfflineRequest = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 35 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 83 65 D0 FC 48 8D 4D 80"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - p_Stryder_StitchRequest = g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 E8 B4"); - p_Stryder_SendOfflineRequest = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 35 ?? ?? ?? ??"); -#endif - Stryder_StitchRequest = p_Stryder_StitchRequest.RCast(); /*48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 E8 B4*/ - Stryder_SendOfflineRequest = p_Stryder_SendOfflineRequest.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 35 ?? ?? ?? ??*/ + g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 E8 B4").GetPtr(v_Stryder_StitchRequest); + g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 56 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 35 ?? ?? ?? ??").GetPtr(v_Stryder_SendOfflineRequest); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// + +#endif // RTECH_STRYDER_H diff --git a/r5dev/sdklauncher/CMakeLists.txt b/r5dev/sdklauncher/CMakeLists.txt index 89f42995..634648ea 100644 --- a/r5dev/sdklauncher/CMakeLists.txt +++ b/r5dev/sdklauncher/CMakeLists.txt @@ -50,6 +50,7 @@ target_link_libraries( ${PROJECT_NAME} PRIVATE "tier0" "tier1" "tier2" + "libdetours" "libcppkore" "libspdlog" diff --git a/r5dev/sdklauncher/advanced_surface.cpp b/r5dev/sdklauncher/advanced_surface.cpp index abfb3705..8cfd35ad 100644 --- a/r5dev/sdklauncher/advanced_surface.cpp +++ b/r5dev/sdklauncher/advanced_surface.cpp @@ -1240,6 +1240,24 @@ eLaunchMode CAdvancedSurface::BuildParameter(string& svParameters) // Purpose: gets processor affinity, and automatically appends the single-core // dedi cvar if core count = 1 // Input : &svParameters - +// +// CPU3 CPU2 CPU1 CPU0 Bin Hex +// ---- ---- ---- ---- --- --- +// OFF OFF OFF ON = 0001 = 1 +// OFF OFF ON OFF = 0010 = 2 +// OFF OFF ON ON = 0011 = 3 +// OFF ON OFF OFF = 0100 = 4 +// OFF ON OFF ON = 0101 = 5 +// OFF ON ON OFF = 0110 = 6 +// OFF ON ON ON = 0111 = 7 +// ON OFF OFF OFF = 1000 = 8 +// ON OFF OFF ON = 1001 = 9 +// ON OFF ON OFF = 1010 = A +// ON OFF ON ON = 1011 = B +// ON ON OFF OFF = 1100 = C +// ON ON OFF ON = 1101 = D +// ON ON ON OFF = 1110 = E +// ON ON ON ON = 1111 = F //----------------------------------------------------------------------------- uint64_t CAdvancedSurface::GetProcessorAffinity(string& svParameters) { diff --git a/r5dev/sdklauncher/sdklauncher.cpp b/r5dev/sdklauncher/sdklauncher.cpp index 90c28b35..ec8ebb25 100644 --- a/r5dev/sdklauncher/sdklauncher.cpp +++ b/r5dev/sdklauncher/sdklauncher.cpp @@ -4,6 +4,7 @@ // //=============================================================================// #include "tier0/binstream.h" +#include "tier1/fmtstr.h" #include "base_surface.h" #include "advanced_surface.h" #include "sdklauncher.h" @@ -235,16 +236,25 @@ bool CLauncher::CreateLaunchContext(eLaunchMode lMode, uint64_t nProcessorAffini void CLauncher::SetupLaunchContext(const char* szConfig, const char* szGameDll, const char* szCommandLine) { CIOStream cfgFile; - string commandLine; + CFmtStrMax commandLine; if (szConfig && szConfig[0]) { - if (cfgFile.Open(Format(GAME_CFG_PATH"%s", szConfig), CIOStream::READ)) + commandLine.Format(GAME_CFG_PATH"%s", szConfig); + + if (cfgFile.Open(commandLine.String(), CIOStream::READ)) { - if (!cfgFile.ReadString(commandLine)) + // Reuse the stack string for the actual command line buffer. + commandLine.Clear(); + + if (!cfgFile.ReadString(commandLine.Access(), commandLine.GetMaxLength())) { AddLog(spdlog::level::level_enum::err, "Failed to read file '%s'!\n", szConfig); } + else + { + commandLine.SetLength(strlen(commandLine.String())); + } } else // Failed to open config file. { @@ -254,18 +264,18 @@ void CLauncher::SetupLaunchContext(const char* szConfig, const char* szGameDll, if (szCommandLine && szCommandLine[0]) { - commandLine.append(szCommandLine); + commandLine.Append(szCommandLine); } m_svGameDll = Format("%s\\%s", m_svCurrentDir.c_str(), szGameDll); - m_svCmdLine = Format("%s\\%s %s", m_svCurrentDir.c_str(), szGameDll, commandLine.c_str()); + m_svCmdLine = Format("%s\\%s %s", m_svCurrentDir.c_str(), szGameDll, commandLine.String()); /////////////////////////////////////////////////////////////////////////// // Print the file paths and arguments. std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl; AddLog(spdlog::level::level_enum::debug, "- CWD: %s\n", m_svCurrentDir.c_str()); AddLog(spdlog::level::level_enum::debug, "- EXE: %s\n", m_svGameDll.c_str()); - AddLog(spdlog::level::level_enum::debug, "- CLI: %s\n", commandLine.c_str()); + AddLog(spdlog::level::level_enum::debug, "- CLI: %s\n", commandLine.String()); std::cout << "----------------------------------------------------------------------------------------------------------------------" << std::endl; } diff --git a/r5dev/studiorender/studiorendercontext.h b/r5dev/studiorender/studiorendercontext.h index 9df75d65..58750545 100644 --- a/r5dev/studiorender/studiorendercontext.h +++ b/r5dev/studiorender/studiorendercontext.h @@ -3,36 +3,24 @@ //------------------------------------------------------------------------- // CSTUDIORENDERCONTEXT //------------------------------------------------------------------------- -inline CMemory CStudioRenderContext__LoadModel; -inline CMemory CStudioRenderContext__LoadMaterials; +inline void (*CStudioRenderContext__LoadModel)(__int64 thisptr, studiohdr_t* pStudioHdr, void* pVtxBuffer, studiohwdata_t* pStudioHWData); +inline __int64 (*CStudioRenderContext__LoadMaterials)(__int64 thisptr, studiohdr_t* pStudioHdr, void* a3, uint32_t* pFlags); /////////////////////////////////////////////////////////////////////////////// class VStudioRenderContext : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CStudioRenderContext::LoadModel", CStudioRenderContext__LoadModel.GetPtr()); - LogFunAdr("CStudioRenderContext::LoadMaterials", CStudioRenderContext__LoadMaterials.GetPtr()); + LogFunAdr("CStudioRenderContext::LoadModel", CStudioRenderContext__LoadModel); + LogFunAdr("CStudioRenderContext::LoadMaterials", CStudioRenderContext__LoadMaterials); } virtual void GetFun(void) const { -#if defined (GAMEDLL_S1) - CStudioRenderContext__LoadModel = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 53 55 56 41 54 41 57"); -#elif defined (GAMEDLL_S2) - CStudioRenderContext__LoadModel = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 54 24 ?? 53 57 41 55 48 81 EC ?? ?? ?? ??"); -#elif defined (GAMEDLL_S3) - CStudioRenderContext__LoadModel = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 54 24 ?? 48 89 4C 24 ?? 53 55 56 57 48 83 EC 78"); -#endif// 0x1404554C0 // 4C 89 44 24 ? 48 89 54 24 ? 48 89 4C 24 ? 53 55 56 57 48 83 EC 78 // - -#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) - CStudioRenderContext__LoadMaterials = g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 55 56 41 57"); -#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) - CStudioRenderContext__LoadMaterials = g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 55 56 41 55"); -#endif// 0x140456B50 // 48 8B C4 4C 89 40 18 55 56 41 55 // + g_GameDll.FindPatternSIMD("4C 89 44 24 ?? 48 89 54 24 ?? 48 89 4C 24 ?? 53 55 56 57 48 83 EC 78").GetPtr(CStudioRenderContext__LoadModel); + g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 55 56 41 55").GetPtr(CStudioRenderContext__LoadMaterials); } virtual void GetVar(void) const { } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Detour(const bool bAttach) const { } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/thirdparty/curl/easy.c b/r5dev/thirdparty/curl/easy.c index 3284d2a8..28d7b997 100644 --- a/r5dev/thirdparty/curl/easy.c +++ b/r5dev/thirdparty/curl/easy.c @@ -895,7 +895,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ outcurl->cookies = Curl_cookie_init(data, - data->cookies->filename, + NULL, outcurl->cookies, data->set.cookiesession); if(!outcurl->cookies) diff --git a/r5dev/thirdparty/detours/include/idetour.h b/r5dev/thirdparty/detours/include/idetour.h index 5c4b72f9..ff2b5bb7 100644 --- a/r5dev/thirdparty/detours/include/idetour.h +++ b/r5dev/thirdparty/detours/include/idetour.h @@ -13,8 +13,17 @@ public: virtual void GetVar(void) const = 0; virtual void GetCon(void) const = 0; - virtual void Attach(void) const = 0; - virtual void Detach(void) const = 0; + virtual void Detour(const bool bAttach) const = 0; + template< + typename T, + typename std::enable_if::value, int>::type = 0> + LONG DetourSetup(_Inout_ T* ppPointer, _In_ T pDetour, const bool bAttach) const + { + if (bAttach) + return DetourAttach(ppPointer, pDetour); + else + return DetourDetach(ppPointer, pDetour); + } }; extern std::vector g_DetourVec; diff --git a/r5dev/thirdparty/detours/src/idetour.cpp b/r5dev/thirdparty/detours/src/idetour.cpp index 264ae8d3..bbd1de53 100644 --- a/r5dev/thirdparty/detours/src/idetour.cpp +++ b/r5dev/thirdparty/detours/src/idetour.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include "../include/detours.h" #include "../include/idetour.h" //----------------------------------------------------------------------------- diff --git a/r5dev/thirdparty/dirtysdk/CMakeLists.txt b/r5dev/thirdparty/dirtysdk/CMakeLists.txt new file mode 100644 index 00000000..6436f9a4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/CMakeLists.txt @@ -0,0 +1,191 @@ +cmake_minimum_required( VERSION 3.16 ) +add_module( "lib" "DirtySDK" "" ${FOLDER_CONTEXT} TRUE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "Platform" +# Core source files + "source/platform/plat-str.c" + "source/platform/plat-time.c" +) + +add_sources( SOURCE_GROUP "Comm" +# Core source files + "source/comm/commsrp.c" + "source/comm/commudp.c" + "source/comm/commudputil.c" +) + +add_sources( SOURCE_GROUP "Crypto" +# Core source files + "source/crypt/cryptaes.c" + "source/crypt/cryptarc4.c" + "source/crypt/cryptchacha.c" + "source/crypt/cryptcurve.c" + "source/crypt/cryptgcm.c" + "source/crypt/crypthash.c" + "source/crypt/crypthmac.c" + "source/crypt/cryptmd5.c" + "source/crypt/cryptmont.c" + "source/crypt/cryptnist.c" + "source/crypt/cryptrandcommon.c" + "source/crypt/cryptrsa.c" + "source/crypt/cryptsha1.c" + "source/crypt/cryptsha2.c" +# Optional core source files + "source/crypt/cryptbn.c" +# PC plat source files + "source/crypt/cryptrand.c" +) + +add_sources( SOURCE_GROUP "Crypto/Include" +# Core include files + "source/crypt/cryptrandpriv.h" + "source/crypt/cryptrandcommon.h" +) + +add_sources( SOURCE_GROUP "Socket" +# Core source files + "source/dirtysock/dirtyaddr.c" + "source/dirtysock/dirtycert.c" + "source/dirtysock/dirtyerr.c" + "source/dirtysock/dirtylib.c" + "source/dirtysock/dirtylib.cpp" + "source/dirtysock/dirtymem.c" + "source/dirtysock/dirtynames.c" + "source/dirtysock/dirtynet.c" + "source/dirtysock/dirtythread.cpp" + "source/dirtysock/dirtyuser.c" + "source/dirtysock/netconn.c" + "source/dirtysock/netconncommon.c" + #"source/dirtysock/netconnlocaluser.cpp" +# PC plat source files + "source/dirtysock/dirtyaddr.c" + "source/dirtysock/pc/dirtyerrpc.c" + "source/dirtysock/pc/dirtylibwin.c" + "source/dirtysock/pc/dirtynetwin.c" + "source/dirtysock/pc/netconnwin.c" +) + +add_sources( SOURCE_GROUP "Socket/Include" +# Core include files + "source/dirtysock/dirtynetpriv.h" + "source/dirtysock/netconncommon.h" + "source/dirtysock/netconnlocaluser.h" +) + +add_sources( SOURCE_GROUP "Game" +# Core source files + "source/game/connapi.c" + "source/game/netgamedist.c" + "source/game/netgamedistserv.c" + "source/game/netgamelink.c" + "source/game/netgameutil.c" +) + +add_sources( SOURCE_GROUP "Codec" +# Core source files + "source/graph/dirtygif.c" + "source/graph/dirtygraph.c" + "source/graph/dirtyjpg.c" + "source/graph/dirtypng.c" +) + +add_sources( SOURCE_GROUP "Misc" +# Core source files + "source/misc/qosclient.c" + "source/misc/qoscommon.c" + "source/misc/weblog.c" +) + +add_sources( SOURCE_GROUP "Misc/Include" +# Core include files + "source/misc/userapipriv.h" +) + +add_sources( SOURCE_GROUP "Protocol" +# Core source files + "source/proto/protoadvt.c" + "source/proto/protohttpmanager.c" + "source/proto/protohttpserv.c" + "source/proto/protohttputil.c" + "source/proto/protohttp2.c" + "source/proto/protoname.c" + "source/proto/protostream.c" + "source/proto/prototunnel.c" + "source/proto/protoupnp.c" +# PC plat source files + "source/proto/protohttp.c" + "source/proto/protomangle.c" + "source/proto/protossl.c" + "source/proto/protowebsocket.c" +) + +add_sources( SOURCE_GROUP "Utility" +# Core source files + "source/util/aws.c" + "source/util/binary7.c" + "source/util/base64.c" + "source/util/hpack.c" + "source/util/jsonformat.c" + "source/util/jsonparse.c" + "source/util/murmurhash3.c" + "source/util/protobufcommon.c" + "source/util/protobufread.c" + "source/util/protobufwrite.c" + "source/util/utf8.c" +) + +add_sources( SOURCE_GROUP "Voip" +# Core source files + "source/voip/voiptunnel.c" + "source/voip/voipgroup.c" +# PC plat source files + "source/voip/voip.c" + "source/voip/voipblocklist.c" + "source/voip/voipconduit.c" + "source/voip/voipcodec.c" + "source/voip/voipcommon.c" + "source/voip/voipconnection.c" + "source/voip/voipdvi.c" + "source/voip/voipmixer.c" + "source/voip/voippcm.c" + "source/voip/pc/voipheadsetpc.c" + "source/voip/pc/voippc.c" + "source/voip/voiptranscribe.c" + + "source/voip/pc/voipnarratepc.cpp" +) + +add_sources( SOURCE_GROUP "Voip/Include" +# Core include files + "source/voip/voipcommon.h" + "source/voip/voipconduit.h" + "source/voip/voipconnection.h" + "source/voip/voipdvi.h" + "source/voip/voipmixer.h" + "source/voip/voippacket.h" + "source/voip/voippcm.h" + "source/voip/voippriv.h" +) + +add_sources( SOURCE_GROUP "XML" +# Core source files + "source/xml/xmlformat.c" + "source/xml/xmlparse.c" +) + +end_sources() +thirdparty_suppress_warnings() + +target_include_directories( ${PROJECT_NAME} PRIVATE + "${THIRDPARTY_SOURCE_DIR}/dirtysdk/include/" + "${THIRDPARTY_SOURCE_DIR}/ea/" + "${THIRDPARTY_SOURCE_DIR}/ea/EAThread/include/" + + "${THIRDPARTY_SOURCE_DIR}/dirtysdk/source/crypt/" + "${THIRDPARTY_SOURCE_DIR}/dirtysdk/source/dirtysock/" + "${THIRDPARTY_SOURCE_DIR}/dirtysdk/source/voip/" +) + +target_compile_definitions( ${PROJECT_NAME} PRIVATE DIRTYCODE_LOGGING=0 ) diff --git a/r5dev/thirdparty/dirtysdk/changelog.txt b/r5dev/thirdparty/dirtysdk/changelog.txt new file mode 100644 index 00000000..a0098c02 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/changelog.txt @@ -0,0 +1,6769 @@ +EADP Game Services DirtySDK 15.1.6.0.5 - August 19, 2020 + + Framework 8.04.04 + + Android AndroidSDK 100.00.00-pr1 + AndroidNDK 15.2.4203891 + Capilano CapilanoSDK 180713-proxy + GDK GDK 200602-proxy + Kettle kettlesdk 7.508.021-proxy + PlayStation4AppContentEx 7.008.001 + PlayStation4AudioInEx 7.008.001 + PS5 ps5sdk 1.00.00.40-proxy + NX nx_config 1.00.05 + nxsdk 9.0.0 + Linux/Unix UnixClang 4.0.1 + Stadia StadiaSDK 1.48-proxy + Win32 WindowsSDK 10.0.19041 + +New in this release: + + DirtySDK (All) + DirtyNet + Added new SocketPacketQueueAdd2() which has a new bPartialAllowed parameter allowing for enqueueing of only + a portion of the submitted packet. (Only used from dirtynetnx for now) + + Added new SocketPacketQueueGetHead() that can be used to get the data associated with the queue head, + without consuming the head and dequeueing it. + + Added new SocketPacketQueueTouchHead() that can be used to update queue's head entry such that it no longer + includes successfully consumed portion of data previously queried with SocketPacketQueueGetHead(). + + ProtoSSL + Added the ability to set async receive on the SSL socket. + + DirtySDK (GDK) + NetConnGDK + Added sandbox mapping for CERT.DEBUG to CERT environment which is used by MS internally when debugging + issues. + + DirtySDK (GDK, XboxOne) + VoipHeadsetXboxOne + Added the ability to query the local user's STT preference from the system. + + DirtySDK (NX) + DirtyNetNX + Added send queue and send thread for offloading send operations from main thread. (socket send system call + proved to block unreasonably long even with non-blocking sockets) - feature disabled by default + + Added new 'asnd' global control selector that can be used to enable async send globally or on a per-socket + basis. + + NetConnNX + Added new '-asyncsend' netconn startup param to turn on socket creation defaulting to async send being + used + + Added new '-silent' to disable the display of any first party dialog as a result of handling network request + errors. + + DirtySDK (PS4) + DirtyWebApiPS4 + Added DirtyWebApiUpdate and 'auto' selector to control where the updating of dirtywebapi happens from. + + Added support for queueing event callbacks when the 'qcal' control is set to true. + + DirtySDK (PS5) + DirtyWebApi2PS5 + Added DirtyWebApiUpdate and 'auto' selector to control where the updating of dirtywebapi happens from. + + Added support for queueing event callbacks when the 'qcal' control is set to true. + + NetConnPS5 + Added ability to query play-first trial status via 'pftr' status selector. + + UserApiPS5/UserListApiPS5 + Added rich presence support. On PS5 rich presence returns the most recent unhidden activity record. + * The activity name is returned in strData. For other field it is up to the game parse the raw json. + + DirtySDK (XboxOne) + NetConnXboxOne + Added sandbox mapping for CERT.DEBUG to CERT environment which is used by MS internally when debugging + issues. + +Changes in this release: + + DirtySDK (All) + DirtyNet + Fixed issues with using async receive on our TCP sockets by making several adjustments to our behavior. + The error conversion we were doing in SocketRecvfrom treated all zero error codes as a closed socket. To address this we + simplified the error conversion by changing the error codes when there is no data in the packet queue to fake the codes + the platform socket API would return. For UDP sockets we don't handle errors in the socket receive thread, which + doesn't work well for TCP sockets. To address this we only added open sockets to the poll list for TCP and other socket + types are unchanged. There is a race condition that is created by using the socket recv thread due to the use of the + 'stat' selector. Due to the socket recv thread already handling these cases we just disable the portion of the 'stat' + selector when async receive is enabled. + + DirtySDK (GDK) + Build + Updated to add the dependencies into the modules with in GDK package that give access to the GDK extensions. + + ProtoHttpGDK + Fixed issue getting PROTOHTTP_RECVDONE from ProtoHttpRecv when passing in iBufMax = 0 by not early returning + if SocketRateThrottle returns zero. + + Changed the setting of the init flags for winhttp to be a global control selector instead of setting it + based on if we are in a debug build or not. + + Fixed state transition when calling WinHttpSendRequest by making state transition before the call in case + send completes (on a different thread) before the end of the call. + + UserListApiGDK + Fixed issue with identifying response from the DATA callback for mute and block list queries by adding in + the type and user index into the data. + + Fixed the xuid passed into the fill out profile function which was using the first index into the array + instead of the current index due to a typo. + + DirtySDK (PC, XboxOne, GDK) + DirtyNetWin + Removed the direct recv from the socket when there is no data in the packet queue. In an attempt to align + the implementations of dirtynet, we will remove our attempted optimization in dirtynetwin. + + DirtySDK (PS4, PS5) + NetConnPS4, NetConnPS5 + Fixed an incorrect debug print statement that indicated failure to acquire PPPoE information when 'type' + status selector is used. + + VoipHeadsetPS4 + Changed the block list interval down to the same value we are using on stadia to try to keep the + implementations aligned. + + DirtySDK (PS5) + NetConnPS5 + Fixed duplicate NP platform termination on shutdown. + + UserListApiPS5 + Fixed problems with querying profiles of 100 friends (up to the page limit) + + DirtySDK (Stadia) + UserListApiStadia + Fixed issue with identifying response from the DATA callback for mute and block list queries by adding in + the type and user index into the data. + + VoipHeadsetStadia + Fixed the inability to get the hardware status by calling the status callback when we get state changes. + Using GgpAddMicAudioStateChangedHandler allows us to get the state changes of the microphone state. When the + microphone is not available for use we must convey the mute. DirtySDK already has an API to do this but we + weren't sending the right callbacks down to signal the changes. + + DirtySDK (XboxOne) + VoipNarrateXboxOne + Changed the implementation to add exception handling to the speech api and prevent multiple asynchronous + reads at once. + +EADP Game Services DirtySDK 15.1.6.0.4 - June 3, 2020 + + Framework 8.04.00 + + Android AndroidSDK 100.00.00-pr1 + AndroidNDK 15.2.4203891 + Capilano CapilanoSDK 180712-1-proxy + GDK GDK 200200-proxy + Kettle kettlesdk 7.508.001-proxy + PlayStation4AppContentEx 7.008.001 + PlayStation4AudioInEx 7.008.001 + PS5 ps5sdk 0.95.00.36-proxy + NX nx_config 1.00.05 + nxsdk 9.0.0 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Stadia StadiaSDK dev-proxy + Win32 WindowsSDK 10.0.18362 + +New in this release: + + DirtySDK (All) + Base64 + Added the ability to remove padding from url encoded via a new API Base64EncodeUrl2. Padding isn't required + by the spec in all cases and with JWT we do not wanted padding added. + + DirtySDK (Sony) + VoipHeadsetPS4 + Added support for querying the blocklist to auto-mute players on Sony platforms as required by TRC R5304. + + DirtySDK (Stadia) + NetConnStadia + Added support for the 'chat' selector to check if the local user can use voice chat. This selector will be + used by the voip system like on other platforms to restrict voice chat. + + PrivilegeApiStadia + Added support for checking the voice chat and multiplayer features on stadia allowing for showing the + overlay if necessary. + + VoipHeadsetStadia + Added support for processing the blocklist to auto-mute players. Added tracking for the remote player ids + which is needed for processing the blocklist. + + Added the checking of the voice chat restrictions for the transmission and playback of voice chat. + + Added support for transcription and text chat accessibility features. This implementation is based on PS4 + where we disable 'vdis' for transcription when narration is active. Like on other platforms we provide the + text chat narration features as a way to allow teams to use our voip features for text chat when voice chat + is inactive. + + Added support for text-to-speech using the cloud based providers via voipnarrate. The narration module + supports a sample rate that differs of that in module. This is handled transparently in the codec (via + resampling) but we need to handle the data coming in with the differing sample rate. + + VoipStadia + Added proper support for local input muting. We had preliminary for the local input muting in the voip + module but it was not implemented in the headset module. + +Changes in this release: + + DirtySDK (All) + DirtyNet + Removed the suppressions around the include of winsock2.h in dirtynet.h to fix issues with all warnings + being suppressed for complation units that include this header file. In previous versions of the WindowsSDK + these suppressions were needed due to warnings being generated by the header file. We no longer see this + behavior on current versions of the WindowSDK. + + NetConn + Increased the number of idle handlers to 64 due to game team use cases where more than 32 need to be + registered. The number of idle handlers should not be the limiting factor to the amount of modules we can + have running at once. + + QosClient + Changed the NetCritEnter to NetCritTry to prevent stalling the main thread waiting for the lock. If we can't + the lock then we can wait for the next frame to do the update. + + VoipHeadset + Changed the headset modules to unify on how they detect a change in the first party mute list uisng the + 'fpmu' selector. Previously on xbox we were using the 'fpmc' selector that returns the number of entries in + the first party mute list. Given that certain implementations could make multiple updates in an interval + that could mean no change in the count, we needed to use a different method. The new method will signal if + an update was made to the list which we signal in the places when we know a change has been made. + + VoipNarrate + Updated the module to expose the sample rate that it uses internally. On system's with differing sample + rates it allows us to resample where necessary at the codec level. + + VoipOpus + Refactored the resampler logic to prevent reallocation when switching sampling rate and update the + calculation the sample rate based on input samples. On Stadia when communicating without accessibility we + will be using a sample rate of 48kHz, our accessibility modules depend on 16kHz which we need to resample in + order to support. Given that we might switch the sampling rate we don't want to reallocate the same state + thus we will just remember what sampling rate the resampler is configured to. + + UserList + Increased the size of the gamertag field to support the increases in the gdk and stadia gamertags. + + DirtySDK (GDK) + NetConnGDK + Changed the implementation of the environment determination to default to production on cases where the + sandbox lookup failed or a valid mapping was not found. In these cases we want to ensure that the default + case of running in production still functions. If we would fall into these cases before these changes we + would be stuck in the environment determination state until the user tried to reconnect to the platform + network again. + + UserListApiGDK + Fixed a build error UserListApiGDK. We now also include userlistapixboxone.h in userlistapi.h. + This matches what was done for userapi.h and fixes compliation issues. + + DirtySDK (NX) + NetConnNX + Added 'eusr' selector support on NX. This selector returns the IEAUser interface given a user index. + + DirtySDK (PS4) + DirtySessionManagerPS4 + Removed Play together feature because it is not officially deprecated. All the assciated functionality will + only be avaliable if you are using SDK version lower than 7.5 SDK + + NetConnPS4 + Changed NetConnStartup to update the title id if it is called while the module is already started and + title id is NETCONNPS4_SAMPLE_TITLE_ID. This will allow the title id to be updated to the game title id + for cases where NetConnStartup is called early for BugSentry and NETCONNPS4_SAMPLE_TITLE_ID is used as + a default. + + DirtySDK (PS5) + DirtyWebApi2PS5 + Changed the way how DirtyWebApi callback is called when we encounter sce errors. The http status code is now also + supplied along with the sce error to help differentiate potential buffer size errors. + + Fixed the unintended knock on of sharing dirtywebapi2. When modules sharing dirtywebapi2 they should only be allowed to + abort the requests made by themselves. + + NetConnPS5 + Changed NetConnStartup to update title id if it is called while the module is already started and + title id is NETCONNPS4_SAMPLE_TITLE_ID. This will allow the title id to be updated to the game title id + for cases where NetConnStartup is called early for BugSentry and NETCONNPS4_SAMPLE_TITLE_ID is used as + a default. + + UserListApiPS5 + Fixed Total item friends count not being set for a list end event. + + Fixed callbacks not firing when using type mask with a user without friends. It will now also fire the callback if + the get account ids request returns zero items. + + Removed an unecessary NetCritLeave in _UserListApiProcessRequest (). This extra NetCritLeave was causing strange + deadlocks because unlocking an unlocked mutex is undefined behavior. + + Changed the restriction on the max blocked list size limit to 2000 per request (this is the max number supported by PSN). + Querying Blocked List with filters or mask parameter are also not supported. + + Fixed the _UserListApiAddQueryParamsToRequest() function to ignore adding query parameters for blocked list reqauest + instead of the friends request. + + DirtySDK (Sony) + VoipHeadsetPS4 + Removed the uChatPrivileges field as it is no longer read out of due to changes in CL1329299. + + DirtySDK (Stadia) + NetConnStadia + Changed the 'tick' selector to support retrieveing the raw JWT. Retrieving the JWT was missed during the + initial implementation of JWT support on stadia. This aligns with how the other platforms support retrieving + the JWT. + + Voip + Enabled the speaker callback on stadia. We are following the PC implementation and this was already + supported at the headset level. + + VoipHeadsetStadia + Fixed VoipHeadsetControl to return -1 when unhandled. + + VoipStadia + Fixed a bug in that the correct user indices were not being passed to apply the mute on. + + DirtySDK (XboxOne) + NetConnXboxOne + Fixed for uncaught exception during environment determination when the TMS class is not enabled in the + appxmanifest. If the application was not setup to include the ability to access title storage an exception + will be thrown when we try to create that class. In this case we will catch the exception and default to the + production environment. + + +EADP Game Services DirtySDK 15.1.6.0.3 - April 21, 2020 + + Framework 8.03.02 + + Android AndroidSDK 100.00.00-pr1 + AndroidNDK 15.2.4203891 + android_config 5.00.00-pr1 + Capilano CapilanoSDK 180712-1-proxy + GDK GDK 200200-proxy + Kettle kettlesdk 7.008.001-proxy + PlayStation4AppContentEx 6.508.010 + PlayStation4AudioInEx 6.508.001 + PS5 ps5sdk 0.95.00.36-proxy + NX nx_config 1.00.05 + nxsdk 9.0.0 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Stadia StadiaSDK dev-proxy + Win32 WindowsSDK 10.0.18362 + + *** Important - First release supporting Stadia *** + This is the first DirtySDK release that supports Stadia. Most but not all functionality is supported. + For more detailed information please please contact Gameplay Services. + +New in this release: + + DirtySDK (All) + DirtyGIF, DirtyGraph + Added support for multiframe GIF decoding. Frames may be decoded all at once or one at a time. + + DirtyUser + Added a generic dirtyuser based on uint64_t identifiers and switched all our platforms to using it. Given + that all our platforms use uint64_t ids there is no reason to require the creation of a platform specific + version that is exactly the same. + + ProtoHttp + Added a new error PROTOHTTP_EAGAIN to be used when we cannot make the request because a resource is not + available or not ready to be used. + + ProtoSSL + Added a public API function to expose the pkcs1 verify features internal to protossl. This functionality + will allow us to verify the RSA signatures when used for cryptographic operations outside the context of a + TLS connection. + + Added a public function to generate a PKCSv1.5 signature. + + DirtySDK (PS5) + UserApiPS5, UserListApiPS5 + Added PS5 support for UserApi and UserListApi + + DirtySDK (Stadia) + NetConnStadia + Added initial support for the stadia platform. Other modules use the unix versions which work out of the + box. + + Added a selector 'gpid' to get the player id for the user registered at a given index. + + UserApiStadia, UserListApiStadia + Adding support for userapi and userlistapi on the stadia platform. For the profile data, stadia only + supports getting the gamertag and id. For the presence data, stadia only supports getting the availability + of the user. + + VoipStadia + Added an implementation of the voip module for stadia. This implementation is generic and inspired from the + other platforms. It will be expected to change when we start implementing the headset functionality. + + VoipHeadsetStadia + Added support for the headset module on stadia. Stadia only supports pulling PCM data from the microphone at + 48kHz. For other platforms we normally only support 16kHz but we don't have the option to modify this on + stadia. Given that 20ms of audio data at 48kHz exceeds our MTU we must compress the audio. To do this we + have added support for the opus codec which is supported on other supported platforms. Opus is configured to + use 16kHz by default but for stadia we have modified the build scripts and source to allow bumping that up + to 48kHz. + +Changes in this release: + + DirtySDK (All) + Base64 + Fixed decoding of non-padded base64. when no padding is present treat the data up to the quad as padded. + + NetConn + Fixed the issue of NetConnDisconnect being called while the ref count is not zero. + + NetGameDistServ + Moved some metrics sampling interval validation logic to where it belongs, from gameserveroimetrics.c to + netgamedistserv.c + + Enhanced debug logging around calculation of idpps, odmpps and inputdrops metrics. + + Replaced NetGamDistServT::bPrevFlowEnabled, NetGameDistServT::bPrevNoInputMode and + NetGameDistServT::uPrevStatClientCount with respective local variables in functions where their usage was + limited to. Fixed implementation of NetGameDistControl('rsta') such that it is now limited to reset the + following boolean variables used to flag if events happened during the sampling interval: + NetGameDistServT::bClientCountChanged, NetGameDistServT::bFlowEnabledChanged, + NetGameDistServT::bNoInputModeChanged. + + Fixed implementation of 'ocnt' selector to now return 1 if sampling interval is invalid. + + Fixed implementation of 'drop' and 'icnt' selector to now return 1 if sampling interval is invalid. + + ProtoHttp + Deprecated the PROTOHTTP_NOTHREADS error (used on PS4/PS5) as the PROTOHTTP_EAGAIN can be used instead as a + general purpose error across the platforms. + + ProtoSSL + Fixed an issue where a PSS signature scheme could be chosen when sending a CertificateVerify message for a + PKCS1 certificate. This would cause OpenSSL to abort the connection with an illegal parameter alert. + + Fixed a possible null-pointer exception by aborting the handshake if we can't find a valid matching + signature scheme in RecvCertificateRequest; this situation could only happen with a non-compliant server. + + Fixed SendCertificateRequest() to build the TLS1.2 SignatureAndHashAlgorithms from the available signature + schemes rather than hard-coding a subset of available signature schemes in the function itself. + + Removing support for ECDSA-SHA512 as we are not looking to support SECP521R1 curves at this time. + + VoipConnection + Changed the initial value (from 127 to 0) of the variable used to track the last received seq number for + reliable data messages on a specific voip connection. The previous value was used to make it clear that + the validity range is limited to [1,127] and that the next expected inbound sequence number at connection + start is 127 + 1 = 1. This had a knock on in a scenario where we need to send a ACK before inbound traffic + would have even been received. That scenario seems only possible in server-based connectivity using + the 1x upload Nx download optimiztion on MIC packets. Upon exercising that race condition, one end of + the connection would ACK seq nb 127 when in reality it has not yet received packet resulting in reliable + data traffic flow being broken. The bug fix consists in initializing the variable with 0 instead resulting + in the next expected inbound sequence number at connection start to be 0 + 1 = 1, and the usage of 0 when + ACKING before traffic comes in, where 0 means 'ignore this field, no sequence number acked'. + + DirtySDK (GDK) + ProtoHttpGDK + Fixed the uri truncation by parsing the information directly from the request headers. If you had a uri + longer than 256 characters we would truncate the information causing the formatted request headers to fail + when making the call to WinHttpOpenRequest. + + Changed the behavior on gdk to not automatically redirect before providing the caller with the redirection + header information. When redirects are enabled with WinHttp, you never get a chance to inspect the + intermediate header information. Their are certain customers (webkit) that depend on being able to pull out + the location headers before the redirect is complete. + + Fixed the corruption of the download payload by only resetting the read and recv positions before doing a + new read. If the caller has read the entire available buffer using ProtoHttpRecv while we have a pending + WinHttpReadData call (due to having some available space in the buffer), our payload may get corrupted. The + iInpOff and iInpLen fields get reset to zero when everything is read in ProtoHttpRecv. This causes the + writes to happen to the wrong location. + + Fixed the processing of the redirect from a POST to a GET by making sure the post size is set to zero. In + the GDK version of ProtoHttp to handle the state change to ST_HEAD we wait for all the data to be sent based + on the amount sent and post size. + + Fixed redirect issue when we try to receive the body but a redirect happens by not trying to receive body + when redirecting. If we are redirecting we will be create a new request and by trying to receive the body + we put our request tracking in a bad state. We try to receive the response and then put our query async-op + into the pending state. This puts our async-op in a bad state for the next request that happens. + + ProtoHttpManager + Fixed requests failing on the GDK platform if pipelining is enabled by disabling this pipelining feature at + the httpmanager level. + + DirtySDK (Linux) + DirtyErrUnix + Fixed the parsing of the errorlist not to exit on an error of zero but use the list term as done on other + platforms. + + DirtySDK (PC) + DirtyNetWin + Fixed local address resolution when using a full tunnel VPN by using an address in the public space to do + the routing query. With the work from home directive some teams have started trying to use their work PCs + at home using the full tunneled VPN. On the windows version of dirtynet, the local address resolution tries + to use 192.168.1.1 for routing query. On home networks you are likely to pick a 192.168.1.x address which + is the non-VPN adapter's address which will fail to be used for prototunnel connectivity purposes, even + when the connection does not leave the host. + + DirtySDK (PS5) + DirtyContextManager + Added the cleanup of Http2 contexts during shutdown + + DirtyWebApiPS5 + Fixed a merge error. sceNpWebApi2AddHttpRequestHeader should be using the web request id instead. + + NetConnPS5 + Changed the default behavior of the shared dirtywebapi2ps5 to queue up the callbacks ('qcal' selector) + + DirtySDK (XboxOne) + VoipHeadsetXboxOne + Changed _VoipHeadsetAddLocalTalker()/_VoipHeadsetRemoveLocalTalker() to no longer invoke + _VoipHeadsetDumpChatUsers() directly but to now flag a boolean that will result in the users being + dumped from VoipHeadsetProcess() instead. This is to avoid possible concurrent execution of + _VoipHeadsetDumpChatUsers() from the socket recv thread (not locking ThreadCrit) and other threads locking + ThreadCrit, i.e. voip thread and main thread. + + +EADP Game Services DirtySDK 15.1.6.0.2 - February 14, 2019 + + Framework 8.02.01 + + Android AndroidSDK 100.00.00-pr1 + AndroidNDK 15.2.4203891 + android_config 5.00.00-pr1 + Capilano CapilanoSDK 180709-proxy + GDK GSDK 191100-proxy + Kettle kettlesdk 7.008.001-proxy + PlayStation4AppContentEx 6.508.010 + PlayStation4AudioInEx 6.508.001 + PS5 ps5sdk 0.90.00.24-proxy + NX nx_config 1.00.05 + nxsdk 9.0.0 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Win32 WindowsSDK 10.0.18362 + + *** Important - First Gen5 release supporting PS5 and GDK targets *** + This is the first DirtySDK release that supports Gen5 consoles. Most but not all functionality is supported. + For more detailed information please please contact Gameplay Services. + + *** Important - Required framework version updated to 8.x line in support of Gen5 consoles *** + +New in this release: + DirtySDK (All) + ConnApi + Added a debugging print to explain that we are connecting in passive mode and a comment explaining what that + is. + + NetGameDist + Added support for client side metrics (rpacklost, lpacklost, odpps, idpps, dist wait time, dis proc time) + + DirtySDK (GDK) + DirtyErrGDK + Added the WinHttp specific error mappings to the module. + + Added the errors from xsapi to the file but given that c++ hardcoded the values. + + Added errors that come from winerror that are also used in the xsapi's httpclient. + + NetConnGDK + Added support for checking the trial information. In the GDK, the request is always asynchronous and thus + we needed to introduce a control call to kick off the request. + + Added protoupnp support, strictly for informational purposes (no adding/removing port mapping as we rely on + Microsoft for that). + + Added support for communications check for the 'chat' selector + + PrivilegeApiGDK + Added support for privilege checks on the GDK + + ProtoHttpGDK + Added support for the WinHttp library backed implementation of ProtoHttp. + + Added support for send and recv rate throttles as supported on the socket and other protocols. These + throttles can be queried and controlled via the normal methods ProtoHttpStatus/ProtoHttpControl + respectively. + + ProtoUpnp + Added new macro 'dscp' for Gemini use - discovers/describes and finds port mapping but does not attempt to + add/delete one. + + Added new status flag PROTOUPNP_STATUS_FNDPORTMAP, used to indicate if a port mapping was found on the + device. + + UserApiGDK + Added support for pulling profile(s), presence and rich presence information via the userapi module. + + Added support for posting rich presence data. + + Added support for specifying the size of the avatar urls based on what we do on xboxone. + + UserListApiGDK + Added support for grabbing the social relationships on GDK without pulling any additional information. + + Added support for the avoid and mute list APIs. For this support we are treating avoid list as blocking as + the closest thing available on that platform. + + Added support for profile, presence and rich presence data lookup as part of the userlist. + + Added handling for the rate limiting errors for retry time. + + VoipHeadsetXboxOne + Added support for finding the audio endpoint on GDK builds for narration. + + VoipNarrateGDK + Added support for using XSpeechSynthesizer for doing local narration on GDK builds. + + DirtySDK (PS5) + DirtyWebApiPS5 + Added support for NpWebApi2 calls. + + DirtySDK (NX) + NetConnNX + Added support for setting the dirtysock thread's affinity using the startup param that is used on other + platforms. The 'affn' selector was also added like on other platforms to query our affinity. + + Implemented NetConnQuery based on implementation of other console platforms. Using the information we have + already in the module we can query the index based on nickname, uid or first logged in user. + +Changes in this release: + DirtySDK (All) + ConnApi + Fixed crash when the secure address on xboxone within our dirtyaddr is not properly set by checking the + validity of the getaddr call. + + CryptRand + Changed the module to use a single cryptrand.c source where possible for our various platforms as the + complexity of the solution was unnecessary. + + Changed the definitions of the CryptRandInit and CryptRandShutdown functions to be private + + DirtyThread + Fixed race condition crash / assert when the dirtysock core threads shut down by moving the logging before + the shutdown indicator. + + ProtoHttp2 + Fixed the truncation of the formatted header by ensuring space for the null terminator in our temporary + buffer we use for HPACK compression + + ProtoHttpManager + Fixed a crash in a print, because pHttpManager can be null after a disconnect in rare cases. + + ProtoHttpServ + Fixed boundary issues with chunk headers and trailers not being fully available when receiving a chunked + transfer. + + ProtoSSL + Fixed the behavior of the alerting when no certificate is received and one is required based on what is + specified by the spec. + + VoipDef + Added VoipHeadsetPreferredDefaultDeviceTypeE enum for use with VoipHeadsetStatus() control selector 'odft'. + + Changed comments to be platform agnostic for VOIP_LOCAL_USER_HEADSETOK, VOIP_LOCAL_USER_INPUTDEVICEOK and + VOIP_LOCAL_USER_OUTPUTDEVICEOK. + + VoipCommon + Fixed wrong exit condition for FOR loop that could result in out-of-bound access of + VoipConnectionListT::bTranscribedTextRequested[] array. + + VoipTranscribe + Fixed buffer resetting to be more consistently applied. + + Fixed an issue where SendFinish could fail to send when using IBM Watson and websockets, which could result + in a session timeout from that transaction as the server waits for the close or trailing silence, neither + of which were being sent, to complete the request. + + Fixed some cases that weren't going through ST_FAIL state properly. + + Fixed to reset record buffer on fail; this resolves some issues with a request following a failure. + + Fixed a rare case where the state machine could be stuck in ST_IDLE permanently. + + Fixed STREAM_END for HTTP2 when finishing a submission (Amazon/Google). + + DirtySDK (GDK) + DirtyCert + Fixed the port offset to use the correct one on all xbox platforms (xdk/gdk) + + ProtoHttpGDK + Fixed incorrect copying of data from ProtoHttpRecvAll after back to back requests by making sure we reset + our state to default. + + UserListGDK + Fixed a leak of the xbl context in the case we failed to kick off the async requests. + + VoipHeadsetXboxOne + Fixed the setting of the bitrate for gdk based on the documentation. + game_chat_audio_encoding_type_and_bitrate was changed to game_chat_audio_encoding_bitrate on the GDK. + + Fixed a bug where the users "text to speech conversion preference" was not being honored. + + DirtySDK (GDK, XboxOne) + VoipHeadsetXboxOne + Changed User^ calls to use 'xuid' and 'gtag' for compatibility with both GDK and XDK + + Removed the platform check for xboxone for bApplyRelFromMuting check on the voip thread. + + VoipXboxOne + Changed User^ calls to use 'xuid' and 'gtag' for compatibility with both GDK and XDK + + DirtySDK (NX) + DirtyNetNX + Fixed issues with NetConnStartup failures due no network connection on the switch + + NetConnNX + Removed the to do in _NetConnGetPlatformEnvironment(). Note: On NX there is no way for the client to + determine the environment. + + DirtySDK (PC) + VoipHeadsetPC + Changed VOIP_HEADSET_MAXDEVICES to 16 and updated some comments to no longer refer to only output + devices. + + Changed VoipHeadsetStatus() with control selector 'odft' to take an iValue of enum + VoipHeadsetPreferredDefaultDeviceTypeE for specifying whether to retrieve the default audio device or + the default voice communications device, and whether to set the DRVM_MAPPER_PREFERRED_FLAGS_PREFERREDONLY + flag. + + VoipPC + Changed print statements in voip status callback for consistency with changes in other platforms. + + DirtySDK (PS4) + DirtyErrPS4 + Added more webapi 2 errors specific to PS5. + + DirtyWebApiPS4 + Added DirtyWebApiRequestEx to allow the addition of request headers + + NetConnPS4 + Added mapping for CZECH, HUNGARIAN, GREEK, ROMANIAN, THAI, VIETNAMESE and INDONESIAN from sony + SCE_SYSTEM_PARAM_LANG_* to our LOBBYAPI_LANGUAGE_*. + + VoipHeadsetPS4 + Changed _VoipHeadsetUpdateStatus() to send VOIP_HEADSET_STATUS_INPUT to pStatusCb and changed + headset references to mic in places where only input device was being checked. + + VoipPS4 + Fixed voip status calback to no longer toggle output device status when an input device is inserted + or removed. Output device status is now set when local talker is registered and is not updated. + + DirtySDK (PS5) + NetConnPS5 + Added the 'pscx' and 'pscd' status selector to create/return a user's push context id and to delete a users + push context id. + + Fixed the gating of the chat restriction, the 'chat' selector returns FALSE when there is no restriction + instead of TRUE. + + Added mapping for CZECH, HUNGARIAN, GREEK, ROMANIAN, THAI, VIETNAMESE and INDONESIAN from sony + SCE_SYSTEM_PARAM_LANG_* to our LOBBYAPI_LANGUAGE_*. + + VoipOpus + Removed the hacks for building on PS5 for the speex package after we upgraded to the latest version. + + DirtySDK (XboxOne) + DirtyAddrXboxOne + Modified the DirtyAddrGetInfoXboxOne to validate that the address has been properly set before trying to + retrieve data. + + NetConnXboxOne + Added exception handling when adding a new NetworkStatusChanged event handler + + VoipHeadsetXboxOne + Changed _VoipHeadsetUpdateStatus() to send VOIP_HEADSET_STATUS_INPUT to pStatusCb and changed + headset references to mic in places where only input device was being checked. + + Changed _VoipHeadsetGetChatUserForVoipUser() to use to_wstring instead of snwprintf + + Fixed gamechat 2 audio buffers not being returned when switching between crossplay modes. + + Fixed first party mute to ignore adjuster none. Muting due to adjuster none is technically not a result of + first party settings. + + Fixed the way how user chat status is being handled for crossplay. + + VoipXboxOne + Fixed voip status calback to no longer toggle output device status when an input device is inserted + or removed. Output device status is now set when local talker is registered and is not updated. + +EADP Game Services DirtySDK 15.1.6.0.1 - October 9, 2019 + + EA Config 5.14.04 + Framework 7.19.00 + + Android AndroidSDK 28 + AndroidNDK r15c-1 + android_config 4.06.01 + Capilano CapilanoSDK 180709-proxy + capilano_config 2.07.02 + Kettle kettlesdk 6.508.001-proxy + kettle_config 2.11.00 + PlayStation4AppContentEx 6.508.010 + PlayStation4AudioInEx 6.508.001 + NX nx_config 1.00.05 + nxsdk 9.0.0 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Win32 DotNet 4.0-2 + DotNetSDK 2.0-4 + VisualStudio 15.4.27004.2002-2-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + DirtyPNG + Added paletted png decode support. + + NetGameDist + Added new status selector 'drop' to return the total dropped packets + + NetGameDistServ + Added a new NetGameDistServStatus2() function that takes more parameters. + + Added 4 new status selector for reporting stats + 'clnu': number of clients + 'drop': total dropped input packet count + 'idps': total input dist packet count + 'odmp': total output dist multi packet count + + Added new control selector 'rsta' to reset stat variables + + ProtoSSL + Added new ECDSA Gameplay Services Root CA for future use. + + VoipNarrate + Added VoipNarrateControl('ctsm') to clear text to speech metrics in VoipTextToSpeechMetricsT + This is used in a fix where incorrect tts metrics may be reported, to make use of this fix update BlazeSDK + to trama.1. + + VoipTranscribe + Added VoipTranscribeControl('cstm') to clear speech to text metrics in VoipSpeechToTextMetricsT + This is used in a fix where incorrect stt metrics may be reported, to make use of this fix update BlazeSDK + to trama.1. + + DirtySDK (XboxOne) + VoipConnection + Added VoipConnectionlistT::bApplyRelFromMuting. + +Changes in this release: + DirtySDK (All) + DirtyJPG + Fixed a crash when attempting to decode an unsupported JPEG variant; an error is returned instead. + + NetConnCommon + Fixed the external list processing potentially being processed by separate threads which can cause double + destroys by using a crit. + + ProtoSSL + Fixed to add limited unicode support (truncation of 16bit ASCII characters to 8bit ASCII) to ASN.1 + certificate string parsing. + + Fixed bug that prevented an ECDSA root ca from working. + + Voip + Fixed a bunch of status/control selector not retuning proper error codes causing log spam. + + Fixed how local voip users set their crossplay flag, which prevents the receiving side from printing this + incorrect log: + voipconnection: [0] error, platform 2, persona 1000000517837 attempted to join, but cross play is not + enabled remotely. + + VoipBlockList + Improved VoipBlockListIsBlocked, when passed a VOIP_SHARED_USER_INDEX it will check all the local users to + determine if iBlockedAccountId should be blocked. + + Fixed destruction, by caching the memgroup information and use it on destruction instead of re-querying it. + + VoipCodec + Added 'plvl' status selector to support querying of current audio power level as calculated for VAD. + + VoipCommon + Removed superfluous conditions in VoipCommonUpdateRemoteStatus as the same conditions are being checked + in VoipConnectionSetSendMask() and VoipConnectionSetRecvMask() + + Fixed default channel selection for the shared user. The default behavior is to have the shared user + have the most restrictive set of channels between all the local users and blocking communication on the + shared user if there are no channels shared by all of the local users. + + VoipConnection + Changed remote user registration path to now flag that reapplying channel config is necessary + (for join-in-progress only). + + Fixed invalid behavior in the voipconnection due to buffer overrun when making shared user updates by using + the correct shared user index. + + DirtySDK (iOS) + NetConnIos + Fix crash in netconnios when attempting to call into netconn common, but it has no ref to do that. + + DirtySDK (NX) + DirtyErrNX + Changed all the error codes to use Nintendo specific ones. + + DirtyNetNX + Fixed the destruction of the hostname cache to happen after the last _SocketIdle call to ensure we don't + crash on shutdown. + + DirtySDK (PC) + VoipHeadsetPC + Fixed _VoipHeadsetSendEncodedAudio() to use a memcleared voipUser for remoteUser instead of an empty string + when loopback is enabled. + + Fixed 'aloc' selector incorrectly returning error code upon user going from 'participating' to + 'not participating'. + + VoipNarratePC + Fixed crash if CoInitialize failed by setting the failure state and not destroying the ref. + + VoipPC + Removed dead code from _VoipActivateLocalTalker(): inappropriate error handling for 'aloc' selector + because it can't return a negative value. + + DirtySDK (PS4) + DirtyErrPS4 + Removed the errors that no longer exist in SDK7.000 conditionally to maintain backwards compat with + SDK6.500 + + DirtySessionManagerPS4 + Fixed an issue where the meta data on the available invites would not be updated if the invite was received + while we were in the console's REST state, this prevent the user from successfully joining the invite. + Now upon attempting to join an invite from the system software we will no longer assume the list is up to + date. + + ProtoHttpPS4 + Fixed redirects failing due to not being able to parse the location header. + + DirtySDK (XboxOne) + ProtoMangelXboxOne + Fixed crash in the IncomingAssociationEventHandler by validating that the module ref is still valid and + unregistering the event handler like we do in other areas of DirtySock. + + Fixed exception that gets thrown when trying to access a security association that was cleaned up by the + remote peer by not accessing the state. + + Fixed crash when mutating the security association vector between the event handler and main thread by + protecting the writing with a crit. + + Moved the manipulation of the list before we start the destruction and protect with the new global state + crit to ensure the operation has succeeded to prevent invalid access to a killed crit. + + UserApiXboxOne + Added handling of http errors code in _UserApiProcessProfileResponse + + VoipCommon + Fixed setting bApplyRelFromMuting whenever send masks have been altered, this makes it so the communication + relationship is reflected by the change of channels. Prior to this bApplyRelFromMuting was only being set + when a user was activated, which could result in updates for mute/unmute, not working. + + Fixed the 'umic' and 'uspkr' control selector to set bApplyRelFromMuting to reflect changes in title code + mute. + + VoipConnection + Changed remote user registration path to now flag that reapplying communication relationships is + necessary (for both initial connect and join-in-progress). + + VoipConnection/VoipXboxOne + Code reapplying communication relationships was moved from VoipConnectionSetSendMask() and + VoipConnectionSetRecvMask() to the _VoipThread(). + + VoipHeadsetXboxOne + Fixed to capture sttEventCount and sttCharCountRecv metrics. + + Removed the comment block around code dealing with first party mute. First party mute will now work as long + as you are on XDK July QFE 9. + + Fixed to allow enabling/disabling STT functionality with the 'tran' control for crossplay mode, when you + are not yet in crossplay mode. + + VoipXboxOne + Modified local user registration and activation paths to now flag that reapplying communication + relationships is necessary. + +EADP Game Services DirtySDK 15.1.6.0.0 - July 24, 2019 + + EA Config 5.14.04 + Framework 7.19.00 + + Android AndroidSDK 28 + AndroidNDK r15c-1 + android_config 4.06.01 + Capilano CapilanoSDK 180702-proxy + capilano_config 2.07.02 + Kettle kettlesdk 6.508.001-proxy + kettle_config 2.11.00 + PlayStation4AppContentEx 6.508.010 + PlayStation4AudioInEx 6.508.001 + NX nx_config 1.00.04 + nxsdk 7.2.1 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Win32 DotNet 4.0-2 + DotNetSDK 2.0-4 + VisualStudio 15.4.27004.2002-2-proxy + WindowsSDK 10.0.10586-proxy + + *** Important - First crossplay compatible release *** + This is the first DirtySDK release that supports crossplay. For more information please refer to our + crossplay integration guide : https://developer.ea.com/display/TEAMS/Cross+Play+Integration+Guide + https://developer.ea.com/display/dirtysock/VoIP+Tech#VoIPTech-CrossplayConstraints + Crossplay is NOT SHIPPABLE for this release. + +New in this release: + Contrib (ALL) + VoipOpus + Changed VoipOpus to be available on pc, ps4 and xone platforms; for use in cross play mode. + + Added speex resampler, to convert to a standard sample rate when needed (for xone). + + Added the _VoipOpusControl selectors: + 'infl' - Set if the input samples are float via iValue, (default int16) + 'inin' - Set if the input samples are int32_t via iValue, (default int16) + 'insr' - Set the input sample rate (default 16000) + + DirtySDK (All) + NetGameDistServ + Added the ability to configure a logging callback which allows logging using a different mechanism. + This is used in cases where we want to log to a different logger on release builds that don't have debug + logging. + + ProtoHttp + Added an explicit failure code to report a failure due to the maximum number of redirections being + exceeded. + + ProtoSSL + Added the support for enabling and disabling of individual curves. + + Added debug print of from/till times if a certifiicate is determined to be expired. + + Voip + Added the ability to set the thread affinity for the VoIP thread when it differs from DirtySDK. + + VoipBlockList + Added VoipBlockList, when integrated with Social's "Muting Web API" produces a cross platform persistent + muting feature. An example of its usage is found in BlazeSDK\samples\Ignition\Friends.cpp. The + VoipBlockList is created when VoipStartup is called, game code is responsible for calling VoipBlockListAdd, + VoipBlockListRemove, and VoipBlockListClear as needed to keep the DirtySDK representation of the mute list + up to date. It is possible to call VoipBlockListIsBlocked to validate if communication is blocked between + the two users. All of these functions operate on a local user index and an account id of the muted user. + + DirtySDK (PC) + VoipHeadsetPC + Added '-pbk' and '+pbk' selectors which were available on PC and XOne, making muting functionality + consistent. + + DirtySDK (PS4) + VoipHeadsetPS4 + Added cross-play support. + + DirtySDK (XboxOne) + NetConnXboxOne + Added 'tsbx' selector, allowing override of test sandbox name. This obviates the need for hosting a file + to specify the sandbox name, although that capability remains. + + VoipXboxOne, VoipHeadsetXboxOne + Added cross-play support. + +Changes in this release: + DirtySDK (All) + CryptChaCha + Changed the implementation of Encrypt and Decrypt to skip Poly1305 if NULL is passed to pTag. In some cases + we just want to use ChaCha20 to encrypt and decrypt, while not needing to use Poly1305 for MAC. + + Optimized the block operation by using memcpy to eliminate an unnecessary loop. + + CryptNist + Changed the ECDSA signing operations to generate the signatures deterministically by generating k based on + the message hash, private key and curve parameters. + + Decreased the size of the private key state, the largest we will need is 48 bytes. + + CryptRand + Improved our PRNG implementation using CryptChaCha. Since each platform we target has a method to get random + bytes from their entropy pool and our fallback is not secure, we have opted to move to a new system of + generating random data. In this new system we use a small amount of random bytes fro the ChaCha cipher key + and IV (nonce) to generate pseudo random bytes. + + DirtyThread + Suppressed the 4702 warning around our main thread which caused issues when LTCG is enabled. + + Removed the call to ThreadEnd as the EAThread Mutex assert issue came back on newer versions. The call is + not needed for the trouble it has caused us. + + NetConn + Added NetConnAccountInfo struct that stores user's nucleus account id and persona id. 'acid' and 'peid' control + selectors will be used to populate it. + + ProtoHttpUtil + Fixed ProtoHttpUrlDecodeStrParm() to reserve space for terminating null character (preventing possible + overflow of the null). Also fixed to not read past the end of the input buffer if a percent character + was the second-to-last or last character. + + ProtoHttp2 + Enhanced the header formatting to use the available space in the output buffer for formatting. This allows + for formatting much larger headers without extra allocations. + + ProtoSSL + Changed the module to use platform ds_strsplit and removed the _ProtoSSLSplitDelimitedString. + + Removed CA certificates with < SHA-256 hash, with the exception of VeriSign Class 3 Public Primary + Certification Authority - G5 which is still required by C&I. Additionally removed VeriSign Universal Root + CA that never saw use. + + Fixed server to not automatically enable all TLS1.3 ciphers if TLS1.3 is enabled and no TLS1.3 ciphers + are enabled. + + Voip + Removed VoipRemove and VoipConnectionRemove, these functions are no longer called. + + Moved internal functions in voip.h to voipcommon.h which is where our internal/private APIs are located. + + Moved VoipActivateLocalUser to VoipCommon to make sure that customers are calling VoipGroupActivateUser. + + VoipCommon + Removed the controls in voipcommon that are used internally to prevent customers from calling them. Instead + of calling the controls, functions were added that are accessible to the source layer of the voip subsystem. + + Moved the VoIP memory function to be part of VoipCommon and removed the implementations for VoipPriv. + + Removed the NULL check for voipcommon state in _VoipCommonTextDataCb as the state will always be valid. + Headset who calls this callback is owned by VoipCommon, thus it cannot be NULL when getting a callback. This + code trips coverity defects because it assumes if you check here you have to check in the later parts of the + function. + + Removed VoipCommonStatus('ruid') and added 'rcvu', since this no longer returns the remote user id. 'rcvu' + returns the "remote connection voip user" + + VoipConduit + Added support for multiple VoIP mixers. + + Added support for controlling playback (used for muting) through a playback callback. + + VoipConnection + Fixed crash when invalid user index is passed into VoipConnectionSend by early returning in this case. + + Changed the shared user index sent to remote players to VOIP_SHARED_REMOTE_INDEX which -1. + + VoipHeadset + Removed VoipHeadsetStatus('ruid') and added 'ruvu', since it does not operate on id's. 'ruvu' returns (TRUE) + if the "remote user voip user" is registered in voip headset. + + VoipPriv + Removed VoipTimerT as it is no longer used. + + Changed VoipUserT to contain only the NetConnAccountInfo, voip user flags and the platform type of + the voip user. It will no longer contain first party IDs. + + Changed VOIP_NullUser and VOIP_SameUser macros to compare persona IDs. + + DirtySDK (PC) + CryptRand + Switched PC to use BCrypt to align with XboxOne. + + VoipHeadsetPC + Removed the default setting of the volume to 90 on the audio output device. This was interfering with game + settings. + + DirtySDK (PS4) + DirtyWebApiPS4 + Fixed DirtyWebApiAddPushEventListener* so it returns a valid id to be passed back to + DirtyWebApiRemovePushEventListener. + + DirtySDK (NX) + ZFileNX + Removed zfilenx as it is identical to unix outside of one function which we ifdef out on that platform. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Enhanced implementation of _ProtoHttpXmlHttpWritingToOutstream() for more robustness. Previous + implementation assumed that iBlockingOpsThreadResult would always contain a number of bytes accumulated + equal to the number of bytes originally submitted. After reviewing the implementation of + SequentialStream::Write(), this assumption proved to be luckily correct (thus this CL being on ml only + and not being associated with a critical ticket). But it was decided to change the implementation of + _ProtoHttpXmlHttpWritingToOutstream() to avoid coding pattern based on an "implicit" assumption. + With the new implementation, things will still work in the future if SequentialStream::Write() is + modified and no longer behave the same (i.e. it could result in partial accumulation). + + ProtoMangleXboxOne + Removed entering/leaving MangleCrit in the lambda executed upon completion of the creation of a security + association. It was left over from a previous implementation and it was no longer protecting any specific + thread safety access. + + VoipCommon + Fixed an issue in _VoipCommonReceiveTextDataNativeCb preventing local user from seeing their own Speech To + Text, requires QFE5 of the July 2018 XDK + + VoipDef, VoipGroup + Removed VoipCbTypeE::VOIP_CBTYPE_AMBREVENT (xboxone-only bad rep auto mute event). It had been unused + since rebasing voip xboxone to game chat 2 (i.e. since Shrike.0). + + Removed xboxone-specific fields from VoipSynthesizedSpeechCfgT because the 'voic' control selector is + no longer usable on xboxone since the introduction of MS Game Chat 2 in Shrike.0. + + VoipHeadsetXboxOne + Changed implementation to comply with game chat 2 reference doc (Xbox Live API Compiled HTML Help) and + MS sample code: only call synthesize_text_to_speech() if text_to_speech_conversion_preference_enabled() + is true for the originator. + + Fixed XMA/audio issues experienced by customers by setting the DO_NOT_USE_SHAPE flag when initializing + XAudio2 for use with text narration. Based on the conversation with NHL and the Frostbite Audio team. By not + using DO_NOT_USE_SHAPE we cause the current xma allocations to have bad data. + +EADP Game Services DirtySDK 15.1.5.0.2 - June 20, 2019 + + EA Config 5.14.04 + Framework 7.19.00 + + Android AndroidSDK 28 + AndroidNDK r15c-1 + android_config 4.06.01 + Capilano CapilanoSDK 180702-proxy + capilano_config 2.07.02 + Kettle kettlesdk 6.508.001-proxy + kettle_config 2.11.00 + PlayStation4AppContentEx 6.508.010 + PlayStation4AudioInEx 6.508.001 + NX nx_config 1.00.04 + nxsdk 7.2.1 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Win32 DotNet 4.0-2 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + DirtyNet + Added the 'poln' control to do a blocking poll specified in nanoseconds. + + Added a SocktetHostnameCacheProcess function to allow us to expire entries from idle to fix entries never + expiring. + + DirtySDK (PS4) + NetConnPS4 + Added play-first trial check using NetConnStatus('pftr') selector. This capability is disabled by default, + use -D:dirtysdk-playfirsttrial=true when generating the solution to enable. Requires the + PlayStation4AppContentEx package. + + Changed 'tria' selector to additionally return TRUE if play-first trial is enabled. + + VoipHeadsetPS4 + Added two new public api to be invoked from voipcommon: VoipHeadsetTranscribedTextPermissionCheck() + and VoipHeadsetSetTranscribedTextReceivedCallback(). + + Added a new callback synopsis: VoipHeadsetTranscribedTextReceivedCbT. + + DirtySDK (XBoxOne) + ProtoHttpXboxOne + Added a period of time before we delete the XmlHttp state to prevent accessing invalid memory from the + HttpCallbacks. There is a small window after the COM memory has been released where callbacks can still + be fired. During this period we want to make sure that both the XmlHttp and HttpRef state is still valid + to prevent crashing. This change will increase the time it takes for the kill list to be cleared. + +Changes in this release: + DirtySDK (All) + Build + Removed the vccorlib includes to the sample program module as this is no longer needed vs2015+. + + ConnApi + Fixed the setup of demangling (and unsetting of addresses) when the game is server hosted but voip is not. + When Xbox Clients setup their dedicated server client in the client list, the address fields in the client + info are set to 0. This causes the connections to the dedicated server to failed because of the invalid IP + addresses. + + ProtoHttpServ + Fixed incorrect linked list manipulations in _ProtoHttServUpdateListen(). Problem description: Upon + inserting a new httpthread entry in the httpthread list, if that list already has more than one entry, + the new entry would incorrectly be inserted after the first entry as the tail of the list... resulting + in the rest of the list being lost. As a knock-on of this bug, CCS-DirtyCast ssl connections would no + longer be cleaned up properly... resulting in stunnel spamming with: + LOG3[1301]: transfer: s_poll_wait: TIMEOUTclose exceeded: closing + + ProtoSSL + Fixed TLS 1.3 client to return illegal_parameter alert instead of handshake_failure when no cipher is + selected. + + Fixed TLS 1.3 client to validate server session id value is empty when the session id sent was empty. + + Fixed TLS 1.3 client to support middlebox compatibility mode for session_id field. + + Fixed TLS 1.3 client to validate Protocol Version or Supported Versions extension. + + Fixed a client bug when a client cert was sent on a previous connection attempt; a subsequent connection + attempt where a client cert was not sent would result in the client trying to send a CertificateVerify + handshake message and crashing with a null-pointer exception. + + Fixed TLS 1.3 client/server to abort on non-null compression value. + + Fixed TLS 1.3 server to return unexpected_message alert instead of no_negotiation when receiving a Hello + Request message. + + Fixed TLS 1.3 server to validate ClientHello version is at most TLS1.2 before parsing supported versions + extension, which previously could overwrite an invalid version before the version was validated. + + Fixed to move bCertSent/bCertRecv from the state ref to the secure state ref, renamed to bSentCert/bRecvCert. + These state booleans make more sense being in the secure state, since they track the status of a specific + session, and also benefit from being automatically reset when a new connection is attempted, which is not + the case with the overall state. + + Fixed to add handshake state transition validation to prevent invalid messages from causing exceptions, + e.g. if a KeyExchange message is sent before ClientHello. + + Fixed to add protection for a server giving us an unsupported elliptic curve (a spec violation). + + Fixed to add protection for a hello extension with an extension size extending past the end of the hello. + + Fixed to remove SSL3_MSG_HELLO_RETRY_REQUEST define, as it is not an explicitly defined handshake message, + and add SSL3_MSG_END_OF_EARLY_DATA handshake define for future reference/use. + + VoipCodec + Fixed VAD to consider all samples in a frame, instead of just the first. Found by inspection. + + VoipGroup + Fixed incorrect low-level conn id to high-level conn id translation logic in + _VoipGroupManagerDisplayTranscribedTextCallback() + + VoipTranscribe + Fixes for some possible sources of metrics issue observed in production. + + DirtySDK (NX) + NetConnNX + Fixed the shutdown by properly clearing the global ref. + + DirtySDK (PC) + VoipNarratePC + Suppressed the warnings from the windows headers which can cause issues on higher warning levels. + + DirtySDK (PC, PS4) + VoipHeadsetPC,VoipHeadsetPS4 + Fixed crash when TTS is enabled but STT is disabled when doing narration by checking for NULL before calling + the 'vdis' control. + + DirtySDK (PS4) + NetConnPS4 + Fixed _NetConnRequestAuthCode() and _NetConnNpAsyncRequest() not returning a negative when an error is + encountered. + + ProtoHttpPS4 + Removed the call to sceHttpAbortRequest which breaks following redirects in certain scenarios. + + VoipCommon (PS4) + Fixed chat restriction not being properly enforced for inbound transcribed text (fix is inspired from + original MS game chat dll implementation). When inbound transcribed text is received from the network, + voipcommon now passes it to VoipHeadset with a call to VoipHeadsetTranscribedTextPermissionCheck(). + If the permission check passes, then VoipHeadset passes the text back to VoipCommon by invoking the + new VoipHeadsetTranscribedTextReceivedCbT callback previously registered with + VoipHeadsetSetTranscribedTextReceivedCallback(). If the permission check fails, VoipHeadset + instead just drops the text and never invokes the VoipHeadsetTranscribedTextReceivedCbT callback. + + DirtySDK (Unix) + DirtyNetUnix + Fixed _SocketPoll(), when processing ppoll result, incorrectly including sockets that were not originally + included in the poll. Potential knock-on: process showing 100% CPU usage caused by ppoll continuously + unblocking immediately. + + VoipUnix + Changed implementation of VoipSetLocalUser() to initialize the local user with user id "dummy_voip_user" + instead of the string returned by NetConnMAC(). This change was done to work around NetConnMAC() not + always returning a valid string when used on a VM because the name of the NIC does not match what + NetConnMAC() is looking for (eth0, eth1 or wlan0). See implementation of _SocketGetMacAddress() in + dirtynetunix. NetConnMAC misbehaving results in VoipSetLocalUser() creating 'empty' voip user id, and + the stress client no longer sending voip traffic over established voip connection because the send mask + is incorrect as a result of the user id being NULL. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Fixed use after free crash in _ProtoHttpProcessKillList by moving the _ProtoHttpUpdateXmlHttp to be the last + function that is called within the loop. + + UserApiXboxOne + Fixed read access violation when growing the response buffer after the second PROTOHTTP_RECVBUFF by only + copying the exact amount of data in the source buffer. + + VoipHeadsetXboxOne + Enhanced implementation of _VoipHeadsetSetRemotUserOutboundMute() and + _VoipHeadsetSetRemotUserInboundMute() to guard against the possibility of + pRemoteChatUser->custom_user_context() being NULL. + + VoipNarrateXboxOne + Changed the AsyncStatus to be fully scoped which fixes build errors in certain conditions. + +EADP Game Services DirtySDK 15.1.5.0.1 - April 9, 2019 + + EA Config 5.14.04 + Framework 7.19.00 + + Android AndroidSDK 28 + AndroidNDK r15c-1 + android_config 4.06.01 + Capilano CapilanoSDK 180702-proxy + capilano_config 2.07.02 + Kettle kettlesdk 6.008.001-proxy + kettle_config 2.11.00 + PlayStation4AudioInEx 6.008.001 + NX nx_config 1.00.04 + nxsdk 7.2.1 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Win32 DotNet 4.0-2 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + + *** Important - First DirtySDK release with NX Support *** + This is the first DirtySDK release with official NX support. Currently VoIP, UserApi and UserListApi are not + supported on this platform. + + *** Important - API Breaking Change *** + The VoipDisplayTranscribeTextCallback function prototype has been changed to return an int32_t instead of + returning void. This enables the user callback to signal that text has been rendered, and prevent the text + from possibly being rendered again from a different VoipGroup. + +New in this release: + DirtySDK (All) + Aws + Added keyPath to signing info. + + Added reading and writing of signed binary events, used in transcription. + + Voip + Added new VOIP_CBTYPE_TTOSEVENT event type to VoipCallback; this event signals the start and end of TTS. + It can be used to temporarily override a push-to-talk system to allow the audio to be sent to the other + player(s) without requiring push-to-talk to be manually activated by the user. + + VoipPC,VoipPS4 + Added auto-muting of audio input when send mask is zero. This prevents STT from taking place when the user + is muted, e.g. in a push-to-talk system when PTT is not engaged. + + Added polling of TTOS status and updating the VoipCommon status variable that tracks it. + + VoipCommon + Added tracking of ttos status, and triggering of VOIP_CBTYPE_TTOSEVENT when ttos status changes. + + VoipHeadsetPC,VoipHeadsetPS4 + Added 'ttos' status selector to indicate when text-to-speech is active. + + Added 'mute' control and status selector to mute input audio, used to prevent transcription when + a player is not sending any audio. + + Added 'spam' pass-through to VoipTranscribe, to allow setting debug level of that module. + + VoipTranscribe + Added Amazon Transcribe support. + + Added 'vdsc' control selector to enable/disable the short audio discard behavior that is enabled by + default. This is used by the VoipHeadset layer to disable short audio discard during TTS, which + would otherwise prevent STT of short TTS phrases from working. + + DirtySDK (NX) + Build + Added NX support + + DirtySDK (PC) + VoipHeadsetPC + Added support for 'txta' control which is used to control how text chat accessibility functions in the context of loopback. + + DirtySDK (PS4) + VoipHeadsetPS4 + Added support for the 'txta' control to change the behavior when using text chat accessibility features. + + DirtySDK (XboxOne) + VoipNarrateXboxOne + Added the narrate module on xboxone to perform non-voip based narration that can be used for text chat. + +Changes/fixes in this release: + DirtySDK (All) + Aws + Fixed xcode build warnings due to truncation. + + ProtoHttp2 + Changed to increase header cache size from 1k to 2k; this allows sending and receiving larger headers. + + Changed the implemention to ignore frames from untracked (closed) streams. + Per the spec: WINDOW_UPDATE can be sent by a peer that has sent a frame bearing the END_STREAM flag. + + Fixed append header rejects new headers if the previous header values were appended to by switching to + strcmp from strncmp. + + VoipDef + [API BREAKING] Changed VoipDisplayTranscribedTextCallback to return an int32_t instead of void. This + allows the application to indicate when it has displayed transcribed text, and prevent further callbacks + against other voipgroups with the same text. + + VoipGroup + Changed the implementation such that the voipgroup layer registers its 'display transcribed text' + callback with the underlying voip sub-system as soon as the voipgroup manager gets created, and such + that the voipgroup layer only unregisters that callback upon destruction of the voipgroup manager. + This change is inspired from the way the 'event' callback is registered/unregistered and it is necessary + to avoid a possible situation where the 'display transcribed text' callback gets called from the + voip thread after the voipgroup manager got destroyed, which can happen if the voipgroup manager is + destroyed after text is submitted for transcription but before the transcription result is received. + + Removed incorrect usage of critical section from VoipGroupSetDisplayTranscribedTextCallback(). Potential + deadlock before this fix: + --> voip thread locking pVoip->Common.ThreadCrit first from _VoipThread() and then blocking on + NetCritEnter(&pManager->crit) from _VoipGroupManagerEventCallback() + --> main thread locking pManager->crit first from VoipGroupSetDisplayTranscribedTextCallback() + and then blocking on NetCritEnter(&pVoipCommon->ThreadCrit) from + VoipSetDisplayTranscribedTextCallback() + + Changed _VoipGroupManagerDisplayTranscribedTextCallback() to respond to the new + VoipDisplayTranscribedTextCallback return parameter and stop iterating through voipgroups if the callback + returns TRUE. + + VoipNarrate + Fixed an issue formatting the Microsoft request body where the format string and parameter count didn't + match. + + Fixed to release any queued requests on destroy. + + Changed VOIPTRANSCRIBE_INPUT_MAX to VOIPNARRATE_INPUT_MAX based on coding standards. + + VoipTranscribe + Fixed a bug where the STT uDurationMsSent metric was not set to the correct value, often being 0. + + Fixed a bug with google+http encoding where the Base64 request could be terminated too early, resulting in + a 400 server error. + + Fixed an issue parsing google error response. + + Changed to increase the size of the JSON parse buffer to be able to handle large Amazon responses. + + Changed to increase the ref response buffer substantially to be able to handle large Amazon responses. + + Changed Watson parsing to use the ref parse buffer instead of allocating dynamically. + + Fixed to allow specification of a non-encoded Watson apikey, as VoipNarrate does. Pre-encoded Watson + apikeys are still supported, but will be deprecated in the future. + + Fixed Watson/Websockets transcription request failing if the server has timed us out since the last + request. + + Added smart_formatting enable for Watson (e.g. "one two three" becomes "1 2 3"). + + Fixed to eat %HESITATION markers from Watson transcription result, if present. + + Fixed a metrics issue where a buffer could be submitted every VoipTranscribeUpdate() execution while + receiving a response, if a very large untranscribable buffer was previously submitted. The impact of this + issue was that metrics would be ticked every frame and very quickly result in bogus metrics with hundreds + of imaginary transactions spanning thousands of minutes. The fix is to wait to submit until the ongoing + receive operation is complete. In practice, this situation is very hard to trigger and involves many + consecutive empty transaction results and a maximum backoff timer of 60s. + + VoipTunnel + Fixed the voice "squelching" logic to not only release the appropriate rebroadcasting slot(s) when the + voice originator stops talking, but also when the voice originator mutes a subset of the players + (voice consumers) to which he is talking. + + DirtySDK (PC) + VoipDef + Changed PC VOIP_MAXLOCALUSERS from 1 to 4, to match NETCONN_MAXLOCALUSERS + + VoipHeadsetPC + Fixed the 'voic' selector and improved voipnarratepc to support other languages with the voic selector. + + Changed PC voip to allow multiple local users, there is still only one device, so there may only be one + active user at a time. Recording, playback, transcription, STT/TTS metrics only function for the active + user index. + + Changed VoipHeadsetControl 'aloc' which now takes and store the active user index. + + Fixed transcription buffer size to use define instead of being hardcoded at 256. + + Fixed assignment of iDebugLevel to be done only under DIRTYCODE_LOGGING=1 + + VoipPC + Changed so that all users on a PC will have the same headset status. + + Changed VoipSetLocalUser to take user index into account (like other platforms). + + Changed VoipActivateLocalUser to take user index into account (like other platforms) however with the added + limitation that only one user can be active at a time, if the active user becomes deactivated a new user + will be selected if possible. + + DirtySDK (PS4) + VoipHeadsetPS4 + Fixed severe stuttering audio issue with PS4 camera voip, broken in audio input pipeline refactor in + Raven.5. + + DirtySDK (Unix) + DirtyNetUnix + Moved the SocketHost logging behind a verbosity check to reduce spam that is not helpful in most cases. + + DirtySDK (XboxOne) + ConnApi + Fixed the voip server dedicated flow to not perform unnecessary demangling on Xbox One. + + DirtyAuthXboxOne + Fixed potential deadlocks by adding the missing NetCritLeave calls when returning early due to errors. + + ProtoHttpXboxOne + Fixed ProtoHttpSend() with an implicit call to ProtoHttpUpdate() to allow internal state machine to be + pumped when ProtoHttpSend() is called repeatedly without intermediate calls to ProtoHttpUpdate(). + + Changed implementation such that ProtoHttpPost(), ProtoHttpPut() and ProtoHttpPatch() can be used without + a mandatory subsequent call to ProtoHttpSend() as it used to be supported prior to Shrike.0. Fix consists + in buffering request data passed to ProtoHttpRequestCb2() into a new "pre-send" buffer. A new PRESEND state + is also introduced to accumulate the content of the pre-send buffer into the xmlhttp object's outbound + sequential stream before accepting any new data from the user subsequently calling ProtoHttpSend(). + + Fixed a bug introduced in Shrike.0 which included a ProtoHttpXboxone refactor to improve the way + potentially long blocking MS calls are being offloaded to worker threads. The new solution broke an edge + case involving request redirection. Upon some race conditions, a request redirection may be incorrectly failed. + + Fixed the possibility of an xmlhttp req being stalled in state + ProtoHttpXmlReqStateE::REQ_ST_WAITING_RESPONSE when a race condition is exercised: xmlhttp req's event + handler modifying eNextState before the main thread initializes it to REQ_ST_INIT. + + Fixed a crashed in OnError by adding a check for bWaitingForAsyncCompletion in _ProtoHttpProcessKillList(). + bWaitingForAsyncCompletion must be false before xmlHttp can transition states + +EADP Game Services DirtySDK 15.1.5.0.0 - January 23, 2019 + + This SDK release was built using the following compiler packages: + + EA Config 5.14.04 + Framework 7.19.00 + + Android AndroidSDK 26.0.2-3 + AndroidNDK r13b-1 + android_config 4.03.00 + Capilano CapilanoSDK 180702-proxy + capilano_config 2.07.02 + Kettle kettlesdk 6.008.001-proxy + kettle_config 2.11.00 + PlayStation4AudioInEx 6.008.001 + iPhone ios_config 1.14.00 + OSX osx_config 1.20.01 + Linux/Unix UnixClang 4.0.1 + Win32 DotNet 4.0-2 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + + *** Important - PUBLIC API IMPACTING - *** + + Reference: https://developer.ea.com/display/dirtysock/DirtySock+Shrike+Upgrade+Guide + + CryptMD2 has been removed as MD2 support is obsolete. + + Removed VOIP_LOCAL_USER_MICON which has been unused since PS2 era. + + DirtySDK voip for xboxone now use MS Game Chat 2 + * Game chat 2 now needs to be added as a sdkreference in nant build scripts. This step is needed for the + game chat 2 dll to be copied to the deployment directory as a post build step. + + Xbox.Game.Chat.2.Cpp.API, Version=8.0 + + + * Because MS game chat 2 is internally dealing with retrieval of XBL accessibility settings for the user, + DirtySDK XboxOne voip no longer needs to be fed with that info by the game code. + + * DirtySDK XboxOne voip is no longer displaying inbound transcribed text in system UI if + game code does not register a display transcribed text callback. (New behavior resulting from + VoipHeadsetXBoxOne being rebased from from windows::xbox::chat to MS game chat 2) + + * DirtySDK XboxOne voip no longer supports the feature originally requested by the EA accessibility team: a + hearing-impaired person requesting transcribed text from other players also wants to see transcribed text + for his/her own speech. (New behavior resulting from VoipHeadsetXBoxOne being rebased from from + windows::xbox::chat to MS game chat 2) + +New in this release: + DirtySDK (All) + AWS + Added new module to supply various utility functions in support of connecting to AWS services, starting + with AWS Signature Version 4. + + ConnApi + Added the game / voip topology configuration to the module, which allows complexity that existed in the + network adapter to be moved down. + + Added status selectors for the configured timeout values. + + CryptBn + Added the CryptBnBitAnd and CryptBnBitXor which performs bitwise and; bitwise xor operations + + CryptCurve + Introduced cryptcurve, an interface over the multiple curve modules we support. + + CryptEcc + Added a new parameter to CryptEccPointFinal to let us know if we are encoding the shared secret into a + buffer. + + CryptHash + Added the ability to lookup the hash based on the size. + + CryptMont + Added support for X25519 and X448 curves based on RFC7748. + + DirtyLib + Added the ability to set an option for crits via NetCritInit2 and added an option to enable a crit in + single-threaded mode for specific multi-threaded scenarios (SocketLookup). + + Added NetLibCrc32 to calculate CRC32, with optional table parameter. + + DirtyMem + Added new VOIP_PLATFORM_MEMID memory identifier. + + Plat-Str + Added new string functions ds_fmtoctstring_lc, ds_strtolower, ds_strtoupper, ds_strtrim, and + ds_strlistinsert in support of AWSV4 signing. + + Plat-Time + Added TIMETOSTRING_CONVERSION_ISO_8601_BASIC to supported ds_timetostr conversion options. + + ProtoHttp2 + Added the ability to decode the debug data in GOAWAY frames. + + ProtoHttpUtil + Added ProtoHttpUtilParseRequest() for parsing the first line of an HTTP request, functionality refactored + from _ProtoHttpServParseHeader() + + ProtoHttpServ + Added new ProtoHttpServControl2 API to allow setting SSL settings specific to a ProtoHttpServ thread. + + Protomangle + Added the service name to the demangler query for better tracking + + ProtoTunnel + Added control to allow setting the behavior for trying a random port on bind failure. + + Added control selectors for maximum number of receive calls per update and idle callback rate. + + VoipConnection + Added support for "reliable opaque" data type needed for sending/receiving MS game chat 2's reliable data + frames. (MS Game chat 2's unreliable data frames are sent as 'MIC' packets.) + + Added a iConnId parameter to VoipConnectRecvVoiceCbT and VoipConnectRegUserCbT. + + Added VoipConnectRecvOpaqueCbT. + + Added a uSendMask parameter to VoipConnectionSend(). + + VoipHeadset + Added VoipHeadsetReceiveOpaqueDataCb(). + + VoipNarrate + Added Amazon Polly support. + + DirtySDK (Apple) + Build + Added a warning for partial available so the build will fail when the deployment version is too low for the + APIs we reference. + + Created an objectivec build type that imports all our C warnings that we enable. + + ProtoSSLApple + Added support for the ALPN extension on versions of the OS that support it. + + DirtySDK (PC) + VoipNarratePC + Added a new VoipNarrate PC module + *Separated TTS SAPI on PC into it own VoipNarratePC module + *Removed the need of copying the output of SAPI into wav buffers + *Aligned behavior with the generic VoipNarrate module + + DirtySDK (XboxOne) + Build scripts + Added game chat 2 header file directory to include path. Added game chat 2 lib to set of external libs to + be linked with. + + Added game chat 2 lib to set of external libs to be linked with. + + PrivilegeApiXboxOne + Added exception handling around usage of xboxone user object methods. + + UserApiXboxOne + Added exception handling around usage of xboxone user object methods. + + UserListApiXboxOne + Added exception handling around usage of xboxone user object methods. + + VoipHeadset + Added VoipHeadsetTranscribedTextReceivedCbT and VoipHeadsetSetTranscribedTextReceivedCallback(). + + VoipXboxOne + Added _VoipUpdateLocalStatus() in charge of polling VoipHeadsetStatus('talk') to detect if local players + are talking or not. + +Changes/fixes in this release: + DirtySDK (All) + CommUdp + Minor fix to use SockaddrInit instead of ds_memclr to make sure the address family is properly specified, + preventing possible warning in later sockaddr call. + + ConnApi + Reduced the complexity of the implementation of connapi now that we have knowledge of the topologies and + hosts. + + Fixed the initialization of the game / voip host, this needs to be happen before any other initialization + to make sure everything lands in the correct state. + + Replaced usage of ConnApiClientInfoT.uId with uLocalClientId / uRemoteClientId where appropriate. uId is + used for identification purposes, in cases where we are establishing a connection the uLocalClientId and + uRemoteClientId are to be used. + + CryptEcc/CryptNist + Changed the module to split up the DH and DSA states, which allows them to be used independently. + + Changed the implementation to allocate the table on the fly instead of keep it in the state. This work + reduces the sitting memory consumption. + + Changed the initialization of the curve state to use the curve identifier. + + Renamed CryptEcc to CryptNist, CryptNist is more specific to the curves we support in the module. + + CryptHmac + Fixed to work with keys longer than the block size. + + CryptMont + Changed the implementation to return an error if passed an unsupported curve identifier. + + DirtyLib + Changed the NetCritInit functions to return an error if the internal allocation fails. + + DirtyNet + Changed SocketBind to fail if the virtual socket is already bound. + + Improved the thread safety in SocketDestroy by protecting the calls to SocketClose. SocketClose may be + invoked at the same time that the DirtyNet subsystem is being destroyed which we must protect against. + + Replaced the use of uCalllast = 0xFFFFFFFF to the boolean bInCallback to prevent the potential of colliding + with a valid tick value + + DirtyThread + Changed the thread initialization to set the processor to any. + + Hpack + Increased the temporary buffer for header parsing to prevent header value truncation. + + Fixed the encoding of large header values (over 127 bytes) to properly encode the size by offsetting after + the value has been written. + + JsonParse + Fixed an issue in JsonFind where parsing an incomplete object caused by invalid JSON could cause a crash. + + NetGameDist + Fixed compilation errors inside the TIMING_DEBUG defines. + + Platform + Removed unused undefs and defines in this module. + + ProtoHttp + Changed the calling of the custom header callback to pass NULL and 0 to pData and iDataLen in preparation + for these parameters being deprecated. + + ProtoHttp2 + Changed the implementation to validate the frame header and the individual frame types payloads per the + spec. + + Fixed documentation for some internal enumerations and fixed SETTINGS_NUM to use the correct value. + + Enabled huffman coding for HTTP/2 header by default. + + Fixed the content-length header to be all lower case. + + Changed "not enough room" debug print in ProtoHttp2Send use verbosity level 1 so that it is not enabled at + the default logging level. + + ProtoHttpUtil + Removed the call to ProtoHttpGetLocationheader via ProtoHttpGetHeaderValue as the functionality was + deprecated. + + ProtoSSL + Fixed to send of empty renegotiation_info extension in ClientHello; there are a small number of + servers that require this or will fail the connection. + + Fixed to send of empty renegotiation_info extension in ServerHello if the client sent one. + + Changed to add no_renegotiation warning alert response by client if a server attempts to renegotiate. + + Fixed a TLS1.3 issue where receiving a ChangeCipherSpec immediately following a HelloRetryRequest would not + be correctly ignored, because the supported_versions extension in the HRR was not being parsed. + + Changed _RecvCertificate to simplify and clarify the certificate parsing, as well as do a better job + skipping certificates that aren't a part of the chain and might violate some of the other checks which + previously would have resulted in verification failure. + + Removed TLS1.3 draft revision support, and updated all draft RFC links to reference the published final RFC + instead. + + Removed SSLv3 version support (previously disabled by default). + + Removed RC4 cipher suites (previously disabled by default). + + Cleaned up some remaining unused MD2 signature algorithm definitions. + + Reorganized/renamed most functions to provide better and more consistent readability. No functional + change. + + Fixed some out-of-date curve defines that limited the 'crvd' selector to only choosing the first two + curves. + + Added full support for RFC8446 style downgrade detection; added server support for setting random values, + added client support for detecting and aborting a TLS1.2 request being illegally downgraded). + + Added server support for remembering HRR cipher we sent, and making sure it is selected when we send the + following ServerHello. Added client support for remembering HRR cipher we received, and making sure we get + it from the ServerHello, alerting if not. + + Fixed to send an illegal_parameter alert when client parses a supported_versions extension with no versions + we support. + + Changed debug output of connecting hostname to include "using TLSx". + + ProtoStream + Fixed copy of error text to copy only the error text (was also copying leftover data from previous request). + + ProtoWebSocket + Removed the Listen/Accept functions as we do not have plans on adding server functionality at this time. + + Removed ProtoWebSocketConnect2 function as it is not used for anything. Please use ProtoWebSocketConnect + function instead. + + VoipCommon + Enhanced implementation of VoipCommonContro('vcid') to support usage of client id 0. + + VoipConnection + Changed resetting the VOIP_LOCAL_USER_SENDVOICE and VOIP_REMOTE_USER_RECVVOICE to be triggered if we have + not sent or recv a mic packet within the VOIP_PING RATE. + + Enhanced implementation to support scenarios where blaze ends up exercising the solution in such a way + that VoipControl('vcid') gets called with a 0 value. A client id of 0 is now valid and a boolean is now + used to indicate whether the 'vcid' selector was used to set the local user id. + + VoipDef + Removed VOIP_LOCAL_USER_MICON which has been unused since PS2 era. + + Changed VOIP_MAXCONNECT from 30 to 32. + + VoipNarrate + Changed VoipNarrateConfig() to no longer take a VoipNarrateRefT, the supplied configuration is stored + globally and used in all calls to VoipNarrateCreate(). VoipNarrateCreate() will fail if not configured + properly. + + Changed VoipNarrateStatus() to have an additional argument 'iValue' to match the same function signature of + other status functions. + + VoipSpeex + Changed speex codec to use the built-in VoipCodec VAD instead of re-implementing internally. + + VoipTranscribe + Changed VoipTranscribeConfig() to no longer take a VoipTranscribeRefT, the supplied configuration is stored + globally and used by all calls to VoipTranscribeCreate(). VoipTranscribeCreate() will fail if not configured + properly. + + Changed VoipTranscribeSubmit() to no longer take an iUserIndex argument, MLU was implemented above this api. + + Changed VoipTranscribeGet() to no longer take an iUserIndex argument, MLU was implemented above this api. + + Changed VoipTranscribeStatus() to have an additional argument 'iValue' to match the same function signature + of other status functions. + + VoipTunnel + Changed the module to use the VoipPacketHeadT definition from voippacket.h + + DirtySDK (Apple) + ProtoSSLApple + Moved the validation of the peer certificate to its own function and state. The validation of the peer + certificates plus the handshaking causes a performance spike. Moving this into a different state and + function makes it so it gets processed on the next update. + + Removed RC4 cipher suites. + + Fixed a crash when calling ProtoSSLClrCACerts before ProtoSSLShutdown by correctly offsetting the array in + the former function. + + Changed the implementation to redirect our allocation to DirtyMem using a custom allocator. + + DirtySDK (iOS) + NetConnIos + Fixed a strict prototypes build warning by removing prototypes that were not needed. + + DirtySDK (PC) + VoipHeadsetPC + Moved the creation of the voice transcription module until it is needed, this saves memory when not in use. + + Removed 'qual' selector from VoipControl; none of the codecs we support reference it. + + DirtySDK (PS4) + DirtyContextManagerPS4 + Fixed DirtyContextManagerShareNetPool() to properly select the lowest slot ref counted NetContext that has a + context size that is greater or equal to the request iNetPoolSize + + DirtyNetPS4 + Removed the attempted optimization when receiving from the packet queue does not return any data. Given that + the clients and servers don't have a packet rate such that this would be necessary we felt that it is best + to remove it. In observed cases this extra call would result in EAGAIN. + + DirtySessionManagerPS4 + Changed the server driven session to be enabled by default. + + Removed unused parameters from DirtySessionManagerCreate. + + Added multi-threaded protection for global variable _ProtoHttp_State. + + DirtyWebApiPS4 + Changed to deleting the request after sceNpWebApiAbortRequest. This is following Sony guidance in ticket + https://ps4.siedev.net/forums/thread/115651. + + ProtoHttpManager + Compiled out the 'pipe' control call on PS4 because the underlying implementation does not support it. + + ProtoHttpPS4 + Fixed the implementation so that the recv throttling doesn't incorrectly cause the request to finish + prematurely. + + Fixed the header formatting to use the input buffer instead of our header cache to prevent truncation. + + Fixed the parsing of large parsing values by increasing the buffer we used to parse. + + Fixed the request functions to actually send the amount of payload data we sent. + + Refactored the call to sceHttpSendRequest() to optionally be called on its own thread. + + Fixed a truncation error with _ProtoHttpIsPEMFormat() function by changing the return type from a uint8_t to a int32_t. + + VoipHeadsetPS4 + Moved the creation of the voice transcription module until it is needed, this saves memory when not in use. + + Moved the creation of the voice narration module until it is needed, this saves memory when not in use. + + Changed VoipHeadsetStatus('sttm'), MLU is now supported; get VoipSpeechToTextMetricsT via pBuf, user index + is iValue. + + Changed VoipHeadsetStatus('ttsm'), MLU is now supported; get VoipTextToSpeechMetricsT via pBuf, user index + is iValue. + + Fixed the +-pbk to include the shared user + + DirtySDK (Unix) + DirtyNetUnix + Removed the attempted optimization when receiving from the packet queue does not return any data. Given that + the clients and servers don't have a packet rate such that this would be necessary we felt that it is best + to remove it. In observed cases this extra call would result in EAGAIN. + + Changed the implementation to allow 'poll' to be called when not in single-threaded mode. + + Changed implementation of _SocketGetMacAddress() such that additional interfaces can easily be added to the + hardcoded default list of interfaces used for mac addr query attempts. + + NetConnUnix + Fixed the order of operations in shutdown to make sure ProtoSSL is shutdown after its dependents. + + VoipUnix + Fixed VoipActivateLocalUser() not properly forcing re-application of channel config. + + DirtySDK (XboxOne) + ConnApi + Fixed XboxOne attempting to demangle the connection to the dedicated server when the voip topology is set to peer + web. + + Changed the logic for setting up the connection state on XboxOne to always set our gameinfo / voipinfo to be + INIT / Demangled as we never connect to ourselves. + + ProtoMangleXboxOne + Enhanced processing of entries in killlist that are in "listening" state. Because there is no system async + op in progress while in that state, we can immediately proceed with a final clean up as soon as the + association is on the killlist. + + ProtoHttpXboxoOne + Changed the implementation to address the module potentially negatively impacting the performance of the + game thread using it. Refactored the way potentially long blocking MS calls are being offloaded to worker + threads. The new solution is involving a single dedicated thread (same lifetime as request object) to which + we offload as little as possible to minimize potential threading issues. As a result of this refactor, + usage of critical section has significantly been reduced. + + ProtoWebSocketXboxOne + Switched back to the base implementation of ProtoWebSocket on XboxOne. The implementation that is provided on + XboxOne leaves out features that are required for our public API. There are no strict requirements on using + their implementation for WebSockets. + + UserListApiXboxOne + Fixed typo in typdef for struct UserListApiPlatformUserContextT as it was colliding with types in + UserApiXboxOne. + + Changed to be not case sensitive when checking for "retry-after: + + VoipCommon + Changed implementation such that XboxOne is no longer displaying inbound transcribed text in system UI if + game code does not register a display transcribed text callback. + + Changed the implementation such that transcribed text to be displayed is now received with a callback + registered with VoipHeadsetSetTranscribedTextReceivedCallback() instead of a callback registered with + VoipConnectionSetCallbacks(). + + VoipHeadsetXboxOne + Changed the implementation to now use MS game chat 2 instead of windows::xbox::chat. + + Changed VoipHeadsetStatus('sttm') will no longer be able to provide metrics for speech to text, as this + info is hidden within game chat 2. + + Changed VoipHeadsetStatus('ttsm'), MLU is now supported; get VoipTextToSpeechMetricsT via pBuf, user index + is iValue. + + VoipPriv + Removed the specialized VoipUserExT. No longer needd after rebasing VoipHeadsetXboxOne from + windows::xbox::chat to MS game chat 2. DirtySDK XBoxOne voip now uses VoipUserT like all + other platforms. + + VoipSerialization + Removed. No longer needed after rebasing VoipHeadsetXboxOne from windows::xbox::chat to MS game chat 2. + + VoipXboxOne + Fixed the calculation of the length of time the voip thread took to be milliseconds and not microseconds. + + Changed VOIP_THREAD_SLEEP_DURATION from 20 ms to 40 ms to meet MS game chat 2 requirements. + + Removed code for auto muting of bad rep user. This is all handled internally by MS game chat 2 now. + + +EADP Game Services DirtySDK 15.1.4.0.5 - December 20, 2018 + + This SDK release was built using the following compiler packages: + + EA Config 5.07.00 + Framework 7.10.00 + + Android AndroidSDK 26.0.2-2 + AndroidNDK r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.07.00 + Kettle kettlesdk 5.508.071-proxy + kettle_config 2.09.00 + PlayStation4AudioInEx 5.508.001 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 4.0.1 + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + + *** Important - This release includes full support for VoIP Accessibility on all platforms *** + VoIP Accessibility includes Transcription (Speech to Text or STT) and Narration (Text to Speech or TTS), in order to + fulfill the requirements of the Computer and Video Accessibility Act (CVAA) that will be enforced in the US starting + January 1st, 2019. Previous releases included support of both STT and TTS for the Xbox One and TTS for the PC; this + release adds support for STT on PC and TTS and STT on PS4. + + Also includes feature request from EA accessibility team: a hearing-impaired person requesting transcribed text from + other players also wants to see transcribed text for her/his own speech. VoipDisplayTranscribedTextCallbackT + callback is invoked with iConnId parameter set to -1 when transcribed text is from a local user. + + Integration requires the application to support rendering of STT text to the screen and acquisition of text from the + user for input into TTS. Please see https://developer.ea.com/pages/viewpage.action?pageId=183248884 or email + GS-DirtySock Support for more information. + + *** Important - PUBLIC API IMPACTING - *** + With the addition of full support of PC and PS4 platforms for Accessibility, some API changes have been made to + present a more unified and consistent interface: + + - Changed public STT interface to use a utf8-encoded char string instead of wchar_t string + - Changed synopsis of 'stot' control selector: iValue parameter used to be enable/disable, it is now local + user index; pValue parameter used to be local user index, it is now enable/disable. + - Renamed VoipSetTranscribedTextReceivedCallback() to VoipSetDisplayTranscribedTextCallback() + - Renamed VoipGroupSetTranscribedTextReceivedCallback() to VoipGroupSetDisplayTranscribedTextCallback() + - Renamed VoipTranscribedTextReceivedCallbackT to VoipDisplayTranscribedTextCallbackT + - VoipDisplayTranscribedTextCallbackT callback can now be invoked with iConnId parameter set to -1 when + transcribed text is from a local user + - XoneSynthesizedSpeechCfgT and PCSynthesizedSpeechCfgT have been unified into a single structure with + platform-specific elements and renamed to VoipSynthesizedSpeechCfgT + +New in this release: + DirtySDK (All) + ConnApi + Added usage of the new VoipGroupControl('self') to inform the underlying voipgroup of the local slot + index in the game. + + ProtoStream + Added ProtoStreamOpen2() to allow use of POST requests. + + Added minimum buffer concept for callback; if specified, a callback will never provide less than this + minimum amount of data, unless it is the end of the stream. + + Added cache of HTTP error response body and added 'serr' selector to return it. + + Voip + Added STT/TTS metrics structures, VoipSpeechToTextMetricsT and VoipTextToSpeechMetricsT, which contain + fields used to track usage of the voip accessibility features. + + VoipGroup + Added new VoipGroupControl('self') selector to be used by ConnApi to inform the underlying voipgroup of + the local slot index in the game. + + DirtySDK (PC,PS4) + VoipTranscribe + Added new module to encapsulate cloud-based transcription (Speech to Text) services for the purpose of + transcribing VoIP audio. This module is fully integrated in DirtySDK VoIP and does not need to be accessed + directly. Configuration of this module happens via a Blaze server configuration. This version supports + local user indices but does not currently support multiple local users. + + DirtySDK (PC) + VoipHeadsetPC + Added support for VoipTranscribe module. + + Added VoipHeadsetStatus 'sttm', to get the VoipSpeechToTextMetricsT via the passed in buffer. + + Added VoipHeadsetStatus 'ttsm', to get the VoipTextToSpeechMetricsT via the passed in buffer. + + DirtySDK (PS4) + VoipHeadsetPS4 + Added support for VoipNarrate and VoipTranscribe modules. + + Added call to sceAudioInIsSharedDevice(), used to replace + sceVoiceGetPortAttr(SCE_VOICE_ATTR_AUDIOINPUT_DEVICE_CAMERA), supplied by the new AudioIn_03 library. + + Added silence detection for discontinuous transmission (previously supplied by sceVoice) and transcription. + + Added VoipHeadsetStatus 'sttm', to get the VoipSpeechToTextMetricsT via the passed in buffer. + + Added VoipHeadsetStatus 'ttsm', to get the VoipTextToSpeechMetricsT via the passed in buffer. + + VoipNarrate + Added new module to encapsulate cloud-based narration (Text to Speech) services for the purpose of producing + VoIP audio from text. This module is fully integrated in DirtySDK VoIP and does not need to be accessed + directly. Configuration of this module happens via a Blaze server configuration. This version supports + local user indices but does not currently support multiple local users. + + DirtySDK (XboxOne) + VoipHeadsetXboxOne + Added VoipHeadsetStatus 'sttm', to get the VoipSpeechToTextMetricsT via the passed in buffer. + + Added VoipHeadsetStatus 'ttsm', to get the VoipTextToSpeechMetricsT via the passed in buffer. + +Changes/fixes in this release: + Contrib (PC) + VoipOpus + Changed sampling rate from 8khz to 16khz for improved audio quality, and to match transcription audio input + rate. + + Fixed decoder not accumulating into the output buffer, which broke >2p VoIP. + + DirtySDK (All) + Base64 + Fixed _Base64Encode, which was erroring out if the encoded size plus null terminator would exactly fit the + output buffer size. + + CryptEcc + Changed the definition of the number of iterations to allow teams to override it if they choose. + + NetConn + Fixed the module startup to assign the global ref after the full startup is complete, this makes it so that + 'open' reports correctly. + + NetGameDist + Changed the _ProcessPacketType to prevent setting bRecvMulti on the server. bRecvMulti is only meant for + clients to tell if they are in 2-player peer-to-peer games or server based games. + + ProtoHttp + Fixed to not abort streaming post on header response if the response is successful (in the 200 range). + + ProtoHttp2 + Changed the timer to be updated only when creating a new connection, instead of every time we create a new + request. We don't want to artificially update the timer when we have yet to send data. This could cause us to + get stuck in ST_CONN if we try to create new requests quicker than we can timeout. + + Changed the send behavior to return zero bytes sent if we don't have space but are not sending a zero sized + payload (stream end). This aligns with how other modules handle similar cases. + + ProtoSSL + Fixed an issue processing fragmented handshake packets found when testing TLS1.3 connections to servers + using Facebook's new TLS library ("fizz"). + + Fixed an issue where a private key file passed in without a signature was misidentified as ECDSA due to an + uninitialized variable in _FindPEMCertificateData. This would cause the key file to fail to load and the + subsequent connection request to fail. + + ProtoStream + Changed some debug verbosity levels. + + ProtoUpnp + Fixed the handling in cases that we might send zero bytes in a post. In this situation we need to make calls + to ProtoHttpSend to send the remaining data. + + ProtoWebsocket + Changed to disable "append header is the same" debug spam at default verbosity, matching the behavior of + other protocols. + + Voip + Renamed VoipTranscribedTextReceivedCallbackT to VoipDisplayTranscribedTextCallbackT. + + Renamed VoipSetTranscribedTextReceivedCallback() to VoipSetDisplayTranscribedTextCallback(). + + Changed XoneSynthesizedSpeechCfgT and PCSynthesizedSpeechCfgT to be unified into a single structure with + platform-specific elements and renamed to VoipSynthesizedSpeechCfgT. + + VoipCommon + Changed synopsis of 'stot' control selector: iValue parameter used to be enable/disable, it is now local + user index; pValue parameter used to be local user index, it is now enable/disable. + + Renamed VoipTranscribedTextReceivedCallbackT to VoipDisplayTranscribedTextCallbackT. + + Changed _VoipCommonTextDataCb (callback to handle locally generated transcribed text) to not only send + transcribed text to remote users, but to additionally display it locally if the local user has STT enabled. + + Changed implementation to now enable text transcription locally if any local user is requesting text + transcription from remote users. + + VoipGroup + Changed implementation of _VoipGroupManagerDisplayTranscribedTextCallback() such that it can now surface + up not only transcribed text from remote users, but also locally transcribed text from local users. + + Renamed _VoipGroupManagerTranscribedTextReceivedCallback() to + _VoipGroupManagerDisplayTranscribedTextCallback(). + + Renamed VoipGroupSetTranscribedTextReceivedCallback() to VoipGroupSetDisplayTranscribedTextCallback(). + + VoipHeadset + Changed VoipHeadsetDisplayTranscribedText() to be an xboxone only api because all other platforms + had stubbed implementation for it. Also renamed it from VoipHeadsetDisplayTranscribedText() to + VoipHeadsetDisplayTranscribedTextInSystemUI() to avoid confusion with the new + VoipDisplayTranscribedTextCallbackT callback. + + DirtySDK (PC) + VoipHeadsetPC + Changed sampling rate from 11.025khz to 16khz for improved audio quality, and to match transcription audio + input rate. + + Fixed a crackle and pop issue with PC TTS. + + Renamed VoipHeadsetControl('stot') to VoipHeadsetControl('tran') for enabling local transcription. + + DirtySDK (PS4) + DirtyErrPS4 + Changed to include audio in/out errors. + + Changed audio input path from sceVoice to use sceAudioIn, to gain access to the uncompressed voice audio for + the purpose of transcription. + + VoipHeadsetPs4 + Renamed VoipHeadsetControl('stot') to VoipHeadsetControl('tran') for enabling local transcription. + + Removed deprecated SCE_VOICE_EVENT_AUDIOINPUT_MUTE_STATUS enum. It was never used by Sony. + + DirtySDK (XboxOne) + NetConnXboxOne + Fixed 'xusr' to early return when an error occurs + + ProtoHttpXboxOne + Fixed a memory stomp when terminating our header buffer when the size of the header is larger than that of + the header cache by terminating the source instead of the destination. + + UserApiXboxOne/UserListXboxOne + Fixed the module to correctly handle timeout errors. In CL1332066, we added a new recv error code to inform + users that a timeout had occurred. While doing this there were several modules that were not catching this + case. + + UserApiXboxOne + Fixed the handling in cases that we might send zero bytes in a post. In this situation we need to make calls + to ProtoHttpSend to send the remaining data. + + VoipHeadsetXboxone + Enhanced robustness with new exception handling around the retrieval of the encoder object to be used to + encode locally acquired voice samples. This is added to mitigate the impact of very low-rate prod crashes + reported by NBA. + + Changed the handling of "bystanders". Adding bystanders to the chat session is no longer working with recent + XDK/recoveries. As soon as a "bystander" is added to the chat session all subsequent chat session updates + fail. After reporting the issue to Microsoft, we were told that Windows::Xbox::Chat is completely deprecated, + and that we could stop adding bystanders without any risk of certification failures. + + Changed VoipHeadsetDisplayTranscribedTextInSystemUI() to support both local and remote originators for + transcribed text. + + Renamed VoipHeadsetControl('stot') to VoipHeadsetControl('tran') for enabling local transcription. + +EADP Game Services DirtySDK 15.1.4.0.4 - October 29, 2018 + + This SDK release was built using the following compiler packages: + + EA Config 5.07.00 + Framework 7.10.00 + + Android AndroidSDK 26.0.2-2 + AndroidNDK r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.07.00 + Kettle kettlesdk 5.508.071-proxy + kettle_config 2.09.00 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + ProtoHttp2 + Added support for checking if an idle connection is still active to prevent any unwanted issues if the + server silently goes away. + +Changes/fixes in this release: + DirtySDK (All) + ConnApi + Fixed a bug introduced in the context of GOSREQ-1975 (Refactored the DirtyCast/nonDirtyCast dedicated + server divergent code paths). To align with the DirtyCast implementation, the conn index passed to + VoipGroupConnect() is being decremented by 1 to save the spot unnecessarily used by the voipserver + in the voip sub-system (because there is no voipconnections to the voipserver itself). But the + call to VoipGroupControl('vcid') was not tailored in the same manner. It now is. + + DirtyCert + Fixed deadlock when disconnecting from the redirector and updating at the same time by not waiting on the + module crit when updating. Given that the window that this can occur is rare, we can just try the lock in + the update case to prevent deadlocking in the future. + + DirtyNet + Fixed logic used to decrement pPacketQueue->iPacketTail in SocketPacketQueueAllocUndo(). Upon an underflow + resulting from decrementing the tail when it is 0, the logic proved to only work when + pPacketQueue->iMaxPackets was a power of two. After the fix, the logic works for any value of + pPacketQueue->iMaxPackets. + + ProtoHttpManager + Fixed the update so that we can process idle protohttp refs. ProtoHttp requires that we allow it to do idle + processing to handle connection wide events. + + VoipGroup + Removed an early return in VoipGroupConnStatus and VoipGroupRemoteUserStatus which hid the correct status + information when a suspended connid is queried. + + DirtySDK (PC) + VoipOpus + Fixed the accumulation of the output buffer as required by the voip mixer. Before if we received multiple + voice bundles we would only write one. + + DirtySDK (PS4) + DirtyWebApiPS4 + Fixed a crash if initialization fails by checking for NULL and configuring the thread life correctly. + +EADP Game Services DirtySDK 15.1.4.0.3 - August 24, 2018 + + This SDK release was built using the following compiler packages: + + EA Config 5.06.00 + Framework 7.10.00 + + Android AndroidSDK 26.0.2-2 + AndroidNDK r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.03.00 + Kettle kettlesdk 5.008.071-proxy + kettle_config 2.06.03 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + VoipGroup + Added a selector to get the mute all status from the voipgroup. + + QosClient + Added a new error code QOS_CLIENT_REQUEST_STATUS_ERROR_TIMEOUT_PARTIAL so we can distinguish a qos test + that wasn't successful getting any probes round trip, vs one that was at least partially successful. + +Changes/fixes in this release: + DirtySDK (All) + ConnApi + Fixed a crash if we try to abort demangling on xbox one when we are not currently demangling. + + DirtyNet + Fixed size of internal structure (HostentT::name[]) used during DNS name lookup: increased size from 64 + bytes to 256 bytes to support recent scenarios involving names longer than 64 chars. The maximum DNS name + length is 253 characters + + ProtoHttp2 + Fixed the 31-bit value parsing/decoding that caused incorrect behavior in the module. When decoding the + stream id and window update increments we were masking out the high bit after shifting instead of before, + which caused to get incorrect values. + + QosClient + Fixed a potential issue where resources would not be freed on SocketLookup failure. + + Enhanced error checking, so that received data is at least the size of a probe before processing it. + + VoipCommon + Enhanced error handling around VoipHeadsetCreate() + + VoipGroup + Changed the code to ignore mute all if it has already been done. + + DirtySDK (iPhone/OSX) + ProtoSSLApple + Fixed being stuck in a bad state when an error occurs in setup by cleaning up the secure state in those + instances. + + DirtySDK (PC) + VoipOpus + Changed the default for opus to use the built in voipcodec vad support to help with stuttering. + + DirtySDK (PS4) + NetConnPS4 + Removed the code terminate the UserService as this uninitentially left in. When we still supported + disabling IEAUser, we needed to use the user service to work how our legacy code worked. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Fixed a crash in the suspend handler by refactoring the code to more careful in how we signal the invalidity + of the xmlhttp requests. + + VoipHeadsetXboxone + Fixed possible crash conditions upon VoipHeadsetDestroy() being called from within VoipHeadsetCreate(). + +EADP Game Services DirtySDK 15.1.4.0.2 - July 12, 2018 + + This SDK release was built using the following compiler packages: + + EA Config 5.06.00 + Framework 7.10.00 + + Android AndroidSDK 26.0.2-2 + AndroidNDK r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.03.00 + Kettle kettlesdk 5.008.071-proxy + kettle_config 2.06.03 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + UserApi + Added a new selector 'rrsp' to disable UserApi from returning First Party raw response. + + DirtySDK (PC) + VoipHeadsetPC + Added Text-to-Speech functionality. You can now use the 'ttos' selector to send text as voice. + +Changes/fixes in this release: + DirtySDK (All) + CommUdp + Fixed the calculation of the packets we can fit in the buffer to account for the encoded subpacket size to + prevent prototunnel rejecting our packets. + + ConnApi + Fixed crashes when creation of the protomangle ref fails on XboxOne by guarding calls to that module. + + NetGameDist + Fixed _SetDataPtrTry to correctly identify uIndexB when we are looking at the server outbound queue. + + ProtoHttp + Fixed failure to invoke the write callbacks with data when we received but have not processed the whole + chunked response. + + ProtoTunnel + Fixed to not rematch on an OUTOFORDER decrypt result, which causes a stream desync and the connection to + fail. + + QosClient + Fixed bug where in some error conditions a protohttp2 stream would be leaked. + + Increased the logging level on protohttp2 used by QoS2.0 to potentially give insight into if the coordinator + is not responding to the client sending results. + + DirtySDK (PS4) + ProtoHttpPs4 + Fixed failure to invoke the write callbacks with data when we received but have not processed the whole + chunked response. + + Fixed no content length downloads to properly calculate the body size when the download is complete. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Fixed the write callback from not processing due to not correcting assessing if we have read the entire body + by tracking the amount read. + + Fixed the processing of the kill list to allow for killing requests not yet in the in-progress state. + + ProtoMangleXboxOne + Fixed a crash if there is a template load failure by moving the creation of the incoming security + association vector earlier. + + UserApiXboxOne + Fixed the ability to fetch 100 profiles at a time by increasing the json buffer to 2048. + + VoipHeadsetXboxOne + Fixed incorrect usage of the chat session in a scenario where the chat session creation previously failed. + + Fixed incorrect inbound queue management in _VoipHeadsetProcessInboundQueues(). + + Fixed incorrect expection handling in _VoipHeadsetReputationQuery() that might cause a crash. + +EADP Game Services DirtySDK 15.1.4.0.1 - June 1, 2018 + + This SDK release was built using the following compiler packages: + + EA Config 5.06.00 + Framework 7.10.00 + + Android AndroidSDK 26.0.2-2 + AndroidNDK r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.03.00 + Kettle kettlesdk 5.008.071-proxy + kettle_config 2.06.03 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + DirtyNet + Added SocketPacketQueueAllocUndo() to be used to "undo" a recent call to SocketPacketQueueAlloc(). + + Added calls to SocketPacketQueueAllocUndo() in scenarios where the previous call to + SocketPacketQueueAlloc() needs to be undone because the allocated resources ended up not being used, + i.e. when WSARecvFrom() fails and when WSAGetOverlappedResult() fails. + + DirtySDK (PS4) + ProtoHttpPS4 + Added the 'ratr' and 'rats' selectors to the implementations for parity with the stock version of protohttp + + Added ProtoHttpControl('mwrk') selector to control the maximum number of worker threads ProtoHttp will spawn. + Defaults to 0 to that the current behavior will not change unless the user changes this value. + Refactored the call to sceHttpSendRequest() to optionally be called in its own thread. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Added the 'ratr' and 'rats' selectors to the implementations for parity with the stock version of protohttp + +Changes/fixes in this release: + DirtySDK (All) + ConnApi + Restored the NetGameDistStatus('mplr') selector which will not account for the server index. + + Refactored the ConnApiStatus('gsrv') selector to merge two different implementations. This + will now always return TRUE if the game is server hosted and FALSE otherwise, and if a buffer is + provided it will copy the server's ConnApiClientT there. + + NetConn + Fixed the return code from NetConnCommonStartup when NetConn has already been started. + + NetGameDist + Fixed incorrect "wraparound" logic for the OutBufData queue. This CL is fixing two things with the function + _SetDataPtrTry() which is used to identify where to write data into the respective ring buffers associated + with inbound and outbound queues. + #1 When used with OutBufData, the function was incorrectly initializing uIindexB with "OutBufData.iBeg" + instead of "InpBufData.iBeg+iIOOffset"... thus opening the door for overwriting the "pending entries" + when the queue is close to filling up. + #2 The iPosB variable was being initialized without considering whether uIndexB was pointing to a + "valid" entry or not... potentially resulting in the code executing with a busted iPosB + (specific to empty queue scenarios) + + Refactored NetGameDistMultiSetup to not automatically remove 1 from the index on the clients. This offset + shall be performed by the user. + + ProtoSSL + Fixed a bug introduced in Raven.0 skipping a certificate that is not a part of chain. + + Fixed a bug introduced in Raven.0 where the client did not correctly handle a server downgrade when + formulating the ClientKeyExchange PreMasterSecret, triggering a downgrade detection on the server and + causing the server to fail the connection with a bad record mac alert. + + QosClient + Fixed a bug, when compiling without logging, a timer variable would not be reset resulting in a spam of + callbacks reporting the Qos procedure has timed out. + + Added a way to get module hResults from the callback structure QosCommonProcessedResultsT or via + QosClientStatus('hres'). + + QosCommon + Fixed building qoscommon so that we don't need to keep adding #ifdefs for new platforms to use strtok. + + VoipConnection + Fixed an issue where the local client id was not being treated consistently in _VoipReliableDataUnpackDATA. + This led to Speech-To-Text data not being displayed in some cases. + + VoipTunnel + Fixed the suspend logic so that if we are removing a suspended game at the end of the list we would clear + it out correctly. If we were to remove suspended data at the end and suspend logic hits later at the same + slot, the voiptunnel would incorrectly error because it believes there is already a game suspended there. + + DirtySDK (iPhone/OSX) + ProtoSSLApple + Fixed an issue where we incorrectly don't process the received data when receiving a FIN in the same packet. + How the processing works we can make many read calls off the socket before the data gets returned back to + the caller. In the case that we get data and FIN, we just see this as the peer closing the connection. + Instead of doing it in this way we should always first process any data we received. + + DirtySDK (PC) + DirtyNetWin + Enhanced tracking of pSocket->bRecvInp such that it gets flagged before WSARecvFrom is attempted. The goal + is to eliminate a theoritical race condition identified during code review: bRecvInp is checked in + SocketRecvFrom() which can be invoked from the main thread... and thus that opens the door for a race + condition with the socket recv thread updating that variable too late (after the call to WSARecvFrom(). + + NetConnWin + Fixed the cleanup when socket creation fails during NetConnStartup(). + + DirtySDK (PS4) + ProtoHttpPS4 + Fixed the header parsing for large headers by using the input pointer to the `sceHttpGetAllResponseHeaders`. + In the parse header code we check if we have the entire header to allow us to continue parsing. If the + header is too large, we truncate the headers and the parse header function will always early return, + attempting to wait for more data. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Fixed overallocating memory when receiving large responses from the server by resetting the offsets when the + entire buffer is read. + + Fixed the logic so that we could kill any requests that we know will no longer receive callbacks. In + conditions such as suspend and disconnects we have observed that no callbacks will be fired which leaves + requests in XMLHTTP_STATE_INPROGRESS. This has the effect of leaving netconn in the ST_CLEANUP state while + waiting for the kill list to be cleared of all requests, which blocks NetConnConnect from connecting to the + first party network. + + ProtoMangleXboxOne + Fixed the crash when template fails to load by failing the creation of the module, it will try to continue + without demangling at the higher levels. + + Removed the concept of secure bypass which is not being used. + + UserApiXboxOne + Fixed a crash when trying to parse the profiles response by ensuring that we received any data. If we didn't + receive any data it could mean that we had something misconfigured or that server just returned an error to + us. We should not have assumed that we would always have data waiting. + +EADP Game Services DirtySDK 15.1.4.0.0 - March 28, 2018 + + This SDK release was built using the following compiler packages: + + EA Config 5.06.00 + Framework 7.10.00 + + Android AndroidSDK 26.0.2-2 + AndroidNDK r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.03.00 + Kettle kettlesdk 5.008.071-proxy + kettle_config 2.06.03 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + + *** Important - This release has not been tested with Xbox February XDK and Sony PS4 5.5 SDK *** + Official support for the newer February XDK and PS4 5.5 SDK will come in the next release. + + *** Important - This is the first release of Qos2.0 *** + QosApi has been replaced with QosClient for Qos2.0. E-mail GS-Operations@ea.com to have a custom Qos profile + setup for your title, it specifies which ping sites to use, and what tests it should conducted. + +New in this release: + DirtySDK (All) + CryptBn + Added a debug function to log the big number. + + Added Added CryptBnByteLen to return number of bytes currently used by the big number. + + CryptChaCha + Added ChaCha20-Poly1305 AEAD cipher implementation. + + DirtyCert + Changed redirector endpoint name to gosca18.ea.com + + Changed to add support for using port 44326 for DirtyCert on Xbox One. + + DirtyErr + Added DirtyErrDecodeHResult which can split an hResult value into its component pieces, to better + understand what it represents. + + ProtoSSL + Added TLS1.3 support. Default requested version remains TLS1.2, so TLS1.3 connections are not attempted + without being explicitly enabled. + + Added DigiCert Global Root CA for EA-signed secure public endpoints (used in place of Symantec/VeriSign). + + Added ECDSA support, including private key parsing, cipher suites, server and client certificates, and + signature algorithms for TLS1.2 and 1.3. + + Added support for TLS_CHACHA20_POLY1305_SHA256 (TLS1.3), and TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + and TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (TLS1.2) cipher suites. + + Added support for RSA-PSS client and server certificates. + + Added ability for ProtoSSLControl 'scrt' control selector to pass in full .pem files with signatures not + already stripped. If no signature is present PKCS#1 is assumed, retaining previous functionality. + + Added ProtoSSLStat('ladd'), selector. Is used to return a cached local client address, that was used + for the last connection. The address is copied into pBuffer, which must be at least sizeof(struct sockaddr) + + QosClient + Added the new module QosClient, a part of the Qos2.0 system, it replaces all functionality of previous QosApi. + Qos2.0 manages the whole Qos process from start to finish, this management had been done by BlazeSDK in the + past. The basic usage of the QosClient module would be; call QosClientCreate() to initialize, regularly + call QosClientUpdate(), call QosClientStart() and wait for its callback with results. Look at the in code + documentation for additional details. + + DirtySDK (XboxOne) + UserListApiXboxOne + Added support for 409 (Rate Limited) response code. + +Changes/fixes in this release: + DirtySDK (All) + ConnApi + Fixed size of dynamically allocated module ref. An unused client entry was always unnecessarily allocated + at the very end of the struture. + + Removed the concept of the GameServer in ConnApi. This was only used for DirtyCast servers, which will now + be located at index 0 of the client list the same way it is for nonDirtyCast servers. + + Removed 'mplr' control selector, which was deemed unneeded. + + Removed the XBox One 'phxc' control selector, and replaced it with a generic 'gsrv' selector. This indicates + whether the host is a dedicated server or a console. + + Refactored ConnApiControl('gsrv') usage for the new functionality. + + Removed ConnApiControl('gsv2') . + + Removed the gameflags and associated parameter in ConnApiConnect, this is leftover from Xenon. + + CryptBn + Changed the CryptBnInverseMod function to be implemented using binary extended gcd. This new algorithm is + much faster than extended euclidean. The new algorithm is explained in the Handbook of Applied Cryptography + Chapter 14.4.3 (14.61). + + Changed the CryptBnMod algorithm to skip trying to reduce or left shift until we have at least incremented + the number once. In our profiles we noticed that there was a lot of wasted time in the reduction checking + when the result was still zero. + + CryptEcc + Optimized the point calculations in _CryptEccPointCalculate by multiplying over the modulus. Doing this + makes it so the final redunction happens on a smaller number. + + Fixed the ECDSA sign and validation when the hash's bitsize is larger than the bitsize of the curve. Based + on the algorithm, when the bitsize of the hash is larger than the order of the curve the hash shall be + truncated. + + Changed the ECDSA signing to be done given the new private key parameter. The private key used previously + was the wrong one and caused the validation to fail on the peer's end. + + DirtyCert + Addressed a TODO to close the connection instead of abort when the work is complete. + + DirtyErr + Changed the parameters to DirtyErrGetHResult(), so the sizes of the fields provided more accurately + represent the size of what is internally stored in the hResult. + + DirtyNetUnix + Added the missing thread termination signal + + JsonFormat + Addressed a TODO to use ds_timetostr instead of implementing it inline. + + NetGameDist + Improved readability of code by renaming some variables. + + NetGameDistServ + Fixed some coding standard issues in the module's InputQueryMulti and InputLocalMulti functions. + + ProtoHttp2 + Changed the frame size clamp macro to call DS_CLAMP. + + Removed half-implemented proxy support until a time when we find a h2 proxy that supports CONNECT. + + ProtoTunnel + Fixed a bug where a packet that could not be decrypted and had a negative stream offset could cause the + tunnel connection to be reset, breaking the stream. This fix includes changes to clarify behavior that + was previously difficult to ascertain without a very close inspection of the code: + + ProtoSSL + Changed to disable RC4-MD5 cipher suite and SSLv3 by default. + + Changed to enable ECDHE cipher suites by default. + + Changed the certificate loading to use Base64Decode2. The implementation was assuming that Base64Decode + returned the amount of data needed in the output but in fact it just returns TRUE or FALSE. The + Base64Decode2 function returns the result that it intended to get. + + Changed cipher preference order to be more in-line with current practices. + + Changed signature schemes/algorithms preference order to be more like boringssl. + + Changed execution of TLS1.3 handshake key generation in RecvServerHello, and signature verification in + RecvServerKeyExchange and RecvCertificateVerify to be async (blocking in smaller increments). + + Changes to simplify certificate parsing and make it easier to read and follow, with a fix and two minor + functional improvements; + - fixed issue observed from inspection of skipping a cert that is not part of the chain not working + for tls1.3, due to the extensions field not being skipped + - moved validation bypass to be immediately after certificate parsing, before date validation + - moved leaf cert caching and cached cert bypass into block specific to parsing the leaf certificate, + simplifying main loop + + Changed signature scheme and cipher selection to account for certificate signing algorithm, if a + certificate is available. + + Changed to allocate certificate and private key buffers internally when using 'scrt' and 'skey' control + selectors, meaning these buffers no longer need to be persisted by the caller. + + Changed signature verify buffer from 2048 bytes to SSL_SIG_MAX. + + Changed signature verify failure alert from bad_record_mac to decrypt_error, as OpenSSL does. + + Fixed ECDHE/SHA1 ciphers for TLS1.0/TLS1.1, and enabled them for those versions. + + Fixed an issue with AES decrypt when a padding failure was detected, returning an immediate error instead + of resulting in a bad_record_mac alert as intended. + + Fixed an issue in _ProtoSSLGenerateSecret() where the size of the PreMasterKey buffer (48) was being used + to build the master secret instead of the key length, which is not always 48. + + Fixed a bug in RecvCertificateVerify in TLS1.3, which was dependent on the ECC context being left + initialized with the proper curve by RecvCertificate. + + Fixed to not send signature_algorithms extension for connection attempts with a TLS version less than 1.2. + + Fixed 'rsao' operator to cover new async crypto states, renamed 'crpt', and marked 'rsao' for deprecation. + + Fixed a server issue processing the ChangeCipherSpec message. + + Fixed a server bug from Raven-pr2 where the signature_algorithms extension would be sent in the ServerHello + by a TLS1.2 or earlier server, which is not legal. + + Fixed a server bug that broke resume flow in tls1.2 and prior when tls1.3 server hello session echo was + implemented. + + Fixed the server to select cipher based on session resume cipher if available, and to not attempt resume if + the ssl version or cipher of the previous session don't match those of the current session. + + Fixed a server bug in ProtoSSLUpdateSendServerKeyExchange(), which was leaving bRSAContextInitialized TRUE. + This would cause a subsequent client certificate verify operation to fail. + + Fixed a server bug when receiving a tls1.3 ClientHello but negotiating a tls1.2 or prior connection with an + ECDHE cipher; the key from the key_share extension would be used when sending the server key exchange, + causing the connection to fail. + + Fixed server bug when verifying an ecdsa client certificate; the state was left initialized, which broke + subsequent encryption of Finished message. + + Fixed SendCertificateRequest to transition to sending Certificate message when TLS1.3 is used. + + Fixed RecvCertificateRequest to choose signature scheme in concert with certificate signing algorithm, if a + certificate is available. + + Fixed SendFinished to transition to RECV_HELLO state if TLS1.3+server+client cert flow. + + Fixed SendServerKeyExchange and RecvCertificateVerify for TLS1.1+server+rsa|ecdsa. + + Fixed RecvCertificate to transition to RecvFinished if a TLS1.3 server receives an empty Certificate + message, and the client cert level is not two (client cert required). + + Fixed SendCertificate to transition to SendFinished if a TLS1.3 client sends an empty Certificate message, + skipping CertificateVerify. + + ProtoPing + Moved this module to the LegacyDirtySDK. + + ProtoPingManager + Moved this module to the LegacyDirtySDK. + + ProtoUdp + Moved this module to the LegacyDirtySDK. + + ProtoUpnp + Changed the implementation to switch from using ProtoUdp to DirtyNet for UDP communication. + + QosApi + Moved this module to the LegacyDirtySDK. + + VoipGroup + Changed the VoipGroupManager to contain a variable length array of voipgroups. In multihub mode we require + more voipgroups due to the number of concurrent blazehubs running. + + VoipTunnel + Refactored indexes for voip clients lists were offset by -1 so that the first console user in the list + would appear at index 0 of the voip client list. This helps to not waist one of the maximum 32 voip slots. + + XmlFormat + Removed XmlValidate debug function that was never implemented. This fixes an outstanding TODO we had to + implement this functionality. + + DirtySDK (Android) + CryptRand + Changed the Android implementation to read from /dev/urandom. This addresses an outstanding TODO we had to + add a native implementation for this platform. + + DirtySDK (Linux) + DirtyNetUnix + Fixed the idle procs not being called when running in singlethreaded mode. + + DirtySDK (macOS/iOS) + NetConnIos + Switched the implementation to use the non-deprecated Base64Decode2. + + ProtoSSLApple + Enabled support for ECDSA and CHACHA20-POLY1305 ciphers. + + DirtySDK (PC) + DirtyNetWin + Changed the implementation of 'poll' to fire the socket callback when we have data waiting. The previous + implementation would just notify that is a socket is waiting but would need to wait for the socket idle + thread for the socket callback to be invoked. + + DirtySDK (PS4) + DirtyLibPS4 + Removed support for singlethreaded mode on PS4, this isn't a supported configuration. The implementation was + taken from unix when it was created. + + ProtoHttpPS4 + Fixed a write callback bug when all data is available immediately, in which case the write callback + completes the transition before the data can be read. + + VoipHeadsetPS4 + Fixed size of dynamically allocated module ref. An unused client entry was always unnecessarily allocated + at the very end of the struture. + + DirtySDK (Unix) + NetConnUnix + Fixed bug (opt build only) inadvertently introduced in Raven-pr2. Body of _NetConnUpdateConnStatus() was + incorrectly made "logging only" by conditionnally compiling it in with DIRTYCODE_LOGGING but that broke + the functionality because the line "pRef->uConnStatus = uNewConnStatus" is not only for logging purposes + but is core functionality for internal state transitions. + + VoipUnix + Changed voipunix to assume single-threaded usage: eliminates run-time threading issues (malformed + prototunnel packets) resulting from voipunix being used from the dirtycast stress client which implies + disabled critical sections. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Fixed the ProtoHttpAbort behavior on XBox One to be similar to other platforms. + + Fixed a write callback bug when all data is available immediately, in which case the write callback + completes the transition before the data can be read. + + ProtoMangleXboxOne + Fixed size of dynamically allocated module ref. An unused client entry was always unnecessarily allocated + at the very end of the struture. + + VoipXboxOne + Fixed copy and paste error, the code was set to set the priority and not the affinity. + +EADP Game Services DirtySDK 15.1.4.0.0-pr2 - February 5, 2018 + + This SDK release was built using the following compiler packages: + + EA Config 5.06.00 + Framework 7.10.00 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.03.00 + Kettle kettlesdk 5.008.051-proxy + kettle_config 2.06.02 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + ProtoHttpServ + Added ability to specify ALPN string for ProtoSSL. + + ProtoSSL + Added support for rsassa-pss signed certificates. + + Added support for draft22 and draft23 versions of TLS1.3. + + Added client support for TLS1.3 session resumption. + + Added safe reading for handshake receive functions and extension parsing. + + Added client certificate support for TLS1.3. + + QosApi + Added QosApiControl('stwi') - set the amount of ms before a stall is counted, if a stall is detected test + timeout will be extended (default 500ms). + + UserListApiPS4 + Added order directions for list queries + +Changes/fixes in this release: + DirtySDK (All) + Build + Fixed the build scripts to correctly set the optionset for C++ files when building as a DLL. + + ConnApi + Removed the empty ConnApiMigratePlatformHost function. + + DirtyNet + Changed the implementation of the map address functions to return only -1 in error as negative numbers are + expect for real (non-virtual) IPv4 addresses. We do not want to return error when remapping an IPv4 address. + + Changed the implementation of the remap address function to handle the case an IPv4 address was tried to be + remapped. + + Fixed SockaddrInSetPort to not generate constant value truncation warning on Windows builds. + + Hpack + Fixed crash in _HpackDecodeLiteralField if the indexed entry decode failed by stopping further processing. + + Fixed crash in _HpackHuffmanTreeDecode if the huffman code lookup in the tree failed. + + NetGameDist + Fixed parsing of the stat packet to prevent reading past the end of the buffer. The module reads and writes + to the stat packet based on the number of players. On the client this is a fixed value, while on the server + this is adjusted based on when players join and leave the game. Due to a mismatch between the both sides, + the server will write less than the client expects causing it to potentially read past the end of the + buffer. + + Protobuf + Fixed reading and writing of non-packed repeated fields. When this was originally implemented, how I + understood the encoding documentation is that with proto3 all repeated fields were packed, which was + incorrect. How it actually works is that the primitive types (varint, 32-bit and 64-bit) are packed while + length delimited types are not. Due to this I needed to make a change in the APIs so it can account for the + behavior change in the implementation. + + Refactored the functions dealing with reading packed repeated fields to be a bit more elegant to use. + + Removed the output sizes for the fixed type functions (non-repeated) as this is implicit based on the + function. + + Removed the unnecessary fixed writing functions at align with the reading size. + + Refactored the error checking based on the expectations for each fixed function. + + ProtoHttpUtil + Changed ProtoHttpUrlEncode functions to return the append point rather than always returning zero. Also + changed to mirror C99 vsnprintf() overflow semantics. + + ProtoSSL + Fixed crash when parsing ASN.1 binary objects by preventing a negative size when zero sized data is parsed + from the header. + + Fixed a memory leak when CryptRSAInit fails when verifying the signature and the context is heap allocated. + + Removed support for md2-signed certificates. + + Fixed a bug in sending of TLS1.3 HelloRetryRequest by server. + + Fixed a bug receiving 0/n record split records introduced in Raven-pr1. + + Fixed SendCertificateVerify to work with PSS encoded signatures when using TLS1.2. PSS signature support + was added along with TLS1.3 in Raven-pr1, which exposed the lack of support. + + Fixed to increase ClientHello body format buffer to 2k to accomodate large session tickets in TLS1.3 + resume flow. + + Fixed to prevent ProtoSSL running as a server using DirtyCert. + + Fixed two bugs introduced in DirtySDK 13.3.1.6.0 when code to check AES padding was added. First, in SSLv3 + the block cipher padding value is not specified and therefore should not be checked. This would likely + break any SSLv3 connection negotiated with AES. Second, the padding check itself was biased one character + forward, meaning it missed checking the very first padding byte, instead comparing the last byte (the + length) to itself. + + Fixed to support the full 64 bits of sequence number (previously, only 32 bits were supported). + + QosApi + Changed http and probing timeouts, so they are treated separately and have their own error codes. + + Fixed issue when the client stalls, it was possible to timeout without having had a chance to do the + probing. QosApi will detect client side stalls and will increase the timeout to compensate. + + Changed QosApi to reject latency packets that are well beyond expected ranges, 20 seconds. + + DirtySDK (Apple) + ProtoSSLApple + Changed the code to cleanup the secure state when we detect that the connection was closed. + + DirtySDK (XboxOne) + UserApiXboxOne + Fixed a crash due to the UserApi ref being created before users are added to NetConn by creating + ProtoHttp refs for all possible users. + + VoipHeadsetXboxOne + Changed the LoadLibraryEx input to be wrapped with a TEXT macro to ensure it works on non-UNICODE + environments. + +EADP Game Services DirtySDK 15.1.4.0.0-pr1 - December 7, 2017 + + This SDK release was built using the following compiler packages: + + EA Config 5.06.00 + Framework 7.08.02 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 170608-proxy + capilano_config 2.03.00 + Kettle kettlesdk 5.008.051-proxy + kettle_config 2.06.02 + iPhone ios_config 1.09.00 + iphonesdk 10.0-proxy-1 + OSX UnixGCC 0.10.00 + osx_config 1.15.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + + *** Important - First DirtySDK release with Speech-To-Text (STT) and Text-To-Speech (TTS) support *** + Support available for XboxOne only. PS4 and PC not yet supported + + *** Important - PS4 Random module required *** + With this version loading of the PS4 Random module is required + + *** Important - Public API changes for NetConnXboxOne *** + Removed support for -nologin parameter with NetConnStartup() and NetConnConnect(). + + *** Important - Public API changes for NetGameDistServ *** + Removed the notion of a "started" game as it proved to be completely broken. NetGameDistServStartGame() and + NetGameDistServStopGame() were removed. When a game is started or stopped (like during a blaze match replay), the + game code running on the client is in charge of exercising the netgamedist flow control. + + *** Important - Public API changes for Voip *** + Removed for good 'umic' and 'uspk' control selectors that had been deprecated for a long time already. + +New in this release: + DirtySDK (All) + Base64 + Added support for base64 url string encoding and decoding per RFC 4648 Section 5. Added new + internal functions to do the encoding / decoding to be shared between the various functions. + + Build + Added a target for building full html docs. + + CommUdpUtil + Added as a new file where code shared between commupd and dirtycast is located. Contains new util + functions with explicit names that improve readability and hide bit/byte level inspection in + commudp packets. + + CryptBn + Added functions to left and right shift by more than 1 bit at a time. Defined a macro for right shift by 1 + to mimic how left shift works. + + CryptEcc + Added a function to the module to validate if a point is on the curve. + + Added support for ECDSA signing and verification which can be used instead of RSA for those purposes. + + CryptRSA + Added support for Chinese Remainder Theorem (CRT) algorithm to speed up RSA operations that use the private + key. Since the data and sate used for the different RSA computations are different, I introduced a union + that can represent the data we are working with. + + DirtyNet + Added a minimum rate limit of 1460 bytes/sec to the SocketRate API. This also fixes the issue that + setting a rate lower than this will result in no data going through. + + Added support for IPv6 address parsing from text. + + DirtyThread + Added support for condition variables via the new dirtythread module which is powered by EAThread. + Added support for threads via the new dirtythread module which is powered by EAThread. + + NetGameDistServ + Added a call to _NetGameDistServFlowControl() in NetGameDistServAddClient() such that a player + joining results in DirtyCast announcing "I no longer receive" until the joiner explicitly confirms + "I can receive" with a call to NetGameDistControl('lrcv'). + + NetGameLink + Added the number of naks sent to peer from us to the NetGameLinkStatT structure. + + Moved 'slen' and 'sque' to be a status selector. We are keeping the control selector version of + these but they are now consider deprecated. + + Platform + Added include to to provide access to wchar_t + + Protobuf + Added a reader and writer for the protobuf (version 3) wire format. + + ProtoHttp + Added secure proxy support using HTTP CONNECT. + + Added global proxy setting via new 'gpxy' ProtoHttpControl selector. + + Added an additional request API to specify custom and receive header callbacks, which take priority over the + global callbacks. + + ProtoHttpServ + Added a listen function to allow users to specify their own TCP backlog settings. + + ProtoSSL + Added TLS1.3 protocol support, implementing draft-18 through draft-21 versions at compile-time (defaulting + to draft-18). TLS1.2 remains the default client-requested version. + + Added support for sha512 handshake hash. + + Added debug output of Master Secret in a form suitable for Wireshark decryption via (Pre)-Master Secret log + file. + + ProtoWebSocket + Added message sending (ProtoWebSocketSendMessage) and receiving (ProtoWebSocketRecvMessage). Already + existing Send/Recv functions use a stream-like metaphor. + + QosApi + Added QosApiControl('ncrt') - passed to ProtoHttpControl, if TRUE will proceed even if SSL cert errors + are detected (defaults FALSE), for debug purposes. + + Added QosApiStatus('ncrt') - get the 'ncrt' setting, if TRUE we will ignore SSL errors for setup + communication. + + Added QosApiControl('soli') - passed down to the socket as a SO_LINGER option if its >= 0, a 0 value + will abort the socket on disconnection with no TIME_WAIT state (ideal for stress testing). + + Added QosApiStatus('soli') - get the value passed to ProtoHttp 'soli'. + + Voip + Added VoipSetTranscribedTextReceivedCallback() for voipgroup layer to register a callback used by + lower-level voip to notify about inbound transcribed text over voip connections. + + VoipConnection + Added a per-connection outbound sequence number instead of the global one, needed to support a + scenario where transcribed text is not sent on ALL connections. + + Added a NULL check when setting bPeerIsAcking as based on code before it pConnection might be NULL. + + VoipDef + Added XoneSynthesizedSpeechCfgT to be used with VoipControl('voic'). + + Added VoipTranscribedTextReceivedCallbackT which is the prototype of the callback registered with + VoipSetTranscribedTextReceivedCallback() and VoipGroupSetTranscribedTextReceivedCallback(). + + VoipGroup + Added VoipGroupSetTranscribedTextReceivedCallback() for game code to register a callback used by + DS to notify about inbound transcribed text over voip connections. + + VoipPacket + Added new VOIP_PACKET_STATUS_FLAG_STT, flagging that at least one user on the originating side as requested + transcribed text to be sent. + + Added aRemoteClientId field to ReliableDataInfoT. Required to uniquely identify who the reliable data is + for. + + VoipTunnel + Added new events for adding and deleting games from the VoipTunnel. + + VoipHeadset + Added VoipHeadsetDisplayTranscribedText() to display transcribed text in system overlay. + + DirtySDK (Apple) + DirtyErrUnix + Added the errors from Apple's Security.framework headers SecBase.h and SecureTransport.h + + ProtoSSLApple + Added an implementation of ProtoSSL that is driven by Apple's SecureTransport library. + + DirtySDK (PC) + DirtyNetWin + Added SocketControl('soli') - set SO_LINGER On the specified socket, iData1 is timeout in seconds. + + Added support for IPv4 sockets, required for Windows XP compatibility. Use SocketControl('afam') control + selector to set global address family to use for sockets. + + VoipAux + Added support for the Opus VoIP codec. This codec uses the default settings and 8kHz sampling rate. + + VoipHeadsetPc + Added support for variable length frames for PC codec's which is needed to add Opus support. To allow the + codec to choose if it can support variable length frames, have the headset status fallthrough down to the + codec. Due to the current codecs not supporting variable length frames, I put the unhandled logging message + at a higher verbosity to prevent spamming. Changed the voice data callback to only validate the size of the + frames when not using variable length frames. Changed the calculation of sub-packet size to work for both + variable and fixed frames. + + DirtySDK (PS4) + DirtyNetPS4 + Added support for SO_LINGER socket option via the 'soli' control like on PC/Unix + + NetConnPS4 + Added NetConnStatus('locl') and NetConnStatus('locn') API to fetch locale. + + Added support for -delaySEMD in NetConnStartup to ignore the state of SystemEventMessageDispatcher. + + ProtoHttpPS4 + Added rate limiting. + + DirtySDK (Unix) + DirtyNetUnix + Added SocketControl('soli') - set SO_LINGER On the specified socket, iData1 is timeout in seconds. + + VoipUnix + Added as a new file. Not real voice. Artificial voip data injection on voip connections. Injected + sub-packet rate and sub-packet size are controllable via voip control selectors ('psiz', 'prat'). + + DirtySDK (XboxOne) + UserApiXboxOne + Added support for retrieval of the TTS/STT settings that are a part of the profile data. + + Added Accessibility field to UserApiProfileT. + + VoipHeadsetXboxOne + Added support for leveraging new Text-to-speech (TTS) and Speech-to-text (STT) features in XboxOne XDK. + * Added 'ttos' control selector to be used by the game code to feed in text to be synthesized (from + a speech-impaired user). + * Added 'stot' control selector to be used by the game code to enable local generation of transcribed + text to be sent reliably to all remote peers that requested it (i.e. hearing-impaired user). + * Added 'voic' control selector with platform-specific params used for identifying "voice" used when + producing synthesized speech. + + VoipSerializationXboxOne + Added "game display name" property to the remote user object because we need to feed that into. + Windows::Xbox::UI::Accessibility::SendSpeechToTextString(). So that information is now part of the + user serialization. + +API Breaking Changes in this release: + DirtySDK (All) + Build + Removed support for non-namespaced include imports, you must use the full path. + For example: versus + + CommTcp + Removed CommTCP for good. + + ConnApi + Renamed 'phcc' selector (PC Host Capilano Client) to 'phxc' (Pc Host Xboxone Client). ('phxc' used to + stand for "PC Host Xenon Client" but it was free now that xbox360 is no longer supported.) + + Removed the option of using ConnApi as the owner of voip. Calling VoipStartup/VoipShutdown is now ALWAYS + required before using ConnApi with voip-enabled topologies. + + DirtyLang + Removed LOBBYAPI_LANGUAGE_TEGULU, which was replaced with LOBBYAPI_LANGUAGE_TELUGU long time ago. + + Removed LOBBYAPI_LANGUAGE_UIGUR, which was replaced with LOBBYAPI_LANGUAGE_UIGHUR long time ago. + + Removed LOBBYAPI_LANGUAGE_WELCH, which was replaced with LOBBYAPI_LANGUAGE_WELSH long time ago. + + Removed LOBBYAPI_COUNTRY_SLOVAKIA_, which was replaced with LOBBYAPI_COUNTRY_SLOVAKIA long time ago. + + Removed LOBBYAPI_COUNTRY_ST_HELENA, which was replaced with + LOBBYAPI_COUNTRY_ST_HELENA_ASCENSION_AND_TRISTAN_DA_CUNHA long time ago. + + NetGameDistServ + Removed the notion of a "started" game as it proved to be completely broken. + NetGameDistServStartGame()/NetGameDistServStopGame() were removed. When a game is started or stopped + (like during a blaze match replay), the game code running on the client is in charge of exercising the + netgamedist flow control. + + Updated the stats structure to include more useful information and trimming the sizes down. There was no + need to include ping due to the fact that it can be derived from latency if needed. Added the send queue + length to allow detection when the queue is filling up at the lowest level. To allow us to get this I added + a fallthrough from NetGameDistStatus to NetGameLinkStatus. Added the packet loss and nak information as more + indicators to problems on the client->server connection. + + NetGameLink + Renamed NETGAME_QOS_DURATION_MIN to NETGAMELINK_QOS_DURATION_MIN and moved it from public domain to private + domain. + + Renamed NETGAME_QOS_DURATION_MAX to NETGAMELINK_QOS_DURATION_MAX and moved it from public domain to private + domain. + + Renamed NETGAME_QOS_INTERVAL_MIN to NETGAMELINK_QOS_INTERVAL_MIN and moved it from public domain to private + domain. + + Renamed NETGAME_QOS_PACKETSIZE_MIN to NETGAMELINK_QOS_PACKETSIZE_MIN and moved it from public domain to + private domain. + + Changed the 'mwid' to a status from a control based on a v8 TODO we still had outstanding. + + Voip + Removed for good 'umic' and 'uspk' control selectors that had been deprecated for a long time already. + + Removed VoipDisconnect() and renamed VoipDisconnect2() to VoipDisconnect(). + + Removed the uAddress parameter from the VoipDisconnect() synopsis. + + Removed VoipSpeaker and VoipMicrophone from the public interface. This API can be misused by customers and + given that it used to be the way to deal with muting we want to remove it to prevent incorrect use. + + VoipGroup + Removed VoipGroupConnect() and renamed VoipGroupConnect2() to VoipGroupConnect(). + + Removed VoipGroupResume() and renamed VoipGroupResume2() to VoipGroupResume(). + + DirtySDK (XboxOne) + NetConnXboxOne + Removed support for -nologin parameter with NetConnStartup() and NetConnConnect(). -nologin used to be + exercised by FIFA tools. Instead of using -nologin, tools can just call NetConnStartup() and not + call NetConnConnect(). Implicitly that means tools do not need to wait for netconn to reach +onl state + before doing generic networking activity. + +Changes/fixes in this release: + DirtySDK (All) + Build + Changed the documentation generation to use the headerfiles fileset which is updated to include the correct + files across platform. This allows us to reduce the complexity of our doxygen configuration generation as + it now works similarly to the sourcefiles fileset. + + Fixed hack that gets a property that just equates to ${config-system} with just ${config-system}. + + Changed the build scripts to explicitly build against the newest C standard (c11). We currently are not + supporting the older standards and don't want to assume a default. This fixes some issues with how some + 1st Party APIs are defined. + + Common + Removed the platform specific zlib functionality, instead we call into DirtySDK which implements it already + for all platforms. + + ConnApi + Removed parameter validation from ConnApiControl('sqos') and ConnApiControl('lqos') to eliminate + code duplication with equivalent validation made in NetGameLinkControl('sqos') and + NetGameLinkControl('lqos'). + + Removed the ProtoUpnp code from ConnApi, which is already handled in NetConn. The ConnApi module would be + doing the UPnP operations too late for it to be of value as we want this to be available early so it can be + updated in the user's information. + + Refactored the virtualisation of the VoIP ports to the module that owns the VoIP connection. In a dedicated + server scenario, ConnApi does not establish any VoIP connection as this is handled by the VoipTunnel owned + by the ConnApiVoipManager. + + Refactored ConnApiRemoveClient to validate the index passed in to ensure that it is the valid range. + + Removing code relating to increasing our connection timeout required to create a secure association used for + legacy failover mode (which was removed). + + CommUdp + Enhanced internal implementation with major cosmetic fixes to re-align with coding standard. No functional + impact. + + Removed code duplication between commudp and voipservercomm by moving shared code to a new file called + CommUDPUtil. + + CryptBn + Changed the right shift operation to clear the memory when shrinking the width to match what we do in other + places. + + CryptEcc + Changed the output of the functions CryptEccPublic, CryptEccSecret and CryptEccSign to be optional. + + CryptRSA + Changed the internal exponentiation function so it can take in different data for different needs. + + Refactored the RSA exponentiation code to use a sliding window algorithm as an optimization. + + DirtyAddr + Changed DIRTYADDR_MACHINEADDR_MAXLEN from 127 to 372 for xboxone specifically to fix a rare issue demonstrated + in production where the SecureDeviceAddr blob would be clamped when stored in a DirtyAddrT because too large. + After discussing with MS, 372 was identified as the right size to avoid this kind of issue. + (See comment in the code for more details) + + DirtyCert + Changed the 'prld' to use the correct variable when changing a value based on a control. It was using iControl + which tells us which control we are working on instead of iValue. + + DirtyJpg + Fixed an unusual case where the huffman decoder was reading past the end of the input file. + + DirtyLib + Removed some erroneous extra argument provided to NetPrintf. + + Changed the array printing code to add the opening brace on its own log line. This allows the array to be + copied more easily from the log files. + + Changed NetPrintfVerboseCode to only call ds_vsnprintf when the verbosity is over the check level. + + Removed unused defines from the Gen3 era. + + Refactored the NetCrit system to be driven by EAThread instead of being implemented in place. + + DirtyNames + Removed unneeded extern for function declarations. + + DirtyNet + Changed to refactor address conversion to text and address printing, moving some code from plat-str to + dirtynet, and consolidated and rearranged. Also updated for coding standards, and reordered address + functions to make more sense and align with header. + + Cleaned up the ambiguity about what an INVALID_SOCKET descriptor means. Protect operations that + require a valid socket descriptor or that valid only when the socket has not been added to the kill + list. When shutting down a socket we want to make sure that we protect the socket for the short time + before it is actually released. + + Changed the map translate print to level 3 verbosity based on how often we print this. + + Fixed the code so we have the tick when we finish the receive into allocated packet queue entries. When + looking at the time between adding an entry to the packet queue and removing we were noticing that packets + off the physical socket were reporting misleading times. + + MurmurHash3 + Changed the code to use the EABase little endian define. The current code was not correctly applying the + little endian logic for all the support systems. + + NetConnCommon + Moved implementation of netconn external cleanup mechanism to netconncommon to eliminate code duplication + in netconnxboxone and netconnps4. + _NetConnAddToExternalCleanupList() becomes NetConnCommonAddToExternalCleanupList(). + _NetConnProcessExternalCleanupList() becomes NetConnCommonProcessExternalCleanupList(). + + Moved iRefCount from platform specific implementations to NetConnCommonStartup. Moved the associated printf + on PC and Unix for consistency in the logging. + + Fixed an issue with NetConnStartup, NetConnShutdown and DirtyCert from the recent merging on NetConnCommon + into all platforms. + + NetGameDist + Enhanced internal implementation with major cosmetic fixes to re-align with coding standard. No functional + impact. + + Changed implementation of NetGameDistInputLocalMulti() to improve readability without impacting + functionality: rewrote a very complex if statement in a more explicit manner. + + Removed the unused variable iStrmMaxPktSize, leftover from when dist owned the streams. + + Moved the status selectors to the NetGameDistStatus function from NetGameDistControl to align with the rest + of DirtySDK + + NetGameDistServ + Fixed flow control handling when Join-in-Progress is exercised. + + Fixed usage of _NetGameDistServSanityCheckIn(). It is meant to be used after a call to + NetGameDistInputPeek(), but it was not used like that before this fix. + + Removed unused NetGameDistServT::aInKeepAlive and NetGameDistServT::aOutKeepAlive. + + Adjusted the send and peek timing logging to be a multiple of the configured fixed rate to prevent spamming + the logs when on higher fixed rate configurations. + + NetGameLink + Enhanced internal implementation with major cosmetic fixes to re-align with coding standard. No functional + impact. + + Renamed KEEP_ALIVE_TIME to NETGAMELINK_KEEPALIVE_TIME. + + Enhanced the send packet code to prevent sync packets from appending to unreliable packets. The sync + packets are used to drive the stats we provide at the NetGameLink level, dropping one of these packets would + skew this information. + + Changed NetGameLink to adjust the stream max packet size based on the CommUDP max width. + + Plat-str + Added ds_fmtoctstring(), which formats hexadecimal text from an input binary blob of data. + + ProtoHttp + Fixed a bug where the base url was not correctly processed when using a proxy server. + + Changed ProtoHttpStatus() function to pass down unhandled selectors to ProtoSSLStat(), rather than directly + returning a -1 unhandled result. + + Fixed crash in the instance we call ProtoHttpRecvAll with a NULL buffer but the response has no body. + Normally when a response has a body the use of a NULL buffer and size of zero will prompt ProtoHttp to tell + us that we need to allocate more space to be able to receive. If there is no body and we get a success right + away if will try to null-terminate that buffer which would lead to a crash. + + ProtoHttp2 + Changed ProtoHttp2Status() function to pass down unhandled selectors to ProtoSSLStat(), rather than + directly returning a -1 unhandled result. + + Fixed a missing NetPrintf argument for [%p] print (pState). + + Enhanced the logging for when we get an invalid stream id in a frame that requires one. + + Changed the trigger for incrementing the flow control window to when it will be under the size of the window + to prevent pausing the download. + + Fixed out of bound access when trying to print invalid frame types. + + ProtoHttpManager + Changed the code to prevent a dereference of NULL when logging an error as the pHttpCmd variable might be + NULL. + + ProtoHttpServ + Fixed crash in protohttpserv when a http2 client is used. + + Increased the size of the URL data in the request structure to 128 bytes. + + Fixed a bug with not setting the correct maximum buffer size for the write callback. The maximum needed to + account for the amount of data that was already written. + + ProtoMangle + Removed unnecessary platform-specific code used while generating random port. From now on, a port range of + 6000 is used on all platforms. + + ProtoSSL + Changed TLS_ECDHE_RSA_WITH_AES_(128|256)_CBC_SHA to be TLS1.2+ only after testing revealed issues with + TLS1.1 and prior. + + Fixed issue where _ProtoSSLRecvHandshakeFinish() was processing multiple handshake packets at once, which + could cause the handshake hash to be computed for more handshake packets than was correct. This did not + cause an issue prior to TLS1.3, but resulted in bad handshake hash calculations in the CertificateVerify + and Finished messages received from mozilla servers (which send the Certificate and CertificateVerify in + one SSL frame) when using TLS1.3. + + Fixed _ProtoSSLRecvServerKeyExchange() to validate the RSA-PKCS1.5 signature padding (in addition to adding + RSASSA-PSS validation for TLS1.3). + + Changed signature validation to use generated object comparison rather than parsing the included object. + This is generally understood to be better practice than ASN.1 parsing, which is error-prone. + + Changed to consolidate some other code (debug printing of premaster and master secrets, clearing premaster + secret from memory) in _ProtoSSLBuildKey(). + + Fixed a possible issue in _ProtoSSLHandshakeHashGet() where an unknown hash result from CryptHashGetSize() + could fall through without error. Changed handling of a hash too large for the provided buffer by + truncating the size and continuing rather than returning an error. + + Fixed an issue where a certificate date validation failure would not fail the connection IFF the signing + certificate was not available locally but was successfully installed on-demand by DirtyCert. + + Changed to refactored secure data receive flow, and increased internal receive buffer. This allows + handshake messages that are larger than a single SSL record to be received successfully, as well as in + general simplifying and cleaning up the receive code. While fragmented SSL handshake messages are very + rare they can happen when a very large Certificate is sent by the server, for example. + + Fixed _ProtoSSLUpdateRecvCertificate() to validate outer certificate envelope size. + + Fixed to add validation of ChangeCipherSpec message size and content. + + Changed the signature verification to heap allocate the CryptRSA state. The size of the CryptRSA state can + be rather large due to the number of CryptBnT structures that we use in certain conditions. This is the + only instance of us using a stack allocated CryptRSA state and we rather prevent stack overflow issues. + + Fixed a crash by ensuring we successfully get the Hash ASN object before trying to generate a hash. + + Changed the certification validity expiration prints to only be done when DEBUG_VAL_CERT is enabled. + + Changed ProtoSSLShutdown to clear the stored CAs. + + ProtoTunnel + Changed the send callback to early out when sending on the server socket used for xbox one. This socket + should be treated the same as the normal prototunnel socket, we will not try to match a tunnel. + + Refactored the tunnel flush timeouts to check the last send time on individual tunnels instead of mass + flushing all tunnels regularly regardless of when the last send occurred. This should help improve packet + bundling behavior. + + ProtoWebSocket + Fixed an issue where receiving an empty control frame (e.g. a ping) would cause the receiving to get stuck. + + Added logging when we enforce the minimum keep-alive internal of 30s to let users know what is happening. + + QosApi + Removed an unused function prototype. + + Tools + Removed the mapparse and mpsummary tools as they are no longer used. The source code for these can be found + at //gos/tools/dirtyutils. + + UserListApi + Added a comment to specify that a certain structure is only valid within the UserListApiCallbackT. + + Voip + Changed VoipSetEventCallback() to now enter the ThreadCrit before proceeding. This is to avoid any + potential theoretical race condition where the voipthread would kick in and use the callback while the + main thread is in the middle of altering it. + + VoipAux + Updated the build script's include to make sure that it adds the package.dir to the fromfile attribute. + The basedir only applies to the files in source.txt and not the fromfile call itself. + + VoipCommon + Changed VoipCommonStartup() to now take a new input parameter called pTextDataCb() invoked internally + when the system notifies readiness of newly locally generated transcribed text (ready for transmission + over the network). + + Fixed potential buffer overrun caused by trying to set channel settings on the shared audio input device + on a platform that doesn't support it. + + Changed the logic to make sure that we have a valid index into LocalUsers/RemoteUsers for the 'luid'/'ruid' + status selectors. + + Changed the check for support of the shared user index, checking negative does not work against 0xff when + checking against an int32_t type. Instead of checking for negative we want to check that the shared user + index is not set to the invalid value. When selecting the channel it did not seem as if the original check + was doing what was desired. + + Changed the shared user index checks to compile time instead of runtime to prevent certain compilers + generating unreachable code warnings. + + VoipConnection + Changed implementation to properly initialize the per-connection voice send timers used for sending at + a fixed 10hz rate. Prior to this change, there was a possibility for the first voip MIC packet being sent + to contain more than 100 ms of voice (i.e. the packet would keep filling up to the max sub-packet limit). + + Fixed setting/testing of the VOIP_PACKET_STATUS_FLAG_STT flag. + + Enhanced logics protecting against reliable data packing overflow. + + Changed implementation to now pack and unpack target client id with reliable data. Allows for ignoring + inbound reliable data for which we are not the target when receiving voip MIC packet from the voipserver. + + Fixed the name of the _VoipEncodeFlags and _VoipDecodeFlags function to _VoipEncodeLocalHeadsetStatus and + _VoipDecodeRemoteHeadsetStatus respectively to better reflect their purpose. + + Changed how much channel configuration data we send in the ping packets. Added a channel user indices bitset + to flag which indices the channel configuration corresponds to. This allows us to send less channel + configuration data in the ping packets. + Removed the fixed channel configuration from the ping packet and use the variable data where the reliable + data used to live to write the needed channel configuration. This bumps the VoIP packet version to 'h'. + + VoipGroup + Fixed a cleanup ordering issue in _VoipGroupManagerShutdown() that could result in a crash when + _VoipGroupManagerEVentCallback() is invoked from the voip thread. + + Added support for VOIP_CONNID_ALL in the VoipGroupMuteByXXX methods to affects all users in the VoipGroup + at once. + + Refactored the module ref logging to use %p instead of 0x%08x. + + Added VoipGroupMuteByClientId3. + + Added VoipGroupMuteAll, which stacks with the other VoipGroupMuteByXXX methods. + + VoipHeadset + Changed VoipHeadsetCreate() to now take a new input parameter called pTextDataCb() invoked internally + when the system notifies readiness of newly generated transcribed text. + + VoipMixer + Removed unused parameter iMixBufferUnused from VoipMixerAccumulate() synopsis. + + VoipPacket + Removed VOIP_MAXSUBPKTS_PER_PKT. Had not been needed since _VoipConnectionIsSendPktReady() had + been added. + + Removed the VOIP_PACKET_RELIABLE_FLAG_FIRST flag, no longer needed. + + Changed the value of VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF and VOIP_PACKET_RELIABLE_FLAG_DATA. + + VoipTunnel + Fixed the client flags to properly reset the VOIPTUNNEL_CLIENTFLAG_RECVVOICE when a user leaves the game. + + Enhanced the send logic to no longer call the user callback when send fails. We want to correctly represent + how much data we are sending, this could potentially misrepresent our metrics. This aligns with what we do + for game traffic. + + Fixed missing NetPrintf argument. + + XmlParse + Removed assignment of a variable that gets reassigned on the next line. + + DirtySDK (PC) + CommUdp + Fixed an incorrect sizeof() in PC-specific portion of _CommUDPListen0(). + + DirtyNetWin + Fixed incorrect debug printfs for the 'keep' selector. + + Switched to using a uintptr_t variable to store the temp handle when creating our threads so we don't need + to have a 32-bit / 64-bit check. + + VoipPc + Changed implementation to align with new synopsis of VoipCommonStartup(). + + Fixed the duplicate check by checking the right most condition to use the correct define. + + DirtySDK (PS4) + CryptRand + Refactored the PS4 implementation to use the random library provided by the SDK. Due to only being able to + generate 64 bytes at a time, we use it to seed our random. With this change you are now required to load the + Random module. + + DirtyLibPS4 + Refactored _NetTickCount to use sceKernelGetProcessTimeCounter function which stops while the system is + suspended. + + DirtySessionManagerPs4 + Refactored the control code to align more closely with other DirtySDK modules. We want to get rid of the + large switch statement in the beginning of the function as it is hard to maintain. Instead we will return an + error when we reach the end of the invitation controls if we are doing the sessions via the server. + + NetConnPS4 + Refactored internal implementation. Changed NetConnUserT to be a collection of fields belonging to a single + user instead of a collection of arrays indexed with the user index. + NetConnUserT::aNpid[] renamed to NetConnUserT::NpId. + NetConnUserT::aNpState[] renamed to NetConnUserT::NpState . + NetConnUserT::aUserServiceUserId[] renamed to NetConnUserT::UserServiceUserId. + NetConnUserT::aAuthCode[] renamed to NetConnUserT::AuthCode. + NetConnUserT::aParentalCtrls[] renamed to NetConnUserT::ParentalCtrls. + NetConnUserT::aPsPluss[] renamed to NetConnUserT::PsPlus. + NetConnUserT::aNpAvailability[] renamed to NetConnUserT::NpAvailability. + NetConnRefT::NpUsers renamed to NetConnRefT::aUsers[]. + Created NetConnNpRequestTypeE. Needed for new synopsis of _NetConnUpdateNpRequests(). + + Enhanced internal implementation. + * For consistency with how things are done in other DirtySDK files, local variables declaration made in for + loops were moved to the beginning of the function. + * Renamed some local variables with more explicit names. + * Made debug logging more consistent across the files. + * Uniformized usage of i, iIndex, iUserIndex and iLocalUserIndex --> it is now iLocalUserIndex everywhere. + + Refactored internal implementation: moved implementation of netconn external cleanup mechanism to + netconncommon to eliminate code duplication, and also slightly modified implementation details + such that the new common api is used in a more uniformed manner. + + Changed from using IEAUser to KettleUser to align with the code used in netconnxboxone. + + ProtoHttpPS4 + Fixed a potential crash when running out of system resources. + + Fixed the handling of an error when rate limiting reduced the number of bytes to send to 0. + + Fixed an issue with rate throttling that prevented request with no payload from completing. + + Refactored the timeout behavior to use the our own timeouts rather than the native PS4 timeout system to + better match what we do on other consoles. + + Fixed the return value of the 'data' status selector to return the size of the header plus the currently + received size of the Http body. + + VoipHeadsetPS4 + Modified implementation to now unconditionally apply most restrictive voip settings to comply with Sony TRC + R4061 when there are multiple local users interacting with a game (note: that's different from EA MLU which + implies multiple users authenticated with Blaze). Under such conditions, we used to apply most restrictive + voip settings only if users were sharing output audio via TV and input audio via PS4 cam mic. If both + users were identified to be using dedicated headsets, we used to apply respective privileges on a + per-headset basis. Sony confirmed this being an incorrect strategy + (https://ps4.scedev.net/support/issue/131717/_Need_help_with_interpretation_of_TR_4061) as there is no way + to make sure that the restricted user is not hearing the voice even if headsets are detected because what + is detected as a headset connected to a controller can rather be a speaker. + + VoipPS4 + Changed implementation to align with new synopsis of VoipCommonStartup(). + + DirtySDK (Unix) + DirtyLibUnix + Removed the NetTickSet function which was previously used to workaround an issue with an older version of + the linux kernel. + + Removed the _NetLib_bCritEnabled variable which has been replaced with a NetConn startup parameter. + + DirtyNetUnix (All) + Moved global socket control 'spam' to _SocketControlGlobal while leaving per socket 'spam' control in + SocketControl(). This does not change any behavior but aligns the grouping of global controls and socket + specific controls. + + Aligned with dirtynetwin regarding the logging in SocketConnect, we should be logging the remote address + after translation. + + Changed SocketDestroy to wait for any current socket lookups to finish processing. If there is a current + socket lookup processing and we destroy the hostname cache, after the processing is finish it will segfault. + + DirtyNetUnix (iOS/OSX) + Fixed a bug that broke iOS IPv6 compatibility, introduced in the 15.1.3.0.0 SDK release. + + DirtySDK (XBoxOne) + NetConnXboxOne + Refactored internal implementation: moved implementation of netconn external cleanup mechanism to + netconncommon to eliminate code duplication, and also slightly modified implementation details + such that the new common api is used in a more uniformed manner. + + Fixed an incorrect debug trace in _NetworkStatusThread(). + + Refactored to cache the value of NetConnStatus('tria') so avoid the performance hit from accessing the + 1st party object. + + ProtoHttpXboxOne + Refactored calls to 1st party API which take a long time to resolve so that they are run in a seperate + thread. + + Refactored to move the Open() and initialization of the IXMLHttpRequest2 object into a task to prevent its + long execution time from causing visual glitches when an HTTP request is performed. + + Refactored so that the request header callback would fire on the same thread as the http requestor thread. + This is a requirement for WebKit. + + Fixed a potential crash when cleaning up the XMLHTTP object. + + Fixed an issue for request requiring DirtyCert, the DirtyCert request was not complete at the time we were + trying to query the token which caused these operations to fail. + + Refactored threads so that the custom header callback is triggered on the same thread as the caller of + ProtoHttpUpdate(). + + Refactored the process kill list to accomodate part of the clean up process occuring in a new thread. + + Fixed an error that set the XmlHttp object to NULL incorrectly. + + Fixed a race condition by forcing all XmlHttp objects to be destroyed via the kill list. + + Moved a call to _ProtoHttpRequest on a thread inside the critical section. + + Fixed an occasional Nucleus login issue by working around an error the MS api returns after reaching the + maximum number of redirections. + + Fixed an issue where the protohttp reference can be destroyed while an asynchronous task is in progress. + + Fixed an issue where a critical section was not left before an early return. + + ProtoMangleXboxOne + Removed usage of the PROTOMANGLE_STATE_UNLOCK state for security associations. This state was an + unnecessary state because the "unlocking" step has never really been enforced by Microsoft. + It was a pre-gen4 launch concept that did not become real at gen4 launch. + + ProtoTunnel + Fixed to track up to sixteen different remote server ports for the server socket. + + Changes to status/control selectors: + -changed 'bnds' control selector to not recreate the server socket if it already exists. + -changed 'bnds' status selector to return if the specified port is in the list of server ports. + -changed 'bnds' status and control selectors to use iValue instead of iValue2 for the server port. + -added 'bndr' control selector to remove a mapped server port. + + VoipHeadsetXboxOne + Removed while loop in _VoipHeadsetPollForVoiceData() because there is no good reason to read more + that one sample bundle per voip thread iteration. Moreover, the while loop proved to be problematic + with TTS specifically because the capture source suddenly has a lot of samples ready (... much more + that what typically is delivered in a 20 ms sampling window) and that resulted in the originator + filling up the inbound receive queues of consumers. + + Changed implementation to use NetTickUsec() instead of NetTick() to get usec precision when logging + voice submission activity. + + Fixed a vector by changing it to an immutable vector to prevent a possible crash. + + VoipXboxOne + Changed implementation to align with new synopsis of VoipCommonStartup(). + + Changed implementation to properly support new Text-to-speech (TTS) and Speech-to-text (STT) features + in XboxOne. These changes became unavoidable with STT/TTS because without them it was nearly impossible + to play back successfully a very long input text. The inbound queue on the consumer side would just + continuously overflow. + * Changed VOIP_THREAD_SLEEP_DURATION from 18 ms to 20 ms. It used to be 18 ms because we had + problems pumping the voip thread at a sharp 20 ms rate. This problem was resolved by replacing + calls to NetTick() with calls to NetTickUsec() in _VoipThread(). NetTick() does use the high + res timer internally but it rounds up the result when it returns a ms value. The previous + implementation ended up not being able to create a sharp 20 ms pump rate because it was + subtracting two rounded up value. The new implementation is performing a single usec to + msec conversion at the last minute when it is about to call NetConnSleep(). + * Also, the priority of the _VoipThread() was changed from THREAD_PRIORITY_HIGHEST to + THREAD_PRIORITY_TIME_CRITICAL. The new implementation is much closer to what MS does in the + GameChat DLL. + + +EADP Game Services DirtySDK 15.1.3.0.5 - October 10, 2017 + + This SDK release was built using the following compiler packages: + + EA Config 5.01.00 + Framework 7.01.00 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 170302-proxy + capilano_config 1.05.07 + Kettle kettlesdk 4.508.111-proxy + kettle_config 2.01.00 + iPhone ios_config 1.07.00 + iphonesdk 8.0-proxy-3 + OSX UnixGCC 0.10.00 + osx_config 1.11.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + ProtoWebSocket + Added APIs to allow sending of text data to the server. Our other APIs only allowed sending binary data + but the protocol also supports text, for more information see: https://tools.ietf.org/html/rfc6455#section-5.6 + +Changes/fixes in this release: + DirtySDK (All) + ConnApi + Fixed negative indexing when updating connection timers for the DirtyCast client. + + MurmurHash3 + Changed the function name of the internal implementation of rotl64 to prevent colliding on other systems + that might provide it. + + ProtoHttp + Fixed a write callback bug when all data is available immediately, in which case the write callback was + completing the transaction before the data could be read. + + ProtoTunnel + Fixed an Xbox One specific issue when a player is in multiple meshes with more than one server, e.g. in a + situation where a user is in a CC-assisted game group while being in a dedicated server game. In such an + instance, the packets for the first server connected to would cease to flow when the second server + connection was established. + + DirtySDK (PS4) + ProtoHttpPS4 + Refactored the timeout behaviour to use our own timeouts rather than the native PS4 timeout system to better + match what we do on other systems. + + DirtySDK (XboxOne) + ConnApi + Changed the size of a local variable used to store a SecureDeviceAddress as it could potentially be not + large enough for some theoretical networking conditions. + + DirtyAddr + Changed DIRTYADDR_MACHINEADDR_MAXLEN from 127 to 372 for xboxone specifically to fix a rare issue + demonstrated in production where the SecureDeviceAddr blob would be clamped when stored in a DirtyAddrT + because too large. After discussing with MS, 372 was identified as the right size to avoid this kind + of issue. (See comment in the code for more details) + + NetConnXboxOne + Fixed an issue with resume where if we didn't set the the status thread connectivity level to none will + cause netconn to skip the xbl_wait state causing connection failures + + ProtoHttpXboxOne + Fixed an issue where a redirect request would never be killed when redirection is disabled due to it never + going into the completed state. + + VoipHeadsetXboxOne + Fixed a potential crash in a race condition to create a chat participant for a bystander. + +EADP Game Services DirtySDK 15.1.3.0.4 - August 30, 2017 + + This SDK release was built using the following compiler packages: + + EA Config 5.01.00 + Framework 7.01.00 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 10.0.14393.2156-proxy + capilano_config 1.05.07 + Kettle kettlesdk 4.508.031-proxy + kettle_config 2.01.00 + iPhone ios_config 1.07.00 + iphonesdk 8.0-proxy-3 + OSX UnixGCC 0.10.00 + osx_config 1.11.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +New in this release: + DirtySDK (All) + ProtoHttp/ProtoHttp2 + Added a timeout error (-7) when our module level timeout hits to allow the caller to differentiate between + errors at the lower protocols currently driven by HRESULT. + + ProtoHttp2 + Added support for HTTP/2 frame padding, see: https://tools.ietf.org/html/rfc7540#section-10.7 + + VoipTunnel + Added VoipTunnelStatus('talk') which returns the current count of "talking" clients, which is a client for + which the VoipTunnel detects inbound voip traffic. + +Changes/fixes in this release: + DirtySDK (All) + Build + Changed the optimized buildtype to correctly derive from what buildtype we are current building (static / + dynamic) using ${dirtysdk-buildtype}. + + CommUDP + Fixed an issue in CommUDPSend() where a full reliable buffer would prevent unreliable packets from being + sent. + + Hpack + Fixed the string decode function to initialize the output to NULL to prevent from freeing stack memory + when there is temporary data still being pointed to from previous functions. + + ProtoWebSocket + Updated the initial handshake receive to be copied into a temporary buffer and changed how it detects we + have the complete handshake header. Previously, if we get the HTTP/1.1 header data and some of the websocket + data combined in a single receive call, we have trouble getting into the open state as expected. + + DirtySDK (XboxOne) + ProtoHttpXboxOne + Fixed the header parsing to replace the final \r\n with NULL characters to align with our other protohttp + implementations. This is required for the headers to be compatible with the ProtoHttpUtil header parsing + functions. + + Removed the memmove in recv that was causing performance issues. On Xbxone the memmove is not really necessary + since we always allocate a buffer big enough for the full response. + + UserListApiXboxOne + Fixed a crash that happens when reading the XboxUserProfile results by adding null checks and exception handling. + +EADP Game Services DirtySDK 15.1.3.0.3 - July 12, 2017 + + This SDK release was built using the following compiler packages: + + EA Config 5.01.00 + Framework 7.01.00 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 10.0.14393.2156-proxy + capilano_config 1.05.07 + Kettle kettlesdk 4.508.001-pr-proxy + kettle_config 2.01.00 + iPhone ios_config 1.07.00 + iphonesdk 8.0-proxy-3 + OSX UnixGCC 0.10.00 + osx_config 1.11.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +Changes/fixes in this release: + DirtySDK (All) + Build + Updated the voipaux sourcefiles includes to make that it adds the package.dir to the fromfile attribute. + The basedir only applies to the files in source.txt and not the fromfile call itself. + + ConnApi + Updated ConnApiRemoveClient to validate the passed in index to ensure it is in the valid range we + support. + + Removed the virtualization of the voip port when voip is disabled. + This allows for virtualization to only be done by modules that own the voip connection. + + DirtyNet + Updated the async receive code to cleanup the packet queue entry when receiving data into it results + in an error, allowing it to be used again. + + NetGameDistServ + Removed the notion of a "started" game as it proved to be completely broken. + When a game is started or stopped, the game code running on the client is in charge of exercising the + netgamedist flow control. + + ProtoHttp2 + Updated the processing of the url when using a proxy server, we needed to update the url after doing our + parsing. + + ProtoHttpServ + Updated the URL length in the request data to 128 bytes. + + DirtySDK (Unix) + DirtyNet + Updated SocketConnect logging to align with dirtynetwin which logs the remote address after translation. + +EADP Game Services DirtySDK 15.1.3.0.2 - June 1, 2017 + + This SDK release was built using the following compiler packages: + + EA Config 5.01.00 + Framework 7.01.00 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 10.0.14393.2156-proxy + capilano_config 1.05.07 + Kettle kettlesdk 4.508.001-pr-proxy + kettle_config 2.01.00 + iPhone ios_config 1.07.00 + iphonesdk 8.0-proxy-3 + OSX UnixGCC 0.10.00 + osx_config 1.11.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +Changes/fixes in this release: + DirtySDK (All) + CommUDP + Added a reliable resend timer separate from our normal send timer to control when we resend reliable + packets. When customers mix reliable and unreliable packets, it didn't play well with the timer that + we had setup within commudp. + + CryptBn + Updated the calculation of the big number bit length to use the correct builtin based on the + representation of uint64_t on our platform. On 32-bit platforms we were trying to calculate using + the unsigned long version when uint64_t is a unsigned long long on that platform. + + CryptEcc + Updated the double and add operations for elliptic curve key generation to use a sliding window. + This algorithm is adapted from the one used for RSA Exponentiation which is discussed in the Handbook of + Applied Cryptography Chapter 14.6. + + ProtoSSL + Updated the server handshake to make sure that we send the server key exchange for ECDHE ciphers + when client certs are enabled. + + Fixed to add PKCS#1 1.5 padding validation to signature verification, added OBJ_sha384 and OBJ_sha512 to + ASN.1 parser table, now required for proper PKCS#1 signature validation. + + Fixed to add overflow checking on ASN.1 length parsing. + + Removed obsolete and unsafe OTG3 and GOS2011 CAs from trusted store. + + DirtySDK (PS4) + ProtoHttpPS4 + Fixed an issue when a POST must be changed to a GET. + + Refactored the creation and destruction of 1st party http contexts objects so that they only exist during requests. + + Refactored the maximum number of times the net context could be shared from a hardcoded constant to a variable + that can be set through ProtoHttpControl('mncs') or DirtyContextManagerControl('mncs'). + + Fixed an issue where a socket is potential left open after a sceEpoll is destroyed without being unset. + + DirtySDK (XboxOne) + NetConnXboxOne + Removed the use of the cached User^ and instead rely upon EAUser to provide the information we + previously relied upon the cached ref for. + +EADP Game Services DirtySDK 15.1.3.0.1 - April 11, 2017 + + This SDK release was built using the following compiler packages: + + EA Config 5.01.00 + Framework 7.01.00 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 10.0.14393.2156-proxy + capilano_config 1.05.07 + Kettle kettlesdk 4.508.001-pr-proxy + kettle_config 2.01.00 + iPhone ios_config 1.07.00 + iphonesdk 8.0-proxy-3 + OSX UnixGCC 0.10.00 + osx_config 1.11.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + +Changes/fixes in this release: + DirtySDK (All) + CommUDP + Changed the #define for ESC_CAUSE_LOSS to not use the define statement as part of the PS4 4.5 SDK changes. + + DirtySDK (PS4) + DirtySessionManagerPS4 + Changed the constant SCE_NP_PLAY_TOGETHER_MAX_ONLINE_ID_LIST_NUM to SCE_NP_PLAY_TOGETHER_MAX_INVITEE_LIST_NUM as part of the PS4 4.5 SDK changes. + +EADP Game Services DirtySDK 15.1.3.0.0 - March 31, 2017 + + This SDK release was built using the following compiler packages: + + EA Config 5.01.00 + Framework 7.01.00 + + Android androidsdk 24.4.1-1-2 + androidndk r13b + android_config 4.00.00 + Capilano CapilanoSDK 10.0.14393.2156-proxy + capilano_config 1.05.07 + Kettle kettlesdk 4.008.131-proxy + kettle_config 1.14.00 + iPhone ios_config 1.07.00 + iphonesdk 8.0-proxy-3 + OSX UnixGCC 0.10.00 + osx_config 1.11.00 + Linux/Unix UnixClang 0.12.02-proxy + Win32 DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 14.0.23107-1-proxy + WindowsSDK 10.0.10586-proxy + + *** Important - DirtyCast Failover Mode has been removed *** + Support of DirtyCast Failover mode has been removed in this release. + This feature has now been replaced by Connection Concierge. + + *** Important - Public API changes UserListApi PS4 *** + The profile mask is now not supported when querying for the block list. + Please specify a type mask of 0 instead. + + *** Important - Public API changes for ConnApi *** + Removed ConnApiStart(). + Removed ConnApiStop(). + Removed ConnApiRematch(). + Removed ConnApiSetPresence(). + + *** Important - PS4 User Identifier Changed *** + As part of Sony's SDK/API 4.0 Sony has deprecated APIs that use OnlineIds, in + favor of replacement versions using the PS4 account ids. All DirtySDK calls to + PSN have been switched to use PS4 account ids instead of OnlineIds. + + *** Important - Public API changes for DirtyNet *** + Renamed SocketInfo('size') to SocketInfo('psiz') + Renamed SocketPacketQueueStatus('size') to SocketPacketQueueStatus('psiz') + Renamed SocketPacketQueueStatus('full') to SocketPacketQueueStatus('pful') + + *** Important - WinRT/WinPRT *** + Removed WinRT and WinPRT support completely. + + *** Important - Modules removed from DirtySDK *** + The following modules have been migrated to the new LegacyDirtySDK package + LobbyLan + Sort + Tagfield + NetResource + WebOffer + + +New in this release: + DirtySDK (ALL) + Build + Added exporting of the buildtype in our module's publicdata, which allowed dependent packages to easily find + our exported libs/dlls. + + Enhanced build scripts to define headerfiles so the headers show correctly in Visual Studio. + + CryptBn + Added a dedicated big number module based on the CryptRSA's implementation, this is used a basis for all + the crypto based math for RSA/ECC operations. + + CryptEcc + Added this module to implement math for short Weierstrass curves using CryptBn. This is the basis for the + support of the P-256/P-384 curves we use within ProtoSSL. + + ConnApi + Added more detail connection metrics which could be retrieved in the ConnApiInfoT struct as a ConnApiConnTimer with the following fields: + * uCreateSATime - time it takes to resolve secure association (xbox one) + * uConnectTime - time it takes for intial connection attempt + * uDemangleTime - time it takes to attempt demangling on the connection + * uDemangleConnectTime - time it takes to attempt a connection after demangling + + Hpack + Added a module to implement HTTP/2 Header compression support based on + https://tools.ietf.org/html/rfc7541. This module is used primarily by the new ProtoHttp2 module. + + JsonParse + Added new JsonParse2() API, which handles allocation of required memory internally. + + Added new getters supporting an error flag (JsonGetString2, JsonGetInteger2, JsonGetDat2, JsonGetBoolean2, + JsonGetEnum2), which can be leveraged to simplify parsing code that wants to validate whether fields were + present or not. JsonGetInteger2 also supports min/max valudation. + + Plat-str + Added new ds_memclr API that encapsulates memsetting memory to zero. In an attempt to prevent incorrect + uses of memsetting, we have introduced an API that encapsulates the more common case for memset. For cases + where memset to zero is not desired a ds_memset function was created so we can easily track conforming code. + + ProtoHttp + Added support for http PATCH requests. + + ProtoHttp2 + Added a module to implement HTTP/2 based on https://tools.ietf.org/html/rfc7540. + + ProtoHttpUtil + Added support for parsing the HTTP/2 style header which name begin with a leading ':' character. + + ProtoSSL + Added support for setting TCP_NODELAY socket option on the ProtoSSL socket. + + Added support for the elliptic curve client hello extension. This extension allows us to negotiate which + elliptic curves we support. + + Added support for ECDHE Key Exchange and the following cipher suites: + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA256 + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA + Due to the performance of our configured elliptic curves, these cipher suites are currently disabled by + default. + + ProtoTunnel + Added new status selector 'vers' that can be used to query the prototunnel version negotiated on a specific + tunnel. + + VoipCommon + Enhanced debug logging for voip muting using 'umic' and 'uspk' control selectors. And fixed incorrect + prefix in some other already existing debug traces. + + VoipGroup + Enhanced logging for the low level connectivity id when resuming a connection. + + DirtySDK (iOS) + Build + Added an objective-c fileset / source text for adding multiple files to build as objective-c filetype. + + Added the -Wno-deprecated-declarations flag to disable warnings cause by CHttp functions used in dirtynet. + + DirtySDK (PS4) + DirtyContextManagerPS4 + Added a system to share sceNetPools to make it possible to create more concurrent ProtoHttpRefTs. + + DirtyErrPS4 + Added errors from the Sony SSL/HTTP libraries. + + Added missing errors from the Sony WebApi library. + + DirtyWebApiPS4 + Added server error message reporting when dealing with errors. We previously only reported the SDK side + errors but not the errors reported by the server. The server errors are not defined in a manner that we + can get the data from DirtyErr so we need to rely upon the data returned by the response information + struct. + + NetConnPS4 + Enhanced NetConnShutdown() with implicit IEAUser entries cleanup in case the game team is not calling + NetConnRemoveLocalUser() before calling NetConnShutdown(), or in case recent remove requests are still + pending in the internal request list when calling NetConnShutdown(). + + ProtoHttpPS4 + Added support for http POSTs with no payload. + + Added additional printf messages for debugging http headers. + + Added error logging so we know what the exact Sony errors were when any error occurs. + + VoipPS4 + Added VoipControl('+pri') to be used to by game code to signal that the specified local user shall be + included when internally calculating most restrictive voip privileges + + Added VoipControl('-pri') to be used to by game code to signal that the specified local user shall not be + included when internally calculating most restrictive voip privileges. + + DirtySDK (XboxOne) + NetConnXboxOne + Enhanced NetConnShutdown() with implicit IEAUser entries cleanup in case the game team is not calling + NetConnRemoveLocalUser() before calling NetConnShutdown(), or in case recent remove requests are still + pending in the internal request list when calling NetConnShutdown(). + +Changes/fixes in this release: + DirtySDK (ALL) + Build + Switched over to using Visual Studio 2015 by default. + + Refactored contribs to unflatten the include structure. To prevent collisions with other libraries we + are adding extra directories to our include structure. + common/include -> common/include/libsample + voipaux/include -> voipaux/include/voipaux + + General + Refactored locations that use memcpy/memcpy_s to use ds_memcpy/ds_memcpy_s. + + ConnApi + Removed ConnApiStart(), ConnApiStop(), ConnApiRematch(), ConnApiSetPresence(). Those functions are + leftovers from the xbox360 times... they have been no-op for a long period. They no longer need to exist. + + Fixed the setting of the GameServer's VoIP connection id by setting to VOIP_CONNID_NONE to prevent + incorrect teardowns of any active VoIP Connections. When in a Game Group and a DirtyCast hosted game, + if we leave the game the VoIP connection would have been incorrectly torn down. This causes our VoIP port + to be devirtualized, which caused our subsequent VoIP connections a higher chance of getting into a bad + state. So instead we needed to indicate that the DirtyCast GameServer does not own any VoIP connections and + that we will only devirtualize the VoIP port when tearing down the connection. + + Changed the virtualization of ports to be at the ConnApi level instead of the connection level. When doing + virtualization of ports at a connection level we can quickly run out of available ports. Since we know that + the virtualized ports do not change we can easily move this. With this change I updated our documentation + of how the client level ports are assigned to be more clear of what is happening. + + Removed gameserver mode concept used for fallback. Fallback mode has been replaced with connection concierge. + + DirtyLib + Changed to move platform-specific time code from plat-time to dirtylib. + + Changed NetPrintfCode() to initilize string buffer separately from instantiation. This is an optimization + to prevent the entire array from having to be memset at instantiation on every invocation. + + DirtyMem + Removed these two external MEMIDs: PLAYERSYNCSERVICE_MEMID ('plss') & TELEMETRYAPI_MEMID ('telm') + Any external module using the dirtymem api is now assumed to be internally defining its own mem id. + + Removed unused internal MEMIDS: PROTONAME_MEMID ('pnam') & PROTOARIES_MEMID ('pari') & + FRIENDAPI_MEMID ('frnd') & DIRTYLSP_MEMID ('dlsp') & + COMMSER_MEMID ('cser') + + DirtyNet + Fixed error conversion logic in SockRecvfrom(). Socket type (stream vs dgram vs raw) is now taken + into account to determine what conversion to use. The incorrrect logic proved to be problematic with + TCP sockets using async recv thread and being explicitly polled for recv by the client code instead + of just relying on recv callback invocation by the DS layer. + + Changed (renamed) SocketInfo('size') to SocketInfo('psiz') + + Changed (renamed) SocketPacketQueueStatus('size') to SocketPacketQueueStatus('psiz') + + Changed (renamed) SocketPacketQueueStatus('full') to SocketPacketQueueStatus('pful') + + Fixed SocketPacketQueueStatus 'pdrp' and pmax' when using the SocketPacketQueueAlloc code path. + + Changed SocketAddrMapTranslate to accept addresses of the same type without reporting an error. + + DirtySock + Removed the LobbyLan, Sort, Tagfield, NetResource and WebOffer modules. + These can now be found in the LegacyDirtySDK package. + + Fixed potential threading issues with modules with updates calling through NetConnIdle. + + JsonParse + Fixed JsonFind() to prevent reading outside the parse buffer. + + Fixed JsonParse() to return zero if the parse buffer is too small. + + NetConn + Changed NetConn to track NetConnStartups and Shutdowns with a reference count. Copied the handling on some + NetConnStartup params over to NetConnConnect options. + + NetGameUtil + Fixed NetGameUtilConnect() and NetGameUtilAdvert() to correctly handle failing calls to + _NetGameUtilAdvtConstruct(). + + Fixed local error codes possibly conflictig with COMM_* error codes in NetGameUtilConnect(). + + Changed implementation to eliminate code duplication: advt ref validation code + is now centralized in _NetGameUtilAdvtConstruct(). + + Plat-str + Changed to replace use of strncpy() with inline implementation in ds_strnzcpy(), for improved performance + when copying a string into a large buffer. The standard version will fill any unwritten parts of the + destination buffer with zeros. + + ProtoHttpServ + Changed strLocation (redirection location) to ProtoHttpServeResponseT. + + ProtoMangle + Updated logging to know what ports are bound and what currently module ref that logs belong to + + QosApi + Removed unused tagfield include. + + Changed the default packet queue length to 12 (up from 10), in testing this covers the most extreme case + of _QosApiRecvCB NetCritTry never succeeding. + + Added logging describing packet queue health, a warning will be printed if the queue is close to being + full. + + Fixed a print which indicated both FW probes were sent to the same address, when they in fact have not. + + VoipCommon + Fixed a potential race condition between the main thread and the voip thread when altering the voip channel + config. The bApplyChannelConfig variable was being set to TRUE before propagating the change down to the + voip layer with VoipControl('chan'). Under some timing conditions, that could result in the voip thread + applying the voip channel config with the old values instead of the new values because the data structures + involved were not yet updated. + + VoipGroup + Fixed some coding standard violations, arrays are required to be prefixed with "a" + + VoipTunnel + Changed to using qsort for sorting our internal lookup table. + + Changed suspended data not found logging to be at a higher verbosity as we assume this something that doesn't happen often and can be quite spammy + + DirtySDK (PS4) + DirtyContextManagerPS4 + Fixed a bug which prevented the manager from properly cleaning itself up on shutdown. + + Changed to Sony API using PSN account ids. + + DirtyLibPS4 + Changed mutex creation to attempt to fallback to an unnamed mutex when create returns + SCE_KERNEL_ERROR_EAGAIN. + + DirtyUserPS4 + Removed unused tagfield include. + + Changed to Sony API using PSN account ids. + + DirtyWebApiPS4 + Fixed DIRTYSESSMGR_MEMID being incorrectly used instead of DIRTYWEBAPI_MEMID. + + Changed mutex creation to attempt to fallback to an unnamed mutex when create returns + SCE_KERNEL_ERROR_EAGAIN. + + Removed the SDK 2.5 checks as we are long past the days of titles allowed to ship on those old SDKs. + + Removed deferred delete list. + + Changed to Sony API using PSN account ids. + + NetConnPS4 + Fixed multiple cosmetic issues to improve code readability and alignment with coding convention. + + Fixed an inconsistent error code in NetConnShutdown. + + Changed to Sony API using PSN account ids. + + PrivilegeApiPS4 + Changed to Sony API using PSN account ids. + + ProtoHttpPS4 + Fixed the dumping of the POST data payload, we needed to use the size instead of the result of send. + + Changed the HTTP connection template creation to disable gzip decompression. ProtoHttp does not offer this + type of functionality which causes some isues when trying to read gzipped compressed bodies from the + server. + + Fixed an issue where large headers were not processed properly. + + Fixed a possible truncation of a url when formatting a request. + + Fixed an issue with long urls getting truncated. + + Refactored to deactivate the Sony library's automated handling of redirections and cookies in order to + provide the exact same behavior as other platforms. + + Fixed the handling of binary certs. + + Fixed the http headers to be associated with the sceRequest rather than the sceTemplate to make sure the + headers are cleared at the appropriate times. This prevents the http library from running out of memory + after a few uses of a protohttp reference. + + UserApiPS4 + Changed to Sony API using PSN account ids. + + UserListApiPS4 + Changed to Sony API using PSN account ids. + + VoipPS4 + Changed to Sony API using PSN account ids. + + VoipHeadsetPS4 + Fixed dirtysdk incorrectly requiring a voip user to be a member of a blaze mesh for his privacy settings + to contribute to the calculation of the most restrictive voip setting on the console in scenarios where + multiple users are locally signed-in. DirtySDK now only requires the user to be authenticated with blaz + for his privacy settings to contribute to the calculation of the most restrictive voip setting. The + rational being that a blaze authenticated user is implicitly a user interacting with the game UI and + therefore a "particiapting user" as defined by Sony. + + Changed initialization of Sony voice lib to leverage the new sceVoiceSetThreadsParams() api + (introduced is SDK 3.50) to specify the priority and the affinity of the internal voice and audio port + threads. This replaces the usage of ephemeral high-priority threads for calling sceVoiceStart() and + sceVoiceCreatePort() such that internal threads inherit of the right attributes. + + Changed _VoipHeadsetUpdateChatPrivileges() to now take the new +pri / -pri config into account when + calculating pHeadset->bSharedPrivilege. + + DirtySDK (Unix) + DirtyNetUnix + Fixed to revert address selection to prefer IPv4 addresses on non-Apple platforms. Apple platforms + continue to prefer IPv6 addresses, a fix that was required for iOS10 compatibility. + + NetConnUnix + Fixed an inconsistent error code in NetConnShutdown. + + DirtySDK (XboxOne) + DirtyAddrXboxOne + Removed TagField usage as TagField will be moving to the LegacyDirtySDK. + + Fixed an off-by-1 error for decoding binary7 Xbox One addresses. + + DirtyUserXboxOne + Removed TagField usage as TagField will be moving to the LegacyDirtySDK. + + ProtoHttpXboxone + Refactored implementation to use a request killlist where terminated requests are queued until they + complete for good. That is replacing the former explicit abort operations. + + Changed the implementation with the introduction of a state machine to track asynchronous evolution + of a specific xmlhttp object. + + +EADP Game Services DirtySDK 2015 Winter 2.3.0 - October 19, 2016 + + This SDK release was built using the following compiler packages: + + EA Config 4.03.01 + Framework 6.04.01 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 10.0.14393.1058-proxy + capilano_config 1.05.03 + Kettle kettlesdk 3.508.041-proxy + kettle_config 1.06.00 + iPhone ios_config 1.03.00 + iphonesdk 8.0-proxy-2 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.02-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-4-proxy + WindowsPhoneSDK 8.0.9900-proxy + + *** Important: The CommTAPI and CommSer modules have been removed *** + + *** Important: The CryptSSC2 module has been removed, for string encrypt/decrypt please use CryptArc4 *** + + *** Important: The SOCKET_ASYNCRECVTHREAD = 0 support has been removed *** + +New in this release: + DirtySDK (All) + ConnApi + Added a new control selector 'dtif' that can be used to override the default timeout value for XB1 + security association creation or the demangling timeout on other platforms (in a cc-assisted or failover + context). + + DirtyNet + Added support for TCP keep-alive settings using the 'keep' control. In some environments we need to be able + to tweak the keep-alive settings to ensure that firewalls and other external parties do not close our + connections without notifying us. + + Added SocketHostnameCacheDel, which deletes an entry from the hostname cache (internal use only). + + Added debug print when a lookup is refcounted. + + NetConn + Added ability to allow users to override the cpu affinity of our internal threads with a netconn startup + param "-affinity". + + Platform + Added ds_strtoull function to convert strings to uint64_t. + + Added support for using EABase to drive the platform.h defines. + + ProtoSSL + Added support for the ALPN extension (Application Level Protocol Negotiation) on both the client & server + side. This extension helps negotiate a protocol between the client & server; see the spec at + https://tools.ietf.org/html/rfc7301. + + Added ProtoSSLControl() 'resu' selector, to enable/disable SSL session resume (enabled by default, + preserving previous behavior). + + Added support for setting the keep alive settings for the TCP socket. To support this I've added + an 'skep' option and followed the same pattern as the other socket options that we set for this module. + + QosApi + Added ability to configure much of qosapi with QosApiControl, via the BlazeSDK util component this provides + us with many options to tweak QOS behaviors post ship. New selectors are: + 'bpco' - if used overrides config from server, this will be the number of bandwidth probes used for tests + 'bpso' - if used overrides config from server, this will be the size of bandwidth probes used for tests + 'isyt' - sets the maximum number of ms to wait to initially synchronize all latency requests + 'lpco' - if used overrides config from server, this will be the number of latency probes used for tests + 'pque' - passed to SocketControl 'pque' if not 0, otherwise the numpackets will be passed to SocketControl + 'pque' + 'rbuf' - passed to SocketControl 'rbuf' + 'repc' - sets the number of extra probes sent (in addition to those which received no response) on a re-issue + 'sado' - set the override address to send the http requests to when issuing a service request, overrides + parameter passed to QosApiServiceRequest + 'salp' - if TRUE use old logic of sending all latency probes at once (default FALSE) + 'sbuf' - passed to SocketControl 'sbuf' + 'sprt' - set the override port to send the http requests to when issuing a service request, overrides + parameter passed to QosApiServiceRequest + 'tgbp' - sets the minimum number of ms between sending latency probes to the same site. + 'ussl' - if TRUE use https else http for setup communication (defaults TRUE) + + DirtySDK (PS4) + DirtySessionManagerPS4 + Added new status selectors for PlayTogther support. New selectors are: + 'pthe' - poll this selector to check if the client has received the PlayTogether event + 'ptil' - if a PlayTogether event has been received use this selector to retrieve the invite list + + NetConnPS4 + Added a new startup parameter "-checktick" on PS4 to control how we initialize DirtyEventManager. The new + startup parameter will control if we fail our initialization of DirtyEventManager in the case that we have + potentially missed events because the EASystemEventMessageDispatcher has been ticked. By default we will + not check if the dispatcher has been ticked so teams don't need to worry about aligning the startup of the + two systems. + + ProtoHttpPS4 + Added protohttpps4 for native support of HTTP and SSL. + + DirtySDK (XboxOne) + NetConnXboxOne + Added extra mapping from sandbox to environment to prevent having the title download storage figure out the + environment: + * EARW.3 -> CERT + * XDP1.0 -> PROD (EA Access Testing) + +Changes/fixes in this release: + DirtySDK (ALL) + Build + Changed to set the optionset to the C++ build type when building those types of files. With newer packages + we see that our C++ files were trying to be built as C and causing us to generate some compiler errors due + to that. + + ConnApi + Changed to move the virtualization of ports to be handled at the connapi layer instead of having the + network adapter deal with it. + + DirtyCert + Changed to move DirtyCert updating to the internal NetLib idle thread. To make integration easier for our + customers, this makes it possible to use ProtoHttp without the need to poll NetConnIdle. The major + requirement that ProtoHttp depended on was DirtyCert, which needs to be updated in case we needed to pull + down any CA certs. + + DirtyNet + Fixed to no longer refcount HostentT refs that are in the list but are in a done state. + + JsonParse + Fixed crash issue parsing json with leading whitespace. + + ProtoHttp + Fixed a bug in both ProtoHttpGetNextHeader() and ProtoHttpFindHeaderValue(), when dealing with an empty + header. + + ProtoHttpServ + Fixed an issue which could lead an http client to hang indefinately or until timeout when the client + requested a keep-alive. + + Fixed a cert cache issue where it was possible to reconnect to a server with a cached cert using IP address + instead of name. + + ProtoTunnel + Fixed to initialize the sockaddr used in _ProtoTunnelRecvCallback to prevent address translation spam. + + QosApi + Changed to improve latency tests accuracy in poor network conditions; latency is now done in parallel + across all ping sites, preventing random lag spikes from effecting one result. + + Changed to improve logging by making it more configurable with QosApiControl('spam') - set the verbosity of + the module, default 1 (0 errors, 1 debug info, 2 extended debug info, 3 per probe info). + + Changed public APIs QosApiCreate and QosApiServiceRequest to simplify API and better reflect data types + being passed. + + Removed unused defines QOSAPI_STATUS_XB_*, QOSAPI_LISTENFL_*, and QOSAPI_RESPONSE_MAXSIZE. + + Removed and replaced QOSAPI_REQUESTFL_* with QosApiRequestTypeE. + + VoipConnection + Fixed coverity warning about potential out of bound access of pConnection->VoipMicrPacket[] array when + user index is VOIP_SHARED_USER_INDEX (0xFF on PC). + + Fixed a bug in use of memset, which resulted in target buffer not being cleared. + + VoipTunnel + Removed deprecated flags and fields that were set for deprecation since v9 + * VOIPTUNNEL_CLIENTFLAG_BROADCASTING_VOICE + * VOIPTUNNEL_GAMEFLAG_MAX_VOICES_REACHED + * VoipTunnelGameT.iNumClientsBroadcasting + + XmlFormat + Fixed access to unitialized variable coverity warning. + + DirtySDK (PS4) + DirtySessionManagerPS4 + Fixed to try to catch configuration errors with dirtysessionmanager as we want to send failures back to + bring it to the attention of integrators. + + Fixed an issue where the return value was not properly being set because of a bad enclosure of assignment. + + DirtySDK (Unix) + Build + Removed voiptunnel.c from unix specific sources as this is already part of main source list. + + DirtyNetUnix + Fixed _SocketLookupThread to prefer IPv6 addresses, when available. This fixes compatibility with iOS10. + + Fixed SocketConnect to scrub a hostname cache entry when connect() returns an EHOSTUNREACH error. This + type of error happens when switching from an IPv4 to an IPv6 connection or vice versa. + + DirtySDK (XboxOne) + Build + Changed to use sdkreferences task when depending on any of the extension assemblies on XboxOne. + + ProtoMangleXboxOne + Fixed a potential issue where an IPv6 user mapping might get updated instead of added. + + NetConnXboxOne + Fixed to handle the AsyncStatus::Started case when calling DownloadBlobAsync. FIFA was seeing instances + where we would crash when getting a AsyncStatus::Started due to the fact that we don't handle and just kill + the async operation. After discussing this with Microsoft their suggestion was just to ignore this status + as you should expect to get another callback when the operation actually finishes. + + Fixed an issue were a failed environment check would not properly trigger a retry, leaving netconn stuck in + ~env state indefinitely. + + +EADP Game Services DirtySDK 2015 Winter 2.2.3 - September 19, 2016 + + This SDK release was built using the following compiler packages: + + EA Config 3.04.00 + Framework 5.05.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 10.0.10586.1203-proxy + capilano_config 1.03.03 + Kettle kettlesdk 3.508.041-proxy + kettle_config 1.06.00 + iPhone ios_config 1.03.00 + iphonesdk 8.0-proxy-2 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.01-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-1-proxy + WindowsPhoneSDK 8.0.9900-proxy + +New in this release + DirtySDK (All) + DirtyNet + Added support for TCP keep-alive settings using the 'keep' control + + +EADP Game Services DirtySDK 2015 Winter 2.2.2 - September 06, 2016 + + This SDK release was built using the following compiler packages: + + EA Config 3.04.00 + Framework 5.05.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 10.0.10586.1203-proxy + capilano_config 1.03.03 + Kettle kettlesdk 3.508.041-proxy + kettle_config 1.06.00 + iPhone ios_config 1.03.00 + iphonesdk 8.0-proxy-2 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.01-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-1-proxy + WindowsPhoneSDK 8.0.9900-proxy + +New in this release: + DirtySDK (All) + ProtoSSL + Added ProtoSSLStat() 'rsao' selector to signal that there is currently an RSA operation ongoing. + +Changes/fixes in this release: + DirtySDK (All) + ProtoTunnel + Fixed a knock-on in cl 1174902 when faced with discarding an out-of-order packet that could not be + recovered. This would cause a stream reset that broke the connection. This fix reverts part of that + change to make the out-of-order packet discard safe and not break the connection. In doing so it undoes + the original fix, meaning some other resolution for that issue will be required moving forward. + + DirtySDK (PC) + VoipPC + Fixed intermittently broken voip on PC caused by bad initialization of the VoIP user identifier. + + DirtySDK (Unix) + NetConnUnix + Fixed to check for NULL terminator when parsing /proc/cpuinfo in the case we have a truncated string + (does not include \n). This caused issues on newer hardware where the flags field grew larger than 512, + which caused a crash from the loop never exiting. + + +EADP Game Services DirtySDK 2015 Winter 2.2.1 - July 13, 2016 + + This SDK release was built using the following compiler packages: + + EA Config 3.04.00 + Framework 5.05.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 10.0.10586.1203-proxy + capilano_config 1.03.03 + Kettle kettlesdk 3.508.041-proxy + kettle_config 1.06.00 + iPhone ios_config 1.03.00 + iphonesdk 8.0-proxy-2 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.01-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-1-proxy + WindowsPhoneSDK 8.0.9900-proxy + +Changes/fixes in this release: + DirtySDK (PS4) + DirtySessionManagerPS4 + Changed DirtySessionManager to better catch configuration errors and service it back to integrators. + + VoipHeadsetPS4 + Fixed _VoipHeadsetUpdateChatPrivileges() to ignore non-participating user. This fix is necessary to comply + with TRC R4061G in PS4-TestCase_for_TRC1.5_e stating: A non-voip-restricted user in a game should not see + his voip blocked because of a voip-restricted user signed-in on the console but not participating in the + game. + + DirtySDK (XboxOne) + NetConnXboxOne + Fixed potential crash in the lambda expression performed when the DownloadBlobAsync() operation completes + with completion status AsyncStatus::Canceled. The code incorrectly uses a reference that was invalidated + when the operation cancellation was initiated. + + ProtoHttpXboxOne + Fixed a potential external cleanup issue by adding a timeout to stop waiting for the E_ABORT that might + never come. Instead after 5 second time we will fake the E_ABORT Event ourselves. + + DirtySDK (Unix) + DirtyNetUnix + Removed the use of AI_V4MAPPED flag when ai_family is AF_UNSPEC. + + +EADP Game Services DirtySDK 2015 Winter 2.2.0 - June 09, 2016 + + This SDK release was built using the following compiler packages: + + EA Config 3.04.00 + Framework 5.05.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 10.0.10586.1203-proxy + capilano_config 1.03.03 + Kettle kettlesdk 3.508.041-proxy + kettle_config 1.06.00 + iPhone ios_config 1.03.00 + iphonesdk 8.0-proxy-2 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.01-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-1-proxy + WindowsPhoneSDK 8.0.9900-proxy +New in this release: + DirtySDK (All) + Build + Enabled shadow variable warnings on targets that doesn't set this by default. + + DirtyLib + Added NetPrintRateLimit, implementing automated rate limiting of redundant lines of NetPrintf output. + Identical sequences of lines are suppressed for up to 100ms, or when a new line of text is printed. + The number of suppressions is appended to the line of text. Lines of text over 256 characters are + not rate-limited. + + DirtyNet + Added SocketPacketQueueStatus('full') to query if socket inbound packet queue is full or not. + + Added SocketPacketQueueStatus('size') to query current size (in packets) of socket inbound packet queue. + + Added IPv6 support definitions and includes. + + Added SocketAddressMap functionality, moved here from dirtynetxboxone, for general IPv6 support. IPv6 + addresses are mapped to pseudo-IPv4 addresses for public consumption. + + NetConn + Added standard error code for NetConnStartup. + + ProtoHttpServ + Added function to log via the logging callback at varying verbosity levels. + + ProtoSSL + Added debug output of certificate signature type when validating a certificate. + + QosApi + Added code to leverage the new support for SocketControl('pque') on physical sockets (non-virtual). QosApi + is now using a socket with a 10-deep recv paquet queue to eliminate the possibility of inducing artificial + latency resulting from inbound latency probes remaining in the system socket receive buffer because the + 1-deep queue was full. (Happens when the critical section cannot be entered in the recv callback registered + by qosapi - _QosApiRecvCB()) + + DirtySDK (Android/iOS/PC/Unix/XboxOne) + DirtyNetWin + Added IPv6 support, switched to using IPv6 sockets internally, leveraging dual-protocol stack functionality + to continue to support IPv4. Added IPv6 name resolution support to SocketLookup(). IPv4 names continue + to be preferred when available. + + DirtySDK(WinRT) + DirtyNet + Added new define INADDR_LOOPBACK for WinRT configurations + + DirtySDK(XboxOne) + NetConnXboxOne + Added a new XBLWait state so make sure we wait for the connectivity level to become xbox live. + +Changes/fixes in this release: + DirtySDK (All) + Build + Moved the contents of dirtysock-initialize.xml to Initialize.xml file and tell the package to + initializeself. This makes it so the package will include Initialize.xml automatically without us needing + to do it explicitly. + + Changed implementation to now export the DIRTYCODE_LOGGING defines based on the + dirtysdk_debug_enable / dirtysdk_debug_disable build properties. + + Removed hhc.exe from the SDK and depend on the userdoc package to provide this when building documentation. + + Removed unused macro / defines + + Removed unused release build scripts + + ConnApi + Fixed the GameServer's bAllocated flag to be correctly set TRUE during client initialization. + + CryptGCM + Fixed crash in _CryptGcmDecrypt() when passed a negative length. This condition is rare and has been seen + when an SSL server incorrectly sends an unencrypted alert message when aborting a connection. + + DirtyLib + Changed to move/consolidate NetPrintfCode and NetPrintfHook to dirtylib from platform-specific files. + + Moved DIRTY_TOKEN_PASTE and DIRTY_CONCATENATE_HELPER here from dirtynet.h as a better fit. + + DirtyNet + Fixed implementation to skip empty hostname cache entries before checking for expiration, to eliminate + confusing debug output. + + Refactored recv path for physical sockets (non-virtual): + * Replaced hard-coded 1-deep queue with configurable n-deep queue. + * Performed appropriate changes for all flavors (win, x1, ps4, unix) to be as close as possible + (eliminated unjustified platform specificities) + * Simplified recv path logic in terms of what is done in the context of SocketrecvFrom() vs in the + context of socketrecvthread + + Standardized verbosity tracking and usage (spam levels). + + JsonParse + Added handling for JSON strings inside of JsonSeekObjectEnd. + + Plat-str + Cleanup and better support of IPv6 address printing. + + ProtoAdvt + Fixed a couple of places that didn't initialize a sockaddr struct before use. + + ProtoHttpServ + Changed all the logging to use the logging callback so we get in the applications that run this module. + + ProtoSSL + Fixed a bug when requesting an SSLv3 connection where the requested cipher list would be empty, causing + the connection to fail. This is only relevant to test setups as DirtySDK clients do not request SSLv3 + by default. + + ProtoTunnel + Removed tunnel mode concept from prototunnel. + + DirtySDK (PC) + DirtyNetWin + Removed DirtyNetXboxOne. Consolidated win and xboxone implementations in a single file called DirtyNetWin + to achieve reduction of code duplication. + + VoipConnection + Fixed the voip data to not be processed if there is no listening device attached to prevent TTY spam on PC. + + DirtySDK (PS4) + Build + Removed unsupported defines as part of the PS4 3.500 SDK upgrade. The ORBIS_SDK_VERSION was replaced by + SCE_ORBIS_SDK_VERSION. + + Removed very old version checks that are no longer needed. + + Removed the privilege checks for asynchronous multiplayer as this is not supported on the newer SDK. + + Changed implementation to now set the config-vs-version on PS4 to support the use case of Frostbite titles. + Make sure to only set the VS2015 compiler flags for vc compilers. + + Fixed PS4 compiler warnings introduced in the PS4 3.500 upgrade, reported by a customer (Unreachable code + and checking NULL on arrays). + + DirtyNetPS4 + Added handling of sceNpWebApiDeleteRequest failures. + + DirtySessionManagerPS4 + Fixed potential mem leaks caused by the changeable data never being released when it exists. + + Fixed implementation of DirtySessionManagerControl('gchg') to invoke + _DirtySessionManagerSetChangeableSessionData() instead of _DirtySessionManagerSetSessionData(). + + DirtyWebApiPS4 + Changed where iThreadLife is set to DirtyWebApiCreate (main thread). + + NetConn + Fixed implementation to check the result code of sceAppContentAppParamGetInt() when querying for trial + status. + + UserApiPS4 + Fixed a double counting problem when a first party query fails. + + DirtySDK (XboxOne) + Build + Fixed compiler warnings introduced by upgrading to the March XDK. + * Fixed shadow variable usage in 'poll' selector (dirtynetxboxone) + * Fixed an incorrect implementation of SocketRecvfrom the pBuf parameter is non-const (dirtynetxboxone) + * Remove unused variables on platforms they are not used on (connapi, user, privilege) + * Use a conditional instead of assigning an integer to a boolean in C++ (voipxboxone) + * Remove printing of serialization size as that was the only use of the variable and not very helpful to + logging. + * Suppress warning in T2Host relating to changing of sizes between compilers and alignment attributes + * Assign the enumeration to the correct value instead of treating it as an integer (privilege) + * Switch from while(1) to for (;;) to prevent a constant expression warning (SampleCore) + * Remove unused variable (connapi, SampleCore) + + DirtyLibWin + Changed to statically initialize nettick frequency to a safe value, so any calls to NetTick() on Xbox One + before NetConnStartup() will not crash. + + DirtyNetXboxOne + Switched to using DirtyNet functions for Address Map functionality that were previously internal. + + Removed DirtyNetXboxOne. Consolidated win and xboxone implementations in a single file called DirtyNetWin + to achieve reduction of code duplication. + + VoipHeadsetXboxOne + Refactored bad rep check query to use normal lambda expressions. + + +EADP Game Services DirtySDK 2015 Winter 2.1.0 - March 31, 2016 + + This SDK release was built using the following compiler packages: + + EA Config 3.04.00 + Framework 5.05.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 10.0.10586.1100.0-proxy + capilano_config 1.03.03 + Kettle kettlesdk 3.008.211-proxy + kettle_config 1.05.00 + iPhone ios_config 1.03.00 + iphonesdk 8.0-proxy-2 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.01-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-1-proxy + WindowsPhoneSDK 8.0.9900-proxy + + *** Important - Xbox One XSAPI 2.0 Support *** + This version of DirtySDK supports XSAPI 2.0 which is not backwards compatible with XSAPI 1.0. + Integrators will need to build against XSAPI 2.0. + + *** Important - ConnApi client uId of 0 is now considered invalid *** + ConnApi client with a uId of 0 will now be consider invalid and will be ignore by ConnApi. + + *** Important - First DirtySDK version compatible with the Connection Concierge feature *** + This version of DirtySDK includes a large amount of changes that were introduced specifically for interoperability + with the Connection Concierge tech (https://docs.developer.ea.com/display/TEAMS/Connection+Concierge+Project). + Regardless of your game leveraging Connection Concierge or not, those changes should be transparent to you. + +New in this release: + DirtySDK (All) + CommUdp + Added backwards-compatible update to protocol to allow versioning, defined previous version as 1.0 and new + version as 1.1. + + Added larger subpacket support (increased from a maximum of 250 bytes to 1530 bytes) for connections + negotiated to commudp protocol version 1.1 or higher. Subpackets requiring 250 bytes or fewer continue to + use one byte of encoding, while larger subpackets require two bytes. This allows packet redundancy, when + combined with raising the default redundancy limit, for larger packets. + + ConnApi + Added the ability to specify a user index when activating the P2P connection forced failure debug + option ('!res'). Setting the index to -1 will affect all users like the old behavior. + + Added support for hosted connections (obtained from Connection Concierge). New code paths were added + to use connection IDs received from Connection Concierge when setting up game connections, voip + connections and tunnels. Related public api changes: + * Added multiple new fields to ConnApiClientInfoT. + * Added CONNAPI_CCMODE_* defines. + * Added a new 'ccmd' control selector to specify the Connection Concierge mode associated with + the connapi instance. + + DirtyNet + Added prints to track virtual port addition and deletion. + + Added packet drop and packet high water to packet queue. + + Added SocketInfo() 'pdrp' and 'pmax' selectors. + + JsonParse + Added support for JSON payloads greater than 64k. + + Added support for JsonParse taking a length of -1, in which case strlen is used to calculate the size of + the input JSON buffer. + + Added support for JsonParse to take a null parse buffer, in which case the size in bytes required for the + parse buffer are calculated and returned. + + ProtoHttpServ + Added support for a 'spam' control to control the amount of logging with do within the module. + + Added a request timeout to cover situations where the remote side stops sending or receiving, defaulting + to 30 seconds. + + Added a ProtoHttpServControl('idle') selector to set the idle/keep-alive timeout. + + Added PROTOHTTPSERV_FLAG_LOOPBACK. + + Added ProtoHttpServCreate2() which has an additional uFlags parameter. + + ProtoTunnel + Added a new 'actv' status selector to get the number of active tunnels on based on a specific prototunnel + version. + + Added a new 'vset' status selector to get a comma delimited list of versions we support to report to the + CCS. + + Added PROTOTUNNEL_VERSION, PROTOTUNNEL_VERSION_MIN, PROTOTUNNEL_VERSION_MAX to the public header file. + + Voip + Added support for one way muting. + + VoipCommon + Added a new 'vcid' control selector to assign a local 32-bit identifier to a specific voip connection. + + VoipConnection + Added support for hosted connections (obtained from Connection Concierge). New code paths were added + to use connection IDs received from Connection Concierge when setting up voip connections. + Related public api changes: + * New uRemoteClientId field added to VoipConnectionT. + * Renamed uClientId field to uLocalClientId in VoipConnectionT. + + VoipGroup + Added support for hosted connections (obtained from Connection Concierge). New code paths were added + to use connection IDs received from Connection Concierge when setting up voip connections. + Related public api changes: + * Added VOIPGROUP_CCMODE_* defines. + * Added a new 'ccmd' control selector to specify the Connection Concierge mode associated with + the voipgroup. + * Added VoipGroupConnect2() which takes an additional bIsConnectivityHosted input parameter and an + additional uLowLevelConnectivityId input parameter. + * Added VoipGroupResume2() which takes an additional uLowLevelConnectivityId input parameter. For + Connection Conncierge support specifically. + + VoipTunnel + Added support for allowing voiptunnel clients to exist in multiple games. This functionality to used to + mimic how Blaze handles clients in Games and Game Groups. Related public api changes: + * Added VOIPTUNNEL_MAXSUSPENDED. + * Added VoipTunnelSuspendInfoT. + * Added iNumSuspended field to VoipTunnelClientT. + + Added support for a unique game identifier that can be specified when creating the game in the + voiptunnel. New API functions were added to allow users to work with this identifier. Related public + api changes: + * Added VoipTunnelGameListAdd2(). + * Added iGameIdx input param to VoipTunnelClientListDel(). + * Added VoipTunnelGameListMatchIndex(). + * Added VoipTunnelGameListMatchId(). + + DirtySDK (PS4) + DirtyNet + Added missing definition for INADDR_LOOPBACK for PS4, needed in protohttpserv. + + VoipHeadsetPS4 + Added the ability to configure voice lib thread attributes. You can override the settings with your + Blazeserver util.cfg. + + DirtySDK (PS4/XboxOne) + NetConn + Added a new 'eusr' selector that returns the IEAUser object at a given index. + + DirtySDK (XboxOne) + DirtyNetXboxOne + Added functionality to remap IPv6 addresses to existing IPv4 virtual addresses. It was demonstrated that + this update is possible in a cable pull scenario. + + ProtoMangleXboxOne + Added processing for updates of secure associations which the are already in a completed state to update + the IPv6 address mappings. + + Voip + Added bad reputation auto muting support in order to better satisfy XR-086. This feature will be on by default. + +Changes/fixes in this release: + DirtySDK (All) + Build + Fixed doc build errors. + + Fixed warnings generated by VS2015 Update 1 + + Changed the build scripts to use the config-vs-version property to better handle changing between versions + of Visual Studio. + + ConnApi + Fixed how we increment the client count. + + Fixed the 'cadr' selector to return the prototunnel address used. + + Removed CONNAPI_STATUS_CLSE as it is no longer used. + + Fixed an issue that caused the previous mute settings not to be properly retained when resuming a voip + connection. + + Fixed an issue where a game group would use the wrong voip port when joining a game session, then a + game group, then leaving the game session. + + Fixed ConnApiControl 'adve' selector, when disabling advertising, to also destroy the NetGameUtil ref + if it has already been created. + + DirtyCert + Fixed to not issue a redundant preload request if a preload request is already in the queue. + + Fixed a bug where a successful preload triggered by a validation failure would result in a failed + transaction, because the subsequent on-demand request would be considered to fail due to adding a + redundant CA. + + DirtyNet + Fixed to include missing return in 'pque' selector that caused misleading debug warning. + + Changed packet queue max size from 127 to 1024. + + JsonFormat + Fixed _JsonUpdateHeader() to prevent buffer offset from exceeding the buffer size during an overflow + condition. This fixes a possible memory stomp on a subsequent call to a JsonFormat method. + + JsonParse + Fixed a bug when executing a JsonFind against an element in an unclosed array; the find would skip past the + end of the parse buffer data and access uninitialized memory. + + Fixed a bug where executing a JsonFind against an unclosed element, containing a name but no value, would + result in returning a pointer to the beginning of the Json buffer. + + Fixed some other cases where the parse buffer could be accessed after the terminator. + + Fixed one issue each in the decode and escape lookup tables. + + Fixed to ensure after calling JsonParse that the parse buffer is always zero terminated. If this is not + possible, zero is returned. + + Changed name of JsonGetListItemEnd to JsonGetObjectEnd to better match JSON naming conventions. A #define + wrapper supporting the old name is included to maintain compatibility with previous code. + + NetConn + Fixed documentation error in header for NetConnIdle. + + Plat-time + Fixed a bug in weekday calculation in ds_secstotime. + + ProtoHttpManager + Fixed to use consistent modulename as prefix to debug output. + + ProtoHttpServ + Removed code in _ProtoHttpServUpdateRecvBody() that set the received body flag true when receiving a + chunked transfer without any initial data. + + Changed the ProtoHttpServControl('time') selector to set the request timeout. + + Fixed a typo in the default idle timeout value, which was supposed to be five minutes but was in fact much + longer. + + Changed size of ProtoHttpServRequestT.strContentType[] from 32 to 64 to match what we use for + ProtoHttpServResponseT + + Changed the code in the _ProtoHttpServParseHeader to skip all the URI / TYPE information that is in the header + This fixes an issue with skipping the first valid header value when parsing + + ProtoTunnel + Fixed two issues when attempting to switch keys. The first issue was on the side receiving a packet + encrypted by the new key, in ProtoTunnelValidatePacket(), which was not using the new crypto state to try + decrypting the packet. The second issue was on the sending side, where a new HMAC was not being calculated + when the new key was chosen. These issues caused failures in scenarios with players in a game and a + gamegroup, when players left either the game or the gamegroup, and could not maintain their p2p + connections. + + Fixed an issue where a large stream offset could cause a rematch to fail due to out-of-order discard. The + send stream offset is now reset to zero when a new key is selected, matching the behavior of the rematch + code on the receive side, which already assumes a stream offset of zero. + + Fixed possible issues with crypto setup by making sure send/recv states are both initialized all of the + time when switching keys. + + Fixed a bug which broke a reconnection scenario where one side of a connection was already active, while + the other was not. The bug was due to an incorrect change of hard-coded return values in + _ProtoTunnelDecryptAndValidatePacket2(), introduced along with the addition of ProtoTunnel 1.1 protocol + support. + + Removed usage of hard-coded values. Replaced with explicit defines for packet decrypt flow. + + Changed _ProtoTunnelDecryptAndValidatePacket() to return a VALIDATE error if an attempt to recover an out + of order packet fails. + + Changed to implementation to simplify error returns from _ProtoTunnelRecvData() and error handling of + values returned by that function in _ProtoTunnelRecv(). + + Fixed a bug in _ProtoTunnelFindTunnel(), where a failed match of an inactive tunnel would print an error + message, but continue on with the tunnel index as if the match had succeeded. This would cause some + parameters (port, addr) to be updated incorrectly before the packet was subsequently rejected, which was + mostly harmless but could cause some confusing temporary side effects particularly in the debug printing. + + Fixed various documentation and debug printing items. + + ProtoWebSocket + Fixed some documentation errors regarding several TODOs in the code. + + VoipCommon + Fixed a potential memory stomp when setting voip channel information for a user index which is out of range. + + Fixed the channel selection functions to properly include attempts to modify the VOIP_SHARED_USER_INDEX. + + VoipGroup + Fixed a bug during the transition from a gamegroup to a game so that the proper mute status from the game + group would transfer over into the game. + + VoipTunnel + Remove support for the 'dcst' control which was used to set a DirtyCast hack. We now support this + functionality by default as there exists no configuration we were disable the hack. + + Fixed a possible memory leak detected by Coverity, in a situation when trying to create a VoipTunnel object + and running out of memory. + + DirtySDK (iOS) + Plat-time + Fixed broken NetPrintf time stamps. + + DirtySDK (PC) + DirtyNetWin + Fixed the usage of the socket select() function to be compatible with Berkley sockets. + + DirtySDK (XboxOne) + Build + Removed support for the XboxOne ADK + + DirtyNetXboxOne + Fixed the usage of the socket select() function to be compatible with Berkley sockets. + + NetConnXboxOne + Fixed an issue where token acquisition did not reset properly after a cable pull and a reconnect, leading + to a report that the token acquisition failed when it had not been attempted. + +EADP Game Services DirtySDK 2015 Winter 2.0.0 - October 22, 2015 + + This SDK release was built using the following compiler packages: + + EA Config 2.25.03 + Framework 3.32.02 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 6.2.13332.0-proxy + capilano_config 1.02.00 + Kettle kettlesdk 3.008.041-proxy + kettle_config 1.03.01 + iPhone ios_config 1.01.05 + iphonesdk 7.0-proxy-5 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.01-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-1-proxy + WindowsPhoneSDK 8.0.9900-proxy + + *** Important - DIRTYSDK_IEAUSER_ENABLED compile switch no longer supported *** + Deprecated DIRTYSDK_IEAUSER_ENABLED compile switch such that IEAUser integration can no longer be turned off at + compile time when using DirtySDK NetConn for PS4 or XboxOne. + + *** Important - Gen 3 platform specific code has been removed *** + Gen3 support was deprecated in 15.1.1.1.0. The remaining Gen 3 specific code have now been removed. + +New in this release: + DirtySDK (All) + Build + Added support for structured xml libs-external when Framework is version 3.33.00 or higher. + + Added support for Visual Studio 2015. To enable support the vsversion property needs to be + set to "2015" when building as the new warning suppressions are not backwards compatible. + + Added new build property dirtysdk-contrib that allows enabling the contribs to build Without + samples. If dirtysdk-contrib does not need to be specified when dirtysdk-samples is true. + + CryptSTP1 + Removed CryptSTP1 support. + + Displist + Removed Displist support. + + Hasher + Removed Hasher support. + + NetGameLink + Added verbosity setting to reduce QoS data spam in the logs unless desired. + + Added the reception time in ticks of the packet to NetGamePacketHeadT metadata. + + ProtoHttpServ + Added a new callback for processing the incoming header. This callback allows us to opt out of further + processing if we know that request data is malformed based on header information. + + Added support for handling the 'Expect: 100-continue' header if sent by clients. ProtoHttpServ will always + just send back the 100 status code and will not reject requests based on the expect header. If a payload is + too large it can be rejected using the new header processing callback. + + ProtoAries + Removed ProtoAries support. + + ProtoHttpUtil + Added support for decoding url-encoded strings. + + Added support for parsing query parameters similar to what we use for parsing header information. + + DirtySDK (Android) + Plat-time + Added Android implementation to ds_plattimetotime(). + + DirtySDK (PS4) + DirtySessionManagerPS4 + Added support for ps4 session changeable data. + + Added new server-driven mode (all operations that are executed from a server are now disabled, i.e. + everything except what is necessary for invitation). DirtySessionManagerBinaryHeaderT and + DirtySessionManagerChangeableBinaryHeaderT are now public (PS4 only). This is to enable a compile time + check to ensure the Blaze Server's structs are equivalent to those. C++ static assert are used in the + Blaze client code (game manager) and they rely on those structures being public. + + Added new status selectors: + 'gcda' - to get the active session's changeable binary data size + 'gpcd' - to get the pending session's changeable binary data size + 'gagm' - to get the active changeable data game mode + 'gpgm' - to get the pending changeable data game mode + 'gagt' - to get the active binary data game type + 'gpgt' - to get the pending binary data game type + 'lsts' - to get the last status code returned from the calls to the Sony APIs + + Added new control selectors: + 'gchg' - to queue getting the changeable binary data into the pending session + 'serv' - to turn on server-driven session - in that mode, this client tech no longer honors any + client request for operations that are now owned by the server + 'sess' - to set session identifier (server-driven mode only) + 'spgm' - to set the pending session game mode + 'spgt' - to set the pending session game type + + DirtySDK (PS4/XboxOne) + NetConn + Added a new status selector 'eusr' which will return the IEAUser given a dirtysock user index. + + VoipHeadset + Added new status selector 'sact' to determine if the shared device is active (Kinect/PS Camera). + +Changes/fixes in this release: + DirtySDK (All) + Build + Removed the AddDirtySDKLibsToModule task, external libraries are now added to the + package.DirtySDK.dirtysock.libs.external fileset instead. + + Fixed 'NULL is defined to be 0' warning in platform.h by using #if defined instead of #ifdef. + + Removed the DIRTYCODE_API from typedefs, definitions do not need to be exported for dynamic libraries. + + Fixed an issue with how our C++ filesets are organized which caused files to be built both static and + dynamic instead of based on the build configuration. + + NetGameDist + Fixed NetGameDistInputQueryMulti() and NetGameDistInputCheck() to always poll NetGameDistUpdate() for + new packet data if no remote inputs are available. Previously, NetGameDistUpdate() would not be polled + if there were no local input packets queued for sending, but this behavior had a negative impact in + multi mode, requiring the caller poll NetGameDistUpdate() themselves if they had no inputs to send. To + fix that issue and simplify the code, the (minor) optimization of not calling NetGameDistUpdate() if no + local input packets are queued, when in non-multi mode, was removed. + + Fixed NetGameDistInputQueryMulti() to be safer in the event the caller calling the function following a + queue overrun. In the event of a queue overrun it is possible to lose multi inputs that have a delta + value that is not one (the default) assigned. A delta greater than one that is lost will point the + paired input queue pointer into unallocated buffer space, resulting in an invalid input with a length + of zero. This would result in a memcpy of -1 bytes which would result in a memory stomp and subsequent + crash. This scenario is now detected and handled safely; although the caller should still detect the + overflow error and shut the game down, this condition will no longer cause a crash. + + Fixed NetGameDistInputCheck() to return "ready to receive" in multi mode if remote inputs are + available, regardless of whether there are local inputs queued for sending or not. This issue would + cause titles calling NetGameDistInputCheck() to incorrectly not receive data that was available if they + were not sending a steady stream of inputs themselves. + + ProtoHttpServ + Changed to update the data that is based through the various callbacks for handling the request and + responses. Instead of using the same type, specific request and response types have been created. + + Changed the read/write callbacks to be receive/send to be clearer for integrators. + + Changed the request callback to allow to be called continuously if more processing time is needed based on + the result. + + Changed the logging callback to no longer return a result. + + ProtoMangle + Fixed a Valgrind warning by initializing a variable and preventing the possibility of copying garbage data + to a buffer. + + ProtoTunnel + Removed 1.0 protocol support and remaining Xbox 360 code. + + Removed ability to conditionally compile out HMAC. + + Fixed issue with _ProtoTunnelFindTunnel() not detecting a port change during handshake. + + Voip + Removed Gen3 only parameter for VoipStartup(). + + Changed the way we update voip channel settings. We now update it right when we get a ping and mic packet + with the new channel information instead of in voip idle. This also addresses potential multi-threading + issue with applying channel configs. + + VoipCommon + Fixed shared channel configs not being recalculated after a channel reset. + + Fixed a situation where channel configs are applied before we have any registered user. + + VoipConnection + Fixed a wrong usage of ds_memcpy. + + VoipGroup + Fixed an issue with mute flags being inconsistent or reset when player join both a game group and a game + session. + + Fixed an issues with mute flag consistency across multiple voip groups when a user is a part of more than + one at the same time. + + Fixed an issue where joining a second voip group or leaving an additional voip group would mute or unmute + all players respectively. + + Changed 'umic' and 'uspk' control selectors to deprecated. + + WebOffer + Changed the signature of WebOfferGetBusy2 to take the strDefaultMessage as a const char * instead of char * + to prevent a writable strings warning on PS4. + + DirtySDK (PC) + ProtoPingPC + Fixed an issue on Windows XP where the ProtoPingIcmpResponseT structure was too small (the size required + turns out to not be documented explicitly). Adding a 32 byte buffer to the structure resolved the issue. + + DirtySDK (PS4) + DirtyNetPS4 + Changed implementation such that socket receive thread now runs at high priority. + + DirtySessionManagerPS4 + Changed the dirtysessionmanger callback not to block when we have dirtywebapi queuing enabled. + + DirtyWebApiPS4 + Fixed a race condition where the webapi thread sometimes would miss the signal to wake up. + + NetConnPS4 + Removed internal automatic detection of system users by NetConn. Usage of NetConnAddLocalUser() and + NetConnRemoveLocalUser() is now mandatory. Implicit: Also removed 'cuis' and 'csiu' status selectors + for converting system index into dirtysdk index (or opposite). + + Changed the method to determine what error state NetConn enters to more accurately reflect the reason we + could not go online in a multiple user scenario. + + VoipPS4 + Changed implementation such that voip thread now runs at high priority. + + VoipHeadsetPS4 + Fixed initialization of voice lib thread priorities. sceVoiceStart() and sceVoiceCreatePort() are now + being called from dedicated ephemeral high-priority threads to force internal Sony threads to also + execute as high priority threads. This bug fix is required to eliminate crackling voice during heavy + processing scenarios (game play). + + Changed voice lib initialization to comply with voice lib doc and to mimic what is done in Sony samples. + + Fixed _VoipHeadsetUpdateDecoderConnections() not being called when pHeadset->bSharedPrivilege changes. + + Changed implementation to flush the internal buffer of the decoder port when it is no longer connected to + any speaker port. This is required to avoid "old voice" being played back when the decoder port is being + reconnected to a speaker port. + + DirtySDK (Unix) + DirtyNetUnix + Fixed to prevent the logging of ENOTCONN errors when shutting down the socket to prevent unnecessary spam. + + DirtySDK (XboxOne) + NetConnXboxOne + Removed internal automatic detection of system users by NetConn. Usage of NetConnAddLocalUser() and + NetConnRemoveLocalUser() is now mandatory. Implicit: Also removed 'cuis' and 'csiu' status selectors + for converting system index into dirtysdk index (or opposite). + + Refactored NetConnXboxOne to reduced complexity. + + ProtoHttpXboxOne + Refactored the status function to be consistent with other platforms. Certain selectors are only handled + when the state of the module is ST_BODY or ST_DONE. + + ProtoMangleXboxOne + Fixed _ProtoMangleRemoteHostAddRef() and _ProtoMangleRemoteHostDelRef() such that multi-threaded usage of + refRemoteHostRefCountMap is now guarded by the critical section. + + Fixed implementation to now try-catch around usage of method used to compare two secure device addresses to + gracefully handle rare cases where a COMException is thrown. + + Fixed implementation to now try-catch around SecureDeviceAddress::FromBytes(). + + UserApiXboxOne + Changed to using the AsyncOp refs to track the progress of requests. This prevents an issue where the + previous bitfield might get stomped on in certain conditions and makes the code clearer to understand. + + VoipXboxOne + Changed to move VoIP state destruction to NetConnIdle shutdown callback. This move is because the VoIP + thread can become blocked in Microsoft Chat code for over ten seconds in certain loss-of-network scenarios. + This was causing long stalls when trying to VoipShutdown() upon loss of network. Also added guard on + VoipStartup() to ensure it is not called in a state where the module has not been shut down (VoipShutdown() + already had the corresponding guard on the module having been started). + + +EADP Game Services DirtySDK 2015 Winter 1.1.0 - July 09, 2015 + + This SDK release was built using the following compiler packages: + + EA Config 2.25.01 + Framework 3.32.02 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 6.2.12999.0-1-proxy + capilano_config 1.01.02 + Kettle kettlesdk 2.508.081-proxy + kettle_config 1.03.00 + iPhone ios_config 1.01.05 + iphonesdk 7.0-proxy-5 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.12.01-proxy + Win32/WinRT/WinPRT + DotNet 4.0 + DotNetSDK 2.0-4 + VisualStudio 11.0.50727-1-proxy + WindowsSDK 8.0.50727-1-proxy + WindowsPhoneSDK 8.0.9900-proxy + + *** Important - Gen3 platforms (PlayStation 3 and Xbox 360) no longer supported *** + The Winter 1.0.x stability branch should be used for any further development on these platforms. + +New in this release: + DirtySDK (All) + Build + Removed Gen3 Support. + + ConnAPI + Added support to gate the voip connection based on QoS validation. The 'estv' selector can be used to + ungate the voip connection. + + ProtoHttp + Added returning of http status code as part of the 'hres' status selector. + + ProtoSSL + Added signature_algorithms hello extension support, enabled by default. Some servers require this + extension to be present before accepting a connection request. + + Added validation of supported signature algorithms upon receiving a CertificateRequest handshake message. + + XmlParse + Added support for 64-bit integers via XmlContentGetInteger64 and XmlAttribGetInteger64. + + DirtySDK (PC) + NetConnWin + Added support querying for MAC address with valid IP using the 'ethr'/'macx' when non-zero is passed into + iData. + + DirtySDK (PS4) + DirtyNetPS4 + Added 'spam' selector support to change debug verbosity. + + NetConnPS4 + Added an error message for when PRX is not loaded for the sceNpAuthCreateAsyncRequest call. + + VoipHeadsetPS4 + Added support for the following bitrates: 14400, 16000 and 22533. The default bitrate has been increased + from 7300 to 16000. + +Changes/fixes in this release: + DirtySDK (All) + Build + Changed DirtySDK build scripts to use Framework Structured XML. + + DirtyCert + Changed requesting certificates use format state field with "ST=" instead of "S=", which is the default + OpenSSL nomenclature. + + NetConn + Changed NetConnDisconnect() to also abort UPnP operations. + + ProtoHttp + Changed default PUT/POST behavior to not reuse a previous connection (keep-alive). When making a PUT or + POST request and utilizing a keep-alive connection, it is possible for the server to close the connection + and for that connection close to be "in flight" when issuing the request. This results in the request + being failed when the connection close is received. Because a PUT or POST request can result in modifying + server state, it may not be automatically re-issued. Not using keep-alive avoids this complication. A new + ProtoHttpControl('rput') selector has been added to disable this behavior if the caller is willing to deal + with this complication in trade for getting connection reuse on a PUT/POST. + + ProtoSSL + Fixed incorrect CertificateVerify formatting when using TLS1.2. This fixes client certificate support that + was previously broken when connecting to a server supporting TLS1.2. + + Fixed an error with 'hres' selector not returning the right hresult in some cases. + + ProtoTunnel + Fixed diagnostic output in _ProtoTunnelFindTunnel(), when re-matching addr/port, to include correct tunnel + index instead of the max tunnel index. + + DirtySDK (PC/XboxOne) + DirtyLibWin + Fixed debug print on QueryPerformanceCounter() failure that was comparing against the wrong result code. + + DirtySDK (PS4) + PrivilegeApiPS4 + Fixed a logical error that prevented a dialog from being appropriately terminated when aborted and the + reference is not destroyed. + + DirtyWebApiPS4 + Fixed an issue where we were using the wrong critical section for handling errors which ended up in a + deadlock if another request was made before the response was handled. + + DirtySessionManagerPS4 + Changed 'gses' control selector to now guard against passing in "INVALID". + + VoipHeadsetPS4 + Fixed the wrong user index being set when polling for voice. + + DirtySDK (PS4/XboxOne) + NetConn + Added ability to pass -1 into NetConnRemoveLocalUser(), in which case an internal lookup will be performed. + + Added a new selector 'iusr' than can be used to retrieve the index given the IEAUser UserId. + + VoipHeadset + Fixed voipheadsetDeleteLocalUser() to also clear the last headset status. + + Fixed a situation where channel configs are applied before we have any registered user with voipheadset. + + DirtySDK (PS4/Unix) + DirtyNet + Fixed SocketConnect() to copy the remote address when performing an non-blocking connect + (SCE_NET_EINPROGRESS/SYS_NET_EINPROGRESS/EINPROGRESS) for ps4/ps3/unix. + + DirtySDK (Unix) + DirtyLibUnix + Fixed NetTickUsec() to use proper scale for seconds. + + DirtySDK (XboxOne) + NetConnXboxOne + Changed the environment to be mapped to the sandbox ID. If it can't be mapped it will default back to TMS + RETAIL -> PROD + CERT -> CERT + EARW.1 -> TEST + +EADP Game Services DirtySDK 2015 Winter 1.0.1 - June 05, 2015 + + This SDK release was built using the following compiler packages: + + EA Config 2.19.00 + Framework 3.25.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 6.2.12710.0-1-proxy + capilano_config 1.01.00 + Kettle kettlesdk 2.508.051-proxy + kettle_config 1.02.00 + iPhone ios_config 1.01.05 + iphonesdk 7.0-proxy-5 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.07.00 + PS3 ps3sdk 470.001-lite + PlayStation3NpEx 470.001 + Win32/WinRT/WinPRT + DotNetSDK 4.0 + VisualStudio 11.0.50727-proxy + WindowsSDK 8.0.50727-proxy + WindowsPhoneSDK 8.0.9900-proxy + Xenon XenonSDK 2.0.21256.3-proxy + + *** Important - ProtoTunnel format modified - backward compatibility broken *** + Th Winter 1.0 release of DirtySDK introduced a new ProtoTunnel protocol version - 1.1. By default, this + is the version used for all platforms except for Xbox 360. ProtoTunnel v1.1 is not compatible with v1.0, + and Xbox 360 is not compatible with v1.1. Please make sure to rebuild any game servers in addition to + game clients when upgrading to this release from a version older than Winter 1.0. + +New in this release: + DirtySDK (PS4) + NetConnPS4 + Added NetConnRegisterNpStateCallback() to receive callbacks directly from NP State events. + +Changes/fixes in this release: + DirtySDK (All) + NetGameLink + Fixed a rare latency estimation issue at start of connection when local and remote clocks are close to but + not quite synchronized. + + DirtySDK (PC) + VoipPC + Fixed VoipCommonUpdateRemoteStatus() not being invoked from the VoipThread. + + Fixed bPrivileged not defaulting to TRUE. + + VoipCommon + Removed unnecessary PC specificities. + + DirtySDK (PS3) + VoipHeadsetPS3 + Fixed implementation to no longer wait for the user to be in a participating state for acquiring and + playing voice. Without this change, there is some potential for garbled voice at connection establishment. + + DirtySDK (PS4) + VoipHeadsetPS4 + Changed VoIP muting based on privileges back to be the most restrictive between local users. It will + be reverted back to be muted on a per player basis once Sony provides us a way to check whether VoIP + is being played back on the TV or headsets. + + DirtySDK (XboxOne) + ConnApi + Fixed _ConnApiInitClientConnectionState() to not reset ip address if it was already resolved (required for + host migration to succeed with peer hosted topology when p2p voip is active before host migration). + + NetConnXboxOne + Fixed the value of the internet connectivity state to reset after a resume to prevent the user from getting + stuck in an offline state after a NetConnDisconnect()/NetConnConnect(). + + UserListApiXboxOne + Fixed the queueing of UserListApiIsFriendAsync() requests which return success codes but then behaved + strangely when more than one request was active. + + Fixed a possible memory leak on shutdown. + + DirtySDK (PC/PS3/Unix/XboxOne) + DirtyLib + Fixed a rare thread safety issue in SocketLookup around + refcounting + + +EADP Game Services DirtySDK 2015 Winter 1.0.0 - April 9, 2015 + + This SDK release was built using the following compiler packages: + + EA Config 2.19.00 + Framework 3.25.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 6.2.12128.0-proxy + capilano_config 1.01.00 + Kettle kettlesdk 2.000.121-proxy + kettle_config 1.02.00 + iPhone ios_config 1.01.05 + iphonesdk 7.0-proxy-5 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.07.00 + PS3 ps3sdk 450.001-lite + PlayStation3NpEx 450.001 + Win32/WinRT/WinPRT + DotNetSDK 4.0 + VisualStudio 11.0.50727-proxy + WindowsSDK 8.0.50727-proxy + WindowsPhoneSDK 8.0.9900-proxy + Xenon XenonSDK 2.0.21256.3-proxy + + *** Important - Please update your QOS servers from your Blaze server configurations *** + The changes to the qosapi requires the new version of the QOS servers in order to function properly. + Contact GOSOPS for information about the most up to date QOS servers. + + *** Important - ProtoTunnel format modified - backward compatibility broken *** + This release of DirtySDK introduces a new ProtoTunnel protocol version - 1.1. By default, this is the + version used for all platforms except for Xbox 360. ProtoTunnel v1.1 is not compatible with v1.0, and + Xbox 360 is not compatible with v1.1. Please make sure to rebuild any game servers in addition to game + clients when upgrading to this release. + +New in this release: + + DirtySDK (All) + ProtoTunnel + Added support for the new 1.1 version of the ProtoTunnel protocol, with the following features: + - Introduces backwards compatibility for future protocol versions. + - Control packet data data used in handshaking is still authenticated, but is now unencrypted. + - Adds fast matching (and re-matching, when the source addr and/or port changes) of packet data to the + correct endpoint. + - Version 1.0 can still be selected and is the default for Xbox 360 operation. Xbox 360 is not + compatible with 1.1 or later versions of the protocol. + - Version 1.0 is compatible with the previous ProtoTunnel protocol. + + CommUDP + Added a packet received flag which can be accessed through NetGameUtilStatus('pkrc'). The flag is stored in + ConnApiClientT GameInfo.uConnFlags. This part of the more specific join error task GOSREQ-184. + + DirtySDK (PS3) + NetConnPS3 + Added 'nper' selector to NetConnStatus() to allow the user to differentiate a user logging out from PSN + from PSN disconnect. + + DirtySDK (XboxOne) + NetConnXboxOne + Added a critical section to protect against NetConnStatus('xusr') being called from the voip thread. + + VoipHeadsetXboxOne + Added support for MS voip bystanders (cert requirement). + + DirtySDK (Xenon) + ConnApiXenon + Added ConnApiControl('xonl') which can be used to tell ConnApiXenon to no longer attempt game connectivity + but to only perform Xsession management. + + Added _ConnApiMoveToNextState() for improved logging of ConnApiXenon state transitions. + +Changes/fixes in this release: + + DirtySDK (All) + CryptRSA + Refactored to make modular exponentiation an iterative process. This allows it to be spread out over + multiple updates, which is useful as the operation can take significant amounts of clock time and is + blocking. As part of this change, roughly 12k of temporary state that used to be on the stack is now + moved to the RSA state. + + Switched to use 64-bit math on Linux and PS4, improving execution time on those platforms. + + DirtyCert + Switched to new gosca.ea.com endpoint using certificate issued against GOS2015 CA. + + DirtySessionManager + Renamed DirtySessionManagerComplete() to DirtySessionManagerGetSecureIp(), and made that new function + xenon-only. + + NetConn + Changed UPnP initialization: if peerport netconn param is not specified, netconn now configures + protoupnp with default port 3659 instead of default port 0. + + NetGameDist + Fixed _strNetGameDistDataType to also cover GMDIST_DATA_NODATA. + + ProtoHttp + Changes to harden protohttp against multi-threaded calls into update, send, and recv functions from + interfering with one another. + + ProtoSSL + Enabled ServerName Hello Extension by default. + + Refactored to take advantage of iteratve modular exponentation for SendClientKeyExchange and + SendCertificateVerify operations. + + Switched CA provider flag from GOS2013 CA to GOS2015 CA. This allows the new DirtyCert endpoint to + provision CA certificates. + + QosApi + Refactored qosapi to improve maintainability, this is extensive changes throughout the + implementation. + + Changed qosapi so that the different phases of the qos process (latency, bandwidth, firewall) + do not have dependencies on one another. In the past firewall could only be done after + bandwidth, and qos phases would have an impact on the error status of other qos phases. + + Changed qosapi to use the new ip reported by the server rather than the IP address discovered + by the DNS lookup. The old behavior prevented XboxOne from successfully querying the NAT type, + due to DNS round robin issues. + + Removed the client to client methods QosApiListen() and QosApiRequest() from the qosapi. + + VoipConnection + Fixed incorrect array size for LocalUsers + + DirtySDK (PS3) + DirtyNetPS3 + Fixed a race condition that causes memory corruption when calling NetConnShutdown() while a + hostname lookup is in progress. NetConnShutdown() now waits until all hostname threads are + finished. + + DirtySDK (PS4) + DirtySessionManagerPS4 + Removed DirtySessionManagerComplete(). + + DirtySDK (XboxOne) + NetConnXboxOne + Fixed a case where the connectivity level can be stuck because of a missing notification from + Microsoft. When logging out after a resume event it seems that we sometimes do not receive an + XboxLiveAccess notification when logging back in after the resume. This fix forces a check of + the network state after 5 seconds. + + DirtySDK (Xenon) + ConnApiXenon + Changed implementation to guarantee that _ConnApiUpdateDemangle() will never be performed before + XSessionCreate() or XSesssionMigrateHost() completes (on both session host and session members). + Failure to enforce such a serialization can lead to 2 potential problems: secure address being + resolved with an invalid session id (if primary session creation is not complete) or connapi + exiting ST_MIGRATE state prematurely before firing SESSEVENT (if secondary session migration is + not complete). + + Changed ConnApiUpdate() such that _ConnApiUpdateDemangle() cannot be called when a session + creation or a session migration is pending. + + Removed usage of ConnApiSessionManagerStatus('idle') at the beginning of _ConnApiUpdateDemangle(): + no longer required now that calls to _ConnApiUpdateDemangle() from ConnApiUpdate() are limited to + states where it is allowed. + + Fixed _ConnApiInitClientList() to properly initialize pConnApi->GameServer.ClientInfo.strTunnelKey + (inspired from connapi.c) This fix is minor because the tunnel key is not used on xenon anyway. + + ConnApiXenonSessionManager + Enhanced implementation of ConnApiXenonSessionManagerComplete() to make it clear that + ConnApiXenonSessionManagerGetSecureIp() CANNOT return "in-progress". + + Fixed implementation of ConnApiXenonSessionManagerComplete() to return the right secure ip address for the + console specified as input. It used to return pRef->iAddrPrimary which is not guaranteed to match the + specified console. + + Renamed ConnApiXenonSessionManagerComplete() to ConnApiXenonSessionManagerGetSecureIp() + + Removed ConnApiXenonSessionManagerControl('conn') which was dead code because the 'conn' selector is no + longer supported in DirtySessionManager. + + DirtySessionManagerXenon + Renamed DirtySessionManagerComplete() to DirtySessionManagerGetSecureIp(). + + VoipXenon + Fixed implementation of VoipSetLocalUser() to properly setup the VoipUserT to be passed to + _VoipRegisterLocalTalker(). Without this fix, wrong xuids are registered with XHV resulting + in calls to pXHVEngine->SubmitIncomingChatData() intermittently failing with ERROR_INVALID_FUNCTION. + + +EADP Game Services DirtySDK 2015 Winter 0.3.0 - March 16, 2015 + + This SDK release was built using the following compiler packages: + + EA Config 2.19.00 + Framework 3.25.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.07 + Capilano CapilanoSDK 6.2.12128.0-proxy + capilano_config 1.01.00 + Kettle kettlesdk 2.000.121-proxy + kettle_config 1.02.00 + iPhone ios_config 1.01.05 + iphonesdk 7.0-proxy-5 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.07.00 + PS3 ps3sdk 450.001-lite + PlayStation3NpEx 450.001 + Win32/WinRT/WinPRT + DotNetSDK 4.0 + VisualStudio 11.0.50727-proxy + WindowsSDK 8.0.50727-proxy + WindowsPhoneSDK 8.0.9900-proxy + Xenon XenonSDK 2.0.21256.3-proxy + +New in this release: + + DirtySDK (All) + CommUDP + Added a metric to track packet loss prevented by a redundancy mechanic. + + DirtyLib + Added new NetTickUsec() to return microsecond-precision tick counter for profiling purposes. + + ProtoSSL + Added GOS2015 SHA2 CA. + + Added VeriSign 2008 Universal SHA2 CA. + + VoipConnection + Added support for piggy-backing reliable data over voip traffic (data packets and ping packets) + * Added VoipConnectionReliableBroadcastUser() and VoipConnectionReliableBroadcastOpaque(). + * Added LinkedReliableDataT. + * Extended VoipConnectionT and VoipConnectionlistT for reliable data support. + * Changed synopsis of VoipConnectRecvVoiceCbT. + + VoipHeadset + Added support for piggy-backing reliable data over voip traffic (data packets and ping packets) + * Changed to adapt to change in synopsis of VoipConnectRecvVoiceCbT. + + VoipPacket + Added support for piggy-backing reliable data over voip traffic (data packets and ping packets) + * Added new constants, and ReliableDataInfoT, ReliableDataT, ReliableAckT. + + VoipPriv + Added support for piggy-backing reliable data over voip traffic (data packets and ping packets) + * Changed implementation of VOIP_CopyUser() to use ds_memcpy_s() instead of ds_strnzcpy(). + * Changed VoipUserExT to be conditionally compiled in for XBOXONE only. + + DirtySDK (PS4) + DirtyWebApiPS4 + Added DirtyWebApiUpdate to issue callbacks from same thread as NetConnIdle. This feature can be enabled + or disabled with 'qcal' control selector. By default the feature is disabled. + + DirtySDK (XboxOne) + VoipXboxOne + Added support for piggy-backing reliable data over voip traffic (data packets and ping packets) + * Added call to VoipConnectionReliableBroadcastUser() from VoipActivateLocalUser(). + + DirtySDK (Xenon) + ConnApiXenon + Removed the dependency of the CONNAPI_MAXCLIENT from the function ConnApiUpdateDemangle(). + +Changes/fixes in this release: + + DirtySDK (iOS) + DirtyLib + Fixed incorrect critical section length for iOS64 + + DirtySDK (PC) + Build + Fixed compilation warnings for deprecated winsock2 calls with WindowsSDK 8.1 + + DirtySDK (PS3) + NetConnPS3 + Fixed the processing of the SCE_NP_MANAGER_STATUS_OFFLINE event when we are early on in the connection + process to prevent us from becoming stuck in +con when PSN is down. + + Fixed the handling of cellNetCtlNetStartDialogUnloadAsync() which was being treated as a synchronous + function. + + DirtySDK (PS4) + VoipPS4 + Changed the chat permission to applied on a player to player basis when not using the Playstation Camera + + DirtySDK (WinRT) + DirtyLibWinRT + Fixed incorrect thread priority initialization in NetLibCreate(). + + DirtySDK (XboxOne) + NetConnXboxOne + Fixed a build error that occurs when IEAUSER is not used. + + PrivilegeApiXboxOne + Fixed a memory stomp. + + DirtySDK (Xenon) + Build + Fixed a build break in the post build step of ATGFramework, which is used for the Xenon samples. + + ConnApiXenon + Fixed secure address resolution not being done for new connections to be established after host migration. + + FriendApiXenon + Fixed a potential issue where friend data could be populated in the incorrect slot. + + +EADP Game Services DirtySDK 2015 Winter 0.2.0 - January 28, 2015 + + This SDK release was built using the following compiler packages: + + EA Config 2.19.00 + Framework 3.25.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.05-1 + Capilano CapilanoSDK 6.2.11785.0-proxy + capilano_config 1.00.14 + Kettle kettlesdk 2.000.091-proxy + kettle_config 1.01.08 + iPhone ios_config 1.01.05 + iphonesdk 7.0-proxy-5 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.07.00 + PS3 ps3sdk 450.001-lite + PlayStation3NpEx 450.001 + Win32/WinRT/WinPRT + DotNetSDK 4.0 + VisualStudio 11.0.50727-proxy + WindowsSDK 8.0.50727-proxy + WindowsPhoneSDK 8.0.9900-proxy + Xenon XenonSDK 2.0.21256.3-proxy + +New in this release: + + DirtySDK (All) + PrivilegeApi + Added PRIVILEGEAPI_PRIV_INVALID privilege for ease of use. + + DirtySDK (PC) + VoipSpeex + Added the dll export tag to extern const VoipCodecDefT VoipSpeex_CodecDef. + + DirtySDK (PS3) + NetConnPS3 + Added logging for connection status changes. + + DirtySDK (PS4) + DirtySessionMangerPS4 + Added session lock support (prevent user from joining). A PS4 session can not be locked/unlocked + using the 'lock' selector. + + NetConnPS4 + Added logging for connection status changes. + + DirtySDK (XboxOne) + NetConnXboxOne + Removed automatic reset user list support when IEAUSER is used. + + Added logging for connection status changes + + UserApiXboxOne + Added Support for ProfileChanged Event. Please use the new UserApiNotifyDataT::ProfileUpdateData type + and UserApiNotifyTypeE::USERAPI_NOTIFY_PROFILE_UPDATE to listen for the event. + + DirtySDK (Xenon) + NetConnXenon + Added logging for connection status changes + +Changes/fixes in this release: + + DirtySDK (All) + ConnApi + Fixed a bug where ConnApi will tried to initialize clients with NULL machine address. + + DirtyMem + Fixed to export DirtyMemAlloc and DirtyMemFree when building as a DLL. + + Added include of to prevent malloc() and free() calls for default memory allocators compiling + out when building as a DLL. + + DirtyPng + Fixed a wrong usage of ds_strnzcpy that leads to a memory stomp. + + NetConn + Fixed NetConnStatus() not propagating the iData parameter to SocketInfo() for selectors that are passed + through. + + ProtoHttp + Fixed a memory pointer issue when sending chunked http transaction faster than the network can keep up + that could lead to potentially sending parts of uninitialized memory over the network. + + Changed to check status of open keep-alive connection in the DONE state, to detect server-closed + connections when they are closed, rather than waiting until the ProtoHttp ref is used in a new transaction + or destroyed. + + VoipConnection + Fixed an integration error. VoipConnectionStartup() will now have a max player check. If for any reason + voip system is not started properly voip setup will be skipped by Blaze. + + DirtySDK (iOS) + DirtyLib + Fixed wrong critical section length for iOS64. + + DirtySDK (PS4) + DirtyWebApiPS4 + Changed priority and scheduling setting of internal threads in favor of less aggressive threading + patterns to avoid negatively impacting game with unnecessary thread switches during game play. + + NetConnPS4 + Fixed an error which prevents users from going online if PSN does not respond quickly to the parental + settings query. + + PrivilegeApiPS4 + Fixed an integration error, which caused _PrivilegeApiUpdateDialogStatus callback not to be removed properly. + + DirtySDK (XboxOne) + ProtoMangleXboxOne + Fixed a potential crash if a client is removed and re-added while corresponding security association + creation is in progress. + + +EADP Game Services DirtySDK 2015 Winter 0.1.0 - December 4, 2014 + + This SDK release was built using the following compiler packages: + + EA Config 2.19.00 + Framework 3.25.00 + + Android androidsdk 18-2 + androidndk r9c + android_config 1.06.05-1 + Capilano CapilanoSDK 6.2.11785.0-proxy + capilano_config 1.00.14 + Kettle kettlesdk 2.000.071-proxy + kettle_config 1.01.07 + iPhone ios_config 1.01.05 + iphonesdk 7.0-proxy-5 + OSX UnixGCC 0.05.00 + osx_config 1.00.01 + Linux/Unix UnixClang 0.07.00 + PS3 ps3sdk 450.001-lite + PlayStation3NpEx 450.001 + Win32/WinRT/WinPRT + DotNetSDK 4.0 + VisualStudio 11.0.50727-proxy + WindowsSDK 8.0.50727-proxy + WindowsPhoneSDK 8.0.9900-proxy + Xenon XenonSDK 2.0.21256.3-proxy + + *** Important - Complementary integration guidelines available on Confluence *** + DirtySDK 2015 Winter Upgrade available here: + https://docs.developer.ea.com/display/dirtysock/DirtySDK+2015+Winter+Upgrade+Guide + +New in this release: + + DirtySDK (All) + ProtoSSL + Added support for AES-128-GCM-SHA256 and AES-256-GCM-SHA384 cipher suites. + + Added two obsolete alternate definitions for sha1withRSAEncryption for compatibility with some software + that generates certificates using these object IDs, for example makecert.exe in WindowsSDK. + + ProtoTunnel + Enhanced debug logging for the 'bnds' selector. + + Voip + Enhanced API for user registration to better support MLU (Multiple Local Users) scenarios. Every local user + should now be registered with the voip sub-system using VoipSetLocalUser() in order for its headset status + info to be available for queries. To additionally get fully functional voip, local users must be further + activated with VoipActivateLocalUser(), which promotes the user to a "participating" state. Basically any + user being pulled into the game should be a participating user. Associated public api impact: + * New synopsis for VoipSetLocalUser() + * New function to promote local users to "participating" state: VoipActivateLocalUser() + + VoipGroup + Enhanced VoipGroup api. It was re-aligned with changes to the voip api called out above. + * New function to promote local users to "participating" state: VoipGroupActivateLocalUser() + + DirtySDK (PC) + DirtyErrPC + Added DirtyErrPC implementation with pc-specific errors. + + ProtoPingPC + Added a new version of protoping for PC, using ICMPSendEcho2() API. This API allows use of ICMP when an + application is run without administrator privilege, which is required for use of RAW sockets employed with + protoping.c. + + DirtySDK (PS4) + VoipHeadsetPS4 + Enhanced _VoipHeadsetIsLocalVoiceFrameReady() to detect voice readiness quicker. + + Added '-pbk' and '+pbk' selectors to better support our voip channel feature under MLU scenarios. + + DirtySDK (WinRT/WintPRT) + DirtyLibWinRT + Enhanced NetPrintfCode() to use OutputDebugStringA() instead of OutputDebugStringW(). + + DirtySDK (XboxOne) + UserApiXboxOne + Added support for the 'avsz' selector. Now different profile pictures sizes can be fetched. + + VoipXboxOne + Added selectors: + VoipStatus 'pcac' - returns true if party chat is active + VoipStatus 'pcsu' - returns true if party chat is currently suspended + VoipControl 'pcsu' - takes iValue as a bool, TRUE suspend party chat, FALSE resume it + In order for VoipControl 'pcsu' to work you must add to the appxmanifest.xml: + + + + +Changes/fixes in this release: + + DirtySDK (All) + General + Replaced APIs deprecated by Microsoft with generic equivalents. + + ConnApi + Changed synopsis of ConnApiOnline(): it no longer has a pSelfAddr parameter. + + Changed ConnApiUserInfoT.uClientFlags to ConnApiUserInfoT.uFlags. + + Changed ConnApiClientInfoT.uMachineId to ConnApiCLientInfoT.uId. + + Removed multiple virtual machine mode: connapi 'mvtm' status and control selectors no longer exist. + The implementation is now multiple-virtual-machine capable by default. + + Removed ConnApiUserInfoT, ConnApiAddUser() and ConnApiRemoveUser() for all platforms except xbox360. + + Removed ConnApiFindClient(), then renamed ConnApiFindClientById() to ConnApiFindClient(). + + Removed ConnApiClientInfoT.strUniqueId + + Removed _ConnApiDefaultCallback() and made the debug logging client-centric instead of user-centric. + + DirtyNet + Changed all dirtynet files to log unsupported status selectors the same way. + + NetGameLink + Changed implementation to avoid code duplication: moved packet loss calculation logics into a static + function. + + Changed the names of several member variables in the NetGameLinkStatT structure. The changes are listed below: + bps to outbps + rps to outrps + nps to outnps + pps to outpps + rpps to outrpps + + ProtoSSL + Changed to request TLS1.2 instead of TLS1.1. Servers are still allowed to select any version from TLS1.2 + down to SSLv3. + + Changed to use 64-bit time for certificate expiration checking, so certificates created with expiration + times far in the future won't fail validation. + + Changed to harden protossl against multi-threaded use causing obscure secure failures. + + VoipConnection + Removed platform specificities in _VoipConnectionInit(). + + VoipPacket + Changed value of VOIP_PACKET_STATUS_FLAG_METADATA from 2 to 1. + + DirtySDK (PS4) + DirtyWebApiPS4 + Removed DirtyWebApiCreate()/DirtyWebApiCreateEx() and renamed DirtyWebApiCreateEx2() to + DirtyWebApiCreate(). + + Changed WebApi threads to only wakeup when there are jobs queued. + + VoipHeadsetPS4 + Fixed establishment of connections between a decoder port and all local speaker ports when a user is added + after a decoder was created. + + DirtySDK (XboxOne) + DirtyNetXboxOne + Fixed _SocketAddrMapGet() to no longer include the port value when trying to match an input ipv6 address + with an existing mapping. This change is required for multi-hub test bench to be functional on xboxone: + because hubs all bind to different game port, the inbound traffic source port may not be same as the ipv6 + address mapped when resolving secure device address for the local console. + + VoipHeadsetXboxOne + Deprecated support for pre-March2014 XDKs (_XDK_VER < 10698). + + Replaced VOIPHEADSET_VOICELIB_DEBUG with VOIPHEADSET_DEBUG. + + DirtySDK (Xenon) + ConnnApiXenon + Changed internal usage of some control selectors: moved call to ConnApiXenonManagerControl('self') + and VoipGroupControl('lusr') from ConnApiOnline() to _ConnApiInitClientList() + + Fixed connapixenon to properly update the remote tunnel port with the peer's internally bound port + when the internal address (i.e. internal to NAT scope) is used to reach that peer. + + Fixed a logical error forcing the voip connection attempt to fail early. diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zfile.h b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zfile.h new file mode 100644 index 00000000..a1959c46 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zfile.h @@ -0,0 +1,143 @@ +/*H********************************************************************************/ +/*! + \File Zfile.h + + \Description + Host file operations. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 02/16/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _Zfile_h +#define _Zfile_h + +/*** Include files ****************************************************************/ + +#if (defined(DIRTYCODE_LINUX)) || (defined(DIRTYCODE_APPLEIOS)) +#include +#endif + +/*** Defines **********************************************************************/ + +#if (defined(DIRTYCODE_LINUX)) || (defined(DIRTYCODE_APPLEIOS)) +#define ZFILE_INVALID (NULL) +#else +#define ZFILE_INVALID (-1) //!< an invalid ZfileT handle +#endif + +#define ZFILE_OPENFLAG_RDONLY (1) +#define ZFILE_OPENFLAG_WRONLY (2) +#define ZFILE_OPENFLAG_RDWR (ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_WRONLY) +#define ZFILE_OPENFLAG_CREATE (4) +#define ZFILE_OPENFLAG_BINARY (8) +#define ZFILE_OPENFLAG_APPEND (16) + +#define ZFILE_SEEKFLAG_CUR (1) +#define ZFILE_SEEKFLAG_END (2) +#define ZFILE_SEEKFLAG_SET (3) + +#define ZFILE_ERROR_NONE (0) //!< no error +#define ZFILE_ERROR_FILEOPEN (-1) //!< generic error opening the file (reading or writing) +#define ZFILE_ERROR_FILECLOSE (-2) //!< generic error closing the file +#define ZFILE_ERROR_FILEWRITE (-3) //!< generic error occurred writing to the file +#define ZFILE_ERROR_FILEDELETE (-4) //!< generic error deleting the file +#define ZFILE_ERROR_FILESTAT (-5) //!< generic error trying to fstat a file +#define ZFILE_ERROR_FILERENAME (-6) //!< generic error renaming a file +#define ZFILE_ERROR_FILENAME (-7) //!< bad filename +#define ZFILE_ERROR_NULLPOINTER (-8) //!< null pointer passed in where data was expected +#define ZFILE_ERROR_NOSUCHFILE (-9) //!< file does not exist +#define ZFILE_ERROR_PERMISSION (-10) //!< permission denied (on opening/writing) + +#define ZFILE_PATHFILE_LENGTHMAX (512) //!< max length of a path/filename string + +/*** Macros ***********************************************************************/ + +#define ZFILESTAT_MODE_READ (0x01) //!< file has read flag set +#define ZFILESTAT_MODE_WRITE (0x02) //!< file has write flag set +#define ZFILESTAT_MODE_EXECUTE (0x04) //!< file has execute flag set +#define ZFILESTAT_MODE_PERMISSIONMASK (0x07) //!< mask for mode flags + +#define ZFILESTAT_MODE_FILE (0x10) //!< item is a file +#define ZFILESTAT_MODE_DIR (0x20) //!< item is a directory +#define ZFILESTAT_MODE_FILETYPEMASK (0x30) //!< mask for file type flags + +/*** Type Definitions *************************************************************/ + +typedef struct ZFileStatT +{ + int64_t iSize; //!< file size in bytes + uint32_t uTimeCreate; //!< file creation time, seconds since epoch + uint32_t uTimeAccess; //!< last access time, seconds since epoch + uint32_t uTimeModify; //!< last modification time, seconds since epoch + uint16_t uMode; //!< file mode (file/dir and R/W/X) + uint16_t uPad1; //!< pad out to even boundary +} ZFileStatT; + +#if !defined(DIRTYCODE_LINUX) && !defined(DIRTYCODE_APPLEIOS) +typedef intptr_t ZFileT; +#else +typedef FILE * ZFileT; +#endif + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Platform-specific implementations (Zfileplatform.c) +*/ + +// open a file +DIRTYCODE_API ZFileT ZFileOpen(const char *pFileName, uint32_t uFlags); + +// close a file +DIRTYCODE_API int32_t ZFileClose(ZFileT iFileId); + +// read from a file +DIRTYCODE_API int32_t ZFileRead(ZFileT iFileId, void *pData, int32_t iSize); + +// write to a file +DIRTYCODE_API int32_t ZFileWrite(ZFileT iFileId, void *pData, int32_t iSize); + +// seek in a file +DIRTYCODE_API int64_t ZFileSeek(ZFileT iFileId, int64_t iOffset, uint32_t uFlags); + +// delete a file +DIRTYCODE_API int32_t ZFileDelete(const char *pFileName); + +// get file status information +DIRTYCODE_API int32_t ZFileStat(const char *pFileName, ZFileStatT *pFileStat); + +// rename a file +DIRTYCODE_API int32_t ZFileRename(const char *pOldname, const char *pNewname); + +// create a directory +DIRTYCODE_API int32_t ZFileMkdir(const char *pPathName); + +/* + Platform-inspecific implementations (Zfile.c) +*/ + +// get file size +DIRTYCODE_API int64_t ZFileSize(ZFileT iFileId); + +// open a file and load it into memory and null-terminate (in case it is a text file) +DIRTYCODE_API char *ZFileLoad(const char *pFileName, int32_t *pFileSize, uint32_t bBinary); + +// save (overwrite) null-terminated data to a file +DIRTYCODE_API int32_t ZFileSave(const char *pFileName, const char *pData, int32_t iSize, uint32_t uFlags); + +#ifdef __cplusplus +}; +#endif + +#endif // _Zfile_h + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zlib.h b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zlib.h new file mode 100644 index 00000000..3e1045c2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zlib.h @@ -0,0 +1,121 @@ +/*H********************************************************************************/ +/*! + \File zlib.h + + \Description + A simple console style test library that provides a basic + notion of processes, output routines and memory allocation. + Used to implement simple test programs in a unix-style + command-line environment. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 09/15/1999 (gschaefer) Initial Design + \Version 11/08/1999 (gschaefer) Cleanup and revision + \Version 03/17/2005 (jfrank) Cleanup and revision +*/ +/********************************************************************************H*/ + +#ifndef _zlib_h +#define _zlib_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock/dirtylib.h" + +/*** Defines **********************************************************************/ + +#define ZLIB_STATUS_RUNNING ((signed)0x80000000) +#define ZLIB_STATUS_UNKNOWN ((signed)0x80000001) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct ZEnviron ZEnviron; +typedef struct ZContext ZContext; +typedef struct ZConsole ZConsole; +typedef int32_t (ZCommand)(ZContext *pArgz, int32_t iArgc, char **pArgv); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create a new process environment. Parses command line and gets process ready to run. +DIRTYCODE_API ZEnviron *ZCreate(ZConsole *pConsole, const char *pCmdline); + +// destroy process environment +DIRTYCODE_API void ZDestroy(ZEnviron *pEnv); + +// execute a process within an existing environment. +DIRTYCODE_API void ZInvoke(ZEnviron *pEnv, ZCommand *pCmd); + +// get process id of current process +DIRTYCODE_API int32_t ZGetPid(void); + +// set current environment from context +DIRTYCODE_API void ZSet(ZContext *context); + +// let process signal it wants a callback (has not completed). +DIRTYCODE_API int32_t ZCallback(ZCommand *pCmd, int32_t iDelay); + +// give time to any existing processes that need it. +DIRTYCODE_API int32_t ZTask(void); + +// remove any environments containing complete processes. +DIRTYCODE_API void ZCleanup(void); + +// kill all active processes in preparation for shutdown +DIRTYCODE_API void ZShutdown(void); + +// allow a process to allocate persistant private context. +DIRTYCODE_API ZContext *ZContextCreate(int32_t iSize); + +// return tick count in milliseconds. +DIRTYCODE_API uint32_t ZTick(void); + +// put process to sleep for some period of time +DIRTYCODE_API void ZSleep(uint32_t uMSecs); + +// display output using standard printf semantics. +DIRTYCODE_API void ZPrintf(const char *pFmt, ...); + +// display output using standard printf semantics (no hook) +DIRTYCODE_API void ZPrintf2(const char *pFmt, ...); + +#if DIRTYCODE_LOGGING + #define ZPrintfDbg(_x) ZPrintf _x +#else + #define ZPrintfDbg(_x) { } +#endif + +// set zprint callback +DIRTYCODE_API void ZPrintfHook(int32_t (*pPrintfHook)(void *pParm, const char *pText), void *pParm); + +// show list of all process environments. +DIRTYCODE_API int32_t ZCmdPS(ZContext *pArgz, int32_t iArgc, char **pArgv); + +// kill an existing process. +DIRTYCODE_API int32_t ZCmdKill(ZContext *pArgz, int32_t iArgc, char **pArgv); + +// return status of current env, command. +DIRTYCODE_API int32_t ZGetStatus(ZEnviron *pEnv); + +// return status of process with specified pid +DIRTYCODE_API int32_t ZGetStatusPid(int32_t iPid); + +// get fourcc/integer from command-line argument +DIRTYCODE_API int32_t ZGetIntArg(const char *pArg); + +#ifdef __cplusplus +}; +#endif + +#endif // _zlib_h + + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zlist.h b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zlist.h new file mode 100644 index 00000000..aafc2356 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zlist.h @@ -0,0 +1,63 @@ +/*H********************************************************************************/ +/*! + \File zlist.h + + \Description + Generic list module for samples to use. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/26/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _zlist_h +#define _zlist_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +#define ZLIST_ERROR_NONE (0) //!< no error (success) +#define ZLIST_ERROR_NULLPOINTER (-1) //!< a null pointer ref was used +#define ZLIST_ERROR_FULL (-2) //!< sending/receiving list is full (msg dropped) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct ZListT ZListT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create a list object +DIRTYCODE_API ZListT *ZListCreate(int32_t iNumEntries, int32_t iEntrySize); + +// add an entry to the back of a data list +DIRTYCODE_API int32_t ZListPushBack(ZListT *pList, void *pEntry); + +// get an entry off the front of a data list +DIRTYCODE_API int32_t ZListPopFront(ZListT *pList, void *pEntry); + +// examine the front entry of a data list +DIRTYCODE_API void *ZListPeekFront(ZListT *pList); + +// erase and entire list +DIRTYCODE_API void ZListClear(ZListT *pList); + +// destroy a list object +DIRTYCODE_API void ZListDestroy(ZListT *pList); + +#ifdef __cplusplus +}; +#endif + +#endif // _zlist_h + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zmem.h b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zmem.h new file mode 100644 index 00000000..c1275be5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zmem.h @@ -0,0 +1,48 @@ +/*H********************************************************************************/ +/*! + \File zmem.h + + \Description + ZAlloc (malloc) and ZFree (free) implementations for use on all platforms. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/16/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _zmem_h +#define _zmem_h + +/*** Include files ****************************************************************/ + +// include this so we can use zmemtrack alongside the zmem libraries +#include "zmemtrack.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// allocate some memory, uSize bytes in length +DIRTYCODE_API void *ZMemAlloc(uint32_t uSize); + +// free a previously allocated memory chunk +DIRTYCODE_API uint32_t ZMemFree(void *pMem); + +#ifdef __cplusplus +}; +#endif + +#endif // _zmem_h + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zmemtrack.h b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zmemtrack.h new file mode 100644 index 00000000..82e6e75b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/include/libsample/zmemtrack.h @@ -0,0 +1,62 @@ +/*H********************************************************************************/ +/*! + \File zmemtrack.h + + \Description + Routines for tracking memory allocations. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 02/15/2005 (jbrookes) First Version, based on jfrank's implementation. +*/ +/********************************************************************************H*/ + +#ifndef _zmemtrack_h +#define _zmemtrack_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +#define ZMEMTRACK_PRINTFLAG_TRACKING (1) //!< print more verbose output + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! logging function +typedef void (ZMemtrackLogCbT)(const char *pText, void *pUserData); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init Zmemtrack module +DIRTYCODE_API void ZMemtrackStartup(void); + +// shut down Zmemtrack module +DIRTYCODE_API void ZMemtrackShutdown(void); + +// set the logging callback +DIRTYCODE_API void ZMemtrackCallback(ZMemtrackLogCbT *pLoggingCb, void *pUserData); + +// track an allocation +DIRTYCODE_API void ZMemtrackAlloc(void *pMem, uint32_t uSize, uint32_t uTag); + +// track a free operation +DIRTYCODE_API void ZMemtrackFree(void *pMem, uint32_t *pSize); + +// print current tracking info +DIRTYCODE_API void ZMemtrackPrint(uint32_t uFlags, uint32_t uTag, const char *pModuleName); + +#ifdef __cplusplus +}; +#endif + +#endif // _zmemtrack_h + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/source/pc/zfilepc.c b/r5dev/thirdparty/dirtysdk/contrib/common/source/pc/zfilepc.c new file mode 100644 index 00000000..7cb2fb6a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/source/pc/zfilepc.c @@ -0,0 +1,404 @@ +/*H********************************************************************************/ +/*! + \File zfilepc.c + + \Description + Basic file operations (open, read, write, close, size). + + \Notes + None. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 11/18/1004 (jbrookes) First Version + \Version 1.1 03/16/2005 (jfrank) Updates for common sample libraries +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include // _mkdir + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" + +#include "zfile.h" +#include "zlib.h" +#include "zmem.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function ZFileOpen + + \Description + Open a file. + + \Input *pFileName - file name to open + \Input uFlags - flags dictating how to open the file + + \Output + int32_t - file descriptor, or -1 if there was an error + + \Version 1.0 03/16/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +ZFileT ZFileOpen(const char *pFileName, uint32_t uFlags) +{ + char strMode[16] = ""; + FILE *pFile; + + if(pFileName == NULL) + return(ZFILE_INVALID); + if(strlen(pFileName) == 0) + return(ZFILE_INVALID); + + ds_memclr(strMode, sizeof(strMode)); + + // map zfile flags to Win32 Mode flags + if (uFlags & ZFILE_OPENFLAG_APPEND) + { + strcat(strMode, "a"); + if (uFlags & ZFILE_OPENFLAG_RDONLY) + strcat(strMode, "+"); + } + else if (uFlags & ZFILE_OPENFLAG_RDONLY) + { + strcat(strMode, "r"); + if (uFlags & ZFILE_OPENFLAG_WRONLY) + strcat(strMode, "+"); + } + else if (uFlags & ZFILE_OPENFLAG_WRONLY) + strcat(strMode, "w"); + if (uFlags & ZFILE_OPENFLAG_BINARY) + strcat(strMode, "b"); + + if ((pFile = fopen(pFileName, strMode)) != NULL) + { + return((ZFileT)pFile); + } + else + { + ZPrintfDbg(("zfilepc: error %d opening file '%s'\n", errno, pFileName)); + return(ZFILE_INVALID); + } +} + +/*F********************************************************************************/ +/*! + \Function ZFileClose + + \Description + Close a file + + \Input iFileId - file descriptor + + \Output int32_t - return value from fclose() + + \Version 1.0 11/18/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileClose(ZFileT iFileId) +{ + if (iFileId != ZFILE_INVALID) + { + return(fclose((FILE *)iFileId)); + } + else + { + return(ZFILE_ERROR_FILECLOSE); + } +} + +/*F********************************************************************************/ +/*! + \Function ZFileRead + + \Description + Read from a file. + + \Input *pData - pointer to buffer to read to + \Input iSize - amount of data to read + \Input iFileId - file descriptor + + \Output + Number of bytes read + + \Version 1.0 11/18/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileRead(ZFileT iFileId, void *pData, int32_t iSize) +{ + int32_t iResult; + + iResult = (int32_t)fread(pData, 1, iSize, (FILE *)iFileId); + + return(ferror((FILE *)iFileId) ? 0 : iResult); +} + +/*F********************************************************************************/ +/*! + \Function ZFileWrite + + \Description + Write to a file. + + \Input *pData - pointer to buffer to write from + \Input iSize - amount of data to write + \Input iFileId - file descriptor + + \Output + Number of bytes written + + \Version 1.0 11/18/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileWrite(ZFileT iFileId, void *pData, int32_t iSize) +{ + if ((iFileId == ZFILE_INVALID) || (pData == NULL) || (iSize == 0)) + { + return(ZFILE_ERROR_FILEWRITE); + } + return((int32_t)fwrite(pData, 1, iSize, (FILE *)iFileId)); +} + +/*F********************************************************************************/ +/*! + \Function ZFileSeek + + \Description + Seek to location in file. + + \Input iFileId - file id to seek + \Input iOffset - offset to seek to + \Input uFlags - seek mode (ZFILE_SEEKFLAG_*) + + \Output + int64_t - resultant seek location, or -1 on error + + \Version 03/16/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int64_t ZFileSeek(ZFileT iFileId, int64_t iOffset, uint32_t uFlags) +{ + int64_t iResult; + int32_t iFlags=0; + + if (uFlags == ZFILE_SEEKFLAG_CUR) + iFlags = SEEK_CUR; + else if (uFlags == ZFILE_SEEKFLAG_END) + iFlags = SEEK_END; + else if (uFlags == ZFILE_SEEKFLAG_SET) + iFlags = SEEK_SET; + + iResult = _fseeki64((FILE *)iFileId, iOffset, iFlags); + iResult = _ftelli64((FILE *)iFileId); + return((iResult >= 0) ? iResult : -1); +} + + +/*F********************************************************************************/ +/*! + \Function ZFileDelete + + \Description + Delete a file. + + \Input *pFileName - filename of file to delete + + \Output int32_t - 0=success, error code otherwise + + \Version 03/23/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileDelete(const char *pFileName) +{ + int32_t iResult; + + if(pFileName == NULL) + return(ZFILE_ERROR_FILENAME); + + iResult = remove(pFileName); + if(iResult == 0) + return(ZFILE_ERROR_NONE); + else + return(ZFILE_ERROR_FILEDELETE); +} + + +/*F********************************************************************************/ +/*! + \Function ZFileStat + + \Description + Get File Stat information on a file/dir. + + \Input *pFileName - filename/dir to stat + \Input *pStat + + \Output int32_t - 0=success, error code otherwise + + \Version 03/25/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileStat(const char *pFileName, ZFileStatT *pFileStat) +{ + struct _stat FileStat; + int32_t iResult; + + // check for error conditions + if(pFileName == NULL) + return(ZFILE_ERROR_FILENAME); + if(pFileStat == NULL) + return(ZFILE_ERROR_NULLPOINTER); + + // get file status + iResult = _stat(pFileName, &FileStat); + + // check for some specific errors + if((iResult == -1) && (errno == ENOENT)) + return(ZFILE_ERROR_NOSUCHFILE); + else if(errno == EACCES) + return(ZFILE_ERROR_PERMISSION); + else if(iResult != 0) + return(ZFILE_ERROR_FILESTAT); + + // clear the incoming buffer + ds_memclr(pFileStat, sizeof(ZFileStatT)); + // copy from the PC-specific structures + pFileStat->iSize = FileStat.st_size; + pFileStat->uTimeAccess = FileStat.st_atime; + pFileStat->uTimeCreate = FileStat.st_ctime; + pFileStat->uTimeModify = FileStat.st_mtime; + // get the file modes + if(FileStat.st_mode & _S_IFDIR) + pFileStat->uMode |= ZFILESTAT_MODE_DIR; + if(FileStat.st_mode & _S_IFREG) + pFileStat->uMode |= ZFILESTAT_MODE_FILE; + if(FileStat.st_mode & _S_IREAD) + pFileStat->uMode |= ZFILESTAT_MODE_READ; + if(FileStat.st_mode & _S_IWRITE) + pFileStat->uMode |= ZFILESTAT_MODE_WRITE; + if(FileStat.st_mode & _S_IEXEC) + pFileStat->uMode |= ZFILESTAT_MODE_EXECUTE; + + // done - return no error + return(ZFILE_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function ZFileRename + + \Description + Rename a file. + + \Input *pOldname - old name + \Input *pNewname - new name + + \Output int32_t - 0=success, error code otherwise + + \Version 03/30/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileRename(const char *pOldname, const char *pNewname) +{ + int32_t iResult; + + // check for error conditions + if((pOldname == NULL) || (pNewname == NULL)) + return(ZFILE_ERROR_NULLPOINTER); + + // rename the file + iResult = rename(pOldname, pNewname); + + if(iResult == 0) + return(ZFILE_ERROR_NONE); + else + return(ZFILE_ERROR_FILERENAME); +} + +/*F********************************************************************************/ +/*! + \Function ZFileMkdir + + \Description + Make a directory, recursively + + \Input *pPathName - directory path to create + + \Output + int32_t - 0=success, error code otherwise + + \Version 01/25/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ZFileMkdir(const char *pPathName) +{ + char strPath[1024], *pPath, cTerm = '\0'; + + // copy pathname + ds_strnzcpy(strPath, pPathName, sizeof(strPath)); + + // translate forward slashes to backward slashes + for (pPath = strPath; *pPath != '\0'; pPath += 1) + { + if (*pPath == '/') + { + *pPath = '\\'; + } + } + + // traverse pathname, making each directory component as we go + for (pPath = strPath; ; pPath += 1) + { + if (*pPath == '\\') + { + cTerm = *pPath; + *pPath = '\0'; + } + if (*pPath == '\0') + { + int32_t iResult; + if ((iResult = _mkdir(strPath)) != 0) + { + if (errno == ENOENT) + { + ZPrintfDbg(("zfilepc: could not create directory '%s'\n", strPath)); + return(-1); + } + if (errno == EEXIST) + { + ZPrintfDbg(("zfilepc: directory %s already exists\n", strPath)); + } + } + } + if (cTerm != '\0') + { + *pPath = cTerm; + cTerm = '\0'; + } + if (*pPath == '\0') + { + break; + } + } + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/source/unix/zfileunix.c b/r5dev/thirdparty/dirtysdk/contrib/common/source/unix/zfileunix.c new file mode 100644 index 00000000..5296a146 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/source/unix/zfileunix.c @@ -0,0 +1,472 @@ +/*H********************************************************************************/ +/*! + \File zfilepc.c + + \Description + Basic file operations (open, read, write, close, size). + + \Notes + None. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 11/18/1004 (jbrookes) First Version + \Version 1.1 03/16/2005 (jfrank) Updates for common sample libraries +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtylib.h" + +#include "zfile.h" +#include "zlib.h" +#include "zmem.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _ZFilePathTranslate + + \Description + Translate a path from dos-style to unix-style + + \Input *pPathName - path name to translate + \Input *pStrBuf - [out] output for translated path + \Input iBufLen - size of output buffer + + \Output + char * - pointer to translated path + + \Version 10/10/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ZFilePathTranslate(const char *pPathName, char *pStrBuf, int32_t iBufLen) +{ + char *pPath; + + // copy pathname + ds_strnzcpy(pStrBuf, pPathName, iBufLen); + + // translate any backward slashes to forward slashes + for (pPath = pStrBuf; *pPath != '\0'; pPath += 1) + { + if (*pPath == '\\') + { + *pPath = '/'; + } + } + return(pStrBuf); +} + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function ZFileOpen + + \Description + Open a file. + + \Input *pFileName - file name to open + \Input uFlags - flags dictating how to open the file + + \Output + int32_t - file descriptor, or -1 if there was an error + + \Version 1.0 03/16/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +ZFileT ZFileOpen(const char *pFileName, uint32_t uFlags) +{ + char strFileName[4096]; + char strMode[16] = ""; + FILE *pFile; + + if ((pFileName == NULL) || (pFileName[0] == '\0')) + { + return(ZFILE_INVALID); + } + + pFileName = _ZFilePathTranslate(pFileName, strFileName, sizeof(strFileName)); + + ds_memclr(strMode, sizeof(strMode)); + + // map zfile flags to Win32 Mode flags + if (uFlags & ZFILE_OPENFLAG_APPEND) + { + strcat(strMode, "a"); + if (uFlags & ZFILE_OPENFLAG_RDONLY) + strcat(strMode, "+"); + } + else if (uFlags & ZFILE_OPENFLAG_RDONLY) + { + strcat(strMode, "r"); + if (uFlags & ZFILE_OPENFLAG_WRONLY) + strcat(strMode, "+"); + } + else if (uFlags & ZFILE_OPENFLAG_WRONLY) + { + strcat(strMode, "w"); + } + if (uFlags & ZFILE_OPENFLAG_BINARY) + { + strcat(strMode, "b"); + } + + if ((pFile = fopen(pFileName, strMode)) != NULL) + { + return((ZFileT)pFile); + } + else + { + return(ZFILE_INVALID); + } +} + +/*F********************************************************************************/ +/*! + \Function ZFileClose + + \Description + Close a file + + \Input iFileId - file descriptor + + \Output int32_t - return value from fclose() + + \Version 1.0 11/18/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileClose(ZFileT iFileId) +{ + return(fclose((FILE *)iFileId)); +} + +/*F********************************************************************************/ +/*! + \Function ZFileRead + + \Description + Read from a file. + + \Input *pData - pointer to buffer to read to + \Input iSize - amount of data to read + \Input iFileId - file descriptor + + \Output + Number of bytes read + + \Version 1.0 11/18/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileRead(ZFileT iFileId, void *pData, int32_t iSize) +{ + int32_t iResult = (int32_t)fread(pData, 1, iSize, (FILE *)iFileId); + return(ferror((FILE *)iFileId) ? -1 : iResult); +} + +/*F********************************************************************************/ +/*! + \Function ZFileWrite + + \Description + Write to a file. + + \Input *pData - pointer to buffer to write from + \Input iSize - amount of data to write + \Input iFileId - file descriptor + + \Output + Number of bytes written + + \Version 1.0 11/18/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileWrite(ZFileT iFileId, void *pData, int32_t iSize) +{ + int32_t iResult; + + if (iFileId == ZFILE_INVALID) + { + return(0); + } + if ((pData == NULL) || (iSize == 0)) + { + return(0); + } + + iResult = (int32_t)fwrite(pData, 1, iSize, (FILE *)iFileId); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ZFileSeek + + \Description + Seek to location in file. + + \Input iFileId - file id to seek + \Input iOffset - offset to seek to + \Input uFlags - seek mode (ZFILE_SEEKFLAG_*) + + \Output + int64_t - resultant seek location, or -1 on error + + \Version 03/16/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int64_t ZFileSeek(ZFileT iFileId, int64_t iOffset, uint32_t uFlags) +{ + int64_t iResult; + int32_t iFlags=0; + + if (uFlags == ZFILE_SEEKFLAG_CUR) + { + iFlags = SEEK_CUR; + } + else if (uFlags == ZFILE_SEEKFLAG_END) + { + iFlags = SEEK_END; + } + else if (uFlags == ZFILE_SEEKFLAG_SET) + { + iFlags = SEEK_SET; + } + + fseek((FILE *)iFileId, iOffset, iFlags); + iResult = ftell((FILE *)iFileId); + return((iResult >= 0) ? iResult : -1); +} + +/*F********************************************************************************/ +/*! + \Function ZFileDelete + + \Description + Delete a file. + + \Input *pFileName - filename of file to delete + + \Output int32_t - 0=success, error code otherwise + + \Version 03/23/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileDelete(const char *pFileName) +{ + char strFileName[4096]; + int32_t iResult; + + if ((pFileName == NULL) || (pFileName[0] == '\0')) + { + return(ZFILE_ERROR_FILENAME); + } + + pFileName = _ZFilePathTranslate(pFileName, strFileName, sizeof(strFileName)); + + iResult = remove(pFileName); + if (iResult == 0) + { + return(ZFILE_ERROR_NONE); + } + else + { + return(ZFILE_ERROR_FILEDELETE); + } +} + +/*F********************************************************************************/ +/*! + \Function ZFileStat + + \Description + Get File Stat information on a file/dir. + + \Input *pFileName - filename/dir to stat + \Input *pStat + + \Output int32_t - 0=success, error code otherwise + + \Version 03/25/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileStat(const char *pFileName, ZFileStatT *pFileStat) +{ + #if !defined(DIRTYCODE_NX) + char strFileName[4096]; + struct stat FileStat; + int32_t iResult; + + // check for error conditions + if (pFileName == NULL) + { + return(ZFILE_ERROR_FILENAME); + } + if (pFileStat == NULL) + { + return(ZFILE_ERROR_NULLPOINTER); + } + + pFileName = _ZFilePathTranslate(pFileName, strFileName, sizeof(strFileName)); + + // get file status + iResult = stat(pFileName, &FileStat); + + // check for some specific errors + if ((iResult == -1) && (errno == ENOENT)) + { + return(ZFILE_ERROR_NOSUCHFILE); + } + else if (errno == EACCES) + { + return(ZFILE_ERROR_PERMISSION); + } + else if (iResult != 0) + { + return(ZFILE_ERROR_FILESTAT); + } + + // clear the incoming buffer + ds_memclr(pFileStat, sizeof(ZFileStatT)); + // copy from the PC-specific structures + pFileStat->iSize = FileStat.st_size; + pFileStat->uTimeAccess = (uint32_t)FileStat.st_atime; + pFileStat->uTimeCreate = (uint32_t)FileStat.st_ctime; + pFileStat->uTimeModify = (uint32_t)FileStat.st_mtime; + // get the file modes + if (S_ISDIR(FileStat.st_mode)) + { + pFileStat->uMode |= ZFILESTAT_MODE_DIR; + } + if (S_ISREG(FileStat.st_mode)) + { + pFileStat->uMode |= ZFILESTAT_MODE_FILE; + } + if (FileStat.st_mode & S_IRUSR) + { + pFileStat->uMode |= ZFILESTAT_MODE_READ; + } + if (FileStat.st_mode & S_IWUSR) + { + pFileStat->uMode |= ZFILESTAT_MODE_WRITE; + } + if (FileStat.st_mode & S_IXUSR) + { + pFileStat->uMode |= ZFILESTAT_MODE_EXECUTE; + } + #endif + + // done - return no error + return(ZFILE_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function ZFileRename + + \Description + Rename a file. + + \Input *pOldname - old name + \Input *pNewname - new name + + \Output int32_t - 0=success, error code otherwise + + \Version 03/30/2005 (jfrank) First Version +*/ +/********************************************************************************F*/ +int32_t ZFileRename(const char *pOldname, const char *pNewname) +{ + int32_t iResult; + + // check for error conditions + if ((pOldname == NULL) || (pNewname == NULL)) + { + return(ZFILE_ERROR_NULLPOINTER); + } + + // rename the file + iResult = rename(pOldname, pNewname); + + return((iResult == 0) ? ZFILE_ERROR_NONE : ZFILE_ERROR_FILERENAME); +} + +/*F********************************************************************************/ +/*! + \Function ZFileMkdir + + \Description + Make a directory, recursively + + \Input *pPathName - directory path to create + + \Output + int32_t - 0=success, error code otherwise + + \Version 01/25/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ZFileMkdir(const char *pPathName) +{ + char strPathName[4096], *pPath, cTerm = '\0'; + + pPath = _ZFilePathTranslate(pPathName, strPathName, sizeof(strPathName)); + + // traverse pathname, making each directory component as we go + for ( ; ; pPath += 1) + { + if (*pPath == '/') + { + cTerm = *pPath; + *pPath = '\0'; + } + if (*pPath == '\0') + { + int32_t iResult; + if ((iResult = mkdir(strPathName, S_IRWXU)) != 0) + { + if (errno == EEXIST) + { + ZPrintfDbg(("zfilepc: directory %s already exists\n", strPathName)); + } + else + { + ZPrintfDbg(("zfileunix: could not create directory '%s' err=%s\n", strPathName, DirtyErrGetName(errno))); + return(-1); + } + } + } + if (cTerm != '\0') + { + *pPath = cTerm; + cTerm = '\0'; + } + if (*pPath == '\0') + { + break; + } + } + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/source/zfile.c b/r5dev/thirdparty/dirtysdk/contrib/common/source/zfile.c new file mode 100644 index 00000000..b589d92b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/source/zfile.c @@ -0,0 +1,199 @@ +/*H********************************************************************************/ +/*! + \File zfile.c + + \Description + Platform-inspecific host file operations. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 02/16/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "zmem.h" +#include "zlib.h" +#include "zfile.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function ZFileSize + + \Description + Get file size. + + \Input iFileId - file id to get size of + + \Output + int32_t - file size, or -1 on error + + \Version 02/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int64_t ZFileSize(ZFileT iFileId) +{ + int64_t iEnd = ZFileSeek(iFileId, 0, ZFILE_SEEKFLAG_END); + ZFileSeek(iFileId, 0, ZFILE_SEEKFLAG_SET); + return(iEnd); +} + +/*F********************************************************************************/ +/*! + \Function ZFileLoad + + \Description + Open a file and read it into memory. + + \Input *pFilename - pointer to name of file to read + \Input *pFileSize - [out] storage for file size + \Input uOpenFlags - open flags, or zero for default (READONLY) + + \Output + char * - pointer to file data, or NULL + + \Version 02/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +char *ZFileLoad(const char *pFileName, int32_t *pFileSize, uint32_t uOpenFlags) +{ + int64_t iFileSize64; + int32_t iFileSize, iResult; + char *pFileMem; + ZFileT iFileId; + + // determine open flags + if (uOpenFlags == 0) + { + uOpenFlags = ZFILE_OPENFLAG_RDONLY; + } + + // open the file + iFileId = ZFileOpen(pFileName, uOpenFlags); + if (iFileId == ZFILE_INVALID) + { + printf("zfile: unable to open file '%s'\n", pFileName); + return(NULL); + } + + // get the file size + iFileSize64 = ZFileSize(iFileId); + if (iFileSize64 < 0) + { + printf("zfile: unable to get size of file '%s'\n", pFileName); + return(NULL); + } + iFileSize = (int32_t)iFileSize64; // this function does not support files >2GB + + // allocate and clear memory for the file + pFileMem = (char *) ZMemAlloc(iFileSize+1); + if (pFileMem == NULL) + { + printf("zfile: unable to allocate %d bytes to load file '%s'\n", iFileSize+1, pFileName); + return(NULL); + } + ds_memclr(pFileMem, iFileSize+1); + + // read file into memory + iResult = ZFileRead(iFileId, pFileMem, iFileSize); + if (iResult <= 0) + { + printf("zfile: unable to read file '%s'\n", pFileName); + return(NULL); + } + /* null-terminate; we do this in addition to the memset up above because + under windows the size of a text file on disk may be larger than the + file in memory */ + pFileMem[iResult] = '\0'; + + // if size parameter is not null, set it + if (pFileSize != NULL) + { + *pFileSize = iResult; + } + + // close the file + iResult = ZFileClose(iFileId); + if (iResult < 0) + { + printf("zfile: error closing file '%s'\n", pFileName); + } + + // return pointer to memory + return(pFileMem); +} + + +/*F********************************************************************************/ +/*! + \Function ZFileSave + + \Description + Save data to a file. (OVERWRITE and CREATE by default) + + \Input *pFilename - pointer to name of file to read + \Input *pData - pointer to data to save to a file + \Input iSize - amount of data to save to the file + \Input uOpenFlags - open flags, or zero for default (WRONLY|CREATE) + + \Output + int32_t - 0 if success, error code otherwise + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t ZFileSave(const char *pFileName, const char *pData, int32_t iSize, uint32_t uOpenFlags) +{ + int32_t iResult; + ZFileT iFileId; + + // determine what flags to use + if (uOpenFlags == 0) + { + uOpenFlags = ZFILE_OPENFLAG_WRONLY | ZFILE_OPENFLAG_CREATE; + } + + // open the file + iFileId = ZFileOpen(pFileName, uOpenFlags); + if (iFileId == ZFILE_INVALID) + { + ZPrintf("zfile: unable to open file '%s' with flags 0x%X\n", pFileName, uOpenFlags); + return(ZFILE_ERROR_FILEOPEN); + } + + // now write the data + iResult = ZFileWrite(iFileId, (char *)pData, iSize); + if(iResult != iSize) + { + ZPrintf("zfile: write error - size to write [%d] size written [%d]\n", iSize, iResult); + return(ZFILE_ERROR_FILEWRITE); + } + + // and close the file + iResult = ZFileClose(iFileId); + if(iResult != 0) + { + ZPrintf("zfile: close error [%d=0x%X]\n", iResult, iResult); + return(ZFILE_ERROR_FILECLOSE); + } + + // else successful save occurred + return(ZFILE_ERROR_NONE); +} + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/source/zlib.c b/r5dev/thirdparty/dirtysdk/contrib/common/source/zlib.c new file mode 100644 index 00000000..59b73927 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/source/zlib.c @@ -0,0 +1,805 @@ +/*H********************************************************************************/ +/*! + \File zlib.c + + \Description + A simple console style test library that provides a basic + notion of processes, output routines and memory allocation. + Used to implement simple test programs in a unix-style + command-line environment. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 09/15/1999 (gschaefer) Initial Design + \Version 11/08/1999 (gschaefer) Cleanup and revision + \Version 03/17/2005 (jfrank) Cleanup and revision +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#if defined(_WIN32) && !defined(_XBOX) +#define WIN32_LEAN_AND_MEAN 1 + +#pragma warning(push,0) +#include +#pragma warning(pop) +#endif + +#include +#include +#include +#include +#include + +#include "DirtySDK/platform.h" + +#include "DirtySDK/dirtydefs.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "zlib.h" +#include "zmem.h" + +/*** Defines **********************************************************************/ + +#if !defined(_WIN32) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) +#define wsprintf sprintf +#endif + +/*** Type Definitions *************************************************************/ + +struct ZEnviron +{ + ZEnviron *pNext; //!< list of active process contexts + ZContext *pContext; //!< process private context + ZConsole *pConsole; //!< console output pointer + ZCommand *pCommand; //!< pointer to process code + + uint32_t uRuntime; //!< number of ms process has run + uint32_t uRuncount; //!< number of time slices + + int32_t iPID; //!< process identifier + int32_t iStatus; //!< exit status + uint32_t uSchedule; //!< next scheduled tick to run at + + int32_t iArgc; //!< arg count for program + char *pArgv[200]; //!< pointer to entry parms + char strArgb[16*1024]; //!< buffer to hold entry parms +}; + +/*** Variables ********************************************************************/ + +static ZEnviron *_pEnvCurr = NULL; //!< current process +static ZEnviron *_pEnvList = NULL; //!< list of processes + +static void *_Zlib_pPrintfParm = NULL; +static int32_t (*_Zlib_pPrintfHook)(void *pParm, const char *pText) = NULL; + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ZCreate + + \Description + Create a new process environment. Parses command line + and gets process ready to run. + + \Input console - default output console for process + \Input cmdline - the entry command line params + + \Output ZEnviron - new process environment + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +ZEnviron *ZCreate(ZConsole *console, const char *cmdline) +{ + char term; + char *s; + ZEnviron *env; + static uint32_t pid = 1; + + // get new environment + env = (ZEnviron *)ZMemAlloc(sizeof(*env)); + ds_memclr(env, sizeof(*env)); + + // assign unique pid + env->iPID = pid++; + + // save console ref + env->pConsole = console; + + // copy command line + ds_strnzcpy(env->strArgb, cmdline, sizeof(env->strArgb)); + s = env->strArgb; + + // parse command line + for (env->iArgc = 0; env->iArgc < (signed)(sizeof(env->pArgv)/sizeof(env->pArgv[0])); env->iArgc += 1) + { + // skip to next token + while ((*s != 0) && (*s <= ' ')) + ++s; + // see if anything to save + if (*s == 0) + break; + // see if there is a terminator + term = ((*s == '"') || (*s == '\'') ? *s++ : 0); + // record start of token + env->pArgv[env->iArgc] = s; + // find end of token + while ((*s != 0) && (((term == 0) && (*s > ' ')) || ((term != 0) && (*s != term)))) + ++s; + // terminate token + if (*s != 0) + *s++ = 0; + } + + // set status to terminated + env->iStatus = 0; + env->uSchedule = (uint32_t)(-1); + + // put into process list + env->pNext = _pEnvList; + _pEnvList = env; + + // return new environment + return(env); +} + +/*F********************************************************************************/ +/*! + \Function ZDestroy + + \Description + Destroy a process environment + + \Input env - existing process environment + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void ZDestroy(ZEnviron *env) +{ + ZEnviron **scan; + + // remove from environment list + for (scan = &_pEnvList; *scan != NULL; scan = &(*scan)->pNext) + { + if (*scan == env) + { + *scan = env->pNext; + break; + } + } + + // remove from active + if (_pEnvCurr == env) + _pEnvCurr = NULL; + + // destroy any attached context + if (env->pContext != NULL) + ZMemFree(env->pContext); + + // destroy the environment + ZMemFree(env); + return; +} + +/*F********************************************************************************/ +/*! + \Function ZInvoke + + \Description + Execute a process within an existing environment + + \Input env - existing environment + \Input cmd - process code pointer + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void ZInvoke(ZEnviron *env, ZCommand *cmd) +{ + uint32_t tick1, tick2; + + // make sure there is a current environment + if (env == NULL) + return; + _pEnvCurr = env; + + // if this is first run + if (cmd != NULL) + { + // save the command + _pEnvCurr->pCommand = cmd; + // remove any existing context + ZContextCreate(0); + } + else if (_pEnvCurr->pCommand == NULL) + { + return; + } + + // invoke the command + tick1 = ZTick(); + _pEnvCurr->uSchedule = (uint32_t)(-1); + _pEnvCurr->uRuncount += 1; + _pEnvCurr->iStatus = _pEnvCurr->pCommand(_pEnvCurr->pContext, _pEnvCurr->iArgc, _pEnvCurr->pArgv); + tick2 = ZTick(); + _pEnvCurr->uRuntime += tick2-tick1; + + // handle callback request + if ((_pEnvCurr->uSchedule != (uint32_t)(-1)) && (_pEnvCurr->iStatus == ZLIB_STATUS_RUNNING)) + _pEnvCurr->uSchedule += tick2; +} + +/*F********************************************************************************/ +/*! + \Function ZSet + + \Description + Set current environment, based on specified context + + \Input context - context; environment that owns this context will be made current + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void ZSet(ZContext *context) +{ + ZEnviron *env; + + // find context + for (env = _pEnvCurr; env != NULL && env->pContext != context; env = env->pNext) + ; + + // found it? make it current + if (env != NULL) + { + _pEnvCurr = env; + } +} + +/*F********************************************************************************/ +/*! + \Function ZGetPid + + \Description + Get process id of current environment + + \Output + int32_t - pid, or zero if no current environment + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ZGetPid(void) +{ + int32_t iPID = 0; + if (_pEnvCurr != NULL) + { + iPID = _pEnvCurr->iPID; + } + return(iPID); +} + +/*F********************************************************************************/ +/*! + \Function ZCallback + + \Description + Let process signal it wants a callback (has not completed) + + \Input cmd - pointer to callback code + \Input delay - milliseconds until callback + + \Output int32_t - special return value that process returns with + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ZCallback(ZCommand *cmd, int32_t delay) +{ + _pEnvCurr->pCommand = cmd; + _pEnvCurr->uSchedule = delay; + return(ZLIB_STATUS_RUNNING); +} + +/*F********************************************************************************/ +/*! + \Function ZTask + + \Description + Give time to any existing processes that need it. + + \Output int32_t - number of ticks until the next requested update + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ZTask(void) +{ + ZEnviron *env; + uint32_t tick = ZTick(); + uint32_t next = tick+1000; + + // walk environ list and execute processes which are scheduled + for (env = _pEnvList; env != NULL; env = env->pNext) + { + if (tick > env->uSchedule) + ZInvoke(env, NULL); + if (next > env->uSchedule) + next = env->uSchedule; + } + + // figure time until next tick + tick = ZTick(); + return((tick > next) ? 0 : next-tick); +} + +/*F********************************************************************************/ +/*! + \Function ZCleanup + + \Description + Remove any environments containing complete processes. + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void ZCleanup(void) +{ + ZEnviron *env; + + for (env = _pEnvList; env != NULL;) + { + if (env->uSchedule == (uint32_t)(-1)) + { + ZEnviron *kill = env; + env = env->pNext; + ZDestroy(kill); + } + else + { + env = env->pNext; + } + } +} + +/*F********************************************************************************/ +/*! + \Function ZShutdown + + \Description + Kill all active processes in preparation for shutdown. + + \Version 02/18/03 (jbrookes) +*/ +/********************************************************************************F*/ +void ZShutdown(void) +{ + ZEnviron *env; + + for (env = _pEnvList; env != NULL; env = env->pNext) + { + // kill the process + env->uSchedule = (uint32_t)(-1); + env->iStatus = (env->pCommand)(env->pContext, 0, env->pArgv); + env->uSchedule = (uint32_t)(-1); + } + + while(_pEnvList != NULL) + { + ZTask(); + ZCleanup(); + } +} + +/*F********************************************************************************/ +/*! + \Function ZCreateContext + + \Description + Allow a process to allocate persistent private context. + + \Input size - size of needed context + + \Output ZContext * - context of requested size + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +ZContext *ZContextCreate(int32_t size) +{ + if (_pEnvCurr == NULL) + return(NULL); + + if (_pEnvCurr->pContext != NULL) + ZMemFree(_pEnvCurr->pContext); + + _pEnvCurr->pContext = ((size > 0) ? (ZContext *)ZMemAlloc(size) : NULL); + return(_pEnvCurr->pContext); +} + +/*F********************************************************************************/ +/*! + \Function ZPrintf + + \Description + Display output using standard printf semantics. + + \Input Standard printf inputs. + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void ZPrintf(const char *fmt, ...) +{ + char text[4096]; + va_list args; + int32_t iOutput=1; + + // parse the data + va_start(args, fmt); + ds_vsnprintf(text, sizeof(text), fmt, args); + va_end(args); + + // send to debug hook if set + if (_Zlib_pPrintfHook != NULL) + { + iOutput = _Zlib_pPrintfHook(_Zlib_pPrintfParm, text); + } + + // if debug hook didn't override output, print here + if (iOutput != 0) + { + #if defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + OutputDebugStringA(text); + #else + printf("%s", text); + #endif + } +} + +/*F********************************************************************************/ +/*! + \Function ZPrintf2 + + \Description + Display output using standard printf semantics (no hook). + + \Input Standard printf inputs. + + \Version 10/18/2011 (jbrookes) +*/ +/********************************************************************************F*/ +void ZPrintf2(const char *fmt, ...) +{ + char text[4096]; + va_list args; + + // parse the data + va_start(args, fmt); + ds_vsnprintf(text, sizeof(text), fmt, args); + va_end(args); + + // print here + #if defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + OutputDebugStringA(text); + #else + printf("%s", text); + #endif +} + +/*F********************************************************************************/ +/*! + \Function ZPrintfHook + + \Description + Hook into output. + + \Input *pPrintfHook - pointer to function to call with output + \Input *pParm - user parameter + + \Output + None. + + \Version 03/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void ZPrintfHook(int32_t (*pPrintfHook)(void *pParm, const char *pText), void *pParm) +{ + _Zlib_pPrintfHook = pPrintfHook; + _Zlib_pPrintfParm = pParm; +} + +/*F********************************************************************************/ +/*! + \Function ZCmdPS + + \Description + Show list of all process environments. + + \Input *argz - context + \Input argc - arg count + \Input argz - arg strings + + \Output int32_t - exit code + + \Version 10/04/1999 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ZCmdPS(ZContext *argz, int32_t argc, char **pArgv) +{ + ZEnviron *env; + uint32_t tick = ZTick(); + + // handle help + if (argc == 0) { + ZPrintf("%s - show process status list\r\n", pArgv[0]); + return(0); + } + + // show process status list + ZPrintf(" PID STATUS ITER TIME COMMAND\r\n"); + for (env = _pEnvList; env != NULL; env = env->pNext) { + int32_t i; + char text[256]; + char *s = text; + + // dont show ourselves + if (env == _pEnvCurr) + continue; + + // put in pid + s += wsprintf(s, "%5d", env->iPID); + + // add in state/time till next + if (env->uSchedule == (uint32_t)(-1)) + { + s += wsprintf(s, " F %4d", env->iStatus); + } + else + { + int32_t timeout = env->uSchedule-tick; + if (timeout < 0) + timeout = 0; + if (timeout > 9999) + timeout = 9999; + s += wsprintf(s, " S %4d", timeout); + } + + // show run count + s += wsprintf(s, " %4d", (env->uRuncount < 9999 ? env->uRuncount : 9999)); + + // show time used + s += wsprintf(s, " %2d.%03d", env->uRuntime/1000, env->uRuntime%1000); + + // show command name + s += wsprintf(s, " %s", env->pArgv[0]); + + // show command parms + for (i = 1; i < env->iArgc; ++i) + s += wsprintf(s, " '%s'", env->pArgv[i]); + + // end of line + *s++ = '\r'; + *s++ = '\n'; + *s = 0; + ZPrintf("%s", text); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ZCmdKill + + \Description + Kill an existing process. + + \Input *argz - context + \Input argc - arg count + \Input argz - arg strings + + \Output int32_t - exit code + + \Version 10/04/1999 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ZCmdKill(ZContext *argz, int32_t argc, char **pArgv) +{ + int32_t pid; + char *s, *d; + ZEnviron *env; + + // handle help + if (argc == 0) { + ZPrintf("%s pid|name - kill a running command\r\n", pArgv[0]); + return(0); + } + + // check usage + if (argc != 2) { + ZPrintf("usage: %s pid|name\r\n", pArgv[0]); + return(-1); + } + + // get the pid + pid = 0; + for (s = pArgv[1]; (*s >= '0') && (*s <= '9'); ++s) + pid = (pid * 10) + (*s & 15); + + // if its zero, see if the name matches + for (env = _pEnvList; env != NULL; env = env->pNext) + { + for (s = pArgv[1], d = env->pArgv[0]; *s != 0; ++s, ++d) + { + if (*s != *d) + break; + } + if (*s == 0) + { + pid = env->iPID; + break; + } + } + + // make sure we got something + if (pid <= 0) + { + ZPrintf("%s: invalid pid: %s\r\n", pArgv[0], pArgv[1]); + return(-2); + } + + // search process list for match + for (env = _pEnvList; env != NULL; env = env->pNext) { + if ((env != _pEnvCurr) && (env->iPID == pid)) + break; + } + + // error if no matching process + if (env == NULL) + { + ZPrintf("%s: no such process %d\r\n", pArgv[0], pid); + return(-3); + } + + // if already dead + if (env->uSchedule == (uint32_t)(-1)) + { + ZPrintf("%s: process %d already dead\r\n", pArgv[0], pid); + return(-4); + } + + // kill the process + env->uSchedule = (uint32_t)(-1); + env->iStatus = (env->pCommand)(env->pContext, 0, env->pArgv); + env->uSchedule = (uint32_t)(-1); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ZGetStatus + + \Description + Return status of current command. Returns -1 if pEnv is Null. + + \Input pEnv -pointer to current env, command. + + \Output Status of current command. Returns -1 if pEnv is Null. + + \Version 29/11/2005 (TE) +*/ +/********************************************************************************F*/ +int32_t ZGetStatus(ZEnviron *pEnv) +{ + return(pEnv ? pEnv->iStatus :-1); +} + +/*F********************************************************************************/ +/*! + \Function ZGetStatusPid + + \Description + Get status of the process specified by pid + + \Output + int32_t - status, or ZLIB_STATUS_UNKNOWN if process is not found + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ZGetStatusPid(int32_t iPID) +{ + ZEnviron *env; + + // show process status list + for (env = _pEnvList; env != NULL; env = env->pNext) + { + if (env->iPID == iPID) + { + return(env->iStatus); + } + } + return(ZLIB_STATUS_UNKNOWN); +} + +/*F********************************************************************************/ +/*! + \Function ZGetIntArg + + \Description + Get fourcc/integer from command-line argument + + \Input *pArg - pointer to argument + + \Version 11/26/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ZGetIntArg(const char *pArg) +{ + int32_t iValue; + + // check for possible fourcc value + if ((strlen(pArg) == 4) && (isalpha(pArg[0]) || isalpha(pArg[1]) || isalpha(pArg[2]) || isalpha(pArg[3]))) + { + iValue = pArg[0] << 24; + iValue |= pArg[1] << 16; + iValue |= pArg[2] << 8; + iValue |= pArg[3]; + } + else + { + iValue = (signed)strtol(pArg, NULL, 10); + } + return(iValue); +} + +/*F*************************************************************************************/ +/*! + \Function ZTick + + \Description + Return some kind of increasing tick count with millisecond scale (does + not need to have millisecond precision, but higher precision is better). + + \Output + uint32_t - millisecond tick count + + \Version 1.0 05/06/2005 (jfrank) First Version +*/ +/*************************************************************************************F*/ +uint32_t ZTick(void) +{ + return(NetTick()); +} + + +/*F********************************************************************************/ +/*! + \Function ZSleep + + \Description + Put process to sleep for some period of time + + \Input uMSecs - Number of milliseconds to sleep for. + + \Version 05/06/2005 (jfrank) +*/ +/********************************************************************************F*/ +void ZSleep(uint32_t uMSecs) +{ + NetConnSleep(uMSecs); +} + + + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/source/zlist.c b/r5dev/thirdparty/dirtysdk/contrib/common/source/zlist.c new file mode 100644 index 00000000..b8b0085d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/source/zlist.c @@ -0,0 +1,270 @@ +/*H********************************************************************************/ +/*! + \File zlist.h + + \Description + Generic list module for samples to use. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/26/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock.h" +#include "zmem.h" +#include "zlib.h" +#include "zlist.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +struct ZListT +{ + uint32_t iNumEntries; //!< number of entries in the list + uint32_t iEntrySize; //!< size of each entry + uint32_t uHead; //!< index of input list head entry + uint32_t uTail; //!< index of input list tail entry + void *pData; //!< data +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function ZListCreate + + \Description + Create a list object + + \Input iMaxEntries - maximum number of entries in the list + \Input iEntrySize - maximum size of each entry in the list + + \Output ZListT * - pointer to the created list + + \Version 04/26/2005 (jfrank) +*/ +/********************************************************************************F*/ +ZListT *ZListCreate(int32_t iNumEntries, int32_t iEntrySize) +{ + ZListT *pList; + + // check for errors + if((iNumEntries <= 0) || (iEntrySize <= 0)) + { + ZPrintf("Could not create list with [%d] entries of size [%d] total size [%d]\n", + iNumEntries, iEntrySize, iNumEntries * iEntrySize); + return(NULL); + } + + // create the list + pList = (ZListT *)ZMemAlloc(sizeof(ZListT)); + ds_memclr(pList, sizeof(*pList)); + pList->iEntrySize = iEntrySize; + pList->iNumEntries = iNumEntries; + pList->pData = (ZListT *)ZMemAlloc(iNumEntries * iEntrySize); + return(pList); +} + + +/*F********************************************************************************/ +/*! + \Function ZListPushBack + + \Description + Add and entry to the back of a data list + + \Input pList - pointer to the list to use + \Input pEntry - entry to add (will be copied in) + + \Output int32_t - 0 for success, error code otherwise + + \TODO + The current implementation doesn't wrap the queue, so if the head + starts chasing the tail but doesn't catch up, a large amount of the + buffer space can end up being wasted. Either the queue needs to be + modified to wrap, or the buffer memory shifted to allow the head to + be reset to zero without catching the tail. + + \Version 04/26/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t ZListPushBack(ZListT *pList, void *pEntry) +{ + char *pDest; + + // check to make sure we got an entry + if ((pEntry == NULL) || (pList == NULL)) + { + return(ZLIST_ERROR_NULLPOINTER); + } + + // see if the list is full + if (pList->uTail >= pList->iNumEntries) + { + return(ZLIST_ERROR_FULL); + } + + // insert into the list + pDest = (char *)pList->pData + (pList->uTail * pList->iEntrySize); + ds_memcpy(pDest, pEntry, pList->iEntrySize); + // increment the tail pointer + pList->uTail++; + + // done + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ZListPopFront + + \Description + Get an entry off the front of a data list + + \Input pList - pointer to the list to destroy + \Input pEntry - destination for the entry to get (will be copied in), NULL to discard data + + \Output int32_t - <0 for error, 0 for no data left, >0 for amount of data left + + \Version 04/26/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t ZListPopFront(ZListT *pList, void *pEntry) +{ + uint32_t uAmtLeft; + char *pSrc; + + // check to make sure we got an entry + if (pList == NULL) + { + return(ZLIST_ERROR_NULLPOINTER); + } + + // see if the list is empty + uAmtLeft = pList->uTail - pList->uHead; + if (uAmtLeft == 0) + { + // no error - list is just empty + return(0); + } + else + { + // only copy data if we have a container for it + if (pEntry) + { + // copy from the list into the new location + pSrc = (char *)pList->pData + (pList->uHead * pList->iEntrySize); + ds_memcpy(pEntry, (void *)pSrc, pList->iEntrySize); + } + pList->uHead++; + // test for empty list situation + if (pList->uHead == pList->uTail) + { + // empty list - reset + pList->uHead = 0; + pList->uTail = 0; + } + } + + // done + return(uAmtLeft); +} + + +/*F********************************************************************************/ +/*! + \Function ZListPeekFront + + \Description + Examine the front entry of a data list + + \Input pList - pointer to the list to destroy + + \Output void * - pointer to the first entry, NULL if empty + + \Version 04/26/2005 (jfrank) +*/ +/********************************************************************************F*/ +void *ZListPeekFront(ZListT *pList) +{ + char *pSrc; + + // check for errors + if(pList == NULL) + { + return(NULL); + } + + // if list is empty, return NULL + if (pList->uHead == pList->uTail) + { + return(NULL); + } + + // otherwise return a pointer to the data in question + pSrc = (char *)pList->pData + (pList->uHead * pList->iEntrySize); + return(pSrc); +} + + +/*F********************************************************************************/ +/*! + \Function ZListClear + + \Description + Clear the entire list + + \Input pList - pointer to the list to destroy + + \Output None + + \Version 04/26/2005 (jfrank) +*/ +/********************************************************************************F*/ +void ZListClear(ZListT *pList) +{ + if(pList) + { + ds_memclr(pList->pData, pList->iNumEntries * pList->iEntrySize); + pList->uHead = 0; + pList->uTail = 0; + } +} + + +/*F********************************************************************************/ +/*! + \Function ZListDestroy + + \Description + Destroy a list object and free all associated memory + + \Input pList - pointer to the list to destroy + + \Output None + + \Version 04/26/2005 (jfrank) +*/ +/********************************************************************************F*/ +void ZListDestroy(ZListT *pList) +{ + if (pList) + { + if (pList->pData) + { + ZMemFree(pList->pData); + } + + ZMemFree(pList); + } +} + diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/source/zmem.c b/r5dev/thirdparty/dirtysdk/contrib/common/source/zmem.c new file mode 100644 index 00000000..578ade24 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/source/zmem.c @@ -0,0 +1,83 @@ +/*H********************************************************************************/ +/*! + \File zmem.c + + \Description + Memory implementations for use on all platforms. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/16/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" + +#include "zmemtrack.h" +#include "zmem.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function ZMemAlloc + + \Description + Allocate some memory + + \Input uSize - amount of memory, in bytes, to allocate + + \Output void * - pointer to an allocated memory chunk + + \Version 03/16/2005 (jfrank) +*/ +/********************************************************************************F*/ +void *ZMemAlloc(uint32_t uSize) +{ + void *pMem; + if ((pMem = (void *)malloc(uSize)) != NULL) + { + ds_memset(pMem, 0xCD, uSize); + ZMemtrackAlloc(pMem, uSize, 0); + } + return(pMem); +} + + +/*F********************************************************************************/ +/*! + \Function ZMemFree + + \Description + Free a previously allocated chunk of memory + + \Input void *pMem - pointer to an allocated memory chunk + + \Output None + + \Version 03/16/2005 (jfrank) +*/ +/********************************************************************************F*/ +uint32_t ZMemFree(void *pMem) +{ + uint32_t uSize; + ZMemtrackFree(pMem, &uSize); + ds_memset(pMem, 0xEF, uSize); + free(pMem); + return(uSize); +} diff --git a/r5dev/thirdparty/dirtysdk/contrib/common/source/zmemtrack.c b/r5dev/thirdparty/dirtysdk/contrib/common/source/zmemtrack.c new file mode 100644 index 00000000..46fdd4c2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/common/source/zmemtrack.c @@ -0,0 +1,371 @@ +/*H********************************************************************************/ +/*! + \File zmemtrack.c + + \Description + Routines for tracking memory allocations. + + \Copyright + Copyright (c) 2005-2017 Electronic Arts Inc. + + \Version 02/15/2005 (jbrookes) First Version, based on jfrank's implementation. +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "zlib.h" +#include "zmem.h" +#include "zmemtrack.h" + +/*** Defines **********************************************************************/ + +#define ZMEMTRACK_MEMDUMPBYTES (64) //!< number of bytes to print around the leak +#define ZMEMTRACK_MAXALLOCATIONS (1024*8) //!< maximum list allocation size + +/*** Type Definitions *************************************************************/ + +typedef struct ZMemtrackElemT +{ + void *pMem; + uint32_t uMemSize; + uint32_t uTag; +} ZMemtrackElemT; + +typedef struct ZMemtrackRefT +{ + uint32_t uNumAllocations; + uint32_t uMaxAllocations; + uint32_t uTotalAllocations; + uint32_t uTotalMemory; + uint32_t uMaxMemory; + uint8_t bOverflow; + uint8_t bStarted; + uint8_t _pad[2]; + + ZMemtrackLogCbT *pLoggingCb; + void *pUserData; + + ZMemtrackElemT MemList[ZMEMTRACK_MAXALLOCATIONS]; +} ZMemtrackRefT; + +/*** Variables ********************************************************************/ + +static ZMemtrackRefT _ZMemtrack_Ref; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _ZMemtrackLogPrintf + + \Description + Logs the information from the module + + \Input *pFormat - information to log + \Input ... - additional parameters + + \Version 09/18/2017 (eesponda) +*/ +/********************************************************************************F*/ +static void _ZMemtrackLogPrintf(const char *pFormat, ...) +{ + char strText[2048]; + int32_t iOffset = 0; + va_list Args; + ZMemtrackRefT *pRef = &_ZMemtrack_Ref; + + // format output + va_start(Args, pFormat); + iOffset += ds_vsnprintf(strText+iOffset, sizeof(strText)-iOffset, pFormat, Args); + va_end(Args); + + // forward to callback, or print if not callback installed + if (pRef->pLoggingCb != NULL) + { + pRef->pLoggingCb(strText, pRef->pUserData); + } + else + { + ZPrintf("zmemtrack: %s", strText); + } +} + +/*F********************************************************************************/ +/*! + \Function _ZMemtrackPrintLeak + + \Description + Print a memory leak to debug output. + + \Input *pElem - pointer to allocation that was leaked + + \Version 02/15/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ZMemtrackPrintLeak(ZMemtrackElemT *pElem) +{ + static const char _hex[] = "0123456789ABCDEF"; + uint32_t uBytes; + char strOutput[128]; + int32_t iOutput = 2; + + _ZMemtrackLogPrintf("allocated: [%d] bytes at [%p] with tag '%c%c%c%c'\n", pElem->uMemSize, pElem->pMem, + (uint8_t)(pElem->uTag>>24), (uint8_t)(pElem->uTag>>16), (uint8_t)(pElem->uTag>>8), (uint8_t)(pElem->uTag)); + + ds_memset(strOutput, ' ', sizeof(strOutput)-1); + strOutput[sizeof(strOutput)-1] = '\0'; + + for (uBytes = 0; (uBytes < pElem->uMemSize) && (uBytes < ZMEMTRACK_MEMDUMPBYTES); uBytes++, iOutput += 2) + { + unsigned char cByte = ((unsigned char *)(pElem->pMem))[uBytes]; + strOutput[iOutput] = _hex[cByte>>4]; + strOutput[iOutput+1] = _hex[cByte&0xf]; + strOutput[(iOutput/2)+40] = isprint(cByte) ? cByte : '.'; + if (uBytes > 0) + { + if (((uBytes+1) % 16) == 0) + { + strOutput[(iOutput/2)+40+1] = '\0'; + _ZMemtrackLogPrintf("%s\n", strOutput); + ds_memset(strOutput, ' ', sizeof(strOutput)-1); + strOutput[sizeof(strOutput)-1] = '\0'; + iOutput = 0; + } + else if (((uBytes+1) % 4) == 0) + { + iOutput++; + } + } + } + + if (((uBytes > ZMEMTRACK_MEMDUMPBYTES) && (uBytes % ZMEMTRACK_MEMDUMPBYTES) != 0) || (pElem->uMemSize < 16)) + { + strOutput[(iOutput/2)+40+1] = '\0'; + _ZMemtrackLogPrintf("%s\n", strOutput); + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ZMemtrackStartup + + \Description + Start up the ZMemtracking module. + + \Version 02/15/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ZMemtrackStartup(void) +{ + ZMemtrackRefT *pRef = &_ZMemtrack_Ref; + ds_memclr(pRef, sizeof(*pRef)); + pRef->bStarted = TRUE; +} + + +/*F********************************************************************************/ +/*! + \Function ZMemtrackShutdown + + \Description + Shut down the ZMemtracking module. + + \Version 02/15/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ZMemtrackShutdown(void) +{ + // dump the current status of the entire module + ZMemtrackPrint(ZMEMTRACK_PRINTFLAG_TRACKING, 0, NULL); + _ZMemtrack_Ref.bStarted = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function ZMemtrackCallback + + \Description + Set the logging callback + + \Input *pLoggingCb - logging function pointer + \Input *pUserData - additional data to pass along + + \Version 09/18/2017 (eesponda) +*/ +/********************************************************************************F*/ +void ZMemtrackCallback(ZMemtrackLogCbT *pLoggingCb, void *pUserData) +{ + ZMemtrackRefT *pRef = &_ZMemtrack_Ref; + pRef->pLoggingCb = pLoggingCb; + pRef->pUserData = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function ZMemtrackAlloc + + \Description + Track an allocation. + + \Input *pMem - pointer to allocated memory block + \Input uSize - size of allocated memory block + \Input uTag - allocation tag + \Version 02/15/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ZMemtrackAlloc(void *pMem, uint32_t uSize, uint32_t uTag) +{ + ZMemtrackRefT *pRef = &_ZMemtrack_Ref; + uint32_t uMemEntry; + + // now if we got the memory, add to the list + if ((pMem == NULL) || (pRef->bStarted == FALSE)) + { + return; + } + + // find a clear spot + for (uMemEntry = 0; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++) + { + if (pRef->MemList[uMemEntry].pMem == NULL) + { + // get the memory location + pRef->uTotalMemory += uSize; + pRef->uNumAllocations += 1; + pRef->uTotalAllocations += 1; + + // update high-water tracking + if (pRef->uMaxAllocations < pRef->uNumAllocations) + { + pRef->uMaxAllocations = pRef->uNumAllocations; + } + if (pRef->uMaxMemory < pRef->uTotalMemory) + { + pRef->uMaxMemory = pRef->uTotalMemory; + } + + // store the info + pRef->MemList[uMemEntry].pMem = pMem; + pRef->MemList[uMemEntry].uMemSize = uSize; + pRef->MemList[uMemEntry].uTag = uTag; + + break; + } + } + + // check to see if we ran out of room to store this stuff + if (uMemEntry == ZMEMTRACK_MAXALLOCATIONS) + { + pRef->bOverflow = 1; + } +} + + +/*F********************************************************************************/ +/*! + \Function ZMemtrackFree + + \Description + Track a free operation. + + \Input *pMem - pointer to allocated memory block + \Input *pSize - [out] storage for memory block size + + \Version 02/15/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ZMemtrackFree(void *pMem, uint32_t *pSize) +{ + ZMemtrackRefT *pRef = &_ZMemtrack_Ref; + uint32_t uMemEntry; + + if ((pMem == NULL) || (pRef->bStarted == FALSE)) + { + *pSize = 0; + return; + } + + for (uMemEntry = 0, *pSize = 0; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++) + { + if (pRef->MemList[uMemEntry].pMem == pMem) + { + pRef->uTotalMemory -= pRef->MemList[uMemEntry].uMemSize; + pRef->uNumAllocations -= 1; + *pSize = pRef->MemList[uMemEntry].uMemSize; + ds_memclr(&pRef->MemList[uMemEntry], sizeof(pRef->MemList[uMemEntry])); + break; + } + } +} + + +/*F********************************************************************************/ +/*! + \Function ZMemtrackPrint + + \Description + Print overall memory info. + + \Input uFlags - ZMemtrack_PRINTFLAG_* + \Input uTag - [optional] if non-zero, only display memory leaks stamped with this tag + \Input *pModuleName - [optional] pointer to module name + + \Version 02/15/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ZMemtrackPrint(uint32_t uFlags, uint32_t uTag, const char *pModuleName) +{ + ZMemtrackRefT *pRef = &_ZMemtrack_Ref; + uint32_t uMemEntry; + + if (uFlags & ZMEMTRACK_PRINTFLAG_TRACKING) + { + _ZMemtrackLogPrintf("memory report\n"); + _ZMemtrackLogPrintf(" maximum number of allocations at once: [%u]\n", pRef->uMaxAllocations); + _ZMemtrackLogPrintf(" current number of allocations : [%u]\n", pRef->uNumAllocations); + _ZMemtrackLogPrintf(" total number of allocations ever : [%u]\n", pRef->uTotalAllocations); + _ZMemtrackLogPrintf(" maximum memory allocated : [%u] bytes\n", pRef->uMaxMemory); + _ZMemtrackLogPrintf(" current memory allocated : [%u] bytes\n", pRef->uTotalMemory); + _ZMemtrackLogPrintf("\n"); + } + + if (pRef->bOverflow) + { + _ZMemtrackLogPrintf("WARNING: Allocation watcher overflowed!"); + } + + // see if there were any leaks + for (uMemEntry = 0; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++) + { + ZMemtrackElemT *pElem = &pRef->MemList[uMemEntry]; + if ((pElem->pMem != NULL) && ((uTag == 0) || (pElem->uTag == uTag))) + { + break; + } + } + + // if there were leaks, display them + if (uMemEntry != ZMEMTRACK_MAXALLOCATIONS) + { + _ZMemtrackLogPrintf("detected %s memory leaks!\n", pModuleName != NULL ? pModuleName : ""); + for ( ; uMemEntry < ZMEMTRACK_MAXALLOCATIONS; uMemEntry++) + { + ZMemtrackElemT *pElem = &pRef->MemList[uMemEntry]; + if ((pElem->pMem != NULL) && ((uTag == 0) || (pElem->uTag == uTag))) + { + _ZMemtrackPrintLeak(pElem); + } + } + } + +} diff --git a/r5dev/thirdparty/dirtysdk/contrib/voipaux/include/voipaux/voipopus.h b/r5dev/thirdparty/dirtysdk/contrib/voipaux/include/voipaux/voipopus.h new file mode 100644 index 00000000..f54e6b31 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/voipaux/include/voipaux/voipopus.h @@ -0,0 +1,36 @@ +/*H*************************************************************************************/ +/*! + \File voipopus.c + + \Description + PC Audio Encoder / Decoder using Opus + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +#ifndef _voipopus_h +#define _voipopus_h + +/*** Includes **************************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/voip/voipcodec.h" + +/*** Variables *************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// opus codec definition +DIRTYCODE_API extern const VoipCodecDefT VoipOpus_CodecDef; + +#if defined(__cplusplus) +} +#endif + +#endif // _voipopus_h diff --git a/r5dev/thirdparty/dirtysdk/contrib/voipaux/include/voipaux/voipspeex.h b/r5dev/thirdparty/dirtysdk/contrib/voipaux/include/voipaux/voipspeex.h new file mode 100644 index 00000000..7f54f9b6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/voipaux/include/voipaux/voipspeex.h @@ -0,0 +1,46 @@ +/*H********************************************************************************/ +/*! + \File voipspeex.h + + \Description + PC Audio Encoder / Decoder using Speex + + \Copyright + Copyright (c) Electronic Arts 2007. ALL RIGHTS RESERVED. + + \Version 1.0 04/02/2007 (cadam) First version +*/ +/********************************************************************************H*/ + +#ifndef _voipspeex_h +#define _voipspeex_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/voip/voipcodec.h" +#include "DirtySDK/voip/voip.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// speex codec definition +DIRTYCODE_API extern const VoipCodecDefT VoipSpeex_CodecDef; + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif // _voipspeex_h diff --git a/r5dev/thirdparty/dirtysdk/contrib/voipaux/source/voipopus.c b/r5dev/thirdparty/dirtysdk/contrib/voipaux/source/voipopus.c new file mode 100644 index 00000000..b7549c8e --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/voipaux/source/voipopus.c @@ -0,0 +1,582 @@ +/*H*************************************************************************************/ +/*! + \File voipopus.c + + \Description + PC Audio Encoder / Decoder using Opus + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. + + \Notes + We depend on the Speex resampler for resampling (recommended by Opus) + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/dirtysock.h" + +#include +#include + +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/voip/voip.h" +#include "voipopus.h" + +/*** Defines ***************************************************************************/ + +//! maximum duration frame +#define VOIPOPUS_MAX_FRAME (5760) + +//! sampling rate we support in Hz +#if !defined(VOIPOPUS_DEFAULT_SAMPLING_RATE) + #define VOIPOPUS_DEFAULT_SAMPLING_RATE (16000) +#endif + +//! duration of the frame in milliseconds; 20ms +#define VOIPOPUS_FRAMEDURATION (20) + +//! number of channels we support (mono or stereo) +#define VOIPOPUS_DEFAULT_CHANNELS (1) + +//! hard-coded maximum output used when encoding, this is taken from value in voippacket.h (VOIP_MAXMICRPKTSIZE) +#define VOIPOPUS_MAX_OUTPUT (1238) + +//! speex resampler quality value (it is a number from 1 - 10) +#define VOIPOPUS_RESAMPLER_QUALITY (3) + +//! this much space will be needed to resample 20ms of audio +#define VOIPOPUS_RESAMPLE_BUFFER_SIZE ((VOIPOPUS_FRAMEDURATION * VOIPOPUS_DEFAULT_SAMPLING_RATE * sizeof(float)) / 1000) + +/*** Macros ****************************************************************************/ + +//! calculate the sample rate based on number of samples +#define VOIPOPUS_GetSampleRate(uNumSamples) (((uNumSamples) * 1000) / VOIPOPUS_FRAMEDURATION) + +/*** Type Definition *******************************************************************/ + +//! voipopus module state +typedef struct VoipOpusRefT +{ + VoipCodecRefT CodecState; + + int32_t iMemGroup; //!< memgroup id + void *pMemGroupUserData; //!< memgroup userdata + + int32_t iVerbose; //!< logging verbosity level + int32_t iOutputVolume; //!< volumn configuration + + uint32_t uSampleRateIn; //!< what is the sample rate of the data being passed to encode + uint32_t uResamplerRate; //!< sample rate that our resampler is configured to. this allows us to switch the resampler on and off without reallocation + uint8_t bInt32Input; //!< is the input data coming as 32 bit integers + uint8_t bFloatInput; //!< is the input data coming as 32 bit floats + uint8_t _pad[2]; + + SpeexResamplerState *pSpeexResampler; //!< resampler used if sample rate != VOIPOPUS_DEFAULT_SAMPLING_RATE + OpusEncoder *pEncoder; //!< opus encoder + OpusDecoder *aDecoders[1]; //!< opus variable decoders (must come last!) +} VoipOpusRefT; + +/*** Function Prototypes ***************************************************************/ + +static VoipCodecRefT *_VoipOpusCreate(int32_t iNumDecoders); +static void _VoipOpusDestroy(VoipCodecRefT *pState); +static int32_t _VoipOpusEncodeBlock(VoipCodecRefT *pState, uint8_t *pOutput, const int16_t *pInput, int32_t iNumSamples); +static int32_t _VoipOpusDecodeBlock(VoipCodecRefT *pState, int32_t *pOutput, const uint8_t *pInput, int32_t iInputBytes, int32_t iChannel); +static void _VoipOpusReset(VoipCodecRefT *pState); +static int32_t _VoipOpusControl(VoipCodecRefT *pState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); +static int32_t _VoipOpusStatus(VoipCodecRefT *pState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize); + +/*** Variables *************************************************************************/ + +//! voipopus codec definition +const VoipCodecDefT VoipOpus_CodecDef = +{ + _VoipOpusCreate, + _VoipOpusDestroy, + _VoipOpusEncodeBlock, + _VoipOpusDecodeBlock, + _VoipOpusReset, + _VoipOpusControl, + _VoipOpusStatus +}; + +#if DIRTYSOCK_ERRORNAMES +//! errors returned from the opus api +static const DirtyErrT _VoipOpus_aErrList[] = +{ + DIRTYSOCK_ErrorName(OPUS_OK), // 0; No Error + DIRTYSOCK_ErrorName(OPUS_BAD_ARG), // -1; One of more invalid/out of range arguments + DIRTYSOCK_ErrorName(OPUS_BUFFER_TOO_SMALL), // -2; The mode struct passed is invalid + DIRTYSOCK_ErrorName(OPUS_INTERNAL_ERROR), // -3; An internal error was detected + DIRTYSOCK_ErrorName(OPUS_INVALID_PACKET), // -4; The compressed data passed is corrupted + DIRTYSOCK_ErrorName(OPUS_UNIMPLEMENTED), // -5; Invalid/unsupported request number + DIRTYSOCK_ErrorName(OPUS_INVALID_STATE), // -6; An encoder or decoder structure is invalid or already freed + DIRTYSOCK_ErrorName(OPUS_ALLOC_FAIL), // -7; Memory allocation has failed + DIRTYSOCK_ListEnd() +}; +#endif + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusCreate + + \Description + Create a Opus codec state. + + \Input iNumDecoders - number of decoder channels + + \Output + VoipCodecStateT * - pointer to opus codec state + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static VoipCodecRefT *_VoipOpusCreate(int32_t iNumDecoders) +{ + VoipOpusRefT *pState; + int32_t iResult, iMemGroup, iDecoder, iMemSize; + void *pMemGroupUserData; + + // query memgroup information + iMemGroup = VoipStatus(NULL, 'mgrp', 0, NULL, 0); + VoipStatus(NULL, 'mgud', 0, &pMemGroupUserData, sizeof(pMemGroupUserData)); + + // allocate and initialize module state + iMemSize = sizeof(*pState) + (sizeof(OpusDecoder *) * (iNumDecoders - 1)); + if ((pState = (VoipOpusRefT *)DirtyMemAlloc(iMemSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipopus: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pState, iMemSize); + pState->CodecState.pCodecDef = &VoipOpus_CodecDef; + pState->CodecState.iDecodeChannels = iNumDecoders; + pState->CodecState.bVadEnabled = TRUE; + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + pState->iVerbose = 2; + pState->iOutputVolume = 1 << VOIP_CODEC_OUTPUT_FRACTIONAL; + + // allocate and initialize the encoder + if ((iMemSize = opus_encoder_get_size(VOIPOPUS_DEFAULT_CHANNELS)) <= 0) + { + NetPrintf(("voipopus: unable to get encoder size for allocation\n")); + _VoipOpusDestroy(&pState->CodecState); + return(NULL); + } + if ((pState->pEncoder = (OpusEncoder *)DirtyMemAlloc(iMemSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipopus: unable to allocate the encoder\n")); + _VoipOpusDestroy(&pState->CodecState); + return(NULL); + } + if ((iResult = opus_encoder_init(pState->pEncoder, VOIPOPUS_DEFAULT_SAMPLING_RATE, VOIPOPUS_DEFAULT_CHANNELS, OPUS_APPLICATION_VOIP)) != OPUS_OK) + { + NetPrintf(("voipopus: unable to initialize the encoder (err=%s)\n", DirtyErrGetNameList(iResult, _VoipOpus_aErrList))); + + _VoipOpusDestroy(&pState->CodecState); + return(NULL); + } + // allocate and initialize the decoders + if ((iMemSize = opus_decoder_get_size(VOIPOPUS_DEFAULT_CHANNELS)) <= 0) + { + NetPrintf(("voipopus: unable to get decoder size for allocation\n")); + _VoipOpusDestroy(&pState->CodecState); + return(NULL); + } + for (iDecoder = 0; iDecoder < iNumDecoders; iDecoder += 1) + { + if ((pState->aDecoders[iDecoder] = (OpusDecoder *)DirtyMemAlloc(iMemSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipopus: unable to allocate the decoder\n")); + _VoipOpusDestroy(&pState->CodecState); + return(NULL); + } + if ((iResult = opus_decoder_init(pState->aDecoders[iDecoder], VOIPOPUS_DEFAULT_SAMPLING_RATE, VOIPOPUS_DEFAULT_CHANNELS)) != OPUS_OK) + { + NetPrintf(("voipopus: unable to initialize the decoder (err=%s)\n", DirtyErrGetNameList(iResult, _VoipOpus_aErrList))); + _VoipOpusDestroy(&pState->CodecState); + return(NULL); + } + } + return(&pState->CodecState); +} + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusDestroy + + \Description + Destroy the Opus codec state + + \Input *pState - codec state + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static void _VoipOpusDestroy(VoipCodecRefT *pState) +{ + OpusDecoder **pDecoder; + int32_t iDecoder; + VoipOpusRefT *pOpus = (VoipOpusRefT *)pState; + + for (iDecoder = 0; iDecoder < pOpus->CodecState.iDecodeChannels; iDecoder += 1) + { + if ((pDecoder = &pOpus->aDecoders[iDecoder]) != NULL) + { + DirtyMemFree(*pDecoder, VOIP_MEMID, pOpus->iMemGroup, pOpus->pMemGroupUserData); + *pDecoder = NULL; + } + } + if (pOpus->pEncoder != NULL) + { + DirtyMemFree(pOpus->pEncoder, VOIP_MEMID, pOpus->iMemGroup, pOpus->pMemGroupUserData); + pOpus->pEncoder = NULL; + } + + if (pOpus->pSpeexResampler != NULL) + { + speex_resampler_destroy(pOpus->pSpeexResampler); + pOpus->pSpeexResampler = NULL; + } + + DirtyMemFree(pOpus, VOIP_MEMID, pOpus->iMemGroup, pOpus->pMemGroupUserData); +} + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusConvertInt32ToFloat + + \Description + Convert int32_t samples into floats, within the same buffer passed in + + \Input *pState - codec state + \Input *pInSamples - input int32_t samples + \Input iNumSamples - number of samples in pInSamples + + \Output + int32_t - number of samples converted + + \Version 04/09/2019 (cvienneau) +*/ +/*************************************************************************************F*/ +static int32_t _VoipOpusConvertInt32ToFloat(VoipCodecRefT *pState, uint8_t *pInBytes, int32_t iNumSamples) +{ + VoipOpusRefT *pOpus = (VoipOpusRefT *)pState; + + if (pOpus->bInt32Input) + { + int32_t *pInput = (int32_t*)pInBytes; + float *pOutput = (float*)pInBytes; + + int32_t iBufferIndex; + for (iBufferIndex = 0; iBufferIndex < iNumSamples; ++iBufferIndex) + { + pOutput[iBufferIndex] = (float)pInput[iBufferIndex] / INT32_MAX; + } + return (iBufferIndex); + } + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusResample + + \Description + Resample in coming samples to the rate of VOIPOPUS_DEFAULT_SAMPLING_RATE. + If VOIPOPUS_DEFAULT_SAMPLING_RATE equal iNumSamples no resampling is done. + + \Input *pState - codec state + \Input *pInBytes - input samples in byte array + \Input iNumSamples - number of samples in pInBytes + \Input *pOutBytes - output samples written + \Input uOutBuffSize - size of pOutBytes + + \Output + int32_t - number of samples written + + \Version 03/26/2019 (tcho) +*/ +/*************************************************************************************F*/ +static int32_t _VoipOpusResample(VoipCodecRefT *pState, const uint8_t *pInBytes, int32_t iNumSamples, uint8_t *pOutBytes, uint32_t uOutBuffSize) +{ + VoipOpusRefT *pOpus = (VoipOpusRefT *)pState; + int32_t iOutputSamples = iNumSamples; //default skipped resample + int32_t iError; + + if (pOpus->bFloatInput) + { + iOutputSamples = uOutBuffSize / sizeof(float); //goes into speex_resampler_process_float as buffer size, comes out as samples written + if ((iError = speex_resampler_process_float(pOpus->pSpeexResampler, 0, (const float *)pInBytes, (uint32_t *)&iNumSamples, (float *)pOutBytes, (uint32_t *)&iOutputSamples)) != 0) + { + NetPrintf(("voipopus: error resampling float, %d input samples (Error = %d).\n", iNumSamples, iError)); + } + } + else + { + iOutputSamples = uOutBuffSize / sizeof(int16_t); //goes into speex_resampler_process_int as buffer size, comes out as samples written + if ((iError = speex_resampler_process_int(pOpus->pSpeexResampler, 0, (const int16_t *)pInBytes, (uint32_t *)&iNumSamples, (int16_t *)pOutBytes, (uint32_t *)&iOutputSamples)) != 0) + { + NetPrintf(("voipopus: error resampling int16, %d input samples, %d (Error = %d).\n", iNumSamples, iError)); + } + } + + return(iOutputSamples); +} + + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusEncodeBlock + + \Description + Encode a buffer 16-bit audio or float sample using the Opus encoder + + \Input *pState - codec state + \Input *pOutput - [out] outbut buffer + \Input *pInput - input buffer + \Input iNumSamples - the number of samples to encode + + \Output + int32_t - positive=number of encoded bytes, negative=error + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static int32_t _VoipOpusEncodeBlock(VoipCodecRefT *pState, uint8_t *pOutput, const int16_t *pInput, int32_t iNumSamples) +{ + VoipOpusRefT *pOpus = (VoipOpusRefT *)pState; + int32_t iResult = -1; + uint32_t uSampleRateIn; + uint8_t aResampledData[VOIPOPUS_RESAMPLE_BUFFER_SIZE]; + + /* if we haven't set a sample rate manually via the 'insr' control calculate the sample rate based on number of samples. this is with the assumption of 20ms audio + $$todo$$ investigate removing the manual calls of 'insr' on xbox if we can confirm that this calculation works */ + uSampleRateIn = (pOpus->uSampleRateIn == 0) ? VOIPOPUS_GetSampleRate(iNumSamples) : pOpus->uSampleRateIn; + + // convert the data to a useable format if needed + iResult = _VoipOpusConvertInt32ToFloat(pState, (uint8_t*)pInput, iNumSamples); + + if (uSampleRateIn != VOIPOPUS_DEFAULT_SAMPLING_RATE) + { + // re-create the resampler if needed + if (uSampleRateIn != pOpus->uResamplerRate) + { + if (pOpus->pSpeexResampler != NULL) + { + speex_resampler_destroy(pOpus->pSpeexResampler); + } + if ((pOpus->pSpeexResampler = speex_resampler_init_frac(VOIPOPUS_DEFAULT_CHANNELS, uSampleRateIn, VOIPOPUS_DEFAULT_SAMPLING_RATE, uSampleRateIn, VOIPOPUS_DEFAULT_SAMPLING_RATE, VOIPOPUS_RESAMPLER_QUALITY, &iResult)) == NULL) + { + NetPrintf(("voipopus: unable to allocate resampler (Error = %d).\n", iResult)); + return(iResult); + } + pOpus->uResamplerRate = uSampleRateIn; + } + + // resample the data if needed + iResult = _VoipOpusResample(pState, (uint8_t*)pInput, iNumSamples, aResampledData, sizeof(aResampledData)); + if (iResult != iNumSamples) + { + pInput = (const int16_t*)aResampledData; + iNumSamples = iResult; + } + } + + // encode as float or int16 + if (pOpus->bFloatInput) + { + if ((iResult = opus_encode_float(pOpus->pEncoder, (float*)pInput, iNumSamples, pOutput, VOIPOPUS_MAX_OUTPUT)) < 0) + { + NetPrintf(("voipopus: unable to encode float (err=%s)\n", DirtyErrGetNameList(iResult, _VoipOpus_aErrList))); + } + } + else + { + if ((iResult = opus_encode(pOpus->pEncoder, pInput, iNumSamples, pOutput, VOIPOPUS_MAX_OUTPUT)) < 0) + { + NetPrintf(("voipopus: unable to encode int16_t (err=%s)\n", DirtyErrGetNameList(iResult, _VoipOpus_aErrList))); + } + } + return(iResult); +} + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusDecodeBlock + + \Description + Decode a Opus encoded input to 16-bit linear PCM samples + + \Input *pState - codec state + \Input *pOutput - [out] outbut buffer + \Input *pInput - input buffer + \Input iInputBytes - size of the input buffer + \Input iChannel - the decode channel for which we are decoding data + + \Output + int32_t - positive=number of decoded samples, negative=error + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static int32_t _VoipOpusDecodeBlock(VoipCodecRefT *pState, int32_t *pOutput, const uint8_t *pInput, int32_t iInputBytes, int32_t iChannel) +{ + VoipOpusRefT *pOpus = (VoipOpusRefT *)pState; + int32_t iResult, iSample; + int16_t aOutput[VOIPOPUS_MAX_FRAME]; + + if ((iChannel < 0) || (iChannel > pOpus->CodecState.iDecodeChannels)) + { + NetPrintf(("voipopus: trying to decode with invalid decoder channel\n")); + return(-1); + } + + if ((iResult = opus_decode(pOpus->aDecoders[iChannel], pInput, iInputBytes, aOutput, VOIPOPUS_MAX_FRAME, 0)) < 0) + { + NetPrintf(("voipopus: unable to decode (err=%s)\n", DirtyErrGetNameList(iResult, _VoipOpus_aErrList))); + } + + // accumulate output in the expected format + for (iSample = 0; iSample < iResult; iSample += 1) + { + pOutput[iSample] += (aOutput[iSample] * pOpus->iOutputVolume) >> VOIP_CODEC_OUTPUT_FRACTIONAL; + } + return(iResult); +} + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusReset + + \Description + Reset the codec state + + \Input *pState - codec state + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static void _VoipOpusReset(VoipCodecRefT *pState) +{ + int32_t iChannel; + VoipOpusRefT *pOpus = (VoipOpusRefT *)pState; + + opus_encoder_ctl(pOpus->pEncoder, OPUS_RESET_STATE); + + for (iChannel = 0; iChannel < pOpus->CodecState.iDecodeChannels; iChannel += 1) + { + opus_decoder_ctl(pOpus->aDecoders[iChannel], OPUS_RESET_STATE); + } +} + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusControl + + \Description + Set control options + + \Input *pState - codec state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'plvl' - Set the output volumn + 'infl' - Set if the input samples are float via iValue, (default int16) + 'inin' - Set if the input samples are int32_t via iValue, (default int16) + 'insr' - Set the input sample rate + 'spam' - Set debug output verbosity + \endverbatim + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static int32_t _VoipOpusControl(VoipCodecRefT *pState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + VoipOpusRefT *pOpus = (VoipOpusRefT *)pState; + + if (iControl == 'plvl') + { + pOpus->iOutputVolume = iValue; + return(0); + } + if (iControl == 'infl') + { + pOpus->bInt32Input = FALSE; + pOpus->bFloatInput = iValue; + return(0); + } + if (iControl == 'inin') + { + pOpus->bInt32Input = iValue; //int32s are converted to float, so we set both + pOpus->bFloatInput = iValue; + return(0); + } + + if (iControl == 'insr') + { + pOpus->uSampleRateIn = iValue; + return(0); + } + if (iControl == 'spam') + { + pOpus->iVerbose = iValue; + return(0); + } + // unhandled control + return(-1); +} + +/*F*************************************************************************************/ +/*! + \Function _VoipOpusStatus + + \Description + Get codec status + + \Input *pState - codec state + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuffer - [out] storage for selector output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'vlen' - returns TRUE to indicate we are using variable length frames + \endverbatim + + \Version 07/03/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static int32_t _VoipOpusStatus(VoipCodecRefT *pState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + if (iSelect == 'vlen') + { + *(uint8_t *)pBuffer = TRUE; + return(0); + } + // unhandle selector + return(-1); +} diff --git a/r5dev/thirdparty/dirtysdk/contrib/voipaux/source/voipspeex.c b/r5dev/thirdparty/dirtysdk/contrib/voipaux/source/voipspeex.c new file mode 100644 index 00000000..1086aa64 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/contrib/voipaux/source/voipspeex.c @@ -0,0 +1,663 @@ +/*H*************************************************************************************************/ +/*! + \File voipspeex.c + + \Description + PC Audio Encoder / Decoder using Speex + + \Copyright + Copyright (c) Electronic Arts 2007. ALL RIGHTS RESERVED. + + \Version 1.0 04/02/2007 (cadam) First version +*/ +/*************************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "voipspeex.h" + +#include +#include + +/*** Defines ***************************************************************************/ + +#define VOIPSPEEX_ENABLED (TRUE) // enable speex codec + +#ifndef VOIPSPEEX_PREPROCESS +#define VOIPSPEEX_PREPROCESS (0) // Speex preprocessor +#endif + +// encode/decode information +#define VOIPSPEEX_FRAME_SIZE (160) +#define VOIPSPEEX_MAX_BYTES (200) +#define VOIPSPEEX_COMP_SIZE (38) + +// speex settings +#define VOIPSPEEX_COMPLEXITY (1) +#define VOIPSPEEX_QUALITY (8) +#define VOIPSPEEX_PERCEPTUAL (1) + +// speex preprocessor settings +#define VOIPSPEEX_DENOISE (1) +#define VOIPSPEEX_AGC (1) +#define VOIPSPEEX_PROB_START (30) +#define VOIPSPEEX_PROB_CONTINUE (7) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! voipspeex decoder ref +typedef struct VoipSpeexDecoderT +{ + int32_t iChannel; + void *pDecodeState; +} VoipSpeexDecoderT; + +//! voipspeex module state +typedef struct VoipSpeexStateT +{ + VoipCodecRefT CodecState; + + // module memory group + int32_t iMemGroup; + void *pMemGroupUserData; + + int32_t iNumChannels; + + int32_t iOutputVolume; + + SpeexBits sEncodeBits; + SpeexBits *pDecodeBits; + + void *pEncodeState; + VoipSpeexDecoderT *pDecoders; + SpeexPreprocessState *pPreprocessor; + + #if DIRTYCODE_LOGGING + int32_t iDebugLevel; + #endif +} VoipSpeexStateT; + + +/*** Function Prototypes ***************************************************************/ + +#if VOIPSPEEX_ENABLED +static VoipCodecRefT *_VoipSpeexCreate(int32_t iDecodeChannels); +static void _VoipSpeexDestroy(VoipCodecRefT *pState); +static int32_t _VoipSpeexEncodeBlock(VoipCodecRefT *pState, uint8_t *pOut, const int16_t *pInp, int32_t iNumSamples); +static int32_t _VoipSpeexDecodeBlock(VoipCodecRefT *pState, int32_t *pOut, const uint8_t *pInp, int32_t iInputBytes, int32_t iChannel); +static void _VoipSpeexReset(VoipCodecRefT *pState); +static int32_t _VoipSpeexControl(VoipCodecRefT *pState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); +static int32_t _VoipSpeexStatus(VoipCodecRefT *pState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize); +#else +static VoipCodecRefT *_VoipSpeexCreate(int32_t iDecodeChannels) { return NULL; } +static void _VoipSpeexDestroy(VoipCodecRefT *pState) { } +static int32_t _VoipSpeexEncodeBlock(VoipCodecRefT *pState, uint8_t *pOut, const int16_t *pInp, int32_t iNumSamples) { return -1; } +static int32_t _VoipSpeexDecodeBlock(VoipCodecRefT *pState, int32_t *pOut, const uint8_t *pInp, int32_t iInputBytes, int32_t iChannel) { return -1; } +static void _VoipSpeexReset(VoipCodecRefT *pState) { } +static int32_t _VoipSpeexControl(VoipCodecRefT *pState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) { return -1; } +static int32_t _VoipSpeexStatus(VoipCodecRefT *pState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize) { return -1; } +#endif + +/*** Variables *************************************************************************/ + +//! voipspeex codec block +const VoipCodecDefT VoipSpeex_CodecDef = +{ + _VoipSpeexCreate, + _VoipSpeexDestroy, + _VoipSpeexEncodeBlock, + _VoipSpeexDecodeBlock, + _VoipSpeexReset, + _VoipSpeexControl, + _VoipSpeexStatus, +}; + +#if VOIPSPEEX_ENABLED + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexSetControls + + \Description + Sets all the controls on the codec + + \Input pCodecState - codec state + + \Version 01/12/08 (grouse) +*/ +/*************************************************************************************************F*/ +static void _VoipSpeexSetControls( VoipCodecRefT *pCodecState ) +{ + VoipSpeexStateT *pState = (VoipSpeexStateT *)pCodecState; + + int32_t iChannel; + + int32_t iComplexity = VOIPSPEEX_COMPLEXITY; + int32_t iQuality = VOIPSPEEX_QUALITY; + int32_t iPerceptual = VOIPSPEEX_PERCEPTUAL; + +#if VOIPSPEEX_PREPROCESS + int32_t iDenoise = VOIPSPEEX_DENOISE; + int32_t iAGC = VOIPSPEEX_AGC; + int32_t iProbStart = VOIPSPEEX_PROB_START; + int32_t iProbContinue = VOIPSPEEX_PROB_CONTINUE; +#endif + + int32_t iReturnVal = 0; + + iReturnVal = speex_encoder_ctl(pState->pEncodeState, SPEEX_SET_COMPLEXITY, &iComplexity); + if( iReturnVal != 0 ) + { + NetPrintf(("voipspeex: error setting encoder control SPEEX_SET_COMPLEXITY with code %d\n", iReturnVal)); + } + + iReturnVal = speex_encoder_ctl(pState->pEncodeState, SPEEX_SET_QUALITY, &iQuality); + if( iReturnVal != 0 ) + { + NetPrintf(("voipspeex: error setting encoder control SPEEX_SET_QUALITY with code %d\n", iReturnVal)); + } + + for (iChannel=0; iChannel < pState->CodecState.iDecodeChannels; iChannel++) + { + iReturnVal = speex_decoder_ctl(pState->pDecoders[iChannel].pDecodeState, SPEEX_SET_ENH, &iPerceptual); + if( iReturnVal != 0 ) + { + NetPrintf(("voipspeex: error setting decoder control SPEEX_SET_ENH for decoder %d with code %d\n", iChannel, iReturnVal)); + } + } + +#if VOIPSPEEX_PREPROCESS + iReturnVal = speex_preprocess_ctl(pState->pPreprocessor, SPEEX_PREPROCESS_SET_DENOISE, &iDenoise); + if( iReturnVal != 0 ) + { + NetPrintf(("voipspeex: error setting preprocessor control SPEEX_PREPROCESS_SET_DENOISE with code %d\n", iReturnVal)); + } + + iReturnVal = speex_preprocess_ctl(pState->pPreprocessor, SPEEX_PREPROCESS_SET_AGC, &iAGC); + if( iReturnVal != 0 ) + { + NetPrintf(("voipspeex: error setting preprocessor control SPEEX_PREPROCESS_SET_AGC with code %d\n", iReturnVal)); + } + + iReturnVal = speex_preprocess_ctl(pState->pPreprocessor, SPEEX_PREPROCESS_SET_PROB_START, &iProbStart); + if( iReturnVal != 0 ) + { + NetPrintf(("voipspeex: error re-setting preprocessor control SPEEX_PREPROCESS_SET_PROB_START with code %d\n", iReturnVal)); + } + + iReturnVal = speex_preprocess_ctl(pState->pPreprocessor, SPEEX_PREPROCESS_SET_PROB_CONTINUE, &iProbContinue); + if( iReturnVal != 0 ) + { + NetPrintf(("voipspeex: error re-setting preprocessor control SPEEX_PREPROCESS_SET_PROB_CONTINUE with code %d\n", iReturnVal)); + } +#endif +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexCreate + + \Description + Create a Speex codec state. + + \Input iDecodeChannels - number of decoder channels + + \Output + VoipCodecStateT * - pointer to speex codec state + + \Version 04/02/2007 (cadam) +*/ +/*************************************************************************************************F*/ +static VoipCodecRefT *_VoipSpeexCreate(int32_t iDecodeChannels) +{ + VoipSpeexStateT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + int32_t iChannel; + + // Query mem group id + iMemGroup = VoipStatus(NULL, 'mgrp', 0, NULL, 0); + + // Query mem group user data + VoipStatus(NULL, 'mgud', 0, &pMemGroupUserData, sizeof(pMemGroupUserData)); + + // allocate memory for the state structure and initialize the variables + pState = (VoipSpeexStateT *)DirtyMemAlloc(sizeof(VoipSpeexStateT), VOIP_MEMID, iMemGroup, pMemGroupUserData); + ds_memclr(pState, sizeof(VoipSpeexStateT)); + NetPrintfVerbose((pState->iDebugLevel, 0, "voipspeex: allocated module reference at %p\n", pState)); + + // initialize the state variables + pState->CodecState.pCodecDef = &VoipSpeex_CodecDef; + pState->CodecState.iDecodeChannels = iDecodeChannels; + pState->CodecState.bVadEnabled = TRUE; + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + pState->iNumChannels = iDecodeChannels; + + // set the power threshold and output level + pState->iOutputVolume = 1 << VOIP_CODEC_OUTPUT_FRACTIONAL; + + // initialize the speex bits + speex_bits_init(&pState->sEncodeBits); + + // create and initialize the encoder + pState->pEncodeState = speex_encoder_init(&speex_nb_mode); + if (pState->pEncodeState == NULL) + { + NetPrintf(("voipspeex: failed to create encoder\n")); + speex_bits_destroy(&pState->sEncodeBits); + DirtyMemFree(pState, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(NULL); + } + + // allocate an array of pointers to hold pointers to one decoder per channel + pState->pDecoders = DirtyMemAlloc(sizeof(VoipSpeexDecoderT) * iDecodeChannels, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + NetPrintfVerbose((pState->iDebugLevel, 0, "voipspeex: decoder array allocated %d bytes at %p\n", sizeof(VoipSpeexDecoderT) * iDecodeChannels, pState->pDecoders)); + pState->pDecodeBits = DirtyMemAlloc(sizeof(SpeexBits) * iDecodeChannels, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + NetPrintfVerbose((pState->iDebugLevel, 0, "voipspeex: decode bits array allocated %d bytes at %p\n", sizeof(SpeexBits) * iDecodeChannels, pState->pDecodeBits)); + + // initialize the decoders + for (iChannel = 0; iChannel < pState->iNumChannels; iChannel++) + { + pState->pDecoders[iChannel].iChannel = iChannel; + pState->pDecoders[iChannel].pDecodeState = speex_decoder_init(&speex_nb_mode); + + if (pState->pDecoders[iChannel].pDecodeState == NULL) + { + // we only need to destroy the ones before the failure + pState->iNumChannels = iChannel; + + NetPrintf(("voipspeex: failed to create decoder %d\n", iChannel)); + for (iChannel = 0; iChannel < pState->iNumChannels; iChannel++) + { + speex_decoder_destroy(pState->pDecoders[iChannel].pDecodeState); + speex_bits_destroy(&pState->pDecodeBits[iChannel]); + } + + DirtyMemFree(pState->pDecoders, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + DirtyMemFree(pState->pDecodeBits, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + speex_encoder_destroy(pState->pEncodeState); + DirtyMemFree(pState, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + return(NULL); + } + + speex_bits_init(&pState->pDecodeBits[iChannel]); + } + +#if VOIPSPEEX_PREPROCESS + pState->pPreprocessor = speex_preprocess_state_init(VOIPSPEEX_FRAME_SIZE, 8000); + if (pState->pPreprocessor == NULL) + { + NetPrintf(("voipspeex: Failed to create pre-processor\n")); + + for (iChannel = 0; iChannel < pState->iNumChannels; iChannel++) + { + if (pState->pDecoders[iChannel].pDecodeState) + { + speex_decoder_destroy(pState->pDecoders[iChannel].pDecodeState); + speex_bits_destroy(&pState->pDecodeBits[iChannel]); + } + } + + DirtyMemFree(pState->pDecoders, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + DirtyMemFree(pState->pDecodeBits, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + speex_encoder_destroy(pState->pEncodeState); + DirtyMemFree(pState, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + return(NULL); + } +#else + pState->pPreprocessor = NULL; +#endif + + _VoipSpeexSetControls(&pState->CodecState); + + NetPrintfVerbose((pState->iDebugLevel, 0, "voipspeex: returning %p\n", &pState->CodecState)); + return(&pState->CodecState); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexDestroy + + \Description + Destroy a Speex codec state. + + \Input *pState - state to destroy + + \Version 04/02/2007 (cadam) +*/ +/*************************************************************************************************F*/ +static void _VoipSpeexDestroy(VoipCodecRefT *pCodecState) +{ + int32_t i; + + VoipSpeexStateT *pState = (VoipSpeexStateT *)pCodecState; + +#if VOIPSPEEX_PREPROCESS + speex_preprocess_state_destroy(pState->pPreprocessor); +#endif + + // destroy the decoders then free the memory that was allocated for them + for (i = 0; i < pState->iNumChannels; i++) + { + speex_decoder_destroy(pState->pDecoders[i].pDecodeState); + speex_bits_destroy(&pState->pDecodeBits[i]); + } + + // free the array that held the decoders + DirtyMemFree(pState->pDecoders, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + // free the array that held the decode bits + DirtyMemFree(pState->pDecodeBits, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + // destroy the encoder + speex_encoder_destroy(pState->pEncodeState); + + // destroy the SpeexBits + speex_bits_destroy(&pState->sEncodeBits); + + // free the state + DirtyMemFree(pState, VOIP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexEncodeBlock + + \Description + Encode a buffer 16-bit audio samples using the Speex encoder. + + \Input *pState - pointer to encode state + \Input *pOut - pointer to output buffer + \Input *pInp - pointer to input buffer + \Input iNumSamples - number of samples to encode + + \Output + int32_t - size of compressed data in bytes + + \Version 04/05/2007 (cadam) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipSpeexEncodeBlock(VoipCodecRefT *pCodecState, unsigned char *pOut, const int16_t *pInp, int32_t iNumSamples) +{ + int32_t iFrame, iNumFrames, iSample; + VoipSpeexStateT *pState = (VoipSpeexStateT *)pCodecState; + unsigned char *pOutput = pOut; + const int16_t *pInput = pInp; + spx_int16_t iFrameBuffer[VOIPSPEEX_FRAME_SIZE]; + int32_t iNumBytes, iNumBytesEncoded = 0; + + iNumFrames = iNumSamples/VOIPSPEEX_FRAME_SIZE; + + NetPrintfVerbose((pState->iDebugLevel, 2, "voipspeex: encoding %d samples (%d frames)\n", iNumSamples, iNumFrames)); + + if ((iNumSamples % VOIPSPEEX_FRAME_SIZE) != 0) + { + NetPrintf(("voipspeex: error - speex encoder can only encode multiples of %d samples.\n", VOIPSPEEX_FRAME_SIZE)); + return(0); + } + + for (iFrame = 0; iFrame < iNumFrames; iFrame++) + { + ds_memclr(iFrameBuffer, sizeof(iFrameBuffer)); + + // convert the 16 bit input values to a float buffer for Speex + for (iSample = 0; iSample < VOIPSPEEX_FRAME_SIZE; iSample++) + { + iFrameBuffer[iSample] = pInput[iSample]; + } + + // reset the SpeexBits + speex_bits_reset(&pState->sEncodeBits); + +#if VOIPSPEEX_PREPROCESS + speex_preprocess(pState->pPreprocessor, iFrameBuffer, NULL); +#endif + + // encode the frame + speex_encode_int(pState->pEncodeState, iFrameBuffer, &pState->sEncodeBits); + + // write the result to the output and get the number of bytes written + iNumBytes = speex_bits_write(&pState->sEncodeBits, (char *)pOutput, VOIPSPEEX_MAX_BYTES); + + // increment the pointers and the total number of bytes written + pInput += VOIPSPEEX_FRAME_SIZE; + pOutput += iNumBytes; + iNumBytesEncoded += iNumBytes; + } + + return(iNumBytesEncoded); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexDecodeBlock + + \Description + Decode Speex-encoded input to 16-bit linear PCM samples, and accumulate in the given output buffer. + + \Input *pState - pointer to decode state + \Input *pOut - pointer to output buffer + \Input *pInp - pointer to input buffer + \Input iInputBytes - size of input data + \Input iChannel - the decode channel for which we are decoding data + + \Output + int32_t - number of samples decoded + + \Version 04/05/2007 (cadam) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipSpeexDecodeBlock(VoipCodecRefT *pCodecState, int32_t *pOut, const unsigned char *pInp, int32_t iInputBytes, int32_t iChannel) +{ + int32_t iFrame, iFrameSize, iNumFrames, iSample; + VoipSpeexStateT *pState = (VoipSpeexStateT *)pCodecState; + int32_t *pOutput = pOut; + unsigned char *pInput = (unsigned char *)pInp; + spx_int16_t iFrameBuffer[VOIPSPEEX_FRAME_SIZE]; + + iFrameSize = VOIPSPEEX_COMP_SIZE; + iNumFrames = iInputBytes/iFrameSize; + if (iInputBytes == 0) + { + NetPrintf(("voipspeex: no data to decode\n")); + return(0); + } + if ((iInputBytes % iFrameSize) != 0) + { + NetPrintf(("voipspeex: speex decoder can only decode multiples of %d bytes (%d submitted)\n", iFrameSize, iInputBytes)); + return(0); + } + + NetPrintfVerbose((pState->iDebugLevel, 2, "voipspeex: decoding %d bytes (%d frames)\n", iInputBytes, iNumFrames)); + + for (iFrame = 0; iFrame < iNumFrames; iFrame++) + { + ds_memclr(iFrameBuffer, sizeof(iFrameBuffer)); + + // reset the SpeexBits + speex_bits_reset(&pState->pDecodeBits[iChannel]); + + // read the bits from the input + speex_bits_read_from(&pState->pDecodeBits[iChannel], (char *)pInput, iFrameSize); + + // decode the bits + speex_decode_int(pState->pDecoders[iChannel].pDecodeState, &pState->pDecodeBits[iChannel], iFrameBuffer); + + // convert the float buffer to an int32_t array for Speex + for (iSample = 0; iSample < VOIPSPEEX_FRAME_SIZE; iSample++) + { + pOutput[iSample] += (((int32_t)iFrameBuffer[iSample] * pState->iOutputVolume) >> VOIP_CODEC_OUTPUT_FRACTIONAL); + } + + // increment the pointers and the total number of bytes written + pInput += iFrameSize; + pOutput += VOIPSPEEX_FRAME_SIZE; + } + + return(iNumFrames * VOIPSPEEX_FRAME_SIZE); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexReset + + \Description + Resets codec state. + + \Input *pState - pointer to decode state + + \Version 04/05/2007 (cadam) +*/ +/*************************************************************************************************F*/ +static void _VoipSpeexReset(VoipCodecRefT *pCodecState) +{ + int32_t i; + + VoipSpeexStateT *pState = (VoipSpeexStateT *)pCodecState; + + // reset the SpeexBits + speex_bits_reset(&pState->sEncodeBits); + + // reset the encoder + speex_encoder_ctl(pState->pEncodeState, SPEEX_RESET_STATE, NULL); + + // reset the decoders + for (i = 0; i < pState->iNumChannels; i++) + { + speex_decoder_ctl(pState->pDecoders[i].pDecodeState, SPEEX_RESET_STATE, NULL); + } + + _VoipSpeexSetControls(&pState->CodecState); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexControl + + \Description + Modifies parameters of the codec + + \Input *pState - pointer to decode state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'plvl' - Set the output power level + 'spam' - Set debug output verbosity (debug only) + \endverbatim + + \Version 03/12/2008 (grouse) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipSpeexControl(VoipCodecRefT *pCodecState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + VoipSpeexStateT *pState = (VoipSpeexStateT *)pCodecState; + + if ((iControl == 'plvl') && (pState != NULL)) + { + pState->iOutputVolume = iValue; + return(0); + } + #if DIRTYCODE_LOGGING + if ((iControl == 'spam') && (pState != NULL)) + { + pState->iDebugLevel = iValue; + return(0); + } + #endif + NetPrintf(("voipspeex: unhandled control selector '%C'\n", iControl)); + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipSpeexStatus + + \Description + Get codec status + + \Input *pCodecState - pointer to decode state + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuffer - [out] storage for selector output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'fsiz' - size of encoder output / decoder input in bytes (iValue=samples per frame) + 'fsmp' - frame sample size for current codec + \endverbatim + + \Version 10/11/2011 (jbrookes) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipSpeexStatus(VoipCodecRefT *pCodecState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + VoipSpeexStateT *pModuleState = (VoipSpeexStateT *)pCodecState; + + // these options require module state + if (pModuleState != NULL) + { + if (iSelect == 'fsiz') + { + return((iValue/VOIPSPEEX_FRAME_SIZE) * VOIPSPEEX_COMP_SIZE); + } + if (iSelect == 'fsmp') + { + return(VOIPSPEEX_FRAME_SIZE); + } + } + NetPrintfVerbose((pModuleState->iDebugLevel, 1, "voipspeex: unhandled status selector '%C'\n", iSelect)); + return(-1); +} + +// speex function overrides +void _speex_fatal(const char *str, const char *file, int line) +{ + NetPrintf(("Fatal (internal) error in %s, line %d: %s\n", file, line, str )); +} + +void speex_warning(const char *str) +{ + NetPrintf(("libspeex: warning: %s\n", str)); +} + +void speex_warning_int(const char *str, int val) +{ + NetPrintf(("libspeex: warning: %s %d\n", str, val)); +} + +void speex_notify(const char *str) +{ + NetPrintf(("libspeex: notification: %s\n", str)); +} + +#endif // VOIPSPEEX_ENABLED + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commall.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commall.h new file mode 100644 index 00000000..2bf646b1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commall.h @@ -0,0 +1,337 @@ +/*H*************************************************************************************/ +/*! + \File commall.h + + \Description + This is the common communication header required for use with any + of the CommXXXX routines. It provides a unified calling structure + as well as unified status and error values. + + \Copyright + Copyright (c) Electronic Arts 2002 + + \Version 0.5 02/23/1999 (gschaefer) First Version + \Version 1.0 02/25/1999 (gschaefer) Alpha release + \Version 1.1 11/20/2002 (jbrookes) Added Send() flags parameter, protocol definitions. +*/ +/*************************************************************************************H*/ + +#ifndef _commall_h +#define _commall_h + +/*! +\Moduledef CommAll CommAll +\Modulemember Comm +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +#define COMM_OFFLINE 1 //!< status - offline +#define COMM_CONNECTING 2 //!< status - connecting +#define COMM_ONLINE 3 //!< status - online +#define COMM_FAILURE 4 //!< status - failure + +#define COMM_PENDING 1 //!< pending +#define COMM_NOERROR 0 //!< no error +#define COMM_BADPARM -1 //!< invalid parameter +#define COMM_BADSTATE -2 //!< invalid state +#define COMM_BADADDRESS -3 //!< invalid address +#define COMM_NORESOURCE -4 //!< no resources available +#define COMM_UNEXPECTED -5 //!< unexpected +#define COMM_MINBUFFER -6 //!< minbuffer +#define COMM_NODATA -7 //!< no data available +#define COMM_INPROGRESS -8 //!< operation in progress +#define COMM_PORTBOUND -9 //!< requested local port is already bound + +// start of protocol numbering +#define COMM_PROTO_TCP (1) //!< TCP protocol +#define COMM_PROTO_UDP (2) //!< UDP protocol +#define COMM_PROTO_SRP (3) //!< SRP protocol + +#define COMM_FLAGS_RELIABLE 0 //!< CommRef->Send() flag - use reliable transmission +#define COMM_FLAGS_UNRELIABLE 1 //!< CommRef->Send() flag - use unreliable transmission +#define COMM_FLAGS_BROADCAST 2 //!< CommRef->Send() flag - use unreliable broadcast transmission + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct CommRef CommRef; + +//! common reference type +struct CommRef +{ + //! construct the class + /*! + * note: initialized winsock for first class. also creates linked + * list of all current instances of the class and worker thread + * to do most udp stuff. + * + * entry: maxwid=max record width, maxinp/maxout=input/output packet buffer size + * exit: none + */ + CommRef* (*Construct)(int32_t maxwid, int32_t maxinp, int32_t maxout); + + //! destruct the class + /*! + * entry: none + * exit: none + */ + void (*Destroy)(CommRef *what); + + //! resolve an address + /*! + * entry: what=endpoint ref, addr=address, buffer=target buffer, buflen=length + * exit: <0=error, 0=in progress, >0=complete + */ + int32_t (*Resolve)(struct CommRef *what, const char *addr, char *buf, int32_t len, char iDiv); + + //! stop the resolver + /*! + * entry: what=endpoint ref + * exit: none + */ + void (*Unresolve)(CommRef *what); + + //! listen for a connection + /*! + * entry: addr=port to listen on (only :port portion used) + * exit: negative=error, zero=ok + */ + int32_t (*Listen)(CommRef *what, const char *addr); + + //! stop listening + /*! + * entry: none + * exit: negative=error, zero=ok + */ + int32_t (*Unlisten)(CommRef *what); + + //! initiate a connection to a peer + /*! + * note: does not currently perform dns translation + * + * entry: addr=address in ip-address:port form + * exit: negative=error, zero=ok + */ + int32_t (*Connect)(CommRef *what, const char *addr); + + //! terminate a connection + /*! + * entry: none + * exit: negative=error, zero=ok + */ + int32_t (*Unconnect)(CommRef *what); + + //! set event callback hook + /*! + * Note1: this is an optional callback which is called after new data has been + * received and buffered or at other times where the protocol state has + * significantly changed. It is called by a private thread so the routines it + * uses must be thread safe. It can be used to provide "life" to servers or + * other modules which use comm services. The code must not take too long to + * execute since it will impact comm performance if it does. + * + * Note2: By disabling and enabling this callback at specific times, it is + * possible to avoid threading concerns in the upper layer (i.e., if you + * disable the callback while the callback is executing, this call will block + * until the callback completes). + * + * entry: comm reference + * exit: none + */ + void (*Callback)(CommRef *what, void (*callback)(CommRef *ref, int32_t event)); + + //! return current stream status + /*! + * entry: none + * exit: CONNECTING, OFFLINE, ONLINE or FAILURE + */ + int32_t (*Status)(CommRef *what); + + //! control connection behavior (optional) + /*! + * see specific implementation for entry and exit parameter descriptions + */ + int32_t (*Control)(CommRef *what, int32_t iControl, int32_t iValue, void *pValue); + + //! return current clock tick + /*! + * entry: none + * exit: elapsed millisecs from some epoch + */ + uint32_t (*Tick)(CommRef *what); + + //! send a packet + /*! + * note: zero length packets may not be sent (they are ignored) + * + * entry: buffer=pointer to data, length=length of data, flags=COMM_FLAGS_* (see defines above) + * exit: negative=error, zero=ok + */ + int32_t (*Send)(CommRef *what, const void *buffer, int32_t length, uint32_t flags); + + //! peek at waiting packet + /*! + * entry: target=target buffer, length=buffer length, when=tick received at + * exit: negative=nothing pending, else packet length + */ + int32_t (*Peek)(CommRef *what, void *target, int32_t length, uint32_t *when); + + //! receive a packet from the buffer + /*! + * entry: target=target buffer, length=buffer length, what=tick received at + * exit: negative=error, else packet length + */ + int32_t (*Recv)(CommRef *what, void *target, int32_t length, uint32_t *when); + + //! send data callback hook + /*! + * Note: this is an optional callback which is called immediately before + * a packet is transmitted. Due to error handling, it may get called more + * than once for the same packet if the packet is sent more than once. + * + * entry: same as Send + * exit: none + */ + void (*SendCallback)(CommRef *what, void *buffer, int32_t length, uint32_t when); + + //! receive data callback hook + /*! + * Note: this is an optional callback which is called immediately after + * a packet is received. By the time this function is called, the packet + * is available for input via the Recv function. + * + * entry: same as Recv + * exit: none + */ + void (*RecvCallback)(CommRef *what, void *target, int32_t length, uint32_t when); + + //! module memory group + int32_t memgroup; + void *memgrpusrdata; + + //! user definable storage + int32_t refnum; + + //! user definable reference + void *refptr; + + //! socket ref + SocketT *sockptr; + + //! maximum packet width (read only) + uint16_t maxwid; + + //! maximum number of input packets buffered + uint8_t maxinp; + + //! maximum number of output packets buffered + uint8_t maxout; + + //! data transfer statistics (read only) + int32_t datasent; + + //! data transfer statistics (read only) + int32_t datarcvd; + + //! data transfer statistics (read only) + int32_t packsent; + + //! data transfer statistics (read only) + int32_t packrcvd; + + //! data transfer statistics (read only) + int32_t packlost; + + //! data tranfer statictics (read only) + uint32_t packsaved; // tracks the number of packers recovered by commudp redundancy mechanism + + //! data transfer statistics (read only) + int32_t naksent; + + //! data transfer statistics (read only) + int32_t overhead; // tracks the sent overhead including IP4 and UDP header + + //! data transfer statistics (read only) + int32_t rcvoverhead; // tracks the receive overhead including IP4 and UDP header + + //!< host ip address (read only) + uint32_t hostip; + + //!< peer ip address (read only) + uint32_t peerip; + + //!< host port (read only) + uint16_t hostport; + + //!< peer port (read only) + uint16_t peerport; + + //!< if packet was received (read only) + uint8_t bpackrcvd; + + uint8_t _pad[3]; +}; + +// typedef versions for discrete declarations +typedef CommRef *(CommAllConstructT)(int32_t maxwid, int32_t maxinp, int32_t maxout); +typedef void (CommAllDestroyT)(CommRef *what); +typedef int32_t (CommAllResolveT)(struct CommRef *what, const char *addr, char *buf, int32_t len, char iDiv); +typedef void (CommAllUnresolveT)(CommRef *what); +typedef int32_t (CommAllListenT)(CommRef *what, const char *addr); +typedef int32_t (CommAllUnlistenT)(CommRef *what); +typedef int32_t (CommAllConnectT)(CommRef *what, const char *addr); +typedef int32_t (CommAllUnconnectT)(CommRef *what); +typedef void (CommAllCallbackT)(CommRef *what, void (*callback)(CommRef *ref, int32_t event)); +typedef int32_t (CommAllStatusT)(CommRef *what); +typedef int32_t (CommAllControlT)(CommRef *what, int32_t iControl, int32_t iValue, void *pValue); +typedef uint32_t (CommAllTickT)(CommRef *what); +typedef int32_t (CommAllSendT)(CommRef *what, const void *buffer, int32_t length, uint32_t flags); +typedef int32_t (CommAllPeekT)(CommRef *what, void *target, int32_t length, uint32_t *when); +typedef int32_t (CommAllRecvT)(CommRef *what, void *target, int32_t length, uint32_t *when); +typedef void (CommAllSendCallbackT)(CommRef *what, const void *, int32_t length, uint32_t when); +typedef void (CommAllRecvCallbackT)(CommRef *what, void *target, int32_t length, uint32_t when); +typedef void (CommAllEventCallbackT)(CommRef *what, int32_t event); + + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +// known protocol constructors (these also appear in the .h file for the +// protocol in question, but are included here for convenience) -- see +// protocol .h file for more details + +/* + * Construct the class + * + * entry: maxwid=max record width, maxinp/maxout=input/output packet buffer size + * exit: none + */ + +#if 0 +//typedef struct CommUDPRef CommUDPRef; +CommRef *CommUDPConstruct(int32_t maxwid, int32_t maxinp, int32_t maxout); + +//typedef struct CommSRPRef CommSRPRef; +CommRef *CommSRPConstruct(int32_t maxwid, int32_t maxinp, int32_t maxout); +#endif + +//@} + +#endif // _commall_h + + + + + + + + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commsrp.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commsrp.h new file mode 100644 index 00000000..0922d3d4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commsrp.h @@ -0,0 +1,103 @@ +/*H*************************************************************************************/ +/*! + \File commsrp.h + + \Description + This is CommSRP (Selectively Reliable Protocol), a datagram packet-based + transport class. + + \Copyright + Copyright Electronic Arts 1999-2003 + + \Version 0.5 01/03/03 (jbrookes) Initial Version, based on CommTCP + \Version 0.7 01/07/03 (jbrookes) Working unreliable transport, based on CommUDP + \Version 0.8 01/08/03 (jbrookes) Working reliable transport. + \Version 0.9 02/09/03 (jbrookes) Added support for sending zero-byte packets, and fixed PS2 alignment issue. +*/ +/*************************************************************************************H*/ + +#ifndef _commsrp_h +#define _commsrp_h + +/*! +\Moduledef CommSRP CommSRP +\Modulemember Comm +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +// basic reference returned/used by all routines +typedef struct CommSRPRef CommSRPRef; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// construct the class +DIRTYCODE_API CommSRPRef *CommSRPConstruct(int32_t maxwid, int32_t maxinp, int32_t maxout); + +// destruct the class +DIRTYCODE_API void CommSRPDestroy(CommSRPRef *what); + +// resolve an address +DIRTYCODE_API int32_t CommSRPResolve(CommSRPRef *what, const char *addr, char *buf, int32_t len, char div); + +// stop the resolver +DIRTYCODE_API void CommSRPUnresolve(CommSRPRef *what); + +// listen for a connection +DIRTYCODE_API int32_t CommSRPListen(CommSRPRef *what, const char *addr); + +// stop listening +DIRTYCODE_API int32_t CommSRPUnlisten(CommSRPRef *what); + +// initiate a connection to a peer +DIRTYCODE_API int32_t CommSRPConnect(CommSRPRef *what, const char *addr); + +// terminate a connection +DIRTYCODE_API int32_t CommSRPUnconnect(CommSRPRef *what); + +// set event callback hook +DIRTYCODE_API void CommSRPCallback(CommSRPRef *what, void (*callback)(CommRef *ref, int32_t event)); + +// return current stream status +DIRTYCODE_API int32_t CommSRPStatus(CommSRPRef *what); + +// return current clock tick +DIRTYCODE_API uint32_t CommSRPTick(CommSRPRef *what); + +// send a packet +DIRTYCODE_API int32_t CommSRPSend(CommSRPRef *what, const void *buffer, int32_t length, uint32_t flags); + +// peek at waiting packet +DIRTYCODE_API int32_t CommSRPPeek(CommSRPRef *what, void *target, int32_t length, uint32_t *when); + +// receive a packet from the buffer +DIRTYCODE_API int32_t CommSRPRecv(CommSRPRef *what, void *target, int32_t length, uint32_t *when); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _commsrp_h + + + + + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commudp.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commudp.h new file mode 100644 index 00000000..85119f15 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commudp.h @@ -0,0 +1,120 @@ +/*H*************************************************************************************************/ +/*! + + \File commudp.h + + \Description + This is a simple UDP transport class which incorporations the notions of virtual + connections and error free transfer. The protocol is optimized for use in a real-time + environment where latency is more important than bandwidth. Overhead is low with + the protocol adding only 8 bytes per packet on top of that required by UDP itself. + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 1999-2003. ALL RIGHTS RESERVED. + + \Version 0.1 02/09/99 (GWS) First Version + \Version 0.2 02/14/99 (GWS) Revised and enhanced + \Version 0.5 02/14/99 (GWS) Alpha release + \Version 1.0 07/30/99 (GWS) Final release + \Version 2.0 10/27/99 (GWS) Revised to use winsock 1.1/2.0 + \Version 2.1 12/04/99 (GWS) Removed winsock 1.1 support + \Version 2.2 01/12/00 (GWS) Fixed receive tick bug + \Version 2.3 06/12/00 (GWS) Added fastack for low-latency nets + \Version 2.4 07/07/00 (GWS) Added firewall penetrator + \Version 3.0 12/04/00 (GWS) Reported to dirtysock + \Version 3.1 11/20/02 (JLB) Added Send() flags parameter + \Version 3.2 02/18/03 (JLB) Fixes for multiple connection support + \Version 3.3 05/06/03 (GWS) Allowed poke to come from any IP + \Version 3.4 09/02/03 (JLB) Added unreliable packet type + \Version 4.0 09/12/03 (JLB) Per-send optional unreliable transport + +*/ +/*************************************************************************************************H*/ + + +#ifndef _commudp_h +#define _commudp_h + +/*! +\Moduledef CommUDP CommUDP +\Modulemember Comm +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +// basic reference returned/used by all routines +typedef struct CommUDPRef CommUDPRef; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// construct the class +DIRTYCODE_API CommUDPRef *CommUDPConstruct(int32_t iMaxWid, int32_t iMaxInp, int32_t iMaxOut); + +// destruct the class +DIRTYCODE_API void CommUDPDestroy(CommUDPRef *pRef); + +// resolve an address +DIRTYCODE_API int32_t CommUDPResolve(CommUDPRef *pRef, const char *pAddr, char *pBuf, int32_t iLen, char cDiv); + +// resolve an address +DIRTYCODE_API void CommUDPUnresolve(CommUDPRef *pRef); + +// listen for a connection +DIRTYCODE_API int32_t CommUDPListen(CommUDPRef *pRef, const char *pAddr); + +// stop listening +DIRTYCODE_API int32_t CommUDPUnlisten(CommUDPRef *pRef); + +// initiate a connection to a peer +DIRTYCODE_API int32_t CommUDPConnect(CommUDPRef *pRef, const char *pAddr); + +// terminate a connection +DIRTYCODE_API int32_t CommUDPUnconnect(CommUDPRef *pRef); + +// set event callback hook +DIRTYCODE_API void CommUDPCallback(CommUDPRef *pRef, void (*pCallback)(void *pRef, int32_t iEvent)); + +// return current stream status +DIRTYCODE_API int32_t CommUDPStatus(CommUDPRef *pRef); + +// control connection behavior +DIRTYCODE_API int32_t CommUDPControl(CommUDPRef *pRef, int32_t iControl, int32_t iValue, void *pValue); + +// return current clock tick +DIRTYCODE_API uint32_t CommUDPTick(CommUDPRef *pRef); + +// send a packet +DIRTYCODE_API int32_t CommUDPSend(CommUDPRef *pRef, const void *pBuffer, int32_t iLength, uint32_t uFlags); + +// peek at waiting packet +DIRTYCODE_API int32_t CommUDPPeek(CommUDPRef *pRef, void *pTarget, int32_t iLength, uint32_t *pWhen); + +// receive a packet from the buffer +DIRTYCODE_API int32_t CommUDPRecv(CommUDPRef *pRef, void *pTarget, int32_t iLength, uint32_t *pWhen); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _commudp_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commudputil.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commudputil.h new file mode 100644 index 00000000..273ac1fe --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/comm/commudputil.h @@ -0,0 +1,92 @@ +/*H*************************************************************************************************/ +/*! + + \File commudputil.h + + \Description + CommUdp knowledge to be shared with gameserver implementation. + + \Copyright + Copyright (c) 2006-2017 Electronic Arts Inc. + + \Version 1.0 09/01/2017 (mclouatre) First Version + +*/ +/*************************************************************************************************H*/ + + +#ifndef _commudputil_h +#define _commudputil_h + +/*! +\Moduledef CommUDP CommUDP +\Modulemember Comm +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +//! define protocol packet types +enum { + COMMUDP_RAW_PACKET_INIT = 1, // initiate a connection + COMMUDP_RAW_PACKET_CONN, // confirm a connection + COMMUDP_RAW_PACKET_DISC, // terminate a connection + COMMUDP_RAW_PACKET_NAK, // force resend of lost data + COMMUDP_RAW_PACKET_POKE, // try and poke through firewall + + COMMUDP_RAW_PACKET_UNREL = 128, // unreliable packet send (must be power of two) + // 128-255 reserved for unreliable packet sequence + COMMUDP_RAW_PACKET_DATA = 256, // initial data packet sequence number (must be power of two) + + /* Width of the sequence window, can be anything provided + RAW_PACKET_DATA + RAW_PACKET_DATA_WINDOW + doesn't overlap the meta-data bits. */ + COMMUDP_RAW_PACKET_DATA_WINDOW = (1 << 24) - 256 +}; + +#define COMMUDP_RAW_METATYPE1_SIZE (8) +#define COMMUDP_SEQ_META_SHIFT (28 - 4) +#define COMMUDP_SEQ_MULTI_SHIFT (28) +#define COMMUDP_SEQ_MULTI_INC (1 << COMMUDP_SEQ_MULTI_SHIFT) +#define COMMUDP_SEQ_MASK (0x00ffffff) + + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Encodes a subpacket size field. +DIRTYCODE_API uint32_t CommUDPUtilEncodeSubpacketSize(uint8_t *pBuf, uint32_t uVal); + +// Decodes a subpacket size field (see _CommUDPEncodeSubpacketSize for notes) +DIRTYCODE_API uint32_t CommUDPUtilDecodeSubpacketSize(const uint8_t *pBuf, int32_t *pVal); + +// Extracts metatype from host-ordered commupd header seq field (RawUDPPacketT.body.uSeq) +DIRTYCODE_API uint32_t CommUDPUtilGetMetaTypeFromSeq(uint32_t uSeq); + +// Returns the metadata size for a given metadata type. +DIRTYCODE_API uint32_t CommUDPUtilGetMetaSize(uint32_t uMetaType); + +// Extracts the number of extra subpackets (excluding the main subpacket) from host - ordered commupd header seq field(RawUDPPacketT.body.uSeq) +DIRTYCODE_API uint32_t CommUDPUtilGetExtraSubPktCountFromSeq(uint32_t uSeq); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _commudputil_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptaes.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptaes.h new file mode 100644 index 00000000..0d530cce --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptaes.h @@ -0,0 +1,97 @@ +/*H********************************************************************************/ +/*! + \File cryptaes.h + + \Description + An implementation of the AES-128 and AES-256 cipher, based on the FIPS + standard, intended for use with the TLS AES cipher suites. + + This implementation deliberately uses the naming conventions from the + standard where possible in order to aid comprehension. + + \Notes + References: + FIPS197 standard: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + + \Copyright + Copyright (c) 2011 Electronic Arts + + \Version 01/20/2011 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _cryptaes_h +#define _cryptaes_h + +/*! +\Moduledef CryptAes CryptAes +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#define CRYPTAES_MAXROUNDS (14) + +#define CRYPTAES_KEYTYPE_ENCRYPT (0) +#define CRYPTAES_KEYTYPE_DECRYPT (1) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct CryptAesKeyScheduleT +{ + uint16_t uNumRounds; + uint16_t uKeyWords; + uint32_t aKeySchedule[(CRYPTAES_MAXROUNDS+1)*8]; +} CryptAesKeyScheduleT; + +// opaque module state +typedef struct CryptAesT CryptAesT; + +//! all fields are PRIVATE +struct CryptAesT +{ + CryptAesKeyScheduleT KeySchedule; + uint8_t aInitVec[16]; //!< initialization vector/CBC state +}; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// initialize state for AES encryption/decryption module +DIRTYCODE_API void CryptAesInit(CryptAesT *pAes, const uint8_t *pKeyBuf, int32_t iKeyLen, uint32_t uKeyType, const uint8_t *pInitVec); + +// encrypt data with the AES cipher in CBC mode +DIRTYCODE_API void CryptAesEncrypt(CryptAesT *pAes, uint8_t *pBuffer, int32_t iLength); + +// decrypt data with the AES cipher in CBC mode +DIRTYCODE_API void CryptAesDecrypt(CryptAesT *pAes, uint8_t *pBuffer, int32_t iLength); + +// encrypt a 16 byte data block with AES cipher +DIRTYCODE_API void CryptAesEncryptBlock(CryptAesKeyScheduleT *pKeySchedule, const uint8_t *pInput, uint8_t *pOutput); + +// decrypt a 16 byte data block with AES cipher +DIRTYCODE_API void CryptAesDecryptBlock(CryptAesKeyScheduleT *pKeySchedule, const uint8_t *pInput, uint8_t *pOutput); + +// initialize AES key schedule +DIRTYCODE_API void CryptAesInitKeySchedule(CryptAesKeyScheduleT *pKeySchedule, const uint8_t *pKeyBuf, int32_t iKeyLen, uint32_t uKeyType); + + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptaes_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptarc4.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptarc4.h new file mode 100644 index 00000000..f9bfb24f --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptarc4.h @@ -0,0 +1,102 @@ +/*H*************************************************************************************/ +/*! + \File cryptarc4.h + + \Description + This module is a from-scratch ARC4 implementation designed to avoid + any intellectual property complications. The ARC4 stream cipher is + known to produce output that is compatible with the RC4 stream cipher. + + \Notes + This algorithm from this cypher was taken from the web site: ciphersaber.gurus.com + It is based on the RC4 compatible algorithm that was published in the 2nd ed of + the book Applied Cryptography by Bruce Schneier. This is a private-key stream + cipher which means that some other secure way of exchanging cipher keys prior + to algorithm use is required. Its strength is directly related to the key exchange + algorithm strengh. In operation, each individual stream message must use a unique + key. This is handled by appending on 10 byte random value onto the private key. + This 10-byte data can be sent by public means to the receptor (or included at the + start of a file or whatever). When the private key is combined with this public + key, it essentially puts the cypher into a random starting state (it is like + using a message digest routine to generate a random hash for password comparison). + The code below was written from scratch using only a textual algorithm description. + + \Copyright + Copyright (c) Electronic Arts 2000-2002 + + \Version 1.0 02/25/2000 (gschaefer) First Version + \Version 1.1 11/06/2002 (jbrookes) Removed Create()/Destroy() to eliminate mem alloc dependencies. +*/ +/*************************************************************************************H*/ + +#ifndef _cryptarc4_h +#define _cryptarc4_h + +/*! +\Moduledef CryptArc4 CryptArc4 +\Modulemember Crypt +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct CryptArc4T CryptArc4T; + +//! all fields are PRIVATE +struct CryptArc4T +{ + uint8_t state[256]; + uint8_t walk; + uint8_t swap; +}; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create state for ARC4 encryption/decryption module. +DIRTYCODE_API void CryptArc4Init(CryptArc4T *pState, const unsigned char *pKeyBuf, int32_t iKeyLen, int32_t iIter); + +// apply the cipher to the data. calling twice undoes the uncryption +DIRTYCODE_API void CryptArc4Apply(CryptArc4T *pState, unsigned char *pBuffer, int32_t iLength); + +// advance the cipher state by iLength bytes +DIRTYCODE_API void CryptArc4Advance(CryptArc4T *pState, int32_t iLength); + +// encrypt an asciiz string, with a 7-bit asciiz string result +DIRTYCODE_API void CryptArc4StringEncrypt(char *pDst, int32_t iLen, const char *pSrc, const uint8_t *pKey, int32_t iKey, int32_t iIter); + +// decrypt an asciiz string, from a 7-bit asciiz encrypted string +DIRTYCODE_API void CryptArc4StringDecrypt(char *pDst, int32_t iLen, const char *pSrc, const uint8_t *pKey, int32_t iKey, int32_t iIter); + +#if DIRTYCODE_DEBUG + // encryption/decryption helper intended for use with static strings that need to be encrypted in the binary + #define CryptArc4StringEncryptStatic(_pStr, _iStrSize, _pKey, _iKeySize, _pStrSrc) CryptArc4StringEncryptStaticCode(_pStr, _iStrSize, _pKey, _iKeySize, _pStrSrc) +#else + // release version of string encrypt; does not pass source (plaintext) string + #define CryptArc4StringEncryptStatic(_pStr, _iStrSize, _pKey, _iKeySize, _pStrSrc) CryptArc4StringEncryptStaticCode(_pStr, _iStrSize, _pKey, _iKeySize, NULL) +#endif + +// encryption/decryption helper intended for use with static strings that need to be encrypted in the binary (do not call directly; use CryptArc4StringEncryptStatic() wrapper) +DIRTYCODE_API int32_t CryptArc4StringEncryptStaticCode(char *pStr, int32_t iStrSize, const uint8_t *pKey, int32_t iKeySize, const char *pStrSrc); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptarc4_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptbn.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptbn.h new file mode 100644 index 00000000..0000430b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptbn.h @@ -0,0 +1,156 @@ +/*H*************************************************************************************/ +/*! + \File cryptbn.h + + \Description + This module is implements big integer math needed for our cryptography + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. + + \Version 01/18/2017 (eesponda) First version split from CryptRSA +*/ +/*************************************************************************************H*/ + +#ifndef _cryptbn_h +#define _cryptbn_h + +/*! +\Moduledef CryptBn CryptBn +\Modulemember Crypt +*/ +//@{ + +/*** Include files ********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **************************************************************************/ + +// size of a each unit in the large number +#if defined(DIRTYCODE_PC) && DIRTYCODE_64BITPTR == 0 +#define UCRYPT_SIZE (4) +#else +#define UCRYPT_SIZE (8) +#endif +// bits per word +#define UCRYPT_BITSIZE (UCRYPT_SIZE*8) +// maximum bits of number (add one unit of wiggle room) +#define CRYPTBN_MAX_BITS (4096+UCRYPT_BITSIZE) +// maximum width of number +#define CRYPTBN_MAX_WIDTH (CRYPTBN_MAX_BITS/UCRYPT_BITSIZE) + +/*** Type Definitions *****************************************************************/ + +// define crypt types based on UCRYPT_SIZE +#if (UCRYPT_SIZE == 4) +typedef uint32_t ucrypt_t; +#else +typedef uint64_t ucrypt_t; +#endif + +//! big number state +typedef struct CryptBnT +{ + ucrypt_t aData[CRYPTBN_MAX_WIDTH]; + int32_t iWidth; + uint32_t uSign; +} CryptBnT; + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init big number state at specific width +DIRTYCODE_API void CryptBnInit(CryptBnT *pState, int32_t iWidth); + +// init big number state with an unsigned number +DIRTYCODE_API void CryptBnInitSet(CryptBnT *pState, uint32_t uValue); + +// init big number state from a buffer +DIRTYCODE_API int32_t CryptBnInitFrom(CryptBnT *pState, int32_t iWidth, const uint8_t *pSource, int32_t iLength); + +// init big number state from a buffer as little-endian +DIRTYCODE_API int32_t CryptBnInitLeFrom(CryptBnT *pState, const uint8_t *pSource, int32_t iLength); + +// left shift the big number by a bit +DIRTYCODE_API void CryptBnLeftShift(CryptBnT *pState); + +// left shift the big number by multiple bits +DIRTYCODE_API void CryptBnLeftShift2(CryptBnT *pState, int32_t iBits); + +// right shift the big number by a bit +#define CryptBnRightShift(pState) CryptBnRightShift2(pState, 1) + +// right shift the big number by multiple bits +DIRTYCODE_API void CryptBnRightShift2(CryptBnT *pState, int32_t iBits); + +// tests a bit in the big number and returns if it is set +DIRTYCODE_API uint8_t CryptBnBitTest(const CryptBnT *pState, int32_t iBit); + +// set a bit in the big number +DIRTYCODE_API void CryptBnBitSet(CryptBnT *pState, int32_t iBit); + +// bitwise and two big numbers +DIRTYCODE_API void CryptBnBitAnd(CryptBnT *pState, const CryptBnT *pLhs, const CryptBnT *pRhs); + +// bitwise xor two big numbers +DIRTYCODE_API void CryptBnBitXor(CryptBnT *pState, const CryptBnT *pLhs, const CryptBnT *pRhs); + +// returns bit length +DIRTYCODE_API int32_t CryptBnBitLen(const CryptBnT *pState); + +// returns byte length +DIRTYCODE_API int32_t CryptBnByteLen(const CryptBnT *pState); + +// add two big numbers over modulus +DIRTYCODE_API void CryptBnModAdd(CryptBnT *pState, const CryptBnT *pAdd1, const CryptBnT *pAdd2, const CryptBnT *pMod); + +// accumulate one big number into another +DIRTYCODE_API void CryptBnAccumulate(CryptBnT *pState, const CryptBnT *pAdd); + +// increment the number by 1 +DIRTYCODE_API void CryptBnIncrement(CryptBnT *pState); + +// subtract two big numbers +DIRTYCODE_API void CryptBnSubtract(CryptBnT *pState, const CryptBnT *pSub1, const CryptBnT *pSub2); + +// subtract the number by 1 +DIRTYCODE_API void CryptBnDecrement(CryptBnT *pState); + +// perform modular multiply operation +DIRTYCODE_API void CryptBnModMultiply(CryptBnT *pState, const CryptBnT *pMul1, const CryptBnT *pMul2, const CryptBnT *pMod); + +// multiplication using classical algorithm +DIRTYCODE_API void CryptBnMultiply(CryptBnT *pState, const CryptBnT *pMul1, const CryptBnT *pMul2); + +// does a modulus/divide operation using the classical method +DIRTYCODE_API void CryptBnMod(const CryptBnT *pDividend, const CryptBnT *pDivisor, CryptBnT *pRemainder, CryptBnT *pQuotient); + +// perform inverse modulo operation +DIRTYCODE_API void CryptBnInverseMod(CryptBnT *pState, const CryptBnT *pMod); + +// copy the big number data into output buffer +DIRTYCODE_API void CryptBnFinal(const CryptBnT *pState, uint8_t *pResult, int32_t iLength); + +// copy the bit number data into output buffer as little endian +DIRTYCODE_API void CryptBnFinalLe(const CryptBnT *pState, uint8_t *pResult, int32_t iLength); + +// copy a big number +DIRTYCODE_API void CryptBnClone(CryptBnT *pDst, const CryptBnT *pSrc); + +// print the big number to the log +DIRTYCODE_API void CryptBnPrint(const CryptBnT *pState, const char *pTitle); + +// compare two big numbers +DIRTYCODE_API int32_t CryptBnCompare(const CryptBnT *pLhs, const CryptBnT *pRhs); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptbn_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptchacha.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptchacha.h new file mode 100644 index 00000000..41cfedbd --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptchacha.h @@ -0,0 +1,65 @@ +/*H********************************************************************************/ +/*! + \File cryptchacha.h + + \Description + + \Copyright + Copyright (c) 2018 Electronic Arts + + \Version 02/12/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _cryptchacha_h +#define _cryptchacha_h + +/*! +\Moduledef CryptChaCha CryptChaCha +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// opaque module state +typedef struct CryptChaChaT CryptChaChaT; + +//! all fields are PRIVATE +struct CryptChaChaT +{ + uint32_t aInput[16]; +}; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// initialize state for ChaCha encryption/decryption module +DIRTYCODE_API void CryptChaChaInit(CryptChaChaT *pChaCha, const uint8_t *pKeyBuf, int32_t iKeyLen); + +// encrypt data with the ChaCha cipher +DIRTYCODE_API int32_t CryptChaChaEncrypt(CryptChaChaT *pChaCha, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen); + +// decrypt data with the ChaCha cipher +DIRTYCODE_API int32_t CryptChaChaDecrypt(CryptChaChaT *pChaCha, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, const uint8_t *pTag, int32_t iTagLen); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptgcm_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptcurve.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptcurve.h new file mode 100644 index 00000000..07469096 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptcurve.h @@ -0,0 +1,98 @@ +/*H********************************************************************************/ +/*! + \File cryptcurve.h + + \Description + This module implements an interface over our different curve crypto APIs + + \Copyright + Copyright (c) Electronic Arts 2018. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +#ifndef _cryptcurve_h +#define _cryptcurve_h + +/*! +\Moduledef CryptCurve CryptCurve +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptbn.h" +#include "DirtySDK/crypt/cryptdef.h" +#include "DirtySDK/crypt/cryptnist.h" + +/*** Defines **********************************************************************/ + +//! maximum state storage we need for diffie hellman +#define CRYPTCURVE_DH_MAXSTATE (sizeof(CryptNistDhT)) +//! maximum state storage we need for digital signature algorithm +#define CRYPTCURVE_DSA_MAXSTATE (sizeof(CryptNistDsaT)) + +/*** Type Definitions *************************************************************/ + +//! curve init function +typedef int32_t (CryptCurveInitT)(void *pState, int32_t iCurveType); + +//! curve generate public function +typedef int32_t (CryptCurvePublicT)(void *pState, CryptEccPointT *pResult, uint32_t *pCryptUsecs); + +//! curve generate secret function +typedef int32_t (CryptCurveSecretT)(void *pState, CryptEccPointT *pPublicKey, CryptEccPointT *pResult, uint32_t *pCryptUsecs); + +//! curve init point +typedef int32_t (CryptCurvePointInitT)(CryptEccPointT *pPoint, const uint8_t *pBuffer, int32_t iBufLen); + +//! curve output point to buffer (for dh cases) +typedef int32_t (CryptCurvePointFinalT)(void *pState, const CryptEccPointT *pPoint, uint8_t bSecret, uint8_t *pBuffer, int32_t iBufLen); + +//! curve generate dsa signature +typedef int32_t (CryptCurveSignT)(void *pState, const CryptBnT *pPrivateKey, const uint8_t *pHash, int32_t iHashSize, CryptEccPointT *pSignature, uint32_t *pCryptUsecs); + +//! curve verify dsa signature +typedef int32_t (CryptCurveVerifyT)(void *pState, CryptEccPointT *pPublicKey, const uint8_t *pHash, int32_t iHashSize, CryptEccPointT *pSignature, uint32_t *pCryptUsecs); + +//! interface for dh functionality +typedef struct CryptCurveDhT +{ + CryptCurveInitT *Init; + CryptCurvePublicT *Public; + CryptCurveSecretT *Secret; + CryptCurvePointInitT *PointInit; + CryptCurvePointFinalT *PointFinal; + int32_t iCurveType; +} CryptCurveDhT; + +//! interface for dsa functionality +typedef struct CryptCurveDsaT +{ + CryptCurveInitT *Init; + CryptCurveSignT *Sign; + CryptCurveVerifyT *Verify; + CryptCurvePointInitT *PointInit; + int32_t iCurveType; +} CryptCurveDsaT; + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// get dh reference and initializes based on curve type +DIRTYCODE_API const CryptCurveDhT *CryptCurveGetDh(int32_t iCurveType); + +// get dsa reference and initializes based on curve type +DIRTYCODE_API const CryptCurveDsaT *CryptCurveGetDsa(int32_t iCurveType); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptcurve_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptdef.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptdef.h new file mode 100644 index 00000000..b63f56ee --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptdef.h @@ -0,0 +1,89 @@ +/*H*******************************************************************/ +/*! + \File cryptdef.h + + \Description + Common definitions for Crypt modules + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 1.0 11/19/2013 (jbrookes) First Version +*/ +/*******************************************************************H*/ + +#ifndef _cryptdef_h +#define _cryptdef_h + +/*! +\Moduledef CryptDef CryptDef +\Modulemember Crypt +*/ +//@{ + +/*** Include files ***************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptbn.h" + +/*** Defines *********************************************************/ + +/*** Macros **********************************************************/ + +/*** Type Definitions ************************************************/ + +//! a binary large object, such as a modulus or exponent +typedef struct CryptBinaryObjT +{ + uint8_t *pObjData; + int32_t iObjSize; +} CryptBinaryObjT; + +//! used for the point calculations that are used for elliptic curve crypto +typedef struct CryptEccPointT +{ + CryptBnT X; + CryptBnT Y; +} CryptEccPointT; + +//! curve supported groups as per: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 +typedef enum CryptCurveE +{ + CRYPTCURVE_UNASSIGNED, + CRYPTCURVE_SECT163K1, + CRYPTCURVE_SECT163R1, + CRYPTCURVE_SECT163R2, + CRYPTCURVE_SECT193R1, + CRYPTCURVE_SECT193R2, + CRYPTCURVE_SECT233K1, + CRYPTCURVE_SECT233R1, + CRYPTCURVE_SECT239K1, + CRYPTCURVE_SECT283K1, + CRYPTCURVE_SECT283R1, + CRYPTCURVE_SECT409K1, + CRYPTCURVE_SECT409R1, + CRYPTCURVE_SECT571K1, + CRYPTCURVE_SECT571R1, + CRYPTCURVE_SECP160K1, + CRYPTCURVE_SECP160R1, + CRYPTCURVE_SECP160R2, + CRYPTCURVE_SECP192K1, + CRYPTCURVE_SECP192R1, + CRYPTCURVE_SECP224K1, + CRYPTCURVE_SECP224R1, + CRYPTCURVE_SECP256K1, + CRYPTCURVE_SECP256R1, + CRYPTCURVE_SECP384R1, + CRYPTCURVE_SECP521R1, + CRYPTCURVE_BRAINPOOLP256R1, + CRYPTCURVE_BRAINPOOLP384R1, + CRYPTCURVE_BRAINPOOLP512R1, + CRYPTCURVE_X25519, + CRYPTCURVE_X448, + CRYPTCURVE_MAX = CRYPTCURVE_X448 +} CryptCurveE; + +//@} + +#endif // _cryptdef_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptgcm.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptgcm.h new file mode 100644 index 00000000..6b65d2c3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptgcm.h @@ -0,0 +1,81 @@ +/*H********************************************************************************/ +/*! + \File cryptgcm.h + + \Description + An implementation of the GCM-128 and GCM-256 cipher, based on the NIST + standard, intended for use in implementation of TLS1.1 GCM cipher suites. + + This implementation uses Shoup's method utilizing 4-bit tables as described + in the GCM specifications. While not particularly fast, table generation + is quick and memory usage required small. This implementation is restricted + to a feature set suitable for implementation of TLS1.1 GCM cipher suites. + + \Copyright + Copyright (c) 2014 Electronic Arts + + \Version 07/01/2014 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _cryptgcm_h +#define _cryptgcm_h + +/*! +\Moduledef CryptGcm CryptGcm +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptaes.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// opaque module state +typedef struct CryptGcmT CryptGcmT; + +//! all fields are PRIVATE +struct CryptGcmT +{ + uint64_t HL[16]; //!< precalculated htable, low + uint64_t HH[16]; //!< precalculated htable, high + uint64_t uLen; //!< data length + uint64_t uAddLen; //!< additional data length + uint8_t aBaseEctr[16]; //!< first ECTR for tag + uint8_t aY[16]; //!< y scratch buffer + uint8_t aBuf[16]; //!< I/O scratch buffer + uint32_t uMode; //!< CRYPTAES_KETYPE_ENCRYPT or CRYPTAES_KEYTYPE_DECRYPT + CryptAesKeyScheduleT KeySchedule; //!< AES key schedule +}; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// initialize state for GCM encryption/decryption module +DIRTYCODE_API void CryptGcmInit(CryptGcmT *pGcm, const uint8_t *pKeyBuf, int32_t iKeyLen); + +// encrypt data with the GCM cipher +DIRTYCODE_API int32_t CryptGcmEncrypt(CryptGcmT *pGcm, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen); + +// decrypt data with the GCM cipher +DIRTYCODE_API int32_t CryptGcmDecrypt(CryptGcmT *pGcm, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptgcm_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/crypthash.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/crypthash.h new file mode 100644 index 00000000..cb9a66e6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/crypthash.h @@ -0,0 +1,98 @@ +/*H*******************************************************************/ +/*! + \File crypthash.h + + \Description + This module implements a generic wrapper for all cryptograph + hash modules. + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 1.0 11/05/2013 (jbrookes) First Version +*/ +/*******************************************************************H*/ + +#ifndef _crypthash_h +#define _crypthash_h + +/*! +\Moduledef CryptHash CryptHash +\Modulemember Crypt +*/ +//@{ + +/*** Include files ***************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptmd5.h" +#include "DirtySDK/crypt/cryptsha1.h" +#include "DirtySDK/crypt/cryptsha2.h" +#include "DirtySDK/util/murmurhash3.h" + +/*** Defines *********************************************************/ + +typedef enum CryptHashTypeE +{ + CRYPTHASH_NULL, + CRYPTHASH_MURMUR3, //!< murmerhash3; NOT cryto grade but VERY fast, use with caution + CRYPTHASH_MD5, //!< MD5; semi-obsolete, use with caution + CRYPTHASH_SHA1, //!< SHA1 + CRYPTHASH_SHA224, //!< SHA2-224 + CRYPTHASH_SHA256, //!< SHA2-256 + CRYPTHASH_SHA384, //!< SHA2-384 + CRYPTHASH_SHA512, //!< SHA2-512 + CRYPTHASH_NUMHASHES +} CryptHashTypeE; + +#define CRYPTHASH_MAXDIGEST (CRYPTSHA2_HASHSIZE_MAX) //!< SHA2 has the largest digest size +#define CRYPTHASH_MAXSTATE (sizeof(CryptSha2T)) //!< SHA2 has the largest state + +/*** Macros **********************************************************/ + +/*** Type Definitions ************************************************/ + +//! crypthash init func +typedef void (CryptHashInitT)(void *pState, int32_t iHashSize); + +//! crypthash update func +typedef void (CryptHashUpdateT)(void *pState, const uint8_t *pInput, uint32_t uInputLength); + +//! crypthash final func +typedef void (CryptHashFinalT)(void *pState, void *pBuffer, uint32_t uLength); + +//! crypthash function block +typedef struct CryptHashT +{ + CryptHashInitT *Init; + CryptHashUpdateT *Update; + CryptHashFinalT *Final; + CryptHashTypeE eHashType; + int32_t iHashSize; +} CryptHashT; + +/*** Variables *******************************************************/ + +/*** Functions *******************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +//!< get reference to hash function +DIRTYCODE_API const CryptHashT *CryptHashGet(CryptHashTypeE eHashType); + +//!< get hash size of specified hash function +DIRTYCODE_API int32_t CryptHashGetSize(CryptHashTypeE eHashType); + +//!< get hash type based on size of hash (excludes murmurhash3) +DIRTYCODE_API CryptHashTypeE CryptHashGetBySize(int32_t iHashSize); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _crypthash_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/crypthmac.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/crypthmac.h new file mode 100644 index 00000000..82b08e11 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/crypthmac.h @@ -0,0 +1,62 @@ +/*H********************************************************************************/ +/*! + \File crypthmac.h + + \Description + + \Notes + References: + + \Copyright + Copyright (c) 2013 Electronic Arts Inc. + + \Version 01/14/2013 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _crypthmac_h +#define _crypthmac_h + +/*! +\Moduledef CryptHmac CryptHmac +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct CryptHmacMsgT +{ + const uint8_t *pMessage; + int32_t iMessageLen; +} CryptHmacMsgT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// calculate HMAC message digest algorithm +DIRTYCODE_API int32_t CryptHmacCalc(uint8_t *pBuffer, int32_t iBufLen, const uint8_t *pMessage, int32_t iMessageLen, const uint8_t *pKey, int32_t iKeyLen, CryptHashTypeE eHashType); + +// calculate HMAC message digest algorithm; this version allows multiple buffers to allow for easy hashing of sparse messages +DIRTYCODE_API int32_t CryptHmacCalcMulti(uint8_t *pBuffer, int32_t iBufLen, const CryptHmacMsgT *pMessageList, int32_t iNumMessages, const uint8_t *pKey, int32_t iKeyLen, CryptHashTypeE eHashType); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptmd5.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptmd5.h new file mode 100644 index 00000000..6be3c90d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptmd5.h @@ -0,0 +1,137 @@ +/*H*************************************************************************************************/ +/*! + + \File cryptmd5.h + + \Description + The MD5 message digest algorithm developed by Ron Rivest and documented + in RFC1321. This implementation is based on the RFC but does not use the + sample code.. It should be free from intellectual property concerns and + a reference is included below which further clarifies this point. + + Note that this implementation is limited to hashing no more than 2^32 + bytes after which its results would be impatible with a fully compliant + implementation. + + \Notes + http://www.ietf.org/ietf/IPR/RSA-MD-all + + The following was recevied Fenbruary 23,2000 + From: "Linn, John" + + February 19, 2000 + + The purpose of this memo is to clarify the status of intellectual + property rights asserted by RSA Security Inc. ("RSA") in the MD2, MD4 and + MD5 message-digest algorithms, which are documented in RFC-1319, RFC-1320, + and RFC-1321 respectively. + + Implementations of these message-digest algorithms, including + implementations derived from the reference C code in RFC-1319, RFC-1320, and + RFC-1321, may be made, used, and sold without license from RSA for any + purpose. + + No rights other than the ones explicitly set forth above are + granted. Further, although RSA grants rights to implement certain + algorithms as defined by identified RFCs, including implementations derived + from the reference C code in those RFCs, no right to use, copy, sell, or + distribute any other implementations of the MD2, MD4, or MD5 message-digest + algorithms created, implemented, or distributed by RSA is hereby granted by + implication, estoppel, or otherwise. Parties interested in licensing + security components and toolkits written by RSA should contact the company + to discuss receiving a license. All other questions should be directed to + Margaret K. Seif, General Counsel, RSA Security Inc., 36 Crosby Drive, + Bedford, Massachusetts 01730. + + Implementations of the MD2, MD4, or MD5 algorithms may be subject to + United States laws and regulations controlling the export of technical data, + computer software, laboratory prototypes and other commodities (including + the Arms Export Control Act, as amended, and the Export Administration Act + of 1970). The transfer of certain technical data and commodities may + require a license from the cognizant agency of the United States Government. + RSA neither represents that a license shall not be required for a particular + implementation nor that, if required, one shall be issued. + + + DISCLAIMER: RSA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES + OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, VALIDITY OF + INTELLECTUAL PROPERTY RIGHTS, ISSUED OR PENDING, OR THE ABSENCE OF LATENT OR + OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE, IN CONNECTION WITH THE MD2, MD4, + OR MD5 ALGORITHMS. NOTHING IN THIS GRANT OF RIGHTS SHALL BE CONSTRUED AS A + REPRESENTATION OR WARRANTY GIVEN BY RSA THAT THE IMPLEMENTATION OF THE + ALGORITHM WILL NOT INFRINGE THE INTELLECTUAL PROPERTY RIGHTS OF ANY THIRD + PARTY. IN NO EVENT SHALL RSA, ITS TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, + PARENTS AND AFFILIATES BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES OF + ANY KIND RESULTING FROM IMPLEMENTATION OF THIS ALGORITHM, INCLUDING ECONOMIC + DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER RSA + SHALL BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 03/16/2001 (GWS) First Version +*/ +/*************************************************************************************************H*/ + +#ifndef _cryptmd5_h +#define _cryptmd5_h + +/*! +\Moduledef CryptMD5 CryptMD5 +\Modulemember Crypt +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +#define MD5_BINARY_OUT 16 //!< length of binary output +#define MD5_STRING_OUT 33 //!< length of string output + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct CryptMD5T CryptMD5T; + +//! all fields are PRIVATE +struct CryptMD5T +{ + unsigned char strData[64+8];//!< partial data block (8 for padding) + uint32_t uCount; //!< total byte count + uint32_t uRegs[4]; //!< the digest registers +}; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init the MD5 context. +DIRTYCODE_API void CryptMD5Init(CryptMD5T *pContext); + +// init the MD5 context (alternate form) +DIRTYCODE_API void CryptMD5Init2(CryptMD5T *pContext, int32_t iHashSize); + +// add data to the MD5 context (hash the data). +DIRTYCODE_API void CryptMD5Update(CryptMD5T *pContext, const void *pBuffer, int32_t iLength); + +// convert MD5 state into final output form +DIRTYCODE_API void CryptMD5Final(CryptMD5T *pContext, void *pBuffer, int32_t iLength); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptmd5_h + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptmont.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptmont.h new file mode 100644 index 00000000..2b899232 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptmont.h @@ -0,0 +1,91 @@ +/*H********************************************************************************/ +/*! + \File cryptmont.h + + \Description + This module implements the math for elliptic curve cryptography + using montgomery curves + + \Copyright + Copyright (c) Electronic Arts 2018. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +#ifndef _cryptmont_h +#define _cryptmont_h + +/*! +\Moduledef CryptMont CryptMont +\Modulemember Crypt +*/ +//@{ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptbn.h" +#include "DirtySDK/crypt/cryptdef.h" + +/*** Type Definitions **************************************************************/ + +typedef struct CryptMontT +{ + CryptBnT Prime; //!< curve prime + CryptBnT PrimeMin2; //!< curve prime - 2 used for exponentiation + CryptBnT BasePoint; //!< curve base point (only u) + CryptBnT A24; //!< constant used for calculations + CryptBnT PrivateKey; //!< private key + + //! working state + CryptBnT X_2; + CryptBnT X_3; + + CryptEccPointT Result; //!< cached result and z_2/z_3 during the point state + + int32_t iMemGroup; //!< memgroup id + void *pMemGroupUserdata; //!< memgroup userdata + CryptBnT *pTable; //!< table used for sliding window in exponentiation + + enum + { + CRYPTMONT_COMPUTE_POINT,//!< calculate the point + CRYPTMONT_COMPUTE_EXP //!< calculate the final result + } eState; //!< current state of the calculation + + int16_t iBitIndex; //!< bit index for the calculation (private key or exponent) + uint8_t bAccumulOne; //!< used to skip the first multiply + uint8_t uSwap; //!< current swap state + uint32_t uCryptUsecs; //!< total number of usecs the operation took + int32_t iCurveType; //!< what curve were we initialized with +} CryptMontT; + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init the curve state +DIRTYCODE_API int32_t CryptMontInit(CryptMontT *pState, int32_t iCurveType); + +// for testing-purposes; set the private key +DIRTYCODE_API void CryptMontSetPrivateKey(CryptMontT *pState, const uint8_t *pKey); + +// generates a public key based on the curve parameters: BasePoint * Secret +DIRTYCODE_API int32_t CryptMontPublic(CryptMontT *pState, CryptEccPointT *pResult, uint32_t *pCryptUsecs); + +// generate a shared secret based on the curve parameters and other parties public key: PublicKey * Secret +DIRTYCODE_API int32_t CryptMontSecret(CryptMontT *pState, CryptEccPointT *pPublicKey, CryptEccPointT *pResult, uint32_t *pCryptUsecs); + +// initialize a point on the curve from a buffer +DIRTYCODE_API int32_t CryptMontPointInitFrom(CryptEccPointT *pPoint, const uint8_t *pBuffer, int32_t iBufSize); + +// output a point to a buffer +DIRTYCODE_API int32_t CryptMontPointFinal(const CryptMontT *pState, const CryptEccPointT *pPoint, uint8_t bSecret, uint8_t *pBuffer, int32_t iBufSize); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptmont_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptnist.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptnist.h new file mode 100644 index 00000000..ba9d4508 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptnist.h @@ -0,0 +1,116 @@ +/*H********************************************************************************/ +/*! + \File cryptnist.h + + \Description + This module implements the math for elliptic curve cryptography + using curves in short Weierstrass form (NIST curves) + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +#ifndef _cryptnist_h +#define _cryptnist_h + +/*! +\Moduledef CryptNist CryptNist +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptbn.h" +#include "DirtySDK/crypt/cryptdef.h" + +/*** Defines **********************************************************************/ + +//! window size used for sliding window +#define CRYPTNIST_WINDOW_SIZE (5) + +//! precomputed table size based on the window size +#define CRYPTNIST_TABLE_SIZE (1 << (CRYPTNIST_WINDOW_SIZE - 1)) + +/*** Type Definitions *************************************************************/ + +//! private state that defines the curve +typedef struct CryptNistT +{ + CryptBnT Prime; //!< the finite field that the curve is defined over + CryptBnT CoefficientA; //!< cofficient that defines the curve + CryptBnT CoefficientB; //!< cofficient that defines the curve + CryptEccPointT BasePoint; //!< generator point on the curve + CryptBnT Order; //!< the number of point operations on the curve until the resultant line is vertical + + int32_t iMemGroup; //!< memgroup id + void *pMemGroupUserdata; //!< memgroup userdata + CryptEccPointT *pTable; //!< precomputed values used for for computation + int32_t iKeyIndex; //!< current bit index state when generating + int32_t iSize; //!< size of curve parameters + + uint32_t uCryptUSecs; //!< number of total usecs the operation took +} CryptNistT; + +//! state dealing with diffie hellmen key exchange +typedef struct CryptNistDhT +{ + CryptNistT Ecc; //!< base ecc state + + CryptBnT PrivateKey; //!< private key used to calculate public key and shared secret + CryptEccPointT Result; //!< working state +} CryptNistDhT; + +//! state dealing with dsa sign and verify +typedef struct CryptNistDsaT +{ + CryptNistT Ecc; //!< base ecc state + + CryptBnT K; //!< secret used for signing operations + CryptBnT U1; //!< used for verification operations + CryptBnT U2; //!< used for verification operations + CryptEccPointT Result; //!< working state +} CryptNistDsaT; + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// initializes the crypt state to perform diffie hellmen key exchange +DIRTYCODE_API int32_t CryptNistInitDh(CryptNistDhT *pState, int32_t iCurveType); + +// initializes the crypt state to perform dsa sign and verify +DIRTYCODE_API int32_t CryptNistInitDsa(CryptNistDsaT *pState, int32_t iCurveType); + +// generates a public key based on the curve parameters: BasePoint * Secret +DIRTYCODE_API int32_t CryptNistPublic(CryptNistDhT *pState, CryptEccPointT *pResult, uint32_t *pCryptUsecs); + +// generate a shared secret based on the curve parameters and other parties public key: PublicKey * Secret +DIRTYCODE_API int32_t CryptNistSecret(CryptNistDhT *pState, CryptEccPointT *pPublicKey, CryptEccPointT *pResult, uint32_t *pCryptUsecs); + +// generate a ecdsa signature +DIRTYCODE_API int32_t CryptNistSign(CryptNistDsaT *pState, const CryptBnT *pPrivateKey, const uint8_t *pHash, int32_t iHashSize, CryptEccPointT *pSignature, uint32_t *pCryptUsecs); + +// verify a ecdsa signature +DIRTYCODE_API int32_t CryptNistVerify(CryptNistDsaT *pState, CryptEccPointT *pPublicKey, const uint8_t *pHash, int32_t iHashSize, CryptEccPointT *pSignature, uint32_t *pCryptUsecs); + +// check if the point is on the curve +DIRTYCODE_API uint8_t CryptNistPointValidate(const CryptNistDhT *pState, const CryptEccPointT *pPoint); + +// initialize a point on the curve from a buffer +DIRTYCODE_API int32_t CryptNistPointInitFrom(CryptEccPointT *pPoint, const uint8_t *pBuffer, int32_t iBufLen); + +// output a point to a buffer (for dh, dsa encoding looks different which we do in protossl) +DIRTYCODE_API int32_t CryptNistPointFinal(const CryptNistDhT *pState, const CryptEccPointT *pPoint, uint8_t bSecret, uint8_t *pBuffer, int32_t iBufLen); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptnist_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptrand.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptrand.h new file mode 100644 index 00000000..63829811 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptrand.h @@ -0,0 +1,53 @@ +/*H********************************************************************************/ +/*! + \File cryptrand.h + + \Description + Cryptographic random number generator. Uses system-defined rng where + available. + + \Copyright + Copyright (c) 2012 Electronic Arts Inc. + + \Version 12/05/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _cryptrand_h +#define _cryptrand_h + +/*! +\Moduledef CryptRand CryptRand +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// get random data +DIRTYCODE_API void CryptRandGet(uint8_t *pBuffer, int32_t iBufSize); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptrand_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptrsa.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptrsa.h new file mode 100644 index 00000000..7eb9e001 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptrsa.h @@ -0,0 +1,138 @@ +/*H*************************************************************************************/ +/*! + \File cryptrsa.h + + \Description + This module is a from-scratch RSA implementation in order to avoid any + intellectual property issues. The 1024 bit RSA public key encryption algorithm + was implemented from a specification provided by Netscape for SSL implementation + (see protossl.h). + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 03/08/2002 (gschaefer) First Version (protossl) + \Version 11/18/2002 (jbrookes) Names changed to include "Proto" + \Version 03/05/2003 (jbrookes) Split RSA encryption from protossl +*/ +/*************************************************************************************H*/ + +#ifndef _cryptrsa_h +#define _cryptrsa_h + +/*! +\Moduledef CryptRSA CryptRSA +\Modulemember Crypt +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptbn.h" +#include "DirtySDK/crypt/cryptdef.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! union of our working state depending on the type of operations +typedef union CryptRSAWorkingT +{ + struct + { + CryptBnT Modulus; //!< public key modulus + CryptBnT Exponent; //!< public key exponent + + // working memory for modular exponentiation + CryptBnT Powerof; + CryptBnT Accumul; + } PublicKey; + + struct + { + /* selected private key state we use for chinese remainder theorum computation + we have access to the other data (modulus, exponent) but it is not used so + not worth storing */ + CryptBnT PrimeP; //!< prime factor p of modulus + CryptBnT PrimeQ; //!< prime factor q of modulus + CryptBnT ExponentP; //!< exponent dP mod p-1 + CryptBnT ExponentQ; //!< exponent dQ mod q-1 + CryptBnT Coeffecient; //!< inverse of q mod p + + // working memory for modular exponentiation + CryptBnT PowerofP; + CryptBnT PowerofQ; + CryptBnT M1; + CryptBnT M2; + + // current state of computation + enum + { + CRT_COMPUTE_M1, + CRT_COMPUTE_M2 + } eState; + } PrivateKey; +} CryptRSAWorkingT; + +//! cryptrsa state +typedef struct CryptRSAT +{ + CryptRSAWorkingT Working; //!< working state & data for doing modular exponentiation + + CryptBnT *pTable; //!< table used in sliding window for precomputed powers + CryptBnT aTable[1]; //!< fixed sized table used for public key operations which is our base case + + // memory allocation data + int32_t iMemGroup; + void *pMemGroupUserData; + + int32_t iKeyModSize; //!< size of public key modulus + uint8_t EncryptBlock[1024]; //!< input/output data + + int16_t iExpBitIndex; //!< bit index into the current exponent we are working on + uint8_t bAccumulOne; //!< can we skip the first multiply? + uint8_t bPrivate; //!< are we public or private key operation? + + // rsa profiling info + uint32_t uCryptMsecs; + uint32_t uCryptUsecs; + uint32_t uNumExpCalls; +} CryptRSAT; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init rsa state +DIRTYCODE_API int32_t CryptRSAInit(CryptRSAT *pState, const uint8_t *pModulus, int32_t iModSize, const uint8_t *pExponent, int32_t iExpSize); + +// init rsa state for private key data +DIRTYCODE_API int32_t CryptRSAInit2(CryptRSAT *pState, int32_t iModSize, const CryptBinaryObjT *pPrimeP, const CryptBinaryObjT *pPrimeQ, const CryptBinaryObjT *pExponentP, const CryptBinaryObjT *pExponentQ, const CryptBinaryObjT *pCoeffecient); + +// setup the master shared secret for encryption +DIRTYCODE_API void CryptRSAInitMaster(CryptRSAT *pState, const uint8_t *pMaster, int32_t iMasterLen); + +// setup the master shared secret for encryption using PKCS1.5 +DIRTYCODE_API void CryptRSAInitPrivate(CryptRSAT *pState, const uint8_t *pMaster, int32_t iMasterLen); + +// setup the encrypted signature for decryption. +DIRTYCODE_API void CryptRSAInitSignature(CryptRSAT *pState, const uint8_t *pSig, int32_t iSigLen); + +// do the encryption/decryption +DIRTYCODE_API int32_t CryptRSAEncrypt(CryptRSAT *pState, int32_t iIter); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptrsa_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptsha1.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptsha1.h new file mode 100644 index 00000000..1d7bd0e0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptsha1.h @@ -0,0 +1,84 @@ +/*H*******************************************************************/ +/*! + \File cryptsha1.h + + \Description + This module implements SHA1 as defined in RFC 3174. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Notes + The implementation is based on the algorithm description in sections + 3 through 6 of RFC 3174 and not on the C code in section 7. + + The code deliberately uses some of the naming conventions from + the RFC to in order to aid comprehension. + + This implementation is limited to hashing no more than 2^32-9 bytes. + It will silently produce the wrong result if an attempt is made to + hash more data. + + \Version 1.0 06/06/2004 (sbevan) First Version +*/ +/*******************************************************************H*/ + +#ifndef _cryptsha1_h +#define _cryptsha1_h + +/*! +\Moduledef CryptSha1 CryptSha1 +\Modulemember Crypt +*/ +//@{ + +/*** Include files ***************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines *********************************************************/ + +#define CRYPTSHA1_HASHSIZE (160/8) //!< length of SHA1 hash + +/*** Macros **********************************************************/ + +/*** Type Definitions ************************************************/ + +typedef struct CryptSha1T CryptSha1T; + +//! all fields are PRIVATE +struct CryptSha1T +{ + uint32_t uCount; //!< total length of hash data in bytes + uint32_t uPartialCount; //!< # bytes in the strData + uint32_t H[5]; //!< message digest + uint8_t strData[16*4]; //!< partial data block +}; + +/*** Variables *******************************************************/ + +/*** Functions *******************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init the SHA1 context. +DIRTYCODE_API void CryptSha1Init(CryptSha1T *pSha1); + +// init the SHA1 context (alternate form) +DIRTYCODE_API void CryptSha1Init2(CryptSha1T *pSha1, int32_t iHashSize); + +// add data to the SHA1 context (hash the data). +DIRTYCODE_API void CryptSha1Update(CryptSha1T *pSha1, const uint8_t *pInput, uint32_t uInputLength); + +// convert SHA1 state into final output form +DIRTYCODE_API void CryptSha1Final(CryptSha1T *pSha1, void *pBuffer, uint32_t uLength); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptsha2.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptsha2.h new file mode 100644 index 00000000..a69a81c4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/crypt/cryptsha2.h @@ -0,0 +1,87 @@ +/*H*******************************************************************/ +/*! + \File cryptsha2.h + + \Description + This module implements SHA2 as defined in RFC 6234, which is + itself based on FIPS 180-2. + + \Notes + This implementation is limited to hashing no more than 2^32-9 + bytes. It will silently produce the wrong result if an attempt + is made to hash more data. + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 1.0 11/04/2013 (jbrookes) First Version +*/ +/*******************************************************************H*/ + +#ifndef _cryptsha2_h +#define _cryptsha2_h + +/*! +\Moduledef CryptSha2 CryptSha2 +\Modulemember Crypt +*/ +//@{ + +/*** Include files ***************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines *********************************************************/ + +//! maximum hash result +#define CRYPTSHA224_HASHSIZE (28) +#define CRYPTSHA256_HASHSIZE (32) +#define CRYPTSHA384_HASHSIZE (48) +#define CRYPTSHA512_HASHSIZE (64) +#define CRYPTSHA2_HASHSIZE_MAX (CRYPTSHA512_HASHSIZE) + +/*** Macros **********************************************************/ + +/*** Type Definitions ************************************************/ + +//! private SHA2 state +typedef struct CryptSha2T +{ + uint32_t uCount; //!< total length of hash data in bytes + uint8_t uHashSize; //!< hash size + uint8_t uBlockSize; //!< 64/128 depending on mode + uint8_t uPartialCount; //!< # bytes in the partial data block + uint8_t _pad; + union + { + uint32_t H_32[8]; + uint64_t H_64[8]; + }TempHash; //!< temporary hash state + uint8_t strData[128]; //!< partial data block +} CryptSha2T; + +/*** Variables *******************************************************/ + +/*** Functions *******************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init the SHA2 context. +DIRTYCODE_API void CryptSha2Init(CryptSha2T *pSha2, int32_t iHashSize); + +// add data to the SHA2 context (hash the data). +DIRTYCODE_API void CryptSha2Update(CryptSha2T *pSha2, const uint8_t *pInput, uint32_t uInputLength); + +// convert SHA2 state into final output form +DIRTYCODE_API void CryptSha2Final(CryptSha2T *pSha2, uint8_t *pBuffer, uint32_t uLength); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _cryptsha2_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtydefs.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtydefs.h new file mode 100644 index 00000000..4b50f561 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtydefs.h @@ -0,0 +1,49 @@ +/*H********************************************************************************/ +/*! + + \File dirtydefs.h + + \Description: + DirtySock platform independent definitions and enumerations. + + [DEPRECATE] + + \Copyright + Copyright (c) Electronic Arts 1999-2005 + + \Version 1.0 02/03/99 (JLB) First Version + \Version 1.1 04/01/99 (MDB) Added Endian types + \Version 1.2 03/27/02 (GWS) Made NULL C++ friendly, added DIRTYCODE_IOP, CODE_UNIX + \Version 1.3 02/22/05 (JEF) Moved CODE_XXX to DIRTYCODE_XXX to avoid conflicts + \Version 1.4 03/22/05 (GWS) Replaced with include of platform.h +*/ +/********************************************************************************H*/ + +#ifndef _dirtydefs_h +#define _dirtydefs_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +// Microsoft Facilty codes when building a HRESTULT go up to 81, so we'll just start at 128 +// we have 11 bits to work with giving us a max value of 2047 +#define DIRTYAPI_SOCKET (128) +#define DIRTYAPI_PROTO_HTTP (129) +#define DIRTYAPI_PROTO_SSL (130) +#define DIRTYAPI_QOS (131) +#define DIRTYAPI_MAX (2047) + +#define DIRTYAPI_SOCKET_ERR_ALREADY_ACTIVE (-1) +#define DIRTYAPI_SOCKET_ERR_NO_MEMORY (-2) +#define DIRTYAPI_SOCKET_ERR_HOST_NAME_CACHE (-3) +#define DIRTYAPI_SOCKET_ERR_PLATFORM_SPECIFIC (-4) +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +#endif // _dirtydefs_h + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtylang.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtylang.h new file mode 100644 index 00000000..0799c438 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtylang.h @@ -0,0 +1,746 @@ +/*H********************************************************************************/ +/*! + \File dirtylang.h + + \Description + Country and Language Code Information + + \Notes + This module provides country and language codes for both client and + server code in DirtySDK. + 2-letter language codes are currently available here: + http://en.wikipedia.org/wiki/ISO_639 + 2-letter country codes are currently available here: + http://en.wikipedia.org/wiki/ISO_3166 + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 11/30/2004 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtylang_h +#define _dirtylang_h + +/*! +\Moduledef DirtyLang DirtyLang +\Modulemember DirtySock +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +//! Commonly used locality values +#define LOBBYAPI_LOCALITY_UNKNOWN ('zzZZ') +#define LOBBYAPI_LOCALITY_EN_US ('enUS') +#define LOBBYAPI_LOCALITY_BLANK ('\0\0\0\0') +#define LOBBYAPI_LOCALITY_WILDCARD ('****') + +#define LOBBYAPI_LOCALITY_DEFAULT LOBBYAPI_LOCALITY_EN_US +#define LOBBYAPI_LOCALITY_DEFAULT_STR "enUS" +#define LOBBYAPI_LOCALITY_UNKNOWN_STR "zzZZ" + +//! Non-specific, commonly used country and language codes +#define LOBBYAPI_LANGUAGE_UNKNOWN ('zz') +#define LOBBYAPI_COUNTRY_UNKNOWN ('ZZ') +#define LOBBYAPI_LANGUAGE_WILDCARD ('**') +#define LOBBYAPI_COUNTRY_WILDCARD ('**') +#define LOBBYAPI_LANGUAGE_UNKNOWN_STR ("zz") +#define LOBBYAPI_COUNTRY_UNKNOWN_STR ("ZZ") + +//! Languages +#define LOBBYAPI_LANGUAGE_AFAN_OROMO ('om') +#define LOBBYAPI_LANGUAGE_ABKHAZIAN ('ab') +#define LOBBYAPI_LANGUAGE_AFAR ('aa') +#define LOBBYAPI_LANGUAGE_AFRIKAANS ('af') +#define LOBBYAPI_LANGUAGE_ALBANIAN ('sq') +#define LOBBYAPI_LANGUAGE_AMHARIC ('am') +#define LOBBYAPI_LANGUAGE_ARABIC ('ar') +#define LOBBYAPI_LANGUAGE_ARMENIAN ('hy') +#define LOBBYAPI_LANGUAGE_ASSAMESE ('as') +#define LOBBYAPI_LANGUAGE_AYMARA ('ay') +#define LOBBYAPI_LANGUAGE_AZERBAIJANI ('az') +#define LOBBYAPI_LANGUAGE_BASHKIR ('ba') +#define LOBBYAPI_LANGUAGE_BASQUE ('eu') +#define LOBBYAPI_LANGUAGE_BENGALI ('bn') +#define LOBBYAPI_LANGUAGE_BHUTANI ('dz') +#define LOBBYAPI_LANGUAGE_BIHARI ('bh') +#define LOBBYAPI_LANGUAGE_BISLAMA ('bi') +#define LOBBYAPI_LANGUAGE_BRETON ('br') +#define LOBBYAPI_LANGUAGE_BULGARIAN ('bg') +#define LOBBYAPI_LANGUAGE_BURMESE ('my') +#define LOBBYAPI_LANGUAGE_BYELORUSSIAN ('be') +#define LOBBYAPI_LANGUAGE_CAMBODIAN ('km') +#define LOBBYAPI_LANGUAGE_CATALAN ('ca') +#define LOBBYAPI_LANGUAGE_CHINESE ('zh') +#define LOBBYAPI_LANGUAGE_CORSICAN ('co') +#define LOBBYAPI_LANGUAGE_CROATIAN ('hr') +#define LOBBYAPI_LANGUAGE_CZECH ('cs') +#define LOBBYAPI_LANGUAGE_DANISH ('da') +#define LOBBYAPI_LANGUAGE_DUTCH ('nl') +#define LOBBYAPI_LANGUAGE_ENGLISH ('en') +#define LOBBYAPI_LANGUAGE_ESPERANTO ('eo') +#define LOBBYAPI_LANGUAGE_ESTONIAN ('et') +#define LOBBYAPI_LANGUAGE_FAEROESE ('fo') +#define LOBBYAPI_LANGUAGE_FIJI ('fj') +#define LOBBYAPI_LANGUAGE_FINNISH ('fi') +#define LOBBYAPI_LANGUAGE_FRENCH ('fr') +#define LOBBYAPI_LANGUAGE_FRISIAN ('fy') +#define LOBBYAPI_LANGUAGE_GALICIAN ('gl') +#define LOBBYAPI_LANGUAGE_GEORGIAN ('ka') +#define LOBBYAPI_LANGUAGE_GERMAN ('de') +#define LOBBYAPI_LANGUAGE_GREEK ('el') +#define LOBBYAPI_LANGUAGE_GREENLANDIC ('kl') +#define LOBBYAPI_LANGUAGE_GUARANI ('gn') +#define LOBBYAPI_LANGUAGE_GUJARATI ('gu') +#define LOBBYAPI_LANGUAGE_HAUSA ('ha') +#define LOBBYAPI_LANGUAGE_HEBREW ('he') +#define LOBBYAPI_LANGUAGE_HINDI ('hi') +#define LOBBYAPI_LANGUAGE_HUNGARIAN ('hu') +#define LOBBYAPI_LANGUAGE_ICELANDIC ('is') +#define LOBBYAPI_LANGUAGE_INDONESIAN ('id') +#define LOBBYAPI_LANGUAGE_INTERLINGUA ('ia') +#define LOBBYAPI_LANGUAGE_INTERLINGUE ('ie') +#define LOBBYAPI_LANGUAGE_INUPIAK ('ik') +#define LOBBYAPI_LANGUAGE_INUKTITUT ('iu') +#define LOBBYAPI_LANGUAGE_IRISH ('ga') +#define LOBBYAPI_LANGUAGE_ITALIAN ('it') +#define LOBBYAPI_LANGUAGE_JAPANESE ('ja') +#define LOBBYAPI_LANGUAGE_JAVANESE ('jw') +#define LOBBYAPI_LANGUAGE_KANNADA ('kn') +#define LOBBYAPI_LANGUAGE_KASHMIRI ('ks') +#define LOBBYAPI_LANGUAGE_KAZAKH ('kk') +#define LOBBYAPI_LANGUAGE_KINYARWANDA ('rw') +#define LOBBYAPI_LANGUAGE_KIRGHIZ ('ky') +#define LOBBYAPI_LANGUAGE_KIRUNDI ('rn') +#define LOBBYAPI_LANGUAGE_KOREAN ('ko') +#define LOBBYAPI_LANGUAGE_KURDISH ('ku') +#define LOBBYAPI_LANGUAGE_LAOTHIAN ('lo') +#define LOBBYAPI_LANGUAGE_LATIN ('la') +#define LOBBYAPI_LANGUAGE_LATVIAN_LETTISH ('lv') +#define LOBBYAPI_LANGUAGE_LINGALA ('ln') +#define LOBBYAPI_LANGUAGE_LITHUANIAN ('lt') +#define LOBBYAPI_LANGUAGE_MACEDONIAN ('mk') +#define LOBBYAPI_LANGUAGE_MALAGASY ('mg') +#define LOBBYAPI_LANGUAGE_MALAY ('ms') +#define LOBBYAPI_LANGUAGE_MALAYALAM ('ml') +#define LOBBYAPI_LANGUAGE_MALTESE ('mt') +#define LOBBYAPI_LANGUAGE_MAORI ('mi') +#define LOBBYAPI_LANGUAGE_MARATHI ('mr') +#define LOBBYAPI_LANGUAGE_MOLDAVIAN ('mo') +#define LOBBYAPI_LANGUAGE_MONGOLIAN ('mn') +#define LOBBYAPI_LANGUAGE_NAURU ('na') +#define LOBBYAPI_LANGUAGE_NEPALI ('ne') +#define LOBBYAPI_LANGUAGE_NORWEGIAN ('no') +#define LOBBYAPI_LANGUAGE_OCCITAN ('oc') +#define LOBBYAPI_LANGUAGE_ORIYA ('or') +#define LOBBYAPI_LANGUAGE_PASHTO_PUSHTO ('ps') +#define LOBBYAPI_LANGUAGE_PERSIAN ('fa') +#define LOBBYAPI_LANGUAGE_POLISH ('pl') +#define LOBBYAPI_LANGUAGE_PORTUGUESE ('pt') +#define LOBBYAPI_LANGUAGE_PUNJABI ('pa') +#define LOBBYAPI_LANGUAGE_QUECHUA ('qu') +#define LOBBYAPI_LANGUAGE_RHAETO_ROMANCE ('rm') +#define LOBBYAPI_LANGUAGE_ROMANIAN ('ro') +#define LOBBYAPI_LANGUAGE_RUSSIAN ('ru') +#define LOBBYAPI_LANGUAGE_SAMOAN ('sm') +#define LOBBYAPI_LANGUAGE_SANGRO ('sg') +#define LOBBYAPI_LANGUAGE_SANSKRIT ('sa') +#define LOBBYAPI_LANGUAGE_SCOTS_GAELIC ('gd') +#define LOBBYAPI_LANGUAGE_SERBIAN ('sr') +#define LOBBYAPI_LANGUAGE_SERBO_CROATIAN ('sh') +#define LOBBYAPI_LANGUAGE_SESOTHO ('st') +#define LOBBYAPI_LANGUAGE_SETSWANA ('tn') +#define LOBBYAPI_LANGUAGE_SHONA ('sn') +#define LOBBYAPI_LANGUAGE_SINDHI ('sd') +#define LOBBYAPI_LANGUAGE_SINGHALESE ('si') +#define LOBBYAPI_LANGUAGE_SISWATI ('ss') +#define LOBBYAPI_LANGUAGE_SLOVAK ('sk') +#define LOBBYAPI_LANGUAGE_SLOVENIAN ('sl') +#define LOBBYAPI_LANGUAGE_SOMALI ('so') +#define LOBBYAPI_LANGUAGE_SPANISH ('es') +#define LOBBYAPI_LANGUAGE_SUDANESE ('su') +#define LOBBYAPI_LANGUAGE_SWAHILI ('sw') +#define LOBBYAPI_LANGUAGE_SWEDISH ('sv') +#define LOBBYAPI_LANGUAGE_TAGALOG ('tl') +#define LOBBYAPI_LANGUAGE_TAJIK ('tg') +#define LOBBYAPI_LANGUAGE_TAMIL ('ta') +#define LOBBYAPI_LANGUAGE_TATAR ('tt') +#define LOBBYAPI_LANGUAGE_TELUGU ('te') +#define LOBBYAPI_LANGUAGE_THAI ('th') +#define LOBBYAPI_LANGUAGE_TIBETAN ('bo') +#define LOBBYAPI_LANGUAGE_TIGRINYA ('ti') +#define LOBBYAPI_LANGUAGE_TONGA ('to') +#define LOBBYAPI_LANGUAGE_TSONGA ('ts') +#define LOBBYAPI_LANGUAGE_TURKISH ('tr') +#define LOBBYAPI_LANGUAGE_TURKMEN ('tk') +#define LOBBYAPI_LANGUAGE_TWI ('tw') +#define LOBBYAPI_LANGUAGE_UIGHUR ('ug') +#define LOBBYAPI_LANGUAGE_UKRAINIAN ('uk') +#define LOBBYAPI_LANGUAGE_URDU ('ur') +#define LOBBYAPI_LANGUAGE_UZBEK ('uz') +#define LOBBYAPI_LANGUAGE_VIETNAMESE ('vi') +#define LOBBYAPI_LANGUAGE_VOLAPUK ('vo') +#define LOBBYAPI_LANGUAGE_WELSH ('cy') +#define LOBBYAPI_LANGUAGE_WOLOF ('wo') +#define LOBBYAPI_LANGUAGE_XHOSA ('xh') +#define LOBBYAPI_LANGUAGE_YIDDISH ('yi') +#define LOBBYAPI_LANGUAGE_YORUBA ('yo') +#define LOBBYAPI_LANGUAGE_ZHUANG ('za') +#define LOBBYAPI_LANGUAGE_ZULU ('zu') + +// Languages: added on Mar-25-2011 according to ISO 639-1 +#define LOBBYAPI_LANGUAGE_BOSNIAN ('bs') +#define LOBBYAPI_LANGUAGE_DIVEHI ('dv') +#define LOBBYAPI_LANGUAGE_IGBO ('ig') +#define LOBBYAPI_LANGUAGE_LUXEMBOURGISH ('lb') +#define LOBBYAPI_LANGUAGE_YI ('ii') +#define LOBBYAPI_LANGUAGE_NORWEGIAN_BOKMAL ('nb') +#define LOBBYAPI_LANGUAGE_NORWEGIAN_NYNORSK ('nn') +#define LOBBYAPI_LANGUAGE_SAMI ('se') + +// Default language +#define LOBBYAPI_LANGUAGE_DEFAULT LOBBYAPI_LANGUAGE_ENGLISH +#define LOBBYAPI_LANGUAGE_DEFAULT_STR "en" + +//! Countries +#define LOBBYAPI_COUNTRY_AFGHANISTAN ('AF') +#define LOBBYAPI_COUNTRY_ALBANIA ('AL') +#define LOBBYAPI_COUNTRY_ALGERIA ('DZ') +#define LOBBYAPI_COUNTRY_AMERICAN_SAMOA ('AS') +#define LOBBYAPI_COUNTRY_ANDORRA ('AD') +#define LOBBYAPI_COUNTRY_ANGOLA ('AO') +#define LOBBYAPI_COUNTRY_ANGUILLA ('AI') +#define LOBBYAPI_COUNTRY_ANTARCTICA ('AQ') +#define LOBBYAPI_COUNTRY_ANTIGUA_BARBUDA ('AG') +#define LOBBYAPI_COUNTRY_ARGENTINA ('AR') +#define LOBBYAPI_COUNTRY_ARMENIA ('AM') +#define LOBBYAPI_COUNTRY_ARUBA ('AW') +#define LOBBYAPI_COUNTRY_AUSTRALIA ('AU') +#define LOBBYAPI_COUNTRY_AUSTRIA ('AT') +#define LOBBYAPI_COUNTRY_AZERBAIJAN ('AZ') +#define LOBBYAPI_COUNTRY_BAHAMAS ('BS') +#define LOBBYAPI_COUNTRY_BAHRAIN ('BH') +#define LOBBYAPI_COUNTRY_BANGLADESH ('BD') +#define LOBBYAPI_COUNTRY_BARBADOS ('BB') +#define LOBBYAPI_COUNTRY_BELARUS ('BY') +#define LOBBYAPI_COUNTRY_BELGIUM ('BE') +#define LOBBYAPI_COUNTRY_BELIZE ('BZ') +#define LOBBYAPI_COUNTRY_BENIN ('BJ') +#define LOBBYAPI_COUNTRY_BERMUDA ('BM') +#define LOBBYAPI_COUNTRY_BHUTAN ('BT') +#define LOBBYAPI_COUNTRY_BOLIVIA ('BO') +#define LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA ('BA') +#define LOBBYAPI_COUNTRY_BOTSWANA ('BW') +#define LOBBYAPI_COUNTRY_BOUVET_ISLAND ('BV') +#define LOBBYAPI_COUNTRY_BRAZIL ('BR') +#define LOBBYAPI_COUNTRY_BRITISH_INDIAN_OCEAN_TERRITORY ('IO') +#define LOBBYAPI_COUNTRY_BRUNEI_DARUSSALAM ('BN') +#define LOBBYAPI_COUNTRY_BULGARIA ('BG') +#define LOBBYAPI_COUNTRY_BURKINA_FASO ('BF') +#define LOBBYAPI_COUNTRY_BURUNDI ('BI') +#define LOBBYAPI_COUNTRY_CAMBODIA ('KH') +#define LOBBYAPI_COUNTRY_CAMEROON ('CM') +#define LOBBYAPI_COUNTRY_CANADA ('CA') +#define LOBBYAPI_COUNTRY_CAPE_VERDE ('CV') +#define LOBBYAPI_COUNTRY_CAYMAN_ISLANDS ('KY') +#define LOBBYAPI_COUNTRY_CENTRAL_AFRICAN_REPUBLIC ('CF') +#define LOBBYAPI_COUNTRY_CHAD ('TD') +#define LOBBYAPI_COUNTRY_CHILE ('CL') +#define LOBBYAPI_COUNTRY_CHINA ('CN') +#define LOBBYAPI_COUNTRY_CHRISTMAS_ISLAND ('CX') +#define LOBBYAPI_COUNTRY_COCOS_KEELING_ISLANDS ('CC') +#define LOBBYAPI_COUNTRY_COLOMBIA ('CO') +#define LOBBYAPI_COUNTRY_COMOROS ('KM') +#define LOBBYAPI_COUNTRY_CONGO ('CG') +#define LOBBYAPI_COUNTRY_COOK_ISLANDS ('CK') +#define LOBBYAPI_COUNTRY_COSTA_RICA ('CR') +#define LOBBYAPI_COUNTRY_COTE_DIVOIRE ('CI') +#define LOBBYAPI_COUNTRY_CROATIA ('HR') +#define LOBBYAPI_COUNTRY_CUBA ('CU') +#define LOBBYAPI_COUNTRY_CYPRUS ('CY') +#define LOBBYAPI_COUNTRY_CZECH_REPUBLIC ('CZ') +#define LOBBYAPI_COUNTRY_DENMARK ('DK') +#define LOBBYAPI_COUNTRY_DJIBOUTI ('DJ') +#define LOBBYAPI_COUNTRY_DOMINICA ('DM') +#define LOBBYAPI_COUNTRY_DOMINICAN_REPUBLIC ('DO') +#define LOBBYAPI_COUNTRY_EAST_TIMOR ('TP') +#define LOBBYAPI_COUNTRY_ECUADOR ('EC') +#define LOBBYAPI_COUNTRY_EGYPT ('EG') +#define LOBBYAPI_COUNTRY_EL_SALVADOR ('SV') +#define LOBBYAPI_COUNTRY_EQUATORIAL_GUINEA ('GQ') +#define LOBBYAPI_COUNTRY_ERITREA ('ER') +#define LOBBYAPI_COUNTRY_ESTONIA ('EE') +#define LOBBYAPI_COUNTRY_ETHIOPIA ('ET') +#define LOBBYAPI_COUNTRY_EUROPE_SSGFI_ONLY ('EU') +#define LOBBYAPI_COUNTRY_FALKLAND_ISLANDS ('FK') +#define LOBBYAPI_COUNTRY_FAEROE_ISLANDS ('FO') +#define LOBBYAPI_COUNTRY_FIJI ('FJ') +#define LOBBYAPI_COUNTRY_FINLAND ('FI') +#define LOBBYAPI_COUNTRY_FRANCE ('FR') +#define LOBBYAPI_COUNTRY_FRANCE_METROPOLITAN ('FX') +#define LOBBYAPI_COUNTRY_FRENCH_GUIANA ('GF') +#define LOBBYAPI_COUNTRY_FRENCH_POLYNESIA ('PF') +#define LOBBYAPI_COUNTRY_FRENCH_SOUTHERN_TERRITORIES ('TF') +#define LOBBYAPI_COUNTRY_GABON ('GA') +#define LOBBYAPI_COUNTRY_GAMBIA ('GM') +#define LOBBYAPI_COUNTRY_GEORGIA ('GE') +#define LOBBYAPI_COUNTRY_GERMANY ('DE') +#define LOBBYAPI_COUNTRY_GHANA ('GH') +#define LOBBYAPI_COUNTRY_GIBRALTAR ('GI') +#define LOBBYAPI_COUNTRY_GREECE ('GR') +#define LOBBYAPI_COUNTRY_GREENLAND ('GL') +#define LOBBYAPI_COUNTRY_GRENADA ('GD') +#define LOBBYAPI_COUNTRY_GUADELOUPE ('GP') +#define LOBBYAPI_COUNTRY_GUAM ('GU') +#define LOBBYAPI_COUNTRY_GUATEMALA ('GT') +#define LOBBYAPI_COUNTRY_GUINEA ('GN') +#define LOBBYAPI_COUNTRY_GUINEA_BISSAU ('GW') +#define LOBBYAPI_COUNTRY_GUYANA ('GY') +#define LOBBYAPI_COUNTRY_HAITI ('HT') +#define LOBBYAPI_COUNTRY_HEARD_AND_MC_DONALD_ISLANDS ('HM') +#define LOBBYAPI_COUNTRY_HONDURAS ('HN') +#define LOBBYAPI_COUNTRY_HONG_KONG ('HK') +#define LOBBYAPI_COUNTRY_HUNGARY ('HU') +#define LOBBYAPI_COUNTRY_ICELAND ('IS') +#define LOBBYAPI_COUNTRY_INDIA ('IN') +#define LOBBYAPI_COUNTRY_INDONESIA ('ID') +#define LOBBYAPI_COUNTRY_INTERNATIONAL_SSGFI_ONLY ('II') +#define LOBBYAPI_COUNTRY_IRAN ('IR') +#define LOBBYAPI_COUNTRY_IRAQ ('IQ') +#define LOBBYAPI_COUNTRY_IRELAND ('IE') +#define LOBBYAPI_COUNTRY_ISRAEL ('IL') +#define LOBBYAPI_COUNTRY_ITALY ('IT') +#define LOBBYAPI_COUNTRY_JAMAICA ('JM') +#define LOBBYAPI_COUNTRY_JAPAN ('JP') +#define LOBBYAPI_COUNTRY_JORDAN ('JO') +#define LOBBYAPI_COUNTRY_KAZAKHSTAN ('KZ') +#define LOBBYAPI_COUNTRY_KENYA ('KE') +#define LOBBYAPI_COUNTRY_KIRIBATI ('KI') +#define LOBBYAPI_COUNTRY_KOREA_DEMOCRATIC_PEOPLES_REPUBLIC_OF ('KP') +#define LOBBYAPI_COUNTRY_KOREA_REPUBLIC_OF ('KR') +#define LOBBYAPI_COUNTRY_KUWAIT ('KW') +#define LOBBYAPI_COUNTRY_KYRGYZSTAN ('KG') +#define LOBBYAPI_COUNTRY_LAO_PEOPLES_DEMOCRATIC_REPUBLIC ('LA') +#define LOBBYAPI_COUNTRY_LATVIA ('LV') +#define LOBBYAPI_COUNTRY_LEBANON ('LB') +#define LOBBYAPI_COUNTRY_LESOTHO ('LS') +#define LOBBYAPI_COUNTRY_LIBERIA ('LR') +#define LOBBYAPI_COUNTRY_LIBYAN_ARAB_JAMAHIRIYA ('LY') +#define LOBBYAPI_COUNTRY_LIECHTENSTEIN ('LI') +#define LOBBYAPI_COUNTRY_LITHUANIA ('LT') +#define LOBBYAPI_COUNTRY_LUXEMBOURG ('LU') +#define LOBBYAPI_COUNTRY_MACAU ('MO') +#define LOBBYAPI_COUNTRY_MACEDONIA_THE_FORMER_YUGOSLAV_REPUBLIC_OF ('MK') +#define LOBBYAPI_COUNTRY_MADAGASCAR ('MG') +#define LOBBYAPI_COUNTRY_MALAWI ('MW') +#define LOBBYAPI_COUNTRY_MALAYSIA ('MY') +#define LOBBYAPI_COUNTRY_MALDIVES ('MV') +#define LOBBYAPI_COUNTRY_MALI ('ML') +#define LOBBYAPI_COUNTRY_MALTA ('MT') +#define LOBBYAPI_COUNTRY_MARSHALL_ISLANDS ('MH') +#define LOBBYAPI_COUNTRY_MARTINIQUE ('MQ') +#define LOBBYAPI_COUNTRY_MAURITANIA ('MR') +#define LOBBYAPI_COUNTRY_MAURITIUS ('MU') +#define LOBBYAPI_COUNTRY_MAYOTTE ('YT') +#define LOBBYAPI_COUNTRY_MEXICO ('MX') +#define LOBBYAPI_COUNTRY_MICRONESIA_FEDERATED_STATES_OF ('FM') +#define LOBBYAPI_COUNTRY_MOLDOVA_REPUBLIC_OF ('MD') +#define LOBBYAPI_COUNTRY_MONACO ('MC') +#define LOBBYAPI_COUNTRY_MONGOLIA ('MN') +#define LOBBYAPI_COUNTRY_MONTSERRAT ('MS') +#define LOBBYAPI_COUNTRY_MOROCCO ('MA') +#define LOBBYAPI_COUNTRY_MOZAMBIQUE ('MZ') +#define LOBBYAPI_COUNTRY_MYANMAR ('MM') +#define LOBBYAPI_COUNTRY_NAMIBIA ('NA') +#define LOBBYAPI_COUNTRY_NAURU ('NR') +#define LOBBYAPI_COUNTRY_NEPAL ('NP') +#define LOBBYAPI_COUNTRY_NETHERLANDS ('NL') +#define LOBBYAPI_COUNTRY_NETHERLANDS_ANTILLES ('AN') +#define LOBBYAPI_COUNTRY_NEW_CALEDONIA ('NC') +#define LOBBYAPI_COUNTRY_NEW_ZEALAND ('NZ') +#define LOBBYAPI_COUNTRY_NICARAGUA ('NI') +#define LOBBYAPI_COUNTRY_NIGER ('NE') +#define LOBBYAPI_COUNTRY_NIGERIA ('NG') +#define LOBBYAPI_COUNTRY_NIUE ('NU') +#define LOBBYAPI_COUNTRY_NORFOLK_ISLAND ('NF') +#define LOBBYAPI_COUNTRY_NORTHERN_MARIANA_ISLANDS ('MP') +#define LOBBYAPI_COUNTRY_NORWAY ('NO') +#define LOBBYAPI_COUNTRY_OMAN ('OM') +#define LOBBYAPI_COUNTRY_PAKISTAN ('PK') +#define LOBBYAPI_COUNTRY_PALAU ('PW') +#define LOBBYAPI_COUNTRY_PANAMA ('PA') +#define LOBBYAPI_COUNTRY_PAPUA_NEW_GUINEA ('PG') +#define LOBBYAPI_COUNTRY_PARAGUAY ('PY') +#define LOBBYAPI_COUNTRY_PERU ('PE') +#define LOBBYAPI_COUNTRY_PHILIPPINES ('PH') +#define LOBBYAPI_COUNTRY_PITCAIRN ('PN') +#define LOBBYAPI_COUNTRY_POLAND ('PL') +#define LOBBYAPI_COUNTRY_PORTUGAL ('PT') +#define LOBBYAPI_COUNTRY_PUERTO_RICO ('PR') +#define LOBBYAPI_COUNTRY_QATAR ('QA') +#define LOBBYAPI_COUNTRY_REUNION ('RE') +#define LOBBYAPI_COUNTRY_ROMANIA ('RO') +#define LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION ('RU') +#define LOBBYAPI_COUNTRY_RWANDA ('RW') +#define LOBBYAPI_COUNTRY_SAINT_KITTS_AND_NEVIS ('KN') +#define LOBBYAPI_COUNTRY_SAINT_LUCIA ('LC') +#define LOBBYAPI_COUNTRY_SAINT_VINCENT_AND_THE_GRENADINES ('VC') +#define LOBBYAPI_COUNTRY_SAMOA ('WS') +#define LOBBYAPI_COUNTRY_SAN_MARINO ('SM') +#define LOBBYAPI_COUNTRY_SAO_TOME_AND_PRINCIPE ('ST') +#define LOBBYAPI_COUNTRY_SAUDI_ARABIA ('SA') +#define LOBBYAPI_COUNTRY_SENEGAL ('SN') +#define LOBBYAPI_COUNTRY_SEYCHELLES ('SC') +#define LOBBYAPI_COUNTRY_SIERRA_LEONE ('SL') +#define LOBBYAPI_COUNTRY_SINGAPORE ('SG') +#define LOBBYAPI_COUNTRY_SLOVAKIA ('SK') +#define LOBBYAPI_COUNTRY_SLOVENIA ('SI') +#define LOBBYAPI_COUNTRY_SOLOMON_ISLANDS ('SB') +#define LOBBYAPI_COUNTRY_SOMALIA ('SO') +#define LOBBYAPI_COUNTRY_SOUTH_AFRICA ('ZA') +#define LOBBYAPI_COUNTRY_SOUTH_GEORGIA_AND_THE_SOUTH_SANDWICH_ISLANDS ('GS') +#define LOBBYAPI_COUNTRY_SPAIN ('ES') +#define LOBBYAPI_COUNTRY_SRI_LANKA ('LK') +#define LOBBYAPI_COUNTRY_ST_HELENA_ASCENSION_AND_TRISTAN_DA_CUNHA ('SH') +#define LOBBYAPI_COUNTRY_ST_PIERRE_AND_MIQUELON ('PM') +#define LOBBYAPI_COUNTRY_SUDAN ('SD') +#define LOBBYAPI_COUNTRY_SURINAME ('SR') +#define LOBBYAPI_COUNTRY_SVALBARD_AND_JAN_MAYEN_ISLANDS ('SJ') +#define LOBBYAPI_COUNTRY_SWAZILAND ('SZ') +#define LOBBYAPI_COUNTRY_SWEDEN ('SE') +#define LOBBYAPI_COUNTRY_SWITZERLAND ('CH') +#define LOBBYAPI_COUNTRY_SYRIAN_ARAB_REPUBLIC ('SY') +#define LOBBYAPI_COUNTRY_TAIWAN ('TW') +#define LOBBYAPI_COUNTRY_TAJIKISTAN ('TJ') +#define LOBBYAPI_COUNTRY_TANZANIA_UNITED_REPUBLIC_OF ('TZ') +#define LOBBYAPI_COUNTRY_THAILAND ('TH') +#define LOBBYAPI_COUNTRY_TOGO ('TG') +#define LOBBYAPI_COUNTRY_TOKELAU ('TK') +#define LOBBYAPI_COUNTRY_TONGA ('TO') +#define LOBBYAPI_COUNTRY_TRINIDAD_AND_TOBAGO ('TT') +#define LOBBYAPI_COUNTRY_TUNISIA ('TN') +#define LOBBYAPI_COUNTRY_TURKEY ('TR') +#define LOBBYAPI_COUNTRY_TURKMENISTAN ('TM') +#define LOBBYAPI_COUNTRY_TURKS_AND_CAICOS_ISLANDS ('TC') +#define LOBBYAPI_COUNTRY_TUVALU ('TV') +#define LOBBYAPI_COUNTRY_UGANDA ('UG') +#define LOBBYAPI_COUNTRY_UKRAINE ('UA') +#define LOBBYAPI_COUNTRY_UNITED_ARAB_EMIRATES ('AE') +#define LOBBYAPI_COUNTRY_UNITED_KINGDOM ('GB') +#define LOBBYAPI_COUNTRY_UNITED_STATES ('US') +#define LOBBYAPI_COUNTRY_UNITED_STATES_MINOR_OUTLYING_ISLANDS ('UM') +#define LOBBYAPI_COUNTRY_URUGUAY ('UY') +#define LOBBYAPI_COUNTRY_UZBEKISTAN ('UZ') +#define LOBBYAPI_COUNTRY_VANUATU ('VU') +#define LOBBYAPI_COUNTRY_VATICAN_CITY_STATE ('VA') +#define LOBBYAPI_COUNTRY_VENEZUELA ('VE') +#define LOBBYAPI_COUNTRY_VIETNAM ('VN') +#define LOBBYAPI_COUNTRY_VIRGIN_ISLANDS_BRITISH ('VG') +#define LOBBYAPI_COUNTRY_VIRGIN_ISLANDS_US ('VI') +#define LOBBYAPI_COUNTRY_WALLIS_AND_FUTUNA_ISLANDS ('WF') +#define LOBBYAPI_COUNTRY_WESTERN_SAHARA ('EH') +#define LOBBYAPI_COUNTRY_YEMEN ('YE') +#define LOBBYAPI_COUNTRY_YUGOSLAVIA ('YU') +#define LOBBYAPI_COUNTRY_ZAIRE ('ZR') +#define LOBBYAPI_COUNTRY_ZAMBIA ('ZM') +#define LOBBYAPI_COUNTRY_ZIMBABWE ('ZW') + +// Countries: added on Mar-25-2011 +#define LOBBYAPI_COUNTRY_SERBIA_AND_MONTENEGRO ('CS') +#define LOBBYAPI_COUNTRY_MONTENEGRO ('ME') +#define LOBBYAPI_COUNTRY_SERBIA ('RS') +#define LOBBYAPI_COUNTRY_CONGO_DRC ('CD') +#define LOBBYAPI_COUNTRY_PALESTINIAN_TERRITORY ('PS') +#define LOBBYAPI_COUNTRY_GUERNSEY ('GG') +#define LOBBYAPI_COUNTRY_JERSEY ('JE') +#define LOBBYAPI_COUNTRY_ISLE_OF_MAN ('IM') +#define LOBBYAPI_COUNTRY_TIMOR_LESTE ('TL') + +// Default country +#define LOBBYAPI_COUNTRY_DEFAULT LOBBYAPI_COUNTRY_UNITED_STATES +#define LOBBYAPI_COUNTRY_DEFAULT_STR "US" + + +//! Currencies (ISO-4217) +#define LOBBYAPI_CURRENCY_UNITED_ARAB_EMIRATS_DIRHAM ('AED') +#define LOBBYAPI_CURRENCY_AFGHAN_AFGHANI ('AFN') +#define LOBBYAPI_CURRENCY_ALBANIAN_LEK ('ALL') +#define LOBBYAPI_CURRENCY_ARMENIAN_DRAM ('AMD') +#define LOBBYAPI_CURRENCY_NETHERLANDS_ANTILLEAN_GUILDER ('ANG') +#define LOBBYAPI_CURRENCY_ANGOLAN_KWANZA ('AOA') +#define LOBBYAPI_CURRENCY_ARGENTINE_PESO ('ARS') +#define LOBBYAPI_CURRENCY_AUSTRALIAN_DOLLAR ('AUD') +#define LOBBYAPI_CURRENCY_ARUBAN_FLORIN ('AWG') +#define LOBBYAPI_CURRENCY_AZERBAIJANI_PMANAT ('AZN') +#define LOBBYAPI_CURRENCY_BOSNIA_AND_HERZEGOVINA_CONVERTIBLE_MARK ('BAM') +#define LOBBYAPI_CURRENCY_BARBADOS_DOLLAR ('BBD') +#define LOBBYAPI_CURRENCY_BANGLADESHI_TAKA ('BDT') +#define LOBBYAPI_CURRENCY_BULGARIAN_LEV ('BGN') +#define LOBBYAPI_CURRENCY_BAHRAINI_DINAR ('BHD') +#define LOBBYAPI_CURRENCY_BURUNDIAN_FRANC ('BIF') +#define LOBBYAPI_CURRENCY_BERMUDIAN_DOLLAR ('BMD') +#define LOBBYAPI_CURRENCY_BRUNEI_DOLLAR ('BND') +#define LOBBYAPI_CURRENCY_BOLIVIANO ('BOB') +#define LOBBYAPI_CURRENCY_BOLIVIAN_MVDOL ('BOV') +#define LOBBYAPI_CURRENCY_BRAZILIAN_REAL ('BRL') +#define LOBBYAPI_CURRENCY_BAHAMIAN_DOLLAR ('BSD') +#define LOBBYAPI_CURRENCY_BHUTANESE_NGULTRUM ('BTN') +#define LOBBYAPI_CURRENCY_BOTSWANA_PULA ('BWP') +#define LOBBYAPI_CURRENCY_BELARUSIAN_RUBLE ('BYR') +#define LOBBYAPI_CURRENCY_BELIZE_DOLLAR ('BZD') +#define LOBBYAPI_CURRENCY_CANADIAN_DOLLAR ('CAD') +#define LOBBYAPI_CURRENCY_CONGOLESE_FRANC ('CDF') +#define LOBBYAPI_CURRENCY_WIR_EURO ('CHE') +#define LOBBYAPI_CURRENCY_SWISS_FRANC ('CHF') +#define LOBBYAPI_CURRENCY_WIR_FRANC ('CHW') +#define LOBBYAPI_CURRENCY_UNIDAD_DE_FOMENTO ('CLF') +#define LOBBYAPI_CURRENCY_CHILEAN_PESO ('CLP') +#define LOBBYAPI_CURRENCY_CHINESE_YUAN ('CNY') +#define LOBBYAPI_CURRENCY_COLOBIAN_PESO ('COP') +#define LOBBYAPI_CURRENCY_UNIDAD_DE_VALOR_REAL ('COU') +#define LOBBYAPI_CURRENCY_COSTA_RICAN_COLON ('CRC') +#define LOBBYAPI_CURRENCY_CUBAN_CONVERTIBLE_PESO ('CUC') +#define LOBBYAPI_CURRENCY_CUBAN_PESO ('CUP') +#define LOBBYAPI_CURRENCY_CAP_VERDE_ESCUDO ('CVE') +#define LOBBYAPI_CURRENCY_CZECH_KORUNA ('CZK') +#define LOBBYAPI_CURRENCY_DJIBOUTIAN_FRANC ('DJF') +#define LOBBYAPI_CURRENCY_DANISH_KRONE ('DKK') +#define LOBBYAPI_CURRENCY_DOMINICAN_PESO ('DOP') +#define LOBBYAPI_CURRENCY_ALGERIAN_DINAR ('DZD') +#define LOBBYAPI_CURRENCY_EGYPTIAN_POUND ('EGP') +#define LOBBYAPI_CURRENCY_ERITREAN_NAKFA ('ERN') +#define LOBBYAPI_CURRENCY_ETHIOPIAN_BIRR ('ETB') +#define LOBBYAPI_CURRENCY_EURO ('EUR') +#define LOBBYAPI_CURRENCY_FIJI_DOLLAR ('FJD') +#define LOBBYAPI_CURRENCY_FALKLAND_ISLANDS_POUND ('FKP') +#define LOBBYAPI_CURRENCY_POUND_STERLING ('GBP') +#define LOBBYAPI_CURRENCY_GEORGIAN_LARI ('GEL') +#define LOBBYAPI_CURRENCY_GHANAIAN_CEDI ('GHS') +#define LOBBYAPI_CURRENCY_GIBRALTAR_POUND ('GIP') +#define LOBBYAPI_CURRENCY_GAMBIAN_DALASI ('GMD') +#define LOBBYAPI_CURRENCY_GUINEAN_FRANC ('GNF') +#define LOBBYAPI_CURRENCY_GUATEMALAN_QUETZAL ('GTQ') +#define LOBBYAPI_CURRENCY_GUYANESE_DOLLAR ('GYD') +#define LOBBYAPI_CURRENCY_HONG_KONG_DOLLAR ('HKD') +#define LOBBYAPI_CURRENCY_HONDURAN_LEMPIRA ('HNL') +#define LOBBYAPI_CURRENCY_CROATIAN_KUNA ('HRK') +#define LOBBYAPI_CURRENCY_HAITIAN_GOURDE ('HTG') +#define LOBBYAPI_CURRENCY_HUNGARIAN_FORINT ('HUF') +#define LOBBYAPI_CURRENCY_INDONESIAN_RUPIAH ('IDR') +#define LOBBYAPI_CURRENCY_ISRAELI_NEW_SHEQEL ('ILS') +#define LOBBYAPI_CURRENCY_INDIAN_RUPEE ('INR') +#define LOBBYAPI_CURRENCY_IRAQI_DINAR ('IQD') +#define LOBBYAPI_CURRENCY_IRANIAN_RIAL ('IRR') +#define LOBBYAPI_CURRENCY_ICELANDIC_KRONA ('ISK') +#define LOBBYAPI_CURRENCY_JAMAICAN_DOLLAR ('JMD') +#define LOBBYAPI_CURRENCY_JORDANIAN_DINAR ('JOD') +#define LOBBYAPI_CURRENCY_JAPANESE_YEN ('JPY') +#define LOBBYAPI_CURRENCY_KENYAN_SHILLING ('KES') +#define LOBBYAPI_CURRENCY_KYRGYZSTANI_SOM ('KGS') +#define LOBBYAPI_CURRENCY_CAMBODIAN_RIEL ('KHR') +#define LOBBYAPI_CURRENCY_COMORO_FRANC ('KMF') +#define LOBBYAPI_CURRENCY_NORTH_KOREAN_WON ('KPW') +#define LOBBYAPI_CURRENCY_SOUTH_KOREAN_WON ('KRW') +#define LOBBYAPI_CURRENCY_KUWAITI_DINAR ('KWD') +#define LOBBYAPI_CURRENCY_CAYMAN_ISLANDS_DOLLAR ('KYD') +#define LOBBYAPI_CURRENCY_KAZAKHSTANI_TENGE ('KZT') +#define LOBBYAPI_CURRENCY_LAO_KIP ('LAK') +#define LOBBYAPI_CURRENCY_LEBANESE_POUND ('LBP') +#define LOBBYAPI_CURRENCY_SRI_LANKAN_RUPEE ('LKR') +#define LOBBYAPI_CURRENCY_LIBERIAN_DOLLAR ('LRD') +#define LOBBYAPI_CURRENCY_LESOTHO_LOTI ('LSL') +#define LOBBYAPI_CURRENCY_LITHUANIAN_LITAS ('LTL') +#define LOBBYAPI_CURRENCY_LATVIAN_LATS ('LVL') +#define LOBBYAPI_CURRENCY_LYBIAN_DINAR ('LYD') +#define LOBBYAPI_CURRENCY_MOROCCAN_DIRHAM ('MAD') +#define LOBBYAPI_CURRENCY_MOLDOVAN_LEU ('MDL') +#define LOBBYAPI_CURRENCY_MALAGASY_ARIARY ('MGA') +#define LOBBYAPI_CURRENCY_MACEDONIAN_DENAR ('MKD') +#define LOBBYAPI_CURRENCY_MYANMA_KYAT ('MMK') +#define LOBBYAPI_CURRENCY_MONGOLIAN_TUGRIK ('MNT') +#define LOBBYAPI_CURRENCY_MACANESE_PATACA ('MOP') +#define LOBBYAPI_CURRENCY_MAURITANIAN_OUGUIYA ('MRO') +#define LOBBYAPI_CURRENCY_MAURITIAN_RUPEE ('MUR') +#define LOBBYAPI_CURRENCY_MALDIVIAN_RUFIYAA ('MVR') +#define LOBBYAPI_CURRENCY_MALAWAIAN_KWACHA ('MWK') +#define LOBBYAPI_CURRENCY_MEXICAN_PESO ('MXN') +#define LOBBYAPI_CURRENCY_MEXICAN_UNIDAD_DE_INVERSION ('MXV') +#define LOBBYAPI_CURRENCY_MALAYSIAN_RINGGIT ('MYR') +#define LOBBYAPI_CURRENCY_MOZAMBICAN_METICAL ('MZN') +#define LOBBYAPI_CURRENCY_NAMIBIAN_DOLLAR ('NAD') +#define LOBBYAPI_CURRENCY_NIGERIAN_NAIRA ('NGN') +#define LOBBYAPI_CURRENCY_NICARAGUAN_CORDOBA ('NIO') +#define LOBBYAPI_CURRENCY_NORVEGIAN_KRONE ('NOK') +#define LOBBYAPI_CURRENCY_NEPALESE_RUPEE ('NPR') +#define LOBBYAPI_CURRENCY_NEW_ZEALAND_DOLLAR ('NZD') +#define LOBBYAPI_CURRENCY_OMANI_RIAL ('OMR') +#define LOBBYAPI_CURRENCY_PANAMANIAN_BALBOA ('PAB') +#define LOBBYAPI_CURRENCY_PERUVIAN_NUEVO_SOL ('PEN') +#define LOBBYAPI_CURRENCY_PAPUA_NEW_GUINEAN_KINA ('PGK') +#define LOBBYAPI_CURRENCY_PHILIPPINE_PESO ('PHP') +#define LOBBYAPI_CURRENCY_PAKISTANI_RUPEE ('PKR') +#define LOBBYAPI_CURRENCY_POLISH_ZLOTY ('PLN') +#define LOBBYAPI_CURRENCY_PARAGUAYAN_GUARANI ('PYG') +#define LOBBYAPI_CURRENCY_QATARI_RIAL ('QAR') +#define LOBBYAPI_CURRENCY_ROMANIAN_NEW_LEU ('RON') +#define LOBBYAPI_CURRENCY_SERBIAN_DINAR ('RSD') +#define LOBBYAPI_CURRENCY_RUSSIAN_RUBLE ('RUB') +#define LOBBYAPI_CURRENCY_RWANDAN_FRANC ('RWF') +#define LOBBYAPI_CURRENCY_SAUDI_RIYAL ('SAR') +#define LOBBYAPI_CURRENCY_SOLOMON_ISLANDS_DOLLAR ('SBD') +#define LOBBYAPI_CURRENCY_SEYCHELLES_RUPEE ('SRC') +#define LOBBYAPI_CURRENCY_SUDANESE_POUND ('SDG') +#define LOBBYAPI_CURRENCY_SWEDISH_KRONA ('SEK') +#define LOBBYAPI_CURRENCY_SINGAPORE_DOLLAR ('SGD') +#define LOBBYAPI_CURRENCY_SAINT_HELENA_POUND ('SHP') +#define LOBBYAPI_CURRENCY_SIERRA_LEONEAN_LEONE ('SLL') +#define LOBBYAPI_CURRENCY_SOMALI_SHILLING ('SOS') +#define LOBBYAPI_CURRENCY_SURINAMESE_DOLLAR ('SRD') +#define LOBBYAPI_CURRENCY_SOUTH_SUDANESE_POUND ('SSP') +#define LOBBYAPI_CURRENCY_SAO_TOME_AND_PRINCIPE_DOBRA ('STD') +#define LOBBYAPI_CURRENCY_SYRIAN_POUND ('SYP') +#define LOBBYAPI_CURRENCY_SWAZI_LILANGENI ('SZL') +#define LOBBYAPI_CURRENCY_THAI_BAHT ('THB') +#define LOBBYAPI_CURRENCY_TAJIKISTANI_SOMONI ('TJS') +#define LOBBYAPI_CURRENCY_TURKMENISTANI_MANAT ('TMT') +#define LOBBYAPI_CURRENCY_TUNISIAN_DINAR ('TND') +#define LOBBYAPI_CURRENCY_TONGAN_PAANGA ('TOP') +#define LOBBYAPI_CURRENCY_TURKISH_LIRA ('TRY') +#define LOBBYAPI_CURRENCY_TRINIDAD_AND_TOBAGO_DOLLAR ('TTD') +#define LOBBYAPI_CURRENCY_NEW_TAIWAN_DOLLAR ('TWD') +#define LOBBYAPI_CURRENCY_TANZANIAN_SHILLING ('TZS') +#define LOBBYAPI_CURRENCY_UKRAINIAN_HRYVNIA ('UAH') +#define LOBBYAPI_CURRENCY_UGANDAN_SHILLING ('UGX') +#define LOBBYAPI_CURRENCY_UNITED_STATES_DOLLAR ('USD') +#define LOBBYAPI_CURRENCY_UNITED_STATES_DOLLAR_NEXT_DAY ('USN') +#define LOBBYAPI_CURRENCY_UNITED_STATES_DOLLAR_SAME_DAY ('USS') +#define LOBBYAPI_CURRENCY_URUGUAY_PESO_EN_UNIDADES_INDEXADAS ('UYI') +#define LOBBYAPI_CURRENCY_URUGUAYAN_PESO ('UYU') +#define LOBBYAPI_CURRENCY_UZBEKISTAN_SOM ('UZS') +#define LOBBYAPI_CURRENCY_VENEZUELAN_BOLIVAR_FUERTE ('VEF') +#define LOBBYAPI_CURRENCY_VIETNAMESE_DONG ('VND') +#define LOBBYAPI_CURRENCY_VANUATU_VATU ('VUV') +#define LOBBYAPI_CURRENCY_SAMOAN_TALA ('WST') +#define LOBBYAPI_CURRENCY_CFA_FRANC_BEAC ('XAF') +#define LOBBYAPI_CURRENCY_EAST_CARABBEAN_DOLLAR ('XCD') +#define LOBBYAPI_CURRENCY_CFA_FRANC_BCEAO ('XOF') +#define LOBBYAPI_CURRENCY_CFP_FRANC ('XPF') +#define LOBBYAPI_CURRENCY_YEMENI_RIAL ('YER') +#define LOBBYAPI_CURRENCY_SOUTH_AFRICAN_RAND ('ZAR') +#define LOBBYAPI_CURRENCY_ZAMBIAN_KWACHA ('ZMK') +#define LOBBYAPI_CURRENCY_ZIMBABWE_DOLLAR ('ZWL') + +// Default currency +#define LOBBYAPI_CURRENCY_DEFAULT LOBBYAPI_CURRENCY_UNITED_STATES_DOLLAR +#define LOBBYAPI_CURRENCY_DEFAULT_STR "USD" + + +/*** Macros ***********************************************************************/ + +//! Write the currency code to a character string +#define LOBBYAPI_CreateCurrencyString(strOutstring, uCurrency) \ + { \ + (strOutstring)[0] = (char)(((uCurrency) >> 16 ) & 0xFF); \ + (strOutstring)[1] = (char)(((uCurrency) >> 8) & 0xFF); \ + (strOutstring)[2] = (char)((uCurrency) & 0xFF); \ + (strOutstring)[3]='\0'; \ + } + +//! toupper replacement +#define LOBBYAPI_LocalizerTokenToUpper(uCharToModify) \ + ((((unsigned char)(uCharToModify) >= 'a') && ((unsigned char)(uCharToModify) <= 'z')) ? \ + (((unsigned char)(uCharToModify)) & (~32)) : \ + (uCharToModify)) + +//! tolower replacement +#define LOBBYAPI_LocalizerTokenToLower(uCharToModify) \ + ((((unsigned char)(uCharToModify) >= 'A') && ((unsigned char)(uCharToModify) <= 'Z')) ? \ + (((unsigned char)(uCharToModify)) | (32)) : \ + (uCharToModify)) + +//! Create a localizer token from shorts representing country and language +#define LOBBYAPI_LocalizerTokenCreate(uLanguage, uCountry) \ + (((LOBBYAPI_LocalizerTokenShortToLower(uLanguage)) << 16) + (LOBBYAPI_LocalizerTokenShortToUpper(uCountry))) + +//! Create a localizer token from strings containing country and language +#define LOBBYAPI_LocalizerTokenCreateFromStrings(strLanguage, strCountry) \ + (LOBBYAPI_LocalizerTokenCreate(LOBBYAPI_LocalizerTokenGetShortFromString(strLanguage),LOBBYAPI_LocalizerTokenGetShortFromString(strCountry))) + +//! Create a localizer token from a single string (language + country - ex: "enUS") +#define LOBBYAPI_LocalizerTokenCreateFromString(strLocality) \ + (LOBBYAPI_LocalizerTokenCreateFromStrings(&(strLocality)[0], &(strLocality)[2])) + +//! Get a int16_t integer from a string +#define LOBBYAPI_LocalizerTokenGetShortFromString(strInstring) (( (((unsigned char*)(strInstring) == NULL) || ((unsigned char*)strInstring)[0] == '\0')) ? \ + ((uint16_t)(0)) : \ + ((uint16_t)((((unsigned char*)strInstring)[0] << 8) | ((unsigned char*)strInstring)[1]))) + +//! Pull the country (int16_t) from a localizer token (int32_t) +#define LOBBYAPI_LocalizerTokenGetCountry(uToken) ((uint16_t)((uToken) & 0xFFFF)) + +//! Pull the language (int16_t) from a localizer token (int32_t) +#define LOBBYAPI_LocalizerTokenGetLanguage(uToken) ((uint16_t)(((uToken) >> 16) & 0xFFFF)) + +//! Replace the country in a locality value +#define LOBBYAPI_LocalizerTokenSetCountry(uToken, uCountry) (uToken) = (((uToken) & 0xFFFF0000) | (uCountry)); + +//! Replace the language in a locality value +#define LOBBYAPI_LocalizerTokenSetLanguage(uToken, uLanguage) (uToken) = (((uToken) & 0x0000FFFF) | ((uLanguage) << 16)); + +//! Write the country contained in a localizer token to a character string +#define LOBBYAPI_LocalizerTokenCreateCountryString(strOutstring, uToken) \ + { \ + (strOutstring)[0] = (char)(((uToken) >> 8) & 0xFF); \ + (strOutstring)[1] = (char)((uToken) & 0xFF); \ + (strOutstring)[2]='\0'; \ + } + + +//! Write the language contained in a localizer token to a character string +#define LOBBYAPI_LocalizerTokenCreateLanguageString(strOutstring, uToken) \ + { \ + (strOutstring)[0]=(char)(((uToken) >> 24) & 0xFF); \ + (strOutstring)[1]=(char)(((uToken) >> 16) & 0xFF); \ + (strOutstring)[2]='\0'; \ + } + +//! Write the entire locality string to a character string +#define LOBBYAPI_LocalizerTokenCreateLocalityString(strOutstring, uToken) \ + { \ + (strOutstring)[0]=(char)(((uToken) >> 24) & 0xFF); \ + (strOutstring)[1]=(char)(((uToken) >> 16) & 0xFF); \ + (strOutstring)[2]=(char)(((uToken) >> 8) & 0xFF); \ + (strOutstring)[3]=(char)((uToken) & 0xFF); \ + (strOutstring)[4]='\0'; \ + } + +//! Macro to provide an easy way to display the token in character format +#define LOBBYAPI_LocalizerTokenPrintCharArray(uToken) \ + (char)(((uToken)>>24)&0xFF), (char)(((uToken)>>16)&0xFF), (char)(((uToken)>>8)&0xFF), (char)((uToken)&0xFF) + +//! Provide a way to capitalize the elements in a int16_t +#define LOBBYAPI_LocalizerTokenShortToUpper(uShort) \ + ((uint16_t)(((LOBBYAPI_LocalizerTokenToUpper(((uShort) >> 8) & 0xFF)) << 8) + \ + (LOBBYAPI_LocalizerTokenToUpper((uShort) & 0xFF)))) + +//! Provide a way to lowercase the elements in a int16_t +#define LOBBYAPI_LocalizerTokenShortToLower(uShort) \ + ((uint16_t)(((LOBBYAPI_LocalizerTokenToLower(((uShort) >> 8) & 0xFF)) << 8) + \ + (LOBBYAPI_LocalizerTokenToLower((uShort) & 0xFF)))) + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +//@} + +#endif // _dirtylang_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock.h new file mode 100644 index 00000000..e844e870 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock.h @@ -0,0 +1,44 @@ +/*H*************************************************************************************/ +/*! + \File dirtysock.h + + \Description + Platform independent interface to network layers. Based on + BSD sockets, but with performance modifications. Allows truly + portable modules to be written and moved to different platforms + needing only different support wrappers (no change to actual + network modes). + + \Copyright + Copyright (c) Electronic Arts 2001-2014 + + \Version 1.0 08/01/2001 (gschaefer) First Version +*/ +/*************************************************************************************H*/ + +#ifndef _dirtysock_h +#define _dirtysock_h + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +#ifndef DIRTYSOCK +#define DIRTYSOCK (TRUE) +#include "DirtySDK/dirtydefs.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#endif + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#endif // _dirtysock_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyaddr.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyaddr.h new file mode 100644 index 00000000..66c88cd4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyaddr.h @@ -0,0 +1,98 @@ +/*H********************************************************************************/ +/*! + \File dirtyaddr.h + + \Description + Definition for portable address type. + + \Copyright + Copyright (c) Electronic Arts 2004 + + \Version 1.0 04/07/2004 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtyaddr_h +#define _dirtyaddr_h + +/*! +\Moduledef DirtyAddr DirtyAddr +\Modulemember DirtySock +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#if defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) +/* In production, when an xboxone is located behind an OpenWRT based router that has + an IPv6 connection, the router defaults to assigning ULA prefixes via SLAAC and + DHCPv6. This results in the console having a global IPv6 address, a link local + IPv6 address, 2 ULA IPv6 addresses, and an IPv4 address. In such a scenario, + the Secure Device Address of the console is large enough that it cannot fit in a + 127-byte DirtyAddrT (size used on other platforms). + + After checking with MS, we got a confirmation that the size of a + SecureDevicAddress will never exceed 300 bytes. (enforced both in the + Windows::Networking::XboxLive and the Windows::Xbox::Networking namespaces). + + A call to DirtyAddrSetInfoXboxOne() for a 300-byte SecureDeviceAddress blob + results in 370 bytes being written in the DirtyAddrT. Consequently, it is safe + to make the size DIRTYADDR_MACHINEADDR_MAXLEN 372 on xboxone. +*/ +#define DIRTYADDR_MACHINEADDR_MAXLEN (372) +#else +#define DIRTYADDR_MACHINEADDR_MAXLEN (127) +#endif +#define DIRTYADDR_MACHINEADDR_MAXSIZE (DIRTYADDR_MACHINEADDR_MAXLEN + 1) + +/*** Macros ***********************************************************************/ + +//! compare two opaque addresses for equality (same=TRUE, different=FALSE) +#define DirtyAddrCompare(_pAddr1, _pAddr2) (!strcmp((_pAddr1)->strMachineAddr, (_pAddr2)->strMachineAddr)) + +/*** Type Definitions *************************************************************/ + +//! opaque address type +typedef struct DirtyAddrT +{ + char strMachineAddr[DIRTYADDR_MACHINEADDR_MAXSIZE]; +} DirtyAddrT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +//! convert a DirtyAddrT to native format +DIRTYCODE_API uint32_t DirtyAddrToHostAddr(void *pOutput, int32_t iBufLen, const DirtyAddrT *pAddr); + +//! convert a host-format address to native format +DIRTYCODE_API uint32_t DirtyAddrFromHostAddr(DirtyAddrT *pAddr, const void *pInput); + +//! get local address in DirtyAddr form +DIRTYCODE_API uint32_t DirtyAddrGetLocalAddr(DirtyAddrT *pAddr); + +#if defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) +//! get Xbox One extended info into dirtyaddr +DIRTYCODE_API uint8_t DirtyAddrGetInfoXboxOne(const DirtyAddrT *pDirtyAddr, void *pXuid, void *pSecureDeviceAddressBlob, int32_t *pBlobSize); + +//! set Xbox One extended info into dirtyaddr +DIRTYCODE_API void DirtyAddrSetInfoXboxOne(DirtyAddrT *pDirtyAddr, const void *pXuid, const void *pSecureDeviceAddressBlob, int32_t iBlobSize); + +#endif + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtyaddr_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtycert.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtycert.h new file mode 100644 index 00000000..840ff490 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtycert.h @@ -0,0 +1,77 @@ +/*H********************************************************************************/ +/*! + \File dirtycert.h + + \Description + This module defines the CA fallback mechanism which is used by ProtoSSL. + + \Copyright + Copyright (c) 2012 Electronic Arts Inc. + + \Version 01/23/2012 (szhu) +*/ +/********************************************************************************H*/ + +#ifndef _dirtycert_h +#define _dirtycert_h + +/*! +\Moduledef NetConnDefs NetConnDefs +\Modulemember DirtySock +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/proto/protossl.h" + +/*** Defines **********************************************************************/ +#define DIRTYCERT_SERVICENAME_SIZE (128) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// opaque module state ref +typedef struct DirtyCertRefT DirtyCertRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create dirtycert module +DIRTYCODE_API int32_t DirtyCertCreate(void); + +// release resources and destroy module +DIRTYCODE_API int32_t DirtyCertDestroy(void); + +// initiate a CA fetch request +DIRTYCODE_API int32_t DirtyCertCARequestCert(const ProtoSSLCertInfoT *pCertInfo, const char *pHost, int32_t iPort); + +// initiate a CA prefetch request +DIRTYCODE_API void DirtyCertCAPreloadCerts(const char *pServiceName); + +// if a CA fetch request is complete +DIRTYCODE_API int32_t DirtyCertCARequestDone(int32_t iRequestId); + +// release resources used by a CA fetch request +DIRTYCODE_API int32_t DirtyCertCARequestFree(int32_t iRequestId); + +// control module behavior +DIRTYCODE_API int32_t DirtyCertControl(int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// get module status +DIRTYCODE_API int32_t DirtyCertStatus(int32_t iStatus, void *pBuffer, int32_t iBufSize); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtycert_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyerr.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyerr.h new file mode 100644 index 00000000..59f39a1c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyerr.h @@ -0,0 +1,97 @@ +/*H********************************************************************************/ +/*! + \File dirtyerr.h + + \Description + Dirtysock debug error routines. + + \Copyright + Copyright (c) 2005 Electronic Arts + + \Version 06/13/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtyerr_h +#define _dirtyerr_h + +/*! +\Moduledef DirtyErr DirtyErr +\Modulemember DirtySock +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#define DIRTYSOCK_ERRORNAMES (DIRTYCODE_LOGGING && TRUE) +#define DIRTYSOCK_LISTTERM (0x45454545) + +/*** Macros ***********************************************************************/ + +#if DIRTYSOCK_ERRORNAMES +#define DIRTYSOCK_ErrorName(_iError) { (uint32_t)_iError, #_iError } +#define DIRTYSOCK_ListEnd() { DIRTYSOCK_LISTTERM, "" } +#endif + +/*** Type Definitions *************************************************************/ + +typedef struct DirtyErrT +{ + uint32_t uError; + const char *pErrorName; +} DirtyErrT; + +#ifdef DIRTYCODE_PS4 + +typedef void (*DirtySockAppErrorCallback)(int32_t errorCode); + +#endif + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DIRTYCODE_PS4 + +//set Application Error Callback +void DirtyErrAppCallbackSet(DirtySockAppErrorCallback pCallback); + +//Inovke App Error Callback if set to report sony error code back to application layer +void DirtyErrAppReport(int32_t iError); + +#endif + +// take a system-specific error code, and either resolve it to its define name or format it as a hex number +DIRTYCODE_API void DirtyErrName(char *pBuffer, int32_t iBufSize, uint32_t uError); + +// same as DirtyErrName, but references the specified list +DIRTYCODE_API void DirtyErrNameList(char *pBuffer, int32_t iBufSize, uint32_t uError, const DirtyErrT *pList); + +// same as DirtyErrName, except a pointer is returned +DIRTYCODE_API const char *DirtyErrGetName(uint32_t uError); + +// same as DirtyErrGetName, but references the specified list +DIRTYCODE_API const char *DirtyErrGetNameList(uint32_t uError, const DirtyErrT *pList); + +// create a unique error code for use accross DirtySDK +DIRTYCODE_API uint32_t DirtyErrGetHResult(uint16_t uFacility, int16_t iCode, uint8_t bFailure); + +// break a hresult back into its components +DIRTYCODE_API void DirtyErrDecodeHResult(uint32_t hResult, uint16_t* uFacility, int16_t* iCode, uint8_t* bCustomer, uint8_t* bFailure); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtyerr_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtylib.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtylib.h new file mode 100644 index 00000000..f117c6b1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtylib.h @@ -0,0 +1,234 @@ +/*H**************************************************************************************/ +/*! + \File dirtylib.h + + \Description + Provide basic library functions for use by network layer code. + This is needed because the network code is platform/project + independent and needs to rely on a certain set of basic + functions. + + \Copyright + Copyright (c) Electronic Arts 2001-2018 + + \Version 0.5 08/01/01 (GWS) First Version + \Version 1.0 12/31/01 (GWS) Redesigned for Tiburon environment +*/ +/**************************************************************************************H*/ + +#ifndef _dirtylib_h +#define _dirtylib_h + +/*! +\Moduledef DirtyLib DirtyLib +\Modulemember DirtySock +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +// define platform-specific options +#ifndef DIRTYCODE_LOGGING + #if DIRTYCODE_DEBUG + //in debug mode logging is defaulted to on + #define DIRTYCODE_LOGGING (1) + #else + //if its not specified then turn it off + #define DIRTYCODE_LOGGING (0) + #endif +#endif + +//! define NetCrit options +#define NETCRIT_OPTION_NONE (0) //!< default settings +#define NETCRIT_OPTION_SINGLETHREADENABLE (1) //!< enable the crit even when in single-threaded mode + +// debug printing routines +#if DIRTYCODE_LOGGING + #define NetPrintf(_x) NetPrintfCode _x + #define NetPrintfVerbose(_x) NetPrintfVerboseCode _x + #define NetPrintArray(_pMem, _iSize, _pTitle) NetPrintArrayCode(_pMem, _iSize, _pTitle) + #define NetPrintMem(_pMem, _iSize, _pTitle) NetPrintMemCode(_pMem, _iSize, _pTitle) + #define NetPrintWrap(_pString, _iWrapCol) NetPrintWrapCode(_pString, _iWrapCol) + #define NetTimeStampEnable(_bEnableTimeStamp) NetTimeStampEnableCode(_bEnableTimeStamp) +#else + #define NetPrintf(_x) { } + #define NetPrintfVerbose(_x) { } + #define NetPrintArray(_pMem, _iSize, _pTitle) { } + #define NetPrintMem(_pMem, _iSize, _pTitle) { } + #define NetPrintWrap(_pString, _iWrapCol) { } + #define NetTimeStampEnable(_bEnableTimeStamp) { } +#endif + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct NetCritPrivT NetCritPrivT; + +//! critical section definition +typedef struct NetCritT +{ + NetCritPrivT *pData; +} NetCritT; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Portable routines implemented in dirtynet.c +*/ + +// platform-common create (called internally by NetLibCreate) +DIRTYCODE_API void NetLibCommonInit(void); + +// platform-common shutdown (called internally by NetLibDestroy) +DIRTYCODE_API void NetLibCommonShutdown(void); + +// reset net idle list +DIRTYCODE_API void NetIdleReset(void); + +// remove a function to the idle callback list. +DIRTYCODE_API void NetIdleAdd(void (*proc)(void *ref), void *ref); + +// call all the functions in the idle list. +DIRTYCODE_API void NetIdleDel(void (*proc)(void *ref), void *ref); + +// make sure all idle calls have completed +DIRTYCODE_API void NetIdleDone(void); + +// add a function to the idle callback list +DIRTYCODE_API void NetIdleCall(void); + +// return 32-bit hash from given input string +DIRTYCODE_API int32_t NetHash(const char *pString); + +// return 32-bit hash from given buffer +DIRTYCODE_API int32_t NetHashBin(const void *pBuffer, uint32_t uLength); + +// return 32-bit CRC from given buffer +DIRTYCODE_API int32_t NetCrc32(const uint8_t *pBuffer, int32_t iBufLen, const uint32_t *pCrcTable); + +// A simple psuedo-random sequence generator +DIRTYCODE_API uint32_t NetRand(uint32_t uLimit); + +// return time +DIRTYCODE_API uint64_t NetTime(void); + +// enable logging time stamp +DIRTYCODE_API void NetTimeStampEnableCode(uint8_t bEnableTimeStamp); + +// hook into debug output +DIRTYCODE_API void NetPrintfHook(int32_t (*pPrintfDebugHook)(void *pParm, const char *pText), void *pParm); + +// diagnostic output routine (do not call directly, use NetPrintf() wrapper +DIRTYCODE_API int32_t NetPrintfCode(const char *fmt, ...); + +// diagnostic output routine (do not call directly, use NetPrintf() wrapper +DIRTYCODE_API void NetPrintfVerboseCode(int32_t iVerbosityLevel, int32_t iCheckLevel, const char *pFormat, ...); + +// print input buffer with wrapping (do not call directly; use NetPrintWrap() wrapper) +DIRTYCODE_API void NetPrintWrapCode(const char *pData, int32_t iWrapCol); + +// print memory as hex (do not call directly; use NetPrintMem() wrapper) +DIRTYCODE_API void NetPrintMemCode(const void *pMem, int32_t iSize, const char *pTitle); + +// print memory as a c-style array (do not call directly; use NetPrintArray() wrapper) +DIRTYCODE_API void NetPrintArrayCode(const void *pMem, int32_t iSize, const char *pTitle); + +/* + Platform-specific routines implemented in dirtynet.c +*/ + +// initialize the network library functions. +DIRTYCODE_API void NetLibCreate(int32_t iThreadPrio, int32_t iThreadStackSize, int32_t iThreadCpuAffinity); + +// shutdown the network library. +DIRTYCODE_API void NetLibDestroy(uint32_t uShutdownFlags); + +// return an increasing tick count with millisecond scale +DIRTYCODE_API uint32_t NetTick(void); + +// return microsecond timer, intended for debug timing purposes only +DIRTYCODE_API uint64_t NetTickUsec(void); + + +/* +The NetTickDiff() macro implies 2 steps. + +The first step consists in substracting 2 unsigned values. When working with unsigned +types, modular arithmetic (aka "wrap around" behavior) is taking place. It is similar +to reading a clock. + Adding clockwise: 9 + 4 = 1 (13 mod 12) + Substracting counterclockwise: 1 - 4 = 9 (-3 mod 12) +Obviously the value range here is [0,0xFFFFFFFF] and not [0,11]. +By the virtue of modular arithmetic, the difference between _uNewTime and _uOldTime is +always valid, even in scenarios where one (or both) of the two values has just +"wrapped around". + +The second step consists in casting the unsigned result of step 1 into a signed +integer. The result of that second step is the final outcome of the macro, i.e. a +value ranging between + -2147483648 (2's complement notation: 0x80000000) and + 2147483647 (2's complement notation: 0x7FFFFFFF) + +Consequently, the maximum time difference (positive or negative) that can be calculated +between _uNewTime and _uOldTime is 2147483647 ms, i.e. approximately 596,8 hours (24,9 days). + +Any longer period of time captured with an initial call to NetTick() and a final +call to NetTick() and then calculated by feeding both values to NetTickDiff() would +incorrectly result in a time difference much shorter than reality. + +If _uNewTime is more recent than _uOldTime (by not more than 596,8 hours), then +the returned time difference will be positive. + +If _uOldTime is more recent than _uNewTime (by not more than 596,8 hours), then +the returned time difference will be negative. +*/ + +// return signed difference between new tick count and old tick count (new - old) +#define NetTickDiff(_uNewTime, _uOldTime) ((signed)((_uNewTime) - (_uOldTime))) + +// return localtime +DIRTYCODE_API struct tm *NetLocalTime(struct tm *pTm, uint64_t uElap); + +// convert a platform-specific time format to generic time format +DIRTYCODE_API struct tm *NetPlattimeToTime(struct tm *pTm, void *pPlatTime); + +// convert a platform-specific time format to generic time format, with milliseconds +DIRTYCODE_API struct tm *NetPlattimeToTimeMs(struct tm *pTm, int32_t *pImSec); + +// initialize a critical section for use -- includes name for verbose debugging on some platforms +DIRTYCODE_API int32_t NetCritInit(NetCritT *pCrit, const char *pCritName); + +// initialize a critical section with the ability to set options (NETCRIT_OPTION_*) +DIRTYCODE_API int32_t NetCritInit2(NetCritT *pCrit, const char *pCritName, uint32_t uFlags); + +// release resources and destroy critical section +DIRTYCODE_API void NetCritKill(NetCritT *pCrit); + +// attempt to gain access to critical section +DIRTYCODE_API int32_t NetCritTry(NetCritT *pCrit); + +// enter a critical section, blocking if needed +DIRTYCODE_API void NetCritEnter(NetCritT *pCrit); + +// leave a critical section +DIRTYCODE_API void NetCritLeave(NetCritT *pCrit); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtylib_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtymem.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtymem.h new file mode 100644 index 00000000..9fa4ebc5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtymem.h @@ -0,0 +1,180 @@ +/*H********************************************************************************/ +/*! + \File dirtymem.h + + \Description + DirtySock memory allocation routines. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 10/12/2005 (jbrookes) First Version + \Version 11/19/2008 (mclouatre) Adding pMemGroupUserData to mem groups +*/ +/********************************************************************************H*/ + +#ifndef _dirtymem_h +#define _dirtymem_h + +/*! +\Moduledef DirtyMem DirtyMem +\Modulemember DirtySock +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +/*! + All DirtySock modules have their memory identifiers defined here. +*/ +// comm modules +#define COMMSRP_MEMID ('csrp') +#define COMMUDP_MEMID ('cudp') + +// crypt modules +#define CRYPTRSA_MEMID ('crsa') +#define CRYPTRAND_MEMID ('rand') + +// dirtysock modules +#define DIRTYAUTH_MEMID ('dath') +#define DIRTYCERT_MEMID ('dcrt') +#define DIRTYCM_MEMID ('dhcm') +#define DIRTYSESSMGR_MEMID ('dsmg') +#define DIRTYWEBAPI_MEMID ('weba') +#define DIRTYEVENT_DISP_MEMID ('semd') +#define SOCKET_MEMID ('dsoc') +#define NETCONN_MEMID ('ncon') +#define DIRTYTHREAD_MEMID ('dthr') + +// game modules +#define CONNAPI_MEMID ('conn') +#define NETGAMEDIST_MEMID ('ngdt') +#define NETGAMEDISTSERV_MEMID ('ngds') +#define NETGAMELINK_MEMID ('nglk') +#define NETGAMEUTIL_MEMID ('ngut') + +// graph modules +#define DIRTYGRAPH_MEMID ('dgph') +#define DIRTYJPG_MEMID ('djpg') +#define DIRTYPNG_MEMID ('dpng') + +// misc modules +#define LOBBYLAN_MEMID ('llan') +#define USERAPI_MEMID ('uapi') +#define USERLISTAPI_MEMID ('ulst') +#define WEBLOG_MEMID ('wlog') +#define PRIVILEGEAPI_MEMID ('priv') + +// proto modules +#define PROTOADVT_MEMID ('padv') +#define PROTOHTTP_MEMID ('phtp') +#define HTTPSERV_MEMID ('hsrv') +#define HTTPMGR_MEMID ('hmgr') +#define PROTOMANGLE_MEMID ('pmgl') +#define PROTOPING_MEMID ('ppng') +#define PINGMGR_MEMID ('lpmg') +#define PROTOSSL_MEMID ('pssl') +#define PROTOSTREAM_MEMID ('pstr') +#define PROTOTUNNEL_MEMID ('ptun') +#define PROTOUDP_MEMID ('pudp') +#define PROTOUPNP_MEMID ('pupp') +#define PROTOWEBSOCKET_MEMID ('webs') + +// util modules +#define DISPLIST_MEMID ('ldsp') +#define HASHER_MEMID ('lhsh') +#define SORT_MEMID ('lsor') +#define HPACK_MEMID ('hpak') +#define PROTOBUF_MEMID ('pbuf') + +// qos module +#define QOSAPI_MEMID ('dqos') +#define QOS_CLIENT_MEMID ('qosc') +#define QOS_COMMON_MEMID ('qcom') + +// voip module +#define VOIP_MEMID ('voip') +#define VOIPNARRATE_MEMID ('vnar') +#define VOIPTRANSCRIBE_MEMID ('vscr') +#define VOIP_PLATFORM_MEMID ('vplt') // +#include +#include +#include + +#elif !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_PC) && !defined(DIRTYCODE_GDK) + +#include +#include /* struct sockaddr_in */ +#include /* struct in_addr */ + +#if defined(DIRTYCODE_PS4) +#define IPPROTO_IPV4 4 +#ifdef INADDR_BROADCAST + #undef INADDR_BROADCAST +#endif +#ifdef INADDR_ANY + #undef INADDR_ANY + #define INADDR_ANY 0x00000000 +#endif +#ifdef INADDR_LOOPBACK + #undef INADDR_LOOPBACK + #define INADDR_LOOPBACK 0x7f000001 +#endif + +#endif // defined(DIRTYCODE_PS4) + +#else // DIRTYCODE_PC or DIRTYCODE_XBOXONE + +#if !defined(_WINSOCKAPI_) || defined(EA_FORCE_WINSOCK2_INCLUDE) // if was included, skip, unless we want to force include it +#include +#include +#endif // _WINSOCKAPI_ + +#endif // including system socket/inet headers + + +/* + IPv6 definitions for common code for platforms that don't have them, + until if/when they are available natively +*/ + +#if defined(DIRTYCODE_PS4) +#if !defined(s6_addr) // not sure what ipv6 definitions will look like but this is a likely definition we can check for +struct in6_addr { + uint8_t s6_addr[16]; /* IPv6 address */ +}; + +struct sockaddr_in6 { + uint16_t sin6_family; /* AF_INET6 */ + uint16_t sin6_port; /* port number */ + uint32_t sin6_flowinfo; /* IPv6 flow information */ + struct in6_addr sin6_addr; /* IPv6 address */ + uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */ +}; +#endif // !defined(s6_addr) +#endif // DIRTYCODE_PS4 + + +/* + DirtyNet specific defines +*/ + +#define SOCK_NORECV 1 //!< caller does not want to receive more data +#define SOCK_NOSEND 2 //!< caller does not want to send more data + +#if !defined (DIRTYCODE_NX) +#ifndef INADDR_BROADCAST +#define INADDR_BROADCAST 0xffffffff +#endif +#endif + +#define CALLB_NONE 0 //!< no callback +#define CALLB_SEND 1 //!< call when we can send +#define CALLB_RECV 2 //!< call when we can receive + +#define SOCKLOOK_LOCALADDR "*" + +#define SOCKLOOK_FLAGS_ALLOWXDNS (1) + +//! maximum udp packet size we can receive (constrained by Xbox 360 max VDP packet size) +#define SOCKET_MAXUDPRECV (1264) + +//! maximum number of virtual ports that can be specified +#define SOCKET_MAXVIRTUALPORTS (32) + +// Errors + +#define SOCKERR_NONE 0 //!< no error +#define SOCKERR_CLOSED -1 //!< the socket is closed +#define SOCKERR_NOTCONN -2 //!< the socket is not connected +#define SOCKERR_BLOCKED -3 //!< operation would result in blocking +#define SOCKERR_ADDRESS -4 //!< the address is invalid +#define SOCKERR_UNREACH -5 //!< network cannot be accessed by this host +#define SOCKERR_REFUSED -6 //!< connection refused by the recipient +#define SOCKERR_OTHER -7 //!< unclassified error +#define SOCKERR_NOMEM -8 //!< out of memory +#define SOCKERR_NORSRC -9 //!< out of resources +#define SOCKERR_UNSUPPORT -10 //!< unsupported operation +#define SOCKERR_INVALID -11 //!< resource or operation is invalid +#define SOCKERR_ADDRINUSE -12 //!< address already in use +#define SOCKERR_CONNRESET -13 //!< connection has been reset +#define SOCKERR_BADPIPE -14 //!< EBADF or EPIPE + +//! error occured when trying to map address using SocketAddrMapAddress (+ip6)/SocketAddrRemapAddress (~ip6) +#define SOCKMAP_ERROR (-1) + +//! maximum number of send callbacks supported (internal use only - needs to be public for DirtyCast stress tester) +#define SOCKET_MAXSENDCALLBACKS (8) + + +/*** Macros ****************************************************************************/ + +/* +Macros used to write into a sockaddr structure take into account the fact that the sa_data field is defined +as an array of signed char on Windows and an array of unsigned char on other platforms. Doing this is required +to avoid C4365 warning occurring when these macros are used in .cpp files. (The same warning would not occur +when using these macros in .c files. The windows compiler (cl.exe) only warns about C4365 with .cpp files. +*/ +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_PC) +#define SA_DATA_TYPE char +#else +#define SA_DATA_TYPE unsigned char +#endif + +//! init a sockaddr to zero and set its family type +#define SockaddrInit(addr,fam) { (addr)->sa_family = (fam); ds_memclr((addr)->sa_data, sizeof((addr)->sa_data)); } + +//! get the port in host format from sockaddr +#define SockaddrInGetPort(addr) ((((unsigned char)(addr)->sa_data[0])<<8)|(((unsigned char)(addr)->sa_data[1])<<0)) + +//! set the port in host format in a sockaddr +#define SockaddrInSetPort(addr,val) { (addr)->sa_data[0] = (SA_DATA_TYPE)(((val)>>8)&0xff); (addr)->sa_data[1] = (SA_DATA_TYPE)((val)&0xff); } + +//! get the address in host format from sockaddr +#define SockaddrInGetAddr(addr) (((((((unsigned char)((addr)->sa_data[2])<<8)|(unsigned char)((addr)->sa_data[3]))<<8)|(unsigned char)((addr)->sa_data[4]))<<8)|(unsigned char)((addr)->sa_data[5])) + +//! set the address in host format in a sockaddr +#define SockaddrInSetAddr(addr,val) { uint32_t val2 = (val); (addr)->sa_data[5] = (SA_DATA_TYPE)val2; val2 >>= 8; (addr)->sa_data[4] = (SA_DATA_TYPE)val2; val2 >>= 8; (addr)->sa_data[3] = (SA_DATA_TYPE)val2; val2 >>= 8; (addr)->sa_data[2] = (SA_DATA_TYPE)val2; } + +//! get the misc field in host format from sockaddr +#define SockaddrInGetMisc(addr) (((((((unsigned char)((addr)->sa_data[6])<<8)|(unsigned char)((addr)->sa_data[7]))<<8)|(unsigned char)((addr)->sa_data[8]))<<8)|(unsigned char)((addr)->sa_data[9])) + +//! set the misc field in host format in a sockaddr +#define SockaddrInSetMisc(addr,val) { uint32_t val2 = (val); (addr)->sa_data[9] = (SA_DATA_TYPE)val2; val2 >>= 8; (addr)->sa_data[8] = (SA_DATA_TYPE)val2; val2 >>= 8; (addr)->sa_data[7] = (SA_DATA_TYPE)val2; val2 >>= 8; (addr)->sa_data[6] = (SA_DATA_TYPE)val2; } + +//! detect loopback address (family independent) +#define SockaddrIsLoopback(addr) (( ((addr)->sa_family == AF_INET) && ((addr)->sa_data[0] == 127) && ((addr)->sa_data[1] == 0) && ((addr)->sa_data[2] == 0) && ((addr)->sa_data[3] == 1) )) + +/* + sockaddr_v6 helpers +*/ + +//! init a sockaddr6 to zero and set its family type +#define SockaddrInit6(addr,fam) { ds_memclr(addr, sizeof(*(addr))); (addr)->sin6_family = (fam); } + +//! get IPv6 address from IPv4-mapped IPv6 address, also works for NAT64 +#define SockaddrIn6GetAddr4(addr) (((((((uint8_t)((addr)->sin6_addr.s6_addr[12])<<8)|(uint8_t)((addr)->sin6_addr.s6_addr[13]))<<8)|(uint8_t)((addr)->sin6_addr.s6_addr[14]))<<8)|(uint8_t)((addr)->sin6_addr.s6_addr[15])) + +/*** Type Definitions ******************************************************************/ + + +// basic socket type is a pointer +typedef struct SocketT SocketT; + +//! a host lookup structure -- uses a callback +//! system to determine when lookup has finished +typedef struct HostentT +{ + int32_t done; //!< public: indicates when lookup is complete + uint32_t addr; //!< public: resolved host address + + int32_t (*Done)(struct HostentT *); //!< public: callback to indicate completion status + void (*Free)(struct HostentT *); //!< public: callback to release Hostent structure + + char name[256]; //!< private (the maximum DNS name length is 253 characters) + int32_t sema; //!< private + int32_t thread; //!< private + void* pData; //!< private + uint32_t timeout; //!< private + struct HostentT *pNext; //!< private + int32_t refcount; //!< private +} HostentT; + +//! global socket send callback +typedef int32_t (SocketSendCallbackT)(SocketT *pSocket, int32_t iType, const uint8_t *pData, int32_t iDataSize, const struct sockaddr *pTo, void *pCallref); + +//! socket send callback entry (internal use only - needs to be public for DirtyCast stress tester) +typedef struct SocketSendCallbackEntryT +{ + SocketSendCallbackT *pSendCallback; //!< global send callback + void *pSendCallref; //!< user callback data +} SocketSendCallbackEntryT; + + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + platform independent functions, implemented in dirtynet.c +*/ + +// compare two sockaddr structures +DIRTYCODE_API int32_t SockaddrCompare(const struct sockaddr *pAddr1, const struct sockaddr *pAddr2); + +// set the address in text format in a sockaddr +DIRTYCODE_API int32_t SockaddrInSetAddrText(struct sockaddr *pAddr, const char *pStr); + +// get the address in text format from sockaddr +DIRTYCODE_API char *SockaddrInGetAddrText(const struct sockaddr *pAddr, char *pStr, int32_t iLen); + +// parse address:port combination +DIRTYCODE_API int32_t SockaddrInParse(struct sockaddr *pAddr, const char *pParse); + +// parse address:port:port combination into separate components +DIRTYCODE_API int32_t SockaddrInParse2(uint32_t *pAddr, int32_t *pPort, int32_t *pPort2, const char *pParse); + +// convert 32-bit internet address into textual form +DIRTYCODE_API char *SocketInAddrGetText(uint32_t uAddr, char *pStr, int32_t iLen); + +// convert textual internet address into 32-bit integer form +DIRTYCODE_API int32_t SocketInTextGetAddr(const char *pAddrText); + +// convert int16_t from host to network byte order +DIRTYCODE_API uint16_t SocketHtons(uint16_t uAddr); + +// convert int32_t from host to network byte order +DIRTYCODE_API uint32_t SocketHtonl(uint32_t uAddr); + +// convert int16_t from network to host byte order +DIRTYCODE_API uint16_t SocketNtohs(uint16_t uAddr); + +// convert int32_t from network to host byte order. +DIRTYCODE_API uint32_t SocketNtohl(uint32_t uAddr); + +/* + platform dependent functions, implemented in dirtynet.c +*/ + +// create new instance of socket interface module +DIRTYCODE_API int32_t SocketCreate(int32_t iThreadPrio, int32_t iThreadStackSize, int32_t iThreadCpuAffinity); + +// release resources and destroy module. +DIRTYCODE_API int32_t SocketDestroy(uint32_t uShutdownFlags); + +// create a new transfer endpoint +DIRTYCODE_API SocketT *SocketOpen(int32_t af, int32_t type, int32_t protocol); + +// perform partial/complete shutdown of socket +DIRTYCODE_API int32_t SocketShutdown(SocketT *pSocket, int32_t how); + +// close a socket +DIRTYCODE_API int32_t SocketClose(SocketT *pSocket); + +// import a socket - may be SocketT pointer or sony socket ref +DIRTYCODE_API SocketT *SocketImport(intptr_t uSockRef); + +// release an imported socket +DIRTYCODE_API void SocketRelease(SocketT *pSocket); + +// bind a local address/port to a socket +DIRTYCODE_API int32_t SocketBind(SocketT *pSocket, const struct sockaddr *name, int32_t namelen); + +// return information about an existing socket. +DIRTYCODE_API int32_t SocketInfo(SocketT *pSocket, int32_t iInfo, int32_t iData, void *pBuf, int32_t iLen); + +// send a control message to the dirtysock layer +DIRTYCODE_API int32_t SocketControl(SocketT *pSocket, int32_t option, int32_t data1, void *data2, void *data3); + +// start listening for an incoming connection on the socket +DIRTYCODE_API int32_t SocketListen(SocketT *pSocket, int32_t backlog); + +// attempt to accept an incoming connection from a +DIRTYCODE_API SocketT *SocketAccept(SocketT *pSocket, struct sockaddr *addr, int32_t *addrlen); + +// initiate a connection attempt to a remote host +DIRTYCODE_API int32_t SocketConnect(SocketT *pSocket, struct sockaddr *name, int32_t namelen); + +// send data to a remote host +DIRTYCODE_API int32_t SocketSendto(SocketT *pSocket, const char *buf, int32_t len, int32_t flags, const struct sockaddr *to, int32_t tolen); + +// same as SocketSendto() with "to" set to NULL +#define SocketSend(_pSocket, _pBuf, iLen, iFlags) SocketSendto(_pSocket, _pBuf, iLen, iFlags, NULL, 0) + +// receive data from a remote host +DIRTYCODE_API int32_t SocketRecvfrom(SocketT *pSocket, char *buf, int32_t len, int32_t flags, struct sockaddr *from, int32_t *fromlen); + +// same as SocketRecvfrom() with "from" set to NULL. +#define SocketRecv(_pSocket, pBuf, iLen, iFlags) SocketRecvfrom(_pSocket, pBuf, iLen, iFlags, NULL, 0) + +// register a callback routine for notification of socket events +DIRTYCODE_API int32_t SocketCallback(SocketT *pSocket, int32_t flags, int32_t timeout, void *ref, int32_t (*proc)(SocketT *pSocket, int32_t flags, void *ref)); + +// return the host address that would be used in order to communicate with the given destination address. +DIRTYCODE_API int32_t SocketHost(struct sockaddr *host, int32_t hostlen, const struct sockaddr *dest, int32_t destlen); + +// lookup a host by name and return the corresponding Internet address +DIRTYCODE_API HostentT *SocketLookup(const char *text, int32_t timeout); + +// lookup a host by name and return the corresponding Internet address, with flags +#define SocketLookup2(_text, _timeout, _flags) SocketLookup(_text, _timeout) + +// invoke all registered send callbacks (internal use only - needs to be public for DirtyCast stress tester) +int32_t SocketSendCallbackInvoke(SocketSendCallbackEntryT aCbEntries[], SocketT *pSocket, int32_t iType, const char *pBuf, int32_t iLen, const struct sockaddr *pTo); + + +// return "external" local address +DIRTYCODE_API uint32_t SocketGetLocalAddr(void); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtynet_h + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtysessionmanager.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtysessionmanager.h new file mode 100644 index 00000000..8004fc73 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtysessionmanager.h @@ -0,0 +1,120 @@ +/*H*************************************************************************************/ +/*! + \File dirtysessionmanager.h + + \Description + DirtySessionManager handles the creation, joinning and leaving of + a session. Offers mechanism to encode and decode the session, and does some + session flags management + + \Notes + + \Copyright + Copyright (c) Electronic Arts 2003-2007 + + \Version 1.0 11/03/2003 (jbrookes) First Version + \Version 2.0 11/04/2007 (jbrookes) Removed from ProtoMangle namespace, cleanup + \Version 2.2 10/26/2009 (mclouatre) Renamed from core/include/dirtysessionmanager.h to core/include/xenon/dirtysessionmanagerxenon.h + \Version 2.3 03/26/2013 (cvienneau) Renamed from core/include/xenon/dirtysessionmanagerxenon.h to core/include/dirtysessionmanager.h +*/ +/*************************************************************************************H*/ + +#ifndef _dirtysessionmanager_h +#define _dirtysessionmanager_h + +/*! +\Moduledef DirtySessionManager DirtySessionManager +\Modulemember DirtySock +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtyaddr.h" + +/*** Defines ***************************************************************************/ + +#define DIRTYSESSIONMANAGER_FLAG_PUBLICSLOT (1) +#define DIRTYSESSIONMANAGER_FLAG_PRIVATESLOT (2) + +#if defined(DIRTYCODE_PS4) +#define DIRTY_SESSION_GAME_MODE_LENGTH 257 +#endif + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct DirtySessionManagerRefT DirtySessionManagerRefT; +#if DIRTYCODE_DEBUG +//! true skill values struct +typedef struct DirtySessionManagerTrueSkillRefT //!< true skill struct used for debugging +{ + double dMu; + double dSigma; +} DirtySessionManagerTrueSkillRefT; +#endif + +#if defined(DIRTYCODE_PS4) +//!< data that we control within the binary blob portion of the session +typedef struct DirtySessionManagerBinaryHeaderT +{ + int64_t iLobbyId; + int64_t iGameType; +} DirtySessionManagerBinaryHeaderT; + +//!< data that we control within the changeable binary blob portion of the session +typedef struct DirtySessionManagerChangeableBinaryHeaderT +{ + char strGameMode[DIRTY_SESSION_GAME_MODE_LENGTH]; +} DirtySessionManagerChangeableBinaryHeaderT; +#endif + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// allocate module state and prepare for use +DirtySessionManagerRefT *DirtySessionManagerCreate(void); + +// destroy the module and release its state +void DirtySessionManagerDestroy(DirtySessionManagerRefT *pRef); + +// give time to module to do its thing (should be called periodically to allow module to perform work) +void DirtySessionManagerUpdate(DirtySessionManagerRefT *pRef); + +// join one (or many) remote player(s) by specifying the session and type of slot to use +void DirtySessionManagerConnect(DirtySessionManagerRefT *pRef, const char **pSessID, uint32_t *pSlot, uint32_t uCount); + +// dirtysessionmanager control +int32_t DirtySessionManagerControl(DirtySessionManagerRefT *pRef, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue); + +// get module status based on selector +int32_t DirtySessionManagerStatus(DirtySessionManagerRefT *pRef, int32_t iSelect, void *pBuf, int32_t iBufSize); + +// get module status based on selector +int32_t DirtySessionManagerStatus2(DirtySessionManagerRefT *pRef, int32_t iSelect, int32_t iValue, int32_t iValue2, int32_t iValue3, void *pBuf, int32_t iBufSize); + +// encode +void DirtySessionManagerEncodeSession(char *pBuffer, int32_t iBufSize, const void *pSessionInfo); + +// decode +void DirtySessionManagerDecodeSession(void *pSessionInfo, const char *pBuffer); + +// create the session (previously the 'sess' control selector) +int32_t DirtySessionManagerCreateSess(DirtySessionManagerRefT *pRef, uint32_t bRanked, uint32_t *uUserFlags, const char *pSession, DirtyAddrT *pLocalAddrs); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtysessionmanager_h + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtythread.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtythread.h new file mode 100644 index 00000000..2eb96a3a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtythread.h @@ -0,0 +1,78 @@ +/*H**************************************************************************************/ +/*! + \File dirtythread.h + + \Description + Provide threading library functions for use by network layer code. + + \Copyright + Copyright (c) Electronic Arts 2017 + + \Version 09/27/17 (eesponda) +*/ +/**************************************************************************************H*/ + +#ifndef _dirtythread_h +#define _dirtythread_h + +/*! +\Moduledef DirtyThread DirtyThread +\Modulemember DirtySock +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Type Definitions ******************************************************************/ + +// configuration for the thread +typedef struct DirtyThreadConfigT +{ + int32_t iPriority; //!< priority of the thread, platform dependent + int32_t iAffinity; //!< affinity mask + const char *pName; //!< name of the thread + int32_t iVerbosity; //!< verbosity of logging information (deprecated) +} DirtyThreadConfigT; + +// function that gets run on the thread +typedef void (DirtyRunnableFunctionT)(void* pUserData); + +// opaque module ref +typedef struct DirtyConditionRefT DirtyConditionRefT; + +// forward declaration +typedef struct NetCritT NetCritT; + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// start the thread +DIRTYCODE_API int32_t DirtyThreadCreate(DirtyRunnableFunctionT *pFunction, void *pUserData, const DirtyThreadConfigT *pConfig); + +// get thread id +DIRTYCODE_API const char *DirtyThreadGetThreadId(char *pBuffer, int32_t iBufSize); + +// create a condition variable with name +DIRTYCODE_API DirtyConditionRefT *DirtyConditionCreate(const char *pName); + +// destroy the condition +DIRTYCODE_API void DirtyConditionDestroy(DirtyConditionRefT *pCondition); + +// wait for a condition +DIRTYCODE_API void DirtyConditionWait(DirtyConditionRefT *pCondition, NetCritT *pCrit); + +// signal the condition +DIRTYCODE_API uint8_t DirtyConditionSignal(DirtyConditionRefT *pCondition); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtythread_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyuser.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyuser.h new file mode 100644 index 00000000..5c92d799 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/dirtyuser.h @@ -0,0 +1,66 @@ +/*H********************************************************************************/ +/*! + \File dirtyuser.h + + \Description + Definition for portable user type. + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 04/25/13 (mclouatre) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtyuser_h +#define _dirtyuser_h + +/*! +\Moduledef DirtyUser DirtyUser +\Modulemember DirtySock +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#define DIRTYUSER_NATIVEUSER_MAXLEN (64) +#define DIRTYUSER_NATIVEUSER_MAXSIZE (DIRTYUSER_NATIVEUSER_MAXLEN + 1) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque user type +typedef struct DirtyUserT +{ + char strNativeUser[DIRTYUSER_NATIVEUSER_MAXSIZE]; +} DirtyUserT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +//! convert a DirtyUserT to native format +DIRTYCODE_API uint32_t DirtyUserToNativeUser(void *pOutput, int32_t iBufLen, const DirtyUserT *pUser); + +//! convert a native format to a DirtyUserT +DIRTYCODE_API uint32_t DirtyUserFromNativeUser(DirtyUserT *pUser, const void *pInput); + +//! compare two opaque users for equality (same=TRUE, different=FALSE) +DIRTYCODE_API int32_t DirtyUserCompare(DirtyUserT *pUser1, DirtyUserT *pUser2); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtyuser_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/netconn.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/netconn.h new file mode 100644 index 00000000..43ccc167 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/netconn.h @@ -0,0 +1,160 @@ +/*H*************************************************************************************/ +/*! + \File netconn.h + + \Description + Provides network setup and teardown support. Does not actually create any + kind of network connections. + + \Copyright + Copyright (c) Electronic Arts 2001-2009 + + \Version 03/12/2001 (gschaefer) First Version +*/ +/*************************************************************************************H*/ + +#ifndef _netconn_h +#define _netconn_h + +/*! +\Moduledef NetConnDefs NetConnDefs +\Modulemember DirtySock +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/netconndefs.h" + +/*** Defines ***************************************************************************/ + +//! maximum number of local users +#if defined(DIRTYCODE_XBOXONE) + #define NETCONN_MAXLOCALUSERS (16) +#elif defined(DIRTYCODE_NX) + #define NETCONN_MAXLOCALUSERS (8) +#else + #define NETCONN_MAXLOCALUSERS (4) +#endif + + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct NetConnUserDataT +{ + char strName[16]; + void *pRawData1; + void *pRawData2; +} NetConnUserDataT; + +//! account structure +typedef struct NetConnAccountInfoT +{ + int64_t iAccountId; //!< the EA account Id of the user + int64_t iPersonaId; //!< the EA persona Id of the user +} NetConnAccountInfoT; + +#if defined(DIRTYCODE_PS4) +typedef void (NetConnNpStateCallbackT)(int32_t /* SceUserServiceUserId */ userId, int32_t /* SceNpState */ state, void* pUserData); +#endif + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// bring the network connection module to life +DIRTYCODE_API int32_t NetConnStartup(const char *pParams); + +// query the list of available connection configurations +DIRTYCODE_API int32_t NetConnQuery(const char *pDevice, NetConfigRecT *pList, int32_t iSize); + +// bring the networking online with a specific configuration +DIRTYCODE_API int32_t NetConnConnect(const NetConfigRecT *pConfig, const char *pParms, int32_t iData); + +// set module behavior based on input selector +DIRTYCODE_API int32_t NetConnControl(int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue, void *pValue2); + +// check general network connection status (added param) +DIRTYCODE_API int32_t NetConnStatus(int32_t iKind, int32_t iData, void *pBuf, int32_t iBufSize); + +// return MAC address in textual form +DIRTYCODE_API const char *NetConnMAC(void); + +// take down the network connection +DIRTYCODE_API int32_t NetConnDisconnect(void); + +// shutdown the network code and return to idle state +DIRTYCODE_API int32_t NetConnShutdown(uint32_t uShutdownFlags); + +// return elapsed time in milliseconds +DIRTYCODE_API uint32_t NetConnElapsed(void); + +// sleep the application (burn cycles) for some number of milliseconds +DIRTYCODE_API void NetConnSleep(int32_t iMilliSecs); + +// add an idle handler that will get called periodically +DIRTYCODE_API int32_t NetConnIdleAdd(void (*proc)(void *data, uint32_t tick), void *data); + +// remove a previously added idle handler +DIRTYCODE_API int32_t NetConnIdleDel(void (*proc)(void *data, uint32_t tick), void *data); + +// provide "life" to the network code +DIRTYCODE_API void NetConnIdle(void); + +// shut down netconn idle handler +// NOTE: this is meant for internal use only, and should not be called by applications directly +DIRTYCODE_API void NetConnIdleShutdown(void); + +// Enable or disable the timing of netconnidles +DIRTYCODE_API void NetConnTiming(uint8_t uEnableTiming); + +typedef void (UserInfoCallbackT)(NetConnUserDataT *pUserDataT, void *pData); + +// get the unique machine id +DIRTYCODE_API uint32_t NetConnMachineId(void); + +// set the unique machine id +DIRTYCODE_API void NetConnSetMachineId(uint32_t uMachineId); + +#if DIRTYCODE_LOGGING +DIRTYCODE_API void NetConnMonitorValue(const char* pName, int32_t iValue); +#endif + +// copy a startup parameter +DIRTYCODE_API int32_t NetConnCopyParam(char *pDst, int32_t iDstLen, const char *pParamName, const char *pSrc, const char *pDefault); + +// create dirtycert module +DIRTYCODE_API int32_t NetConnDirtyCertCreate(const char *pParams); + +// translate netconn environment to string +DIRTYCODE_API const char *NetConnGetEnvStr(void); + +#if defined(DIRTYCODE_PS4) +DIRTYCODE_API void NetConnRegisterNpStateCallback(NetConnNpStateCallbackT *pCallback, void *pUserData); +#endif + +#ifdef __cplusplus +} + +// forward declare IEAUser so as not to create a dependency +namespace EA { namespace User { class IEAUser; } } + +// use this function to tell netconn about a newly detected local user on the local console +DIRTYCODE_API int32_t NetConnAddLocalUser(int32_t iLocalUserIndex, const EA::User::IEAUser *pLocalUser); + +// use this function to tell netconn about a removed local user on the local console +// in cases where the index is unknown, pass -1 and we will do an internal query +DIRTYCODE_API int32_t NetConnRemoveLocalUser(int32_t iLocalUserIndex, const EA::User::IEAUser *pLocalUser); +#endif // __cplusplus + +//@} + +#endif // _netconn_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/netconndefs.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/netconndefs.h new file mode 100644 index 00000000..6c7780ad --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtysock/netconndefs.h @@ -0,0 +1,75 @@ +/*H********************************************************************************/ +/*! + \File netconndefs.h + + \Description + Definitions for the netconn module. + + \Copyright + Copyright (c) 2005-2009 Electronic Arts Inc. + + \Version 09/29/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _netconndefs_h +#define _netconndefs_h + +/*! +\Moduledef NetConnDefs NetConnDefs +\Modulemember DirtySock +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +// interface types, returned by NetConnStatus('type') +#define NETCONN_IFTYPE_NONE (1) //!< indeterminate interface type +#define NETCONN_IFTYPE_MODEM (2) //!< interface is a modem +#define NETCONN_IFTYPE_ETHER (4) //!< interface is ethernet +#define NETCONN_IFTYPE_USB (8) //!< interface bus type is USB +#define NETCONN_IFTYPE_PPPOE (16) //!< interface is PPPoE +#define NETCONN_IFTYPE_WIRELESS (32) //!< interface is wireless (wifi) +#define NETCONN_IFTYPE_CELL (64) //!< interface is cellular + +// EA back-end environment types, returned by NetConnStatus('envi') +#define NETCONN_PLATENV_DEV (8) //!< Dev environment - Note (0) is used by the 'envi' NetConnStatus selector to indicate ~inp/try again +#define NETCONN_PLATENV_TEST (1) //!< Test environment +#define NETCONN_PLATENV_CERT (2) //!< Certification environment +#define NETCONN_PLATENV_PROD (4) //!< Production environment + +// generic netconn error responses +#define NETCONN_ERROR_ISACTIVE (-1) //!< the module is currently active +#define NETCONN_ERROR_NOTACTIVE (-2) //!< the module isn't currently active + + +// generic NetConnStartup errors +#define NETCONN_ERROR_NO_MEMORY (-2) +#define NETCONN_ERROR_SOCKET_CREATE (-3) +#define NETCONN_ERROR_DIRTYCERT_CREATE (-4) +#define NETCONN_ERROR_PROTOSSL_CREATE (-5) +#define NETCONN_ERROR_PROTOUPNP_CREATE (-6) +#define NETCONN_ERROR_INTERNAL (-7) +#define NETCONN_ERROR_PLATFORM_SPECIFIC (-8) +#define NETCONN_ERROR_ALREADY_STARTED (-9) +#define NETCONN_ERROR_RETRY (-10) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! network configuration entry +typedef void * NetConfigRecT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +//@} + +#endif // _netconndefs_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtyvers.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtyvers.h new file mode 100644 index 00000000..3f1fc5dc --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/dirtyvers.h @@ -0,0 +1,52 @@ +/*H********************************************************************************/ +/*! + \File dirtyvers.h + + \Description + DirtySock SDK version. + + \Copyright + Copyright (c) 2003-2005 Electronic Arts + + \Version 06/13/2003 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtyvers_h +#define _dirtyvers_h + +/*! +\Moduledef DirtyVers DirtyVers +\Modulemember DirtySock +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + + +/*** Defines **********************************************************************/ + +#define DIRTYSDK_VERSION_MAKE(year, season, major, minor, patch) (((year) * 100000000) + ((season) * 1000000) + ((major) * 10000) + ((minor) * 100) + (patch)) + +#define DIRTYSDK_VERSION_YEAR (15) +#define DIRTYSDK_VERSION_SEASON (1) +#define DIRTYSDK_VERSION_MAJOR (6) +#define DIRTYSDK_VERSION_MINOR (0) +#define DIRTYSDK_VERSION_PATCH (5) +#define DIRTYSDK_VERSION (DIRTYSDK_VERSION_MAKE(DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH)) +#define DIRTYVERS (DIRTYSDK_VERSION) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +//@} + +#endif // _dirtyvers_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/connapi.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/connapi.h new file mode 100644 index 00000000..e943a171 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/connapi.h @@ -0,0 +1,266 @@ +/*H********************************************************************************/ +/*! + \File connapi.h + + \Description + ConnApi is a high-level connection manager, that packages the "connect to + peer" process into a single module. Both game connections and voice + connections can be managed. Multiple peers are supported in a host/client + model for the game connection, and a peer/peer model for the voice + connections. + + \Copyright + Copyright (c) Electronic Arts 2005. ALL RIGHTS RESERVED. + + \Version 01/04/2005 (jbrookes) first version +*/ +/********************************************************************************H*/ + +#ifndef _connapi_h +#define _connapi_h + +/*! +\Moduledef ConnApi ConnApi +\Modulemember Game +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtyaddr.h" +#include "DirtySDK/game/netgameutil.h" +#include "DirtySDK/game/netgamelink.h" +#include "DirtySDK/game/netgamedist.h" +#include "DirtySDK/comm/commudp.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/prototunnel.h" + +/*** Defines **********************************************************************/ + + +// connection flags +#define CONNAPI_CONNFLAG_GAMECONN (1) //!< game connection supported +#define CONNAPI_CONNFLAG_VOIPCONN (2) //!< voip connection supported +#define CONNAPI_CONNFLAG_GAMEVOIP (3) //!< game and voip connections supported + +// connection status flags +#define CONNAPI_CONNFLAG_CONNECTED (4) //!< set if connection succeeded +#define CONNAPI_CONNFLAG_DEMANGLED (16) //!< set if demangler was attempted +#define CONNAPI_CONNFLAG_PKTRECEIVED (32) //!< set if packets are received (for game connection only) + +// error codes +#define CONNAPI_ERROR_INVALID_STATE (-1) //!< connapi is in an invalid state +#define CONNAPI_ERROR_CLIENTLIST_FULL (-2) //!< client list is full +#define CONNAPI_ERROR_SLOT_USED (-3) //!< selected slot already in use +#define CONNAPI_ERROR_SLOT_OUT_OF_RANGE (-4) //!< selected slot is not an valid index into the client array +#define CONNAPI_CALLBACKS_FULL (-5) //!< maximum number of callbacks registered +#define CONNAPI_CALLBACK_NOT_FOUND (-6) //!< callback not found + +// supported connection concierge mode (to be used with 'ccmd' control selector) +#define CONNAPI_CCMODE_PEERONLY (0) //!< peer connections only +#define CONNAPI_CCMODE_HOSTEDONLY (1) //!< ccs connections only +#define CONNAPI_CCMODE_HOSTEDFALLBACK (2) //!< peer connections fallback to ccs on failure + +//! ConnApi callback types +typedef enum ConnApiCbTypeE +{ + CONNAPI_CBTYPE_GAMEEVENT, //!< a game event occurred + CONNAPI_CBTYPE_DESTEVENT, //!< link/util ref destruction event + CONNAPI_CBTYPE_VOIPEVENT, //!< a voip event occurred + CONNAPI_NUMCBTYPES //!< number of callback types +} ConnApiCbTypeE; + +//! connection status +typedef enum ConnApiConnStatusE +{ + CONNAPI_STATUS_INIT, //!< initialization state + CONNAPI_STATUS_CONN, //!< connecting to peer + CONNAPI_STATUS_MNGL, //!< demangling + CONNAPI_STATUS_ACTV, //!< connection established + CONNAPI_STATUS_DISC, //!< disconnected + CONNAPI_NUMSTATUSTYPES //!< max number of status types +} ConnApiConnStatusE; + +//! game topology types +typedef enum ConnApiGameTopologyE +{ + CONNAPI_GAMETOPOLOGY_DISABLED, //!< no game traffic + CONNAPI_GAMETOPOLOGY_PEERWEB, //!< peer to peer full mesh + CONNAPI_GAMETOPOLOGY_PEERHOSTED, //!< hosted by peer + CONNAPI_GAMETOPOLOGY_SERVERHOSTED //!< hosted by server +} ConnApiGameTopologyE; + +//! voip topology types +typedef enum ConnApiVoipTopologyE +{ + CONNAPI_VOIPTOPOLOGY_DISABLED, //!< no voip traffic + CONNAPI_VOIPTOPOLOGY_PEERWEB, //!< peer to peer full mesh + CONNAPI_VOIPTOPOLOGY_SERVERHOSTED //!< peer to peer routed via server +} ConnApiVoipTopologyE; + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ +//! connection timers +typedef struct ConnApiConnTimersT +{ + uint32_t uCreateSATime; //!< time it takes to resolve secure association (xbox one) + uint32_t uConnectTime; //!< time it takes for intial connection attempt + uint32_t uDemangleTime; //!< time it takes to attempt demangling on the connection + uint32_t uDemangleConnectTime; //!< time it takes to attempt a connection after demangling +} ConnApiConnTimersT; + +//! connection info +typedef struct ConnApiConnInfoT +{ + uint16_t uLocalPort; //!< local (bind) port + uint16_t uMnglPort; //!< demangled port, if any + uint8_t bDemangling; //!< is demangling process ongoing? (internal use only - see notes in connapi.c file header) + uint8_t uConnFlags; //!< connection status flags (CONNAPI_CONNSTAT_*) + uint8_t _pad[2]; + ConnApiConnStatusE eStatus; //!< status of connection (CONNAPI_STATUS_*) + uint32_t iConnStart; //!< NetTick() recorded at connection or demangling start (internal use only) + ConnApiConnTimersT ConnTimers; //!< connection timers +} ConnApiConnInfoT; + +//! client info +typedef struct ConnApiClientInfoT +{ + uint32_t uId; //!< unique connapi client id, uId should never be 0. A client with uId of 0 will be consider invalid. + uint32_t uAddr; //!< external internet address of user + uint8_t bIsConnectivityHosted; //!< flag for whether the connectivity to this client is direct or server-hosted + uint32_t uRemoteClientId; //!< remote client id (used when setting up tunnel, game connection and voip connection with this client) + uint32_t uLocalClientId; //!< local client id (used when setting up tunnel, game connection and voip connection with this client) + uint32_t uHostingServerId; //!< id of the hosting server or 0 if none + uint32_t uLocalAddr; //!< internal address of user + uint16_t uGamePort; //!< external (send) port to use for game connection, or zero to use global port + uint16_t uVoipPort; //!< external (send) port to use for voip connection, or zero to use global port + uint16_t uLocalGamePort; //!< local (bind) port to use for game connection, or zero to use global port + uint16_t uLocalVoipPort; //!< local (bind) port to use for voip connection, or zero to use global port + uint16_t uTunnelPort; //!< user's tunnel port, or zero to use default + uint16_t uLocalTunnelPort; //!< user's local tunnel port + uint8_t bEnableQos; //!< enable QoS for this client, call ConnApiControl() with 'sqos' and 'lqos' to configure QoS settings + uint8_t _pad[3]; + DirtyAddrT DirtyAddr; //!< dirtyaddr address of client + char strTunnelKey[PROTOTUNNEL_MAXKEYLEN]; //!< tunnel key +} ConnApiClientInfoT; + +//! connection type +typedef struct ConnApiClientT +{ + ConnApiClientInfoT ClientInfo; + ConnApiConnInfoT GameInfo; //!< info about game connection + ConnApiConnInfoT VoipInfo; //!< info about voip connection + NetGameUtilRefT *pGameUtilRef; //!< util ref for connection + NetGameLinkRefT *pGameLinkRef; //!< link ref for connection + NetGameDistRefT *pGameDistRef; //!< dist ref for connection (for app use; not managed by ConnApi) + int32_t iTunnelId; //!< tunnel identifier (if any) + uint16_t uConnFlags; //!< CONNAPI_CONNFLAG_* describing the connection type (read-only) + int16_t iVoipConnId; //!< voip connection identifier + uint16_t uFlags; //!< internal client flags + uint8_t bAllocated; //!< TRUE if this slot is allocated, else FALSE + uint8_t bEstablishVoip; //!< used to establish voip when enable QoS is set (after QoS has been validated), call ConnApiControl() with 'estv' to configure +} ConnApiClientT; + +//! connection list type +typedef struct ConnApiClientListT +{ + int32_t iNumClients; //!< number of clients in list + int32_t iMaxClients; //!< max number of clients + ConnApiClientT Clients[1]; //!< client array (variable length) +} ConnApiClientListT; + +//! callback info +typedef struct ConnApiCbInfoT +{ + int32_t iClientIndex; //!< index of client event is for + uint32_t eType; //!< type of event (CONNAPI_CBTYPE_*) + uint32_t eOldStatus; //!< old status (CONNAPI_STATUS_*) + uint32_t eNewStatus; //!< new status (CONNAPI_STATUS_*) + const ConnApiClientT* pClient; //!< pointer to the corresponding client structure +} ConnApiCbInfoT; + +//! opaque module ref +typedef struct ConnApiRefT ConnApiRefT; + +/*! + \Callback ConnApiCallbackT + + \Description + Callback fired when a connection event happens identified by the information + in pCbInfo + + \Input *pConnApi - module state + \Input *pCbInfo - callback information + \Input *pUserData - user information passed along with the callback +*/ +typedef void (ConnApiCallbackT)(ConnApiRefT *pConnApi, ConnApiCbInfoT *pCbInfo, void *pUserData); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +//! create the module state +#define ConnApiCreate(_iGamePort, _iMaxClients, _pCallback, _pUserData) ConnApiCreate2(_iGamePort, _iMaxClients, _pCallback, _pUserData, (CommAllConstructT *)CommUDPConstruct) + +// create the module state +DIRTYCODE_API ConnApiRefT *ConnApiCreate2(int32_t iGamePort, int32_t iMaxClients, ConnApiCallbackT *pCallback, void *pUserData, CommAllConstructT *pConstruct); + +// this function should be called once the user has logged on and the input parameters are available +DIRTYCODE_API void ConnApiOnline(ConnApiRefT *pConnApi, const char *pGameName, uint32_t uSelfId, ConnApiGameTopologyE eGameTopology, ConnApiVoipTopologyE eVoipTopology); + +// destroy the module state +DIRTYCODE_API void ConnApiDestroy(ConnApiRefT *pConnApi); + +// host or connect to a game / voip, with the possibility to import connection. +DIRTYCODE_API void ConnApiConnect(ConnApiRefT *pConnApi, ConnApiClientInfoT *pClientList, int32_t iClientListSize, int32_t iGameHostIndex, int32_t iVoipHostIndex, int32_t iSessId); + +// add a new client to a pre-existing game in the specified index. +DIRTYCODE_API int32_t ConnApiAddClient(ConnApiRefT *pConnApi, ConnApiClientInfoT *pClientInfo, int32_t iClientIdx); + +// return the ConnApiClientT for the specified client (by id) +DIRTYCODE_API uint8_t ConnApiFindClient(ConnApiRefT *pConnApi, ConnApiClientInfoT *pClientInfo, ConnApiClientT *pOutClient); + +// remove a current client from a game +DIRTYCODE_API void ConnApiRemoveClient(ConnApiRefT *pConnApi, int32_t iClientIdx); + +// redo all connections, using the host specified +DIRTYCODE_API void ConnApiMigrateGameHost(ConnApiRefT *pConnApi, int32_t iNewGameHostIndex); + +// disconnect from game +DIRTYCODE_API void ConnApiDisconnect(ConnApiRefT *pConnApi); + +// get list of current connections +DIRTYCODE_API const ConnApiClientListT *ConnApiGetClientList(ConnApiRefT *pConnApi); + +// connapi status +DIRTYCODE_API int32_t ConnApiStatus(ConnApiRefT *pConnApi, int32_t iSelect, void *pBuf, int32_t iBufSize); + +// connapi status (version 2) +DIRTYCODE_API int32_t ConnApiStatus2(ConnApiRefT *pConnApi, int32_t iSelect, void *pData, void *pBuf, int32_t iBufSize); + +// connapi control +DIRTYCODE_API int32_t ConnApiControl(ConnApiRefT *pConnApi, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// update connapi module (must be called directly if auto-update is disabled) +DIRTYCODE_API void ConnApiUpdate(ConnApiRefT *pConnApi); + +// Register a new callback +DIRTYCODE_API int32_t ConnApiAddCallback(ConnApiRefT *pConnApi, ConnApiCallbackT *pCallback, void *pUserData); + +// Removes a callback previously registered +DIRTYCODE_API int32_t ConnApiRemoveCallback(ConnApiRefT *pConnApi, ConnApiCallbackT *pCallback, void *pUserData); + +#ifdef __cplusplus +}; +#endif + +//@} + +#endif // _connapi_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamedist.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamedist.h new file mode 100644 index 00000000..b2b617d5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamedist.h @@ -0,0 +1,177 @@ +/*H*************************************************************************************/ +/*! + \File netgamedist.h + + \Description + The GameDist class provides the main control interface between the + individual game controllers and the distributed game-input engine. + The GameInput and GameComm classes provided the interfaces used by + this class for real-world communication. + + \Copyright + Copyright (c) Electronic Arts 1998-2007. + + \Version 1.0 12/15/1998 (gschaefer) First Version + \Version 1.1 12/26/1999 (gschaefer) Revised to add external stats interface + \Version 1.2 11/18/2002 (jbrookes) Moved to NetGame hierarchy +*/ +/*************************************************************************************H*/ + +#ifndef _netgamedist_h +#define _netgamedist_h + +/*! +\Moduledef NetGameDist NetGameDist +\Modulemember Game +*/ +//@{ + + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/game/netgameerr.h" +#include "DirtySDK/game/netgamepkt.h" + +/*** Defines ***************************************************************************/ + +#define GMDIST_DEFAULT_BUFFERSIZE_IN (16384) //local packets lost: number of packets (from peer) lost + uint8_t naksent; //!< number of NAKs received by peer (from us) +} NetGameDistStatT; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// construct the game client +DIRTYCODE_API NetGameDistRefT *NetGameDistCreate(void *pLinkRef, NetGameDistStatProc *pStatProc, NetGameDistSendProc *pSendProc, NetGameDistRecvProc *pRecvProc, uint32_t uInBufferSize, uint32_t uOutBufferSize ); + +// destruct the game client +DIRTYCODE_API void NetGameDistDestroy(NetGameDistRefT *pRef); + +// status function +DIRTYCODE_API int32_t NetGameDistStatus(NetGameDistRefT *pRef, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize); + +// for game-server usage, make NetGameDist send multi-packet +DIRTYCODE_API void NetGameDistSetServer(NetGameDistRefT *pRef, uint8_t bActAsServer); + +// on the server: iDist index will be the index of the player that owns the dist +// on the client: iDist index will be the index of the local player that owns the dist +// iTotplayers will always be the total players in game +DIRTYCODE_API void NetGameDistMultiSetup(NetGameDistRefT *pRef, int32_t iDistIndex, int32_t iTotPlrs ); + +// set whether to enable meta-information sending, the mask and the version number +DIRTYCODE_API void NetGameDistMetaSetup(NetGameDistRefT *pRef, uint8_t bSparse, uint32_t uMask, uint32_t uVersion); + +// peek input from peer +DIRTYCODE_API int32_t NetGameDistInputPeek(NetGameDistRefT *pRef, uint8_t *pType, void *pPeer, int32_t *pPlen); + +// return completed input from client +DIRTYCODE_API int32_t NetGameDistInputQuery(NetGameDistRefT *pRef, void *pOurs, int32_t *pOlen, void *pPeer, int32_t *pPlen); + +// return completed input from multiple clients +DIRTYCODE_API int32_t NetGameDistInputQueryMulti(NetGameDistRefT *pRef, uint8_t *pDataTypes, void **pInputs, int32_t *pLen); + +// provide local input data +DIRTYCODE_API int32_t NetGameDistInputLocal(NetGameDistRefT *pRef, void *pBuffer, int32_t iLen); + +// provide local input data (n-consoles). Client usage is to provide one-long arrays. Type can be GMDIST_DATA_INPUT, GMDIST_DATA_INPUT_DROPPABLE +DIRTYCODE_API int32_t NetGameDistInputLocalMulti(NetGameDistRefT *pRef, uint8_t *pTypesArray, void **ppBuffer, int32_t *pLengthsArray, int32_t iDelta); + +// check input status (see how long till next) +DIRTYCODE_API void NetGameDistInputCheck(NetGameDistRefT *pRef, int32_t *pSend, int32_t *pRecv); + +// set new input exchange rate +DIRTYCODE_API void NetGameDistInputRate(NetGameDistRefT *pRef, int32_t iRate); + +// flush the input queue (note that this must be done with +DIRTYCODE_API void NetGameDistInputClear(NetGameDistRefT *pRef); + +// set NetGameDist operation parameters +DIRTYCODE_API int32_t NetGameDistControl(NetGameDistRefT *pRef, int32_t iSelect, int32_t iValue, void *pValue); + +// dispatch to appropriate updaters +DIRTYCODE_API uint32_t NetGameDistUpdate(NetGameDistRefT *pRef); + +// override the send,recv,stats procs or set the drop proc +DIRTYCODE_API void NetGameDistSetProc(NetGameDistRefT *pRef, int32_t iKind, void *pProc); + +// For the game server to provides stats, the array must be sized according to what was passed to NetGameDistMultiSetup +DIRTYCODE_API void NetGameDistSendStats(NetGameDistRefT *pRef, NetGameDistStatT *pStats); + +// return non-zero if there ever was an overflow +DIRTYCODE_API int32_t NetGameDistGetError(NetGameDistRefT *pRef); + +// copies into the passed buffer the last error text. +DIRTYCODE_API void NetGameDistGetErrorText(NetGameDistRefT *pRef, char *pErrorBuffer, int32_t iBufSize); + +// reset the error count +DIRTYCODE_API void NetGameDistResetError(NetGameDistRefT *pRef); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _netgamedist_h + + + + + + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamedistserv.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamedistserv.h new file mode 100644 index 00000000..3da9835f --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamedistserv.h @@ -0,0 +1,97 @@ +/*H********************************************************************************/ +/*! + \File netgamedistserv.h + + \Description + Server module to handle 2+ NetGameDist connections in a client/server + architecture. + + \Copyright + Copyright (c) 2007 Electronic Arts + + \Version 02/01/2007 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _netgamedistserv_h +#define _netgamedistserv_h + +/*! +\Moduledef NetGameDistServ NetGameDistServ +\Modulemember Game +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/game/netgamelink.h" +#include "DirtySDK/game/netgamedist.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque module state +typedef struct NetGameDistServT NetGameDistServT; + +//! logging function type +typedef int32_t (NetGameDistServLoggingCbT)(const char *pText, void *pUserData); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create the module state +DIRTYCODE_API NetGameDistServT *NetGameDistServCreate(int32_t iMaxClients, int32_t iVerbosity); + +// destroy the module state +DIRTYCODE_API void NetGameDistServDestroy(NetGameDistServT *pDistServ); + +// add a client to module +DIRTYCODE_API int32_t NetGameDistServAddClient(NetGameDistServT *pDistServ, int32_t iClient, NetGameLinkRefT *pLinkRef, const char *pClientName); + +// del a client from module +DIRTYCODE_API int32_t NetGameDistServDelClient(NetGameDistServT *pDistServ, int32_t iClient); + +// notify that a client disconnected +DIRTYCODE_API int32_t NetGameDistServDiscClient(NetGameDistServT *pDistServ, int32_t iClient); + +// update a client +DIRTYCODE_API int32_t NetGameDistServUpdateClient(NetGameDistServT *pDistServ, int32_t iClient); + +// update the module (must be called at fixed rate) +DIRTYCODE_API void NetGameDistServUpdate(NetGameDistServT *pDistServ); + +// whether the highwater mark changed, and the current highwater values. +DIRTYCODE_API uint8_t NetGameDistServHighWaterChanged(NetGameDistServT *pDistServ, int32_t* pHighWaterInputQueue, int32_t* pHighWaterOutputQueue); + +// return the lastest error reported by netgamedist, for client iClient +DIRTYCODE_API char* NetGameDistServExplainError(NetGameDistServT *pDistServ, int32_t iClient); + +// netgamedistserv control +DIRTYCODE_API int32_t NetGameDistServControl(NetGameDistServT *pDistServ, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// netgamedistserv status +DIRTYCODE_API int32_t NetGameDistServStatus(NetGameDistServT *pDistServ, int32_t iSelect, void *pBuf, int32_t iBufSize); + +// netgamedistserv2 status +DIRTYCODE_API int32_t NetGameDistServStatus2(NetGameDistServT *pDistServ, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize); + +// set logging callback +DIRTYCODE_API void NetGameDistServSetLoggingCallback(NetGameDistServT *pDistServ, NetGameDistServLoggingCbT *pLoggingCb, void *pUserData); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _netgamedistserv_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgameerr.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgameerr.h new file mode 100644 index 00000000..287c3844 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgameerr.h @@ -0,0 +1,67 @@ +/*H********************************************************************************/ +/*! + \File netgameerr.h + + \Description + Error codes for GameDist functions (errors always negative) + + \Copyright + Copyright (c) Electronic Arts 1998-2007 + + \Version 1.0 23/06/2009 (jrainy) First Version +*/ +/********************************************************************************H*/ + +#ifndef _netgameerr_h +#define _netgameerr_h + +/*! +\Moduledef NetGameErr NetGameErr +\Modulemember Game +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#define GMDIST_ORPHAN (-1) //local packets lost: number of packets (from peer) lost since start - not always equal to (rpacksent - lpackrcvd) + uint32_t lpacksaved; //!< number of packets recovered by underlying commudp redundancy mechanism + uint32_t rnaksent; //!< number of NAKs sent by peer (to us) since start + uint32_t rpacksent; //!< number of packets sent by peer (to us) since start + uint32_t rpackrcvd; //!< number of packets received by peer (from us) since start + uint32_t rpacklost; //!< local->remote packets lost: number of packets (from us) lost by peer since start - not always equal to (lpacksent - rpackrcvd) + /* Notes + Attempting to calculate the number of local->remote packets lost with (lpacksent - rpackrcvd) sometimes + lead to a boosted packet loss result because some in-flight packets are accounted in the sent counter but not + in the rcved counter. For a precise local->remote packet loss value, rpacklost is better because + it is obtained from the remote end directly (from the inbound NetGameLinkSyncT packet) + + Attempting to calculate the number of remote->local packets lost with (rpacksent - lpackrcvd) sometimes + lead to reduced (or even negative) packet loss result because additional inbound packets are + received when commudp retransmission mechanisms quick in to guarantee transport reliability. Those + packet are accounted for in the rcved counter but not in the sent counter. For a precise + remote->local packet loss value, lpacklost is better because it takes the above phenomenon + into account. */ + + uint8_t isconn; //!< listening or connecting \Deprecated + uint8_t isopen; //!< listening, connecting, or connected + uint8_t pad0[2]; //!< pad to four-byte alignment + + uint32_t pingtick; //!< last tick at which ping stuff updated + uint32_t pingslot; //!< the index containing the current ping + int32_t pingdev; //!< ping deviation + int32_t pingavg; //!< ping average + + NetGameLinkHistT pinghist[PING_HISTORY]; +} NetGameLinkStatT; + +// stream structure +typedef struct NetGameLinkStreamT NetGameLinkStreamT; + +//! dist stream send proc +typedef int32_t (NetGameLinkStreamSendProc)(NetGameLinkStreamT *pStream, int32_t iSubchan, int32_t iKind, void *pBuffer, int32_t iLen); +//! dist stream recv proc +typedef void (NetGameLinkStreamRecvProc)(NetGameLinkStreamT *pStream, int32_t iSubchan, int32_t iKind, void *pBuffer, int32_t iLen); + +typedef struct NetGameLinkStreamInpT +{ + char *pInpData; //!< private + int32_t iInpSize; //!< private + int32_t iInpProg; //!< private + int32_t iInpKind; //!< private +} NetGameLinkStreamInpT; + +//! structure to access network stream with +struct NetGameLinkStreamT +{ + NetGameLinkStreamT *pNext; //!< private + NetGameLinkRefT *pClient; //!< private + + int32_t iIdent; //!< private (can read -- stream identifier) + int32_t iSubchan; //!< number of subchannels + int32_t iRefNum; //!< public (for callers use) + void *pRefPtr; //!< public (for callers use) + NetGameLinkStreamSendProc *Send;//!< public + NetGameLinkStreamRecvProc *Recv;//!< public + int32_t iQueueDepth; //!< public read only (total bytes in output queue) + int32_t iQueueLimit; //!< public read/write (maximum outgoing queue limit) + + int32_t iHighWaterUsed; //!< public read only + int32_t iHighWaterNeeded; //!< public read only + + int32_t iInpMaxm; //!< private + NetGameLinkStreamInpT *pInp; //!< private + + char *pOutData; //!< private + int32_t iOutMaxm; //!< private + int32_t iOutSize; //!< private + int32_t iOutProg; //!< private + + char *pSynData; //!< private + int32_t iSynMaxm; //!< private + int32_t iSynSize; //!< private +}; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// construct the game client +DIRTYCODE_API NetGameLinkRefT *NetGameLinkCreate(void *pCommRef, int32_t iOwner, int32_t iBufLen); + +// destruct the game client +DIRTYCODE_API void NetGameLinkDestroy(NetGameLinkRefT *pRef); + +// register a callback function +DIRTYCODE_API void NetGameLinkCallback(NetGameLinkRefT *pRef, void *pCallData, void (*pCallProc)(void *pCallData, int32_t iKind)); + +// status function +DIRTYCODE_API int32_t NetGameLinkStatus(NetGameLinkRefT *pRef, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize); + +// incoming packet stream from upper layer +DIRTYCODE_API int32_t NetGameLinkSend(NetGameLinkRefT *pRef, NetGamePacketT *pPkt, int32_t iLen); + +// peek into the buffer +DIRTYCODE_API int32_t NetGameLinkPeek(NetGameLinkRefT *pRef, NetGamePacketT **ppPkt); + +// peek into the buffer for specific packet types +DIRTYCODE_API int32_t NetGameLinkPeek2(NetGameLinkRefT *pRef, NetGamePacketT **ppPkt, uint32_t uMask); + +// outgoing packet stream to upper layer +DIRTYCODE_API int32_t NetGameLinkRecv(NetGameLinkRefT *pRef, NetGamePacketT *pBuf, int32_t iLen, uint8_t bDist); + +// same as NetGameLinkRecv, but takes a mask of which GAME_PACKET types we want +DIRTYCODE_API int32_t NetGameLinkRecv2(NetGameLinkRefT *pRef, NetGamePacketT *pBuf, int32_t iLen, uint32_t uMask); + +// control behavior +DIRTYCODE_API int32_t NetGameLinkControl(NetGameLinkRefT *pRef, int32_t iSelect, int32_t iValue, void *pValue); + +// dispatch to appropriate updaters +DIRTYCODE_API uint32_t NetGameLinkUpdate(NetGameLinkRefT *pRef); + +// create a new network stream +DIRTYCODE_API NetGameLinkStreamT *NetGameLinkCreateStream(NetGameLinkRefT *pRef, int32_t iSubChan, int32_t iIdent, int32_t iInpLen, int32_t iOutLen, int32_t iSynLen); + +// destroy an existing network stream +DIRTYCODE_API void NetGameLinkDestroyStream(NetGameLinkRefT *pRef, NetGameLinkStreamT *pStream); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _netgamelink_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamepkt.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamepkt.h new file mode 100644 index 00000000..72d0bdfb --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgamepkt.h @@ -0,0 +1,159 @@ +/*H*************************************************************************************/ +/*! + \File netgamepkt.h + + \Description + Defines netgame packet constants and types. + + \Copyright + Copyright (c) Electronic Arts 1998-2007. + + \Version 1.0 12/12/1998 (gschaefer) First Version + \Version 1.1 11/18/2002 (jbrookes) Moved to NetGame hierarchy + \Version 1.2 02/04/2007 (jbrookes) Cleanup, added NetGameMaxPacketT +*/ +/*************************************************************************************H*/ + +#ifndef _netgamepkt_h +#define _netgamepkt_h + +/*! +\Moduledef NetGamePkt NetGamePkt +\Modulemember Game +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/* sampling period constants + dont enum these or you will regret it + (the compiler translates divides by these into logical shifts) */ +#define RTT_SAMPLE_PERIOD 1024 +#define LATE_SAMPLE_PERIOD 512 + +//! identifiers of client/server packets +enum +{ + GAME_PACKET_ONE_BEFORE_FIRST =1,//!< enum with value 1 available for future use. If needed just lower ONE_BEFORE_FIRST to zero + GAME_PACKET_INPUT, //!< data exchanged by machines using dist, usually controller inputs + GAME_PACKET_INPUT_MULTI, //!< same as INPUT but from the game server. + GAME_PACKET_STATS, //!< stats sent by the game server. + GAME_PACKET_USER, //!< user packet + GAME_PACKET_USER_UNRELIABLE, //!< user packet, to be sent unreliably + GAME_PACKET_USER_BROADCAST, //!< user packet, to be sent unreliably to broadcast address + GAME_PACKET_INPUT_FLOW, //!< dist flow control, sent to indicate readiness + GAME_PACKET_INPUT_MULTI_FAT, //!< same as INPUT_MULTI but 16-bit individual packet sizes (to allow 256-1200 sizes). + GAME_PACKET_INPUT_META, //!< meta-information on the packet format + GAME_PACKET_LINK_STRM, //!< netgamelink stream packet + GAME_PACKET_ONE_PAST_LAST, //!< used to mark the valid range. Insert new kinds before + GAME_PACKET_SYNC = 64 //!< OR this value with packet kind to signal that packet conveys sync info +}; + +//! default max raw data packet size +#define NETGAME_DATAPKT_DEFSIZE (240) + +//! max raw data packet size +#define NETGAME_DATAPKT_MAXSIZE (1200) + +//! max size of packet tail (sync+kind) +#define NETGAME_DATAPKT_MAXTAIL (sizeof(NetGamePacketSyncT)+1) + +//! max stream packet size +#define NETGAME_STRMPKT_DEFSIZE (200) +#define NETGAME_STRMPKT_MAXSIZE (NETGAME_DATAPKT_MAXSIZE - 12) + +//! default max input/output buffers, in packets +#define NETGAME_DATABUF_MAXSIZE (32) + +// sync packet flags + +//!< indicates repl field is not yet valid (remote side has not received our first sync packet yet) +#define NETGAME_SYNCFLAG_REPLINVALID (0x01) + + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! packet header (common for every packet) +typedef struct NetGamePacketHeadT +{ + int32_t size; //!< total size (including header, 4-byte aligned) + uint32_t when; //!< reception time in ticks + uint16_t len; //!< length of packet body + uint8_t kind; //!< packet type (see enum above) + uint8_t pad; //!< unused (alignment) +} NetGamePacketHeadT; + +//! format of a sync packet (piggybacks onto regular packets) -- FOR INTERNAL USE ONLY +typedef struct NetGamePacketSyncT +{ + uint32_t repl; + uint32_t echo; + int16_t late; + uint8_t psnt; //!< number of packets sent since last update + uint8_t prcvd; //!< number of packets received since last update + uint8_t plost; //!< number of inbound packets lost since last update + uint8_t nsnt; //!< number of NAKs sent since last update + uint8_t flags; //!< sync packet flags (NETGAME_SYNCFLAG_*) + uint8_t size; //!< size of sync packet (MUST COME LAST) +} NetGamePacketSyncT; + +//! format of packets which are exchanged by client/server +typedef struct NetGamePacketT +{ + //! packet header (not sent) + NetGamePacketHeadT head; + + //! game packet body + union { + //! stream data + struct { + int32_t ident; //!< stream ident + int32_t kind; //!< token + int32_t size; //!< packet size + uint8_t data[NETGAME_STRMPKT_DEFSIZE];//!< binary data + } strm; + //! raw data packet + uint8_t data[NETGAME_DATAPKT_DEFSIZE]; + } body; + + //! space for game packet tail + uint8_t tail[NETGAME_DATAPKT_MAXTAIL]; +} NetGamePacketT; + +//! format of a max-sized link packet (may be substituted for a NetGamePacketT) +typedef struct NetGameMaxPacketT +{ + //! packet header (not sent) + NetGamePacketHeadT head; + + //! game packet body + union { + //! stream data + struct { + int32_t ident; //!< stream ident + int32_t kind; //!< token + int32_t size; //!< packet size + uint8_t data[NETGAME_STRMPKT_MAXSIZE];//!< binary data + } strm; + //! raw data packet + uint8_t data[NETGAME_DATAPKT_MAXSIZE]; + } body; + + //! space for game packet tail + uint8_t tail[NETGAME_DATAPKT_MAXTAIL]; +} NetGameMaxPacketT; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +//@} + +#endif // _netgamepkt_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgameutil.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgameutil.h new file mode 100644 index 00000000..12cd5ae1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/game/netgameutil.h @@ -0,0 +1,91 @@ +/*H*************************************************************************************/ +/*! + \File netgameutil.h + + \Description + Group of functions to help setup network games + + \Copyright + Copyright (c) Electronic Arts 2001-2002 + + \Version 1.0 01/09/2001 (gschaefer) First Version + \Version 1.1 11/18/2002 (jbrookes) Moved to NetGame hierarchy +*/ +/*************************************************************************************H*/ + +#ifndef _netgameutil_h +#define _netgameutil_h + +/*! +\Moduledef NetGameUtil NetGameUtil +\Modulemember Game +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/comm/commall.h" + +/*** Defines ***************************************************************************/ + +#define NETGAME_CONN_LISTEN (1) //!< listen for connect +#define NETGAME_CONN_CONNECT (2) //!< connect to peer +#define NETGAME_CONN_AUTO (NETGAME_CONN_LISTEN|NETGAME_CONN_CONNECT) //!< (debug only) autoconnect + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct NetGameUtilRefT NetGameUtilRefT; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// construct the game setup module +DIRTYCODE_API NetGameUtilRefT *NetGameUtilCreate(void); + +// destroy the game setup module +DIRTYCODE_API void NetGameUtilDestroy(NetGameUtilRefT *ref); + +// reset the game setup module +DIRTYCODE_API void NetGameUtilReset(NetGameUtilRefT *ref); + +// set internal GameUtil parameters +DIRTYCODE_API void NetGameUtilControl(NetGameUtilRefT *pRef, int32_t iKind, int32_t iValue); + +// get a connection (connect/listen) +DIRTYCODE_API int32_t NetGameUtilConnect(NetGameUtilRefT *ref, int32_t conn, const char *addr, CommAllConstructT *pConstruct); + +// check connection status +DIRTYCODE_API void *NetGameUtilComplete(NetGameUtilRefT *ref); + +// return status info +DIRTYCODE_API int32_t NetGameUtilStatus(NetGameUtilRefT *ref, int32_t iSelect, void *pBuf, int32_t iBufSize); + +// send out an advertisement +DIRTYCODE_API void NetGameUtilAdvert(NetGameUtilRefT *ref, const char *kind, const char *name, const char *note); + +// cancel current advertisement +DIRTYCODE_API void NetGameUtilWithdraw(NetGameUtilRefT *ref, const char *kind, const char *name); + +// find ip address of a specific advertisement +DIRTYCODE_API uint32_t NetGameUtilLocate(NetGameUtilRefT *ref, const char *kind, const char *name); + +// return a list of all advertisements +DIRTYCODE_API int32_t NetGameUtilQuery(NetGameUtilRefT *ref, const char *kind, char *buf, int32_t max); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _netgameutil_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtygif.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtygif.h new file mode 100644 index 00000000..0bec40ba --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtygif.h @@ -0,0 +1,97 @@ +/*H********************************************************************************/ +/*! + \File dirtygif.h + + \Description + Routines to parse and decode a GIF file. + + \Copyright + Copyright (c) 2003-2020 Electronic Arts Inc. + + \Version 11/13/2003 (jbrookes) First Version + \Version 01/28/2020 (jbrookes) Added multiframe (animated) support +*/ +/********************************************************************************H*/ + +#ifndef _dirtygif_h +#define _dirtygif_h + +/*! +\Moduledef DirtyGif DirtyGif +\Modulemember Graph +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! structure to represent a parsed gif/gif frame +typedef struct DirtyGifHdrT +{ + // image info + const uint8_t *pImageData; //!< pointer to start of gif bits + const uint8_t *pImageEnd; //!< pointer to end of gif bits + const uint8_t *pColorTable; //!< pointer to gif color table + + uint32_t uNumFrames; //!< number of image frames + + uint16_t uTop; //!< top offset of image frame + uint16_t uLeft; //!< left offset of image frame + uint16_t uWidth; //!< image width + uint16_t uHeight; //!< image height + uint16_t uNumColors; //!< number of color table entries + uint16_t uDelay; //!< time delay after displaying current frame before moving to next in 1/100ths of a second + + // misc info + uint8_t bInterlaced; //!< flag indicating an interlaced image + uint8_t bHasAlpha; //!< flag indicating transparent color present + uint8_t uTransColor; //!< which color index is transparent + uint8_t pad; +} DirtyGifHdrT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// identify if image is a gif or not +DIRTYCODE_API int32_t DirtyGifIdentify(const uint8_t *pImageData, uint32_t uImageLen); + +// parse GIF into something we can use +DIRTYCODE_API int32_t DirtyGifParse(DirtyGifHdrT *pGifHdr, const uint8_t *pGifData, const uint8_t *pGifEnd); + +// parse GIF with extended info +DIRTYCODE_API int32_t DirtyGifParseEx(DirtyGifHdrT *pGifHdr, const uint8_t *pGifData, const uint8_t *pGifEnd, DirtyGifHdrT *pFrames, uint32_t uNumFrames); + +// decode a GIF palette to 32bit color table +DIRTYCODE_API int32_t DirtyGifDecodePalette(DirtyGifHdrT *pGifHdr, uint8_t *pPalette, uint8_t *pPaletteEnd, uint8_t uAlpha); + +// decode a GIF image to 8bit paletted image +DIRTYCODE_API int32_t DirtyGifDecodeImage(DirtyGifHdrT *pGifHdr, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight, uint32_t bVflip); + +// decode a GIF image to 32bit direct-color image +DIRTYCODE_API int32_t DirtyGifDecodeImage32(DirtyGifHdrT *pGifHdr, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight, uint32_t bVflip); + +// decode a GIF image to one or more 32bit direct-color images +DIRTYCODE_API int32_t DirtyGifDecodeImage32Multi(DirtyGifHdrT *pGifHdr, DirtyGifHdrT *pFrameInfo, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight, int32_t iNumFrames, uint32_t bVflip); + +// decode a specific frame of a GIF image into a single 32bit ARGB direct-color bitmap. +DIRTYCODE_API int32_t DirtyGifDecodeImage32Frame(DirtyGifHdrT *pGifHdr, DirtyGifHdrT *pFrameInfo, uint8_t *pImageData, uint8_t *p8BitImage, int32_t iBufWidth, int32_t iBufHeight, int32_t iFrame, int32_t iNumFrames, uint32_t bVflip); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtygif_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtygraph.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtygraph.h new file mode 100644 index 00000000..68b3948b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtygraph.h @@ -0,0 +1,93 @@ +/*H********************************************************************************/ +/*! + \File dirtygraph.h + + \Description + Routines for decoding an encoded graphics image. + + \Copyright + Copyright (c) 2006-2020 Electronic Arts Inc. + + \Version 03/09/2006 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtygraph_h +#define _dirtygraph_h + +/*! +\Moduledef DirtyGraph DirtyGraph +\Modulemember Graph +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +//! supported image types +typedef enum DirtyGraphImageTypeE +{ + DIRTYGRAPH_IMAGETYPE_UNKNOWN = 0, + DIRTYGRAPH_IMAGETYPE_GIF, + DIRTYGRAPH_IMAGETYPE_JPG, + DIRTYGRAPH_IMAGETYPE_PNG +} DirtyGraphImageTypeE; + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! structure representing parsed image info +typedef struct DirtyGraphInfoT +{ + const uint8_t *pImageData; //!< pointer to start of data + uint32_t uLength; //!< length of file + int16_t iWidth; //!< image width + int16_t iHeight; //!< image height + uint16_t uNumFrames; //!< number of image frames + uint8_t uType; //!< image type + uint8_t _pad; +} DirtyGraphInfoT; + +//! opaque module state +typedef struct DirtyGraphRefT DirtyGraphRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create module state +DIRTYCODE_API DirtyGraphRefT *DirtyGraphCreate(void); + +// done with module state +DIRTYCODE_API void DirtyGraphDestroy(DirtyGraphRefT *pRef); + +// parse the header +DIRTYCODE_API int32_t DirtyGraphDecodeHeader(DirtyGraphRefT *pRef, DirtyGraphInfoT *pInfo, const uint8_t *pImageData, uint32_t uImageLen); + +// get extra image info +DIRTYCODE_API int32_t DirtyGraphGetImageInfo(DirtyGraphRefT *pRef, DirtyGraphInfoT *pInfo, int32_t iSelect, void *pBuffer, int32_t iBufSize); + +// decode an image +DIRTYCODE_API int32_t DirtyGraphDecodeImage(DirtyGraphRefT *pRef, DirtyGraphInfoT *pInfo, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight); + +// decode a multiframe image +DIRTYCODE_API int32_t DirtyGraphDecodeImageMulti(DirtyGraphRefT *pRef, DirtyGraphInfoT *pInfo, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight); + +// decode a single frame of a multiframe image +DIRTYCODE_API int32_t DirtyGraphDecodeImageFrame(DirtyGraphRefT *pDirtyGraph, DirtyGraphInfoT *pInfo, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight, int32_t iFrame); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtygraph_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtyjpg.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtyjpg.h new file mode 100644 index 00000000..b948f553 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtyjpg.h @@ -0,0 +1,95 @@ +/*H********************************************************************************/ +/*! + \File dirtyjpg.h + + \Description + Routines to parse and decode a JPEG image file. JFIF and EXIF file formats + are supported. + + \Copyright + Copyright (c) 2006 Electronic Arts Inc. + + \Version 02/23/2006 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtyjpg_h +#define _dirtyjpg_h + +/*! +\Moduledef DirtyJpg DirtyJpg +\Modulemember Graph +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#define DIRTYJPG_ERR_NONE (0) +#define DIRTYJPG_ERR_NOMAGIC (-1) +#define DIRTYJPG_ERR_TOOSHORT (-2) +#define DIRTYJPG_ERR_NOBUFFER (-3) +#define DIRTYJPG_ERR_BADSTATE (-4) +#define DIRTYJPG_ERR_BADFRAME (-5) +#define DIRTYJPG_ERR_BADVERS (-6) +#define DIRTYJPG_ERR_ENDOFDATA (-7) +#define DIRTYJPG_ERR_BADDQT (-8) +#define DIRTYJPG_ERR_BADDHT (-9) +#define DIRTYJPG_ERR_BADSOF0 (-10) +#define DIRTYJPG_ERR_BITDEPTH (-11) // unsupported bitdepth +#define DIRTYJPG_ERR_DECODER (-12) // decoding error +#define DIRTYJPG_ERR_NOSUPPORT (-13) // unsupported feature +#define DIRTYJPG_ERR_NOFORMAT (-14) // unsupported format (not JFIF or EXIF) + +typedef struct DirtyJpgStateT DirtyJpgStateT; + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! structure to represent a parsed gif +typedef struct DirtyJpgHdrT +{ + // image info + const uint8_t *pImageData; //!< pointer to start of data + uint32_t uLength; //!< length of file + uint32_t uWidth; //!< image width + uint32_t uHeight; //!< image height +} DirtyJpgHdrT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create module state +DIRTYCODE_API DirtyJpgStateT *DirtyJpgCreate(void); + +// reset to default +DIRTYCODE_API void DirtyJpgReset(DirtyJpgStateT *pState); + +// done with module state +DIRTYCODE_API void DirtyJpgDestroy(DirtyJpgStateT *pState); + +// identify if image is a jpg or not +DIRTYCODE_API int32_t DirtyJpgIdentify(DirtyJpgStateT *pState, const uint8_t *pImage, uint32_t uLength); + +// parse the header +DIRTYCODE_API int32_t DirtyJpgDecodeHeader(DirtyJpgStateT *pState, DirtyJpgHdrT *pJpgHdr, const uint8_t *pImage, uint32_t uLength); + +// parse the image +DIRTYCODE_API int32_t DirtyJpgDecodeImage(DirtyJpgStateT *pState, DirtyJpgHdrT *pJpgHdr, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtyjpg_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtypng.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtypng.h new file mode 100644 index 00000000..615761f4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/graph/dirtypng.h @@ -0,0 +1,134 @@ +/*H********************************************************************************/ +/*! + \File dirtypng.h + + \Description + Routines to decode a PNG into a raw image and palette. These routines + were written from scratch based on the published specifications. The + functions and style were heavily based on the dirtygif.c and dirtyjpeg.c + files coded by James Brookes. + + Supported features + Bit Depth of 1, 2, 4, and 8 + Colour Type 0, 2, 4, 6 (grayscale, truecolor, grayscale with alpha, truecolor with alpha) + Noncompressed blocks, statically compressed blocks, dynamically compressed blocks + Zlib compression levels 0, 1, 2 and 9 (none through maximum) + Interlace method Adam7 + Filter types 0-4 (none, sub, up, average, paeth) + + Unsupported features + Bit Depth of 16 support + Colour Type 3 (Indexed Color support) + PLTE chunk support + Ancillary chunk support + + \Notes + References: + [1] http://www.w3.org/TR/PNG/ + [2] http://www.gzip.org/zlib/rfc1950.pdf + [3] http://www.gzip.org/zlib/rfc1951.pdf + [4] http://www.zlib.net/feldspar.html + [5] http://www.schaik.com/pngsuite/#basic + + \Copyright + Copyright (c) 2007 Electronic Arts Inc. + + \Version 02/05/2007 (christianadam) First Version +*/ +/********************************************************************************H*/ + +#ifndef _dirtypng_h +#define _dirtypng_h + +/*! +\Moduledef DirtyPng DirtyPng +\Modulemember Graph +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#define DIRTYPNG_ERR_NONE (0) +#define DIRTYPNG_ERR_TOOSHORT (-1) +#define DIRTYPNG_ERR_BADCRC (-2) +#define DIRTYPNG_ERR_BADTYPE (-3) +#define DIRTYPNG_ERR_BADCOLOR (-4) +#define DIRTYPNG_ERR_BADDEPTH (-5) +#define DIRTYPNG_ERR_BADCOMPR (-6) +#define DIRTYPNG_ERR_BADFILTR (-7) +#define DIRTYPNG_ERR_BADINTRL (-8) +#define DIRTYPNG_ERR_ALLOCFAIL (-9) +#define DIRTYPNG_ERR_TYPEMISS (-10) +#define DIRTYPNG_ERR_BADORDER (-11) +#define DIRTYPNG_ERR_TYPEDUPL (-12) +#define DIRTYPNG_ERR_UNKNCRIT (-13) +#define DIRTYPNG_ERR_BADCM (-14) +#define DIRTYPNG_ERR_BADCI (-15) +#define DIRTYPNG_ERR_BADFLG (-16) +#define DIRTYPNG_ERR_FDICTSET (-17) +#define DIRTYPNG_ERR_BADBTYPE (-18) +#define DIRTYPNG_ERR_NOBLKEND (-19) +#define DIRTYPNG_ERR_BADBLKLEN (-20) +#define DIRTYPNG_ERR_MAXCODES (-21) +#define DIRTYPNG_ERR_NOCODES (-22) +#define DIRTYPNG_ERR_BADCODE (-23) +#define DIRTYPNG_ERR_INVFILE (-24) +#define DIRTYPNG_ERR_UNKNOWN (-25) + +typedef struct DirtyPngStateT DirtyPngStateT; + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! structure to represent a parsed png +typedef struct DirtyPngHdrT +{ + // image info + const uint8_t *pImageData; //!< pointer to start of png bits + const uint8_t *pImageEnd; //!< pointer to end of png bits + uint32_t uWidth; //!< image width + uint32_t uHeight; //!< image height + + // misc info + int8_t iBitDepth; //!< bit depth of the image + int8_t iColourType; //!< colour type of the image + int8_t iCompression; //!< compression method of the image + int8_t iFilter; //!< filter method of the image + int8_t iInterlace; //!< interlace method of the image +} DirtyPngHdrT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create module state +DIRTYCODE_API DirtyPngStateT *DirtyPngCreate(void); + +// done with module state +DIRTYCODE_API void DirtyPngDestroy(DirtyPngStateT *pState); + +// identify if image is a png or not +DIRTYCODE_API int32_t DirtyPngIdentify(const uint8_t *pImageData, uint32_t uImageLen); + +// parse PNG into something we can use +DIRTYCODE_API int32_t DirtyPngParse(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr, const uint8_t *pImageData, const uint8_t *pImageEnd); + +// decode a PNG image +DIRTYCODE_API int32_t DirtyPngDecodeImage(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtypng_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/privilegeapi.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/privilegeapi.h new file mode 100644 index 00000000..06edcf2a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/privilegeapi.h @@ -0,0 +1,118 @@ +/*H*************************************************************************************/ +/*! + + \File privilegeapi.h + + \Description + Check first party privileges and parental controls + + \Copyright + Copyright (c) Electronic Arts 2013. + + \Version 09/02/13 (mcorcoran) First Version +*/ +/*************************************************************************************H*/ + +#ifndef _privilegeapi_h +#define _privilegeapi_h + +/*! +\Moduledef PrivilegeApi PrivilegeApi +\Modulemember User +*/ +//@{ + + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct PrivilegeApiRefT PrivilegeApiRefT; + +/*** Variables *************************************************************************/ + +typedef enum PrivilegeApiPrivE +{ + PRIVILEGEAPI_PRIV_INVALID = (0), + PRIVILEGEAPI_PRIV_COMMUNICATION_VOICE_INGAME = (-1), + PRIVILEGEAPI_PRIV_COMMUNICATION_VOICE_SKYPE = (-2), + PRIVILEGEAPI_PRIV_VIDEO_COMMUNICATIONS = (-3), + PRIVILEGEAPI_PRIV_COMMUNICATIONS = (-4), + PRIVILEGEAPI_PRIV_USER_CREATED_CONTENT = (-5), + PRIVILEGEAPI_PRIV_MULTIPLAYER_SESSIONS_REALTIME = (-6), + PRIVILEGEAPI_PRIV_DOWNLOAD_FREE_CONTENT = (-7), + PRIVILEGEAPI_PRIV_FITNESS_UPLOAD = (-8), + PRIVILEGEAPI_PRIV_VIEW_FRIENDS_LIST = (-9), + PRIVILEGEAPI_PRIV_SHARE_KINECT_CONTENT = (-10), + PRIVILEGEAPI_PRIV_MULTIPLAYER_PARTIES = (-11), + PRIVILEGEAPI_PRIV_CLOUD_GAMING_MANAGE_SESSION = (-12), + PRIVILEGEAPI_PRIV_CLOUD_GAMING_JOIN_SESSION = (-13), + PRIVILEGEAPI_PRIV_CLOUD_SAVED_GAMES = (-14), + PRIVILEGEAPI_PRIV_PREMIUM_CONTENT = (-15), + PRIVILEGEAPI_PRIV_INTERNET_BROWSER = (-16), + PRIVILEGEAPI_PRIV_SUBSCRIPTION_CONTENT = (-17), + PRIVILEGEAPI_PRIV_PREMIUM_VIDEO = (-18), + PRIVILEGEAPI_PRIV_GAME_DVR = (-19), + PRIVILEGEAPI_PRIV_SOCIAL_NETWORK_SHARING = (-20), + PRIVILEGEAPI_PRIV_PURCHASE_CONTENT = (-21), + PRIVILEGEAPI_PRIV_PROFILE_VIEWING = (-22), + PRIVILEGEAPI_PRIV_PRESENCE = (-23), + PRIVILEGEAPI_PRIV_CONTENT_AUTHOR = (-24), + PRIVILEGEAPI_PRIV_UNSAFE_PROGRAMMING = (-25), + PRIVILEGEAPI_PRIV_MULTIPLAYER_SESSIONS_ASYNC = (-26), + PRIVILEGEAPI_PRIV_ONLINE_ACCESS = (-27), + PRIVILEGEAPI_PRIV_EA_ACCESS = (256) +} PrivilegeApiPrivE; + +#define PRIVILEGEAPI_STATUS_NONE (0x0000) +#define PRIVILEGEAPI_STATUS_IN_PROGRESS (0x0001) +#define PRIVILEGEAPI_STATUS_GRANTED (0x0002) +#define PRIVILEGEAPI_STATUS_GRANTED_FRIENDS_ONLY (0x0004) +#define PRIVILEGEAPI_STATUS_RESTRICTED (0x0008) +#define PRIVILEGEAPI_STATUS_BANNED (0x0010) +#define PRIVILEGEAPI_STATUS_PURCHASE_REQUIRED (0x0020) +#define PRIVILEGEAPI_STATUS_ABORTED (0x0040) +#define PRIVILEGEAPI_STATUS_ERROR (0x0080) +#define PRIVILEGEAPI_STATUS_ADDITIONAL_CHECKS_REQUIRED (0x0100) +#define PRIVILEGEAPI_STATUS_UNDERAGE (0x0200) + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// creates the module +DIRTYCODE_API PrivilegeApiRefT *PrivilegeApiCreate(void); + +// destroys the module +DIRTYCODE_API int32_t PrivilegeApiDestroy(PrivilegeApiRefT *pRef); + +// checks for the existance of 1 or more privileges for a given user +DIRTYCODE_API int32_t PrivilegeApiCheckPrivilegesAsync(PrivilegeApiRefT *pRef, int32_t iUserIndex, const PrivilegeApiPrivE *pPrivileges, int32_t iPrivilegeCount, int32_t *pHint); + +// checks for the existance of 1 or more privileges for a given user, and show the user a system provided UI in the even tthe user does not have the requested privileges +DIRTYCODE_API int32_t PrivilegeApiCheckPrivilegesAsyncWithUi(PrivilegeApiRefT *pRef, int32_t iUserIndex, const PrivilegeApiPrivE *pPrivileges, int32_t iPrivilegeCount, int32_t *pHint, const char *pUiMessage); + +// checks/polls for the result of a given request id returned from the PrivilegeApiCheckPrivilegesAsync() functions +DIRTYCODE_API int32_t PrivilegeApiCheckResult(PrivilegeApiRefT *pRef, int32_t iRequestId); + +// frees up a request id once you are finished with it +DIRTYCODE_API int32_t PrivilegeApiReleaseRequest(PrivilegeApiRefT *pRef, int32_t iRequestId); + +// aborts a currently executing async request +DIRTYCODE_API int32_t PrivilegeApiAbort(PrivilegeApiRefT *pRef, int32_t iRequestId); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/qosclient.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/qosclient.h new file mode 100644 index 00000000..53475883 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/qosclient.h @@ -0,0 +1,76 @@ +/*H********************************************************************************/ +/*! + \File qosclient.h + + \Description + Main include for the Quality of Service module. + + \Copyright + Copyright (c) 2017 Electronic Arts Inc. + + \Version 1.0 04/07/2017 (cvienneau) 2.0 +*/ +/********************************************************************************H*/ + +#ifndef _qosclient_h +#define _qosclient_h + +/*! +\Moduledef QosClient QosClient +\Modulemember Misc +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/misc/qoscommon.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Forward Declarations *********************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque module ref +typedef struct QosClientRefT QosClientRefT; + +//! event callback function prototype +typedef void (QosClientCallbackT)(QosClientRefT *pQosClient, QosCommonProcessedResultsT *pResults, void *pUserData); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create the module state +DIRTYCODE_API QosClientRefT *QosClientCreate(QosClientCallbackT *pCallback, void *pUserData, uint16_t uListenPort); + +// let the module do processing +DIRTYCODE_API void QosClientUpdate(QosClientRefT *pQosClient); + +// begin QOS process managed by the QOS coordinator +DIRTYCODE_API void QosClientStart(QosClientRefT *pQosClient, const char *pStrCoordinatorAddr, uint16_t uPort, const char * strQosProfile); + +// change the behavior of the QOS system +DIRTYCODE_API int32_t QosClientControl(QosClientRefT *pQosClient, int32_t iControl, int32_t iValue, void *pValue); + +// get the status, of the QOS system +DIRTYCODE_API int32_t QosClientStatus(QosClientRefT *pQosClient, int32_t iSelect, int32_t iData, void *pBuf, int32_t iBufSize); + +// destroy the module state +DIRTYCODE_API void QosClientDestroy(QosClientRefT *pQosClient); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _filename_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/qoscommon.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/qoscommon.h new file mode 100644 index 00000000..2246cfe0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/qoscommon.h @@ -0,0 +1,297 @@ +/*H********************************************************************************/ +/*! + \File qoscommon.h + + \Description + This code declares shared items between client and server of the QOS system. + + \Copyright + Copyright (c) 2017 Electronic Arts Inc. + +*/ +/********************************************************************************H*/ + +#ifndef _qoscommon_h +#define _qoscommon_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtyaddr.h" +#include "DirtySDK/dirtysock/dirtynet.h" + +/*** Defines **********************************************************************/ +#define QOS_COMMON_HMAC_TYPE (CRYPTHASH_SHA256) //!< a secure strong hashing algorithm +#define QOS_COMMON_HMAC_SIZE (32) //!< CRYPTSHA256_HASHSIZE +#define QOS_COMMON_SECURE_KEY_LENGTH (16) //!< length of random data as key, in generation of hmac +#define QOS_COMMON_SIZEOF_PROBE_DATA (45) //!< total byte count of probe, excluding hmac +#define QOS_COMMON_PROBE_PROTOCOL_ID ('qos2') //!< probe packets will all start with this to identify them as being the QOS 2 protocol +#define QOS_COMMON_PROBE_VERSION_MAJOR (2) //!< identifies changes to QosCommonProbePacketT, 2 bytes, first byte indicates api breaking +#define QOS_COMMON_PROBE_VERSION_MINOR (0) //!< second byte compatible bug fixes. +#define QOS_COMMON_PROBE_VERSION ((QOS_COMMON_PROBE_VERSION_MAJOR << 8) | QOS_COMMON_PROBE_VERSION_MINOR) //!< the combined major, minor version bytes. +#define QOS_COMMON_MIN_PACKET_SIZE (QOS_COMMON_SIZEOF_PROBE_DATA + QOS_COMMON_HMAC_SIZE) //!< size of on wire representation of the probe packet +#define QOS_COMMON_MAX_PACKET_SIZE (SOCKET_MAXUDPRECV) //!< maximum probe/packet length +#define QOS_COMMON_MAX_PROBE_COUNT (32) //!< maximum probes per request +#define QOS_COMMON_MAX_SITES (32) //!< maximum sites we can test against at one time +#define QOS_COMMON_MAX_CONTROL_CONFIGS (6) //!< maximum number of QosClientControl selectors we can include in the request +#define QOS_COMMON_MAX_TESTS (4) //!< latency, bandwidthUP, bandwidthDown; and extra room for something we haven't considered +#define QOS_COMMON_MAX_RESULTS (QOS_COMMON_MAX_SITES * QOS_COMMON_MAX_TESTS) //!< highest number of possible results we can generate +#define QOS_COMMON_MAX_URL_LENGTH (256) //!< size used for URL strings +#define QOS_COMMON_MAX_RPC_STRING (128) //!< size used for typical strings +#define QOS_COMMON_MAX_RPC_STRING_SHORT (16) //!< size used for strings we expect to be very short +#define QOS_COMMON_MAX_RPC_BODY_SIZE (1024 * 200) //!< rpc messages should never exceed this size, raw results are our biggest message +#define QOS_COMMON_DEFAULT_RPC_BODY_SIZE (QOS_COMMON_MAX_RPC_BODY_SIZE / 10) //!< An amount of space that will usually be enough to receive an rpc message into +#define QOS_COMMON_CONTENT_TYPE ("application/grpc") //!< http content type, everything is protobuff +#define QOS_COMMON_SERVER_URL ("/eadp.qoscoordinator.QOSCoordinatorRegistration/registerServer") //!< used by server to register to the QOS server, and heartbeat to refresh its availability +#define QOS_COMMON_CLIENT_URL ("/eadp.qoscoordinator.QOSCoordinator/ClientCall") //!< used by client to request config and push back final results + + +// flags contained in probe packets +#define QOS_COMMON_PACKET_FLAG_LAST_PROBE (1) //!< set if the client expects this is the last packet in this test + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! just enough space to hold a address family, port, and an v4 or v6 address. Used for storage or communicating address on the wire, never used to actually send/recv. +typedef struct QosCommonAddrT +{ + union + { + union + { + uint8_t aBytes[16]; + uint16_t aWords[8]; + uint32_t aDwords[4]; + }v6; + uint32_t v4; + }addr; + uint16_t uFamily; + uint16_t uPort; +} QosCommonAddrT; + +//! describes the configuration for a particular QOS test +typedef struct QosCommonTestT +{ + char strTestName[QOS_COMMON_MAX_RPC_STRING_SHORT]; //!< name of the test + char strSiteName[QOS_COMMON_MAX_RPC_STRING_SHORT]; //!< alias of target QOS server + uint16_t uProbeSizeUp; //!< minimum size of the probe, the probe will be padded with extra data to reach this size + uint16_t uProbeSizeDown; //!< minimum size of the probe, the probe will be padded with extra data to reach this size + uint16_t uTimeout; //!< maximum amount of time before giving up on completing the test + uint16_t uMinTimeBetwenProbes; //!< minimum amount of time between each probe sent from the client + uint16_t uTimeTillResend; //!< if we haven't got the expected number of responses back in this much time, we will send more probes + uint16_t uInitSyncTimeout; //!< the amount of time we might wait to synchronize all requests together + uint8_t uProbeCountUp; //!< number of probes to send to the server + uint8_t uProbeCountDown; //!< number of probes there server should respond with for each probe received + uint8_t uResendExtraProbeCount; //!< in the case of finding packet loss, we request the lost packets again with a few extras, to reduce the chance that those are lost too + uint8_t uAcceptableLostProbeCount; //!< typically we would receive (uProbeCountUp * uProbeCountDown) probes, this indicates how many less probes we can receive without triggering a re-send +} QosCommonTestT; + +//! describes QosClientControl overrides that should be executed before doing any QosTests +typedef struct QosCommonControlConfigT +{ + char strValue[QOS_COMMON_MAX_RPC_STRING]; //!< string value, such as QosClientControl(mQosClient, 'sprt', 0, strValue); + char strControl[5]; //!< dirty sdks four character code identifier, usually in the form of 'wxzy' + int32_t iValue; //!< integer value, such as QosClientControl(mQosClient, 'lprt', iValue, NULL); +} QosCommonControlConfigT; + +//! describes a site, QOS Tests are performed against a QOS/ping Site +typedef struct QosCommonSiteT +{ + char strSiteName[QOS_COMMON_MAX_RPC_STRING_SHORT]; //!< identifies where a site is, such as bio-sjc + char strProbeAddr[QOS_COMMON_MAX_RPC_STRING]; //!< url to communicate with the server + uint8_t aSecureKey[QOS_COMMON_SECURE_KEY_LENGTH]; //!< key used in generating hmac + uint16_t uProbePort; //!< port to use when communicating + uint16_t uProbeVersion; //!< the version the site is operating with +} QosCommonSiteT; + +//! a collection of sites, control configs and tests to describe what actions the client should be taking +typedef struct QosCommonClientConfigT +{ + QosCommonSiteT aSites[QOS_COMMON_MAX_SITES]; //!< list of sites that are potential targets + QosCommonControlConfigT aControlConfigs[QOS_COMMON_MAX_CONTROL_CONFIGS]; //!< list of control settings to be run before doing tests + QosCommonTestT aQosTests[QOS_COMMON_MAX_TESTS]; //!< list tests to be done + QosCommonAddrT clientAddressFromCoordinator; //!< the client must report its external address to the QOS server, the coordinator will initialize this to what it sees the clients address as + uint8_t uNumSites; //!< number of QosCommonSiteT contained in aSites + uint8_t uNumControlConfigs; //!< number of QosCommonControlConfigT contained in aControlConfigs + uint8_t uNumQosTests; //!< number of QosCommonTestT contained aQosTests +} QosCommonClientConfigT; + +//!< a firewall or NAT (network address translation) indicates how difficult it is to make a connection with this location +typedef enum QosCommonFirewallTypeE +{ + QOS_COMMON_FIREWALL_UNKNOWN, + QOS_COMMON_FIREWALL_OPEN, + QOS_COMMON_FIREWALL_MODERATE, + QOS_COMMON_FIREWALL_STRICT, + QOS_COMMON_FIREWALL_NUMNATTYPES //!< max number of NAT types, must be at the end +} QosCommonFirewallTypeE; + +//! contains latency and bandwidth results for a given site +typedef struct QosCommonTestResultT +{ + char strSiteName[QOS_COMMON_MAX_RPC_STRING_SHORT]; + uint32_t uMinRTT; + uint32_t uUpbps; + uint32_t uDownbps; + uint32_t hResult; +} QosCommonTestResultT; + +//! the coordinator will examine a QosCommonRawResultsT and produce usable QosCommonProcessedResultsT +typedef struct QosCommonProcessedResultsT +{ + QosCommonTestResultT aTestResults[QOS_COMMON_MAX_SITES]; //!< an ordered list (by "best" site, coordinator's decision) of all test results + QosCommonAddrT clientExternalAddress; //!< this is the address the coordinator recommends using for communication to this client + QosCommonFirewallTypeE eFirewallType; //!< this is what the coordinator classifies the firewall type to be + uint32_t uNumResults; //!< number of results found in aTestResults + uint32_t uTimeTillRetry; //!< if this is != 0, the QOS server wants us to check back in this many ms for a new test + uint32_t hResult; //!< holds the error/status code of the request +} QosCommonProcessedResultsT; + +//! the coordinator responds with configuration or results +typedef struct QosCommonCoordinatorToClientResponseT +{ + QosCommonClientConfigT configuration; //!< what does the coordinator want the client to do + QosCommonProcessedResultsT results; //!< contains the processed data from the coordinator, this will be provided if there coordinator doesn't have more tests for the client to do + uint32_t uServiceRequestID; //!< number uniquely identifies this transaction taking place between client and coordinator +} QosCommonCoordinatorToClientResponseT; + +//! sent to and from the QOS server as a udp probe, note QosCommonSerializeProbePacket and QosCommonDeserializeProbePacket determines on wire packing and order +typedef struct QosCommonProbePacketT +{ + QosCommonAddrT clientAddressFromService; //!< initially the client address from coordinator, however address from server prospective takes precedence, used to authenticate the packet is coming from the address that generated it + uint32_t uProtocol; //!< QOS 2.0 packets will always contain 'qos2' for easy identification. + uint32_t uServiceRequestId; //!< provided by the QosCoordinator, unique to the client doing multiple QOS actions, used to identify which server resources this client is using + uint32_t uServerReceiveTime; //!< time the server received this probe from the client + uint16_t uServerSendDelta; //!< duration the server held onto the packet before sending the response probe, this is latency added by the server process + uint16_t uVersion; //!< uniquely identifies protocol + uint16_t uProbeSizeUp; //!< indicates how big this probe is, including any padding for bandwidth + uint16_t uProbeSizeDown; //!< indicates how big this probe is, including any padding for bandwidth + uint16_t uClientRequestId; //!< provided by the client, unique to a particular QOS action, used to pair request and responses together + uint8_t uProbeCountUp; //!< count index of this probe + uint8_t uProbeCountDown; //!< from client (number of probes there server should respond with for each probe received) from server (count index of this probe) + uint8_t uExpectedProbeCountUp; //!< number of probes the client expects to send to the sever in this test (assuming no packet loss) +} QosCommonProbePacketT; + +//! a single QOS probe result information +typedef struct QosCommonProbeResultT +{ + uint32_t uClientSendTime; //!< time the client initially sent this QOS probe + uint32_t uServerReceiveTime; //!< time the server received this probe from the client (note that server and client time are not in sync) + uint16_t uServerSendDelta; //!< duration the server held onto the packet before sending the response probe, this is latency added by the server process + uint16_t uClientReceiveDelta; //!< duration since sending before the client received response from the QOS server for this probe +} QosCommonProbeResultT; + +//!< after the client performs the tests, raw results are provided to the coordinator for analysis +//! Its expected each QosCommonRawResultsT can be used to generate latency values, bandwidth values, with two QosCommonRawResultsT firewall values can be calculated +typedef struct QosCommonRawResultsT +{ + QosCommonProbeResultT aProbeResult[QOS_COMMON_MAX_PROBE_COUNT]; //!< a list of individual probe timings + QosCommonAddrT clientAddressFromServer; //!< clients address from prospective of the QOS server + char strSiteName[QOS_COMMON_MAX_RPC_STRING_SHORT]; //!< identifies where a site is, such as bio-sjc + char strTestName[QOS_COMMON_MAX_RPC_STRING_SHORT]; //!< identifies which test, such as latency, bandwidthUp, bandwidthDown + uint32_t hResult; //!< holds the error/status code of the request + uint8_t uProbeCountUp; //!< number of probes the request sent when all retrying and everything is done (usually same as uProbeCountRequestedUp if no probes were lost, otherwise we send more) + uint8_t uProbeCountDown; //!< number of valid probes from this request that came back from the server (usually same as uProbeCountRequestedDown at the end unless an error or probes lost) + uint8_t uProbeCountHighWater; //!< the highest count of probe we receive back + //note, uProbeCountHighWater, uProbeCountDown, and uProbeCountUp aren't shared with the coordinator +} QosCommonRawResultsT ; + +//! data the client will tell the coordinator about itself (aside from test results) to help it make its decisions +typedef struct QosCommonClientModifiersT +{ + QosCommonAddrT clientAddressInternal; //!< address the client believes it is using + char strPlatform[QOS_COMMON_MAX_RPC_STRING_SHORT]; //!< a string describing which platform the client is + QosCommonFirewallTypeE eOSFirewallType; //!< the firewall type that the client OS suggests it might be + uint32_t uPacketQueueRemaining; //!< number of spots left in the packet queue after the client has done its tests, a 0 value indicates a problem + uint32_t uStallCountCoordinator; //!< number of stall events encountered when attempting to communicate with the coordinator + uint32_t uStallDurationCoordinator; //!< total duration of time spent stalled while communicating with the coordinator + uint32_t uStallCountProbe; //!< number of stall events encountered when attempting to send probes to the qos server + uint32_t uStallDurationProbe; //!< total duration of time spent stalled while communicating with the qos server + uint32_t hResult; //!< current error status of the QosClient module, can be used to report errors to the coordinator +} QosCommonClientModifiersT; + +//! the client informs the coordinator of who it is and any results it has gathered in order to get configuration or processed "final" results +typedef struct QosCommonClientToCoordinatorRequestT +{ + QosCommonRawResultsT aResults[QOS_COMMON_MAX_RESULTS]; //!< a set of raw results for each site, for each test performed + char strQosProfile[QOS_COMMON_MAX_RPC_STRING]; //!< a string describing which setting the coordinator should use for this title. (likely service name, like fifa-pc-2017) + uint32_t uServiceRequestID; //!< number uniquely identifies this transaction taking place between client and coordinator + uint32_t uNumResults; //!< number results in aResults, if the client has results to report + uint16_t uProbeVersion; //!< uniquely identifies protocol + QosCommonClientModifiersT clientModifiers; //!< a set of fields the client wants to share with the coordinator such as health information to use in decision process +} QosCommonClientToCoordinatorRequestT; + +//! the QOS server will request to be registered with the coordinator +typedef struct QosCommonServerToCoordinatorRequestT +{ + char strAddr[QOS_COMMON_MAX_RPC_STRING]; //!< url to communicate with the server + char strSiteName[QOS_COMMON_MAX_RPC_STRING_SHORT]; //!< location where this server resides ie bio-sjc + char strPool[QOS_COMMON_MAX_RPC_STRING]; //!< segregate servers at a given location, such as only fifa is using this server or only for special qa tests + uint8_t aSecureKey[QOS_COMMON_SECURE_KEY_LENGTH]; //!< key used to generate hmac + uint16_t uPort; //!< port to use when communicating + uint16_t uCapacityPerSec; //!< maximum number of clients the coordinator should send to the server per second + uint16_t uLastLoadPerSec; //!< number of qos tests started last second + uint16_t uProbeVersion; //!< the probe packet version this server will operate with + uint16_t uUpdateInterval; //!< amount of time in ms that the coordinator should wait before expecting the next heartbeat + uint8_t bShuttingDown; //!< indicates if this server has begun to shutdown, if so no more clients should be sent to this server +} QosCommonServerToCoordinatorRequestT; + +//! registration message is for debug purpose only +typedef struct QosCommonCoordinatorToServerResponseT +{ + char strRegistrationMessage[QOS_COMMON_MAX_RPC_STRING]; //!< message provided by the coordinator for this server, likely explaining any error status + uint32_t uMinServiceRequestID; //!< don't accept service request id's older than this +} QosCommonCoordinatorToServerResponseT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// QosCommonClientToCoordinatorRequestT helpers +DIRTYCODE_API uint8_t* QosCommonClientToCoordinatorRequestEncode(const QosCommonClientToCoordinatorRequestT *pClientToCoordinatorRequest, uint8_t *pBuffer, uint32_t uBuffSize, uint32_t *pOutSize); +DIRTYCODE_API int32_t QosCommonClientToCoordinatorEstimateEncodedSize(const QosCommonClientToCoordinatorRequestT *pClientToCoordinatorRequest); +DIRTYCODE_API void QosCommonClientToCoordinatorPrint(const QosCommonClientToCoordinatorRequestT *pClientToCoordinatorRequest, uint32_t uLogLevel); + +// QosCommonCoordinatorToClientResponseT helpers +DIRTYCODE_API int32_t QosCommonCoordinatorToClientResponseDecode(QosCommonCoordinatorToClientResponseT *pResponse, const uint8_t *pBuffer, uint32_t uBuffSize); +DIRTYCODE_API void QosCommonCoordinatorToClientPrint(const QosCommonCoordinatorToClientResponseT *pResponse, uint32_t uLogLevel); + +// QosCommonServerToCoordinatorRequestT helpers +DIRTYCODE_API uint8_t* QosCommonServerToCoordinatorRequestEncode(const QosCommonServerToCoordinatorRequestT *pServerRegistrationRequest, uint8_t *pBuffer, uint32_t uBuffSize, uint32_t *pOutSize); + +// QosCommonCoordinatorToServerResponseT helpers +DIRTYCODE_API int32_t QosCommonCoordinatorToServerResponseDecode(QosCommonCoordinatorToServerResponseT *pServerRegistrationResponse, const uint8_t *pBuffer, uint32_t uBuffSize); + +// helpers for address +DIRTYCODE_API char* QosCommonAddrToString(const QosCommonAddrT *pAddr, char *pBuffer, int32_t iBufSize); +DIRTYCODE_API int32_t QosCommonStringToAddr(char *pStrIn, QosCommonAddrT *pOutAddr); +DIRTYCODE_API uint8_t QosCommonIsAddrEqual(QosCommonAddrT *pAddr1, QosCommonAddrT *pAddr2); +DIRTYCODE_API uint8_t QosCommonIsRemappedAddrEqual(QosCommonAddrT *pAddr1, struct sockaddr *pAddr2); +DIRTYCODE_API void QosCommonConvertAddr(QosCommonAddrT *pTargetAddr, struct sockaddr *pSourceAddr); + +// helpers for serializing and deserializing packets +DIRTYCODE_API uint8_t QosCommonSerializeProbePacket(uint8_t *pOutBuff, uint32_t uBuffSize, const QosCommonProbePacketT *pInPacket, uint8_t *pSecureKey); +DIRTYCODE_API uint8_t QosCommonDeserializeProbePacket(QosCommonProbePacketT *pOutPacket, uint8_t *pInBuff, uint8_t *pSecureKey1, uint8_t *pSecureKey2); +DIRTYCODE_API void QosCommonDeserializeProbePacketInsecure(QosCommonProbePacketT *pOutPacket, uint8_t *pInBuff); +DIRTYCODE_API uint16_t QosCommonDeserializeClientRequestId(uint8_t *pInBuff); +DIRTYCODE_API uint32_t QosCommonDeserializeServiceRequestId(uint8_t *pInBuff); + +// helpers for version numbering +DIRTYCODE_API uint16_t QosCommonMakeVersion(uint8_t uMajor, uint8_t uMinor); +DIRTYCODE_API void QosCommonGetVersion(uint16_t uVersion, uint8_t *uMajor, uint8_t *uMinor); +DIRTYCODE_API uint8_t QosCommonIsCompatibleVersion(uint16_t uVersion1, uint16_t uVersion2); +DIRTYCODE_API uint8_t QosCommonIsCompatibleProbeVersion(uint16_t uVersion); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _qoscommon_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/userapi.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/userapi.h new file mode 100644 index 00000000..30528890 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/userapi.h @@ -0,0 +1,280 @@ +/*H*************************************************************************************************/ +/*! + \File userapi.h + + \Description + Expose first party player information + + \Notes + This API currently only supports Gen 4 platforms (XBox One and PS4). + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2001-2013. ALL RIGHTS RESERVED. + + \Version 05/12/13 (mcorcoran) First Version + \Version 12/02/14 (amakoukji) API revamped +*/ +/*************************************************************************************************H*/ + +#ifndef _userapi_h +#define _userapi_h + +/*! +\Moduledef UserApi UserApi +\Modulemember User +*/ +//@{ + +/*** Include files ********************************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtyuser.h" + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) +#include "DirtySDK/misc/xboxone/userapixboxone.h" +#endif + +/*** Defines **************************************************************************************/ + +//!< Maximum sizes of various strings +#define USERAPI_NOTIFY_LIST_MAX_SIZE (50) +#define USERAPI_GAMERTAG_MAXLEN (128) +#define USERAPI_AVATAR_URL_MAXLEN (256) +#define USERAPI_DISPLAYNAME_MAXLEN (256) +#define USERAPI_PLATFORM_NAME_MAXLEN (64) +#define USERAPI_TITLE_NAME_MAXLEN (128) +#define USERAPI_TITLE_ID_MAXLEN (64) +#define USERAPI_RICH_PRES_MAXLEN (512) +#define USERAPI_GAME_DATA_MAXLEN (172) + +#define USERAPI_MASK_PROFILE (0x01) +#define USERAPI_MASK_PRESENCE (0x02) +#define USERAPI_MASK_RICH_PRESENCE (0x04) +#define USERAPI_MASK_PROFILES (0x08) + +/*** Macros ***************************************************************************************/ + +/*** Definitions *****************************************************************************/ + +//!< UserApi Error types +typedef enum UserApiEventErrorE +{ + USERAPI_ERROR_OK = 0, // Success code + + USERAPI_ERROR_REQUEST_FAILED = -100, // Failed to retrieve id + USERAPI_ERROR_REQUEST_NOT_FOUND, // User wasn't found + USERAPI_ERROR_REQUEST_FORBIDDEN, // local user does not have access to requested user information + USERAPI_ERROR_REQUEST_SERVER_ERROR, // Internal server error + USERAPI_ERROR_REQUEST_UNAVAILABLE, // Service unavailable + USERAPI_ERROR_REQUEST_TIMEOUT, // Profile response was not received on time + USERAPI_ERROR_UNSUPPORTED, + USERAPI_ERROR_INPROGRESS, + USERAPI_ERROR_REQUEST_PARSE_FAILED, + USERAPI_ERROR_NO_USER, + USERAPI_ERROR_FULL +} UserApiEventErrorE; + +//!< Type of first party notifications +// Note that minimal information is returned by the notifications themselves and a follow up query to the first party will often be necessary to update the internal state +typedef enum UserApiNotifyTypeE +{ + USERAPI_NOTIFY_PRESENCE_UPDATE = 0, + USERAPI_NOTIFY_TITLE_UPDATE, + USERAPI_NOTIFY_RICH_PRESENCE_UPDATE, + USERAPI_NOTIFY_PROFILE_UPDATE +} UserApiNotifyTypeE; + +//!< Categories of data for UserApi callbacks +typedef enum UserApiEventTypeE +{ + USERAPI_EVENT_DATA = 0, // The current event has user data available + USERAPI_EVENT_END_OF_LIST // The current event has completion data available through EventDetails.EndOfList +} UserApiEventTypeE; + +//!< List of possible online statuses +typedef enum UserApiOnlineStatusE +{ + USERAPI_PRESENCE_UNKNOWN = 0, + USERAPI_PRESENCE_OFFLINE, + USERAPI_PRESENCE_ONLINE, + USERAPI_PRESENCE_AWAY +} UserApiOnlineStatusE; + +//!< Rich presence response data structure +typedef struct UserApiRichPresenceT +{ + char strData[USERAPI_RICH_PRES_MAXLEN + 1]; // Rich presence string + char strGameData[USERAPI_GAME_DATA_MAXLEN + 1]; // GameData Base64 string + void *pRawData; +} UserApiRichPresenceT; + +//!< Presence data response structure +typedef struct UserApiPresenceT +{ + UserApiOnlineStatusE ePresenceStatus; // online, offline, away or unknown + char strPlatform[USERAPI_PLATFORM_NAME_MAXLEN+1]; // name of the console the user is on + char strTitleName[USERAPI_TITLE_NAME_MAXLEN+1]; // title name + char strTitleId[USERAPI_TITLE_ID_MAXLEN+1]; // title identifier + uint8_t bIsPlayingSameTitle; + void *pRawData; +} UserApiPresenceT; + +//!< Profile data response structure +typedef struct UserApiProfileT +{ + char strDisplayName[USERAPI_DISPLAYNAME_MAXLEN + 1]; // The users real name. This string will contain strGamerTag if the real name is not available + char strGamertag [USERAPI_GAMERTAG_MAXLEN + 1]; // The users gamertag for XBox One or onlineId for PS4 + char strAvatarUrl [USERAPI_AVATAR_URL_MAXLEN + 1]; // URI pointing to the avatar image for XBox One and PS4 + uint32_t uLocale; // The users location and language encoded using the LOBBYAPI macros in dirtylang.h + #if defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) + UserApiAccessibilityT Accessibility; // Contains the accessibility settings + #endif + void *pRawData; // pointer to object that contain all first part data received +} UserApiProfileT; + +//!< General response structure +typedef struct UserApiUserDataT +{ + DirtyUserT DirtyUser; // A DirtySDK representation or the native user on the current platform + uint32_t uUserDataMask; // A mask value to determine which values below are valid + UserApiProfileT Profile; // Valid when uUserDataMask & USERAPI_MASK_PROFILE > 0 + UserApiPresenceT Presence; // Valid when uUserDataMask & USERAPI_MASK_PRESENCE > 0 + UserApiRichPresenceT RichPresence; // Valid when uUserDataMask & USERAPI_MASK_RICH_PRESENCE > 0 +} UserApiUserDataT; + +//!< Request metadata response structure +typedef struct UserApiEndOfListT +{ + int32_t iTotalRequested; + int32_t iTotalReceived; + int32_t iTotalErrors; +} UserApiEndOfListT; + +//!< Container union for responses +typedef union UserApiEventDetailsT +{ + UserApiUserDataT UserData; // Valid when eEventType == USERAPI_EVENT_DATA + UserApiEndOfListT EndOfList; // Valid when eEventType == USERAPI_EVENT_END_OF_LIST +} UserApiEventDetailsT; + +//!< User metadata fpr USERAPI_EVENT_DATA +typedef struct UserApiEventDataT +{ + UserApiEventErrorE eError; // Error code associated with this event + UserApiEventTypeE eEventType; // Event type used to specify the type of data in EventDetails + uint32_t uUserIndex; // Index of the user that made the request + UserApiEventDetailsT EventDetails; // Contains event details specific for the eEventType event type +} UserApiEventDataT; + +//!< Response from data post style request +typedef struct UserApiPostResponseT +{ + uint32_t uUserIndex; // Index of the user that made the request + UserApiEventErrorE eError; // HTTP Error code associated with this event + const char *pMessage; // Message if applicable +} UserApiPostResponseT; + +//!< opaque module ref +typedef struct UserApiRefT UserApiRefT; + +//!< Title change notification data +typedef struct UserApiNotifyTitleDataT +{ + DirtyUserT DirtyUser; // user who's presence was updated + uint32_t uTitleId; // title that was updated + uint32_t uUserIndex; // user the notification is for +} UserApiNotifyTitleDataT; + +//!< Presence change notification data, note that this remains incomplete since minimal data is provided by first parties +typedef struct UserApiNotifyPresenceDataT +{ + DirtyUserT DirtyUser; // user who's presence was updated + uint32_t uUserIndex; // user the notification is for +} UserApiNotifyPresenceDataT; + +//!< Rich presence change notification data, note that this remains incomplete since minimal data is provided by first parties +typedef struct UserApiNotifyRichPresenceDataT +{ + DirtyUserT DirtyUser; // user who's presence was updated + uint32_t uUserIndex; // user the notification is for +} UserApiNotifyRichPresenceDataT; + +//!< Rich presence change notification data, note that this remains incomplete since minimal data is provided by first parties +typedef struct UserApiNotifyProfileUpdateDataT +{ + DirtyUserT DirtyUser; // user who's presence was updated + uint32_t uUserIndex; // user the notification is for +} UserApiNotifyProfileUpdateDataT; + +//!< container union for notification data +typedef union UserApiNotifyDataT +{ + UserApiNotifyTitleDataT TitleData; // Valid when eNotifyType == USERAPI_NOTIFY_TITLE_UPDATE + UserApiNotifyPresenceDataT PresenceData; // Valid when eNotifyType == USERAPI_NOTIFY_PRESENCE + UserApiNotifyRichPresenceDataT RichPresenceData; // Valid when eNotifyType == USERAPI_NOTIFY_RICH_PRESENCE + UserApiNotifyProfileUpdateDataT ProfileUpdateData; // Valid When eNotifyType == USERAPI_NOTIFY_PROFILE_UPDATE +} UserApiNotifyDataT; + +//!< callback types +typedef void (UserApiCallbackT) (UserApiRefT *pRef, UserApiEventDataT *pUserApiEventData, void *pUserData); +typedef void (UserApiPostCallbackT) (UserApiRefT *pRef, UserApiPostResponseT *pResponse, void *pUserData); +typedef void (UserApiUpdateCallbackT)(UserApiRefT *pRef, UserApiNotifyTypeE eNotifyType, UserApiNotifyDataT *pData, void *pUserData); + +/*** Function Prototypes **************************************************************************/ + +/*** Variables ************************************************************************************/ + +/*** Private Functions ****************************************************************************/ + +/*** Public Functions *****************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Starts UserApi. +DIRTYCODE_API UserApiRefT *UserApiCreate(void); + +// Starts shutting down module. Module will not accept new requests, and will abort ongoing ones. Also, registered UserApiCallbackT callback will not be called even if there is data available for processing +DIRTYCODE_API int32_t UserApiDestroy(UserApiRefT *pRef); + +// Get status information. +DIRTYCODE_API int32_t UserApiStatus(UserApiRefT *pRef, int32_t iSelect, void *pBuf, int32_t iBufSize); + +// Control behavior of module +DIRTYCODE_API int32_t UserApiControl(UserApiRefT *pRef, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// Starts the process to retrieve a batch of user information for players. pLookupUsers is a pointer to the first DirtyUserT, and iLookupUsersLength is the number of DirtyUserTs. +DIRTYCODE_API int32_t UserApiRequestProfilesAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUsers, int32_t iLookupUsersLength, UserApiCallbackT *pCallback, void *pUserData); + +// Starts the process to retrieve user information of a single user. pLookupUsers is a pointer to the DirtyUserT +DIRTYCODE_API int32_t UserApiRequestProfileAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUser, UserApiCallbackT *pCallback, uint32_t uUserDataMask, void *pUserData); + +// Starts the process to retrieve Presence information of a single user. pLookupUsers is a pointer to the DirtyUserT +DIRTYCODE_API int32_t UserApiRequestPresenceAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUser, UserApiCallbackT *pCallback, uint32_t uUserDataMask, void *pUserData); + +// Starts the process to retrieve Rich Presence information of a single user. pLookupUsers is a pointer to the DirtyUserT +DIRTYCODE_API int32_t UserApiRequestRichPresenceAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUser, UserApiCallbackT *pCallback, uint32_t uUserDataMask, void *pUserData); + +// Log that a local user recently interacted with another +DIRTYCODE_API int32_t UserApiPostRecentlyMetAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pPlayerMet, void *pAdditionalInfo, UserApiPostCallbackT *pCallback, void *pUserData); + +// Log new rich presence info; usage of this is very dependant on the first party's method of rich presence +DIRTYCODE_API int32_t UserApiPostRichPresenceAsync(UserApiRefT *pRef, uint32_t uUserIndex, UserApiRichPresenceT *pData, UserApiPostCallbackT *pCallback, void *pUserData); + +// Register for callbacks +DIRTYCODE_API int32_t UserApiRegisterUpdateEvent(UserApiRefT *pRef, uint32_t uUserIndex, UserApiNotifyTypeE eType, UserApiUpdateCallbackT *pNotifyCb, void *pUserData); + +// Update the internal state of the module, and call registered UserApiCallbackT callback if there are GamerCard/Profile responses available. This function should be called periodically. +DIRTYCODE_API void UserApiUpdate(UserApiRefT *pRef); + +// cancels all pending transactions +DIRTYCODE_API int32_t UserApiCancel(UserApiRefT *pRef, uint32_t uUserIndex); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _userapi_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/userlistapi.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/userlistapi.h new file mode 100644 index 00000000..04bfd537 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/misc/userlistapi.h @@ -0,0 +1,251 @@ +/*H*************************************************************************************************/ +/*! + \File userlistapi.h + + \Description + The UserListApi module allow the user to fetche friends, blocked users and + muted users from first parties. + + \Notes + This API currently only supports Gen 4 platforms (XBox One and PS4). + + \Copyright + Copyright (c) Electronic Arts 2013. + + \Version 04/17/13 (amakoukji) + \Version 12/02/14 (amakoukji) API revamped +*/ +/*************************************************************************************************H*/ + +#ifndef _userlistapi_h +#define _userlistapi_h + +/*! +\Moduledef UserList UserList +\Modulemember User +*/ +//@{ + +/*** Include files ********************************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtyuser.h" +#include "DirtySDK/misc/userapi.h" + +#if defined (DIRTYCODE_XBOXONE) || defined (DIRTYCODE_GDK) +#include "DirtySDK/misc/xboxone/userlistapixboxone.h" +#elif defined (DIRTYCODE_PS5) +#include "DirtySDK/misc/ps5/userlistapips5.h" +#elif defined (DIRTYCODE_PS4) +#include "DirtySDK/misc/ps4/userlistapips4.h" +#endif + +/*** Defines **************************************************************************************/ + +#define USERLISTAPI_ERROR_UNSUPPORTED (-1) +#define USERLISTAPI_ERROR_INPROGRESS (-2) +#define USERLISTAPI_ERROR_BAD_PARAM (-3) +#define USERLISTAPI_ERROR_NO_USER (-4) +#define USERLISTAPI_ERROR_TIMEOUT (-5) +#define USERLISTAPI_ERROR_GUEST (-6) +#define USERLISTAPI_ERROR_INVALID_USER (-7) +#define USERLISTAPI_ERROR_USER_INDEX_RANGE (-8) +#define USERLISTAPI_ERROR_RATE_LIMITED (-9) +#define USERLISTAPI_ERROR_UNKNOWN (-99) + +#define USERLISTAPI_MASK_PROFILE (0x01) +#define USERLISTAPI_MASK_PRESENCE (0x02) +#define USERLISTAPI_MASK_RICH_PRESENCE (0x04) + +#define USERLISTAPI_NOTIFY_LIST_MAX_SIZE (50) + +typedef enum UserListApiTypeE +{ + USERLISTAPI_TYPE_FRIENDS, + USERLISTAPI_TYPE_BLOCKED, + USERLISTAPI_TYPE_FRIENDS_OF_FRIEND, + USERLISTAPI_TYPE_MUTED +} UserListApiTypeE; + +typedef enum UserListApiIfATypeE +{ + USERLISTAPI_IS_FRIENDS, + USERLISTAPI_IS_BLOCKED +} UserListApiIfATypeE; + +typedef enum UserListApiReturnTypeE +{ + TYPE_USER_DATA, + TYPE_LIST_END +} UserListApiReturnTypeE; + +typedef enum UserListApiIsAReturnTypeE +{ + USERLISTAPI_IS_OF_TYPE, + USERLISTAPI_IS_NOT_OF_TYPE, + USERLISTAPI_IS_MYSELF, + USERLISTAPI_IS_TYPE_UNKNOWN +} UserListApiIsAReturnTypeE; + +typedef enum UserListApiNotifyTypeE +{ + USERLISTAPI_NOTIFY_FRIENDLIST_UPDATE = 0, + USERLISTAPI_NOTIFY_BLOCKEDLIST_UPDATE +} +UserListApiNotifyTypeE; + +typedef enum UserListApiUpdateTypeE +{ + USERLISTAPI_UPDATE_TYPE_UNKNOWN = 0, + USERLISTAPI_UPDATE_TYPE_REMOVED, + USERLISTAPI_UPDATE_TYPE_ADD +} UserListApiUpdateTypeE; + +/*** Macros ***************************************************************************************/ + +/*** Type Definitions *****************************************************************************/ + +//!< opaque module ref +typedef struct UserListApiRefT UserListApiRefT; + +//!< search filters ref, this will be further defined in platforms specific headers +typedef struct UserListApiFiltersT UserListApiFiltersT; + +//!< response structure for user data +typedef struct UserListApiUserDataT +{ + DirtyUserT DirtyUser; // user identifier + uint8_t bIsMutualFriend; // TRUE if the user is a mutual friend, FALSE otherwise + uint8_t bIsFavorite; // TRUE if the user is a favorite friend, FALSE otherwise + int32_t iUserProfileError; // >= 0 means the profile is valid, otherwise a meaningful error code + UserApiUserDataT ExtendedUserData; // user details containing profile, presence and rich presence + UserListApiTypeE eRequestType; // type of user lists, friends, recently met, etc + uint32_t uUserIndex; // index of local user index + UserListApiFiltersT *pUserListFilters; // filters used for this query, first Party Specific, only valid inside the UserListApiCallbackT + void *pRawData; // pointer to 1st party friend data +} UserListApiUserDataT; + +//!< response structure for metadata +typedef struct UserListApiEndDataT +{ + UserListApiTypeE eRequestType; // type of user lists, friends, recently met, etc + UserListApiFiltersT *pUserListFilters; // filters used for this query, first Party Specific, only valid inside the UserListApiCallbackT + uint32_t uUserIndex; // index of local user index + uint32_t uTypeMask; // type mask + int64_t iLimit; // maximum number of users to return + int64_t iOffset; // index of first user from 1st party query + int64_t iTotalFriendCount; // total number of users that match the query, will become valid + int32_t Error; // error code returned by the function + int32_t iRateLimRetrySecs; // if Error is USERLISTAPI_ERROR_RATE_LIMITED then this is the number of seconds to wait before retrying +} UserListApiEndDataT; + +//!< IsA style request response +typedef struct UserListApiIsADataT +{ + DirtyUserT DirtyUser; // user identifier + uint32_t uUserIndex; // index of local user index + UserListApiIsAReturnTypeE eIsaType; // result (yes, no, unknown or myself) + uint8_t bIsMutualFriend; // TRUE if the user is a mutual friend, FALSE otherwise + uint8_t bIsFavorite; // TRUE if the user is a favorite friend, FALSE otherwise + int32_t Error; // error code returned by the function + int32_t iRateLimRetrySecs; // if Error is USERLISTAPI_ERROR_RATE_LIMITED then this is the number of seconds to wait before retrying +} UserListApiIsADataT; + +//!< container union for generic response +typedef union UserListApiEventDataT +{ + struct UserListApiUserDataT UserData; // user data for eResponseType = TYPE_USER_DATA + struct UserListApiEndDataT ListEndData; // meta data for eResponseType = TYPE_LIST_END +} UserListApiEventDataT; + +//!< presence update notification data +typedef struct UserListApiNotifyPresenceUpdateData +{ + DirtyUserT DirtyUser; // user identifier +} UserListApiNotifyPresenceUpdateData; + +//!< rich presence update notification data +typedef struct UserListApiNotifyRichPresenceUpdateData +{ + DirtyUserT DirtyUser; // user identifier +} UserListApiNotifyRichPresenceUpdateData; + +//!< friend list update notification data +typedef struct UserListApiNotifyFriendListUpdate +{ + DirtyUserT DirtyUser; // user identifier + UserListApiUpdateTypeE eType; + uint32_t uUserIndex; // user index for whom this notification belongs +} UserListApiNotifyFriendListUpdate; + +//!< blocked list update notification data +typedef struct UserListApiNotifyBlockedListUpdate +{ + DirtyUserT DirtyUser; // user identifier + UserListApiUpdateTypeE eType; + uint32_t uUserIndex; // user index for whom this notification belongs +} UserListApiNotifyBlockedListUpdate; + + +//!< container union for notification results +typedef union UserListApiNotifyDataT +{ + UserListApiNotifyFriendListUpdate FriendListData; + UserListApiNotifyBlockedListUpdate BlockedListData; +} UserListApiNotifyDataT; + +// + +/*** Defines **********************************************************************/ + +// identify platform if not yet known +#if defined(DIRTYCODE_PC) || defined(DIRTYCODE_LINUX) +// do nothing +#elif defined(EA_PLATFORM_XBSX) + #define DIRTYCODE_GDK 1 + #define DIRTYCODE_XBSX 1 +#elif defined(EA_PLATFORM_XBOX_GDK) + #define DIRTYCODE_GDK 1 + #define DIRTYCODE_XBOXONE 1 +#elif defined(EA_PLATFORM_CAPILANO) + #define DIRTYCODE_XBOXONE 1 +#elif defined(EA_PLATFORM_MICROSOFT) + #define DIRTYCODE_PC 1 +#elif defined(EA_PLATFORM_PS4) + #define DIRTYCODE_PS4 1 +#elif defined(EA_PLATFORM_PS5) + #define DIRTYCODE_PS5 1 + #define DIRTYCODE_PS4 1 +#elif defined(EA_PLATFORM_IPHONE) || defined(EA_PLATFORM_IPHONE_SIMULATOR) + #define DIRTYCODE_APPLEIOS 1 +#elif defined(EA_PLATFORM_OSX) + #define DIRTYCODE_APPLEOSX 1 +#elif defined(EA_PLATFORM_ANDROID) + #define DIRTYCODE_ANDROID 1 +#elif defined(EA_PLATFORM_STADIA) + #define DIRTYCODE_LINUX 1 + #define DIRTYCODE_STADIA 1 +#elif defined(EA_PLATFORM_LINUX) + #define DIRTYCODE_LINUX 1 +#elif defined(EA_PLATFORM_NX) + #define DIRTYCODE_NX 1 +#endif + +// define platform name +#if defined(DIRTYCODE_ANDROID) +#define DIRTYCODE_PLATNAME "Android" +#define DIRTYCODE_PLATNAME_SHORT "android" +#elif defined(DIRTYCODE_APPLEIOS) +#define DIRTYCODE_PLATNAME "AppleIOS" +#define DIRTYCODE_PLATNAME_SHORT "ios" +#elif defined(DIRTYCODE_APPLEOSX) +#define DIRTYCODE_PLATNAME "AppleOSX" +#define DIRTYCODE_PLATNAME_SHORT "osx" +#elif defined(DIRTYCODE_XBSX) +#define DIRTYCODE_PLATNAME "XboxSeriesX" +#define DIRTYCODE_PLATNAME_SHORT "xbsx" +#elif defined(DIRTYCODE_XBOXONE) +#define DIRTYCODE_PLATNAME "XboxOne" +#define DIRTYCODE_PLATNAME_SHORT "xone" +#elif defined(DIRTYCODE_PS5) +#define DIRTYCODE_PLATNAME "PlayStation5" +#define DIRTYCODE_PLATNAME_SHORT "ps5" +#elif defined(DIRTYCODE_PS4) +#define DIRTYCODE_PLATNAME "PlayStation4" +#define DIRTYCODE_PLATNAME_SHORT "ps4" +#elif defined(DIRTYCODE_STADIA) +#define DIRTYCODE_PLATNAME "Stadia" +#define DIRTYCODE_PLATNAME_SHORT "stadia" +#elif defined(DIRTYCODE_LINUX) +#define DIRTYCODE_PLATNAME "Linux" +#define DIRTYCODE_PLATNAME_SHORT "linux" +#elif defined(DIRTYCODE_PC) +#define DIRTYCODE_PLATNAME "Windows" +#define DIRTYCODE_PLATNAME_SHORT "pc" +#elif defined(DIRTYCODE_NX) +#define DIRTYCODE_PLATNAME "Switch" +#define DIRTYCODE_PLATNAME_SHORT "nx" +#else +#error The platform was not predefined and could not be auto-determined! +#endif + +//For Building as a dll +#if defined(EA_DLL) + #ifndef DIRTYCODE_DLL + #define DIRTYCODE_DLL + #endif + #ifndef DIRTYCODE_API + #define DIRTYCODE_API __declspec(dllimport) + #endif +#else + #define DIRTYCODE_API +#endif + +// define 32-bit or 64-bit pointers +#if (EA_PLATFORM_PTR_SIZE == 8) + #define DIRTYCODE_64BITPTR (1) +#else + #define DIRTYCODE_64BITPTR (0) +#endif + +// we need va_list to be a universal type +#include +#include +#include + +/*** Defines **********************************************************************/ + +// force our definition of TRUE/FALSE +#ifdef TRUE +#undef TRUE +#undef FALSE +#endif + +#define FALSE (0) +#define TRUE (1) + +// map common debug code definitions to our debug code definition +#ifndef DIRTYCODE_DEBUG + #if defined(EA_DEBUG) + #define DIRTYCODE_DEBUG (1) + #else + #define DIRTYCODE_DEBUG (0) + #endif +#endif + +/*** Macros ***********************************************************************/ + +//! min of _x and _y +#define DS_MIN(_x, _y) (((_x) < (_y)) ? (_x) : (_y)) + +//! max of _x and _y +#define DS_MAX(_x, _y) (((_x) > (_y)) ? (_x) : (_y)) + +//! abs of _x +#define DS_ABS(_x) (((_x) < 0) ? -(_x) : (_x)) + +//! clamp _x between _min and _max; if _low < _high result is undefined +#define DS_CLAMP(_x, _min, _max) (((_x) > (_max)) ? (_max) : (((_x) < (_min)) ? (_min) : (_x))) + +/*** Type Definitions *************************************************************/ + +//! time-to-string conversion type +typedef enum TimeToStringConversionTypeE +{ + TIMETOSTRING_CONVERSION_ISO_8601, //!< ISO 8601 standard: yyyy-MM-ddTHH:mmZ where Z means 0 UTC offset, and no Z means local time zone + TIMETOSTRING_CONVERSION_ISO_8601_BASIC, //!< ISO 8601 basic format (no hyphens or colon) : https://en.wikipedia.org/wiki/ISO_8601#cite_ref-15 + TIMETOSTRING_CONVERSION_RFC_0822, //!< RFC 0822 format, updated by RFC1123: day, dd mon yyyy hh:mm:ss zzz + TIMETOSTRING_CONVERSION_ASN1_UTCTIME, //!< ASN.1 UTCTime format + TIMETOSTRING_CONVERSION_ASN1_GENTIME, //!< ASN.1 GeneralizedTime format + TIMETOSTRING_CONVERSION_UNKNOWN //!< unknown source format type; try to auto-determine if possible +} TimeToStringConversionTypeE; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// replacement time functions +DIRTYCODE_API uint64_t ds_timeinsecs(void); +DIRTYCODE_API int32_t ds_timezone(void); +DIRTYCODE_API struct tm *ds_localtime(struct tm *pTm, uint64_t elap); +DIRTYCODE_API struct tm *ds_secstotime(struct tm *pTm, uint64_t elap); +DIRTYCODE_API uint64_t ds_timetosecs(const struct tm *pTm); +DIRTYCODE_API struct tm *ds_plattimetotime(struct tm *pTm, void *pPlatTime); +DIRTYCODE_API struct tm *ds_plattimetotimems(struct tm *pTm, int32_t *pImSec); +DIRTYCODE_API char *ds_timetostr(const struct tm *pTm, TimeToStringConversionTypeE eConvType, uint8_t bLocalTime, char *pStr, int32_t iBufSize); +DIRTYCODE_API char *ds_secstostr(uint64_t elap, TimeToStringConversionTypeE eConvType, uint8_t bLocalTime, char *pStr, int32_t iBufSize); +DIRTYCODE_API uint64_t ds_strtotime(const char *str); +DIRTYCODE_API uint64_t ds_strtotime2(const char *pStr, TimeToStringConversionTypeE eConvType); + +// replacement string functions +DIRTYCODE_API int32_t ds_strnlen(const char *pBuffer, int32_t iLength); +DIRTYCODE_API int32_t ds_vsnprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args); +DIRTYCODE_API int32_t ds_vsnzprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args); +DIRTYCODE_API int32_t ds_snzprintf(char *pBuffer, int32_t iLength, const char *pFormat, ...); +DIRTYCODE_API char *ds_strnzcpy(char *pDest, const char *pSource, int32_t iCount); +DIRTYCODE_API int32_t ds_strnzcat(char *pDst, const char *pSrc, int32_t iDstLen); +DIRTYCODE_API int32_t ds_stricmp(const char *pString1, const char *pString2); +DIRTYCODE_API int32_t ds_strnicmp(const char *pString1, const char *pString2, uint32_t uCount); +DIRTYCODE_API char *ds_stristr(const char *pHaystack, const char *pNeedle); +DIRTYCODE_API char* ds_strtok_r(char *pStr, const char *pDelim, char **ppSavePtr); + +#if defined(_WIN32) || defined(_WIN64) + #define ds_strtoll(_buf, _outp, _radix) _strtoi64(_buf, _outp, _radix) + #define ds_strtoull(_buf, _outp, _radix) _strtoui64(_buf, _outp, _radix) +#else + #define ds_strtoll(_buf, _outp, _radix) strtoll(_buf, _outp, _radix) + #define ds_strtoull(_buf, _outp, _radix) strtoull(_buf, _outp, _radix) +#endif + +// 'original' string functions +DIRTYCODE_API char *ds_fmtoctstring(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen); +DIRTYCODE_API char *ds_fmtoctstring_lc(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen); +DIRTYCODE_API char *ds_strtolower(char *pString); +DIRTYCODE_API char *ds_strtoupper(char *pString); +DIRTYCODE_API char *ds_strtrim(char *pString); +DIRTYCODE_API int32_t ds_strlistinsert(char *pStrList, int32_t iListBufLen, const char *pString, char cTerm); +DIRTYCODE_API int32_t ds_strsubzcpy(char *pDst, int32_t iDstLen, const char *pSrc, int32_t iSrcLen); +DIRTYCODE_API int32_t ds_strsubzcat(char *pDst, int32_t iDstLen, const char *pSrc, int32_t iSrcLen); +DIRTYCODE_API int32_t ds_strcmpwc(const char *pString1, const char *pStrWild); +DIRTYCODE_API int32_t ds_stricmpwc(const char *pString1, const char *pStrWild); +DIRTYCODE_API int32_t ds_strsplit(const char *pSrc, char cDelimiter, char *pDst, int32_t iDstSize, const char **pNewSrc); + +// mem functions +DIRTYCODE_API void ds_memcpy(void *pDst, const void *pSrc, int32_t iDstLen); +DIRTYCODE_API void ds_memcpy_s(void *pDst, int32_t iDstLen, const void *pSrc, int32_t iSrcLen); +DIRTYCODE_API void ds_memclr(void *pMem, int32_t iCount); +DIRTYCODE_API void ds_memset(void *pMem, int32_t iValue, int32_t iCount); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _platform_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoadvt.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoadvt.h new file mode 100644 index 00000000..8c28b56a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoadvt.h @@ -0,0 +1,99 @@ +/*H*************************************************************************************/ +/*! + + \File protoadvt.h + + \Description + This advertising module provides a relatively simple multi-protocol + distributed name server architecture utilizing the broadcast capabilities + of UDP and IPX. Once the module is instantiated, it can be used as both + an advertiser (server) and watcher (client) simultaneously. + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 02/17/99 (GWS) Original version + \Version 1.1 02/25/99 (GWS) Alpha release + \Version 1.2 08/10/99 (GWS) Final release + \Version 1.3 12/04/00 (GWS) Revised for Dirtysock + +*/ +/*************************************************************************************H*/ + +#ifndef _protoadvt_h +#define _protoadvt_h + +/*! +\Moduledef ProtoAdvt ProtoAdvt +\Modulemember Proto +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +//! define the ports used for service broadcasts +#define ADVERT_BROADCAST_PORT_UDP 9999 +#define ADVERT_BROADCAST_PORT_IPX 9999 + +//! define the static packet header identifier +#define ADVERT_PACKET_IDENTIFIER "gEA" + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! module reference -- passed as first arg to all module functions +typedef struct ProtoAdvtRef ProtoAdvtRef; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// construct an advertising agent +DIRTYCODE_API ProtoAdvtRef *ProtoAdvtConstruct(int32_t buffer); + +// destruct an advertising agent +DIRTYCODE_API void ProtoAdvtDestroy(ProtoAdvtRef *what); + +// query for available services +DIRTYCODE_API int32_t ProtoAdvtQuery(ProtoAdvtRef *what, const char *kind, const char *proto, + char *buffer, int32_t buflen, int32_t local); + +// locate a specific advertisement and return advertisers address (UDP only) +DIRTYCODE_API uint32_t ProtoAdvtLocate(ProtoAdvtRef *ref, const char *kind, const char *name, + uint32_t *host, uint32_t defval); + +// advertise a service as available +DIRTYCODE_API int32_t ProtoAdvtAnnounce(ProtoAdvtRef *what, const char *kind, const char *name, + const char *addr, const char *note, int32_t freq); + +// cancel server advertisement +DIRTYCODE_API int32_t ProtoAdvtCancel(ProtoAdvtRef *what, const char *kind, const char *name); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protoadvt_h + + + + + + + + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttp.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttp.h new file mode 100644 index 00000000..3a763f50 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttp.h @@ -0,0 +1,229 @@ +/*H********************************************************************************/ +/*! + \File protohttp.h + + \Description + This module implements an HTTP client that can perform basic transactions + (get/put) with an HTTP server. It conforms to but does not fully implement + the 1.1 HTTP spec (http://www.w3.org/Protocols/rfc2616/rfc2616.html), and + allows for secure HTTP transactions as well as insecure transactions. + + \Copyright + Copyright (c) Electronic Arts 2000-2018. ALL RIGHTS RESERVED. + + \Version 0.5 02/21/2000 (gschaefer) First Version + \Version 1.0 12/07/2000 (gschaefer) Added PS2/Dirtysock support + \Version 1.1 03/03/2004 (sbevan) ProtoSSL/https rewrite, added limited Post support. + \Version 1.2 11/18/2004 (jbrookes) Refactored, updated to HTTP 1.1, added full Post support. +*/ +/********************************************************************************H*/ + +#ifndef _protohttp_h +#define _protohttp_h + +/*! +\Moduledef ProtoHttp ProtoHttp +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/proto/protohttputil.h" + +/*** Defines **********************************************************************/ + +// defines for ProtoHttpPost's bDoPut parameter +#define PROTOHTTP_POST (0) //!< execute a POST when calling ProtoHttpPost +#define PROTOHTTP_PUT (1) //!< execute a PUT when calling ProtoHttpPost + +// define for ProtoHttpPost's iDataSize parameter +#define PROTOHTTP_STREAM_BEGIN (-1) //!< start streaming upload (size unknown) + +// define for ProtoHttpSend's iDataSize parameter +#define PROTOHTTP_STREAM_END (0) //!< streaming upload operation is complete + +// defines for ProtoHttpGet's bHeadOnly parameter +#define PROTOHTTP_HEADBODY (0) //!< get head and body when calling ProtoHttpGet +#define PROTOHTTP_HEADONLY (1) //!< only get head, not body, when calling ProtoHttpGet + +// defines for ProtoHttpRecv's return result +#define PROTOHTTP_RECVDONE (-1) //!< receive operation complete, and all data has been read +#define PROTOHTTP_RECVFAIL (-2) //!< receive operation failed +#define PROTOHTTP_RECVWAIT (-3) //!< waiting for body data +#define PROTOHTTP_RECVHEAD (-4) //!< in headonly mode and header has been received +#define PROTOHTTP_RECVBUFF (-5) //!< recvall did not have enough space in the provided buffer +#define PROTOHTTP_RECVRDIR (-6) //!< maximum number of redirects exceeded + +// generic protohttp errors (do not overlap with RECV* codes) +#define PROTOHTTP_MINBUFF (-16) //!< not enough input buffer space for operation +#define PROTOHTTP_TIMEOUT (-17) //!< did not receive response from server in configured time + +#define PROTOHTTP_NOTHREADS (-18) //!< not enough worker threads can be spawned (deprecated) +#define PROTOHTTP_EAGAIN (-18) //!< resource not available + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque module ref +typedef struct ProtoHttpRefT ProtoHttpRefT; + +//! write callback info +#ifndef _PROTOHTTP_WRITECBINFO_DEFINED +typedef struct ProtoHttpWriteCbInfoT +{ + ProtoHttpRequestTypeE eRequestType; + ProtoHttpResponseE eRequestResponse; +} ProtoHttpWriteCbInfoT; + +/*! + \Callback ProtoHttpWriteCbT + + \Description + Write data callback + + \Input *pState - module state + \Input *pCbInfo - information about about the request this data is for + \Input *pData - data we received from the server + \Input iDataSize - size of the data + \Input *pUserData - user data pointer passed along in the callback + + \Output + int32_t - unused + */ +typedef int32_t (ProtoHttpWriteCbT)(ProtoHttpRefT *pState, const ProtoHttpWriteCbInfoT *pCbInfo, const char *pData, int32_t iDataSize, void *pUserData); + +#define _PROTOHTTP_WRITECBINFO_DEFINED +#endif + +/*! + \Callback ProtoHttpCustomHeaderCbT + + \Description + Callback that may be used to customize request header before sending + + \Input *pState - module state + \Input *pHeader - [out] request headers that we are going to send + \Input uHeaderSize - size of the request headers + \Input *pData - pointer to data sent in the request. deprecated, will always be NULL + \Input iDataLen - size of the data sent in the request. deprecated, will always be 0 + \Input *pUserRef - user data pointer passed along in the callback + + \Output + int32_t - negative=error, size of the header=success + + \Deprecated + pData and iDataLen are deprecated. We will pass NULL and 0 to them respectively. +*/ +typedef int32_t (ProtoHttpCustomHeaderCbT)(ProtoHttpRefT *pState, char *pHeader, uint32_t uHeaderSize, const char *pData, int64_t iDataLen, void *pUserRef); + +/*! + \Callback ProtoHttpReceiveHeaderCbT + + \Description + Callback that may be used to implement custom header parsing on header receipt + + \Input *pState - module state + \Input *pHeader - header we received from the server + \Input uHeaderSize - size of the response header + \Input *pUserRef - user data pointer passed along in the callback + + \Output + int32_t - unused +*/ +//! callback that may be used to implement custom header parsing on header receipt +typedef int32_t (ProtoHttpReceiveHeaderCbT)(ProtoHttpRefT *pState, const char *pHeader, uint32_t uHeaderSize, void *pUserRef); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// allocate module state and prepare for use +DIRTYCODE_API ProtoHttpRefT *ProtoHttpCreate(int32_t iRcvBuf); + +// set custom header callback +DIRTYCODE_API void ProtoHttpCallback(ProtoHttpRefT *pState, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserRef); + +// destroy the module and release its state +DIRTYCODE_API void ProtoHttpDestroy(ProtoHttpRefT *pState); + +// initiate an HTTP transfer +DIRTYCODE_API int32_t ProtoHttpGet(ProtoHttpRefT *pState, const char *pUrl, uint32_t bHeadOnly); + +// return the actual url data +DIRTYCODE_API int32_t ProtoHttpRecv(ProtoHttpRefT *pState, char *pBuffer, int32_t iBufMin, int32_t iBufMax); + +// receive all of the response data +DIRTYCODE_API int32_t ProtoHttpRecvAll(ProtoHttpRefT *pState, char *pBuffer, int32_t iBufSize); + +// initiate transfer of data to to the server via an HTTP POST command +DIRTYCODE_API int32_t ProtoHttpPost(ProtoHttpRefT *pState, const char *pUrl, const char *pData, int64_t iDataSize, uint32_t bDoPut); + +// send the actual url data +DIRTYCODE_API int32_t ProtoHttpSend(ProtoHttpRefT *pState, const char *pData, int32_t iDataSize); + +// request deletion of a server-based resource +DIRTYCODE_API int32_t ProtoHttpDelete(ProtoHttpRefT *pState, const char *pUrl); + +// get server options for specified resource +DIRTYCODE_API int32_t ProtoHttpOptions(ProtoHttpRefT *pState, const char *pUrl); + +// initiate transfer of data to to the server via an HTTP PATCH command +DIRTYCODE_API int32_t ProtoHttpPatch(ProtoHttpRefT *pState, const char *pUrl, const char *pData, int64_t iDataSize); + +// make an HTTP request +#define ProtoHttpRequest(_pState, _pUrl, _pData, _iDataSize, _eRequestType) ProtoHttpRequestCb2(_pState, _pUrl, _pData, _iDataSize, _eRequestType, NULL, NULL, NULL, NULL) + +// make an HTTP request with write callback +#define ProtoHttpRequestCb(_pState, _pUrl, _pData, _iDataSize, _eRequestType, _pWriteCb, _pWriteCbUserData) ProtoHttpRequestCb2(_pState, _pUrl, _pData, _iDataSize, _eRequestType, _pWriteCb, NULL, NULL, _pWriteCbUserData) + +// make an HTTP request with write, custom header and receive header callback +DIRTYCODE_API int32_t ProtoHttpRequestCb2(ProtoHttpRefT *pState, const char *pUrl, const char *pData, int64_t iDataSize, ProtoHttpRequestTypeE eRequestType, ProtoHttpWriteCbT *pWriteCb, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData); + +// abort current transaction +DIRTYCODE_API void ProtoHttpAbort(ProtoHttpRefT *pState); + +// set base url (used in relative url support) +DIRTYCODE_API void ProtoHttpSetBaseUrl(ProtoHttpRefT *pState, const char *pUrl); + +// get location header (requires state and special processing for relative urls) +DIRTYCODE_API int32_t ProtoHttpGetLocationHeader(ProtoHttpRefT *pState, const char *pInpBuf, char *pBuffer, int32_t iBufSize, const char **pHdrEnd); + +// control function; functionality based on input selector +DIRTYCODE_API int32_t ProtoHttpControl(ProtoHttpRefT *pState, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue); + +// return module status based on input selector +DIRTYCODE_API int32_t ProtoHttpStatus(ProtoHttpRefT *pState, int32_t iSelect, void *pBuffer, int32_t iBufSize); + +// check whether a request for the given Url would be a keep-alive transaction +DIRTYCODE_API int32_t ProtoHttpCheckKeepAlive(ProtoHttpRefT *pState, const char *pUrl); + +// give time to module to do its thing (should be called periodically to allow module to perform work) +DIRTYCODE_API void ProtoHttpUpdate(ProtoHttpRefT *pState); + +// add an X.509 CA certificate that will be recognized in future transactions +DIRTYCODE_API int32_t ProtoHttpSetCACert(const uint8_t *pCACert, int32_t iCertSize); + +// same as ProtoHttpSetCACert(), but .pem certificates multiply bundled are parsed bottom to top +DIRTYCODE_API int32_t ProtoHttpSetCACert2(const uint8_t *pCACert, int32_t iCertSize); + +// validate all CAs that have not already been validated +DIRTYCODE_API int32_t ProtoHttpValidateAllCA(void); + +// clear all CA certs +DIRTYCODE_API void ProtoHttpClrCACerts(void); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protohttp_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttp2.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttp2.h new file mode 100644 index 00000000..d424379e --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttp2.h @@ -0,0 +1,193 @@ +/*H********************************************************************************/ +/*! + \File protohttp2.h + + \Description + This module implements an HTTP/2 client that can perform basic transactions + with an HTTP/2 server. It conforms to but does not fully implement + the 2.0 HTTP spec (https://tools.ietf.org/html/rfc7540), and + allows for secure HTTP transactions as well as insecure transactions. + + \Copyright + Copyright (c) Electronic Arts 2016-2018. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +#ifndef _protohttp2_h +#define _protohttp2_h + +/*! +\Moduledef ProtoHttp2 ProtoHttp2 +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/proto/protohttputil.h" + +/*** Defines **********************************************************************/ + +// define for ProtoHttp2Request iDataSize parameter +#define PROTOHTTP2_STREAM_BEGIN (-1) //!< start streaming upload (size unknown) + +// define for ProtoHttp2Send's iDataSize parameter +#define PROTOHTTP2_STREAM_END (0) //!< streaming upload operation is complete + +// define for ProtoHttp2Status/ProtoHttp2Control iStreamId parameter +#define PROTOHTTP2_INVALID_STREAMID (-1) //!< invalid stream for the functions that allow for invalid + +// defines for ProtoHttpRecv's return result +#define PROTOHTTP2_RECVDONE (-1) //!< receive operation complete, and all data has been read +#define PROTOHTTP2_RECVFAIL (-2) //!< receive operation failed +#define PROTOHTTP2_RECVWAIT (-3) //!< waiting for body data +#define PROTOHTTP2_RECVHEAD (-4) //!< in headonly mode and header has been received +#define PROTOHTTP2_RECVBUFF (-5) //!< recvall did not have enough space in the provided buffer + +// generic protohttp errors (do not overlap with RECV* codes) +#define PROTOHTTP2_MINBUFF (-6) //!< not enough input buffer space for operation +#define PROTOHTTP2_TIMEOUT (-7) //!< did not receive response from server in configured time + +/*** Type Definitions *************************************************************/ + +//! opaque module ref +typedef struct ProtoHttp2RefT ProtoHttp2RefT; + +//! write callback info +typedef struct ProtoHttp2WriteCbInfoT +{ + ProtoHttpRequestTypeE eRequestType; + ProtoHttpResponseE eRequestResponse; + int32_t iStreamId; +} ProtoHttp2WriteCbInfoT; + +/*! + \Callback ProtoHttp2WriteCbT + + \Description + Write data callback + + \Input *pState - module state + \Input *pCbInfo - information about about the request this data is for + \Input *pData - data we received from the server + \Input iDataSize - size of the data + \Input *pUserData - user data pointer passed along in the callback + + \Output + int32_t - unused +*/ +typedef int32_t (ProtoHttp2WriteCbT)(ProtoHttp2RefT *pState, const ProtoHttp2WriteCbInfoT *pCbInfo, const uint8_t *pData, int32_t iDataSize, void *pUserData); + +/*! + \Callback ProtoHttp2CustomHeaderCbT + + \Description + Callback that may be used to customize request header before sending + + \Input *pState - module state + \Input *pHeader - [out] request headers that we are going to send + \Input uHeaderSize - size of the request headers + \Input *pData - pointer to data sent in the request. deprecated, will always be NULL + \Input iDataLen - size of the data sent in the request. deprecated, will always be 0 + \Input *pUserData - user data pointer passed along in the callback + + \Output + int32_t - negative=error, size of the header=success + + \Deprecated + pData and iDataLen are deprecated. We will pass NULL and 0 to them respectively. +*/ +typedef int32_t (ProtoHttp2CustomHeaderCbT)(ProtoHttp2RefT *pState, char *pHeader, uint32_t uHeaderSize, const uint8_t *pData, int64_t iDataLen, void *pUserData); + +/*! + \Callback ProtoHttp2ReceiveHeaderCbT + + \Description + Callback that may be used to implement custom header parsing on header receipt + + \Input *pState - module state + \Input iStreamId - stream identifier for request + \Input *pHeader - header we received from the server + \Input uHeaderSize - size of the response header + \Input *pUserData - user data pointer passed along in the callback + + \Output + int32_t - unused +*/ +typedef int32_t (ProtoHttp2ReceiveHeaderCbT)(ProtoHttp2RefT *pState, int32_t iStreamId, const char *pHeader, uint32_t uHeaderSize, void *pUserData); + +//! state of the stream (see https://tools.ietf.org/html/rfc7540#section-5.1 for reference) +typedef enum ProtoHttp2StreamStateE +{ + STREAMSTATE_IDLE, + STREAMSTATE_OPEN, + STREAMSTATE_HALF_CLOSED_LOCAL, + STREAMSTATE_HALF_CLOSED_REMOTE, + STREAMSTATE_CLOSED +} ProtoHttp2StreamStateE; + +/*** Functions ********************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// allocate the module state and prepare for use +DIRTYCODE_API ProtoHttp2RefT *ProtoHttp2Create(int32_t iRecvBuf); + +// give time to module to do its thing +DIRTYCODE_API void ProtoHttp2Update(ProtoHttp2RefT *pState); + +// destroy the module and release its state +DIRTYCODE_API void ProtoHttp2Destroy(ProtoHttp2RefT *pState); + +// set the custom header callbacks +DIRTYCODE_API void ProtoHttp2Callback(ProtoHttp2RefT *pState, ProtoHttp2CustomHeaderCbT *pCustomHeaderCb, ProtoHttp2ReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData); + +// initiate an HTTP request +DIRTYCODE_API int32_t ProtoHttp2Request(ProtoHttp2RefT *pState, const char *pUrl, const uint8_t *pData, int32_t iDataSize, ProtoHttpRequestTypeE eRequestType, int32_t *pStreamId); + +// initiate an HTTP request with a write callback +DIRTYCODE_API int32_t ProtoHttp2RequestCb(ProtoHttp2RefT *pState, const char *pUrl, const uint8_t *pData, int32_t iDataSize, ProtoHttpRequestTypeE eRequestType, int32_t *pStreamId, ProtoHttp2WriteCbT *pWriteCb, void *pUserData); + +// initiate an HTTP request with a write, custom header and receive header callback +DIRTYCODE_API int32_t ProtoHttp2RequestCb2(ProtoHttp2RefT *pState, const char *pUrl, const uint8_t *pData, int32_t iDataSize, ProtoHttpRequestTypeE eRequestType, int32_t *pStreamId, ProtoHttp2WriteCbT *pWriteCb, ProtoHttp2CustomHeaderCbT *pCustomHeaderCb, ProtoHttp2ReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData); + +// send data for a specific stream, NULL data ends the stream +DIRTYCODE_API int32_t ProtoHttp2Send(ProtoHttp2RefT *pState, int32_t iStreamId, const uint8_t *pData, int32_t iDataSize); + +// receive the actual url data +DIRTYCODE_API int32_t ProtoHttp2Recv(ProtoHttp2RefT *pState, int32_t iStreamId, uint8_t *pBuffer, int32_t iBufMin, int32_t iBufMax); + +// receive all of the response data for the given stream +DIRTYCODE_API int32_t ProtoHttp2RecvAll(ProtoHttp2RefT *pState, int32_t iStreamId, uint8_t *pBuffer, int32_t iBufSize); + +// abort the passed in stream's transaction +DIRTYCODE_API void ProtoHttp2Abort(ProtoHttp2RefT *pState, int32_t iStreamId); + +// close the connection to the server +DIRTYCODE_API void ProtoHttp2Close(ProtoHttp2RefT *pState); + +// return module status based on input selector +DIRTYCODE_API int32_t ProtoHttp2Status(ProtoHttp2RefT *pState, int32_t iStreamId, int32_t iSelect, void *pBuffer, int32_t iBufSize); + +// control function; functionality based on input selector +DIRTYCODE_API int32_t ProtoHttp2Control(ProtoHttp2RefT *pState, int32_t iStreamId, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// set base url (used in relative url support) +DIRTYCODE_API void ProtoHttp2SetBaseUrl(ProtoHttp2RefT *pState, const char *pUrl); + +// get location header (requires state and special processing for relative urls) +DIRTYCODE_API int32_t ProtoHttp2GetLocationHeader(ProtoHttp2RefT *pState, const char *pInpBuf, char *pBuffer, int32_t iBufSize, const char **pHdrEnd); + +// release resources for the stream identified by id +DIRTYCODE_API void ProtoHttp2StreamFree(ProtoHttp2RefT *pState, int32_t iStreamId); + +#if defined(__cplusplus) +} +#endif + +//@} + +#endif // _protohttp2_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttpmanager.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttpmanager.h new file mode 100644 index 00000000..d7815a27 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttpmanager.h @@ -0,0 +1,131 @@ +/*H********************************************************************************/ +/*! + \File protohttpmanager.h + + \Description + High-level module designed to create and manage a pool of ProtoHttp refs. A + client application can submit rapid-fire http requests and ProtoHttpManager + will distribute them efficiently across the ref pool internally, queuing + them for efficient use of keep-alive and pipelining requests where possible. + + \Notes + None. + + \Todo + Pipelining + + \Copyright + Copyright (c) Electronic Arts 2009. + + \Version 1.0 05/20/2009 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _protohttpmanager_h +#define _protohttpmanager_h + +/*! +\Moduledef ProtoHttpManager ProtoHttpManager +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/proto/protohttp.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! httpmanager stats +typedef struct HttpManagerStatT +{ + uint32_t uNumActiveTransactions; //!< current number of active transactions + uint32_t uMaxActiveTransactions; //!< maximum (highwater) number of active transactions + uint32_t uNumQueuedTransactions; //!< current number of queued transactions + uint32_t uMaxQueuedTransactions; //!< maximum (highwater) number of queued transactions + uint32_t uNumTransactions; //!< total number of transactions issued + uint32_t uNumKeepAliveTransactions; //!< total number of keep-alive transactions issued + uint32_t uNumPipelinedTransactions; //!< total number of pipelined transactions issued + uint32_t uSumQueueWaitLatency; //!< total amount of time spent waiting in queue + uint32_t uMaxQueueWaitLatency; //!< max time any single request had to wait + uint32_t uSumQueueFreeLatency; //!< total amount of time spent waiting for transaction to freed + uint32_t uMaxQueueFreeLatency; //!< max time any single transaction waited to be freed + uint64_t uTransactionBytes; //!< total bytes xferred + uint32_t uTransactionTime; //!< total transaction time +} HttpManagerStatT; + +//! opaque module ref +typedef struct HttpManagerRefT HttpManagerRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// allocate module state and prepare for use +DIRTYCODE_API HttpManagerRefT *HttpManagerCreate(int32_t iHttpBufSize, int32_t iHttpNumRefs); + +// set custom header callback +DIRTYCODE_API void HttpManagerCallback(HttpManagerRefT *pHttpManager, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb); + +// destroy the module and release its state +DIRTYCODE_API void HttpManagerDestroy(HttpManagerRefT *pHttpManager); + +// allocate a new transaction handle +DIRTYCODE_API int32_t HttpManagerAlloc(HttpManagerRefT *pHttpManager); + +// release a transaction handle +DIRTYCODE_API void HttpManagerFree(HttpManagerRefT *pHttpManager, int32_t iHandle); + +// initiate an HTTP transfer +DIRTYCODE_API int32_t HttpManagerGet(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl, uint32_t bHeadOnly); + +// return the actual url data +DIRTYCODE_API int32_t HttpManagerRecv(HttpManagerRefT *pHttpManager, int32_t iHandle, char *pBuffer, int32_t iBufMin, int32_t iBufMax); + +// receive all of the response data +DIRTYCODE_API int32_t HttpManagerRecvAll(HttpManagerRefT *pHttpManager, int32_t iHandle, char *pBuffer, int32_t iBufSize); + +// initiate transfer of data to to the server via an HTTP POST command +DIRTYCODE_API int32_t HttpManagerPost(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl, const char *pData, int64_t iDataSize, uint32_t bDoPut); + +// send the actual url data +DIRTYCODE_API int32_t HttpManagerSend(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pData, int32_t iDataSize); + +// make an http request +#define HttpManagerRequest(_pHttpManager, _iHandle, _pUrl, _pData, _iDataSize, _eRequestType) HttpManagerRequestCb2(_pHttpManager, _iHandle, _pUrl, _pData, _iDataSize, _eRequestType, NULL, NULL, NULL, NULL) + +// make an http request with write callback +#define HttpManagerRequestCb(_pHttpManager, _iHandle, _pUrl, _pData, _iDataSize, _eRequestType, _pWriteCb, _pWriteCbUserData) HttpManagerRequestCb2(_pHttpManager, _iHandle, _pUrl, _pData, _iDataSize, _eRequestType, _pWriteCb, NULL, NULL, _pWriteCbUserData) + +// make an http request with write, custom header and receive header callback +DIRTYCODE_API int32_t HttpManagerRequestCb2(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl, const char *pData, int64_t iDataSize, ProtoHttpRequestTypeE eRequestType, ProtoHttpWriteCbT *pWriteCb, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData); + +// set base url +DIRTYCODE_API void HttpManagerSetBaseUrl(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl); + +// control function; functionality based on input selector (-1 to apply to all refs) +DIRTYCODE_API int32_t HttpManagerControl(HttpManagerRefT *pHttpManager, int32_t iHandle, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue); + +// return module status based on input selector +DIRTYCODE_API int32_t HttpManagerStatus(HttpManagerRefT *pHttpManager, int32_t iHandle, int32_t iSelect, void *pBuffer, int32_t iBufSize); + +// give time to module to do its thing (should be called periodically to allow module to perform work) +DIRTYCODE_API void HttpManagerUpdate(HttpManagerRefT *pHttpManager); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protohttpmanager_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttpserv.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttpserv.h new file mode 100644 index 00000000..f5c6f97a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttpserv.h @@ -0,0 +1,177 @@ +/*H********************************************************************************/ +/*! + \File protohttpserv.h + + \Description + This module implements an HTTP server that can perform basic transactions + (get/put) with an HTTP client. It conforms to but does not fully implement + the 1.1 HTTP spec (http://www.w3.org/Protocols/rfc2616/rfc2616.html), and + allows for secure HTTP transactions as well as insecure transactions. + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 1.0 12/11/2013 (jbrookes) Initial version, based on HttpServ tester2 module +*/ +/********************************************************************************H*/ + +#ifndef _protohttpserv_h +#define _protohttpserv_h + +/*! +\Moduledef ProtoHttpServ ProtoHttpServ +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/proto/protohttputil.h" + +/*** Defines **********************************************************************/ + +#define PROTOHTTPSERV_FLAG_LOOPBACK (0x01) //!< when set, only traffic from the same host will be accepted on the listen port + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct ProtoHttpServRequestT +{ + ProtoHttpRequestTypeE eRequestType; //!< The HTTP method + uint32_t uAddr; //!< Address of where the request originated from + int64_t iContentLength; //!< `Content-Length` header value, how big is the request + int64_t iContentRecv; //!< Amount of data received when processing request + char strContentType[64]; //!< `Content-Type` header value, what type of data is being received + char strUrl[128]; //!< The path / resource (when a RESTful API) that is being accessed. Example: /person/{id} + char strQuery[128]; //!< Query strings passed into the request. Example: ?name="foobar" + char strHeader[16*1024]; //!< HTTP headers sent with the request. Use ProtoHttpUtil functions for parsing + void *pData; //!< User Specific data (store any data to help handle the request) +} ProtoHttpServRequestT; + +typedef struct ProtoHttpServResponseT +{ + ProtoHttpResponseE eResponseCode; //!< The HTTP code (200, 404, etc) + char strContentType[64]; //!< `Content-Type` header value, what type of data is being sent + int64_t iContentLength; //!< `Content-Length` header value, how much data is being sent. + int64_t iContentRead; //!< Amount of data read that is being sent + char strHeader[1*1024]; //!< Custom headers being sent + int32_t iHeaderLen; //!< Size of the custom headers + int32_t iChunkLength; //!< Use `Transfer-Encoding: chunked` if greater than zero + char strLocation[1 * 1024]; //!< Redirect Location + void *pData; //!< User Specific data (store any data to help respond) +} ProtoHttpServResponseT; + +/*! + \Callback ProtoHttpServRequestCbT + + \Description + Invoked when a request is ready for processing + + \Input *pRequest - Information that allows for processing of request + \Input *pResponse - [out] Information that ProtoHttpServ used in responding to the request + \Input *pUserData - User Specific Data that was passed into ProtoHttpServCallback + + \Output + int32_t - zero=complete, positive=in-progress, negative=error +*/ +typedef int32_t (ProtoHttpServRequestCbT)(ProtoHttpServRequestT *pRequest, ProtoHttpServResponseT *pResponse, void *pUserData); + +/*! + \Callback ProtoHttpServReceiveCbT + + \Description + Invoked when new incoming data is received for a request + + \Input *pRequest - [in/out] Information about the request + \Input *pBuffer - The data that was received (NULL when complete) + \Input iBufSize - The size of the data received (zero when complete) + \Input *pUserData - User Specific Data that was passed into ProtoHttpServCallback + + \Output + int32_t - zero=no data processed, positive=amount of data processed, negative=error +*/ +typedef int32_t (ProtoHttpServReceiveCbT)(ProtoHttpServRequestT *pRequest, const char *pBuffer, int32_t iBufSize, void *pUserData); + +/*! + \Callback ProtoHttpServSendCbT + + \Description + Invoked when outgoing data needs to be sent for the response + + \Input *pResponse - Information about the response we are sending + \Input *pBuffer - The data we are writting to for the response (NULL when complete) + \Input iBufSize - The size of the outgoing buffer (zero when complete) + \Input *pUserData - User Specific Data that was passed into ProtoHttpServCallback + + \Output + int32_t - positive=amount of data written, other=error +*/ +typedef int32_t (ProtoHttpServSendCbT)(ProtoHttpServResponseT *pResponse, char *pBuffer, int32_t iBufSize, void *pUserData); + +/*! + \Callback ProtoHttpServHeaderCbT + + \Description + Invoked when a new request header is received + + \Input *pRequest - [in/out] Information about the request we are going to process + \Input *pResponse - [out] Used for handling the response, this allows us to error if header is malformed + \Input *pUserData - User Specific Data that was passed into ProtoHttpServCallback + + \Output + int32_t - positive=success, negative=error +*/ +typedef int32_t (ProtoHttpServHeaderCbT)(ProtoHttpServRequestT *pRequest, ProtoHttpServResponseT *pResponse, void *pUserData); + +//! logging function type +typedef void (ProtoHttpServLogCbT)(const char *pText, void *pUserData); + +//! opaque module ref +typedef struct ProtoHttpServRefT ProtoHttpServRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create an httpserv module +DIRTYCODE_API ProtoHttpServRefT *ProtoHttpServCreate(int32_t iPort, int32_t iSecure, const char *pName); + +// create an httpserv module +DIRTYCODE_API ProtoHttpServRefT *ProtoHttpServCreate2(int32_t iPort, int32_t iSecure, const char *pName, uint32_t uFlags); + +// destroy an httpserv module +DIRTYCODE_API void ProtoHttpServDestroy(ProtoHttpServRefT *pHttpServ); + +// set httpserv callback function handlers (required) +DIRTYCODE_API void ProtoHttpServCallback(ProtoHttpServRefT *pHttpServ, ProtoHttpServRequestCbT *pRequestCb, ProtoHttpServReceiveCbT *pReceiveCb, ProtoHttpServSendCbT *pSendCb, ProtoHttpServHeaderCbT *pHeaderCb, ProtoHttpServLogCbT *pLogCb, void *pUserData); + +// control function; functionality based on input selector +DIRTYCODE_API int32_t ProtoHttpServControl(ProtoHttpServRefT *pHttpServ, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue); + +// control function allowing thread-specific control; functionality based on input selector +DIRTYCODE_API int32_t ProtoHttpServControl2(ProtoHttpServRefT *pHttpServ, int32_t iThread, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue); + +// return module status based on input selector +DIRTYCODE_API int32_t ProtoHttpServStatus(ProtoHttpServRefT *pHttpServ, int32_t iSelect, void *pBuffer, int32_t iBufSize); + +// update the module +DIRTYCODE_API void ProtoHttpServUpdate(ProtoHttpServRefT *pHttpServ); + +// updates the backlog setting on the listen socket, not required to be called unless you need to update the backlog +DIRTYCODE_API int32_t ProtoHttpServListen(ProtoHttpServRefT *pHttpServ, int32_t iBacklog); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protohttpserv_h + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttputil.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttputil.h new file mode 100644 index 00000000..df7a8735 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protohttputil.h @@ -0,0 +1,176 @@ +/*H********************************************************************************/ +/*! + \File protohttputil.h + + \Description + This module implements HTTP support routines such as URL processing, header + parsing, etc. + + \Copyright + Copyright (c) Electronic Arts 2000-2012. + + \Version 1.0 12/18/2012 (jbrookes) Refactored into separate module from protohttp. +*/ +/********************************************************************************H*/ + +#ifndef _protohttputil_h +#define _protohttputil_h + +/*! +\Moduledef ProtoHttpUtil ProtoHttpUtil +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +//! max supported protocol scheme length +#define PROTOHTTPUTIL_SCHEME_MAX (32) + +/*** Macros ***********************************************************************/ + +//! get class of response code +#define PROTOHTTP_GetResponseClass(_eError) (((_eError)/100) * 100) + +/*** Type Definitions *************************************************************/ + +//! request types +typedef enum ProtoHttpRequestTypeE +{ + PROTOHTTP_REQUESTTYPE_HEAD = 0, //!< HEAD request - does not return any data + PROTOHTTP_REQUESTTYPE_GET, //!< GET request - get data from a server + PROTOHTTP_REQUESTTYPE_POST, //!< POST request - post data to a server + PROTOHTTP_REQUESTTYPE_PUT, //!< PUT request - put data on a server + PROTOHTTP_REQUESTTYPE_DELETE, //!< DELETE request - delete resource from a server + PROTOHTTP_REQUESTTYPE_OPTIONS, //!< OPTIONS request - get server options for specified resource + PROTOHTTP_REQUESTTYPE_PATCH, //!< PATCH request - like PUT but updates the ressource instead of overwriting + PROTOHTTP_REQUESTTYPE_CONNECT, //!< CONNECT request - connect to a proxy that can tunnel (internal use only) + + PROTOHTTP_NUMREQUESTTYPES +} ProtoHttpRequestTypeE; + +/*! HTTP response codes - see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10 + for more detailed information. These response codes may be retrieved from ProtoHttp + by calling ProtoHttpStatus('code'). Note that response codes are not available until + a response has been received from the server and parsed by ProtoHttp. If a response + code is not yet available, -1 will be returned. */ +typedef enum ProtoHttpResponseE +{ + PROTOHTTP_RESPONSE_PENDING = -1, //!< response has not yet been received + + // 1xx - informational reponse + PROTOHTTP_RESPONSE_INFORMATIONAL = 100, //!< generic name for a 1xx class response + PROTOHTTP_RESPONSE_CONTINUE = 100, //!< continue with request, generally ignored + PROTOHTTP_RESPONSE_SWITCHPROTO, //!< 101 - OK response to client switch protocol request + + // 2xx - success response + PROTOHTTP_RESPONSE_SUCCESSFUL = 200, //!< generic name for a 2xx class response + PROTOHTTP_RESPONSE_OK = 200, //!< client's request was successfully received, understood, and accepted + PROTOHTTP_RESPONSE_CREATED, //!< new resource has been created + PROTOHTTP_RESPONSE_ACCEPTED, //!< request accepted but not complete + PROTOHTTP_RESPONSE_NONAUTH, //!< non-authoritative info (ok) + PROTOHTTP_RESPONSE_NOCONTENT, //!< request fulfilled, no message body + PROTOHTTP_RESPONSE_RESETCONTENT, //!< request success, reset document view + PROTOHTTP_RESPONSE_PARTIALCONTENT, //!< server has fulfilled partial GET request + + // 3xx - redirection response + PROTOHTTP_RESPONSE_REDIRECTION = 300, //!< generic name for a 3xx class response + PROTOHTTP_RESPONSE_MULTIPLECHOICES = 300, //!< requested resource corresponds to a set of representations + PROTOHTTP_RESPONSE_MOVEDPERMANENTLY, //!< requested resource has been moved permanently to new URI + PROTOHTTP_RESPONSE_FOUND, //!< requested resources has been moved temporarily to a new URI + PROTOHTTP_RESPONSE_SEEOTHER, //!< response can be found under a different URI + PROTOHTTP_RESPONSE_NOTMODIFIED, //!< response to conditional get when document has not changed + PROTOHTTP_RESPONSE_USEPROXY, //!< requested resource must be accessed through proxy + PROTOHTTP_RESPONSE_RESERVED, //!< old response code - reserved + PROTOHTTP_RESPONSE_TEMPREDIRECT, //!< requested resource resides temporarily under a different URI + + // 4xx - client error response + PROTOHTTP_RESPONSE_CLIENTERROR = 400, //!< generic name for a 4xx class response + PROTOHTTP_RESPONSE_BADREQUEST = 400, //!< request could not be understood by server due to malformed syntax + PROTOHTTP_RESPONSE_UNAUTHORIZED, //!< request requires user authorization + PROTOHTTP_RESPONSE_PAYMENTREQUIRED, //!< reserved for future user + PROTOHTTP_RESPONSE_FORBIDDEN, //!< request understood, but server will not fulfill it + PROTOHTTP_RESPONSE_NOTFOUND, //!< Request-URI not found + PROTOHTTP_RESPONSE_METHODNOTALLOWED, //!< method specified in the Request-Line is not allowed + PROTOHTTP_RESPONSE_NOTACCEPTABLE, //!< resource incapable of generating content acceptable according to accept headers in request + PROTOHTTP_RESPONSE_PROXYAUTHREQ, //!< client must first authenticate with proxy + PROTOHTTP_RESPONSE_REQUESTTIMEOUT, //!< client did not produce response within server timeout + PROTOHTTP_RESPONSE_CONFLICT, //!< request could not be completed due to a conflict with current state of the resource + PROTOHTTP_RESPONSE_GONE, //!< requested resource is no longer available and no forwarding address is known + PROTOHTTP_RESPONSE_LENGTHREQUIRED, //!< a Content-Length header was not specified and is required + PROTOHTTP_RESPONSE_PRECONFAILED, //!< precondition given in request-header field(s) failed + PROTOHTTP_RESPONSE_REQENTITYTOOLARGE, //!< request entity is larger than the server is able or willing to process + PROTOHTTP_RESPONSE_REQURITOOLONG, //!< Request-URI is longer than the server is willing to interpret + PROTOHTTP_RESPONSE_UNSUPPORTEDMEDIA, //!< request entity is in unsupported format + PROTOHTTP_RESPONSE_REQUESTRANGE, //!< invalid range in Range request header + PROTOHTTP_RESPONSE_EXPECTATIONFAILED, //!< expectation in Expect request-header field could not be met by server + + // 5xx - server error response + PROTOHTTP_RESPONSE_SERVERERROR = 500, //!< generic name for a 5xx class response + PROTOHTTP_RESPONSE_INTERNALSERVERERROR = 500, //!< an unexpected condition prevented the server from fulfilling the request + PROTOHTTP_RESPONSE_NOTIMPLEMENTED, //!< the server does not support the functionality required to fulfill the request + PROTOHTTP_RESPONSE_BADGATEWAY, //!< invalid response from gateway server + PROTOHTTP_RESPONSE_SERVICEUNAVAILABLE, //!< unable to handle request due to a temporary overloading or maintainance + PROTOHTTP_RESPONSE_GATEWAYTIMEOUT, //!< gateway or DNS server timeout + PROTOHTTP_RESPONSE_HTTPVERSUNSUPPORTED //!< the server does not support the HTTP protocol version that was used in the request +} ProtoHttpResponseE; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// parse HTTP request line from the format as sent on the wire +DIRTYCODE_API const char *ProtoHttpUtilParseRequest(const char *pStrHeader, char *pStrMethod, int32_t iMethodLen, char *pStrUri, int32_t iUriLen, char *pStrQuery, int32_t iQueryLen, ProtoHttpRequestTypeE *pRequestType, uint8_t *pHttp1_0); + +// finds the header field value for the specified header field +DIRTYCODE_API const char *ProtoHttpFindHeaderValue(const char *pHdrBuf, const char *pHeaderText); + +// extracts the header field value for the specified header field entry +DIRTYCODE_API int32_t ProtoHttpExtractHeaderValue(const char *pHdrField, char *pOutBuf, int32_t iOutLen, const char **ppHdrEnd); + +// parse header response code from HTTP header +DIRTYCODE_API int32_t ProtoHttpParseHeaderCode(const char *pHdrBuf); + +// extract a header value from the specified header text +DIRTYCODE_API int32_t ProtoHttpGetHeaderValue(void *pState, const char *pHdrBuf, const char *pName, char *pBuffer, int32_t iBufSize, const char **pHdrEnd); + +// get next header name and value from header buffer +DIRTYCODE_API int32_t ProtoHttpGetNextHeader(void *pState, const char *pHdrBuf, char *pName, int32_t iNameSize, char *pValue, int32_t iValSize, const char **pHdrEnd); + +// url-encode a integer parameter +DIRTYCODE_API int32_t ProtoHttpUrlEncodeIntParm(char *pBuffer, int32_t iLength, const char *pParm, int32_t iValue); + +// url-encode a string parameter +DIRTYCODE_API int32_t ProtoHttpUrlEncodeStrParm(char *pBuffer, int32_t iLength, const char *pParm, const char *pData); + +// url-encode a string parameter with custom safe table +DIRTYCODE_API int32_t ProtoHttpUrlEncodeStrParm2(char *pBuffer, int32_t iLength, const char *pParm, const char *pData, const char *pStrSafe); + +// url-decode a string parameter +DIRTYCODE_API int32_t ProtoHttpUrlDecodeStrParm(const char *pBuffer, char *pData, int32_t iDataSize); + +// parse a url into component parts +DIRTYCODE_API const char *ProtoHttpUrlParse(const char *pUrl, char *pKind, int32_t iKindSize, char *pHost, int32_t iHostSize, int32_t *pPort, int32_t *pSecure); + +// parse a url into component parts +DIRTYCODE_API const char *ProtoHttpUrlParse2(const char *pUrl, char *pKind, int32_t iKindSize, char *pHost, int32_t iHostSize, int32_t *pPort, int32_t *pSecure, uint8_t *bPortSpecified); + +// get next param name and value from URI buffer +DIRTYCODE_API int32_t ProtoHttpGetNextParam(void *pState, const char *pUriBuf, char *pName, int32_t iNameSize, char *pValue, int32_t iValSize, const char **pUriEnd); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protohttp_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protomangle.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protomangle.h new file mode 100644 index 00000000..7edd9390 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protomangle.h @@ -0,0 +1,139 @@ +/*H*************************************************************************************************/ +/*! + + \File protomangle.h + + \Description + This module encapsulates use of the EA.Com Demangler service. + + \Notes + The ProtoMangle client was developed from version 1.1 of the Peer Connection Service + Protocol Specification: + + http://docs.online.ea.com/infrastructure/demangler/peer-connection-service-protocol.doc + + Please see the following documentation for information on the Demangler service: + + http://docs.online.ea.com/infrastructure/demangler/demangler.html + http://docs.online.ea.com/infrastructure/demangler/peer-connection-service-reqs.doc + http://docs.online.ea.com/infrastructure/demangler/peer-connection-service-engineering-spec.doc + http://docs.online.ea.com/infrastructure/demangler/Peer-Connection-Service-Architecture.doc + + Original work done by C&C: Red Alert 2, on which the Demangler service is based: + + http://www.worldwide.ea.com/articles/render/attachment.aspx?id=919 + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2003. ALL RIGHTS RESERVED. + + \Version 1.0 04/03/2003 (JLB) First Version +*/ +/*************************************************************************************************H*/ + +#ifndef _protomangle_h +#define _protomangle_h + +/*! +\Moduledef ProtoMangle ProtoMangle +\Modulemember Proto +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +//! max cookie length, including terminator +#define PROTOMANGLE_STRCOOKIE_MAX (64) + +//! max gameFeatureID length, including terminator +#define PROTOMANGLE_STRGAMEID_MAX (32) + +//! max LKey length, including terminator +#define PROTOMANGLE_STRLKEY_MAX (64) + +//! max server name length, including terminator +#define PROTOMANGLE_STRSERVER_MAX (32) + +/* + Default server name/port +*/ + +//! default server name +#define PROTOMANGLE_SERVER ("demangler.ea.com") + +//! development server +#define PROTOMANGLE_TEST_SERVER ("peach.online.ea.com") + +//! default server port +#define PROTOMANGLE_PORT (3658) + +//! response codes +typedef enum ProtoMangleStatusE +{ + PROTOMANGLE_STATUS_CONNECTED, + PROTOMANGLE_STATUS_FAILED, + + PROTOMANGLE_NUMSTATUS +} ProtoMangleStatusE; + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct ProtoMangleRefT ProtoMangleRefT; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// allocate module state and prepare for use +DIRTYCODE_API ProtoMangleRefT *ProtoMangleCreate(const char *pServer, int32_t iPort, const char *pGameID, const char *pLKey); + +// destroy the module and release its state +DIRTYCODE_API void ProtoMangleDestroy(ProtoMangleRefT *pRef); + +// give time to module to do its thing (should be called periodically to allow module to perform work) +DIRTYCODE_API void ProtoMangleUpdate(ProtoMangleRefT *pRef); + +// connect to demangler server +DIRTYCODE_API void ProtoMangleConnect(ProtoMangleRefT *pRef, int32_t iGamePort, const char *pSessID); + +// connect to demangler server (extended) +DIRTYCODE_API void ProtoMangleConnect2(ProtoMangleRefT *pRef, int32_t iGamePort, const char *pSessID, int32_t iSessIDLen); + +// connect to demangler server, using the given socket ref to issue UDP probes instead of creating one +DIRTYCODE_API void ProtoMangleConnectSocket(ProtoMangleRefT *pRef, intptr_t uSocketRef, const char *pSessID); + +// get result +DIRTYCODE_API int32_t ProtoMangleComplete(ProtoMangleRefT *pRef, int32_t *pAddr, int32_t *pPort); + +// submit result to server +DIRTYCODE_API int32_t ProtoMangleReport(ProtoMangleRefT *pRef, ProtoMangleStatusE eStatus, int32_t iLatency); + +// protomangle control +DIRTYCODE_API int32_t ProtoMangleControl(ProtoMangleRefT *pRef, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue); + +// get module status based on selector +DIRTYCODE_API int32_t ProtoMangleStatus(ProtoMangleRefT *pRef, int32_t iSelect, void *pBuf, int32_t iBufSize); + +// find a secure template for given network parameters +#if defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) +const char *ProtoMangleFindTemplate(char *pStrTemplateName, int32_t iTemplateNameSize, int32_t iLocalPort, int32_t iRemotePort, uint8_t bTcp, int32_t iVerbose); +#endif + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protomangle_h + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoname.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoname.h new file mode 100644 index 00000000..36778254 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoname.h @@ -0,0 +1,63 @@ +/*H*************************************************************************************************/ +/*! + + \File protoname.h + + \Description + This module provides name lookup services via DNS. It is platform indepedent + and can be used to resolve names for use with other protocol modules. At this + point, name support is being removed from other protocol modules so it can be + centralized here. + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 03/19/02 (GWS) First Version + +*/ +/*************************************************************************************************H*/ + +#ifndef _protoname_h +#define _protoname_h + +/*! +\Moduledef ProtoName ProtoName +\Modulemember Proto +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynet.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// async lookup of a domain name +DIRTYCODE_API HostentT *ProtoNameAsync(const char *pName, int32_t iTimeout); + +// synchronous (blocking) lookup of a domain name. +DIRTYCODE_API uint32_t ProtoNameSync(const char *pName, int32_t iTimeout); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protoname_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protossl.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protossl.h new file mode 100644 index 00000000..f6e7179d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protossl.h @@ -0,0 +1,277 @@ +/*H*************************************************************************************************/ +/*! + \File protossl.h + + \Description + This module is a from-scratch TLS implementation. It does not use any + third-party code of any kind and was developed entirely by EA. + + \Notes + References: + TLS1.0 RFC: https://tools.ietf.org/html/rfc2246 + TLS1.1 RFC: https://tools.ietf.org/html/rfc4346 + TLS1.2 RFC: https://tools.ietf.org/html/rfc5246 + TLS1.3 RFC: https://tools.ietf.org/html/rfc8446 + ASN.1 encoding rules: https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + + \Copyright + Copyright (c) Electronic Arts 2002-2018 + + \Version 03/08/2002 (gschaefer) Initial SSL 2.0 implementation + \Version 03/03/2004 (sbevan) Added certificate validation + \Version 11/05/2005 (gschaefer) Rewritten to follow SSL 3.0 specification + \Version 10/12/2012 (jbrookes) Added support for TLS1.0 & TLS1.1 + \Version 10/20/2013 (jbrookes) Added server handshake, client cert support + \Version 11/06/2013 (jbrookes) Added support for TLS1.2 + \Version 03/31/2017 (eesponda) Added support for EC ciphers + \Version 03/28/2018 (jbrookes) Added support for TLS1.3 + \Version 08/15/2018 (jbrookes) Removed SSLv3 & RC4 ciphers +*/ +/*************************************************************************************************H*/ + +#ifndef _protossl_h +#define _protossl_h + +/*! +\Moduledef ProtoSSL ProtoSSL +\Modulemember Proto +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptdef.h" +#include "DirtySDK/crypt/cryptrsa.h" + +/*** Defines ***************************************************************************/ + +// supported TLS versions +#define PROTOSSL_VERSION_TLS1_0 (0x0301) +#define PROTOSSL_VERSION_TLS1_1 (0x0302) +#define PROTOSSL_VERSION_TLS1_2 (0x0303) +#define PROTOSSL_VERSION_TLS1_3 (0x0304) + +// protossl failure codes (retrieve with ProtoSSLStat('fail') +#define PROTOSSL_ERROR_NONE ( 0) //!< no error +#define PROTOSSL_ERROR_DNS (-1) //!< DNS failure +#define PROTOSSL_ERROR_CONN (-10) //!< TCP connection failure +#define PROTOSSL_ERROR_CONN_SSL2 (-11) //!< connection attempt was using unsupported SSLv2 record format +#define PROTOSSL_ERROR_CONN_NOTSSL (-12) //!< connection attempt was not recognized as SSL +#define PROTOSSL_ERROR_CONN_MINVERS (-13) //!< request failed minimum protocol version restriction +#define PROTOSSL_ERROR_CONN_MAXVERS (-14) //!< request failed maximum protocol version restriction +#define PROTOSSL_ERROR_CONN_NOCIPHER (-15) //!< no supported cipher +#define PROTOSSL_ERROR_CONN_NOCURVE (-16) //!< no supported curve +#define PROTOSSL_ERROR_CERT_INVALID (-20) //!< certificate invalid +#define PROTOSSL_ERROR_CERT_HOST (-21) //!< certificate not issued to this host +#define PROTOSSL_ERROR_CERT_NOTRUST (-22) //!< certificate is not trusted (recognized) +#define PROTOSSL_ERROR_CERT_MISSING (-23) //!< certificate not provided in certificate message +#define PROTOSSL_ERROR_CERT_BADDATE (-24) //!< certificate date range validity check failed +#define PROTOSSL_ERROR_CERT_REQUEST (-25) //!< CA fetch request failed +#define PROTOSSL_ERROR_SETUP (-30) //!< failure in secure setup +#define PROTOSSL_ERROR_SECURE (-31) //!< failure in secure connection after setup +#define PROTOSSL_ERROR_UNKNOWN (-32) //!< unknown failure + +// SSLv3 cipher suites (available for TLS1.0+ although SSLv3 is no longer supported) +#define PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA (1<<0) +#define PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA (1<<1) +// TLS1.0 cipher suites +#define PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA (1<<2) +#define PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA (1<<3) +#define PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (1<<4) +#define PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (1<<5) +// TLS1.2 cipher suites +#define PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256 (1<<6) +#define PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256 (1<<7) +#define PROTOSSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256 (1<<8) +#define PROTOSSL_CIPHER_RSA_WITH_AES_256_GCM_SHA384 (1<<9) +#define PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (1<<10) +#define PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (1<<11) +#define PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (1<<12) +#define PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (1<<13) +#define PROTOSSL_CIPHER_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (1<<14) +#define PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (1<<15) +#define PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (1<<16) +#define PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (1<<17) +#define PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (1<<18) +#define PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (1<<19) +// TLS1.3 cipher suites +#define PROTOSSL_CIPHER_AES_128_GCM_SHA256 (1<<20) +#define PROTOSSL_CIPHER_AES_256_GCM_SHA384 (1<<21) +#define PROTOSSL_CIPHER_CHACHA20_POLY1305_SHA256 (1<<22) + +//! all rsa cipher suites (minus disabled rc4-md5) +#define PROTOSSL_CIPHER_RSA (\ + PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA|PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA|\ + PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256|PROTOSSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256|\ + PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256|PROTOSSL_CIPHER_RSA_WITH_AES_256_GCM_SHA384) + +//! all ecc cipher suites +#define PROTOSSL_CIPHER_ECC (\ + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA|PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256|\ + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA|PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA384|\ + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256|PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA|\ + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_GCM_SHA384|PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA|\ + PROTOSSL_CIPHER_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256|\ + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256|PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256|\ + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384|PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384|\ + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) + +//! all tls1.3 cipher suites +#define PROTOSSL_CIPHER_ALL_13 (PROTOSSL_CIPHER_AES_128_GCM_SHA256|PROTOSSL_CIPHER_AES_256_GCM_SHA384|PROTOSSL_CIPHER_CHACHA20_POLY1305_SHA256) + +//! default cipher suites +#define PROTOSSL_CIPHER_ALL (PROTOSSL_CIPHER_RSA|PROTOSSL_CIPHER_ECC|PROTOSSL_CIPHER_ALL_13) + +// client cert flags (ProtoSSLControl() 'ccrt' selector) +#define PROTOSSL_CLIENTCERT_NONE (0) +#define PROTOSSL_CLIENTCERT_OPTIONAL (1) +#define PROTOSSL_CLIENTCERT_REQUIRED (2) + +// clienthello extensions +#define PROTOSSL_HELLOEXTN_NONE (0) +#define PROTOSSL_HELLOEXTN_SERVERNAME (1) +#define PROTOSSL_HELLOEXTN_SIGALGS (2) +#define PROTOSSL_HELLOEXTN_ALPN (4) +#define PROTOSSL_HELLOEXTN_ELLIPTIC_CURVES (8) +// all extensions +#define PROTOSSL_HELLOEXTN_ALL (\ + PROTOSSL_HELLOEXTN_SERVERNAME|PROTOSSL_HELLOEXTN_SIGALGS|PROTOSSL_HELLOEXTN_ALPN) +// default extensions +#define PROTOSSL_HELLOEXTN_DEFAULT (PROTOSSL_HELLOEXTN_ALL) + +//! elliptic curves +#define PROTOSSL_CURVE_SECP256R1 (1 << 0) +#define PROTOSSL_CURVE_SECP384R1 (1 << 1) +#define PROTOSSL_CURVE_X25519 (1 << 2) +#define PROTOSSL_CURVE_X448 (1 << 3) + +//! all curves +#define PROTOSSL_CURVE_ALL (\ + PROTOSSL_CURVE_SECP256R1|PROTOSSL_CURVE_SECP384R1|PROTOSSL_CURVE_X25519|PROTOSSL_CURVE_X448) +//! default curves +#define PROTOSSL_CURVE_DEFAULT (PROTOSSL_CURVE_ALL) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! identity fields for X509 issuer/subject +typedef struct ProtoSSLCertIdentT +{ + char strCountry[32]; + char strState[32]; + char strCity[32]; + char strOrg[32]; + char strUnit[256]; + char strCommon[64]; +} ProtoSSLCertIdentT; + +//! alert info, returned by ProtoSSLStat() 'alrt' selector after an SSL alert has been received +typedef struct ProtoSSLAlertDescT +{ + int32_t iAlertType; + const char *pAlertDesc; +} ProtoSSLAlertDescT; + +//! cert info, returned by ProtoSSLStat() 'cert' selector after certificate failure +typedef struct ProtoSSLCertInfoT +{ + ProtoSSLCertIdentT Ident; + int32_t iKeyModSize; +} ProtoSSLCertInfoT; + +//! state for pkcs1 operations +typedef struct ProtoSSLPkcs1T +{ + CryptRSAT RSAContext; //!< context used for rsa operations +} ProtoSSLPkcs1T; + +// opaque module state ref +typedef struct ProtoSSLRefT ProtoSSLRefT; + +// forward declaration of sockaddr +struct sockaddr; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// protossl startup +DIRTYCODE_API int32_t ProtoSSLStartup(void); + +// protossl shutdown +DIRTYCODE_API void ProtoSSLShutdown(void); + +// allocate an SSL connection and prepare for use +DIRTYCODE_API ProtoSSLRefT *ProtoSSLCreate(void); + +// reset connection back to base state. +DIRTYCODE_API void ProtoSSLReset(ProtoSSLRefT *pState); + +// destroy the module and release its state +DIRTYCODE_API void ProtoSSLDestroy(ProtoSSLRefT *pState); + +// give time to module to do its thing (should be called periodically to allow module to perform work) +DIRTYCODE_API void ProtoSSLUpdate(ProtoSSLRefT *pState); + +// Accept an incoming connection. +DIRTYCODE_API ProtoSSLRefT* ProtoSSLAccept(ProtoSSLRefT *pState, int32_t iSecure, struct sockaddr *pAddr, int32_t *pAddrlen); + +// Create a socket bound to the given address. +DIRTYCODE_API int32_t ProtoSSLBind(ProtoSSLRefT *pState, const struct sockaddr *pAddr, int32_t pAddrlen); + +// make a secure connection to a server. +DIRTYCODE_API int32_t ProtoSSLConnect(ProtoSSLRefT *pState, int32_t iSecure, const char *pAddr, uint32_t uAddr, int32_t iPort); + +// disconnect from the server. +DIRTYCODE_API int32_t ProtoSSLDisconnect(ProtoSSLRefT *pState); + +// Start listening for an incoming connection. +DIRTYCODE_API int32_t ProtoSSLListen(ProtoSSLRefT *pState, int32_t iBacklog); + +// send secure data to the server. +DIRTYCODE_API int32_t ProtoSSLSend(ProtoSSLRefT *pState, const char *pBuffer, int32_t iLength); + +// receive secure data from the server. +DIRTYCODE_API int32_t ProtoSSLRecv(ProtoSSLRefT *pState, char *pBuffer, int32_t iLength); + +// return the current module status (according to selector) +DIRTYCODE_API int32_t ProtoSSLStat(ProtoSSLRefT *pState, int32_t iSelect, void *pBuffer, int32_t iLength); + +// control module behavior +DIRTYCODE_API int32_t ProtoSSLControl(ProtoSSLRefT *pState, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue); + +// add an X.509 CA certificate that will be recognized in future transactions +DIRTYCODE_API int32_t ProtoSSLSetCACert(const uint8_t *pCACert, int32_t iCertSize); + +// same as ProtoSSLSetCACert(), but certs are not validated at load time +DIRTYCODE_API int32_t ProtoSSLSetCACert2(const uint8_t *pCACert, int32_t iCertSize); + +// validate all CAs that have not already been validated +DIRTYCODE_API int32_t ProtoSSLValidateAllCA(void); + +// clear all CA certs +DIRTYCODE_API void ProtoSSLClrCACerts(void); + +// generate a pkcs1.5 rsa signature - init +DIRTYCODE_API int32_t ProtoSSLPkcs1GenerateInit(ProtoSSLPkcs1T *pPkcs1, const uint8_t *pHashData, int32_t iHashLen, int32_t iHashType, int32_t iModSize, const CryptBinaryObjT *pPrimeP, const CryptBinaryObjT *pPrimeQ, const CryptBinaryObjT *pExponentP, const CryptBinaryObjT *pExponentQ, const CryptBinaryObjT *pCoefficient); + +// generate a pkcs1.5 rsa signature +DIRTYCODE_API int32_t ProtoSSLPkcs1GenerateUpdate(ProtoSSLPkcs1T *pPkcs1, int32_t iNumIterations, uint8_t *pSigData, int32_t iSigSize); + +// verify the pkcs1.5 rsa signature +DIRTYCODE_API int32_t ProtoSSLPkcs1Verify(const uint8_t *pSignature, int32_t iSigLen, const uint8_t *pHashData, int32_t iHashLen, int32_t iHashType, const uint8_t *pMod, int32_t iModSize, const uint8_t *pExp, int32_t iExpSize); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protossl_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protostream.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protostream.h new file mode 100644 index 00000000..bd2edb71 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protostream.h @@ -0,0 +1,107 @@ +/*H********************************************************************************/ +/*! + \File protostream.h + + \Description + Manage streaming of an Internet media resource. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 11/16/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _protostream_h +#define _protostream_h + +/*! +\Moduledef ProtoStream ProtoStream +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/proto/protohttp.h" + +/*** Defines **********************************************************************/ + +/* when a stream is opened, the repeat frequency is specified in seconds. these + constants handle special cases of "one time play" (once) and "immediate + restart" (immed). */ +#define PROTOSTREAM_FREQ_ONCE (-1) //!< one time play +#define PROTOSTREAM_FREQ_IMMED (0) //!< immediate restart + +//! callback status indicating state of the stream +typedef enum ProtoStreamStatusE +{ + PROTOSTREAM_STATUS_BEGIN = 0, //!< first buffer of data (start of stream) + PROTOSTREAM_STATUS_DATA, //!< data has been received + PROTOSTREAM_STATUS_SYNC, //!< data was dropped from the stream + PROTOSTREAM_STATUS_DONE //!< end of stream (no data) +} ProtoStreamStatusE; + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque module state +typedef struct ProtoStreamRefT ProtoStreamRefT; + +//! data callback definition +typedef int32_t (ProtoStreamCallbackT)(ProtoStreamRefT *pProtoStream, ProtoStreamStatusE eStatus, const uint8_t *pBuffer, int32_t iBufSize, void *pUserData); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create the module +DIRTYCODE_API ProtoStreamRefT *ProtoStreamCreate(int32_t iBufSize); + +// destroy the module +DIRTYCODE_API void ProtoStreamDestroy(ProtoStreamRefT *pProtoStream); + +// set recurring callback +DIRTYCODE_API void ProtoStreamSetCallback(ProtoStreamRefT *pProtoStream, int32_t iRate, ProtoStreamCallbackT *pCallback, void *pUserData); + +// set http custom header send/recv callbacks +DIRTYCODE_API void ProtoStreamSetHttpCallback(ProtoStreamRefT *pProtoStream, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData); + +// begin streaming an internet media resource +DIRTYCODE_API int32_t ProtoStreamOpen(ProtoStreamRefT *pProtoStream, const char *pUrl, int32_t iFreq); + +// begin streaming an internet media resource, with a request body (using POST) +DIRTYCODE_API int32_t ProtoStreamOpen2(ProtoStreamRefT *pProtoStream, const char *pUrl, const char *pReq, int32_t iFreq); + +// read at least iMinLen bytes from stream into user buffer +DIRTYCODE_API int32_t ProtoStreamRead(ProtoStreamRefT *pProtoStream, char *pBuffer, int32_t iBufLen, int32_t iMinLen); + +// pause stream +DIRTYCODE_API void ProtoStreamPause(ProtoStreamRefT *pProtoStream, uint8_t bPause); + +// stop streaming +DIRTYCODE_API void ProtoStreamClose(ProtoStreamRefT *pProtoStream); + +// get module status +DIRTYCODE_API int32_t ProtoStreamStatus(ProtoStreamRefT *pProtoStream, int32_t iStatus, void *pBuffer, int32_t iBufSize); + +// set control options +DIRTYCODE_API int32_t ProtoStreamControl(ProtoStreamRefT *pProtoStream, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// update module +DIRTYCODE_API void ProtoStreamUpdate(ProtoStreamRefT *pProtoStream); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protostream_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/prototunnel.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/prototunnel.h new file mode 100644 index 00000000..8dbf867d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/prototunnel.h @@ -0,0 +1,148 @@ +/*H********************************************************************************/ +/*! + \File prototunnel.h + + \Description + ProtoTunnel creates and manages a Virtual DirtySock Tunnel (VDST) + connection. The tunnel transparently bundles data sent from multiple ports + to a specific remote host into a single send, and optionally encrypts + portions of that packet. Only data sent over a UDP socket may be + tunneled. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 12/02/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _prototunnel_h +#define _prototunnel_h + +/*! +\Moduledef ProtoTunnel ProtoTunnel +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +// prototunnel protocol versions (maj.min) +#define PROTOTUNNEL_VERSION_1_1 (0x0101) +#define PROTOTUNNEL_VERSION (PROTOTUNNEL_VERSION_1_1) //!< default protocol version +#define PROTOTUNNEL_VERSION_MIN (PROTOTUNNEL_VERSION_1_1) //!< minimum protocol version +#define PROTOTUNNEL_VERSION_MAX (PROTOTUNNEL_VERSION_1_1) //!< maximum protocol version + +//! maximum number of ports that can be mapped to a tunnel +#define PROTOTUNNEL_MAXPORTS (8) + +//! port flag indicating data is encrypted +#define PROTOTUNNEL_PORTFLAG_ENCRYPTED (1) +#define PROTOTUNNEL_PORTFLAG_AUTOFLUSH (2) + +//! max key length +#define PROTOTUNNEL_MAXKEYLEN (128) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! tunnel event types +typedef enum ProtoTunnelEventE +{ + PROTOTUNNEL_EVENT_RECVNOMATCH, //!< data received from source with no tunnel mapping + + PROTOTUNNEL_NUMEVENTS //!< total number of events +} ProtoTunnelEventE; + +//! map description +typedef struct ProtoTunnelInfoT +{ + uint32_t uRemoteClientId; //!< unique client ID of remote client + uint32_t uRemoteAddr; //!< remote tunnel address + uint16_t uRemotePort; //!< remote tunnel port (if zero, use local port) + uint16_t aRemotePortList[PROTOTUNNEL_MAXPORTS]; //!< port(s) to map + uint8_t aPortFlags[PROTOTUNNEL_MAXPORTS]; //!< port flags + uint16_t uTunnelVers; //!< tunnel protocol version + uint8_t _pad[2]; +} ProtoTunnelInfoT; + +//! tunnel statistics +typedef struct ProtoTunnelStatT +{ + uint32_t uLastPacketTime; //!< last time packet was snet or received + uint32_t uUpdateTime; //!< time when ProtoTunnelStatT was updated + uint32_t uPrevUpdateTime; //!< last time ProtoTunnelStatT was updated (can be used to keep track of update interval) + uint32_t uEfficiency; //!< send/receive efficiency for the last update interval + uint32_t uRawBytesPerSecond; //!< raw byte per second for the last update interval + uint32_t uBytePerSecond; //!< data bytes (user data) per second for the last update interval + uint32_t uNumBytes; //!< total number of bytes + uint32_t uNumSubpacketBytes; //!< total number of subpacket bytes (user data) + uint16_t uNumPackets; //!< total number of packets + uint16_t uNumSubpackets; //!< total number of subpackets + uint16_t uNumDiscards; //!< total number of out-of-order packet discards + uint16_t _pad; +} ProtoTunnelStatT; + +//! opaque module ref +typedef struct ProtoTunnelRefT ProtoTunnelRefT; + +//! prototunnel event callback +typedef void (ProtoTunnelCallbackT)(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelEventE eEvent, const char *pData, int32_t iDataSize, struct sockaddr *pRecvAddr, void *pUserData); + +//! raw inbound data receive callback (shall return 1 if it swallows the data, 0 otherwise) +typedef int32_t (RawRecvCallbackT)(SocketT *pSocket, uint8_t *pData, int32_t iRecvLen, const struct sockaddr *pFrom, int32_t iFromLen, void *pUserData); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create the module +DIRTYCODE_API ProtoTunnelRefT *ProtoTunnelCreate(int32_t iMaxTunnels, int32_t iTunnelPort); + +// destroy the module +DIRTYCODE_API void ProtoTunnelDestroy(ProtoTunnelRefT *pProtoTunnel); + +// set event callback +DIRTYCODE_API void ProtoTunnelCallback(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelCallbackT *pCallback, void *pUserData); + +// allocate a tunnel +DIRTYCODE_API int32_t ProtoTunnelAlloc(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelInfoT *pInfo, const char *pKey); + +// free a tunnel +DIRTYCODE_API uint32_t ProtoTunnelFree(ProtoTunnelRefT *pProtoTunnel, uint32_t uTunnelId, const char *pKey); + +// free a tunnel +DIRTYCODE_API uint32_t ProtoTunnelFree2(ProtoTunnelRefT *pProtoTunnel, uint32_t uTunnelId, const char *pKey, uint32_t uAddr); + +// update port mapping for specified tunnel +DIRTYCODE_API int32_t ProtoTunnelUpdatePortList(ProtoTunnelRefT *pProtoTunnel, uint32_t uTunnelId, ProtoTunnelInfoT *pInfo); + +// get module status +DIRTYCODE_API int32_t ProtoTunnelStatus(ProtoTunnelRefT *pProtoTunnel, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize); + +// control the module +DIRTYCODE_API int32_t ProtoTunnelControl(ProtoTunnelRefT *pProtoTunnel, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue); + +// update the module +DIRTYCODE_API void ProtoTunnelUpdate(ProtoTunnelRefT *pProtoTunnel); + +// use this function to send raw data to a remote host from the prototunnel socket +DIRTYCODE_API int32_t ProtoTunnelRawSendto(ProtoTunnelRefT *pProtoTunnel, const char *pBuf, int32_t iLen, const struct sockaddr *pTo, int32_t iToLen); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _prototunnel_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoupnp.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoupnp.h new file mode 100644 index 00000000..cba29ae9 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protoupnp.h @@ -0,0 +1,90 @@ +/*H********************************************************************************/ +/*! + \File protoupnp.h + + \Description + Implements a simple UPnP client, designed specifically to talk to a UPnP + router and open up a firewall port for peer-peer communication with a + remote client. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/23/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _protoupnp_h +#define _protoupnp_h + +/*! +\Moduledef ProtoUpnp ProtoUpnp +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +// status bits for ProtoUpnpStatus('stat') + +#define PROTOUPNP_STATUS_DISCOVERED (1) //!< discovered a upnp device +#define PROTOUPNP_STATUS_DESCRIBED (2) //!< described a upnp device +#define PROTOUPNP_STATUS_GOTEXTADDR (4) //!< got external address for device +#define PROTOUPNP_STATUS_ADDPORTMAP (8) //!< successfully added port mapping +#define PROTOUPNP_STATUS_DELPORTMAP (16) //!< successfully deleted port mapping +#define PROTOUPNP_STATUS_FNDPORTMAP (32) //!< found existing port mapping + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! protoupnp macro element definition +typedef struct ProtoUpnpMacroT +{ + int32_t iControl; + int32_t iValue; + int32_t iValue2; + void *pValue; +} ProtoUpnpMacroT; + +//! opaque module ref +typedef struct ProtoUpnpRefT ProtoUpnpRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create the module +DIRTYCODE_API ProtoUpnpRefT *ProtoUpnpCreate(void); + +// get module ref +DIRTYCODE_API ProtoUpnpRefT *ProtoUpnpGetRef(void); + +// destroy the module +DIRTYCODE_API void ProtoUpnpDestroy(ProtoUpnpRefT *pProtoUpnp); + +// get module status +DIRTYCODE_API int32_t ProtoUpnpStatus(ProtoUpnpRefT *pProtoUpnp, int32_t iSelect, void *pBuf, int32_t iBufSize); + +// protoupnp control +DIRTYCODE_API int32_t ProtoUpnpControl(ProtoUpnpRefT *pProtoUpnp, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue); + +// update the module +DIRTYCODE_API void ProtoUpnpUpdate(ProtoUpnpRefT *pProtoUpnp); + +#ifdef __cplusplus +}; +#endif + +//@} + +#endif // _protoupnp_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protowebsocket.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protowebsocket.h new file mode 100644 index 00000000..86a18543 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/proto/protowebsocket.h @@ -0,0 +1,122 @@ +/*H********************************************************************************/ +/*! + \File protowebsocket.h + + \Description + This module implements a WebSocket client as documented in RFC 6455 + (http://tools.ietf.org/html/rfc6455). Note that implementation details + may vary when a platform-specific implementation is required. The API + as currently implemented offers stream-like operations; message-based + operations are currently not supported. + + \Todo + - Message-based APIs + + \Copyright + Copyright (c) 2012 Electronic Arts Inc. + + \Version 11/26/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _protowebsocket_h +#define _protowebsocket_h + +/*! +\Moduledef ProtoWebSocket ProtoWebSocket +\Modulemember Proto +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +// close reasons +#define PROTOWEBSOCKET_CLOSEREASON_BASE (1000) +#define PROTOWEBSOCKET_CLOSEREASON_NORMAL (PROTOWEBSOCKET_CLOSEREASON_BASE+0) +#define PROTOWEBSOCKET_CLOSEREASON_GOINGAWAY (PROTOWEBSOCKET_CLOSEREASON_BASE+1) +#define PROTOWEBSOCKET_CLOSEREASON_PROTOCOLERROR (PROTOWEBSOCKET_CLOSEREASON_BASE+2) +#define PROTOWEBSOCKET_CLOSEREASON_UNSUPPORTEDDATA (PROTOWEBSOCKET_CLOSEREASON_BASE+3) +#define PROTOWEBSOCKET_CLOSEREASON_NOSTATUSRECVD (PROTOWEBSOCKET_CLOSEREASON_BASE+5) +#define PROTOWEBSOCKET_CLOSEREASON_ABNORMALCLOSURE (PROTOWEBSOCKET_CLOSEREASON_BASE+6) +#define PROTOWEBSOCKET_CLOSEREASON_INVALIDFRAMEDATA (PROTOWEBSOCKET_CLOSEREASON_BASE+7) +#define PROTOWEBSOCKET_CLOSEREASON_POLICYVIOLATION (PROTOWEBSOCKET_CLOSEREASON_BASE+8) +#define PROTOWEBSOCKET_CLOSEREASON_MESSAGETOOBIG (PROTOWEBSOCKET_CLOSEREASON_BASE+9) +#define PROTOWEBSOCKET_CLOSEREASON_MANDATORYEXT (PROTOWEBSOCKET_CLOSEREASON_BASE+10) +#define PROTOWEBSOCKET_CLOSEREASON_INTERNALERR (PROTOWEBSOCKET_CLOSEREASON_BASE+11) +#define PROTOWEBSOCKET_CLOSEREASON_SERVICERESTART (PROTOWEBSOCKET_CLOSEREASON_BASE+12) +#define PROTOWEBSOCKET_CLOSEREASON_TRYAGAINLATER (PROTOWEBSOCKET_CLOSEREASON_BASE+13) +#define PROTOWEBSOCKET_CLOSEREASON_TLSHANDSHAKE (PROTOWEBSOCKET_CLOSEREASON_BASE+15) +#define PROTOWEBSOCKET_CLOSEREASON_MAX (PROTOWEBSOCKET_CLOSEREASON_TLSHANDSHAKE) +#define PROTOWEBSOCKET_CLOSEREASON_COUNT (PROTOWEBSOCKET_CLOSEREASON_MAX-PROTOWEBSOCKET_CLOSEREASON_BASE+1) +// unidentified close reason +#define PROTOWEBSOCKET_CLOSEREASON_UNKNOWN (PROTOWEBSOCKET_CLOSEREASON_BASE+100) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque module state definition +typedef struct ProtoWebSocketRefT ProtoWebSocketRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// allocate a websocket connection and prepare for use +DIRTYCODE_API ProtoWebSocketRefT *ProtoWebSocketCreate(int32_t iBufSize); + +// allocate a websocket connection and prepare for use (protossl function signature compatible) +DIRTYCODE_API ProtoWebSocketRefT *ProtoWebSocketCreate2(void); + +// destroy a connection and release state +DIRTYCODE_API void ProtoWebSocketDestroy(ProtoWebSocketRefT *pWebSocket); + +// initiate a connection to a server +DIRTYCODE_API int32_t ProtoWebSocketConnect(ProtoWebSocketRefT *pWebSocket, const char *pUrl); + +// disconnect from the server +DIRTYCODE_API int32_t ProtoWebSocketDisconnect(ProtoWebSocketRefT *pWebSocket); + +// send data to a server over a websocket connection +DIRTYCODE_API int32_t ProtoWebSocketSend(ProtoWebSocketRefT *pWebSocket, const char *pBuffer, int32_t iLength); + +// send text data to a server over a websocket connection +DIRTYCODE_API int32_t ProtoWebSocketSendText(ProtoWebSocketRefT *pWebSocket, const char *pBuffer); + +// send a message to a server over a websocket connection +DIRTYCODE_API int32_t ProtoWebSocketSendMessage(ProtoWebSocketRefT *pWebSocket, const char *pBuffer, int32_t iLength); + +// send a text message to a server over a websocket connection +DIRTYCODE_API int32_t ProtoWebSocketSendMessageText(ProtoWebSocketRefT *pWebSocket, const char *pBuffer); + +// recv data from a server over a websocket connection +DIRTYCODE_API int32_t ProtoWebSocketRecv(ProtoWebSocketRefT *pWebSocket, char *pBuffer, int32_t iLength); + +// recv a message from a server over a websocket connection +DIRTYCODE_API int32_t ProtoWebSocketRecvMessage(ProtoWebSocketRefT *pWebSocket, char *pBuffer, int32_t iLength); + +// get module status +DIRTYCODE_API int32_t ProtoWebSocketStatus(ProtoWebSocketRefT *pWebSocket, int32_t iSelect, void *pBuffer, int32_t iBufSize); + +// control module behavior +DIRTYCODE_API int32_t ProtoWebSocketControl(ProtoWebSocketRefT *pWebSocket, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue); + +// give time to module to do its thing (should be called periodically to allow module to perform work) +DIRTYCODE_API void ProtoWebSocketUpdate(ProtoWebSocketRefT *pWebSocket); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _protowebsocket_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/aws.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/aws.h new file mode 100644 index 00000000..74defc6a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/aws.h @@ -0,0 +1,70 @@ +/*H********************************************************************************/ +/*! + \File aws.h + + \Description + Implements AWS utility functions, including SigV4 signing and signed binary + event reading and writing. + + \Copyright + Copyright 2018 Electronic Arts + + \Version 12/26/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _aws_h +#define _aws_h + +/*! +\Moduledef AWS AWS +\Modulemember Util +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! AWS signing info +typedef struct AWSSignInfoT +{ + char strRegion[32]; //!< region request is being made in + char strService[32]; //!< name of service for request + char strKeyPath[64]; //!< keypath for request + char strSignature[65]; //!< latest signature, hex encoded + char strKey[64]; //!< secret key used to sign request +} AWSSignInfoT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// sign given header with AWS Signature Version 4 signing process +DIRTYCODE_API int32_t AWSSignSigV4(char *pHeader, int32_t iHeaderSize, const char *pRequest, const char *pKeyInfo, const char *pService, AWSSignInfoT *pSignInfo); + +// write a signed binary event +DIRTYCODE_API int32_t AWSWriteEvent(uint8_t *pBuffer, int32_t iBufSize, const uint8_t *pData, int32_t *pDataSize, const char *pEvent, AWSSignInfoT *pSignInfo); + +// read a signed binary event +DIRTYCODE_API int32_t AWSReadEvent(const uint8_t *pBuffer, int32_t iBufLen, char *pEventType, int32_t iEventSize, char *pMessage, int32_t *pMessageSize); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _aws_h + + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/base64.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/base64.h new file mode 100644 index 00000000..febd280b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/base64.h @@ -0,0 +1,88 @@ +/*H*******************************************************************/ +/*! + \File base64.h + + \Description + This module Base-64 encoding/decoding as defined in RFC + 989, 1040, 1113, 1421, 1521 and 2045. + + \Copyright + Copyright (c) Electronic Arts 2003. ALL RIGHTS RESERVED. + + \Version 1.0 12/11/2003 (SJB) First Version +*/ +/*******************************************************************H*/ + +#ifndef _base64_h +#define _base64_h + +/*! +\Moduledef Base64 Base64 +\Modulemember Util +*/ +//@{ + +/*** Include files ***************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines *********************************************************/ + +/*** Macros **********************************************************/ + +/*! The number of bytes it takes to Base-64 encode the given number + of bytes. The result includes any required padding but not a '\0' + terminator. */ +#define Base64EncodedSize(x) ((((x)+2)/3)*4) + +/*! The maximum number of bytes it takes to hold the decoded version + of a Base-64 encoded string that is 'x' bytes long. + + In this version of Base-64, 'x' is always a multiple of 4 and the + result is always a multiple of 3 (i.e. there may be up to 2 padding + bytes at the end of the decoded value). It is assumed that the + exact length of the string (minus any padding) is either encoded in + the string or is external to the string. */ +#define Base64DecodedSize(x) (((x)/4)*3) + +/*** Type Definitions ************************************************/ + +/*** Variables *******************************************************/ + +/*** Functions *******************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Base64 encode a string +DIRTYCODE_API void Base64Encode(int32_t iInputLen, const char *pInput, char *pOutput); + +// Base64 encode a string, buffer-safe version +DIRTYCODE_API int32_t Base64Encode2(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen); + +// decode a Base64-encoded string +DIRTYCODE_API int32_t Base64Decode(int32_t iInputLen, const char *pInput, char *pOutput); + +// decode a Base64-encoded string, return decoded size +DIRTYCODE_API int32_t Base64Decode2(int32_t iInputLen, const char *pInput, char *pOutput); + +// decode a Base64-encoded string, return decoded size, buffer-safe version +DIRTYCODE_API int32_t Base64Decode3(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen); + +// Base64 encode a url string +DIRTYCODE_API int32_t Base64EncodeUrl(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen); + +// Base64 encode a url string with more options +DIRTYCODE_API int32_t Base64EncodeUrl2(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen, uint8_t bPadded); + +// decode a Base64-encode url string +DIRTYCODE_API int32_t Base64DecodeUrl(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/binary7.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/binary7.h new file mode 100644 index 00000000..288708ae --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/binary7.h @@ -0,0 +1,55 @@ +/*H*************************************************************************************/ +/*! + \File binary7.h + + \Description + This module provides routines to encode/decode binary7 data to/from a buffer. + + \Copyright + Copyright (c) Electronic Arts 2009. ALL RIGHTS RESERVED. + + \Version 1.0 11/02/2009 (cadam) First version +*/ +/*************************************************************************************H*/ + +#ifndef _binary7_h +#define _binary7_h + +/*! +\Moduledef Binary7 Binary7 +\Modulemember Util +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// set a binary field using more efficient encoding +DIRTYCODE_API char *Binary7Encode(unsigned char *pDst, int32_t iDstLen, unsigned const char *pSrc, int32_t iSrcLen, uint32_t bTerminate); + +// get binary field contents +DIRTYCODE_API unsigned const char *Binary7Decode(unsigned char *pDst, int32_t iDstLen, unsigned const char *pSrc); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _binary7_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/hpack.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/hpack.h new file mode 100644 index 00000000..c59fb602 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/hpack.h @@ -0,0 +1,63 @@ +/*H********************************************************************************/ +/*! + \File hpack.h + + \Description + This module implements a decode/encoder based on the HPACK spec + (https://tools.ietf.org/html/rfc7541). Which is used for encoding/decoding + the HEADERS frame in the HTTP/2 protocol. + + \Copyright + Copyright (c) Electronic Arts 2016. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +#ifndef _hpack_h +#define _hpack_h + +/*! +\Moduledef Hpack Hpack +\Modulemember Util +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Type Definitions *************************************************************/ + +//! opaque module ref +typedef struct HpackRefT HpackRefT; + +/*** Functions ********************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// create the module +DIRTYCODE_API HpackRefT *HpackCreate(uint32_t uTableMax, uint8_t bDecoder); + +// destroy the module +DIRTYCODE_API void HpackDestroy(HpackRefT *pRef); + +// unpack the header +DIRTYCODE_API int32_t HpackDecode(HpackRefT *pRef, const uint8_t *pInput, int32_t iInpSize, char *pOutput, int32_t iOutSize); + +// encode the header +DIRTYCODE_API int32_t HpackEncode(HpackRefT *pRef, const char *pInput, uint8_t *pOutput, int32_t iOutSize, uint8_t bHuffman); + +// clear the dynamic table +DIRTYCODE_API void HpackClear(HpackRefT *pRef); + +// resize the dynamic table +DIRTYCODE_API void HpackResize(HpackRefT *pRef, uint32_t uTableSize, uint8_t bSendUpdate); + +#if defined(__cplusplus) +} +#endif + +//@} + +#endif // _hpack_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/jsonformat.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/jsonformat.h new file mode 100644 index 00000000..5b2ae4c7 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/jsonformat.h @@ -0,0 +1,98 @@ +/*H*************************************************************************************/ +/*! + \File jsonformat.h + + \Description + This module formats simple Json, in a linear fashion, using a character buffer + that the client provides. + + \Notes + References: + JSON RFC: http://tools.ietf.org/html/rfc4627 + + \Copyright + Copyright (c) Electronic Arts 2012. + + \Version 12/11/2012 (jbrookes) First Version +*/ +/*************************************************************************************H*/ + +#ifndef _jsonformat_h +#define _jsonformat_h + +/*! +\Moduledef JsonFormat JsonFormat +\Modulemember Util +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +#define JSON_ERR_NONE (0) //!< no error +#define JSON_ERR_FULL (-1) //!< buffer is full, no space to add attribute or element. +#define JSON_ERR_UNINIT (-2) //!< did not call JsonInit() prior to writing. +#define JSON_ERR_NOT_OPEN (-3) //!< attempt to set elem or attrib, but, no tag opened. +#define JSON_ERR_ATTR_POSITION (-4) //!< attempt to set an attrib, but, child element already added to tag. +#define JSON_ERR_INVALID_PARAM (-5) //!< invalid parameter passed to function + +// Encoding control flags +#define JSON_FL_WHITESPACE (1) //!< include formatting whitespace + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init the API -- pass in the character buffer. MUST be called first. +DIRTYCODE_API void JsonInit(char *pBuffer, int32_t iBufLen, uint8_t uFlags); + +// notify the API that the buf size was increased +DIRTYCODE_API void JsonBufSizeIncrease(char *pBuffer, int32_t iNewBufLen); + +// finish JSON output to this buffer; use returned pointer ref +DIRTYCODE_API char *JsonFinish(char *pBuffer); + +// start an object +DIRTYCODE_API int32_t JsonObjectStart(char *pBuffer, const char *pName); + +// end the current object -- must have an outstanding open object +DIRTYCODE_API int32_t JsonObjectEnd(char *pBuffer); + +// start an array +DIRTYCODE_API int32_t JsonArrayStart(char *pBuffer, const char *pName); + +// end the current array -- must have an outstanding open array +DIRTYCODE_API int32_t JsonArrayEnd(char *pBuffer); + +// add a string element +DIRTYCODE_API int32_t JsonAddStr(char *pBuffer, const char *pElemName, const char *pValue); + +// add a number +DIRTYCODE_API int32_t JsonAddNum(char *pBuffer, const char *pElemName, const char *pFormatSpec, float fValue); + +// add an integer element +DIRTYCODE_API int32_t JsonAddInt(char *pBuffer, const char *pElemName, int64_t iValue); + +// add a date (will be encoded as a string; use JsonAddInteger for integer encoding) +DIRTYCODE_API int32_t JsonAddDate(char *pBuffer, const char *pElemName, uint32_t uEpochDate); + +// build a JSON output string from a formatted pattern (like vprintf) $$TODO +#ifdef va_start +DIRTYCODE_API char *JsonFormatVPrintf(char *pJsonBuff, int32_t iBufLen, const char *pFormat, va_list pFmtArgs); +#endif + +// build a JSON output string from a formatted pattern (like printf) $$TODO +DIRTYCODE_API char *JsonFormatPrintf(char *pJsonBuff, int32_t iBufLen, const char *pFormat, ...); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _jsonformat_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/jsonparse.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/jsonparse.h new file mode 100644 index 00000000..bf6211b3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/jsonparse.h @@ -0,0 +1,98 @@ +/*H*************************************************************************************/ +/*! + \File jsonparse.h + + \Description + A simple JSON parser. + + \Notes + Written by Greg Schaefer outside of EA for a personal project, but donated + back for a good cause. + + \Copyright + Copyright (c) Electronic Arts 2012. + + \Version 12/11/2012 (jbrookes) Added to DirtySDK, added some new functionality +*/ +/*************************************************************************************H*/ + +#ifndef _jsonparse_h +#define _jsonparse_h + +/*! +\Moduledef JsonParse JsonParse +\Modulemember Util +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +//! alternate name for JsonSeekObjectEnd (DEPRECATED) +#define JsonGetListItemEnd(__pObject) JsonSeekObjectEnd(__pObject) + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// parse json, preparing lookup for fast dereference +DIRTYCODE_API int32_t JsonParse(uint16_t *pDst, int32_t iMax, const char *pSrc, int32_t iLen); + +// parse json, allocate required memory for parse buffer internally +DIRTYCODE_API uint16_t *JsonParse2(const char *pSrc, int32_t iLen, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData); + +// locate a json element +DIRTYCODE_API const char *JsonFind(const uint16_t *pParse, const char *pName); + +// locate a json element, starting from an offset, with an optional array index +DIRTYCODE_API const char *JsonFind2(const uint16_t *pParse, const char *pJson, const char *pName, int32_t iIndex); + +// get a string element +DIRTYCODE_API int32_t JsonGetString(const char *pJson, char *pBuffer, int32_t iLength, const char *pDefault); + +// get a string element, with accumulating error +DIRTYCODE_API int32_t JsonGetString2(const char *pJson, char *pBuffer, int32_t iLength, const char *pDefault, uint8_t *pError); + +// get an integer element +DIRTYCODE_API int64_t JsonGetInteger(const char *pJson, int64_t iDefault); + +// get an integer element, with range validation and accumulating error +DIRTYCODE_API int64_t JsonGetInteger2(const char *pJson, int64_t iDefault, int64_t iMin, int64_t iMax, uint8_t *pError); + +// get a date in ISO_8601 format +DIRTYCODE_API uint32_t JsonGetDate(const char *pJson, uint32_t uDefault); + +// get a date in ISO_8601 format, with accumulating error +DIRTYCODE_API uint32_t JsonGetDate2(const char *pJson, uint32_t uDefault, uint8_t *pError); + +// get a boolean element +DIRTYCODE_API uint8_t JsonGetBoolean(const char *pJson, uint8_t bDefault); + +// get a boolean element, with accumulating error +DIRTYCODE_API uint8_t JsonGetBoolean2(const char *pJson, uint8_t bDefault, uint8_t *pError); + +// get an enum element +DIRTYCODE_API int32_t JsonGetEnum(const char *pJson, const char *pEnumArray[], int32_t iDefault); + +// get an enum element, with accumulating error +DIRTYCODE_API int32_t JsonGetEnum2(const char *pJson, const char *pEnumArray[], int32_t iDefault, uint8_t *pError); + +// seek to the end of an object within the JSON text buffer +DIRTYCODE_API const char *JsonSeekObjectEnd(const char *pObject); + +// seek to the start of the value of a key / value pair within the JSON text buffer +DIRTYCODE_API const char *JsonSeekValue(const char *pKey); + + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _jsonparse_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/murmurhash3.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/murmurhash3.h new file mode 100644 index 00000000..29e32577 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/murmurhash3.h @@ -0,0 +1,79 @@ +/*H********************************************************************************/ +/*! + \File murmurhash3.h + + \Description + An implementation of MurmurHash3, based heavily on the x64 128bit output + implementation. MurmurHash3 is a public domain hashing algorithm written + by Austin Appleby, ref http://code.google.com/p/smhasher/wiki/MurmurHash3. + + MurmurHash3 is not cryptographically secure, however it has excellent + collision resistance and is orders of magnitude faster than the fastest + secure cryptographic hash. + + \Copyright + Copyright (c) 2014 Electronic Arts + + \Version 03/07/2014 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _murmurhash3_h +#define _murmurhash3_h + +/*! +\Moduledef MurmurHash3 MurmurHash3 +\Modulemember Util +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +#define MURMURHASH_HASHSIZE (16) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct MurmurHash3T +{ + uint64_t aState[2]; //!< hash state + int32_t iCount; //!< total byte count + uint8_t aData[16]; //!< partial data block +} MurmurHash3T; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// init the MurmurHash3 context +DIRTYCODE_API void MurmurHash3Init(MurmurHash3T *pContext); + +// init the MurmurHash3 context, with hash size +DIRTYCODE_API void MurmurHash3Init2(MurmurHash3T *pContext, int32_t iHashSize); + +// add data to the MurmurHash3 context (hash the data) +DIRTYCODE_API void MurmurHash3Update(MurmurHash3T *pContext, const void *pBuffer, int32_t iLength); + +// convert MurmurHash3 state into final output form +DIRTYCODE_API void MurmurHash3Final(MurmurHash3T *pContext, void *pBuffer, int32_t iLength); + +// hash the data, all-in-one version +DIRTYCODE_API void MurmurHash3(void *_pOutput, int32_t iOutLen, const void *_pInput, int32_t iInpLen, const void *pKey, int32_t iKeyLen); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _murmurhash3_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufcommon.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufcommon.h new file mode 100644 index 00000000..25227469 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufcommon.h @@ -0,0 +1,59 @@ +/*H*************************************************************************************/ +/*! + \File protobufcommon.h + + \Description + Shared definitions and functionality for the protobuf encoder / decoder + + \Copyright + Copyright (c) Electronic Arts 2017-2018. ALL RIGHTS RESERVED. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +#ifndef _protobufcommon_h +#define _protobufcommon_h + +/*! +\Moduledef ProtobufCommon ProtobufCommon +\Modulemember Util +*/ +//@{ + +/*** Include Files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Type Definitions ******************************************************************/ + +//! different field types that are supported by protobuf. note, the order is needed to follow the defined values of the protocol +typedef enum ProtobufTypeE +{ + PROTOBUF_TYPE_VARINT, //!< int32, int64, uint32, uint64, sint32, sint64, bool, enum + PROTOBUF_TYPE_64BIT, //!< fixed64 (uint64_t), sfixed64 (int64_t), double + PROTOBUF_TYPE_LENGTH_DELIMITED, //!< string, bytes, embedded messages, packed repeated fields + PROTOBUF_TYPE_START_GROUP, //!< groups (deprecated) + PROTOBUF_TYPE_END_GROUP, //!< groups (deprecated) + PROTOBUF_TYPE_32BIT //!< fixed32 (uint32_t), sfixed32 (int32_t), float +} ProtobufTypeE; + +/*** Functions *************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// read the message size out of the payload +DIRTYCODE_API const uint8_t *ProtobufCommonReadSize(const uint8_t *pBuffer, int32_t iBufLen, int32_t *pResult); + +// get the string representation of a type +DIRTYCODE_API const char *ProtobufCommonGetType(ProtobufTypeE eType); + +#if defined(__cplusplus) +} +#endif + +//@} + +#endif // _protobufcommon_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufread.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufread.h new file mode 100644 index 00000000..0f8e0700 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufread.h @@ -0,0 +1,113 @@ +/*H*************************************************************************************/ +/*! + \File protobufread.h + + \Description + Interface of decoder for the Google Protobuf wire format + See: https://developers.google.com/protocol-buffers/docs/encoding + + This module only supports protobuf version 3, if any lesser versions are used + the result is undefined. + + \Copyright + Copyright (c) Electronic Arts 2017-2018. ALL RIGHTS RESERVED. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +#ifndef _protobufread_h +#define _protobufread_h + +/*! +\Moduledef ProtobufRead ProtobufRead +\Modulemember Util +*/ +//@{ + +/*** Includes **************************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Type Definitions ******************************************************************/ + +//! struct for ease of use for the API +typedef struct ProtobufReadT +{ + const uint8_t *pBuffer; //!< pointer to the start of the buffer + const uint8_t *pBufEnd; //!< pointer to the end of the buffer +} ProtobufReadT; + +/*** Functions *************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// initialize the state +DIRTYCODE_API void ProtobufReadInit(ProtobufReadT *pState, const uint8_t *pBuffer, int32_t iBufLen); + +// find a field by identifier +DIRTYCODE_API const uint8_t *ProtobufReadFind(const ProtobufReadT *pState, uint32_t uField); + +// find a field by identifier that allows continuation for non-packed repeated fields +DIRTYCODE_API const uint8_t *ProtobufReadFind2(const ProtobufReadT *pState, uint32_t uField, const uint8_t *pBuffer); + +// read the current field header +DIRTYCODE_API const uint8_t *ProtobufReadHeader(const ProtobufReadT *pState, const uint8_t *pCurrent, uint32_t *pField, uint8_t *pType); + +// skip over a field +DIRTYCODE_API const uint8_t *ProtobufReadSkip(const ProtobufReadT *pState, const uint8_t *pCurrent, uint8_t uType); + +// read the varint int32, int64, uint32, uint64, bool or enum from the buffer +DIRTYCODE_API uint64_t ProtobufReadVarint(const ProtobufReadT *pState, const uint8_t *pCurrent); + +// alternative version used in conjunction with repeated field reading +DIRTYCODE_API const uint8_t *ProtobufReadVarint2(const ProtobufReadT *pState, const uint8_t *pCurrent, uint64_t *pResult); + +// read the repeated varint int32, int64, uint32, uint64, bool or enum from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadRepeatedVarint(const ProtobufReadT *pState, const uint8_t *pCurrent, uint64_t *pResult, int32_t iCount); + +// read the varint sint32 from the buffer (zigzag decoded) +DIRTYCODE_API const uint8_t *ProtobufReadSint32(const ProtobufReadT *pState, const uint8_t *pCurrent, int32_t *pResult); + +// read the repeated varint sint32 from the buffer (zigzag decoded) +DIRTYCODE_API const uint8_t *ProtobufReadRepeatedSint32(const ProtobufReadT *pState, const uint8_t *pCurrent, int32_t *pResult, int32_t iCount); + +// read the varint sint64 from the buffer (zigzag decoded) +DIRTYCODE_API const uint8_t *ProtobufReadSint64(const ProtobufReadT *pState, const uint8_t *pCurrent, int64_t *pResult); + +// read the repeated varint sint64 from the buffer (zigzag decoded) +DIRTYCODE_API const uint8_t *ProtobufReadRepeatedSint64(const ProtobufReadT *pState, const uint8_t *pCurrent, int64_t *pResult, int32_t iCount); + +// read fixed32, float or sfixed32 from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadFixed32(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput); + +// read repeated fixed32, float or sfixed32 from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadRepeatedFixed32(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput, int32_t iOutLen); + +// read fixed64, double or sfixed64 from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadFixed64(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput); + +// read repeated fixed64, double or sfixed64 from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadRepeatedFixed64(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput, int32_t iOutLen); + +// read the bytes from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadBytes(const ProtobufReadT *pState, const uint8_t *pCurrent, uint8_t *pOutput, int32_t iOutLen); + +// read the string from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadString(const ProtobufReadT *pState, const uint8_t *pCurrent, char *pOutput, int32_t iOutLen); + +// read an embedded message information from the buffer +DIRTYCODE_API const uint8_t *ProtobufReadMessage(const ProtobufReadT *pState, const uint8_t *pCurrent, ProtobufReadT *pMsg); + +// read the number of elements in the repeated field +DIRTYCODE_API int32_t ProtobufReadNumRepeatedElements(const ProtobufReadT *pState, uint32_t uField, uint8_t uType); + +#if defined(__cplusplus) +} +#endif + +//@} + +#endif // _protobufread_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufwrite.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufwrite.h new file mode 100644 index 00000000..8b4204fc --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/protobufwrite.h @@ -0,0 +1,105 @@ +/*H*************************************************************************************/ +/*! + \File protobufwrite.h + + \Description + Interface of encoder for the Google Protobuf wire format + See: https://developers.google.com/protocol-buffers/docs/encoding + + \Copyright + Copyright (c) Electronic Arts 2017-2018. ALL RIGHTS RESERVED. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +#ifndef _protobufwrite_h +#define _protobufwrite_h + +/*! +\Moduledef ProtobufWrite ProtobufWrite +\Modulemember Util +*/ +//@{ + +/*** Includes **************************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Type Definitions ******************************************************************/ + +//! used for result for the write functions +typedef enum ProtobufWriteErrorE +{ + PROTOBUFWRITE_ERROR_OK, //!< successful operation + PROTOBUFWRITE_ERROR_FULL, //!< the buffer is full + PROTOBUFWRITE_ERROR_EMBED, //!< called begin when out of slots or calling end when begin hasn't been called +} ProtobufWriteErrorE; + +//! opaque module ref +typedef struct ProtobufWriteRefT ProtobufWriteRefT; + +/*** Functions *************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// initialize the encoder, must be called first +DIRTYCODE_API ProtobufWriteRefT *ProtobufWriteCreate(uint8_t *pBuffer, int32_t iBufLen, uint8_t bEncodeSize); + +// write the varint int32, int64, uint32, uint64, bool or enum to the buffer +DIRTYCODE_API int32_t ProtobufWriteVarint(ProtobufWriteRefT *pState, uint64_t uValue, uint32_t uField); + +// write repeated varint to the buffer +DIRTYCODE_API int32_t ProtobufWriteRepeatedVarint(ProtobufWriteRefT *pState, uint64_t *pValues, int32_t iCount, uint32_t uField); + +// write the varint sint32 to the buffer (zigzag encoded) +DIRTYCODE_API int32_t ProtobufWriteSint32(ProtobufWriteRefT *pState, int32_t iValue, uint32_t uField); + +// write repeated varint sint32 to the buffer (zigzag encoded) +DIRTYCODE_API int32_t ProtobufWriteRepeatedSint32(ProtobufWriteRefT *pState, int32_t *pValues, int32_t iCount, uint32_t uField); + +// write the varint sint64 to the buffer (zigzag encoded) +DIRTYCODE_API int32_t ProtobufWriteSint64(ProtobufWriteRefT *pState, int64_t iValue, uint32_t uField); + +// write repeated varint sint64 to the buffer (zigzag encoded) +DIRTYCODE_API int32_t ProtobufWriteRepeatedSint64(ProtobufWriteRefT *pState, int64_t *pValues, int32_t iCount, uint32_t uField); + +// write fixed32, float or sfixed32 to the buffer +DIRTYCODE_API int32_t ProtobufWriteFixed32(ProtobufWriteRefT *pState, const void *pValue, uint32_t uField); + +// write repeated fixed32, float or sfixed32 to the buffer +DIRTYCODE_API int32_t ProtobufWriteRepeatedFixed32(ProtobufWriteRefT *pState, const void *pInput, int32_t iInpLen, uint32_t uField); + +// write fixed64, double or sfixed64 to the buffer +DIRTYCODE_API int32_t ProtobufWriteFixed64(ProtobufWriteRefT *pState, const void *pValue, uint32_t uField); + +// write repeated fixed64, double or sfixed64 to the buffer +DIRTYCODE_API int32_t ProtobufWriteRepeatedFixed64(ProtobufWriteRefT *pState, const void *pInput, int32_t iInpLen, uint32_t uField); + +// write the length delimited string, bytes or embedded message/packed repeated fields (if already encoded in pValue) to the buffer +DIRTYCODE_API int32_t ProtobufWriteLengthDelimited(ProtobufWriteRefT *pState, const void *pValue, int32_t iLength, uint32_t uField); + +// alias function to write string +#define ProtobufWriteString(pState, pValue, iLength, uField) ProtobufWriteLengthDelimited(pState, pValue, iLength, uField) + +// alias function to write bytes +#define ProtobufWriteBytes(pState, pValue, iLength, uField) ProtobufWriteLengthDelimited(pState, pValue, iLength, uField) + +// begin an embedded message/packed repeated fields +DIRTYCODE_API int32_t ProtobufWriteMessageBegin(ProtobufWriteRefT *pState, uint32_t uField); + +// end an embedded message/packed repeated fields +DIRTYCODE_API int32_t ProtobufWriteMessageEnd(ProtobufWriteRefT *pState); + +// finish the writing of the message, cleanup the module state and return the size of the payload +DIRTYCODE_API int32_t ProtobufWriteDestroy(ProtobufWriteRefT *pState); + +#if defined(__cplusplus) +} +#endif + +//@} + +#endif // _protobufwrite_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/utf8.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/utf8.h new file mode 100644 index 00000000..08626745 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/util/utf8.h @@ -0,0 +1,101 @@ +/*H*************************************************************************************************/ +/*! + + \File utf8.h + + \Description + This module implements routines for converting to and from UTF-8. + + \Notes + This code only decodes the first three octets of UTF-8, thus it only handles UCS-2 codes, + not UCS-4 codes. It also does not handle UTF-16 (and surrogate pairs), and is therefore + limited to encoding to/decoding from the basic reference plane. + + Helpful references: + + http://www.utf-8.com/ - links + http://www.cis.ohio-state.edu/cgi-bin/rfc/rfc2279.html - RFC 2279 + http://www.unicode.org/charts/ - UNICODE character charts + http://www-106.ibm.com/developerworks/library/utfencodingforms/ - UNICODE primer + http://www.columbia.edu/kermit/utf8.html - UTF-8 samples + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2003. ALL RIGHTS RESERVED. + + \Version 1.0 03/25/03 (JLB) First version. + +*/ +/*************************************************************************************************H*/ + +#ifndef _utf8_h +#define _utf8_h + +/*! +\Moduledef Utf8 Utf8 +\Modulemember Util +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! UTF-8 to 8bit translation table +typedef struct Utf8TransTblT +{ + uint32_t uRangeBegin; + uint32_t uRangeEnd; + unsigned char *pCodeTbl; +} Utf8TransTblT; + +//! 8bit to UTF-8 translation table +typedef struct Utf8EncodeTblT +{ + uint16_t uCodeTbl[256]; +} Utf8EncodeTblT; + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// strip non-ASCII characters from a UTF-8 encoded string +DIRTYCODE_API int32_t Utf8Strip(char *pOutStr, int32_t iBufSize, const char *pInStr); + +// replace non-ASCII characters in a UTF-8 encoded string with 'cReplace' +DIRTYCODE_API int32_t Utf8Replace(char *pOutStr, int32_t iBufSize, const char *pInStr, char cReplace); + +// get code point length of UTF-8 encoded string +DIRTYCODE_API int32_t Utf8StrLen(const char *pStr); + +// encode a UCS-2 string to UTF-8 +DIRTYCODE_API int32_t Utf8EncodeFromUCS2(char *pOutStr, int32_t iBufLen, const uint16_t *pInStr); + +// encode a single UCS-2 "char" to UTF-8 string. +DIRTYCODE_API int32_t Utf8EncodeFromUCS2CodePt(char *pOutPtr, uint16_t uCodePt); + +// decode a UTF-8 encoded string into UCS-2 +DIRTYCODE_API int32_t Utf8DecodeToUCS2(uint16_t *pOutStr, int32_t iBufLen, const char *pInStr); + +// encode the given 8bit input string to UTF-8, based on the input translation table +DIRTYCODE_API int32_t Utf8EncodeFrom8Bit(char *pOutStr, int32_t iBufLen, const char *pInStr, const Utf8EncodeTblT *pEncodeTbl); + +// translate the given UTF-8 sequence based on the NULL-terminated array of given tables +DIRTYCODE_API int32_t Utf8TranslateTo8Bit(char *pOutStr, int32_t iBufLen, const char *pInStr, char cReplace, const Utf8TransTblT *pTransTbl); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _utf8_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voip.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voip.h new file mode 100644 index 00000000..a61a99ba --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voip.h @@ -0,0 +1,100 @@ +/*H*************************************************************************************************/ +/*! + + \File voip.h + + \Description + Main include for Voice Over IP module. + + \Notes + MLU: + Every Local User should be registered with the voip subsytem in order to get headset status info with VoipSetLocalUser(). + In order to get fully functional voip a user must be further activated which enrolls them as a participating user with VoipGroupActivateLocalUser(). + + Basically any users that are being pulled into the game should be a participating user. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002-2004. ALL RIGHTS RESERVED. + + \Version 1.0 11/19/02 (IRS) First Version + \Version 2.0 05/13/03 (GWS) Rewrite to fix misc bugs and improve network connection. + \Version 3.0 11/03/03 (JLB) Implemented unloadable support, major code cleanup and reorganization. + \Version 3.5 03/02/04 (JLB) VoIP 2.0 - API changes for future multiple channel support. + \Version 3.6 11/19/08 (mclouatre) Modified synopsis of VoipStatus() +*/ +/*************************************************************************************************H*/ + +#ifndef _voip_h +#define _voip_h + +/*! +\Moduledef VoipApi VoipApi +\Modulemember Voip +*/ +//@{ + +/*** Include files ********************************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/voip/voipdef.h" + +/*** Defines **************************************************************************************/ + +/*** Macros ***************************************************************************************/ + +/*** Type Definitions *****************************************************************************/ + +/*** Variables ************************************************************************************/ + +/*** Functions ************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// prepare voip for use +DIRTYCODE_API VoipRefT *VoipStartup(int32_t iMaxPeers, int32_t iData); + +// return pointer to current module state +DIRTYCODE_API VoipRefT *VoipGetRef(void); + +// release all voip resources +DIRTYCODE_API void VoipShutdown(VoipRefT *pVoip, uint32_t uShutdownFlags); + +// register/unregister specified local user with the voip sub-system (allows for local headset status query, but not voice acquisition/playback) +DIRTYCODE_API void VoipSetLocalUser(VoipRefT *pVoip, int32_t iLocalUserIndex, uint32_t bRegister); + +// return information about local hardware state on a per-user basis +DIRTYCODE_API int32_t VoipLocalUserStatus(VoipRefT *pVoip, int32_t iLocalUserIndex); + +// return status +DIRTYCODE_API int32_t VoipStatus(VoipRefT *pVoip, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize); //do any of the controls need user index? + +// set control options +DIRTYCODE_API int32_t VoipControl(VoipRefT *pVoip, int32_t iControl, int32_t iValue, void *pValue); //do any of the controls need user index? + +// set speaker output callback (only available on some platforms) +DIRTYCODE_API void VoipSpkrCallback(VoipRefT *pVoip, VoipSpkrCallbackT *pCallback, void *pUserData); + +// pick a channel +DIRTYCODE_API int32_t VoipSelectChannel(VoipRefT *pVoip, int32_t iUserIndex, int32_t iChannel, VoipChanModeE eMode); + +// go back to not using channels +DIRTYCODE_API void VoipResetChannels(VoipRefT *pVoip, int32_t iUserIndex); + +// config voice transcriptions (for default config set uProfile to -1) +DIRTYCODE_API void VoipConfigTranscription(VoipRefT *pVoip, uint32_t uProfile, const char *pUrl, const char *pKey); + +// config text narration (for the default config set uProvider to -1) +DIRTYCODE_API void VoipConfigNarration(VoipRefT *pVoip, uint32_t uProvider, const char *pUrl, const char *pKey); + +// set first party id callback +DIRTYCODE_API void VoipRegisterFirstPartyIdCallback(VoipRefT *pVoip, VoipFirstPartyIdCallbackCbT *pCallback, void *pUserData); +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _voip_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voipblocklist.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voipblocklist.h new file mode 100644 index 00000000..d2089e8c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voipblocklist.h @@ -0,0 +1,62 @@ + +/*H*************************************************************************************************/ +/*! + \File voipblocklist.h + + \Description + Allow blocking of voip communication based on account id. + + \Copyright + Copyright (c) 2019 Electronic Arts Inc. + + \Version 07/03/2019 (cvienneau) First Version +*/ +/*************************************************************************************************H*/ + +#ifndef _voipblocklist_h +#define _voipblocklist_h + +/*** Include files ********************************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/voip/voipdef.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ +typedef struct VoipBlockListT VoipBlockListT; + +/*** Macros ***************************************************************************************/ + +/*** Variables ************************************************************************************/ + +/*** Functions ************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// creates the VoipBlockListT (internal use only by voipcommon) +DIRTYCODE_API VoipBlockListT *VoipBlockListCreate(void); + +// destroys the VoipBlockListT (internal use only by voipcommon) +DIRTYCODE_API void VoipBlockListDestroy(VoipRefT *pVoip); + +// add a user to be blocked by the local user +DIRTYCODE_API uint8_t VoipBlockListAdd(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iMutedAccountId); + +// remove a user that was blocked by the local user +DIRTYCODE_API uint8_t VoipBlockListRemove(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iMutedAccountId); + +// check if a user is blocked by the local user +DIRTYCODE_API uint8_t VoipBlockListIsBlocked(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iMutedAccountId); + +// clear the blocked list for the local user (-1 for all users) +DIRTYCODE_API uint8_t VoipBlockListClear(VoipRefT *pVoip, int32_t iLocalUserIndex); + +#ifdef __cplusplus +} +#endif + +#endif // _voipblocklist_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voipcodec.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voipcodec.h new file mode 100644 index 00000000..148494a4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voipcodec.h @@ -0,0 +1,137 @@ +/*H********************************************************************************/ +/*! + \File voipcodec.h + + \Description + Defines an interface for Voip codec modules, and a singleton dispatcher + used to manage codec instances. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 08/04/2004 (jbrookes) First version +*/ +/********************************************************************************H*/ + +#ifndef _voipcodec_h +#define _voipcodec_h + +/*! +\Moduledef VoipCodec VoipCodec +\Modulemember Voip +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/voip/voiptranscribe.h" + +/*** Defines **********************************************************************/ + +//! used by some of the fractional integer codes +#define VOIP_CODEC_OUTPUT_FRACTIONAL (12) + +//! operate on active codec (passed for ident, valid for VoipDestroy, VoipControl) +#define VOIP_CODEC_ACTIVE (-1) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque module ref +typedef struct VoipCodecRefT VoipCodecRefT; + +//! create function type +typedef VoipCodecRefT *(VoipCodecCreateT)(int32_t iNumDecoders); + +//! destroy function type +typedef void (VoipCodecDestroyT)(VoipCodecRefT *pState); + +//! encode function type +typedef int32_t (VoipCodecEncodeT)(VoipCodecRefT *pState, uint8_t *pOutput, const int16_t *pInput, int32_t iNumSamples); + +//! decode function type +typedef int32_t (VoipCodecDecodeT)(VoipCodecRefT *pState, int32_t *pOutput, const uint8_t *pInput, int32_t iInputBytes, int32_t iChannel); + +//! reset function type +typedef void (VoipCodecResetT)(VoipCodecRefT *pState); + +//! control function type +typedef int32_t (VoipCodecControlT)(VoipCodecRefT *pState, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue); + +//! status function type +typedef int32_t (VoipCodecStatusT)(VoipCodecRefT *pState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize); + +//! codec function block +typedef struct VoipCodecDefT +{ + VoipCodecCreateT *pCreate; + VoipCodecDestroyT *pDestroy; + VoipCodecEncodeT *pEncode; + VoipCodecDecodeT *pDecode; + VoipCodecResetT *pReset; + VoipCodecControlT *pControl; + VoipCodecStatusT *pStatus; +} VoipCodecDefT; + +//! codec state structure +struct VoipCodecRefT +{ + const VoipCodecDefT *pCodecDef; + int32_t iDecodeChannels; + + #if DIRTYCODE_LOGGING + int32_t iDebugLevel; //>16)&0xff) +#define VOIPTRANSCRIBE_PROFILE_FORMAT(_uProfile) (VoipTranscribeFormatE)(((_uProfile)>>8)&0xff) +#define VOIPTRANSCRIBE_PROFILE_TRANSPORT(_uProfile) (VoipTranscribeTransportE)((_uProfile)&0xff) + +// disabled profile +#define VOIPTRANSCRIBE_PROFILE_DISABLED VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_NONE, VOIPTRANSCRIBE_FORMAT_NONE, VOIPTRANSCRIBE_TRANSPORT_NONE) +// watson profiles +#define VOIPTRANSCRIBE_PROFILE_IBMWATSON_HTTP_LI16 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_IBMWATSON, VOIPTRANSCRIBE_FORMAT_LI16, VOIPTRANSCRIBE_TRANSPORT_HTTP) +#define VOIPTRANSCRIBE_PROFILE_IBMWATSON_HTTP_WAV16 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_IBMWATSON, VOIPTRANSCRIBE_FORMAT_WAV16, VOIPTRANSCRIBE_TRANSPORT_HTTP) +#define VOIPTRANSCRIBE_PROFILE_IBMWATSON_HTTP_OPUS VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_IBMWATSON, VOIPTRANSCRIBE_FORMAT_OPUS, VOIPTRANSCRIBE_TRANSPORT_HTTP) +#define VOIPTRANSCRIBE_PROFILE_IBMWATSON_WEBSOCKETS_LI16 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_IBMWATSON, VOIPTRANSCRIBE_FORMAT_LI16, VOIPTRANSCRIBE_TRANSPORT_WEBSOCKETS) +#define VOIPTRANSCRIBE_PROFILE_IBMWATSON_WEBSOCKETS_WAV16 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_IBMWATSON, VOIPTRANSCRIBE_FORMAT_WAV16, VOIPTRANSCRIBE_TRANSPORT_WEBSOCKETS) +#define VOIPTRANSCRIBE_PROFILE_IBMWATSON_WEBSOCKETS_OPUS VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_IBMWATSON, VOIPTRANSCRIBE_FORMAT_OPUS, VOIPTRANSCRIBE_TRANSPORT_WEBSOCKETS) +// bing profiles +#define VOIPTRANSCRIBE_PROFILE_MICROSOFT_HTTP_WAV16 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_MICROSOFT, VOIPTRANSCRIBE_FORMAT_WAV16, VOIPTRANSCRIBE_TRANSPORT_HTTP) +#define VOIPTRANSCRIBE_PROFILE_MICROSOFT_HTTP_OPUS VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_MICROSOFT, VOIPTRANSCRIBE_FORMAT_OPUS, VOIPTRANSCRIBE_TRANSPORT_HTTP) +// google profiles +#define VOIPTRANSCRIBE_PROFILE_GOOGLE_HTTP_LI16 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_GOOGLE, VOIPTRANSCRIBE_FORMAT_LI16, VOIPTRANSCRIBE_TRANSPORT_HTTP) +#define VOIPTRANSCRIBE_PROFILE_GOOGLE_HTTP_OPUS VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_GOOGLE, VOIPTRANSCRIBE_FORMAT_OPUS, VOIPTRANSCRIBE_TRANSPORT_HTTP) +#define VOIPTRANSCRIBE_PROFILE_GOOGLE_HTTP2_LI16 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_GOOGLE, VOIPTRANSCRIBE_FORMAT_LI16, VOIPTRANSCRIBE_TRANSPORT_HTTP2) +#define VOIPTRANSCRIBE_PROFILE_GOOGLE_HTTP2_OPUS VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_GOOGLE, VOIPTRANSCRIBE_FORMAT_OPUS, VOIPTRANSCRIBE_TRANSPORT_HTTP2) +// amazon profiles +#define VOIPTRANSCRIBE_PROFILE_AMAZON_HTTP2 VOIPTRANSCRIBE_PROFILE_CONSTRUCT(VOIPTRANSCRIBE_PROVIDER_AMAZON, VOIPTRANSCRIBE_FORMAT_LI16, VOIPTRANSCRIBE_TRANSPORT_HTTP2) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! possible transcription providers +typedef enum VoipTranscribeProviderE +{ + VOIPTRANSCRIBE_PROVIDER_NONE, + VOIPTRANSCRIBE_PROVIDER_IBMWATSON, //!< IBM Watson + VOIPTRANSCRIBE_PROVIDER_MICROSOFT, //!< Microsoft Cognitive Services + VOIPTRANSCRIBE_PROVIDER_GOOGLE, //!< Google Speech + VOIPTRANSCRIBE_PROVIDER_AMAZON, //!< Amazon Transcribe + VOIPTRANSCRIBE_NUMPROVIDERS +} VoipTranscribeProviderE; + +//! possible transcription audio formats +typedef enum VoipTranscribeFormatE +{ + VOIPTRANSCRIBE_FORMAT_NONE, + VOIPTRANSCRIBE_FORMAT_LI16, //!< 16bit linear PCM audio with no framing + VOIPTRANSCRIBE_FORMAT_WAV16, //!< 16bit linear PCM in a WAV wrapper + VOIPTRANSCRIBE_FORMAT_OPUS, //!< Opus codec + VOIPTRANSCRIBE_NUMFORMATS +} VoipTranscribeFormatE; + +//! possible transport protocols +typedef enum VoipTranscribeTransportE +{ + VOIPTRANSCRIBE_TRANSPORT_NONE, + VOIPTRANSCRIBE_TRANSPORT_HTTP, //!< HTTPS + VOIPTRANSCRIBE_TRANSPORT_HTTP2, //!< HTTP2 + VOIPTRANSCRIBE_TRANSPORT_WEBSOCKETS, //!< WebSockets + VOIPTRANSCRIBE_NUMTRANSPORTS +} VoipTranscribeTransportE; + +//! opaque module state +typedef struct VoipTranscribeRefT VoipTranscribeRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create the module +DIRTYCODE_API VoipTranscribeRefT *VoipTranscribeCreate(int32_t iBufSize); + +// configure the transcribe module +DIRTYCODE_API void VoipTranscribeConfig(uint32_t uProfile, const char *pUrl, const char *pKey); + +// destroy the module +DIRTYCODE_API void VoipTranscribeDestroy(VoipTranscribeRefT *pVoipTranscribe); + +// submit voice data to be transcribed +DIRTYCODE_API int32_t VoipTranscribeSubmit(VoipTranscribeRefT *pVoipTranscribe, const uint8_t *pBuffer, int32_t iBufLen); + +// get a transcription +DIRTYCODE_API int32_t VoipTranscribeGet(VoipTranscribeRefT *pVoipTranscribe, char *pBuffer, int32_t iBufLen); + +// get module status +DIRTYCODE_API int32_t VoipTranscribeStatus(VoipTranscribeRefT *pVoipTranscribe, int32_t iStatus, int32_t iValue, void *pBuffer, int32_t iBufSize); + +// set control options +DIRTYCODE_API int32_t VoipTranscribeControl(VoipTranscribeRefT *pVoipTranscribe, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// update module +DIRTYCODE_API void VoipTranscribeUpdate(VoipTranscribeRefT *pVoipTranscribe); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _voiptranscribe_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voiptunnel.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voiptunnel.h new file mode 100644 index 00000000..07365718 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/voip/voiptunnel.h @@ -0,0 +1,208 @@ +/*H********************************************************************************/ +/*! + \File voiptunnel.h + + \Description + This module implements the main logic for the VoipTunnel server. + + Description forthcoming. + + \Copyright + Copyright (c) 2006 Electronic Arts Inc. + + \Version 03/24/2006 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _voiptunnel_h +#define _voiptunnel_h + +/*! +\Moduledef VoipTunnel VoipTunnel +\Modulemember Voip +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/proto/prototunnel.h" + +/*** Defines **********************************************************************/ + +//! max number of clients that can appear in a group +#define VOIPTUNNEL_MAXGROUPSIZE (32) + +//! max number of games that we can have suspended +#define VOIPTUNNEL_MAXSUSPENDED (4) + +//! default amount of time elapsed before incoming voice is assumed to be stopped, in milliseconds +#define VOIPTUNNEL_RECVVOICE_TIMEOUT_DEFAULT (1 * 1000) + +//! default maximum number of players can be talking at once in a single game +#define VOIPTUNNEL_MAX_BROADCASTING_VOICES_DEFAULT 4 + +// client flags +#define VOIPTUNNEL_CLIENTFLAG_RECVVOICE (1<<0) //!< voiptunnel is currently receiving voice from this client +#define VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED (1<<1) //!< this client has reached its max simultaneous talkers + +/*** Type Definitions *************************************************************/ + +//! voiptunnel event types +typedef enum VoipTunnelEventE +{ + VOIPTUNNEL_EVENT_ADDCLIENT, //!< client added to client list + VOIPTUNNEL_EVENT_DELCLIENT, //!< client removed from client list + VOIPTUNNEL_EVENT_MATCHADDR, //!< client matched addr + VOIPTUNNEL_EVENT_MATCHPORT, //!< client matched port + VOIPTUNNEL_EVENT_RECVVOICE, //!< received voice data from client + VOIPTUNNEL_EVENT_SENDVOICE, //!< sending voice data to client + VOIPTUNNEL_EVENT_DEADVOICE, //!< voice connection has gone dead + VOIPTUNNEL_EVENT_MAXDVOICE, //!< additional clients attempting to send voice will not be rebroadcast + VOIPTUNNEL_EVENT_AVLBVOICE, //!< additional clients may once again send voice + VOIPTUNNEL_EVENT_ADDGAME, //!< game added to game list + VOIPTUNNEL_EVENT_DELGAME, //!< game removed from game list + + VOIPTUNNEL_NUMEVENTS //!< total number of events +} VoipTunnelEventE; + +//! client specific game we save when suspending +typedef struct VoipTunnelSuspendInfoT +{ + int16_t iGameIdx; + uint8_t _pad[2]; + int32_t iNumClients; + int32_t aClientIds[VOIPTUNNEL_MAXGROUPSIZE]; + char strTunnelKey[PROTOTUNNEL_MAXKEYLEN]; +} VoipTunnelSuspendInfoT; + +//! voiptunnel client info +typedef struct VoipTunnelClientT +{ + uint32_t uRemoteAddr; //!< remote address + uint16_t uRemoteGamePort; //!< remote game port + uint16_t uRemoteVoipPort; //!< remote voip port + int16_t iGameIdx; //!< game index client is associated with (active) + int16_t iNumSuspended; //!< number of games this client has suspended + uint8_t uFlags; //!< client flags + uint8_t uPacketVersion; //!< version of voip protocol as identified by connection packets + uint8_t _pad[2]; + uint32_t uClientId; //!< unique client identifier + uint32_t uLastUpdate; //!< last update time in milliseconds + uint32_t uLastRecvVoice; //!< last tick voice mic data was received from this client + uint32_t uLastRecvGame; //!< last tick game data was received from this client (not managed by voiptunnel; for user convenience) + uint32_t uSendMask; //!< current voice send mask (from client) + uint32_t uGameSendMask; //!< current voice send mask (from game) + uint32_t uTunnelId; //!< tunnel id -- for (optional) use by application + int32_t iNumClients; //!< number of clients in client list + int32_t iNumTalker; //!< number of other clients talking to this client + int32_t iUserData0; //!< storage for user-specified data (not managed by voiptunnel; for user convenience) + int32_t aClientIds[VOIPTUNNEL_MAXGROUPSIZE]; //!< list of other clients in send group (GAMEFLAG_SEND_MULTI) + char strTunnelKey[PROTOTUNNEL_MAXKEYLEN]; //!< this client's portion of the tunnel key + + //! keeps track of the game's the client belongs to + VoipTunnelSuspendInfoT aSuspendedData[VOIPTUNNEL_MAXSUSPENDED]; +} VoipTunnelClientT; + +//! game structure +typedef struct VoipTunnelGameT +{ + int32_t iNumClients; //!< current number of clients + int32_t iNumTalkingClients; //!< number of "talking" clients (i.e. client with VOIPTUNNEL_CLIENTFLAG_RECVVOICE flag set) + uint32_t uVoiceDataDropMetric; //!< the number of voice packets that were not rebroadcast in this game due to max broadcasters constraint + uint32_t uGameId; //!< unique game identifier + uint32_t aClientList[VOIPTUNNEL_MAXGROUPSIZE]; + uint8_t bClientActive[VOIPTUNNEL_MAXGROUPSIZE]; //!< active clients for the aClientList +} VoipTunnelGameT; + +//! opaque module state +typedef struct VoipTunnelRefT VoipTunnelRefT; + +//! voiptunnel callback data +typedef struct VoipTunnelEventDataT +{ + VoipTunnelEventE eEvent; + VoipTunnelClientT *pClient; + int32_t iDataSize; +} VoipTunnelEventDataT; + +//! voiptunnel event callback +typedef void (VoipTunnelCallbackT)(VoipTunnelRefT *pVoipTunnel, const VoipTunnelEventDataT *pEventData, void *pUserData); + +//! voiptunnel match function +typedef int32_t (VoipTunnelMatchFuncT)(VoipTunnelClientT *pClient, void *pUserData); + +/*** Function Prototypes **********************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create the module state +DIRTYCODE_API VoipTunnelRefT *VoipTunnelCreate(uint32_t uVoipPort, int32_t iMaxClients, int32_t iMaxGames); + +// set optional voiptunnel event callback +DIRTYCODE_API void VoipTunnelCallback(VoipTunnelRefT *pVoipTunnel, VoipTunnelCallbackT *pCallback, void *pUserData); + +// destroy the module state +DIRTYCODE_API void VoipTunnelDestroy(VoipTunnelRefT *pVoipTunnel); + +// add a client to the client list +DIRTYCODE_API int32_t VoipTunnelClientListAdd(VoipTunnelRefT *pVoipTunnel, const VoipTunnelClientT *pClientInfo, VoipTunnelClientT **ppNewClient); + +// add a client to the client list, at a specific index +DIRTYCODE_API int32_t VoipTunnelClientListAdd2(VoipTunnelRefT *pVoipTunnel, const VoipTunnelClientT *pClientInfo, VoipTunnelClientT **ppNewClient, int32_t iIndex); + +// remove a client from the client list +DIRTYCODE_API void VoipTunnelClientListDel(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, int32_t iGameIdx); + +// add a game to game list +DIRTYCODE_API int32_t VoipTunnelGameListAdd(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx); + +// add a game to game list with unique game identifier +DIRTYCODE_API int32_t VoipTunnelGameListAdd2(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx, uint32_t uGameId); + +// remove a game from game list +DIRTYCODE_API int32_t VoipTunnelGameListDel(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx); + +// return client matching given address +DIRTYCODE_API VoipTunnelClientT *VoipTunnelClientListMatchAddr(VoipTunnelRefT *pVoipTunnel, uint32_t uRemoteAddr); + +// return client matching given client identifier +DIRTYCODE_API VoipTunnelClientT *VoipTunnelClientListMatchId(VoipTunnelRefT *pVoipTunnel, uint32_t uClientId); + +// return client at given client index +DIRTYCODE_API VoipTunnelClientT *VoipTunnelClientListMatchIndex(VoipTunnelRefT *pVoipTunnel, uint32_t uClientIndex); + +// return client matching given address and port +DIRTYCODE_API VoipTunnelClientT *VoipTunnelClientListMatchSockaddr(VoipTunnelRefT *pVoipTunnel, struct sockaddr *pSockaddr); + +// return client using user-supplied match function +DIRTYCODE_API VoipTunnelClientT *VoipTunnelClientListMatchFunc(VoipTunnelRefT *pVoipTunnel, VoipTunnelMatchFuncT *pMatchFunc, void *pUserData); + +// return game at given game index +DIRTYCODE_API VoipTunnelGameT *VoipTunnelGameListMatchIndex(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx); + +// return game matching given game identifier +DIRTYCODE_API VoipTunnelGameT *VoipTunnelGameListMatchId(VoipTunnelRefT *pVoipTunnel, uint32_t uGameId, int32_t *pGameIdx); + +// let api know client's send mask should be updated +DIRTYCODE_API void VoipTunnelClientRefreshSendMask(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient); + +// get module status +DIRTYCODE_API int32_t VoipTunnelStatus(VoipTunnelRefT *pVoipTunnel, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize); + +// control the module +DIRTYCODE_API int32_t VoipTunnelControl(VoipTunnelRefT *pVoipTunnel, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue); + +// update voiptunnel state +DIRTYCODE_API void VoipTunnelUpdate(VoipTunnelRefT *pVoipTunnel); + +#ifdef __cplusplus +}; +#endif + +//@} + +#endif // _voiptunnel_h + diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/xml/xmlformat.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/xml/xmlformat.h new file mode 100644 index 00000000..ef55fce6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/xml/xmlformat.h @@ -0,0 +1,141 @@ +/*H*************************************************************************************/ +/*! + \File xmlformat.h + + \Description + This module formats simple Xml, in a linear fashion, using a character buffer + that the client provides. + + \Notes + \verbatim + Since this is a simple linear buffer write, without support for a DOM, the following + rules apply: + - You cannot set attributes or element values if an element (tag) is not started. + - If you start an element, (tag), you have to set the attributes before any + child elements are added. + - The XmlElemAddXXX calls will create a complete element, complete with start + and end tags. + - XmlInit MUST be called on the client buffer, before any api functions are + called. + - Once XML output is complete, the client should call XmlFinish(). There is + some hidden data in the buffer which must be cleared prior to output. Client + should account for 24 extra bytes in buffer for use by Xml API. + + References: + 1) XML Core: http://www.w3.org/XML/Core/ + 2) XML 1.0 4th edition specifications: http://www.w3.org/TR/xml/ + \endverbatim + + \Copyright + Copyright (c) Electronic Arts 2004-2008. ALL RIGHTS RESERVED. + + \Version 01/30/2004 (jbertrand) First Version +*/ +/*************************************************************************************H*/ + +#ifndef _xmlformat_h +#define _xmlformat_h + +/*! +\Moduledef XmlFormat XmlFormat +\Modulemember Util +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +#define XML_ERR_NONE 0 +#define XML_ERR_FULL -1 //Buffer is full, no space to add attribute or element. +#define XML_ERR_UNINIT -2 //Did not call XmlInit() prior to writing. +#define XML_ERR_NOT_OPEN -3 //Attempt to set elem or attrib, but, no tag opened. +#define XML_ERR_ATTR_POSITION -4 //Attempt to set an attrib, but, child element already added to tag. +#define XML_ERR_INVALID_PARAM -5 //Invalid parameter passed to function + +// Encoding control flags +#define XML_FL_WHITESPACE 1 //!< Include formatting whitespace + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Init the API -- pass in the character buffer. MUST be called first. +DIRTYCODE_API void XmlInit(char *pBuffer, int32_t iBufLen, uint8_t uFlags); + +// Notify the xmlformat API that the buf size was increased +DIRTYCODE_API void XmlBufSizeIncrease(char *pBuffer, int32_t iNewBufLen); + +// Finish XML output to this buffer. +DIRTYCODE_API char *XmlFinish(char *pBuffer); + +// Start an element to which other elements or attributes may be added. +DIRTYCODE_API int32_t XmlTagStart(char *pBuffer, const char *pName); + +// End the current element or tag-- must have an outstanding open tag. +DIRTYCODE_API int32_t XmlTagEnd(char *pBuffer); + +// Add a complete, contained text element. Builds start and end tag. Nothing can be appended to this node. +DIRTYCODE_API int32_t XmlElemAddString(char *pBuffer, const char *pElemName, const char *pValue); + +// Add a complete, contained integer element. Builds start and end tag. Nothing can be appended to this node. +DIRTYCODE_API int32_t XmlElemAddInt(char *pBuffer, const char *pElemName, int32_t iValue); + +// Add a complete, contained decimal element. Format it using formatSpec. Builds start and end tag. Nothing can be appended to this node. +DIRTYCODE_API int32_t XmlElemAddFloat(char *pBuffer, const char *pElemName, const char *pFormatSpec, float fValue); + +// Add a complete, contained Date elem, using epoch date. Builds start and end tag. Nothing can be appended to this node. +DIRTYCODE_API int32_t XmlElemAddDate(char *pBuffer, const char *pElemName, uint32_t uEpochDate); + +// Set a text attribute for an open element (started tag). Must be called before element text is set. +DIRTYCODE_API int32_t XmlAttrSetString(char *pBuffer, const char *pAttrName, const char *pValue); + +// Set a text attribute for an open element (started tag), with no text encoding being performed on the input string. Must be called before element text is set. +DIRTYCODE_API int32_t XmlAttrSetStringRaw(char *pBuffer, const char *pAttr); + +// Set an integer attribute for an open element (started tag). Must be called before element text is set. +DIRTYCODE_API int32_t XmlAttrSetInt(char *pBuffer, const char *pAttrName, int32_t iValue); + +// Set an IP address attribute (in dot notation) +DIRTYCODE_API int32_t XmlAttrSetAddr(char *pBuffer, const char *pAttrName, uint32_t uAddr); + +// Set a decimal attribute for an open element (started tag). Format using formatSpec. Must be called before element text is set. +DIRTYCODE_API int32_t XmlAttrSetFloat(char *pBuffer, const char *pAttrName, const char *pFormatSpec, float fValue); + +// Set a date attribute for an open element (started tag). Must be called before element text is set. +DIRTYCODE_API int32_t XmlAttrSetDate(char *pBuffer, const char *pAttrName, uint32_t uEpochDate); + +// Set a text value for an open element. Must have an open element (started tag). +DIRTYCODE_API int32_t XmlElemSetString(char *pBuffer, const char *pValue); + +// Set a text value for an open element, with no text encoding being performed on the input string. Must have an open element (started tag). +DIRTYCODE_API int32_t XmlElemSetStringRaw(char *pBuffer, const char *pValue); + +// Set an integer value for an open element. Must have an open element (started tag). +DIRTYCODE_API int32_t XmlElemSetInt(char *pBuffer, int32_t iValue); + +// Set an IP address value for an open element (in dot notation). Must have an open element (started tag). +DIRTYCODE_API int32_t XmlElemSetAddr(char *pBuffer, uint32_t uAddr); + +// Set a date for an open element. Must have an open element (started tag). +DIRTYCODE_API int32_t XmlElemSetDate(char *pBuffer, uint32_t uEpoch); + +// Build an XML output string from a formatted pattern (like vprintf) +#ifdef va_start +DIRTYCODE_API char *XmlFormatVPrintf(char *pXmlBuff, int32_t iBufLen, const char *pFormat, va_list pFmtArgs); +#endif + +// Build an XML output string from a formatted pattern (like printf) +DIRTYCODE_API char *XmlFormatPrintf(char *pXmlBuff, int32_t iBufLen, const char *pFormat, ...); + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _xmlformat_h diff --git a/r5dev/thirdparty/dirtysdk/include/DirtySDK/xml/xmlparse.h b/r5dev/thirdparty/dirtysdk/include/DirtySDK/xml/xmlparse.h new file mode 100644 index 00000000..a11b40d0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/include/DirtySDK/xml/xmlparse.h @@ -0,0 +1,130 @@ +/*H*************************************************************************************************/ +/*! + + \File xmlparse.h + + \Description + \verbatim + This is a simple XML parser for use in controlled situations. It should not + be used with arbitrary XML from uncontrolled hosts (i.e., test its parsing against + a particular host before using it). This only implement a small subset of full + XML and while suitable for many applications, it is easily confused by badly + formed XML or by some of the ligitimate but convoluated XML conventions. + + In particular, this parser cannot handle degenerate empty elements (i.e.,

+ will confuse it). Empty elements must be of proper XML form

. Only the + predefined entity types (< > & ' ") are supported. This module + does not support unicode values. + \endverbatim + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 01/30/2002 (GWS) First Version + +*/ +/*************************************************************************************************H*/ + +#ifndef _xmlparse_h +#define _xmlparse_h + +/*! +\Moduledef XmlParse XmlParse +\Modulemember Util +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +// debug printing routines +#if DIRTYCODE_LOGGING + #define XmlPrintFmt(_x) XmlPrintFmtCode _x +#else + #define XmlPrintFmt(_x) { } +#endif + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// skip to the next xml entity (used to enumerate lists) +DIRTYCODE_API const char *XmlSkip(const char *strXml); + +// find an entity within an xml document +DIRTYCODE_API const char *XmlFind(const char *strXml, const char *strName); + +// skip to next entity with same name as current +DIRTYCODE_API const char *XmlNext(const char *strXml); + +// step to next xml element +DIRTYCODE_API const char *XmlStep(const char *strXml); + +// determines if the xml entity pointed to is complete +DIRTYCODE_API uint32_t XmlComplete(const char *pXml); + +// return entity contents as a string +DIRTYCODE_API int32_t XmlContentGetString(const char *strXml, char *strBuffer, int32_t iLength, const char *strDefault); + +// return entity contents as an integer +DIRTYCODE_API int32_t XmlContentGetInteger(const char *strXml, int32_t iDefault); + +// return entity contents as a 64-bit integer +DIRTYCODE_API int64_t XmlContentGetInteger64(const char *strXml, int64_t iDefault); + +// return entity contents as a token (a packed sequence of characters) +DIRTYCODE_API int32_t XmlContentGetToken(const char *pXml, int32_t iDefault); + +// return epoch seconds for a date +DIRTYCODE_API uint32_t XmlContentGetDate(const char *pXml, uint32_t uDefault); + +// parse entity contents as an internet dot-notation address +DIRTYCODE_API int32_t XmlContentGetAddress(const char *pXml, int32_t iDefault); + +// return binary encoded data +DIRTYCODE_API int32_t XmlContentGetBinary(const char *pXml, char *pBuffer, int32_t iLength); + +// return entity attribute as a string +DIRTYCODE_API int32_t XmlAttribGetString(const char *strXml, const char *strAttrib, char *strBuffer, int32_t iLength, const char *strDefault); + +// return entity attribute as an integer +DIRTYCODE_API int32_t XmlAttribGetInteger(const char *strXml, const char *strAttrib, int32_t iDefault); + +// return entity attribute as an 64-bit integer +DIRTYCODE_API int64_t XmlAttribGetInteger64(const char *strXml, const char *strAttrib, int64_t iDefault); + +// return entity attribute as a token +DIRTYCODE_API int32_t XmlAttribGetToken(const char *pXml, const char *pAttrib, int32_t iDefault); + +// return epoch seconds for a date +DIRTYCODE_API uint32_t XmlAttribGetDate(const char *pXml, const char *pAttrib, uint32_t uDefault); + +// convert epoch to date components +DIRTYCODE_API int32_t XmlConvEpoch2Date(uint32_t uEpoch, int32_t *pYear, int32_t *pMonth, int32_t *pDay, int32_t *pHour, int32_t *pMinute, int32_t *pSecond); + +// debug output of XML reformatted to look 'nicer' - use macro wrapper XmlPrintFmt(), do not call directly +#if DIRTYCODE_LOGGING +DIRTYCODE_API void XmlPrintFmtCode(const char *pXml, const char *pFormat, ...); +#endif + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _xmlparse_h diff --git a/r5dev/thirdparty/dirtysdk/sample/dirtysim/example.txt b/r5dev/thirdparty/dirtysdk/sample/dirtysim/example.txt new file mode 100644 index 00000000..ee1420ac --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/dirtysim/example.txt @@ -0,0 +1,31 @@ +Example of using DirtySim with Tester2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +DirtySim creates a "bridge" between two clients. It servers as a middle-man that can introduce packet loss, packet reordering, or packet corruption. Here's an example setup and some Tester2 commands that are used to drive the clients that connect through the bridge for both game and voice. In both cases, the three options are given; direct connection, tunneled connection, and tunneled connection through DirtySim. + +ClientA DirtySim ClientB +10.8.13.194:8010 10.8.13.194:8000 10.8.13.194:8020 + +; Voice connnection, direct +t2host voice create -nodevselect 8; voice connect 10.8.13.194:6000:6001 +t2host voice create -nodevselect 8; voice connect 10.8.13.194:6001:6000 + +; Voice connection, tunneled, direct +t2host tunnel create 43214321 8010; tunnel alloc 12341234 10.8.13.194 8020 abcd; net ctrl vadd 6000; voice create -nodevselect 8; voice ctrl clid 16777216; voice connect 1.0.0.0 01000001 +t2host tunnel create 12341234 8020; tunnel alloc 43214321 10.8.13.194 8010 abcd; net ctrl vadd 6000; voice create -nodevselect 8; voice ctrl clid 16777217; voice connect 1.0.0.0 01000000 + +; Voice connection, tunneled, dirtysim +t2host tunnel create 43214321 8010; tunnel alloc 12341234 10.8.13.194 8000 abcd; net ctrl vadd 6000; voice create -nodevselect 8; voice ctrl clid 16777216; voice connect 1.0.0.0 01000001 +t2host tunnel create 12341234 8020; tunnel alloc 43214321 10.8.13.194 8000 abcd; net ctrl vadd 6000; voice create -nodevselect 8; voice ctrl clid 16777217; voice connect 1.0.0.0 01000000 + +; Game connection, direct +t2host gamelink connect 10.8.13.194:3658:3659 +t2host gamelink connect 10.8.13.194:3659:3658 + +; Game connection, tunneled, direct +t2host tunnel create 43214321 8010; tunnel alloc 12341234 10.8.13.194 8020 abcd; net ctrl vadd 3658; gamelink connect 1.0.0.0:3658 +t2host tunnel create 12341234 8020; tunnel alloc 43214321 10.8.13.194 8010 abcd; net ctrl vadd 3658; gamelink connect 1.0.0.0:3658 + +; Game connection, tunneled, dirtysim +t2host tunnel create 43214321 8010; tunnel alloc 12341234 10.8.13.194 8000 abcd; net ctrl vadd 3658; gamelink connect 1.0.0.0:3658 +t2host tunnel create 12341234 8020; tunnel alloc 43214321 10.8.13.194 8000 abcd; net ctrl vadd 3658; gamelink connect 1.0.0.0:3658 \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/sample/dirtysim/source/dirtysim.c b/r5dev/thirdparty/dirtysdk/sample/dirtysim/source/dirtysim.c new file mode 100644 index 00000000..9dc9b5c6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/dirtysim/source/dirtysim.c @@ -0,0 +1,489 @@ +/*H*************************************************************************************/ +/*! + \File dirtysim.c + + \Description + Lightweight network simulator intended to allow exercise of various codepaths + in network code that handle adverse network conditions (latency, packet loss, + out of order packets). Is *not* intended to accurately simulate real network + conditions. + + \Copyright + Copyright (c) Electronic Arts 2018. + + \Version 01/25/2018 (jbrookes) First Version +*/ +/*************************************************************************************H*/ + +/*** Example Usage *********************************************************************/ + +/*** Include files *********************************************************************/ + +#include +#include +#include + +// dirtysock includes +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/proto/protossl.h" + +// zlib includes +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +/*** Defines ***************************************************************************/ + +// PACKET_CHANCE percentage chance packet is lost, out of order, or corrupted +#define PACKET_CHANCE_PCT (10) +#define PACKET_CHANCE ((65536*PACKET_CHANCE_PCT)/100) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct DirtyPacketT +{ + uint32_t uPacketSize; + uint32_t uPacketTick; + uint8_t aPacketData[SOCKET_MAXUDPRECV]; +} DirtyPacketT; + +typedef struct DirtyPacketQueueT +{ + uint32_t uNumPackets; + uint32_t uFirstPacket; + uint32_t uLastPacket; + DirtyPacketT PacketBuf[128]; +} DirtyPacketQueueT; + +typedef struct DirtyBridgeT +{ + struct sockaddr ClientAddrA; + DirtyPacketQueueT ClientQueueA; + struct sockaddr ClientAddrB; + DirtyPacketQueueT ClientQueueB; +} DirtyBridgeT; + +typedef struct DirtysimOptionsT +{ + uint32_t uTemp; +} DirtysimOptionsT; + +typedef struct DirtysimStateT +{ + SocketT *pSocket; + DirtyBridgeT Bridge; +} DirtysimStateT; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +// Public variables + + +/*** Private Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function dirtysim_zprintf_hook + + \Description + Hook up debug output for ZPrintf where appropriate + + \Input *pParm - unused + \Input *pText - text to print + + \Output + int32_t - one + + \Version 01/25/2018 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t dirtysim_zprintf_hook(void *pParm, const char *pText) +{ + // on PC we want output to console in addition to debug output + #if defined(DIRTYCODE_PC) + printf("%s", pText); + #endif + // don't suppress debug output + return(1); +} + +/*F*************************************************************************************/ +/*! + \Function network_startup + + \Description + Start up required DirtySDK networking + + \Input *pParm - unused + \Input *pText - text to print + + \Output + int32_t - one + + \Version 01/25/2018 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t network_startup(void) +{ + int32_t iResult=0, iStatus, iTimeout; + + // start network + NetConnStartup("-servicename=dirtysim"); + + // bring up the interface + NetConnConnect(NULL, NULL, 0); + + // wait for network interface activation + for (iTimeout = NetTick() + 15*1000; ; ) + { + // update network + NetConnIdle(); + + // get current status + iStatus = NetConnStatus('conn', 0, NULL, 0); + if ((iStatus == '+onl') || ((iStatus >> 24) == '-')) + { + break; + } + + // check for timeout + if (iTimeout < (signed)NetTick()) + { + ZPrintf("dirtysim: timeout waiting for interface activation\n"); + break; + } + + // give time to other threads + NetConnSleep(500); + } + + // check result code + if ((iStatus = NetConnStatus('conn', 0, NULL, 0)) == '+onl') + { + ZPrintf("dirtysim: interface active\n"); + iResult = 1; + } + else if ((iStatus >> 24) == '-') + { + ZPrintf("dirtysim: error %C bringing up interface\n", iStatus); + } + + // return result to caller + return(iResult); +} + +/*F*************************************************************************************/ +/*! + \Function process_args + + \Description + Process command-line arguments + + \Input iArgc - arg count + \Input *pArgv[] - arg list + \Input *pOptions - [out] parsed options + + \Version 01/25/2018 (jbrookes) +*/ +/*************************************************************************************F*/ +static void process_args(int32_t iArgc, const char *pArgv[], DirtysimOptionsT *pOptions) +{ + int32_t iArg; + + // echo options + for (iArg = 0; iArg < iArgc; iArg += 1) + { + ZPrintf("%s ", pArgv[iArg]); + } + ZPrintf("\n"); + + ds_memclr(pOptions, sizeof(*pOptions)); + + // init default options + + // pick off command-line options + for (iArg = 1; (iArg < iArgc) && (pArgv[iArg][0] == '-'); iArg += 1) + { + #if 0 // example processing + if (!strcmp(pArgv[iArg], "-someopt") && ((iArg+1) < iArgc)) + { + // process option, skip additional value if present (e.g. -someopt value) + iArg += 1; + } + #endif + } +} + +/*F*************************************************************************************/ +/*! + \Function add_packet_to_queue + + \Description + Add a packet to packet queue + + \Input *pPacketQueue - packet queue to add to + \Input iPacketData - packet data to add + \Input iPacketSize - size of packet data + + \Version 01/25/2018 (jbrookes) +*/ +/*************************************************************************************F*/ +static void add_packet_to_queue(DirtyPacketQueueT *pPacketQueue, const uint8_t *pPacketData, int32_t iPacketSize) +{ + const uint32_t uQueueSize = sizeof(pPacketQueue->PacketBuf)/sizeof(pPacketQueue->PacketBuf[0]); + DirtyPacketT *pPacket; + + if (pPacketQueue->uNumPackets == uQueueSize) + { + ZPrintf("dirtysim: packet queue full\n"); + return; + } + + // allocate packet + pPacket = &pPacketQueue->PacketBuf[pPacketQueue->uNumPackets++]; + // copy data + ds_memcpy_s(pPacket->aPacketData, sizeof(pPacket->aPacketData), pPacketData, iPacketSize); + pPacket->uPacketSize = iPacketSize; + // timestamp receive + pPacket->uPacketTick = NetTick(); +} + +/*F*************************************************************************************/ +/*! + \Function get_packet_from_queue + + \Description + Get a packet from packet queue + + \Input *pPacketQueue - packet queue to get packet from + \Input iPacketData - [out] packet buffer + \Input iPacketSize - size of packet buffer + \Input uMinQueueSize - min queue size to allow packet reordering + + \Version 01/25/2018 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t get_packet_from_queue(DirtyPacketQueueT *pPacketQueue, uint8_t *pPacketData, int32_t iPacketSize, uint32_t uMinQueueSize) +{ + DirtyPacketT *pPacket; + + // make sure we buffer at least one packet to be able to do out-of-order + if (pPacketQueue->uNumPackets <= uMinQueueSize) + { + return(0); + } + + // deallocate packet + pPacket = &pPacketQueue->PacketBuf[--pPacketQueue->uNumPackets]; + // copy data + ds_memcpy_s(pPacketData, iPacketSize, pPacket->aPacketData, pPacket->uPacketSize); + // return + return(pPacket->uPacketSize); +} + +/*F*************************************************************************************/ +/*! + \Function packet_queue_process + + \Description + Process packet queue, introducing possible packet loss, out of order, + or corruption. + + \Input *pState - application state + \Input *pPacketQueue - packet queue to process + \Input *pSrcAddr - bridge src addr + \Input *pDstAddr - bridge dst addr + + \Version 01/25/2018 (jbrookes) +*/ +/*************************************************************************************F*/ +static void packet_queue_process(DirtysimStateT *pState, DirtyPacketQueueT *pPacketQueue, struct sockaddr *pSrcAddr, struct sockaddr *pDstAddr) +{ + uint8_t aPacketBuf[SOCKET_MAXUDPRECV]; + int32_t iAddrLen = sizeof(*pDstAddr), iPacketLen; + uint32_t uRandom; + + // get packet; give us a buffer to allow out-of-order packets + if ((iPacketLen = get_packet_from_queue(pPacketQueue, aPacketBuf, sizeof(aPacketBuf), 2)) == 0) + { + return; + } + + // get a random 16bit number + uRandom = NetRand(0xffff); + + // see if we should do something + if (uRandom < PACKET_CHANCE) + { + ZPrintf("dirtysim: %A->%A (%d bytes) (depth=%d) [LOST]\n", pSrcAddr, pDstAddr, iPacketLen, pPacketQueue->uNumPackets); + // packet lost; don't forward + return; + } + else if (uRandom < (PACKET_CHANCE*2)) + { + uint8_t aPacketBuf2[SOCKET_MAXUDPRECV]; + int32_t iPacketLen2; + + // packet out of order - get next packet and send it first + if ((iPacketLen2 = get_packet_from_queue(pPacketQueue, aPacketBuf2, sizeof(aPacketBuf2), 1)) > 0) + { + ZPrintf("dirtysim: %A->%A (%d bytes) (depth=%d) [OUTOFORDER]\n", pSrcAddr, pDstAddr, iPacketLen2, pPacketQueue->uNumPackets); + SocketSendto(pState->pSocket, (char *)aPacketBuf2, iPacketLen2, 0, pDstAddr, iAddrLen); + } + } + else if (uRandom < (PACKET_CHANCE*3)) + { + // corrupt the packet; we pick a random byte and set it to the index value + uRandom = NetRand(iPacketLen-1); + aPacketBuf[uRandom] = (uint8_t)(uRandom&0xff); + ZPrintf("dirtysim: %A->%A (%d bytes) (depth=%d) [CORRUPTED]\n", pSrcAddr, pDstAddr, iPacketLen, pPacketQueue->uNumPackets); + } + else + { + ZPrintf("dirtysim: %A->%A (%d bytes) (depth=%d)\n", pSrcAddr, pDstAddr, iPacketLen, pPacketQueue->uNumPackets); + } + + // forward the packet + SocketSendto(pState->pSocket, (char *)aPacketBuf, iPacketLen, 0, pDstAddr, iAddrLen); +} + + +/*** Public Functions ******************************************************************/ + +// dll-friendly DirtyMemAlloc +#if !defined(DIRTYCODE_DLL) +void *DirtyMemAlloc(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#else +void *DirtyMemAlloc2(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#endif +{ + return(malloc(iSize)); +} + +// dll-friendly DirtyMemFree +#if !defined(DIRTYCODE_DLL) +void DirtyMemFree(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#else +void DirtyMemFree2(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#endif +{ + free(pMem); +} + + +/*F*************************************************************************************/ +/*! + \Function main + + \Description + Main processing for DirtySim + + \Input argc - input argument count + \Input *argv[] - input argument list + + \Output + int32_t - negative on failure, else zero + + \Version 01/25/2018 (jbrookes) +*/ +/*************************************************************************************F*/ +int main(int32_t argc, const char *argv[]) +{ + int32_t iCount, iLen, iTimeout; + DirtysimOptionsT DirtysimOptions; + DirtysimStateT DirtysimState; + uint8_t aPacketBuf[SOCKET_MAXUDPRECV]; + struct sockaddr BindAddr, RecvAddr; + int32_t iAddrLen = sizeof(RecvAddr), iRecvLen; + + #if defined(DIRTYCODE_DLL) + DirtyMemFuncSet(&DirtyMemAlloc2, &DirtyMemFree2); + #endif + + // hook into zprintf output + ZPrintfHook(dirtysim_zprintf_hook, NULL); + + // get options + process_args(argc, argv, &DirtysimOptions); + + // init state + + // start dirtysock + if (!network_startup()) + { + return(-1); + } + + // init bridge + ds_memclr(&DirtysimState.Bridge, sizeof(DirtysimState.Bridge)); + + // client a + SockaddrInit(&DirtysimState.Bridge.ClientAddrA, AF_INET); + SockaddrInSetAddr(&DirtysimState.Bridge.ClientAddrA, SocketInTextGetAddr("10.8.13.194")); + SockaddrInSetPort(&DirtysimState.Bridge.ClientAddrA, 8010); + // client b + SockaddrInit(&DirtysimState.Bridge.ClientAddrB, AF_INET); + SockaddrInSetAddr(&DirtysimState.Bridge.ClientAddrB, SocketInTextGetAddr("10.8.13.194")); + SockaddrInSetPort(&DirtysimState.Bridge.ClientAddrB, 8020); + + // create & bind DirtySim socket + DirtysimState.pSocket = SocketOpen(AF_INET, SOCK_DGRAM, 0); + SockaddrInit(&BindAddr, AF_INET); + SockaddrInSetPort(&BindAddr, 8000); + SocketBind(DirtysimState.pSocket, &BindAddr, iAddrLen); + + // just keep working + for (iCount = 0, iLen = -1, iTimeout = NetTick()-1; ; ) + { + // receive a packet + iRecvLen = SocketRecvfrom(DirtysimState.pSocket, (char *)aPacketBuf, sizeof(aPacketBuf), 0, &RecvAddr, &iAddrLen); + + // if we got something, add it to the packet queue + if (iRecvLen > 0) + { + //ZPrintf("dirtysim: read %d byte packet from %A\n", iRecvLen, &RecvAddr); + // ClientA->ClientB + if (!SockaddrCompare(&RecvAddr, &DirtysimState.Bridge.ClientAddrA)) + { + add_packet_to_queue(&DirtysimState.Bridge.ClientQueueA, aPacketBuf, iRecvLen); + } + // ClientB->ClientA + else if (!SockaddrCompare(&RecvAddr, &DirtysimState.Bridge.ClientAddrB)) + { + add_packet_to_queue(&DirtysimState.Bridge.ClientQueueB, aPacketBuf, iRecvLen); + } + } + + // process the packet queue; if there's a packet to send, send it + packet_queue_process(&DirtysimState, &DirtysimState.Bridge.ClientQueueA, &DirtysimState.Bridge.ClientAddrA, &DirtysimState.Bridge.ClientAddrB); + packet_queue_process(&DirtysimState, &DirtysimState.Bridge.ClientQueueB, &DirtysimState.Bridge.ClientAddrB, &DirtysimState.Bridge.ClientAddrA); + + // if we got something, try again + if (iRecvLen > 0) + { + continue; + } + + // sleep a bit + NetConnSleep(10); + } + + //ZPrintf("dirtysim: done\n"); + + // disconnect from the network% + //NetConnDisconnect(); + + // shutdown the network connections && destroy the dirtysock code + //NetConnShutdown(FALSE); + //return(0); +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/sample/libstat/source/libstat.c b/r5dev/thirdparty/dirtysdk/sample/libstat/source/libstat.c new file mode 100644 index 00000000..38f57541 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/libstat/source/libstat.c @@ -0,0 +1,240 @@ +/*H*************************************************************************************************/ +/*! + + \File libstat.c + + \Description + This sample links in all DirtySock EE code, so that the linker-generated map file may + be used to estimate DirtySock EE code size. + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 02/02/04 (JLB) First Version + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" + +// dirtysock includes +#include "DirtySDK/game/connapi.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/graph/dirtygif.h" +#include "DirtySDK/graph/dirtygraph.h" +#include "DirtySDK/graph/dirtyjpg.h" + +// crypt includes +#include "DirtySDK/crypt/cryptarc4.h" +#include "DirtySDK/crypt/cryptmd5.h" +#include "DirtySDK/crypt/cryptrsa.h" +#include "DirtySDK/crypt/cryptsha1.h" + +// lobby includes +#include "DirtySDK/util/base64.h" +#include "DirtySDK/util/utf8.h" + +// netgame includes +#include "DirtySDK/game/netgamedist.h" +#include "DirtySDK/game/netgamedistserv.h" +#include "DirtySDK/game/netgamelink.h" +#include "DirtySDK/game/netgameutil.h" + +// proto includes +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/proto/protomangle.h" +#include "DirtySDK/proto/protoname.h" +#include "DirtySDK/proto/protostream.h" +#include "DirtySDK/proto/protoupnp.h" + +// voip includes +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voiptunnel.h" + +// xml includes +#include "DirtySDK/xml/xmlformat.h" +#include "DirtySDK/xml/xmlparse.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + + +// Private variables + +// Public variables + + +/*** Public Functions ******************************************************************/ + + +/* + +Required alloc/free functions + +*/ +#ifndef DIRTYCODE_DLL +void *DirtyMemAlloc(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +{ + return(malloc(iSize)); +} + +void DirtyMemFree(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +{ + free(pMem); +} +#endif + +/* + + init routines - the following routines are provided to make sure the modules + link into the application + +*/ + + +/* + + main routine + +*/ + +static void _CryptModules(void) +{ + // cryptarc4 + CryptArc4Init(NULL, NULL, 0, 0); + + // cryptmd5 + CryptMD5Init(NULL); + + // cryptrsa + CryptRSAInit(NULL, NULL, 0, NULL, 0); + + // cryptsha1 + CryptSha1Init(NULL); +} + +static void _DirtysockModules(void) +{ + // dirtylibps2 + NetTick(); + + //dirtygif + DirtyGifIdentify(NULL, 0); + + //dirtygraph + DirtyGraphCreate(); + + //dirtyjpg + DirtyJpgCreate(); + + // netconn/netconnps2 + NetConnMAC(); + + // Proto Includes + // protohttp + ProtoHttpCreate(0); + + // protomangle + ProtoMangleCreate("", 0, "", ""); + + // protoname + ProtoNameAsync("", 0); + + // protostream + ProtoStreamCreate(0); + + // protoupnp + ProtoUpnpStatus(NULL, 0, NULL, 0); +} + +static void _NetgameModules(void) +{ + // netgamelink-e + NetGameLinkCreate(NULL, 0, 0); + + // netgameutil-e + NetGameUtilCreate(); + + // netgamedist + NetGameDistCreate(NULL, NULL, NULL, NULL, 0, 0); + + // netgamedistserv + NetGameDistServCreate(0, 0); + + //connapi + ConnApiGetClientList(NULL); +} + +static void _LobbyModules(void) +{ + // base64 + Base64Encode(0, NULL, NULL); + + // lobbyutf8 + Utf8Strip(NULL, 0, NULL); +} + +static void _XmlModules(void) +{ + // xmlformat + XmlInit(NULL, 0, 0); + + // xmlparse + XmlSkip(NULL); +} + +static void _ContribModules(void) +{ + // voip + VoipStartup(1, 0); + + //voiptunnel + VoipTunnelCreate(0, 0, 0); +} + +int main(int32_t argc, char *argv[]) +{ + // pull in crypt modules + _CryptModules(); + + // pull in DirtySock modules + _DirtysockModules(); + + // pull in lobby modules + _LobbyModules(); + + // pull in netgame modules + _NetgameModules(); + + // pull in xml modules + _XmlModules(); + + // pull in contrib modules + _ContribModules(); + + // done + return(0); +} + + + + diff --git a/r5dev/thirdparty/dirtysdk/sample/ssltest/source/ssltest.c b/r5dev/thirdparty/dirtysdk/sample/ssltest/source/ssltest.c new file mode 100644 index 00000000..f137ad4c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/ssltest/source/ssltest.c @@ -0,0 +1,768 @@ +/*H*************************************************************************************/ +/*! + \File sslstress.c + + \Description + A smoke tester for TLS/SSL, designed to connect to a series of servers while + exercising options (version, cipher) to validate basic connectability across + the protocol feature set supported by ProtoSSL. + + \Copyright + Copyright (c) Electronic Arts 2017. + + \Version 08/03/2017 (jbrookes) First Version +*/ +/*************************************************************************************H*/ + +/*** Example Usage *********************************************************************/ + +/* + Test TLS1.3 servers: + -cert e:\temp\testcerts\DSTRootCAX3.crt -minvers 4 -minciph 14 https://enabled.tls13.com https://tls13.crypto.mozilla.org/ https://tls.ctf.network/ https://rustls.jbp.io/ https://h2o.examp1e.net https://www.mew.org/ + Test TLS1.2 servers: + https://www.google.com https://www.yahoo.com https://www.amazon.com https://facebook.com https://badssl.com +*/ + +/*** Include files *********************************************************************/ + +#include +#include +#include + +// dirtysock includes +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/proto/protossl.h" + +// zlib includes +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +/*** Defines ***************************************************************************/ + +#define SSLTEST_MAXCIPHER_PRE10 1 // index of max cipher supported prior to tls1.0 +#define SSLTEST_MAXCIPHER_PRE12 5 // index of max cipher supported prior to tls1.2 +#define SSLTEST_MAXCIPHER_PRE13 19 // index of max cipher supported prior to tls1.3 +#define SSLTEST_CIPHER_ALL 23 // index of entry supporting all ciphers + +enum +{ + SSLTEST_STAGE_SETUP = 0, + SSLTEST_STAGE_RUNNING, + SSLTEST_STAGE_RESULT, + SSLTEST_STAGE_COMPLETE +}; + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct SsltestOptionsT +{ + int32_t iMinTest; + int32_t iMaxTest; + int32_t iMinServ; + int32_t iMaxServ; + int32_t iMinVers; + int32_t iMaxVers; + int32_t iMinCiph; + int32_t iMaxCiph; + uint8_t bNcrt; + uint8_t bSkip; + uint8_t _pad[2]; +} SsltestOptionsT; + + +typedef struct SsltestStateT +{ + int32_t iTest; + int32_t iServ; + int32_t iVers; + int32_t iCiph; + uint8_t *pClientCert; + int32_t iCertSize; + uint8_t *pClientKey; + int32_t iKeySize; + char strResult[128]; +} SsltestStateT; + +// test function type +typedef void(SsltestTestFuncT)(ProtoHttpRefT *pHttpRef, SsltestOptionsT *pOptions, SsltestStateT *pState, int32_t iStage); + +// test type +typedef struct SsltestTestT +{ + SsltestTestFuncT *pTestFunc; + char *pTestName; +} SsltestTestT; + +/*** Function Prototypes ***************************************************************/ + +static void test_cipher_and_version(ProtoHttpRefT *pHttpRef, SsltestOptionsT *pOptions, SsltestStateT *pState, int32_t iStage); +static void test_resume(ProtoHttpRefT *pHttpRef, SsltestOptionsT *pOptions, SsltestStateT *pState, int32_t iStage); +static void test_basic(ProtoHttpRefT *pHttpRef, SsltestOptionsT *pOptions, SsltestStateT *pState, int32_t iStage); + +/*** Variables *************************************************************************/ + +// Private variables + +static uint16_t _ssltest_versions[] = +{ + PROTOSSL_VERSION_TLS1_0, + PROTOSSL_VERSION_TLS1_1, + PROTOSSL_VERSION_TLS1_2, + PROTOSSL_VERSION_TLS1_3 +}; + +static const char *_ssltest_tlsnames[] = +{ + "TLS1.0", + "TLS1.1", + "TLS1.2", + "TLS1.3", +}; + +// protossl ciphers - matches list order in protossl.h +static uint32_t _ssltest_ciphers[] = +{ + // SSLv3 cipher suites + PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA, + PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA, + // TLS1.0 cipher suites + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA, + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA, + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + // the following cipher suites are TLS1.2+ only + PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256, + PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256, + PROTOSSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256, + PROTOSSL_CIPHER_RSA_WITH_AES_256_GCM_SHA384, + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + PROTOSSL_CIPHER_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + // the following cipher suites are TLS1.3+ only + PROTOSSL_CIPHER_AES_128_GCM_SHA256, + PROTOSSL_CIPHER_AES_256_GCM_SHA384, + PROTOSSL_CIPHER_CHACHA20_POLY1305_SHA256, + // all ciphers + PROTOSSL_CIPHER_ALL|PROTOSSL_CIPHER_ALL_13 +}; + +static const char *_ssltest_ciphernames[] = +{ + // SSLv3 cipher suites + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", + // TLS1.0 cipher suites + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + // TLS1.2 cipher suites + "TLS_RSA_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + // TLS1.3 cipher suites + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + // a synthetic for all ciphers + "TLS_ALL_CIPHERS" +}; + +static SsltestTestT _ssltest_tests[] = +{ + { test_resume, "resume" }, + { test_cipher_and_version, "cipher and version" }, + { test_basic, "basic" } +}; + +// Public variables + + +/*** Private Functions ******************************************************************/ + +static int32_t ssltest_zprintf_hook(void *pParm, const char *pText) +{ + // on PC we want output to console in addition to debug output + #if defined(DIRTYCODE_PC) + printf("%s", pText); + #endif + // don't suppress debug output + return(1); +} + +static int32_t network_startup(void) +{ + int32_t iResult=0, iStatus, iTimeout; + + // start network + NetConnStartup("-servicename=sslstress"); + + // bring up the interface + NetConnConnect(NULL, NULL, 0); + + // wait for network interface activation + for (iTimeout = NetTick() + 15*1000; ; ) + { + // update network + NetConnIdle(); + + // get current status + iStatus = NetConnStatus('conn', 0, NULL, 0); + if ((iStatus == '+onl') || ((iStatus >> 24) == '-')) + { + break; + } + + // check for timeout + if (iTimeout < (signed)NetTick()) + { + ZPrintf("ssltest: timeout waiting for interface activation\n"); + break; + } + + // give time to other threads + NetConnSleep(500); + } + + // check result code + if ((iStatus = NetConnStatus('conn', 0, NULL, 0)) == '+onl') + { + ZPrintf("ssltest: interface active\n"); + iResult = 1; + } + else if ((iStatus >> 24) == '-') + { + ZPrintf("ssltest: error %C bringing up interface\n", iStatus); + } + + // return result to caller + return(iResult); +} + +static uint8_t *load_pem(const char *pFilename, int32_t *pCertSize) +{ + char *pCertBuf; + int32_t iFileSize; + + // load certificate file + if ((pCertBuf = (char *)ZFileLoad(pFilename, &iFileSize, ZFILE_OPENFLAG_RDONLY)) == NULL) + { + return(NULL); + } + + // calculate length + *pCertSize = iFileSize; + // return to caller + return((uint8_t *)pCertBuf); +} + +static void load_ca(const char *pCertPath) +{ + const uint8_t *pFileData; + int32_t iFileSize, iResult = 0; + + // try and open file + if ((pFileData = (const uint8_t *)ZFileLoad(pCertPath, &iFileSize, ZFILE_OPENFLAG_RDONLY | ZFILE_OPENFLAG_BINARY)) != NULL) + { + iResult = ProtoHttpSetCACert(pFileData, iFileSize); + ZMemFree((void *)pFileData); + } + ZPrintf("ssltest: load of certificate '%s' %s\n", pCertPath, (iResult > 0) ? "succeeded" : "failed"); +} + +static void load_cert(SsltestStateT *pState, const char *pCertPath) +{ + // load the cert + pState->pClientCert = load_pem(pCertPath, &pState->iCertSize); + // output result + ZPrintf("ssltest: load of client certificate '%s' %s\n", pCertPath, (pState->pClientCert != NULL) ? "succeeded" : "failed"); +} + +static void load_key(SsltestStateT *pState, const char *pCertPath) +{ + // load the key + pState->pClientKey = load_pem(pCertPath, &pState->iKeySize); + // output result + ZPrintf("ssltest: load of client key '%s' %s\n", pCertPath, (pState->pClientKey != NULL) ? "succeeded" : "failed"); +} + +// test all ciphers and tls versions between min and max +static void test_cipher_and_version(ProtoHttpRefT *pHttpRef, SsltestOptionsT *pOptions, SsltestStateT *pState, int32_t iStage) +{ + if (iStage == SSLTEST_STAGE_SETUP) + { + // disable session resumption to force a new connection every attempt + ProtoHttpControl(pHttpRef, 'resu', 0, 0, NULL); + } + else if (iStage == SSLTEST_STAGE_RUNNING) + { + // select next cipher/version + if (++pState->iCiph >= pOptions->iMaxCiph) + { + pState->iVers += 1; + pState->iCiph = pOptions->iMinCiph; + } + // select next server + if (pState->iVers >= pOptions->iMaxVers) + { + pState->iServ += 1; + pState->iVers = pOptions->iMinVers; + } + } + + if (iStage < SSLTEST_STAGE_RESULT) + { + // select specific cipher + ProtoHttpControl(pHttpRef, 'ciph', _ssltest_ciphers[pState->iCiph], 0, NULL); + } + + // format result text + if (iStage == SSLTEST_STAGE_RESULT) + { + ds_snzprintf(pState->strResult, sizeof(pState->strResult), "vers=%x", ProtoHttpStatus(pHttpRef, 'vers', NULL, 0)); + } +} + +// test session resumption in all tls versions +static void test_resume(ProtoHttpRefT *pHttpRef, SsltestOptionsT *pOptions, SsltestStateT *pState, int32_t iStage) +{ + static int32_t _iTest; + + if (iStage == SSLTEST_STAGE_SETUP) + { + // enable session resumption + ProtoHttpControl(pHttpRef, 'resu', 1, 0, NULL); + + // enable all ciphers + pState->iCiph = SSLTEST_CIPHER_ALL; + ProtoHttpControl(pHttpRef, 'ciph', _ssltest_ciphers[pState->iCiph], 0, NULL); + + // since we want to do tests twice, track that here + _iTest = -1; + } + + // since we're testing resume, we want to connect twice (first non-resuming, second resuming) + if (iStage < SSLTEST_STAGE_RESULT) + { + if (++_iTest == 2) + { + // select next version + pState->iVers += 1; + // if past max, select next server + if (pState->iVers >= pOptions->iMaxVers) + { + pState->iServ += 1; + pState->iVers = pOptions->iMinVers; + } + _iTest = 0; + } + } + + // format result text + if (iStage == SSLTEST_STAGE_RESULT) + { + char strCipher[64] = ""; + ProtoHttpStatus(pHttpRef, 'ciph', strCipher, sizeof(strCipher)); + ds_snzprintf(pState->strResult, sizeof(pState->strResult), "vers=%x, ciph=%s, resume=%s", ProtoHttpStatus(pHttpRef, 'vers', NULL, 0), strCipher, + ProtoHttpStatus(pHttpRef, 'resu', NULL, 0) ? "true" : "false"); + } +} + +// test a single basic connection with all ciphers enabled +static void test_basic(ProtoHttpRefT *pHttpRef, SsltestOptionsT *pOptions, SsltestStateT *pState, int32_t iStage) +{ + if (iStage == SSLTEST_STAGE_SETUP) + { + // enable all ciphers + pState->iCiph = SSLTEST_CIPHER_ALL; + ProtoHttpControl(pHttpRef, 'ciph', _ssltest_ciphers[pState->iCiph], 0, NULL); + //ProtoHttpControl(pHttpRef, 'crvd', 2, 0, NULL); + } + + if (iStage == SSLTEST_STAGE_RUNNING) + { + // select next server + if (pState->iVers >= pOptions->iMaxVers) + { + pState->iServ += 1; + pState->iVers = pOptions->iMinVers; + } + } + + // handle results formatting + if (iStage == SSLTEST_STAGE_RESULT) + { + // format result text + char strCipher[64] = "", strSigAlg[32] = "salg="; + int32_t iSigAlg; + ProtoHttpStatus(pHttpRef, 'ciph', strCipher, sizeof(strCipher)); + iSigAlg = ProtoHttpStatus(pHttpRef, 'salg', strSigAlg+5, sizeof(strSigAlg)-5); + ds_snzprintf(pState->strResult, sizeof(pState->strResult), "vers=%x, ciph=%s %s", ProtoHttpStatus(pHttpRef, 'vers', NULL, 0), strCipher, iSigAlg ? strSigAlg : ""); + } + + // handle completion + if (iStage == SSLTEST_STAGE_COMPLETE) + { + // select next version + pState->iVers += 1; + } +} + +static const char **process_args(SsltestStateT *pState, SsltestOptionsT *pOptions, int32_t iArgc, const char *pArgv[]) +{ + static const char *strServerDefault[] = { "https://www.google.com" }, **pServers; + int32_t iArg; + + // echo options + for (iArg = 0; iArg < iArgc; iArg += 1) + { + ZPrintf("%s ", pArgv[iArg]); + } + ZPrintf("\n"); + + ds_memclr(pOptions, sizeof(*pOptions)); + + // init default options + pOptions->iMinVers = 0; + pOptions->iMaxVers = (int32_t)sizeof(_ssltest_versions)/sizeof(_ssltest_versions[0]); + pOptions->iMinCiph = 0; + pOptions->iMaxCiph = (int32_t)sizeof(_ssltest_ciphers)/sizeof(_ssltest_ciphers[0]) - 1; // remove SSLTEST_CIPHER_ALL + pOptions->iMinTest = 0; + pOptions->iMaxTest = (int32_t)sizeof(_ssltest_tests)/sizeof(_ssltest_tests[0]); + pOptions->bSkip = FALSE; + + // pick off command-line options + for (iArg = 1; (iArg < iArgc) && (pArgv[iArg][0] == '-'); iArg += 1) + { + if (!strcmp(pArgv[iArg], "-cert") && ((iArg+1) < iArgc)) + { + load_ca(pArgv[iArg+1]); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-scrt") && ((iArg+1) < iArgc)) + { + load_cert(pState, pArgv[iArg+1]); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-skey") && ((iArg+1) < iArgc)) + { + load_key(pState, pArgv[iArg+1]); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-skip")) + { + pOptions->bSkip = TRUE; + } + if (!strcmp(pArgv[iArg], "-ncrt")) + { + pOptions->bNcrt = TRUE; + } + if (!strcmp(pArgv[iArg], "-mintest") && ((iArg+1) < iArgc)) + { + pOptions->iMinTest = strtol(pArgv[iArg+1], NULL, 10); + ZPrintf("ssltest: mintest=%d\n", pOptions->iMinTest); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-maxtest") && ((iArg+1) < iArgc)) + { + pOptions->iMaxTest = strtol(pArgv[iArg+1], NULL, 10); + ZPrintf("ssltest: maxtest=%d\n", pOptions->iMaxTest); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-minvers") && ((iArg+1) < iArgc)) + { + pOptions->iMinVers = strtol(pArgv[iArg+1], NULL, 10); + ZPrintf("ssltest: minvers=%d\n", pOptions->iMinVers); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-maxvers") && ((iArg+1) < iArgc)) + { + pOptions->iMaxVers = strtol(pArgv[iArg+1], NULL, 10); + ZPrintf("ssltest: maxvers=%d\n", pOptions->iMaxVers); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-minciph") && ((iArg+1) < iArgc)) + { + pOptions->iMinCiph = strtol(pArgv[iArg+1], NULL, 10); + ZPrintf("ssltest: minciph=%d\n", pOptions->iMinCiph); + iArg += 1; + } + if (!strcmp(pArgv[iArg], "-maxciph") && ((iArg+1) < iArgc)) + { + pOptions->iMaxCiph = strtol(pArgv[iArg+1], NULL, 10); + ZPrintf("ssltest: maxciph=%d\n", pOptions->iMaxCiph); + iArg += 1; + } + } + + // find servers in argument list + if (iArg < iArgc) + { + pServers = pArgv; + pOptions->iMinServ = iArg; + pOptions->iMaxServ = iArgc; + } + else // if no servers specified, use default + { + pServers = strServerDefault; + pOptions->iMinServ = 0; + pOptions->iMaxServ = 1; + } + + // log params + ZPrintf("ssltest: params(vers[%d,%d] ciph[%d,%d] test[%d,%d]\n", pOptions->iMinVers, pOptions->iMaxVers, pOptions->iMinCiph, pOptions->iMaxCiph, pOptions->iMinTest, pOptions->iMaxTest); + + // return servers reference to caller + return(pServers); +} + +static void fail_check(ProtoHttpRefT *pHttpRef, int32_t iLen, char *pResultText, int32_t iResultLen) +{ + ProtoSSLAlertDescT AlertDesc; + int32_t iSockErr = ProtoHttpStatus(pHttpRef, 'serr', NULL, 0); + int32_t iSslFail = ProtoHttpStatus(pHttpRef, 'essl', NULL, 0); + int32_t iAlert = ProtoHttpStatus(pHttpRef, 'alrt', &AlertDesc, sizeof(AlertDesc)); + int32_t iOffset; + + if (iAlert > 0) + { + iOffset = ds_snzprintf(pResultText, iResultLen, "%s ssl alert %s (%d)", (iAlert == 1) ? "recv" : "sent", AlertDesc.pAlertDesc, AlertDesc.iAlertType); + } + else + { + iOffset = ds_snzprintf(pResultText, iResultLen, "download failed (err=%d, sockerr=%d sslerr=%d)", iLen, iSockErr, iSslFail); + } + + if ((iSslFail == PROTOSSL_ERROR_CERT_INVALID) || (iSslFail == PROTOSSL_ERROR_CERT_HOST) || + (iSslFail == PROTOSSL_ERROR_CERT_NOTRUST) || (iSslFail == PROTOSSL_ERROR_CERT_REQUEST)) + { + ProtoSSLCertInfoT CertInfo; + if (ProtoHttpStatus(pHttpRef, 'cert', &CertInfo, sizeof(CertInfo)) == 0) + { + ds_snzprintf(pResultText+iOffset, iResultLen-iOffset, " cert failure (%d): (C=%s, ST=%s, L=%s, O=%s, OU=%s, CN=%s)", iSslFail, + CertInfo.Ident.strCountry, CertInfo.Ident.strState, CertInfo.Ident.strCity, + CertInfo.Ident.strOrg, CertInfo.Ident.strUnit, CertInfo.Ident.strCommon); + } + else + { + ds_snzprintf(pResultText+iOffset, iResultLen-iOffset, " could not get cert info"); + } + } +} + + +/*** Public Functions ******************************************************************/ + +// dll-friendly DirtyMemAlloc +#if !defined(DIRTYCODE_DLL) +void *DirtyMemAlloc(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#else +void *DirtyMemAlloc2(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#endif +{ + return(malloc(iSize)); +} + +// dll-friendly DirtyMemFree +#if !defined(DIRTYCODE_DLL) +void DirtyMemFree(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#else +void DirtyMemFree2(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#endif +{ + free(pMem); +} + +// usage: ssltest [options] +int main(int32_t argc, const char *argv[]) +{ + int32_t iCount, iLen, iState, iTimeout; + ProtoHttpRefT *pHttpRef; + char strBuffer[16*1024]; + const char **pServers; + SsltestOptionsT SsltestOptions, *pOptions = &SsltestOptions; + SsltestStateT SsltestState, *pState = &SsltestState; + + #if defined(DIRTYCODE_DLL) + DirtyMemFuncSet(&DirtyMemAlloc2, &DirtyMemFree2); + #endif + + // hook into zprintf output + ZPrintfHook(ssltest_zprintf_hook, NULL); + + // clear state and options settings + ds_memclr(pState, sizeof(*pState)); + ds_memclr(pOptions, sizeof(*pOptions)); + + // get options + pServers = process_args(pState, pOptions, argc, argv); + + // init test state + pState->iTest = pOptions->iMinTest; + pState->iServ = pOptions->iMinServ; + pState->iVers = pOptions->iMinVers; + pState->iCiph = pOptions->iMinCiph; + + // start dirtysock + if (!network_startup()) + { + return(-1); + } + + // setup http module + pHttpRef = ProtoHttpCreate(4096); + + // just keep working + for (iCount = 0, iState = SSLTEST_STAGE_SETUP, iLen = -1, iTimeout = NetTick()-1; ; ) + { + const SsltestTestT *pTest = &_ssltest_tests[pState->iTest]; + + // see if its time to query + if ((iTimeout != 0) && (NetTickDiff(NetTick(), iTimeout) >= 0)) + { + // set up for test + if (iState == SSLTEST_STAGE_SETUP) + { + ZPrintf("ssltest: running %s test\n", pTest->pTestName); + } + pTest->pTestFunc(pHttpRef, &SsltestOptions, pState, iState); + + // see if we're at the end of our test + if (pState->iServ >= pOptions->iMaxServ) + { + // go to next test + pTest = &_ssltest_tests[++pState->iTest]; + // see if we're done with our tests + if (pState->iTest >= pOptions->iMaxTest) + { + break; + } + // reset test state + pState->iServ = pOptions->iMinServ; + pState->iVers = pOptions->iMinVers; + pState->iCiph = pOptions->iMinCiph; + // move to setup + iState = SSLTEST_STAGE_SETUP; + continue; + } + else + { + iState = SSLTEST_STAGE_RUNNING; + } + + // if TLS1.0 or TLS1.1, exclude ciphers that were introduced after TLS1.1 + if ((_ssltest_versions[pState->iVers] < PROTOSSL_VERSION_TLS1_2) && (pState->iCiph > SSLTEST_MAXCIPHER_PRE12) && (pState->iCiph != SSLTEST_CIPHER_ALL)) + { + continue; + } + // if TLS1.2, exclude ciphers that were introduced for TLS1.3 + if ((_ssltest_versions[pState->iVers] < PROTOSSL_VERSION_TLS1_3) && (pState->iCiph > SSLTEST_MAXCIPHER_PRE13) && (pState->iCiph != SSLTEST_CIPHER_ALL)) + { + continue; + } + // if TLS1.3, exclude ciphers that predate TLS1.3 + if ((_ssltest_versions[pState->iVers] == PROTOSSL_VERSION_TLS1_3) && (pState->iCiph <= SSLTEST_MAXCIPHER_PRE13) && (pState->iCiph != SSLTEST_CIPHER_ALL)) + { + continue; + } + + // set ssl options + ProtoHttpControl(pHttpRef, 'vers', _ssltest_versions[pState->iVers], 0, NULL); + ProtoHttpControl(pHttpRef, 'ncrt', pOptions->bNcrt, 0, NULL); + + // set client cert if specified + if (pState->pClientCert != NULL) + { + ProtoHttpControl(pHttpRef, 'scrt', pState->iCertSize, 0, pState->pClientCert); + } + // set client key if specified + if (pState->pClientKey != NULL) + { + ProtoHttpControl(pHttpRef, 'skey', pState->iKeySize, 0, pState->pClientKey); + } + + // set http options + ProtoHttpControl(pHttpRef, 'keep', 0, 0, NULL); + + // run the test + iTimeout = NetTick(); + if (!pOptions->bSkip) + { + ProtoHttpGet(pHttpRef, pServers[pState->iServ], FALSE); + iTimeout += 30*1000; + } + iLen = 0; + iCount += 1; // count the attempt + } + + if (!pOptions->bSkip) + { + // update protohttp + ProtoHttpUpdate(pHttpRef); + + // read incoming data into buffer + if ((iLen = ProtoHttpRecvAll(pHttpRef, strBuffer, sizeof(strBuffer) - 1)) != PROTOHTTP_RECVWAIT) + { + char strFailInfo[256] = ""; + int32_t iVers = pState->iVers; + if ((iLen > 0) || (iLen == PROTOHTTP_RECVBUFF)) + { + pTest->pTestFunc(pHttpRef, &SsltestOptions, &SsltestState, SSLTEST_STAGE_RESULT); + ZPrintf("ssltest: [success] version=%s cipher=%s server=%s (%s)\n", _ssltest_tlsnames[iVers], _ssltest_ciphernames[pState->iCiph], pServers[pState->iServ], pState->strResult); + } + else + { + fail_check(pHttpRef, iLen, strFailInfo, sizeof(strFailInfo)); + ZPrintf("ssltest: [failure] version=%s cipher=%s server=%s (%s)\n", _ssltest_tlsnames[iVers], _ssltest_ciphernames[pState->iCiph], pServers[pState->iServ], strFailInfo); + } + pTest->pTestFunc(pHttpRef, &SsltestOptions, &SsltestState, SSLTEST_STAGE_COMPLETE); + iTimeout = NetTick() - 1; + } + } + else + { + ZPrintf("ssltest: [skipped] version=%s cipher=%s server=%s\n", _ssltest_tlsnames[pState->iVers], _ssltest_ciphernames[pState->iCiph], pServers[pState->iServ]); + pTest->pTestFunc(pHttpRef, &SsltestOptions, &SsltestState, SSLTEST_STAGE_COMPLETE); + } + + // sleep a bit + NetConnSleep(10); + } + + ZPrintf("ssltest: done (%d tests)\n", iCount); + + // shut down HTTP + ProtoHttpDestroy(pHttpRef); + + // disconnect from the network + NetConnDisconnect(); + + // shutdown the network connections && destroy the dirtysock code + NetConnShutdown(FALSE); + return(0); +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/club/club1.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/data/club/club1.txt new file mode 100644 index 00000000..cb3d5dfa --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/data/club/club1.txt @@ -0,0 +1,89 @@ +#sign on an create a club +club connect sdevlobby01.online.ea.com 11900 csoc030 csotest csoc030 +club create fooclub tag30 UK 1 2 3 +club recruit csoc031 +club recruit csoc032 +club recruit csoc033 +club recruit csoc034 +club recruit csoc035 +club recruit csoc036 + +sleep 30 + +#recruitee accepts +club connect sdevlobby01.online.ea.com 11900 csoc031 csotest +sleep 10 +# Find with no params, will find my recruited clubs.. +club find +#note: accept also takes a clubid param, but, to make it easy, tester2 stores id from find above. +club accept +sleep 30 + +#another dude accepts +club connect sdevlobby01.online.ea.com 11900 csoc032 csotest +sleep 10 +club find +club accept +sleep 30 + +#just say no +club connect sdevlobby01.online.ea.com 11900 csoc033 csotest +sleep 10 +club find +club reject +sleep 30 + +#another dude accepts +club connect sdevlobby01.online.ea.com 11900 csoc034 csotest +sleep 10 +club find +club accept +sleep 30 + +# now, GM can transfer to member, who accepted was recruit above.. +club connect sdevlobby01.online.ea.com 11900 csoc031 csotest +sleep 10 +club get +club transfer csoc032 + +# now, transferee can accept transfer .. +club connect sdevlobby01.online.ea.com 11900 csoc031 csotest +sleep 10 +club get +club acceptX + +#new gm should be able to recruit.. +club recruit csoc038 + +#report some scores.. NOTE fiso001 must belong to another club to see stats update.. +club score csoc032 1 fiso001 3 +club get + + + +club connect sdevlobby01.online.ea.com 11900 csoc035 csotest +sleep 10 +club find +club accept +sleep 30 + +club connect sdevlobby01.online.ea.com 11900 csoc038 csotest +sleep 10 +club find +club accept +sleep 30 + +#this will fail --not the gm +club connect sdevlobby01.online.ea.com 11900 csoc030 csotest +sleep 10 +club find tag30 T +club disband +sleep 30 + + + + + + + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/http/rsa1000bitCA.pem b/r5dev/thirdparty/dirtysdk/sample/tester2/data/http/rsa1000bitCA.pem new file mode 100644 index 00000000..8125dc28 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/data/http/rsa1000bitCA.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD +VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0 +MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxIDAeBgNV +BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2Vy +dmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUAA4GJ +ADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII +0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphI +uR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZI +hvcNAQECBQADfgBl3X7hsuyw4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3 +YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc +1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA== +-----END CERTIFICATE----- + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/http/thawteCp.pem b/r5dev/thirdparty/dirtysdk/sample/tester2/data/http/thawteCp.pem new file mode 100644 index 00000000..51285e33 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/data/http/thawteCp.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx +FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD +VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy +dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t +MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB +MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG +A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp +b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl +cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE +VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ +ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR +uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI +hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM +pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/readme.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/readme.txt new file mode 100644 index 00000000..cd932674 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/readme.txt @@ -0,0 +1,9 @@ +Files in this directory are input for the 'utf8' command in the tester application. + +Filename Format Description +~~~~~~~~~~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +utf8test00.txt UTF-8 Middle English text +utf8test01.txt UTF-8 Middle High German +utf8test02.txt UTF-8 "I can eat glass and it doesn't hurt me" in 29 languages +ucs2test00.txt UCS-2 "You have been ejected from the game" in FIFA's supported languages +ucs2test01.txt UCS-2 More FIFA text \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/ucs2test00.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/ucs2test00.txt new file mode 100644 index 00000000..876c5c8a Binary files /dev/null and b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/ucs2test00.txt differ diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/ucs2test01.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/ucs2test01.txt new file mode 100644 index 00000000..fad08cab Binary files /dev/null and b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/ucs2test01.txt differ diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test00.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test00.txt new file mode 100644 index 00000000..d348f546 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test00.txt @@ -0,0 +1,7 @@ +From Laȝamon's The Chronicles of England, Middle English, West Midlands: + +An preost wes on leoden, Laȝamon was ihoten +He wes Leovenaðes sone -- liðe him be Drihten. +He wonede at Ernleȝe at æðelen are chirechen, +Uppen Sevarne staþe, sel þar him þuhte, +Onfest Radestone, þer he bock radde. \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test01.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test01.txt new file mode 100644 index 00000000..6acb8489 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test01.txt @@ -0,0 +1,9 @@ +From the Tagelied of Wolfram von Eschenbach (Middle High German): +Sîne klâwen durh die wolken sint geslagen, +er stîget ûf mit grôzer kraft, +ich sih in grâwen tägelîch als er wil tagen, +den tac, der im geselleschaft +erwenden wil, dem werden man, +den ich mit sorgen în verliez. +ich bringe in hinnen, ob ich kan. +sîn vil manegiu tugent michz leisten hiez. diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test02.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test02.txt new file mode 100644 index 00000000..4928614d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/data/utf8/utf8test02.txt @@ -0,0 +1,29 @@ +English: I can eat glass and it doesn't hurt me. +Latin: Vitrum edere possum; mihi non nocet. +Esperanto: Mi povas manĝi vitron, ĝi ne damaĝas min. +French: Je peux manger du verre, ça ne me fait pas de mal. +Provençal / Occitan: Pòdi manjar de veire, me nafrariá pas. +Québécois: J'peux manger d'la vitre, ça m'fa pas mal. +Walloon: Dji pou magnî do vêre, çoula m' freut nén må. +Basque: Kristala jan dezaket, ez dit minik ematen. +Catalan: Puc menjar vidre que no em fa mal. +Spanish: Puedo comer vidrio, no me hace daño. +Aragones: Puedo minchar beire, no me'n fa mal . +Galician: Eu podo xantar cristais e non cortarme. +Portuguese: Posso comer vidro, não me faz mal. +Brazilian Portuguese: Consigo comer vidro. Não me machuca. +Cabo Verde Creole: M' podê cumê vidru, ca ta maguâ-m'. +Italian: Posso mangiare il vetro e non mi fa male. +Roman: Me posso magna' er vetro, e nun me fa male. +Sicilian: Puotsu mangiari u vitru, nun mi fa mali. +Milanese: Sôn bôn de magnà el véder, el me fa minga mal. +Venetian: Mi posso magnare el vetro, no'l me fa mae. +Romanian: Pot să mănânc sticlă și ea nu mă rănește. +Cornish: Mý a yl dybry gwéder hag éf ny wra ow ankenya. +Welsh: Dw i'n gallu bwyta gwydr, 'dyw e ddim yn gwneud dolur i mi. +Manx Gaelic: Foddym gee glonney agh cha jean eh gortaghey mee. +Old Irish (Latin): Con·iccim ithi nglano. Ním·géna. +Irish: Is féidir liom gloinne a ithe. Ní dhéanann sí dochar ar bith dom. +Scottish Gaelic: S urrainn dhomh gloinne ithe; cha ghoirtich i mi. +Anglo-Saxon (Latin): Ic mæg glæs eotan ond hit ne hearmiað me. +Middle English: Ich canne glas eten and hit hirtiþ me nouȝt. diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/testhttpsrv/readme.txt b/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/testhttpsrv/readme.txt new file mode 100644 index 00000000..481bb8a6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/testhttpsrv/readme.txt @@ -0,0 +1,22 @@ +tw.py is a python script to act as a test server to simulate the conditions below. The syntax is python tw.py --ip --handler . +By default the server will listen on port 9000. + +1. Server responds normally to request. +python tw.py --ip 10.30.80.175 --handler normal + +2. Server is not responding to requests. +python tw.py --ip 10.30.80.175 --handler noresponse + +The server will not respond with a proper HTTP response. + +3. Server is responding slowly to requests. +python tw.py --ip 10.30.80.175 --handler slow + +Server will respond in 30s + +4. Server is responding with a certain HTTP error code. +python tw.py --ip 10.30.80.175 --handler error --error 403 + +5. Server is not listening on a port at all. +Don't start the server. + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/testhttpsrv/tw.py b/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/testhttpsrv/tw.py new file mode 100644 index 00000000..f96c11b3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/testhttpsrv/tw.py @@ -0,0 +1,113 @@ +from SocketServer import ThreadingMixIn +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +import sys +import time +import optparse +import os +import uuid + +errorcode = 404 +tmp_dir = 'twtmp' + +class NormalHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.do_all() + + def do_POST(self): + global tmp_dir + length = long(self.headers.getheader('content-length')) + chunk = self.rfile.read(length) + + fname = os.path.join(tmp_dir, str(uuid.uuid1())) + fout = file(fname, 'wb') + fout.write(chunk) + fout.close() + + fsize = os.path.getsize(fname) + + self.do_all([fname, str(fsize)]) + + def do_HEAD(self): + self.do_all() + + def do_PUT(self): + self.do_all(); + + def do_DELETE(self): + self.do_all(); + + def do_all(self, response=[]): + self.send_response(200) + self.send_header('Content-type','text/plain') + self.end_headers() + self.wfile.write(self.path+'\n') + for r in response: + self.wfile.write(r+'\n') + +class NoResponseHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.do_all() + + def do_POST(self): + self.do_all() + + def do_all(self): + pass + +class SlowResponseHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.do_all() + + def do_POST(self): + self.do_all() + + def do_all(self): + time.sleep(30) + self.send_response(200) + self.send_header('Content-type','text/plain') + self.end_headers() + self.wfile.write(self.path) + +class ErrorResponseHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.do_all() + + def do_POST(self): + self.do_all() + + def do_all(self): + global errorcode + self.send_response(errorcode) + self.send_header('Content-type','text/plain') + self.end_headers() + self.wfile.write(self.path) + +class ThreadingHTTPServer(ThreadingMixIn, HTTPServer): + pass + +handlers = {'normal':NormalHandler, 'noresponse':NoResponseHandler, 'slow':SlowResponseHandler, 'error':ErrorResponseHandler} + +parser = optparse.OptionParser(usage = '%prog --ip=IP --port=PORT --handler=HANDLER --error=ERROR') +parser.add_option('-i', '--ip', dest='ip', + help='listen on ip', metavar='IP') +parser.add_option('-p', '--port', dest='port', type=int, default=9000, + help='listen on port PORT', metavar='PORT') +parser.add_option('-l', '--handler', dest='handler', + help='how to respond', metavar='HANDLER') +parser.add_option('-e', '--error', dest='error', type=int, + help='what error to respond with', metavar='ERROR') + +opts, args = parser.parse_args() + +ip = opts.ip +port = opts.port +handler = opts.handler +if opts.error: + errorcode = opts.error + +if not os.path.exists(tmp_dir): + os.mkdir(tmp_dir) + + +server = ThreadingHTTPServer((ip, port), handlers[handler]) +server.serve_forever() diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/xexconfig.xml b/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/xexconfig.xml new file mode 100644 index 00000000..bd21979a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/scripts/xexconfig.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm.c new file mode 100644 index 00000000..5ac2fb5d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm.c @@ -0,0 +1,329 @@ +/*H********************************************************************************/ +/*! + \File testercomm.c + + \Description + This module provides a communication layer between the host and the client. + Typical operations are SendLine() and GetLine(), which send and receive + lines of text, commands, debug output, etc. Each platform will implement + its own way of communicating through files, debugger API calls, etc. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/23/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include "DirtySDK/dirtysock.h" +#include "libsample/zmem.h" +#include "libsample/zlib.h" +#include "libsample/zlist.h" +#include "testerregistry.h" +#include "testercomm.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterCommCreate + + \Description + Create a tester host client communication module. + + \Input None + + \Output TesterCommT * - pointer to allocated module + + \Version 03/23/2005 (jfrank) +*/ +/********************************************************************************F*/ +TesterCommT *TesterCommCreate(void) +{ + TesterCommT *pState; + + // create the module + pState = (TesterCommT *)ZMemAlloc(sizeof(TesterCommT)); + ds_memclr((void *)pState, sizeof(TesterCommT)); + pState->pInterface = (TesterCommInterfaceT *)ZMemAlloc(sizeof(TesterCommInterfaceT)); + ds_memclr((void *)pState->pInterface, sizeof(TesterCommInterfaceT)); + + // create the lists + pState->pInputData = ZListCreate(TESTERCOMM_NUMCOMMANDS_MAX, sizeof(TesterCommDataT)); + pState->pOutputData = ZListCreate(TESTERCOMM_NUMCOMMANDS_MAX, sizeof(TesterCommDataT)); + + // add this module to the registry + TesterRegistrySetPointer("COMM", pState); + + // return the pointer + return(pState); +} + + +/*F********************************************************************************/ +/*! + \Function TesterCommMessage + + \Description + Send a message to the other side + + \Input *pState - module state + \Input iMsgType - type of message to send (TESTER_MSGTYPE_XXX) + \Input *pMsgText - text of the message to send + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterCommMessage(TesterCommT *pState, int32_t iMsgType, const char *pMsgText) +{ + // check for error conditions + if ((pState == NULL) || + (iMsgType <= 0) || + (iMsgType >= TESTER_MSGTYPE_MAX) || + (pMsgText == NULL)) + { + return(-1); + } + + ds_strnzcpy(pState->Message.strBuffer, pMsgText, sizeof(pState->Message.strBuffer)); + pState->Message.iType = iMsgType; + return(ZListPushBack(pState->pOutputData, &pState->Message)); +} + + +/*F********************************************************************************/ +/*! + \Function TesterCommRegister + + \Description + Register a callback for a particular type of message + + \Input *pState - module state + \Input iMsgType - type of message register for (TESTER_MSGTYPE_XXX) + \Input *pCallback - callback to call when the message comes in + \Input *pParam - user pointer to pass to the callback + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterCommRegister(TesterCommT *pState, int32_t iMsgType, TesterCommMsgCbT *pCallback, void *pParam) +{ + // check error conditions + if ((pState == NULL) || + (iMsgType <= 0) || + (iMsgType >= TESTER_MSGTYPE_MAX)) + { + return(-1); + } + + // now register the callback + pState->MessageMap[iMsgType] = pCallback; + pState->pMessageMapUserData[iMsgType] = pParam; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function TesterCommStatus + + \Description + Get module status. + + \Input *pState - module state + \Input iSelect - status selector + \Input iValue - selector specific + + \Output int32_t - selector specific + + \Version 10/31/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t TesterCommStatus(TesterCommT *pState, int32_t iSelect, int32_t iValue) +{ + if (iSelect == 'inpt') + { + return(pState->bGotInput); + } + // unhandled + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function TesterCommSuspend + + \Description + Suspend the comm module. Service the files but don't + execute any commands until the Wake function is called, + + \Input *pState - pointer to host client comm module + + \Output None + + \Version 04/07/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterCommSuspend(TesterCommT *pState) +{ + if(pState) + pState->uSuspended = 1; +} + + +/*F********************************************************************************/ +/*! + \Function TesterCommWake + + \Description + Wake the comm module to begin processing commands again. + + \Input *pState - pointer to host client comm module + + \Output None + + \Version 04/07/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterCommWake(TesterCommT *pState) +{ + if(pState) + pState->uSuspended = 0; +} + + +/*F********************************************************************************/ +/*! + \Function TesterCommDestroy + + \Description + Destroy a tester host client communication module. + + \Input *pState - pointer to host client comm module + + \Output None + + \Version 03/23/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterCommDestroy(TesterCommT *pState) +{ + if (pState) + { + // disconnect first, just in case + TesterCommDisconnect(pState); + ZListDestroy(pState->pInputData); + ZListDestroy(pState->pOutputData); + + // delete this item from the registry + TesterRegistrySetPointer("COMM", NULL); + + // now dump the memory + ZMemFree(pState->pInterface->pData); + ZMemFree(pState->pInterface); + ZMemFree(pState); + } +} + + +/*F********************************************************************************/ +/*! + \Function TesterCommConnect + + \Description + Connect the host client communication module. + + \Input *pState - pointer to host client comm module + \Input *pParams - startup parameters + \Input bIsHost - TRUE=host, FALSE=client + + \Output int32_t - 0 for success, error code otherwise + + \Version 05/02/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterCommConnect(TesterCommT *pState, const char *pParams, uint32_t bIsHost) +{ + // check for errors + if (pState == NULL) + return(-1); + if (pState->pInterface == NULL) + return(-1); + if (pState->pInterface->CommConnectFunc == NULL) + return(-1); + + return(pState->pInterface->CommConnectFunc(pState, pParams, bIsHost)); +} + +/*F********************************************************************************/ +/*! + \Function TesterCommUpdate + + \Description + Give the host/client interface module some processor time. Call this + once in a while to pump the input and output pipes. + + \Input *pState - module state + + \Output int32_t - 0 for success, error code otherwise + + \Version 05/02/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterCommUpdate(TesterCommT *pState) +{ + // check for errors + if (pState == NULL) + return(-1); + if (pState->pInterface == NULL) + return(-1); + if (pState->pInterface->CommUpdateFunc == NULL) + return(-1); + + return(pState->pInterface->CommUpdateFunc(pState)); +} + + +/*F********************************************************************************/ +/*! + \Function TesterCommDisconnect + + \Description + Disconnect the host client communication module. + + \Input *pState - pointer to host client comm module + + \Output int32_t - 0=success, error code otherwise + + \Version 05/02/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterCommDisconnect(TesterCommT *pState) +{ + // check for errors + if (pState == NULL) + return(-1); + if (pState->pInterface == NULL) + return(-1); + if (pState->pInterface->CommDisconnectFunc == NULL) + return(-1); + + return(pState->pInterface->CommDisconnectFunc(pState)); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm_file.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm_file.c new file mode 100644 index 00000000..cb3dcb14 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm_file.c @@ -0,0 +1,543 @@ +/*H********************************************************************************/ +/*! + \File testercomm_file.c + + \Description + This module provides a communication layer between the host and the client. + Typical operations are SendLine() and GetLine(), which send and receive + lines of text, commands, debug output, etc. Each platform will implement + its own way of communicating through files, debugger API calls, etc. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/23/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/util/base64.h" +#include "DirtySDK/util/jsonformat.h" +#include "DirtySDK/util/jsonparse.h" +#include "libsample/zmem.h" +#include "libsample/zlib.h" +#include "libsample/zlist.h" +#include "libsample/zfile.h" +#include "testerprofile.h" +#include "testerregistry.h" +#include "testercomm.h" + +/*** Defines **********************************************************************/ + +#define TESTERCOMM_FILEPATHSIZE_DEFAULT (256) //!< path/file storage +#define TESTERCOMM_PATH_DEFAULT (TESTERPROFILE_CONTROLDIR_VALUEDEFAULT) //!< default path for control files + +#define TESTERCOMM_OUTPUTTIMEOUT_DEFAULT (60*1000) //!< timeout (ms) for a output message + +#define TESTERCOMM_FILEBUFFERSIZE (4096) //!< size of temp file buffer to use + +#define TESTERCOMM_FILEPROFILING (0) //! turn this on to see times for file writing + +/*** Type Definitions *************************************************************/ + +typedef struct TesterCommFileT TesterCommFileT; +struct TesterCommFileT +{ + // file specific stuff + char strControlDir[TESTERPROFILE_CONTROLDIR_SIZEDEFAULT]; //!< location of control directory + char strTempPathFile[TESTERCOMM_FILEPATHSIZE_DEFAULT]; //!< temporary output filename + char strInputPathFile [TESTERCOMM_FILEPATHSIZE_DEFAULT]; //!< input file filename + char strOutputPathFile[TESTERCOMM_FILEPATHSIZE_DEFAULT]; //!< output file filename +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _TesterCommCheckInput + + \Description + Check for data coming from the other side (host or client) and pull + any data into our internal buffer, if possible. + + \Input *pState - pointer to host client comm module + + \Output int32_t - 0 = no data, >0 = data, error code otherwise + + \Version 03/24/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommCheckInput(TesterCommT *pState) +{ + TesterCommFileT *pInterfaceData; + char *pInputData; + const char *pInputLoop; + int32_t iInputDataSize; + ZFileT iInputFile; + uint16_t aJson[16*1024]; + + if ((pState == NULL) || (pState->pInterface->pData == NULL)) + { + return(-1); + } + pInterfaceData = pState->pInterface->pData; + + // check to make sure our filename is sane + if (strlen(pInterfaceData->strInputPathFile) == 0) + { + return(-1); + } + + // open the input file first + iInputFile = ZFileOpen(pInterfaceData->strInputPathFile, ZFILE_OPENFLAG_RDONLY | ZFILE_OPENFLAG_APPEND); + + if (iInputFile == ZFILE_INVALID) + { + // could not open it - nothing there - not necessarily an error + return(-1); + } + + // get the file size + iInputDataSize = (int32_t)ZFileSize(iInputFile); + + // close the file + ZFileClose(iInputFile); + + // check for empty file + if (iInputDataSize == 0) + { + // delete the file + ZFileDelete(pInterfaceData->strInputPathFile); + // and quit + return(0); + } + + // load the contents of the file into the buffer + pInputData = ZFileLoad(pInterfaceData->strInputPathFile, &(iInputDataSize), 0); + JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pInputData, iInputDataSize); + + // and delete the file + ZFileDelete(pInterfaceData->strInputPathFile); + + if (pInputData == NULL) + { + // no data to process + return(0); + } + + // remember that we got input + pState->bGotInput = TRUE; + + // push each line onto the list + iInputDataSize = 0; + while ((pInputLoop = JsonFind2(aJson, NULL, "msg[", iInputDataSize)) != NULL) + { + char strBuffer[sizeof(pState->LineData.strBuffer)]; + int32_t iBufLen; + + ds_memclr(&pState->LineData, sizeof(pState->LineData)); + pState->LineData.iType = (int32_t)JsonGetInteger(JsonFind2(aJson, pInputLoop, ".TYPE", iInputDataSize), 0); + iBufLen = JsonGetString(JsonFind2(aJson, pInputLoop, ".TEXT", iInputDataSize), strBuffer, sizeof(strBuffer), ""); + Base64Decode3(strBuffer, iBufLen, pState->LineData.strBuffer, sizeof(pState->LineData.strBuffer)); + // add to the back of the list + // remember to add one for the terminator + ZListPushBack(pState->pInputData, &pState->LineData); + + iInputDataSize += 1; + } + + // kill the file memory we used to store the temp input + ZMemFree(pInputData); + + // done - return how much we got from the file + return(iInputDataSize); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterCommCheckOutput + + \Description + Check and send data from the output buffer, if possible. + + \Input *pState - pointer to host client comm module + + \Output int32_t - 0=success, error code otherwise + + \Version 03/24/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommCheckOutput(TesterCommT *pState) +{ + TesterCommFileT *pInterfaceData; + ZFileStatT FileStat; + ZFileT iTempFile; + int32_t iResult; + int32_t iLineLength=0; +#if TESTERCOMM_FILEPROFILING + int32_t iTime1, iTime2; +#endif + + // file writing stuff + const char *pFileBuf; + + // check for error conditions + if ((pState == NULL) || (pState->pInterface->pData == NULL)) + { + return(-1); + } + pInterfaceData = pState->pInterface->pData; + + // see if there's any data to send + if (ZListPeekFront(pState->pOutputData) == NULL) + { + // reset the last send time - we're not timing out because there's no data + pState->uLastSendTime = ZTick(); + return(0); + } + + // see if we've timed out + if (ZTick() - pState->uLastSendTime > TESTERCOMM_OUTPUTTIMEOUT_DEFAULT) + { + // timeout occurred - dump all pending messages + // and pop a warning + ZListClear(pState->pOutputData); + ZPrintf("testercomm: TIMEOUT in sending data. List cleared. Comms Lost?\n"); + } + + // we've got data to send in the output list + // see if we can send it in the file + iResult = ZFileStat(pInterfaceData->strOutputPathFile, &FileStat); + + // only do something if the file doesn't exist + // so quit if the NOSUCHFILE isn't set + if (iResult != ZFILE_ERROR_NOSUCHFILE) + return(0); + + // open the file + iTempFile = ZFileOpen(pInterfaceData->strTempPathFile, ZFILE_OPENFLAG_WRONLY | ZFILE_OPENFLAG_CREATE); + if (iTempFile == ZFILE_INVALID) + { + // could not open it - error condition + return(-1); + } + +#if TESTERCOMM_FILEPROFILING + printf("--**-- [%12d] Starting get from buffer to file \n", ZTick()); +#endif + JsonInit(pState->strCommand, sizeof(pState->strCommand), 0); + JsonArrayStart(pState->strCommand, "msg"); + while(ZListPeekFront(pState->pOutputData)) + { + char strBuffer[sizeof(pState->LineData.strBuffer)]; + + // snag the data into the local buffer + ZListPopFront(pState->pOutputData, &pState->LineData); + + // create the stuff to write + JsonObjectStart(pState->strCommand, NULL); + JsonAddInt(pState->strCommand, "TYPE", pState->LineData.iType); + Base64Encode2(pState->LineData.strBuffer, (int32_t)strlen(pState->LineData.strBuffer), strBuffer, sizeof(strBuffer)); + if ((iResult = JsonAddStr(pState->strCommand, "TEXT", strBuffer)) == JSON_ERR_FULL) + { + // Sometimes, the text we get in will be too big. Make sure this is an easy error to diagnose. + printf("Text too large to send in TESTERCOMM_COMMANDSIZE_DEFAULT. Discarding.\n"); + } + JsonObjectEnd(pState->strCommand); + } + JsonArrayEnd(pState->strCommand); + pFileBuf = JsonFinish(pState->strCommand); + iLineLength = (int32_t)strlen(pFileBuf); + + // write the last little chunk + if (iLineLength > 0) + { +#if TESTERCOMM_FILEPROFILING + iTime1 = ZTick(); +#endif + ZFileWrite(iTempFile, (void *)pFileBuf, iLineLength); +#if TESTERCOMM_FILEPROFILING + iTime2 = ZTick(); + printf("------ [%12d] Writing [%d] bytes to file took [%d] Ticks.\n", + ZTick(), iWrittenSize, iTime2 - iTime1); +#endif + } + +#if TESTERCOMM_FILEPROFILING + printf("--**-- [%12d] Done pushing from buffer to file \n", ZTick()); +#endif + + // close the temp file + ZFileClose(iTempFile); + iTempFile = 0; +#if TESTERCOMM_FILEPROFILING + printf("--**-- [%12d] File closed. \n", ZTick()); +#endif + + // move the temp file into the real file + iResult = ZFileRename(pInterfaceData->strTempPathFile, pInterfaceData->strOutputPathFile); + if (iResult != 0) + { + NetPrintf(("testercomm: error [%d=0x%X] renaming [%s] to [%s]\n", + iResult, iResult, pInterfaceData->strTempPathFile, pInterfaceData->strOutputPathFile)); + } +#if TESTERCOMM_FILEPROFILING + printf("--**-- [%12d] File renamed. \n", ZTick()); +#endif + + // mark the last send time + pState->uLastSendTime = ZTick(); + + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterCommConnect + + \Description + Connect the host client communication module. + + \Input *pState - pointer to host client comm module + \Input *pParams - startup parameters + \Input bIsHost - TRUE=host, FALSE=client + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/23/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommConnect(TesterCommT *pState, const char *pParams, uint32_t bIsHost) +{ + TesterCommFileT *pInterfaceData; + uint32_t uControlDirSize; + uint32_t uTempPathFileSize; + uint32_t uInputPathFileSize; + uint32_t uOutputPathFileSize; + char strInputFile[256]; + char strOutputFile[256]; + char strControlDir[256]; + uint16_t aJson[512]; + + // check for error conditions + if ((pState == NULL) || (pParams == NULL) || (pState->pInterface->pData == NULL)) + { + ZPrintf("testercomm: connect got invalid NULL pointer - pState [0x%X] pParams [0x%X]\n", pState, pParams); + return(-1); + } + pInterfaceData = pState->pInterface->pData; + + // get the necessary startup parameters + JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pParams, -1); + ds_memclr(strInputFile, sizeof(strInputFile)); + ds_memclr(strOutputFile, sizeof(strOutputFile)); + ds_memclr(strControlDir, sizeof(strControlDir)); + JsonGetString(JsonFind(aJson, "INPUTFILE"), strInputFile, sizeof(strInputFile), ""); + JsonGetString(JsonFind(aJson, "OUTPUTFILE"), strOutputFile, sizeof(strOutputFile), ""); + JsonGetString(JsonFind(aJson, "CONTROLDIR"), strControlDir, sizeof(strControlDir), ""); + + // check for more error conditions + if ((strlen(strInputFile) == 0) || (strlen(strOutputFile) == 0)) + { + ZPrintf("testercomm: connect got invalid startup paths - INPUTFILE [%s] OUTPUTFILE [%s] CONTROLDIR [%s}\n", + strInputFile, strOutputFile, strControlDir); + return(-1); + } + + // wipe out the current stored strings + ds_memclr((void *)pInterfaceData->strControlDir, sizeof(pInterfaceData->strControlDir)); + ds_memclr((void *)pInterfaceData->strTempPathFile, sizeof(pInterfaceData->strTempPathFile)); + ds_memclr((void *)pInterfaceData->strInputPathFile, sizeof(pInterfaceData->strInputPathFile)); + ds_memclr((void *)pInterfaceData->strOutputPathFile, sizeof(pInterfaceData->strOutputPathFile)); + + // get the available string size + uControlDirSize = sizeof(pInterfaceData->strControlDir)-1; + uTempPathFileSize = sizeof(pInterfaceData->strTempPathFile)-1; + uInputPathFileSize = sizeof(pInterfaceData->strInputPathFile)-1; + uOutputPathFileSize = sizeof(pInterfaceData->strOutputPathFile)-1; + + // if we got nothing in for the control directory, use the default + if (strlen(strControlDir) == 0) + { + strncat(pInterfaceData->strControlDir, TESTERCOMM_PATH_DEFAULT, uControlDirSize); + strncat(pInterfaceData->strTempPathFile, TESTERCOMM_PATH_DEFAULT, uTempPathFileSize); + strncat(pInterfaceData->strInputPathFile, TESTERCOMM_PATH_DEFAULT, uInputPathFileSize); + strncat(pInterfaceData->strOutputPathFile, TESTERCOMM_PATH_DEFAULT, uOutputPathFileSize); + } + else + { + strncat(pInterfaceData->strControlDir, strControlDir, uControlDirSize); + strncat(pInterfaceData->strTempPathFile, strControlDir, uTempPathFileSize); + strncat(pInterfaceData->strInputPathFile, strControlDir, uInputPathFileSize); + strncat(pInterfaceData->strOutputPathFile, strControlDir, uOutputPathFileSize); + } + + // subtract off the amount of size we've used + uTempPathFileSize -= (uint32_t)strlen(pInterfaceData->strTempPathFile); + uInputPathFileSize -= (uint32_t)strlen(pInterfaceData->strInputPathFile); + uOutputPathFileSize -= (uint32_t)strlen(pInterfaceData->strOutputPathFile); + + // attach the paths + if (strlen(strControlDir) > 0) + { + strncat(pInterfaceData->strTempPathFile, "/", uTempPathFileSize--); + strncat(pInterfaceData->strInputPathFile, "/", uInputPathFileSize--); + strncat(pInterfaceData->strOutputPathFile, "/", uOutputPathFileSize--); + } + + // attach an extra char for the tempfile. + strncat(pInterfaceData->strTempPathFile, "_", uTempPathFileSize--); + + // attach the filename + strncat(pInterfaceData->strTempPathFile, strOutputFile, uTempPathFileSize); + strncat(pInterfaceData->strInputPathFile, strInputFile, uInputPathFileSize); + strncat(pInterfaceData->strOutputPathFile, strOutputFile, uOutputPathFileSize); + + // some debugging + ZPrintf("testercomm: connect ControlDir [%s]\n", pInterfaceData->strControlDir); + ZPrintf("testercomm: connect TempPathFile [%s]\n", pInterfaceData->strTempPathFile); + ZPrintf("testercomm: connect InputPathFile [%s]\n", pInterfaceData->strInputPathFile); + ZPrintf("testercomm: connect OutputPathFile [%s]\n", pInterfaceData->strOutputPathFile); + + // wipe out any existing output files at startup (remove any previous junk) + ZFileDelete(pInterfaceData->strTempPathFile); + ZFileDelete(pInterfaceData->strOutputPathFile); + + // dump anything from an incoming connection + ZFileDelete(pInterfaceData->strInputPathFile); + + // set the tick time so we don't automatically get a timeout + pState->uLastSendTime = ZTick(); + + // done for now + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterCommUpdate + + \Description + Give the host/client interface module some processor time. Call this + once in a while to pump the input and output pipes. + + \Input *pState - module state + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommUpdate(TesterCommT *pState) +{ + int32_t iResult; + + if (pState == NULL) + return(-1); + + // quit if we are suspended (don't do any more commands) + if (pState->uSuspended) + return(0); + + // check for outgoing and incoming data + _TesterCommCheckOutput(pState); + _TesterCommCheckInput(pState); + + // now call the callbacks for incoming messages + iResult = ZListPopFront(pState->pInputData, &pState->LineData); + while(iResult > 0) + { + // try to access the message map + if (pState->MessageMap[pState->LineData.iType] != NULL) + { + // protect against recursion by suspending commands until this one completes + TesterCommSuspend(pState); + (pState->MessageMap[pState->LineData.iType])(pState, pState->LineData.strBuffer, pState->pMessageMapUserData[pState->LineData.iType]); + TesterCommWake(pState); + } + + // try to get the next chunk + iResult = ZListPopFront(pState->pInputData, &pState->LineData); + } + + // done + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterCommDisconnect + + \Description + Disconnect the host client communication module. + + \Input *pState - pointer to host client comm module + + \Output int32_t - 0=success, error code otherwise + + \Version 03/23/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommDisconnect(TesterCommT *pState) +{ + TesterCommFileT *pData; + + // check for error conditions + if ((pState == NULL) || (pState->pInterface->pData == NULL)) + { + return(-1); + } + pData = pState->pInterface->pData; + + // wipe out the current strings + ds_memclr((void *)pData->strControlDir, sizeof(pData->strControlDir)); + ds_memclr((void *)pData->strTempPathFile, sizeof(pData->strTempPathFile)); + ds_memclr((void *)pData->strInputPathFile, sizeof(pData->strInputPathFile)); + ds_memclr((void *)pData->strOutputPathFile, sizeof(pData->strOutputPathFile)); + + // else return no error + return(0); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterCommAttachFile + + \Description + Attach file module function pointers to a tester comm module. + + \Input *pState - pointer to host client comm module + + \Output None + + \Version 05/02/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterCommAttachFile(TesterCommT *pState) +{ + if(pState == NULL) + return; + + ZPrintf("testercomm: attaching FILE interface methods\n"); + + pState->pInterface->CommConnectFunc = &_TesterCommConnect; + pState->pInterface->CommUpdateFunc = &_TesterCommUpdate; + pState->pInterface->CommDisconnectFunc = &_TesterCommDisconnect; + pState->pInterface->pData = (TesterCommFileT *)ZMemAlloc(sizeof(TesterCommFileT)); + ds_memclr(pState->pInterface->pData, sizeof(TesterCommFileT)); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm_socket.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm_socket.c new file mode 100644 index 00000000..9e5ee050 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/comm/testercomm_socket.c @@ -0,0 +1,606 @@ +/*H********************************************************************************/ +/*! + \File testercomm_socket.c + + \Description + This module provides a communication layer between the host and the client + using socket I/O. Typical operations are SendLine() and GetLine(), which + send and receive lines of text, commands, debug output, etc. Each platform + may implement its own way of communicating - through files, debugger API + calls, etc. + + \Copyright + Copyright 2011 Electronic Arts Inc. + + \Version 10/17/2011 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/netconn.h" // for NetConnSleep() +#include "DirtySDK/util/base64.h" +#include "DirtySDK/util/jsonformat.h" +#include "DirtySDK/util/jsonparse.h" +#include "libsample/zmem.h" +#include "libsample/zlib.h" +#include "libsample/zlist.h" +#include "libsample/zfile.h" + +#include "testerprofile.h" +#include "testerregistry.h" +#include "testercomm.h" + +/*** Defines **********************************************************************/ + +#define T2COMM_PORT (3232) //!< communications port +#define TESTERCOMM_OUTPUTTIMEOUT_DEFAULT (60*1000) //!< timeout (ms) for a output message + +/*** Type Definitions *************************************************************/ + +//! module state +typedef struct TesterCommSocketT +{ + SocketT *pSocket; //!< comm socket + SocketT *pListen; //!< listen socket used by host + int32_t iPort; //!< port to bind to + + char strHostName[TESTERPROFILE_HOSTNAME_SIZEDEFAULT]; //!< host address to connect to + enum + { + ST_INIT, + ST_CONN, + ST_LIST, + ST_OPEN, + ST_FAIL + }eState; //!< module state + uint8_t bIsHost; //!< TRUE if host else FALSE + char strInputData[16*1024]; //!< input buffer +} TesterCommSocketT; + +/*** Private Function Prototypes **************************************************/ + +static int32_t _TesterCommDisconnect(TesterCommT *pState); +static int32_t _TesterCommCheckInput(TesterCommT *pState); + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _TesterCommConnect2 + + \Description + Connect the host client communication module. + + \Input *pSocketState - module state + + \Output + int32_t - 0=success, else failure + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommConnect2(TesterCommSocketT *pSocketState) +{ + struct sockaddr SockAddr; + uint32_t uHostAddr; + int32_t iResult; + + // set up sockaddr + SockaddrInit(&SockAddr, AF_INET); + SockaddrInSetPort(&SockAddr, pSocketState->iPort); + + // if we have somewhere to connect to, do it + if (pSocketState->strHostName[0] != '\0') + { + // create the socket + if ((pSocketState->pSocket = SocketOpen(AF_INET, SOCK_STREAM, 0)) == NULL) + { + ZPrintf("testercomm_socket: cannot create socket\n"); + pSocketState->eState = ST_FAIL; + return(-1); + } + + // check for invalid addr + if ((uHostAddr = SocketInTextGetAddr(pSocketState->strHostName)) == 0) + { + ZPrintf("testercomm_socket: connect got invalid host address %s\n", pSocketState->strHostName); + pSocketState->eState = ST_FAIL; + return(-2); + } + + // initiate connection + SockaddrInSetAddr(&SockAddr, uHostAddr); + if ((iResult = SocketConnect(pSocketState->pSocket, &SockAddr, sizeof(SockAddr))) != SOCKERR_NONE) + { + ZPrintf("testercomm_socket: error %d initiating connection to %s\n", iResult, pSocketState->strHostName); + SocketClose(pSocketState->pSocket); + pSocketState->pSocket = NULL; + pSocketState->eState = ST_FAIL; + return(-3); + } + pSocketState->eState = ST_CONN; + ZPrintf("testercomm_socket: connect hostname=%a\n", uHostAddr); + } + else + { + struct sockaddr BindAddr; + + /* only create the listen socket the first time, if the peer disconnects we will + just go back into the listen state to accept the new connection */ + if (pSocketState->pListen == NULL) + { + // create the socket + if ((pSocketState->pListen = SocketOpen(AF_INET, SOCK_STREAM, 0)) == NULL) + { + ZPrintf("testercomm_socket: cannot create socket\n"); + pSocketState->eState = ST_FAIL; + return(-4); + } + + // set SO_LINGER to zero on listen socket so it doesn't linger and cause errors in testing + SocketControl(pSocketState->pListen, 'soli', 0, NULL, NULL); + // set to reuse addr incase it is still bound from previous session + SocketControl(pSocketState->pListen, 'radr', 1, NULL, NULL); + + // bind the socket + if ((iResult = SocketBind(pSocketState->pListen, &SockAddr, sizeof(SockAddr))) != SOCKERR_NONE) + { + ZPrintf("testercomm_socket: error %d binding to port %d\n", iResult, pSocketState->iPort); + SocketClose(pSocketState->pListen); + pSocketState->pListen = NULL; + pSocketState->eState = ST_FAIL; + ZPrintf("testercomm_socket: next bind will use a random port\n"); + pSocketState->iPort = 0; + return(-5); + } + // listen on the socket + if ((iResult = SocketListen(pSocketState->pListen, 2)) != SOCKERR_NONE) + { + ZPrintf("testercomm_socket: error %d listening on socket\n", iResult); + SocketClose(pSocketState->pListen); + pSocketState->pListen = NULL; + pSocketState->eState = ST_FAIL; + return(-6); + } + } + + pSocketState->eState = ST_LIST; + SocketInfo(pSocketState->pListen, 'bind', 0, &BindAddr, sizeof(BindAddr)); + ZPrintf("testercomm_socket: waiting for connection on port %d\n", SockaddrInGetPort(&BindAddr)); + } + + // done for now + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _TesterCommDisconnect2 + + \Description + Disconnect the host client communication module. + + \Input *pSocketState - pointer to host client comm module + + \Output + int32_t - 0=success, error code otherwise + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommDisconnect2(TesterCommSocketT *pSocketState) +{ + ZPrintf("testercomm_socket: disconnect\n"); + // dispose of socket(s) + if (pSocketState->pSocket != NULL) + { + SocketClose(pSocketState->pSocket); + pSocketState->pSocket = NULL; + } + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _TesterCommCheckConnect + + \Description + Check to see if a connection has been established + + \Input *pState - module state + + \Output + int32_t - 1=open, 0=connecting, negative=error + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommCheckConnect(TesterCommT *pState) +{ + TesterCommSocketT *pSocketState = pState->pInterface->pData; + + // if already open nothing to do + if (pSocketState->eState == ST_OPEN) + { + return(1); + } + + if (pSocketState->eState == ST_LIST) + { + struct sockaddr RequestAddr; + int32_t iAddrLen = sizeof(RequestAddr); + + // accept incoming connection + SockaddrInit(&RequestAddr, AF_INET); + if ((pSocketState->pSocket = SocketAccept(pSocketState->pListen, &RequestAddr, &iAddrLen)) != NULL) + { + ZPrintf("testercomm_socket: accepted incoming connection request from %A\n", &RequestAddr); + pSocketState->eState = ST_OPEN; + } + } + else if (pSocketState->eState == ST_CONN) + { + int32_t iResult; + + // waiting for connection to be complete + if ((iResult = SocketInfo(pSocketState->pSocket, 'stat', 0, NULL, 0)) > 0) + { + ZPrintf("testercomm_socket: connection complete\n"); + pSocketState->eState = ST_OPEN; + } + else if (iResult < 0) + { + pSocketState->eState = ST_FAIL; + } + } + else if (pSocketState->eState == ST_FAIL) + { + _TesterCommDisconnect2(pSocketState); + + // dump all the messages + ZListClear(pState->pOutputData); + + // set that we haven't received any input + pState->bGotInput = FALSE; + + _TesterCommConnect2(pSocketState); + } + + if (pSocketState->eState == ST_OPEN) + { + // set the tick time so we don't automatically get a timeout + pState->uLastSendTime = ZTick(); + return(1); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _TesterCommCheckInput + + \Description + Check for data coming from the other side (host or client) and pull + any data into our internal buffer, if possible. + + \Input *pState - pointer to host client comm module + + \Output + int32_t - 0 = no data, >0 = data, error code otherwise + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommCheckInput(TesterCommT *pState) +{ + TesterCommSocketT *pSocketState = pState->pInterface->pData; + int32_t iBytesRecv = 0, iLinesRecv, iResult; + const char *pInputLoop; + uint16_t *pJson; + + // don't do anything if we have already closed the socket + if (pSocketState->eState != ST_OPEN) + { + return(-1); + } + + // try to get as much data as possible + while ((iResult = SocketRecv(pSocketState->pSocket, pSocketState->strInputData+iBytesRecv, sizeof(pSocketState->strInputData)-iBytesRecv, 0)) != 0) + { + if (iResult > 0) + { + iBytesRecv += iResult; + pState->bGotInput = TRUE; + } + else if (iResult < 0) + { + // an error occurred + ZPrintf("testercomm_socket: error %d receiving data\n", iResult); + pSocketState->eState = ST_FAIL; + return(-1); + } + } + if (iBytesRecv == 0) + { + return(0); + } + + pJson = JsonParse2(pSocketState->strInputData, -1, 0, 0, NULL); + + // push each line onto the list + iLinesRecv = 0; + while ((pInputLoop = JsonFind2(pJson, NULL, "msg[", iLinesRecv)) != NULL) + { + char strBuffer[sizeof(pState->LineData.strBuffer)]; + int32_t iBufLen; + + ds_memclr(&pState->LineData, sizeof(pState->LineData)); + pState->LineData.iType = (int32_t)JsonGetInteger(JsonFind2(pJson, pInputLoop, ".TYPE", iLinesRecv), 0); + iBufLen = JsonGetString(JsonFind2(pJson, pInputLoop, ".TEXT", iLinesRecv), strBuffer, sizeof(strBuffer), ""); + Base64Decode3(strBuffer, iBufLen, pState->LineData.strBuffer, sizeof(pState->LineData.strBuffer)); + + // add to the back of the list; remember to add one for the terminator + ZListPushBack(pState->pInputData, &pState->LineData); + + iLinesRecv += 1; + } + + ZMemFree(pJson); + + // done - return how much we got from the file + return(iLinesRecv); +} + +/*F********************************************************************************/ +/*! + \Function _TesterCommCheckOutput + + \Description + Check and send data from the output buffer, if possible. + + \Input *pState - pointer to host client comm module + + \Output + int32_t - 0=success, error code otherwise + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommCheckOutput(TesterCommT *pState) +{ + TesterCommSocketT *pSocketState = pState->pInterface->pData; + int32_t iLineLength, iBytesSent, iResult, iTickDiff; + char *pWrite, *pBuffer; + + // see if there's any data to send + if (ZListPeekFront(pState->pOutputData) == NULL) + { + // reset the last send time - we're not timing out because there's no data + pState->uLastSendTime = ZTick(); + return(0); + } + + // see if we've timed out + if ((iTickDiff = NetTickDiff(ZTick(), pState->uLastSendTime)) > TESTERCOMM_OUTPUTTIMEOUT_DEFAULT) + { + // timeout occurred - dump all pending messages and pop a warning + ZListClear(pState->pOutputData); + + ZPrintf("testercomm_socket: TIMEOUT in sending data (%dms). List cleared. Comms Lost?\n", iTickDiff); + pSocketState->eState = ST_FAIL; + } + + // push output queue stuff by sending commands + JsonInit(pState->strCommand, sizeof(pState->strCommand), 0); + JsonArrayStart(pState->strCommand, "msg"); + + while (ZListPeekFront(pState->pOutputData) != NULL) + { + char strBuffer[sizeof(pState->LineData.strBuffer)]; + + // snag the data into the local buffer + ZListPopFront(pState->pOutputData, &pState->LineData); + + // create the stuff to write + JsonObjectStart(pState->strCommand, NULL); + JsonAddInt(pState->strCommand, "TYPE", pState->LineData.iType); + Base64Encode2(pState->LineData.strBuffer, (int32_t)strlen(pState->LineData.strBuffer), strBuffer, sizeof(strBuffer)); + if ((iResult = JsonAddStr(pState->strCommand, "TEXT", strBuffer)) == JSON_ERR_FULL) + { + // Sometimes, the text we get in will be too big. Make sure this is an easy error to diagnose. + ZPrintf("testercomm_socket: text too large to send in TESTERCOMM_COMMANDSIZE_DEFAULT. Discarding.\n"); + } + JsonObjectEnd(pState->strCommand); + } + JsonArrayEnd(pState->strCommand); + pBuffer = JsonFinish(pState->strCommand); + iLineLength = (int32_t)strlen(pBuffer); + + // send it + for (pWrite = pBuffer, iBytesSent = 0; iLineLength != 0; ) + { + if ((iResult = SocketSend(pSocketState->pSocket, pWrite, iLineLength, 0)) < 0) + { + ZPrintf("testercomm_socket: error %d sending command to remote target\n", iResult); + pSocketState->eState = ST_FAIL; + return(-1); + } + else + { + iLineLength -= iResult; + pWrite += iResult; + iBytesSent += iResult; + } + + if (iLineLength != 0) + { + NetConnSleep(5); + } + } + + // mark the last send time + pState->uLastSendTime = ZTick(); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _TesterCommConnect + + \Description + Connect the host client communication module. + + \Input *pState - pointer to host client comm module + \Input *pParams - startup parameters + \Input bIsHost - TRUE=host, FALSE=client + + \Output + int32_t - 0 for success, error code otherwise + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommConnect(TesterCommT *pState, const char *pParams, uint32_t bIsHost) +{ + TesterCommSocketT *pSocketState = pState->pInterface->pData; + char *pUserPort; + uint16_t aJson[512]; + + // remember host status + pSocketState->bIsHost = (uint8_t)bIsHost; + pSocketState->iPort = T2COMM_PORT; + + // get startup parameters + JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pParams, -1); + JsonGetString(JsonFind(aJson, "HOSTNAME"), pSocketState->strHostName, sizeof(pSocketState->strHostName), ""); + + // check for a user specified port + if ((pUserPort = strchr(pSocketState->strHostName, ':')) != NULL ) + { + pSocketState->iPort = atoi(pUserPort+1); + } + + // do the connect + return(_TesterCommConnect2(pSocketState)); +} + +/*F********************************************************************************/ +/*! + \Function _TesterCommUpdate + + \Description + Give the host/client interface module some processor time. Call this + once in a while to pump the input and output pipes. + + \Input *pState - module state + + \Output + int32_t - 0 for success, error code otherwise + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommUpdate(TesterCommT *pState) +{ + int32_t iResult; + + // quit if we are suspended (don't do any more commands) + if (pState->uSuspended) + { + return(0); + } + + // check for accepted connection + if (_TesterCommCheckConnect(pState) <= 0) + { + return(0); + } + + // check for outgoing and incoming data + _TesterCommCheckInput(pState); + _TesterCommCheckOutput(pState); + + // now call the callbacks for incoming messages + while ((iResult = ZListPopFront(pState->pInputData, &pState->LineData)) > 0) + { + // try to access the message map + if ((pState->LineData.iType >= 0) && (pState->MessageMap[pState->LineData.iType] != NULL)) + { + // protect against recursion by suspending commands until this one completes + TesterCommSuspend(pState); + (pState->MessageMap[pState->LineData.iType])(pState, pState->LineData.strBuffer, pState->pMessageMapUserData[pState->LineData.iType]); + TesterCommWake(pState); + } + } + + // done + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _TesterCommDisconnect + + \Description + Disconnect the host client communication module. + + \Input *pState - pointer to host client comm module + + \Output + int32_t - 0=success, error code otherwise + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TesterCommDisconnect(TesterCommT *pState) +{ + TesterCommSocketT *pSocketState = (TesterCommSocketT *)pState->pInterface->pData; + + _TesterCommDisconnect2(pSocketState); + if (pSocketState->pListen != NULL) + { + SocketClose(pSocketState->pListen); + pSocketState->pListen = NULL; + } + + return(0); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterCommAttachSocket + + \Description + Attach file module function pointers to a tester comm module. + + \Input *pState - pointer to host client comm module + + \Version 10/17/2011 (jbrookes) +*/ +/********************************************************************************F*/ +void TesterCommAttachSocket(TesterCommT *pState) +{ + ZPrintf("testercomm_socket: attaching socket interface methods\n"); + pState->pInterface->CommConnectFunc = &_TesterCommConnect; + pState->pInterface->CommUpdateFunc = &_TesterCommUpdate; + pState->pInterface->CommDisconnectFunc = &_TesterCommDisconnect; + pState->pInterface->pData = (TesterCommSocketT *)ZMemAlloc(sizeof(TesterCommSocketT)); + ds_memclr(pState->pInterface->pData, sizeof(TesterCommSocketT)); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/advert.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/advert.c new file mode 100644 index 00000000..fec72dd6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/advert.c @@ -0,0 +1,156 @@ +/*H********************************************************************************/ +/*! + \File advert.c + + \Description + Test the ProtoAdvt module. + + \Copyright + Copyright (c) Electronic Arts 2003-2005. ALL RIGHTS RESERVED. + + \Version 03/28/2003 (gschaefer) First Version +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protoadvt.h" + +#include "libsample/zlib.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +#define WATCH_BUFFER 4096 + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private variables + +static ProtoAdvtRef *_Advert_pAdvert = NULL; + +// Public variables + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdAdvert + + \Description + Handle advertisement watcher + + \Input *argz - + \Input argc - + \Input **argv - + + \Output int32_t - + + \Version 1.0 02/17/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t CmdAdvertWatch(ZContext *argz, int32_t argc, char **argv) +{ + char *pBuffer = (char *)argz; + char strBuffer[WATCH_BUFFER]; + + // get list of advertisements + ProtoAdvtQuery(_Advert_pAdvert, "TESTER", "", strBuffer, sizeof(strBuffer), 0); + if (strcmp(pBuffer, strBuffer) != 0) + { + ZPrintf("Advertising list:\n"); + ZPrintf("%s", strBuffer); + ZPrintf("...end of list...\n"); + strcpy(pBuffer, strBuffer); + } + + // keep running + return(ZCallback(&CmdAdvertWatch, 500)); +} + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdAdvert + + \Description + Exercise the advertising module. + + \Input *argz - + \Input argc - + \Input **argv - + + \Output int32_t - + + \Version 1.0 02/17/2003 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdAdvert(ZContext *argz, int32_t argc, char **argv) +{ + int32_t iCount; + char strBuffer[4096]; + + // handle help + if (argc == 0) + { + ZPrintf("%s list|add|del\n", argv[0]); + return(0); + } + + // startup advertising if needed + if (_Advert_pAdvert == NULL) + { + _Advert_pAdvert = ProtoAdvtConstruct(16); + } + + // handle listing advertisements + if ((argc == 2) && (strcmp(argv[1], "list") == 0)) + { + iCount = ProtoAdvtQuery(_Advert_pAdvert, "TESTER", "", strBuffer, sizeof(strBuffer), 0); + if (iCount > 0) + { + ZPrintf("%s", strBuffer); + } + } + + // constant watch + if ((argc == 2) && (strcmp(argv[1], "watch") == 0)) + { + char *pBuffer = (char *)ZContextCreate(WATCH_BUFFER); + pBuffer[0] = 0; + return(ZCallback(&CmdAdvertWatch, 100)); + } + + // add an advertisement + if ((argc > 2) && (strcmp(argv[1], "add") == 0)) + { + ProtoAdvtAnnounce(_Advert_pAdvert, "TESTER", argv[2], "", "TCP:~1:1024\tUDP:~1:1024", 0); + } + + // delete an advertisement + if ((argc > 2) && (strcmp(argv[1], "del") == 0)) + { + ProtoAdvtCancel(_Advert_pAdvert, "TESTER", argv[2]); + } + + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/base64.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/base64.c new file mode 100644 index 00000000..6e351a43 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/base64.c @@ -0,0 +1,106 @@ +/*H********************************************************************************/ +/*! + \File base64.c + + \Description + Test the Base64 encoder and decoder + + \Copyright + Copyright (c) 2018 Electronic Arts Inc. + + \Version 12/16/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/util/base64.h" +#include "testermodules.h" + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// Variables + +/*** Private Functions ************************************************************/ + + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CmdBase64 + + \Description + Test the Json formatter and parser + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 12/16/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdBase64(ZContext *argz, int32_t argc, char *argv[]) +{ + // check for a file argument... if we have one, load and parse it + if ((argc == 4) && !ds_stricmp(argv[1], "decode")) + { + char *pFileData, *pFileDecoded; + ZFileT iOutFileId; + int32_t iFileSize; + + if ((pFileData = ZFileLoad(argv[2], &iFileSize, FALSE)) != NULL) + { + int32_t iDecodedSize = Base64DecodedSize(iFileSize); + if ((pFileDecoded = ZMemAlloc(iDecodedSize)) != NULL) + { + if (Base64Decode3(pFileData, iFileSize, pFileDecoded, iDecodedSize) > 0) + { + if ((iOutFileId = ZFileOpen(argv[3], ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_BINARY|ZFILE_OPENFLAG_CREATE)) != ZFILE_INVALID) + { + ZFileWrite(iOutFileId, pFileDecoded, iDecodedSize); + ZFileClose(iOutFileId); + ZPrintf("%s: decoded %d bytes\n", argv[0], iDecodedSize); + } + else + { + ZPrintf("%s: could not open file '%s' for writing", argv[0], argv[3]); + } + } + else + { + ZPrintf("%s: could not decode file '%s'\n", argv[0], argv[2]); + } + ZMemFree(pFileDecoded); + } + else + { + ZPrintf("%s: could not allocate %d bytes for base64 decode\n", argv[0], iDecodedSize); + } + ZMemFree(pFileData); + } + else + { + ZPrintf("%s: could not open file '%s' for reading\n", argv[0], argv[2]); + } + } + + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/crypt.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/crypt.c new file mode 100644 index 00000000..d0658aa6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/crypt.c @@ -0,0 +1,2179 @@ +/*H********************************************************************************/ +/*! + \File crypt.c + + \Description + Test the crypto modules. + + \Copyright + Copyright (c) 2013 Electronic Arts Inc. + + \Version 01/11/2013 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/crypt/cryptaes.h" +#include "DirtySDK/crypt/cryptarc4.h" +#include "DirtySDK/crypt/cryptchacha.h" +#include "DirtySDK/crypt/cryptcurve.h" +#include "DirtySDK/crypt/cryptgcm.h" +#include "DirtySDK/crypt/cryptmont.h" +#include "DirtySDK/crypt/cryptnist.h" +#include "DirtySDK/crypt/cryptrand.h" +#include "DirtySDK/crypt/cryptrsa.h" +#include "DirtySDK/crypt/cryptsha2.h" +#include "DirtySDK/crypt/crypthash.h" +#include "DirtySDK/crypt/crypthmac.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/util/base64.h" +#include "DirtySDK/util/murmurhash3.h" + +#include "testermodules.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! type to encapsulate gcm test data +typedef struct CryptTestGcmT +{ + const uint8_t aKey[32]; + int32_t iKeySize; + const uint8_t aInitVec[12]; + const uint8_t aInput[256]; + int32_t iInputSize; + const uint8_t aData[256]; + int32_t iDataSize; + const uint8_t aOutput[256]; + int32_t iOutputSize; + const uint8_t aTagRslt[16]; +} CryptTestGcmT; + +//! type to encapsulate sha2 test data +typedef struct CryptTestSha2 +{ + const char *pString; + const uint8_t strHashRslt[4][CRYPTHASH_MAXDIGEST]; +} CryptTestSha2; + +//! type to encapsulate hmac test params +typedef struct CryptTestHmacParam +{ + uint8_t uKeyLen; + uint8_t uInpLen; + uint8_t uOutLen; + const uint8_t strRslt[CRYPTHASH_MAXDIGEST]; +} CryptTestHmacParam; + +//! type to encapsulate hmac test data +typedef struct CryptTestHmac +{ + const char *pKey; + const char *pString; + const CryptTestHmacParam Param[CRYPTHASH_NUMHASHES]; +} CryptTestHmac; + +typedef struct CryptTestDsaCaseT +{ + const uint8_t aMessage[16]; + CryptHashTypeE eHashType; + const char strCaseName[16]; + const uint8_t aSignature[97]; +} CryptTestDsaCaseT; + +//! type to encapsulate dsa test data +typedef struct CryptTestDsaT +{ + CryptCurveE eCurveType; + int32_t iCurveSize; + const char strTestName[16]; + const uint8_t aPrivateKey[48]; + const uint8_t aPublicKey[97]; + const CryptTestDsaCaseT aCases[10]; +} CryptTestDsaT; + +/*** Variables ********************************************************************/ + +//! gcm test data from the FIPS standard +static const CryptTestGcmT _Crypt_GcmTest[] = +{ + // Test cases 0-3 use 128 bit keys + + // Test Case 0: validate tag result with zero key and no data + { + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // key + 16, // key size + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // iv + { 0 }, 0, // input, input size + { 0 }, 0, // data, data size + { 0 }, 0, // output, output size + { 0x58,0xe2,0xfc,0xce, 0xfa,0x7e,0x30,0x61, 0x36,0x7f,0x1d,0x57, 0xa4,0xe7,0x45,0x5a } // tag result + }, + // Test Case 1: validate encryption and tag result with zero key and zero data + { + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // key + 16, // key size + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // iv + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // input + 16, // input size + { 0 }, 0, // data, data size + { 0x03,0x88,0xda,0xce, 0x60,0xb6,0xa3,0x92, 0xf3,0x28,0xc2,0xb9, 0x71,0xb2,0xfe,0x78 }, // output + 16, // output size + { 0xab,0x6e,0x47,0xd4, 0x2c,0xec,0x13,0xbd, 0xf5,0x3a,0x67,0xb2, 0x12,0x57,0xbd,0xdf } // tag result + }, + // Test Case 2: validate encryption and tag result with non-zero key and data + { + { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 }, // key + 16, // key size + { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88 }, // iv + { + 0xd9,0x31,0x32,0x25, 0xf8,0x84,0x06,0xe5, 0xa5,0x59,0x09,0xc5, 0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53, 0x15,0x34,0xf7,0xda, 0x2e,0x4c,0x30,0x3d, 0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95, 0x95,0x68,0x09,0x53, 0x2f,0xcf,0x0e,0x24, 0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5, 0xaa,0x0d,0xe6,0x57, 0xba,0x63,0x7b,0x39, 0x1a,0xaf,0xd2,0x55 + }, 64, // input, input size + { 0 }, 0, // data, data size + { + 0x42,0x83,0x1e,0xc2, 0x21,0x77,0x74,0x24, 0x4b,0x72,0x21,0xb7, 0x84,0xd0,0xd4,0x9c, + 0xe3,0xaa,0x21,0x2f, 0x2c,0x02,0xa4,0xe0, 0x35,0xc1,0x7e,0x23, 0x29,0xac,0xa1,0x2e, + 0x21,0xd5,0x14,0xb2, 0x54,0x66,0x93,0x1c, 0x7d,0x8f,0x6a,0x5a, 0xac,0x84,0xaa,0x05, + 0x1b,0xa3,0x0b,0x39, 0x6a,0x0a,0xac,0x97, 0x3d,0x58,0xe0,0x91, 0x47,0x3f,0x59,0x85 + }, 64, // output, output size + { 0x4d,0x5c,0x2a,0xf3, 0x27,0xcd,0x64,0xa6, 0x2c,0xf3,0x5a,0xbd, 0x2b,0xa6,0xfa,0xb4 } // tag result + }, + // Test Case 3: validate encryption and tag result with non-zero key, un-aligned data, and additional data + { + { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 }, // key + 16, // key size + { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88 }, // iv + { + 0xd9,0x31,0x32,0x25, 0xf8,0x84,0x06,0xe5, 0xa5,0x59,0x09,0xc5, 0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53, 0x15,0x34,0xf7,0xda, 0x2e,0x4c,0x30,0x3d, 0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95, 0x95,0x68,0x09,0x53, 0x2f,0xcf,0x0e,0x24, 0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5, 0xaa,0x0d,0xe6,0x57, 0xba,0x63,0x7b,0x39 + }, 60, // input, input size + { + 0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef, 0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef, + 0xab,0xad,0xda,0xd2 + }, 20, // data, data size + { // output + 0x42,0x83,0x1e,0xc2, 0x21,0x77,0x74,0x24, 0x4b,0x72,0x21,0xb7, 0x84,0xd0,0xd4,0x9c, + 0xe3,0xaa,0x21,0x2f, 0x2c,0x02,0xa4,0xe0, 0x35,0xc1,0x7e,0x23, 0x29,0xac,0xa1,0x2e, + 0x21,0xd5,0x14,0xb2, 0x54,0x66,0x93,0x1c, 0x7d,0x8f,0x6a,0x5a, 0xac,0x84,0xaa,0x05, + 0x1b,0xa3,0x0b,0x39, 0x6a,0x0a,0xac,0x97, 0x3d,0x58,0xe0,0x91 + }, + 60, // output size + { 0x5b,0xc9,0x4f,0xbc, 0x32,0x21,0xa5,0xdb, 0x94,0xfa,0xe9,0x5a, 0xe7,0x12,0x1a,0x47 } // tag result + }, + + // Test cases 4-7 use 256 bit keys + + // Test Case 4: validate tag result with zero key and no data + { + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, // key + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, + 32, // key size + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // iv + { 0 }, 0, // input, input size + { 0 }, 0, // data, data size + { 0 }, 0, // output, output size + { 0x53,0x0f,0x8a,0xfb, 0xc7,0x45,0x36,0xb9, 0xa9,0x63,0xb4,0xf1, 0xc4,0xcb,0x73,0x8b } // tag result + }, + // Test Case 5: validate encryption and tag result with zero key and zero data + { + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, // key + 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, + 32, // key size + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // iv + { 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 }, // input + 16, // input size + { 0 }, 0, // data, data size + { 0xce,0xa7,0x40,0x3d, 0x4d,0x60,0x6b,0x6e, 0x07,0x4e,0xc5,0xd3, 0xba,0xf3,0x9d,0x18 }, // output + 16, // output size + { 0xd0,0xd1,0xc8,0xa7, 0x99,0x99,0x6b,0xf0, 0x26,0x5b,0x98,0xb5, 0xd4,0x8a,0xb9,0x19 } // tag result + }, + // Test Case 6: validate encryption and tag result with non-zero key and data + { + { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08, // key + 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 }, + 32, // key size + { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88 }, // iv + { + 0xd9,0x31,0x32,0x25, 0xf8,0x84,0x06,0xe5, 0xa5,0x59,0x09,0xc5, 0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53, 0x15,0x34,0xf7,0xda, 0x2e,0x4c,0x30,0x3d, 0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95, 0x95,0x68,0x09,0x53, 0x2f,0xcf,0x0e,0x24, 0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5, 0xaa,0x0d,0xe6,0x57, 0xba,0x63,0x7b,0x39, 0x1a,0xaf,0xd2,0x55 + }, 64, // input, input size + { 0 }, 0, // data, data size + { + 0x52,0x2d,0xc1,0xf0, 0x99,0x56,0x7d,0x07, 0xf4,0x7f,0x37,0xa3, 0x2a,0x84,0x42,0x7d, + 0x64,0x3a,0x8c,0xdc, 0xbf,0xe5,0xc0,0xc9, 0x75,0x98,0xa2,0xbd, 0x25,0x55,0xd1,0xaa, + 0x8c,0xb0,0x8e,0x48, 0x59,0x0d,0xbb,0x3d, 0xa7,0xb0,0x8b,0x10, 0x56,0x82,0x88,0x38, + 0xc5,0xf6,0x1e,0x63, 0x93,0xba,0x7a,0x0a, 0xbc,0xc9,0xf6,0x62, 0x89,0x80,0x15,0xad, + }, 64, // output, output size + { 0xb0,0x94,0xda,0xc5, 0xd9,0x34,0x71,0xbd, 0xec,0x1a,0x50,0x22, 0x70,0xe3,0xcc,0x6c } // tag result + }, + // Test Case 3: validate encryption and tag result with non-zero key, un-aligned data, and additional data + { + { 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08, // key + 0xfe,0xff,0xe9,0x92, 0x86,0x65,0x73,0x1c, 0x6d,0x6a,0x8f,0x94, 0x67,0x30,0x83,0x08 }, + 32, // key size + { 0xca,0xfe,0xba,0xbe, 0xfa,0xce,0xdb,0xad, 0xde,0xca,0xf8,0x88 }, // iv + { + 0xd9,0x31,0x32,0x25, 0xf8,0x84,0x06,0xe5, 0xa5,0x59,0x09,0xc5, 0xaf,0xf5,0x26,0x9a, + 0x86,0xa7,0xa9,0x53, 0x15,0x34,0xf7,0xda, 0x2e,0x4c,0x30,0x3d, 0x8a,0x31,0x8a,0x72, + 0x1c,0x3c,0x0c,0x95, 0x95,0x68,0x09,0x53, 0x2f,0xcf,0x0e,0x24, 0x49,0xa6,0xb5,0x25, + 0xb1,0x6a,0xed,0xf5, 0xaa,0x0d,0xe6,0x57, 0xba,0x63,0x7b,0x39 + }, 60, // input, input size + { + 0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef, 0xfe,0xed,0xfa,0xce, 0xde,0xad,0xbe,0xef, + 0xab,0xad,0xda,0xd2 + }, 20, // data, data size + { // output + 0x52,0x2d,0xc1,0xf0, 0x99,0x56,0x7d,0x07, 0xf4,0x7f,0x37,0xa3, 0x2a,0x84,0x42,0x7d, + 0x64,0x3a,0x8c,0xdc, 0xbf,0xe5,0xc0,0xc9, 0x75,0x98,0xa2,0xbd, 0x25,0x55,0xd1,0xaa, + 0x8c,0xb0,0x8e,0x48, 0x59,0x0d,0xbb,0x3d, 0xa7,0xb0,0x8b,0x10, 0x56,0x82,0x88,0x38, + 0xc5,0xf6,0x1e,0x63, 0x93,0xba,0x7a,0x0a, 0xbc,0xc9,0xf6,0x62, + }, + 60, // output size + { 0x76,0xfc,0x6e,0xce, 0x0f,0x4e,0x17,0x68, 0xcd,0xdf,0x88,0x53, 0xbb,0x2d,0x55,0x1b } // tag result + }, +}; + +//! sha2 test data +static const CryptTestSha2 _Crypt_Sha2Test[] = +{ + { + "", + { + { // sha224 + 0xd1,0x4a,0x02,0x8c, 0x2a,0x3a,0x2b,0xc9, 0x47,0x61,0x02,0xbb, 0x28,0x82,0x34,0xc4, + 0x15,0xa2,0xb0,0x1f, 0x82,0x8e,0xa6,0x2a, 0xc5,0xb3,0xe4,0x2f + }, + { // sha256 + 0xe3,0xb0,0xc4,0x42, 0x98,0xfc,0x1c,0x14, 0x9a,0xfb,0xf4,0xc8, 0x99,0x6f,0xb9,0x24, + 0x27,0xae,0x41,0xe4, 0x64,0x9b,0x93,0x4c, 0xa4,0x95,0x99,0x1b, 0x78,0x52,0xb8,0x55 + }, + { // sha384 + 0x38,0xb0,0x60,0xa7, 0x51,0xac,0x96,0x38, 0x4c,0xd9,0x32,0x7e, 0xb1,0xb1,0xe3,0x6a, + 0x21,0xfd,0xb7,0x11, 0x14,0xbe,0x07,0x43, 0x4c,0x0c,0xc7,0xbf, 0x63,0xf6,0xe1,0xda, + 0x27,0x4e,0xde,0xbf, 0xe7,0x6f,0x65,0xfb, 0xd5,0x1a,0xd2,0xf1, 0x48,0x98,0xb9,0x5b + }, + { // sha512 + 0xcf,0x83,0xe1,0x35, 0x7e,0xef,0xb8,0xbd, 0xf1,0x54,0x28,0x50, 0xd6,0x6d,0x80,0x07, + 0xd6,0x20,0xe4,0x05, 0x0b,0x57,0x15,0xdc, 0x83,0xf4,0xa9,0x21, 0xd3,0x6c,0xe9,0xce, + 0x47,0xd0,0xd1,0x3c, 0x5d,0x85,0xf2,0xb0, 0xff,0x83,0x18,0xd2, 0x87,0x7e,0xec,0x2f, + 0x63,0xb9,0x31,0xbd, 0x47,0x41,0x7a,0x81, 0xa5,0x38,0x32,0x7a, 0xf9,0x27,0xda,0x3e + } + } + }, + { + "The quick brown fox jumps over the lazy dog", + { + { // sha224 + 0x73,0x0e,0x10,0x9b, 0xd7,0xa8,0xa3,0x2b, 0x1c,0xb9,0xd9,0xa0, 0x9a,0xa2,0x32,0x5d, + 0x24,0x30,0x58,0x7d, 0xdb,0xc0,0xc3,0x8b, 0xad,0x91,0x15,0x25 + }, + { // sha256 + 0xd7,0xa8,0xfb,0xb3, 0x07,0xd7,0x80,0x94, 0x69,0xca,0x9a,0xbc, 0xb0,0x08,0x2e,0x4f, + 0x8d,0x56,0x51,0xe4, 0x6d,0x3c,0xdb,0x76, 0x2d,0x02,0xd0,0xbf, 0x37,0xc9,0xe5,0x92 + }, + { // sha384 + 0xca,0x73,0x7f,0x10, 0x14,0xa4,0x8f,0x4c, 0x0b,0x6d,0xd4,0x3c, 0xb1,0x77,0xb0,0xaf, + 0xd9,0xe5,0x16,0x93, 0x67,0x54,0x4c,0x49, 0x40,0x11,0xe3,0x31, 0x7d,0xbf,0x9a,0x50, + 0x9c,0xb1,0xe5,0xdc, 0x1e,0x85,0xa9,0x41, 0xbb,0xee,0x3d,0x7f, 0x2a,0xfb,0xc9,0xb1 + }, + { // sha512 + 0x07,0xe5,0x47,0xd9, 0x58,0x6f,0x6a,0x73, 0xf7,0x3f,0xba,0xc0, 0x43,0x5e,0xd7,0x69, + 0x51,0x21,0x8f,0xb7, 0xd0,0xc8,0xd7,0x88, 0xa3,0x09,0xd7,0x85, 0x43,0x6b,0xbb,0x64, + 0x2e,0x93,0xa2,0x52, 0xa9,0x54,0xf2,0x39, 0x12,0x54,0x7d,0x1e, 0x8a,0x3b,0x5e,0xd6, + 0xe1,0xbf,0xd7,0x09, 0x78,0x21,0x23,0x3f, 0xa0,0x53,0x8f,0x3d, 0xb8,0x54,0xfe,0xe6 + } + } + }, + { + "The quick brown fox jumps over the lazy dog.", + { + { // sha224 + 0x61,0x9c,0xba,0x8e, 0x8e,0x05,0x82,0x6e, 0x9b,0x8c,0x51,0x9c, 0x0a,0x5c,0x68,0xf4, + 0xfb,0x65,0x3e,0x8a, 0x3d,0x8a,0xa0,0x4b, 0xb2,0xc8,0xcd,0x4c + }, + { // sha256 + 0xef,0x53,0x7f,0x25, 0xc8,0x95,0xbf,0xa7, 0x82,0x52,0x65,0x29, 0xa9,0xb6,0x3d,0x97, + 0xaa,0x63,0x15,0x64, 0xd5,0xd7,0x89,0xc2, 0xb7,0x65,0x44,0x8c, 0x86,0x35,0xfb,0x6c + }, + { // sha384 + 0xed,0x89,0x24,0x81, 0xd8,0x27,0x2c,0xa6, 0xdf,0x37,0x0b,0xf7, 0x06,0xe4,0xd7,0xbc, + 0x1b,0x57,0x39,0xfa, 0x21,0x77,0xaa,0xe6, 0xc5,0x0e,0x94,0x66, 0x78,0x71,0x8f,0xc6, + 0x7a,0x7a,0xf2,0x81, 0x9a,0x02,0x1c,0x2f, 0xc3,0x4e,0x91,0xbd, 0xb6,0x34,0x09,0xd7 + }, + { // sha512 + 0x91,0xea,0x12,0x45, 0xf2,0x0d,0x46,0xae, 0x9a,0x03,0x7a,0x98, 0x9f,0x54,0xf1,0xf7, + 0x90,0xf0,0xa4,0x76, 0x07,0xee,0xb8,0xa1, 0x4d,0x12,0x89,0x0c, 0xea,0x77,0xa1,0xbb, + 0xc6,0xc7,0xed,0x9c, 0xf2,0x05,0xe6,0x7b, 0x7f,0x2b,0x8f,0xd4, 0xc7,0xdf,0xd3,0xa7, + 0xa8,0x61,0x7e,0x45, 0xf3,0xc4,0x63,0xd4, 0x81,0xc7,0xe5,0x86, 0xc3,0x9a,0xc1,0xed + } + } + }, + // test to validate sha384/sha512 128-bit length pad is correct + { + "The quick brown fox jumps over the lazy dog...\r\n" + "The quick brown fox jumps over the lazy dog...\r\n" + "The quick brown fox j\r\n", + { + { // sha224 + 0x6b,0x7c,0x3d,0xfb, 0xe4,0x3d,0x5c,0x0a, 0x9d,0xe5,0xa0,0x9e, 0x2c,0x90,0xc1,0x62, + 0xa5,0x1c,0xb3,0x32, 0x99,0x9e,0x88,0xce, 0xdb,0x5f,0xbc,0x57 + }, + { // sha256 + 0x0d,0x4c,0xaf,0x0a, 0x31,0x1c,0xb6,0x26, 0xdf,0x91,0x02,0xc2, 0x01,0xc1,0x8a,0x12, + 0x8a,0x0b,0x4b,0x4d, 0x1d,0xfa,0xa3,0xa9, 0xd9,0x43,0xc6,0x47, 0xd3,0x92,0xb6,0x54 + }, + { // sha384 + 0xed,0xae,0xd3,0xa8, 0xc6,0x3b,0x44,0x63, 0xb9,0xd6,0x34,0x8b, 0x86,0xda,0x9f,0xaf, + 0x69,0xe9,0x91,0x48, 0xe6,0xef,0xfc,0x71, 0x8a,0xf9,0x64,0x1e, 0x46,0x69,0x05,0x97, + 0x32,0xb8,0x0f,0xc8, 0x0b,0x84,0x39,0x7f, 0x58,0x3e,0xc3,0x50, 0x2b,0xa4,0x91,0xd8 + }, + { // sha512 + 0xb6,0x39,0x3a,0x1a, 0x27,0x4d,0x62,0xe3, 0x6c,0x9b,0xbf,0x83, 0xc4,0x39,0x68,0xcd, + 0x7b,0xaa,0x36,0x04, 0xb7,0x05,0xea,0x28, 0xd8,0x43,0x23,0x20, 0xe1,0xf5,0x29,0x6b, + 0xc1,0x93,0x6c,0xcb, 0x08,0x4e,0xad,0xfe, 0xd8,0xc9,0x38,0xd2, 0x91,0x9c,0x47,0xbd, + 0x04,0xdc,0xa1,0x3c, 0x66,0x7b,0xaf,0x47, 0xbd,0xc7,0xa4,0xfd, 0xa8,0xa2,0xc0,0xde + } + } + }, + { + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567" + "0123456701234567012345670123456701234567012345670123456701234567", + { + { // sha224 + 0x56,0x7F,0x69,0xF1, 0x68,0xCD,0x78,0x44, 0xE6,0x52,0x59,0xCE, 0x65,0x8F,0xE7,0xAA, + 0xDF,0xA2,0x52,0x16, 0xE6,0x8E,0xCA,0x0E, 0xB7,0xAB,0x82,0x62 + }, + { // sha256 + 0x59,0x48,0x47,0x32, 0x84,0x51,0xBD,0xFA, 0x85,0x05,0x62,0x25, 0x46,0x2C,0xC1,0xD8, + 0x67,0xD8,0x77,0xFB, 0x38,0x8D,0xF0,0xCE, 0x35,0xF2,0x5A,0xB5, 0x56,0x2B,0xFB,0xB5 + }, + { // sha384 + 0x2F,0xC6,0x4A,0x4F, 0x50,0x0D,0xDB,0x68, 0x28,0xF6,0xA3,0x43, 0x0B,0x8D,0xD7,0x2A, + 0x36,0x8E,0xB7,0xF3, 0xA8,0x32,0x2A,0x70, 0xBC,0x84,0x27,0x5B, 0x9C,0x0B,0x3A,0xB0, + 0x0D,0x27,0xA5,0xCC, 0x3C,0x2D,0x22,0x4A, 0xA6,0xB6,0x1A,0x0D, 0x79,0xFB,0x45,0x96 + }, + { // sha512 + 0x89,0xD0,0x5B,0xA6, 0x32,0xC6,0x99,0xC3, 0x12,0x31,0xDE,0xD4, 0xFF,0xC1,0x27,0xD5, + 0xA8,0x94,0xDA,0xD4, 0x12,0xC0,0xE0,0x24, 0xDB,0x87,0x2D,0x1A, 0xBD,0x2B,0xA8,0x14, + 0x1A,0x0F,0x85,0x07, 0x2A,0x9B,0xE1,0xE2, 0xAA,0x04,0xCF,0x33, 0xC7,0x65,0xCB,0x51, + 0x08,0x13,0xA3,0x9C, 0xD5,0xA8,0x4C,0x4A, 0xCA,0xA6,0x4D,0x3F, 0x3F,0xB7,0xBA,0xE9 + } + } + } +}; + +//! HMAC test data from https://tools.ietf.org/html/rfc2202 (md5, sha1) and https://tools.ietf.org/html/rfc4231#section-4.2 (sha2), respectively +static const CryptTestHmac _Crypt_HmacTest[] = +{ + // test case 1 + { + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + "Hi There", + { + { // murmurhash3 + 16, 8, 16, { 0x0C,0x55,0x91,0xD1, 0x01,0x73,0x5B,0x63, 0x43,0x0F,0x65,0x26, 0x9E,0x80,0x7A,0x63 } + }, + { // md5 + 16, 8, 16, { 0x92,0x94,0x72,0x7a, 0x36,0x38,0xbb,0x1c, 0x13,0xf4,0x8e,0xf8, 0x15,0x8b,0xfc,0x9d } + }, + { // sha1 + 20, 8, 20, { 0xB6,0x17,0x31,0x86, 0x55,0x05,0x72,0x64, 0xE2,0x8B,0xC0,0xB6, 0xFB,0x37,0x8C,0x8E, + 0xF1,0x46,0xBE,0x00 } + }, + { // sha224 + 20, 8, 28, { 0x89,0x6F,0xB1,0x12, 0x8A,0xBB,0xDF,0x19, 0x68,0x32,0x10,0x7C, 0xD4,0x9D,0xF3,0x3F, + 0x47,0xB4,0xB1,0x16, 0x99,0x12,0xBA,0x4F, 0x53,0x68,0x4B,0x22 } + }, + { // sha256 + 20, 8, 32, { 0xB0,0x34,0x4C,0x61, 0xD8,0xDB,0x38,0x53, 0x5C,0xA8,0xAF,0xCE, 0xAF,0x0B,0xF1,0x2B, + 0x88,0x1D,0xC2,0x00, 0xC9,0x83,0x3D,0xA7, 0x26,0xE9,0x37,0x6C, 0x2E,0x32,0xCF,0xF7 } + }, + { // sha384 + 20, 8, 48, { 0xAF,0xD0,0x39,0x44, 0xD8,0x48,0x95,0x62, 0x6B,0x08,0x25,0xF4, 0xAB,0x46,0x90,0x7F, + 0x15,0xF9,0xDA,0xDB, 0xE4,0x10,0x1E,0xC6, 0x82,0xAA,0x03,0x4C, 0x7C,0xEB,0xC5,0x9C, + 0xFA,0xEA,0x9E,0xA9, 0x07,0x6E,0xDE,0x7F, 0x4A,0xF1,0x52,0xE8, 0xB2,0xFA,0x9C,0xB6 } + }, + { // sha512 + 20, 8, 64, { 0x87,0xAA,0x7C,0xDE, 0xA5,0xEF,0x61,0x9D, 0x4F,0xF0,0xB4,0x24, 0x1A,0x1D,0x6C,0xB0, + 0x23,0x79,0xF4,0xE2, 0xCE,0x4E,0xC2,0x78, 0x7A,0xD0,0xB3,0x05, 0x45,0xE1,0x7C,0xDE, + 0xDA,0xA8,0x33,0xB7, 0xD6,0xB8,0xA7,0x02, 0x03,0x8B,0x27,0x4E, 0xAE,0xA3,0xF4,0xE4, + 0xBE,0x9D,0x91,0x4E, 0xEB,0x61,0xF1,0x70, 0x2E,0x69,0x6C,0x20, 0x3A,0x12,0x68,0x54 } + }, + }, + }, + // test case 2 + { + "Jefe", + "what do ya want for nothing?", + { + { // murmurhash3 + 4, 28, 16, { 0xD9,0x8B,0x6A,0x6D, 0x61,0x65,0xCB,0xEF, 0x42,0xA4,0x66,0x71, 0x5B,0xD1,0xFE,0xEF } + }, + { // md5 + 4, 28, 16, { 0x75,0x0C,0x78,0x3E, 0x6A,0xB0,0xB5,0x03, 0xEA,0xA8,0x6E,0x31, 0x0A,0x5D,0xB7,0x38 } + }, + { // sha1 + 4, 28, 20, { 0xEF,0xFC,0xDF,0x6A, 0xE5,0xEB,0x2F,0xA2, 0xD2,0x74,0x16,0xD5, 0xF1,0x84,0xDF,0x9C, + 0x25,0x9A,0x7C,0x79 } + }, + { // sha224 + 4, 28, 28, { 0xA3,0x0E,0x01,0x09, 0x8B,0xC6,0xDB,0xBF, 0x45,0x69,0x0F,0x3A, 0x7E,0x9E,0x6D,0x0F, + 0x8B,0xBE,0xA2,0xA3, 0x9E,0x61,0x48,0x00, 0x8F,0xD0,0x5E,0x44 } + }, + { // sha256 + 4, 28, 32, { 0x5B,0xDC,0xC1,0x46, 0xBF,0x60,0x75,0x4E, 0x6A,0x04,0x24,0x26, 0x08,0x95,0x75,0xC7, + 0x5A,0x00,0x3F,0x08, 0x9D,0x27,0x39,0x83, 0x9D,0xEC,0x58,0xB9, 0x64,0xEC,0x38,0x43 } + }, + { // sha384 + 4, 28, 48, { 0xAF,0x45,0xD2,0xE3, 0x76,0x48,0x40,0x31, 0x61,0x7F,0x78,0xD2, 0xB5,0x8A,0x6B,0x1B, + 0x9C,0x7E,0xF4,0x64, 0xF5,0xA0,0x1B,0x47, 0xE4,0x2E,0xC3,0x73, 0x63,0x22,0x44,0x5E, + 0x8E,0x22,0x40,0xCA, 0x5E,0x69,0xE2,0xC7, 0x8B,0x32,0x39,0xEC, 0xFA,0xB2,0x16,0x49 } + }, + { // sha512 + 4, 28, 64, { 0x16,0x4B,0x7A,0x7B, 0xFC,0xF8,0x19,0xE2, 0xE3,0x95,0xFB,0xE7, 0x3B,0x56,0xE0,0xA3, + 0x87,0xBD,0x64,0x22, 0x2E,0x83,0x1F,0xD6, 0x10,0x27,0x0C,0xD7, 0xEA,0x25,0x05,0x54, + 0x97,0x58,0xBF,0x75, 0xC0,0x5A,0x99,0x4A, 0x6D,0x03,0x4F,0x65, 0xF8,0xF0,0xE6,0xFD, + 0xCA,0xEA,0xB1,0xA3, 0x4D,0x4A,0x6B,0x4B, 0x63,0x6E,0x07,0x0A, 0x38,0xBC,0xE7,0x37 } + } + } + }, + // test case 3 + { + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa", + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd", + { + { 0, 0, 0, { 0 } }, // murmurhash3 + { // md5 + 16, 50, 16, { 0x56,0xbe,0x34,0x52, 0x1d,0x14,0x4c,0x88, 0xdb,0xb8,0xc7,0x33, 0xf0,0xe8,0xb3,0xf6 } + }, + { // sha1 + 20, 50, 20, { 0x12,0x5d,0x73,0x42, 0xb9,0xac,0x11,0xcd, 0x91,0xa3,0x9a,0xf4, 0x8a,0xa1,0x7b,0x4f, + 0x63,0xf1,0x75,0xd3 } + }, + { // sha224 + 20, 50, 20, { 0x7f,0xb3,0xcb,0x35, 0x88,0xc6,0xc1,0xf6, 0xff,0xa9,0x69,0x4d, 0x7d,0x6a,0xd2,0x64, + 0x93,0x65,0xb0,0xc1, 0xf6,0x5d,0x69,0xd1, 0xec,0x83,0x33,0xea } + }, + { // sha256 + 20, 50, 20, { 0x77,0x3e,0xa9,0x1e, 0x36,0x80,0x0e,0x46, 0x85,0x4d,0xb8,0xeb, 0xd0,0x91,0x81,0xa7, + 0x29,0x59,0x09,0x8b, 0x3e,0xf8,0xc1,0x22, 0xd9,0x63,0x55,0x14, 0xce,0xd5,0x65,0xfe } + }, + { // sha256 + 20, 50, 20, { 0x88,0x06,0x26,0x08, 0xd3,0xe6,0xad,0x8a, 0x0a,0xa2,0xac,0xe0, 0x14,0xc8,0xa8,0x6f, + 0x0a,0xa6,0x35,0xd9, 0x47,0xac,0x9f,0xeb, 0xe8,0x3e,0xf4,0xe5, 0x59,0x66,0x14,0x4b, + 0x2a,0x5a,0xb3,0x9d, 0xc1,0x38,0x14,0xb9, 0x4e,0x3a,0xb6,0xe1, 0x01,0xa3,0x4f,0x27 } + }, + { // sha512 + 20, 50, 20, { 0xfa,0x73,0xb0,0x08, 0x9d,0x56,0xa2,0x84, 0xef,0xb0,0xf0,0x75, 0x6c,0x89,0x0b,0xe9, + 0xb1,0xb5,0xdb,0xdd, 0x8e,0xe8,0x1a,0x36, 0x55,0xf8,0x3e,0x33, 0xb2,0x27,0x9d,0x39, + 0xbf,0x3e,0x84,0x82, 0x79,0xa7,0x22,0xc8, 0x06,0xb4,0x85,0xa4, 0x7e,0x67,0xc8,0x07, + 0xb9,0x46,0xa3,0x37, 0xbe,0xe8,0x94,0x26, 0x74,0x27,0x88,0x59, 0xe1,0x32,0x92,0xfb } + } + } + }, + // test case 4 + { + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd", + { + { 0, 0, 0, { 0 } }, // murmurhash3 + { // md5 + 25, 50, 16, { 0x69,0x7e,0xaf,0x0a, 0xca,0x3a,0x3a,0xea, 0x3a,0x75,0x16,0x47, 0x46,0xff,0xaa,0x79 } + }, + { // sha1 + 25, 50, 20, { 0x4c,0x90,0x07,0xf4, 0x02,0x62,0x50,0xc6, 0xbc,0x84,0x14,0xf9, 0xbf,0x50,0xc8,0x6c, + 0x2d,0x72,0x35,0xda } + }, + { // sha224 + 25, 50, 28, { 0x6c,0x11,0x50,0x68, 0x74,0x01,0x3c,0xac, 0x6a,0x2a,0xbc,0x1b, 0xb3,0x82,0x62,0x7c, + 0xec,0x6a,0x90,0xd8, 0x6e,0xfc,0x01,0x2d, 0xe7,0xaf,0xec,0x5a } + }, + { // sha256 + 25, 50, 32, { 0x82,0x55,0x8a,0x38, 0x9a,0x44,0x3c,0x0e, 0xa4,0xcc,0x81,0x98, 0x99,0xf2,0x08,0x3a, + 0x85,0xf0,0xfa,0xa3, 0xe5,0x78,0xf8,0x07, 0x7a,0x2e,0x3f,0xf4, 0x67,0x29,0x66,0x5b } + }, + { // sha384 + 25, 50, 48, { 0x3e,0x8a,0x69,0xb7, 0x78,0x3c,0x25,0x85, 0x19,0x33,0xab,0x62, 0x90,0xaf,0x6c,0xa7, + 0x7a,0x99,0x81,0x48, 0x08,0x50,0x00,0x9c, 0xc5,0x57,0x7c,0x6e, 0x1f,0x57,0x3b,0x4e, + 0x68,0x01,0xdd,0x23, 0xc4,0xa7,0xd6,0x79, 0xcc,0xf8,0xa3,0x86, 0xc6,0x74,0xcf,0xfb } + }, + { // sha512 + 25, 50, 64, { 0xb0,0xba,0x46,0x56, 0x37,0x45,0x8c,0x69, 0x90,0xe5,0xa8,0xc5, 0xf6,0x1d,0x4a,0xf7, + 0xe5,0x76,0xd9,0x7f, 0xf9,0x4b,0x87,0x2d, 0xe7,0x6f,0x80,0x50, 0x36,0x1e,0xe3,0xdb, + 0xa9,0x1c,0xa5,0xc1, 0x1a,0xa2,0x5e,0xb4, 0xd6,0x79,0x27,0x5c, 0xc5,0x78,0x80,0x63, + 0xa5,0xf1,0x97,0x41, 0x12,0x0c,0x4f,0x2d, 0xe2,0xad,0xeb,0xeb, 0x10,0xa2,0x98,0xdd } + } + } + }, + // test case 5 + { + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + "Test With Truncation", + { + { 0, 0, 0, { 0 } }, // murmurhash3 + { // md5 + 16, 20, 12, { 0x56,0x46,0x1e,0xf2, 0x34,0x2e,0xdc,0x00, 0xf9,0xba,0xb9,0x95, 0x69,0x0e,0xfd,0x4c } + }, + { // sha1 + 20, 20, 16, { 0x4c,0x1a,0x03,0x42, 0x4b,0x55,0xe0,0x7f, 0xe7,0xf2,0x7b,0xe1, 0xd5,0x8b,0xb9,0x32 } + }, + { // sha224 + 20, 20, 16, { 0x0e,0x2a,0xea,0x68, 0xa9,0x0c,0x8d,0x37, 0xc9,0x88,0xbc,0xdb, 0x9f,0xca,0x6f,0xa8 } + }, + { // sha256 + 20, 20, 16, { 0xa3,0xb6,0x16,0x74, 0x73,0x10,0x0e,0xe0, 0x6e,0x0c,0x79,0x6c, 0x29,0x55,0x55,0x2b } + }, + { // sha384 + 20, 20, 16, { 0x3a,0xbf,0x34,0xc3, 0x50,0x3b,0x2a,0x23, 0xa4,0x6e,0xfc,0x61, 0x9b,0xae,0xf8,0x97 } + }, + { // sha512 + 20, 20, 16, { 0x41,0x5f,0xad,0x62, 0x71,0x58,0x0a,0x53, 0x1d,0x41,0x79,0xbc, 0x89,0x1d,0x87,0xa6 } + } + } + }, + // test case 6 + { + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + "Test Using Larger Than Block-Size Key - Hash Key First", + { + { 0, 0, 0, { 0 } }, // murmurhash3 + { // md5 + 80, 54, 16, { 0x6b,0x1a,0xb7,0xfe, 0x4b,0xd7,0xbf,0x8f, 0x0b,0x62,0xe6,0xce, 0x61,0xb9,0xd0,0xcd } + }, + { // sha1 + 80, 54, 20, { 0xaa,0x4a,0xe5,0xe1, 0x52,0x72,0xd0,0x0e, 0x95,0x70,0x56,0x37, 0xce,0x8a,0x3b,0x55, + 0xed,0x40,0x21,0x12 } + }, + { // sha224 + 131, 54, 28, { 0x95,0xe9,0xa0,0xdb, 0x96,0x20,0x95,0xad, 0xae,0xbe,0x9b,0x2d, 0x6f,0x0d,0xbc,0xe2, + 0xd4,0x99,0xf1,0x12, 0xf2,0xd2,0xb7,0x27, 0x3f,0xa6,0x87,0x0e } + }, + { // sha256 + 131, 54, 32, { 0x60,0xe4,0x31,0x59, 0x1e,0xe0,0xb6,0x7f, 0x0d,0x8a,0x26,0xaa, 0xcb,0xf5,0xb7,0x7f, + 0x8e,0x0b,0xc6,0x21, 0x37,0x28,0xc5,0x14, 0x05,0x46,0x04,0x0f, 0x0e,0xe3,0x7f,0x54 } + }, + { // sha384 + 131, 54, 48, { 0x4e,0xce,0x08,0x44, 0x85,0x81,0x3e,0x90, 0x88,0xd2,0xc6,0x3a, 0x04,0x1b,0xc5,0xb4, + 0x4f,0x9e,0xf1,0x01, 0x2a,0x2b,0x58,0x8f, 0x3c,0xd1,0x1f,0x05, 0x03,0x3a,0xc4,0xc6, + 0x0c,0x2e,0xf6,0xab, 0x40,0x30,0xfe,0x82, 0x96,0x24,0x8d,0xf1, 0x63,0xf4,0x49,0x52 } + }, + { // sha512 + 131, 54, 64, { 0x80,0xb2,0x42,0x63, 0xc7,0xc1,0xa3,0xeb, 0xb7,0x14,0x93,0xc1, 0xdd,0x7b,0xe8,0xb4, + 0x9b,0x46,0xd1,0xf4, 0x1b,0x4a,0xee,0xc1, 0x12,0x1b,0x01,0x37, 0x83,0xf8,0xf3,0x52, + 0x6b,0x56,0xd0,0x37, 0xe0,0x5f,0x25,0x98, 0xbd,0x0f,0xd2,0x21, 0x5d,0x6a,0x1e,0x52, + 0x95,0xe6,0x4f,0x73, 0xf6,0x3f,0x0a,0xec, 0x8b,0x91,0x5a,0x98, 0x5d,0x78,0x65,0x98 } + } + }, + }, + // test case 7 for md5/sha1 + { + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", + { + { 0, 0, 0, { 0 } }, // murmurhash3 + { // md5 + 80, 73, 16, { 0x6f,0x63,0x0f,0xad, 0x67,0xcd,0xa0,0xee, 0x1f,0xb1,0xf5,0x62, 0xdb,0x3a,0xa5,0x3e } + }, + { // sha1 + 80, 73, 20, { 0xe8,0xe9,0x9d,0x0f, 0x45,0x23,0x7d,0x78, 0x6d,0x6b,0xba,0xa7, 0x96,0x5c,0x78,0x08, + 0xbb,0xff,0x1a,0x91 } + }, + { 0, 0, 0, { 0 } }, // sha224 + { 0, 0, 0, { 0 } }, // sha256 + { 0, 0, 0, { 0 } }, // sha384 + { 0, 0, 0, { 0 } }, // sha512 + } + }, + // test case 7 for sha2 + { + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa", + "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", + { + { 0, 0, 0, { 0 } }, // murmurhash3 + { 0, 0, 0, { 0 } }, // md5 + { 0, 0, 0, { 0 } }, // sha1 + { // sha224 + 131, 152, 28, { 0x3a,0x85,0x41,0x66, 0xac,0x5d,0x9f,0x02, 0x3f,0x54,0xd5,0x17, 0xd0,0xb3,0x9d,0xbd, + 0x94,0x67,0x70,0xdb, 0x9c,0x2b,0x95,0xc9, 0xf6,0xf5,0x65,0xd1 } + }, + { // sha256 + 131, 152, 28, { 0x9b,0x09,0xff,0xa7, 0x1b,0x94,0x2f,0xcb, 0x27,0x63,0x5f,0xbc, 0xd5,0xb0,0xe9,0x44, + 0xbf,0xdc,0x63,0x64, 0x4f,0x07,0x13,0x93, 0x8a,0x7f,0x51,0x53, 0x5c,0x3a,0x35,0xe2 } + }, + { // sha384 + 131, 152, 28, { 0x66,0x17,0x17,0x8e, 0x94,0x1f,0x02,0x0d, 0x35,0x1e,0x2f,0x25, 0x4e,0x8f,0xd3,0x2c, + 0x60,0x24,0x20,0xfe, 0xb0,0xb8,0xfb,0x9a, 0xdc,0xce,0xbb,0x82, 0x46,0x1e,0x99,0xc5, + 0xa6,0x78,0xcc,0x31, 0xe7,0x99,0x17,0x6d, 0x38,0x60,0xe6,0x11, 0x0c,0x46,0x52,0x3e } + }, + { // sha512 + 131, 152, 28, { 0xe3,0x7b,0x6a,0x77, 0x5d,0xc8,0x7d,0xba, 0xa4,0xdf,0xa9,0xf9, 0x6e,0x5e,0x3f,0xfd, + 0xde,0xbd,0x71,0xf8, 0x86,0x72,0x89,0x86, 0x5d,0xf5,0xa3,0x2d, 0x20,0xcd,0xc9,0x44, + 0xb6,0x02,0x2c,0xac, 0x3c,0x49,0x82,0xb1, 0x0d,0x5e,0xeb,0x55, 0xc3,0xe4,0xde,0x15, + 0x13,0x46,0x76,0xfb, 0x6d,0xe0,0x44,0x60, 0x65,0xc9,0x74,0x40, 0xfa,0x8c,0x6a,0x58 } + } + } + } +}; + +//! test vectors from https://tools.ietf.org/html/rfc6979#appendix-A.2 +static const CryptTestDsaT _Crypt_DsaTests[] = +{ + { + CRYPTCURVE_SECP256R1, 32, "secp256r1", + { 0xC9, 0xAF, 0xA9, 0xD8, 0x45, 0xBA, 0x75, 0x16, 0x6B, 0x5C, 0x21, 0x57, 0x67, 0xB1, 0xD6, 0x93, + 0x4E, 0x50, 0xC3, 0xDB, 0x36, 0xE8, 0x9B, 0x12, 0x7B, 0x8A, 0x62, 0x2B, 0x12, 0x0F, 0x67, 0x21 }, + { 0x04, 0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D, 0x31, 0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35, 0x6D, 0x68, + 0xC0, 0x49, 0xB8, 0x92, 0x3B, 0x61, 0xFA, 0x6C, 0xE6, 0x69, 0x62, 0x2E, 0x60, 0xF2, 0x9F, 0xB6, + 0x79, 0x03, 0xFE, 0x10, 0x08, 0xB8, 0xBC, 0x99, 0xA4, 0x1A, 0xE9, 0xE9, 0x56, 0x28, 0xBC, 0x64, + 0xF2, 0xF1, 0xB2, 0x0C, 0x2D, 0x7E, 0x9F, 0x51, 0x77, 0xA3, 0xC2, 0x94, 0xD4, 0x46, 0x22, 0x99 }, + { + { "sample", CRYPTHASH_SHA1, "sha1", + { 0x61, 0x34, 0x0C, 0x88, 0xC3, 0xAA, 0xEB, 0xEB, 0x4F, 0x6D, 0x66, 0x7F, 0x67, 0x2C, 0xA9, 0x75, + 0x9A, 0x6C, 0xCA, 0xA9, 0xFA, 0x88, 0x11, 0x31, 0x30, 0x39, 0xEE, 0x4A, 0x35, 0x47, 0x1D, 0x32, + 0x6D, 0x7F, 0x14, 0x7D, 0xAC, 0x08, 0x94, 0x41, 0xBB, 0x2E, 0x2F, 0xE8, 0xF7, 0xA3, 0xFA, 0x26, + 0x4B, 0x9C, 0x47, 0x50, 0x98, 0xFD, 0xCF, 0x6E, 0x00, 0xD7, 0xC9, 0x96, 0xE1, 0xB8, 0xB7, 0xEB } + }, + { "sample", CRYPTHASH_SHA224, "sha224", + { 0x53, 0xB2, 0xFF, 0xF5, 0xD1, 0x75, 0x2B, 0x2C, 0x68, 0x9D, 0xF2, 0x57, 0xC0, 0x4C, 0x40, 0xA5, + 0x87, 0xFA, 0xBA, 0xBB, 0x3F, 0x6F, 0xC2, 0x70, 0x2F, 0x13, 0x43, 0xAF, 0x7C, 0xA9, 0xAA, 0x3F, + 0xB9, 0xAF, 0xB6, 0x4F, 0xDC, 0x03, 0xDC, 0x1A, 0x13, 0x1C, 0x7D, 0x23, 0x86, 0xD1, 0x1E, 0x34, + 0x9F, 0x07, 0x0A, 0xA4, 0x32, 0xA4, 0xAC, 0xC9, 0x18, 0xBE, 0xA9, 0x88, 0xBF, 0x75, 0xC7, 0x4C } + }, + { "sample", CRYPTHASH_SHA256, "sha256", + { 0xEF, 0xD4, 0x8B, 0x2A, 0xAC, 0xB6, 0xA8, 0xFD, 0x11, 0x40, 0xDD, 0x9C, 0xD4, 0x5E, 0x81, 0xD6, + 0x9D, 0x2C, 0x87, 0x7B, 0x56, 0xAA, 0xF9, 0x91, 0xC3, 0x4D, 0x0E, 0xA8, 0x4E, 0xAF, 0x37, 0x16, + 0xF7, 0xCB, 0x1C, 0x94, 0x2D, 0x65, 0x7C, 0x41, 0xD4, 0x36, 0xC7, 0xA1, 0xB6, 0xE2, 0x9F, 0x65, + 0xF3, 0xE9, 0x00, 0xDB, 0xB9, 0xAF, 0xF4, 0x06, 0x4D, 0xC4, 0xAB, 0x2F, 0x84, 0x3A, 0xCD, 0xA8 } + }, + { "sample", CRYPTHASH_SHA384, "sha384", + { 0x0E, 0xAF, 0xEA, 0x03, 0x9B, 0x20, 0xE9, 0xB4, 0x23, 0x09, 0xFB, 0x1D, 0x89, 0xE2, 0x13, 0x05, + 0x7C, 0xBF, 0x97, 0x3D, 0xC0, 0xCF, 0xC8, 0xF1, 0x29, 0xED, 0xDD, 0xC8, 0x00, 0xEF, 0x77, 0x19, + 0x48, 0x61, 0xF0, 0x49, 0x1E, 0x69, 0x98, 0xB9, 0x45, 0x51, 0x93, 0xE3, 0x4E, 0x7B, 0x0D, 0x28, + 0x4D, 0xDD, 0x71, 0x49, 0xA7, 0x4B, 0x95, 0xB9, 0x26, 0x1F, 0x13, 0xAB, 0xDE, 0x94, 0x09, 0x54 } + }, + { "sample", CRYPTHASH_SHA512, "sha512", + { 0x84, 0x96, 0xA6, 0x0B, 0x5E, 0x9B, 0x47, 0xC8, 0x25, 0x48, 0x88, 0x27, 0xE0, 0x49, 0x5B, 0x0E, + 0x3F, 0xA1, 0x09, 0xEC, 0x45, 0x68, 0xFD, 0x3F, 0x8D, 0x10, 0x97, 0x67, 0x8E, 0xB9, 0x7F, 0x00, + 0x23, 0x62, 0xAB, 0x1A, 0xDB, 0xE2, 0xB8, 0xAD, 0xF9, 0xCB, 0x9E, 0xDA, 0xB7, 0x40, 0xEA, 0x60, + 0x49, 0xC0, 0x28, 0x11, 0x4F, 0x24, 0x60, 0xF9, 0x65, 0x54, 0xF6, 0x1F, 0xAE, 0x33, 0x02, 0xFE } + }, + { "test", CRYPTHASH_SHA1, "sha1", + { 0x0C, 0xBC, 0xC8, 0x6F, 0xD6, 0xAB, 0xD1, 0xD9, 0x9E, 0x70, 0x3E, 0x1E, 0xC5, 0x00, 0x69, 0xEE, + 0x5C, 0x0B, 0x4B, 0xA4, 0xB9, 0xAC, 0x60, 0xE4, 0x09, 0xE8, 0xEC, 0x59, 0x10, 0xD8, 0x1A, 0x89, + 0x01, 0xB9, 0xD7, 0xB7, 0x3D, 0xFA, 0xA6, 0x0D, 0x56, 0x51, 0xEC, 0x45, 0x91, 0xA0, 0x13, 0x6F, + 0x87, 0x65, 0x3E, 0x0F, 0xD7, 0x80, 0xC3, 0xB1, 0xBC, 0x87, 0x2F, 0xFD, 0xEA, 0xE4, 0x79, 0xB1 } + }, + { "test", CRYPTHASH_SHA224, "sha224", + { 0xC3, 0x7E, 0xDB, 0x6F, 0x0A, 0xE7, 0x9D, 0x47, 0xC3, 0xC2, 0x7E, 0x96, 0x2F, 0xA2, 0x69, 0xBB, + 0x4F, 0x44, 0x17, 0x70, 0x35, 0x7E, 0x11, 0x4E, 0xE5, 0x11, 0xF6, 0x62, 0xEC, 0x34, 0xA6, 0x92, + 0xC8, 0x20, 0x05, 0x3A, 0x05, 0x79, 0x1E, 0x52, 0x1F, 0xCA, 0xAD, 0x60, 0x42, 0xD4, 0x0A, 0xEA, + 0x1D, 0x6B, 0x1A, 0x54, 0x01, 0x38, 0x55, 0x8F, 0x47, 0xD0, 0x71, 0x98, 0x00, 0xE1, 0x8F, 0x2D } + }, + { "test", CRYPTHASH_SHA256, "sha256", + { 0xF1, 0xAB, 0xB0, 0x23, 0x51, 0x83, 0x51, 0xCD, 0x71, 0xD8, 0x81, 0x56, 0x7B, 0x1E, 0xA6, 0x63, + 0xED, 0x3E, 0xFC, 0xF6, 0xC5, 0x13, 0x2B, 0x35, 0x4F, 0x28, 0xD3, 0xB0, 0xB7, 0xD3, 0x83, 0x67, + 0x01, 0x9F, 0x41, 0x13, 0x74, 0x2A, 0x2B, 0x14, 0xBD, 0x25, 0x92, 0x6B, 0x49, 0xC6, 0x49, 0x15, + 0x5F, 0x26, 0x7E, 0x60, 0xD3, 0x81, 0x4B, 0x4C, 0x0C, 0xC8, 0x42, 0x50, 0xE4, 0x6F, 0x00, 0x83 } + }, + { "test", CRYPTHASH_SHA384, "sha384", + { 0x83, 0x91, 0x0E, 0x8B, 0x48, 0xBB, 0x0C, 0x74, 0x24, 0x4E, 0xBD, 0xF7, 0xF0, 0x7A, 0x1C, 0x54, + 0x13, 0xD6, 0x14, 0x72, 0xBD, 0x94, 0x1E, 0xF3, 0x92, 0x0E, 0x62, 0x3F, 0xBC, 0xCE, 0xBE, 0xB6, + 0x8D, 0xDB, 0xEC, 0x54, 0xCF, 0x8C, 0xD5, 0x87, 0x48, 0x83, 0x84, 0x1D, 0x71, 0x21, 0x42, 0xA5, + 0x6A, 0x8D, 0x0F, 0x21, 0x8F, 0x50, 0x03, 0xCB, 0x02, 0x96, 0xB6, 0xB5, 0x09, 0x61, 0x9F, 0x2C } + }, + { "test", CRYPTHASH_SHA512, "sha512", + { 0x46, 0x1D, 0x93, 0xF3, 0x1B, 0x65, 0x40, 0x89, 0x47, 0x88, 0xFD, 0x20, 0x6C, 0x07, 0xCF, 0xA0, + 0xCC, 0x35, 0xF4, 0x6F, 0xA3, 0xC9, 0x18, 0x16, 0xFF, 0xF1, 0x04, 0x0A, 0xD1, 0x58, 0x1A, 0x04, + 0x39, 0xAF, 0x9F, 0x15, 0xDE, 0x0D, 0xB8, 0xD9, 0x7E, 0x72, 0x71, 0x9C, 0x74, 0x82, 0x0D, 0x30, + 0x4C, 0xE5, 0x22, 0x6E, 0x32, 0xDE, 0xDA, 0xE6, 0x75, 0x19, 0xE8, 0x40, 0xD1, 0x19, 0x4E, 0x55 } + }, + } + }, + { + CRYPTCURVE_SECP384R1, 48, "secp384r1", + { 0x6B, 0x9D, 0x3D, 0xAD, 0x2E, 0x1B, 0x8C, 0x1C, 0x05, 0xB1, 0x98, 0x75, 0xB6, 0x65, 0x9F, 0x4D, + 0xE2, 0x3C, 0x3B, 0x66, 0x7B, 0xF2, 0x97, 0xBA, 0x9A, 0xA4, 0x77, 0x40, 0x78, 0x71, 0x37, 0xD8, + 0x96, 0xD5, 0x72, 0x4E, 0x4C, 0x70, 0xA8, 0x25, 0xF8, 0x72, 0xC9, 0xEA, 0x60, 0xD2, 0xED, 0xF5 }, + { 0x04, 0xEC, 0x3A, 0x4E, 0x41, 0x5B, 0x4E, 0x19, 0xA4, 0x56, 0x86, 0x18, 0x02, 0x9F, 0x42, 0x7F, 0xA5, + 0xDA, 0x9A, 0x8B, 0xC4, 0xAE, 0x92, 0xE0, 0x2E, 0x06, 0xAA, 0xE5, 0x28, 0x6B, 0x30, 0x0C, 0x64, + 0xDE, 0xF8, 0xF0, 0xEA, 0x90, 0x55, 0x86, 0x60, 0x64, 0xA2, 0x54, 0x51, 0x54, 0x80, 0xBC, 0x13, + 0x80, 0x15, 0xD9, 0xB7, 0x2D, 0x7D, 0x57, 0x24, 0x4E, 0xA8, 0xEF, 0x9A, 0xC0, 0xC6, 0x21, 0x89, + 0x67, 0x08, 0xA5, 0x93, 0x67, 0xF9, 0xDF, 0xB9, 0xF5, 0x4C, 0xA8, 0x4B, 0x3F, 0x1C, 0x9D, 0xB1, + 0x28, 0x8B, 0x23, 0x1C, 0x3A, 0xE0, 0xD4, 0xFE, 0x73, 0x44, 0xFD, 0x25, 0x33, 0x26, 0x47, 0x20 }, + { + { "sample", CRYPTHASH_SHA1, "sha1", + { 0xEC, 0x74, 0x8D, 0x83, 0x92, 0x43, 0xD6, 0xFB, 0xEF, 0x4F, 0xC5, 0xC4, 0x85, 0x9A, 0x7D, 0xFF, + 0xD7, 0xF3, 0xAB, 0xDD, 0xF7, 0x20, 0x14, 0x54, 0x0C, 0x16, 0xD7, 0x33, 0x09, 0x83, 0x4F, 0xA3, + 0x7B, 0x9B, 0xA0, 0x02, 0x89, 0x9F, 0x6F, 0xDA, 0x3A, 0x4A, 0x93, 0x86, 0x79, 0x0D, 0x4E, 0xB2, + 0xA3, 0xBC, 0xFA, 0x94, 0x7B, 0xEE, 0xF4, 0x73, 0x2B, 0xF2, 0x47, 0xAC, 0x17, 0xF7, 0x16, 0x76, + 0xCB, 0x31, 0xA8, 0x47, 0xB9, 0xFF, 0x0C, 0xBC, 0x9C, 0x9E, 0xD4, 0xC1, 0xA5, 0xB3, 0xFA, 0xCF, + 0x26, 0xF4, 0x9C, 0xA0, 0x31, 0xD4, 0x85, 0x75, 0x70, 0xCC, 0xB5, 0xCA, 0x44, 0x24, 0xA4, 0x43 } + }, + { "sample", CRYPTHASH_SHA224, "sha224", + { 0x42, 0x35, 0x6E, 0x76, 0xB5, 0x5A, 0x6D, 0x9B, 0x46, 0x31, 0xC8, 0x65, 0x44, 0x5D, 0xBE, 0x54, + 0xE0, 0x56, 0xD3, 0xB3, 0x43, 0x17, 0x66, 0xD0, 0x50, 0x92, 0x44, 0x79, 0x3C, 0x3F, 0x93, 0x66, + 0x45, 0x0F, 0x76, 0xEE, 0x3D, 0xE4, 0x3F, 0x5A, 0x12, 0x53, 0x33, 0xA6, 0xBE, 0x06, 0x01, 0x22, + 0x9D, 0xA0, 0xC8, 0x17, 0x87, 0x06, 0x40, 0x21, 0xE7, 0x8D, 0xF6, 0x58, 0xF2, 0xFB, 0xB0, 0xB0, + 0x42, 0xBF, 0x30, 0x46, 0x65, 0xDB, 0x72, 0x1F, 0x07, 0x7A, 0x42, 0x98, 0xB0, 0x95, 0xE4, 0x83, + 0x4C, 0x08, 0x2C, 0x03, 0xD8, 0x30, 0x28, 0xEF, 0xBF, 0x93, 0xA3, 0xC2, 0x39, 0x40, 0xCA, 0x8D } + }, + { "sample", CRYPTHASH_SHA256, "sha256", + { 0x21, 0xB1, 0x3D, 0x1E, 0x01, 0x3C, 0x7F, 0xA1, 0x39, 0x2D, 0x03, 0xC5, 0xF9, 0x9A, 0xF8, 0xB3, + 0x0C, 0x57, 0x0C, 0x6F, 0x98, 0xD4, 0xEA, 0x8E, 0x35, 0x4B, 0x63, 0xA2, 0x1D, 0x3D, 0xAA, 0x33, + 0xBD, 0xE1, 0xE8, 0x88, 0xE6, 0x33, 0x55, 0xD9, 0x2F, 0xA2, 0xB3, 0xC3, 0x6D, 0x8F, 0xB2, 0xCD, + 0xF3, 0xAA, 0x44, 0x3F, 0xB1, 0x07, 0x74, 0x5B, 0xF4, 0xBD, 0x77, 0xCB, 0x38, 0x91, 0x67, 0x46, + 0x32, 0x06, 0x8A, 0x10, 0xCA, 0x67, 0xE3, 0xD4, 0x5D, 0xB2, 0x26, 0x6F, 0xA7, 0xD1, 0xFE, 0xEB, + 0xEF, 0xDC, 0x63, 0xEC, 0xCD, 0x1A, 0xC4, 0x2E, 0xC0, 0xCB, 0x86, 0x68, 0xA4, 0xFA, 0x0A, 0xB0 } + }, + { "sample", CRYPTHASH_SHA384, "sha384", + { 0x94, 0xED, 0xBB, 0x92, 0xA5, 0xEC, 0xB8, 0xAA, 0xD4, 0x73, 0x6E, 0x56, 0xC6, 0x91, 0x91, 0x6B, + 0x3F, 0x88, 0x14, 0x06, 0x66, 0xCE, 0x9F, 0xA7, 0x3D, 0x64, 0xC4, 0xEA, 0x95, 0xAD, 0x13, 0x3C, + 0x81, 0xA6, 0x48, 0x15, 0x2E, 0x44, 0xAC, 0xF9, 0x6E, 0x36, 0xDD, 0x1E, 0x80, 0xFA, 0xBE, 0x46, + 0x99, 0xEF, 0x4A, 0xEB, 0x15, 0xF1, 0x78, 0xCE, 0xA1, 0xFE, 0x40, 0xDB, 0x26, 0x03, 0x13, 0x8F, + 0x13, 0x0E, 0x74, 0x0A, 0x19, 0x62, 0x45, 0x26, 0x20, 0x3B, 0x63, 0x51, 0xD0, 0xA3, 0xA9, 0x4F, + 0xA3, 0x29, 0xC1, 0x45, 0x78, 0x6E, 0x67, 0x9E, 0x7B, 0x82, 0xC7, 0x1A, 0x38, 0x62, 0x8A, 0xC8 } + }, + { "sample", CRYPTHASH_SHA512, "sha512", + { 0xED, 0x09, 0x59, 0xD5, 0x88, 0x0A, 0xB2, 0xD8, 0x69, 0xAE, 0x7F, 0x6C, 0x29, 0x15, 0xC6, 0xD6, + 0x0F, 0x96, 0x50, 0x7F, 0x9C, 0xB3, 0xE0, 0x47, 0xC0, 0x04, 0x68, 0x61, 0xDA, 0x4A, 0x79, 0x9C, + 0xFE, 0x30, 0xF3, 0x5C, 0xC9, 0x00, 0x05, 0x6D, 0x7C, 0x99, 0xCD, 0x78, 0x82, 0x43, 0x37, 0x09, + 0x51, 0x2C, 0x8C, 0xCE, 0xEE, 0x38, 0x90, 0xA8, 0x40, 0x58, 0xCE, 0x1E, 0x22, 0xDB, 0xC2, 0x19, + 0x8F, 0x42, 0x32, 0x3C, 0xE8, 0xAC, 0xA9, 0x13, 0x53, 0x29, 0xF0, 0x3C, 0x06, 0x8E, 0x51, 0x12, + 0xDC, 0x7C, 0xC3, 0xEF, 0x34, 0x46, 0xDE, 0xFC, 0xEB, 0x01, 0xA4, 0x5C, 0x26, 0x67, 0xFD, 0xD5 } + }, + { "test", CRYPTHASH_SHA1, "sha1", + { 0x4B, 0xC3, 0x5D, 0x3A, 0x50, 0xEF, 0x4E, 0x30, 0x57, 0x6F, 0x58, 0xCD, 0x96, 0xCE, 0x6B, 0xF6, + 0x38, 0x02, 0x5E, 0xE6, 0x24, 0x00, 0x4A, 0x1F, 0x77, 0x89, 0xA8, 0xB8, 0xE4, 0x3D, 0x06, 0x78, + 0xAC, 0xD9, 0xD2, 0x98, 0x76, 0xDA, 0xF4, 0x66, 0x38, 0x64, 0x5F, 0x7F, 0x40, 0x4B, 0x11, 0xC7, + 0xD5, 0xA6, 0x32, 0x6C, 0x49, 0x4E, 0xD3, 0xFF, 0x61, 0x47, 0x03, 0x87, 0x89, 0x61, 0xC0, 0xFD, + 0xE7, 0xB2, 0xC2, 0x78, 0xF9, 0xA6, 0x5F, 0xD8, 0xC4, 0xB7, 0x18, 0x62, 0x01, 0xA2, 0x99, 0x16, + 0x95, 0xBA, 0x1C, 0x84, 0x54, 0x13, 0x27, 0xE9, 0x66, 0xFA, 0x7B, 0x50, 0xF7, 0x38, 0x22, 0x82 } + }, + { "test", CRYPTHASH_SHA224, "sha224", + { 0xE8, 0xC9, 0xD0, 0xB6, 0xEA, 0x72, 0xA0, 0xE7, 0x83, 0x7F, 0xEA, 0x1D, 0x14, 0xA1, 0xA9, 0x55, + 0x7F, 0x29, 0xFA, 0xA4, 0x5D, 0x3E, 0x7E, 0xE8, 0x88, 0xFC, 0x5B, 0xF9, 0x54, 0xB5, 0xE6, 0x24, + 0x64, 0xA9, 0xA8, 0x17, 0xC4, 0x7F, 0xF7, 0x8B, 0x8C, 0x11, 0x06, 0x6B, 0x24, 0x08, 0x0E, 0x72, + 0x07, 0x04, 0x1D, 0x4A, 0x7A, 0x03, 0x79, 0xAC, 0x72, 0x32, 0xFF, 0x72, 0xE6, 0xF7, 0x7B, 0x6D, + 0xDB, 0x8F, 0x09, 0xB1, 0x6C, 0xCE, 0x0E, 0xC3, 0x28, 0x6B, 0x2B, 0xD4, 0x3F, 0xA8, 0xC6, 0x14, + 0x1C, 0x53, 0xEA, 0x5A, 0xBE, 0xF0, 0xD8, 0x23, 0x10, 0x77, 0xA0, 0x45, 0x40, 0xA9, 0x6B, 0x66 } + }, + { "test", CRYPTHASH_SHA256, "sha256", + { 0x6D, 0x6D, 0xEF, 0xAC, 0x9A, 0xB6, 0x4D, 0xAB, 0xAF, 0xE3, 0x6C, 0x6B, 0xF5, 0x10, 0x35, 0x2A, + 0x4C, 0xC2, 0x70, 0x01, 0x26, 0x36, 0x38, 0xE5, 0xB1, 0x6D, 0x9B, 0xB5, 0x1D, 0x45, 0x15, 0x59, + 0xF9, 0x18, 0xEE, 0xDA, 0xF2, 0x29, 0x3B, 0xE5, 0xB4, 0x75, 0xCC, 0x8F, 0x01, 0x88, 0x63, 0x6B, + 0x2D, 0x46, 0xF3, 0xBE, 0xCB, 0xCC, 0x52, 0x3D, 0x5F, 0x1A, 0x12, 0x56, 0xBF, 0x0C, 0x9B, 0x02, + 0x4D, 0x87, 0x9B, 0xA9, 0xE8, 0x38, 0x14, 0x4C, 0x8B, 0xA6, 0xBA, 0xEB, 0x4B, 0x53, 0xB4, 0x7D, + 0x51, 0xAB, 0x37, 0x3F, 0x98, 0x45, 0xC0, 0x51, 0x4E, 0xEF, 0xB1, 0x40, 0x24, 0x78, 0x72, 0x65 } + }, + { "test", CRYPTHASH_SHA384, "sha384", + { 0x82, 0x03, 0xB6, 0x3D, 0x3C, 0x85, 0x3E, 0x8D, 0x77, 0x22, 0x7F, 0xB3, 0x77, 0xBC, 0xF7, 0xB7, + 0xB7, 0x72, 0xE9, 0x78, 0x92, 0xA8, 0x0F, 0x36, 0xAB, 0x77, 0x5D, 0x50, 0x9D, 0x7A, 0x5F, 0xEB, + 0x05, 0x42, 0xA7, 0xF0, 0x81, 0x29, 0x98, 0xDA, 0x8F, 0x1D, 0xD3, 0xCA, 0x3C, 0xF0, 0x23, 0xDB, + 0xDD, 0xD0, 0x76, 0x04, 0x48, 0xD4, 0x2D, 0x8A, 0x43, 0xAF, 0x45, 0xAF, 0x83, 0x6F, 0xCE, 0x4D, + 0xE8, 0xBE, 0x06, 0xB4, 0x85, 0xE9, 0xB6, 0x1B, 0x82, 0x7C, 0x2F, 0x13, 0x17, 0x39, 0x23, 0xE0, + 0x6A, 0x73, 0x9F, 0x04, 0x06, 0x49, 0xA6, 0x67, 0xBF, 0x3B, 0x82, 0x82, 0x46, 0xBA, 0xA5, 0xA5 } + }, + { "test", CRYPTHASH_SHA512, "sha512", + { 0xA0, 0xD5, 0xD0, 0x90, 0xC9, 0x98, 0x0F, 0xAF, 0x3C, 0x2C, 0xE5, 0x7B, 0x7A, 0xE9, 0x51, 0xD3, + 0x19, 0x77, 0xDD, 0x11, 0xC7, 0x75, 0xD3, 0x14, 0xAF, 0x55, 0xF7, 0x6C, 0x67, 0x64, 0x47, 0xD0, + 0x6F, 0xB6, 0x49, 0x5C, 0xD2, 0x1B, 0x4B, 0x6E, 0x34, 0x0F, 0xC2, 0x36, 0x58, 0x4F, 0xB2, 0x77, + 0x97, 0x69, 0x84, 0xE5, 0x9B, 0x4C, 0x77, 0xB0, 0xE8, 0xE4, 0x46, 0x0D, 0xCA, 0x3D, 0x9F, 0x20, + 0xE0, 0x7B, 0x9B, 0xB1, 0xF6, 0x3B, 0xEE, 0xFA, 0xF5, 0x76, 0xF6, 0xB2, 0xE8, 0xB2, 0x24, 0x63, + 0x4A, 0x20, 0x92, 0xCD, 0x37, 0x92, 0xE0, 0x15, 0x9A, 0xD9, 0xCE, 0xE3, 0x76, 0x59, 0xC7, 0x36 } + } + } + } +}; + +// buffer for large random number used in timing tests +static uint8_t _Crypt_aLargeRandom[2*1024*1024]; +const int32_t _Crypt_iTimingIter = 16; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestArc4 + + \Description + Test the CryptArc4 module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 01/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestArc4(ZContext *argz, int32_t argc, char *argv[]) +{ + static const char *_strEncrypt[] = + { + "Test first string encryption.", + "Another string to test encrypt.", + "Strings are fun to encrypt!", + "This string is exactly 127 characters long, sans null. It is meant to test the behavior of Encrypt/Decrypt with a full buffer.", + "This string is more than 127 characters long. It will safely truncate the result string, which will result in the test failing.", + }; + char strEncryptBuf[128], strDecryptBuf[128]; + uint8_t aEncryptKey[32]; + int32_t iFailed, iString; + + ZPrintf("%s: testing CryptArc4\n", argv[0]); + + // generate a random encryption key + CryptRandGet(aEncryptKey, sizeof(aEncryptKey)); + + // test string encryption/decryption + for (iString = 0, iFailed = 0; iString < (signed)(sizeof(_strEncrypt)/sizeof(_strEncrypt[0])); iString += 1) + { + CryptArc4StringEncrypt(strEncryptBuf, sizeof(strEncryptBuf), _strEncrypt[iString], aEncryptKey, sizeof(aEncryptKey), 1); + CryptArc4StringDecrypt(strDecryptBuf, sizeof(strDecryptBuf), strEncryptBuf, aEncryptKey, sizeof(aEncryptKey), 1); + ZPrintf("%s: '%s'->'%s'\n", argv[0], _strEncrypt[iString], strEncryptBuf); + if (strcmp(strDecryptBuf, _strEncrypt[iString])) + { + ZPrintf("%s: encrypt/decrypt failed; '%s' != '%s'\n", argv[0], _strEncrypt[iString], strDecryptBuf); + iFailed += 1; + } + } + + ZPrintf("%s: ---------------------\n", argv[0]); + + // one test intentionally fails, so subtract that out + iFailed -= 1; + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestGcm + + \Description + Test the CryptGcm module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 07/08/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestGcm(ZContext *argz, int32_t argc, char *argv[]) +{ + uint8_t aBuffer[1024], aTagRslt[16]; + int32_t iFail, iFailed, iTest; + const CryptTestGcmT *pTest; + CryptGcmT Gcm; + + ZPrintf("%s: testing CryptGcm\n", argv[0]); + + for (iTest = 0, iFailed = 0; iTest < (signed)(sizeof(_Crypt_GcmTest)/sizeof(_Crypt_GcmTest[0])); iTest += 1) + { + pTest = &_Crypt_GcmTest[iTest]; + iFail = 0; + + // do encryption + CryptGcmInit(&Gcm, pTest->aKey, pTest->iKeySize); + ds_memcpy(aBuffer, pTest->aInput, pTest->iInputSize); + CryptGcmEncrypt(&Gcm, aBuffer, pTest->iInputSize, pTest->aInitVec, sizeof(pTest->aInitVec), pTest->aData, pTest->iDataSize, aTagRslt, sizeof(aTagRslt)); + + if (memcmp(pTest->aTagRslt, aTagRslt, sizeof(pTest->aTagRslt))) + { + ZPrintf("%s: gcm encrypt test %d failed with invalid tag result\n", argv[0], iTest); + NetPrintMem(pTest->aTagRslt, sizeof(pTest->aTagRslt), "expected"); + NetPrintMem(aTagRslt, sizeof(pTest->aTagRslt), "actual"); + iFail |= 1; + } + if ((pTest->iInputSize != 0) && memcmp(pTest->aOutput, aBuffer, pTest->iOutputSize)) + { + ZPrintf("%s: gcm encrypt test %d failed with invalid output result\n", argv[0], iTest); + NetPrintMem(pTest->aOutput, pTest->iOutputSize, "expected"); + NetPrintMem(aBuffer, pTest->iOutputSize, "actual"); + iFail |= 1; + } + + // decrypt + CryptGcmInit(&Gcm, pTest->aKey, pTest->iKeySize); + CryptGcmDecrypt(&Gcm, aBuffer, pTest->iInputSize, pTest->aInitVec, sizeof(pTest->aInitVec), pTest->aData, pTest->iDataSize, aTagRslt, sizeof(aTagRslt)); + + if (memcmp(pTest->aTagRslt, aTagRslt, sizeof(pTest->aTagRslt))) + { + ZPrintf("%s: gcm decrypt test %d failed with invalid tag result\n", argv[0], iTest); + NetPrintMem(pTest->aTagRslt, sizeof(pTest->aTagRslt), "expected"); + NetPrintMem(aTagRslt, sizeof(pTest->aTagRslt), "actual"); + iFail |= 1; + } + if ((pTest->iInputSize != 0) && memcmp(pTest->aInput, aBuffer, pTest->iInputSize)) + { + ZPrintf("%s: gcm decrypt test %d failed with invalid output result\n", argv[0], iTest); + NetPrintMem(pTest->aInput, pTest->iInputSize, "expected"); + NetPrintMem(aBuffer, pTest->iOutputSize, "actual"); + iFail |= 1; + } + + ZPrintf("%s: test #%d %s\n", argv[0], iTest, iFail ? "failed" : "passed"); + + // add to failed count + iFailed += iFail; + } + + ZPrintf("%s: ---------------------\n", argv[0]); + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestChaCha + + \Description + Test the CryptChaCha module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestChaCha(ZContext *argz, int32_t argc, char *argv[]) +{ + CryptChaChaT ChaChaState; + int32_t iResult; + + // test vectors from https://tools.ietf.org/html/rfc7539#section-2.8.2 + static const uint8_t _aKey[] = + { + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f + }; + static const uint8_t _aNonce[] = + { + 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 + }; + static const uint8_t _aAddData[] = + { + 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7 + }; + uint8_t aTagBuf[16]; + static uint8_t aData[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; + + ZPrintf("%s: testing CryptChaCha\n", argv[0]); + + // encrypt the data + CryptChaChaInit(&ChaChaState, _aKey, sizeof(_aKey)); + CryptChaChaEncrypt(&ChaChaState, aData, (int32_t)sizeof(aData)-1, _aNonce, (int32_t)sizeof(_aNonce), _aAddData, (int32_t)sizeof(_aAddData), aTagBuf, (int32_t)sizeof(aTagBuf)); + + // decrypt and authenticate the data + CryptChaChaInit(&ChaChaState, _aKey, sizeof(_aKey)); + iResult = CryptChaChaDecrypt(&ChaChaState, aData, (int32_t)sizeof(aData)-1, _aNonce, (int32_t)sizeof(_aNonce), _aAddData, (int32_t)sizeof(_aAddData), aTagBuf, (int32_t)sizeof(aTagBuf)); + + ZPrintf("%s: ---------------------\n", argv[0]); + + return(iResult != -1 ? 0 : 1); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestMurmurHash3 + + \Description + Test MurmurHash3 + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 11/04/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestMurmurHash3(ZContext *argz, int32_t argc, char *argv[]) +{ + char strHashBuf0[16], strHashBuf1[16]; + int32_t iFailed = 0, iCount, iString, iDataSize, iNumIter; + uint32_t uStartTick; + const uint8_t aKeyBuf[16] = // init vector from MurmurHash3Init, little-endian 64bit words + { + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67, + 0x76,0x54,0x32,0x10,0xfe,0xdc,0xba,0x98, + }; + MurmurHash3T MurmurCtx; + + ZPrintf("%s: testing MurmurHash3 with a %d byte random buffer", argv[0], sizeof(_Crypt_aLargeRandom)); + + // test versions for compatibility; first the crypt-style Init/Update/Final + MurmurHash3Init(&MurmurCtx); + MurmurHash3Update(&MurmurCtx, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)); + MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0)); + // now the all-in-one-go version + MurmurHash3(strHashBuf1, sizeof(strHashBuf1), _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom), aKeyBuf, sizeof(aKeyBuf)); + // test result + if (memcmp(strHashBuf0, strHashBuf1, sizeof(strHashBuf0))) + { + ZPrintf("; hash failed!\n"); + NetPrintMem(strHashBuf0, sizeof(strHashBuf0), "hash0 result"); + NetPrintMem(strHashBuf1, sizeof(strHashBuf1), "hash1 result"); + iFailed += 1; + } + else + { + ZPrintf("; success\n"); + } + + ZPrintf("%s: testing MurmurHash3 with a %d byte random buffer and random sizes", argv[0], sizeof(_Crypt_aLargeRandom)); + + // now test crypt version with multiple updates that are not 16-byte aligned + MurmurHash3Init(&MurmurCtx); + for (iCount = 0, iNumIter = 0; iCount < (int32_t)sizeof(_Crypt_aLargeRandom); iCount += iDataSize, iNumIter += 1) + { + CryptRandGet((uint8_t *)&iDataSize, sizeof(iDataSize)); + iDataSize &= (sizeof(_Crypt_aLargeRandom)-1)/16; + if (iCount+iDataSize > (int32_t)sizeof(_Crypt_aLargeRandom)) + { + iDataSize = sizeof(_Crypt_aLargeRandom)-iCount; + } + MurmurHash3Update(&MurmurCtx, _Crypt_aLargeRandom+iCount, iDataSize); + } + MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0)); + // compare to previous result + if (memcmp(strHashBuf0, strHashBuf1, sizeof(strHashBuf0))) + { + ZPrintf("; hash failed!\n"); + NetPrintMem(strHashBuf0, sizeof(strHashBuf0), "hash0 result"); + NetPrintMem(strHashBuf1, sizeof(strHashBuf1), "hash1 result"); + iFailed += 1; + } + else + { + ZPrintf("; success (%d iterations)\n", iNumIter); + } + + // test all strings + for (iString = 0; iString < (signed)(sizeof(_Crypt_Sha2Test)/sizeof(_Crypt_Sha2Test[0])); iString += 1) + { + ZPrintf("%s: hashing \"%s\"", argv[0], _Crypt_Sha2Test[iString].pString); + + // test versions for compatibility; first the crypt-style Init/Update/Final + MurmurHash3Init(&MurmurCtx); + MurmurHash3Update(&MurmurCtx, (uint8_t *)_Crypt_Sha2Test[iString].pString, (uint32_t)strlen(_Crypt_Sha2Test[iString].pString)); + MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0)); + // now the all-in-one-go version + MurmurHash3(strHashBuf1, sizeof(strHashBuf1), (uint8_t *)_Crypt_Sha2Test[iString].pString, (uint32_t)strlen(_Crypt_Sha2Test[iString].pString), aKeyBuf, sizeof(aKeyBuf)); + + if (memcmp(strHashBuf0, strHashBuf1, sizeof(strHashBuf0))) + { + ZPrintf("; hash failed!\n"); + NetPrintMem(strHashBuf0, sizeof(strHashBuf0), "hash0 result"); + NetPrintMem(strHashBuf1, sizeof(strHashBuf1), "hash1 result"); + iFailed += 1; + } + else + { + ZPrintf("; success\n"); + } + } + + // timing tests + + // time atomic version + ZPrintf("%s: hashing %d bytes of random data %d times for timing test (atomic version); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter); + for (iCount = 0, uStartTick = NetTick(); iCount < _Crypt_iTimingIter; iCount += 1) + { + MurmurHash3(strHashBuf0, sizeof(strHashBuf0), _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)-16, aKeyBuf, sizeof(aKeyBuf)); + } + ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick)); + + // time crypt-style version + ZPrintf("%s: hashing %d bytes of random data %d times for timing test (crypt version); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter); + for (iCount = 0, uStartTick = NetTick(); iCount < _Crypt_iTimingIter; iCount += 1) + { + MurmurHash3Init(&MurmurCtx); + MurmurHash3Update(&MurmurCtx, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)); + MurmurHash3Final(&MurmurCtx, strHashBuf0, sizeof(strHashBuf0)); + } + ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick)); + + ZPrintf("%s: ---------------------\n", argv[0]); + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestMD5 + + \Description + Test the CryptMD5 module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 11/04/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestMD5(ZContext *argz, int32_t argc, char *argv[]) +{ + char strHashBuf[MD5_BINARY_OUT]; + int32_t iFailed = 0, iCount; + uint32_t uStartTick; + CryptMD5T MD5; + + ZPrintf("%s: testing CryptMD5\n", argv[0]); + + // do a timing test + + // test all modes + ZPrintf("%s: hashing %d bytes of random data %d times for timing test; ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter); + uStartTick = NetTick(); + + for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1) + { + CryptMD5Init(&MD5); + CryptMD5Update(&MD5, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)); + CryptMD5Final(&MD5, (uint8_t *)strHashBuf, sizeof(strHashBuf)); + } + + ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick)); + + ZPrintf("%s: ---------------------\n", argv[0]); + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestSha1 + + \Description + Test the CryptSha1 module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 11/04/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestSha1(ZContext *argz, int32_t argc, char *argv[]) +{ + char strHashBuf[20]; + int32_t iFailed = 0, iCount; + uint32_t uStartTick; + CryptSha1T Sha1; + + ZPrintf("%s: testing CryptSha1\n", argv[0]); + + // do a timing test + + // test all modes + ZPrintf("%s: hashing %d bytes of random data %d times for timing test; ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter); + uStartTick = NetTick(); + + for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1) + { + CryptSha1Init(&Sha1); + CryptSha1Update(&Sha1, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)); + CryptSha1Final(&Sha1, (uint8_t *)strHashBuf, sizeof(strHashBuf)); + } + + ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick)); + + ZPrintf("%s: ---------------------\n", argv[0]); + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestSha2 + + \Description + Test the CryptSha2 module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 11/04/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestSha2(ZContext *argz, int32_t argc, char *argv[]) +{ + char strHashBuf[128]; + static const uint8_t _HashSizes[] = { CRYPTSHA224_HASHSIZE, CRYPTSHA256_HASHSIZE, CRYPTSHA384_HASHSIZE, CRYPTSHA512_HASHSIZE }; + int32_t iFailed, iString, iMode, iCount; + uint32_t uStartTick; + CryptSha2T Sha2; + + ZPrintf("%s: testing CryptSha2\n", argv[0]); + + // test all strings + for (iFailed = 0, iString = 0; iString < (signed)(sizeof(_Crypt_Sha2Test)/sizeof(_Crypt_Sha2Test[0])); iString += 1) + { + // test all modes + for (iMode = 0; iMode < 4; iMode += 1) + { + ZPrintf("%s: hashing \"%s\" (mode %d)", argv[0], _Crypt_Sha2Test[iString].pString, iMode); + + CryptSha2Init(&Sha2, _HashSizes[iMode]); + CryptSha2Update(&Sha2, (uint8_t *)_Crypt_Sha2Test[iString].pString, (uint32_t)strlen(_Crypt_Sha2Test[iString].pString)); + CryptSha2Final(&Sha2, (uint8_t *)strHashBuf, _HashSizes[iMode]); + + if (memcmp(strHashBuf, _Crypt_Sha2Test[iString].strHashRslt[iMode], (uint32_t)_HashSizes[iMode])) + { + ZPrintf("; hash failed!\n"); + NetPrintMem(strHashBuf, (uint32_t)_HashSizes[iMode], "hash result"); + NetPrintMem(_Crypt_Sha2Test[iString].strHashRslt[iMode], (uint32_t)_HashSizes[iMode], "expected result"); + iFailed += 1; + } + else + { + ZPrintf("; success\n"); + } + } + } + + // do a timing test + + // test all modes + for (iMode = 0; iMode < 4; iMode += 1) + { + ZPrintf("%s: hashing %d bytes of random data %d times for timing test (mode %d); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter, iMode); + uStartTick = NetTick(); + + for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1) + { + CryptSha2Init(&Sha2, _HashSizes[iMode]); + CryptSha2Update(&Sha2, _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)); + CryptSha2Final(&Sha2, (uint8_t *)strHashBuf, _HashSizes[iMode]); + } + + ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick)); + } + + ZPrintf("%s: ---------------------\n", argv[0]); + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestHmac + + \Description + Test the CryptHmac module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 11/05/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestHmac(ZContext *argz, int32_t argc, char *argv[]) +{ + uint8_t strHmacBuf[128]; + const uint8_t strZeroBuf[64] = { 0 }; + int32_t iFailed, iTest, iMode, iCount; + uint32_t uStartTick; + int32_t iHashSize; + const CryptTestHmac *pTest; + + ZPrintf("%s: testing CryptHmac\n", argv[0]); + + // test all strings + for (iFailed = 0, iTest = 0; iTest < (signed)(sizeof(_Crypt_HmacTest)/sizeof(_Crypt_HmacTest[0])); iTest += 1) + { + ZPrintf("%s: test %d\n", argv[0], iTest+1); + + for (iMode = CRYPTHASH_MURMUR3; iMode <= CRYPTHASH_SHA512; iMode += 1) + { + iHashSize = CryptHashGetSize((CryptHashTypeE)iMode); + pTest = &_Crypt_HmacTest[iTest]; + + // skip tests with blank results + if (!memcmp(pTest->Param[iMode-1].strRslt, strZeroBuf, iHashSize)) + { + continue; + } + + // run the test + ZPrintf("%s: hashing \"%s\" (mode %d)", argv[0], pTest->pString, iMode); + CryptHmacCalc(strHmacBuf, (int32_t)sizeof(strHmacBuf), (uint8_t *)pTest->pString, pTest->Param[iMode-1].uInpLen, (uint8_t *)pTest->pKey, pTest->Param[iMode-1].uKeyLen, iMode); + + // check test result + if (memcmp(strHmacBuf, pTest->Param[iMode-1].strRslt, pTest->Param[iMode-1].uOutLen)) + { + ZPrintf("; hmac failed!\n"); + NetPrintMem(strHmacBuf, pTest->Param[iMode-1].uOutLen, "hmac result"); + NetPrintMem(pTest->Param[iMode-1].strRslt, pTest->Param[iMode-1].uOutLen, "expected result"); + iFailed += 1; + } + else + { + ZPrintf("; success\n"); + } + } + } + + // do a timing test + + // test all modes + for (iMode = CRYPTHASH_MURMUR3, iTest = 0; iMode <= CRYPTHASH_SHA512; iMode += 1) + { + ZPrintf("%s: hashing %d bytes of random data %d times for timing test (mode %d); ", argv[0], sizeof(_Crypt_aLargeRandom), _Crypt_iTimingIter, iMode); + uStartTick = NetTick(); + + for (iCount = 0; iCount < _Crypt_iTimingIter; iCount += 1) + { + CryptHmacCalc(strHmacBuf, (int32_t)sizeof(strHmacBuf), _Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom), (uint8_t *)_Crypt_HmacTest[iTest].pKey, (int32_t)strlen(_Crypt_HmacTest[iTest].pKey), (CryptHashTypeE)iMode); + } + + ZPrintf("%dms\n", NetTickDiff(NetTick(), uStartTick)); + } + + ZPrintf("%s: ---------------------\n", argv[0]); + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestRSA + + \Description + Test the CryptRSA module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else number of failed tests + + \Version 11/22/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestRSA(ZContext *argz, int32_t argc, char *argv[]) +{ + static const uint8_t _aModulus[] = + { + 0xe2, 0x02, 0x41, 0x01, 0xa1, 0x02, 0xb8, 0xe2, 0x3e, 0x2a, 0x0a, 0x6d, 0x8b, 0xf9, + 0xf4, 0x73, 0xd3, 0xdb, 0x15, 0xac, 0x0a, 0xf6, 0xf2, 0x4c, 0x6d, 0x46, 0xf7, 0xd4, 0x1b, + 0xb9, 0xce, 0x62, 0xbc, 0x58, 0x20, 0x57, 0x4c, 0xf9, 0xa6, 0x6b, 0xf4, 0xe1, 0x29, 0x93, + 0x15, 0x0e, 0xce, 0x35, 0x7e, 0x02, 0x9f, 0x18, 0xf4, 0xa2, 0x4f, 0x49, 0x69, 0x4a, 0xd0, + 0x98, 0xdd, 0x28, 0x6e, 0x5f, 0xf5, 0x3e, 0xe2, 0xa3, 0x56, 0xe3, 0x8c, 0xcd, 0xf9, 0x07, + 0x8d, 0x44, 0x42, 0x69, 0xf7, 0x41, 0x44, 0x54, 0x13, 0xa7, 0x35, 0x2e, 0x87, 0xc3, 0x0e, + 0x10, 0x00, 0xa7, 0x14, 0xc1, 0x3b, 0x36, 0xfb, 0x37, 0xee, 0x25, 0xc2, 0xa5, 0x04, 0xcb, + 0x7e, 0x26, 0x27, 0xe8, 0x61, 0xb9, 0x33, 0x96, 0x49, 0x5b, 0x1e, 0xa2, 0x09, 0xff, 0xf8, + 0xe2, 0x13, 0x7c, 0x99, 0x34, 0x25, 0xdc, 0xdf, 0xdf, 0xc8, 0x3a, 0x8d, 0x56, 0xbb, 0xc4, + 0x18, 0xe3, 0x8a, 0x7a, 0x1b, 0x38, 0x80, 0x7e, 0x9c, 0x9d, 0x43, 0x04, 0x11, 0x47, 0x81, + 0xf0, 0x32, 0x29, 0xe6, 0x9d, 0x72, 0xc7, 0x8d, 0xff, 0x0e, 0x03, 0x6b, 0xb6, 0xa3, 0xb0, + 0x6d, 0xb6, 0x5e, 0xc7, 0x78, 0x14, 0x08, 0xea, 0xdd, 0xcb, 0x15, 0xb6, 0x1c, 0xdd, 0xe7, + 0x5a, 0x25, 0x49, 0xe7, 0xbd, 0xe7, 0x1b, 0xcc, 0x81, 0xf2, 0x32, 0x93, 0xc8, 0x27, 0xc9, + 0x5d, 0xd8, 0x89, 0x11, 0xc4, 0xf3, 0x6b, 0x37, 0x9b, 0x6d, 0x36, 0x0a, 0x8d, 0xef, 0xca, + 0x79, 0xfa, 0x6e, 0x06, 0xcf, 0x96, 0x12, 0xe3, 0x94, 0xd7, 0x69, 0x34, 0x9d, 0xf5, 0xf6, + 0xe0, 0x54, 0x77, 0xba, 0xc9, 0xe6, 0x74, 0x2a, 0xbe, 0x20, 0xc3, 0xe2, 0x05, 0xa9, 0x61, + 0x11, 0xb8, 0x16, 0x35, 0xb7, 0xad, 0xda, 0xe9, 0xed, 0x60, 0xb4, 0x13, 0xc9, 0xeb, 0x45, + 0xe3, 0xf7 + }; + static const uint8_t _aPublicExponent[] = { 0x01, 0x00, 0x01 }; + static const uint8_t _aPrivateExponent[] = + { + 0x7d, 0x66, 0x78, 0xc1, 0x39, 0xa0, 0x34, 0x0b, 0x3c, 0x28, 0xc1, 0x6e, 0x74, 0xeb, 0x34, + 0x6a, 0x8c, 0x13, 0x14, 0x5c, 0x48, 0x1d, 0x2a, 0xe5, 0xa3, 0x00, 0x6c, 0x83, 0xe0, 0xfa, + 0x60, 0x7c, 0x42, 0x3a, 0xb7, 0x7f, 0x18, 0xf3, 0xb0, 0x16, 0x58, 0x62, 0x12, 0x5a, 0x4c, + 0xa5, 0xd1, 0x5e, 0xb6, 0xd3, 0x27, 0x89, 0x86, 0x3b, 0x04, 0xb9, 0x1b, 0xd5, 0xea, 0x15, + 0xd7, 0x28, 0x16, 0xcd, 0xe1, 0x5a, 0x8a, 0x0f, 0xcd, 0x27, 0x26, 0xba, 0x26, 0x41, 0xbd, + 0x6d, 0x31, 0x58, 0x70, 0x5b, 0x63, 0x59, 0x2f, 0x2a, 0x68, 0x84, 0xaf, 0xc9, 0x57, 0x65, + 0x23, 0xa7, 0x91, 0x09, 0x82, 0x1c, 0x88, 0x99, 0x48, 0xe6, 0xe4, 0xb0, 0x01, 0x10, 0x13, + 0xa7, 0x82, 0x1f, 0x1b, 0x11, 0xd2, 0x80, 0xc1, 0xa4, 0xf0, 0x43, 0x42, 0x3f, 0x27, 0xcd, + 0xf0, 0xb8, 0x02, 0x9d, 0x7f, 0xa0, 0xa5, 0x0a, 0x0f, 0xb0, 0x68, 0xdc, 0x23, 0x46, 0x77, + 0x6b, 0x87, 0xed, 0x29, 0x5f, 0x08, 0xfc, 0xd7, 0x58, 0x0e, 0x7c, 0x85, 0x24, 0x72, 0x2b, + 0xf1, 0x1d, 0x0a, 0x86, 0xe6, 0xe6, 0x05, 0x6f, 0xef, 0x3f, 0x74, 0x6a, 0xf3, 0x53, 0x57, + 0xf1, 0x80, 0x3e, 0x0d, 0x26, 0x5f, 0xce, 0xfd, 0x20, 0x28, 0x8c, 0x61, 0xfe, 0x18, 0x23, + 0xf2, 0x1f, 0x08, 0x20, 0xd6, 0x96, 0x95, 0x32, 0x6c, 0x61, 0xd2, 0xdb, 0xf8, 0x37, 0x37, + 0x02, 0xf7, 0x54, 0x77, 0xe3, 0x8b, 0x2e, 0x59, 0xd8, 0x9c, 0x61, 0x45, 0x54, 0x95, 0x27, + 0x29, 0x16, 0x19, 0xa1, 0xae, 0x76, 0xf4, 0xbf, 0x07, 0x39, 0xc4, 0x5f, 0xac, 0xb8, 0x94, + 0x38, 0x1d, 0xfa, 0x40, 0x0e, 0x4f, 0xb8, 0xa7, 0xa1, 0xb6, 0xe5, 0xe1, 0x91, 0x86, 0x1d, + 0x7f, 0xc2, 0x3e, 0x0d, 0x55, 0x0d, 0xbc, 0x28, 0x40, 0x3d, 0xce, 0xb5, 0xc9, 0xee, 0x17, + 0xc9 + }; + static const uint8_t _aPrime1[] = + { + 0xf3, 0xd4, 0x21, 0xef, 0xb5, 0xab, 0x27, 0x40, 0xb2, 0xb2, 0x55, 0x6c, 0x9b, 0x9f, + 0x68, 0xe0, 0x41, 0x8f, 0x34, 0xa2, 0x2b, 0x53, 0x2d, 0xd4, 0x04, 0x35, 0xd4, 0x37, 0x13, + 0x88, 0x89, 0xef, 0xdd, 0x6f, 0x00, 0x49, 0xbc, 0x8a, 0x32, 0x0a, 0x86, 0x87, 0x79, 0x07, + 0x6c, 0x66, 0xfd, 0x72, 0x7b, 0x84, 0xf5, 0xdd, 0x27, 0x33, 0x87, 0xdb, 0x01, 0x89, 0xd3, + 0x4a, 0x72, 0x48, 0xba, 0xf2, 0x1a, 0x25, 0x8e, 0x6c, 0x46, 0xe4, 0x04, 0x72, 0x5c, 0x69, + 0xa5, 0x0f, 0xc9, 0x99, 0xed, 0x54, 0x64, 0xa5, 0x5f, 0xbe, 0xf4, 0x5f, 0xea, 0xc9, 0x4c, + 0x20, 0xc9, 0xda, 0xdd, 0xc2, 0x33, 0x95, 0xce, 0x3c, 0x09, 0xf9, 0x9b, 0xad, 0xca, 0x00, + 0x43, 0xeb, 0xe4, 0x19, 0xa9, 0x26, 0xb5, 0x4b, 0xa8, 0xa7, 0x35, 0xd2, 0xc1, 0x9d, 0x56, + 0x3c, 0x83, 0x32, 0x94, 0xa8, 0xee, 0xee, 0x87, 0x53 + }; + static const uint8_t _aPrime2[] = + { + 0xed, 0x4a, 0x67, 0x2c, 0xac, 0xa0, 0x96, 0x8d, 0x0e, 0xed, 0x85, 0x0a, 0x3e, 0xf1, + 0xaf, 0xe7, 0xda, 0x63, 0x04, 0x8e, 0x7a, 0x12, 0x5c, 0x15, 0x6d, 0x74, 0x63, 0x1e, 0x3c, + 0x65, 0xc5, 0x0a, 0x37, 0x44, 0xa2, 0x4b, 0x34, 0x9e, 0xaa, 0xbe, 0xce, 0x71, 0xab, 0x49, + 0x3a, 0xc0, 0x66, 0x6f, 0x13, 0xfe, 0x13, 0x54, 0x70, 0x88, 0x24, 0x25, 0x0e, 0x06, 0xde, + 0x85, 0x46, 0x5d, 0xa2, 0x5c, 0x72, 0xde, 0x57, 0x19, 0x4f, 0x33, 0x2b, 0xdb, 0xdf, 0xfb, + 0x3e, 0x86, 0xc6, 0x93, 0x52, 0xb9, 0x21, 0xc1, 0x19, 0xf5, 0xde, 0x96, 0x55, 0x30, 0x76, + 0xc4, 0x22, 0x72, 0x0c, 0x40, 0x4a, 0xa5, 0x61, 0xa4, 0xd7, 0xb4, 0xb4, 0x89, 0x30, 0xf3, + 0x06, 0xe1, 0x32, 0xd4, 0xaf, 0x72, 0x80, 0x86, 0xaa, 0x34, 0x9a, 0x64, 0x89, 0xd3, 0x80, + 0x7c, 0x4b, 0x39, 0xcb, 0x14, 0x9e, 0x66, 0x10, 0x4d + }; + static const uint8_t _aExponent1[] = + { + 0xa7, 0xdd, 0x30, 0xab, 0xf6, 0x37, 0x69, 0xe3, 0xb9, 0xe2, 0xda, 0xba, 0xd5, 0xfd, + 0x0e, 0x57, 0xed, 0xea, 0xa8, 0x82, 0xc9, 0x2f, 0x0f, 0xca, 0xfa, 0x47, 0x10, 0xde, 0x06, + 0x1d, 0xa7, 0x51, 0x32, 0xf2, 0x9b, 0x91, 0x28, 0x33, 0x40, 0x36, 0x4c, 0xdd, 0xf1, 0xad, + 0xf1, 0xac, 0x89, 0xea, 0x8a, 0x2d, 0x44, 0x93, 0x47, 0xcc, 0xcb, 0x48, 0x34, 0xab, 0xed, + 0x82, 0x40, 0x61, 0xe0, 0x0a, 0x93, 0x83, 0xad, 0xa4, 0xcf, 0xbd, 0x65, 0x6e, 0x52, 0x3f, + 0x0d, 0x3b, 0x6c, 0x41, 0x03, 0xca, 0x69, 0x2c, 0x0d, 0x59, 0xca, 0xa6, 0x4a, 0x5e, 0xe1, + 0x81, 0x65, 0x56, 0xbf, 0xfb, 0x56, 0x46, 0x59, 0x60, 0xae, 0x41, 0x61, 0x33, 0x69, 0x71, + 0x7b, 0x51, 0x68, 0x8d, 0x5e, 0x0d, 0xdf, 0x1c, 0xc2, 0x74, 0xb3, 0xb2, 0x70, 0x47, 0x60, + 0xba, 0x72, 0x5c, 0x9d, 0x4a, 0x1c, 0x8c, 0xad, 0x2f + }; + static const uint8_t _aExponent2[] = + { + 0x84, 0x99, 0x05, 0x1a, 0x93, 0xc4, 0x91, 0x1c, 0x75, 0xf1, 0x08, 0x5c, 0xf7, 0x5b, + 0x7b, 0x1e, 0xa6, 0x8c, 0x9a, 0x69, 0x3b, 0x91, 0xb2, 0xdf, 0x4e, 0x70, 0xb1, 0x4a, 0x9e, + 0x19, 0x88, 0x87, 0xf2, 0xe6, 0x69, 0x82, 0x78, 0xff, 0x09, 0x0e, 0xe2, 0xb1, 0xe6, 0x33, + 0x5f, 0x9f, 0x50, 0x1e, 0x56, 0x1f, 0xae, 0x91, 0x8a, 0xe8, 0xa8, 0xba, 0x04, 0x22, 0x96, + 0x8a, 0x07, 0x0e, 0x1f, 0xc2, 0x65, 0x76, 0x15, 0x59, 0xd1, 0x46, 0x19, 0x06, 0x1f, 0x1d, + 0x78, 0x8d, 0x3b, 0xbd, 0xeb, 0x86, 0x04, 0x74, 0xb1, 0x9b, 0x11, 0x2d, 0x14, 0xa1, 0xa6, + 0x5c, 0x67, 0x9b, 0x2f, 0x79, 0x65, 0xbd, 0x10, 0xd9, 0x5a, 0xa8, 0x62, 0x12, 0x1f, 0xc6, + 0x4e, 0x5b, 0xdd, 0x59, 0xb8, 0x48, 0xd5, 0xc5, 0x6a, 0xab, 0x46, 0x73, 0x54, 0x09, 0x5a, + 0x4d, 0x1a, 0x84, 0x4b, 0x15, 0x54, 0x86, 0x58, 0x29 + }; + static const uint8_t _aCoefficient[] = + { + 0x90, 0xba, 0x74, 0xe6, 0x17, 0xc2, 0xf9, 0x0f, 0xad, 0x57, 0x9d, 0x86, 0x78, 0x8d, + 0xa5, 0x6f, 0x86, 0xd1, 0x9a, 0xfe, 0xb6, 0x95, 0x47, 0x14, 0x0f, 0x92, 0xc9, 0xb6, 0x25, + 0x43, 0x63, 0x70, 0xdc, 0xbf, 0x83, 0x8b, 0x27, 0x7a, 0x2d, 0x3c, 0xe7, 0x50, 0xfc, 0x29, + 0x4b, 0xda, 0xbd, 0xa2, 0x38, 0x4c, 0x37, 0xfc, 0xf5, 0x9b, 0x3e, 0xbb, 0x28, 0x0f, 0xda, + 0x52, 0x06, 0x69, 0xd6, 0xf7, 0x3f, 0x2b, 0xd0, 0xae, 0xc0, 0x85, 0x85, 0xb7, 0xd9, 0xbc, + 0x3b, 0x20, 0x2d, 0xe1, 0x10, 0xc9, 0x84, 0x76, 0xbe, 0xa2, 0x6c, 0xaf, 0x36, 0x84, 0x13, + 0xfa, 0x4c, 0x80, 0x6d, 0x09, 0x89, 0xa6, 0x6d, 0xa4, 0x6b, 0x96, 0x58, 0x5f, 0x05, 0x37, + 0x70, 0xee, 0x78, 0x08, 0xc1, 0xa3, 0x26, 0xef, 0xda, 0xa4, 0x71, 0x11, 0x09, 0xcb, 0xa9, + 0x49, 0x05, 0x55, 0x2d, 0x10, 0x98, 0xca, 0xa9, 0x42 + }; + static const CryptBinaryObjT _Prime1 = { (uint8_t *)_aPrime1, sizeof(_aPrime1) }; + static const CryptBinaryObjT _Prime2 = { (uint8_t *)_aPrime2, sizeof(_aPrime2) }; + static const CryptBinaryObjT _Exponent1 = { (uint8_t *)_aExponent1, sizeof(_aExponent1) }; + static const CryptBinaryObjT _Exponent2 = { (uint8_t *)_aExponent2, sizeof(_aExponent2) }; + static const CryptBinaryObjT _Coefficient = { (uint8_t *)_aCoefficient, sizeof(_aCoefficient) }; + static const uint8_t _strRsaEncryptMessage[] = "test rsa"; + + CryptRSAT RSA, RSA2, RSA3; + CryptSha2T Sha2; + ProtoSSLPkcs1T Pkcs1; + uint8_t aHashObj[CRYPTSHA256_HASHSIZE], aSigObj[sizeof(_aModulus)]; + + // encrypt + if (CryptRSAInit(&RSA, _aModulus, sizeof(_aModulus), _aPublicExponent, sizeof(_aPublicExponent)) != 0) + { + return(1); + } + CryptRSAInitMaster(&RSA, _strRsaEncryptMessage, sizeof(_strRsaEncryptMessage)); + CryptRSAEncrypt(&RSA, 0); + ZPrintf("crypt: rsa encrypt took %ums\n", RSA.uCryptMsecs); + + // decrypt using private exponent + if (CryptRSAInit(&RSA2, _aModulus, sizeof(_aModulus), _aPrivateExponent, sizeof(_aPrivateExponent)) != 0) + { + return(1); + } + CryptRSAInitSignature(&RSA2, RSA.EncryptBlock, sizeof(_aModulus)); + CryptRSAEncrypt(&RSA2, 0); + ZPrintf("crypt: rsa decrypt took %ums\n", RSA2.uCryptMsecs); + + // verify result + if (memcmp(_strRsaEncryptMessage, RSA2.EncryptBlock+sizeof(_aModulus)-sizeof(_strRsaEncryptMessage), sizeof(_strRsaEncryptMessage)) != 0) + { + return(1); + } + + // decrypt using crt + if (CryptRSAInit2(&RSA3, sizeof(_aModulus), &_Prime1, &_Prime2, &_Exponent1, &_Exponent2, &_Coefficient) != 0) + { + return(1); + } + CryptRSAInitSignature(&RSA3, RSA.EncryptBlock, sizeof(_aModulus)); + CryptRSAEncrypt(&RSA3, 0); + ZPrintf("crypt: rsa decrypt using crt took %ums\n", RSA3.uCryptMsecs); + + // verify result + if (memcmp(_strRsaEncryptMessage, RSA3.EncryptBlock+sizeof(_aModulus)-sizeof(_strRsaEncryptMessage), sizeof(_strRsaEncryptMessage)) != 0) + { + return(1); + } + + // hash the message + CryptSha2Init(&Sha2, sizeof(aHashObj)); + CryptSha2Update(&Sha2, _strRsaEncryptMessage, sizeof(_strRsaEncryptMessage)); + CryptSha2Final(&Sha2, aHashObj, sizeof(aHashObj)); + + // generate the signature + if (ProtoSSLPkcs1GenerateInit(&Pkcs1, aHashObj, sizeof(aHashObj), CRYPTHASH_SHA256, sizeof(_aModulus), &_Prime1, &_Prime2, &_Exponent1, &_Exponent2, &_Coefficient) != 0) + { + return(1); + } + ProtoSSLPkcs1GenerateUpdate(&Pkcs1, 0, aSigObj, sizeof(aSigObj)); + // verify the signature + if (ProtoSSLPkcs1Verify(aSigObj, sizeof(aSigObj), aHashObj, sizeof(aHashObj), CRYPTHASH_SHA256, _aModulus, sizeof(_aModulus), _aPublicExponent, sizeof(_aPublicExponent)) != 0) + { + return(1); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestNist + + \Description + Test the CryptNist module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else test failed + + \Version 11/29/2018 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestNist(ZContext *argz, int32_t argc, char *argv[]) +{ + uint8_t bResult = TRUE; + CryptNistDhT Alice, Bob; + CryptEccPointT AlicePublic, BobPublic, AliceShared, BobShared; + + // initialize the secp256r1 states + CryptNistInitDh(&Alice, CRYPTCURVE_SECP256R1); + CryptNistInitDh(&Bob, CRYPTCURVE_SECP256R1); + + // generate public key + while (CryptNistPublic(&Alice, &AlicePublic, NULL) > 0) + ; + ZPrintf("crypt: alice public secp256r1 time=%u\n", Alice.Ecc.uCryptUSecs); + + while (CryptNistPublic(&Bob, &BobPublic, NULL) > 0) + ; + ZPrintf("crypt: bob public secp256r1 time=%u\n", Bob.Ecc.uCryptUSecs); + + // generate shared secrets + while (CryptNistSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0) + ; + ZPrintf("crypt: alice shared secp256r1 time=%u\n", Alice.Ecc.uCryptUSecs); + + while (CryptNistSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0) + ; + ZPrintf("crypt: bob shared secp256r1 time=%u\n", Bob.Ecc.uCryptUSecs); + + // validate secp256r1 results + bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0; + bResult &= memcmp(AliceShared.Y.aData, BobShared.Y.aData, sizeof(AliceShared.Y.aData)) == 0; + + // initialize the secp384r1 states + CryptNistInitDh(&Alice, CRYPTCURVE_SECP384R1); + CryptNistInitDh(&Bob, CRYPTCURVE_SECP384R1); + + // generate public key + while (CryptNistPublic(&Alice, &AlicePublic, NULL) > 0) + ; + ZPrintf("crypt: alice public secp384r1 time=%u\n", Alice.Ecc.uCryptUSecs); + + while (CryptNistPublic(&Bob, &BobPublic, NULL) > 0) + ; + ZPrintf("crypt: bob public secp384r1 time=%u\n", Bob.Ecc.uCryptUSecs); + + // generate shared secrets + while (CryptNistSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0) + ; + ZPrintf("crypt: alice shared secp384r1 time=%u\n", Alice.Ecc.uCryptUSecs); + + while (CryptNistSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0) + ; + ZPrintf("crypt: bob shared secp384r1 time=%u\n", Bob.Ecc.uCryptUSecs); + + // validate secp384r1 results + bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0; + bResult &= memcmp(AliceShared.Y.aData, BobShared.Y.aData, sizeof(AliceShared.Y.aData)) == 0; + + return(bResult ? 0 : 1); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestDsa + + \Description + Test the ECDSA signing / verify operations + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else test failed + + \Version 01/17/2019 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestDsa(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iCurve, iFailed = 0; + + for (iCurve = 0; iCurve < (signed)(sizeof(_Crypt_DsaTests)/sizeof(_Crypt_DsaTests[0])); iCurve += 1) + { + const CryptTestDsaT *pTestData = &_Crypt_DsaTests[iCurve]; + int32_t iTest; + CryptEccPointT PublicKey; + CryptBnT PrivateKey; + + CryptNistPointInitFrom(&PublicKey, pTestData->aPublicKey, (pTestData->iCurveSize*2)+1); + CryptBnInitFrom(&PrivateKey, -1, pTestData->aPrivateKey, pTestData->iCurveSize); + + for (iTest = 0; iTest < (signed)(sizeof(pTestData->aCases)/sizeof(pTestData->aCases[0])); iTest += 1) + { + const CryptTestDsaCaseT *pCase = &pTestData->aCases[iTest]; + int32_t iHashSize = CryptHashGetSize(pCase->eHashType), iSignResult, iVerifyResult; + + CryptNistDsaT Sign, Verify; + CryptEccPointT Signature; + const CryptHashT *pHash; + uint8_t aHash[CRYPTHASH_MAXDIGEST], aSignature[97]; + + // initialize dsa state + CryptNistInitDsa(&Sign, pTestData->eCurveType); + CryptNistInitDsa(&Verify, pTestData->eCurveType); + + // hash the message + if ((pHash = CryptHashGet(pCase->eHashType)) != NULL) + { + uint8_t aHashState[CRYPTHASH_MAXSTATE]; + pHash->Init(aHashState, iHashSize); + pHash->Update(aHashState, pCase->aMessage, (signed)strlen((const char *)pCase->aMessage)); + pHash->Final(aHashState, aHash, iHashSize); + } + + // generate and check signature for expected result + while (CryptNistSign(&Sign, &PrivateKey, aHash, iHashSize, &Signature, NULL) > 0) + ; + CryptBnFinal(&Signature.X, aSignature, Sign.Ecc.iSize); + CryptBnFinal(&Signature.Y, aSignature+Sign.Ecc.iSize, Sign.Ecc.iSize); + iSignResult = memcmp(aSignature, pCase->aSignature, Sign.Ecc.iSize*2); + ZPrintf("crypt: sign curve=%d, hash=%d, message=%s, time=%u\n", + pTestData->eCurveType, pCase->eHashType, pCase->aMessage, Sign.Ecc.uCryptUSecs); + + // verify signature + while ((iVerifyResult = CryptNistVerify(&Verify, &PublicKey, aHash, iHashSize, &Signature, NULL)) > 0) + ; + ZPrintf("crypt: verify curve=%d, hash=%d, message=%s, time=%u\n", + pTestData->eCurveType, pCase->eHashType, pCase->aMessage, Verify.Ecc.uCryptUSecs); + + if ((iSignResult != 0) || (iVerifyResult != 0)) + { + iFailed += 1; + } + } + } + + return(iFailed); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestMont + + \Description + Test the CryptMont module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else test failed + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestMont(ZContext *argz, int32_t argc, char *argv[]) +{ + #if DIRTYCODE_DEBUG + const uint8_t aAlicePrivate25519[] = { + 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 0x66, 0x45, + 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a + }; + const uint8_t aBobPrivate25519[] = { + 0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83, 0x80, 0x0e, 0xe6, + 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb + }; + const uint8_t aAlicePublic25519[] = { + 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, + 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a + }; + const uint8_t aBobPublic25519[] = { + 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, + 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f + }; + const uint8_t aSharedSecrete25519[] = { + 0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25, + 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42 + }; + const uint8_t aAlicePrivate448[] = { + 0x9a, 0x8f, 0x49, 0x25, 0xd1, 0x51, 0x9f, 0x57, 0x75, 0xcf, 0x46, 0xb0, 0x4b, 0x58, 0x00, 0xd4, + 0xee, 0x9e, 0xe8, 0xba, 0xe8, 0xbc, 0x55, 0x65, 0xd4, 0x98, 0xc2, 0x8d, 0xd9, 0xc9, 0xba, 0xf5, + 0x74, 0xa9, 0x41, 0x97, 0x44, 0x89, 0x73, 0x91, 0x00, 0x63, 0x82, 0xa6, 0xf1, 0x27, 0xab, 0x1d, + 0x9a, 0xc2, 0xd8, 0xc0, 0xa5, 0x98, 0x72, 0x6b + }; + const uint8_t aBobPrivate448[] = { + 0x1c, 0x30, 0x6a, 0x7a, 0xc2, 0xa0, 0xe2, 0xe0, 0x99, 0x0b, 0x29, 0x44, 0x70, 0xcb, 0xa3, 0x39, + 0xe6, 0x45, 0x37, 0x72, 0xb0, 0x75, 0x81, 0x1d, 0x8f, 0xad, 0x0d, 0x1d, 0x69, 0x27, 0xc1, 0x20, + 0xbb, 0x5e, 0xe8, 0x97, 0x2b, 0x0d, 0x3e, 0x21, 0x37, 0x4c, 0x9c, 0x92, 0x1b, 0x09, 0xd1, 0xb0, + 0x36, 0x6f, 0x10, 0xb6, 0x51, 0x73, 0x99, 0x2d + }; + const uint8_t aAlicePublic448[] = { + 0x9b, 0x08, 0xf7, 0xcc, 0x31, 0xb7, 0xe3, 0xe6, 0x7d, 0x22, 0xd5, 0xae, 0xa1, 0x21, 0x07, 0x4a, + 0x27, 0x3b, 0xd2, 0xb8, 0x3d, 0xe0, 0x9c, 0x63, 0xfa, 0xa7, 0x3d, 0x2c, 0x22, 0xc5, 0xd9, 0xbb, + 0xc8, 0x36, 0x64, 0x72, 0x41, 0xd9, 0x53, 0xd4, 0x0c, 0x5b, 0x12, 0xda, 0x88, 0x12, 0x0d, 0x53, + 0x17, 0x7f, 0x80, 0xe5, 0x32, 0xc4, 0x1f, 0xa0 + }; + const uint8_t aBobPublic448[] = { + 0x3e, 0xb7, 0xa8, 0x29, 0xb0, 0xcd, 0x20, 0xf5, 0xbc, 0xfc, 0x0b, 0x59, 0x9b, 0x6f, 0xec, 0xcf, + 0x6d, 0xa4, 0x62, 0x71, 0x07, 0xbd, 0xb0, 0xd4, 0xf3, 0x45, 0xb4, 0x30, 0x27, 0xd8, 0xb9, 0x72, + 0xfc, 0x3e, 0x34, 0xfb, 0x42, 0x32, 0xa1, 0x3c, 0xa7, 0x06, 0xdc, 0xb5, 0x7a, 0xec, 0x3d, 0xae, + 0x07, 0xbd, 0xc1, 0xc6, 0x7b, 0xf3, 0x36, 0x09 + }; + const uint8_t aSharedSecret448[] = { + 0x07, 0xff, 0xf4, 0x18, 0x1a, 0xc6, 0xcc, 0x95, 0xec, 0x1c, 0x16, 0xa9, 0x4a, 0x0f, 0x74, 0xd1, + 0x2d, 0xa2, 0x32, 0xce, 0x40, 0xa7, 0x75, 0x52, 0x28, 0x1d, 0x28, 0x2b, 0xb6, 0x0c, 0x0b, 0x56, + 0xfd, 0x24, 0x64, 0xc3, 0x35, 0x54, 0x39, 0x36, 0x52, 0x1c, 0x24, 0x40, 0x30, 0x85, 0xd5, 0x9a, + 0x44, 0x9a, 0x50, 0x37, 0x51, 0x4a, 0x87, 0x9d + }; + #endif + + CryptMontT Alice, Bob; + CryptEccPointT AlicePublic, BobPublic, AliceShared, BobShared; + uint8_t bResult = TRUE; + + // initialize the context (x25519) + CryptMontInit(&Alice, CRYPTCURVE_X25519); + CryptMontInit(&Bob, CRYPTCURVE_X25519); + + #if DIRTYCODE_DEBUG + ZPrintf("crypt: validating x25519 using test vectors\n"); + CryptMontSetPrivateKey(&Alice, aAlicePrivate25519); + CryptMontSetPrivateKey(&Bob, aBobPrivate25519); + #endif + + // generate alice public + while (CryptMontPublic(&Alice, &AlicePublic, NULL) > 0) + ; + #if DIRTYCODE_DEBUG + bResult &= memcmp(aAlicePublic25519, AlicePublic.X.aData, 32) == 0; + #endif + ZPrintf("crypt: alice public x25519 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + + // generate bob public + while (CryptMontPublic(&Bob, &BobPublic, NULL) > 0) + ; + #if DIRTYCODE_DEBUG + bResult &= memcmp(aBobPublic25519, BobPublic.X.aData, 32) == 0; + #endif + ZPrintf("crypt: bob public x25519 time=%u reuslt=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + + // generate alice shared + while (CryptMontSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0) + ; + + // generate bob shared + while (CryptMontSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0) + ; + #if DIRTYCODE_DEBUG + bResult &= memcmp(aSharedSecrete25519, AliceShared.X.aData, 32) == 0; + bResult &= memcmp(aSharedSecrete25519, BobShared.X.aData, 32) == 0; + #else + bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0; + #endif + + ZPrintf("crypt: alice shared x25519 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + ZPrintf("crypt: bob shared x25519 time=%u result=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + + // initialize the context (x448) + CryptMontInit(&Alice, CRYPTCURVE_X448); + CryptMontInit(&Bob, CRYPTCURVE_X448); + + #if DIRTYCODE_DEBUG + ZPrintf("crypt: validating x448 using test vectors\n"); + CryptMontSetPrivateKey(&Alice, aAlicePrivate448); + CryptMontSetPrivateKey(&Bob, aBobPrivate448); + #endif + + // generate alice public + while (CryptMontPublic(&Alice, &AlicePublic, NULL) > 0) + ; + #if DIRTYCODE_DEBUG + bResult &= memcmp(aAlicePublic448, AlicePublic.X.aData, 56) == 0; + #endif + ZPrintf("crypt: alice public x448 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + + // generate bob public + while (CryptMontPublic(&Bob, &BobPublic, NULL) > 0) + ; + #if DIRTYCODE_DEBUG + bResult &= memcmp(aBobPublic448, BobPublic.X.aData, 56) == 0; + #endif + ZPrintf("crypt: bob public x448 time=%u result=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + + // generate alice shared + while (CryptMontSecret(&Alice, &BobPublic, &AliceShared, NULL) > 0) + ; + // generate bob shared + while (CryptMontSecret(&Bob, &AlicePublic, &BobShared, NULL) > 0) + ; + #if DIRTYCODE_DEBUG + bResult &= memcmp(aSharedSecret448, AliceShared.X.aData, 56) == 0; + bResult &= memcmp(aSharedSecret448, BobShared.X.aData, 56) == 0; + #else + bResult &= memcmp(AliceShared.X.aData, BobShared.X.aData, sizeof(AliceShared.X.aData)) == 0; + #endif + + ZPrintf("crypt: alice shared x448 time=%u result=%s\n", Alice.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + ZPrintf("crypt: bob shared x448 time=%u result=%s\n", Bob.uCryptUsecs, bResult ? "SUCCESS" : "FAILURE"); + + return(bResult ? 0 : 1); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestRand + + \Description + Test the CryptRand module + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else test failed + + \Version 01/24/2019 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestRand(ZContext *argz, int32_t argc, char *argv[]) +{ + static uint8_t aTest[2097152]; + int32_t iSize = 16, iIter = 1; + + for (iIter = 1; iSize <= (signed)sizeof(aTest); iIter += 1, iSize <<= 1) + { + uint64_t uTick = NetTickUsec(); + CryptRandGet(aTest, iSize); + ZPrintf("crypt: rand %d (size=%d) took %u\n", + iIter, iSize, NetTickDiff(NetTickUsec(), uTick)); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptAesDecryptPrivateKey + + \Description + Decrypt a base64 encoded cipher text + + \Input iKeyLen - size of the key (16, 32) + \Input *pPass - password uses to encrypt + \Input *pIv - generated IV + \Input *pEncryptedPem - cipher text we are decrypting (base64 encoded) + \Input *pDecryptedPem - plain text we are validating against (base64 encoded) + + \Output + int32_t - zero=success, else test failed + + \Version 03/29/2019 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptAesDecryptPrivateKey(int32_t iKeyLen, const char *pPass, const uint8_t *pIv, const char *pEncryptedPem, const char *pDecryptedPem) +{ + CryptAesT Aes; + CryptMD5T MD5; + uint8_t aKey[32]; + int32_t iKeySize = 0, iPadSize, iPad, iPadStart, iBytesWritten; + char aPrivateKeyDER[2048], aPrivateKeyPEM[2048]; + + // validate key length is valid + if ((iKeyLen != 16) && (iKeyLen != 32)) + { + return(1); + } + + /* The algorithm hashes the password to generate the key of the appropriate size. This is based on the algorithm used in OpenSSL which is not standard. + https://www.openssl.org/docs/man1.0.2/man3/EVP_BytesToKey.html + OpenSSL calls their function with MD5 and count as 1 which is exactly how our function behaves. */ + for (iBytesWritten = 0; iBytesWritten < iKeyLen; iBytesWritten += MD5_BINARY_OUT) + { + CryptMD5Init(&MD5); + + // if we have written bytes add in the previous output + if (iBytesWritten > 0) + { + CryptMD5Update(&MD5, aKey, MD5_BINARY_OUT); + } + CryptMD5Update(&MD5, pPass, (signed)strlen(pPass)); + CryptMD5Update(&MD5, pIv, 8); // the algorithm uses 8 bytes of the IV as salt + CryptMD5Final(&MD5, aKey+iBytesWritten, iKeyLen-iBytesWritten); + } + + // decrypt the private key + CryptAesInit(&Aes, aKey, iKeyLen, CRYPTAES_KEYTYPE_DECRYPT, pIv); + iKeySize = Base64Decode2((signed)strlen(pEncryptedPem), pEncryptedPem, aPrivateKeyDER); + CryptAesDecrypt(&Aes, (uint8_t *)aPrivateKeyDER, iKeySize); + + // verify the padding, remove padding if successful + iPadSize = aPrivateKeyDER[iKeySize-1]; + for (iPad = 0, iPadStart = iKeySize-iPadSize; iPad < iPadSize; iPad += 1) + { + if (aPrivateKeyDER[iPadStart+iPad] == iPadSize) + { + continue; + } + ZPrintf("crypt: aes padding is invalid"); + return(1); + } + iKeySize -= iPadSize; + + // base64 encode to validate + Base64Encode2(aPrivateKeyDER, iKeySize, aPrivateKeyPEM, sizeof(aPrivateKeyPEM)); + return(memcmp(aPrivateKeyPEM, pDecryptedPem, strlen(pDecryptedPem))); +} + +/*F********************************************************************************/ +/*! + \Function _CmdCryptTestAes + + \Description + Test the CryptAes module by decrypting private keys generated by OpenSSL + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - zero=success, else test failed + + \Version 03/29/2019 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdCryptTestAes(ZContext *argz, int32_t argc, char *argv[]) +{ + // DEK-Info: AES-128-CBC,40EF2D13FDB65054775F4FD1AF4E3D24 + static const uint8_t _strIv128[] = { 0x40, 0xEF, 0x2D, 0x13, 0xFD, 0xB6, 0x50, 0x54, 0x77, 0x5F, 0x4F, 0xD1, 0xAF, 0x4E, 0x3D, 0x24 }; + static const char _strEncrypted128[] = "8xdgF3zSj27fwsQc0QiNrWNY8dN5fqw/olTn9URQSt2szU4P/uDm12/lVJeXj/errhq9tSZmJEdFTPYPKfrBRWSkJdzRy3LFxCR/RhgNI96UOdxqhhTrAAU7F/Zy5rp7tCG/KhiB5E2IAaxrsF4Ver+XfI77UV+6iTIqgEPJqNyLt8WFtm22qXgkWg412+IxEcNk4yuohyDyNR2XYry9KXjZlc3WjFBYPy++//hOR4NEBqVB4bxHukaS8b10CxXjkFwKI+t4CTjE0pJ6o3kt2lLHzCxj9z9PhJZOX3o2ail7aiGSNeA1dAgx3TPP8iVn7K6yFWrZBa9ysTjWxHpJyGLdE+wPgwQcCOwxRT/HZqoi4doa1g5d7C+g9lycVlBqy9G5xUdTA/ANHOXhFuisjaeFAIifX1veITdq4cTEjwzj6vGPqG8TVxoEZjnkS7tr2g01HdWsvMDTGIU5ZGMVkZESbQr1NfIh1+MgFfCFB22LH0zajgmutU8N5V/FOvvP3vWAW/PAtYoubyethUYtJPEAFnTtBTvhyT3m94njr1BdJdsurCymXUkk2zYj2uPEMpRatNV6YgiODP3yWzGjl3NRDvhbVcjgNfpEozfLh8qOta/AO30azD4r48MD/JiodzA905KdcCfHQGOC8mJ+g2UK8BIv2+OGIPFoy1nUFFHl/ZPAfkm8XlxPN+avfR92bSf/ZOLhte/yliGTRlFVPn7dTzNpbsxGGQ1vK+HL7otUgQSAknIrN74G5zlnKcdz4VUG03Lj2gG53en+ijmSTqVUM+J7j65C2ssnBaeRu24azpc0ON+8bieS4Ii0PvEwk+E+4krHoBH7xRm4dSDl/gkFwPT2DbTF7SaztNCtR/HlpLxCBMBJvD9dHu4tKCa+eTyQbbnJ2IWPy4x7JN3B2EGneJhApNJKRENBpDhABpv+wt6g07bQPwlCKysn1YKKjHaIV9uAjzdRNTJufzL+phmwZ6Au7DD6wRyur5VbcSfjcCW8K1PdnxdUBiiQ07/OfIwht5GL+rPz4M8eu0Wd97lI42Alk3ui5gu00BJ+DoSkkyXx8WTiOp1U7emgTURlWjLdf8sqH9AoZQFI9J8kCeP9jHzHOB5d77PGbt7LxTvxAZO0l0SRTrj6PYj+nmMyyBv8nw4+U1Ww0WsShYnkJV4BO1cT6x2/nkZlASoG/iFtlisJQOq7Maz+R9HZIvCHeliqdvnf/fjyicDbVAmOc7BJvCINcmICmTqvwqrN1r5fUKAr9jwJORbaLA0i6reZ4xHVySfK9bdkgeg5Nxm8X/eUt57s6FGJnJKpCYxN8m6mzsuY+1uhIbSZTrrchoPFWk5MrBku1XfvKRDxFaQMpW+MxyMRqvoGJvtx00iK1EC1GYtZtqUx5HZ1eTyCvX5CoY+z7Kc3TBivbOs1YxQCo4ID3YPV7uErW7+OFGshOuamZeyzTHTC0FlxF+DWGE35URjt7NuggEFFSS33my7j6GB2eC6VnIKGfzobIpN9DbheJgQegilV21LsZJkatfkrHQ0XPyAYd7GbzG9bjg2/Q3mNMqQ5rNJGrsH0G2+ARJBHDOpMXx3bgRKitgVRwxau"; + // DEK-Info: AES-256-CBC,A530A7D78DA83CB609551E79A920E01D + static const uint8_t _strIv256[] = { 0xA5, 0x30, 0xA7, 0xD7, 0x8D, 0xA8, 0x3C, 0xB6, 0x09, 0x55, 0x1E, 0x79, 0xA9, 0x20, 0xE0, 0x1D }; + static const char _strEncrypted256[] = "QKzr7/7Ig3G4h+Gw6opJRRykoMnP7im3kzVV3kVLPj8ZEOa59CjfzERo2rC2Qyh1klkNHogky1rYKF8IHPXdYquVgCqlN2mspr0dnDO44HsIAuVhtM2UsFH0V80ePbl9W8PaMjR+YYqXanLkVsln+xS87ODUptcbLJnSwF0+nWwyR1b71tuMjV28L4i8sVwaGQMJbWQTmyQjV+dNgLO9+JCw7Nrz+rypnPQ4OWfE5b5hfqc9LKxDJsV5IuJ76+7O4DyURyP4+3kjTUbTz/FERao6/CSSlaQShXNsDs8oV1nz5QfkgZdMFshNr994BudgAwb1Xhnfp6A5+4ZhxpSB/Z/e0uItaGJzcmBcLQZv/FXuOM337egUqWo67nTFEHtUnSlsLFZXx4+lpOMN/y0fFJXJOTly3eaOnyn83CsExZansLQeNXwapi4s4L+KZGnEgRp8ouAwkRs7YFa5B+qd4bxTJe7hNabqzMH4RcOFbSW2esyuWu8tcN831nRHI+i76KaGjz2zerWmVjbAOAKJDynRGnZ0wGtWix4mOiciDhSGe95OTP/ayFbIpJAgGdyp3/AA/JSPk7NqBYEqkyELxzRvsUYJdx86tCTALK7z5Cz7CKVjoaksRLrGurphQbPBMY+SRSlI7zEIg3DmtGJh6+pbJ0osV8bK2ffprfm75pod2EDYpFNCLxGWwkQyBQ0oKHs2vIl84Yv087xaE7bcqkIAcr5rilFDUfQhIL8Rdq4A1C0LBNWOkdlCoY0c2+RRU84F6XFm4FfptJkdAvEflMYH8/wfmrxNGj/NzgFsBKSJQvun9C+VMobRgxd0bxZoRH3Gnzk074D1c4kieWRQTfD2zGBmZB/e6SS2TMrRBCsIjmqXC0SyaxW+49aEa4PuDME+udNobAjVKxPh3Mmaxd94D0MhL2TRA1ATDyKJ/vdH5dvem9gcveNk5ZdQRswiJsmiND1RM7Gibbx9vM9wUU1RxI1AuVnM/DqxJ9dfktDb1qWcNKaBf/PrFjRrrK4gsmGJdcuCn935o6H+1/84YwZBnTkmLpjITxLIP5ee6rmZPqLKlD+1c0RDhM/CEDUqAymjw7YPC3RAgcXqK3h3AXj8T5e/na6izS79SuJoQc/zH/9cFRIC0PDTGVefuja+QagIA6zCSU4dnLKB/5JeykaFDnA6lPbZWqWkrjXCfbqXfXNGZg+8EyP6+EnJt33alcouFjnW4N4ZdzbjV08p6g8+MR72lJqMtVjdenl3ZTp8+0NbVU1qNTzPEOazsg2txKICTIAhrJ7hzk1peHrhTg9DBkgngSedFsuOZxCI3PtVEWPJbGH289vjqy9GBGhHkHXW748CnpxGKgHtG/uhJAA6oS7sjVUhkwaw4uOfREmPczokJeolf2QZIPHTkMWaN0SFmSGa6i1bMeFXSGxVLKDhWS0xZFWBwBr1EjgPA13hgXpxZPJ0xIoXTPzXRhpU/k4ALAhw13wYGIARdLJQWV8ttniSV/4NENo0wNX34iWUKaNY4xe6YGP8G4MSKxqj4t6gIgKMbss9EPUR13S6GjphukLJF6bp/w2MeBU9XW24aGHf9LLNk27ORSmyahGe"; + // decrypted pem + static const char _strDecrypted[] = "MIIEpAIBAAKCAQEAqegt555/R+0iotSoCDVF2Hnk8Z++Q05rY9mDep2On6Z7IEALqt1YTm46JOlyBQ3DocwUTO6evfxipEe1drJ5j0nxcHAuBQFv7QqCIWFQCNBrmAgAjtMXWVBWRVPx4Bxz6J57JFQWnsBVN3o6qXEmsvu1AQ5BLSnoJkgfVNUooAw4fumrcxrJniL9HWefch+5Twl5QBAq4pcMImy2j5CoGAinf3CMeewfj0gx71zSQrAh79stTvNXhKcll7+RXzQzSJ8x4T2SCnRqZoAkCm57dHHYPlUqMVpo5rp4+cvM8ThnGix3Bm3vHmo3ECTXmxWZnqVnyrW+xLrhwAng23Km5wIDAQABAoIBABoeqmfwEssg5nE31fklD26/FnYaw6ofNR0thCvmatWaR1Vm4yRKUNgF1AR2quOiARAvinNlP5wfX563s2ri9xMTH/3UEQ8N8IH1Wpn9Fsu0FsZRfJx4UR+W3RxdUlLMrPj23CFyl+Oh17fmn3wK/BMJ5QKbiWODDMBN2Bs3hb5MwoZPrNu2bDgUIGHEKKNO8JTeaQaL06NsiYyZ06cONTxVRSzbA6/ItcyaNURn3s8LWwq/Nw9CRE/sH8DngTD3nPcTCl9IW5Ly1Khktsps8FlIsPIcoGXxaYepi+EEEhn72AnC37tc5Hhy0raeR/V70SyQzBnJ2CpDj+WTeye3sWkCgYEA0ljKLDYNvSrTezSPPbbCk9+lvZoDIgtJBn7FERCH6wjAPi91+DsTk8UMtSUoOoRuA3CA8kG+t0N78+kpQv8GOtRynJdLB3XZ3bIao98Uy6GwihuKhlIw8aaammtKv4M/a+c9IAB6S92A32HnN8p3MdkKjH+Oe53U/dPnN4aVDj0CgYEAzsh79SdenVkAxwAhx4KcI5XIj2/1PyJ2HmjzFKPbHwZTbRhg0Bb+aqxtSxLL84YzbidG36ROoXgrR03/1D75c3GBintMpZU6iZLYuk105/JwjFfbeT7iVTMwbCyt2KceqbIPfuj4Fq4QHPk/3v0dyqFYrGx3biRWrb75Jja73/MCgYEAjJBfEipRBg+fYV7fNftyjcTPiiNoYpCuDRQb9upYGb0wUp3+tJIt4/qzIj4hYvFqXwwIb1t16hvDslwmganQbPHZYUFNF0AvBhJl8Qo7aFS1l6UN7fLw2Btghz6Zpd88O4w8ca1ADICKBTz0eXjoLDyA6yC2g28WjUTU52sdInUCgYBcx21+wKxeExobL774Qm0GNmVnnkc+jZrL6DDw2NgvImp6L474ruh2OmlLXuKtoFAhI3RUUeeJ4V4hvyiDNcI8/veth8cLIFrEcWPWq3xBufCvt5fc3c81hSM71gwmgk1qvF7hhWwS2QSXy2nSBmXAjgY4Tu6DN8DAckhd0f8X6wKBgQC2rHX1Z81G4d/ztPRx2FvhOqLMZSeMSzxZMxEWJH52MszQUlhHC7q32MiMkRmeqk9rwUQV2vCNljBBwdJuIV0OeSjwVfKznEOCL6vwyOEnVqxGo3IJhVPFFxxtEjhIOKznIDPzqdEfPqmzKn1AqRfXyizk9iucm4WbO7mvY59m8Q=="; + + int32_t iResult = 0; + // test aes128 + iResult |= _CmdCryptAesDecryptPrivateKey(16, "device.key", _strIv128, _strEncrypted128, _strDecrypted); + // test aes256 + iResult |= _CmdCryptAesDecryptPrivateKey(32, "device.key", _strIv256, _strEncrypted256, _strDecrypted); + return(iResult); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdCrypt + + \Description + Test the crypto modules + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 01/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdCrypt(ZContext *argz, int32_t argc, char *argv[]) +{ + // populate random buffer + CryptRandGet(_Crypt_aLargeRandom, sizeof(_Crypt_aLargeRandom)); + + if (argc < 2) + { + int32_t iFailed = 0; + iFailed += _CmdCryptTestArc4(argz, argc, argv); + iFailed += _CmdCryptTestChaCha(argz, argc, argv); + iFailed += _CmdCryptTestGcm(argz, argc, argv); + iFailed += _CmdCryptTestHmac(argz, argc, argv); + iFailed += _CmdCryptTestMurmurHash3(argz, argc, argv); + iFailed += _CmdCryptTestMD5(argz, argc, argv); + iFailed += _CmdCryptTestSha1(argz, argc, argv); + iFailed += _CmdCryptTestSha2(argz, argc, argv); + iFailed += _CmdCryptTestRSA(argz, argc, argv); + iFailed += _CmdCryptTestMont(argz, argc, argv); + iFailed += _CmdCryptTestNist(argz, argc, argv); + iFailed += _CmdCryptTestDsa(argz, argc, argv); + iFailed += _CmdCryptTestRand(argz, argc, argv); + iFailed += _CmdCryptTestAes(argz, argc, argv); + + ZPrintf("%s: %d tests failed\n", argv[0], iFailed); + } + else if (strcmp(argv[1], "rc4") == 0) + { + ZPrintf("%s: rc4 test %s\n", argv[0], _CmdCryptTestArc4(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "chacha20") == 0) + { + ZPrintf("%s: chacha20 test %s\n", argv[0], _CmdCryptTestChaCha(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "gcm") == 0) + { + ZPrintf("%s: gcm test %s\n", argv[0], _CmdCryptTestGcm(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "hmac") == 0) + { + ZPrintf("%s: hmac test %s\n", argv[0], _CmdCryptTestHmac(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "murmur3") == 0) + { + ZPrintf("%s: murmur3 test %s\n", argv[0], _CmdCryptTestMurmurHash3(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "md5") == 0) + { + ZPrintf("%s: md5 test %s\n", argv[0], _CmdCryptTestMD5(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "sha1") == 0) + { + ZPrintf("%s: sha1 test %s\n", argv[0], _CmdCryptTestSha1(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "sha2") == 0) + { + ZPrintf("%s: sha2 test %s\n", argv[0], _CmdCryptTestSha2(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "rsa") == 0) + { + ZPrintf("%s: rsa test %s\n", argv[0], _CmdCryptTestRSA(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "mont") == 0) + { + ZPrintf("%s: mont test %s\n", argv[0], _CmdCryptTestMont(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "nist") == 0) + { + ZPrintf("%s: nist test %s\n", argv[0], _CmdCryptTestNist(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "dsa") == 0) + { + ZPrintf("%s: dsa test %s\n", argv[0], _CmdCryptTestDsa(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "rand") == 0) + { + ZPrintf("%s: rand test %s\n", argv[0], _CmdCryptTestRand(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else if (strcmp(argv[1], "aes") == 0) + { + ZPrintf("%s: aes test %s\n", argv[0], _CmdCryptTestAes(argz, argc, argv) == 0 ? "succeeded" : "failed"); + } + else + { + ZPrintf("usage: %s [rc4|chacha20|gcm|hmac|murmur3|md5|sha1|sha2|rsa|mont|nist]\n", argv[0]); + ZPrintf("omitting command executes all tests in sequence\n"); + } + + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/demangler.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/demangler.c new file mode 100644 index 00000000..edcd74e4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/demangler.c @@ -0,0 +1,371 @@ +/*H********************************************************************************/ +/*! + \File demangler.c + + \Description + A simple tester of the EA.com demangler service, using ProtoMangle. + + \Copyright + Copyright (c) 2003-2005 Electronic Arts Inc. + + \Version 04/03/2003 (jbrookes) First Version +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protomangle.h" +#include "DirtySDK/game/netgameutil.h" +#include "DirtySDK/game/netgamelink.h" +#include "DirtySDK/game/netgamepkt.h" +#include "DirtySDK/comm/commudp.h" + +#include "libsample/zlib.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +#define DEMANGLER_CONN_TIMEOUT (15 * 1000) +#define DEMANGLER_MNGL_TIMEOUT (15 * 1000) +#define DEMANGLER_SHAREDSOCKETTEST (FALSE) // note: this test is a hack and is not valid on PS2 + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct DemanglerRefT +{ + ProtoMangleRefT *pMangler; + int32_t bRequestIssued; + int32_t iGamePort; + int32_t iPeerAddr; + int32_t iPeerPort; + int32_t iConnType; + int32_t iMnglStart; + int32_t iConnStart; + char strSessID[32]; + + NetGameUtilRefT *pUtilRef; + NetGameLinkRefT *pLinkRef; + + int32_t callcnt; + + #if DEMANGLER_SHAREDSOCKETTEST + SocketT *pSocket; + #endif + + // connection state + enum + { + IDLE, INIT, CONNINIT, LISTEN, FAIL, CONN, SHUTDOWN + } state; +} DemanglerRefT; + +/*** Function Prototypes ***************************************************************/ + + +/*** Variables *************************************************************************/ + + +// Private variables + +static DemanglerRefT *_pDemanglerRef = NULL; + +// Public variables + + +/*** Private Functions *****************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _CmdDemanglerCB + + \Description + Update demangling process. + + \Input *argz - + \Input argc - + \Input **argv - + + \Output int32_t - + + \Version 04/03/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdDemanglerCB(ZContext *argz, int32_t argc, char **argv) +{ + DemanglerRefT *pRef = (DemanglerRefT *)argz; + int32_t iResult; + + // check for kill + if (argc == 0) + { + if (pRef->pMangler != NULL) + { + ProtoMangleDestroy(pRef->pMangler); + } + if (pRef->pLinkRef != NULL) + { + NetGameLinkDestroy(pRef->pLinkRef); + } + if (pRef->pUtilRef != NULL) + { + NetGameUtilDestroy(pRef->pUtilRef); + } + return(0); + } + + // update demangler client + if (pRef->pMangler != NULL) + { + ProtoMangleUpdate(pRef->pMangler); + } + + if (pRef->state == INIT) + { + if (pRef->bRequestIssued == FALSE) + { + #if DEMANGLER_SHAREDSOCKETTEST + // create socket and bind it to game port + if (pRef->pSocket == NULL) + { + struct sockaddr bindaddr; + + pRef->pSocket = SocketOpen(AF_INET, SOCK_DGRAM, 0); + SockaddrInit(&bindaddr, AF_INET); + SockaddrInSetPort(&bindaddr, pRef->iGamePort); + SocketBind(pRef->pSocket, &bindaddr, sizeof(bindaddr)); + } + ProtoMangleConnectSocket(pRef->pMangler, (uint32_t)pRef->pSocket, pRef->strSessID); + #else + ProtoMangleConnect(pRef->pMangler, pRef->iGamePort, pRef->strSessID); + #endif + pRef->bRequestIssued = TRUE; + pRef->iMnglStart = NetTick(); + ZPrintf("demangler: connect issued at %d\n", NetTick()); + } + + if ((iResult = ProtoMangleComplete(pRef->pMangler, &pRef->iPeerAddr, &pRef->iPeerPort)) > 0) + { + ZPrintf("Demangler successful:\n"); + ZPrintf(" iPeerAddr: %a\n", pRef->iPeerAddr); + ZPrintf(" iPeerPort: %d\n", pRef->iPeerPort); + + pRef->state = CONNINIT; + } + else if (iResult < 0) + { + if (ProtoMangleStatus(pRef->pMangler, 'time', NULL, 0) == TRUE) + { + ZPrintf("demangler: timeout at %d\n", NetTick()); + } + else + { + ZPrintf("demangler: ProtoMangleComplete()=%d\n", iResult); + } + pRef->state = IDLE; + } + } + + if (pRef->state == CONNINIT) + { + char strAddr[48]; + + // create utilref + pRef->pUtilRef = NetGameUtilCreate(); + ds_snzprintf(strAddr, sizeof(strAddr), "%a:%d:%d", pRef->iPeerAddr, pRef->iGamePort, pRef->iPeerPort); + NetGameUtilConnect(pRef->pUtilRef, pRef->iConnType, strAddr, (CommAllConstructT *)CommUDPConstruct); + + pRef->iConnStart = NetTick(); + pRef->state = LISTEN; + } + + if (pRef->state == LISTEN) + { + void *pCommRef; + + // check for connect + pCommRef = NetGameUtilComplete(pRef->pUtilRef); + if (pCommRef != NULL) + { + // got a connect -- startup link + ZPrintf("%s: comm established\n", argv[0]); + pRef->pLinkRef = NetGameLinkCreate(pCommRef, FALSE, 8192); + ZPrintf("%s: link running\n", argv[0]); + pRef->state = CONN; + + // report success to protomangle server + ProtoMangleReport(pRef->pMangler, PROTOMANGLE_STATUS_CONNECTED, -1); + } + + // check for timeout + if ((NetTick() - pRef->iConnStart) > DEMANGLER_CONN_TIMEOUT) + { + // report failure to protomangle server + ProtoMangleReport(pRef->pMangler, PROTOMANGLE_STATUS_FAILED, -1); + pRef->state = FAIL; + } + } + + if (pRef->state == CONN) + { + NetGameLinkStatT Stats; + + // check connection status + NetGameLinkStatus(pRef->pLinkRef, 'stat', 0, &Stats, sizeof(NetGameLinkStatT)); + if (Stats.isopen) + { + NetGamePacketT TestPacket; + + // send an unreliable packet + TestPacket.head.kind = GAME_PACKET_USER_UNRELIABLE; + TestPacket.head.len = NETGAME_DATAPKT_DEFSIZE; + sprintf((char *)TestPacket.body.data, "demangler test packet %d", pRef->callcnt); + if (NetGameLinkSend(pRef->pLinkRef,&TestPacket,1) > 0) + { + pRef->callcnt++; + } + + // check for receive + while (NetGameLinkRecv(pRef->pLinkRef,&TestPacket,1,FALSE) > 0) + { + // sanity checks that the data got here OK + if (TestPacket.head.len != NETGAME_DATAPKT_DEFSIZE) + { + ZPrintf("packet size invalid\n"); + } + if ((TestPacket.head.kind != GAME_PACKET_USER_UNRELIABLE) && (TestPacket.head.kind != GAME_PACKET_USER)) + { + ZPrintf("packet type invalid\n"); + } + + // output data + ZPrintf("%s\n",(char *)TestPacket.body.data); + } + } + } + + if ((pRef->state == CONN) || (pRef->state == FAIL)) + { + // update ProtoMangle to send response to server + if (pRef->pMangler != NULL) + { + if (ProtoMangleComplete(pRef->pMangler, NULL, NULL) != 0) + { + ZPrintf("posted result to server\n"); + } + } + } + + return(ZCallback(&_CmdDemanglerCB, 100)); +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdDemangler + A simple tester of the EA.com demangler service, using ProtoMangle. + + \Description + + \Input *argz - + \Input argc - + \Input **argv - + + \Output + int32_t - + + \Version 04/03/2003 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdDemangler(ZContext *argz, int32_t argc, char **argv) +{ + const char *pServer, *pSessId; + uint8_t bReuseRef = TRUE; + DemanglerRefT *pRef; + + // usage + if ((argc < 3) || (argc > 4) || (strcmp(argv[1], "conn") && strcmp(argv[1], "list") && strcmp(argv[1], "disc") && strcmp(argv[1], "dest"))) + { + ZPrintf(" connect to a peer using demangler server\n"); + ZPrintf(" usage: %s [conn|list|disc|dest] [sessID]\n", argv[0]); + return(0); + } + + // destroy? + if (!strcmp(argv[1], "dest")) + { + if (_pDemanglerRef != NULL) + { + _CmdDemanglerCB((ZContext *)_pDemanglerRef, 0, NULL); + _pDemanglerRef = NULL; + } + else + { + ZPrintf(" %s: no module to destroy\n", argv[0]); + } + return(0); + } + + // disconnect? + if (!strcmp(argv[1], "disc")) + { + if (_pDemanglerRef != NULL) + { + if (_pDemanglerRef->pLinkRef != NULL) + { + NetGameLinkDestroy(_pDemanglerRef->pLinkRef); + } + if (_pDemanglerRef->pUtilRef != NULL) + { + NetGameUtilDestroy(_pDemanglerRef->pUtilRef); + } + } + return(0); + } + + // resolve server and session identifer + if (argc == 4) + { + pServer = argv[2]; + pSessId = argv[3]; + } + else + { + pServer = PROTOMANGLE_SERVER; + pSessId = argv[2]; + } + + // allocate context? + if ((pRef = _pDemanglerRef) == NULL) + { + pRef = (DemanglerRefT *) ZContextCreate(sizeof(*pRef)); + ds_memclr(pRef, sizeof(*pRef)); + + pRef->pMangler = ProtoMangleCreate(pServer, PROTOMANGLE_PORT, "mangletest-pc-2004", ""); + ProtoMangleControl(pRef->pMangler, 'time', DEMANGLER_MNGL_TIMEOUT, 0, NULL); + + _pDemanglerRef = pRef; + bReuseRef = FALSE; + } + + strcpy(pRef->strSessID, pSessId); + pRef->iConnType = (!strcmp(argv[1],"conn")) ? NETGAME_CONN_CONNECT : NETGAME_CONN_LISTEN; + pRef->state = INIT; + pRef->iGamePort = 10000; + pRef->bRequestIssued = FALSE; + + return(bReuseRef ? 0 : ZCallback(&_CmdDemanglerCB, 100)); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/gamelink.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/gamelink.c new file mode 100644 index 00000000..9ee93de1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/gamelink.c @@ -0,0 +1,626 @@ +/*H********************************************************************************/ +/*! + \File gamelink.c + + \Description + Test NetGameLink + + \Copyright + Copyright (c) Electronic Arts 2018. + + \Version 02/02/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#ifdef _WIN32 + #pragma warning(push,0) + #include + #pragma warning(pop) +#endif + +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/game/netgameutil.h" +#include "DirtySDK/game/netgamelink.h" +#include "DirtySDK/comm/commudp.h" + +#if defined(DIRTYCODE_XBOXONE) +#include "DirtySDK/DirtySock/dirtyaddr.h" +#endif + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +//! gamelink states +enum +{ + GAMELINK_STATUS_DISC, + GAMELINK_STATUS_CONN, + GAMELINK_STATUS_OPEN, + GAMELINK_STATUS_ACTV +}; + +//! default gamelink port +#define GAMELINK_PORT (3658) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct GameLinkAppT +{ + NetGameUtilRefT *pGameUtil; + NetGameLinkRefT *pGameLink; + uint32_t uLoclAddr; + uint8_t bZCallback; + uint8_t uLinkStatus; + uint8_t uPackSendVal; + uint8_t _pad[2]; +} GameLinkAppT; + +/*** Function Prototypes **********************************************************/ + +static void _GameLinkCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _GameLinkDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _GameLinkConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _GameLinkControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _GameLinkSend(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables ********************************************************************/ + +static T2SubCmdT _GameLink_Commands[] = +{ + { "create", _GameLinkCreate }, + { "destroy", _GameLinkDestroy }, + { "connect", _GameLinkConnect }, + { "ctrl", _GameLinkControl }, + { "send", _GameLinkSend }, + { "", NULL }, +}; + +static GameLinkAppT _GameLink_App = { 0 }; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _GetIntArg + + \Description + Get fourcc/integer from command-line argument + + \Input *pArg - pointer to argument + + \Version 10/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _GetIntArg(const char *pArg) +{ + int32_t iValue; + + // check for possible fourcc value + if ((strlen(pArg) == 4) && (isalpha(pArg[0]) || isalpha(pArg[1]) || isalpha(pArg[2]) || isalpha(pArg[3]))) + { + iValue = pArg[0] << 24; + iValue |= pArg[1] << 16; + iValue |= pArg[2] << 8; + iValue |= pArg[3]; + } + else + { + iValue = (signed)strtol(pArg, NULL, 10); + } + return(iValue); +} + +/*F********************************************************************************/ +/*! + \Function _GetConnectParms + + \Description + Create connection string for gamelink connection + + \Input *pConnName - [out] connection string + \Input iNameSize - size of connname buffer + \Input uLoclAddr - local address + \Input uLoclPort - local port + \Input uConnAddr - peer address + \Input uConnPort - peer port + + \Output + int32_t - connection flags + + \Version 02/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _GetConnectParms(char *pConnName, int32_t iNameSize, uint32_t uLoclAddr, uint16_t uLoclPort, uint32_t uConnAddr, uint16_t uConnPort) +{ + char strLoclAddr[32], strConnAddr[32]; + int32_t iConnFlags = NETGAME_CONN_CONNECT; + uint32_t uAddrTemp; + uint8_t bHosting; + + ds_snzprintf(strLoclAddr, sizeof(strLoclAddr), "%a:%d", uLoclAddr, uLoclPort); + ds_snzprintf(strConnAddr, sizeof(strConnAddr), "%a:%d", uConnAddr, uConnPort); + + bHosting = (strcmp(strLoclAddr, strConnAddr) < 0) ? TRUE : FALSE; + + /* set up parms based on whether we are "hosting" or not. the connection name is the + unique address of the "host" concatenated with the unique address of the "client" */ + if (bHosting == TRUE) + { + // swap names + uAddrTemp = uConnAddr; + uConnAddr = uLoclAddr; + uLoclAddr = uAddrTemp; + // set conn flags to listen + iConnFlags = NETGAME_CONN_LISTEN; + } + + // format connection name and return connection flags + ds_snzprintf(pConnName, iNameSize, "%x-%x", uLoclAddr, uConnAddr); + return(iConnFlags); +} + +/*F********************************************************************************/ +/*! + \Function _GameUtilConnect + + \Description + Connect to peer + + \Input *pApp - module state + \Input uLoclAddr - local address + \Input uLoclPort - local port + \Input uConnAddr - peer address + \Input uConnPort - peer port + + \Output + int32_t - result of NetGameUtilConnect, or -1 on failure + + \Version 02/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _GameUtilConnect(GameLinkAppT *pApp, uint32_t uLoclAddr, uint16_t uLoclPort, uint32_t uConnAddr, uint16_t uConnPort) +{ + char strConn[128], strConnName[64]; + int32_t iConnFlags; + + if ((pApp->pGameUtil == NULL) && ((pApp->pGameUtil = NetGameUtilCreate()) == NULL)) + { + ZPrintf("gamelink: could not create util ref\n"); + return(-1); + } + + // set game link options +#if 0 + _SetGamelinkOpt(pClient->pGameUtilRef, 'minp', pConnApi->iGameMinp); + _SetGamelinkOpt(pClient->pGameUtilRef, 'mout', pConnApi->iGameMout); + _SetGamelinkOpt(pClient->pGameUtilRef, 'mwid', pConnApi->iGameMwid); + if (pConnApi->iGameUnackLimit != 0) + { + _SetGamelinkOpt(pClient->pGameUtilRef, 'ulmt', pConnApi->iGameUnackLimit); + } + + // set our client id (used by gameserver to uniquely identify us) + NetGameUtilControl(pClient->pGameUtilRef, 'clid', pClient->ClientInfo.uLocalClientId); + NetGameUtilControl(pClient->pGameUtilRef, 'rcid', pClient->ClientInfo.uRemoteClientId); +#endif + + // determine connection parameters + iConnFlags = _GetConnectParms(strConnName, sizeof(strConnName), uLoclAddr, uLoclPort, uConnAddr, uConnPort); + + // format connect string + ds_snzprintf(strConn, sizeof(strConn), "%a:%d:%d#%s", uConnAddr, uLoclPort, uConnPort, strConnName); + + // start the connection attempt + return(NetGameUtilConnect(pApp->pGameUtil, iConnFlags, strConn, (CommAllConstructT *)CommUDPConstruct)); +} + +/*F********************************************************************************/ +/*! + \Function _GameLinkDestroyApp + + \Description + Destroy app, clearing state + + \Input *pApp - app state + + \Version 02/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _GameLinkDestroyApp(GameLinkAppT *pApp) +{ + ds_memclr(pApp, sizeof(*pApp)); +} + +/* + + GameLink Commands + +*/ + +/*F*************************************************************************************/ +/*! + \Function _GameLinkCreate + + \Description + GameLink subcommand - create gamelink module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE if usage request + + \Version 02/02/2018 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _GameLinkCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + GameLinkAppT *pApp = &_GameLink_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s create\n", argv[0]); + return; + } + + pApp->uLoclAddr = SocketInfo(NULL, 'addr', 0, NULL, 0); +} + +/*F*************************************************************************************/ +/*! + \Function _GameLinkDestroy + + \Description + GameLink subcommand - destroy gamelink module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE if usage request + + \Version 02/02/2018 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _GameLinkDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + GameLinkAppT *pApp = &_GameLink_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + _GameLinkDestroyApp(pApp); +} + +/*F*************************************************************************************/ +/*! + \Function _GameLinkConnect + + \Description + GameLink subcommand - connect to peer + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE if usage request + + \Version 02/02/2018 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _GameLinkConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + GameLinkAppT *pApp = &_GameLink_App; + uint32_t uConnAddr, uConnPort, uLoclPort, uRemoteClientId; + + if ((bHelp == TRUE) || (argc > 4)) + { + ZPrintf(" usage: %s connect [rclientid]\n", argv[0]); + return; + } + + // get remote address, port, and local port (if specified) in addr:port:port2 format + SockaddrInParse2(&uConnAddr, (int32_t *)&uConnPort, (int32_t *)&uLoclPort, argv[2]); + if (uConnPort == 0) + { + uConnPort = GAMELINK_PORT; + } + if (uLoclPort == 0) + { + uLoclPort = GAMELINK_PORT; + } + + // get remote client id, or use remote address if unspecified + uRemoteClientId = (argc == 4) ? (uint32_t)strtol(argv[3], NULL, 16) : uConnAddr; + + // start the connect + if (_GameUtilConnect(pApp, pApp->uLoclAddr, uLoclPort, uConnAddr, uConnPort) < 0) + { + ZPrintf("%s: error trying to connect\n"); + return; + } + // log connection attempt, uptdate status + ZPrintf("%s: connecting to %a:%u:%u (clientId=0x%08x)\n", argv[0], uConnAddr, uConnPort, uLoclPort, uRemoteClientId); + pApp->uLinkStatus = GAMELINK_STATUS_CONN; +} + +/*F*************************************************************************************/ +/*! + \Function _GameLinkControl + + \Description + GameLink control subcommand - set control options + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE if usage request + + \Version 02/02/2018 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _GameLinkControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + GameLinkAppT *pApp = &_GameLink_App; + int32_t iCmd, iValue = 0; + + if ((argc < 3) && (argc > 4)) + { + ZPrintf("%s: invalid ctrl command\n", argv[0]); + bHelp = TRUE; + } + if (bHelp == TRUE) + { + ZPrintf(" usage: %s ctrl \n", argv[0]); + return; + } + + // get control command + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + // get control argument, if specified + if (argc > 3) + { + iValue = _GetIntArg(argv[3]); + } + + // pass it down + NetGameLinkControl(pApp->pGameLink, iCmd, iValue, NULL); +} + +/*F*************************************************************************************/ +/*! + \Function _GameLinkSend + + \Description + GameLink subcommand - send data + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE if usage request + + \Version 02/02/2018 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _GameLinkSend(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + GameLinkAppT *pApp = &_GameLink_App; + NetGameMaxPacketT Packet; + int32_t iSize, iResult; + + if (argc != 3) + { + ZPrintf("%s: invalid send command\n", argv[0]); + bHelp = TRUE; + } + if (bHelp == TRUE) + { + ZPrintf(" usage: %s send \n", argv[0]); + return; + } + + // get size + if (((iSize = _GetIntArg(argv[2])) < 0) || (iSize > NETGAME_DATAPKT_MAXSIZE)) + { + ZPrintf("%s: size %d invalid, setting size to %d\n", NETGAME_DATAPKT_MAXSIZE); + iSize = NETGAME_DATAPKT_MAXSIZE; + } + + // format packet head + ds_memclr(&Packet.head, sizeof(Packet.head)); + Packet.head.kind = GAME_PACKET_USER; + Packet.head.len = iSize; + + // format packet data + ds_memset(&Packet.body.data, pApp->uPackSendVal++, iSize); + + // send the packet + iResult = NetGameLinkSend(pApp->pGameLink, (NetGamePacketT *)&Packet, 1); + ZPrintf("%s: sent %d bytes\n", argv[0], iResult); +} + +/*F********************************************************************************/ +/*! + \Function _CmdGameLinkCb + + \Description + Update gamelink command + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - standard return value + + \Version 02/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdGameLinkCb(ZContext *argz, int32_t argc, char *argv[]) +{ + GameLinkAppT *pApp = &_GameLink_App; + + // check for kill + if (argc == 0) + { + _GameLinkDestroyApp(pApp); + ZPrintf("%s: killed\n", argv[0]); + return(0); + } + + // update in connecting state + if (pApp->uLinkStatus == GAMELINK_STATUS_CONN) + { + CommRef *pCommRef; + + // check for established connection + if ((pCommRef = NetGameUtilComplete(pApp->pGameUtil)) != NULL) + { + if ((pApp->pGameLink = NetGameLinkCreate(pCommRef, FALSE, 8192)) != NULL) + { + ZPrintf("%s: game connection established\n", argv[0]); + // indicate we've connected + pApp->uLinkStatus = GAMELINK_STATUS_OPEN; + } + else + { + ZPrintf("%s: game connection failed\n", argv[0]); + pApp->uLinkStatus = GAMELINK_STATUS_DISC; + } + } + } + + // update in open state + if (pApp->uLinkStatus == GAMELINK_STATUS_OPEN) + { + NetGameLinkStatT Stat; + + // give time to NetGameLink to run any connection-related processes + NetGameLinkUpdate(pApp->pGameLink); + + // get link stats + NetGameLinkStatus(pApp->pGameLink, 'stat', 0, &Stat, sizeof(NetGameLinkStatT)); + + // see if we're open + if (Stat.isopen == TRUE) + { + // mark as active + ZPrintf("%s: game connection is active\n", argv[0]); + pApp->uLinkStatus = GAMELINK_STATUS_ACTV; + } + } + + // update in active state + if (pApp->uLinkStatus == GAMELINK_STATUS_ACTV) + { + NetGameMaxPacketT Packet; + NetGameLinkStatT Stat; + + // get link stats + NetGameLinkStatus(pApp->pGameLink, 'stat', 0, &Stat, sizeof(NetGameLinkStatT)); + + // make sure connection is still open + if (Stat.isopen == FALSE) + { + ZPrintf("%s: game connection closed\n", argv[0]); + pApp->uLinkStatus = GAMELINK_STATUS_DISC; + } + // see if we've timed out + else if (NetTickDiff(Stat.tick, Stat.rcvdlast) > 20000) + { + ZPrintf("%s: game connection timed out\n", argv[0]); + pApp->uLinkStatus = GAMELINK_STATUS_DISC; + } + + // try and receive something; if we get something echo it to output + if (NetGameLinkRecv(pApp->pGameLink, (NetGamePacketT *)&Packet, sizeof(Packet.body.data), FALSE) > 0) + { + // cap max size to echo + #if DIRTYCODE_DEBUG + int32_t iSize = DS_MIN(Packet.head.len, 64); + NetPrintMem(Packet.body.data, iSize, "packet data"); + #endif + } + } + + // keep running + return(ZCallback(&_CmdGameLinkCb, 16)); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdGameLink + + \Description + Initiate GameLink connection. + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - standard return value + + \Version 02/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdGameLink(ZContext *argz, int32_t argc, char *argv[]) +{ + GameLinkAppT *pApp = &_GameLink_App; + T2SubCmdT *pCmd; + uint8_t bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_GameLink_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the gamelink module\n"); + T2SubCmdUsage(argv[0], _GameLink_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _GameLinkCreate) && (pApp->pGameLink == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _GameLinkCreate(pApp, 1, &pCreate, bHelp); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + return(ZCallback(_CmdGameLinkCb, 16)); + } + else + { + return(0); + } +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/help.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/help.c new file mode 100644 index 00000000..dd7efdf2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/help.c @@ -0,0 +1,84 @@ +/*H********************************************************************************/ +/*! + \File help.c + + \Description + Handles help for tester2. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" + +#include "libsample/zlib.h" + +#include "testerregistry.h" +#include "testermodules.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdHelp + + \Description + Do some registry operations + + \Input *argz - environment + \Input argc - number of args + \Input **argv - argument list + + \Output int32_t - standard return code + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t CmdHelp(ZContext *argz, int32_t argc, char **argv) +{ + TesterModulesT *pModules; + + // get the modules pointer from the registry, if available + pModules = (TesterModulesT *)TesterRegistryGetPointer("MODULES"); + + // as part of the help function, the help command is called to get help on help. + // stop the recusion by not calling the TesterModulesHelp function for help(NULL) + if (pModules == NULL) + { + // no modules created + ZPrintf("HELP: No module help available\n"); + } + else if (argc < 1) + { + // get help on help + ZPrintf(" get help on modules\n"); + ZPrintf(" usage: %s \n", argv[0]); + } + else if (argc == 2) + { + // get help on a specific command + TesterModulesHelp(pModules, argv[1]); + } + else + { + // get help on all commands by default + TesterModulesHelp(pModules, NULL); + } + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/history.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/history.c new file mode 100644 index 00000000..463fcd22 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/history.c @@ -0,0 +1,131 @@ +/*H********************************************************************************/ +/*! + \File history.c + + \Description + Handles the history command for Tester2 + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" + +#include "libsample/zmem.h" +#include "libsample/zlib.h" + +#include "testercomm.h" +#include "testerregistry.h" +#include "testermodules.h" +#include "testerhistory.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdHistory + + \Description + Handle the history command + + \Input *argz - environment + \Input argc - number of args + \Input **argv - argument list + + \Output int32_t - standard return code + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t CmdHistory(ZContext *argz, int32_t argc, char **argv) +{ + int32_t iNum, iHeadCount, iTailCount; + TesterModulesT *pModules; + TesterHistoryT *pHistory; + TesterCommT *pComm; + const char *pText; + + // get the history module (if we can) + if ((pHistory = (TesterHistoryT *)TesterRegistryGetPointer("HISTORY")) == NULL) + { + ZPrintf("history: could not get information about history - no history module accessible.\n"); + return(-1); + } + + // history help? + if (argc < 1) + { + ZPrintf(" see previously executed command lines\n"); + ZPrintf(" usage: %s ", argv[0]); + return(0); + } + + // otherwise, get some history info + TesterHistoryHeadTailCount(pHistory, &iHeadCount, &iTailCount); + + // see if we just want to dump the history + if (argc == 1) + { + for (iNum = iHeadCount; iNum <= iTailCount; iNum++) + { + if ((pText = TesterHistoryGet(pHistory, iNum, NULL, 0)) != NULL) + { + ZPrintf("history: - [%5d] {%s}\n",iNum, pText); + } + } + } + + // see if we want to execute a command + if (argc == 2) + { + iNum = atoi(argv[1]); + pText = TesterHistoryGet(pHistory, iNum, NULL, 0); + // don't recursively execute the history command itself + if ((pText != NULL) && (iNum < iTailCount)) + { + // execute it locally + if ((pModules = TesterRegistryGetPointer("MODULES")) == NULL) + { + ZPrintf("history: could not dispatch historical command locally {%s}\n", pText); + } + else + { + TesterModulesDispatch(pModules, pText); + } + + // send it to the other side for execution + if ((pComm = (TesterCommT *)TesterRegistryGetPointer("COMM")) == NULL) + { + ZPrintf("history: could not dispatch historical command remotely {%s}\n", pText); + } + else + { + TesterCommMessage(pComm, TESTER_MSGTYPE_COMMAND, pText); + } + } + else + { + ZPrintf("history: - [%5d] error getting historical command number [%s].\n", iNum, argv[1]); + } + } + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/hpack.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/hpack.c new file mode 100644 index 00000000..af40695f --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/hpack.c @@ -0,0 +1,429 @@ +/*H********************************************************************************/ +/*! + \File hpack.c + + \Description + Test the HPACK encoder and decoder. + + \Copyright + Copyright (c) 2016 Electronic Arts Inc. + + \Version 10/07/2016 (eesponda) +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/util/hpack.h" + +#include "testermodules.h" + +/*** Private functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CmdHpackDecodeRequest + + \Description + Test cases from the RFC Appendix C.3 + Link: https://tools.ietf.org/html/rfc7541#appendix-C.3 + + \Version 10/07/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackDecodeRequest(void) +{ + HpackRefT *pRef; + char strHeader[1024*1]; + uint8_t aFirstRequest[] = { 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, + 0x6f, 0x6d }; + uint8_t aSecondRequest[] = { 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, + 0x63, 0x61, 0x63, 0x68, 0x65 }; + uint8_t aThirdRequest[] = { 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x76, 0x61, 0x6c, + 0x75, 0x65 }; + + if ((pRef = HpackCreate(4096, TRUE)) == NULL) + { + return; + } + + HpackDecode(pRef, aFirstRequest, sizeof(aFirstRequest), strHeader, sizeof(strHeader)); + ZPrintf("first request\n%s\n", strHeader); + HpackDecode(pRef, aSecondRequest, sizeof(aSecondRequest), strHeader, sizeof(strHeader)); + ZPrintf("second request\n%s\n", strHeader); + HpackDecode(pRef, aThirdRequest, sizeof(aThirdRequest), strHeader, sizeof(strHeader)); + ZPrintf("third request\n%s\n", strHeader); + + HpackDestroy(pRef); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHpackDecodeResponse + + \Description + Test cases from the RFC Appendix C.5 + Link: https://tools.ietf.org/html/rfc7541#appendix-C.5 + + \Version 10/07/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackDecodeResponse(void) +{ + HpackRefT *pRef; + char strHeader[1024*1]; + uint8_t aFirstResponse[] = { 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, + 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, + 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d }; + uint8_t aSecondResponse[] = { 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf }; + uint8_t aThirdResponse[] = { 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, + 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, + 0x54, 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, + 0x44, 0x4a, 0x4b, 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, + 0x55, 0x41, 0x58, 0x51, 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61, + 0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x3d, 0x31 }; + + if ((pRef = HpackCreate(256, TRUE)) == NULL) + { + return; + } + + HpackDecode(pRef, aFirstResponse, sizeof(aFirstResponse), strHeader, sizeof(strHeader)); + ZPrintf("first response\n%s\n", strHeader); + HpackDecode(pRef, aSecondResponse, sizeof(aSecondResponse), strHeader, sizeof(strHeader)); + ZPrintf("second response\n%s\n", strHeader); + HpackDecode(pRef, aThirdResponse, sizeof(aThirdResponse), strHeader, sizeof(strHeader)); + ZPrintf("third response\n%s\n", strHeader); + + HpackDestroy(pRef); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHpackDecodeRequestHuffman + + \Description + Test cases from the RFC Appendix C.4 + Link: https://tools.ietf.org/html/rfc7541#appendix-C.4 + + \Version 10/18/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackDecodeRequestHuffman(void) +{ + HpackRefT *pRef; + char strHeader[1024*1]; + uint8_t aFirstRequest[] = { 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff }; + uint8_t aSecondRequest[] = { 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf }; + uint8_t aThirdRequest[] = { 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x89, 0x25, + 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf }; + + if ((pRef = HpackCreate(4096, TRUE)) == NULL) + { + return; + } + + HpackDecode(pRef, aFirstRequest, sizeof(aFirstRequest), strHeader, sizeof(strHeader)); + ZPrintf("first request (huffman)\n%s\n", strHeader); + HpackDecode(pRef, aSecondRequest, sizeof(aSecondRequest), strHeader, sizeof(strHeader)); + ZPrintf("second request (huffman)\n%s\n", strHeader); + HpackDecode(pRef, aThirdRequest, sizeof(aThirdRequest), strHeader, sizeof(strHeader)); + ZPrintf("third request (huffman)\n%s\n", strHeader); + + HpackDestroy(pRef); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHpackDecodeResponseHuffman + + \Description + Test cases from the RFC Appendix C.6 + Link: https://tools.ietf.org/html/rfc7541#appendix-C.6 + + \Version 10/18/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackDecodeResponseHuffman(void) +{ + HpackRefT *pRef; + char strHeader[1024*1]; + uint8_t aFirstResponse[] = { 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, + 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, + 0x2d, 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, + 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3 }; + uint8_t aSecondResponse[] = { 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf }; + uint8_t aThirdResponse[] = { 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, + 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, + 0x77, 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, + 0x39, 0x60, 0xd5, 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, + 0x95, 0x87, 0x31, 0x60, 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07 }; + + if ((pRef = HpackCreate(256, TRUE)) == NULL) + { + return; + } + + HpackDecode(pRef, aFirstResponse, sizeof(aFirstResponse), strHeader, sizeof(strHeader)); + ZPrintf("first response (huffman)\n%s\n", strHeader); + HpackDecode(pRef, aSecondResponse, sizeof(aSecondResponse), strHeader, sizeof(strHeader)); + ZPrintf("second response (huffman)\n%s\n", strHeader); + HpackDecode(pRef, aThirdResponse, sizeof(aThirdResponse), strHeader, sizeof(strHeader)); + ZPrintf("third response (huffman)\n%s\n", strHeader); + + HpackDestroy(pRef); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHpackEncodeRequest + + \Description + Test cases from the RFC Appendix C.3 (encoding) + Link: https://tools.ietf.org/html/rfc7541#appendix-C.3 + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackEncodeRequest(void) +{ + HpackRefT *pRef; + uint8_t aOutput[1024] = { 0 }; + char strFirstRequest[] = ":method: GET\r\n:scheme: http\r\n:path: /\r\n:authority: www.example.com\r\n"; + uint8_t aFirstRequest[] = { 0x82, 0x86, 0x84, 0x41, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x2e, 0x63, 0x6f, 0x6d }; + + char strSecondRequest[] = ":method: GET\r\n:scheme: http\r\n:path: /\r\n:authority: www.example.com\r\ncache-control: no-cache\r\n"; + uint8_t aSecondRequest[] = { 0x82, 0x86, 0x84, 0xbe, 0x58, 0x08, 0x6e, 0x6f, 0x2d, 0x63, 0x61, 0x63, 0x68, 0x65 }; + + char strThirdRequest[] = ":method: GET\r\n:scheme: https\r\n:path: /index.html\r\n:authority: www.example.com\r\ncustom-key: custom-value\r\n"; + uint8_t aThirdRequest[] = { 0x82, 0x87, 0x85, 0xbf, 0x40, 0x0a, 0x63, 0x75, 0x73, + 0x74, 0x6f, 0x6d, 0x2d, 0x6b, 0x65, 0x79, 0x0c, 0x63, + 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x2d, 0x76, 0x61, 0x6c, + 0x75, 0x65 }; + + if ((pRef = HpackCreate(4096, FALSE)) == NULL) + { + return; + } + + HpackEncode(pRef, strFirstRequest, aOutput, sizeof(aOutput), FALSE); + if (memcmp(aOutput, aFirstRequest, sizeof(aFirstRequest)) == 0) + { + ZPrintf("successfully encoded first request\n"); + } + HpackEncode(pRef, strSecondRequest, aOutput, sizeof(aOutput), FALSE); + if (memcmp(aOutput, aSecondRequest, sizeof(aSecondRequest)) == 0) + { + ZPrintf("successfully encoded second request\n"); + } + HpackEncode(pRef, strThirdRequest, aOutput, sizeof(aOutput), FALSE); + if (memcmp(aOutput, aThirdRequest, sizeof(aThirdRequest)) == 0) + { + ZPrintf("successfully encoded third request\n"); + } + + HpackDestroy(pRef); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHpackEncodeResponse + + \Description + Test cases from the RFC Appendix C.5 (encode) + Link: https://tools.ietf.org/html/rfc7541#appendix-C.5 + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackEncodeResponse(void) +{ + HpackRefT *pRef; + uint8_t aOutput[1024] = { 0 }; + + char strFirstResponse[] = ":status: 302\r\ncache-control: private\r\ndate: Mon, 21 Oct 2013 20:13:21 GMT\r\nlocation: https://www.example.com\r\n"; + uint8_t aFirstResponse[] = { 0x48, 0x03, 0x33, 0x30, 0x32, 0x58, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x61, 0x1d, + 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x32, 0x30, 0x31, 0x33, + 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x31, 0x20, 0x47, 0x4d, 0x54, 0x6e, 0x17, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d }; + + char strSecondResponse[] = ":status: 307\r\ncache-control: private\r\ndate: Mon, 21 Oct 2013 20:13:21 GMT\r\nlocation: https://www.example.com\r\n"; + uint8_t aSecondResponse[] = { 0x48, 0x03, 0x33, 0x30, 0x37, 0xc1, 0xc0, 0xbf }; + + char strThirdResponse[] = ":status: 200\r\ncache-control: private\r\ndate: Mon, 21 Oct 2013 20:13:22 GMT\r\nlocation: https://www.example.com\r\ncontent-encoding: gzip\r\nset-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\r\n"; + uint8_t aThirdResponse[] = { 0x88, 0xc1, 0x61, 0x1d, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x31, 0x20, 0x4f, 0x63, 0x74, 0x20, + 0x32, 0x30, 0x31, 0x33, 0x20, 0x32, 0x30, 0x3a, 0x31, 0x33, 0x3a, 0x32, 0x32, 0x20, 0x47, 0x4d, + 0x54, 0xc0, 0x5a, 0x04, 0x67, 0x7a, 0x69, 0x70, 0x77, 0x38, 0x66, 0x6f, 0x6f, 0x3d, 0x41, 0x53, + 0x44, 0x4a, 0x4b, 0x48, 0x51, 0x4b, 0x42, 0x5a, 0x58, 0x4f, 0x51, 0x57, 0x45, 0x4f, 0x50, 0x49, + 0x55, 0x41, 0x58, 0x51, 0x57, 0x45, 0x4f, 0x49, 0x55, 0x3b, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61, + 0x67, 0x65, 0x3d, 0x33, 0x36, 0x30, 0x30, 0x3b, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x3d, 0x31 }; + + if ((pRef = HpackCreate(4096, FALSE)) == NULL) + { + return; + } + + HpackEncode(pRef, strFirstResponse, aOutput, sizeof(aOutput), FALSE); + if (memcmp(aOutput, aFirstResponse, sizeof(aFirstResponse)) == 0) + { + ZPrintf("successfully encoded first response\n"); + } + HpackEncode(pRef, strSecondResponse, aOutput, sizeof(aOutput), FALSE); + if (memcmp(aOutput, aSecondResponse, sizeof(aSecondResponse)) == 0) + { + ZPrintf("successfully encoded second response\n"); + } + HpackEncode(pRef, strThirdResponse, aOutput, sizeof(aOutput), FALSE); + if (memcmp(aOutput, aThirdResponse, sizeof(aThirdResponse)) == 0) + { + ZPrintf("successfully encoded third response\n"); + } + + HpackDestroy(pRef); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHpackEncodeRequestHuffman + + \Description + Test cases from the RFC Appendix C.4 (encode) + Link: https://tools.ietf.org/html/rfc7541#appendix-C.4 + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackEncodeRequestHuffman(void) +{ + HpackRefT *pRef; + uint8_t aOutput[1024] = { 0 }; + char strFirstRequest[] = ":method: GET\r\n:scheme: http\r\n:path: /\r\n:authority: www.example.com\r\n"; + char strSecondRequest[] = ":method: GET\r\n:scheme: http\r\n:path: /\r\n:authority: www.example.com\r\ncache-control: no-cache\r\n"; + char strThirdRequest[] = ":method: GET\r\n:scheme: https\r\n:path: /index.html\r\n:authority: www.example.com\r\ncustom-key: custom-value\r\n"; + uint8_t aFirstRequest[] = { 0x82, 0x86, 0x84, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff }; + uint8_t aSecondRequest[] = { 0x82, 0x86, 0x84, 0xbe, 0x58, 0x86, 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf }; + uint8_t aThirdRequest[] = { 0x82, 0x87, 0x85, 0xbf, 0x40, 0x88, 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f, 0x89, 0x25, + 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf }; + + if ((pRef = HpackCreate(4096, FALSE)) == NULL) + { + return; + } + + HpackEncode(pRef, strFirstRequest, aOutput, sizeof(aOutput), TRUE); + if (memcmp(aOutput, aFirstRequest, sizeof(aFirstRequest)) == 0) + { + ZPrintf("successfully encoded first request (huffman)\n"); + } + HpackEncode(pRef, strSecondRequest, aOutput, sizeof(aOutput), TRUE); + if (memcmp(aOutput, aSecondRequest, sizeof(aSecondRequest)) == 0) + { + ZPrintf("successfully encoded second request (huffman)\n"); + } + HpackEncode(pRef, strThirdRequest, aOutput, sizeof(aOutput), TRUE); + if (memcmp(aOutput, aThirdRequest, sizeof(aThirdRequest)) == 0) + { + ZPrintf("successfully encoded third request (huffman)\n"); + } + + HpackDestroy(pRef); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHpackEncodeResponseHuffman + + \Description + Test cases from the RFC Appendix C.6 (encode) + Link: https://tools.ietf.org/html/rfc7541#appendix-C.6 + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHpackEncodeResponseHuffman(void) +{ + HpackRefT *pRef; + uint8_t aOutput[1024] = { 0 }; + + char strFirstResponse[] = ":status: 302\r\ncache-control: private\r\ndate: Mon, 21 Oct 2013 20:13:21 GMT\r\nlocation: https://www.example.com\r\n"; + char strSecondResponse[] = ":status: 307\r\ncache-control: private\r\ndate: Mon, 21 Oct 2013 20:13:21 GMT\r\nlocation: https://www.example.com\r\n"; + char strThirdResponse[] = ":status: 200\r\ncache-control: private\r\ndate: Mon, 21 Oct 2013 20:13:22 GMT\r\nlocation: https://www.example.com\r\ncontent-encoding: gzip\r\nset-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\r\n"; + uint8_t aFirstResponse[] = { 0x48, 0x82, 0x64, 0x02, 0x58, 0x85, 0xae, 0xc3, 0x77, 0x1a, 0x4b, 0x61, 0x96, 0xd0, 0x7a, 0xbe, + 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, + 0x2d, 0x1b, 0xff, 0x6e, 0x91, 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, + 0xe9, 0xae, 0x82, 0xae, 0x43, 0xd3 }; + uint8_t aSecondResponse[] = { 0x48, 0x83, 0x64, 0x0e, 0xff, 0xc1, 0xc0, 0xbf }; + uint8_t aThirdResponse[] = { 0x88, 0xc1, 0x61, 0x96, 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, 0x95, + 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x84, 0xa6, 0x2d, 0x1b, 0xff, 0xc0, 0x5a, 0x83, 0x9b, 0xd9, 0xab, + 0x77, 0xad, 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, 0xcd, 0x5b, + 0x39, 0x60, 0xd5, 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, + 0x95, 0x87, 0x31, 0x60, 0x65, 0xc0, 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07 }; + + if ((pRef = HpackCreate(4096, FALSE)) == NULL) + { + return; + } + + HpackEncode(pRef, strFirstResponse, aOutput, sizeof(aOutput), TRUE); + if (memcmp(aOutput, aFirstResponse, sizeof(aFirstResponse)) == 0) + { + ZPrintf("successfully encoded first response (huffman)\n"); + } + + HpackEncode(pRef, strSecondResponse, aOutput, sizeof(aOutput), TRUE); + if (memcmp(aOutput, aSecondResponse, sizeof(aSecondResponse)) == 0) + { + ZPrintf("successfully encoded second response (huffman)\n"); + } + HpackEncode(pRef, strThirdResponse, aOutput, sizeof(aOutput), TRUE); + if (memcmp(aOutput, aThirdResponse, sizeof(aThirdResponse)) == 0) + { + ZPrintf("successfully encoded third response (huffman)\n"); + } + + HpackDestroy(pRef); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdHpack + + \Description + Test the HPACK encoder and decoder. + + \Input *pArgz - environment + \Input iArgc - standard number of arguments + \Input *pArgv[] - standard arg list + + \Output standard return value + + \Version 10/07/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CmdHpack(ZContext *pArgz, int32_t iArgc, char *pArgcv[]) +{ + _CmdHpackDecodeRequest(); + _CmdHpackDecodeResponse(); + _CmdHpackDecodeRequestHuffman(); + _CmdHpackDecodeResponseHuffman(); + _CmdHpackEncodeRequest(); + _CmdHpackEncodeResponse(); + _CmdHpackEncodeRequestHuffman(); + _CmdHpackEncodeResponseHuffman(); + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/http.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/http.c new file mode 100644 index 00000000..d434ae55 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/http.c @@ -0,0 +1,1310 @@ +/*H********************************************************************************/ +/*! + \File http.c + + \Description + Implements basic http get and post client. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 10/28/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/proto/protossl.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +#define HTTP_BUFSIZE (4096) +#define HTTP_RATE (1) + +#define HTTP_XTRAHDR0 "" +#define HTTP_XTRAHDR1 "X-Agent: DirtySock HTTP Tester\r\n" // test "normal" replacement (replaces Accept: header) +#define HTTP_XTRAHDR2 "User-Agent: DirtySock HTTP Tester\r\n" // test "extended" replacement (replaces User-Agent: and Accept: headers) + +//$$ tmp -- special test header used for hard-coded multipart/form-data testing -- this should be removed at some point when we have real multipart/form-data support +#define HTTP_XTRAHDR3 "Content-Type: multipart/form-data; boundary=TeStInG\r\n" \ + "User-Agent: DirtySock HTTP Tester\r\n" \ + "Accept: */*\r\n" + +#define HTTP_APNDHDR HTTP_XTRAHDR0 + +/*** Function Prototypes **********************************************************/ + +static int32_t _CmdHttpIdleCB(ZContext *argz, int32_t argc, char *argv[]); + +/*** Type Definitions *************************************************************/ + +typedef struct HttpRefT // module state storage +{ + ProtoHttpRefT *http; + int64_t show; + int64_t count; + int32_t sttime; + int64_t iDataSize; + int64_t iSentSize; + int32_t iSendBufData; + int32_t iSendBufSent; + ZFileT iInpFile; + ZFileT iOutFile; + int32_t iOutSize; + char *pOutData; + char *pClientCert; + int32_t iCertSize; + char *pClientKey; + int32_t iKeySize; + int32_t iDebugLevel; + uint8_t bStreaming; + uint8_t bUseWriteCb; + uint8_t bRecvAll; + char strModuleName[32]; + char strHost[128]; + char strCookie[2048]; + char strApndHdr[2048]; + char strFileBuffer[64*1024]; // must be at a minimum 4k or protohttp buffer size, whichever is larger + + // module state + enum + { + IDLE, DNLOAD, UPLOAD + } state; +} HttpRefT; + +/*** Variables ********************************************************************/ + +static HttpRefT _Http_Ref; +static uint8_t _Http_bInitialized = FALSE; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _GetIntArg + + \Description + Get fourcc/integer from command-line argument + + \Input *pArg - pointer to argument + + \Version 10/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _GetIntArg(const char *pArg) +{ + int32_t iValue; + + // check for possible fourcc value + if ((strlen(pArg) == 4) && (isalpha(pArg[0]) || isalpha(pArg[1]) || isalpha(pArg[2]) || isalpha(pArg[3]))) + { + iValue = pArg[0] << 24; + iValue |= pArg[1] << 16; + iValue |= pArg[2] << 8; + iValue |= pArg[3]; + } + else + { + iValue = (signed)strtol(pArg, NULL, 10); + } + return(iValue); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttpUsage + + \Description + Display usage information. + + \Input argc - argument count + \Input *argv[] - argument list + + \Version 02/18/2008 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CmdHttpUsage(int argc, char *argv[]) +{ + if (argc == 2) + { + ZPrintf(" execute http get or put operations\n"); + ZPrintf(" usage: %s [cclr|cert|cer2|cver|create|ctrl|debug|destroy|get|head|put|post|puts|delete|options|parse|stat|urlparse]", argv[0]); + } + else if (argc == 3) + { + if (!strcmp(argv[2], "cclr") || !strcmp(argv[2], "cert") || !strcmp(argv[2], "cer2") || !strcmp(argv[2], "cver")) + { + ZPrintf(" usage: %s cert|cer2 - load certificate file\n", argv[0]); + ZPrintf(" %s cclr - clear dynamic CA certs\n"); + ZPrintf(" %s cver - verify dynamic CA certs\n"); + } + else if (!strcmp(argv[2], "create")) + { + ZPrintf(" usage: %s create \n", argv[0]); + } + else if (!strcmp(argv[2], "destroy")) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + } + else if (!strcmp(argv[2], "get") || !strcmp(argv[2], "head") || !strcmp(argv[2], "options") || !strcmp(argv[2], "delete")) + { + ZPrintf(" usage: %s %s [url] \n", argv[0], argv[2]); + } + else if (!strcmp(argv[2], "put") || !strcmp(argv[2], "puts") || !strcmp(argv[2], "post")) + { + ZPrintf(" usage: %s %s [url] [infile] \n", argv[0], argv[2]); + } + else if (!strcmp(argv[2], "debug")) + { + ZPrintf(" usage: %s debug [debuglevel]\n", argv[0]); + } + else if (!strcmp(argv[2], "parse")) + { + ZPrintf(" usage: %s parse [url]\n", argv[0]); + } + } +} + +static int32_t _HttpUrlParseTest(const char *pModuleName, const char *pUrl) +{ + char strKind[16], strHost[256]; + int32_t iPort, iSecure; + uint8_t bPortSpecified; + const char *pUri; + + ZPrintf("%s: parsing url %s\n", pModuleName, pUrl); + pUri = ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure, &bPortSpecified); + + ZPrintf("%s: uri=%s\n", pModuleName, pUri); + ZPrintf("%s: kind=%s\n", pModuleName, strKind); + ZPrintf("%s: host=%s\n", pModuleName, strHost); + ZPrintf("%s: iPort=%d\n", pModuleName, iPort); + ZPrintf("%s: iSecure=%d\n", pModuleName, iSecure); + ZPrintf("%s: bPortSpecified=%s\n", pModuleName, bPortSpecified ? "TRUE" : "FALSE"); + + return(0); +} + +static int32_t _HttpUrlEncodeTest(const char *pModuleName, char **pArgs, int32_t iNumArgs) +{ + char strUrl[128]; + int32_t iArg, iOff; + + ds_snzprintf(strUrl, sizeof(strUrl), "https://urltest.ea.com/"); + for (iArg = 0, iOff = (int32_t)strlen(strUrl); iArg < iNumArgs; iArg += 2) + { + iOff += ProtoHttpUrlEncodeStrParm(strUrl+iOff, sizeof(strUrl)-iOff, pArgs[iArg], pArgs[iArg+1]); + } + ProtoHttpUrlEncodeIntParm(strUrl, sizeof(strUrl), "&test=", 2918123); + ZPrintf("%s: url=%s\n", pModuleName, strUrl); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpLoadCertificate + + \Description + Display usage information. + + \Input *pFilename - certificate filename + \Input *pCertSize - [out] storage for cert size + + \Output + char * - pointer to certificate, or NULL on error + + \Version 10/20/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_HttpLoadCertificate(const char *pFilename, int32_t *pCertSize) +{ + char *pCertBuf; + int32_t iFileSize; + + // load certificate file + if ((pCertBuf = (char *)ZFileLoad(pFilename, &iFileSize, ZFILE_OPENFLAG_RDONLY)) == NULL) + { + return(NULL); + } + + // calculate length + *pCertSize = iFileSize; + // return to caller + return(pCertBuf); +} + +/*F********************************************************************************/ +/*! + \Function _HttpReset + + \Description + Reset transaction state + + \Input *pRef - module state + + \Version 03/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpReset(HttpRefT *pRef) +{ + // clear any previous stats + pRef->count = 0; + pRef->show = 0; + pRef->iDataSize = 0; + pRef->iSentSize = 0; + pRef->iSendBufSent = 0; + + // clear previous options + pRef->bStreaming = FALSE; + pRef->bUseWriteCb = FALSE; + pRef->bRecvAll = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _HttpCustomHeaderCallback + + \Description + ProtoHttp custom header callback. + + \Input *pProtoHttp - protohttp module state + \Input *pHeader - header to be sent + \Input uHeaderSize - header size + \Input *pUserData - user ref (HttpRefT) + + \Output + int32_t - zero + + \Notes + The header returned should be terminated by a *single* CRLF; ProtoHttp will + append the final CRLF to complete the header. The callback may return the + size of the header, or zero, in which case ProtoHttp will calculate the + headersize using strlen(). + + \Version 02/24/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpCustomHeaderCallback(ProtoHttpRefT *pProtoHttp, char *pHeader, uint32_t uHeaderSize, const char *pData, int64_t iDataLen, void *pUserRef) +{ + int32_t iOffset; + + NetPrintf(("http: custom header callback, size=%d, data=%d, pData=%p\n", uHeaderSize, iDataLen, pData)); + + // find end of header + iOffset = (int32_t)strlen(pHeader); + + // append a header + iOffset += ds_snzprintf(pHeader+iOffset, uHeaderSize-iOffset, "X-CustomHeaderCallback: Testing custom header callback\r\n"); + + // we can either return the header length or zero; in the latter case protohttp will re-calculate for us + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function _HttpRecvHeaderCallback + + \Description + ProtoHttp recv header callback. + + \Input *pProtoHttp - protohttp module state + \Input *pHeader - received header + \Input uHeaderSize - header size + \Input *pUserData - user ref (HttpRefT) + + \Output + int32_t - zero + + \Version 02/24/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpRecvHeaderCallback(ProtoHttpRefT *pProtoHttp, const char *pHeader, uint32_t uHeaderSize, void *pUserRef) +{ + HttpRefT *pHttp = (HttpRefT *)pUserRef; + char strBuffer[2048], strName[128]; + const char *pHdrTmp; + int32_t iLocnSize; + + // check for location header + if ((iLocnSize = ProtoHttpGetHeaderValue(pProtoHttp, pHeader, "location", NULL, 0, NULL)) > 0) + { + ZPrintf("http: location header size=%d\n", iLocnSize); + if (ProtoHttpGetHeaderValue(pProtoHttp, pHeader, "location", strBuffer, sizeof(strBuffer), NULL) == 0) + { + ZPrintf("http: location url='%s'\n", strBuffer); + } + else + { + ZPrintf("http: error querying location url\n"); + } + } + + // test ProtoHttpGetNextHeader() + for (pHdrTmp = pHeader; ProtoHttpGetNextHeader(pProtoHttp, pHdrTmp, strName, sizeof(strName), strBuffer, sizeof(strBuffer), &pHdrTmp) == 0; ) + { + #if 0 + ZPrintf("http: ===%s: %s\n", strName, strBuffer); + #endif + } + + // parse any set-cookie requests + for (pHdrTmp = pHeader; ProtoHttpGetHeaderValue(pProtoHttp, pHdrTmp, "set-cookie", strBuffer, sizeof(strBuffer), &pHdrTmp) == 0; ) + { + // print the cookie + ZPrintf("http: parsed cookie '%s'\n", strBuffer); + + // add field seperator + if (pHttp->strCookie[0] != '\0') + { + ds_strnzcat(pHttp->strCookie, ", ", sizeof(pHttp->strCookie)); + } + // append to cookie list + ds_strnzcat(pHttp->strCookie, strBuffer, sizeof(pHttp->strCookie)); + } + + // if this is a redirection update append header with any new cookies + if ((PROTOHTTP_GetResponseClass(ProtoHttpStatus(pProtoHttp, 'code', NULL, 0)) == PROTOHTTP_RESPONSE_REDIRECTION) && (pHttp->strCookie[0] != '\0')) + { + // format append header + ds_snzprintf(strBuffer, sizeof(strBuffer), "Cookie: %s\r\n%s", pHttp->strCookie, HTTP_APNDHDR); + ds_strnzcat(strBuffer, pHttp->strApndHdr, sizeof(strBuffer)); + ProtoHttpControl(pProtoHttp, 'apnd', 0, 0, strBuffer); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpReallocBuff + + \Description + Realloc buffer used for RecvAll operation + + \Input *pRef - pointer to http ref + + \Version 07/29/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpReallocBuff(HttpRefT *pRef) +{ + char *pNewData; + int32_t iNewSize; + + // calc new buffer size + if ((iNewSize = pRef->iOutSize) == 0) + { + // try getting body size + if ((iNewSize = ProtoHttpStatus(pRef->http, 'body', NULL, 0)) > 0) + { + // bump up buffer size for recvall null terminator + //$$ TODO V9 -- why 2 required, not 1?? + iNewSize += 2; + } + else + { + // assign a fixed size, since we didn't get a body size + iNewSize = 4096; + } + } + else + { + iNewSize *= 2; + } + + // allocate new buffer + if ((pNewData = ZMemAlloc(iNewSize)) == NULL) + { + return; + } + // if realloc, copy old data and free old pointer + if (pRef->pOutData != NULL) + { + ds_memcpy(pNewData, pRef->pOutData, pRef->iOutSize); + ZMemFree(pRef->pOutData); + } + // save new pointer + pRef->pOutData = pNewData; + pRef->iOutSize = iNewSize; +} + +/*F********************************************************************************/ +/*! + \Function _HttpDownloadProcessData + + \Description + Process data received in a download operation + + \Input *pRef - module state + \Input iLen - data length or PROTOHTTP_RECV* + + \Version 05/03/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpDownloadProcessData(HttpRefT *pRef, int32_t iLen) +{ + int32_t iResult; + + // see if we should show progress + if ((pRef->count/1024) != (pRef->show/1024)) + { + pRef->show = pRef->count; + ZPrintf("%s: downloaded %qd bytes\n", pRef->strModuleName, pRef->count); + } + + // see if we are done + if ((iLen < 0) && (iLen != PROTOHTTP_RECVWAIT)) + { + // completed successfully? + if ((iLen == PROTOHTTP_RECVDONE) || (iLen == PROTOHTTP_RECVHEAD)) + { + int32_t iDlTime = NetTickDiff(NetTick(), pRef->sttime); + int64_t iBodySize; + char strRespHdr[1024]; + + ZPrintf("%s: download complete (%qd bytes)\n", pRef->strModuleName, pRef->count); + ZPrintf("%s: download time: %qd bytes in %.2f seconds (%.3f k/sec)\n", pRef->strModuleName, pRef->count, + (float)iDlTime/1000.0f, ((float)pRef->count * 1000.0f) / ((float)iDlTime * 1024.0f)); + + // make sure we got it all + if ((iBodySize = ProtoHttpStatus(pRef->http, 'body', NULL, 0)) != pRef->count) + { + ZPrintf("%s: WARNING -- mismatch between expected size (%d) and actual size (%d)\n", pRef->strModuleName, iBodySize, pRef->count); + } + + // display response header + if (ProtoHttpStatus(pRef->http, 'htxt', strRespHdr, sizeof(strRespHdr)) >= 0) + { + ZPrintf("%s response header:\n%s\n", pRef->strModuleName, strRespHdr); + } + + // display a couple of parsed fields + if (ProtoHttpStatus(pRef->http, 'head', NULL, 0) > 0) + { + time_t tLastMod = ProtoHttpStatus(pRef->http, 'date', NULL, 0); + const char *pTime; + + if (tLastMod != 0) + { + if ((pTime = ctime(&tLastMod)) != NULL) + { + ZPrintf("%s: Last-Modified: %s", pRef->strModuleName, pTime); + } + } + // get content-length; by passing in a 64bit int we support 64bit transfer sizes + ProtoHttpStatus(pRef->http, 'body', &iBodySize, sizeof(iBodySize)); + ZPrintf("%s: Content-Length: %qd\n", pRef->strModuleName, iBodySize); + } + } + else // failure + { + ProtoSSLAlertDescT AlertDesc; + int32_t iSockErr = ProtoHttpStatus(pRef->http, 'serr', NULL, 0); + int32_t iSslFail = ProtoHttpStatus(pRef->http, 'essl', NULL, 0); + int32_t iAlert = ProtoHttpStatus(pRef->http, 'alrt', &AlertDesc, sizeof(AlertDesc)); + ZPrintf("%s: download failed (err=%d, sockerr=%d sslerr=%d)\n", pRef->strModuleName, iLen, iSockErr, iSslFail); + if ((iSslFail == PROTOSSL_ERROR_CERT_INVALID) || (iSslFail == PROTOSSL_ERROR_CERT_HOST) || + (iSslFail == PROTOSSL_ERROR_CERT_NOTRUST) || (iSslFail == PROTOSSL_ERROR_CERT_REQUEST)) + { + ProtoSSLCertInfoT CertInfo; + if (ProtoHttpStatus(pRef->http, 'cert', &CertInfo, sizeof(CertInfo)) == 0) + { + ZPrintf("%s: cert failure (%d): (C=%s, ST=%s, L=%s, O=%s, OU=%s, CN=%s)\n", pRef->strModuleName, iSslFail, + CertInfo.Ident.strCountry, CertInfo.Ident.strState, CertInfo.Ident.strCity, + CertInfo.Ident.strOrg, CertInfo.Ident.strUnit, CertInfo.Ident.strCommon); + } + else + { + ZPrintf("%s: could not get cert info\n", pRef->strModuleName); + } + } + if (iAlert > 0) + { + ZPrintf("%s: %s ssl alert %s (%d)\n", pRef->strModuleName, (iAlert == 1) ? "recv" : "sent", AlertDesc.pAlertDesc, AlertDesc.iAlertType); + } + } + + ZPrintf("%s: hResult=0x%08x\n", pRef->strModuleName, ProtoHttpStatus(pRef->http, 'hres', NULL, 0)); + + // if file exists, close it + if (pRef->iOutFile != ZFILE_INVALID) + { + if ((iResult = ZFileClose(pRef->iOutFile)) != 0) + { + ZPrintf("%s: error %d closing output file\n", pRef->strModuleName, iResult); + } + pRef->iOutFile = ZFILE_INVALID; + } + + // if output buffer exists, free it + if (pRef->pOutData != NULL) + { + pRef->pOutData = NULL; + } + pRef->iOutSize = 0; + + // set to idle state + pRef->state = IDLE; + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpWriteCb + + \Description + Implementation of ProtoHttp write callback + + \Input *pState - http module state + \Input *pWriteInfo - callback info + \Input *pData - transaction data pointer + \Input iDataSize - size of data + \Input *pUserData - user callback data + + \Output + int32_t - zero + + \Version 05/03/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpWriteCb(ProtoHttpRefT *pState, const ProtoHttpWriteCbInfoT *pWriteInfo, const char *pData, int32_t iDataSize, void *pUserData) +{ + HttpRefT *pRef = (HttpRefT *)pUserData; + //NetPrintf(("http: writecb (%d,%d) %d bytes\n", pWriteInfo->eRequestType, pWriteInfo->eRequestResponse, iDataSize)); + + // if we got data, update count and write to output file if available + if (iDataSize > 0) + { + pRef->count += iDataSize; + if (pRef->iOutFile != ZFILE_INVALID) + { + ZFileWrite(pRef->iOutFile, (void *)pData, iDataSize); + } + } + + // process + _HttpDownloadProcessData(pRef, iDataSize); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpRecvData + + \Description + Receive data from ProtoHttp using either ProtoHttpRecv() or + ProtoHttpRecvAll(). + + \Input *pRef - module state + \Input *pState - transaction state + + \Version 07/02/2013 (jbrookes) brought over from httpmgr +*/ +/********************************************************************************F*/ +static int32_t _HttpRecvData(HttpRefT *pRef) +{ + char strBuf[16*1024]; + int32_t iLen; + + // check for data + if (!pRef->bRecvAll) + { + while ((iLen = ProtoHttpRecv(pRef->http, strBuf, 1, sizeof(strBuf))) > 0) + { + pRef->count += iLen; + if (pRef->iOutFile != ZFILE_INVALID) + { + ZFileWrite(pRef->iOutFile, strBuf, iLen); + } + } + } + else + { + // receive all the data + if ((iLen = ProtoHttpRecvAll(pRef->http, pRef->pOutData, pRef->iOutSize)) > 0) + { + pRef->count = iLen; + if (pRef->iOutFile != ZFILE_INVALID) + { + ZFileWrite(pRef->iOutFile, pRef->pOutData, iLen); + } + iLen = PROTOHTTP_RECVDONE; + } + else if (iLen == PROTOHTTP_RECVBUFF) + { + // grow the buffer + _HttpReallocBuff(pRef); + // swallow error code + iLen = 0; + } + } + return(iLen); +} + +/*F********************************************************************************/ +/*! + \Function _HttpDownloadProcess + + \Description + Process a download transaction, using polling method + + \Input *pRef - module state + + \Version 10/28/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpDownloadProcess(HttpRefT *pRef) +{ + int32_t iLen; + + // if we're not doing the write callback thing, poll for data + for (iLen = 1; (iLen != PROTOHTTP_RECVWAIT) && (iLen != 0) && (pRef->state != IDLE); ) + { + // receive data + iLen = _HttpRecvData(pRef); + // process data + _HttpDownloadProcessData(pRef, iLen); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpUploadProcess + + \Description + Process an upload transaction. + + \Input *pRef - module state + + \Version 10/28/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpUploadProcess(HttpRefT *pRef) +{ + int32_t iResult; + + // if no input file, nothing to send + if (pRef->iInpFile == ZFILE_INVALID) + { + return; + } + + // send all the data + while ((pRef->state == UPLOAD) && (pRef->iSentSize < pRef->iDataSize)) + { + // do we need more data? + if (pRef->iSendBufSent == pRef->iSendBufData) + { + if ((pRef->iSendBufData = ZFileRead(pRef->iInpFile, pRef->strFileBuffer, sizeof(pRef->strFileBuffer))) > 0) + { + ZPrintf("%s: read %d bytes from file\n", pRef->strModuleName, pRef->iSendBufData); + pRef->iSendBufSent = 0; + } + else + { + ZPrintf("%s: error %d reading from file\n", pRef->strModuleName, pRef->iSendBufData); + pRef->state = IDLE; + } + } + + // do we have buffered data to send? + if (pRef->iSendBufSent < pRef->iSendBufData) + { + iResult = ProtoHttpSend(pRef->http, pRef->strFileBuffer + pRef->iSendBufSent, pRef->iSendBufData - pRef->iSendBufSent); + if (iResult > 0) + { + pRef->iSentSize += iResult; + pRef->iSendBufSent += iResult; + ZPrintf("%s: sent %d bytes (%qd total)\n", pRef->strModuleName, iResult, pRef->iSentSize); + } + else if (iResult < 0) + { + ZPrintf("%s: ProtoHttpSend() failed; error %d\n", pRef->strModuleName, iResult); + pRef->state = IDLE; + } + else + { + break; + } + } + } + // check for upload completion + if (pRef->iSentSize == pRef->iDataSize) + { + int32_t ultime = NetTick() - pRef->sttime; + + // if streaming upload, signal we are done + if (pRef->bStreaming == TRUE) + { + ProtoHttpSend(pRef->http, NULL, PROTOHTTP_STREAM_END); + pRef->bStreaming = FALSE; + } + + // done uploading + ZPrintf("%s: upload complete (%qd bytes)\n", pRef->strModuleName, pRef->iSentSize); + ZPrintf("%s: upload time: %d bytes in %.2f seconds (%.3f k/sec)\n", pRef->strModuleName, pRef->iSentSize, + (float)ultime/1000.0f, ((float)pRef->iSentSize * 1000.0f) / ((float)ultime * 1024.0f)); + + // close the file + ZFileClose(pRef->iInpFile); + pRef->iInpFile = ZFILE_INVALID; + + // transition to download state to receive server response + pRef->state = DNLOAD; + } +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttpIdleCB + + \Description + Callback to process while idle + + \Input *argz - + \Input argc - + \Input *argv[] - + + \Output int32_t - + + \Version 09/26/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdHttpIdleCB(ZContext *argz, int32_t argc, char *argv[]) +{ + HttpRefT *pRef = &_Http_Ref; + + // shut down? + if ((argc == 0) || (pRef->http == NULL)) + { + if (pRef->http != NULL) + { + ProtoHttpDestroy(pRef->http); + pRef->http = NULL; + } + return(0); + } + + // give ref processing time + ProtoHttpUpdate(pRef->http); + + // download processing (if not using write callback) + if ((pRef->state == DNLOAD) && (!pRef->bUseWriteCb)) + { + _HttpDownloadProcess(pRef); + } + // upload processing + if (pRef->state == UPLOAD) + { + _HttpUploadProcess(pRef); + } + + // keep on idling + return(ZCallback(&_CmdHttpIdleCB, HTTP_RATE)); +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdHttp + + \Description + Initiate an HTTP transaction. + + \Input *argz - + \Input argc - + \Input *argv[] - + + \Output int32_t - + + \Version 10/28/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdHttp(ZContext *argz, int32_t argc, char *argv[]) +{ + const char *pFileName = NULL, *pUrl; + HttpRefT *pRef = &_Http_Ref; + int32_t iResult = 0, iBufSize = HTTP_BUFSIZE; + char strBuffer[4096]; + int32_t iStartArg = 2; // first arg past get/put/delete/whatever + + if (argc < 2) + { + return(0); + } + + // check for help + if ((argc >= 2) && !strcmp(argv[1], "help")) + { + _CmdHttpUsage(argc, argv); + return(iResult); + } + + // check for 'parse' command + if ((argc == 3) && !strcmp(argv[1], "parse")) + { + char strKind[8], strHost[128]; + const char *pUri; + int32_t iPort, iSecure; + ds_memclr(strKind, sizeof(strKind)); + ds_memclr(strHost, sizeof(strHost)); + pUri = ProtoHttpUrlParse(argv[2], strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure); + ZPrintf("parsed url: kind=%s, host=%s, port=%d, secure=%d, uri=%s\n", strKind, strHost, iPort, iSecure, pUri); + return(0); + } + + // if not initialized yet, do so now + if (_Http_bInitialized == FALSE) + { + ds_memclr(pRef, sizeof(*pRef)); + ds_strnzcpy(pRef->strModuleName, argv[0], sizeof(pRef->strModuleName)); + pRef->iInpFile = ZFILE_INVALID; + pRef->iOutFile = ZFILE_INVALID; + _Http_bInitialized = TRUE; + } + + // check for explicit destroy + if ((argc == 2) && !ds_stricmp(argv[1], "destroy")) + { + if (pRef->http != NULL) + { + ProtoHttpDestroy(pRef->http); + pRef->http = NULL; + } + if (pRef->pClientCert != NULL) + { + ZMemFree(pRef->pClientCert); + } + if (pRef->pClientKey != NULL) + { + ZMemFree(pRef->pClientKey); + } + _Http_bInitialized = FALSE; + return(iResult); + } + + // check for create request + if ((argc == 3) && !strcmp(argv[1], "create")) + { + iBufSize = (int32_t)strtol(argv[2], NULL, 10); + } + + // check for request to set a CA certificate + if ((argc == 3) && ((!strcmp(argv[1], "cert")) || (!strcmp(argv[1], "cer2")))) + { + const uint8_t *pFileData; + int32_t iFileSize; + + // try and open file + if ((pFileData = (const uint8_t *)ZFileLoad(argv[2], &iFileSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != NULL) + { + if (!strcmp(argv[1], "cert")) + { + iResult = ProtoHttpSetCACert(pFileData, iFileSize); + } + else + { + iResult = ProtoHttpSetCACert2(pFileData, iFileSize); + } + ZMemFree((void *)pFileData); + } + else + { + ZPrintf("%s: unable to load certificate file '%s'\n", argv[0], argv[2]); + } + return((iResult >= 0) ? 0 : -1); + } + else if (!strcmp(argv[1], "cclr")) + { + ZPrintf("%s: clearing dynamic CA certs\n", argv[0]); + ProtoHttpClrCACerts(); + return(0); + } + else if (!strcmp(argv[1], "cver")) + { + int32_t iInvalid; + ZPrintf("%s: verifying dynamic CA certs\n", argv[0]); + if ((iInvalid = ProtoHttpValidateAllCA()) > 0) + { + ZPrintf("%s: could not verify %d CA certs\n", argv[0], iInvalid); + iResult = -1; + } + return(iResult); + } + else if ((argc == 3) && !strcmp(argv[1], "scrt")) + { + if ((pRef->pClientCert = (char *)_HttpLoadCertificate(argv[2], &pRef->iCertSize)) == NULL) + { + ZPrintf("%s: could not load client certificate '%s'\n", argv[0], argv[2]); + iResult = -1; + } + return(iResult); + } + else if ((argc == 3) && !strcmp(argv[1], "skey")) + { + if ((pRef->pClientKey = (char *)_HttpLoadCertificate(argv[2], &pRef->iKeySize)) == NULL) + { + ZPrintf("%s: could not load client private key '%s'\n", argv[0], argv[2]); + iResult = -1; + } + return(iResult); + } + + // test url parsing + if ((argc == 3) && !strcmp(argv[1], "urlparse")) + { + return(_HttpUrlParseTest(argv[0], argv[2])); + } + + // test url encoding + if ((argc >= 3) && !strcmp(argv[1], "urlencode")) + { + return(_HttpUrlEncodeTest(argv[0], argv+2, argc-2)); + } + + // create protohttp module if necessary + if (pRef->http == NULL) + { + // disable the secure flags to help with our testing setup + #if DIRTYCODE_GDK + ProtoHttpControl(pRef->http, 'secu', 0, 0, NULL); + #endif + + ZPrintf("%s: creating module with a %dkbyte buffer\n", argv[0], iBufSize); + pRef->http = ProtoHttpCreate(iBufSize); + if (pRef->http != NULL) + { + ProtoHttpCallback(pRef->http, _HttpCustomHeaderCallback, _HttpRecvHeaderCallback, pRef); + pRef->iDebugLevel = 1; + } + } + + // check for create request -- if so, we're done + if ((argc <= 3) && !strcmp(argv[1], "create")) + { + return(iResult); + } + else if ((argc > 2) && (argc < 7) && !strcmp(argv[1], "ctrl")) + { + int32_t iCmd, iValue = 0, iValue2 = 0; + const char *pValue = NULL; + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + if (argc > 3) + { + iValue = _GetIntArg(argv[3]); + } + + if (argc > 4) + { + iValue2 = _GetIntArg(argv[4]); + } + + if (argc > 5) + { + pValue = argv[5]; + } + + // snoop 'spam' + if (iCmd == 'spam') + { + pRef->iDebugLevel = iValue; + } + + return(ProtoHttpControl(pRef->http, iCmd, iValue, iValue2, (void *)pValue)); + } + else if ((argc == 3) && !strcmp(argv[1], "stat")) + { + int32_t iCmd; + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + iResult = ProtoHttpStatus(pRef->http, iCmd, strBuffer, sizeof(strBuffer)); + ZPrintf("http: ProtoHttpStatus('%s') returned %d\n", argv[2], iResult); + if (strBuffer[0] != '\0') + { + ZPrintf("%s\n", strBuffer); + } + return(0); + } + // check for setting of base url + else if ((argc == 3) && !strcmp(argv[1], "base")) + { + ProtoHttpSetBaseUrl(pRef->http, argv[2]); + return(iResult); + } + // check for debug setting + else if (!strcmp(argv[1], "debug") && (argc == 3)) + { + int32_t iDebugLevel = (int32_t)strtol(argv[2], NULL, 10); + pRef->iDebugLevel = iDebugLevel; + return(iResult); + } + else if (!ds_stricmp(argv[1], "abrt")) + { + ProtoHttpAbort(pRef->http); + pRef->state = IDLE; + return(iResult); + } + + // check for valid get/put request + else if ((!ds_stricmp(argv[1], "get") || !ds_stricmp(argv[1], "head") || !ds_stricmp(argv[1], "put") || !ds_stricmp(argv[1], "post") || + !ds_stricmp(argv[1], "puts") || !ds_stricmp(argv[1], "delete") || !ds_stricmp(argv[1], "options")) && + ((argc > 2) || (argc < 6))) + { + int32_t iArg; + + // reset to clear any previous stats + _HttpReset(pRef); + + // init start timer + pRef->sttime = NetTick(); + + // set up append header + ds_strnzcpy(pRef->strApndHdr, HTTP_APNDHDR, sizeof(pRef->strApndHdr)); + + // check for args + for (iArg = 2; (iArg < argc) && (argv[iArg][0] == '-'); iArg += 1) + { + if (!ds_strnicmp(argv[iArg], "-header=", 8)) + { + ds_strnzcat(pRef->strApndHdr, argv[iArg]+8, sizeof(pRef->strApndHdr)); + ds_strnzcat(pRef->strApndHdr, "\r\n", sizeof(pRef->strApndHdr)); + } + if (!ds_strnicmp(argv[iArg], "-writecb", 8)) + { + pRef->bUseWriteCb = TRUE; + } + if (!ds_strnicmp(argv[iArg], "-recvall", 8)) + { + pRef->bRecvAll = TRUE; + } + // skip any option arguments to find url and (optionally) filename + iStartArg += 1; + } + + // set client cert if specified + if (pRef->pClientCert != NULL) + { + ProtoHttpControl(pRef->http, 'scrt', pRef->iCertSize, 0, pRef->pClientCert); + } + // set client key if specified + if (pRef->pClientKey != NULL) + { + ProtoHttpControl(pRef->http, 'skey', pRef->iKeySize, 0, pRef->pClientKey); + } + + // fall-through to code below + } + else + { + ZPrintf(" unrecognized or badly formatted command '%s'\n", argv[1]); + _CmdHttpUsage(argc, argv); + return(iResult); + } + + // locate url and filename + pUrl = argv[iStartArg]; + if (argc > iStartArg) + { + pFileName = argv[iStartArg+1]; + } + + // do we have a url? + if (pUrl != NULL) + { + const char* pTemp = pUrl; //< Used for parsing the query param + char strKind[5], strHost[128], strName[32], strValue[32]; + int32_t iPort, iSecure; + + // get url info + ProtoHttpUrlParse(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure); + + // if the host has changed, reset cookie buffer + if (ds_stricmp(pRef->strHost, strHost) && (pRef->strCookie[0] != '\0')) + { + ZPrintf("http: host change to %s; resetting cookies from %s\n", strHost, pRef->strHost); + pRef->strCookie[0] = '\0'; + } + // save host + ds_strnzcpy(pRef->strHost, strHost, sizeof(strHost)); + + while (ProtoHttpGetNextParam(pRef->http, pTemp, strName, sizeof(strName), strValue, sizeof(strValue), &pTemp) == 0) + { + ProtoHttpUrlDecodeStrParm(strValue, strValue, sizeof(strValue)); + + #if 0 + ZPrintf("http query param: key=%s, value=%s\n", strName, strValue); + #endif + } + } + else + { + return(0); + } + + // set append header + strBuffer[0] = '\0'; + if (pRef->strCookie[0] != '\0') + { + ds_snzprintf(strBuffer, sizeof(strBuffer), "Cookie: %s\r\n", pRef->strCookie); + } + ds_strnzcat(strBuffer, pRef->strApndHdr, sizeof(strBuffer)); + ProtoHttpControl(pRef->http, 'apnd', 0, 0, strBuffer); + // set debug level + ProtoHttpControl(pRef->http, 'spam', pRef->iDebugLevel, 0, NULL); + + // if we're uploading, open required input file + if (!ds_stricmp(argv[1], "put") || !ds_stricmp(argv[1], "post") || !ds_stricmp(argv[1], "puts")) + { + ZPrintf("%s: uploading %s to %s\n", argv[0], pFileName, pUrl); + + // assume failure + iResult = -1; + + // try and open file + if ((pRef->iInpFile = ZFileOpen(pFileName, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != ZFILE_INVALID) + { + // get the file size + if ((pRef->iDataSize = ZFileSize(pRef->iInpFile)) > 0) + { + // load data from file + if ((pRef->iSendBufData = ZFileRead(pRef->iInpFile, pRef->strFileBuffer, sizeof(pRef->strFileBuffer))) > 0) + { + if (ds_stricmp(argv[1], "puts")) + { + // initiate put/post transaction + ZPrintf("%s: uploading %d bytes\n", argv[0], pRef->iDataSize); + if ((pRef->iSendBufSent = ProtoHttpPost(pRef->http, pUrl, pRef->strFileBuffer, pRef->iDataSize, !ds_stricmp(argv[1], "put") ? PROTOHTTP_PUT : PROTOHTTP_POST)) < 0) + { + ZPrintf("%s: error %d initiating send\n", argv[0], pRef->iSendBufSent); + iResult = -1; + } + else if (pRef->iSendBufSent >= 0) + { + ZPrintf("%s: sent %d bytes\n", argv[0], pRef->iSendBufSent); + pRef->iSentSize = pRef->iSendBufSent; + } + } + else + { + // initiate streaming put + if ((iResult = ProtoHttpPost(pRef->http, pUrl, NULL, PROTOHTTP_STREAM_BEGIN, PROTOHTTP_POST)) >= 0) + { + pRef->bStreaming = TRUE; + } + } + + // wait for reply + pRef->state = UPLOAD; + + // locate output file + pFileName = (argc > (iStartArg+2)) ? argv[iStartArg+2] : NULL; + } + else + { + ZPrintf("%s: error %d reading data from file\n", argv[0], pRef->iSendBufData, pFileName); + } + } + else + { + ZPrintf("%s: error %d getting size of file %s\n", argv[0], pRef->iDataSize, pFileName); + } + } + else + { + ZPrintf("%s: unable to load file '%s'\n", argv[0], pFileName); + } + } + + // open output file? + if (pFileName != NULL) + { + ZPrintf("%s: saving %s data to %s\n", argv[0], pUrl, pFileName); + if ((pRef->iOutFile = ZFileOpen(pFileName, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_BINARY|ZFILE_OPENFLAG_CREATE)) == ZFILE_INVALID) + { + ZPrintf("%s: error opening file '%s' for writing\n", argv[0], pFileName); + } + } + + // issue request + if (!ds_stricmp(argv[1], "get") || !ds_stricmp(argv[1], "head") || !ds_stricmp(argv[1], "delete") || !ds_stricmp(argv[1], "options")) + { + ProtoHttpRequestTypeE eRequestType; + + ZPrintf("%s: downloading %s\n", argv[0], pUrl); + + // map to protohttp request type + if (!ds_stricmp(argv[1], "head")) + { + eRequestType = PROTOHTTP_REQUESTTYPE_HEAD; + } + else if (!ds_stricmp(argv[1], "get")) + { + eRequestType = PROTOHTTP_REQUESTTYPE_GET; + } + else if (!ds_stricmp(argv[1], "delete")) + { + eRequestType = PROTOHTTP_REQUESTTYPE_DELETE; + } + else if (!ds_stricmp(argv[1], "options")) + { + eRequestType = PROTOHTTP_REQUESTTYPE_OPTIONS; + } + else + { + ZPrintf("%s: unrecognized request %s\n", argv[0], argv[1]); + return(-1); + } + + if (pRef->bUseWriteCb) + { + iResult = ProtoHttpRequestCb(pRef->http, pUrl, NULL, 0, eRequestType, _HttpWriteCb, pRef); + } + else + { + iResult = ProtoHttpRequest(pRef->http, pUrl, NULL, 0, eRequestType); + } + + if (iResult == 0) + { + pRef->state = DNLOAD; + } + } + + // set up recurring callback to process transaction + if (pRef->state != IDLE) + { + iResult = ZCallback(_CmdHttpIdleCB, HTTP_RATE); + } + + return(iResult); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/http2.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/http2.c new file mode 100644 index 00000000..e69db7ec --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/http2.c @@ -0,0 +1,989 @@ +/*H********************************************************************************/ +/*! + \File http2.c + + \Description + Test the ProtoHttp2 client + + \Copyright + Copyright (c) 2016 Electronic Arts Inc. + + \Version 12/01/2016 (eesponda) +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protohttp2.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/util/protobufcommon.h" +#include "DirtySDK/util/protobufwrite.h" +#include "DirtySDK/util/protobufread.h" +#include "libsample/zmem.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +//! default address for the grpc server we are testing +#define DEFAULT_GRPC_SERVER ("http://10.14.141.208:50051") + +/*** Type Definitions *************************************************************/ + +//! wrapper for the data payloads used for our tests +typedef struct FixedBufferT +{ + int32_t iSize; + uint8_t aData[256]; +} FixedBufferT; + +//! generator function for data +typedef FixedBufferT (GenerateFn)(void); + +//! handle response function +typedef const uint8_t *(ResponseFn)(const uint8_t *pBuf, int32_t iBufLen); + +//! location used for grpc tests +typedef struct LocationT +{ + uint64_t iLatitude; + uint64_t iLongitude; +} LocationT; + +typedef struct Http2RefT +{ + ProtoHttp2RefT *pHttp; //!< http2 module ref + int32_t iStreamId; //!< main stream identifier + + char strGrpcHost[32]; //!< the default host to run grpc tests + + uint8_t *pBuf; //!< buffer for reading data + int32_t iBufSize; //!< size of the buffer + + FixedBufferT Buffer; //!< buffer for sending data + int32_t iDataSent; //!< how much data we have sent + + GenerateFn *pGen; //!< streaming generator fn + ResponseFn *pResp; //!< response handling fn + int32_t iIndex; //!< index of the data for client/bi-directional sends + int32_t iMaxIndex; //!< max index of data + + uint32_t uTimer; //!< timer to gauge operations + int32_t iCount; //!< number of bytes downloaded + int32_t iShow; //!< current bytes show in logging +} Http2RefT; + +/*** Variables ********************************************************************/ + +// instance for used in testing +static Http2RefT _Http2 = { NULL, 0, DEFAULT_GRPC_SERVER, NULL, 0, { 0, { 0 } }, 0, NULL, NULL, 0, 0, 0, 0, 0 }; + +//! list of locations use for RecordRoute RPC +static const LocationT _aFeatureLocations[] = +{ + { 407838351, (uint64_t)-746143763 }, + { 408122808, (uint64_t)-743999179 }, + { 413628156, (uint64_t)-749015468 }, + { 419999544, (uint64_t)-740371136 }, + { 414008389, (uint64_t)-743951297 }, + { 419611318, (uint64_t)-746524769 }, + { 406109563, (uint64_t)-742186778 }, + { 416802456, (uint64_t)-742370183 }, + { 412950425, (uint64_t)-741077389 }, + { 412144655, (uint64_t)-743949739 }, + { 415736605, (uint64_t)-742847522 }, + { 413843930, (uint64_t)-740501726 }, + { 410873075, (uint64_t)-744459023 }, + { 412346009, (uint64_t)-744026814 }, + { 402948455, (uint64_t)-747903913 }, + { 406337092, (uint64_t)-740122226 }, + { 406421967, (uint64_t)-747727624 }, + { 416318082, (uint64_t)-749677716 }, + { 415301720, (uint64_t)-748416257 }, + { 402647019, (uint64_t)-747071791 }, + { 412567807, (uint64_t)-741058078 }, + { 416855156, (uint64_t)-744420597 }, + { 404663628, (uint64_t)-744820157 }, + { 407113723, (uint64_t)-749746483 }, + { 402133926, (uint64_t)-743613249 }, + { 400273442, (uint64_t)-741220915 }, + { 411236786, (uint64_t)-744070769 }, + { 411633782, (uint64_t)-746784970 }, + { 415830701, (uint64_t)-742952812 }, + { 413447164, (uint64_t)-748712898 }, + { 405047245, (uint64_t)-749800722 }, + { 418858923, (uint64_t)-746156790 }, + { 417951888, (uint64_t)-748484944 }, + { 407033786, (uint64_t)-743977337 }, + { 417548014, (uint64_t)-740075041 }, + { 410395868, (uint64_t)-744972325 }, + { 404615353, (uint64_t)-745129803 }, + { 406589790, (uint64_t)-743560121 }, + { 414653148, (uint64_t)-740477477 }, + { 405957808, (uint64_t)-743255336 }, + { 411733589, (uint64_t)-741648093 }, + { 412676291, (uint64_t)-742606606 }, + { 409224445, (uint64_t)-748286738 }, + { 406523420, (uint64_t)-742135517 }, + { 401827388, (uint64_t)-740294537 }, + { 410564152, (uint64_t)-743685054 }, + { 408472324, (uint64_t)-740726046 }, + { 412452168, (uint64_t)-740214052 }, + { 409146138, (uint64_t)-746188906 }, + { 404701380, (uint64_t)-744781745 }, + { 409642566, (uint64_t)-746017679 }, + { 408031728, (uint64_t)-748645385 }, + { 413700272, (uint64_t)-742135189 }, + { 404310607, (uint64_t)-740282632 }, + { 409319800, (uint64_t)-746201391 }, + { 406685311, (uint64_t)-742108603 }, + { 419018117, (uint64_t)-749142781 }, + { 412856162, (uint64_t)-745148837 }, + { 416560744, (uint64_t)-746721964 }, + { 405314270, (uint64_t)-749836354 }, + { 414219548, (uint64_t)-743327440 }, + { 415534177, (uint64_t)-742900616 }, + { 406898530, (uint64_t)-749127080 }, + { 407586880, (uint64_t)-741670168 }, + { 400106455, (uint64_t)-742870190 }, + { 400066188, (uint64_t)-746793294 }, + { 418803880, (uint64_t)-744102673 }, + { 414204288, (uint64_t)-747895140 }, + { 414777405, (uint64_t)-740615601 }, + { 415464475, (uint64_t)-747175374 }, + { 404062378, (uint64_t)-746376177 }, + { 405688272, (uint64_t)-749285130 }, + { 400342070, (uint64_t)-748788996 }, + { 401809022, (uint64_t)-744157964 }, + { 404226644, (uint64_t)-740517141 }, + { 410322033, (uint64_t)-747871659 }, + { 407100674, (uint64_t)-747742727 }, + { 418811433, (uint64_t)-741718005 }, + { 415034302, (uint64_t)-743850945 }, + { 411349992, (uint64_t)-743694161 }, + { 404839914, (uint64_t)-744759616 }, + { 414638017, (uint64_t)-745957854 }, + { 412127800, (uint64_t)-740173578 }, + { 401263460, (uint64_t)-747964303 }, + { 412843391, (uint64_t)-749086026 }, + { 418512773, (uint64_t)-743067823 }, + { 404318328, (uint64_t)-740835638 }, + { 419020746, (uint64_t)-741172328 }, + { 404080723, (uint64_t)-746119569 }, + { 401012643, (uint64_t)-744035134 }, + { 404306372, (uint64_t)-741079661 }, + { 403966326, (uint64_t)-748519297 }, + { 405002031, (uint64_t)-748407866 }, + { 409532885, (uint64_t)-742200683 }, + { 416851321, (uint64_t)-742674555 }, + { 406411633, (uint64_t)-741722051 }, + { 413069058, (uint64_t)-744597778 }, + { 418465462, (uint64_t)-746859398 }, + { 411733222, (uint64_t)-744228360 }, + { 410248224, (uint64_t)-747127767 } +}; + +/*** Private functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2WriteGrpcHeader + + \Description + Encodes the required size of the message into the buffer + + \Input *pEncoder - the message encoder + \Input *pPayload - the payload the message was encoded to + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHttp2WriteGrpcHeader(ProtobufWriteRefT *pEncoder, FixedBufferT *pPayload) +{ + pPayload->iSize = ProtobufWriteDestroy(pEncoder); + // add extra for compression byte + pPayload->iSize += 1; +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2GetFeature + + \Description + Encodes the payload for the GetFeature RPC + + \Output + FixedBufferT - the buffer we have encoded + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static FixedBufferT _CmdHttp2GetFeature(void) +{ + FixedBufferT Payload; + ProtobufWriteRefT *pEncoder; + + /* + rpc GetFeature(Point) returns (Feature) {} + + message Point { + int32 latitude = 1; + int32 longitude = 2; + } + */ + + ds_memclr(&Payload, sizeof(Payload)); + pEncoder = ProtobufWriteCreate(Payload.aData+1, sizeof(Payload.aData)-1, TRUE); + + ProtobufWriteVarint(pEncoder, 409146138, 1); + ProtobufWriteVarint(pEncoder, (uint64_t)-746188906, 2); + + // write the header + _CmdHttp2WriteGrpcHeader(pEncoder, &Payload); + + return(Payload); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2GetFeatureResponse + + \Description + Handles the response for the GetFeature RPC + + \Input *pBuffer - payload + \Input iBufLen - size of payload + + \Output + const uint8_t * - new buffer location past the response + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static const uint8_t *_CmdHttp2GetFeatureResponse(const uint8_t *pBuffer, int32_t iBufLen) +{ + int32_t iMsgSize; + ProtobufReadT Reader, Msg; + struct { + char strName[256]; + LocationT Point; + } Response; + + ds_memset(&Response, -1, sizeof(Response)); + + /* + rpc GetFeature(Point) returns (Feature) {} + + message Point { + int32 latitude = 1; + int32 longitude = 2; + } + + message Feature { + string name = 1; + Point location = 2; + } + */ + + // get message size (skipping compression) + pBuffer = ProtobufCommonReadSize(pBuffer+1, iBufLen-1, &iMsgSize); + ProtobufReadInit(&Reader, pBuffer, iMsgSize); + + ProtobufReadString(&Reader, ProtobufReadFind(&Reader, 1), Response.strName, sizeof(Response.strName)); + if (ProtobufReadMessage(&Reader, ProtobufReadFind(&Reader, 2), &Msg) != NULL) + { + Response.Point.iLatitude = ProtobufReadVarint(&Msg, ProtobufReadFind(&Msg, 1)); + Response.Point.iLongitude = ProtobufReadVarint(&Msg, ProtobufReadFind(&Msg, 2)); + } + + ZPrintf("http2: name (%s), location (%d/%d)\n", Response.strName, Response.Point.iLatitude, Response.Point.iLongitude); + return(pBuffer+iMsgSize); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2ListFeatures + + \Description + Encodes the payload for the ListFeatures RPC + + \Output + FixedBufferT - the buffer we have encoded + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static FixedBufferT _CmdHttp2ListFeatures(void) +{ + /* + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + message Rectangle { + Point lo = 1; + Point hi = 2; + } + */ + + FixedBufferT Payload; + ProtobufWriteRefT *pEncoder; + + ds_memclr(&Payload, sizeof(Payload)); + pEncoder = ProtobufWriteCreate(Payload.aData+1, sizeof(Payload.aData)-1, TRUE); + + ProtobufWriteMessageBegin(pEncoder, 1); + ProtobufWriteVarint(pEncoder, 400000000, 1); + ProtobufWriteVarint(pEncoder, (uint64_t)-750000000, 2); + ProtobufWriteMessageEnd(pEncoder); + + ProtobufWriteMessageBegin(pEncoder, 2); + ProtobufWriteVarint(pEncoder, 420000000, 1); + ProtobufWriteVarint(pEncoder, (uint64_t)-730000000, 2); + ProtobufWriteMessageEnd(pEncoder); + + // write the header + _CmdHttp2WriteGrpcHeader(pEncoder, &Payload); + + return(Payload); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2ListFeaturesResponse + + \Description + Handles the response for the ListFeatures RPC + + \Input *pBuffer - payload + \Input iBufLen - size of payload + + \Output + const uint8_t * - new buffer location past the response + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static const uint8_t *_CmdHttp2ListFeaturesResponse(const uint8_t *pBuffer, int32_t iBufLen) +{ + int32_t iMsgSize; + ProtobufReadT Reader, Msg; + + struct { + char strName[256]; + LocationT Point; + } Response; + + /* + rpc ListFeatures(Rectangle) returns (stream Feature) {} + + message Point { + int32 latitude = 1; + int32 longitude = 2; + } + + message Feature { + string name = 1; + Point location = 2; + } + */ + + // get message size (skipping compression) + pBuffer = ProtobufCommonReadSize(pBuffer+1, iBufLen-1, &iMsgSize); + ProtobufReadInit(&Reader, pBuffer, iMsgSize); + + ProtobufReadString(&Reader, ProtobufReadFind(&Reader, 1), Response.strName, sizeof(Response.strName)); + if (ProtobufReadMessage(&Reader, ProtobufReadFind(&Reader, 2), &Msg) != NULL) + { + Response.Point.iLatitude = ProtobufReadVarint(&Msg, ProtobufReadFind(&Msg, 1)); + Response.Point.iLongitude = ProtobufReadVarint(&Msg, ProtobufReadFind(&Msg, 2)); + } + + ZPrintf("http2: name (%s), location (%d/%d)\n", Response.strName, Response.Point.iLatitude, Response.Point.iLongitude); + return(pBuffer+iMsgSize); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2RecordRoute + + \Description + Encodes the payload for the RecordRoute RPC + + \Output + FixedBufferT - the buffer we have encoded + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static FixedBufferT _CmdHttp2RecordRoute(void) +{ + /* + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + message Point { + int32 latitude = 1; + int32 longitude = 2; + } + */ + + FixedBufferT Payload; + ProtobufWriteRefT *pEncoder; + const int32_t iIndex = rand() % (sizeof(_aFeatureLocations)/sizeof(_aFeatureLocations[0])); + + ds_memclr(&Payload, sizeof(Payload)); + pEncoder = ProtobufWriteCreate(Payload.aData+1, sizeof(Payload.aData)-1, TRUE); + ProtobufWriteVarint(pEncoder, _aFeatureLocations[iIndex].iLatitude, 1); + ProtobufWriteVarint(pEncoder, _aFeatureLocations[iIndex].iLongitude, 2); + + // write the header + _CmdHttp2WriteGrpcHeader(pEncoder, &Payload); + + return(Payload); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2RecordRouteResponse + + \Description + Handles the response for the RecordRoute RPC + + \Input *pBuffer - payload + \Input iBufLen - size of payload + + \Output + const uint8_t * - new buffer location past the response + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static const uint8_t *_CmdHttp2RecordRouteResponse(const uint8_t *pBuffer, int32_t iBufLen) +{ + int32_t iMsgSize; + ProtobufReadT Reader; + + struct { + int32_t iPointCount; + int32_t iFeatureCount; + int32_t iDistance; + int32_t iElapsedTime; + } Response; + + /* + rpc RecordRoute(stream Point) returns (RouteSummary) {} + + message RouteSummary { + int32 point_count = 1; + int32 feature_count = 2; + int32 distance = 3; + int32 elapsed_time = 4; + } + */ + + // get message size (skipping compression) + pBuffer = ProtobufCommonReadSize(pBuffer+1, iBufLen-1, &iMsgSize); + ProtobufReadInit(&Reader, pBuffer, iMsgSize); + + Response.iPointCount = (int32_t)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, 1)); + Response.iFeatureCount = (int32_t)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, 2)); + Response.iDistance = (int32_t)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, 3)); + Response.iElapsedTime = (int32_t)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, 4)); + + ZPrintf("http2: pointcount %d, featurecount %d, distance %d, elapsedtime %d\n", Response.iPointCount, Response.iFeatureCount, Response.iDistance, Response.iElapsedTime); + return(pBuffer+iMsgSize); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2RouteChat + + \Description + Encodes the payload for the RouteChat RPC + + \Output + FixedBufferT - the buffer we have encoded + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static FixedBufferT _CmdHttp2RouteChat(void) +{ + /* + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} + + message RouteNote { + Point location = 1; + string message = 2; + } + */ + + FixedBufferT Payload = { 0, { 0 } }; + int32_t iIndex; + const char *aMessages[] = { "First message", "Second message", "Third message", "Fourth message" }; + LocationT aLocations[] = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 0, 0 } }; + + for (iIndex = 0; iIndex < 4; iIndex += 1) + { + FixedBufferT Message = { 0, { 0 } }; + ProtobufWriteRefT *pEncoder; + + pEncoder = ProtobufWriteCreate(Message.aData+1, sizeof(Message.aData)-1, TRUE); + ProtobufWriteMessageBegin(pEncoder, 1); + ProtobufWriteVarint(pEncoder, aLocations[iIndex].iLatitude, 1); + ProtobufWriteVarint(pEncoder, aLocations[iIndex].iLongitude, 2); + ProtobufWriteMessageEnd(pEncoder); + ProtobufWriteString(pEncoder, aMessages[iIndex], (signed)strlen(aMessages[iIndex]), 2); + + _CmdHttp2WriteGrpcHeader(pEncoder, &Message); + + ds_memcpy(Payload.aData+Payload.iSize, Message.aData, Message.iSize); + Payload.iSize += Message.iSize; + } + + return(Payload); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2RouteChatResponse + + \Description + Handles the response for the RouteChat RPC + + \Input *pBuffer - payload + \Input iBufLen - size of payload + + \Output + const uint8_t * - new buffer location past the response + + \Version 07/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static const uint8_t *_CmdHttp2RouteChatResponse(const uint8_t *pBuffer, int32_t iBufLen) +{ + int32_t iMsgSize; + ProtobufReadT Reader, Msg; + + struct { + LocationT Point; + char strMessage[256]; + } Response; + + /* + rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} + + message RouteNote { + Point location = 1; + string message = 2; + } + */ + + // get message size (skipping compression) + pBuffer = ProtobufCommonReadSize(pBuffer+1, iBufLen-1, &iMsgSize); + ProtobufReadInit(&Reader, pBuffer, iMsgSize); + + if (ProtobufReadMessage(&Reader, ProtobufReadFind(&Reader, 1), &Msg) != NULL) + { + Response.Point.iLatitude = ProtobufReadVarint(&Msg, ProtobufReadFind(&Msg, 1)); + Response.Point.iLongitude = ProtobufReadVarint(&Msg, ProtobufReadFind(&Msg, 2)); + } + ProtobufReadString(&Reader, ProtobufReadFind(&Reader, 2), Response.strMessage, sizeof(Response.strMessage)); + + ZPrintf("http2: location (%d/%d), message (%s)\n", Response.Point.iLatitude, Response.Point.iLongitude, Response.strMessage); + return(pBuffer+iMsgSize); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2CleaupSend + + Cleans up the module ref for a request send + + \Input *pHttp2 - module ref for this test to cleanup + + \Version 12/01/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHttp2CleaupSend(Http2RefT *pHttp2) +{ + ds_memclr(&pHttp2->Buffer, sizeof(pHttp2->Buffer)); + pHttp2->iDataSent = 0; + pHttp2->pGen = NULL; + pHttp2->iMaxIndex = 0; + pHttp2->iIndex = 0; +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2IdleCb + + \Description + Update for the http2 testing module + + \Input *pArgz - environment + \Input iArgc - standard number of arguments + \Input *pArgv[] - standard arg list + + \Version 12/01/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdHttp2IdleCb(ZContext *pArgz, int32_t iArgc, char *pArgv[]) +{ + int32_t iResult; + Http2RefT *pHttp2 = &_Http2; + + // clean up the ref if needed + if (iArgc == 0) + { + if (pHttp2->pHttp != NULL) + { + ProtoHttp2Destroy(pHttp2->pHttp); + ProtoSSLClrCACerts(); + pHttp2->pHttp = NULL; + } + return(0); + } + + // try to send data + if (pHttp2->Buffer.iSize > 0) + { + if (pHttp2->iDataSent < pHttp2->Buffer.iSize) + { + int32_t iDataSent; + if ((iDataSent = ProtoHttp2Send(pHttp2->pHttp, pHttp2->iStreamId, pHttp2->Buffer.aData+pHttp2->iDataSent, pHttp2->Buffer.iSize-pHttp2->iDataSent)) < 0) + { + return(1); + } + pHttp2->iDataSent += iDataSent; + } + else if (pHttp2->iIndex < pHttp2->iMaxIndex) + { + pHttp2->Buffer = pHttp2->pGen(); + pHttp2->iDataSent = 0; + pHttp2->iIndex += 1; + } + else + { + _CmdHttp2CleaupSend(pHttp2); + ProtoHttp2Send(pHttp2->pHttp, pHttp2->iStreamId, NULL, PROTOHTTP2_STREAM_END); + } + } + + // try to read data + if (pHttp2->iStreamId != 0) + { + ProtoHttpRequestTypeE eRequestType = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'rtyp', NULL, 0); + + /* for straight gets we want to use this to measure how fast our downloads are + other types can be handled as before */ + if (eRequestType == PROTOHTTP_REQUESTTYPE_GET) + { + uint8_t strBuf[16*1024]; + while ((iResult = ProtoHttp2Recv(pHttp2->pHttp, pHttp2->iStreamId, strBuf, 1, sizeof(strBuf))) > 0) + { + ProtoHttp2Update(pHttp2->pHttp); + pHttp2->iCount += iResult; + } + + if (pHttp2->iCount != pHttp2->iShow) + { + ZPrintf("http2: downloaded %d bytes\n", pHttp2->iCount); + pHttp2->iShow = pHttp2->iCount; + } + if (iResult == PROTOHTTP2_RECVDONE || iResult == PROTOHTTP2_RECVHEAD) + { + int32_t iTickDiff = NetTickDiff(NetTick(), pHttp2->uTimer); + + iResult = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'head', NULL, 0); + ZPrintf("http2: 'head' %d\n", iResult); + + iResult = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'body', NULL, 0); + ZPrintf("http2: 'body' %d in %.2f seconds (%.3f k/sec)\n", iResult, (float)iTickDiff/1000.0f, + ((float)iResult * 1000.0f) / ((float)iTickDiff * 1024.0f)); + + iResult = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'done', NULL, 0); + ZPrintf("http2: 'done' %s\n", iResult ? "TRUE" : "FALSE"); + + ProtoHttp2StreamFree(pHttp2->pHttp, pHttp2->iStreamId); + pHttp2->iStreamId = 0; + pHttp2->iCount = pHttp2->iShow = 0; + } + } + else + { + uint8_t bDone; + iResult = ProtoHttp2RecvAll(pHttp2->pHttp, pHttp2->iStreamId, pHttp2->pBuf, pHttp2->iBufSize); + bDone = iResult != PROTOHTTP2_RECVWAIT && iResult != PROTOHTTP2_RECVBUFF; + + // handle finished + if (iResult > 0) + { + int32_t iBody; + int32_t iTickDiff = NetTickDiff(NetTick(), pHttp2->uTimer); + + iResult = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'head', NULL, 0); + ZPrintf("http2: 'head' %d\n", iResult); + + iBody = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'body', NULL, 0); + ZPrintf("http2: 'body' %d in %.2f seconds (%.3f k/sec)\n", iBody, (float)iTickDiff/1000.0f, + ((float)iBody * 1000.0f) / ((float)iTickDiff * 1024.0f)); + + iResult = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'done', NULL, 0); + ZPrintf("http2: 'done' %s\n", iResult ? "TRUE" : "FALSE"); + + iResult = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'hres', NULL, 0); + ZPrintf("http2: 'hres' 0x%08x\n", iResult); + + // we have received the full response, now we can handle it + if (pHttp2->pResp != NULL) + { + const uint8_t *pCur = pHttp2->pBuf; + const uint8_t *pEnd = pHttp2->pBuf+iBody; + while (pCur < pEnd) + { + pCur = pHttp2->pResp(pCur, (int32_t)(pEnd-pCur)); + } + } + } + // increase size if needed + else if (iResult == PROTOHTTP2_RECVBUFF) + { + if (pHttp2->iBufSize == 0) + { + pHttp2->pBuf = ZMemAlloc(4096); + pHttp2->iBufSize = 4096; + } + else + { + uint8_t *pNewBuf = ZMemAlloc(pHttp2->iBufSize * 2); + ds_memcpy(pNewBuf, pHttp2->pBuf, pHttp2->iBufSize); + + ZMemFree(pHttp2->pBuf); + pHttp2->pBuf = pNewBuf; + pHttp2->iBufSize *= 2; + } + + ZPrintf("http2: allocated larger buffer: new size %d\n", pHttp2->iBufSize); + } + else if ((iResult == PROTOHTTP2_RECVFAIL) || (iResult == PROTOHTTP2_TIMEOUT)) + { + iResult = ProtoHttp2Status(pHttp2->pHttp, pHttp2->iStreamId, 'hres', NULL, 0); + ZPrintf("http2: receive failed (0x%08x)\n", iResult); + } + + // cleanup now that we are done + if (bDone == TRUE) + { + ProtoHttp2StreamFree(pHttp2->pHttp, pHttp2->iStreamId); + pHttp2->iStreamId = 0; + + ZMemFree(pHttp2->pBuf); + pHttp2->pBuf = NULL; + pHttp2->iBufSize = 0; + pHttp2->pResp = NULL; + } + } + } + + // update the ref + ProtoHttp2Update(pHttp2->pHttp); + + return(ZCallback(_CmdHttp2IdleCb, 1)); // slow enough to allow us to test abort +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2WriteCb + + \Description + Callback when we receive data (if registered) + + \Input *pState - module state + \Input *pCbInfo - information about the request + \Input *pData - data we are receiving + \Input iDataSize - size of the data + \Input *pUserData - user specific data + + \Output + int32_t - result of the operation + + \Version 12/01/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CmdHttp2WriteCb(ProtoHttp2RefT *pState, const ProtoHttp2WriteCbInfoT *pCbInfo, const uint8_t *pData, int32_t iDataSize, void *pUserData) +{ + Http2RefT *pHttp2 = (Http2RefT *)pUserData; + ZPrintf("received %d\n", iDataSize); + + // if the request is complete then cleanup the stream + if ((iDataSize == PROTOHTTP2_RECVDONE) || (iDataSize == PROTOHTTP2_RECVFAIL) || (iDataSize == PROTOHTTP2_TIMEOUT)) + { + ProtoHttp2StreamFree(pState, pCbInfo->iStreamId); + pHttp2->iStreamId = 0; + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttp2CreateGrpcRequest + + \Description + Wrapper to create grpc request + + \Input *pHttp2 - module state + \Input *pUri - the path of the request + \Input *pPayloadFn - pointer to function to generate payload data + \Input iNumPayload - number of entries in array + \Input *pResponseFn - pointer to function to handle response data + + \Version 12/01/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _CmdHttp2CreateGrpcRequest(Http2RefT *pHttp2, const char *pUri, GenerateFn *pPayloadFn, int32_t iNumPayloads, ResponseFn *pResponseFn) +{ + char strUrl[128]; + + // setup url + ds_snzprintf(strUrl, sizeof(strUrl), "%s/%s", pHttp2->strGrpcHost, pUri); + + // update headers, we set null first to clear the previous headers + ProtoHttp2Control(pHttp2->pHttp, 0, 'apnd', 0, 0, NULL); + ProtoHttp2Control(pHttp2->pHttp, 0, 'apnd', 0, 0, (void *)"te: trailers\r\ncontent-type: application/grpc\r\n"); + + pHttp2->uTimer = NetTick(); + + // create the request + if (ProtoHttp2Request(pHttp2->pHttp, strUrl, NULL, PROTOHTTP2_STREAM_BEGIN, PROTOHTTP_REQUESTTYPE_POST, &pHttp2->iStreamId) < 0) + { + ZPrintf("http2: failed to create request %s\n", pUri); + return; + } + // set the array and num indexies + pHttp2->pGen = pPayloadFn; + pHttp2->pResp = pResponseFn; + pHttp2->iMaxIndex = iNumPayloads; + // set index to 1 as we always read first entry + pHttp2->iIndex = 1; + // read the first entry + pHttp2->Buffer = pPayloadFn(); +} + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdHttp2 + + \Description + Entrypoint for the http2 testing module + + \Input *pArgz - environment + \Input iArgc - standard number of arguments + \Input *pArgv[] - standard arg list + + \Output + int32_t - standard return value + + \Version 12/01/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CmdHttp2(ZContext *pArgz, int32_t iArgc, char *pArgv[]) +{ + Http2RefT *pHttp2 = &_Http2; + + // allocate the module if needed + if (pHttp2->pHttp == NULL) + { + if ((pHttp2->pHttp = ProtoHttp2Create(0)) == NULL) + { + ZPrintf("http2: could not allocate module state\n"); + return(1); + } + } + + // make sure there are enough parameters + if (iArgc < 2) + { + return(0); + } + + // close the connection + if (ds_stricmp(pArgv[1], "close") == 0) + { + ProtoHttp2Close(pHttp2->pHttp); + } + // update the logging + else if (ds_stricmp(pArgv[1], "spam") == 0) + { + ProtoHttp2Control(pHttp2->pHttp, PROTOHTTP2_INVALID_STREAMID, 'spam', atoi(pArgv[2]), 0, NULL); + } + else if (ds_stricmp(pArgv[1], "time") == 0) + { + ProtoHttp2Control(pHttp2->pHttp, PROTOHTTP2_INVALID_STREAMID, 'time', atoi(pArgv[2]), 0, NULL); + } + else if (ds_stricmp(pArgv[1], "grpc") == 0) + { + ds_strnzcpy(pHttp2->strGrpcHost, pArgv[2], sizeof(pHttp2->strGrpcHost)); + } + + if (pHttp2->iStreamId == 0) + { + // handle the normal get/head/options + if ((ds_stricmp(pArgv[1], "get") == 0) || (ds_stricmp(pArgv[1], "head") == 0) || (ds_stricmp(pArgv[1], "options") == 0)) + { + // a bit nasty but gets the job done + ProtoHttpRequestTypeE eRequestType = *pArgv[1] == 'g' ? PROTOHTTP_REQUESTTYPE_GET : *pArgv[1] == 'h' ? PROTOHTTP_REQUESTTYPE_HEAD : PROTOHTTP_REQUESTTYPE_OPTIONS; + + pHttp2->uTimer = NetTick(); + + if ((iArgc == 4) && (ds_stricmp(pArgv[3], "-cb") == 0)) + { + ProtoHttp2RequestCb(pHttp2->pHttp, pArgv[2], NULL, 0, eRequestType, &pHttp2->iStreamId, _CmdHttp2WriteCb, &_Http2); + } + else + { + ProtoHttp2Request(pHttp2->pHttp, pArgv[2], NULL, 0, eRequestType, &pHttp2->iStreamId); + } + } + else if (ds_stricmp(pArgv[1], "test1") == 0) + { + _CmdHttp2CreateGrpcRequest(pHttp2, "routeguide.RouteGuide/GetFeature", &_CmdHttp2GetFeature, 0, &_CmdHttp2GetFeatureResponse); + } + else if (ds_stricmp(pArgv[1], "test2") == 0) + { + _CmdHttp2CreateGrpcRequest(pHttp2, "routeguide.RouteGuide/ListFeatures", &_CmdHttp2ListFeatures, 0, &_CmdHttp2ListFeaturesResponse); + } + else if (ds_stricmp(pArgv[1], "test3") == 0) + { + _CmdHttp2CreateGrpcRequest(pHttp2, "routeguide.RouteGuide/RecordRoute", &_CmdHttp2RecordRoute, 10, _CmdHttp2RecordRouteResponse); + } + else if (ds_stricmp(pArgv[1], "test4") == 0) + { + _CmdHttp2CreateGrpcRequest(pHttp2, "routeguide.RouteGuide/RouteChat", &_CmdHttp2RouteChat, 5, _CmdHttp2RouteChatResponse); + } + } + else + { + if (ds_stricmp(pArgv[1], "abort") == 0) + { + // abort the request + ProtoHttp2Abort(pHttp2->pHttp, pHttp2->iStreamId); + + // cleanup send data + _CmdHttp2CleaupSend(pHttp2); + + // cleanup stream + ProtoHttp2StreamFree(pHttp2->pHttp, pHttp2->iStreamId); + pHttp2->iStreamId = 0; + } + } + + return(ZCallback(_CmdHttp2IdleCb, 1)); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/httpmgr.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/httpmgr.c new file mode 100644 index 00000000..514b75e2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/httpmgr.c @@ -0,0 +1,1514 @@ +/*H********************************************************************************/ +/*! + \File httpmgr.c + + \Description + Implements basic http get and post client. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 10/28/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protohttpmanager.h" +#include "DirtySDK/proto/protossl.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +#define HTTP_BUFSIZE (100) +#define HTTP_RATE (1) +#define HTTP_MAXCMDS (64) // max number of in-flight commands + +#define HTTP_XTRAHDR0 "" +#define HTTP_XTRAHDR1 "X-Agent: DirtySock HTTP Tester\r\n" // test "normal" replacement (replaces Accept: header) +#define HTTP_XTRAHDR2 "User-Agent: DirtySock HTTP Tester\r\n" // test "extended" replacement (replaces User-Agent: and Accept: headers) + +//$$ tmp -- special test header used for hard-coded multipart/form-data testing -- this should be removed at some point when we have real multipart/form-data support +#define HTTP_XTRAHDR3 "Content-Type: multipart/form-data; boundary=TeStInG\r\n" \ + "User-Agent: DirtySock HTTP Tester\r\n" \ + "Accept: */*\r\n" + +#define HTTP_APNDHDR HTTP_XTRAHDR0 + +/*** Function Prototypes **********************************************************/ + +static int32_t _CmdHttpMgrIdleCB(ZContext *argz, int32_t argc, char *argv[]); + +/*** Type Definitions *************************************************************/ + +typedef struct HttpStateT // individual request states +{ + HttpManagerRefT *pHttpManager; + enum + { + IDLE, DNLOAD, UPLOAD, MGET + } state; + int32_t iHandle; + char strCookie[1024]; + int64_t iDataSize; + int64_t iSentSize; + int32_t iSendBufData; + int32_t iSendBufSent; + ZFileT iInpFile; + ZFileT iOutFile; + int32_t iOutSize; + char *pOutData; + int64_t show; + int64_t count; + int32_t sttime; + uint8_t bStreaming; + char strFileBuffer[16*1024]; +}HttpStateT; + +typedef struct HttpMgrRefT // module state storage +{ + HttpManagerRefT *pHttpManager; + int32_t iDebugLevel; + const char *pMgetBuffer; + const char *pMgetOffset; + uint32_t uMgetStart; + uint32_t uMgetTransactions; + + uint8_t bMgetShowUrlsOnly; + uint8_t bRecvAll; // use recvall instead of recv + uint8_t bUseWriteCb; + uint8_t _pad; + + char strModuleName[32]; + char strMgetFilename[256]; + char strApndHdr[2048]; + + // module state + HttpStateT States[HTTP_MAXCMDS]; + +} HttpMgrRefT; + +/*** Variables ********************************************************************/ + +static HttpMgrRefT _HttpMgr_Ref; +static uint8_t _HttpMgr_bInitialized = FALSE; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _HttpCheckHndlAndHref + + \Description + Validate 'hndl' and 'href' status selectors + + \Input *pState - transaction state + \Input *pProtoHttp - protohttpref to get handle for + \Input *pLogStr - string to identify caller + + \Version 10/03/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpCheckHndlAndHref(HttpStateT *pState, ProtoHttpRefT *pProtoHttp, const char *pLogStr) +{ + HttpMgrRefT *pRef = &_HttpMgr_Ref; + ProtoHttpRefT *pProtoHttpCheck; + int32_t iHandle, iResult; + + // get handle from href + if ((iHandle = HttpManagerStatus(pState->pHttpManager, -1, 'hndl', &pProtoHttp, sizeof(pProtoHttp))) < 0) + { + ZPrintf("httpmgr: %s request could not get handle for href %p\n", pLogStr, pProtoHttp); + return; + } + // get href from handle + if ((iResult = HttpManagerStatus(pState->pHttpManager, iHandle, 'href', &pProtoHttpCheck, sizeof(pProtoHttpCheck))) < 0) + { + ZPrintf("httpmgr: %s request could not get href for handle %d\n", pLogStr, iHandle); + return; + } + // make sure we got the right href + if (pProtoHttp != pProtoHttpCheck) + { + ZPrintf("httpmgr: %s request got wrong href %p for handle %d (expected %p)\n", pLogStr, pProtoHttpCheck, iHandle, pProtoHttp); + return; + } + // log success + if (pRef->iDebugLevel > 1) + { + ZPrintf("httpmgr: processing %s request for handle %d (ref %p)\n", pLogStr, iHandle, pProtoHttp); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpCustomHeaderCallback + + \Description + ProtoHttp send header callback. + + \Input *pProtoHttp - protohttp module state + \Input *pHeader - received header + \Input uHeaderSize - header size + \Input *pUserData - user ref (HttpMgrRefT) + + \Output + int32_t - zero + + \Notes + The header returned should be terminated by a *single* CRLF; ProtoHttp will + append the final CRLF to complete the header. The callback may return the + size of the header, or zero, in which case ProtoHttp will calculate the + headersize using strlen(). + + \Version 02/24/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpCustomHeaderCallback(ProtoHttpRefT *pProtoHttp, char *pHeader, uint32_t uHeaderSize, const char *pData, int64_t iDataLen, void *pUserRef) +{ + HttpStateT *pState = (HttpStateT *)pUserRef; + uint32_t uBufSize; + char *pAppend; + + // check 'hndl' and 'href' status selectors + _HttpCheckHndlAndHref(pState, pProtoHttp, "custom header"); + + // find append point and calc free header space + pAppend = pHeader + strlen(pHeader); + uBufSize = uHeaderSize - (uint32_t)(pAppend - pHeader); + + // append our header info +#if !DIRTYCODE_XBOXONE + if (pState->strCookie[0] != '\0') + { + /* $$todo -- cookies aren't really saved across multiple transactions, so that has + to be solved before this will work */ + ds_snzprintf(pAppend, uBufSize, "Cookie: %s\r\n%s", pState->strCookie, HTTP_APNDHDR); + } + else +#endif + { + ds_strnzcpy(pAppend, "X-Append: Custom append test\r\n", uBufSize); + } + + // recalc header size + uHeaderSize = (uint32_t)strlen(pHeader); + + return(uHeaderSize); +} + +/*F********************************************************************************/ +/*! + \Function _HttpRecvHeaderCallback + + \Description + ProtoHttp recv header callback. + + \Input *pProtoHttp - protohttp module state + \Input *pHeader - received header + \Input uHeaderSize - header size + \Input *pUserData - user ref (HttpMgrRefT) + + \Output + None. + + \Version 02/24/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpRecvHeaderCallback(ProtoHttpRefT *pProtoHttp, const char *pHeader, uint32_t uHeaderSize, void *pUserRef) +{ + HttpMgrRefT *pRef = &_HttpMgr_Ref; + HttpStateT *pState = (HttpStateT *)pUserRef; + char strBuffer[1024], strName[128]; + const char *pHdrTmp; + int32_t iLocnSize; + + // check 'hndl' and 'href' status selectors + _HttpCheckHndlAndHref(pState, pProtoHttp, "receive header"); + + // check for location header + if ((iLocnSize = ProtoHttpGetHeaderValue(pProtoHttp, pHeader, "location", NULL, 0, NULL)) > 0) + { + if (pRef->iDebugLevel > 1) + { + ZPrintf("httpmgr: location header size=%d\n", iLocnSize); + } + if (ProtoHttpGetHeaderValue(pProtoHttp, pHeader, "location", strBuffer, sizeof(strBuffer), NULL) == 0) + { + if (pRef->iDebugLevel > 1) + { + ZPrintf("httpmgr: location url='%s'\n", strBuffer); + } + } + else + { + ZPrintf("httpmgr: error querying location url\n"); + } + } + + // test ProtoHttpGetNextHeader() + for (pHdrTmp = pHeader; ProtoHttpGetNextHeader(pProtoHttp, pHdrTmp, strName, sizeof(strName), strBuffer, sizeof(strBuffer), &pHdrTmp) == 0; ) + { + #if 0 + ZPrintf("httpmgr: ===%s: %s\n", strName, strBuffer); + #endif + } + + // parse any set-cookie requests + for (pHdrTmp = pHeader; ProtoHttpGetHeaderValue(pProtoHttp, pHdrTmp, "set-cookie", strBuffer, sizeof(strBuffer), &pHdrTmp) == 0; ) + { + // print the cookie + if (pRef->iDebugLevel > 1) + { + ZPrintf("httpmgr: parsed cookie '%s'\n", strBuffer); + } + + // add field seperator + if (pState->strCookie[0] != '\0') + { + ds_strnzcat(pState->strCookie, ", ", sizeof(pState->strCookie)); + } + // append to cookie list + ds_strnzcat(pState->strCookie, strBuffer, sizeof(pState->strCookie)); + } + + // if we have any cookies, set them here for inclusion in next request + if (pState->strCookie[0] != '\0') + { + // format append header + ds_snzprintf(strBuffer, sizeof(strBuffer), "Cookie: %s\r\n%s", pState->strCookie, HTTP_APNDHDR); + ProtoHttpControl(pProtoHttp, 'apnd', 0, 0, strBuffer); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrAllocState + + \Description + Allocate an HttpStateT ref for tracking a transaction. + + \Input *pRef - pointer to httpmgr ref + + \Output + HttpStateT * - allocated state, or NULL on failure + + \Version 02/15/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static HttpStateT *_HttpMgrAllocState(HttpMgrRefT *pRef) +{ + HttpStateT *pState; + int32_t iState; + + for (iState = 0; iState < HTTP_MAXCMDS; iState += 1) + { + pState = &pRef->States[iState]; + if (pState->iHandle == 0) + { + // clear any previous stats + pState->count = 0; + pState->show = 0; + pState->iDataSize = 0; + pState->iSentSize = 0; + pState->bStreaming = FALSE; + pState->iHandle = HttpManagerAlloc(pRef->pHttpManager); + pState->pHttpManager = pRef->pHttpManager; + + // set callback user data pointer + HttpManagerControl(pRef->pHttpManager, pState->iHandle, 'cbup', 0, 0, pState); + + // init start timer + pState->sttime = NetTick(); + + // return initialized state to caller + return(pState); + } + } + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrFreeState + + \Description + Free an allocated HttpStateT ref. + + \Input *pRef - pointer to httpmgr ref + \Input *pState - pointer to state to free + + + \Version 02/18/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpMgrFreeState(HttpMgrRefT *pRef, HttpStateT *pState) +{ + // free handle + HttpManagerFree(pRef->pHttpManager, pState->iHandle); + // reset state memory + ds_memclr(pState, sizeof(*pState)); +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrReallocBuff + + \Description + Free an allocated HttpStateT ref. + + \Input *pRef - pointer to httpmgr ref + \Input *pState - pointer to transaction state + + \Version 07/29/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpMgrReallocBuff(HttpMgrRefT *pRef, HttpStateT *pState) +{ + char *pNewData; + int32_t iNewSize; + + // calc new buffer size + if ((iNewSize = pState->iOutSize) == 0) + { + // try getting body size + if ((iNewSize = HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'body', NULL, 0)) > 0) + { + // bump up buffer size for recvall null terminator + //$$ TODO V9 -- why 2 required, not 1?? + iNewSize += 2; + } + else + { + // assign a fixed size, since we didn't get a body size + iNewSize = 4096; + } + } + else + { + iNewSize *= 2; + } + + // allocate new buffer + if ((pNewData = ZMemAlloc(iNewSize)) == NULL) + { + return; + } + // if realloc, copy old data and free old pointer + if (pState->pOutData != NULL) + { + ds_memcpy(pNewData, pState->pOutData, pState->iOutSize); + ZMemFree(pState->pOutData); + } + // save new pointer + pState->pOutData = pNewData; + pState->iOutSize = iNewSize; +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrCheckComplete + + \Description + See if the HTTP transaction is complete. + + \Input *pRef - pointer to http ref + \Input *pCmdName - module name + + \Output + int32_t - completion status from ProtoHttpStatus() 'done' selector + + \Version 10/28/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpMgrCheckComplete(HttpManagerRefT *pHttpManager, HttpStateT *pState, const char *pCmdName) +{ + ProtoHttpResponseE eResponse; + int32_t iResult; + + // wait for ref to be assigned before checking ref status + if ((iResult = HttpManagerStatus(pHttpManager, pState->iHandle, 'href', NULL, 0)) < 0) + { + ZPrintf("httpmgr: waiting for httpref to be assigned\n"); + return(0); + } + // wait for header response + if ((iResult = HttpManagerStatus(pHttpManager, pState->iHandle, 'head', NULL, 0)) == -2) + { + ZPrintf("httpmgr: waiting for head response (result=%d)\n", iResult); + return(0); + } + // check for completion + if ((iResult = HttpManagerStatus(pHttpManager, pState->iHandle, 'done', NULL, 0)) == 0) + { + ZPrintf("httpmgr: waiting for completion (result=%d)\n", iResult); + return(0); + } + + // get completion result + eResponse = (ProtoHttpResponseE)HttpManagerStatus(pHttpManager, pState->iHandle, 'code', NULL, 0); + switch (PROTOHTTP_GetResponseClass(eResponse)) + { + case PROTOHTTP_RESPONSE_SUCCESSFUL: + ZPrintf("%s: success (%d)\n", pCmdName, eResponse); + break; + + case PROTOHTTP_RESPONSE_CLIENTERROR: + ZPrintf("%s: client error %d\n", pCmdName, eResponse); + break; + + case PROTOHTTP_RESPONSE_SERVERERROR: + ZPrintf("%s: server error %d\n", pCmdName, eResponse); + break; + + default: + ZPrintf("%s: unexpected result code %d\n", pCmdName, eResponse); + break; + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrMget + + \Description + Process an mget request + + \Input *pRef - module state + + \Version 02/16/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpMgrMget(HttpMgrRefT *pRef) +{ + const char *pHref = NULL, *pHref2, *pEndQuote; + char strUrl[1024], strFile[1024]; + char *strArgs[4] = { "httpmgr", "get", "", "" }; + + if (pRef->pMgetOffset != NULL) + { + // look for hrefs + for (pHref = strstr(pRef->pMgetOffset, "href=\""); pHref != NULL; pHref = strstr(pHref2, "href=\"")) + { + // skip href text + pHref2 = pHref + 6; + + // find trailing quote + if ((pEndQuote = strchr(pHref2, '"')) == NULL) + { + // skip it + continue; + } + + // copy the url + ds_strsubzcpy(strUrl, sizeof(strUrl), pHref2, (int32_t)(pEndQuote-pHref2)); + + // make sure it's a full URL + if (strncmp(strUrl, "http://", 7) && strncmp(strUrl, "https://", 8)) + { + // skip it + continue; + } + + // make a filename for the file, skipping http ref + ds_snzprintf(strFile, sizeof(strFile), "%s-data\\%s", pRef->strMgetFilename, strUrl+7); + + // issue an http command + strArgs[2] = strUrl; + strArgs[3] = "";//strFile; + + if (!pRef->bMgetShowUrlsOnly) + { + if (CmdHttpMgr(NULL, 3, strArgs) != 0) + { + // safe current url for next time around + pRef->pMgetOffset = pHref2; + break; + } + else + { + pRef->uMgetTransactions += 1; + } + } + else + { + ZPrintf("%s %s %s %s\n", strArgs[0], strArgs[1], strArgs[2], strArgs[3]); + } + } + } + + // update HttpManager ($$note -- should we need to have this here?) + HttpManagerUpdate(pRef->pHttpManager); + + // have we parsed the whole buffer? + if (pHref == NULL) + { + // mark that we have completed parsing the buffer + if (pRef->pMgetOffset != NULL) + { + pRef->pMgetOffset = NULL; + } + + // are all of our transactions complete? + if (HttpManagerStatus(pRef->pHttpManager, -1, 'busy', NULL, 0) == 0) + { + HttpManagerStatT MgetStats; + + // report time taken for mget request + ZPrintf("httpmgr: mget completed in %dms (%d transactions)\n", NetTickDiff(NetTick(), pRef->uMgetStart), pRef->uMgetTransactions); + + // get and display stats + if (HttpManagerStatus(pRef->pHttpManager, -1, 'stat', &MgetStats, sizeof(MgetStats)) == 0) + { + // display stats + ZPrintf("httpmgr: mget transactions: %d\n", MgetStats.uNumTransactions); + if (MgetStats.uNumTransactions > 0) + { + ZPrintf("httpmgr: keepalive transactions: %d\n", MgetStats.uNumKeepAliveTransactions); + ZPrintf("httpmgr: pipelined transactions: %d\n", MgetStats.uNumPipelinedTransactions); + ZPrintf("httpmgr: max active transactions: %d\n", MgetStats.uMaxActiveTransactions); + ZPrintf("httpmgr: max queued transactions: %d\n", MgetStats.uMaxQueuedTransactions); + ZPrintf("httpmgr: sum queue wait time: %dms\n", MgetStats.uSumQueueWaitLatency); + ZPrintf("httpmgr: avg queue wait time: %dms\n", MgetStats.uSumQueueWaitLatency/MgetStats.uNumTransactions); + ZPrintf("httpmgr: max queue wait time: %dms\n", MgetStats.uMaxQueueWaitLatency); + ZPrintf("httpmgr: sum queue free time: %dms\n", MgetStats.uSumQueueFreeLatency); + ZPrintf("httpmgr: avg queue free time: %dms\n", MgetStats.uSumQueueFreeLatency/MgetStats.uNumTransactions); + ZPrintf("httpmgr: max queue free time: %dms\n", MgetStats.uMaxQueueFreeLatency); + ZPrintf("httpmgr: total bytes transferred: %d\n", MgetStats.uTransactionBytes); + ZPrintf("httpmgr: total transaction time: %d\n", MgetStats.uTransactionTime); + ZPrintf("httpmgr: avg bytes per second %.2f\n", (float)MgetStats.uTransactionBytes*1000.0f/(float)MgetStats.uTransactionTime); + ZPrintf("httpmgr: avg transaction size %.2f\n", (float)MgetStats.uTransactionBytes/(float)MgetStats.uNumTransactions); + } + } + else + { + ZPrintf("%s: could not get stats\n", pRef->strModuleName); + } + + // reset stats + ZPrintf("httpmgr: resetting stats\n"); + HttpManagerControl(pRef->pHttpManager, -1, 'stcl', 0, 0, NULL); + + // dispose of mget buffer + ZMemFree((void *)pRef->pMgetBuffer); + pRef->pMgetBuffer = NULL; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrDownloadProcessData + + \Description + Process data for a download transaction. + + \Input *pRef - httpmanager state + \Input *pState - transaction state + \Input iLen - recv response + + \Version 07/02/2012 (jbrookes) split from _HttpMgrDownloadProcess() +*/ +/********************************************************************************F*/ +static void _HttpMgrDownloadProcessData(HttpMgrRefT *pRef, HttpStateT *pState, int32_t iLen) +{ + char strBuf[1024]; + + // see if we should show progress + if ((pState->count/1024) != (pState->show/1024)) + { + pState->show = pState->count; + if (pRef->iDebugLevel > 1) + { + ZPrintf("%s: downloaded %qd bytes\n", pRef->strModuleName, pState->count); + } + } + + // see if we are done + if ((iLen < 0) && (iLen != PROTOHTTP_RECVWAIT)) + { + // get the url we issued + HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'urls', strBuf, sizeof(strBuf)); + + // completed successfully? + if ((iLen == PROTOHTTP_RECVDONE) || (iLen == PROTOHTTP_RECVHEAD)) + { + if (pRef->iDebugLevel > 1) + { + int32_t iDlTime = NetTickDiff(NetTick(), pState->sttime); + int32_t iHdrCode = HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'code', NULL, 0); + + ZPrintf("%s: %s download done (%d): %qd bytes in %.2f seconds (%.3f k/sec)\n", pRef->strModuleName, strBuf, iHdrCode, pState->count, + (float)iDlTime/1000.0f, ((float)pState->count * 1000.0f) / ((float)iDlTime * 1024.0f)); + } + + // display some header info (suppress if it's an mget, unless our debuglevel is high) + if ((pRef->pMgetBuffer == NULL) || (pRef->iDebugLevel > 1)) + { + if (HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'htxt', strBuf, sizeof(strBuf)) >= 0) + { + ZPrintf("%s response header:\n%s\n", pRef->strModuleName, strBuf); + } + + // display a couple of parsed fields + if (HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'head', NULL, 0) > 0) + { + time_t tLastMod = HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'date', NULL, 0); + const char *pTime; + int64_t iBodySize; + + if (tLastMod != 0) + { + if ((pTime = ctime(&tLastMod)) != NULL) + { + ZPrintf("%s: Last-Modified: %s", pRef->strModuleName, pTime); + } + } + HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'body', &iBodySize, sizeof(iBodySize)); + ZPrintf("%s: Content-Length: %qd\n", pRef->strModuleName, iBodySize); + } + } + } + else + { + int32_t iSslFail = HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'essl', NULL, 0); + ZPrintf("%s: download failed (err=%d, sslerr=%d)\n", pRef->strModuleName, iLen, iSslFail); + if ((iSslFail == PROTOSSL_ERROR_CERT_INVALID) || (iSslFail == PROTOSSL_ERROR_CERT_HOST) || (iSslFail == PROTOSSL_ERROR_CERT_NOTRUST)) + { + ProtoSSLCertInfoT CertInfo; + if (HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'cert', &CertInfo, sizeof(CertInfo)) == 0) + { + ZPrintf("%s: cert failure (%d): (C=%s, ST=%s, L=%s, O=%s, OU=%s, CN=%s)\n", pRef->strModuleName, iSslFail, + CertInfo.Ident.strCountry, CertInfo.Ident.strState, CertInfo.Ident.strCity, + CertInfo.Ident.strOrg, CertInfo.Ident.strUnit, CertInfo.Ident.strCommon); + } + else + { + ZPrintf("%s: could not get cert info\n", pRef->strModuleName); + } + } + } + + // if file exists, close it + if (pState->iOutFile > 0) + { + ZFileClose(pState->iOutFile); + } + pState->iOutFile = 0; + + // if output buffer exists, free it + if (pState->pOutData != NULL) + { + pState->pOutData = NULL; + } + pState->iOutSize = 0; + + // free state tracking + _HttpMgrFreeState(pRef, pState); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrRecvData + + \Description + Receive data from HttpManager using eithe HttpManagerRecv() or + HttpManagerRecvAll(). + + \Input *pRef - httpmanager state + \Input *pState - transaction state + + \Version 07/02/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpMgrRecvData(HttpMgrRefT *pRef, HttpStateT *pState) +{ + char strBuf[16*1024]; + int32_t iLen; + + // check for data + if (!pRef->bRecvAll) + { + while ((iLen = HttpManagerRecv(pRef->pHttpManager, pState->iHandle, strBuf, 1, sizeof(strBuf))) > 0) + { + pState->count += iLen; + if (pState->iOutFile != 0) + { + ZFileWrite(pState->iOutFile, strBuf, iLen); + } + } + } + else + { + // receive all the data + if ((iLen = HttpManagerRecvAll(pRef->pHttpManager, pState->iHandle, pState->pOutData, pState->iOutSize)) > 0) + { + pState->count = iLen; + if (pState->iOutFile != 0) + { + ZFileWrite(pState->iOutFile, pState->pOutData, iLen); + } + iLen = PROTOHTTP_RECVDONE; + } + else if (iLen == PROTOHTTP_RECVBUFF) + { + // grow the buffer + _HttpMgrReallocBuff(pRef, pState); + // swallow error code + iLen = 0; + } + } + return(iLen); +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrDownloadProcess + + \Description + Process a download transaction. + + \Input *pRef - httpmanager state + \Input *pState - transaction state + + \Version 10/28/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpMgrDownloadProcess(HttpMgrRefT *pRef, HttpStateT *pState) +{ + int32_t iLen; + + // if we're not doing the write callback thing, poll for data + for (iLen = 1; (iLen != PROTOHTTP_RECVWAIT) && (iLen != 0) && (pState->state != IDLE); ) + { + // receive data + iLen = _HttpMgrRecvData(pRef, pState); + // process data + _HttpMgrDownloadProcessData(pRef, pState, iLen); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrWriteCb + + \Description + Implementation of ProtoHttp write callback + + \Input *pState - http module state + \Input *pWriteInfo - callback info + \Input *pData - transaction data pointer + \Input iDataSize - size of data + \Input *pUserData - user callback data + + \Output + int32_t - zero + + \Version 05/03/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpMgrWriteCb(ProtoHttpRefT *pProtoHttp, const ProtoHttpWriteCbInfoT *pWriteInfo, const char *pData, int32_t iDataSize, void *pUserData) +{ + HttpStateT *pState = (HttpStateT *)pUserData; +#if 0 + static const char *_strRequestNames[] = + { + "PROTOHTTP_REQUESTTYPE_HEAD", "PROTOHTTP_REQUESTTYPE_GET", "PROTOHTTP_REQUESTTYPE_POST", + "PROTOHTTP_REQUESTTYPE_PUT", "PROTOHTTP_REQUESTTYPE_DELETE", "PROTOHTTP_REQUESTTYPE_OPTIONS" + }; + ZPrintf("httpmgr: writecb (%s,%d) recv=%d\n", _strRequestNames[pWriteInfo->eRequestType], pWriteInfo->eRequestResponse, iDataSize); +#endif + + // detect minbuff error + if (iDataSize == PROTOHTTP_RECVBUFF) + { + // grow the buffer and return + _HttpMgrReallocBuff(&_HttpMgr_Ref, pState); + return(0); + } + + // update count and write to output file if available + pState->count += iDataSize; + if (pState->iOutFile != ZFILE_INVALID) + { + ZFileWrite(pState->iOutFile, (void *)pData, iDataSize); + } + + // update/completion procesing + _HttpMgrDownloadProcessData(&_HttpMgr_Ref, pState, iDataSize); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpMgrUploadProcess + + \Description + Process an upload transaction. + + \Input *pRef - module state + + \Output + None. + + \Version 10/28/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpMgrUploadProcess(HttpMgrRefT *pRef, HttpStateT *pState) +{ + char strResponse[1024]; + int32_t iCode, iResult; + + // read data? + if (pState->iInpFile != ZFILE_INVALID) + { + while (pState->iSentSize < pState->iDataSize) + { + // do we need more data? + if (pState->iSendBufSent == pState->iSendBufData) + { + if ((pState->iSendBufData = ZFileRead(pState->iInpFile, pState->strFileBuffer, sizeof(pState->strFileBuffer))) > 0) + { + ZPrintf("%s: read %d bytes from file\n", pRef->strModuleName, pState->iSendBufData); + pState->iSendBufSent = 0; + } + else + { + ZPrintf("%s: error %d reading from file\n", pRef->strModuleName, pState->iSendBufData); + pState->state = IDLE; + } + } + + // do we have buffered data to send? + if (pState->iSendBufSent < pState->iSendBufData) + { + iResult = HttpManagerSend(pRef->pHttpManager, pState->iHandle, pState->strFileBuffer + pState->iSendBufSent, pState->iSendBufData - pState->iSendBufSent); + if (iResult > 0) + { + pState->iSentSize += iResult; + pState->iSendBufSent += iResult; + ZPrintf("%s: sent %d bytes\n", pRef->strModuleName, iResult); + } + else if (iResult < 0) + { + ZPrintf("%s: HttpManagerSend() failed; error %d\n", pRef->strModuleName, iResult); + pState->state = IDLE; + } + else + { + break; + } + } + } + // check for upload completion + if (pState->iSentSize == pState->iDataSize) + { + // if streaming upload, signal we are done + if (pState->bStreaming == TRUE) + { + HttpManagerSend(pRef->pHttpManager, pState->iHandle, NULL, PROTOHTTP_STREAM_END); + pState->bStreaming = FALSE; + } + + // done uploading + ZPrintf("%s: uploaded %qd bytes\n", pRef->strModuleName, pState->iSentSize); + + // close the file + ZFileClose(pState->iInpFile); + pState->iInpFile = ZFILE_INVALID; + } + } + + // give it time + HttpManagerUpdate(pRef->pHttpManager); + + // see if we've received an HTTP 1xx (INFORMATIONAL) header + iCode = HttpManagerStatus(pRef->pHttpManager, pState->iHandle, 'info', strResponse, sizeof(strResponse)); + if (PROTOHTTP_GetResponseClass(iCode) == PROTOHTTP_RESPONSE_INFORMATIONAL) + { + // got response header, so print it + NetPrintf(("httpmgr: received %d response header\n----------------------------------\n%s----------------------------------\n", iCode, strResponse)); + } + + // done? + if ((iResult = _HttpMgrCheckComplete(pRef->pHttpManager, pState, pRef->strModuleName)) != 0) + { + if (iResult > 0) + { + int32_t ultime = NetTickDiff(NetTick(), pState->sttime); + ZPrintf("%s: upload complete (%qd bytes)\n", pRef->strModuleName, pState->iSentSize); + ZPrintf("upload time: %qd bytes in %.2f seconds (%.3f k/sec)\n", pState->iSentSize, (float)ultime/1000.0f, + ((float)pState->iSentSize * 1000.0f) / ((float)ultime * 1024.0f)); + + ds_memclr(strResponse, sizeof(strResponse)); + iResult = HttpManagerRecv(pRef->pHttpManager, pState->iHandle, strResponse, 1, sizeof(strResponse)); + if (iResult > 0) + { + ZPrintf("http result:\n%s", strResponse); + } + } + + _HttpMgrFreeState(pRef, pState); + } +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttpMgrIdleCB + + \Description + Callback to process while idle + + \Input *argz - + \Input argc - + \Input *argv[] - + + \Output int32_t - + + \Version 09/26/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdHttpMgrIdleCB(ZContext *argz, int32_t argc, char *argv[]) +{ + HttpMgrRefT *pRef = &_HttpMgr_Ref; + int32_t iState; + + // shut down? + if ((argc == 0) || (pRef->pHttpManager == NULL)) + { + if (pRef->pHttpManager != NULL) + { + HttpManagerDestroy(pRef->pHttpManager); + pRef->pHttpManager = NULL; + } + return(0); + } + + // update httpmanager + if (pRef->pHttpManager != NULL) + { + HttpManagerUpdate(pRef->pHttpManager); + } + + // look for active transactions to process + for (iState = 0; iState < HTTP_MAXCMDS; iState += 1) + { + // process a download (if not using write callback) + if ((pRef->States[iState].state == DNLOAD) && (!pRef->bUseWriteCb)) + { + _HttpMgrDownloadProcess(pRef, &pRef->States[iState]); + } + + // process an upload + if (pRef->States[iState].state == UPLOAD) + { + _HttpMgrUploadProcess(pRef, &pRef->States[iState]); + } + } + + // process mget request + if (pRef->pMgetBuffer != NULL) + { + _HttpMgrMget(pRef); + } + + // keep on idling + return(ZCallback(&_CmdHttpMgrIdleCB, HTTP_RATE)); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttpMgrUsage + + \Description + Display usage information. + + \Input argc - argument count + \Input *argv[] - argument list + + \Output + None. + + \Version 02/18/2008 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CmdHttpMgrUsage(int argc, char *argv[]) +{ + if (argc == 2) + { + ZPrintf(" execute http get or put operations\n"); + ZPrintf(" usage: %s [cclr|cert|cer2|cver|create|ctrl|destroy|free|get|mget|put|puts|parse|stat]", argv[0]); + } + else if (argc == 3) + { + if (!strcmp(argv[2], "cclr") || !strcmp(argv[2], "cert") || !strcmp(argv[2], "cer2") || !strcmp(argv[2], "cver")) + { + ZPrintf(" usage: %s cert|cer2 - load certificate file\n", argv[0]); + ZPrintf(" %s cclr - clear dynamic certificates\n"); + ZPrintf(" %s cver - verify dynamic CA certs\n"); + } + else if (!strcmp(argv[2], "create")) + { + ZPrintf(" usage: %s create \n", argv[0]); + } + else if (!strcmp(argv[2], "destroy")) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + } + else if (!strcmp(argv[2], "free")) + { + ZPrintf(" usage: %s get \n", argv[0]); + } + else if (!strcmp(argv[2], "get")) + { + ZPrintf(" usage: %s get [url] \n", argv[0]); + } + else if (!strcmp(argv[2], "mget")) + { + ZPrintf(" usage: %s mget \n", argv[0]); + } + else if (!strcmp(argv[2], "put")) + { + ZPrintf(" usage: %s put [url] [file]\n", argv[0]); + } + else if (!strcmp(argv[2], "puts")) + { + ZPrintf(" usage: %s puts [url] [file]\n", argv[0]); + } + else if (!strcmp(argv[2], "parse")) + { + ZPrintf(" usage: %s parse [url]\n", argv[0]); + } + } +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdHttpMgr + + \Description + Initiate an HTTP transaction. + + \Input *argz - + \Input argc - + \Input *argv[] - + + \Output int32_t - + + \Version 10/28/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdHttpMgr(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iResult = 0, iBufSize = HTTP_BUFSIZE, iNumRefs = 4; + const char *pFileName = NULL, *pUrl; + HttpMgrRefT *pRef = &_HttpMgr_Ref; + HttpStateT *pState = NULL; + const char *pFileData; + int32_t iArg, iStartArg = 2; // first arg past get/put/delete/whatever + int32_t iFileSize; + + if (argc < 2) + { + return(0); + } + + // check for help + if ((argc >= 2) && !strcmp(argv[1], "help")) + { + _CmdHttpMgrUsage(argc, argv); + return(iResult); + } + + // check for 'parse' command + if ((argc == 3) && !strcmp(argv[1], "parse")) + { + char strKind[5], strHost[128]; + const char *pUri; + int32_t iPort, iSecure; + ds_memclr(strKind, sizeof(strKind)); + ds_memclr(strHost, sizeof(strHost)); + pUri = ProtoHttpUrlParse(argv[2], strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure); + ZPrintf("parsed url: kind=%s, host=%s, port=%d, secure=%d, uri=%s\n", strKind, strHost, iPort, iSecure, pUri); + return(0); + } + + // if not initialized yet, do so now + if (_HttpMgr_bInitialized == FALSE) + { + ds_memclr(pRef, sizeof(*pRef)); + _HttpMgr_bInitialized = TRUE; + } + + // check for explicit destroy + if ((argc == 2) && !ds_stricmp(argv[1], "destroy")) + { + if (pRef->pHttpManager != NULL) + { + HttpManagerDestroy(pRef->pHttpManager); + pRef->pHttpManager = NULL; + } + return(iResult); + } + + // check for request to set a certificate + if ((argc == 3) && ((!strcmp(argv[1], "cert")) || (!strcmp(argv[1], "cer2")))) + { + const uint8_t *pCertData; + int32_t iCertSize; + + // try and open file + if ((pCertData = (const uint8_t *)ZFileLoad(argv[2], &iCertSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != NULL) + { + if (!strcmp(argv[1], "cert")) + { + iResult = ProtoHttpSetCACert(pCertData, iCertSize); + } + else + { + iResult = ProtoHttpSetCACert2(pCertData, iCertSize); + } + ZMemFree((void *)pCertData); + } + else + { + ZPrintf("%s: unable to load certificate file '%s'\n", argv[0], argv[2]); + } + return((iResult > 0) ? 0 : -1); + } + else if (!strcmp(argv[1], "cclr")) + { + ZPrintf("%s: clearing dynamic certs\n", argv[0]); + ProtoHttpClrCACerts(); + return(0); + } + else if (!strcmp(argv[1], "cver")) + { + int32_t iInvalid; + ZPrintf("%s: verifying dynamic CA certs\n", argv[0]); + if ((iInvalid = ProtoHttpValidateAllCA()) > 0) + { + ZPrintf("%s: could not verify %d CA certs\n", iInvalid); + iResult = -1; + } + return(iResult); + } + + // check for create request + if ((argc >= 2) && !strcmp(argv[1], "create")) + { + if (argc >= 3) + { + iBufSize = (int32_t)strtol(argv[2], NULL, 10); + } + if (argc >= 4) + { + iNumRefs = (int32_t)strtol(argv[3], NULL, 10); + } + } + + // create httpmanager module if necessary + if (pRef->pHttpManager == NULL) + { + ZPrintf("%s: creating module with a %d refs and %dkbyte buffer\n", argv[0], iNumRefs, iBufSize); + ds_memclr(pRef, sizeof(*pRef)); + ds_strnzcpy(pRef->strModuleName, argv[0], sizeof(pRef->strModuleName)); + pRef->pHttpManager = HttpManagerCreate(iBufSize, iNumRefs); + if (pRef->pHttpManager != NULL) + { + HttpManagerCallback(pRef->pHttpManager, _HttpCustomHeaderCallback, _HttpRecvHeaderCallback); + pRef->iDebugLevel = 1; + HttpManagerControl(pRef->pHttpManager, -1, 'spam', pRef->iDebugLevel, 0, NULL); + } + } + + // check for create request -- if so, we're done + if ((argc >= 2) && !strcmp(argv[1], "create")) + { + return(iResult); + } + else if ((argc > 2) && (argc < 6) && !strcmp(argv[1], "ctrl")) + { + int32_t iCmd, iValue = 0, iValue2 = 0; + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + if (argc > 3) + { + iValue = (int32_t)strtol(argv[3], NULL, 10); + } + + if (argc > 4) + { + iValue2 = (int32_t)strtol(argv[4], NULL, 10); + } + + // snoop 'spam' + if (iCmd == 'spam') + { + pRef->iDebugLevel = iValue; + } + + return(HttpManagerControl(pRef->pHttpManager, /*iHandle*/ -1, iCmd, iValue, iValue2, NULL)); + } + else if ((argc == 3) && !strcmp(argv[1], "stat")) + { + int32_t iCmd; + char strBufferTemp[1024] = ""; + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + iResult = HttpManagerStatus(pRef->pHttpManager, /*iHandle*/ -1, iCmd, strBufferTemp, sizeof(strBufferTemp)); + ZPrintf("%s: ProtoHttpStatus('%s') returned %d\n", argv[0], argv[2], iResult); + if (strBufferTemp[0] != '\0') + { + ZPrintf("%s\n", strBufferTemp); + } + return(0); + } + // check for setting of base url + else if ((argc == 3) && !strcmp(argv[1], "base")) + { + HttpManagerSetBaseUrl(pRef->pHttpManager, /*iHandle*/ -1, argv[2]); + return(iResult); + } + // check for valid get/put request + else if ((!ds_stricmp(argv[1], "get") || !ds_stricmp(argv[1], "head") || !ds_stricmp(argv[1], "put") || !ds_stricmp(argv[1], "post") || + !ds_stricmp(argv[1], "puts") || !ds_stricmp(argv[1], "delete") || !ds_stricmp(argv[1], "options")) && + ((argc > 2) || (argc < 5))) + { + // allocate a new state record + pState = _HttpMgrAllocState(pRef); + if (pState == NULL) + { + // if we could not allocate state, return error so upper layer can deal with it + return(-1); + } + + // fall-through to code below + } + else if (!ds_stricmp(argv[1], "mget") || !ds_stricmp(argv[1], "free")) + { + // do nothing, fall through + } + else + { + ZPrintf(" unrecognized or badly formatted command '%s'\n", argv[1]); + _CmdHttpMgrUsage(argc, argv); + return(-1); + } + + // clear previous options + pRef->bUseWriteCb = FALSE; + pRef->bRecvAll = FALSE; + + // set up append header + ds_strnzcpy(pRef->strApndHdr, HTTP_APNDHDR, sizeof(pRef->strApndHdr)); + + // check for args + for (iArg = 2; (iArg < argc) && (argv[iArg][0] == '-'); iArg += 1) + { + if (!ds_strnicmp(argv[iArg], "-header=", 8)) + { + ds_strnzcat(pRef->strApndHdr, argv[iArg]+8, sizeof(pRef->strApndHdr)); + ds_strnzcat(pRef->strApndHdr, "\r\n", sizeof(pRef->strApndHdr)); + } + if (!ds_strnicmp(argv[iArg], "-writecb", 8)) + { + pRef->bUseWriteCb = TRUE; + } + if (!ds_strnicmp(argv[iArg], "-recvall", 8)) + { + pRef->bRecvAll = TRUE; + } + // skip any option arguments to find url and (optionally) filename + iStartArg += 1; + } + + // locate url and filename + pUrl = argv[iStartArg]; + if (argc > (iStartArg+1)) + { + pFileName = argv[iStartArg+1]; + } + + if (!pUrl) + { + ZPrintf("%s: no url specified\n", argv[0]); + return(-1); + } + + // set append header + if ((pState != NULL) || (!ds_stricmp(argv[1], "mget"))) + { + char strBuffer[1024] = "\0"; + int32_t iHandle = (pState != NULL) ? pState->iHandle : -1; + //if (pRef->strCookie[0] != '\0') + //{ + // ds_snzprintf(strBuffer, sizeof(strBuffer), "Cookie: %s\r\n", pRef->strCookie); + //} + ds_strnzcat(strBuffer, pRef->strApndHdr, sizeof(strBuffer)); + HttpManagerControl(pRef->pHttpManager, iHandle, 'apnd', 0, 0, strBuffer); + } + + // see if we're uploading or downloading + if (!ds_stricmp(argv[1], "put") || !ds_stricmp(argv[1], "post") || !ds_stricmp(argv[1], "puts")) + { + ZPrintf("%s: uploading %s to %s\n", argv[0], pFileName, pUrl); + + // assume failure + iResult = -1; + + // try and open file + if ((pState->iInpFile = ZFileOpen(pFileName, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != ZFILE_INVALID) + { + // get the file size + if ((pState->iDataSize = ZFileSize(pState->iInpFile)) > 0) + { + // load data from file + if ((pState->iSendBufData = ZFileRead(pState->iInpFile, pState->strFileBuffer, sizeof(pState->strFileBuffer))) > 0) + { + if (ds_stricmp(argv[1], "puts")) + { + // initiate put/post transaction + ZPrintf("%s: uploading %qd bytes\n", argv[0], pState->iDataSize); + if ((pState->iSendBufSent = HttpManagerPost(pRef->pHttpManager, pState->iHandle, pUrl, pState->strFileBuffer, pState->iDataSize, !ds_stricmp(argv[1], "put") ? PROTOHTTP_PUT : PROTOHTTP_POST)) < 0) + { + ZPrintf("%s: error %d initiating send\n", pState->iSendBufSent); + iResult = -1; + } + else if (pState->iSendBufSent > 0) + { + ZPrintf("%s: sent %d bytes\n", argv[0], pState->iSendBufSent); + pState->iSentSize = pState->iSendBufSent; + } + } + else + { + // initiate streaming put + if ((iResult = HttpManagerPost(pRef->pHttpManager, pState->iHandle, pUrl, NULL, PROTOHTTP_STREAM_BEGIN, PROTOHTTP_PUT)) == 0) + { + pState->bStreaming = TRUE; + } + } + + // wait for reply + pState->state = UPLOAD; + iResult = 0; + } + else + { + ZPrintf("%s: error %d reading data from file\n", argv[0], pState->iSendBufData, pFileName); + } + } + else + { + ZPrintf("%s: error %d getting size of file %s\n", argv[0], pState->iDataSize, pFileName); + } + } + else + { + ZPrintf("%s: unable to load file '%s'\n", argv[0], pFileName); + } + } + else if (!ds_stricmp(argv[1], "head") || !ds_stricmp(argv[1], "get")) + { + ProtoHttpRequestTypeE eRequestType = !ds_stricmp(argv[1], "head") ? PROTOHTTP_REQUESTTYPE_HEAD : PROTOHTTP_REQUESTTYPE_GET; + + if (pFileName != NULL) + { + if (pRef->iDebugLevel > 1) + { + pState->iOutFile = ZFileOpen(pFileName, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_BINARY|ZFILE_OPENFLAG_CREATE); + } + } + else if (pRef->iDebugLevel > 1) + { + ZPrintf("%s: downloading %s\n", argv[0], pUrl); + } + + // initiate transaction + if (pRef->bUseWriteCb) + { + iResult = HttpManagerRequestCb(pRef->pHttpManager, pState->iHandle, pUrl, NULL, 0, eRequestType, _HttpMgrWriteCb, pState); + } + else + { + iResult = HttpManagerRequest(pRef->pHttpManager, pState->iHandle, pUrl, NULL, 0, eRequestType); + } + if (iResult == 0) + { + pState->state = DNLOAD; + } + } + else if (!ds_stricmp(argv[1], "mget") && (pRef->pMgetBuffer == NULL)) + { + if ((pFileData = ZFileLoad(argv[iStartArg], &iFileSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != NULL) + { + // set up for mget process + pRef->pMgetBuffer = pRef->pMgetOffset = pFileData; + pRef->uMgetStart = NetTick(); + ds_strnzcpy(pRef->strMgetFilename, argv[iStartArg], sizeof(pRef->strMgetFilename)); + } + } + else if (!ds_stricmp(argv[1], "delete")) + { + if ((iResult = HttpManagerRequest(pRef->pHttpManager, pState->iHandle, pUrl, NULL, 0, PROTOHTTP_REQUESTTYPE_DELETE)) == 0) + { + pState->state = DNLOAD; + } + } + else if (!ds_stricmp(argv[1], "options")) + { + if ((iResult = HttpManagerRequest(pRef->pHttpManager, pState->iHandle, pUrl, NULL, 0, PROTOHTTP_REQUESTTYPE_DELETE)) == 0) + { + pState->state = DNLOAD; + } + } + else if (!ds_stricmp(argv[1], "free")) + { + int32_t iHandle = atoi(argv[2]); + ZPrintf("HttpManagerFree %d\n", iHandle); + HttpManagerFree(pRef->pHttpManager, iHandle); + } + else + { + ZPrintf("%s: unrecognized request %s\n", argv[0], argv[1]); + iResult = -1; + } + + // set up recurring callback to process transaction, if any + if (((pState != NULL) && (pState->state != IDLE) && (pRef->pMgetBuffer == NULL)) || ((pState == NULL) && (pRef->pMgetBuffer != NULL))) + { + iResult = ZCallback(_CmdHttpMgrIdleCB, HTTP_RATE); + } + return(iResult); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/httpserv.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/httpserv.c new file mode 100644 index 00000000..3ef23eeb --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/httpserv.c @@ -0,0 +1,904 @@ +/*H********************************************************************************/ +/*! + \File httpserv.c + + \Description + Implements basic http server using ProtoHttpServ + + \Copyright + Copyright (c) 2012 Electronic Arts Inc. + + \Version 09/11/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include // gettimeofday +#endif + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/dirtysock/netconn.h" // NetConnSleep +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protohttpserv.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +#define HTTPSERV_RATE (1) +#define HTTPSERV_LISTENPORT (9000) + +/*** Function Prototypes **********************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct HttpServT +{ + ProtoHttpServRefT *pHttpServ; + + char *pServerCert; + int32_t iServerCertLen; + char *pServerKey; + int32_t iServerKeyLen; + int32_t iChunkLen; + + char strServerName[128]; + char strFileDir[512]; +} HttpServT; + +/*** Variables ********************************************************************/ + +static HttpServT _HttpServ_Ref; +static uint8_t _HttpServ_bInitialized = FALSE; + +//! map of filename extensions to content-types +static const char *_ProtoHttpServ_strContentTypes[][2] = +{ + { ".htm", "text/html" }, + { ".html", "text/html" }, + { ".css", "text/css" }, + { ".xml", "text/xml" }, + { ".jpg", "image/jpeg" }, + { ".gif", "image/gif" }, + { ".png", "image/png" }, + { ".mp3", "audio/mpeg" } +}; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _GetIntArg + + \Description + Get fourcc/integer from command-line argument + + \Input *pArg - pointer to argument + + \Version 10/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _GetIntArg(const char *pArg) +{ + int32_t iValue; + + // check for possible fourcc value + if ((strlen(pArg) == 4) && (isalpha(pArg[0]) || isalpha(pArg[1]) || isalpha(pArg[2]) || isalpha(pArg[3]))) + { + iValue = pArg[0] << 24; + iValue |= pArg[1] << 16; + iValue |= pArg[2] << 8; + iValue |= pArg[3]; + } + else + { + iValue = (signed)strtol(pArg, NULL, 10); + } + return(iValue); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServGetCurrentTime + + \Description + Gets and parses the current time into components + + \Input *uYear - pointer to uint32_t var to store 'year' in + \Input *uMonth - pointer to uint8_t var to store 'month' in + \Input *uDay - pointer to uint8_t var to store 'day' in + \Input *uHour - pointer to uint8_t var to store 'hour' in + \Input *uMin - pointer to uint8_t var to store 'min' in + \Input *uSec - pointer to uint8_t var to store 'sec' in + \Input *uMillis - pointer to uint32_t var to store 'milliseconds' in + + \Version 10/30/2013 (jbrookes) Borrowed from eafn logger +*/ +/********************************************************************************F*/ +static void _HttpServGetCurrentTime(uint32_t *uYear, uint8_t *uMonth, uint8_t *uDay, uint8_t *uHour, uint8_t *uMin, uint8_t *uSec, uint32_t *uMillis) +{ +#if DIRTYCODE_PC + SYSTEMTIME SystemTime; + GetLocalTime(&SystemTime); + *uYear = SystemTime.wYear; + *uMonth = SystemTime.wMonth; + *uDay = SystemTime.wDay; + *uHour = SystemTime.wHour; + *uMin = SystemTime.wMinute; + *uSec = SystemTime.wSecond; + *uMillis = SystemTime.wMilliseconds; +#else // all non-pc +#if DIRTYCODE_LINUX + struct timeval tv; + struct tm *pTime; + gettimeofday(&tv, NULL); + pTime = gmtime((time_t *)&tv.tv_sec); + *uMillis = tv.tv_usec / 1000; +#else + //$$TODO: plat-time doesn't have anything to get millis + struct tm TmTime, *pTime; + pTime = ds_secstotime(&TmTime, ds_timeinsecs()); + *uMillis = 0; +#endif + *uYear = 1900 + pTime->tm_year; + *uMonth = pTime->tm_mon + 1; + *uDay = pTime->tm_mday; + *uHour = pTime->tm_hour; + *uMin = pTime->tm_min; + *uSec = pTime->tm_sec; +#endif // !DIRTYCODE_PC +} + +/*F********************************************************************************/ +/*! + \Function _HttpServGetContentType + + \Description + Get content-type based on target url + + \Input *strUrl - full url including file + + \Output + const char * - content type + + \Version 07/09/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_HttpServGetContentType(const char *strUrl) +{ + const char *pContentType = _ProtoHttpServ_strContentTypes[0][1]; + int32_t iType; + + for (iType = 0; iType < (signed)(sizeof(_ProtoHttpServ_strContentTypes)/sizeof(_ProtoHttpServ_strContentTypes[0])); iType += 1) + { + if (ds_stristr(strUrl, _ProtoHttpServ_strContentTypes[iType][0])) + { + pContentType = _ProtoHttpServ_strContentTypes[iType][1]; + break; + } + } + + return(pContentType); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServLoadCertificate + + \Description + Load pem certificate file and trim begin/end text. + + \Input *pFilename - name of certificate file to open + \Input *pCertSize - [out] storage for size of certificate + + \Output + const char * - certificate data + + \Version 10/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_HttpServLoadCertificate(const char *pFilename, int32_t *pCertSize) +{ + char *pCertBuf; + int32_t iFileSize; + + // load certificate file + if ((pCertBuf = (char *)ZFileLoad(pFilename, &iFileSize, ZFILE_OPENFLAG_RDONLY)) == NULL) + { + return(NULL); + } + + // set size and return buffer to caller + *pCertSize = iFileSize; + return(pCertBuf); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServProcessGet + + \Description + Process GET/HEAD request + + \Input *pServerState - module state + \Input *pRequest - request information + \Input *pResponse - [out] response information + + \Output + int32_t - response code + + \Version 12/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpServProcessGet(HttpServT *pServerState, const ProtoHttpServRequestT *pRequest, ProtoHttpServResponseT *pResponse) +{ + const char* pHdr = pRequest->strHeader; + char strFilePath[4096], strName[128], strValue[4*1024]; + ZFileStatT FileStat; + int32_t iFileLen, iResult; + ZFileT iFileId; + uint32_t uModifiedSince = 0, uUnmodifiedSince = 0; + struct tm TmTime; + char strTime[64]; + + // if empty url, substitute default + if (*(pRequest->strUrl+1) == '\0') + { + ds_snzprintf(strFilePath, sizeof(strFilePath), "%s/index.html", pServerState->strFileDir, pRequest->strUrl + 1); + } + else + { + ds_snzprintf(strFilePath, sizeof(strFilePath), "%s/%s", pServerState->strFileDir, pRequest->strUrl + 1); + } + + // stat the file + if ((iResult = ZFileStat(strFilePath, &FileStat)) != ZFILE_ERROR_NONE) + { + // no file + ZPrintf("httpserv: could not stat file '%s'\n", strFilePath); + pResponse->eResponseCode = PROTOHTTP_RESPONSE_NOTFOUND; + return(-1); + } + + // see if url refers to a file + if ((iFileId = ZFileOpen(strFilePath, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) == ZFILE_INVALID) + { + ZPrintf("httpserv: could not open file '%s' for reading\n", strFilePath); + pResponse->eResponseCode = PROTOHTTP_RESPONSE_INTERNALSERVERERROR; + return(-2); + } + // get the file size + if ((iFileLen = (int32_t)ZFileSize(iFileId)) < 0) + { + ZPrintf("httpserv: error %d getting file size\n", iFileLen); + pResponse->eResponseCode = PROTOHTTP_RESPONSE_INTERNALSERVERERROR; + return(-3); + } + + // parse the header for request specific data + while (ProtoHttpGetNextHeader(NULL, pHdr, strName, sizeof(strName), strValue, sizeof(strValue), &pHdr) == 0) + { + if (ds_stricmp(strName, "if-modified-since") == 0) + { + uModifiedSince = (uint32_t)ds_strtotime(strValue); + } + else if (ds_stricmp(strName, "if-unmodified-since") == 0) + { + uUnmodifiedSince = (uint32_t)ds_strtotime(strValue); + } + } + + if (uModifiedSince != 0 && ((int32_t)(FileStat.uTimeModify-uModifiedSince) <= 0)) + { + pResponse->eResponseCode = PROTOHTTP_RESPONSE_NOTMODIFIED; + ZPrintf("httpserv: file not modified (%d-%d=%d)\n", uModifiedSince, FileStat.uTimeModify, + (int32_t)(FileStat.uTimeModify-uModifiedSince)); + return(0); + } + if (uUnmodifiedSince != 0 && ((int32_t)(FileStat.uTimeModify-uUnmodifiedSince) > 0)) + { + ZPrintf("httpserv: file modified since (%d-%d=%d)\n", uUnmodifiedSince, FileStat.uTimeModify, + (int32_t)(FileStat.uTimeModify-uUnmodifiedSince)); + pResponse->eResponseCode = PROTOHTTP_RESPONSE_PRECONFAILED; + return(-4); + } + + // set last modified time + ds_secstotime(&TmTime, FileStat.uTimeModify); + pResponse->iHeaderLen += ds_snzprintf(pResponse->strHeader, sizeof(pResponse->strHeader)-pResponse->iHeaderLen, "Last-Modified: %s\r\n", + ds_timetostr(&TmTime, TIMETOSTRING_CONVERSION_RFC_0822, FALSE, strTime, sizeof(strTime))); + + // set content-type + ds_strnzcpy(pResponse->strContentType, _HttpServGetContentType(pRequest->strUrl), sizeof(pResponse->strContentType)); + + // set request info + pResponse->iContentLength = iFileLen; + pResponse->iChunkLength = pServerState->iChunkLen; + pResponse->pData = (void *)iFileId; + pResponse->eResponseCode = PROTOHTTP_RESPONSE_OK; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServProcessPut + + \Description + Process PUT/POST request + + \Input *pServerState - module state + \Input *pRequest - request information + \Input *pResponse - [out] response information + + \Output + int32_t - response code + + \Version 12/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpServProcessPut(HttpServT *pServerState, const ProtoHttpServRequestT *pRequest, ProtoHttpServResponseT *pResponse) +{ + ZFileT iFileId = (ZFileT)(uintptr_t)pRequest->pData; + if (iFileId == 0 || iFileId == ZFILE_INVALID) + { + pResponse->eResponseCode = PROTOHTTP_RESPONSE_INTERNALSERVERERROR; + return(-1); + } + + // we've processed the request + pResponse->pData = (void *)(uintptr_t)iFileId; + pResponse->eResponseCode = PROTOHTTP_RESPONSE_CREATED; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServRequestCb + + \Description + ProtoHttpServ request callback handler + + \Input *pRequest - request information + \Input *pResponse - [out] response information + \Input *pUserData - callback user data + + \Output + int32_t - response code + + \Version 12/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpServRequestCb(ProtoHttpServRequestT *pRequest, ProtoHttpServResponseT *pResponse, void *pUserData) +{ + HttpServT *pServerState = (HttpServT *)pUserData; + int32_t iResult = -1; + + // init default response values + pResponse->eResponseCode = PROTOHTTP_RESPONSE_NOTIMPLEMENTED; + + // handle the request + if ((pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_GET) || (pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_HEAD)) + { + iResult = _HttpServProcessGet(pServerState, pRequest, pResponse); + } + if ((pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_PUT) || (pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_PATCH) || (pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_POST)) + { + iResult = _HttpServProcessPut(pServerState, pRequest, pResponse); + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServReceiveCb + + \Description + ProtoHttpServ inbound data callback handler + + \Input *pServerState- module state + \Input *pBuffer - data to write + \Input iBufSize - size of the data + \Input *pUserData - user data + + \Output + int32_t - negative=failure, else bytes written + + \Version 12/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpServReceiveCb(ProtoHttpServRequestT *pRequest, const char *pBuffer, int32_t iBufSize, void *pUserData) +{ + ZFileT iFileId = (ZFileT)(uintptr_t)pRequest->pData; + int32_t iResult = 0; + + // if the file failed to load previously or + // not the correct request type + // then early out + if (iFileId == ZFILE_INVALID) + { + return(-1); + } + if (pRequest->eRequestType != PROTOHTTP_REQUESTTYPE_POST && pRequest->eRequestType != PROTOHTTP_REQUESTTYPE_PUT && pRequest->eRequestType != PROTOHTTP_REQUESTTYPE_PATCH) + { + return(iBufSize); + } + + // check for upload completion + if (pBuffer == NULL) + { + ZFileClose(iFileId); + } + else if ((iResult = ZFileWrite(iFileId, (void *)pBuffer, iBufSize)) < 0) + { + NetPrintf(("httpserv: error %d writing to file\n", iResult)); + } + // return result + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServHeaderCb + + \Description + ProtoHttpServ inbound data callback handler + + \Input *pRequest - [in/out] data used to process the request + \Input *pResponse - [out] data used to send the response + \Input *pUserData - user data + + \Output + int32_t - negative=failure, zero=success +*/ +/********************************************************************************F*/ +static int32_t _HttpServHeaderCb(ProtoHttpServRequestT *pRequest, ProtoHttpServResponseT *pResponse, void *pUserData) +{ + HttpServT *pServerState = (HttpServT *)pUserData; + char strFilePath[1024]; + ZFileT iFileId = ZFILE_INVALID; + + if (pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_POST || + pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_PUT || + pRequest->eRequestType == PROTOHTTP_REQUESTTYPE_PATCH) + { + // create filepath + ds_snzprintf(strFilePath, sizeof(strFilePath), "%s\\%s", pServerState->strFileDir, pRequest->strUrl + 1); + + // try to open the file + if ((iFileId = ZFileOpen(strFilePath, ZFILE_OPENFLAG_WRONLY | ZFILE_OPENFLAG_BINARY)) == ZFILE_INVALID) + { + ZPrintf("httpserv: could not open file '%s' for writing\n", strFilePath); + pResponse->eResponseCode = PROTOHTTP_RESPONSE_INTERNALSERVERERROR; + return(-1); + } + } + + // set the file id for use during receiving/sending + pRequest->pData = (void *)(uintptr_t)iFileId; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServSendCb + + \Description + ProtoHttpServ outbound callback handler + + \Input *pServerState - module state + \Input *pBuffer - data to write to + \Input iBufSize - size of the data + \Input *pUserData - user data + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 12/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpServSendCb(ProtoHttpServResponseT *pResponse, char *pBuffer, int32_t iBufSize, void *pUserData) +{ + //HttpServT *pServerState = (HttpServT *)pUserData; + ZFileT iFileId = (ZFileT)(uintptr_t)pResponse->pData; + int32_t iResult = 0; + + if (iFileId == ZFILE_INVALID) + { + return(0); + } + + // check for download completion + if (pBuffer == NULL) + { + ZFileClose(iFileId); + } + else if ((iResult = ZFileRead(iFileId, pBuffer, iBufSize)) < 0) + { + ZPrintf("httpserv: error %d reading from file\n", iResult); + } + // return result + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _HttpServLogCb + + \Description + ProtoHttpServ logging function + + \Input *pText - text to print + \Input *pUserData - user data (module state) + + \Output + int32_t - zero + + \Version 12/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpServLogCb(const char *pText, void *pUserData) +{ + uint32_t uYear, uMillis; + uint8_t uMonth, uDay, uHour, uMin, uSec; + + // format prefix to output buffer + _HttpServGetCurrentTime(&uYear, &uMonth, &uDay, &uHour, &uMin, &uSec, &uMillis); + + ZPrintf("%02u/%02u/%02u %02u:%02u:%02u.%03u %s", uYear, uMonth, uDay, uHour, uMin, uSec, uMillis, pText); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttpServIdleCB + + \Description + Callback to process while idle + + \Input *argz - + \Input argc - + \Input *argv[] - + + \Output int32_t - + + \Version 09/26/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdHttpServIdleCB(ZContext *argz, int32_t argc, char *argv[]) +{ + HttpServT *pRef = &_HttpServ_Ref; + + // shut down? + if (argc == 0) + { + if (pRef->pHttpServ != NULL) + { + ProtoHttpServDestroy(pRef->pHttpServ); + pRef->pHttpServ = NULL; + } + return(0); + } + // httpserv destroyed? + if (pRef->pHttpServ == NULL) + { + return(0); + } + + // update protohttpserv module + ProtoHttpServUpdate(pRef->pHttpServ); + + // keep on idling + return(ZCallback(&_CmdHttpServIdleCB, HTTPSERV_RATE)); +} + +/*F********************************************************************************/ +/*! + \Function _CmdHttpServUsage + + \Description + Display usage information. + + \Input argc - argument count + \Input *argv[] - argument list + + \Version 09/13/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CmdHttpServUsage(int argc, char *argv[]) +{ + if (argc <= 2) + { + ZPrintf("httpserv: basic http server\n"); + ZPrintf("httpserv: usage: %s [alpn|cert|chunked|ciph|filedir|listen|setcert|setkey|stop|vers|vmin]", argv[0]); + } + else if (argc == 3) + { + if (!strcmp(argv[2], "alpn")) + { + ZPrintf("httpserv: usage: %s alpn [alpnstr]\n", argv[0]); + } + if (!strcmp(argv[2], "ccrt")) + { + ZPrintf("httpserv: usage: %s ccrt [level]\n", argv[0]); + } + else if (!strcmp(argv[2], "cert")) + { + ZPrintf("httpserv: usage: %s cert [cacertfile]\n", argv[0]); + } + else if (!strcmp(argv[2], "chunked")) + { + ZPrintf("httpserv: usage: %s chunked [chunklen]\n", argv[0]); + } + else if (!strcmp(argv[2], "ciph")) + { + ZPrintf("httpserv: usage: %s ciph [ciphers]\n", argv[0]); + } + else if (!strcmp(argv[2], "filedir")) + { + ZPrintf("httpserv: usage: %s filedir \n", argv[0]); + } + else if (!strcmp(argv[2], "listen")) + { + ZPrintf("httpserv: usage: %s listen [listenport] \n", argv[0]); + } + else if (!strcmp(argv[2], "setcert")) + { + ZPrintf("httpserv: usage: %s setcert [certfile]\n", argv[0]); + } + else if (!strcmp(argv[2], "setkey")) + { + ZPrintf("httpserv: usage: %s setkey [certkey]\n", argv[0]); + } + else if (!strcmp(argv[2], "stop")) + { + ZPrintf("httpserv: usage: %s stop\n", argv[0]); + } + else if (!strcmp(argv[2], "vers")) + { + ZPrintf("httpserv: usage: %s vers [version]\n", argv[0]); + } + else if (!strcmp(argv[2], "vmin")) + { + ZPrintf("httpserv: usage: %s vmin [version]\n", argv[0]); + } + } +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdHttpServ + + \Description + Simple HTTP server + + \Input *argz - context + \Input argc - command count + \Input *argv[] - command arguments + + \Output int32_t - + + \Version 09/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdHttpServ(ZContext *argz, int32_t argc, char *argv[]) +{ + HttpServT *pServerState = &_HttpServ_Ref; + int32_t iResult = 0; + + if ((argc < 2) || !ds_stricmp(argv[1], "help")) + { + _CmdHttpServUsage(argc, argv); + return(iResult); + } + + // if not initialized yet, do so now + if (_HttpServ_bInitialized == FALSE) + { + ds_memclr(pServerState, sizeof(*pServerState)); + ds_strnzcpy(pServerState->strServerName, "HttpServ", sizeof(pServerState->strServerName)); + ds_strnzcpy(pServerState->strFileDir, "c:\\temp\\httpserv", sizeof(pServerState->strFileDir)); + _HttpServ_bInitialized = TRUE; + } + + // check for filedir set + if ((argc == 3) && !ds_stricmp(argv[1], "filedir")) + { + ds_strnzcpy(pServerState->strFileDir, argv[2], sizeof(pServerState->strFileDir)); + return(iResult); + } + + // check for listen + if ((argc >= 2) && (argc < 5) && !ds_stricmp(argv[1], "listen")) + { + int32_t iPort = HTTPSERV_LISTENPORT; + int32_t iSecure = 0; // insecure by default + + if ((argc > 3) && !ds_stricmp(argv[3], "secure")) + { + iSecure = 1; + } + if (argc > 2) + { + iPort = (int32_t)strtol(argv[2], NULL, 10); + if ((iPort < 1) || (iPort > 65535)) + { + ZPrintf("httpserv: invalid port %d specified in listen request\n", iPort); + return(-1); + } + } + + // destroy previous httpserv ref, if any + if (pServerState->pHttpServ != NULL) + { + ProtoHttpServDestroy(pServerState->pHttpServ); + } + + // create new httpserv ref + if ((pServerState->pHttpServ = ProtoHttpServCreate(iPort, iSecure, pServerState->strServerName)) == NULL) + { + ZPrintf("httpserv: could not create httpserv state on port %d\n", iPort); + return(-2); + } + + // set up httpserv callbacks + ProtoHttpServCallback(pServerState->pHttpServ, _HttpServRequestCb, _HttpServReceiveCb, _HttpServSendCb, _HttpServHeaderCb, _HttpServLogCb, pServerState); + + // install recurring update + iResult = ZCallback(_CmdHttpServIdleCB, HTTPSERV_RATE); + } + + // the following functions require http serv state + if (pServerState->pHttpServ == NULL) + { + ZPrintf("%s: '%s' requires httpserv creation (use 'listen' command)\n", argv[0], argv[1]); + return(iResult); + } + + // check for server stop + if ((argc == 2) && !ds_stricmp(argv[1], "stop")) + { + ProtoHttpServDestroy(pServerState->pHttpServ); + pServerState->pHttpServ = NULL; + + ZPrintf("httpserv: protohttpserv stopped listening\n"); + } + + // check for setting alpn + if ((argc == 3) && !ds_stricmp(argv[1], "alpn")) + { + return(ProtoHttpServControl(pServerState->pHttpServ, 'alpn', 0, 0, argv[2])); + } + + // check for client cert level specification + if ((argc == 3) && !ds_stricmp(argv[1], "ccrt")) + { + return(ProtoHttpServControl(pServerState->pHttpServ, 'ccrt', (int32_t)strtol(argv[2], NULL, 10), 0, NULL)); + } + + // check for cacert load + if ((argc == 3) && !ds_stricmp(argv[1], "cert")) + { + const uint8_t *pFileData; + int32_t iFileSize; + + // try and open file + if ((pFileData = (const uint8_t *)ZFileLoad(argv[2], &iFileSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != NULL) + { + iResult = ProtoSSLSetCACert(pFileData, iFileSize); + ZMemFree((void *)pFileData); + } + else + { + ZPrintf("%s: unable to load certificate file '%s'\n", argv[0], argv[2]); + } + return((iResult > 0) ? 0 : -1); + } + + // check for chunked transfer (download) enable + if ((argc >= 2) && !ds_stricmp(argv[1], "chunked")) + { + int32_t iChunkLen = 4096; + if (argc > 2) + { + iChunkLen = (int32_t)strtol(argv[2], NULL, 10); + } + ZPrintf("httpserv: enabling chunked transfers and setting chunk size to %d\n", iChunkLen); + pServerState->iChunkLen = iChunkLen; + return(iResult); + } + + // check for cipher set + if ((argc == 3) && !ds_stricmp(argv[1], "ciph")) + { + return(ProtoHttpServControl(pServerState->pHttpServ, 'ciph', (int32_t)strtol(argv[2], NULL, 16), 0, NULL)); + } + + // check for server certificate + if ((argc == 3) && !ds_stricmp(argv[1], "setcert")) + { + if ((pServerState->pServerCert = _HttpServLoadCertificate(argv[2], &pServerState->iServerCertLen)) != NULL) + { + iResult = ProtoHttpServControl(pServerState->pHttpServ, 'scrt', pServerState->iServerCertLen, 0, pServerState->pServerCert); + } + else + { + ZPrintf("%s: could not load certificate '%s'\n", argv[0], argv[2]); + iResult = -1; + } + return(iResult); + } + + // check for server private key + if ((argc == 3) && !ds_stricmp(argv[1], "setkey")) + { + if ((pServerState->pServerKey = _HttpServLoadCertificate(argv[2], &pServerState->iServerKeyLen)) != NULL) + { + iResult = ProtoHttpServControl(pServerState->pHttpServ, 'skey', pServerState->iServerKeyLen, 0, pServerState->pServerKey); + } + else + { + ZPrintf("httpserv: could not load private key '%s'\n", argv[2]); + iResult = -1; + } + return(iResult); + } + + // check for version set + if ((argc == 3) && !ds_stricmp(argv[1], "vers")) + { + return(ProtoHttpServControl(pServerState->pHttpServ, 'vers', (int32_t)strtol(argv[2], NULL, 16), 0, NULL)); + } + + // check for min version set + if ((argc == 3) && !ds_stricmp(argv[1], "vmin")) + { + return(ProtoHttpServControl(pServerState->pHttpServ, 'vmin', (int32_t)strtol(argv[2], NULL, 16), 0, NULL)); + } + + // check for control + if ((argc > 3) && (!ds_stricmp(argv[1], "ctrl"))) + { + int32_t iCmd, iThread, iValue = 0, iValue2 = 0; + const char *pValue = NULL; + + iCmd = _GetIntArg(argv[2]); + iThread = _GetIntArg(argv[3]); + + if (argc > 4) + { + iValue = _GetIntArg(argv[3]); + } + if (argc > 5) + { + iValue2 = _GetIntArg(argv[4]); + } + if (argc > 6) + { + pValue = argv[5]; + } + + return(ProtoHttpServControl2(pServerState->pHttpServ, iThread, iCmd, iValue, iValue2, (void *)pValue)); + } + + return(iResult); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/imageconv.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/imageconv.c new file mode 100644 index 00000000..b6ab1b42 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/imageconv.c @@ -0,0 +1,731 @@ +/*H********************************************************************************/ +/*! + \File imageconv.c + + \Description + Test DirtyGraph image conversion routines. + + \Copyright + Copyright (c) 2006 Electronic Arts Inc. + + \Version 02/23/2006 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/graph/dirtygraph.h" +#include "DirtySDK/graph/dirtygif.h" +#include "DirtySDK/graph/dirtyjpg.h" +#include "DirtySDK/graph/dirtypng.h" + +#include "libsample/zfile.h" +#include "libsample/zlib.h" +#include "libsample/zmem.h" + +#include "testermodules.h" + +#define RUNLIBJPEG (FALSE) + +#if RUNLIBJPEG +extern BITMAPINFO *jpeg_read_dibitmap(char *fname, char *errbuf, long errlen, long *cmpsize); +#endif + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +static DirtyGraphRefT *pDirtyGraph; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvParseHeader + + \Description + Read input image and parse it + + \Input *pInputFile - input file data + \Input uInputSize - size of input file data + \Input *pImageInfo - [out] storage for image info + + \Output + DirtyGraphInfoT * - pointer to parsed image info, or NULL + + \Version 01/17/2020 (jbrookes) Split from _CmdImgConvDecodeImage +*/ +/********************************************************************************F*/ +static DirtyGraphInfoT *_CmdImgConvParseHeader(const uint8_t *pInputFile, uint32_t uInputSize, DirtyGraphInfoT *pImageInfo) +{ + int32_t iError; + const char* _strImageTypes[] = { "unknown", "gif", "jpg", "png" }; + + // create module state + if (pDirtyGraph == NULL) + { + pDirtyGraph = DirtyGraphCreate(); + } + + // parse the image header + if ((iError = DirtyGraphDecodeHeader(pDirtyGraph, pImageInfo, pInputFile, uInputSize)) < 0) + { + ZPrintf("imgconv: error %d trying to parse image\n", iError); + DirtyGraphDestroy(pDirtyGraph); + pDirtyGraph = NULL; + return(NULL); + } + + // identify image type + ZPrintf("imgconv: parsed %s image\n", _strImageTypes[pImageInfo->uType]); + return(pImageInfo); +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvDecodeImage + + \Description + Read input image, and decode it to a 32bit ARGB file using DirtyGraph + + \Input *pInputFile - input file data + \Input uInputSize - size of input file data + \Input pImageInfo - image info + \Input bMultiImage - multi-image decoding enabled + + \Output + uint8_t * - pointer to output 32bit ARGB image, or NULL + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t *_CmdImgConvDecodeImage(const uint8_t *pInputFile, uint32_t uInputSize, DirtyGraphInfoT *pImageInfo, uint8_t bMultiImage) +{ + uint8_t *p32Bit; + int32_t iError, iNumFrames = bMultiImage ? pImageInfo->uNumFrames : 1; + + // allocate space for 32bit raw image + if ((p32Bit = ZMemAlloc(pImageInfo->uNumFrames*pImageInfo->iWidth*pImageInfo->iHeight*4)) == NULL) + { + ZPrintf("imgconv: could not allocate memory for decoded image\n"); + DirtyGraphDestroy(pDirtyGraph); + pDirtyGraph = NULL; + return(NULL); + } + + // decode the image + if (iNumFrames == 1) + { + if ((iError = DirtyGraphDecodeImage(pDirtyGraph, pImageInfo, p32Bit, pImageInfo->iWidth, pImageInfo->iHeight)) < 0) + { + ZPrintf("imgconv: error %d trying to decode image\n", iError); + DirtyGraphDestroy(pDirtyGraph); + pDirtyGraph = NULL; + ZMemFree(p32Bit); + return(NULL); + } + } + else + { + // get animation info + int32_t *pAnimInfo = ZMemAlloc(pImageInfo->uNumFrames*sizeof(int32_t)); + int32_t iFrame, iFrames = DirtyGraphGetImageInfo(pDirtyGraph, pImageInfo, 'anim', pAnimInfo, pImageInfo->uNumFrames*sizeof(int32_t)); + ZPrintf("imgconv: %d frames with animation delays of "); + for (iFrame = 0; iFrame < iFrames; iFrame += 1) + { + ZPrintf("%d,", pAnimInfo[iFrame]); + } + ZPrintf("\n"); + ZMemFree(pAnimInfo); + + // decode the multiframe inmage + if ((iError = DirtyGraphDecodeImageMulti(pDirtyGraph, pImageInfo, p32Bit, pImageInfo->iWidth, pImageInfo->iHeight)) < 0) + { + ZPrintf("imgconv: error %d trying to decode image\n", iError); + DirtyGraphDestroy(pDirtyGraph); + pDirtyGraph = NULL; + ZMemFree(p32Bit); + return(NULL); + } + + } + + // return 32bit image buffer + return(p32Bit); +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvSwizzleLineBMP + + \Description + Swizzle scanline in place. + + \Input *pScan - scanline to swizzled + \Input iWidth - width of scanline + + \Output + None + + \Version 03/07/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CmdImgConvSwizzleLineBMP(uint8_t *pScan, int32_t iWidth) +{ + int32_t iCurW; + uint8_t uTmp; + + // do the swap and swizzle in one pass + for (iCurW = 0; iCurW < iWidth; iCurW += 1, pScan += 4) + { + // swap a and b + uTmp = pScan[0]; // save a + pScan[0] = pScan[3]; // b->a + pScan[3] = uTmp; // a->b + + // swap r and g + uTmp = pScan[1]; // save r + pScan[1] = pScan[2]; // g->r + pScan[2] = uTmp; // r->g + } +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvSwapAndSwizzleLineBMP + + \Description + Swap scanlines pointed to by pScanLo and pScanHi and swizzle in-place. + + \Input *pScanLo - lo scanline to swap&swizzle + \Input *pScanHi - hi scanline to swap&swizzle + \Input iWidth - width of scanline + + \Output + None + + \Version 03/07/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CmdImgConvSwapAndSwizzleLineBMP(uint8_t *pScanLo, uint8_t *pScanHi, int32_t iWidth) +{ + int32_t iCurW; + uint8_t uTmp; + + // do the swap and swizzle in one pass + for (iCurW = 0; iCurW < iWidth; iCurW += 1, pScanLo += 4, pScanHi += 4) + { + // swap a(hi) and b(lo) + uTmp = pScanHi[0]; // save a(hi) + pScanHi[0] = pScanLo[3]; // b(lo)->a(hi) + pScanLo[3] = uTmp; // a(hi)->b(lo) + + // swap b(hi) and a(lo) + uTmp = pScanHi[3]; // save b(hi) + pScanHi[3] = pScanLo[0]; // a(lo)->b(hi) + pScanLo[0] = uTmp; // b(hi)->a(lo) + + // swap r(hi) and g(lo) + uTmp = pScanHi[1]; // save r(hi) + pScanHi[1] = pScanLo[2]; // g(lo)->r(hi) + pScanLo[2] = uTmp; // r(hi)->g(lo) + + // swap g(hi) and r(lo) + uTmp = pScanHi[2]; // save g(hi) + pScanHi[2] = pScanLo[1]; // r(lo)->g(hi) + pScanLo[1] = uTmp; // g(hi)->r(lo) + } +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvSaveBMP + + \Description + Save input 32bit ARGB image as a 32bit BMP file + + \Input *pFilename - filename of file to save + \Input *pImageData - input image data + \Input iWidth - width of input image + \Input iHeight - height of input image + + \Output + int32_t - ZFileClose() result + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdImgConvSaveBMP(const char *pFileName, const uint8_t *pImageData, int32_t iWidth, int32_t iHeight) +{ + BITMAPFILEHEADER BitmapFileHeader; + BITMAPINFOHEADER BitmapInfoHeader; + uint8_t *pScanHi, *pScanLo, *pBMPData; + ZFileT iFileId; + int32_t iCurH; + + // open the file for writing + if ((iFileId = ZFileOpen(pFileName, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_CREATE|ZFILE_OPENFLAG_BINARY)) < 0) + { + return(-1); + } + + // make a temp copy for conversion to bmp format + if ((pBMPData = ZMemAlloc(iWidth*iHeight*4)) == NULL) + { + return(-2); + } + memcpy(pBMPData, pImageData, iWidth*iHeight*4); + + // vflip image and convert from ARGB to BGRA in one pass + for (iCurH = 0; iCurH < (iHeight/2); iCurH++) + { + // ref scanlines to swap and swizzle + pScanLo = pBMPData + (iCurH*iWidth*4); + pScanHi = pBMPData + ((iHeight-iCurH-1)*iWidth*4); + + // do the swap and swizzle + _CmdImgConvSwapAndSwizzleLineBMP(pScanLo, pScanHi, iWidth); + } + // if height is odd, swizzle the center scanline + if (iHeight & 1) + { + // ref scanlines to swap and swizzle + pScanLo = pBMPData + (iCurH*iWidth*4); + + // do the swap and swizzle + _CmdImgConvSwizzleLineBMP(pScanLo, iWidth); + } + + // format bitmap header + ds_memclr(&BitmapFileHeader, sizeof(BitmapFileHeader)); + BitmapFileHeader.bfType = 'MB'; + BitmapFileHeader.bfSize = sizeof(BitmapFileHeader)+sizeof(BitmapInfoHeader)+(iWidth*iHeight*4); + BitmapFileHeader.bfOffBits = sizeof(BitmapFileHeader)+sizeof(BitmapInfoHeader); + + // write fileheader to output file + if (ZFileWrite(iFileId, &BitmapFileHeader, sizeof(BitmapFileHeader)) < 0) + { + ZFileClose(iFileId); + return(-1); + } + + // format bitmapinfo header + ds_memclr(&BitmapInfoHeader, sizeof(BitmapInfoHeader)); + BitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER); + BitmapInfoHeader.biWidth = iWidth; + BitmapInfoHeader.biHeight = iHeight; + BitmapInfoHeader.biPlanes = 1; + BitmapInfoHeader.biBitCount = 32; + BitmapInfoHeader.biCompression = BI_RGB; + BitmapInfoHeader.biSizeImage = iWidth*iHeight*4; + BitmapInfoHeader.biXPelsPerMeter = 0; + BitmapInfoHeader.biYPelsPerMeter = 0; + BitmapInfoHeader.biClrUsed = 0; + BitmapInfoHeader.biClrImportant = 0; + + // write infoheader to output file + if (ZFileWrite(iFileId, &BitmapInfoHeader, sizeof(BitmapInfoHeader)) < 0) + { + ZFileClose(iFileId); + return(-1); + } + + // write pixel data to output file + if (ZFileWrite(iFileId, pBMPData, iWidth*iHeight*4) < 0) + { + ZFileClose(iFileId); + return(-1); + } + + // free pixel data buffer + ZMemFree(pBMPData); + + // close the file + return(ZFileClose(iFileId)); +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvSaveRAW + + \Description + Save input 32bit ARGB image as a 24bit RAW image. + + \Input *pFilename - filename of file to save + \Input *pImageData - input image data + \Input iWidth - width of input image + \Input iHeight - height of input image + + \Output + int32_t - ZFileSave() result + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdImgConvSaveRAW(const char *pFileName, uint8_t *pImageData, int32_t iWidth, int32_t iHeight) +{ + uint8_t *pSrc, *pDst; + int32_t iW, iH; + + // convert 32bit ARGB image to 24bit RGB image (inline) + for (pSrc=pImageData, pDst=pImageData, iH=0; iH < iHeight; iH++) + { + for (iW = 0; iW < iWidth; iW++) + { + pDst[0] = pSrc[1]; + pDst[1] = pSrc[2]; + pDst[2] = pSrc[3]; + pSrc += 4; + pDst += 3; + } + } + + // write the file + return(ZFileSave(pFileName, (const char *)pImageData, iWidth*iHeight*3, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_CREATE|ZFILE_OPENFLAG_BINARY)); +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvDecodeAndSave + + \Description + Decode input to 32bit ARGB and save as raw or bmp. + + \Input *pFilename - filename of file to save + \Input *pInputFile - input file + \Input iInputSize - input file size + \Input *pImageInfo - image info + + \Output + int32_t - ZFileSave() result + + \Version 01/17/2020 (jbrookes) Split from CmdImgConv() +*/ +/********************************************************************************F*/ +static int32_t _CmdImgConvDecodeAndSave(const char *pFileName, uint8_t *pInputFile, int32_t iInputSize, DirtyGraphInfoT *pImageInfo) +{ + uint8_t *p32BitImage; + int32_t iResult; + + // convert to 32bit raw image + if ((p32BitImage = _CmdImgConvDecodeImage(pInputFile, iInputSize, pImageInfo, FALSE)) == NULL) + { + return(0); + } + + // save output image based on type + if (ds_stristr(pFileName, ".raw")) + { + iResult = _CmdImgConvSaveRAW(pFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight); + } + else if (ds_stristr(pFileName, ".bmp")) + { + iResult = _CmdImgConvSaveBMP(pFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight); + } + else + { + ZPrintf("imgconv: output filetype unrecognized\n"); + iResult = -1; + } + + // success? + if (iResult >= 0) + { + ZPrintf("imgconv: saved output image %s\n", pFileName); + } + else + { + ZPrintf("imgconv: error writing output file '%s'\n", pFileName); + } + + // dispose of buffer and return result to caller + ZMemFree(p32BitImage); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvDecodeAndSaveMulti + + \Description + Decode input multi-frame file to 32bit ARGB and save as raw or bmp files. + A numerical extension is appended to differentiate the frames. Uses + DirtyGraphDecodeImage32Multi() + + \Input *pFilename - filename of file to save + \Input *pInputFile - input file + \Input iInputSize - input file size + \Input *pImageInfo - image info + + \Output + int32_t - ZFileSave() result + + \Version 01/17/2020 (jbrookes) Split from CmdImgConv() +*/ +/********************************************************************************F*/ +static int32_t _CmdImgConvDecodeAndSaveMulti(const char *pFileName, uint8_t *pInputFile, int32_t iInputSize, DirtyGraphInfoT *pImageInfo) +{ + char strOutputFileName[1024], *pExt; + uint8_t *p32BitImage; + int32_t iResult, iFrame, iFrameSize; + uint8_t bBmp; + + // convert to 32bit raw image + if ((p32BitImage = _CmdImgConvDecodeImage(pInputFile, iInputSize, pImageInfo, TRUE)) == NULL) + { + return(0); + } + + // copy filename + ds_strnzcpy(strOutputFileName, pFileName, sizeof(strOutputFileName)); + + // save output image based on type + if ((pExt = ds_stristr(strOutputFileName, ".raw")) != NULL) + { + bBmp = FALSE; + } + else if ((pExt = ds_stristr(strOutputFileName, ".bmp")) != NULL) + { + bBmp = TRUE; + } + else + { + ZPrintf("imgconv: output filetype unrecognized\n"); + return(0); + } + + // set up filename for writing multiple frames; first truncate the extension + *pExt = '\0'; + + // write out frame data as successive images + for (iFrame = 0, iFrameSize = pImageInfo->iWidth*pImageInfo->iHeight*4, iResult = 0; (iFrame < pImageInfo->uNumFrames) && (iResult == 0); iFrame += 1) + { + ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-%02d%s", iFrame, bBmp ? ".bmp" : ".raw"); + iResult = bBmp ? _CmdImgConvSaveBMP(strOutputFileName, p32BitImage+(iFrame*iFrameSize), pImageInfo->iWidth, pImageInfo->iHeight) : _CmdImgConvSaveRAW(strOutputFileName, p32BitImage+(iFrame * iFrameSize), pImageInfo->iWidth, pImageInfo->iHeight); + } + + // success? + if (iResult >= 0) + { + ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-XX%s", bBmp ? ".bmp" : ".raw"); + ZPrintf("imgconv: saved %d output images %s\n", iFrame, strOutputFileName); + } + else + { + ZPrintf("imgconv: error writing output file '%s'\n", strOutputFileName); + } + + // dispose of buffer and return result to caller + ZMemFree(p32BitImage); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _CmdImgConvDecodeAndSaveMulti2 + + \Description + Decode input multi-frame file to 32bit ARGB and save as raw or bmp files. + A numerical extension is appended to differentiate the frames. Uses + DirtyGraphDecodeImageFrame(). + + \Input *pFilename - filename of file to save + \Input *pInputFile - input file + \Input iInputSize - input file size + \Input *pImageInfo - image info + + \Output + int32_t - ZFileSave() result + + \Version 02/06/2020 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdImgConvDecodeAndSaveMulti2(const char *pFileName, uint8_t *pInputFile, int32_t iInputSize, DirtyGraphInfoT *pImageInfo) +{ + char strOutputFileName[1024], *pExt; + uint8_t *p32BitImage; + int32_t iResult, iFrame, iError; + uint8_t bBmp; + + // allocate space for 32bit raw image + if ((p32BitImage = ZMemAlloc(pImageInfo->iWidth*pImageInfo->iHeight*4)) == NULL) + { + ZPrintf("imgconv: could not allocate memory for decoded image\n"); + DirtyGraphDestroy(pDirtyGraph); + pDirtyGraph = NULL; + return(0); + } + + // copy filename + ds_strnzcpy(strOutputFileName, pFileName, sizeof(strOutputFileName)); + + // save output image based on type + if ((pExt = ds_stristr(strOutputFileName, ".raw")) != NULL) + { + bBmp = FALSE; + } + else if ((pExt = ds_stristr(strOutputFileName, ".bmp")) != NULL) + { + bBmp = TRUE; + } + else + { + ZPrintf("imgconv: output filetype unrecognized\n"); + return(0); + } + + // set up filename for writing multiple frames; first truncate the extension + *pExt = '\0'; + + // write out frame data as successive images + for (iFrame = 0, iResult = 0; (iFrame < pImageInfo->uNumFrames) && (iResult == 0); iFrame += 1) + { + // decode the multiframe inmage + uint64_t uTick = NetTickUsec(); + if ((iError = DirtyGraphDecodeImageFrame(pDirtyGraph, pImageInfo, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight, iFrame)) < 0) + { + ZPrintf("imgconv: error %d trying to decode image\n", iError); + DirtyGraphDestroy(pDirtyGraph); + pDirtyGraph = NULL; + ZMemFree(p32BitImage); + return(0); + } + ZPrintf("imgconv: %dus for decode\n", NetTickDiff(NetTickUsec(), uTick)); + + // create filename + ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-%02d%s", iFrame, bBmp ? ".bmp" : ".raw"); + + // save the image + iResult = bBmp ? _CmdImgConvSaveBMP(strOutputFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight) : _CmdImgConvSaveRAW(strOutputFileName, p32BitImage, pImageInfo->iWidth, pImageInfo->iHeight); + } + + // success? + if (iResult >= 0) + { + ds_snzprintf(pExt, (signed)sizeof(strOutputFileName)-(pExt-strOutputFileName), "-XX%s", bBmp ? ".bmp" : ".raw"); + ZPrintf("imgconv: saved %d output images %s\n", iFrame, strOutputFileName); + } + else + { + ZPrintf("imgconv: error writing output file '%s'\n", strOutputFileName); + } + + // dispose of buffer and return result to caller + ZMemFree(p32BitImage); + return(iResult); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdStream + + \Description + Create the Module module. + + \Input *argz - environment + \Input argc - number of args + \Input *argv[] - argument list + + \Output int32_t - standard return code + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdImgConv(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iInputSize, iResult, iArg = 1; + uint8_t *pInputFile; + const char *pInputName; + DirtyGraphInfoT ImageInfo; + uint8_t bMultiFrame = FALSE, bMultiFrame2 = FALSE; + + // check for module state destroy (no arguments) + if ((argc == 1) && (pDirtyGraph != NULL)) + { + // destroy state + DirtyGraphDestroy(pDirtyGraph); + pDirtyGraph = NULL; + ZPrintf("%s: graph instance destroyed\n", argv[0]); + return(0); + } + + // check for multiframe argument + if ((argc > 1) && !strcmp(argv[iArg], "-m")) + { + iArg += 1; + bMultiFrame = TRUE; + } + // check for multiframe flavor #2 + if ((argc > 1) && !strcmp(argv[iArg], "-m2")) + { + iArg += 1; + bMultiFrame = TRUE; + bMultiFrame2 = TRUE; + } + + // usage + if ((argc == iArg) || (argc > iArg+2)) + { + ZPrintf("usage: %s [-m] \n", argv[0]); + return(0); + } + pInputName = argv[iArg]; + + // open input file for reading + if ((pInputFile = (uint8_t *)ZFileLoad(pInputName, &iInputSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) == NULL) + { + ZPrintf("%s: unable to open input file '%s'\n", argv[0], pInputName); + return(0); + } + + #if RUNLIBJPEG + // first read it with libjpeg for comparison + if (ds_stristr(pInputName, ".jpg")) + { + char strError[256]; + jpeg_read_dibitmap((char *)pInputName, strError, sizeof(strError), NULL); + } + #endif + + // parse image info + if (_CmdImgConvParseHeader(pInputFile, iInputSize, &ImageInfo) == NULL) + { + return(0); + } + + // output? + if (argc == iArg+2) + { + if ((ImageInfo.uNumFrames == 1) || !bMultiFrame) + { + iResult = _CmdImgConvDecodeAndSave(argv[iArg+1], pInputFile, iInputSize, &ImageInfo); + } + else if (!bMultiFrame2) + { + iResult = _CmdImgConvDecodeAndSaveMulti(argv[iArg+1], pInputFile, iInputSize, &ImageInfo); + } + else + { + iResult = _CmdImgConvDecodeAndSaveMulti2(argv[iArg+1], pInputFile, iInputSize, &ImageInfo); + } + } + + // dispose of source image + ZMemFree(pInputFile); + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/json.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/json.c new file mode 100644 index 00000000..5203e6aa --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/json.c @@ -0,0 +1,235 @@ +/*H********************************************************************************/ +/*! + \File json.c + + \Description + Test the JSON formatter and parser. + + \Copyright + Copyright (c) 2012 Electronic Arts Inc. + + \Version 12/11/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/util/jsonformat.h" +#include "DirtySDK/util/jsonparse.h" +#include "testermodules.h" + +#include "libsample/zlib.h" +#include "libsample/zfile.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// Variables + +static const char *_pSimpleArrayStringTest = \ +"{" \ + "[" \ + "\"abcd\"," \ + "\"efgh\"," \ + "\"ijkl\"," \ + "\"mnop\"" \ + "]" \ +"};"; + +static const char *_pSimpleArrayNumberTest = \ +"{" \ + "[" \ + "101," \ + "5005," \ + "7," \ + "32768" \ + "]" \ +"};"; + + +/*** Private Functions ************************************************************/ + +static void _SimpleDecode(void) +{ + char strBuffer[1024], *pBuffer; + uint16_t buff[4096]; + const char *pData = "{\"isAllowed\":false,\"reasons\":[{\"reason\":\"CheckFailed\"}]}"; + const char *pResult; + uint8_t bAllowed; + + JsonParse(buff, sizeof(buff) / sizeof(buff[0]), pData, (int32_t)strlen(pData)); + pResult = JsonFind(buff, "isAllowed"); + bAllowed = (JsonGetBoolean(pResult, 0) == 0) ? FALSE : TRUE; + + // encode some Json + // "id": 2533274790412952, + // "name": "TestPlayer0", + // "seatIndex": -1 + JsonInit(strBuffer, sizeof(strBuffer), JSON_FL_WHITESPACE); + JsonObjectStart(strBuffer, NULL); + JsonAddInt(strBuffer, "id", 2533274790412952LL); + JsonAddStr(strBuffer, "name", "TestPlayer0"); + JsonAddInt(strBuffer, "seatIndex", -1); + JsonObjectEnd(strBuffer); + pBuffer = JsonFinish(strBuffer); + ZPrintf("encoded Json:\n------------\n%s\n-----------\n", pBuffer); +} + +static void _SimpleEncode(void) +{ + char strBuffer[1024], *pBuffer; + + /* now something a little more challenging: + + { + "body": + { + "attachments":null, + "partnerData": + { + "MultiplayerMessageType":"YourTurn", + "SessionId": "C8921C4E-4FC2-439E-9368-5D26889BD1BB" + } + }, + "header": + { + "attributes":null, + "expiration":"2011-10-11T23:59:59.9999999", + "id":null, + "messageType":"Multiplayer", + "recipients":[{"userId":"GoTeamEmily","userType":"Gamertag"},{"userId":"Longstreet360","userType":"Gamertag"}]], + "sender":"Striker", + "senderPlatform":null, + "sent":"2011-10-13T16:40:58.1890842-07:00", + "targetPlatforms":["MP3"], + "title":1297287259 + } + } + + */ + JsonInit(strBuffer, sizeof(strBuffer), JSON_FL_WHITESPACE); + JsonObjectStart(strBuffer, "body"); + JsonAddStr(strBuffer, "attachments", NULL); + JsonObjectStart(strBuffer, "partnerData"); + JsonAddStr(strBuffer, "MultiplayerMessageType", "YourTurn"); + JsonAddStr(strBuffer, "SessionId", "C8921C4E-4FC2-439E-9368-5D26889BD1BB"); // AddGuid()? + JsonObjectEnd(strBuffer); + JsonObjectEnd(strBuffer); + JsonObjectStart(strBuffer, "header"); + JsonAddStr(strBuffer, "attributes", NULL); + JsonAddStr(strBuffer, "expiration", "2011-10-11T23:59:59.9999999"); // AddDate()? + JsonAddStr(strBuffer, "id", NULL); + JsonAddStr(strBuffer, "messageType", "Multiplayer"); + JsonArrayStart(strBuffer, "recipients"); + JsonObjectStart(strBuffer, NULL); + JsonAddStr(strBuffer, "userId", "GoTeamEmily"); + JsonAddStr(strBuffer, "userType", "Gamertag"); + JsonObjectEnd(strBuffer); + JsonObjectStart(strBuffer, NULL); + JsonAddStr(strBuffer, "userId", "Longstreet360"); + JsonAddStr(strBuffer, "userType", "Gamertag"); + JsonObjectEnd(strBuffer); + JsonArrayEnd(strBuffer); + JsonAddStr(strBuffer, "sender", "Striker"); + JsonAddStr(strBuffer, "senderPlatform", "null"); + JsonAddStr(strBuffer, "sent", "2011-10-13T16:40:58.1890842-07:00"); // AddDate()? + JsonArrayStart(strBuffer, "targetPlatforms"); + JsonAddStr(strBuffer, NULL, "MP3"); + JsonArrayEnd(strBuffer); + JsonAddInt(strBuffer, "title", 1297287259); + JsonObjectEnd(strBuffer); + pBuffer = JsonFinish(strBuffer); + ZPrintf("encoded Json:\n------------\n%s\n-----------\n", pBuffer); +} + +static void _ArrayTests(void) +{ + int32_t iElement, iValue; + uint16_t aJsonParseBuf[512]; + char strTemp[16]; + + JsonParse(aJsonParseBuf, sizeof(aJsonParseBuf)/sizeof(aJsonParseBuf[0]), _pSimpleArrayStringTest, (int32_t)strlen(_pSimpleArrayStringTest)); + for (iElement = 0; iElement < 4; iElement += 1) + { + JsonGetString(JsonFind2(aJsonParseBuf, NULL, "[", iElement), strTemp, sizeof(strTemp), ""); + ZPrintf("str[%d]=%s\n", iElement, strTemp); + } + + JsonParse(aJsonParseBuf, sizeof(aJsonParseBuf)/sizeof(aJsonParseBuf[0]), _pSimpleArrayNumberTest, (int32_t)strlen(_pSimpleArrayNumberTest)); + for (iElement = 0; iElement < 5; iElement += 1) + { + iValue = (int32_t)JsonGetInteger(JsonFind2(aJsonParseBuf, NULL, "[", iElement), -1); + ZPrintf("str[%d]=%d\n", iElement, iValue); + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdJson + + \Description + Test the Json formatter and parser + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 12/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdJson(ZContext *argz, int32_t argc, char *argv[]) +{ + // check for a file argument... if we have one, load and parse it + if (argc == 2) + { + uint16_t *pJsonParseBuf; + int32_t iFileSize; + char *pFileData; + + if ((pFileData = ZFileLoad(argv[1], &iFileSize, FALSE)) != NULL) + { + if ((pJsonParseBuf = JsonParse2(pFileData, -1, 0, 0, NULL)) != NULL) + { + // + // insert custom parsing code here + // + } + else + { + ZPrintf("%s: could not parse buffer\n", argv[0]); + } + + free(pFileData); + } + else + { + ZPrintf("%s: could not open file '%s'\n", argv[0], argv[1]); + } + } + else + { + _SimpleDecode(); + _SimpleEncode(); + // test parsing some simple arrays + _ArrayTests(); + } + + //$$todo - more tests + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/lang.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/lang.c new file mode 100644 index 00000000..97207261 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/lang.c @@ -0,0 +1,138 @@ +/*H********************************************************************************/ +/*! + \File lang.c + + \Description + Test LobbyLang macros + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/03/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtylang.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "libsample/zlib.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CmdLangTest + + \Description + Test LobbyLang macros + + \Input ZContext *argz - environment + \Input int32_t argc - standard number of arguments + \Input char **argv - standard arg list + + \Output standard return value +*/ +/********************************************************************************F*/ +int32_t CmdLang(ZContext *argz, int32_t argc, char **argv) +{ + // check usage + if (argc < 1) + { + ZPrintf(" test all lobbylang macros and display output\n"); + ZPrintf(" usage: %s \n", argv[0]); + return(0); + } + else + { + uint32_t uToken; + uint32_t uCurrency; + uint16_t uLanguage; + uint16_t uCountry; + uint16_t uSymbols; + uint16_t uTemp; + char strCurrency[4]; + char strLang[3]; + char strCountry[3]; + char strToken[5]; + + uLanguage = LOBBYAPI_LANGUAGE_GERMAN; + uCountry = LOBBYAPI_COUNTRY_GERMANY; + uToken = LOBBYAPI_LocalizerTokenCreate(uLanguage, uCountry); + ZPrintf("token for germany and german: %c%c%c%c\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken)); + uCountry = LOBBYAPI_LocalizerTokenGetCountry(uToken); + uLanguage = LOBBYAPI_LocalizerTokenGetLanguage(uToken); + uSymbols = LOBBYAPI_LocalizerTokenGetShortFromString("*^"); + uTemp = LOBBYAPI_LocalizerTokenShortToUpper(uLanguage); + ZPrintf("language [%c%c=0x%X] toupper [%c%c=0x%X]\n", + (uLanguage>>8)&0xFF, uLanguage&0xFF, uLanguage, + (uTemp>>8)&0xFF, uTemp&0xFF, uTemp); + uTemp = LOBBYAPI_LocalizerTokenShortToLower(uCountry); + ZPrintf("country [%c%c=0x%X] tolower [%c%c=0x%X]\n", + (uCountry>>8)&0xFF, uCountry&0xFF, uCountry, + (uTemp>>8)&0xFF, uTemp&0xFF, uTemp); + ZPrintf("symbols (should not change) 0x%X toupper 0x%X\n",uSymbols, LOBBYAPI_LocalizerTokenShortToUpper(uSymbols)); + LOBBYAPI_LocalizerTokenSetCountry(uToken, LOBBYAPI_COUNTRY_UNITED_STATES); + ZPrintf("token change to USA: %c%c%c%c\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken)); + LOBBYAPI_LocalizerTokenSetLanguage(uToken, LOBBYAPI_LANGUAGE_ENGLISH); + ZPrintf("token change to USA and ENGLISH: %c%c%c%c\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken)); + LOBBYAPI_LocalizerTokenCreateCountryString(strCountry, uToken); + ZPrintf("Country string [%s]\n", strCountry); + LOBBYAPI_LocalizerTokenCreateLanguageString(strLang, uToken); + ZPrintf("Language string [%s]\n", strLang); + LOBBYAPI_LocalizerTokenCreateLocalityString(strToken, uToken); + ZPrintf("Locality string [%s]\n", strToken); + ZPrintf("int16_t from string 'Aa': 0x%X\n", LOBBYAPI_LocalizerTokenGetShortFromString("Aa")); + uToken = LOBBYAPI_LocalizerTokenCreateFromStrings("FR", "FR"); + ZPrintf("create from strings: %c%c%c%c\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken)); + sprintf(strToken, "frCA"); + uToken = LOBBYAPI_LocalizerTokenCreateFromString(strToken); + ZPrintf("create from string: %s --> %c%c%c%c\n", strToken, LOBBYAPI_LocalizerTokenPrintCharArray(uToken)); + + // currency + uCurrency = LOBBYAPI_CURRENCY_EURO; + LOBBYAPI_CreateCurrencyString(strCurrency, uCurrency); + ZPrintf("Currency string [%s]\n", strCurrency); + + uCurrency = LOBBYAPI_CURRENCY_UNITED_STATES_DOLLAR; + LOBBYAPI_CreateCurrencyString(strCurrency, uCurrency); + ZPrintf("Currency string [%s]\n", strCurrency); + + uCurrency = LOBBYAPI_CURRENCY_CANADIAN_DOLLAR; + LOBBYAPI_CreateCurrencyString(strCurrency, uCurrency); + ZPrintf("Currency string [%s]\n", strCurrency); + +// > langtest +// token for germany and german: deDE +// language [de=0x6465] toupper [DE=0x4445] +// country [DE=0x4445] tolower [de=0x6465] +// symbols (should not change) 0x2A5E toupper 0x2A5E +// token change to USA: deUS +// token change to USA and ENGLISH: enUS +// Country string [US] +// Language string [en] +// Locality string [enUS] +// int16_t from string 'Aa': 0x4161 +// create from strings: frFR +// create from string: frCA --> frCA + + uToken = NetConnStatus('locl', 0, NULL, 0); + ZPrintf("Current locl: %c%c%c%c\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken)); + + } + return(0); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/memdebug.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/memdebug.c new file mode 100644 index 00000000..5e4e8d23 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/memdebug.c @@ -0,0 +1,73 @@ +/*H********************************************************************************/ +/*! + \File memdebug.c + + \Description + Enable/Disable DirtySock memory debugging. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 11/01/2005 (jbookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/netconn.h" + +#include "libsample/zlib.h" + +#include "testermemory.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdMemDebug + + \Description + Enable/Disable DirtySock memory debugging. + + \Input *argz - environment + \Input argc - number of args + \Input **argv - argument list + + \Output int32_t - standard return code + + \Version 11/01/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t CmdMemDebug(ZContext *argz, int32_t argc, char **argv) +{ + uint32_t bEnable; + + // check usage + if (argc != 2) + { + ZPrintf(" control memory auditing features\n"); + ZPrintf(" usage: %s [0|1] - disable/enable memory debugging\n", argv[0]); + return(0); + } + + // parse the arg + bEnable = (int32_t)strtol(argv[1], NULL, 10); + TesterMemorySetDebug(bEnable); + ZPrintf(" %s: memory debugging %s\n", argv[0], bEnable ? "enabled" : "disabled"); + + // done + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/net.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/net.c new file mode 100644 index 00000000..be0996b2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/net.c @@ -0,0 +1,592 @@ +/*H*************************************************************************************/ +/*! + \File lobby.c + + \Description + Reference application for the NetApi module. + + \Notes + Base code from locker test function by jbrookes. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 04/12/2005 (jfrank) First Version +*/ +/*************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include +#include +#include + +#if defined (__CELLOS_LV2__) +#include +#include +#include +#endif + +#ifdef _XBOX +#include +#endif + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/dirtysock/dirtyerr.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" + +#include "testerregistry.h" +#include "testerhostcore.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct NetAppT +{ + int32_t iNetUp; + int8_t bExternalCleanupComplete; +} NetAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _NetStartup(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _NetQuery(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +#if defined(DIRTYCODE_PS4) +static void _NetTicket(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +#endif +static void _NetConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _NetId(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _NetDisconnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _NetShutdown(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _NetControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _NetStatus(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _NetPorts(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables *************************************************************************/ + +static T2SubCmdT _Net_Commands[] = +{ + { "startup", _NetStartup }, + { "start", _NetStartup }, + { "query", _NetQuery }, + { "ctrl", _NetControl }, + { "id", _NetId }, + { "status", _NetStatus }, + { "ports", _NetPorts }, +#if defined(DIRTYCODE_PS4) + { "ticket", _NetTicket }, +#endif + { "connect", _NetConnect }, + { "disconnect", _NetDisconnect }, + { "shutdown", _NetShutdown }, + { "stop", _NetShutdown }, + { "", NULL } +}; + +static NetAppT _Net_App = +{ 0, + 0 +}; + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function _NetExtCleanupCallback + + \Description + To test external cleanup mechanism + + \Input *pCallbackData - pointer to platform-specific dataspace + + \Output + int32_t - zero=success; -1=try again; other negative=error + + \Version 10/01/2011 (mclouatre) +*/ +/*************************************************************************************************F*/ +static int32_t _NetExtCleanupCallback(void *pCallbackData) +{ + NetAppT *pApp = (NetAppT *)pCallbackData; + + if (pApp->bExternalCleanupComplete) + { + return(0); // complete + } + else + { + return(-1); // try again + } +} + +/*F*************************************************************************************/ +/*! + \Function _NetStartup + + \Description + Net subcommand - start dirtysock + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 04/12/2005 (jfrank) +*/ +/**************************************************************************************F*/ +static void _NetStartup(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + NetAppT *pApp = (NetAppT *)pCmdRef; + char strBuf[256] = "-servicename=tester2"; + int32_t iLoop; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s [start|startup] \"\"\n", argv[0], argv[1]); + return; + } + + for (iLoop = 2; iLoop < argc; iLoop++) + { + ds_strnzcat(strBuf, " ", (int32_t)(sizeof(strBuf) - strlen(strBuf) - 1)); + ds_strnzcat(strBuf, argv[iLoop], (int32_t)(sizeof(strBuf) - strlen(strBuf) - 1)); + } + + ZPrintf("NET: Starting dirtysock with params {%s}.\n", strBuf); + NetConnStartup(strBuf); + + pApp->iNetUp = 1; +} + +/*F*************************************************************************************/ +/*! + \Function _NetQuery + + \Description + Net subcommand - issue a NetConnQuery call + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 05/12/2005 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _NetQuery(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + if ((bHelp == TRUE) || (argc > 3)) + { + ZPrintf(" usage: %s [query] \"\"\n", argv[0], argv[1]); + return; + } + + if (argc == 2) + { + NetConnQuery(NULL, NULL, 0); + } + else if (argc == 3) + { + NetConnQuery(argv[2], NULL, 0); + } +} + +#if defined(DIRTYCODE_PS4) +/*F*************************************************************************************/ +/*! + \Function _NetTicket + + \Description + Net subcommand - test ps3 ticketing system + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 10/09/2009 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _NetTicket(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + uint8_t aTicketBuf[1024]; + int32_t iResult; + + // acquire ticket + if ((iResult = NetConnStatus('tick', 0, aTicketBuf, sizeof(aTicketBuf))) < 1) + { + NetPrintf(("net: NetConnStatus('tick') returned %d\n", iResult)); + return; + } + // display environment + if ((iResult = NetConnStatus('envi', 0, NULL, 0)) < 1) + { + NetPrintf(("net: NetConnStatus('envi') returned %d\n", iResult)); + return; + } + NetPrintf(("net: platform environment is %d\n")); +} +#endif //defined(DIRTYCODE_PS4) + +/*F*************************************************************************************/ +/*! + \Function _NetConnect + + \Description + Net subcommand - connect the networking + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 04/12/2005 (jfrank) First Version +*/ +/**************************************************************************************F*/ +static void _NetConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + if ((bHelp == TRUE) || (argc < 2) || (argc > 4)) + { + ZPrintf(" usage: %s connect \"\"\n", argv[0]); + return; + } + + if (argc == 2) + { + NetConnConnect(NULL, NULL, 0); + } + else if (argc == 3) + { + NetConnConnect((const NetConfigRecT *)argv[2], NULL, 0); + } + else if (argv[2][0] == '-') + { + NetConnConnect(NULL, argv[3], 0); + } + else + { + NetConnConnect((const NetConfigRecT *)argv[2], argv[3], 0); + } +} + +/*F*************************************************************************************/ +/*! + \Function _NetId + + \Description + Net subcommand - set up accountId and personaId + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 07/31/2019 (mgallant) +*/ +/**************************************************************************************F*/ +static void _NetId(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s id [accountId] [personaId]\n", argv[0]); + return; + } + + int64_t iAccountId = (int64_t) strtol(argv[2], NULL, 10); + int64_t iPersonaId = (int64_t) strtol(argv[3], NULL, 10); + + ZPrintf("net: executing NetConnControl('%s', %d, %d, %s, %s)\n", "acid", 0, sizeof(iAccountId), "", NULL); + NetConnControl('acid', 0, sizeof(iAccountId), &iAccountId, NULL); + + ZPrintf("net: executing NetConnControl('%s', %d, %d, %s, %s)\n", "peid", 0, sizeof(iPersonaId), "", NULL); + NetConnControl('peid', 0, sizeof(iPersonaId), &iPersonaId, NULL); + + return; + +} + +/*F*************************************************************************************/ +/*! + \Function _NetDisconnect + + \Description + Net subcommand - connect the networking + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 04/12/2005 (jfrank) First Version +*/ +/**************************************************************************************F*/ +static void _NetDisconnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + if (bHelp == TRUE) + { + ZPrintf(" usage: %s disconnect\n", argv[0]); + return; + } + + NetConnDisconnect(); +} + +/*F*************************************************************************************/ +/*! + \Function _NetShutdown + + \Description + Net subcommand - shut down dirtysock + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 04/12/2005 (jfrank) First Version +*/ +/**************************************************************************************F*/ +static void _NetShutdown(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + NetAppT *pApp = (NetAppT *)pCmdRef; + TesterHostCoreT *pCore; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s [stop|shutdown]\n", argv[0]); + return; + } + + pApp->iNetUp = 0; + + // signal to the core that we want to shut everything down + if ((pCore = (TesterHostCoreT *)TesterRegistryGetPointer("CORE")) != NULL) + { + TesterHostCoreShutdown(pCore); + } +} + +/*F********************************************************************************/ +/*! + \Function _NetControl + + \Description + Execute NetConnControl() + + \Input *pApp - pointer to upnp module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Version 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _NetControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + NetAppT *pApp = (NetAppT *)pCmdRef; + int32_t iCmd, iValue=0, iValue2=0; + void *pValue = NULL; + void *pValue2 = NULL; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s ctrl [val1] [val2] [strVal] [strVal]\n", argv[0]); + return; + } + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + if (argc > 3) + { + iValue = (int32_t)strtol(argv[3], NULL, 10); + } + if (argc > 4) + { + iValue2 = (int32_t)strtol(argv[4], NULL, 10); + } + if (argc > 5) + { + pValue = argv[5]; + } + if (argc > 6) + { + pValue2 = argv[6]; + } + + if (strcmp("recu", argv[2]) == 0) + { + pApp->bExternalCleanupComplete = FALSE; + pValue = (void *)_NetExtCleanupCallback; + pValue2 = (void *)pApp; + } + + ZPrintf("net: executing NetConnControl('%s', %d, %d, %s, %s)\n", argv[2], iValue, iValue2, pValue ? "" : "(null)", pValue2 ? "" : "(null)"); + NetConnControl(iCmd, iValue, iValue2, pValue, pValue2); +} + +/*F*************************************************************************************/ +/*! + \Function _NetStatus + + \Description + Net subcommand - Execute NetConnStatus() + + \Input *pApp - pointer to lobby app + \Input argc - argument count + \Input *argv[] - argument list + + \Version 04/12/2005 (jfrank) First Version +*/ +/**************************************************************************************F*/ +static void _NetStatus(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + unsigned char *pToken; + uint32_t uResult, uToken, iData; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s status <4-char status token to get> \n", argv[0]); + return; + } + + pToken = (unsigned char *)argv[2]; + uToken = (pToken[0] << 24) | (pToken[1] << 16) | (pToken[2] << 8) | pToken[3]; + iData = (int32_t)strtol(argv[3], NULL, 10); + +#if defined(DIRTYCODE_XBOXONE) + if (strcmp("tick", argv[2]) == 0) + { + char strToken[16 * 1024]; + uResult = NetConnStatus(uToken, iData, (void *)strToken, sizeof(strToken)); + } + else if (strcmp("xadd", argv[2]) == 0) + { + uint8_t aSecureDeviceAddressBlob[256]; + + uResult = NetConnStatus(uToken, iData, (void *)aSecureDeviceAddressBlob, sizeof(aSecureDeviceAddressBlob)); + } + else +#endif + { + uResult = NetConnStatus(uToken, iData, NULL, 0); + } + + // if printable, display result as text + if (isprint((uResult >> 24) & 0xff) && isprint((uResult >> 16) & 0xff) && + isprint((uResult >> 8) & 0xff) && isprint((uResult >> 0) & 0xff)) + { + ZPrintf("%s: status of ('%C', %d) is {'%C')\n", argv[0], uToken, iData, uResult); + } + else // display result as decimal and hex + { + ZPrintf("%s: status of ('%C', %d) is {%d/0x%08X)\n", argv[0], uToken, iData, uResult, uResult); + } +} + +/*F********************************************************************************/ +/*! + \Function _NetPorts + + \Description + Like "netstat" on unix + + \Input *pApp - pointer to upnp module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Version 09/25/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _NetPorts(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SocketT *pSocket; + int32_t iResult; + uint32_t uPort; + struct sockaddr SockName; + + SockaddrInit(&SockName, AF_INET); + + // find ports that are bound + for (uPort = 1024; uPort < 65536; uPort++) + { + // create a UDP socket + if ((pSocket = SocketOpen(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == NULL) + { + NetPrintf(("net: error -- could not allocate socket resource\n")); + return; + } + + // bind the socket + SockaddrInSetPort(&SockName, uPort); + iResult = SocketBind(pSocket, &SockName, sizeof(SockName)); + if (iResult == SOCKERR_ADDRINUSE) + { + ZPrintf("net: %5s %5d\n", "UDP", uPort); + } + + // close the socket + if (SocketClose(pSocket) != 0) + { + NetPrintf(("net: error -- could not free socket resource\n")); + return; + } + + // give some time to network stack + NetConnSleep(1); + NetConnIdle(); + } +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function CmdNet + + \Description + Net command. + + \Input *argz - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output + int32_t - zero + + \Version 11/24/04 (jbrookes) +*/ +/**************************************************************************************F*/ +int32_t CmdNet(ZContext *argz, int32_t argc, char *argv[]) +{ + void *pCmdRef = &_Net_App; + unsigned char bHelp; + T2SubCmdT *pCmd; + + // handle shutdown + if(argc == 0) + { + // nothing to do + return(0); + } + // handle basic help + else if ((argc < 2) || (((pCmd = T2SubCmdParse(_Net_Commands, argc, argv, &bHelp)) == NULL))) + { + T2SubCmdUsage(argv[0], _Net_Commands); + return(0); + } + + // hand off to command + pCmd->pFunc(pCmdRef, argc, argv, bHelp); + return(0); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/netprint.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/netprint.c new file mode 100644 index 00000000..6a2cc3b6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/netprint.c @@ -0,0 +1,927 @@ +/*H********************************************************************************/ +/*! + \File netprint.c + + \Description + Test ds_vsnprintf() for compliance with standard routines. + + \Copyright + Copyright (c) 2009-2010 Electronic Arts Inc. + + \Version 10/28/2009 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "libsample/zlib.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// Variables + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _NetFormatAddr6 + + \Description + Initialize a struct sockaddr_in6 given address and port + + \Input *pAddr6 - address to fill in + \Input *pWords - 128 bit address + \Input *uPort - port + + \Version 09/01/2017 (jbrookes) +*/ +/********************************************************************************F*/ +#if !defined(DIRTYCODE_NX) +static void _NetFormatAddr6(struct sockaddr_in6 *pAddr6, const uint16_t *pWords, uint16_t uPort) +{ + int32_t iWord; + ds_memclr(pAddr6, sizeof(*pAddr6)); + pAddr6->sin6_family = AF_INET6; + pAddr6->sin6_port = SocketNtohs(uPort); + pAddr6->sin6_flowinfo = 0; + for (iWord = 0; iWord < 8; iWord += 1) + { + pAddr6->sin6_addr.s6_addr[(iWord*2)+0] = (uint8_t)(pWords[iWord]>>8); + pAddr6->sin6_addr.s6_addr[(iWord*2)+1] = (uint8_t)(pWords[iWord]&0xff); + } +} +#endif + +/*F********************************************************************************/ +/*! + \Function _NetPrintStrCmp + + \Description + Compare two strings; if they are the same, print one out, otherwise + print both out. + + \Input *pStrCtrl - pointer to "control" string (what we are expecting) + \Input *pStrTest - pointer to "test" string (what we actually produced) + \Input *pStrType - pointer to type field, to use in displaying the result + + \Output + int32_t - 0=same, 1=different + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintStrCmp(const char *pStrCtrl, const char *pStrTest, const char *pStrType) +{ + int32_t iResult; + if (strcmp(pStrCtrl, pStrTest) != 0) + { + ZPrintf("netprint: [%s] ctrl(%s) != test(%s)\n", pStrType, pStrCtrl, pStrTest); + iResult = 1; + } + else + { + ZPrintf("netprint: [%s] \"%s\"\n", pStrType, pStrTest); + iResult = 0; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintMemCmp + + \Description + Compare two buffers; if they are the same, print one out, otherwise + print both out. + + \Input *pCtrl - pointer to "control" buffer (what we are expecting) + \Input *pTest - pointer to "test" buffer (what we actually produced) + \Input iLen - buffer length + \Input *pType - pointer to type field, to use in displaying the result + + \Output + int32_t - 0=same, 1=different + + \Version 09/01/2017 (jbrookes) +*/ +/********************************************************************************F*/ +#ifndef DIRTYCODE_NX +static int32_t _NetPrintMemCmp(const uint8_t *pCtrl, int32_t iCtrlLen, const uint8_t *pTest, int32_t iTestLen, const char *pStrType) +{ + int32_t iResult = 1; + if (iCtrlLen != iTestLen) + { + ZPrintf("netprint: [%s] ctrllen(%d) != testlen(%d)\n", pStrType, iCtrlLen, iTestLen); + } + else if (memcmp(pCtrl, pTest, iCtrlLen) != 0) + { + ZPrintf("netprint: [%s] ctrl != test\n", pStrType); + NetPrintMem(pCtrl, iCtrlLen, "ctrl"); + NetPrintMem(pTest, iCtrlLen, "test"); + } + else + { + ZPrintf("netprint: [%s] ctrl=test\n", pStrType); + iResult = 0; + } + return(iResult); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _NetPrintStrIntCmp + + \Description + Compare two strings AND two results; if they are the same, print one out, + otherwise print both out. + + \Input *pStrCtrl - pointer to "control" string (what we are expecting) + \Input *_pStrTest - pointer to "test" string (what we actually produced) + \Input iCtrlRslt - expected result + \Input iTestRslt - actual result + \Input iBufferLimit - size of buffer we were writing into + \Input *pStrType - pointer to type field, to use in displaying the result + + \Output + int32_t - 0=same, 1=different + + \Version 02/15/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintStrIntCmp(const char *pStrCtrl, const char *_pStrTest, int32_t iCtrlRslt, int32_t iTestRslt, int32_t iBufferLimit, const char *pStrType) +{ + int32_t iResult, iStrCmp; + const char *pStrTest; + + // if we have a zero-sized buffer, point to an empty string so we can compare/print it safely + pStrTest = (iBufferLimit > 0) ? _pStrTest : ""; + + // compare the strings + iStrCmp = strcmp(pStrCtrl, pStrTest); + + if ((iStrCmp != 0) && (iCtrlRslt != iTestRslt)) + { + ZPrintf("netprint: [%s] ctrl(%s) != test(%s) and ctrl(%d) != test(%d)\n", pStrType, pStrCtrl, pStrTest, iCtrlRslt, iTestRslt); + iResult = 1; + } + else if (iStrCmp != 0) + { + ZPrintf("netprint: [%s] ctrl(%s) != test(%s)\n", pStrType, pStrCtrl, pStrTest); + iResult = 1; + } + else if (iCtrlRslt != iTestRslt) + { + ZPrintf("netprint: [%s] ctrl(%d) != test(%d)\n", pStrType, iCtrlRslt, iTestRslt); + iResult = 1; + } + else + { + if (iTestRslt > 0) + { + ZPrintf("netprint: [%s] \"%s\" (%d)\n", pStrType, pStrTest, iTestRslt); + } + else + { + ZPrintf("netprint: [%s] "" (%d)\n", pStrType, iTestRslt); + } + iResult = 0; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintFloat + + \Description + Print a floating-point value with both ds_snzprintf and platform sprintf(), + and compare the results. Flag a warning if they are different. + + \Input *pFmt - format string + \Input fValue - float to print + + \Output + int32_t - 0=same, 1=different + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintFloat(const char *pFmt, double fValue) +{ + char strCtrl[256], strTest[256]; + + sprintf(strCtrl, pFmt, fValue); + ds_snzprintf(strTest, sizeof(strTest), pFmt, fValue); + + return(_NetPrintStrCmp(strCtrl, strTest, "flt")); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintInt + + \Description + Print an integer value with both ds_snzprintf and platform sprintf(), + and compare the results. Flag a warning if they are different. + + \Input *pFmt - format string + \Input iValue - integer to print + + \Output + int32_t - 0=same, 1=different + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintInt(const char *pFmt, int32_t iValue) +{ + char strCtrl[256], strTest[256]; + + sprintf(strCtrl, pFmt, iValue); + ds_snzprintf(strTest, sizeof(strTest), pFmt, iValue); + + return(_NetPrintStrCmp(strCtrl, strTest, "int")); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintLongInt + + \Description + Print a 64-bit integer value with both ds_snzprintf and platform sprintf(), + and compare the results. Flag a warning if they are different. + + \Input *pFmt - format string + \Input iValue - 64-bit integer to print + + \Output + int32_t - 0=same, 1=different + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintLongInt(const char *pFmt, int64_t iValue) +{ + char strCtrl[256], strTest[256]; + + sprintf(strCtrl, pFmt, iValue); + ds_snzprintf(strTest, sizeof(strTest), pFmt, iValue); + + return(_NetPrintStrCmp(strCtrl, strTest, "lng")); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintStr + + \Description + Print a string with both ds_snzprintf() and platform sprintf(), + and compare the results. Flag a warning if they are different. + + \Input *pFmt - format string + \Input *pStr - string to print + + \Output + int32_t - 0=same, 1=different + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintStr(const char *pFmt, const char *pStr) +{ + char strCtrl[256], strTest[256]; + + sprintf(strCtrl, pFmt, pStr); + ds_snzprintf(strTest, sizeof(strTest), pFmt, pStr); + + return(_NetPrintStrCmp(strCtrl, strTest, "str")); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintPregen + + \Description + Print formatted output with ds_vsnprintf() and compare to a pre-generated + (static) string. Flag a warning if they are different. + + \Input *pPreGen - pointer to pre-generated string (what we expect) + \Input *pFmt - format specifier + \Input ... - variable argument list + + \Output + int32_t - 0=same, 1=different + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintPregen(const char *pPreGen, const char *pFmt, ...) +{ + char strTest[1024]; + va_list Args; + + // format the output + va_start(Args, pFmt); + ds_vsnprintf(strTest, sizeof(strTest), pFmt, Args); + va_end(Args); + + return(_NetPrintStrCmp(pPreGen, strTest, "pre")); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintOverflow + + \Description + Print formatted output with ds_vsnprintf() and compare to a pre-generated + (static) string. Flag a warning if they are different. + + \Input iBufferLimit - size we want to limit buffer to + \Input *pPreGen - pre-generated string to compare formatted result to + \Input iExpectedResult - expected result code from ds_vsnzprintf() + \Input *pFmt - format specifier + \Input ... - variable argument list + + \Output + int32_t - 0=same, 1=different + + \Version 02/15/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintOverflow(int32_t iBufferLimit, const char *pPreGen, int32_t iExpectedResult, const char *pFmt, ...) +{ + char strTest[128]; + int32_t iResult, iStrCmp, iCheck, iMemStomp; + char cMemChar = 0xcc; + va_list Args; + + // pre-initialize array + ds_memset(strTest, cMemChar, sizeof(strTest)); + + // format the output + va_start(Args, pFmt); + iResult = ds_vsnzprintf(strTest, iBufferLimit, pFmt, Args); + va_end(Args); + + // make sure we didn't write outside our bounds + for (iCheck = iBufferLimit, iMemStomp = 0; iCheck < (signed)sizeof(strTest); iCheck += 1) + { + if (strTest[iCheck] != cMemChar) + { + iMemStomp += 1; + } + } + + // did the test succeed or fail? + iStrCmp = _NetPrintStrIntCmp(pPreGen, strTest, iExpectedResult, iResult, iBufferLimit, "ovr"); + if ((iStrCmp != 0) || (iMemStomp != 0)) + { + return(1); + } + else + { + return(0); + } +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestFlts + + \Description + Execute a series of floating-point printing tests. + + \Output + int32_t - number of test failures + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestFlts(void) +{ + int32_t iResult = 0; + ZPrintf("netprint: Floating-point comparative tests\n"); + iResult += _NetPrintFloat("%2.0f", 10.0f); + iResult += _NetPrintFloat("%f", pow(2,52)); + iResult += _NetPrintFloat("%f", pow(2,53)); + iResult += _NetPrintFloat("%.15f", 0.00000000000000000000099f); // we don't test .16 here; ms printf doesn't round at 16+ digits + iResult += _NetPrintFloat("%.15f", 0.0099f); + iResult += _NetPrintFloat("%.15f", 0.0000000099f); + iResult += _NetPrintFloat("%f", 1.99f); + iResult += _NetPrintFloat("%f", -1.99); + iResult += _NetPrintFloat("%f", 1.0f); + iResult += _NetPrintFloat("%f", 0.75f); + iResult += _NetPrintFloat("%2.2f", 1.0); + iResult += _NetPrintFloat("%+2.2f", 1.0); + iResult += _NetPrintFloat("%f", -1.99); + iResult += _NetPrintFloat("%.2f", 9.99); + iResult += _NetPrintFloat("%.2f", 9.999); + iResult += _NetPrintFloat("%.2f", -1.999); + iResult += _NetPrintFloat("%.2f", 0.1); + iResult += _NetPrintFloat("%.15f", 3.1415926535897932384626433832795); + + /* this section is for stuff that is not compatible with sprintf() or + is not compatible with the single-param _NetPrintFloat(). For these + tests, we compare against a pre-generated string to make sure our + output is consistently what we expect across all platforms. */ + + // make sure all fp selectors result in %f behavior + iResult += _NetPrintPregen("%e 1.000000 %E 1.000000", "%%e %e %%E %E", 1.0f, 1.0f); + iResult += _NetPrintPregen("%g 1.000000 %G 1.000000", "%%g %g %%G %G", 1.0f, 1.0f); + iResult += _NetPrintPregen("%F 1.000000", "%%F %F", 1.0f); + // test variable width with fp + iResult += _NetPrintPregen(" 1", "%2.*f", 2, 1.0f); + // test a really large number, but less than our maximum + iResult += _NetPrintPregen("9223372036854775808.000000", "%f", pow(2,63)); + // test a really large number, greater than our max + iResult += _NetPrintPregen("0.(BIG)", "%f", pow(2,64)); + + // floating-point test summary + ZPrintf("netprint: %d floating-point test discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestInts + + \Description + Execute a series of integer printing tests. + + \Output + int32_t - number of test failures + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestInts(void) +{ + int32_t iResult = 0; + const int32_t iBits = (signed)sizeof(int32_t)*8; + const int32_t iMaxInt = (1 << (iBits - 1)) + 1; + int32_t iInt0 = 8; + + ZPrintf("netprint: Integer tests\n"); + + iResult += _NetPrintInt("%d = 8", iInt0); + iResult += _NetPrintInt("%+d = +8", iInt0); + iResult += _NetPrintInt("%d = -maxint", iMaxInt); + iResult += _NetPrintInt("char %c = 'a'", 'a'); + iResult += _NetPrintInt("hex %x = ff", 0xff); + iResult += _NetPrintInt("hex %02x = 00", 0); + iResult += _NetPrintInt("oct %o = 10", 010); + iResult += _NetPrintInt("oct %03o = 010", 8); + iResult += _NetPrintLongInt("%llu", 0x900f123412341234ull); + iResult += _NetPrintLongInt("0x%llx", 0x900f123412341234ull); +#if defined(DIRTYCODE_PC) + iResult += _NetPrintLongInt("%I64d", 0x900f123412341234ull); +#endif +#if defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_APPLEIOS) + iResult += _NetPrintLongInt("%qd", 0x900f123412341234ull); +#endif + iResult += _NetPrintPregen("signed -5 = unsigned 4294967291 = hex fffffffb", "signed %d = unsigned %u = hex %x", -5, -5, -5); + iResult += _NetPrintPregen("4294967286,-10", "%u,%d", -10, -10); + iResult += _NetPrintPregen("0,10,20,30,100,200,1000", "%d,%d,%d,%d,%d,%d,%d", 0, 10, 20, 30, 100, 200, 1000); + iResult += _NetPrintPregen("0,-10,-20,-30,-100,-200,-1000", "%d,%d,%d,%d,%d,%d,%d", 0, -10, -20, -30, -100, -200, -1000); + + ZPrintf("netprint: %d integer test discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestPtrs + + \Description + Execute a series of pointer printing tests. + + \Output + int32_t - number of test failures + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestPtrs(void) +{ + int32_t iResult = 0; + char *pStr = "string test", *pNul = NULL; + char strTemp[128]; + + ZPrintf("netprint: Pointer tests\n"); +#if DIRTYCODE_64BITPTR + _NetPrintPregen("p=$123456789abcdef0", "p=%p", (void *)0x123456789abcdef0); + +#if defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) || defined(DIRTYCODE_PS4) + sprintf(strTemp, "p=$%016lx, null p=(null)", (uintptr_t)pStr); +#else + sprintf(strTemp, "p=$%016llx, null p=(null)", (uintptr_t)pStr); +#endif + +#else + _NetPrintPregen("p=$12345678", "p=%p", (void *)0x12345678); + sprintf(strTemp, "p=$%08x, null p=(null)", (uint32_t)pStr); +#endif + iResult += _NetPrintPregen(strTemp, "p=%p, null p=%p", pStr, pNul); + + ZPrintf("netprint: %d pointer test discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestStrs + + \Description + Execute a series of string printing tests. + + \Output + int32_t - number of test failures + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestStrs(void) +{ + int32_t iResult = 0; + char *pStr = "string test"; + wchar_t *pWideStr = L"wide string test"; + + ZPrintf("netprint: String tests\n"); + iResult += _NetPrintStr("string test=%s", pStr); +#if defined(DIRTYCODE_PC) + iResult += _NetPrintStr("wide string test=%S", (const char *)pWideStr); +#else + iResult += _NetPrintStr("wide string test=%ls", (const char *)pWideStr); +#endif + iResult += _NetPrintStr("string test=%s", NULL); + + ZPrintf("netprint: %d string test discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestAlgn + + \Description + Execute a series of string alignment formatting tests. + + \Output + int32_t - number of test failures + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestAlgn(void) +{ + int32_t iResult = 0; + + ZPrintf("netprint: Alignment tests\n"); + + iResult += _NetPrintStr(" left just: \"%-10s\"", "left"); + iResult += _NetPrintStr("right just: \"%10s\"", "right"); + iResult += _NetPrintInt(" 6: %04d zero padded", 6); + iResult += _NetPrintInt(" 6: %-4d left just", 6); + iResult += _NetPrintInt(" 6: %4d right just", 6); + iResult += _NetPrintInt("-6: %04d zero padded", -6); + iResult += _NetPrintInt("-6: %-4d left just", -6); + iResult += _NetPrintInt("-6: %4d right just", -6); + iResult += _NetPrintPregen(" 6: 0006 zero padded, variable-length", " 6: %0*d zero padded, variable-length", 4, 6); + iResult += _NetPrintPregen(" 6: 6 left just, variable-length", " 6: %-*d left just, variable-length", 4, 6); + + iResult += _NetPrintPregen(" a: a left just", " a: %-8c left just", 'a'); + iResult += _NetPrintPregen(" b: b right just", " b: %8c right just", 'b'); + iResult += _NetPrintPregen(" c: c left just, variable-length", " c: %-*c left just, variable-length", 8, 'c'); + iResult += _NetPrintPregen(" d: d right just, variable-length", " d: %*c right just, variable-length", 8, 'd'); + + ZPrintf("netprint: %d alignment test discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestMisc + + \Description + Execute a series of miscelleneous printing tests. + + \Output + int32_t - number of test failures + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestMisc(void) +{ + int32_t iResult = 0; + int32_t iInt1 = 0; + char strTest[256], strTest2[128]; + + ZPrintf("netprint: Misc tests\n"); + + // test %n selector + ds_snzprintf(strTest, sizeof(strTest), "%%n test %n", &iInt1); + ds_snzprintf(strTest2, sizeof(strTest2), "(%d chars written)", iInt1); + ds_strnzcat(strTest, strTest2, sizeof(strTest)); + iResult += _NetPrintStrCmp("%n test (8 chars written)", strTest, "msc"); + + iResult += _NetPrintPregen("# test: 10", "# test: %#d", 10); + iResult += _NetPrintPregen("1 1 1 1 1.000000 1 1 1", "%hd %hhd %ld %lld %f %zd %jd %td", 1, 1, 1l, 1ll, 1.0, 1, 1, 1); + iResult += _NetPrintPregen("testing invalid trailing pct: ", "testing invalid trailing pct: %"); + iResult += _NetPrintPregen("testing valid trailing pct: %", "testing valid trailing pct: %%"); + + ZPrintf("netprint: %d misc test discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestAddr + + \Description + Execute a series of address printing tests. + + \Output + int32_t - number of test failures + + \Version 08/28/2017 (jbrookes) +*/ +/********************************************************************************F*/ +#ifndef DIRTYCODE_NX +static int32_t _NetPrintTestAddr(void) +{ + uint32_t uAddr, uNumAddrs, uWord; + struct sockaddr_in6 SockAddr6; + char strAddr6[48]; + int32_t iResult = 0; + uint16_t aWords[8]; + + struct _NetAddr6 + { + const uint16_t aWords[8]; + const char *pAddrText; + }; + static const struct _NetAddr6 _aAddr6ToString[] = + { + { { 0x2001, 0x0db8, 0x0000, 0x0000, 0x0008, 0x0800, 0x200c, 0x417A }, "2001:db8::8:800:200c:417a" }, // a unicast address + { { 0xff01, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0101 }, "ff01::101" }, // a multicast address + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001 }, "::1" }, // the loopback address + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, "::" }, // the unspecified address + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xc0a8, 0x0001 }, "::ffff:192.168.0.1" }, // an ipv4-mapped ipv6 address + { { 0x0064, 0xff9b, 0x0000, 0x0000, 0x0000, 0x0000, 0xc0a8, 0x0001 }, "64:ff9b::192.168.0.1" }, // a nat64 address + { { 0x0102, 0x0000, 0x0506, 0x0000, 0x0000, 0x0708, 0x090a, 0x0000 }, "102:0:506::708:90a:0" }, // synthetic test + { { 0x1234, 0x5678, 0x9abc, 0xdef0, 0x0000, 0x1234, 0x5678, 0x9abc }, "1234:5678:9abc:def0:0:1234:5678:9abc" } // synthetic test + }; + static const struct _NetAddr6 _aStringToAddr6[] = + { + { { 0x2001, 0x0db8, 0x0000, 0x0000, 0x0008, 0x0800, 0x200c, 0x417A }, "2001:DB8::8:800:200C:417A" }, // a unicast address + { { 0xff01, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0101 }, "FF01::101" }, // a multicast address + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001 }, "::1" }, // the loopback address + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, "::" }, // the unspecified address + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xc0a8, 0x0001 }, "::ffff:c0a8:1" }, // an ipv4-mapped ipv6 address + { { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xc0a8, 0x0001 }, "::ffff:192.168.0.1" }, // a mixed-notation ipv4-mapped ipv6 address + { { 0x0064, 0xff9b, 0x0000, 0x0000, 0x0000, 0x0000, 0xc0a8, 0x0001 }, "64:ff9b::c0a8:1" }, // a nat64 address + { { 0x0064, 0xff9b, 0x0000, 0x0000, 0x0000, 0x0000, 0xc0a8, 0x9b18 }, "64:ff9b::192.168.155.24" }, // a mixed-notation nat64 address + { { 0x0102, 0x0000, 0x0506, 0x0000, 0x0000, 0x0708, 0x090a, 0x0000 }, "102:0:506::708:90a:0" }, // synthetic test + { { 0x1234, 0x5678, 0x9abc, 0xdef0, 0x0000, 0x1234, 0x5678, 0x9abc }, "1234:5678:9abc:def0:0:1234:5678:9abc" } // synthetic test + }; + + // test addr6 to string conversion + uNumAddrs = sizeof(_aAddr6ToString) / sizeof(_aAddr6ToString[0]); + for (uAddr = 0; uAddr < uNumAddrs; uAddr += 1) + { + _NetFormatAddr6(&SockAddr6, _aAddr6ToString[uAddr].aWords, 0); + SockaddrInGetAddrText((struct sockaddr *)&SockAddr6, strAddr6, sizeof(strAddr6)); + iResult += _NetPrintStrCmp(_aAddr6ToString[uAddr].pAddrText, strAddr6, "addr"); + } + + // test string to addr6 conversion + uNumAddrs = sizeof(_aStringToAddr6)/sizeof(_aStringToAddr6[0]); + for (uAddr = 0; uAddr < uNumAddrs; uAddr += 1) + { + SockaddrInSetAddrText((struct sockaddr *)&SockAddr6, _aStringToAddr6[uAddr].pAddrText); + for (uWord = 0; uWord < 8; uWord += 1) + { + aWords[uWord] = SocketNtohs(*(uint16_t *)(SockAddr6.sin6_addr.s6_addr+(uWord*2))); + } + iResult += _NetPrintMemCmp((const uint8_t *)_aStringToAddr6[uAddr].aWords, sizeof(_aStringToAddr6[uAddr].aWords), (const uint8_t *)aWords, sizeof(aWords), "addr"); + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestCust + + \Description + Execute a series of custom printing tests. These are for selectors that + are specific to the DirtySock platform string formatting functions. + + \Output + int32_t - number of test failures + + \Version 05/05/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestCust(void) +{ + int32_t iResult = 0; + struct sockaddr SockAddr4; + struct sockaddr_in6 SockAddr6, SockAddr6_2, SockAddr6_3; + uint8_t aSin6Addr[16] = { 0x5a, 0x23, 0x01, 0x32, 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xde, 0x00, 0x02 }; + uint32_t uAddr; + + SockaddrInit(&SockAddr4, AF_INET); + SockaddrInSetAddr(&SockAddr4, 0xC0A80001); + SockaddrInSetPort(&SockAddr4, 3658); + + // create an IPv4-mapped IPv6 address + ds_memclr(&SockAddr6, sizeof(SockAddr6)); + uAddr = SockaddrInGetAddr(&SockAddr4); + SockAddr6.sin6_family = AF_INET6; + SockAddr6.sin6_port = SocketNtohs(SockaddrInGetPort(&SockAddr4)); + SockAddr6.sin6_flowinfo = 0; + SockAddr6.sin6_addr.s6_addr[10] = 0xff; + SockAddr6.sin6_addr.s6_addr[11] = 0xff; + SockAddr6.sin6_addr.s6_addr[12] = (uint8_t)(uAddr >> 24); + SockAddr6.sin6_addr.s6_addr[13] = (uint8_t)(uAddr >> 16); + SockAddr6.sin6_addr.s6_addr[14] = (uint8_t)(uAddr >> 8); + SockAddr6.sin6_addr.s6_addr[15] = (uint8_t)(uAddr >> 0); + + // create a simulated full IPv6 address + SockaddrInit6(&SockAddr6_2, AF_INET6); + SockAddr6_2.sin6_port = SocketNtohs(3658); + ds_memcpy(&SockAddr6_2.sin6_addr, aSin6Addr, sizeof(SockAddr6_2.sin6_addr)); + + // create a NAT64 address + ds_memcpy(&SockAddr6_3, &SockAddr6, sizeof(SockAddr6_3)); + SockAddr6_3.sin6_addr.s6_addr[1] = 0x64; + SockAddr6_3.sin6_addr.s6_addr[2] = 0xff; + SockAddr6_3.sin6_addr.s6_addr[3] = 0x9b; + SockAddr6_3.sin6_addr.s6_addr[10] = 0x00; + SockAddr6_3.sin6_addr.s6_addr[11] = 0x00; + + ZPrintf("netprint: Custom tests\n"); + + iResult += _NetPrintPregen("addr=192.168.0.1", "addr=%a", SockaddrInGetAddr(&SockAddr4)); + iResult += _NetPrintPregen("addr=255.255.255.255", "addr=%a", 0xffffffff); + iResult += _NetPrintPregen("addr=[192.168.0.1]:3658", "addr=%A", &SockAddr4); + iResult += _NetPrintPregen("addr=[::ffff:192.168.0.1]:3658", "addr=%A", &SockAddr6); + iResult += _NetPrintPregen("addr=[64:ff9b::192.168.0.1]:3658", "addr=%A", &SockAddr6_3); + iResult += _NetPrintPregen("addr=[5a23:132:ff12::12de:2]:3658", "addr=%A", &SockAddr6_2); + iResult += _NetPrintPregen("'dflt' = 'dflt'", "'dflt' = '%C'", 'dflt'); + iResult += _NetPrintPregen("'d*l*' = 'd*l*'", "'d*l*' = '%C'", ('d' << 24) | ('l' << 8)); + + ZPrintf("netprint: %d custom test discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + + return(iResult); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestOver + + \Description + Execute a series of printing tests exercising the overflow functionality. + + \Output + int32_t - number of test failures + + \Version 02/15/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestOver(void) +{ + const char strOver[] = "<<>>"; + int32_t iResult0 = 0, iResult1 = 0; + + // test some overflow scenarios with ds_snzprintf2 + ZPrintf("netprint: Overflow tests\n"); + iResult1 += _NetPrintOverflow( 0, "", 14, "%s", strOver); + iResult1 += _NetPrintOverflow( 1, "", 14, "%s", strOver); + iResult1 += _NetPrintOverflow(12, "<<>", 14, "%s", strOver); + iResult1 += _NetPrintOverflow(15, "<<>>", 14, "%s", strOver); + iResult1 += _NetPrintOverflow(20, " <<>", 20, "%20s", strOver); + iResult1 += _NetPrintOverflow(20, "<<>> ", 20, "%-20s", strOver); + iResult1 += _NetPrintOverflow(21, " <<>>", 20, "%20s", strOver); + iResult1 += _NetPrintOverflow(16, "-<<>>", 16, "-%s-", strOver); + iResult1 += _NetPrintOverflow(17, "-<<>>-", 16, "-%s-", strOver); + + ZPrintf("netprint: %d overflow test discrepencies\n", iResult1); + ZPrintf("netprint: ------------------------------------\n"); + return(iResult0+iResult1); +} + +/*F********************************************************************************/ +/*! + \Function _NetPrintTestSpam + + \Description + Execute a series of printing tests exercising the spam suppression + + \Output + int32_t - number of test failures + + \Version 04/05/2016 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetPrintTestSpam(void) +{ + int32_t iTest; + + // test suppression of multiple identical lines, with output forced by a non-identical line + NetPrintf(("netprint: rate limit test #1 start\n")); + for (iTest = 0; iTest < 32; iTest += 1) + { + NetPrintf(("netprint: testing rate limiter\n")); + } + NetPrintf(("netprint: rate limit test #1 finish\n")); + + // test suppression of multiple identical lines, with output forced by timeout + NetPrintf(("netprint: rate limit test #2 start\n")); + for (iTest = 0; iTest < 32; iTest += 1) + { + NetPrintf(("netprint: testing rate limiter\n")); + NetConnSleep(30); + } + NetPrintf(("netprint: rate limit test #2 finish\n")); + + return(0); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdNetPrint + + \Description + Test the ds_vsnprintf function + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - standard return value + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdNetPrint(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iResult = 0; + + ZPrintf("netprint: ------------------------------------\n"); + ZPrintf("netprint: Testing ds_snzprintf() vs sprintf()\n"); + ZPrintf("netprint: ------------------------------------\n"); + + iResult += _NetPrintTestStrs(); + iResult += _NetPrintTestFlts(); + iResult += _NetPrintTestInts(); + iResult += _NetPrintTestPtrs(); + + #ifndef DIRTYCODE_NX + iResult += _NetPrintTestAddr(); + iResult += _NetPrintTestCust(); + #endif + + iResult += _NetPrintTestAlgn(); + iResult += _NetPrintTestMisc(); + iResult += _NetPrintTestOver(); + iResult += _NetPrintTestSpam(); + + ZPrintf("netprint: Test results: %d total discrepencies\n", iResult); + ZPrintf("netprint: ------------------------------------\n"); + + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/privilege.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/privilege.c new file mode 100644 index 00000000..22d40b1b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/privilege.c @@ -0,0 +1,385 @@ +/*H*************************************************************************************/ +/*! + \File privilege.c + + \Description + Reference application for the privilege api. + + \Copyright + Copyright (c) Electronic Arts 2014. ALL RIGHTS RESERVED. + + \Version 18/02/2014 (amakoukji) +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" + +#if defined(DIRTYCODE_PS4) +#include +#include +#endif + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/misc/privilegeapi.h" + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct PrivaAppT +{ + PrivilegeApiRefT *pPrivApi; + int32_t iCurrentQueryId; + + uint8_t bStarted; + + unsigned char bZCallback; +} PrivaAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _PrivApiCreate(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _PrivApiDestroy(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _PrivApiCheckPriv(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _PrivApiAbort(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static int32_t _CmdPrivTickCb(ZContext *argz, int32_t argc, char *argv[]); + +/*** Variables *************************************************************************/ + +// Private variables +static T2SubCmdT _Ula_Commands[] = +{ + { "create", _PrivApiCreate }, + { "destroy", _PrivApiDestroy }, + { "check", _PrivApiCheckPriv }, + { "abort", _PrivApiAbort }, + { "", NULL } +}; + +static PrivaAppT _Priv_App; + +// Public variables + +/*** Private Functions *****************************************************************/ +/*F********************************************************************************/ +/*! + \Function _CmdPrivTickCb + + \Description + Priv callback, called after command has been issued. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback, or zero to terminate + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static int32_t _CmdPrivTickCb(ZContext *argz, int32_t argc, char *argv[]) +{ + PrivaAppT *pApp = (PrivaAppT*)&_Priv_App; + int32_t iResult = 0; + + // check for kill + if (argc == 0) + { + ZPrintf("%s: killed\n", argv[0]); + PrivilegeApiDestroy(pApp->pPrivApi); + return(0); + } + + // update module + if (pApp->pPrivApi != NULL && pApp->iCurrentQueryId != -1) + { + iResult = PrivilegeApiCheckResult(pApp->pPrivApi, pApp->iCurrentQueryId); + + if (iResult < 0) + { + ZPrintf("%s: error in privcheck for %d with code %d. Possibly Aborted? \n", argv[0], pApp->iCurrentQueryId, iResult); + pApp->iCurrentQueryId = -1; + } + else if (iResult & PRIVILEGEAPI_STATUS_IN_PROGRESS) + { + + } + else + { + ZPrintf("%s: Result of priv check %d = %d \n", argv[0], pApp->iCurrentQueryId, iResult); + PrivilegeApiReleaseRequest(pApp->pPrivApi, pApp->iCurrentQueryId); + pApp->iCurrentQueryId = -1; + } + } + + // keep recurring + return(ZCallback(&_CmdPrivTickCb, 17)); +} + +/*F*************************************************************************************/ +/*! + \Function _PrivApiCreate + + \Description + UserList create + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _PrivApiCreate(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + PrivaAppT *pApp = (PrivaAppT*)&_Priv_App; +#if defined(DIRTYCODE_PS4) + int32_t iResult = 0; +#endif + + if ((bHelp == TRUE) || (argc != 1 && argc != 2)) + { + ZPrintf(" usage: %s create \n", argv[0]); + return; + } + + if (pApp->bStarted) + { + ZPrintf("%s: already created\n", argv[0]); + return; + } + + pApp->iCurrentQueryId = -1; +#if defined(DIRTYCODE_PS4) + if ((iResult = sceSysmoduleLoadModule(SCE_SYSMODULE_NP_COMMERCE)) != SCE_OK) + { + ZPrintf("%s: error loading commerce module with code %d\n", argv[0], iResult); + return; + } + if ((iResult = sceSysmoduleLoadModule(SCE_SYSMODULE_MESSAGE_DIALOG)) != SCE_OK) + { + ZPrintf("%s: error loading message module with code %d\n", argv[0], iResult); + return; + } +#endif + + // allocate ProtoUdp module + if ((pApp->pPrivApi = PrivilegeApiCreate()) == NULL) + { + ZPrintf("%s: unable to create userlist module\n", argv[0]); + return; + } + + pApp->bStarted = TRUE; + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + ZCallback(_CmdPrivTickCb, 17); + } +} + +/*F*************************************************************************************/ +/*! + \Function _PrivApiDestroy + + \Description + UserList destroy + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _PrivApiDestroy(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + PrivaAppT *pApp = (PrivaAppT*)&_Priv_App; + + if ((bHelp == TRUE) || (argc != 2)) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: not yet created\n", argv[0]); + return; + } + + PrivilegeApiDestroy(pApp->pPrivApi); + pApp->bStarted = FALSE; +} + +/*F*************************************************************************************/ +/*! + \Function _PrivApiCheckPriv + + \Description + Fetch friends list + + \Input *_pApp - pointer to app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _PrivApiCheckPriv(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + PrivaAppT *pApp = (PrivaAppT*)&_Priv_App; + int32_t iResult; + int32_t iUserIndex = 0; + PrivilegeApiPrivE ePrivId = PRIVILEGEAPI_PRIV_INVALID; + int32_t iHint = 0; + int32_t bUseUI = FALSE; + + if ((bHelp == TRUE) || ((argc != 4) && (argc != 5))) + { + ZPrintf(" usage: %s checkpriv [user index] [privilege id] [use UI?]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: not yet created\n", argv[0]); + return; + } + + if (pApp->iCurrentQueryId > -1) + { + ZPrintf("%s: check busy! Try again later\n", argv[0]); + return; + } + + // get arguments + iUserIndex = (int32_t)strtol(argv[2], NULL, 10); + ePrivId = (PrivilegeApiPrivE)strtol(argv[3], NULL, 10); + if (argc == 5) + { + int iUserIn = (int32_t)strtol(argv[4], NULL, 10); + bUseUI = iUserIn > 0 ? TRUE : FALSE; + } + + iResult = PrivilegeApiCheckPrivilegesAsyncWithUi(pApp->pPrivApi, iUserIndex, &ePrivId, 1, &iHint, bUseUI ? "Test" : NULL); + + if (iResult > 0) + { + pApp->iCurrentQueryId = iResult; + ZPrintf("%s check query %d started\n", argv[0], iResult); + } + else if (iResult == 0) + { + ZPrintf("%s: Privilege Check result %d\n", argv[0], iHint); + } + else + { + ZPrintf("%s: PrivilegeApiCheckPrivilegesAsyncWithUi() failed with code %d\n", argv[0], iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _PrivApiAbort + + \Description + Fetch blocked list + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _PrivApiAbort(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + PrivaAppT *pApp = (PrivaAppT*)&_Priv_App; + int32_t iResult; + int32_t iId = 0; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s abort [query id]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: priv not yet created\n", argv[0]); + return; + } + // get the receive buffer size (use 0=ANY if not specified) + iId = (int32_t)strtol(argv[2], NULL, 10); + iResult = PrivilegeApiAbort(pApp->pPrivApi, iId); + if (iResult < 0) + { + ZPrintf("%s failed with error code\n", argv[0], iResult); + } + else + { + ZPrintf("%s: query %d aborted\n", argv[0], iId); + } +} + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function CmdPriv + + \Description + Priv command. + + \Input *argz - unused + \Input argc - argument count + \Input **argv - argument list + + \Output + int32_t - zero + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +int32_t CmdPriv(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + PrivaAppT *pApp = &_Priv_App; + unsigned char bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Ula_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the privilege api module\n"); + T2SubCmdUsage(argv[0], _Ula_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _PrivApiCreate) && (pApp->pPrivApi == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _PrivApiCreate(pApp, 1, &pCreate, bHelp); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/qos.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/qos.c new file mode 100644 index 00000000..39c8efdd --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/qos.c @@ -0,0 +1,408 @@ +/*H*************************************************************************************/ +/*! + \File qos.c + + \Description + Reference application for QosClient. + + \Copyright + Copyright (c) Electronic Arts 2008. ALL RIGHTS RESERVED. + + \Version 1.0 05/25/2008 (cadam) First Version +*/ +/*************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protoname.h" +#include "DirtySDK/misc/qosclient.h" + +#include "libsample/zlib.h" + +#include "testerregistry.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct QosAppT +{ + QosClientRefT *pQosClient; + + unsigned char bZCallback; +} QosAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _QosCreate(void *pCmdRef, int32_t argc, char **argv, unsigned char bHelp); +static void _QosDestroy(void *pCmdRef, int32_t argc, char **argv, unsigned char bHelp); +static void _QosStart(void *pCmdRef, int32_t argc, char **argv, unsigned char bHelp); +static void _QosControl(void *pCmdRef, int32_t argc, char **argv, unsigned char bHelp); +static void _QosStatus(void *pCmdRef, int32_t argc, char **argv, unsigned char bHelp); + +/*** Variables *************************************************************************/ + +static QosAppT _Qos_App = { NULL, FALSE }; + +static T2SubCmdT _Qos_Commands[] = +{ + { "create", _QosCreate }, + { "destroy", _QosDestroy }, + { "start", _QosStart }, + { "control", _QosControl }, + { "status", _QosStatus }, + { "", NULL } +}; + +/*** Private Functions *****************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CmdQosUsage + + \Description + Display usage information. + + \Input argc - argument count + \Input *argv[] - argument list + + \Output + None. + + \Version 04/25/2008 (cadam) +*/ +/********************************************************************************F*/ +/*static void _CmdQosUsage(int argc, char *argv[]) +{ + if (argc == 2) + { + ZPrintf(" listen for, request, and/or control and get the status of current requests\n"); + ZPrintf(" usage: %s [create|destroy|listen|request|service|cancel|control|status|nattype]", argv[0]); + } + else if (argc == 3) + { + if (!strcmp(argv[2], "create")) + { + ZPrintf(" usage: %s create [serviceport]\n", argv[0]); + } + else if (!strcmp(argv[2], "destroy")) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + } + else if (!strcmp(argv[2], "listen")) + { + ZPrintf(" usage: %s listen [response]\n", argv[0]); + } + else if (!strcmp(argv[2], "request")) + { + ZPrintf(" usage: %s request [address] [probes]\n", argv[0]); + } + else if (!strcmp(argv[2], "service")) + { + ZPrintf(" usage: %s service [address] [serviceid] [probes]\n", argv[0]); + } + else if (!strcmp(argv[2], "cancel")) + { + ZPrintf(" usage: %s cancel [requestid]\n", argv[0]); + } + else if (!strcmp(argv[2], "control")) + { + ZPrintf(" usage: %s control [control] [value] [pvalue]\n", argv[0]); + } + else if (!strcmp(argv[2], "status")) + { + ZPrintf(" usage: %s status [select] [data]\n", argv[0]); + } + else if (!strcmp(argv[2], "nattype")) + { + ZPrintf(" usage: %s nattype\n", argv[0]); + } + } +} +*/ + +/*F*************************************************************************************/ +/*! + \Function _QosCreate + + \Description + Qos subcommand - create qosclient reference + + \Input *pCmdRef + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 04/25/2008 (cadam) First Version +*/ +/**************************************************************************************F*/ +static void _QosCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + QosAppT *pApp = &_Qos_App; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s create [serviceport]\n", argv[0]); + return; + } + + if (pApp->pQosClient != NULL) + { + ZPrintf(" %s: ref has already been created\n", argv[0]); + return; + } + + pApp->pQosClient = QosClientCreate(NULL, "", (int32_t)strtol(argv[2], NULL, 10)); +} + + +/*F*************************************************************************************/ +/*! + \Function _QosDestroy + + \Description + Qos subcommand - destroy qosclient reference + + \Input *pCmdRef + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 04/25/2008 (cadam) First Version +*/ +/**************************************************************************************F*/ +static void _QosDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + QosAppT *pApp = &_Qos_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + QosClientDestroy(pApp->pQosClient); + pApp->pQosClient = NULL; +} + +/*F*************************************************************************************/ +/*! + \Function _QosStart + + \Description + Qos subcommand - issue a QoS service request + + \Input *pCmdRef + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 04/25/2008 (cadam) First Version +*/ +/**************************************************************************************F*/ +static void _QosStart(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + QosAppT *pApp = &_Qos_App; + + if ((bHelp == TRUE) || (argc < 5)) + { + ZPrintf(" usage: %s service [address] [probeport] [profile name]\n", argv[0]); + return; + } + + QosClientStart(pApp->pQosClient, argv[2], (int32_t)strtol(argv[3], NULL, 10), argv[4]); +} + +/*F*************************************************************************************/ +/*! + \Function _QosControl + + \Description + Qos subcommand - QoS control function + + \Input *pCmdRef + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 04/25/2008 (cadam) First Version +*/ +/**************************************************************************************F*/ +static void _QosControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + int32_t iCtrl; + QosAppT *pApp = &_Qos_App; + + if ((bHelp == TRUE) || (argc < 5)) + { + ZPrintf(" usage: %s control [control] [value] [pvalue]\n", argv[0]); + return; + } + + iCtrl = argv[2][0] << 24; + iCtrl |= argv[2][1] << 16; + iCtrl |= argv[2][2] << 8; + iCtrl |= argv[2][3]; + + QosClientControl(pApp->pQosClient, iCtrl, (int32_t)strtol(argv[3], NULL, 10), argv[4]); +} + + +/*F*************************************************************************************/ +/*! + \Function _QosStatus + + \Description + Qos subcommand - QoS status function + + \Input *pCmdRef + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 04/25/2008 (cadam) First Version +*/ +/**************************************************************************************F*/ +static void _QosStatus(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + int32_t iSelect; + QosAppT *pApp = &_Qos_App; + char strBuffer[256]; + + if ((bHelp == TRUE) || (argc < 4)) + { + ZPrintf(" usage: %s status [select] [data]\n", argv[0]); + return; + } + + iSelect = argv[2][0] << 24; + iSelect |= argv[2][1] << 16; + iSelect |= argv[2][2] << 8; + iSelect |= argv[2][3]; + + QosClientStatus(pApp->pQosClient, iSelect, (int32_t)strtol(argv[3], NULL, 10), &strBuffer, sizeof(strBuffer)); +} + +/*F*************************************************************************************/ +/*! + \Function _CmdQosCb + + \Description + Qos idle callback. + + \Input *argz - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output + int32_t - zero + + \Version 1.0 04/25/2008 (cadam) First Version +*/ +/**************************************************************************************F*/ +static int32_t _CmdQosCb(ZContext *argz, int32_t argc, char *argv[]) +{ + QosAppT *pApp = &_Qos_App; + + // check for kill + if (argc == 0) + { + ZPrintf("%s: killed\n", argv[0]); + QosClientDestroy(pApp->pQosClient); + return(0); + } + + // update at fastest rate + return(ZCallback(_CmdQosCb, 1000)); +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function CmdQos + + \Description + QoS command. + + \Input *argz - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output + int32_t - zero + + \Version 1.0 04/25/2008 (cadam) First Version +*/ +/**************************************************************************************F*/ +int32_t CmdQos(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + QosAppT *pApp = &_Qos_App; + unsigned char bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Qos_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" %s: issue a qos request, get the nat information, or get the status of or control a request\n", argv[0]); + T2SubCmdUsage(argv[0], _Qos_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _QosCreate) && (pApp->pQosClient == NULL)) + { + char *pCreate = "create 11204"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _QosCreate(pApp, 1, &pCreate, bHelp); + if (pApp->pQosClient == NULL) + { + ZPrintf(" %s: error creating module\n", argv[0]); + return(0); + } + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + return(ZCallback(_CmdQosCb, 1000)); + } + else + { + return(0); + } +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/registry.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/registry.c new file mode 100644 index 00000000..5081c7e5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/registry.c @@ -0,0 +1,66 @@ +/*H********************************************************************************/ +/*! + \File registry.c + + \Description + Handles registry for tester2. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/platform.h" + +#include "libsample/zlib.h" + +#include "testerregistry.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdRegistry + + \Description + Do some registry operations + + \Input *argz - environment + \Input argc - number of args + \Input *argv[] - argument list + + \Output int32_t - standard return code + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t CmdRegistry(ZContext *argz, int32_t argc, char *argv[]) +{ + if(argc < 1) + { + ZPrintf(" print registry\n"); + ZPrintf(" usage: %s\n", argv[0]); + } + else + { + ZPrintf("registry: printing registry:\n"); + TesterRegistryPrint(); + ZPrintf("registry: done\n"); + } + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/runscript.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/runscript.c new file mode 100644 index 00000000..7ba243d8 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/runscript.c @@ -0,0 +1,189 @@ +/*H********************************************************************************/ +/*! + \File runscript.c + + \Description + Run a script in Tester2. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" + +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testerregistry.h" +#include "testermodules.h" +#include "testercomm.h" +#include "testerclientcore.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _dispatchCommand + + \Description + Dispatch a single command line, locally and remotely. + + \Input *pCmd - pointer to this command eg. run. + \Input *bClientOnly - if true, only run on client side. + \Input *pLine - the command line to be run. + + \Output int32_t - return code; 0 if command ran, -1 if empty or some error + + \Version 30/11/2005 (TE) +*/ +/********************************************************************************F*/ +int32_t _dispatchCommand(char* pCmd, int bClientOnly, char * pLine) +{ + TesterModulesT *pModules; + TesterCommT *pComm = NULL; + int32_t iResult = -1; + + if ((strlen(pLine) == 0) || (pLine[0] == '#')) + { + return(iResult); + } + // execute it locally + if ((pModules = (TesterModulesT *)TesterRegistryGetPointer("MODULES")) == NULL) + { + ZPrintf("%s: could not dispatch script command locally {%s}\n", pCmd, pLine); + } + else + { + ZPrintf("%s: {%s}\n", pCmd, pLine); + // check to see if its a local command + TesterModulesDispatch(pModules, pLine); + iResult = 0; + } + + // try host too... + if (!bClientOnly) + { + // send it to the other side for execution + if ((pComm = TesterRegistryGetPointer("COMM")) == NULL) + { + ZPrintf("%s: could not dispatch command remotely {%s}\n", pCmd, pLine); + } + else + { + TesterCommMessage(pComm, TESTER_MSGTYPE_COMMAND, pLine); + iResult = 0; + } + } + return(iResult); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdRunScript + + \Description + Run a script + + \Input *argz - environment + \Input argc - number of args + \Input *argv[] - argument list + + \Output int32_t - standard return code + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t CmdRunScript(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iFileSize; + char *pFile = NULL, *pLine = NULL, *pEnd = NULL; + TesterClientCoreT *pState = NULL; + char strSleep[100]; //to format a sleep command + static int iSleepTimeStatic =2; //default sleep is 2 secs between commands + + // check usage + if (argc < 2) + { + ZPrintf( "usage: %s - run a script\n", argv[0]); + ZPrintf("usage: %s -s -set sleep secs between script commands\n", argv[0]); + return(-1); + } + if (strcmp (argv[1],"-s") == 0) + { //set the sleep time.. + if (argc < 3) + { + ZPrintf( "usage: %s -s \n", argv[0]); + return(-1); + } + iSleepTimeStatic = atoi(argv[2]); + ZPrintf("%s: sleep between commands set to: %d secs \n", argv[0], iSleepTimeStatic); + return(0); + } + + // otherwise run the script + ZPrintf("%s: running script {%s}\n", argv[0], argv[1]); + + // load the data in + pFile = ZFileLoad(argv[1], &iFileSize, 0); + if(pFile == NULL) + { + ZPrintf("%s: failed to load file {%s}\n", argv[0], argv[1]); + return(0); + } + pState = (TesterClientCoreT *)TesterRegistryGetPointer("CORE"); + + // fire off each line + pLine = pFile; + //NO strtok() --it is static and i/o uses it too!! + if ( (pEnd = strstr(pFile, "\n")) != NULL) + { + *pEnd++ = '\0'; + } + + while(pLine) + { + if ((_dispatchCommand(argv[0], 0, pLine) == 0) && (iSleepTimeStatic > 0)) + { //did something.. so sleep + //format sleep command and sleep to catch up + sprintf (strSleep, "sleep %d", iSleepTimeStatic); + _dispatchCommand( argv[0], 1, strSleep); //clientOnly=1 + } + if (pState) + { // pump i/o between commands.. + TesterClientCoreIdle(pState); + } + + // try to process the next line + pLine = pEnd; + if ( (pLine) &&((pEnd = strstr(pLine, "\n")) != NULL)) + { + *pEnd++ = '\0'; + } + } + + ZPrintf("%s: ran scripts in {%s}\n", argv[0], argv[1]); + + // kill the memory allocated by loadfile + ZMemFree(pFile); + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/secure.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/secure.c new file mode 100644 index 00000000..01c14135 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/secure.c @@ -0,0 +1,152 @@ +/*H********************************************************** + * secure.c + * + * Description: + * + * Test SSL code. + * + * Copyright (c) Tiburon Entertainment, Inc. 2002. + * All rights reserved. + * + * Ver. Description + * 1.0 03/08/2002 (GWS) [Greg Schaefer] Cleanup and revision + * + *H*/ + +// Includes + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/proto/protossl.h" + +#include "libsample/zlib.h" + +#include "testermodules.h" + + +// Constants + +typedef struct SecureRef // module state storage +{ + uint32_t timeout; // operation timeout + + ProtoSSLRefT *ssl; + char url[256]; + +} SecureRef; + + +/*F****************************************************** + * CmdSecureCB + * + * Description: + * Callback to wait for authent completion. + * + * Inputs: + * Context, arg count, arg strings (zlib standard) + * + * Outputs: + * Exit code + * + * Ver. Description + * 1.0 10/4/99 (GWS) Initial version + * + *F*/ +static int32_t _CmdSecureCB(ZContext *argz, int32_t argc, char *argv[]) +{ + SecureRef *ref = (SecureRef *)argz; + + // check for kill + if (argc == 0) + { + ProtoSSLDestroy(ref->ssl); + return(0); + } + + // check for timeout + if (ZTick() > ref->timeout) + { + ZPrintf("%s: timeout\n", argv[0]); + ProtoSSLDestroy(ref->ssl); + return(-1); + } + + // give transaction time + ProtoSSLUpdate(ref->ssl); + + // see if connection complete + if (ProtoSSLStat(ref->ssl, 'stat', NULL, 0) > 0) + { + int32_t len; + char buf[1024]; + + if (ref->url[0] != 0) + { + ProtoSSLSend(ref->ssl, ref->url, -1); + ref->url[0] = 0; + } + + len = ProtoSSLRecv(ref->ssl, buf, sizeof(buf)); + if (len > 0) + { + ZPrintf("[%s]\n", buf); + } + } + + // keep running + return(ZCallback(&_CmdSecureCB, 100)); +} + +/*F****************************************************** + * CmdSecure + * + * Description: + * Test the SSL handler + * + * Inputs: + * Context, arg count, arg strings (zlib standard) + * + * Outputs: + * Exit code + * + * Ver. Description + * 1.0 10/4/99 (GWS) Initial version + * + *F*/ +int32_t CmdSecure(ZContext *argz, int32_t argc, char *argv[]) +{ + SecureRef *ref; + + // handle help + if (argc < 3) + { + ZPrintf(" test protossl module\n"); + ZPrintf(" usage: %s [address] [port] - setup SSL connection\n", argv[0]); + return(0); + } + + // setup connection + ZPrintf("%s: ssl connect\n", argv[0]); + + ref = (SecureRef *) ZContextCreate(sizeof(*ref)); + ref->timeout = ZTick()+120*1000; + + // setup ssl state + ref->ssl = ProtoSSLCreate(); + + // attempt to connect + ProtoSSLConnect(ref->ssl, TRUE, argv[1], 0, strtol(argv[2], NULL, 10)); + + // setup the url + strcpy(ref->url, "GET / HTTP/1.0\r\n" + "Accept: */*\r\n" + "Content-Length: 0\r\n" + "User-Agent: Custom/1.0\r\n" + "\r\n"); + + // wait for reply + return(ZCallback(&_CmdSecureCB, 100)); +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/session.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/session.c new file mode 100644 index 00000000..0d3c2479 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/session.c @@ -0,0 +1,732 @@ +/*H********************************************************************************/ +/*! + \File session.c + + \Description + Test sessions + + \Notes + Test framework largely borrowed from ws.c by James Brookes. + + \Copyright + Copyright (c) Electronic Arts 2013. + + \Version 03/26/2013 (cvienneau) First Version +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtysessionmanager.h" +#include "DirtySDK/dirtysock/dirtyuser.h" +#include "DirtySDK/dirtysock/netconn.h" + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testersubcmd.h" +#include "testermodules.h" + +#if defined(DIRTYCODE_PS4) && !defined(DIRTYCODE_PS5) +#include +#include +#endif + +/*** Defines **********************************************************************/ +#if defined(DIRTYCODE_PS4) && !defined(DIRTYCODE_PS5) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct SessionAppT +{ + DirtySessionManagerRefT *pDirtySessionManager; +} SessionAppT; + +/*** Function Prototypes **********************************************************/ +static void _SessionCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionStatus(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionMaxUsers(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionImage(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionSetup(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionInvite(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionInviteNoDialog(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionAccept(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionAcceptDialog(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SessionAbortRequest(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables ********************************************************************/ + +static T2SubCmdT _Session_Commands[] = +{ + { "create", _SessionCreate }, + { "destroy", _SessionDestroy }, + { "ctrl", _SessionControl }, + { "stat", _SessionStatus }, + { "maxusers", _SessionMaxUsers }, + { "image", _SessionImage }, + { "setup", _SessionSetup }, + { "invite", _SessionInvite }, + { "inviteNoDialog", _SessionInviteNoDialog}, + { "accept", _SessionAccept }, + { "acceptdialog",_SessionAcceptDialog}, + { "abort", _SessionAbortRequest }, + { "", NULL }, +}; + +static SessionAppT _Session_App = { NULL }; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _SessionDestroyApp + + \Description + Destroy app, freeing modules. + + \Input *pApp - app state + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SessionDestroyApp(SessionAppT *pApp) +{ + if (pApp->pDirtySessionManager != NULL) + { + DirtySessionManagerDestroy(pApp->pDirtySessionManager); + } + ds_memclr(pApp, sizeof(*pApp)); +} + +/* + + Session Commands + +*/ + +/*F*************************************************************************************/ +/*! + \Function _SessionCreate + + \Description + Session subcommand - create websocket module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _SessionCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s create\n", argv[0]); + return; + } + + // create a websocket module if it isn't already started + if ((pApp->pDirtySessionManager = DirtySessionManagerCreate()) == NULL) + { + ZPrintf("%s: error creating DirtySessionManager ref.\n", argv[0]); + return; + } +} + +/*F*************************************************************************************/ +/*! + \Function _SessionDestroy + + \Description + Session subcommand - destroy websocket module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _SessionDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + _SessionDestroyApp(pApp); +} + +/*F*************************************************************************************/ +/*! + \Function _SessionControl + + \Description + Session control subcommand - set control options + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _SessionControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + int32_t iCmd, iValue = 0, iValue2 = 0; + void *pValue = NULL; + + if ((bHelp == TRUE) || (argc < 3) || (argc > 6)) + { + ZPrintf(" usage: %s ctrl [cmd] \n", argv[0]); + return; + } + + // get the command + iCmd = ZGetIntArg(argv[2]); + + // get optional arguments + if (argc > 3) + { + iValue = ZGetIntArg(argv[3]); + } + if (argc > 4) + { + iValue2 = ZGetIntArg(argv[4]); + } + if (argc > 5) + { + pValue = argv[5]; + } + + // issue the control call + DirtySessionManagerControl(pApp->pDirtySessionManager, iCmd, iValue, iValue2, pValue); +} + +/*F*************************************************************************************/ +/*! + \Function _SessionStatus + + \Description + Session status subcommand - query module status + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _SessionStatus(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + int32_t iCmd, iResult, iValue = 0, iValue2 = 0, iValue3 = 0; + char strBuffer[512] = ""; + + if ((bHelp == TRUE) || (argc < 3) || (argc > 7)) + { + ZPrintf(" usage: %s stat \n", argv[0]); + return; + } + + // get the command + iCmd = ZGetIntArg(argv[2]); + + // get optional arguments + if (argc > 3) + { + iValue = ZGetIntArg(argv[3]); + } + if (argc > 4) + { + iValue2 = ZGetIntArg(argv[4]); + } + if (argc > 5) + { + iValue3 = ZGetIntArg(argv[5]); + } + + // issue the status call + iResult = DirtySessionManagerStatus2(pApp->pDirtySessionManager, iCmd, iValue, iValue2, iValue3, strBuffer, sizeof(strBuffer)); + + // report result + ZPrintf("Session: DirtySessionManagerStatus('%C') returned %d (\"%s\")\n", iCmd, iResult, strBuffer); +} + +/*F*************************************************************************************/ +/*! + \Function _SessionMaxUsers + + \Description + Wrapper for 'smau' + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 12/09/2013 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _SessionMaxUsers(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + int32_t iResult; + int32_t iMaxUsers; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s \n", argv[0]); + return; + } + + sscanf(argv[2], "%d", &iMaxUsers); + + if ((iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'smau', 0, iMaxUsers, NULL)) < 0) + { + ZPrintf("Session: max users update ('smau') failed with err %d\n", iResult); + return; + } + if ((iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'umau', 0, 0, NULL)) < 0) + { + ZPrintf("Session: max users update ('umau') failed with err %d\n", iResult); + return; + } + + // report result + ZPrintf("Session: successfully initiated session max users update to %d\n", iMaxUsers); +} + +/*F*************************************************************************************/ +/*! + \Function _SessionImage + + \Description + Wrapper for 'simg' + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 6/6/2013 (cvienneau) +*/ +/**************************************************************************************F*/ +static void _SessionImage(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + int32_t iResult; + + // T2 image Blue + uint8_t img_buf[] = {255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 1, 0, 96, 0, 96, 0, 0, 255, 225, 0, 104, 69, 120, 105, 102, 0, 0, 77, 77, 0, 42, 0, 0, 0, 8, 0, 4, 1, 26, 0, 5, 0, 0, 0, 1, 0, 0, 0, 62, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0, 70, 1, 40, 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 1, 49, 0, 2, 0, 0, 0, 18, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 1, 0, 0, 0, 96, 0, 0, 0, 1, 80, 97, 105, 110, 116, 46, 78, 69, 84, 32, 118, 51, 46, 53, 46, 49, 48, 0, 255, 219, 0, 67, 0, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 5, 3, 3, 3, 3, 3, 6, 4, 4, 3, 5, 7, 6, 7, 7, 7, 6, 7, 7, 8, 9, 11, 9, 8, 8, 10, 8, 7, 7, 10, 13, 10, 10, 11, 12, 12, 12, 12, 7, 9, 14, 15, 13, 12, 14, 11, 12, 12, 12, 255, 219, 0, 67, 1, 2, 2, 2, 3, 3, 3, 6, 3, 3, 6, 12, 8, 7, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 255, 192, 0, 17, 8, 0, 36, 0, 64, 3, 1, 34, 0, 2, 17, 1, 3, 17, 1, 255, 196, 0, 31, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 255, 196, 0, 181, 16, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125, 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 255, 196, 0, 31, 1, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 255, 196, 0, 181, 17, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119, 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250, 255, 218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 252, 195, 162, 138, 43, 253, 208, 62, 76, 40, 175, 217, 223, 216, 183, 224, 15, 252, 19, 115, 246, 162, 189, 240, 55, 129, 116, 189, 39, 251, 115, 226, 118, 185, 165, 199, 246, 139, 31, 181, 120, 162, 215, 207, 187, 138, 204, 207, 117, 251, 198, 116, 129, 112, 34, 153, 184, 96, 167, 110, 23, 57, 0, 247, 191, 181, 71, 236, 101, 255, 0, 4, 221, 253, 137, 252, 97, 167, 104, 31, 19, 188, 55, 255, 0, 8, 206, 173, 171, 89, 255, 0, 104, 90, 193, 253, 161, 226, 139, 223, 54, 13, 237, 30, 253, 214, 242, 200, 163, 230, 70, 24, 36, 30, 58, 98, 191, 2, 196, 253, 32, 178, 250, 25, 143, 246, 68, 242, 156, 127, 214, 26, 114, 80, 250, 188, 121, 165, 20, 218, 230, 140, 125, 167, 51, 142, 143, 222, 74, 218, 62, 199, 74, 195, 54, 185, 185, 149, 189, 79, 194, 90, 43, 245, 43, 254, 9, 173, 255, 0, 4, 184, 248, 1, 251, 68, 233, 127, 22, 62, 48, 248, 227, 88, 150, 231, 225, 135, 134, 117, 205, 84, 232, 26, 13, 165, 244, 246, 237, 253, 145, 109, 230, 74, 46, 174, 57, 251, 89, 79, 36, 97, 23, 42, 231, 201, 114, 196, 158, 43, 211, 190, 16, 255, 0, 193, 51, 63, 100, 95, 248, 42, 199, 236, 217, 226, 173, 119, 224, 23, 135, 124, 103, 240, 203, 196, 62, 30, 185, 147, 79, 130, 77, 78, 246, 226, 85, 107, 161, 16, 146, 47, 58, 57, 110, 46, 81, 160, 125, 195, 38, 55, 87, 24, 57, 3, 128, 222, 174, 107, 227, 166, 67, 151, 226, 170, 208, 196, 80, 175, 236, 232, 202, 17, 171, 85, 83, 94, 206, 148, 167, 180, 102, 220, 148, 174, 182, 124, 177, 149, 158, 154, 147, 28, 60, 154, 186, 177, 248, 207, 69, 62, 226, 6, 182, 157, 227, 112, 3, 198, 197, 88, 2, 14, 8, 224, 242, 41, 149, 251, 66, 102, 1, 69, 20, 80, 7, 216, 191, 240, 64, 111, 249, 75, 103, 194, 111, 251, 140, 127, 233, 154, 250, 189, 243, 254, 14, 162, 255, 0, 147, 192, 248, 119, 255, 0, 98, 112, 255, 0, 210, 219, 154, 248, 35, 246, 66, 253, 169, 124, 65, 251, 22, 126, 209, 62, 30, 248, 153, 225, 107, 61, 26, 255, 0, 94, 240, 215, 218, 126, 203, 6, 171, 20, 146, 218, 73, 231, 219, 75, 108, 251, 214, 57, 35, 115, 132, 153, 136, 195, 143, 152, 12, 228, 100, 30, 187, 246, 253, 255, 0, 130, 134, 120, 211, 254, 10, 53, 241, 35, 70, 241, 71, 141, 244, 191, 11, 233, 90, 134, 135, 166, 255, 0, 101, 65, 30, 135, 109, 60, 16, 188, 94, 107, 203, 185, 132, 211, 74, 75, 110, 144, 242, 8, 24, 3, 142, 245, 248, 222, 103, 193, 57, 157, 127, 18, 240, 156, 81, 77, 71, 234, 212, 240, 206, 148, 157, 253, 238, 119, 42, 175, 225, 237, 239, 173, 77, 213, 68, 169, 56, 117, 185, 232, 223, 240, 73, 191, 248, 36, 223, 136, 63, 224, 164, 223, 16, 110, 174, 174, 174, 238, 60, 57, 240, 223, 195, 147, 34, 107, 90, 194, 32, 105, 167, 144, 128, 194, 210, 212, 48, 218, 102, 43, 130, 88, 229, 98, 86, 12, 193, 137, 68, 127, 209, 31, 219, 119, 226, 255, 0, 140, 63, 98, 255, 0, 128, 55, 223, 179, 223, 236, 157, 251, 62, 252, 86, 91, 84, 142, 91, 61, 75, 197, 150, 222, 15, 212, 102, 182, 83, 34, 237, 154, 75, 105, 188, 162, 215, 87, 47, 208, 220, 147, 177, 66, 175, 151, 184, 5, 41, 240, 7, 236, 89, 255, 0, 5, 203, 248, 167, 251, 8, 124, 5, 177, 248, 121, 224, 191, 9, 124, 49, 185, 210, 108, 174, 103, 188, 123, 189, 79, 78, 190, 150, 242, 242, 105, 164, 44, 207, 43, 71, 119, 26, 18, 6, 212, 24, 65, 242, 198, 160, 228, 130, 79, 171, 255, 0, 196, 82, 159, 180, 15, 253, 9, 255, 0, 7, 63, 240, 85, 169, 127, 242, 125, 124, 7, 28, 112, 167, 30, 231, 92, 73, 245, 186, 216, 74, 88, 140, 13, 9, 94, 141, 25, 86, 229, 131, 107, 106, 149, 34, 149, 231, 39, 171, 81, 147, 180, 83, 181, 183, 190, 180, 231, 78, 48, 178, 118, 103, 230, 221, 205, 180, 150, 119, 18, 67, 52, 111, 20, 177, 49, 71, 71, 82, 172, 140, 14, 8, 32, 244, 32, 211, 42, 206, 181, 170, 201, 174, 235, 55, 119, 211, 42, 44, 183, 147, 60, 238, 16, 16, 161, 153, 139, 16, 51, 158, 50, 106, 181, 127, 80, 198, 252, 171, 155, 115, 140, 40, 162, 138, 160, 10, 40, 162, 128, 10, 40, 162, 128, 10, 40, 162, 128, 63, 255, 217}; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s image\n", argv[0]); + return; + } + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'simg', 0, sizeof(img_buf), img_buf); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'uimg', 0, 0, NULL); + + // report result + ZPrintf("Session: new image attached\n"); +} + + +/*F*************************************************************************************/ +/*! + \Function _SessionSetup + + \Description + Wrapper for creating a session with common defaults + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 6/6/2013 (cvienneau) +*/ +/**************************************************************************************F*/ +static void _SessionSetup(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + int32_t iResult; + + // T2 image Green + uint8_t img_buf[] = {255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 1, 0, 96, 0, 96, 0, 0, 255, 225, 0, 104, 69, 120, 105, 102, 0, 0, 77, 77, 0, 42, 0, 0, 0, 8, 0, 4, 1, 26, 0, 5, 0, 0, 0, 1, 0, 0, 0, 62, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0, 70, 1, 40, 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 1, 49, 0, 2, 0, 0, 0, 18, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 1, 0, 0, 0, 96, 0, 0, 0, 1, 80, 97, 105, 110, 116, 46, 78, 69, 84, 32, 118, 51, 46, 53, 46, 49, 48, 0, 255, 219, 0, 67, 0, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 5, 3, 3, 3, 3, 3, 6, 4, 4, 3, 5, 7, 6, 7, 7, 7, 6, 7, 7, 8, 9, 11, 9, 8, 8, 10, 8, 7, 7, 10, 13, 10, 10, 11, 12, 12, 12, 12, 7, 9, 14, 15, 13, 12, 14, 11, 12, 12, 12, 255, 219, 0, 67, 1, 2, 2, 2, 3, 3, 3, 6, 3, 3, 6, 12, 8, 7, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 255, 192, 0, 17, 8, 0, 36, 0, 64, 3, 1, 34, 0, 2, 17, 1, 3, 17, 1, 255, 196, 0, 31, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 255, 196, 0, 181, 16, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125, 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 255, 196, 0, 31, 1, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 255, 196, 0, 181, 17, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119, 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250, 255, 218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 240, 122, 40, 171, 254, 21, 176, 181, 213, 124, 81, 166, 218, 223, 77, 246, 107, 43, 155, 168, 162, 184, 155, 120, 79, 42, 54, 112, 25, 183, 30, 6, 1, 39, 39, 129, 138, 252, 145, 43, 187, 31, 198, 145, 87, 105, 34, 133, 21, 250, 175, 240, 247, 254, 8, 243, 251, 44, 124, 91, 214, 165, 211, 124, 41, 241, 131, 93, 241, 62, 163, 4, 6, 230, 75, 93, 39, 197, 122, 69, 236, 209, 196, 25, 84, 200, 82, 59, 102, 96, 161, 157, 70, 226, 49, 150, 3, 184, 172, 255, 0, 138, 31, 240, 73, 127, 217, 87, 225, 61, 214, 161, 167, 107, 127, 25, 117, 109, 19, 94, 178, 183, 50, 255, 0, 103, 106, 94, 45, 209, 237, 174, 1, 41, 185, 55, 68, 246, 234, 248, 97, 130, 56, 228, 30, 43, 215, 121, 30, 37, 71, 154, 241, 183, 170, 62, 213, 240, 6, 102, 169, 251, 87, 42, 124, 189, 249, 213, 190, 243, 242, 226, 138, 251, 147, 246, 53, 255, 0, 130, 83, 248, 91, 226, 31, 236, 201, 47, 198, 47, 139, 222, 51, 212, 60, 37, 224, 214, 134, 91, 152, 98, 211, 213, 22, 117, 183, 141, 204, 126, 115, 200, 233, 39, 222, 117, 33, 99, 88, 217, 155, 43, 131, 150, 11, 90, 223, 28, 191, 224, 147, 159, 15, 188, 71, 251, 42, 106, 31, 22, 254, 5, 120, 247, 88, 241, 86, 137, 164, 90, 207, 125, 53, 182, 171, 18, 151, 185, 134, 2, 124, 253, 172, 34, 133, 163, 116, 85, 118, 216, 241, 229, 128, 224, 140, 140, 227, 28, 171, 16, 233, 251, 68, 150, 215, 181, 213, 237, 222, 199, 4, 56, 67, 50, 158, 29, 98, 20, 86, 177, 231, 81, 230, 92, 238, 63, 205, 203, 123, 216, 248, 22, 138, 40, 175, 52, 249, 128, 162, 138, 40, 3, 238, 207, 248, 55, 199, 254, 79, 55, 196, 223, 246, 37, 221, 127, 233, 117, 133, 112, 159, 240, 91, 31, 249, 72, 111, 139, 63, 235, 203, 78, 255, 0, 210, 56, 171, 132, 255, 0, 130, 124, 126, 218, 191, 240, 194, 63, 25, 245, 63, 23, 127, 194, 53, 255, 0, 9, 87, 246, 142, 139, 46, 143, 246, 79, 237, 31, 176, 249, 123, 231, 130, 111, 51, 127, 149, 38, 113, 228, 99, 110, 209, 247, 179, 158, 48, 112, 127, 109, 159, 218, 131, 254, 27, 19, 246, 135, 213, 188, 123, 253, 135, 255, 0, 8, 239, 246, 164, 22, 240, 253, 135, 237, 191, 108, 242, 188, 168, 86, 60, 249, 158, 92, 121, 206, 220, 253, 209, 140, 227, 158, 181, 235, 75, 21, 73, 229, 202, 130, 126, 247, 53, 237, 229, 175, 200, 251, 42, 185, 174, 22, 92, 51, 12, 189, 79, 247, 170, 167, 53, 172, 246, 179, 214, 246, 183, 94, 247, 61, 131, 254, 9, 231, 251, 30, 252, 85, 253, 185, 52, 219, 189, 21, 124, 99, 226, 111, 15, 124, 35, 178, 145, 96, 213, 157, 175, 230, 107, 75, 150, 82, 142, 45, 161, 183, 221, 229, 201, 32, 219, 27, 18, 70, 216, 240, 172, 114, 118, 43, 123, 95, 237, 193, 251, 122, 252, 56, 253, 155, 191, 102, 235, 159, 217, 243, 224, 88, 75, 235, 115, 107, 46, 151, 171, 107, 17, 200, 100, 134, 20, 114, 69, 194, 172, 191, 242, 222, 121, 114, 193, 221, 127, 118, 161, 200, 92, 240, 19, 152, 253, 151, 255, 0, 224, 183, 154, 127, 236, 191, 240, 19, 195, 62, 5, 211, 126, 16, 139, 184, 124, 63, 104, 33, 150, 235, 254, 18, 113, 9, 188, 157, 137, 121, 102, 41, 246, 70, 219, 190, 70, 102, 219, 185, 176, 8, 25, 56, 205, 116, 63, 20, 191, 224, 224, 79, 248, 89, 95, 12, 188, 71, 225, 207, 248, 84, 159, 98, 254, 223, 210, 238, 116, 223, 180, 127, 194, 81, 230, 121, 30, 116, 77, 30, 253, 191, 100, 27, 177, 187, 56, 200, 206, 58, 138, 239, 163, 91, 9, 79, 13, 203, 10, 214, 155, 86, 111, 150, 77, 250, 46, 200, 250, 28, 22, 51, 38, 195, 101, 158, 203, 15, 140, 229, 175, 56, 218, 114, 116, 231, 39, 103, 246, 34, 244, 81, 87, 210, 250, 223, 126, 214, 252, 226, 162, 138, 43, 230, 143, 203, 2, 138, 40, 160, 2, 138, 40, 160, 2, 138, 40, 160, 2, 138, 40, 160, 15, 255, 217}; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s setup\n", argv[0]); + return; + } + + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'simg', 0, sizeof(img_buf), img_buf); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'smau', 0, 22, NULL); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'slid', 0, 8, "12345678"); + + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'snam', 0, 0, "Default Name"); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'snam', 0, 'enUS', "enUS Name"); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'snam', 0, 'frFR', "frFR Name"); + + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'ssta', 0, 0, "Default Status"); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'ssta', 0, 'enUS', "enUS Status"); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'ssta', 0, 'frFR', "frFR Status"); + + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'scre', 0, 0, NULL); + + // report result + ZPrintf("Session: Setup Complete\n"); +} + +/*F*************************************************************************************/ +/*! + \Function _SessionInvite + + \Description + Wrapper for opening invite dialog + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 6/6/2013 (cvienneau) +*/ +/**************************************************************************************F*/ +static void _SessionInvite(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + int32_t iResult; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s invite\n", argv[0]); + return; + } + + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'imus', 16, 0, NULL); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'imsg', 0, 0, "Play T2 with me"); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'iued', 1, 0, NULL); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'osid', 0, 0, NULL); + + // report result + ZPrintf("Session: Invite Complete\n"); +} + + + +/*F*************************************************************************************/ +/*! + \Function _SessionInviteNoDialog + + \Description + Wrapper for _SessionInviteNoDialog + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/4/2013 (tcho) +*/ +/**************************************************************************************F*/ +static void _SessionInviteNoDialog(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + #if defined(DIRTYCODE_PS4) + + SessionAppT *pApp = &_Session_App; + int32_t iResult; + + if (bHelp == TRUE) + { + ZPrintf("usage: %s inviteNoDialog \n", argv[0]); + return; + } + + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'imus', 16, 0, NULL); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'imsg', 0, 0, "Play T2 with me"); + + for(int32_t index = 2; index < argc; ++index) + { + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'ianp', 0, 0, argv[index]); + } + + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'sind', 1, 0, NULL); + iResult = DirtySessionManagerControl(pApp->pDirtySessionManager, 'icnp', 0, 0, NULL); + + // report result + ZPrintf("Session: Invite Complete\n"); + + #else + + ZPrintf("Session: Invite without dialog not support\n"); + + #endif +} + +/*F*************************************************************************************/ +/*! + \Function _SessionAccept + + \Description + Examin current invites and join one of them + Must be done after DirtySessionManagerControl('ginv') has completed + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 6/6/2013 (cvienneau) +*/ +/**************************************************************************************F*/ +static void _SessionAccept(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + int32_t i, iInviteCount, iUserIndex = 0; + DirtyUserT user; + char strUserName[32]; + char strMessage[512]; + char strSessionId[64]; + strUserName[0] = 0; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s accept \n", argv[0]); + return; + } + + // get optional arguments + if (argc > 3) + { + iUserIndex = ZGetIntArg(argv[3]); + } + + iInviteCount = DirtySessionManagerStatus2(pApp->pDirtySessionManager, 'ginv', iUserIndex, 0, 0, NULL, 0); + + ZPrintf(" Current Invitations: %d\n", iInviteCount); + + //print out info from all the invites + for (i = 0; i < iInviteCount; i++) + { + DirtySessionManagerStatus2(pApp->pDirtySessionManager, 'gims', iUserIndex, i, 0, strMessage, sizeof(strMessage)); + DirtySessionManagerStatus2(pApp->pDirtySessionManager, 'gisi', iUserIndex, i, 0, strSessionId, sizeof(strSessionId)); + DirtySessionManagerStatus2(pApp->pDirtySessionManager, 'giun', iUserIndex, i, 0, &user, sizeof(user)); + #if defined(DIRTYCODE_PS4) + SceNpAccountId accountId; + DirtyUserToNativeUser(&accountId, sizeof(accountId), &user); + ds_snzprintf(strUserName, sizeof(strUserName), "%llu", accountId); + #endif + ZPrintf(" * %d %s %llu %s\n", iInviteCount, strSessionId, strUserName, strMessage); + + } + + if (iInviteCount > 0) + { + ZPrintf(" Joining: %s\n", strSessionId); + //join what ever last invite was processes + DirtySessionManagerControl(pApp->pDirtySessionManager, 'sjoi', 0, 0, strSessionId); + DirtySessionManagerControl(pApp->pDirtySessionManager, 'uinv', 0, 0, NULL); + } + + // report result + ZPrintf("Session: Accept Complete\n"); +} + +/*F*************************************************************************************/ +/*! + \Function _SessionAcceptDialog + + \Description + Join the session the user selected from the invitation dialog 'osrd' + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 6/6/2013 (cvienneau) +*/ +/**************************************************************************************F*/ +static void _SessionAcceptDialog(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + char strSessionId[64]; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s acceptdialog\n", argv[0]); + return; + } + + //check to see that we do have a session id + DirtySessionManagerStatus(pApp->pDirtySessionManager, 'gssi', strSessionId, sizeof(strSessionId)); + + if (strSessionId[0] == '\0') + { + //it doen't look the there is a selected session + ZPrintf("Session: no session slected to join\n"); + } + else + { + ZPrintf("Session: joining selected session\n"); + DirtySessionManagerControl(pApp->pDirtySessionManager, 'sjoi', 0, 0, strSessionId); + DirtySessionManagerControl(pApp->pDirtySessionManager, 'cssi', 0, 0, NULL); + } + + // report result + ZPrintf("Session: AcceptDialog Complete\n"); +} + +/*F*************************************************************************************/ +/*! + \Function _SessionAbortRequest + + \Description + Create several requests then abort them. + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 6/6/2013 (cvienneau) +*/ +/**************************************************************************************F*/ +static void _SessionAbortRequest(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + SessionAppT *pApp = &_Session_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s abort\n", argv[0]); + return; + } + + //que getting invites for every index + DirtySessionManagerControl(pApp->pDirtySessionManager, 'ginv', 0, 0, NULL); + DirtySessionManagerControl(pApp->pDirtySessionManager, 'ginv', 0, 0, NULL); + DirtySessionManagerControl(pApp->pDirtySessionManager, 'ginv', 0, 0, NULL); + DirtySessionManagerControl(pApp->pDirtySessionManager, 'ginv', 0, 0, NULL); + + //give the items a chance to process just a bit + //NetConnSleep(500); + + //abort all outsanding requests + DirtySessionManagerControl(pApp->pDirtySessionManager, 'abrt', 0, 0, NULL); + + // report result + ZPrintf("Session: Abort Test Complete\n"); +} + +/*F********************************************************************************/ +/*! + \Function _CmdSessionCb + + \Description + Update Session command + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_T -standard return value + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdSessionCb(ZContext *argz, int32_t argc, char *argv[]) +{ + SessionAppT *pApp = &_Session_App; + + // check for kill + if (argc == 0) + { + _SessionDestroyApp(pApp); + ZPrintf("%s: killed\n", argv[0]); + return(0); + } + + // give life to the module + if (pApp->pDirtySessionManager != NULL) + { + // update the module + DirtySessionManagerUpdate(pApp->pDirtySessionManager); + } + + // keep running + return(ZCallback(&_CmdSessionCb, 16)); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdSession + + \Description + Session (WebSocket) command + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - standard return value + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdSession(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + SessionAppT *pApp = &_Session_App; + int32_t iResult = 0; + uint8_t bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Session_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the DirtySessionManager module\n"); + T2SubCmdUsage(argv[0], _Session_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _SessionCreate) && (pApp->pDirtySessionManager == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _SessionCreate(pApp, 1, &pCreate, bHelp); + iResult = ZCallback(_CmdSessionCb, 16); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + // one-time install of periodic callback + if (pCmd->pFunc == _SessionCreate) + { + iResult = ZCallback(_CmdSessionCb, 16); + } + return(iResult); +} + +#endif //defined(DIRTYCODE_PS4) + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/sleep.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/sleep.c new file mode 100644 index 00000000..57639349 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/sleep.c @@ -0,0 +1,92 @@ +/*H********************************************************************************/ +/*! + \File net.c + + \Description + Handles SLEEP for tester2 + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" + +#include "libsample/zlib.h" + +#include "testerregistry.h" +#include "testercomm.h" +#include "testerclientcore.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdSleep + + \Description + Sleep for a while + + \Input *argz - environment + \Input argc - number of args + \Input *argv[] - argument list + + \Output int32_t - standard return code + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t CmdSleep(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iTime, iTick; + TesterClientCoreT *pCore; + TesterCommT *pComm; + + //milliseconds to sleep before pumping I/O.. + #define TICK_STEP 100 + + pCore = (TesterClientCoreT *)TesterRegistryGetPointer("CORE"); + + // don't recurse + if (argc < 2) + { + ZPrintf(" sleep for the given amount of time\n"); + ZPrintf(" usage: %s \n", argv[0]); + } + else + { + iTime = atoi(argv[1]) *1000; + ZPrintf("%s: sleep for [%d] seconds.\n", argv[0], iTime/1000); + for (iTick = 0; iTick < iTime; iTick += TICK_STEP) + { + // pump output when sleeping; This makes "sleep" client only. + TesterClientCoreIdle(pCore); + ZSleep(TICK_STEP); //NOTE: ZSleep calls NetConnIdle.. + } + } + + // now, reset comm timer since we were sleeping.. + if ((pComm = (TesterCommT *)TesterRegistryGetPointer("COMM")) != NULL) + { + // slept - so bump timeout.. + pComm->uLastSendTime = ZTick(); + } + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/socket.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/socket.c new file mode 100644 index 00000000..ece667db --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/socket.c @@ -0,0 +1,565 @@ +/*H*************************************************************************************/ +/*! + \File socket.c + + \Description + Reference application for the socket module. + + \Copyright + Copyright (c) Electronic Arts 2011. ALL RIGHTS RESERVED. + + \Version 01/20/2011 (mclouatre) +*/ +/*************************************************************************************H*/ + + +/*** Include files *********************************************************************/ +#include +#include // for sscanf() + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" + +#include "libsample/zlib.h" + +#include "DirtySDK/dirtysock/dirtynet.h" +#include "testersubcmd.h" +#include "testermodules.h" + + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ +typedef struct SocketAppT +{ + SocketT *pSocket; + int32_t iSocketType; + int32_t bSocketBound; + int32_t iVerbosityLevel; + int32_t bAverageCalculationCycleStarted; + uint32_t uAverageCalculationCycleStartTick; + + // reception data + uint8_t bReceiving; + uint8_t recvBuffer[64]; + int32_t iUdpDatagramsReceived; + int32_t iReadIntervalInMs; + uint32_t uLastRecvTick; + struct sockaddr source; + + // transmission data + uint8_t bTransmitting; + uint8_t xmitBuffer[64]; + int32_t iUdpDatagramsSent; + int32_t iWriteIntervalInMs; + uint32_t uLastSentTick; + struct sockaddr destination; + +} SocketAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _SocketOpen(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _SocketBind(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _SocketClose(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _SocketRecvFrom(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _SocketSendTo(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _SetDebugVerbosity(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); + + + +/*** Variables *************************************************************************/ + +// Private variables +static T2SubCmdT _Socket_Commands[] = +{ + { "open", _SocketOpen }, + { "bind", _SocketBind }, + { "close", _SocketClose }, + { "sendto", _SocketSendTo }, + { "recvfrom", _SocketRecvFrom }, + { "verbose", _SetDebugVerbosity }, + { "", NULL } +}; + +static SocketAppT _Socket_App; +uint8_t _SocketApp_bInitialized; + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function _CmdSocketCb + + \Description + Socket idle callback. + + \Input *argz - unused + \Input argc - argument count + \Input **argv - argument list + + \Output + int32_t - zero + + \Version 01/20/2011 (mclouatre) +*/ +/**************************************************************************************F*/ +static int32_t _CmdSocketCb(ZContext *argz, int32_t argc, char **argv) +{ + SocketAppT *pApp = &_Socket_App; + int32_t iInterval; + + // if socket no more exists, stop invoking this callback + if (!pApp->pSocket) + { + return(0); + } + + // display send/recv average every 5 sec + if (pApp->bReceiving || pApp->bTransmitting) + { + if (pApp->bAverageCalculationCycleStarted) + { + // check if cycle completed + if (NetTickDiff(NetTick(), pApp->uAverageCalculationCycleStartTick) > 10000) + { + uint32_t uSendingAverage; + uint32_t uReceivingAverage; + + pApp->bAverageCalculationCycleStarted = FALSE; + + uSendingAverage = pApp->iUdpDatagramsSent / 10; + + uReceivingAverage = pApp->iUdpDatagramsReceived / 10; + + ZPrintf(" LAST 10 SECONDS -> dgrams sent=%d | dgrams rcved=%d | avg dgrams sent=%d/sec | avg dgrams rcved=%d/sec\n", + pApp->iUdpDatagramsSent, pApp->iUdpDatagramsReceived, uSendingAverage, uReceivingAverage); + } + } + + if (!pApp->bAverageCalculationCycleStarted) + { + // mark cycle as started + pApp->bAverageCalculationCycleStarted = TRUE; + + // reset cycle start tick + pApp->uAverageCalculationCycleStartTick = NetTick(); + + // reset counters + pApp->iUdpDatagramsSent = 0; + pApp->iUdpDatagramsReceived = 0; + } + } + + if (pApp->iSocketType == SOCK_DGRAM) + { + // is receiving enabled? + if (pApp->bReceiving) + { + uint32_t uCurrentTick = NetTick(); + int32_t iRecvLen; + int32_t iSourceLen; + int32_t iSourcePort; + char strSourceAddrText[16]; + iRecvLen = SocketRecvfrom(pApp->pSocket, (char *)&pApp->recvBuffer, sizeof(pApp->recvBuffer), 0, &pApp->source, &iSourceLen); + if (iRecvLen > 0) + { + pApp->iUdpDatagramsReceived++; + SockaddrInGetAddrText(&pApp->source, strSourceAddrText, sizeof(strSourceAddrText)); + iSourcePort = SockaddrInGetPort(&pApp->source); + if (pApp->iVerbosityLevel > 3) + { + ZPrintf(" received 1 datagram (size %d) from %s:%d at time %d (delta = %d ms)\n", + iRecvLen, strSourceAddrText, iSourcePort, uCurrentTick, NetTickDiff(uCurrentTick, pApp->uLastRecvTick)); + } + pApp->uLastRecvTick = uCurrentTick; + } + else + { + if (pApp->iVerbosityLevel > 3) + { + ZPrintf(" SocketRecvfrom() returned %d at time %d\n", iRecvLen, uCurrentTick); + } + } + } + + // is transmitting enabled? + if (pApp->bTransmitting) + { + uint32_t uCurrentTick = NetTick(); + int32_t iSentLen; + int32_t iDestinationPort; + char strDestAddrText[16]; + iSentLen = SocketSendto(pApp->pSocket, (char *)&pApp->xmitBuffer, sizeof(pApp->recvBuffer), 0, &pApp->destination, sizeof(pApp->destination)); + if (iSentLen > 0) + { + pApp->iUdpDatagramsSent++; + SockaddrInGetAddrText(&pApp->destination, strDestAddrText, sizeof(strDestAddrText)); + iDestinationPort = SockaddrInGetPort(&pApp->destination); + if (pApp->iVerbosityLevel > 3) + { + ZPrintf(" sent 1 datagram (size %d) to %s:%d at time %d (delta =%d ms)\n", + iSentLen, strDestAddrText, iDestinationPort, uCurrentTick, NetTickDiff(uCurrentTick, pApp->uLastSentTick)); + } + pApp->uLastSentTick = uCurrentTick; + } + else + { + if (pApp->iVerbosityLevel > 3) + { + ZPrintf(" SocketSendto() returned %d at time %d\n", iSentLen, uCurrentTick); + } + } + } + } + else + { + ZPrintf(" send/recv processing not implemented for raw or tcp sockets (current socket type = %d)\n", pApp->iSocketType); + return(0); + } + + if (pApp->iReadIntervalInMs < pApp->iWriteIntervalInMs) + { + iInterval = pApp->iReadIntervalInMs; + } + else + { + iInterval = pApp->iWriteIntervalInMs; + } + return(ZCallback(_CmdSocketCb, iInterval)); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketOpen + + \Description + Socket subcommand - open a socket + + \Input *pApp - pointer to Socket app + \Input argc - argument count + \Input **argv - argument list + + \Version 01/20/2011 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _SocketOpen(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + SocketAppT *pApp = (SocketAppT *)_pApp; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s %s \n", argv[0], argv[1]); + return; + } + + if (strcmp(argv[2], "raw") == 0) + { + pApp->iSocketType = SOCK_RAW; + } + else if (strcmp(argv[2], "udp") == 0) + { + pApp->iSocketType = SOCK_DGRAM; + } + else if (strcmp(argv[2], "tcp") == 0) + { + pApp->iSocketType = SOCK_STREAM; + } + else + { + ZPrintf(" invalid socket type\n", argv[2]); + ZPrintf(" usage: %s %s \n", argv[0], argv[1]); + return; + } + + // make sure we don't already have a socket + if (pApp->pSocket != NULL) + { + ZPrintf(" %s: there is already a socket opened\n", argv[0]); + return; + } + + pApp->pSocket = SocketOpen(AF_INET, pApp->iSocketType, 0); + if (pApp->pSocket) + { + ZPrintf(" %s: opened %s socket (ref=%p)\n", argv[0], argv[2], pApp->pSocket); + } + else + { + ZPrintf(" %s: failed to open socket\n", argv[0]); + return; + } + pApp->bSocketBound = FALSE; + + // read and write interval defaul to 1 sec until recv or send is initiated + pApp->iReadIntervalInMs = 1000; + pApp->iWriteIntervalInMs = 1000; + + // register periodic callback + ZCallback(_CmdSocketCb, pApp->iReadIntervalInMs); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketBind + + \Description + Socket subcommand - bind a socket to a local IP address and port + + \Input *pApp - pointer to Socket app + \Input argc - argument count + \Input **argv - argument list + + \Version 01/20/2011 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _SocketBind(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + SocketAppT *pApp = (SocketAppT *)_pApp; + int32_t iResult; + struct sockaddr bindAddr; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s %s \n", argv[0], argv[1]); + return; + } + + if (!pApp->pSocket) + { + ZPrintf(" %s: socket is not yet opened\n", argv[0]); + return; + } + + if (pApp->bSocketBound) + { + ZPrintf(" %s: socket (ref=%p) is already bound\n", argv[0], pApp->pSocket); + return; + } + + // initialize bindAddr from input string + if ((SockaddrInParse(&bindAddr, argv[2]) & 0x3) != 0x3) + { + ZPrintf(" %s: badly formatted parameter\n", argv[0]); + return; + } + + iResult = SocketBind(pApp->pSocket, &bindAddr, sizeof(bindAddr)); + if (iResult < 0) + { + ZPrintf(" %s: socket (ref=%p) failed to bind to %s - err = %d\n", argv[0], pApp->pSocket, argv[2], iResult); + } + else + { + SocketInfo(pApp->pSocket, 'bind', 0, &bindAddr, sizeof(bindAddr)); + ZPrintf(" %s: socket (ref=%p) bound to %A\n", argv[0], pApp->pSocket, &bindAddr); + pApp->bSocketBound = TRUE; + } +} + +/*F*************************************************************************************/ +/*! + \Function _SocketClose + + \Description + Socket subcommand - close a socket + + \Input *pApp - pointer to Socket app + \Input argc - argument count + \Input **argv - argument list + + \Version 01/20/2011 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _SocketClose(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + SocketAppT *pApp = (SocketAppT *)_pApp; + + if ((bHelp == TRUE) || (argc != 2)) + { + ZPrintf(" usage: %s %s\n", argv[0], argv[1]); + return; + } + + if (!pApp->pSocket) + { + ZPrintf(" %s: socket is not yet opened\n", argv[0]); + return; + } + + SocketClose(pApp->pSocket); + pApp->bSocketBound = FALSE; + ZPrintf(" %s: closed socket (ref=%p)\n", argv[0], pApp->pSocket); + pApp->pSocket = NULL; +} + +/*F*************************************************************************************/ +/*! + \Function _SocketRecvFrom + + \Description + Socket subcommand - recvfrom operation on previously created socket + + \Input *pApp - pointer to Socket app + \Input argc - argument count + \Input **argv - argument list + + \Version 01/20/2011 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _SocketRecvFrom(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + SocketAppT *pApp = (SocketAppT *)_pApp; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s %s \n", argv[0], argv[1]); + return; + } + + if (!pApp->pSocket) + { + ZPrintf(" %s: socket is not yet opened\n", argv[0]); + return; + } + + if (!pApp->bSocketBound) + { + ZPrintf(" %s: socket (ref=%p) is not yet bound\n", argv[0], pApp->pSocket); + return; + } + + // get read rate + sscanf(argv[2], "%d", &pApp->iReadIntervalInMs); + + ZPrintf(" %s: data reception enabled on socket (ref=%p) - read rate is: 1 read operation every %d ms\n", argv[0], pApp->pSocket, pApp->iReadIntervalInMs); + pApp->bReceiving = TRUE; +} + +/*F*************************************************************************************/ +/*! + \Function _SocketSendTo + + \Description + Socket subcommand - sendto operation on previously created socket + + \Input *pApp - pointer to Socket app + \Input argc - argument count + \Input **argv - argument list + + \Version 01/20/2011 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _SocketSendTo(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + SocketAppT *pApp = (SocketAppT *)_pApp; + + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s %s \n", argv[0], argv[1]); + return; + } + + if (!pApp->pSocket) + { + ZPrintf(" %s: socket is not yet opened\n", argv[0]); + return; + } + + // initialize destination from input string + if ((SockaddrInParse(&pApp->destination, argv[2]) & 0x3) != 0x3) + { + ZPrintf(" %s: badly formatted parameter\n", argv[0]); + return; + } + + if (!pApp->bSocketBound) + { + ZPrintf(" %s: socket (ref=%p) is not yet bound\n", argv[0], pApp->pSocket); + return; + } + + // get write rate + sscanf(argv[3], "%d", &pApp->iWriteIntervalInMs); + + ZPrintf(" %s: data transmission enabled on socket (ref=%p) - write rate is: 1 write operation every %d ms\n", argv[0], pApp->pSocket, pApp->iWriteIntervalInMs); + + pApp->bTransmitting = TRUE; +} + +/*F*************************************************************************************/ +/*! + \Function _SetDebugVerbosity + + \Description + Socket subcommand - sendto operation on previously created socket + + \Input *pApp - pointer to Socket app + \Input argc - argument count + \Input **argv - argument list + + \Version 01/20/2011 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _SetDebugVerbosity(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + SocketAppT *pApp = (SocketAppT *)_pApp; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s %s \n", argv[0], argv[1]); + return; + } + + // get write rate + sscanf(argv[2], "%d", &pApp->iVerbosityLevel); +} + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function CmdSocket + + \Description + Socket command. + + \Input *argz - unused + \Input argc - argument count + \Input **argv - argument list + + \Output + int32_t - zero + + \Version 01/20/2011 (mclouatre) First Version +*/ +/**************************************************************************************F*/ +int32_t CmdSocket(ZContext *argz, int32_t argc, char **argv) +{ + T2SubCmdT *pCmd; + SocketAppT *pApp = &_Socket_App; + unsigned char bHelp; + + // initialize state + if (!_SocketApp_bInitialized) + { + ds_memclr(pApp, sizeof(*pApp)); + _SocketApp_bInitialized = TRUE; + } + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Socket_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" exercises DirtySock socket API\n"); + T2SubCmdUsage(argv[0], _Socket_Commands); + return(0); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/source.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/source.c new file mode 100644 index 00000000..4094f807 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/source.c @@ -0,0 +1,234 @@ +/*H********************************************************************************/ +/*! + \File source.c + + \Description + A tester command to implement 'source' (like unix) command + + \Copyright + Copyright (c) 2012 Electronic Arts Inc. + + \Version 10/10/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testerregistry.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct SourceRefT +{ + TesterModulesT *pModulesState; + char *pScriptData; + char *pScriptLine; + int32_t iFileSize; + int32_t iCurrProc; + int32_t iCurrRslt; + int32_t iCurrStat; + int32_t iNumCmds; + int32_t iNumCmdsFailed; +} SourceRefT; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _SourceExecuteCmd + + \Description + 'source' callback, called after command has been issued. + + \Input *pRef - command module state + \Input *pScriptLine - current line of source script + + \Output + char * - pointer to next line of script, or NULL + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_SourceExecuteCmd(SourceRefT *pRef, char *pScriptLine) +{ + char *pScriptEnd; + + // find end of command + for (pScriptEnd = pScriptLine; (*pScriptEnd != '\r') && (*pScriptEnd != '\n') && (*pScriptEnd != '\0'); pScriptEnd += 1) + ; + + // terminate and skip to next command start + if (*pScriptEnd != '\0') + { + for (*pScriptEnd++ = '\0'; ((*pScriptEnd == '\r') || (*pScriptEnd == '\n')) && (*pScriptEnd != '\0'); pScriptEnd += 1) + ; + } + + // execute the line + ZPrintf("source: executing '%s'\n", pScriptLine); + pRef->iCurrStat = TesterModulesDispatch(pRef->pModulesState, pScriptLine); + pRef->iNumCmds += 1; + // get process id of process we just executed + pRef->iCurrProc = ZGetPid(); + + /* restore current environment... required because TexterModulesDispatch()/ZInvoke() leaves the dispatched + command as the current environment, which messes up our subsequent call to ZCallback() */ + ZSet((ZContext *)pRef); + + // return pointer to next command, or NULL if no more commands + return((*pScriptEnd != '\0') ? pScriptEnd : NULL); +} + +/*F********************************************************************************/ +/*! + \Function _CmdSourceCb + + \Description + 'source' callback, called after command has been issued. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback, or zero to terminate + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdSourceCb(ZContext *argz, int32_t argc, char *argv[]) +{ + SourceRefT *pRef = (SourceRefT *)argz;\ + int32_t iStatus; + + // check for kill + if (argc == 0) + { + ZPrintf("%s: killed\n", argv[0]); + return(0); + } + + // if we have a current process running, check it + if (pRef->iCurrProc != -1) + { + /* $$ TODO - this works to determine the command is no longer running, but getting the final status/return code + doesn't work; it is not available since the command is no more */ + if ((iStatus = ZGetStatusPid(pRef->iCurrProc)) != ZLIB_STATUS_RUNNING) + { + // determine process result -- first check for immediate-exit result + if (pRef->iCurrStat != ZLIB_STATUS_RUNNING) + { + iStatus = pRef->iCurrStat; + } + else if (iStatus == ZLIB_STATUS_UNKNOWN) + { + iStatus = 0; // any 'async' command will do this until we can implement a way to get the exit code + } + ZPrintf("%s: process %d complete (result=%d)\n", argv[0], pRef->iCurrProc, iStatus); + // track completion status + pRef->iNumCmdsFailed += iStatus != 0; + // clear current command + pRef->iCurrProc = -1; + } + } + + // issue a new command? + if (pRef->iCurrProc == -1) + { + // if we have more commands, start a new one + if (pRef->pScriptLine != NULL) + { + pRef->pScriptLine = _SourceExecuteCmd(pRef, pRef->pScriptLine); + } + else + { + // we're done, time to quit + ZMemFree(pRef->pScriptData); + ZPrintf("%s: done (%d commands, %d failed)\n", argv[0], pRef->iNumCmds, pRef->iNumCmdsFailed); + return(0); + } + } + + // keep recurring + return(ZCallback(&_CmdSourceCb, 16)); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdSource + + \Description + Upnp command. This command starts the ProtoUpnp module. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdSource(ZContext *argz, int32_t argc, char *argv[]) +{ + SourceRefT *pRef; + + // handle basic help + if (argc != 2) + { + ZPrintf("usage: %s \n", argv[0]); + return(0); + } + + // otherwise run the script + ZPrintf("%s: running script '%s'\n", argv[0], argv[1]); + + // allocate context + if ((pRef = (SourceRefT *)ZContextCreate(sizeof(*pRef))) == NULL) + { + ZPrintf("%s: could not allocate state\n", argv[0]); + return(-1); + } + ds_memclr(pRef, sizeof(*pRef)); + + // get modules state + if ((pRef->pModulesState = (TesterModulesT *)TesterRegistryGetPointer("MODULES")) == NULL) + { + ZPrintf("%s: could not get module state for dispatch\n", argv[0]); + return(-2); + } + + // load the script + if ((pRef->pScriptData = ZFileLoad(argv[1], &pRef->iFileSize, 0)) == NULL) + { + ZPrintf("%s: failed to load file {%s}\n", argv[0], argv[1]); + return(-3); + } + pRef->pScriptLine = pRef->pScriptData; + pRef->iCurrProc = -1; + + // set up recurring callback + return(ZCallback(_CmdSourceCb, 16)); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/stream.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/stream.c new file mode 100644 index 00000000..aded589b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/stream.c @@ -0,0 +1,1285 @@ +/*H********************************************************************************/ +/*! + \File stream.c + + \Description + Test the ProtoStream module. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 11/16/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/proto/protostream.h" +#include "DirtySDK/xml/xmlparse.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +#define STREAMER_USECALLBACK (TRUE) + +#define STREAMER_DEBUG (FALSE) + +#define STREAM_DEFAULTSKIP (15) // 15 seconds + +#define STREAM_MAXURLS (4) + +/*** Type Definitions *************************************************************/ + +typedef struct StreamStationT +{ + char strName[32]; + char strInfo[64]; + char strType[32]; + char strUrl[STREAM_MAXURLS][256]; +} StreamStationT; + +typedef struct StreamPlaylistT +{ + int32_t iNumStations; + StreamStationT Stations[1]; //!< variable-length array of stations +} StreamPlaylistT; + +typedef struct StreamCmdRefT +{ + ProtoStreamRefT *pProtoStream; + StreamPlaylistT *pPlaylist; + int32_t *pRandom; + int32_t iStartTick; + int32_t iSkipTick; + int32_t iSkipTime; + int32_t iCurStation; + int32_t iCurUrl; + int32_t iMetaInterval; + int32_t iMetaOffset; + int32_t iMetaSize; + uint8_t bRandomPlay; + char strMetaBuffer[(255*16)+1]; + char strLastMetaBuffer[(255*16)+1]; +} StreamCmdRefT; + +/*** Function Prototypes **********************************************************/ + +static void _SubcmdStreamCreate(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SubcmdStreamDestroy(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SubcmdStreamOpen(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SubcmdStreamClose(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SubcmdStreamSkip(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _SubcmdStreamControl(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables ********************************************************************/ + +//! subcommand table +static T2SubCmdT _Stream_Commands[] = +{ + { "create", _SubcmdStreamCreate }, + { "destroy", _SubcmdStreamDestroy }, + { "open", _SubcmdStreamOpen }, + { "close", _SubcmdStreamClose }, + { "skip", _SubcmdStreamSkip }, + { "ctrl", _SubcmdStreamControl }, + { "", NULL } +}; + +//! single instance of the stream module +static StreamCmdRefT *_Stream_pCmdRef = NULL; + +//! basic playlist with sportscenter ref +static StreamPlaylistT _Playlist = +{ + 1, + { + { + "ESPN Sportscenter", + "sportscenter 32k/sec", + "Sports", + { + "http://stestbesl01.beta.ea.com/espnradio/sportscenter/sportscenter.mp3", + } + } + }, +}; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _StreamOpen + + \Description + Open the stream specified by the given url + + \Input *pCmdRef - module state + \Input *pStation - station reference + \Input iRestartFreq - restart frequency + + \Output + None. + + \Version 11/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamOpen(StreamCmdRefT *pCmdRef, StreamStationT *pStation, int32_t iRestartFreq) +{ + char strTempUrl[128], *pUrl; + + // ref url + pUrl = pStation->strUrl[pCmdRef->iCurUrl]; + + // validate url + if (strncmp(pUrl, "http://", 7)) + { + ZPrintf("stream: invalid URL '%s'\n", pUrl); + return; + } + + // see if URL needs a trailing slash + if (!strchr(pUrl+7, '/')) + { + ds_strnzcpy(strTempUrl, pUrl, sizeof(strTempUrl)); + ds_strnzcat(strTempUrl, "/", sizeof(strTempUrl)); + pUrl = strTempUrl; + } + + // open the stream + if (pStation->strName[0] != '\0') + { + ZPrintf("stream: opening [%d] %s (url=%s)\n", (int32_t)(pStation - pCmdRef->pPlaylist->Stations), pStation->strName, pUrl); + } + else + { + ZPrintf("stream: opening url %s\n", pUrl); + } + ProtoStreamOpen(pCmdRef->pProtoStream, pUrl, iRestartFreq); + pCmdRef->iMetaInterval = 0; + pCmdRef->iMetaSize = 0; + pCmdRef->iMetaOffset = 0; +} + +/*F********************************************************************************/ +/*! + \Function _StreamXmlGetString + + \Description + Get an xml entity's string contents + + \Input *pXml - source xml to get data from + \Input *pName - name of entity + \Input *pBuffer - [out] storage for entity contents + \Input iBufSize - size of buffer pointed to by pBuffer + + \Output + None. + + \Version 11/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _StreamXmlGetString(const char *pXml, const char *pName, char *pBuffer, int32_t iBufSize) +{ + ds_memclr(pBuffer, iBufSize); + if ((pXml = XmlFind(pXml, pName)) != NULL) + { + return(XmlContentGetString(pXml, pBuffer, iBufSize, "")); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _StreamDisplayPlaylist + + \Description + Display the given playlist + + \Input *pCmdRef - module state + \Input *pPlaylist - playlist to display + + \Output + None. + + \Version 08/18/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamDisplayPlaylist(StreamCmdRefT *pCmdRef, StreamPlaylistT *pPlaylist) +{ + int32_t iStation; + + for (iStation = 0; iStation < pPlaylist->iNumStations; iStation++) + { + ZPrintf("stream: [%2d] %s\n", iStation, pPlaylist->Stations[iStation].strName); + } +} + +/*F********************************************************************************/ +/*! + \Function _StreamOpenPlaylist + + \Description + Open the playlist specified by the given filename + + \Input *pCmdRef - module state + \Input *pName - name of playlist to open + + \Output + None. + + \Version 08/16/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamOpenPlaylist(StreamCmdRefT *pCmdRef, const char *pName) +{ + const char *pPlayFile, *pXml, *pXml2; + int32_t iFileSize, iNumStations; + + // open the playlist + ZPrintf("stream: opening playlist '%s'\n", pName); + if ((pPlayFile = ZFileLoad(pName, &iFileSize, FALSE)) == NULL) + { + ZPrintf("stream: error opening playlist\n"); + return; + } + + // see if it is a playlist + if ((pXml = XmlFind(pPlayFile, "playlist.station")) == NULL) + { + ZPrintf("stream: file is not a playlist\n", pName); + return; + } + + // see how many stations are defined + for (pXml2 = pXml, iNumStations = 1; ; ) + { + if ((pXml2 = XmlSkip(pXml2)) == NULL) + { + break; + } + if (!strncmp(pXml2+1, "station", 7)) + { + iNumStations += 1; + } + } + + ZPrintf("stream: parsed %d stations\n", iNumStations); + if (iNumStations != 0) + { + int32_t iPlaylistSize, iRandom, iRandIdx; + StreamStationT *pStation; + int32_t *pTmpRandom; + + // allocate a new playlist structure + if ((pCmdRef->pPlaylist != NULL) && (pCmdRef->pPlaylist != &_Playlist)) + { + ZMemFree(pCmdRef->pPlaylist); + } + if (pCmdRef->pRandom != NULL) + { + ZMemFree(pCmdRef->pPlaylist); + } + iPlaylistSize = sizeof(*pCmdRef->pPlaylist) + (sizeof(pCmdRef->pPlaylist->Stations[0]) * (iNumStations - 1)); + pCmdRef->pPlaylist = ZMemAlloc(iPlaylistSize); + ds_memclr(pCmdRef->pPlaylist, iPlaylistSize); + pCmdRef->pPlaylist->iNumStations = iNumStations; + + // parse xml into new playlist + for (pStation = pCmdRef->pPlaylist->Stations; ; ) + { + if (!strncmp((char *)pXml+1, "station", 7)) + { + _StreamXmlGetString(pXml, "station.name", pStation->strName, sizeof(pStation->strName)); + _StreamXmlGetString(pXml, "station.info", pStation->strInfo, sizeof(pStation->strInfo)); + _StreamXmlGetString(pXml, "station.type", pStation->strType, sizeof(pStation->strType)); + _StreamXmlGetString(pXml, "station.url0", pStation->strUrl[0], sizeof(pStation->strUrl[0])); + _StreamXmlGetString(pXml, "station.url1", pStation->strUrl[1], sizeof(pStation->strUrl[1])); + _StreamXmlGetString(pXml, "station.url2", pStation->strUrl[2], sizeof(pStation->strUrl[2])); + _StreamXmlGetString(pXml, "station.url3", pStation->strUrl[3], sizeof(pStation->strUrl[3])); + pStation += 1; + } + if ((pXml = XmlSkip(pXml)) == NULL) + { + break; + } + } + + // create new buffer for randomizing playlist + pCmdRef->pRandom = ZMemAlloc(iNumStations*sizeof(*pCmdRef->pRandom)); + + // create playlist indices + pTmpRandom = ZMemAlloc((iNumStations+1)*sizeof(*pCmdRef->pRandom)); + for (iRandom = 0; iRandom < iNumStations; iRandom++) + { + pTmpRandom[iRandom] = iRandom; + } + + // generate random walk through playlist + for (iRandom = 0; iRandom < (iNumStations-1); iRandom++) + { + iRandIdx = rand() % (iNumStations-iRandom); + pCmdRef->pRandom[iRandom] = pTmpRandom[iRandIdx]; + memmove(&pTmpRandom[iRandIdx], &pTmpRandom[iRandIdx+1], (iNumStations-iRandIdx-1)*sizeof(*pTmpRandom)); + } + pCmdRef->pRandom[iRandom] = pTmpRandom[0]; + + // free temp buffer + ZMemFree(pTmpRandom); + } + + // done with the file + ZMemFree((void *)pPlayFile); +} + +/*F********************************************************************************/ +/*! + \Function _StreamGetRandIndex + + \Description + Get index of given station in random playlist. + + \Input *pCmdRef - module state + \Input iIndex - station index in playlist + + \Output + int32_t - index of station in random playlist + + \Version 11/14/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _StreamGetRandIndex(StreamCmdRefT *pCmdRef, int32_t iIndex) +{ + int32_t iStation; + for (iStation = 0; iStation < pCmdRef->pPlaylist->iNumStations; iStation++) + { + if (pCmdRef->pRandom[iStation] == iIndex) + { + return(iStation); + } + } + NetPrintf(("stream: unable to find station %d in random playlist\n", iIndex)); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _StreamClose + + \Description + Close the current stream, if any + + \Input *pCmdRef - module state + + \Output + None. + + \Version 11/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamClose(StreamCmdRefT *pCmdRef) +{ + // close the stream + ZPrintf("stream: closing active stream\n"); + ProtoStreamClose(pCmdRef->pProtoStream); +} + +/*F********************************************************************************/ +/*! + \Function _StreamGetCurStation + + \Description + Get index of current station (possibly applying random index) + + \Input *pCmdRef - module state + + \Output + int32_t - index of current station + + \Version 11/24/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _StreamGetCurStation(StreamCmdRefT *pCmdRef) +{ + int32_t iStation; + iStation = (pCmdRef->bRandomPlay) ? pCmdRef->pRandom[pCmdRef->iCurStation] : pCmdRef->iCurStation; + return(iStation); +} + +/*F********************************************************************************/ +/*! + \Function _StreamNextUrl + + \Description + Skip to next url for current station + + \Input *pCmdRef - module state + + \Output + None. + + \Version 11/22/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamNext(StreamCmdRefT *pCmdRef) +{ + pCmdRef->iCurUrl = pCmdRef->iCurUrl + 1; + _StreamOpen(pCmdRef, &pCmdRef->pPlaylist->Stations[_StreamGetCurStation(pCmdRef)], PROTOSTREAM_FREQ_IMMED); +} + +/*F********************************************************************************/ +/*! + \Function _StreamSkip + + \Description + Skip to next stream. + + \Input *pCmdRef - module state + \Input iCurTick - current tick + + \Output + None. + + \Version 11/22/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamSkip(StreamCmdRefT *pCmdRef, int32_t iCurTick) +{ + // switch to next station + pCmdRef->iCurStation = (pCmdRef->iCurStation + 1) % pCmdRef->pPlaylist->iNumStations; + + // switch to next stream + pCmdRef->iCurUrl = 0; + + // open the stream + _StreamOpen(pCmdRef, &pCmdRef->pPlaylist->Stations[_StreamGetCurStation(pCmdRef)], PROTOSTREAM_FREQ_IMMED); + + // set next update + pCmdRef->iSkipTick = iCurTick; +} + +/*F********************************************************************************/ +/*! + \Function _StreamParseHeader + + \Description + Parse data from from icy header response + + \Input *pCmdRef - module state + \Input *pHeader - http response header + + \Output + None. + + \Version 03/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamParseHeader(StreamCmdRefT *pCmdRef, const char *pHeader) +{ + char strData[256], *pData, *pHeaderInfo; + + // parse meta-interval data, if present + if ((pHeaderInfo = ds_stristr(pHeader, "icy-metaint")) != NULL) + { + // parse meta interval + while (!isdigit(*pHeaderInfo)) + { + pHeaderInfo += 1; + } + pCmdRef->iMetaInterval = strtol(pHeaderInfo, NULL, 10); + NetPrintf(("stream: metadata interval is %d\n", pCmdRef->iMetaInterval)); + + pCmdRef->iMetaOffset = pCmdRef->iMetaInterval; + } + + // parse icy-name, if present + if ((pHeaderInfo = ds_stristr(pHeader, "icy-name:")) != NULL) + { + pHeaderInfo += sizeof("icy-name:")-1; + for (pData = strData; *pHeaderInfo != '\r'; pHeaderInfo += 1, pData += 1) + { + *pData = *pHeaderInfo; + } + *pData = '\0'; + ZPrintf("=====================================================================================================\n"); + ZPrintf("stream: %s\n", strData); + } + + // parse icy-genre, if present + if ((pHeaderInfo = ds_stristr(pHeader, "icy-genre:")) != NULL) + { + pHeaderInfo += sizeof("icy-genre:")-1; + for (pData = strData; *pHeaderInfo != '\r'; pHeaderInfo += 1, pData += 1) + { + *pData = *pHeaderInfo; + } + *pData = '\0'; + ZPrintf("stream: %s\n", strData); + } + + // parse icy-br, if present + if ((pHeaderInfo = ds_stristr(pHeader, "icy-br:")) != NULL) + { + pHeaderInfo += sizeof("icy-br:")-1; + for (pData = strData; *pHeaderInfo != '\r'; pHeaderInfo += 1, pData += 1) + { + *pData = *pHeaderInfo; + } + *pData = '\0'; + ZPrintf("stream: %skbps\n", strData); + ZPrintf("=====================================================================================================\n"); + } +} + +/*F********************************************************************************/ +/*! + \Function _StreamProcessMetaData + + \Description + Process metadata embedded in mp3 stream. + + \Input *pCmdRef - module state + \Input *pData - stream data + \Input iSize - amount of data available for processing + + \Output + int32_t - number of bytes consumed + + \Version 03/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _StreamProcessMetaData(StreamCmdRefT *pCmdRef, const uint8_t *pData, int32_t iSize) +{ + int32_t iConsume, iConsumed; + + // process all data + for ( iConsumed = 0; iSize > 0; ) + { + // if we're processing mp3 data, bail + if (pCmdRef->iMetaOffset > 0) + { + break; + } + + // are we processing a metadata header? + if (pCmdRef->iMetaSize > 0) + { + if (iSize >= pCmdRef->iMetaSize) + { + iConsume = pCmdRef->iMetaSize; + pCmdRef->iMetaOffset = pCmdRef->iMetaInterval; + } + else + { + iConsume = iSize; + } + + // copy metadata into buffer + ds_strsubzcat(pCmdRef->strMetaBuffer, sizeof(pCmdRef->strMetaBuffer), (char *)pData, iConsume); + + // skip metadata + pData += iConsume; + iSize -= iConsume; + + // mark metasize as consumed + pCmdRef->iMetaSize -= iConsume; + iConsumed += iConsume; + } + // do we have a metadata header? + else if (pCmdRef->iMetaOffset == 0) + { + pCmdRef->iMetaSize = *pData * 16; + pCmdRef->strMetaBuffer[0] = '\0'; + pData += 1; + iSize -= 1; + iConsumed += 1; + } + + // if we're done with the header, reset the offset + if (pCmdRef->iMetaSize == 0) + { + // did we get any metadata? + if (pCmdRef->strMetaBuffer[0] != '\0') + { + // did the metadata change since the last update? + if (strcmp(pCmdRef->strLastMetaBuffer, pCmdRef->strMetaBuffer)) + { + char *pTitle, *pEndTitle; + + // first, save the update + strcpy(pCmdRef->strLastMetaBuffer, pCmdRef->strMetaBuffer); + + // try and find title for display + if ((pTitle = strstr(pCmdRef->strMetaBuffer, "StreamTitle")) != NULL) + { + if ((pTitle = strchr(pTitle, '\'')) != NULL) + { + time_t uTime; + + pTitle += 1; + if ((pEndTitle = strchr(pTitle, ';')) != NULL) + { + *(pEndTitle-1) = '\0'; + } + else if ((pEndTitle = strchr(pTitle, '\'')) != NULL) + { + *pEndTitle = '\0'; + } + + if ((uTime = ds_timeinsecs()) != 0) + { + struct tm CurTime; + ds_secstotime(&CurTime, uTime); + ZPrintf("stream: %02d:%02d now playing - %s\n", CurTime.tm_hour, CurTime.tm_min, pTitle); + } + else + { + ZPrintf("stream: now playing - %s\n", pTitle); + } + } + } + } + } + + // reset offset + pCmdRef->iMetaOffset = pCmdRef->iMetaInterval; + } + } + + return(iConsumed); +} + +/*F********************************************************************************/ +/*! + \Function _StreamDone + + \Description + Process stream completion + + \Input *pCmdRef - module state + \Input iCurTick - current tick + + \Output + None. + + \Version 03/31/2008 (jbrookes) +*/ +/********************************************************************************F*/ +static void _StreamDone(StreamCmdRefT *pCmdRef, int32_t iCurTick) +{ + if (pCmdRef->pPlaylist->iNumStations > 1) + { + int32_t iNextUrl = pCmdRef->iCurUrl + 1; + if ((iNextUrl < STREAM_MAXURLS) && (pCmdRef->pPlaylist->Stations[_StreamGetCurStation(pCmdRef)].strUrl[iNextUrl][0] != '\0')) + { + _StreamNext(pCmdRef); + } + else + { + _StreamSkip(pCmdRef, iCurTick); + } + } + else + { + // if it's a one-shot, just stop playback + ProtoStreamClose(pCmdRef->pProtoStream); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamCallback + + \Description + ProtoStream callback handler + + \Input *pProtoStream - protostream module state + \Input eStatus - callback status + \Input *pBuffer - pointer to buffered data, or NULL if no data + \Input iBufSize - amount of data available in buffer + \Input *pUserData - user data pointer + + \Output + int32_t - number of bytes consumed + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +#if STREAMER_USECALLBACK +static int32_t _ProtoStreamCallback(ProtoStreamRefT *pProtoStream, ProtoStreamStatusE eStatus, const uint8_t *pBuffer, int32_t iBufSize, void *pUserData) +{ + StreamCmdRefT *pCmdRef = (StreamCmdRefT *)pUserData; + const uint8_t *pBufStart = pBuffer; + int32_t iResult; + #if STREAMER_DEBUG + int32_t iTick; + #endif + + // validate cmdref + if ((pCmdRef == NULL) || (pCmdRef->pProtoStream != pProtoStream)) + { + NetPrintf(("stream: error -- invalid callback ref\n")); + return(0); + } + + // handle stream start state + if (eStatus == PROTOSTREAM_STATUS_BEGIN) + { + char strHeader[512]; + + // get header + ProtoStreamStatus(pProtoStream, 'htxt', strHeader, sizeof(strHeader)); + + // print header + NetPrintf(("stream: stream start\n")); + + // parse icy header + _StreamParseHeader(pCmdRef, strHeader); + + // save start time + pCmdRef->iStartTick = ZTick(); + } + + // calc current tick + #if STREAMER_DEBUG + iTick = ZTick() - pCmdRef->iStartTick; + #endif + + // handle stream data ready state + if ((eStatus == PROTOSTREAM_STATUS_BEGIN) || (eStatus == PROTOSTREAM_STATUS_DATA)) + { + // decode processing + for ( iResult = 1; iResult > 0; ) + { + // process metadata, if any + if (pCmdRef->iMetaInterval != 0) + { + // clamp maximum amount of data + iResult = _StreamProcessMetaData(pCmdRef, pBuffer, iBufSize); + if (iResult > 0) + { + pBuffer += iResult; + iBufSize -= iResult; + } + // make sure we don't consume data into the next metaheader + if (iBufSize > pCmdRef->iMetaOffset) + { + iBufSize = pCmdRef->iMetaOffset; + } + } + + // process mp3 data + if (iBufSize > 0) + { + // $$TODO$$ mimic mp3 decoding: for now just consume the entire buffer + pBuffer += iBufSize; + + // update meta offset + if (pCmdRef->iMetaInterval != 0) + { + pCmdRef->iMetaOffset -= iBufSize; + } + } + else + { + break; + } + } + } + + // handle stream done state + if (eStatus == PROTOSTREAM_STATUS_DONE) + { + // kill the player + NetPrintf(("stream: stream done; resetting player\n")); + _StreamDone(pCmdRef, ZTick()); + } + + #if STREAMER_DEBUG + if ((pBuffer - pBufStart) > 0) + { + // display status info + ZPrintf("stream: consumed %d bytes at %d:%02d:%03d\n", pBuffer - pBufStart, + iTick / (1000*60), // minutes + (iTick / 1000) % 60, // seconds + iTick % 1000); // thousandths of a second + } + #endif + + return(pBuffer - pBufStart); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _StreamUpdate + + \Description + Read data from ProtoStream (non-callback interface) + + \Input *pProtoStream - protostream module state + \Input eStatus - callback status + \Input *pBuffer - pointer to buffered data, or NULL if no data + \Input iBufSize - amount of data available in buffer + \Input *pUserData - user data pointer + + \Output + int32_t - number of bytes consumed + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +#if !STREAMER_USECALLBACK +static int32_t _StreamUpdate(StreamCmdRefT *pCmdRef) +{ + static char aBuffer[64 * 1024]; + int32_t iResult; + + if ((iResult = ProtoStreamRead(pCmdRef->pProtoStream, aBuffer, sizeof(aBuffer), 64)) > 0) + { + + } + + return(iResult); +} +#endif + + +/* + Stream Commands +*/ + +/*F*************************************************************************************/ +/*! + \Function _SubcmdStreamCreate + + \Description + Stream subcommand - create the module + + \Input *_pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE means display command help + + \Output None. + + \Version 1.0 11/21/2005 (jbrookes) First Version +*/ +/**************************************************************************************F*/ +static void _SubcmdStreamCreate(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + StreamCmdRefT *pCmdRef; + int32_t iBufSize; + + // usage + if ((argc > 3) || (bHelp == TRUE)) + { + ZPrintf(" usage: %s create [bufsize]\n", argv[0]); + return; + } + + // get buffer size + iBufSize = (argc == 3) ? strtol(argv[2], NULL, 10) * 1024 : 32 * 1024; + + // allocate context + pCmdRef = _Stream_pCmdRef = ZMemAlloc(sizeof(*pCmdRef)); + ds_memclr(pCmdRef, sizeof(*pCmdRef)); + + // create the streaming module + pCmdRef->pProtoStream = ProtoStreamCreate(iBufSize); + +#if STREAMER_USECALLBACK + // set up callback info + ProtoStreamSetCallback(pCmdRef->pProtoStream, 100, _ProtoStreamCallback, pCmdRef); +#endif + + // allow shoutcast server compatibility (shoutcast returns "ICY" instead of "HTTP" response) + ProtoStreamControl(pCmdRef->pProtoStream, 'hver', FALSE, 0, NULL); + + // pretend to be WinAmp and enable metadata, as some servers require these + ProtoStreamControl(pCmdRef->pProtoStream, 'apnd', 0, 0, + "User-Agent: WinampMPEG/5.11\r\n" + "Icy-MetaData:1\r\n" + "Accept: */*\r\n"); + + // set default playlist + pCmdRef->pPlaylist = &_Playlist; + + // seed random number generator for random playlist traversal + srand(NetTick()); + // enable random play + pCmdRef->bRandomPlay = TRUE; +} + +/*F*************************************************************************************/ +/*! + \Function _SubcmdStreamDestroy + + \Description + Stream subcommand - destroy module + + \Input *_pCmdRef - module state + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE means display command help + + \Output None. + + \Version 1.0 11/21/2005 (jbrookes) First Version +*/ +/**************************************************************************************F*/ +static void _SubcmdStreamDestroy(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + StreamCmdRefT *pCmdRef = (StreamCmdRefT *)_pCmdRef; + + // validate arguments + if ((argc != 2) || bHelp) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + // close current strean, if any + _StreamClose(pCmdRef); + + // free playlist, if any + if (pCmdRef->pPlaylist != &_Playlist) + { + ZMemFree(pCmdRef->pPlaylist); + } + if (pCmdRef->pRandom != NULL) + { + ZMemFree(pCmdRef->pRandom); + } + + // destroy streamer + ProtoStreamDestroy(pCmdRef->pProtoStream); + + // destroy module state + ZMemFree(pCmdRef); + _Stream_pCmdRef = NULL; +} + +/*F*************************************************************************************/ +/*! + \Function _SubcmdStreamOpen + + \Description + Stream subcommand - open a stream + + \Input *_pCmdRef - module state + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE means display command help + + \Output None. + + \Version 1.0 11/21/2005 (jbrookes) First Version +*/ +/**************************************************************************************F*/ +static void _SubcmdStreamOpen(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + StreamCmdRefT *pCmdRef = (StreamCmdRefT *)_pCmdRef; + StreamStationT Station, *pStation; + int32_t iFreq, iStation = -1; + + // validate arguments + if (((argc != 3) && (argc != 4)) || bHelp) + { + ZPrintf(" usage: %s open [url|playlist|index] [freq]\n", argv[0]); + return; + } + + // playlist? + if (strstr(argv[2], ".xml")) + { + _StreamOpenPlaylist(pCmdRef, argv[2]); + _StreamDisplayPlaylist(pCmdRef, pCmdRef->pPlaylist); + return; + } + else if (!ds_strnicmp(argv[2], "http", 4)) // built-in url? + { + ds_memclr(&Station, sizeof(Station)); + ds_strnzcpy(Station.strUrl[0], argv[2], sizeof(Station.strUrl[0])); + pCmdRef->iCurUrl = 0; + pStation = &Station; + } + else // assume it is a playlist index + { + if ((iStation = strtol(argv[2], NULL, 10)) >= pCmdRef->pPlaylist->iNumStations) + { + iStation = 0; + } + pStation = &pCmdRef->pPlaylist->Stations[iStation]; + pCmdRef->iCurStation = pCmdRef->bRandomPlay ? _StreamGetRandIndex(pCmdRef, iStation) : iStation; + } + + // get restart frequency + iFreq = (argc == 4) ? strtol(argv[3], NULL, 10) : PROTOSTREAM_FREQ_IMMED; + + // reset url index + pCmdRef->iCurUrl = 0; + + // open the stream + _StreamOpen(pCmdRef, pStation, iFreq); +} + +/*F*************************************************************************************/ +/*! + \Function _SubcmdStreamClose + + \Description + Stream subcommand - close a stream + + \Input *_pCmdRef - module state + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE means display command help + + \Output None. + + \Version 1.0 11/21/2005 (jbrookes) First Version +*/ +/**************************************************************************************F*/ +static void _SubcmdStreamClose(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + StreamCmdRefT *pCmdRef = (StreamCmdRefT *)_pCmdRef; + + // validate arguments + if ((argc != 2) || bHelp) + { + ZPrintf(" usage: %s close\n", argv[0]); + return; + } + + // reset skip + pCmdRef->iSkipTime = 0; + + // destroy stream + _StreamClose(pCmdRef); +} + +/*F*************************************************************************************/ +/*! + \Function _SubcmdStreamSkip + + \Description + Stream subcommand - enable skip mode + + \Input *_pCmdRef - module state + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE means display command help + + \Output None. + + \Version 1.0 11/21/2005 (jbrookes) First Version +*/ +/**************************************************************************************F*/ +static void _SubcmdStreamSkip(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + StreamCmdRefT *pCmdRef = (StreamCmdRefT *)_pCmdRef; + + // validate arguments + if (((argc != 2) && (argc != 3)) || bHelp) + { + ZPrintf(" usage: %s skip [random]|[time]\n", argv[0]); + return; + } + + // activate? + if ((pCmdRef->iSkipTime == 0) || (argc == 3)) + { + // get skip time in seconds + pCmdRef->iSkipTime = (argc == 3) ? strtol(argv[2], NULL, 10) : STREAM_DEFAULTSKIP; + + // convert to milliseconds + pCmdRef->iSkipTime *= 1000; + } + else + { + // stop skipping + pCmdRef->iSkipTime = 0; + } +} + +/*F*************************************************************************************/ +/*! + \Function _SubcmdStreamControl + + \Description + Stream subcommand - call control function + + \Input *_pCmdRef - module state + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - TRUE means display command help + + \Output None. + + \Version 1.0 11/21/2005 (jbrookes) First Version +*/ +/**************************************************************************************F*/ +static void _SubcmdStreamControl(void *_pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + StreamCmdRefT *pCmdRef = (StreamCmdRefT *)_pCmdRef; + int32_t iCmd, iValue=0, iValue2=0; + void *pValue=NULL; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s ctrl \n", argv[0]); + return; + } + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + if (argc > 3) + { + iValue = strtol(argv[3], NULL, 10); + } + + if (argc > 4) + { + iValue2 = strtol(argv[4], NULL, 10); + } + + // handle stream-specific commands + if (iCmd == 'rand') + { + pCmdRef->bRandomPlay = !pCmdRef->bRandomPlay; + ZPrintf("stream: random play %s\n", pCmdRef->bRandomPlay ? "enabled" : "disabled"); + return; + } + // pass unhandled selectors to ProtoStreamControl(); + ZPrintf("stream: executing ProtoStreamControl(pProtoUpnp, '%s', %d, %d, %s)\n", argv[2], iValue, iValue2, pValue ? pValue : "(null)"); + ProtoStreamControl(pCmdRef->pProtoStream, iCmd, iValue, iValue2, pValue); +} + +/*F********************************************************************************/ +/*! + \Function _CmdStreamCb + + \Description + Recurring stream callback. + + \Input *argz - environment + \Input argc - number of args + \Input *argv[] - argument list + + \Output int32_t - standard return code + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdStreamCb(ZContext *argz, int32_t argc, char *argv[]) +{ + StreamCmdRefT *pCmdRef = _Stream_pCmdRef; + int32_t iCurTick = ZTick(); + + // if no ref, we're done + if (pCmdRef == NULL) + { + return(0); + } + + // check for kill + if (argc == 0) + { + char *strArgs[2] = { "stream", "destroy" }; + ZPrintf("%s: killed\n", argv[0]); + _SubcmdStreamDestroy(pCmdRef, 2, strArgs, 0); + return(0); + } + + // update skip processing + if (pCmdRef->iSkipTime != 0) + { + if ((iCurTick - pCmdRef->iSkipTick) > pCmdRef->iSkipTime) + { + _StreamSkip(pCmdRef, iCurTick); + } + } + + // update protostream module + ProtoStreamUpdate(pCmdRef->pProtoStream); + + #if !STREAMER_USECALLBACK + _StreamUpdate(pCmdRef); + #endif + + // keep running + return(ZCallback(&_CmdStreamCb, 16)); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdStream + + \Description + Create the Module module. + + \Input *argz - environment + \Input argc - number of args + \Input *argv[] - argument list + + \Output int32_t - standard return code + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdStream(ZContext *argz, int32_t argc, char *argv[]) +{ + unsigned char bHelp, bCreate = FALSE; + StreamCmdRefT *pCmdRef = _Stream_pCmdRef; + T2SubCmdT *pCmd; + + // handle basic help + if ((argc < 2) || (((pCmd = T2SubCmdParse(_Stream_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the protostream module\n"); + T2SubCmdUsage(argv[0], _Stream_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmdRef == NULL) && strcmp(pCmd->strName, "create")) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _SubcmdStreamCreate(pCmdRef, 1, &pCreate, bHelp); + pCmdRef = _Stream_pCmdRef; + bCreate = TRUE; + } + + // hand off to command + pCmd->pFunc(pCmdRef, argc, argv, bHelp); + + // if we executed create, remember + if (pCmd->pFunc == _SubcmdStreamCreate) + { + bCreate = TRUE; + } + + // if we executed create, install periodic callback + return((bCreate == TRUE) ? ZCallback(_CmdStreamCb, 100) : 0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/string.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/string.c new file mode 100644 index 00000000..e0861482 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/string.c @@ -0,0 +1,138 @@ +/*H********************************************************************************/ +/*! + \File string.c + + \Description + Test the plat-str functionality. + + \Copyright + Copyright (c) 2012 Electronic Arts + + \Version 10/01/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "libsample/zlib.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// Variables + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _StringCompareWild + + \Description + Compare two strings with wildcard matching and print the result + + \Input bExpected - expected result of comparison (true or false) + \Input *pStrTest - pointer to string to match against + \Input *pStrWild - pointer to wildcard string to match with + \Input bNoCase - TRUE for case insensitive, else FALSE + + \Output + int32_t - 0=expected result, 1=different result + + \Version 10/01/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _StringCompareWild(uint32_t bExpected, const char *pStrTest, const char *pStrWild, uint8_t bNoCase) +{ + uint32_t bMatch; + int32_t iResult; + + if (bNoCase) + { + bMatch = ds_stricmpwc(pStrTest, pStrWild) == 0 ? TRUE : FALSE; + } + else + { + bMatch = ds_strcmpwc(pStrTest, pStrWild) == 0 ? TRUE : FALSE; + } + + iResult = (bMatch != bExpected) ? 1 : 0; + ZPrintf("string: %s; strtest(%s) %s strwild(%s)\n", iResult ? "failure" : "success", pStrTest, bMatch ? "matched" : "did not match", pStrWild); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _StringTestCompareWild + + \Description + Perform wild-card string comparison tests + + \Output + int32_t - 0=passed, else failed + + \Version 10/01/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _StringTestCompareWild(void) +{ + int32_t iResult = 0; + + ZPrintf("string: String wild-card comparison tests\n"); + + iResult += _StringCompareWild(TRUE, "gosca.ea.com", "*.ea.com", FALSE); + iResult += _StringCompareWild(FALSE, "gosca.ea.com", "*.online.ea.com", FALSE); + + ZPrintf("string: String wild-card case-insensitive comparison tests\n"); + + iResult += _StringCompareWild(TRUE, "GOSCA.EA.COM", "*.ea.com", TRUE); + iResult += _StringCompareWild(FALSE, "GOSCA.EA.COM", "*.online.ea.com", TRUE); + + ZPrintf("string: %d string test discrepencies\n", iResult); + ZPrintf("string: ------------------------------------\n"); + return(iResult); +} + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CmdString + + \Description + Test platform string functions + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 10/01/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdString(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iResult = 0; + + ZPrintf("string: ------------------------------------------------\n"); + ZPrintf("string: Testing platform string (non-printing) functions\n"); + ZPrintf("string: ------------------------------------------------\n"); + + iResult += _StringTestCompareWild(); + + ZPrintf("string: Test results: %d total discrepencies\n", iResult); + ZPrintf("string: ----------------------------------------------\n"); + + return(0); +} + + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/time.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/time.c new file mode 100644 index 00000000..af5dab4a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/time.c @@ -0,0 +1,172 @@ +/*H********************************************************************************/ +/*! + \File time.c + + \Description + Test the plat-time functionality. + + \Copyright + Copyright (c) 2012 Electronic Arts + + \Version 07/12/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "libsample/zlib.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct TimeStrToTimeT +{ + char strTime[32]; + uint64_t uTime; + TimeToStringConversionTypeE eConvType; +} TimeStrToTimeT; + +/*** Variables ********************************************************************/ + +// Variables + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _TimeStrToTime + + \Description + Execute ds_strtotime/ds_strtotime2 and compare to expected result + + \Output + int32_t - 0=success, 1=failed + + \Version 02/27/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TimeStrToTime(const TimeStrToTimeT *pTime, int32_t iTest) +{ + uint64_t uEpoch = ds_strtotime2(pTime->strTime, pTime->eConvType); + if (uEpoch != pTime->uTime) + { + ZPrintf("time: ds_strtotime2() test %d failed; got=%qd, expected=%qd\n", uEpoch, pTime->uTime); + } + return(uEpoch != pTime->uTime); +} + +/*F********************************************************************************/ +/*! + \Function _TimeTestStrToTime + + \Description + Test ds_strtotime/ds_strtotime2 + + \Output + int32_t - number of test result failures + + \Version 02/27/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _TimeTestStrToTime(void) +{ + static const TimeStrToTimeT _Times[] = + { + { "Sun, 06 Nov 1994 08:49:37 GMT", 784111777, TIMETOSTRING_CONVERSION_UNKNOWN }, // RFC 822/1123 + { "Sun Nov 6 08:49:37 1994", 784111777, TIMETOSTRING_CONVERSION_UNKNOWN }, // asctime + { "1994-11-06T08:49:37Z", 784111777, TIMETOSTRING_CONVERSION_ISO_8601 }, + { "941106084937", 784111777, TIMETOSTRING_CONVERSION_ASN1_UTCTIME }, + { "641106084937", 2993186977, TIMETOSTRING_CONVERSION_ASN1_UTCTIME }, + { "701106084937", 26729377, TIMETOSTRING_CONVERSION_ASN1_UTCTIME }, + { "9411060849", 784111740, TIMETOSTRING_CONVERSION_ASN1_UTCTIME }, + { "19941106084937", 784111777, TIMETOSTRING_CONVERSION_ASN1_GENTIME }, + { "19941106084937.123", 784111777, TIMETOSTRING_CONVERSION_ASN1_GENTIME }, + { "20390101000000", 2177452800, TIMETOSTRING_CONVERSION_ASN1_GENTIME }, + { "20501106084937", 2551337377, TIMETOSTRING_CONVERSION_ASN1_GENTIME }, + { "21500621143040", 5695108240, TIMETOSTRING_CONVERSION_ASN1_GENTIME } + }; + int32_t iTime, iResult = 0; + for (iResult = 0, iTime = 0; iTime < (int32_t)(sizeof(_Times)/sizeof(_Times[0])); iTime += 1) + { + iResult += _TimeStrToTime(&_Times[iTime], iTime); + } + return(iResult); +} + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CmdTime + + \Description + Test the time functions + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 07/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdTime(ZContext *argz, int32_t argc, char *argv[]) +{ + char strBuf[20]; + struct tm curtime; + uint64_t uTime; + int32_t iZone; + void *pPlatTime = NULL; + int32_t iResult = 0; + + // get current time... this is equivalent to time(0) + uTime = ds_timeinsecs(); + ZPrintf("time: ds_timeinsecs()=%qd\n", uTime); + + // convert to tm time + ds_secstotime(&curtime, uTime); + ZPrintf("time: ds_secstotime()=%d/%d/%d %02d:%02d:%02d\n", + curtime.tm_mon+1, curtime.tm_mday, curtime.tm_year+1900, + curtime.tm_hour, curtime.tm_min, curtime.tm_sec); + + // test ISO_8601 conversion + ZPrintf("time: ds_timetostr(ISO_8601)=%s\n", ds_timetostr(&curtime, TIMETOSTRING_CONVERSION_ISO_8601, 1, strBuf, sizeof(strBuf))); + + // test RFC_0822 conversion + ZPrintf("time: ds_timetostr(RFC_0822)=%s\n", ds_timetostr(&curtime, TIMETOSTRING_CONVERSION_RFC_0822, 1, strBuf, sizeof(strBuf))); + + // get localtime + ds_memclr(&curtime, sizeof(curtime)); + ds_localtime(&curtime, uTime); + ZPrintf("time: ds_localtime()=%d/%d/%d %d:%d:%d\n", + curtime.tm_mon+1, curtime.tm_mday, curtime.tm_year+1900, + curtime.tm_hour, curtime.tm_min, curtime.tm_sec); + + // timezone (delta between local and GMT time in seconds) + iZone = ds_timezone(); + ZPrintf("time: ds_timezone=%ds, %dh\n", iZone, iZone/(60*60)); + + // test ds_strtotime() + iResult += _TimeTestStrToTime(); + ZPrintf("time: %d failed strtotime tests\n", iResult); + + if (pPlatTime != NULL) + { + ds_memclr(&curtime, sizeof(curtime)); + ds_plattimetotime(&curtime, pPlatTime); + ZPrintf("time: ds_plattimetotime()=%d/%d/%d %d:%d:%d\n", + curtime.tm_mon+1, curtime.tm_mday, curtime.tm_year+1900, + curtime.tm_hour, curtime.tm_min, curtime.tm_sec); + } + + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/tunnel.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/tunnel.c new file mode 100644 index 00000000..887537f3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/tunnel.c @@ -0,0 +1,367 @@ +/*H********************************************************************************/ +/*! + \File tunnel.c + + \Description + A tester command to test ProtoTunnel + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 12/02/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/proto/prototunnel.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "testerregistry.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct TunnelAppT +{ + ProtoTunnelRefT *pProtoTunnel; + unsigned char bZCallback; +} TunnelAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _TunnelCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _TunnelDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _TunnelAlloc(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _TunnelFree(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _TunnelControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables ********************************************************************/ + +static T2SubCmdT _Tunnel_Commands[] = +{ + { "create", _TunnelCreate }, + { "destroy", _TunnelDestroy }, + { "alloc", _TunnelAlloc }, + { "free", _TunnelFree }, + { "ctrl", _TunnelControl }, + { "", NULL } +}; + +static TunnelAppT _Tunnel_App = { NULL, FALSE }; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _TunnelCreate + + \Description + Tunnel subcommand - create tunnel module + + \Input *pApp - pointer to tunnel module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _TunnelCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + TunnelAppT *pApp = &_Tunnel_App; + int32_t iTunnelPort; + uint32_t uClientId; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s create [port]\n", argv[0]); + return; + } + + // get the port + iTunnelPort = (argc == 4) ? (int32_t)strtol(argv[3], NULL, 10) : 9600; + + // create the tunnel + pApp->pProtoTunnel = ProtoTunnelCreate(4, iTunnelPort); + + // set client id + uClientId = (uint32_t)strtol(argv[2], NULL, 16); + ProtoTunnelControl(pApp->pProtoTunnel, 'clid', uClientId, 0, NULL); +} + +/*F********************************************************************************/ +/*! + \Function _TunnelDestroy + + \Description + Tunnel subcommand - destroy tunnel module + + \Input *pApp - pointer to tunnel module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _TunnelDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + TunnelAppT *pApp = &_Tunnel_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s create\n", argv[0]); + return; + } + + if (pApp->pProtoTunnel != NULL) + { + ProtoTunnelDestroy(pApp->pProtoTunnel); + ds_memclr(&pApp, sizeof(pApp)); + } +} + +/*F********************************************************************************/ +/*! + \Function _TunnelAlloc + + \Description + Tunnel subcommand - allocate a tunnel + + \Input *pApp - pointer to tunnel module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 12/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _TunnelAlloc(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + TunnelAppT *pApp = &_Tunnel_App; + ProtoTunnelInfoT Info; + char *pKey, strKey[] = "24906usdfnasdf1&"; + + if ((bHelp == TRUE) || (argc < 5)) + { + ZPrintf(" usage: %s alloc [clientId] [addr] [port] \n", argv[0]); + return; + } + + // ref key + pKey = (argc == 6) ? argv[5] : strKey; + + ds_memclr(&Info, sizeof(Info)); + Info.uRemoteClientId = (uint32_t)strtol(argv[2], 0, 16); + Info.uRemoteAddr = SocketInTextGetAddr(argv[3]); + Info.uRemotePort = strtol(argv[4], 0, 10); + //$$ hardcode for now + Info.aRemotePortList[0] = 3658; + Info.aPortFlags[0] = PROTOTUNNEL_PORTFLAG_ENCRYPTED; + Info.aRemotePortList[1] = VOIP_PORT; + Info.aPortFlags[1] = PROTOTUNNEL_PORTFLAG_ENCRYPTED; + + // allocate a mapping + ZPrintf("%s: tunnel alloc clientId=0x%08x addr=%a port=%d key=%s\n", argv[0], Info.uRemoteClientId, Info.uRemoteAddr, Info.uRemotePort, pKey); + ProtoTunnelAlloc(pApp->pProtoTunnel, &Info, pKey); +} + +/*F********************************************************************************/ +/*! + \Function _TunnelFree + + \Description + Tunnel subcommand - free a tunnel + + \Input *pApp - pointer to tunnel module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 12/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _TunnelFree(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + TunnelAppT *pApp = &_Tunnel_App; + uint32_t uTunnelId; + const char *pKey; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s free [tunnelId] \n", argv[0]); + return; + } + + // ref key + pKey = (argc == 4) ? argv[3] : NULL; + + // free a mapping + uTunnelId = (uint32_t)strtol(argv[2], NULL, 16); + ProtoTunnelFree(pApp->pProtoTunnel, uTunnelId, pKey); +} + +/*F********************************************************************************/ +/*! + \Function _TunnelControl + + \Description + Tunnel subcommand - execute a UPnP command + + \Input *pApp - pointer to tunnel module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _TunnelControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + TunnelAppT *pApp = &_Tunnel_App; + int32_t iCmd, iValue, iValue2; + const char *pValue=NULL; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s ctrl [parm]...\n", argv[0]); + return; + } + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + iValue = (argc > 3) ? (int32_t)strtol(argv[3], NULL, 10) : 0; + iValue2 = (argc > 4) ? (int32_t)strtol(argv[4], NULL, 10) : 0; + + ZPrintf("tunnel: executing ProtoTunnelControl(pProtoTunnel, '%s', %d, %d, %s)\n", argv[2], iValue, iValue2, pValue ? pValue : "(null)"); + ProtoTunnelControl(pApp->pProtoTunnel, iCmd, iValue, iValue2, pValue); +} + +/*F********************************************************************************/ +/*! + \Function _CmdTunnelCb + + \Description + Tunnel callback, called after command has been issued. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback, or zero to terminate + + \Version 1.0 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdTunnelCb(ZContext *argz, int32_t argc, char *argv[]) +{ + TunnelAppT *pApp = &_Tunnel_App; + + // check for kill + if (argc == 0) + { + ZPrintf("%s: killed\n", argv[0]); + ProtoTunnelDestroy(pApp->pProtoTunnel); + return(0); + } + + // update module + if (pApp->pProtoTunnel != NULL) + { + ProtoTunnelUpdate(pApp->pProtoTunnel); + } + + // keep recurring + return(ZCallback(&_CmdTunnelCb, 17)); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdTunnel + + \Description + Tunnel command. This command starts the ProtoTunnel module. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback + + \Version 1.0 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdTunnel(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + TunnelAppT *pApp = &_Tunnel_App; + unsigned char bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Tunnel_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the prototunnel module\n"); + T2SubCmdUsage(argv[0], _Tunnel_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _TunnelCreate) && (pApp->pProtoTunnel == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _TunnelCreate(pApp, 1, &pCreate, bHelp); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + return(ZCallback(_CmdTunnelCb, 17)); + } + else + { + return(0); + } +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/upnp.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/upnp.c new file mode 100644 index 00000000..62837b62 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/upnp.c @@ -0,0 +1,327 @@ +/*H********************************************************************************/ +/*! + \File upnp.c + + \Description + A tester command to test ProtoUpnp + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/23/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protoupnp.h" + +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testerregistry.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct UpnpAppT +{ + ProtoUpnpRefT *pProtoUpnp; + + unsigned char bZCallback; +} UpnpAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _UpnpCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _UpnpDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _UpnpControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _UpnpFakeResponse(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables ********************************************************************/ + +static T2SubCmdT _Upnp_Commands[] = +{ + { "create", _UpnpCreate }, + { "destroy", _UpnpDestroy }, + { "ctrl", _UpnpControl }, + { "fake", _UpnpFakeResponse }, + { "", NULL } +}; + +static UpnpAppT _Upnp_App = { NULL, FALSE }; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _UpnpCreate + + \Description + Upnp subcommand - create upnp module + + \Input *pApp - pointer to upnp module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _UpnpCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + UpnpAppT *pApp = &_Upnp_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s create\n", argv[0]); + return; + } + + pApp->pProtoUpnp = ProtoUpnpCreate(); +} + +/*F********************************************************************************/ +/*! + \Function _UpnpDestroy + + \Description + Upnp subcommand - destroy upnp module + + \Input *pApp - pointer to upnp module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _UpnpDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + UpnpAppT *pApp = &_Upnp_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + ProtoUpnpDestroy(pApp->pProtoUpnp); +} + +/*F********************************************************************************/ +/*! + \Function _UpnpControl + + \Description + Upnp subcommand - execute a UPnP command + + \Input *pApp - pointer to upnp module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _UpnpControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + UpnpAppT *pApp = &_Upnp_App; + int32_t iCmd, iValue=0, iValue2=0; + const char *pValue=NULL; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s command \n", argv[0]); + return; + } + + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + if (argc == 4) + { + if (iCmd == 'macr') + { + iValue = argv[3][0] << 24; + iValue |= argv[3][1] << 16; + iValue |= argv[3][2] << 8; + iValue |= argv[3][3]; + } + else if (iCmd == 'gvar') + { + pValue = argv[3]; + } + else + { + iValue = (int32_t)strtol(argv[3], NULL, 10); + } + } + + ZPrintf("upnp: executing ProtoUpnpControl(pProtoUpnp, '%s', %d, %d, %s)\n", argv[2], iValue, iValue2, pValue ? pValue : "(null)"); + ProtoUpnpControl(pApp->pProtoUpnp, iCmd, iValue, iValue2, pValue); +} + +/*F********************************************************************************/ +/*! + \Function _UpnpFakeResponse + + \Description + Upnp subcommand - fake a upnp response + + \Input *pApp - pointer to upnp module + \Input argc - argument count + \Input *argv[] - argument list + \Input bHelp - true if help request, else false + + \Output + None. + + \Version 1.0 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _UpnpFakeResponse(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + UpnpAppT *pApp = &_Upnp_App; + char *pResponseFile; + int32_t iCmd, iFileSize; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s fake \n", argv[0]); + return; + } + + // assemble command + iCmd = argv[2][0] << 24; + iCmd |= argv[2][1] << 16; + iCmd |= argv[2][2] << 8; + iCmd |= argv[2][3]; + + // load response file + if ((pResponseFile = ZFileLoad(argv[3], &iFileSize, ZFILE_OPENFLAG_RDONLY)) == NULL) + { + ZPrintf("%s: unable to open response file '%s'\n", argv[3]); + return; + } + + // fake the command + ProtoUpnpControl(pApp->pProtoUpnp, 'fake', iCmd, 0, pResponseFile); + + // unload the file + ZMemFree(pResponseFile); +} + +/*F********************************************************************************/ +/*! + \Function _CmdUpnpCb + + \Description + Upnp callback, called after command has been issued. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback, or zero to terminate + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdUpnpCb(ZContext *argz, int32_t argc, char *argv[]) +{ + UpnpAppT *pApp = &_Upnp_App; + + // check for kill + if (argc == 0) + { + ZPrintf("%s: killed\n", argv[0]); + ProtoUpnpDestroy(pApp->pProtoUpnp); + return(0); + } + + // update module + ProtoUpnpUpdate(pApp->pProtoUpnp); + + // keep recurring + return(ZCallback(&_CmdUpnpCb, 17)); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdUpnp + + \Description + Upnp command. This command starts the ProtoUpnp module. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdUpnp(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + UpnpAppT *pApp = &_Upnp_App; + unsigned char bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Upnp_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the protoupnp module\n"); + T2SubCmdUsage(argv[0], _Upnp_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _UpnpCreate) && (pApp->pProtoUpnp == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _UpnpCreate(pApp, 1, &pCreate, bHelp); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + return(ZCallback(_CmdUpnpCb, 17)); + } + else + { + return(0); + } +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/user.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/user.c new file mode 100644 index 00000000..f9c82d82 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/user.c @@ -0,0 +1,725 @@ +/*H*************************************************************************************/ +/*! + \File user.c + + \Description + Reference application for the userapi. + + \Copyright + Copyright (c) Electronic Arts 2014. ALL RIGHTS RESERVED. + + \Version 18/02/2014 (amakoukji) +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/misc/userapi.h" + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct UaAppT +{ + UserApiRefT *pUa; + + uint8_t bStarted; + + unsigned char bZCallback; +} UaAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _UaCreate(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UaControl(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UaDestroy(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UaGetProfiles(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UaGetProfile(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UaRegister(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UaRecentlyMet(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UaSetRichPresence(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static int32_t _CmdUaTickCb(ZContext *argz, int32_t argc, char *argv[]); +static void _CmdUaCb(UserApiRefT *pRef, UserApiEventDataT *pUserApiEventData, void *pUserData); +static void _CmdUaUpdateCb(UserApiRefT *pRef, UserApiNotifyTypeE eNotifyType, UserApiNotifyDataT *pData, void *pUserData); +static void _CmdUaPostCb(UserApiRefT *pRef, UserApiPostResponseT *pResponse, void *pUserData); + +/*** Variables *************************************************************************/ + +// Private variables +static T2SubCmdT _Ua_Commands[] = +{ + { "create", _UaCreate }, + { "control", _UaControl }, + { "destroy", _UaDestroy }, + { "getprofiles", _UaGetProfiles }, + { "getprofile", _UaGetProfile }, + { "register", _UaRegister }, + { "recentlymet", _UaRecentlyMet }, + { "setrichpresence", _UaSetRichPresence }, + { "", NULL } +}; + +static UaAppT _Ua_App; + +// Public variables + +/*** Private Functions *****************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _CmdUaTickCb + + \Description + UserList callback, called after command has been issued. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback, or zero to terminate + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static int32_t _CmdUaTickCb(ZContext *argz, int32_t argc, char *argv[]) +{ + UaAppT *pApp = &_Ua_App; + + if (pApp->bStarted != FALSE) + { + // check for kill + if (argc == 0) + { + ZPrintf("%s: killed\n", argv[0]); + UserApiDestroy(pApp->pUa); + return(0); + } + + // update module + if (pApp->pUa != NULL) + { + UserApiUpdate(pApp->pUa); + } + + // keep recurring + return(ZCallback(&_CmdUaTickCb, 17)); + } + + return(ZLIB_STATUS_UNKNOWN); +} + +/*F********************************************************************************/ +/*! + \Function _CmdUaCb + + \Description + user callback + + \Input *pRef - UserListApiRefT reference + \Input *pUserApiEventData - result data struct + \Input pUserData - NULL + + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static void _CmdUaCb(UserApiRefT *pRef, UserApiEventDataT *pUserApiEventData, void *pUserData) +{ + uint64_t uUserId; + + if (pUserApiEventData->eEventType == USERAPI_EVENT_DATA) + { + DirtyUserToNativeUser(&uUserId, sizeof(uUserId), &(pUserApiEventData->EventDetails.UserData.DirtyUser)); + ZPrintf("user: profile data \n\t id %llu ", uUserId); + + if (pUserApiEventData->EventDetails.UserData.uUserDataMask & USERAPI_MASK_PROFILE) + { + ZPrintf("\n\twith gamertag %s and avatar at %s ", pUserApiEventData->EventDetails.UserData.Profile.strGamertag, pUserApiEventData->EventDetails.UserData.Profile.strAvatarUrl); + } + + if (pUserApiEventData->EventDetails.UserData.uUserDataMask & USERAPI_MASK_PRESENCE) + { + ZPrintf("\n\t%s ", (pUserApiEventData->EventDetails.UserData.Presence.ePresenceStatus == USERAPI_PRESENCE_ONLINE) ? "is online" : + (pUserApiEventData->EventDetails.UserData.Presence.ePresenceStatus == USERAPI_PRESENCE_AWAY) ? "is away" : "is offline"); + if (pUserApiEventData->EventDetails.UserData.Presence.ePresenceStatus == USERAPI_PRESENCE_ONLINE) + { + ZPrintf("\n\tin title %s on plaform %s", pUserApiEventData->EventDetails.UserData.Presence.strTitleName, pUserApiEventData->EventDetails.UserData.Presence.strPlatform); + } + } + + if (pUserApiEventData->EventDetails.UserData.uUserDataMask & USERAPI_MASK_RICH_PRESENCE) + { + ZPrintf("\n\twith rich presence \"%s\"", pUserApiEventData->EventDetails.UserData.RichPresence.strData); + } + ZPrintf("\n"); + } + else if (pUserApiEventData->eEventType == USERAPI_EVENT_END_OF_LIST) + { + ZPrintf("user: End of profiles report\n \tTotal Requested : %d\n\tTotal Received : %d\n\tTotalErrors : %d\n", pUserApiEventData->EventDetails.EndOfList.iTotalRequested, + pUserApiEventData->EventDetails.EndOfList.iTotalReceived, + pUserApiEventData->EventDetails.EndOfList.iTotalErrors); + } +} + +/*F********************************************************************************/ +/*! + \Function _CmdUaUpdateCb + + \Description + user callback for 1st party notifications + + \Input *pRef - UserListApiRefT reference + \Input eNotifyType - type of callback + \Input *pUserApiEventData - result data struct + \Input pUserData - NULL + + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static void _CmdUaUpdateCb(UserApiRefT *pRef, UserApiNotifyTypeE eNotifyType, UserApiNotifyDataT *pData, void *pUserData) +{ + uint64_t uUserId; + + if (eNotifyType == USERAPI_NOTIFY_PRESENCE_UPDATE) + { + DirtyUserToNativeUser(&uUserId, sizeof(uUserId), &(pData->PresenceData.DirtyUser)); + ZPrintf("user: presence update for id %llu\n", uUserId); + } + else if (eNotifyType == USERAPI_NOTIFY_TITLE_UPDATE) + { + DirtyUserToNativeUser(&uUserId, sizeof(uUserId), &(pData->TitleData.DirtyUser)); + ZPrintf("user: title update for id %llu\n", uUserId); + } + else if (eNotifyType == USERAPI_NOTIFY_RICH_PRESENCE_UPDATE) + { + DirtyUserToNativeUser(&uUserId, sizeof(uUserId), &(pData->RichPresenceData.DirtyUser)); + ZPrintf("user: rich presence update for id %llu\n", uUserId); + } + + +} + +/*F********************************************************************************/ +/*! + \Function _CmdUaPostCb + + \Description + user callback for posted data + + \Input *pRef - UserListApiRefT reference + \Input *pResponse - result data struct + \Input pUserData - NULL + + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static void _CmdUaPostCb(UserApiRefT *pRef, UserApiPostResponseT *pResponse, void *pUserData) +{ + ZPrintf("user: response from data push, error code=%d, message=\"%s\"\n", pResponse->eError, pResponse->pMessage); +} + +/*F*************************************************************************************/ +/*! + \Function _UaCreate + + \Description + Udp subcommand - create udp socket + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaCreate(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UaAppT *pApp = (UaAppT *)_pApp; + + if ((bHelp == TRUE) || (argc != 2 )) + { + ZPrintf(" usage: %s create\n", argv[0]); + return; + } + + if (pApp->bStarted) + { + ZPrintf("%s: user already created\n", argv[0]); + return; + } + + // allocate UserApi module + if ((pApp->pUa = UserApiCreate()) == NULL) + { + ZPrintf("%s: unable to create protoudp module\n", argv[0]); + return; + } + + pApp->bStarted = TRUE; + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + ZCallback(_CmdUaTickCb, 17); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UaControl + + \Description + Call UserApiControl + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaControl(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UaAppT *pApp = (UaAppT *)_pApp; + int32_t iResult; + int32_t iControl = 0; + int32_t iValue = 0; + int32_t iValue2 = 0; + + if ((bHelp == TRUE) || (argc != 6) || strlen(argv[2]) != 4) + { + ZPrintf(" usage: %s control [4 character selector] [iValue] [iValue2] [pValue]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: user not yet created\n", argv[0]); + return; + } + + iControl += argv[2][0] << 24; + iControl += argv[2][1] << 16; + iControl += argv[2][2] << 8; + iControl += argv[2][3]; + iValue = strtol(argv[3], NULL, 10); + iValue2 = strtol(argv[4], NULL, 10); + iResult = UserApiControl(pApp->pUa, iControl, iValue, iValue2, argv[5]); + ZPrintf("control result for %s\n", argv[2], iResult); +} + +/*F*************************************************************************************/ +/*! + \Function _UaDestroy + + \Description + Destroy UserApi + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaDestroy(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UaAppT *pApp = (UaAppT *)_pApp; + + if ((bHelp == TRUE) || (argc != 2)) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: user not yet created\n", argv[0]); + return; + } + + UserApiDestroy(pApp->pUa); + pApp->bZCallback = FALSE; + pApp->bStarted = FALSE; +} + +/*F*************************************************************************************/ +/*! + \Function _UaGetProfiles + + \Description + Batch profile request. Cannot get presence concurrently. Max 100 + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaGetProfiles(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UaAppT *pApp = (UaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + char *pch; + uint32_t uNumUsers = 0; + DirtyUserT aDirtyUsers[100]; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s getprofiles [user index] [comma seperated user user ids]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: user not yet created\n", argv[0]); + return; + } + + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + ds_memclr(aDirtyUsers, sizeof(aDirtyUsers)); + + // parse list + pch = strtok (argv[3],","); + while (pch != NULL) + { + uint64_t uUserId = ds_strtoull(pch, NULL, 10); + DirtyUserFromNativeUser(&aDirtyUsers[uNumUsers++], &uUserId); + pch = strtok (NULL, ","); + } + + iResult = UserApiRequestProfilesAsync(pApp->pUa, uUserIndex, aDirtyUsers, uNumUsers, &_CmdUaCb, NULL); + if (iResult == USERAPI_ERROR_UNSUPPORTED) + { + ZPrintf("getprofiles is not supported on this platform\n", iResult); + } + else + { + ZPrintf("getprofiles: UserApiRequestProfilesAsync() returned %d\n", iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UaGetProfile + + \Description + Profile request. Can get presence concurrently + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaGetProfile(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UaAppT *pApp = (UaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + char *pCh; + uint32_t uUserDataMask = 0; + DirtyUserT DirtyUser; + uint64_t uUserId; + int32_t iBase; + + if ((bHelp == TRUE) || (argc != 5)) + { + ZPrintf(" usage: %s getprofile [user index] [user ids] [comma seperated flags in profile,presence,richpresence]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: user not yet created\n", argv[0]); + return; + } + + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + + pCh = argv[3]; + iBase = 10; + if ((pCh[0] == '0') && (pCh[1] == 'x')) + { + pCh += 2; + iBase = 16; + } + uUserId = ds_strtoull(pCh, NULL, iBase); + DirtyUserFromNativeUser(&DirtyUser, &uUserId); + + // parse list + pCh = strtok(argv[4], ","); + while (pCh != NULL) + { + if (ds_stricmp(pCh, "profile") == 0) + { + uUserDataMask |= USERAPI_MASK_PROFILE; + } + else if (ds_stricmp(pCh, "presence") == 0) + { + uUserDataMask |= USERAPI_MASK_PRESENCE; + } + else if (ds_stricmp(pCh, "richpresence") == 0) + { + uUserDataMask |= USERAPI_MASK_RICH_PRESENCE; + } + + pCh = strtok (NULL, ","); + } + + iResult = UserApiRequestProfileAsync(pApp->pUa, uUserIndex, &DirtyUser, &_CmdUaCb, uUserDataMask, NULL); + if (iResult == USERAPI_ERROR_UNSUPPORTED) + { + ZPrintf("getprofiles is not supported on this platform\n", iResult); + } + else + { + ZPrintf("getprofiles: UserListApiGetListAsync() returned %d\n", iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UaRegister + + \Description + Register for 1st party notifications + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaRegister(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UaAppT *pApp = (UaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + UserApiNotifyTypeE eType = USERAPI_NOTIFY_PRESENCE_UPDATE; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s register [user index] [presence|title|richpresence]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + + if (ds_stricmp(argv[3], "presence") == 0) + { + eType = USERAPI_NOTIFY_PRESENCE_UPDATE; + } + else if (ds_stricmp(argv[3], "title") == 0) + { + eType = USERAPI_NOTIFY_TITLE_UPDATE; + } + else if (ds_stricmp(argv[3], "richpresence") == 0) + { + eType = USERAPI_NOTIFY_RICH_PRESENCE_UPDATE; + } + else + { + ZPrintf(" usage: %s register [user index] [presence|title|richpresence]\n", argv[0]); + return; + } + + iResult = UserApiRegisterUpdateEvent(pApp->pUa, uUserIndex, eType, &_CmdUaUpdateCb, NULL); + if (iResult == USERAPI_ERROR_UNSUPPORTED) + { + ZPrintf("%s is not supported on this platform\n", argv[0], iResult); + } + else + { + ZPrintf("%s: UserApiRegisterUpdateEvent() returned %d\n", argv[0], iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UaRecentlyMet + + \Description + Submit a recently met player report + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaRecentlyMet(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ +#if !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) + UaAppT *pApp = (UaAppT *)_pApp; + int32_t iResult = 0; + uint32_t uUserIndex = 0; + DirtyUserT DirtyUser; + uint64_t AcountId; + int32_t iBase = 0; + char *pCh; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s recentlymet [user index] [user online ids]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + + pCh = argv[3]; + iBase = 10; + if ((pCh[0] == '0') && (pCh[1] == 'x')) + { + pCh += 2; + iBase = 16; + } + AcountId = ds_strtoll(pCh, NULL, iBase); + DirtyUserFromNativeUser(&DirtyUser, &AcountId); + + iResult = UserApiPostRecentlyMetAsync(pApp->pUa, uUserIndex, &DirtyUser, NULL, &_CmdUaPostCb, NULL); + if (iResult == USERAPI_ERROR_UNSUPPORTED) + { + ZPrintf("recentlymet is not supported on this platform\n", iResult); + } + else + { + ZPrintf("recentlymet: UserApiPostRecentlyMetAsync() returned %d\n", iResult); + } +#else + ZPrintf(" %s recentlymet is not supported on XBox\n", argv[0]); +#endif +} + +/*F*************************************************************************************/ +/*! + \Function _UaSetRichPresence + + \Description + Set Rich presence + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UaSetRichPresence(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UaAppT *pApp = (UaAppT *)_pApp; + int32_t iResult = 0; + uint32_t uUserIndex = 0; + UserApiRichPresenceT Data; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s setrichpresence [user index] [rich presence string]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + + ds_memclr(&Data, sizeof(UserApiRichPresenceT)); + ds_strnzcpy(Data.strData, argv[3], (int32_t)sizeof(Data.strData)); + + iResult = UserApiPostRichPresenceAsync(pApp->pUa, uUserIndex, &Data, &_CmdUaPostCb, NULL); + if (iResult == USERAPI_ERROR_UNSUPPORTED) + { + ZPrintf("setrichpresence is not supported on this platform\n", iResult); + } + else + { + ZPrintf("recentlymet: UserApiPostRichPresenceAsync() returned %d\n", iResult); + } +} + + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function CmdUdp + + \Description + Udp command. + + \Input *argz - unused + \Input argc - argument count + \Input **argv - argument list + + \Output + int32_t - zero + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +int32_t CmdUser(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + UaAppT *pApp = &_Ua_App; + unsigned char bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Ua_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the user module\n"); + T2SubCmdUsage(argv[0], _Ua_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _UaCreate) && (pApp->pUa == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _UaCreate(pApp, 1, &pCreate, bHelp); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/userlist.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/userlist.c new file mode 100644 index 00000000..1e1c2d3b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/userlist.c @@ -0,0 +1,604 @@ +/*H*************************************************************************************/ +/*! + \File userlist.c + + \Description + Reference application for the userlistapi. + + \Copyright + Copyright (c) Electronic Arts 2014. ALL RIGHTS RESERVED. + + \Version 18/02/2014 (amakoukji) +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/misc/userlistapi.h" + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef struct UlaAppT +{ + UserListApiRefT *pUla; + char aRecvBuf[2048]; + char aSendBuf[2048]; + + uint8_t bStarted; + + unsigned char bZCallback; +} UlaAppT; + +/*** Function Prototypes ***************************************************************/ + +static void _UlaCreate(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UlaDestroy(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UlaFriends(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UlaBlocked(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UlaRegister(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UlaIsFriend(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _UlaIsBlocked(void *_pApp, int32_t argc, char **argv, unsigned char bHelp); +static void _CmdUlaCb(UserListApiRefT *pRef, UserListApiReturnTypeE eResponseType, UserListApiEventDataT *pUserApiEventData, void *pUserData); +static void _CmdUlaIsACb(UserListApiRefT *pRef, UserListApiIfATypeE eResponseType, UserListApiIsADataT *pUserApiEventData, void *pUserData); +static void _CmdUlaUpdateCb(UserListApiRefT *pRef, UserListApiNotifyTypeE eType, UserListApiNotifyDataT *pData, void *pUserData); +static int32_t _CmdUlaTickCb(ZContext *argz, int32_t argc, char *argv[]); + +/*** Variables *************************************************************************/ + +// Private variables +static T2SubCmdT _Ula_Commands[] = +{ + { "create", _UlaCreate }, + { "destroy", _UlaDestroy }, + { "getfriends", _UlaFriends }, + { "getblocked", _UlaBlocked }, + { "register", _UlaRegister }, + { "isfriend", _UlaIsFriend }, + { "isblocked", _UlaIsBlocked }, + { "", NULL } +}; + +static UlaAppT _Ula_App; + +// Public variables + +/*** Private Functions *****************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _CmdUlaTickCb + + \Description + UserList callback, called after command has been issued. + + \Input *argz - pointer to context + \Input argc - number of command-line arguments + \Input *argv[] - command-line argument list + + \Output + int32_t - result of zcallback, or zero to terminate + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static int32_t _CmdUlaTickCb(ZContext *argz, int32_t argc, char *argv[]) +{ + UlaAppT *pApp = &_Ula_App; + + if (pApp->bStarted != FALSE) + { + // check for kill + if (argc == 0) + { + ZPrintf("%s: killed\n", argv[0]); + UserListApiDestroy(pApp->pUla); + return(0); + } + + // update module + if (pApp->pUla != NULL) + { + UserListApiUpdate(pApp->pUla); + } + + // keep recurring + return(ZCallback(&_CmdUlaTickCb, 17)); + } + + return(ZLIB_STATUS_UNKNOWN); +} + +/*F********************************************************************************/ +/*! + \Function _CmdUlaCb + + \Description + userlist callback + + \Input *pRef - UserListApiRefT reference + \Input eResponseType - type of query + \Input *pUserApiEventData - result data struct + \Input pUserData - NULL + + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static void _CmdUlaCb(UserListApiRefT *pRef, UserListApiReturnTypeE eResponseType, UserListApiEventDataT *pUserApiEventData, void *pUserData) +{ + uint64_t uUserId; + + if (eResponseType == TYPE_USER_DATA) + { + DirtyUserToNativeUser(&uUserId, sizeof(uUserId), &pUserApiEventData->UserData.DirtyUser); + + if (pUserApiEventData->UserData.ExtendedUserData.uUserDataMask != 0) + { + ZPrintf("Friend: userid: %llu, gamertag %s\n", uUserId, pUserApiEventData->UserData.ExtendedUserData.Profile.strGamertag); + } + else + { + ZPrintf("Friend: userid: %llu\n", uUserId); + } + } + else if (eResponseType == TYPE_LIST_END) + { + ZPrintf("Friend: END OF LIST\n"); + } + +} + +/*F********************************************************************************/ +/*! + \Function _CmdUlaIsACb + + \Description + userlist callback + + \Input *pRef - UserListApiRefT reference + \Input eResponseType - type of query + \Input *pUserApiEventData - result data struct + \Input pUserData - NULL + + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static void _CmdUlaIsACb(UserListApiRefT *pRef, UserListApiIfATypeE eResponseType, UserListApiIsADataT *pUserApiEventData, void *pUserData) +{ + ZPrintf("%s: %s %s\n", eResponseType == USERLISTAPI_IS_FRIENDS ? "IsFriend" : "IsBlocked", + pUserApiEventData->eIsaType == USERLISTAPI_IS_OF_TYPE ? "is a" : "is NOT a", + eResponseType == USERLISTAPI_IS_FRIENDS ? "friend" : "blocked user"); +} + +/*F********************************************************************************/ +/*! + \Function _CmdUlaUpdateCb + + \Description + userlist callback + + \Input *pRef - UserListApiRefT reference + \Input eType - type of query + \Input *pData - result data struct + \Input pUserData - NULL + + + \Version 18/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static void _CmdUlaUpdateCb(UserListApiRefT *pRef, UserListApiNotifyTypeE eType, UserListApiNotifyDataT *pData, void *pUserData) +{ + if (eType == USERLISTAPI_NOTIFY_FRIENDLIST_UPDATE) + { + ZPrintf("userlist: Friend list update notification received!\n"); + } + else if (eType == USERLISTAPI_NOTIFY_BLOCKEDLIST_UPDATE) + { + ZPrintf("userlist: Blocked list update notification received!\n"); + } + else + { + ZPrintf("userlist: Unknown first party notification received!\n"); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UlaCreate + + \Description + UserList create + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UlaCreate(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UlaAppT *pApp = (UlaAppT *)_pApp; + int32_t iReceiveBufferSize; + + if ((bHelp == TRUE) || (argc != 2 && argc != 3)) + { + ZPrintf(" usage: %s create [receive buffer size]\n", argv[0]); + return; + } + + if (pApp->bStarted) + { + ZPrintf("%s: userlist already created, re-creating\n", argv[0]); + UserListApiDestroy(pApp->pUla); + } + + // get the receive buffer size (use 0=ANY if not specified) + iReceiveBufferSize = (argc == 3) ? strtol(argv[2], NULL, 10) : 0; + + // allocate ProtoUdp module + if ((pApp->pUla = UserListApiCreate(iReceiveBufferSize)) == NULL) + { + ZPrintf("%s: unable to create userlist module\n", argv[0]); + return; + } + + pApp->bStarted = TRUE; + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + ZCallback(_CmdUlaTickCb, 17); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UlaDestroy + + \Description + UserList destroy + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UlaDestroy(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UlaAppT *pApp = (UlaAppT *)_pApp; + + if ((bHelp == TRUE) || (argc != 2)) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + + UserListApiDestroy(pApp->pUla); + pApp->bZCallback = FALSE; + pApp->bStarted = FALSE; +} + +/*F*************************************************************************************/ +/*! + \Function _UlaFriends + + \Description + Fetch friends list + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UlaFriends(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UlaAppT *pApp = (UlaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s getfriends [user index]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + iResult = UserListApiGetListAsync(pApp->pUla, uUserIndex, USERLISTAPI_TYPE_FRIENDS, 1000, 0, NULL, &_CmdUlaCb, USERLISTAPI_MASK_PROFILE | USERLISTAPI_MASK_PRESENCE, NULL); + if (iResult == USERLISTAPI_ERROR_UNSUPPORTED) + { + ZPrintf("%s is not supported on this platform\n", argv[0], iResult); + } + else + { + ZPrintf("%s: UserListApiGetListAsync() returned %d\n", argv[0], iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UlaBlocked + + \Description + Fetch blocked list + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UlaBlocked(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UlaAppT *pApp = (UlaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + + if ((bHelp == TRUE) || (argc != 3)) + { + ZPrintf(" usage: %s getblocked [user index]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + iResult = UserListApiGetListAsync(pApp->pUla, uUserIndex, USERLISTAPI_TYPE_BLOCKED, 50, 0, NULL, &_CmdUlaCb, USERLISTAPI_MASK_PROFILE, NULL); + if (iResult == USERLISTAPI_ERROR_UNSUPPORTED) + { + ZPrintf("%s is not supported on this platform\n", argv[0], iResult); + } + else + { + ZPrintf("%s: UserListApiGetListAsync() returned %d\n", argv[0], iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UlaIsFriend + + \Description + Check if a user is a friend + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UlaIsFriend(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UlaAppT *pApp = (UlaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + DirtyUserT User; + uint64_t uUserId; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s isfriend [user index] [userid]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + + uUserId = ds_strtoull(argv[3], NULL, 10); + DirtyUserFromNativeUser(&User, &uUserId); + + iResult = UserListApiIsFriendAsync(pApp->pUla, uUserIndex, &User, &_CmdUlaIsACb, NULL); + if (iResult == USERLISTAPI_ERROR_UNSUPPORTED) + { + ZPrintf("%s is not supported on this platform\n", argv[0], iResult); + } + else + { + ZPrintf("%s: UserListApiIsFriendAsync() returned %d\n", argv[0], iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UlaIsBlocked + + \Description + Check if a user is blocked + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UlaIsBlocked(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UlaAppT *pApp = (UlaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + DirtyUserT User; + uint64_t uUserId; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s isblocked [user index] [userid]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + + uUserId = ds_strtoull(argv[3], NULL, 10); + DirtyUserFromNativeUser(&User, &uUserId); + + iResult = UserListApiIsBlockedAsync(pApp->pUla, uUserIndex, &User, &_CmdUlaIsACb, NULL); + if (iResult == USERLISTAPI_ERROR_UNSUPPORTED) + { + ZPrintf("%s is not supported on this platform\n", argv[0], iResult); + } + else + { + ZPrintf("%s: UserListApiIsBlockedAsync() returned %d\n", argv[0], iResult); + } +} + +/*F*************************************************************************************/ +/*! + \Function _UlaRegister + + \Description + Check if a user is blocked + + \Input *_pApp - pointer to FriendApi app + \Input argc - argument count + \Input **argv - argument list + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +static void _UlaRegister(void *_pApp, int32_t argc, char **argv, unsigned char bHelp) +{ + UlaAppT *pApp = (UlaAppT *)_pApp; + int32_t iResult; + uint32_t uUserIndex = 0; + DirtyUserT User; + uint8_t bTypeFriends = TRUE; + uint64_t uUserId; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s register [user index] [friend|blocked]\n", argv[0]); + return; + } + + if (!pApp->bStarted) + { + ZPrintf("%s: userlist not yet created\n", argv[0]); + return; + } + // get the receive buffer size (use 0=ANY if not specified) + uUserIndex = (uint32_t)strtol(argv[2], NULL, 10); + + uUserId = ds_strtoull(argv[3], NULL, 10); + DirtyUserFromNativeUser(&User, &uUserId); + + if (ds_stricmp(argv[3], "friend") == 0) + { + bTypeFriends = TRUE; + } + else if (ds_stricmp(argv[3], "blocked") == 0) + { + bTypeFriends = FALSE; + } + else + { + ZPrintf(" usage: %s register [user index] [friend|blocked]\n", argv[0]); + return; + } + + iResult = UserListApiRegisterUpdateEvent(pApp->pUla, uUserIndex, bTypeFriends == TRUE ? USERLISTAPI_NOTIFY_FRIENDLIST_UPDATE : USERLISTAPI_NOTIFY_BLOCKEDLIST_UPDATE, &_CmdUlaUpdateCb, NULL); + if (iResult == USERLISTAPI_ERROR_UNSUPPORTED) + { + ZPrintf("%s is not supported on this platform\n", argv[0], iResult); + } + else + { + ZPrintf("%s: UserListApiRegisterUpdateEvent() returned %d\n", argv[0], iResult); + } +} + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function CmdUserList + + \Description + Udp command. + + \Input *argz - unused + \Input argc - argument count + \Input **argv - argument list + + \Output + int32_t - zero + + \Version 18/02/2014 (amakoukji) +*/ +/**************************************************************************************F*/ +int32_t CmdUserList(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + UlaAppT *pApp = &_Ula_App; + unsigned char bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Ula_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the userlist module\n"); + T2SubCmdUsage(argv[0], _Ula_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _UlaCreate) && (pApp->pUla == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _UlaCreate(pApp, 1, &pCreate, bHelp); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/utf8.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/utf8.c new file mode 100644 index 00000000..67e6bde8 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/utf8.c @@ -0,0 +1,260 @@ +/*H********************************************************************************/ +/*! + \File utf8.c + + \Description + Tester routine for UTF-8 encoding/decoding. + + \Copyright + Copyright (c) 2003-2005 Electronic Arts Inc. + + \Version 03/25/2003 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/util/utf8.h" + +#include "libsample/zfile.h" +#include "libsample/zlib.h" +#include "libsample/zmem.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +#define NUM_SUBTABLES (3) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + + +// Private variables + + +/* + UTF-8 to 7bit ASCII translation tables +*/ + +// Unicode: C0 Controls and Basic Latin +static unsigned char _TransBasicLatinTo7Bit[128] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0xA, 255, 255, 0xD, 255, 255, // 00-0F + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 10-2F + ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', // 20-2F + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', // 30-3F + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', // 40-4F + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', // 50-5F + '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', // 60-6F + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 255 // 70-7F +}; + +// Unicode: C1 Controls and Latin-1 Supplement +static unsigned char _TransLatin1To7Bit[] = +{ + // NOTE: 0x80-0x9F skipped + 255, '!', 'c', 'L', 'o', 'Y', '|', 255, 255, 255, 255, 255, 255, 255, 255, '-', // A0-AF + 255, 255, 255, 255, '`', 'u', 'P', '.', ',', 255, 255, 255, 255, 255, 255, '?', // B0-BF + 'A', 'A', 'A', 'A', 'A', 'A', 255, 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', // C0-CF + 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', 255, 'U', 'U', 'U', 'U', 'Y', 255, 'B', // D0-DF + 'a', 'a', 'a', 'a', 'a', 'a', 255, 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', // E0-EF + 255, 'n', 'o', 'o', 'o', 'o', 'o', '/', 255, 'u', 'u', 'u', 'u', 'y', 255, 'y' // F0-FF +}; + +// Unicode: Latin Extended-A +static unsigned char _TransLatinExtATo7Bit[128] = +{ + 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', // 100-10F + 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', // 110-11F + 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', // 120-12F + 'I', 'i', 255, 255, 'J', 'j', 'K', 'k', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', // 130-13F + 'l', 'L', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', 'N', 'n', 'O', 'o', 'O', 'o', // 140-14F + 'O', 'o', 255, 255, 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', // 150-15F + 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', // 160-16F + 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 'f', // 170-17F +}; + +// master table +static Utf8TransTblT _TransTo7BitASCII[NUM_SUBTABLES+1] = +{ + { 0x0000, 0x007F, _TransBasicLatinTo7Bit}, // handle Basic Latin set + { 0x00A0, 0x00FF, _TransLatin1To7Bit }, // handle Latin1 + { 0x0100, 0x017F, _TransLatinExtATo7Bit }, // handle LatinExtA + + { 0x0000, 0x0000, NULL, }, // NULL terminator +}; + +/* + UTF-8 to 8bit ASCII translation tables +*/ + +static unsigned char _TransTo8BitASCIIa[256]; + +// master table +static Utf8TransTblT _TransTo8BitASCII[NUM_SUBTABLES+1] = +{ + { 0x0000, 0x00FF, _TransTo8BitASCIIa }, // handle Basic Latin set + { 0x0000, 0x0000, NULL, }, // NULL terminator +}; + + +// Public variables + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CmdUtf8Init8BitTransTbl + + \Description + + \Input None. + + \Output + None. + + \Version 03/25/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CmdUtf8Init8BitTransTbl(void) +{ + int32_t iEntry; + + for (iEntry = 0; iEntry < 256; iEntry++) + { + _TransTo8BitASCIIa[iEntry] = (unsigned char)iEntry; + } +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdUtf8 + + \Description + + \Input *argz - + \Input argc - + \Input **argv - + + \Output + int32_t - + + \Version 1.0 03/25/03 (JLB) First Version +*/ +/********************************************************************************F*/ +int32_t CmdUtf8(ZContext *argz, int32_t argc, char **argv) +{ + int32_t iFileSize; + char *pCharData; + + if (argc != 3) + { + ZPrintf(" test the utf8 module\n"); + ZPrintf(" usage: %s [l|r|s|7|8|c|u] [filename]\n", argv[0]); + ZPrintf(" r = replace\n"); + ZPrintf(" s = strip\n"); + ZPrintf(" 7 = translate UTF-8 to 7bit ASCII\n"); + ZPrintf(" 8 = translate UTF-8 to 8bit ASCII\n"); + ZPrintf(" c = translate UTF-8 to UCS-2\n"); + ZPrintf(" u = translate UCS-2 to UTF-8\n"); + return(0); + } + + _CmdUtf8Init8BitTransTbl(); + + if ((pCharData = ZFileLoad(argv[2], &iFileSize, TRUE)) != NULL) + { + unsigned char *pUTF8Data = NULL; + uint16_t *pUCS2Data = NULL; + int32_t iUCS2Size = 0; + int32_t iUTF8Size = 0; + char cOption; + + cOption = argv[1][0]; + if (cOption != 'u') + { + ZPrintf("\n\n--Input--\n\n%s", pCharData); + } + + if (cOption == 'l') + { + iUTF8Size = Utf8StrLen(pCharData); + ZPrintf(" strlen('%s')=%d\n", pCharData, iUTF8Size); + } + else if (cOption == 'r') + { + Utf8Replace(pCharData, iFileSize, pCharData, '*'); + ZPrintf("\n\n--Output--\n\n%s", pCharData); + } + else if (cOption == 's') + { + Utf8Strip(pCharData, iFileSize, pCharData); + ZPrintf("\n\n--Output--\n\n%s", pCharData); + } + else if (cOption == '7') + { + Utf8TranslateTo8Bit(pCharData, iFileSize, pCharData, '*', _TransTo7BitASCII); + ZPrintf("\n\n--7 Bit ASCII--\n\n%s", pCharData); + } + else if (cOption == '8') + { + Utf8TranslateTo8Bit(pCharData, iFileSize, pCharData, '*', _TransTo8BitASCII); + ZPrintf("\n\n--8 Bit ASCII--\n\n%s", pCharData); + } + else if (cOption == 'c') + { + pUCS2Data = (uint16_t *)ZMemAlloc(iFileSize*sizeof(int16_t)); + iUCS2Size = Utf8DecodeToUCS2(pUCS2Data, iFileSize, pCharData) * sizeof(int16_t); + } + else if (cOption == 'u') + { + pUTF8Data = (unsigned char *)ZMemAlloc(iFileSize); + iUTF8Size = Utf8EncodeFromUCS2((char *)pUTF8Data, iFileSize, (const uint16_t *)pCharData); + } + else + { + ZPrintf("Unsupported option '%c'\n",cOption); + } + + // free file buffer + ZMemFree(pCharData); + + // write out UCS-2 data, if any + if ((pUCS2Data != NULL) && (iUCS2Size > 0)) + { + ZFileSave("out_ucs2.txt", (const char *)pUCS2Data, iUCS2Size, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_BINARY); + ZMemFree(pUCS2Data); + } + + // write out UTF-8 data, if any + if ((pUTF8Data != NULL) && (iUTF8Size > 0)) + { + ZFileSave("out_utf8.txt", (char *)pUTF8Data, iUTF8Size, ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_BINARY); + ZMemFree(pUTF8Data); + } + } + else + { + ZPrintf("Unable to open input file.\n"); + } + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/voice.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/voice.c new file mode 100644 index 00000000..3106bc82 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/voice.c @@ -0,0 +1,1583 @@ +/*H********************************************************************************/ +/*! + \File voice.c + + \Description + Test Voice over IP + + \Copyright + Copyright (c) Electronic Arts 2004-2005. ALL RIGHTS RESERVED. + + \Version 07/22/2004 (jbrookes) First Version + \Version 07/09/2012 (akirchner) Added functionality to play pre-recorded file +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#ifdef _WIN32 + #pragma warning(push,0) + #include + #pragma warning(pop) +#endif + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voipdef.h" +#include "DirtySDK/voip/voipgroup.h" +#include "DirtySDK/voip/voipnarrate.h" +#include "DirtySDK/voip/voiptranscribe.h" + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) +#include "DirtySDK/DirtySock/dirtyaddr.h" +#endif + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testersubcmd.h" +#include "testermodules.h" + +#if !defined(DIRTYCODE_PS4) && (!defined(DIRTYCODE_XBOXONE) || !defined(DIRTYCODE_GDK)) + #include "DirtySDK/voip/voipcodec.h" +#endif + +#if defined(DIRTYCODE_PC) + #include "t2hostresource.h" + #include "voipaux/voipspeex.h" +#endif +#if defined(DIRTYCODE_PS4) || defined(DIRTYCODE_PC) || defined(DIRTYCODE_STADIA) + #include "voipaux/voipopus.h" +#endif + +/*** Defines **********************************************************************/ + +// defined in math.h but not part of the standard so just define it here +#define M_PI 3.14159265358979323846 + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct VoiceAppT // gamer daemon state +{ + VoipRefT *pVoip; + VoipGroupRefT *pVoipGroup; + int16_t *pBuffer; + ZFileT iFile; + int32_t iAddress; + int32_t iConnID; + int32_t iSamples; + int32_t iModulation; + uint8_t bRecording; + uint8_t bPlaying; + uint8_t bZCallback; + uint8_t _pad; + uint32_t uClientId; +} VoiceAppT; + +/*** Function Prototypes **********************************************************/ + +static void _VoiceCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +#if !defined(DIRTYCODE_PS4) && !defined(DIRTYCODE_STADIA) +static void _VoiceCodecControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +#endif +static void _VoiceSTT(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceTTS(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceRecord(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoicePlay(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceModulate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceResetChannels(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceSelectChannel(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceShowChannels(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceVolume(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +static void _VoiceConn(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceUserLocal(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceUserRemote(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceSetLocalUser(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _VoiceActivateLocalUser(void *CmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables ********************************************************************/ + +static T2SubCmdT _Voice_Commands[] = +{ + { "create", _VoiceCreate }, + { "destroy", _VoiceDestroy }, + { "connect", _VoiceConnect }, +#if !defined(DIRTYCODE_PS4) && (!defined(DIRTYCODE_XBOXONE) || !defined(DIRTYCODE_GDK)) && !defined(DIRTYCODE_STADIA) + { "cdec", _VoiceCodecControl }, +#endif + { "ctrl", _VoiceControl }, + { "record", _VoiceRecord }, + { "play", _VoicePlay }, + { "modulate", _VoiceModulate }, + { "resetchans", _VoiceResetChannels }, + { "selectchan", _VoiceSelectChannel }, + { "showchans", _VoiceShowChannels }, + { "volume", _VoiceVolume }, + { "conn", _VoiceConn }, + { "userlocal", _VoiceUserLocal }, + { "userremote", _VoiceUserRemote }, + { "set", _VoiceSetLocalUser }, + { "activate", _VoiceActivateLocalUser }, + { "stt", _VoiceSTT }, + { "tts", _VoiceTTS }, + { "", NULL }, +}; + +static VoiceAppT _Voice_App = { NULL, NULL, NULL, (ZFileT)NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _VoiceSpkrModulate + + \Description + Voice modulation using a simple ring modulation filter. Used to test + modification of voice data using the speaker callback. + + \Input *pApp - module state + \Input *pFrameData - pointer to output data + \Input iNumSamples - number of samples in output data + + \Version 11/27/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoiceSpkrModulate(VoiceAppT *pApp, int16_t *pFrameData, int32_t iNumSamples) +{ + static int _iCounter = 0; + int32_t iSample; + double dSin; + + if (pApp->iModulation == 0) + { + return; + } + + for (iSample = 0; iSample < iNumSamples; iSample += 1) + { + dSin = sin(2 * M_PI * pApp->iModulation * _iCounter/15999); + pFrameData[iSample] = (int16_t)(((int32_t)pFrameData[iSample] * (int32_t)(dSin*32768.0))/32768); + if (++_iCounter >= 16000) + { + _iCounter = 0; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoiceEvntCallback + + \Description + Event callback - optional callback to receive voice events + + \Input *pVoip - voip module state + \Input eCbType - callback type (VOIP_CBTYPE_*) + \Input iValue - callback value + \Input *pUserData - user data pointer + + \Version 10/31/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoiceEvntCallback(VoipRefT *pVoip, VoipCbTypeE eCbType, int32_t iValue, void *pUserData) +{ + static const char *_strEventName[] = { "VOIP_CBTYPE_AMBREVENT", "VOIP_CBTYPE_HSETEVENT", "VOIP_CBTYPE_FROMEVENT", "VOIP_CBTYPE_SENDEVENT", "VOIP_CBTYPE_TTOSEVENT" }; + ZPrintf("voice: %s event callback (iValue=%d)\n", _strEventName[eCbType], iValue); +} + +/*F********************************************************************************/ +/*! + \Function _VoiceSpkrCallback + + \Description + Speaker callback - optional callback to receive voice data ready for + output; this module uses this callback to capture voice data for writing + to a file. + + \Input *pFrameData - pointer to output data + \Input iNumSamples - number of samples in output data + \Input *pUserData - app ref + + \Version 10/31/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoiceSpkrCallback(int16_t *pFrameData, int32_t iNumSamples, void *pUserData) +{ + VoiceAppT *pApp = (VoiceAppT *)pUserData; + int32_t iDataSize, iResult; + + // apply modulation if enabled + _VoiceSpkrModulate(pApp, pFrameData, iNumSamples); + + // no file to write to? + if (pApp->iFile <= 0) + { + return; + } + + // write audio to output file + iDataSize = iNumSamples * sizeof(*pFrameData); + if ((iResult = ZFileWrite(pApp->iFile, pFrameData, iDataSize)) != iDataSize) + { + ZPrintf("voice: error %d writing %d samples to file\n", iResult, iNumSamples); + } + else + { + pApp->iSamples += iNumSamples; + ZPrintf("voice: wrote %d samples to output file (%d total)\n", iNumSamples, pApp->iSamples); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoiceFirstyParytIdCallback + + \Description + Retrieves the id of the local user for testing loopback + + \Input uPersonaId - unused + \Input *pUserData - unused + + \Output + uint64_t - local player id or zero (error/unhandled) +*/ +/********************************************************************************F*/ +static uint64_t _VoiceFirstyParytIdCallback(uint64_t uPersonaId, void *pUserData) +{ + uint64_t uPlayerId = 0; + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + NetConnStatus('xuid', 0, &uPlayerId, sizeof(uPlayerId)); + #elif defined(DIRTYCODE_STADIA) + NetConnStatus('gpid', 0, &uPlayerId, sizeof(uPlayerId)); + #endif + return(uPlayerId); +} + +/*F********************************************************************************/ +/*! + \Function _VoiceDisplayTranscribedTextCallback + + \Description + Callback handling notification about transcribed text being ready for local display. + + \Input iConnId - connection identifier + \Input iRemoteUserIndex - remote user index + \Input *pText - transcribed text + \Input *pUserData - callback user data + + \Output + int32_t - TRUE + + \Version 10/31/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoiceDisplayTranscribedTextCallback(int32_t iConnId, int32_t iUserIndex, const char *pText, void *pUserData) +{ + ZPrintf("voice: %s user[%d] \"%s\"\n", ((iConnId != -1) ? "remote" : "local"), iUserIndex, pText); + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function _CmdVoiceDevSelectProc + + \Description + Voice device select for PC, + + \Input win - window handle + \Input msg - window message + \Input wparm - message parameter + \Input lparm - message parameter + + \Output + LRESULT - FALSE + + \Version 07/22/2004 (jbrookes) +*/ +/********************************************************************************F*/ +#if defined(DIRTYCODE_PC) +static LRESULT CALLBACK _CmdVoiceDevSelectProc(HWND win, UINT msg, WPARAM wparm, LPARAM lparm) +{ + // handle init special (create the class) + if (msg == WM_INITDIALOG) + { + char pDeviceName[64]; + int32_t iDevice, iNumDevices; + VoipRefT *pVoip; + + pVoip = VoipGetRef(); + + // add input devices to combo box and select the first item + iNumDevices = VoipStatus(pVoip, 'idev', -1, NULL, 0); + for (iDevice = 0; iDevice < iNumDevices; iDevice++) + { + VoipStatus(pVoip, 'idev', iDevice, pDeviceName, 64); + SendDlgItemMessage(win, IDC_VOICEINP, CB_ADDSTRING, 0, (LPARAM)pDeviceName); + } + // select default input device, if available; otherwise just pick the first + if ((iDevice = VoipStatus(pVoip, 'idft', 0, NULL, 0)) == -1) + { + iDevice = 0; + } + SendDlgItemMessage(win, IDC_VOICEINP, CB_SETCURSEL, iDevice, 0); + + // add output devices to combo box and select the first item + iNumDevices = VoipStatus(pVoip, 'odev', -1, NULL, 0); + for (iDevice = 0; iDevice < iNumDevices; iDevice++) + { + VoipStatus(pVoip, 'odev', iDevice, pDeviceName, 64); + SendDlgItemMessage(win, IDC_VOICEOUT, CB_ADDSTRING, 0, (LPARAM)pDeviceName); + } + // select default output device, if available; otherwise just pick the first + if ((iDevice = VoipStatus(pVoip, 'odft', VOIP_DEFAULTDEVICE_VOICECOM, NULL, 0)) == -1) + { + iDevice = 0; + } + SendDlgItemMessage(win, IDC_VOICEOUT, CB_SETCURSEL, iDevice, 0); + } + + // handle ok + if ((msg == WM_COMMAND) && (LOWORD(wparm) == IDOK)) + { + VoipRefT *pVoip; + int32_t iInpDev, iOutDev; + + pVoip = VoipGetRef(); + + iInpDev = SendDlgItemMessage(win, IDC_VOICEINP, CB_GETCURSEL, 0, 0); + iOutDev = SendDlgItemMessage(win, IDC_VOICEOUT, CB_GETCURSEL, 0, 0); + + VoipControl(pVoip, 'idev', iInpDev, NULL); + VoipControl(pVoip, 'odev', iOutDev, NULL); + + DestroyWindow(win); + } + + // let windows handle + return(FALSE); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _VoiceDestroyApp + + \Description + Destroy app, freeing modules. + + \Input *pApp - app state + + \Output + None. + + \Version 12/12/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoiceDestroyApp(VoiceAppT *pApp) +{ + if (pApp->pVoipGroup != NULL) + { + VoipGroupDestroy(pApp->pVoipGroup); + } + if (pApp->pVoip) + { + VoipShutdown(pApp->pVoip, 0); + } + ds_memclr(pApp, sizeof(*pApp)); +} + +/*F********************************************************************************/ +/*! + \Function _VoiceConnSharingCb + + \Description + Connection Sharing Callback fired by the VoipGroup + + \Input *pVoipGroup - voip group ref + \Input eCbType - event identifier + \Input iConnId - connection ID + \Input *pUserData - user callback data + \Input bSending - client sending flag + \Input bReceiving - client receiving flag + + \Version 02/13/2019 (eesponda) +*/ +/********************************************************************************F*/ +static void _VoiceConnSharingCb(VoipGroupRefT *pVoipGroup, ConnSharingCbTypeE eCbType, int32_t iConnId, void *pUserData, uint8_t bSending, uint8_t bReceiving) +{ + // no behavior is implemented, the connections should be tracked if we want to support multiple concurrent groups + ZPrintf("voice: conn sharing callback hit with %s for id %d\n", + eCbType == VOIPGROUP_CBTYPE_CONNSUSPEND ? "VOIPGROUP_CBTYPE_CONNSUSPEND" : "VOIPGROUP_CBTYPE_CONNRESUME", iConnId); +} + +/* + + Voice Commands + +*/ + +/*F*************************************************************************************/ +/*! + \Function _VoiceCreate + + \Description + Voice subcommand - create voice module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output None. + + \Version 12/12/2005 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + int32_t iArg=2, iMaxPeers, iQuality=-1; + #if defined(DIRTYCODE_PC) + uint8_t bDevSelect = TRUE; + #elif defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + const char *pOpt; + #endif + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s create -nodevselect -qual \n", argv[0]); + return; + } + + // check for device select disable + #if defined(DIRTYCODE_PC) + if ((argc > 2) && !strcmp(argv[iArg], "-nodevselect")) + { + bDevSelect = FALSE; + iArg += 1; + argc -= 1; + } + #endif + + // set quality? + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + if ((argc > 2) && ((pOpt = strstr(argv[iArg], "-qual")) != NULL)) + { + pOpt += strlen("-qual") + 1; + iQuality = strtol(pOpt, NULL, 10); + iArg += 1; + argc -= 1; + } + #endif + + // allow setting of max number of remote clients, defaulting to sixteen + iMaxPeers = (argc == 3) ? strtol(argv[iArg], NULL, 10) : 16; + + // start up voice module if it isn't already started + if ((pApp->pVoip = VoipGetRef()) == NULL) + { + if ((pApp->pVoip = VoipStartup(iMaxPeers, 0)) == NULL) + { + ZPrintf("voice: failed to create voip module\n"); + return; + } + } + + // register and select speex (for PC) and set the default volume to 90 + #if defined(DIRTYCODE_PC) + VoipControl(pApp->pVoip, 'creg', 'spex', (void *)&VoipSpeex_CodecDef); + VoipControl(pApp->pVoip, 'svol', 90, NULL); + #endif + #if defined(DIRTYCODE_PS4) || defined(DIRTYCODE_PC) || defined(DIRTYCODE_STADIA) + VoipControl(pApp->pVoip, 'creg', 'opus', (void *)&VoipOpus_CodecDef); + VoipControl(pApp->pVoip, 'cdec', 'opus', NULL); + #endif + + // set speaker callback + VoipSpkrCallback(pApp->pVoip, _VoiceSpkrCallback, pApp); + + // set first party id callback + VoipRegisterFirstPartyIdCallback(pApp->pVoip, _VoiceFirstyParytIdCallback, pApp); + + if (iQuality != -1) + { + VoipControl(pApp->pVoip, 'qual', iQuality, NULL); + } + + // create the voipgroup, with max 8 groups + pApp->pVoipGroup = VoipGroupCreate(8); + // set event callback + VoipGroupSetEventCallback(pApp->pVoipGroup, _VoiceEvntCallback, pApp); + // set the connection sharing callback + VoipGroupSetConnSharingEventCallback(pApp->pVoipGroup, _VoiceConnSharingCb, pApp); + + // set our clientId using local address + pApp->uClientId = SocketInfo(NULL, 'addr', 0, NULL, 0); + VoipGroupControl(pApp->pVoipGroup, 'clid', pApp->uClientId, 0, NULL); + + // select output device (PC only) + #if defined(DIRTYCODE_PC) + if (bDevSelect) + { + ShowWindow(CreateDialogParam(GetModuleHandle(NULL), "VOICEDEVSELECT", HWND_DESKTOP, (DLGPROC)_CmdVoiceDevSelectProc,0), TRUE); + } + #endif +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceDestroy + + \Description + Voice subcommand - destroy voice module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output None. + + \Version 12/12/2005 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + if (pApp->pBuffer) + { + ZMemFree((void *) pApp->pBuffer); + pApp->pBuffer = NULL; + } + + _VoiceDestroyApp(pApp); +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceConnect + + \Description + Voice subcommand - connect to peer + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output None. + + \Version 12/12/2005 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + + if ((bHelp == TRUE) || (argc > 5)) + { + ZPrintf(" usage: %s connect [rclientid] [connindex]\n", argv[0]); + return; + } + + if (argc >= 3) + { + uint32_t uRemoteAddr, uRemotePort, uManglePort, uRemoteClientId; + int32_t iClientIndex; + + // get remote address, port, and local port (if specified) in addr:port:port2 format + SockaddrInParse2(&uRemoteAddr, (int32_t *)&uRemotePort, (int32_t *)&uManglePort, argv[2]); + if (uRemotePort == 0) + { + uRemotePort = VOIP_PORT; + } + if (uManglePort == 0) + { + uManglePort = VOIP_PORT; + } + + // get remote client id, or use remote address if unspecified + uRemoteClientId = (argc >= 4) ? strtoul(argv[3], NULL, 16) : uRemoteAddr; + iClientIndex = (argc >= 5) ? strtol(argv[4], NULL, 10) : VOIP_CONNID_NONE; + + // disable loop-back mode, in case it was previously set + VoipGroupControl(pApp->pVoipGroup, 'loop', FALSE, 0, NULL); + + // log connection attempt + ZPrintf("%s: connecting to %a:%u:%u (clientId=0x%08x)\n", argv[0], uRemoteAddr, uManglePort, uRemotePort, uRemoteClientId); + + // start connect to remote peer + VoipGroupControl(pApp->pVoipGroup, 'vcid', iClientIndex, 0, &pApp->uClientId); + pApp->iConnID = VoipGroupConnect(pApp->pVoipGroup, iClientIndex, uRemoteAddr, uManglePort, uRemotePort, pApp->uClientId, 0, FALSE, uRemoteClientId); + } + else + { + #if !defined(DIRTYCODE_XBOXONE) || !defined(DIRTYCODE_GDK) + // set loopback + ZPrintf("%s: enabling loopback\n", argv[0]); + VoipGroupControl(pApp->pVoipGroup, 'loop', TRUE, 0, NULL); + #else + ZPrintf("%s: loopback not supported on xboxone\n", argv[0]); + #endif + } +} + +#if !defined(DIRTYCODE_PS4) && (!defined(DIRTYCODE_XBOXONE) || !defined(DIRTYCODE_GDK)) && !defined(DIRTYCODE_STADIA) +/*F*************************************************************************************/ +/*! + \Function _VoiceCodecControl + + \Description + Voice control subcommand - set control options + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output None. + + \Version 05/06/2011 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceCodecControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + if (bHelp == TRUE) + { + ZPrintf(" usage: %s cdec [arg2]\n", argv[0]); + return; + } + + if ((argc > 4) && (argc < 7)) + { + int32_t iIdent, iCmd, iValue = 0, iValue2 = 0; + + iIdent = ZGetIntArg(argv[2]); + iCmd = ZGetIntArg(argv[3]); + + if (argc > 4) + { + iValue = ZGetIntArg(argv[4]); + } + if (argc > 5) + { + iValue2 = ZGetIntArg(argv[5]); + } + + VoipCodecControl(iIdent, iCmd, iValue, iValue2, NULL); + } +} +#endif + +/*F*************************************************************************************/ +/*! + \Function _VoiceControl + + \Description + Voice control subcommand - set control options + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output None. + + \Version 05/05/2011 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + void *pValue = NULL; + int32_t iValue2; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s ctrl \n", argv[0]); + return; + } + + if (argc > 2) + { + int32_t iCmd, iValue = 0; + + // get control selector + iCmd = ZGetIntArg(argv[2]); + + // get control value if specified + if (argc > 3) + { + iValue = ZGetIntArg(argv[3]); + } + // if control is clid save the value + if (iCmd == 'clid') + { + pApp->uClientId = (unsigned)iValue; + } + + // if enabling speech-to-text, set callback + if (iCmd == 'stot') + { + iValue2 = (argc > 4) ? ZGetIntArg(argv[4]) : 0; + pValue = &iValue2; + VoipGroupSetDisplayTranscribedTextCallback(pApp->pVoipGroup, _VoiceDisplayTranscribedTextCallback, pApp); + } + else if (argc > 4) + { + pValue = argv[4]; + } + + VoipControl(pApp->pVoip, iCmd, iValue, pValue); + } + else + { + ZPrintf("%s: invalid ctrl command\n", argv[0]); + } +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceRecord + + \Description + Voice subcommand - start/stop recording + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 10/28/2011 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceRecord(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + uint8_t bStart; + + if ((bHelp == TRUE) || (argc < 3) || (argc > 4)) + { + ZPrintf(" usage: %s record [start|stop] \n", argv[0]); + return; + } + + // see if we are stopping or starting + if (!strcmp(argv[2], "start")) + { + bStart = TRUE; + } + else if (!strcmp(argv[2], "stop")) + { + bStart = FALSE; + } + else + { + ZPrintf("%s: invalid start/stop argument %s\n", argv[0], argv[2]); + return; + } + + if (bStart) + { + if (pApp->bPlaying) + { + ZPrintf("%s: cannot start, currently playing\n", argv[0]); + return; + } + + if (pApp->bRecording) + { + ZPrintf("%s: cannot start, already recording\n", argv[0]); + return; + } + if (argc != 4) + { + ZPrintf("%s: cannot record without a filename to write to\n", argv[0]); + return; + } + if ((pApp->iFile = ZFileOpen(argv[3], ZFILE_OPENFLAG_CREATE|ZFILE_OPENFLAG_WRONLY|ZFILE_OPENFLAG_BINARY)) < 0) + { + ZPrintf("%s: error %d opening file '%s' for recording\n", pApp->iFile, argv[0], argv[3]); + } + ZPrintf("%s: opened file '%s' for recording\n", argv[0], argv[3]); + pApp->bRecording = TRUE; + pApp->iSamples = 0; + } + else + { + int32_t iResult; + ZFileT iFileId; + + if (!pApp->bRecording) + { + ZPrintf("%s: cannot stop, not recording\n", argv[0]); + return; + } + + // save fileid, clear it from ref (stop the callback from writing to it) + iFileId = pApp->iFile; + pApp->iFile = 0; + pApp->bRecording = FALSE; + ZPrintf("%s: recording stopped, %d samples (%d bytes) written\n", argv[0], pApp->iSamples, pApp->iSamples * sizeof(int16_t)); + + // close the file + if ((iResult = ZFileClose(iFileId)) < 0) + { + ZPrintf("%s: error %d closing recording file\n", argv[0], iResult); + } + } +} + +/*F*************************************************************************************/ +/*! + \Function _VoicePlay + + \Description + Voice subcommand - start/stop playing + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 7/9/2012 (akirchner) +*/ +/**************************************************************************************F*/ +static void _VoicePlay(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT * pApp = & _Voice_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s play [start|stop] \n", argv[0]); + return; + } + + if ((argc >= 3) && (!strcmp(argv[2], "start"))) + { + int32_t iResult; + + if (pApp->bRecording) + { + ZPrintf("%s: cannot start, currently recording\n", argv[0]); + return; + } + + if (pApp->bPlaying) + { + ZPrintf("%s: cannot start, already playing\n", argv[0]); + return; + } + + if (argc != 4) + { + ZPrintf(" usage: %s play start \n", argv[0]); + return; + } + + // open file + if ((pApp->iFile = ZFileOpen(argv[3], ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) < 0) + { + ZPrintf("%s: error %d opening file '%s' for playing\n", argv[0], pApp->iFile, argv[3]); + return; + } + + // get size + if ((pApp->iSamples = ZFileSize(pApp->iFile)) < 0) + { + ZPrintf("%s: error %d invalid size of file '%s'\n", argv[0], pApp->iFile, argv[3]); + return; + } + + // read file + if ((pApp->pBuffer = (int16_t *) ZMemAlloc(pApp->iSamples)) == NULL) + { + ZPrintf("%s: error allocating %d bytes of memory\n", argv[0], pApp->iSamples); + return; + } + + if ((iResult = ZFileSeek(pApp->iFile, 0, ZFILE_SEEKFLAG_SET)) < 0) + { + ZMemFree((void *) pApp->pBuffer); + pApp->pBuffer = NULL; + + ZPrintf("%s: error %d seeking begging of file '%s'\n", argv[0], iResult, argv[3]); + return; + } + + if (ZFileRead(pApp->iFile, (void *) pApp->pBuffer, pApp->iSamples) < 0) + { + ZMemFree((void *) pApp->pBuffer); + pApp->pBuffer = NULL; + + ZPrintf("%s: error %d reading file '%s'\n", argv[0], pApp->iFile, argv[3]); + return; + } + + // close file + if ((iResult = ZFileClose(pApp->iFile)) < 0) + { + ZMemFree((void *) pApp->pBuffer); + pApp->pBuffer = NULL; + + ZPrintf("%s: error %d closing file '%s' for playing\n", argv[0], iResult, argv[3]); + return; + } + + if (VoipControl(pApp->pVoip, 'play', pApp->iSamples, (void *) pApp->pBuffer) < 0) + { + ZMemFree((void *) pApp->pBuffer); + pApp->pBuffer = NULL; + + ZPrintf("%s: failed to activate playing mode\n", argv[0]); + return; + } + + ZPrintf("%s: opened file '%s' of %d bytes for playing\n", argv[0], argv[3], pApp->iSamples); + + pApp->iFile = 0; + pApp->bPlaying = TRUE; + } + else if ((argc >= 3) && (!strcmp(argv[2], "stop"))) + { + if (argc != 3) + { + ZPrintf(" usage: %s play stop\n", argv[0]); + return; + } + + if (!pApp->bPlaying) + { + ZPrintf("%s: cannot stop, not playing\n", argv[0]); + return; + } + + if (VoipControl(pApp->pVoip, 'play', 0, NULL) < 0) + { + ZMemFree((void *) pApp->pBuffer); + + ZPrintf("%s: failed to deactivate playing mode\n", argv[0]); + return; + } + + pApp->bPlaying = FALSE; + + if (pApp->pBuffer) + { + ZMemFree((void *) pApp->pBuffer); + pApp->pBuffer = NULL; + } + + ZPrintf("%s: playing stopped\n", argv[0]); + } + else + { + ZPrintf("%s: invalid argument. Neither start nor stop\n", argv[0]); + return; + } +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceModulate + + \Description + Voice subcommand - set voice modulation (zero=disabled) + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2018 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceModulate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT * pApp = & _Voice_App; + + if ((bHelp == TRUE) || (argc > 3)) + { + ZPrintf(" usage: %s modulate \n", argv[0]); + return; + } + + // get modulation rate + pApp->iModulation = (argc == 3) ? strtol(argv[2], NULL, 10) : 1000; +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceResetChannels + + \Description + Voice subcommand - reset channel configuration + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 02/16/2012 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _VoiceResetChannels(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + if ((bHelp == TRUE) || (argc != 2)) + { + ZPrintf(" usage: %s resetchans\n", argv[0]); + return; + } + + ZPrintf("%s: resetting channel config\n", argv[0]); + VoipResetChannels(VoipGetRef(), 0); +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceSelectChannel + + \Description + Voice subcommand - subscribe/unsubcribes to/from a voip channel + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 02/16/2012 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _VoiceSelectChannel(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + int32_t iChannel; + VoipChanModeE eMode; + + if ((bHelp == TRUE) || (argc != 4)) + { + ZPrintf(" usage: %s selectchan \n", argv[0]); + return; + } + + sscanf(argv[2], "%d", &iChannel); + if ((iChannel < 0) && (iChannel > 63)) + { + ZPrintf("%s: invalid channel id argument: %s\n", argv[0], argv[2]); + return; + } + + if (!strcmp(argv[3], "talk")) + { + eMode = VOIP_CHANSEND; + } + else if (!strcmp(argv[3], "listen")) + { + eMode = VOIP_CHANRECV; + } + else if (!strcmp(argv[3], "both")) + { + eMode = VOIP_CHANSENDRECV; + } + else if (!strcmp(argv[3], "none")) + { + eMode = VOIP_CHANNONE; + } + else + { + ZPrintf("%s: invalid channel mode argument: %s\n", argv[0], argv[3]); + return; + } + + ZPrintf("%s: setting channel %d:%s\n", argv[0], iChannel, argv[3]); + VoipSelectChannel(VoipGetRef(), 0, iChannel, eMode); +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceTTS + + \Description + Voice subcommand - Send Text as Voice + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 05/14/2018 (tcho) +*/ +/**************************************************************************************F*/ +static void _VoiceTTS(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + char strText[1024]; + + if (bHelp) + { + ZPrintf(" usage: %s tts config [params] or gender [male|female] or \"text you wish to say\" [user index]\n", argv[0]); + return; + } + + if (!ds_stricmp(argv[2], "config")) + { + if (argc == 6) + { + VoipNarrateProviderE eProvider = VOIPNARRATE_PROVIDER_NONE; + if (!ds_stricmp(argv[3], "watson")) eProvider = VOIPNARRATE_PROVIDER_IBMWATSON; + if (!ds_stricmp(argv[3], "microsoft")) eProvider = VOIPNARRATE_PROVIDER_MICROSOFT; + if (!ds_stricmp(argv[3], "google")) eProvider = VOIPNARRATE_PROVIDER_GOOGLE; + if (!ds_stricmp(argv[3], "amazon")) eProvider = VOIPNARRATE_PROVIDER_AMAZON; + VoipConfigNarration(VoipGetRef(), eProvider, argv[4], argv[5]); + } + else + { + ZPrintf(" usage: %s tts config [none|watson|microsoft|google] \n", argv[0]); + } + } + else if (!ds_stricmp(argv[2], "gender")) + { + VoipSynthesizedSpeechCfgT VoipSpeechConfig; + ds_memclr(&VoipSpeechConfig, sizeof(VoipSpeechConfig)); + if (!ds_stricmp(argv[3], "female")) + { + VoipSpeechConfig.iPersonaGender = 1; + } + VoipControl(VoipGetRef(), 'voic', 0, &VoipSpeechConfig); + } + else + { + sscanf(argv[2], "%256[^\n]", strText); + VoipControl(VoipGetRef(), 'ttos', (argc > 3) ? ds_strtoll(argv[3], NULL, 10) : 0, strText); + } +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceSTT + + \Description + Voice subcommand - Speech to Text functionality + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 12/13/2018 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _VoiceSTT(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoipTranscribeProviderE eProvider = VOIPTRANSCRIBE_PROVIDER_NONE; + VoipTranscribeTransportE eTransport = VOIPTRANSCRIBE_TRANSPORT_HTTP; + VoipTranscribeFormatE eFormat = VOIPTRANSCRIBE_FORMAT_LI16; + if (bHelp || ds_stricmp(argv[2], "config") || ((ds_stricmp(argv[3], "none") && (argc != 8)))) + { + ZPrintf(" usage: %s stt config [none|watson|microsoft|google|amazon] [li16|wav|opus] [http|h2|ws] \n", argv[0]); + return; + } + if (!ds_stricmp(argv[3], "watson")) eProvider = VOIPTRANSCRIBE_PROVIDER_IBMWATSON; + if (!ds_stricmp(argv[3], "microsoft")) eProvider = VOIPTRANSCRIBE_PROVIDER_MICROSOFT; + if (!ds_stricmp(argv[3], "google")) eProvider = VOIPTRANSCRIBE_PROVIDER_GOOGLE; + if (!ds_stricmp(argv[3], "amazon")) eProvider = VOIPTRANSCRIBE_PROVIDER_AMAZON; + if (!ds_stricmp(argv[4], "wav")) eFormat = VOIPTRANSCRIBE_FORMAT_WAV16; + if (!ds_stricmp(argv[4], "opus")) eFormat = VOIPTRANSCRIBE_FORMAT_OPUS; + if (!ds_stricmp(argv[5], "h2")) eTransport = VOIPTRANSCRIBE_TRANSPORT_HTTP2; + if (!ds_stricmp(argv[5], "ws")) eTransport = VOIPTRANSCRIBE_TRANSPORT_WEBSOCKETS; + VoipConfigTranscription(VoipGetRef(), VOIPTRANSCRIBE_PROFILE_CONSTRUCT(eProvider, eFormat, eTransport), argv[6], argv[7]); +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceShowChannels + + \Description + Voice subcommand - show local voip channel config + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 02/16/2012 (mclouatre) +*/ +/**************************************************************************************F*/ +static void _VoiceShowChannels(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + int32_t iChannelSlot, iChannelId; + char *pChannelModeStr; + VoipChanModeE eChannelMode; + char strChannelId[8]; + char strChannelSlot[32]; + char strChannelCfg[32]; + + if ((bHelp == TRUE) || (argc != 2)) + { + ZPrintf(" usage: %s showchans\n", argv[0]); + return; + } + + for (iChannelSlot = 0; iChannelSlot < VoipGroupStatus(NULL, 'chnc', 0, NULL, 0); iChannelSlot++) + { + // get channel id and channel mode + iChannelId = VoipGroupStatus(NULL, 'chnl', iChannelSlot, &eChannelMode, sizeof(eChannelMode)); + // init to default + pChannelModeStr = "UNKNOWN"; + + // initialize channel mode string and + switch (eChannelMode) + { + case VOIP_CHANNONE: + pChannelModeStr = "UNSUBSCRIBED"; + ds_snzprintf(strChannelId, sizeof(strChannelId), "n/a"); + break; + case VOIP_CHANSEND: + pChannelModeStr = "TALK-ONLY"; + ds_snzprintf(strChannelId, sizeof(strChannelId), "%03d", iChannelId); + break; + case VOIP_CHANRECV: + pChannelModeStr = "LISTEN-ONLY"; + ds_snzprintf(strChannelId, sizeof(strChannelId), "%03d", iChannelId); + break; + case VOIP_CHANSENDRECV: + pChannelModeStr = "TALK+LISTEN"; + ds_snzprintf(strChannelId, sizeof(strChannelId), "%03d", iChannelId); + break; + } + + // build channel slot string + ds_snzprintf(strChannelSlot, sizeof(strChannelSlot), "VOIP channel slot %d", iChannelSlot); + + // build channel config string + ds_snzprintf(strChannelCfg, sizeof(strChannelCfg), "id=%s mode=%s", strChannelId, pChannelModeStr); + + ZPrintf(" %s ----> %s\n", strChannelSlot, strChannelCfg); + } +} + +/*F*************************************************************************************/ +/*! + \Function _VoiceVolume + + \Description + Voice subcommand - set the output volume level + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Output None. + + \Version 04/22/2009 (cadam) +*/ +/**************************************************************************************F*/ +static void _VoiceVolume(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + float fOutputLevel; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s volume \n", argv[0]); + return; + } + + if (argc == 3) + { + fOutputLevel = strtod(argv[2], NULL); + ZPrintf("%s: setting volume to %f\n", argv[0], fOutputLevel); + VoipControl(pApp->pVoip, 'plvl', 0, &fOutputLevel); + } +} + +static void _VoiceSetLocalUser(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + int32_t iLocalUserIndex; + uint32_t bRegister; + int32_t iOnOffIndex; + +#if defined(DIRTYCODE_PS4) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + if (bHelp == TRUE || argc != 4) + { + ZPrintf(" usage: %s set \n", argv[0]); + return; + } + + iLocalUserIndex = strtol(argv[2], NULL, 10); + iOnOffIndex = 3; +#else + if (bHelp == TRUE || argc != 3) + { + ZPrintf(" usage: %s set \n", argv[0]); + return; + } + + iLocalUserIndex = 0; + iOnOffIndex = 2; +#endif + + if (strcmp(argv[iOnOffIndex], "on") == 0) + { + bRegister = TRUE; + ZPrintf("%s: registering local user at index %d with voip sub-system\n", argv[0], iLocalUserIndex); + } + else + { + bRegister = FALSE; + ZPrintf("%s: unregistering local user at index %d from voip sub-system\n", argv[0], iLocalUserIndex); + } + + VoipSetLocalUser(pApp->pVoip, iLocalUserIndex, bRegister); +} + +static void _VoiceActivateLocalUser(void *CmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + uint32_t iLocalUserIndex; + uint32_t bActivate; + int32_t iOnOffIndex; + +#if defined(DIRTYCODE_PS4) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + if (bHelp == TRUE || argc != 4) + { + ZPrintf(" usage: %s participate \n", argv[0]); + return; + } + + iLocalUserIndex = strtol(argv[2], NULL, 10); + iOnOffIndex = 3; +#else + if (bHelp == TRUE || argc != 3) + { + ZPrintf(" usage: %s participate \n", argv[0]); + return; + } + + iLocalUserIndex = 0; + iOnOffIndex = 2; +#endif + + if (strcmp(argv[iOnOffIndex], "on") == 0) + { + bActivate = TRUE; + ZPrintf("%s: promoting local user %d to participating state\n", argv[0], iLocalUserIndex); + } + else + { + bActivate = FALSE; + ZPrintf("%s: forcing local user %d out of participating state\n", argv[0], iLocalUserIndex); + } + + VoipGroupActivateLocalUser(pApp->pVoipGroup, iLocalUserIndex, bActivate); +} + +static void _VoiceConn(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + uint32_t uOutput; + int32_t iConnId = 0; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s connremote \n", argv[0]); + return; + } + + if (argc == 3) + { + iConnId = strtod(argv[2], NULL); + uOutput = VoipGroupConnStatus(pApp->pVoipGroup, iConnId); + ZPrintf("Status = %08x\n", uOutput); + if (uOutput == 0) + { + ZPrintf("No VoipConnRemote flags set\n"); + } + else + { + if (uOutput & VOIP_CONN_CONNECTED) + ZPrintf("VOIP_CONN_CONNECTED\n"); + if (uOutput & VOIP_CONN_BROADCONN) + ZPrintf("VOIP_CONN_BROADCONN\n"); + if (uOutput & VOIP_CONN_ACTIVE) + ZPrintf("VOIP_CONN_ACTIVE\n"); + if (uOutput & VOIP_CONN_STOPPED) + ZPrintf("VOIP_CONN_STOPPED\n"); + } + } +} + +static void _VoiceUserLocal(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + uint32_t uOutput; + int32_t iUserIndex = 0; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s userlocal \n", argv[0]); + return; + } + + if (argc == 3) + { + iUserIndex = strtod(argv[2], NULL); + uOutput = VoipLocalUserStatus(pApp->pVoip, iUserIndex); + ZPrintf("Status = %08x\n", uOutput); + if (uOutput == 0) + { + ZPrintf("No VoipUserLocal flags set\n"); + } + else + { + if (uOutput & VOIP_LOCAL_USER_HEADSETOK) + ZPrintf("VOIP_LOCAL_USER_HEADSETOK\n"); + if (uOutput & VOIP_LOCAL_USER_TALKING) + ZPrintf("VOIP_LOCAL_USER_TALKING\n"); + if (uOutput & VOIP_LOCAL_USER_SENDVOICE) + ZPrintf("VOIP_LOCAL_USER_SENDVOICE\n"); + if (uOutput & VOIP_LOCAL_USER_INPUTDEVICEOK) + ZPrintf("VOIP_LOCAL_USER_INPUTDEVICEOK\n"); + if (uOutput & VOIP_LOCAL_USER_OUTPUTDEVICEOK) + ZPrintf("VOIP_LOCAL_USER_OUTPUTDEVICEOK\n"); + } + } +} + +static void _VoiceUserRemote(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + VoiceAppT *pApp = &_Voice_App; + uint32_t uOutput; + int32_t iUserIndex = 0; + int32_t iConnId = 0; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s userremote \n", argv[0]); + return; + } + + if (argc == 4) + { + iConnId = strtod(argv[2], NULL); + iUserIndex = strtod(argv[3], NULL); + uOutput = VoipGroupRemoteUserStatus(pApp->pVoipGroup, iConnId, iUserIndex); + ZPrintf("Status = %08x\n", uOutput); + if (uOutput == 0) + { + ZPrintf("No VoipUserRemote flags set\n"); + } + else + { + if (uOutput & VOIP_REMOTE_USER_HEADSETOK) + ZPrintf("VOIP_REMOTE_USER_HEADSETOK\n"); + if (uOutput & VOIP_REMOTE_USER_RECVVOICE) + ZPrintf("VOIP_REMOTE_CONN_RECVVOICE\n"); + } + } +} + + +/*F********************************************************************************/ +/*! + \Function _CmdVoiceCb + + \Description + Update voice command + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 07/22/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdVoiceCb(ZContext *argz, int32_t argc, char *argv[]) +{ + VoiceAppT *pApp = &_Voice_App; + + // check for kill + if (argc == 0) + { + _VoiceDestroyApp(pApp); + ZPrintf("%s: killed\n", argv[0]); + return(0); + } + + // keep running + return(ZCallback(&_CmdVoiceCb, 16)); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdVoice + + \Description + Initiate Voice connection. + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 07/22/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdVoice(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + VoiceAppT *pApp = &_Voice_App; + unsigned char bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_Voice_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the voip module\n"); + T2SubCmdUsage(argv[0], _Voice_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _VoiceCreate) && (pApp->pVoip == NULL)) + { + // only create the new ref if we are not exercising VoiceControl('dcde')\VoiceControl('drat') + // to modify the default sample rate and codec BEFORE the module creation + if ( !((strcmp(argv[1], "ctrl") == 0) && (strcmp(argv[2], "dcde") == 0)) && + !((strcmp(argv[1], "ctrl") == 0) && (strcmp(argv[2], "drat") == 0)) ) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _VoiceCreate(pApp, 1, &pCreate, bHelp); + } + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + // one-time install of periodic callback + if (pApp->bZCallback == FALSE) + { + pApp->bZCallback = TRUE; + return(ZCallback(_CmdVoiceCb, 16)); + } + else + { + return(0); + } +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/ws.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/ws.c new file mode 100644 index 00000000..a4ce1fa6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/ws.c @@ -0,0 +1,671 @@ +/*H********************************************************************************/ +/*! + \File ws.c + + \Description + Test WebSockets + + \Notes + Websockets test URL: ws://echo.websocket.org/ (see + http://www.websocket.org/echo.html). + + \Copyright + Copyright (c) Electronic Arts 2012. + + \Version 11/27/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protowebsocket.h" + +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testersubcmd.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct WSAppT +{ + ProtoWebSocketRefT *pWebSocket; + char *pSendBuf; + int32_t iSendLen; + int32_t iSendOff; + char *pRecvBuf; + int32_t iRecvBufLen; + uint8_t bUseMessageApis; // if TRUE, use message semantics for send/recv; specified as send argument +} WSAppT; + +/*** Function Prototypes **********************************************************/ + +static void _WSCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _WSDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _WSConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _WSDisconnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _WSControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _WSStatus(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); +static void _WSSend(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +/*** Variables ********************************************************************/ + +static T2SubCmdT _WS_Commands[] = +{ + { "create", _WSCreate }, + { "destroy", _WSDestroy }, + { "connect", _WSConnect }, + { "disconnect", _WSDisconnect }, + { "ctrl", _WSControl }, + { "stat", _WSStatus }, + { "send", _WSSend }, + { "", NULL }, +}; + +static WSAppT _WS_App = { NULL, NULL, 0, 0, "", 0, 0 }; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _WSResetApp + + \Description + Reset app, freeing buffers / resetting state + + \Input *pApp - app state + + \Version 03/31/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _WSResetApp(WSAppT *pApp) +{ + if (pApp->pSendBuf != NULL) + { + ZMemFree(pApp->pSendBuf); + pApp->pSendBuf = NULL; + } + pApp->iSendLen = 0; + pApp->iSendOff = 0; +} + +/*F********************************************************************************/ +/*! + \Function _WSDestroyApp + + \Description + Destroy app, freeing modules. + + \Input *pApp - app state + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _WSDestroyApp(WSAppT *pApp) +{ + // reset state + _WSResetApp(pApp); + + // free recv buffer + if (pApp->pRecvBuf != NULL) + { + ZMemFree(pApp->pRecvBuf); + pApp->pRecvBuf = NULL; + } + pApp->iRecvBufLen = 0; + + // destroy module + if (pApp->pWebSocket != NULL) + { + ProtoWebSocketDestroy(pApp->pWebSocket); + } + ds_memclr(pApp, sizeof(*pApp)); +} + +/* + + WS Commands + +*/ + +/*F*************************************************************************************/ +/*! + \Function _WSCreate + + \Description + WS subcommand - create websocket module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _WSCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + WSAppT *pApp = &_WS_App; + int32_t iBufSize = 4*1024; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s create \n", argv[0]); + return; + } + + // if we already have one, skip + if (pApp->pWebSocket != NULL) + { + ZPrintf("%s: websocket module already created\n", argv[0]); + return; + } + + // bufsize arg? + if (argc > 2) + { + iBufSize = (int32_t)strtol(argv[2], NULL, 10); + } + + // create app receive buffer (+1 to hold terminator character) + if ((pApp->pRecvBuf = ZMemAlloc(iBufSize+1)) == NULL) + { + ZPrintf("%s: could not allocate memory for application receive buffer\n", argv[0]); + _WSDestroyApp(pApp); + return; + } + pApp->iRecvBufLen = iBufSize+1; + + // create a websocket module if it isn't already started + if ((pApp->pWebSocket = ProtoWebSocketCreate(iBufSize)) == NULL) + { + ZPrintf("%s: error creating websocket module\n", argv[0]); + _WSDestroyApp(pApp); + } +} + +/*F*************************************************************************************/ +/*! + \Function _WSDestroy + + \Description + WS subcommand - destroy websocket module + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _WSDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + WSAppT *pApp = &_WS_App; + + if (bHelp == TRUE) + { + ZPrintf(" usage: %s destroy\n", argv[0]); + return; + } + + _WSDestroyApp(pApp); +} + +/*F*************************************************************************************/ +/*! + \Function _WSConnect + + \Description + WS subcommand - connect to server + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _WSConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + WSAppT *pApp = &_WS_App; + int32_t iArg, iStartArg = 2; + char strApndHdr[1024] = ""; + + if ((bHelp == TRUE) || (argc < 3)) + { + ZPrintf(" usage: %s connect \n", argv[0]); + return; + } + + // reset app state + _WSResetApp(pApp); + + // check for connect args + for (iArg = iStartArg; (iArg < argc) && (argv[iArg][0] == '-'); iArg += 1) + { + if (!ds_strnicmp(argv[iArg], "-header=", 8)) + { + ds_strnzcat(strApndHdr, argv[iArg]+8, sizeof(strApndHdr)); + ds_strnzcat(strApndHdr, "\r\n", sizeof(strApndHdr)); + } + // skip any option arguments to find url and (optionally) filename + iStartArg += 1; + } + + // log connection attempt + ZPrintf("%s: connecting to '%s'\n", argv[0], argv[iStartArg]); + + // set append header? + if (strApndHdr[0] != '\0') + { + ProtoWebSocketControl(pApp->pWebSocket, 'apnd', 0, 0, strApndHdr); + } + + // start connect to remote user + ProtoWebSocketConnect(pApp->pWebSocket, argv[iStartArg]); +} + +/*F*************************************************************************************/ +/*! + \Function _WSDisconnect + + \Description + WS subcommand - disconnect from server + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/30/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _WSDisconnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + WSAppT *pApp = &_WS_App; + + if ((bHelp == TRUE) || (argc != 2)) + { + ZPrintf(" usage: %s disconnect\n", argv[0]); + return; + } + + // log connection attempt + ZPrintf("%s: disconnecting from server\n", argv[0], argv[2]); + + // start connect to remote user + ProtoWebSocketDisconnect(pApp->pWebSocket); +} + +/*F*************************************************************************************/ +/*! + \Function _WSControl + + \Description + WS control subcommand - set control options + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _WSControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + WSAppT *pApp = &_WS_App; + int32_t iCmd, iValue = 0, iValue2 = 0; + void *pValue = NULL; + + if ((bHelp == TRUE) || (argc < 3) || (argc > 6)) + { + ZPrintf(" usage: %s ctrl [cmd] \n", argv[0]); + return; + } + + // get the command + iCmd = ZGetIntArg(argv[2]); + + // get optional arguments + if (argc > 3) + { + iValue = ZGetIntArg(argv[3]); + } + if (argc > 4) + { + iValue2 = ZGetIntArg(argv[4]); + } + if (argc > 5) + { + pValue = argv[5]; + } + + // issue the control call + ProtoWebSocketControl(pApp->pWebSocket, iCmd, iValue, iValue2, pValue); +} + +/*F*************************************************************************************/ +/*! + \Function _WSStatus + + \Description + WS status subcommand - query module status + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/27/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _WSStatus(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + WSAppT *pApp = &_WS_App; + int32_t iCmd, iResult; + char strBuffer[256] = ""; + + if ((bHelp == TRUE) || (argc < 3) || (argc > 5)) + { + ZPrintf(" usage: %s stat \n", argv[0]); + return; + } + + // get the command + iCmd = ZGetIntArg(argv[2]); + + // issue the status call + iResult = ProtoWebSocketStatus(pApp->pWebSocket, iCmd, strBuffer, sizeof(strBuffer)); + + // report result + ZPrintf("ws: ProtoWebSocketStatus('%C') returned %d (\"%s\")\n", iCmd, iResult, strBuffer); +} + +/*F*************************************************************************************/ +/*! + \Function _WSSend + + \Description + WS status subcommand - send data + + \Input *pCmdRef - unused + \Input argc - argument count + \Input *argv[] - argument list + + \Version 11/29/2012 (jbrookes) +*/ +/**************************************************************************************F*/ +static void _WSSend(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp) +{ + WSAppT *pApp = &_WS_App; + int32_t iMsgLen, iStartArg=2, iMinArgs=4; + char *pMessage; + + if ((bHelp == TRUE) || (argc < iMinArgs)) + { + ZPrintf(" usage: %s send [-msg] -f -m \"message\"\n", argv[0]); + return; + } + + if (pApp->pSendBuf != NULL) + { + ZPrintf("%s: already sending data, please try later\n", argv[0]); + return; + } + + if (!strcmp(argv[iStartArg], "-msg")) + { + pApp->bUseMessageApis = TRUE; + iStartArg += 1; + iMinArgs += 1; + } + + if (!strcmp(argv[iStartArg], "-f") && (argc == iMinArgs)) + { + char *pFileData; + int32_t iFileSize; + + // try to open file + if ((pFileData = ZFileLoad(argv[iStartArg+1], &iFileSize, ZFILE_OPENFLAG_RDONLY|ZFILE_OPENFLAG_BINARY)) != NULL) + { + pMessage = pFileData; + iMsgLen = iFileSize; + } + else + { + ZPrintf("%s: could not open file '%s' for sending\n", argv[0], argv[iStartArg+1]); + return; + } + } + else if (!strcmp(argv[iStartArg], "-m")) + { + if ((iMsgLen = (int32_t)strlen(argv[iStartArg+1])) > 0) + { + pMessage = ZMemAlloc(iMsgLen+1); + ds_strnzcpy(pMessage, argv[iStartArg+1], iMsgLen+1); + } + else + { + ZPrintf("%s: will not send empty message\n", argv[0]); + return; + } + } + else + { + ZPrintf("%s: unknown send option '%s'\n", argv[0], argv[iStartArg]); + return; + } + + // set up send params + pApp->pSendBuf = pMessage; + pApp->iSendLen = iMsgLen; + pApp->iSendOff = 0; +} + +/*F*************************************************************************************/ +/*! + \Function _CmdWSSend + + \Description + Wraps ProtoWebSocketSend/Message + + \Input *pApp - WS app ref + \Input *pBuffer - send data + \Input iLength - length of send data + + \Version 04/11/2017 (jbrookes) +*/ +/**************************************************************************************F*/ +static int32_t _CmdWSSend(WSAppT *pApp, const char *pBuffer, int32_t iLength) +{ + return ((pApp->bUseMessageApis) ? ProtoWebSocketSendMessage(pApp->pWebSocket, pBuffer, iLength) : ProtoWebSocketSend(pApp->pWebSocket, pBuffer, iLength)); +} + +/*F*************************************************************************************/ +/*! + \Function _CmdWSRecv + + \Description + Wraps ProtoWebSocketRecv/Message + + \Input *pApp - WS app ref + \Input *pBuffer - [out] recv buffer + \Input iLength - length of recv buffer + + \Version 04/11/2017 (jbrookes) +*/ +/**************************************************************************************F*/ +static int32_t _CmdWSRecv(WSAppT *pApp, char *pBuffer, int32_t iLength) +{ + return ((pApp->bUseMessageApis) ? ProtoWebSocketRecvMessage(pApp->pWebSocket, pBuffer, iLength) : ProtoWebSocketRecv(pApp->pWebSocket, pBuffer, iLength)); +} + +/*F********************************************************************************/ +/*! + \Function _CmdWSCb + + \Description + Update WS command + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_T -standard return value + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CmdWSCb(ZContext *argz, int32_t argc, char *argv[]) +{ + WSAppT *pApp = &_WS_App; + int32_t iResult; + + // check for kill + if (argc == 0) + { + _WSDestroyApp(pApp); + ZPrintf("%s: killed\n", argv[0]); + return(0); + } + + // give life to the module + if (pApp->pWebSocket != NULL) + { + // update the module + ProtoWebSocketUpdate(pApp->pWebSocket); + + // processing, if we're connected + if (ProtoWebSocketStatus(pApp->pWebSocket, 'stat', NULL, 0) == 1) + { + // try and receive some data + if ((iResult = _CmdWSRecv(pApp, pApp->pRecvBuf, pApp->iRecvBufLen - 1)) > 0) + { + NetPrintf(("%s: received %d byte server frame\n", argv[0], iResult)); + // null terminate it so we can print it + pApp->pRecvBuf[iResult] = '\0'; + // print it + NetPrintWrap(pApp->pRecvBuf, 80); + // clear message flag + pApp->bUseMessageApis = FALSE; + } + else if (iResult == SOCKERR_NOMEM) + { + // our buffer is too small, so double it + int32_t iRecvBufLen = pApp->iRecvBufLen * 2; + char *pRecvBuf = ZMemAlloc(iRecvBufLen); + if (pRecvBuf != NULL) + { + ZPrintf("%s: increasing receive buffer to %d bytes\n", argv[0], iRecvBufLen); + ds_memcpy_s(pRecvBuf, iRecvBufLen, pApp->pRecvBuf, pApp->iRecvBufLen); + ZMemFree(pApp->pRecvBuf); + pApp->pRecvBuf = pRecvBuf; + pApp->iRecvBufLen = iRecvBufLen; + } + else + { + ZPrintf("%s: error allocating %d bytes of memory for receive buffer\n", iRecvBufLen); + } + } + else if (iResult < 0) + { + ZPrintf("%s: error %d receiving\n", argv[0], iResult); + // clear message flag + pApp->bUseMessageApis = FALSE; + // keep running + return(ZCallback(&_CmdWSCb, 16)); + } + + // try and send some data + if (pApp->pSendBuf != NULL) + { + if ((iResult = _CmdWSSend(pApp, pApp->pSendBuf + pApp->iSendOff, pApp->iSendLen - pApp->iSendOff)) > 0) + { + pApp->iSendOff += iResult; + if (pApp->iSendOff == pApp->iSendLen) + { + ZPrintf("%s: sent %d byte message\n", argv[0], pApp->iSendLen); + ZMemFree(pApp->pSendBuf); + pApp->pSendBuf = NULL; + } + } + else if (iResult < 0) + { + ZPrintf("%s: error %d sending message\n", argv[0], iResult); + } + } + } + } + + // keep running + return(ZCallback(&_CmdWSCb, 16)); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CmdWS + + \Description + WS (WebSocket) command + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output + int32_t - standard return value + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CmdWS(ZContext *argz, int32_t argc, char *argv[]) +{ + T2SubCmdT *pCmd; + WSAppT *pApp = &_WS_App; + int32_t iResult = 0; + uint8_t bHelp; + + // handle basic help + if ((argc <= 1) || (((pCmd = T2SubCmdParse(_WS_Commands, argc, argv, &bHelp)) == NULL))) + { + ZPrintf(" test the websocket module\n"); + T2SubCmdUsage(argv[0], _WS_Commands); + return(0); + } + + // if no ref yet, make one + if ((pCmd->pFunc != _WSCreate) && (pApp->pWebSocket == NULL)) + { + char *pCreate = "create"; + ZPrintf(" %s: ref has not been created - creating\n", argv[0]); + _WSCreate(pApp, 1, &pCreate, bHelp); + iResult = ZCallback(_CmdWSCb, 16); + } + + // hand off to command + pCmd->pFunc(pApp, argc, argv, bHelp); + + // one-time install of periodic callback + if (pCmd->pFunc == _WSCreate) + { + iResult = ZCallback(_CmdWSCb, 16); + } + return(iResult); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/xml.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/xml.c new file mode 100644 index 00000000..9136573b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/modules/xml.c @@ -0,0 +1,320 @@ +/*H********************************************************************************/ +/*! + \File xml.c + + \Description + Test the XML parser. + + \Copyright + Copyright (c) 1999-2005 Electronic Arts Inc. + + \Version 10/04/1999 (gschaefer) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/xml/xmlparse.h" +#include "DirtySDK/xml/xmlformat.h" +#include "testermodules.h" + +#include "libsample/zlib.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// Variables + +static const char _CmdXml_strData1[] = +"" +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +" " +"]> " +"" +" 123456789012345678" +" 0" +" Successfully Retrieved Data" +" Dudeman" +" Dudeman" +" 0" +" 1" +" dudeman@ea.com" +" 25" +" 12" +" 1970" +" USA" +" 1" +" 0" +""; + +static const char _CmdXml_strData2[] = +" " +" " +" " +" " +" " +" " +" " +" " +""; + +static const char _CmdXml_strData3[] = +#if 0 +"" +"1" +"" +"" +"" +"xml to entity failed, xml is invalid.unexpected element (uri:\"\", local:\"html\"). Expected elements are <{}error>" +//"xml to entity failed, xml is invalid.unexpected element (uri:\"\", local:\"html\"). Expected elements are {}error" +"" +"" +"" +#elif 0 +"" +"" +"" +"" +"" +"" +"My Default Persona" +"" +"" +"" +"" +"" +"" +"Feature not supported yet." +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"Feature not supported yet." +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"Requested list not found." +"" +"" +"" +"" +"" +"" +"" +"My Default Persona" +"" +"" +"" +"" +"" +"" +"Feature not supported yet." +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"" +"Feature not supported yet." +"" +"" +"" +"" +"" +"" +"" +"" +"" +"Requested list not found." +"" +"" +"<" +"list name='block" +"ed_users'/>" +"" +" " +#endif +; + + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CmdXml + + \Description + Test the Xml parser + + \Input *argz - environment + \Input argc - standard number of arguments + \Input *argv[] - standard arg list + + \Output standard return value + + \Version 10/04/1999 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t CmdXml(ZContext *argz, int32_t argc, char *argv[]) +{ + int32_t iIndex; + char strData[1024]; + const char *pXml; + + for (pXml = _CmdXml_strData3; pXml != NULL; pXml = XmlSkip(pXml)) + { + ZPrintf("%s\n", XmlComplete(pXml) ? "valid" : "invalid"); + } + + XmlPrintFmt((_CmdXml_strData3, "")); + + // check usage + if (argc < 2) + { + ZPrintf(" test xmlparse and xmlformat modules\n"); + ZPrintf(" usage: %s testnum [1..3]\n", argv[0]); + return(0); + } + + // get the index + iIndex = atoi(argv[1]); + + // do the tests + if (iIndex == 1) + { + XmlContentGetString(XmlFind(_CmdXml_strData1, "get-account-info-reply.email"), strData, sizeof(strData), ""); + ZPrintf("email=%s\n", strData); + + ZPrintf("id=%lld\n", XmlContentGetInteger64(XmlFind(_CmdXml_strData1, "get-account-info-reply.id"), -1)); + ZPrintf("bdateyear=%d\n", XmlContentGetInteger(XmlFind(_CmdXml_strData1, "get-account-info-reply.bdateyear"), -1)); + } + if (iIndex == 2) + { + pXml = XmlFind(_CmdXml_strData2, "TOURNAMENT.MATCHES"); + ZPrintf("hit=%s\n", pXml); + + ZPrintf("beginDate=%lld\n", XmlAttribGetInteger64(XmlFind(_CmdXml_strData2, "TOURNAMENT"), "beginDate", -1)); + ZPrintf("id=%d\n", XmlAttribGetInteger(XmlFind(_CmdXml_strData2, "TOURNAMENT"), "id", -1)); + } + if (iIndex == 3) + { + char strBuffer[1024]; + + // encode some XML + XmlInit(strBuffer, sizeof(strBuffer), XML_FL_WHITESPACE); + + XmlTagStart(strBuffer, "TestFormat1"); + XmlElemAddString(strBuffer, "strText", "Some chars that should be encoded: =, <, >, &, \", \x7f"); + XmlTagEnd(strBuffer); + + XmlTagStart(strBuffer, "TestFormat2"); + XmlElemAddString(strBuffer, "strText", "Some chars that should not be encoded: \t, \r, \n"); + XmlTagEnd(strBuffer); + + XmlFinish(strBuffer); + + ZPrintf("encoded xml:\n------------\n%s\n-----------\n", strBuffer); + + // now parse the encoded xml + XmlContentGetString(XmlFind(strBuffer, "TestFormat1.strText"), strData, sizeof(strData), ""); + ZPrintf("test1=%s\n", strData); + + XmlContentGetString(XmlFind(strBuffer, "TestFormat2.strText"), strData, sizeof(strData), ""); + ZPrintf("test2=%s\n", strData); + } + + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/T2Client.cpp b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/T2Client.cpp new file mode 100644 index 00000000..8f520e76 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/T2Client.cpp @@ -0,0 +1,795 @@ +/*H********************************************************************************/ +/*! + \File T2Client.cpp + + \Description + Defines the entry point for the application. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/21/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#endif + +#pragma warning(push,0) +#include +#pragma warning(pop) + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/util/jsonformat.h" +#include "DirtySDK/util/jsonparse.h" +#include "libsample/zmem.h" +#include "libsample/zmemtrack.h" +#include "testermodules.h" +#include "testerprofile.h" +#include "testerregistry.h" +#include "testerclientcore.h" +#include "testercomm.h" +#include "T2ClientResource.h" + +/*** Defines **********************************************************************/ + +#define MAX_LOADSTRING (100) + +// custom events +#define WM_CLEARSCREEN (WM_APP+1000) + +/*** Type Definitions *************************************************************/ + +typedef struct T2ClientGeometryT +{ + uint32_t uTopBorder; + uint32_t uConsoleBottomToCommandTop; + uint32_t uCommandHeight; + uint32_t uBottomBorder; + uint32_t uLeftBorder; + uint32_t uRightBorder; +} T2ClientGeometryT; + +typedef struct T2ClientT +{ + HINSTANCE hInst; //!< current instance + TCHAR strTitle[MAX_LOADSTRING]; //!< The title bar text + TCHAR strWindowClass[MAX_LOADSTRING]; //!< the main window class name + char strCurrentProfile[TESTERPROFILE_PROFILEFILENAME_SIZEDEFAULT]; // active profile + TesterClientCoreT *pClientCore; //!< pointer to client core structure + int32_t iTimerID; //!< timer used to call NetIdle(); + WNDPROC CommandEditProc; //!< edit box handler + WNDPROC ConsoleEditProc; //!< console box handler + int32_t iCommandHistoryOffset; //!< command history offset + T2ClientGeometryT Geometry; //!< window geometry + char strConnectParams[512]; //!< connect parameters + char strScript[256]; //!< script to execute + int32_t iScrollbackSize; +} T2ClientT; + +/*** forward Declaration ********************************************************************/ +VOID CALLBACK _T2ClientTimerCallback(HWND hDlg, UINT uMsg, UINT_PTR pIDEvent, DWORD dTime); + + +/*** Variables ********************************************************************/ + +static T2ClientT T2Client = {NULL, "", "", "", NULL, 0, NULL, NULL, 1, {0,0,0,0,0,0}}; + +/*** External Functions ***********************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _T2ClientDisplayOutput + + \Description + Take input from TesterConsole and dump it to the specified edit box. + + \Input *pBuf - string containing the debug output to display + \Input iLen - length of buffer + \Input iRefcon - user-specified parameter (unused) + \Input *pRefptr - user-specified parameter (window pointer) + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2ClientDisplayOutput(const char *pBuf, int32_t iLen, int32_t iRefcon, void *pRefptr) +{ + int32_t iAdd; + HWND Ctrl = (HWND)pRefptr; + char strTempText[16*1024], *pTempText, cPrev; + + // replace cr with crlf for proper display + for (pTempText = strTempText, cPrev = '\0'; (*pBuf != '\0') && ((pTempText-strTempText) < (sizeof(strTempText)-1)); pBuf++, pTempText++) + { + if ((*pBuf == '\n') && (cPrev != '\r')) + { + *pTempText++ = '\r'; + } + *pTempText = cPrev = *pBuf; + } + *pTempText = '\0'; + + // see if we need to delete old data + if (SendMessage(Ctrl, WM_GETTEXTLENGTH, 0, 0) > T2Client.iScrollbackSize) { + SendMessage(Ctrl, EM_SETSEL, 0, 8192); + SendMessage(Ctrl, EM_REPLACESEL, FALSE, (LPARAM)""); + iAdd = SendMessage(Ctrl, WM_GETTEXTLENGTH, 0, 0); + SendMessage(Ctrl, EM_SETSEL, iAdd-1, iAdd); + SendMessage(Ctrl, EM_REPLACESEL, FALSE, (LPARAM)""); + } + + iAdd = SendMessage(Ctrl, WM_GETTEXTLENGTH, 0, 0); + SendMessage(Ctrl, EM_SETSEL, iAdd, iAdd); + SendMessage(Ctrl, EM_REPLACESEL, FALSE, (LPARAM)strTempText); + SendMessage(Ctrl, EM_SCROLLCARET, 0, 0); +} + + +/*F********************************************************************************/ +/*! + \Function _T2ClientCommandEditProc + + \Description + Message handler for the command line edit box. Intercepts uparrow, etc. + + \Input hDlg - dialog handle + \Input uMessage - message identifier + \Input wParam - message specifics (pointer to uint32_t) + \Input lParam - message specifics (pointer) + + \Output LRESULT - message handling status return code + + \Version 04/06/2005 (jfrank) +*/ +/********************************************************************************F*/ +static LRESULT CALLBACK _T2ClientCommandEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + const char *pCommandLine = NULL; + + if (msg == WM_KEYDOWN) + { + // adjust if necessary + if (wParam == VK_UP) + { + T2Client.iCommandHistoryOffset--; + pCommandLine = TesterClientCoreGetHistoricalCommand(T2Client.pClientCore, &(T2Client.iCommandHistoryOffset), NULL, 0); + } + else if (wParam == VK_DOWN) + { + T2Client.iCommandHistoryOffset++; + pCommandLine = TesterClientCoreGetHistoricalCommand(T2Client.pClientCore, &(T2Client.iCommandHistoryOffset), NULL, 0); + } + + // if we got a key, redraw and quit + if (pCommandLine != NULL) + { + SetWindowText(hWnd, pCommandLine); + SendMessage(hWnd, EM_SETSEL, 0, -1); + SendMessage(hWnd, EM_SETSEL, (WPARAM)-1, 0); + return(0); + } + } + // not handled by this handler - call the original one + return(CallWindowProc( static_cast(T2Client.CommandEditProc), hWnd, msg, wParam, lParam )); +} + + +/*F********************************************************************************/ +/*! + \Function _T2ClientConsoleEditProc + + \Description + Message handler for the console box. Intercepts messages. + + \Input hDlg - dialog handle + \Input uMessage - message identifier + \Input wParam - message specifics (pointer to uint32_t) + \Input lParam - message specifics (pointer) + + \Output LRESULT - message handling status return code + + \Version 04/06/2005 (jfrank) +*/ +/********************************************************************************F*/ +static LRESULT CALLBACK _T2ClientConsoleEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + int32_t wmId, wmEvent; + + // find out the control ID which requires attention + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + if (msg == WM_CONTEXTMENU) + { + // Create the popup menu + HMENU hPopup; + POINT p; + + GetCursorPos(&p); + hPopup = LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE( IDR_CONSOLECONTEXTMENU )); + hPopup = GetSubMenu(hPopup, 0); + TrackPopupMenu(hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, 0, hWnd, NULL); + return(0); + } + else if (msg == WM_COMMAND) + { + if (wmId == ID_CONSOLEOPTIONS_CLEAR) + { + TesterConsoleT *pConsole; + + if ((pConsole = (TesterConsoleT *)TesterRegistryGetPointer("CONSOLE")) != NULL) + { + TesterConsoleClear(pConsole); + SetWindowText(hWnd, ""); + } + return(0); + } + else if (wmId == ID_CONSOLEOPTIONS_SELECTALL) + { + SendMessage(hWnd, EM_SETSEL, 0, -1); + } + else if (wmId == ID_CONSOLEOPTIONS_RECONNECT) + { + ZPrintf("T2Client ---> Reconnecting the client and host.\n"); + TesterClientCoreDisconnect(T2Client.pClientCore); + TesterClientCoreConnect(T2Client.pClientCore, T2Client.strConnectParams); + } + else if (wmId == ID_CONSOLEOPTIONS_DISCONNECT) + { + ZPrintf("T2Client ---> Disconnecting the client from the host.\n"); + TesterClientCoreDisconnect(T2Client.pClientCore); + } + } + // not handled by this handler - call the original one + return(CallWindowProc( static_cast(T2Client.ConsoleEditProc), hWnd, msg, wParam, lParam)); +} + + +/*F********************************************************************************/ +/*! + \Function _T2ClientCommandConsole + + \Description + Message handler for command console box. + + \Input hDlg - dialog handle + \Input uMessage - message identifier + \Input wParam - message specifics (pointer to uint32_t) + \Input lParam - message specifics (pointer) + + \Output LRESULT - message handling status return code + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static LRESULT CALLBACK _T2ClientCommandConsole(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam) +{ + const char strTitlePrefix[] = "TesterClient:"; + char strCommand[TESTERCOMM_COMMANDSIZE_MAX]; + char strTitle[TESTERPROFILE_PROFILEFILENAME_SIZEDEFAULT + sizeof(strTitlePrefix) + 1]; + char *pCommand; + int32_t wmId, wmEvent; + HFONT font; + RECT CommandConsoleRect, ConsoleRect, CommandRect; + uint32_t uX, uY, uWidth, uHeight; + + // find out the control ID which requires attention + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (uMessage) + { + case WM_INITDIALOG: + { + char strHostname[128]; + uint16_t aJson[512]; + + // get the window geometry + GetWindowRect(hDlg, &CommandConsoleRect); + GetWindowRect(GetDlgItem(hDlg, IDC_EDIT_CONSOLE), &ConsoleRect); + GetWindowRect(GetDlgItem(hDlg, IDC_EDIT_COMMAND), &CommandRect); + T2Client.Geometry.uLeftBorder = ConsoleRect.left - CommandConsoleRect.left - 4; + T2Client.Geometry.uRightBorder = CommandConsoleRect.right - ConsoleRect.right - 4; + T2Client.Geometry.uBottomBorder = CommandConsoleRect.bottom - CommandRect.bottom; + T2Client.Geometry.uConsoleBottomToCommandTop = CommandRect.top - ConsoleRect.bottom; + T2Client.Geometry.uCommandHeight = CommandRect.bottom - CommandRect.top; + T2Client.Geometry.uTopBorder = ConsoleRect.top - 23; + + // set up custom handler for up arrow + #if defined(_WIN64) + SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam); + T2Client.CommandEditProc = reinterpret_cast(SetWindowLongPtr( GetDlgItem(hDlg, IDC_EDIT_COMMAND), GWLP_WNDPROC,(LPARAM)_T2ClientCommandEditProc )); + T2Client.ConsoleEditProc = reinterpret_cast(SetWindowLongPtr( GetDlgItem(hDlg, IDC_EDIT_CONSOLE), GWLP_WNDPROC,(LPARAM)_T2ClientConsoleEditProc )); + #else + SetWindowLong(hDlg, GWL_USERDATA, lParam); + T2Client.CommandEditProc = reinterpret_cast(SetWindowLong( GetDlgItem(hDlg, IDC_EDIT_COMMAND), GWL_WNDPROC,(LPARAM)_T2ClientCommandEditProc )); + T2Client.ConsoleEditProc = reinterpret_cast(SetWindowLong( GetDlgItem(hDlg, IDC_EDIT_CONSOLE), GWL_WNDPROC,(LPARAM)_T2ClientConsoleEditProc )); + #endif + + // set the font + font = CreateFont(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE, ""); + SendDlgItemMessage(hDlg, IDC_EDIT_CONSOLE, WM_SETFONT, (LPARAM)font, 0); + + // set the display function + TesterClientCoreDisplayFunc(T2Client.pClientCore, _T2ClientDisplayOutput, 0, GetDlgItem(hDlg, IDC_EDIT_CONSOLE)); + + SendMessage( GetDlgItem(hDlg, IDC_EDIT_CONSOLE), (UINT) EM_LIMITTEXT, T2Client.iScrollbackSize+8192, 0 ); + + // set the title bar + JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), T2Client.strConnectParams, -1); + JsonGetString(JsonFind(aJson, "HOSTNAME"), strHostname, sizeof(strHostname), ""); + if (strHostname[0] == '\0') + { + ds_strnzcpy(strHostname, "listening", sizeof(strHostname)); + } + ds_snzprintf(strTitle, sizeof(strTitle), "%s %s (%s)", strTitlePrefix, T2Client.strCurrentProfile, strHostname); + SetWindowText(hDlg, strTitle); + + // start the timer for IDLE callback handling + T2Client.iTimerID = SetTimer( GetParent(hDlg), 1, (uint32_t)(1000/60), _T2ClientTimerCallback); + } + + break; + case WM_SIZE: + + // get the new main window size + GetClientRect(hDlg, &CommandConsoleRect); + + // draw the console + uX = CommandConsoleRect.left + T2Client.Geometry.uLeftBorder; + uY = CommandConsoleRect.top + T2Client.Geometry.uTopBorder; + uWidth = (CommandConsoleRect.right - CommandConsoleRect.left) + - T2Client.Geometry.uLeftBorder - T2Client.Geometry.uRightBorder; + uHeight = (CommandConsoleRect.bottom - CommandConsoleRect.top) + - T2Client.Geometry.uBottomBorder - T2Client.Geometry.uTopBorder + - T2Client.Geometry.uCommandHeight - T2Client.Geometry.uConsoleBottomToCommandTop; + MoveWindow(GetDlgItem(hDlg, IDC_EDIT_CONSOLE), uX, uY, uWidth, uHeight, TRUE); + + // draw the command window - shares some stuff with the console + uY = CommandConsoleRect.bottom - T2Client.Geometry.uBottomBorder + - T2Client.Geometry.uCommandHeight; + uHeight = T2Client.Geometry.uCommandHeight; + MoveWindow(GetDlgItem(hDlg, IDC_EDIT_COMMAND), uX, uY, uWidth, uHeight, TRUE); + + break; + case WM_COMMAND: + // check for EXIT button or exit icon (upper right X) + if ((wmId == ID_BUTTON_EXIT) || (wmId == IDCANCEL)) + { + EndDialog(hDlg, wmId); + PostQuitMessage(0); + return(TRUE); + } + // check to see if we hit enter + else if (wmId == IDOK) + { + GetWindowText( GetDlgItem(hDlg, IDC_EDIT_COMMAND), &(strCommand[1]), sizeof(strCommand)-2); + + // specially process the ! command + if ((strCommand[1] == '!') || (strCommand[1] == '?')) + { + strCommand[0] = strCommand[1]; + strCommand[1] = ' '; + pCommand = &(strCommand[0]); + } + else + { + pCommand = &(strCommand[1]); + } + TesterClientCoreSendCommand(T2Client.pClientCore, pCommand); + SetWindowText( GetDlgItem(hDlg, IDC_EDIT_COMMAND), ""); + // reset the offset + T2Client.iCommandHistoryOffset = 1; + } + // end + break; + } + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _T2ClientTimerCallback + + \Description + Timer callback - used to activate the TesterCoreIdle function. + + \Input Not used. + + \Output None + + \Version 03/22/2005 (jfrank) +*/ +/********************************************************************************F*/ +VOID CALLBACK _T2ClientTimerCallback(HWND hDlg, UINT uMsg, UINT_PTR pIDEvent, DWORD dTime) +{ + if (T2Client.pClientCore) + { + if(strlen(T2Client.strScript) > 0) + { + char strCommand[256]; + sprintf(strCommand, "runscript %s", T2Client.strScript); + TesterClientCoreSendCommand(T2Client.pClientCore, strCommand); + T2Client.strScript[0] = '\0'; + } + + // pump the networking layer + TesterClientCoreIdle(T2Client.pClientCore); + } + return; +} + +/*F********************************************************************************/ +/*! + \Function _T2ClientProfilesLoad + + \Description + Load all the profile information into the profile dialog combo boxes. + + \Input hDlg - dialog handle + + \Output None + + \Version 03/22/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2ClientProfilesLoad(HWND hDlg) +{ + TesterProfileEntryT Entry; + int32_t iEntryNum = 0; + + // get all the entries + while(TesterClientCoreProfileGet(T2Client.pClientCore, iEntryNum++, &Entry) >= 0) + { + // set the dialog combo boxes + SendMessage( GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), + (UINT)CB_INSERTSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)(Entry.strProfileName)); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_SHARINGLOCATION), + (UINT)CB_INSERTSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)(Entry.strControlDirectory)); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_STARTUPPARAMS), + (UINT)CB_INSERTSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)(Entry.strCommandLine)); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_FILELOGDIRECTORY), + (UINT)CB_INSERTSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)(Entry.strLogDirectory)); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_HOSTNAME), + (UINT)CB_INSERTSTRING, (WPARAM)-1, (LPARAM)(LPCTSTR)(Entry.strHostname)); + } +} + +/*F********************************************************************************/ +/*! + \Function _T2ClientProfileDialogReset + + \Description + Reset the contents and selections in the profile dialog box + and reload all the profiles. + + \Input hDlg - dialog handle + + \Output None + + \Version 03/22/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2ClientProfileDialogReset(HWND hDlg) +{ + // clear all the lists + SendMessage( GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), (UINT)CB_RESETCONTENT, 0, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_SHARINGLOCATION), (UINT)CB_RESETCONTENT, 0, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_STARTUPPARAMS), (UINT)CB_RESETCONTENT, 0, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_FILELOGDIRECTORY), (UINT)CB_RESETCONTENT, 0, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_HOSTNAME), (UINT)CB_RESETCONTENT, 0, 0); + // clear the check boxes + SendMessage( GetDlgItem(hDlg, IDC_CHECKBOX_LOGGING), (UINT)BM_SETCHECK, BST_UNCHECKED, 0); + SendMessage( GetDlgItem(hDlg, IDC_CHECKBOX_STARTNETWORKING),(UINT)BM_SETCHECK, BST_UNCHECKED, 0); + // load all the profiles + _T2ClientProfilesLoad(hDlg); +} + +/*F********************************************************************************/ +/*! + \Function _T2ClientProfileSave + + \Description + Save the currently selected profile using the profile manager + + \Input hDlg - dialog handle + \Input *pEntry - [out,optional] storage for entry + + \Version 03/22/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2ClientProfileSave(HWND hDlg, TesterProfileEntryT *pEntry) +{ + TesterProfileEntryT Entry; + ds_memclr(&Entry, sizeof(Entry)); + + // create the profile entry parts + GetWindowText(GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), Entry.strProfileName, sizeof(Entry.strProfileName)-1); + GetWindowText(GetDlgItem(hDlg, IDC_COMBO_SHARINGLOCATION), Entry.strControlDirectory, sizeof(Entry.strControlDirectory)-1); + GetWindowText(GetDlgItem(hDlg, IDC_COMBO_STARTUPPARAMS), Entry.strCommandLine, sizeof(Entry.strCommandLine)-1); + GetWindowText(GetDlgItem(hDlg, IDC_COMBO_FILELOGDIRECTORY), Entry.strLogDirectory, sizeof(Entry.strLogDirectory)-1); + GetWindowText(GetDlgItem(hDlg, IDC_COMBO_HOSTNAME), Entry.strHostname, sizeof(Entry.strHostname)-1); + Entry.uLogEnable = (unsigned char)SendMessage( GetDlgItem(hDlg, IDC_CHECKBOX_LOGGING), (UINT)BM_GETCHECK, 0, 0); + Entry.uNetworkStartup = (unsigned char)SendMessage( GetDlgItem(hDlg, IDC_CHECKBOX_STARTNETWORKING), (UINT)BM_GETCHECK, 0, 0); + + // make sure we have a profile name + if (Entry.strProfileName[0] == '\0') + { + ds_snzprintf(Entry.strProfileName, sizeof(Entry.strProfileName), "profile-%d\n", NetRand(32*1024)); + } + + // add the profile + TesterClientCoreProfileAdd(T2Client.pClientCore, &Entry); + + // copy back to user + if (pEntry != NULL) + { + ds_memcpy(pEntry, &Entry, sizeof(*pEntry)); + } +} + + +/*F********************************************************************************/ +/*! + \Function _T2ClientSetActiveProfile + + \Description + Set the active profile in the profile list + + \Input hDlg - dialog handle + \Input iItemNum - item number in the list + + \Output None + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2ClientSetActiveProfile(HWND hDlg, int32_t iItemNum) +{ + TesterProfileEntryT Entry; + //char strProfile[TESTERPROFILE_PROFILEFILENAME_SIZEDEFAULT]; + //char strPlatform[TESTERPROFILE_PLATFORM_SIZEDEFAULT]; + //uint32_t uLogEnable, uNetworkStartup; + + // determine the numeric settings for the profile + iItemNum = TesterClientCoreProfileGet(T2Client.pClientCore, iItemNum, &Entry); + + // set the defaults + SendMessage( GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), (UINT)CB_SETCURSEL, iItemNum, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_SHARINGLOCATION), (UINT)CB_SETCURSEL, iItemNum, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_STARTUPPARAMS), (UINT)CB_SETCURSEL, iItemNum, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_FILELOGDIRECTORY), (UINT)CB_SETCURSEL, iItemNum, 0); + SendMessage( GetDlgItem(hDlg, IDC_COMBO_HOSTNAME), (UINT)CB_SETCURSEL, iItemNum, 0); + SendMessage( GetDlgItem(hDlg, IDC_CHECKBOX_LOGGING), (UINT)BM_SETCHECK, (Entry.uLogEnable ? BST_CHECKED : BST_UNCHECKED), 0); + SendMessage( GetDlgItem(hDlg, IDC_CHECKBOX_STARTNETWORKING),(UINT)BM_SETCHECK, (Entry.uNetworkStartup ? BST_CHECKED : BST_UNCHECKED), 0); +} + + + +/*F********************************************************************************/ +/*! + \Function _T2ClientProfileSelect + + \Description + Message handler for profile select dialog. + + \Input hDlg - dialog handle + \Input uMessage - message identifier + \Input wParam - message specifics (pointer to uint32_t) + \Input lParam - message specifics (pointer) + + \Output LRESULT - message handling status return code + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +static LRESULT CALLBACK _T2ClientProfileSelect(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam) +{ + TesterProfileEntryT Entry; + int32_t wmId, wmEvent; + int32_t iItemNum; + + // find out the control ID which requires attention + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + switch (uMessage) + { + case WM_INITDIALOG: + // wipe out all the lists and start over + _T2ClientProfileDialogReset(hDlg); + // activate the default profile + _T2ClientSetActiveProfile(hDlg, -1); + break; + case WM_COMMAND: + // check for EXIT button or exit icon (upper right X) + if ((wmId == IDCANCEL) || (wmId == ID_BUTTON_EXIT)) + { + EndDialog(hDlg, wmId); + PostQuitMessage(0); + return(TRUE); + } + // check for action on the profile name + else if (wmId == IDC_COMBO_PROFILENAME) + { + if (wmEvent == CBN_SELENDOK) + { + // we've selected something in the profile list + iItemNum = SendMessage( GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), (UINT)CB_GETCURSEL, 0, 0); + _T2ClientSetActiveProfile(hDlg, iItemNum); + } + } + // check for action on the sharing location + else if (wmId == IDC_COMBO_SHARINGLOCATION) + { + // nothing to do + } + // check for action on the sharing location + else if (wmId == IDC_COMBO_HOSTNAME) + { + // nothing to do + } + // check for saving the profile + else if (wmId == IDC_BUTTON_SAVEPROFILE) + { + GetWindowText( GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), Entry.strProfileName, sizeof(Entry.strProfileName)-1); + if (strlen(Entry.strProfileName) > 0) + { + // save the current profile manually + _T2ClientProfileSave(hDlg, NULL); + // reset the window state + _T2ClientProfileDialogReset(hDlg); + // now set the current one as default and display it + TesterClientCoreProfileDefault(T2Client.pClientCore, Entry.strProfileName); + _T2ClientSetActiveProfile(hDlg, -1); + } + } + // check for deleting the profile + else if (wmId == IDC_BUTTON_DELETEPROFILE) + { + ds_memclr((void *)Entry.strProfileName, sizeof(Entry.strProfileName)); + GetWindowText( GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), Entry.strProfileName, sizeof(Entry.strProfileName)-1); + // delete the current profile + TesterClientCoreProfileDelete(T2Client.pClientCore, Entry.strProfileName); + // reset the window state + _T2ClientProfileDialogReset(hDlg); + } + // connect? + else if (wmId == ID_BUTTON_CONNECT) + { + char buffer[64]; + + // save the current profile manually + _T2ClientProfileSave(hDlg, &Entry); + // set it as the default + TesterClientCoreProfileDefault(T2Client.pClientCore, Entry.strProfileName); + ds_strnzcpy(T2Client.strCurrentProfile, Entry.strProfileName, sizeof(T2Client.strCurrentProfile)); + // get profile index + iItemNum = SendMessage(GetDlgItem(hDlg, IDC_COMBO_PROFILENAME), (UINT)CB_GETCURSEL, 0, 0); + + // create the specified entry parameters + JsonInit(T2Client.strConnectParams, sizeof(T2Client.strConnectParams), 0); + JsonAddInt(T2Client.strConnectParams, "PROFILENUM", iItemNum); + JsonAddStr(T2Client.strConnectParams, "PROFILENAME", Entry.strProfileName); + + JsonAddStr(T2Client.strConnectParams, "INPUTFILE", TESTERCOMM_CLIENTINPUTFILE); + JsonAddStr(T2Client.strConnectParams, "OUTPUTFILE", TESTERCOMM_CLIENTOUTPUTFILE); + JsonAddStr(T2Client.strConnectParams, "CONTROLDIR", Entry.strControlDirectory); + JsonAddStr(T2Client.strConnectParams, "HOSTNAME", Entry.strHostname); + + // connect the client core + TesterClientCoreConnect(T2Client.pClientCore, JsonFinish(T2Client.strConnectParams)); + + // start the timer for IDLE callback handling + T2Client.iTimerID = SetTimer( GetParent(hDlg), 1, (uint32_t)(1000/60), _T2ClientTimerCallback); + + GetWindowText(GetDlgItem(hDlg, IDC_SCROLLBACKSIZE), buffer, sizeof(buffer)); + + T2Client.iScrollbackSize = atol(buffer)*1024; + if (T2Client.iScrollbackSize<512000) + { + T2Client.iScrollbackSize = 512000; + } + + // and kill this dialog + EndDialog(hDlg, wmId); + return(TRUE); + } + + // end + break; + } + return(FALSE); +} + + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function WinMain + + \Description + Main() routine. Starts GUI. + + \Input hInstance - window instance + \Input hPrevInstance - previous (calling) window instance + \Input lpCmdLine - command line parameters + \Input iCmdShow - show/hide the window + + \Output 0 for success + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, int32_t iCmdShow) +{ + ZPrintf(("\nStarting T2Client.\n\n")); + + ZMemtrackStartup(); + + // start up networking + NetConnStartup(""); + + // create and connect the core module + T2Client.pClientCore = TesterClientCoreCreate(lpCmdLine); + + if(lpCmdLine[0] == 0) + { + DialogBox(T2Client.hInst, (LPCTSTR)IDD_PROFILESELECT, NULL, (DLGPROC)_T2ClientProfileSelect); + } + else + { + char strPlatform[256]; + char strHost[256]; + char strLogFile[256]; + char strControlDir[256]; + + ds_strnzcpy(strPlatform, strtok(lpCmdLine, " "), sizeof(strPlatform)); + ds_strnzcpy(strHost, strtok(NULL, " "), sizeof(strHost)); + ds_strnzcpy(T2Client.strScript, strtok(NULL, " "), sizeof(T2Client.strScript)); + ds_strnzcpy(strControlDir, strtok(NULL, " "), sizeof(strControlDir)); + ds_strnzcpy(strLogFile, strtok(NULL, " "), sizeof(strLogFile)); + + ds_strnzcpy(T2Client.strCurrentProfile, "auto", sizeof(T2Client.strCurrentProfile)); + + JsonInit(T2Client.strConnectParams, sizeof(T2Client.strConnectParams), 0); + JsonAddStr(T2Client.strConnectParams, "INPUTFILE", TESTERCOMM_CLIENTINPUTFILE); + JsonAddStr(T2Client.strConnectParams, "OUTPUTFILE", TESTERCOMM_CLIENTOUTPUTFILE); + JsonAddStr(T2Client.strConnectParams, "LOGFILE", strLogFile); + JsonAddStr(T2Client.strConnectParams, "CONTROLDIR", strControlDir); + JsonAddStr(T2Client.strConnectParams, "HOSTNAME", strHost); + // connect the client core + TesterClientCoreConnect(T2Client.pClientCore, JsonFinish(T2Client.strConnectParams)); + } + DialogBox(T2Client.hInst, (LPCTSTR)IDD_COMMANDCONSOLE, NULL, (DLGPROC)_T2ClientCommandConsole); + + // disconnect and destroy the client core module + TesterClientCoreDestroy(T2Client.pClientCore); + + // shut down networking + NetConnShutdown(0); + + ZMemtrackShutdown(); + + ZPrintf(("Quitting T2Client.\n")); + + // return success + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/T2Host.cpp b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/T2Host.cpp new file mode 100644 index 00000000..bc8aabad --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/T2Host.cpp @@ -0,0 +1,452 @@ +/*H********************************************************************************/ +/*! + \File T2Host.cpp + + \Description + Main file for Tester2 Host Application. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/22/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#pragma warning(push,0) +#include +#pragma warning(pop) + +#include +#include +#include +#include + +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/util/jsonformat.h" + +#include "libsample/zmemtrack.h" +#include "libsample/zlib.h" + +#include "testerhostcore.h" +#include "testercomm.h" +#include "testerregistry.h" + +#include "t2hostresource.h" + +/*** Defines **********************************************************************/ + +#ifndef GWL_WNDPROC +#define GWL_WNDPROC GWLP_WNDPROC // pc64 +#endif + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +TesterHostCoreT *g_pHostCore; //!< global host core pointer +HWND g_pMainWin = NULL; //!< pointer to the main window +WNDPROC g_hEditProc; //!< edit box handler +int32_t g_iTimerID; //!< handle to global timer to activate callbacks + +/*** Private Functions ************************************************************/ + +#if defined(DIRTYCODE_DLL) +// pull in the dependencies to call DirtyMemFuncSet +extern "C" void* DirtyMemAlloc2(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void* pMemGroupUserData); +extern "C" void DirtyMemFree2(void* pMem, int32_t iMemModule, int32_t iMemGroup, void* pMemGroupUserData); +#endif + +/*F********************************************************************************/ +/*! + \Function _T2HostDisplayOutput + + \Description + Take input from TesterConsole and dump it to the specified edit box. + + \Input *pBuf - string containing the debug output to display + \Input iLen - length of buffer + \Input iRefcon - user-specified parameter (unused) + \Input *pRefptr - user-specified parameter (window pointer) + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2HostDisplayOutput(const char *pBuf, int32_t iLen, int32_t iRefcon, void *pRefptr) +{ + int32_t iAdd; + HWND Ctrl = (HWND)pRefptr; + char strTempText[16*1025], *pTempText, cPrev; + + // replace cr with crlf for proper display + for (pTempText = strTempText, cPrev = '\0'; (*pBuf != '\0') && ((pTempText-strTempText) < (sizeof(strTempText)-1)); pBuf++, pTempText++) + { + if ((*pBuf == '\n') && (cPrev != '\r')) + { + *pTempText++ = '\r'; + } + *pTempText = cPrev = *pBuf; + } + *pTempText = '\0'; + + // see if we need to delete old data + if (SendMessage(Ctrl, WM_GETTEXTLENGTH, 0, 0) > 24000) + { + SendMessage(Ctrl, EM_SETSEL, 0, 8192); + SendMessage(Ctrl, EM_REPLACESEL, FALSE, (LPARAM)""); + iAdd = SendMessage(Ctrl, WM_GETTEXTLENGTH, 0, 0); + SendMessage(Ctrl, EM_SETSEL, (WPARAM)(iAdd-1), iAdd); + SendMessage(Ctrl, EM_REPLACESEL, FALSE, (LPARAM)""); + } + + iAdd = SendMessage(Ctrl, WM_GETTEXTLENGTH, 0, 0); + SendMessage(Ctrl, EM_SETSEL, (WPARAM)iAdd, iAdd); + SendMessage(Ctrl, EM_REPLACESEL, FALSE, (LPARAM)strTempText); + SendMessage(Ctrl, EM_SCROLLCARET, 0, 0); +} + + +/*F********************************************************************************/ +/*! + \Function _T2HostTimerCallback + + \Description + Timer callback - used to activate the TesterCoreIdle function. + + \Input Not used. + + \Output None + + \Version 03/22/2005 (jfrank) +*/ +/********************************************************************************F*/ +VOID CALLBACK _T2HostTimerCallback(HWND hDlg, UINT uMsg, UINT_PTR pIDEvent, DWORD dTime) +{ + // pump the networking layer + TesterHostCoreIdle(g_pHostCore); +} + +/*F********************************************************************************/ +/*! + \Function _T2HostCommandEditProc + + \Description + Message handler for the command line edit box. Intercepts uparrow, etc. + + \Input hDlg - dialog handle + \Input uMessage - message identifier + \Input wParam - message specifics (pointer to uint32_t) + \Input lParam - message specifics (pointer) + + \Output LRESULT - message handling status return code + + \Version 04/06/2005 (jfrank) +*/ +/********************************************************************************F*/ +static LRESULT CALLBACK _T2HostCommandEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + const char *pCommandLine = NULL; + + if (msg == WM_KEYDOWN) + { + if (wParam == VK_UP) + { + pCommandLine = TesterHostCoreGetHistory(g_pHostCore, -1, NULL, 0); + } + else if (wParam == VK_DOWN) + { + pCommandLine = TesterHostCoreGetHistory(g_pHostCore, 1, NULL, 0); + } + + // if we got a key, redraw and quit + if (pCommandLine != NULL) + { + SetWindowText(hWnd, pCommandLine); + SendMessage(hWnd, EM_SETSEL, 0, -1); + SendMessage(hWnd, EM_SETSEL, (WPARAM)-1, 0); + return(0); + } + } + + // not handled by this handler - call the original one + return(CallWindowProc(static_cast(g_hEditProc), hWnd, msg, wParam, lParam)); +} + + +/*F********************************************************************************/ +/*! + \Function _T2HostDialogProc + + \Description + Main window dialog handler + + \Input Standard windows WNDPROC + + \Output Message special. + + \Version 03/22/2005 (jfrank) +*/ +/********************************************************************************F*/ +static LRESULT CALLBACK _T2HostDialogProc(HWND win, UINT msg, WPARAM wparm, LPARAM lparm) +{ + // handle close special (delete the class) + if (msg == WM_DESTROY) + { + return(FALSE); + } + + // handle init special (create the class) + if (msg == WM_INITDIALOG) + { + HFONT font = CreateFont(14, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE, ""); + SendDlgItemMessage(win, IDC_OUTPUT, WM_SETFONT, (WPARAM)font, 0); + + // set up command window handler + g_hEditProc = reinterpret_cast(SetWindowLongPtr(GetDlgItem(win, IDC_INPUT), GWL_WNDPROC, (LONG_PTR)_T2HostCommandEditProc)); + + // start the timer for IDLE callback handling + g_iTimerID = (int32_t)SetTimer( win, 1, (uint32_t)(1000/60), _T2HostTimerCallback); + } + + // close dialog + if (msg == WM_CLOSE) + { + TesterHostCoreDisconnect(g_pHostCore); + KillTimer(win, (UINT_PTR)g_iTimerID); + PostQuitMessage(0); + return(DefWindowProc(win, msg, wparm, lparm)); + } + + // look for return key + if ((msg == WM_COMMAND) && (LOWORD(wparm) == IDOK)) + { + if (g_pHostCore != NULL) + { + char strInput[16*1024]; + + // get the command line + GetWindowText(GetDlgItem(win, IDC_INPUT), strInput, sizeof(strInput)); + SetWindowText(GetDlgItem(win, IDC_INPUT), ""); + + // local echo + ZPrintf("\n> %s\n", strInput); + + // and execute the command + TesterHostCoreDispatch(g_pHostCore, strInput); + } + } + + // let windows handle + return(FALSE); +} + + +/*F********************************************************************************/ +/*! + \Function _T2HostCmdClear + + \Description + Clear the console + + \Input *argz - environment + \Input argc - num args + \Input **argv - arg list + + \Output int32_t - standard return code + + \Version 04/07/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _T2HostCmdClear(ZContext *argz, int32_t argc, char **argv) +{ + TesterConsoleT *pConsole; + + if (argc < 1) + { + ZPrintf(" clear the display.\n"); + ZPrintf(" usage: %s\n", argv[0]); + } + else + { + // clear the display + SetWindowText(GetDlgItem(g_pMainWin, IDC_OUTPUT), ""); + + // clear the console + if ((pConsole = (TesterConsoleT *)TesterRegistryGetPointer("CONSOLE")) != NULL) + { + TesterConsoleClear(pConsole); + } + } + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function _T2HostCmdExit + + \Description + Quit + + \Input *argz - environment + \Input argc - num args + \Input **argv - arg list + + \Output int32_t - standard return code + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _T2HostCmdExit(ZContext *argz, int32_t argc, char **argv) +{ + if (argc >= 1) + { + PostQuitMessage(0); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _T2HostRegisterModules + + \Description + Register client commands (local commands, like exit, history, etc.) + + \Input None + + \Output None + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2HostRegisterModules(void) +{ + TesterHostCoreRegister(g_pHostCore, "exit", &_T2HostCmdExit); + TesterHostCoreRegister(g_pHostCore, "clear", &_T2HostCmdClear); +} + + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function WinMain + + \Description + Main windows entry point. + + \Input Standard windows startup params + + \Output Process exit code + + \Version 03/22/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t APIENTRY WinMain(HINSTANCE inst, HINSTANCE prev, char *cmdline, int32_t show) +{ + char strBase[128] = "", strHostName[128] = "", *pBase, strParams[512]; + MSG msg; + + ZPrintf("\nStarting T2Host.\n\n"); + + // check for path argument (indicates file passing) + if ((pBase = strstr(cmdline, "-path=")) != NULL) + { + pBase += strlen("-path="); + ds_strnzcpy(strBase, pBase, sizeof(strBase)); + ZPrintf("t2host: base path=%s\n", strBase); + } + + // check for connect argument (indicates host connect instead of accept) + if ((pBase = strstr(cmdline, "-connect=")) != NULL) + { + pBase += strlen("-connect="); + ds_strnzcpy(strHostName, pBase, sizeof(strHostName)); + ZPrintf("t2host: connect=%s\n", strHostName); + } + +#if defined(DIRTYCODE_DLL) + + DirtyMemFuncSet(&DirtyMemAlloc2, &DirtyMemFree2); + +#endif + + ZMemtrackStartup(); + + // start the network + NetConnStartup("-servicename=tester2"); + + // create the module + JsonInit(strParams, sizeof(strParams), 0); + JsonAddStr(strParams, "INPUTFILE", TESTERCOMM_HOSTINPUTFILE); + JsonAddStr(strParams, "OUTPUTFILE", TESTERCOMM_HOSTOUTPUTFILE); + JsonAddStr(strParams, "CONTROLDIR", strBase); + JsonAddStr(strParams, "HOSTNAME", strHostName); + g_pHostCore = TesterHostCoreCreate(JsonFinish(strParams)); + + // create the tester dialog + g_pMainWin = CreateDialogParam(GetModuleHandle(NULL), "MAIN", HWND_DESKTOP, (DLGPROC)_T2HostDialogProc, 0); + + TesterHostCoreDisplayFunc(g_pHostCore, _T2HostDisplayOutput, 0, GetDlgItem(g_pMainWin, IDC_OUTPUT)); + + _T2HostRegisterModules(); + + // command-line command? + if (cmdline[0] != '\0') + { + // set the command + SetWindowText(GetDlgItem(g_pMainWin, IDC_INPUT), cmdline); + // fake a carriage return + _T2HostDialogProc(g_pMainWin, WM_COMMAND, IDOK, 0); + } + + // main message loop + while (GetMessage(&msg, NULL, 0, 0)) + { + // pump the host core module + TesterHostCoreUpdate(g_pHostCore, 1); + + // let dialog manager run + if (!IsDialogMessage(g_pMainWin, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + // give time to zlib + ZTask(); + ZCleanup(); + + // give time to network + NetConnIdle(); + } + + // kill all active processes + ZShutdown(); + + // kill the host core module + TesterHostCoreDestroy(g_pHostCore); + + // done with dialog + DestroyWindow(g_pMainWin); + + // shut down the network + NetConnShutdown(0); + + ZMemtrackShutdown(); + + _CrtDumpMemoryLeaks(); + + ZPrintf("\nQuitting T2Host.\n\n"); + + return(0); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2Client.rc b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2Client.rc new file mode 100644 index 00000000..5bab1793 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2Client.rc @@ -0,0 +1,173 @@ +// Microsoft Visual C++ generated resource script. +// +#include "T2ClientResource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PROFILESELECT DIALOGEX 0, 0, 282, 222 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "Tester2 Profile Selection" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Connect",ID_BUTTON_CONNECT,54,201,50,14 + PUSHBUTTON "Save Profile",IDC_BUTTON_SAVEPROFILE,220,20,55,20, + BS_MULTILINE + PUSHBUTTON "Delete Profile",IDC_BUTTON_DELETEPROFILE,220,45,55,20, + BS_MULTILINE + PUSHBUTTON "Exit",ID_BUTTON_EXIT,169,201,50,14 + COMBOBOX IDC_COMBO_PROFILENAME,85,20,125,130,CBS_DROPDOWN | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_HOSTNAME,85,40,125,130,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_SHARINGLOCATION,85,60,125,130,CBS_DROPDOWN | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_STARTUPPARAMS,85,80,125,130,CBS_DROPDOWN | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_COMBO_FILELOGDIRECTORY,85,120,125,130,CBS_DROPDOWN | + CBS_SORT | WS_DISABLED | WS_VSCROLL | WS_TABSTOP + LTEXT "Profile Name",IDC_LABEL_PROFILENAME,5,20,75,13, + SS_CENTERIMAGE,WS_EX_RIGHT + LTEXT "Hostname",IDC_HOSTNAME,5,40,75,13,SS_CENTERIMAGE, + WS_EX_RIGHT + LTEXT "Sharing Location",IDC_LABEL_SHARINGLOCATION,5,60,75,13, + SS_CENTERIMAGE,WS_EX_RIGHT + LTEXT "Extra Startup Params",IDC_LABEL_STARTUPPARAMS,5,80,75, + 13,SS_CENTERIMAGE,WS_EX_RIGHT + LTEXT "File Log Directory",IDC_LABEL_FILELOGDIRECTORY,17,120, + 63,13,SS_CENTERIMAGE | WS_DISABLED,WS_EX_RIGHT + CONTROL "Log to File",IDC_CHECKBOX_LOGGING,"Button", + BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,85,161,123,10 + CONTROL "Start Networking on Connect", + IDC_CHECKBOX_STARTNETWORKING,"Button",BS_AUTOCHECKBOX | + WS_DISABLED | WS_TABSTOP,85,180,123,10 + LTEXT "Scrollback size (KB)",IDC_LABEL_SCROLLBACKSIZE,7,100,73, + 13,SS_CENTERIMAGE,WS_EX_RIGHT + EDITTEXT IDC_SCROLLBACKSIZE,85,100,125,14,ES_AUTOHSCROLL +END + +IDD_COMMANDCONSOLE DIALOGEX 0, 0, 500, 217 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | + WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_WINDOWEDGE +CAPTION "Tester2 Client Command Console" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_EDIT_CONSOLE,1,7,497,191,ES_MULTILINE | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL | + NOT WS_TABSTOP + EDITTEXT IDC_EDIT_COMMAND,1,202,497,12,ES_AUTOHSCROLL +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "T2ClientResource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PROFILESELECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 215 + END + + IDD_COMMANDCONSOLE, DIALOG + BEGIN + LEFTMARGIN, 1 + RIGHTMARGIN, 498 + TOPMARGIN, 7 + BOTTOMMARGIN, 214 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONSOLECONTEXTMENU MENU +BEGIN + POPUP "Console Options" + BEGIN + MENUITEM "Clear", ID_CONSOLEOPTIONS_CLEAR + MENUITEM "Select All", ID_CONSOLEOPTIONS_SELECTALL + MENUITEM SEPARATOR + MENUITEM "Reconnect", ID_CONSOLEOPTIONS_RECONNECT + MENUITEM "Disconnect", ID_CONSOLEOPTIONS_DISCONNECT + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2ClientResource.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2ClientResource.h new file mode 100644 index 00000000..3ace7df3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2ClientResource.h @@ -0,0 +1,60 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by T2Client.rc +// +#define IDC_MYICON 2 +#define IDC_HOSTNAME 2 +#define IDD_T2CLIENT_DIALOG 102 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDC_T2CLIENT 109 +#define IDR_MAINFRAME 128 +#define IDD_PROFILESELECT 129 +#define IDD_COMMANDCONSOLE 130 +#define IDR_MENU1 131 +#define IDR_CONSOLECONTEXTMENU 131 +#define IDC_LABEL_PROFILENAME 1000 + +#define IDC_LABEL_STARTUPPARAMS 1002 +#define IDC_LABEL_SHARINGLOCATION 1003 +#define IDC_COMBO_PROFILENAME 1004 +#define IDC_BUTTON_SAVEPROFILE 1005 +#define IDC_BUTTON_DELETEPROFILE 1006 + +#define IDC_COMBO_STARTUPPARAMS 1008 +#define IDC_COMBO_SHARINGLOCATION 1009 +#define ID_BUTTON_CONNECT 1010 +#define ID_BUTTON_EXIT 1011 +#define IDC_CHECKBOX_STARTNETWORKING 1012 +#define IDC_COMBO_FILELOGDIRECTORY 1013 +#define IDC_COMBO_STARTUPPARAMS2 1014 +#define IDC_COMBO_HOSTNAME 1014 +#define IDC_CHECKBOX_LOGGING 1015 +#define IDC_LABEL_FILELOGDIRECTORY 1016 +#define IDC_BUTTON1 1017 +#define IDC_UPDATECLIENTCORE 1017 +#define IDC_EDIT_CONSOLE 1018 +#define IDC_LABEL_STARTUPPARAMS2 1018 +#define IDC_LABEL_SCROLLBACKSIZE 1018 +#define IDC_EDIT2 1019 +#define IDC_EDIT_COMMAND 1019 +#define IDC_SCROLLBACKSIZE 1019 +#define IDM_PROFILE_SELECT 32773 +#define ID_CONSOLEOPTIONS_CLEAR 32775 +#define ID_CONSOLEOPTIONS_SELECTALL 32776 +#define ID_CONSOLEOPTIONS_RECONNECT 32777 +#define ID_CONSOLEOPTIONS_DISCONNECT 32778 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 132 +#define _APS_NEXT_COMMAND_VALUE 32780 +#define _APS_NEXT_CONTROL_VALUE 1021 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2Host.rc b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2Host.rc new file mode 100644 index 00000000..94e374e3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2Host.rc @@ -0,0 +1,184 @@ +// Microsoft Visual C++ generated resource script. +// +#include "T2HostResource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +MAIN DIALOGEX 0, 0, 500, 226 +STYLE DS_SETFONT | DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "Tester2 PC Host" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + EDITTEXT IDC_INPUT,7,205,486,14,ES_AUTOHSCROLL + EDITTEXT IDC_OUTPUT,7,7,486,195,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + +MODEMSELECT DIALOGEX 0, 0, 194, 85 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Select Modem" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,7,60,50,14 + PUSHBUTTON "Cancel",IDCANCEL,134,60,50,14 + COMBOBOX IDC_MODEMS,43,15,131,83,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Modem:",IDC_STATIC,16,18,26,8 + EDITTEXT IDC_NUMBER,43,35,130,14,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Number:",IDC_STATIC,15,38,28,8 +END + +VOICEDEVSELECT DIALOGEX 0, 0, 194, 85 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Select Voice Device" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,71,60,50,14 + COMBOBOX IDC_VOICEINP,43,15,131,83,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Input:",IDC_STATIC,18,18,19,8 + COMBOBOX IDC_VOICEOUT,43,35,131,83,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + LTEXT "Output:",IDC_STATIC,17,38,24,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + "MAIN", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 493 + TOPMARGIN, 7 + BOTTOMMARGIN, 219 + END + + "MODEMSELECT", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 184 + TOPMARGIN, 7 + BOTTOMMARGIN, 74 + END + + "VOICEDEVSELECT", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 184 + TOPMARGIN, 7 + BOTTOMMARGIN, 74 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "T2HostResource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Electronic Arts" + VALUE "FileDescription", "Tester2 Host" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "T2Host" + VALUE "LegalCopyright", "Copyright 2005" + VALUE "OriginalFilename", "T2Host.exe" + VALUE "ProductName", " T2Host" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2HostResource.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2HostResource.h new file mode 100644 index 00000000..18deceec --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/resource/T2HostResource.h @@ -0,0 +1,22 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by T2Host.rc +// +#define IDD_DIALOG1 101 +#define IDC_INPUT 1005 +#define IDC_OUTPUT 1006 +#define IDC_NUMBER 1007 +#define IDC_VOICEINP 1008 +#define IDC_VOICEOUT 1010 +#define IDC_MODEMS 1141 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 107 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1009 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/testermoduleshost.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/testermoduleshost.c new file mode 100644 index 00000000..f66f961f --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/pc/testermoduleshost.c @@ -0,0 +1,51 @@ +/*H********************************************************************************/ +/*! + \File testermodulespchost.c + + \Description + PC specific module startup. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function TesterModulesRegisterPlatformCommands + + \Description + Register all PC-specific modules + + \Input *pState - module state + + \Output 0=success, error code otherwise + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterModulesRegisterPlatformCommands(TesterModulesT *pState) +{ + // tester2 pc-specific modules + TesterModulesRegister(pState, "secure",&CmdSecure); + TesterModulesRegister(pState, "voice", &CmdVoice); + return(TESTERMODULES_ERROR_NONE); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/render.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/render.h new file mode 100644 index 00000000..0e072e02 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/render.h @@ -0,0 +1,53 @@ +/*H********************************************************************************/ +/*! + \File render.h + + \Description + Sample app rendering + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 06/04/2013 (cvienneau) First Version +*/ +/********************************************************************************H*/ +#ifndef _T2Render_h +#define _T2Render_h + +/*** Include files ****************************************************************/ +#include "DirtySDK/dirtysock.h" +#include + +/*** Defines **********************************************************************/ +#if defined(DIRTYCODE_PS4) + + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ +typedef struct T2Render +{ + int32_t iVideoOut; + int32_t ibufferIndex; + SceKernelEqueue eqFlip; + int64_t flipArg; + int32_t loop; +} T2Render; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +int32_t T2RenderInit(T2Render *pApp); +int32_t T2RenderUpdate(T2Render *pApp); +int32_t T2RenderTerm(T2Render *pApp); + +#ifdef __cplusplus +}; +#endif + +#endif // #if defined(DIRTYCODE_PS4) +#endif //_T2Render_h \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerclientcore.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerclientcore.c new file mode 100644 index 00000000..40e54f52 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerclientcore.c @@ -0,0 +1,663 @@ +/*H********************************************************************************/ +/*! + \File testerclientcore.c + + \Description + Main control module for the Tester2 Client application. + + \Notes + This is the main host module for the tester2 client application. + It contains mostly global-variable type objects and operations, similar + to LobbyAPI. TesterClientCore is responsible for starting up all the + necessary child modules like TesterConsole, TesterComm, etc. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/17/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/util/jsonparse.h" + +#include "libsample/zmem.h" +#include "libsample/zfile.h" + +#include "testerprofile.h" +#include "testercomm.h" +#include "testerconsole.h" +#include "testerhistory.h" +#include "testermodules.h" +#include "testerregistry.h" +#include "testerclientcore.h" + +/*** Defines **********************************************************************/ + +#define TESTERCLIENTCORE_ROOTPATH_DEFAULT ".\\T2Client" +#define TESTERCLIENTCORE_PROFILEFILE_DEFAULT "profiles.txt" +#define TESTERCLIENTCORE_CONSOLESIZE_DEFAULT 16384 + +/*** Type Definitions *************************************************************/ + +struct TesterClientCoreT +{ + TesterProfileT *pProfile; //!< profile manager module + TesterCommT *pComm; //!< host/client communication module + TesterConsoleT *pConsole; //!< console for managing output + TesterHistoryT *pHistory; //!< command history module + TesterModulesT *pModules; //!< client side command modules + TesterProfileEntryT CmdLineProfile; //!< the command line as passed + int iLogFile; //!< Log File identifier + TesterConsoleDisplayCbT *pDisplayProc; //!< procedure for displaying output +}; + +/*** Variables ********************************************************************/ + +/*** External Functions ***********************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _TesterClientCoreMsgControl + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - user supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterClientCoreMsgControl(TesterCommT *pState, const char *pMsg, void *pParam) +{ + TesterClientCoreT *pCore = (TesterClientCoreT *)pParam; + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONTROL, pMsg); +} + +/*F********************************************************************************/ +/*! + \Function _TesterClientCoreMsgCommand + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - user supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterClientCoreMsgCommand(TesterCommT *pState, const char *pMsg, void *pParam) +{ + TesterClientCoreT *pCore = (TesterClientCoreT *)pParam; + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_COMMAND, "> "); + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_COMMAND, pMsg); + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_COMMAND, "\n"); +} + +/*F********************************************************************************/ +/*! + \Function _TesterClientCoreMsgStatus + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - user supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterClientCoreMsgStatus(TesterCommT *pState, const char *pMsg, void *pParam) +{ + //TesterClientCoreT *pCore = (TesterClientCoreT *)pParam; + //TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_STATUS, pMsg); +} + +/*F********************************************************************************/ +/*! + \Function _TesterClientCoreMsgConsole + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - user supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterClientCoreMsgConsole(TesterCommT *pState, const char *pMsg, void *pParam) +{ + TesterClientCoreT *pCore = (TesterClientCoreT *)pParam; + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONSOLE, pMsg); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterClientCorePrintf + + \Description + Tester Printf function. + + \Input *pParm - Module state + \Input *pText - Message text + + \Output 0=success + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +#if DIRTYCODE_LOGGING +static int32_t _TesterClientCorePrintf(void *pParm, const char *pText) +{ + TesterClientCoreT *pCore = pParm; + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONSOLE, pText); + return(0); +} +#endif + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreCreate + + \Description + Create a TesterClientCore module and return the pointer to it + + \Input None + + \Output TesterClientCoreT * - newly allocated and created TesterClientCoreT + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +TesterClientCoreT *TesterClientCoreCreate(char *strCmdLine) +{ + TesterClientCoreT *pState; + + // create and wipe clean the module + pState = (TesterClientCoreT *)ZMemAlloc(sizeof(TesterClientCoreT)); + ds_memclr((void *)pState, sizeof(TesterClientCoreT)); + + // start up the registry + TesterRegistryCreate(-1); + TesterRegistrySetPointer("CORE", pState); + TesterRegistrySetString("ROOT", TESTERCLIENTCORE_ROOTPATH_DEFAULT); + + // now create the children modules + pState->pProfile = TesterProfileCreate(); + pState->pComm = TesterCommCreate(); + pState->pConsole = TesterConsoleCreate(TESTERCLIENTCORE_CONSOLESIZE_DEFAULT, TRUE); + pState->pModules = TesterModulesCreate(); + pState->pHistory = TesterHistoryCreate(-1); + +#if DIRTYCODE_LOGGING + // hook in the netprintf to the console + NetPrintfHook(_TesterClientCorePrintf, pState); +#endif + + // start up profile earlier so we can get data + TesterProfileConnect(pState->pProfile, TESTERCLIENTCORE_ROOTPATH_DEFAULT "\\" TESTERCLIENTCORE_PROFILEFILE_DEFAULT); + + // register client commands + TesterModulesRegisterClientCommands(pState->pModules); + + // done + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreConnect + + \Description + Connect the core module and all its children + + \Input *pState - TesterClientCoreT module to connect + \Input *pParams - startup parameters + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreConnect(TesterClientCoreT *pState, const char *pParams) +{ + TesterProfileEntryT CurProfile; + char strHostname[64], strProfilename[64], strMessage[128], strControlDir[2]; + uint16_t aJson[512]; + + ds_memclr(&CurProfile, sizeof(TesterProfileEntryT)); + + // check the state + if (pState == NULL) + { + return(TESTERCLIENTCORE_ERROR_NULLPOINTER); + } + // check the children + if ((pState->pComm == NULL) || (pState->pProfile == NULL)) + { + return(TESTERCLIENTCORE_ERROR_NULLPOINTER); + } + + JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pParams, -1); + + // attach an interface method + JsonGetString(JsonFind(aJson, "CONTROLDIR"), strControlDir, sizeof(strControlDir), ""); + if (strControlDir[0] != '\0') + { + TesterCommAttachFile(pState->pComm); + } + else + { + TesterCommAttachSocket(pState->pComm); + } + + // connect the children modules + TesterCommConnect(pState->pComm, pParams, FALSE); + + // register callbacks for all the types of messages we'll see + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_CONTROL, _TesterClientCoreMsgControl, (void *)pState); + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_COMMAND, _TesterClientCoreMsgCommand, (void *)pState); + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_STATUS, _TesterClientCoreMsgStatus , (void *)pState); + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_CONSOLE, _TesterClientCoreMsgConsole, (void *)pState); + + // send connected message to the host + JsonGetString(JsonFind(aJson, "HOSTNAME"), strHostname, sizeof(strHostname), ""); + JsonGetString(JsonFind(aJson, "PROFILENAME"), strProfilename, sizeof(strProfilename), ""); + ds_snzprintf(strMessage, sizeof(strMessage), "t2client: client '%s' connected to host %s\n", strProfilename, strHostname); + TesterCommMessage(pState->pComm, TESTER_MSGTYPE_CONSOLE, strMessage); + + // load historical commands + TesterProfileGet(pState->pProfile, JsonGetInteger(JsonFind(aJson, "PROFILENUM"), -2), &CurProfile); + TesterHistoryLoad(pState->pHistory, CurProfile.strHistoryFile); + + // now just wait for something + return(TESTERCLIENTCORE_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreIdle + + \Description + Idle function - pump this to make networking, etc. happy. + + \Input *pState - TesterClientCoreT module to service + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterClientCoreIdle(TesterClientCoreT *pState) +{ + if(pState == NULL) + return; + + if(pState->pComm) + { + // pump the comm update function + TesterCommUpdate(pState->pComm); + } + + // now flush any debug output + if((pState->pDisplayProc) && (pState->pConsole)) + { + TesterConsoleFlush(pState->pConsole, pState->pDisplayProc); + } +} + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreSendCommand + + \Description + Send a command to the host - should comes from the client GUI command line. + + \Input *pState - TesterClientCoreT module + \Input *pData - command to send + + \Output 0=success, error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreSendCommand(TesterClientCoreT *pState, const char *pData) +{ + int32_t iResult; + + // echo it locally + _TesterClientCoreMsgCommand(pState->pComm, pData, pState); + + // add it to the history + TesterHistoryAdd(pState->pHistory, pData); + + // check to see if its a local command + iResult = TesterModulesDispatch(pState->pModules, pData); + + // did it work locally? + if(iResult == 0) + { + return(TESTERCLIENTCORE_ERROR_NONE); + } + // not a local command - send it to the host to deal with + else + { + return(TesterCommMessage(pState->pComm, TESTER_MSGTYPE_COMMAND, pData)); + } +} + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreProfileGet + + \Description + Return a tagfield with a specified profile's data + + \Input *pState - TesterClientCoreT module state + \Input iIndex - 0-based index of the profile to get, -1 for default entry + \Input *pDest - destination entry for the profile + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreProfileGet(TesterClientCoreT *pState, int32_t iIndex, TesterProfileEntryT *pDest) +{ + // check for error conditions + if ((pState == NULL) || (pDest == NULL)) + return(TESTERCLIENTCORE_ERROR_NULLPOINTER); + + // get the specified profile + return(TesterProfileGet(pState->pProfile, iIndex, pDest)); +} + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreRegister + + \Description + Register a command + + \Input *pState - TesterClientCoreT module state + \Input *pCommand - command name to register with + \Input *pFunctionPtr - the ZCommand module to call + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreRegister(TesterClientCoreT *pState, const char *pCommand, ZCommand *pFunctionPtr) +{ + if(pState == NULL) + return(TESTERCLIENTCORE_ERROR_NULLPOINTER); + + return(TesterModulesRegister(pState->pModules, pCommand, pFunctionPtr)); +} + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreProfileAdd + + \Description + Add a profile to the list + + \Input *pState - module state + \Input *pEntry - entry to add + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreProfileAdd(TesterClientCoreT *pState, TesterProfileEntryT *pEntry) +{ + int32_t iResult; + + // add the profile + iResult = TesterProfileAdd(pState->pProfile, pEntry); + if(iResult != 0) + return(iResult); + + // trigger a save automatically + iResult = TesterProfileSave(pState->pProfile); + return(iResult); +} + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreProfileDelete + + \Description + Remove a profile from the list + + \Input *pState - module state + \Input *pName - profile to nuke + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreProfileDelete(TesterClientCoreT *pState, const char *pName) +{ + return(TesterProfileDelete(pState->pProfile, pName)); +} + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreProfileDefault + + \Description + Remove a profile from the list + + \Input *pState - module state + \Input *pName - profile to set as default + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreProfileDefault(TesterClientCoreT *pState, const char *pName) +{ + return(TesterProfileSetDefaultName(pState->pProfile, pName)); +} + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreDisplayFunc + + \Description + Register a display function to show stuff on the screen. + + \Input *pState - TesterClientCoreT module state + \Input *pProc - function pointer to display procedure + \Input iRefcon - int32_t value to pass to the display function when called + \Input *pDisplayRef - ref value to pass to the display function when called (hDlg) + + \Output None + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterClientCoreDisplayFunc(TesterClientCoreT *pState, TesterConsoleDisplayCbT *pProc, int32_t iRefcon, void *pRefptr) +{ + pState->pDisplayProc = pProc; + TesterConsoleConnect(pState->pConsole, iRefcon, pRefptr); +} + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreGetHistoricalCommand + + \Description + Get a historical command from the history module. + + \Input *pState - TesterClientCoreT module state + \Input *pOffsetFromCurrent - pointer to the offset, may be clamped if necessary + \Input *pBuf - destination buffer (may be NULL) + \Input iSize - size of destination buffer + + \Output + const char * - history text, or null + + \Version 04/06/2005 (jfrank) +*/ +/********************************************************************************F*/ +const char *TesterClientCoreGetHistoricalCommand(TesterClientCoreT *pState, int32_t *pOffsetFromCurrent, char *pBuf, int32_t iSize) +{ + int32_t iHeadCount, iTailCount; + int32_t iOffsetFromCurrent; + const char *pText = NULL; + + // check for errors + if (pState == NULL) + { + return(NULL); + } + + // get the current head and tail + TesterHistoryHeadTailCount(pState->pHistory, &iHeadCount, &iTailCount); + + // clamp the incoming value + iOffsetFromCurrent = *pOffsetFromCurrent; + if (iOffsetFromCurrent > 1) + { + iOffsetFromCurrent = 1; + } + if ((iHeadCount - iTailCount) > iOffsetFromCurrent) + { + iOffsetFromCurrent = (iHeadCount - iTailCount); + } + *pOffsetFromCurrent = iOffsetFromCurrent; + + // get the command + if ((iOffsetFromCurrent == 1) && (pBuf != NULL)) + { + // set a blank entry + ds_memclr(pBuf, iSize); + } + else + { + pText = TesterHistoryGet(pState->pHistory, (iTailCount + iOffsetFromCurrent), pBuf, iSize); + } + return(pText); +} + + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreDisconnect + + \Description + Disconnect the core module and all its children + + \Input *pState - TesterClientCoreT module to disconnect + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterClientCoreDisconnect(TesterClientCoreT *pState) +{ + // check the state + if (pState == NULL) + return(TESTERCLIENTCORE_ERROR_NULLPOINTER); + // check the children + if ((pState->pComm == NULL) || (pState->pProfile == NULL)) + return(TESTERCLIENTCORE_ERROR_NULLPOINTER); + + // disconnect the children modules + TesterCommDisconnect(pState->pComm); + + return(TESTERCLIENTCORE_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function TesterClientCoreDestroy + + \Description + Destroy a TesterClientCoreT module and all its children + + \Input *pState - TesterClientCoreT module to destroy + + \Output None + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterClientCoreDestroy(TesterClientCoreT *pState) +{ + TesterProfileEntryT CurProfile; + + // check the state + if (pState == NULL) + return; + + TesterProfileGet(pState->pProfile, -1, &CurProfile); + TesterHistorySave(pState->pHistory, CurProfile.strHistoryFile); + + // shut down profile late so we can save settings if need be + TesterProfileDisconnect(pState->pProfile); + + // destoy all the children modules + TesterProfileDestroy(pState->pProfile); + TesterCommDestroy(pState->pComm); + TesterConsoleDestroy(pState->pConsole); + TesterModulesDestroy(pState->pModules); + TesterHistoryDestroy(pState->pHistory); + TesterRegistryDestroy(); + +#if DIRTYCODE_LOGGING + // unhook debug output + NetPrintfHook(NULL, NULL); +#endif + + // wipe the pointers clean + pState->pComm = NULL; + pState->pProfile = NULL; + pState->pConsole = NULL; + pState->pHistory = NULL; + + // now destroy this module + ZMemFree((void *)pState); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerclientcore.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerclientcore.h new file mode 100644 index 00000000..00376c2d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerclientcore.h @@ -0,0 +1,100 @@ +/*H********************************************************************************/ +/*! + \File testerclientcore.h + + \Description + Main control module for the Tester2 Client application. + + \Notes + This is the main host module for the tester2 client application. + It contains mostly global-variable type objects and operations, similar + to LobbyAPI. TesterClientCore is responsible for starting up all the + necessary child modules like TesterConsole, TesterHostClientComm, etc. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/17/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ +#ifndef _testerclientcore_h +#define _testerclientcore_h + +/*** Include files ****************************************************************/ + +#include "testerconsole.h" +#include "testerprofile.h" + +/*** Defines **********************************************************************/ + +#define TESTERCLIENTCORE_ERROR_NONE (0) //!< no error (success) +#define TESTERCLIENTCORE_ERROR_NULLPOINTER (-1) //!< a null pointer ref was used + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// module state object +typedef struct TesterClientCoreT TesterClientCoreT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// -------------------- FRAMEWORK -------------------------------- + +// create a testerclientcore module +TesterClientCoreT *TesterClientCoreCreate(char* strCmdLine); + +// connect the core module +int32_t TesterClientCoreConnect(TesterClientCoreT *pState, const char *pParams); + +// register function to flush output to the display +void TesterClientCoreDisplayFunc(TesterClientCoreT *pState, + TesterConsoleDisplayCbT *pProc, + int32_t iRefcon, void *pRefptr); + +// idle function - pump this to make networking, etc. happy +void TesterClientCoreIdle(TesterClientCoreT *pState); + +// register a module +int32_t TesterClientCoreRegister(TesterClientCoreT *pState, const char *pCommand, ZCommand *pFunctionPtr); + +// disconnect the core module +int32_t TesterClientCoreDisconnect(TesterClientCoreT *pState); + +// destroy a testerclientcore module +void TesterClientCoreDestroy(TesterClientCoreT *pState); + +// get a historical command +const char *TesterClientCoreGetHistoricalCommand(TesterClientCoreT *pState, int32_t *pOffsetFromCurrent, char *pBuf, int32_t iSize); + +// -------------------- COMMUNICATION FUNCTIONS ------------------ + +// send a command (from GUI command line) +int32_t TesterClientCoreSendCommand(TesterClientCoreT *pState, const char *pData); + +// -------------------- PROFILE FUNCTIONS ------------------------ + +// return a tagfield with a specified profile's data +int32_t TesterClientCoreProfileGet(TesterClientCoreT *pState, int32_t iIndex, TesterProfileEntryT *pDest); + +// add a profile +int32_t TesterClientCoreProfileAdd(TesterClientCoreT *pState, TesterProfileEntryT *pEntry); + +// delete a profile +int32_t TesterClientCoreProfileDelete(TesterClientCoreT *pState, const char *pName); + +// set a profile as the default +int32_t TesterClientCoreProfileDefault(TesterClientCoreT *pState, const char *pName); + +#ifdef __cplusplus +}; +#endif + +#endif // _testerclientcore_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testercomm.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testercomm.h new file mode 100644 index 00000000..bf8b578b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testercomm.h @@ -0,0 +1,152 @@ +/*H********************************************************************************/ +/*! + \File testercomm.h + + \Description + This module provides a communication layer between the host and the client. + Typical operations are SendLine() and GetLine(), which send and receive + lines of text, commands, debug output, etc. Each platform will implement + its own way of communicating through files, debugger API calls, etc. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/23/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _testercomm_h +#define _testercomm_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock.h" +#include "libsample/zlist.h" + +/*** Defines **********************************************************************/ + +#define TESTER_MSGTYPE_NONE (0) //!< no message type +#define TESTER_MSGTYPE_CONTROL (1) //!< control message - for communication between host/client +#define TESTER_MSGTYPE_COMMAND (2) //!< command message - execute host command +#define TESTER_MSGTYPE_STATUS (3) //!< status message - for status updates between host/client +#define TESTER_MSGTYPE_CONSOLE (4) //!< console output - for display on-screen +#define TESTER_MSGTYPE_MAX (8) //!< max number of message types + +#define TESTERCOMM_NUMCOMMANDS_MAX (512) //!< number of possible input/output commands outstanding +#define TESTERCOMM_COMMANDSIZE_MAX (256*1024) //!< max size of each command line +#define TESTERCOMM_ARGNUM_MAX (16) //!< max number of arguments for each command line + +//! FILE-SPECIFIC defines +#define TESTERCOMM_CLIENTINPUTFILE ("clientinput.txt") +#define TESTERCOMM_CLIENTOUTPUTFILE ("clientoutput.txt") +#define TESTERCOMM_HOSTINPUTFILE (TESTERCOMM_CLIENTOUTPUTFILE) +#define TESTERCOMM_HOSTOUTPUTFILE (TESTERCOMM_CLIENTINPUTFILE) + +//! MSDM-SPECIFIC defines + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// the comm module itself +typedef struct TesterCommT TesterCommT; + +// message type callback +// Callback prototype used for status updates, if enabled +typedef void (TesterCommMsgCbT)(TesterCommT *pState, const char *pMsg, void *pParam); + +// data container for messages to send between host and client +typedef struct TesterCommDataT +{ + int32_t iType; //!< message type + char strBuffer[TESTERCOMM_COMMANDSIZE_MAX]; //!< command data pointer +} TesterCommDataT; + +// interface pointers - filled in by a particular interface adapter function +typedef struct TesterCommInterfaceT +{ + //! connection function + int32_t (*CommConnectFunc) (TesterCommT *pState, const char *pParams, uint32_t bIsHost); + //! update function + int32_t (*CommUpdateFunc) (TesterCommT *pState); + //! disconnection function + int32_t (*CommDisconnectFunc)(TesterCommT *pState); + void *pData; //!< interface-specific data +} TesterCommInterfaceT; + +// module state +struct TesterCommT +{ + // function calls and interface specific stuff + TesterCommInterfaceT *pInterface; //!< interface-specific functions + + // communication stuff + uint32_t uLastConnectTime; //!< the last time a connect was attempted + uint32_t uLastSendTime; //!< last time an output message was sent + TesterCommMsgCbT *MessageMap[TESTER_MSGTYPE_MAX]; //!< message map array + void *pMessageMapUserData[TESTER_MSGTYPE_MAX]; //!< user-supplied data to call back with + uint8_t uSuspended; //!< 1=suspended, 0=awake + uint8_t bGotInput; //!< TRUE=got input from the other side, else FALSE + uint8_t uPad[2]; //!< pad data + + char strCommand[TESTERCOMM_COMMANDSIZE_MAX]; //!< temporary command processing buffer + char strResponse[TESTERCOMM_COMMANDSIZE_MAX]; //!< temporary response processing buffer (XENON only) + TesterCommDataT LineData; //!< temporary line data buffer + TesterCommDataT Message; //!< temporary message buffer + + // data lists + ZListT *pInputData; //!< input commands + ZListT *pOutputData; //!< output commands +}; + + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create a tester host client communication module +TesterCommT *TesterCommCreate(void); + +// connect the host client communication module +int32_t TesterCommConnect(TesterCommT *pState, const char *pParams, uint32_t bIsHost); + +// give the host/client interface some processing time (call this once in a while) +int32_t TesterCommUpdate(TesterCommT *pState); + +// send a message to the other side +int32_t TesterCommMessage(TesterCommT *pState, int32_t iMsgType, const char *pMsgText); + +// register a callback with a particular message type +int32_t TesterCommRegister(TesterCommT *pState, int32_t iMsgType, TesterCommMsgCbT *pCallback, void *pParam); + +// get module status +int32_t TesterCommStatus(TesterCommT *pState, int32_t iSelect, int32_t iValue); + +// suspend the comm module, active until a wake call is issued +void TesterCommSuspend(TesterCommT *pState); + +// wake the comm module from a suspend +void TesterCommWake(TesterCommT *pState); + +// disconnect the host client communication module +int32_t TesterCommDisconnect(TesterCommT *pState); + +// destroy a tester host client communication module +void TesterCommDestroy(TesterCommT *pState); + +// testercomm_file method to attach to host +void TesterCommAttachFile(TesterCommT *pState); + +// testercomm_socket method to attach to host +void TesterCommAttachSocket(TesterCommT *pState); + +#ifdef __cplusplus +}; +#endif + +#endif // _testercomm_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerconsole.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerconsole.c new file mode 100644 index 00000000..001b0fa8 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerconsole.c @@ -0,0 +1,258 @@ +/*H********************************************************************************/ +/*! + \File testerconsole.c + + \Description + This module buffers console output for the tester application. + In essence, it is just a large FIFO which knows how to handle + newline characters as expected. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 09/15/1999 (gschaefer) First Version + \Version 11/08/1999 (gschaefer) Cleanup and revision + \Version 03/29/2005 (jfrank) Update for Tester2 +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include "DirtySDK/dirtysock.h" +#include "libsample/zmem.h" +#include "testercomm.h" +#include "testerregistry.h" +#include "testerconsole.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +struct TesterConsoleT //!< console state structure +{ + char *pBuf; //!< pointer to console buffer + int32_t iLen; //!< length of console buffer + int32_t iInp; //!< fifo input offset + int32_t iOut; //!< fifo output offset + int32_t iWrap; //!< flag to indicate overflow handling + int32_t iRefcon; //!< reference constant for callback + void *pRefptr; //!< reference pointer for callback +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterConsoleOutput + + \Description + Add text to console buffer. + + \Input *pRef - console reference + \Input *pText - text to add to buffer (\n newlines are fine) + + \Output None + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +static void _TesterConsoleAddText(TesterConsoleT *pRef, const char *pText) +{ + char ch; + + // insert complete string + for ( ; (ch = *pText++) != 0; ) + { + // save the data and advance index + pRef->pBuf[pRef->iInp] = ch; + pRef->iInp = (pRef->iInp+1) % pRef->iLen; + + // check for overflow + if (pRef->iInp == pRef->iOut) + { + if (!pRef->iWrap) + { + // restore input index to old value + pRef->iInp = (pRef->iInp+pRef->iLen-1) % pRef->iLen; + break; + } + // delete oldest character + pRef->iOut = (pRef->iOut+1) % pRef->iLen; + } + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterConsoleCreate + + \Description + Create instance of a console buffer. + + \Input iSize - buffer size (recommended 4K min) + \Input iWrap - flag to indicate overflow action (true=wrap over oldest) + \Input iRefcon - constant passed back during flush callback + \Input pRefptr - pointer passed back during flush callback + + \Output TesterConsoleT * - new console pointer + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +TesterConsoleT *TesterConsoleCreate(int32_t iSize, int32_t iWrap) +{ + TesterConsoleT *pRef; + + pRef = ZMemAlloc(sizeof(*pRef)); + if (pRef == NULL) + return(NULL); + + pRef->iLen = iSize; + + pRef->iWrap = iWrap; + pRef->iInp = pRef->iOut = 0; + pRef->pBuf = ZMemAlloc(pRef->iLen+1); + + TesterRegistrySetPointer("CONSOLE", pRef); + + return(pRef); +} + + +/*F********************************************************************************/ +/*! + \Function TesterConsoleCreate + + \Description + Connect a console to a particular ref set. + + \Input *pRef - console reference + \Input iRefcon - constant passed back during flush callback + \Input pRefptr - pointer passed back during flush callback + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterConsoleConnect(TesterConsoleT *pRef, int32_t iRefcon, void *pRefptr) +{ + pRef->iRefcon = iRefcon; + pRef->pRefptr = pRefptr; +} + + +/*F********************************************************************************/ +/*! + \Function TesterConsoleClear + + \Description + Clear the console + + \Input *pRef - console reference + + \Output None + + \Version 04/07/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterConsoleClear(TesterConsoleT *pRef) +{ + // check for errors + if(pRef == NULL) + return; + + // wipe both the data and the head/tail pointers + ds_memclr(pRef->pBuf, (pRef->iLen)+1); + pRef->iInp = pRef->iOut = 0; +} + + +/*F********************************************************************************/ +/*! + \Function TesterConsoleOutput + + \Description + Add text to console buffer. + + \Input *pRef - console reference + \Input iMsgType - message type to prepend to the console output, NONE for none + \Input *pText - text to add to buffer (\n newlines are fine) + + \Output None + + \Version 04/04/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterConsoleOutput(TesterConsoleT *pRef, int32_t iMsgType, const char *pText) +{ + // add the content of the text + _TesterConsoleAddText(pRef, pText); +} + + +/*F********************************************************************************/ +/*! + \Function TesterConsoleFlush + + \Description + Flush buffer data to output handler (calls output handler). + + \Input *pRef - console reference + \Input *pProc - Address of output handler + + \Output None + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void TesterConsoleFlush(TesterConsoleT *pRef, TesterConsoleDisplayCbT *pProc) +{ + if (pRef->iOut < pRef->iInp) + { + // grab data in one chunk + pRef->pBuf[pRef->iInp] = 0; + (*pProc)(pRef->pBuf + pRef->iOut, pRef->iInp - pRef->iOut, pRef->iRefcon, pRef->pRefptr); + pRef->iOut = pRef->iInp; + } + else if (pRef->iOut > pRef->iInp) + { + // grab data in two chunks + pRef->pBuf[pRef->iLen] = 0; + (*pProc)(pRef->pBuf + pRef->iOut, pRef->iLen - pRef->iOut, pRef->iRefcon, pRef->pRefptr); + pRef->pBuf[pRef->iInp] = 0; + (*pProc)(pRef->pBuf, pRef->iInp, pRef->iRefcon, pRef->pRefptr); + pRef->iOut = pRef->iInp; + } +} + + +/*F********************************************************************************/ +/*! + \Function TesterConsoleDestroy + + \Description + Release resources and destroy console module. + + \Input *pRef - console reference + + \Output None + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void TesterConsoleDestroy(TesterConsoleT *pRef) +{ + ZMemFree(pRef->pBuf); + ZMemFree(pRef); + TesterRegistrySetPointer("CONSOLE", NULL); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerconsole.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerconsole.h new file mode 100644 index 00000000..09635dc5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerconsole.h @@ -0,0 +1,69 @@ +/*H********************************************************************************/ +/*! + \File testerconsole.h + + \Description + This module buffers console output for the tester application. + In essense, it is just a large FIFO which knows how to handle + newline characters as expected. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 09/15/1999 (gschaefer) First Version + \Version 11/08/1999 (gschaefer) Cleanup and revision + \Version 03/29/2005 (jfrank) Update for Tester2 +*/ +/********************************************************************************H*/ + +#ifndef _testerconsole_h +#define _testerconsole_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// module state +typedef struct TesterConsoleT TesterConsoleT; + +// display callback +typedef void (TesterConsoleDisplayCbT)(const char *pBuf, int32_t iLen, int32_t iRefcon, void *pRefptr); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Create instance of a console buffer. +TesterConsoleT *TesterConsoleCreate(int32_t iSize, int32_t iWrap); + +// Connect a console to a particular ref set. +void TesterConsoleConnect(TesterConsoleT *pRef, int32_t iRefcon, void *pRefptr); + +// Clear the console +void TesterConsoleClear(TesterConsoleT *pRef); + +// Add text to console buffer. +void TesterConsoleOutput(TesterConsoleT *pRef, int32_t iMsgType, const char *pText); + +// Flush buffer data to output handler (calls output handler) +void TesterConsoleFlush(TesterConsoleT *pRef, TesterConsoleDisplayCbT *pProc); + +// Release resources and destroy console module. +void TesterConsoleDestroy(TesterConsoleT *pRef); + +#ifdef __cplusplus +}; +#endif + +#endif // _testerconsole_h + + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhistory.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhistory.c new file mode 100644 index 00000000..ef8e4522 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhistory.c @@ -0,0 +1,408 @@ +/*H********************************************************************************/ +/*! + \File testerhistory.c + + \Description + Maintains command history for a particular user. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/05/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/dirtysock.h" +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testercomm.h" +#include "testerregistry.h" +#include "testerhistory.h" + +/*** Defines **********************************************************************/ + +#define TESTERHISTORY_SIZE_DEFAULT (100) //!< number of entries to save + +/*** Type Definitions *************************************************************/ + +//! Each history entry has a structure +typedef struct TesterHistoryEntryT +{ + int32_t iCount; //!< history entry count + char strText[TESTERCOMM_COMMANDSIZE_MAX]; //!< command text +} TesterHistoryEntryT; + +struct TesterHistoryT +{ + int32_t iTotalEntries; //!< total entries + int32_t iHeadIndex; //!< head index - first valid entry + int32_t iTailIndex; //!< tail index - last valid entry + int32_t iHeadCount; //!< command count of the head entry + int32_t iTailCount; //!< command count of the tail entry + int32_t iCount; //!< history count object - keep track of how many entries have gone past + TesterHistoryEntryT *pEntries; //!< tester history entries +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _TesterHistoryGetFullPath + + \Description + Get full path to history file. + + \Input *pFilename - name of history file + + \Output + const char * - pointer to full name + + \Version 05/11/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_TesterHistoryGetFullPath(const char *pFilename) +{ + static char strHistoryName[128]; + + // create full path + TesterRegistryGetString("ROOT", strHistoryName, sizeof(strHistoryName)); + if (strHistoryName[0] != '\0') + { + ds_strnzcat(strHistoryName, "\\", sizeof(strHistoryName)); + ds_strnzcat(strHistoryName, pFilename, sizeof(strHistoryName)); + } + else + { + ds_strnzcpy(strHistoryName, ".\\", sizeof(strHistoryName)); + } + return(strHistoryName); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterHistoryCreate + + \Description + Create a tester history module + + \Input iSize - size of history to create, 0 for default + + \Output TesterHistoryT * - allocated tester history module + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +TesterHistoryT *TesterHistoryCreate(int32_t iSize) +{ + TesterHistoryT *pState; + uint32_t uBufferSize; + + // allocate module state + pState = ZMemAlloc(sizeof(TesterHistoryT)); + ds_memclr(pState, sizeof(TesterHistoryT)); + + // allocate history entries + pState->iTotalEntries = (iSize <= 0) ? TESTERHISTORY_SIZE_DEFAULT : iSize; + uBufferSize = pState->iTotalEntries * sizeof(TesterHistoryEntryT); + pState->pEntries = ZMemAlloc(uBufferSize); + ds_memclr(pState->pEntries, uBufferSize); + + TesterRegistrySetPointer("HISTORY", pState); + + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function TesterHistoryGet + + \Description + Return the text for a particular tester command + + \Input *pState - module state + \Input iNum - entry number requested + \Input *pBuf - destination buffer (may be NULL) + \Input iSize - size of destination buffer + + \Output + const char * - pointer to history entry, or NULL + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +const char *TesterHistoryGet(TesterHistoryT *pState, int32_t iNum, char *pBuf, int32_t iSize) +{ + TesterHistoryEntryT *pEntry = NULL; + int32_t iIndex; + + // see if we have the entry + for (iIndex = 0; (iIndex < pState->iTotalEntries) && (pEntry == NULL); iIndex++) + { + if (pState->pEntries[iIndex].iCount == iNum) + { + pEntry = &(pState->pEntries[iIndex]); + break; + } + } + + // did we find it? + if (pEntry == NULL) + { + return(NULL); + } + + // copy in the data + if (pBuf != NULL) + { + ds_strnzcpy(pBuf, pEntry->strText, iSize); + } + return(pEntry->strText); +} + +/*F********************************************************************************/ +/*! + \Function TesterHistorySave + + \Description + Save historical commands to a file. + + \Input *pState - module state + \Input *pFilename - filename to save the history in + + \Output int32_t - number of entries saved, <0 if error + + \Version 05/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHistorySave(TesterHistoryT *pState, const char *pFilename) +{ + const char *pCommand; + int32_t iHead, iTail, iLoop, iTotalSize; + ZFileT iFileId; + + if ((pState == NULL) || (pFilename == NULL)) + { + return(-1); + } + + // convert to full path + pFilename = _TesterHistoryGetFullPath(pFilename); + + // get the count + if (TesterHistoryHeadTailCount(pState, &iHead, &iTail)) + { + // probably an empty list - don't save + return(0); + } + + // try and open the file + if ((iFileId = ZFileOpen(pFilename, ZFILE_OPENFLAG_WRONLY | ZFILE_OPENFLAG_CREATE)) < 0) + { + ZPrintf("testerhistory: error %d trying to save history to %s\n", iFileId, pFilename); + return(0); + } + + // save the history (overwrite anything already there) + for (iLoop = iHead, iTotalSize = 0; iLoop <= iTail; iLoop += 1) + { + if ((pCommand = TesterHistoryGet(pState, iLoop, NULL, 0)) != NULL) + { + ZFileWrite(iFileId, (void *)pCommand, (int32_t)strlen(pCommand)); + ZFileWrite(iFileId, (void *)"\n", 1); + iTotalSize += 1; + } + } + ZFileClose(iFileId); + + // return the number of entries saved + return(iTotalSize); +} + +/*F********************************************************************************/ +/*! + \Function TesterHistoryLoad + + \Description + Load historical commands into the command history from a file. + + \Notes + Loads all commands in the file into memory, but based on the size + of the history created, only the last N number of commands + (where N is the number of commands requested at TesterHistoryCreate) + will be saved in memory. + + \Input *pState - module state + \Input *pFilename - filename with the history, one command per line + + \Output int32_t - number of entries in the file, <0 if error + + \Version 05/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHistoryLoad(TesterHistoryT *pState, const char *pFilename) +{ + char *pHistory, *pCmd; + const char strSep[] = "\r\n"; + int32_t iFileSize; + int32_t iNumCommands = 0; + + if ((pState == NULL) || (pFilename == NULL)) + { + return(-1); + } + + // convert to full path + pFilename = _TesterHistoryGetFullPath(pFilename); + + // load history file + if ((pHistory = ZFileLoad(pFilename, &iFileSize, 0)) == NULL) + { + return(0); + } + + // parse history file + for (pCmd = strtok(pHistory, strSep); pCmd != NULL; pCmd = strtok(NULL, strSep)) + { + TesterHistoryAdd(pState, pCmd); + iNumCommands++; + } + + ZMemFree(pHistory); + return(iNumCommands); +} + +/*F********************************************************************************/ +/*! + \Function TesterHistoryAdd + + \Description + Add an entry to the tester history + + \Input *pState - module state + \Input *pBuf - text to add + + \Output int32_t - index value ( >= 0 ) or error code ( < 0 ) if error occurs + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHistoryAdd(TesterHistoryT *pState, const char *pBuf) +{ + TesterHistoryEntryT *pEntry; + + // check for errors + if ((pState == NULL) || (pBuf == NULL)) + { + return(TESTERHISTORY_ERROR_NULLPOINTER); + } + + // clear the new entry + pEntry = &(pState->pEntries[pState->iTailIndex]); + ds_memclr(pEntry, sizeof(TesterHistoryEntryT)); + + // and copy the new entry in + ds_strnzcpy(pEntry->strText, pBuf, sizeof(pEntry->strText)); + pState->iTailCount = pState->iCount; + pEntry->iCount = pState->iCount; + pState->iCount++; + + // adjust the tail pointer + pState->iTailIndex++; + if (pState->iTailIndex >= pState->iTotalEntries) + { + pState->iTailIndex = 0; + } + + // see if we need to adjust the head pointer + if (pState->iHeadIndex == pState->iTailIndex) + { + // we're looping around - adjust the pointer + pState->iHeadIndex++; + // adjust if we're looping around + if(pState->iHeadIndex >= pState->iTotalEntries) + pState->iHeadIndex = 0; + // get the head count + pState->iHeadCount = pState->pEntries[pState->iHeadIndex].iCount; + } + + // return the index number + return(pState->iCount); +} + +/*F********************************************************************************/ +/*! + \Function TesterHistoryHeadTailCount + + \Description + Return the head and tail history numbers + + \Input *pState - module state + \Input *pHeadNum - destination history entry number of the first entry in the buffer + \Input *pTailNum - destination history entry number of the last entry in the buffer + + \Output int32_t - 0=success, error code otherwise + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHistoryHeadTailCount(TesterHistoryT *pState, int32_t *pHeadNum, int32_t *pTailNum) +{ + // set the incoming values to some default values, just in case we run into an error + *pHeadNum = 0; + *pTailNum = 0; + + // check for errors + if ((pState == NULL) || (pHeadNum == NULL) || (pTailNum == NULL)) + { + return(TESTERHISTORY_ERROR_NULLPOINTER); + } + + // see if the buffer is empty + if (pState->iHeadIndex == pState->iTailIndex) + { + return(TESTERHISTORY_ERROR_NOSUCHENTRY); + } + + // otherwise give out some information + *pHeadNum = pState->iHeadCount; + *pTailNum = pState->iTailCount; + return(TESTERHISTORY_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function TesterHistoryDestroy + + \Description + Destroy a tester history object + + \Input *pState - module state + + \Output None + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterHistoryDestroy(TesterHistoryT *pState) +{ + // kill the object if non-null + if (pState) + { + ZMemFree(pState->pEntries); + ZMemFree(pState); + } + + TesterRegistrySetPointer("HISTORY", NULL); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhistory.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhistory.h new file mode 100644 index 00000000..a4faf90f --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhistory.h @@ -0,0 +1,66 @@ +/*H********************************************************************************/ +/*! + \File testerhistory.h + + \Description + Maintains command history for a particular user. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/05/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _testerhistory_h +#define _testerhistory_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +#define TESTERHISTORY_ERROR_NONE (0) //!< no error +#define TESTERHISTORY_ERROR_NULLPOINTER (-1) //!< null pointer sent (invalid) +#define TESTERHISTORY_ERROR_NOSUCHENTRY (-2) //!< entry requested doesn't exist + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct TesterHistoryT TesterHistoryT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create a tester history module +TesterHistoryT *TesterHistoryCreate(int32_t iSize); + +// get an entry in the history +const char *TesterHistoryGet(TesterHistoryT *pState, int32_t iNum, char *pBuf, int32_t iSize); + +// add an entry to the history +int32_t TesterHistoryAdd(TesterHistoryT *pState, const char *pBuf); + +// return the head and tail history numbers +int32_t TesterHistoryHeadTailCount(TesterHistoryT *pState, int32_t *pHeadNum, int32_t *pTailNum); + +// save historical commands to a file +int32_t TesterHistorySave(TesterHistoryT *pState, const char *pFilename); + +// load historical commands from a file +int32_t TesterHistoryLoad(TesterHistoryT *pState, const char *pFilename); + +// destroy a tester history object +void TesterHistoryDestroy(TesterHistoryT *pState); + +#ifdef __cplusplus +}; +#endif + +#endif // _testerhistory_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhostcore.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhostcore.c new file mode 100644 index 00000000..fbf4f950 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhostcore.c @@ -0,0 +1,868 @@ +/*H********************************************************************************/ +/*! + \File testerhostcore.c + + \Description + Main control module for the Tester2 host application. + + \Notes + This is the main host module for the tester2 host application. + It contains mostly global-variable type objects and operations, similar + to LobbyAPI. TesterHostCore is responsible for starting up all the + necessary child modules like TesterConsole, TesterComm, etc. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/17/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtydefs.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/util/jsonformat.h" +#include "DirtySDK/util/jsonparse.h" + +#include "libsample/zfile.h" +#include "libsample/zmem.h" +#include "testercomm.h" +#include "testerconsole.h" +#include "testermodules.h" +#include "testerregistry.h" +#include "testerhistory.h" +#include "testerhostcore.h" + +/*** Defines **********************************************************************/ + +#define TESTERHOSTCORE_CONSOLESIZE_DEFAULT (16384) //!< default size of display console + +/*** Type Definitions *************************************************************/ + +struct TesterHostCoreT +{ + TesterCommT *pComm; //!< host/client communication module + TesterConsoleT *pConsole; //!< console for managing output + TesterConsoleDisplayCbT *pDisplayProc; //!< procedure for displaying output + TesterModulesT *pModules; //!< tester modules/dispatcher + TesterHistoryT *pHistory; //!< host history (only supported on some platforms) + int32_t iCurHistory; //!< current history index + int16_t iShutdown; //!< internal - set to 1 to shut stuff down + int16_t iLocalEcho; //!< internal - set to 1 to locally echo output + ZFileT zFile; //!< logfile + char strCommand[TESTERCOMM_COMMANDSIZE_MAX]; //!< temporary command processing buffer +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Private Variables ************************************************************/ + +//! used to format strings for sending +static char _TesterHostCore_strTempText[4096] = ""; + +/*** External Functions ***********************************************************/ + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreUpdate + + \Description + Update function to pump the host/client buffers + and do other idle processing tasks. + + \Input *pData - user specified data (modules state TesterHostCoreT *) + \Input uTick - NetIdle ticks + + \Output None + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterHostCoreUpdate(void *pData, uint32_t uTick) +{ + TesterHostCoreT *pState = (TesterHostCoreT *)pData; + + // check for errors + if (pState == NULL) + return; + + // update the host client comm pipe + if (pState->pComm) + { + TesterCommUpdate(pState->pComm); + } + + // service the output + if ((pState->pConsole) && (pState->pDisplayProc)) + TesterConsoleFlush(pState->pConsole, pState->pDisplayProc); + + // see if we want to shut all running commands down + if(pState->iShutdown) + { + // shut down all running commands + pState->iShutdown = 0; + ZShutdown(); + // now disconnect dirtysock + NetConnDisconnect(); + } +} + + +/*F********************************************************************************/ +/*! + \Function _TesterHostCoreMsgControl + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - User-supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterHostCoreMsgControl(TesterCommT *pState, const char *pMsg, void *pParam) +{ + const char strNetStart[] = "NETSTART"; + const char strNetStop[] = "NETSTOP"; + TesterHostCoreT *pCore = pParam; + char *pNetParams; + + // print locally + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONTROL, pMsg); + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONTROL, "\n"); + + // ---- CONNECT ---- : + if (strcmp(pMsg, "CONNECT") == 0) + { + // print locally + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONTROL, "CONNECTED\n"); + // send it along + TesterCommMessage(pState, TESTER_MSGTYPE_CONTROL, "CONNECTED\n"); + } + // ---- NETSTART ---- : bring up the network + else if (strncmp(pMsg, strNetStart, strlen(strNetStart)) == 0) + { + // extract the net params + pNetParams = (char *)(&pMsg[strlen(strNetStart)+1]); + + // connect the module + TesterHostCoreConnect(pCore, pNetParams); + + // resolve local address + ZPrintf("Local address: %a\n", NetConnStatus('addr', 0, NULL, 0)); + + // get MAC address + ZPrintf("Local MAC address: %s\n", NetConnMAC()); + + } + // ---- NETSTOP ---- : shut down the network + else if (strncmp(pMsg, strNetStop, strlen(strNetStop)) == 0) + { + // disconnect stuff + TesterHostCoreDisconnect(pCore); + } + else + { + ZPrintf("Unknown CONTROL call: {%s}\n", pMsg); + } +} + + +/*F********************************************************************************/ +/*! + \Function _TesterHostCoreMsgStatus + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - User-supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterHostCoreMsgStatus(TesterCommT *pState, const char *pMsg, void *pParam) +{ + TesterHostCoreT *pCore = pParam; + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_STATUS, pMsg); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterHostCoreMsgCommand + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - User-supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterHostCoreMsgCommand(TesterCommT *pState, const char *pMsg, void *pParam) +{ + TesterHostCoreT *pCore = (TesterHostCoreT *)pParam; + int32_t iResult; + + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_COMMAND, "\n> "); + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_COMMAND, pMsg); + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_COMMAND, "\n"); + + // signal that we've received the message + JsonInit(pState->strCommand, sizeof(pState->strCommand), 0); + JsonAddStr(pState->strCommand, "COMMAND", pMsg); + JsonAddInt(pState->strCommand, "STATUS", 'rcvd'); + TesterCommMessage(pState, TESTER_MSGTYPE_STATUS, JsonFinish(pState->strCommand)); + + // signal that we're executing the message + JsonInit(pState->strCommand, sizeof(pState->strCommand), 0); + JsonAddStr(pState->strCommand, "COMMAND", pMsg); + JsonAddInt(pState->strCommand, "STATUS", 'exec'); + TesterCommMessage(pState, TESTER_MSGTYPE_STATUS, JsonFinish(pState->strCommand)); + + // dispatch the command + iResult = TesterHostCoreDispatch(pCore, pMsg); + + // display the results locally + ds_memclr(pState->strCommand, sizeof(pState->strCommand)); + if (iResult == 0) + { + ds_snzprintf(pState->strCommand, sizeof(pState->strCommand), "done {%s} error {none}\n", pMsg); + } + else + { + ds_snzprintf(pState->strCommand, sizeof(pState->strCommand), " {%s} error {%d}\n", pMsg, iResult); + } + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONSOLE, pState->strCommand); + ZPrintf("%s", pState->strCommand); //send it back too? + + // send results back to the client + JsonInit(pState->strCommand, sizeof(pState->strCommand), 0); + JsonAddStr(pState->strCommand, "COMMAND", pMsg); + JsonAddInt(pState->strCommand, "STATUS", 'done'); + // include an error if present + if (iResult) + { + JsonAddInt(pState->strCommand, "ERROR", iResult); + } + TesterCommMessage(pState, TESTER_MSGTYPE_STATUS, JsonFinish(pState->strCommand)); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterHostCoreMsgConsole + + \Description + Handle incoming messages from TesterComm + + \Input *pState - Module state + \Input *pMsg - Message text + \Input *pParam - User-supplied data + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterHostCoreMsgConsole(TesterCommT *pState, const char *pMsg, void *pParam) +{ + TesterHostCoreT *pCore = pParam; + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONSOLE, pMsg); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterHostCorePrintf + + \Description + Tester Printf function. + + \Input *pParm - Module state + \Input *pText - Message text + + \Output 0=success + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TesterHostCorePrintf(void *pParm, const char *pText) +{ + TesterHostCoreT *pCore = pParm; + + // this is where all NetPrintf calls wind up + // and remember that ZPrintf calls NetPrintf + // so, on the host, we have to do three things: + // 1. print locally + // 2. send back to the client + // 3. write to logfile + + // add locally + TesterConsoleOutput(pCore->pConsole, TESTER_MSGTYPE_CONSOLE, pText); + + // send to the client, but only if we've gotten input from the client + if (TesterCommStatus(pCore->pComm, 'inpt', 0)) + { + // append to buffer + ds_strnzcat(_TesterHostCore_strTempText, pText, sizeof(_TesterHostCore_strTempText)); + + // do we have a linefeed? + if (strrchr(_TesterHostCore_strTempText, '\n') != NULL) + { + TesterCommMessage(pCore->pComm, TESTER_MSGTYPE_CONSOLE, _TesterHostCore_strTempText); + _TesterHostCore_strTempText[0] = '\0'; + } + } + + // write to logfile + if (pCore->zFile != ZFILE_INVALID) + { + ZFileWrite(pCore->zFile, (char *)pText, (int32_t)(sizeof(char) * strlen(pText))); + } + + // code to write directly to output; useful when running from command-line without a debugger + #if defined(DIRTYCODE_XBOXONE) && 0 + { + DWORD dwBytesWritten; + WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), + pText, + strlen(pText), + &dwBytesWritten, + NULL); + } + #endif + + // returning a 1 here echoes locally + return(pCore->iLocalEcho); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreCreate + + \Description + Create a TesterHostCoreT module and return the pointer to it + + \Input *pParams - startup parameters + + \Output TesterHostCoreT * - newly allocated and created TesterHostCoreT + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +TesterHostCoreT *TesterHostCoreCreate(const char *pParams) +{ + TesterHostCoreT *pState; + uint16_t aJson[512]; + char strControlDir[2]; // check to see if a control dir was specified + + // create and wipe clean the module + pState = (TesterHostCoreT *)ZMemAlloc(sizeof(TesterHostCoreT)); + ds_memclr((void *)pState, sizeof(TesterHostCoreT)); + pState->zFile = (ZFileT)ZFILE_INVALID; + + JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pParams, -1); + + // create a registry + TesterRegistryCreate(-1); + TesterRegistrySetPointer("CORE", pState); + + // now create the children modules + pState->pComm = TesterCommCreate(); + pState->pConsole = TesterConsoleCreate(TESTERHOSTCORE_CONSOLESIZE_DEFAULT, TRUE); + pState->pModules = TesterModulesCreate(); + + // determine if we should locally echo stuff + pState->iLocalEcho = (int16_t)JsonGetInteger(JsonFind(aJson, "LOCALECHO"), 1); + + // register host commands + TesterModulesRegisterHostCommands(pState->pModules); + + // register platform-specific commands + TesterModulesRegisterPlatformCommands(pState->pModules); + + // attach an interface method + JsonGetString(JsonFind(aJson, "CONTROLDIR"), strControlDir, sizeof(strControlDir), ""); + if (strControlDir[0] != '\0') + { + TesterCommAttachFile(pState->pComm); + } + else + { + TesterCommAttachSocket(pState->pComm); + } + + // pass the incoming startup parameters through to the comm modules + TesterCommConnect(pState->pComm, pParams, TRUE); + + // register callbacks for all the types of messages we'll see + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_CONTROL, _TesterHostCoreMsgControl, (void *)pState); + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_COMMAND, _TesterHostCoreMsgCommand, (void *)pState); + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_STATUS, _TesterHostCoreMsgStatus, (void *)pState); + TesterCommRegister(pState->pComm, TESTER_MSGTYPE_CONSOLE, _TesterHostCoreMsgConsole, (void *)pState); + + // create history + pState->pHistory = TesterHistoryCreate(-1); + + #if DIRTYCODE_LOGGING + // in debug mode, hook into debug output + NetPrintfHook(_TesterHostCorePrintf, pState); + #endif + + // hook into tester output + ZPrintfHook(_TesterHostCorePrintf, pState); + + // done + return(pState); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreConnect + + \Description + Connect the core module and all its children + + \Input *pState - TesterHostCoreT module to connect + \Input *pNetParams - Parameters to pass to NetConnStartup() + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHostCoreConnect(TesterHostCoreT *pState, const char *pNetParams) +{ + // check the state + if (pState == NULL) + return(TESTERHOSTCORE_ERROR_NULLPOINTER); + + // start dirtysock + ZPrintf("HOSTCORE: Starting Dirtysock with NetParams {%s}\n", + (pNetParams==NULL) ? "" : pNetParams ); + NetConnStartup(pNetParams); + + // done + return(TESTERHOSTCORE_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreIdle + + \Description + Idle function - pump this to make networking and other modules happy. + + \Input *pState - TesterHostCoreT module state + + \Output None + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterHostCoreIdle(TesterHostCoreT *pState) +{ + // give time to the network + NetConnIdle(); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreDisplayFunc + + \Description + Register a display function to show stuff on the screen. + + \Input *pState - TesterHostCoreT module state + \Input *pProc - function pointer to display procedure + \Input iRefcon - int32_t value to pass to the display function when called + \Input *pDisplayRef - ref value to pass to the display function when called (hDlg) + + \Output None + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterHostCoreDisplayFunc(TesterHostCoreT *pState, TesterConsoleDisplayCbT *pProc, int32_t iRefcon, void *pRefptr) +{ + pState->pDisplayProc = pProc; + TesterConsoleConnect(pState->pConsole, iRefcon, pRefptr); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreDispatch + + \Description + Dispatch a function (if possible) based on the incoming command line. + + \Input *pState - pointer to host core module + \Input *pCommandLine - standard command line (command arg1 arg2 arg3 ...) + + \Output 0=success, error code otherwise + + \Version 10/31/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t TesterHostCoreDispatch(TesterHostCoreT *pState, const char *pCommandLine) +{ + const char *pSemi; + uint8_t bQuoted; + + // check for multiple commands separated by a semi-colon + for (bQuoted = *pCommandLine == '"', pSemi = pCommandLine+1; *pSemi != '\0'; pSemi += 1) + { + // update quoted status + if ((*pSemi == '"') && (*(pSemi-1) != '\\')) + { + bQuoted = !bQuoted; + } + + // process semicolon, if not quoted + if ((*pSemi == ';') && (*(pSemi-1) != '\\') && !bQuoted) + { + ds_strsubzcpy(pState->strCommand, sizeof(pState->strCommand), pCommandLine, (int32_t)(pSemi - pCommandLine)); + + // dispatch the command + TesterHistoryAdd(pState->pHistory, pState->strCommand); + pState->iCurHistory = -1; + TesterModulesDispatch(pState->pModules, pState->strCommand); + + // skip past command + pCommandLine = pSemi + 1; + } + } + + // dispatch the command + TesterHistoryAdd(pState->pHistory, pCommandLine); + pState->iCurHistory = -1; + return(TesterModulesDispatch(pState->pModules, pCommandLine)); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreGetHistory + + \Description + Get prev/next history entry. + + \Input *pState - pointer to host core module + \Input iPrevNext - amount to index prev (negative) or next (positive) in buffer + \Input *pBuf - [out] storage for returned history entry (may be null) + \Input iSize - size of output buffer + + \Output + const char * - pointer to history text, or NULL + + \Version 10/31/2005 (jbrookes) +*/ +/********************************************************************************F*/ +const char *TesterHostCoreGetHistory(TesterHostCoreT *pState, int32_t iPrevNext, char *pBuf, int32_t iSize) +{ + int32_t iHeadCount, iTailCount; + const char *pText = NULL; + + // get current min/max + TesterHistoryHeadTailCount(pState->pHistory, &iHeadCount, &iTailCount); + + // if we have a current history index, modify from there + if (pState->iCurHistory != -1) + { + // update with clamping + pState->iCurHistory += iPrevNext; + if (pState->iCurHistory > iTailCount) + { + // iTailCount+1 to return an empty entry at the tail (current commandline) + pState->iCurHistory = iTailCount+1; + } + if (pState->iCurHistory < iHeadCount) + { + pState->iCurHistory = iHeadCount; + } + } + else + { + pState->iCurHistory = (iPrevNext < 0) ? iTailCount : iHeadCount; + } + + // get history entry + if (pState->iCurHistory <= iTailCount) + { + pText = TesterHistoryGet(pState->pHistory, pState->iCurHistory, pBuf, iSize); + } + else if (pBuf != NULL) + { + *pBuf = '\0'; + } + + // return history text + return(pText); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreSendStatus + + \Description + Send a command to the host - should comes from the client GUI command line. + + \Input *pState - TesterHostCoreT module + \Input *pData - command to send + + \Output 0=success, error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHostCoreSendStatus(TesterHostCoreT *pState, const char *pData) +{ + // echo it locally + _TesterHostCoreMsgStatus(pState->pComm, pData, (void *)pState); + + // send it along + return(TesterCommMessage(pState->pComm, TESTER_MSGTYPE_STATUS, pData)); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreRegister + + \Description + Register a command + + \Input *pState - TesterClientCoreT module state + \Input *pCommand - command name to register with + \Input *pFunctionPtr - the ZCommand module to call + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHostCoreRegister(TesterHostCoreT *pState, const char *pCommand, ZCommand *pFunctionPtr) +{ + if (pState == NULL) + return(TESTERHOSTCORE_ERROR_NULLPOINTER); + + return(TesterModulesRegister(pState->pModules, pCommand, pFunctionPtr)); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreDisconnect + + \Description + Disconnect the core module and all its children + + \Input *pState - TesterHostCoreT module to disconnect + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHostCoreDisconnect(TesterHostCoreT *pState) +{ + // check the state + if (pState == NULL) + return(TESTERHOSTCORE_ERROR_NULLPOINTER); + // check the children + if (pState->pComm == NULL) + return(TESTERHOSTCORE_ERROR_NULLPOINTER); + + // kill running commands first + ZShutdown(); + + return(TESTERHOSTCORE_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreStartSavingLog + + \Description + Start writting log message in strFileName file + + \Input *pState - TesterHostCoreT module to disconnect + + \Output int32_t - 0=success, error code otherwise + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterHostCoreStartSavingLog(TesterHostCoreT *pState, const char * strLogfileName) +{ + int32_t retVal = -1; + + if (pState) + { + if (pState->zFile == ZFILE_INVALID) + { + int8_t strCompleteLogfileName[128]; + + ds_memclr(strCompleteLogfileName, sizeof(strCompleteLogfileName)); + + ds_strnzcpy((char *)strCompleteLogfileName, "GAME:\\", sizeof(strCompleteLogfileName)); + ds_strnzcat((char *)strCompleteLogfileName, (char *)strLogfileName, sizeof(strCompleteLogfileName)); + + pState->zFile = ZFileOpen((char *)strCompleteLogfileName, ZFILE_OPENFLAG_WRONLY | ZFILE_OPENFLAG_CREATE); + + if (pState->zFile != ZFILE_INVALID) + { + retVal = 0; + } + } + } + + return(retVal); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreStopSavingLog + + \Description + Stop writting log message in strFileName file + + \Input *pState - TesterHostCoreT module reference + + \Output int32_t - 0=success, error code otherwise + + \Version 09/24/2012 (akirchner) +*/ +int32_t TesterHostCoreStopSavingLog(TesterHostCoreT *pState) +{ + int32_t retVal = -1; + + if (pState->zFile != ZFILE_INVALID) + { + if(ZFileClose(pState->zFile) == 0) + { + pState->zFile = (ZFileT)ZFILE_INVALID; + retVal = 0; + } + } + + return(retVal); +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreShutdown + + \Description + Signal for a shutdown of all running tester modules + + \Input *pState - TesterHostCoreT module to shutdown + + \Output None + + \Version 04/13/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterHostCoreShutdown(TesterHostCoreT *pState) +{ + if(pState) + { + pState->iShutdown = 1; + } +} + + +/*F********************************************************************************/ +/*! + \Function TesterHostCoreDestroy + + \Description + Destroy a TesterHostCoreT module and all its children + + \Input *pState - TesterHostCoreT module to destroy + + \Output None + + \Version 03/21/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterHostCoreDestroy(TesterHostCoreT *pState) +{ + // check the state + if (pState == NULL) + { + return; + } + + // close log file + if (pState->zFile != ZFILE_INVALID) + { + ZFileClose(pState->zFile); + + if(ZFileClose(pState->zFile) == 0) + { + pState->zFile = (ZFileT)ZFILE_INVALID; + } + } + + // destroy history + TesterHistoryDestroy(pState->pHistory); + + // disconnect the children modules + TesterCommDisconnect(pState->pComm); + + // destoy all the children modules + TesterCommDestroy(pState->pComm); + TesterConsoleDestroy(pState->pConsole); + TesterModulesDestroy(pState->pModules); + + // unhook debug output + #if DIRTYCODE_LOGGING + NetPrintfHook(NULL, NULL); + #endif + + // unhook tester output + ZPrintfHook(NULL, NULL); + + // dump the registry + TesterRegistryDestroy(); + + // now destroy this module + ZMemFree((void *)pState); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhostcore.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhostcore.h new file mode 100644 index 00000000..53ef15d0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerhostcore.h @@ -0,0 +1,101 @@ +/*H********************************************************************************/ +/*! + \File testerhostcore.h + + \Description + Main control module for the Tester2 host application. + + \Notes + This is the main host module for the tester2 host application. + It contains mostly global-variable type objects and operations, similar + to LobbyAPI. TesterhostCore is responsible for starting up all the + necessary child modules like TesterConsole, TesterHostClientComm, etc. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/17/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ +#ifndef _testerhostcore_h +#define _testerhostcore_h + +/*** Include files ****************************************************************/ + +#include "libsample/zlib.h" +#include "testerprofile.h" +#include "testerconsole.h" + +/*** Defines **********************************************************************/ + +#define TESTERHOSTCORE_ERROR_NONE (0) //!< no error (success) +#define TESTERHOSTCORE_ERROR_NULLPOINTER (-1) //!< a null pointer ref was used + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// module state object +typedef struct TesterHostCoreT TesterHostCoreT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create a testerhostcore module +TesterHostCoreT *TesterHostCoreCreate(const char *pParams); + +// connect the core module +int32_t TesterHostCoreConnect(TesterHostCoreT *pState, const char *pNetParams); + +// idle function - pump this to make networking, etc. happy +void TesterHostCoreIdle(TesterHostCoreT *pState); + +// register function to flush output to the display +void TesterHostCoreDisplayFunc(TesterHostCoreT *pState, TesterConsoleDisplayCbT *pProc, int32_t iRefcon, void *pRefptr); + +// dispatch a command from an incoming command line - local version +int32_t TesterHostCoreDispatch(TesterHostCoreT *pCore, const char *pCommandLine); + +// get local history info +const char *TesterHostCoreGetHistory(TesterHostCoreT *pState, int32_t iPrevNext, char *pBuf, int32_t iSize); + +// update function - pump this to get data flowing, sending a pointer to the core module as data +void TesterHostCoreUpdate(void *pData, uint32_t uTick); + +// register a module +int32_t TesterHostCoreRegister(TesterHostCoreT *pState, const char *pCommand, ZCommand *pFunctionPtr); + +// start writting log messages in logfile +int32_t TesterHostCoreStartSavingLog(TesterHostCoreT *pState, const char * strLogfileName); + +// stop writting log messages in logfile +int32_t TesterHostCoreStopSavingLog(TesterHostCoreT *pState); + +// signal for a shutdown of all running tester modules +void TesterHostCoreShutdown(TesterHostCoreT *pState); + +// disconnect the core module +int32_t TesterHostCoreDisconnect(TesterHostCoreT *pState); + +// destroy a testerhostcore module +void TesterHostCoreDestroy(TesterHostCoreT *pState); + +// -------------------- COMMUNICATION FUNCTIONS ------------------ + +// send status back to the client +int32_t TesterHostCoreSendStatus(TesterHostCoreT *pState, const char *pData); + +// send console output back to the client +int32_t TesterHostCoreSendConsole(TesterHostCoreT *pState, const char *pData); + +#ifdef __cplusplus +}; +#endif + +#endif // _testerhostcore_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermemory.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermemory.c new file mode 100644 index 00000000..d70f9df2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermemory.c @@ -0,0 +1,125 @@ +/*H********************************************************************************/ +/*! + \File testermemory.c + + \Description + Implement dirtysock memory functions like DirtyMemAlloc and DirtyMemFree. + + \Version 04/01/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ +#include "DirtySDK/platform.h" +#include "testermemory.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "libsample/zmem.h" + + + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +static uint32_t _TesterMemory_bDebug = FALSE; + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterMemorySetDebug + + \Description + Enable/disable verbose memory debugging. + + \Input bDebug - TRUE=enable, FALSE=disable + + \Output + None. + + \Version 11/01/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void TesterMemorySetDebug(uint32_t bDebug) +{ + _TesterMemory_bDebug = bDebug; +} + + +/*F********************************************************************************/ +/*! + \Function DirtyMemAlloc + + \Description + Implementation of the required DirtySock memory allocation routine. + + \Input iSize - size of memory to allocate + \Input iMemModule - memory module id + \Input iMemGroup - memory group id + \Input *pMemGroupUserData - user data associated with mem group + + \Output + void * - pointer to newly allocated memory, or NULL + + \Version 11/01/2005 (jbrookes) + \Version 11/19/2008 (mclouatre) adding suppor for mem group user data +*/ +/********************************************************************************F*/ +#if !defined(DIRTYCODE_DLL) +void *DirtyMemAlloc(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void * pMemGroupUserData) +#else +void *DirtyMemAlloc2(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void * pMemGroupUserData) +#endif +{ + void *pMem = ZMemAlloc(iSize); + if (_TesterMemory_bDebug) + { + DirtyMemDebugAlloc(pMem, iSize, iMemModule, iMemGroup, pMemGroupUserData); + } + return(pMem); +} + +/*F********************************************************************************/ +/*! + \Function DirtyMemFree + + \Description + Implementation of the required DirtySock memory free routine. + + \Input *pMem - pointer to memory block to free + \Input iMemModule - memory module id + \Input iMemGroup - memory group id + \Input *pMemGroupUserData - user data associated with mem group + + \Output + None. + + \Version 11/01/2005 (jbrookes) + \Version 11/19/2008 (mclouatre) adding suppor for mem group user data +*/ +/********************************************************************************F*/ +#if !defined(DIRTYCODE_DLL) +void DirtyMemFree(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#else +void DirtyMemFree2(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#endif +{ + #if DIRTYCODE_DEBUG + uint32_t uSize = ZMemFree(pMem); + #else + ZMemFree(pMem); + #endif + + if (_TesterMemory_bDebug) + { + DirtyMemDebugFree(pMem, uSize, iMemModule, iMemGroup, pMemGroupUserData); + } +} + + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermemory.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermemory.h new file mode 100644 index 00000000..995190bc --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermemory.h @@ -0,0 +1,42 @@ +/*H********************************************************************************/ +/*! + \File testermemory.h + + \Description + Tester memory management. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 11/01/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _testermemory_h +#define _testermemory_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// enable/disable verbose dirtysock memory debugging + void TesterMemorySetDebug(uint32_t bDebug); + +#ifdef __cplusplus +}; +#endif + +#endif // _testermemory_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodules.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodules.c new file mode 100644 index 00000000..2f0be6b2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodules.c @@ -0,0 +1,370 @@ +/*H********************************************************************************/ +/*! + \File testermodules.c + + \Description + Common, platform independent tester modules. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/22/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "libsample/zmem.h" +#include "testercomm.h" +#include "testerregistry.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct TesterModulesCommandT +{ + char strName[TESTERMODULES_COMMANDNAMESIZE_DEFAULT]; //!< name of the command + ZCommand *pFunc; //!< function to call when dispatched +} TesterModulesCommandT; + +struct TesterModulesT +{ + TesterModulesCommandT Commands[TESTERMODULES_NUMCOMMANDS_DEFAULT]; //!< command list + int32_t iNumCommands; +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterModulesCreate + + \Description + Create a tester host client communication module. + + \Input None + + \Output TesterModulesT * - pointer to allocated module + + \Version 04/01/2005 (jfrank) +*/ +/********************************************************************************F*/ +TesterModulesT *TesterModulesCreate(void) +{ + TesterModulesT *pState; + + pState = ZMemAlloc(sizeof(TesterModulesT)); + ds_memclr(pState, sizeof(TesterModulesT)); + TesterRegistrySetPointer("MODULES", pState); + + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function TesterModulesRegister + + \Description + Register a particular command with a function call pointer. + + \Input *pState - pointer to host client comm module + \Input *pCommand - null-terminated string to register the function call with + \Input *pFunction - function call to associate with the string + + \Output int32_t - 0=success, error code otherwise + + \Version 04/01/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterModulesRegister(TesterModulesT *pState, const char *pCommand, ZCommand *pFunctionPtr) +{ + TesterModulesCommandT *pCmd = NULL; + uint32_t uCommand; + int32_t iStrcmp; + + // check for error conditions + if ((pState == NULL) || (pCommand == NULL) || (pFunctionPtr == NULL)) + { + return(TESTERMODULES_ERROR_NULLPOINTER); + } + + // make sure there's room + if (pState->iNumCommands == TESTERMODULES_NUMCOMMANDS_DEFAULT) + { + return(TESTERMODULES_ERROR_COMMANDLISTFULL); + } + + // find where to insert the command + for (uCommand = 0; uCommand < TESTERMODULES_NUMCOMMANDS_DEFAULT; uCommand++) + { + // ref the command + pCmd = &pState->Commands[uCommand]; + + // empty slot? + if (pCmd->pFunc == NULL) + { + break; + } + + // compare the commands + iStrcmp = strcmp(pCommand, pCmd->strName); + + // see if we've already registered this command + if (iStrcmp == 0) + { + if (pCmd->pFunc == pFunctionPtr) + { + NetPrintf(("testermodules: warning -- benign redefinition of command %s\n", pCommand)); + return(TESTERMODULES_ERROR_NONE); + } + else + { + NetPrintf(("testermodules: error -- redefinition of command %s\n", pCommand)); + return(TESTERMODULES_ERROR_REDEFINITON); + } + } + // check to see if we should insert to preserve alphabetical order + else if (iStrcmp < 0) + { + // create a space for the command + memmove(pCmd+1, pCmd, (pState->iNumCommands-uCommand) * sizeof(*pCmd)); + break; + } + } + + // register the command + ds_strnzcpy(pCmd->strName, pCommand, sizeof(pCmd->strName)); + pCmd->pFunc = pFunctionPtr; + pState->iNumCommands += 1; + + return(TESTERMODULES_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function TesterModulesGetCommandName + + \Description + Get a command name in the list at a particular index. + + \Input *pState - pointer to host client comm module + \Input iIndex - index of command name to retrieve + \Input *pBuf - destination string for the command + \Input iBufSize - size of the destination string + + \Output int32_t - 0=success, error code otherwise + + \Version 04/01/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterModulesGetCommandName(TesterModulesT *pState, int32_t iCommandNum, char *pBuf, int32_t iBufSize) +{ + TesterModulesCommandT *pCmd; + + // check for error conditions + if ((pState == NULL) || (pBuf == NULL) || (iBufSize <= 0)) + { + return(TESTERMODULES_ERROR_NULLPOINTER); + } + + // check to see if we're even in range + if ((iCommandNum < 0) || (iCommandNum >= TESTERMODULES_NUMCOMMANDS_DEFAULT)) + { + return(TESTERMODULES_ERROR_NOSUCHCOMMAND); + } + + // now get the entry we want, if it's non-null + pCmd = &(pState->Commands[iCommandNum]); + if (pCmd->strName[0] != 0) + { + ds_strnzcpy(pBuf, pCmd->strName, iBufSize); + } + + return(TESTERMODULES_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function TesterModulesDispatch + + \Description + Dispatch a function (if possible) based on the incoming command line. + + \Input *pState - pointer to host client comm module + \Input *pCommandLine - standard command line (command arg1 arg2 arg3 ...) + + \Output 0=success, error code otherwise + + \Version 04/01/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterModulesDispatch(TesterModulesT *pState, const char *pCommandLine) +{ + char strCommand[256]; + TesterModulesCommandT *pCmd = NULL; + uint32_t uLoop; + char *pCommand; + ZEnviron *pEnv = NULL; + + // helper variables + char *pListName; + uint32_t uListNameSize, uCommandNameSize; + + // check for error conditions + if ((pState == NULL) || (pCommandLine == NULL)) + { + return(TESTERMODULES_ERROR_NULLPOINTER); + } + + // find start of strCommand name + for (pCommand = (char *)pCommandLine; (*pCommand != 0) && (*pCommand <= ' '); pCommand += 1) + ; + + // copy over strCommand and null-terminate + ds_strnzcpy(strCommand, pCommand, sizeof(strCommand)); + + // terminate command name + for (pCommand = strCommand; (*pCommand != 0) && (*pCommand > ' '); pCommand += 1) + ; + *pCommand = 0; + uCommandNameSize = (uint32_t)strlen(strCommand); + + // locate corresponding strCommand + for (uLoop = 0; uLoop < TESTERMODULES_NUMCOMMANDS_DEFAULT; uLoop++) + { + // check to see if we match and we want to re-register a function + // or if we're at the end of the list + pListName = pState->Commands[uLoop].strName; + uListNameSize = (uint32_t)strlen(pListName); + if ((uListNameSize > 0) && (uListNameSize == uCommandNameSize) && (strncmp(strCommand, pListName, uCommandNameSize) == 0)) + { + pCmd = &(pState->Commands[uLoop]); + pEnv = ZCreate(NULL, pCommandLine); + ZInvoke(pEnv, pCmd->pFunc); + break; + } + } + + // show error if no command. or problem with env. + if (((pCmd == NULL) && (strCommand[0] != 0)) || (pEnv == NULL)) + { + return(TESTERMODULES_ERROR_NOSUCHCOMMAND); + } + else //give the status of the actual command.. + { + return(ZGetStatus(pEnv)); + } +} + +/*F********************************************************************************/ +/*! + \Function TesterModulesHelp + + \Description + Display help for a command. Pass NULL to get help for all commands. + + \Input *pState - pointer to host client comm module + \Input *pCommandLine - standard command line (command arg1 arg2 arg3 ...) + + \Output None + + \Version 04/01/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterModulesHelp(TesterModulesT *pState, const char *pCommand) +{ + int32_t iLoop, iFoundHelp=0; + + // check error conditions + if(pState == NULL) + { + return; + } + + // if NULL, list all commands (do not show help for them!) + if(pCommand == NULL) + { + char strLine[128] = ""; + + ZPrintf("tester2 modules\n"); + + // create list of modules + for(iLoop = 0; iLoop < TESTERMODULES_NUMCOMMANDS_DEFAULT; iLoop++) + { + if(pState->Commands[iLoop].strName[0] != '\0') + { + ds_strnzcat(strLine, pState->Commands[iLoop].strName, sizeof(strLine)); + if (strlen(strLine) > 79) + { + ZPrintf("%s\n", strLine); + strLine[0] = '\0'; + } + else + { + ds_strnzcat(strLine, " ", sizeof(strLine)); + } + } + } + + // print last entry? + if (strLine[0] != '\0') + { + ZPrintf("%s\n", strLine); + } + } + // otherwise get help on a specific command + else + { + for(iLoop = 0; iLoop < TESTERMODULES_NUMCOMMANDS_DEFAULT; iLoop++) + { + if(strncmp(pCommand, pState->Commands[iLoop].strName, strlen(pCommand)) == 0) + { + char *pBuf[1]; + pBuf[0] = (pState->Commands[iLoop]).strName; + pState->Commands[iLoop].pFunc(NULL, 0, pBuf); + iFoundHelp = 1; + break; + } + } + + if(iFoundHelp == 0) + { + ZPrintf("ERROR: Could not find help for {%s}\n",pCommand); + } + } +} + +/*F********************************************************************************/ +/*! + \Function TesterModulesDestroy + + \Description + Destroy a tester host client communication module. + + \Input *pState - pointer to host client comm module + + \Output None + + \Version 04/01/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterModulesDestroy(TesterModulesT *pState) +{ + if(pState) + { + ZMemFree(pState); + } + TesterRegistrySetPointer("MODULES", NULL); +} + + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodules.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodules.h new file mode 100644 index 00000000..8bc6ef3a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodules.h @@ -0,0 +1,143 @@ +/*H********************************************************************************/ +/*! + \File testermodules.h + + \Description + Prototypes for tester modules, to be included in platform-specific + implementations of the tester2 host application. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/22/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _testermodules_h +#define _testermodules_h + +/*** Include files ****************************************************************/ + +#include "libsample/zlib.h" + +/*** Defines **********************************************************************/ + +#define TESTERMODULES_COMMANDNAMESIZE_DEFAULT (16) //!< max command name length +#define TESTERMODULES_NUMCOMMANDS_DEFAULT (64) //!< max number of commands + +#define TESTERMODULES_ERROR_NONE (0) //!< no error +#define TESTERMODULES_ERROR_NULLPOINTER (-1) //!< invalid pointer used +#define TESTERMODULES_ERROR_COMMANDLISTFULL (-2) //!< command list full - cannot register another command +#define TESTERMODULES_ERROR_NOSUCHCOMMAND (-3) //!< no such command in the list +#define TESTERMODULES_ERROR_NODIRTYSOCK (-4) //!< dirtysock isn't started +#define TESTERMODULES_ERROR_REDEFINITON (-5) //!< attempt to redefine a command + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// module state +typedef struct TesterModulesT TesterModulesT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create a testermodules module +TesterModulesT *TesterModulesCreate(void); + +// register a module +int32_t TesterModulesRegister(TesterModulesT *pState, const char *pCommand, ZCommand *pFunctionPtr); + +// dispatch a command from an incoming command line +int32_t TesterModulesDispatch(TesterModulesT *pState, const char *pCommandLine); + +// get help on a command, or NULL for help on all commands +void TesterModulesHelp(TesterModulesT *pState, const char *pCommand); + +// return a specific registered module's command line name +int32_t TesterModulesGetCommandName(TesterModulesT *pState, int32_t iCommandNum, char *pBuf, int32_t iBufSize); + +// destroy +void TesterModulesDestroy(TesterModulesT *pState); + +// ---------------------------- CLIENT SPECIFIC ------------------------------ + +// modules registered in this function are available to clients on all platforms +int32_t TesterModulesRegisterClientCommands(TesterModulesT *pState); + +// ---------------------------- HOST SPECIFIC -------------------------------- + +// modules registered in this function are available to hosts on all platforms +int32_t TesterModulesRegisterHostCommands(TesterModulesT *pState); + +// ---------------------------- PLATFORM SPECIFIC ---------------------------- + +/* each platform will need to implement a version of this function to + handle registering any platform-specific functions it will want */ +int32_t TesterModulesRegisterPlatformCommands(TesterModulesT *pState); + +// ---------------------------- TESTER MODULES ------------------------------- + +// tester2 built-ins +int32_t CmdExit(ZContext *argz, int32_t argc, char **argv); +int32_t CmdHelp(ZContext *argz, int32_t argc, char **argv); +int32_t CmdHistory(ZContext *argz, int32_t argc, char **argv); +int32_t CmdMemDebug(ZContext *argz, int32_t argc, char **argv); +int32_t CmdRegistry(ZContext *argz, int32_t argc, char **argv); +int32_t CmdRunScript(ZContext *argz, int32_t argc, char **argv); +int32_t CmdSleep(ZContext *argz, int32_t argc, char **argv); +int32_t CmdSource(ZContext *argz, int32_t argc, char **argv); + +// tester2 modules +int32_t CmdAdvert(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdBase64(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdCrypt(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdDemangler(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdDriver(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdGameLink(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdHpack(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdHttp(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdHttp2(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdHttpMgr(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdHttpServ(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdImgConv(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdJson(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdLang(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdMbTest(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdNet(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdNetPrint(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdNetPrint(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdQos(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdSecure(ZContext *argz, int32_t argc, char *argv[]); +#if defined(DIRTYCODE_PS4) +int32_t CmdSession(ZContext *argz, int32_t argc, char *argv[]); +#endif +int32_t CmdSocket(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdStream(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdString(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdTime(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdTunnel(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdUpnp(ZContext *argz, int32_t argc, char *argv[]); +#if defined(DIRTYCODE_PS4) || defined (DIRTYCODE_XBOXONE) || defined(DIRTYCODE_STADIA) +int32_t CmdUser(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdUserList(ZContext *argz, int32_t argc, char *argv[]); +#endif +#if defined(DIRTYCODE_PS4) || defined (DIRTYCODE_XBOXONE) || defined(DIRTYCODE_STADIA) +int32_t CmdPriv(ZContext *argz, int32_t argc, char *argv[]); +#endif +int32_t CmdUtf8(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdVoice(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdWS(ZContext *argz, int32_t argc, char *argv[]); +int32_t CmdXml(ZContext *argz, int32_t argc, char *argv[]); + +#ifdef __cplusplus +}; +#endif + +#endif // _testermodules_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodulesclient.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodulesclient.c new file mode 100644 index 00000000..fa6c169e --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermodulesclient.c @@ -0,0 +1,55 @@ +/*H********************************************************************************/ +/*! + \File testermodulesclient.c + + \Description + PC specific module startup. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function TesterModulesRegisterHostCommands + + \Description + Register all PC-specific modules + + \Input *pState - module state + + \Output 0=success, error code otherwise + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterModulesRegisterClientCommands(TesterModulesT *pState) +{ + TesterModulesRegister(pState, "run", &CmdRunScript); + TesterModulesRegister(pState, "runscript", &CmdRunScript); + TesterModulesRegister(pState, "history", &CmdHistory); + TesterModulesRegister(pState, "!", &CmdHistory); + TesterModulesRegister(pState, "reg", &CmdRegistry); + TesterModulesRegister(pState, "registry", &CmdRegistry); + TesterModulesRegister(pState, "sleep", &CmdSleep); + return(TESTERMODULES_ERROR_NONE); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermoduleshostcommon.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermoduleshostcommon.c new file mode 100644 index 00000000..69b6718c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testermoduleshostcommon.c @@ -0,0 +1,99 @@ +/*H********************************************************************************/ +/*! + \File testermoduleshost.c + + \Description + PC specific module startup. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/11/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function TesterModulesRegisterHostCommands + + \Description + Register all PC-specific modules + + \Input *pState - module state + + \Output 0=success, error code otherwise + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterModulesRegisterHostCommands(TesterModulesT *pState) +{ + // tester2 built-ins + TesterModulesRegister(pState, "help", &CmdHelp); + TesterModulesRegister(pState, "history", &CmdHistory); + TesterModulesRegister(pState, "!", &CmdHistory); + TesterModulesRegister(pState, "memdebug", &CmdMemDebug); + TesterModulesRegister(pState, "registry", &CmdRegistry); + TesterModulesRegister(pState, "source", &CmdSource); + + // common tester2 modules + TesterModulesRegister(pState, "base64", &CmdBase64); + TesterModulesRegister(pState, "crypt", &CmdCrypt); + TesterModulesRegister(pState, "gamelink", &CmdGameLink); + TesterModulesRegister(pState, "hpack", &CmdHpack); + TesterModulesRegister(pState, "http", &CmdHttp); + TesterModulesRegister(pState, "http2", &CmdHttp2); + TesterModulesRegister(pState, "httpmgr", &CmdHttpMgr); + TesterModulesRegister(pState, "httpserv", &CmdHttpServ); +#if (defined(DIRTYCODE_PC)) + TesterModulesRegister(pState, "ic", &CmdImgConv); +#endif + TesterModulesRegister(pState, "json", &CmdJson); + TesterModulesRegister(pState, "lang", &CmdLang); + TesterModulesRegister(pState, "net", &CmdNet); + TesterModulesRegister(pState, "netprint", &CmdNetPrint); +#if defined(DIRTYCODE_PS4) && !defined(DIRTYCODE_PS5) + TesterModulesRegister(pState, "session", &CmdSession); +#endif + TesterModulesRegister(pState, "socket", &CmdSocket); +#if defined(DIRTYCODE_PC) + TesterModulesRegister(pState, "stream", &CmdStream); +#endif + TesterModulesRegister(pState, "string", &CmdString); + TesterModulesRegister(pState, "time", &CmdTime); + TesterModulesRegister(pState, "tunnel", &CmdTunnel); +#if (defined(DIRTYCODE_PS4) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_STADIA)) && !defined(DIRTYCODE_PS5) + TesterModulesRegister(pState, "user", &CmdUser); + TesterModulesRegister(pState, "userlist", &CmdUserList); +#endif +#if (defined(DIRTYCODE_PS4) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_STADIA)) && !defined(DIRTYCODE_PS5) + TesterModulesRegister(pState, "priv", &CmdPriv); +#endif + TesterModulesRegister(pState, "utf8", &CmdUtf8); + TesterModulesRegister(pState, "ws", &CmdWS); + TesterModulesRegister(pState, "xml", &CmdXml); + TesterModulesRegister(pState, "qos", &CmdQos); + + // tester2 modules for non-Xbox platforms +#if !defined(DIRTYCODE_XBOXONE) + TesterModulesRegister(pState, "demangler", &CmdDemangler); + TesterModulesRegister(pState, "upnp", &CmdUpnp); +#endif + + return(TESTERMODULES_ERROR_NONE); +} diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerprofile.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerprofile.c new file mode 100644 index 00000000..e4e18d21 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerprofile.c @@ -0,0 +1,706 @@ +/*H********************************************************************************/ +/*! + \File testerprofile.c + + \Description + Maintain user profiles for the tester application. + + \Notes + A profile is selected, created, or deleted after client GUI start + and before anything else happens. Currently, the following items + will be stored in a tester2 profile: command history, network startup, + and lobby parameters. Profiles will be stored in a single file on the + clients disk, the elements of the profile encoded into tagfields. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/18/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/util/jsonformat.h" +#include "DirtySDK/util/jsonparse.h" +#include "libsample/zmem.h" +#include "libsample/zlib.h" +#include "libsample/zfile.h" +#include "testerprofile.h" +#include "testerregistry.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// module state +struct TesterProfileT +{ + TesterProfileEntryT Profiles[TESTERPROFILE_NUMPROFILES_DEFAULT]; //!< profile entry list + uint32_t uProfileTail; //!< tail index for the profile list + char strFilename[TESTERPROFILE_PROFILEFILENAME_SIZEDEFAULT]; //!< filename for the profile file +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _TesterProfileGenerateHistoryFilename + + \Description + Use an entry's name to generate a history file name + + \Input *pState - module state + + \Output int32_t - error codes, 0 if success + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterProfileGenerateHistoryFilename(TesterProfileEntryT *pEntry) +{ + int32_t iNumChars, iChecksum; + const char strFileSuffix[] = ".history.txt"; + char strChecksum[16]; + char cReplace; + + + // check for errors + if(pEntry == NULL) + return; + + // create the history filename + iChecksum=0; + ds_memclr(strChecksum, sizeof(strChecksum)); + ds_memclr(pEntry->strHistoryFile, sizeof(pEntry->strHistoryFile)); + for(iNumChars = 0; + (iNumChars < (signed)sizeof(pEntry->strProfileName)) && (pEntry->strProfileName[iNumChars] != 0); + iNumChars++) + { + cReplace = pEntry->strProfileName[iNumChars]; + iChecksum += (int32_t)cReplace; + if( ((cReplace >= '0') && (cReplace <= '9')) || + ((cReplace >= 'a') && (cReplace <= 'z')) || + ((cReplace >= 'A') && (cReplace <= 'Z')) || + ((cReplace >= '0') && (cReplace <= '9')) ) + { + // character OK + } + else + { + cReplace = '_'; + } + pEntry->strHistoryFile[iNumChars] = cReplace; + } + sprintf(strChecksum, "%d", iChecksum); + strncat(pEntry->strHistoryFile, strChecksum, sizeof(pEntry->strHistoryFile) - strlen(pEntry->strHistoryFile) - 1); + strncat(pEntry->strHistoryFile, strFileSuffix, sizeof(pEntry->strHistoryFile) - strlen(pEntry->strHistoryFile) - 1); +} + + +/*F********************************************************************************/ +/*! + \Function _TesterProfileKillAllEntries + + \Description + Delete all the profile entries in the profile list + + \Input *pState - module state to delete all the entries from + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterProfileKillAllEntries(TesterProfileT *pState) +{ + pState->uProfileTail = 0; + ds_memclr(pState->Profiles, sizeof(pState->Profiles)); +} + +/*F********************************************************************************/ +/*! + \Function _TesterProfileParseLine + + \Description + Take an incoming tagfield and create a profile structure from it + + \Input *pData - incoming data buffer + \Input *pEntry - profile entry structure to put the data into + + \Output None + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterProfileParseLine(char *pData, TesterProfileEntryT *pEntry) +{ + uint16_t aJson[512]; + + // check error conditions + if((pData == NULL) || (pEntry == NULL)) + return; + + // wipe the supplied structure + ds_memclr(pEntry, sizeof(*pEntry)); + + JsonParse(aJson, sizeof(aJson)/sizeof(*aJson), pData, -1); + + // populate with data + JsonGetString(JsonFind(aJson, TESTERPROFILE_PROFILENAME_TAGVALUE), pEntry->strProfileName, sizeof(pEntry->strProfileName), TESTERPROFILE_PROFILENAME_VALUEDEFAULT); + JsonGetString(JsonFind(aJson, TESTERPROFILE_CONTROLDIR_TAGVALUE), pEntry->strControlDirectory, sizeof(pEntry->strControlDirectory), TESTERPROFILE_CONTROLDIR_VALUEDEFAULT); + JsonGetString(JsonFind(aJson, TESTERPROFILE_COMMANDLINE_TAGVALUE), pEntry->strCommandLine, sizeof(pEntry->strCommandLine), TESTERPROFILE_COMMANDLINE_VALUEDEFAULT); + JsonGetString(JsonFind(aJson, TESTERPROFILE_LOGDIRECTORY_TAGVALUE), pEntry->strLogDirectory, sizeof(pEntry->strLogDirectory), TESTERPROFILE_LOGDIRECTORY_VALUEDEFAULT); + JsonGetString(JsonFind(aJson, TESTERPROFILE_HOSTNAME_TAGVALUE), pEntry->strHostname, sizeof(pEntry->strHostname), TESTERPROFILE_HOSTNAME_VALUEDEFAULT); + JsonGetString(JsonFind(aJson, TESTERPROFILE_HISTORYFILE_TAGVALUE), pEntry->strHistoryFile, sizeof(pEntry->strHistoryFile), TESTERPROFILE_HISTORYFILE_VALUEDEFAULT); + pEntry->uLogEnable = (uint8_t)JsonGetInteger(JsonFind(aJson, TESTERPROFILE_LOGENABLE_TAGVALUE), TESTERPROFILE_LOGENABLE_VALUEDEFAULT); + pEntry->uNetworkStartup = (uint8_t)JsonGetInteger(JsonFind(aJson, TESTERPROFILE_NETWORKSTART_TAGVALUE), TESTERPROFILE_NETWORKSTART_VALUEDEFAULT); + pEntry->uDefault = (uint8_t)JsonGetInteger(JsonFind(aJson, TESTERPROFILE_DEFAULTPROFILE_TAGVALUE), 0); + + // create a history file name + _TesterProfileGenerateHistoryFilename(pEntry); +} + +/*F********************************************************************************/ +/*! + \Function _TesterProfileParseFile + + \Description + Take incoming data (from a file) and parse into profile structures + + \Notes + This function is destructive and will modify the contents at pData. + + \Input *pState - module state + \Input *pData - incoming data buffer + \Input iSize - amount of data in the buffer + + \Output None + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _TesterProfileParseFile(TesterProfileT *pState, char *pData) +{ + const char strSep[] = {"\r\n"}; + char *pDataPtr; + + // wipe out the previous list in case it exists + _TesterProfileKillAllEntries(pState); + + // walk the pData buffer and parse each incoming line + pDataPtr = (char *)strtok(pData, strSep); + while(pDataPtr) + { + // get an entry based on the line + _TesterProfileParseLine(pDataPtr, &pState->Profiles[pState->uProfileTail]); + pDataPtr = (char *)strtok(NULL, strSep); + pState->uProfileTail++; + } +} + + +/*F********************************************************************************/ +/*! + \Function _TesterProfileReadFile + + \Description + Read a file containing profiles and store it in the profile list. + + \Input *pState - module state + + \Output int32_t - error codes, 0 if success + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TesterProfileReadFile(TesterProfileT *pState) +{ + char *pProfileString; + int32_t iFileSize; + + // load the file in + pProfileString = ZFileLoad(pState->strFilename, &iFileSize, 0); + if(pProfileString == NULL) + { + ZPrintf("testerprofile: TesterProfileConnect error opening file [%s]\n", pState->strFilename); + return(TESTERPROFILE_ERROR_FILEOPEN); + } + + // parse the file out + _TesterProfileParseFile(pState, pProfileString); + + // don't forget to dump the memory when done + ZMemFree(pProfileString); + + // quit + return(TESTERPROFILE_ERROR_NONE); +} + + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function TesterProfileCreate + + \Description + Create a tester profile manager. + + \Input None + + \Output TesterProfileT * - allocated module state pointer + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +TesterProfileT *TesterProfileCreate(void) +{ + TesterProfileT *pState = (TesterProfileT *)ZMemAlloc(sizeof(TesterProfileT)); + ds_memclr(pState, sizeof(TesterProfileT)); + TesterRegistrySetPointer("PROFILE", pState); + return(pState); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileConnect + + \Description + Connect the profile manager to a set of profiles in a file. + + \Input *pState - module state + \Input *pFilename - filename to try to read + + \Output int32_t - error codes, 0 if success + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileConnect(TesterProfileT *pState, const char *pFilename) +{ + + // check for error conditions + if (pState == NULL) + return(TESTERPROFILE_ERROR_NULLPOINTER); + if (pFilename == NULL) + return(TESTERPROFILE_ERROR_INVALIDFILENAME); + if (pState->strFilename[0] != 0) + return(TESTERPROFILE_ERROR_FILEALREADYOPEN); + + // copy the filename in + ds_strnzcpy(pState->strFilename, pFilename, sizeof(pState->strFilename)); + + // read the contents of the requested file + _TesterProfileReadFile(pState); + + // done + return(TESTERPROFILE_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileAdd + + \Description + Add a profile to the list + + \Input *pState - module state + \Input *pEntry - profile entry to add + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileAdd(TesterProfileT *pState, TesterProfileEntryT *pEntry) +{ + TesterProfileEntryT *pTarget = NULL; + uint32_t uLoop, uNameLen, uStoredNameLen; + char *pStoredProfileName; + + // check for errors + if((pState == NULL) || (pEntry == NULL)) + { + return(TESTERPROFILE_ERROR_NULLPOINTER); + } + + // make sure we have a valid history filename + _TesterProfileGenerateHistoryFilename(pEntry); + + // walk the list and look for a place to put it + // watch for a matching profile name as well + uNameLen = (uint32_t)strlen(pEntry->strProfileName); + for(uLoop = 0; (uLoop < pState->uProfileTail) && (pTarget == NULL); uLoop++) + { + pStoredProfileName = pState->Profiles[uLoop].strProfileName; + uStoredNameLen = (uint32_t)strlen(pStoredProfileName); + // if it matches, we have a target + if((uStoredNameLen == uNameLen) && + (strcmp(pStoredProfileName, pEntry->strProfileName) == 0)) + { + pTarget = &pState->Profiles[uLoop]; + } + } + + // if we don't have a match, see if we can make some room at the end + if(pTarget == NULL) + { + // no match - see if we have room + if(pState->uProfileTail < TESTERPROFILE_NUMPROFILES_DEFAULT) + { + pTarget = &pState->Profiles[pState->uProfileTail]; + pState->uProfileTail++; + } + } + + // copy all the parameters in if we found a spot + if(pTarget != NULL) + { + ds_memcpy(pTarget, pEntry, sizeof(TesterProfileEntryT)); + } + + return(TESTERPROFILE_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function TesterProfileDelete + + \Description + Remove a profile from the list + + \Input *pState - module state + \Input *pName - profile to nuke + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileDelete(TesterProfileT *pState, const char *pName) +{ + uint32_t uLoop, uNameLen, uStoredNameLen; + char *pStoredProfileName; + + // check for errors + if((pState == NULL) || (pName == NULL)) + return(TESTERPROFILE_ERROR_NULLPOINTER); + + // nuke the entry in question + uNameLen = (uint32_t)strlen(pName); + for(uLoop = 0; uLoop < pState->uProfileTail; uLoop++) + { + // if it matches, we have a target + pStoredProfileName = (pState->Profiles[uLoop]).strProfileName; + uStoredNameLen = (uint32_t)strlen(pStoredProfileName); + if((uStoredNameLen == uNameLen) && + (strcmp(pStoredProfileName, pName) == 0)) + { + // nuke the entry so we won't save it + ds_memclr(&pState->Profiles[uLoop],sizeof(TesterProfileEntryT)); + } + } + + // now force a save (save won't dump a NULL entry) + TesterProfileSave(pState); + // and re-read the file + _TesterProfileReadFile(pState); + + return(TESTERPROFILE_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileSetDefaultName + + \Description + Set a particular profile as the default profile + + \Input *pState - module state + \Input *pProfileName - profile to set as default + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileSetDefaultName(TesterProfileT *pState, const char *pProfileName) +{ + uint32_t uStoredNameLen, uNameLen; + char *pStoredProfileName; + uint32_t uLoop; + + // check for errors first + if((pState == NULL) || (pProfileName == NULL)) + return(TESTERPROFILE_ERROR_NULLPOINTER); + + uNameLen = (uint32_t)strlen(pProfileName); + for(uLoop = 0; uLoop < TESTERPROFILE_NUMPROFILES_DEFAULT; uLoop++) + { + pState->Profiles[uLoop].uDefault = 0; + pStoredProfileName = pState->Profiles[uLoop].strProfileName; + uStoredNameLen = (uint32_t)strlen(pStoredProfileName); + if((uNameLen == uStoredNameLen) && + (strcmp(pProfileName, pStoredProfileName) == 0)) + { + pState->Profiles[uLoop].uDefault = 1; + } + } + + // no error + return(TESTERPROFILE_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileGetDefaultIndex + + \Description + Return the index of the default profile + + \Input *pState - module state + + \Output int32_t - index of default profile, 0 if no default + + \Version 03/28/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileGetDefaultIndex(TesterProfileT *pState) +{ + uint32_t uLoop; + int32_t iIndex = TESTERPROFILE_ERROR_NOSUCHENTRY; + + if(pState == NULL) + return(TESTERPROFILE_ERROR_NULLPOINTER); + + // loop while less than total number and not a default entry + for(uLoop = 0; uLoop < TESTERPROFILE_NUMPROFILES_DEFAULT; uLoop++) + { + if(pState->Profiles[uLoop].uDefault != 0) + iIndex = uLoop; + } + return(iIndex); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileSave + + \Description + Save all profiles to disk. + + \Notes + Called automatically on disconnect - no need to call this function + unless special functionality is desired. Will not save NULL entries. + + \Input *pState - module state + + \Output int32_t - 0 for success, error code otherwise + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileSave(TesterProfileT *pState) +{ + char strProfile[1024]; + ZFileT iFilePointer; + int32_t iEntryNum; + + // open the file for create (no append) + iFilePointer = ZFileOpen(pState->strFilename, ZFILE_OPENFLAG_CREATE | ZFILE_OPENFLAG_WRONLY); + if (iFilePointer < 0) + { + return(TESTERPROFILE_ERROR_FILEOPEN); + } + + // loop through all entries and write the valid ones out + for (iEntryNum = 0; iEntryNum < TESTERPROFILE_NUMPROFILES_DEFAULT; iEntryNum++) + { + // will return an error if the entry is NULL or nuked + if (TesterProfileGetJson(pState, iEntryNum, strProfile, sizeof(strProfile)) >= 0) + { + // write it to the file + ZFileWrite(iFilePointer, (void *)strProfile, (int32_t)strlen(strProfile)); + } + } + + // close the file + ZFileClose(iFilePointer); + + // done + return(TESTERPROFILE_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileGet + + \Description + Return a specific profile index into a tagfield string buffer. + + \Input *pState - module state + \Input iIndex - 0-based index of the profile to get + \Input *pDest - destination buffer + + \Output int32_t - index number of retreived entry (>=0) , error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileGet(TesterProfileT *pState, int32_t iIndex, TesterProfileEntryT *pDest) +{ + TesterProfileEntryT *pEntry; + + // see if we want the default entry + if (iIndex == -1) + iIndex = TesterProfileGetDefaultIndex(pState); + + // check for error conditions + if ((pState == NULL) || (pDest == NULL)) + return(TESTERPROFILE_ERROR_NULLPOINTER); + else if (iIndex < -1) + return(TESTERPROFILE_ERROR_NOSUCHENTRY); + + // set the entry pointer + pEntry = &pState->Profiles[iIndex]; + + // if the entry has been nuked, don't do anything + if(pEntry->strProfileName[0] == 0) + return(TESTERPROFILE_ERROR_NOSUCHENTRY); + + // copy the data in + ds_memcpy(pDest, pEntry, sizeof(TesterProfileEntryT)); + + // done + return(iIndex); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileGetJson + + \Description + Return a specific profile index into a json string buffer. + + \Input *pState - module state + \Input iIndex - 0-based index of the profile to get + \Input *pDest - destination buffer + \Input iSize - size of the destination buffer + + \Output int32_t - index number of retreived entry (>=0) , error code otherwise + + \Version 03/29/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileGetJson(TesterProfileT *pState, int32_t iIndex, char *pDest, int32_t iSize) +{ + TesterProfileEntryT Entry; + int32_t iResult; + + if (pState == NULL) + return(TESTERPROFILE_ERROR_NULLPOINTER); + + iResult = TesterProfileGet(pState, iIndex, &Entry); + if (iResult < 0) + return(iResult); + + // otherwise dump the json into the buffer + JsonInit(pDest, iSize, 0); + JsonAddStr(pDest, TESTERPROFILE_PROFILENAME_TAGVALUE, Entry.strProfileName); + JsonAddStr(pDest, TESTERPROFILE_HOSTNAME_TAGVALUE, Entry.strHostname); + JsonAddStr(pDest, TESTERPROFILE_CONTROLDIR_TAGVALUE, Entry.strControlDirectory); + JsonAddStr(pDest, TESTERPROFILE_COMMANDLINE_TAGVALUE, Entry.strCommandLine); + JsonAddStr(pDest, TESTERPROFILE_LOGDIRECTORY_TAGVALUE, Entry.strLogDirectory); + JsonAddStr(pDest, TESTERPROFILE_HISTORYFILE_TAGVALUE, Entry.strHistoryFile); + JsonAddInt(pDest, TESTERPROFILE_LOGENABLE_TAGVALUE, Entry.uLogEnable); + JsonAddInt(pDest, TESTERPROFILE_NETWORKSTART_TAGVALUE, Entry.uNetworkStartup); + JsonAddInt(pDest, TESTERPROFILE_DEFAULTPROFILE_TAGVALUE, Entry.uDefault); + pDest = JsonFinish(pDest); + + // done + return(iIndex); +} + + +/*F********************************************************************************/ +/*! + \Function TesterProfileDisconnect + + \Description + Disconnect from a set of profiles and save the current profiles. + + \Input *pState - module state + + \Output int32_t - result of trying to save the profiles (TesterProfileSave()) + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterProfileDisconnect(TesterProfileT *pState) +{ + int32_t iResult; + + // check for valid state - not really an error + if (pState == NULL) + return(TESTERPROFILE_ERROR_NONE); + // check to see if we're already disconnected + if (pState->strFilename[0] == 0) + return(TESTERPROFILE_ERROR_NONE); + + // make a best effort at saving the file + iResult = TesterProfileSave(pState); + if (iResult) + { + ZPrintf("testerprofile: TesterProfileDisconnect: error saving file [%s]\n", + pState->strFilename); + } + + // kill all the entries in the profile list + _TesterProfileKillAllEntries(pState); + + // wipe out the filename to signify we've disconnected + ds_memclr(pState->strFilename, sizeof(pState->strFilename)); + + // return the fclose error code + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function TesterProfileDestroy + + \Description + Destroy and allocated tester profile object. + + \Input *pState - module state + + \Output None + + \Version 03/18/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterProfileDestroy(TesterProfileT *pState) +{ + if(pState) + { + TesterProfileDisconnect(pState); + ZMemFree(pState); + } + TesterRegistrySetPointer("PROFILE", NULL); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerprofile.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerprofile.h new file mode 100644 index 00000000..c9a3a588 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerprofile.h @@ -0,0 +1,142 @@ +/*H********************************************************************************/ +/*! + \File testerprofile.h + + \Description + Maintain user profiles for the tester application. + + \Notes + A profile is selected, created, or deleted after client GUI start + and before anything else happens. Currently, the following items + will be stored in a tester2 profile: command history, network startup, + and lobby parameters. Profiles will be stored in a single file on the + clients disk, the elements of the profile encoded into tagfields. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/18/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _testerprofile_h +#define _testerprofile_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +#define TESTERPROFILE_ERROR_NONE (0) //!< no error +#define TESTERPROFILE_ERROR_NULLPOINTER (-1) //!< invalid pointer used +#define TESTERPROFILE_ERROR_INVALIDFILENAME (-2) //!< bad filename +#define TESTERPROFILE_ERROR_FILEALREADYOPEN (-3) //!< file already opened +#define TESTERPROFILE_ERROR_FILEOPEN (-4) //!< could not open requested file +#define TESTERPROFILE_ERROR_NOSUCHENTRY (-5) //!< entry not found + +#define TESTERPROFILE_NUMPROFILES_DEFAULT (16) //!< max number of profiles to keep around + +#define TESTERPROFILE_PROFILEFILENAME_SIZEDEFAULT (256) //!< size of the profile filename string +#define TESTERPROFILE_PROFILEFILENAME_VALUEDEFAULT (".\\profile.txt") //!< profile filename + +#define TESTERPROFILE_PROFILENAME_SIZEDEFAULT (32) //!< size of each profile name +#define TESTERPROFILE_PROFILENAME_VALUEDEFAULT ("") //!< default name for the profile if none specified +#define TESTERPROFILE_PROFILENAME_TAGVALUE ("NAME") //!< the tag the data will be stored under + +#define TESTERPROFILE_CONTROLDIR_SIZEDEFAULT (256) //!< size of the directory entry +#define TESTERPROFILE_CONTROLDIR_VALUEDEFAULT (".\\") //!< default file sharing directory +#define TESTERPROFILE_CONTROLDIR_TAGVALUE ("CONTROLDIR") //!< the tag the data will be stored under + +#define TESTERPROFILE_COMMANDLINE_SIZEDEFAULT (256) //!< size of the optional command line params +#define TESTERPROFILE_COMMANDLINE_VALUEDEFAULT ("") //!< default command line params +#define TESTERPROFILE_COMMANDLINE_TAGVALUE ("COMMANDLINE") //!< the tag the data will be stored under + +#define TESTERPROFILE_LOGDIRECTORY_SIZEDEFAULT (256) //!< size of the file log location +#define TESTERPROFILE_LOGDIRECTORY_VALUEDEFAULT (".\\") //!< location to create a logfile at +#define TESTERPROFILE_LOGDIRECTORY_TAGVALUE ("LOGDIR") //!< the tag the data will be stored under + +#define TESTERPROFILE_HOSTNAME_SIZEDEFAULT (32) //!< size of the hostname +#define TESTERPROFILE_HOSTNAME_VALUEDEFAULT ("") //!< default hostname +#define TESTERPROFILE_HOSTNAME_TAGVALUE ("HOSTNAME") //!< the tag the data will be stored under + +#define TESTERPROFILE_LOGENABLE_VALUEDEFAULT (1) //!< log to a file by default +#define TESTERPROFILE_LOGENABLE_TAGVALUE ("LOGENABLE") //!< the tag the data will be stored under + +#define TESTERPROFILE_NETWORKSTART_VALUEDEFAULT (1) //!< bring up the network by default +#define TESTERPROFILE_NETWORKSTART_TAGVALUE ("NETSTART") //!< the tag the data will be stored under + +#define TESTERPROFILE_DEFAULTPROFILE_TAGVALUE ("DEFAULT") //!< the tag the data will be stored under + +#define TESTERPROFILE_HISTORYFILE_SIZEDEFAULT (288) //!< size of the history filename +#define TESTERPROFILE_HISTORYFILE_VALUEDEFAULT ("") //!< default value +#define TESTERPROFILE_HISTORYFILE_TAGVALUE ("HISTORYFILE") //!< tag the data will be stored under + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// module state container +typedef struct TesterProfileT TesterProfileT; + +// here's the actual profile data structure +typedef struct TesterProfileEntryT +{ + char strProfileName[TESTERPROFILE_PROFILENAME_SIZEDEFAULT]; //!< profile name + char strControlDirectory[TESTERPROFILE_CONTROLDIR_SIZEDEFAULT]; //!< control directory name + char strCommandLine[TESTERPROFILE_COMMANDLINE_SIZEDEFAULT]; //!< command line options + char strLogDirectory[TESTERPROFILE_LOGDIRECTORY_SIZEDEFAULT]; //!< logfile directory + char strHostname[TESTERPROFILE_HOSTNAME_SIZEDEFAULT]; //!< hostname + char strHistoryFile[TESTERPROFILE_HISTORYFILE_SIZEDEFAULT]; //!< history filename + char strLogFileName[TESTERPROFILE_LOGDIRECTORY_SIZEDEFAULT]; //!< Log file name + unsigned char uLogEnable; //!< 1 to enable file logging + unsigned char uNetworkStartup; //!< 1 for network startup on connect + unsigned char uDefault; //!< 1 for the default profile, 0 otherwise + unsigned char uPad1; //!< pad variable +} TesterProfileEntryT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create a tester profile manager +TesterProfileT *TesterProfileCreate(void); + +// connect the profile manager to a set of profiles in the given profile file +int32_t TesterProfileConnect(TesterProfileT *pState, const char *pFilename); + +// return a specifically indexed profile into a json buffer +int32_t TesterProfileGet(TesterProfileT *pState, int32_t iIndex, TesterProfileEntryT *pDest); + +// return a specifically indexed profile into a json buffer +int32_t TesterProfileGetJson(TesterProfileT *pState, int32_t iIndex, char *pDest, int32_t iSize); + +// add a profile to the list of profiles +int32_t TesterProfileAdd(TesterProfileT *pState, TesterProfileEntryT *pEntry); + +// delete a profile from the list of profiles +int32_t TesterProfileDelete(TesterProfileT *pState, const char *pName); + +// set a particular profile as a default profile (loads first next time) +int32_t TesterProfileSetDefaultName(TesterProfileT *pState, const char *pProfileName); + +// get the index of the default profile +int32_t TesterProfileGetDefaultIndex(TesterProfileT *pState); + +// save the profile file (manual save - will happen automatically on disconnect) +int32_t TesterProfileSave(TesterProfileT *pState); + +// save the current profiles and disconnect the profile manager from the profile set +int32_t TesterProfileDisconnect(TesterProfileT *pState); + +// destroy a tester profile object +void TesterProfileDestroy(TesterProfileT *pState); + +#ifdef __cplusplus +}; +#endif + +#endif // _testerprofile_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerregistry.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerregistry.c new file mode 100644 index 00000000..0878e098 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerregistry.c @@ -0,0 +1,459 @@ +/*H********************************************************************************/ +/*! + \File testerregistry.h + + \Description + Maintains a global registry for the tester2 application. + + \Notes + This module works a little differently than other DirtySock modules. + Because of the nature of a registry (a global collector of shared + information) there is no pointer to pass around - Create simply + allocates memory of a given size and Destroy frees this memory. + Registry entries are stored in tagfield format. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/08/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/dirtysock.h" +#include "libsample/zmem.h" +#include "libsample/zlib.h" +#include "testerregistry.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct TesterRegistryEntryT +{ + uintptr_t uPtr; + int32_t iNum; + + char strName[256]; + char strBuffer[1024]; +} TesterRegistryEntryT; + +// registry module structure - private and not intended for external use +typedef struct TesterRegistryT +{ + int32_t iMaxEntries; //!< maximum number of entries supported + int32_t iNumEntries; //!< current number of entries + TesterRegistryEntryT pReg[1]; //!< the actual registry (variable size array) +} TesterRegistryT; + +/*** Variables ********************************************************************/ + +static TesterRegistryT *_TesterRegistry = NULL; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _TesterRegistryFindEntry + + \Description + Looks up a registry entry by name + + \Input *pEntryName - name of entry we are looking for + + \Output + TesterRegistryEntryT * - NULL or entry if found + + \Version 01/11/2017 (eesponda) +*/ +/********************************************************************************F*/ +static TesterRegistryEntryT *_TesterRegistryFindEntry(const char *pEntryName) +{ + TesterRegistryEntryT *pEntry = NULL; + int32_t iIndex; + + for (iIndex = 0; iIndex < _TesterRegistry->iNumEntries; iIndex += 1) + { + if (strcmp(pEntryName, _TesterRegistry->pReg[iIndex].strName) == 0) + { + pEntry = &_TesterRegistry->pReg[iIndex]; + break; + } + } + + return(pEntry); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function TesterRegistryCreate + + \Description + Create a new registry of a given size. + + \Input iSize - Size of registry, in bytes, to create. (<0) for default size + + \Output None + + \Notes + Calling this function creates a global (static, private) registry which + any module can access. If TesterRegistryCreate is called multiple times, + the old registry is blown away (if it existed) the memory is freed, and + a new registry is created. No memory will be leaked if TesterRegistryCreate + is called multiple times. + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterRegistryCreate(int32_t iSize) +{ + int32_t iMemSize; + + // dump the old registry if it exists + if(_TesterRegistry != NULL) + TesterRegistryDestroy(); + + if(iSize < 0) + { + // use default size + iSize = TESTERREGISTRY_SIZE_DEFAULT; + } + iMemSize = sizeof(*_TesterRegistry) - sizeof(_TesterRegistry->pReg) + (sizeof(_TesterRegistry->pReg) * iSize); + + // now create a new registry + _TesterRegistry = ZMemAlloc(iMemSize); + ds_memclr(_TesterRegistry, iMemSize); + _TesterRegistry->iMaxEntries = iSize; +} + +/*F********************************************************************************/ +/*! + \Function TesterRegistrySetPointer + + \Description + Set a registry entry for a pointer + + \Input *pEntryName - the registry entry name + \Input *pPtr - the pointer to save + + \Output int32_t - 0=success, error code otherwise + + \Notes + Reference count is automatically set to 1. To increment reference count + use TesterRegistryGetPointer. To decrement reference count use + TesterRegistryDecrementRefCount. + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterRegistrySetPointer(const char *pEntryName, const void *pPtr) +{ + TesterRegistryEntryT *pEntry; + + // check for errors + if(_TesterRegistry == NULL) + { + ZPrintf("WARNING: TesterRegistry used before Create.\n"); + return(TESTERREGISTRY_ERROR_NOTINITIALIZED); + } + if(pEntryName == NULL) + { + ZPrintf("WARNING: TesterRegistrySet got NULL pointer for entry name\n"); + return(TESTERREGISTRY_ERROR_BADDATA); + } + + /* find the entry if it exists in the list + otherwise add a new entry to the list + note: we don't remove entries just to simplify */ + + if ((pEntry = _TesterRegistryFindEntry(pEntryName)) == NULL) + { + pEntry = &_TesterRegistry->pReg[_TesterRegistry->iNumEntries]; + ds_strnzcpy(pEntry->strName, pEntryName, sizeof(pEntry->strName)); + _TesterRegistry->iNumEntries += 1; + } + + pEntry->uPtr = (uintptr_t)pPtr; + return(TESTERREGISTRY_ERROR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function TesterRegistrySetNumber + + \Description + Set a registry entry for a number + + \Input *pEntryName - the registry entry name + \Input iNum - the number to save + + \Output int32_t - 0=success, error code otherwise + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterRegistrySetNumber(const char *pEntryName, const int32_t iNum) +{ + TesterRegistryEntryT *pEntry; + + // check for errors + if(_TesterRegistry == NULL) + { + ZPrintf("WARNING: TesterRegistry used before Create.\n"); + return(TESTERREGISTRY_ERROR_NOTINITIALIZED); + } + if(pEntryName == NULL) + { + ZPrintf("WARNING: TesterRegistrySet got NULL pointer for entry name\n"); + return(TESTERREGISTRY_ERROR_BADDATA); + } + + /* find the entry if it exists in the list + otherwise add a new entry to the list + note: we don't remove entries just to simplify */ + + if ((pEntry = _TesterRegistryFindEntry(pEntryName)) == NULL) + { + pEntry = &_TesterRegistry->pReg[_TesterRegistry->iNumEntries]; + ds_strnzcpy(pEntry->strName, pEntryName, sizeof(pEntry->strName)); + _TesterRegistry->iNumEntries += 1; + } + + pEntry->iNum = iNum; + return(TESTERREGISTRY_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterRegistrySetString + + \Description + Set a registry entry for a pointer + + \Input *pEntryName - the registry entry name + \Input *pStr - the string to save + + \Output int32_t - 0=success, error code otherwise + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterRegistrySetString(const char *pEntryName, const char *pStr) +{ + TesterRegistryEntryT *pEntry; + + // check for errors + if(_TesterRegistry == NULL) + { + ZPrintf("WARNING: TesterRegistry used before Create.\n"); + return(TESTERREGISTRY_ERROR_NOTINITIALIZED); + } + if((pEntryName == NULL) || (pStr == NULL)) + { + ZPrintf("WARNING: TesterRegistrySet got NULL pointer for entry name or entry value\n"); + return(TESTERREGISTRY_ERROR_BADDATA); + } + + /* find the entry if it exists in the list + otherwise add a new entry to the list + note: we don't remove entries just to simplify */ + + if ((pEntry = _TesterRegistryFindEntry(pEntryName)) == NULL) + { + pEntry = &_TesterRegistry->pReg[_TesterRegistry->iNumEntries]; + ds_strnzcpy(pEntry->strName, pEntryName, sizeof(pEntry->strName)); + _TesterRegistry->iNumEntries += 1; + } + + ds_strnzcpy(pEntry->strBuffer, pStr, sizeof(pEntry->strBuffer)); + return(TESTERREGISTRY_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterRegistryGetPointer + + \Description + Get a pointer entry from the registry + + \Input *pEntryName - the registry entry name + + \Output + void * - requested pointer, or NULL if no pointer is set + + \Notes + Reference count is incremented when Get is called. To decrement + reference count use TesterRegsitryDecrementRefCount. + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +void *TesterRegistryGetPointer(const char *pEntryName) +{ + TesterRegistryEntryT *pEntry; + + // check for errors + if (_TesterRegistry == NULL) + { + ZPrintf("WARNING: TesterRegistry used before Create.\n"); + return(NULL); + } + + if ((pEntry = _TesterRegistryFindEntry(pEntryName)) == NULL) + { + // no entry found + return(NULL); + } + + return((void *)pEntry->uPtr); +} + +/*F********************************************************************************/ +/*! + \Function TesterRegistryGetNumber + + \Description + Get a numeric entry from the registry + + \Input *pEntryName - the registry entry name + \Input *pNum - destination integer + + \Output int32_t - 0=success, error code otherwise + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterRegistryGetNumber(const char *pEntryName, int32_t *pNum) +{ + TesterRegistryEntryT *pEntry; + + // check for errors + if(_TesterRegistry == NULL) + { + ZPrintf("WARNING: TesterRegistry used before Create.\n"); + return(TESTERREGISTRY_ERROR_NOTINITIALIZED); + } + if((pEntryName == NULL) || (pNum == NULL)) + { + ZPrintf("WARNING: TesterRegistryGet got NULL pointer for entry name\n"); + return(TESTERREGISTRY_ERROR_BADDATA); + } + + if ((pEntry = _TesterRegistryFindEntry(pEntryName)) == NULL) + { + // no entry found + *pNum = 0; + return(TESTERREGISTRY_ERROR_NOSUCHENTRY); + } + + *pNum = pEntry->iNum; + return(TESTERREGISTRY_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterRegistryGetString + + \Description + Get a string entry from the registry + + \Input *pEntryName - the registry entry name + \Input *pBuf - destination string + \Input iBufSize - size of the destination string + + \Output int32_t - 0=success, error code otherwise + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterRegistryGetString(const char *pEntryName, char *pBuf, int32_t iBufSize) +{ + TesterRegistryEntryT *pEntry; + + // check for errors + if(_TesterRegistry == NULL) + { + ZPrintf("WARNING: TesterRegistry used before Create.\n"); + return(TESTERREGISTRY_ERROR_NOTINITIALIZED); + } + if((pEntryName == NULL) || (pBuf == NULL) || (iBufSize == 0)) + { + ZPrintf("WARNING: TesterRegistryGet got NULL pointer for entry name\n"); + return(TESTERREGISTRY_ERROR_BADDATA); + } + + ds_memclr(pBuf, iBufSize); + if ((pEntry = _TesterRegistryFindEntry(pEntryName)) == NULL) + { + // no entry found + return(TESTERREGISTRY_ERROR_NOSUCHENTRY); + } + + ds_strnzcpy(pBuf, pEntry->strBuffer, iBufSize-1); + return(TESTERREGISTRY_ERROR_NONE); +} + + +/*F********************************************************************************/ +/*! + \Function TesterRegistryPrint + + \Description + Print all the members of the registry to the console + + \Input None + + \Output None + + \Version 04/11/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterRegistryPrint(void) +{ + if(_TesterRegistry->iNumEntries == 0) + { + ZPrintf("REGISTRY: SIZE=%d {}\n",_TesterRegistry->iMaxEntries); + } + else + { + int32_t iEntryIndex; + + ZPrintf("REGISTRY: SIZE=%d :\n",_TesterRegistry->iMaxEntries); + for (iEntryIndex = 0; iEntryIndex < _TesterRegistry->iNumEntries; iEntryIndex += 1) + { + const TesterRegistryEntryT *pEntry = &_TesterRegistry->pReg[iEntryIndex]; + ZPrintf("REGISTRY: {name: %s, ptr: 0x%08x, num: %d, str: %s}\n", + pEntry->strName, pEntry->uPtr, pEntry->iNum, pEntry->strBuffer); + } + } +} + +/*F********************************************************************************/ +/*! + \Function TesterRegistryDestroy + + \Description + Destroy the registry and free all associated memory. + + \Input None + + \Output None + + \Version 04/08/2005 (jfrank) +*/ +/********************************************************************************F*/ +void TesterRegistryDestroy(void) +{ + ZMemFree(_TesterRegistry); + _TesterRegistry = NULL; +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerregistry.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerregistry.h new file mode 100644 index 00000000..87f29176 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testerregistry.h @@ -0,0 +1,78 @@ +/*H********************************************************************************/ +/*! + \File testerregistry.h + + \Description + Maintains a global registry for the tester2 application. + + \Notes + This module works a little differently than other DirtySock modules. + Because of the nature of a registry (a global collector of shared + information) there is no pointer to pass around - Create simply + allocates memory of a given size and Destroy frees this memory. + Registry entries are stored in tagfield format. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/08/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +#ifndef _testerregistry_h +#define _testerregistry_h + +/*** Include files ****************************************************************/ + +#define TESTERREGISTRY_ERROR_NONE (0) //!< no error +#define TESTERREGISTRY_ERROR_NOTINITIALIZED (-1) //!< create not called +#define TESTERREGISTRY_ERROR_NOSUCHENTRY (-2) //!< entry requested does not exist +#define TESTERREGISTRY_ERROR_OUTOFSPACE (-3) //!< no more room left in the registry +#define TESTERREGISTRY_ERROR_BADDATA (-4) //!< bad data passed to a function + +/*** Defines **********************************************************************/ + +#define TESTERREGISTRY_SIZE_DEFAULT (4096) //!< default registry size + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// pass in a registry size, or (-1) for default size, allocates that much memory. +// calling create multiple times frees the previous registry (no leak) +void TesterRegistryCreate(int32_t iSize); + +// set a registry entry - pointer +int32_t TesterRegistrySetPointer(const char *pEntryName, const void *pPtr); +// set a registry entry - number +int32_t TesterRegistrySetNumber(const char *pEntryName, const int32_t iNum); +// set a registry entry - string +int32_t TesterRegistrySetString(const char *pEntryName, const char *pStr); + +// get a registry entry - pointer - return error code (0 for none) +void *TesterRegistryGetPointer(const char *pEntryName); +// get a registry entry - pointer - return error code (0 for none) +int32_t TesterRegistryGetNumber(const char *pEntryName, int32_t *pNum); +// get a registry entry - pointer - return error code (0 for none) +int32_t TesterRegistryGetString(const char *pEntryName, char *pBuf, int32_t iBufSize); + +// print the registry +void TesterRegistryPrint(void); + +// destroy a registry +void TesterRegistryDestroy(void); + +#ifdef __cplusplus +}; +#endif + +#endif // _testerregistry_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testersubcmd.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testersubcmd.c new file mode 100644 index 00000000..315f403c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testersubcmd.c @@ -0,0 +1,116 @@ +/*H********************************************************************************/ +/*! + \File .c + + \Description + Helper functions for modules to implement sub-commands. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 05/10/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "libsample/zlib.h" +#include "testersubcmd.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function T2SubCmdUsage + + \Description + Display basic usage for the given subcommand. + + \Input *pCmdName - name of command + \Input *pCmdList - list of subcommands + + \Output + None. + + \Version 05/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void T2SubCmdUsage(const char *pCmdName, T2SubCmdT *pCmdList) +{ + char strUsage[1024] = "help|"; + int32_t iCmd; + + for (iCmd = 0; pCmdList[iCmd].pFunc != NULL; iCmd++) + { + strcat(strUsage, pCmdList[iCmd].strName); + if (pCmdList[iCmd+1].pFunc != NULL) + { + strcat(strUsage, "|"); + } + } + ZPrintf(" usage: %s [%s]\n", pCmdName, strUsage); +} + +/*F********************************************************************************/ +/*! + \Function T2SubCmdParse + + \Description + Parse commandline for subcommand, and return matching command or NULL + if not found. + + \Input *pCmdList - pointer to command list to match against + \Input argc - argument count + \Input *argv[] - argument list + \Input *pHelp - [out] storage for whether help is requested or not + + \Output + T2SubCmdT * - pointer to parsed subcommand, or NULL if no match + + \Version 05/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +T2SubCmdT *T2SubCmdParse(T2SubCmdT *pCmdList, int32_t argc, char *argv[], unsigned char *pHelp) +{ + T2SubCmdT *pCmd = NULL; + int32_t iArgIdx; + + if (argc > 1) + { + iArgIdx = (!strcmp(argv[1], "help")) ? 2 : 1; + + if (iArgIdx < argc) + { + *pHelp = (iArgIdx == 2) ? 1 : 0; + + for (pCmd = pCmdList; pCmd->pFunc != NULL; pCmd++) + { + if (!strcmp(argv[iArgIdx], pCmd->strName)) + { + break; + } + } + + if (pCmd->pFunc == NULL) + { + pCmd = NULL; + } + } + } + + return(pCmd); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/testersubcmd.h b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testersubcmd.h new file mode 100644 index 00000000..94d5290a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/testersubcmd.h @@ -0,0 +1,55 @@ +/*H********************************************************************************/ +/*! + \File testersubcmd.h + + \Description + Helper functions for modules to implement sub-commands. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 05/10/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _testersubcmd_h +#define _testersubcmd_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! subfunction command function prototype +typedef void (T2SubFuncT)(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp); + +//! subfunction command description +typedef struct T2SubCmdT +{ + char strName[16]; + T2SubFuncT *pFunc; +} T2SubCmdT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +//! display list of subcommands +void T2SubCmdUsage(const char *pCmdName, T2SubCmdT *pCmdList); + +//! parse commandline to resolve subcommand +T2SubCmdT *T2SubCmdParse(T2SubCmdT *pCmdList, int32_t argc, char *argv[], unsigned char *pHelp); + +#ifdef __cplusplus +}; +#endif + +#endif // _testersubcmd_h + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/T2Host.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/T2Host.c new file mode 100644 index 00000000..516ce2e0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/T2Host.c @@ -0,0 +1,326 @@ +/*H*************************************************************************************/ +/*! + + \File T2Host.c + + \Description + Tester2 Host Application Framework + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 03/22/2005 (jfrank) First Version +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/util/jsonformat.h" + +#include "libsample/zlib.h" +#include "libsample/zmemtrack.h" + +#include "testerhostcore.h" +#include "testercomm.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// tester host module +TesterHostCoreT *g_pHostCore; + +static volatile uint8_t _bContinue = TRUE; + +/*** Private Functions *****************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _T2HostDisplayOutput + + \Description + Take input from TesterConsole and display it somewhere + + \Input *pBuf - string containing the debug output to display + \Input iLen - length of buffer + \Input iRefcon - user-specified parameter (unused) + \Input *pRefptr - user-specified parameter (window pointer) + + \Output None + + \Version 04/13/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2HostDisplayOutput(const char *pBuf, int32_t iLen, int32_t iRefcon, void *pRefptr) +{ + //printf("%s",pBuf); +} + + +/*F********************************************************************************/ +/*! + \Function _T2HostCommandlineProcess + + \Description + Clear the console + + \Input *argz - environment + \Input argc - num args + \Input **argv - arg list + + \Output int32_t - standard return code + + \Version 04/07/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _TestHostProcessInput(char *pCommandLine, int32_t iCommandLen, int32_t *pCommandOff) +{ + struct pollfd PollFd; + int32_t iResult; + char cInput = 0; + + // poll for input, blocking up to 20ms + PollFd.fd = 0; + PollFd.events = POLLIN; + if ((iResult = poll(&PollFd, 1, 20)) < 0) + { + NetPrintf(("t2host: poll() error %d trying to get input from stdin\n", errno)); + } + + // did we get any input? + if (PollFd.revents & POLLIN) + { + if ((iResult = (int32_t)read(0, &cInput, sizeof(cInput))) > 0) + { + pCommandLine[*pCommandOff] = cInput; + *pCommandOff += 1; + } + else if (iResult < 0) + { + NetPrintf(("t2host: read() error %d trying to read input from stdin\n", errno)); + } + } + + // no update + return(cInput == '\n'); +} + +/*F********************************************************************************/ +/*! + \Function _T2HostCmdClear + + \Description + Clear the console + + \Input *argz - environment + \Input argc - num args + \Input **argv - arg list + + \Output int32_t - standard return code + + \Version 04/07/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _T2HostCmdClear(ZContext *argz, int32_t argc, char **argv) +{ + if (argc < 1) + { + ZPrintf(" clear the display.\n"); + ZPrintf(" usage: %s\n", argv[0]); + } + else + { +#if 0 + // clear the console + //TesterConsoleT *pConsole; + if ((pConsole = (TesterConsoleT *)TesterRegistryGetPointer("CONSOLE")) != NULL) + { + TesterConsoleClear(pConsole); + } +#endif + } + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function _T2HostCmdExit + + \Description + Quit + + \Input *argz - environment + \Input argc - num args + \Input **argv - arg list + + \Output int32_t - standard return code + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +static int32_t _T2HostCmdExit(ZContext *argz, int32_t argc, char **argv) +{ + _bContinue = FALSE; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _T2HostRegisterModules + + \Description + Register client commands (local commands, like exit, history, etc.) + + \Input None + + \Output None + + \Version 04/05/2005 (jfrank) +*/ +/********************************************************************************F*/ +static void _T2HostRegisterModules(void) +{ + TesterHostCoreRegister(g_pHostCore, "exit", &_T2HostCmdExit); + TesterHostCoreRegister(g_pHostCore, "clear", &_T2HostCmdClear); +} + + +/*** Public Functions ******************************************************************/ + +int main(int32_t argc, char *argv[]) +{ + char strParams[512], strCommandLine[256] = "", strBase[256] = "", strHostName[128] = ""; + int32_t iArg, iCommandOff = 0; + uint8_t bInteractive = TRUE; + char strStartupParams[256] = "-servicename=tester2"; + + // start the memtracker before we do anything else + ZMemtrackStartup(); + + ZPrintf(("\nStarting T2Host.\n\n")); + + // get arguments + for (iArg = 0; iArg < argc; iArg++) + { + if (!strncmp(argv[iArg], "-path=", 6)) + { + ds_strnzcpy(strBase, &argv[iArg][6], sizeof(strBase)); + ZPrintf("t2host: base path=%s\n", strBase); + } + if (!strncmp(argv[iArg], "-connect=", 9)) + { + ds_strnzcpy(strHostName, &argv[iArg][9], sizeof(strHostName)); + ZPrintf("t2host: connect=%s\n", strHostName); + } + if (!strncmp(argv[iArg], "-notty", 6)) + { + bInteractive = FALSE; + ZPrintf("t2host: notty mode enabled\n"); + } + if (!strncmp(argv[iArg], "-singlethread", 13)) + { + ds_strnzcat(strStartupParams, " -singlethreaded", sizeof(strStartupParams)); + ZPrintf("t2host: single-threaded mode enabled\n"); + } + } + + // create the module + JsonInit(strParams, sizeof(strParams), 0); + JsonAddStr(strParams, "INPUTFILE", TESTERCOMM_HOSTINPUTFILE); + JsonAddStr(strParams, "OUTPUTFILE", TESTERCOMM_HOSTOUTPUTFILE); + JsonAddStr(strParams, "CONTROLDIR", strBase); + JsonAddStr(strParams, "HOSTNAME", strHostName); + + // startup dirtysdk (must come before TesterHostCoreCreate() if we are using socket comm) + NetConnStartup(strStartupParams); + + NetPrintf(("t2host: ipaddr=%a\n", NetConnStatus('addr', 0, NULL, 0))); + + NetPrintf(("t2host: macaddr=%s\n", NetConnMAC())); + + g_pHostCore = TesterHostCoreCreate(JsonFinish(strParams)); + + // define the function which will show stuff on the screen + TesterHostCoreDisplayFunc(g_pHostCore, _T2HostDisplayOutput, 0, NULL); + + // register basic functions + _T2HostRegisterModules(); + + ZPrintf("T2Host Unix Application Successfully started.\n"); + + // check for command-line command + for (iArg = 1; iArg < argc; iArg += 1) + { + // don't include -base or -connect arg, if present + if (!strncmp(argv[iArg], "-base=", 6) || !strncmp(argv[iArg], "-connect=", 9) || !strncmp(argv[iArg], "-notty", 6) || !strncmp(argv[iArg], "-singlethread", 13)) + { + continue; + } + // add to command-line + ds_strnzcat(strCommandLine, argv[iArg], sizeof(strCommandLine)); + ds_strnzcat(strCommandLine, " ", sizeof(strCommandLine)); + } + ZPrintf("> %s\n", strCommandLine); + TesterHostCoreDispatch(g_pHostCore, strCommandLine); + strCommandLine[0] = '\0'; + + while(_bContinue) + { + // check for input + if (bInteractive && _TestHostProcessInput(strCommandLine, sizeof(strCommandLine), &iCommandOff)) + { + strCommandLine[iCommandOff-1] = '\0'; // remove lf + TesterHostCoreDispatch(g_pHostCore, strCommandLine); + strCommandLine[0] = '\0'; + iCommandOff = 0; + } + + // pump the host core module + TesterHostCoreUpdate(g_pHostCore, 1); + + // give time to zlib + ZTask(); + ZCleanup(); + + // give time to network + NetConnIdle(); + + // sleep for a short while + fflush(stdout); + ZSleep(20); + } + + // code is unreachable for now + TesterHostCoreDisconnect(g_pHostCore); + TesterHostCoreDestroy(g_pHostCore); + + // shut down the network + NetConnShutdown(0); + + ZMemtrackShutdown(); + + ZPrintf("\nQuitting T2Host.\n\n"); + + return(0); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/t2new.cpp b/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/t2new.cpp new file mode 100644 index 00000000..375ec656 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/t2new.cpp @@ -0,0 +1,28 @@ +/*H*************************************************************************************/ +/*! + + \File t2new.cpp + + \Description + This implements code in support of memory allocation in C++ code + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. + + \Version 12/01/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include + +void* operator new[](size_t size, unsigned long, unsigned long, char const*, int, unsigned int, char const*, int) +{ + return operator new(size); +} +void* operator new[](size_t size, char const*, int, unsigned int, char const*, int) +{ + return operator new(size); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/testermoduleshost.c b/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/testermoduleshost.c new file mode 100644 index 00000000..cf751ff1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/source/unix/testermoduleshost.c @@ -0,0 +1,54 @@ +/*H********************************************************************************/ +/*! + \File testermoduleshost.c + + \Description + Unix-specific module startup. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 04/13/2005 (jfrank) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +#include "testermodules.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function TesterModulesRegisterAllCommands + + \Description + Register all modules for the Unix platform + + \Input *pState - module state + + \Output 0=success, error code otherwise + + \Version 04/13/2005 (jfrank) +*/ +/********************************************************************************F*/ +int32_t TesterModulesRegisterPlatformCommands(TesterModulesT *pState) +{ + #if defined(DIRTYCODE_STADIA) + TesterModulesRegister(pState, "voice", &CmdVoice); + #endif + + // tester2 linux-specific modules + return(TESTERMODULES_ERROR_NONE); +} + diff --git a/r5dev/thirdparty/dirtysdk/sample/tester2/tester2_todo.html b/r5dev/thirdparty/dirtysdk/sample/tester2/tester2_todo.html new file mode 100644 index 00000000..e5db9e32 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/tester2/tester2_todo.html @@ -0,0 +1,200 @@ + + +
+
+

Tester2: To-Do/Feature Task List

+ (in order of attention)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SummaryDescriptionStatusTime Estimate
(days)
Actual Time Spent
(days)
Subcommands for NETMake the "net" command use the subcommand architectureactive20
PS2 LoginImplement network config chooser for the "net login" command in T2Host.active80
Xbox LoginMake the Xbox "net login" command do all the xbox live login stuff.active80
PSP DSDemo FrameworkCreate basic PSP DSDemo framework. Do not fill in completely - just use as needed.idle20
PSP dynamic module loadingBuild a module loader for PSP - like the PS2 IRX loader - to dynamically load PRXesidle80
Delete Stale Commands on StartupOn startup, both the host and client should delete any data already present in the buffer (file)idle.250
'-' command prefixUsing the '-' prefix to a command ("-help") clears the debug output and then executes the command.idle.250
Script on StartupAdd the ability to run a script on startupidle.250
Differentiate host and client console outputColor-code or otherwise mark differently data which is coming from the host and data which is coming from the client.idle.50
Client CleanupAdd "net stop" printf and make sure all other states properly printf as well.idle???0
Client CleanupDebug the clear command for both the host and client.idle???0
Separate out Debug Output(from James)
+ The debug output can be overwhelming and it might be nice to have it separated + out (optionally?). Alternative ideas might be the ability to hide/show debug + output (as a toggle) or have a different coloration for it.
idle10
File LoggingAdd the ability to log debug output to a file.idle.50
PING Command FixDirtysock is leaking memory from the allocation at line 1618 in SocketLookup, + where the HostentT structure is allocated. This is being called by ProtoNameAsync + inside the ping command. The ping module probably isn't disposing of the + host structure in all casesidle.10
Help CommandThe help command by itself should only list all available commands. + Additional help can then be accessed by typing "help ". + The current help model comes from the original tester application and + should be updated to reflect this design.idle.250
Concurrent Multi-user profilesFix the profile module to handle the case where multiple Tester2 clients can be open at once and write to the profile file safely.idle40
+


+
+

Tester2: Done/Suspended Tasks

+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SummaryDescriptionStatusTime Estimate
(days)
Actual Time Spent
(days)
Window ResizingCurrently, both windows are fixed in size. This matches the way the original + Tester application worked, but it would be nice to be able to resize the + window. Additionally add a horizontal scroll bar.DONE.25.5
Client CleanupFind out why the client is starting dirtysock and kill it if possible.DONE.05.05
Xenon Investigation and DesignInvestigate Xenon debugger interface. Examine refactoring/extracting common + functionality from the hostclientcomm module and adapting it to the debug + interface.DONE.5.5
Save HistorySave historical commands and load them back in at startupDONE.250
Xenon DevelopmentImplement hostclientcomm module for Xenon.DONE  
Title BarMake the title bar text on the client show the profile name.DONE.1.1
+ + diff --git a/r5dev/thirdparty/dirtysdk/sample/ticker/source/pc/ticker.c b/r5dev/thirdparty/dirtysdk/sample/ticker/source/pc/ticker.c new file mode 100644 index 00000000..9200a358 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/sample/ticker/source/pc/ticker.c @@ -0,0 +1,228 @@ +/*H*************************************************************************************/ +/*! + \File ticker.c + + \Description + This application demonstrates simple use of DirtySock including starting up the + network stack and using ProtoHttp to hit the Yahoo stock ticker on a recurring + basis. + + \Copyright + Copyright (c) Electronic Arts 2016. + + \Version 1.0 12/10/2000 (gschaefer) Initial version + \Version 2.0 11/27/2002 (gschaefer) Revived from the dead + \Version 2.1 02/23/2003 (jbrookes) Rewrote to use demo lib + \Version 2.2 03/06/2003 (jbrookes) Updated to use netconn + \Version 3.0 10/05/2005 (jbrookes) PS3 version + \Version 3.1 11/19/2008 (mclouatre) Adding user data concept to mem group support + \Version 4.0 04/01/2016 (jbrookes) Ported from PS4 version +*/ +/*************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include +#include + +// dirtysock includes +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/misc/weblog.h" +#include "DirtySDK/proto/protohttp.h" + +/*** Defines ***************************************************************************/ + +#define TICKER_WEBLOG_ENABLED (DIRTYCODE_DEBUG && FALSE) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + + +// Public variables + + +/*** Public Functions ******************************************************************/ + +// dll-friendly DirtyMemAlloc +#if !defined(DIRTYCODE_DLL) +void *DirtyMemAlloc(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#else +void *DirtyMemAlloc2(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#endif +{ + return(malloc(iSize)); +} + +// dll-friendly DirtyMemFree +#if !defined(DIRTYCODE_DLL) +void DirtyMemFree(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#else +void DirtyMemFree2(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +#endif +{ + free(pMem); +} + +int main(int32_t argc, char *argv[]) +{ + int32_t iAddr, iLen, iStatus, iTimeout, iCount; + ProtoHttpRefT *pHttp; + char strBuffer[16*1024]; + #if TICKER_WEBLOG_ENABLED + WebLogRefT *pWebLog; + int32_t iVerbose = 1; + #endif + + #if defined(DIRTYCODE_DLL) + DirtyMemFuncSet(&DirtyMemAlloc2, &DirtyMemFree2); + #endif + + #if TICKER_WEBLOG_ENABLED + // create weblog module + pWebLog = WebLogCreate(8192); + #if 0 + // set to post to dev server + WebLogConfigure(pWebLog, "eggplant.online.ea.com:8001", NULL); + #endif + // set weblog protohttp debugging to a high level + WebLogControl(pWebLog, 'spam', iVerbose, 0, NULL); + // hook in to netprintf debug output + #if DIRTYCODE_LOGGING + NetPrintfHook(WebLogDebugHook, pWebLog); + #endif + // start logging + WebLogStart(pWebLog); + #endif + + // start network + NetConnStartup("-servicename=ticker"); + + // bring up the interface + NetConnConnect(NULL, NULL, 0); + + // wait for network interface activation + for (iTimeout = NetTick()+15*1000 ; ; ) + { + // update network + NetConnIdle(); + + // get current status + iStatus = NetConnStatus('conn', 0, NULL, 0); + if ((iStatus == '+onl') || ((iStatus >> 24) == '-')) + { + break; + } + + // check for timeout + if (iTimeout < (signed)NetTick()) + { + NetPrintf(("ticker: timeout waiting for interface activation\n")); + return(-1); + } + + // give time to other threads + NetConnSleep(500); + } + + // check result code + if ((iStatus = NetConnStatus('conn', 0, NULL, 0)) == '+onl') + { + NetPrintf(("ticker: interface active\n")); + } + else if ((iStatus >> 14) == '-') + { + NetPrintf(("ticker: error bringing up interface\n")); + return(-11); + } + + // broadband check (should return TRUE if broadband, else FALSE) + NetPrintf(("iftype=%d\n", NetConnStatus('type', 0, NULL, 0))); + NetPrintf(("broadband=%s\n", NetConnStatus('bbnd', 0, NULL, 0) ? "TRUE" : "FALSE")); + + // acquire and display address + iAddr = NetConnStatus('addr', 0, NULL, 0); + NetPrintf(("addr=%a\n", iAddr)); + + // display mac address + NetPrintf(("mac=%s\n", NetConnMAC())); + + // setup http module + pHttp = ProtoHttpCreate(4096); + + // just keep working + for ( iTimeout = NetTick()-1, iLen=-1, iCount = 0; iCount < 8; ) + { + // see if its time to query + if ((iTimeout != 0) && (NetTick() > (unsigned)iTimeout)) + { + ProtoHttpGet(pHttp, "http://quote.yahoo.com/d/quotes.csv?s=^DJI,^SPC,^IXIC,EA,SNE,YHOO,^AORD,^N225,^FTSE&f=sl1c1&e=.csv", FALSE); + iTimeout = NetTick()+30*1000; + iLen = 0; + iCount += 1; // count the attempt + } + + // update protohttp + ProtoHttpUpdate(pHttp); + + // read incoming data into buffer + if ((iLen == 0) || (iLen == PROTOHTTP_RECVWAIT)) + { + if ((iLen = ProtoHttpRecvAll(pHttp, strBuffer, sizeof(strBuffer)-1)) > 0) + { + // print response + NetPrintf(("ticker: received response\n")); + NetPrintf(("%s", strBuffer)); + // print to console + printf("%s", strBuffer); + } + } + + #if TICKER_WEBLOG_ENABLED + WebLogUpdate(pWebLog); + #endif + + NetConnIdle(); + } + + NetPrintf(("ticker: done\n")); + + // shut down HTTP + ProtoHttpDestroy(pHttp); + + #if TICKER_WEBLOG_ENABLED + // stop the logging + WebLogStop(pWebLog); + // give it five seconds to flush the remaining data and end the transaction gracefully + for (iCount = 0; iCount < (5*1000); iCount += 100) + { + NetConnIdle(); + WebLogUpdate(pWebLog); + NetConnSleep(100); + } + // unhook netprintf debug output *before* we destroy WebLog + #if DIRTYCODE_LOGGING + NetPrintfHook(NULL, NULL); + #endif + // destroy module + WebLogDestroy(pWebLog); + #endif + + // disconnect from the network + NetConnDisconnect(); + + // shutdown the network connections && destroy the dirtysock code + NetConnShutdown(FALSE); + return(0); +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/source/comm/commsrp.c b/r5dev/thirdparty/dirtysdk/source/comm/commsrp.c new file mode 100644 index 00000000..6aa4cbd7 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/comm/commsrp.c @@ -0,0 +1,1656 @@ +/*H*************************************************************************************************/ +/*! + + \File commsrp.c + + \Description + This is CommSRP (Selectively Reliable Protocol), a datagram packet-based + transport class. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 1999-2003. ALL RIGHTS RESERVED. + + \Version 0.5 01/03/03 (JLB) Initial Version, based on CommTCP + \Version 0.7 01/07/03 (JLB) Working unreliable transport, based on CommUDP + \Version 0.8 01/08/03 (JLB) Working reliable transport. + \Version 0.9 02/09/03 (JLB) Added support for sending zero-byte packets, and + fixed PS2 alignment issue. +*/ +/*************************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#define ESC_CAUSES_LOSS (0) + +#if ESC_CAUSES_LOSS +#include +#endif + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/comm/commall.h" +#include "DirtySDK/comm/commsrp.h" + +/*** Defines ***************************************************************************/ + +//! enable debug spam +#define COMMSRP_VERBOSE (COMM_PRINT && FALSE) + +//! control packet types +enum +{ + CTRL_PACKET_FIRST = 16, //!< 16: beginning of packet code range + + CTRL_PACKET_INIT = CTRL_PACKET_FIRST, //!< 16: init packet + CTRL_PACKET_POKE, //!< 17: firewall poke packet + CTRL_PACKET_CONN, //!< 18: connection confirmation packet + CTRL_PACKET_KEEP, //!< 19: keep-alive packet + CTRL_PACKET_DISC, //!< 20: connection terminated packet + + CTRL_PACKET_LAST = 63 //!< 63: end of packet code range +}; + +//! size of sequence set +#define SEQN_SIZE (64) +//! sequence set mask +#define SEQN_MASK (SEQN_SIZE-1) + +//! base of unreliable sequence set +#define UNRELSEQN_BASE (64) +//! base of reliable sequence set +#define RELSEQN_BASE (128) +//! base of reliable packet reception acknowledgement sequence set +#define RELSEQNACK_BASE (192) + +//! rate at which to resend unacknoweldged packets +#define RELIABLE_RESEND_RATE (250) + +//! percentage of packets in receive buffer reserved for reliable packets (divisor) +#define RELIABLE_PCT_RESERVED (8) + + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! raw protocol packet format +/*! Notes: + The RawSRPPacket code field looks like this: + + value(s) meaning + ~~~~~~~~~~~~~~~~~~~~~~ + 0 reserved + 1-15 bundled packet count + 16 ctrl packet: init connection (CTRL_PACKET_INIT) + 17 ctrl packet: poke firewall (CTRL_PACKET_POKE) + 18 ctrl packet: connection established (CTRL_PACKET_CONN) + 19 ctrl packet: keep-alive (CTRL_PACKET_KEEP) + 20 ctrl packet: disconnect (CTRL_PACKET_DISC) + 21-63 reserved + 64-127 unreliable packet: sequence number is code-64 + 128-191 reliable packet: sequence number is code-128 + 192-255 reliable packet acknowledgement: sequence number is code-192 + ~~~~~~~~~~~~~~~~~~~~~~ + + If the code field is 1-15, then the value represents the number of packets + bundled together into the same UDP frame minus one (a value of one is + unsupported, as it would simply make the single packet bulkier). The + bundle format looks as follows: + + offset value meaning + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 0 code bundle count-1 (1-15) + + 1 len0-1 length of first bundled packet + 2 code0 code of first packet + 3 data0 data for first packet + + 4+len0 len1-1 length of second bundled packet + 5+len0 code1 code of second packet + 6+len0 data1 data for second packet + + [...] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*/ + +typedef struct +{ + //! packet header which is not sent/received + //! (this data is used internally) + struct + { + uint32_t when; //!< tick when a packet was received + uint32_t len; //!< size of packet + } head; + //! packet body which is sent/received + struct + { + unsigned char code; //!< packet code (see packet codes above) + unsigned char data[1]; //!< packet user data (variable length) + } body; +} RawSRPPacketT; + + +//! private module storage +struct CommSRPRef +{ + //! common header + CommRef common; + //! linked list of all instances + CommSRPRef *link; + //! comm socket + SocketT *socket; + //! peer address + struct sockaddr peeraddr; + + //! port state + enum { + ST_IDLE, //!< no connection + ST_CONN, //!< connecting + ST_LIST, //!< listening + ST_OPEN, //!< connection established + ST_CLOSE //!< connection closed + } state; + + //! identifier to keep from getting spoofed + uint32_t connident; + + //! width of receive records (same as width of send) + int32_t rcvwid; + //! number of packets reserved for reliable transport + int32_t rcvrelresv; + //! length of receive buffer (multiple of rcvwid) + int32_t rcvlen; + //! fifo input offset + int32_t rcvinp; + //! fifo output offset + int32_t rcvout; + //! pointer to buffer storage + char *rcvbuf; + + //! width of send record (same as width of receive) + int32_t sndwid; + //! length of send buffer (multiple of sndwid) + int32_t sndlen; + //! fifo input offset + int32_t sndinp; + //! fifo output offset + int32_t sndout; + //! pointer to buffer storage + char *sndbuf; + + //! tick at which last packet was sent + uint32_t sendtick; + //! tick at which last packet was received + uint32_t recvtick; + + //! unreliable sequence number + uint32_t unrelseqn; + //! unreliable sequence number + uint32_t unrelrecvseqn; + //! reliable sequence number + uint32_t relseqn; + //! reliable received sequence number + uint32_t relrecvseqn; + + //! allow for dns lookup + uint32_t dnsid; + char dnsquery[256]; + char *dnsbuf; + int32_t dnslen; + char dnsdiv; + + //! semaphore to synchronize thread access + NetCritT crit; + //! callback synchronization + volatile int32_t callback; + //! indcate pending event + int32_t gotevent; + //! callback routine pointer + void (*callproc)(CommRef *ref, int32_t event); +}; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPSetAddrInfo + + \Description + Sets peer/host addr/port info in common ref. + + \Input *ref - reference pointer + \Input sin - address pointer + + \Version 04/16/04 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPSetAddrInfo(CommSRPRef *ref, struct sockaddr *sin) +{ + struct sockaddr SockAddr; + + // save peer addr/port info in common ref + ref->common.peerip = SockaddrInGetAddr(sin); + ref->common.peerport = SockaddrInGetPort(sin); + + // save host addr/port info in common ref + SocketInfo(ref->socket, 'bind', 0, &SockAddr, sizeof(SockAddr)); + ref->common.hostip = SocketGetLocalAddr(); + ref->common.hostport = SockaddrInGetPort(&SockAddr); + + // debug output + NetPrintf(("commsrp: peer=0x%08x:%d, host=0x%08x:%d\n", + ref->common.peerip, + ref->common.peerport, + ref->common.hostip, + ref->common.hostport)); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPSetSocket + + \Description + Sets socket in ref and socketref in common portion of ref. + + \Input *pRef - reference pointer + \Input *pSocket - socket to set + + \Version 08/24/04 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPSetSocket(CommSRPRef *pRef, SocketT *pSocket) +{ + pRef->socket = pSocket; + pRef->common.sockptr = pSocket; +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPResetTransfer + + \Description + Reset the transfer state + + \Input *ref - reference pointer + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPResetTransfer(CommSRPRef *ref) +{ + // reset the send queue + ref->sndinp = 0; + ref->sndout = 0; + + // reset the receive queue + ref->rcvinp = 0; + ref->rcvout = 0; + + // make sendtick really old (in protocol terms) + ref->sendtick = NetTick()-5000; + + // start reliable received sequence at an invalid sequence number + ref->relrecvseqn = (uint32_t)-1; +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPSendImmediate + + \Description + Send an packet to the peer. + + \Input *ref - reference pointer + \Input *packet - packet pointer + + \Output + int32_t - negative=error, else amount of data sent + + \Version 01/07/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _CommSRPSendImmediate(CommSRPRef *ref, RawSRPPacketT *packet) +{ + int32_t len, err; + + #if ESC_CAUSES_LOSS + // lose packets when escape is pressed + if (GetAsyncKeyState(VK_ESCAPE) < 0) + { + ref->sendtick = NetTick(); + return(0); + } + #endif + + // figure out amount to send + len = sizeof(packet->body)-sizeof(packet->body.data); // packet framing + len += packet->head.len; // variable data + + #if COMMSRP_VERBOSE + { + char addrbuf[32]; + + SockaddrInGetAddrText(&ref->peeraddr,addrbuf,sizeof(addrbuf)), + NetPrintf(("_CommSRPSendImmediate: Sending %d bytes to %s:%d\n",len, addrbuf, + SockaddrInGetPort(&ref->peeraddr))); + } + #endif + + // send some data + err = SocketSendto(ref->socket,(char *)&packet->body,len,0, + &ref->peeraddr,sizeof(ref->peeraddr)); + + // check for send failure + if (err != len) + { + NetPrintf(("_CommSRPSendImmediate: SocketSendto returned %d\n", err)); + return(-1); + } + + // update last send time + ref->sendtick = NetTick(); + ref->common.datasent += len; + ref->common.packsent += 1; + + return(len); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPSendCtrl + + \Description + Send a control packet. + + \Input *ref - reference pointer + \Input code - control code to send + + \Notes + Control packets are sent unreliably. + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPSendCtrl(CommSRPRef *ref, unsigned char code) +{ + RawSRPPacketT packet; + + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPSendCtrl: Sending control packet id %d\n", code)); + #endif + + packet.head.len = 0; + packet.body.code = code; + + _CommSRPSendImmediate(ref, &packet); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPProcessInitRequest + + \Description + Send an INIT control packet if we know our peer. + + \Input *ref - reference pointer + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPProcessInitRequest(CommSRPRef *ref) +{ + // see if we know our peer + if ((SockaddrInGetAddr(&ref->peeraddr) == 0) || (SockaddrInGetPort(&ref->peeraddr) == 0)) + { + return; + } + + // send connect message once a second + if ((NetTick() - ref->sendtick) >= 1000) + { + if (ref->state == ST_CONN) + { + // send connection initiation packet + _CommSRPSendCtrl(ref, CTRL_PACKET_INIT); + } + else if (ref->state == ST_LIST) + { + // send poke packet + _CommSRPSendCtrl(ref, CTRL_PACKET_POKE); + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPProcessACK + + \Description + Process a reliable packet acknowledgement + + \Input *ref - reference pointer + \Input *packet - ack packet + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPProcessACK(CommSRPRef *ref, RawSRPPacketT *packet) +{ + uint32_t ackseqnid, refseqnid; + RawSRPPacketT *refpkt; + + // get a pointer to packet this should be an ack for + refpkt = (RawSRPPacketT *)&ref->sndbuf[ref->sndout]; + + // decode sequence ids + ackseqnid = packet->body.code - RELSEQNACK_BASE; + refseqnid = refpkt->body.code - RELSEQN_BASE; + + // compare sequence ids + if (ackseqnid == refseqnid) + { + // packet was successfully transmitted, so dequeue it + ref->sndout = (ref->sndout+ref->sndwid)%ref->sndlen; + + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessACK: Success: confirmed delivery of packet %d\n", + refseqnid)); + #endif + } + else + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessACK: Got old ack %d, wanted ack %d\n", + ackseqnid,refseqnid)); + #endif + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPProcessCtrl + + \Description + Process an incoming control message. + + \Input *ref - reference pointer + \Input *packet - control packet to process + \Input *pFrom - sender's socket address + + \Version 01/07/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPProcessCtrl(CommSRPRef *ref, RawSRPPacketT *packet, struct sockaddr *pFrom) +{ + // update valid receive time + // must put into past to avoid race condition + ref->recvtick = NetTick()-1000; + + switch(packet->body.code) + { + // response to connection request packet + case CTRL_PACKET_INIT: + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessSetup: Received INIT\n")); + #endif + + if (ref->state == ST_LIST) + { + // set the peer + ds_memcpy_s(&ref->peeraddr, sizeof(ref->peeraddr), pFrom, sizeof(*pFrom)); + + // set peer/host addr/port info + _CommSRPSetAddrInfo(ref, pFrom); + + // update state + ref->state = ST_OPEN; + } + + // always send a response + _CommSRPSendCtrl(ref, CTRL_PACKET_CONN); + } + break; + + // response to poke packet + case CTRL_PACKET_POKE: + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessSetup: Received POKE\n")); + #endif + + if (ref->state == ST_CONN) + { + // set the peer + ds_memcpy_s(&ref->peeraddr, sizeof(ref->peeraddr), pFrom, sizeof(*pFrom)); + } + } + break; + + // response to a connect confirmation + case CTRL_PACKET_CONN: + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessSetup: Received CONN\n")); + #endif + + // change to open if not already there + if (ref->state == ST_CONN) + { + // set peer/host addr/port info + _CommSRPSetAddrInfo(ref, pFrom); + + ref->state = ST_OPEN; + } + } + break; + + // response to disconnect message + case CTRL_PACKET_DISC: + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessSetup: Received DISC\n")); + #endif + + // close the connection + if (ref->state == ST_OPEN) + { + ref->state = ST_CLOSE; + } + } + break; + + // response to keepalive message + case CTRL_PACKET_KEEP: + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessSetup: Received KEEP\n")); + #endif + } + break; + + // this case should not happen + default: + { + NetPrintf(("_CommSRPProcessSetup: Unrecognized control packet type %d\n",packet->body.code)); + } + break; + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPProcessData + + \Description + Process incoming data packet. + + \Input *ref - reference pointer + \Input *packet - incoming packet + + \Output + int32_t - <0 = error, 0 = packet discarded (duplicate resend), 1 = packet added to buffer + + \Version 01/07/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _CommSRPProcessData(CommSRPRef *ref, RawSRPPacketT *packet) +{ + RawSRPPacketT *buffer; + + if ((packet->body.code >= UNRELSEQN_BASE) && (packet->body.code < RELSEQN_BASE)) + { + int32_t queuepos, pktsfree; + + // calculate the number of free packets in rcvbuf + queuepos = ((ref->rcvinp+ref->rcvlen)-ref->rcvout)%ref->rcvlen; + pktsfree = (ref->rcvlen - queuepos)/ref->rcvwid; + + // see if there is room in buffer for packet (leave extra space for reliable packets) + if (pktsfree <= ref->rcvrelresv) + { + // no room in buffer -- just drop packet + #if COMMSRP_VERBOSE // (we expect lots of these, so don't spam) + NetPrintf(("_CommSRPProcessData: Unreliable packet %d discarded due to input buffer overrun.\n",ref->relrecvseqn)); + #endif + return(-1); + } + + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessData: Received packet %d\n",packet->body.code - UNRELSEQN_BASE)); + #endif + + // update unreliably received sequence number + ref->unrelrecvseqn = packet->body.code - UNRELSEQN_BASE; + } + else + { + uint32_t uPacketId; + + // see if room in buffer for packet + if ((ref->rcvinp+ref->rcvwid)%ref->rcvlen == ref->rcvout) + { + // no room in buffer -- just drop packet + NetPrintf(("_CommSRPProcessData: Reliable packet discarded due to input buffer overrun\n")); + return(-1); + } + + // get packet ID + uPacketId = (uint32_t)(packet->body.code - RELSEQN_BASE); + + // is this the packet we're expecting? + if (uPacketId == ((ref->relrecvseqn+1) % SEQN_SIZE)) + { + // update reliably received sequence number + ref->relrecvseqn = uPacketId; + + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessData: Received packet %d\n", ref->relrecvseqn)); + #endif + } + else if (uPacketId == ref->relrecvseqn) + { + // this is the previously received packet - return TRUE to ack it in case our last ack got lost + return(1); + } + else + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessData: Discarding duplicate packet %d\n",ref->relrecvseqn)); + #endif + return(0); + } + } + + // add the packet to the buffer + buffer = (RawSRPPacketT *) &ref->rcvbuf[ref->rcvinp]; + ds_memcpy(buffer, packet, ref->rcvwid); + + // limit receive access for callbacks + ref->callback += 1; + // add item to receive buffer + ref->rcvinp = (ref->rcvinp+ref->rcvwid) % ref->rcvlen; + // indicate we got an event + ref->gotevent |= 1; + // let the callback process it + if (ref->common.RecvCallback != NULL) + { + ref->common.RecvCallback((CommRef *)ref, buffer->body.data, buffer->head.len, buffer->head.when); + } + // release access to receive + ref->callback -= 1; + return(1); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPProcessAlive + + \Description + Send out a keep-alive packet (CTRL_PACKET_KEEP) periodically + + \Input *ref - reference pointer + + \Version 01/07/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPProcessAlive(CommSRPRef *ref) +{ + if ((ref->state == ST_OPEN) && (ref->sndinp == ref->sndout)) + { + // see if time has elapsed + if (NetTick() > ref->sendtick+5000) + { + // queue up a keepalive packet + _CommSRPSendCtrl(ref, CTRL_PACKET_KEEP); + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPProcessRecvQueue + + \Description + Attempt to read data from peer and add it to the receive queue. + + \Input *ref - reference pointer + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPProcessRecvQueue(CommSRPRef *ref) +{ + int32_t len, sinlen, iResult = 0; + char *buffer; + struct sockaddr sin; + RawSRPPacketT *packet; + + while((ref->state >= ST_CONN) && (ref->state <= ST_OPEN) && (iResult >= 0)) + { + // get pointer to recv queue + packet = (RawSRPPacketT *) &ref->rcvbuf[ref->rcvinp]; + buffer = (char *) &packet->body; + + // attempt to get a packet + sinlen = sizeof(sin); + len = SocketRecvfrom(ref->socket, buffer, ref->rcvwid, 0, &sin, &sinlen); + + // if we got data add it to recv queue + if (len > 0) + { + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessRecvQueue: Received %d bytes of data\n",len)); + #endif + + packet->head.len = len; + packet->head.when = SockaddrInGetMisc(&sin); + + // process packet + if ((packet->body.code >= CTRL_PACKET_FIRST) && (packet->body.code <= CTRL_PACKET_LAST)) + { + // handle control packets + _CommSRPProcessCtrl(ref, packet, &sin); + } + else if (packet->body.code >= RELSEQNACK_BASE) + { + // handle ack of reliable packet + _CommSRPProcessACK(ref, packet); + } + else + { + // handle data packets + iResult = _CommSRPProcessData(ref, packet); + if (iResult == 1) + { + // check to see if this is a reliable packet and requires an acknowledgement + if ((packet->body.code >= RELSEQN_BASE) && (packet->body.code < RELSEQNACK_BASE)) + { + // send an ack (seqid +64) + #if COMMSRP_VERBOSE + NetPrintf(("_CommSRPProcessRecvQueue: Sending ack for packet %d\n",packet->body.code)); + #endif + + _CommSRPSendCtrl(ref,packet->body.code + SEQN_SIZE); + } + } + } + } + else if (len < 0) + { + // error in recv - close connection + NetPrintf(("_CommSRPProcessRecvQueue: Error %d - closing connection\n",len)); + ref->state = ST_CLOSE; + break; + } + else + { + // no data to receive + break; + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPProcessSendQueue + + \Description + Send data to peer from send queue. + + \Input *ref - reference pointer + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPProcessSendQueue(CommSRPRef *ref) +{ + RawSRPPacketT *packet; + + if ((ref->state == ST_OPEN) && (ref->sndinp != ref->sndout)) + { + // ref next packet to send + packet = (RawSRPPacketT *) &ref->sndbuf[ref->sndout]; + + // send packet + if ((packet->head.when == 0) || ((NetTick() - packet->head.when) >= RELIABLE_RESEND_RATE)) + { + #if COMMSRP_VERBOSE + if (packet->head.when != 0) + { + NetPrintf(("_CommSRPProcessSendQueue: Resending sequence id %d\n", + packet->body.code - RELSEQN_BASE)); + } + #endif + + #if COMMSRP_VERBOSE + if (packet->head.when == 0) + { + NetPrintf(("_CommSRPProcessSendQueue: Sending sequence id %d\n", + packet->body.code - RELSEQN_BASE)); + } + #endif + + // send the packet + if (_CommSRPSendImmediate(ref, packet) < 0) + { + return; + } + + // update sent tick + packet->head.when = NetTick(); + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPClose + + \Description + Close the connection + + \Input *ref - reference pointer + + \Output + int32_t - zero + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _CommSRPClose(CommSRPRef *ref) +{ + // see if we are even connected + if (ref->state != ST_OPEN) + { + return(0); + } + + // send a disconnect message + _CommSRPSendCtrl(ref, CTRL_PACKET_DISC); + + // set to disconnect state + ref->connident = 0; + ref->state = ST_CLOSE; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPEvent0 + + \Description + Private socket callback function for CommSRP event processing. + + \Input *ref - reference pointer + + \Notes + This function should only be called by _CommSRPEvent() + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommSRPEvent0(CommSRPRef *ref) +{ + // send init request if we're connecting + if ((ref->state == ST_CONN) || (ref->state == ST_LIST)) + { + _CommSRPProcessInitRequest(ref); + } + + // receive data into recv queue + _CommSRPProcessRecvQueue(ref); + + // periodically send a keep-alive + _CommSRPProcessAlive(ref); + + // send all queued data + _CommSRPProcessSendQueue(ref); + + // do callback if needed + if ((ref->callback == 0) && (ref->gotevent != 0)) + { + // limit callback access + ref->callback += 1; + // notify user + if (ref->callproc != NULL) + { + ref->callproc((CommRef *)ref, ref->gotevent); + } + // limit callback access + ref->callback -= 1; + // done with event + ref->gotevent = 0; + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPEvent + + \Description + Socket callback function for CommSRP event processing, protected + with critical section. + + \Input *sock - unused + \Input flags - unused + \Input *_ref - reference pointer + + \Output + int32_t - zero + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _CommSRPEvent(SocketT *sock, int32_t flags, void *_ref) +{ + CommSRPRef *ref = _ref; + + // see if we have exclusive access + if (NetCritTry(&ref->crit)) + { + // update + _CommSRPEvent0(ref); + + // free access + NetCritLeave(&ref->crit); + } + + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommSRPSendQueued + + \Description + Add packet to send queue. + + \Input *ref - reference pointer + \Input *packet - packet to add to send queue + + \Output + int32_t - buffer depth + + \Notes + Only reliable packets should be added to the send queue; use _CommSRPSendImmediate() for + unreliable packet data. + + \Version 01/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _CommSRPSendQueued(CommSRPRef *ref, RawSRPPacketT *packet) +{ + int32_t pos; + + // mark timestamp=0 for immediate send + packet->head.when = 0; + + // add the packet to the queue + ref->sndinp = (ref->sndinp+ref->sndwid) % ref->sndlen; + + // try and send immediately + _CommSRPEvent(ref->socket, 0, ref); + + // return buffer depth + pos = (((ref->sndinp+ref->sndlen)-ref->sndout)%ref->sndlen)/ref->sndwid; + return(pos); +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function CommSRPConstruct + + \Description + Construct the class + + \Input maxwid - max record width + \Input maxinp - input packet buffer size + \Input maxout - output packet buffer size + + \Output + CommSRPRef - reference pointer + + \Notes + Initialized winsock for first class. also creates linked + list of all current instances of the class and worker thread + to do most udp stuff. + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +CommSRPRef *CommSRPConstruct(int32_t maxwid, int32_t maxinp, int32_t maxout) +{ + CommSRPRef *ref; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate class storage + ref = DirtyMemAlloc(sizeof(*ref), COMMSRP_MEMID, iMemGroup, pMemGroupUserData); + if (ref == NULL) + return(NULL); + ds_memclr(ref, sizeof(*ref)); + ref->common.memgroup = iMemGroup; + ref->common.memgrpusrdata = pMemGroupUserData; + + // initialize the callback routines + ref->common.Construct = (CommAllConstructT *)CommSRPConstruct; + ref->common.Destroy = (CommAllDestroyT *)CommSRPDestroy; + ref->common.Resolve = (CommAllResolveT *)CommSRPResolve; + ref->common.Unresolve = (CommAllUnresolveT *)CommSRPUnresolve; + ref->common.Listen = (CommAllListenT *)CommSRPListen; + ref->common.Unlisten = (CommAllUnlistenT *)CommSRPUnlisten; + ref->common.Connect = (CommAllConnectT *)CommSRPConnect; + ref->common.Unconnect = (CommAllUnconnectT *)CommSRPUnconnect; + ref->common.Callback = (CommAllCallbackT *)CommSRPCallback; + ref->common.Status = (CommAllStatusT *)CommSRPStatus; + ref->common.Tick = (CommAllTickT *)CommSRPTick; + ref->common.Send = (CommAllSendT *)CommSRPSend; + ref->common.Peek = (CommAllPeekT *)CommSRPPeek; + ref->common.Recv = (CommAllRecvT *)CommSRPRecv; + + // remember max packet width + ref->common.maxwid = maxwid; + ref->common.maxinp = maxinp; + ref->common.maxout = maxout; + + // reset access to shared resources + NetCritInit(&ref->crit, "commsrp"); + + // allocate the buffers + ref->rcvwid = sizeof(RawSRPPacketT)-sizeof(((RawSRPPacketT *)0)->body.data)+maxwid; + ref->rcvwid = (ref->rcvwid+3) & 0x7ffc; + ref->rcvlen = ref->rcvwid * maxinp; + ref->rcvbuf = DirtyMemAlloc(ref->rcvlen, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata); + ref->sndwid = sizeof(RawSRPPacketT)-sizeof(((RawSRPPacketT *)0)->body.data)+maxwid; + ref->sndwid = (ref->sndwid+3) & 0x7ffc; + ref->sndlen = ref->sndwid * maxout; + ref->sndbuf = DirtyMemAlloc(ref->sndlen, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata); + + // calculate number of packets reserved for reliable transport + ref->rcvrelresv = maxinp/RELIABLE_PCT_RESERVED; + + // reset the socket + _CommSRPSetSocket(ref, NULL); + + // reset the state + ref->state = ST_IDLE; + ref->connident = 0; + + // return the reference + return(ref); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPDestroy + + \Description + Destruct the class + + \Input *ref - reference pointer + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +void CommSRPDestroy(CommSRPRef *ref) +{ + // if port is open, close it + if (ref->state == ST_OPEN) + { + _CommSRPClose(ref); + } + + // kill the socket + if (ref->socket != NULL) + { + SocketClose(ref->socket); + _CommSRPSetSocket(ref, NULL); + } + + // get rid of sockets critical section + NetCritKill(&ref->crit); + + // release resources + DirtyMemFree(ref->rcvbuf, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata); + DirtyMemFree(ref->sndbuf, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata); + DirtyMemFree(ref, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPCallback + + \Description + Set upper layer callback + + \Input *ref - reference pointer + \Input *callback - socket generating callback + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +void CommSRPCallback(CommSRPRef *ref, void (*callback)(CommRef *ref, int32_t event)) +{ + ref->callproc = callback; + ref->gotevent |= 2; +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPResolve + + \Description + Resolve an address (unimplemented) + + \Input *ref - endpoint + \Input *addr - resolve address + \Input *buf - target buffer + \Input len - target length (min 64 bytes) + \Input div - divider char + + \Output + int32_t - <0=error, 0=complete (COMM_NOERROR), >0=in progress (COMM_PENDING) + + \Notes + Target list is always double null terminated allowing null + to be used as the divider character if desired. when COMM_PENDING + is returned, target buffer is set to "~" until completion + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPResolve(CommSRPRef *ref, const char *addr, char *buf, int32_t len, char div) +{ + NetPrintf(("commsrp: resolve functionality not supported\n")); + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPUnresolve + + \Description + Stop the resolver + + \Input *ref - endpoint ref + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +void CommSRPUnresolve(CommSRPRef *ref) +{ + return; +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPUnlisten + + \Description + Stop listening. + + \Input *ref - reference pointer + + \Output + int32_t - negative=error, zero=ok + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPUnlisten(CommSRPRef *ref) +{ + // get rid of socket if presernt + if (ref->socket != NULL) + { + SocketClose(ref->socket); + _CommSRPSetSocket(ref, NULL); + } + + // return to idle mode + ref->state = ST_IDLE; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPUnconnect + + \Description + Terminate a connection + + \Input *ref - reference pointer + + \Output + int32_t - negative=error, zero=ok + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPUnconnect(CommSRPRef *ref) +{ + // get rid of socket if presernt + if (ref->socket != NULL) + { + SocketClose(ref->socket); + _CommSRPSetSocket(ref, NULL); + } + + // return to idle mode + ref->state = ST_IDLE; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPStatus + + \Description + Return current stream status + + \Input *ref - reference pointer + + \Output + int32_t - COMM_CONNECTING, COMM_OFFLINE, COMM_ONLINE or COMM_FAILURE + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPStatus(CommSRPRef *ref) +{ + // return state + if ((ref->state == ST_CONN) || (ref->state == ST_LIST)) + return(COMM_CONNECTING); + if ((ref->state == ST_IDLE) || (ref->state == ST_CLOSE)) + return(COMM_OFFLINE); + if (ref->state == ST_OPEN) + return(COMM_ONLINE); + return(COMM_FAILURE); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPTick + + \Description + Return current tick + + \Input *ref - reference pointer + + \Output + uint32_t - elapsed milliseconds + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +uint32_t CommSRPTick(CommSRPRef *ref) +{ + return(NetTick()); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPSend + + \Description + Send a packet + + \Input *ref - reference pointer + \Input *buffer - pointer to data + \Input length - length of data + \Input flags - COMM_FLAGS_* + + \Output + int32_t - negative=error, zero=buffer full (temp fail), positive=queue position (ok) + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPSend(CommSRPRef *ref, const void *buffer, int32_t length, uint32_t flags) +{ + RawSRPPacketT *packet; + int32_t pos; + + // make sure port is open + if (ref->state != ST_OPEN) + { + return(COMM_BADSTATE); + } + + // see if input queue full + if ((ref->sndinp+ref->sndwid)%ref->sndlen == ref->sndout) + { + NetPrintf(("commsrp: input queue full\n")); + return(0); + } + + // return error for oversized packets + if (length > (signed)(ref->sndwid-(sizeof(RawSRPPacketT)-sizeof((RawSRPPacketT *)0)->body.data))) + { + NetPrintf(("commsrp: oversized packet send (%d bytes)\n", length)); + return(COMM_MINBUFFER); + } + + // zero sized packet cannot be sent (they are used for acks) + // instead, treat them as successful which means the queue + // position is returned + if (length == 0) + { + pos = (((ref->sndinp+ref->sndlen)-ref->sndout)%ref->sndlen)/ref->sndwid; + return(pos+1); + } + + // add packet to send queue + packet = (RawSRPPacketT *) &(ref->sndbuf[ref->sndinp]); + + // set packet length + packet->head.len = length; + + // copy user data into queue + ds_memcpy(packet->body.data, buffer, length); + + // send reliable or unreliable? + if (flags & COMM_FLAGS_UNRELIABLE) + { + // unreliable sequence tracking + packet->body.code = (unsigned char)ref->unrelseqn++ + UNRELSEQN_BASE; + ref->unrelseqn &= SEQN_MASK; + + #if COMMSRP_VERBOSE + NetPrintf(("commsrp: sending unreliable sequence number %d (len=%d)\n",packet->body.code-UNRELSEQN_BASE,length)); + #endif + + // unreliable packets are sent immediately + if ((pos = _CommSRPSendImmediate(ref,packet)) > 0) + { + pos = 1; + } + } + else + { + // reliable sequence tracking + packet->body.code = (unsigned char)ref->relseqn++ + RELSEQN_BASE; + ref->relseqn &= SEQN_MASK; + + #if COMMSRP_VERBOSE + NetPrintf(("CommSRPSend: Sending reliable sequence number %d (len=%d)\n",packet->body.code-RELSEQN_BASE,length)); + #endif + + // reliable packets are added to the send queue + pos = _CommSRPSendQueued(ref,packet); + } + + // return buffer depth + return((pos > 0) ? pos : 1); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPPeek + + \Description + Peek at waiting packet, and copy to target buffer if present. + + \Input *ref - reference pointer + \Input *target - target buffer + \Input length - buffer length + \Input *when - tick received at + + \Output + int32_t - negative=nothing pending, else packet length + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPPeek(CommSRPRef *ref, void *target, int32_t length, uint32_t *when) +{ + RawSRPPacketT *packet; + int32_t packetlen; + + // see if a packet is available + if (ref->rcvout == ref->rcvinp) + { + return(COMM_NODATA); + } + + // point to the packet + packet = (RawSRPPacketT *) &(ref->rcvbuf[ref->rcvout]); + + #if COMMSRP_VERBOSE + NetPrintf(("commsrp: received packet, sequence number %d (len=%d)\n",packet->body.code,packet->head.len)); + #endif + + // packet length minus code byte + packetlen = packet->head.len - (sizeof(packet->body)-sizeof(packet->body.data)); + + // copy over the data portion + ds_memcpy(target, packet->body.data, (packetlen < length ? packetlen : length)); + + // get the timestamp + if (when != NULL) + { + *when = packet->head.when; + } + + // return packet data length + return(packetlen); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPRecv + + \Description + Receive a packet from the buffer + + \Input *ref - reference pointer + \Input *target - target buffer + \Input length - buffer length + \Input *when - tick received at + + \Output + int32_t - negative=error, else packet length + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPRecv(CommSRPRef *ref, void *target, int32_t length, uint32_t *when) +{ + // use peek to remove the data + int32_t len = CommSRPPeek(ref, target, length, when); + + if (len >= 0) + { + ref->rcvout = (ref->rcvout+ref->rcvwid)%ref->rcvlen; + } + + // all done + return(len); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPListen + + \Description + Listen for a connection + + \Input *ref - reference pointer + \Input *addr - port to listen on (only :port portion used) + + \Output + int32_t - negative=error, zero=ok + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPListen(CommSRPRef *ref, const char *addr) +{ + int32_t err, iListenPort, iConnPort; + uint32_t uPokeAddr; + struct sockaddr bindaddr; + SocketT *sock; + + // make sure in valid state + if ((ref->state != ST_IDLE) || (ref->socket != NULL)) + { + return(COMM_BADSTATE); + } + + // setup bind points + SockaddrInit(&bindaddr, AF_INET); + + // parse at least port + if ((SockaddrInParse2(&uPokeAddr, &iListenPort, &iConnPort, addr) & 0x2) != 0x2) + { + return(COMM_BADADDRESS); + } + SockaddrInSetPort(&bindaddr, iListenPort); + + // reset to unresolved + _CommSRPResetTransfer(ref); + + // create socket in case its needed + sock = SocketOpen(AF_INET, SOCK_DGRAM, 0); + _CommSRPSetSocket(ref, sock); + if (ref->socket == NULL) + { + return(COMM_NORESOURCE); + } + + // bind locally + #if COMMSRP_VERBOSE + NetPrintf(("commsrp: binding to port %d\n", SockaddrInGetPort(&bindaddr))); + #endif + if ((err = SocketBind(ref->socket, &bindaddr, sizeof(bindaddr))) < 0) + { + NetPrintf(("commsrp: error %d binding socket\n", err)); + SocketClose(ref->socket); + _CommSRPSetSocket(ref, NULL); + return(COMM_UNEXPECTED); + } + + // setup for callbacks + SocketCallback(ref->socket, CALLB_RECV, 100, ref, &_CommSRPEvent); + + // see if we should setup peer address + if (uPokeAddr != 0) + { + if (iConnPort == 0) + { + iConnPort = iListenPort+1; + } + + SockaddrInit(&ref->peeraddr, AF_INET); + SockaddrInSetAddr(&ref->peeraddr, uPokeAddr); + SockaddrInSetPort(&ref->peeraddr, iConnPort); + } + + // put into init mode + ref->state = ST_LIST; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function CommSRPConnect + + \Description + Initiate a connection to a peer + + \Input *ref - reference pointer + \Input *pAddr - address in ip-address:port form + + \Output + int32_t - negative=error, zero=ok + + \Notes + Does not perform dns translation + + \Version 01/03/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t CommSRPConnect(CommSRPRef *ref, const char *pAddr) +{ + struct sockaddr bindaddr; + int32_t iErr, iConnPort, iListenPort; + uint32_t uAddr; + SocketT *sock; + + // make sure in valid state + if ((ref->state != ST_IDLE) || (ref->socket != NULL)) + { + return(COMM_BADSTATE); + } + + // parse address and port from addr string + if ((SockaddrInParse2(&uAddr, &iListenPort, &iConnPort, pAddr) & 0x3) != 0x3) + { + return(COMM_BADADDRESS); + } + + // if we don't have an alternate connect port: connect=listen, listen=listen+1 + if (iConnPort == 0) + { + iConnPort = iListenPort++; + } + + // reset to unresolved + _CommSRPResetTransfer(ref); + + // create the actual socket + sock = SocketOpen(AF_INET, SOCK_DGRAM, 0); + _CommSRPSetSocket(ref, sock); + if (ref->socket == NULL) + { + return(COMM_NORESOURCE); + } + + // set listen port + SockaddrInit(&bindaddr, AF_INET); + SockaddrInSetPort(&bindaddr, iListenPort); + + // bind to port + #if COMMSRP_VERBOSE + NetPrintf(("commsrp: binding to port %d\n", iListenPort)); + #endif + if ((iErr = SocketBind(ref->socket, &bindaddr, sizeof(bindaddr))) < 0) + { + NetPrintf(("commsrp: error %d binding socket\n", iErr)); + return(COMM_UNEXPECTED); + } + + // setup target info + SockaddrInit(&ref->peeraddr, AF_INET); + SockaddrInSetAddr(&ref->peeraddr, uAddr); + SockaddrInSetPort(&ref->peeraddr, iConnPort); + + // setup for callbacks + SocketCallback(ref->socket, CALLB_RECV, 100, ref, &_CommSRPEvent); + + // change the state + ref->state = ST_CONN; + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/source/comm/commudp.c b/r5dev/thirdparty/dirtysdk/source/comm/commudp.c new file mode 100644 index 00000000..5315f3e0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/comm/commudp.c @@ -0,0 +1,2665 @@ +/*H*************************************************************************************************/ +/*! + + \File commudp.c + + \Description + This is a reliable UDP transport class optimized for use in a + controller passing game applications. When the actual data + bandwidth is low (as is typical with controller passing), it + sends redundant data in order to quickly recover from any lost + packets. Overhead is low adding only 8 bytes per packet in + addition to UDP/IP overhead. This module support mutual + connects in order to allow connections through firewalls. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 1999-2003. ALL RIGHTS RESERVED. + + \Version 0.1 02/09/99 (GWS) First Version + \Version 0.2 02/14/99 (GWS) Revised and enhanced + \Version 0.5 02/14/99 (GWS) Alpha release + \Version 1.0 07/30/99 (GWS) Final release + \Version 2.0 10/27/99 (GWS) Revised to use winsock 1.1/2.0 + \Version 2.1 12/04/99 (GWS) Removed winsock 1.1 support + \Version 2.2 01/12/00 (GWS) Fixed receive tick bug + \Version 2.3 06/12/00 (GWS) Added fastack for low-latency nets + \Version 2.4 12/04/00 (GWS) Added firewall penetrator + \Version 3.0 12/04/00 (GWS) Reported to dirtysock + \Version 3.1 11/20/02 (JLB) Added Send() flags parameter + \Version 3.2 02/18/03 (JLB) Fixes for multiple connection support + \Version 3.3 05/06/03 (GWS) Allowed poke to come from any IP + \Version 3.4 09/02/03 (JLB) Added unreliable packet type + \Version 4.0 09/12/03 (JLB) Per-send optional unreliable transport + \Version 5.0 07/07/09 (jrainy) Putting meta-data bits over the high bits of the sequence number +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/comm/commall.h" +#include "DirtySDK/comm/commudp.h" +#include "DirtySDK/comm/commudputil.h" + +/*** Defines ***************************************************************************/ + +#undef COMM_PRINT +#define COMM_PRINT (0) + +#if defined(DIRTYCODE_PC) + #define ESC_CAUSES_LOSS (DIRTYCODE_DEBUG && 0) +#else + #define ESC_CAUSES_LOSS (0) +#endif + +#if ESC_CAUSES_LOSS +#include +#endif + +#define BUSY_KEEPALIVE (100) +#define IDLE_KEEPALIVE (2500) +#define PENETRATE_RATE (1000) +#define UNACK_LIMIT (2048) + +//! max additional space needed by a commudp meta type +#define COMMUDP_MAX_METALEN (8) + +#define REDUNDANT_LIMIT_DEFAULT (64) + +#define COMMUDP_VERSION_1_0 (0x0100) +#define COMMUDP_VERSION_1_1 (0x0101) +#define COMMUDP_VERSION (COMMUDP_VERSION_1_1) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! raw protocol packet format +typedef struct +{ + //! packet header which is not sent/received (this data is used internally) + struct { + int32_t iLen; //!< variable data len (-1=none) (used int32_t for alignment) + uint32_t uWhen; //!< tick when a packet was received + uint32_t uMeta; //!< four-bit metadata field extracted from seq field + } head; + //! packet body which is sent/received + struct { + uint32_t uSeq; //!< packet type or sequence number + uint32_t uAck; //!< acknowledgement of last packet + uint8_t aData[SOCKET_MAXUDPRECV-8];//!< user data + } body; +} RawUDPPacketT; + +//! raw protocol packet header -- used for stack local formatting of handshake packets, and packets with no data +typedef struct +{ + //! packet header which is not sent/received (this data is used internally) + struct { + int32_t iLen; //!< variable data len (-1=none) (used int32_t for alignment) + uint32_t uWhen; //!< tick when a packet was received + uint32_t uMeta; //!< four-bit metadata field extracted from seq field + } head; + //! packet body which is sent/received + struct { + uint32_t uSeq; //!< packet type or seqeunce number + uint32_t uAck; //!< acknowledgement of last packet + uint32_t uCid; //!< client id (v1.0: in INIT/POKE packets; v1.1+: in INIT/POKE/CONN packets) + uint8_t aVers[2]; //!< protocol version (v1.1+ only) + uint8_t aData[62]; //!< space for possible metadata included in control packets + } body; +} RawUDPPacketHeadT; + +//! private module storage +struct CommUDPRef +{ + //! common header is first + CommRef Common; + + //! max amount of unacknowledged data that can be sent in one go (default 2k) + uint32_t uUnackLimit; + + //! max amount of data that can be sent redundantly (default = REDUNDANT_LIMIT_DEFAULT) + uint32_t uRedundantLimit; + + //! linked list of all instances + CommUDPRef *pLink; + //! comm socket + SocketT *pSocket; + //! peer address + struct sockaddr PeerAddr; + + //! port state + enum { + DEAD, //!< dead + IDLE, //!< idle + CONN, //!< conn + LIST, //!< list + OPEN, //!< open + CLOSE //!< close + } eState; + + //! identifier to keep from getting spoofed + uint32_t uConnIdent; + + //! type of metachunk to include in stream (zero=none) + uint32_t uMetaType; + + //! protocol version + uint16_t uVers; + uint16_t _pad; + + //! unique client identifier (used for game server identification) + uint32_t uClientIdent; + //! remote client identifier + uint32_t uRemClientIdent; + + //! width of receive records (same as width of send) + int32_t iRcvWid; + //! length of receive buffer (multiple of rcvwid) + int32_t iRcvLen; + //! fifo input offset + int32_t iRcvInp; + //! fifo output offset + int32_t iRcvOut; + //! pointer to buffer storage + char *pRcvBuf; + //! next packet expected (sequence number) + uint32_t uRcvSeq; + //! next unreliable packet expected + uint32_t uUnreliableRcvSeq; + //! last packet we acknowledged + uint32_t uRcvAck; + //! number of unacknowledged received bytes + int32_t iRcvUnack; + + //! width of send record (same as width of receive) + int32_t iSndWid; + //! length of send buffer (multiple of sndwid) + int32_t iSndLen; + //! fifo input offset + int32_t iSndInp; + //! fifo output offset + int32_t iSndOut; + //! current output point within fifo + int32_t iSndNxt; + //! pointer to buffer storage + char *pSndBuf; + //! next packet to send (sequence number) + uint32_t uSndSeq; + //! unreliable packet sequence number + uint32_t uUnreliableSndSeq; + //! last send result + uint32_t uSndErr; + + //! tick at which last packet was sent + uint32_t uSendTick; + //! tick at which last reliable packet was sent (used for resend tracking) + uint32_t uSendReliableTick; + //! tick at which last packet was received + uint32_t uRecvTick; + //! tick at which last idle callback made + uint32_t uIdleTick; + + //! control access during callbacks + volatile int32_t iCallback; + //! indicate there is an event pending + uint32_t uGotEvent; + //! callback routine pointer + void (*pCallProc)(void *pRef, int32_t iEvent); +}; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +//! linked list of port objects +static CommUDPRef *g_link = NULL; + +//! semaphore to synchronize thread access +static NetCritT g_crit; + +//! missed event marker +static int32_t g_missed; + +//! variable indicates call to _CommUDPEvent() in progress +static int32_t g_inevent; + +#if DIRTYCODE_LOGGING +static const char *g_strConnNames[] = { "COMMUDP_RAW_PACKET_INVALID", "COMMUDP_RAW_PACKET_INIT", "COMMUDP_RAW_PACKET_CONN", "COMMUDP_RAW_PACKET_DISC", "COMMUDP_RAW_PACKET_NAK", "COMMUDP_RAW_PACKET_POKE" }; +#endif + +// Public variables + + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPSeqnDelta + + \Description + Compute the sequence number off of uPos by iDelta places + Can NOT be used for unreliable sequence offsetting + + \Input uPos - starting position + \Input iDelta - offset + + \Output + uint32_t - resulting position + + \Version 07/07/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static uint32_t _CommUDPSeqnDelta(uint32_t uPos, int32_t iDelta) +{ + return(((uPos + iDelta + COMMUDP_RAW_PACKET_DATA_WINDOW - COMMUDP_RAW_PACKET_DATA) % COMMUDP_RAW_PACKET_DATA_WINDOW) + COMMUDP_RAW_PACKET_DATA); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPSeqnDiff + + \Description + Compute the difference in position between two sequence numbers + Can NOT be used to compute unreliable sequence differences + + \Input uPos1 - source position + \Input uPos2 - target position + + \Output + int32_t - signed difference in position + + \Version 07/07/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPSeqnDiff(uint32_t uPos1, uint32_t uPos2) +{ + return((((uPos1 - uPos2) + (3 * COMMUDP_RAW_PACKET_DATA_WINDOW / 2)) % COMMUDP_RAW_PACKET_DATA_WINDOW) - (COMMUDP_RAW_PACKET_DATA_WINDOW / 2)); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPSetAddrInfo + + \Description + Sets peer/host addr/port info in common ref. + + \Input *pRef - reference pointer + \Input *pSin - address pointer + + \Version 04/16/04 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommUDPSetAddrInfo(CommUDPRef *pRef, struct sockaddr *pSin) +{ + // save peer addr/port info in common ref + pRef->Common.peerip = SockaddrInGetAddr(pSin); + pRef->Common.peerport = SockaddrInGetPort(pSin); + NetPrintf(("commudp: [%p] peer=%a:%d\n", pRef, pRef->Common.peerip, pRef->Common.peerport)); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPSetSocket + + \Description + Sets socket in ref and socketref in common portion of ref. + + \Input *pRef - reference pointer + \Input *pSocket - socket to set + + \Version 08/24/04 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommUDPSetSocket(CommUDPRef *pRef, SocketT *pSocket) +{ + pRef->pSocket = pSocket; + pRef->Common.sockptr = pSocket; + if (pSocket != NULL) + { + struct sockaddr SockAddr; + // save host addr/port info in common ref + SocketInfo(pRef->pSocket, 'bind', 0, &SockAddr, sizeof(SockAddr)); + pRef->Common.hostip = SocketGetLocalAddr(); + pRef->Common.hostport = SockaddrInGetPort(&SockAddr); + NetPrintf(("commudp: [%p] host=%a:%d\n", pRef, pRef->Common.hostip, pRef->Common.hostport)); + } + else + { + pRef->Common.hostip = 0; + pRef->Common.hostport = 0; + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPSetConnID + + \Description + Sets connident to the 32bit hash of the specified connection identifier string, if any. + + \Input *pRef - reference pointer + \Input *pStrConn - pointer to user-specified connection string + + \Version 06/16/04 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommUDPSetConnID(CommUDPRef *pRef, const char *pStrConn) +{ + const char *pConnID = strchr(pStrConn, '#'); + if (pConnID != NULL) + { + pRef->uConnIdent = NetHash(pConnID+1); + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPResetTransfer + + \Description + Reset the transfer state + + \Input *pRef - reference pointer + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +static void _CommUDPResetTransfer(CommUDPRef *pRef) +{ + // reset packet received bool + pRef->Common.bpackrcvd = FALSE; + // reset the send queue + pRef->iSndInp = 0; + pRef->iSndOut = 0; + pRef->iSndNxt = 0; + // reset the sequence number + pRef->uSndSeq = COMMUDP_RAW_PACKET_DATA; + pRef->uUnreliableSndSeq = COMMUDP_RAW_PACKET_UNREL; + // reset the receive queue + pRef->iRcvInp = 0; + pRef->iRcvOut = 0; + // reset the packet sequence number + pRef->uRcvSeq = COMMUDP_RAW_PACKET_DATA; + pRef->uUnreliableRcvSeq = COMMUDP_RAW_PACKET_UNREL; + + // no unack data + pRef->iRcvUnack = 0; + + // make sendtick really old (in protocol terms) + pRef->uSendTick = pRef->uSendReliableTick = NetTick()-5000; + // recvtick must be older than tick because the first + // packet that arrives may come in moments before this + // initialization takes place and without this adjustment + // code can compute an elapsed receive time of 0xffffffff] + pRef->uRecvTick = NetTick()-5000; +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPOverhead + + \Description + Computes the bandwidth overhead associated with a packet of length packetLength + + \Input *pRef - module reference + \Input iPktLen - length of the packet we are about to send + + \Output + int32_t - the associated overhead. 28 on most platforms, but higher on xbox360. + + \Version 01/08/07 (JRainy) + +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPOverhead(CommUDPRef *pRef, int32_t iPktLen) +{ + // start with basic IP+UDP header size + int32_t iOverhead = 28; + return(iOverhead); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPWrite + + \Description + Send a packet to the peer + + \Input *pRef - reference pointer + \Input *pPacket - packet pointer + \Input *pPeerAddr - address of peer to send to + \Input uCurrTick - current tick + + \Output + int32_t - negative=error, zero=ok + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPWrite(CommUDPRef *pRef, RawUDPPacketT *pPacket, struct sockaddr *pPeerAddr, uint32_t uCurrTick) +{ + int32_t iErr; + int32_t iLen; + + // figure full packet length (nak/ack fields + variable data) + iLen = sizeof(pPacket->body)-sizeof(pPacket->body.aData)+pPacket->head.iLen; + + // fill in metatype info + if (pRef->uMetaType == 1) + { + int32_t iMetaOffset = ((pPacket->body.uSeq == COMMUDP_RAW_PACKET_INIT) || (pPacket->body.uSeq == COMMUDP_RAW_PACKET_POKE)) ? 4 : 0; + // make room for metadata + memmove(&pPacket->body.aData[COMMUDP_RAW_METATYPE1_SIZE+iMetaOffset], &pPacket->body.aData[0+iMetaOffset], iLen-iMetaOffset); + iLen += COMMUDP_RAW_METATYPE1_SIZE; + // metatype1 src clientident + pPacket->body.aData[0+iMetaOffset] = (uint8_t)(pRef->uClientIdent >> 24); + pPacket->body.aData[1+iMetaOffset] = (uint8_t)(pRef->uClientIdent >> 16); + pPacket->body.aData[2+iMetaOffset] = (uint8_t)(pRef->uClientIdent >> 8); + pPacket->body.aData[3+iMetaOffset] = (uint8_t)(pRef->uClientIdent); + // metatype1 dst clientident + pPacket->body.aData[4+iMetaOffset] = (uint8_t)(pRef->uRemClientIdent >> 24); + pPacket->body.aData[5+iMetaOffset] = (uint8_t)(pRef->uRemClientIdent >> 16); + pPacket->body.aData[6+iMetaOffset] = (uint8_t)(pRef->uRemClientIdent >> 8); + pPacket->body.aData[7+iMetaOffset] = (uint8_t)(pRef->uRemClientIdent); + // set metatype header + pPacket->body.uSeq |= (pRef->uMetaType & 0xf) << COMMUDP_SEQ_META_SHIFT; + } + + #if COMM_PRINT > 1 + NetPrintf(("commudp: [%p] seq:0x%08x ack:0x%08x send %d bytes to %a:%d\n", pRef, pPacket->body.uSeq, pPacket->body.uAck, iLen, SockaddrInGetAddr(pPeerAddr), SockaddrInGetPort(pPeerAddr))); + #endif + #if COMM_PRINT > 2 + NetPrintMem(&pPacket->body, iLen, "cudp-send"); + #endif + + // translate seq and ack to network order for send + pPacket->body.uSeq = SocketHtonl(pPacket->body.uSeq); + pPacket->body.uAck = SocketHtonl(pPacket->body.uAck); + + // store send time in misc field of sockaddr + SockaddrInSetMisc(pPeerAddr, uCurrTick); + + #if ESC_CAUSES_LOSS + // lose packets when escape is pressed + if (GetAsyncKeyState(VK_ESCAPE) < 0) + { + NetPrintf(("commudp: [%p] dropping packet to simulate packet loss (seq=0x%08x)\n", pRef, SocketNtohl(pPacket->body.uSeq))); + iErr = iLen; + } + else + { + // send the packet + iErr = SocketSendto(pRef->pSocket, (char *)&pPacket->body, iLen, 0, pPeerAddr, sizeof(*pPeerAddr)); + } + #else + // send the packet + iErr = SocketSendto(pRef->pSocket, (char *)&pPacket->body, iLen, 0, pPeerAddr, sizeof(*pPeerAddr)); + #endif + + + // translate seq and ack back to host order + pPacket->body.uSeq = SocketNtohl(pPacket->body.uSeq); + pPacket->body.uAck = SocketNtohl(pPacket->body.uAck); + + // check for success + if (iErr == iLen) + { + // update last send time + pRef->uSendTick = uCurrTick; + + // do stats + if (pRef->eState == OPEN) + { + pRef->Common.datasent += iLen; + pRef->Common.packsent += 1; + } + + pRef->Common.overhead += _CommUDPOverhead(pRef, iLen); + + // is the packet reliable? + if ((pPacket->body.uSeq & COMMUDP_SEQ_MASK) >= COMMUDP_RAW_PACKET_DATA) + { + // we assume any reliable send includes an up to date ack value + // which means that we can reset the unacked data count to zero + pRef->iRcvUnack = 0; + // update reliable send timer + pRef->uSendReliableTick = uCurrTick; + } + } + else + { + NetPrintf(("commudp: [%p] SocketSendto() returned %d\n", pRef, iErr)); + pRef->uSndErr = iErr; + iErr = -1; + } + + return(iErr); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPClose + + \Description + Close the connection + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Output + int32_t - negative=error, zero=ok + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPClose(CommUDPRef *pRef, uint32_t uCurrTick) +{ + RawUDPPacketHeadT Packet; + + // if we're open, shut down connection + if (pRef->eState == OPEN) + { + // see if any output data pending + if (pRef->iSndNxt != pRef->iSndInp) + { + NetPrintf(("commudp: [%p] unsent data pending\n", pRef)); + } + else if (pRef->iSndOut != pRef->iSndInp) + { + NetPrintf(("commudp: [%p] unacked data pending\n", pRef)); + } + + // send a disconnect message + Packet.head.iLen = 0; + Packet.body.uSeq = COMMUDP_RAW_PACKET_DISC; + Packet.body.uAck = pRef->uConnIdent; + _CommUDPWrite(pRef, (RawUDPPacketT *)&Packet, &pRef->PeerAddr, uCurrTick); + } + + // set to disconnect state + NetPrintf(("commudp: [%p] closed connection\n", pRef)); + pRef->uConnIdent = 0; + pRef->eState = CLOSE; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPFormatHandshake + + \Description + Format handshake packet + + \Input *pRef - reference pointer + \Input *pPacket - [out] buffer to format handshake packet into + \Input uKind - packet kind (COMMUDP_RAW_PACKET_INIT, COMMUDP_RAW_PACKET_POKE, COMMUDP_RAW_PACKET_CONN) + + \Version 10/29/2015 (jbrookes) +*/ +/*************************************************************************************************F*/ +static void _CommUDPFormatHandshake(CommUDPRef *pRef, RawUDPPacketHeadT *pPacket, uint32_t uKind) +{ + pPacket->body.uSeq = uKind; + pPacket->body.uAck = pRef->uConnIdent; + #if COMMUDP_VERSION > COMMUDP_VERSION_1_0 + pPacket->body.uCid = SocketHtonl(pRef->uClientIdent); + pPacket->body.aVers[0] = COMMUDP_VERSION>>8; + pPacket->body.aVers[1] = COMMUDP_VERSION&0x0f; + pPacket->head.iLen = 6; + #else + if ((kind == COMMUDP_RAW_PACKET_INIT) || (kind == COMMUDP_RAW_PACKET_POKE)) + { + pPacket->body.cid = SocketHtonl(pRef->clientident); + pPacket->head.iLen = 4; + } + else + { + pPacket->head.iLen = 0; + } + #endif +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPProcessSetup + + \Description + Process a setup/teardown request + + \Input *pRef - reference pointer + \Input *pPacket - requesting packet + \Input *pSin - address + \Input uCurrTick- current tick + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +static void _CommUDPProcessSetup(CommUDPRef *pRef, const RawUDPPacketT *pPacket, struct sockaddr *pSin, uint32_t uCurrTick) +{ + // make sure the session identifier matches + if (pPacket->body.uAck != pRef->uConnIdent) + { + NetPrintf(("commudp: [%p] warning - connident mismatch (expected=0x%08x got=0x%08x)\n", pRef, pRef->uConnIdent, pPacket->body.uAck)); + // an init packet with a different session identifier + // indicates that the old session has closed + if (pPacket->body.uSeq == COMMUDP_RAW_PACKET_INIT) + { + pRef->eState = CLOSE; + } + return; + } + + // update valid receive time -- must put into past to avoid race condition + pRef->uRecvTick = uCurrTick-1000; + + // version identification + #if COMMUDP_VERSION > COMMUDP_VERSION_1_0 + if ((pRef->uVers == 0) && ((pPacket->body.uSeq == COMMUDP_RAW_PACKET_INIT) || (pPacket->body.uSeq == COMMUDP_RAW_PACKET_CONN) || (pPacket->body.uSeq == COMMUDP_RAW_PACKET_POKE))) + { + RawUDPPacketHeadT *hshk = (RawUDPPacketHeadT *)pPacket; + pRef->uVers = (pPacket->head.iLen > 4) ? (hshk->body.aVers[0] << 8) | hshk->body.aVers[1] : COMMUDP_VERSION_1_0; + NetPrintf(("commudp: [%p] vers=%d.%d\n", pRef, pRef->uVers >> 8, pRef->uVers & 0xff)); + } + #else + pRef->uVers = COMMUDP_VERSION_1_0; + #endif + + // response to connection/poke query + if ((pPacket->body.uSeq == COMMUDP_RAW_PACKET_INIT) || (pPacket->body.uSeq == COMMUDP_RAW_PACKET_POKE)) + { + RawUDPPacketHeadT connpacket; + + // set host/peer addr/port info + _CommUDPSetAddrInfo(pRef, pSin); + + // send CONN in response to INIT/POKE + NetPrintf(("commudp: [%p] sending CONN packet to %a:%d connident=0x%08x\n", pRef, SockaddrInGetAddr(&pRef->PeerAddr), + SockaddrInGetPort(&pRef->PeerAddr), pRef->uConnIdent)); + _CommUDPFormatHandshake(pRef, &connpacket, COMMUDP_RAW_PACKET_CONN); + _CommUDPWrite(pRef, (RawUDPPacketT *)&connpacket, &pRef->PeerAddr, uCurrTick); + return; + } + + // response to a connect confirmation + if (pPacket->body.uSeq == COMMUDP_RAW_PACKET_CONN) + { + // change to open if not already there + if (pRef->eState == CONN) + { + // set host/peer addr/port info + _CommUDPSetAddrInfo(pRef, pSin); + NetPrintf(("commudp: [%p] transitioning to OPEN state due to received COMMUDP_RAW_PACKET_CONN\n", pRef)); + pRef->eState = OPEN; + } + return; + } + + // response to disconnect message + if (pPacket->body.uSeq == COMMUDP_RAW_PACKET_DISC) + { + // close the connection + if (pRef->eState == OPEN) + { + pRef->eState = CLOSE; + } + NetPrintf(("commudp: [%p] received DISC packet\n", pRef)); + } + + // should not get here +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPProcessInit + + \Description + Initiate a connection + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static void _CommUDPProcessInit(CommUDPRef *pRef, uint32_t uCurrTick) +{ + RawUDPPacketHeadT Packet; + + // send INIT to peer + NetPrintf(("commudp: [%p] sending INIT packet to %a:%d connident=0x%08x clientident=0x%08x\n", pRef, SockaddrInGetAddr(&pRef->PeerAddr), + SockaddrInGetPort(&pRef->PeerAddr), pRef->uConnIdent, pRef->uClientIdent)); + _CommUDPFormatHandshake(pRef, &Packet, COMMUDP_RAW_PACKET_INIT); + _CommUDPWrite(pRef, (RawUDPPacketT *)&Packet, &pRef->PeerAddr, uCurrTick); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPProcessOutput + + \Description + Send data packet(s) + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +static void _CommUDPProcessOutput(CommUDPRef *pRef, uint32_t uCurrTick) +{ + int32_t iIndex, iCount; + int32_t iTLimit = pRef->uUnackLimit, iPLimit; + static RawUDPPacketT Multi; + RawUDPPacketT *pBuffer; + static uint32_t uMLimit = 0x20000000; + int32_t iMetaLen = CommUDPUtilGetMetaSize(pRef->uMetaType); + int32_t iSubpacketLimit = (pRef->uVers > COMMUDP_VERSION_1_0) ? 1530 : 250; + + // figure unacked data length + for (iIndex = pRef->iSndOut; iIndex != pRef->iSndNxt; iIndex = (iIndex+pRef->iSndWid)%pRef->iSndLen) { + pBuffer = (RawUDPPacketT *) &pRef->pSndBuf[iIndex]; + // count down the limit + iTLimit -= pBuffer->head.iLen; + } + + // allow a minimum send rate (256 bytes per 250 ms) + if ((iTLimit < 256) && (uCurrTick-pRef->uSendTick > 250)) + iTLimit = 256; + + // send as much data as limit allows + while (iTLimit > 0) { + + // limit size of forward packet consolidation + iPLimit = SocketInfo(NULL, 'maxp', 0, NULL, 0) - iMetaLen - sizeof(Multi.body) + sizeof(Multi.body.aData); + + if (iPLimit > iTLimit) + iPLimit = iTLimit; + + // attempt forward consolidation of packets + for (iCount = 0; (iCount < 8) && (iPLimit > 0) && (pRef->iSndNxt != pRef->iSndInp); ++iCount) { + pBuffer = (RawUDPPacketT *) &pRef->pSndBuf[pRef->iSndNxt]; + iPLimit -= pBuffer->head.iLen; + iPLimit -= CommUDPUtilEncodeSubpacketSize(NULL, pBuffer->head.iLen); + // if not the first packet, then we must be careful about size + if ((iCount > 0) && (iPLimit <= 0)) + break; + pRef->iSndNxt = (pRef->iSndNxt+pRef->iSndWid) % pRef->iSndLen; + // if packet is too large for subpacket encoding, it must be final packet (first in multisend) + if (pBuffer->head.iLen > iSubpacketLimit) { + ++iCount; + break; + } + } + if (iCount == 0) + return; + + // setup main packet + iIndex = (pRef->iSndNxt+pRef->iSndLen-pRef->iSndWid)%pRef->iSndLen; + pBuffer = (RawUDPPacketT *) &pRef->pSndBuf[iIndex]; + // if they want a callback, do it now + if (pRef->Common.SendCallback != NULL) + pRef->Common.SendCallback((CommRef *)pRef, pBuffer->body.aData, pBuffer->head.iLen, uCurrTick); + ds_memcpy(&Multi, pBuffer, sizeof(pBuffer->head)+8+pBuffer->head.iLen); + iCount -= 1; + + // add in required preceding packets + for (; iCount > 0; --iCount) { + // move to preceding packet + iIndex = (iIndex+pRef->iSndLen-pRef->iSndWid)%pRef->iSndLen; + // point to potential piggyback packet + pBuffer = (RawUDPPacketT *) &pRef->pSndBuf[iIndex]; + // if they want a callback, do it now + if (pRef->Common.SendCallback != NULL) + pRef->Common.SendCallback((CommRef *)pRef, pBuffer->body.aData, pBuffer->head.iLen, uCurrTick); + // combine the packets + Multi.body.uSeq += COMMUDP_SEQ_MULTI_INC; + ds_memcpy(Multi.body.aData+Multi.head.iLen, pBuffer->body.aData, pBuffer->head.iLen); + Multi.head.iLen += pBuffer->head.iLen; + Multi.head.iLen += CommUDPUtilEncodeSubpacketSize(Multi.body.aData+Multi.head.iLen, pBuffer->head.iLen); + } + + // add in optional redundant packets + while ((iIndex != pRef->iSndOut) && (Multi.body.uSeq <= uMLimit)) { + // move to preceding packet + iIndex = (iIndex+pRef->iSndLen-pRef->iSndWid)%pRef->iSndLen; + // point to potential piggyback packet + pBuffer = (RawUDPPacketT *) &pRef->pSndBuf[iIndex]; + // if packet is too large for subpacket encoding, we cannot multi-send it + if (pBuffer->head.iLen > iSubpacketLimit) + break; + // see if combined length would be a problem + if ((Multi.head.iLen + pBuffer->head.iLen + 1) > (signed)pRef->uRedundantLimit) + break; + // if they want a callback, do it now + if (pRef->Common.SendCallback != NULL) + pRef->Common.SendCallback((CommRef *)pRef, pBuffer->body.aData, pBuffer->head.iLen, uCurrTick); + // combine the packets + Multi.body.uSeq += COMMUDP_SEQ_MULTI_INC; + ds_memcpy(Multi.body.aData+Multi.head.iLen, pBuffer->body.aData, pBuffer->head.iLen); + Multi.head.iLen += pBuffer->head.iLen; + Multi.head.iLen += CommUDPUtilEncodeSubpacketSize(Multi.body.aData+Multi.head.iLen, pBuffer->head.iLen); + } + + // adjust the max redundancy + if (iIndex == pRef->iSndOut) { + uMLimit = 0x20000000; + } else { + uMLimit = (uMLimit < 0x80000000 ? uMLimit*2 : 0xf0000000); + } + + // update the ack value (one less than one we are waiting for) + pRef->uRcvAck = pRef->uRcvSeq; + Multi.body.uAck = _CommUDPSeqnDelta(pRef->uRcvSeq, -1); + // go ahead and send the packet + if (_CommUDPWrite(pRef, &Multi, &pRef->PeerAddr, uCurrTick) < 0) + { + return; + } + // count the bandwidth for this packet + iTLimit -= Multi.head.iLen; + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPProcessFlow + + \Description + Perform flow control based on ack/nak packets + + \Input *pRef - reference pointer + \Input *pPacket - incoming packet header + \Input uCurrTick- current tick + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static void _CommUDPProcessFlow(CommUDPRef *pRef, RawUDPPacketHeadT *pPacket, uint32_t uCurrTick) +{ + int32_t iNak; + uint32_t uAck; + + // grab the ack point and nak flag + iNak = (pPacket->body.uSeq == COMMUDP_RAW_PACKET_NAK); + + if (pPacket->body.uAck < COMMUDP_RAW_PACKET_DATA) + { + uAck = (iNak ? pPacket->body.uAck-1 : pPacket->body.uAck); + } + else + { + uAck = (iNak ? _CommUDPSeqnDelta(pPacket->body.uAck, -1) : pPacket->body.uAck); + } + + // advance ack point + while (pRef->iSndOut != pRef->iSndInp) + { + RawUDPPacketT *pBuffer = (RawUDPPacketT *) &pRef->pSndBuf[pRef->iSndOut]; + // see if this packet has been acked + if (((uAck & COMMUDP_SEQ_MASK) < COMMUDP_RAW_PACKET_DATA) || + (_CommUDPSeqnDiff(uAck, pBuffer->body.uSeq) < 0)) + { + break; + } + + // if about to send this packet, skip to next + if (pRef->iSndNxt == pRef->iSndOut) + pRef->iSndNxt = (pRef->iSndNxt+pRef->iSndWid) % pRef->iSndLen; + // remove the packet from the queue + pRef->iSndOut = (pRef->iSndOut+pRef->iSndWid) % pRef->iSndLen; + } + + // reset send point for nak + if (iNak) + { + #if COMM_PRINT + NetPrintf(("commudp: [%p] got NAK for packet %d\n", pRef, uAck+1)); + #endif + // reset send point + pRef->iSndNxt = pRef->iSndOut; + // immediate restart + _CommUDPProcessOutput(pRef, uCurrTick); + } +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPProcessInput + + \Description + Process incoming data packet + + \Input *pRef - module state + \Input *pPacket - incoming packet header + \Input *pData - pointer to packet data + \Input uCurrTick- current network tick + + \Output + int32_t - -1=nak, 0=old, 1=new, 2=buffer full + + \Version 12/04/2000 (gschaefer) +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPProcessInput(CommUDPRef *pRef, RawUDPPacketHeadT *pPacket, uint8_t *pData, uint32_t uCurrTick) +{ + RawUDPPacketT *pBuffer; + + // see if room in buffer for packet + if ((pRef->iRcvInp+pRef->iRcvWid)%pRef->iRcvLen == pRef->iRcvOut) + { + // no room in buffer -- just drop packet + // could nak, but would generate lots of + // network activity with no result + NetPrintf(("commudp: [%p] input buffer overflow\n", pRef)); + return(2); + } + + // handle unreliable receive + if (((pPacket->body.uSeq & COMMUDP_SEQ_MASK) >= COMMUDP_RAW_PACKET_UNREL) && ((pPacket->body.uSeq & COMMUDP_SEQ_MASK) < COMMUDP_RAW_PACKET_DATA)) + { + // calculate the number of free packets in pRcvBuf + int32_t queuepos = ((pRef->iRcvInp+pRef->iRcvLen)-pRef->iRcvOut)%pRef->iRcvLen; + int32_t pktsfree = (pRef->iRcvLen - queuepos)/pRef->iRcvWid; + + // calculate delta between received sequence and expected sequence, accounting for wrapping + int32_t delta = (pPacket->body.uSeq - pRef->uUnreliableRcvSeq) & (COMMUDP_RAW_PACKET_UNREL - 1); + + // update lost packet count + if ((pPacket->body.uSeq > pRef->uUnreliableRcvSeq) || (delta < COMMUDP_RAW_PACKET_UNREL/4)) + { + pRef->Common.packlost += delta; + } + + // calculate new sequence number + if ((pRef->uUnreliableRcvSeq = (pPacket->body.uSeq + 1)) >= COMMUDP_RAW_PACKET_DATA) + { + pRef->uUnreliableRcvSeq = COMMUDP_RAW_PACKET_UNREL + (pRef->uUnreliableRcvSeq - COMMUDP_RAW_PACKET_DATA); + } + + // see if there is room to buffer (leave room for reliable packets) + if (pktsfree <= 4) + { + return(2); + } + } + else + { + // ignore old packets + if (_CommUDPSeqnDiff(pPacket->body.uSeq, pRef->uRcvSeq) < 0) + { + return(0); + } + + // immediate nak for missing packets + if (_CommUDPSeqnDiff(pPacket->body.uSeq, pRef->uRcvSeq) > 0) + { + // update lost packet count + pRef->Common.packlost += _CommUDPSeqnDiff(pPacket->body.uSeq, pRef->uRcvSeq); + + // send a nak packet + #if COMM_PRINT + NetPrintf(("commudp: [%p] sending a NAK of packet %d (tick=%u)\n", pRef, pRef->uRcvSeq, NetTick())); + #endif + pPacket->body.uSeq = COMMUDP_RAW_PACKET_NAK; + pPacket->body.uAck = pRef->uRcvSeq; + pPacket->head.iLen = 0; + _CommUDPWrite(pRef, (RawUDPPacketT *)pPacket, &pRef->PeerAddr, uCurrTick); + + // update number of NAKs sent + pRef->Common.naksent += 1; + + return(-1); + } + + // no further processing for empty (ack) packets + if (pPacket->head.iLen == 0) + { + return(0); + } + } + + // add the packet to the buffer + pBuffer = (RawUDPPacketT *) &pRef->pRcvBuf[pRef->iRcvInp]; + // copy the packet + ds_memcpy_s(pBuffer, sizeof(*pBuffer), pPacket, sizeof(*pPacket)); + ds_memcpy(pBuffer->body.aData, pData, pPacket->head.iLen); + + // limit receive access for callbacks + pRef->iCallback += 1; + // add item to receive buffer + pRef->iRcvInp = (pRef->iRcvInp+pRef->iRcvWid) % pRef->iRcvLen; + + // reliable specific processing + if ((pPacket->body.uSeq & COMMUDP_SEQ_MASK) >= COMMUDP_RAW_PACKET_DATA) + { + pRef->uRcvSeq = _CommUDPSeqnDelta(pRef->uRcvSeq, 1); + // add to unacknowledged byte count + pRef->iRcvUnack += pBuffer->head.iLen; + } + + // indicate we got an event + pRef->uGotEvent |= 1; + // let the callback process it + if (pRef->Common.RecvCallback != NULL) + pRef->Common.RecvCallback((CommRef *)pRef, pBuffer->body.aData, pBuffer->head.iLen, pBuffer->head.uWhen); + // release access to receive + pRef->iCallback -= 1; + return(1); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPProcessPoke + + \Description + Penetrate firewall with poke packet + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Version 07/07/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static void _CommUDPProcessPoke(CommUDPRef *pRef, uint32_t uCurrTick) +{ + RawUDPPacketHeadT Packet; + + // send POKE to peer + NetPrintf(("commudp: [%p] sending POKE packet to %a:%d connident=0x%08x clientident=0x%08x\n", pRef, SockaddrInGetAddr(&pRef->PeerAddr), + SockaddrInGetPort(&pRef->PeerAddr), pRef->uConnIdent, pRef->uClientIdent)); + _CommUDPFormatHandshake(pRef, &Packet, COMMUDP_RAW_PACKET_POKE); + _CommUDPWrite(pRef, (RawUDPPacketT *)&Packet, &pRef->PeerAddr, uCurrTick); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPProcessAlive + + \Description + Send a keepalive packet + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +static void _CommUDPProcessAlive(CommUDPRef *pRef, uint32_t uCurrTick) +{ + RawUDPPacketHeadT Packet; + + // see if we should resend most recent packet + if ((pRef->iSndOut != pRef->iSndInp) && (pRef->iSndNxt == pRef->iSndInp)) { + // this shound result in a multi-send + pRef->iSndNxt = (pRef->iSndNxt+pRef->iSndLen-pRef->iSndWid)%pRef->iSndLen; + _CommUDPProcessOutput(pRef, uCurrTick); + return; + } + + // set our packet number + Packet.body.uSeq = pRef->uSndSeq; + // acknowledge packet prior to one we are waiting for + pRef->uRcvAck = pRef->uRcvSeq; + Packet.body.uAck = _CommUDPSeqnDelta(pRef->uRcvSeq, -1); + + // no data means keepalive + Packet.head.iLen = 0; + // send it + #if COMM_PRINT + NetPrintf(("commudp: [%p] sending keep-alive\n", pRef)); + #endif + _CommUDPWrite(pRef, (RawUDPPacketT *)&Packet, &pRef->PeerAddr, uCurrTick); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPFlush + + \Description + Flush output buffer + + \Input *pRef - reference pointer + \Input uLimit - timeout in milliseconds + \Input uCurrTick- current tick + + \Output + uint32_t - number of packets in buffer + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static uint32_t _CommUDPFlush(CommUDPRef *pRef, uint32_t uLimit, uint32_t uCurrTick) +{ + int32_t iNumPackets; + int32_t iIndex; + RawUDPPacketT *pBuffer; + + iNumPackets = (((pRef->iSndInp+pRef->iSndLen)-pRef->iSndOut)%pRef->iSndLen)/pRef->iSndWid; + NetPrintf(("commudp: [%p] flushing %d packets in send queue\n", pRef, iNumPackets)); + + for (iIndex = pRef->iSndOut; iIndex != pRef->iSndNxt; iIndex = (iIndex+pRef->iSndWid)%pRef->iSndLen) + { + pBuffer = (RawUDPPacketT *) &pRef->pSndBuf[iIndex]; + _CommUDPWrite(pRef, pBuffer, &pRef->PeerAddr, uCurrTick); + } + + return(iNumPackets); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPThreadData + + \Description + Take care of data processing + + \Input uCurrTick- current tick + + \Output + int32_t - number of packets received + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPThreadData(uint32_t uCurrTick) +{ + int32_t iLen; + int32_t iCount = 0; + CommUDPRef *pRef; + CommUDPRef *pListen = NULL; + struct sockaddr Sin; + static RawUDPPacketT Packet; + SocketT *pSocket; + uint32_t bConnIdentMatch=0; + uint32_t uLClientIdent=0, uRClientIdent=0; + + // init sockaddr (else unused bytes cause mismatch during ident) + SockaddrInit(&Sin, AF_INET); + + // scan sockets for incoming packet + Packet.head.iLen = -1; + pSocket = NULL; + for (pRef = g_link; pRef != NULL; pRef = pRef->pLink) + { + // make sure we need to scan this one + if ((pRef->pSocket != NULL) && (pRef->pSocket != pSocket)) + { + // see if data is pending + pSocket = pRef->pSocket; + iLen = sizeof(Sin); + iLen = SocketRecvfrom(pSocket, (char *)&Packet.body, sizeof(Packet.body), 0, &Sin, &iLen); + if (iLen > 0) + { + // track recieve overhead + pRef->Common.rcvoverhead += sizeof(Packet.body)-sizeof(Packet.body.aData) + _CommUDPOverhead(pRef, iLen); + pRef->Common.rcvoverhead += CommUDPUtilGetMetaSize(pRef->uMetaType); + // get length and timestamp the packet + Packet.head.iLen = iLen-(sizeof(Packet.body)-sizeof(Packet.body.aData)); + Packet.head.uWhen = SockaddrInGetMisc(&Sin); + // translate seq and ack to host order + Packet.body.uSeq = SocketNtohl(Packet.body.uSeq); + Packet.body.uAck = SocketNtohl(Packet.body.uAck); + // extract and clear meta bits + Packet.head.uMeta = CommUDPUtilGetMetaTypeFromSeq(Packet.body.uSeq); + Packet.body.uSeq &= ~(0x0f << COMMUDP_SEQ_META_SHIFT); + + #if COMM_PRINT > 1 + NetPrintf(("commudp: [%p] seq:0x%08x ack:0x%08x recv %d bytes from %a:%d (head.iLen=%d, meta=%d) at tick=%d\n", + pRef, Packet.body.uSeq, Packet.body.uAck, iLen, SockaddrInGetAddr(&Sin), SockaddrInGetPort(&Sin), Packet.head.iLen, Packet.head.uMeta, Packet.head.uWhen)); + #endif + #if COMM_PRINT > 2 + NetPrintMem(&Packet.body, iLen, "cudp-recv"); + #endif + + if (Packet.body.uSeq == COMMUDP_RAW_PACKET_CONN || (Packet.body.uSeq == COMMUDP_RAW_PACKET_INIT) || (Packet.body.uSeq == COMMUDP_RAW_PACKET_POKE)) + { + NetPrintf(("commudp: [%p] got %s connident=0x%08x len=%d\n", pRef, g_strConnNames[Packet.body.uSeq], Packet.body.uAck, Packet.head.iLen)); + } + // count the packet + ++iCount; + + break; + } + } + } + + // if we have meta chunk use that for matching with our connection ref + if ((Packet.head.iLen >= COMMUDP_RAW_METATYPE1_SIZE) && (Packet.head.uMeta == 1)) + { + int32_t iMetaOffset = ((Packet.body.uSeq == COMMUDP_RAW_PACKET_INIT) || (Packet.body.uSeq == COMMUDP_RAW_PACKET_POKE)) ? 4 : 0; + + // read meta type one chunk data + uRClientIdent = ((uint32_t)Packet.body.aData[0+iMetaOffset])<<24 | ((uint32_t)Packet.body.aData[1+iMetaOffset])<<16 | ((uint32_t)Packet.body.aData[2+iMetaOffset])<<8 | (uint32_t)Packet.body.aData[3+iMetaOffset]; + uLClientIdent = ((uint32_t)Packet.body.aData[4+iMetaOffset])<<24 | ((uint32_t)Packet.body.aData[5+iMetaOffset])<<16 | ((uint32_t)Packet.body.aData[6+iMetaOffset])<<8 | (uint32_t)Packet.body.aData[7+iMetaOffset]; + + // remove metadata from packet + Packet.head.iLen -= COMMUDP_RAW_METATYPE1_SIZE; + if ((Packet.head.iLen-iMetaOffset) > 0) + { + memmove(&Packet.body.aData[0+iMetaOffset], &Packet.body.aData[COMMUDP_RAW_METATYPE1_SIZE+iMetaOffset], Packet.head.iLen-iMetaOffset); + } + #if COMM_PRINT > 2 + NetPrintf(("commudp: [%p] processed metadata chunk\n", pRef)); + #endif + } + + // walk port list and handle processing + for (pRef = g_link; pRef != NULL; pRef = pRef->pLink) + { + // get latest tick + uCurrTick = NetTick(); + + // identify port to handle connection request + if ((pListen == NULL) && (pSocket == pRef->pSocket) && (pRef->eState == LIST) && (Packet.head.iLen >= 0) && + ((Packet.body.uSeq == COMMUDP_RAW_PACKET_INIT) || (Packet.body.uSeq == COMMUDP_RAW_PACKET_CONN)) && (pRef->uConnIdent == Packet.body.uAck)) + { + pListen = pRef; + } + + if (Packet.head.iLen >= 0) + { + if (Packet.head.uMeta == 1) + { + bConnIdentMatch = (uRClientIdent == pRef->uRemClientIdent) && (uLClientIdent == pRef->uClientIdent); + #if COMM_PRINT > 2 + NetPrintf(("commudp: [%p] matching with metadata: src=0x%08x(0x%08x) dst=0x%08x(0x%08x) match=%d\n", pRef, + uRClientIdent, pRef->uRemClientIdent, uLClientIdent, pRef->uClientIdent, bConnIdentMatch)); + #endif + } + else + { + // if this is an INIT or POKE, make sure the connident matches + bConnIdentMatch = ((Packet.body.uSeq == COMMUDP_RAW_PACKET_INIT) || (Packet.body.uSeq == COMMUDP_RAW_PACKET_POKE)) ? (Packet.body.uAck == pRef->uConnIdent) : 1; + } + } + + // see if packet belongs to someone + if ((Packet.head.iLen >= 0) && (pRef->eState != LIST) && (pRef->eState != CLOSE) && (pSocket == pRef->pSocket) && + (SockaddrCompare(&pRef->PeerAddr, &Sin) == 0) && (bConnIdentMatch)) + { + // we got a packet. + pRef->Common.bpackrcvd = TRUE; + + // transition to open state if we're getting data from peer + if ((pRef->eState == CONN) && ((Packet.body.uSeq == COMMUDP_RAW_PACKET_UNREL) || (Packet.body.uSeq == COMMUDP_RAW_PACKET_DATA))) + { + NetPrintf(("commudp: [%p] transitioning to OPEN state due to received data from peer\n", pRef)); + pRef->eState = OPEN; + } + + // do stats + if (pRef->eState == OPEN) + { + pRef->Common.datarcvd += Packet.head.iLen; + pRef->Common.packrcvd += 1; + } + + // process an incoming packet + if ((Packet.body.uSeq == COMMUDP_RAW_PACKET_INIT) || + (Packet.body.uSeq == COMMUDP_RAW_PACKET_POKE) || + (Packet.body.uSeq == COMMUDP_RAW_PACKET_CONN) || + (Packet.body.uSeq == COMMUDP_RAW_PACKET_DISC)) + { + // handle connection setup/teardown + _CommUDPProcessSetup(pRef, &Packet, &Sin, uCurrTick); + } + else if (pRef->eState != OPEN) + { + // ignore the packet + continue; + } + else if (Packet.body.uSeq == COMMUDP_RAW_PACKET_NAK) + { + // remember we got something + pRef->uRecvTick = Packet.head.uWhen; + // resend the missing data + _CommUDPProcessFlow(pRef, (RawUDPPacketHeadT *)&Packet, uCurrTick); + } + else if (Packet.body.uSeq > COMMUDP_SEQ_MULTI_INC) + { + RawUDPPacketHeadT Multi; + uint32_t uOldSeq = pRef->uRcvSeq; + int32_t iExtraSubPktCount = CommUDPUtilGetExtraSubPktCountFromSeq(Packet.body.uSeq); + Multi.head.uWhen = Packet.head.uWhen; + Multi.body.uSeq = _CommUDPSeqnDelta((Packet.body.uSeq & COMMUDP_SEQ_MASK), -iExtraSubPktCount); + Multi.body.uAck = Packet.body.uAck; + // remember we got something + pRef->uRecvTick = Packet.head.uWhen; + // process all the packets + for (; iExtraSubPktCount >= 0; --iExtraSubPktCount) + { + if (iExtraSubPktCount > 0) + { + Packet.head.iLen -= CommUDPUtilDecodeSubpacketSize(Packet.body.aData+Packet.head.iLen-1, &Multi.head.iLen); + } + else + { + Multi.head.iLen = Packet.head.iLen; + } + Packet.head.iLen -= Multi.head.iLen; + + if (Packet.head.iLen < 0) + { + // we just received corrupt data, bail out + break; + } + + // process the ack info + _CommUDPProcessFlow(pRef, &Multi, uCurrTick); + // process the input data and check for missing data (nak) + // (if nak, stop processing so we only send one nak packet) + if (_CommUDPProcessInput(pRef, &Multi, Packet.body.aData+ Packet.head.iLen, uCurrTick) < 0) + { + iExtraSubPktCount = 0; + } + // see if packets were saved + if ((iExtraSubPktCount > 0) && (uOldSeq != pRef->uRcvSeq)) + { + pRef->Common.packsaved += 1; + #if COMM_PRINT + NetPrintf(("commudp: [%p] redundant packet %d prevented loss of packet %d\n", pRef, Packet.body.uSeq & COMMUDP_SEQ_MASK, uOldSeq)); + #endif + uOldSeq = pRef->uRcvSeq; + } + // advance the packet sequence number + + // if we sent a nak, multi.body.seq got wiped and count2 set to 0, so don't increment it. + if (Multi.body.uSeq >= COMMUDP_RAW_PACKET_UNREL) + { + Multi.body.uSeq = _CommUDPSeqnDelta(Multi.body.uSeq, 1); + } + } + } + else + { + // remember we got something + pRef->uRecvTick = Packet.head.uWhen; + // process the ack info + _CommUDPProcessFlow(pRef, (RawUDPPacketHeadT *)&Packet, uCurrTick); + // save the data + _CommUDPProcessInput(pRef, (RawUDPPacketHeadT *)&Packet, Packet.body.aData, uCurrTick); + } + // mark packet as processed + Packet.head.iLen = -1; + } + + // see if we are trying to connect + if ((pRef->eState == CONN) && (uCurrTick-pRef->uSendTick > 1000)) + { + _CommUDPProcessInit(pRef, uCurrTick); + } + + // see if any output for this port + if ((pRef->eState == OPEN) && (pRef->iSndNxt != pRef->iSndInp)) + { + _CommUDPProcessOutput(pRef, uCurrTick); + } + + // check for connection timeout + if ((pRef->eState == OPEN) && (NetTickDiff(uCurrTick, pRef->uRecvTick) > 120*1000) && (NetTickDiff(uCurrTick, pRef->uSendTick) < 2000)) + { + NetPrintf(("commudp: [%p] closing connection due to timeout\n", pRef)); + NetPrintf(("commudp: [%p] tick=%d, rtick=%d, stick=%d\n", pRef, uCurrTick, pRef->uRecvTick, pRef->uSendTick)); + _CommUDPClose(pRef, uCurrTick); + } + + // see if we should run the penetrator + if ((pRef->eState == LIST) && (pRef->PeerAddr.sa_family == AF_INET) && + (uCurrTick > pRef->uSendTick+PENETRATE_RATE)) + { + // penetrate the firewall + _CommUDPProcessPoke(pRef, uCurrTick); + } + + // see if callback needs an idle tick + if ((pRef->uGotEvent == 0) && (uCurrTick > pRef->uIdleTick+250)) + { + pRef->uIdleTick = uCurrTick; + pRef->uGotEvent |= 4; + } + + // do callback if needed + if ((pRef->iCallback == 0) && (pRef->uGotEvent != 0)) + { + // limit callback access + pRef->iCallback += 1; + // callback the handler + if (pRef->pCallProc != NULL) + { + pRef->pCallProc((CommRef *)pRef, pRef->uGotEvent); + } + else if (pRef->eState == OPEN) + { + #if COMM_PRINT + NetPrintf(("commudp: [%p] no upper layer callback\n", pRef)); + #endif + } + // limit callback access + pRef->iCallback -= 1; + // reset event count + pRef->uGotEvent = 0; + } + + // see if we need a keepalive + // do this after callback since callback often generates a + // packet send that eliminates the need for the idle packet + if ((pRef->eState == OPEN) && (pRef->iSndNxt == pRef->iSndInp)) + { + int32_t timeout = NetTickDiff(uCurrTick, pRef->uSendTick); + int32_t reliabletimeout = NetTickDiff(uCurrTick, pRef->uSendReliableTick); + if (((reliabletimeout > BUSY_KEEPALIVE) && (pRef->uRcvAck != pRef->uRcvSeq)) || + ((reliabletimeout > BUSY_KEEPALIVE) && (pRef->iSndInp != pRef->iSndOut)) || + (timeout > IDLE_KEEPALIVE) || + (pRef->iRcvUnack >= UNACK_LIMIT)) + { + // force reset of unack count just in case + pRef->iRcvUnack = 0; + // send a keepalive + _CommUDPProcessAlive(pRef, uCurrTick); + } + } + } + + // check for unclaimed connection packet (port mangling) + if ((Packet.head.iLen >= 0) && (Sin.sa_family == AF_INET) && + ((Packet.body.uSeq == COMMUDP_RAW_PACKET_POKE) || + (Packet.body.uSeq == COMMUDP_RAW_PACKET_INIT) || + (Packet.body.uSeq == COMMUDP_RAW_PACKET_CONN))) + { + uint32_t bIgnored = FALSE; + NetPrintf(("commudp: [%p] received %s packet from %a:%d\n", pRef, g_strConnNames[Packet.body.uSeq], SockaddrInGetAddr(&Sin), SockaddrInGetPort(&Sin))); + + // look for matching reference + for (pRef = g_link; pRef != NULL; pRef = pRef->pLink) + { + if (((pRef->eState == CONN) || (pRef->eState == LIST)) && (pRef->PeerAddr.sa_family == AF_INET)) + { + if (pRef->uConnIdent == Packet.body.uAck) + { + // see if they are trying to connect to poke source but port is mangled + if ((pSocket == pRef->pSocket) && (SockaddrInGetAddr(&pRef->PeerAddr) == SockaddrInGetAddr(&Sin)) && (SockaddrInGetPort(&pRef->PeerAddr) != SockaddrInGetPort(&Sin))) + { + // assume poke source is masq, change port number to correspond + NetPrintf(("commudp: [%p] changing peer to %a:%d (was expecting %a:%d)\n", pRef, SockaddrInGetAddr(&Sin), SockaddrInGetPort(&Sin), + SockaddrInGetAddr(&pRef->PeerAddr), SockaddrInGetPort(&pRef->PeerAddr))); + ds_memcpy_s(&pRef->PeerAddr, sizeof(pRef->PeerAddr), &Sin, sizeof(Sin)); + } + break; + } + else // remember we ignored a packet with non-matching connident + { + bIgnored = TRUE; + } + } + } + if ((pRef == NULL) && (bIgnored == TRUE)) + { + NetPrintf(("commudp: [%p] ignoring %s packet with connident 0x%08x\n", pRef, g_strConnNames[Packet.body.uSeq], Packet.body.uAck)); + } + } + + // see if this was an initial connection request + if ((pListen != NULL) && (Packet.head.iLen >= 0) && ((Packet.body.uSeq == COMMUDP_RAW_PACKET_INIT) || (Packet.body.uSeq == COMMUDP_RAW_PACKET_CONN))) + { + int32_t iPeerAddr = SockaddrInGetAddr(&pListen->PeerAddr); + pRef = pListen; + + if ((pRef->uConnIdent == Packet.body.uAck) && ((iPeerAddr == 0) || (iPeerAddr == SockaddrInGetAddr(&Sin)))) + { + NetPrintf(("commudp: [%p] transitioning to CONN state after receiving %s packet\n", pRef, g_strConnNames[Packet.body.uSeq])); + // change to connecting state + pRef->eState = CONN; + // if we were listening without a peer address, set it now + if (iPeerAddr == 0) + { + ds_memcpy_s(&pRef->PeerAddr, sizeof(pRef->PeerAddr), &Sin, sizeof(Sin)); + } + // process init packet + _CommUDPProcessSetup(pRef, &Packet, &Sin, uCurrTick); + } + } + + return(iCount); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPEvent + + \Description + Main event function + + \Input *pSock - socket + \Input iFlags - flags + \Input *_ref - to be completed + + \Output + int32_t - 0 + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPEvent(SocketT *pSock, int32_t iFlags, void *_ref) +{ + // remember we're in an event call + g_inevent = 1; + + // see if we have exclusive access + if (NetCritTry(&g_crit)) + { + uint32_t uCurrTick = NetTick(); + // clear event counter before calling _CommUDPProcessThreadData to prevent possible unwanted recursion + g_missed = 0; + // process data as long as we get something + while (_CommUDPThreadData(uCurrTick) > 0) + ; + // free access + NetCritLeave(&g_crit); + } + else + { + g_missed += 1; + #if COMM_PRINT + NetPrintf(("commudp: missed %d events\n", g_missed)); + #endif + } + + // leaving event call + g_inevent = 0; + + // done for now + return(0); +} + + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPEnlistRef + + \Description + Add the given ref to the global linked list of references. + + \Input *pRef - ref to add + + \Notes + This also handles initialization of the global critical section global + missed event counter. + + \Version 02/18/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommUDPEnlistRef(CommUDPRef *pRef) +{ + // if first ref, init global critical section + if (g_link == NULL) + { + NetCritInit(&g_crit, "commudp-global"); + g_missed = 0; + g_inevent = 0; + } + + // add to linked list of ports + NetCritEnter(&g_crit); + pRef->pLink = g_link; + g_link = pRef; + NetCritLeave(&g_crit); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPDelistRef + + \Description + Remove the given ref from the global linked list of references. + + \Input *pRef - ref to remove + + \Notes + This also handles destruction of the global critical section. + + \Version 02/18/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _CommUDPDelistRef(CommUDPRef *pRef) +{ + CommUDPRef **ppLink; + + // remove from linked list of ports + NetCritEnter(&g_crit); + for (ppLink = &(g_link); *ppLink != pRef; ppLink = &((*ppLink)->pLink)) + ; + *ppLink = pRef->pLink; + NetCritLeave(&g_crit); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPListen0 + + \Description + Listen for a connection (private version) + + \Input *pRef - reference pointer + \Input *pSock - fallback socket + \Input *pBindAddr - local port + + \Output + int32_t - negative=error, zero=ok + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPListen0(CommUDPRef *pRef, SocketT *pSock, struct sockaddr *pBindAddr) +{ + int32_t iErr; + CommUDPRef *pFind; + struct sockaddr GlueAddr; + + // make sure in valid state + if (pRef->eState != IDLE) + { + SocketClose(pSock); + return(COMM_BADSTATE); + } + + // reset to unresolved + _CommUDPSetSocket(pRef, NULL); + _CommUDPResetTransfer(pRef); + + // setup bind points + ds_memclr(&GlueAddr, sizeof(GlueAddr)); + ds_memclr(&pRef->PeerAddr, sizeof(pRef->PeerAddr)); + +#if defined(DIRTYCODE_PC) + /* + the following line will fill in bindaddr with the IP addr previously set with + SocketControl(... 'ladr' ...). If that selector was never used, then the IP + address field of bindaddr will just be filled with 0. + note: only required for multi-homed system (PC) + */ + SocketInfo(NULL, 'ladr', 0, pBindAddr, sizeof(*pBindAddr)); +#endif + + // see if there is an existing socket bound to this port + for (pFind = g_link; pFind != NULL; pFind = pFind->pLink) + { + // dont check ourselves or unbound sockets + if ((pFind == pRef) || (pFind->pSocket == NULL)) + continue; + + // see where this endpoint is bound + if (SocketInfo(pFind->pSocket, 'bind', 0, &GlueAddr, sizeof(GlueAddr)) < 0) + continue; + + /* + see if the socket can be reused + if the endpoint is virtual: we only compare the ports + if the endpoint is not virtual: + if bindaddr (specifying what we want to bind to) has ipaddr=0, we only compare the ports + otherwise we compare the full sockaddr structs (i.e. family, port, addr) + */ + if (SockaddrInGetPort(pBindAddr) == SockaddrInGetPort(&GlueAddr)) + { + if ((SocketInfo(pFind->pSocket, 'virt', 0, NULL, 0) == TRUE) || + (SockaddrInGetAddr(pBindAddr) == 0) || + (SockaddrCompare(pBindAddr, &GlueAddr) == 0)) + { + // share the socket + _CommUDPSetSocket(pRef, pFind->pSocket); + + // dont need supplied socket + SocketClose(pSock); + break; + } + } + } + + // create socket if no existing one + if (pFind == NULL) + { + // bind the address to the socket + iErr = SocketBind(pSock, pBindAddr, sizeof(*pBindAddr)); + if (iErr < 0) + { + NetPrintf(("commudp: [%p] bind to %d failed with %d\n", pRef, SockaddrInGetPort(pBindAddr), iErr)); + SockaddrInSetPort(pBindAddr, 0); + if ((iErr = SocketBind(pSock, pBindAddr, sizeof(*pBindAddr))) < 0) + { + NetPrintf(("commudp: [%p] bind to 0 failed with result %d\n", pRef, iErr)); + pRef->eState = DEAD; + SocketClose(pSock); + return((iErr == SOCKERR_INVALID) ? COMM_PORTBOUND : COMM_UNEXPECTED); + } + } + // use the supplied socket + _CommUDPSetSocket(pRef, pSock); + + // setup for socket events + SocketCallback(pSock, CALLB_RECV, 100, NULL, &_CommUDPEvent); + } + + // put into listen mode + pRef->eState = LIST; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function _CommUDPConnect0 + + \Description + Initiate a connection to a peer (private version) + + \Input *pRef - reference pointer + \Input *pSock - socket + \Input *pPeerAddr - peer address + + \Output + int32_t - negative=error, zero=ok + + \Notes + Does not currently perform dns translation + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +static int32_t _CommUDPConnect0(CommUDPRef *pRef, SocketT *pSock, struct sockaddr *pPeerAddr) +{ + // make sure in valid state + if (pRef->eState != IDLE) { + SocketClose(pSock); + return(COMM_BADSTATE); + } + + // reset to unresolved + _CommUDPSetSocket(pRef, NULL); + _CommUDPResetTransfer(pRef); + + // setup target info + ds_memcpy_s(&pRef->PeerAddr, sizeof(pRef->PeerAddr), pPeerAddr, sizeof(*pPeerAddr)); + + // save the socket + _CommUDPSetSocket(pRef, pSock); + + // setup for callbacks + SocketCallback(pSock, CALLB_RECV, 100, NULL, &_CommUDPEvent); + + // change the state + pRef->eState = CONN; + return(0); +} + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function CommUDPConstruct + + \Description + Construct the class + + \Input iMaxWid - max record width + \Input iMaxInp - input packet buffer size + \Input iMaxOut - output packet buffer size + + \Output + CommUDPRef - construct pointer + + \Notes + Initialized winsock for first class. also creates linked + list of all current instances of the class and worker thread + to do most udp stuff. + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +CommUDPRef *CommUDPConstruct(int32_t iMaxWid, int32_t iMaxInp, int32_t iMaxOut) +{ + CommUDPRef *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate class storage + pRef = DirtyMemAlloc(sizeof(*pRef), COMMUDP_MEMID, iMemGroup, pMemGroupUserData); + if (pRef == NULL) + return(NULL); + ds_memclr(pRef, sizeof(*pRef)); + pRef->Common.memgroup = iMemGroup; + pRef->Common.memgrpusrdata = pMemGroupUserData; + + // initialize the callback routines + pRef->Common.Construct = (CommAllConstructT *)CommUDPConstruct; + pRef->Common.Destroy = (CommAllDestroyT *)CommUDPDestroy; + pRef->Common.Resolve = (CommAllResolveT *)CommUDPResolve; + pRef->Common.Unresolve = (CommAllUnresolveT *)CommUDPUnresolve; + pRef->Common.Listen = (CommAllListenT *)CommUDPListen; + pRef->Common.Unlisten = (CommAllUnlistenT *)CommUDPUnlisten; + pRef->Common.Connect = (CommAllConnectT *)CommUDPConnect; + pRef->Common.Unconnect = (CommAllUnconnectT *)CommUDPUnconnect; + pRef->Common.Callback = (CommAllCallbackT *)CommUDPCallback; + pRef->Common.Control = (CommAllControlT *)CommUDPControl; + pRef->Common.Status = (CommAllStatusT *)CommUDPStatus; + pRef->Common.Tick = (CommAllTickT *)CommUDPTick; + pRef->Common.Send = (CommAllSendT *)CommUDPSend; + pRef->Common.Peek = (CommAllPeekT *)CommUDPPeek; + pRef->Common.Recv = (CommAllRecvT *)CommUDPRecv; + + // remember max packet width + pRef->Common.maxwid = iMaxWid; + pRef->Common.maxinp = iMaxInp; + pRef->Common.maxout = iMaxOut; + + // allocate the buffers + pRef->iRcvWid = sizeof(RawUDPPacketT)-sizeof(((RawUDPPacketT *)0)->body.aData)+iMaxWid+COMMUDP_MAX_METALEN; + pRef->iRcvWid = (pRef->iRcvWid +3) & 0x7ffc; + pRef->iRcvLen = pRef->iRcvWid * iMaxInp; + pRef->pRcvBuf = (char *)DirtyMemAlloc(pRef->iRcvLen, COMMUDP_MEMID, pRef->Common.memgroup, pRef->Common.memgrpusrdata); + pRef->iSndWid = sizeof(RawUDPPacketT)-sizeof(((RawUDPPacketT *)0)->body.aData)+iMaxWid+COMMUDP_MAX_METALEN; + pRef->iSndWid = (pRef->iSndWid+3) & 0x7ffc; + pRef->iSndLen = pRef->iSndWid * iMaxOut; + pRef->pSndBuf = (char *)DirtyMemAlloc(pRef->iSndLen, COMMUDP_MEMID, pRef->Common.memgroup, pRef->Common.memgrpusrdata); + + // reset the socket + _CommUDPSetSocket(pRef, NULL); + + // reset peer address + ds_memclr(&pRef->PeerAddr, sizeof(pRef->PeerAddr)); + + // reset the state + pRef->eState = IDLE; + pRef->uConnIdent = 0; + pRef->uUnackLimit = UNACK_LIMIT; + pRef->uRedundantLimit = REDUNDANT_LIMIT_DEFAULT; + + // add to port list + _CommUDPEnlistRef(pRef); + + return(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPDestroy + + \Description + Destruct the class + + \Input *pRef - reference pointer + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +void CommUDPDestroy(CommUDPRef *pRef) +{ + CommUDPRef *pFind; + uint32_t uCurrTick = NetTick(); + + // flush final data + _CommUDPFlush(pRef, 200, uCurrTick); + + // remove from port list + _CommUDPDelistRef(pRef); + + // if port is open, close it + if (pRef->eState == OPEN) + _CommUDPClose(pRef, uCurrTick); + + // kill the socket + if (pRef->pSocket != NULL) { + // see if socket shared + for (pFind = g_link; pFind != NULL; pFind = pFind->pLink) { + if (pFind->pSocket == pRef->pSocket) + break; + } + // if we are only user of this socket + if (pFind == NULL) { + SocketClose(pRef->pSocket); + _CommUDPSetSocket(pRef, NULL); + } + } + + // if last ref, destroy global critical section + if (g_link == NULL) + { + NetCritKill(&g_crit); + } + + // release resources + DirtyMemFree(pRef->pRcvBuf, COMMUDP_MEMID, pRef->Common.memgroup, pRef->Common.memgrpusrdata); + DirtyMemFree(pRef->pSndBuf, COMMUDP_MEMID, pRef->Common.memgroup, pRef->Common.memgrpusrdata); + DirtyMemFree(pRef, COMMUDP_MEMID, pRef->Common.memgroup, pRef->Common.memgrpusrdata); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPCallback + + \Description + Set upper layer callback + + \Input *pRef - reference pointer + \Input *pCallback - socket generating callback + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +void CommUDPCallback(CommUDPRef *pRef, void (*pCallback)(void *pRef, int32_t iEvent)) +{ + NetCritEnter(&g_crit); + pRef->pCallProc = pCallback; + pRef->uGotEvent |= 2; + NetCritLeave(&g_crit); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPResolve + + \Description + Resolve an address + + \Input *pRef - endpoint + \Input *pAddr - resolve address + \Input *pBuf - target buffer + \Input iLen - target length (min 64 bytes) + \Input cDiv - divider char + + \Output + int32_t - <0=error, 0=complete (COMM_NOERROR), >0=in progress (COMM_PENDING) + + \Notes + Target list is always double null terminated allowing null + to be used as the divider character if desired. when COMM_PENDING + is returned, target buffer is set to "~" until completion. + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +int32_t CommUDPResolve(CommUDPRef *pRef, const char *pAddr, char *pBuf, int32_t iLen, char cDiv) +{ + NetPrintf(("commudp: [%p] resolve functionality not supported\n", pRef)); + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUnresolve + + \Description + Stop the resolver + + \Input *pRef - reference pointer + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +void CommUDPUnresolve(CommUDPRef *pRef) +{ +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUnlisten + + \Description + Stop listening + + \Input *pRef - reference pointer + + \Output + int32_t - negative=error, zero=ok + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t CommUDPUnlisten(CommUDPRef *pRef) +{ + uint32_t uCurrTick = NetTick(); + + // flush final data + _CommUDPFlush(pRef, 200, uCurrTick); + + // never close listening socket (it is shared) + if (pRef->eState == LIST) + { + _CommUDPSetSocket(pRef, NULL); + } + + // get rid of socket if presernt + if (pRef->pSocket != NULL) + { + // attempt to close socket + _CommUDPClose(pRef, uCurrTick); + // done with socket + SocketClose(pRef->pSocket); + _CommUDPSetSocket(pRef, NULL); + } + + // return to idle mode + pRef->eState = IDLE; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUnconnect + + \Description + Terminate a connection + + \Input *pRef - reference pointer + + \Output + int32_t - negative=error, zero=ok + + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +int32_t CommUDPUnconnect(CommUDPRef *pRef) +{ + uint32_t uCurrTick = NetTick(); + + // flush final data + _CommUDPFlush(pRef, 200, uCurrTick); + + // never close listening socket (it is shared) + if (pRef->eState == LIST) + { + _CommUDPSetSocket(pRef, NULL); + } + + // get rid of socket if presernt + if (pRef->pSocket != NULL) + { + // attempt to close socket + _CommUDPClose(pRef, uCurrTick); + // done with socket + SocketClose(pRef->pSocket); + _CommUDPSetSocket(pRef, NULL); + } + + // return to idle mode + pRef->eState = IDLE; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPStatus + + \Description + Return current stream status + + \Input *pRef - reference pointer + + \Output + int32_t - CONNECTING, OFFLINE, ONLINE or FAILURE + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +int32_t CommUDPStatus(CommUDPRef *pRef) +{ + // return state + if ((pRef->eState == CONN) || (pRef->eState == LIST)) + return(COMM_CONNECTING); + if ((pRef->eState == IDLE) || (pRef->eState == CLOSE)) + return(COMM_OFFLINE); + if (pRef->eState == OPEN) + return(COMM_ONLINE); + return(COMM_FAILURE); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPControl + + \Description + Set connection behavior. + + \Input *pRef - reference pointer + \Input iControl - control selector + \Input iValue - selector specfic + \Input *pValue - selector specific + + \Output + int32_t - negative=error, else selector result + + \Notes + iControl can be one of the following: + + \verbatim + 'clid' - client identifier + 'meta' - set metatype (default=0) + 'rcid' - remote client identifier + 'rlmt' - redundant packet size limit (default = REDUNDANT_LIMIT_DEFAULT) + 'ulmt' - unacknowledged packet limit (2k default) + \endverbatim + + \Version 02/20/2007 (jbrookes) +*/ +/*************************************************************************************************F*/ +int32_t CommUDPControl(CommUDPRef *pRef, int32_t iControl, int32_t iValue, void *pValue) +{ + if (iControl == 'clid') + { + pRef->uClientIdent = iValue; + return(0); + } + if (iControl == 'meta') + { + NetPrintf(("commudp: [%p] setting metatype=%d\n", pRef, iValue)); + pRef->uMetaType = iValue; + return(0); + } + if (iControl == 'rcid') + { + pRef->uRemClientIdent = iValue; + return(0); + } + if (iControl == 'rlmt') + { + const int32_t iMaxLimit = sizeof(((RawUDPPacketT *)0)->body.aData); + if (iValue == 0) + { + iValue = REDUNDANT_LIMIT_DEFAULT; + } + if (iValue >= iMaxLimit) + { + iValue = iMaxLimit; + } + NetPrintf(("commudp: [%p] redundant limit changed from %d bytes to %d bytes\n", pRef, pRef->uRedundantLimit, iValue)); + pRef->uRedundantLimit = iValue; + return(0); + } + if (iControl == 'ulmt') + { + pRef->uUnackLimit = iValue; + NetPrintf(("commudp: [%p] setting ulimit to %d bytes\n", pRef, pRef->uUnackLimit)); + return(0); + } + // unhandled; pass through to socket module if we have a socket + if (pRef->pSocket != NULL) + { + return(SocketControl(pRef->pSocket, iControl, iValue, pValue, NULL)); + } + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPTick + + \Description + Return current tick + + \Input *pRef - reference pointer + + \Output + uint32_t - elaped milliseconds + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +uint32_t CommUDPTick(CommUDPRef *pRef) +{ + return(NetTick()); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPSend + + \Description + Send a packet + + \Input *pRef - reference pointer + \Input *pBuffer - pointer to data + \Input iLength - length of data + \Input uFlags - COMM_FLAGS_* + + \Output + int32_t - negative=error, zero=buffer full (temp fail), positive=queue position (ok) + + \Notes + Zero length packets may not be sent (they are used for buffer query) + + \Version 12/04/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t CommUDPSend(CommUDPRef *pRef, const void *pBuffer, int32_t iLength, uint32_t uFlags) +{ + RawUDPPacketT PacketBuffer, *pPacket; + uint32_t uCurrTick = NetTick(); + int32_t iPos, iMetaLen, iResult=1; // result=1 is default (min queue) result + + // make sure port is open + if (pRef->eState != OPEN) + { + return(COMM_BADSTATE); + } + + // if metachunk enabled, adjust size + iMetaLen = CommUDPUtilGetMetaSize(pRef->uMetaType); + + // return error for oversized packets + if ((iLength+iMetaLen) > (signed)(pRef->iSndWid-(sizeof(RawUDPPacketT)-sizeof(((RawUDPPacketT *)0)->body.aData)))) + { + NetPrintf(("commudp: [%p] oversized packet send (%d bytes)\n", pRef, iLength)); + return(COMM_MINBUFFER); + } + + /* check for zero-length packets, which cannot be sent (they are used for acks); instead, treat them as successful, + which means the queue position is returned */ + if (iLength == 0) + { + iPos = (((pRef->iSndInp+pRef->iSndLen)-pRef->iSndOut)%pRef->iSndLen)/pRef->iSndWid; + return(iPos+1); + } + + // get packet buffer + if (uFlags & COMM_FLAGS_UNRELIABLE) + { + // unreliable packets are staged locally and flushed immediately + pPacket = &PacketBuffer; + } + else + { + // make sure output buffer isn't full + if ((pRef->iSndInp + pRef->iSndWid) % pRef->iSndLen == pRef->iSndOut) + { + NetPrintfVerbose((COMM_PRINT, 0, "commudp: [%p] send overflow (connident=0x%08x)\n", pRef, pRef->uConnIdent)); + return(0); + } + // reference packet buffer in output queue + pPacket = (RawUDPPacketT *) &(pRef->pSndBuf[pRef->iSndInp]); + } + + // copy the packet to the buffer + ds_memcpy(pPacket->body.aData, pBuffer, iLength); + pPacket->head.iLen = iLength; + // set the send time + pPacket->head.uWhen = uCurrTick; + + // handle unreliable send + if (uFlags & COMM_FLAGS_UNRELIABLE) + { + int32_t iErr = -1; + NetCritEnter(&g_crit); + + // set up and send an unreliable packet + pPacket->body.uSeq = pRef->uUnreliableSndSeq; + pPacket->body.uAck = _CommUDPSeqnDelta(pRef->uRcvSeq, -1); + if (uFlags & COMM_FLAGS_BROADCAST) + { + struct sockaddr PeerAddr; + ds_memcpy_s(&PeerAddr, sizeof(PeerAddr), &pRef->PeerAddr, sizeof(pRef->PeerAddr)); + SockaddrInSetAddr(&PeerAddr, 0xffffffff); + iErr = _CommUDPWrite(pRef, pPacket, &PeerAddr, uCurrTick); + } + else + { + iErr = _CommUDPWrite(pRef, pPacket, &pRef->PeerAddr, uCurrTick); + } + + // calculate new sequence number + if (++pRef->uUnreliableSndSeq >= COMMUDP_RAW_PACKET_DATA) + { + pRef->uUnreliableSndSeq = COMMUDP_RAW_PACKET_UNREL; + } + + NetCritLeave(&g_crit); + // if send failed, return buffer-full(0) or error + if (iErr < 0) + { + iResult = (pRef->uSndErr == SOCKERR_NONE) ? 0 : iErr; + } + } + else + { + // set the data fields + pPacket->body.uSeq = pRef->uSndSeq; + pRef->uSndSeq = _CommUDPSeqnDelta(pRef->uSndSeq, 1); + + pPacket->body.uAck = _CommUDPSeqnDelta(pRef->uRcvSeq, -1); + + // add the packet to the queue + pRef->iSndInp = (pRef->iSndInp+pRef->iSndWid) % pRef->iSndLen; + iPos = (((pRef->iSndInp+pRef->iSndLen)-pRef->iSndOut)%pRef->iSndLen)/pRef->iSndWid; + + // try to send packet immediately if buffer is at least half empty + if (iPos < (pRef->Common.maxout/2)) + { + NetCritEnter(&g_crit); + _CommUDPProcessOutput(pRef, uCurrTick); + + // process incoming if we missed event + if (g_missed != 0) + { + // clear event counter before calling _CommUDPProcessThreadData to prevent possible unwanted recursion + g_missed = 0; + // process data as long as we get something + while (_CommUDPThreadData(uCurrTick) > 0) + ; + NetPrintfVerbose((COMM_PRINT, 0, "commudp: [%p] processing %d after send\n", pRef, g_missed)); + } + NetCritLeave(&g_crit); + } + // return buffer depth + if (iPos > 0) + { + iResult = iPos; + } + } + + return(iResult); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPPeek + + \Description + Peek at waiting packet + + \Input *pRef - reference pointer + \Input *pTarget - target buffer + \Input iLength - buffer length + \Input *pWhen - tick received at + + \Output + int32_t - negative=nothing pending, else packet length + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +int32_t CommUDPPeek(CommUDPRef *pRef, void *pTarget, int32_t iLength, uint32_t *pWhen) +{ + RawUDPPacketT *pPacket; + + // see if a packet is available + if (pRef->iRcvOut == pRef->iRcvInp) + return(COMM_NODATA); + + // point to the packet + pPacket = (RawUDPPacketT *) &(pRef->pRcvBuf[pRef->iRcvOut]); + + // copy data? + if (iLength > 0) + { + // make sure enough space is available + if (iLength < pPacket->head.iLen) + return(COMM_MINBUFFER); + + // copy over the data portion + ds_memcpy(pTarget, pPacket->body.aData, pPacket->head.iLen); + } + + // get the timestamp + if (pWhen != NULL) + *pWhen = pPacket->head.uWhen; + + // return packet data length + return(pPacket->head.iLen); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPRecv + + \Description + Receive a packet from the buffer + + \Input *pRef - reference pointer + \Input *pTarget - target buffer + \Input iLength - buffer length + \Input *pWhen - tick received at + + \Output + int32_t - negative=error, else packet length + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +int32_t CommUDPRecv(CommUDPRef *pRef, void *pTarget, int32_t iLength, uint32_t *pWhen) +{ + // use peek to remove the data + int32_t iLen = CommUDPPeek(pRef, pTarget, iLength, pWhen); + if (iLen >= 0) + pRef->iRcvOut = (pRef->iRcvOut+pRef->iRcvWid)%pRef->iRcvLen; + // all done + return(iLen); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPListen + + \Description + Listen for a connection + + \Input *pRef - reference pointer + \Input *pAddr - port to listen on (only :port portion used) + + \Output + int32_t - negative=error, zero=ok + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +int32_t CommUDPListen(CommUDPRef *pRef, const char *pAddr) +{ + int32_t iErr, iListenPort, iConnPort; + uint32_t uPoke; + SocketT *pSock; + struct sockaddr BindAddr; + + // setup bind points + SockaddrInit(&BindAddr, AF_INET); + + // parse at least port + if ((SockaddrInParse2(&uPoke, &iListenPort, &iConnPort, pAddr) & 0x2) != 0x2) + { + return(COMM_BADADDRESS); + } + SockaddrInSetPort(&BindAddr, iListenPort); + + // create socket in case its needed + pSock = SocketOpen(AF_INET, SOCK_DGRAM, 0); + if (pSock == NULL) + { + return(COMM_NORESOURCE); + } + + // let common code finish up + iErr = _CommUDPListen0(pRef, pSock, &BindAddr); + + // set connection identifier + _CommUDPSetConnID(pRef, pAddr); + + NetPrintf(("commudp: [%p] listen err=%d, bind=%d, connident=0x%08x\n", pRef, iErr, iListenPort, pRef->uConnIdent)); + + // see if we should setup peer address + if ((iErr == 0) && (uPoke != 0)) + { + if (iConnPort == 0) + { + iConnPort = iListenPort+1; + } + + NetPrintf(("commudp: [%p] poke address=%08x:%d\n", pRef, uPoke, iConnPort)); + SockaddrInit(&pRef->PeerAddr, AF_INET); + SockaddrInSetAddr(&pRef->PeerAddr, uPoke); + SockaddrInSetPort(&pRef->PeerAddr, iConnPort); + } + + // clear any previous receive errors + pRef->uSndErr = 0; + return(iErr); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPConnect + + \Description + Initiate a connection to a peer + + \Input *pRef - reference pointer + \Input *pAddr - address in ip-address:port form + + \Output + int32_t - negative=error, zero=ok + + \Notes + Does not currently perform dns translation + + \Version 12/04/00 (GWS) + +*/ +/*************************************************************************************************F*/ +int32_t CommUDPConnect(CommUDPRef *pRef, const char *pAddr) +{ + int32_t iErr, iConnPort, iListenPort; + uint32_t uAddr; + CommUDPRef *pFind; + SocketT *sock; + struct sockaddr BindAddr, GlueAddr, PeerAddr; + + // setup target info + SockaddrInit(&PeerAddr, AF_INET); + SockaddrInit(&BindAddr, AF_INET); + + // parse addr - make sure we have at least an address and port + iErr = SockaddrInParse2(&uAddr, &iListenPort, &iConnPort, pAddr); + if ((iErr & 3) != 3) + { + return(COMM_BADADDRESS); + } + + // if we don't have an alternate connect port, connect=listen + if (iConnPort == 0) + { + iConnPort = iListenPort++; + } + + // set listen port + SockaddrInSetPort(&BindAddr, iListenPort); + + // set connection identifier + _CommUDPSetConnID(pRef, pAddr); + NetPrintf(("commudp: [%p] connect addr=%08x, bind=%d, peer=%d connident=0x%08x\n", + pRef, uAddr, iListenPort, iConnPort, pRef->uConnIdent)); + +#if defined(DIRTYCODE_PC) + /* + the following line will fill in bindaddr with the IP addr previously set with + SocketControl(... 'ladr' ...). If that selector was never used, then the IP + address field of bindaddr will just be filled with 0. + note: only required for multi-homed system (PC) + */ + SocketInfo(NULL, 'ladr', 0, &BindAddr, sizeof(BindAddr)); +#endif + + // see if there is an existing socket bound to this port + for (pFind = g_link, sock = NULL; pFind != NULL; pFind = pFind->pLink) + { + // dont check ourselves or unbound sockets + if ((pFind == pRef) || (pFind->pSocket == NULL)) + continue; + + // see where this endpoint is bound + if (SocketInfo(pFind->pSocket, 'bind', 0, &GlueAddr, sizeof(GlueAddr)) < 0) + continue; + + /* + see if the socket can be reused + if the endpoint is virtual: we only compare the ports + if the endpoint is not virtual: + if bindaddr (specifying what we want to bind to) has ipaddr=0, we only compare the ports + otherwise we compare the full sockaddr structs (i.e. family, port, addr) + */ + if (SockaddrInGetPort(&BindAddr) == SockaddrInGetPort(&GlueAddr)) + { + if ((SocketInfo(pFind->pSocket, 'virt', 0, NULL, 0) == TRUE) || + (SockaddrInGetAddr(&BindAddr) == 0) || + (SockaddrCompare(&BindAddr, &GlueAddr) == 0)) + { + // share the socket + sock = pFind->pSocket; + break; + } + } + } + + // create the actual socket + if (sock == NULL) + { + sock = SocketOpen(AF_INET, SOCK_DGRAM, 0); + if (sock == NULL) + { + return(COMM_NORESOURCE); + } + + // bind socket + if ((iErr = SocketBind(sock, &BindAddr, sizeof(BindAddr))) < 0) + { + NetPrintf(("commudp: [%p] bind to %d failed with %d\n", pRef, iListenPort, iErr)); + SockaddrInSetPort(&BindAddr, 0); + if ((iErr = SocketBind(sock, &BindAddr, sizeof(BindAddr))) < 0) + { + NetPrintf(("commudp: [%p] bind to 0 failed with result %d\n", pRef, iErr)); + SocketClose(sock); + return(COMM_UNEXPECTED); + } + else + { + SocketInfo(sock, 'bind', 0, &BindAddr, sizeof(BindAddr)); + NetPrintf(("commudp: [%p] bound socket to port %d\n", pRef, SockaddrInGetPort(&BindAddr))); + } + } + } + + // set connect sockaddr + SockaddrInSetAddr(&PeerAddr, uAddr); + SockaddrInSetPort(&PeerAddr, iConnPort); + + // clear any previous receive errors + pRef->uSndErr = 0; + + // pass to common handler + return(_CommUDPConnect0(pRef, sock, &PeerAddr)); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/comm/commudputil.c b/r5dev/thirdparty/dirtysdk/source/comm/commudputil.c new file mode 100644 index 00000000..58b308a3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/comm/commudputil.c @@ -0,0 +1,194 @@ +/*H*************************************************************************************************/ +/*! + + \File commudputil.c + + \Description + CommUdp knowledge to be shared with gameserver implementation. + + \Copyright + Copyright (c) 2006-2017 Electronic Arts Inc. + + \Version 1.0 09/01/2017 (mclouatre) First Version +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include "DirtySDK/comm/commudputil.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +/*** Private Functions *****************************************************************/ + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUtilEncodeSubpacketSize + + \Description + Encodes a subpacket size field. A size of 0-250 is encoded in a single byte, while a size + of 251-1530 requires two. 1530 bytes is the maximum allowed size of a subpacket. + + \Input *pBuf - buffer holding encoded subpacket size (NULL to just calculate encoded size) + \Input uVal - subpacket size + + \Output + uint32_t - number of bytes that encoded size consumed (1 or 2) + + \Notes + CommUDP uses a special encoding scheme to encode subpacket sizes in one or two bytes. The + original version of the protocol stored subpacket sizes up to 250 (the maximum previously + allowed) in a single byte at the end of the subpacket data, and the newer scheme was + designed to retain compatibility with that method. Subpacket sizes up to 250 bytes are + therefore still encoded in a single byte that immediately follows the subpacket data. A + value of 251 through 255 is used to represent larger subpacket sizes, which are encoded + with an additional byte, stored immediately previous to the final byte. + + The following table shows how the subpacket size is encoded, with the first column + containing the value of the final byte B0, and B1 representing the previous byte, if + present. + + \verbatim + base10 equation to equivalent expression in format: resulting + val of get sub-pkt size base10 + base2_prefix [base16 range] sub-pkt size + ----------- ------------------ ------------------------------------ ------------ + 0x00-0xFA -> = [0x00-0xFA] = 0 - 250 + 0xFB -> 251 + 0 + = 251 + 00 [0x00-0xFF] = 251 - 506 + 0xFC -> 251 + 256 + = 251 + 01 [0x00-0xFF] = 507 - 762 + 0xFD -> 251 + 512 + = 251 + 10 [0x00-0xFF] = 763 - 1018 + 0xFE -> 251 + 768 + = 251 + 11 [0x00-0xFF] = 1019 - 1274 + 0xFF -> 251 + 1024 + = 251 + 100 [0x00-0xFF] = 1275 - 1530 + \endverbatim + + \Version 10/30/2015 (jbrookes) +*/ +/*************************************************************************************************F*/ +uint32_t CommUDPUtilEncodeSubpacketSize(uint8_t *pBuf, uint32_t uVal) +{ + uint32_t uOffset, uTemp; + if (uVal > 250) + { + if (pBuf != NULL) + { + uTemp = uVal - 251; + pBuf[1] = (uTemp >> 8) + 251; + pBuf[0] = (uint8_t)(uTemp & 0xff); + } + uOffset = 2; + } + else + { + if (pBuf != NULL) + { + pBuf[0] = (uint8_t)uVal; + } + uOffset = 1; + } + return(uOffset); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUtilDecodeSubpacketSize + + \Description + Decodes a subpacket size field (see _CommUDPEncodeSubpacketSize for notes) + + \Input *pBuf - buffer holding encoded subpacket size + \Input *pVal - [out] storage for decoded subpacket size + + \Output + uint32_t - number of bytes encoded size consumed (1 or 2) + + \Version 10/30/2015 (jbrookes) +*/ +/*************************************************************************************************F*/ +uint32_t CommUDPUtilDecodeSubpacketSize(const uint8_t *pBuf, int32_t *pVal) +{ + uint32_t uOffset, uTemp; + if (pBuf[0] > 250) + { + pBuf -= 1; + uTemp = pBuf[1] - 251; + *pVal = 251 + (pBuf[0] | (uTemp << 8)); + uOffset = 2; + } + else + { + *pVal = pBuf[0]; + uOffset = 1; + } + return(uOffset); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUtilGetMetaTypeFromSeq + + \Description + Extracts metatype from host-ordered commupd header seq field (RawUDPPacketT.body.uSeq) + + \Input uSeq - host-ordered commupd header seq field + + \Output + uint32_t - extracted meta type value + + \Version 09/01/2017 (mclouatre) +*/ +/*************************************************************************************************F*/ +uint32_t CommUDPUtilGetMetaTypeFromSeq(uint32_t uSeq) +{ + return((uSeq >> COMMUDP_SEQ_META_SHIFT) & 0xf); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUtilGetMetaSize + + \Description + Returns the metadata size for a given metadata type. + + \Input uMetaType - metadata type + + \Output + uint32_t - metadata size + + \Version 09/01/2017 (mclouatre) +*/ +/*************************************************************************************************F*/ +uint32_t CommUDPUtilGetMetaSize(uint32_t uMetaType) +{ + return(uMetaType ? COMMUDP_RAW_METATYPE1_SIZE : 0); +} + +/*F*************************************************************************************************/ +/*! + \Function CommUDPUtilGetExtraSubPktCountFromSeq + + \Description + Extracts the number of extra subpackets (excluding the main subpacket) from host-ordered + commupd header seq field (RawUDPPacketT.body.uSeq) + + \Input uSeq - host-ordered commupd header seq field + + \Output + uint32_t - extracted extra subpackets count (does not include the main subpacket) + + \Version 09/01/2017 (mclouatre) +*/ +/*************************************************************************************************F*/ +uint32_t CommUDPUtilGetExtraSubPktCountFromSeq(uint32_t uSeq) +{ + return(uSeq >> COMMUDP_SEQ_MULTI_SHIFT); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptaes.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptaes.c new file mode 100644 index 00000000..6a2bd2df --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptaes.c @@ -0,0 +1,722 @@ +/*H********************************************************************************/ +/*! + \File cryptaes.c + + \Description + An implementation of the AES-128 and AES-256 cipher, based on the FIPS + standard, intended for use with the TLS AES cipher suites. + + This implementation deliberately uses the naming conventions from the + standard where possible in order to aid comprehension. + + \Notes + References: + FIPS197 standard: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + + \Copyright + Copyright (c) 2011 Electronic Arts Inc. + + \Version 01/20/2011 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/crypt/cryptaes.h" + +/*** Defines **********************************************************************/ + +/* + 32-bit word rotation +*/ +#define CRYPTAES_ROT(_x, _a) (((_x) << (_a)) | ((_x) >> (32-(_a)))) + +#define CRYPTAES_ROT1(_x) CRYPTAES_ROT((_x), 24) +#define CRYPTAES_ROT2(_x) CRYPTAES_ROT((_x), 16) +#define CRYPTAES_ROT3(_x) CRYPTAES_ROT((_x), 8) + +/* + Read/write big-endian words +*/ +#define CRYPTAES_RDWORD(_ptr) ((((uint32_t)(_ptr)[0]) << 24) | ((uint32_t)((_ptr)[1]) << 16) | (((uint32_t)(_ptr)[2]) << 8) | ((uint32_t)(_ptr)[3])) +#define CRYPTAES_WRWORD(_x, _ptr) ((_ptr)[3] = (uint8_t)(_x),\ + (_ptr)[2] = (uint8_t)((_x)>>8),\ + (_ptr)[1] = (uint8_t)((_x)>>16),\ + (_ptr)[0] = (uint8_t)((_x)>>24)) + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// AES s-box table +static const uint8_t _CryptAes_Sbox[256] = +{ + 0x63,0x7C,0x77,0x7B, 0xF2,0x6B,0x6F,0xC5, 0x30,0x01,0x67,0x2B, 0xFE,0xD7,0xAB,0x76, + 0xCA,0x82,0xC9,0x7D, 0xFA,0x59,0x47,0xF0, 0xAD,0xD4,0xA2,0xAF, 0x9C,0xA4,0x72,0xC0, + 0xB7,0xFD,0x93,0x26, 0x36,0x3F,0xF7,0xCC, 0x34,0xA5,0xE5,0xF1, 0x71,0xD8,0x31,0x15, + 0x04,0xC7,0x23,0xC3, 0x18,0x96,0x05,0x9A, 0x07,0x12,0x80,0xE2, 0xEB,0x27,0xB2,0x75, + 0x09,0x83,0x2C,0x1A, 0x1B,0x6E,0x5A,0xA0, 0x52,0x3B,0xD6,0xB3, 0x29,0xE3,0x2F,0x84, + 0x53,0xD1,0x00,0xED, 0x20,0xFC,0xB1,0x5B, 0x6A,0xCB,0xBE,0x39, 0x4A,0x4C,0x58,0xCF, + 0xD0,0xEF,0xAA,0xFB, 0x43,0x4D,0x33,0x85, 0x45,0xF9,0x02,0x7F, 0x50,0x3C,0x9F,0xA8, + 0x51,0xA3,0x40,0x8F, 0x92,0x9D,0x38,0xF5, 0xBC,0xB6,0xDA,0x21, 0x10,0xFF,0xF3,0xD2, + 0xCD,0x0C,0x13,0xEC, 0x5F,0x97,0x44,0x17, 0xC4,0xA7,0x7E,0x3D, 0x64,0x5D,0x19,0x73, + 0x60,0x81,0x4F,0xDC, 0x22,0x2A,0x90,0x88, 0x46,0xEE,0xB8,0x14, 0xDE,0x5E,0x0B,0xDB, + 0xE0,0x32,0x3A,0x0A, 0x49,0x06,0x24,0x5C, 0xC2,0xD3,0xAC,0x62, 0x91,0x95,0xE4,0x79, + 0xE7,0xC8,0x37,0x6D, 0x8D,0xD5,0x4E,0xA9, 0x6C,0x56,0xF4,0xEA, 0x65,0x7A,0xAE,0x08, + 0xBA,0x78,0x25,0x2E, 0x1C,0xA6,0xB4,0xC6, 0xE8,0xDD,0x74,0x1F, 0x4B,0xBD,0x8B,0x8A, + 0x70,0x3E,0xB5,0x66, 0x48,0x03,0xF6,0x0E, 0x61,0x35,0x57,0xB9, 0x86,0xC1,0x1D,0x9E, + 0xE1,0xF8,0x98,0x11, 0x69,0xD9,0x8E,0x94, 0x9B,0x1E,0x87,0xE9, 0xCE,0x55,0x28,0xDF, + 0x8C,0xA1,0x89,0x0D, 0xBF,0xE6,0x42,0x68, 0x41,0x99,0x2D,0x0F, 0xB0,0x54,0xBB,0x16 +}; + +// AES is-box table +static const uint8_t _CryptAes_Isbox[256] = +{ + 0x52,0x09,0x6a,0xd5, 0x30,0x36,0xa5,0x38, 0xbf,0x40,0xa3,0x9e, 0x81,0xf3,0xd7,0xfb, + 0x7c,0xe3,0x39,0x82, 0x9b,0x2f,0xff,0x87, 0x34,0x8e,0x43,0x44, 0xc4,0xde,0xe9,0xcb, + 0x54,0x7b,0x94,0x32, 0xa6,0xc2,0x23,0x3d, 0xee,0x4c,0x95,0x0b, 0x42,0xfa,0xc3,0x4e, + 0x08,0x2e,0xa1,0x66, 0x28,0xd9,0x24,0xb2, 0x76,0x5b,0xa2,0x49, 0x6d,0x8b,0xd1,0x25, + 0x72,0xf8,0xf6,0x64, 0x86,0x68,0x98,0x16, 0xd4,0xa4,0x5c,0xcc, 0x5d,0x65,0xb6,0x92, + 0x6c,0x70,0x48,0x50, 0xfd,0xed,0xb9,0xda, 0x5e,0x15,0x46,0x57, 0xa7,0x8d,0x9d,0x84, + 0x90,0xd8,0xab,0x00, 0x8c,0xbc,0xd3,0x0a, 0xf7,0xe4,0x58,0x05, 0xb8,0xb3,0x45,0x06, + 0xd0,0x2c,0x1e,0x8f, 0xca,0x3f,0x0f,0x02, 0xc1,0xaf,0xbd,0x03, 0x01,0x13,0x8a,0x6b, + 0x3a,0x91,0x11,0x41, 0x4f,0x67,0xdc,0xea, 0x97,0xf2,0xcf,0xce, 0xf0,0xb4,0xe6,0x73, + 0x96,0xac,0x74,0x22, 0xe7,0xad,0x35,0x85, 0xe2,0xf9,0x37,0xe8, 0x1c,0x75,0xdf,0x6e, + 0x47,0xf1,0x1a,0x71, 0x1d,0x29,0xc5,0x89, 0x6f,0xb7,0x62,0x0e, 0xaa,0x18,0xbe,0x1b, + 0xfc,0x56,0x3e,0x4b, 0xc6,0xd2,0x79,0x20, 0x9a,0xdb,0xc0,0xfe, 0x78,0xcd,0x5a,0xf4, + 0x1f,0xdd,0xa8,0x33, 0x88,0x07,0xc7,0x31, 0xb1,0x12,0x10,0x59, 0x27,0x80,0xec,0x5f, + 0x60,0x51,0x7f,0xa9, 0x19,0xb5,0x4a,0x0d, 0x2d,0xe5,0x7a,0x9f, 0x93,0xc9,0x9c,0xef, + 0xa0,0xe0,0x3b,0x4d, 0xae,0x2a,0xf5,0xb0, 0xc8,0xeb,0xbb,0x3c, 0x83,0x53,0x99,0x61, + 0x17,0x2b,0x04,0x7e, 0xba,0x77,0xd6,0x26, 0xe1,0x69,0x14,0x63, 0x55,0x21,0x0c,0x7d +}; + +// AES Rcon (round constant word) table +static const uint8_t _CryptAes_Rcon[30] = +{ + 0x01,0x02,0x04,0x08, 0x10,0x20,0x40,0x80, 0x1b,0x36,0x6c,0xd8, 0xab,0x4d,0x9a,0x2f, + 0x5e,0xbc,0x63,0xc6, 0x97,0x35,0x6a,0xd4, 0xb3,0x7d,0xfa,0xef, 0xc5,0x91 +}; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CryptAesMul2 + + \Description + Optimized implementation of AES mul-by-two operation x4 using shifts and adds. + + \Input uValue - four-byte composite value to mul-by-two + + \Output + uint32_t - mul-by-two result + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _CryptAesMul2(uint32_t uValue) +{ + static const uint32_t _mt = 0x80808080; + static const uint32_t _mh = 0xfefefefe; + static const uint32_t _mm = 0x1b1b1b1b; + + uint32_t uTemp = uValue & _mt; + uTemp = ((uValue + uValue) & _mh) ^ ((uTemp - (uTemp >> 7)) & _mm); + return(uTemp); +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesXtime + + \Description + AES xtime operation + + \Input uValue - input state value + + \Output + uint8_t - xtime result + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t _CryptAesXtime(uint32_t uValue) +{ + uValue = (uValue & 0x80) ? (uValue << 1) ^ 0x1b : uValue << 1; + return((uint8_t)uValue); +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesInit + + \Description + Initialize crypt state based in input key and initialization vector + + \Input *pAes - cipher state + \Input *pKeyBuf - input key + \Input iKeyLen - length of key in bytes (may be 16 for 128 bit AES, or 32 for 256 bit AES) + \Input *pInitVec - initialization vector for CBC mode + \Input uKeyType - key type (CRYPTAES_KEYTYPE_*) + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptAesInit(CryptAesT *pAes, const uint8_t *pKeyBuf, int32_t iKeyLen, const uint8_t *pInitVec, uint32_t uKeyType) +{ + // init AES key schedule + CryptAesInitKeySchedule(&pAes->KeySchedule, pKeyBuf, iKeyLen, uKeyType); + + // copy the initialization vector + ds_memcpy(pAes->aInitVec, pInitVec, sizeof(pAes->aInitVec)); +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesInvMixCol + + \Description + Perform inverse mix column operation on a key schedule word + + \Input uValue - key schedule word to perform operation on + + \Output + uint32_t - operation result + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _CryptAesInvMixCol(uint32_t uValue) +{ + uint32_t a0, a1, a2, a3; + + a0 = _CryptAesMul2(uValue); + a1 = _CryptAesMul2(a0); + a2 = _CryptAesMul2(a1); + a3 = uValue ^ a2; + a2 = a0 ^ a1 ^ a2; + a0 ^= a3; + a1 ^= a3; + a2 ^= CRYPTAES_ROT3(a0); + a2 ^= CRYPTAES_ROT2(a1); + a2 ^= CRYPTAES_ROT1(a3); + return(a2); +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesInvertKey + + \Description + Invert key schedule, used for decryption + + \Input *pKeySchedule - key schedule + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptAesInvertKey(CryptAesKeyScheduleT *pKeySchedule) +{ + uint32_t uCount, *pState; + for (uCount = 4, pState = pKeySchedule->aKeySchedule; uCount < (unsigned)pKeySchedule->uNumRounds*4; uCount += 1) + { + pState[uCount] = _CryptAesInvMixCol(pState[uCount]); + } +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesEncrypt + + \Description + Encrypt a single block + + \Input *pKeySchedule - cipher key schedule + \Input *pData - [inp/out] data to encrypt, storage for encrypted data + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptAesEncrypt(const CryptAesKeyScheduleT *pKeySchedule, uint32_t *pData) +{ + const uint32_t *pState = pKeySchedule->aKeySchedule; + const uint8_t *pSbox = _CryptAes_Sbox; + uint32_t uRound, uNumRounds; + uint32_t uRow, aRowData[4]; + uint32_t a0, a1, a2, a3, t0, t1; + + // handle pre-round key addition + for (uRow = 0; uRow < 4; uRow += 1, pState += 1) + { + pData[uRow] ^= *pState; + } + + // encrypt a single block of data + for (uRound = 0, uNumRounds = pKeySchedule->uNumRounds; uRound < uNumRounds; uRound += 1) + { + // ByteSub+ShiftRow + for (uRow = 0; uRow < 4; uRow += 1) + { + a0 = (uint32_t)pSbox[(pData[uRow%4] >> 24) & 0xff]; + a1 = (uint32_t)pSbox[(pData[(uRow+1)%4] >> 16) & 0xff]; + a2 = (uint32_t)pSbox[(pData[(uRow+2)%4] >> 8) & 0xff]; + a3 = (uint32_t)pSbox[(pData[(uRow+3)%4]) & 0xff]; + + // MixColumn (unless last round) + if (uRound != (uNumRounds - 1)) + { + t0 = a0 ^ a1 ^ a2 ^ a3; + t1 = a0; + + a0 ^= t0 ^ _CryptAesXtime(a0 ^ a1); + a1 ^= t0 ^ _CryptAesXtime(a1 ^ a2); + a2 ^= t0 ^ _CryptAesXtime(a2 ^ a3); + a3 ^= t0 ^ _CryptAesXtime(a3 ^ t1); + } + + aRowData[uRow] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); + } + + // KeyAddition + for (uRow = 0; uRow < 4; uRow += 1, pState += 1) + { + pData[uRow] = aRowData[uRow] ^ *pState; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesEncryptCBC + + \Description + CBC encrypt of encrypted data blocks (must be block-sized). + + \Input *pAes - cipher state + \Input *pBuffer - [inp/out] data to encrypt, storage for encrypted data + \Input iLength - data length in bytes + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptAesEncryptCBC(CryptAesT *pAes, uint8_t *pBuffer, int32_t iLength) +{ + uint32_t s0, s1, s2, s3; + uint32_t a0, a1, a2, a3; + uint32_t aBlock[4]; + + // read the input vector from bytes into words + s0 = CRYPTAES_RDWORD(pAes->aInitVec+0); + s1 = CRYPTAES_RDWORD(pAes->aInitVec+4); + s2 = CRYPTAES_RDWORD(pAes->aInitVec+8); + s3 = CRYPTAES_RDWORD(pAes->aInitVec+12); + + // encrypt the data + for (iLength -= 16; iLength >= 0; iLength -= 16, pBuffer += 16) + { + // read a block of data from bytes into words + a0 = CRYPTAES_RDWORD(pBuffer+0); + a1 = CRYPTAES_RDWORD(pBuffer+4); + a2 = CRYPTAES_RDWORD(pBuffer+8); + a3 = CRYPTAES_RDWORD(pBuffer+12); + + // xor CBC state against data prior to encryption + aBlock[0] = a0 ^ s0; + aBlock[1] = a1 ^ s1; + aBlock[2] = a2 ^ s2; + aBlock[3] = a3 ^ s3; + + // encrypt the block + _CryptAesEncrypt(&pAes->KeySchedule, aBlock); + + // update state + s0 = aBlock[0]; + s1 = aBlock[1]; + s2 = aBlock[2]; + s3 = aBlock[3]; + + // write encrypted data to buffer + CRYPTAES_WRWORD(s0, pBuffer+0); + CRYPTAES_WRWORD(s1, pBuffer+4); + CRYPTAES_WRWORD(s2, pBuffer+8); + CRYPTAES_WRWORD(s3, pBuffer+12); + } + + // write back updated state + CRYPTAES_WRWORD(s0, pAes->aInitVec+0); + CRYPTAES_WRWORD(s1, pAes->aInitVec+4); + CRYPTAES_WRWORD(s2, pAes->aInitVec+8); + CRYPTAES_WRWORD(s3, pAes->aInitVec+12); +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesDecrypt + + \Description + Decrypt a single block + + \Input *pKeySchedule - cipher key schedule + \Input *pData - [inp/out] data to decrypt, storage for decrypted data + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptAesDecrypt(const CryptAesKeyScheduleT *pKeySchedule, uint32_t *pData) +{ + uint32_t uRound, uNumRounds = pKeySchedule->uNumRounds; + const uint32_t *pState = pKeySchedule->aKeySchedule + ((uNumRounds + 1) * 4); + const uint8_t *pIsbox = _CryptAes_Isbox; + uint32_t uRow, aRowData[4]; + uint32_t a0, a1, a2, a3; + uint32_t xt0, xt1, xt2, xt3, xt4, xt5, xt6; + + // handle pre-round key addition + for (uRow = 4; uRow > 0; uRow -= 1) + { + pData[uRow-1] ^= *(--pState); + } + + // decrypt a single block of data + for (uRound = 0; uRound < uNumRounds; uRound += 1) + { + // ByteSub+ShiftRow + for (uRow = 4; uRow > 0; uRow -= 1) + { + a0 = pIsbox[(pData[(uRow+3)%4] >> 24) & 0xff]; + a1 = pIsbox[(pData[(uRow+2)%4] >> 16) & 0xff]; + a2 = pIsbox[(pData[(uRow+1)%4] >> 8) & 0xff]; + a3 = pIsbox[(pData[uRow%4]) & 0xff]; + + // MixColumn (unless last row) + if (uRound != (uNumRounds - 1)) + { + xt0 = _CryptAesXtime(a0 ^ a1); + xt1 = _CryptAesXtime(a1 ^ a2); + xt2 = _CryptAesXtime(a2 ^ a3); + xt3 = _CryptAesXtime(a3 ^ a0); + xt4 = _CryptAesXtime(xt0 ^ xt1); + xt5 = _CryptAesXtime(xt1 ^ xt2); + xt6 = _CryptAesXtime(xt4 ^ xt5); + + xt0 ^= a1 ^ a2 ^ a3 ^ xt4 ^ xt6; + xt1 ^= a0 ^ a2 ^ a3 ^ xt5 ^ xt6; + xt2 ^= a0 ^ a1 ^ a3 ^ xt4 ^ xt6; + xt3 ^= a0 ^ a1 ^ a2 ^ xt5 ^ xt6; + + aRowData[uRow-1] = (xt0 << 24) | (xt1 << 16) | (xt2 << 8) | xt3; + } + else + { + aRowData[uRow-1] = (a0 << 24) | (a1 << 16) | (a2 << 8) | a3; + } + } + + // KeyAddition + for (uRow = 4; uRow > 0; uRow -= 1) + { + pData[uRow-1] = aRowData[uRow-1] ^ *(--pState); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _CryptAesDecryptCBC + + \Description + CBC decrypt of encrypted data blocks (must be block-sized). + + \Input *pAes - cipher state + \Input *pBuffer - [inp/out] data to decrypt, storage for decrypted data + \Input iLength - data length in bytes + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptAesDecryptCBC(CryptAesT *pAes, uint8_t *pBuffer, int32_t iLength) +{ + uint32_t s0, s1, s2, s3; + uint32_t a0, a1, a2, a3; + uint32_t t0, t1, t2, t3; + uint32_t aBlock[4]; + + // read the input vector from bytes into words + s0 = CRYPTAES_RDWORD(pAes->aInitVec+0); + s1 = CRYPTAES_RDWORD(pAes->aInitVec+4); + s2 = CRYPTAES_RDWORD(pAes->aInitVec+8); + s3 = CRYPTAES_RDWORD(pAes->aInitVec+12); + + // encrypt the data + for (iLength -= 16; iLength >= 0; iLength -= 16, pBuffer += 16) + { + // read a block of data from bytes into words + a0 = CRYPTAES_RDWORD(pBuffer+0); + a1 = CRYPTAES_RDWORD(pBuffer+4); + a2 = CRYPTAES_RDWORD(pBuffer+8); + a3 = CRYPTAES_RDWORD(pBuffer+12); + + // set up for decrypt + aBlock[0] = a0; + aBlock[1] = a1; + aBlock[2] = a2; + aBlock[3] = a3; + + // encrypt the block + _CryptAesDecrypt(&pAes->KeySchedule, aBlock); + + // undo xor against state + t0 = aBlock[0] ^ s0; + t1 = aBlock[1] ^ s1; + t2 = aBlock[2] ^ s2; + t3 = aBlock[3] ^ s3; + + // update state + s0 = a0; + s1 = a1; + s2 = a2; + s3 = a3; + + // write encrypted data to buffer + CRYPTAES_WRWORD(t0, pBuffer+0); + CRYPTAES_WRWORD(t1, pBuffer+4); + CRYPTAES_WRWORD(t2, pBuffer+8); + CRYPTAES_WRWORD(t3, pBuffer+12); + } + + // write back updated state + CRYPTAES_WRWORD(s0, pAes->aInitVec+0); + CRYPTAES_WRWORD(s1, pAes->aInitVec+4); + CRYPTAES_WRWORD(s2, pAes->aInitVec+8); + CRYPTAES_WRWORD(s3, pAes->aInitVec+12); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptAesInit + + \Description + Init the AES cipher with the specified key + + \Input *pAes - cipher state to initialize + \Input *pKeyBuf - pointer to key + \Input iKeyLen - key length + \Input uKeyType - type of crypt operation (CRYPTAES_KEYTYPE_*) + \Input *pInitVec - initialization vector + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptAesInit(CryptAesT *pAes, const uint8_t *pKeyBuf, int32_t iKeyLen, uint32_t uKeyType, const uint8_t *pInitVec) +{ + // clear state + ds_memclr(pAes, sizeof(*pAes)); + // init state + _CryptAesInit(pAes, pKeyBuf, iKeyLen, pInitVec, uKeyType); +} + +/*F********************************************************************************/ +/*! + \Function CryptAesEncrypt + + \Description + Encrypt data with the AES cipher in CBC mode + + \Input *pAes - cipher state + \Input *pBuffer - [inp/out] data to encrypt + \Input iLength - length of data to encrypt (must be a multiple of 16) + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptAesEncrypt(CryptAesT *pAes, uint8_t *pBuffer, int32_t iLength) +{ + _CryptAesEncryptCBC(pAes, pBuffer, iLength); +} + +/*F********************************************************************************/ +/*! + \Function CryptAesEncryptBlock + + \Description + Encrypt 16 byte block with the AES cipher + + \Input *pKeySchedule - cipher key schedule + \Input *pInput - [inp] data to encrypt + \Input *pOutput - [out] encrypted data + + \Notes + pInput and pOutput may overlap + + \Version 07/15/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptAesEncryptBlock(CryptAesKeyScheduleT *pKeySchedule, const uint8_t *pInput, uint8_t *pOutput) +{ + uint32_t aBlock[4]; + + // read a block of data from bytes into words + aBlock[0] = CRYPTAES_RDWORD(pInput+0); + aBlock[1] = CRYPTAES_RDWORD(pInput+4); + aBlock[2] = CRYPTAES_RDWORD(pInput+8); + aBlock[3] = CRYPTAES_RDWORD(pInput+12); + + _CryptAesEncrypt(pKeySchedule, aBlock); + + // write encrypted data to buffer + CRYPTAES_WRWORD(aBlock[0], pOutput+0); + CRYPTAES_WRWORD(aBlock[1], pOutput+4); + CRYPTAES_WRWORD(aBlock[2], pOutput+8); + CRYPTAES_WRWORD(aBlock[3], pOutput+12); +} + +/*F********************************************************************************/ +/*! + \Function CryptAesDecrypt + + \Description + Decrypt data with the AES cipher in CBC mode + + \Input *pAes - cipher state + \Input *pBuffer - [inp/out] data to decrypt + \Input iLength - length of data to decrypt (must be a multiple of 16) + + \Version 01/20/2011 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptAesDecrypt(CryptAesT *pAes, uint8_t *pBuffer, int32_t iLength) +{ + _CryptAesDecryptCBC(pAes, pBuffer, iLength); +} + +/*F********************************************************************************/ +/*! + \Function CryptAesDecryptBlock + + \Description + Decrypt 16 byte block with the AES cipher + + \Input *pKeySchedule - cipher key schedule + \Input *pInput - [inp] data to encrypt + \Input *pOutput - [out] encrypted data + + \Notes + pInput and pOutput may overlap + + \Version 07/15/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptAesDecryptBlock(CryptAesKeyScheduleT *pKeySchedule, const uint8_t *pInput, uint8_t *pOutput) +{ + uint32_t aBlock[4]; + + // read a block of data from bytes into words + aBlock[0] = CRYPTAES_RDWORD(pInput+0); + aBlock[1] = CRYPTAES_RDWORD(pInput+4); + aBlock[2] = CRYPTAES_RDWORD(pInput+8); + aBlock[3] = CRYPTAES_RDWORD(pInput+12); + + _CryptAesDecrypt(pKeySchedule, aBlock); + + // write decrypted data to buffer + CRYPTAES_WRWORD(aBlock[0], pOutput+0); + CRYPTAES_WRWORD(aBlock[1], pOutput+4); + CRYPTAES_WRWORD(aBlock[2], pOutput+8); + CRYPTAES_WRWORD(aBlock[3], pOutput+12); +} + +/*F********************************************************************************/ +/*! + \Function CryptAesInitKeySchedule + + \Description + Init AES key schedule + + \Input *pKeySchedule - cipher key schedule + \Input *pKeyBuf - cipher key + \Input iKeyLen - cipher key length + \Input uKeyType - cipher key type (CRYPTAES_KEYTYPE_*) + + \Version 07/15/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptAesInitKeySchedule(CryptAesKeyScheduleT *pKeySchedule, const uint8_t *pKeyBuf, int32_t iKeyLen, uint32_t uKeyType) +{ + uint32_t uNumRounds, uKeyWords, uWord; + uint32_t uCount, uTemp, uTemp2; + const uint8_t *pRcon = _CryptAes_Rcon; + const uint8_t *pSbox = _CryptAes_Sbox; + uint32_t *pState; + + if (iKeyLen == 16) + { + uNumRounds = 10; + uKeyWords = 4; + } + else if (iKeyLen == 32) + { + uNumRounds = 14; + uKeyWords = 8; + } + else + { + NetPrintf(("cryptaes: key length of %d is not supported\n", iKeyLen)); + return; + } + + // save in state + pKeySchedule->uNumRounds = uNumRounds; + pKeySchedule->uKeyWords = uKeyWords; + + // copy key to key state + for (uWord = 0, pState = pKeySchedule->aKeySchedule; uWord < uKeyWords; uWord += 1, pKeyBuf += 4) + { + pState[uWord] = ((uint32_t)pKeyBuf[0]<<24) | ((uint32_t)pKeyBuf[1]<<16) | ((uint32_t)pKeyBuf[2]<<8) | ((uint32_t)pKeyBuf[3]); + } + + // key expansion + for (uWord = uKeyWords, uCount = (pKeySchedule->uNumRounds+1) * 4; uWord < uCount; uWord += 1) + { + // read key state value + uTemp = pState[uWord-1]; + + // key expansion + if ((uWord % uKeyWords) == 0) + { + uTemp2 = (uint32_t)pSbox[uTemp&0xff]<<8; + uTemp2 |= (uint32_t)pSbox[(uTemp>>8)&0xff]<<16; + uTemp2 |= (uint32_t)pSbox[(uTemp>>16)&0xff]<<24; + uTemp2 |= (uint32_t)pSbox[uTemp>>24]; + uTemp = uTemp2 ^ (((uint32_t)*pRcon)<<24); + pRcon += 1; + } + if ((uKeyWords == 8) && ((uWord % uKeyWords) == 4)) + { + uTemp2 = (uint32_t)pSbox[uTemp&0xff]; + uTemp2 |= (uint32_t)pSbox[(uTemp>>8)&0xff]<<8; + uTemp2 |= (uint32_t)pSbox[(uTemp>>16)&0xff]<<16; + uTemp2 |= (uint32_t)pSbox[uTemp>>24]<<24; + uTemp = uTemp2; + } + + // write back to key schedule + pState[uWord] = pState[uWord-uKeyWords] ^ uTemp; + } + + // if decrypt, invert key schedule + if (uKeyType == CRYPTAES_KEYTYPE_DECRYPT) + { + _CryptAesInvertKey(pKeySchedule); + } +} + diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptarc4.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptarc4.c new file mode 100644 index 00000000..93ce1932 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptarc4.c @@ -0,0 +1,409 @@ +/*H********************************************************************************/ +/*! + \File cryptarc4.c + + \Description + This module is a from-scratch ARC4 implementation designed to avoid + any intellectual property complications. The ARC4 stream cipher is + known to produce output that is compatible with the RC4 stream cipher. + + \Notes + This algorithm from this cypher was taken from the web site: + ciphersaber.gurus.com. It is based on the RC4 compatible algorithm that + was published in the 2nd ed of the book Applied Cryptography by Bruce + Schneier. This is a private-key stream cipher which means that some other + secure way of exchanging cipher keys prior to algorithm use is required. + Its strength is directly related to the key exchange algorithm strength. + In operation, each individual stream message must use a unique key. This + is handled by appending on 10 byte random value onto the private key. This + 10-byte data can be sent by public means to the receptor (or included at the + start of a file or whatever). When the private key is combined with this + public key, it essentially puts the cypher into a random starting state (it + is like using a message digest routine to generate a random hash for + password comparison). The code below was written from scratch using only + a textual algorithm description. + + \Copyright + Copyright (c) 2000-2005 Electronic Arts Inc. + + \Version 1.0 02/25/2000 (gschaefer) First Version + \Version 1.1 11/06/2002 (jbrookes) Removed Create()/Destroy() to eliminate mem + alloc dependencies. +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/crypt/cryptarc4.h" +#include "DirtySDK/crypt/cryptrand.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private variables + +// Public variables + + +/*** Private Functions ************************************************************/ + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptArc4Init + + \Description + Initialize state for ARC4 encryption/decryption module. + + \Input *pState - module state + \Input *pKeyBuf - pointer to crypt key + \Input iKeyLen - length of crypt key (sixteen for RC4-128) + \Input iIter - internal iterator (1=RC4 compat, larger for slightly improved security + + \Version 02/26/2000 (gschaefer) +*/ +/********************************************************************************F*/ +void CryptArc4Init(CryptArc4T *pState, const uint8_t *pKeyBuf, int32_t iKeyLen, int32_t iIter) +{ + uint32_t uWalk; + uint8_t uSwap; + uint8_t uTemp0; + uint8_t uTemp1; + + // clamp iteration count + if (iIter < 1) + { + iIter = 1; + } + + // reset the permutators + pState->walk = 0; + pState->swap = 0; + + // init the state array + for (uWalk = 0; uWalk < 256; ++uWalk) + { + pState->state[uWalk] = (uint8_t)uWalk; + } + + // only do setup if key is valid + if ((iKeyLen > 0) && (iIter > 0)) + { + // mixup the state table + for (uWalk = 0, uSwap = 0; uWalk < 256; ++uWalk) + { + // determine the swap point + uSwap += pState->state[uWalk] + pKeyBuf[uWalk % iKeyLen]; + // swap the entries + uTemp0 = pState->state[uWalk]; + uTemp1 = pState->state[uSwap]; + pState->state[uWalk] = uTemp1; + pState->state[uSwap] = uTemp0; + } + + // advance state for added security (not RC4 compatible) + if (iIter > 1) + { + CryptArc4Advance(pState, iIter*256); + } + } +} + +/*F********************************************************************************/ +/*! + \Function CryptArc4Apply + + \Description + Apply the cipher to the data. Uses reversible XOR so calling twice undoes + the uncryption (assuming the key state has been reset). + + \Input *pState - module state + \Input *pBuffer - buffer to encrypt/decrypt + \Input iLength - length of buffer + + \Version 02/26/2000 (gschaefer) +*/ +/********************************************************************************F*/ +void CryptArc4Apply(CryptArc4T *pState, uint8_t *pBuffer, int32_t iLength) +{ + uint32_t uTemp0; + uint32_t uTemp1; + uint32_t uWalk = pState->walk; + uint32_t uSwap = pState->swap; + + // do each data byte + for (; iLength > 0; --iLength) + { + // determine the swap points + uWalk = (uWalk+1)&255; + uSwap = (uSwap+pState->state[uWalk])&255; + + // swap the state entries + uTemp0 = pState->state[uWalk]; + uTemp1 = pState->state[uSwap]; + pState->state[uWalk] = uTemp1; + pState->state[uSwap] = uTemp0; + + // apply the cypher + *pBuffer++ ^= pState->state[(uTemp0+uTemp1)&255]; + } + + // update the state + pState->walk = uWalk; + pState->swap = uSwap; +} + +/*F********************************************************************************/ +/*! + \Function CryptArc4Advance + + \Description + Advance the cipher state with by iLength bytes. + + \Input *pState - module state + \Input iLength - length to advance + + \Version 12/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptArc4Advance(CryptArc4T *pState, int32_t iLength) +{ + uint32_t uTemp0; + uint32_t uTemp1; + uint32_t uWalk = pState->walk; + uint32_t uSwap = pState->swap; + + // do each data byte + for (; iLength > 0; --iLength) + { + // determine the swap points + uWalk = (uWalk+1)&255; + uSwap = (uSwap+pState->state[uWalk])&255; + + // swap the state entries + uTemp0 = pState->state[uWalk]; + uTemp1 = pState->state[uSwap]; + pState->state[uWalk] = uTemp1; + pState->state[uSwap] = uTemp0; + } + + // update the state + pState->walk = uWalk; + pState->swap = uSwap; +} + +/*F********************************************************************************/ +/*! + \Function CryptArc4StringEncrypt + + \Description + Easy way to encode an asciiz string. The resulting string is iLen chars + of visible ascii characters. String characters must fall in the range of + 32 to 127 to be considered valid. + + \Input *pDst - pointer to output string + \Input iLen - length of output string + \Input *pSrc - pointer to source string (asciiz) + \Input *pKey - key to encrypt with + \Input iKey - length of key (-1=key is asciiz) + \Input iIter - number of initialization iterations + + \Version 01/10/2013 (jbrookes) Based on CryptSSC2 by GWS +*/ +/********************************************************************************F*/ +void CryptArc4StringEncrypt(char *pDst, int32_t iLen, const char *pSrc, const uint8_t *pKey, int32_t iKey, int32_t iIter) +{ + static uint8_t _CryptArc4_aRandom[32], _CryptArc4_bRandStateInit = FALSE; + static CryptArc4T _CryptArc4_Rand; + uint8_t uDat, uEnc; + int32_t iSum; + CryptArc4T Arc4Data; + + // get initial random data + if (_CryptArc4_bRandStateInit == FALSE) + { + CryptRandGet(_CryptArc4_aRandom, sizeof(_CryptArc4_aRandom)); + CryptArc4Init(&_CryptArc4_Rand, pKey, iKey, iIter); + _CryptArc4_bRandStateInit = TRUE; + } + + // init state for string encryption + CryptArc4Init(&Arc4Data, pKey, iKey, iIter); + + // update the random cipher + CryptArc4Apply(&_CryptArc4_Rand, _CryptArc4_aRandom, sizeof(_CryptArc4_aRandom)); + CryptArc4Init(&_CryptArc4_Rand, _CryptArc4_aRandom, sizeof(_CryptArc4_aRandom), iIter); + + // encrypt the string + for (uDat = 0, uEnc = 0; iLen > 1; pDst += 1, iLen -= 1) + { + // get source data + if (pSrc == NULL) + { + // append random fill data + CryptArc4Apply(&_CryptArc4_Rand, &uDat, 1); + // fix the range + uDat = 32+(uDat & 63); + } + else if ((uDat = *pSrc++) == '\0') + { + // append random fill data + pSrc = NULL; + } + + // deal with out of range data + if ((uDat < 32) || (uDat >= 127)) + { + uDat = 127; + } + + // get cipher character + CryptArc4Apply(&Arc4Data, &uEnc, 1); + + // encode the data + iSum = (96 - 32) + uDat + (uEnc % 96); + *pDst = (char)(32 + (iSum % 96)); + } + + // terminate buffer + if (iLen > 0) + { + *pDst = 0; + } +} + +/*F*************************************************************************************************/ +/*! + \Function CryptArc4StringDecrypt + + \Description + Decode an asciiz string encoded with CryptArc4StringEncrypt. + + \Input *pDst - pointer to output string + \Input iLen - length of output string + \Input *pSrc - pointer to source string (asciiz) + \Input *pKey - key to decrypt with + \Input iKey - length of key (-1=key is asciiz) + \Input iIter - number of initialization iterations + + \Version 01/10/2013 (jbrookes) Based on CryptSSC2 by GWS +*/ +/*************************************************************************************************F*/ +void CryptArc4StringDecrypt(char *pDst, int32_t iLen, const char *pSrc, const uint8_t *pKey, int32_t iKey, int32_t iIter) +{ + int32_t iIdx, iSum; + uint8_t uDat, uDec; + CryptArc4T Arc4; + + // setup initial state + CryptArc4Init(&Arc4, pKey, iKey, iIter); + + // decrypt the string + for (iIdx = 0, uDat = 0, uDec = 0; iIdx < iLen-1; pDst += 1) + { + // get encoded source + if ((uDat = pSrc[iIdx++]) == '\0') + { + break; + } + + // get cipher character + CryptArc4Apply(&Arc4, &uDec, 1); + + // decode the data + iSum = (96 - 32) + uDat - (uDec % 96); + *pDst = (char)(32 + (iSum % 96)); + if (*pDst == 127) + { + break; + } + } + + // terminate buffer + if (iIdx < iLen) + { + *pDst = '\0'; + } +} + +/*F*************************************************************************************************/ +/*! + \Function CryptArc4StringEncryptStaticCode + + \Description + This function decrypts an encrypted key/encoded string pair into the source plaintext string. + It also accepts the plaintext string as an argument to verify that the source plaintext string + matches the resulting decrypted string. If there is a mismatch updated key and encoded string + data is printed in c-style array form and an error is returned. The source plaintext string + parameter is only referenced in debug builds to prevent the plaintext string from leaking into + the release binary. The CryptArc4StringEncryptStatic() macro wrapper should always be used to + call this function, and the plaintext string itself should be defined via the preprocessor, + to prevent the plaintext string from leaking into the release binary. + + \Input *pStr - [in/out] pointer to encrypted string, and output buffer for decrypted string + \Input iStrSize - length of string buffer + \Input *pKey - key to decrypt with + \Input iKeySize - length of key + \Input *pStrSrc - decrypted key (debug only) + + \Output + int32_t - zero=success, negative means the decrypted string does not match the plaintext string (debug only) + + \Version 06/05/2014 (jbrookes) +*/ +/*************************************************************************************************F*/ +int32_t CryptArc4StringEncryptStaticCode(char *pStr, int32_t iStrSize, const uint8_t *pKey, int32_t iKeySize, const char *pStrSrc) +{ + CryptArc4T Arc4; + int32_t iResult = 0; + + // decrypt the url using RC4-dropN + CryptArc4Init(&Arc4, pKey, iKeySize, 3*1024); + CryptArc4Apply(&Arc4, (uint8_t *)pStr, iStrSize); + + /* validate the decrypted string matches what we expect; if not it means someone forgot + to update the encrypted data. we only compile this in a DEBUG build to prevent the + plaintext string from leaking into the executable */ + #if DIRTYCODE_DEBUG + if ((pStrSrc != NULL) && (strcmp(pStr, pStrSrc))) + { + uint8_t aKey[32], aEnc[1024]; + NetPrintf(("cryptarc4: mismatch between decrypted string %s and source string %s; encrypted string needs to be updated\n", pStr, pStrSrc)); + // set up url buffer to encrypt... we encrypt the whole buffer so first we clear it + ds_memclr(aEnc, sizeof(aEnc)); + ds_strnzcpy((char *)aEnc, pStrSrc, iStrSize); + // encrypt the url buffer using RC4-dropN + CryptRandGet(aKey, sizeof(aKey)); + CryptArc4Init(&Arc4, aKey, sizeof(aKey), 3*1024); + CryptArc4Apply(&Arc4, aEnc, iStrSize); + // format the encryption key and encrypted url buffer to debug output + NetPrintArray(aKey, sizeof(aKey), "strKey"); + NetPrintArray(aEnc, iStrSize, "strEnc"); + // return failure to caller + iResult = -1; + } + else + { + NetPrintf(("cryptarc4: decrypted string %s\n", pStr)); + } + #endif + + return(iResult); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptbn.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptbn.c new file mode 100644 index 00000000..343692a1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptbn.c @@ -0,0 +1,1375 @@ +/*H*************************************************************************************/ +/*! + \File cryptbn.c + + \Description + This module is implements big integer math needed for our cryptography + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. + + \Version 01/18/2017 (eesponda) First version split from CryptRSA +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include + +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/crypt/cryptbn.h" + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function _CryptBnLzCnt + + \Description + Implements the function to find the MSB + + \Input uValue - the input we are finding the msb for + + \Output + int32_t - the index of the MSB + + \Notes + Uses BitScanReverse on PC as some processors that completely support + the lzcnt instruction. + + Durango supports the lzcnt instruction so it can be used. + + The remaining platforms use the builtin as it has been supported by gcc + for some time. + + \Version 03/06/2017 (eesponda) +*/ +/*************************************************************************************H*/ +static int32_t _CryptBnLzCnt(ucrypt_t uValue) +{ +#if defined(DIRTYCODE_PC) + unsigned long uResult; + + #if UCRYPT_SIZE == 4 + _BitScanReverse(&uResult, uValue); + #else + _BitScanReverse64(&uResult, uValue); + #endif + return(uResult + 1); +#elif defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + #if UCRYPT_SIZE == 4 + return(UCRYPT_BITSIZE - __lzcnt(uValue)); + #else + return(UCRYPT_BITSIZE - __lzcnt64(uValue)); + #endif +#else + #if UCRYPT_SIZE == 4 + return(UCRYPT_BITSIZE - __builtin_clz(uValue)); + #elif DIRTYCODE_64BITPTR == 1 + return(UCRYPT_BITSIZE - __builtin_clzl(uValue)); + #else + return(UCRYPT_BITSIZE - __builtin_clzll(uValue)); + #endif +#endif +} + +/*F*************************************************************************************/ +/*! + \Function _CryptBnExpand + + \Description + Expands the width of the big number by one prepending an entry + + \Input *pState - the big number + \Input uNewEntry - the entry we are adding + + \Version 02/27/2017 (eesponda) +*/ +/*************************************************************************************H*/ +static void _CryptBnExpand(CryptBnT *pState, ucrypt_t uNewEntry) +{ + if (pState->iWidth < CRYPTBN_MAX_WIDTH) + { + pState->aData[pState->iWidth++] = uNewEntry; + } + else + { + NetPrintf(("cryptbn: tried to increase the size of number's width past the max\n")); + } +} + +/*F*************************************************************************************/ +/*! + \Function _CryptBnShrink + + \Description + Shrink until you find a non-zero entry or the width is one + + \Input *pState - the big number + + \Version 02/27/2017 (eesponda) +*/ +/*************************************************************************************H*/ +static void _CryptBnShrink(CryptBnT *pState) +{ + int32_t iWidth = pState->iWidth; + + while ((iWidth > 1) && (pState->aData[iWidth-1] == 0)) + { + iWidth -= 1; + } + pState->iWidth = iWidth; +} + +/*F*************************************************************************************/ +/*! + \Function _CryptBnSet + + \Description + Set an entry from the big number + + \Input *pState - the big number + \Input iIndex - the index of the entry + \Input uValue - the entry value + + \Notes + This helps us safely retrieve entries when the index is outside our bounds + + \Version 02/27/2017 (eesponda) +*/ +/*************************************************************************************H*/ +static void _CryptBnSet(CryptBnT *pState, int32_t iIndex, ucrypt_t uValue) +{ + if (iIndex < pState->iWidth) + { + pState->aData[iIndex] = uValue; + } + else if (iIndex < CRYPTBN_MAX_WIDTH) + { + pState->aData[iIndex] = uValue; + pState->iWidth = iIndex + 1; + } +} + +/*F*************************************************************************************/ +/*! + \Function _CryptBnIsZero + + \Description + Checks if a big number is zero + + \Input *pState - the big number + + \Output + uint8_t - TRUE if zero, FALSE otherwise + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +static uint8_t _CryptBnIsZero(const CryptBnT *pState) +{ + int32_t iWidth; + + for (iWidth = pState->iWidth; iWidth > 0; iWidth -= 1) + { + if (pState->aData[iWidth-1] != 0) + { + return(FALSE); + } + } + return(TRUE); +} + +/*F*************************************************************************************/ +/*! + \Function _CryptBnIsEven + + \Description + Checks if a big number is even + + \Input *pState - the big number + + \Output + uint8_t - TRUE if even, FALSE if odd + + \Version 02/07/2018 (eesponda) +*/ +/*************************************************************************************H*/ +static uint8_t _CryptBnIsEven(const CryptBnT *pState) +{ + // we only check the bottom limb + return((pState->aData[0] & 1) == 0); +} + +/*F*************************************************************************************/ +/*! + \Function _CryptBnAdd + + \Description + Adds two big numbers together + + \Input *pState - the result of the operation + \Input *pAdd1 - the left side + \Input *pAdd2 - the right side + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +static void _CryptBnAdd(CryptBnT *pState, const CryptBnT *pAdd1, const CryptBnT *pAdd2) +{ + ucrypt_t uAccum; + int32_t iWidth = DS_MAX(pAdd1->iWidth, pAdd2->iWidth), iCount; + + // do the add + for (uAccum = 0, iCount = 0; iCount < iWidth; iCount += 1) + { + ucrypt_t uResult = uAccum, uLhs = pAdd1->aData[iCount], uRhs = pAdd2->aData[iCount]; + // do the math, setting carry on overflow + uAccum = ((uResult += uLhs) < uLhs); + uAccum |= ((uResult += uRhs) < uRhs); + // set the value + _CryptBnSet(pState, iCount, uResult); + } + + if (uAccum != 0) + { + _CryptBnExpand(pState, uAccum); + } +} + +/*F*************************************************************************************/ +/*! + \Function _CryptBnSubtract + + \Description + Subtracts two big numbers + + \Input *pState - result of subtraction + \Input *pSub1 - the left hand side of the equation + \Input *pSub2 - the right hand side of the equation + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +static void _CryptBnSubtract(CryptBnT *pState, const CryptBnT *pSub1, const CryptBnT *pSub2) +{ + ucrypt_t uAccum; + int32_t iWidth = DS_MAX(pSub1->iWidth, pSub2->iWidth), iCount; + + for (uAccum = 0, iCount = 0; iCount < iWidth; iCount += 1) + { + ucrypt_t uResult, uFinalResult; + ucrypt_t uLhs = pSub1->aData[iCount], uRhs = pSub2->aData[iCount]; + + // calculate the result without borrow + uResult = uLhs - uRhs; + // calculate the result after borrow + uFinalResult = uResult - uAccum; + + // calculate the new borrow if first or second calculation resulted in overflow + uAccum = (uResult > uLhs) | (uFinalResult > uResult); + + // save result + _CryptBnSet(pState, iCount, uFinalResult); + } +} + +/*** Public functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function CryptBnInit + + \Description + Initializes the large number with zero at a given width + + \Input *pState - pointer to output large number array of ucrypt_t units + \Input iWidth - width of output + + \Version 01/18/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnInit(CryptBnT *pState, int32_t iWidth) +{ + ds_memclr(pState, sizeof(*pState)); + pState->iWidth = iWidth; +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnInitSet + + \Description + Initializes the large number with a number + + \Input *pState - pointer to output large number array of ucrypt_t units + \Input uValue - value we are initializing the big number with + + \Version 01/18/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnInitSet(CryptBnT *pState, uint32_t uValue) +{ + ds_memclr(pState, sizeof(*pState)); + pState->iWidth = 1; + pState->aData[0] = uValue; +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnInitFrom + + \Description + Convert from msb bytes to int32_t number format + + \Input *pState - pointer to output large number array of ucrypt_t units + \Input iWidth - width of output - if negative, auto-calculate width + \Input *pSource - pointer to input large number array of bytes + \Input iLength - length of input in bytes + + \Output + int32_t - width in ucrypt_t units + + \Version 03/08/2002 (gschaefer) + \Version 03/03/2004 (sbevan) Handle odd iLength +*/ +/*************************************************************************************H*/ +int32_t CryptBnInitFrom(CryptBnT *pState, int32_t iWidth, const uint8_t *pSource, int32_t iLength) +{ + ucrypt_t *pResult = pState->aData; + int32_t iFullWordCount = iLength/sizeof(*pResult); + int32_t iWordCount = (iLength+sizeof(*pResult)-1)/sizeof(*pResult); + + // clear the contents + ds_memclr(pState, sizeof(*pState)); + + if (iWidth < 0) + { + iWidth = iWordCount; + } + pState->iWidth = iWidth; + + if (iFullWordCount != iWordCount) + { + /* calculate how many bytes we need to write and + generate a left shift amount based on that */ + int32_t iDiff = iLength - (iFullWordCount * UCRYPT_SIZE); + int32_t iLeftShiftAmount = (iDiff - 1) * 8; + + // encode the bytes into the buffer until there is nothing left + while (iLeftShiftAmount >= 0) + { + pResult[iWidth - 1] |= ((ucrypt_t)*pSource++ << iLeftShiftAmount); + iLeftShiftAmount -= 8; + } + iWidth -= 1; + } + for (; iWidth > 0; iWidth -= 1) + { + #if (UCRYPT_SIZE == 4) + pResult[iWidth-1] = (pSource[0] << 24) | (pSource[1] << 16) | (pSource[2] << 8) | (pSource[3] << 0); + #else + pResult[iWidth-1] = ((ucrypt_t)pSource[0] << 56) | ((ucrypt_t)pSource[1] << 48) | ((ucrypt_t)pSource[2] << 40) | ((ucrypt_t)pSource[3] << 32) | + ((ucrypt_t)pSource[4] << 24) | ((ucrypt_t)pSource[5] << 16) | ((ucrypt_t)pSource[6] << 8) | ((ucrypt_t)pSource[7] << 0); + #endif + pSource += UCRYPT_SIZE; + } + return(iWordCount); +} + + +/*F*************************************************************************************/ +/*! + \Function CryptBnInitLeFrom + + \Description + Convert the bytes into a big number in little-endian form + + \Input *pState - pointer to output large number array of ucrypt_t units + \Input *pSource - pointer to input large number array of bytes + \Input iLength - length of input in bytes + + \Output + int32_t - width in ucrypt_t units + + \Version 04/11/2018 (eesponda) +*/ +/*************************************************************************************H*/ +int32_t CryptBnInitLeFrom(CryptBnT *pState, const uint8_t *pSource, int32_t iLength) +{ + //int32_t iWidth; + ucrypt_t *pResult = pState->aData; + int32_t iWordCount = (iLength+sizeof(*pResult)-1)/sizeof(*pResult); + + // clear the contents and init + ds_memclr(pState, sizeof(*pState)); + pState->iWidth = iWordCount; + ds_memcpy_s(pState->aData, pState->iWidth*sizeof(*pState->aData), pSource, iLength); + + return(iWordCount); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnLeftShift + + \Description + Left shifts a large number by a bit + + \Input *pState - pointer to input and output large number array + + \Notes + pState:iWidth <-- pState:iWidth << 1 + + \Version 10/11/2011 (mdonais) +*/ +/*************************************************************************************H*/ +void CryptBnLeftShift(CryptBnT *pState) +{ + ucrypt_t uAccum; + int32_t iWidth, iCount; + + // do the shift + for (uAccum = 0, iCount = 0, iWidth = pState->iWidth; iCount < iWidth; iCount += 1) + { + ucrypt_t uResult, uFinalResult, uVal; + + uVal = pState->aData[iCount]; + uResult = uVal << 1; + uFinalResult = uResult + uAccum; + uAccum = (uResult < uVal); + pState->aData[iCount] = uFinalResult; + } + + // expand if necessary + if (uAccum != 0) + { + _CryptBnExpand(pState, uAccum); + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnLeftShift2 + + \Description + Left shifts a large number by a number of bits + + \Input *pState - pointer to input and output large number array + \Input iBits - the number of bits to shift + + \Notes + pState:iWidth <-- pState:iWidth << iBits + + \Version 4/14/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnLeftShift2(CryptBnT *pState, int32_t iBits) +{ + int32_t iOffset, iIndex; + const int32_t iBitLen = CryptBnBitLen(pState) + iBits; + + /* make sure this shift would not exceed the width of the number + normally I would just set to zero but I need to indicate to the caller + that the output might not be what they expected */ + if (iBitLen > CRYPTBN_MAX_BITS) + { + NetPrintf(("cryptbn: tried to left shift past the max width which will truncate all the data\n")); + return; + } + + /* if we are trying to shift up one ucrypt_t unit or more + then calculate the number of ucrypt_t units and the number + of leftover bits we need to calculate */ + if (iBits >= UCRYPT_BITSIZE) + { + iOffset = iBits / UCRYPT_BITSIZE; + iBits %= UCRYPT_BITSIZE; + + /* shift the array to the right (left shift) the number of full ucrypt_t units (UCRYPT_BITSIZE) + clear the lower ucrypt_t units + update the width */ + memmove(pState->aData+iOffset, pState->aData, pState->iWidth * sizeof(*pState->aData)); + ds_memclr(pState->aData, iOffset * sizeof(*pState->aData)); + pState->iWidth += iOffset; + + // if there are no left over bits then we have nothing left to do + if (iBits == 0) + { + return; + } + } + + // check if we past a ucrypt_t boundary and need to expand + if (iBitLen > pState->iWidth*UCRYPT_BITSIZE) + { + pState->iWidth += 1; + } + + // shift the items in the array over manually + for (iIndex = pState->iWidth-1; iIndex > 0; iIndex -= 1) + { + pState->aData[iIndex] = (pState->aData[iIndex] << iBits) | (pState->aData[iIndex - 1] >> (UCRYPT_BITSIZE - iBits)); + } + pState->aData[0] <<= iBits; +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnRightShift2 + + \Description + Right shifts a large number by a number of bits + + \Input *pState - pointer to input and output large number array + \Input iBits - the number of bits to shift + + \Notes + pState:iWidth <-- pState:iWidth >> iBits + + \Version 4/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnRightShift2(CryptBnT *pState, int32_t iBits) +{ + int32_t iIndex; + + /* if shifting right more than the bit length is the same + as setting to zero */ + if (CryptBnBitLen(pState) <= iBits) + { + CryptBnInitSet(pState, 0); + return; + } + + /* if we are trying to shift up one ucrypt_t unit or more + then calculate the number of ucrypt_t units and the number + of leftover bits we need to calculate */ + if (iBits >= UCRYPT_BITSIZE) + { + int32_t iOffset = iBits / UCRYPT_BITSIZE; + iBits %= UCRYPT_BITSIZE; + + /* shift the array to the left (right shift) the number of full ucrypt_t units (UCRYPT_BITSIZE) + update the width + clear the upper ucrypt_t units */ + memmove(pState->aData, pState->aData+iOffset, (pState->iWidth-iOffset)*sizeof(*pState->aData)); + pState->iWidth -= iOffset; + ds_memclr(pState->aData+pState->iWidth, iOffset*sizeof(*pState->aData)); + + // if there are no left over bits then we have nothing left to do + if (iBits == 0) + { + return; + } + } + + // shift the items in the array over manually + for (iIndex = 0; iIndex < pState->iWidth-1; iIndex += 1) + { + pState->aData[iIndex] = (pState->aData[iIndex] >> iBits) | (pState->aData[iIndex + 1] << (UCRYPT_BITSIZE - iBits)); + } + pState->aData[iIndex] >>= iBits; + + /* check if we past a ucrypt_t boundary and need to shrink + if the number of bits is at or under the next boundary then shrink to there + we do not shrink under 1, as that is handled at the top of the function */ + if ((pState->iWidth > 1) && (CryptBnBitLen(pState) <= ((pState->iWidth-1)*UCRYPT_BITSIZE))) + { + pState->iWidth -= 1; + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnBitTest + + \Description + Check to see if a particular bit is set within a large bit vector + + \Input *pState - pointer to input bit vector + \Input iBit - bit number to test (zero-relative) + + \Output + uint8_t - TRUE if set, else FALSE + + \Notes + uResult <-- pValue:iWidth & (1 << iBit) + + \Version 03/08/2002 (gschaefer) +*/ +/*************************************************************************************H*/ +uint8_t CryptBnBitTest(const CryptBnT *pState, int32_t iBit) +{ + int32_t iBitOff = iBit & (UCRYPT_BITSIZE-1); + int32_t iOffset = iBit / UCRYPT_BITSIZE; + return((pState->aData[iOffset] & ((ucrypt_t)1 << iBitOff)) != 0); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnBitSet + + \Description + Set a particular bit in the big number + + \Input *pState - pointer to input bit vector + \Input iBit - bit number to set (zero-relative) + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnBitSet(CryptBnT *pState, int32_t iBit) +{ + int32_t iBitOff, iOffset; + + while (pState->iWidth*UCRYPT_BITSIZE <= iBit) + { + _CryptBnExpand(pState, 0); + } + iBitOff = iBit & (UCRYPT_BITSIZE - 1); + iOffset = iBit / UCRYPT_BITSIZE; + pState->aData[iOffset] |= ((ucrypt_t)1 << iBitOff); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnBitAnd + + \Description + Performs bitwise and on two big numbers + + \Input *pState - [out] output of the result of the operation + \Input *pLhs - big number on left hand side of operation + \Input *pRhs - big number on right hand side of operation + + \Version 04/06/2018 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnBitAnd(CryptBnT *pState, const CryptBnT *pLhs, const CryptBnT *pRhs) +{ + int32_t iWidth = DS_MAX(pLhs->iWidth, pRhs->iWidth), iCount; + for (iCount = 0; iCount < iWidth; iCount += 1) + { + _CryptBnSet(pState, iCount, pLhs->aData[iCount] & pRhs->aData[iCount]); + } + + // compact the big number in the case the operation zeroed out the upper limps + _CryptBnShrink(pState); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnBitXor + + \Description + Performs bitwise xor on two big numbers + + \Input *pState - [out] output of the result of the operation + \Input *pLhs - big number on left hand side of operation + \Input *pRhs - big number on right hand side of operation + + \Version 04/06/2018 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnBitXor(CryptBnT *pState, const CryptBnT *pLhs, const CryptBnT *pRhs) +{ + int32_t iWidth = DS_MAX(pLhs->iWidth, pRhs->iWidth), iCount; + for (iCount = 0; iCount < iWidth; iCount += 1) + { + _CryptBnSet(pState, iCount, pLhs->aData[iCount] ^ pRhs->aData[iCount]); + } + + // compact the big number in the case the operation zeroed out the upper limps + _CryptBnShrink(pState); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnBitLen + + \Description + Figures out the bit length of big number + + \Input *pState - big number state + + \Output + int32_t - the number of bits in the big number + + \Version 03/02/2017 (eesponda) +*/ +/*************************************************************************************H*/ +int32_t CryptBnBitLen(const CryptBnT *pState) +{ + int32_t iResult, iIndex; + for (iResult = 0, iIndex = pState->iWidth - 1; iIndex >= 0; iIndex -= 1) + { + if (pState->aData[iIndex] != 0) + { + iResult = _CryptBnLzCnt(pState->aData[iIndex]); + break; + } + } + return(iResult > 0 ? iResult + (UCRYPT_BITSIZE * iIndex) : 0); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnByteLen + + \Description + Figures out the byte length of big number + + \Input *pState - big number state + + \Output + int32_t - the number of bytes in the big number + + \Version 03/13/2018 (eesponda) +*/ +/*************************************************************************************H*/ +int32_t CryptBnByteLen(const CryptBnT *pState) +{ + return(pState->iWidth * (signed)sizeof(*pState->aData)); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnModAdd + + \Description + Adds to big numbers over a modulus + + \Input *pState - big number state + \Input *pAdd1 - first big number to add + \Input *pAdd2 - second big number to add + \Input *pMod - modulus we are adding over + + \Notes + This function does the redunction in a very simple way and isn't meant for + numbers very large over the modulus. We needed to make sure that if we use + the output for modulus multiply it would work under those constraints. + + \Version 04/11/2018 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnModAdd(CryptBnT *pState, const CryptBnT *pAdd1, const CryptBnT *pAdd2, const CryptBnT *pMod) +{ + // do the addition + _CryptBnAdd(pState, pAdd1, pAdd2); + + // reduce? + if (CryptBnCompare(pState, pMod) > 0) + { + CryptBnSubtract(pState, pState, pMod); + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnAccumulate + + \Description + Accumulate one large number into another + + \Input *pState - pointer to large number accumulation buffer + \Input *pAdd - pointer to large number to accumulate to accumulation buffer + + \Notes + pState:iWidth <-- pState:iWidth + pAdd:iWidth + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnAccumulate(CryptBnT *pState, const CryptBnT *pAdd) +{ + if (pState->uSign == 0) + { + _CryptBnAdd(pState, pState, pAdd); + } + else + { + pState->uSign = 0; + CryptBnSubtract(pState, pAdd, pState); + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnIncrement + + \Description + Increment a large number by 1 + + \Input *pState - pointer to large number accumulation buffer + + \Version 02/21/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnIncrement(CryptBnT *pState) +{ + ucrypt_t uAccum; + int32_t iCount, iWidth; + + // do the add + for (uAccum = 1, iCount = 0, iWidth = pState->iWidth; iCount < iWidth; iCount += 1) + { + ucrypt_t uResult = uAccum, uVal = pState->aData[iCount]; + + uAccum = ((uResult += uVal) < uVal); + pState->aData[iCount] = uResult; + + // if we have nothing to carry over we are done + if (uAccum == 0) + { + break; + } + } + + if (uAccum != 0) + { + _CryptBnExpand(pState, uAccum); + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnSubtract + + \Description + Subtract two large numbers + + \Input *pState - pointer to output large number array + \Input *pSub1 - pointer to large number to subtract pSub2 from. + \Input *pSub2 - pointer to second large number + + \Notes + pState:iWidth <-- pSub1:iWidth - pSub2:iWidth + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnSubtract(CryptBnT *pState, const CryptBnT *pSub1, const CryptBnT *pSub2) +{ + // if sub1 - (-sub2) or -sub1 - (sub2) then sub1 + sub2, sign depends on which side is negative + if (pSub1->uSign ^ pSub2->uSign) + { + // add + _CryptBnAdd(pState, pSub1, pSub2); + // set sign + pState->uSign = pSub1->uSign; + } + // if -sub1 - (-sub2) or sub1 - sub2 + else + { + const int32_t iCompare = CryptBnCompare(pSub1, pSub2); + + // if sub1 > sub2 + if (iCompare > 0) + { + // sub1 - sub2 + _CryptBnSubtract(pState, pSub1, pSub2); + // set sign + pState->uSign = pSub1->uSign; + } + // else if sub1 < sub2 + else if (iCompare < 0) + { + // sub2 - sub1 + _CryptBnSubtract(pState, pSub2, pSub1); + // set sign + pState->uSign = ~pSub1->uSign; + } + // else they are the same so it is zero + else + { + CryptBnInitSet(pState, 0); + } + } + + _CryptBnShrink(pState); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnDecrement + + \Description + Decrement a large number by 1 + + \Input *pState - pointer to output large number array + + \Version 02/21/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnDecrement(CryptBnT *pState) +{ + ucrypt_t uAccum; + int32_t iWidth, iCount; + + for (uAccum = 1, iCount = 0, iWidth = pState->iWidth; iCount < iWidth; iCount += 1) + { + ucrypt_t uResult, uVal = pState->aData[iCount]; + + // calculate the result after borrow + uResult = uVal - uAccum; + // calculate the new borrow if first or second calculation resulted in overflow + uAccum = (uResult > uVal); + // save result + _CryptBnSet(pState, iCount, uResult); + + // if nothing left to borrow we are done + if (uAccum == 0) + { + break; + } + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnModMultiply + + \Description + Modular multiply using the classical algorithm + + \Input *pState - pointer to output large number array + \Input *pMul1 - pointer to large number to multiply with pMul2 + \Input *pMul2 - pointer to second large number + \Input *pMod - pointer to modulous large number + + \Notes + pState:iWidth <-- pMul1:iWidth * pMul2:iWidth % pMod:iWidth + + \Version 02/27/2002 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnModMultiply(CryptBnT *pState, const CryptBnT *pMul1, const CryptBnT *pMul2, const CryptBnT *pMod) +{ + int32_t iCount; + CryptBnT Temp1; + CryptBnT *pCur = &Temp1; + uint32_t uSign = pMul2->uSign ^ pMul1->uSign; + + CryptBnInit(pCur, pMul1->iWidth); + + // do all the bits + for (iCount = CryptBnBitLen(pMul1) - 1; iCount >= 0; iCount -= 1) + { + // left shift the result + CryptBnLeftShift(pCur); + + // do modulus reduction? + if (CryptBnCompare(pCur, pMod) > 0) + { + CryptBnSubtract(pCur, pCur, pMod); + } + + // see if we need to add in multiplicand + if (CryptBnBitTest(pMul1, iCount)) + { + // add it in + CryptBnAccumulate(pCur, pMul2); + + // do modulus reduction? + if (CryptBnCompare(pCur, pMod) > 0) + { + CryptBnSubtract(pCur, pCur, pMod); + } + } + } + + // deal with going negative + if (uSign != 0) + { + CryptBnSubtract(pCur, pMod, pCur); + } + + // copy over the result + CryptBnClone(pState, pCur); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnMultiply + + \Description + Performs multiplication of two big numbers using the classical method + + \Input *pState - pointer to output large number array + \Input *pMul1 - pointer to large number to multiply with pMul2 + \Input *pMul2 - pointer to second large number + + \Notes + pState:iWidth <-- pMul1:iWidth * pMul2:iWidth + + \Version 02/28/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnMultiply(CryptBnT *pState, const CryptBnT *pMul1, const CryptBnT *pMul2) +{ + int32_t iCount; + CryptBnT Temp1; + CryptBnT *pCur = &Temp1; + + CryptBnInit(pCur, pMul1->iWidth); + + // do all the bits + for (iCount = CryptBnBitLen(pMul1) - 1; iCount >= 0; iCount -= 1) + { + // left shift the result + CryptBnLeftShift(pCur); + + // see if we need to add in multiplicand + if (CryptBnBitTest(pMul1, iCount)) + { + // add it in + CryptBnAccumulate(pCur, pMul2); + } + } + + pCur->uSign = pMul1->uSign ^ pMul2->uSign; + + // copy over the result + CryptBnClone(pState, pCur); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnMod + + \Description + Performs modulo / divison of dividend by divisor + + \Input *pDividend - the dividend in the equation + \Input *pDivisor - the divisor in the equation + \Input *pRemainder - [out] the result of the modulo operation can be NULL + \Input *pQuotient - [out] the result of the division operation can be NULL + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnMod(const CryptBnT *pDividend, const CryptBnT *pDivisor, CryptBnT *pRemainder, CryptBnT *pQuotient) +{ + int32_t iCount; + CryptBnT Result, Quotient; + + /* if the dividend is less than the divisor means the result of + the modulus is the dividend and the quotient is zero */ + const int32_t iCompare = CryptBnCompare(pDividend, pDivisor); + if (iCompare < 0) + { + CryptBnClone(&Result, pDividend); + CryptBnInitSet(&Quotient, 0); + } + else if (iCompare > 0) + { + CryptBnInitSet(&Result, 0); + CryptBnInitSet(&Quotient, 0); + + /* since we don't need to perform the other operations until we increment + past zero, lets do this in a loop before we move onto the normal flow */ + for (iCount = CryptBnBitLen(pDividend) - 1; iCount >= 0; iCount -= 1) + { + if (CryptBnBitTest(pDividend, iCount)) + { + CryptBnIncrement(&Result); + CryptBnLeftShift(&Result); + break; + } + } + + for (iCount = iCount - 1; iCount >= 0; iCount -= 1) + { + if (CryptBnBitTest(pDividend, iCount)) + { + CryptBnIncrement(&Result); + } + + // check if we need to reduce, we can bypass when the limbs don't match + if ((Result.iWidth >= pDivisor->iWidth) && (CryptBnCompare(&Result, pDivisor) >= 0)) + { + CryptBnSubtract(&Result, &Result, pDivisor); + CryptBnBitSet(&Quotient, iCount); + } + + if (iCount > 0) + { + CryptBnLeftShift(&Result); + } + } + } + else + { + CryptBnInitSet(&Result, 0); + CryptBnInitSet(&Quotient, 1); + } + + if ((pDividend->uSign != 0) && (CryptBnBitLen(&Result) != 0)) + { + Result.uSign = 0; + CryptBnSubtract(&Result, pDivisor, &Result); + } + if (pRemainder != NULL) + { + CryptBnClone(pRemainder, &Result); + } + if (pQuotient != NULL) + { + CryptBnClone(pQuotient, &Quotient); + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnInverseMod + + \Description + Calculates the modular multiplicative inverse using binary extended gcd + + \Input *pState - the big number state + \Input *pMod - the modulus + + \Notes + See Handbook of Applied Cryptography Chapter 14.4.3 (14.61) + http://cacr.uwaterloo.ca/hac/about/chap14.pdf + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnInverseMod(CryptBnT *pState, const CryptBnT *pMod) +{ + int32_t iShift; + CryptBnT U, V, A, B, C, D; + + // if the number is negative then switch the sign and subtract the result from mod + if (pState->uSign != 0) + { + pState->uSign = 0; + CryptBnInverseMod(pState, pMod); + CryptBnSubtract(pState, pMod, pState); + return; + } + + CryptBnClone(&U, pState); + CryptBnClone(&V, pMod); + CryptBnInitSet(&A, 1); + CryptBnInitSet(&B, 0); + CryptBnInitSet(&C, 0); + CryptBnInitSet(&D, 1); + + // while u and v are even divide by zero, accounting for the number of shifts + for (iShift = 0; (_CryptBnIsEven(&U) && _CryptBnIsEven(&V)); iShift += 1) + { + CryptBnRightShift(&U); + CryptBnRightShift(&V); + } + + // quit out when u is zero + while (!_CryptBnIsZero(&U)) + { + // while u is even + while (_CryptBnIsEven(&U)) + { + // u <- u/2 + CryptBnRightShift(&U); + + /* if a congruent to b congruent to 0 mod 2 (they are both even) + then a <- a/2 and b <- b/2 */ + if (_CryptBnIsEven(&A) && _CryptBnIsEven(&B)) + { + CryptBnRightShift(&A); + CryptBnRightShift(&B); + } + // else a <- (a+y)/2 and b <- (b-x)/2 + else + { + CryptBnAccumulate(&A, pMod); + CryptBnRightShift(&A); + CryptBnSubtract(&B, &B, pState); + CryptBnRightShift(&B); + } + } + + + // while v is even + while (_CryptBnIsEven(&V)) + { + // v <- v/2 + CryptBnRightShift(&V); + + /* if c congruent to d congruent to 0 mod 2 (they are both even) + then c <- c/2 and d <- d/2 */ + if (_CryptBnIsEven(&C) && _CryptBnIsEven(&D)) + { + CryptBnRightShift(&C); + CryptBnRightShift(&D); + } + // else c <- (c+y)/2 and d <- (d-x)/2 + else + { + CryptBnAccumulate(&C, pMod); + CryptBnRightShift(&C); + CryptBnSubtract(&D, &D, pState); + CryptBnRightShift(&D); + } + } + + // if u > v then u <- u-v, a <- a-c and b <- b-d + if (CryptBnCompare(&U, &V) >= 0) + { + CryptBnSubtract(&U, &U, &V); + CryptBnSubtract(&A, &A, &C); + CryptBnSubtract(&B, &B, &D); + } + // else v <- v-u, c <- c-a and d <- d-b + else + { + CryptBnSubtract(&V, &V, &U); + CryptBnSubtract(&C, &C, &A); + CryptBnSubtract(&D, &D, &B); + } + } + + // a <- C, b <- D but we only need a for the inverse so return that + CryptBnClone(pState, &C); + + /* note: if you needed to return v, you would need to left shift by iShift + but since we don't we don't do anything else */ +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnFinal + + \Description + Convert from ucrypt_t units back to big-endian bytes + + \Input *pState - the ucrypt_t units to convert + \Input *pResult - where to write the 8-bit result. + \Input iLength - size of the result array in bytes. + + \Version 03/08/2002 (gschaefer) +*/ +/*************************************************************************************H*/ +void CryptBnFinal(const CryptBnT *pState, uint8_t *pResult, int32_t iLength) +{ + const ucrypt_t *pSource = pState->aData; + + // make the length in terms of words + iLength /= sizeof(*pSource); + + // word to byte conversion + for (; iLength > 0; iLength -= 1) + { + #if UCRYPT_SIZE > 4 + *pResult++ = (uint8_t)(pSource[iLength-1] >> 56); + *pResult++ = (uint8_t)(pSource[iLength-1] >> 48); + *pResult++ = (uint8_t)(pSource[iLength-1] >> 40); + *pResult++ = (uint8_t)(pSource[iLength-1] >> 32); + #endif + *pResult++ = (uint8_t)(pSource[iLength-1] >> 24); + *pResult++ = (uint8_t)(pSource[iLength-1] >> 16); + *pResult++ = (uint8_t)(pSource[iLength-1] >> 8); + *pResult++ = (uint8_t)(pSource[iLength-1]); + } +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnFinalLe + + \Description + Convert from ucrypt_t units back to little-endian bytes + + \Input *pState - the ucrypt_t units to convert + \Input *pResult - where to write the 8-bit result. + \Input iLength - size of the result array in bytes. + + \Version 04/11/2018 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnFinalLe(const CryptBnT *pState, uint8_t *pResult, int32_t iLength) +{ + ds_memcpy_s(pResult, iLength, pState->aData, CryptBnByteLen(pState)); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnClone + + \Description + Copies a big number's contents + + \Input *pDst - where we are copying to + \Input *pSrc - where we are copying from + + \Version 02/27/2017 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnClone(CryptBnT *pDst, const CryptBnT *pSrc) +{ + ds_memcpy(pDst, pSrc, sizeof(*pDst)); +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnPrint + + \Description + Prints the contents of a big number + + \Input *pState - the big number we are printing + \Input *pTitle - title of the big number + + \Version 02/06/2018 (eesponda) +*/ +/*************************************************************************************H*/ +void CryptBnPrint(const CryptBnT *pState, const char *pTitle) +{ + #if DIRTYCODE_LOGGING + int32_t iIndex, iOffset; + char strNumber[16*CRYPTBN_MAX_WIDTH]; + const char *pPrefix = (pState->uSign == 0) ? "0x" : "-0x"; + + for (iIndex = pState->iWidth, iOffset = 0; iIndex > 0; iIndex -= 1) + { + iOffset += ds_snzprintf(strNumber+iOffset, sizeof(strNumber)-iOffset, "%016llx", pState->aData[iIndex - 1]); + } + NetPrintf(("cryptbn: %s %s%s (%d)\n", pTitle, pPrefix, strNumber, CryptBnBitLen(pState))); + #endif +} + +/*F*************************************************************************************/ +/*! + \Function CryptBnCompare + + \Description + Compare two big numbers together + + \Input *pLhs - the left side + \Input *pRhs - the right side + + \Output + int32_t - 0=equal, >0=left side larger, <0=right side larger + + \Version 02/17/2017 (eesponda) +*/ +/*************************************************************************************H*/ +int32_t CryptBnCompare(const CryptBnT *pLhs, const CryptBnT *pRhs) +{ + int32_t iWidth; + + for (iWidth = DS_MAX(pLhs->iWidth, pRhs->iWidth); iWidth > 0; iWidth -= 1) + { + ucrypt_t uLhs = pLhs->aData[iWidth - 1], uRhs = pRhs->aData[iWidth - 1]; + if (uLhs == uRhs) + { + continue; + } + + return((uLhs > uRhs) ? 1 : -1); + } + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptchacha.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptchacha.c new file mode 100644 index 00000000..a9289f3e --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptchacha.c @@ -0,0 +1,621 @@ +/*H********************************************************************************/ +/*! + \File cryptchacha.c + + \Description + Implements the ChaCha20-Poly1305 AEAD cipher + + \Notes + This implementation is based on the IETF Protocol and implements the + ChaCha20-Poly1305 AEAD cipher as used in TLS et all. + + References: + - ChaCha20 and Poly1305 for IETF Protocols: https://tools.ietf.org/html/rfc8439 + - ChaCha20-Poly1305 Cipher Suites for TLS: https://tools.ietf.org/html/rfc7905 + - Original implementation of ChaCha: https://cr.yp.to/chacha.html + - Original implementation of Poly1305: https://cr.yp.to/mac.html + + As per https://tools.ietf.org/html/rfc7539#section-2.7, Poly1305 requires + a one-time key and is "...biased, unlike HMAC". As such it is not suitable + for general use, and is therefore included dicrectly in the ChaCha20 + AEAD cipher implementation. + + \Copyright + Copyright (c) 2018 Electronic Arts + + \Version 02/12/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" + +#include "DirtySDK/crypt/cryptbn.h" +#include "DirtySDK/crypt/cryptchacha.h" + +/*** Defines **********************************************************************/ + +#define CRYPTCHACHA_VERBOSE (DIRTYCODE_DEBUG && FALSE) + +/*** Macros ***********************************************************************/ + +#define CHACHA_RDWORD(_ptr) (((uint32_t)(_ptr)[0]) | ((uint32_t)((_ptr)[1])<<8) | (((uint32_t)(_ptr)[2])<<16) | (((uint32_t)(_ptr)[3])<<24)) +#define CHACHA_WRWORD(_ptr, _x) ((_ptr)[0] = (uint8_t)(_x),\ + (_ptr)[1] = (uint8_t)((_x)>>8),\ + (_ptr)[2] = (uint8_t)((_x)>>16),\ + (_ptr)[3] = (uint8_t)((_x)>>24)) +#define CHACHA_ROTATE(_x, _n) (((_x)<<(_n))|((_x)>>(32-(_n)))) +#define CHACHA_XOR(_v, _w) ((_v) ^ (_w)) +#define CHACHA_PLUS(_v, _w) ((_v) + (_w)) +#define CHACHA_PLUSONE(_v) (CHACHA_PLUS((_v), 1)) + +#define CHACHA_QUARTERROUND(_x, _a, _b, _c, _d) \ + (_x)[_a] = CHACHA_PLUS((_x)[_a], (_x)[_b]); (_x)[_d] = CHACHA_ROTATE(CHACHA_XOR((_x)[_d], (_x)[_a]), 16); \ + (_x)[_c] = CHACHA_PLUS((_x)[_c], (_x)[_d]); (_x)[_b] = CHACHA_ROTATE(CHACHA_XOR((_x)[_b], (_x)[_c]), 12); \ + (_x)[_a] = CHACHA_PLUS((_x)[_a], (_x)[_b]); (_x)[_d] = CHACHA_ROTATE(CHACHA_XOR((_x)[_d], (_x)[_a]), 8); \ + (_x)[_c] = CHACHA_PLUS((_x)[_c], (_x)[_d]); (_x)[_b] = CHACHA_ROTATE(CHACHA_XOR((_x)[_b], (_x)[_c]), 7); + +/*** Type Definitions *************************************************************/ + +//! Poly1305 state +typedef struct CryptPolyT +{ + CryptBnT r, s, a, p; +} CryptPolyT; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/* + Poly1305 Routines +*/ + +/*F********************************************************************************/ +/*! + \Function _CryptPolyLE16 + + \Description + Covert input to little endian and pad to 16 bytes + + \Input *pOutput - [out] output for padded and flipped data + \Input *pInput - input to pad/flip + \Input iLength - length of input data + + \Output + int32_t - length (16) + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CryptPolyLE16(uint8_t *pOutput, const uint8_t *pInput, int32_t iLength) +{ + int32_t iData; + if (iLength > 16) + { + iLength = 16; + } + for (iData = 0; iData < 16-iLength; iData += 1) + { + pOutput[iData] = 0; + } + for ( ; iData < 16; iData += 1) + { + pOutput[iData] = pInput[16-iData-1]; + } + return(16); +} + +/*F********************************************************************************/ +/*! + \Function _CryptPolyBnInit + + \Description + Init a BigNumber from input data, optionally extending with 0x01 + + \Input *pState - BigNumber to init + \Input iWidth - width to pass through to CryptBnInit + \Input *pBuffer - input data + \Input iLength - length of input data + \Input iExtra - one to extend with 0x01, else zero + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptPolyBnInit(CryptBnT *pState, int32_t iWidth, const uint8_t *pBuffer, int32_t iLength, int32_t iExtra) +{ + uint8_t aLEData[17]; + if (iExtra) + { + aLEData[0] = 0x01; + } + iLength = _CryptPolyLE16(aLEData+iExtra, pBuffer, iLength); + CryptBnInitFrom(pState, iWidth, aLEData, iLength+iExtra); +} + +/*F********************************************************************************/ +/*! + \Function _CryptPolyClamp + + \Description + Clamp vector as per https://tools.ietf.org/html/rfc7539#section-2.5 + + \Input *pInput - input vector to clamp + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptPolyClamp(uint8_t *pInput) +{ + pInput[3] &= 15; + pInput[7] &= 15; + pInput[11] &= 15; + pInput[15] &= 15; + pInput[4] &= 252; + pInput[8] &= 252; + pInput[12] &= 252; +} + +/*F********************************************************************************/ +/*! + \Function _CryptPolyInit + + \Description + Init Poly state with specified key + + \Input *pState - module state + \Input *pKey - input encryption key + \Input iKeyLen - length of key + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptPolyInit(CryptPolyT *pState, uint8_t *pKey, int32_t iKeyLen) +{ + static const uint8_t p_src[] = { 0x3,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfb }; + // clamp(r): r &= 0x0ffffffc0ffffffc0ffffffc0fffffff + _CryptPolyClamp(pKey); + // r = (le_bytes_to_num(key[0..15]) + _CryptPolyBnInit(&pState->r, -1, pKey, 16, 0); + // s = le_num(key[16..31]) + _CryptPolyBnInit(&pState->s, -1, pKey+16, 16, 0); + // accumulator = 0 + CryptBnInitSet(&pState->a, 0); + // p = (1<<130)-5 + CryptBnInitFrom(&pState->p, -1, p_src, sizeof(p_src)); +} + +/*F********************************************************************************/ +/*! + \Function _CryptPolyUpdate + + \Description + Update Poly state with specified input data. Data is padded to 16 byte + width and extended with 0x1 as per https://tools.ietf.org/html/rfc7539#section-2.5.1 + + \Input *pState - module state + \Input *pData - input data to process + \Input iLength - length of data + + \Notes + BnMultiply and BnMod steps are utilized instead of BnModMultiply because + the product of a and r can exceed the size of the modulus p; this is not + supported by CryptBn. + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptPolyUpdate(CryptPolyT *pState, const uint8_t *pData, int32_t iLength) +{ + CryptBnT n; + + // for i=1 upto ceil(msg length in bytes / 16) + for ( ; iLength > 0; pData += 16, iLength -= 16) + { + // n = le_bytes_to_num(msg[((i-1)*16)..(i*16)] | [0x01]) + _CryptPolyBnInit(&n, -1, pData, iLength, 1); + // a += n + CryptBnAccumulate(&pState->a, &n); + // a *= r + CryptBnMultiply(&pState->a, &pState->r, &pState->a); + // a %= p + CryptBnMod(&pState->a, &pState->p, &pState->a, NULL); + } +} + +/*F********************************************************************************/ +/*! + \Function _CryptPolyFinal + + \Description + Generate final tag + + \Input *pState - module state + \Input *pData - [out] output buffer to write tag to + \Input iLength - length of buffer + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptPolyFinal(CryptPolyT *pState, uint8_t *pData, int32_t iLength) +{ + uint8_t aTag[16]; + // a += s + CryptBnAccumulate(&pState->a, &pState->s); + // num_to_16_le_bytes(a) + CryptBnFinal(&pState->a, aTag, sizeof(aTag)); + // write out the tag + _CryptPolyLE16(pData, aTag, iLength); +} + +/*F********************************************************************************/ +/*! + \Function _CryptPolyGenerateTag + + \Description + Generate authentication tag based on input data, key, and additional + authentication data. + + \Input *pBuffer - input data to generate tag for + \Input iLength - length of input data + \Input *pKey - encryption key used for poly operation + \Input iKeyLen - length of key + \Input *pAddData - additional unencrypted data + \Input iAddLen - length of additional data + \Input *pTag - [out] buffer to write tag to + \Input iTagLen - length of tag buffer + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptPolyGenerateTag(const uint8_t *pBuffer, int32_t iLength, uint8_t *pKey, int32_t iKeyLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen) +{ + CryptPolyT Poly; + uint8_t aLengths[16]; + + // generate add and txt lengths + aLengths[0] = (uint8_t)(iAddLen>>0); + aLengths[1] = (uint8_t)(iAddLen>>8); + aLengths[2] = (uint8_t)(iAddLen>>16); + aLengths[3] = (uint8_t)(iAddLen>>24); + aLengths[4] = aLengths[5] = aLengths[6] = aLengths[7] = 0; + aLengths[8] = (uint8_t)(iLength>>0); + aLengths[9] = (uint8_t)(iLength>>8); + aLengths[10] = (uint8_t)(iLength>>16); + aLengths[11] = (uint8_t)(iLength>>24); + aLengths[12] = aLengths[13] = aLengths[14] = aLengths[15] = 0; + + // init poly state + _CryptPolyInit(&Poly, pKey, iKeyLen); + + // update with add data (padded) | text (padded) | lengths + _CryptPolyUpdate(&Poly, pAddData, iAddLen); + _CryptPolyUpdate(&Poly, pBuffer, iLength); + _CryptPolyUpdate(&Poly, aLengths, sizeof(aLengths)); + + // generate tag + _CryptPolyFinal(&Poly, pTag, iTagLen); +} + +/* + ChaCha20 Routines +*/ + +/*F********************************************************************************/ +/*! + \Function _ChaCha20Block + + \Description + ChaCha20 block round as per https://tools.ietf.org/html/rfc7539#section-2.3.1 + + \Input *pOutput - [out] buffer to write output + \Input *pInput - input for block round + + \Version 02/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ChaCha20Block(uint8_t *pOutput, const uint32_t *pInput) +{ + uint32_t aBlock[16]; + int32_t iBlock; + + // read iBlockInput + ds_memcpy(aBlock, pInput, sizeof(aBlock)); + + // quarter rounds; note that the original reference had eight rounds, while the IETF version has ten + for (iBlock = 0; iBlock < 10; iBlock += 1) + { + CHACHA_QUARTERROUND(aBlock, 0, 4, 8, 12); + CHACHA_QUARTERROUND(aBlock, 1, 5, 9, 13); + CHACHA_QUARTERROUND(aBlock, 2, 6, 10, 14); + CHACHA_QUARTERROUND(aBlock, 3, 7, 11, 15); + CHACHA_QUARTERROUND(aBlock, 0, 5, 10, 15); + CHACHA_QUARTERROUND(aBlock, 1, 6, 11, 12); + CHACHA_QUARTERROUND(aBlock, 2, 7, 8, 13); + CHACHA_QUARTERROUND(aBlock, 3, 4, 9, 14); + } + + // accumulate input and write output + for (iBlock = 0; iBlock < 16; iBlock += 1) + { + CHACHA_WRWORD(pOutput + (4*iBlock), CHACHA_PLUS(aBlock[iBlock], pInput[iBlock])); + } +} + +/*F********************************************************************************/ +/*! + \Function _CryptChaChaInitKey + + \Description + Initialize crypt state based on input key + + \Input *pChaCha - cipher state + \Input *pKeyBuf - input key + \Input iKeyLen - length of key in bytes + + \Version 02/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptChaChaInitKey(CryptChaChaT *pChaCha, const uint8_t *pKeyBuf, int32_t iKeyLen) +{ + static const char _strSigma[16] = "expand 32-byte k"; + static const char _strTau[16] = "expand 16-byte k"; + const char *pConstant; + + // read first half of key + pChaCha->aInput[4] = CHACHA_RDWORD(pKeyBuf+0); + pChaCha->aInput[5] = CHACHA_RDWORD(pKeyBuf+4); + pChaCha->aInput[6] = CHACHA_RDWORD(pKeyBuf+8); + pChaCha->aInput[7] = CHACHA_RDWORD(pKeyBuf+12); + + // determine 2nd half based on key length + if (iKeyLen == 32) + { + pKeyBuf += 16; + pConstant = _strSigma; + } + else + { + pConstant = _strTau; + } + + // read second half of key + pChaCha->aInput[8] = CHACHA_RDWORD(pKeyBuf+0); + pChaCha->aInput[9] = CHACHA_RDWORD(pKeyBuf+4); + pChaCha->aInput[10] = CHACHA_RDWORD(pKeyBuf+8); + pChaCha->aInput[11] = CHACHA_RDWORD(pKeyBuf+12); + + // read in constant + pChaCha->aInput[0] = CHACHA_RDWORD(pConstant+0); + pChaCha->aInput[1] = CHACHA_RDWORD(pConstant+4); + pChaCha->aInput[2] = CHACHA_RDWORD(pConstant+8); + pChaCha->aInput[3] = CHACHA_RDWORD(pConstant+12); +} + +/*F********************************************************************************/ +/*! + \Function _CryptChaChaGeneratePolyKey + + \Description + Generate the Poly key as per https://tools.ietf.org/html/rfc7539#section-2.6 + + \Input *pChaCha - cipher state + \Input *pKey - [out] buffer for generated key + \Input iKeyLen - length of buffer + + \Version 02/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptChaChaGeneratePolyKey(CryptChaChaT *pChaCha, uint8_t *pKey, int32_t iKeyLen) +{ + uint8_t aPoly[64]; + uint32_t uCounter; + // save current counter, set it to zero + uCounter = pChaCha->aInput[12]; + pChaCha->aInput[12] = 0; + // use chacha20 to generate key state + _ChaCha20Block(aPoly, pChaCha->aInput); + // clamp r + _CryptPolyClamp(aPoly); + // copy out key + ds_memcpy_s(pKey, iKeyLen, aPoly, 32); + // restore counter + pChaCha->aInput[12] = uCounter; +} + +/*F********************************************************************************/ +/*! + \Function _CryptChaChaInitIv + + \Description + Initialize IV/Nonce portion of ChaCha state as per + https://tools.ietf.org/html/rfc7539#section-2.3 + + \Input *pChaCha - cipher state + \Input *pInitVec - input iv + \Input iIvLen - iv length + + \Version 02/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptChaChaInitIv(CryptChaChaT *pChaCha, const uint8_t *pInitVec, int32_t iIvLen) +{ + pChaCha->aInput[12] = 1; // as per https://tools.ietf.org/html/rfc7539#section-2.8 initial counter value is one + pChaCha->aInput[13] = CHACHA_RDWORD(pInitVec+0); + pChaCha->aInput[14] = CHACHA_RDWORD(pInitVec+4); + pChaCha->aInput[15] = CHACHA_RDWORD(pInitVec+8); +} + +/*F********************************************************************************/ +/*! + \Function _CryptChaChaEncrypt + + \Description + Encrypted plaintext as per https://tools.ietf.org/html/rfc7539#section-2.4 + + \Input *pChaCha - cipher state + \Input *pInput - input to encrypt + \Input *pOutput - [out] buffer to write decrypted data to (may overlap) + \Input iLength - data length + + \Version 02/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptChaChaEncrypt(CryptChaChaT *pChaCha, const uint8_t *pInput, uint8_t *pOutput, int32_t iLength) +{ + int32_t iCount, iCopyLen; + uint8_t aOutput[64]; + + while (iLength > 0) + { + _ChaCha20Block(aOutput, pChaCha->aInput); + pChaCha->aInput[12] = CHACHA_PLUSONE(pChaCha->aInput[12]); + if (!pChaCha->aInput[12]) + { + pChaCha->aInput[13] = CHACHA_PLUSONE(pChaCha->aInput[13]); + } + for (iCount = 0, iCopyLen = (iLength >= 64) ? 64 : iLength; iCount < iCopyLen; iCount += 1) + { + pOutput[iCount] = pInput[iCount] ^ aOutput[iCount]; + } + iLength -= iCopyLen; + pInput += iCopyLen; + pOutput += iCopyLen; + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptChaChaInit + + \Description + Init the ChaCha cipher with the specified key + + \Input *pChaCha - cipher state to initialize + \Input *pKeyBuf - pointer to key + \Input iKeyLen - key length + + \Version 02/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptChaChaInit(CryptChaChaT *pChaCha, const uint8_t *pKeyBuf, int32_t iKeyLen) +{ + // clear state + ds_memclr(pChaCha, sizeof(*pChaCha)); + // init state with key + _CryptChaChaInitKey(pChaCha, pKeyBuf, iKeyLen); +} + +/*F********************************************************************************/ +/*! + \Function CryptChaChaEncrypt + + \Description + Encrypt input data in place and optionally generate authentication tag + + \Input *pChaCha - cipher state + \Input *pBuffer - [inp/out] data to encrypt + \Input iLength - length of data to encrypt + \Input *pInitVec - initialization vector (IV) + \Input iIvLen - IV len + \Input *pAddData - additional authenticated data + \Input iAddLen - length of additional data + \Input *pTag - [out] tag buffer + \Input iTagLen - tag length + + \Notes + Pass NULL to pTag if you don't want authentication tag to be generated. + + \Output + int32_t - encrypted data size + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CryptChaChaEncrypt(CryptChaChaT *pChaCha, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen) +{ + uint8_t aPolyKey[32]; + + // set IV in state + _CryptChaChaInitIv(pChaCha, pInitVec, iIvLen); + + if (pTag != NULL) + { + // generate the Poly1305 key + _CryptChaChaGeneratePolyKey(pChaCha, aPolyKey, sizeof(aPolyKey)); + } + + // encrypt the input + _CryptChaChaEncrypt(pChaCha, pBuffer, pBuffer, iLength); + + if (pTag != NULL) + { + // generate the authentication tag + _CryptPolyGenerateTag(pBuffer, iLength, aPolyKey, sizeof(aPolyKey), pAddData, iAddLen, pTag, iTagLen); + } + + // return success + return(iLength); +} + +/*F********************************************************************************/ +/*! + \Function CryptChaChaDecrypt + + \Description + Decrypt input data in place and optionally generate authentication tag; + verify it matches the specified tag. + + \Input *pChaCha - cipher state + \Input *pBuffer - [inp/out] data to decrypt + \Input iLength - length of data to decrypt + \Input *pInitVec - initialization vector (IV) + \Input iIvLen - IV len + \Input *pAddData - additional authenticated data + \Input iAddLen - length of additional data + \Input *pTag - tag buffer + \Input iTagLen - tag length + + \Notes + Pass NULL to pTag if you don't want to generate and verify the authentication + tag. + + \Output + int32_t - decrypted data size + + \Version 02/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CryptChaChaDecrypt(CryptChaChaT *pChaCha, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, const uint8_t *pTag, int32_t iTagLen) +{ + uint8_t aPolyKey[32], aTag[16]; + + // set IV in state + _CryptChaChaInitIv(pChaCha, pInitVec, iIvLen); + + if (pTag != NULL) + { + // generate the Poly1305 key + _CryptChaChaGeneratePolyKey(pChaCha, aPolyKey, sizeof(aPolyKey)); + + // generate the authentication tag on the encrypted data + _CryptPolyGenerateTag(pBuffer, iLength, aPolyKey, sizeof(aPolyKey), pAddData, iAddLen, aTag, sizeof(aTag)); + } + + // do the decrypt + _CryptChaChaEncrypt(pChaCha, pBuffer, pBuffer, iLength); + + // return length of decrypted data, or -1 if tag mismatch + return((pTag == NULL) || !memcmp(pTag, aTag, iTagLen) ? iLength : -1); +} + + diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptcurve.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptcurve.c new file mode 100644 index 00000000..340efa8b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptcurve.c @@ -0,0 +1,98 @@ +/*H********************************************************************************/ +/*! + \File cryptcurve.c + + \Description + This module implements an interface over our different curve crypto APIs + + \Copyright + Copyright (c) Electronic Arts 2018. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/crypt/cryptmont.h" +#include "DirtySDK/crypt/cryptcurve.h" + +/*** Variables ********************************************************************/ + +//! mappings for the dh functions +static const CryptCurveDhT _CryptCurve_Dh[] = +{ + { (CryptCurveInitT *)CryptNistInitDh, (CryptCurvePublicT *)CryptNistPublic, (CryptCurveSecretT *)CryptNistSecret, (CryptCurvePointInitT *)CryptNistPointInitFrom, (CryptCurvePointFinalT *)CryptNistPointFinal, CRYPTCURVE_SECP256R1 }, + { (CryptCurveInitT *)CryptNistInitDh, (CryptCurvePublicT *)CryptNistPublic, (CryptCurveSecretT *)CryptNistSecret, (CryptCurvePointInitT *)CryptNistPointInitFrom, (CryptCurvePointFinalT *)CryptNistPointFinal, CRYPTCURVE_SECP384R1 }, + { (CryptCurveInitT *)CryptMontInit, (CryptCurvePublicT *)CryptMontPublic, (CryptCurveSecretT *)CryptMontSecret, (CryptCurvePointInitT *)CryptMontPointInitFrom, (CryptCurvePointFinalT *)CryptMontPointFinal, CRYPTCURVE_X25519 }, + { (CryptCurveInitT *)CryptMontInit, (CryptCurvePublicT *)CryptMontPublic, (CryptCurveSecretT *)CryptMontSecret, (CryptCurvePointInitT *)CryptMontPointInitFrom, (CryptCurvePointFinalT *)CryptMontPointFinal, CRYPTCURVE_X448 } +}; + +//! mappings for the dsa functions +static const CryptCurveDsaT _CryptCurve_Dsa[] = +{ + { (CryptCurveInitT *)CryptNistInitDsa, (CryptCurveSignT *)CryptNistSign, (CryptCurveVerifyT *)CryptNistVerify, (CryptCurvePointInitT *)CryptNistPointInitFrom, CRYPTCURVE_SECP256R1 }, + { (CryptCurveInitT *)CryptNistInitDsa, (CryptCurveSignT *)CryptNistSign, (CryptCurveVerifyT *)CryptNistVerify, (CryptCurvePointInitT *)CryptNistPointInitFrom, CRYPTCURVE_SECP384R1 } +}; + +/*** Public Functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CryptCurveGetDh + + \Description + Gets the diffie hellmen functions based on the curve type + + \Input iCurveType - type that identifies the curve + + \Output + const CryptCurveDhT * - pointer containing functions or NULL (not found) + + \Version 05/04/2018 (eesponda) +*/ +/********************************************************************************F*/ +const CryptCurveDhT *CryptCurveGetDh(int32_t iCurveType) +{ + const CryptCurveDhT *pCurve = NULL; + int32_t iCurveIdx; + + for (iCurveIdx = 0; iCurveIdx < (signed)(sizeof(_CryptCurve_Dh)/sizeof(_CryptCurve_Dh[0])); iCurveIdx += 1) + { + if (_CryptCurve_Dh[iCurveIdx].iCurveType == iCurveType) + { + pCurve = &_CryptCurve_Dh[iCurveIdx]; + break; + } + } + return(pCurve); +} + +/*F********************************************************************************/ +/*! + \Function CryptCurveGetDsa + + \Description + Gets the digital signature algorithm functions based on the curve type + + \Input iCurveType - type that identifies the curve + + \Output + const CryptCurveDsaT * - pointer containing functions or NULL (not found) + + \Version 05/04/2018 (eesponda) +*/ +/********************************************************************************F*/ +const CryptCurveDsaT *CryptCurveGetDsa(int32_t iCurveType) +{ + const CryptCurveDsaT *pCurve = NULL; + int32_t iCurveIdx; + + for (iCurveIdx = 0; iCurveIdx < (signed)(sizeof(_CryptCurve_Dsa)/sizeof(_CryptCurve_Dsa[0])); iCurveIdx += 1) + { + if (_CryptCurve_Dsa[iCurveIdx].iCurveType == iCurveType) + { + pCurve = &_CryptCurve_Dsa[iCurveIdx]; + break; + } + } + return(pCurve); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptgcm.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptgcm.c new file mode 100644 index 00000000..cb83b677 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptgcm.c @@ -0,0 +1,630 @@ +/*H********************************************************************************/ +/*! + \File cryptgcm.c + + \Description + An implementation of the GCM-128 and GCM-256 cipher, based on the NIST + standard, intended for use in implementation of TLS1.1 GCM cipher suites. + + This implementation uses Shoup's method utilizing 4-bit tables as described + in the GCM specifications. While not particularly fast, table generation + is quick and memory usage required small. This implementation is restricted + to a feature set suitable for implementation of TLS1.1 GCM cipher suites. + + \Notes + References: + - GCM specifications: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf + - NIST recommendation: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf + - TLS1.1 RFC AEAD ciphers: http://tools.ietf.org/html/rfc5246#section-6.2.3.3 + - TLS1.1 AES-GCM cipher specifications: http://tools.ietf.org/html/rfc5288 + - AEAD AES-GCM definitions: http://tools.ietf.org/html/rfc5116 + + \Copyright + Copyright (c) 2014 Electronic Arts + + \Version 07/01/2014 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" + +#include "DirtySDK/crypt/cryptgcm.h" + +/*** Defines **********************************************************************/ + +#define CRYPTGCM_VERBOSE (DIRTYCODE_DEBUG && FALSE) + +/*** Macros ***********************************************************************/ + +/* + Read/write big-endian words +*/ +#define CRYPTGCM_RDWORD(_ptr) ((((uint32_t)(_ptr)[0]) << 24) | ((uint32_t)((_ptr)[1]) << 16) | (((uint32_t)(_ptr)[2]) << 8) | ((uint32_t)(_ptr)[3])) +#define CRYPTGCM_WRWORD(_x, _ptr) ((_ptr)[3] = (uint8_t)(_x),\ + (_ptr)[2] = (uint8_t)((_x)>>8),\ + (_ptr)[1] = (uint8_t)((_x)>>16),\ + (_ptr)[0] = (uint8_t)((_x)>>24)) + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// shoup's 4bit multiplication table +static const uint64_t _CryptGCM_aLast4[16] = +{ + 0x0000, 0x1c20, 0x3840, 0x2460, + 0x7080, 0x6ca0, 0x48c0, 0x54e0, + 0xe100, 0xfd20, 0xd940, 0xc560, + 0x9180, 0x8da0, 0xa9c0, 0xb5e0 +}; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CryptGcmGenTable + + \Description + Precompute GCM multiplication table + + \Input *pGcm - cipher state + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptGcmGenTable(CryptGcmT *pGcm) +{ + uint64_t uHi, uLo; + uint64_t uVh, uVl; + uint8_t hInit[16]; + int32_t i, j; + + // generate table source + ds_memclr(hInit, sizeof(hInit)); + CryptAesEncryptBlock(&pGcm->KeySchedule, hInit, hInit); + + // convert table source to 64bit ints + uHi = CRYPTGCM_RDWORD(hInit+0); + uLo = CRYPTGCM_RDWORD(hInit+4); + uVh = (uint64_t) uHi << 32 | uLo; + + uHi = CRYPTGCM_RDWORD(hInit+8); + uLo = CRYPTGCM_RDWORD(hInit+12); + uVl = (uint64_t) uHi << 32 | uLo; + + // init table + pGcm->HL[8] = uVl; + pGcm->HH[8] = uVh; + pGcm->HH[0] = 0; + pGcm->HL[0] = 0; + + for (i = 4; i > 0; i >>= 1) + { + uint32_t uT= (uVl & 1) * 0xe1000000U; + uVl = (uVh << 63) | (uVl >> 1); + uVh = (uVh >> 1) ^ ((uint64_t)uT << 32); + + pGcm->HL[i] = uVl; + pGcm->HH[i] = uVh; + } + + for (i = 2; i < 16; i <<= 1) + { + uint64_t *pHiL = pGcm->HL + i; + uint64_t *pHiH = pGcm->HH + i; + uVh = *pHiH; + uVl = *pHiL; + for (j = 1; j < i; j += 1) + { + pHiH[j] = uVh ^ pGcm->HH[j]; + pHiL[j] = uVl ^ pGcm->HL[j]; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _CryptGcmMultiply + + \Description + GCM multiply of input block by precomputed table + + \Input *pGcm - cipher state + \Input *pOut - [out] storage for result + \Input *pInp - input block to multiply + + \Notes + pOut may overlap pInp + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptGcmMultiply(CryptGcmT *pGcm, uint8_t *pOut, const uint8_t *pInp) +{ + uint8_t uLo, uHi; // lookups into HL/HH + uint8_t uRem; // lookup into static table + uint64_t uZh, uZl; + int32_t iData; + + uLo = pInp[15] & 0xf; + uZh = pGcm->HH[uLo]; + uZl = pGcm->HL[uLo]; + + for (iData = 15; iData >= 0; iData -= 1) + { + uLo = pInp[iData] & 0xf; + uHi = pInp[iData] >> 4; + + if (iData != 15) + { + uRem = (uint8_t) uZl & 0xf; + uZl = (uZh << 60) | (uZl >> 4); + uZh = (uZh >> 4); + uZh ^= (uint64_t) _CryptGCM_aLast4[uRem] << 48; + uZh ^= pGcm->HH[uLo]; + uZl ^= pGcm->HL[uLo]; + + } + + uRem = (uint8_t) uZl & 0xf; + uZl = (uZh << 60) | (uZl >> 4); + uZh = (uZh >> 4); + uZh ^= (uint64_t) _CryptGCM_aLast4[uRem] << 48; + uZh ^= pGcm->HH[uHi]; + uZl ^= pGcm->HL[uHi]; + } + + CRYPTGCM_WRWORD(uZh >> 32, pOut+0); + CRYPTGCM_WRWORD(uZh >> 0, pOut+4); + CRYPTGCM_WRWORD(uZl >> 32, pOut+8); + CRYPTGCM_WRWORD(uZl >> 0, pOut+12); +} + +/*F********************************************************************************/ +/*! + \Function _CryptGcmStart + + \Description + Start of encryption/decryption processing + + \Input *pGcm - cipher state + \Input iMode - CRYPTAES_KEYTYPE_ENCRYPT or CRYPTAES_KEYTYPE_DECRYPT + \Input *pInitVec - initialization vector (IV) + \Input iIvLen - length of IV + \Input *pAddData - additional authenticated data (AAD) + \Input iAddLen - length of AAD + + \Output + int32_t - zero=success, negative=failure + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CryptGcmStart(CryptGcmT *pGcm, int32_t iMode, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen) +{ + int32_t iBlockLen, iData; + const uint8_t *pAad; + + ds_memclr(pGcm->aY, sizeof(pGcm->aY)); + ds_memclr(pGcm->aBuf, sizeof(pGcm->aBuf)); + + pGcm->uMode = iMode; + pGcm->uLen = 0; + pGcm->uAddLen = 0; + + // validate IV length (we only support 12-byte IVs) + if (iIvLen == 12) + { + // generate y0 + ds_memcpy(pGcm->aY, pInitVec, iIvLen); + pGcm->aY[15] = 1; + } + else + { + NetPrintf(("cryptgcm: iv length of %d not supported\n", iIvLen)); + return(-1); + } + + // encrypt the block (GCM always uses forward encryption) and save output for later tag generation + CryptAesEncryptBlock(&pGcm->KeySchedule, pGcm->aY, pGcm->aBaseEctr); + + // GCM process AAD + for (pAad = pAddData, pGcm->uAddLen = iAddLen; iAddLen > 0; ) + { + iBlockLen = (iAddLen < 16) ? iAddLen : 16; + + for (iData = 0; iData < iBlockLen; iData += 1) + { + pGcm->aBuf[iData] ^= pAad[iData]; + } + + _CryptGcmMultiply(pGcm, pGcm->aBuf, pGcm->aBuf); + + iAddLen -= iBlockLen; + pAad += iBlockLen; + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptGcmUpdate + + \Description + Encrypt/decrypt the data + + \Input *pGcm - cipher state + \Input *pOutput - [out] output buffer for decrypted data (may overlap with input) + \Input *pInput - encrypted data to decrypt + \Input iLength - length of input/output + + \Output + int32_t - zero=success, negative=failure + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CryptGcmUpdate(CryptGcmT *pGcm, uint8_t *pOutput, const uint8_t *pInput, int32_t iLength) +{ + uint8_t *pOut = pOutput, aInput[16], aEctr[16]; + int32_t iData, iBlockLen; + const uint8_t *pInp; + + // add in length + pGcm->uLen += iLength; + + // handle input in blocks of 16 + for (pInp = pInput; iLength > 0; ) + { + // get block size + iBlockLen = (iLength < 16) ? iLength : 16; + // copy block to local buffer so we can handle overlapping I/O buffers + ds_memcpy(aInput, pInp, iBlockLen); + + for (iData = 16; iData > 12; iData -= 1) + { + if (++pGcm->aY[iData-1] != 0) + { + break; + } + } + + // encrypt the block (GCM always uses forward encryption) + CryptAesEncryptBlock(&pGcm->KeySchedule, pGcm->aY, aEctr); + + for (iData = 0; iData < iBlockLen; iData += 1) + { + if (pGcm->uMode == CRYPTAES_KEYTYPE_DECRYPT) + { + pGcm->aBuf[iData] ^= aInput[iData]; + } + pOut[iData] = aEctr[iData] ^ aInput[iData]; + if (pGcm->uMode == CRYPTAES_KEYTYPE_ENCRYPT) + { + pGcm->aBuf[iData] ^= pOut[iData]; + } + } + + _CryptGcmMultiply(pGcm, pGcm->aBuf, pGcm->aBuf); + + // move to next block + iLength -= iBlockLen; + pInp += iBlockLen; + pOut += iBlockLen; + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptGcmFinish + + \Description + Generate authentication tag + + \Input *pGcm - cipher state + \Input *pTag - [out] buffer to hold generated tag + \Input iTagLen - size of tag buffer + + \Output + int32_t - zero=success, negative=failure + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CryptGcmFinish(CryptGcmT *pGcm, uint8_t *pTag, int32_t iTagLen) +{ + uint8_t aTagBuf[16]; + uint64_t uLen = pGcm->uLen * 8; + uint64_t uAddLen = pGcm->uAddLen * 8; + int32_t iTag; + + if (iTagLen > (signed)sizeof(aTagBuf)) + { + return(-1); + } + if (iTagLen > 0) + { + ds_memcpy(pTag, pGcm->aBaseEctr, iTagLen); + } + + if (uLen || uAddLen) + { + ds_memclr(aTagBuf, sizeof(aTagBuf)); + + CRYPTGCM_WRWORD(uAddLen >> 32, aTagBuf+0); + CRYPTGCM_WRWORD(uAddLen >> 0, aTagBuf+4); + CRYPTGCM_WRWORD(uLen >> 32, aTagBuf+8); + CRYPTGCM_WRWORD(uLen >> 0, aTagBuf+12); + + for (iTag = 0; iTag < (signed)sizeof(aTagBuf); iTag += 1) + { + pGcm->aBuf[iTag] ^= aTagBuf[iTag]; + } + + _CryptGcmMultiply(pGcm, pGcm->aBuf, pGcm->aBuf); + + for (iTag = 0; iTag < iTagLen; iTag += 1) + { + pTag[iTag] ^= pGcm->aBuf[iTag]; + } + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptGcmEncrypt + + \Description + GCM encrypt/decrypt data + + \Input *pGcm - cipher state + \Input iMode - CRYPTAES_KEYTYPE_ENCRYPT or CRYPTAES_KEYTYPE_DECRYPT + \Input *pOutput - [out] output buffer for decrypted data (may overlap with input) + \Input *pInput - encrypted data to decrypt + \Input iLength - length of input/output + \Input *pInitVec - initialization vector (IV) + \Input iIvLen - length of IV + \Input *pAddData - additional authenticated data (AAD) + \Input iAddLen - length of AAD + \Input *pTag - tag to compare against for authentication + \Input iTagLen - length of tag + + \Output + int32_t - zero=success, negative=failure + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CryptGcmEncrypt(CryptGcmT *pGcm, int32_t iMode, uint8_t *pOutput, const uint8_t *pInput, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen) +{ + int32_t iResult; + + // set up encryption + if ((iResult = _CryptGcmStart(pGcm, iMode, pInitVec, iIvLen, pAddData, iAddLen)) != 0) + { + return(iResult); + } + // do the encryption + if ((iResult = _CryptGcmUpdate(pGcm, pOutput, pInput, iLength)) != 0) + { + return(iResult); + } + // generate the authentication tag + if ((iResult = _CryptGcmFinish(pGcm, pTag, iTagLen)) != 0) + { + return(iResult); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptGcmDecrypt + + \Description + GCM decrypt data, validate against authentication tag + + \Input *pGcm - cipher state + \Input *pOutput - [out] output buffer for decrypted data (may overlap with input) + \Input *pInput - encrypted data to decrypt + \Input iLength - length of input/output + \Input *pInitVec - initialization vector (IV) + \Input iIvLen - length of IV + \Input *pAddData - additional authenticated data (AAD) + \Input iAddLen - length of AAD + \Input *pTag - tag to compare against for authentication + \Input iTagLen - length of tag + + \Output + int32_t - zero=success, negative=failure + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CryptGcmDecrypt(CryptGcmT *pGcm, uint8_t *pOutput, const uint8_t *pInput, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, const uint8_t *pTag, int32_t iTagLen) +{ + int32_t iByte, iDiff, iResult; + uint8_t aDecryptTag[16]; + + // do the decryption and generate authentication tag + if ((iResult = _CryptGcmEncrypt(pGcm, CRYPTAES_KEYTYPE_DECRYPT, pOutput, pInput, iLength, pInitVec, iIvLen, pAddData, iAddLen, aDecryptTag, iTagLen)) != 0) + { + return(iResult); + } + + // constant-time comparison of authentication tag + for (iByte = 0, iDiff = 0; iByte < iTagLen; iByte += 1) + { + iDiff |= pTag[iByte] ^ aDecryptTag[iByte]; + } + + #if CRYPTGCM_VERBOSE + NetPrintMem(aDecryptTag, iTagLen, "CT"); + NetPrintMem(pTag, iTagLen, "T"); + #endif + + // if generated authentication tag does not match input tag, clear output and fail + if (iDiff != 0) + { + ds_memclr(pOutput, iLength); + NetPrintf(("cryptgcm: tag mismatch, decrypt failed\n")); + return(-1); + } + + // success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptGcmInit + + \Description + Initialize crypt state based on input key + + \Input *pGcm - cipher state + \Input *pKeyBuf - input key + \Input iKeyLen - length of key in bytes + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CryptGcmInit(CryptGcmT *pGcm, const uint8_t *pKeyBuf, int32_t iKeyLen) +{ + // init key schedule + CryptAesInitKeySchedule(&pGcm->KeySchedule, pKeyBuf, iKeyLen, CRYPTAES_KEYTYPE_ENCRYPT); + + // generate Shoop(?) table + _CryptGcmGenTable(pGcm); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptGcmInit + + \Description + Init the GCM cipher with the specified key + + \Input *pGcm - cipher state to initialize + \Input *pKeyBuf - pointer to key + \Input iKeyLen - key length + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptGcmInit(CryptGcmT *pGcm, const uint8_t *pKeyBuf, int32_t iKeyLen) +{ + // clear state + ds_memclr(pGcm, sizeof(*pGcm)); + // init state + _CryptGcmInit(pGcm, pKeyBuf, iKeyLen); +} + +/*F********************************************************************************/ +/*! + \Function CryptGcmEncrypt + + \Description + Encrypt data with AES-GCM + + \Input *pGcm - cipher state + \Input *pBuffer - [inp/out] data to encrypt + \Input iLength - length of data to encrypt + \Input *pInitVec - initialization vector (IV) + \Input iIvLen - IV len + \Input *pAddData - additional authenticated data (AAD) + \Input iAddLen - length of AAD + \Input *pTag - [out] tag buffer + \Input iTagLen - tag length + + \Output + int32_t - encrypted data size + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CryptGcmEncrypt(CryptGcmT *pGcm, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen) +{ + int32_t iResult; + + #if CRYPTGCM_VERBOSE + NetPrintf(("cryptgcm: encrypting size=%d ivlen=%d datalen=%d\n", iLength, iIvLen, iAddLen)); + NetPrintMem(pBuffer, iLength, "P"); + NetPrintMem(pInitVec, iIvLen, "V"); + NetPrintMem(pAddData, iAddLen, "A"); + #endif + + iResult = _CryptGcmEncrypt(pGcm, CRYPTAES_KEYTYPE_ENCRYPT, pBuffer, pBuffer, iLength, pInitVec, iIvLen, pAddData, iAddLen, pTag, iTagLen); + + #if CRYPTGCM_VERBOSE + NetPrintMem(pBuffer, iLength, "C"); + #endif + + return(iResult == 0 ? iLength : 0); +} + +/*F********************************************************************************/ +/*! + \Function CryptGcmDecrypt + + \Description + Decrypt data with AES-GCM + + \Input *pGcm - cipher state + \Input *pBuffer - [inp/out] data to decrypt + \Input iLength - length of data to decrypt + \Input *pInitVec - initialization vector (IV) + \Input iIvLen - IV len + \Input *pAddData - additional data (ADD) + \Input iAddLen - length of AAD + \Input *pTag - tag buffer + \Input iTagLen - tag length + + \Output + int32_t - decrypted data size + + \Version 07/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CryptGcmDecrypt(CryptGcmT *pGcm, uint8_t *pBuffer, int32_t iLength, const uint8_t *pInitVec, int32_t iIvLen, const uint8_t *pAddData, int32_t iAddLen, uint8_t *pTag, int32_t iTagLen) +{ + int32_t iResult; + + if (iLength < 0) + { + NetPrintf(("cryptgcm: skipping decrypt of input with size=%d\n", iLength)); + return(-1); + } + + #if CRYPTGCM_VERBOSE + NetPrintf(("cryptgcm: decrypting size=%d ivlen=%d datalen=%d\n", iLength, iIvLen, iAddLen)); + NetPrintMem(pBuffer, iLength, "C"); + NetPrintMem(pInitVec, iIvLen, "V"); + NetPrintMem(pAddData, iAddLen, "A"); + #endif + + iResult = _CryptGcmDecrypt(pGcm, pBuffer, pBuffer, iLength, pInitVec, iIvLen, pAddData, iAddLen, pTag, iTagLen); + + #if CRYPTGCM_VERBOSE + NetPrintMem(pBuffer, iLength, "P"); + #endif + + return(iResult == 0 ? iLength : -1); +} + + diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/crypthash.c b/r5dev/thirdparty/dirtysdk/source/crypt/crypthash.c new file mode 100644 index 00000000..98d19a3b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/crypthash.c @@ -0,0 +1,132 @@ +/*H*******************************************************************/ +/*! + \File crypthash.c + + \Description + This module implements a generic wrapper for all cryptograph + hash modules. + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 1.0 11/05/2013 (jbrookes) First Version +*/ +/*******************************************************************H*/ + +/*** Include files ***************************************************/ + +#include + +#include "DirtySDK/platform.h" + +#include "DirtySDK/crypt/cryptmd5.h" +#include "DirtySDK/crypt/cryptsha1.h" +#include "DirtySDK/crypt/cryptsha2.h" +#include "DirtySDK/util/murmurhash3.h" + +#include "DirtySDK/crypt/crypthash.h" + +/*** Defines *********************************************************/ + +/*** Type Definitions ************************************************/ + +/*** Variables *******************************************************/ + +static const CryptHashT _CryptHash_Hashes[] = +{ + { (CryptHashInitT *)MurmurHash3Init2, (CryptHashUpdateT *)MurmurHash3Update, (CryptHashFinalT *)MurmurHash3Final, CRYPTHASH_MURMUR3, MURMURHASH_HASHSIZE }, + { (CryptHashInitT *)CryptMD5Init2, (CryptHashUpdateT *)CryptMD5Update, (CryptHashFinalT *)CryptMD5Final, CRYPTHASH_MD5, MD5_BINARY_OUT }, + { (CryptHashInitT *)CryptSha1Init2, (CryptHashUpdateT *)CryptSha1Update, (CryptHashFinalT *)CryptSha1Final, CRYPTHASH_SHA1, CRYPTSHA1_HASHSIZE }, + { (CryptHashInitT *)CryptSha2Init, (CryptHashUpdateT *)CryptSha2Update, (CryptHashFinalT *)CryptSha2Final, CRYPTHASH_SHA224, CRYPTSHA224_HASHSIZE }, + { (CryptHashInitT *)CryptSha2Init, (CryptHashUpdateT *)CryptSha2Update, (CryptHashFinalT *)CryptSha2Final, CRYPTHASH_SHA256, CRYPTSHA256_HASHSIZE }, + { (CryptHashInitT *)CryptSha2Init, (CryptHashUpdateT *)CryptSha2Update, (CryptHashFinalT *)CryptSha2Final, CRYPTHASH_SHA384, CRYPTSHA384_HASHSIZE }, + { (CryptHashInitT *)CryptSha2Init, (CryptHashUpdateT *)CryptSha2Update, (CryptHashFinalT *)CryptSha2Final, CRYPTHASH_SHA512, CRYPTSHA512_HASHSIZE } +}; + +/*** Private functions ***********************************************/ + +/*** Public functions ************************************************/ + + +/*F*******************************************************************/ +/*! + \Function CryptHashGet + + \Description + Get CryptHash function block based on input hash size + + \Input eHashType - hash type to get size of + + \Output + CryptHashT * - hash block pointer, or NULL if not found + + \Version 11/05/2013 (jbrookes) +*/ +/*******************************************************************F*/ +const CryptHashT *CryptHashGet(CryptHashTypeE eHashType) +{ + const CryptHashT *pHash; + pHash = ((eHashType > CRYPTHASH_NULL) && (eHashType < CRYPTHASH_NUMHASHES)) ? &_CryptHash_Hashes[((int32_t)eHashType)-1] : (const CryptHashT *)NULL; + return(pHash); +} +/*F*******************************************************************/ +/*! + \Function CryptHashGetSize + + \Description + Get hash size for specified hash type + + \Input eHashType - hash type to get size of + + \Output + int32_t - size of specified hash + + \Version 03/07/2014 (jbrookes) +*/ +/*******************************************************************F*/ +int32_t CryptHashGetSize(CryptHashTypeE eHashType) +{ + const CryptHashT *pHash = CryptHashGet(eHashType); + int32_t iHashSize = (pHash != NULL) ? pHash->iHashSize : -1; + return(iHashSize); +} + +/*F*******************************************************************/ +/*! + \Function CryptHashGetBySize + + \Description + Get the hash based on its size + + \Input iHashSize - size of hash we are trying to lookup + + \Output + CryptHashTypeE - hash type, CRYPTHASH_NULL if not found + + \Notes + Due to murmurhash3 using the same size as MD5, we choose to + not include it. + + \Version 01/09/2018 (jbrookes) +*/ +/*******************************************************************F*/ +CryptHashTypeE CryptHashGetBySize(int32_t iHashSize) +{ + switch (iHashSize) + { + case MD5_BINARY_OUT: + return(CRYPTHASH_MD5); + case CRYPTSHA1_HASHSIZE: + return(CRYPTHASH_SHA1); + case CRYPTSHA224_HASHSIZE: + return(CRYPTHASH_SHA224); + case CRYPTSHA256_HASHSIZE: + return(CRYPTHASH_SHA256); + case CRYPTSHA384_HASHSIZE: + return(CRYPTHASH_SHA384); + case CRYPTSHA512_HASHSIZE: + return(CRYPTHASH_SHA512); + default: + return(CRYPTHASH_NULL); + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/crypthmac.c b/r5dev/thirdparty/dirtysdk/source/crypt/crypthmac.c new file mode 100644 index 00000000..ed3b376b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/crypthmac.c @@ -0,0 +1,150 @@ +/*H********************************************************************************/ +/*! + \File crypthmac.c + + \Description + Implements HMAC as defined in https://tools.ietf.org/html/rfc2104 + + \Copyright + Copyright (c) 2013 Electronic Arts Inc. + + \Version 01/14/2013 (jbrookes) First Version; refactored from ProtoSSL + \Version 07/26/2018 (jbrookes) Fixed to properly handle keys larger than block size +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/crypt/crypthash.h" + +#include "DirtySDK/crypt/crypthmac.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptHmacCalc + + \Description + Implements HMAC as defined in https://tools.ietf.org/html/rfc2104 + + \Input *pBuffer - [out] storage for generated digest + \Input iBufLen - size of output buffer + \Input *pMessage - input data to hash + \Input iMessageLen - size of input data + \Input *pKey - seed + \Input iKeyLen - seed length + \Input eHashType - hash type + + \Output + int32_t - negative=error, else success + + \Version 01/14/2013 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CryptHmacCalc(uint8_t *pBuffer, int32_t iBufLen, const uint8_t *pMessage, int32_t iMessageLen, const uint8_t *pKey, int32_t iKeyLen, CryptHashTypeE eHashType) +{ + CryptHmacMsgT Message; + Message.pMessage = pMessage; + Message.iMessageLen = iMessageLen; + return(CryptHmacCalcMulti(pBuffer, iBufLen, &Message, 1, pKey, iKeyLen, eHashType)); +} + +/*F********************************************************************************/ +/*! + \Function CryptHmacCalcMulti + + \Description + Implements HMAC as defined in https://tools.ietf.org/html/rfc2104. + This version allows multiple buffers to be specified in one call, which is + useful when the input to be hashed is not in a single contiguous buffer. + + \Input *pBuffer - [out] storage for generated MAC + \Input iBufLen - size of output buffer (may be smaller than hash size for truncated HMAC) + \Input *pMessageList- list of messages to hash + \Input iNumMessages - number of messages to hash + \Input *pKey - key + \Input iKeyLen - key length + \Input eHashType - hash type + + \Output + int32_t - negative=error, else success + + \Version 01/14/2013 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CryptHmacCalcMulti(uint8_t *pBuffer, int32_t iBufLen, const CryptHmacMsgT *pMessageList, int32_t iNumMessages, const uint8_t *pKey, int32_t iKeyLen, CryptHashTypeE eHashType) +{ + uint8_t aKiPad[128], aKoPad[128], aKeyHash[128]; // max message block size of SHA-384/512 + int32_t iBufCnt, iMessage, iBlockSize; + const CryptHashT *pHash; + uint8_t aWorkBuf[CRYPTHASH_MAXDIGEST]; + uint8_t aContext[CRYPTHASH_MAXSTATE]; + + // get hash function block for this hash type + if ((pHash = CryptHashGet(eHashType)) == NULL) + { + NetPrintf(("crypthmac: invalid hash type %d\n", eHashType)); + return(-1); + } + // get block size + iBlockSize = (pHash->iHashSize < CRYPTSHA384_HASHSIZE) ? 64 : 128; + // as per https://tools.ietf.org/html/rfc2104#section-3, keys longer than the block size are hashed to the digest size + if (iKeyLen > iBlockSize) + { + pHash->Init(aContext, pHash->iHashSize); + pHash->Update(aContext, pKey, iKeyLen); + pHash->Final(aContext, aKeyHash, pHash->iHashSize); + pKey = aKeyHash; + iKeyLen = pHash->iHashSize; + } + // limit output to hash size + if (iBufLen > pHash->iHashSize) + { + iBufLen = pHash->iHashSize; + } + + // copy key to ipad and opad, pad with zero + ds_memcpy(aKiPad, pKey, iKeyLen); + ds_memclr(aKiPad+iKeyLen, iBlockSize-iKeyLen); + ds_memcpy(aKoPad, pKey, iKeyLen); + ds_memclr(aKoPad+iKeyLen, iBlockSize-iKeyLen); + + // calculate xor of ipad and opad with 0x36/0x5c respectively + for (iBufCnt = 0; iBufCnt < iBlockSize; iBufCnt += 1) + { + aKiPad[iBufCnt] ^= 0x36; + aKoPad[iBufCnt] ^= 0x5c; + } + + // execute hash of ipad + pHash->Init(aContext, pHash->iHashSize); + pHash->Update(aContext, aKiPad, iBlockSize); + for (iMessage = 0; iMessage < iNumMessages; iMessage += 1) + { + pHash->Update(aContext, pMessageList[iMessage].pMessage, pMessageList[iMessage].iMessageLen); + } + pHash->Final(aContext, aWorkBuf, pHash->iHashSize); + + // execute hash of ipad+opad + pHash->Init(aContext, pHash->iHashSize); + pHash->Update(aContext, aKoPad, iBlockSize); + pHash->Update(aContext, aWorkBuf, pHash->iHashSize); + pHash->Final(aContext, pBuffer, iBufLen); + + // success + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptmd5.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptmd5.c new file mode 100644 index 00000000..467b124b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptmd5.c @@ -0,0 +1,513 @@ +/*H*************************************************************************************************/ +/*! + + \File cryptmd5.c + + \Description + The MD5 message digest algorithm developed by Ron Rivest and documented + in RFC1321. This implementation is based on the RFC but does not use the + sample code.. It should be free from intellectual property concerns and + a reference is included below which further clarifies this point. + + Note that this implementation is limited to hashing no more than 2^32 + bytes after which its results would be impatible with a fully compliant + implementation. + + \Notes + http://www.ietf.org/ietf/IPR/RSA-MD-all + + The following was recevied Fenbruary 23,2000 + From: "Linn, John" + + February 19, 2000 + + The purpose of this memo is to clarify the status of intellectual + property rights asserted by RSA Security Inc. ("RSA") in the MD2, MD4 and + MD5 message-digest algorithms, which are documented in RFC-1319, RFC-1320, + and RFC-1321 respectively. + + Implementations of these message-digest algorithms, including + implementations derived from the reference C code in RFC-1319, RFC-1320, and + RFC-1321, may be made, used, and sold without license from RSA for any + purpose. + + No rights other than the ones explicitly set forth above are + granted. Further, although RSA grants rights to implement certain + algorithms as defined by identified RFCs, including implementations derived + from the reference C code in those RFCs, no right to use, copy, sell, or + distribute any other implementations of the MD2, MD4, or MD5 message-digest + algorithms created, implemented, or distributed by RSA is hereby granted by + implication, estoppel, or otherwise. Parties interested in licensing + security components and toolkits written by RSA should contact the company + to discuss receiving a license. All other questions should be directed to + Margaret K. Seif, General Counsel, RSA Security Inc., 36 Crosby Drive, + Bedford, Massachusetts 01730. + + Implementations of the MD2, MD4, or MD5 algorithms may be subject to + United States laws and regulations controlling the export of technical data, + computer software, laboratory prototypes and other commodities (including + the Arms Export Control Act, as amended, and the Export Administration Act + of 1970). The transfer of certain technical data and commodities may + require a license from the cognizant agency of the United States Government. + RSA neither represents that a license shall not be required for a particular + implementation nor that, if required, one shall be issued. + + + DISCLAIMER: RSA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES + OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, VALIDITY OF + INTELLECTUAL PROPERTY RIGHTS, ISSUED OR PENDING, OR THE ABSENCE OF LATENT OR + OTHER DEFECTS, WHETHER OR NOT DISCOVERABLE, IN CONNECTION WITH THE MD2, MD4, + OR MD5 ALGORITHMS. NOTHING IN THIS GRANT OF RIGHTS SHALL BE CONSTRUED AS A + REPRESENTATION OR WARRANTY GIVEN BY RSA THAT THE IMPLEMENTATION OF THE + ALGORITHM WILL NOT INFRINGE THE INTELLECTUAL PROPERTY RIGHTS OF ANY THIRD + PARTY. IN NO EVENT SHALL RSA, ITS TRUSTEES, DIRECTORS, OFFICERS, EMPLOYEES, + PARENTS AND AFFILIATES BE LIABLE FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES OF + ANY KIND RESULTING FROM IMPLEMENTATION OF THIS ALGORITHM, INCLUDING ECONOMIC + DAMAGE OR INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER RSA + SHALL BE ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 03/16/2001 (GWS) First Version + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptmd5.h" + +/*** Defines ***************************************************************************/ + +// the core transformations (ff/gg slightly optimized) +#define FF(x, y, z) (z ^ ((y ^ z) & x)) +#define GG(x, y, z) (y ^ ((x ^ y) & z)) +#define HH(x, y, z) (x ^ y ^ z) +#define II(x, y, z) (y ^ (x | ~z)) + +// accumulate the result of the transformation +#define ACC(r, t1, t2, t3, s, x) \ + r += (t1); r += (t2); r+= (t3); r = (r<>(32-s)); r += x; + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +static const char *_MD5_HexChars = "0123456789abcdef"; + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function _CryptMD5Transform + + \Description + Incorpate 512 bit data block into current context + + \Input *pContext - target MD5 context + \Input *pBlock - pointer to data block + + \Version 03/16/02 (GWS) +*/ +/*************************************************************************************************F*/ +static void _CryptMD5Transform(CryptMD5T *pContext, const unsigned char *pBlock) +{ + register uint32_t a; + register uint32_t b; + register uint32_t c; + register uint32_t d; + uint32_t uData[16]; + // not really volatile, but keeps gcc optimizer from + // going crazy and creating really slow code + volatile uint32_t *pVector = (uint32_t *)pBlock; + + // this is not actually required on a little-endian + // machine like intel, but gcc screws up the code so + // badly without it that its faster with this + // unneeded reordering code than without it. + if (1) + { + // convert the incoming data stream to little endian + pBlock += 64; + pVector = uData+16; + while (pVector != uData) + { + // go from end to start since data is in little endian + // (this is the most efficient method to grab the data) + b = *--pBlock; + b = (b << 8) + *--pBlock; + b = (b << 8) + *--pBlock; + b = (b << 8) + *--pBlock; + *--pVector = b; + } + } + + // load the register values + a = pContext->uRegs[0]; + b = pContext->uRegs[1]; + c = pContext->uRegs[2]; + d = pContext->uRegs[3]; + + // this magic comes from the rfc + ACC(a, FF(b, c, d), pVector[ 0],0xd76aa478, 7, b); + ACC(d, FF(a, b, c), pVector[ 1],0xe8c7b756, 12, a); + ACC(c, FF(d, a, b), pVector[ 2],0x242070db, 17, d); + ACC(b, FF(c, d, a), pVector[ 3],0xc1bdceee, 22, c); + ACC(a, FF(b, c, d), pVector[ 4],0xf57c0faf, 7, b); + ACC(d, FF(a, b, c), pVector[ 5],0x4787c62a, 12, a); + ACC(c, FF(d, a, b), pVector[ 6],0xa8304613, 17, d); + ACC(b, FF(c, d, a), pVector[ 7],0xfd469501, 22, c); + ACC(a, FF(b, c, d), pVector[ 8],0x698098d8, 7, b); + ACC(d, FF(a, b, c), pVector[ 9],0x8b44f7af, 12, a); + ACC(c, FF(d, a, b), pVector[10],0xffff5bb1, 17, d); + ACC(b, FF(c, d, a), pVector[11],0x895cd7be, 22, c); + ACC(a, FF(b, c, d), pVector[12],0x6b901122, 7, b); + ACC(d, FF(a, b, c), pVector[13],0xfd987193, 12, a); + ACC(c, FF(d, a, b), pVector[14],0xa679438e, 17, d); + ACC(b, FF(c, d, a), pVector[15],0x49b40821, 22, c); + + ACC(a, GG(b, c, d), pVector[ 1],0xf61e2562, 5, b); + ACC(d, GG(a, b, c), pVector[ 6],0xc040b340, 9, a); + ACC(c, GG(d, a, b), pVector[11],0x265e5a51, 14, d); + ACC(b, GG(c, d, a), pVector[ 0],0xe9b6c7aa, 20, c); + ACC(a, GG(b, c, d), pVector[ 5],0xd62f105d, 5, b); + ACC(d, GG(a, b, c), pVector[10],0x02441453, 9, a); + ACC(c, GG(d, a, b), pVector[15],0xd8a1e681, 14, d); + ACC(b, GG(c, d, a), pVector[ 4],0xe7d3fbc8, 20, c); + ACC(a, GG(b, c, d), pVector[ 9],0x21e1cde6, 5, b); + ACC(d, GG(a, b, c), pVector[14],0xc33707d6, 9, a); + ACC(c, GG(d, a, b), pVector[ 3],0xf4d50d87, 14, d); + ACC(b, GG(c, d, a), pVector[ 8],0x455a14ed, 20, c); + ACC(a, GG(b, c, d), pVector[13],0xa9e3e905, 5, b); + ACC(d, GG(a, b, c), pVector[ 2],0xfcefa3f8, 9, a); + ACC(c, GG(d, a, b), pVector[ 7],0x676f02d9, 14, d); + ACC(b, GG(c, d, a), pVector[12],0x8d2a4c8a, 20, c); + + ACC(a, HH(b, c, d), pVector[ 5],0xfffa3942, 4, b); + ACC(d, HH(a, b, c), pVector[ 8],0x8771f681, 11, a); + ACC(c, HH(d, a, b), pVector[11],0x6d9d6122, 16, d); + ACC(b, HH(c, d, a), pVector[14],0xfde5380c, 23, c); + ACC(a, HH(b, c, d), pVector[ 1],0xa4beea44, 4, b); + ACC(d, HH(a, b, c), pVector[ 4],0x4bdecfa9, 11, a); + ACC(c, HH(d, a, b), pVector[ 7],0xf6bb4b60, 16, d); + ACC(b, HH(c, d, a), pVector[10],0xbebfbc70, 23, c); + ACC(a, HH(b, c, d), pVector[13],0x289b7ec6, 4, b); + ACC(d, HH(a, b, c), pVector[ 0],0xeaa127fa, 11, a); + ACC(c, HH(d, a, b), pVector[ 3],0xd4ef3085, 16, d); + ACC(b, HH(c, d, a), pVector[ 6],0x04881d05, 23, c); + ACC(a, HH(b, c, d), pVector[ 9],0xd9d4d039, 4, b); + ACC(d, HH(a, b, c), pVector[12],0xe6db99e5, 11, a); + ACC(c, HH(d, a, b), pVector[15],0x1fa27cf8, 16, d); + ACC(b, HH(c, d, a), pVector[ 2],0xc4ac5665, 23, c); + + ACC(a, II(b, c, d), pVector[ 0],0xf4292244, 6, b); + ACC(d, II(a, b, c), pVector[ 7],0x432aff97, 10, a); + ACC(c, II(d, a, b), pVector[14],0xab9423a7, 15, d); + ACC(b, II(c, d, a), pVector[ 5],0xfc93a039, 21, c); + ACC(a, II(b, c, d), pVector[12],0x655b59c3, 6, b); + ACC(d, II(a, b, c), pVector[ 3],0x8f0ccc92, 10, a); + ACC(c, II(d, a, b), pVector[10],0xffeff47d, 15, d); + ACC(b, II(c, d, a), pVector[ 1],0x85845dd1, 21, c); + ACC(a, II(b, c, d), pVector[ 8],0x6fa87e4f, 6, b); + ACC(d, II(a, b, c), pVector[15],0xfe2ce6e0, 10, a); + ACC(c, II(d, a, b), pVector[ 6],0xa3014314, 15, d); + ACC(b, II(c, d, a), pVector[13],0x4e0811a1, 21, c); + ACC(a, II(b, c, d), pVector[ 4],0xf7537e82, 6, b); + ACC(d, II(a, b, c), pVector[11],0xbd3af235, 10, a); + ACC(c, II(d, a, b), pVector[ 2],0x2ad7d2bb, 15, d); + ACC(b, II(c, d, a), pVector[ 9],0xeb86d391, 21, c); + + // update the registers + pContext->uRegs[0] += a; + pContext->uRegs[1] += b; + pContext->uRegs[2] += c; + pContext->uRegs[3] += d; +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function CryptMD5Init + + \Description + Init the MD5 context. + + \Input *pContext - target MD5 context + + \Version 03/16/02 (GWS) +*/ +/*************************************************************************************************F*/ +void CryptMD5Init(CryptMD5T *pContext) +{ + // reset the byte count + pContext->uCount = 0; + + // init as per RFC1321 + pContext->uRegs[0] = 0x67452301; // word A + pContext->uRegs[1] = 0xefcdab89; // word B + pContext->uRegs[2] = 0x98badcfe; // word C + pContext->uRegs[3] = 0x10325476; // word D +} + +/*F*************************************************************************************************/ +/*! + \Function CryptMD5Init2 + + \Description + Init the MD5 context (alternate form) + + \Input *pContext - target MD5 context + \Input iHashSize - hash size (unused) + + \Version 11/05/13 (jbrookes) +*/ +/*************************************************************************************************F*/ +void CryptMD5Init2(CryptMD5T *pContext, int32_t iHashSize) +{ + CryptMD5Init(pContext); +} + +/*F*************************************************************************************************/ +/*! + \Function CryptMD5Update + + \Description + Add data to the MD5 context (hash the data). + + \Input *pContext - target MD5 context + \Input *_pBuffer - input data to hash + \Input iLength - length of buffer (-1=treat pBuffer as asciiz) + + \Version 03/16/02 (GWS) +*/ +/*************************************************************************************************F*/ +void CryptMD5Update(CryptMD5T *pContext, const void *_pBuffer, int32_t iLength) +{ + int32_t uAdd; + uint32_t uCount; + const unsigned char *pBuffer = _pBuffer; + + // allow easy string access + if (iLength < 0) + { + for (iLength = 0; pBuffer[iLength] != 0; ++iLength) + ; + } + + // get index into block buffer + uCount = pContext->uCount&63; + pContext->uCount += iLength; + + // see if we need to append to existing data + if (uCount > 0) + { + // figure out number to fill block + uAdd = 64-uCount; + + // if less than a full block + if (iLength < uAdd) + { + ds_memcpy(pContext->strData+uCount, pBuffer, iLength); + return; + } + + // finish off the block and transform + ds_memcpy(pContext->strData+uCount, pBuffer, uAdd); + pBuffer += uAdd; + iLength -= uAdd; + _CryptMD5Transform(pContext, pContext->strData); + } + + // do 64 byte blocks of data + while (iLength >= 64) + { + _CryptMD5Transform(pContext, pBuffer); + pBuffer += 64; + iLength -= 64; + } + + // store leftover data + if (iLength > 0) + { + ds_memcpy(pContext->strData, pBuffer, iLength); + } +} + +/*F*************************************************************************************************/ +/*! + \Function CryptMD5Final + + \Description + Convert MD5 state into final output form + + \Input *pContext - the MD5 state (from create) + \Input *_pBuffer - the digest output + \Input iLength - length of output buffer + + \Version 03/16/02 (GWS) +*/ +/*************************************************************************************************F*/ +void CryptMD5Final(CryptMD5T *pContext, void *_pBuffer, int32_t iLength) +{ + int32_t uIndex; + uint32_t uZero; + uint32_t *pZero; + uint32_t uData = 0; + unsigned char *pBuffer = _pBuffer; + + // add ending marker + uIndex = pContext->uCount & 63; + pContext->strData[uIndex++] = 0x80; + + // transform block if no room for length data + if (uIndex > 56) + { + // zero rest of the buffer + // (we have 8 extra bytes so this can run over) + pContext->strData[uIndex+0] = 0; + pContext->strData[uIndex+1] = 0; + pContext->strData[uIndex+2] = 0; + pContext->strData[uIndex+3] = 0; + pContext->strData[uIndex+4] = 0; + pContext->strData[uIndex+5] = 0; + pContext->strData[uIndex+6] = 0; + pContext->strData[uIndex+7] = 0; + // transform the block + _CryptMD5Transform(pContext, pContext->strData); + uIndex = 0; + } + + // force zero to next int32_t + pContext->strData[uIndex+0] = 0; + pContext->strData[uIndex+1] = 0; + pContext->strData[uIndex+2] = 0; + // zero to end of block + uZero = (uIndex+3)>>2; + pZero = ((uint32_t *)pContext->strData)+uZero; + do + { + *pZero++ = 0; + } + while (++uZero < 64/4); + + // setup length mask + pContext->strData[56] = (unsigned char)(pContext->uCount<<3); + pContext->strData[57] = (unsigned char)(pContext->uCount>>5); + pContext->strData[58] = (unsigned char)(pContext->uCount>>13); + pContext->strData[59] = (unsigned char)(pContext->uCount>>21); + pContext->strData[60] = (unsigned char)(pContext->uCount>>29); + // final transformation + _CryptMD5Transform(pContext, pContext->strData); + +#ifdef __linux__ + // fast output of binary data in linux (more memory) + if (iLength == MD5_BINARY_OUT/2) + { + uData = pContext->uRegs[0]; + pBuffer[0] = uData; + uData >>= 8; + pBuffer[1] = uData; + uData >>= 8; + pBuffer[2] = uData; + uData >>= 8; + pBuffer[3] = uData; + + uData = pContext->uRegs[1]; + pBuffer[4] = uData; + uData >>= 8; + pBuffer[5] = uData; + uData >>= 8; + pBuffer[6] = uData; + uData >>= 8; + pBuffer[7] = uData; + return; + } + + if (iLength == MD5_BINARY_OUT) + { + uData = pContext->uRegs[0]; + pBuffer[0] = uData; + uData >>= 8; + pBuffer[1] = uData; + uData >>= 8; + pBuffer[2] = uData; + uData >>= 8; + pBuffer[3] = uData; + + uData = pContext->uRegs[1]; + pBuffer[4] = uData; + uData >>= 8; + pBuffer[5] = uData; + uData >>= 8; + pBuffer[6] = uData; + uData >>= 8; + pBuffer[7] = uData; + + uData = pContext->uRegs[2]; + pBuffer[8] = uData; + uData >>= 8; + pBuffer[9] = uData; + uData >>= 8; + pBuffer[10] = uData; + uData >>= 8; + pBuffer[11] = uData; + + uData = pContext->uRegs[3]; + pBuffer[12] = uData; + uData >>= 8; + pBuffer[13] = uData; + uData >>= 8; + pBuffer[14] = uData; + uData >>= 8; + pBuffer[15] = uData; + return; + } +#endif + + // extract data from buffer and save + for (uIndex = 0; uIndex < 16; ++uIndex) + { + // see if we need to fetch a byte + if ((uIndex & 3) == 0) + { + uData = pContext->uRegs[uIndex>>2]; + } + // store the byte + if (iLength >= MD5_STRING_OUT) + { + *pBuffer++ = _MD5_HexChars[(uData>>4)&15]; + *pBuffer++ = _MD5_HexChars[(uData>>0)&15]; + } + else if (uIndex < iLength) + { + *pBuffer++ = (unsigned char)uData; + } + // shift down the data + uData >>= 8; + } + + // add final terminator if needed + if (iLength >= MD5_STRING_OUT) + { + *pBuffer = 0; + } +} + diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptmont.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptmont.c new file mode 100644 index 00000000..939ce246 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptmont.c @@ -0,0 +1,648 @@ +/*H********************************************************************************/ +/*! + \File cryptmont.h + + \Description + This module implements the math for elliptic curve cryptography + using montgomery curves + + \Copyright + Copyright (c) Electronic Arts 2018. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/crypt/cryptrand.h" + +#include "DirtySDK/crypt/cryptmont.h" + +/*** Defines **********************************************************************/ + +//! size of the window used to determined the table size +#define CRYPTMONT_WINDOW_SIZE (5) + +//! calculation of the table size based on the window +#define CRYPTMONT_TABLE_SIZE (1 << (CRYPTMONT_WINDOW_SIZE - 1)) + +//! memgroup for allocating the table +#define CRYPTMONT_MEMID ('mont') + +//! number of iterations to do per call +#define CRYPTMONT_NUM_ITERATIONS (0x10) + +/*** Variables ********************************************************************/ + +//! prime for x25519 +static const uint8_t _aPrime25519[] = +{ + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed +}; + +//! prime for x25519 +static const uint8_t _aPrime448[] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CryptMontSwap + + \Description + Constant time conditional swap of two big numbers + + \Input uSwap - determintes if we should swap + \Input *pLhs - [out] first big number in the swap + \Input *pRhs - [out] second big number in the swap + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptMontSwap(uint8_t uSwap, CryptBnT *pLhs, CryptBnT *pRhs) +{ + CryptBnT Temp, Mask; + + // mask = (1 << bits) - swap + CryptBnInitSet(&Mask, 1); + CryptBnInitSet(&Temp, uSwap); + CryptBnLeftShift2(&Mask, DS_MAX(CryptBnBitLen(pLhs), CryptBnBitLen(pRhs))); + CryptBnSubtract(&Mask, &Mask, &Temp); + + // temp = mask & (lhs ^ rhs) + CryptBnBitXor(&Temp, pLhs, pRhs); + CryptBnBitAnd(&Temp, &Temp, &Mask); + + // lhs ^= temp + CryptBnBitXor(pLhs, pLhs, &Temp); + + // rhs ^= temp + CryptBnBitXor(pRhs, pRhs, &Temp); +} + +/*F********************************************************************************/ +/*! + \Function _CryptMontPointCalculate + + \Description + Calculates the point multiplication on the curve based on the private key + before doing the final result calculation + + \Input *pState - curve state + \Input *pU - the point we multiply with the private key + + \Output + uint8_t - TRUE=complete, FALSE=pending + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _CryptMontPointCalculate(CryptMontT *pState, CryptBnT *pU) +{ + int32_t iIter; + + // initialize the operation + if (pState->iBitIndex < 0) + { + pState->iBitIndex = CryptBnBitLen(&pState->Prime) - 1; + pState->uCryptUsecs = 0; + + CryptBnInitSet(&pState->X_2, 1); + CryptBnInitSet(&pState->Result.X, 0); + CryptBnClone(&pState->X_3, pU); + CryptBnInitSet(&pState->Result.Y, 1); + } + + for (iIter = 0; (pState->iBitIndex >= 0) && (iIter < CRYPTMONT_NUM_ITERATIONS); pState->iBitIndex -= 1, iIter += 1) + { + CryptBnT A, AA, B, BB, C, D, DA, CB, E; + uint8_t bBitSet = CryptBnBitTest(&pState->PrivateKey, pState->iBitIndex); + const uint64_t uTickUsecs = NetTickUsec(); + + CryptBnInitSet(&A, 0); + CryptBnInitSet(&B, 0); + CryptBnInitSet(&C, 0); + CryptBnInitSet(&D, 0); + CryptBnInitSet(&E, 0); + + // swap ^= bitset; + pState->uSwap ^= bBitSet; + + // constant time conditional swap + _CryptMontSwap(pState->uSwap, &pState->X_2, &pState->X_3); + _CryptMontSwap(pState->uSwap, &pState->Result.X, &pState->Result.Y); + + // swap = bitset + pState->uSwap = bBitSet; + + // A = (x_2 + z_2) % p + CryptBnModAdd(&A, &pState->X_2, &pState->Result.X, &pState->Prime); + + // AA = (A * A) % p + CryptBnModMultiply(&AA, &A, &A, &pState->Prime); + + // B = (x_2 - z_2) + CryptBnSubtract(&B, &pState->X_2, &pState->Result.X); + + // BB = (B * B) % p + CryptBnModMultiply(&BB, &B, &B, &pState->Prime); + + // E = (AA - BB) + CryptBnSubtract(&E, &AA, &BB); + + // x_2 = (AA * BB) % p + CryptBnModMultiply(&pState->X_2, &AA, &BB, &pState->Prime); + + // z_2 = (E * (AA + (a24 * E))) % p + CryptBnModMultiply(&pState->Result.X, &pState->A24, &E, &pState->Prime); + CryptBnModAdd(&pState->Result.X, &AA, &pState->Result.X, &pState->Prime); + CryptBnModMultiply(&pState->Result.X, &E, &pState->Result.X, &pState->Prime); + + // C = (x_3 + z_3) % p + CryptBnModAdd(&C, &pState->X_3, &pState->Result.Y, &pState->Prime); + + // D = x_3 - z_3 + CryptBnSubtract(&D, &pState->X_3, &pState->Result.Y); + + // DA = (D * A) % p + CryptBnModMultiply(&DA, &D, &A, &pState->Prime); + + // CB = (C * B) % p + CryptBnModMultiply(&CB, &C, &B, &pState->Prime); + + // x_3 = (DA+CB)^2 % p + CryptBnModAdd(&pState->X_3, &DA, &CB, &pState->Prime); + CryptBnModMultiply(&pState->X_3, &pState->X_3, &pState->X_3, &pState->Prime); + + // z_3 = (u * ((DA-CB)^2 % p)) % p + CryptBnSubtract(&pState->Result.Y, &DA, &CB); + CryptBnModMultiply(&pState->Result.Y, &pState->Result.Y, &pState->Result.Y, &pState->Prime); + CryptBnModMultiply(&pState->Result.Y, pU, &pState->Result.Y, &pState->Prime); + + // update timing + pState->uCryptUsecs += (uint32_t)NetTickDiff(NetTickUsec(), uTickUsecs); + } + + // check for completion of first stage, perform required operations and move to next + if (pState->iBitIndex < 0) + { + // constant time conditional swap + _CryptMontSwap(pState->uSwap, &pState->X_2, &pState->X_3); + _CryptMontSwap(pState->uSwap, &pState->Result.X, &pState->Result.Y); + } + + return((pState->iBitIndex < 0) ? TRUE : FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _CryptMontResultCalculate + + \Description + Determines the final using the final result using the formula: + result = x_2 * (z_2 ^ (p - 2) % p) % p + + \Input *pState - curve state + + \Output + uint8_t - TRUE=complete, FALSE=pending + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _CryptMontResultCalculate(CryptMontT *pState) +{ + int32_t iIter; + + // initialize the operation + if (pState->iBitIndex < 0) + { + int32_t iTableIndex; + CryptBnT Square; + + // query memgroup + DirtyMemGroupQuery(&pState->iMemGroup, &pState->pMemGroupUserdata); + + if ((pState->pTable = (CryptBnT *)DirtyMemAlloc(sizeof(*pState->pTable) * CRYPTMONT_TABLE_SIZE, CRYPTMONT_MEMID, pState->iMemGroup, pState->pMemGroupUserdata)) == NULL) + { + NetPrintf(("cryptmont: failed to allocate window table\n")); + return(TRUE); + } + ds_memclr(pState->pTable, sizeof(*pState->pTable) * CRYPTMONT_TABLE_SIZE); + + /* put the already reduced input into the first index of the table, this will make it so further + entries in the table will not be too large for our modulus operations */ + CryptBnClone(&pState->pTable[0], &pState->Result.X); + + // calculate (input^2) % mod as the basis for the other calculations + CryptBnModMultiply(&Square, &pState->pTable[0], &pState->pTable[0], &pState->Prime); + + // calculate (input^(2+iTableIndex)) % mod for the rest of the table + for (iTableIndex = 1; iTableIndex < CRYPTMONT_TABLE_SIZE; iTableIndex += 1) + { + CryptBnModMultiply(&pState->pTable[iTableIndex], &pState->pTable[iTableIndex - 1], &Square, &pState->Prime); + } + + pState->bAccumulOne = TRUE; + + // save prime - 2 + CryptBnInitSet(&pState->PrimeMin2, 2); + CryptBnSubtract(&pState->PrimeMin2, &pState->Prime, &pState->PrimeMin2); + + pState->iBitIndex = CryptBnBitLen(&pState->PrimeMin2) - 1; + } + + for (iIter = 0; (pState->iBitIndex >= 0) && (iIter < CRYPTMONT_NUM_ITERATIONS); iIter += 1) + { + const uint64_t uTickUsecs = NetTickUsec(); + + /* scan backwards from the current exponent bit until a set bit is found to denote the start of the window + squaring the results until such a bit is found */ + if (CryptBnBitTest(&pState->PrimeMin2, pState->iBitIndex)) + { + int32_t iWindowBit, iWindowValue, iWindowEnd; + + /* scan backwards from the start of the window until the last set bit in the range of the window size is found which denotes the end bit index of the window + calculate the value of the window that will be used when multiplying against our precomputed table we skip the first bit as we know it is set based on the + current exponent bit check we make right above */ + for (iWindowBit = 1, iWindowValue = 1, iWindowEnd = 0; (iWindowBit < CRYPTMONT_WINDOW_SIZE) && ((pState->iBitIndex - iWindowBit) >= 0); iWindowBit += 1) + { + if (CryptBnBitTest(&pState->PrimeMin2, pState->iBitIndex - iWindowBit)) + { + iWindowValue <<= (iWindowBit - iWindowEnd); + iWindowValue |= 1; /* force odd */ + iWindowEnd = iWindowBit; + } + } + + // square for all the bits in the window and moving the exponent bit index down each time + for (iWindowBit = 0; iWindowBit < iWindowEnd + 1; iWindowBit += 1, pState->iBitIndex -= 1) + { + CryptBnModMultiply(&pState->Result.X, &pState->Result.X, &pState->Result.X, &pState->Prime); + } + + // skip the first multiply + if (!pState->bAccumulOne) + { + CryptBnModMultiply(&pState->Result.X, &pState->Result.X, &pState->pTable[iWindowValue/2], &pState->Prime); + } + else + { + CryptBnClone(&pState->Result.X, &pState->pTable[iWindowValue/2]); + pState->bAccumulOne = FALSE; + } + } + else + { + CryptBnModMultiply(&pState->Result.X, &pState->Result.X, &pState->Result.X, &pState->Prime); + pState->iBitIndex -= 1; + } + + // update timing + pState->uCryptUsecs += (uint32_t)NetTickDiff(NetTickUsec(), uTickUsecs); + } + + // calculate the final result + if (pState->iBitIndex < 0) + { + DirtyMemFree(pState->pTable, CRYPTMONT_MEMID, pState->iMemGroup, pState->pMemGroupUserdata); + pState->pTable = NULL; + + CryptBnModMultiply(&pState->Result.X, &pState->X_2, &pState->Result.X, &pState->Prime); + } + + return((pState->iBitIndex < 0) ? TRUE : FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _CryptMontInit25519PrivateKey + + \Description + Initializes the private key based on the requirements for this curve + (x25519) + + \Input *pPrivateKey - [out] private key state + \Input *pK - private key buffer or NULL if we should generate one + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptMontInit25519PrivateKey(CryptBnT *pPrivateKey, const uint8_t *pK) +{ + uint8_t aSecret[32]; + + /* per: https://tools.ietf.org/html/rfc7748#section-5 + For X25519, in order to decode 32 random bytes as an integer scalar, set the three least significant bits of the first byte and + the most significant bit of the last to zero, set the second most significant bit of the last byte to 1 and, finally, decode as + little-endian. This means that the resulting integer is of the form 2^254 plus eight times a value between 0 and 2^251 - 1 (inclusive) */ + + // retrieve the random bytes if not provided one + if (pK == NULL) + { + CryptRandGet(aSecret, sizeof(aSecret)); + } + else + { + ds_memcpy(aSecret, pK, sizeof(aSecret)); + } + + aSecret[0] &= 248; + aSecret[31] &= 127; + aSecret[31] |= 64; + CryptBnInitLeFrom(pPrivateKey, aSecret, sizeof(aSecret)); +} + +/*F********************************************************************************/ +/*! + \Function _CryptMontInit448PrivateKey + + \Description + Initializes the private key based on the requirements for this curve + (x448) + + \Input *pPrivateKey - [out] private key state + \Input *pK - private key buffer or NULL if we should generate one + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptMontInit448PrivateKey(CryptBnT *pPrivateKey, const uint8_t *pK) +{ + uint8_t aSecret[56]; + + /* per: https://tools.ietf.org/html/rfc7748#section-5 + Likewise, for X448, set the two least significant bits of the first byte to 0, and the most significant bit of the last byte to 1. This + means that the resulting integer is of the form 2^447 plus four times a value between 0 and 2^445 - 1 (inclusive). */ + + if (pK == NULL) + { + CryptRandGet(aSecret, sizeof(aSecret)); + } + else + { + ds_memcpy(aSecret, pK, sizeof(aSecret)); + } + + aSecret[0] &= 252; + aSecret[55] |= 128; + CryptBnInitLeFrom(pPrivateKey, aSecret, sizeof(aSecret)); +} + +/*** Public Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptMontInit + + \Description + Initializes the curve given an identifier + + \Input *pState - curve state we are initializing + \Input iCurveType - the curve identifier (CRYPTMONT_CURVE_*) + + \Output + int32_t - 0=success, negative=failure + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptMontInit(CryptMontT *pState, int32_t iCurveType) +{ + // init state + ds_memclr(pState, sizeof(*pState)); + pState->iBitIndex = -1; + pState->iCurveType = iCurveType; + + if (iCurveType == CRYPTCURVE_X25519) + { + // save prime + CryptBnInitFrom(&pState->Prime, -1, _aPrime25519, sizeof(_aPrime25519)); + // save a24 + CryptBnInitSet(&pState->A24, 121665); + // save u + CryptBnInitSet(&pState->BasePoint, 9); + // get k + _CryptMontInit25519PrivateKey(&pState->PrivateKey, NULL); + } + else if (iCurveType == CRYPTCURVE_X448) + { + // save prime + CryptBnInitFrom(&pState->Prime, -1, _aPrime448, sizeof(_aPrime448)); + // save a24 + CryptBnInitSet(&pState->A24, 39081); + // save u + CryptBnInitSet(&pState->BasePoint, 5); + // get k + _CryptMontInit448PrivateKey(&pState->PrivateKey, NULL); + } + else + { + NetPrintf(("cryptmont: unrecognized curve type (%d) passed to init\n", iCurveType)); + return(-1); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function CryptMontSetPrivateKey + + \Description + Sets our internal private key for verifying our test vectors based + on the state's curve type + + \Input *pState - curve state + \Input *pKey - the private key buffer + + \Notes + This is for testing purposes only + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +void CryptMontSetPrivateKey(CryptMontT *pState, const uint8_t *pKey) +{ + #if DIRTYCODE_DEBUG + if (pState->iCurveType == CRYPTCURVE_X25519) + { + _CryptMontInit25519PrivateKey(&pState->PrivateKey, pKey); + } + else if (pState->iCurveType == CRYPTCURVE_X448) + { + _CryptMontInit448PrivateKey(&pState->PrivateKey, pKey); + } + #endif +} + +/*F********************************************************************************/ +/*! + \Function CryptMontPublic + + \Description + Generates our public key by doing our PrivateKey * BasePoint on the curve + + \Input *pState - curve state + \Input *pResult - [out] result output (optional) + \Input *pCryptUsecs - [out] timing information for the operation (optional) + + \Output + int32_t - zero=success, otherwise=pending + + \Notes + If pResult is NULL, the result can be pulled from CryptMontT.Result + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptMontPublic(CryptMontT *pState, CryptEccPointT *pResult, uint32_t *pCryptUsecs) +{ + int32_t iResult = 1; + + if ((pState->eState == CRYPTMONT_COMPUTE_POINT) && (_CryptMontPointCalculate(pState, &pState->BasePoint))) + { + // switch state on completion + pState->eState = CRYPTMONT_COMPUTE_EXP; + } + else if ((pState->eState == CRYPTMONT_COMPUTE_EXP) && (_CryptMontResultCalculate(pState))) + { + // reset back to original state in case they want to perform a different operation + pState->eState = CRYPTMONT_COMPUTE_POINT; + + // copy the timing if passed in + if (pCryptUsecs != NULL) + { + *pCryptUsecs = pState->uCryptUsecs; + } + // signal completion + if (pResult != NULL) + { + CryptBnClone(&pResult->X, &pState->Result.X); + } + iResult = 0; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptMontSecret + + \Description + Generates our shared secret by doing our PrivateKey * PublicKey on the curve + + \Input *pState - curve state + \Input *pPublicKey - the peer's public key + \Input *pResult - [out] result output (optional) + \Input *pCryptUsecs - [out] timing information for the operation (optional) + + \Output + int32_t - zero=success, otherwise=pending + + \Notes + If pResult is NULL, the result can be pulled from CryptMontT.Result + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptMontSecret(CryptMontT *pState, CryptEccPointT *pPublicKey, CryptEccPointT *pResult, uint32_t *pCryptUsecs) +{ + int32_t iResult = 1; + + if ((pState->eState == CRYPTMONT_COMPUTE_POINT) && (_CryptMontPointCalculate(pState, &pPublicKey->X))) + { + // switch state on completion + pState->eState = CRYPTMONT_COMPUTE_EXP; + } + else if ((pState->eState == CRYPTMONT_COMPUTE_EXP) && (_CryptMontResultCalculate(pState))) + { + // reset back to original state in case they want to perform a different operation + pState->eState = CRYPTMONT_COMPUTE_POINT; + + // copy the timing if passed in + if (pCryptUsecs != NULL) + { + *pCryptUsecs = pState->uCryptUsecs; + } + // signal completion + if (pResult != NULL) + { + CryptBnClone(&pResult->X, &pState->Result.X); + } + iResult = 0; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptMontPointInitFrom + + \Description + Initializes our point representation given a buffer + + \Input *pPoint - point state + \Input *pBuffer - the buffer we are copying from + \Input iBufSize - size of the buffer + + \Output + int32_t - zero=success, otherwise=failure + + \Notes + These curves work in little endian so need to copy different functions + from our nist curves. + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptMontPointInitFrom(CryptEccPointT *pPoint, const uint8_t *pBuffer, int32_t iBufSize) +{ + CryptBnInitLeFrom(&pPoint->X, pBuffer, iBufSize); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function CryptMontPointFinal + + \Description + Copies our point data into an output buffer + + \Input *pState - curve state + \Input *pPoint - point state or NULL to use curve result + \Input bSecret - is this the shared secret? + \Input *pBuffer - [out] the buffer we are copying into + \Input iBufSize - size of the buffer + + \Output + int32_t - number of bytes encoded into the buffer + + \Notes + These curves work in little endian so need to copy different functions + from our nist curves. + + The bSecret is unused here but is left for compatibility with the other + curves' API. + + \Version 04/11/2018 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptMontPointFinal(const CryptMontT *pState, const CryptEccPointT *pPoint, uint8_t bSecret, uint8_t *pBuffer, int32_t iBufSize) +{ + // if point not provided assume we are using the result + if ((pState != NULL) && (pPoint == NULL)) + { + pPoint = &pState->Result; + } + + CryptBnFinalLe(&pPoint->X, pBuffer, iBufSize); + return(CryptBnByteLen(&pPoint->X)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptnist.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptnist.c new file mode 100644 index 00000000..9576e2c7 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptnist.c @@ -0,0 +1,1081 @@ +/*H********************************************************************************/ +/*! + \File cryptnist.c + + \Description + This module implements the math for elliptic curve cryptography + using curves in short Weierstrass form (NIST curves) + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/crypt/crypthash.h" +#include "DirtySDK/crypt/crypthmac.h" +#include "DirtySDK/crypt/cryptrand.h" +#include "DirtySDK/crypt/cryptnist.h" + +/*** Defines **********************************************************************/ + +//! the marker that marks uncompressed point format +#define CRYPTNIST_UNCOMPRESSED_MARKER (0x04) + +//! number of iterations to do per call +#if !defined(CRYPTNIST_NUM_ITERATIONS) + #define CRYPTNIST_NUM_ITERATIONS (0x10) +#endif + +//! memgroup for allocating the table +#define CRYPTNIST_MEMID ('nist') + +/*** Type Definitions *************************************************************/ + +//! defintion of a curve, basis of initialization into big numbers +typedef struct NistCurveT +{ + int32_t iIdent; + int32_t iSize; + uint8_t aPrime[48]; + uint8_t aCoefficientA[48]; + uint8_t aCoefficientB[48]; + uint8_t aBasePoint[97]; + uint8_t aOrder[48]; +} NistCurveT; + +/*** Variables ********************************************************************/ + +//! used to easily checked for default points +static const CryptEccPointT _Zero = { { { 0x0 }, 1, 0 }, { { 0x0 }, 1, 0 } }; + +//! the curves we support +static const NistCurveT _aEccCurves[] = +{ + { + CRYPTCURVE_SECP256R1, 32, + { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, + { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC }, + { 0x5A, 0xC6, 0x35, 0xD8, 0xAA, 0x3A, 0x93, 0xE7, 0xB3, 0xEB, 0xBD, 0x55, 0x76, 0x98, 0x86, 0xBC, 0x65, 0x1D, 0x06, 0xB0, 0xCC, 0x53, 0xB0, 0xF6, 0x3B, 0xCE, 0x3C, 0x3E, 0x27, 0xD2, 0x60, 0x4B }, + { 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96, + 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5 }, + { 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51 } + }, + { + CRYPTCURVE_SECP384R1, 48, + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF }, + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFC }, + { 0xB3, 0x31, 0x2F, 0xA7, 0xE2, 0x3E, 0xE7, 0xE4, 0x98, 0x8E, 0x05, 0x6B, 0xE3, 0xF8, 0x2D, 0x19, 0x18, 0x1D, 0x9C, 0x6E, 0xFE, 0x81, 0x41, 0x12, 0x03, 0x14, 0x08, 0x8F, 0x50, 0x13, 0x87, 0x5A, + 0xC6, 0x56, 0x39, 0x8D, 0x8A, 0x2E, 0xD1, 0x9D, 0x2A, 0x85, 0xC8, 0xED, 0xD3, 0xEC, 0x2A, 0xEF }, + { 0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38, + 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, 0x29, + 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F }, + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, + 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73 } + } +}; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CryptNistPointCalculate + + \Description + Does the math for doubling / adding points given they you have calculated + S over the curve's prime + + \Input *pState - curve state + \Input *pP - point P + \Input *pQ - point Q + \Input *pS - S used in calculation + \Input *pResult - [out] point R that is calculation of P+Q + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptNistPointCalculate(CryptNistT *pState, CryptEccPointT *pP, CryptEccPointT *pQ, CryptBnT *pS, CryptEccPointT *pResult) +{ + CryptEccPointT Result; + + // X = S² - P.X - Q.X + CryptBnInitSet(&Result.X, 0); + CryptBnModMultiply(&Result.X, pS, pS, &pState->Prime); + CryptBnSubtract(&Result.X, &Result.X, &pP->X); + CryptBnSubtract(&Result.X, &Result.X, &pQ->X); + + // Y = S * (P.X - X) - P.Y + CryptBnInitSet(&Result.Y, 0); + CryptBnSubtract(&Result.Y, &pP->X, &Result.X); + CryptBnModMultiply(&Result.Y, &Result.Y, pS, &pState->Prime); + CryptBnSubtract(&Result.Y, &Result.Y, &pP->Y); + + // X %= Curve.Prime + CryptBnMod(&Result.X, &pState->Prime, &Result.X, NULL); + // Y %= Curve.Prime + CryptBnMod(&Result.Y, &pState->Prime, &Result.Y, NULL); + + // write out the result + ds_memcpy(pResult, &Result, sizeof(*pResult)); +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistPointAddition + + \Description + Adds two points together + + \Input *pState - curve state + \Input *pPoint1 - first point in addition + \Input *pPoint2 - second point in addtion + \Input *pResult - [out] point R that is result of addition + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptNistPointAddition(CryptNistT *pState, CryptEccPointT *pPoint1, CryptEccPointT *pPoint2, CryptEccPointT *pResult) +{ + if (memcmp(pPoint1, &_Zero, sizeof(*pPoint1)) == 0) + { + ds_memcpy(pResult, pPoint2, sizeof(*pResult)); + } + else if (memcmp(pPoint2, &_Zero, sizeof(*pPoint2)) == 0) + { + ds_memcpy(pResult, pPoint1, sizeof(*pResult)); + } + else + { + CryptBnT A, B; + + CryptBnInitSet(&A, 0); + CryptBnInitSet(&B, 0); + + // A = y1 - y2 + CryptBnSubtract(&A, &pPoint1->Y, &pPoint2->Y); + + // B = (x1 - x2)⁻¹ % Curve.Prime [Inverse Modulus] + CryptBnSubtract(&B, &pPoint1->X, &pPoint2->X); + CryptBnInverseMod(&B, &pState->Prime); + + // A *= B + CryptBnModMultiply(&A, &A, &B, &pState->Prime); + + // calculate the result + _CryptNistPointCalculate(pState, pPoint1, pPoint2, &A, pResult); + } +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistPointDouble + + \Description + Doubles a point + + \Input *pState - curve state + \Input *pPoint - point that we are doubling + \Input *pResult - [out] result of the doubling + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptNistPointDouble(CryptNistT *pState, CryptEccPointT *pPoint, CryptEccPointT *pResult) +{ + CryptBnT A, B; + + // if attempting to double zero, return + if (memcmp(pPoint, &_Zero, sizeof(*pPoint)) == 0) + { + return; + } + + // A = 3 * x * x + a + CryptBnInitSet(&A, 3); + CryptBnMultiply(&A, &A, &pPoint->X); + CryptBnMultiply(&A, &A, &pPoint->X); + CryptBnAccumulate(&A, &pState->CoefficientA); + + // B = (2 * y)⁻¹ % Curve.Prime [Inverse Modulus] + CryptBnClone(&B, &pPoint->Y); + CryptBnLeftShift(&B); + CryptBnInverseMod(&B, &pState->Prime); + + // A *= B + CryptBnModMultiply(&A, &A, &B, &pState->Prime); + + // calculate the result + _CryptNistPointCalculate(pState, pPoint, pPoint, &A, pResult); +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistDoubleAndAdd + + \Description + Performs the scalar multiplication using double and add + operations using a sliding window for the private key + + \Input *pState - curve state + \Input *pInput - point we are multiplying by the private key + \Input *pPrivateKey - private key was using to multiply + \Input *pResult - [out] result of the operation + + \Output + int32_t - zero=success, otherwise=pending + + \Notes + Adapted from the sliding window algorithm used for exponentiation + in RSA + See Handbook of Applied Cryptography Chapter 14.6.1 (14.85): + http://cacr.uwaterloo.ca/hac/about/chap14.pdf + + \Version 05/08/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CryptNistDoubleAndAdd(CryptNistT *pState, CryptEccPointT *pInput, CryptBnT *pPrivateKey, CryptEccPointT *pResult) +{ + int32_t iIter = CRYPTNIST_NUM_ITERATIONS; + + do + { + const uint64_t uTick = NetTickUsec(); + + if (pState->iKeyIndex < 0) + { + int32_t iTableIndex; + CryptEccPointT Double; + + // init working state + ds_memcpy(pResult, &_Zero, sizeof(*pResult)); + + // query memgroup + DirtyMemGroupQuery(&pState->iMemGroup, &pState->pMemGroupUserdata); + + if ((pState->pTable = (CryptEccPointT *)DirtyMemAlloc(sizeof(*pState->pTable) * CRYPTNIST_TABLE_SIZE, CRYPTNIST_MEMID, pState->iMemGroup, pState->pMemGroupUserdata)) == NULL) + { + NetPrintf(("cryptnist: failed to allocate window table\n")); + return(0); + } + ds_memclr(pState->pTable, sizeof(*pState->pTable) * CRYPTNIST_TABLE_SIZE); + + /* put the input into the first + index of the table, this will make it so further + entries in the table will not be too large + for our modulus operations */ + ds_memcpy(&pState->pTable[0], pInput, sizeof(pState->pTable[0])); + + // calculate doubled input as the basis for the other calculations + _CryptNistPointDouble(pState, pInput, &Double); + + // calculate the further added points for the rest of the table + for (iTableIndex = 1; iTableIndex < CRYPTNIST_TABLE_SIZE; iTableIndex += 1) + { + _CryptNistPointAddition(pState, &pState->pTable[iTableIndex - 1], &Double, &pState->pTable[iTableIndex]); + } + + // set the defaults + pState->iKeyIndex = CryptBnBitLen(pPrivateKey) - 1; + + // clear the profiling information + pState->uCryptUSecs = 0; + } + + /* scan backwards from the current private key bit + until a set bit is found to denote the start of the window + doubling the results until such a bit is found */ + if (CryptBnBitTest(pPrivateKey, pState->iKeyIndex)) + { + int32_t iWindowBit, iWindowValue, iWindowEnd; + + /* scan backwards from the start of the window until the last set bit in the range of the window size is found which denotes the end bit index of the window + calculate the value of the window that will be used when adding against our precomputed table. + we skip the first bit as we know it is set based on the current private key bit check we make right above */ + for (iWindowBit = 1, iWindowValue = 1, iWindowEnd = 0; (iWindowBit < CRYPTNIST_WINDOW_SIZE) && ((pState->iKeyIndex - iWindowBit) >= 0); iWindowBit += 1) + { + if (CryptBnBitTest(pPrivateKey, pState->iKeyIndex - iWindowBit)) + { + iWindowValue <<= (iWindowBit - iWindowEnd); + iWindowValue |= 1; // force odd + iWindowEnd = iWindowBit; + } + } + + // double for each bits in the window and moving the private key bit index down each time + for (iWindowBit = 0; iWindowBit < iWindowEnd + 1; iWindowBit += 1, pState->iKeyIndex -= 1) + { + _CryptNistPointDouble(pState, pResult, pResult); + } + + // use the window value to get which precomputed power we need to multiply + _CryptNistPointAddition(pState, pResult, &pState->pTable[iWindowValue/2], pResult); + } + else + { + _CryptNistPointDouble(pState, pResult, pResult); + pState->iKeyIndex -= 1; + } + + // update profiler information + pState->uCryptUSecs += NetTickDiff(NetTickUsec(), uTick); + } + while ((pState->iKeyIndex >= 0) && (--iIter > 0)); + + // cleanup memory when done + if (pState->iKeyIndex < 0) + { + DirtyMemFree(pState->pTable, CRYPTNIST_MEMID, pState->iMemGroup, pState->pMemGroupUserdata); + pState->pTable = NULL; + } + + return(pState->iKeyIndex >= 0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistDoubleMultiply + + \Description + Uses Shamir's Trick to do double point multiplication: + (pInput1 * pPrivateKey1) + (pInput2 * pPrivateKey2) + + \Input *pState - curve state + \Input *pInput1 - point we are multiplying by pMul1 + \Input *pMul1 - big number we are multiplying by pInput1 + \Input *pInput2 - point we are multiplying by pMul2 + \Input *pMul2 - big number we are multiplying by pInput2 + \Input *pResult - [out] result of the operation + + \Output + int32_t - zero=success, otherwise=pending + + \Notes + For more information on the algorithm, see the Double-scalar + multiplication slides: http://www.lirmm.fr/~imbert/talks/laurent_Asilomar_08.pdf + + \Version 05/15/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CryptNistDoubleMultiply(CryptNistT *pState, CryptEccPointT *pInput1, CryptBnT *pMul1, CryptEccPointT *pInput2, CryptBnT *pMul2, CryptEccPointT *pResult) +{ + int32_t iIter = CRYPTNIST_NUM_ITERATIONS; + + do + { + const uint64_t uTick = NetTickUsec(); + uint8_t bLHBitSet, bRHBitSet; + + if (pState->iKeyIndex < 0) + { + DirtyMemGroupQuery(&pState->iMemGroup, &pState->pMemGroupUserdata); + + if ((pState->pTable = (CryptEccPointT *)DirtyMemAlloc(sizeof(*pState->pTable), CRYPTNIST_MEMID, pState->iMemGroup, pState->pMemGroupUserdata)) == NULL) + { + NetPrintf(("cryptnist: failed to allocate window table\n")); + return(0); + } + ds_memclr(pState->pTable, sizeof(*pState->pTable)); + + // precompute pInput1 + pInput2 + _CryptNistPointAddition(pState, pInput1, pInput2, &pState->pTable[0]); + + // init working state + ds_memcpy(pResult, &_Zero, sizeof(*pResult)); + + // set the maximum bit size + pState->iKeyIndex = DS_MAX(CryptBnBitLen(pMul1), CryptBnBitLen(pMul2)) - 1; + + // clear the profiling information + pState->uCryptUSecs = 0; + } + + // double for each column + _CryptNistPointDouble(pState, pResult, pResult); + + // check the bit set in the column + bLHBitSet = CryptBnBitTest(pMul1, pState->iKeyIndex); + bRHBitSet = CryptBnBitTest(pMul2, pState->iKeyIndex); + + // if only left hand, then add by that side + if ((bLHBitSet == TRUE) && (bRHBitSet == FALSE)) + { + _CryptNistPointAddition(pState, pResult, pInput1, pResult); + } + // if only right hand, then add by that side + else if ((bLHBitSet == FALSE) && (bRHBitSet == TRUE)) + { + _CryptNistPointAddition(pState, pResult, pInput2, pResult); + } + // if both sides, then add by the sum of both + else if ((bLHBitSet == TRUE) && (bRHBitSet == TRUE)) + { + _CryptNistPointAddition(pState, pResult, &pState->pTable[0], pResult); + } + + // update profiler information + pState->uCryptUSecs += NetTickDiff(NetTickUsec(), uTick); + } + while ((--pState->iKeyIndex >= 0) && (--iIter > 0)); + + // cleanup table memory + if (pState->iKeyIndex < 0) + { + DirtyMemFree(pState->pTable, CRYPTNIST_MEMID, pState->iMemGroup, pState->pMemGroupUserdata); + pState->pTable = NULL; + } + + return(pState->iKeyIndex >= 0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistRand + + \Description + Generates a random number in the range [1, curve.order-1] + + \Input *pState - curve state + \Input *pResult - [out] random number generated + + \Version 05/15/2017 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptNistRand(const CryptNistT *pState, CryptBnT *pResult) +{ + uint8_t aSecret[48]; + CryptBnT Temp; + + // generate a secret to the specified size + CryptRandGet(aSecret, sizeof(aSecret)); + CryptBnInitFrom(pResult, -1, aSecret, DS_MIN((int32_t)sizeof(aSecret), pState->iSize)); + + // clone order so we don't modify + CryptBnClone(&Temp, &pState->Order); + + // random number = (rand() % order-1 + 1) + CryptBnDecrement(&Temp); + CryptBnMod(pResult, &Temp, pResult, NULL); + CryptBnIncrement(pResult); +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistDsaInitHash + + \Description + Initializes the hash, truncating if necessary + + \Input *pState - curve state + \Input *pHashData - the buffer with the hash digest + \Input iHashSize - size of the buffer + \Input *pHash - [out] big number representation of the hash + + \Notes + Based on the algorithm for ECDSA, the hash needs to be truncated to the + bit length of curve.order which can be done by a right shift. The problem + is that other implementations don't follow this and just truncate whole + bytes first. For this reason that is what we do here. + + \Version 02/13/2018 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptNistDsaInitHash(const CryptNistT *pState, const uint8_t *pHashData, int32_t iHashSize, CryptBnT *pHash) +{ + int32_t iOrderSize = CryptBnBitLen(&pState->Order); + + if ((iHashSize*8) > iOrderSize) + { + iHashSize = (iOrderSize + 7) / 8; + } + CryptBnInitFrom(pHash, -1, pHashData, iHashSize); +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistInit + + \Description + Initializes the curve state + + \Input *pState - curve state + \Input *pCurve - curve data to initialize the state + + \Output + int32_t - 0=success, negative=failure + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CryptNistInit(CryptNistT *pState, const NistCurveT *pCurve) +{ + // initialize the curve parameters + pState->iSize = pCurve->iSize; + CryptBnInitFrom(&pState->Prime, -1, pCurve->aPrime, pCurve->iSize); + CryptBnInitFrom(&pState->CoefficientA, -1, pCurve->aCoefficientA, pCurve->iSize); + CryptBnInitFrom(&pState->CoefficientB, -1, pCurve->aCoefficientB, pCurve->iSize); + CryptBnInitFrom(&pState->Order, -1, pCurve->aOrder, pCurve->iSize); + if (CryptNistPointInitFrom(&pState->BasePoint, pCurve->aBasePoint, pCurve->iSize * 2 + 1) < 0) + { + return(-1); + } + + // initialize the computation state + pState->iKeyIndex = -1; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _CryptNistDsaInitK + + \Description + Generates a K value for signing operations deterministically based on + the private key, message hash and curve + + \Input *pState - curve state + \Input *pPrivateKey - private key information + \Input *pHash - hash of the message we are signing + \Input iHashSize - size of the hash + \Input *pResult - [out] K value for signing + + \Notes + This work is based on RFC6979: https://tools.ietf.org/html/rfc6979 + It allows for selecting a K value without the use of random data. + Aside from this generating benefit the ability to generate deterministic + signatures are required for other cryptographic operations. + + \Version 01/08/2018 (eesponda) +*/ +/********************************************************************************F*/ +static void _CryptNistDsaInitK(const CryptNistT *pState, const CryptBnT *pPrivateKey, const uint8_t *pHash, int32_t iHashSize, CryptBnT *pResult) +{ + static const uint8_t _aZero[] = { 0x00 }, _aOne[] = { 0x01 }; + + CryptHashTypeE eHashType; + uint8_t aNewHash[CRYPTHASH_MAXDIGEST], aK[CRYPTHASH_MAXDIGEST], aV[CRYPTHASH_MAXDIGEST], aT[CRYPTHASH_MAXDIGEST], aPrivateKey[48]; + CryptHmacMsgT aMessages[4]; + int32_t iHashOffset = iHashSize < pState->iSize ? pState->iSize - iHashSize : 0; + + // figure out the hash based on the type + if ((eHashType = CryptHashGetBySize(iHashSize)) == CRYPTHASH_NULL) + { + NetPrintf(("cryptnist: cannot generate deterministic k, unsupported hash used (size=%d)\n", iHashSize)); + return; + } + + // copy the hash to our buffer, this ensures correct format + ds_memclr(aNewHash, sizeof(aNewHash)); + ds_memcpy_s(aNewHash+iHashOffset, sizeof(aNewHash)-iHashOffset, pHash, iHashSize); + + // init k, v, private key + ds_memclr(aK, sizeof(aK)); + ds_memset(aV, 0x01, sizeof(aV)); + CryptBnFinal(pPrivateKey, aPrivateKey, pState->iSize); + + // K = HMAC(V || 0x00 || PrivateKey || H(m)) + aMessages[0].pMessage = aV; + aMessages[0].iMessageLen = iHashSize; + aMessages[1].pMessage = _aZero; + aMessages[1].iMessageLen = sizeof(_aZero); + aMessages[2].pMessage = aPrivateKey; + aMessages[2].iMessageLen = pState->iSize; + aMessages[3].pMessage = aNewHash; + aMessages[3].iMessageLen = pState->iSize; + CryptHmacCalcMulti(aK, iHashSize, aMessages, 4, aK, iHashSize, eHashType); + + // V = HMAC(V) + CryptHmacCalc(aV, iHashSize, aV, iHashSize, aK, iHashSize, eHashType); + + // K = HMAC(V || 0x01 || PrivateKey || H(m)) + aMessages[0].pMessage = aV; + aMessages[0].iMessageLen = iHashSize; + aMessages[1].pMessage = _aOne; + aMessages[1].iMessageLen = sizeof(_aOne); + aMessages[2].pMessage = aPrivateKey; + aMessages[2].iMessageLen = pState->iSize; + aMessages[3].pMessage = aNewHash; + aMessages[3].iMessageLen = pState->iSize; + CryptHmacCalcMulti(aK, iHashSize, aMessages, 4, aK, iHashSize, eHashType); + + // V = HMAC(V) + CryptHmacCalc(aV, iHashSize, aV, iHashSize, aK, iHashSize, eHashType); + + // generate T for new K candidate + while (1) + { + int32_t iOffset = 0, iBytes; + + while (iOffset < pState->iSize) + { + // V = HMAC(V) + CryptHmacCalc(aV, iHashSize, aV, iHashSize, aK, iHashSize, eHashType); + iBytes = DS_MIN(iHashSize, pState->iSize - iOffset); + ds_memcpy(aT+iOffset, aV, iBytes); + iOffset += iBytes; + } + + // copy result and check if suitable + CryptBnInitFrom(pResult, -1, aT, pState->iSize); + if (CryptBnCompare(pResult, &pState->Order) < 0) + { + break; + } + + // K = HMAC(V || 0x00) + aMessages[0].pMessage = aV; + aMessages[0].iMessageLen = iHashSize; + aMessages[1].pMessage = _aZero; + aMessages[1].iMessageLen = sizeof(_aZero); + CryptHmacCalcMulti(aK, iHashSize, aMessages, 2, aK, iHashSize, eHashType); + + // V = HMAC(V) + CryptHmacCalc(aV, iHashSize, aV, iHashSize, aK, iHashSize, eHashType); + } +} + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptNistInitDh + + \Description + Initializes the curve state for performing diffie hellmen key exchange + + \Input *pState - curve state + \Input iCurveType - identifier for the curve we are using + + \Output + int32_t - 0=success, negative=failure + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistInitDh(CryptNistDhT *pState, int32_t iCurveType) +{ + int32_t iResult, iCurveIdx; + const NistCurveT *pCurve = NULL; + + ds_memclr(pState, sizeof(*pState)); + + // find the curve + for (iCurveIdx = 0; iCurveIdx < (signed)(sizeof(_aEccCurves)/sizeof(_aEccCurves[0])); iCurveIdx += 1) + { + if (iCurveType == _aEccCurves[iCurveIdx].iIdent) + { + pCurve = &_aEccCurves[iCurveIdx]; + break; + } + } + // check to make sure we found a valid curve + if (pCurve == NULL) + { + NetPrintf(("cryptnist: could not find matching curve information for dh given ident %d\n", iCurveType)); + return(-1); + } + + // initialize the curve and computation data + if ((iResult = _CryptNistInit(&pState->Ecc, pCurve)) == 0) + { + // generate random private key + _CryptNistRand(&pState->Ecc, &pState->PrivateKey); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistInitDsa + + \Description + Initializes the curve state for performing dsa sign and verify + + \Input *pState - curve state + \Input iCurveType - identifier for the curve we are using + + \Output + int32_t - 0=success, negative=failure + + \Version 04/12/2018 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistInitDsa(CryptNistDsaT *pState, int32_t iCurveType) +{ + int32_t iCurveIdx; + const NistCurveT *pCurve = NULL; + + ds_memclr(pState, sizeof(*pState)); + + // find the curve + for (iCurveIdx = 0; iCurveIdx < (signed)(sizeof(_aEccCurves)/sizeof(_aEccCurves[0])); iCurveIdx += 1) + { + if (iCurveType == _aEccCurves[iCurveIdx].iIdent) + { + pCurve = &_aEccCurves[iCurveIdx]; + break; + } + } + // check to make sure we found a valid curve + if (pCurve == NULL) + { + NetPrintf(("cryptnist: could not find matching curve information for dh given ident %d\n", iCurveType)); + return(-1); + } + + // initialize the curve and computation data + return(_CryptNistInit(&pState->Ecc, pCurve)); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistPublic + + \Description + Generates a public key + + \Input *pState - curve state + \Input *pResult - [out] point on the curve that represents the public key (optional) + \Input *pCryptUsecs - [out] timing information for the operation (optional) + + \Output + int32_t - zero=success, otherwise=pending + + \Notes + If pResult is NULL, the result can be taken from CryptNistT.Result + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistPublic(CryptNistDhT *pState, CryptEccPointT *pResult, uint32_t *pCryptUsecs) +{ + int32_t iResult; + + if ((iResult = _CryptNistDoubleAndAdd(&pState->Ecc, &pState->Ecc.BasePoint, &pState->PrivateKey, &pState->Result)) == 0) + { + // copy the timing if passed in + if (pCryptUsecs != NULL) + { + *pCryptUsecs = pState->Ecc.uCryptUSecs; + } + /* copy the final result if a valid destination is provided (the caller can copy the result + later if desired) */ + if (pResult != NULL) + { + ds_memcpy(pResult, &pState->Result, sizeof(*pResult)); + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistSecret + + \Description + Generates a shared secret + + \Input *pState - curve state + \Input *pPublicKey - point as a basis to compute the shared secret + \Input *pResult - [out] point on the curve that represents the shared secret (optional) + \Input *pCryptUsecs - [out] timing information for the operation (optional) + + \Output + int32_t - zero=success, otherwise=pending + + \Notes + If pResult is NULL, the result can be taken from CryptNistT.Result + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistSecret(CryptNistDhT *pState, CryptEccPointT *pPublicKey, CryptEccPointT *pResult, uint32_t *pCryptUsecs) +{ + int32_t iResult; + + if ((iResult = _CryptNistDoubleAndAdd(&pState->Ecc, pPublicKey, &pState->PrivateKey, &pState->Result)) == 0) + { + // copy the timing if passed in + if (pCryptUsecs != NULL) + { + *pCryptUsecs = pState->Ecc.uCryptUSecs; + } + /* copy the final result if a valid destination is provided (the caller can copy the result + later if desired) */ + if (pResult != NULL) + { + ds_memcpy(pResult, &pState->Result, sizeof(*pResult)); + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistSign + + \Description + Generates an elliptic curve dsa signature + + \Input *pState - curve state + \Input *pPrivateKey - key used to sign + \Input *pHash - hash we are creating a signature for + \Input iHashSize - size of the hash + \Input *pSignature - [out] point on the curve that represents our signature (optional) + \Input *pCryptUsecs - [out] timing information for the operation (optional) + + \Output + int32_t - zero=success, otherwise=pending + + \Notes + If pSignature is NULL, the result can be taken from CryptNistT.Result + + \Version 05/15/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistSign(CryptNistDsaT *pState, const CryptBnT *pPrivateKey, const uint8_t *pHash, int32_t iHashSize, CryptEccPointT *pSignature, uint32_t *pCryptUsecs) +{ + int32_t iResult; + CryptNistT *pEcc = &pState->Ecc; + + // generate a secret to the specified size + if (CryptBnBitLen(&pState->K) == 0) + { + _CryptNistDsaInitK(pEcc, pPrivateKey, pHash, iHashSize, &pState->K); + } + + // calculate x,y using k * basepoint + if ((iResult = _CryptNistDoubleAndAdd(pEcc, &pEcc->BasePoint, &pState->K, &pState->Result)) == 0) + { + const uint64_t uTickUsecs = NetTickUsec(); + CryptBnT Hash; + + // r = x % order + CryptBnMod(&pState->Result.X, &pEcc->Order, &pState->Result.X, NULL); + + // s1 = ((hash + r * private) + CryptBnInitFrom(&Hash, -1, pHash, iHashSize); + + // truncate the hash if necessary + _CryptNistDsaInitHash(pEcc, pHash, iHashSize, &Hash); + + CryptBnMultiply(&pState->Result.Y, &pState->Result.X, pPrivateKey); + CryptBnAccumulate(&pState->Result.Y, &Hash); + + // s2 = inverse_mod(k, order) + CryptBnInverseMod(&pState->K, &pEcc->Order); + + // s = s1 * s2 % order + CryptBnModMultiply(&pState->Result.Y, &pState->Result.Y, &pState->K, &pEcc->Order); + + /* copy the final result if a valid destination is provided (the caller can copy the result + later if desired) */ + if (pSignature != NULL) + { + ds_memcpy(pSignature, &pState->Result, sizeof(*pSignature)); + } + + // update profiler information + pEcc->uCryptUSecs += NetTickDiff(NetTickUsec(), uTickUsecs); + + // copy the timing if passed in + if (pCryptUsecs != NULL) + { + *pCryptUsecs = pEcc->uCryptUSecs; + } + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistVerify + + \Description + Verify the elliptic curve dsa signature + + \Input *pState - curve state + \Input *pPublicKey - the public used for verifying the signature + \Input *pHash - hash we are verifying a signature for + \Input iHashSize - size of the hash + \Input *pSignature - point on the curve that represents our signature + \Input *pCryptUsecs - [out] timing information for the operation (optional) + + \Output + int32_t - zero=success, >0=pending, <0=failure + + \Version 05/15/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistVerify(CryptNistDsaT *pState, CryptEccPointT *pPublicKey, const uint8_t *pHash, int32_t iHashSize, CryptEccPointT *pSignature, uint32_t *pCryptUsecs) +{ + int32_t iResult; + CryptNistT *pEcc = &pState->Ecc; + + // initialize state if necessary + if (pEcc->iKeyIndex < 0) + { + CryptBnT S, Hash; + + // truncate the hash if necessary + _CryptNistDsaInitHash(pEcc, pHash, iHashSize, &Hash); + + // s' = inverse_mod(signature.s, order); + CryptBnClone(&S, &pSignature->Y); + CryptBnInverseMod(&S, &pEcc->Order); + + // u1 = s' * hash % order + CryptBnModMultiply(&pState->U1, &Hash, &S, &pEcc->Order); + // u2 = s' * signature.r % order + CryptBnModMultiply(&pState->U2, &pSignature->X, &S, &pEcc->Order); + } + + // result = (u1 * basepoint) + (u2 * publickey) + if ((iResult = _CryptNistDoubleMultiply(pEcc, &pEcc->BasePoint, &pState->U1, pPublicKey, &pState->U2, &pState->Result)) == 0) + { + // copy the timing if passed in + if (pCryptUsecs != NULL) + { + *pCryptUsecs = pEcc->uCryptUSecs; + } + // signature valid if result.x == signature.x + iResult = (memcmp(&pSignature->X, &pState->Result.X, sizeof(pSignature->X)) == 0) ? 0 : -1; + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistPointValidate + + \Description + Validate that the point is on the curve + + \Input *pState - curve state + \Input *pPoint - point we are validating + + \Output + uint8_t - TRUE=on the curve, FALSE=not on the curve + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +uint8_t CryptNistPointValidate(const CryptNistDhT *pState, const CryptEccPointT *pPoint) +{ + CryptBnT Result, X, Y, A; + const CryptNistT *pEcc = &pState->Ecc; + + // a' = curve.a * x + CryptBnMultiply(&A, &pEcc->CoefficientA, &pPoint->X); + + // y' = y * y + CryptBnMultiply(&Y, &pPoint->Y, &pPoint->Y); + + // x' = x * x * x + CryptBnMultiply(&X, &pPoint->X, &pPoint->X); + CryptBnMultiply(&X, &X, &pPoint->X); + + // result = y' - x' - a' - curve.b + CryptBnSubtract(&Result, &Y, &X); + CryptBnSubtract(&Result, &Result, &A); + CryptBnSubtract(&Result, &Result, &pEcc->CoefficientB); + + CryptBnMod(&Result, &pEcc->Prime, &Result, NULL); + return(CryptBnBitLen(&Result) == 0); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistPointInitFrom + + \Description + Loads a point from a buffer + + \Input *pPoint - [out] the point we are writing the information to + \Input *pBuffer - buffer we are reading the point data from + \Input iBufLen - size of the buffer + + \Output + int32_t - 0=success, negative=failure + + \Notes + We only support reading uncompressed points + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistPointInitFrom(CryptEccPointT *pPoint, const uint8_t *pBuffer, int32_t iBufLen) +{ + int32_t iPointLen = (iBufLen-1)/2; + + // we only support uncompressed points, so ensure that this is the case + if (*pBuffer++ != CRYPTNIST_UNCOMPRESSED_MARKER) + { + NetPrintf(("cryptnist: could not initialize curve, base point in wrong format\n")); + return(-1); + } + CryptBnInitFrom(&pPoint->X, -1, pBuffer, iPointLen); + CryptBnInitFrom(&pPoint->Y, -1, pBuffer+iPointLen, iPointLen); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function CryptNistPointFinal + + \Description + Loads a point into a buffer for dh + + \Input *pState - curve state + \Input *pPoint - point state or NULL to use curve result + \Input bSecret - is this the shared secret? (only need x) + \Input *pBuffer - buffer we are writing the point data to + \Input iBufLen - size of the buffer + + \Output + int32_t - 0=success, negative=failure + + \Notes + We only support writing uncompressed points + + \Version 02/17/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptNistPointFinal(const CryptNistDhT *pState, const CryptEccPointT *pPoint, uint8_t bSecret, uint8_t *pBuffer, int32_t iBufLen) +{ + if ((pState != NULL) && (pPoint == NULL)) + { + pPoint = &pState->Result; + } + const int32_t iXSize = CryptBnByteLen(&pPoint->X); + const int32_t iYSize = CryptBnByteLen(&pPoint->Y); + const int32_t iOutputSize = !bSecret ? (iXSize + iYSize + 1) : iXSize; + + if (iBufLen < iOutputSize) + { + NetPrintf(("cryptnist: not enough space in output buffer to encode points\n")); + return(-1); + } + + if (!bSecret) + { + *pBuffer++ = CRYPTNIST_UNCOMPRESSED_MARKER; + CryptBnFinal(&pPoint->X, pBuffer, iXSize); + CryptBnFinal(&pPoint->Y, pBuffer+iXSize, iYSize); + } + else + { + CryptBnFinal(&pPoint->X, pBuffer, iXSize); + } + return(iOutputSize); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptrand.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrand.c new file mode 100644 index 00000000..207ae14d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrand.c @@ -0,0 +1,217 @@ +/*H********************************************************************************/ +/*! + \File cryptrand.c + + \Description + Cryptographic random number generator using system-defined rng and chacha + cipher. + + \Notes + Native implementations used on: Android, Apple OSX, Apple iOS, Linux, PS4, Windows + See the platform specific cryptrand source files for more details. + + References: http://tools.ietf.org/html/rfc4086 + http://randomnumber.org/links.htm + + \Copyright + Copyright (c) 2012-2020 Electronic Arts Inc. +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "cryptrandpriv.h" + +#if defined(CRYPTRAND_LINUX) +#include +#include +#include +#elif defined(CRYPTRAND_APPLE) +#include +#include +#elif defined(CRYPTRAND_WINDOWS) +#define WIN32_LEAN_AND_MEAN 1 +#include +#include +#elif defined(CRYPTRAND_SONY) +#include +#endif + +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/crypt/cryptrand.h" +#include "cryptrandcommon.h" + +/*** Type Definitions *************************************************************/ + +//! internal rand state +typedef struct CryptRandRefT +{ + CryptRandCommonT Common; //!< common state + int32_t iMemGroup; //!< memgroup id + void *pMemGroupUserData; //!< user data associated with memgroup + + #if defined(CRYPTRAND_LINUX) + int32_t iFd; //!< urandom file descriptor + #elif defined(CRYPTRAND_WINDOWS) + BCRYPT_ALG_HANDLE hAlgProvider; + #endif +} CryptRandRefT; + +/*** Variables ********************************************************************/ + +//! global rand state +static CryptRandRefT *_CryptRand_pState = NULL; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _CryptRandGetEntropy + + \Description + Get random data from the entropy pool + + \Input *pBuffer - [out] random data + \Input iBufSize - size of random data + + \Version 01/24/2019 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _CryptRandGetEntropy(uint8_t *pBuffer, int32_t iBufSize) +{ + #if defined(CRYPTRAND_LINUX) + if (read(_CryptRand_pState->iFd, pBuffer, iBufSize) == iBufSize) + { + return(0); + } + NetPrintf(("cryptrand: read(/dev/urandom) failed (err=%s)\n", DirtyErrGetName(errno))); + #elif defined(CRYPTRAND_APPLE) + int32_t iResult; + if ((iResult = SecRandomCopyBytes(kSecRandomDefault, iBufSize, pBuffer)) >= 0) + { + return(0); + } + NetPrintf(("cryptrand: SecRandomCopyBytes() failed (err=%s)\n", DirtyErrGetName(iResult))); + #elif defined(CRYPTRAND_WINDOWS) + if (BCryptGenRandom(_CryptRand_pState->hAlgProvider, pBuffer, iBufSize, 0) == S_OK) + { + return(0); + } + NetPrintf(("cryptrand: BCryptGetRandom() failed (err=%d)\n", GetLastError())); + #elif defined(CRYPTRAND_SONY) + int32_t iResult; + if ((iResult = sceRandomGetRandomNumber(pBuffer, iBufSize)) == SCE_OK) + { + return(0); + } + NetPrintf(("cryptrand: sceRandomGetRandomNumber() failed (err=%s)\n", DirtyErrGetName(iResult))); + #endif + return(-1); +} + +/*** Public Functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CryptRandInit + + \Description + Initialize CryptRand module + + \Version 12/05/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t CryptRandInit(void) +{ + CryptRandRefT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query memgroup info + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate state memory + if ((pState = (CryptRandRefT *)DirtyMemAlloc(sizeof(*pState), CRYPTRAND_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("cryptrand: failed to allocate state\n")); + return(-1); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + pState->Common.GetEntropy = &_CryptRandGetEntropy; + + #if defined(CRYPTRAND_LINUX) + // open urandom + if ((pState->iFd = open("/dev/urandom", O_RDONLY)) < 0) + { + DirtyMemFree(pState, CRYPTRAND_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(-1); + } + #elif defined(CRYPTRAND_WINDOWS) + // open algorithm provider + if (BCryptOpenAlgorithmProvider(&pState->hAlgProvider, BCRYPT_RNG_ALGORITHM, NULL, 0) != S_OK) + { + DirtyMemFree(pState, CRYPTRAND_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(-1); + } + #endif + _CryptRand_pState = pState; + + // init the crypt rand state after the proper setup has been done + if (CryptRandCommonInit(&pState->Common) < 0) + { + CryptRandShutdown(); + return(-1); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function CryptRandShutdown + + \Description + Shut down the CryptRand module + + \Version 12/05/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptRandShutdown(void) +{ + CryptRandRefT *pState = _CryptRand_pState; + + #if defined(CRYPTRAND_LINUX) + // close file descriptor + close(pState->iFd); + pState->iFd = -1; + #elif defined(CRYPTRAND_WINDOWS) + // close algorithm provider + BCryptCloseAlgorithmProvider(pState->hAlgProvider, 0); + pState->hAlgProvider = 0; + #endif + + // free memory and reset state pointer + DirtyMemFree(pState, CRYPTRAND_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + _CryptRand_pState = NULL; +} + +/*F********************************************************************************/ +/*! + \Function CryptRandGet + + \Description + Get random data + + \Input *pBuffer - [out] random data + \Input iBufSize - size of random data + + \Version 12/05/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void CryptRandGet(uint8_t *pBuffer, int32_t iBufSize) +{ + CryptRandCommonGet(&_CryptRand_pState->Common, pBuffer, iBufSize); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandcommon.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandcommon.c new file mode 100644 index 00000000..e51a97c0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandcommon.c @@ -0,0 +1,116 @@ +/*H********************************************************************************/ +/*! + \File cryptrandcommon.c + + \Description + Cryptographic random number generator using system-defined rng and chacha + cipher. + + \Notes + Native implementations used on: Android, Apple OSX, Apple iOS, Linux, PS4, Windows + See the platform specific cryptrand source files for more details. + + References: http://tools.ietf.org/html/rfc4086 + http://randomnumber.org/links.htm + + \Copyright + Copyright (c) 2012-2019 Electronic Arts Inc. + + \Version 12/05/2012 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock/dirtylib.h" +#include "cryptrandcommon.h" + +/*** Defines **********************************************************************/ + +//! key rotation interval in bytes +#define CRYPTRAND_KEY_INTERVAL (4*1024) + +/*** Public Functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function CryptRandCommonInit + + \Description + Initialize CryptRand module's common state + + \Input *pCommon - common state + + \Output + int32_t - result of initialization (0=success, negative=failure) + + \Version 01/24/2019 (eesponda) +*/ +/********************************************************************************F*/ +int32_t CryptRandCommonInit(CryptRandCommonT *pCommon) +{ + uint8_t aKey[32]; + + // pull key from entropy source + if (pCommon->GetEntropy(aKey, sizeof(aKey)) != 0) + { + NetPrintf(("cryptrand: unable to pull bytes from entropy for cipher key\n")); + return(-1); + } + + // initialize the stream cipher and initialize bytes left + CryptChaChaInit(&pCommon->Cipher, aKey, sizeof(aKey)); + pCommon->iBytesLeft = CRYPTRAND_KEY_INTERVAL; + + // clear key from stack memory + ds_memclr(aKey, sizeof(aKey)); + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function CryptRandCommonGet + + \Description + Get random data + + \Input *pCommon - common state + \Input *pBuffer - [out] random bytes + \Input iBufSize - size of the random bytes we are requesting + + \Version 01/24/2019 (eesponda) +*/ +/********************************************************************************F*/ +void CryptRandCommonGet(CryptRandCommonT *pCommon, uint8_t *pBuffer, int32_t iBufSize) +{ + uint8_t aNonce[12]; + uint8_t bGetEntropySuccess = TRUE; + + // check if we need to rotate the key (which pulls from entropy) + if (pCommon->iBytesLeft == 0) + { + bGetEntropySuccess &= (CryptRandCommonInit(pCommon) == 0); + } + + // get nonce from entropy source + bGetEntropySuccess &= (pCommon->GetEntropy(aNonce, sizeof(aNonce)) == 0); + + // abort if getting entropy failed + if (bGetEntropySuccess == FALSE) + { + /* in the case that getting bytes from the entropy pool fails, we have no other alternative + other than to abort. there is no secure fallback we could use and most notable software + prng will abort in this case. + in an attempt to prevent this case at all costs we make sure that this operation succeeds + in the initialize, otherwise the initialization fails which prevents the startup of + netconn. it is better that we fail then sacrifice the security guarentees of our software. */ + NetPrintf(("cryptrand: unable to pull bytes from entropy pool for encrypt; aborting\n")); + *(volatile uint8_t*)(0) = 0; + } + + // encrypt bytes to get our pseudo-random data + CryptChaChaEncrypt(&pCommon->Cipher, pBuffer, iBufSize, aNonce, sizeof(aNonce), NULL, 0, NULL, 0); + pCommon->iBytesLeft = (iBufSize >= pCommon->iBytesLeft) ? 0 : pCommon->iBytesLeft - iBufSize; +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandcommon.h b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandcommon.h new file mode 100644 index 00000000..e2107702 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandcommon.h @@ -0,0 +1,57 @@ +/*H********************************************************************************/ +/*! + \File cryptrandcommon.h + + \Description + Common APIs used for internally in the CryptRand module + + \Copyright + Copyright (c) 2019 Electronic Arts Inc. + + \Version 01/24/2019 (eesponda) +*/ +/********************************************************************************H*/ + +#ifndef _cryptrandcommon_h +#define _cryptrandcommon_h + +/*! +\Moduledef CryptRandCommon CryptRandCommon +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptchacha.h" + +/*** Type Definitions *************************************************************/ + +//! common state used for random number generation +typedef struct CryptRandCommonT +{ + CryptChaChaT Cipher; //!< cipher used to generate random numbers + int32_t iBytesLeft; //!< bytes remaining before key rotation + int32_t (*GetEntropy)(uint8_t *pBuffer, int32_t iBufSize); //!< pointer to internal function to get entropy +} CryptRandCommonT; + +/*** Functions ********************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// initialize the common state +int32_t CryptRandCommonInit(CryptRandCommonT *pCommon); + +// generate a psuedo-random number +void CryptRandCommonGet(CryptRandCommonT *pCommon, uint8_t *pBuffer, int32_t iBufSize); + +#if defined(__cplusplus) +} +#endif + +//@} + +#endif // _cryptrandcommon_h diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandpriv.h b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandpriv.h new file mode 100644 index 00000000..4d1d6677 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrandpriv.h @@ -0,0 +1,61 @@ +/*H********************************************************************************/ +/*! + \File cryptrandpriv.h + + \Description + Internal APIs for the CryptRand module + + \Copyright + Copyright (c) 2020 Electronic Arts Inc. + + \Version 02/04/2020 (eesponda) +*/ +/********************************************************************************H*/ + +#ifndef _cryptrandpriv_h +#define _cryptrandpriv_h + +/*! +\Moduledef CryptRandPriv CryptRandPriv +\Modulemember Crypt +*/ +//@{ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" + +/*** Defines **********************************************************************/ + +// define OS flavors of random +#if defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_ANDROID) +#define CRYPTRAND_LINUX +#elif defined(DIRTYCODE_APPLEOSX) || defined(DIRTYCODE_APPLEIOS) +#define CRYPTRAND_APPLE +#elif defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) +#define CRYPTRAND_WINDOWS +#elif defined(DIRTYCODE_PS4) || defined(DIRTYCODE_PS5) +#define CRYPTRAND_SONY +#elif defined(DIRTYCODE_NX) +#define CRYPTRAND_NX +#endif + +/*** Functions ********************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif + +// initialize the module +int32_t CryptRandInit(void); + +// destroy the module +void CryptRandShutdown(void); + +#if defined(__cplusplus) +} +#endif + +//@} + +#endif // _cryptrandpriv_h diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptrsa.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrsa.c new file mode 100644 index 00000000..90494d62 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptrsa.c @@ -0,0 +1,556 @@ +/*H********************************************************************************/ +/*! + \File cryptrsa.c + + \Description + This module is a from-scratch RSA implementation in order to avoid + any intellectual property issues. The 1024 bit RSA public key + encryption algorithm was implemented from a specification provided + by Netscape for SSL implementation (see protossl.h). + + \Copyright + Copyright (c) Electronic Arts 2002-2011. + + \Todo + 64bit word support (UCRYPT_SIZE=8) has been tested to work with the unix64-gcc + (Linux) and ps3-gcc (PS3) compilers; however the current implementation does + not work with certain odd modulus sizes (for example the 1000-bit modulus of + the built-in RSA CA certificate), so it is not currently enabled. + + \Version 1.0 03/08/2002 (gschaefer) First Version (protossl) + \Version 1.1 03/05/2003 (jbrookes) Split RSA encryption from protossl + \Version 1.2 11/16/2011 (jbrookes) Optimizations to improve dbg&opt performance +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include // memset + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/crypt/cryptrand.h" + +#include "DirtySDK/crypt/cryptrsa.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private variables + + +// Public variables + + +/*** Private functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _CryptRSAExponentiate + + \Description + Do the actual encryption: result = (value ^ exponent) % modulus. + Exponentiation is done using a sliding window, this function needs to + be called iteratively until the function returns zero. + + \Input *pState - crypt state + \Input *pAccumul - [out] where we accumulate our result + \Input *pPowerof - contains our initial value and is used for squaring + \Input *pExponent - the exponent of the equation + \Input *pModulus - the modulus of the equation + + \Output + int32_t - zero=done, else call again + + \Notes + See Handbook of Applied Cryptography Chapter 14.6.1 (14.85): + http://cacr.uwaterloo.ca/hac/about/chap14.pdf + + \Version 05/08/2017 (eesponda) +*/ +/********************************************************************************H*/ +static int32_t _CryptRSAExponentiate(CryptRSAT *pState, CryptBnT *pAccumul, CryptBnT *pPowerof, const CryptBnT *pExponent, const CryptBnT *pModulus) +{ + uint64_t uTickUsecs = NetTickUsec(); + int32_t iResult = 1; + int32_t iMaxExponentBit = CryptBnBitLen(pExponent); + /* use a fixed window size of 6 for larger exponents, + we provide a fallthrough for smaller exponents */ + const int32_t iWindowSize = iMaxExponentBit > 512 ? 6 : 1; + + // if this is the first time, handle our precomputations + if (pState->pTable == NULL) + { + const int32_t iTableSize = 1 << (iWindowSize - 1); + + if (iTableSize == 1) + { + // point to fixed array + pState->pTable = pState->aTable; + } + else + { + DirtyMemGroupQuery(&pState->iMemGroup, &pState->pMemGroupUserData); + + // allocate memory for the table to avoid large data on the stack + if ((pState->pTable = (CryptBnT *)DirtyMemAlloc(sizeof(CryptBnT) * iTableSize, CRYPTRSA_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("cryptrsa: [%p] unable to allocate memory for precomputed table used for sliding window\n", pState)); + return(0); + } + } + ds_memclr(pState->pTable, sizeof(CryptBnT) * iTableSize); + + /* put the already reduced input into the first + index of the table, this will make it so further + entries in the table will not be too large + for our modulus operations */ + CryptBnClone(&pState->pTable[0], pPowerof); + + /* precompute the different powers of input + that we use when we have a window that is + greater than one */ + if (iTableSize > 1) + { + int32_t iTableIndex; + CryptBnT Square; + + // calculate (input^2) % mod as the basis for the other calculations + CryptBnModMultiply(&Square, &pState->pTable[0], &pState->pTable[0], pModulus); + + // calculate (input^(2+iTableIndex)) % mod for the rest of the table + for (iTableIndex = 1; iTableIndex < iTableSize; iTableIndex += 1) + { + CryptBnModMultiply(&pState->pTable[iTableIndex], &pState->pTable[iTableIndex - 1], &Square, pModulus); + } + } + + // set the defaults + pState->iExpBitIndex = iMaxExponentBit - 1; + } + + /* scan backwards from the current exponent bit + until a set bit is found to denote the start of the window + squaring the results until such a bit is found */ + if (CryptBnBitTest(pExponent, pState->iExpBitIndex)) + { + int32_t iWindowBit, iWindowValue, iWindowEnd; + + /* scan backwards from the start of the window until the last set bit in the range of the window size is found which denotes the end bit index of the window + calculate the value of the window that will be used when multiplying against our precomputed table + we skip the first bit as we know it is set based on the current exponent bit check we make right above */ + for (iWindowBit = 1, iWindowValue = 1, iWindowEnd = 0; (iWindowBit < iWindowSize) && ((pState->iExpBitIndex - iWindowBit) >= 0); iWindowBit += 1) + { + if (CryptBnBitTest(pExponent, pState->iExpBitIndex - iWindowBit)) + { + iWindowValue <<= (iWindowBit - iWindowEnd); + iWindowValue |= 1; /* force odd */ + iWindowEnd = iWindowBit; + } + } + + // square for all the bits in the window and moving the exponent bit index down each time + for (iWindowBit = 0; iWindowBit < iWindowEnd + 1; iWindowBit += 1, pState->iExpBitIndex -= 1) + { + CryptBnModMultiply(pAccumul, pAccumul, pAccumul, pModulus); + } + + // use the window value to get which precomputed power we need to multiply skip the first multiply + if (!pState->bAccumulOne) + { + CryptBnModMultiply(pAccumul, pAccumul, &pState->pTable[iWindowValue/2], pModulus); + } + else + { + CryptBnClone(pAccumul, &pState->pTable[iWindowValue/2]); + pState->bAccumulOne = FALSE; + } + } + else + { + CryptBnModMultiply(pAccumul, pAccumul, pAccumul, pModulus); + pState->iExpBitIndex -= 1; + } + + // check if we are done + if (pState->iExpBitIndex < 0) + { + // we are done, update timings and return appropriate result + pState->uCryptMsecs = (pState->uCryptUsecs+500)/1000; + iResult = 0; + + if (pState->pTable != pState->aTable) + { + // clean up the table memory + DirtyMemFree(pState->pTable, CRYPTRSA_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + pState->pTable = NULL; + } + + // update timing + pState->uCryptUsecs += (uint32_t)NetTickDiff(NetTickUsec(), uTickUsecs); + pState->uNumExpCalls += 1; + + return(iResult); +} + + +/*F********************************************************************************/ +/*! + \Function _CryptRSAEncryptPublic + + \Description + Handle the exponentiate calls when using the public key data + + \Input *pState - crypt state + \Input iIter - number of iterations + + \Output + int32_t - zero=done, else call again + + \Version 04/11/2017 (eesponda) +*/ +/********************************************************************************H*/ +static int32_t _CryptRSAEncryptPublic(CryptRSAT *pState, int32_t iIter) +{ + int32_t iCount, iResult; + + for (iCount = 0, iResult = 1; (iCount < iIter) && (iResult > 0); iCount += 1) + { + iResult = _CryptRSAExponentiate(pState, &pState->Working.PublicKey.Accumul, &pState->Working.PublicKey.Powerof, &pState->Working.PublicKey.Exponent, &pState->Working.PublicKey.Modulus); + } + // if we are done, return encrypted data + if (iResult == 0) + { + CryptBnFinal(&pState->Working.PublicKey.Accumul, pState->EncryptBlock, pState->iKeyModSize); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _CryptRSAEncryptPrivate + + \Description + Handle the exponentiate calls when using the private key data which + using chinese remainder theorum + + \Input *pState - crypt state + \Input iIter - number of iterations + + \Output + int32_t - zero=done, else call again + + \Version 04/11/2017 (eesponda) +*/ +/********************************************************************************H*/ +static int32_t _CryptRSAEncryptPrivate(CryptRSAT *pState, int32_t iIter) +{ + int32_t iCount = 0, iResult = 1; + + if (pState->Working.PrivateKey.eState == CRT_COMPUTE_M1) + { + /* compute m1 + m1 = c ^ dP % p */ + for (; (iCount < iIter) && (iResult > 0); iCount += 1) + { + iResult = _CryptRSAExponentiate(pState, &pState->Working.PrivateKey.M1, &pState->Working.PrivateKey.PowerofP, &pState->Working.PrivateKey.ExponentP, &pState->Working.PrivateKey.PrimeP); + } + + if (iResult == 0) + { + // reset the state and move to next part of computation + pState->Working.PrivateKey.eState = CRT_COMPUTE_M2; + pState->bAccumulOne = TRUE; + pState->iExpBitIndex = 0; + iResult = 1; + } + } + if (pState->Working.PrivateKey.eState == CRT_COMPUTE_M2) + { + /* compute m2 + m2 = c ^ dQ % q */ + for (; (iCount < iIter) && (iResult > 0); iCount += 1) + { + iResult = _CryptRSAExponentiate(pState, &pState->Working.PrivateKey.M2, &pState->Working.PrivateKey.PowerofQ, &pState->Working.PrivateKey.ExponentQ, &pState->Working.PrivateKey.PrimeQ); + } + + if (iResult == 0) + { + CryptBnT Result; + + /* compute h + h = qInv * (m1 - m2) % p */ + CryptBnInit(&Result, 1); + CryptBnSubtract(&Result, &pState->Working.PrivateKey.M1, &pState->Working.PrivateKey.M2); + CryptBnModMultiply(&Result, &Result, &pState->Working.PrivateKey.Coeffecient, &pState->Working.PrivateKey.PrimeP); + + /* compute m + m = m2 + h * q */ + CryptBnMultiply(&Result, &Result, &pState->Working.PrivateKey.PrimeQ); + CryptBnAccumulate(&Result, &pState->Working.PrivateKey.M2); + + // return final result + CryptBnFinal(&Result, pState->EncryptBlock, pState->iKeyModSize); + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _CryptRSAInitEncrypt + + \Description + Initializes the common encrypt data and data depending on the type + of key + + \Input *pState - crypt state + + \Version 04/11/2017 (eesponda) +*/ +/********************************************************************************H*/ +static void _CryptRSAInitEncrypt(CryptRSAT *pState) +{ + // initialize accumulator to 1 (and remember for first-multiply optimization) + pState->bAccumulOne = TRUE; + + // convert data to encrypt to native word size we will operate in + if (!pState->bPrivate) + { + CryptBnInitFrom(&pState->Working.PublicKey.Powerof, -1, pState->EncryptBlock, pState->iKeyModSize); + } + else + { + /* we need to reduce before every operation to get the cipher + text to less than our modulus in the operations. + our modular multiply in bn doesn't handle the cases where they + are larger */ + CryptBnInitFrom(&pState->Working.PrivateKey.PowerofP, -1, pState->EncryptBlock, pState->iKeyModSize); + CryptBnInitFrom(&pState->Working.PrivateKey.PowerofQ, -1, pState->EncryptBlock, pState->iKeyModSize); + CryptBnMod(&pState->Working.PrivateKey.PowerofP, &pState->Working.PrivateKey.PrimeP, &pState->Working.PrivateKey.PowerofP, NULL); + CryptBnMod(&pState->Working.PrivateKey.PowerofQ, &pState->Working.PrivateKey.PrimeQ, &pState->Working.PrivateKey.PowerofQ, NULL); + } +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function CryptRSAInit + + \Description + Init RSA state. + + \Input *pState - module state + \Input *pModulus - crypto modulus + \Input iModSize - size of modulus + \Input *pExponent - crypto exponent + \Input iExpSize - size of exponent + + \Output + int32_t - zero for success, negative for error + + \Notes + This module supports a max modulus size of 4096 (iModSize=512) and requires + a minimum size of 1024 (iModSize=64). The exponent must be between 1 + and 512 bytes in size, inclusive. + + \Version 03/05/03 (jbrookes) Split from protossl +*/ +/********************************************************************************H*/ +int32_t CryptRSAInit(CryptRSAT *pState, const uint8_t *pModulus, int32_t iModSize, const uint8_t *pExponent, int32_t iExpSize) +{ + int32_t iResult = 0; + + // validate modulus size + if ((iModSize < 64) || ((iModSize/UCRYPT_SIZE) > CRYPTBN_MAX_WIDTH)) + { + NetPrintf(("cryptrsa: iModSize of %d is invalid\n", iModSize)); + return(-1); + } + // validate exponent size + if ((iExpSize < 1) || ((iExpSize/UCRYPT_SIZE) > CRYPTBN_MAX_WIDTH)) + { + NetPrintf(("cryptrsa: iExpSize of %d is invalid\n", iExpSize)); + return(-2); + } + + // initialize state + ds_memclr(pState, sizeof(*pState)); + CryptBnInitFrom(&pState->Working.PublicKey.Modulus, -1, pModulus, pState->iKeyModSize = iModSize); + CryptBnInitFrom(&pState->Working.PublicKey.Exponent, -1, pExponent, iExpSize); + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function CryptRSAInit2 + + \Description + Init RSA state for private key operations + + \Input *pState - module state + \Input iModSize - size of the public key modulus + \Input *pPrimeP - factor p of modulus + \Input *pPrimeQ - factor q of modulus + \Input *pExponentP - inverse of exponent mod p-1 + \Input *pExponentQ - inverse of exponent mod q-1 + \Input *pCoeffecient- inverse of q mod p + \Output + int32_t - zero for success, negative for error + + \Version 04/11/2017 (eesponda) +*/ +/********************************************************************************H*/ +int32_t CryptRSAInit2(CryptRSAT *pState, int32_t iModSize, const CryptBinaryObjT *pPrimeP, const CryptBinaryObjT *pPrimeQ, const CryptBinaryObjT *pExponentP, const CryptBinaryObjT *pExponentQ, const CryptBinaryObjT *pCoeffecient) +{ + // initialize state + ds_memclr(pState, sizeof(*pState)); + + // init state + CryptBnInitFrom(&pState->Working.PrivateKey.PrimeP, -1, pPrimeP->pObjData, pPrimeP->iObjSize); + CryptBnInitFrom(&pState->Working.PrivateKey.PrimeQ, -1, pPrimeQ->pObjData, pPrimeQ->iObjSize); + CryptBnInitFrom(&pState->Working.PrivateKey.ExponentP, -1, pExponentP->pObjData, pExponentP->iObjSize); + CryptBnInitFrom(&pState->Working.PrivateKey.ExponentQ, -1, pExponentQ->pObjData, pExponentQ->iObjSize); + CryptBnInitFrom(&pState->Working.PrivateKey.Coeffecient, -1, pCoeffecient->pObjData, pCoeffecient->iObjSize); + pState->iKeyModSize = iModSize; + pState->bPrivate = TRUE; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function CryptRSAInitMaster + + \Description + Setup the master shared secret for encryption + + \Input *pState - module state + \Input *pMaster - the master shared secret to encrypt + \Input iMasterLen - the length of the master shared secret + + \Version 02/07/2014 (jbrookes) Rewritten to use CryptRand +*/ +/********************************************************************************H*/ +void CryptRSAInitMaster(CryptRSAT *pState, const uint8_t *pMaster, int32_t iMasterLen) +{ + int32_t iIndex; + uint32_t uRandom; + + // fill encrypt block with random data + CryptRandGet(pState->EncryptBlock, pState->iKeyModSize); + /* As per PKCS1 http://www.emc.com/emc-plus/rsa-labs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf section 7.2.1, + the pseudo-random padding octets must be non-zero. Failing to adhere to this restriction (or any other errors in pkcs1 encoding) + will typically result in a bad_record_mac fatal alert from the remote host. The following code gets four random bytes as seed + and uses a simple Knuth RNG to fill in any zero bytes that were originally generated. */ + CryptRandGet((uint8_t *)&uRandom, sizeof(uRandom)); + for (iIndex = 0; iIndex < pState->iKeyModSize; iIndex += 1) + { + while (pState->EncryptBlock[iIndex] == 0) + { + uRandom = (uRandom * 69069) + 69069; + pState->EncryptBlock[iIndex] ^= (uint8_t)uRandom; + } + } + // set PKCS public key signature + pState->EncryptBlock[0] = 0; // zero pad + pState->EncryptBlock[1] = 2; // public key is type 2 + // add data to encrypt block + iIndex = pState->iKeyModSize - iMasterLen; + pState->EncryptBlock[iIndex-1] = 0; // zero byte prior to data + ds_memcpy(pState->EncryptBlock+iIndex, pMaster, iMasterLen); + // handle initializing the state according to the type of key + _CryptRSAInitEncrypt(pState); +} + +/*F********************************************************************************/ +/*! + \Function CryptRSAInitPrivate + + \Description + Setup the master shared secret for encryption using PKCS1.5 + + \Input *pState - module state + \Input *pMaster - the master shared secret to encrypt + \Input iMasterLen - the length of the master shared secret + + \Version 11/03/2013 (jbrookes) +*/ +/********************************************************************************H*/ +void CryptRSAInitPrivate(CryptRSAT *pState, const uint8_t *pMaster, int32_t iMasterLen) +{ + int32_t iIndex; + // put in PKCS1.5 signature + pState->EncryptBlock[0] = 0; + pState->EncryptBlock[1] = 1; + // PKCS1.5 signing pads with 0xff + ds_memset(pState->EncryptBlock+2, 0xff, pState->iKeyModSize-2); + // add data to encrypt block + iIndex = pState->iKeyModSize - iMasterLen; + pState->EncryptBlock[iIndex-1] = 0; // zero byte prior to data + ds_memcpy(pState->EncryptBlock+iIndex, pMaster, iMasterLen); + // handle initializing the state according to the type of key + _CryptRSAInitEncrypt(pState); +} + +/*F********************************************************************************/ +/*! + \Function CryptRSAInitSignature + + \Description + Setup the encrypted signature for decryption. + + \Input *pState - module state + \Input *pSig - the encrypted signature + \Input iSigLen - the length of the encrypted signature. + + \Version 03/03/2004 (sbevan) +*/ +/********************************************************************************H*/ +void CryptRSAInitSignature(CryptRSAT *pState, const uint8_t *pSig, int32_t iSigLen) +{ + ds_memcpy(pState->EncryptBlock, pSig, iSigLen); + // handle initializing the state according to the type of key + _CryptRSAInitEncrypt(pState); +} + +/*F********************************************************************************/ +/*! + \Function CryptRSAEncrypt + + \Description + Encrypt data. + + \Input *pState - module state + \Input iIter - number of iterations to execute (zero=do all) + + \Output + int32_t - zero=operation complete, else call again + + \Version 03/05/2003 (jbrookes) Split from protossl +*/ +/********************************************************************************H*/ +int32_t CryptRSAEncrypt(CryptRSAT *pState, int32_t iIter) +{ + if (iIter == 0) + { + iIter = 0x7fffffff; + } + + return((!pState->bPrivate) ? _CryptRSAEncryptPublic(pState, iIter) : _CryptRSAEncryptPrivate(pState, iIter)); +} + + diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptsha1.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptsha1.c new file mode 100644 index 00000000..0efd03e7 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptsha1.c @@ -0,0 +1,344 @@ +/*H*******************************************************************/ +/*! + \File cryptsha1.c + + \Description + This module implements SHA1 as defined in RFC 3174. + + \Notes + The implementation is based on the algorithm description in sections + 3 through 6 of RFC 3174 and not on the C code in section 7. + + Currently the code is a straightforward implementation of the + algorithm, no attempt has been made to optimize it in any way. + See the notes for individual functions for descriptions of where + optimizations are possible, but note that what is good for an x86 + with large caches may not be good for a MIPS based PS2 which has + a much smaller instruction cache. + + The code deliberately uses some of the naming conventions from + the RFC to in order to aid comprehension. + + This implementation is limited to hashing no more than 2^32-9 bytes. + It will silently produce the wrong result if an attempt is made to + hash more data. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 06/06/2004 (sbevan) First Version +*/ +/*******************************************************************H*/ + +/*** Include files ***************************************************/ + +#include /* memcpy */ + +#include "DirtySDK/platform.h" +#include "DirtySDK/crypt/cryptsha1.h" + +/*** Defines *********************************************************/ + +// A circular left shift, see RFC 3174 section 3.c +// +// gcc understands the two shift idiom and replaces this +// with single rotate instruction on platforms that have it. +// +#define CRYPTSHA1_rol(n, x) (((x)<<(n))|((x)>>(32-(n)))) + +/*** Type Definitions ************************************************/ + +/*** Variables *******************************************************/ + +/*** Private functions ***********************************************/ + +/*F*******************************************************************/ +/*! + \Function _CryptSha1CopyHash + + \Description + Extract the SHA1 hash and copy it to a byte buffer. + + \Input *pSha1 - SHA1 state + \Input *pBuffer - where to store the hash + \Input uLength - how many bytes of the hash to extract + + \Output None + + \Version 1.0 06/06/2004 (sbevan) First Version +*/ +/*******************************************************************F*/ +static void _CryptSha1CopyHash(CryptSha1T *pSha1, void *pBuffer, uint32_t uLength) +{ + unsigned char *pOutput = pBuffer; + uint32_t i; + + if (uLength > CRYPTSHA1_HASHSIZE) + { + uLength = CRYPTSHA1_HASHSIZE; + } + for (i = 0; i != uLength; i += 1) + { + pOutput[i] = pSha1->H[(i/4)]>>((3-(i%4))*8); + } +} + +/*F*******************************************************************/ +/*! + \Function _CryptSha1ProcessBlock + + \Description + SHA1 a 64-byte block of data. + + \Input *pSha1 - SHA1 state + \Input *M - start of 64-bytes to be processed + + \Output None + + \Notes + This is a literal translation of Method 1 as described in + RFC 3174 section 6.1. The variable names deliberately match those + used in that section in order to aid manual verification of algorithm + correctness. + + There is a lot of scope for performance improvements. For example :- + + a) performing manual loop unrolling and loop fusion. + An the extreme this could result in a single straight line + chunk of code that on a register rich machine this could + result in everything except the input and hash living in + a register. + + b) taking advantage of hardware that can perform big-endian (MIPS) + and/or mis-aligned (x86) loads. + + OpenSSL does a) but it causes problems for some compilers since + they don't do a good job of optimizing functions containing 80+ + local variables. GCC should be able to cope but the x86 and + MIPS assembly would need to be checked to make sure. + + \Version 1.0 06/06/2004 (sbevan) First Version +*/ +/*******************************************************************F*/ +static void _CryptSha1ProcessBlock(CryptSha1T *pSha1, const unsigned char *M) +{ + uint32_t i; + uint32_t t; + unsigned A, B, C, D, E; + uint32_t W[80]; + + // RFC 3174 section 6.1.a, divide input into 16 words (big-endian format) + for (i = 0; i != 16; i += 1) + { + W[i] = (M[i*4]<<24)|(M[i*4+1]<<16)|(M[i*4+2]<<8)|M[i*4+3]; + } + // RFC 3174 section 6.1.b + for (t = 16; t != 80; t += 1) + { + W[t] = CRYPTSHA1_rol(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + // RFC 3174 section 6.1.c + A = pSha1->H[0]; + B = pSha1->H[1]; + C = pSha1->H[2]; + D = pSha1->H[3]; + E = pSha1->H[4]; + + // RFC 3174 section 6.1.d split into 4 groups one for each variation of + // f as defined in RFC 3174 section 5. + for (t = 0; t != 20; t += 1) + { + uint32_t TEMP = CRYPTSHA1_rol(5, A) + ((B&C)|((~B)&D)) + E + W[t] + 0x5A827999; + E = D; D = C; C = CRYPTSHA1_rol(30, B); B = A; A = TEMP; + } + + for (t = 20; t != 40; t += 1) + { + uint32_t TEMP = CRYPTSHA1_rol(5, A) + (B^C^D) + E + W[t] + 0x6ED9EBA1; + E = D; D = C; C = CRYPTSHA1_rol(30, B); B = A; A = TEMP; + } + + for (t = 40; t != 60; t += 1) + { + uint32_t TEMP = CRYPTSHA1_rol(5, A) + ((B&C)|(B&D)|(C&D)) + E + W[t] + 0x8F1BBCDC; + E = D; D = C; C = CRYPTSHA1_rol(30, B); B = A; A = TEMP; + } + + for (t = 60; t != 80; t += 1) + { + uint32_t TEMP = CRYPTSHA1_rol(5, A) + (B^C^D) + E + W[t] + 0xCA62C1D6; + E = D; D = C; C = CRYPTSHA1_rol(30, B); B = A; A = TEMP; + } + + // Section 6.1.e + pSha1->H[0] += A; + pSha1->H[1] += B; + pSha1->H[2] += C; + pSha1->H[3] += D; + pSha1->H[4] += E; +} + + +/*** Public functions ************************************************/ + + +/*F*******************************************************************/ +/*! + \Function CryptSha1Init + + \Description + Hash the input and add it to the state + + \Input *pSha1 - SHA1 state + + \Output None + + \Version 1.0 06/06/2004 (sbevan) First Version +*/ +/*******************************************************************F*/ +void CryptSha1Init(CryptSha1T *pSha1) +{ + pSha1->uCount = 0; + pSha1->uPartialCount = 0; + // All the constants come from RFC 3174 section 6.1 + pSha1->H[0] = 0x67452301; + pSha1->H[1] = 0xEFCDAB89; + pSha1->H[2] = 0x98BADCFE; + pSha1->H[3] = 0x10325476; + pSha1->H[4] = 0xC3D2E1F0; +} + +/*F*******************************************************************/ +/*! + \Function CryptSha1Init2 + + \Description + Hash the input and add it to the state (alternate form) + + \Input *pSha1 - SHA1 state + \Input iHashSize - hash size (unused) + + \Version 11/05/2013 (jbrookes) +*/ +/*******************************************************************F*/ +void CryptSha1Init2(CryptSha1T *pSha1, int32_t iHashSize) +{ + CryptSha1Init(pSha1); +} + +/*F*******************************************************************/ +/*! + \Function CryptSha1Update + + \Description + Hash the input and add it to the state + + \Input *pSha1 - SHA1 state + \Input *pInput - the input + \Input uInputLen - length of input in bytes + + \Output None + + \Version 1.0 06/06/2004 (sbevan) First Version +*/ +/*******************************************************************F*/ +void CryptSha1Update(CryptSha1T *pSha1, const unsigned char *pInput, uint32_t uInputLen) +{ + if (pSha1->uPartialCount != 0) + { + uint32_t uWant = sizeof(pSha1->strData) - pSha1->uPartialCount; + uint32_t uHave = uWant > uInputLen ? uInputLen : uWant; + ds_memcpy(&pSha1->strData[pSha1->uPartialCount], pInput, uHave); + pInput += uHave; + uInputLen -= uHave; + if (uHave == uWant) + { + _CryptSha1ProcessBlock(pSha1, pSha1->strData); + pSha1->uCount += sizeof(pSha1->strData); + pSha1->uPartialCount = 0; + } + else + { + pSha1->uPartialCount += uHave; + } + } + while (uInputLen >= sizeof(pSha1->strData)) + { + _CryptSha1ProcessBlock(pSha1, pInput); + pSha1->uCount += sizeof(pSha1->strData); + uInputLen -= sizeof(pSha1->strData); + pInput += sizeof(pSha1->strData); + } + if (uInputLen != 0) + { + ds_memcpy(&pSha1->strData[pSha1->uPartialCount], pInput, uInputLen); + pSha1->uPartialCount += uInputLen; + } +} + +/*F*******************************************************************/ +/*! + \Function CryptSha1Final + + \Description + Generate the final hash from the SHA1 state + + \Input *pSha1 - the SHA1 state + \Input *pBuffer - where the hash should be written + \Input uLength - the number of bytes to write, [0..CRYPTSHA1_HASHSIZE] + + \Output none + + \Notes + Usually callers want the whole hash and so uOutputLen would be + CRYPTSHA1_HASHSIZE. However, if only a partial hash is needed then + any value up to CRYPTSHA1_HASHSIZE can be used. Any value + greater than CRYPTSHA1_HASHSIZE is silently truncated to + CRYPTSHA1_HASHSIZE. + + CryptSha1Final is not idempotent. It could easily be made so but + callers typically tend not to need it so it is not supported. + + \Version 1.0 06/06/2004 (sbevan) First Version +*/ +/*******************************************************************F*/ +void CryptSha1Final(CryptSha1T *pSha1, void *pBuffer, uint32_t uLength) +{ + uint32_t i; + uint8_t uPad = 0x80; + uint32_t uSpace = sizeof(pSha1->strData) - pSha1->uPartialCount; + + pSha1->uCount += pSha1->uPartialCount; + if (uSpace < 9) + { + pSha1->strData[pSha1->uPartialCount] = uPad; + for (i = pSha1->uPartialCount+1; i < sizeof(pSha1->strData); i += 1) + { + pSha1->strData[i] = 0x0; + } + _CryptSha1ProcessBlock(pSha1, pSha1->strData); + uPad = 0x0; + pSha1->uPartialCount = 0; + } + pSha1->strData[pSha1->uPartialCount] = uPad; + for (i = pSha1->uPartialCount+1; i < sizeof(pSha1->strData)-8; i += 1) + { + pSha1->strData[i] = 0x0; + } + + /* Append length in bits, as per RFC 3174 section 4.c except we only + support a 32-bit byte length / 35-bit bit length. Uses some + bit shifting to avoid having to explicitly calculate + pSha1->uCount*8 which could overflow 32-bits. */ + pSha1->strData[56] = 0; + pSha1->strData[57] = 0; + pSha1->strData[58] = 0; + pSha1->strData[59] = (pSha1->uCount>>(32-3))&0xFF; + pSha1->strData[60] = (pSha1->uCount>>(24-3))&0xFF; + pSha1->strData[61] = (pSha1->uCount>>(16-3))&0xFF; + pSha1->strData[62] = (pSha1->uCount>>(8-3))&0xFF; + pSha1->strData[63] = (pSha1->uCount<<3)&0xFF; + _CryptSha1ProcessBlock(pSha1, pSha1->strData); + + _CryptSha1CopyHash(pSha1, pBuffer, uLength); +} diff --git a/r5dev/thirdparty/dirtysdk/source/crypt/cryptsha2.c b/r5dev/thirdparty/dirtysdk/source/crypt/cryptsha2.c new file mode 100644 index 00000000..3647d67e --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/crypt/cryptsha2.c @@ -0,0 +1,513 @@ +/*H*******************************************************************/ +/*! + \File cryptsha2.c + + \Description + This module implements SHA2 as defined in RFC 6234, which is + itself based on FIPS 180-2. This implementation is modeled + after the CryptSha1 implementation by sbevan. + + \Notes + The implementation is based on the algorithm description in sections + 4 through 6 of RFC 6234 and not on the C code in section 8. + + Currently the code is a straightforward implementation of the + algorithm, no attempt has been made to optimize it in any way. + + The code deliberately uses some of the naming conventions from + the RFC to in order to aid comprehension. + + This implementation is limited to hashing no more than 2^32-9 + bytes. It will silently produce the wrong result if an attempt + is made to hash more data. + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 1.0 11/05/2013 (jbrookes) First Version +*/ +/*******************************************************************H*/ + +/*** Include files ***************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/crypt/cryptsha2.h" + +/*** Defines *********************************************************/ + +// definitions for functions and constants used: http://tools.ietf.org/html/rfc6234#section-5 +#define SHR(_n,_x) ((_x)>>(_n)) + +#define ROTR(_n,_x,_w) (((_x)>>(_n))|((_x)<<((_w)-(_n)))) +#define ROTL(_n,_x,_w) (((_x)<<(_n))|((_x)>>((_w)-(_n)))) + +#define CH(_x,_y,_z) (((_x)&(_y))^(~(_x)&(_z))) +#define MAJ(_x,_y,_z) (((_x)&(_y))^((_x)&(_z))^((_y)&(_z))) + +#define BSIG0_32(_x) (ROTR(2,_x,32)^ROTR(13,_x,32)^ROTR(22,_x,32)) +#define BSIG1_32(_x) (ROTR(6,_x,32)^ROTR(11,_x,32)^ROTR(25,_x,32)) +#define SSIG0_32(_x) (ROTR(7,_x,32)^ROTR(18,_x,32)^SHR(3,_x)) +#define SSIG1_32(_x) (ROTR(17,_x,32)^ROTR(19,_x,32)^SHR(10,_x)) + +#define BSIG0_64(_x) (ROTR(28,_x,64)^ROTR(34,_x,64)^ROTR(39,_x,64)) +#define BSIG1_64(_x) (ROTR(14,_x,64)^ROTR(18,_x,64)^ROTR(41,_x,64)) +#define SSIG0_64(_x) (ROTR(1,_x,64)^ROTR(8,_x,64)^SHR(7,_x)) +#define SSIG1_64(_x) (ROTR(19,_x,64)^ROTR(61,_x,64)^SHR(6,_x)) + +/*** Type Definitions ************************************************/ + +/*** Variables *******************************************************/ + +// constants defined in FIPS 180-3 section 4.2.2 +static const uint32_t K_32[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +// constants defined in FIPS 180-3 section 4.2.3 +static const uint64_t K_64[80] = +{ + 0x428A2F98D728AE22ull, 0x7137449123EF65CDull, 0xB5C0FBCFEC4D3B2Full, 0xE9B5DBA58189DBBCull, + 0x3956C25BF348B538ull, 0x59F111F1B605D019ull, 0x923F82A4AF194F9Bull, 0xAB1C5ED5DA6D8118ull, + 0xD807AA98A3030242ull, 0x12835B0145706FBEull, 0x243185BE4EE4B28Cull, 0x550C7DC3D5FFB4E2ull, + 0x72BE5D74F27B896Full, 0x80DEB1FE3B1696B1ull, 0x9BDC06A725C71235ull, 0xC19BF174CF692694ull, + 0xE49B69C19EF14AD2ull, 0xEFBE4786384F25E3ull, 0x0FC19DC68B8CD5B5ull, 0x240CA1CC77AC9C65ull, + 0x2DE92C6F592B0275ull, 0x4A7484AA6EA6E483ull, 0x5CB0A9DCBD41FBD4ull, 0x76F988DA831153B5ull, + 0x983E5152EE66DFABull, 0xA831C66D2DB43210ull, 0xB00327C898FB213Full, 0xBF597FC7BEEF0EE4ull, + 0xC6E00BF33DA88FC2ull, 0xD5A79147930AA725ull, 0x06CA6351E003826Full, 0x142929670A0E6E70ull, + 0x27B70A8546D22FFCull, 0x2E1B21385C26C926ull, 0x4D2C6DFC5AC42AEDull, 0x53380D139D95B3DFull, + 0x650A73548BAF63DEull, 0x766A0ABB3C77B2A8ull, 0x81C2C92E47EDAEE6ull, 0x92722C851482353Bull, + 0xA2BFE8A14CF10364ull, 0xA81A664BBC423001ull, 0xC24B8B70D0F89791ull, 0xC76C51A30654BE30ull, + 0xD192E819D6EF5218ull, 0xD69906245565A910ull, 0xF40E35855771202Aull, 0x106AA07032BBD1B8ull, + 0x19A4C116B8D2D0C8ull, 0x1E376C085141AB53ull, 0x2748774CDF8EEB99ull, 0x34B0BCB5E19B48A8ull, + 0x391C0CB3C5C95A63ull, 0x4ED8AA4AE3418ACBull, 0x5B9CCA4F7763E373ull, 0x682E6FF3D6B2B8A3ull, + 0x748F82EE5DEFB2FCull, 0x78A5636F43172F60ull, 0x84C87814A1F0AB72ull, 0x8CC702081A6439ECull, + 0x90BEFFFA23631E28ull, 0xA4506CEBDE82BDE9ull, 0xBEF9A3F7B2C67915ull, 0xC67178F2E372532Bull, + 0xCA273ECEEA26619Cull, 0xD186B8C721C0C207ull, 0xEADA7DD6CDE0EB1Eull, 0xF57D4F7FEE6ED178ull, + 0x06F067AA72176FBAull, 0x0A637DC5A2C898A6ull, 0x113F9804BEF90DAEull, 0x1B710B35131C471Bull, + 0x28DB77F523047D84ull, 0x32CAAB7B40C72493ull, 0x3C9EBE0A15C9BEBCull, 0x431D67C49C100D4Cull, + 0x4CC5D4BECB3E42B6ull, 0x597F299CFC657E2Aull, 0x5FCB6FAB3AD6FAECull, 0x6C44198C4A475817ull +}; + + +/*** Private functions ***********************************************/ + + +/*F*******************************************************************/ +/*! + \Function _CryptSha2CopyHash224_256 + + \Description + Extract the SHA2 hash and copy it to a byte buffer. + + \Input *pSha2 - SHA2 state + \Input *pBuffer - where to store the hash + \Input uLength - how many bytes of the hash to extract + + \Version 11/04/2013 (jbrookes) +*/ +/*******************************************************************F*/ +static void _CryptSha2CopyHash224_256(CryptSha2T *pSha2, uint8_t *pBuffer, uint32_t uLength) +{ + uint32_t uByte; + + if (uLength > pSha2->uHashSize) + { + uLength = pSha2->uHashSize; + } + for (uByte = 0; uByte != uLength; uByte += 1) + { + pBuffer[uByte] = pSha2->TempHash.H_32[(uByte/4)]>>((3-(uByte%4))*8); + } +} + +/*F*******************************************************************/ +/*! + \Function _CryptSha2CopyHash384_512 + + \Description + Extract the SHA2 hash and copy it to a byte buffer. + + \Input *pSha2 - SHA2 state + \Input *pBuffer - where to store the hash + \Input uLength - how many bytes of the hash to extract + + \Version 11/04/2013 (jbrookes) +*/ +/*******************************************************************F*/ +static void _CryptSha2CopyHash384_512(CryptSha2T *pSha2, uint8_t *pBuffer, uint32_t uLength) +{ + uint32_t uByte; + + if (uLength > pSha2->uHashSize) + { + uLength = pSha2->uHashSize; + } + for (uByte = 0; uByte != uLength; uByte += 1) + { + pBuffer[uByte] = pSha2->TempHash.H_64[(uByte/8)]>>((7-(uByte%8))*8); + } +} + +/*F*******************************************************************/ +/*! + \Function _CryptSha2ProcessBlock224_256 + + \Description + SHA2 a 64-byte block of data using 224 or 256 bit version. + + \Input *pSha2 - SHA2 state + \Input *M - start of 64-bytes to be processed + + \Notes + This is a literal translation of the method described in RFC + 6234 section 6.2. The variable names deliberately match those + used in that section in order to aid manual verification of + algorithm correctness. + + \Version 11/04/2013 (jbrookes) +*/ +/*******************************************************************F*/ +static void _CryptSha2ProcessBlock224_256(CryptSha2T *pSha2, const uint8_t *M) +{ + unsigned a, b, c, d, e, f, g, h; + uint32_t W[64]; + uint32_t t; + unsigned T1, T2; + + // RFC 6234 section 6.2: http://tools.ietf.org/html/rfc6234#section-6.2 + for (t = 0; t < 16; t += 1) + { + W[t] = (M[t*4]<<24)|(M[t*4+1]<<16)|(M[t*4+2]<<8)|M[t*4+3]; + } + for (t = 16; t < 64; t += 1) + { + W[t] = SSIG1_32(W[t-2]) + W[t-7] + SSIG0_32(W[t-15]) + W[t-16]; + } + + // section 6.2.2 + a = pSha2->TempHash.H_32[0]; + b = pSha2->TempHash.H_32[1]; + c = pSha2->TempHash.H_32[2]; + d = pSha2->TempHash.H_32[3]; + e = pSha2->TempHash.H_32[4]; + f = pSha2->TempHash.H_32[5]; + g = pSha2->TempHash.H_32[6]; + h = pSha2->TempHash.H_32[7]; + + // section 6.1.3 + for (t = 0; t < 64; t += 1) + { + T1 = h + BSIG1_32(e) + CH(e, f, g) + K_32[t] + W[t]; + T2 = BSIG0_32(a) + MAJ(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + // section 6.1.4 + pSha2->TempHash.H_32[0] += a; + pSha2->TempHash.H_32[1] += b; + pSha2->TempHash.H_32[2] += c; + pSha2->TempHash.H_32[3] += d; + pSha2->TempHash.H_32[4] += e; + pSha2->TempHash.H_32[5] += f; + pSha2->TempHash.H_32[6] += g; + pSha2->TempHash.H_32[7] += h; +} + +/*F*******************************************************************/ +/*! + \Function _CryptSha2ProcessBlock384_512 + + \Description + SHA2 a 64-byte block of data using 384 or 512 bit version + + \Input *pSha2 - SHA2 state + \Input *M - start of 64-bytes to be processed + + \Notes + This is a literal translation of the method described in RFC + 6234 section 6.4. The variable names deliberately match those + used in that section in order to aid manual verification of + algorithm correctness. + + \Version 11/04/2013 (jbrookes) +*/ +/*******************************************************************F*/ +static void _CryptSha2ProcessBlock384_512(CryptSha2T *pSha2, const uint8_t *M) +{ + uint64_t a, b, c, d, e, f, g, h; + uint64_t T1, T2; + uint64_t W[80]; + uint32_t t; + + // RFC 6234 section 6.4: http://tools.ietf.org/html/rfc6234#section-6.4 + for (t = 0; t < 16; t += 1) + { + W[t] = ((uint64_t)M[t*8+0]<<56)|((uint64_t)M[t*8+1]<<48)|((uint64_t)M[t*8+2]<<40)|((uint64_t)M[t*8+3]<<32)| + ((uint64_t)M[t*8+4]<<24)|((uint64_t)M[t*8+5]<<16)|((uint64_t)M[t*8+6]<<8)|(uint64_t)M[t*8+7]; + } + for (t = 16; t < 80; t += 1) + { + W[t] = SSIG1_64(W[t-2]) + W[t-7] + SSIG0_64(W[t-15]) + W[t-16]; + } + + // section 6.4.2 + a = pSha2->TempHash.H_64[0]; + b = pSha2->TempHash.H_64[1]; + c = pSha2->TempHash.H_64[2]; + d = pSha2->TempHash.H_64[3]; + e = pSha2->TempHash.H_64[4]; + f = pSha2->TempHash.H_64[5]; + g = pSha2->TempHash.H_64[6]; + h = pSha2->TempHash.H_64[7]; + + // section 6.4.3 + for (t = 0; t < 80; t += 1) + { + T1 = h + BSIG1_64(e) + CH(e, f, g) + K_64[t] + W[t]; + T2 = BSIG0_64(a) + MAJ(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + // section 6.4.4 + pSha2->TempHash.H_64[0] += a; + pSha2->TempHash.H_64[1] += b; + pSha2->TempHash.H_64[2] += c; + pSha2->TempHash.H_64[3] += d; + pSha2->TempHash.H_64[4] += e; + pSha2->TempHash.H_64[5] += f; + pSha2->TempHash.H_64[6] += g; + pSha2->TempHash.H_64[7] += h; +} + + +/*** Public functions ************************************************/ + + +/*F*******************************************************************/ +/*! + \Function CryptSha2Init + + \Description + Initialize SHA2 state based on mode + + \Input *pSha2 - SHA2 state + \Input iHashSize - hash size (CRYPTSHA*_HASHSIZE) + + \Version 11/04/2013 (jbrookes) +*/ +/*******************************************************************F*/ +void CryptSha2Init(CryptSha2T *pSha2, int32_t iHashSize) +{ + pSha2->uCount = 0; + pSha2->uPartialCount = 0; + pSha2->uHashSize = (uint8_t)iHashSize; + pSha2->uBlockSize = (pSha2->uHashSize < CRYPTSHA384_HASHSIZE) ? 64 : 128; + + // all the constants come from RFC 6234 section 6.1 + if (pSha2->uHashSize == CRYPTSHA224_HASHSIZE) + { + pSha2->TempHash.H_32[0] = 0xc1059ed8; + pSha2->TempHash.H_32[1] = 0x367cd507; + pSha2->TempHash.H_32[2] = 0x3070dd17; + pSha2->TempHash.H_32[3] = 0xf70e5939; + pSha2->TempHash.H_32[4] = 0xffc00b31; + pSha2->TempHash.H_32[5] = 0x68581511; + pSha2->TempHash.H_32[6] = 0x64f98fa7; + pSha2->TempHash.H_32[7] = 0xbefa4fa4; + } + else if (pSha2->uHashSize == CRYPTSHA256_HASHSIZE) + { + pSha2->TempHash.H_32[0] = 0x6a09e667; + pSha2->TempHash.H_32[1] = 0xbb67ae85; + pSha2->TempHash.H_32[2] = 0x3c6ef372; + pSha2->TempHash.H_32[3] = 0xa54ff53a; + pSha2->TempHash.H_32[4] = 0x510e527f; + pSha2->TempHash.H_32[5] = 0x9b05688c; + pSha2->TempHash.H_32[6] = 0x1f83d9ab; + pSha2->TempHash.H_32[7] = 0x5be0cd19; + } + else if (pSha2->uHashSize == CRYPTSHA384_HASHSIZE) + { + pSha2->TempHash.H_64[0] = 0xcbbb9d5dc1059ed8; + pSha2->TempHash.H_64[1] = 0x629a292a367cd507; + pSha2->TempHash.H_64[2] = 0x9159015a3070dd17; + pSha2->TempHash.H_64[3] = 0x152fecd8f70e5939; + pSha2->TempHash.H_64[4] = 0x67332667ffc00b31; + pSha2->TempHash.H_64[5] = 0x8eb44a8768581511; + pSha2->TempHash.H_64[6] = 0xdb0c2e0d64f98fa7; + pSha2->TempHash.H_64[7] = 0x47b5481dbefa4fa4; + } + else if (pSha2->uHashSize == CRYPTSHA512_HASHSIZE) + { + pSha2->TempHash.H_64[0] = 0x6a09e667f3bcc908; + pSha2->TempHash.H_64[1] = 0xbb67ae8584caa73b; + pSha2->TempHash.H_64[2] = 0x3c6ef372fe94f82b; + pSha2->TempHash.H_64[3] = 0xa54ff53a5f1d36f1; + pSha2->TempHash.H_64[4] = 0x510e527fade682d1; + pSha2->TempHash.H_64[5] = 0x9b05688c2b3e6c1f; + pSha2->TempHash.H_64[6] = 0x1f83d9abfb41bd6b; + pSha2->TempHash.H_64[7] = 0x5be0cd19137e2179; + } + else + { + NetPrintf(("cryptsha2: invalid hashsize %d\n", pSha2->uHashSize)); + } +} + +/*F*******************************************************************/ +/*! + \Function CryptSha2Update + + \Description + Hash the input and add it to the state + + \Input *pSha2 - SHA2 state + \Input *pInput - the input + \Input uInputLen - length of input in bytes + + \Version 11/04/2013 (jbrookes) +*/ +/*******************************************************************F*/ +void CryptSha2Update(CryptSha2T *pSha2, const uint8_t *pInput, uint32_t uInputLen) +{ + if (pSha2->uPartialCount != 0) + { + uint32_t uWant = pSha2->uBlockSize - pSha2->uPartialCount; + uint32_t uHave = uWant > uInputLen ? uInputLen : uWant; + ds_memcpy(&pSha2->strData[pSha2->uPartialCount], pInput, uHave); + pInput += uHave; + uInputLen -= uHave; + if (uHave == uWant) + { + pSha2->uHashSize < CRYPTSHA384_HASHSIZE ? _CryptSha2ProcessBlock224_256(pSha2, pSha2->strData) : _CryptSha2ProcessBlock384_512(pSha2, pSha2->strData); + pSha2->uCount += pSha2->uBlockSize; + pSha2->uPartialCount = 0; + } + else + { + pSha2->uPartialCount += uHave; + } + } + while (uInputLen >= pSha2->uBlockSize) + { + pSha2->uHashSize < CRYPTSHA384_HASHSIZE ? _CryptSha2ProcessBlock224_256(pSha2, pInput) : _CryptSha2ProcessBlock384_512(pSha2, pInput); + pSha2->uCount += pSha2->uBlockSize; + uInputLen -= pSha2->uBlockSize; + pInput += pSha2->uBlockSize; + } + if (uInputLen != 0) + { + ds_memcpy(&pSha2->strData[pSha2->uPartialCount], pInput, uInputLen); + pSha2->uPartialCount += uInputLen; + } +} + +/*F*******************************************************************/ +/*! + \Function CryptSha2Final + + \Description + Generate the final hash from the SHA2 state + + \Input *pSha2 - the SHA2 state + \Input *pBuffer - [out] where the hash should be written + \Input uLength - the number of bytes to write (may be less than hash size) + + \Version 11/04/2013 (jbrookes) +*/ +/*******************************************************************F*/ +void CryptSha2Final(CryptSha2T *pSha2, uint8_t *pBuffer, uint32_t uLength) +{ + uint32_t uByte, uSpace = pSha2->uBlockSize - pSha2->uPartialCount; + const uint32_t uLengthSize = (pSha2->uHashSize < CRYPTSHA384_HASHSIZE) ? 8 : 16; + uint8_t uPad = 0x80; + + pSha2->uCount += pSha2->uPartialCount; + if (uSpace < (uLengthSize+1)) + { + pSha2->strData[pSha2->uPartialCount] = uPad; + for (uByte = pSha2->uPartialCount+1; uByte < pSha2->uBlockSize; uByte += 1) + { + pSha2->strData[uByte] = 0x0; + } + pSha2->uHashSize < CRYPTSHA384_HASHSIZE ? _CryptSha2ProcessBlock224_256(pSha2, pSha2->strData) : _CryptSha2ProcessBlock384_512(pSha2, pSha2->strData); + uPad = 0x0; + pSha2->uPartialCount = 0; + } + pSha2->strData[pSha2->uPartialCount] = uPad; + for (uByte = pSha2->uPartialCount+1; uByte < (uint32_t)pSha2->uBlockSize-uLengthSize; uByte += 1) + { + pSha2->strData[uByte] = 0x0; + } + + /* append length in bits, as per RFC 6234 section 4 except we only support a 32-bit byte + length (35-bit bit length). uses some bit shifting to avoid having to explicitly calculate + pSha1->uCount*8 which could overflow 32-bits */ + if (uLengthSize == 16) + { + pSha2->strData[pSha2->uBlockSize-16] = 0; + pSha2->strData[pSha2->uBlockSize-15] = 0; + pSha2->strData[pSha2->uBlockSize-14] = 0; + pSha2->strData[pSha2->uBlockSize-13] = 0; + pSha2->strData[pSha2->uBlockSize-12] = 0; + pSha2->strData[pSha2->uBlockSize-11] = 0; + pSha2->strData[pSha2->uBlockSize-10] = 0; + pSha2->strData[pSha2->uBlockSize- 9] = 0; + } + pSha2->strData[pSha2->uBlockSize-8] = 0; + pSha2->strData[pSha2->uBlockSize-7] = 0; + pSha2->strData[pSha2->uBlockSize-6] = 0; + pSha2->strData[pSha2->uBlockSize-5] = (pSha2->uCount>>(32-3))&0xFF; + pSha2->strData[pSha2->uBlockSize-4] = (pSha2->uCount>>(24-3))&0xFF; + pSha2->strData[pSha2->uBlockSize-3] = (pSha2->uCount>>(16-3))&0xFF; + pSha2->strData[pSha2->uBlockSize-2] = (pSha2->uCount>>(8-3))&0xFF; + pSha2->strData[pSha2->uBlockSize-1] = (pSha2->uCount<<3)&0xFF; + + // process final block, and copy the hash to final buffer + if (pSha2->uHashSize < CRYPTSHA384_HASHSIZE) + { + _CryptSha2ProcessBlock224_256(pSha2, pSha2->strData); + _CryptSha2CopyHash224_256(pSha2, pBuffer, uLength); + } + else + { + _CryptSha2ProcessBlock384_512(pSha2, pSha2->strData); + _CryptSha2CopyHash384_512(pSha2, pBuffer, uLength); + } +} + + + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyaddr.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyaddr.c new file mode 100644 index 00000000..6721c43b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyaddr.c @@ -0,0 +1,135 @@ +/*H********************************************************************************/ +/*! + \File dirtyaddr.c + + \Description + Opaque address functions. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 04/09/2004 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyaddr.h" +#include "DirtySDK/dirtysock/netconn.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function DirtyAddrToHostAddr + + \Description + Convert from DirtyAddrT to native format. + + \Input *pOutput - [out] storage for native format address + \Input iBufLen - length of output buffer + \Input *pAddr - source address to convert + + \Output + uint32_t - number of characters consumed from input, or zero on failure + + \Notes + On common platforms, a DirtyAddrT is a string-encoded network-order address + field. This function converts that input to a binary-encoded host-order + address. + + \Version 04/09/2004 (jbrookes) +*/ +/********************************************************************************F*/ +uint32_t DirtyAddrToHostAddr(void *pOutput, int32_t iBufLen, const DirtyAddrT *pAddr) +{ + uint32_t uAddress; + + // make sure our buffer is big enough + if (iBufLen < (signed)sizeof(uAddress)) + { + NetPrintf(("dirtyaddr: output buffer too small\n")); + return(0); + } + + // convert from string to int32_t + uAddress = (uint32_t)strtoul(&pAddr->strMachineAddr[1], NULL, 16); + + // convert from network order to host order + uAddress = SocketNtohl(uAddress); + + // copy to output + ds_memcpy(pOutput, &uAddress, sizeof(uAddress)); + return(9); +} + +/*F********************************************************************************/ +/*! + \Function DirtyAddrFromHostAddr + + \Description + Convert from native format to DirtyAddrT. + + \Input *pAddr - [out] storage for output DirtyAddrT + \Input *pInput - pointer to native format address + + \Output + uint32_t - TRUE if successful, else FALSE + + \Notes + On common platforms, a DirtyAddrT is a string-encoded network-order address + field. This function converts a binary-encoded host-order address to that + format. + + \Version 04/09/2004 (jbrookes) +*/ +/********************************************************************************F*/ +uint32_t DirtyAddrFromHostAddr(DirtyAddrT *pAddr, const void *pInput) +{ + uint32_t uAddress; + + // make sure output buffer is okay + if ((pInput == NULL) || (((intptr_t)pInput & 0x3) != 0)) + { + return(FALSE); + } + + uAddress = SocketHtonl(*(const uint32_t *)pInput); + ds_snzprintf(pAddr->strMachineAddr, sizeof(pAddr->strMachineAddr), "$%08x", uAddress); + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function DirtyAddrGetLocalAddr + + \Description + Get the local address in DirtyAddr form. + + \Input *pAddr - [out] storage for output DirtyAddrT + + \Output + uint32_t - TRUE if successful, else FALSE + + \Version 09/29/2004 (jbrookes) +*/ +/********************************************************************************F*/ +uint32_t DirtyAddrGetLocalAddr(DirtyAddrT *pAddr) +{ + uint32_t uLocalAddr = NetConnStatus('addr', 0, NULL, 0); + DirtyAddrFromHostAddr(pAddr, &uLocalAddr); + return(TRUE); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtycert.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtycert.c new file mode 100644 index 00000000..f07c7e3d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtycert.c @@ -0,0 +1,1239 @@ +/*H**************************************************************************/ +/*! + + \File dirtycert.c + + \Description + This module defines the CA fallback mechanism which is used by ProtoSSL. + + \Notes + This module is designed to be thread-safe except the creating/destroying APIs, + however because some other code (such as CA list) are not thread-safe yet, + so it's not totally thread-safe right now. + If two protossl instances are requesting the same CA cert at the same time, + ref-counting is used and only one CA fetch request will be made to the server. + + \Copyright + Copyright (c) Electronic Arts 2012. + + \Version 01/23/2012 (szhu) +*/ +/***************************************************************************H*/ + +/*** Include files ***********************************************************/ + +#include + +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/crypt/cryptarc4.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/xml/xmlparse.h" +#include "DirtySDK/util/base64.h" + +#include "DirtySDK/dirtysock/dirtycert.h" + +/*** Defines ***************************************************************************/ + +#define DIRTYCERT_MAXREQUESTS (16) //!< max concurrent CA requests +#define DIRTYCERT_TIMEOUT (30*1000) //!< default dirtycert timeout +#define DIRTYCERT_MAXURL (2048) //!< max url length +#define DIRTYCERT_MAXCERTIFICATE (8*1024) //!< max certificate size, in bytes +#define DIRTYCERT_MAXRESPONSE ((DIRTYCERT_MAXCERTIFICATE)*3) //! max response size, in bytes +#define DIRTYCERT_MAXCERTDECODED ((Base64DecodedSize(DIRTYCERT_MAXCERTIFICATE)+3)&0x7ffc) //!< max certificate size (decoded), in bytes +#define DIRTYCERT_REQUESTID_NONE (-1) //!< request id none +#define DIRTYCERT_VERSION (0x0101) //!< dirtycert version: update this for major bug fixes or protocol additions/changes + +/* dirtycert production url; NOTE: this url is encrypted as stored in the binary to prevent aa + malicious user from being able to use an easy string search to find and possibly modify it. + if the url is changed, _DirtyCertUrlSetup() will complain and spit out new enc/key parameters + for the updated url, which must be used to replace the current definitions in that function. */ +#define DIRTYCERT_URL "https://gosca18.ea.com:4432%d/redirector" +#define DIRTYCERT_URL_ENCRYPTED (TRUE) //!< TRUE if the dirtycert URL is specified in encrypted form; DO NOT CHECK IN DISABLED + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + #define DIRTYCERT_PORT_OFFSET (6) //!< xbox platform offset is +1 from other platforms +#else + #define DIRTYCERT_PORT_OFFSET (5) //!< default platform offset is +5 from the base port number of 44320 +#endif + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! request type +typedef enum RequestTypeE +{ + RT_ONDEMAND = 0, + RT_PREFETCH +} RequestTypeE; + +//! request status +typedef enum RequestStatusE +{ + RS_NONE = 0, + RS_NOT_STARTED, + RS_IN_PROGRESS, + RS_DONE, + RS_FAILED, +} RequestStatusE; + +//! CA fetch request +typedef struct DirtyCertCARequestT +{ + ProtoSSLCertInfoT CertInfo; //!< certificate info (on-demand request only) + char strHost[256]; //!< host we are making an on-demand request for (on-demand request only) + int32_t iPort; //!< port we are making an on-demand request for (on-demand request only) + RequestTypeE eType; //!< request type + RequestStatusE eStatus; //!< request status + int32_t iRefCount; //!< ref count for this request +}DirtyCertCARequestT; + +//! module state +struct DirtyCertRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + NetCritT crit; //!< critical section used to ensure thread safety + ProtoHttpRefT *pHttp; //!< http ref + + char strServiceName[DIRTYCERT_SERVICENAME_SIZE]; //!< service name used to identify requester (required) + char strUrl[DIRTYCERT_MAXURL]; //!< url buffer + + // buffer for response processing + char aResponseBuf[DIRTYCERT_MAXRESPONSE]; //!< buffer for http response + char aCertBuf[DIRTYCERT_MAXCERTIFICATE]; //!< buffer for a single certificate + char aCertDecodedBuf[DIRTYCERT_MAXCERTDECODED]; //!< buffer for a single certificate (decoded) + uint8_t bPreload; //!< TRUE if we are to execute preload, else FALSE + + uint32_t uTimeout; //!< request timeout + int32_t iRequestId; //!< current on-going request (==index in request list) + int32_t iCount; //!< count of valid requests + DirtyCertCARequestT requests[DIRTYCERT_MAXREQUESTS]; //!< request list +}; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +//! module state ref +static DirtyCertRefT *_DirtyCert_pState = NULL; + +//! dirtycert url +#if DIRTYCERT_URL_ENCRYPTED + static char _DirtyCert_strRedirectorUrl[64]; +#else + #if 1 + static const char *_DirtyCert_strRedirectorUrl = DIRTYCERT_URL; + #else // dev redirector, use only for testing, do NOT check in enabled + #error DO NOT CHECK THIS IN ENABLED!! + static const char *_DirtyCert_strRedirectorUrl = "http://gosredirector.online.ea.com:42125/redirector"; + #endif +#endif + +// Public variables + +/*** Private functions ******************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _DirtyCertUrlSetup + + \Description + Decrypt encrypted DirtyCert URL; also used to generate encrypted URL + + \Output + int32_t - 0=success, negative=failure + + \Version 06/04/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyCertUrlSetup(void) +{ +#if DIRTYCERT_URL_ENCRYPTED + static const uint8_t strKey[] = + { + 0x67,0x9A,0xE3,0x11, 0x6E,0x4C,0xFD,0xA2, 0x6D,0xC8,0x2D,0xF8, 0x5B,0x22,0xF1,0x40, + 0x40,0xE8,0x92,0x22, 0x8A,0x96,0x75,0x2B, 0x64,0x53,0xC6,0x8D, 0xED,0xA0,0xF9,0x21, + }; + static const uint8_t strEnc[] = + { + 0x25,0xEB,0x51,0x91, 0x6C,0x78,0xCB,0xF2, 0x2F,0xEA,0xE7,0x56, 0x37,0x96,0xE2,0x7C, + 0x49,0x8C,0x19,0x75, 0x59,0xBD,0xD8,0x3D, 0x39,0x81,0x91,0xE5, 0x9B,0x6C,0xE7,0x29, + 0x2B,0x72,0xB7,0x0A, 0x76,0x60,0x49,0x04, 0x90,0x7F,0xDB,0x54, 0xFD,0xC1,0x5E,0x2E, + 0xDB,0x37,0xF8,0x77, 0x21,0x06,0xDD,0x4E, 0xCD,0xCB,0xA3,0xF9, 0x87,0x74,0xDE,0x64, + }; + char strUrlDecrypt[sizeof(_DirtyCert_strRedirectorUrl)]; + ds_memcpy_s(strUrlDecrypt, sizeof(strUrlDecrypt), strEnc, sizeof(strEnc)); + if (CryptArc4StringEncryptStatic(strUrlDecrypt, sizeof(strUrlDecrypt), strKey, sizeof(strKey), DIRTYCERT_URL) < 0) + { + return(-1); + } + // url has the port offset as a format paramter, so we fill it in here + ds_snzprintf(_DirtyCert_strRedirectorUrl, sizeof(_DirtyCert_strRedirectorUrl), strUrlDecrypt, DIRTYCERT_PORT_OFFSET); + return(0); +#else + return(0); +#endif +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _DirtyCertCAGetStrIdent + + \Description + Debug-only function to get a string identifier of the request, for debug printing + + \Input *pState - dirtycert state + \Input *pRequest - request to get string identifier for + + \Output + const char * - pointer to string ident + + \Version 04/18/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_DirtyCertCAGetStrIdent(DirtyCertRefT *pState, DirtyCertCARequestT *pRequest) +{ + static char _strIdent[512]; + if (pRequest->eType == RT_ONDEMAND) + { + ds_snzprintf(_strIdent, sizeof(_strIdent), "[%s:%s:%d]", pRequest->CertInfo.Ident.strCommon, pRequest->CertInfo.Ident.strUnit, pRequest->CertInfo.iKeyModSize); + } + else + { + ds_snzprintf(_strIdent, sizeof(_strIdent), "[%s]", pState->strServiceName); + } + return(_strIdent); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _DirtyCertUrlEncodeStrParm + + \Description + Wrapper for ProtoHttpUrlEncodeStrParm() that won't encode a null string. + + \Input *pBuffer - output buffer to encode into + \Input iLength - length of output buffer + \Input *pParm - parameter name (not encoded) + \Input *pData - parameter data + + \Version 04/18/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _DirtyCertUrlEncodeStrParm(char *pBuffer, int32_t iLength, const char *pParm, const char *pData) +{ + if (*pData != '\0') + { + ProtoHttpUrlEncodeStrParm(pBuffer, iLength, pParm, pData); + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyCertCompareCertInfo + + \Description + Compare two cert infos. + + \Input *pCertInfo1 - the cert info to be compared + \Input *pCertInfo2 - the cert info to be compared + + \Output int32_t - zero=match, non-zero=no-match + + \Version 01/23/2012 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _DirtyCertCompareCertInfo(const ProtoSSLCertInfoT *pCertInfo1, const ProtoSSLCertInfoT *pCertInfo2) +{ + if ((pCertInfo1->iKeyModSize != pCertInfo2->iKeyModSize) + || strcmp(pCertInfo1->Ident.strCountry, pCertInfo2->Ident.strCountry) + || strcmp(pCertInfo1->Ident.strState, pCertInfo2->Ident.strState) + || strcmp(pCertInfo1->Ident.strCity, pCertInfo2->Ident.strCity) + || strcmp(pCertInfo1->Ident.strOrg, pCertInfo2->Ident.strOrg) + || strcmp(pCertInfo1->Ident.strCommon, pCertInfo2->Ident.strCommon) + || strcmp(pCertInfo1->Ident.strUnit, pCertInfo2->Ident.strUnit)) + { + return(1); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyCertValidateServiceName + + \Description + Validate that specified servicename is valid. + + \Input *pServiceName - servicename to validate + + \Output + int32_t - negative=invalid, else valid + + \Version 03/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyCertValidateServiceName(const char *pServiceName) +{ + if (*pServiceName == '\0') + { + return(-1); + } + else + { + return(0); + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyCertSetServiceName + + \Description + Format a request into provided buffer. + + \Input *pState - module ref + \Input *pName - service name to set (game-year-platform or game) + + \Version 03/19/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static void _DirtyCertSetServiceName(DirtyCertRefT *pState, const char *pName) +{ + char strServiceName[sizeof(pState->strServiceName)]; + if (!strchr(pName, '-')) + { + ds_snzprintf(strServiceName, sizeof(strServiceName), "%s-%d-%s", pName, 2000+DIRTYSDK_VERSION_YEAR, DIRTYCODE_PLATNAME_SHORT); + } + else + { + ds_strnzcpy(strServiceName, pName, sizeof(strServiceName)); + } + if (strcmp(pState->strServiceName, strServiceName)) + { + ds_strnzcpy(pState->strServiceName, strServiceName, sizeof(pState->strServiceName)); + NetPrintf(("dirtycert: servicename set to '%s'\n", pState->strServiceName)); + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyCertFormatRequestUrl + + \Description + Format a request into provided buffer. + + \Input *pState - module ref + \Input *pRequest - CA request + \Input *pBuf - user-supplied url buffer + \Input iBufLen - size of buffer pointed to by pBuf + + \Version 01/23/2012 (szhu) +*/ +/********************************************************************************F*/ +static void _DirtyCertFormatRequestUrl(DirtyCertRefT *pState, DirtyCertCARequestT *pRequest, char *pBuf, int32_t iBufLen) +{ + // table of "safe" characters + // 0=hex encode, non-zero=direct encode, @-O=valid hex digit (&15 to get value) + static char _DirtyCert_strSafe[256] = + "0000000000000000" "0000000000000000" // 0x00 - 0x1f + "0000000000000110" "@ABCDEFGHI000000" // 0x20 - 0x3f (allow period, dash, and digits) + "0JKLMNO111111111" "1111111111100000" // 0x40 - 0x5f (allow uppercase) + "0JKLMNO111111111" "1111111111100000" // 0x60 - 0x7f (allow lowercase) + "0000000000000000" "0000000000000000" // 0x80 - 0x9f + "0000000000000000" "0000000000000000" // 0xa0 - 0xbf + "0000000000000000" "0000000000000000" // 0xc0 - 0xdf + "0000000000000000" "0000000000000000"; // 0xe0 - 0xff} + char strTemp[32]; + + // create URL and attach DirtySDK version + ds_snzprintf(pBuf, iBufLen, "%s/%s", _DirtyCert_strRedirectorUrl, (pRequest->eType == RT_ONDEMAND) ? "findCACertificates" : "getCACertificates"); + + // append dirtysdk version + ds_snzprintf(strTemp, sizeof(strTemp), "%d.%d.%d.%d.%d", DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH); + ProtoHttpUrlEncodeStrParm2(pBuf, iBufLen, "?v=", strTemp, _DirtyCert_strSafe); + + // append dirtycert version + ProtoHttpUrlEncodeIntParm(pBuf, iBufLen, "&vers=", DIRTYCERT_VERSION); + + // append servicename + ProtoHttpUrlEncodeStrParm2(pBuf, iBufLen, "&name=", pState->strServiceName, _DirtyCert_strSafe); + + /* add additional on-demand parameters; for params name and format, refer to: + blazeserver\dev3\component\redirector\gen\redirectortypes.tdf */ + if (pRequest->eType == RT_ONDEMAND) + { + // append required parameters + ProtoHttpUrlEncodeStrParm2(pBuf, iBufLen, "&host=", pRequest->strHost, _DirtyCert_strSafe); + ProtoHttpUrlEncodeIntParm(pBuf, iBufLen, "&port=", pRequest->iPort); + ProtoHttpUrlEncodeIntParm(pBuf, iBufLen, "&bits=", pRequest->CertInfo.iKeyModSize * 8); + // append parameters identifying required CA (empty fields are not included) + _DirtyCertUrlEncodeStrParm(pBuf, iBufLen, "&entr|CN=", pRequest->CertInfo.Ident.strCommon); + _DirtyCertUrlEncodeStrParm(pBuf, iBufLen, "&entr|C=", pRequest->CertInfo.Ident.strCountry); + _DirtyCertUrlEncodeStrParm(pBuf, iBufLen, "&entr|O=", pRequest->CertInfo.Ident.strOrg); + _DirtyCertUrlEncodeStrParm(pBuf, iBufLen, "&entr|OU=", pRequest->CertInfo.Ident.strUnit); + _DirtyCertUrlEncodeStrParm(pBuf, iBufLen, "&entr|L=", pRequest->CertInfo.Ident.strCity); + _DirtyCertUrlEncodeStrParm(pBuf, iBufLen, "&entr|ST=", pRequest->CertInfo.Ident.strState); + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyCertCreateRequest + + \Description + Issue a CA fetch request. + + \Input *pState - module ref + \Input iRequestId - CA request id + \Input *pRequest - CA request + + \Output int32_t - 0 for success + + \Version 01/23/2012 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _DirtyCertCreateRequest(DirtyCertRefT *pState, int32_t iRequestId, DirtyCertCARequestT *pRequest) +{ + // if we're not busy + if (pState->iRequestId == DIRTYCERT_REQUESTID_NONE) + { + ds_memclr(pState->strUrl, sizeof(pState->strUrl)); + // format request url + _DirtyCertFormatRequestUrl(pState, pRequest, pState->strUrl, sizeof(pState->strUrl)); + // set timeout + ProtoHttpControl(pState->pHttp, 'time', pState->uTimeout, 0, NULL); + // if pre-load set keep-alive + if (pRequest->eType == RT_PREFETCH) + { + ProtoHttpControl(pState->pHttp, 'keep', 1, 0, NULL); + } + // http GET + NetPrintf(("dirtycert: making CA request: %s\n", pState->strUrl)); + if (ProtoHttpGet(pState->pHttp, pState->strUrl, 0) >= 0) + { + pState->iRequestId = iRequestId; + pRequest->eStatus = RS_IN_PROGRESS; + } + else + { + // failed? + pRequest->eStatus = RS_FAILED; + NetPrintf(("dirtycert: ProtoHttpGet(%p, %s) failed.\n", pState->pHttp, pState->strUrl)); + } + } + + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function _DirtyCertCARequestFree + + \Description + Release resources used by a CA fetch request. + + \Input *pState - module state + \Input *pRequest - request ptr + \Input iRequestId - request id + + \Output + int32_t - negative=error, else success + + \Version 04/18/2012 (jbrookes) +*/ +/************************************************************************************F*/ +static int32_t _DirtyCertCARequestFree(DirtyCertRefT *pState, DirtyCertCARequestT *pRequest, int32_t iRequestId) +{ + int32_t iResult = 0; + + if (pRequest->iRefCount <= 0) + { + // error request id? + iResult = -3; + } + else if (--pRequest->iRefCount == 0) + { + // if this is an active request, abort it + if ((pState->iRequestId == iRequestId) && (pState->iRequestId != -1)) + { + ProtoHttpAbort(pState->pHttp); + pState->iRequestId = DIRTYCERT_REQUESTID_NONE; + } + NetPrintf(("dirtycert: freeing CA fetch request for %s\n", _DirtyCertCAGetStrIdent(pState, pRequest))); + ds_memclr(pRequest, sizeof(*pRequest)); + pRequest->eStatus = RS_NONE; + // if no queued requests, close connection + if (--pState->iCount == 0) + { + ProtoHttpControl(pState->pHttp, 'disc', 0, 0, NULL); + } + // success + iResult = 1; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyCertProcessResponse + + \Description + Process response. + + \Input *pState - module ref + \Input *pResponse - http response + \Input iLen - length of response + \Input *pFailed - [out] storage for number of CA certificate loads that failed + + \Output int32_t - positive=installed certs, 0 or negative=error + + \Version 01/24/2012 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _DirtyCertProcessResponse(DirtyCertRefT *pState, const char *pResponse, int32_t iLen, int32_t *pFailed) +{ + int32_t iCount = 0, iCertLen, iResult; + const char *pCert; + + *pFailed = 0; + + // for response format, refer to: blazeserver\dev3\component\redirector\gen\redirectortypes.tdf + pCert = XmlFind(pResponse, "cacertificate.certificatelist.certificatelist"); + while (pCert) + { + ds_memclr(pState->aCertBuf, sizeof(pState->aCertBuf)); + if ((iCertLen = XmlContentGetString(pCert, pState->aCertBuf, sizeof(pState->aCertBuf), "")) > 0) + { + // the cert response might be base64 encoded + char sEncoding[32]; + ds_memclr(sEncoding, sizeof(sEncoding)); + XmlAttribGetString(pCert, "enc", sEncoding, sizeof(sEncoding), ""); + // base64 encoded, decode and install the cert + if (ds_stricmp(sEncoding, "base64") == 0) + { + ds_memclr(pState->aCertDecodedBuf, sizeof(pState->aCertDecodedBuf)); + if (Base64Decode(iCertLen, pState->aCertBuf, pState->aCertDecodedBuf)) + { + if ((iResult = ProtoSSLSetCACert((uint8_t *)pState->aCertDecodedBuf, (int32_t)strlen((const char*)pState->aCertDecodedBuf))) >= 0) + { + iCount += iResult; + } + else + { + *pFailed += 1; + } + } + } + else + { + // plain, install the cert + if ((iResult = ProtoSSLSetCACert((uint8_t *)pState->aCertBuf, iCertLen)) > 0) + { + iCount += iResult; + } + else + { + *pFailed += 1; + } + } + } + pCert = XmlNext(pCert); + } + return(iCount); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyCertUpdate + + \Description + Update status of DirtyCert module. + + \Input *pData - pointer to DirtyCert module ref + + \Notes + This is be the only private 'static' function that needs to acquire crit + because it's actually called from NetIdle thread. + + \Version 01/23/2012 (szhu) +*/ +/********************************************************************************F*/ +static void _DirtyCertUpdate(void *pData) +{ + DirtyCertRefT *pState = (DirtyCertRefT*)pData; + DirtyCertCARequestT *pRequest; + + /* attempt to acquire critical section. there is dependency between this crit + and the idle crit, we want to prevent a deadlock in those conditions */ + if (!NetCritTry(&pState->crit)) + { + return; + } + + if (pState->iRequestId != DIRTYCERT_REQUESTID_NONE) + { + pRequest = &pState->requests[pState->iRequestId]; + + if (pRequest->eStatus == RS_IN_PROGRESS) + { + int32_t iHttpStatus; + // update http + ProtoHttpUpdate(pState->pHttp); + // if we've done with current http request + if ((iHttpStatus = ProtoHttpStatus(pState->pHttp, 'done', NULL, 0)) > 0) + { + // check http response code, we only deal with "20X OK" + if ((ProtoHttpStatus(pState->pHttp, 'code', NULL, 0) / 100) == 2) + { + int32_t iRecvLen, iFailed; + // zero buf + ds_memclr(pState->aResponseBuf, sizeof(pState->aResponseBuf)); + if ((iRecvLen = ProtoHttpRecvAll(pState->pHttp, (char*)pState->aResponseBuf, sizeof(pState->aResponseBuf))) > 0) + { + // install received certs + iRecvLen = _DirtyCertProcessResponse(pState, pState->aResponseBuf, iRecvLen, &iFailed); + pRequest->eStatus = RS_DONE; + pState->iRequestId = DIRTYCERT_REQUESTID_NONE; + NetPrintf(("dirtycert: %d cert(s) installed for %s (%d failed)\n", iRecvLen, + _DirtyCertCAGetStrIdent(pState, pRequest), iFailed)); + } + } + + // unknown error? (ex. http 500 error) + if (pRequest->eStatus != RS_DONE) + { + iHttpStatus = -1; + NetPrintf(("dirtycert: CA fetch request failed (httpcode=%d)\n", ProtoHttpStatus(pState->pHttp, 'code', NULL, 0))); + } + } + else if (iHttpStatus == 0) + { + // if we're missing CA cert for our http ref, we fail. + if (ProtoHttpStatus(pState->pHttp, 'cfip', NULL, 0) > 0) + { + // mark request as failed but don't reset pState->iRequestId otherwise a new request may be issued + pRequest->eStatus = RS_FAILED; + NetPrintf(("dirtycert: CA fetch request failed because of no CA available for redirector: %s\n", _DirtyCert_strRedirectorUrl)); + } + } + + // if we failed (or timed-out) + if (iHttpStatus < 0) + { + pRequest->eStatus = RS_FAILED; + pState->iRequestId = DIRTYCERT_REQUESTID_NONE; + NetPrintf(("dirtycert: CA fetch request for %s %s\n", _DirtyCertCAGetStrIdent(pState, pRequest), + ProtoHttpStatus(pState->pHttp, 'time', NULL, 0) ? "timeout" : "failed")); + } + + // prefetch requests are executed only once, and automatically clean up after themselves upon completion + if ((pRequest->eType == RT_PREFETCH) && (iHttpStatus != 0)) + { + pState->bPreload = FALSE; + _DirtyCertCARequestFree(pState, pRequest, pState->iRequestId); + } + } + } + + // if we need to issue a new request? + if ((pState->iRequestId == DIRTYCERT_REQUESTID_NONE) && (pState->iCount > 0)) + { + int32_t iRequestId; + for (iRequestId = 0; iRequestId < DIRTYCERT_MAXREQUESTS; iRequestId++) + { + pRequest = &pState->requests[iRequestId]; + if ((pRequest->iRefCount > 0) && (pRequest->eStatus == RS_NOT_STARTED)) + { + if (_DirtyCertCreateRequest(pState, iRequestId, pRequest) == 0) + { + break; + } + } + } + } + + // release critical section + NetCritLeave(&pState->crit); +} + + +/*** Public functions ********************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function DirtyCertCreate + + \Description + Startup DirtyCert module. + + \Output int32_t - 0 for success, negative for error + + \Version 01/23/2012 (szhu) +*/ +/*************************************************************************************************F*/ +int32_t DirtyCertCreate(void) +{ + DirtyCertRefT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + if (_DirtyCert_pState != NULL) + { + NetPrintf(("dirtycert: DirtyCertCreate() called while module is already active\n")); + return(-1); + } + + // decrypt url + if (_DirtyCertUrlSetup() < 0) + { + NetPrintf(("dirtycert: error setting up url; dirtycert startup failure\n")); + return(-2); + } + + // query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pState = DirtyMemAlloc(sizeof(*pState), DIRTYCERT_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtycert: could not allocate module state\n")); + return(-3); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + pState->bPreload = TRUE; + + // create http ref + if ((pState->pHttp = ProtoHttpCreate(DIRTYCERT_MAXRESPONSE)) == NULL) + { + DirtyMemFree(pState, DIRTYCERT_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(-4); + } + // no request + pState->iRequestId = DIRTYCERT_REQUESTID_NONE; + + // set request timeout + pState->uTimeout = DIRTYCERT_TIMEOUT; + + // init crit + NetCritInit(&pState->crit, "DirtyCert"); + + // add update function to netidle handler + NetIdleAdd(_DirtyCertUpdate, pState); + + // save module ref + _DirtyCert_pState = pState; + + NetPrintf(("dirtycert: created dirtycert 0x%p (pHttp=%p)\n", pState, pState->pHttp)); + // return module state + return(0); +} + +/*F************************************************************************/ +/*! + \Function DirtyCertDestroy + + \Description + Destroy the module and release its state. + + \Output int32_t - 0 for success, negative for error + + \Version 01/23/2012 (szhu) +*/ +/*************************************************************************F*/ +int32_t DirtyCertDestroy(void) +{ + DirtyCertRefT *pState = _DirtyCert_pState; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtycert: DirtyCertDestroy() called while module is not active\n")); + return(-1); + } + + // remove idle handler + NetIdleDel(_DirtyCertUpdate, pState); + + // clear global module ref + _DirtyCert_pState = NULL; + + // destroy http ref + ProtoHttpDestroy(pState->pHttp); + + // destroy crit + NetCritKill(&pState->crit); + + // free the memory + DirtyMemFree(pState, DIRTYCERT_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + NetPrintf(("dirtycert: freed dirtycert %p\n", pState)); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function DirtyCertCARequestCert + + \Description + Initialize a CA fetch request. + + \Input *pCertInfo - CA Cert Info + \Input *pHost - host we are fetching CA for + \Input iPort - port we are fetching CA for + + \Output + int32_t - positive=id of request, negative=error + + \Version 01/23/2012 (szhu) +*/ +/********************************************************************************F*/ +int32_t DirtyCertCARequestCert(const ProtoSSLCertInfoT *pCertInfo, const char *pHost, int32_t iPort) +{ + DirtyCertRefT *pState = _DirtyCert_pState; + int32_t i, iSlot = -1; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtycert: DirtyCertCARequestCert() called while module is not active\n")); + return(-1); + } + + // acquire critical section + NetCritEnter(&pState->crit); + + // require a valid service name + if (_DirtyCertValidateServiceName(pState->strServiceName) < 0) + { + NetCritLeave(&pState->crit); + return(-2); + } + + // if we haven't successfully executed pre-load yet, slot in a pre-load request first + if (pState->bPreload) + { + DirtyCertCAPreloadCerts(pState->strServiceName); + } + + // check if it matches any in-progress request + for (i = 0; i < DIRTYCERT_MAXREQUESTS; i++) + { + // if this is an empty slot + if (pState->requests[i].iRefCount <= 0) + { + // mark it + if (iSlot < 0) + { + iSlot = i; + } + continue; + } + + // we found an in-progress request with the same cert info + if (_DirtyCertCompareCertInfo(pCertInfo, &pState->requests[i].CertInfo) == 0) + { + iSlot = i; + break; + } + } + + if (iSlot >= 0) + { + DirtyCertCARequestT *pRequest = &pState->requests[iSlot]; + // a CA request for this cert is already queued + if (pRequest->iRefCount > 0) + { + // inc refcount + pRequest->iRefCount++; + NetPrintf(("dirtycert: CA fetch request %s already queued (status=%d), updated refcount to %d.\n", + _DirtyCertCAGetStrIdent(pState, pRequest), (int32_t)pRequest->eStatus, pRequest->iRefCount)); + } + // no matching request, create a new one + else + { + ds_memclr(pRequest, sizeof(*pRequest)); + // set request type and status + pRequest->eType = RT_ONDEMAND; + pRequest->eStatus = RS_NOT_STARTED; + // save host info + ds_strnzcpy(pRequest->strHost, pHost, sizeof(pRequest->strHost)); + pRequest->iPort = iPort; + // save cert info + ds_memcpy_s(&pRequest->CertInfo, sizeof(pRequest->CertInfo), pCertInfo, sizeof(*pCertInfo)); + // set refcount + pRequest->iRefCount = 1; + pState->iCount++; + NetPrintf(("dirtycert: queued CA fetch request for %s\n", _DirtyCertCAGetStrIdent(pState, pRequest))); + _DirtyCertCreateRequest(pState, iSlot, pRequest); + } + // public slot reference is +1 to reserve zero + iSlot += 1; + } + else + { + NetPrintf(("dirtycert: too many CA fetch requests.\n")); + } + + // release critical section + NetCritLeave(&pState->crit); + + // return slot ref to caller + return(iSlot); +} + +/*F********************************************************************************/ +/*! + \Function DirtyCertCAPreloadCerts + + \Description + Initialize a CA preload request + + \Input *pServiceName - servicename to identify CA preload set ("gamename-gameyear-platform") + + \Notes + Unlike the explicit CA load, a preload request is fire and forget, and + will clean up after itself when complete. + + \Version 04/18/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyCertCAPreloadCerts(const char *pServiceName) +{ + DirtyCertRefT *pState = _DirtyCert_pState; + int32_t i, iSlot = -1; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtycert: DirtyCertCARequestCert() called while module is not active\n")); + return; + } + + // require a valid servicename + if (_DirtyCertValidateServiceName(pServiceName) < 0) + { + return; + } + + // acquire critical section + NetCritEnter(&pState->crit); + + // if there is already a prefetch queued, ignore this request + for (i = 0; i < DIRTYCERT_MAXREQUESTS; i++) + { + if ((pState->requests[i].iRefCount > 0) && (pState->requests[i].eType == RT_PREFETCH)) + { + NetPrintf(("dirtycert: CA prefetch request already queued; request ignored\n")); + NetCritLeave(&pState->crit); + return; + } + } + + // save service name (required for all request types) + _DirtyCertSetServiceName(pState, pServiceName); + + // find an empty slot + for (i = 0; i < DIRTYCERT_MAXREQUESTS; i++) + { + // if this is an empty slot + if (pState->requests[i].iRefCount <= 0) + { + // mark it + if (iSlot < 0) + { + iSlot = i; + break; + } + } + } + + if (iSlot >= 0) + { + DirtyCertCARequestT *pRequest = &pState->requests[iSlot]; + + ds_memclr(pRequest, sizeof(*pRequest)); + // set request type and status + pRequest->eType = RT_PREFETCH; + pRequest->eStatus = RS_NOT_STARTED; + // set refcount + pRequest->iRefCount = 1; + pState->iCount++; + + NetPrintf(("dirtycert: queued CA prefetch request for %s\n", _DirtyCertCAGetStrIdent(pState, pRequest))); + _DirtyCertCreateRequest(pState, iSlot, pRequest); + } + else + { + NetPrintf(("dirtycert: too many CA fetch requests.\n")); + } + + // release critical section + NetCritLeave(&pState->crit); +} + +/*F*************************************************************************************/ +/*! + \Function DirtyCertCARequestDone + + \Description + Determine if a CA fetch request is complete. + + \Input iRequestId - id of CA request + + \Output + int32_t - zero=in progess, neg=done w/error, pos=done w/success + + \Version 01/23/2012 (szhu) +*/ +/************************************************************************************F*/ +int32_t DirtyCertCARequestDone(int32_t iRequestId) +{ + DirtyCertRefT *pState = _DirtyCert_pState; + DirtyCertCARequestT *pRequest; + int32_t iResult = 0; + + // internal request id is public id - 1 + iRequestId -= 1; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtycert: DirtyCertCARequestDone() called while module is not active\n")); + return(-1); + } + // if it's a valid request id + if ((iRequestId < 0) || (iRequestId >= DIRTYCERT_MAXREQUESTS)) + { + NetPrintf(("dirtycert: invalid request id (%d)\n", iRequestId)); + return(-2); + } + + // acquire critical section + NetCritEnter(&pState->crit); + + pRequest = &pState->requests[iRequestId]; + if (pRequest->iRefCount <= 0) + { + // error request id? + iResult = -3; + } + else if (pRequest->eStatus == RS_FAILED) + { + iResult = -4; + } + else if (pRequest->eStatus == RS_DONE) + { + iResult = 1; + } + + // release critical section + NetCritLeave(&pState->crit); + + return(iResult); +} + +/*F*************************************************************************************/ +/*! + \Function DirtyCertCARequestFree + + \Description + Release resources used by a CA fetch request. + + \Input iRequestId - id of CA request + + \Output int32_t - negative=error, else success + + \Version 01/23/2012 (szhu) +*/ +/************************************************************************************F*/ +int32_t DirtyCertCARequestFree(int32_t iRequestId) +{ + DirtyCertRefT *pState = _DirtyCert_pState; + int32_t iResult; + + // internal request id is public id - 1 + iRequestId -= 1; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtycert: DirtyCertCARequestFree() called while module is not active\n")); + return(-1); + } + // if it's a valid request id + if ((iRequestId < 0) || (iRequestId >= DIRTYCERT_MAXREQUESTS)) + { + NetPrintf(("dirtycert: invalid request id (%d)\n", iRequestId)); + return(-2); + } + + // acquire critical section + NetCritEnter(&pState->crit); + + // free the request + iResult = _DirtyCertCARequestFree(pState, &pState->requests[iRequestId], iRequestId); + + // release critical section + NetCritLeave(&pState->crit); + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function DirtyCertControl + + \Description + Set module behavior based on input selector. + + \Input iControl - input selector + \Input iValue - selector input + \Input iValue2 - selector input + \Input *pValue - selector input + + \Output + int32_t - selector result + + \Notes + iControl can be one of the following: + + \verbatim + SELECTOR DESCRIPTION + 'prld' Enables/disables preload + 'snam' Sets service name to use for on-demand requests + 'time' Sets CA request timeout in milliseconds (default=30s) + \endverbatim + + \Version 01/24/2012 (szhu) +*/ +/********************************************************************************F*/ +int32_t DirtyCertControl(int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + DirtyCertRefT *pState = _DirtyCert_pState; + int32_t iResult = -100; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtycert: DirtyCertControl() called while module is not active\n")); + return(-1); + } + + // acquire critical section + NetCritEnter(&pState->crit); + + // set preload enable/disable + if (iControl == 'prld') + { + NetPrintf(("dirtycert: %s CA preload\n", (iValue != 0) ? "enabling" : "disabling")); + pState->bPreload = (iValue != 0) ? TRUE : FALSE; + iResult = 0; + } + // set service name + if (iControl == 'snam') + { + _DirtyCertSetServiceName(pState, (const char *)pValue); + iResult = 0; + } + // set timeout + if (iControl == 'time') + { + NetPrintf(("dirtycert: setting timeout to %d ms\n", iValue)); + pState->uTimeout = (unsigned)iValue; + iResult = 0; + } + + // release critical section + NetCritLeave(&pState->crit); + + // unhandled? + if (iResult == -100) + { + NetPrintf(("dirtycert: unhandled control option '%C'\n", iControl)); + iResult = -1; + } + + // return control result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function DirtyCertStatus + + \Description + Get module status + + \Input iStatus - info selector (see notes) + \Input *pBuffer - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector result + + \Notes + iStatus can be one of the following: + + \verbatim + SELECTOR DESCRIPTION + 'snam' Stores service name in output buffer (if not NULL); returns validity + \endverbatim + + \Version 03/25/2013 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyCertStatus(int32_t iStatus, void *pBuffer, int32_t iBufSize) +{ + DirtyCertRefT *pState = _DirtyCert_pState; + int32_t iResult = -100; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtycert: DirtyCertStatus() called while module is not active\n")); + return(-1); + } + + // acquire critical section + NetCritEnter(&pState->crit); + + // get service name, return whether it is valid or not + if (iStatus == 'snam') + { + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, pState->strServiceName, iBufSize); + } + iResult = _DirtyCertValidateServiceName(pState->strServiceName); + } + + // release critical section + NetCritLeave(&pState->crit); + + // unhandled? + if (iResult == -100) + { + NetPrintf(("dirtycert: unhandled status option '%C'\n", iStatus)); + iResult = -1; + } + + // return control result to caller + return(iResult); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyerr.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyerr.c new file mode 100644 index 00000000..41dadb9d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyerr.c @@ -0,0 +1,105 @@ +/*H********************************************************************************/ +/*! + \File dirtyerr.c + + \Description + Dirtysock platform independent debug error routines. + + \Copyright + Copyright (c) 2014 Electronic Arts Inc. + + \Version 09/16/2014 (cvienneau) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyerr.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function DirtyErrGetHResult + + \Description + create a unique error code for use across DirtySDK + + \Input uFacility - the module id the hResult is being generated for (only bottom 11 bits) + \Input iCode - the error code + \Input bFailure - true if the code represents a failure + + \Output uint32_t - hResult value + + \Notes + \verbatim + Description of HRESULT: + http://msdn.microsoft.com/en-us/library/cc231198.aspx + Bit [ 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 09, 08, 07 06, 05, 04, 03, 02, 01, 00] + Field[ S, R, C, N, X, - - - - - - - - - - - FACILITY - - - - - , - - - - - - - - - - - - - - - - CODE - - - - - - - - - - - - ] + S - Severity - indicates success/fail; 0 - Success, 1 - Failure + R - Reserved portion of the facility code, corresponds to NT's second severity bit; 1 - Severe Failure + C - Customer. This bit specifies if the value is customer-defined or Microsoft-defined; 0 - Microsoft-defined, 1 - Customer-defined + N - Reserved portion of the facility code. Used to indicate a mapped NT status value. + X - Reserved portion of the facility code. Reserved for internal use. Used to indicate HRESULT values that are not status values, but are instead message ids for display strings. + FACILITY - indicates the system service that is responsible for the error. + CODE - is the facility's status code + \endverbatim + \Version 09/16/2014 (cvienneau) +*/ +/********************************************************************************F*/ +uint32_t DirtyErrGetHResult(uint16_t uFacility, int16_t iCode, uint8_t bFailure) +{ + uint32_t hResult = 0; + + if (bFailure) + { + hResult |= 0x80000000; //set the "Severity" bit, usually we use an hResult to describe failures so this is the usual case + } + hResult |= 0x20000000; //set the "Customer" bit, we aren't MS so we'll always set this + uFacility &= ~(0xF800); //the top 5 bits of the facility passed in do not belong to the user, we will clear them out so they don't mess up the upper bits (we could maybe assert this) + hResult |= (uFacility << 16); //set the "FACILITY", the module producing the error + hResult |= (uint16_t)iCode; //set the "CODE", the error id + return(hResult); +} + +/*F********************************************************************************/ +/*! + \Function DirtyErrDecodeHResult + + \Description + break a hresult back into its components + + \Input hResult - the hresult to be decoded + \Input pFacility - return the module id the hResult is being generated for + \Input pCode - return the error code + \Input pCustomer - return true if the customer bit is set (note DS will always set the customer bit in DirtyErrGetHResult). + \Input pFailure - return true if the code represents a failure + + \Version 01/17/2017 (cvienneau) +*/ +/********************************************************************************F*/ +void DirtyErrDecodeHResult(uint32_t hResult, uint16_t* pFacility, int16_t* pCode, uint8_t* pCustomer, uint8_t* pFailure) +{ + if (pFailure != NULL) + *pFailure = (hResult & 0x80000000) >> 31; + + if (pCustomer != NULL) + *pCustomer = (hResult & 0x20000000) >> 29; + + if (pFacility != NULL) + *pFacility = (hResult & 0x7FF0000) >> 16; + + if (pCode != NULL) + *pCode = (hResult & 0x0000FFFF); +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtylib.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtylib.c new file mode 100644 index 00000000..0d5ec652 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtylib.c @@ -0,0 +1,899 @@ +/*H********************************************************************************/ +/*! + \File dirtylib.c + + \Description + Platform independent routines for support library for network code. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 09/15/1999 (gschaefer) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/dirtysock.h" + +#if defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) +#include +#endif + +/*** Defines **********************************************************************/ + +// max size of line that will be tracked for netprint rate limiting +#define NETPRINT_RATELIMIT_MAXSTRINGLEN (256) + +/*** Type Definitions *************************************************************/ + +// rate limit info +typedef struct NetPrintRateLimitT +{ + NetCritT RateCrit; + uint32_t uRateLimitCounter; + uint32_t uPrintTime; + uint8_t bRateLimitInProgress; + char strRateLimitBuffer[NETPRINT_RATELIMIT_MAXSTRINGLEN+32]; +} NetPrintRateLimitT; + +/*** Variables ********************************************************************/ + +#if DIRTYCODE_LOGGING +// time stamp functionality +static uint8_t _NetLib_bEnableTimeStamp = TRUE; + +// rate limit variables +static NetPrintRateLimitT _NetLib_RateLimit; +static uint8_t _NetLib_bRateLimitInitialized = FALSE; + +// debugging hooks +static void *_NetLib_pDebugParm = NULL; +static int32_t (*_NetLib_pDebugHook)(void *pParm, const char *pText) = NULL; +#endif + +// idle critical section +static NetCritT _NetLib_IdleCrit; + +// idle task list +static struct +{ + void (*pProc)(void *pRef); + void *pRef; +} _NetLib_IdleList[32]; + +// number of installed idle task handlers +static int32_t _NetLib_iIdleSize = 0; + +//! table for calculating classic CRC-32 +static const uint32_t _NetLib_Crc32Table[256] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +/*** Private Functions ************************************************************/ + + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************************/ +/*! + \Function _NetPrintRateLimit + + \Description + Rate limit NetPrintf output; identical lines of fewer than 256 characters that are + line-terminated (end in a \n) will be suppressed, until a new line of text is output or + a 100ms timer elapses. At that point the line will be output, with additional text + indicating the number of lines that would have been output in total. A critical section + guarantees integrity of the buffer and tracking information, but it is only tried to prevent + any possibility of deadlock. In the case of a critical section try failure, rate limit + processing is skipped until the next print statement. + + \Input *pText - output text to (potentially) rate limit + + \Output + int32_t - zero=not rate limited (line should be printed), one=rate limited (do not print) + + \Version 04/05/2016 (jbrookes) +*/ +/*************************************************************************************************F*/ +static int32_t _NetPrintRateLimit(const char *pText) +{ + NetPrintRateLimitT *pRateLimit = &_NetLib_RateLimit; + int32_t iTextLen = (int32_t)strlen(pText); + uint32_t uCurTick = NetTick(), uDiffTick = 0; + int32_t iStrCmp, iResult = 0; + /* rate limit IFF: + - output is relatively small & line-terminated + - we have initialized rate limiting + - we are not printing rate-limited text ourselves + - we can secure access to the critical section (MUST COME LAST!) */ + if ((iTextLen >= NETPRINT_RATELIMIT_MAXSTRINGLEN) || (pText[iTextLen-1] != '\n') || !_NetLib_bRateLimitInitialized || pRateLimit->bRateLimitInProgress || !NetCritTry(&pRateLimit->RateCrit)) + { + return(iResult); + } + // does the string match our current string? + if ((iStrCmp = strcmp(pText, pRateLimit->strRateLimitBuffer)) == 0) + { + // if yes, calculate tick difference between now and when the line was buffered + uDiffTick = NetTickDiff(uCurTick, pRateLimit->uPrintTime); + } + // if we have a line we are rate-limiting, and either the timeout has elapsed or there is a new line + if ((pRateLimit->uRateLimitCounter > 1) && ((uDiffTick > 100) || (iStrCmp != 0))) + { + // print the line, tagging on how many times it was printed + iTextLen = (int32_t)strlen(pRateLimit->strRateLimitBuffer); + pRateLimit->strRateLimitBuffer[iTextLen-1] = '\0'; + pRateLimit->bRateLimitInProgress = TRUE; + NetPrintfCode("%s (%d times)\n", pRateLimit->strRateLimitBuffer, pRateLimit->uRateLimitCounter-1); + pRateLimit->bRateLimitInProgress = FALSE; + // set compare to non-matching to force restart below + iStrCmp = 1; + } + // if this line doesn't match our current buffered line, buffer it + if (iStrCmp != 0) + { + ds_strnzcpy(pRateLimit->strRateLimitBuffer, pText, sizeof(pRateLimit->strRateLimitBuffer)); + pRateLimit->uRateLimitCounter = 1; + pRateLimit->uPrintTime = NetTick(); + } + else + { + // match, so increment rate counter and suppress the line + pRateLimit->uRateLimitCounter += 1; + iResult = 1; + } + NetCritLeave(&pRateLimit->RateCrit); + return(iResult); +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function NetTimeStamp + + \Description + A printf function that returns a date time string for time stamp. + Only if time stamps is enabled + + \Input *pBuffer - pointer to format time stamp string + \Input iLen - length of the supplied buffer + + \Output + int32_t - return the length of the Time Stamp String + + \Version 09/10/2013 (tcho) +*/ +/********************************************************************************F*/ +static int32_t _NetTimeStamp(char *pBuffer, int32_t iLen) +{ + int32_t iTimeStampLen = 0; + + if (_NetLib_bEnableTimeStamp == TRUE) + { + struct tm tm; + int32_t imsec; + + ds_plattimetotimems(&tm, &imsec); + iTimeStampLen = ds_snzprintf(pBuffer, iLen,"%d/%02d/%02d-%02d:%02d:%02d.%03.3d ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, imsec); + } + + return(iTimeStampLen); +} +#endif + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function NetLibCommonInit + + \Description + NetLib platform-common initialization + + \Version 04/05/2016 (jbrookes) +*/ +/********************************************************************************F*/ +void NetLibCommonInit(void) +{ + // reset the idle list + NetIdleReset(); + + // initialize critical sections + NetCritInit(NULL, "lib-global"); + NetCritInit(&_NetLib_IdleCrit, "lib-idle"); + + // set up rate limiting + #if DIRTYCODE_LOGGING + ds_memclr(&_NetLib_RateLimit, sizeof(_NetLib_RateLimit)); + NetCritInit(&_NetLib_RateLimit.RateCrit, "lib-rate"); + _NetLib_bRateLimitInitialized = TRUE; + #endif +} + +/*F********************************************************************************/ +/*! + \Function NetLibCommonShutdown + + \Description + NetLib platform-common shutdown + + \Version 04/05/2016 (jbrookes) +*/ +/********************************************************************************F*/ +void NetLibCommonShutdown(void) +{ + // kill critical sections + #if DIRTYCODE_LOGGING + _NetLib_bRateLimitInitialized = FALSE; + NetCritKill(&_NetLib_RateLimit.RateCrit); + #endif + NetCritKill(&_NetLib_IdleCrit); + NetCritKill(NULL); +} + +/*F********************************************************************************/ +/*! + \Function NetIdleReset + + \Description + Reset idle function count. + + \Version 06/21/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void NetIdleReset(void) +{ + _NetLib_iIdleSize = 0; +} + +/*F********************************************************************************/ +/*! + \Function NetIdleAdd + + \Description + Add a function to the idle callback list. The functions are called whenever + NetIdleCall() is called. + + \Input *pProc - callback function pointer + \Input *pRef - function specific parameter + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void NetIdleAdd(void (*pProc)(void *pRef), void *pRef) +{ + // make sure proc is valid + if (pProc == NULL) + { + NetPrintf(("dirtylib: attempt to add an invalid idle function\n")); + return; + } + + // add item to list + _NetLib_IdleList[_NetLib_iIdleSize].pProc = pProc; + _NetLib_IdleList[_NetLib_iIdleSize].pRef = pRef; + _NetLib_iIdleSize += 1; +} + +/*F********************************************************************************/ +/*! + \Function NetIdleDel + + \Description + Remove a function from the idle callback list. + + \Input *pProc - callback function pointer + \Input *pRef - function specific parameter + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void NetIdleDel(void (*pProc)(void *pRef), void *pRef) +{ + int32_t iProc; + + // make sure proc is valid + if (pProc == NULL) + { + NetPrintf(("dirtylib: attempt to delete an invalid idle function\n")); + return; + } + + // mark item as deleted + for (iProc = 0; iProc < _NetLib_iIdleSize; ++iProc) + { + if ((_NetLib_IdleList[iProc].pProc == pProc) && (_NetLib_IdleList[iProc].pRef == pRef)) + { + _NetLib_IdleList[iProc].pProc = NULL; + _NetLib_IdleList[iProc].pRef = NULL; + break; + } + } +} + +/*F********************************************************************************/ +/*! + \Function NetIdleDone + + \Description + Make sure all idle calls have completed + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void NetIdleDone(void) +{ + NetCritEnter(&_NetLib_IdleCrit); + NetCritLeave(&_NetLib_IdleCrit); +} + +/*F********************************************************************************/ +/*! + \Function NetIdleCall + + \Description + Call all of the functions in the idle list. + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +void NetIdleCall(void) +{ + int32_t iProc; + + // only do idle call if we have control + if (NetCritTry(&_NetLib_IdleCrit)) + { + // walk the table calling routines + for (iProc = 0; iProc < _NetLib_iIdleSize; ++iProc) + { + // get pProc pointer + void (*pProc)(void *pRef) = _NetLib_IdleList[iProc].pProc; + void *pRef = _NetLib_IdleList[iProc].pRef; + + /* if pProc is deleted, handle removal here (this + helps prevent corrupting table in race condition) */ + if (pProc == NULL) + { + // swap with final element + _NetLib_IdleList[iProc].pProc = _NetLib_IdleList[_NetLib_iIdleSize-1].pProc; + _NetLib_IdleList[iProc].pRef = _NetLib_IdleList[_NetLib_iIdleSize-1].pRef; + _NetLib_IdleList[_NetLib_iIdleSize-1].pProc = NULL; + _NetLib_IdleList[_NetLib_iIdleSize-1].pRef = NULL; + + // drop the item count + _NetLib_iIdleSize -= 1; + + // restart the loop at new current element + --iProc; + continue; + } + // perform the idle call + (*pProc)(pRef); + } + // done with critical section + NetCritLeave(&_NetLib_IdleCrit); + } +} + +/*F********************************************************************************/ +/*! + \Function NetHash + + \Description + Calculate a unique 32-bit hash based on the given input string. + + \Input *pString - pointer to string to calc hash of + + \Output + int32_t - resultant 32bit hash + + \Version 2.0 07/26/2011 (jrainy) rewrite to lower collision rate +*/ +/********************************************************************************F*/ +int32_t NetHash(const char *pString) +{ + return(NetHashBin(pString, (uint32_t)strlen(pString))); +} + +/*F********************************************************************************/ +/*! + \Function NetHashBin + + \Description + Calculate a unique 32-bit hash based on the given buffer. + + \Input *pBuffer - pointer to buffer to calc hash of + \Input *uLength - length of buffer to calc hash of + + \Output + int32_t - resultant 32bit hash + + \Version 1.0 02/14/2011 (jrainy) First Version +*/ +/********************************************************************************F*/ +int32_t NetHashBin(const void *pBuffer, uint32_t uLength) +{ + // prime factor to multiply by at each 16-char block boundary + static uint32_t uShift = 436481627; + + // prime factors to multiply individual characters by + static uint32_t uFactors[16] = { + 682050377, 933939593, 169587707, 131017121, + 926940523, 102453581, 543947221, 775968049, + 129461173, 793216343, 870352919, 455044847, + 747808279, 727551509, 431178773, 519827743}; + + // running hash + uint32_t uSum = 0; + uint32_t uChar; + + for (uChar = 0; uChar != uLength; uChar++) + { + // at each 16-byte boundary, multiply the running hash by fixed factor + if ((uChar & 0xf) == 0) + { + uSum *= uShift; + } + // sum up the value of the char at position iChar by prime factor iChar%16 + uSum += ((uint8_t)((char*)pBuffer)[uChar]) * uFactors[uChar & 0xf]; + } + + return((int32_t)uSum); +} + + +/*F********************************************************************************/ +/*! + \Function NetCrc32 + + \Description + Calculate CRC32 of specified data. If no table is specified, the default + table is used. + + \Input *pBuffer - buffer to calculate crc32 + \Input iBufLen - length of buffer + \Input *pCrcTable - crc32 table to use, or NULL for the default + + \Output + int32_t - CRC32 of input data + + \Version 01/17/2019 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetCrc32(const uint8_t *pBuffer, int32_t iBufLen, const uint32_t *pCrcTable) +{ + uint32_t uCrc32; + // use default table if none specified + if (pCrcTable == NULL) + { + pCrcTable = _NetLib_Crc32Table; + } + // calculate crc + for (uCrc32 = 0xffffffff; iBufLen > 0; iBufLen -= 1) + { + uCrc32 = (uCrc32 >> 8) ^ pCrcTable[(uCrc32 ^ *pBuffer++) & 0xff]; + } + // return to caller + return(uCrc32 ^ 0xffffffff); +} + +/*F*************************************************************************************************/ +/*! + \Function NetRand + + \Description + A simple pseudo-random sequence generator. The sequence is implicitly seeded in the first + call with the millisecond tick count at the time of the call + + \Input uLimit - upper bound of pseudo-random number output + + \Output + uint32_t - pseudo-random number from [0...(uLimit - 1)] + + \Version 06/25/2009 (jbrookes) +*/ +/*************************************************************************************************F*/ +uint32_t NetRand(uint32_t uLimit) +{ + static uint32_t _aRand = 0; + if (_aRand == 0) + { + _aRand = NetTick(); + } + if (uLimit == 0) + { + return(0); + } + _aRand = (_aRand * 125) % 2796203; + return(_aRand % uLimit); +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function NetTimeStampEnableCode + + \Description + Enables Time Stamp in Logging + + \Input bEnableTimeStamp - TRUE to enable Time Stamp in Logging + + \Version 9/11/2014 (tcho) +*/ +/********************************************************************************F*/ +void NetTimeStampEnableCode(uint8_t bEnableTimeStamp) +{ + _NetLib_bEnableTimeStamp = bEnableTimeStamp; +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function NetPrintfHook + + \Description + Hook into debug output. + + \Input *pPrintfDebugHook - pointer to function to call with debug output + \Input *pParm - user parameter + + \Version 03/29/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void NetPrintfHook(int32_t (*pPrintfDebugHook)(void *pParm, const char *pText), void *pParm) +{ + _NetLib_pDebugHook = pPrintfDebugHook; + _NetLib_pDebugParm = pParm; +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function NetPrintfCode + + \Description + Debug formatted output + + \Input *pFormat - pointer to format string + \Input ... - variable argument list + + \Output + int32_t - zero + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t NetPrintfCode(const char *pFormat, ...) +{ + va_list pFmtArgs; + char strText[4096]; + const char *pText = strText; + int32_t iOutput = 1; + int32_t iTimeStampLen = 0; + + // init the string buffer (not done at instantiation so as to avoid having to clear the entire array, for perf) + strText[0] = '\0'; + + // only returns time stamp if time stamp is enabled + iTimeStampLen = _NetTimeStamp(strText, sizeof(strText)); + + // format the text + va_start(pFmtArgs, pFormat); + if ((pFormat[0] == '%') && (pFormat[1] == 's') && (pFormat[2] == '\0')) + { + ds_strnzcat(strText + iTimeStampLen, va_arg(pFmtArgs, const char *), sizeof(strText) - iTimeStampLen); + } + else + { + ds_vsnprintf(strText + iTimeStampLen, sizeof(strText) - iTimeStampLen, pFormat, pFmtArgs); + } + va_end(pFmtArgs); + + // check for rate limit (omit timestamp from consideration) + if (_NetPrintRateLimit(strText+iTimeStampLen)) + { + return(0); + } + + // forward to debug hook, if defined + if (_NetLib_pDebugHook != NULL) + { + iOutput = _NetLib_pDebugHook(_NetLib_pDebugParm, pText); + } + + // output to debug output, unless suppressed by debug hook + if (iOutput != 0) + { + #if defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + OutputDebugStringA(pText); + #else + printf("%s", pText); + #endif + } + + return(0); +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function NetPrintfVerboseCode + + \Description + Display input data if iVerbosityLevel is > iCheckLevel + + \Input iVerbosityLevel - current verbosity level + \Input iCheckLevel - level to check against + \Input *pFormat - format string + + \Version 12/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void NetPrintfVerboseCode(int32_t iVerbosityLevel, int32_t iCheckLevel, const char *pFormat, ...) +{ + va_list Args; + char strText[1024]; + + if (iVerbosityLevel > iCheckLevel) + { + va_start(Args, pFormat); + ds_vsnprintf(strText, sizeof(strText), pFormat, Args); + va_end(Args); + + NetPrintf(("%s", strText)); + } +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function NetPrintWrapCode + + \Description + Display input data with wrapping. + + \Input *pString - pointer to packet data to display + \Input iWrapCol - number of columns to wrap at + + \Version 09/15/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void NetPrintWrapCode(const char *pString, int32_t iWrapCol) +{ + const char *pTemp, *pEnd, *pEqual, *pSpace; + char strTemp[256] = " "; + uint32_t bDone; + int32_t iLen; + + // loop through whole packet + for (bDone = FALSE; bDone == FALSE; ) + { + // scan forward, tracking whitespace, linefeeds, and equal signs + for (pTemp=pString, pEnd=pTemp+iWrapCol, pSpace=NULL, pEqual=NULL; (pTemp < pEnd); pTemp++) + { + // remember most recent whitespace + if ((*pTemp == ' ') || (*pTemp == '\t')) + { + pSpace = pTemp; + } + + // remember most recent equal sign + if (*pTemp == '=') + { + pEqual = pTemp; + } + + // if eol or eos, break here + if ((*pTemp == '\n') || (*pTemp == '\0')) + { + break; + } + } + + // scanned an entire line? + if (pTemp == pEnd) + { + // see if we have whitespace to break on + if (pSpace != NULL) + { + pTemp = pSpace; + } + // see if we have an equals to break on + else if (pEqual != NULL) + { + pTemp = pEqual; + } + } + + // format string for output + iLen = (int32_t)(pTemp - pString + 1); + strncpy(strTemp + 3, pString, iLen); + if (*pTemp == '\0') + { + strTemp[iLen+2] = '\n'; + strTemp[iLen+3] = '\0'; + bDone = TRUE; + } + else if ((*pTemp != '\n') && (*pTemp != '\r')) + { + strTemp[iLen+3] = '\n'; + strTemp[iLen+4] = '\0'; + } + else + { + strTemp[iLen+3] = '\0'; + } + + // print it out + NetPrintf(("%s", strTemp)); + + // increment to next line + pString += iLen; + } +} +#endif // #if DIRTYCODE_LOGGING + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************************/ +/*! + \Function NetPrintMemCode + + \Description + Dump memory to debug output + + \Input *pMem - pointer to memory to dump + \Input iSize - size of memory block to dump + \Input *pTitle - pointer to title of memory block + + \Version 1.0 05/17/05 (jbrookes) First Version +*/ +/*************************************************************************************************F*/ +void NetPrintMemCode(const void *pMem, int32_t iSize, const char *pTitle) +{ + static const char _hex[] = "0123456789ABCDEF"; + char strOutput[128]; + int32_t iBytes, iOutput = 2; + + ds_memset(strOutput, ' ', sizeof(strOutput)-1); + strOutput[sizeof(strOutput)-1] = '\0'; + + NetPrintf(("dirtylib: dumping memory for object %s (%d bytes)\n", pTitle, iSize)); + + for (iBytes = 0; iBytes < iSize; iBytes++, iOutput += 2) + { + unsigned char cByte = ((unsigned char *)pMem)[iBytes]; + strOutput[iOutput] = _hex[cByte>>4]; + strOutput[iOutput+1] = _hex[cByte&0xf]; + strOutput[(iOutput/2)+40] = isprint(cByte) ? cByte : '.'; + if (iBytes > 0) + { + if (((iBytes+1) % 16) == 0) + { + strOutput[(iOutput/2)+40+1] = '\0'; + NetPrintf(("%s\n", strOutput)); + ds_memset(strOutput, ' ', sizeof(strOutput)-1); + strOutput[sizeof(strOutput)-1] = '\0'; + iOutput = 0; + } + else if (((iBytes+1) % 4) == 0) + { + iOutput++; + } + } + } + + if ((iBytes % 16) != 0) + { + strOutput[(iOutput/2)+40+1] = '\0'; + NetPrintf(("%s\n", strOutput)); + } +} +#endif + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************************/ +/*! + \Function NetPrintArrayCode + + \Description + Dump memory to debug output in the form of a c-style array declaration + + \Input *pMem - pointer to memory to dump + \Input iSize - size of memory block to dump + \Input *pTitle - pointer to title of memory block + + \Version 06/05/2014 (jbrookes) +*/ +/*************************************************************************************************F*/ +void NetPrintArrayCode(const void *pMem, int32_t iSize, const char *pTitle) +{ + static const char _hex[] = "0123456789ABCDEF"; + char strOutput[128]; + int32_t iBytes, iOutput = 4; + + ds_memset(strOutput, ' ', sizeof(strOutput)-1); + strOutput[sizeof(strOutput)-1] = '\0'; + + NetPrintf(("dirtylib: dumping declaration for object %s (%d bytes)\n", pTitle, iSize)); + NetPrintf(("static const uint8_t %s[] =\n", pTitle)); + NetPrintf(("{\n")); + + for (iBytes = 0; iBytes < iSize; iBytes++) + { + uint8_t cByte = ((uint8_t *)pMem)[iBytes]; + strOutput[iOutput+0] = '0'; + strOutput[iOutput+1] = 'x'; + strOutput[iOutput+2] = _hex[cByte>>4]; + strOutput[iOutput+3] = _hex[cByte&0xf]; + strOutput[iOutput+4] = ','; + iOutput += 5; + if (iBytes > 0) + { + if (((iBytes+1) % 16) == 0) + { + strOutput[iOutput] = '\0'; + NetPrintf(("%s\n", strOutput)); + ds_memset(strOutput, ' ', sizeof(strOutput)-1); + strOutput[sizeof(strOutput)-1] = '\0'; + iOutput = 4; + } + else if (((iBytes+1) % 4) == 0) + { + iOutput++; + } + } + } + + if ((iBytes % 16) != 0) + { + strOutput[iOutput] = '\0'; + NetPrintf(("%s\n", strOutput)); + } + + NetPrintf(("};\n")); +} +#endif diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtylib.cpp b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtylib.cpp new file mode 100644 index 00000000..2c2aeb91 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtylib.cpp @@ -0,0 +1,230 @@ +/*H********************************************************************************/ +/*! + + \File dirtylib.cpp + + \Description + Platform specific support library for network code. Suppplies + simple time, memory, and semaphore functions. + + \Copyright + Copyright (c) Electronic Arts 2002-2018. ALL RIGHTS RESERVED. + + \Version 01/02/02 (eesponda) Initial C++ version ported to use EAThread +*/ +/********************************************************************************H*/ + +#include "eathread/eathread_mutex.h" + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/dirtysock/dirtylib.h" + +/*** Type Definitions ************************************************************/ + +// critical section state +struct NetCritPrivT +{ + EA::Thread::Mutex Mutex; //!< mutex state, must come first + + uint8_t bEnabled; //!< controls if this lock is enabled + uint8_t _pad[3]; + + int32_t iMemGroup; //!< memory group identifier + void *pMemGroupUserdata; //!< user data passed along with allocation +}; + +/*** Variables *******************************************************************/ + +// global critical section +static NetCritPrivT _NetLib_GlobalCrit; + +// sets the functions to no-op in this situation +#if defined(DIRTYCODE_LINUX) +extern uint8_t _NetLib_bSingleThreaded; +#else +static uint8_t _NetLib_bSingleThreaded = FALSE; +#endif + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function NetCritInit + + \Description + Initialize a critical section for use. Allocates persistant storage + for the lifetime of the critical section if not using the global crit. + + \Input *pCrit - critical section marker + \Input *pCritName - name of the critical section + + \Output + int32_t - zero=success, negative=failure + + \Version 09/26/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t NetCritInit(NetCritT *pCrit, const char *pCritName) +{ + return(NetCritInit2(pCrit, pCritName, NETCRIT_OPTION_NONE)); +} + +/*F********************************************************************************/ +/*! + \Function NetCritInit2 + + \Description + Initialize a critical section for use. Allocates persistant storage + for the lifetime of the critical section if not using the global crit. + + \Input *pCrit - critical section marker + \Input *pCritName - name of the critical section + \Input uFlags - NETCRIT_OPTIONS_* flag options to set on the crit + + \Output + int32_t - zero=success, negative=failure + + \Version 07/24/2018 (eesponda) +*/ +/********************************************************************************F*/ +int32_t NetCritInit2(NetCritT *pCrit, const char *pCritName, uint32_t uFlags) +{ + NetCritPrivT *pPriv; + EA::Thread::MutexParameters Params; + ds_strnzcpy(Params.mName, pCritName, sizeof(Params.mName)); + Params.mbIntraProcess = true; + + // allocate memory if necessary + if (pCrit != NULL) + { + int32_t iMemGroup; + void *pMemGroupUserdata; + + // query memgroup info + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserdata); + + // allocate the memory needed for mutex state + if ((pCrit->pData = (NetCritPrivT *)DirtyMemAlloc(sizeof(*pCrit->pData), DIRTYTHREAD_MEMID, iMemGroup, pMemGroupUserdata)) == NULL) + { + return(-1); + } + ds_memclr(pCrit->pData, sizeof(*pCrit->pData)); + pCrit->pData->iMemGroup = iMemGroup; + pCrit->pData->pMemGroupUserdata = pMemGroupUserdata; + } + /* we always clear the single-thread enable option on the global crit. it would be counter productive if it was + enabled when we are in single-threaded mode */ + else + { + uFlags &= ~NETCRIT_OPTION_SINGLETHREADENABLE; + } + + pPriv = (pCrit ? pCrit->pData : &_NetLib_GlobalCrit); + pPriv->Mutex.Init(&Params); + pPriv->bEnabled = !_NetLib_bSingleThreaded || ((uFlags & NETCRIT_OPTION_SINGLETHREADENABLE) == NETCRIT_OPTION_SINGLETHREADENABLE); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetCritKill + + \Description + Release resources and destroy critical section. + + \Input *pCrit - critical section marker + + \Version 09/26/2017 (eesponda) +*/ +/********************************************************************************F*/ +void NetCritKill(NetCritT *pCrit) +{ + EA::Thread::Mutex *pMutex = (pCrit ? &pCrit->pData->Mutex : &_NetLib_GlobalCrit.Mutex); + pMutex->~Mutex(); + + // if we are not using the global crit free the memory + if (pCrit != NULL) + { + NetCritPrivT *pPriv = pCrit->pData; + DirtyMemFree(pPriv, DIRTYTHREAD_MEMID, pPriv->iMemGroup, pPriv->pMemGroupUserdata); + pCrit->pData = NULL; + } +} + +/*F********************************************************************************/ +/*! + \Function NetCritEnter + + \Description + Enter a critical section, blocking if needed. + + \Input *pCrit - critical section marker + + \Version 09/26/2017 (eesponda) +*/ +/********************************************************************************F*/ +void NetCritEnter(NetCritT *pCrit) +{ + NetCritPrivT *pPriv = (pCrit ? pCrit->pData : &_NetLib_GlobalCrit); + if (pPriv->bEnabled) + { + pPriv->Mutex.Lock(); + } +} + +/*F********************************************************************************/ +/*! + \Function NetCritTry + + \Description + Attempt to gain access to critical section. Always returns immediately + regadless of access status. A thread that already has access to a critical + section can always receive repeated access to it. + + \Input *pCrit - critical section marker + + \Output + int32_t - zero=unable to get access, non-zero=access granted + + \Version 09/26/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t NetCritTry(NetCritT *pCrit) +{ + NetCritPrivT *pPriv = (pCrit ? pCrit->pData : &_NetLib_GlobalCrit); + if (pPriv->bEnabled) + { + return(pPriv->Mutex.Lock(EA::Thread::kTimeoutImmediate) > 0); + } + else + { + return(1); + } +} + +/*F********************************************************************************/ +/*! + \Function NetCritLeave + + \Description + Leave a critical section. Must be called once for every NetCritEnter (or + successful NetCritTry). + + \Input *pCrit - critical section marker + + \Version 09/26/2017 (eesponda) +*/ +/********************************************************************************F*/ +void NetCritLeave(NetCritT *pCrit) +{ + NetCritPrivT *pPriv = (pCrit ? pCrit->pData : &_NetLib_GlobalCrit); + if (pPriv->bEnabled) + { + pPriv->Mutex.Unlock(); + } +} + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtymem.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtymem.c new file mode 100644 index 00000000..30035549 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtymem.c @@ -0,0 +1,278 @@ +/*H********************************************************************************/ +/*! + \File dirtymem.c + + \Description + DirtySock memory allocation routines. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 10/12/2005 (jbrookes) First Version + \Version 11/19/2008 (mclouatre) Adding pMemGroupUserData to mem groups +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +/*** Defines **********************************************************************/ + +#define DIRTYMEM_MAXGROUPS (16) + +/*** Type Definitions *************************************************************/ + +typedef struct DirtyMemInfoT +{ + int32_t iMemGroup; + void *pMemGroupUserData; +} DirtyMemInfoT; + + +/*** Variables ********************************************************************/ + +static int32_t _DirtyMem_iGroup = 0; +static DirtyMemInfoT _DirtyMem_iGroupStack[DIRTYMEM_MAXGROUPS] = { {'dflt', NULL} }; + +//#if defined(DIRTYCODE_DLL) + +static DirtyMemFreeT *_DirtyMem_pMemFreeFunc = NULL; +static DirtyMemAllocT *_DirtyMem_pMemAllocFunc = NULL; + +//#endif + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function DirtyMemGroupEnter + + \Description + Set group that will be used for allocations. + + \Input iMemGroup - group id + \Input *pMemGroupUserData - User-provided data pointer + + \Output + None. + + \Version 10/13/2005 (jbrookes) + \Version 11/19/2008 (mclouatre) Adding pMemGroupUserData to mem groups +*/ +/********************************************************************************F*/ +void DirtyMemGroupEnter(int32_t iMemGroup, void *pMemGroupUserData) +{ + // DIRTYMEM_MAXGROUPS - 1: the first slot is occupied by 'dflt' + if (_DirtyMem_iGroup >= (DIRTYMEM_MAXGROUPS - 1)) + { + NetPrintf(("dirtymem: group stack overflow\n")); + return; + } + _DirtyMem_iGroup += 1; + _DirtyMem_iGroupStack[_DirtyMem_iGroup].iMemGroup = iMemGroup; + _DirtyMem_iGroupStack[_DirtyMem_iGroup].pMemGroupUserData = pMemGroupUserData; +} + +/*F********************************************************************************/ +/*! + \Function DirtyMemGroupLeave + + \Description + Restore previous group that will be used for allocations. + + \Version 10/13/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyMemGroupLeave(void) +{ + if (_DirtyMem_iGroup <= 0) + { + NetPrintf(("dirtymem: group stack underflow\n")); + return; + } + _DirtyMem_iGroup -= 1; +} + +/*F********************************************************************************/ +/*! + \Function DirtyMemGroupQuery + + \Description + Return current memory group data. + + \Input *pMemGroup - [OUT param] pointer to variable to be filled with mem group id + \Input **ppMemGroupUserData - [OUT param] pointer to variable to be filled with pointer to user data + + \Output + None. + + \Version 10/13/2005 (jbrookes) + \Version 11/18/2008 (mclouatre) returned values now passed in [OUT] parameters +*/ +/********************************************************************************F*/ +void DirtyMemGroupQuery(int32_t *pMemGroup, void **ppMemGroupUserData) +{ + if (pMemGroup != NULL) + { + *pMemGroup = _DirtyMem_iGroupStack[_DirtyMem_iGroup].iMemGroup; + } + if (ppMemGroupUserData != NULL) + { + *ppMemGroupUserData = _DirtyMem_iGroupStack[_DirtyMem_iGroup].pMemGroupUserData; + } +} + +/*F********************************************************************************/ +/*! + \Function DirtyMemDebugAlloc + + \Description + Display memory allocation information to debug output. + + \Input *pMem - address of memory being freed + \Input iSize - size of allocation + \Input iMemModule - memory module + \Input iMemGroup - memory group + \Input *pMemGroupUserData - pointer to user data + + \Output + None. + + \Version 10/13/2005 (jbrookes) + \Version 11/18/2008 (mclouatre) adding pMemGroupUserData parameter +*/ +/********************************************************************************F*/ +#if DIRTYCODE_DEBUG +void DirtyMemDebugAlloc(void *pMem, int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +{ + NetPrintf(("dirtymem: [a] %p mod=%C grp=%C udataptr=%p size=%d\n", pMem, iMemModule, iMemGroup, pMemGroupUserData, iSize)); +} +#endif + +/*F********************************************************************************/ +/*! + \Function DirtyMemDebugFree + + \Description + Display memory free information to debug output. + + \Input *pMem - address of memory being freed + \Input iSize - size of allocation (if available), or zero + \Input iMemModule - memory module + \Input iMemGroup - memory group + \Input *pMemGroupUserData - pointer to user data + + \Output + None. + + \Version 10/13/2005 (jbrookes) + \Version 11/18/2008 (mclouatre) adding pMemGroupUserData parameter +*/ +/********************************************************************************F*/ +#if DIRTYCODE_DEBUG +void DirtyMemDebugFree(void *pMem, int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +{ + NetPrintf(("dirtymem: [f] %p mod=%C grp=%C udataptr=%p size=%d\n", pMem, iMemModule, iMemGroup, pMemGroupUserData, iSize)); +} +#endif + +//#if defined(DIRTYCODE_DLL) + +/*F********************************************************************************/ +/*! + \Function DirtyMemFuncSet + + \Description + This is only avaliable in the DLL mode. + Set Memory Allocate and Free functions that DirtySDK will use. + If any of the parameters are NULL it will use the default implementation + + \Input *pMemAlloc - function pointer to Mem Alloc function + \Input *pMemFree - function pointer to Mem Free function + + \Output + None. + + \Version 3/14/2014 (tcho) +*/ +/********************************************************************************F*/ +void DirtyMemFuncSet(DirtyMemAllocT *pMemAlloc, DirtyMemFreeT *pMemFree) +{ + _DirtyMem_pMemFreeFunc = pMemFree; + _DirtyMem_pMemAllocFunc = pMemAlloc; +} + + +/*F********************************************************************************/ +/*! + \Function DirtyMemAlloc + + \Description + Only Avaliable in DLL mode. + Implementation of the required DirtySock memory allocation routine. + + \Input iSize - size of memory to allocate + \Input iMemModule - memory module id + \Input iMemGroup - memory group id + \Input *pMemGroupUserData - user data associated with mem group + + \Output + void * - pointer to newly allocated memory, or NULL + + \Version 3/14/2014 (tcho) +*/ +/********************************************************************************F*/ +void *DirtyMemAlloc(int32_t iSize, int32_t iMemModule, int32_t iMemGroup, void * pMemGroupUserData) +{ + void *pMem = NULL; + + if (_DirtyMem_pMemAllocFunc != NULL) + { + pMem = _DirtyMem_pMemAllocFunc(iSize, iMemModule, iMemGroup, pMemGroupUserData); + } + else + { + pMem = (void *)malloc(iSize); + } + + return(pMem); +} + +/*F********************************************************************************/ +/*! + \Function DirtyMemFree + + \Description + Only Avaliable in DLL mode. + Implementation of the required DirtySock memory free routine. + + \Input *pMem - pointer to memory block to free + \Input iMemModule - memory module id + \Input iMemGroup - memory group id + \Input *pMemGroupUserData - user data associated with mem group + + \Output + None. + + \Version 3/14/2014 (tcho) +*/ +/********************************************************************************F*/ +void DirtyMemFree(void *pMem, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +{ + if (_DirtyMem_pMemFreeFunc != NULL) + { + _DirtyMem_pMemFreeFunc(pMem, iMemModule, iMemGroup, pMemGroupUserData); + } + else + { + free(pMem); + } +} + +//#endif diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynames.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynames.c new file mode 100644 index 00000000..15d242c4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynames.c @@ -0,0 +1,246 @@ +/*H*************************************************************************************************/ +/*! + + \File dirtynames.c + + \Description + This module provides helper functions for manipulating persona and master + account name strings. + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002-2003. ALL RIGHTS RESERVED. + + \Version 1.0 12/10/02 (DBO) First Version + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynames.h" + +/*** Defines ***************************************************************************/ + +#ifndef TRUE +#define TRUE (1) +#define FALSE (0) +#endif + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +static const unsigned char xlat[256] ={ + 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, + 0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 + }; + +// Public variables + + +/*** Private Functions *****************************************************************/ + +/*F************************************************************************************************/ +/*! + \Function toXlatIndex + + \Description + Converts a char to xlat table index using its ascii char representation. + Characters outside the range [0, 255] are treated as 255. + + \Input str - Input char + + \Output xlat index - int32_t (0 to 255) +*/ +/************************************************************************************************F*/ +int32_t toXlatIndex(const char str) +{ + int32_t xlatIndex = (int32_t)str; + return (xlatIndex >= 0 && xlatIndex < 256) ? xlatIndex : 255; +} + +/*** Public Functions ******************************************************************/ + + +/*F************************************************************************************************/ +/*! + \Function DirtyUsernameCompare + + \Description + Compare two master account names. Ignore case and non-printable ascii characters. + + \Input pName1 - Input account name 1 + \Input pName2 - Input account name 2 + + \Output int32_t - < 0 = pName1 < pName2 + = 0 = pName1 = pName2 + > 0 = pName1 > pName2 + + \Version 1.0 05/01/2003 (DBO) Taken from LobbyHasher (HashStrCmp) +*/ +/************************************************************************************************F*/ +int32_t DirtyUsernameCompare(const char *pName1, const char *pName2) +{ + int32_t iCmp; + unsigned char cCh1, cCh2; + + // compare the strings + do { + // grab the data + while ((cCh1 = xlat[toXlatIndex(*pName1++)]) == 1) + ; + while ((cCh2 = xlat[toXlatIndex(*pName2++)]) == 1) + ; + // see if different + if ((iCmp = cCh1-cCh2) != 0) + return(iCmp); + } while (cCh1 != 0); + + // they are the same + return(0); +} + + +/*F************************************************************************************************/ +/*! + \Function DirtyUsernameSubstr + + \Description + Determine if pMatch is a substring of pSrc. Ignore case and non-printable ascii characters. + + \Input pSrc - Location to store converted name + \Input pMatch - Input account name + + \Output int32_t - TRUE if pMatch is a substring of pSrc; FALSE otherwise + + \Version 1.0 05/02/2003 (DBO) Initial version. +*/ +/************************************************************************************************F*/ +int32_t DirtyUsernameSubstr ( const char *pSrc, const char *pMatch ) +{ + int32_t iSrcIdx; + int32_t iMatchIdx1; + int32_t iMatchIdx2; + char cCh1; + char cCh2; + + if ( (pMatch == NULL) || (pMatch[0] == '\0') ) + return(TRUE); + if ( (pSrc == NULL) || (pSrc[0] == '\0') ) + return(FALSE); + + for(iSrcIdx = 0; pSrc[iSrcIdx] != '\0'; iSrcIdx++) + { + iMatchIdx1 = iSrcIdx; + iMatchIdx2 = 0; + + do + { + // Skip over ignored characters + while ( (cCh1 = xlat[toXlatIndex(pSrc[iMatchIdx1++])]) == 1 ) + ; + while ( (cCh2 = xlat[toXlatIndex(pMatch[iMatchIdx2++])]) == 1 ) + ; + + if ( cCh2 == 0 ) + return(TRUE); + } + while ( (cCh1 != 0) && (cCh1 == cCh2) ); + } + return(FALSE); +} + +/*F************************************************************************************************/ +/*! + \Function DirtyNameCreateCanonical + + \Description + Create the canonical form of the given name. This essentially lowercases the name and + strips non-printable ascii characters as per the xlat table. + + \Input pName - The input name to create the canonical name from. + \Input pCanonical - The buffer to output the canonical name into + \Input uLen - The length of pCanonical + + \Output int32_t - 0 on success; -1 if the output buffer is too small +*/ +/************************************************************************************************F*/ +int32_t DirtyNameCreateCanonical(const char *pName, char * pCanonical, size_t uLen) +{ + unsigned char cCh; + + while (uLen > 0) + { + // Strip the ignored characters + while ((cCh = xlat[toXlatIndex(*pName++)]) == 1) + ; + + if ((cCh >= 'A') && (cCh <= 'Z')) + cCh |= 32; + *pCanonical++ = cCh; + uLen--; + if (cCh == 0) + return 0; + } + return -1; +} + +/*F************************************************************************************************/ +/*! + \Function DirtyUsernameHash + + \Description + Generate the hash code for the given persona name. This hashing function will hash the + canonical form of the name rather than the raw bytes to ensure that equivalent names + always hash to the same value, as per the xlat table. + + \Input pName - The input name to generate a hash code. + + \Output uint32_t - The generated hash code. +*/ +/************************************************************************************************F*/ +uint32_t DirtyUsernameHash(const char *pName) +{ + unsigned char cCh; + uint32_t result = 2166136261U; // FNV1 hash. Perhaps the best string hash. + + do + { + // Strip the ignored characters + while ((cCh = xlat[toXlatIndex(*pName++)]) == 1) + ; + + result = (result * 16777619) ^ cCh; + + } while (cCh != 0); + + return result; +} + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynet.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynet.c new file mode 100644 index 00000000..fcf4a767 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynet.c @@ -0,0 +1,2777 @@ +/*H*************************************************************************************************/ +/*! + \File dirtynet.c + + \Description + Platform-independent network related routines. + + \Copyright + Copyright (c) Electronic Arts 2002-2018 + + \Version 1.0 01/02/2002 (gschaefer) First Version + \Version 1.1 01/27/2003 (jbrookes) Split from dirtynetwin.c +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtynet.h" + +#include "dirtynetpriv.h" + +/*** Defines ***************************************************************************/ + +//! 30s hostname cache timeout +#define DIRTYNET_HOSTNAMECACHELIFETIME (30*1000) + +//! verbose logging of dirtynet packet queue operations +#define DIRTYNET_PACKETQUEUEDEBUG (DIRTYCODE_LOGGING && FALSE) + +//! verbose logging of dirtynet rate estimation +#define DIRTYNET_RATEDEBUG (DIRTYCODE_LOGGING && FALSE) + +//! maximum allowable packet queue size +#define DIRTYNET_PACKETQUEUEMAX (1024) + +//! minimum throttle rate supported +#define DIRTYNET_MIN_THROTTLE_RATE (1460) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! socket hostname cache entry +typedef struct SocketHostnameCacheEntryT +{ + char strDnsName[256]; + uint32_t uAddress; + uint32_t uTimer; +} SocketHostnameCacheEntryT; + +//! socket hostname cache +struct SocketHostnameCacheT +{ + int32_t iMaxEntries; + int32_t iMemGroup; + void *pMemGroupUserData; + NetCritT Crit; + SocketHostnameCacheEntryT CacheEntries[1]; //!< variable-length cache entry list +}; + +//! socket packet queue +struct SocketPacketQueueT +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + int16_t iNumPackets; //!< number of packets in the queue + int16_t iMaxPackets; //!< max queue size + int16_t iPacketHead; //!< current packet queue head + int16_t iPacketTail; //!< current packet queue tail + + uint32_t uLatency; //!< simulated packet latency target, in milliseconds + uint32_t uDeviation; //!< simulated packet deviation target, in milliseconds + uint32_t uPacketLoss; //!< packet loss percentage, 16.16 fractional integer + + uint32_t uPacketDrop; //!< number of packets overwritten due to queue overflow + uint32_t uPacketMax; //!< maximum number of packets in the queue (high water mark) + + uint32_t uLatencyTime; //!< current amount of latency in the packet queue + int32_t iDeviationTime; //!< current deviation + + SocketPacketQueueEntryT aPacketQueue[1]; //!< variable-length queue entry list +}; + +#ifndef DIRTYCODE_NX +//! socket address map entry +struct SocketAddrMapEntryT +{ + int32_t iRefCount; + int32_t iVirtualAddress; + struct sockaddr_in6 SockAddr6; +}; +#endif + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + + +// Public variables + + +/*** Private Functions *****************************************************************/ + +#ifndef DIRTYCODE_NX +/*F********************************************************************************/ +/*! + \Function _SockaddrIn6Identify + + \Description + Identify IPv6 sockaddr based on specified matching sequence (must come at + the start of address bytes). + + \Input *pAddr6 - address to identify + \Input *pCheckVal - bytes to check + \Input iValLen - length of byte sequence to check + + \Output + uint8_t - TRUE if the address matches, else FALSE + + \Version 04/12/2016 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t _SockaddrIn6Identify(const struct sockaddr_in6 *pAddr6, const uint8_t *pCheckVal, int32_t iValLen) +{ + uint8_t bResult = !memcmp(pCheckVal, pAddr6->sin6_addr.s6_addr, iValLen) ? TRUE : FALSE; + return(bResult); +} + +/*F********************************************************************************/ +/*! + \Function _SockaddrIn6IsIPv4 + + \Description + Identify if specified sockaddr_in6 is an IPv4-mapped IPv6 address + + \Input *pAddr6 - address to identify + + \Output + uint8_t - TRUE if the address is IPv4-mapped, else FALSE + + \Version 04/12/2016 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t _SockaddrIn6IsIPv4(const struct sockaddr_in6 *pAddr6) +{ + const uint8_t aIpv4Prefix[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }; + return(_SockaddrIn6Identify(pAddr6, aIpv4Prefix, sizeof(aIpv4Prefix))); +} + +/*F********************************************************************************/ +/*! + \Function _SockaddrIn6IsNAT64 + + \Description + Identify if specified sockaddr_in6 is a NAT64 IPv6 address + + \Input *pAddr6 - address to identify + + \Output + uint8_t - TRUE if the address is NAT64, else FALSE + + \Version 04/12/2016 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t _SockaddrIn6IsNAT64(const struct sockaddr_in6 *pAddr6) +{ + const uint8_t aNat64Prefix[] = { 0x00, 0x64, 0xff, 0x9b }; + return(_SockaddrIn6Identify(pAddr6, aNat64Prefix, sizeof(aNat64Prefix))); +} + +/*F********************************************************************************/ +/*! + \Function _SockaddrIn6IsZero + + \Description + Identify if specified sockaddr_in6 is zero (unspecified) + + \Input *pAddr6 - address to identify + + \Output + uint8_t - TRUE if the address is zero, else FALSE + + \Version 04/22/2016 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t _SockaddrIn6IsZero(const struct sockaddr_in6 *pAddr6) +{ + const uint8_t aIpv6Zero[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + return(_SockaddrIn6Identify(pAddr6, aIpv6Zero, sizeof(aIpv6Zero))); +} + +/*F*************************************************************************************************/ +/*! +\Function _SockaddrIn6SetAddrText + +\Description +Set Internet address component of sockaddr_in6 struct from textural address. + +\Input *pAddr6 - pointer to ipv6 address +\Input *pStr - pointer to source ipv6 address + +\Output +int32_t - zero=no error, negative=error + +\Notes +See _SockaddrIn6GetAddrText for format considerations and references + +\Version 08/28/2017 (jbrookes) +*/ +/*************************************************************************************************F*/ +static int32_t _SockaddrIn6SetAddrText(struct sockaddr_in6 *pAddr6, const char *pStr) +{ + uint16_t aAddrWords[8], *pWords, uWordVal; + int32_t iWordIdx, iWordCt, iSkipIdx, iWriteIdx; + const char *pEnd, *pDot; + uint32_t uAddr32 = 0; + + // see if we have dot notation (e.g. nat64, ipv4-mapped) + if ((pDot = strchr(pStr, '.')) != NULL) + { + // find start of dot notation number + for (pDot -= 1; isalnum(*pDot) && (pDot > pStr); pDot -= 1) + ; + // get 32 bit address encoded in dot notation + uAddr32 = SocketInTextGetAddr(pDot + 1); + } + + // init word array + ds_memclr(aAddrWords, sizeof(aAddrWords)); + + // convert address words, remember if we had a skip + for (iWordIdx = 0, iSkipIdx = -1; (*pStr != '\0') && (iWordIdx < 8); pStr += 1) + { + uWordVal = SocketHtons(strtol(pStr, (char **)&pEnd, 16)); + pStr = pEnd; + + if ((*pStr == ':') || (*pStr == '\0')) + { + aAddrWords[iWordIdx++] = uWordVal; + } + if ((*pStr == ':') && (pStr[1] == ':')) + { + iSkipIdx = iWordIdx; + pStr += 1; + } + if ((pStr == pDot) && (uAddr32 != 0)) + { + aAddrWords[iWordIdx++] = SocketHtons((uint16_t)(uAddr32 >> 16)); + aAddrWords[iWordIdx++] = SocketHtons((uint16_t)(uAddr32 >> 0)); + break; + } + + if (*pStr == '\0') + { + break; + } + } + + // copy to sockaddr, with skip + for (iWordCt = iWordIdx, iWordIdx = iWriteIdx = 0, pWords = (uint16_t *)pAddr6->sin6_addr.s6_addr; iWriteIdx < 8; ) + { + if (iWriteIdx == iSkipIdx) + { + for (iWordCt = 8 - iWordCt; iWordCt > 0; iWordCt -= 1) + { + pWords[iWriteIdx++] = 0; + } + } + else + { + pWords[iWriteIdx++] = aAddrWords[iWordIdx++]; + } + } + + return(0); +} +/*F*************************************************************************************************/ +/*! + \Function _SockaddrIn6GetAddrText + + \Description + Return Internet address component of sockaddr_in6 struct in textual form (see below). + + \Input *pAddr6 - pointer to ipv6 address + \Input *pStr - pointer to storage for text address + \Input iLen - length of buffer + + \Output + char * - returns pStr on success, NULL on failure + + \Notes + IPv6 textual format guidelines are outlined in https://tools.ietf.org/html/rfc4291#section-2.2 + and later refined in https://tools.ietf.org/html/rfc5952, which narrows the range of + supported options to standardize are more rigid specification. This implementation + follows the more limited set of specifications outlined in rfc5952. The short version is: + + - Preferred form is x:x:x:x:x:x:x:x, where the 'x's are one of four hexadecimal digits of + the eight 16-bit pieces of the address + - Leading zeros within a field MUST be suppressed + - The use of :: indicates one or more groups of 16 bits of zeros; it can appear only once + in an address + - The :: symbole MUST be used to its maximum capability. It MUST NOT be used to shorten + just one 16-bit field + - If there are two or more sets of 16-bit zeros of equal length, the left-most MUST be + shortened + - Alphabetic characters in the address MUST be represented in lowercase + - It is RECOMMENDED to use mixed notation if the address can be distinguished as having + an IPv4 address embedded in the lower 32 bits solely from the address field through + the use of a well-known prefix. + + \Version 08/28/2017 (jbrookes) +*/ +/*************************************************************************************************F*/ +static char *_SockaddrIn6GetAddrText(const struct sockaddr_in6 *pAddr6, char *pStr, int32_t iLen) +{ + uint16_t aAddrWords[8], *pWords; + int32_t iWord, iOffset; + int32_t iZeroStart, iZeroCount; + int32_t iZeroStartTmp, iZeroCountTmp; + char strAddr32[16] = ""; + + // convert to words in host format + for (iWord = 0, pWords = (uint16_t *)pAddr6->sin6_addr.s6_addr; iWord < 8; iWord += 1) + { + aAddrWords[iWord] = SocketNtohs(pWords[iWord]); + } + + // if this is a mixed-notation address convert the final 32bits to a dot-notation address string + if (_SockaddrIn6IsIPv4(pAddr6) || _SockaddrIn6IsNAT64(pAddr6)) + { + uint32_t uAddr32 = (uint32_t)aAddrWords[6] << 16 | (uint32_t)aAddrWords[7]; + SocketInAddrGetText(uAddr32, strAddr32, sizeof(strAddr32)); + } + + // find longest stretch of two or more zeros (if there is one) + for (iWord = iZeroStart = iZeroCount = iZeroCountTmp = iZeroStartTmp = 0; iWord < 8; iWord += 1) + { + if (aAddrWords[iWord] == 0) + { + if (iZeroCountTmp == 0) + { + iZeroStartTmp = iWord; + } + iZeroCountTmp += 1; + } + else + { + iZeroCountTmp = 0; + } + + if (iZeroCountTmp > iZeroCount) + { + iZeroStart = iZeroStartTmp; + iZeroCount = iZeroCountTmp; + } + } + + // format address string + for (iWord = 0, iOffset = 0; iWord < 8; iWord += 1) + { + if ((iWord != iZeroStart) || (iZeroCount < 2)) + { + iOffset += ds_snzprintf(pStr + iOffset, iLen - iOffset, "%x", aAddrWords[iWord]); + if (iWord < 7) + { + iOffset += ds_snzprintf(pStr + iOffset, iLen - iOffset, ":"); + } + } + else + { + iOffset += ds_snzprintf(pStr + iOffset, iLen - iOffset, (iWord == 0) ? "::" : ":"); + iWord += iZeroCount - 1; + } + + // output dot portion of mixed-notation address, if present + if ((strAddr32[0] != '\0') && (iWord == 5)) + { + iOffset += ds_snzprintf(pStr + iOffset, iLen - iOffset, "%s", strAddr32); + iWord += 2; + } + } + + // return to caller + return(pStr); +} + +#endif +/*F*************************************************************************************************/ +/*! + \Function _SockaddrIn4SetAddrText + + \Description + Set Internet address component of sockaddr struct from textual address (a.b.c.d). + + \Input *pAddr - sockaddr structure + \Input *pStr - textual address + + \Output + int32_t - zero=no error, negative=error + + \Version 10/04/1999 (gschaefer) +*/ +/*************************************************************************************************F*/ +static int32_t _SockaddrIn4SetAddrText(struct sockaddr *pAddr, const char *pStr) +{ + uint8_t *pIpAddr = (uint8_t *)(pAddr->sa_data+2); + int32_t iOctet; + + for (iOctet = 0; iOctet < 4; iOctet += 1, pStr += 1) + { + pIpAddr[iOctet] = '\0'; + while ((*pStr >= '0') && (*pStr <= '9')) + { + pIpAddr[iOctet] = (pIpAddr[iOctet]*10) + (*pStr++ & 15); + } + if ((iOctet < 3) && (*pStr != '.')) + { + pIpAddr[0] = pIpAddr[1] = pIpAddr[2] = pIpAddr[3] = '\0'; + return(-1); + } + } + + return(0); +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function SockaddrCompare + + \Description + Compare two sockaddr structs and see if address is the same. This is different + from simple binary compare because only relevent fields are checked. + + \Input *pAddr1 - address #1 + \Input *pAddr2 - address to compare with address #1 + + \Output + int32_t - zero=no error, negative=error + + \Version 08/30/1999 (gschaefer) +*/ +/*************************************************************************************************F*/ +int32_t SockaddrCompare(const struct sockaddr *pAddr1, const struct sockaddr *pAddr2) +{ + int32_t len = sizeof(*pAddr1)-sizeof(pAddr1->sa_family); + + // make sure address family matches + if (pAddr1->sa_family != pAddr2->sa_family) + { + return(pAddr1->sa_family- pAddr2->sa_family); + } + + // do type specific comparison + if (pAddr1->sa_family == AF_INET) + { + // compare port and address + len = 2 + 4; + } + else if (pAddr1->sa_family == AF_INET6) + { + // compare port, flow info, and address + len = 2 + 4 + 8; + } + + // binary compare of address data + return(memcmp(pAddr1->sa_data, pAddr2->sa_data, len)); +} + +/*F*************************************************************************************************/ +/*! + \Function SockaddrInSetAddrText + + \Description + Set Internet address component of sockaddr struct from textual address. + Important: Only works with AF_INET addresses for nx + + \Input *pAddr - sockaddr structure + \Input *pStr - textual address + + \Output + int32_t - zero=no error, negative=error + + \Version 10/04/1999 (gschaefer) +*/ +/*************************************************************************************************F*/ +int32_t SockaddrInSetAddrText(struct sockaddr *pAddr, const char *pStr) +{ + int32_t iResult = -1; + if (pAddr->sa_family == AF_INET) + { + iResult = _SockaddrIn4SetAddrText(pAddr, pStr); + } + #ifndef DIRTYCODE_NX + else if (pAddr->sa_family == AF_INET6) + { + iResult = _SockaddrIn6SetAddrText((struct sockaddr_in6 *)pAddr, pStr); + } + #endif + return(iResult); +} + +/*F*************************************************************************************************/ +/*! + \Function SockaddrInGetAddrText + + \Description + Convert a sockaddr into textual form based on address family + + \Input *pAddr - sockaddr struct + \Input *pStr - address buffer + \Input iLen - address length + + \Output + char * - returns str on success, NULL on failure + + \Version 10/04/1999 (gschaefer) +*/ +/*************************************************************************************************F*/ +char *SockaddrInGetAddrText(const struct sockaddr *pAddr, char *pStr, int32_t iLen) +{ + char *pResult = NULL; + if (pAddr->sa_family == AF_INET) + { + pResult = SocketInAddrGetText(SockaddrInGetAddr(pAddr), pStr, iLen); + } + #ifndef DIRTYCODE_NX + else if (pAddr->sa_family == AF_INET6) + { + pResult = _SockaddrIn6GetAddrText((const struct sockaddr_in6 *)pAddr, pStr, iLen); + } + #endif + return(pResult); +} + +/*F*************************************************************************************************/ +/*! + \Function SockaddrInParse + + \Description + Convert textual internet address:port into sockaddr structure + + \Input *pAddr - sockaddr to fill in + \Input *pParse - textual address + + \Output + int32_t - flags: + 0=parsed nothing + 1=parsed addr + 2=parsed port + 3=parsed addr+port + + \Version 11/23/2002 (gschaefer) +*/ +/*************************************************************************************************F*/ +int32_t SockaddrInParse(struct sockaddr *pAddr, const char *pParse) +{ + int32_t iReturn = 0, iPort = 0; + uint32_t uAddr = 0; + + // init the address + SockaddrInit(pAddr, AF_INET); + + // parse addr:port + iReturn = SockaddrInParse2(&uAddr, &iPort, NULL, pParse); + + // set addr:port in sockaddr + SockaddrInSetAddr(pAddr, uAddr); + SockaddrInSetPort(pAddr, iPort); + + // return parse info + return(iReturn); +} + +/*F*************************************************************************************************/ +/*! + \Function SockaddrInParse2 + + \Description + Convert textual internet address:port into sockaddr structure + + If the textual internet address:port is followed by a second :port, the second port + is optionally parsed into pPort2, if not NULL. + + \Input *pAddr - address to fill in + \Input *pPort - port to fill in + \Input *pPort2 - second port to fill in + \Input *pParse - textual address + + \Output + int32_t - flags: + 0=parsed nothing + 1=parsed addr + 2=parsed port + 3=parsed addr+port + 4=parsed port2 + + \Version 11/23/02 (GWS) First Version +*/ +/*************************************************************************************************F*/ +int32_t SockaddrInParse2(uint32_t *pAddr, int32_t *pPort, int32_t *pPort2, const char *pParse) +{ + int32_t iReturn = 0; + uint32_t uVal; + + // skip embedded white-space + while ((*pParse > 0) && (*pParse <= ' ')) + { + ++pParse; + } + + // parse the address (no dns for listen) + for (uVal = 0; ((*pParse >= '0') && (*pParse <= '9')) || (*pParse == '.'); ++pParse) + { + // either add or shift + if (*pParse != '.') + { + uVal = (uVal - (uVal & 255)) + ((uVal & 255) * 10) + (*pParse & 15); + } + else + { + uVal <<= 8; + } + } + if ((*pAddr = uVal) != 0) + { + iReturn |= 1; + } + + // skip non-port info + while ((*pParse != ':') && (*pParse != 0)) + { + ++pParse; + } + + // parse the port + uVal = 0; + if (*pParse == ':') + { + for (++pParse; (*pParse >= '0') && (*pParse <= '9'); ++pParse) + { + uVal = (uVal * 10) + (*pParse & 15); + } + iReturn |= 2; + } + *pPort = (int32_t)uVal; + + // parse port2 (optional) + if (pPort2 != NULL) + { + uVal = 0; + if (*pParse == ':') + { + for (++pParse; (*pParse >= '0') && (*pParse <= '9'); ++pParse) + { + uVal = (uVal * 10) + (*pParse & 15); + } + iReturn |= 4; + } + *pPort2 = (int32_t)uVal; + } + + // return the address + return(iReturn); +} + +/*F*************************************************************************************************/ +/*! + \Function SocketInAddrGetText + + \Description + Convert 32-bit internet address into textual form. + + \Input uAddr - address + \Input *pStr - [out] address buffer + \Input iLen - address length + + \Output + char * - returns str on success, NULL on failure + + \Version 06/17/2009 (jbrookes) +*/ +/*************************************************************************************************F*/ +char *SocketInAddrGetText(uint32_t uAddr, char *pStr, int32_t iLen) +{ + uint8_t uAddrByte[4]; + int32_t iIndex; + char *pStrStart = pStr; + + uAddrByte[0] = (uint8_t)(uAddr>>24); + uAddrByte[1] = (uint8_t)(uAddr>>16); + uAddrByte[2] = (uint8_t)(uAddr>>8); + uAddrByte[3] = (uint8_t)(uAddr>>0); + + for (iIndex = 0; iIndex < 4; iIndex += 1) + { + uint32_t uNumber = uAddrByte[iIndex]; + if (uNumber > 99) + { + *pStr++ = (char)('0' + (uNumber / 100)); + uNumber %= 100; + *pStr++ = (char)('0' + (uNumber / 10)); + uNumber %= 10; + } + if (uNumber > 9) + { + *pStr++ = (char)('0' + (uNumber / 10)); + uNumber %= 10; + } + *pStr++ = (char)('0' + uNumber); + if (iIndex < 3) + { + *pStr++ = '.'; + } + } + *pStr = '\0'; + return(pStrStart); +} + +/*F*************************************************************************************************/ +/*! + \Function SocketInTextGetAddr + + \Description + Convert textual internet address into 32-bit integer form + + \Input *pAddrText - textual address + + \Output + int32_t - integer form + + \Version 11/23/02 (JLB) First Version + +*/ +/*************************************************************************************************F*/ +int32_t SocketInTextGetAddr(const char *pAddrText) +{ + struct sockaddr SockAddr; + int32_t iAddr = 0; + + SockaddrInit(&SockAddr, AF_INET); + if (SockaddrInSetAddrText(&SockAddr, pAddrText) == 0) + { + iAddr = SockaddrInGetAddr(&SockAddr); + } + return(iAddr); +} + +/*F*************************************************************************************************/ +/*! + \Function SocketHtons + + \Description + Convert uint16_t from host to network byte order + + \Input uAddr - value to convert + + \Output + uint16_t - converted value + + \Version 10/04/1999 (gschaefer) +*/ +/*************************************************************************************************F*/ +uint16_t SocketHtons(uint16_t uAddr) +{ + uint8_t uNetw[2]; + ds_memcpy_s(uNetw, sizeof(uNetw), &uAddr, sizeof(uAddr)); + return((uNetw[0]<<8)|(uNetw[1]<<0)); +} + +/*F*************************************************************************************************/ +/*! + \Function SocketHtonl + + \Description + Convert uint32_t from host to network byte order. + + \Input uAddr - value to convert + + \Output + uint32_t - converted value + + \Version 10/04/1999 (gschaefer) +*/ +/*************************************************************************************************F*/ +uint32_t SocketHtonl(uint32_t uAddr) +{ + uint8_t uNetw[4]; + ds_memcpy_s(uNetw, sizeof(uNetw), &uAddr, sizeof(uAddr)); + return((((((uNetw[0]<<8)|uNetw[1])<<8)|uNetw[2])<<8)|uNetw[3]); +} + +/*F*************************************************************************************************/ +/*! + \Function SocketNtohs + + \Description + Convert uint16_t from network to host byte order. + + \Input uAddr - value to convert + + \Output + uint16_t - converted value + + \Version 10/0/99 (GWS) First Version + +*/ +/*************************************************************************************************F*/ +uint16_t SocketNtohs(uint16_t uAddr) +{ + uint8_t uNetw[2]; + uNetw[1] = (uint8_t)uAddr; + uAddr >>= 8; + uNetw[0] = (uint8_t)uAddr; + ds_memcpy_s(&uAddr, sizeof(uAddr), uNetw, sizeof(uNetw)); + return(uAddr); +} + +/*F*************************************************************************************************/ +/*! + \Function SocketNtohl + + \Description + Convert uint32_t from network to host byte order. + + \Input uAddr - value to convert + + \Output + uint32_t - converted value + + \Version 10/04/1999 (gschaefer) +*/ +/*************************************************************************************************F*/ +uint32_t SocketNtohl(uint32_t uAddr) +{ + uint8_t uNetw[4]; + uNetw[3] = (uint8_t)uAddr; + uAddr >>= 8; + uNetw[2] = (uint8_t)uAddr; + uAddr >>= 8; + uNetw[1] = (uint8_t)uAddr; + uAddr >>= 8; + uNetw[0] = (uint8_t)uAddr; + ds_memcpy_s(&uAddr, sizeof(uAddr), uNetw, sizeof(uNetw)); + return(uAddr); +} + +/* + HostName Cache functions +*/ + +/*F********************************************************************************/ +/*! + \Function SocketHostnameCacheCreate + + \Description + Create short-term hostname (DNS) cache + + \Input iMemGroup - memgroup to alloc/free with + \Input *pMemGroupUserData - memgroup user data to alloc/free with + + \Output + SocketHostnameCacheT * - hostname cache or NULL on failure + + \Version 10/09/2013 (jbrookes) +*/ +/********************************************************************************F*/ +SocketHostnameCacheT *SocketHostnameCacheCreate(int32_t iMemGroup, void *pMemGroupUserData) +{ + const int32_t iMaxEntries = 16; + int32_t iCacheSize = sizeof(SocketHostnameCacheT) + (iMaxEntries * sizeof(SocketHostnameCacheEntryT)); + SocketHostnameCacheT *pCache; + + // alloc and init cache + if ((pCache = DirtyMemAlloc(iCacheSize, SOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynet: could not alloc %d bytes for hostname cache\n", iCacheSize)); + return(NULL); + } + ds_memclr(pCache, iCacheSize); + + // set base info + pCache->iMaxEntries = iMaxEntries; + pCache->iMemGroup = iMemGroup; + pCache->pMemGroupUserData = pMemGroupUserData; + + // initialize crit + NetCritInit2(&pCache->Crit, "HostnameCache", NETCRIT_OPTION_SINGLETHREADENABLE); + + // return to caller + return(pCache); +} + +/*F********************************************************************************/ +/*! + \Function SocketHostnameCacheDestroy + + \Description + Destroy short-term hostname (DNS) cache + + \Input *pCache - hostname cache + + \Version 10/09/2013 (jbrookes) +*/ +/********************************************************************************F*/ +void SocketHostnameCacheDestroy(SocketHostnameCacheT *pCache) +{ + NetCritKill(&pCache->Crit); + DirtyMemFree(pCache, SOCKET_MEMID, pCache->iMemGroup, pCache->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function SocketHostnameCacheAdd + + \Description + Add hostname and address to hostname cache + + \Input *pCache - hostname cache + \Input *pStrHost - hostname to add + \Input uAddress - address of hostname + \Input iVerbose - debug level + + \Version 10/09/2013 (jbrookes) +*/ +/********************************************************************************F*/ +void SocketHostnameCacheAdd(SocketHostnameCacheT *pCache, const char *pStrHost, uint32_t uAddress, int32_t iVerbose) +{ + SocketHostnameCacheEntryT *pCacheEntry; + int32_t iCacheIdx; + + // if we're already in the cache, bail + if (SocketHostnameCacheGet(pCache, pStrHost, 0) != 0) + { + return; + } + + // scan cache for an open entry + NetCritEnter(&pCache->Crit); + for (iCacheIdx = 0; iCacheIdx < pCache->iMaxEntries; iCacheIdx += 1) + { + pCacheEntry = &pCache->CacheEntries[iCacheIdx]; + if (pCacheEntry->uAddress == 0) + { + NetPrintfVerbose((iVerbose, 1, "dirtynet: adding hostname cache entry %s/%a\n", pStrHost, uAddress)); + ds_strnzcpy(pCacheEntry->strDnsName, pStrHost, sizeof(pCacheEntry->strDnsName)); + pCacheEntry->uAddress = uAddress; + pCacheEntry->uTimer = NetTick(); + break; + } + } + NetCritLeave(&pCache->Crit); +} + +/*F********************************************************************************/ +/*! + \Function SocketHostnameCacheGet + + \Description + Get address for hostname from cache, if available. + + \Input *pCache - hostname cache + \Input *pStrHost - hostname to add + \Input iVerbose - debug level + + \Output + uint32_t - address for hostname, or zero if not in cache + + \Version 10/09/2013 (jbrookes) +*/ +/********************************************************************************F*/ +uint32_t SocketHostnameCacheGet(SocketHostnameCacheT *pCache, const char *pStrHost, int32_t iVerbose) +{ + SocketHostnameCacheEntryT *pCacheEntry; + uint32_t uAddress; + int32_t iCacheIdx; + + // scan cache for dns entry + NetCritEnter(&pCache->Crit); + for (iCacheIdx = 0, uAddress = 0; iCacheIdx < pCache->iMaxEntries; iCacheIdx += 1) + { + pCacheEntry = &pCache->CacheEntries[iCacheIdx]; + // skip empty entries + if (pCacheEntry->strDnsName[0] == '\0') + { + continue; + } + // check for entry we want + if (!strcmp(pCacheEntry->strDnsName, pStrHost)) + { + NetPrintfVerbose((iVerbose, 0, "dirtynet: %s=%a [cache]\n", pCacheEntry->strDnsName, pCacheEntry->uAddress)); + uAddress = pCache->CacheEntries[iCacheIdx].uAddress; + break; + } + } + NetCritLeave(&pCache->Crit); + return(uAddress); +} + +/*F********************************************************************************/ +/*! + \Function SocketHostnameCacheDel + + \Description + Remove hostname cache entry from the cache, if it exists. pStrHost is + checked if non-NULL and uAddress is checked if non-zero (note that both + can be checked if desired). + + \Input *pCache - hostname cache + \Input *pStrHost - hostname of cache entry to delete, or NULL + \Input uAddress - address of cache entry to delete, or zero + \Input iVerbose - debug level + + \Version 09/23/2016 (jbrookes) +*/ +/********************************************************************************F*/ +void SocketHostnameCacheDel(SocketHostnameCacheT *pCache, const char *pStrHost, uint32_t uAddress, int32_t iVerbose) +{ + SocketHostnameCacheEntryT *pCacheEntry; + int32_t iCacheIdx; + + // scan cache for dns entry + NetCritEnter(&pCache->Crit); + for (iCacheIdx = 0; iCacheIdx < pCache->iMaxEntries; iCacheIdx += 1) + { + pCacheEntry = &pCache->CacheEntries[iCacheIdx]; + // skip empty entries + if (pCacheEntry->strDnsName[0] == '\0') + { + continue; + } + // check for hostname match + if ((pStrHost != NULL) && strcmp(pCacheEntry->strDnsName, pStrHost)) + { + continue; + } + // check for address match + if ((uAddress != 0) && (pCacheEntry->uAddress != uAddress)) + { + continue; + } + // found a match; delete the entry + NetPrintfVerbose((iVerbose, 1, "dirtynet: deleting hostname cache entry %s/%a\n", pCacheEntry->strDnsName, pCacheEntry->uAddress)); + ds_memclr(pCacheEntry, sizeof(*pCacheEntry)); + break; + } + NetCritLeave(&pCache->Crit); +} + +/*F********************************************************************************/ +/*! + \Function SocketHostnameCacheProcess + + \Description + Process the hostname cache, clear expired cache entries + + \Input *pCache - hostname cache + \Input iVerbose - debug level + + \Version 04/23/2019 (eesponda) +*/ +/********************************************************************************F*/ +void SocketHostnameCacheProcess(SocketHostnameCacheT *pCache, int32_t iVerbose) +{ + SocketHostnameCacheEntryT *pCacheEntry; + uint32_t uCurTick; + int32_t iCacheIdx; + + if (!NetCritTry(&pCache->Crit)) + { + return; + } + + // scan cache for dns entry + for (iCacheIdx = 0, uCurTick = NetTick(); iCacheIdx < pCache->iMaxEntries; iCacheIdx += 1) + { + pCacheEntry = &pCache->CacheEntries[iCacheIdx]; + // skip empty entries + if (pCacheEntry->strDnsName[0] == '\0') + { + continue; + } + // check for expiration + if (NetTickDiff(uCurTick, pCacheEntry->uTimer) > DIRTYNET_HOSTNAMECACHELIFETIME) + { + NetPrintfVerbose((iVerbose, 1, "dirtynet: expiring hostname cache entry %s/%a\n", pCacheEntry->strDnsName, pCacheEntry->uAddress)); + ds_memclr(pCacheEntry, sizeof(*pCacheEntry)); + } + } + NetCritLeave(&pCache->Crit); +} + +/*F********************************************************************************/ +/*! + \Function SocketHostnameAddRef + + \Description + Check for in-progress DNS requests we can piggyback on, instead of issuing + a new request. + + \Input **ppHostList - list of active lookups + \Input *pHost - current lookup + \Input bUseRef - force using a new entry if bUseRef=FALSE (should normally be TRUE) + + \Output + HostentT * - Pre-existing DNS request we have refcounted, or NULL + + \Version 01/16/2014 (jbrookes) +*/ +/********************************************************************************F*/ +HostentT *SocketHostnameAddRef(HostentT **ppHostList, HostentT *pHost, uint8_t bUseRef) +{ + HostentT *pHost2 = NULL; + + // look for an in-progress refcounted lookup + NetCritEnter(NULL); + if (bUseRef == TRUE) + { + for (pHost2 = *ppHostList; pHost2 != NULL; pHost2 = pHost2->pNext) + { + if (!strcmp(pHost2->name, pHost->name) && !pHost2->done) + { + break; + } + } + } + + // new lookup, so add to list + if (pHost2 == NULL) + { + pHost->refcount = 1; + pHost->pNext = *ppHostList; + *ppHostList = pHost; + pHost = NULL; + } + else // found an in-progress lookup, so piggyback on it + { + pHost = pHost2; + pHost->refcount += 1; + NetPrintfVerbose((SocketInfo(NULL, 'spam', 0, NULL, 0), 0, "dirtynet: %s lookup refcounted (%d refs)\n", pHost->name, pHost->refcount)); + } + + NetCritLeave(NULL); + return(pHost); +} + +/*F********************************************************************************/ +/*! + \Function SocketHostnameListProcess + + \Description + Process list of in-progress DNS requests, disposing of those that are + completed and no longer referenced. + + \Input **ppHostList - list of active lookups + \Input iMemGroup - memgroup hostname lookup records are allocated with + \Input *pMemGroupUserData - memgroup userdata hostname lookup records are allocated with + + \Notes + This function is called from the SocketIdle thread, which is already guarded + by the global critical section. It is therefore assumed that it does not + need to be explicitly guarded here. + + \Version 01/16/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void SocketHostnameListProcess(HostentT **ppHostList, int32_t iMemGroup, void *pMemGroupUserData) +{ + HostentT **ppHost; + for (ppHost = ppHostList; *ppHost != NULL;) + { + if ((*ppHost)->refcount == 0) + { + HostentT *pHost = *ppHost; + *ppHost = (*ppHost)->pNext; + DirtyMemFree(pHost, SOCKET_MEMID, iMemGroup, pMemGroupUserData); + } + else + { + ppHost = &(*ppHost)->pNext; + } + } +} + +/* + Packet Queue functions +*/ + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueCreate + + \Description + Create a packet queue + + \Input iMaxPackets - size of queue, in packets (max 127) + \Input iMemGroup - memgroup to alloc/free with + \Input *pMemGroupUserData - memgroup user data to alloc/free with + + \Output + SocketPacketQueueT * - packet queue, or NULL on failure + + \Version 02/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +SocketPacketQueueT *SocketPacketQueueCreate(int32_t iMaxPackets, int32_t iMemGroup, void *pMemGroupUserData) +{ + SocketPacketQueueT *pPacketQueue; + int32_t iQueueSize; + + // enforce min/max queue sizes + iMaxPackets = DS_CLAMP(iMaxPackets, 1, DIRTYNET_PACKETQUEUEMAX); + + // calculate memory required for queue + iQueueSize = sizeof(*pPacketQueue) + ((iMaxPackets-1) * sizeof(pPacketQueue->aPacketQueue[0])); + + // alloc and init queue + if ((pPacketQueue = DirtyMemAlloc(iQueueSize, SOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynet: could not alloc %d bytes for packet queue\n", iQueueSize)); + return(NULL); + } + ds_memclr(pPacketQueue, iQueueSize); + + // set base info + pPacketQueue->iNumPackets = 0; + pPacketQueue->iMaxPackets = iMaxPackets; + pPacketQueue->iMemGroup = iMemGroup; + pPacketQueue->pMemGroupUserData = pMemGroupUserData; + + // latency/packet loss simulation setup + pPacketQueue->uLatencyTime = NetTick(); + + //$$temp - testing + //pPacketQueue->uLatency = 100; + //pPacketQueue->uDeviation = 5; + //pPacketQueue->uPacketLoss = 5*65536; + + // return queue to caller + return(pPacketQueue); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueDestroy + + \Description + Destroy packet queue + + \Input *pPacketQueue - packet queue to destroy + + \Version 02/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void SocketPacketQueueDestroy(SocketPacketQueueT *pPacketQueue) +{ + DirtyMemFree(pPacketQueue, SOCKET_MEMID, pPacketQueue->iMemGroup, pPacketQueue->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueResize + + \Description + Resize a packet queue, if the max packet size is different + + \Input *pPacketQueue - packet queue to resize (may be null) + \Input iMaxPackets - new size of queue, in packets (max 127) + \Input iMemGroup - memgroup to alloc/free with + \Input *pMemGroupUserData - memgroup user data to alloc/free with + + \Output + SocketPacketQueueT * - pointer to resized packet queue + + \Notes + If the new max queue size is less than the number of packets in the current + queue, packets will be overwritten in the usual manner (older discarded in + favor of newer). + + \Version 05/30/2014 (jbrookes) +*/ +/********************************************************************************F*/ +SocketPacketQueueT *SocketPacketQueueResize(SocketPacketQueueT *pPacketQueue, int32_t iMaxPackets, int32_t iMemGroup, void *pMemGroupUserData) +{ + uint8_t aPacketData[SOCKET_MAXUDPRECV]; + SocketPacketQueueT *pNewPacketQueue; + struct sockaddr PacketAddr; + int32_t iPacketSize; + + // enforce min/max queue sizes + iMaxPackets = DS_CLAMP(iMaxPackets, 1, DIRTYNET_PACKETQUEUEMAX); + + // if we have a queue and it's already the right size, return it + if ((pPacketQueue != NULL) && (pPacketQueue->iMaxPackets == iMaxPackets)) + { + return(pPacketQueue); + } + + // create new queue + NetPrintf(("dirtynet: [%p] re-creating socket packet queue with %d max packets\n", pPacketQueue, iMaxPackets)); + if ((pNewPacketQueue = SocketPacketQueueCreate(iMaxPackets, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynet: could not allocate new packet queue\n")); + return(pPacketQueue); + } + + // copy old data (if any) over, and destroy the old packet queue + if (pPacketQueue != NULL) + { + while ((iPacketSize = SocketPacketQueueRem(pPacketQueue, aPacketData, sizeof(aPacketData), &PacketAddr)) > 0) + { + SocketPacketQueueAdd(pNewPacketQueue, aPacketData, iPacketSize, &PacketAddr); + } + SocketPacketQueueDestroy(pPacketQueue); + } + + // return resized queue to caller + return(pNewPacketQueue); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueAdd + + \Description + Add a packet to packet queue + + \Input *pPacketQueue - packet queue to add to + \Input *pPacketData - packet data to add to queue + \Input iPacketSize - size of packet data + \Input *pPacketAddr - remote address associated with packet + + \Output + int32_t - >=0: number of bytes buffered, -1: packet too large + + \Version 07/28/2020 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueAdd(SocketPacketQueueT *pPacketQueue, const uint8_t *pPacketData, int32_t iPacketSize, struct sockaddr *pPacketAddr) +{ + return(SocketPacketQueueAdd2(pPacketQueue, pPacketData, iPacketSize, pPacketAddr, FALSE)); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueAdd2 + + \Description + Add a packet to packet queue + + \Input *pPacketQueue - packet queue to add to + \Input *pPacketData - packet data to add to queue + \Input iPacketSize - size of packet data + \Input *pPacketAddr - remote address associated with packet + \Input bPartialAllowed - allow consuming only a portion of the submitted data (NX only) + + \Output + int32_t - >=0: number of bytes buffered, -1: packet too large + + \Version 02/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueAdd2(SocketPacketQueueT *pPacketQueue, const uint8_t *pPacketData, int32_t iPacketSize, struct sockaddr *pPacketAddr, uint32_t bPartialAllowed) +{ + SocketPacketQueueEntryT *pQueueEntry; + + if (bPartialAllowed == FALSE) + { + // reject if packet data is too large + if (iPacketSize > SOCKET_MAXUDPRECV) + { + NetPrintf(("dirtynet: [%p] packet too large to add to queue\n", pPacketQueue)); + return(-1); + } + } + + // if queue is full, overwrite oldest member + if (pPacketQueue->iNumPackets == pPacketQueue->iMaxPackets) + { + NetPrintf(("dirtynet: [%p] add to full queue; oldest entry will be overwritten\n", pPacketQueue)); + pPacketQueue->iPacketHead = (pPacketQueue->iPacketHead + 1) % pPacketQueue->iMaxPackets; + pPacketQueue->uPacketDrop += 1; + } + else + { + pPacketQueue->iNumPackets += 1; + if (pPacketQueue->uPacketMax < (unsigned)pPacketQueue->iNumPackets) + { + pPacketQueue->uPacketMax = (unsigned)pPacketQueue->iNumPackets; + } + } + // set packet entry + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] [%d] packet queue entry added (%d entries)\n", pPacketQueue, pPacketQueue->iPacketTail, pPacketQueue->iNumPackets)); + pQueueEntry = &pPacketQueue->aPacketQueue[pPacketQueue->iPacketTail]; + + if (iPacketSize > (signed)sizeof(pQueueEntry->aPacketData)) + { + iPacketSize = sizeof(pQueueEntry->aPacketData); + } + ds_memcpy_s(pQueueEntry->aPacketData, sizeof(pQueueEntry->aPacketData), pPacketData, iPacketSize); + + if (pPacketAddr) + { + ds_memcpy(&pQueueEntry->PacketAddr, pPacketAddr, sizeof(pQueueEntry->PacketAddr)); + } + else + { + ds_memclr(&pQueueEntry->PacketAddr, sizeof(pQueueEntry->PacketAddr)); + pQueueEntry->PacketAddr.sa_family = AF_UNSPEC; + } + + pQueueEntry->iPacketSize = iPacketSize; + pQueueEntry->uPacketTick = NetTick(); + // add to queue + pPacketQueue->iPacketTail = (pPacketQueue->iPacketTail + 1) % pPacketQueue->iMaxPackets; + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] head=%d tail=%d\n", pPacketQueue, pPacketQueue->iPacketHead, pPacketQueue->iPacketTail)); + // return number of bytes buffered + return(pQueueEntry->iPacketSize); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueAlloc + + \Description + Alloc a packet queue entry. This is used when receiving data directly into + the packet queue data buffer. + + \Input *pPacketQueue - packet queue to alloc entry from + + \Output + SocketPacketQueueEntryT * - packet queue entry + + \Version 02/24/2014 (jbrookes) +*/ +/********************************************************************************F*/ +SocketPacketQueueEntryT *SocketPacketQueueAlloc(SocketPacketQueueT *pPacketQueue) +{ + SocketPacketQueueEntryT *pQueueEntry; + // if queue is full, alloc over oldest member + if (pPacketQueue->iNumPackets == pPacketQueue->iMaxPackets) + { + // print a warning if we're altering a slot that is in use + if (pPacketQueue->aPacketQueue[pPacketQueue->iPacketTail].iPacketSize != -1) + { + NetPrintf(("dirtynet: [%p] alloc to full queue; oldest entry will be overwritten\n", pPacketQueue)); + pPacketQueue->uPacketDrop += 1; + } + pPacketQueue->iPacketHead = (pPacketQueue->iPacketHead + 1) % pPacketQueue->iMaxPackets; + } + else + { + pPacketQueue->iNumPackets += 1; + if (pPacketQueue->uPacketMax < (unsigned)pPacketQueue->iNumPackets) + { + pPacketQueue->uPacketMax = (unsigned)pPacketQueue->iNumPackets; + } + } + // allocate queue entry + pQueueEntry = &pPacketQueue->aPacketQueue[pPacketQueue->iPacketTail]; + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] [%d] packet queue entry alloc (%d entries)\n", pPacketQueue, pPacketQueue->iPacketTail, pPacketQueue->iNumPackets)); + // mark packet as allocated + pQueueEntry->iPacketSize = -1; + // add to queue + pPacketQueue->iPacketTail = (pPacketQueue->iPacketTail + 1) % pPacketQueue->iMaxPackets; + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] head=%d tail=%d\n", pPacketQueue, pPacketQueue->iPacketHead, pPacketQueue->iPacketTail)); + return(pQueueEntry); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueAllocUndo + + \Description + Undo a recent call to SocketPacketQueueAlloc(). Cannot be used if packet + queue has been altered in between calling SocketPacketQueueAlloc() and + SocketPacketQueueAllocUndo() + + \Input *pPacketQueue - packet queue to undo alloc entry from + + \Version 09/04/2018 (mclouatre) +*/ +/********************************************************************************F*/ +void SocketPacketQueueAllocUndo(SocketPacketQueueT *pPacketQueue) +{ + pPacketQueue->iNumPackets -= 1; + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] [%d] recent alloc undone from tail (%d entries)\n", pPacketQueue, pPacketQueue->iPacketTail, pPacketQueue->iNumPackets)); + + // modular arithmetic is not used here to avoid complications when the substraction underflows + pPacketQueue->iPacketTail -= 1; + if (pPacketQueue->iPacketTail < 0) + { + pPacketQueue->iPacketTail = pPacketQueue->iMaxPackets - 1; + } + + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] head=%d tail=%d\n", pPacketQueue, pPacketQueue->iPacketHead, pPacketQueue->iPacketTail)); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueRem + + \Description + Remove a packet from packet queue + + \Input *pPacketQueue - packet queue to remove from + \Input *pPacketData - [out] storage for packet data or NULL + \Input iPacketSize - size of packet output data buffer + \Input *pPacketAddr - [out] storage for packet addr or NULL + + \Output + int32_t - positive=size of packet, zero=no packet, negative=failure + + \Notes + The packet data and address outputs are optional if you are just + trying to remove the entry at the head. + + \Version 02/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueRem(SocketPacketQueueT *pPacketQueue, uint8_t *pPacketData, int32_t iPacketSize, struct sockaddr *pPacketAddr) +{ + SocketPacketQueueEntryT *pQueueEntry; + uint32_t uCurTick = NetTick(); + + // nothing to do if queue is empty + if (pPacketQueue->iNumPackets == 0) + { + return(0); + } + // get head packet + pQueueEntry = &pPacketQueue->aPacketQueue[pPacketQueue->iPacketHead]; + // nothing to do if packet was allocated and has not been filled + if (pQueueEntry->iPacketSize < 0) + { + return(0); + } + + // apply simulated latency, if enabled + if (pPacketQueue->uLatency != 0) + { + // compare to current latency + if (NetTickDiff(uCurTick, pQueueEntry->uPacketTick) < (signed)pPacketQueue->uLatency + pPacketQueue->iDeviationTime) + { + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] latency=%d, deviation=%d\n", pPacketQueue, NetTickDiff(uCurTick, pPacketQueue->uLatencyTime), pPacketQueue->iDeviationTime)); + return(0); + } + + // update packet receive timestamp + SockaddrInSetMisc(&pQueueEntry->PacketAddr, uCurTick); + + // recalculate deviation + pPacketQueue->iDeviationTime = (signed)NetRand(pPacketQueue->uDeviation*2) - (signed)pPacketQueue->uDeviation; + } + + // get packet size to copy (will truncate if output buffer is too small) + if (iPacketSize > pQueueEntry->iPacketSize) + { + iPacketSize = pQueueEntry->iPacketSize; + } + // copy out packet data and source + if (pPacketData != NULL) + { + ds_memcpy(pPacketData, pQueueEntry->aPacketData, iPacketSize); + } + if (pPacketAddr != NULL) + { + ds_memcpy(pPacketAddr, &pQueueEntry->PacketAddr, sizeof(pQueueEntry->PacketAddr)); + } + // remove packet from queue + pPacketQueue->iNumPackets -= 1; + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] [%d] packet queue entry removed from head in %dms (%d entries)\n", pPacketQueue, pPacketQueue->iPacketHead, + NetTickDiff(NetTick(), pQueueEntry->uPacketTick), pPacketQueue->iNumPackets)); + pPacketQueue->iPacketHead = (pPacketQueue->iPacketHead + 1) % pPacketQueue->iMaxPackets; + NetPrintfVerbose((DIRTYNET_PACKETQUEUEDEBUG, 0, "dirtynet: [%p] head=%d tail=%d\n", pPacketQueue, pPacketQueue->iPacketHead, pPacketQueue->iPacketTail)); + + // simulate packet loss + if (pPacketQueue->uPacketLoss != 0) + { + uint32_t uRand = NetRand(100*65536); + if (uRand < pPacketQueue->uPacketLoss) + { + NetPrintf(("dirtynet: [%p] lost packet (rand=%d, comp=%d)!\n", pPacketQueue, uRand, pPacketQueue->uPacketLoss)); + return(0); + } + } + + // return success + return(pQueueEntry->iPacketSize); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueRemStream + + \Description + Remove a stream packet from packet queue + + \Input *pPacketQueue - packet queue to add to + \Input *pPacketData - [out] storage for packet data or NULL + \Input iPacketSize - size of packet output data buffer + + \Output + int32_t - positive=amount of data read, zero=no packet, negative=failure + + \Notes + The packet data output is optional if you are just trying to remove the entry + at the head. + + \Version 11/20/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueRemStream(SocketPacketQueueT *pPacketQueue, uint8_t *pPacketData, int32_t iPacketSize) +{ + SocketPacketQueueEntryT *pQueueEntry; + int32_t iPacketRead; + + // nothing to do if queue is empty + if (pPacketQueue->iNumPackets == 0) + { + return(0); + } + // get head packet + pQueueEntry = &pPacketQueue->aPacketQueue[pPacketQueue->iPacketHead]; + // nothing to do if packet was allocated and has not been filled + if (pQueueEntry->iPacketSize < 0) + { + return(0); + } + + // get packet size to copy + iPacketRead = DS_MIN(iPacketSize, pQueueEntry->iPacketSize); + + // copy the packet data and offset by amount read + if (pPacketData != NULL) + { + ds_memcpy(pPacketData, pQueueEntry->aPacketData, iPacketRead); + + pPacketData += iPacketRead; + iPacketSize -= iPacketRead; + } + + // if we copied less than the size of the entry adjust the entry as needed, otherwise remove packet from queue + if ((iPacketRead > 0) && (iPacketRead < pQueueEntry->iPacketSize)) + { + memmove(pQueueEntry->aPacketData, pQueueEntry->aPacketData+iPacketRead, pQueueEntry->iPacketSize-iPacketRead); + pQueueEntry->iPacketSize -= iPacketRead; + } + else + { + pPacketQueue->iNumPackets -= 1; + pPacketQueue->iPacketHead = (pPacketQueue->iPacketHead + 1) % pPacketQueue->iMaxPackets; + } + + // continue to copy if we still have space in the buffer + if (iPacketSize > 0) + { + iPacketRead += SocketPacketQueueRemStream(pPacketQueue, pPacketData, iPacketSize); + } + return(iPacketRead); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueGetHead + + \Description + Get pointers to data associated with queue head. + + \Input *pPacketQueue - packet queue to remove from + \Input **ppPacketData - [out] to be filled with pointer to packet data (can be NULL) + \Input *pPacketSize - [out] to be filled with size of packet data (can be NULL) + \Input **ppPacketAddr - [out] to be filled with pointer to packet addr (can be NULL) + + \Output + int32_t - positive=size of packet, zero=no packet + + \Version 08/07/2020 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueGetHead(SocketPacketQueueT *pPacketQueue, uint8_t **ppPacketData, int32_t *pPacketSize, struct sockaddr **ppPacketAddr) +{ + SocketPacketQueueEntryT *pQueueEntry; + + // nothing to do if queue is empty + if (pPacketQueue->iNumPackets == 0) + { + return(0); + } + + // get head packet + pQueueEntry = &pPacketQueue->aPacketQueue[pPacketQueue->iPacketHead]; + // nothing to do if packet was allocated and has not been filled + if (pQueueEntry->iPacketSize < 0) + { + return(0); + } + + // fill output variables + if (ppPacketData != NULL) + { + *ppPacketData = pQueueEntry->aPacketData; + } + if (pPacketSize != NULL) + { + *pPacketSize = pQueueEntry->iPacketSize; + } + if (ppPacketAddr != NULL) + { + *ppPacketAddr = &pQueueEntry->PacketAddr; + } + + // return success + return(pQueueEntry->iPacketSize); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueTouchHead + + \Description + Update queue's head entry such that it no longer includes successfully + consumed portion of data. + + \Input *pPacketQueue - packet queue to remove from + \Input iConsumedSize - amount of data consumed from head entry (in bytes) + + \Output + int32_t - 0 success, negative: error + + \Version 08/07/2020 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueTouchHead(SocketPacketQueueT *pPacketQueue, int32_t iConsumedSize) +{ + SocketPacketQueueEntryT *pQueueEntry; + + // nothing to do if queue is empty + if (pPacketQueue->iNumPackets == 0) + { + return(-1); + } + + // get head packet + pQueueEntry = &pPacketQueue->aPacketQueue[pPacketQueue->iPacketHead]; + // nothing to do if packet was allocated and has not been filled + if (pQueueEntry->iPacketSize < 0) + { + return(-2); + } + + // update size of packet queue entry + pQueueEntry->iPacketSize -= iConsumedSize; + + // remove consumed data from data buffer + memmove(pQueueEntry->aPacketData, pQueueEntry->aPacketData + iConsumedSize, pQueueEntry->iPacketSize); + + // return success + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueControl + + \Description + Control socket packet queue options + + \Input *pPacketQueue - packet queue control function; different selectors + control different behaviors + \Input iControl - control selector + \Input iValue - selector specific + + \Output + int32_t - selector result + + \Notes + iControl can be one of the following: + + \verbatim + 'pdev' - set simulated packet deviation + 'plat' - set simulated packet latency + 'plos' - set simulated packet loss + \endverbatim + + \Version 10/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueControl(SocketPacketQueueT *pPacketQueue, int32_t iControl, int32_t iValue) +{ + if (iControl == 'pdev') + { + NetPrintf(("dirtynet: setting simulated packet deviation=%dms\n", iValue)); + pPacketQueue->uDeviation = iValue; + return(0); + } + if (iControl == 'plat') + { + NetPrintf(("dirtynet: setting simulated packet latency=%dms\n", iValue)); + pPacketQueue->uLatency = iValue; + return(0); + } + if (iControl == 'plos') + { + NetPrintf(("dirtynet: setting simulated packet loss to %d.%d\n", iValue >> 16, iValue & 0xffff)); + pPacketQueue->uPacketLoss = iValue; + return(0); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function SocketPacketQueueStatus + + \Description + Get status of socket packet queue + + \Input *pPacketQueue - packet queue to get status of + \Input iStatus - status selector + + \Output + int32_t - selector result + + \Notes + iStatus can be one of the following: + + \verbatim + 'pful' - TRUE if queue is full, FALSE otherwise + 'pdrp' - number of packets overwritten due to queue overflow + 'pmax' - maximum number of packets in queue (high water) + 'pnum' - number of packets in queue + 'psiz' - queue size (in packets) + \endverbatim + + \Version 10/20/2015 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketPacketQueueStatus(SocketPacketQueueT *pPacketQueue, int32_t iStatus) +{ + if (iStatus == 'pful') + { + return(pPacketQueue->iMaxPackets == pPacketQueue->iNumPackets ? TRUE : FALSE); + } + if (iStatus == 'pdrp') + { + return((signed)pPacketQueue->uPacketDrop); + } + if (iStatus == 'pmax') + { + return((signed)pPacketQueue->uPacketMax); + } + if (iStatus == 'pnum') + { + return(pPacketQueue->iNumPackets); + } + if (iStatus == 'psiz') + { + return(pPacketQueue->iMaxPackets); + } + // invalid selector + return(-1); +} + +/* + Rate functions +*/ + +/*F********************************************************************************/ +/*! + \Function SocketRateUpdate + + \Description + Update socket data rate estimation + + \Input *pRate - state used to store rate estimation data + \Input iData - amount of data being sent/recv + \Input *pOpName - indicates send or recv (for debug use only) + + \Notes + Rate estimation is based on a rolling 16-deep history of ~100ms samples + of data rate (the actual period may vary slightly based on update rate + and tick resolution) covering a total of ~1600-1700ms. We also keep track + of the rate we are called at (excluding multiple calls within the same + tick) so we can estimate how much data we need to have sent by the next + time we are called. This is important because we will always be shooting + lower than our cap if we don't consider this factor, and the amount we + are shooting lower by increases the slower the update rate. + + \Version 08/20/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void SocketRateUpdate(SocketRateT *pRate, int32_t iData, const char *pOpName) +{ + const uint32_t uMaxIndex = (unsigned)(sizeof(pRate->aDataHist)/sizeof(pRate->aDataHist[0])); + uint32_t uCurTick = NetTick(), uOldTick; + uint32_t uIndex, uCallRate, uCallSum, uDataSum; + int32_t iTickDiff; + + // reserve tick=0 as uninitialized + if (uCurTick == 0) + { + uCurTick = 1; + } + // initialize update tick counters + if (pRate->uLastTick == 0) + { + pRate->uLastTick = NetTickDiff(uCurTick, 2); + // initialize rate tick counter to current-100 so as to force immediate history update + pRate->uLastRateTick = NetTickDiff(uCurTick, 100); + // start off at max rate + pRate->uCurRate = pRate->uNextRate = pRate->uMaxRate; + } + // exclude error results + if (iData < 0) + { + return; + } + + // update the data + pRate->aDataHist[pRate->uDataIndex] += iData; + /* update the call count, only only if time has elapsed since our last call, since we + want the true update rate) */ + if (NetTickDiff(uCurTick, pRate->uLastTick) > 1) + { + pRate->aCallHist[pRate->uDataIndex] += 1; + } + // update last update tick + pRate->uLastTick = uCurTick; + /* update timestamp, but only if it hasn't been updated already. we do this so we can + correctly update the rate calculation continuously with the current sample */ + if (pRate->aTickHist[pRate->uDataIndex] == 0) + { + pRate->aTickHist[pRate->uDataIndex] = uCurTick; + } + + // get oldest tick & sum recorded data & callcounts + for (uIndex = (pRate->uDataIndex + 1) % uMaxIndex, uOldTick = 0, uCallSum = 0, uDataSum = 0; ; uIndex = (uIndex + 1) % uMaxIndex) + { + // skip uninitialized tick values + if ((uOldTick == 0) && (pRate->aTickHist[uIndex] != 0)) + { + uOldTick = pRate->aTickHist[uIndex]; + } + // update call sum + uCallSum += pRate->aCallHist[uIndex]; + // update data sum + uDataSum += pRate->aDataHist[uIndex]; + // quit when we've hit every entry + if (uIndex == pRate->uDataIndex) + { + break; + } + } + + // update rate estimation + if ((iTickDiff = NetTickDiff(uCurTick, uOldTick)) > 0) + { + // calculate call rate + uCallRate = (uCallSum > 0) ? iTickDiff/uCallSum : 0; + // update current rate estimation + pRate->uCurRate = (uDataSum * 1000) / iTickDiff; + // update next rate estimation (fudge slightly by 2x as it gives us better tracking to our desired rate) + pRate->uNextRate = (uDataSum * 1000) / (iTickDiff+(uCallRate*2)); + + #if DIRTYNET_RATEDEBUG + if (pRate->uCurRate != 0) + { + NetPrintf(("dirtynet: [%p] rate=%4.2fkb nextrate=%4.2f callrate=%dms tickdiff=%d tick=%d\n", pRate, ((float)pRate->uCurRate)/1024.0f, ((float)pRate->uNextRate)/1024.0f, uCallRate, iTickDiff, uCurTick)); + } + #endif + } + + // move to next slot in history every 100ms + if (NetTickDiff(uCurTick, pRate->uLastRateTick) >= 100) + { + pRate->uDataIndex = (pRate->uDataIndex + 1) % uMaxIndex; + pRate->aDataHist[pRate->uDataIndex] = 0; + pRate->aTickHist[pRate->uDataIndex] = 0; + pRate->aCallHist[pRate->uDataIndex] = 0; + pRate->uLastRateTick = uCurTick; + + #if DIRTYNET_RATEDEBUG + if (pRate->uCurRate != 0) + { + NetPrintf(("dirtynet: [%p] %s=%5d rate=%4.2fkb/s indx=%2d tick=%08x diff=%d\n", pRate, pOpName, uDataSum, ((float)pRate->uCurRate)/1024.0f, pRate->uDataIndex, uCurTick, iTickDiff)); + } + #endif + } +} + +/*F********************************************************************************/ +/*! + \Function SocketRateThrottle + + \Description + Throttles data size to send or recv based on calculated data rate and + configured max rate. + + \Input *pRate - state used to calculate rate + \Input iSockType - socket type (SOCK_DGRAM, SOCK_STREAM, etc) + \Input iData - amount of data being sent/recv + \Input *pOpName - indicates send or recv (for debug use only) + + \Output + int32_t - amount of data to send/recv + + \Version 08/20/2014 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketRateThrottle(SocketRateT *pRate, int32_t iSockType, int32_t iData, const char *pOpName) +{ + int32_t iRateDiff; + + // max rate of zero means rate throttling is disabled + if ((pRate->uMaxRate == 0) || (iSockType != SOCK_STREAM)) + { + return(iData); + } + + // enforce min max update rate + if (pRate->uMaxRate < DIRTYNET_MIN_THROTTLE_RATE) + { + NetPrintf(("dirtynet: [%p] clamping max rate throttle of %d to %d", pRate->uMaxRate, DIRTYNET_MIN_THROTTLE_RATE)); + pRate->uMaxRate = DIRTYNET_MIN_THROTTLE_RATE; + } + + // if we're exceeding the max rate, update rate estimation and return no data + if ((iRateDiff = pRate->uMaxRate - pRate->uNextRate) <= 0) + { + NetPrintfVerbose((DIRTYNET_RATEDEBUG, 0, "dirtynet: [%p] exceeding max rate; clamping to zero\n", pRate)); + iData = 0; + } + else if (iData > iRateDiff) // return a limited amount of data so as not to exceed max rate + { + /* clamp in multiples of the typical TCP maximum segment size, so as not to generate fragmented packets + note that the TCP maximum segment size is equal to our minimum throttle rate, otherwise setting the + uMaxRate to less than this value would result in iData always being equal to 0. */ + iData = (iRateDiff / DIRTYNET_MIN_THROTTLE_RATE) * DIRTYNET_MIN_THROTTLE_RATE; + NetPrintfVerbose((DIRTYNET_RATEDEBUG, 0, "dirtynet: [%p] exceeding max rate; clamping to %d bytes from %d bytes\n", pRate, iRateDiff, pRate->uMaxRate - pRate->uNextRate)); + } + + // if we are returning no data, update the rate as the caller won't + if (iData == 0) + { + SocketRateUpdate(pRate, 0, pOpName); + } + + return(iData); +} + +/* + Send Callback functions +*/ + +/*F********************************************************************************/ +/*! + \Function SocketSendCallbackAdd + + \Description + Register a new socket send callback + + \Input aCbEntries - collection of callbacks to add to + \Input *pCbEntry - entry to be added + + \Output + int32_t - zero=success; negative=failure + + \Version 07/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t SocketSendCallbackAdd(SocketSendCallbackEntryT aCbEntries[], SocketSendCallbackEntryT *pCbEntry) +{ + int32_t iRetCode = -1; // default to failure + int32_t iEntryIndex; + + for(iEntryIndex = 0; iEntryIndex < SOCKET_MAXSENDCALLBACKS; iEntryIndex++) + { + if (aCbEntries[iEntryIndex].pSendCallback == NULL) + { + aCbEntries[iEntryIndex].pSendCallback = pCbEntry->pSendCallback; + aCbEntries[iEntryIndex].pSendCallref = pCbEntry->pSendCallref; + + NetPrintf(("dirtynet: adding send callback (%p, %p)\n", pCbEntry->pSendCallback, pCbEntry->pSendCallref)); + + iRetCode = 0; // success + + break; + } + } + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function SocketSendCallbackRem + + \Description + Unregister a socket send callback that was already registered. + + \Input aCbEntries - collection of callbacks to remove from + \Input *pCbEntry - entry to be removed + + \Output + int32_t - zero=success; negative=failure + + \Version 07/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t SocketSendCallbackRem(SocketSendCallbackEntryT aCbEntries[], SocketSendCallbackEntryT *pCbEntry) +{ + int32_t iRetCode = -1; // default to failure + int32_t iEntryIndex; + + for(iEntryIndex = 0; iEntryIndex < SOCKET_MAXSENDCALLBACKS; iEntryIndex++) + { + if (aCbEntries[iEntryIndex].pSendCallback == pCbEntry->pSendCallback && aCbEntries[iEntryIndex].pSendCallref == pCbEntry->pSendCallref) + { + NetPrintf(("dirtynet: removing send callback (%p, %p)\n", aCbEntries[iEntryIndex].pSendCallback, aCbEntries[iEntryIndex].pSendCallref)); + + aCbEntries[iEntryIndex].pSendCallback = NULL; + aCbEntries[iEntryIndex].pSendCallref = NULL; + + iRetCode = 0; // success + + break; + } + } + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function SocketSendCallbackInvoke + + \Description + Invoke all send callbacks in specified collection. Only one of the callback + is supposed to return "handled"... warn if not the case. + + \Input aCbEntries - collection of callbacks to be invoked + \Input pSocket - socket reference + \Input iType - socket type + \Input *pBuf - the data to be sent + \Input iLen - size of data + \Input *pTo - the address to send to (NULL=use connection address) + + \Output + int32_t - 0 = send not handled; >0 = send successfully handled (bytes sent); <0 = send handled but failed (SOCKERR_XXX) + + \Version 07/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t SocketSendCallbackInvoke(SocketSendCallbackEntryT aCbEntries[], SocketT *pSocket, int32_t iType, const char *pBuf, int32_t iLen, const struct sockaddr *pTo) +{ + int32_t iRetCode = 0; // default to send not handled + int32_t iResult; + int32_t iEntryIndex; + + for (iEntryIndex = 0; iEntryIndex < SOCKET_MAXSENDCALLBACKS; iEntryIndex++) + { + // we expect zero or one send callback to handle the send, more than one indicates an invalid condition + if (aCbEntries[iEntryIndex].pSendCallback != NULL) + { + if((iResult = aCbEntries[iEntryIndex].pSendCallback(pSocket, iType, (const uint8_t *)pBuf, iLen, pTo, aCbEntries[iEntryIndex].pSendCallref)) != 0) + { + if (iRetCode == 0) + { + iRetCode = iResult; + } + else + { + NetPrintf(("dirtynet: critical error - send handled by more than one callback (iEntryIndex = %d, iResult = %d)\n", iEntryIndex, iResult)); + } + } + } + } + + return(iRetCode); +} + +/* + + Addr map functions + +*/ +#ifndef DIRTYCODE_NX +/*F*************************************************************************************/ +/*! + \Function _Sockaddr6SetAddrV4Mapped + + \Description + Set a V4-mapped IPv6 in6_addr + + \Input *pAddr6 - [out] storage for IPv4-mapped IPv6 address + \Input uAddr4 - IPv4 address to map + + \Version 03/01/2016 (jbrookes) +*/ +/************************************************************************************F*/ +static void _Sockaddr6SetAddrV4Mapped(struct in6_addr *pAddr6, uint32_t uAddr4) +{ + ds_memclr(pAddr6, sizeof(*pAddr6)); + pAddr6->s6_addr[10] = 0xff; + pAddr6->s6_addr[11] = 0xff; + pAddr6->s6_addr[12] = (uint8_t)(uAddr4 >> 24); + pAddr6->s6_addr[13] = (uint8_t)(uAddr4 >> 16); + pAddr6->s6_addr[14] = (uint8_t)(uAddr4 >> 8); + pAddr6->s6_addr[15] = (uint8_t)(uAddr4); +} + +/*F*************************************************************************************/ +/*! + \Function _Sockaddr6SetV4Mapped + + \Description + Set a V4-mapped IPv6 sockaddr6 + + \Input *pResult6 - [out] storage for IPv4-mapped IPv6 sockaddr + \Input *pSource4 - IPv4 address to map + + \Version 03/01/2016 (jbrookes) +*/ +/************************************************************************************F*/ +static void _Sockaddr6SetV4Mapped(struct sockaddr_in6 *pResult6, const struct sockaddr *pSource4) +{ + pResult6->sin6_family = AF_INET6; + pResult6->sin6_port = SocketNtohs(SockaddrInGetPort(pSource4)); + pResult6->sin6_flowinfo = 0; + _Sockaddr6SetAddrV4Mapped(&pResult6->sin6_addr, SockaddrInGetAddr(pSource4)); + pResult6->sin6_scope_id = 0; +} + +/*F*************************************************************************************/ +/*! + \Function _SocketAddrMapAlloc + + \Description + Alloc (or re-alloc) address map entry list + + \Input *pAddrMap - address map + \Input iNumEntries - new entry list size + \Input iMemGroup - memory group + \Input *pMemGroupUserData - memory group user data + + \Output + int32_t - negative=error, zero=success + + \Version 04/17/2013 (jbrookes) +*/ +/************************************************************************************F*/ +static int32_t _SocketAddrMapAlloc(SocketAddrMapT *pAddrMap, int32_t iNumEntries, int32_t iMemGroup, void *pMemGroupUserData) +{ + int32_t iOldEntryListSize = 0, iNewEntryListSize = iNumEntries*sizeof(SocketAddrMapEntryT); + SocketAddrMapEntryT *pNewEntries; + + // allocate new map memory + if ((pNewEntries = (SocketAddrMapEntryT *)DirtyMemAlloc(iNewEntryListSize, SOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + return(-1); + } + // clear new map memory + ds_memclr(pNewEntries, iNewEntryListSize); + // copy previous map data + if (pAddrMap->pMapEntries != NULL) + { + iOldEntryListSize = pAddrMap->iNumEntries*sizeof(SocketAddrMapEntryT); + ds_memcpy(pNewEntries, pAddrMap->pMapEntries, iOldEntryListSize); + DirtyMemFree(pAddrMap->pMapEntries, SOCKET_MEMID, iMemGroup, pMemGroupUserData); + } + // update state + pAddrMap->iNumEntries = iNumEntries; + pAddrMap->pMapEntries = pNewEntries; + pAddrMap->iMemGroup = iMemGroup; + pAddrMap->pMemGroupUserData = pMemGroupUserData; + // return success + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketAddrMapGet + + \Description + Find a map entry, based on specified address + + \Input *pAddrMap - address map + \Input *pAddr - address to get entry for + \Input iAddrSize - size of address + + \Output + SockAddrMapEntryT * - pointer to map entry or NULL if not found + + \Version 04/17/2013 (jbrookes) +*/ +/************************************************************************************F*/ +static SocketAddrMapEntryT *_SocketAddrMapGet(const SocketAddrMapT *pAddrMap, const struct sockaddr *pAddr, int32_t iAddrSize) +{ + const struct sockaddr_in6 *pAddr6 = (const struct sockaddr_in6 *)pAddr; + SocketAddrMapEntryT *pMapEntry; + int32_t iMapEntry; + + for (iMapEntry = 0; iMapEntry < pAddrMap->iNumEntries; iMapEntry += 1) + { + pMapEntry = &pAddrMap->pMapEntries[iMapEntry]; + if ((pAddr->sa_family == AF_INET) && (pMapEntry->iVirtualAddress == SockaddrInGetAddr(pAddr))) + { + return(pMapEntry); + } + if ((pAddr->sa_family == AF_INET6) && (!memcmp(&pAddr6->sin6_addr, &pMapEntry->SockAddr6.sin6_addr, sizeof(pAddr6->sin6_addr)))) + { + return(pMapEntry); + } + } + return(NULL); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketAddrMapSet + + \Description + Initialize a map entry + + \Input *pAddrMap - address map + \Input *pMapEntry - entry to set + \Input *pAddr6 - IPv6 address to set + \Input iAddrSize - size of address + + \Version 04/17/2013 (jbrookes) +*/ +/************************************************************************************F*/ +static void _SocketAddrMapSet(SocketAddrMapT *pAddrMap, SocketAddrMapEntryT *pMapEntry, const struct sockaddr_in6 *pAddr6, int32_t iAddrSize) +{ + pMapEntry->iRefCount = 1; + pMapEntry->iVirtualAddress = pAddrMap->iNextVirtAddr; + pAddrMap->iNextVirtAddr = (pAddrMap->iNextVirtAddr + 1) & 0x00ffffff; + ds_memcpy(&pMapEntry->SockAddr6, pAddr6, iAddrSize); + NetPrintfVerbose((SocketInfo(NULL, 'spam', 0, NULL, 0), 1, "dirtynet: [%d] add map %A to %a\n", pMapEntry - pAddrMap->pMapEntries, pAddr6, pMapEntry->iVirtualAddress)); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketAddrMapRemap + + \Description + Alter the value of an initialized map entry + + \Input *pAddrMap - address map + \Input *pMapEntry - entry to set + \Input *pAddr6 - IPv6 address to set + \Input iAddrSize - size of address + + \Version 12/03/2015 (amakoukji) +*/ +/************************************************************************************F*/ +static void _SocketAddrMapRemap(const SocketAddrMapT *pAddrMap, SocketAddrMapEntryT *pMapEntry, const struct sockaddr_in6 *pAddr6, int32_t iAddrSize) +{ + if (memcmp(&pMapEntry->SockAddr6, pAddr6, iAddrSize) == 0) + { + NetPrintf(("dirtynet: [%d] attempt to remap %a to identical IPv6 address %A\n", pMapEntry - pAddrMap->pMapEntries, pMapEntry->iVirtualAddress, pAddr6)); + } + NetPrintfVerbose((SocketInfo(NULL, 'spam', 0, NULL, 0), 1, "dirtynet: [%d] remapped %A -> %A for virtual address %a\n", pMapEntry - pAddrMap->pMapEntries, &pMapEntry->SockAddr6, pAddr6, pMapEntry->iVirtualAddress)); + ds_memcpy(&pMapEntry->SockAddr6, pAddr6, iAddrSize); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketAddrMapDel + + \Description + Dereference (and clear, if no more references) a map entry + + \Input *pAddrMap - address map + \Input *pMapEntry - entry to del + + \Version 04/17/2013 (jbrookes) +*/ +/************************************************************************************F*/ +static void _SocketAddrMapDel(SocketAddrMapT *pAddrMap, SocketAddrMapEntryT *pMapEntry) +{ + NetPrintfVerbose((SocketInfo(NULL, 'spam', 0, NULL, 0), 1, "dirtynet: [%d] del map %A to %a (decremented refcount to %d)\n", + pMapEntry - pAddrMap->pMapEntries, &pMapEntry->SockAddr6, pMapEntry->iVirtualAddress, pMapEntry->iRefCount-1)); + if (--pMapEntry->iRefCount == 0) + { + ds_memclr(pMapEntry, sizeof(*pMapEntry)); + } +} + +/*F*************************************************************************************/ +/*! + \Function _SocketAddrMapAdd + + \Description + Add an IPv6 address to the address mapping table + + \Input *pAddrMap - address map + \Input *pAddr6 - address to add to the mapping table + \Input iAddrSize - size of address + + \Output + int32_t - SOCKMAP_ERROR=error, else virtual IPv4 address for newly mapped IPv6 address + + \Version 04/17/2013 (jbrookes) +*/ +/************************************************************************************F*/ +static int32_t _SocketAddrMapAdd(SocketAddrMapT *pAddrMap, const struct sockaddr_in6 *pAddr6, int32_t iAddrSize) +{ + int32_t iMapEntry; + + // find an empty slot + for (iMapEntry = 0; iMapEntry < pAddrMap->iNumEntries; iMapEntry += 1) + { + if (pAddrMap->pMapEntries[iMapEntry].iVirtualAddress == 0) + { + _SocketAddrMapSet(pAddrMap, &pAddrMap->pMapEntries[iMapEntry], pAddr6, iAddrSize); + return(pAddrMap->pMapEntries[iMapEntry].iVirtualAddress); + } + } + + // if no empty slot, realloc the array + if ((_SocketAddrMapAlloc(pAddrMap, pAddrMap->iNumEntries+8, pAddrMap->iMemGroup, pAddrMap->pMemGroupUserData)) < 0) + { + return(SOCKMAP_ERROR); + } + // try the add again + return(_SocketAddrMapAdd(pAddrMap, pAddr6, iAddrSize)); +} + +/*F*************************************************************************************/ +/*! + \Function SocketAddrMapInit + + \Description + Initialize a Socket Address Map + + \Input *pAddrMap - address map + \Input iMemGroup - memory group + \Input *pMemGroupUserData - memory group user data + + \Version 03/03/2016 (jbrookes) +*/ +/************************************************************************************F*/ +void SocketAddrMapInit(SocketAddrMapT *pAddrMap, int32_t iMemGroup, void *pMemGroupUserData) +{ + // set up address map + ds_memclr(pAddrMap, sizeof(*pAddrMap)); + pAddrMap->iMemGroup = iMemGroup; + pAddrMap->pMemGroupUserData = pMemGroupUserData; + // init ipv6 map virtual address + pAddrMap->iNextVirtAddr = NetTick() & 0x00ffffff; +} + +/*F*************************************************************************************/ +/*! + \Function SocketAddrMapShutdown + + \Description + Clean up a Socket Address Map + + \Input *pAddrMap - address map + + \Version 03/03/2016 (jbrookes) +*/ +/************************************************************************************F*/ +void SocketAddrMapShutdown(SocketAddrMapT *pAddrMap) +{ + if (pAddrMap->pMapEntries != NULL) + { + DirtyMemFree(pAddrMap->pMapEntries, SOCKET_MEMID, pAddrMap->iMemGroup, pAddrMap->pMemGroupUserData); + } +} + +/*F*************************************************************************************/ +/*! + \Function SocketAddrMapAddress + + \Description + Map an IPv6 address and return a virtual IPv4 address that can be used to + reference it. + + \Input *pAddrMap - address map + \Input *pAddr - address to add to the mapping table + \Input iAddrSize - size of address + + \Output + int32_t - SOCKMAP_ERROR=error, else virtual IPv4 address for newly mapped IPv6 address + + \Version 04/17/2013 (jbrookes) +*/ +/************************************************************************************F*/ +int32_t SocketAddrMapAddress(SocketAddrMapT *pAddrMap, const struct sockaddr *pAddr, int32_t iAddrSize) +{ + const struct sockaddr_in6 *pAddr6 = (const struct sockaddr_in6 *)pAddr; + SocketAddrMapEntryT *pMapEntry; + + // if IPv4, just return the IPv4 address directly, without mapping + if (pAddr->sa_family == AF_INET) + { + return(SockaddrInGetAddr(pAddr)); + } + // we only map ipv6 addresses + if ((pAddr->sa_family != AF_INET6) || (iAddrSize < (signed)sizeof(struct sockaddr_in6))) + { + NetPrintf(("dirtynet: can't map address of type %d size=%d\n", pAddr->sa_family, iAddrSize)); + return(SOCKMAP_ERROR); + } + // force address size to in6 (is this necessary??) + iAddrSize = sizeof(struct sockaddr_in6); + // if it's an IPv4-mapped IPv6 address, return the IPv4 address + if (_SockaddrIn6IsIPv4(pAddr6) || _SockaddrIn6IsZero(pAddr6)) + { + return(SockaddrIn6GetAddr4(pAddr6)); + } + // see if this address is already mapped + if ((pMapEntry = _SocketAddrMapGet(pAddrMap, pAddr, iAddrSize)) != NULL) + { + pMapEntry->iRefCount += 1; + NetPrintfVerbose((SocketInfo(NULL, 'spam', 0, NULL, 0), 1, "dirtynet: [%d] map %A to %a (incremented refcount to %d)\n", pMapEntry - pAddrMap->pMapEntries, &pMapEntry->SockAddr6, pMapEntry->iVirtualAddress, pMapEntry->iRefCount)); + return(pMapEntry->iVirtualAddress); + } + // add it to the map + return(_SocketAddrMapAdd(pAddrMap, pAddr6, iAddrSize)); +} + +/*F*************************************************************************************/ +/*! + \Function SocketAddrRemapAddress + + \Description + Remap an existing IPv6 address and return a virtual IPv4 address that can be used to + reference it. + + \Input *pAddrMap - address map + \Input *pOldAddr - address that should be in mapping table + \Input *pNewAddr - address to update to the mapping table + \Input iAddrSize - size of address + + \Output + int32_t - SOCKMAP_ERROR=error, else virtual IPv4 address for remapped IPv6 address + + \Version 12/03/2015 (amakoukji) +*/ +/************************************************************************************F*/ +int32_t SocketAddrRemapAddress(SocketAddrMapT *pAddrMap, const struct sockaddr *pOldAddr, const struct sockaddr *pNewAddr, int32_t iAddrSize) +{ + SocketAddrMapEntryT *pMapEntry; + + // if IPv4, just return the IPv4 address directly, without mapping + if (pNewAddr->sa_family == AF_INET) + { + return(SockaddrInGetAddr(pNewAddr)); + } + // we only map ipv6 addresses + if ((pNewAddr->sa_family != AF_INET6) || (iAddrSize < (signed)sizeof(struct sockaddr_in6))) + { + NetPrintf(("dirtynet: can't remap address of type %d size=%d\n", pNewAddr->sa_family, iAddrSize)); + return(SOCKMAP_ERROR); + } + iAddrSize = sizeof(struct sockaddr_in6); + // see if this address is already mapped + if ((pMapEntry = _SocketAddrMapGet(pAddrMap, (const struct sockaddr *)pOldAddr, iAddrSize)) != NULL) + { + NetPrintf(("dirtynet: attempting to remap virtual address %a, from %A to %A\n", pMapEntry->iVirtualAddress, pOldAddr, pNewAddr)); + _SocketAddrMapRemap(pAddrMap, pMapEntry, (const struct sockaddr_in6 *)pNewAddr, iAddrSize); + return(pMapEntry->iVirtualAddress); + } + + // did not find the address + NetPrintf(("dirtynet: attempt to remap unmapped address %A, attempting to add mapping instead\n", pNewAddr)); + return(SocketAddrMapAddress(pAddrMap, (const struct sockaddr *)pNewAddr, iAddrSize)); +} + +/*F*************************************************************************************/ +/*! + \Function SocketAddrUnmapAddress + + \Description + Removes an address mapping from the mapping table. + + \Input *pAddrMap - address map + \Input *pAddr - address to remove from the mapping table + \Input iAddrSize - size of address + + \Output + int32_t - negative=error, else zero + + \Version 04/18/2013 (jbrookes) +*/ +/************************************************************************************F*/ +int32_t SocketAddrUnmapAddress(SocketAddrMapT *pAddrMap, const struct sockaddr *pAddr, int32_t iAddrSize) +{ + SocketAddrMapEntryT *pMapEntry; + + // we only map ipv6 addresses + if ((pAddr->sa_family != AF_INET6) || (iAddrSize < (signed)sizeof(struct sockaddr_in6))) + { + NetPrintf(("dirtynet: can't unmap address of type %d size=%d\n", pAddr->sa_family, iAddrSize)); + return(-1); + } + iAddrSize = sizeof(struct sockaddr_in6); + // get the map entry for this address + if ((pMapEntry = _SocketAddrMapGet(pAddrMap, (const struct sockaddr *)pAddr, iAddrSize)) == NULL) + { + NetPrintf(("dirtynet: address unmap operation on an address not in the table\n")); + return(-2); + } + // unmap it + _SocketAddrMapDel(pAddrMap, pMapEntry); + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function SocketAddrMapGet + + \Description + Check if an address is in the address map, and if so return its mapped equivalent + + \Input *pAddrMap - address map + \Input *pResult - [out] storage for result + \Input *pSource - source address + \Input *pNameLen - [out] storage for result length + + \Output + struct sockaddr * - pointer to result address, or NULL if not found + + \Version 04/18/2013 (jbrookes) +*/ +/************************************************************************************F*/ +struct sockaddr *SocketAddrMapGet(const SocketAddrMapT *pAddrMap, struct sockaddr *pResult, const struct sockaddr *pSource, int32_t *pNameLen) +{ + SocketAddrMapEntryT *pMapEntry = _SocketAddrMapGet(pAddrMap, pSource, *pNameLen); + struct sockaddr *pReturn = NULL; + + if (pMapEntry != NULL) + { + if (pResult->sa_family == AF_INET) + { + SockaddrInit(pResult, AF_INET); + SockaddrInSetAddr(pResult, pMapEntry->iVirtualAddress); + pReturn = pResult; + } + else if (pResult->sa_family == AF_INET6) + { + ds_memcpy_s(pResult, sizeof(*pResult), &pMapEntry->SockAddr6, sizeof(pMapEntry->SockAddr6)); + pReturn = pResult; + } + } + + return(pReturn); +} + +/*F*************************************************************************************/ +/*! + \Function SocketAddrMapTranslate + + \Description + Translate the following: + - IPv4 real address to IPV4-mapped IPv6 address + - IPv4 virtual address to IPv6 address + - IPv6 address to IPv4 address + + \Input *pAddrMap - address map + \Input *pResult - [out] storage for translated result + \Input *pSource - source address + \Input *pNameLen - [out] storage for result length + + \Output + struct sockaddr * - pointer to resulting IPv6 address + + \Version 04/15/2013 (jbrookes) +*/ +/************************************************************************************F*/ +struct sockaddr *SocketAddrMapTranslate(const SocketAddrMapT *pAddrMap, struct sockaddr *pResult, const struct sockaddr *pSource, int32_t *pNameLen) +{ + SocketAddrMapEntryT *pMapEntry; + struct sockaddr *pReturn = NULL; + + // handle broken use cases where the family is not specified + if ((pSource->sa_family != AF_INET) && (pSource->sa_family != AF_INET6)) + { + NetPrintf(("dirtynet: unsupported source family %d in SocketAddrMapTranslate(); assuming AF_INET\n", pSource->sa_family)); + ((struct sockaddr *)pSource)->sa_family = AF_INET; + } + if ((pResult->sa_family != AF_INET) && (pResult->sa_family != AF_INET6)) + { + NetPrintf(("dirtynet: unsupported result family %d in SocketAddrMapTranslate(); assuming AF_INET\n", pResult->sa_family)); + pResult->sa_family = AF_INET; + } + + // handle IPv4->IPv6 + if ((pSource->sa_family == AF_INET) && (pResult->sa_family == AF_INET6)) + { + struct sockaddr_in6 *pResult6 = (struct sockaddr_in6 *)pResult; + uint32_t uAddr = SockaddrInGetAddr(pSource); + // handle a regular IPv4 address + if ((uAddr == 0) || ((uAddr >> 24) != 0)) + { + ds_memclr(pResult, sizeof(*pResult)); + _Sockaddr6SetV4Mapped(pResult6, pSource); + *pNameLen = sizeof(*pResult6); + pReturn = pResult; + } + // get IPv6 address from virtual IPv4 + else if ((pMapEntry = _SocketAddrMapGet(pAddrMap, pSource, *pNameLen)) != NULL) + { + ds_memcpy_s(pResult6, sizeof(*pResult6), &pMapEntry->SockAddr6, sizeof(pMapEntry->SockAddr6)); + pResult6->sin6_port = SocketNtohs(SockaddrInGetPort(pSource)); + *pNameLen = sizeof(*pResult6); + pReturn = pResult; + } + else + { + NetPrintf(("dirtynet: could not find address for virtual address %a\n", SockaddrInGetAddr(pSource))); + } + } + // handle IPv6->IPv4 + else if ((pSource->sa_family == AF_INET6) && (pResult->sa_family == AF_INET)) + { + struct sockaddr_in6 *pSource6 = (struct sockaddr_in6 *)pSource; + // translate IPv6 to virtual IPv4 address + if ((pMapEntry = _SocketAddrMapGet(pAddrMap, pSource, *pNameLen)) != NULL) + { + struct sockaddr *pResult4 = (struct sockaddr *)pResult; + SockaddrInit(pResult4, AF_INET); + SockaddrInSetAddr(pResult4, pMapEntry->iVirtualAddress); + SockaddrInSetPort(pResult4, SocketHtons(pSource6->sin6_port)); + *pNameLen = sizeof(*pResult4); + pReturn = pResult; + } + // is this an IPv4-mapped IPv6 address? + else if (_SockaddrIn6IsIPv4(pSource6)) + { + struct sockaddr *pResult4 = (struct sockaddr *)pResult; + uint32_t uAddress = SockaddrIn6GetAddr4(pSource6); + SockaddrInit(pResult4, AF_INET); + SockaddrInSetAddr(pResult4, uAddress); + SockaddrInSetPort(pResult4, SocketHtons(pSource6->sin6_port)); + *pNameLen = sizeof(*pResult4); + pReturn = pResult; + } + } + else + { + *pNameLen = (pResult->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); + ds_memcpy_s(pResult, *pNameLen, pSource, *pNameLen); + pReturn = pResult; + } + if (pReturn != NULL) + { + NetPrintfVerbose((SocketInfo(NULL, 'spam', 0, NULL, 0), 2, "dirtynet: map translate %A->%A\n", pSource, pReturn)); + } + else + { + NetPrintf(("dirtynet: map translate %A failed\n", pSource)); + pReturn = (struct sockaddr *)pSource; + *pNameLen = sizeof(*pSource); + } + return(pReturn); +} +#endif diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynetpriv.h b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynetpriv.h new file mode 100644 index 00000000..79bfb8fd --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtynetpriv.h @@ -0,0 +1,212 @@ +/*H*************************************************************************************/ +/*! + \File dirtynetpriv.h + + \Description + Private include for platform independent interface to network layers. + + \Copyright + Copyright (c) Electronic Arts 2002-2014 + + \Version 1.0 08/07/2014 (jbrookes) First version, split from dirtynet.h +*/ +/*************************************************************************************H*/ + +#ifndef _dirtynetpriv_h +#define _dirtynetpriv_h + +/*! +\Moduledef DirtyNetPriv DirtyNetPriv +\Modulemember DirtySock +*/ +//@{ + +/*** Include files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynet.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! socket hostname cache +typedef struct SocketHostnameCacheT SocketHostnameCacheT; + +//! socket packet queue +typedef struct SocketPacketQueueT SocketPacketQueueT; + +//! socket packet queue entry +typedef struct SocketPacketQueueEntryT +{ + int32_t iPacketSize; //!< packet size + struct sockaddr PacketAddr; //!< packet source + uint32_t uPacketTick; //!< tick packet was added to the queue + uint8_t aPacketData[SOCKET_MAXUDPRECV]; //!< packet data +} SocketPacketQueueEntryT; + +//! socket rate estimation +typedef struct SocketRateT +{ + uint32_t uMaxRate; //!< maximum transfer rate in bytes/sec (zero=no limit), minimum 1460 bytes/sec + uint32_t uCurRate; //!< current transfer rate in bytes/sec + uint32_t uNextRate; //!< estimated rate at next update + uint32_t uLastTick; //!< last update tick + uint32_t uLastRateTick; //!< tick count at last rate update + uint32_t aTickHist[16]; //!< tick history (when update was recorded) + uint32_t aDataHist[16]; //!< data history (how much data was sent during update) + uint8_t aCallHist[16]; //!< call history (how many times we were updated)) + uint8_t uDataIndex; //!< current update index + uint8_t _pad[3]; +} SocketRateT; + +// socket address map entry (private) +typedef struct SocketAddrMapEntryT SocketAddrMapEntryT; + +//! socket address map +typedef struct SocketAddrMapT +{ + int32_t iNumEntries; + int32_t iNextVirtAddr; + int32_t iMemGroup; + void *pMemGroupUserData; + SocketAddrMapEntryT *pMapEntries; //!< variable-length array +} SocketAddrMapT; + + +/*** Variables *************************************************************************/ + +/*** Functions *************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + HostName Cache functions +*/ + +// create hostname cache +SocketHostnameCacheT *SocketHostnameCacheCreate(int32_t iMemGroup, void *pMemGroupUserData); + +// destroy hostname cache +void SocketHostnameCacheDestroy(SocketHostnameCacheT *pCache); + +// add entry to hostname cache +void SocketHostnameCacheAdd(SocketHostnameCacheT *pCache, const char *pStrHost, uint32_t uAddress, int32_t iVerbose); + +// get entry from hostname cache +uint32_t SocketHostnameCacheGet(SocketHostnameCacheT *pCache, const char *pStrHost, int32_t iVerbose); + +// del entry from hostname cache +void SocketHostnameCacheDel(SocketHostnameCacheT *pCache, const char *pStrHost, uint32_t uAddress, int32_t iVerbose); + +// process cache entries to delete expired +void SocketHostnameCacheProcess(SocketHostnameCacheT *pCache, int32_t iVerbose); + +// check for refcounted (in-progress) hostname lookup then addref, or force using a new entry if bUseRef=false +HostentT *SocketHostnameAddRef(HostentT **ppHostList, HostentT *pHost, uint8_t bUseRef); + +// process hostname list, delete completed lookups +void SocketHostnameListProcess(HostentT **ppHostList, int32_t iMemGroup, void *pMemGroupUserData); + +/* + Packet Queue functions +*/ + +// create packet queue +SocketPacketQueueT *SocketPacketQueueCreate(int32_t iMaxPackets, int32_t iMemGroup, void *pMemGroupUserData); + +// destroy packet queue +void SocketPacketQueueDestroy(SocketPacketQueueT *pPacketQueue); + +// resize a packet queue +SocketPacketQueueT *SocketPacketQueueResize(SocketPacketQueueT *pPacketQueue, int32_t iMaxPackets, int32_t iMemGroup, void *pMemGroupUserData); + +// packet queue control function +int32_t SocketPacketQueueControl(SocketPacketQueueT *pPacketQueue, int32_t iControl, int32_t iValue); + +// packet queue status function +int32_t SocketPacketQueueStatus(SocketPacketQueueT *pPacketQueue, int32_t iStatus); + +// add to packet queue +int32_t SocketPacketQueueAdd(SocketPacketQueueT *pPacketQueue, const uint8_t *pPacketData, int32_t iPacketSize, struct sockaddr *pPacketAddr); +int32_t SocketPacketQueueAdd2(SocketPacketQueueT *pPacketQueue, const uint8_t *pPacketData, int32_t iPacketSize, struct sockaddr *pPacketAddr, uint32_t bPartialAllowed); + +// alloc packet queue entry +SocketPacketQueueEntryT *SocketPacketQueueAlloc(SocketPacketQueueT *pPacketQueue); + +// undo previous call to SocketPacketQueueAlloc() +void SocketPacketQueueAllocUndo(SocketPacketQueueT *pPacketQueue); + +// remove from packet queue +int32_t SocketPacketQueueRem(SocketPacketQueueT *pPacketQueue, uint8_t *pPacketData, int32_t iPacketSize, struct sockaddr *pPacketAddr); + +// remove stream packet from packet queue +int32_t SocketPacketQueueRemStream(SocketPacketQueueT *pPacketQueue, uint8_t *pPacketData, int32_t iPacketSize); + +// get pointers to data associated with queue head +int32_t SocketPacketQueueGetHead(SocketPacketQueueT *pPacketQueue, uint8_t **ppPacketData, int32_t *pPacketSize, struct sockaddr **ppPacketAddr); + +// update queue's head entry such that it no longer includes successfully consumed portion of data +int32_t SocketPacketQueueTouchHead(SocketPacketQueueT *pPacketQueue, int32_t iConsumedSize); + +/* + Rate functions +*/ + +// update socket data rate calculations +void SocketRateUpdate(SocketRateT *pRate, int32_t iData, const char *pOpName); + +// throttle based on socket data rate calcuations and configured max rate +int32_t SocketRateThrottle(SocketRateT *pRate, int32_t iSockType, int32_t iData, const char *pOpName); + +/* + Send Callback functions +*/ + +// register a new socket send callback +int32_t SocketSendCallbackAdd(SocketSendCallbackEntryT aCbList[], SocketSendCallbackEntryT *pCbEntry); + +// removes a socket send callback previously registered +int32_t SocketSendCallbackRem(SocketSendCallbackEntryT aCbList[], SocketSendCallbackEntryT *pCbEntry); + +/* + + AddrMap functions + +*/ + +// initialize a socket address map +void SocketAddrMapInit(SocketAddrMapT *pAddrMap, int32_t iMemGroup, void *pMemGroupUserData); + +// cleanup a socket address map +void SocketAddrMapShutdown(SocketAddrMapT *pAddrMap); + +// check if source address is in address map, and if so return mapped address +struct sockaddr *SocketAddrMapGet(const SocketAddrMapT *pAddrMap, struct sockaddr *pResult, const struct sockaddr *pSource, int32_t *pNameLen); + +// translate between IPv4, vIPv4, and IPv6 addresses +struct sockaddr *SocketAddrMapTranslate(const SocketAddrMapT *pAddrMap, struct sockaddr *pResult, const struct sockaddr *pSource, int32_t *pNameLen); + +// map an IPv6 address and return a vIPv4 address that can be used to reference it +int32_t SocketAddrMapAddress(SocketAddrMapT *pAddrMap, const struct sockaddr *pAddr, int32_t iAddrSize); + +// remap an existing IPv6 address and return a vIPv4 address that can be used to reference it +int32_t SocketAddrRemapAddress(SocketAddrMapT *pAddrMap, const struct sockaddr *pOldAddr, const struct sockaddr *pNewAddr, int32_t iAddrSize); + +// removes an addres mapping from the mapping table +int32_t SocketAddrUnmapAddress(SocketAddrMapT *pAddrMap, const struct sockaddr *pAddr, int32_t iAddrSize); + + +#ifdef __cplusplus +} +#endif + +//@} + +#endif // _dirtynetpriv_h + + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtythread.cpp b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtythread.cpp new file mode 100644 index 00000000..49518cbe --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtythread.cpp @@ -0,0 +1,262 @@ +/*H**************************************************************************************/ +/*! + \File dirtythread.cpp + + \Description + Provide threading library functions for use by network layer code. + + \Copyright + Copyright (c) Electronic Arts 2017 + + \Version 09/27/17 (eesponda) +*/ +/**************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include "eathread/eathread_condition.h" +#include "eathread/eathread_mutex.h" +#include "eathread/eathread_thread.h" + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/dirtysock/dirtythread.h" + +/*** Type Definitions ******************************************************************/ + +// data about your thread +typedef struct DirtyThreadT +{ + DirtyRunnableFunctionT *pFunction; //!< the function that implements the guts of the thread + void *pUserData; //!< user data passed along with the function + + int32_t iMemGroup; //!< memgroup identifier + void *pMemGroupUserData; //!< memgroup userdata +} DirtyThreadT; + +struct DirtyConditionRefT +{ + EA::Thread::Condition Condition; //!< condition state, must come first + + int32_t iMemGroup; //!< memory group identifier + void *pMemGroupUserdata; //!< user data passed along with allocation +}; + +/*** Private Functions *****************************************************************/ + +/*F**************************************************************************************/ +/*! + \Function _DirtyThreadWrapper + + \Description + Wrapper function that allows us to log and cleanup thread state + + \Input *pUserData - our threading state + + \Output + intptr_t - thread result + + \Version 09/28/2017 (eesponda) +*/ +/***************************************************************************************F*/ +EA_DISABLE_VC_WARNING(4702) +static intptr_t _DirtyThreadWrapper(void *pUserData) +{ + DirtyThreadT *pThread = (DirtyThreadT *)pUserData; + + // run the thread function + pThread->pFunction(pThread->pUserData); + + // clean up and return + DirtyMemFree(pThread, DIRTYTHREAD_MEMID, pThread->iMemGroup, pThread->pMemGroupUserData); + + return(0); +} +EA_RESTORE_VC_WARNING() + +/*** Public Functions *******************************************************************/ + + +/*F**************************************************************************************/ +/*! + \Function DirtyThreadCreate + + \Description + Allocate the thread state and start a thread, running it in our own wrapper function + + \Input *pFunction - function that is run in the thread + \Input *pUserData - user data passed along with that function + \Input *pConfig - addtional configuration needed + + \Output + int32_t - zero=success, negative=failure + + \Version 09/28/2017 (eesponda) +*/ +/***************************************************************************************F*/ +int32_t DirtyThreadCreate(DirtyRunnableFunctionT *pFunction, void *pUserData, const DirtyThreadConfigT *pConfig) +{ + /* we create the thread object on the stack here as it is not tied to the lifetime of the thread + just facilitates its creation */ + EA::Thread::Thread Thread; + EA::Thread::ThreadParameters Parameters; + DirtyThreadT *pThread; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query memgroup info + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate thread state + if ((pThread = (DirtyThreadT *)DirtyMemAlloc(sizeof(*pThread), DIRTYTHREAD_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + return(-1); + } + ds_memclr(pThread, sizeof(*pThread)); + pThread->pFunction = pFunction; + pThread->pUserData = pUserData; + pThread->iMemGroup = iMemGroup; + pThread->pMemGroupUserData = pMemGroupUserData; + + Parameters.mnAffinityMask = (pConfig->iAffinity > 0) ? pConfig->iAffinity : EA::Thread::kThreadAffinityMaskAny; + Parameters.mnPriority = pConfig->iPriority; + Parameters.mnProcessor = EA::Thread::kProcessorAny; + Parameters.mpName = pConfig->pName; + + // start the thread + if (Thread.Begin(_DirtyThreadWrapper, pThread, &Parameters) == EA::Thread::kThreadIdInvalid) + { + DirtyMemFree(pThread, DIRTYTHREAD_MEMID, iMemGroup, pMemGroupUserData); + return(-2); + } + return(0); +} + +/*F**************************************************************************************/ +/*! + \Function DirtyThreadGetThreadId + + \Description + Gets the thread identifier that can be used for logging purposes + + \Input *pBuffer - [out] the output buffer for the thread id + \Input iBufSize - size of the output buffer + + \Output + const char *- pointer to the output buffer passed in + + \Version 02/04/2020 (eesponda) +*/ +/***************************************************************************************F*/ +const char *DirtyThreadGetThreadId(char *pBuffer, int32_t iBufSize) +{ + return(ds_strnzcpy(pBuffer, EAThreadSysThreadIdToString(EA::Thread::GetSysThreadId()), iBufSize)); +} + +/*F**************************************************************************************/ +/*! + \Function DirtyConditionCreate + + \Description + Creates a conditional variable with the given name + + \Input *pName - name for the conditional + + \Output + DirtyConditionRefT * - object ref on success, NULL otherwise + + \Version 09/27/2017 (eesponda) +*/ +/***************************************************************************************F*/ +DirtyConditionRefT *DirtyConditionCreate(const char *pName) +{ + DirtyConditionRefT *pCondition; + int32_t iMemGroup; + void *pMemGroupUserdata; + EA::Thread::ConditionParameters Params; + + // query memgroup info + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserdata); + + // setup parameters + ds_strnzcpy(Params.mName, pName, sizeof(Params.mName)); + Params.mbIntraProcess = true; + + // allocate the memory needed for condition state + if ((pCondition = (DirtyConditionRefT *)DirtyMemAlloc(sizeof(*pCondition), DIRTYTHREAD_MEMID, iMemGroup, pMemGroupUserdata)) == NULL) + { + NetPrintf(("dirtythread: unable to allocate state for condition variable\n")); + return(NULL); + } + ds_memclr(pCondition, sizeof(*pCondition)); + pCondition->iMemGroup = iMemGroup; + pCondition->pMemGroupUserdata = pMemGroupUserdata; + if (!pCondition->Condition.Init(&Params)) + { + NetPrintf(("dirtythread: [%p] condition init returned false\n", pCondition)); + DirtyConditionDestroy(pCondition); + return(NULL); + } + + return(pCondition); +} + +/*F**************************************************************************************/ +/*! + \Function DirtyConditionDestroy + + \Description + Destroys the condition variable + + \Input *pCondition - condition variable state + + \Version 09/27/2017 (eesponda) +*/ +/***************************************************************************************F*/ +void DirtyConditionDestroy(DirtyConditionRefT *pCondition) +{ + pCondition->Condition.~Condition(); + DirtyMemFree(pCondition, DIRTYTHREAD_MEMID, pCondition->iMemGroup, pCondition->pMemGroupUserdata); +} + +/*F**************************************************************************************/ +/*! + \Function DirtyConditionWait + + \Description + Waits on a condition given a crit (mutex) + + \Input *pCondition - condition variable state + \Input *pCrit - mutex used for wait + + \Version 09/27/2017 (eesponda) +*/ +/***************************************************************************************F*/ +void DirtyConditionWait(DirtyConditionRefT *pCondition, NetCritT *pCrit) +{ + // we can cast here since we know that the first field in crit is the mutex + EA::Thread::Mutex *pMutex = (EA::Thread::Mutex *)pCrit->pData; + pCondition->Condition.Wait(pMutex); +} + +/*F**************************************************************************************/ +/*! + \Function DirtyConditionSignal + + \Description + Signals a condition + + \Input *pCondition - condition variable state + + \Output + uint8_t - TRUE if successful, FALSE otherwsie + + \Version 09/27/2017 (eesponda) +*/ +/***************************************************************************************F*/ +uint8_t DirtyConditionSignal(DirtyConditionRefT *pCondition) +{ + return(pCondition->Condition.Signal()); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyuser.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyuser.c new file mode 100644 index 00000000..945bc6f0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/dirtyuser.c @@ -0,0 +1,117 @@ +/*H********************************************************************************/ +/*! + + \File dirtyuser.c + + \Description + Generic user functions. Given that all our supported platforms use + uint64_t, we can combine all our functions. + + \Copyright + Copyright (c) Electronic Arts 2020. ALL RIGHTS RESERVED. + + \Version 03/05/20 (eesponda) +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyuser.h" +#include "DirtySDK/util/binary7.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function DirtyUserToNativeUser + + \Description + Convert a DirtyUserT to native format. + + \Input *pOutput - [out] storage for native format user + \Input iBufLen - length of output buffer + \Input *pUser - source user to convert + + \Output + uint32_t - TRUE if successful, else FALSE + + \Version 03/05/2020 (eesponda) +*/ +/********************************************************************************F*/ +uint32_t DirtyUserToNativeUser(void *pOutput, int32_t iBufLen, const DirtyUserT *pUser) +{ + // make sure output buffer is big enough + if (iBufLen < (signed)sizeof(uint64_t)) + { + return(FALSE); + } + // make sure the encoding is correct + if (*pUser->strNativeUser != '^') + { + return(FALSE); + } + + // decode contents of pUser + Binary7Decode((uint8_t *)pOutput, iBufLen, (const uint8_t *)pUser->strNativeUser+1); + + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function DirtyUserFromNativeUser + + \Description + Convert native format user data to a DirtyUserT. + + \Input *pUser - [out] storage for output DirtyUserT + \Input *pInput - pointer to native format user + + \Output + uint32_t - TRUE if successful, else FALSE + + \Version 03/05/2020 (eesponda) +*/ +/********************************************************************************F*/ +uint32_t DirtyUserFromNativeUser(DirtyUserT *pUser, const void *pInput) +{ + uint64_t *pPlayerId = (uint64_t *)pInput; + // clear output data + ds_memclr(pUser, sizeof(*pUser)); + + // encode input SceNpAccountId into pUser + pUser->strNativeUser[0] = '^'; + Binary7Encode((uint8_t *)pUser->strNativeUser+1, sizeof(*pUser)-1, (uint8_t *)pPlayerId, sizeof(*pPlayerId), TRUE); + + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function DirtyUserCompare + + \Description + Compare two DirtyUserT users for equality. + + \Input *pUser1 - user 1 + \Input *pUser2 - user 2 + + \Output + int32_t - TRUE if successful, else FALSE + + \Version 03/05/2020 (eesponda) +*/ +/********************************************************************************F*/ +int32_t DirtyUserCompare(DirtyUserT *pUser1, DirtyUserT *pUser2) +{ + return(strcmp(pUser1->strNativeUser, pUser2->strNativeUser) == 0); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/netconn.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconn.c new file mode 100644 index 00000000..300e69be --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconn.c @@ -0,0 +1,611 @@ +/*H*************************************************************************************************/ +/*! + + \File netconn.c + + \Description + Provides network setup and teardown support. Does not actually create any connections. + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2001-2017. ALL RIGHTS RESERVED. + + \Version 1.0 03/12/01 (GWS) First Version + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtycert.h" +#include "DirtySDK/dirtysock/netconn.h" + +/*** Defines **************************************************************************************/ + +//! maximum number of idle handlers that may be registered +#define NETCONN_MAXIDLEHANDLERS (64) + +/*** Macros ***************************************************************************************/ + +/*** Type Definitions *****************************************************************************/ + +/*** Function Prototypes **************************************************************************/ + +/*** Variables ************************************************************************************/ + +static struct +{ + void (*proc)(void *pData, uint32_t uTick); + void *data; +} _NETidle[NETCONN_MAXIDLEHANDLERS]; + +static const char _NetConn_HexEncode[16] = "0123456789abcdef"; + +static uint32_t _NetConn_uLastIdleTick = 0; +static uint8_t _NetConn_bTickInitialized = FALSE; +static uint8_t _NetConn_bNetConnIdleTiming = FALSE; +static uint32_t _NetConn_uMachineId = 0; + +/*** Private Functions ****************************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _NetConnIdleProcs + + \Description + Process any registered idle handlers. This is for internal use only. + + \Input uCurTick - current millisecond tick count + + \Output + None. + + \Version 05/26/2005 (doneill) +*/ +/********************************************************************************F*/ +static void _NetConnIdleProcs(uint32_t uCurTick) +{ + int32_t iProc; + + // call other idle handlers + for (iProc = 0; iProc < NETCONN_MAXIDLEHANDLERS; iProc++) + { + if (_NETidle[iProc].proc != NULL) + { + (_NETidle[iProc].proc)(_NETidle[iProc].data, uCurTick); + } + } +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _NetConnMonitorRate + + \Description + Monitor the rate at which NetconnIdle is called. Output diagnosis messages + + \Input uCurrTick - current millisecond tick count + iTickDiff - tick difference between previous call + + \Output + None. + + \Version 06/11/2009 (jrainy) +*/ +/********************************************************************************F*/ +static void _NetConnMonitorRate(uint32_t uCurrTick, int32_t iTickDiff) +{ + static int32_t iMaxDiff = 0; + static int32_t iMinDiff = 0; + static int32_t iRunningCount = 0; + static int32_t uStartTick = 0; + static int32_t uLastTick = 0; + + if ((iTickDiff < iMinDiff) || (iTickDiff > iMaxDiff) || (NetTickDiff(uCurrTick, uStartTick) > 2000)) + { + if (iRunningCount) + { + NetPrintf(("netconn: %d netconnidles of %d to %d ms between times %d and %d.\n", iRunningCount, iMinDiff, iMaxDiff, uStartTick, uLastTick)); + uStartTick = uLastTick; + } + else + { + uStartTick = uCurrTick; + } + iRunningCount = 1; + iMaxDiff = (iTickDiff * 15) / 10; + iMinDiff = (iTickDiff * 5) / 10; + } + else + { + iRunningCount++; + } + uLastTick = uCurrTick; +} +#endif + +/*** Public functions *****************************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function NetConnMAC + + \Description + Return the network MAC address string to the caller + + \Output + const char * - textual MAC address string + + \Version 1.0 04/29/01 (GWS) First Version +*/ +/*************************************************************************************************F*/ +const char *NetConnMAC(void) +{ + static union + { + unsigned char uMac[16]; + } Stack; + static char strMAC[16] = ""; + + // see if we need to query mac address + if (strMAC[0] == 0) + { + // get MAC address + if (NetConnStatus('macx', 0, Stack.uMac, sizeof(Stack.uMac)) >= 0) + { + // format into string + strMAC[0] = '$'; + strMAC[1] = _NetConn_HexEncode[Stack.uMac[0]>>4]; + strMAC[2] = _NetConn_HexEncode[Stack.uMac[0]&15]; + strMAC[3] = _NetConn_HexEncode[Stack.uMac[1]>>4]; + strMAC[4] = _NetConn_HexEncode[Stack.uMac[1]&15]; + strMAC[5] = _NetConn_HexEncode[Stack.uMac[2]>>4]; + strMAC[6] = _NetConn_HexEncode[Stack.uMac[2]&15]; + strMAC[7] = _NetConn_HexEncode[Stack.uMac[3]>>4]; + strMAC[8] = _NetConn_HexEncode[Stack.uMac[3]&15]; + strMAC[9] = _NetConn_HexEncode[Stack.uMac[4]>>4]; + strMAC[10] = _NetConn_HexEncode[Stack.uMac[4]&15]; + strMAC[11] = _NetConn_HexEncode[Stack.uMac[5]>>4]; + strMAC[12] = _NetConn_HexEncode[Stack.uMac[5]&15]; + strMAC[13] = 0; + } + } + + return(strMAC); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnElapsed + + \Description + Return elapsed time in milliseconds. The epoch (zero time) is not defined. + This function should be used to determine elapsed time by calling once, + saving the result, then later calling again and subtracting the original + result from the new result. This will give the elapsed time in millisecs. + + \Output + uint32_t - elapsed milliseconds + + \Version 1.0 03/10/01 (GWS) First Version +*/ +/*************************************************************************************************F*/ +uint32_t NetConnElapsed(void) +{ + return(NetTick()); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnIdleAdd + + \Description + Add an idle handler that will get called periodically. The frequency of calls + is not guarenteed, but should be 60Hz min. + + \Input *proc - callback function + \Input *data - reference data provided to callback function + + \Output + int32_t - negative=error, zero=success + + \Version 1.0 03/10/01 (GWS) First Version +*/ +/*************************************************************************************************F*/ +int32_t NetConnIdleAdd(void (*proc)(void *data, uint32_t tick), void *data) +{ + int32_t i; + + // locate slot and add + for (i = 0; i < NETCONN_MAXIDLEHANDLERS; ++i) + { + // make sure it's not already added + if ((_NETidle[i].proc == proc) && (_NETidle[i].data == data)) + { + NetPrintf(("netconn: ignoring add of an idle handler that is already registered\n")); + return(-1); + } + + // if there's space in the table, add the function + if (_NETidle[i].proc == NULL) + { + _NETidle[i].proc = proc; + _NETidle[i].data = data; + return(0); + } + } + + // warn the user that the add failed + NetPrintf(("netconn: unable to add new idle handler as table is full\n")); + + // no space in table + return(-2); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnIdleDel + + \Description + Remove a previously added idle handler. The data parameter must match what + was given when the function was added. + + \Input *proc - callback function + \Input *data - same value given to NetConnIdleAdd + + \Output + int32_t - negative=error, zero=success + + \Version 1.0 03/10/01 (GWS) First Version +*/ +/*************************************************************************************************F*/ +int32_t NetConnIdleDel(void (*proc)(void *data, uint32_t tick), void *data) +{ + int32_t i; + + // locate slot and del + for (i = 0; i < NETCONN_MAXIDLEHANDLERS; ++i) + { + if ((_NETidle[i].proc == proc) && (_NETidle[i].data == data)) + { + _NETidle[i].proc = NULL; + _NETidle[i].data = NULL; + return(0); + } + } + + // warn the user the handler did not exist + NetPrintf(("netconn: ignoring delete of an idle handler that is not registered\n")); + + // not in table + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnIdle + + \Description + Provide "life" to the network code. This function needs to be called periodically + (typically once per game loop, generally speaking 5-10hz minimum) for the network code to + function properly. + + \Version 1.0 03/10/01 (GWS) First Version +*/ +/*************************************************************************************************F*/ +void NetConnIdle(void) +{ + uint32_t uCurTick; + + // make sure module is available + if (NetConnStatus('open', 0, NULL, 0) == 0) + { + return; + } + + // get current tick count + uCurTick = NetTick(); + + // initialized last tick counter? + if (_NetConn_bTickInitialized == FALSE) + { + _NetConn_uLastIdleTick = uCurTick - 5; + _NetConn_bTickInitialized = TRUE; + } + + // debug timing of idle rate + #if DIRTYCODE_LOGGING + if (_NetConn_bNetConnIdleTiming) + { + int32_t iTickDiff = NetTickDiff(uCurTick, _NetConn_uLastIdleTick); + _NetConnMonitorRate(uCurTick, iTickDiff); + } + #endif + + _NetConn_uLastIdleTick = uCurTick; + + // call registered idle handlers + _NetConnIdleProcs(uCurTick); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnIdleShutdown + + \Description + Shut down the NetConnIdle handler. + This function is intended for internal use only. It should not be called by an application. + + \Version 1.0 06/16/04 (JLB) First Version +*/ +/*************************************************************************************************F*/ +void NetConnIdleShutdown(void) +{ + int32_t iProc; + + for (iProc = 0; iProc < NETCONN_MAXIDLEHANDLERS; iProc++) + { + if (_NETidle[iProc].proc != NULL) + { + NetPrintf(("netconn: removing idle handler at shutdown\n")); + _NETidle[iProc].proc = NULL; + _NETidle[iProc].data = NULL; + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnTiming + + \Description + Enable or disable the timing of netconnidles + + \Input uEnableTiming - on or off + + \Output + None. + + \Version 1.0 11/06/09 (jrainy) First Version +*/ +/*************************************************************************************************F*/ +void NetConnTiming(uint8_t uEnableTiming) +{ + _NetConn_bNetConnIdleTiming = uEnableTiming; +} + +#if DIRTYCODE_LOGGING +#define MONITOR_SIZE 200 +#define MONITOR_VARIABLES 64 +#define MONITOR_NAME_SIZE 64 +void NetConnMonitorValue(const char* pName, int32_t iValue) +{ + int32_t iIndex, iPrintIndex; + static char strNames[MONITOR_VARIABLES][MONITOR_NAME_SIZE] = {{0}}; + static int32_t iMonitoredValues[MONITOR_VARIABLES][MONITOR_SIZE] = {{0}}; + static int32_t iMonitoredCount[MONITOR_VARIABLES] = {0}; + + for(iIndex = 0; iIndex < MONITOR_VARIABLES; iIndex++) + { + if ((!ds_strnicmp(pName, strNames[iIndex], MONITOR_NAME_SIZE)) || (strNames[iIndex][0] == 0)) + { + break; + } + } + + if (iIndex == MONITOR_VARIABLES) + { + NetPrintf(("netconn: too many monitored variables\n")); + return; + } + + if (!strNames[iIndex][0]) + { + ds_strnzcpy(strNames[iIndex], pName, MONITOR_NAME_SIZE); + } + + iMonitoredValues[iIndex][iMonitoredCount[iIndex]++] = iValue; + + if (iMonitoredCount[iIndex] == MONITOR_SIZE) + { + NetPrintf(("NetConn: displaying monitored values \"%s\"\n", strNames[iIndex])); + for(iPrintIndex = 0; iPrintIndex < MONITOR_SIZE; iPrintIndex += 10) + { + NetPrintf(("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", + iMonitoredValues[iIndex][iPrintIndex + 0], + iMonitoredValues[iIndex][iPrintIndex + 1], + iMonitoredValues[iIndex][iPrintIndex + 2], + iMonitoredValues[iIndex][iPrintIndex + 3], + iMonitoredValues[iIndex][iPrintIndex + 4], + iMonitoredValues[iIndex][iPrintIndex + 5], + iMonitoredValues[iIndex][iPrintIndex + 6], + iMonitoredValues[iIndex][iPrintIndex + 7], + iMonitoredValues[iIndex][iPrintIndex + 8], + iMonitoredValues[iIndex][iPrintIndex + 9])); + } + iMonitoredCount[iIndex] = 0; + } + + + +} + +#endif + +/*F********************************************************************************/ +/*! + \Function NetConnMachineId + + \Description + Gets a unique id for this machine. + + \Output + uint32_t - machine id of this machine. + + \Version 01/31/2014 (cvienneau) +*/ +/********************************************************************************F*/ +uint32_t NetConnMachineId(void) +{ + return(_NetConn_uMachineId); +} + +/*F********************************************************************************/ +/*! + \Function NetConnSetMachineId + + \Description + Sets a unique id for this machine. + + \Input uMachineId - new value + + \Version 01/31/2014 (cvienneau) +*/ +/********************************************************************************F*/ +void NetConnSetMachineId(uint32_t uMachineId) +{ + _NetConn_uMachineId = uMachineId; + NetPrintf(("netconn: machineId set to %x\n", uMachineId)); +} + + +/*F********************************************************************************/ +/*! + \Function NetConnCopyParam + + \Description + Copy a command-line parameter. + + \Input *pDst - output buffer + \Input iDstLen - output buffer length + \Input *pParamName - name of parameter to check for + \Input *pSrc - input string to look for parameters in + \Input *pDefault - default string to use if paramname not found + + \Output + int32_t - number of bytes written + + \Version 07/18/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnCopyParam(char *pDst, int32_t iDstLen, const char *pParamName, const char *pSrc, const char *pDefault) +{ + int32_t iIndex; + + // find parameter + if ((pSrc = strstr(pSrc, pParamName)) == NULL) + { + // copy in default + ds_strnzcpy(pDst, pDefault, iDstLen); + return((int32_t)strlen(pDefault)); + } + + // skip parameter name + pSrc += strlen(pParamName); + + // make sure buffer has enough room + if (--iDstLen < 0) + { + return(0); + } + + // copy the string + for (iIndex = 0; (iIndex < iDstLen) && (pSrc[iIndex] != '\0') && (pSrc[iIndex] != ' '); iIndex++) + { + pDst[iIndex] = pSrc[iIndex]; + } + + // write null terminator and return number of bytes written + pDst[iIndex] = '\0'; + return(iIndex); +} + +/*F********************************************************************************/ +/*! + \Function NetConnDirtyCertCreate + + \Description + Create DirtyCert, intitialize service name if provided + + \Input *pParams - input params + + \Output + int32_t - negative=failure, else success + + \Version 03/28/2013 (jbrookes) + */ +/********************************************************************************F*/ +int32_t NetConnDirtyCertCreate(const char *pParams) +{ + char strServiceName[DIRTYCERT_SERVICENAME_SIZE]; + + // create the dirtycert module + if (DirtyCertCreate() != 0) + { + NetConnShutdown(0); + NetPrintf(("netconn: unable to create dirtycert\n")); + return(-1); + } + // check for servicename + if (strstr(pParams, "-servicename=") != NULL) + { + // get service name + NetConnCopyParam(strServiceName, sizeof(strServiceName), "-servicename=", pParams, ""); + // set service name in dirtycert + DirtyCertControl('snam', 0, 0, strServiceName); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetConnGetEnvStr + + \Description + Translate specified NETCONN_PLATENV_ define into an environment string + + \Output + const char * - pointer to env str + + \Version 07/09/2013 (jbrookes) + */ +/********************************************************************************F*/ +const char *NetConnGetEnvStr(void) +{ + const char *pEnv; + switch(NetConnStatus('envi', 0, NULL, 0)) + { + case NETCONN_PLATENV_DEV: + pEnv = "dev"; + break; + + case NETCONN_PLATENV_TEST: + pEnv = "test"; + break; + + case NETCONN_PLATENV_CERT: + pEnv = "cert"; + break; + + case NETCONN_PLATENV_PROD: + pEnv = "prod"; + break; + + default: + NetPrintf(("netconn: could not get env str\n")); + pEnv = "unkn"; + break; + } + return(pEnv); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/netconncommon.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconncommon.c new file mode 100644 index 00000000..7c5c695d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconncommon.c @@ -0,0 +1,414 @@ +/*H********************************************************************************/ +/*! + \File netconncommon.c + + \Description + Cross-platform netconn data types and functions. + + \Copyright + Copyright (c) 2014 Electronic Arts Inc. + + \Version 05/21/2014 (mclouatre) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtycert.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protossl.h" +#include "netconncommon.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function NetConnCommonStartup + + \Description + Start up common functionality. + + \Input iNetConnRefSize - size of netconn ref to allocate + \Input *pParams - startup parameters + \Input *pRef - (out) netconn ref if successful; else NULL + + \Output + int32_t - 0 for success; negative for error + + \Version 05/21/2014 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t NetConnCommonStartup(int32_t iNetConnRefSize, const char *pParams, NetConnCommonRefT **pRef) +{ + NetConnCommonRefT *pCommonRef = *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // refcount if already started + if (pCommonRef != NULL) + { + ++(pCommonRef->iRefCount); + NetPrintf(("netconncommon: NetConnStartup() called with params='%s' while the module is already started, refcounting to %d\n", pParams, pCommonRef->iRefCount)); + return(NETCONN_ERROR_ALREADY_STARTED); + } + + // alloc and init ref + if ((pCommonRef = (NetConnCommonRefT *)DirtyMemAlloc(iNetConnRefSize, NETCONN_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("netconncommon: unable to allocate module state\n")); + return(NETCONN_ERROR_NO_MEMORY); + } + ds_memclr(pCommonRef, iNetConnRefSize); + pCommonRef->iMemGroup = iMemGroup; + pCommonRef->pMemGroupUserData = pMemGroupUserData; + pCommonRef->iRefCount = 1; + pCommonRef->iDebugLevel = 1; + + // allocate external cleanup list + pCommonRef->iExternalCleanupListMax = NETCONN_EXTERNAL_CLEANUP_LIST_INITIAL_CAPACITY; + if ((pCommonRef->pExternalCleanupList = (NetConnExternalCleanupEntryT *)DirtyMemAlloc(pCommonRef->iExternalCleanupListMax * sizeof(NetConnExternalCleanupEntryT), NETCONN_MEMID, pCommonRef->iMemGroup, pCommonRef->pMemGroupUserData)) == NULL) + { + DirtyMemFree(pCommonRef, NETCONN_MEMID, pCommonRef->iMemGroup, pCommonRef->pMemGroupUserData); + NetPrintf(("netconncommmon: unable to allocate memory for initial external cleanup list\n")); + (*pRef) = NULL; + return(NETCONN_ERROR_NO_MEMORY); + } + ds_memclr(pCommonRef->pExternalCleanupList, pCommonRef->iExternalCleanupListMax * sizeof(NetConnExternalCleanupEntryT)); + + NetCritInit(&pCommonRef->crit, "netconnCrit"); + + // save ref + (*pRef) = pCommonRef; + + // successful completion + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetConnCommonShutdown + + \Description + Shutdown common functionality. + + \Input *pCommonRef - common module state + + \Version 05/21/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void NetConnCommonShutdown(NetConnCommonRefT *pCommonRef) +{ + NetCritKill(&pCommonRef->crit); + + // free the cleanup list memory + if (pCommonRef->pExternalCleanupList != NULL) + { + DirtyMemFree(pCommonRef->pExternalCleanupList, NETCONN_MEMID, pCommonRef->iMemGroup, pCommonRef->pMemGroupUserData); + pCommonRef->pExternalCleanupList = NULL; + } + + // dispose of ref + DirtyMemFree(pCommonRef, NETCONN_MEMID, pCommonRef->iMemGroup, pCommonRef->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function NetConnCommonAddToExternalCleanupList + + \Description + Add an entry to the list of external module pending successful cleanup. + + \Input *pCommonRef - NetConnCommonRefT reference + \Input *pCleanupCb - cleanup callback + \Input *pCleanupData - data to be passed to cleanup callback + + \Output + uint32_t - 0 for success; -1 for failure. + + \Version 12/07/2009 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t NetConnCommonAddToExternalCleanupList(NetConnCommonRefT *pCommonRef, NetConnExternalCleanupCallbackT pCleanupCb, void *pCleanupData) +{ + NetCritEnter(&pCommonRef->crit); + + // if list if full, double its size. + if (pCommonRef->iExternalCleanupListCnt == pCommonRef->iExternalCleanupListMax) + { + NetConnExternalCleanupEntryT *pNewList; + + // allocate new external cleanup list + if ((pNewList = (NetConnExternalCleanupEntryT *) DirtyMemAlloc(2 * pCommonRef->iExternalCleanupListMax * sizeof(NetConnExternalCleanupEntryT), NETCONN_MEMID, pCommonRef->iMemGroup, pCommonRef->pMemGroupUserData)) == NULL) + { + NetPrintf(("netconncommon: unable to allocate memory for the external cleanup list\n")); + NetCritLeave(&pCommonRef->crit); + return(-1); + } + ds_memclr(pNewList, 2 * pCommonRef->iExternalCleanupListMax * sizeof(NetConnExternalCleanupEntryT)); + + // copy contents of old list in contents of new list + ds_memcpy(pNewList, pCommonRef->pExternalCleanupList, pCommonRef->iExternalCleanupListMax * sizeof(NetConnExternalCleanupEntryT)); + + // free old list and replace it with new list + DirtyMemFree(pCommonRef->pExternalCleanupList, NETCONN_MEMID, pCommonRef->iMemGroup, pCommonRef->pMemGroupUserData); + pCommonRef->pExternalCleanupList = pNewList; + pCommonRef->iExternalCleanupListMax = pCommonRef->iExternalCleanupListMax * 2; + } + + // add new entry to the list + pCommonRef->pExternalCleanupList[pCommonRef->iExternalCleanupListCnt].pCleanupCb = pCleanupCb; + pCommonRef->pExternalCleanupList[pCommonRef->iExternalCleanupListCnt].pCleanupData = pCleanupData; + pCommonRef->iExternalCleanupListCnt += 1; + + NetCritLeave(&pCommonRef->crit); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetConnCommonProcessExternalCleanupList + + \Description + Walk external cleanup list and try to destroy each individual entry. + + \Input *pCommonRef - NetConnCommonRefT reference + + \Output + int32_t - number of valid entries in the cleanup list, negative integer upon failure + + \Version 03/13/2017 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t NetConnCommonProcessExternalCleanupList(NetConnCommonRefT *pCommonRef) +{ + int32_t iEntryIndex; + int32_t iEntryIndex2; + + // early exit if cleanup list not yet allocated + if (pCommonRef->pExternalCleanupList == NULL) + { + return(-1); + } + + // make sure we don't re-enter this code; some modules may call NetConnStatus() during their cleanup + if (!NetCritTry(&pCommonRef->crit)) + { + return(-2); + } + + for (iEntryIndex = 0; iEntryIndex < pCommonRef->iExternalCleanupListMax; iEntryIndex++) + { + if (pCommonRef->pExternalCleanupList[iEntryIndex].pCleanupCb == NULL) + { + // no more entry in list + break; + } + + if(pCommonRef->pExternalCleanupList[iEntryIndex].pCleanupCb(pCommonRef->pExternalCleanupList[iEntryIndex].pCleanupData) == 0) + { + pCommonRef->iExternalCleanupListCnt -= 1; + + NetPrintf(("netconncommon: successfully destroyed external module (cleanup data ptr = %p), external cleanup list count decremented to %d\n", + pCommonRef->pExternalCleanupList[iEntryIndex].pCleanupData, pCommonRef->iExternalCleanupListCnt)); + + // move all following entries one cell backward in the array + for(iEntryIndex2 = iEntryIndex; iEntryIndex2 < pCommonRef->iExternalCleanupListMax; iEntryIndex2++) + { + if (iEntryIndex2 == pCommonRef->iExternalCleanupListMax-1) + { + // last entry, reset to NULL + pCommonRef->pExternalCleanupList[iEntryIndex2].pCleanupCb = NULL; + pCommonRef->pExternalCleanupList[iEntryIndex2].pCleanupData = NULL; + } + else + { + pCommonRef->pExternalCleanupList[iEntryIndex2].pCleanupCb = pCommonRef->pExternalCleanupList[iEntryIndex2+1].pCleanupCb; + pCommonRef->pExternalCleanupList[iEntryIndex2].pCleanupData = pCommonRef->pExternalCleanupList[iEntryIndex2+1].pCleanupData; + } + } + } + } + + // clear re-enter block + NetCritLeave(&pCommonRef->crit); + + return(pCommonRef->iExternalCleanupListCnt); +} + +/*F********************************************************************************/ +/*! + \Function NetConnCommonCheckRef + + \Description + Decrement and verify the reference count. + + \Input *pCommonRef - NetConnCommonRefT reference + + \Output + int32_t - 0 if ready to shutdown, NETCONN_ERROR_ISACTIVE otherwise + + \Version 10/23/2017 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t NetConnCommonCheckRef(NetConnCommonRefT *pCommonRef) +{ + // check the refcount + if (--(pCommonRef->iRefCount) > 0) + { + NetPrintf(("netconncommon: NetConnShutdown() called, new refcount is %d\n", pCommonRef->iRefCount)); + return(NETCONN_ERROR_ISACTIVE); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetConnCommonControl + + \Description + Set module behavior based on input selector. + + \Input *pCommonRef - netconncommon ref + \Input iControl - input selector + \Input iValue - selector input + \Input iValue2 - selector input + \Input *pValue - selector input + \Input *pValue2 - selector input + + \Output + int32_t - selector result + + \Notes + iControl can be one of the following: + + \verbatim + 'acid' - set the "Account Id" of user index iValue, via pValue as int64_t (iValue2 is buffer size) + 'peid' - set the "Persona Id" of user index iValue, via pValue as int64_t (iValue2 is buffer size) + \endverbatim + + Unhandled selectors are passed through to SocketControl() + + \Version 07/02/2019 (tcho) +*/ +/********************************************************************************F*/ +int32_t NetConnCommonControl(NetConnCommonRefT *pCommonRef, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue, void *pValue2) +{ + // set account id + if (iControl == 'acid') + { + if ((iValue >= 0) && (iValue < NETCONN_MAXLOCALUSERS) && (iValue2 == sizeof(int64_t))) + { + ds_memcpy(&pCommonRef->aAccountInfo[iValue].iAccountId, pValue, iValue2); + return(0); + } + else + { + NetPrintf(("netconncommon: error - NetConnControl('acid') called with invalid parameters.\n")); + return(-1); + } + } + + // set persona id + if (iControl == 'peid') + { + if ((iValue >= 0) && (iValue < NETCONN_MAXLOCALUSERS) && (iValue2 == sizeof(int64_t))) + { + ds_memcpy(&pCommonRef->aAccountInfo[iValue].iPersonaId, pValue, iValue2); + return(0); + } + else + { + NetPrintf(("netconncommon: error - NetConnControl('peid') called with invalid parameters.\n")); + return(-1); + } + } + + // pass through unhandled selectors to SocketControl() + return(SocketControl(NULL, iControl, iValue, pValue, pValue2)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnCommonStatus + + \Description + Check general network connection status. Different selectors return + different status attributes. + + \Input *pCommonRef - netconncommon ref + \Input iKind - status selector + \Input iData - (optional) selector specific + \Input *pBuf - (optional) pointer to output buffer + \Input iBufSize - (optional) size of output buffer + + \Output + int32_t - selector specific + + \Notes + iKind can be one of the following: + + \verbatim + acid: get the "Account Id" for the user index at iData, via pBuf as an int64_t + peid: get the "Persona Id" for the user index at iData, via pBuf as an int64_t + \endverbatim + + Unhandled selectors are passed through to SocketInfo() + + \Version 07/02/2019 (tcho) +*/ +/*************************************************************************************************F*/ +int32_t NetConnCommonStatus(NetConnCommonRefT *pCommonRef, int32_t iKind, int32_t iData, void *pBuf, int32_t iBufSize) +{ + // get account id + if (iKind == 'acid') + { + if ((iData >= 0) && (iData < NETCONN_MAXLOCALUSERS) && (iBufSize == sizeof(int64_t))) + { + ds_memcpy(pBuf, &pCommonRef->aAccountInfo[iData].iAccountId, iBufSize); + return(0); + } + else + { + NetPrintf(("netconncommon: error - NetConnStatus('acid') called with invalid parameters.\n")); + return(-1); + } + } + + // get persona id + if (iKind == 'peid') + { + if ((iData >= 0) && (iData < NETCONN_MAXLOCALUSERS) && (iBufSize == sizeof(int64_t))) + { + ds_memcpy(pBuf, &pCommonRef->aAccountInfo[iData].iPersonaId, iBufSize); + return(0); + } + else + { + NetPrintf(("netconncommon: error - NetConnStatus('peid') called with invalid parameters.\n")); + return(-1); + } + } + + // pass unrecognized options to SocketInfo + return(SocketInfo(NULL, iKind, iData, pBuf, iBufSize)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/netconncommon.h b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconncommon.h new file mode 100644 index 00000000..192c9b9c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconncommon.h @@ -0,0 +1,92 @@ +/*H********************************************************************************/ +/*! + \File netconncommon.h + + \Description + Cross-platform netconn data types and private functions. + + \Copyright + Copyright (c) 2014 Electronic Arts Inc. + + \Version 05/21/2009 (mclouatre) First Version +*/ +/********************************************************************************H*/ + +#ifndef _netconncommon_h +#define _netconncommon_h + +/*** Include files ****************************************************************/ +#include "DirtySDK/dirtysock/netconn.h" + +/*** Defines **********************************************************************/ + +// initial size of external cleanup list (in number of entries) +#define NETCONN_EXTERNAL_CLEANUP_LIST_INITIAL_CAPACITY (12) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! external cleanup callback function prototype +typedef int32_t(*NetConnExternalCleanupCallbackT)(void *pNetConnExternalCleanupData); + +typedef struct NetConnExternalCleanupEntryT +{ + void *pCleanupData; //!< pointer to data to be passed to the external cleanup callback + NetConnExternalCleanupCallbackT pCleanupCb;//!< external cleanup callback +} NetConnExternalCleanupEntryT; + +typedef struct NetConnCommonRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + int32_t iDebugLevel; + + int32_t iExternalCleanupListMax; //!< maximum number of entries in the array + int32_t iExternalCleanupListCnt; //!< number of valid entries in the array + NetConnExternalCleanupEntryT *pExternalCleanupList; //!< pointer to an array of entries pending external cleanup completion + + int32_t iRefCount; //!< module reference counter + + NetConnAccountInfoT aAccountInfo[NETCONN_MAXLOCALUSERS]; //!< account info array + + NetCritT crit; +} NetConnCommonRefT; + + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// handle common shutdown functionality +void NetConnCommonShutdown(NetConnCommonRefT *pCommonRef); + +// handle common startup functionality +int32_t NetConnCommonStartup(int32_t iNetConnRefSize, const char *pParams, NetConnCommonRefT **pRef); + +// add an entry to the list of external module pending successful cleanup +int32_t NetConnCommonAddToExternalCleanupList(NetConnCommonRefT *pCommonRef, NetConnExternalCleanupCallbackT pCleanupCb, void *pCleanupData); + +// walk external cleanup list and try to destroy each individual entry +int32_t NetConnCommonProcessExternalCleanupList(NetConnCommonRefT *pCommonRef); + +// decrement and verify the reference count for shutdown +int32_t NetConnCommonCheckRef(NetConnCommonRefT *pCommonRef); + +// set module behavior based on input selector +int32_t NetConnCommonControl(NetConnCommonRefT *pCommonRef, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue, void *pValue2); + +// check general network connection status (added param) +int32_t NetConnCommonStatus(NetConnCommonRefT *pCommonRef, int32_t iKind, int32_t iData, void *pBuf, int32_t iBufSize); +#ifdef __cplusplus +} +#endif + +#endif // _netconcommon_h + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/netconnlocaluser.cpp b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconnlocaluser.cpp new file mode 100644 index 00000000..c9dc834b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconnlocaluser.cpp @@ -0,0 +1,474 @@ +/*H********************************************************************************/ +/*! + \File netconnlocaluser.cpp + + \Description + Cross-platform netconn data types and functions. + + \Copyright + Copyright (c) 2014 Electronic Arts Inc. + + \Version 05/21/2014 (mclouatre) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "netconnlocaluser.h" + +#ifndef DIRTYCODE_PS5 +#include "IEAUser/IEAUser.h" +#endif + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +static NetConnLocalUserRefT *_NetConnLocalUser_pRef = NULL; //!< module state pointer + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _NetConnLocalUserEnqueueIEAUserEvent + + \Description + Add the specified entry at the head of the specified list. + + \Input *pUserEventEntry - event entry to be enqueued + \Input **pList - list to add the event to + + \Version 05/09/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _NetConnLocalUserEnqueueIEAUserEvent(NetConnIEAUserEventT *pUserEventEntry, NetConnIEAUserEventT **pList) +{ + if (*pList != NULL) + { + pUserEventEntry->pNext = *pList; + } + + *pList = pUserEventEntry; +} + +/*F********************************************************************************/ +/*! + \Function _NetConnLocalUserDequeueIEAUserEvent + + \Description + Get an event entry from the tail fo the specified list. + + \Input **pList - list to dequeue from + + \Output + NetConnIEAUserEventT * - pointer to free IEAUserEvent entry + + \Version 05/09/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static NetConnIEAUserEventT * _NetConnLocalUserDequeueIEAUserEvent(NetConnIEAUserEventT **pList) +{ + NetConnIEAUserEventT *pUserEventEntry = NULL; + + // find tail + if (*pList != NULL) + { + NetConnIEAUserEventT *pPrevious = NULL; + for (pUserEventEntry = *pList; pUserEventEntry->pNext != NULL; pUserEventEntry = pUserEventEntry->pNext) + { + pPrevious = pUserEventEntry; + } + + if (pPrevious) + { + pPrevious->pNext = NULL; + } + else + { + *pList = NULL; + } + } + + return(pUserEventEntry); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnLocalUserGetFreeIEAUserEvent + + \Description + Get a free IEAUserEvent entry from the free list. + + \Input *pLocalUserRef - netconn module state + + \Output + NetConnIEAUserEventT * - pointer to free IEAUserEvent entry + + \Version 05/09/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static NetConnIEAUserEventT * _NetConnLocalUserGetFreeIEAUserEvent(NetConnLocalUserRefT *pLocalUserRef) +{ + NetConnIEAUserEventT *pUserEventEntry; + + // check if free list is empty + if (pLocalUserRef->pIEAUserFreeEventList == NULL) + { + int32_t iLoop = 0; + + // add 4 entries to the free list + for (iLoop = 0; iLoop < 4; iLoop++) + { + pUserEventEntry = (NetConnIEAUserEventT *) DirtyMemAlloc(sizeof(NetConnIEAUserEventT), NETCONN_MEMID, pLocalUserRef->iMemGroup, pLocalUserRef->pMemGroupUserData); + + if (pUserEventEntry) + { + ds_memclr(pUserEventEntry, sizeof(*pUserEventEntry)); + + _NetConnLocalUserEnqueueIEAUserEvent(pUserEventEntry, &pLocalUserRef->pIEAUserFreeEventList); + + NetPrintfVerbose((pLocalUserRef->iDebugLevel, 0, "netconnlocaluser: [%p] allocated a new free user event entry\n", pUserEventEntry)); + } + else + { + NetPrintf(("netconnlocaluser: failed to allocate a new user event entry\n")); + } + } + } + + pUserEventEntry = _NetConnLocalUserDequeueIEAUserEvent(&pLocalUserRef->pIEAUserFreeEventList); + + return(pUserEventEntry); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnLocalUserAddIEAUserEvent + + \Description + Add an entry to the list of IEAUserEvents + + \Input *pLocalUserRef - netconn module state + \Input iLocalUserIndex - local user index + \Input *pIEAUser - pointer to IEAUser object + \Input eEvent - event type + + \Output + int32_t - 0 for success; negative for error + + \Version 05/09/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _NetConnLocalUserAddIEAUserEvent(NetConnLocalUserRefT *pLocalUserRef, int32_t iLocalUserIndex, const EA::User::IEAUser *pIEAUser, NetConnIEAUserEventTypeE eEvent) +{ + int32_t iResult = 0; + + NetCritEnter(&pLocalUserRef->crit); + + NetConnIEAUserEventT *pUserEventEntry = _NetConnLocalUserGetFreeIEAUserEvent(pLocalUserRef); + + if (pUserEventEntry) + { + pUserEventEntry->eEvent = eEvent; + pUserEventEntry->iLocalUserIndex = iLocalUserIndex; + pUserEventEntry->pIEAUser = pIEAUser; +#ifndef DIRTYCODE_PS5 + pUserEventEntry->pIEAUser->AddRef(); +#endif + + _NetConnLocalUserEnqueueIEAUserEvent(pUserEventEntry, &pLocalUserRef->pIEAUserEventList); + +#ifndef DIRTYCODE_PS5 + NetPrintfVerbose((pLocalUserRef->iDebugLevel, 0, "netconnlocaluser: [%p] IEAUser event queued (local user index = %d, local user id = 0x%08x, event = %s)\n", + pUserEventEntry, iLocalUserIndex, (int32_t)pIEAUser->GetUserID(), (eEvent==NETCONN_EVENT_IEAUSER_ADDED?"added":"removed"))); +#endif + } + else + { + iResult = -1; + } + + NetCritLeave(&pLocalUserRef->crit); + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnLocalUserClearIEAUserEventList + + \Description + Clear list of pending IEAUser events. + + \Input *pLocalUserRef - netconn module state + + \Version 05/09/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _NetConnLocalUserClearIEAUserEventList(NetConnLocalUserRefT *pLocalUserRef) +{ + NetConnIEAUserEventT *pUserEventEntry; + + NetCritEnter(&pLocalUserRef->crit); + + while ((pUserEventEntry = _NetConnLocalUserDequeueIEAUserEvent(&pLocalUserRef->pIEAUserEventList)) != NULL) + { + // return event entry to free list +#ifndef DIRTYCODE_PS5 + pUserEventEntry->pIEAUser->Release(); +#endif + ds_memclr(pUserEventEntry, sizeof(*pUserEventEntry)); + _NetConnLocalUserEnqueueIEAUserEvent(pUserEventEntry, &pLocalUserRef->pIEAUserFreeEventList); + } + + NetCritLeave(&pLocalUserRef->crit); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnLocalUserClearIEAUserFreeEventList + + \Description + Clear list of free IEAUser events. + + \Input *pLocalUserRef - netconn module state + + \Version 05/09/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _NetConnLocalUserClearIEAUserFreeEventList(NetConnLocalUserRefT *pLocalUserRef) +{ + NetConnIEAUserEventT *pUserEventEntry; + + NetCritEnter(&pLocalUserRef->crit); + + while ((pUserEventEntry = _NetConnLocalUserDequeueIEAUserEvent(&pLocalUserRef->pIEAUserFreeEventList)) != NULL) + { + DirtyMemFree(pUserEventEntry, NETCONN_MEMID, pLocalUserRef->iMemGroup, pLocalUserRef->pMemGroupUserData); + + NetPrintfVerbose((pLocalUserRef->iDebugLevel, 0, "netconnlocaluser: [%p] freed user event entry\n", pUserEventEntry)); + } + + NetCritLeave(&pLocalUserRef->crit); +} + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function NetConnLocalUserInit + + \Description + Start up local user functionality. + + \Input *pNetConn - parent NetConn reference + \Input *pAddUserCb - added user event handler + \Input *pRemoveUserCb - removed user event handler + + \Output + NetConnLocalUserRefT - local user reference on success; NULL on error + + \Version 10/24/2017 (amakoukji) +*/ +/********************************************************************************F*/ +NetConnLocalUserRefT* NetConnLocalUserInit(NetConnRefT *pNetConn, NetConnAddLocalUserCallbackT *pAddUserCb, NetConnRemoveLocalUserCallbackT *pRemoveUserCb) +{ + NetConnLocalUserRefT *pLocalUserRef = _NetConnLocalUser_pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // refcount if already started + if (pLocalUserRef != NULL) + { + NetPrintf(("netconnlocaluser: NetConnLocalUserInit() called while module is already active\n")); + return(NULL); + } + + // alloc and init ref + if ((pLocalUserRef = (NetConnLocalUserRefT*)DirtyMemAlloc(sizeof(NetConnLocalUserRefT), NETCONN_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("netconnlocaluser: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pLocalUserRef, sizeof(NetConnLocalUserRefT)); + pLocalUserRef->iMemGroup = iMemGroup; + pLocalUserRef->pMemGroupUserData = pMemGroupUserData; + pLocalUserRef->pAddUserCb = pAddUserCb; + pLocalUserRef->pRemoveUserCb = pRemoveUserCb; + pLocalUserRef->pNetConn = pNetConn; + + NetCritInit(&pLocalUserRef->crit, "netconnlocaluserCrit"); + + // save ref + _NetConnLocalUser_pRef = pLocalUserRef; + + // successful completion + return(pLocalUserRef); +} + +/*F********************************************************************************/ +/*! + \Function NetConnLocalUserDestroy + + \Description + Shutdown common functionality. + + \Input *pLocalUserRef - module state + + \Version 10/24/2017 (amakoukji) +*/ +/********************************************************************************F*/ +void NetConnLocalUserDestroy(NetConnLocalUserRefT *pLocalUserRef) +{ + _NetConnLocalUserClearIEAUserEventList(pLocalUserRef); + _NetConnLocalUserClearIEAUserFreeEventList(pLocalUserRef); + + NetCritKill(&pLocalUserRef->crit); + + // dispose of ref + DirtyMemFree(pLocalUserRef, NETCONN_MEMID, pLocalUserRef->iMemGroup, pLocalUserRef->pMemGroupUserData); + _NetConnLocalUser_pRef = NULL; +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnLocalUserAdd + + \Description + Use this function to tell netconn about a newly detected local user on the local console. + + \Input iLocalUserIndex - index at which DirtySDK needs to insert this user it its internal user array + \Input pLocalUser - pointer to associated IEAUser + + \Output + int32_t - 0 for success; negative for error + + \Version 01/16/2014 (mclouatre) +*/ +/*************************************************************************************************F*/ +int32_t NetConnLocalUserAdd(int32_t iLocalUserIndex, const EA::User::IEAUser *pLocalUser) +{ + NetConnLocalUserRefT *pLocalUserRef = _NetConnLocalUser_pRef; + int32_t iRetCode = 0; // default to success + + if ((iLocalUserIndex >= 0) && (iLocalUserIndex < NETCONN_MAXLOCALUSERS)) + { + iRetCode = _NetConnLocalUserAddIEAUserEvent(pLocalUserRef, iLocalUserIndex, pLocalUser, NETCONN_EVENT_IEAUSER_ADDED); + } + else + { + NetPrintf(("netconnlocaluser: NetConnLocalUserAddLocalUser() called with an invalid index (%d)\n", iLocalUserIndex)); + iRetCode = -1; + } + + return(iRetCode); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnLocalUserRemove + + \Description + Use this function to tell netconn about a local user that no longer exists + + \Input iLocalUserIndex - index in the internal DirtySDK user array at which the user needs to be cleared + pass -1 when the index is unknown and a lookup will be done + \Input pLocalUser - pointer to associated IEAUser + + \Output + int32_t - 0 for success; negative for error + + \Version 01/16/2014 (mclouatre) +*/ +/*************************************************************************************************F*/ +int32_t NetConnLocalUserRemove(int32_t iLocalUserIndex, const EA::User::IEAUser *pLocalUser) +{ + NetConnLocalUserRefT *pLocalUserRef = _NetConnLocalUser_pRef; + int32_t iRetCode = 0; // default to success + + if (iLocalUserIndex == -1) + { + NetCritEnter(&pLocalUserRef->crit); + iLocalUserIndex = NetConnStatus('usri', 0, (void *)pLocalUser, 0); + NetCritLeave(&pLocalUserRef->crit); + } + + if ((iLocalUserIndex >= 0) && (iLocalUserIndex < NETCONN_MAXLOCALUSERS)) + { + iRetCode = _NetConnLocalUserAddIEAUserEvent(pLocalUserRef, iLocalUserIndex, pLocalUser, NETCONN_EVENT_IEAUSER_REMOVED); + } + else + { + NetPrintf(("netconnlocaluser: NetConnLocalUserRemoveLocalUser() called with an invalid index (%d)\n", iLocalUserIndex)); + iRetCode = -1; + } + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function NetConnLocalUserUpdate + + \Description + Update our internally maintained array of NetConnUsers from the array + of IEAUsers + + \Input *pLocalUserRef - module reference + + \Version 01/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void NetConnLocalUserUpdate(NetConnLocalUserRefT *pLocalUserRef) +{ + NetConnIEAUserEventT *pUserEventEntry; + + if (pLocalUserRef != NULL) + { + NetCritEnter(&pLocalUserRef->crit); + + // process events + while ((pUserEventEntry = _NetConnLocalUserDequeueIEAUserEvent(&pLocalUserRef->pIEAUserEventList)) != NULL) + { + if (pUserEventEntry->eEvent == NETCONN_EVENT_IEAUSER_ADDED) + { + if (pLocalUserRef->pAddUserCb != NULL) + { + pLocalUserRef->pAddUserCb(pLocalUserRef, pUserEventEntry->iLocalUserIndex, pUserEventEntry->pIEAUser); + } + } + else + { + if (pLocalUserRef->pRemoveUserCb != NULL) + { + pLocalUserRef->pRemoveUserCb(pLocalUserRef, pUserEventEntry->iLocalUserIndex, pUserEventEntry->pIEAUser); + } + } + +#ifndef DIRTYCODE_PS5 + NetPrintfVerbose((pLocalUserRef->iDebugLevel, 0, "netconnlocaluser: [%p] IEAUser event dequeued (local user index = %d, local user id = 0x%08x, event = %s)\n", + pUserEventEntry, pUserEventEntry->iLocalUserIndex, (int32_t)pUserEventEntry->pIEAUser->GetUserID(), (pUserEventEntry->eEvent == NETCONN_EVENT_IEAUSER_ADDED ? "added" : "removed"))); + + // return event entry to free list + pUserEventEntry->pIEAUser->Release(); +#endif + ds_memclr(pUserEventEntry, sizeof(*pUserEventEntry)); + _NetConnLocalUserEnqueueIEAUserEvent(pUserEventEntry, &pLocalUserRef->pIEAUserFreeEventList); + } + + NetCritLeave(&pLocalUserRef->crit); + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/netconnlocaluser.h b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconnlocaluser.h new file mode 100644 index 00000000..17192e6e --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/netconnlocaluser.h @@ -0,0 +1,94 @@ +/*H********************************************************************************/ +/*! + \File netconnlocaluser.h + + \Description + Wrapper for EA::User::IEAUser functionality + + \Copyright + Copyright (c) 2017 Electronic Arts Inc. + + \Version 10/24/2017 (amakoukji) First Version +*/ +/********************************************************************************H*/ + +#ifndef _netconnlocaluser_h +#define _netconnlocaluser_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ +struct NetConnRefT; + +//! user event callback function prototype +typedef void (NetConnAddLocalUserCallbackT)(struct NetConnLocalUserRefT *pCommonRef, int32_t iLocalUserIndex, const EA::User::IEAUser *pIEAUser); +typedef void (NetConnRemoveLocalUserCallbackT)(struct NetConnLocalUserRefT *pCommonRef, int32_t iLocalUserIndex, const EA::User::IEAUser *pIEAUser); + + +typedef enum NetConnIEAUserEventTypeE +{ + NETCONN_EVENT_IEAUSER_ADDED = 0, + NETCONN_EVENT_IEAUSER_REMOVED +} NetConnIEAUserEventTypeE; + +typedef struct NetConnIEAUserEventT +{ + struct NetConnIEAUserEventT *pNext; //!< linked list + const EA::User::IEAUser *pIEAUser; //!< IEAUser reference + NetConnIEAUserEventTypeE eEvent; //!< event type + int32_t iLocalUserIndex; //!< local user index +} NetConnIEAUserEventT; + +typedef struct NetConnLocalUserRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + int32_t iDebugLevel; + + NetConnIEAUserEventT *pIEAUserFreeEventList; //!< list of free IEAUser + NetConnIEAUserEventT *pIEAUserEventList; //!< list of pending NetConnIEAUserEvents - populated by customers with NetConnAddLocaUser()/NetConnRemoveUser() + + NetConnAddLocalUserCallbackT *pAddUserCb; + NetConnRemoveLocalUserCallbackT *pRemoveUserCb; + + NetConnRefT *pNetConn; //!< parent + + NetCritT crit; +} NetConnLocalUserRefT; + + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// handle shutdown functionality +void NetConnLocalUserDestroy(NetConnLocalUserRefT *pLocalUserRef); + +// handle startup functionality +NetConnLocalUserRefT* NetConnLocalUserInit(NetConnRefT *pNetConn, NetConnAddLocalUserCallbackT *pAddUserCb, NetConnRemoveLocalUserCallbackT *pRemoveUserCb); + +// handle add user functionality +int32_t NetConnLocalUserAdd(int32_t iLocalUserIndex, const EA::User::IEAUser *pLocalUser); + +// handle remove user functionality +int32_t NetConnLocalUserRemove(int32_t iLocalUserIndex, const EA::User::IEAUser *pLocalUser); + +// handle user update functionality +void NetConnLocalUserUpdate(NetConnLocalUserRefT *pLocalUserRef); + +#ifdef __cplusplus +} +#endif + +#endif // _netconnlocaluser_h + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtyerrpc.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtyerrpc.c new file mode 100644 index 00000000..48892e7b --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtyerrpc.c @@ -0,0 +1,255 @@ +/*H********************************************************************************/ +/*! + \File dirtyerrpc.c + + \Description + Dirtysock debug error routines. + + \Copyright + Copyright (c) 2014 Electronic Arts Inc. + + \Version 07/01/2014 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyerr.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +#if DIRTYSOCK_ERRORNAMES + +static DirtyErrT _DirtyErr_List[] = +{ + /* + WinError.h" + */ + DIRTYSOCK_ErrorName(ERROR_INVALID_FUNCTION), // 1 + DIRTYSOCK_ErrorName(ERROR_ACCESS_DENIED), // 5 + DIRTYSOCK_ErrorName(ERROR_NO_MORE_FILES), // 18 + DIRTYSOCK_ErrorName(ERROR_ALREADY_ASSIGNED), // 85 + DIRTYSOCK_ErrorName(ERROR_INVALID_PARAMETER), // 87 + DIRTYSOCK_ErrorName(ERROR_INSUFFICIENT_BUFFER), // 122 + DIRTYSOCK_ErrorName(ERROR_ALREADY_EXISTS), // 183 + DIRTYSOCK_ErrorName(ERROR_NO_DATA), // 232 + DIRTYSOCK_ErrorName(ERROR_INVALID_PORT_ATTRIBUTES), // 545 + DIRTYSOCK_ErrorName(ERROR_IO_INCOMPLETE), // 996 + DIRTYSOCK_ErrorName(ERROR_IO_PENDING), // 997 + DIRTYSOCK_ErrorName(ERROR_NOT_FOUND), // 1168 + DIRTYSOCK_ErrorName(ERROR_NO_MATCH), // 1169 + DIRTYSOCK_ErrorName(ERROR_CONNECTION_INVALID), // 1229 + DIRTYSOCK_ErrorName(ERROR_SERVICE_NOT_FOUND), // 1243 + DIRTYSOCK_ErrorName(ERROR_FUNCTION_FAILED), // 1627 + + DIRTYSOCK_ErrorName(RPC_S_CALL_FAILED), // 1726L + DIRTYSOCK_ErrorName(RPC_S_CALL_FAILED_DNE), // 1727L + + DIRTYSOCK_ErrorName(OR_INVALID_OXID), // 1910L -- the object exporter specified was not found + + // Misc other errors + DIRTYSOCK_ErrorName(ERROR_INVALID_STATE), // 5023L - The group or resource is not in the correct state to perform the requested operation + + /* + WinSock2.h + */ + // WinSock error codes from 10000-11999 + DIRTYSOCK_ErrorName(WSAEINTR), // 10004L - A blocking operation was interrupted by a call to WSACancelBlockingCall. + DIRTYSOCK_ErrorName(WSAEBADF), // 10009L - The file handle supplied is not valid. + DIRTYSOCK_ErrorName(WSAEACCES), // 10013L - An attempt was made to access a socket in a way forbidden by its access permissions. + DIRTYSOCK_ErrorName(WSAEFAULT), // 10014L - The system detected an invalid pointer address in attempting to use a pointer argument in a call. + DIRTYSOCK_ErrorName(WSAEINVAL), // 10022L - An invalid argument was supplied. + DIRTYSOCK_ErrorName(WSAEMFILE), // 10024L - Too many open sockets. + DIRTYSOCK_ErrorName(WSAEWOULDBLOCK), // 10035L - A non-blocking socket operation could not be completed immediately. + DIRTYSOCK_ErrorName(WSAEINPROGRESS), // 10036L - A blocking operation is currently executing. + DIRTYSOCK_ErrorName(WSAEALREADY), // 10037L - An operation was attempted on a non-blocking socket that already had an operation in progress. + DIRTYSOCK_ErrorName(WSAENOTSOCK), // 10038L - An operation was attempted on something that is not a socket. + DIRTYSOCK_ErrorName(WSAEDESTADDRREQ), // 10039L - A required address was omitted from an operation on a socket. + DIRTYSOCK_ErrorName(WSAEMSGSIZE), // 10040L - A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself. + DIRTYSOCK_ErrorName(WSAEPROTOTYPE), // 10041L - A protocol was specified in the socket function call that does not support the semantics of the socket type requested. + DIRTYSOCK_ErrorName(WSAENOPROTOOPT), // 10042L - An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call. + DIRTYSOCK_ErrorName(WSAEPROTONOSUPPORT), // 10043L - The requested protocol has not been configured into the system, or no implementation for it exists. + DIRTYSOCK_ErrorName(WSAESOCKTNOSUPPORT), // 10044L - The support for the specified socket type does not exist in this address family. + DIRTYSOCK_ErrorName(WSAEOPNOTSUPP), // 10045L - The attempted operation is not supported for the type of object referenced. + DIRTYSOCK_ErrorName(WSAEPFNOSUPPORT), // 10046L - The protocol family has not been configured into the system or no implementation for it exists. + DIRTYSOCK_ErrorName(WSAEAFNOSUPPORT), // 10047L - An address incompatible with the requested protocol was used. + DIRTYSOCK_ErrorName(WSAEADDRINUSE), // 10048L - Only one usage of each socket address (protocol/network address/port) is normally permitted. + DIRTYSOCK_ErrorName(WSAEADDRNOTAVAIL), // 10049L - The requested address is not valid in its context. + DIRTYSOCK_ErrorName(WSAENETDOWN), // 10050L - A socket operation encountered a dead network. + DIRTYSOCK_ErrorName(WSAENETUNREACH), // 10051L - A socket operation was attempted to an unreachable network. + DIRTYSOCK_ErrorName(WSAENETRESET), // 10052L - The connection has been broken due to keep-alive activity detecting a failure while the operation was in progress. + DIRTYSOCK_ErrorName(WSAECONNABORTED), // 10053L - An established connection was aborted by the software in your host machine. + DIRTYSOCK_ErrorName(WSAECONNRESET), // 10054L - An existing connection was forcibly closed by the remote host. + DIRTYSOCK_ErrorName(WSAENOBUFS), // 10055L - An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full. + DIRTYSOCK_ErrorName(WSAEISCONN), // 10056L - A connect request was made on an already connected socket. + DIRTYSOCK_ErrorName(WSAENOTCONN), // 10057L - A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied. + DIRTYSOCK_ErrorName(WSAESHUTDOWN), // 10058L - A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call. + DIRTYSOCK_ErrorName(WSAETOOMANYREFS), // 10059L - Too many references to some kernel object. + DIRTYSOCK_ErrorName(WSAETIMEDOUT), // 10060L - A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. + DIRTYSOCK_ErrorName(WSAECONNREFUSED), // 10061L - No connection could be made because the target machine actively refused it. + DIRTYSOCK_ErrorName(WSAELOOP), // 10062L - Cannot translate name. + DIRTYSOCK_ErrorName(WSAENAMETOOLONG), // 10063L - Name component or name was too long. + DIRTYSOCK_ErrorName(WSAEHOSTDOWN), // 10064L - A socket operation failed because the destination host was down. + DIRTYSOCK_ErrorName(WSAEHOSTUNREACH), // 10065L - A socket operation was attempted to an unreachable host. + DIRTYSOCK_ErrorName(WSAENOTEMPTY), // 10066L - Cannot remove a directory that is not empty. + DIRTYSOCK_ErrorName(WSAEPROCLIM), // 10067L - A Windows Sockets implementation may have a limit on the number of applications that may use it simultaneously. + DIRTYSOCK_ErrorName(WSAEUSERS), // 10068L - Ran out of quota. + DIRTYSOCK_ErrorName(WSAEDQUOT), // 10069L - Ran out of disk quota. + DIRTYSOCK_ErrorName(WSAESTALE), // 10070L - File handle reference is no longer available. + DIRTYSOCK_ErrorName(WSAEREMOTE), // 10071L - Item is not available locally. + DIRTYSOCK_ErrorName(WSASYSNOTREADY), // 10091L - WSAStartup cannot function at this time because the underlying system it uses to provide network services is currently unavailable. + DIRTYSOCK_ErrorName(WSAVERNOTSUPPORTED), // 10092L - The Windows Sockets version requested is not supported. + DIRTYSOCK_ErrorName(WSANOTINITIALISED), // 10093L - Either the application has not called WSAStartup, or WSAStartup failed. + DIRTYSOCK_ErrorName(WSAEDISCON), // 10101L - Returned by WSARecv or WSARecvFrom to indicate the remote party has initiated a graceful shutdown sequence. + DIRTYSOCK_ErrorName(WSAENOMORE), // 10102L - No more results can be returned by WSALookupServiceNext. + DIRTYSOCK_ErrorName(WSAECANCELLED), // 10103L - A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled. + DIRTYSOCK_ErrorName(WSAEINVALIDPROCTABLE), // 10104L - The procedure call table is invalid. + DIRTYSOCK_ErrorName(WSAEINVALIDPROVIDER), // 10105L - The requested service provider is invalid. + DIRTYSOCK_ErrorName(WSAEPROVIDERFAILEDINIT),// 10106L - The requested service provider could not be loaded or initialized. + DIRTYSOCK_ErrorName(WSASYSCALLFAILURE), // 10107L - A system call that should never fail has failed. + DIRTYSOCK_ErrorName(WSASERVICE_NOT_FOUND), // 10108L - No such service is known. The service cannot be found in the specified name space. + DIRTYSOCK_ErrorName(WSATYPE_NOT_FOUND), // 10109L - The specified class was not found. + DIRTYSOCK_ErrorName(WSA_E_NO_MORE), // 10110L - No more results can be returned by WSALookupServiceNext. + DIRTYSOCK_ErrorName(WSA_E_CANCELLED), // 10111L - A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled. + DIRTYSOCK_ErrorName(WSAEREFUSED), // 10112L - A database query failed because it was actively refused. + DIRTYSOCK_ErrorName(WSAHOST_NOT_FOUND), // 11001L - No such host is known. + + // NULL terminate + DIRTYSOCK_ListEnd() +}; + +#define DIRTYERR_NUMERRORS (sizeof(_DirtyErr_List) / sizeof(_DirtyErr_List[0])) + +#endif // #if DIRTYSOCK_ERRORNAMES + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function DirtyErrNameList + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized, or formats it as a hex + number if not. + + \Input *pBuffer - [out] pointer to output buffer to store result + \Input iBufSize - size of output buffer + \Input uError - error code to format + \Input *pList - error list to use + + \Version 06/13/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyErrNameList(char *pBuffer, int32_t iBufSize, uint32_t uError, const DirtyErrT *pList) +{ + static char strUnknown[16]; + + #if DIRTYSOCK_ERRORNAMES + int32_t iErr; + + // first try to match exactly + for (iErr = 0; pList[iErr].uError != DIRTYSOCK_LISTTERM; iErr++) + { + if ((pList[iErr].uError & ~0x80000000) == (uError & ~0x80000000)) + { + ds_snzprintf(pBuffer, iBufSize, "%s/0x%08x", pList[iErr].pErrorName, uError); + return; + } + } + + // if not found, try to match lower 16 bits of error code + for (iErr = 0; pList[iErr].uError != DIRTYSOCK_LISTTERM; iErr++) + { + if ((pList[iErr].uError & ~0xffff0000) == (uError & ~0xffff0000)) + { + ds_snzprintf(pBuffer, iBufSize, "%s/0x%08x", pList[iErr].pErrorName, uError); + return; + } + } + #endif + + ds_snzprintf(pBuffer, iBufSize, "0x%08x", uError); +} + +/*F********************************************************************************/ +/*! + \Function DirtyErrName + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized or formats it as a hex + number if not. + + \Input *pBuffer - [out] pointer to output buffer to store result + \Input iBufSize - size of output buffer + \Input uError - error code to format + + \Version 06/13/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyErrName(char *pBuffer, int32_t iBufSize, uint32_t uError) +{ + #if DIRTYSOCK_ERRORNAMES + DirtyErrNameList(pBuffer, iBufSize, uError, _DirtyErr_List); + #endif +} + +/*F********************************************************************************/ +/*! + \Function DirtyErrGetNameList + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized or formats it as a hex + number if not. + + \Input uError - error code to format + \Input *pList - error list to use + + \Output + const char *- pointer to error name or error formatted in hex + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +const char *DirtyErrGetNameList(uint32_t uError, const DirtyErrT *pList) +{ + static char strName[8][64]; + static uint8_t uLast = 0; + char *pName = strName[uLast++]; + if (uLast > 7) uLast = 0; + DirtyErrNameList(pName, sizeof(strName[0]), uError, pList); + return(pName); +} + +/*F********************************************************************************/ +/*! + \Function DirtyErrGetName + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized or formats it as a hex + number if not. + + \Input uError - error code to format + + \Output + const char *- pointer to error name or error formatted in hex + + \Version 06/13/2005 (jbrookes) +*/ +/********************************************************************************F*/ +const char *DirtyErrGetName(uint32_t uError) +{ + static char strName[128]; + DirtyErrName(strName, sizeof(strName), uError); + return(strName); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtylibwin.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtylibwin.c new file mode 100644 index 00000000..d0c7bc76 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtylibwin.c @@ -0,0 +1,423 @@ +/*H********************************************************************************/ +/*! + + \File dirtylibwin.c + + \Description + Platform specific support library for network code. Suppplies + simple time, memory, and semaphore functions. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 01/02/02 (GWS) Initial version (ported from PS2 IOP) + +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#pragma warning(push,0) +#include +#pragma warning(pop) + +#include "DirtySDK/platform.h" + +#if !defined(DIRTYCODE_XBOXONE) +#include +#endif + +#include +#include +#include + +#include +#include "DirtySDK/dirtysock/dirtythread.h" +#include "DirtySDK/dirtysock/dirtylib.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +static uint32_t _NetLibGetTickCount2(void); + +/*** Variables ********************************************************************/ + +// Private variables + +// idle thread state +static volatile int32_t g_idlelife = -1; + +// queryperformancecounter frequency (static init so calls to NetTick() before NetLibCreate won't crash) +static LARGE_INTEGER _NetLib_lFreq = { 1 }; + +#if defined(DIRTYCODE_PC) || defined(DIRTYCODE_GDK) +uint32_t _NetLib_bUseHighResTimer = FALSE; +#else +uint32_t _NetLib_bUseHighResTimer = TRUE; +#endif + +// selected timer function +static uint32_t (*_NetLib_pTimerFunc)(void) = _NetLibGetTickCount2; + +// Public variables + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _NetLibThread + + \Description + Thread to handle special library tasks + + \Input _null - unused + + \Version 01/02/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static void _NetLibThread(void *_null) +{ + char strThreadId[32]; + + // get the thread id + DirtyThreadGetThreadId(strThreadId, sizeof(strThreadId)); + + // show we are alive + NetPrintf(("dirtylibwin: idle thread running (thid=%s)\n", strThreadId)); + g_idlelife = 1; + + // run while we have sema + while (g_idlelife == 1) + { + // call idle functions + NetIdleCall(); + // wait for next tick + Sleep(50); + } + + // report termination + NetPrintf(("dirtylibwin: idle thread exiting\n")); + + // show we are dead + g_idlelife = 0; +} + +/*F********************************************************************************/ +/*! + \Function _NetLibGetTickCount + + \Description + Millisecond-accurate tick counter. + + \Output + uint32_t - millisecond tick counter + + \Version 11/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _NetLibGetTickCount(void) +{ + LARGE_INTEGER lCount; + QueryPerformanceCounter(&lCount); + return((uint32_t)((lCount.QuadPart*1000)/_NetLib_lFreq.QuadPart)); +} + +/*F********************************************************************************/ +/*! + \Function _NetLibGetTickCount2 + + \Description + Millisecond tick counter, with variable precision. + + \Output + uint32_t - millisecond tick counter + + \Version 11/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _NetLibGetTickCount2(void) +{ + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + return(_NetLibGetTickCount()); //$$TODO -- better method than querying high performance counter? + #else + return(timeGetTime()); + #endif +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function NetLibCreate + + \Description + Initialize the network library functions + + \Input iThreadPrio - priority to start the _NetLibThread with + \Input iThreadStackSize - stack size to start the _NetLibThread with (in bytes) + \Input iThreadCpuAffinity - cpu affinity to start the _NetLibThread with + + \Version 01/02/2002 (gschaefer) +*/ +/********************************************************************************F*/ +void NetLibCreate(int32_t iThreadPrio, int32_t iThreadStackSize, int32_t iThreadCpuAffinity) +{ + DirtyThreadConfigT ThreadConfig; + int32_t iResult; + + // init common netlib functionality + NetLibCommonInit(); + + // configure thread parameters + ds_memclr(&ThreadConfig, sizeof(ThreadConfig)); + ThreadConfig.pName = "NetLib"; + ThreadConfig.iPriority = iThreadPrio; + ThreadConfig.iAffinity = iThreadCpuAffinity; + ThreadConfig.iVerbosity = 1; + + // create a worker thread + if ((iResult = DirtyThreadCreate(_NetLibThread, NULL, &ThreadConfig)) != 0) + { + NetPrintf(("dirtylibwin: unable to create netidle thread (err=%d)\n", iResult)); + g_idlelife = 0; + } + + // set up high-performance timer, if available + if (QueryPerformanceFrequency(&_NetLib_lFreq) == 0) + { + NetPrintf(("dirtylibwin: high-frequency performance counter not available\n")); + } + // use high-performance timer for tick counter? + if (_NetLib_bUseHighResTimer) + { + _NetLib_pTimerFunc = _NetLibGetTickCount; + } +} + +/*F********************************************************************************/ +/*! + \Function NetLibDestroy + + \Description + Destroy the network lib + + \Input uShutdownFlags - NET_SHUTDOWN_* flags + + \Version 01/02/02 (gschaefer) +*/ +/********************************************************************************F*/ +void NetLibDestroy(uint32_t uShutdownFlags) +{ + // if the thread is running + if (g_idlelife == 1) + { + // signal a shutdown + g_idlelife = 2; + + // wait for thread to terminate + while (g_idlelife > 0) + { + Sleep(1); + } + } + + // shut down common functionality + NetLibCommonShutdown(); +} + +/*F********************************************************************************/ +/*! + \Function NetTick + + \Description + Return some kind of increasing tick count with millisecond scale (does + not need to have millisecond precision, but higher precision is better). + + \Output + uint32_t - millisecond tick count + + \Version 09/15/1999 (gschaefer) +*/ +/********************************************************************************F*/ +uint32_t NetTick(void) +{ + return(_NetLib_pTimerFunc()); +} + +/*F********************************************************************************/ +/*! + \Function NetTickUsec + + \Description + Return increasing tick count in microseconds. Used for performance timing + purposes. + + \Output + uint64_t - microsecond tick count + + \Version 01/30/2015 (jbrookes) +*/ +/********************************************************************************F*/ +uint64_t NetTickUsec(void) +{ + LARGE_INTEGER lCount; + QueryPerformanceCounter(&lCount); + return((uint64_t)((lCount.QuadPart*1000000)/_NetLib_lFreq.QuadPart)); +} + +/*F********************************************************************************/ +/*! + \Function NetLocalTime + + \Description + This converts the input GMT time to the local time as specified by the + system clock. This function follows the re-entrant localtime_r function + signature. + + \Input *pTm - storage for localtime output + \Input uElap - GMT time + + \Output + struct tm * - pointer to localtime result + + \Version 04/23/2008 (jbrookes) +*/ +/********************************************************************************F*/ +struct tm *NetLocalTime(struct tm *pTm, uint64_t uElap) +{ + time_t uTimeT = (time_t)uElap; + localtime_s(pTm, &uTimeT); + return(pTm); +} + +/*F********************************************************************************/ +/*! + \Function NetPlattimeToTime + + \Description + This converts the input platform-specific time data structure to the + generic time data structure. + + \Input *pTm - generic time data structure to be filled by the function + \Input *pPlatTime - pointer to the platform-specific data structure + + \Output + struct tm * - NULL=failure; else pointer to user-provided generic time data structure + + \Notes + pPlatTime is expected to point to a __timeb64 on PC platforms, and a + SYSTEMTIME on Xbox One. + + \Version 05/08/2010 (mclouatre) +*/ +/********************************************************************************F*/ +struct tm *NetPlattimeToTime(struct tm *pTm, void *pPlatTime) +{ + #if defined(DIRTYCODE_PC) + struct __timeb64 timebuffer = *(struct __timeb64 *)pPlatTime; + struct tm resultTm = *(_localtime64(&(timebuffer.time))); + + pTm->tm_sec = resultTm.tm_sec; + pTm->tm_min = resultTm.tm_min; + pTm->tm_hour = resultTm.tm_hour; + pTm->tm_mday = resultTm.tm_mday; + pTm->tm_mon = resultTm.tm_mon; + pTm->tm_year = resultTm.tm_year; + pTm->tm_wday = resultTm.tm_wday; + pTm->tm_yday = resultTm.tm_yday; + pTm->tm_isdst =resultTm.tm_isdst; + #else // XBOXONE + SYSTEMTIME systemTime = *(SYSTEMTIME *)pPlatTime; + + pTm->tm_sec = systemTime.wSecond; + pTm->tm_min = systemTime.wMinute; + pTm->tm_hour = systemTime.wHour; + pTm->tm_mday = systemTime.wDay; + pTm->tm_mon = systemTime.wMonth - 1; + pTm->tm_year = systemTime.wYear - 1900; + pTm->tm_wday = systemTime.wDayOfWeek; + pTm->tm_yday = 0; + pTm->tm_isdst = 0; + #endif + + return(pTm); +} + +/*F********************************************************************************/ +/*! + \Function NetPlattimeToTimeMs + + \Description + This function retrieves the current date time and fills in the + generic time data structure prrovided. It has the option of returning millisecond + which is not part of the generic time data structure + + \Input *pTm - generic time data structure to be filled by the function + \Input *pImsec - output param for milisecond to be filled by the function (optional can be NULL) + + \Output + struct tm * - NULL=failure; else pointer to user-provided generic time data structure + + \Version 09/16/2014 (tcho) +*/ +/********************************************************************************F*/ +struct tm *NetPlattimeToTimeMs(struct tm *pTm , int32_t *pImsec) +{ + void *pPlatTime; + int32_t iMsec; + + #if defined(DIRTYCODE_PC) + struct __timeb64 timebuffer; + _ftime64_s(&timebuffer); + iMsec = timebuffer.millitm; + pPlatTime = (void *)&timebuffer; + #else // XBOXONE + SYSTEMTIME systemTime; + GetLocalTime( &systemTime ); + iMsec = systemTime.wMilliseconds; + pPlatTime = (void *)&systemTime; + #endif + + if (pImsec != NULL) + { + *pImsec = iMsec; + } + + if (pTm == NULL) + { + return(NULL); + } + + return(NetPlattimeToTime(pTm, pPlatTime)); +} + +/*F********************************************************************************/ +/*! + \Function NetTime + + \Description + This function replaces the standard library time() function. Main + differences are the missing pointer parameter (not needed) and the uint64_t + return value. The function returns 0 on unsupported platforms vs time which + returns -1. + + \Output + uint64_t - number of elapsed seconds since Jan 1, 1970. + + \Version 01/12/2005 (gschaefer) +*/ +/********************************************************************************F*/ +uint64_t NetTime(void) +{ + return((uint64_t)time(NULL)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtynetwin.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtynetwin.c new file mode 100644 index 00000000..779be9f4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/dirtynetwin.c @@ -0,0 +1,3180 @@ +/*H*************************************************************************************/ +/*! + \File dirtynetwin.c + + \Description + Provide a wrapper that translates the Winsock network interface + into DirtySock calls. In the case of Winsock, little translation + is needed since it is based off BSD sockets (as is DirtySock). + + \Copyright + Copyright (c) Electronic Arts 1999-2018. + + \Version 1.0 01/02/2002 (gschaefer) First Version +*/ +/*************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 // avoid windows.h including extra stuff, including winsock.h which we don't want +#endif + +#include "DirtySDK/platform.h" + +#if defined(DIRTYCODE_XBOXONE) +#pragma warning(push,0) +#include +#pragma warning(pop) +#include +#include +#include // for tcp keep alive definitions +#else +#pragma warning(push,0) +#include +#include +#include // for tcp keep alive definitions +#pragma warning(pop) +#endif + +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtythread.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/dirtyvers.h" + +#include "dirtynetpriv.h" // private include for dirtynet common functions + +/*** Defines ***************************************************************************/ + +#define SOCKET_MAXEVENTS (64) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! dirtysock connection socket structure +struct SocketT +{ + SocketT *pNext; //!< link to next active + SocketT *pKill; //!< link to next killed socket + + SOCKET uSocket; //!< winsock socket ref + + int32_t iFamily; //!< protocol family + int32_t iType; //!< protocol type + int32_t iProto; //!< protocol ident + + int8_t iOpened; //!< negative=error, zero=not open (connecting), positive=open + uint8_t uImported; //!< whether socket was imported or not + uint8_t bVirtual; //!< if true, socket is virtual + uint8_t uShutdown; //!< shutdown flag + + int32_t iLastError; //!< last socket error + + struct sockaddr LocalAddr; //!< local address + struct sockaddr RemoteAddr; //!< remote address + + uint16_t uVirtualPort; //!< virtual port, if set + uint8_t bAsyncRecv; //!< TRUE if async recv is enabled + int8_t iVerbose; //!< debug level + uint8_t bSendCbs; //!< TRUE if send cbs are enabled, false otherwise + uint8_t _pad0[3]; + + SocketRateT SendRate; //!< send rate estimation data + SocketRateT RecvRate; //!< recv rate estimation data + + uint32_t uCallMask; //!< valid callback events + uint32_t uCallLast; //!< last callback tick + uint32_t uCallIdle; //!< ticks between idle calls + void *pCallRef; //!< reference calback value + int32_t (*pCallback)(SocketT *pSocket, int32_t iFlags, void *pRef); + + WSAOVERLAPPED Overlapped; //!< overlapped i/o structure + NetCritT RecvCrit; //!< receive critical section + int32_t iAddrLen; //!< storage for async address length write by WSARecv + uint32_t uRecvFlag; //!< flags from recv operation + uint8_t bRecvInp; //!< if true, a receive operation is in progress + uint8_t bInCallback; //!< in a socket callback + uint8_t _pad1[2]; + + struct sockaddr RecvAddr; //!< receive address + struct sockaddr_in6 RecvAddr6; //!< receive address (ipv6) + + int32_t iPacketQueueResizePending; // -1 if no resize pending, new size otherwise + SocketPacketQueueT *pRecvQueue; + SocketPacketQueueEntryT *pRecvPacket; +}; + +//! local state +typedef struct SocketStateT +{ + SocketT *pSockList; //!< master socket list + SocketT *pSockKill; //!< list of killed sockets + HostentT *pHostList; //!< list of ongoing name resolution operations + + uint16_t aVirtualPorts[SOCKET_MAXVIRTUALPORTS]; //!< virtual port list + int32_t iMaxPacket; //!< maximum packet size + int32_t iFamily; //!< family to use for socket operations + + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + int32_t iVersion; //!< winsock version + int32_t iVerbose; //!< debug level + #if defined(DIRTYCODE_XBOXONE) + uint32_t uLocalAddr; //!< local ipv4 address + uint32_t uRandBindPort; + #else + uint32_t uAdapterAddress; //!< local interface used for SocketBind() operations if non-zero + #endif + + volatile int32_t iRecvLife; //!< receive thread alive indicator + WSAEVENT hEvent; //!< event used to wake up receive thread + NetCritT EventCrit; //!< event critical section (used when killing events) + + SocketAddrMapT AddrMap; //!< address map for translating ipv6 addresses to ipv4 virtual addresses and back + + SocketHostnameCacheT *pHostnameCache; //!< hostname cache + + SocketSendCallbackEntryT aSendCbEntries[SOCKET_MAXSENDCALLBACKS]; //!< collection of send callbacks +} SocketStateT; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +//! module state ref +static SocketStateT *_Socket_pState = NULL; + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function _XlatError0 + + \Description + Translate a winsock error to dirtysock + + \Input iErr - return value from winsock call + \Input iWsaErr - winsock error (from WSAGetLastError()) + + \Output + int32_t - dirtysock error + + \Version 09/09/2004 (jbrookes) +*/ +/************************************************************************************F*/ +static int32_t _XlatError0(int32_t iErr, int32_t iWsaErr) +{ + if (iErr < 0) + { + iErr = iWsaErr; + if ((iErr == WSAEWOULDBLOCK) || (iErr == WSA_IO_PENDING)) + iErr = SOCKERR_NONE; + else if ((iErr == WSAENETUNREACH) || (iErr == WSAEHOSTUNREACH)) + iErr = SOCKERR_UNREACH; + else if (iErr == WSAENOTCONN) + iErr = SOCKERR_NOTCONN; + else if (iErr == WSAECONNREFUSED) + iErr = SOCKERR_REFUSED; + else if (iErr == WSAEINVAL) + iErr = SOCKERR_INVALID; + else if (iErr == WSAECONNRESET) + iErr = SOCKERR_CONNRESET; + else + { + NetPrintf(("dirtynetwin: error %s\n", DirtyErrGetName(iErr))); + iErr = SOCKERR_OTHER; + } + } + return(iErr); +} + +/*F*************************************************************************************/ +/*! + \Function _XlatError + + \Description + Translate the most recent winsock error to dirtysock + + \Input iErr - return value from winsock call + + \Output + int32_t - dirtysock error + + \Version 01/02/2002 (gschaefer) +*/ +/************************************************************************************F*/ +static int32_t _XlatError(int32_t iErr) +{ + return(_XlatError0(iErr, WSAGetLastError())); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketOpen + + \Description + Allocates a SocketT. If uSocket is INVALID_SOCKET, a WinSock socket ref is + created, otherwise uSocket is used. + + \Input uSocket - socket to use, or INVALID_SOCKET + \Input iFamily - address family + \Input iType - type (SOCK_DGRAM, SOCK_STREAM, ...) + \Input iProto - protocol + + \Output + SocketT * - pointer to new socket, or NULL + + \Version 03/03/2005 (jbrookes) +*/ +/************************************************************************************F*/ +static SocketT *_SocketOpen(SOCKET uSocket, int32_t iFamily, int32_t iType, int32_t iProto) +{ + SocketStateT *pState = _Socket_pState; + const uint32_t uTrue = 1, uFalse = 0; + const int32_t iQueueSize = (iType != SOCK_STREAM) ? 1 : 8; + SocketT *pSocket; + + // allocate memory + if ((pSocket = (SocketT *)DirtyMemAlloc(sizeof(*pSocket), SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetwin: unable to allocate memory for socket\n")); + return(NULL); + } + ds_memclr(pSocket, sizeof(*pSocket)); + + // open a winsock socket + if ((uSocket == INVALID_SOCKET) && ((uSocket = WSASocketW(iFamily, iType, iProto, NULL, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)) + { + NetPrintf(("dirtynetwin: error %d creating socket\n", WSAGetLastError())); + DirtyMemFree(pSocket, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(NULL); + } + + // create packet queue + if ((pSocket->pRecvQueue = SocketPacketQueueCreate(iQueueSize, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetwin: failed to create socket queue for socket\n")); + closesocket(uSocket); + DirtyMemFree(pSocket, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(NULL); + } + pSocket->iPacketQueueResizePending = -1; + + // save the socket + pSocket->uSocket = uSocket; + pSocket->iLastError = SOCKERR_NONE; + pSocket->iVerbose = 1; + + // set to non blocking + ioctlsocket(uSocket, FIONBIO, (u_long *)&uTrue); + + // if udp, allow broadcast + if (iType == SOCK_DGRAM) + { + setsockopt(uSocket, SOL_SOCKET, SO_BROADCAST, (char *)&uTrue, sizeof(uTrue)); + } + // if raw, set hdrincl + if (iType == SOCK_RAW) + { + setsockopt(uSocket, IPPROTO_IP, IP_HDRINCL, (char *)&uTrue, sizeof(uTrue)); + } + // disable IPv6 only (allow IPv4 use) + if (iFamily == AF_INET6) + { + setsockopt(uSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&uFalse, sizeof(uFalse)); + } + + // set family/proto info + pSocket->iFamily = iFamily; + pSocket->iType = iType; + pSocket->iProto = iProto; + pSocket->bAsyncRecv = ((iType == SOCK_DGRAM) || (iType == SOCK_RAW)) ? TRUE : FALSE; + pSocket->bSendCbs = TRUE; + + // create overlapped i/o event object + ds_memclr(&pSocket->Overlapped, sizeof(pSocket->Overlapped)); + pSocket->Overlapped.hEvent = WSACreateEvent(); + + // initialize receive critical section + NetCritInit(&pSocket->RecvCrit, "recvthread"); + + // install into list + NetCritEnter(NULL); + pSocket->pNext = pState->pSockList; + pState->pSockList = pSocket; + NetCritLeave(NULL); + + // return the socket + return(pSocket); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketClose + + \Description + Disposes of a SocketT, including removal from the global socket list and + disposal of the SocketT allocated memory. Does NOT dispose of the winsock + socket ref. + + \Input *pSocket - socket to close + \Input bShutdown - if TRUE, shutdown and close socket ref + + \Output + int32_t - negative=failure, zero=success + + \Version 01/14/2005 (jbrookes) +*/ +/************************************************************************************F*/ +static int32_t _SocketClose(SocketT *pSocket, uint32_t bShutdown) +{ + SocketStateT *pState = _Socket_pState; + uint8_t bSockInList = FALSE; + SocketT **ppSocket; + + // for access to socket list + NetCritEnter(NULL); + + // remove sock from linked list + for (ppSocket = &pState->pSockList; *ppSocket != NULL; ppSocket = &(*ppSocket)->pNext) + { + if (*ppSocket == pSocket) + { + *ppSocket = pSocket->pNext; + bSockInList = TRUE; + break; + } + } + + // release before NetIdleDone + NetCritLeave(NULL); + + // make sure the socket is in the socket list (and therefore valid) + if (!bSockInList) + { + NetPrintf(("dirtynetwin: warning, trying to close socket 0x%08x that is not in the socket list\n", (uintptr_t)pSocket)); + return(-1); + } + + // finish any idle call + NetIdleDone(); + + // acquire global critical section + NetCritEnter(NULL); + + // wake up out of WaitForMultipleEvents() + WSASetEvent(pState->hEvent); + + // acquire event critical section + NetCritEnter(&pState->EventCrit); + + // close event + WSACloseEvent(pSocket->Overlapped.hEvent); + pSocket->Overlapped.hEvent = WSA_INVALID_EVENT; + + // release event critical section + NetCritLeave(&pState->EventCrit); + + // release global critical section + NetCritLeave(NULL); + + // destroy packet queue + if (pSocket->pRecvQueue != NULL) + { + SocketPacketQueueDestroy(pSocket->pRecvQueue); + } + + // mark as closed + if (bShutdown && (pSocket->uSocket != INVALID_SOCKET)) + { + // close winsock socket + shutdown(pSocket->uSocket, 2); + closesocket(pSocket->uSocket); + } + pSocket->uSocket = INVALID_SOCKET; + pSocket->iOpened = 0; + + /* Put into killed list: + Usage of a kill list allows for postponing two things + * destruction of RecvCrit + * release of socket data structure memory + This ensures that RecvCrit is not freed while in-use by a running thread. + Such a scenario can occur when the receive callback invoked by _SocketRecvThread() + (while RecvCrit is entered) calls _SocketClose() */ + NetCritEnter(NULL); + pSocket->pKill = pState->pSockKill; + pState->pSockKill = pSocket; + NetCritLeave(NULL); + + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketIdle + + \Description + Call idle processing code to give connections time. + + \Input *_pState - module state + + \Version 10/15/1999 (gschaefer) +*/ +/************************************************************************************F*/ +static void _SocketIdle(void *_pState) +{ + SocketStateT *pState = (SocketStateT *)_pState; + SocketT *pSocket; + uint32_t uTick = NetTick(); + + // for access to socket list and kill list + NetCritEnter(NULL); + + // walk socket list and perform any callbacks + for (pSocket = pState->pSockList; pSocket != NULL; pSocket = pSocket->pNext) + { + // see if we should do callback + if ((pSocket->uCallIdle != 0) && (pSocket->pCallback != NULL) && (!pSocket->bInCallback) && (NetTickDiff(uTick, pSocket->uCallLast) > pSocket->uCallIdle)) + { + pSocket->bInCallback = TRUE; + pSocket->pCallback(pSocket, 0, pSocket->pCallRef); + pSocket->bInCallback = FALSE; + pSocket->uCallLast = uTick = NetTick(); + } + } + + // delete any killed sockets + while ((pSocket = pState->pSockKill) != NULL) + { + pState->pSockKill = pSocket->pKill; + + // release the socket's receive critical section + NetCritKill(&pSocket->RecvCrit); + + // free the socket memory + DirtyMemFree(pSocket, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + + // process dns cache list, delete expired entries + SocketHostnameCacheProcess(pState->pHostnameCache, pState->iVerbose); + + // process hostname list, delete completed lookup requests + SocketHostnameListProcess(&pState->pHostList, pState->iMemGroup, pState->pMemGroupUserData); + + // release access to socket list and kill list + NetCritLeave(NULL); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketRecvfromPacketQueue + + \Description + Check if there is a pending inbound packet in the socket packet queue. + + \Input *pSocket - pointer to socket + \Input *pBuf - [out] buffer to receive data + \Input iLen - length of recv buffer + \Input *pFrom - [out] address data was received from (NULL=ignore) + \Input *pFromLen - [out] length of address + + \Output + int32_t - size of packet extracted from queue, 0 if no packet + + \Version 04/20/2016 (mclouatre) +*/ +/************************************************************************************F*/ +static int32_t _SocketRecvfromPacketQueue(SocketT *pSocket, const char *pBuf, int32_t iLen, struct sockaddr *pFrom, int32_t *pFromLen) +{ + int32_t iResult = 0; + + // make sure destination buffer is valid + if ((iLen > 0) && (pBuf != NULL)) + { + // get a packet + if (pSocket->iType != SOCK_STREAM) + { + iResult = SocketPacketQueueRem(pSocket->pRecvQueue, (uint8_t *)pBuf, iLen, &pSocket->RecvAddr); + } + else + { + iResult = SocketPacketQueueRemStream(pSocket->pRecvQueue, (uint8_t *)pBuf, iLen); + } + + if (iResult > 0) + { + if (pFrom != NULL) + { + ds_memcpy_s(pFrom, sizeof(*pFrom), &pSocket->RecvAddr, sizeof(pSocket->RecvAddr)); + *pFromLen = sizeof(*pFrom); + } + } + } + + return(iResult); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketRecvfrom + + \Description + Check if there is a pending inbound packet in the receive buffer of the + system socket + + \Input *pSocket - pointer to socket + \Input *pBuf - [out] buffer to receive data + \Input iLen - length of recv buffer + \Input *pFrom - [out] address data was received from (NULL=ignore) + \Input *pFromLen - [out] length of address + \Input *pRecvErr - [out] pointer to variable to be filled with recv err code + + \Output + int32_t - positive=data bytes received, else standard error code + + \Version 04/20/2016 (mclouatre) +*/ +/************************************************************************************F*/ +static int32_t _SocketRecvfrom(SocketT *pSocket, const char *pBuf, int32_t iLen, struct sockaddr *pFrom, int32_t *pFromLen, int32_t *pRecvErr) +{ + int32_t iResult = 0; + + // make sure socket ref is valid + if (pSocket->uSocket == INVALID_SOCKET) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + if (pFrom != NULL) + { + if (pSocket->iFamily == AF_INET) + { + iResult = recvfrom(pSocket->uSocket, (char *)pBuf, iLen, 0, pFrom, pFromLen); + } + if (pSocket->iFamily == AF_INET6) + { + struct sockaddr_in6 SockAddr6; + int32_t iFromLen6 = sizeof(SockAddr6); + SockAddr6.sin6_family = AF_INET; + if ((iResult = recvfrom(pSocket->uSocket, (char *)pBuf, iLen, 0, (struct sockaddr *)&SockAddr6, &iFromLen6)) > 0) + { + SocketAddrMapTranslate(&_Socket_pState->AddrMap, pFrom, (struct sockaddr *)&SockAddr6, pFromLen); + } + } + SockaddrInSetMisc(pFrom, NetTick()); + } + else + { + iResult = recv(pSocket->uSocket, (char *)pBuf, iLen, 0); + pFrom = &pSocket->RemoteAddr; + } + + // get most recent socket error + if ((*pRecvErr = WSAGetLastError()) == WSAEMSGSIZE) + { + /* if there was a message truncation, simply return the truncated size. this matches what we + do with the async recv thread version and also the linux behavior of recvfrom() */ + iResult = iLen; + } + + return(iResult); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketProcessQueueResize + + \Description + Resize socket packet queue if needed. + + \Input *pSocket - pointer to socket + + \Notes + This function is meant to be called from the _SocketRecvThread() only. + Do not use this function in code paths exercised by other threads. + + \Version 04/20/2016 (mclouatre) +*/ +/************************************************************************************F*/ +static void _SocketProcessQueueResize(SocketT *pSocket) +{ + SocketStateT *pState = _Socket_pState; + if ((pSocket->iPacketQueueResizePending != -1)) + { + pSocket->pRecvQueue = SocketPacketQueueResize(pSocket->pRecvQueue, pSocket->iPacketQueueResizePending, pState->iMemGroup, pState->pMemGroupUserData); + pSocket->iPacketQueueResizePending = -1; + } +} + +/*F*************************************************************************************/ +/*! + \Function _SocketRecvfromAsyncComplete + + \Description + Called when data is received by _SocketRecvThread(). If there is a callback + registered, then the socket is passed to that callback so the data may be + consumed. + + \Input *pSocket - pointer to socket that has new data + \Input iBytesReceived - number of bytes received + + \Notes + This function is meant to be called from the _SocketRecvThread() only. + Do not use this function in code paths exercised by other threads. + + \Version 08/30/2004 (jbrookes) +*/ +/************************************************************************************F*/ +static void _SocketRecvfromAsyncComplete(SocketT *pSocket, int32_t iBytesReceived) +{ + // translate IPv6->IPv4, save receive timestamp + if (pSocket->iType != SOCK_STREAM) + { + if (pSocket->iFamily == AF_INET6) + { + int32_t iNameLen = sizeof(pSocket->RecvAddr); + SockaddrInit(&pSocket->RecvAddr, AF_INET); + SocketAddrMapTranslate(&_Socket_pState->AddrMap, &pSocket->RecvAddr, (struct sockaddr *)&pSocket->RecvAddr6, &iNameLen); + } + SockaddrInSetMisc(&pSocket->RecvAddr, NetTick()); + } + + // complete setup of packet queue entry (already has data) by updating the size and reception time + pSocket->pRecvPacket->iPacketSize = iBytesReceived; + pSocket->pRecvPacket->uPacketTick = NetTick(); + ds_memcpy_s(&pSocket->pRecvPacket->PacketAddr, sizeof(pSocket->pRecvPacket->PacketAddr), &pSocket->RecvAddr, sizeof(pSocket->RecvAddr)); + + // we are done with populating that specific packet queue entry, kill reference into packet queue + pSocket->pRecvPacket = NULL; + + /* This is a safe place to deal with pending packet queue resize because the recvcrit is guaranteed to be locked + and there is no async recv operation in progress on the queue (which implicitly means that pSocket->pRecvPacket is NOT + pointing to a buffer in the queue. + + It is important to execute this after pSocket->pRecvPacket (which points to an entry in the queue) is fully initialized + and befor the recv callback is invoked (because we want to resize to happen before SocketRecvfrom being potentially + invoked from the callback. */ + _SocketProcessQueueResize(pSocket); + + // see if we should issue callback + if ((!pSocket->bInCallback) && (pSocket->pCallback != NULL) && (pSocket->uCallMask & CALLB_RECV)) + { + pSocket->bInCallback = TRUE; + (pSocket->pCallback)(pSocket, 0, pSocket->pCallRef); + pSocket->bInCallback = FALSE; + pSocket->uCallLast = NetTick(); + } +} + +/*F*************************************************************************************/ +/*! + \Function _SocketRecvfromAsync + + \Description + Issue an overlapped recv call on the given socket. + + \Input *pSocket - pointer to socket to read from + + \Notes + This function is meant to be called from the _SocketRecvThread() only. + Do not use this function in code paths exercised by other threads. + + \Version 08/30/2004 (jbrookes) +*/ +/************************************************************************************F*/ +static void _SocketRecvfromAsync(SocketT *pSocket) +{ + int32_t iResult = 0; + int32_t iRecvErr; + WSABUF RecvBuf; + + /* Mark the operation as in progress. + For scenarios where the recv call returned 0 (meaning immediate completion), we enforce an execution + path similar to recv call not completing immediately. We asssume two things: + * _SocketRecvThread() will not wait on the next WSAWaitForMultipleEvents() because the event for this socket is signaled. + * The following call to WSAGetOverlappedResult() will then detect completion of the recv operation. */ + pSocket->bRecvInp = TRUE; + + // get a packet queue entry to receive into + pSocket->pRecvPacket = SocketPacketQueueAlloc(pSocket->pRecvQueue); + + // set up for recv call + RecvBuf.buf = (CHAR *)pSocket->pRecvPacket->aPacketData; + RecvBuf.len = sizeof(pSocket->pRecvPacket->aPacketData); + pSocket->uRecvFlag = 0; + + // try and receive some data + if (pSocket->iType == SOCK_DGRAM) + { + if (pSocket->iFamily == AF_INET) + { + pSocket->iAddrLen = sizeof(pSocket->RecvAddr); + iResult = WSARecvFrom(pSocket->uSocket, &RecvBuf, 1, NULL, (LPDWORD)&pSocket->uRecvFlag, (struct sockaddr *)&pSocket->RecvAddr, (LPINT)&pSocket->iAddrLen, &pSocket->Overlapped, NULL); + } + if (pSocket->iFamily == AF_INET6) + { + pSocket->iAddrLen = sizeof(pSocket->RecvAddr6); + iResult = WSARecvFrom(pSocket->uSocket, &RecvBuf, 1, NULL, (LPDWORD)&pSocket->uRecvFlag, (struct sockaddr *)&pSocket->RecvAddr6, (LPINT)&pSocket->iAddrLen, &pSocket->Overlapped, NULL); + } + } + else // pSocket->iType == SOCK_RAW + { + iResult = WSARecv(pSocket->uSocket, &RecvBuf, 1, NULL, (LPDWORD)&pSocket->uRecvFlag, &pSocket->Overlapped, NULL); + } + + // error? + if ((iResult == SOCKET_ERROR) && ((iRecvErr = WSAGetLastError()) != WSA_IO_PENDING)) + { + if (pSocket->iType != SOCK_STREAM) + { + NetPrintf(("dirtynetwin: [%p] error %s when trying to initiate async receive on socket\n", pSocket, DirtyErrGetName(iRecvErr))); + } + else + { + // in the testing done, winsock2 returns WSAECONNABORTED when the FIN comes in for the socket. in reviewing the error codes there was no other more suitable check to make + NetPrintf(("dirtynetwin: [%p] connection %s\n", pSocket, (iRecvErr == WSAECONNABORTED) ? "closed" : "failed")); + pSocket->iOpened = -1; + } + + // clean up resources that were reserved when the async recv was initiated + SocketPacketQueueAllocUndo(pSocket->pRecvQueue); + + // mark that receive operation is no longer in progress + pSocket->bRecvInp = FALSE; + } +} + +/*F*************************************************************************************/ +/*! + \Function _SocketRecvThread + + \Description + Wait for incoming data and deliver it immediately to the registered socket callback, + if any. + + \Input pUnused - unused + + \Version 08/30/2004 (jbrookes) +*/ +/************************************************************************************F*/ +static void _SocketRecvThread(void *pUnused) +{ + SocketStateT *pState = _Socket_pState; + WSAEVENT aEventList[SOCKET_MAXEVENTS]; + int32_t iNumEvents, iResult; + SocketT *pSocket; + char strThreadId[32]; + + // get the thread id + DirtyThreadGetThreadId(strThreadId, sizeof(strThreadId)); + + // show we are alive + NetPrintfVerbose((pState->iVerbose, 0, "dirtynetwin: receive thread started (thid=%s)\n", strThreadId)); + + // clear event list + ds_memclr(aEventList, sizeof(aEventList)); + + // loop until done + while (pState->iRecvLife == 1) + { + // acquire global critical section for access to g_socklist + NetCritEnter(NULL); + + // add global event to list + iNumEvents = 0; + aEventList[iNumEvents++] = pState->hEvent; + + /* Walk the socket list and for each eligible socket do: + 1- If an async recv has recently completed, then finalize the recv operation and invoke the recv callback if registered. + Then mark the async recv operatoin has no longer in progress. + 2- If an async recv is not in progress, initiate one if n-deep packet queue is not full. Then mark the async recv operation + as in progress. + 3- If an async revc is in progress (old or newly initiated), then add the socket's event to the event list that the + thread later blocks on when calling WSAWaitForMultipleEvents(). */ + for (pSocket = pState->pSockList; (pSocket != NULL) && (iNumEvents < SOCKET_MAXEVENTS); pSocket = pSocket->pNext) + { + // only handle non-virtual sockets with asyncrecv true + if ((!pSocket->bVirtual) && (pSocket->uSocket != INVALID_SOCKET) && pSocket->bAsyncRecv) + { + // acquire socket critical section + NetCritEnter(&pSocket->RecvCrit); + + // is an asynchronous recv in progress on this socket? + if (pSocket->bRecvInp) + { + int32_t iRecvResult; + + // check for overlapped read completion + if (WSAGetOverlappedResult(pSocket->uSocket, &pSocket->Overlapped, (LPDWORD)&iRecvResult, FALSE, (LPDWORD)&pSocket->uRecvFlag) == TRUE) + { + // mark that receive operation is no longer in progress + pSocket->bRecvInp = FALSE; + + _SocketRecvfromAsyncComplete(pSocket, iRecvResult); + } + else if ((iResult = WSAGetLastError()) != WSA_IO_INCOMPLETE) + { + // clean up resources that were reserved when the async recv was initiated + SocketPacketQueueAllocUndo(pSocket->pRecvQueue); + + #if DIRTYCODE_LOGGING + if (iResult != WSAECONNRESET) + { + NetPrintf(("dirtynetwin: [%p] WSAGetOverlappedResult error %d\n", pSocket, iResult)); + } + #endif + + // mark that receive operation is no longer in progress + pSocket->bRecvInp = FALSE; + + /* This is a safe place to deal with pending packet queue resize because the recvcrit is guaranteed to be locked + and there is no async read operation in progress on the queue (which implicitly means that pSocket->pRecvPacket is NOT + pointing to a buffer in the queue. */ + _SocketProcessQueueResize(pSocket); + } + } + + /* Before proceeding, make sure that the user callback invoked in _SocketRecvThreadFinishAsyncRead() did not close the socket + with SocketClose(). We know that SocketClose() function resets the value of pSocket->socket to INVALID_SOCKET. Also, we + know that it does not destroy pSocket but it queues it in the global kill list. Since this list cannot be processed before + the code below, as it also runs in the context of the global critical section, the following code is thread-safe. */ + if ((pSocket->uSocket != INVALID_SOCKET) && ((pSocket->iType != SOCK_STREAM) || (pSocket->iOpened > 0))) + { + // should we initiate a new asynchronous recv on this socket? + if (!pSocket->bRecvInp && !SocketPacketQueueStatus(pSocket->pRecvQueue, 'pful')) + { + _SocketRecvfromAsync(pSocket); + } + + // if an asynchronous recv (old or newly initiated) is in progress, then add the associated event to event list + if (pSocket->bRecvInp) + { + aEventList[iNumEvents++] = pSocket->Overlapped.hEvent; + } + } + + // release socket critical section + NetCritLeave(&pSocket->RecvCrit); + } + } + + // protect against events being deleted + NetCritEnter(&pState->EventCrit); + + // release global critical section + NetCritLeave(NULL); + + // wait for an event to trigger + iResult = WSAWaitForMultipleEvents(iNumEvents, aEventList, FALSE, WSA_INFINITE, FALSE) - WSA_WAIT_EVENT_0; + + // reset the signaled event + if ((iResult >= 0) && (iResult < iNumEvents)) + { + WSAResetEvent(aEventList[iResult]); + } + + // leave event protected section + NetCritLeave(&pState->EventCrit); + } + + // indicate we are done + NetPrintfVerbose((pState->iVerbose, 0, "dirtynetwin: receive thread exit\n")); + pState->iRecvLife = 0; +} + +/*F*************************************************************************************/ +/*! + \Function _SocketPoll + + \Description + Perform a blocking poll in nanoseconds + + \Input *pState - pointer to module state + \Input uPollUsec - time to perform the poll (select) for + \Input *ppSocketList - socket list or NULL + + \Output + int32_t - result of the select call + + \Version 04/17/2019 (eesponda) +*/ +/************************************************************************************F*/ +static int32_t _SocketPoll(SocketStateT *pState, uint32_t uPollUsec, SocketT **ppSocketList) +{ + fd_set FdRead, FdExcept; + struct timeval TimeVal; + SocketT *pTempSocket, **ppSocket; + int32_t iSocket, iResult; + int32_t iMaxSocket = 0; + + FD_ZERO(&FdRead); + FD_ZERO(&FdExcept); + TimeVal.tv_sec = uPollUsec/1000000; + TimeVal.tv_usec = uPollUsec%1000000; + + // if socket list specified, use it + if (ppSocketList != NULL) + { + // add sockets to select list (FD_SETSIZE is 64) + for (ppSocket = ppSocketList, iSocket = 0; (*ppSocket != NULL) && (iSocket < FD_SETSIZE); ppSocket++, iSocket++) + { + FD_SET((*ppSocket)->uSocket, &FdRead); + FD_SET((*ppSocket)->uSocket, &FdExcept); + iMaxSocket = DS_MAX((int32_t)((*ppSocket)->uSocket), iMaxSocket); + } + } + else + { + // get exclusive access to socket list + NetCritEnter(NULL); + // walk socket list and add all sockets + for (pTempSocket = pState->pSockList, iSocket = 0; (pTempSocket != NULL) && (iSocket < FD_SETSIZE); pTempSocket = pTempSocket->pNext, iSocket++) + { + if (pTempSocket->uSocket != INVALID_SOCKET) + { + FD_SET(pTempSocket->uSocket, &FdRead); + FD_SET(pTempSocket->uSocket, &FdExcept); + iMaxSocket = DS_MAX((int32_t)(pTempSocket->uSocket), iMaxSocket); + } + } + // for access to g_socklist and g_sockkill + NetCritLeave(NULL); + } + + // wait for input on the socket list for up to iData1 milliseconds + iResult = select(iMaxSocket + 1, &FdRead, NULL, &FdExcept, &TimeVal); + + // if any sockets have pending data, figure out which ones + if (iResult > 0) + { + // re-acquire critical section + NetCritEnter(NULL); + + // update sockets if there is data to be read or not + for (pTempSocket = pState->pSockList; pTempSocket != NULL; pTempSocket = pTempSocket->pNext) + { + if ((pTempSocket->uSocket != INVALID_SOCKET) && + (FD_ISSET(pTempSocket->uSocket, &FdRead)) && + (pTempSocket->pCallback != NULL) && + (pTempSocket->uCallMask & CALLB_RECV)) + { + pTempSocket->pCallback(pTempSocket, 0, pTempSocket->pCallRef); + pTempSocket->uCallLast = NetTick(); + } + } + + // release the critical section + NetCritLeave(NULL); + } + + // return number of file descriptors with pending data + return(iResult); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketInfoGlobal + + \Description + Return information about global state + + \Input iInfo - selector for desired information + \Input iData - selector specific + \Input *pBuf - [out] return buffer + \Input iLen - buffer length + + \Output + int32_t - size of returned data or error code (negative value) + + \Notes + These selectors need to be documented in SocketInfo() to allow our + documentation generation to pick them up. + + \Version 03/31/2017 (eesponda) +*/ +/************************************************************************************F*/ +static int32_t _SocketInfoGlobal(int32_t iInfo, int32_t iData, void *pBuf, int32_t iLen) +{ + SocketStateT *pState = _Socket_pState; + + if (iInfo == 'addr') + { + #if defined(DIRTYCODE_XBOXONE) + // force new acquisition of address? + if (iData == 1) + { + pState->uLocalAddr = 0; + } + #endif + return(SocketGetLocalAddr()); + } + // get socket bound to given port + if ( (iInfo == 'bind') || (iInfo == 'bndu') ) + { + SocketT *pSocket; + struct sockaddr BindAddr; + int32_t iFound = -1; + + // for access to socket list + NetCritEnter(NULL); + + // walk socket list and find matching socket + for (pSocket = pState->pSockList; pSocket != NULL; pSocket = pSocket->pNext) + { + // if iInfo is 'bndu', only consider sockets of type SOCK_DGRAM + // note: 'bndu' stands for "bind udp" + if ( (iInfo == 'bind') || ((iInfo == 'bndu') && (pSocket->iType == SOCK_DGRAM)) ) + { + // get socket info + SocketInfo(pSocket, 'bind', 0, &BindAddr, sizeof(BindAddr)); + if (SockaddrInGetPort(&BindAddr) == iData) + { + *(SocketT **)pBuf = pSocket; + iFound = 0; + break; + } + } + } + + // for access to g_socklist and g_sockkill + NetCritLeave(NULL); + return(iFound); + } + // check if specified ipv4 address is virtual and return associated ipv6 address if so + if (iInfo == '?ip6') + { + int32_t iResult = -1; + struct sockaddr_in6 SockAddr6, *pSockAddr6; + struct sockaddr SockAddr; + int32_t iNameLen; + + SockaddrInit(&SockAddr, AF_INET); + SockaddrInSetAddr(&SockAddr, iData); + + SockaddrInit6(&SockAddr6, AF_INET6); + pSockAddr6 = ((pBuf != NULL) && (iLen == sizeof(SockAddr6))) ? (struct sockaddr_in6 *)pBuf : &SockAddr6; + iNameLen = sizeof(SockAddr6); + + iResult = SocketAddrMapGet(&pState->AddrMap, (struct sockaddr *)pSockAddr6, &SockAddr, &iNameLen) ? 1 : 0; + return(iResult); + } + + #if !defined(DIRTYCODE_XBOXONE) + // get local address previously specified by user for subsequent SocketBind() operations + if (iInfo == 'ladr') + { + // If 'ladr' had not been set previously, address field of output sockaddr buffer + // will just be filled with 0. + SockaddrInSetAddr((struct sockaddr *)pBuf, pState->uAdapterAddress); + return(0); + } + #endif + // return max packet size + if (iInfo == 'maxp') + { + return(pState->iMaxPacket); + } + // get send callback function pointer (iData specifies index in array) + if (iInfo == 'sdcf') + { + if ((pBuf != NULL) && (iLen == sizeof(pState->aSendCbEntries[iData].pSendCallback))) + { + ds_memcpy(pBuf, &pState->aSendCbEntries[iData].pSendCallback, sizeof(pState->aSendCbEntries[iData].pSendCallback)); + return(0); + } + + NetPrintf(("dirtynetwin: 'sdcf' selector used with invalid paramaters\n")); + return(-1); + } + // get send callback user data pointer (iData specifies index in array) + if (iInfo == 'sdcu') + { + if ((pBuf != NULL) && (iLen == sizeof(pState->aSendCbEntries[iData].pSendCallref))) + { + ds_memcpy(pBuf, &pState->aSendCbEntries[iData].pSendCallref, sizeof(pState->aSendCbEntries[iData].pSendCallref)); + return(0); + } + + NetPrintf(("dirtynetwin: 'sdcu' selector used with invalid paramaters\n")); + return(-1); + } + // return global debug output level + if (iInfo == 'spam') + { + return(pState->iVerbose); + } + // unhandled + NetPrintf(("dirtynetwin: unhandled global SocketInfo() selector '%C'\n", iInfo)); + return(-1); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketControlGlobal + + \Description + Process a global control message (type specific operation) + + \Input iOption - the option to pass + \Input iData1 - message specific parm + \Input *pData2 - message specific parm + \Input *pData3 - message specific parm + + \Output + int32_t - message specific result (-1=unsupported message, -2=no such module) + + \Notes + These selectors need to be documented in SocketControl() to allow our + documentation generation to pick them up. + + \Version 03/31/2017 (eesponda) +*/ +/************************************************************************************F*/ +static int32_t _SocketControlGlobal(int32_t iOption, int32_t iData1, void *pData2, void *pData3) +{ + SocketStateT *pState = _Socket_pState; + + // set address family to use for socket operations + if (iOption == 'afam') + { + if ((iData1 != AF_INET) && (iData1 != AF_INET6)) + { + return(-1); + } + pState->iFamily = iData1; + NetPrintf(("dirtynetwin: address family used for socket operations set to %s\n", pState->iFamily == AF_INET ? "AF_INET" : "AF_INET6")); + return(0); + } + // handle connect message + if (iOption == 'conn') + { + return(0); + } + // handle disconnect message + if (iOption == 'disc') + { + return(0); + } + // set an ipv6 address into the mapping table + if (iOption == '+ip6') + { + return(SocketAddrMapAddress(&pState->AddrMap, (const struct sockaddr *)pData2, iData1)); + } + // del an ipv6 address from the mapping table + if (iOption == '-ip6') + { + return(SocketAddrUnmapAddress(&pState->AddrMap, (const struct sockaddr *)pData2, iData1)); + } + // remap an existing ipv6 address in the mapping table + if (iOption == '~ip6') + { + return(SocketAddrRemapAddress(&pState->AddrMap, (const struct sockaddr *)pData2, (const struct sockaddr *)pData3, iData1)); + } + #if !defined(DIRTYCODE_XBOXONE) + // set local address? (used to select between multiple network interfaces) + if (iOption == 'ladr') + { + pState->uAdapterAddress = (unsigned)iData1; + return(0); + } + #endif + // set max udp packet size + if (iOption == 'maxp') + { + NetPrintf(("dirtynetwin: setting max udp packet size to %d\n", iData1)); + pState->iMaxPacket = iData1; + return(0); + } + // block waiting on input from socket list in milliseconds + if (iOption == 'poll') + { + return(_SocketPoll(pState, (unsigned)iData1*1000, pData2)); + } + if (iOption == 'poln') + { + return(_SocketPoll(pState, (unsigned)iData1, pData2)); + } + // set/unset send callback (iData1=TRUE for set - FALSE for unset, pData2=callback, pData3=callref) + if (iOption == 'sdcb') + { + SocketSendCallbackEntryT sendCbEntry; + sendCbEntry.pSendCallback = (SocketSendCallbackT *)pData2; + sendCbEntry.pSendCallref = pData3; + + if (iData1) + { + return(SocketSendCallbackAdd(&pState->aSendCbEntries[0], &sendCbEntry)); + } + else + { + return(SocketSendCallbackRem(&pState->aSendCbEntries[0], &sendCbEntry)); + } + } + // set debug level + if (iOption == 'spam') + { + // set module level debug level + pState->iVerbose = iData1; + return(0); + } + // mark a port as virtual + if (iOption == 'vadd') + { + int32_t iPort; + + // find a slot to add virtual port + for (iPort = 0; pState->aVirtualPorts[iPort] != 0; iPort++) + ; + if (iPort < SOCKET_MAXVIRTUALPORTS) + { + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetwin: added port %d to virtual port list\n", iData1)); + pState->aVirtualPorts[iPort] = (uint16_t)iData1; + return(0); + } + } + // remove port from virtual port list + if (iOption == 'vdel') + { + int32_t iPort; + + // find virtual port in list + for (iPort = 0; (iPort < SOCKET_MAXVIRTUALPORTS) && (pState->aVirtualPorts[iPort] != (uint16_t)iData1); iPort++) + ; + if (iPort < SOCKET_MAXVIRTUALPORTS) + { + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetwin: removed port %d from virtual port list\n", iData1)); + pState->aVirtualPorts[iPort] = 0; + return(0); + } + } + // unhandled + NetPrintf(("dirtynetwin: unhandled global SocketControl() option '%C'\n", iOption)); + return(-1); +} + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function SocketCreate + + \Description + Create new instance of socket interface module. Initializes all global + resources and makes module ready for use. + + \Input iThreadPrio - priority to start threads with + \Input iThreadStackSize - stack size to start threads with (in bytes) + \Input iThreadCpuAffinity - cpu affinity to start threads with + + \Output + int32_t - negative=error, zero=success + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketCreate(int32_t iThreadPrio, int32_t iThreadStackSize, int32_t iThreadCpuAffinity) +{ + SocketStateT *pState = _Socket_pState; + WSADATA WSAData; + int32_t iResult; + int32_t iMemGroup; + void *pMemGroupUserData; + DirtyThreadConfigT ThreadConfig; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // error if already started + if (pState != NULL) + { + NetPrintfVerbose((pState->iVerbose, 0, "dirtynetwin: SocketCreate() called while module is already active\n")); + return(-1); + } + + // print version info + NetPrintf(("dirtynetwin: DirtySDK v%d.%d.%d.%d.%d\n", DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH)); + + // alloc and init state ref + if ((pState = (SocketStateT *)DirtyMemAlloc(sizeof(*pState), SOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetwin: unable to allocate module state\n")); + return(-2); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + pState->iMaxPacket = SOCKET_MAXUDPRECV; + pState->iFamily = AF_INET6; + pState->iVerbose = 1; + + // save global module ref + _Socket_pState = pState; + + // startup network libs + NetLibCreate(iThreadPrio, iThreadStackSize, iThreadCpuAffinity); + + // start winsock + ds_memclr(&WSAData, sizeof(WSAData)); + iResult = WSAStartup(MAKEWORD(2,2), &WSAData); + if (iResult != 0) + { + NetPrintf(("dirtynetwin: error %d loading winsock library\n", iResult)); + SocketDestroy((uint32_t)(-1)); + return(-3); + } + + // save the available version + pState->iVersion = (LOBYTE(WSAData.wVersion)<<8)|(HIBYTE(WSAData.wVersion)<<0); + + // create hostname cache + if ((pState->pHostnameCache = SocketHostnameCacheCreate(iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetwin: unable to create hostname cache\n")); + SocketDestroy((uint32_t)(-1)); + return(-4); + } + + // add our idle handler + NetIdleAdd(&_SocketIdle, pState); + + // create a global event to notify recv thread of newly available sockets + pState->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (pState->hEvent == NULL) + { + NetPrintf(("dirtynetwin: error %d creating state event\n", GetLastError())); + SocketDestroy(0); + return(-5); + } + + // initialize event critical section + NetCritInit(&pState->EventCrit, "recv-event-crit"); + + /* if recvthread has been created but has no chance to run (upon failure or shutdown + immediately), there will be a problem. by setting iRecvLife to non-zero before + thread creation we ensure SocketDestroy work properly. */ + pState->iRecvLife = 1; // see _SocketRecvThread for more details + + // configure thread parameters + ds_memclr(&ThreadConfig, sizeof(ThreadConfig)); + ThreadConfig.pName = "SocketRecv"; + ThreadConfig.iPriority = iThreadPrio; + ThreadConfig.iAffinity = iThreadCpuAffinity; + ThreadConfig.iVerbosity = pState->iVerbose; + + // start up socket receive thread + if ((iResult = DirtyThreadCreate(_SocketRecvThread, NULL, &ThreadConfig)) != 0) + { + pState->iRecvLife = 0; // no recvthread was created, reset to 0 + NetPrintf(("dirtynetwin: error %d creating socket receive thread\n", iResult)); + SocketDestroy(0); + return(-6); + } + + // init socket address map + SocketAddrMapInit(&pState->AddrMap, pState->iMemGroup, pState->pMemGroupUserData); + + // return success + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function SocketDestroy + + \Description + Release resources and destroy module. + + \Input uShutdownFlags - shutdown flags + + \Output + int32_t - negative=error, zero=success + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketDestroy(uint32_t uShutdownFlags) +{ + SocketStateT *pState = _Socket_pState; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtynetwin: SocketDestroy() called while module is not active\n")); + return(-1); + } + + NetPrintf(("dirtynetwin: shutting down\n")); + + // $$TODO- why don't we do this on XONE too? + #if !defined(DIRTYCODE_XBOXONE) + // wait until all lookup threads are done + while (pState->pHostList != NULL) + { + volatile HostentT **ppHost; + int32_t iSocketLookups; + + // check for lookup threads that are still active + for (ppHost = &pState->pHostList, iSocketLookups = 0; *ppHost != NULL; ppHost = (volatile HostentT **)&(*ppHost)->pNext) + { + iSocketLookups += (*ppHost)->thread ? 0 : 1; + } + // if no ongoing socket lookups, we're done + if (iSocketLookups == 0) + { + break; + } + Sleep(1); + } + #endif + + // kill idle callbacks + NetIdleDel(&_SocketIdle, pState); + + // let any idle event finish + NetIdleDone(); + + // tell receive thread to quit and wake it up (if running) + if (pState->iRecvLife == 1) + { + pState->iRecvLife = 2; + if (pState->hEvent != NULL) + { + WSASetEvent(pState->hEvent); + } + + // wait for thread to terminate + while (pState->iRecvLife > 0) + { + Sleep(1); + } + } + + // cleanup addr map, if allocated + SocketAddrMapShutdown(&pState->AddrMap); + + // close all sockets + NetCritEnter(NULL); + while (pState->pSockList != NULL) + { + SocketClose(pState->pSockList); + } + NetCritLeave(NULL); + + // clear the kill list + _SocketIdle(pState); + + // destroy hostname cache + if (pState->pHostnameCache != NULL) + { + SocketHostnameCacheDestroy(pState->pHostnameCache); + } + + // we rely on the Handle having been created to know if the critical section was created. + if (pState->hEvent != NULL) + { + // destroy event critical section + NetCritKill(&pState->EventCrit); + // delete global event + CloseHandle(pState->hEvent); + } + + // free the memory and clear global module ref + _Socket_pState = NULL; + DirtyMemFree(pState, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + // shut down network libs + NetLibDestroy(0); + + // shutdown winsock, unless told otherwise + if (uShutdownFlags == 0) + { + WSACleanup(); + } + + NetPrintf(("dirtynetwin: shutdown complete\n")); + + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function SocketOpen + + \Description + Create a new transfer endpoint. A socket endpoint is required for any + data transfer operation. + + \Input iFamily - address family (AF_INET) [IGNORED] + \Input iType - socket type (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, ...) + \Input iProto - protocol type for SOCK_RAW (unused by others) + + \Output + SocketT - socket reference + + \Notes + The address family specified here is ignored; instead it is dictated based on + the internal configuration setting which defaults to AF_INET6, but can be + overridden using the 'fam' SocketControl() selector. + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +SocketT *SocketOpen(int32_t iFamily, int32_t iType, int32_t iProto) +{ + return(_SocketOpen(INVALID_SOCKET, _Socket_pState->iFamily, iType, iProto)); +} + +/*F*************************************************************************************/ +/*! + \Function SocketClose + + \Description + Close a socket. Performs a graceful shutdown of connection oriented protocols. + + \Input *pSocket - socket reference + + \Output + int32_t - zero + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketClose(SocketT *pSocket) +{ + return(_SocketClose(pSocket, TRUE)); +} + +/*F*************************************************************************************/ +/*! + \Function SocketImport + + \Description + Import a socket. The given socket ref may be a SocketT, in which case a + SocketT pointer to the ref is returned, or it can be an actual Sony socket ref, + in which case a SocketT is created for the Sony socket ref. + + \Input uSockRef - socket reference + + \Output + SocketT * - pointer to imported socket, or NULL + + \Version 01/14/2005 (jbrookes) +*/ +/************************************************************************************F*/ +SocketT *SocketImport(intptr_t uSockRef) +{ + SocketStateT *pState = _Socket_pState; + int32_t iProto, iProtoSize; + SocketT *pSocket; + + // see if this socket is already in our socket list + NetCritEnter(NULL); + for (pSocket = pState->pSockList; pSocket != NULL; pSocket = pSocket->pNext) + { + if (pSocket == (SocketT *)uSockRef) + { + break; + } + } + NetCritLeave(NULL); + + // if socket is in socket list, just return it + if (pSocket != NULL) + { + return(pSocket); + } + + //$$ TODO - this assumes AF_INET + + // get info from socket ref + iProtoSize = sizeof(iProto); + if (getsockopt((SOCKET)uSockRef, 0, SO_TYPE, (char *)&iProto, &iProtoSize) != SOCKET_ERROR) + { + // create the socket (note: winsock socket types directly map to dirtysock socket types) + pSocket = _SocketOpen(uSockRef, pSocket->iFamily, iProto, 0); + + // update local and remote addresses + SocketInfo(pSocket, 'bind', 0, &pSocket->LocalAddr, sizeof(pSocket->LocalAddr)); + SocketInfo(pSocket, 'peer', 0, &pSocket->RemoteAddr, sizeof(pSocket->RemoteAddr)); + + // mark it as imported + pSocket->uImported = 1; + } + + return(pSocket); +} + +/*F*************************************************************************************/ +/*! + \Function SocketRelease + + \Description + Release an imported socket. + + \Input *pSocket - pointer to socket + + \Version 01/14/2005 (jbrookes) +*/ +/************************************************************************************F*/ +void SocketRelease(SocketT *pSocket) +{ + // if it wasn't imported, nothing to do + if (!pSocket->uImported) + { + return; + } + + // dispose of SocketT, but leave the sockref alone + _SocketClose(pSocket, FALSE); +} + +/*F*************************************************************************************/ +/*! + \Function SocketShutdown + + \Description + Perform partial/complete shutdown of socket indicating that either sending + and/or receiving is complete. + + \Input *pSocket - socket reference + \Input iHow - SOCK_NOSEND and/or SOCK_NORECV + + \Output + int32_t - zero + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketShutdown(SocketT *pSocket, int32_t iHow) +{ + // no shutdown for an invalid socket + if (pSocket->uSocket == INVALID_SOCKET) + { + return(0); + } + + // translate how + if (iHow == SOCK_NOSEND) + { + iHow = SD_SEND; + } + else if (iHow == SOCK_NORECV) + { + iHow = SD_RECEIVE; + } + else if (iHow == (SOCK_NOSEND|SOCK_NORECV)) + { + iHow = SD_BOTH; + } + + pSocket->uShutdown |= iHow; + shutdown(pSocket->uSocket, iHow); + + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function SocketBind + + \Description + Bind a local address/port to a socket. + + \Input *pSocket - socket reference + \Input *pName - local address/port + \Input iNameLen - length of name + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Notes + If either address or port is zero, then they are filled in automatically. + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketBind(SocketT *pSocket, const struct sockaddr *pName, int32_t iNameLen) +{ + SocketStateT *pState = _Socket_pState; + struct sockaddr_in6 SockAddr6; + int32_t iResult; + + #if !defined(DIRTYCODE_XBOXONE) + struct sockaddr BindAddr; + + // bind to specific address? + if ((SockaddrInGetAddr(pName) == 0) && (pState->uAdapterAddress != 0)) + { + ds_memcpy_s(&BindAddr, sizeof(BindAddr), pName, sizeof(*pName)); + SockaddrInSetAddr(&BindAddr, pState->uAdapterAddress); + pName = &BindAddr; + } + #endif + + // is the bind port a virtual port? + if (pSocket->iType == SOCK_DGRAM) + { + int32_t iPort; + uint16_t uPort; + + if ((uPort = SockaddrInGetPort(pName)) != 0) + { + // find virtual port in list + for (iPort = 0; (iPort < SOCKET_MAXVIRTUALPORTS) && (pState->aVirtualPorts[iPort] != uPort); iPort++) + ; + if (iPort < SOCKET_MAXVIRTUALPORTS) + { + // acquire socket critical section + NetCritEnter(&pSocket->RecvCrit); + + // check to see if the socket is bound + if (pSocket->bVirtual && (pSocket->uVirtualPort != 0)) + { + NetPrintf(("dirtynetwin: [%p] failed to bind socket to %u which was already bound to port %u virtual\n", pSocket, uPort, pSocket->uVirtualPort)); + NetCritLeave(&pSocket->RecvCrit); + return(pSocket->iLastError = SOCKERR_INVALID); + } + + // close winsock socket + NetPrintf(("dirtynetwin: [%p] making socket bound to port %d virtual\n", pSocket, uPort)); + if (pSocket->uSocket != INVALID_SOCKET) + { + shutdown(pSocket->uSocket, SOCK_NOSEND); + closesocket(pSocket->uSocket); + pSocket->uSocket = INVALID_SOCKET; + } + /* increase socket queue size; this protects virtual sockets from having data pushed into + them and overwriting previous data that hasn't been read yet */ + pSocket->pRecvQueue = SocketPacketQueueResize(pSocket->pRecvQueue, 4, pState->iMemGroup, pState->pMemGroupUserData); + // mark socket as virtual + pSocket->uVirtualPort = uPort; + pSocket->bVirtual = TRUE; + + // release socket critical section + NetCritLeave(&pSocket->RecvCrit); + + return(0); + } + } + } + + #if defined(DIRTYCODE_XBOXONE) + // set up IPv6 address (do not use an IPv4-mapped IPv6 here as it will disallow IPv6 address use) + ds_memclr(&SockAddr6, sizeof(SockAddr6)); + SockAddr6.sin6_family = AF_INET6; + SockAddr6.sin6_port = SocketNtohs(SockaddrInGetPort(pName)); + pName = (const struct sockaddr *)&SockAddr6; + iNameLen = sizeof(SockAddr6); + #else + // translate IPv4 -> IPv6 address (if needed) + if ((pSocket->iFamily == AF_INET6) && (pName->sa_family != AF_INET6)) + { + ds_memclr(&SockAddr6, sizeof(SockAddr6)); + SockAddr6.sin6_family = AF_INET6; + SockAddr6.sin6_port = SocketNtohs(SockaddrInGetPort(pName)); + pName = SocketAddrMapTranslate(&_Socket_pState->AddrMap, (struct sockaddr *)&SockAddr6, pName, &iNameLen); + } + #endif + + // execute the bind + iResult = _XlatError(bind(pSocket->uSocket, pName, iNameLen)); + + // notify read thread that socket is ready to be read from + if ((iResult == SOCKERR_NONE) && pSocket->bAsyncRecv) + { + WSASetEvent(pState->hEvent); + } + + // return result to caller + pSocket->iLastError = iResult; + return(pSocket->iLastError); +} + +/*F*************************************************************************************/ +/*! + \Function SocketConnect + + \Description + Initiate a connection attempt to a remote host. + + \Input *pSocket - socket reference + \Input *pName - pointer to name of socket to connect to + \Input iNameLen - length of name + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Notes + Only has real meaning for stream protocols. For a datagram protocol, this + just sets the default remote host. + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketConnect(SocketT *pSocket, struct sockaddr *pName, int32_t iNameLen) +{ + struct sockaddr_in6 SockAddr6; + struct sockaddr SockAddr, *pSockAddr = NULL; + int32_t iResult, iSockAddrLen = 0; + + // mark as not open + pSocket->iOpened = 0; + + // save connect address + ds_memcpy_s(&pSocket->RemoteAddr, sizeof(pSocket->RemoteAddr), pName, sizeof(*pName)); + + // init sockaddr + if (pSocket->iFamily == AF_INET) + { + SockaddrInit(&SockAddr, AF_INET); + pSockAddr = &SockAddr; + iSockAddrLen = sizeof(SockAddr); + } + else if (pSocket->iFamily == AF_INET6) + { + // initialize family of SockAddr6 + SockaddrInit6(&SockAddr6, AF_INET6); + pSockAddr = (struct sockaddr *)&SockAddr6; + iSockAddrLen = sizeof(SockAddr6); + } + + #if !defined(DIRTYCODE_XBOXONE) + /* execute an explicit bind - this allows us to specify a non-zero local address + or port (see SocketBind()). a SOCKERR_INVALID result here means the socket has + already been bound, so we ignore that particular error */ + if (((iResult = SocketBind(pSocket, pSockAddr, iSockAddrLen)) < 0) && (iResult != SOCKERR_INVALID)) + { + pSocket->iLastError = iResult; + return(pSocket->iLastError); + } + #endif + + // translate to IPv6 if required + if (pSocket->iFamily == AF_INET6) + { + pName = SocketAddrMapTranslate(&_Socket_pState->AddrMap, pSockAddr, pName, &iNameLen); + } + + // execute the connect + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetwin: connecting to %A\n", pName)); + iResult = _XlatError(connect(pSocket->uSocket, pName, iNameLen)); + + // notify read thread that socket is ready to be read from + if ((iResult == SOCKERR_NONE) && pSocket->bAsyncRecv) + { + WSASetEvent(_Socket_pState->hEvent); + } + + // return result to caller + pSocket->iLastError = iResult; + return(pSocket->iLastError); +} + +/*F*************************************************************************************/ +/*! + \Function SocketListen + + \Description + Start listening for an incoming connection on the socket. The socket must already + be bound and a stream oriented connection must be in use. + + \Input *pSocket - socket reference to bound socket (see SocketBind()) + \Input iBackLog - number of pending connections allowed + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketListen(SocketT *pSocket, int32_t iBackLog) +{ + // do the listen + pSocket->iLastError = _XlatError(listen(pSocket->uSocket, iBackLog)); + return(pSocket->iLastError); +} + +/*F*************************************************************************************/ +/*! + \Function SocketAccept + + \Description + Accept an incoming connection attempt on a socket. + + \Input *pSocket - socket reference to socket in listening state (see SocketListen()) + \Input *pAddr - pointer to storage for address of the connecting entity, or NULL + \Input *pAddrLen - pointer to storage for length of address, or NULL + + \Output + SocketT * - the accepted socket, or NULL if not available + + \Notes + The integer pointed to by addrlen should on input contain the number of characters + in the buffer addr. On exit it will contain the number of characters in the + output address. + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +SocketT *SocketAccept(SocketT *pSocket, struct sockaddr *pAddr, int32_t *pAddrLen) +{ + SocketT *pOpen = NULL; + SOCKET iIncoming; + + pSocket->iLastError = SOCKERR_INVALID; + + // see if already connected + if (pSocket->uSocket == INVALID_SOCKET) + { + return(NULL); + } + + // make sure turn parm is valid + if ((pAddr != NULL) && (*pAddrLen < sizeof(struct sockaddr))) + { + return(NULL); + } + + #if !defined(DIRTYCODE_XBOXONE) + // perform inet accept + if (pSocket->iFamily == AF_INET) + { + iIncoming = accept(pSocket->uSocket, pAddr, pAddrLen); + if (iIncoming != INVALID_SOCKET) + { + pOpen = _SocketOpen(iIncoming, pSocket->iFamily, pSocket->iType, pSocket->iProto); + pSocket->iLastError = SOCKERR_NONE; + } + else + { + // use a negative error code to force _XlatError to internally call WSAGetLastError() and translate the obtained result + pSocket->iLastError = _XlatError(-99); + + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + NetPrintf(("dirtynetwin: accept() failed err=%d\n", WSAGetLastError())); + } + } + } + #endif + + // perform inet6 accept + if (pSocket->iFamily == AF_INET6) + { + struct sockaddr_in6 SockAddr6; + int32_t iAddrLen; + + SockaddrInit6(&SockAddr6, AF_INET6); + iAddrLen = sizeof(SockAddr6); + iIncoming = accept(pSocket->uSocket, (struct sockaddr *)&SockAddr6, &iAddrLen); + if (iIncoming != INVALID_SOCKET) + { + pOpen = _SocketOpen(iIncoming, pSocket->iFamily, pSocket->iType, pSocket->iProto); + pSocket->iLastError = SOCKERR_NONE; + // translate ipv6 to ipv4 virtual address + SocketAddrMapAddress(&_Socket_pState->AddrMap, (struct sockaddr *)&SockAddr6, sizeof(SockAddr6)); + + // save translated connecting info for caller + SockaddrInit(pAddr, AF_INET); + SocketAddrMapTranslate(&_Socket_pState->AddrMap, pAddr, (struct sockaddr *) &SockAddr6, pAddrLen); + } + else + { + // use a negative error code to force _XlatError to internally call WSAGetLastError() and translate the obtained result + pSocket->iLastError = _XlatError(-99); + + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + NetPrintf(("dirtynetwin: accept() failed err=%d\n", WSAGetLastError())); + } + } + } + + // return the socket + return(pOpen); +} + +/*F*************************************************************************************/ +/*! + \Function SocketSendto + + \Description + Send data to a remote host. The destination address is supplied along with + the data. Should only be used with datagram sockets as stream sockets always + send to the connected peer. + + \Input *pSocket - socket reference + \Input *pBuf - the data to be sent + \Input iLen - size of data + \Input iFlags - unused + \Input *pTo - the address to send to (NULL=use connection address) + \Input iToLen - length of address + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketSendto(SocketT *pSocket, const char *pBuf, int32_t iLen, int32_t iFlags, const struct sockaddr *pTo, int32_t iToLen) +{ + SocketStateT *pState = _Socket_pState; + int32_t iResult; + + if (pSocket->bSendCbs) + { + // if installed, give socket callback right of first refusal + if ((iResult = SocketSendCallbackInvoke(&pState->aSendCbEntries[0], pSocket, pSocket->iType, pBuf, iLen, pTo)) > 0) + { + return(iResult); + } + } + + // make sure socket ref is valid + if (pSocket->uSocket == INVALID_SOCKET) + { + #if DIRTYCODE_LOGGING + uint32_t uAddr = 0, uPort = 0; + if (pTo) + { + uAddr = SockaddrInGetAddr(pTo); + uPort = SockaddrInGetPort(pTo); + } + NetPrintf(("dirtynetwin: attempting to send to %a:%d on invalid socket\n", uAddr, uPort)); + #endif + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + // handle optional data rate throttling + if ((iLen = SocketRateThrottle(&pSocket->SendRate, pSocket->iType, iLen, "send")) == 0) + { + return(0); + } + + // use appropriate version + if (pTo == NULL) + { + iResult = send(pSocket->uSocket, pBuf, iLen, 0); + pTo = &pSocket->RemoteAddr; + } + else + { + struct sockaddr_in6 SockAddr6; + if (pSocket->iFamily == AF_INET6) + { + SockaddrInit6(&SockAddr6, AF_INET6); + iToLen = sizeof(SockAddr6); + SocketAddrMapTranslate(&pState->AddrMap, (struct sockaddr *)&SockAddr6, pTo, &iToLen); + pTo = (struct sockaddr *)&SockAddr6; + } + iResult = sendto(pSocket->uSocket, pBuf, iLen, 0, pTo, iToLen); + } + + // update data rate estimation + SocketRateUpdate(&pSocket->SendRate, iResult, "send"); + + // return bytes sent + pSocket->iLastError = _XlatError(iResult); + return(pSocket->iLastError); +} + +/*F*************************************************************************************/ +/*! + \Function SocketRecvfrom + + \Description + Receive data from a remote host. If socket is a connected stream, then data can + only come from that source. A datagram socket can receive from any remote host. + + \Input *pSocket - socket reference + \Input *pBuf - buffer to receive data + \Input iLen - length of recv buffer + \Input iFlags - unused + \Input *pFrom - address data was received from (NULL=ignore) + \Input *pFromLen - length of address + + \Output + int32_t - positive=data bytes received, else standard error code + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketRecvfrom(SocketT *pSocket, char *pBuf, int32_t iLen, int32_t iFlags, struct sockaddr *pFrom, int32_t *pFromLen) +{ + int32_t iRecv = 0, iRecvErr = 0; + + // handle rate throttling, if enabled + if ((iLen = SocketRateThrottle(&pSocket->RecvRate, pSocket->iType, iLen, "recv")) == 0) + { + return(0); + } + + // handle if the socket was killed + if (pSocket->pKill != NULL) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + // sockets marked for async recv had actual receive operation take place in the thread + if (pSocket->bAsyncRecv) + { + uint32_t bSocketPacketQueueFull; + + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + + // remember if packet queue was full prior to calling _SocketRecvfromPacketQueue() + bSocketPacketQueueFull = SocketPacketQueueStatus(pSocket->pRecvQueue, 'pful'); + + /* given the socket could be either a TCP or UDP socket we handle the no data condition the same. + this is due to the below, when we do error conversion we override the system error with EWOULDBLOCK because + with TCP zero would be mean closed. if we are doing direct recv calls then the translation will + convert based on the errno returned after the call */ + if ((iRecv = _SocketRecvfromPacketQueue(pSocket, pBuf, iLen, pFrom, pFromLen)) == 0) + { + iRecv = -1; + iRecvErr = WSAEWOULDBLOCK; + } + + // when data is obtained from the packet queue, we lose visibility on system socket errors + pSocket->iLastError = SOCKERR_NONE; + + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + + /* If packet queue had reached "full" state, then _SocketRecvThread had not been + adding that socket to the event list anymore. Consequently, if _SocketRecvThread + is currently blocked on WSAWaitForMultipleEvents(), it may not wake up if there is + inbound traffic on this socket. To work around this, we wake it up explicitly here. */ + if (bSocketPacketQueueFull) + { + WSASetEvent(_Socket_pState->hEvent); + } + } + else + { + iRecv = _SocketRecvfrom(pSocket, pBuf, iLen, pFrom, pFromLen, &iRecvErr); + } + + // do error conversion + iRecv = (iRecv == 0) ? SOCKERR_CLOSED : _XlatError0(iRecv, iRecvErr); + + // update data rate estimation + SocketRateUpdate(&pSocket->RecvRate, pSocket->iLastError, "recv"); + + // return the error code + pSocket->iLastError = iRecv; + return(pSocket->iLastError); +} + +/*F*************************************************************************************/ +/*! + \Function SocketInfo + + \Description + Return information about an existing socket. + + \Input *pSocket - socket reference + \Input iInfo - selector for desired information + \Input iData - selector specific + \Input *pBuf - [out] return buffer + \Input iLen - buffer length + + \Output + int32_t - size of returned data or error code (negative value) + + \Notes + iInfo can be one of the following: + + \verbatim + 'addr' - return local address + 'conn' - who we are connecting to + 'bind' - return bind data (if pSocket == NULL, get socket bound to given port) + 'bndu' - return bind data (only with pSocket=NULL, get SOCK_DGRAM socket bound to given port) + '?ip6' - return TRUE if ipv4 address specified in iData is virtual, and fill in pBuf with ipv6 address if not NULL + 'ladr' - get local address previously specified by user for subsequent SocketBind() operations (pc only) + 'maxp' - return configured max packet size + 'maxr' - return configured max recv rate (bytes/sec; zero=uncapped) + 'maxs' - return configured max send rate (bytes/sec; zero=uncapped) + 'pdrp' - return socket packet queue number of packets dropped + 'peer' - peer info (only valid if connected) + 'pmax' - return socket packet queue max depth + 'pnum' - return socket packet queue current depth + 'ratr' - return current recv rate estimation (bytes/sec) + 'rats' - return current send rate estimation (bytes/sec) + 'sdcf' - get installed send callback function pointer (iData specifies index in array) + 'sdcu' - get installed send callback userdata pointer (iData specifies index in array) + 'serr' - last socket error + 'psiz' - return socket packet queue max size + 'sock' - return windows socket associated with the specified DirtySock socket + 'spam' - return debug level for debug output + 'stat' - TRUE if connected, else FALSE + 'virt' - TRUE if socket is virtual, else FALSE + \endverbatim + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketInfo(SocketT *pSocket, int32_t iInfo, int32_t iData, void *pBuf, int32_t iLen) +{ + SocketStateT *pState = _Socket_pState; + + // always zero results by default + #if defined(DIRTYCODE_XBOXONE) + if (pBuf != NULL) + #else + if ((pBuf != NULL) && (iInfo != 'ladr')) + #endif + { + ds_memclr(pBuf, iLen); + } + + // handle global socket options + if (pSocket == NULL) + { + return(_SocketInfoGlobal(iInfo, iData, pBuf, iLen)); + } + + // return local bind data + if (iInfo == 'bind') + { + int32_t iResult = -1; + if (pSocket->bVirtual) + { + SockaddrInit((struct sockaddr *)pBuf, AF_INET); + SockaddrInSetPort((struct sockaddr *)pBuf, pSocket->uVirtualPort); + iResult = 0; + } + else if (pSocket->uSocket != INVALID_SOCKET) + { + struct sockaddr_in6 SockAddr6; + iLen = sizeof(SockAddr6); + if ((iResult = getsockname(pSocket->uSocket, (struct sockaddr *)&SockAddr6, &iLen)) == 0) + { + SockaddrInit((struct sockaddr *)pBuf, AF_INET); + SockaddrInSetPort((struct sockaddr *)pBuf, SocketHtons(SockAddr6.sin6_port)); + SockaddrInSetAddr((struct sockaddr *)pBuf, SocketAddrMapAddress(&pState->AddrMap, (struct sockaddr *)&SockAddr6, sizeof(SockAddr6))); + } + iResult = _XlatError(iResult); + } + return(iResult); + } + + // return configured max recv rate + if (iInfo == 'maxr') + { + return(pSocket->RecvRate.uMaxRate); + } + + // return configured max send rate + if (iInfo == 'maxs') + { + return(pSocket->SendRate.uMaxRate); + } + + // return socket protocol + if (iInfo == 'prot') + { + return(pSocket->iProto); + } + + // return whether the socket is virtual or not + if (iInfo == 'virt') + { + return(pSocket->bVirtual); + } + + /* + make sure the socket is alive + ** AFTER THIS POINT WE ENSURE THE SOCKET DESCRIPTOR AND PACKET QUEUE ARE VALID ** + */ + if (pSocket->pKill != NULL) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + // return peer info (only valid if connected) + if ((iInfo == 'conn') || (iInfo == 'peer')) + { + getpeername(pSocket->uSocket, (struct sockaddr *)pBuf, &iLen); + //$$TODO this needs to be IPv6, translate to virtual address for output + return(0); + } + // get packet queue info + if ((iInfo == 'pdrp') || (iInfo == 'pmax') || (iInfo == 'psiz')) + { + int32_t iResult; + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + // get packet queue status + iResult = SocketPacketQueueStatus(pSocket->pRecvQueue, iInfo); + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + return(iResult); + } + + // return current recv rate estimation + if (iInfo == 'ratr') + { + return(pSocket->RecvRate.uCurRate); + } + + // return current send rate estimation + if (iInfo == 'rats') + { + return(pSocket->SendRate.uCurRate); + } + + // return last socket error + if (iInfo == 'serr') + { + return(pSocket->iLastError); + } + + // return windows socket identifier + if (iInfo == 'sock') + { + if (pBuf != NULL) + { + if (iLen == (int32_t)sizeof(pSocket->uSocket)) + { + ds_memcpy(pBuf, &pSocket->uSocket, sizeof(pSocket->uSocket)); + } + } + return((int32_t)pSocket->uSocket); + } + + // return socket status + if (iInfo == 'stat') + { + fd_set FdRead, FdWrite, FdExcept; + struct timeval TimeVal; + + // if not a connected socket, return TRUE + if (pSocket->iType != SOCK_STREAM) + { + return(1); + } + + // if not connected, use select to determine connect + if (pSocket->iOpened == 0) + { + // setup write/exception lists so we can select against the socket + FD_ZERO(&FdWrite); + FD_ZERO(&FdExcept); + FD_SET(pSocket->uSocket, &FdWrite); + FD_SET(pSocket->uSocket, &FdExcept); + TimeVal.tv_sec = TimeVal.tv_usec = 0; + if (select(pSocket->uSocket + 1, NULL, &FdWrite, &FdExcept, &TimeVal) > 0) + { + // if we got an exception, that means connect failed + if (FdExcept.fd_count > 0) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetwin: connection failed\n")); + pSocket->iOpened = -1; + } + // if socket is writable, that means connect succeeded + else if (FdWrite.fd_count > 0) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetwin: connection open\n")); + pSocket->iOpened = 1; + } + } + } + + /* if previously connected, make sure connect still valid. we only do this when not doing async receive for two + reasons + 1. there is a race condition between the poll waking up and querying the bytes available. if the bytes are + read on the receive thread between the poll and ioctl then it would think the socket is closed because the + socket has already been drained + 2. our reasoning behind using the async receive thread could be the cost of recv, plus other calls may be + expensive as well. the async receive thread will already set the correct state on the socket thus we can + skip the query and return iOpened back to the user */ + if (!pSocket->bAsyncRecv && (pSocket->iOpened > 0)) + { + FD_ZERO(&FdRead); + FD_ZERO(&FdExcept); + FD_SET(pSocket->uSocket, &FdRead); + FD_SET(pSocket->uSocket, &FdExcept); + TimeVal.tv_sec = TimeVal.tv_usec = 0; + if (select(pSocket->uSocket + 1, &FdRead, NULL, &FdExcept, &TimeVal) > 0) + { + // if we got an exception, that means connect failed (usually closed by remote peer) + if (FdExcept.fd_count > 0) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetwin: connection failure\n")); + pSocket->iOpened = -1; + } + else if (FdRead.fd_count > 0) + { + u_long uAvailBytes = 1; // u_long is required by ioctlsocket + // if socket is readable but there's no data available, connect was closed + // uAvailBytes might be less than actual bytes, so it can only be used for zero-test + if ((ioctlsocket(pSocket->uSocket, FIONREAD, &uAvailBytes) != 0) || (uAvailBytes == 0)) + { + pSocket->iLastError = SOCKERR_CLOSED; + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetwin: connection closed (wsaerr=%d)\n", (uAvailBytes==0) ? WSAECONNRESET : WSAGetLastError())); + pSocket->iOpened = -1; + } + } + } + } + /* if we still have packets in the queue, tell the caller that we are still open. this makes sure they read the + complete stream of data */ + else if (pSocket->bAsyncRecv && (pSocket->iOpened < 0) && (SocketInfo(pSocket, 'pnum', 0, NULL, 0) != 0)) + { + return(1); + } + + // return connect status + return(pSocket->iOpened); + } + + return(-1); +} + +/*F*************************************************************************************/ +/*! + \Function SocketCallback + + \Description + Register a callback routine for notification of socket events. Also includes + timeout support. + + \Input *pSocket - socket reference + \Input iMask - valid callback events (CALLB_NONE, CALLB_SEND, CALLB_RECV) + \Input iIdle - if nonzero, specifies the number of ticks between idle calls + \Input *pRef - user data to be passed to proc + \Input *pProc - user callback + + \Output + int32_t - zero + + \Notes + A callback will reset the idle timer, so when specifying a callback and an + idle processing time, the idle processing time represents the maximum elapsed + time between calls. + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +#pragma optimize("", off) // make sure this block of code is not reordered +int32_t SocketCallback(SocketT *pSocket, int32_t iMask, int32_t iIdle, void *pRef, int32_t (*pProc)(SocketT *pSocket, int32_t iFlags, void *pRef)) +{ + pSocket->uCallIdle = iIdle; + pSocket->uCallMask = iMask; + pSocket->pCallRef = pRef; + pSocket->pCallback = pProc; + return(0); +} +#pragma optimize("", on) + +/*F*************************************************************************************/ +/*! + \Function SocketControl + + \Description + Process a control message (type specific operation) + + \Input *pSocket - socket to control, or NULL for module-level option + \Input iOption - the option to pass + \Input iData1 - message specific parm + \Input *pData2 - message specific parm + \Input *pData3 - message specific parm + + \Output + int32_t - message specific result (-1=unsupported message, -2=no such module) + + \Notes + iOption can be one of the following: + + \verbatim + 'afam' - set internet address family to use for socket operations (defaults to AF_INET6, AF_INET is also supported)) + 'arcv' - set async receive enable/disable (default enabled for DGRAM/RAW, disabled for TCP) + 'conn' - handle connect message + 'disc' - handle disconnect message + '+ip6' - add an IPv6 address into the mapping table and return a virtual IPv4 address to reference it + '-ip6' - del an IPv6 address from the mapping table + '~ip6' - remap an existing IPv6 address in the mapping table + 'keep' - set TCP keep-alive settings on PC (iData1=enable/disable, iData2=keep-alive time, iData3=keep-alive interval) + 'ladr' - set local address for subsequent SocketBind() operations (pc only) + 'maxp' - set max udp packet size + 'maxr' - set max recv rate (bytes/sec; zero=uncapped) + 'maxs' - set max send rate (bytes/sec; zero=uncapped) + 'nbio' - set nonblocking/blocking mode (TCP only, iData1=TRUE (nonblocking) or FALSE (blocking)) + 'ndly' - set TCP_NODELAY state for given stream socket (iData1=zero or one) + 'pdev' - set simulated packet deviation + 'plat' - set simulated packet latency + 'plos' - set simulated packet loss + 'poll' - execute blocking wait on given socket list (pData2) or all sockets (pData2=NULL), 64 max sockets in milliseconds + 'poln' - execute blocking wait on given socket list (pData2) or all sockets (pData2=NULL), 64 max sockets in microseconds + 'pque' - set socket packet queue depth + 'push' - push data into given socket (iData1=size, pData2=data ptr, pData3=sockaddr ptr) + 'rbuf' - set socket recv buffer size + 'sbuf' - set socket send buffer size + 'scbk' - enable/disable "send callbacks usage" on specified socket (defaults to enable) + 'sdcb' - set/unset send callback (iData1=TRUE for set - FALSE for unset, pData2=callback, pData3=callref) + 'soli' - set SO_LINGER On the specified socket, iData1 is timeout in seconds + 'spam' - set debug level for debug output + 'vadd' - add a port to virtual port list + 'vdel' - del a port from virtual port list + \endverbatim + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketControl(SocketT *pSocket, int32_t iOption, int32_t iData1, void *pData2, void *pData3) +{ + SocketStateT *pState = _Socket_pState; + int32_t iResult; + + // handle global control + if (pSocket == NULL) + { + return(_SocketControlGlobal(iOption, iData1, pData2, pData3)); + } + + // set async recv enable + if (iOption == 'arcv') + { + // set socket async recv flag + pSocket->bAsyncRecv = iData1 ? TRUE : FALSE; + // wake up recvthread to update socket polling + WSASetEvent(pState->hEvent); + return(0); + } + // set max recv rate + if (iOption == 'maxr') + { + NetPrintf(("dirtynetwin: setting max recv rate to %d bytes/sec\n", iData1)); + pSocket->RecvRate.uMaxRate = iData1; + return(0); + } + // set max send rate + if (iOption == 'maxs') + { + NetPrintf(("dirtynetwin: setting max send rate to %d bytes/sec\n", iData1)); + pSocket->SendRate.uMaxRate = iData1; + return(0); + } + // enable/disable "send callbacks usage" on specified socket (defaults to enable) + if (iOption == 'scbk') + { + if (pSocket->bSendCbs != (iData1?TRUE:FALSE)) + { + NetPrintf(("dirtynetwin: send callbacks usage changed from %s to %s for socket ref %p\n", (pSocket->bSendCbs?"ON":"OFF"), (iData1?"ON":"OFF"), pSocket)); + pSocket->bSendCbs = (iData1?TRUE:FALSE); + } + return(0); + } + // set debug level + if (iOption == 'spam') + { + // per-socket debug level + pSocket->iVerbose = iData1; + return(0); + } + + /* + make sure the socket is alive + ** AFTER THIS POINT WE ENSURE THE SOCKET DESCRIPTOR AND PACKET QUEUE ARE VALID ** + */ + if (pSocket->pKill != NULL) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + #if !defined(DIRTYCODE_XBOXONE) + // configure TCP keep-alive + if (iOption == 'keep') + { + struct tcp_keepalive TcpKeepAlive; + DWORD dwBytesReturned; + + if (pSocket->iType != SOCK_STREAM) + { + NetPrintf(("dirtynetwin: [%p] 'keep' selector can only be used on a SOCK_STREAM socket\n", pSocket)); + return(-1); + } + if (pSocket->uSocket == INVALID_SOCKET) + { + NetPrintf(("dirtynetwin: [%p] 'keep' selector used with an invalid socket\n", pSocket)); + return(-1); + } + + // initialize tcpkeep alive structure + ds_memclr(&TcpKeepAlive, sizeof(TcpKeepAlive)); + + TcpKeepAlive.onoff = (uint8_t)iData1; // on/off + if (TcpKeepAlive.onoff) + { + TcpKeepAlive.keepalivetime = *(uint32_t*)pData2; // timeout in ms + TcpKeepAlive.keepaliveinterval = *(uint32_t*)pData3; // interval in ms + } + else + { + TcpKeepAlive.keepalivetime = 0; // timeout in ms + TcpKeepAlive.keepaliveinterval = 0; // interval in ms + } + + if ((iResult = WSAIoctl(pSocket->uSocket, SIO_KEEPALIVE_VALS, (void *)&TcpKeepAlive, sizeof(TcpKeepAlive), NULL, 0, &dwBytesReturned, NULL, NULL)) == 0) + { + pSocket->iLastError = SOCKERR_NONE; + NetPrintfVerbose((pSocket->iVerbose, 1, "dirtynetwin: [%p] successfully %s the TCP keep-alive (timeout=%dms, interval=%dms)\n", pSocket, + iData1?"enabled":"disabled", TcpKeepAlive.keepalivetime, TcpKeepAlive.keepaliveinterval)); + } + else + { + pSocket->iLastError = _XlatError(iResult); + NetPrintf(("dirtynetwin: [%p] failed to %s TCP keep-alive for low-level socket (err = %d)\n", pSocket, + iData1?"enable":"disable", WSAGetLastError())); + } + + return(pSocket->iLastError); + } + #endif + // if a stream socket, set nonblocking/blocking mode + if ((iOption == 'nbio') && (pSocket->iType == SOCK_STREAM)) + { + uint32_t uNbio = (uint32_t)iData1; + iResult = ioctlsocket(pSocket->uSocket, FIONBIO, (u_long *)&uNbio); + pSocket->iLastError = _XlatError(iResult); + NetPrintf(("dirtynetwin: setting socket 0x%p to %s mode %s (LastError=%d).\n", pSocket, iData1 ? "nonblocking" : "blocking", iResult ? "failed" : "succeeded", pSocket->iLastError)); + return(pSocket->iLastError); + } + // if a stream socket, set TCP_NODELAY state + if ((iOption == 'ndly') && (pSocket->iType == SOCK_STREAM)) + { + iResult = setsockopt(pSocket->uSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&iData1, sizeof(iData1)); + pSocket->iLastError = _XlatError(iResult); + return(pSocket->iLastError); + } + // set simulated packet loss or packet latency + if ((iOption == 'pdev') || (iOption == 'plat') || (iOption == 'plos')) + { + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + // forward selector to packet queue + iResult = SocketPacketQueueControl(pSocket->pRecvQueue, iOption, iData1); + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + return(iResult); + } + + // change packet queue size + if (iOption == 'pque') + { + if (pSocket->bAsyncRecv) + { + pSocket->iPacketQueueResizePending = iData1; + } + else + { + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + // resize the queue + pSocket->pRecvQueue = SocketPacketQueueResize(pSocket->pRecvQueue, iData1, pState->iMemGroup, pState->pMemGroupUserData); + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + } + // return success + return(0); + } + + // push data into receive buffer + if (iOption == 'push') + { + // acquire socket critical section + NetCritEnter(&pSocket->RecvCrit); + + // don't allow data that is too large (for the buffer) to be pushed + if (iData1 > SOCKET_MAXUDPRECV) + { + NetPrintf(("dirtynetwin: request to push %d bytes of data discarded (max=%d)\n", iData1, SOCKET_MAXUDPRECV)); + NetCritLeave(&pSocket->RecvCrit); + return(-1); + } + + // add packet to queue + SocketPacketQueueAdd(pSocket->pRecvQueue, (uint8_t *)pData2, iData1, (struct sockaddr *)pData3); + + // release socket critical section + NetCritLeave(&pSocket->RecvCrit); + + // see if we should issue callback + if ((pSocket->pCallback != NULL) && (pSocket->uCallMask & CALLB_RECV)) + { + pSocket->pCallback(pSocket, 0, pSocket->pCallRef); + } + return(0); + } + // set REUSEADDR + if (iOption == 'radr') + { + iResult = setsockopt(pSocket->uSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&iData1, sizeof(iData1)); + pSocket->iLastError = _XlatError(iResult); + return(pSocket->iLastError); + } + // set socket receive buffer size + if ((iOption == 'rbuf') || (iOption == 'sbuf')) + { + int32_t iOldSize, iNewSize, iOptLen=4; + int32_t iSockOpt = (iOption == 'rbuf') ? SO_RCVBUF : SO_SNDBUF; + + // get current buffer size + getsockopt(pSocket->uSocket, SOL_SOCKET, iSockOpt, (char *)&iOldSize, &iOptLen); + + // set new size + iResult = setsockopt(pSocket->uSocket, SOL_SOCKET, iSockOpt, (const char *)&iData1, sizeof(iData1)); + pSocket->iLastError = _XlatError(iResult); + + // get new size + getsockopt(pSocket->uSocket, SOL_SOCKET, iSockOpt, (char *)&iNewSize, &iOptLen); + NetPrintf(("dirtynetwin: setsockopt(%s) changed buffer size from %d to %d\n", (iOption == 'rbuf') ? "SO_RCVBUF" : "SO_SNDBUF", + iOldSize, iNewSize)); + + return(pSocket->iLastError); + } + // set SO_LINGER + if (iOption == 'soli') + { + struct linger lingerOptions; + lingerOptions.l_onoff = TRUE; + lingerOptions.l_linger = iData1; + iResult = setsockopt(pSocket->uSocket, SOL_SOCKET, SO_LINGER, (const char *)&lingerOptions, sizeof(lingerOptions)); + pSocket->iLastError = _XlatError(iResult); + return(pSocket->iLastError); + } + // unhandled + return(-1); +} + +/*F*************************************************************************************/ +/*! + \Function SocketGetLocalAddr + + \Description + Returns the "external" local address (ie, the address as a machine "out on + the Internet" would see as the local machine's address). + + \Output + uint32_t - local address + + \Version 07/28/2003 (jbrookes) +*/ +/************************************************************************************F*/ +uint32_t SocketGetLocalAddr(void) +{ + #if defined(DIRTYCODE_XBOXONE) + SocketStateT *pState = _Socket_pState; + struct sockaddr InetAddr, HostAddr; + + if ((pState->uLocalAddr == 0) || (pState->uLocalAddr == 0x7f000001)) + { + // create a remote internet address + ds_memclr(&InetAddr, sizeof(InetAddr)); + InetAddr.sa_family = AF_INET; + SockaddrInSetPort(&InetAddr, 79); + SockaddrInSetAddr(&InetAddr, 0x9f990000); + + // ask socket to give us local address that can connect to it + ds_memclr(&HostAddr, sizeof(HostAddr)); + SocketHost(&HostAddr, sizeof(HostAddr), &InetAddr, sizeof(InetAddr)); + + pState->uLocalAddr = SockaddrInGetAddr(&HostAddr); + } + return(pState->uLocalAddr); + #else + struct sockaddr InetAddr, HostAddr; + + // create a remote internet address + ds_memclr(&InetAddr, sizeof(InetAddr)); + InetAddr.sa_family = AF_INET; + SockaddrInSetPort(&InetAddr, 79); + SockaddrInSetAddr(&InetAddr, 0x9f990000); + + // ask socket to give us local address that can connect to it + ds_memclr(&HostAddr, sizeof(HostAddr)); + SocketHost(&HostAddr, sizeof(HostAddr), &InetAddr, sizeof(InetAddr)); + + return(SockaddrInGetAddr(&HostAddr)); + #endif +} + +/*F*************************************************************************************/ +/*! + \Function _SocketLookupDone + + \Description + Callback to determine if gethostbyname is complete. + + \Input *pHost - pointer to host lookup record + + \Output + int32_t - zero=in progess, neg=done w/error, pos=done w/success + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +static int32_t _SocketLookupDone(HostentT *pHost) +{ + // return current status + return(pHost->done); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketLookupFree + + \Description + Release resources used by gethostbyname. + + \Input *pHost - pointer to host lookup record + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +static void _SocketLookupFree(HostentT *pHost) +{ + // release resource + pHost->refcount -= 1; +} + +/*F*************************************************************************************/ +/*! + \Function _SocketLookupThread + + \Description + Socket lookup thread + + \Input *pUserData - pointer to host lookup record + + \Output + int32_t - zero + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +static void _SocketLookupThread(void *pUserData) +{ + SocketStateT *pState = _Socket_pState; + HostentT *pHost = (HostentT *)pUserData; + struct addrinfo Hints, *pList = NULL; + int32_t iResult; + + // setup lookup hints + ds_memclr(&Hints, sizeof(Hints)); + Hints.ai_family = AF_UNSPEC; + Hints.ai_socktype = SOCK_STREAM; // set specific socktype to limit to one result per type + Hints.ai_protocol = IPPROTO_TCP; // set specific proto to limit to one result per type + Hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; + + // perform the blocking dns query + if ((iResult = getaddrinfo(pHost->name, NULL, &Hints, &pList)) == 0) + { + struct addrinfo *pAddrInfo; + + // first loop we look for IPv4 addresses (prefer IPv4 to IPv6) + for (pAddrInfo = pList; pAddrInfo != NULL; pAddrInfo = pAddrInfo->ai_next) + { + // verbose logging of address info if spam settings warrant + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetwin: addr=%A\n", pAddrInfo->ai_addr)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetwin: ai_flags=0x%08x\n", pAddrInfo->ai_flags)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetwin: ai_family=%d\n", pAddrInfo->ai_family)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetwin: ai_socktype=%d\n", pAddrInfo->ai_socktype)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetwin: ai_protocol=%d\n", pAddrInfo->ai_protocol)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetwin: name=%s\n", pAddrInfo->ai_canonname)); + + // extract first IPv4 address we come across + if ((pHost->addr == 0) && (pAddrInfo->ai_family == AF_INET)) + { + pHost->addr = SocketAddrMapAddress(&pState->AddrMap, pAddrInfo->ai_addr, (int32_t)pAddrInfo->ai_addrlen); + } + } + + // if we haven't found one yet, look for any address, and pick the first one we come across + for (pAddrInfo = pList; (pAddrInfo != NULL) && (pHost->addr == 0); pAddrInfo = pAddrInfo->ai_next) + { + pHost->addr = SocketAddrMapAddress(&pState->AddrMap, pAddrInfo->ai_addr, (int32_t)pAddrInfo->ai_addrlen); + } + + // print selected address + NetPrintfVerbose((pState->iVerbose, 0, "dirtynetwin: %s=%a\n", pHost->name, pHost->addr)); + + // mark success + pHost->done = 1; + + // add hostname to cache + SocketHostnameCacheAdd(pState->pHostnameCache, pHost->name, pHost->addr, pState->iVerbose); + + // release memory + freeaddrinfo(pList); + } + else + { + // unsuccessful + NetPrintf(("dirtynetwin: getaddrinfo('%s', ...) failed err=%d\n", pHost->name, iResult)); + pHost->done = -1; + } + + #if !defined(DIRTYCODE_XBOXONE) + // note thread completion + pHost->thread = 1; + #endif + // release thread-allocated refcount on hostname resource + pHost->refcount -= 1; +} + +/*F*************************************************************************************/ +/*! + \Function SocketLookup + + \Description + Lookup a host by name and return the corresponding Internet address. Uses + a callback/polling system since the socket library does not allow blocking. + + \Input *pText - pointer to null terminated address string + \Input iTimeout - number of milliseconds to wait for completion + + \Output + HostentT * - hostent struct that includes callback vectors + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +HostentT *SocketLookup(const char *pText, int32_t iTimeout) +{ + SocketStateT *pState = _Socket_pState; + int32_t iAddr, iResult; + HostentT *pHost, *pHostRef; + DirtyThreadConfigT ThreadConfig; + + NetPrintf(("dirtynetwin: looking up address for host '%s'\n", pText)); + + // dont allow negative timeouts + if (iTimeout < 0) + { + return(NULL); + } + + // create new structure + pHost = DirtyMemAlloc(sizeof(*pHost), SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + ds_memclr(pHost, sizeof(*pHost)); + + // setup callbacks + pHost->Done = &_SocketLookupDone; + pHost->Free = &_SocketLookupFree; + // copy over the target address + ds_strnzcpy(pHost->name, pText, sizeof(pHost->name)); + + // check for refcounted lookup + if ((pHostRef = SocketHostnameAddRef(&pState->pHostList, pHost, TRUE)) != NULL) + { + DirtyMemFree(pHost, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(pHostRef); + } + + // check for dot notation, then check hostname cache + if (((iAddr = SocketInTextGetAddr(pText)) != 0) || ((iAddr = SocketHostnameCacheGet(pState->pHostnameCache, pText, pState->iVerbose)) != 0)) + { + // save the data + pHost->addr = iAddr; + pHost->done = 1; + // mark as "thread complete" so _SocketLookupFree will release memory + pHost->thread = 1; + // return completed record + return(pHost); + } + + /* add an extra refcount for the thread; this ensures the host structure survives until the thread + is done with it. this must be done before thread creation. */ + pHost->refcount += 1; + + // configure dns thread parameters + ds_memclr(&ThreadConfig, sizeof(ThreadConfig)); + ThreadConfig.pName = "SocketLookup"; + ThreadConfig.iAffinity = NetConnStatus('affn', 0, NULL, 0); + ThreadConfig.iVerbosity = pState->iVerbose-1; + + // create dns lookup thread + if ((iResult = DirtyThreadCreate(_SocketLookupThread, pHost, &ThreadConfig)) != 0) + { + NetPrintf(("dirtynetwin: unable to create socket lookup thread (err=%d)\n", iResult)); + pHost->done = -1; + // remove refcount we just added + pHost->refcount -= 1; + } + + // return the host reference + return(pHost); +} + +/*F*************************************************************************************/ +/*! + \Function SocketHost + + \Description + Return the host address that would be used in order to communicate with the given + destination address. + + \Input *pHost - local sockaddr struct + \Input iHostLen - length of structure (sizeof(host)) + \Input *pDest - remote sockaddr struct + \Input iDestLen - length of structure (sizeof(dest)) + + \Output + int32_t - zero=success, negative=error + + \Version 10/04/1999 (gschaefer) +*/ +/************************************************************************************F*/ +int32_t SocketHost(struct sockaddr *pHost, int32_t iHostLen, const struct sockaddr *pDest, int32_t iDestLen) +{ + SocketStateT *pState = _Socket_pState; + struct sockaddr SockAddr; + char strHostName[256]; + int32_t iErr; + SOCKET iSock; + + // must be same kind of addresses + if (iHostLen != iDestLen) + { + return(-1); + } + + // do family specific lookup + if (pDest->sa_family == AF_INET) + { + // make them match initially + ds_memcpy(pHost, pDest, iHostLen); + + #if !defined(DIRTYCODE_XBOXONE) + // respect adapter override + if (pState->uAdapterAddress != 0) + { + SockaddrInSetAddr(pHost, pState->uAdapterAddress); + return(0); + } + #endif + + // zero the address portion + pHost->sa_data[2] = pHost->sa_data[3] = pHost->sa_data[4] = pHost->sa_data[5] = 0; + + // create a temp socket (must be datagram) + iSock = socket(AF_INET, SOCK_DGRAM, 0); + if (iSock != INVALID_SOCKET) + { + // use routing check if winsock 2.0 + if (pState->iVersion >= 0x200) + { + // get interface that would be used for this dest + ds_memclr(&SockAddr, sizeof(SockAddr)); + if (WSAIoctl(iSock, SIO_ROUTING_INTERFACE_QUERY, (void *)pDest, iDestLen, &SockAddr, sizeof(SockAddr), (LPDWORD)&iHostLen, NULL, NULL) < 0) + { + iErr = WSAGetLastError(); + NetPrintf(("dirtynetwin: WSAIoctl(SIO_ROUTING_INTERFACE_QUERY) returned %d\n", iErr)); + } + else + { + // copy over the result + ds_memcpy(pHost->sa_data+2, SockAddr.sa_data+2, 4); + } + + // convert loopback to requested address + if (SockaddrInGetAddr(pHost) == INADDR_LOOPBACK) + { + ds_memcpy(pHost->sa_data+2, pDest->sa_data+2, 4); + } + } + + // if no result, try connecting to socket then reading + // (this only works on some non-microsoft stacks) + if (SockaddrInGetAddr(pHost) == 0) + { + // connect to remote address + if (connect(iSock, pDest, iDestLen) == 0) + { + // query the host address + if (getsockname(iSock, &SockAddr, &iHostLen) == 0) + { + // just copy over the address portion + ds_memcpy(pHost->sa_data+2, SockAddr.sa_data+2, 4); + } + } + } + + // $$TODO evaluate possibility of unifying - (keep PC version) + #if defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) + // if still no result, use classic gethosthame/gethostbyname + // (works for microsoft, not for non-microsoft stacks) + if (SockaddrInGetAddr(pHost) == 0) + { + struct hostent *pHostent; + + // get machine name + gethostname(strHostName, sizeof(strHostName)); + // lookup ip info + pHostent = gethostbyname(strHostName); + if (pHostent != NULL) + { + ds_memcpy(pHost->sa_data+2, pHostent->h_addr_list[0], 4); + } + } + #else + // if still no result, use classic gethosthame/getaddrinfo + // (works for microsoft, not for non-microsoft stacks) + if (SockaddrInGetAddr(pHost) == 0) + { + int32_t iResult; + struct addrinfo Hints, *pList = NULL; + + // get machine name + ds_memclr(&Hints, sizeof(Hints)); + Hints.ai_family = AF_INET; + Hints.ai_socktype = SOCK_STREAM; + Hints.ai_protocol = IPPROTO_TCP; + gethostname(strHostName, sizeof(strHostName)); + // lookup ip info + if ((iResult = getaddrinfo(strHostName, NULL, &Hints, &pList)) == 0) + { + ds_memcpy(pHost->sa_data+2, pList->ai_addr->sa_data+2, 4); + } + else + { + NetPrintf(("dirtynetwin: getaddrinfo('%s', ...) failed err=%d\n", strHostName, iResult)); + } + + // release memory + freeaddrinfo(pList); + } + #endif + + // close the socket + closesocket(iSock); + } + return(0); + } + + // unsupported family + ds_memclr(pHost, iHostLen); + return(-3); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/netconnwin.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/netconnwin.c new file mode 100644 index 00000000..c25f0c56 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/pc/netconnwin.c @@ -0,0 +1,1257 @@ +/*H************************************************************************************************* + + \File netconnwin.c + + \Description + Provides network setup and teardown support. Does not actually create any + kind of network connections (see NetPlay). + + \Copyright + Copyright (c) 2001-2010 Electronic Arts Inc. + + \Version 03/12/2001 (gschaefer) First version + \Version 12/16/2001 (sbevan) Edited config file hack in NetConnDevStart + \Version 06/23/2009 (mclouatre) _NetConFindAdapter() changed to _NetConnFindMacAddress() + +*************************************************************************************************H*/ + +/*** Include files ********************************************************************************/ + +#define WIN32_LEAN_AND_MEAN 1 // avoid extra stuff + +#pragma warning(push,0) +#include +#pragma warning(pop) + +#include +#include // for GetAdaptersAddresses() +#include // for PIP_ADAPTER_ADDRESSES +#include // for strtol() + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtycert.h" +#include "DirtySDK/dirtylang.h" // for locality macros and definitions +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protoupnp.h" +#include "netconncommon.h" + +/*** Defines **************************************************************************************/ + +// LOCALE_SNAME: defined in WinNls.h (WINVER >= 0x0600). This block is just there for the case where the define is absent. +#ifndef LOCALE_SNAME +#define LOCALE_SNAME 0x0000005c // locale name (ie: en-us) +#endif + +//! GetGeoInfo returns XX for unknown country +#define NETCONNWIN_COUNTRY_UNKNOWN_STR ("XX") + +//! UPNP port +#define NETCONN_DEFAULT_UPNP_PORT (3659) + +/*** Macros ***************************************************************************************/ + +/*** Type Definitions *****************************************************************************/ + +//! private module state +typedef struct NetConnRefT +{ + NetConnCommonRefT Common; //!< cross-platform netconn data (must come first!) + + enum + { + ST_INIT, + ST_CONN, + ST_IDLE, + } eState; + + int32_t iPeerPort; //!< peer port to be opened by upnp; if zero, still find upnp router but don't open a port + ProtoUpnpRefT *pProtoUpnp; //!< protoupnp module state + int32_t iThreadCpuAffinity; //!< cpu affinity we use for our internal threads +} NetConnRefT; + +/*** Function Prototypes **************************************************************************/ + +/*** Variables ************************************************************************************/ + +//! mapping table to map Windows Language IDs to DirtySock encodings +/* + Language Identifier Constants and Strings: http://msdn.microsoft.com/en-us/library/dd318693(v=vs.85).aspx + +1. In order to reduce the size of the mapping table: + if locality(Primary Id, SUBLANG_NEUTRAL) == locality(Primary Id, SUBLANG_DEFAULT), + then only locality(Primary Id, SUBLANG_DEFAULT) is included by the table. + That means if we can't find locality(Primary Id, SUBLANG_NEUTRAL) in the table, + we should search the table again with langid(Primary Id, SUBLANG_DEFAULT). + Defined in WinNT.h: + #define SUBLANG_NEUTRAL 0x00 // language neutral + #define SUBLANG_DEFAULT 0x01 // user default + #define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 // Arabic (Saudi Arabia) + // normally, specific sublangs (ex. SUBLANG_ARABIC_SAUDI_ARABIA) are used by the table instead of using SUBLANG_DEFAULT. + + Another reason why we don't define all locality(Primary Id, SUBLANG_NEUTRAL) is: + Normally Windows API returns langid(Primary Id, specific_sublang) instead of langid(Primary Id, SUBLANG_NEUTRAL). + +2. The reason why we use a mapping table is because Windows API GetLocaleInfo(LOCALE_SNAME) is only available under Vista (or later). + +3. The result of GetLocaleInfo(LOCALE_SNAME) will be used to verify if the mapping table is good by _NetConnVerifyAllLocales. + (debug version only, Vista / Windows7 only) + +4. NetConnStatus('locl', 0, NULL, 0) can be used to obtain locality for local system. + The return value of NetConnStatus('locl', 0, NULL, 0) is similar to the return value of "locl" on the XBOX 360, ex. 'enUS'. +*/ +static uint16_t _NetConn_WindowsLocaleMap[214][3] = +{ + { MAKELANGID(LANG_AFRIKAANS, SUBLANG_AFRIKAANS_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_AFRIKAANS, LOBBYAPI_COUNTRY_SOUTH_AFRICA }, + { MAKELANGID(LANG_ALBANIAN, SUBLANG_ALBANIAN_ALBANIA), LOBBYAPI_LANGUAGE_ALBANIAN, LOBBYAPI_COUNTRY_ALBANIA }, + { MAKELANGID(LANG_AMHARIC, SUBLANG_AMHARIC_ETHIOPIA), LOBBYAPI_LANGUAGE_AMHARIC, LOBBYAPI_COUNTRY_ETHIOPIA }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_ALGERIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_ALGERIA }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_BAHRAIN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_BAHRAIN }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_EGYPT), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_EGYPT }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_IRAQ), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_IRAQ }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_JORDAN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_JORDAN }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_KUWAIT), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_KUWAIT }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_LEBANON), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_LEBANON }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_LIBYA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_LIBYAN_ARAB_JAMAHIRIYA }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_MOROCCO), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_MOROCCO }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_OMAN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_OMAN }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_QATAR), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_QATAR }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_SAUDI_ARABIA }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SYRIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_SYRIAN_ARAB_REPUBLIC }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_TUNISIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_TUNISIA }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_UAE), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_UNITED_ARAB_EMIRATES }, + { MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_YEMEN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_YEMEN }, + { MAKELANGID(LANG_ARMENIAN, SUBLANG_ARMENIAN_ARMENIA), LOBBYAPI_LANGUAGE_ARMENIAN, LOBBYAPI_COUNTRY_ARMENIA }, + { MAKELANGID(LANG_ASSAMESE, SUBLANG_ASSAMESE_INDIA), LOBBYAPI_LANGUAGE_ASSAMESE, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_AZERI, SUBLANG_AZERI_CYRILLIC), LOBBYAPI_LANGUAGE_AZERBAIJANI, LOBBYAPI_COUNTRY_AZERBAIJAN }, + { MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), LOBBYAPI_LANGUAGE_AZERBAIJANI, LOBBYAPI_COUNTRY_AZERBAIJAN }, + { MAKELANGID(LANG_BASHKIR, SUBLANG_BASHKIR_RUSSIA), LOBBYAPI_LANGUAGE_BASHKIR, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION }, + { MAKELANGID(LANG_BASQUE, SUBLANG_BASQUE_BASQUE), LOBBYAPI_LANGUAGE_BASQUE, LOBBYAPI_COUNTRY_SPAIN }, + { MAKELANGID(LANG_BELARUSIAN, SUBLANG_BELARUSIAN_BELARUS), LOBBYAPI_LANGUAGE_BYELORUSSIAN, LOBBYAPI_COUNTRY_BELARUS }, + { MAKELANGID(LANG_BENGALI, SUBLANG_BENGALI_BANGLADESH), LOBBYAPI_LANGUAGE_BENGALI, LOBBYAPI_COUNTRY_BANGLADESH }, + { MAKELANGID(LANG_BENGALI, SUBLANG_BENGALI_INDIA), LOBBYAPI_LANGUAGE_BENGALI, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_BOSNIAN, SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC), LOBBYAPI_LANGUAGE_BOSNIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA }, + { MAKELANGID(LANG_BOSNIAN, SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN), LOBBYAPI_LANGUAGE_BOSNIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA }, + { MAKELANGID(LANG_BRETON, SUBLANG_BRETON_FRANCE), LOBBYAPI_LANGUAGE_BRETON, LOBBYAPI_COUNTRY_FRANCE }, + { MAKELANGID(LANG_BULGARIAN, SUBLANG_BULGARIAN_BULGARIA), LOBBYAPI_LANGUAGE_BULGARIAN, LOBBYAPI_COUNTRY_BULGARIA }, + { MAKELANGID(LANG_CATALAN, SUBLANG_CATALAN_CATALAN), LOBBYAPI_LANGUAGE_CATALAN, LOBBYAPI_COUNTRY_SPAIN }, + { MAKELANGID(LANG_CHINESE, SUBLANG_NEUTRAL), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_CHINA }, + { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_HONG_KONG }, + { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_MACAU), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_MACAU }, + { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_SINGAPORE }, + { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_CHINA }, + { MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_TAIWAN }, + { MAKELANGID(LANG_CORSICAN, SUBLANG_CORSICAN_FRANCE), LOBBYAPI_LANGUAGE_CORSICAN, LOBBYAPI_COUNTRY_FRANCE }, + { MAKELANGID(LANG_CROATIAN, SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN), LOBBYAPI_LANGUAGE_CROATIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA }, + { MAKELANGID(LANG_CROATIAN, SUBLANG_CROATIAN_CROATIA), LOBBYAPI_LANGUAGE_CROATIAN, LOBBYAPI_COUNTRY_CROATIA }, + { MAKELANGID(LANG_CZECH, SUBLANG_CZECH_CZECH_REPUBLIC), LOBBYAPI_LANGUAGE_CZECH, LOBBYAPI_COUNTRY_CZECH_REPUBLIC }, + { MAKELANGID(LANG_DANISH, SUBLANG_DANISH_DENMARK), LOBBYAPI_LANGUAGE_DANISH, LOBBYAPI_COUNTRY_DENMARK }, + { MAKELANGID(LANG_DIVEHI, SUBLANG_DIVEHI_MALDIVES), LOBBYAPI_LANGUAGE_DIVEHI, LOBBYAPI_COUNTRY_MALDIVES }, + { MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), LOBBYAPI_LANGUAGE_DUTCH, LOBBYAPI_COUNTRY_BELGIUM }, + { MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), LOBBYAPI_LANGUAGE_DUTCH, LOBBYAPI_COUNTRY_NETHERLANDS }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_AUS), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_AUSTRALIA }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_BELIZE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_BELIZE }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CAN), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_CANADA }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CARIBBEAN), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_UNKNOWN }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_INDIA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_IRELAND }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_JAMAICA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_JAMAICA }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_MALAYSIA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_MALAYSIA }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_NZ), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_NEW_ZEALAND }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_PHILIPPINES), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_PHILIPPINES }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SINGAPORE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_SINGAPORE }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_SOUTH_AFRICA }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_TRINIDAD), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_TRINIDAD_AND_TOBAGO }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_UNITED_KINGDOM }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_UNITED_STATES }, + { MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_ZIMBABWE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_ZIMBABWE }, + { MAKELANGID(LANG_ESTONIAN, SUBLANG_ESTONIAN_ESTONIA), LOBBYAPI_LANGUAGE_ESTONIAN, LOBBYAPI_COUNTRY_ESTONIA }, + { MAKELANGID(LANG_FAEROESE, SUBLANG_FAEROESE_FAROE_ISLANDS), LOBBYAPI_LANGUAGE_FAEROESE, LOBBYAPI_COUNTRY_FAEROE_ISLANDS }, + { MAKELANGID(LANG_FINNISH, SUBLANG_FINNISH_FINLAND), LOBBYAPI_LANGUAGE_FINNISH, LOBBYAPI_COUNTRY_FINLAND }, + { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_BELGIUM }, + { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_CANADIAN), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_CANADA }, + { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_FRANCE }, + { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_LUXEMBOURG), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_LUXEMBOURG }, + { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_MONACO), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_MONACO }, + { MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_SWISS), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_SWITZERLAND }, + { MAKELANGID(LANG_FRISIAN, SUBLANG_FRISIAN_NETHERLANDS), LOBBYAPI_LANGUAGE_FRISIAN, LOBBYAPI_COUNTRY_NETHERLANDS }, + { MAKELANGID(LANG_GALICIAN, SUBLANG_GALICIAN_GALICIAN), LOBBYAPI_LANGUAGE_GALICIAN, LOBBYAPI_COUNTRY_SPAIN }, + { MAKELANGID(LANG_GEORGIAN, SUBLANG_GEORGIAN_GEORGIA), LOBBYAPI_LANGUAGE_GEORGIAN, LOBBYAPI_COUNTRY_GEORGIA }, + { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_AUSTRIA }, + { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_GERMANY }, + { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_LIECHTENSTEIN), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_LIECHTENSTEIN }, + { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_LUXEMBOURG), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_LUXEMBOURG }, + { MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_SWITZERLAND }, + { MAKELANGID(LANG_GREEK, SUBLANG_GREEK_GREECE), LOBBYAPI_LANGUAGE_GREEK, LOBBYAPI_COUNTRY_GREECE }, + { MAKELANGID(LANG_GREENLANDIC, SUBLANG_GREENLANDIC_GREENLAND), LOBBYAPI_LANGUAGE_GREENLANDIC, LOBBYAPI_COUNTRY_GREENLAND }, + { MAKELANGID(LANG_GUJARATI, SUBLANG_GUJARATI_INDIA), LOBBYAPI_LANGUAGE_GUJARATI, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_HAUSA, SUBLANG_HAUSA_NIGERIA_LATIN), LOBBYAPI_LANGUAGE_HAUSA, LOBBYAPI_COUNTRY_NIGERIA }, + { MAKELANGID(LANG_HEBREW, SUBLANG_HEBREW_ISRAEL), LOBBYAPI_LANGUAGE_HEBREW, LOBBYAPI_COUNTRY_ISRAEL }, + { MAKELANGID(LANG_HINDI, SUBLANG_HINDI_INDIA), LOBBYAPI_LANGUAGE_HINDI, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_HUNGARIAN, SUBLANG_HUNGARIAN_HUNGARY), LOBBYAPI_LANGUAGE_HUNGARIAN, LOBBYAPI_COUNTRY_HUNGARY }, + { MAKELANGID(LANG_ICELANDIC, SUBLANG_ICELANDIC_ICELAND), LOBBYAPI_LANGUAGE_ICELANDIC, LOBBYAPI_COUNTRY_ICELAND }, + { MAKELANGID(LANG_IGBO, SUBLANG_IGBO_NIGERIA), LOBBYAPI_LANGUAGE_IGBO, LOBBYAPI_COUNTRY_NIGERIA }, + { MAKELANGID(LANG_INDONESIAN, SUBLANG_INDONESIAN_INDONESIA), LOBBYAPI_LANGUAGE_INDONESIAN, LOBBYAPI_COUNTRY_INDONESIA }, + { MAKELANGID(LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA_LATIN), LOBBYAPI_LANGUAGE_INUKTITUT, LOBBYAPI_COUNTRY_CANADA }, + { MAKELANGID(LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA), LOBBYAPI_LANGUAGE_INUKTITUT, LOBBYAPI_COUNTRY_CANADA }, + { MAKELANGID(LANG_IRISH, SUBLANG_NEUTRAL), LOBBYAPI_LANGUAGE_IRISH, LOBBYAPI_COUNTRY_IRELAND }, + { MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), LOBBYAPI_LANGUAGE_IRISH, LOBBYAPI_COUNTRY_IRELAND }, + { MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), LOBBYAPI_LANGUAGE_ITALIAN, LOBBYAPI_COUNTRY_ITALY }, + { MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN_SWISS), LOBBYAPI_LANGUAGE_ITALIAN, LOBBYAPI_COUNTRY_SWITZERLAND }, + { MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), LOBBYAPI_LANGUAGE_JAPANESE, LOBBYAPI_COUNTRY_JAPAN }, + { MAKELANGID(LANG_KANNADA, SUBLANG_KANNADA_INDIA), LOBBYAPI_LANGUAGE_KANNADA, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_KAZAK, SUBLANG_KAZAK_KAZAKHSTAN), LOBBYAPI_LANGUAGE_KAZAKH, LOBBYAPI_COUNTRY_KAZAKHSTAN }, + { MAKELANGID(LANG_KHMER, SUBLANG_KHMER_CAMBODIA), LOBBYAPI_LANGUAGE_CAMBODIAN, LOBBYAPI_COUNTRY_CAMBODIA }, + { MAKELANGID(LANG_KINYARWANDA, SUBLANG_KINYARWANDA_RWANDA), LOBBYAPI_LANGUAGE_KINYARWANDA, LOBBYAPI_COUNTRY_RWANDA }, + { MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN), LOBBYAPI_LANGUAGE_KOREAN, LOBBYAPI_COUNTRY_KOREA_REPUBLIC_OF }, + { MAKELANGID(LANG_KYRGYZ, SUBLANG_KYRGYZ_KYRGYZSTAN), LOBBYAPI_LANGUAGE_KIRGHIZ, LOBBYAPI_COUNTRY_KYRGYZSTAN }, + { MAKELANGID(LANG_LAO, SUBLANG_LAO_LAO), LOBBYAPI_LANGUAGE_LAOTHIAN, LOBBYAPI_COUNTRY_LAO_PEOPLES_DEMOCRATIC_REPUBLIC }, + { MAKELANGID(LANG_LATVIAN, SUBLANG_LATVIAN_LATVIA), LOBBYAPI_LANGUAGE_LATVIAN_LETTISH, LOBBYAPI_COUNTRY_LATVIA }, + { MAKELANGID(LANG_LITHUANIAN, SUBLANG_LITHUANIAN), LOBBYAPI_LANGUAGE_LITHUANIAN, LOBBYAPI_COUNTRY_LITHUANIA }, + { MAKELANGID(LANG_LUXEMBOURGISH, SUBLANG_LUXEMBOURGISH_LUXEMBOURG), LOBBYAPI_LANGUAGE_LUXEMBOURGISH, LOBBYAPI_COUNTRY_LUXEMBOURG }, + { MAKELANGID(LANG_MACEDONIAN, SUBLANG_MACEDONIAN_MACEDONIA), LOBBYAPI_LANGUAGE_MACEDONIAN, LOBBYAPI_COUNTRY_MACEDONIA_THE_FORMER_YUGOSLAV_REPUBLIC_OF }, + { MAKELANGID(LANG_MALAY, SUBLANG_MALAY_BRUNEI_DARUSSALAM), LOBBYAPI_LANGUAGE_MALAY, LOBBYAPI_COUNTRY_BRUNEI_DARUSSALAM }, + { MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), LOBBYAPI_LANGUAGE_MALAY, LOBBYAPI_COUNTRY_MALAYSIA }, + { MAKELANGID(LANG_MALAYALAM, SUBLANG_MALAYALAM_INDIA), LOBBYAPI_LANGUAGE_MALAYALAM, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_MALTESE, SUBLANG_MALTESE_MALTA), LOBBYAPI_LANGUAGE_MALTESE, LOBBYAPI_COUNTRY_MALTA }, + { MAKELANGID(LANG_MAORI, SUBLANG_MAORI_NEW_ZEALAND), LOBBYAPI_LANGUAGE_MAORI, LOBBYAPI_COUNTRY_NEW_ZEALAND }, + { MAKELANGID(LANG_MARATHI, SUBLANG_MARATHI_INDIA), LOBBYAPI_LANGUAGE_MARATHI, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_MONGOLIAN, SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), LOBBYAPI_LANGUAGE_MONGOLIAN, LOBBYAPI_COUNTRY_MONGOLIA }, + { MAKELANGID(LANG_MONGOLIAN, SUBLANG_MONGOLIAN_PRC), LOBBYAPI_LANGUAGE_MONGOLIAN, LOBBYAPI_COUNTRY_CHINA }, + { MAKELANGID(LANG_NEPALI, SUBLANG_NEPALI_NEPAL), LOBBYAPI_LANGUAGE_NEPALI, LOBBYAPI_COUNTRY_NEPAL }, + { MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL), LOBBYAPI_LANGUAGE_NORWEGIAN_BOKMAL, LOBBYAPI_COUNTRY_NORWAY }, + { MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK), LOBBYAPI_LANGUAGE_NORWEGIAN_NYNORSK, LOBBYAPI_COUNTRY_NORWAY }, + { MAKELANGID(LANG_OCCITAN, SUBLANG_OCCITAN_FRANCE), LOBBYAPI_LANGUAGE_OCCITAN, LOBBYAPI_COUNTRY_FRANCE }, + { MAKELANGID(LANG_ORIYA, SUBLANG_ORIYA_INDIA), LOBBYAPI_LANGUAGE_ORIYA, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_PASHTO, SUBLANG_PASHTO_AFGHANISTAN), LOBBYAPI_LANGUAGE_PASHTO_PUSHTO, LOBBYAPI_COUNTRY_AFGHANISTAN }, + { MAKELANGID(LANG_PERSIAN, SUBLANG_PERSIAN_IRAN), LOBBYAPI_LANGUAGE_PERSIAN, LOBBYAPI_COUNTRY_IRAN }, + { MAKELANGID(LANG_POLISH, SUBLANG_POLISH_POLAND), LOBBYAPI_LANGUAGE_POLISH, LOBBYAPI_COUNTRY_POLAND }, + { MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), LOBBYAPI_LANGUAGE_PORTUGUESE, LOBBYAPI_COUNTRY_BRAZIL }, + { MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE), LOBBYAPI_LANGUAGE_PORTUGUESE, LOBBYAPI_COUNTRY_PORTUGAL }, + { MAKELANGID(LANG_PUNJABI, SUBLANG_PUNJABI_INDIA), LOBBYAPI_LANGUAGE_PUNJABI, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_QUECHUA, SUBLANG_QUECHUA_BOLIVIA), LOBBYAPI_LANGUAGE_QUECHUA, LOBBYAPI_COUNTRY_BOLIVIA }, + { MAKELANGID(LANG_QUECHUA, SUBLANG_QUECHUA_ECUADOR), LOBBYAPI_LANGUAGE_QUECHUA, LOBBYAPI_COUNTRY_ECUADOR }, + { MAKELANGID(LANG_QUECHUA, SUBLANG_QUECHUA_PERU), LOBBYAPI_LANGUAGE_QUECHUA, LOBBYAPI_COUNTRY_PERU }, + { MAKELANGID(LANG_ROMANIAN, SUBLANG_ROMANIAN_ROMANIA), LOBBYAPI_LANGUAGE_ROMANIAN, LOBBYAPI_COUNTRY_ROMANIA }, + { MAKELANGID(LANG_ROMANSH, SUBLANG_ROMANSH_SWITZERLAND), LOBBYAPI_LANGUAGE_RHAETO_ROMANCE, LOBBYAPI_COUNTRY_SWITZERLAND }, + { MAKELANGID(LANG_RUSSIAN, SUBLANG_RUSSIAN_RUSSIA), LOBBYAPI_LANGUAGE_RUSSIAN, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_NORTHERN_FINLAND), LOBBYAPI_LANGUAGE_SAMI, LOBBYAPI_COUNTRY_FINLAND }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_NORTHERN_NORWAY), LOBBYAPI_LANGUAGE_SAMI, LOBBYAPI_COUNTRY_NORWAY }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_NORTHERN_SWEDEN), LOBBYAPI_LANGUAGE_SAMI, LOBBYAPI_COUNTRY_SWEDEN }, + { MAKELANGID(LANG_SANSKRIT, SUBLANG_SANSKRIT_INDIA), LOBBYAPI_LANGUAGE_SANSKRIT, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA }, + { MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN),LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA }, + { MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA_AND_MONTENEGRO }, + { MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_LATIN), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA_AND_MONTENEGRO }, + { MAKELANGID(LANG_SERBIAN, 0x0C), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_MONTENEGRO }, + { MAKELANGID(LANG_SERBIAN, 0x0B), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_MONTENEGRO }, + { MAKELANGID(LANG_SERBIAN, 0x0A), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA }, + { MAKELANGID(LANG_SERBIAN, 0x09), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA }, + { MAKELANGID(LANG_SINHALESE, SUBLANG_SINHALESE_SRI_LANKA), LOBBYAPI_LANGUAGE_SINGHALESE, LOBBYAPI_COUNTRY_SRI_LANKA }, + { MAKELANGID(LANG_SLOVAK, SUBLANG_SLOVAK_SLOVAKIA), LOBBYAPI_LANGUAGE_SLOVAK, LOBBYAPI_COUNTRY_SLOVAKIA }, + { MAKELANGID(LANG_SLOVENIAN, SUBLANG_SLOVENIAN_SLOVENIA), LOBBYAPI_LANGUAGE_SLOVENIAN, LOBBYAPI_COUNTRY_SLOVENIA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_ARGENTINA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_BOLIVIA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_BOLIVIA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_CHILE), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_CHILE }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_COLOMBIA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_COLOMBIA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_COSTA_RICA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_COSTA_RICA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_DOMINICAN_REPUBLIC), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_DOMINICAN_REPUBLIC }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_ECUADOR), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_ECUADOR }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_EL_SALVADOR), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_EL_SALVADOR }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_GUATEMALA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_GUATEMALA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_HONDURAS), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_HONDURAS }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MEXICAN), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_MEXICO }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_NICARAGUA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_NICARAGUA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PANAMA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PANAMA }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PARAGUAY), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PARAGUAY }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PERU), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PERU }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PUERTO_RICO), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PUERTO_RICO }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_SPAIN }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_SPAIN }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_US), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_UNITED_STATES }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_URUGUAY), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_URUGUAY }, + { MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_VENEZUELA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_VENEZUELA }, + { MAKELANGID(LANG_SWAHILI, SUBLANG_DEFAULT), LOBBYAPI_LANGUAGE_SWAHILI, LOBBYAPI_COUNTRY_KENYA }, + { MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH_FINLAND), LOBBYAPI_LANGUAGE_SWEDISH, LOBBYAPI_COUNTRY_FINLAND }, + { MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), LOBBYAPI_LANGUAGE_SWEDISH, LOBBYAPI_COUNTRY_SWEDEN }, + { MAKELANGID(LANG_TAJIK, SUBLANG_TAJIK_TAJIKISTAN), LOBBYAPI_LANGUAGE_TAJIK, LOBBYAPI_COUNTRY_TAJIKISTAN }, + { MAKELANGID(LANG_TAMIL, SUBLANG_TAMIL_INDIA), LOBBYAPI_LANGUAGE_TAMIL, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_TATAR, SUBLANG_TATAR_RUSSIA), LOBBYAPI_LANGUAGE_TATAR, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION }, + { MAKELANGID(LANG_TELUGU, SUBLANG_TELUGU_INDIA), LOBBYAPI_LANGUAGE_TELUGU, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_THAI, SUBLANG_THAI_THAILAND), LOBBYAPI_LANGUAGE_THAI, LOBBYAPI_COUNTRY_THAILAND }, + { MAKELANGID(LANG_TIBETAN, SUBLANG_TIBETAN_PRC), LOBBYAPI_LANGUAGE_TIBETAN, LOBBYAPI_COUNTRY_CHINA }, + { MAKELANGID(LANG_TSWANA, SUBLANG_TSWANA_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_SETSWANA, LOBBYAPI_COUNTRY_SOUTH_AFRICA }, + { MAKELANGID(LANG_TURKISH, SUBLANG_TURKISH_TURKEY), LOBBYAPI_LANGUAGE_TURKISH, LOBBYAPI_COUNTRY_TURKEY }, + { MAKELANGID(LANG_TURKMEN, SUBLANG_TURKMEN_TURKMENISTAN), LOBBYAPI_LANGUAGE_TURKMEN, LOBBYAPI_COUNTRY_TURKMENISTAN }, + { MAKELANGID(LANG_UIGHUR, SUBLANG_UIGHUR_PRC), LOBBYAPI_LANGUAGE_UIGHUR, LOBBYAPI_COUNTRY_CHINA }, + { MAKELANGID(LANG_UKRAINIAN, SUBLANG_UKRAINIAN_UKRAINE), LOBBYAPI_LANGUAGE_UKRAINIAN, LOBBYAPI_COUNTRY_UKRAINE }, + { MAKELANGID(LANG_URDU, SUBLANG_URDU_PAKISTAN), LOBBYAPI_LANGUAGE_URDU, LOBBYAPI_COUNTRY_PAKISTAN }, + { MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_CYRILLIC), LOBBYAPI_LANGUAGE_UZBEK, LOBBYAPI_COUNTRY_UZBEKISTAN }, + { MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), LOBBYAPI_LANGUAGE_UZBEK, LOBBYAPI_COUNTRY_UZBEKISTAN }, + { MAKELANGID(LANG_VIETNAMESE, SUBLANG_VIETNAMESE_VIETNAM), LOBBYAPI_LANGUAGE_VIETNAMESE, LOBBYAPI_COUNTRY_VIETNAM }, + { MAKELANGID(LANG_WELSH, SUBLANG_WELSH_UNITED_KINGDOM), LOBBYAPI_LANGUAGE_WELSH, LOBBYAPI_COUNTRY_UNITED_KINGDOM }, + { MAKELANGID(LANG_WOLOF, SUBLANG_WOLOF_SENEGAL), LOBBYAPI_LANGUAGE_WOLOF, LOBBYAPI_COUNTRY_SENEGAL }, + { MAKELANGID(LANG_XHOSA, SUBLANG_XHOSA_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_XHOSA, LOBBYAPI_COUNTRY_SOUTH_AFRICA }, + { MAKELANGID(LANG_YI, SUBLANG_YI_PRC), LOBBYAPI_LANGUAGE_YI, LOBBYAPI_COUNTRY_CHINA }, + { MAKELANGID(LANG_YORUBA, SUBLANG_YORUBA_NIGERIA), LOBBYAPI_LANGUAGE_YORUBA, LOBBYAPI_COUNTRY_NIGERIA }, + { MAKELANGID(LANG_ZULU, SUBLANG_ZULU_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_ZULU, LOBBYAPI_COUNTRY_SOUTH_AFRICA }, + { MAKELANGID(0x91, SUBLANG_DEFAULT), LOBBYAPI_LANGUAGE_SCOTS_GAELIC, LOBBYAPI_COUNTRY_UNITED_KINGDOM }, + // the following are valid Windows locales, but not covered by ISO639-1, LOBBYAPI_LANGUAGE_UNKNOWN will be returned for them. + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_INARI_FINLAND), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_FINLAND }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_LULE_NORWAY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_NORWAY }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_LULE_SWEDEN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SWEDEN }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_SKOLT_FINLAND), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_FINLAND }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_SOUTHERN_NORWAY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_NORWAY }, + { MAKELANGID(LANG_SAMI, SUBLANG_SAMI_SOUTHERN_SWEDEN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SWEDEN }, + { MAKELANGID(LANG_UPPER_SORBIAN, SUBLANG_UPPER_SORBIAN_GERMANY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_GERMANY }, + { MAKELANGID(LANG_LOWER_SORBIAN, SUBLANG_LOWER_SORBIAN_GERMANY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_GERMANY }, + { MAKELANGID(LANG_KONKANI, SUBLANG_KONKANI_INDIA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_INDIA }, + { MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SYRIAN_ARAB_REPUBLIC }, + { MAKELANGID(LANG_TAMAZIGHT, SUBLANG_NEUTRAL), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_ALGERIA }, + { MAKELANGID(LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_ALGERIA }, + { MAKELANGID(LANG_FILIPINO, SUBLANG_FILIPINO_PHILIPPINES), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_PHILIPPINES }, + { MAKELANGID(LANG_SOTHO, SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SOUTH_AFRICA }, + { MAKELANGID(LANG_MAPUDUNGUN, SUBLANG_MAPUDUNGUN_CHILE), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_CHILE }, + { MAKELANGID(LANG_MOHAWK, SUBLANG_MOHAWK_MOHAWK), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_CANADA }, + { MAKELANGID(LANG_ALSATIAN, SUBLANG_ALSATIAN_FRANCE), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_FRANCE }, + { MAKELANGID(LANG_YAKUT, SUBLANG_YAKUT_RUSSIA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION }, + { MAKELANGID(LANG_KICHE, SUBLANG_KICHE_GUATEMALA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_GUATEMALA }, + { MAKELANGID(LANG_DARI, SUBLANG_DARI_AFGHANISTAN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_AFGHANISTAN } +}; + +//! global module ref +static NetConnRefT *_NetConn_pRef = NULL; + +/*** Private Functions ****************************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _NetConnGetLocaleInfo + + \Description + To obtain locale information for specified lang id. + + \Input uLangId - the lang id used to extract locale info from + + \Output + int32_t - the same output to NetConnStatus('locl'), ex. enUS + + \Version 04/14/2011 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _NetConnGetLocaleInfo(uint16_t uLangId) +{ + uint16_t uLanguage, uCountry; + uint32_t uIndex; + + // search the pre-defined mapping table + for (uIndex = 0; uIndex < (sizeof(_NetConn_WindowsLocaleMap)/sizeof(_NetConn_WindowsLocaleMap[0])); uIndex++) + { + // _NetConn_WindowsLocaleMap[uIndex][0]: LangId, [1]: language, [2]: COUNTRY + if (uLangId == _NetConn_WindowsLocaleMap[uIndex][0]) + { + uLanguage = _NetConn_WindowsLocaleMap[uIndex][1]; + uCountry = _NetConn_WindowsLocaleMap[uIndex][2]; + return(LOBBYAPI_LocalizerTokenCreate(uLanguage, uCountry)); + } + } + + // not found in pre-defined mapping table, search again with SUBLANG_DEFAULT (only for SUBLANG_NEUTRAL) + if ((SUBLANGID(uLangId) == SUBLANG_NEUTRAL) && (SUBLANG_NEUTRAL != SUBLANG_DEFAULT)) + { + uLangId = PRIMARYLANGID(uLangId); + return(_NetConnGetLocaleInfo(MAKELANGID(uLangId, SUBLANG_DEFAULT))); + } + + NetPrintf(("netconnwin: failed to obtain locale info for localid (0x%04x), %s is returned.\n", (uint32_t)uLangId, LOBBYAPI_LOCALITY_UNKNOWN_STR)); + // not found, zzZZ will be returned + return(LOBBYAPI_LOCALITY_UNKNOWN); +} + + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _NetConnVerifyLocale + + \Description + Check if a _NetConn_WindowsLocaleMap entry is good by comparing with Windows API (GetLocaleInfo)'s result. + + \Input *pLocale - a _NetConn_WindowsLocaleMap entry -- uint16_t[3] + \Input iAlwaysPrint - (boolean) TRUE, always prints debug trace; FALSE, prints debug trace upon error + + \Output + int32_t - (boolean) TRUE=success, FALSE=failure + + \Notes + For debugging purpose only. + + \Version 03/25/2011 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _NetConnVerifyLocale(uint16_t *pLocale, int32_t iAlwaysPrint) +{ + int32_t iResult = FALSE; + char strLocale[LOCALE_NAME_MAX_LENGTH], strDefined[32]; + char *pTemp1, *pTemp2; + uint32_t uToken; + + // convert array to string like enUS + uToken = LOBBYAPI_LocalizerTokenCreate(pLocale[1], pLocale[2]); + LOBBYAPI_LocalizerTokenCreateLocalityString(strDefined, uToken); + + // compare between mapping table value and Windows API GetLocaleInfo(LOCALE_SNAME) to verify whether the mapping table value is good. + ds_memclr(strLocale, sizeof(strLocale)); + if (GetLocaleInfo(pLocale[0], LOCALE_SNAME, strLocale, LOCALE_NAME_MAX_LENGTH) > 0) + { + // GetLocaleInfo may return name-script-COUNTRY. If so, we remove the script: name-script-COUNTRY --> nameCOUNTRY + if ((pTemp1 = strchr(strLocale, '-')) != NULL) + { + // name-script-COUNTRY --> nameCOUNTRY + if ((pTemp2 = strchr(pTemp1 + 1, '-')) != NULL) + { + pTemp2++; // skip '-' + memmove(pTemp1, pTemp2, strlen(pTemp2) + 1); // plus '\0' + } + else // name-COUNTRY --> nameCOUNTRY + { + memmove(pTemp1, pTemp1 + 1, strlen(pTemp1 + 1) + 1); + } + } + + // GetLocaleInfo may return name-COUNTRY_xxxx. If so, we remove _xxxx: nameCOUNTRY_xxxx --> nameCOUNTRY + if ((pTemp1 = strchr(strLocale, '_')) != NULL) + { + pTemp1[0] = 0; + } + + // the language name should be in lowercase while the country name should be in uppercase. + // 1, enUS == enUS (perfect); 2, zzCA == xxxCA (not covered by ISO639-1); 3, xxCA == xxyCA (not covered by ISO639-1); 4, enZZ == enxxx; + if((strcmp(strDefined, strLocale) == 0) + || (strncmp(strDefined, LOBBYAPI_LANGUAGE_UNKNOWN_STR, 2) == 0 && strncmp(strDefined + 2, strLocale + 3, 2) == 0) + || (strncmp(strDefined, strLocale, 2) == 0 && strncmp(strDefined + 2, strLocale + 3, 2) == 0) + || (strncmp(strDefined, strLocale, 2) == 0 && strncmp(strDefined + 2, LOBBYAPI_COUNTRY_UNKNOWN_STR, 2) == 0 && strlen(strLocale) != 4)) + { + // check if _NetConnGetLocaleInfo behaves correctly + if ((uint32_t)_NetConnGetLocaleInfo(pLocale[0]) == uToken) + { + iResult = TRUE; + if (iAlwaysPrint != FALSE) + { + NetPrintf(("netconnwin: passed -- GetLocaleInfo(0x%04x) returned (%s)!\n", (uint32_t)pLocale[0], strLocale)); + } + // if not perfect (enUS == enUS), print debug trace + if (strcmp(strDefined, strLocale) != 0) + { + NetPrintf(("netconnwin: notice -- GetLocaleInfo(0x%04x) returned (%s) - (%s)!\n", (uint32_t)pLocale[0], strLocale, strDefined)); + } + } + } + } + + if (iResult == FALSE) + { + NetPrintf(("netconnwin: ERROR -- GetLocaleInfo(0x%04x) returned (%s) - (%s)!\n", (uint32_t)pLocale[0], strLocale, strDefined)); + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnVerifyAllLocales + + \Description + Verify if _NetConn_WindowsLocaleMap items are good. For debugging purpose only. + + \Notes + For unknown language, zz will be returned; + For unknown country, ZZ will be returned; + For unknown locality, zzZZ will be returned. + + \Version 03/25/2011 (szhu) +*/ +/********************************************************************************F*/ +static void _NetConnVerifyAllLocales(void) +{ + uint16_t aNeutral[3]; + uint32_t uIndex, uToken; + char strLocale[LOCALE_NAME_MAX_LENGTH]; + + for (uIndex = 0; uIndex < (sizeof(_NetConn_WindowsLocaleMap)/sizeof(_NetConn_WindowsLocaleMap[0])); uIndex++) + { + if (_NetConnVerifyLocale(_NetConn_WindowsLocaleMap[uIndex], FALSE) == FALSE) + { + NetPrintf(("netconnwin: error found in _NetConn_WindowsLocaleMap[%u] (0x%04x)!\n", uIndex, (uint32_t)_NetConn_WindowsLocaleMap[uIndex][0])); + DebugBreak(); + } + + // verify if langid(Primary ID, SUBLANG_NEUTRAL) works out + // exception 0x0404: 0x0404 --> ZH_TW, 0x0004 --> ZH_CN + if ((_NetConn_WindowsLocaleMap[uIndex][0] != 0x0404) && (SUBLANGID(_NetConn_WindowsLocaleMap[uIndex][0]) == SUBLANG_DEFAULT)) + { + aNeutral[0] = MAKELANGID(PRIMARYLANGID(_NetConn_WindowsLocaleMap[uIndex][0]), SUBLANG_NEUTRAL); + aNeutral[1] = _NetConn_WindowsLocaleMap[uIndex][1]; + aNeutral[2] = _NetConn_WindowsLocaleMap[uIndex][2]; + + if (_NetConnVerifyLocale(aNeutral, FALSE) == FALSE) + { + NetPrintf(("netconnwin: error found in _NetConn_WindowsLocaleMap[%u](Neutral) (0x%04x)!\n", uIndex, (uint32_t)aNeutral[0])); + DebugBreak(); + } + } + } + NetPrintf(("netconnwin: checked _NetConn_WindowsLocaleMap -- %u entries in total.\n", uIndex)); + + // print locale information + uToken = NetConnStatus('locl', 0, NULL, 0); + NetPrintf(("netconnwin: locality returned by NetConnStatus(locl,0): (%c%c%c%c).\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken))); + + // test if _NetConnGetLocaleInfo works fine when given an invalid langid + uToken = _NetConnGetLocaleInfo(0x9FFF); + NetPrintf(("netconnwin: _NetConnGetLocaleInfo(0x9FFF) returned: (%c%c%c%c) - (%s).\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken), LOBBYAPI_LOCALITY_UNKNOWN_STR)); + + // list locales that not covered by the mapping table + for (uIndex = 1; uIndex <= 0x8200; uIndex++) + { + if ((PRIMARYLANGID(uIndex) == LANG_NEUTRAL) + || (uIndex == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)) + || (uIndex == MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT))) + { + continue; + } + + if ((GetLocaleInfo(uIndex, LOCALE_SNAME, strLocale, LOCALE_NAME_MAX_LENGTH) > 0) + && (_NetConnGetLocaleInfo(uIndex) == 'zzZZ')) + { + NetPrintf(("netconnwin: not covered (0x%04x): %s!\n", uIndex, strLocale)); + } + } +} +#endif //DIRTYCODE_LOGGING + +/*F********************************************************************************/ +/*! + \Function _NetConnIsValidMacAddress + + \Description + Checks to make sure the address is valid + + \Input *pAddr - The current address we are checking for validity + \Input bCheckIP - Should we check the IP Address of the NIC to make sure it is active + + \Output + uint8_t - TRUE=valid, FALSE=invalid +*/ +/********************************************************************************F*/ +static uint8_t _NetConnIsValidMacAddress(PIP_ADAPTER_ADDRESSES pAddr, uint8_t bCheckIP) +{ + PIP_ADAPTER_UNICAST_ADDRESS pUnicastAddr; + + // If the address is not 6-byte long it is invalid + if (pAddr->PhysicalAddressLength != 6) + { + return(FALSE); + } + + // If we are not checking the IP address then the MAC is valid + // as far as we are concerned + if (bCheckIP == FALSE) + { + return(TRUE); + } + + // Make sure the address has a valid IP in multiple NIC situation + for (pUnicastAddr = pAddr->FirstUnicastAddress; pUnicastAddr != NULL; pUnicastAddr = pUnicastAddr->Next) + { + if ((pUnicastAddr->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) == IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) + { + return(TRUE); + } + } + + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnFindMacAddress + + \Description + Scans through adapter list and finds an active adapter. + + \Input *pMacAddr - user-provided buffer to be filled with MAC address + \Input iAddrSize - The size of the buffer + \Input bCheckIP - If we should check for a valid IP on the NIC + + \Output + int32_t - Negative=error, zero=success + + \Version 06/23/2009 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _NetConnFindMacAddress(uint8_t *pMacAddr, int32_t iAddrSize, uint8_t bCheckIP) +{ + NetConnRefT *pRef = _NetConn_pRef; + DWORD dwMsRetVal; + ULONG ulOutBufLen; + ULONG ulDummyBuffer; + PIP_ADAPTER_ADDRESSES pAdaptersAddrBuf; + PIP_ADAPTER_ADDRESSES pCurrAddr; + +#if DIRTYCODE_LOGGING + uint32_t uByteIndex; + char strPrintBuf[32]; + int32_t iCurrLen; +#endif + + int32_t iRetVal = 0; + + // make an initial call to GetAdaptersAddresses() to get the + // size needed into the ulOutBufLen variable + ulOutBufLen = 1; // make sure that size provided is too small to store the info. + dwMsRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, (PIP_ADAPTER_ADDRESSES) &ulDummyBuffer, &ulOutBufLen); + if (dwMsRetVal != ERROR_BUFFER_OVERFLOW) + { + // if windows does not return an overflow error, something is wrong... + NetPrintf(("netconnwin: GetAdaptersAddresses() failed with err %d when querying for output buffer size\n", dwMsRetVal)); + return(-1); + } + + /* + allocate a big enough buffer. + note: + * it is not a good idea to preallocate this as we will have to seriously overallocate to support rare cases + with a long list of IP_ADAPTER_ADDRESSES returned. MS doc states that Windows will "typically" not + require a buffer larger than 15 Kbytes http://msdn.microsoft.com/en-us/library/aa365915(VS.85).aspx + * it is not a good idea to allocate this buffer on the stack as it can be quite huge and could lead to a + stack overflow. + */ + if ((pAdaptersAddrBuf = DirtyMemAlloc(ulOutBufLen, NETCONN_MEMID, pRef->Common.iMemGroup, pRef->Common.pMemGroupUserData)) == NULL) + { + NetPrintf(("netconnwin: unable to allocate output buffer required to call GetAdaptersAddresses()\n")); + return(-2); + } + + // make a second call to GetAdaptersAddresses() to get the actual data we want + dwMsRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAdaptersAddrBuf, &ulOutBufLen); + if (dwMsRetVal == NO_ERROR) + { + // loop through the adapters and match first entry with valid MAC address + pCurrAddr = pAdaptersAddrBuf; + while (pCurrAddr) + { + // make sure interface type can have a MAC address + if ((pCurrAddr->IfType == IF_TYPE_ETHERNET_CSMACD) || (pCurrAddr->IfType == IF_TYPE_IEEE80211)) + { + // make sure physical address is 6-byte long and has a valid ip address; if not, it is not a MAC address + if (_NetConnIsValidMacAddress(pCurrAddr, bCheckIP) == TRUE) + { + ds_memcpy_s(pMacAddr, iAddrSize, pCurrAddr->PhysicalAddress, pCurrAddr->PhysicalAddressLength); + + #if DIRTYCODE_LOGGING + // We need to use sprintf here because if we try to NetPrintf we will + // have timestamps inbetween where we are printing out the mac address + + ds_memclr(strPrintBuf, sizeof(strPrintBuf)); + iCurrLen = 0; + + for (uByteIndex = 0; uByteIndex < (int32_t) pCurrAddr->PhysicalAddressLength; uByteIndex++) + { + if (uByteIndex == (pCurrAddr->PhysicalAddressLength - 1)) + { + iCurrLen += ds_snzprintf(strPrintBuf + iCurrLen, sizeof(strPrintBuf) - iCurrLen, "%.2X", (int32_t)pCurrAddr->PhysicalAddress[uByteIndex]); + } + else + { + iCurrLen += ds_snzprintf(strPrintBuf + iCurrLen, sizeof(strPrintBuf) - iCurrLen, "%.2X-", (int32_t)pCurrAddr->PhysicalAddress[uByteIndex]); + } + } + + NetPrintf(("netconnwin: valid MAC address found --> %s\n", strPrintBuf)); + #endif + + // valid MAC address found! exit the function with success + break; + } + } + + pCurrAddr = pCurrAddr->Next; + } + + if (pCurrAddr == NULL) + { + NetPrintf(("netconnwin: could not find any valid adapters to get a MAC address from\n")); + iRetVal = -3; + } + } + else + { + NetPrintf(("netconnwin: GetAdaptersAddresses() failed with err %d when querying for adapters data size\n", dwMsRetVal)); + iRetVal = -4; + } + + // free buffer used with GetAdaptersAddresses + DirtyMemFree(pAdaptersAddrBuf, NETCONN_MEMID, pRef->Common.iMemGroup, pRef->Common.pMemGroupUserData); + + return(iRetVal); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnShutdownInternal + + \Description + Shutdown the network code and return to idle state for internal use + + \Input pRef - netconn ref + \Input bStopEE - ignored in PC implementation + + \Output + int32_t - negative=error, else zero + + \Version 1/13/2020 (tcho) +*/ +/********************************************************************************F*/ +int32_t _NetConnShutdownInternal(NetConnRefT *pRef, uint32_t bStopEE) +{ + int32_t iResult = 0; + + // decrement and check the refcount + if ((iResult = NetConnCommonCheckRef((NetConnCommonRefT*)pRef)) < 0) + { + return(iResult); + } + + if (_NetConn_pRef != NULL) + { + NetConnDisconnect(); + } + + // destroy the upnp ref + if (pRef->pProtoUpnp != NULL) + { + ProtoUpnpDestroy(pRef->pProtoUpnp); + pRef->pProtoUpnp = NULL; + } + + // shut down protossl + ProtoSSLShutdown(); + + // shut down dirtycert + DirtyCertDestroy(); + + // shut down Idle handler + NetConnIdleShutdown(); + + // shut down dirtysock + SocketDestroy(0); + + // common shutdown (must come last as this frees the memory) + NetConnCommonShutdown(&pRef->Common); + _NetConn_pRef = NULL; + + return(0); +} + +/*** Public functions *****************************************************************************/ + +/*F********************************************************************************/ +/*! + \Function NetConnStartup + + \Description + Bring the network connection module to life. Does not actually start any + network activity. + + \Input *pParams - startup parameters + + \Output + int32_t - zero=success, negative=failure + + \Notes + NetConnRefT::iRefCount serves as a counter for the number of times + NetConnStartup has been called. This allows us to track how many modules + are using it and how many times we expect NetConnShutdown to the called. + In the past we only allowed a single call to NetConnStartup but some + libraries may need to networking without a guarentee that the game has + already started it. + + pParams can contain the following terms: + + \verbatim + -noupnp : disable UPNP + -servicename= : set servicename required for SSL use + -affinity= : cpu affinity we set for our internal threads + \endverbatim + + \Version 3/10/01 (GWS) +*/ +/********************************************************************************F*/ +int32_t NetConnStartup(const char *pParams) +{ + NetConnRefT *pRef = _NetConn_pRef; + int32_t iResult = 0; + char strThreadCpuAffinity[16]; + + // allow NULL params + if (pParams == NULL) + { + pParams = ""; + } + + // debug display of input params + NetPrintf(("netconnwin: startup params='%s'\n", pParams)); + + // common startup + // pRef shall hold the address of the NetConnRefT after completion if no error occured + iResult = NetConnCommonStartup(sizeof(*pRef), pParams, (NetConnCommonRefT**)(&pRef)); + + // treat the result of the common startup, if already started simply early out + if (iResult == NETCONN_ERROR_ALREADY_STARTED) + { + return(0); + } + // otherwise, if an error occured report it + else if (iResult < 0) + { + return(iResult); + } + + pRef->eState = ST_INIT; + pRef->iPeerPort = NETCONN_DEFAULT_UPNP_PORT; + + // get the cpu affinity string from our startup params, defaulting to 0x0 + ds_memclr(strThreadCpuAffinity, sizeof(strThreadCpuAffinity)); + NetConnCopyParam(strThreadCpuAffinity, sizeof(strThreadCpuAffinity), "-affinity=", pParams, "0x0"); + pRef->iThreadCpuAffinity = strtol(strThreadCpuAffinity, NULL, 16); + + // start up dirtysock + if (SocketCreate(THREAD_PRIORITY_HIGHEST, 0, pRef->iThreadCpuAffinity) != 0) + { + _NetConnShutdownInternal(pRef, FALSE); + NetPrintf(("netconnwin: unable to start up dirtysock\n")); + return(NETCONN_ERROR_SOCKET_CREATE); + } + + // create and configure dirtycert + if (NetConnDirtyCertCreate(pParams)) + { + _NetConnShutdownInternal(pRef, FALSE); + NetPrintf(("netconnwin: unable to create dirtycert\n")); + return(NETCONN_ERROR_DIRTYCERT_CREATE); + } + + // start up protossl + if (ProtoSSLStartup() < 0) + { + _NetConnShutdownInternal(pRef, FALSE); + NetPrintf(("netconnwin: unable to start up protossl\n")); + return(NETCONN_ERROR_PROTOSSL_CREATE); + } + + if (strstr(pParams, "-noupnp") == NULL) + { + pRef->pProtoUpnp = ProtoUpnpCreate(); + if (pRef->pProtoUpnp == NULL) + { + _NetConnShutdownInternal(pRef, FALSE); + NetPrintf(("netconnwin: unable to start up protoupnp\n")); + return(NETCONN_ERROR_PROTOUPNP_CREATE); + } + } + + // save ref + _NetConn_pRef = pRef; + + #if DIRTYCODE_LOGGING + // if we made changes to the mapping table (_NetConn_WindowsLocaleMap), + // uncomment the following line and build-debug-run to verify if the changes are good. + //_NetConnVerifyAllLocales(); + #endif + + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnQuery + + \Description + Query the list of available connection configurations. This list is loaded + from the specified device. The list is returned in a simple fixed width + array with one string per array element. The caller can find the user portion + of the config name via strchr(item, '#')+1. + + \Input *pDevice - device to scan (mc0:, mc1:, pfs0:, pfs1:) + \Input *pList - buffer to store output array in + \Input iSize - length of buffer in bytes + + \Output + int32_t - negative=error, else number of configurations + + \Version 01/18/02 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetConnQuery(const char *pDevice, NetConfigRecT *pList, int32_t iSize) +{ + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnConnect + + \Description + Used to bring the networking online with a specific configuration. Uses the + configuration returned by NetConnQuery. + + \Input *pConfig - the configuration entry from NetConnQuery + \Input *pOption - asciiz list of config parameters + "peerport=" to specify peer port to be opened by upnp. + \Input iData - platform-specific + + \Output + int32_t - negative=error, zero=success + + \Version 01/18/02 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetConnConnect(const NetConfigRecT *pConfig, const char *pOption, int32_t iData) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // check connection options, if present + if (pOption != NULL) + { + const char *pOpt; + // check for specification of peer port + if ((pOpt = strstr(pOption, "peerport=")) != NULL) + { + pRef->iPeerPort = strtol(pOpt+9, NULL, 10); + } + } + NetPrintf(("netconnwin: upnp peerport=%d %s\n", + pRef->iPeerPort, (pRef->iPeerPort == NETCONN_DEFAULT_UPNP_PORT ? "(default)" : "(selected via netconnconnect param)"))); + + // make sure we aren't already connected + if (pRef->eState == ST_INIT) + { + // connecting + pRef->eState = ST_CONN; + + // discover upnp router information + if (pRef->pProtoUpnp != NULL) + { + if (pRef->iPeerPort != 0) + { + ProtoUpnpControl(pRef->pProtoUpnp, 'port', pRef->iPeerPort, 0, NULL); + ProtoUpnpControl(pRef->pProtoUpnp, 'macr', 'upnp', 0, NULL); + } + else + { + ProtoUpnpControl(pRef->pProtoUpnp, 'macr', 'dscg', 0, NULL); + } + } + } + else + { + NetPrintf(("netconnwin: NetConnConnect() ignored because already connected!\n")); + } + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnDisconnect + + \Description + Used to bring down the network connection. After calling this, it would + be necessary to call NetConnConnect to bring the connection back up or + NetConnShutdown to completely shutdown all network support. + + \Output + int32_t - negative=error, zero=success + + \Version 02/09/02 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetConnDisconnect(void) +{ + NetConnRefT *pRef = _NetConn_pRef; + + if (pRef->eState == ST_CONN) + { + // shutdown the interfaces (but leave API running) + pRef->eState = ST_INIT; + } + + // abort upnp operations + if (pRef->pProtoUpnp != NULL) + { + ProtoUpnpControl(pRef->pProtoUpnp, 'abrt', 0, 0, NULL); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetConnControl + + \Description + Set module behavior based on input selector. + + \Input iControl - input selector + \Input iValue - selector input + \Input iValue2 - selector input + \Input *pValue - selector input + \Input *pValue2 - selector input + + \Output + int32_t - selector result + + \Notes + iControl can be one of the following: + + \verbatim + snam: set DirtyCert service name + \endverbatim + + Unhandled selectors are passed through to NetConnCommonControl() + + \Version 04/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnControl(int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue, void *pValue2) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // make sure module is started before allowing any other control calls + if (pRef == NULL) + { + NetPrintf(("netconnwin: warning - calling NetConnControl() while module is not initialized\n")); + return(-1); + } + + // set dirtycert service name + if (iControl == 'snam') + { + return(DirtyCertControl('snam', 0, 0, pValue)); + } + + // pass through unhandled selectors to NetConnCommon + return(NetConnCommonControl(&pRef->Common, iControl, iValue, iValue2, pValue, pValue2)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnStatus + + \Description + Check general network connection status. Different selectors return + different status attributes. + + \Input iKind - status selector + \Input iData - (optional) selector specific + \Input *pBuf - (optional) pointer to output buffer + \Input iBufSize - (optional) size of output buffer + + \Output + int32_t - selector specific + + \Notes + iKind can be one of the following: + + \verbatim + addr: ip address of client + affn: get the thread cpu affinity setting + bbnd: broadband true or false + conn: true/false indication of whether connection in progress + ethr: mac address of adapter based on iData (0=first, otherwise=first active) (returned in pBuf), 0=success, negative=error + locl: return locality (Windows locale) for local system, ex. 'enUS' + For unrecognized locale, 'zzZZ' will be returned. + locn: return location (Windows location) for local system, ex. 'zzCA' + The language part of the return value should be ignored by the caller. + If local system has 'no location' set, 'zzZZ' will be returned. + macx: mac address of adapter based on iData (0=first, otherwise=first active) (returned in pBuf), 0=success, negative=error + onln: true/false indication of whether network is operational + open: true/false indication of whether network code running + type: NETCONN_IFTYPE_* + upnp: return protoupnp port info, if available + vers: return DirtySDK version + \endverbatim + + Unhandled selectors are passed through to NetConnCommonStatus() + + \Version 03/10/2001 (gschaefer) +*/ +/*************************************************************************************************F*/ +int32_t NetConnStatus(int32_t iKind, int32_t iData, void *pBuf, int32_t iBufSize) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // see if network code is initialized + if (iKind == 'open') + { + return(pRef != NULL); + } + // return DirtySDK version + if (iKind == 'vers') + { + return(DIRTYSDK_VERSION); + } + + // make sure module is started before allowing any other status calls + if (pRef == NULL) + { + NetPrintf(("netconn: warning - calling NetConnStatus() while module is not initialized\n")); + return(-1); + } + + // return client ip address + if (iKind == 'addr') + { + return(SocketGetLocalAddr()); + } + + // return the thread cpu affinity + if (iKind == 'affn') + { + return(pRef->iThreadCpuAffinity); + } + + // return whether we are broadband or not + if (iKind == 'bbnd') + { + // assume broadband + return(TRUE); + } + + // return connection status + if (iKind == 'conn') + { + return((pRef->eState == ST_CONN) ? '+onl' : '-dsc'); + } + + // return host mac address + if ((iKind == 'ethr') || (iKind == 'macx')) + { + if (pBuf != NULL) + { + // clear out the buffer + ds_memclr(pBuf, iBufSize); + + // find a valid MAC address + return(_NetConnFindMacAddress(pBuf, iBufSize, iData == 0 ? FALSE : TRUE)); + } + } + + // see if connected to ISP/LAN + if (iKind == 'onln') + { + return(pRef->eState == ST_CONN); + } + + // return what type of interface we are connected with + if (iKind == 'type') + { + // assume broadband + return(NETCONN_IFTYPE_ETHER); + } + + // return upnp addportmap info, if available + if (iKind == 'upnp') + { + // if protoupnp is available, and we've added a port map, return the external port for the port mapping + if ((pRef->pProtoUpnp != NULL) && (ProtoUpnpStatus(pRef->pProtoUpnp, 'stat', NULL, 0) & PROTOUPNP_STATUS_ADDPORTMAP)) + { + return(ProtoUpnpStatus(pRef->pProtoUpnp, 'extp', NULL, 0)); + } + } + + // return Windows locale information + if (iKind == 'locl') + { + return(_NetConnGetLocaleInfo(GetUserDefaultLangID())); + } + + // return Windows location information + if (iKind == 'locn') + { + GEOID iGeoID; + char strCountry[8]; // ISO2, [8] is enough + uint16_t uLanguage = LOBBYAPI_LANGUAGE_UNKNOWN, // language is always set to zz + uCountry = LOBBYAPI_COUNTRY_UNKNOWN; // default to ZZ + + // check if local system has location set + if ((iGeoID = GetUserGeoID(GEOCLASS_NATION)) == GEOID_NOT_AVAILABLE) + { + return(LOBBYAPI_LOCALITY_UNKNOWN); + } + + ds_memclr(strCountry, sizeof(strCountry)); + if ((GetGeoInfoA(iGeoID, GEO_ISO2, strCountry, sizeof(strCountry), 0) > 0) + && (strlen(strCountry) == sizeof(uint16_t)) + && (ds_stricmp(strCountry, NETCONNWIN_COUNTRY_UNKNOWN_STR) != 0)) + { + // a valid country code: 2-letter, and not equal to XX + uCountry = LOBBYAPI_LocalizerTokenGetShortFromString(strCountry); + } + + return(LOBBYAPI_LocalizerTokenCreate(uLanguage, uCountry)); + } + + // pass unrecognized options to NetConnCommon + return(NetConnCommonStatus(&pRef->Common, iKind, iData, pBuf, iBufSize)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnShutdown + + \Description + Shutdown the network code and return to idle state. + + \Input bStopEE - ignored in PC implementation + + \Output + negative=error, zero=success + + \Version 3/10/01 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetConnShutdown(uint32_t bStopEE) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // error see if already stopped + if (pRef == NULL) + { + return(NETCONN_ERROR_NOTACTIVE); + } + + return(_NetConnShutdownInternal(pRef, bStopEE)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetConnSleep + + \Description + Sleep the application (yield thread) for some number of milliseconds. + + \Input iMilliSecs - number of milliseconds to block for + + \Output + None + + \Version 04/30/02 (GWS) +*/ +/*************************************************************************************************F*/ +void NetConnSleep(int32_t iMilliSecs) +{ + Sleep(iMilliSecs); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtyerrunix.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtyerrunix.c new file mode 100644 index 00000000..87f12252 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtyerrunix.c @@ -0,0 +1,364 @@ +/*H********************************************************************************/ +/*! + \File dirtyerrunix.c + + \Description + Dirtysock debug error routines. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 10/04/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include // included so we know where to find network headers +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyerr.h" + +#if defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) +#include +#include +#endif + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +#if DIRTYSOCK_ERRORNAMES +static DirtyErrT _DirtyErr_List[] = +{ + DIRTYSOCK_ErrorName(EPERM), // 1 Operation not permitted + DIRTYSOCK_ErrorName(ENOENT), // 2 No such file or directory + DIRTYSOCK_ErrorName(ESRCH), // 3 No such process + DIRTYSOCK_ErrorName(EINTR), // 4 Interrupted system call + DIRTYSOCK_ErrorName(EIO), // 5 I/O error + DIRTYSOCK_ErrorName(ENXIO), // 6 No such device or address + DIRTYSOCK_ErrorName(E2BIG), // 7 Arg list too long + DIRTYSOCK_ErrorName(ENOEXEC), // 8 Exec format error + DIRTYSOCK_ErrorName(EBADF), // 9 Bad file number + DIRTYSOCK_ErrorName(ECHILD), // 10 No child processes + //DIRTYSOCK_ErrorName(EAGAIN), // 11 Try again + DIRTYSOCK_ErrorName(EWOULDBLOCK), // 11 Operation would block + DIRTYSOCK_ErrorName(ENOMEM), // 12 Out of memory + DIRTYSOCK_ErrorName(EACCES), // 13 Permission denied + DIRTYSOCK_ErrorName(EFAULT), // 14 Bad address + DIRTYSOCK_ErrorName(ENOTBLK), // 15 Block device required + DIRTYSOCK_ErrorName(EBUSY), // 16 Device or resource busy + DIRTYSOCK_ErrorName(EEXIST), // 17 File exists + DIRTYSOCK_ErrorName(EXDEV), // 18 Cross-device link + DIRTYSOCK_ErrorName(ENODEV), // 19 No such device + DIRTYSOCK_ErrorName(ENOTDIR), // 20 Not a directory + DIRTYSOCK_ErrorName(EISDIR), // 21 Is a directory + DIRTYSOCK_ErrorName(EINVAL), // 22 Invalid argument + DIRTYSOCK_ErrorName(ENFILE), // 23 File table overflow + DIRTYSOCK_ErrorName(EMFILE), // 24 Too many open files + DIRTYSOCK_ErrorName(ENOTTY), // 25 Not a typewriter + DIRTYSOCK_ErrorName(ETXTBSY), // 26 Text file busy + DIRTYSOCK_ErrorName(EFBIG), // 27 File too large + DIRTYSOCK_ErrorName(ENOSPC), // 28 No space left on device + DIRTYSOCK_ErrorName(ESPIPE), // 29 Illegal seek + DIRTYSOCK_ErrorName(EROFS), // 30 Read-only file system + DIRTYSOCK_ErrorName(EMLINK), // 31 Too many links + DIRTYSOCK_ErrorName(EPIPE), // 32 Broken pipe + DIRTYSOCK_ErrorName(EDOM), // 33 Math argument out of domain of func + DIRTYSOCK_ErrorName(ERANGE), // 34 Math result not representable + DIRTYSOCK_ErrorName(EDEADLK), // 35 Resource deadlock would occur + DIRTYSOCK_ErrorName(ENAMETOOLONG), // 36 File name too long + DIRTYSOCK_ErrorName(ENOLCK), // 37 No record locks available + DIRTYSOCK_ErrorName(ENOSYS), // 38 Function not implemented + DIRTYSOCK_ErrorName(ENOTEMPTY), // 39 Directory not empty + DIRTYSOCK_ErrorName(ELOOP), // 40 Too many symbolic links encountered + DIRTYSOCK_ErrorName(ENOMSG), // 42 No message of desired type + DIRTYSOCK_ErrorName(EIDRM), // 43 Identifier removed +#if defined(DIRTYCODE_LINUX) + DIRTYSOCK_ErrorName(ECHRNG), // 44 Channel number out of range + DIRTYSOCK_ErrorName(EL2NSYNC), // 45 Level 2 not synchronized + DIRTYSOCK_ErrorName(EL3HLT), // 46 Level 3 halted + DIRTYSOCK_ErrorName(EL3RST), // 47 Level 3 reset + DIRTYSOCK_ErrorName(ELNRNG), // 48 Link number out of range + DIRTYSOCK_ErrorName(EUNATCH), // 49 Protocol driver not attached + DIRTYSOCK_ErrorName(ENOCSI), // 50 No CSI structure available + DIRTYSOCK_ErrorName(EL2HLT), // 51 Level 2 halted + DIRTYSOCK_ErrorName(EBADE), // 52 Invalid exchange + DIRTYSOCK_ErrorName(EBADR), // 53 Invalid request descriptor + DIRTYSOCK_ErrorName(EXFULL), // 54 Exchange full + DIRTYSOCK_ErrorName(ENOANO), // 55 No anode + DIRTYSOCK_ErrorName(EBADRQC), // 56 Invalid request code + DIRTYSOCK_ErrorName(EBADSLT), // 57 Invalid slot + DIRTYSOCK_ErrorName(EDEADLOCK), // 58 File locking deadlock error + DIRTYSOCK_ErrorName(EBFONT), // 59 Bad font file format +#endif + DIRTYSOCK_ErrorName(ENOSTR), // 60 Device not a stream + DIRTYSOCK_ErrorName(ENODATA), // 61 No data available + DIRTYSOCK_ErrorName(ETIME), // 62 Timer expired + DIRTYSOCK_ErrorName(ENOSR), // 63 Out of streams resources +#if defined(DIRTYCODE_LINUX) + DIRTYSOCK_ErrorName(ENONET), // 64 Machine is not on the network + DIRTYSOCK_ErrorName(ENOPKG), // 65 Package not installed +#endif + DIRTYSOCK_ErrorName(EREMOTE), // 66 Object is remote + DIRTYSOCK_ErrorName(ENOLINK), // 67 Link has been severed +#if defined(DIRTYCODE_LINUX) + DIRTYSOCK_ErrorName(EADV), // 68 Advertise error + DIRTYSOCK_ErrorName(ESRMNT), // 69 Srmount error + DIRTYSOCK_ErrorName(ECOMM), // 70 Communication error on send +#endif + DIRTYSOCK_ErrorName(EPROTO), // 71 Protocol error + DIRTYSOCK_ErrorName(EMULTIHOP), // 72 Multihop attempted +#if defined(DIRTYCODE_LINUX) + DIRTYSOCK_ErrorName(EDOTDOT), // 73 RFS specific error +#endif + DIRTYSOCK_ErrorName(EBADMSG), // 74 Not a data message + DIRTYSOCK_ErrorName(EOVERFLOW), // 75 Value too large for defined data type +#if defined(DIRTYCODE_LINUX) + DIRTYSOCK_ErrorName(ENOTUNIQ), // 76 Name not unique on network + DIRTYSOCK_ErrorName(EBADFD), // 77 File descriptor in bad state + DIRTYSOCK_ErrorName(EREMCHG), // 78 Remote address changed + DIRTYSOCK_ErrorName(ELIBACC), // 79 Can not access a needed shared library + DIRTYSOCK_ErrorName(ELIBBAD), // 80 Accessing a corrupted shared library + DIRTYSOCK_ErrorName(ELIBSCN), // 81 .lib section in a.out corrupted + DIRTYSOCK_ErrorName(ELIBMAX), // 82 Attempting to link in too many shared libraries + DIRTYSOCK_ErrorName(ELIBEXEC), // 83 Cannot exec a shared library directly +#endif + DIRTYSOCK_ErrorName(EILSEQ), // 84 Illegal byte sequence +#if defined(DIRTYCODE_LINUX) + DIRTYSOCK_ErrorName(ERESTART), // 85 Interrupted system call should be restarted + DIRTYSOCK_ErrorName(ESTRPIPE), // 86 Streams pipe error +#endif + DIRTYSOCK_ErrorName(EUSERS), // 87 Too many users + DIRTYSOCK_ErrorName(ENOTSOCK), // 88 Socket operation on non-socket + DIRTYSOCK_ErrorName(EDESTADDRREQ), // 89 Destination address required + DIRTYSOCK_ErrorName(EMSGSIZE), // 90 Message too long + DIRTYSOCK_ErrorName(EPROTOTYPE), // 91 Protocol wrong type for socket + DIRTYSOCK_ErrorName(ENOPROTOOPT), // 92 Protocol not available + DIRTYSOCK_ErrorName(EPROTONOSUPPORT), // 93 Protocol not supported + DIRTYSOCK_ErrorName(ESOCKTNOSUPPORT), // 94 Socket type not supported + DIRTYSOCK_ErrorName(EOPNOTSUPP), // 95 Operation not supported on transport endpoint + DIRTYSOCK_ErrorName(EPFNOSUPPORT), // 96 Protocol family not supported + DIRTYSOCK_ErrorName(EAFNOSUPPORT), // 97 Address family not supported by protocol + DIRTYSOCK_ErrorName(EADDRINUSE), // 98 Address already in use + DIRTYSOCK_ErrorName(EADDRNOTAVAIL), // 99 Cannot assign requested address + DIRTYSOCK_ErrorName(ENETDOWN), // 100 Network is down + DIRTYSOCK_ErrorName(ENETUNREACH), // 101 Network is unreachable + DIRTYSOCK_ErrorName(ENETRESET), // 102 Network dropped connection because of reset + DIRTYSOCK_ErrorName(ECONNABORTED), // 103 Software caused connection abort + DIRTYSOCK_ErrorName(ECONNRESET), // 104 Connection reset by peer + DIRTYSOCK_ErrorName(ENOBUFS), // 105 No buffer space available + DIRTYSOCK_ErrorName(EISCONN), // 106 Transport endpoint is already connected + DIRTYSOCK_ErrorName(ENOTCONN), // 107 Transport endpoint is not connected + DIRTYSOCK_ErrorName(ESHUTDOWN), // 108 Cannot send after transport endpoint shutdown + DIRTYSOCK_ErrorName(ETOOMANYREFS), // 109 Too many references: cannot splice + DIRTYSOCK_ErrorName(ETIMEDOUT), // 110 Connection timed out + DIRTYSOCK_ErrorName(ECONNREFUSED), // 111 Connection refused + DIRTYSOCK_ErrorName(EHOSTDOWN), // 112 Host is down + DIRTYSOCK_ErrorName(EHOSTUNREACH), // 113 No route to host + DIRTYSOCK_ErrorName(EALREADY), // 114 Operation already in progress + DIRTYSOCK_ErrorName(EINPROGRESS), // 115 Operation now in progress + DIRTYSOCK_ErrorName(ESTALE), // 116 Stale NFS file handle +#if defined(DIRTYCODE_LINUX) + DIRTYSOCK_ErrorName(EUCLEAN), // 117 Structure needs cleaning + DIRTYSOCK_ErrorName(ENOTNAM), // 118 Not a XENIX named type file + DIRTYSOCK_ErrorName(ENAVAIL), // 119 No XENIX semaphores available + DIRTYSOCK_ErrorName(EISNAM), // 120 Is a named type file + DIRTYSOCK_ErrorName(EREMOTEIO), // 121 Remote I/O error +#endif + DIRTYSOCK_ErrorName(EDQUOT), // 122 Quota exceeded + +#if defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) + DIRTYSOCK_ErrorName(errSecUnimplemented), // -4 Function or operation not implemented. + DIRTYSOCK_ErrorName(errSecIO), // -35 I/O error (bummers) + DIRTYSOCK_ErrorName(errSecParam), // -50 One or more parameters passed to a function were not valid. + DIRTYSOCK_ErrorName(errSecAllocate), // -108 Failed to allocate memory. + DIRTYSOCK_ErrorName(errSecUserCanceled), // -128 User canceled the operation. + DIRTYSOCK_ErrorName(errSecBadReq), // -909 Bad parameter or invalid state for operation. + + DIRTYSOCK_ErrorName(errSSLProtocol), // -9800 SSL protocol error + DIRTYSOCK_ErrorName(errSSLNegotiation), // -9801 Cipher Suite negotiation failure + DIRTYSOCK_ErrorName(errSSLFatalAlert), // -9802 Fatal alert + DIRTYSOCK_ErrorName(errSSLWouldBlock), // -9803 I/O would block (not fatal) + DIRTYSOCK_ErrorName(errSSLSessionNotFound), // -9804 attempt to restore an unknown session + DIRTYSOCK_ErrorName(errSSLClosedGraceful), // -9805 connection closed gracefully + DIRTYSOCK_ErrorName(errSSLClosedAbort), // -9806 connection closed via error + DIRTYSOCK_ErrorName(errSSLXCertChainInvalid), // -9806 invalid certificate chain + DIRTYSOCK_ErrorName(errSSLBadCert), // -9808 bad certificate format + DIRTYSOCK_ErrorName(errSSLCrypto), // -9809 underlying cryptographic error + DIRTYSOCK_ErrorName(errSSLInternal), // -9810 Internal error + DIRTYSOCK_ErrorName(errSSLModuleAttach), // -9811 module attach failure + DIRTYSOCK_ErrorName(errSSLUnknownRootCert), // -9812 valid cert chain, untrusted root + DIRTYSOCK_ErrorName(errSSLNoRootCert), // -9813 cert chain not verified by root + DIRTYSOCK_ErrorName(errSSLCertExpired), // -9814 chain had an expired cert + DIRTYSOCK_ErrorName(errSSLCertNotYetValid), // -9815 chain had a cert not yet valid + DIRTYSOCK_ErrorName(errSSLClosedNoNotify), // -9816 server closed session with no notification + DIRTYSOCK_ErrorName(errSSLBufferOverflow), // -9817 insufficient buffer provided + DIRTYSOCK_ErrorName(errSSLBadCipherSuite), // -9818 bad SSLCipherSuite + + DIRTYSOCK_ErrorName(errSSLPeerUnexpectedMsg), // -9819 unexpected message received + DIRTYSOCK_ErrorName(errSSLPeerBadRecordMac), // -9820 bad MAC + DIRTYSOCK_ErrorName(errSSLPeerDecryptionFail), // -9821 decryption failed + DIRTYSOCK_ErrorName(errSSLPeerRecordOverflow), // -9822 record overflow + DIRTYSOCK_ErrorName(errSSLPeerDecompressFail), // -9823 decompression failure + DIRTYSOCK_ErrorName(errSSLPeerHandshakeFail), // -9824 handshake failure + DIRTYSOCK_ErrorName(errSSLPeerBadCert), // -9825 misc. bad certificate + DIRTYSOCK_ErrorName(errSSLPeerUnsupportedCert), // -9826 bad unsupported cert format + DIRTYSOCK_ErrorName(errSSLPeerCertRevoked), // -9827 certificate revoked + DIRTYSOCK_ErrorName(errSSLPeerCertExpired), // -9828 certificate expired + DIRTYSOCK_ErrorName(errSSLPeerCertUnknown), // -9829 unknown certificate + DIRTYSOCK_ErrorName(errSSLIllegalParam), // -9830 illegal parameter + DIRTYSOCK_ErrorName(errSSLPeerUnknownCA), // -9831 unknown Cert Authority + DIRTYSOCK_ErrorName(errSSLPeerAccessDenied), // -9832 access denied + DIRTYSOCK_ErrorName(errSSLPeerDecodeError), // -9833 decoding error + DIRTYSOCK_ErrorName(errSSLPeerDecryptError), // -9834 decryption error + DIRTYSOCK_ErrorName(errSSLPeerExportRestriction), // -9835 export restriction + DIRTYSOCK_ErrorName(errSSLPeerProtocolVersion), // -9836 bad protocol version + DIRTYSOCK_ErrorName(errSSLPeerInsufficientSecurity),// -9837 insufficient security + DIRTYSOCK_ErrorName(errSSLPeerInternalError), // -9838 internal error + DIRTYSOCK_ErrorName(errSSLPeerUserCancelled), // -9839 user canceled + DIRTYSOCK_ErrorName(errSSLPeerNoRenegotiation), // -9840 no renegotiation allowed + + DIRTYSOCK_ErrorName(errSSLPeerAuthCompleted), // -9841 peer cert is valid, or was ignored if verification disabled + DIRTYSOCK_ErrorName(errSSLClientCertRequested), // -9842 server has requested a client cert + + DIRTYSOCK_ErrorName(errSSLHostNameMismatch), // -9843 peer host name mismatch + DIRTYSOCK_ErrorName(errSSLConnectionRefused), // -9844 peer dropped connection before responding + DIRTYSOCK_ErrorName(errSSLDecryptionFail), // -9845 decryption failure + DIRTYSOCK_ErrorName(errSSLBadRecordMac), // -9846 bad MAC + DIRTYSOCK_ErrorName(errSSLRecordOverflow), // -9847 record overflow + DIRTYSOCK_ErrorName(errSSLBadConfiguration), // -9848 configuration error + DIRTYSOCK_ErrorName(errSSLUnexpectedRecord), // -9849 unexpected (skipped) record in DTLS + DIRTYSOCK_ErrorName(errSSLWeakPeerEphemeralDHKey), // -9850 weak ephemeral dh key + + DIRTYSOCK_ErrorName(errSSLClientHelloReceived), // -9851 SNI + + DIRTYSOCK_ErrorName(errSecNotAvailable), // -25291 No keychain is available. You may need to restart your computer. + DIRTYSOCK_ErrorName(errSecAuthFailed), // -25293 The user name or passphrase you entered is not correct. + DIRTYSOCK_ErrorName(errSecDuplicateItem), // -25299 The specified item already exists in the keychain. + DIRTYSOCK_ErrorName(errSecItemNotFound), // -25300 The specified item could not be found in the keychain. + DIRTYSOCK_ErrorName(errSecInteractionNotAllowed), // -25308 User interaction is not allowed. + DIRTYSOCK_ErrorName(errSecDecode), // -26275 Unable to decode the provided data. + + DIRTYSOCK_ErrorName(errSecVerifyFailed), // -67808 A cryptographic verification failure has occured. +#endif + + // NULL terminate + DIRTYSOCK_ListEnd() +}; +#endif + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function DirtyErrNameList + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized or formats it as a hex + number if not. + + \Input *pBuffer - [out] pointer to output buffer to store result + \Input iBufSize - size of output buffer + \Input uError - error code to format + \Input *pList - error list to use + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyErrNameList(char *pBuffer, int32_t iBufSize, uint32_t uError, const DirtyErrT *pList) +{ + #if DIRTYSOCK_ERRORNAMES + int32_t iErr; + + for (iErr = 0; pList[iErr].uError != DIRTYSOCK_LISTTERM; iErr++) + { + if (pList[iErr].uError == uError) + { + ds_strnzcpy(pBuffer, pList[iErr].pErrorName, iBufSize); + return; + } + } + #endif + + ds_snzprintf(pBuffer, iBufSize, "0x%08x", uError); +} + +/*F********************************************************************************/ +/*! + \Function DirtyErrName + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized or formats it as a hex + number if not. + + \Input *pBuffer - [out] pointer to output buffer to store result + \Input iBufSize - size of output buffer + \Input uError - error code to format + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyErrName(char *pBuffer, int32_t iBufSize, uint32_t uError) +{ + #if DIRTYSOCK_ERRORNAMES + DirtyErrNameList(pBuffer, iBufSize, uError, _DirtyErr_List); + #endif +} + +/*F********************************************************************************/ +/*! + \Function DirtyErrGetNameList + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized or formats it as a hex + number if not. + + \Input uError - error code to format + \Input *pList - error list to use + + \Output + const char *- pointer to error name or error formatted in hex + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +const char *DirtyErrGetNameList(uint32_t uError, const DirtyErrT *pList) +{ + static char strName[8][64]; + static uint8_t uLast = 0; + char *pName = strName[uLast++]; + if (uLast > 7) uLast = 0; + DirtyErrNameList(pName, sizeof(strName[0]), uError, pList); + return(pName); +} + +/*F********************************************************************************/ +/*! + \Function DirtyErrGetName + + \Description + This function takes as input a system-specific error code, and either + resolves it to its define name if it is recognized or formats it as a hex + number if not. + + \Input uError - error code to format + + \Output + const char *- pointer to error name or error formatted in hex + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +const char *DirtyErrGetName(uint32_t uError) +{ + static char strName[64]; + DirtyErrName(strName, sizeof(strName), uError); + return(strName); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtylibunix.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtylibunix.c new file mode 100644 index 00000000..67635321 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtylibunix.c @@ -0,0 +1,360 @@ +/*H********************************************************************************/ +/*! + \File dirtylibunix.c + + \Description + Platform-specific support library for network code. Supplies simple time, + debug printing, and mutex functions. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 10/04/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtythread.h" +#include "DirtySDK/dirtysock/netconn.h" + +/*** Defines **********************************************************************/ + + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +// idle thread state +static volatile int32_t g_idlelife = -1; +uint8_t _NetLib_bSingleThreaded = FALSE; + +#if DIRTYCODE_LOGGING +// static printf buffer to avoid using dynamic allocation +static char _NetLib_aPrintfMem[4096]; +#endif + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _NetLibThread + + \Description + Thread to handle special library tasks. + + \Input *pArg - pointer to argument + + \Version 06/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _NetLibThread(void *pArg) +{ + char strThreadId[32]; + + // get the thread id + DirtyThreadGetThreadId(strThreadId, sizeof(strThreadId)); + + // show we are running + NetPrintf(("dirtylibunix: idle thread running (thid=%s)\n", strThreadId)); + g_idlelife = 1; + + // run while we have sema + while (g_idlelife == 1) + { + // call idle functions + NetIdleCall(); + + // wait for next tick + usleep(50*1000); + } + + // report termination + NetPrintf(("dirtylibunix: idle thread exiting\n")); + + // show we are dead + g_idlelife = 0; +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function NetLibCreate + + \Description + Initialize the network library module. + + \Input iThreadPrio - priority to start the _NetLibThread with + \Input iThreadStackSize - stack size to start the _NetLibThread with (in bytes) + \Input iThreadCpuAffinity - cpu affinity to start the _NetLibThread with + + \Version 06/21/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void NetLibCreate(int32_t iThreadPrio, int32_t iThreadStackSize, int32_t iThreadCpuAffinity) +{ + int32_t iResult; + + if (iThreadPrio < 0) + { + NetPrintf(("dirtylibunix: starting in single-threaded mode\n")); + _NetLib_bSingleThreaded = TRUE; + } + + // init common functionality + NetLibCommonInit(); + + // init idlelife tracker + g_idlelife = -1; + + // create a worker thread + if (!_NetLib_bSingleThreaded) + { + DirtyThreadConfigT ThreadConfig; + + // configure the threading + ds_memclr(&ThreadConfig, sizeof(ThreadConfig)); + ThreadConfig.pName = "NetLib"; + ThreadConfig.iAffinity = iThreadCpuAffinity; + ThreadConfig.iPriority = iThreadPrio; + ThreadConfig.iVerbosity = 1; + + if ((iResult = DirtyThreadCreate(_NetLibThread, NULL, &ThreadConfig)) == 0) + { + // wait for thread startup + while (g_idlelife == -1) + { + usleep(100); + } + } + else + { + NetPrintf(("dirtylibunix: unable to create idle thread (err=%d)\n", iResult)); + g_idlelife = 0; + } + + } + + #if DIRTYCODE_LOGGING + // set explicit printf buffer to avoid dynamic malloc() use + setvbuf(stdout, _NetLib_aPrintfMem, _IOLBF, sizeof(_NetLib_aPrintfMem)); + #endif +} + +/*F********************************************************************************/ +/*! + \Function NetLibDestroy + + \Description + Destroy the network library module. + + \Input uShutdownFlags - NET_SHUTDOWN_* flags + + \Version 06/21/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void NetLibDestroy(uint32_t uShutdownFlags) +{ + // signal a shutdown when the thread is running + if ((!_NetLib_bSingleThreaded) && (g_idlelife == 1)) + { + g_idlelife = 2; + + // wait for thread to terminate + while (g_idlelife > 0) + { + usleep(1); + } + } + + // shut down common functionality + NetLibCommonShutdown(); +} + +/*F********************************************************************************/ +/*! + \Function NetTick + + \Description + Return some kind of increasing tick count with millisecond scale (does + not need to have millisecond precision, but higher precision is better). + + \Output + uint32_t - millisecond tick count + + \Version 06/21/2006 (jbrookes) +*/ +/********************************************************************************F*/ +uint32_t NetTick(void) +{ + uint32_t uCurTick; + struct timeval now; + gettimeofday(&now, 0); + uCurTick = (uint32_t)(((uint64_t)now.tv_sec*1000) + (uint64_t)now.tv_usec/1000); + return(uCurTick); +} + +/*F********************************************************************************/ +/*! + \Function NetTickUsec + + \Description + Return increasing tick count in microseconds. Used for performance timing + purposes. + + \Output + uint64_t - microsecond tick count + + \Version 01/30/2015 (jbrookes) +*/ +/********************************************************************************F*/ +uint64_t NetTickUsec(void) +{ + struct timeval now; + gettimeofday(&now, 0); + return(((uint64_t)now.tv_sec*1000000) + (uint64_t)now.tv_usec); +} + +/*F********************************************************************************/ +/*! + \Function NetLocalTime + + \Description + This converts the input GMT time to the local time as specified by the + system clock. This function follows the re-entrant localtime_r function + signature. + + \Input *pTm - storage for localtime output + \Input uElap - GMT time + + \Output + struct tm * - pointer to localtime result + + \Version 04/23/2008 (jbrookes) +*/ +/********************************************************************************F*/ +struct tm *NetLocalTime(struct tm *pTm, uint64_t uElap) +{ + time_t uTimeT = (time_t)uElap; + return(localtime_r(&uTimeT, pTm)); +} + +/*F********************************************************************************/ +/*! + \Function NetPlattimeToTime + + \Description + This converts the input platform-specific time data structure to the + generic time data structure. + + \Input *pTm - generic time data structure to be filled by the function + \Input *pPlatTime - pointer to the platform-specific data structure + + \Output + struct tm * - NULL=failure; else pointer to user-provided generic time data structure + + \Notes + pPlatTime is expected to point to a timespec on Unix platforms + + \Version 05/08/2010 (mclouatre) +*/ +/********************************************************************************F*/ +struct tm *NetPlattimeToTime(struct tm *pTm, void *pPlatTime) +{ + #if defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_ANDROID) + struct timespec timeSpec = *(struct timespec *)pPlatTime; + localtime_r(&timeSpec.tv_sec, pTm); + #elif defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) + struct timeval timeVal = *(struct timeval *)pPlatTime; + localtime_r(&timeVal.tv_sec, pTm); + #else + pTm = NULL; + #endif + + return(pTm); +} + +/*F********************************************************************************/ +/*! + \Function NetPlattimeToTimeMs + + \Description + This function retrieves the current date time and fills in the + generic time data structure prrovided. It has the option of returning millisecond + which is not part of the generic time data structure + + \Input *pTm - generic time data structure to be filled by the function + \Input *pImSec - output param for milisecond to be filled by the function (optional can be NULL) + + \Output + struct tm * - NULL=failure; else pointer to user-provided generic time data structure + + \Version 09/16/2014 (tcho) +*/ +/********************************************************************************F*/ +struct tm *NetPlattimeToTimeMs(struct tm *pTm , int32_t *pImSec) +{ + void *pPlatTime; + int32_t iMsec; + + #if defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_ANDROID) + struct timespec timeSpec; + clock_gettime(CLOCK_REALTIME, &timeSpec); + iMsec = timeSpec.tv_nsec/1000000; + pPlatTime = (void *)&timeSpec; + #elif defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) + struct timeval timeVal; + gettimeofday(&timeVal, NULL); + + iMsec = timeVal.tv_usec/1000; + pPlatTime = (void*)&timeVal; + #else + return (NULL); + #endif + + if (pImSec != NULL) + { + *pImSec = iMsec; + } + + if (pTm == NULL) + { + return(NULL); + } + + return(NetPlattimeToTime(pTm, pPlatTime)); +} + +/*F********************************************************************************/ +/*! + \Function NetTime + + \Description + This function replaces the standard library time() function. Main + differences are the missing pointer parameter (not needed) and the uint64_t + return value. The function returns 0 on unsupported platforms vs time which + returns -1. + + \Output + uint64_t - number of elapsed seconds since Jan 1, 1970. + + \Version 01/12/2005 (gschaefer) +*/ +/********************************************************************************F*/ +uint64_t NetTime(void) +{ + return((uint64_t)time(NULL)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtynetunix.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtynetunix.c new file mode 100644 index 00000000..c0c2332a --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/dirtynetunix.c @@ -0,0 +1,3513 @@ +/*H********************************************************************************/ +/*! + \File dirtynetunix.c + + \Description + Provides a wrapper that translates the Unix network interface to the + DirtySock portable networking interface. + + \Copyright + Copyright (c) 2010 Electronic Arts Inc. + + \Version 04/05/2010 (jbrookes) First version; a vanilla port to Linux from PS3 +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DirtySDK/platform.h" + +#if defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) + #include + #include + #include + #include +#else + #include + #include // struct ifreq + #include // ioctl +#endif + +#if defined(DIRTYCODE_APPLEIOS) + #include + #include + #include + #include +#endif + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtythread.h" +#include "DirtySDK/dirtysock/netconn.h" + +#include "dirtynetpriv.h" // private include for dirtynet common functions + +/*** Defines **********************************************************************/ + +#define INVALID_SOCKET (-1) + +#define SOCKET_MAXPOLL (32) + +#define SOCKET_VERBOSE (DIRTYCODE_DEBUG && FALSE) + +/*** Type Definitions *************************************************************/ + +//! private socketlookup structure containing extra data +typedef struct SocketLookupPrivT +{ + HostentT Host; //!< must come first! +} SocketLookupPrivT; + +//! dirtysock connection socket structure +struct SocketT +{ + SocketT *pNext; //!< link to next active + SocketT *pKill; //!< link to next killed socket + + int32_t iFamily; //!< protocol family + int32_t iType; //!< protocol type + int32_t iProto; //!< protocol ident + + int8_t iOpened; //!< negative=error, zero=not open (connecting), positive=open + uint8_t bImported; //!< whether socket was imported or not + uint8_t bVirtual; //!< if true, socket is virtual + uint8_t bHasData; //!< zero if no data, else has data ready to be read + uint8_t bInCallback; //!< in a socket callback + uint8_t uBrokenFlag; //!< 0 or 1=might not be broken, >1=broken socket + uint8_t bAsyncRecv; //!< if true, async recv is enabled + uint8_t bSendCbs; //!< TRUE if send cbs are enabled, false otherwise + int8_t iVerbose; //!< debug level + uint8_t __pad[3]; + + int32_t uSocket; //!< unix socket ref + int32_t iLastError; //!< last socket error + + struct sockaddr LocalAddr; //!< local address + struct sockaddr RemoteAddr; //!< remote address + + uint16_t uVirtualPort; //!< virtual port, if set + uint16_t uPollIdx; //!< index in blocking poll() operation + + SocketRateT SendRate; //!< send rate estimation data + SocketRateT RecvRate; //!< recv rate estimation data + + int32_t iCallMask; //!< valid callback events + uint32_t uCallLast; //!< last callback tick + uint32_t uCallIdle; //!< ticks between idle calls + void *pCallRef; //!< reference calback value + int32_t (*pCallback)(SocketT *pSocket, int32_t iFlags, void *pRef); + + NetCritT RecvCrit; //!< receive critical section + int32_t iRecvErr; //!< last error that occurred + uint32_t uRecvFlag; //!< flags from recv operation + int32_t iRbufSize; //!< read buffer size (bytes) + int32_t iSbufSize; //!< send buffer size (bytes) + + struct sockaddr RecvAddr; //!< receive address + + struct sockaddr_in6 RecvAddr6; //!< receive address (ipv6) + + SocketPacketQueueT *pRecvQueue; + SocketPacketQueueEntryT *pRecvPacket; +}; + +//! standard ipv4 packet header (see RFC791) +typedef struct HeaderIpv4 +{ + uint8_t verslen; //!< version and length fields (4 bits each) + uint8_t service; //!< type of service field + uint8_t length[2]; //!< total packet length (header+data) + uint8_t ident[2]; //!< packet sequence number + uint8_t frag[2]; //!< fragmentation information + uint8_t time; //!< time to live (remaining hop count) + uint8_t proto; //!< transport protocol number + uint8_t check[2]; //!< header checksum + uint8_t srcaddr[4]; //!< source ip address + uint8_t dstaddr[4]; //!< dest ip address +} HeaderIpv4; + +//! local state +typedef struct SocketStateT +{ + SocketT *pSockList; //!< master socket list + SocketT *pSockKill; //!< list of killed sockets + HostentT *pHostList; //!< list of ongoing name resolution operations + + uint16_t aVirtualPorts[SOCKET_MAXVIRTUALPORTS]; //!< virtual port list + + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + uint32_t uConnStatus; //!< current connection status + uint32_t uLocalAddr; //!< local internet address for active interface + int32_t iMaxPacket; //!< maximum packet size + + uint8_t aMacAddr[6]; //!< MAC address for active interface + uint8_t bSingleThreaded; //!< TRUE if in single-threaded mode + int8_t iVerbose; //!< debug output verbosity + + volatile int32_t iRecvLife; + + SocketAddrMapT AddrMap; //!< address map for translating ipv6 addresses to ipv4 virtual addresses and back + + SocketHostnameCacheT *pHostnameCache; //!< hostname cache + + SocketSendCallbackEntryT aSendCbEntries[SOCKET_MAXSENDCALLBACKS]; //!< collection of send callbacks +} SocketStateT; + +/*** Variables ********************************************************************/ + +//! module state ref +static SocketStateT *_Socket_pState = NULL; + +#if DIRTYSOCK_ERRORNAMES +//! getaddrinfo() error result table +static const DirtyErrT _GAI_ErrList[] = +{ + DIRTYSOCK_ErrorName(EAI_BADFLAGS), // -1; Invalid value for 'ai_flags' field. + DIRTYSOCK_ErrorName(EAI_NONAME), // -2; NAME or SERVICE is unknown. + DIRTYSOCK_ErrorName(EAI_AGAIN), // -3; Temporary failure in name resolution. + DIRTYSOCK_ErrorName(EAI_FAIL), // -4; Non-recoverable failure in name res. + #if defined(__USE_GNU) || DIRTYCODE_ANDROID + DIRTYSOCK_ErrorName(EAI_NODATA), // -5: No address associated with NAME. + #endif + DIRTYSOCK_ErrorName(EAI_FAMILY), // -6; 'ai_family' not supported. + DIRTYSOCK_ErrorName(EAI_SOCKTYPE), // -7; 'ai_socktype' not supported. + DIRTYSOCK_ErrorName(EAI_SERVICE), // -8; SERVICE not supported for 'ai_socktype'. + #if defined(__USE_GNU) || DIRTYCODE_ANDROID + DIRTYSOCK_ErrorName(EAI_ADDRFAMILY), // -9; Address family for NAME not supported. + #endif + DIRTYSOCK_ErrorName(EAI_MEMORY), // -10; Memory allocation failure. + DIRTYSOCK_ErrorName(EAI_SYSTEM), // -11; System error returned in `errno'. + DIRTYSOCK_ErrorName(EAI_OVERFLOW), // -12; Argument buffer overflow. + #if DIRTYCODE_LINUX && defined(__USE_GNU) + DIRTYSOCK_ErrorName(EAI_INPROGRESS), // -100; Processing request in progress. + DIRTYSOCK_ErrorName(EAI_CANCELED), // -101; Request canceled. + DIRTYSOCK_ErrorName(EAI_NOTCANCELED), // -102; Request not canceled. + DIRTYSOCK_ErrorName(EAI_ALLDONE), // -103; All requests done. + DIRTYSOCK_ErrorName(EAI_INTR), // -104; Interrupted by a signal. + DIRTYSOCK_ErrorName(EAI_IDN_ENCODE), // -105; IDN encoding failed. + #endif + DIRTYSOCK_ListEnd() +}; +#endif + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _SocketTranslateError2 + + \Description + Translate a BSD error to dirtysock + + \Input iErr - BSD error code + \Input iErrno - errno or override of the value + + \Output + int32_t - dirtysock error (SOCKERR_*) + + \Version 06/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _SocketTranslateError2(int32_t iErr, int32_t iErrno) +{ + if (iErr < 0) + { + iErr = iErrno; + if ((iErr == EWOULDBLOCK) || (iErr == EINPROGRESS)) + iErr = SOCKERR_NONE; + else if (iErr == EHOSTUNREACH) + iErr = SOCKERR_UNREACH; + else if (iErr == ENOTCONN) + iErr = SOCKERR_NOTCONN; + else if (iErr == ECONNREFUSED) + iErr = SOCKERR_REFUSED; + else if (iErr == ECONNRESET) + iErr = SOCKERR_CONNRESET; + else if ((iErr == EBADF) || (iErr == EPIPE)) + iErr = SOCKERR_BADPIPE; + else + iErr = SOCKERR_OTHER; + } + return(iErr); +} + +/*F********************************************************************************/ +/*! + \Function _SocketTranslateError + + \Description + Translate a BSD error to dirtysock + + \Input iErr - BSD error code + + \Output + int32_t - dirtysock error (SOCKERR_*) + + \Version 06/19/2020 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _SocketTranslateError(int32_t iErr) +{ + return(_SocketTranslateError2(iErr, errno)); +} + +#if defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_ANDROID) +/*F********************************************************************************/ +/*! + \Function _SocketDisableSigpipe + + \Description + Disable SIGPIPE signal. + + \Version 12/08/2003 (sbevan) +*/ +/********************************************************************************F*/ +static void _SocketDisableSigpipe(void) +{ + struct sigaction sa; + ds_memclr(&sa, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGPIPE, &sa, 0); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _SocketCreateSocket + + \Description + Create a system level socket. + + \Input iAddrFamily - address family (AF_INET) + \Input iType - socket type (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, ...) + \Input iProto - protocol type for SOCK_RAW (unused by others) + + \Output + int32_t - socket handle + + \Version 16/02/2012 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _SocketCreateSocket(int32_t iAddrFamily, int32_t iType, int32_t iProto) +{ + int32_t iSocket; + // create socket + if ((iSocket = socket(iAddrFamily, iType, iProto)) >= 0) + { + const uint32_t uTrue = 1, uFalse = 0; + // if dgram, allow broadcast + if (iType == SOCK_DGRAM) + { + setsockopt(iSocket, SOL_SOCKET, SO_BROADCAST, &uTrue, sizeof(uTrue)); + } + // if a raw socket, set header include + if (iType == SOCK_RAW) + { + setsockopt(iSocket, IPPROTO_IP, IP_HDRINCL, &uTrue, sizeof(uTrue)); + } + // set nonblocking operation + if (fcntl(iSocket, F_SETFL, O_NONBLOCK) < 0) + { + NetPrintf(("dirtynetunix: error trying to make socket non-blocking (err=%d)\n", errno)); + } + // disable IPV6 only + setsockopt(iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&uFalse, sizeof(uFalse)); + + #if defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) + // ignore SIGPIPE; we ignore this signal globally, but that doesn't work on iOS/MacX + if (setsockopt(iSocket, SOL_SOCKET, SO_NOSIGPIPE, &uTrue, sizeof(uTrue)) < 0) + { + NetPrintf(("dirtynetunix: unable to set NOSIGPIPE on socket (err=%s)\n", DirtyErrGetName(errno))); + } + #endif + } + else + { + NetPrintf(("dirtynetunix: socket() failed (err=%s)\n", DirtyErrGetName(errno))); + } + return(iSocket); +} + +/*F********************************************************************************/ +/*! + \Function _SocketOpen + + \Description + Create a new transfer endpoint. A socket endpoint is required for any + data transfer operation. If iSocket != -1 then used existing socket. + + \Input iSocket - Socket descriptor to use or -1 to create new + \Input iAddrFamily - address family (AF_INET) + \Input iType - socket type (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, ...) + \Input iProto - protocol type for SOCK_RAW (unused by others) + \Input iOpened - 0=not open (connecting), 1=open + + \Output + SocketT * - socket reference + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static SocketT *_SocketOpen(int32_t iSocket, int32_t iAddrFamily, int32_t iType, int32_t iProto, int32_t iOpened) +{ + SocketStateT *pState = _Socket_pState; + const int32_t iQueueSize = (iType != SOCK_STREAM) ? 1 : 8; + SocketT *pSocket; + + // allocate memory + if ((pSocket = DirtyMemAlloc(sizeof(*pSocket), SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetunix: unable to allocate memory for socket\n")); + return(NULL); + } + ds_memclr(pSocket, sizeof(*pSocket)); + + // open a socket (force to AF_INET6 if we are opening it) + if ((iSocket == -1) && ((iSocket = _SocketCreateSocket(iAddrFamily = AF_INET6, iType, iProto)) < 0)) + { + NetPrintf(("dirtynetunix: error %s creating socket\n", DirtyErrGetName(iSocket))); + DirtyMemFree(pSocket, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(NULL); + } + + // create packet queue + if ((pSocket->pRecvQueue = SocketPacketQueueCreate(iQueueSize, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetunix: failed to create socket queue for socket\n")); + close(iSocket); + DirtyMemFree(pSocket, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(NULL); + } + + // set family/proto info + pSocket->iFamily = iAddrFamily; + pSocket->iType = iType; + pSocket->iProto = iProto; + pSocket->uSocket = iSocket; + pSocket->iOpened = iOpened; + pSocket->iLastError = SOCKERR_NONE; + pSocket->bAsyncRecv = ((pState->bSingleThreaded == FALSE) && ((iType == SOCK_DGRAM) || (iType == SOCK_RAW))) ? TRUE : FALSE; + pSocket->bSendCbs = TRUE; + pSocket->iVerbose = 1; + + // inititalize critical section + NetCritInit(&pSocket->RecvCrit, "inet-recv"); + + // install into list + NetCritEnter(NULL); + pSocket->pNext = pState->pSockList; + pState->pSockList = pSocket; + NetCritLeave(NULL); + + // return the socket + return(pSocket); +} + +/*F********************************************************************************/ +/*! + \Function _SocketReopen + + \Description + Recreate a socket endpoint. + + \Input *pSocket - socket ref + + \Output + SocketT * - socket ref on success, NULL for error + + \Notes + This function should not be called from the async/idle thread, + so socket recreation cannot happen in SocketRecvfrom. + + \Version 16/02/2012 (szhu) +*/ +/********************************************************************************F*/ +static SocketT *_SocketReopen(SocketT *pSocket) +{ + // we need recvcrit to prevent the socket from being used by the async/idle thread + // for non-virtual non-tcp sockets only + if (!pSocket->bVirtual && ((pSocket->iType == SOCK_DGRAM) || (pSocket->iType == SOCK_RAW))) + { + SocketT *pResult = NULL; + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + NetPrintf(("dirtynetunix: trying to recreate the socket handle for %x->%d\n", pSocket, pSocket->uSocket)); + + // close existing socket handle + if (pSocket->uSocket != INVALID_SOCKET) + { + close(pSocket->uSocket); + pSocket->uSocket = INVALID_SOCKET; + } + + // create socket + if ((pSocket->uSocket = _SocketCreateSocket(pSocket->iFamily, pSocket->iType, pSocket->iProto)) >= 0) + { + // set socket buffer size + if (pSocket->iRbufSize > 0) + { + SocketControl(pSocket, 'rbuf', pSocket->iRbufSize, NULL, 0); + } + if (pSocket->iSbufSize > 0) + { + SocketControl(pSocket, 'sbuf', pSocket->iSbufSize, NULL, 0); + } + // bind if previous socket was bound to a specific port + if (SockaddrInGetPort(&pSocket->LocalAddr) != 0) + { + int32_t iResult; + // always set reuseaddr flag for socket recreation + SocketControl(pSocket, 'radr', 1, NULL, 0); + // we don't call SocketBind() here (to avoid virtual socket translation) + if ((iResult = bind(pSocket->uSocket, &pSocket->LocalAddr, sizeof(pSocket->LocalAddr))) < 0) + { + pSocket->iLastError = _SocketTranslateError(iResult); + NetPrintf(("dirtynetunix: bind() to %a:%d failed (err=%s)\n", + SockaddrInGetAddr(&pSocket->LocalAddr), + SockaddrInGetPort(&pSocket->LocalAddr), + DirtyErrGetName(errno))); + } + } + // connect if previous socket was connected to a specific remote + if (SockaddrInGetPort(&pSocket->RemoteAddr) != 0) + { + struct sockaddr SockAddr; + // make a copy of remote + ds_memcpy_s(&SockAddr, sizeof(SockAddr), &pSocket->RemoteAddr, sizeof(pSocket->RemoteAddr)); + SocketConnect(pSocket, &SockAddr, sizeof(SockAddr)); + } + // success + pSocket->uBrokenFlag = 0; + pResult = pSocket; + NetPrintf(("dirtynetunix: socket recreation (%x->%d) succeeded.\n", pSocket, pSocket->uSocket)); + } + else + { + pSocket->iLastError = _SocketTranslateError(pSocket->uSocket); + NetPrintf(("dirtynetunix: socket recreation (%x) failed.\n", pSocket)); + } + + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + return(pResult); + } + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function _SocketClose + + \Description + Disposes of a SocketT, including disposal of the SocketT allocated memory. Does + NOT dispose of the unix socket ref. + + \Input *pSocket - socket to close + + \Output + int32_t - negative=error, else zero + + \Version 06/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _SocketClose(SocketT *pSocket) +{ + SocketStateT *pState = _Socket_pState; + uint32_t bSockInList; + SocketT **ppSocket; + + // remove sock from linked list + NetCritEnter(NULL); + for (ppSocket = &pState->pSockList, bSockInList = FALSE; *ppSocket != NULL; ppSocket = &(*ppSocket)->pNext) + { + if (*ppSocket == pSocket) + { + *ppSocket = pSocket->pNext; + bSockInList = TRUE; + break; + } + } + NetCritLeave(NULL); + + // make sure the socket is in the socket list (and therefore valid) + if (bSockInList == FALSE) + { + NetPrintf(("dirtynetunix: warning, trying to close socket 0x%08x that is not in the socket list\n", (intptr_t)pSocket)); + return(-1); + } + + // finish any idle call + NetIdleDone(); + + // mark as closed + pSocket->uSocket = INVALID_SOCKET; + pSocket->iOpened = FALSE; + + // kill critical section + NetCritKill(&pSocket->RecvCrit); + + // destroy packet queue + if (pSocket->pRecvQueue != NULL) + { + SocketPacketQueueDestroy(pSocket->pRecvQueue); + } + + // put into killed list + NetCritEnter(NULL); + pSocket->pKill = pState->pSockKill; + pState->pSockKill = pSocket; + NetCritLeave(NULL); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _SocketIdle + + \Description + Call idle processing code to give connections time. + + \Input *pData - pointer to socket state + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SocketIdle(void *pData) +{ + SocketStateT *pState = (SocketStateT *)pData; + SocketT *pSocket; + uint32_t uTick; + + // for access to g_socklist and g_sockkill + NetCritEnter(NULL); + + // get current tick + uTick = NetTick(); + + // walk socket list and perform any callbacks + for (pSocket = pState->pSockList; pSocket != NULL; pSocket = pSocket->pNext) + { + // see if we should do callback + if ((pSocket->uCallIdle != 0) && + (pSocket->pCallback != NULL) && + (!pSocket->bInCallback) && + (NetTickDiff(uTick, pSocket->uCallLast) > (signed)pSocket->uCallIdle)) + { + pSocket->bInCallback = TRUE; + (pSocket->pCallback)(pSocket, 0, pSocket->pCallRef); + pSocket->bInCallback = FALSE; + pSocket->uCallLast = uTick = NetTick(); + } + } + + // delete any killed sockets + while ((pSocket = pState->pSockKill) != NULL) + { + pState->pSockKill = pSocket->pKill; + DirtyMemFree(pSocket, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + + // process dns cache list, delete expired entries + SocketHostnameCacheProcess(pState->pHostnameCache, pState->iVerbose); + + // process hostname list, delete completed lookup requests + SocketHostnameListProcess(&pState->pHostList, pState->iMemGroup, pState->pMemGroupUserData); + + // for access to g_socklist and g_sockkill + NetCritLeave(NULL); +} + +/*F*************************************************************************************/ +/*! + \Function _SocketRecvfromPacketQueue + + \Description + Check if there is a pending inbound packet in the socket packet queue. + + \Input *pSocket - pointer to socket + \Input *pBuf - [out] buffer to receive data + \Input iLen - length of recv buffer + \Input *pFrom - [out] address data was received from (NULL=ignore) + \Input *pFromLen - [out] length of address + + \Output + int32_t - size of packet extracted from queue, 0 if no packet + + \Version 04/20/2016 (mclouatre) +*/ +/************************************************************************************F*/ +static int32_t _SocketRecvfromPacketQueue(SocketT *pSocket, const char *pBuf, int32_t iLen, struct sockaddr *pFrom, int32_t *pFromLen) +{ + int32_t iResult = 0; + + // make sure destination buffer is valid + if ((iLen > 0) && (pBuf != NULL)) + { + // get a packet + if (pSocket->iType != SOCK_STREAM) + { + iResult = SocketPacketQueueRem(pSocket->pRecvQueue, (uint8_t *)pBuf, iLen, &pSocket->RecvAddr); + } + else + { + iResult = SocketPacketQueueRemStream(pSocket->pRecvQueue, (uint8_t *)pBuf, iLen); + } + + if (iResult > 0) + { + if (pFrom != NULL) + { + ds_memcpy_s(pFrom, sizeof(*pFrom), &pSocket->RecvAddr, sizeof(pSocket->RecvAddr)); + *pFromLen = sizeof(*pFrom); + } + } + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _SocketPoll + + \Description + Execute a blocking poll on input of all sockets. + + \Input pState - pointer to module state + \Input uPollNsec - maximum number of nanoseconds to block in poll + + \Output + int32_t - result of the ppoll call + + \Version 04/02/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _SocketPoll(SocketStateT *pState, uint32_t uPollNsec) +{ + struct pollfd aPollFds[1024]; + #if defined(DIRTYCODE_APPLEOSX) || defined(DIRTYCODE_APPLEIOS) + int32_t iPollTime = (int32_t)(uPollNsec/1000000); + #else + struct timespec PollTime = { .tv_sec = uPollNsec/1000000000, .tv_nsec = uPollNsec%1000000000 }; + #endif + int32_t iPoll, iMaxPoll, iResult; + SocketT *pSocket; + + // for access to socket list + NetCritEnter(NULL); + + // walk socket list and find matching socket + for (pSocket = pState->pSockList, iPoll = 0, iMaxPoll = 1024; (pSocket != NULL) && (iPoll < iMaxPoll); pSocket = pSocket->pNext) + { + // skip invalid sockets or sockets we have an error on + if ((pSocket->uSocket == INVALID_SOCKET) || (pSocket->bHasData & 0x80)) + { + pSocket->uPollIdx = iMaxPoll; // mark the socket as 'not in the poll array' + continue; + } + + // add socket to poll array + aPollFds[iPoll].fd = pSocket->uSocket; + aPollFds[iPoll].events = POLLIN; + aPollFds[iPoll].revents = 0; + + // remember poll index + pSocket->uPollIdx = iPoll++; + } + + // release critical section + NetCritLeave(NULL); + + // execute the poll + #if defined(DIRTYCODE_APPLEOSX) || defined(DIRTYCODE_APPLEIOS) + iResult = poll(aPollFds, iPoll, iPollTime); + #else + iResult = ppoll(aPollFds, iPoll, &PollTime, NULL); + #endif + + // if any sockets have pending data, figure out which ones + if (iResult > 0) + { + uint32_t uCurTick; + + // re-acquire critical section + NetCritEnter(NULL); + + // update sockets if there is data to be read or not + for (pSocket = pState->pSockList, uCurTick = NetTick(); pSocket != NULL; pSocket = pSocket->pNext) + { + /* skip socket if it was not in poll array submitted to poll()/ppoll() + only sockets explicitely added to the poll array have uPollIdx in the valid range [0, iMaxPoll[ */ + if (pSocket->uPollIdx >= iMaxPoll) + { + continue; + } + + pSocket->bHasData += aPollFds[pSocket->uPollIdx].revents & POLLIN ? 1 : 0; + if (pSocket->bHasData + && !pSocket->bInCallback + && (pSocket->pCallback != NULL) + && (pSocket->iCallMask & CALLB_RECV)) + { + pSocket->bInCallback = TRUE; + pSocket->pCallback(pSocket, 0, pSocket->pCallRef); + pSocket->bInCallback = FALSE; + pSocket->uCallLast = uCurTick; + } + + // if we have a socket error, remove from future poll events + if (aPollFds[pSocket->uPollIdx].revents & (POLLERR|POLLHUP)) + { + pSocket->bHasData |= 0x80; + } + } + + // release the critical section + NetCritLeave(NULL); + } + else if (iResult < 0) + { + NetPrintf(("dirtynetunix: poll() failed (err=%s)\n", DirtyErrGetName(errno))); + } + + // return number of file descriptors with pending data + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _SocketLookupDone + + \Description + Callback to determine if gethostbyname is complete. + + \Input *pHost - pointer to host lookup record + + \Output + int32_t - zero=in progess, neg=done w/error, pos=done w/success + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _SocketLookupDone(HostentT *pHost) +{ + // return current status + return(pHost->done); +} + +/*F********************************************************************************/ +/*! + \Function _SocketLookupFree + + \Description + Release resources used by SocketLookup() + + \Input *pHost - pointer to host lookup record + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SocketLookupFree(HostentT *pHost) +{ + // release resource + pHost->refcount -= 1; +} + +/*F********************************************************************************/ +/*! + \Function _SocketLookupThread + + \Description + Socket lookup thread + + \Input *_pRef - thread argument (hostent record) + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SocketLookupThread(void *_pRef) +{ + SocketStateT *pState = _Socket_pState; + HostentT *pHost = (HostentT *)_pRef; + int32_t iResult; + struct addrinfo Hints, *pList; + int32_t iPreferredFamily; + char strThreadId[32]; + + // if state is null the module has been shut down + if (pState == NULL) + { + return; + } + + // get the thread id + DirtyThreadGetThreadId(strThreadId, sizeof(strThreadId)); + + // setup lookup hints + ds_memclr(&Hints, sizeof(Hints)); + Hints.ai_family = AF_UNSPEC; + Hints.ai_socktype = SOCK_STREAM; // set specific socktype to limit to one result per type + Hints.ai_protocol = IPPROTO_TCP; // set specific proto to limit to one result per type + Hints.ai_flags = AI_ADDRCONFIG; + + #if !defined(DIRTYCODE_ANDROID) + Hints.ai_flags |= AI_V4MAPPED; + #endif + + #if defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) + iPreferredFamily = AF_INET6; + #else + iPreferredFamily = AF_INET; + #endif + + // start lookup + NetPrintf(("dirtynetunix: lookup thread start; name=%s (thid=%s)\n", pHost->name, strThreadId)); + if ((iResult = getaddrinfo(pHost->name, NULL, &Hints, &pList)) == 0) + { + struct addrinfo *pAddrInfo; + + // first loop we look for addresses matching our preferred address family + for (pAddrInfo = pList; pAddrInfo != NULL; pAddrInfo = pAddrInfo->ai_next) + { + // verbose logging of address info if spam settings warrant + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetunix: addr=%A\n", pAddrInfo->ai_addr)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetunix: ai_flags=0x%08x\n", pAddrInfo->ai_flags)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetunix: ai_family=%d\n", pAddrInfo->ai_family)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetunix: ai_socktype=%d\n", pAddrInfo->ai_socktype)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetunix: ai_protocol=%d\n", pAddrInfo->ai_protocol)); + NetPrintfVerbose((pState->iVerbose, 2, "dirtynetunix: name=%s\n", pAddrInfo->ai_canonname)); + + // extract first IPv6 address we come across + if ((pHost->addr == 0) && (pAddrInfo->ai_family == iPreferredFamily)) + { + pHost->addr = SocketAddrMapAddress(&pState->AddrMap, pAddrInfo->ai_addr, (int32_t)pAddrInfo->ai_addrlen); + } + } + + // if we haven't found one yet, look for any address, and pick the first one we come across + for (pAddrInfo = pList; (pAddrInfo != NULL) && (pHost->addr == 0); pAddrInfo = pAddrInfo->ai_next) + { + pHost->addr = SocketAddrMapAddress(&pState->AddrMap, pAddrInfo->ai_addr, (int32_t)pAddrInfo->ai_addrlen); + } + + // print selected address + NetPrintf(("dirtynetunix: %s=%a\n", pHost->name, pHost->addr)); + + // mark success + pHost->done = 1; + + // add hostname to cache + SocketHostnameCacheAdd(pState->pHostnameCache, pHost->name, pHost->addr, pState->iVerbose); + + // release memory + freeaddrinfo(pList); + } + else + { + // unsuccessful + NetPrintf(("dirtynetunix: getaddrinfo('%s', ...) failed err=%s\n", pHost->name, DirtyErrGetNameList(iResult, _GAI_ErrList))); + pHost->done = -1; + } + + // note thread completion + pHost->thread = 1; + + NetPrintf(("dirtynetunix: lookup thread exit; name=%s (thid=%s)\n", pHost->name, strThreadId)); + + // release thread-allocated refcount on hostname resource + pHost->refcount -= 1; +} + +/*F********************************************************************************/ +/*! + \Function _SocketRecvfrom + + \Description + Receive data from a remote host on a datagram socket. + + \Input *pSocket - socket reference + \Input *pBuf - buffer to receive data + \Input iLen - length of recv buffer + \Input *pFrom - address data was received from (NULL=ignore) + \Input *pFromLen - length of address + + \Output + int32_t - positive=data bytes received, else error + + \Version 09/10/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _SocketRecvfrom(SocketT *pSocket, char *pBuf, int32_t iLen, struct sockaddr *pFrom, int32_t *pFromLen) +{ + int32_t iResult; + + // make sure socket ref is valid + if (pSocket->uSocket == INVALID_SOCKET) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + if (pFrom != NULL) + { + // do the receive + struct sockaddr_in6 SockAddr6; + SockaddrInit6(&SockAddr6, AF_INET6); + *pFromLen = sizeof(SockAddr6); + SockaddrInit(pFrom, AF_INET); + if ((iResult = (int32_t)recvfrom(pSocket->uSocket, pBuf, iLen, 0, (struct sockaddr *)&SockAddr6, (socklen_t *)pFromLen)) > 0) + { + SocketAddrMapTranslate(&_Socket_pState->AddrMap, pFrom, (struct sockaddr *)&SockAddr6, pFromLen); + SockaddrInSetMisc(pFrom, NetTick()); + } + } + else + { + iResult = (int32_t)recv(pSocket->uSocket, pBuf, iLen, 0); + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _SocketRecvToPacketQueue + + \Description + Attempt to receive data from the given socket and to push it directly + in the packet queue. + + \Input *pState - pointer to module state + \Input *pSocket - pointer to socket to read from + + \Output + int32_t - receive result + + \Version 10/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SocketRecvToPacketQueue(SocketStateT *pState, SocketT *pSocket) +{ + int32_t iRecvResult; + + // early exit if packet queue is full or this is a virtual socket + if (SocketPacketQueueStatus(pSocket->pRecvQueue, 'pful') || (pSocket->bVirtual == TRUE)) + { + return; + } + + // get a packet queue entry to receive into + pSocket->pRecvPacket = SocketPacketQueueAlloc(pSocket->pRecvQueue); + + // try and receive some data + if ((pSocket->iType == SOCK_DGRAM) || (pSocket->iType == SOCK_RAW)) + { + int32_t iFromLen = sizeof(pSocket->RecvAddr); + iRecvResult = _SocketRecvfrom(pSocket, (char *)pSocket->pRecvPacket->aPacketData, sizeof(pSocket->pRecvPacket->aPacketData), &pSocket->RecvAddr, &iFromLen); + } + else + { + iRecvResult = _SocketRecvfrom(pSocket, (char *)pSocket->pRecvPacket->aPacketData, sizeof(pSocket->pRecvPacket->aPacketData), NULL, 0); + } + + // if the read completed successfully, save the originator address, packet size and reception time; forward data to socket callback if needed + if (iRecvResult > 0) + { + pSocket->pRecvPacket->iPacketSize = iRecvResult; + pSocket->pRecvPacket->uPacketTick = NetTick(); + ds_memcpy_s(&pSocket->pRecvPacket->PacketAddr, sizeof(pSocket->pRecvPacket->PacketAddr), &pSocket->RecvAddr, sizeof(pSocket->RecvAddr)); + + // see if we should issue callback + if ((pSocket->uCallLast != (unsigned)-1) && (pSocket->pCallback != NULL) && (pSocket->iCallMask & CALLB_RECV)) + { + pSocket->uCallLast = (unsigned)-1; + (pSocket->pCallback)(pSocket, 0, pSocket->pCallRef); + pSocket->uCallLast = NetTick(); + } + } + else + { + if (errno != EAGAIN) + { + // if we are using a TCP socket and we didn't receive positive bytes, we are closed + if ((pSocket->iType == SOCK_STREAM) && (iRecvResult <= 0)) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetunix: [%p] connection %s\n", pSocket, (iRecvResult == 0) ? "closed" : "failed")); + pSocket->iOpened = -1; + } + else + { + NetPrintf(("dirtynetunix: [%p] _SocketRecvfrom() to packet queue returned %d (err=%s)\n", pSocket, iRecvResult, DirtyErrGetName(errno))); + } + } + + // clean up resources that were reserved for the receive operation + SocketPacketQueueAllocUndo(pSocket->pRecvQueue); + } +} + +/*F********************************************************************************/ +/*! + \Function _SocketRecvThread + + \Description + Wait for incoming data and deliver it immediately to the socket callback, + if registered. + + \Input *pArg - pointer to Socket module state + + \Version 10/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SocketRecvThread(void *pArg) +{ + typedef struct PollListT + { + SocketT *aSockets[SOCKET_MAXPOLL]; + struct pollfd aPollFds[SOCKET_MAXPOLL]; + int32_t iCount; + } PollListT; + + PollListT pollList, previousPollList; + SocketT *pSocket; + int32_t iListIndex, iResult; + SocketStateT *pState = (SocketStateT *)pArg; + char strThreadId[32]; + + // get the thread id + DirtyThreadGetThreadId(strThreadId, sizeof(strThreadId)); + + // show we are alive + pState->iRecvLife = 1; + NetPrintf(("dirtynetunix: recv thread running (thid=%s)\n", strThreadId)); + + // reset contents of pollList + ds_memclr(&pollList, sizeof(pollList)); + + // loop until done + while(pState->iRecvLife == 1) + { + // reset contents of previousPollList + ds_memclr(&previousPollList, sizeof(previousPollList)); + + // make a copy of the poll list used for the last poll() call + for (iListIndex = 0; iListIndex < pollList.iCount; iListIndex++) + { + // copy entry from pollList to previousPollList + previousPollList.aSockets[iListIndex] = pollList.aSockets[iListIndex]; + previousPollList.aPollFds[iListIndex] = pollList.aPollFds[iListIndex]; + } + previousPollList.iCount = pollList.iCount; + + // reset contents of pollList in preparation for the next poll() call + ds_memclr(&pollList, sizeof(pollList)); + + // acquire global critical section for access to socket list + NetCritEnter(NULL); + + // walk the socket list and do two things: + // 1- if the socket is ready for reading, perform the read operation + // 2- if the buffer in which inbound data is saved is empty, initiate a new low-level read operation for that socket + for (pSocket = pState->pSockList; (pSocket != NULL) && (pollList.iCount < SOCKET_MAXPOLL); pSocket = pSocket->pNext) + { + // only handle non-virtual sockets with asyncrecv enabled + if ((pSocket->bVirtual == FALSE) && (pSocket->uSocket != INVALID_SOCKET) && (pSocket->bAsyncRecv == TRUE)) + { + // acquire socket critical section + NetCritEnter(&pSocket->RecvCrit); + + // was this socket in the poll list of the previous poll() call + for (iListIndex = 0; iListIndex < previousPollList.iCount; iListIndex++) + { + if (previousPollList.aSockets[iListIndex] == pSocket) + { + // socket was in previous poll list! + // now check if poll() notified that this socket is ready for reading + if (previousPollList.aPollFds[iListIndex].revents & POLLIN) + { + /* + Note: + The poll() doc states that some error codes returned by the function + may only apply to one of the sockets in the poll list. For this reason, + we check the polling result for all entries in the list regardless + of the return value of poll(). + */ + + // ready for reading, so go ahead and read + _SocketRecvToPacketQueue(pState, previousPollList.aSockets[iListIndex]); + } + /* + POLLNVAL: The file descriptor is not open. we need to exclude this socket + handle from being added to the poll list, otherwise the poll() will keep + returning 1 at once. + + Due to the race-condition (the main thread might have recreated the socket + before we reach here), 'brokenflag' is accumulated rather simply set to TRUE, and + the socket will be excluded from the poll list only if it's marked as broken + twice or more ('brokenflag' is reset to 0 when recreating the socket, so actually + the max possible value of 'brokenflag' is 2). + */ + else if (previousPollList.aPollFds[iListIndex].revents & POLLNVAL) + { + NetPrintf(("dirtynetunix: marking socket (%x->%d) as broken upon POLLNVAL\n", pSocket, pSocket->uSocket)); + pSocket->uBrokenFlag++; + } + break; + } + } + + /* if the socket is not virtual, the socket is open (TCP) and if there is room in the recv queue, + then add this socket to the poll list to be used by the next poll() call */ + if (!SocketPacketQueueStatus(pSocket->pRecvQueue, 'pful') && (pSocket->uSocket != INVALID_SOCKET) && (pSocket->uBrokenFlag <= 1) && ((pSocket->iType != SOCK_STREAM) || (pSocket->iOpened > 0))) + { + // add socket to poll list + pollList.aSockets[pollList.iCount] = pSocket; + pollList.aPollFds[pollList.iCount].fd = pSocket->uSocket; + pollList.aPollFds[pollList.iCount].events = POLLIN; + pollList.iCount += 1; + } + + // release socket critical section + NetCritLeave(&pSocket->RecvCrit); + } + } + + // release global critical section + NetCritLeave(NULL); + + // any sockets? + if (pollList.iCount > 0) + { + // poll for data (wait up to 50ms) + iResult = poll(pollList.aPollFds, pollList.iCount, 50); + + if (iResult < 0) + { + NetPrintf(("dirtynetunix: poll() failed (err=%s)\n", DirtyErrGetName(errno))); + + // stall for 50ms because experiment shows that next call to poll() may not block + // internally if a socket is alreay in error. + usleep(50*1000); + } + } + else + { + // no sockets, so stall for 50ms + usleep(50*1000); + } + } + + // indicate we are done + NetPrintf(("dirtynetunix: receive thread exit\n")); + pState->iRecvLife = 0; +} + +/*F********************************************************************************/ +/*! + \Function _SocketGetMacAddress + + \Description + Attempt to retreive MAC address of the system. + + \Input *pState - pointer to module state + + \Output + uint8_t - TRUE if MAC address found, FALSE otherwise + + \Notes + Usage of getifaddrs() is preferred over usage of ioctl() with a socket to save + the socket creation step. However, not all platforms support the AF_LINK address + family. In those cases, usage of ioctl() can't be avoided. + + \Version 05/12/2004 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t _SocketGetMacAddress(SocketStateT *pState) +{ + int32_t iResult; + uint8_t bFound = FALSE; + +#if defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) + struct ifaddrs* pIntfList = NULL; + struct ifaddrs* pIntf = NULL; + + // retrieve the current interfaces - returns 0 on success + iResult = getifaddrs(&pIntfList); + if (iResult == 0) + { + // loop through linked list of interfaces + pIntf = pIntfList; + while(pIntf != NULL) + { + // "en0" is the name of the wifi adapter on the iPhone + if ((pIntf->ifa_addr->sa_family == AF_LINK) && strcmp(pIntf->ifa_name, "en0") == 0) + { + struct sockaddr_dl* pDataLinkSockAddr = (struct sockaddr_dl *)(pIntf->ifa_addr); + + if (pDataLinkSockAddr && pDataLinkSockAddr->sdl_alen == 6) + { + ds_memcpy(pState->aMacAddr, LLADDR(pDataLinkSockAddr), 6); + + NetPrintf(("dirtynetunix: mac address - %X:%X:%X:%X:%X:%X\n", + (uint32_t)pState->aMacAddr[0], (uint32_t)pState->aMacAddr[1], (uint32_t)pState->aMacAddr[2], + (uint32_t)pState->aMacAddr[3], (uint32_t)pState->aMacAddr[4], (uint32_t)pState->aMacAddr[5])); + + bFound = TRUE; + + break; + } + } + + pIntf = pIntf->ifa_next; + } + + // free interface list returned by getifaddrs() + freeifaddrs(pIntfList); + } + else + { + NetPrintf(("dirtynetunix: getifaddrs() returned nonzero status: %d\n", iResult)); + } +#else + struct ifreq req; + int32_t fd; + int32_t iIfIndex; + + const char *aIfrName[] = + { + "eth0", + "eth1", + "wlan0", + "end-of-interface-array" + }; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) + { + for (iIfIndex = 0; strcmp(aIfrName[iIfIndex],"end-of-interface-array"); iIfIndex++) + { + strncpy(req.ifr_name, aIfrName[iIfIndex], IFNAMSIZ); + if ((iResult = ioctl(fd, SIOCGIFHWADDR, &req)) >= 0) + { + ds_memcpy(pState->aMacAddr, req.ifr_hwaddr.sa_data, 6); + bFound = TRUE; + break; + } + else + { + NetPrintf(("dirtynetunix: (%s) failed to query MAC address - SIOCGIFHWADDR ioctl failure %d\n", aIfrName[iIfIndex], errno)); + } + } + + close(fd); + } + else + { + NetPrintf(("dirtynetunix: can't open socket %d for MAC address query with ioctl(SIOCGIFHWADDR)\n", errno)); + } +#endif + + return(bFound); +} + +/*F********************************************************************************/ +/*! + \Function _SocketInfoGlobal + + \Description + Return information about global state + + \Input iInfo - selector for desired information + \Input iData - selector specific + \Input *pBuf - return buffer + \Input iLen - buffer length + + \Output + int32_t - selector-specific + + \Notes + These selectors need to be documented in SocketInfo() to allow our + documentation generation to pick them up. + + \Version 03/31/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _SocketInfoGlobal(int32_t iInfo, int32_t iData, void *pBuf, int32_t iLen) +{ + SocketStateT *pState = _Socket_pState; + + if (iInfo == 'addr') + { + #if defined(DIRTYCODE_APPLEIOS) //$$TODO -- evaluate + if (pState->uLocalAddr == 0) + { + // get local address here, or possibly at network startup + struct ifaddrs* interfaces = NULL; + + NetPrintf(("dirtynetunix: querying interfaces\n")); + + int error = getifaddrs(&interfaces); + if (error == 0) + { + struct ifaddrs *currentAddress; + for (currentAddress = interfaces; currentAddress; currentAddress = currentAddress->ifa_next) + { + // only consider live inet interfaces and return first valid non-loopback address + if (((currentAddress->ifa_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP)) + { + struct sockaddr *pHostAddr = (struct sockaddr *)currentAddress->ifa_addr; + if ((currentAddress->ifa_addr->sa_family == AF_INET) && (pState->uLocalAddr == 0)) + { + pState->uLocalAddr = SockaddrInGetAddr(pHostAddr); + NetPrintf(("dirtynetunix: found local address %a\n", pState->uLocalAddr)); + } + else if (currentAddress->ifa_addr->sa_family == AF_INET6) + { + NetPrintf(("dirtynetunix: found interface %A\n", (struct sockaddr *)pHostAddr)); + } + } + } + + freeifaddrs(interfaces); + } + else + { + NetPrintf(("dirtynetunix: error %d querying interfaces\n", errno)); + } + } + return(pState->uLocalAddr); + #else + struct sockaddr HostAddr, DestAddr; + SockaddrInit(&DestAddr, AF_INET); + SockaddrInSetAddr(&DestAddr, (uint32_t)iData); + if (SocketHost(&HostAddr, sizeof(HostAddr), &DestAddr, sizeof(DestAddr)) != -1) + { + return(SockaddrInGetAddr(&HostAddr)); + } + else + { + return(-1); + } + #endif + } + // get socket bound to given port + if ((iInfo == 'bind') || (iInfo == 'bndu')) + { + SocketT *pSocket; + struct sockaddr BindAddr; + int32_t iFound = -1; + + // for access to socket list + NetCritEnter(NULL); + + // walk socket list and find matching socket + for (pSocket = pState->pSockList; pSocket != NULL; pSocket = pSocket->pNext) + { + // if iInfo is 'bndu', only consider sockets of type SOCK_DGRAM + // note: 'bndu' stands for "bind udp" + if ((iInfo == 'bind') || ((iInfo == 'bndu') && (pSocket->iType == SOCK_DGRAM))) + { + // get socket info + SocketInfo(pSocket, 'bind', 0, &BindAddr, sizeof(BindAddr)); + if (SockaddrInGetPort(&BindAddr) == iData) + { + *(SocketT **)pBuf = pSocket; + iFound = 0; + break; + } + } + } + + // for access to g_socklist and g_sockkill + NetCritLeave(NULL); + return(iFound); + } + + if (iInfo == 'conn') + { + return(pState->uConnStatus); + } + + #ifdef DIRTYCODE_ANDROID + if (iInfo == 'eth0' || iInfo == 'wan0') + { + int32_t iRet = -1; + int32_t fd = socket(AF_INET, SOCK_DGRAM, 0); + struct ifreq ifr; + + if (fd == -1) + { + NetPrintf(("dirtynetunix: SocketInfo('eth0/wan0') cannot create socket descriptor.\n")); + iRet = -2; + } + else + { + if (iInfo == 'eth0') + { + strncpy(ifr.ifr_name, "eth0", IFNAMSIZ); + } + else + { + strncpy(ifr.ifr_name, "wlan0", IFNAMSIZ); + } + + if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) + { + close(fd); + NetPrintf(("dirtynetunix: SocketInfo('%c') cannot find an IP address for device %s. Errno %s\n", iInfo, ifr.ifr_name, strerror(errno))); + iRet = -3; + } + else + { + close(fd); + struct sockaddr_in* ipaddr = (struct sockaddr_in*)&ifr.ifr_addr; + if (ipaddr->sin_addr.s_addr != 0) + { + NetPrintf(("dirtynetunix: SocketInfo('%c') network address found for %s.\n", iInfo, ifr.ifr_name)); + iRet = 0; + } + else + { + NetPrintf(("dirtynetunix: SocketInfo('%c') cannot find an IP address for device %s.\n", iInfo, ifr.ifr_name)); + iRet = -4; + } + } + } + + return(iRet); + } + #endif + + // get MAC address + if ((iInfo == 'ethr') || (iInfo == 'macx')) + { + uint8_t aZeros[6] = { 0, 0, 0, 0, 0, 0 }; + uint8_t bFound = TRUE; + + // early exit if user-provided buffer not correct + if ((pBuf == NULL) && (iLen < (signed)sizeof(pState->aMacAddr))) + { + return(-1); + } + + // try to get mac address if we don't already have it + if (!memcmp(pState->aMacAddr, aZeros, sizeof(pState->aMacAddr))) + { + bFound = _SocketGetMacAddress(pState); + } + + if (bFound) + { + // copy MAC address in user-provided buffer and signal success + ds_memcpy(pBuf, &pState->aMacAddr, sizeof(pState->aMacAddr)); + return(0); + } + + // signal failure - no MAC address found + return(-1); + } + + // check if specified ipv4 address is virtual and return associated ipv6 address if so + if (iInfo == '?ip6') + { + int32_t iResult = -1; + struct sockaddr_in6 SockAddr6, *pSockAddr6; + struct sockaddr SockAddr; + int32_t iNameLen; + + SockaddrInit(&SockAddr, AF_INET); + SockaddrInSetAddr(&SockAddr, iData); + + SockaddrInit6(&SockAddr6, AF_INET6); + pSockAddr6 = ((pBuf != NULL) && (iLen == sizeof(SockAddr6))) ? (struct sockaddr_in6 *)pBuf : &SockAddr6; + iNameLen = sizeof(SockAddr6); + + iResult = SocketAddrMapGet(&pState->AddrMap, (struct sockaddr *)pSockAddr6, &SockAddr, &iNameLen) ? 1 : 0; + return(iResult); + } + + // return max packet size + if (iInfo == 'maxp') + { + return(pState->iMaxPacket); + } + + // get send callback function pointer (iData specifies index in array) + if (iInfo == 'sdcf') + { + if ((pBuf != NULL) && (iLen == sizeof(pState->aSendCbEntries[iData].pSendCallback))) + { + ds_memcpy(pBuf, &pState->aSendCbEntries[iData].pSendCallback, sizeof(pState->aSendCbEntries[iData].pSendCallback)); + return(0); + } + + NetPrintf(("dirtynetunix: 'sdcf' selector used with invalid paramaters\n")); + return(-1); + } + // get send callback user data pointer (iData specifies index in array) + if (iInfo == 'sdcu') + { + if ((pBuf != NULL) && (iLen == sizeof(pState->aSendCbEntries[iData].pSendCallref))) + { + ds_memcpy(pBuf, &pState->aSendCbEntries[iData].pSendCallref, sizeof(pState->aSendCbEntries[iData].pSendCallref)); + return(0); + } + + NetPrintf(("dirtynetunix: 'sdcu' selector used with invalid paramaters\n")); + return(-1); + } + // return global debug output level + if (iInfo == 'spam') + { + return(pState->iVerbose); + } + + NetPrintf(("dirtynetunix: unhandled global SocketInfo() selector '%C'\n", iInfo)); + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _SocketControlGlobal + + \Description + Process a global control message (type specific operation) + + \Input iOption - the option to pass + \Input iData1 - message specific parm + \Input *pData2 - message specific parm + \Input *pData3 - message specific parm + + \Output + int32_t - message specific result (-1=unsupported message) + + \Notes + These selectors need to be documented in SocketControl() to allow our + documentation generation to pick them up. + + \Version 03/31/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _SocketControlGlobal(int32_t iOption, int32_t iData1, void *pData2, void *pData3) +{ + SocketStateT *pState = _Socket_pState; + + // init network stack and bring up interface + if (iOption == 'conn') + { + #if defined(DIRTYCODE_APPLEIOS) //$$TODO - evaluate + CFStringRef URLString = CFStringCreateWithCString(kCFAllocatorDefault, "http://gos.ea.com/util/test.jsp", kCFStringEncodingASCII); + CFStringRef getString = CFStringCreateWithCString(kCFAllocatorDefault, "GET", kCFStringEncodingASCII); + CFURLRef baseURL = NULL; + CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault, URLString, baseURL); + CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, getString, url, kCFHTTPVersion1_1); + CFMutableDataRef data = CFDataCreateMutable(NULL, 0); + CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(NULL, request); + + if (CFReadStreamOpen(readStream)) + { + char done = FALSE; + do + { + const int BUFSIZE = 4096; + unsigned char buf[BUFSIZE]; + int bytesRead = (int)CFReadStreamRead(readStream, buf, BUFSIZE); + if (bytesRead > 0) + { + CFDataAppendBytes(data, buf, bytesRead); + } + else if (bytesRead == 0) + { + done = TRUE; + } + else + { + done = TRUE; + } + } while (!done); + } + + CFReadStreamClose(readStream); + CFRelease(readStream); + readStream = nil; + + CFRelease(url); + url = NULL; + + CFRelease(data); + data = NULL; + + CFRelease(URLString); + URLString = NULL; + + CFRelease(getString); + getString = NULL; + #endif + + pState->uConnStatus = '+onl'; + return(0); + } + // bring down interface + if (iOption == 'disc') + { + NetPrintf(("dirtynetunix: disconnecting from network\n")); + pState->uConnStatus = '-off'; + return(0); + } + // set an ipv6 address into the mapping table + if (iOption == '+ip6') + { + return(SocketAddrMapAddress(&pState->AddrMap, (const struct sockaddr *)pData2, iData1)); + } + // del an ipv6 address from the mapping table + if (iOption == '-ip6') + { + return(SocketAddrUnmapAddress(&pState->AddrMap, (const struct sockaddr *)pData2, iData1)); + } + // remap an existing ipv6 address in the mapping table + if (iOption == '~ip6') + { + return(SocketAddrRemapAddress(&pState->AddrMap, (const struct sockaddr *)pData2, (const struct sockaddr *)pData3, iData1)); + } + // handle any idle processing required + if (iOption == 'idle') + { + // in single-threaded mode, we have to give life to the network idle process + if (pState->bSingleThreaded) + { + NetIdleCall(); + } + return(0); + } + // set max udp packet size + if (iOption == 'maxp') + { + NetPrintf(("dirtynetunix: setting max udp packet size to %d\n", iData1)); + pState->iMaxPacket = iData1; + return(0); + } + // block waiting on input from socket list + if (iOption == 'poll') + { + return(_SocketPoll(pState, (unsigned)iData1*1000000)); + } + if (iOption == 'poln') + { + return(_SocketPoll(pState, (unsigned)iData1*1000)); + } + // set/unset send callback (iData1=TRUE for set - FALSE for unset, pData2=callback, pData3=callref) + if (iOption == 'sdcb') + { + SocketSendCallbackEntryT sendCbEntry; + sendCbEntry.pSendCallback = (SocketSendCallbackT *)pData2; + sendCbEntry.pSendCallref = pData3; + + if (iData1) + { + return(SocketSendCallbackAdd(&pState->aSendCbEntries[0], &sendCbEntry)); + } + else + { + return(SocketSendCallbackRem(&pState->aSendCbEntries[0], &sendCbEntry)); + } + } + // set debug spam level + if (iOption == 'spam') + { + // module level debug level + pState->iVerbose = iData1; + return(0); + } + // mark a port as virtual + if (iOption == 'vadd') + { + int32_t iPort; + + // find a slot to add virtual port + for (iPort = 0; pState->aVirtualPorts[iPort] != 0; iPort++) + ; + if (iPort < SOCKET_MAXVIRTUALPORTS) + { + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetunix: added port %d to virtual port list\n", iData1)); + pState->aVirtualPorts[iPort] = (uint16_t)iData1; + return(0); + } + } + // remove port from virtual port list + if (iOption == 'vdel') + { + int32_t iPort; + + // find virtual port in list + for (iPort = 0; (iPort < SOCKET_MAXVIRTUALPORTS) && (pState->aVirtualPorts[iPort] != (uint16_t)iData1); iPort++) + ; + if (iPort < SOCKET_MAXVIRTUALPORTS) + { + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetunix: removed port %d from virtual port list\n", iData1)); + pState->aVirtualPorts[iPort] = 0; + return(0); + } + } + // unhandled + NetPrintf(("dirtynetunix: unhandled global SocketControl() option '%C'\n", iOption)); + return(-1); +} + +/*** Public Functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function SocketCreate + + \Description + Create new instance of socket interface module. Initializes all global + resources and makes module ready for use. + + \Input iThreadPrio - priority to start threads with + \Input iThreadStackSize - stack size to start threads with (in bytes) + \Input iThreadCpuAffinity - cpu affinity to start threads with + + \Output + int32_t - negative=error, zero=success + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketCreate(int32_t iThreadPrio, int32_t iThreadStackSize, int32_t iThreadCpuAffinity) +{ + SocketStateT *pState = _Socket_pState; + int32_t iMemGroup; + void *pMemGroupUserData; + int32_t iResult; + + // Query mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // error if already started + if (pState != NULL) + { + NetPrintf(("dirtynetunix: SocketCreate() called while module is already active\n")); + return(-1); + } + + // print version info + NetPrintf(("dirtynetunix: DirtySDK v%d.%d.%d.%d.%d\n", DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH)); + + // alloc and init state ref + if ((pState = DirtyMemAlloc(sizeof(*pState), SOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetunix: unable to allocate module state\n")); + return(-2); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + pState->iMaxPacket = SOCKET_MAXUDPRECV; + pState->iVerbose = 1; + + if (iThreadPrio < 0) + { + pState->bSingleThreaded = TRUE; + } + + // disable SIGPIPE (Linux-based system only) + #if defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_ANDROID) + _SocketDisableSigpipe(); + #endif + + // startup network libs + NetLibCreate(iThreadPrio, iThreadStackSize, iThreadCpuAffinity); + + // create hostname cache + if ((pState->pHostnameCache = SocketHostnameCacheCreate(iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtynetunix: unable to create hostname cache\n")); + SocketDestroy((uint32_t)(-1)); + return(-3); + } + + // add our idle handler + NetIdleAdd(&_SocketIdle, pState); + + // create high-priority receive thread + if (!pState->bSingleThreaded) + { + DirtyThreadConfigT ThreadConfig; + + // configure threading + ds_memclr(&ThreadConfig, sizeof(ThreadConfig)); + ThreadConfig.pName = "SocketRecv"; + ThreadConfig.iAffinity = iThreadCpuAffinity; + ThreadConfig.iPriority = iThreadPrio; + ThreadConfig.iVerbosity = pState->iVerbose; + + if ((iResult = DirtyThreadCreate(_SocketRecvThread, pState, &ThreadConfig)) == 0) + { + // wait for receive thread startup + while (pState->iRecvLife == 0) + { + usleep(100); + } + } + else + { + NetPrintf(("dirtynetunix: unable to create recv thread (err=%d)\n", iResult)); + pState->iRecvLife = 0; + } + } + + // init socket address map + SocketAddrMapInit(&pState->AddrMap, pState->iMemGroup, pState->pMemGroupUserData); + + // save state + _Socket_pState = pState; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function SocketDestroy + + \Description + Release resources and destroy module. + + \Input uShutdownFlags - shutdown flags + + \Output + int32_t - negative=error, zero=success + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketDestroy(uint32_t uShutdownFlags) +{ + SocketStateT *pState = _Socket_pState; + + // error if not active + if (pState == NULL) + { + NetPrintf(("dirtynetunix: SocketDestroy() called while module is not active\n")); + return(-1); + } + + NetPrintf(("dirtynetunix: shutting down\n")); + + // wait until all lookup threads are done + while (pState->pHostList != NULL) + { + volatile HostentT **ppHost; + int32_t iSocketLookups; + + // check for lookup threads that are still active + for (ppHost = (volatile HostentT **)&pState->pHostList, iSocketLookups = 0; *ppHost != NULL; ppHost = (volatile HostentT **)&(*ppHost)->pNext) + { + iSocketLookups += (*ppHost)->thread ? 0 : 1; + } + // if no ongoing socket lookups, we're done + if (iSocketLookups == 0) + { + break; + } + NetConnSleep(1); + } + + // kill idle callbacks + NetIdleDel(&_SocketIdle, pState); + + // let any idle event finish + NetIdleDone(); + + if ((!pState->bSingleThreaded) && (pState->iRecvLife == 1)) + { + // tell receive thread to quit + pState->iRecvLife = 2; + // wait for thread to terminate + while (pState->iRecvLife > 0) + { + usleep(1*1000); + } + } + + // cleanup addr map, if allocated + SocketAddrMapShutdown(&pState->AddrMap); + + // close any remaining sockets + NetCritEnter(NULL); + while (pState->pSockList != NULL) + { + SocketClose(pState->pSockList); + } + NetCritLeave(NULL); + + // clear the kill list + _SocketIdle(pState); + + // destroy hostname cache + if (pState->pHostnameCache != NULL) + { + SocketHostnameCacheDestroy(pState->pHostnameCache); + } + + // shut down network libs + NetLibDestroy(0); + + // dispose of state + DirtyMemFree(pState, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + _Socket_pState = NULL; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function SocketOpen + + \Description + Create a new transfer endpoint. A socket endpoint is required for any + data transfer operation. + + \Input iAddrFamily - address family (AF_INET) + \Input iType - socket type (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, ...) + \Input iProto - protocol type for SOCK_RAW (unused by others) + + \Output + SocketT * - socket reference + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +SocketT *SocketOpen(int32_t iAddrFamily, int32_t iType, int32_t iProto) +{ + return(_SocketOpen(-1, iAddrFamily, iType, iProto, 0)); +} + +/*F********************************************************************************/ +/*! + \Function SocketClose + + \Description + Close a socket. Performs a graceful shutdown of connection oriented protocols. + + \Input *pSocket - socket reference + + \Output + int32_t - negative=error, else zero + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketClose(SocketT *pSocket) +{ + int32_t iSocket = pSocket->uSocket; + + // stop sending + SocketShutdown(pSocket, SOCK_NOSEND); + + // dispose of SocketT + if (_SocketClose(pSocket) < 0) + { + return(-1); + } + + // close unix socket if allocated + if (iSocket >= 0) + { + // close socket + if (close(iSocket) < 0) + { + NetPrintf(("dirtynetunix: close() failed (err=%s)\n", DirtyErrGetName(errno))); + } + } + + // success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function SocketImport + + \Description + Import a socket. The given socket ref may be a SocketT, in which case a + SocketT pointer to the ref is returned, or it can be an actual unix socket ref, + in which case a SocketT is created for the unix socket ref. + + \Input uSockRef - socket reference + + \Output + SocketT * - pointer to imported socket, or NULL + + \Version 01/14/2005 (jbrookes) +*/ +/********************************************************************************F*/ +SocketT *SocketImport(intptr_t uSockRef) +{ + SocketStateT *pState = _Socket_pState; + socklen_t iProtoSize; + int32_t iProto; + SocketT *pSock; + + // see if this socket is already in our socket list + NetCritEnter(NULL); + for (pSock = pState->pSockList; pSock != NULL; pSock = pSock->pNext) + { + if (pSock == (SocketT *)uSockRef) + { + break; + } + } + NetCritLeave(NULL); + + // if socket is in socket list, just return it + if (pSock != NULL) + { + return(pSock); + } + + // get info from socket ref + iProtoSize = sizeof(iProto); + if (getsockopt((int32_t)uSockRef, SOL_SOCKET, SO_TYPE, &iProto, &iProtoSize) == 0) + { + // create the socket + pSock = _SocketOpen((int32_t)uSockRef, AF_INET, iProto, 0, 0); + + // update local and remote addresses + SocketInfo(pSock, 'bind', 0, &pSock->LocalAddr, sizeof(pSock->LocalAddr)); + SocketInfo(pSock, 'peer', 0, &pSock->RemoteAddr, sizeof(pSock->RemoteAddr)); + + // mark it as imported + pSock->bImported = TRUE; + } + else + { + NetPrintf(("dirtynetunix: getsockopt(SO_TYPE) failed (err=%s)\n", DirtyErrGetName(errno))); + } + + return(pSock); +} + +/*F********************************************************************************/ +/*! + \Function SocketRelease + + \Description + Release an imported socket. + + \Input *pSocket - pointer to socket + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void SocketRelease(SocketT *pSocket) +{ + // if it wasn't imported, nothing to do + if (pSocket->bImported == FALSE) + { + return; + } + + // dispose of SocketT, but leave the sockref alone + _SocketClose(pSocket); +} + +/*F********************************************************************************/ +/*! + \Function SocketShutdown + + \Description + Perform partial/complete shutdown of socket indicating that either sending + and/or receiving is complete. + + \Input *pSocket - socket reference + \Input iHow - SOCK_NOSEND and/or SOCK_NORECV + + \Output + int32_t - negative=error, else zero + + \Version 09/10/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketShutdown(SocketT *pSocket, int32_t iHow) +{ + int32_t iResult=0; + + // only shutdown a connected socket + if (pSocket->iType != SOCK_STREAM) + { + pSocket->iLastError = SOCKERR_NONE; + return(pSocket->iLastError); + } + + // make sure socket ref is valid + if (pSocket->uSocket == INVALID_SOCKET) + { + pSocket->iLastError = SOCKERR_NONE; + return(pSocket->iLastError); + } + + // translate how + if (iHow == SOCK_NOSEND) + { + iHow = SHUT_WR; + } + else if (iHow == SOCK_NORECV) + { + iHow = SHUT_RD; + } + else if (iHow == (SOCK_NOSEND|SOCK_NORECV)) + { + iHow = SHUT_RDWR; + } + + // do the shutdown + if (shutdown(pSocket->uSocket, iHow) < 0) + { + iResult = errno; + + // log only useful messages + if (iResult != ENOTCONN) + { + NetPrintf(("dirtynetunix: shutdown() failed (err=%s)\n", DirtyErrGetName(iResult))); + } + } + + pSocket->iLastError = _SocketTranslateError(iResult); + return(pSocket->iLastError); +} + +/*F********************************************************************************/ +/*! + \Function SocketBind + + \Description + Bind a local address/port to a socket. + + \Input *pSocket - socket reference + \Input *pName - local address/port + \Input iNameLen - length of name + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Notes + If either address or port is zero, then they are filled in automatically. + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketBind(SocketT *pSocket, const struct sockaddr *pName, int32_t iNameLen) +{ + SocketStateT *pState = _Socket_pState; + struct sockaddr_in6 SockAddr6; + int32_t iResult; + + // make sure socket is valid + if (pSocket->uSocket < 0) + { + NetPrintf(("dirtynetunix: attempt to bind invalid socket\n")); + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + // save local address + ds_memcpy_s(&pSocket->LocalAddr, sizeof(pSocket->LocalAddr), pName, sizeof(*pName)); + + // is the bind port a virtual port? + if (pSocket->iType == SOCK_DGRAM) + { + int32_t iPort; + uint16_t uPort; + + if ((uPort = SockaddrInGetPort(pName)) != 0) + { + // find virtual port in list + for (iPort = 0; (iPort < SOCKET_MAXVIRTUALPORTS) && (pState->aVirtualPorts[iPort] != uPort); iPort++) + ; + if (iPort < SOCKET_MAXVIRTUALPORTS) + { + // acquire socket critical section + NetCritEnter(&pSocket->RecvCrit); + + // check to see if the socket is bound + if (pSocket->bVirtual && (pSocket->uVirtualPort != 0)) + { + NetPrintf(("dirtynetunix: [%p] failed to bind socket to %u which was already bound to port %u virtual\n", pSocket, uPort, pSocket->uVirtualPort)); + NetCritLeave(&pSocket->RecvCrit); + return(pSocket->iLastError = SOCKERR_INVALID); + } + + // close winsock socket + NetPrintf(("dirtynetunix: [%p] making socket bound to port %d virtual\n", pSocket, uPort)); + if (pSocket->uSocket != INVALID_SOCKET) + { + shutdown(pSocket->uSocket, SOCK_NOSEND); + close(pSocket->uSocket); + pSocket->uSocket = INVALID_SOCKET; + } + /* increase socket queue size; this protects virtual sockets from having data pushed into + them and overwriting previous data that hasn't been read yet */ + pSocket->pRecvQueue = SocketPacketQueueResize(pSocket->pRecvQueue, 4, pState->iMemGroup, pState->pMemGroupUserData); + // mark socket as virtual + pSocket->uVirtualPort = uPort; + pSocket->bVirtual = TRUE; + + // release socket critical section + NetCritLeave(&pSocket->RecvCrit); + return(0); + } + } + } + + // translate IPv4 -> IPv6 address (if needed) + if (pName->sa_family != AF_INET6) + { + ds_memclr(&SockAddr6, sizeof(SockAddr6)); + SockAddr6.sin6_family = AF_INET6; + SockAddr6.sin6_port = SocketHtons(SockaddrInGetPort(pName)); + pName = SocketAddrMapTranslate(&pState->AddrMap, (struct sockaddr *)&SockAddr6, pName, &iNameLen); + } + + // do the bind + if ((iResult = bind(pSocket->uSocket, pName, iNameLen)) < 0) + { + NetPrintf(("dirtynetunix: bind() to port %d failed (err=%s)\n", SockaddrInGetPort(pName), DirtyErrGetName(errno))); + } + else if (SockaddrInGetPort(&pSocket->LocalAddr) == 0) + { + iNameLen = sizeof(pSocket->LocalAddr); + iResult = getsockname(pSocket->uSocket, &pSocket->LocalAddr, (socklen_t *)&iNameLen); + NetPrintf(("dirtynetunix: bind(port=0) succeeded, local address=%a:%d.\n", + SockaddrInGetAddr(&pSocket->LocalAddr), + SockaddrInGetPort(&pSocket->LocalAddr))); + } + + pSocket->iLastError = _SocketTranslateError(iResult); + return(pSocket->iLastError); +} + +/*F********************************************************************************/ +/*! + \Function SocketConnect + + \Description + Initiate a connection attempt to a remote host. + + \Input *pSocket - socket reference + \Input *pName - pointer to name of socket to connect to + \Input iNameLen - length of name + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Notes + Only has real meaning for stream protocols. For a datagram protocol, this + just sets the default remote host. + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketConnect(SocketT *pSocket, struct sockaddr *pName, int32_t iNameLen) +{ + struct sockaddr_in6 SockAddr6; + int32_t iResult; + + // initialize family of Sockaddr6 + SockaddrInit6(&SockAddr6, AF_INET6); + + // translate to IPv6 if required + pName = SocketAddrMapTranslate(&_Socket_pState->AddrMap, (struct sockaddr *)&SockAddr6, pName, &iNameLen); + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetunix: connecting to %A\n", pName)); + + // do the connect + pSocket->iOpened = 0; + if ((iResult = connect(pSocket->uSocket, pName, iNameLen)) == 0) + { + // if connect succeeded (usually for udp sockets) or attempting to establish a non-blocking connection save correct address + ds_memcpy_s(&pSocket->RemoteAddr, sizeof(pSocket->RemoteAddr), pName, sizeof(*pName)); + } + else if (errno == EHOSTUNREACH) + { + /* if host is unreachable, purge from hostname cache (if present). this is helpful in + situations where the addressing scheme has changed; for example if a device switches + from an IPv4 hosted connection to an IPv6 connection. in such a case the old (now + invalid) address is purged from the cache more quickly than if we waited for it to + expire from the normal cache timeout */ + SocketHostnameCacheDel(_Socket_pState->pHostnameCache, NULL, SockaddrInGetAddr(pName), _Socket_pState->iVerbose); + } + else if (errno != EINPROGRESS) + { + NetPrintf(("dirtynetunix: connect() failed (err=%s)\n", DirtyErrGetName(errno))); + } + + pSocket->iLastError = _SocketTranslateError(iResult); + return(pSocket->iLastError); +} + +/*F********************************************************************************/ +/*! + \Function SocketListen + + \Description + Start listening for an incoming connection on the socket. The socket must already + be bound and a stream oriented connection must be in use. + + \Input *pSocket - socket reference to bound socket (see SocketBind()) + \Input iBacklog - number of pending connections allowed + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketListen(SocketT *pSocket, int32_t iBacklog) +{ + int32_t iResult; + + // do the listen + if ((iResult = listen(pSocket->uSocket, iBacklog)) < 0) + { + NetPrintf(("dirtynetunix: listen() failed (err=%s)\n", DirtyErrGetName(errno))); + } + + pSocket->iLastError = _SocketTranslateError(iResult); + return(pSocket->iLastError); +} + +/*F********************************************************************************/ +/*! + \Function SocketAccept + + \Description + Accept an incoming connection attempt on a socket. + + \Input *pSocket - socket reference to socket in listening state (see SocketListen()) + \Input *pAddr - pointer to storage for address of the connecting entity, or NULL + \Input *pAddrLen - pointer to storage for length of address, or NULL + + \Output + SocketT * - the accepted socket, or NULL if not available + + \Notes + The integer pointed to by addrlen should on input contain the number of characters + in the buffer addr. On exit it will contain the number of characters in the + output address. + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +SocketT *SocketAccept(SocketT *pSocket, struct sockaddr *pAddr, int32_t *pAddrLen) +{ + SocketT *pOpen = NULL; + int32_t iIncoming; + + pSocket->iLastError = SOCKERR_INVALID; + + // make sure we have a socket + if (pSocket->uSocket == INVALID_SOCKET) + { + NetPrintf(("dirtynetunix: accept() called on invalid socket\n")); + return(NULL); + } + + // make sure turn parm is valid + if ((pAddr != NULL) && (*pAddrLen < (signed)sizeof(struct sockaddr))) + { + NetPrintf(("dirtynetunix: accept() called with invalid address\n")); + return(NULL); + } + + // perform inet6 accept + if (pSocket->iFamily == AF_INET6) + { + struct sockaddr_in6 SockAddr6; + socklen_t iAddrLen; + + SockaddrInit6(&SockAddr6, AF_INET6); + SockAddr6.sin6_port = SocketNtohs(SockaddrInGetPort(pAddr)); + SockAddr6.sin6_addr = in6addr_any; + iAddrLen = sizeof(SockAddr6); + + iIncoming = accept(pSocket->uSocket, (struct sockaddr *)&SockAddr6, &iAddrLen); + if (iIncoming != -1) + { + // Allocate socket structure and install in list + pOpen = _SocketOpen(iIncoming, pSocket->iFamily, pSocket->iType, pSocket->iProto, 1); + pSocket->iLastError = SOCKERR_NONE; + + #if defined(DIRTYCODE_ANDROID) || defined(DIRTYCODE_LINUX) + /* http://linux.die.net/man/2/accept: + On Linux, the new socket returned by accept() does not inherit file status flags + such as O_NONBLOCK and O_ASYNC from the listening socket. This behaviour differs + from the canonical BSD sockets implementation. */ + // set nonblocking operation + if (fcntl(iIncoming, F_SETFL, O_NONBLOCK) < 0) + { + NetPrintf(("dirtynetunix: error trying to make socket non-blocking (err=%d)\n", errno)); + } + #endif + // translate ipv6 to ipv4 virtual address + SocketAddrMapAddress(&_Socket_pState->AddrMap, (const struct sockaddr *)&SockAddr6, sizeof(SockAddr6)); + // save translated connecting info for caller + SockaddrInit(pAddr, AF_INET); + SocketAddrMapTranslate(&_Socket_pState->AddrMap, pAddr, (struct sockaddr *)&SockAddr6, pAddrLen); + } + else + { + pSocket->iLastError = _SocketTranslateError(iIncoming); + if (errno != EWOULDBLOCK) + { + NetPrintf(("dirtynetunix: accept() failed (err=%s)\n", DirtyErrGetName(errno))); + } + } + } + + // return the socket + return(pOpen); +} + +/*F********************************************************************************/ +/*! + \Function SocketSendto + + \Description + Send data to a remote host. The destination address is supplied along with + the data. Should only be used with datagram sockets as stream sockets always + send to the connected peer. + + \Input *pSocket - socket reference + \Input *pBuf - the data to be sent + \Input iLen - size of data + \Input iFlags - unused + \Input *pTo - the address to send to (NULL=use connection address) + \Input iToLen - length of address + + \Output + int32_t - standard network error code (SOCKERR_xxx) + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketSendto(SocketT *pSocket, const char *pBuf, int32_t iLen, int32_t iFlags, const struct sockaddr *pTo, int32_t iToLen) +{ + SocketStateT *pState = _Socket_pState; + int32_t iResult = -1; + + if (pSocket->bSendCbs) + { + // if installed, give socket callback right of first refusal + if ((iResult = SocketSendCallbackInvoke(&pState->aSendCbEntries[0], pSocket, pSocket->iType, pBuf, iLen, pTo)) > 0) + { + return(iResult); + } + } + + // make sure socket ref is valid + if (pSocket->uSocket < 0) + { + #if DIRTYCODE_LOGGING + uint32_t uAddr = 0, uPort = 0; + if (pTo) + { + uAddr = SockaddrInGetAddr(pTo); + uPort = SockaddrInGetPort(pTo); + } + NetPrintf(("dirtynetunix: attempting to send to %a:%d on invalid socket\n", uAddr, uPort)); + #endif + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + // handle optional data rate throttling + if ((iLen = SocketRateThrottle(&pSocket->SendRate, pSocket->iType, iLen, "send")) == 0) + { + return(0); + } + + // use appropriate version + if (pTo == NULL) + { + if ((iResult = (int32_t)send(pSocket->uSocket, pBuf, iLen, 0)) < 0) + { + if (errno != EWOULDBLOCK) + NetPrintf(("dirtynetunix: send() failed (err=%s)\n", DirtyErrGetName(errno))); + } + } + else + { + struct sockaddr_in6 SockAddr6; + struct sockaddr *pTo6; + + // do the send + #if SOCKET_VERBOSE + NetPrintf(("dirtynetunix: sending %d bytes to %a:%d\n", iLen, SockaddrInGetAddr(pTo), SockaddrInGetPort(pTo))); + #endif + + SockaddrInit6(&SockAddr6, AF_INET6); + iToLen = sizeof(SockAddr6); + pTo6 = SocketAddrMapTranslate(&pState->AddrMap, (struct sockaddr *)&SockAddr6, pTo, &iToLen); + if ((iResult = (int32_t)sendto(pSocket->uSocket, pBuf, iLen, 0, pTo6, iToLen)) < 0) + { + NetPrintf(("dirtynetunix: sendto(%A) failed (err=%s)\n", pTo6, DirtyErrGetName(errno))); + } + } + // translate error + pSocket->iLastError = iResult = _SocketTranslateError(iResult); + if (iResult == SOCKERR_BADPIPE) + { + // recreate socket (iLastError will be set in _SocketReopen) + if (_SocketReopen(pSocket)) + { + // success, re-send (should either work or error out) + return(SocketSendto(pSocket, pBuf, iLen, iFlags, pTo, iToLen)); + } + // failed to recreate the socket, error + } + + // update data rate estimation + SocketRateUpdate(&pSocket->SendRate, iResult, "send"); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function SocketRecvfrom + + \Description + Receive data from a remote host. If socket is a connected stream, then data can + only come from that source. A datagram socket can receive from any remote host. + + \Input *pSocket - socket reference + \Input *pBuf - buffer to receive data + \Input iLen - length of recv buffer + \Input iFlags - unused + \Input *pFrom - address data was received from (NULL=ignore) + \Input *pFromLen - length of address + + \Output + int32_t - positive=data bytes received, else standard error code + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketRecvfrom(SocketT *pSocket, char *pBuf, int32_t iLen, int32_t iFlags, struct sockaddr *pFrom, int32_t *pFromLen) +{ + int32_t iRecv = -1, iErrno = 0; + + // clear "hasdata" hint + pSocket->bHasData = 0; + + // handle rate throttling, if enabled + if ((iLen = SocketRateThrottle(&pSocket->RecvRate, pSocket->iType, iLen, "recv")) == 0) + { + return(0); + } + // handle if the socket was killed + if (pSocket->pKill != NULL) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + /* sockets marked for async recv had actual receive operation take place in the thread. sockets marked as virtual have the + packet pushed into them (specific to unix as we disable async receive for singlethreaded mode). */ + if ((pSocket->bAsyncRecv == TRUE) || (pSocket->bVirtual == TRUE)) + { + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + + /* given the socket could be either a TCP or UDP socket we handle the no data condition the same. + this is due to the below, when we do error conversion we override the system error with EWOULDBLOCK because + with TCP zero would be mean closed. if we are doing direct recv calls then the translation will + convert based on the errno returned after the call */ + if ((iRecv = _SocketRecvfromPacketQueue(pSocket, pBuf, iLen, pFrom, pFromLen)) == 0) + { + iRecv = -1; + iErrno = EWOULDBLOCK; + } + + // when data is obtained from the packet queue, we lose visibility on system socket errors + pSocket->iLastError = SOCKERR_NONE; + + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + } + else // non-async recvthread socket + { + // do direct recv call + if (((iRecv = _SocketRecvfrom(pSocket, pBuf, iLen, pFrom, pFromLen)) < 0) && ((iErrno = errno) != EAGAIN)) + { + NetPrintf(("dirtynetunix: _SocketRecvfrom() failed on a SOCK_STREAM socket (err=%s)\n", DirtyErrGetName(iErrno))); + } + } + + // do error conversion + iRecv = (iRecv == 0) ? SOCKERR_CLOSED : _SocketTranslateError2(iRecv, iErrno); + + // update data rate estimation + SocketRateUpdate(&pSocket->RecvRate, iRecv, "recv"); + + // return the error code + pSocket->iLastError = iRecv; + return(iRecv); +} + +/*F********************************************************************************/ +/*! + \Function SocketInfo + + \Description + Return information about an existing socket. + + \Input *pSocket - socket reference + \Input iInfo - selector for desired information + \Input iData - selector specific + \Input *pBuf - return buffer + \Input iLen - buffer length + + \Output + int32_t - selector-specific + + \Notes + iInfo can be one of the following: + + \verbatim + 'addr' - returns interface address; iData=destination address for routing + 'bind' - return bind data (if pSocket == NULL, get socket bound to given port) + 'bndu' - return bind data (only with pSocket=NULL, get SOCK_DGRAM socket bound to given port) + 'conn' - connection status + 'eth0' - returns 0 if we have a valid ip for eth0 device. negative if error or no address found (Android Only) + 'ethr'/'macx' - local ethernet address (returned in pBuf), 0=success, negative=error + '?ip6' - return TRUE if ipv4 address specified in iData is virtual, and fill in pBuf with ipv6 address if not NULL + 'maxp' - return configured max packet size + 'maxr' - return configured max recv rate (bytes/sec; zero=uncapped) + 'maxs' - return configured max send rate (bytes/sec; zero=uncapped) + 'pdrp' - return socket packet queue number of packets dropped + 'peer' - peer info (only valid if connected) + 'pmax' - return socket packet queue max depth + 'pnum' - return socket packet queue current depth + 'ratr' - return current recv rate estimation (bytes/sec) + 'rats' - return current send rate estimation (bytes/sec) + 'read' - return if socket has data available for reading + 'sdcf' - get installed send callback function pointer (iData specifies index in array) + 'sdcu' - get installed send callback userdata pointer (iData specifies index in array) + 'serr' - last socket error + 'psiz' - return socket packet queue max size + 'sock' - return socket associated with the specified DirtySock socket + 'spam' - return debug level for debug output + 'stat' - socket status + 'virt' - TRUE if socket is virtual, else FALSE + 'wan0' - returns true if we have a valid ip for wlan0 device. Negative if error or no address found (Android Only) + \endverbatim + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketInfo(SocketT *pSocket, int32_t iInfo, int32_t iData, void *pBuf, int32_t iLen) +{ + SocketStateT *pState = _Socket_pState; + + // always zero results by default + if (pBuf != NULL) + { + ds_memclr(pBuf, iLen); + } + + // handle global socket options + if (pSocket == NULL) + { + return(_SocketInfoGlobal(iInfo, iData, pBuf, iLen)); + } + + // return local bind data + if (iInfo == 'bind') + { + int32_t iResult = -1; + if (pSocket->bVirtual == TRUE) + { + SockaddrInit((struct sockaddr *)pBuf, AF_INET); + SockaddrInSetPort((struct sockaddr *)pBuf, pSocket->uVirtualPort); + iResult = 0; + } + else if (pSocket->uSocket != INVALID_SOCKET) + { + struct sockaddr_in6 SockAddr6; + iLen = sizeof(SockAddr6); + if ((iResult = getsockname(pSocket->uSocket, (struct sockaddr *)&SockAddr6, (socklen_t *)&iLen)) == 0) + { + SockaddrInit((struct sockaddr *)pBuf, AF_INET); + SockaddrInSetPort((struct sockaddr *)pBuf, SocketHtons(SockAddr6.sin6_port)); + SockaddrInSetAddr((struct sockaddr *)pBuf, SocketAddrMapAddress(&pState->AddrMap, (struct sockaddr *)&SockAddr6, sizeof(SockAddr6))); + } + iResult = _SocketTranslateError(iResult); + } + return(iResult); + } + + // return configured max recv rate + if (iInfo == 'maxr') + { + return(pSocket->RecvRate.uMaxRate); + } + + // return configured max send rate + if (iInfo == 'maxs') + { + return(pSocket->SendRate.uMaxRate); + } + + // return whether the socket is virtual or not + if (iInfo == 'virt') + { + return(pSocket->bVirtual); + } + + /* + make sure the socket is alive + ** AFTER THIS POINT WE ENSURE THE SOCKET DESCRIPTOR AND PACKET QUEUE ARE VALID ** + */ + if (pSocket->pKill != NULL) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + + // return local peer data + if ((iInfo == 'conn') || (iInfo == 'peer')) + { + if (iLen >= (signed)sizeof(pSocket->LocalAddr)) + { + getpeername(pSocket->uSocket, pBuf, (socklen_t *)&iLen); + } + return(0); + } + + // get packet queue info + if ((iInfo == 'pdrp') || (iInfo == 'pmax') || (iInfo == 'pnum') || (iInfo == 'psiz')) + { + int32_t iResult; + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + // get packet queue status + iResult = SocketPacketQueueStatus(pSocket->pRecvQueue, iInfo); + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + // return success + return(iResult); + } + + // return current recv rate estimation + if (iInfo == 'ratr') + { + return(pSocket->RecvRate.uCurRate); + } + + // return current send rate estimation + if (iInfo == 'rats') + { + return(pSocket->SendRate.uCurRate); + } + + // return if socket has data + if (iInfo == 'read') + { + return(pSocket->bHasData); + } + + // return last socket error + if (iInfo == 'serr') + { + return(pSocket->iLastError); + } + + // return unix socket ref + if (iInfo == 'sock') + { + return(pSocket->uSocket); + } + + // return socket status + if (iInfo == 'stat') + { + struct pollfd PollFd; + + // if not a connected socket, return TRUE + if (pSocket->iType != SOCK_STREAM) + { + return(1); + } + + // if not connected, use poll to determine connect + if (pSocket->iOpened == 0) + { + ds_memclr(&PollFd, sizeof(PollFd)); + PollFd.fd = pSocket->uSocket; + PollFd.events = POLLOUT; + if (poll(&PollFd, 1, 0) != 0) + { + /* + Experimentation shows that on connect failed: + Android: (only) POLLERR is returned. + iOS5: (only) POLLHUP is returned. + Linux: both POLLERR and POLLHUP are returned (POLLERR|POLLHUP). + + To make the code work on all platforms, we test if any of POLLERR and POLLHUP was set. + */ + if ((PollFd.revents & POLLERR) || (PollFd.revents & POLLHUP)) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetunix: read exception on connect\n")); + pSocket->iOpened = -1; + } + // if socket is writable, that means connect succeeded + else if (PollFd.revents & POLLOUT) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetunix: connection open\n")); + pSocket->iOpened = 1; + } + } + } + + /* if previously connected, make sure connect still valid. we only do this when not doing async receive for two + reasons + 1. there is a race condition between the poll waking up and querying the bytes available. if the bytes are + read on the receive thread between the poll and ioctl then it would think the socket is closed because the + socket has already been drained + 2. our reasoning behind using the async receive thread could be the cost of recv, plus other calls may be + expensive as well. the async receive thread will already set the correct state on the socket thus we can + skip the query and return iOpened back to the user */ + if (!pSocket->bAsyncRecv && (pSocket->iOpened > 0)) + { + ds_memclr(&PollFd, sizeof(PollFd)); + PollFd.fd = pSocket->uSocket; + PollFd.events = POLLIN; + if (poll(&PollFd, 1, 0) != 0) + { + // if we got an exception, that means connect failed (usually closed by remote peer) + if ((PollFd.revents & POLLERR) || (PollFd.revents & POLLHUP)) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetunix: connection failure\n")); + pSocket->iOpened = -1; + } + else if (PollFd.revents & POLLIN) + { + int32_t iAvailBytes = 1; + // get number of bytes for read (might be less than actual bytes, so it can only be used for zero-test) + if (ioctl(pSocket->uSocket, FIONREAD, &iAvailBytes) != 0) + { + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetunix: ioctl(FIONREAD) failed (err=%s).\n", DirtyErrGetName(errno))); + } + // if socket is readable but there's no data available for read, connect was closed + else if (iAvailBytes == 0) + { + pSocket->iLastError = SOCKERR_CLOSED; + NetPrintfVerbose((pSocket->iVerbose, 0, "dirtynetunix: connection closed\n")); + pSocket->iOpened = -1; + } + } + } + } + /* if we still have packets in the queue, tell the caller that we are still open. this makes sure they read the + complete stream of data */ + else if (pSocket->bAsyncRecv && (pSocket->iOpened < 0) && (SocketInfo(pSocket, 'pnum', 0, NULL, 0) != 0)) + { + return(1); + } + + // return connect status + return(pSocket->iOpened); + } + + // unhandled option? + NetPrintf(("dirtynetunix: unhandled SocketInfo() option '%C'\n", iInfo)); + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function SocketCallback + + \Description + Register a callback routine for notification of socket events. Also includes + timeout support. + + \Input *pSocket - socket reference + \Input iMask - valid callback events (CALLB_NONE, CALLB_SEND, CALLB_RECV) + \Input iIdle - if nonzero, specifies the number of ticks between idle calls + \Input *pRef - user data to be passed to proc + \Input *pProc - user callback + + \Output + int32_t - zero + + \Notes + A callback will reset the idle timer, so when specifying a callback and an + idle processing time, the idle processing time represents the maximum elapsed + time between calls. + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketCallback(SocketT *pSocket, int32_t iMask, int32_t iIdle, void *pRef, int32_t (*pProc)(SocketT *pSock, int32_t iFlags, void *pRef)) +{ + pSocket->uCallIdle = iIdle; + pSocket->iCallMask = iMask; + pSocket->pCallRef = pRef; + pSocket->pCallback = pProc; + pSocket->uCallLast = NetTick() - iIdle; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function SocketControl + + \Description + Process a control message (type specific operation) + + \Input *pSocket - socket to control, or NULL for module-level option + \Input iOption - the option to pass + \Input iData1 - message specific parm + \Input *pData2 - message specific parm + \Input *pData3 - message specific parm + + \Output + int32_t - message specific result (-1=unsupported message) + + \Notes + iOption can be one of the following: + + \verbatim + 'arcv' - set async receive enable/disable (default enabled for DGRAM/RAW, disabled for TCP) + 'conn' - init network stack + 'disc' - bring down network stack + 'idle' - perform any network connection related processing + '+ip6' - add an IPv6 address into the mapping table and return a virtual IPv4 address to reference it + '-ip6' - del an IPv6 address from the mapping table + '~ip6' - remap an existing IPv6 address in the mapping table + 'keep' - set TCP keep-alive settings on Linux (iData1=enable/disable, iData2=keep-alive time, iData3=keep-alive interval) + 'maxp' - set max udp packet size + 'maxr' - set max recv rate (bytes/sec; zero=uncapped) + 'maxs' - set max send rate (bytes/sec; zero=uncapped) + 'nbio' - set nonblocking/blocking mode (TCP only, iData1=TRUE (nonblocking) or FALSE (blocking)) + 'ndly' - set TCP_NODELAY state for given stream socket (iData1=zero or one) + 'pdev' - set simulated packet deviation + 'plat' - set simulated packet latency + 'plos' - set simulated packet loss + 'poll' - block waiting on input from socket list (iData1=ms to block) + 'poln' - block waiting on input from socket list (iData1=us to block), unsupported on apple platforms fallback to 'poll' behavior + 'pque' - set socket packet queue depth + 'push' - push data into given socket receive buffer (iData1=size, pData2=data ptr, pData3=sockaddr ptr) + 'radr' - set SO_REUSEADDR On the specified socket + 'rbuf' - set socket recv buffer size + 'sbuf' - set socket send buffer size + 'scbk' - enable/disable "send callbacks usage" on specified socket (defaults to enable) + 'sdcb' - set/unset send callback (iData1=TRUE for set - FALSE for unset, pData2=callback, pData3=callref) + 'soli' - set SO_LINGER On the specified socket, iData1 is timeout in seconds + 'spam' - set debug level for debug output + 'vadd' - add a port to virtual port list + 'vdel' - del a port from virtual port list + \endverbatim + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t SocketControl(SocketT *pSocket, int32_t iOption, int32_t iData1, void *pData2, void *pData3) +{ + SocketStateT *pState = _Socket_pState; + int32_t iResult; + + // handle global controls + if (pSocket == NULL) + { + return(_SocketControlGlobal(iOption, iData1, pData2, pData3)); + } + + // set async recv enable + if (iOption == 'arcv') + { + // set socket async recv flag + pSocket->bAsyncRecv = iData1 ? TRUE : FALSE; + return(0); + } + + // set max recv rate + if (iOption == 'maxr') + { + NetPrintf(("dirtynetunix: setting max recv rate to %d bytes/sec\n", iData1)); + pSocket->RecvRate.uMaxRate = iData1; + return(0); + } + // set max send rate + if (iOption == 'maxs') + { + NetPrintf(("dirtynetunix: setting max send rate to %d bytes/sec\n", iData1)); + pSocket->SendRate.uMaxRate = iData1; + return(0); + } + // enable/disable "send callbacks usage" on specified socket (defaults to enable) + if (iOption == 'scbk') + { + if (pSocket->bSendCbs != (iData1?TRUE:FALSE)) + { + NetPrintf(("dirtynetunix: send callbacks usage changed from %s to %s for socket ref %p\n", (pSocket->bSendCbs?"ON":"OFF"), (iData1?"ON":"OFF"), pSocket)); + pSocket->bSendCbs = (iData1?TRUE:FALSE); + } + return(0); + } + // set debug spam level + if (iOption == 'spam') + { + // per-socket debug level + pSocket->iVerbose = iData1; + return(0); + } + + /* + make sure the socket is alive + ** AFTER THIS POINT WE ENSURE THE SOCKET DESCRIPTOR AND PACKET QUEUE ARE VALID ** + */ + if (pSocket->pKill != NULL) + { + pSocket->iLastError = SOCKERR_INVALID; + return(pSocket->iLastError); + } + +#if defined(DIRTYCODE_LINUX) + // configure TCP keep-alive + if (iOption == 'keep') + { + uint32_t bKeepAlive, uKeepAliveTime, uKeepAliveInterval; + + if (pSocket->iType != SOCK_STREAM) + { + NetPrintf(("dirtynetunix: [%p] 'keep' control can only be used on a SOCK_STREAM socket\n", pSocket)); + return(-1); + } + + bKeepAlive = (uint8_t)iData1; //!< enable/disable keep-alive option + uKeepAliveTime = bKeepAlive ? *(uint32_t *)pData2 / 1000 : 0; //!< get keep-alive time and convert to seconds + uKeepAliveInterval = bKeepAlive ? *(uint32_t *)pData3 / 1000 : 0; //!< get keep-alive interval and convert to seconds + + if ((iResult = setsockopt(pSocket->uSocket, SOL_SOCKET, SO_KEEPALIVE, &bKeepAlive, sizeof(bKeepAlive))) != 0) + { + pSocket->iLastError = _SocketTranslateError(iResult); + NetPrintf(("dirtynetunix: [%p] failed to set SO_KEEPALIVE to %s (err=%d)\n", pSocket, bKeepAlive ? "true" : "false", pSocket->iLastError)); + } + else if ((iResult = setsockopt(pSocket->uSocket, SOL_TCP, TCP_KEEPIDLE, &uKeepAliveTime, sizeof(uKeepAliveTime))) != 0) + { + pSocket->iLastError = _SocketTranslateError(iResult); + NetPrintf(("dirtynetunix: [%p] failed to set TCP_KEEPIDLE to %ums (err=%d)\n", pSocket, uKeepAliveTime*1000, pSocket->iLastError)); + } + else if ((iResult = setsockopt(pSocket->uSocket, SOL_TCP, TCP_KEEPINTVL, &uKeepAliveInterval, sizeof(uKeepAliveInterval))) != 0) + { + pSocket->iLastError = _SocketTranslateError(iResult); + NetPrintf(("dirtynetunix: [%p] failed to set TCP_KEEPINTVL to %ums (err=%d)\n", pSocket, uKeepAliveInterval*1000, pSocket->iLastError)); + } + else + { + pSocket->iLastError = SOCKERR_NONE; + + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetunix: [%p] successfully set the TCP keep-alive options (enabled=%s, timeout=%ums, interval=%ums)\n", + pSocket, bKeepAlive ? "true" : "false", uKeepAliveTime*1000, uKeepAliveInterval*1000)); + } + + return(pSocket->iLastError); + } +#endif + // if a stream socket, set nonblocking/blocking mode + if ((iOption == 'nbio') && (pSocket->iType == SOCK_STREAM)) + { + int32_t iVal = fcntl(pSocket->uSocket, F_GETFL, O_NONBLOCK); + iVal = iData1 ? (iVal | O_NONBLOCK) : (iVal & ~O_NONBLOCK); + iResult = fcntl(pSocket->uSocket, F_SETFL, iVal); + pSocket->iLastError = _SocketTranslateError(iResult); + NetPrintf(("dirtynetunix: setting socket:0x%x to %s mode %s (LastError=%d).\n", pSocket, iData1 ? "nonblocking" : "blocking", iResult ? "failed" : "succeeded", pSocket->iLastError)); + return(pSocket->iLastError); + } + // if a stream socket, set TCP_NODELAY state + if ((iOption == 'ndly') && (pSocket->iType == SOCK_STREAM)) + { + iResult = setsockopt(pSocket->uSocket, IPPROTO_TCP, TCP_NODELAY, &iData1, sizeof(iData1)); + pSocket->iLastError = _SocketTranslateError(iResult); + return(pSocket->iLastError); + } + // set simulated packet loss or packet latency + if ((iOption == 'pdev') || (iOption == 'plat') || (iOption == 'plos')) + { + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + // forward selector to packet queue + iResult = SocketPacketQueueControl(pSocket->pRecvQueue, iOption, iData1); + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + return(iResult); + } + + // change packet queue size + if (iOption == 'pque') + { + // acquire socket receive critical section + NetCritEnter(&pSocket->RecvCrit); + // resize the queue + pSocket->pRecvQueue = SocketPacketQueueResize(pSocket->pRecvQueue, iData1, pState->iMemGroup, pState->pMemGroupUserData); + // release socket receive critical section + NetCritLeave(&pSocket->RecvCrit); + // return success + return(0); + } + + // push data into receive buffer + if (iOption == 'push') + { + // acquire socket critical section + NetCritEnter(&pSocket->RecvCrit); + + // don't allow data that is too large (for the buffer) to be pushed + if (iData1 > SOCKET_MAXUDPRECV) + { + NetPrintf(("dirtynetunix: request to push %d bytes of data discarded (max=%d)\n", iData1, SOCKET_MAXUDPRECV)); + NetCritLeave(&pSocket->RecvCrit); + return(-1); + } + + // add packet to queue + SocketPacketQueueAdd(pSocket->pRecvQueue, (uint8_t *)pData2, iData1, (struct sockaddr *)pData3); + // remember we have data + pSocket->bHasData = 1; + + // release socket critical section + NetCritLeave(&pSocket->RecvCrit); + + // see if we should issue callback + if ((pSocket->pCallback != NULL) && (pSocket->iCallMask & CALLB_RECV)) + { + pSocket->pCallback(pSocket, 0, pSocket->pCallRef); + } + return(0); + } + // set SO_REUSEADDR + if (iOption == 'radr') + { + iResult = setsockopt(pSocket->uSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&iData1, sizeof(iData1)); + pSocket->iLastError = _SocketTranslateError(iResult); + return(pSocket->iLastError); + } + // set socket receive buffer size + if ((iOption == 'rbuf') || (iOption == 'sbuf')) + { + int32_t iOldSize, iNewSize; + int32_t iSockOpt = (iOption == 'rbuf') ? SO_RCVBUF : SO_SNDBUF; + socklen_t uOptLen = 4; + + // get current buffer size + getsockopt(pSocket->uSocket, SOL_SOCKET, iSockOpt, (char *)&iOldSize, &uOptLen); + + // set new size + iResult = setsockopt(pSocket->uSocket, SOL_SOCKET, iSockOpt, (const char *)&iData1, sizeof(iData1)); + if ((pSocket->iLastError = _SocketTranslateError(iResult)) == SOCKERR_NONE) + { + // save new buffer size + if (iOption == 'rbuf') + { + pSocket->iRbufSize = iData1; + } + else + { + pSocket->iSbufSize = iData1; + } + } + + // get new size + getsockopt(pSocket->uSocket, SOL_SOCKET, iSockOpt, (char *)&iNewSize, &uOptLen); + #if defined(DIRTYCODE_LINUX) + /* as per SO_RCVBUF/SO_SNDBUF documentation: "The kernel doubles the value (to allow space for bookkeeping + overhead) when it is set using setsockopt(), and this doubled value is returned by getsockopt()." To + account for this we halve the getsockopt() value so it matches what we requested. */ + iNewSize /= 2; + #endif + NetPrintf(("dirtynetunix: setsockopt(%s) changed buffer size from %d to %d\n", (iOption == 'rbuf') ? "SO_RCVBUF" : "SO_SNDBUF", + iOldSize, iNewSize)); + + return(pSocket->iLastError); + } + // set SO_LINGER + if (iOption == 'soli') + { + struct linger lingerOptions; + lingerOptions.l_onoff = TRUE; + lingerOptions.l_linger = iData1; + iResult = setsockopt(pSocket->uSocket, SOL_SOCKET, SO_LINGER, &lingerOptions, sizeof(lingerOptions)); + pSocket->iLastError = _SocketTranslateError(iResult); + return(pSocket->iLastError); + } + // unhandled + NetPrintf(("dirtynetunix: unhandled control option '%C'\n", iOption)); + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function SocketGetLocalAddr + + \Description + Returns the "external" local address (ie, the address as a machine "out on + the Internet" would see as the local machine's address). + + \Output + uint32_t - local address + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +uint32_t SocketGetLocalAddr(void) +{ + SocketStateT *pState = _Socket_pState; + if (pState->uLocalAddr == 0) + { + pState->uLocalAddr = SocketInfo(NULL, 'addr', 0, NULL, 0); + } + return(pState->uLocalAddr); +} + +/*F********************************************************************************/ +/*! + \Function SocketLookup + + \Description + Lookup a host by name and return the corresponding Internet address. Uses + a callback/polling system since the socket library does not allow blocking. + + \Input *pText - pointer to null terminated address string + \Input iTimeout - number of milliseconds to wait for completion + + \Output + HostentT * - hostent struct that includes callback vectors + + \Version 06/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +HostentT *SocketLookup(const char *pText, int32_t iTimeout) +{ + SocketStateT *pState = _Socket_pState; + SocketLookupPrivT *pPriv; + int32_t iAddr, iResult; + HostentT *pHost, *pHostRef; + DirtyThreadConfigT ThreadConfig; + + NetPrintf(("dirtynetunix: looking up address for host '%s'\n", pText)); + + // dont allow negative timeouts + if (iTimeout < 0) + { + return(NULL); + } + + // create new structure + pPriv = DirtyMemAlloc(sizeof(*pPriv), SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + ds_memclr(pPriv, sizeof(*pPriv)); + pHost = &pPriv->Host; + + // setup callbacks + pHost->Done = &_SocketLookupDone; + pHost->Free = &_SocketLookupFree; + // copy over the target address + ds_strnzcpy(pHost->name, pText, sizeof(pHost->name)); + + // look for refcounted lookup + if ((pHostRef = SocketHostnameAddRef(&pState->pHostList, &pPriv->Host, TRUE)) != NULL) + { + DirtyMemFree(pPriv, SOCKET_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + return(pHostRef); + } + + // check for dot notation, then check hostname cache + if (((iAddr = SocketInTextGetAddr(pText)) != 0) || ((iAddr = SocketHostnameCacheGet(pState->pHostnameCache, pText, pState->iVerbose)) != 0)) + { + // we've got a dot-notation address + pHost->addr = iAddr; + pHost->done = 1; + // return completed record + return(pHost); + } + + /* add an extra refcount for the thread; this ensures the host structure survives until the thread + is done with it. this must be done before thread creation. */ + pHost->refcount += 1; + + // configure threading + ds_memclr(&ThreadConfig, sizeof(ThreadConfig)); + ThreadConfig.pName = "SocketLookup"; + ThreadConfig.iAffinity = NetConnStatus('affn', 0, NULL, 0); + ThreadConfig.iVerbosity = pState->iVerbose-1; + + // create dns lookup thread + if ((iResult = DirtyThreadCreate(_SocketLookupThread, pPriv, &ThreadConfig)) < 0) + { + NetPrintf(("dirtynetunix: failed to create lookup thread (err=%d)\n", iResult)); + pPriv->Host.done = -1; + // remove refcount we just added + pHost->refcount -= 1; + } + + // return the host reference + return(pHost); +} + +/*F********************************************************************************/ +/*! + \Function SocketHost + + \Description + Return the host address that would be used in order to communicate with + the given destination address. + + \Input *pHost - [out] local sockaddr struct + \Input iHostlen - length of structure (sizeof(host)) + \Input *pDest - remote sockaddr struct + \Input iDestlen - length of structure (sizeof(dest)) + + \Output + int32_t - zero=success, negative=error + + \Version 12/12/2003 (sbevan) +*/ +/********************************************************************************F*/ +int32_t SocketHost(struct sockaddr *pHost, int32_t iHostlen, const struct sockaddr *pDest, int32_t iDestlen) +{ +#if defined(DIRTYCODE_APPLEIOS) + SocketStateT *pState = _Socket_pState; + + // must be same kind of addresses + if (iHostlen != iDestlen) + { + return(-1); + } + + // do family specific lookup + if (pDest->sa_family == AF_INET) + { + // special case destination of zero or loopback to return self + if ((SockaddrInGetAddr(pDest) == 0) || (SockaddrInGetAddr(pDest) == 0x7f000000)) + { + ds_memcpy(pHost, pDest, iHostlen); + return(0); + } + else + { + ds_memclr(pHost, iHostlen); + pHost->sa_family = AF_INET; + SockaddrInSetAddr(pHost, pState->uLocalAddr); + return(0); + } + } + + // unsupported family + ds_memclr(pHost, iHostlen); + return(-3); +#elif defined(DIRTYCODE_LINUX) || defined(DIRTYCODE_ANDROID) + struct sockaddr_in HostAddr; + struct sockaddr_in DestAddr; + uint32_t uSource = 0, uTarget; + int32_t iSocket; +#if DIRTYCODE_LOGGING + SocketStateT *pState = _Socket_pState; +#endif + // get target address + uTarget = SockaddrInGetAddr(pDest); + + // create a temp socket (must be datagram) + iSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (iSocket != INVALID_SOCKET) + { + int32_t iIndex; + int32_t iCount; + struct ifreq EndpRec[16]; + struct ifconf EndpList; + uint32_t uAddr; + uint32_t uMask; + + // request list of interfaces + ds_memclr(&EndpList, sizeof(EndpList)); + EndpList.ifc_req = EndpRec; + EndpList.ifc_len = sizeof(EndpRec); + if (ioctl(iSocket, SIOCGIFCONF, &EndpList) >= 0) + { + // figure out number and walk the list + iCount = EndpList.ifc_len / sizeof(EndpRec[0]); + for (iIndex = 0; iIndex < iCount; ++iIndex) + { + // extract the individual fields + ds_memcpy(&HostAddr, &EndpRec[iIndex].ifr_addr, sizeof(HostAddr)); + uAddr = ntohl(HostAddr.sin_addr.s_addr); + ioctl(iSocket, SIOCGIFNETMASK, &EndpRec[iIndex]); + ds_memcpy(&DestAddr, &EndpRec[iIndex].ifr_broadaddr, sizeof(DestAddr)); + uMask = ntohl(DestAddr.sin_addr.s_addr); + ioctl(iSocket, SIOCGIFFLAGS, &EndpRec[iIndex]); + + NetPrintfVerbose((pState->iVerbose, 1, "dirtynetunix: checking interface name=%s, fam=%d, flags=%04x, addr=%08x, mask=%08x\n", + EndpRec[iIndex].ifr_name, HostAddr.sin_family, + EndpRec[iIndex].ifr_flags, uAddr, uMask)); + + // only consider live inet interfaces + if ((HostAddr.sin_family == AF_INET) && ((EndpRec[iIndex].ifr_flags & (IFF_LOOPBACK+IFF_UP)) == (IFF_UP))) + { + // if target is within address range, must be hit + if ((uAddr & uMask) == (uTarget & uMask)) + { + uSource = uAddr; + break; + } + // if in a private address space and nothing else found + if (((uAddr & 0xff000000) == 0x0a000000) || ((uAddr & 0xffff0000) == 0xc0a80000)) + { + if (uSource == 0) + { + uSource = uAddr; + } + } + // always take a public address + else + { + uSource = uAddr; + } + } + } + } + // close the socket + close(iSocket); + } + + // populate dest addr + SockaddrInit(pHost, AF_INET); + SockaddrInSetAddr(pHost, uSource); + + // return result + return((uSource != 0) ? 0 : -1); +#else + return(-1); +#endif +} + diff --git a/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/netconnunix.c b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/netconnunix.c new file mode 100644 index 00000000..428ea2ea --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/dirtysock/unix/netconnunix.c @@ -0,0 +1,895 @@ +/*H********************************************************************************/ +/*! + \File netconnunix.c + + \Description + Provides network setup and teardown support. Does not actually create any + kind of network connections. + + \Copyright + Copyright (c) 2010 Electronic Arts Inc. + + \Version 04/05/2010 (jbrookes) First version; a vanilla port to Unix from PS3 +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtycert.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protoupnp.h" +#include "netconncommon.h" + +#ifdef DIRTYCODE_APPLEIOS + +#include "DirtySDK/dirtysock/iphone/netconnios.h" + +#endif + +/*** Defines **********************************************************************/ + +//! UPNP port +#define NETCONN_DEFAULT_UPNP_PORT (3659) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! private module state +typedef struct NetConnRefT +{ + NetConnCommonRefT Common; //!< cross-platform netconn data (must come first!) + + enum + { + ST_INIT, //!< initialization + ST_CONN, //!< bringing up network interface + ST_IDLE, //!< active + } eState; //!< internal connection state + + uint32_t uConnStatus; //!< connection status (surfaced to user) + + ProtoUpnpRefT *pProtoUpnp; //!< protoupnp module state + int32_t iPeerPort; //!< peer port to be opened by upnp; if zero, still find upnp router but don't open a port + int32_t iNumProcCores; //!< number of processor cores on the system + int32_t iThreadCpuAffinity; //!< cpu affinity used for our internal threads +} NetConnRefT; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +//! global module ref +static NetConnRefT *_NetConn_pRef = NULL; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _NetConnUpdateConnStatus + + \Description + Update the Connection Status and provide logging of status changes + + \Input *pRef - pointer to net NetConn module ref + \Input *uNewConnStatus - the new conn status + + \Version 01/19/2015 (tcho) +*/ +/********************************************************************************F*/ +static void _NetConnUpdateConnStatus(NetConnRefT *pRef, uint32_t uNewConnStatus) +{ + #if DIRTYCODE_LOGGING + int32_t iIndex; + char strConnStatus[5]; + + for (iIndex = 0; iIndex < 4; ++iIndex) + { + strConnStatus[iIndex] = ((char *) &uNewConnStatus)[3 - iIndex]; + } + + strConnStatus[4] = 0; + + NetPrintf(("netconnunix: netconn status changed to %s\n", strConnStatus)); + #endif + + pRef->uConnStatus = uNewConnStatus; +} + +#if defined(DIRTYCODE_LINUX) +/*F********************************************************************************/ +/*! + \Function _NetConnGetProcLine + + \Description + Parse a single line of the processor entry file. + + \Input *pLine - pointer to line to parse + \Input *pName - [out] buffer to store parsed field name + \Input iNameLen - size of name buffer + \Input *pData - [out] buffer to store parsed field data + \Input iDataLen - size of data buffer + + \Version 04/26/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static void _NetConnGetProcLine(const char *pLine, char *pName, int32_t iNameLen, char *pData, int32_t iDataLen) +{ + const char *pParse; + + // skip to end of field name + for (pParse = pLine; (*pParse != '\t') && (*pParse != ':'); pParse += 1) + ; + + // copy field name + ds_strsubzcpy(pName, iNameLen, pLine, pParse - pLine); + + // skip to field value + for ( ; (*pParse == ' ') || (*pParse == '\t') || (*pParse == ':'); pParse += 1) + ; + + // find end of field value + for (pLine = pParse; (*pParse != '\n') && (*pParse != '\0'); pParse += 1) + ; + + // copy field value + ds_strsubzcpy(pData, iDataLen, pLine, pParse - pLine); +} +#endif + +#if defined(DIRTYCODE_LINUX) +/*F********************************************************************************/ +/*! + \Function _NetConnGetProcRecord + + \Description + Parse a single proc record. + + \Input *pProcFile - proc record file pointer + + \Output + int32_t - one=success, zero=no more entries + + \Version 04/26/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetConnGetProcRecord(FILE *pProcFile) +{ + char strBuf[1024], strName[32], strValue[128], *pLine; + while (((pLine = fgets(strBuf, sizeof(strBuf), pProcFile)) != NULL) && (strBuf[0] != '\n')) + { + _NetConnGetProcLine(strBuf, strName, sizeof(strName), strValue, sizeof(strValue)); + } + return(pLine != NULL); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _NetConnGetNumProcs + + \Description + Get the number of processors on the system. + + \Output + int32_t - number of processors in system, or <=0 on failure + + \Version 04/26/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _NetConnGetNumProcs(void) +{ + #if defined(DIRTYCODE_LINUX) + FILE *pFile = fopen("/proc/cpuinfo", "r"); + int32_t iNumProcs = -1; + + if (pFile != NULL) + { + for (iNumProcs = 0; _NetConnGetProcRecord(pFile) != 0; iNumProcs += 1) + ; + fclose(pFile); + + NetPrintf(("netconnunix: parsed %d processor cores from cpuinfo\n", iNumProcs)); + } + else + { + NetPrintf(("netconnunix: could not open proc file\n")); + } + return(iNumProcs); + #else + return(-1); + #endif +} + +/*F********************************************************************************/ +/*! + \Function _NetConnGetInterfaceType + + \Description + Get interface type and return it to caller. + + \Input *pRef - module state + + \Output + uint32_t - interface type bitfield (NETCONN_IFTYPE_*) + + \Version 10/08/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _NetConnGetInterfaceType(NetConnRefT *pRef) +{ + uint32_t uIfType = NETCONN_IFTYPE_ETHER; + +#if defined(DIRTYCODE_ANDROID) + + uIfType = NETCONN_IFTYPE_NONE; + + if (SocketInfo(NULL, 'eth0', 0, NULL, 0) == 0) + { + uIfType = NETCONN_IFTYPE_WIRELESS; + } + + if (SocketInfo(NULL, 'wan0', 0, NULL, 0) == 0) + { + uIfType = NETCONN_IFTYPE_CELL; + } + +#elif defined(DIRTYCODE_APPLEIOS) + + uIfType = NetConnStatusIos(&(_NetConn_pRef->Common), 'type', 0, NULL, 0); + +#endif + + return(uIfType); +} + +/*F********************************************************************************/ +/*! + \Function _NetConnUpdate + + \Description + Update status of NetConn module. This function is called by NetConnIdle. + + \Input *pData - pointer to NetConn module ref + \Input uTick - current tick counter + + \Version 07/18/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _NetConnUpdate(void *pData, uint32_t uTick) +{ + NetConnRefT *pRef = (NetConnRefT *)pData; + + // perform idle processing + SocketControl(NULL, 'idle', uTick, NULL, NULL); + + // wait for network active status + if (pRef->eState == ST_CONN) + { + uint32_t uSocketConnStatus = SocketInfo(NULL, 'conn', 0, NULL, 0); + + if (pRef->uConnStatus != uSocketConnStatus) + { + _NetConnUpdateConnStatus(pRef, uSocketConnStatus); + } + + if (pRef->uConnStatus == '+onl') + { + // discover upnp router information + if (pRef->pProtoUpnp != NULL) + { + if (pRef->iPeerPort != 0) + { + ProtoUpnpControl(pRef->pProtoUpnp, 'port', pRef->iPeerPort, 0, NULL); + ProtoUpnpControl(pRef->pProtoUpnp, 'macr', 'upnp', 0, NULL); + } + else + { + ProtoUpnpControl(pRef->pProtoUpnp, 'macr', 'dscg', 0, NULL); + } + } + + pRef->eState = ST_IDLE; + } + } + + // update connection status while idle + if (pRef->eState == ST_IDLE) + { + // update connection status if not already in an error state + if ((pRef->uConnStatus >> 24) != '-') + { + uint32_t uSocketConnStat = SocketInfo(NULL, 'conn', 0, NULL, 0); + + if (pRef->uConnStatus != uSocketConnStat) + { + _NetConnUpdateConnStatus(pRef, uSocketConnStat); + } + } + } + + // if error status, go to idle state from any other state + if ((pRef->eState != ST_IDLE) && (pRef->uConnStatus >> 24 == '-')) + { + pRef->eState = ST_IDLE; + } +} + +/*F********************************************************************************/ +/*! + \Function _NetConnShutdownInternal + + \Description + Shutdown the network code and return to idle state for internal use + + \Input pRef - netconn ref + \Input uShutdownFlags - shutdown configuration flags + + \Output + int32_t - negative=error, else zero + + \Version 1/13/2020 (tcho) +*/ +/********************************************************************************F*/ +int32_t _NetConnShutdownInternal(NetConnRefT *pRef, uint32_t uShutdownFlags) +{ + int32_t iResult = 0; + + // decrement and check the refcount + if ((iResult = NetConnCommonCheckRef((NetConnCommonRefT*)pRef)) < 0) + { + return(iResult); + } + + if (_NetConn_pRef != NULL) + { + // disconnect network interfaces + NetConnDisconnect(); + } + + // destroy the upnp ref + if (pRef->pProtoUpnp != NULL) + { + ProtoUpnpDestroy(pRef->pProtoUpnp); + pRef->pProtoUpnp = NULL; + } + + // destroy the dirtycert module + DirtyCertDestroy(); + + // shut down protossl + ProtoSSLShutdown(); + + // remove netconn idle task + NetConnIdleDel(_NetConnUpdate, pRef); + + // shut down Idle handler + NetConnIdleShutdown(); + + // shutdown the network code + SocketDestroy(0); + + #ifdef DIRTYCODE_APPLEIOS + + //call ios NetConnShutdown + NetConnShutdownIos(uShutdownFlags); + + #endif + + // common shutdown (must come last as this frees the memory) + NetConnCommonShutdown(&pRef->Common); + _NetConn_pRef = NULL; + + return(0); +} + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function NetConnStartup + + \Description + Bring the network connection module to life. Creates connection with IOP + resources and gets things ready to go. Puts all device drivers into "probe" + mode so they look for appropriate hardware. Does not actually start any + network activity. + + \Input *pParams - startup parameters + + \Output + int32_t - zero=success, negative=failure + + \Notes + NetConnRefT::iRefCount serves as a counter for the number of times + NetConnStartup has been called. This allows us to track how many modules + are using it and how many times we expect NetConnShutdown to the called. + In the past we only allowed a single call to NetConnStartup but some + libraries may need to networking without a guarentee that the game has + already started it. + + pParams can contain the following terms: + + \verbatim + -noupnp - disable upnp support + -servicename - set servicename required for SSL use + -singlethreaded - start DirtySock in single-threaded mode (typically when used in servers) + -affinity= - the cpu affinity for our internal threads + \endverbatim + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnStartup(const char *pParams) +{ + NetConnRefT *pRef = _NetConn_pRef; + int32_t iThreadPrio = 10; + int32_t iRet = 0; + int32_t iResult = 0; + char strThreadCpuAffinity[16]; + + // allow NULL params + if (pParams == NULL) + { + pParams = ""; + } + + // debug display of input params + NetPrintf(("netconnunix: startup params='%s'\n", pParams)); + + // common startup + // pRef shall hold the address of the NetConnRefT after completion if no error occured + iResult = NetConnCommonStartup(sizeof(*pRef), pParams, (NetConnCommonRefT**)(&pRef)); + + // treat the result of the common startup, if already started simply early out + if (iResult == NETCONN_ERROR_ALREADY_STARTED) + { + return(0); + } + // otherwise, if an error occured report it + else if (iResult < 0) + { + return(iResult); + } + + pRef->eState = ST_INIT; + pRef->iPeerPort = NETCONN_DEFAULT_UPNP_PORT; + + // check for singlethreaded mode + if (strstr(pParams, "-singlethreaded")) + { + iThreadPrio = -1; + } + + // get the thread cpu affinity setting from our startup params, defaulting to 0x0 + ds_memclr(strThreadCpuAffinity, sizeof(strThreadCpuAffinity)); + NetConnCopyParam(strThreadCpuAffinity, sizeof(strThreadCpuAffinity), "-affinity=", pParams, "0x0"); + pRef->iThreadCpuAffinity =(int32_t) strtol(strThreadCpuAffinity, NULL, 16); + + // create network instance + if (SocketCreate(iThreadPrio, 0, pRef->iThreadCpuAffinity) != 0) + { + NetPrintf(("netconnunix: unable to start up dirtysock\n")); + _NetConnShutdownInternal(pRef, 0); + return(NETCONN_ERROR_SOCKET_CREATE); + } + + // create and configure dirtycert + if (NetConnDirtyCertCreate(pParams)) + { + _NetConnShutdownInternal(pRef, 0); + NetPrintf(("netconnunix: unable to create dirtycert\n")); + return(NETCONN_ERROR_DIRTYCERT_CREATE); + } + + // start up protossl + if (ProtoSSLStartup() < 0) + { + _NetConnShutdownInternal(pRef, 0); + NetPrintf(("netconnunix: unable to start up protossl\n")); + return(NETCONN_ERROR_PROTOSSL_CREATE); + } + + // create the upnp module + if (!strstr(pParams, "-noupnp")) + { + pRef->pProtoUpnp = ProtoUpnpCreate(); + if (pRef->pProtoUpnp == NULL) + { + _NetConnShutdownInternal(pRef, 0); + NetPrintf(("netconnunix: unable to start up protoupnp\n")); + return(NETCONN_ERROR_PROTOUPNP_CREATE); + } + } + + // add netconn task handle + if (NetConnIdleAdd(_NetConnUpdate, pRef) < 0) + { + _NetConnShutdownInternal(pRef, 0); + NetPrintf(("netconnunix: unable to add netconn task handler\n")); + return(NETCONN_ERROR_INTERNAL); + } + +#ifdef DIRTYCODE_APPLEIOS + //call ios netconn startup + if ((iRet = NetConnStartupIos(pParams)) < 0) + { + _NetConnShutdownInternal(pRef, 0); + return(iRet); + } +#endif + + // save ref + _NetConn_pRef = pRef; + + return(iRet); +} + +/*F********************************************************************************/ +/*! + \Function NetConnQuery + + \Description + Query the list of available connection configurations. This list is loaded + from the specified device. The list is returned in a simple fixed width + array with one string per array element. The caller can find the user portion + of the config name via strchr(item, '#')+1. + + \Input *pDevice - device to scan (mc0:, mc1:, pfs0:, pfs1:) + \Input *pList - buffer to store output array in + \Input iSize - length of buffer in bytes + + \Output + int32_t - negative=error, else number of configurations + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnQuery(const char *pDevice, NetConfigRecT *pList, int32_t iSize) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetConnConnect + + \Description + Used to bring the networking online with a specific configuration. Uses a + configuration returned by NetConnQuery. + + \Input *pConfig - unused + \Input *pOption - asciiz list of config parameters + "peerport=" to specify peer port to be opened by upnp. + \Input iData - platform-specific + + \Output + int32_t - negative=error, zero=success + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnConnect(const NetConfigRecT *pConfig, const char *pOption, int32_t iData) +{ + int32_t iResult = 0; + NetConnRefT *pRef = _NetConn_pRef; + + // check connection options, if present + if (pRef->eState == ST_INIT) + { + // check for connect options + if (pOption != NULL) + { + const char *pOpt; + + // check for specification of peer port + if ((pOpt = strstr(pOption, "peerport=")) != NULL) + { + pRef->iPeerPort = (int32_t)strtol(pOpt+9, NULL, 10); + } + } + NetPrintf(("netconnunix: upnp peerport=%d %s\n", + pRef->iPeerPort, (pRef->iPeerPort == NETCONN_DEFAULT_UPNP_PORT ? "(default)" : "(selected via netconnconnect param)"))); + + // start up network interface + SocketControl(NULL, 'conn', 0, NULL, NULL); + + // transition to connecting state + pRef->eState = ST_CONN; + } + else + { + NetPrintf(("netconnunix: NetConnConnect() ignored because already connected!\n")); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function NetConnDisconnect + + \Description + Used to bring down the network connection. After calling this, it would + be necessary to call NetConnConnect to bring the connection back up or + NetConnShutdown to completely shutdown all network support. + + \Output + int32_t - negative=error, zero=success + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnDisconnect(void) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // shut down networking + if (pRef->eState != ST_INIT) + { + // bring down network interface + SocketControl(NULL, 'disc', 0, NULL, NULL); + + // reset status + pRef->eState = ST_INIT; + pRef->uConnStatus = 0; + } + + // abort upnp operations + if (pRef->pProtoUpnp != NULL) + { + ProtoUpnpControl(pRef->pProtoUpnp, 'abrt', 0, 0, NULL); + } + + // done + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetConnControl + + \Description + Set module behavior based on input selector. + + \Input iControl - input selector + \Input iValue - selector input + \Input iValue2 - selector input + \Input *pValue - selector input + \Input *pValue2 - selector input + + \Output + int32_t - selector result + + \Notes + iControl can be one of the following: + + \verbatim + snam: set DirtyCert service name + \endverbatim + + Unhandled selectors are passed through to NetConnCommonControl() + + \Version 1.0 04/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnControl(int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue, void *pValue2) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // make sure module is started before allowing any other control calls + if (pRef == NULL) + { + NetPrintf(("netconnunix: warning - calling NetConnControl() while module is not initialized\n")); + return(-1); + } + + // set dirtycert service name + if (iControl == 'snam') + { + return(DirtyCertControl('snam', 0, 0, pValue)); + } + + // pass through unhandled selectors to NetConnCommon + return(NetConnCommonControl(&pRef->Common, iControl, iValue, iValue2, pValue, pValue2)); +} + +/*F********************************************************************************/ +/*! + \Function NetConnStatus + + \Description + Check general network connection status. Different selectors return + different status attributes. + + \Input iKind - status selector ('open', 'conn', 'onln') + \Input iData - (optional) selector specific + \Input *pBuf - (optional) pointer to output buffer + \Input iBufSize - (optional) size of output buffer + + \Output + int32_t - selector specific + + \Notes + iKind can be one of the following: + + \verbatim + addr: ip address of client + affn: thread cpu affinity setting + bbnd: TRUE if broadband, else FALSE + conn: connection status: +onl=online, ~=in progress, -=NETCONN_ERROR_* + hwid: (IOS only) This will return the vendor id in pBuf. pBuf must be at least 17 byte. + macx: MAC address of client (returned in pBuf) + ncon: returns the network connectivity level (iOS and Android Only) + onln: true/false indication of whether network is operational + open: true/false indication of whether network code running + proc: number of processor cores on the system (Linux only) + type: connection type: NETCONN_IFTYPE_* bitfield + upnp: return protoupnp external port info, if available + vers: return DirtySDK version + \endverbatim + + Unhandled selectors are passed through to NetConnCommonStatus() + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnStatus(int32_t iKind, int32_t iData, void *pBuf, int32_t iBufSize) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // initialize output buffer + if (pBuf != NULL) + { + ds_memclr(pBuf, iBufSize); + } + + // see if network code is initialized + if (iKind == 'open') + { + return(pRef != NULL); + } + // return DirtySDK version + if (iKind == 'vers') + { + return(DIRTYSDK_VERSION); + } + + // make sure module is started before allowing any other status calls + if (pRef == NULL) + { + NetPrintf(("netconnunix: warning - calling NetConnStatus() while module is not initialized\n")); + return(-1); + } + + // return the thread cpu affinity setting + if (iKind == 'affn') + { + return(pRef->iThreadCpuAffinity); + } + + // return broadband (TRUE/FALSE) + if (iKind == 'bbnd') + { + return(TRUE); + } + // connection status + if (iKind == 'conn') + { + return(pRef->uConnStatus); + } + // see if connected to ISP/LAN + if (iKind == 'onln') + { + return(pRef->uConnStatus == '+onl'); + } + + // return number of processor cores + if (iKind == 'proc') + { + if (pRef->iNumProcCores == 0) + { + pRef->iNumProcCores = _NetConnGetNumProcs(); + } + return(pRef->iNumProcCores); + } + // return interface type (more verbose) + if (iKind == 'type') + { + return(_NetConnGetInterfaceType(pRef)); + } + // return upnp addportmap info, if available + if (iKind == 'upnp') + { + // if protoupnp is available, and we've added a port map, return the external port for the port mapping + if ((pRef->pProtoUpnp != NULL) && (ProtoUpnpStatus(pRef->pProtoUpnp, 'stat', NULL, 0) & PROTOUPNP_STATUS_ADDPORTMAP)) + { + return(ProtoUpnpStatus(pRef->pProtoUpnp, 'extp', NULL, 0)); + } + } +#ifdef DIRTYCODE_ANDROID + + if (iKind == 'ncon') + { + if ((SocketInfo(NULL, 'eth0', 0, NULL, 0) == 0) || SocketInfo(NULL, 'wan0', 0, NULL, 0) == 0) + { + return(TRUE); + } + else + { + return(FALSE); + } + } + +#endif + + +#ifdef DIRTYCODE_APPLEIOS + + //pass unrecgnized options to NetConnStatusIos and Socket Info + return(NetConnStatusIos(&pRef->Common, iKind, iData, pBuf, iBufSize)); + +#else + + // pass unrecognized options to NetConnCommon + return(NetConnCommonStatus(&pRef->Common, iKind, iData, pBuf, iBufSize)); + +#endif + +} + +/*F********************************************************************************/ +/*! + \Function NetConnShutdown + + \Description + Shutdown the network code and return to idle state. + + \Input uShutdownFlags - shutdown configuration flags + + \Output + int32_t - negative=error, else zero + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetConnShutdown(uint32_t uShutdownFlags) +{ + NetConnRefT *pRef = _NetConn_pRef; + + // make sure we've been started + if (pRef == NULL) + { + return(NETCONN_ERROR_NOTACTIVE); + } + + return(_NetConnShutdownInternal(pRef, uShutdownFlags)); +} + +/*F********************************************************************************/ +/*! + \Function NetConnSleep + + \Description + Sleep the application for some number of milliseconds. + + \Input iMilliSecs - number of milliseconds to block for + + \Version 10/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void NetConnSleep(int32_t iMilliSecs) +{ + usleep(iMilliSecs*1000); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/game/connapi.c b/r5dev/thirdparty/dirtysdk/source/game/connapi.c new file mode 100644 index 00000000..cfd923b1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/game/connapi.c @@ -0,0 +1,4180 @@ +/*H********************************************************************************/ +/*! + \File connapi.c + + \Description + ConnApi is a high-level connection manager, that packages the "connect to + peer" process into a single module. Both game connections and voice + connections can be managed. Multiple peers are supported in a host/client + model for the game connection, and a peer/peer model for the voice + connections. + + \Copyright + Copyright (c) Electronic Arts 2005. ALL RIGHTS RESERVED. + + \Notes + About ConnApi using ProtoMangle: + + For plaforms other than xboxone: + * ConnApi uses ProtoMangle to exercice connection demangling logic + with a central demangler service. + * When demangler is enabled, a typical connection state transition is: + success: INIT --> CONN --> ACTV + success after demangling: INIT --> CONN --> MNGL --> INIT --> CONN --> ACTV + * For each connection, the boolean ConnApiConnInfoT::bDemangling is used to + track "demangling being in progress" across the "MNGL --> INIT --> CONN --> ACTV" + state sequence such that ProtoMangleReport() gets called appropriately + once post-demangling conn attempt finishes. + * Additionally, the boolean ConnApiConnInfoT::bDemangling is used to "serialize" + demangling attempts as interaction with the demangler is limited to one session + at a time. + + For xboxone: + * The central demangling service is never used, but the secure device association + creation is mapped to the protomangle metaphore. + * A typical connection state transition is: + p2p conn (SecureAssoc needed): MNGL --> INIT --> CONN --> ACTV + server conn (SecureAssoc not needed): INIT --> CONN --> ACTV + * For each connection, the boolean ConnApiConnInfoT::bDemangling no longer means + "demangling being in progress" across states. It's rather used in such way that, + for server conns, if the conn attempt fails, then the MGNL state is not entered. + + + \Version 01/04/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtynames.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/proto/protomangle.h" +#include "DirtySDK/proto/prototunnel.h" +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voipgroup.h" + +#include "DirtySDK/game/connapi.h" + +/*** Defines **********************************************************************/ + +//! define if we're using Xbox networking +#if defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) +#define CONNAPI_XBOX_NETWORKING 1 +#else +#define CONNAPI_XBOX_NETWORKING 0 +#endif + +//! default connapi timeout +#define CONNAPI_TIMEOUT_DEFAULT (15*1000) + +//! connapi connection timeout +#define CONNAPI_CONNTIMEOUT_DEFAULT (10*1000) + +//! connapi default demangler timeout (per user) +#if CONNAPI_XBOX_NETWORKING +#define CONNAPI_DEMANGLER_TIMEOUT (2*CONNAPI_CONNTIMEOUT_DEFAULT) // experimentation showed that creation of security association can be significantly long +#define CONNAPI_DEMANGLER_WITH_FAILOVER_TIMEOUT (CONNAPI_CONNTIMEOUT_DEFAULT) // however for scenarios involving CC assistance, we don't wait that long +#else +#define CONNAPI_DEMANGLER_TIMEOUT (CONNAPI_CONNTIMEOUT_DEFAULT) +#define CONNAPI_DEMANGLER_WITH_FAILOVER_TIMEOUT (CONNAPI_DEMANGLER_TIMEOUT) +#endif + +//! default GameLink buffer size +#define CONNAPI_LINKBUFDEFAULT (1024) + +//! test demangling +#define CONNAPI_DEMANGLE_TEST (DIRTYCODE_DEBUG && FALSE) + +//! random demangle local port? +#define CONNAPI_RNDLCLDEMANGLEPORT (0) // used to be enabled for WII revolution platform + +//! max number of registered callbacks +#define CONNAPI_MAX_CALLBACKS (8) + +//! connapi client flags +#define CONNAPI_CLIENTFLAG_REMOVE (1) // remove client from clientlist +#define CONNAPI_CLIENTFLAG_TUNNELPORTDEMANGLED (2) // tunnel port has been demangled +#define CONNAPI_CLIENTFLAG_SECUREADDRRESOLVED (4) // secure address has been resolved (xboxone-specific) + +//! debug flags +#define CONNAPI_CLIENTFLAG_P2PFAILDBG (128) // set to force P2P connections to fail + +/*** Type Definitions *************************************************************/ +struct ConnApiRefT +{ + //! connapi user callback info + ConnApiCallbackT *pCallback[CONNAPI_MAX_CALLBACKS]; + void *pUserData[CONNAPI_MAX_CALLBACKS]; + + //! dirtymem memory group + int32_t iMemGroup; + void *pMemGroupUserData; + + //! game port to connect on + uint16_t uGamePort; + + //! voip port to connect on + uint16_t uVoipPort; + + //! game connection flags + uint16_t uConnFlags; + + //! netmask, used for external address comparisons + uint32_t uNetMask; + + //! game name + char strGameName[32]; + + //! game link buffer size + int32_t iLinkBufSize; + + //! master game util ref (used for advertising) + NetGameUtilRefT *pGameUtilRef; + + //! protomangle ref + ProtoMangleRefT *pProtoMangle; + + //! prototunnel ref + ProtoTunnelRefT *pProtoTunnel; + + //! prototunnel port + int32_t iTunnelPort; + + //! do we own tunnel ref? + int32_t bTunnelOwner; + + //! protomangle server name + char strDemanglerServer[48]; + + //! voip ref + VoipRefT *pVoipRef; + + //! voipgroup ref + VoipGroupRefT *pVoipGroupRef; + + //! comm construct function + CommAllConstructT *pCommConstruct; + + //! our address + DirtyAddrT SelfAddr; + + //! our unique identifier + uint32_t uSelfId; + + //! index of ourself in client list + int32_t iSelf; + + //! session identifier + int32_t iSessId; + + //! connection timeout value + int32_t iConnTimeout; + + //! timeout value + int32_t iTimeout; + + //! demangler timeouts + int32_t iConfigMnglTimeout; // configured timeout for cases where CC assistance are not applicable + int32_t iConfigMnglTimeoutFailover; // configured timeout for cases where CC assistance are applicable + int32_t iCurrentMnglTimeout; // effective timeout value to be passed down to ProtoMangle + + //! gamelink configuration - input packet queue size + int32_t iGameMinp; + + //! gamelink configuration - output packet queue size + int32_t iGameMout; + + //! gamelink configuration - max packet width + int32_t iGameMwid; + + //! gamelink configuration - unacknowledged packet window + int32_t iGameUnackLimit; + + //! Connection Concierge mode (CONNAPI_CCMODE_*) + int32_t iCcMode; + + //! session information + char strSession[128]; + + //! demangler reporting? + uint8_t bReporting; + + //! is demangler enabled? + uint8_t bDemanglerEnabled; + + //! is tunnel enabled? + uint8_t bTunnelEnabled; + + //! inside of a callback? + uint8_t bInCallback; + + //! disc callback on client removal? + uint8_t bRemoveCallback; + + //! auto-update enabled? + uint8_t bAutoUpdate; + + #if CONNAPI_XBOX_NETWORKING + //! 'exsn', the external session name - passed to the demangler + char strExternalSessionName[128]; + + //! 'estn', the external session template name - passed to the demangler + char strExternalSessionTemplateName[128]; + + //! 'scid', the service configuration id - passed to the demangler + char strScid[128]; + #endif + + //! 'adve', TRUE if ProtoAdvt advertising enabled + uint8_t bDoAdvertising; + + //! 'meta', TRUE if commudp metadata enabled + uint8_t bCommUdpMetadata; + + //! game socket ref, if available + uintptr_t uGameSockRef; + + //! voip socket ref, if available + uintptr_t uVoipSockRef; + + //! tunnel socket ref, if available + uintptr_t uTunlSockRef; + + //! game host + int32_t iGameHostIndex; + //! voip host + int32_t iVoipHostIndex; + + #if DIRTYCODE_DEBUG + //! force direct connection to fail ? + uint8_t bFailP2PConnect; + #endif + + uint32_t uGameTunnelFlag; + uint32_t uGameTunnelFlagOverride; + + int32_t iQosDuration; + int32_t iQosInterval; + int32_t iQosPacketSize; + + //! client state + enum + { + ST_IDLE, //!< idle + ST_INGAME //!< hosting or joined a game + } eState; + + //! game network topology + ConnApiGameTopologyE eGameTopology; + //! voip network topology + ConnApiVoipTopologyE eVoipTopology; + + //! client list - must come last in ref as it is variable length + ConnApiClientListT ClientList; +}; + +/*** Variables ********************************************************************/ + +//! number of voipgroups we allocate in the manager +static int8_t _ConnApi_iMaxVoipGroups = 8; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _ConnApiDefaultCallback + + \Description + Default ConnApi user callback. On a debug build, displays state transition + information. + + \Input *pConnApi - connection manager ref + \Input *pCbInfo - connection info + \Input *pUserData - user callback data + + \Version 01/17/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiDefaultCallback(ConnApiRefT *pConnApi, ConnApiCbInfoT *pCbInfo, void *pUserData) +{ + #if DIRTYCODE_LOGGING + static const char *_StateNames[CONNAPI_NUMSTATUSTYPES] = + { + "CONNAPI_STATUS_INIT", + "CONNAPI_STATUS_CONN", + "CONNAPI_STATUS_MNGL", + "CONNAPI_STATUS_ACTV", + "CONNAPI_STATUS_DISC" + }; + static const char *_TypeNames[CONNAPI_NUMCBTYPES] = + { + "CONNAPI_CBTYPE_GAMEEVENT", + "CONNAPI_CBTYPE_DESTEVENT", + "CONNAPI_CBTYPE_VOIPEVENT", + }; + + // display state change + NetPrintf(("connapi: [%p] client %d) [%s] %s -> %s\n", pConnApi, pCbInfo->iClientIndex, _TypeNames[pCbInfo->eType], + _StateNames[pCbInfo->eOldStatus], _StateNames[pCbInfo->eNewStatus])); + #endif +} + +#if CONNAPI_RNDLCLDEMANGLEPORT +/*F********************************************************************************/ +/*! + \Function _ConnApiGenerateDemanglePort + + \Description + Generate a "random" demangle port to use, actually generated based on the + two lowest bytes of the MAC address and multiplied by a small prime number, + and then modded into a small range of ports. The intent here is to generate + ports that will both not be reused on successive attempts and also be unlikely + to collide with other client choices, in case there are other clients operating + the same code behind the same NAT device. + + \Input *pConnApi - connection manager ref + + \Output + uint16_t - generated demangle port to use + + \Version 03/02/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static uint16_t _ConnApiGenerateDemanglePort(ConnApiRefT *pConnApi) +{ + static const uint16_t _uPortBase = 10000; + static const uint16_t _uPortRange = 1000; + static uint16_t _uPortOffset = 0xffff; + + // initialize port offset based on mac address to avoid collisions with other clients behind the same NAT + if (_uPortOffset == 0xffff) + { + uint8_t aMacAddr[6]; + if (NetConnStatus('macx', 0, aMacAddr, sizeof(aMacAddr)) >= 0) + { + // generate mac-based port within specified range + NetPrintf(("connapi: [%p] generating port offset based on MAC addr '%s'\n", pConnApi, NetConnMAC())); + _uPortOffset = aMacAddr[4]; + _uPortOffset = (_uPortOffset << 8) + aMacAddr[5]; + _uPortOffset *= 7; + } + else + { + NetPrintf(("connapi: [%p] unable to acquire mac address\n", pConnApi)); + _uPortOffset = 0; + } + } + + // generate new port + _uPortOffset = (_uPortOffset + 1) % _uPortRange; + return(_uPortBase + _uPortOffset); +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _ConnApiDisplayClientInfo + + \Description + Debug-only function to print the given client info to debug output. + + \Input *pClient - pointer to client to display + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiDisplayClientInfo(ConnApiClientT *pClient, int32_t iClient) +{ + if (pClient->bAllocated) + { + NetPrintf(("connapi: %d) id:0x%08x lid:0x%08x rid:0x%08x ip:%a hosted:%s qos:%s dirtyaddr:%s\n", + iClient, pClient->ClientInfo.uId, pClient->ClientInfo.uLocalClientId, pClient->ClientInfo.uRemoteClientId, + pClient->ClientInfo.uAddr, pClient->ClientInfo.bIsConnectivityHosted ? "TRUE" : "FALSE", + pClient->ClientInfo.bEnableQos ? "TRUE" : "FALSE", pClient->ClientInfo.DirtyAddr.strMachineAddr)); + } + else + { + NetPrintf(("connapi: %d) empty\n", iClient)); + } +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _ConnApiDisplayClientList + + \Description + Debug-only function to print the given client list to debug output. + + \Input *pClientList - pointer to client list to display + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiDisplayClientList(ConnApiClientListT *pClientList) +{ + int32_t iClient; + + NetPrintf(("connapi: clientlist display\n")); + for (iClient = 0; iClient < pClientList->iMaxClients; iClient++) + { + _ConnApiDisplayClientInfo(&pClientList->Clients[iClient], iClient); + } +} +#endif + +/*F********************************************************************************/ +/*! + \Function _ConnApiClientReleaseProtoMangleResources + + \Description + When both voip and game are in DISC state, let ProtoMangle know that it + can release resources associated with this client. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to init + + \Notes + 1- Really needed for Xbox One only. Ends up being a no-op on other platform. + + 2- For dirtycast-based scenarios, the client entry for the gameserver always has + pClient->VoipInfo.eStatus = CONNAPI_STATUS_INIT, so the call to ProtoMangleContro() + is never exercised. + + \Version 09/13/2013 (mclouatre) +*/ +/********************************************************************************F*/ +static void _ConnApiClientReleaseProtoMangleResources(ConnApiRefT *pConnApi, ConnApiClientT *pClient) +{ + if ((pConnApi->bDemanglerEnabled == TRUE) && (pClient->GameInfo.eStatus == CONNAPI_STATUS_DISC) && (pClient->VoipInfo.eStatus == CONNAPI_STATUS_DISC)) + { + ProtoMangleControl(pConnApi->pProtoMangle, 'remv', (int32_t)(pClient - pConnApi->ClientList.Clients), 0, NULL); + } +} + + +#if CONNAPI_XBOX_NETWORKING +/*F********************************************************************************/ +/*! + \Function _ConnApiInitClientConnectionState + + \Description + Initialize client's game and voip connection states based on selected game and voip topology. + + \Input *pConnApi - connection manager ref + \Input *pClient - pointer to client to init + \Input iClientIndex - index of client + \Input uConnMode - bit mask to specify what connection state needs to be initialized (CONNAPI_CONNFLAG_XXXCONN) + + \Version 11/08/2013 (mclouatre) +*/ +/********************************************************************************F*/ +static void _ConnApiInitClientConnectionState(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex, uint32_t uConnMode) +{ + /* + $todo - revisit this logics to make it more explicit + --> p2p game connections that do not need to be demangled are skipped in _ConnApiUpdateConnections() because + they don't have the CONNAPI_CONNFLAG_GAMECONN flag set + --> the same trick is not feasible with p2p voip connections because they need to reach the CONNAPI_STATUS_ACTV state + even if routed through a server; so we make sure they do not get demangled by defaulting straight + to CONNAPI_STATUS_INIT here. + */ + + if (uConnMode & CONNAPI_CONNFLAG_GAMECONN) + { + // if we're a xboxone client in phxc mode and we are currently dealing with the dedicated server client entry or we are connectivity hosted, + // then make sure we do not attempt to demangle with protomanglexboxone + if (!pConnApi->bDemanglerEnabled || pClient->ClientInfo.bIsConnectivityHosted || (iClientIndex == pConnApi->iSelf) || + ((pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_SERVERHOSTED) && (iClientIndex == pConnApi->iGameHostIndex))) + { + pClient->GameInfo.eStatus = CONNAPI_STATUS_INIT; + pClient->GameInfo.bDemangling = TRUE; // make sure demangling will not be attempted if first connection attempt to server fails + } + else + { + pClient->GameInfo.eStatus = CONNAPI_STATUS_MNGL; + + // if secure address is already known, don't clear it - it will be reused + if ((pClient->uFlags & CONNAPI_CLIENTFLAG_SECUREADDRRESOLVED) == 0) + { + pClient->ClientInfo.uAddr = 0; + pClient->ClientInfo.uLocalAddr = 0; + } + } + } + + if (uConnMode & CONNAPI_CONNFLAG_VOIPCONN) + { + // if voip is routed through a dedicated server, game is routed through a dedicated server but voip is not or when connectivity hosted, then make sure we do + // not attempt to demangle voip connections with protomanglexboxone. There is no need to check to see if the client is a voip topology host (host will never have conn mode CONNAPI_CONNFLAG_VOIPCONN) + if (!pConnApi->bDemanglerEnabled || pClient->ClientInfo.bIsConnectivityHosted || (iClientIndex == pConnApi->iSelf) || + (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) || + ((pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_SERVERHOSTED) && (iClientIndex == pConnApi->iGameHostIndex))) + { + pClient->VoipInfo.eStatus = CONNAPI_STATUS_INIT; + pClient->VoipInfo.bDemangling = TRUE; // make sure demangling will not be attempted if first connection attempt to server fails + } + else + { + pClient->VoipInfo.eStatus = CONNAPI_STATUS_MNGL; + + // if secure address is already known, don't clear it - it will be reused + if ((pClient->uFlags & CONNAPI_CLIENTFLAG_SECUREADDRRESOLVED) == 0) + { + pClient->ClientInfo.uAddr = 0; + pClient->ClientInfo.uLocalAddr = 0; + } + } + } +} +#endif + +/*F********************************************************************************/ +/*! + \Function _ConnApiInitClient + + \Description + Initialize a single client based on input user info. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to init + \Input *pClientInfo - pointer to user info to init client with + \Input iClientIndex - index of client + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiInitClient(ConnApiRefT *pConnApi, ConnApiClientT *pClient, ConnApiClientInfoT *pClientInfo, int32_t iClientIndex) +{ + // initialize new client structure and save input user info + ds_memclr(pClient, sizeof(*pClient)); + ds_memcpy_s(&pClient->ClientInfo, sizeof(pClient->ClientInfo), pClientInfo, sizeof(*pClientInfo)); + + // initialize default voip values + pClient->iVoipConnId = VOIP_CONNID_NONE; + + // set up remote (connect) port info + pClient->GameInfo.uMnglPort = (pClient->ClientInfo.uGamePort == 0) ? pConnApi->uGamePort : pClient->ClientInfo.uGamePort; + pClient->VoipInfo.uMnglPort = (pClient->ClientInfo.uVoipPort == 0) ? pConnApi->uVoipPort : pClient->ClientInfo.uVoipPort; + + // set up local (bind) port info + pClient->GameInfo.uLocalPort = ((pClient->ClientInfo.uLocalGamePort == 0) || (pConnApi->bTunnelEnabled == TRUE)) ? pConnApi->uGamePort : pClient->ClientInfo.uLocalGamePort; + pClient->VoipInfo.uLocalPort = ((pClient->ClientInfo.uLocalVoipPort == 0) || (pConnApi->bTunnelEnabled == TRUE)) ? pConnApi->uVoipPort : pClient->ClientInfo.uLocalVoipPort; + + // set unique client identifier if not already supplied + if (pClient->ClientInfo.uId == 0) + { + pClient->ClientInfo.uId = (uint32_t)iClientIndex + 1; + } + + if ((pClient->ClientInfo.bEnableQos == TRUE) && (iClientIndex != pConnApi->iSelf)) + { + NetPrintf(("connapi: [%p] delaying voip connection for player %d because of QoS validation\n", pConnApi, iClientIndex)); + } + else + { + // Voip is not delayed when bEnableQos is FALSE + pClient->bEstablishVoip = TRUE; + } + + #if CONNAPI_XBOX_NETWORKING + _ConnApiInitClientConnectionState(pConnApi, pClient, iClientIndex, CONNAPI_CONNFLAG_GAMECONN|CONNAPI_CONNFLAG_VOIPCONN); + #endif + + // mark as allocated + pClient->bAllocated = TRUE; +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateClientFlags + + \Description + Update client flags based on game mode and game flags. + + \Input *pConnApi - pointer to module state + + \Version 05/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiUpdateClientFlags(ConnApiRefT *pConnApi) +{ + ConnApiClientT *pClient; + int32_t iClient; + + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + pClient = &pConnApi->ClientList.Clients[iClient]; + if (!pClient->bAllocated || (iClient == pConnApi->iSelf)) + { + continue; + } + pClient->uConnFlags = pConnApi->uConnFlags; + + // when not in a peer web game topology, only establish a game connection to the host + if ((iClient != pConnApi->iGameHostIndex) && (pConnApi->iGameHostIndex != pConnApi->iSelf) && (pConnApi->eGameTopology != CONNAPI_GAMETOPOLOGY_PEERWEB)) + { + pClient->uConnFlags &= ~CONNAPI_CONNFLAG_GAMECONN; + } + if (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) + { + // when in a server hosted voip topology, don't establish a voip connection to the host + if (iClient == pConnApi->iVoipHostIndex) + { + pClient->uConnFlags &= ~CONNAPI_CONNFLAG_VOIPCONN; + } + } + else + { + /* if we are in a game with a server hosted and the voip topology is not server hosted, don't establish + connections to that host */ + if ((pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_SERVERHOSTED) && (iClient == pConnApi->iGameHostIndex)) + { + pClient->uConnFlags &= ~CONNAPI_CONNFLAG_VOIPCONN; + } + } + // if voip needs to be disabled while we do QoS measurements, update the conn flags accordingly + if (pClient->bEstablishVoip == FALSE) + { + pClient->uConnFlags &= ~CONNAPI_CONNFLAG_VOIPCONN; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiGenerateSessionKey + + \Description + Generate session key for demangling session. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to peer + \Input iClientIndex - index of peer + \Input *pSess - [out] pointer to session buffer + \Input iSessSize - size of session buffer + \Input *pSessType - type of session - "game" or "voip" + + \Version 01/13/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiGenerateSessionKey(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex, char *pSess, int32_t iSessSize, const char *pSessType) +{ + uint32_t uIdA, uIdB, uTemp; + + uIdA = pClient->ClientInfo.uLocalClientId; + uIdB = pClient->ClientInfo.uRemoteClientId; + if (uIdB < uIdA) + { + uTemp = uIdA; + uIdA = uIdB; + uIdB = uTemp; + } + + ds_snzprintf(pSess, iSessSize, "%08x-%08x-%s-%08x", uIdA, uIdB, pSessType, pConnApi->iSessId); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiTunnelAlloc + + \Description + Allocate a tunnel for the given client. + + \Input *pConnApi - pointer to module state + \Input *pClient - client to connect to + \Input iClientIndex - index of client + \Input uRemoteAddr - remote address to tunnel to + \Input bLocalAddr - TRUE if we are using local address, else FALSE + + \Version 12/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiTunnelAlloc(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex, uint32_t uRemoteAddr, uint8_t bLocalAddr) +{ + ProtoTunnelInfoT TunnelInfo; + + // if connectivity is hosted and if the tunnel to the hosting server was already created in the connection flow of another client + // then skip tunnel creation and reuse that tunnel + if (pClient->ClientInfo.bIsConnectivityHosted) + { + int32_t iClient; + + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + if ( (pClient != &pConnApi->ClientList.Clients[iClient]) && + (pClient->ClientInfo.uHostingServerId == pConnApi->ClientList.Clients[iClient].ClientInfo.uHostingServerId) && + (pConnApi->ClientList.Clients[iClient].iTunnelId != 0) ) + { + pClient->iTunnelId = pConnApi->ClientList.Clients[iClient].iTunnelId; + NetPrintf(("connapi: [%p] client %d is reusing tunnel 0x%08x to hosting server 0x%08x\n", pConnApi, iClientIndex, pClient->iTunnelId, pClient->ClientInfo.uHostingServerId)); + return; + } + } + } + + // set up tunnel info + ds_memclr(&TunnelInfo, sizeof(TunnelInfo)); + + TunnelInfo.uRemoteClientId = pClient->ClientInfo.bIsConnectivityHosted ? pClient->ClientInfo.uHostingServerId : pClient->ClientInfo.uRemoteClientId; + TunnelInfo.uRemoteAddr = uRemoteAddr; + TunnelInfo.uRemotePort = bLocalAddr ? pClient->ClientInfo.uLocalTunnelPort : pClient->ClientInfo.uTunnelPort; + TunnelInfo.aRemotePortList[0] = pClient->GameInfo.uMnglPort; + TunnelInfo.aRemotePortList[1] = pClient->VoipInfo.uMnglPort; + TunnelInfo.aPortFlags[0] = (pConnApi->uGameTunnelFlagOverride) ? (pConnApi->uGameTunnelFlag) : (PROTOTUNNEL_PORTFLAG_ENCRYPTED|PROTOTUNNEL_PORTFLAG_AUTOFLUSH); + TunnelInfo.aPortFlags[1] = PROTOTUNNEL_PORTFLAG_ENCRYPTED; + + NetPrintf(("connapi: [%p] setting client %d clientId=0x%08x TunnelInfo.uRemotePort %d %s\n", pConnApi, iClientIndex, TunnelInfo.uRemoteClientId, TunnelInfo.uRemotePort, + pClient->ClientInfo.bIsConnectivityHosted ? "(hosted connection)" : "(direct connection)")); + + // allocate tunnel, set the local client id and return to caller + pClient->iTunnelId = ProtoTunnelAlloc(pConnApi->pProtoTunnel, &TunnelInfo, pClient->ClientInfo.strTunnelKey); + if (pClient->iTunnelId >= 0) + { + ProtoTunnelControl(pConnApi->pProtoTunnel, 'tcid', pClient->iTunnelId, (int32_t)pClient->ClientInfo.uLocalClientId, NULL); + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiVoipTunnelAlloc + + \Description + Allocate a tunnel for voip for the given client depending on voip settings. + + \Input *pConnApi - pointer to module state + \Input *pClient - client to connect to + \Input iClientIndex - index of client + \Input uRemoteAddr - remote address to tunnel to + \Input bLocalAddr - TRUE if we are using local address, else FALSE + + \Version 12/23/2008 (jrainy) +*/ +/********************************************************************************F*/ +static void _ConnApiVoipTunnelAlloc(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex, uint32_t uRemoteAddr, uint8_t bLocalAddr) +{ + int32_t iTunnelId = 0; + + // if doing redirect via host, check for previously created tunnel for re-use + if (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) + { + iTunnelId = pConnApi->ClientList.Clients[pConnApi->iVoipHostIndex].iTunnelId; + } + + // if no reused tunnel, create one + if (iTunnelId == 0) + { + _ConnApiTunnelAlloc(pConnApi, pClient, iClientIndex, uRemoteAddr, bLocalAddr); + } + else + { + pClient->iTunnelId = iTunnelId; + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiTunnelFree + + \Description + Free the given client's tunnel. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to allocate tunnel for + + \Version 12/20/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiTunnelFree(ConnApiRefT *pConnApi, ConnApiClientT *pClient) +{ + // tunnel active? + if (!pConnApi->bTunnelEnabled) + { + return; + } + + // if voip to this client is redirected via the host in a C/S game, + // and we're trying to free a client, but not the host itself, skip tunnel destruction. + if (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) + { + if ((pConnApi->ClientList.Clients[pConnApi->iVoipHostIndex].iTunnelId == pClient->iTunnelId) && + (&pConnApi->ClientList.Clients[pConnApi->iVoipHostIndex] != pClient)) + { + pClient->iTunnelId = 0; + return; + } + } + + // if connectivity is hosted and if the tunnel to the hosting server is still used for another client + // then skip tunnel destruction + if (pClient->ClientInfo.bIsConnectivityHosted) + { + int32_t iClient; + + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + if ( (pClient != &pConnApi->ClientList.Clients[iClient]) && + (pClient->ClientInfo.uHostingServerId == pConnApi->ClientList.Clients[iClient].ClientInfo.uHostingServerId) && + (pClient->iTunnelId == pConnApi->ClientList.Clients[iClient].iTunnelId) ) + { + #if DIRTYCODE_LOGGING + ConnApiClientT *pFirstClient = &pConnApi->ClientList.Clients[0]; + NetPrintf(("connapi: [%p] freeing tunnel 0x%08x to hosting server 0x%08x is skipped for client %d because tunnel still used by at least client %d\n", + pConnApi, pClient->iTunnelId, pClient->ClientInfo.uHostingServerId, (pClient - pFirstClient), iClient)); + #endif + pClient->iTunnelId = 0; + return; + } + } + } + + if (pClient->iTunnelId != 0) + { + ProtoTunnelFree2(pConnApi->pProtoTunnel, pClient->iTunnelId, pClient->ClientInfo.strTunnelKey, pClient->ClientInfo.uAddr); + pClient->iTunnelId = 0; + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiGetConnectAddr + + \Description + Selects between internal and internal address to use for connection, + based on whether the external addresses are equal or not. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to connect to + \Input *pLocalAddr - [out] storage for boolean indicating whether local address was used or not + \Input uConnMode - game conn or voip conn + \Input **pClientUsedRet - the pointer to use to return the client actually used + + \Output + int32_t - ip address to be used to connnect to the specified client + + \Version 01/11/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ConnApiGetConnectAddr(ConnApiRefT *pConnApi, ConnApiClientT *pClient, uint8_t *pLocalAddr, const uint32_t uConnMode, ConnApiClientT **pClientUsedRet) +{ + ConnApiClientT *pSelf; + int32_t uAddr; + uint8_t bLocalAddr = FALSE; + ConnApiClientT *pClientUsed = NULL; + + #if DIRTYCODE_LOGGING + const char *pConn = (uConnMode == CONNAPI_CONNFLAG_GAMECONN) ? "game" : "voip"; + #endif + + // ref local client info + pSelf = &pConnApi->ClientList.Clients[pConnApi->iSelf]; + + if ((uConnMode == CONNAPI_CONNFLAG_VOIPCONN) && (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED)) + { + uAddr = pConnApi->ClientList.Clients[pConnApi->iVoipHostIndex].ClientInfo.uAddr; + pClientUsed = &pConnApi->ClientList.Clients[pConnApi->iVoipHostIndex]; + NetPrintf(("connapi: [%s] using host address to connect to (or disconnect from) client 0x%08x\n", pConn, pClient->ClientInfo.uId)); + } + else + { + if (pClient->ClientInfo.uAddr == 0) + { + /* we get into passive mode when we don't have an address for our peer and we are expecting the incoming traffic to help us choose the correct remote address. + for clarity of how this works, we can look at the behavior of commudp, given that the game traffic is what comes first in the establishing the connections. + when commudp sees that the address is 0.0.0.0, it will normally not send POKE packets to the peer. when the INIT packets come in the remote address gets updated based + on the source of the traffic. after which point we can complete the connection with our peer having the updated address. + + NOTE: when we introduced prototunnel to the equation the passive functionality was broken because it using a virtual address, not understanding what the real address is. + this means that we will send invalid POKE packets to 0.0.0.0 via the tunnel which can be seen as errors in the logs. when we receive incoming traffic the connection + can still complete successfully using the same logic explained above. + + this behavior is only expected in a connection between an xbox client (using xbox secure networking) and dedicated server (from the standpoint of the server). + if this behavior is seen anywhere else there is a problem in the assigning of address to connapi. dirtysock will recover as long as one side has a correct address. + if both sides have invalid addresses you could expect the connection to fail */ + NetPrintf(("connapi: [%s] using %a address to connect to (or disconnect from) client 0x%08x in passive mode\n", pConn, pClient->ClientInfo.uAddr, pClient->ClientInfo.uId)); + uAddr = pClient->ClientInfo.uAddr; + pClientUsed = pClient; + } + // if external addresses match, use local address + else if ((pSelf->ClientInfo.uAddr & pConnApi->uNetMask) == (pClient->ClientInfo.uAddr & pConnApi->uNetMask)) + { + NetPrintf(("connapi: [%s] using local address to connect to (or disconnect from) client 0x%08x\n", pConn, pClient->ClientInfo.uId)); + uAddr = pClient->ClientInfo.uLocalAddr; + pClientUsed = pClient; + bLocalAddr = TRUE; + } + else + { + NetPrintf(("connapi: [%s] using peer address to connect to (or disconnect from) client 0x%08x\n", pConn, pClient->ClientInfo.uId)); + uAddr = pClient->ClientInfo.uAddr; + + pClientUsed = pClient; + } + + #if DIRTYCODE_DEBUG + if (pClient->ClientInfo.bIsConnectivityHosted == FALSE) + { + if (pConnApi->bFailP2PConnect) + { + // global P2P fail flag is set + NetPrintf(("connapi: [%s] !! P2P CONNECTION FAILURE TRICK !! - global P2P fail - peer address for client 0x%08x replaced with unreachable value\n", pConn, pClient->ClientInfo.uId)); + uAddr = 0; + } + else if (pClient->uFlags & CONNAPI_CLIENTFLAG_P2PFAILDBG) + { + NetPrintf(("connapi: [%s] !! P2P CONNECTION FAILURE TRICK !! - remote P2P fail flag - peer address for client 0x%08x replaced with unreachable value\n", pConn, pClient->ClientInfo.uId)); + uAddr = 0; + } + else if (pConnApi->ClientList.Clients[pConnApi->iSelf].uFlags & CONNAPI_CLIENTFLAG_P2PFAILDBG) + { + NetPrintf(("connapi: [%s] !! P2P CONNECTION FAILURE TRICK !! - self P2P fail flag - peer address for client 0x%08x replaced with unreachable value\n", pConn, pClient->ClientInfo.uId)); + uAddr = 0; + } + } + #endif + } + + *pLocalAddr = bLocalAddr; + + if (pClientUsedRet) + { + *pClientUsedRet = pClientUsed; + } + + return(uAddr); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiVoipGroupConnSharingCallback + + \Description + Use to invoke VoipGroupResume() and VoipGroupSuspend when notified + by the VoipGroup module. + + \Input *pVoipGroup - voip group ref + \Input eCbType - event identifier + \Input iConnId - connection ID + \Input *pUserData - user callback data + \Input bSending - client sending flag + \Input bReceiving - client receiving flag + + \Version 11/11/2009 (mclouatre) +*/ +/********************************************************************************F*/ +static void _ConnApiVoipGroupConnSharingCallback(VoipGroupRefT *pVoipGroup, ConnSharingCbTypeE eCbType, int32_t iConnId, void *pUserData, uint8_t bSending, uint8_t bReceiving) +{ + ConnApiRefT *pConnApi = (ConnApiRefT *)pUserData; + ConnApiClientT *pClient = &pConnApi->ClientList.Clients[iConnId]; + ConnApiClientT *pClientUsed; + int32_t iConnectAddr; + uint8_t bLocalAddr; + + iConnectAddr = _ConnApiGetConnectAddr(pConnApi, pClient, &bLocalAddr, CONNAPI_CONNFLAG_VOIPCONN, &pClientUsed); + + if (eCbType == VOIPGROUP_CBTYPE_CONNSUSPEND) + { + NetPrintf(("connapi: [%p] suspending voip connection to client 0x%08x:%a at %d\n", pConnApi, pClient->ClientInfo.uId, iConnectAddr, NetTick())); + VoipGroupSuspend(pVoipGroup, iConnId); + } + else if (eCbType == VOIPGROUP_CBTYPE_CONNRESUME) + { + // do we have a tunnel to this client? + if (pClientUsed->iTunnelId > 0) + { + NetPrintf(("connapi: [%p] we have a tunnel for client %d; using virtual address %a\n", pConnApi, + iConnId, pClientUsed->iTunnelId)); + iConnectAddr = pClientUsed->iTunnelId; + } + + NetPrintf(("connapi: [%p] resuming voip connection to client 0x%08x:%a at %d\n", pConnApi, pClient->ClientInfo.uId, iConnectAddr, NetTick())); + VoipGroupResume(pVoipGroup, iConnId, iConnectAddr, pClient->VoipInfo.uMnglPort, pClient->VoipInfo.uLocalPort, pClient->ClientInfo.uId, pConnApi->iSessId, pClient->ClientInfo.bIsConnectivityHosted); + #if DIRTYCODE_LOGGING + if (bSending != bReceiving) + { + NetPrintf(("connapi: [%p] warning - send and receive mute flags are different for client 0x%08x:%a\n", pConnApi, pClient->ClientInfo.uId, iConnectAddr)); + } + #endif + VoipGroupMuteByConnId(pVoipGroup, iConnId, bSending); + } + else + { + NetPrintf(("connapi: [%p] critical error - unknown connection sharing event type\n", pConnApi)); + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiGetConnectParms + + \Description + Gets connection parameters. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to connect to + \Input *pConnName - [out] storage for connection name + \Input iNameSize - size of output buffer + + \Output + int32_t - connection flags (NEGAME_CONN_*) + + \Version 04/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ConnApiGetConnectParms(ConnApiRefT *pConnApi, ConnApiClientT *pClient, char *pConnName, int32_t iNameSize) +{ + uint32_t uAddrA, uAddrB, uAddrT; + uint32_t bHosting = TRUE; + int32_t iConnFlags; + + // reference unique address strings + uAddrA = pClient->ClientInfo.uRemoteClientId; + uAddrB = pClient->ClientInfo.uLocalClientId; + + /* determine if we are "hosting" or not, in NetGame terms (one peer + must listen and one peer must connect for each connection) */ + if (pConnApi->eGameTopology != CONNAPI_GAMETOPOLOGY_PEERWEB) + { + // if we're client/server, server listens and clients connect + bHosting = (pConnApi->iGameHostIndex == pConnApi->iSelf); + } + else + { + // if we're peer-web, compare addresses to choose listener and connector + bHosting = uAddrA < uAddrB; + } + + /* set up parms based on whether we are "hosting" or not. the connection name is the + unique address of the "host" concatenated with the unique address of the "client" */ + if (bHosting == TRUE) + { + // swap names + uAddrT = uAddrB; + uAddrB = uAddrA; + uAddrA = uAddrT; + + // set conn flags + iConnFlags = NETGAME_CONN_LISTEN; + } + else + { + iConnFlags = NETGAME_CONN_CONNECT; + } + + // format connection name and return connection flags + ds_snzprintf(pConnName, iNameSize, "%x-%x", uAddrA, uAddrB); + return(iConnFlags); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateCallback + + \Description + Trigger user callback if the state has changed. + + \Input *pConnApi - pointer to module state + \Input iClientIndex - index of client + \Input eType - type of connection (CONNAPI_CBTYPE_*) + \Input eOldStatus - previous status + \Input eNewStatus - current status + + \Output + int32_t - one=active, zero=disconnected + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiUpdateCallback(ConnApiRefT *pConnApi, int32_t iClientIndex, ConnApiCbTypeE eType, ConnApiConnStatusE eOldStatus, ConnApiConnStatusE eNewStatus) +{ + ConnApiCbInfoT CbInfo; + ConnApiConnInfoT *pConnInfo = NULL; + int32_t iIndex; + + // if no change, no callback + if (eOldStatus == eNewStatus) + { + return; + } + + // otherwise, fire off a callback + CbInfo.iClientIndex = iClientIndex; + CbInfo.eType = eType; + CbInfo.eOldStatus = eOldStatus; + CbInfo.eNewStatus = eNewStatus; + CbInfo.pClient = &pConnApi->ClientList.Clients[iClientIndex]; + + if (eType == CONNAPI_CBTYPE_GAMEEVENT) + { + pConnInfo = (ConnApiConnInfoT *)&CbInfo.pClient->GameInfo; + } + else if (eType == CONNAPI_CBTYPE_VOIPEVENT) + { + pConnInfo = (ConnApiConnInfoT *)&CbInfo.pClient->VoipInfo; + } + + // update connection timers + if (pConnInfo != NULL) + { + // finished demangling + if (eOldStatus == CONNAPI_STATUS_MNGL) + { + if (pConnInfo->iConnStart != 0) + { + #if CONNAPI_XBOX_NETWORKING + pConnInfo->ConnTimers.uCreateSATime = NetTickDiff(NetTick(), pConnInfo->iConnStart); + #else + pConnInfo->ConnTimers.uDemangleTime = NetTickDiff(NetTick(), pConnInfo->iConnStart); + #endif + } + else + { + #if CONNAPI_XBOX_NETWORKING + pConnInfo->ConnTimers.uCreateSATime = 0; + #else + pConnInfo->ConnTimers.uDemangleTime = 0; + #endif + } + } + // it went from some state to active or disconnect log connect time + else if ((eOldStatus == CONNAPI_STATUS_CONN) && ((eNewStatus == CONNAPI_STATUS_ACTV) || (eNewStatus == CONNAPI_STATUS_DISC))) + { + if (pConnInfo->uConnFlags & CONNAPI_CONNFLAG_DEMANGLED) + { + if (pConnInfo->iConnStart != 0) + { + pConnInfo->ConnTimers.uDemangleConnectTime = NetTickDiff(NetTick(), pConnInfo->iConnStart); + } + else + { + pConnInfo->ConnTimers.uDemangleConnectTime = 0; + } + } + else + { + if (pConnInfo->iConnStart != 0) + { + pConnInfo->ConnTimers.uConnectTime = NetTickDiff(NetTick(), pConnInfo->iConnStart); + } + else + { + pConnInfo->ConnTimers.uConnectTime = 0; + } + } + } + } + + // call the callback + pConnApi->bInCallback = TRUE; + _ConnApiDefaultCallback(pConnApi, &CbInfo, NULL); + for(iIndex = 0; iIndex < CONNAPI_MAX_CALLBACKS; iIndex++) + { + if (pConnApi->pCallback[iIndex] != NULL) + { + pConnApi->pCallback[iIndex](pConnApi, &CbInfo, pConnApi->pUserData[iIndex]); + } + } + + pConnApi->bInCallback = FALSE; +} + + +/*F********************************************************************************/ +/*! + \Function _ConnApiDestroyGameConnection + + \Description + Destroy game link to given client. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to close game connection for + \Input iClientIndex - index of client in client array + \Input *pReason - reason connection is being closed (for debug use) + + \Version 01/12/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiDestroyGameConnection(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex, const char *pReason) +{ + // if refs are about to be destroyed, notify application + if ((pClient->pGameDistRef != NULL) || (pClient->pGameLinkRef != NULL)) + { + _ConnApiUpdateCallback(pConnApi, iClientIndex, CONNAPI_CBTYPE_DESTEVENT, CONNAPI_STATUS_ACTV, CONNAPI_STATUS_DISC); + } + + // destroy the refs + NetPrintf(("connapi: [%p] destroying game connection to client 0x%08x: %s at %d\n", pConnApi, pClient->ClientInfo.uId, pReason, NetTick())); + if (pClient->pGameLinkRef != NULL) + { + NetGameLinkDestroy(pClient->pGameLinkRef); + pClient->pGameLinkRef = NULL; + } + if (pClient->pGameUtilRef != NULL) + { + NetGameUtilDestroy(pClient->pGameUtilRef); + pClient->pGameUtilRef = NULL; + } + + pClient->GameInfo.eStatus = CONNAPI_STATUS_DISC; + _ConnApiClientReleaseProtoMangleResources(pConnApi, pClient); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiDestroyVoipConnection + + \Description + Destroy voip link to given client. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to close game connection for + \Input *pReason - reason connection is being closed (for debug use) + + \Version 01/12/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiDestroyVoipConnection(ConnApiRefT *pConnApi, ConnApiClientT *pClient, const char *pReason) +{ + if (pClient->iVoipConnId >= 0) + { + NetPrintf(("connapi: destroying voip connection to client 0x%08x: %s at %d\n", pClient->ClientInfo.uId, pReason, NetTick())); + VoipGroupDisconnect(pConnApi->pVoipGroupRef, pClient->iVoipConnId); + pClient->iVoipConnId = VOIP_CONNID_NONE; + } + pClient->VoipInfo.eStatus = CONNAPI_STATUS_DISC; + + _ConnApiClientReleaseProtoMangleResources(pConnApi, pClient); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiDisconnectClient + + \Description + Disconnect a client. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to disconnect + \Input iClientIndex - client index + \Input *pReason - reason for the close + + \Version 01/12/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiDisconnectClient(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex, const char *pReason) +{ + _ConnApiDestroyGameConnection(pConnApi, pClient, iClientIndex, pReason); + _ConnApiDestroyVoipConnection(pConnApi, pClient, pReason); + _ConnApiTunnelFree(pConnApi, pClient); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiInitClientList + + \Description + Initialize client list based on input client list. + + \Input *pConnApi - pointer to module state + \Input *pClientList - list of client addresses + \Input iNumClients - number of clients in list + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiInitClientList(ConnApiRefT *pConnApi, ConnApiClientInfoT *pClientList, int32_t iNumClients) +{ + ConnApiClientT *pClient; + int32_t iClient; + + // make sure client count is below max + if (iNumClients > pConnApi->ClientList.iMaxClients) + { + NetPrintf(("connapi: [%p] cannot host %d clients; clamping to %d\n", pConnApi, iNumClients, pConnApi->ClientList.iMaxClients)); + iNumClients = pConnApi->ClientList.iMaxClients; + } + + // find our index + pConnApi->iSelf = -1; // init so we can check after setup to make sure we're in the list + for (iClient = 0, pConnApi->ClientList.iNumClients = 0; iClient < iNumClients; iClient++) + { + // remember our index in list + if (pClientList[iClient].uId == pConnApi->uSelfId) + { + pConnApi->iSelf = iClient; + } + } + + // copy input client list + for (iClient = 0, pConnApi->ClientList.iNumClients = 0; iClient < iNumClients; iClient++) + { + // ref client structure + pClient = &pConnApi->ClientList.Clients[iClient]; + + // need to check to see if the client passed it is valid. + if (pClientList[iClient].uId != 0) + { + // init client structure and copy user info + _ConnApiInitClient(pConnApi, pClient, &pClientList[iClient], iClient); + + // if us, update dirtyaddr + if (iClient == pConnApi->iSelf) + { + // update dirtyaddr and save ref + ds_memcpy_s(&pConnApi->SelfAddr, sizeof(pConnApi->SelfAddr), &pClient->ClientInfo.DirtyAddr, sizeof(pClient->ClientInfo.DirtyAddr)); + } + + // increment client count + pConnApi->ClientList.iNumClients += 1; + } + } + + // make sure iSelf is valid before we continue + if (pConnApi->iSelf >= 0) + { + // ref local client + pClient = &pConnApi->ClientList.Clients[pConnApi->iSelf]; + + // set local user + if (pConnApi->pVoipRef != NULL) + { + VoipGroupControl(pConnApi->pVoipGroupRef, 'clid', pClient->ClientInfo.uId, 0, NULL); + } + + // set prototunnel user + if (pConnApi->pProtoTunnel != NULL) + { + ProtoTunnelControl(pConnApi->pProtoTunnel, 'clid', pClient->ClientInfo.uId, 0, NULL); + } + + // if the voip is server hosted don't ever establish voip from the host + if ((pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) && (pConnApi->iVoipHostIndex == pConnApi->iSelf)) + { + pConnApi->uConnFlags &= ~CONNAPI_CONNFLAG_VOIPCONN; + } + } + + // set initial client flags + _ConnApiUpdateClientFlags(pConnApi); + + #if DIRTYCODE_LOGGING + // make sure we're in the list + if (pConnApi->iSelf == -1) + { + NetPrintf(("connapi: local client 0x%08x not found in client list\n", pConnApi->uSelfId)); + } + // display client list + _ConnApiDisplayClientList(&pConnApi->ClientList); + #endif +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiParseAdvertField + + \Description + Parse a field from an advertisement. + + \Input *pOutBuf - output buffer for field + \Input iOutSize - size of output buffer + \Input *pInpBuf - pointer to start of field in advertisement buffer + \Input cTerminator - field termination character + + \Output + char * - pointer to next field + + \Version 01/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ConnApiParseAdvertField(char *pOutBuf, int32_t iOutSize, char *pInpBuf, char cTerminator) +{ + char *pEndPtr; + + pEndPtr = strchr(pInpBuf, cTerminator); + *pEndPtr = '\0'; + + ds_strnzcpy(pOutBuf, pInpBuf, iOutSize); + return(pEndPtr+1); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiCheckAdvert + + \Description + Scan current adverts for any adverts that are broadcast by clients we are + connecting to. + + \Input *pConnApi - pointer to module state + + \Version 01/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiCheckAdvert(ConnApiRefT *pConnApi) +{ + char strAdvtList[512], *pAdvt, *pTemp; + char strName[32], strNote[32], strAddr[32]; + ConnApiClientT *pClient; + int32_t iAdvt, iNumAdvt, iClient; + uint32_t uAdvtId; + uint32_t uLocalAddr; + + // see if there are any advertisements + iNumAdvt = NetGameUtilQuery(pConnApi->pGameUtilRef, pConnApi->strGameName, strAdvtList, sizeof(strAdvtList)); + NetPrintf(("connapi: [%p] found %d advertisements\n", pConnApi, iNumAdvt)); + + // parse any advertisements + for (pAdvt = strAdvtList, iAdvt = 0; iAdvt < iNumAdvt; iAdvt++) + { + // extract info from advertisement + pAdvt = _ConnApiParseAdvertField(strName, sizeof(strName), pAdvt, '\t'); + pAdvt = _ConnApiParseAdvertField(strNote, sizeof(strNote), pAdvt, '\t'); + pAdvt = _ConnApiParseAdvertField(strAddr, sizeof(strAddr), pAdvt+4, '\n'); + + sscanf(strName, "%u", &uAdvtId); + + // extract address from addr field + pTemp = strchr(strAddr, ':'); + *pTemp = '\0'; + uLocalAddr = SocketInTextGetAddr(strAddr); + + // does the name match one of our client's names? + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + pClient = &pConnApi->ClientList.Clients[iClient]; + + if ((uAdvtId == pClient->ClientInfo.uId) && (pClient->ClientInfo.uLocalAddr != uLocalAddr)) + { + NetPrintf(("connapi: updating local address of machine Id %u from %a to %a\n", uAdvtId, pClient->ClientInfo.uAddr, uLocalAddr)); + pClient->ClientInfo.uLocalAddr = uLocalAddr; + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiSetGamelinkOpt + + \Description + Set a gamelink option, if it isn't the default. + + \Input *pUtilRef - pointer to util ref for game link + \Input iOpt - gamelink option to set + \Input iValue - value to set + + \Version 06/03/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiSetGamelinkOpt(NetGameUtilRefT *pUtilRef, int32_t iOpt, int32_t iValue) +{ + if (iValue != 0) + { + NetGameUtilControl(pUtilRef, iOpt, iValue); + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiDemangleReport + + \Description + Initiate a demangler report to indicate connection success or failure. + + \Input *pConnApi - pointer to module state + \Input *pInfo - connection info + \Input eStatus - connection result (success/fail) + + \Version 01/17/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiDemangleReport(ConnApiRefT *pConnApi, ConnApiConnInfoT *pInfo, ProtoMangleStatusE eStatus) +{ + if (pInfo->bDemangling != TRUE) + { + // not demangling, nothing to report + return; + } + + ProtoMangleReport(pConnApi->pProtoMangle, eStatus, -1); + pConnApi->bReporting = TRUE; + pInfo->bDemangling = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiRemoveClient + + \Description + Remove a current client from a game. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to remove + \Input iClientIndex - index of client to remove + + \Version 04/08/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiRemoveClient(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex) +{ + ConnApiConnStatusE eGameStatus = pClient->GameInfo.eStatus; + ConnApiConnStatusE eVoipStatus = pClient->VoipInfo.eStatus; + ConnApiConnStatusE eNewGameStatus; + ConnApiConnStatusE eNewVoipStatus; + + // disconnect them + _ConnApiDisconnectClient(pConnApi, pClient, iClientIndex, "removal"); + + eNewGameStatus = pClient->GameInfo.eStatus; + eNewVoipStatus = pClient->VoipInfo.eStatus; + + // if we were demangling them, abort demangling + #if CONNAPI_XBOX_NETWORKING + if ((pClient->GameInfo.eStatus == CONNAPI_STATUS_MNGL) || (pClient->VoipInfo.eStatus == CONNAPI_STATUS_MNGL)) + #else + if ((pClient->GameInfo.bDemangling == TRUE) || (pClient->VoipInfo.bDemangling == TRUE)) + #endif + { + NetPrintf(("connapi: aborting demangle of client 0x%08x - being removed from client list\n", pClient->ClientInfo.uId)); + ProtoMangleControl(pConnApi->pProtoMangle, 'abrt', iClientIndex, 0, NULL); + } + + // decrement overall count + pConnApi->ClientList.iNumClients -= 1; + + ds_memclr(&pConnApi->ClientList.Clients[iClientIndex], sizeof(ConnApiClientT)); + + // send a disconnect event + if (pConnApi->bRemoveCallback == TRUE) + { + if ((pConnApi->uConnFlags & CONNAPI_CONNFLAG_GAMECONN) != 0) + { + _ConnApiUpdateCallback(pConnApi, iClientIndex, CONNAPI_CBTYPE_GAMEEVENT, eGameStatus, eNewGameStatus); + } + if ((pConnApi->uConnFlags & CONNAPI_CONNFLAG_VOIPCONN) != 0) + { + _ConnApiUpdateCallback(pConnApi, iClientIndex, CONNAPI_CBTYPE_VOIPEVENT, eVoipStatus, eNewVoipStatus); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateGameClient + + \Description + Process game connection associated with this client. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to update + \Input iClientIndex - index of client + + \Output + int32_t - one=active, zero=disconnected + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ConnApiUpdateGameClient(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex) +{ + ConnApiConnStatusE eStatus = pClient->GameInfo.eStatus; + uint8_t bLocalAddr; + int32_t iConnTimeout; + + // are game connections disabled? + if ((pConnApi->uConnFlags & CONNAPI_CONNFLAG_GAMECONN) == 0) + { + // if we're not connected, just bail + if ((pClient->GameInfo.eStatus == CONNAPI_STATUS_INIT) || (pClient->GameInfo.eStatus == CONNAPI_STATUS_DISC)) + { + return(0); + } + + // if we're voip only and not already disconnected, kill the connection + _ConnApiDestroyGameConnection(pConnApi, pClient, iClientIndex, "connection closed (voiponly)"); + } + + // handle initial connection state + if (pClient->GameInfo.eStatus == CONNAPI_STATUS_INIT) + { + ConnApiClientT *pClientUsed; + + // get address to connect with + int32_t iConnectAddr = _ConnApiGetConnectAddr(pConnApi, pClient, &bLocalAddr, CONNAPI_CONNFLAG_GAMECONN, &pClientUsed); + + NetPrintf(("connapi: [%p] establishing game connection to client 0x%08x:%a at %d\n", pConnApi, pClient->ClientInfo.uId, iConnectAddr, NetTick())); + + // create tunnel? + if ((pConnApi->bTunnelEnabled) && (pClientUsed->iTunnelId == 0)) + { + _ConnApiTunnelAlloc(pConnApi, pClientUsed, iClientIndex, iConnectAddr, bLocalAddr); + } + + // do we have a tunnel to this client? + if (pClientUsed->iTunnelId > 0) + { + NetPrintf(("connapi: [%p] tunnel allocated for client %d (local id 0x%08x, remote id 0x%08x); switching to use virtual address %a\n", pConnApi, + iClientIndex, pClient->ClientInfo.uLocalClientId, + (pClient->ClientInfo.bIsConnectivityHosted ? pClient->ClientInfo.uHostingServerId : pClient->ClientInfo.uRemoteClientId), pClientUsed->iTunnelId)); + iConnectAddr = pClientUsed->iTunnelId; + } + + // try to create game connection + DirtyMemGroupEnter(pConnApi->iMemGroup, pConnApi->pMemGroupUserData); + pClient->pGameUtilRef = NetGameUtilCreate(); + DirtyMemGroupLeave(); + if (pClient->pGameUtilRef != NULL) + { + char strConn[128], strConnName[64]; + int32_t iConnFlags; + + // set game link options + _ConnApiSetGamelinkOpt(pClient->pGameUtilRef, 'minp', pConnApi->iGameMinp); + _ConnApiSetGamelinkOpt(pClient->pGameUtilRef, 'mout', pConnApi->iGameMout); + _ConnApiSetGamelinkOpt(pClient->pGameUtilRef, 'mwid', pConnApi->iGameMwid); + if (pConnApi->iGameUnackLimit != 0) + { + _ConnApiSetGamelinkOpt(pClient->pGameUtilRef, 'ulmt', pConnApi->iGameUnackLimit); + } + + // set our client id (used by gameserver to uniquely identify us) + NetGameUtilControl(pClient->pGameUtilRef, 'clid', pClient->ClientInfo.uLocalClientId); + NetGameUtilControl(pClient->pGameUtilRef, 'rcid', pClient->ClientInfo.uRemoteClientId); + + // determine connection parameters + iConnFlags = _ConnApiGetConnectParms(pConnApi, pClient, strConnName, sizeof(strConnName)); + + // format connect string + ds_snzprintf(strConn, sizeof(strConn), "%a:%d:%d#%s", iConnectAddr, + pClient->GameInfo.uLocalPort, pClient->GameInfo.uMnglPort, strConnName); + + // start the connection attempt + NetGameUtilConnect(pClient->pGameUtilRef, iConnFlags, strConn, pConnApi->pCommConstruct); + pClient->GameInfo.eStatus = CONNAPI_STATUS_CONN; + + // remember connection start time + pClient->GameInfo.iConnStart = NetTick(); + + NetGameUtilControl(pClient->pGameUtilRef, 'meta', (pConnApi->bCommUdpMetadata || (pClient->ClientInfo.bIsConnectivityHosted)) ? 1 : 0); + } + else + { + NetPrintf(("connapi: unable to allocate util ref for connection %d to client 0x%08x\n", iClientIndex, pClient->ClientInfo.uId)); + pClient->GameInfo.eStatus = CONNAPI_STATUS_DISC; + _ConnApiClientReleaseProtoMangleResources(pConnApi, pClient); + } + } + + // waiting for connection + if (pClient->GameInfo.eStatus == CONNAPI_STATUS_CONN) + { + void *pCommRef; + + if (pClient->pGameLinkRef == NULL) + { + // check for established connection + if ((pCommRef = NetGameUtilComplete(pClient->pGameUtilRef)) != NULL) + { + DirtyMemGroupEnter(pConnApi->iMemGroup, pConnApi->pMemGroupUserData); + pClient->pGameLinkRef = NetGameLinkCreate(pCommRef, FALSE, pConnApi->iLinkBufSize); + DirtyMemGroupLeave(); + if (pClient->pGameLinkRef != NULL) + { + NetPrintf(("connapi: game connection %d to client 0x%08x established at %d\n", iClientIndex, pClient->ClientInfo.uId, NetTick())); + + if (pClient->ClientInfo.bEnableQos) + { + NetPrintf(("connapi: enabling QoS over NetGameLink on connection %d to client 0x%08x\n", iClientIndex, pClient->ClientInfo.uId)); + NetGameLinkControl(pClient->pGameLinkRef, 'sqos', pConnApi->iQosDuration, &pConnApi->iQosInterval); + NetGameLinkControl(pClient->pGameLinkRef, 'lqos', pConnApi->iQosPacketSize, NULL); + } + + // if we were demangling, report success + _ConnApiDemangleReport(pConnApi, &pClient->GameInfo, PROTOMANGLE_STATUS_CONNECTED); + + // save socket ref for multi-demangle if we need it + if (!pConnApi->bTunnelEnabled) + { + NetGameUtilStatus(pClient->pGameUtilRef, 'sock', &pConnApi->uGameSockRef, sizeof(pConnApi->uGameSockRef)); + } + + // indicate we've connected + pClient->GameInfo.uConnFlags |= CONNAPI_CONNFLAG_CONNECTED; + } + else + { + NetPrintf(("connapi: unable to allocate link ref for connection %d to client 0x%08x\n", iClientIndex, pClient->ClientInfo.uId)); + pClient->GameInfo.eStatus = CONNAPI_STATUS_DISC; + _ConnApiClientReleaseProtoMangleResources(pConnApi, pClient); + } + } + } + + // check for gamelink saying we're connected + if (pClient->pGameLinkRef != NULL) + { + NetGameLinkStatT Stat; + + // give time to NetGameLink to run any connection-related processes + NetGameLinkUpdate(pClient->pGameLinkRef); + + // get link stats + NetGameLinkStatus(pClient->pGameLinkRef, 'stat', 0, &Stat, sizeof(NetGameLinkStatT)); + + // see if we're open + if (Stat.isopen == TRUE) + { + // mark as active + NetPrintf(("connapi: game connection %d to client 0x%08x is active at %d\n", iClientIndex, pClient->ClientInfo.uId, NetTick())); + pClient->GameInfo.eStatus = CONNAPI_STATUS_ACTV; + } + } + + // check for connection timeout + iConnTimeout = pConnApi->iConnTimeout; + + // check for connection timeout + // The connection timeout, and a subsequent disconnection, should only occur if we still have not connected to the peer. + // If we have a pGameLinkRef, then that means we MUST have established a connection to the peer, but are still doing QoS. + if ((pClient->GameInfo.eStatus == CONNAPI_STATUS_CONN) && (pClient->pGameLinkRef == NULL) && (NetTickDiff(NetTick(), pClient->GameInfo.iConnStart) > iConnTimeout)) + { + _ConnApiDestroyGameConnection(pConnApi, pClient, iClientIndex, "connection timeout"); + + // on xboxone, we never want to go back to MNGL state from CONN state + #if CONNAPI_XBOX_NETWORKING + NetPrintf(("connapi: game connection to client 0x%08x failed\n", pClient->ClientInfo.uId)); + if (pClient->GameInfo.bDemangling) + { + _ConnApiDemangleReport(pConnApi, &pClient->GameInfo, PROTOMANGLE_STATUS_FAILED); + } + + pClient->GameInfo.eStatus = CONNAPI_STATUS_DISC; + #else + // initial attempt to connect failed + if (pClient->GameInfo.bDemangling == FALSE) + { + if ((pConnApi->bDemanglerEnabled) && (pClient->ClientInfo.bIsConnectivityHosted == FALSE) && (pConnApi->eGameTopology != CONNAPI_GAMETOPOLOGY_SERVERHOSTED)) + { + NetPrintf(("connapi: game status=mngl for connection %d to client 0x%08x\n", iClientIndex, pClient->ClientInfo.uId)); + pClient->GameInfo.eStatus = CONNAPI_STATUS_MNGL; + } + else + { + if (pConnApi->bDemanglerEnabled && pClient->ClientInfo.bIsConnectivityHosted == TRUE) + { + NetPrintf(("connapi: demangling skipped for connection %d to client 0x%08x because the client is connectivity hosted\n", iClientIndex, pClient->ClientInfo.uId)); + } + + pClient->GameInfo.eStatus = CONNAPI_STATUS_DISC; + } + } + else + { + NetPrintf(("connapi: game connection to client 0x%08x after demangle failed\n", pClient->ClientInfo.uId)); + _ConnApiDemangleReport(pConnApi, &pClient->GameInfo, PROTOMANGLE_STATUS_FAILED); + + pClient->GameInfo.eStatus = CONNAPI_STATUS_DISC; + } + #endif + } // timeout + + // set the packet receive flag if a packet was received + if ((pClient->pGameUtilRef != NULL) && (NetGameUtilStatus(pClient->pGameUtilRef, 'pkrc', NULL, 0) == TRUE)) + { + pClient->GameInfo.uConnFlags |= CONNAPI_CONNFLAG_PKTRECEIVED; + } + } // waiting for connection + + // update connection status during active phase + if (pClient->GameInfo.eStatus == CONNAPI_STATUS_ACTV) + { + // get link stats + NetGameLinkStatT Stat; + NetGameLinkStatus(pClient->pGameLinkRef, 'stat', 0, &Stat, sizeof(NetGameLinkStatT)); + + // make sure connection is still open + if (Stat.isopen == FALSE) + { + _ConnApiDestroyGameConnection(pConnApi, pClient, iClientIndex, "connection closed"); + } + // see if we've timed out + else if (NetTickDiff(Stat.tick, Stat.rcvdlast) > pConnApi->iTimeout) + { + _ConnApiDestroyGameConnection(pConnApi, pClient, iClientIndex, "connection timed out"); + } + } + + // trigger callback if state change + _ConnApiUpdateCallback(pConnApi, iClientIndex, CONNAPI_CBTYPE_GAMEEVENT, eStatus, (ConnApiConnStatusE)pClient->GameInfo.eStatus); + + // return active or inactive + return(pClient->GameInfo.eStatus != CONNAPI_STATUS_DISC); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateVoipClient + + \Description + Process voip connection associated with this connection. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to connection to update + \Input iClientIndex - index of connection + + \Output + int32_t - one=active, zero=disconnected + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ConnApiUpdateVoipClient(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex) +{ + ConnApiConnStatusE eStatus = pClient->VoipInfo.eStatus; + int32_t iVoipConnId, iVoipStatus = 0; + uint8_t bLocalAddr; + + // are voip connections disabled? + if ((pConnApi->uConnFlags & CONNAPI_CONNFLAG_VOIPCONN) == 0) + { + // if we're not connected, just bail + if ((pClient->VoipInfo.eStatus == CONNAPI_STATUS_INIT) || (pClient->VoipInfo.eStatus == CONNAPI_STATUS_DISC)) + { + return(0); + } + + // if we're game only and not already disconnected, close the connection + _ConnApiDestroyVoipConnection(pConnApi, pClient, "connection closed (gameonly)"); + } + + // handle initial connection state + if (pClient->VoipInfo.eStatus == CONNAPI_STATUS_INIT) + { + ConnApiClientT *pClientUsed; + int32_t iAdjustedVoipClientIndex = (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) ? iClientIndex - 1 : iClientIndex; + + // get address to connect with + int32_t iConnectAddr = _ConnApiGetConnectAddr(pConnApi, pClient, &bLocalAddr, CONNAPI_CONNFLAG_VOIPCONN, &pClientUsed); + + NetPrintf(("connapi: [%p] establishing voip connection to client 0x%08x:%a at %d\n", pConnApi, + pClient->ClientInfo.uId, iConnectAddr, NetTick())); + + // create tunnel? + if ((pConnApi->bTunnelEnabled) && (pClientUsed->iTunnelId == 0)) + { + _ConnApiVoipTunnelAlloc(pConnApi, pClientUsed, iClientIndex, iConnectAddr, bLocalAddr); + } + + // do we have a tunnel to this client? + if (pClientUsed->iTunnelId > 0) + { + NetPrintf(("connapi: [%p] tunnel allocated for client %d; switching to use virtual address %a\n", pConnApi, + iClientIndex, pClientUsed->iTunnelId)); + iConnectAddr = pClientUsed->iTunnelId; + } + + // initiate connection attempt + // $$todo - deprecate 'vcid' and add a new uLocalClientId parameter to VoipGroupConnect() - the current implementation can be problematic if + // the return conn id does not correspond to the one set with 'vcid' + VoipGroupControl(pConnApi->pVoipGroupRef, 'vcid', iAdjustedVoipClientIndex, 0, &pClient->ClientInfo.uLocalClientId); + iVoipConnId = VoipGroupConnect(pConnApi->pVoipGroupRef, iAdjustedVoipClientIndex, iConnectAddr, pClient->VoipInfo.uMnglPort, pClient->VoipInfo.uLocalPort, + pClient->ClientInfo.uId, pConnApi->iSessId, pClient->ClientInfo.bIsConnectivityHosted, pClient->ClientInfo.uRemoteClientId); + + if (iVoipConnId >= 0) + { + pClient->iVoipConnId = iVoipConnId; + pClient->VoipInfo.eStatus = CONNAPI_STATUS_CONN; + pClient->VoipInfo.iConnStart = NetTick(); + } + else + { + NetPrintf(("connapi: unable to init voip for client index %d (client id: 0x%08x)\n", iClientIndex, pClient->ClientInfo.uId)); + pClient->VoipInfo.eStatus = CONNAPI_STATUS_DISC; + _ConnApiClientReleaseProtoMangleResources(pConnApi, pClient); + } + } + + // get connection status + if ((pClient->VoipInfo.eStatus == CONNAPI_STATUS_CONN) || (pClient->VoipInfo.eStatus == CONNAPI_STATUS_ACTV)) + { + iVoipStatus = VoipGroupConnStatus(pConnApi->pVoipGroupRef, pClient->iVoipConnId); + } + + // for both cases below, CONNAPI_STATUS_CONN and CONNAPI_STATUS_ACTV, when we detect a disconnection, + // we won't set the iVoipConnId to NONE nor trigger a disconnection. This is because the voipgroup code, + // in order to provide correct voip connection sharing, needs to be told only of authoritative "connect" + // and "disconnect" by the higher-level lobby (plasma, lobbysdk, blazesdk, ...) techonology. + + // update connection status during connect phase + if (pClient->VoipInfo.eStatus == CONNAPI_STATUS_CONN) + { + // check for established connection + if (iVoipStatus & VOIP_CONN_CONNECTED) + { + NetPrintf(("connapi: voip connection for client index %d (client id: 0x%08x) established at %d\n", iClientIndex, pClient->ClientInfo.uId, NetTick())); + + // if we were demangling, report success + _ConnApiDemangleReport(pConnApi, &pClient->VoipInfo, PROTOMANGLE_STATUS_CONNECTED); + + // save socket ref for multi-demangle if we need it + if (!pConnApi->bTunnelEnabled) + { + VoipGroupStatus(pConnApi->pVoipGroupRef, 'sock', 0, &pConnApi->uVoipSockRef, sizeof(pConnApi->uVoipSockRef)); + } + + // mark as active + pClient->VoipInfo.eStatus = CONNAPI_STATUS_ACTV; + pClient->VoipInfo.uConnFlags |= CONNAPI_CONNFLAG_CONNECTED; + } + else if (iVoipStatus & VOIP_CONN_STOPPED) + { + NetPrintf(("connapi: voip connection attempt to client 0x%08x failed at %d\n", pClient->ClientInfo.uId, NetTick())); + + // on xboxone, we never want to go back to MNGL state from CONN state + #if CONNAPI_XBOX_NETWORKING + NetPrintf(("connapi: voip connection attempt to client 0x%08x failed\n", pClient->ClientInfo.uId)); + if (pClient->VoipInfo.bDemangling == TRUE) + { + _ConnApiDemangleReport(pConnApi, &pClient->VoipInfo, PROTOMANGLE_STATUS_FAILED); + } + + pClient->VoipInfo.eStatus = CONNAPI_STATUS_DISC; + #else + if (pClient->VoipInfo.bDemangling == TRUE) + { + NetPrintf(("connapi: voip connection attempt to client 0x%08x after demangle failed\n", pClient->ClientInfo.uId)); + _ConnApiDemangleReport(pConnApi, &pClient->VoipInfo, PROTOMANGLE_STATUS_FAILED); + + pClient->VoipInfo.eStatus = CONNAPI_STATUS_DISC; + } + else + { + if (pConnApi->bDemanglerEnabled && pClient->ClientInfo.bIsConnectivityHosted == FALSE) + { + NetPrintf(("connapi: voip status=mngl for client index %d (client id: 0x%08x)\n", iClientIndex, pClient->ClientInfo.uId)); + pClient->VoipInfo.eStatus = CONNAPI_STATUS_MNGL; + } + else + { + if (pConnApi->bDemanglerEnabled && pClient->ClientInfo.bIsConnectivityHosted == TRUE) + { + NetPrintf(("connapi: voip connection demangling skipped for client index %d (client id: 0x%08x) because the client is connectivity hosted\n", iClientIndex, pClient->ClientInfo.uId)); + } + + pClient->VoipInfo.eStatus = CONNAPI_STATUS_DISC; + } + } // bDemangling = FALSE + #endif + } // REMOTE_DISCONNECTED + } // STATUS_CONN + + // update client in active state + if (pClient->VoipInfo.eStatus == CONNAPI_STATUS_ACTV) + { + if (iVoipStatus & VOIP_CONN_STOPPED) + { + NetPrintf(("connapi: voip connection to client 0x%08x terminated at %d\n", pClient->ClientInfo.uId, NetTick())); + pClient->VoipInfo.eStatus = CONNAPI_STATUS_DISC; + _ConnApiClientReleaseProtoMangleResources(pConnApi, pClient); + } + } + + // trigger callback if state change + _ConnApiUpdateCallback(pConnApi, iClientIndex, CONNAPI_CBTYPE_VOIPEVENT, eStatus, (ConnApiConnStatusE)pClient->VoipInfo.eStatus); + + // return active or inactive + return(pClient->VoipInfo.eStatus != CONNAPI_STATUS_DISC); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateDemangleReport + + \Description + Update demangler during the Report phase. + + \Input *pConnApi - pointer to module state + + \Output + uint32_t - TRUE if reporting is in progress, FALSE otherwise + + \Version 01/17/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ConnApiUpdateDemangleReport(ConnApiRefT *pConnApi) +{ + // if not reporting, don't process + if (pConnApi->bReporting != FALSE) + { + #if CONNAPI_XBOX_NETWORKING + // there is no reporting phase on xbox one... so we alway fake that reporting is complete + pConnApi->bReporting = FALSE; + #else + // update client + ProtoMangleUpdate(pConnApi->pProtoMangle); + + // check for completion + if (ProtoMangleComplete(pConnApi->pProtoMangle, NULL, NULL) != 0) + { + pConnApi->bReporting = FALSE; + } + #endif + } + + return(pConnApi->bReporting); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateDemangle + + \Description + Update client connection in CONNAPI_STATUS_MNGL state. + + \Input *pConnApi - pointer to module state + \Input *pClient - pointer to client to update + \Input iClientIndex - index of client + \Input *pConnInfo - pointer to connection info (game or voip) + \Input iType - type of connection (zero=game, one=voip) + + \Version 01/13/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiUpdateDemangle(ConnApiRefT *pConnApi, ConnApiClientT *pClient, int32_t iClientIndex, ConnApiConnInfoT *pConnInfo, int32_t iType) +{ + static const char _Types[2][5] = { "game", "voip" }; + static uint32_t _Flags[2] = { CONNAPI_CONNFLAG_GAMECONN, CONNAPI_CONNFLAG_VOIPCONN }; + ConnApiCbTypeE eCbType; + + #if !CONNAPI_XBOX_NETWORKING + int32_t iClient; + #endif + + // initialize eType + if (iType == 0) + { + eCbType = CONNAPI_CBTYPE_GAMEEVENT; + } + else + { + eCbType = CONNAPI_CBTYPE_VOIPEVENT; + } + + // ignore game/voip demangle if we're not doing game/voip connections + if ((_Flags[iType] & pConnApi->uConnFlags) == 0) + { + return; + } + + #if !CONNAPI_XBOX_NETWORKING + // if anyone is in a connecting state, wait to demangle + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + if ((pConnApi->ClientList.Clients[iClient].GameInfo.eStatus == CONNAPI_STATUS_CONN) || + (pConnApi->ClientList.Clients[iClient].VoipInfo.eStatus == CONNAPI_STATUS_CONN)) + { + NetPrintf(("connapi: [%p] deferring demangle until there are no ongoing connection attempts\n", pConnApi)); + return; + } + } + #endif + + // tunnel-specific processing + if (pConnApi->bTunnelEnabled) + { + // if we've already demangled the tunnel port, use previous demangle result + if (pClient->uFlags & CONNAPI_CLIENTFLAG_TUNNELPORTDEMANGLED) + { + ConnApiConnStatusE eStatus = pConnInfo->eStatus; + pConnInfo->eStatus = CONNAPI_STATUS_INIT; + pConnInfo->uConnFlags |= CONNAPI_CONNFLAG_DEMANGLED; + + NetPrintf(("connapi: [%p] reusing previously demangled tunnel port\n", pConnApi)); + + // trigger callback if state change + _ConnApiUpdateCallback(pConnApi, iClientIndex, eCbType, eStatus, (ConnApiConnStatusE)pConnInfo->eStatus); + return; + } + } + else + { + // if we've already resolved the secure address, use it + if (pClient->uFlags & CONNAPI_CLIENTFLAG_SECUREADDRRESOLVED) + { + ConnApiConnStatusE eStatus = pConnInfo->eStatus; + pConnInfo->eStatus = CONNAPI_STATUS_INIT; + pConnInfo->uConnFlags |= CONNAPI_CONNFLAG_DEMANGLED; + + NetPrintf(("connapi: [%p] reusing previously resolved secure address\n", pConnApi)); + + // trigger callback if state change + _ConnApiUpdateCallback(pConnApi, iClientIndex, eCbType, eStatus, (ConnApiConnStatusE)pConnInfo->eStatus); + return; + } + } + + // are we in an idle state? + if (ProtoMangleStatus(pConnApi->pProtoMangle, 'idle', NULL, iClientIndex)) + { + char strSess[64]; + intptr_t uSockRef; + uint32_t uDemanglePort = 0; + + // show we're demangling + pConnInfo->bDemangling = TRUE; + + // create session id + _ConnApiGenerateSessionKey(pConnApi, pClient, iClientIndex, strSess, sizeof(strSess), _Types[iType]); + + // get socket ref + if (pConnApi->bTunnelEnabled) + { + uSockRef = pConnApi->uTunlSockRef; + } + else if (iType == 0) + { + uSockRef = pConnApi->uGameSockRef; + } + else + { + uSockRef = pConnApi->uVoipSockRef; + } + + // if socket ref is null, connect normally + if (uSockRef == 0) + { + #if !CONNAPI_RNDLCLDEMANGLEPORT + uDemanglePort = (iType == 0) ? pConnApi->uGamePort : pConnApi->uVoipPort; + uDemanglePort = (pConnApi->bTunnelEnabled) ? (unsigned)pConnApi->iTunnelPort : uDemanglePort; + #else + uDemanglePort = _ConnApiGenerateDemanglePort(pConnApi); + if (pConnApi->bTunnelEnabled) + { + // if we're tunneling, we need to recreate the tunnel socket bound to the new port + ProtoTunnelControl(pConnApi->pProtoTunnel, 'bind', uDemanglePort, 0, NULL); + } + else + { + // if not tunneling, we need to update the local port info with the new local (bind) demangle port + pConnInfo->uLocalPort = (uint16_t)uDemanglePort; + } + #endif + // if tunneling we always want to use the tunnel socket ref for demangling + if (pConnApi->bTunnelEnabled) + { + ProtoTunnelStatus(pConnApi->pProtoTunnel, 'sock', 0, &pConnApi->uTunlSockRef, sizeof(pConnApi->uTunlSockRef)); + uSockRef = pConnApi->uTunlSockRef; + } + } + + if (iType == 0) + { + pClient->GameInfo.iConnStart = NetTick(); + } + else + { + pClient->VoipInfo.iConnStart = NetTick(); + } + + #if CONNAPI_XBOX_NETWORKING + { + // since the SecureDeviceAddr fits in the DirtyAddrT, it is safe to use a buffer as large as a DirtyAddrT + char aSecureDeviceAddressBlob[DIRTYADDR_MACHINEADDR_MAXLEN]; + int32_t iBlobSize; + + if (DirtyAddrGetInfoXboxOne(&pConnApi->ClientList.Clients[iClientIndex].ClientInfo.DirtyAddr, NULL, aSecureDeviceAddressBlob, &iBlobSize)) + { + NetPrintf(("connapi: initiating %s secure address resolution of client 0x%08x at %d\n", _Types[iType], pClient->ClientInfo.uId, NetTick())); + ProtoMangleConnect2(pConnApi->pProtoMangle, iClientIndex, aSecureDeviceAddressBlob, iBlobSize); + } + else + { + ConnApiConnStatusE eStatus = pConnInfo->eStatus; + NetPrintf(("connapi: failed to initiate the %s secure address resolution of client 0x%08x due to device address being invalid\n", _Types[iType], pClient->ClientInfo.uId)); + pConnInfo->eStatus = CONNAPI_STATUS_DISC; + _ConnApiUpdateCallback(pConnApi, iClientIndex, eCbType, eStatus, (ConnApiConnStatusE)pConnInfo->eStatus); + return; + } + } + #else + // before demangling, flush the tunnel to make sure there are no buffered packets + if (pConnApi->bTunnelEnabled) + { + ProtoTunnelControl(pConnApi->pProtoTunnel, 'flsh', pClient->iTunnelId, 0, NULL); + } + + // kick off demangling process + if (uSockRef == 0) + { + NetPrintf(("connapi: initiating %s:%d demangle of client 0x%08x at %d\n", _Types[iType], uDemanglePort, pClient->ClientInfo.uId, NetTick())); + ProtoMangleConnect(pConnApi->pProtoMangle, uDemanglePort, strSess); + + } + else + { + NetPrintf(("connapi: initiating %s demangle of client 0x%08x using sockref 0x%08x at %d\n", _Types[iType], pClient->ClientInfo.uId, uSockRef, NetTick())); + ProtoMangleConnectSocket(pConnApi->pProtoMangle, uSockRef, strSess); + } + #endif + } + else + { + if (pConnInfo->bDemangling != FALSE) + { + int32_t iAddr, iPort, iResult; + + // update client + ProtoMangleUpdate(pConnApi->pProtoMangle); + + // check for completion + #if CONNAPI_XBOX_NETWORKING + iPort = iClientIndex; + #endif + if ((iResult = ProtoMangleComplete(pConnApi->pProtoMangle, &iAddr, &iPort)) != 0) + { + ConnApiConnStatusE eStatus = pConnInfo->eStatus; + + if (eStatus != CONNAPI_STATUS_ACTV) + { + if (iResult > 0) + { + #if CONNAPI_XBOX_NETWORKING + NetPrintf(("connapi: %s secure address resolution for user 0x%08x is successful ipaddr=%a at %d\n", + _Types[iType], pClient->ClientInfo.uId, iAddr, NetTick())); + iPort = pConnInfo->uMnglPort; + #else + NetPrintf(("connapi: %s demangle of client 0x%08x successful port=%d at %d\n", _Types[iType], pClient->ClientInfo.uId, iPort, NetTick())); + #endif + + pClient->ClientInfo.uAddr = pClient->ClientInfo.uLocalAddr = iAddr; + if (pConnApi->bTunnelEnabled) + { + // for xboxone, clients do not yet have a valid tunnel id at this point because client enters to MNGL state before INIT state (unlike other platforms) + #if !CONNAPI_XBOX_NETWORKING + ProtoTunnelControl(pConnApi->pProtoTunnel, 'rprt', pClient->iTunnelId, iPort, NULL); + #endif + + pClient->uFlags |= CONNAPI_CLIENTFLAG_TUNNELPORTDEMANGLED; + } + else + { + pConnInfo->uMnglPort = (uint16_t)iPort; + + #if CONNAPI_XBOX_NETWORKING + pClient->uFlags |= CONNAPI_CLIENTFLAG_SECUREADDRRESOLVED; + #endif + } + pConnInfo->eStatus = CONNAPI_STATUS_INIT; + pConnInfo->uConnFlags |= CONNAPI_CONNFLAG_DEMANGLED; + } + else + { + NetPrintf(("connapi: %s demangle of client 0x%08x failed (timeout=%s)\n", _Types[iType], pClient->ClientInfo.uId, + ProtoMangleStatus(pConnApi->pProtoMangle, 'time', NULL, iClientIndex) ? "true" : "false")); + pConnInfo->eStatus = CONNAPI_STATUS_DISC; + } + } + else + { + NetPrintf(("connapi: [%p] %s demangle of client 0x%08x finished with %d but ignored because connection aleady active (timeout=%s)\n", pConnApi, + _Types[iType], pClient->ClientInfo.uId, iResult, ProtoMangleStatus(pConnApi->pProtoMangle, 'time', NULL, iClientIndex) ? "true" : "false")); + } + + // trigger callback if state change + _ConnApiUpdateCallback(pConnApi, iClientIndex, eCbType, eStatus, (ConnApiConnStatusE)pConnInfo->eStatus); + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateRemoval + + \Description + Scan through client list and remove clients marked for removal. + + \Input *pConnApi - pointer to module state + + \Version 04/11/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiUpdateRemoval(ConnApiRefT *pConnApi) +{ + ConnApiClientT *pClient; + int32_t iClientIndex; + + for (iClientIndex = 0; iClientIndex < pConnApi->ClientList.iMaxClients; iClientIndex++) + { + // ref client + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + + // if client needs to be removed, remove them + if (pClient->uFlags & CONNAPI_CLIENTFLAG_REMOVE) + { + _ConnApiRemoveClient(pConnApi, pClient, iClientIndex); + } + } +} + + +/*F********************************************************************************/ +/*! + \Function _ConnApiRemoveClientSetup + + \Description + Set up a client for removal from the game. + + \Input *pConnApi - pointer to module state + \Input iClientIndex - index of client to remove (used if pClientName is NULL) + \Input uFlags - client removal flags + + \Notes + If this function is called inside of a ConnApi callback, the removal will + be deferred until the next time NetConnIdle() is called. Otherwise, the + removal will happen immediately. + + \Version 04/08/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiRemoveClientSetup(ConnApiRefT *pConnApi, int32_t iClientIndex, uint16_t uFlags) +{ + ConnApiClientT *pClient; + + // don't allow self removal + if (iClientIndex == pConnApi->iSelf) + { + NetPrintf(("connapi: [%p] can't remove self from game\n", pConnApi)); + return; + } + + // ref the client and mark them for removal + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + pClient->uFlags |= uFlags; + + // if we're not in a callback, do the removal immediately + if (pConnApi->bInCallback == FALSE) + { + _ConnApiUpdateRemoval(pConnApi); + } +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiUpdateConnections + + \Description + Update ConnApi connections. + + \Input *pConnApi - pointer to module state + + \Output + int32_t - number of connections that are not in the DISC state + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ConnApiUpdateConnections(ConnApiRefT *pConnApi) +{ + ConnApiClientT *pClient; + int32_t iActive, iClientIndex; + uint32_t bDemangling; + + // update game connections + for (iActive = 0, iClientIndex = 0; iClientIndex < pConnApi->ClientList.iMaxClients; iClientIndex++) + { + // ref connection + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + + // don't update if iClientIndex is us or unallocated + if (!pClient->bAllocated) + { + continue; + } + + if (pClient->uConnFlags & CONNAPI_CONNFLAG_GAMECONN) + { + // process game connection + iActive += _ConnApiUpdateGameClient(pConnApi, pClient, iClientIndex); + } + } + + // update voip connections + if (pConnApi->pVoipRef != NULL) + { + for (iClientIndex = 0; iClientIndex < pConnApi->ClientList.iMaxClients; iClientIndex++) + { + // ref connection + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + + // don't update if iClientIndex is us or unallocated + if (!pClient->bAllocated) + { + continue; + } + + if (pClient->uConnFlags & CONNAPI_CONNFLAG_VOIPCONN) + { + // process voip connection + iActive += _ConnApiUpdateVoipClient(pConnApi, pClient, iClientIndex); + } + } + } + + // update reporting + bDemangling = _ConnApiUpdateDemangleReport(pConnApi); + + // update game demangling + for (iClientIndex = 0; iClientIndex < pConnApi->ClientList.iMaxClients; iClientIndex++) + { + // ref connection + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + + // don't update if iClientIndex is us or unallocated + if ((iClientIndex == pConnApi->iSelf) || !pClient->bAllocated) + { + continue; + } + + // demangle game connection? +#if CONNAPI_XBOX_NETWORKING + // on xboxone, we don't serialize demangling of different clients, we want them occurring in parallel + if ((pClient->GameInfo.eStatus == CONNAPI_STATUS_MNGL) && (pClient->uConnFlags & CONNAPI_CONNFLAG_GAMECONN)) +#else + if ((pClient->GameInfo.eStatus == CONNAPI_STATUS_MNGL) && (pClient->uConnFlags & CONNAPI_CONNFLAG_GAMECONN) && (bDemangling == FALSE)) +#endif + + { + _ConnApiUpdateDemangle(pConnApi, pClient, iClientIndex, &pClient->GameInfo, 0); + } + bDemangling |= pClient->GameInfo.bDemangling; + } + + // update voip demangling + for (iClientIndex = 0; iClientIndex < pConnApi->ClientList.iMaxClients; iClientIndex++) + { + // ref connection + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + + // don't update if iClientIndex is us or unallocated + if ((iClientIndex == pConnApi->iSelf) || !pClient->bAllocated) + { + continue; + } + + // demangle voip connection? +#if CONNAPI_XBOX_NETWORKING + // on xboxone, we don't serialize demangling of different clients, we want them occurring in parallel + if ((pClient->VoipInfo.eStatus == CONNAPI_STATUS_MNGL)) +#else + if ((pClient->VoipInfo.eStatus == CONNAPI_STATUS_MNGL) && (bDemangling == FALSE)) +#endif + + { + _ConnApiUpdateDemangle(pConnApi, pClient, iClientIndex, &pClient->VoipInfo, 1); + } + } + + // update tunnel + if ((pConnApi->bTunnelEnabled != 0) && (pConnApi->pProtoTunnel != NULL)) + { + ProtoTunnelUpdate(pConnApi->pProtoTunnel); + } + + return(iActive); +} + +/*F********************************************************************************/ +/*! + \Function _ConnApiIdle + + \Description + NetConn idle function to update the ConnApi module. + + \Input *pData - pointer to module state + \Input uTick - current tick count + + \Notes + This function is installed as a NetConn Idle function. NetConnIdle() + must be regularly polled for this function to be called. + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ConnApiIdle(void *pData, uint32_t uTick) +{ + ConnApiRefT *pConnApi = (ConnApiRefT *)pData; + + if (pConnApi->bAutoUpdate == TRUE) + { + ConnApiUpdate(pConnApi); + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ConnApiCreate2 + + \Description + Create the module state. + + \Input iGamePort - game connection port + \Input iMaxClients - maximum number of clients allowed + \Input *pCallback - pointer to user callback + \Input *pUserData - pointer to user data + \Input *pConstruct - comm construct function + + \Output + ConnApiRefT * - pointer to module state, or NULL + + \Version 01/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +ConnApiRefT *ConnApiCreate2(int32_t iGamePort, int32_t iMaxClients, ConnApiCallbackT *pCallback, void *pUserData, CommAllConstructT *pConstruct) +{ + ConnApiRefT *pConnApi; + int32_t iMemGroup; + void *pMemGroupUserData; + int32_t iSize; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // calculate size of module state + iSize = sizeof(*pConnApi) + (sizeof(ConnApiClientT) * (iMaxClients - 1)); + + // allocate and init module state + if ((pConnApi = DirtyMemAlloc(iSize, CONNAPI_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("connapi: could not allocate module state... connapi initialization aborted!\n")); + return(NULL); + } + ds_memclr(pConnApi, iSize); + pConnApi->iMemGroup = iMemGroup; + pConnApi->pMemGroupUserData = pMemGroupUserData; + + if ((pConnApi->pVoipGroupRef = VoipGroupCreate(_ConnApi_iMaxVoipGroups)) == NULL) + { + // release module memory + DirtyMemFree(pConnApi, CONNAPI_MEMID, pConnApi->iMemGroup, pConnApi->pMemGroupUserData); + + NetPrintf(("connapi: [%p] no more voip groups available... connapi initialization aborted!\n", pConnApi)); + return(NULL); + } + + // register connection sharing callback with underlying voip group instance + VoipGroupSetConnSharingEventCallback(pConnApi->pVoipGroupRef, _ConnApiVoipGroupConnSharingCallback, pConnApi); + + // save info + pConnApi->uGamePort = (uint16_t)iGamePort; + pConnApi->uVoipPort = VOIP_PORT; + pConnApi->ClientList.iMaxClients = iMaxClients; + pConnApi->pCallback[0] = (pCallback != NULL) ? pCallback : _ConnApiDefaultCallback; + pConnApi->pUserData[0] = pUserData; + pConnApi->pCommConstruct = pConstruct; + + // set default values + pConnApi->uConnFlags = CONNAPI_CONNFLAG_GAMEVOIP; + pConnApi->iLinkBufSize = CONNAPI_LINKBUFDEFAULT; + pConnApi->iConnTimeout = CONNAPI_CONNTIMEOUT_DEFAULT; + pConnApi->iTimeout = CONNAPI_TIMEOUT_DEFAULT; + pConnApi->iConfigMnglTimeout = CONNAPI_DEMANGLER_TIMEOUT; + pConnApi->iConfigMnglTimeoutFailover = CONNAPI_DEMANGLER_WITH_FAILOVER_TIMEOUT; + pConnApi->iCurrentMnglTimeout = 0; + pConnApi->iTunnelPort = 3658; + pConnApi->bDemanglerEnabled = TRUE; + pConnApi->bAutoUpdate = TRUE; +#if !CONNAPI_XBOX_NETWORKING + pConnApi->bDoAdvertising = TRUE; +#endif + + pConnApi->uNetMask = 0xffffffff; + + pConnApi->iQosDuration = 0; // QoS is disabled by default + pConnApi->iQosInterval = 0; + pConnApi->iQosPacketSize = 0; + pConnApi->iGameHostIndex = -1; + pConnApi->iVoipHostIndex = -1; + + // set default demangler server and create demangler + ds_strnzcpy(pConnApi->strDemanglerServer, PROTOMANGLE_SERVER, sizeof(pConnApi->strDemanglerServer)); + + // add update function to netconn idle handler + NetConnIdleAdd(_ConnApiIdle, pConnApi); + + // return module state to caller + return(pConnApi); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiOnline + + \Description + This function should be called once the user has logged on and the input + parameters are available + + \Input *pConnApi - pointer to module state + \Input *pGameName - pointer to game resource string (eg cso/NCAA-2006/na) + \Input uSelfId - unique identifier for the local connapi client + \Input eGameTopology- type of game + \Input eVoipTopology- type of voip + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ConnApiOnline(ConnApiRefT *pConnApi, const char *pGameName, uint32_t uSelfId, ConnApiGameTopologyE eGameTopology, ConnApiVoipTopologyE eVoipTopology) +{ + char strAdvt[32]; + + NetPrintf(("connapi: [%p] ConnApiOnline() invoked with uSelfId=0x%08x and pGameName=%s\n", pConnApi, uSelfId, pGameName)); + + // save info + ds_strnzcpy(pConnApi->strGameName, pGameName, sizeof(pConnApi->strGameName)); + pConnApi->uSelfId = uSelfId; + + // if voip was disabled before online was called change the topology to disabled + if ((pConnApi->uConnFlags & CONNAPI_CONNFLAG_VOIPCONN) == 0) + { + NetPrintf(("connapi: [%p] requested voip topology ignored because voip globally disabled\n", pConnApi)); + eVoipTopology = CONNAPI_VOIPTOPOLOGY_DISABLED; + } + + // save the topology and set the connection flags accordingly + if ((pConnApi->eGameTopology = eGameTopology) == CONNAPI_GAMETOPOLOGY_DISABLED) + { + pConnApi->uConnFlags &= ~CONNAPI_CONNFLAG_GAMECONN; + } + if ((pConnApi->eVoipTopology = eVoipTopology) == CONNAPI_VOIPTOPOLOGY_DISABLED) + { + pConnApi->uConnFlags &= ~CONNAPI_CONNFLAG_VOIPCONN; + } + + // get VoIP ref + if ((pConnApi->eVoipTopology != CONNAPI_VOIPTOPOLOGY_DISABLED) && (pConnApi->pVoipRef == NULL)) + { + if ((pConnApi->pVoipRef = VoipGetRef()) == NULL) + { + NetPrintf(("connapi: [%p] critical error! ConnApiOnline() is invoked on a voip-enabled ConnApi before VoipStartup() was called externally.\n", pConnApi)); + return; + } + } + + // set memory grouping, this requires DirtyMemGroupLeave() to be called before return + DirtyMemGroupEnter(pConnApi->iMemGroup, pConnApi->pMemGroupUserData); + + // create util ref for subnet advertising + if (pConnApi->bDoAdvertising) + { + NetPrintf(("connapi: [%p] creating NetGameUtil ref used for advertising purposes\n", pConnApi)); + if (pConnApi->pGameUtilRef == NULL) + { + if ((pConnApi->pGameUtilRef = NetGameUtilCreate()) == NULL) + { + NetPrintf(("connapi: [%p] failed to create the NetGameUtil ref used for advertising purposes\n", pConnApi)); + DirtyMemGroupLeave(); + return; + } + } + else + { + NetPrintf(("connapi: [%p] can't create the NetGameUtil ref used for advertising purposes because there already exists one\n", pConnApi)); + DirtyMemGroupLeave(); + return; + } + } + else + { + NetPrintf(("connapi: [%p] skipped creation of the NetGameUtil ref used for advertising purposes\n", pConnApi)); + } + + // on non-XboxOne platforms handle the cases where we should disable the demangler + #if !CONNAPI_XBOX_NETWORKING + if ((pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_SERVERHOSTED) && (pConnApi->eVoipTopology != CONNAPI_VOIPTOPOLOGY_PEERWEB)) + { + NetPrintf(("connapi: [%p] internally disabling demangler for mesh involving server-based game/voip topologies\n", pConnApi)); + pConnApi->bDemanglerEnabled = FALSE; + } + /* for CC-assisted scenarios, we don't allow demangling for meshes with potentially + more than 2 players because connapi serializes the demangling attempts internally, and multiple + back-to-back failing demangling attempts can induce a long wait time that will result in + some clients not even having a chance to attempt CC-assisted path. */ + else if ((pConnApi->ClientList.iMaxClients > 2) && (pConnApi->iCcMode != CONNAPI_CCMODE_PEERONLY)) + { + NetPrintf(("connapi: [%p] internally disabling demangler for CC-assisted mesh with a max player count (%d) larger than 2\n", pConnApi, pConnApi->ClientList.iMaxClients)); + pConnApi->bDemanglerEnabled = FALSE; + } + #endif + + // create demangler + if ((pConnApi->bDemanglerEnabled) && (pConnApi->pProtoMangle == NULL)) + { + #if CONNAPI_XBOX_NETWORKING + NetPrintf(("connapi: [%p] creating demangler ref with max clients = %d\n", pConnApi, pConnApi->ClientList.iMaxClients)); + if ((pConnApi->pProtoMangle = ProtoMangleCreate(pConnApi->strDemanglerServer, pConnApi->ClientList.iMaxClients, pConnApi->strGameName, "")) == NULL) + #else + NetPrintf(("connapi: [%p] creating demangler ref with gamename=%s and server=%s\n", pConnApi, pConnApi->strGameName, pConnApi->strDemanglerServer)); + if ((pConnApi->pProtoMangle = ProtoMangleCreate(pConnApi->strDemanglerServer, PROTOMANGLE_PORT, pConnApi->strGameName, "")) == NULL) + #endif + { + NetPrintf(("connapi: [%p] unable to create ProtoMangle module\n", pConnApi)); + pConnApi->bDemanglerEnabled = FALSE; + } + else + { + // now that we have a valid ProtoMangle, immediately make sure that ConnApi-driven demangler timeout is passed down to it + ConnApiControl(pConnApi, 'dtim', pConnApi->iConfigMnglTimeout, 0, NULL); + ConnApiControl(pConnApi, 'dtif', pConnApi->iConfigMnglTimeoutFailover, 0, NULL); + } + } + + // create tunnel module + if ((pConnApi->bTunnelEnabled) && (pConnApi->pProtoTunnel == NULL)) + { + if ((pConnApi->pProtoTunnel = ProtoTunnelCreate(pConnApi->ClientList.iMaxClients-1, pConnApi->iTunnelPort)) == NULL) + { + // unable to create, so disable the tunnel + pConnApi->bTunnelEnabled = FALSE; + } + else + { + // we own the tunnel + pConnApi->bTunnelOwner = TRUE; + } + } + + // set voip/gamelink timeouts + ConnApiControl(pConnApi, 'time', pConnApi->iTimeout, 0, NULL); + + #if CONNAPI_XBOX_NETWORKING + // set external session name and scid (calls into ProtoMangle) + ConnApiControl(pConnApi, 'exsn', 0, 0, pConnApi->strExternalSessionName); + ConnApiControl(pConnApi, 'exst', 0, 0, pConnApi->strExternalSessionTemplateName); + ConnApiControl(pConnApi, 'scid', 0, 0, pConnApi->strScid); + #endif + + ds_snzprintf(strAdvt, sizeof(strAdvt), "%u", pConnApi->uSelfId); + + // start advertising + if (pConnApi->pGameUtilRef != NULL) + { + NetGameUtilAdvert(pConnApi->pGameUtilRef, pConnApi->strGameName, strAdvt, ""); + } + + // leave memory group + DirtyMemGroupLeave(); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiDestroy + + \Description + Destroy the module state. + + \Input *pConnApi - pointer to module state + + \Version 01/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ConnApiDestroy(ConnApiRefT *pConnApi) +{ + // disconnect + ConnApiDisconnect(pConnApi); + + // remove idle handler + NetConnIdleDel(_ConnApiIdle, pConnApi); + + VoipGroupDestroy(pConnApi->pVoipGroupRef); + + // destroy advertising gameutil ref + if (pConnApi->pGameUtilRef != NULL) + { + NetGameUtilDestroy(pConnApi->pGameUtilRef); + } + + // destroy tunnel, if present and we are the owner + if ((pConnApi->pProtoTunnel != NULL) && (pConnApi->bTunnelOwner == TRUE)) + { + ProtoTunnelDestroy(pConnApi->pProtoTunnel); + } + + // destroy demangler + if (pConnApi->pProtoMangle != NULL) + { + ProtoMangleDestroy(pConnApi->pProtoMangle); + } + + // release module memory + DirtyMemFree(pConnApi, CONNAPI_MEMID, pConnApi->iMemGroup, pConnApi->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiConnect + + \Description + Connect to a game. + + \Input *pConnApi - pointer to module state + \Input *pClientList - list of clients in game session + \Input iClientListSize - number of clients in list + \Input iGameHostIndex - index in the client list who will be serving as game host + \Input iVoipHostIndex - index in the clinet list who will be serving as voip host + \Input iSessId - unique session identifier + + \Notes + ConnApi supports invalid entries in the client list. Invalid clients are detected with a DirtyAddr that is zeroed out + + \Version 09/29/2009 (cvienneau) +*/ +/********************************************************************************F*/ +void ConnApiConnect(ConnApiRefT *pConnApi, ConnApiClientInfoT *pClientList, int32_t iClientListSize, int32_t iGameHostIndex, int32_t iVoipHostIndex, int32_t iSessId) +{ + NetPrintf(("connapi: [%p] ConnApiConnect() called with listSize=%d, gamehost=%d, voiphost=%d, sessionId=%d\n", + pConnApi, iClientListSize, iGameHostIndex, iVoipHostIndex, iSessId)); + + // make sure we're idle + if (pConnApi->eState != ST_IDLE) + { + NetPrintf(("connapi: [%p] can't host or connect to a game when not in idle state\n", pConnApi)); + return; + } + + // save session identifier + pConnApi->iSessId = iSessId; + + // virtualize ports if tunneling + if (pConnApi->bTunnelEnabled == TRUE) + { + NetConnControl('vadd', pConnApi->uGamePort, 0, NULL, NULL); + + if (pConnApi->eVoipTopology != CONNAPI_VOIPTOPOLOGY_DISABLED) + { + NetConnControl('vadd', pConnApi->uVoipPort, 0, NULL, NULL); + } + } + + // initialize the game / voip host index only in topologies they apply + if ((pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_PEERHOSTED) || (pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_SERVERHOSTED)) + { + pConnApi->iGameHostIndex = iGameHostIndex; + } + if (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) + { + pConnApi->iVoipHostIndex = iVoipHostIndex; + } + + // init client list + _ConnApiInitClientList(pConnApi, pClientList, iClientListSize); + + // let the voipgroup know if we are hosting or not + VoipGroupControl(pConnApi->pVoipGroupRef, 'serv', (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED), 0, NULL); + + #if CONNAPI_XBOX_NETWORKING + if (pConnApi->bDemanglerEnabled == TRUE) + { + // let protomanglexboxone know what our index is + ProtoMangleControl(pConnApi->pProtoMangle, 'self', pConnApi->iSelf, 0, NULL); + } + #endif + + // check for advertisement if necessary + if (pConnApi->pGameUtilRef != NULL) + { + _ConnApiCheckAdvert(pConnApi); + } + + pConnApi->eState = ST_INGAME; +} + +/*F********************************************************************************/ +/*! + \Function ConnApiMigrateGameHost + + \Description + Reopen all connections, using the host specified. + This is for host migration to a different host in non-peerweb, needing new + connections for everyone. + + \Input *pConnApi - pointer to module state + \Input iNewGameHostIndex - index of the new game host + + \Version 09/21/2007 (jrainy) +*/ +/********************************************************************************F*/ +void ConnApiMigrateGameHost(ConnApiRefT *pConnApi, int32_t iNewGameHostIndex) +{ + ConnApiClientT *pClient; + int32_t iClientIndex; + ConnApiConnStatusE eStatus; + + pConnApi->iGameHostIndex = iNewGameHostIndex; + pConnApi->eState = ST_INGAME; + + for (iClientIndex = 0; iClientIndex < pConnApi->ClientList.iMaxClients; iClientIndex++) + { + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + if (pClient->bAllocated && (pClient->GameInfo.eStatus != CONNAPI_STATUS_ACTV)) + { + eStatus = pClient->GameInfo.eStatus; + + #if CONNAPI_XBOX_NETWORKING + _ConnApiInitClientConnectionState(pConnApi, pClient, iClientIndex, CONNAPI_CONNFLAG_GAMECONN); + #else + pClient->GameInfo.eStatus = CONNAPI_STATUS_INIT; + #endif + + _ConnApiUpdateCallback(pConnApi, iClientIndex, CONNAPI_CBTYPE_GAMEEVENT, eStatus, (ConnApiConnStatusE)pClient->GameInfo.eStatus); + } + } +} + +/*F********************************************************************************/ +/*! + \Function ConnApiAddClient + + \Description + Add a new client to a pre-existing game at the specified index. + + \Input *pConnApi - pointer to module state + \Input *pClientInfo - info on joining user + \Input iClientIndex - index to add client to + + \Output + 0 if successful, error code otherwise. + + \Notes + This function should be called by all current members of a game while + ConnApiConnect() is called by the joining client. + + \Version 06/16/2008 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ConnApiAddClient(ConnApiRefT *pConnApi, ConnApiClientInfoT *pClientInfo, int32_t iClientIndex) +{ + ConnApiClientT *pClient; + + // make sure we're not idle + if (pConnApi->eState == ST_IDLE) + { + NetPrintf(("connapi: [%p] can't add a connection to a game in idle state\n", pConnApi)); + return(CONNAPI_ERROR_INVALID_STATE); + } + + // make sure there is room + if (pConnApi->ClientList.iNumClients == pConnApi->ClientList.iMaxClients) + { + NetPrintf(("connapi: [%p] can't add a connection to the game because it is full\n", pConnApi)); + return(CONNAPI_ERROR_CLIENTLIST_FULL); + } + + // make sure the selected slot is valid + if ((iClientIndex < 0) || (iClientIndex >= pConnApi->ClientList.iMaxClients)) + { + NetPrintf(("connapi: [%p] can't add a connection to the game in slot %d because valid slot range 0-%d\n", pConnApi, iClientIndex, pConnApi->ClientList.iMaxClients-1)); + return(CONNAPI_ERROR_SLOT_OUT_OF_RANGE); + } + + // get pointer to new client structure to fill in, and increment client count + pClient = &pConnApi->ClientList.Clients[iClientIndex]; + + // check slot and make sure it is uninitialized + if (pClient->bAllocated == TRUE) + { + NetPrintf(("connapi: [%p] slot %d already allocated; cannot add a new client in this slot\n", pConnApi, iClientIndex)); + return(CONNAPI_ERROR_SLOT_USED); + } + + // add client to list + _ConnApiInitClient(pConnApi, pClient, pClientInfo, iClientIndex); + + // display client info + #if DIRTYCODE_LOGGING + NetPrintf(("connapi: [%p] adding client to clientlist\n", pConnApi)); + _ConnApiDisplayClientInfo(&pConnApi->ClientList.Clients[iClientIndex], iClientIndex); + #endif + + // increment client count + pConnApi->ClientList.iNumClients += 1; + + // check for advertisement if necessary + if (pConnApi->pGameUtilRef != NULL) + { + _ConnApiCheckAdvert(pConnApi); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiFindClient + + \Description + Returns the ConnApiClientT of a given client, if found by id. + + \Input *pConnApi - pointer to module state + \Input *pClientInfo - info on searched user + \Input *pOutClient - used to return the ClientT structure of the client + + \Output + uint8_t - TRUE if the client is found, FALSE otherwise + + \Version 06/05/2008 (jbrookes) +*/ +/********************************************************************************F*/ +uint8_t ConnApiFindClient(ConnApiRefT *pConnApi, ConnApiClientInfoT *pClientInfo, ConnApiClientT *pOutClient) +{ + int32_t iClient; + + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + if (pConnApi->ClientList.Clients[iClient].ClientInfo.uId == pClientInfo->uId) + { + ds_memcpy_s(pOutClient, sizeof(*pOutClient), &pConnApi->ClientList.Clients[iClient], sizeof(pConnApi->ClientList.Clients[iClient])); + return(TRUE); + } + } + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiRemoveClient + + \Description + Remove a current client from a game. + + \Input *pConnApi - pointer to module state + \Input iClientIndex - index of client to remove (used if pClientName is NULL) + + \Notes + If this function is called inside of a ConnApi callback, the removal will + be deferred until the next time NetConnIdle() is called. Otherwise, the + removal will happen immediately. + + \Version 06/14/2008 (jbrookes) +*/ +/********************************************************************************F*/ +void ConnApiRemoveClient(ConnApiRefT *pConnApi, int32_t iClientIndex) +{ + // make sure the select slot is valid + if ((iClientIndex < 0) || (iClientIndex >= pConnApi->ClientList.iMaxClients)) + { + NetPrintf(("connapi: [%p] can't remove a connection from the game in slot %d because valid slot range is 0-%d\n", pConnApi, iClientIndex, pConnApi->ClientList.iMaxClients-1)); + return; + } + + _ConnApiRemoveClientSetup(pConnApi, iClientIndex, CONNAPI_CLIENTFLAG_REMOVE); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiDisconnect + + \Description + Stop game, disconnect from clients, and reset client list. + + \Input *pConnApi - pointer to module state + + \Notes + Any NetGameDistRefs created by the application that references a NetGameUtil/ + NeGameLink combination created by ConnApi must destroy the DistRef(s) before + calling this function. + + \Version 01/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ConnApiDisconnect(ConnApiRefT *pConnApi) +{ + ConnApiClientT *pClient; + int32_t iClient; + + NetPrintf(("connapi: [%p] disconnecting\n", pConnApi)); + + // make sure we're not idle + if (pConnApi->eState == ST_IDLE) + { + NetPrintf(("connapi: [%p] can't disconnect when in idle state\n", pConnApi)); + return; + } + + // walk client list + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + // ref client + pClient = &pConnApi->ClientList.Clients[iClient]; + + // if it's not us and was allocated, disconnect from them + if ((iClient != pConnApi->iSelf) && pClient->bAllocated) + { + _ConnApiDisconnectClient(pConnApi, &pConnApi->ClientList.Clients[iClient], iClient, "disconnect"); + } + } + + // reset client list + pConnApi->ClientList.iNumClients = 0; + ds_memclr(&pConnApi->ClientList.Clients, pConnApi->ClientList.iMaxClients * sizeof(ConnApiClientT)); + + // devirtualize ports if tunneling is enabled + if (pConnApi->bTunnelEnabled == TRUE) + { + NetConnControl('vdel', pConnApi->uGamePort, 0, NULL, NULL); + + if (pConnApi->eVoipTopology != CONNAPI_VOIPTOPOLOGY_DISABLED) + { + NetConnControl('vdel', pConnApi->uVoipPort, 0, NULL, NULL); + } + } + + // clear socket refs + pConnApi->uTunlSockRef = 0; + pConnApi->uGameSockRef = 0; + pConnApi->uVoipSockRef = 0; + + // go to idle state + pConnApi->eState = ST_IDLE; +} + +/*F********************************************************************************/ +/*! + \Function ConnApiGetClientList + + \Description + Get a list of current connections. + + \Input *pConnApi - pointer to module state + + \Output + ConnApiClientListT * - pointer to client list + + \Version 01/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +const ConnApiClientListT *ConnApiGetClientList(ConnApiRefT *pConnApi) +{ + return(&pConnApi->ClientList); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiStatus + + \Description + Get status information. + + \Input *pConnApi - pointer to module state + \Input iSelect - status selector + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'cbfp' - return current callback function pointer in output buffer + 'cbup' - return current callback data pointer in output buffer + 'ctim' - returns the connection timeout + 'dtim' - get effective demangler timeout (in milliseconds) + 'ghst' - return the the host index ConnApiClientT (via pBuf) for the game host in peer hosted and server hosted games + 'gprt' - return game port + 'gsrv' - return whether the game is server hosted (ConnApiClientT returned via pBuf) + 'ingm' - currently 'in game' (connecting to or connected to one or more peers) + 'lbuf' - returns GameLink buffer allocation size + 'lclt' - returns one past the index of the last allocated player + 'lcon' - returns the number of consoles (excluding the game server) allocated in the client list + 'minp' - returns GameLink input buffer queue length (zero=default) + 'mngl' - returns whether demangler is enabled or not + 'mout' - returns GameLink output buffer queue length (zero=default) + 'mplr' - *deprecated - replaced by 'lclt'* returns one past the index of the last allocated player + 'mwid' - returns GameLink max packet size (zero=default) + 'nmsk' - returns current netmask + 'peer' - returns game conn peer-web enable/disable status + 'self' - returns index of local user in client list + 'sock' - copy socket ref to pBuf + 'sess' - copies session information into output buffer + 'time' - returns the timeout + 'tprt' - returns port tunnel has been bound to (if available) + 'tref' - returns the prototunnel ref used + 'tunl' - returns whether tunnel is enabled or not + 'type' - returns current connection type (CONNAPI_CONNFLAG_*) + 'vhst' - return the host index and ConnApiClientT (via pBuf) for the voip host in server hosted voip + 'vprt' - return voip port + \endverbatim + + \Version 01/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ConnApiStatus(ConnApiRefT *pConnApi, int32_t iSelect, void *pBuf, int32_t iBufSize) +{ + return(ConnApiStatus2(pConnApi, iSelect, NULL, pBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiStatus2 + + \Description + Get status information. + + \Input *pConnApi - pointer to module state + \Input iSelect - status selector + \Input *pData - input data + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'cadr' - stands for Connection Address, ip address used at the platform socket level to reach this client (regardless of tunneling being used or not) + 'cbfp' - return current callback function pointer in output buffer + 'cbup' - return current callback data pointer in output buffer + 'cprt' - stands for Connection Port, UDP port used at the platform socket level to reach this client + 'ctim' - returns the connection timeout + 'dtim' - get effective demangler timeout (in milliseconds) + 'ghst' - return the the host index ConnApiClientT (via pBuf) for the game host in peer hosted and server hosted games + 'gprt' - return game port + 'gsrv' - return whether the game is server hosted (ConnApiClientT returned via pBuf) + 'host' - returns whether hosting or not, plus copies host name to buffer + 'ingm' - currently 'in game' (connecting to or connected to one or more peers) + 'lbuf' - returns GameLink buffer allocation size + 'lclt' - returns one past the index of the last allocated player + 'lcon' - returns the number of consoles (excluding the game server) allocated in the client list + 'minp' - returns GameLink input buffer queue length (zero=default) + 'mngl' - returns whether demangler is enabled or not + 'mout' - returns GameLink output buffer queue length (zero=default) + 'mplr' - *deprecated - replaced by 'lclt'* returns one past the index of the last allocated player + 'mvtm' - return true if multiple virtual machine mode is active + 'mwid' - returns GameLink max packet size (zero=default) + 'nmsk' - returns current netmask + 'peer' - returns game conn peer-web enable/disable status + 'self' - returns index of local user in client list + 'sock' - copy socket ref to pBuf + 'sess' - copies session information into output buffer + 'time' - returns the timeout + 'tprt' - returns port tunnel has been bound to (if available) + 'tref' - returns the prototunnel ref used + 'tunl' - returns whether tunnel is enabled or not + 'tunr' - returns receive protunnel stats as a ProtoTunnelStatT in pBuf for a given client pData + 'tuns' - returns send prototunnel stats as a ProtoTunnelStatT in pBuf for a given client pData + 'type' - returns current connection type (CONNAPI_CONNFLAG_*) + 'vgrp' - returns voipgroup pointer in pBuf + 'vhst' - return the host index and ConnApiClientT (via pBuf) for the voip host in server hosted voip + 'vprt' - return voip port + \endverbatim + + \Version 01/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ConnApiStatus2(ConnApiRefT *pConnApi, int32_t iSelect, void *pData, void *pBuf, int32_t iBufSize) +{ + if ((iSelect == 'cadr') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(uint32_t))) + { + uint8_t bLocalAddr; + ConnApiClientT *pClientUsed; + ConnApiClientT *pClient = (ConnApiClientT *)pData; + struct sockaddr addr; + + *(uint32_t*)pBuf = _ConnApiGetConnectAddr(pConnApi, pClient, &bLocalAddr, CONNAPI_CONNFLAG_GAMECONN, &pClientUsed); + + // if we are using the tunnel return the tunnel address + if (pConnApi->pProtoTunnel != NULL) + { + ProtoTunnelStatus(pConnApi->pProtoTunnel, 'vtop', pClient->iTunnelId, &addr, sizeof(addr)); + *(uint32_t*)pBuf = SockaddrInGetAddr(&addr); + } + + if (pClientUsed->GameInfo.eStatus != CONNAPI_STATUS_ACTV) + { + return(-1); + } + + return(0); + } + if ((iSelect == 'cbfp') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(pConnApi->pCallback[0]))) + { + ds_memcpy(pBuf, &(pConnApi->pCallback[0]), sizeof(pConnApi->pCallback[0])); + return(0); + } + if ((iSelect == 'cbup') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(pConnApi->pUserData[0]))) + { + ds_memcpy(pBuf, &(pConnApi->pUserData[0]), sizeof(pConnApi->pUserData[0])); + return(0); + } + if ((iSelect == 'cprt') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(uint16_t))) + { + uint8_t bLocalAddr; + ConnApiClientT *pClientUsed; + ConnApiClientT *pClient = (ConnApiClientT *)pData; + + _ConnApiGetConnectAddr(pConnApi, pClient, &bLocalAddr, CONNAPI_CONNFLAG_GAMECONN, &pClientUsed); + + if (pClientUsed->GameInfo.eStatus != CONNAPI_STATUS_ACTV) + { + return(-1); + } + + if (pClientUsed->iTunnelId > 0) + { + ProtoTunnelStatus(pConnApi->pProtoTunnel, 'rprt', pClientUsed->iTunnelId, pBuf, iBufSize); + } + else + { + *(uint16_t*)pBuf = pClientUsed->GameInfo.uMnglPort; + } + return(0); + } + if (iSelect == 'ctim') + { + return(pConnApi->iConnTimeout); + } + if (iSelect == 'dtim') + { + return(pConnApi->iCurrentMnglTimeout); + } + if ((iSelect == 'ghst') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(ConnApiClientT))) + { + if ((pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_PEERHOSTED) || (pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_SERVERHOSTED)) + { + ds_memcpy(pBuf, &pConnApi->ClientList.Clients[pConnApi->iGameHostIndex], sizeof(ConnApiClientT)); + return(pConnApi->iGameHostIndex); + } + return(-1); + } + if (iSelect == 'gprt') + { + return(pConnApi->uGamePort); + } + if (iSelect == 'gsrv') + { + uint8_t bHostIsGameServer; + if ((bHostIsGameServer = (pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_SERVERHOSTED)) == TRUE) + { + if ((pBuf != NULL) && (iBufSize >= (signed)sizeof(ConnApiClientT))) + { + ds_memcpy(pBuf, &pConnApi->ClientList.Clients[pConnApi->iGameHostIndex], sizeof(ConnApiClientT)); + } + } + return(bHostIsGameServer); + } + if (iSelect == 'ingm') + { + return(pConnApi->eState != ST_IDLE); + } + if (iSelect == 'lbuf') + { + return(pConnApi->iLinkBufSize); + } + if (iSelect == 'lcon') + { + int32_t iNumberPlayers = ConnApiStatus2(pConnApi, 'lclt', pData, pBuf, iBufSize); + + // if the game is server hosted we do not want to include the server + if (ConnApiStatus(pConnApi, 'gsrv', NULL, 0)) + { + --iNumberPlayers; + } + + return(iNumberPlayers); + } + if (iSelect == 'minp') + { + return(pConnApi->iGameMinp); + } + if (iSelect == 'mngl') + { + return(pConnApi->bDemanglerEnabled); + } + if (iSelect == 'mout') + { + return(pConnApi->iGameMout); + } + if ((iSelect == 'mplr') || (iSelect == 'lclt')) + { + int32_t iClient; + int32_t iNumberPlayers = 0; + for (iClient = 0; iClient < pConnApi->ClientList.iMaxClients; iClient++) + { + if (pConnApi->ClientList.Clients[iClient].bAllocated) + { + iNumberPlayers = iClient + 1; + } + } + return(iNumberPlayers); + } + if (iSelect == 'mwid') + { + return(pConnApi->iGameMwid); + } + if (iSelect == 'nmsk') + { + return(pConnApi->uNetMask); + } + if (iSelect == 'peer') + { + return(pConnApi->eGameTopology == CONNAPI_GAMETOPOLOGY_PEERWEB); + } + if (iSelect == 'self') + { + return(pConnApi->iSelf); + } + if (iSelect == 'sess') + { + ds_strnzcpy((char *)pBuf, pConnApi->strSession, iBufSize); + return(0); + } + if (iSelect == 'sock') + { + if (iBufSize >= (signed)sizeof(intptr_t)) + { + if (pConnApi->bTunnelEnabled) + { + ProtoTunnelStatus(pConnApi->pProtoTunnel, 'sock', 0, pBuf, iBufSize); + } + else if ((pConnApi->iGameHostIndex >= 0) && (pConnApi->ClientList.Clients[pConnApi->iGameHostIndex].pGameUtilRef != NULL)) + { + NetGameUtilStatus(pConnApi->ClientList.Clients[pConnApi->iGameHostIndex].pGameUtilRef, 'sock', pBuf, iBufSize); + } + else + { + NetPrintf(("connapi: [%p] ConnApiStatus('sock') failed because game socket not yet available (tunnel enabled = %s)\n", pConnApi, + pConnApi->bTunnelEnabled?"true":"false")); + return(-2); + } + return(0); + } + else + { + NetPrintf(("connapi: [%p] ConnApiStatus('sock') failed because size (%d) of user-provided buffer is too small (required: %d)\n", pConnApi, + iBufSize, sizeof(intptr_t))); + return(-1); + } + } + if (iSelect == 'time') + { + return(pConnApi->iTimeout); + } + if ((iSelect == 'tprt') && (pConnApi->pProtoTunnel != NULL)) + { + return(ProtoTunnelStatus(pConnApi->pProtoTunnel, 'lprt', 0, NULL, 0)); + } + if (iSelect == 'tref' && (pConnApi->pProtoTunnel != NULL)) + { + if (iBufSize >= (signed)sizeof(ProtoTunnelRefT*)) + { + ds_memcpy(pBuf, &pConnApi->pProtoTunnel, sizeof(ProtoTunnelRefT*)); + return(0); + } + else + { + NetPrintf(("connapi: [%p] ConnApiStatus('tref') failed because size (%d) of user-provided buffer is too small (required: %d)\n", pConnApi, + iBufSize, sizeof(ProtoTunnelRefT*))); + return(-1); + } + } + if (iSelect == 'tunl') + { + return(pConnApi->bTunnelEnabled); + } + if (iSelect == 'tunr') + { + if ((pData != NULL) && (pBuf != NULL) && (iBufSize == sizeof(ProtoTunnelStatT))) + { + ConnApiClientT *pClient = (ConnApiClientT *)pData; + ProtoTunnelStatus(pConnApi->pProtoTunnel, 'rcvs', pClient->iTunnelId, pBuf, iBufSize); + return(0); + } + else + { + NetPrintf(("connapi: [%p] ConnApiStatus('tunr') failed due to invalid arguments\n", pConnApi)); + return(-1); + } + } + if (iSelect == 'tuns') + { + if ((pData != NULL) && (pBuf != NULL) && (iBufSize == sizeof(ProtoTunnelStatT))) + { + ConnApiClientT *pClient = (ConnApiClientT *)pData; + ProtoTunnelStatus(pConnApi->pProtoTunnel, 'snds', pClient->iTunnelId, pBuf, iBufSize); + return(0); + } + else + { + NetPrintf(("connapi: [%p] ConnApiStatus('tuns') failed due to invalid arguments\n", pConnApi)); + return(-1); + } + } + if (iSelect == 'type') + { + return(pConnApi->uConnFlags); + } + if (iSelect == 'ulmt') + { + return(pConnApi->iGameUnackLimit); + } + if (iSelect == 'vgrp') + { + if (iBufSize >= (int32_t)sizeof(pConnApi->pVoipGroupRef)) + { + ds_memcpy(pBuf, &pConnApi->pVoipGroupRef, sizeof(pConnApi->pVoipGroupRef)); + return(0); + } + return(-1); + } + if ((iSelect == 'vhst') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(ConnApiClientT))) + { + if (pConnApi->eVoipTopology == CONNAPI_VOIPTOPOLOGY_SERVERHOSTED) + { + ds_memcpy(pBuf, &pConnApi->ClientList.Clients[pConnApi->iVoipHostIndex], sizeof(ConnApiClientT)); + return(pConnApi->iVoipHostIndex); + } + return(-1); + } + if (iSelect == 'vprt') + { + return(pConnApi->uVoipPort); + } + // unhandled + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiControl + + \Description + Control behavior of module. + + \Input *pConnApi - pointer to module state + \Input iControl - status selector + \Input iValue - control value + \Input iValue2 - control value + \Input *pValue - control value + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'adve' - set to enable ProtoAdvt advertising + 'auto' - set auto-update enable/disable - iValue=TRUE or FALSE (default TRUE) + 'cbfp' - set callback function pointer - pValue=pCallback + 'cbup' - set callback user data pointer - pValue=pUserData + 'ccmd' - set the CC mode (CONNAPI_CCMODE_*) + 'ctim' - set connection timeout - iValue=timeout (minimum & default 10000 ms) + 'dist' - set dist ref - iValue=index of client or 'gsrv' for host user, pValue=dist ref + 'dsrv' - set demangler server - pValue=pointer to demangler server name (default demangler.ea.com) + 'dtif' - set demangler timeout for scenarios involving or CC assistance - iValue=timeout in milliseconds + 'dtim' - set demangler timeout - iValue=timeout in milliseconds + 'exsn' - set the external session name for the MultiplayerSessionReference (xbox one only) + 'exst' - set the external session template name for the MultiplayerSessionReference (xbox one only) + 'estv' - enable flag to establish voip for a client after it was delayed, iValue=client index + 'gprt' - set game port to use - iValue=port + 'lbuf' - set game link buffer size - iValue=size (default 1024) + 'lqos' - set QoS packet size used when creating NetGameLinks. iValue=packet size in bytes + 'maxg' - set maximum number of voipgroups we support + 'meta' - enable/disable commudp metadata + 'minp' - set GameLink input buffer queue length - iValue=length (default 32) + 'mngl' - set demangler enable/disable - iValue=TRUE/FALSE (default TRUE) + 'mout' - set GameLink output buffer queue length - iValue=length (default 32) + 'mvtm' - set multiple virtual machine mode enable/disable - iValue=TRUE/FALSE (default FALSE) + 'mwid' - set GameLink max packet size - iValue=size (default NETGAME_DATAPKT_DEFSIZE, max NETGAME_DATAPKT_MAXSIZE) + 'nmsk' - set netmask used for external address comparisons - iValue=mask (default 0xffffffff) + 'rcbk' - set enable of disc callback on removal - iValue=TRUE/FALSE (default FALSE) + 'scid' - set the service configuration id for the MultiplayerSessionReference (xbox one only) + 'sqos' - set QoS settings used when creating NetGameLinks. iValue=QoS duration (0 disables QoS), iValue2=QoS packet interval + 'stun' - set prototunnel ref + 'tctl' - set prototunnel control data + 'time' - set timeout - iValue=timeout in ms (default 15 seconds) + 'tgam' - enable the override of game tunnel flags - iValue=falgs, iValue2=boolean(overrides or not) + 'tunl' - set tunnel parms: + iValue = TRUE/FALSE to enable/disable or negative to ignore + iValue2 = tunnel port, or negative to ignore + 'type' - set connection type - iValue = CONNAPI_CONNFLAG_* + 'voig' - pass-through to VoipGroupControl() - 'getr' VoipGroupControl selector has been deprecated please switch to ConnApiStatus('vgrp') instead + 'voip' - pass-through to VoipControl() - iValue=VoIP iControl, iValue2= VoIP iValue + 'vprt' - set voip port to use - iValue=port + 'vset' - set voip enable/disable (default=TRUE; call before ConnApiOnline()) + '!res' - force secure address resolution to fail, to simulate p2p connection failure. + iValue = TRUE/FALSE to enable/disable + iValue2 = index of user to force fail or -1 for all users or local index for all users + \endverbatim + + \Version 01/04/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ConnApiControl(ConnApiRefT *pConnApi, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'adve') + { + #if CONNAPI_XBOX_NETWORKING + NetPrintf(("connapi: ProtoAdvt advertising cannot be enabled on Xbox One\n")); + #else + NetPrintf(("connapi: [%p] ProtoAdvt advertising %s\n", pConnApi, (iValue?"enabled":"disabled"))); + pConnApi->bDoAdvertising = iValue; + // if disabling advertising and we already have an advertising ref, kill it + if ((pConnApi->bDoAdvertising == FALSE) && (pConnApi->pGameUtilRef != NULL)) + { + NetGameUtilDestroy(pConnApi->pGameUtilRef); + pConnApi->pGameUtilRef = NULL; + } + #endif + return(0); + } + if (iControl == 'auto') + { + pConnApi->bAutoUpdate = iValue; + return(0); + } + if (iControl == 'ccmd') + { + #if DIRTYCODE_LOGGING + static const char *_ConnApiCcModeNames[] = + { + "CONNAPI_CCMODE_PEERONLY", + "CONNAPI_CCMODE_HOSTEDONLY", + "CONNAPI_CCMODE_HOSTEDFALLBACK" + }; + #endif + + switch (iValue) + { + case (CONNAPI_CCMODE_PEERONLY): + VoipGroupControl(pConnApi->pVoipGroupRef, 'ccmd', VOIPGROUP_CCMODE_PEERONLY, 0, NULL); + break; + case (CONNAPI_CCMODE_HOSTEDONLY): + VoipGroupControl(pConnApi->pVoipGroupRef, 'ccmd', VOIPGROUP_CCMODE_HOSTEDONLY, 0, NULL); + break; + case (CONNAPI_CCMODE_HOSTEDFALLBACK): + VoipGroupControl(pConnApi->pVoipGroupRef, 'ccmd', VOIPGROUP_CCMODE_HOSTEDFALLBACK, 0, NULL); + break; + default: + NetPrintf(("connapi: [%p] unsupported CC mode %d\n", pConnApi, iValue)); + return(-1); + } + NetPrintf(("connapi: [%p] CC mode = %d (%s)\n", pConnApi, iValue, _ConnApiCcModeNames[iValue])); + pConnApi->iCcMode = iValue; + return(0); + } + if (iControl == 'cbfp') + { + // set callback function pointer + pConnApi->pCallback[0] = ((pValue != NULL) ? (ConnApiCallbackT *)pValue : _ConnApiDefaultCallback); + return(0); + } + if (iControl == 'cbup') + { + // set callback user data pointer + pConnApi->pUserData[0] = pValue; + return(0); + } + if ((iControl == 'ctim') && (iValue >= CONNAPI_CONNTIMEOUT_DEFAULT)) + { + NetPrintf(("connapi: [%p] setting connection timeout to %d\n", pConnApi, iValue)); + pConnApi->iConnTimeout = iValue; + return(0); + } + if (iControl == 'dist') + { + // set dist ref for specified client + + // special value to signify the host + if (iValue == 'gsrv') + { + iValue = pConnApi->iGameHostIndex; + } + + if ((iValue >= 0) && (iValue < pConnApi->ClientList.iMaxClients)) + { + pConnApi->ClientList.Clients[iValue].pGameDistRef = (NetGameDistRefT *)pValue; + return(0); + } + } + if (iControl == 'dsrv') + { + // set demangler server + ds_strnzcpy(pConnApi->strDemanglerServer, (const char *)pValue, sizeof(pConnApi->strDemanglerServer)); + return(0); + } + if ((iControl == 'dtim') || (iControl == 'dtif')) + { + // set demangler timeout + int32_t iNewMnglTimeout; + #if DIRTYCODE_LOGGING + uint8_t bIsFailoverPossible = FALSE; + #endif + + // special case to allow using a different timeout value for connapi involving CC assistance + if (iControl == 'dtif') + { + NetPrintf(("connapi: [%p] changing demangler_with_failover timeout config (%d ms --> %d ms)\n", pConnApi, pConnApi->iConfigMnglTimeoutFailover, iValue)); + pConnApi->iConfigMnglTimeoutFailover = iValue; + } + else + { + NetPrintf(("connapi: [%p] changing demangler timeout config (%d ms --> %d ms)\n", pConnApi, pConnApi->iConfigMnglTimeout, iValue)); + pConnApi->iConfigMnglTimeout = iValue; + } + + // check if the special timeout value for cc scenarios is applicable + if (pConnApi->iCcMode != CONNAPI_CCMODE_PEERONLY) + { + #if DIRTYCODE_LOGGING + bIsFailoverPossible = TRUE; + #endif + iNewMnglTimeout = pConnApi->iConfigMnglTimeoutFailover; + } + else + { + iNewMnglTimeout = pConnApi->iConfigMnglTimeout; + } + + // pass new effective demangler timeout value down to ProtoMangle + if ((pConnApi->pProtoMangle != NULL) && (iNewMnglTimeout != pConnApi->iCurrentMnglTimeout)) + { + NetPrintf(("connapi: [%p] applying new demangler timeout (%d ms --> %d ms) for a demangling scenario %s\n", + pConnApi, pConnApi->iCurrentMnglTimeout, iNewMnglTimeout, (bIsFailoverPossible?"with":"without"))); + pConnApi->iCurrentMnglTimeout = iNewMnglTimeout; + ProtoMangleControl(pConnApi->pProtoMangle, 'time', pConnApi->iCurrentMnglTimeout, 0, NULL); + } + + return(0); + } + #if CONNAPI_XBOX_NETWORKING + if (iControl == 'exsn') + { + if ((pValue == NULL) || (*(char*)pValue == '\0')) + { + NetPrintf(("connapi: [%p] 'exsn', invalid external session name\n", pConnApi)); + } + else + { + if (pConnApi->strExternalSessionName != pValue) + { + ds_strnzcpy(pConnApi->strExternalSessionName, (char*)pValue, sizeof(pConnApi->strExternalSessionName)); + NetPrintf(("connapi: [%p] 'exsn', external session name saved as (%s)\n", pConnApi, pConnApi->strExternalSessionName)); + } + if (pConnApi->pProtoMangle != NULL) + { + return(ProtoMangleControl(pConnApi->pProtoMangle, 'exsn', 0, 0, pConnApi->strExternalSessionName)); + } + return(0); + } + } + if (iControl == 'exst') + { + if ((pValue == NULL) || (*(char*)pValue == '\0')) + { + NetPrintf(("connapi: [%p] 'exst', invalid external session template name\n", pConnApi)); + } + else + { + if (pConnApi->strExternalSessionTemplateName != pValue) + { + ds_strnzcpy(pConnApi->strExternalSessionTemplateName, (char*)pValue, sizeof(pConnApi->strExternalSessionTemplateName)); + NetPrintf(("connapi: [%p] 'exst', external session template name saved as (%s)\n", pConnApi, pConnApi->strExternalSessionTemplateName)); + } + if (pConnApi->pProtoMangle != NULL) + { + return(ProtoMangleControl(pConnApi->pProtoMangle, 'exst', 0, 0, pConnApi->strExternalSessionTemplateName)); + } + return(0); + } + } + #endif + if (iControl == 'gprt') + { + NetPrintf(("connapi: [%p] using game port %d\n", pConnApi, iValue)); + pConnApi->uGamePort = (uint16_t)iValue; + return(0); + } + if (iControl == 'lbuf') + { + // set game link buffer size + pConnApi->iLinkBufSize = iValue; + return(0); + } + if (iControl == 'maxg') + { + // set maximum number of voipgroups + NetPrintf(("connapi: changing maximum number of voipgroups from %d to %d\n", _ConnApi_iMaxVoipGroups, iValue)); + _ConnApi_iMaxVoipGroups = (int8_t)iValue; + return(0); + } + if (iControl == 'meta') + { + // enable/disable commudp metadata + NetPrintf(("connapi: [%p] commudp metadata %s\n", pConnApi, iValue ? "enabled" : "disabled")); + pConnApi->bCommUdpMetadata = iValue; + return(0); + } + if (iControl == 'minp') + { + // set gamelink input packet queue length + pConnApi->iGameMinp = iValue; + return(0); + } + if (iControl == 'mngl') + { + #if CONNAPI_XBOX_NETWORKING + NetPrintf(("connapi: [%p] demangler cannot be disabled on Xbox One\n", pConnApi)); + #else + // set demangler enable/disable + NetPrintf(("connapi: [%p] demangling %s\n", pConnApi, iValue ? "enabled" : "disabled")); + pConnApi->bDemanglerEnabled = iValue; + #endif + return(0); + } + if (iControl == 'mout') + { + // set gamelink output packet queue length + pConnApi->iGameMout = iValue; + return(0); + } + if (iControl == 'mwid') + { + // set gamelink packet length + pConnApi->iGameMwid = iValue; + return(0); + } + if (iControl == 'nmsk') + { + // set netmask + pConnApi->uNetMask = (unsigned)iValue; + return(0); + } + if (iControl == 'rcbk') + { + // enable disc callback on removal + pConnApi->bRemoveCallback = iValue; + return(0); + } + #if CONNAPI_XBOX_NETWORKING + if (iControl == 'scid') + { + if ((pValue == NULL) || (*(char*)pValue == '\0')) + { + NetPrintf(("connapi: [%p] 'scid', invalid service configuration id\n", pConnApi)); + } + else + { + if (pConnApi->strScid != pValue) + { + ds_strnzcpy(pConnApi->strScid, (char*)pValue, sizeof(pConnApi->strScid)); + NetPrintf(("connapi: [%p] 'scid', service configuration name saved as (%s)\n", pConnApi, pConnApi->strScid)); + } + if (pConnApi->pProtoMangle != NULL) + { + return(ProtoMangleControl(pConnApi->pProtoMangle, 'scid', 0, 0, pConnApi->strScid)); + } + return(0); + } + } + #endif + if ((iControl == 'stun') && (pConnApi->pProtoTunnel == NULL) && (pValue != NULL)) + { + // set prototunnel ref + pConnApi->pProtoTunnel = (ProtoTunnelRefT *)pValue; + return(0); + } + if (iControl == 'time') + { + // set timeout + pConnApi->iTimeout = iValue; + VoipGroupControl(pConnApi->pVoipGroupRef, 'time', iValue, 0, NULL); + return(0); + } + if ((iControl == 'tctl') && (pConnApi->pProtoTunnel != NULL)) + { + return(ProtoTunnelControl(pConnApi->pProtoTunnel, iValue, iValue2, 0, pValue)); + } + if (iControl == 'tgam') + { + pConnApi->uGameTunnelFlag = iValue; + pConnApi->uGameTunnelFlagOverride = iValue2; + } + if (iControl == 'tunl') + { + // set tunnel status + if (iValue >= 0) + { + pConnApi->bTunnelEnabled = iValue; + VoipGroupControl(pConnApi->pVoipGroupRef, 'tunl', iValue, 0, NULL); + } + if (iValue2 > 0) + { + pConnApi->iTunnelPort = iValue2; + } + return(0); + } + if (iControl == 'type') + { + // set connection flags (CONNAPI_CONNFLAG_*) + NetPrintf(("connapi: [%p] connflag change from 0x%02x to 0x%02x\n", pConnApi, pConnApi->uConnFlags, iValue)); + pConnApi->uConnFlags = (uint16_t)iValue; + return(0); + } + if (iControl == 'ulmt') + { + // set gamelink unack window size + pConnApi->iGameUnackLimit = iValue; + return(0); + } + if (iControl == 'voig') + { + if (iValue == 'getr') //$$todo remove 'getr' support in future release + { + return(ConnApiStatus(pConnApi, 'vgrp', pValue, sizeof(pConnApi->pVoipGroupRef))); + } + VoipGroupControl(pConnApi->pVoipGroupRef, iValue, iValue2, 0, pValue); + return(0); + } + if (iControl == 'voip') + { + if(pConnApi->pVoipRef != NULL) + { + VoipControl(pConnApi->pVoipRef, iValue, iValue2, pValue); + return(0); + } + + NetPrintf(("connapi: [%p] - WARNING - ConnApiControl(): processing of 'voip' selector failed because of an uninitialized VOIP module reference!\n", pConnApi)); + } + if (iControl == 'vprt') + { + NetPrintf(("connapi: [%p] using voip port %d\n", pConnApi, iValue)); + pConnApi->uVoipPort = (uint16_t)iValue; + return(0); + } + if (iControl == 'vset') + { + uint8_t bVoipEnabled; + // if disabling VoIP, set game flags appropriately + if ((bVoipEnabled = iValue) == FALSE) + { + NetPrintf(("connapi: [%p] 'vset' used to globally disable voip\n", pConnApi)); + pConnApi->uConnFlags &= ~CONNAPI_CONNFLAG_VOIPCONN; + } + return(0); + } + #if DIRTYCODE_DEBUG + if (iControl == '!res') + { + ConnApiClientT *pClient; + if (iValue2 >= 0 && iValue2 < pConnApi->ClientList.iMaxClients) + { + pClient = &pConnApi->ClientList.Clients[iValue2]; + if (pClient->bAllocated == TRUE) + { + if (iValue > 0) + { + NetPrintf(("connapi: [%p] setting debug FailP2P flag for user at index %d\n", pConnApi, iValue2)); + pConnApi->ClientList.Clients[iValue2].uFlags |= CONNAPI_CLIENTFLAG_P2PFAILDBG; + if (iValue2 == pConnApi->iSelf) + { + NetPrintf(("connapi: [%p] setting debug FailP2P flag with our own index, disabling P2P for all users for all users\n", pConnApi)); + } + } + else + { + NetPrintf(("connapi: [%p] resetting debug FailP2P flag for user at index %d\n", pConnApi, iValue2)); + pConnApi->ClientList.Clients[iValue2].uFlags &= ~CONNAPI_CLIENTFLAG_P2PFAILDBG; + } + } + else + { + NetPrintf(("connapi: [%p] failed to set debug FailP2P flag for user at index %d\n", pConnApi, iValue2)); + return(-1); + } + } + else if (iValue2 < 0) + { + pConnApi->bFailP2PConnect = iValue; + NetPrintf(("connapi: [%p] setting debug FailP2P flag to %d for all users\n", pConnApi, iValue)); + } + else + { + NetPrintf(("connapi: [%p] cannot set debug FailP2P flag to %d for user at index %d\n", pConnApi, iValue, iValue2)); + } + + return(0); + } + #endif + if (iControl == 'sqos') + { + pConnApi->iQosDuration = iValue; + NetPrintf(("connapi: [%p] total duration of QoS characterization over netgamelinks --> %d ms %s\n", + pConnApi, pConnApi->iQosDuration, ((pConnApi->iQosDuration == 0) ? "(QoS over NetGameLink disabled)" : ""))); + + pConnApi->iQosInterval = iValue2; + NetPrintf(("connapi: [%p] send interval used for QoS characterization over netgamelinks --> %d ms\n", pConnApi, pConnApi->iQosInterval)); + + return(0); + } + if (iControl == 'lqos') + { + NetPrintf(("connapi: [%p] packet size used for QoS characterization over netgamelinks --> %d bytes\n", pConnApi, iValue)); + pConnApi->iQosPacketSize = iValue; + return(0); + } + if (iControl == 'estv') + { + ConnApiClientT *pClient; + if (iValue < pConnApi->ClientList.iMaxClients) + { + pClient = &pConnApi->ClientList.Clients[iValue]; + if (pClient->bAllocated == TRUE) + { + // no-op if it is already established + // in the case of the local user, he will always have bEstablishVoip == TRUE so let's not spam with extra logs about it + if (pClient->bEstablishVoip == FALSE) + { + NetPrintf(("connapi: [%p] activating delayed voip for client %d\n", pConnApi, iValue)); + pClient->bEstablishVoip = TRUE; + return(0); + } + } + else + { + NetPrintf(("connapi: [%p] activating delayed voip failed because client %d not allocated\n", pConnApi, iValue)); + return(-1); + } + } + else + { + NetPrintf(("connapi: [%p] activating delayed voip failed because of client index of range\n", pConnApi)); + return(-1); + } + } + + // unhandled + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiUpdate + + \Description + Update the ConnApi module (must be called directly if auto-update is disabled) + + \Input *pConnApi - pointer to module state + + \Notes + By default, ConnApiUpdate() is called internally via a NetConnIdle() callback + (auto-update). If auto-update is disabled via ConnApiControl('auto'), + ConnApiUpdate must be polled by the application instead. + + \Version 01/06/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ConnApiUpdate(ConnApiRefT *pConnApi) +{ + // update client flags + _ConnApiUpdateClientFlags(pConnApi); + + // update connapi connections + _ConnApiUpdateConnections(pConnApi); + + #if CONNAPI_XBOX_NETWORKING + /* + update protomangle outside the demangling phase context + required for protomangle to be pumped after a call to ProtoMangleControl('remv') + */ + if (pConnApi->bDemanglerEnabled == TRUE) + { + ProtoMangleUpdate(pConnApi->pProtoMangle); + } + #endif + + // handle removal of clients from client list, if requested + _ConnApiUpdateRemoval(pConnApi); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiAddCallback + + \Description + Register a new callback + + \Input *pConnApi - pointer to module state + \Input *pCallback - the callback to add + \Input *pUserData - the user data that will be passed back + + \Output + int32_t - negative means error. 0 or greater: slot used + + \Version 09/18/2008 (jrainy) +*/ +/********************************************************************************F*/ +int32_t ConnApiAddCallback(ConnApiRefT *pConnApi, ConnApiCallbackT *pCallback, void *pUserData) +{ + int32_t iIndex; + + // skip the first (0th, which is reserved for 'cbfp' and 'cbup' backward compatibility. + for(iIndex = 1; iIndex < CONNAPI_MAX_CALLBACKS; iIndex++) + { + if (pConnApi->pCallback[iIndex] == NULL) + { + pConnApi->pCallback[iIndex] = pCallback; + pConnApi->pUserData[iIndex] = pUserData; + + return(iIndex); + } + } + return(CONNAPI_CALLBACKS_FULL); +} + +/*F********************************************************************************/ +/*! + \Function ConnApiRemoveCallback + + \Description + Unregister a callback + + \Input *pConnApi - pointer to module state + \Input *pCallback - the callback to remove + \Input *pUserData - the user data that was originally passed in + + \Output + int32_t - negative means error. 0 or greater: slot freed + + \Version 09/18/2008 (jrainy) +*/ +/********************************************************************************F*/ +int32_t ConnApiRemoveCallback(ConnApiRefT *pConnApi, ConnApiCallbackT *pCallback, void *pUserData) +{ + int32_t iIndex; + + // skip the first (0th, which is reserved for 'cbfp' and 'cbup' backward compatibility. + for(iIndex = 1; iIndex < CONNAPI_MAX_CALLBACKS; iIndex++) + { + if ((pConnApi->pCallback[iIndex] == pCallback) && (pConnApi->pUserData[iIndex] == pUserData)) + { + pConnApi->pCallback[iIndex] = NULL; + pConnApi->pUserData[iIndex] = NULL; + return(iIndex); + } + } + return(CONNAPI_CALLBACK_NOT_FOUND); +} diff --git a/r5dev/thirdparty/dirtysdk/source/game/netgamedist.c b/r5dev/thirdparty/dirtysdk/source/game/netgamedist.c new file mode 100644 index 00000000..252a9914 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/game/netgamedist.c @@ -0,0 +1,2579 @@ +/*H*************************************************************************************************/ +/*! + + \File netgamedist.c + + \Description + This file provides some upper layer protocol abstractions such as controller packet + buffering and exchange logic. + + \Notes + \verbatim + + From a "client" perspective: + + pRef->InpBufData is the inbound queue where inbound multipackets from server are accumulated. + * pRef->InpBufData.iBeg identifies the oldest entry in that queue + (i.e. the next entry to be consumed locally) + * pRef->InpBufData.iEnd identifies the next free entry in that queue + (i.e. the next spot to write into when we read from the socket) + * An overflow in that queue is always detected by comparing pRef->InpBufData.iBeg and pRef->InpBufData.iEnd. + + pRef->OutBufData is the outbound queue where outbound packets are accumulated pending transmission to the server. + * pRef->OutBufData.iBeg identifies the oldest entry in that queue + (i.e. the next entry to be sent over the network) + * pRef->OutBufData.iEnd identifies the next free entry in that queue + (i.e. the next spot to write into when user submits data) + * SPECIFICITY: When pRef->OutBufData.iBeg is advanced, the entry that it used to point to is NOT YET invalidated + because it still needs to be surfaced up to the user later when paired with a inbound bundle from the server. + To track those "pending entries" located before the position of pRef->OutBufData.iBeg, this third pointer + is used: "pRef->InpBufData.iBeg+pRef->iIOOffset". Notice that it consists of a pointer tracking the + other queue + an adjustment offset. + * An overflow in that queue is always detected by comparing "pRef->InpBufData.iBeg+pRef->iIOOffset" + and pRef->OutBufData.iEnd. + + From a "server" perspective: + + pRef->InpBufData is the inbound queue where inbound packets from a specific client are accumulated. + * pRef->InpBufData.iBeg identifies the oldest entry in that queue + (i.e. the next entry to be consumed locally) + * pRef->InpBufData.iEnd identifies the next free entry in that queue + (i.e. the next spot to write into when we read from the socket) + * An overflow in that queue is always detected by comparing pRef->InpBufData.iBeg and pRef->InpBufData.iEnd. + + pRef->OutBufData is the outbound queue where outbound multipackets are accumulated pending transmission to a specific client. + * pRef->OutBufData.iBeg identifies the oldest entry in that queue + (i.e. the next entry to be sent over the network) + * pRef->OutBufData.iEnd identifies the next free entry in that queue + (i.e. the next spot to write into when the server has a bundle of paired inputs to submit for transmission) + * An overflow in that queue is always detected by comparing pRef->OutBufData.iBeg and pRef->OutBufData.iEnd + + \endverbatim + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2000-2018. ALL RIGHTS RESERVED. + + \Version 1.0 12/20/00 (GWS) Based on split of GmClient.c + \Version 1.1 12/31/01 (GWS) Cleaned up and made really platform independent + \Version 1.2 12/03/09 (mclouatre) Added configurable run-time verbosity +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/game/netgamepkt.h" +#include "DirtySDK/game/netgamelink.h" +#include "DirtySDK/game/netgamedist.h" + +/*** Defines ***************************************************************************/ + +#define NETGAMEDIST_VERBOSITY (2) + +#define TIMING_DEBUG (0) +#define PING_DEBUG (0) +#define INPUTCHECK_LOGGING_DELAY (15) // 15 msec +#define GMDIST_META_ARRAY_SIZE (32) // how many past versions of sparse multipacket to keep + +// PACKET_WINDOW can be overriden at build time with the nant global property called dirtysdk-distpktwindow-size +#ifndef PACKET_WINDOW +#define PACKET_WINDOW (64) +#endif + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! Describe an entry in the flat buffer. +typedef struct GameBufferLookupT +{ + uint32_t uInsertTime; //!< time when packet was queued + uint32_t uPos; //!< indexes into OutBufData.pControllerIO or InpBufData.pControllerIO + uint16_t uLen; + uint16_t uLenSize; +} GameBufferLookupT; + +//! Flat buffer structure. Implements a wrapping queue of packets. +typedef struct GameBufferDataT +{ + //! incoming and outgoing controller packets + unsigned char *pControllerIO; + //! length of the io buffer + uint32_t uBufLen; + // input/output lookup, addresses into pControllerIO + GameBufferLookupT IOLookUp[PACKET_WINDOW]; + //! index of first packet to send/process in pControllerIO + int32_t iBeg; + //! index of last packet to send/process in pControllerIO + int32_t iEnd; +} GameBufferDataT; + +//! Describes one version of multipacket. +typedef struct NetGameDistMetaInfoT +{ + //! which entries are used + uint32_t uMask; + //! number of players used + uint8_t uPlayerCount; + //! version number + uint8_t uVer; +} NetGameDistMetaInfoT; + +//! netgamedist internal state +struct NetGameDistRefT +{ + //! module memory group + int32_t iMemGroup; + void *pMemGroupUserData; + + //! output buffer for packets and lookup table + GameBufferDataT OutBufData; + //! input buffer for packets and lookup table + GameBufferDataT InpBufData; + + //! offset between the input and output queues + int32_t iIOOffset; + + //! local sequence number + uint32_t uLocalSeq; + //! global sequence number + uint32_t uGlobalSeq; + + //! external status monitoring + NetGameLinkStatT NetGameLinkStats; + + //! current exchange rate + int32_t iInputRate; + //! input exchange window + int32_t iInputWind; + //! clamp the min window size + int32_t iInputMini; + //! clamp the max window size + int32_t iInputMaxi; + + //! when to recalc window + uint32_t uInpCalc; + //! when packet was last send + uint32_t uInpNext; + + //! netgamelink ref + void *pNetGameLinkRef; + //! netgamelink stat func + NetGameDistStatProc *pStatProc; + //! netgame send function + NetGameDistSendProc *pSendProc; + //! netgame recv function + NetGameDistRecvProc *pRecvProc; + + NetGameDistDropProc *pDropProc; + NetGameDistPeekProc *pPeekProc; + + NetGameDistLinkCtrlProc *pLinkCtrlProc; + + //! when bActAsServer is true the index is of the player that owns the dist + //! if bActAsServer is false the index is of the local player in the dist game + uint32_t uDistIndex; + + //! the total number of players + uint32_t uTotalPlyrs; + + //! true if we are receiving multi packets from dirtycast in OTP mode + //! false if when inbound packets originated from the client in a 2 player mode + uint32_t bRecvMulti; + + //! whether we are acting as the server. + uint32_t bActAsServer; + + //! a max packet for use by input + NetGameMaxPacketT MaxPkt; + + //! a max packet for use by input + char aMultiBuf[NETGAME_DATAPKT_MAXSIZE]; + + //! the number of writes to a position in the input queue, (dropproc) + int32_t aPacketId[PACKET_WINDOW]; + + //! latest stats received by each pClient + NetGameDistStatT aRecvStats[GMDIST_MAX_CLIENTS]; + + //! Error condition. Set during calls like update, if an error occurs. + int32_t iErrCond; + int32_t iLastSentDelta; + uint32_t uSkippedInputCheckLogCount; + uint32_t uLastInputCheckLogTick; + + int32_t iInboundDropPktCnt; + int32_t iInboundPktCnt; + int32_t iOutboundPktCnt; + + //! total wait time in input queue + int32_t iWaitTimeTotal; + + //! total Input deqeued count + int32_t iInboundDequeueCnt; + + //! total dist processing time + int32_t iDistProcTimeTotal; + + //! total dist inputs processed + int32_t iDistProcCnt; + + char strErrorText[GMDIST_ERROR_SIZE]; + + //! whether we must update the flow control flags to the game server + uint8_t bUpdateFlowCtrl; + + //! the range of meta info we must update the + uint8_t uUpdateMetaInfoBeg; + uint8_t uUpdateMetaInfoEnd; + + //! whether we are ready to send or not + uint8_t bRdySend; + + //! whether we are ready to receive or not + uint8_t bRdyRecv; + + //! whether remote is ready to send or not + uint8_t bRdySendRemote; + + //! whether remote is ready to receive or not + uint8_t bRdyRecvRemote; + + uint32_t uLocalCRC; + uint8_t bLocalCRCValid; + + uint32_t uRemoteCRC; + uint8_t bRemoteCRCValid; + + //! debug output verbosity + uint8_t uVerbose; + + //! boolean indicating whether we want to surface CRC cahllenges from the GS + uint8_t bCRCChallenges; + + //! boolean indicating whether we received meta information on the packet layout + uint8_t bGotMetaInfo; + + //! boolean indicating whether we are sending sparse multi-packets + uint8_t bSparse; + + //! Meta information about sparse multi-packets to send + NetGameDistMetaInfoT aMetaInfoToSend[GMDIST_META_ARRAY_SIZE]; + + //! Received meta information about sparse multi-packets (wraps around) + NetGameDistMetaInfoT aMetaInfo[GMDIST_META_ARRAY_SIZE]; + + //! The version meta information from the last peeked or queried packet + uint32_t uLastQueriedVersion; +}; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +#if DIRTYCODE_LOGGING +// The following is meant to be indexed with the corresponding constants defined in netgamedist.h. +// Array contents need to be tailored if constants are removed, modified or added. +static char _strNetGameDistDataTypes[6][32] = + {"INVALID", + "GMDIST_DATA_NONE", + "GMDIST_DATA_INPUT", + "GMDIST_DATA_INPUT_DROPPABLE", + "GMDIST_DATA_DISCONNECT", + "GMDIST_DATA_NODATA"}; +#endif + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +#if PING_DEBUG +/*F*************************************************************************************************/ +/*! + \Function _PingHistory + + \Description + Display the uPing history [DEBUG only] + + \Input *pStats - pointer to NetGameLinkStat struct + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +static void _PingHistory(const NetGameLinkStatT *pStats) +{ + int32_t iPing; + int32_t iIndex; + char strMin[64]; + char strMax[64]; + char strAvg[64]; + char strCnt[64]; + const NetGameLinkHistT *pHist; + static uint32_t uPrev = 0; + + // see if its time + if (pStats->pingslot == uPrev) + { + return; + } + uPrev = pStats->pingslot; + + iPing = 0; + + for (iIndex = 0; iIndex < PING_HISTORY; ++iIndex) + { + pHist = pStats->pinghist + ((pStats->pingslot - iIndex) & (PING_HISTORY-1)); + strMin[iIndex] = '0'+pHist->min/50; + strMax[iIndex] = '0'+pHist->max/50; + strAvg[iIndex] = '0'+pHist->avg/50; + strCnt[iIndex] = '0'+pHist->cnt; + + iPing += pHist->avg; + } + strMin[iIndex] = 0; + strMax[iIndex] = 0; + strAvg[iIndex] = 0; + strCnt[iIndex] = 0; + + iPing /= PING_HISTORY; + + NetPrintf(("history(%d/%d): ping=%d, late=%d, calc=%d\n", pStats->pingslot, pStats->pingtick, pStats->ping, pStats->late, iPing)); + NetPrintf((" %s\n", strMin)); + NetPrintf((" %s\n", strMax)); + NetPrintf((" %s\n", strAvg)); + NetPrintf((" %s\n", strCnt)); +} +#endif + +/*F*************************************************************************************************/ +/*! + \Function _SetDataPtrTry + + \Description + returns the position in the input or output buffer for addition of a packet of length uLength. + + \Input *pRef - reference pointer + \Input *pBuffer - buffer pointer + \Input uLength - length of the next packet to be stored + + \Output + int32_t - pos if a space was found. -1 if the buffer cannot accomodate uLength + + \Version 02/08/07 (jrainy) +*/ +/*************************************************************************************************F*/ +static int32_t _SetDataPtrTry(NetGameDistRefT *pRef, GameBufferDataT *pBuffer, uint16_t uLength) +{ + uint32_t uIndexA, uIndexB; + uint32_t uPosA; + + if (uLength > pBuffer->uBufLen) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "overflow in _SetDataPtrTry. requested length (%d) > buffer size (%d), ", uLength, pBuffer->uBufLen); + NetPrintf(("netgamedist: [%p] critical failure in _SetDataPtrTry()\n", pRef)); + return(-1); + } + + // identify the IOLookup index that points to the last-written entry (uIndexA), and find out where the corresponding next free byte exactly is (uPosA) + uIndexA = GMDIST_Modulo(pBuffer->iEnd - 1, PACKET_WINDOW); + uPosA = pBuffer->IOLookUp[uIndexA].uPos + pBuffer->IOLookUp[uIndexA].uLen; + uPosA = ((uPosA + 3)&~3); // aligns posA to the next 4-byte boundary + + // identify the IOLookup index that points to the oldest valid entry (uIndexB) + if ((pBuffer == &pRef->InpBufData) || pRef->bActAsServer) + { + /* for the input queue (inbound data from either server or client) or the server output queue (outbound data to client) + the iBeg index identifies the oldest valid data */ + uIndexB = pBuffer->iBeg; + } + else + { + /* For the client output queue (outbound data to server), the iBeg index identifies the next packet to be + sent to the server not the oldest valid data. The oldest valid data, i.e. pending data to be + notified as paired by the game server, is rather always identified using a combination of + the "inbound" iBeg and the iOOffset. */ + uIndexB = GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset, PACKET_WINDOW); + } + + // if the buffer is empty then we can just start filling it in from the beginning + if (uIndexB == (unsigned)pBuffer->iEnd) + { + return(0); + } + else + { + /* note: uIndexB can be used to access valid data in IOLookup[] only if uIndexB != pBuffer->iEnd + because pBuffer->iEnd points to the next "free" entry... so it contains no valid data yet. */ + + // find out where the last valid byte exactly is (uPosB) + uint32_t uPosB = pBuffer->IOLookUp[uIndexB].uPos; + + if (uPosA >= uPosB) + { + // if we can't fit at the end, let's retry from the beginning + if ((uPosA + uLength) > pBuffer->uBufLen) + { + uPosA = 0; + // fall through to the next 'if' + } + else + { + return(uPosA); + } + } + if (uPosB >= uPosA) + { + if ((uPosA + uLength) >= uPosB) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "overflow in _SetDataPtrTry. indexA was %d, indexB was %d, posA was %d, posB was %d, ", uIndexA, uIndexB, pBuffer->IOLookUp[uIndexA].uPos + pBuffer->IOLookUp[uIndexA].uLen, uPosB); + + NetPrintf(("netgamedist: [%p] %s buffer full.\n", pRef, ((pBuffer == &pRef->InpBufData)?"input":"output"))); + return(-1); + } + else + { + return(uPosA); + } + } + } + + // unreachable code. If we get here something went terribly wrong. + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "_SetDataPtrTry reached unreachable code."); + NetPrintf(("netgamedist: [%p] critical failure in _SetDataPtrTry() - unreachable code\n", pRef)); + + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function _SetDataPtr + + \Description + Prepares the input or output buffer for addition of a packet of length uLength. + + \Input *pRef - reference pointer + \Input *pBuffer - buffer pointer + \Input uLength - length of the next packet to be stored + + \Output + uint8_t - TRUE if a space was found. FALSE if the buffer cannot accomodate uLength + + \Version 02/08/07 (jrainy) +*/ +/*************************************************************************************************F*/ +static uint8_t _SetDataPtr(NetGameDistRefT *pRef, GameBufferDataT *pBuffer, uint16_t uLength) +{ + int32_t uPos = _SetDataPtrTry(pRef, pBuffer, uLength); + + if (uPos != -1) + { + pBuffer->IOLookUp[pBuffer->iEnd].uPos = uPos; + return(TRUE); + } + + return(FALSE); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistCheckWindow + + \Description + Handle the window stretching. If we have too many unacknowledged packets, we start sending less often + + \Input *pRef - The NetGameDist ref + \Input iRemain - Remaining frame time (before window stretching) + \Input uTick - Current tick + + \Version 12/21/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static void _NetGameDistCheckWindow(NetGameDistRefT *pRef, int32_t iRemain, uint32_t uTick) +{ + int32_t iQueue; + + if (!pRef->bRecvMulti) + { + // stretch cycle if we are over window + iQueue = GMDIST_Modulo(pRef->OutBufData.iEnd - GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset, PACKET_WINDOW), PACKET_WINDOW); + if ((iQueue > pRef->iInputWind) && (iRemain < pRef->iInputRate /2)) + { + #if TIMING_DEBUG + // dont show single cycle adjustments + if (uTick+pRef->iInputRate/2-pRef->uInpNext != 1) + { + NetPrintf(("netgamedist: [%p] stretching cycle (que=%d, win=%d, tick=%d, add=%d)\n", + pRef, iQueue, pRef->iInputWind, uTick, (uTick+pRef->iInputRate/2)-pRef->uInpNext)); + } + #endif + // stretch the next send + pRef->uInpNext = uTick+pRef->iInputRate /2; + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistUpdateSendTime + + \Description + Update the next send time after sending each packet. In p2p mode, clamp the next send to + no earlier than half a frame after *now* and no later than two frames after *now* + + \Input *pRef - The NetGameDist ref + + \Version 12/21/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static void _NetGameDistUpdateSendTime(NetGameDistRefT *pRef) +{ + uint32_t uTick; + int32_t iNext; + // get current time + + uTick = NetTick(); + // record the send time so we can schedule next packet + pRef->uInpNext += pRef->iInputRate; + + if (!pRef->bRecvMulti) + { + // figure time till next send + iNext = pRef->uInpNext - uTick; + // clamp the time range to half/double rate + if (iNext < pRef->iInputRate / 2) + { + #if TIMING_DEBUG + NetPrintf(("send clamping to half (was %d)\n", iNext)); + #endif + pRef->uInpNext = uTick + pRef->iInputRate /2; + } + if (iNext > pRef->iInputRate * 2) + { + #if TIMING_DEBUG + NetPrintf(("send clamping to double (was %d)\n", iNext)); + #endif + pRef->uInpNext = uTick + pRef->iInputRate *2; + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistSendInput + + \Description + Provide local input data + + \Input *pRef - reference pointer + \Input *pBuffer - controller data + \Input iLength - data length + \Input iLengthSize - size of length buffer + + \Output + int32_t - negative=error (including GMDIST_OVERFLOW=overflow), positive=packet successfully sent or saved to NetGameDist send buffer + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameDistSendInput(NetGameDistRefT *pRef, void *pBuffer, int32_t iLength, int32_t iLengthSize) +{ + int32_t iNext, iResult, iBeg; + unsigned char *pData; + + // add packet to queue + if (pBuffer != NULL) + { + // verify length is valid + if (iLength < 0) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "netgamedist: _NetGameDistSendInput with iLength %d.", iLength); + NetPrintf(("netgamedist: invalid buffer length passed to _NetGameDistSendInput()!\n")); + return(GMDIST_INVALID); + } + + // see if room to buffer packet + iNext = GMDIST_Modulo(pRef->OutBufData.iEnd+1, PACKET_WINDOW); + // the - 1 steals a spot but prevents: + // input local prechanging iooffset triggering this + iBeg = GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset - 1, PACKET_WINDOW); + if (!pRef->bActAsServer && (iNext == iBeg)) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "netgamedist: _NetGameDistSendInput with iNext %d and iBeg %d.", iNext, iBeg); + return(GMDIST_OVERFLOW); + } + + if (iNext == pRef->OutBufData.iBeg) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "netgamedist: _NetGameDistSendInput with iNext %d and OutBufData.iBeg %d.", iNext, pRef->OutBufData.iBeg); + return(GMDIST_OVERFLOW); + } + + // point to buffer + if (!_SetDataPtr(pRef, &pRef->OutBufData, iLength)) + { + return(GMDIST_OVERFLOW); + } + pData = pRef->OutBufData.pControllerIO+pRef->OutBufData.IOLookUp[pRef->OutBufData.iEnd].uPos; + + // copy data into buffer + pRef->OutBufData.IOLookUp[pRef->OutBufData.iEnd].uLen = iLength; + pRef->OutBufData.IOLookUp[pRef->OutBufData.iEnd].uLenSize = iLengthSize; + ds_memcpy(pData, pBuffer, iLength); + + // save down the time + pRef->OutBufData.IOLookUp[pRef->OutBufData.iEnd].uInsertTime = NetTick(); + + // incorporate into buffer + pRef->OutBufData.iEnd = iNext; + + // moved from inside the 'while' below + // this will update the send time on send attempt, not on actual send. + // i.e. "queue packets at regular interval", not "queue packets so that + // they send at regular interval", which is not really doable anyway. + _NetGameDistUpdateSendTime(pRef); + } + + // try and send packet + // prevent sending if we have pending metainfo to send as it will affect the format + while ((pRef->OutBufData.iBeg != pRef->OutBufData.iEnd) && (pRef->uUpdateMetaInfoBeg == pRef->uUpdateMetaInfoEnd)) + { + // point to buffer + pData = pRef->OutBufData.pControllerIO+pRef->OutBufData.IOLookUp[pRef->OutBufData.iBeg].uPos; + + // setup a data input packet + if (pRef->bActAsServer) + { + if (pRef->OutBufData.IOLookUp[pRef->OutBufData.iBeg].uLenSize == 2) + { + pRef->MaxPkt.head.kind = GAME_PACKET_INPUT_MULTI_FAT; + } + else + { + pRef->MaxPkt.head.kind = GAME_PACKET_INPUT_MULTI; + } + } + else + { + pRef->MaxPkt.head.kind = GAME_PACKET_INPUT; + } + + pRef->MaxPkt.head.len = pRef->OutBufData.IOLookUp[pRef->OutBufData.iBeg].uLen; + ds_memcpy(pRef->MaxPkt.body.data, pData, pRef->MaxPkt.head.len); + + // try and send + iResult = pRef->pSendProc(pRef->pNetGameLinkRef, (NetGamePacketT*)&pRef->MaxPkt, 1); + if (iResult > 0) + { + // all is well -- remove from buffer + pRef->OutBufData.iBeg = GMDIST_Modulo(pRef->OutBufData.iBeg+1, PACKET_WINDOW); + } + else if (iResult < 0) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "netgamedist: GMDIST_SENDPROC_FAILED result is %d.", iResult); + + pRef->iErrCond = GMDIST_SENDPROC_FAILED; + NetPrintf(("netgamedist: sendproc failed in _NetGameDistSendInput()!\n")); + return(GMDIST_SENDPROC_FAILED); + } + else + { + // lower-level transport is out of send buffer space at the moment exit for now and try again later. + break; + } + } + + // packet was either buffered and/or sent + return(1); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProcessPacketType + + \Description + Whenever a packet of a given type is received, this function is called + to do any specific process. + + \Input *pRef - reference pointer + \Input uPacketType - the packet type + + \Version 02/22/07 (jrainy) +*/ +/*************************************************************************************************F*/ +static void _ProcessPacketType(NetGameDistRefT *pRef, uint8_t uPacketType) +{ + if (!pRef->bActAsServer && ((uPacketType == GAME_PACKET_INPUT_MULTI) || (uPacketType == GAME_PACKET_INPUT_MULTI_FAT))) + { + pRef->bRecvMulti = TRUE; + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistSendFlowUpdate + + \Description + Sends a packet to the server enabling or disabling flow control + + \Input *pRef - reference pointer + + \Version 09/29/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static void _NetGameDistSendFlowUpdate(NetGameDistRefT *pRef) +{ + int32_t iResult; + + pRef->MaxPkt.head.kind = GAME_PACKET_INPUT_FLOW; + pRef->MaxPkt.head.len = 7; + + pRef->MaxPkt.body.data[0] = pRef->bRdySend; + pRef->MaxPkt.body.data[1] = pRef->bRdyRecv; + + pRef->MaxPkt.body.data[2] = pRef->bLocalCRCValid; + pRef->MaxPkt.body.data[3] = (pRef->uLocalCRC >> 24); + pRef->MaxPkt.body.data[4] = (pRef->uLocalCRC >> 16); + pRef->MaxPkt.body.data[5] = (pRef->uLocalCRC >> 8); + pRef->MaxPkt.body.data[6] = pRef->uLocalCRC; + + pRef->bLocalCRCValid = FALSE; + + iResult = pRef->pSendProc(pRef->pNetGameLinkRef, (NetGamePacketT*)&pRef->MaxPkt, 1); + + if (iResult < 0) + { + NetPrintf(("netgamedist: [%p] Flow update failed (error=%d)!\n", pRef, iResult)); + pRef->bUpdateFlowCtrl = FALSE; + } + else if (iResult > 0) + { + pRef->bUpdateFlowCtrl = FALSE; + } + else + { + NetPrintf(("netgamedist: [%p] Flow update deferred (error=overflow)!\n", pRef)); + // nothing to do here, the caller - NetGameDistUpdate - will try again later. + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistSendMetaInfo + + \Description + Sends a packet to the client describing the format of upcoming packets + + \Input *pRef - reference pointer + + \Version 09/29/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static void _NetGameDistSendMetaInfo(NetGameDistRefT *pRef) +{ + int32_t iResult; + NetGameDistMetaInfoT *pMetaInfo; + + while(pRef->uUpdateMetaInfoBeg != pRef->uUpdateMetaInfoEnd) + { + pMetaInfo = &pRef->aMetaInfoToSend[pRef->uUpdateMetaInfoBeg]; + + pRef->MaxPkt.head.kind = GAME_PACKET_INPUT_META; + pRef->MaxPkt.head.len = 5; + + pRef->MaxPkt.body.data[0] = pMetaInfo->uVer; + pRef->MaxPkt.body.data[1] = pMetaInfo->uMask >> 24; + pRef->MaxPkt.body.data[2] = pMetaInfo->uMask >> 16; + pRef->MaxPkt.body.data[3] = pMetaInfo->uMask >> 8; + pRef->MaxPkt.body.data[4] = pMetaInfo->uMask; + + iResult = pRef->pSendProc(pRef->pNetGameLinkRef, (NetGamePacketT*)&pRef->MaxPkt, 1); + + if (iResult < 0) + { + NetPrintf(("netgamedist: [%p] Meta info update failed (error=%d)!\n", pRef, iResult)); + pRef->uUpdateMetaInfoBeg = GMDIST_Modulo(pRef->uUpdateMetaInfoBeg + 1, GMDIST_META_ARRAY_SIZE); + } + else if (iResult > 0) + { + NetPrintf(("netgamedist: [%p] Meta info update sent %d 0x%08x !\n", pRef, pMetaInfo->uVer, pMetaInfo->uMask)); + pRef->uUpdateMetaInfoBeg = GMDIST_Modulo(pRef->uUpdateMetaInfoBeg + 1, GMDIST_META_ARRAY_SIZE); + } + else + { + NetPrintf(("netgamedist: [%p] Meta info update deferred (error=overflow)!\n", pRef)); + // nothing to do here, the caller - NetGameDistUpdate - will try again later. + + break; + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistCountBits + + \Description + Count the number of bits set in a given uint32_t variable + + \Input *pRef - reference pointer + \Input uMask - mask + + \Output + uint32_t - the number of bits set + + \Version 09/26/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static uint32_t _NetGameDistCountBits(NetGameDistRefT *pRef, uint32_t uMask) +{ + uint32_t uCount = 0; + + while(uMask) + { + uCount += (uMask & 1); + uMask /= 2; + }; + + return(uCount); +} + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistInputCheckLog + + \Description + Logs the values returned by NetGameDistInputCheck. + + \Input *pRef - reference pointer + \Input *pSend - the pointer to pSend the client passed to InputCheck + \Input *pRecv - the pointer to pRecv the client passed to InputCheck + + \Version 12/21/09 (jrainy) +*/ +/*************************************************************************************************F*/ +static void _NetGameDistInputCheckLog(NetGameDistRefT *pRef, int32_t *pSend, int32_t *pRecv) +{ + uint32_t uCurrentTick = NetTick(); + uint32_t uDelay; + + // Calculate delay since last log + uDelay = NetTickDiff(uCurrentTick, pRef->uLastInputCheckLogTick); + + // Check if condition to display the trace is met. + // Condition is: meaningful send/recv info ready OR skip timeout expire + if ( (pSend && (*pSend==0)) || (pRecv && (*pRecv!=0)) || // check meaningful send/recv + (uDelay >= INPUTCHECK_LOGGING_DELAY) ) // check skip timeout + { + if (pSend && pRecv) + { + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] exiting NetGameDistInputCheck() tick=%d, *pSend=%d, *pRecv=%d logskipped_count=%d\n", + pRef, uCurrentTick, *pSend, *pRecv, pRef->uSkippedInputCheckLogCount)); + } + else if (pSend && !pRecv) + { + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] exiting NetGameDistInputCheck() tick=%d, *pSend=%d, pRecv=NULL logskipped_count=%d\n", + pRef, uCurrentTick, *pSend, pRef->uSkippedInputCheckLogCount)); + } + else if (!pSend && pRecv) + { + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] exiting NetGameDistInputCheck() tick=%d, pSend=NULL, *pRecv=%d logskipped_count=%d\n", + pRef, uCurrentTick, *pRecv, pRef->uSkippedInputCheckLogCount)); + } + else + { + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] exiting NetGameDistInputCheck() tick=%d, pSend=NULL, pRecv=NULL logskipped_count=%d\n", + pRef, uCurrentTick, pRef->uSkippedInputCheckLogCount)); + } + + // Re-initialize variables used to skip some logs + pRef->uSkippedInputCheckLogCount = 0; + pRef->uLastInputCheckLogTick = uCurrentTick; + } + else + { + pRef->uSkippedInputCheckLogCount++; + } +} +#endif + +/*F*************************************************************************************************/ +/*! + \Function _NetGameDistInputPeekRecv + + \Description + Behaves just as a receive function, taking data from netgamelink. However, we peek first and + if an overflow is detected, we do not take the packet from netgamelink. In this case, we + return 0, as if doing was ready to be received + + \Input *pRef - reference pointer + \Input *pBuf - buffer to receive into + \Input iLen - available length in buf + + \Output + int32_t - recvproc return value, or forced to 0 if we'd be in overflow + + \Version 08/15/11 (jrainy) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameDistInputPeekRecv(NetGameDistRefT *pRef, NetGamePacketT *pBuf, int32_t iLen) +{ + NetGamePacketT* pPeekPacket = NULL; + + if (pRef->pPeekProc) + { + uint32_t uDistMask = (1 << GAME_PACKET_INPUT) | + (1 << GAME_PACKET_INPUT_MULTI) | + (1 << GAME_PACKET_INPUT_MULTI_FAT); + + (*pRef->pPeekProc)(pRef->pNetGameLinkRef, &pPeekPacket, uDistMask); + + // ok, we have a mean to see what is coming, and we got something. + if (pPeekPacket) + { + // let's check if we have space for it. + // for completeness, we may check buffer overflow on slots and dropproc, but this seems overkill + if (_SetDataPtrTry(pRef, &pRef->InpBufData, pPeekPacket->head.len + 1) < 0) + { + NetPrintf(("netgamedist: [%p] not receiving from link layer a packet of type %d and length %d because our buffer is full\n", pRef, pPeekPacket->head.kind, pPeekPacket->head.len)); + // act as if the recvproc had nothing. + return(0); + } + } + } + + return((*pRef->pRecvProc)(pRef->pNetGameLinkRef, pBuf, iLen, TRUE)); +} +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistCreate + + \Description + Create the game pClient + + \Input *pNetGameLinkRef - netgamelink module ref + \Input *pStatProc - netgamelink stat callback + \Input *pSendProc - game send func + \Input *pRecvProc - game recv func + \Input uInBufferSize - input buffer size, plan around PACKET_WINDOW*n*packets + \Input uOutBufferSize - output buffer size, plan around PACKET_WINDOW*packets + + \Output + NetGameDistRefT * - pointer to module state + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +NetGameDistRefT *NetGameDistCreate(void *pNetGameLinkRef, NetGameDistStatProc *pStatProc, NetGameDistSendProc *pSendProc, NetGameDistRecvProc *pRecvProc, uint32_t uInBufferSize, uint32_t uOutBufferSize ) +{ + NetGameDistRefT *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pRef = DirtyMemAlloc(sizeof(*pRef), NETGAMEDIST_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("netgamedist: [%p] unable to allocate module state\n", pRef)); + return(NULL); + } + ds_memclr(pRef, sizeof(*pRef)); + pRef->iMemGroup = iMemGroup; + pRef->pMemGroupUserData = pMemGroupUserData; + pRef->iLastSentDelta = -1; + + // Allocate the input and output buffers + pRef->InpBufData.pControllerIO = DirtyMemAlloc( uInBufferSize, NETGAMEDIST_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData ); + pRef->OutBufData.pControllerIO = DirtyMemAlloc( uOutBufferSize, NETGAMEDIST_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData ); + + ds_memclr(pRef->InpBufData.pControllerIO, uInBufferSize); + ds_memclr(pRef->OutBufData.pControllerIO, uOutBufferSize); + + pRef->InpBufData.uBufLen = uInBufferSize; + pRef->OutBufData.uBufLen = uOutBufferSize; + + // save the link layer callbacks + pRef->pNetGameLinkRef = pNetGameLinkRef; + pRef->pStatProc = pStatProc; + pRef->pSendProc = pSendProc; + pRef->pRecvProc = pRecvProc; + + // set default controller exchange rate + pRef->iInputRate = 50; + // set the defaults + pRef->iInputMini = 1; + pRef->iInputMaxi = 10; + + // Defaults to two players so that the non-multi version behaves as it used to. + pRef->uTotalPlyrs = 2; + + // Set default verbosity level + pRef->uVerbose = 1; + + // init check tick counter + pRef->uLastInputCheckLogTick = NetTick(); + + NetPrintf(("netgamedist: module created with PACKET_WINDOW = %d\n", PACKET_WINDOW)); + + return(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistDestroy + + \Description + Destroy the game pClient + + \Input *pRef - reference pointer + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameDistDestroy(NetGameDistRefT *pRef) +{ + DirtyMemFree( pRef->InpBufData.pControllerIO, NETGAMEDIST_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData ); + DirtyMemFree( pRef->OutBufData.pControllerIO, NETGAMEDIST_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData ); + pRef->InpBufData.pControllerIO = NULL; + pRef->OutBufData.pControllerIO = NULL; + + // free our memory + DirtyMemFree(pRef, NETGAMEDIST_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistStatus + + \Description + Get status information + + \Input *pRef - reference pointer + \Input iSelect - selector + \Input iValue - input value + \Input *pBuf - output buffer + \Input iBufSize - output buffer size + + \Output + int32_t - selector specific return value + + \Notes + This is read-only data which can be read at any time. This reference becomes + invalid when the module is destroyed. + + The count of [?snd..?out] is the number of packets ready to send. The min of + [?cmp..?inp] and [?cmp..?out] is the number of packets ready to process (because + you need a packet from each peer in order to perform processing). + + Selectors are: + + \verbatim + SELECTOR RETURN RESULT + 'dcnt' returns the total inbound dequeued count + 'drop' returns the total input packet dropped + 'icnt' returns the total inbound packet count + 'late' returns the latency from the link stats + 'mult' returns the number of players and writes their stats to pBuf + 'ocnt' returns the total outbound packet count + 'pcnt' returns the total packet processed count (full round trip) + 'plat' returns the end-to-end latency in number of packets + 'prti' returns the total packet processed time (full round trip) + 'pwin' returns PACKET_WINDOW configured for netgamedist + 'qver' returns the last queried packet version + 'rate' returns current exchange rate + 'rcrc' if iValue==0 returns whether we have a value otherwise returns the value itself + 'rrcv' returns if remote is ready to receive or not + 'rsnd' returns if remote is ready to send or not + 'stat' returns the link stats via pBuf + 'wait' returns total inbound packet wait time; + 'wind' returns the current exchange window + '?cmp' returns the offset of first packet to process in input buffer + '?cws' returns if we can send. FALSE if sending would overflow the send queue, iValue is length + '?inp' returns the offset of last packet to process in input buffer + '?out' returns the offset of last packet to send in output buffer + '?snd' returns the offset of first packet to send in output buffer + \endverbatim + + Unhandled selectors are passed on to NetGameLink + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistStatus(NetGameDistRefT *pRef, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + if (iSelect == 'dcnt') + { + return(pRef->iInboundDequeueCnt); + } + if (iSelect == 'drop') + { + return(pRef->iInboundDropPktCnt); + } + if (iSelect == 'icnt') + { + return(pRef->iInboundPktCnt); + } + if (iSelect == 'late') + { + return(pRef->NetGameLinkStats.late); + } + if (iSelect == 'mult') + { + // Make user-provide buffer is large enough to receive a pointer + if ((pBuf != NULL) && (iBufSize >= (int32_t)(sizeof(NetGameDistStatT) * pRef->uTotalPlyrs))) + { + ds_memcpy(pBuf, pRef->aRecvStats, sizeof(NetGameDistStatT) * pRef->uTotalPlyrs); + return(pRef->uTotalPlyrs); + } + else + { + // unhandled + return(-1); + } + } + if (iSelect == 'ocnt') + { + return(pRef->iOutboundPktCnt); + } + if (iSelect == 'plat') + { + return(GMDIST_Modulo(pRef->OutBufData.iEnd - GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset, PACKET_WINDOW), PACKET_WINDOW)); + } + if (iSelect == 'pcnt') + { + return(pRef->iDistProcCnt); + } + if (iSelect == 'prti') + { + return(pRef->iDistProcTimeTotal); + } + if (iSelect == 'pwin') + { + return(PACKET_WINDOW); + } + if (iSelect == 'qver') + { + return(pRef->uLastQueriedVersion); + } + if (iSelect == 'rate') + { + return(pRef->iInputRate); + } + if (iSelect == 'rcrc') + { + if (iValue) + { + int32_t ret = pRef->uRemoteCRC; + pRef->uRemoteCRC = 0; + pRef->bRemoteCRCValid = FALSE; + return(ret); + } + else + { + return(pRef->bRemoteCRCValid); + } + } + if (iSelect == 'rrcv') + { + return(pRef->bRdyRecvRemote); + } + if (iSelect == 'rsnd') + { + return(pRef->bRdySendRemote); + } + if (iSelect == 'stat') + { + (pRef->pStatProc)(pRef->pNetGameLinkRef, iSelect, iValue, &pRef->NetGameLinkStats, sizeof(NetGameLinkStatT)); + + // Make user-provide buffer is large enough to receive a pointer + if ((pBuf != NULL) && (iBufSize >= (int32_t)sizeof(NetGameLinkStatT))) + { + ds_memcpy(pBuf, &pRef->NetGameLinkStats, sizeof(NetGameLinkStatT)); + return(0); + } + else + { + // unhandled + return(-1); + } + } + if (iSelect == 'wait') + { + return(pRef->iWaitTimeTotal); + } + if (iSelect == 'wind') + { + return(pRef->iInputWind); + } + if (iSelect == '?cmp') + { + return(pRef->InpBufData.iBeg); + } + if (iSelect == '?cws') + { + int32_t iNext; + if (iValue < 0) + { + return(FALSE); + } + + iNext = GMDIST_Modulo(pRef->OutBufData.iEnd +1, PACKET_WINDOW); + + // mclouatre April 26th 2018 - unclear to me what the -1 is for in the condition below + if (!pRef->bActAsServer && (iNext == GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset - 1, PACKET_WINDOW))) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "'?cws' failure while comparing indices"); + return(FALSE); + } + if (_SetDataPtrTry(pRef, &pRef->OutBufData, iValue) == -1) + { + return(FALSE); + } + + return(TRUE); + } + if (iSelect == '?inp') + { + return(pRef->InpBufData.iEnd); + } + if (iSelect == '?out') + { + return(pRef->OutBufData.iEnd); + } + if (iSelect == '?snd') + { + return(pRef->OutBufData.iBeg); + } + + // fallthrough to NetGameLinkStatus + return((pRef->pStatProc)(pRef->pNetGameLinkRef, iSelect, iValue, pBuf, iBufSize)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistSetServer + + \Description + Get pointer to status counters + + \Input *pRef - reference pointer + \Input bActAsServer - boolean, whether NetGameDist should send multi-packets + + \Version 02/26/07 (jrainy) +*/ +/*************************************************************************************************F*/ +void NetGameDistSetServer(NetGameDistRefT *pRef, uint8_t bActAsServer) +{ + // the trigraph is there to make sure we don't store non-{0,1} value in. + pRef->bActAsServer = bActAsServer ? TRUE : FALSE; +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistMultiSetup + + \Description + Tells NetGameDist how many players there are in the game, and which one we are. + Will only affect the order packets are received from NetGameDistInputQueryMulti. + + \Input *pRef - netgamelink module ref + \Input iDistIndex - which player we are + \Input iTotPlrs - total number of players + + \Version 02/08/07 (jrainy) +*/ +/*************************************************************************************************F*/ +void NetGameDistMultiSetup(NetGameDistRefT *pRef, int32_t iDistIndex, int32_t iTotPlrs) +{ + NetPrintf(("netgamedist: [%p] NetGameDistMultiSetup index: %d, total players: %d\n", pRef, iDistIndex, iTotPlrs)); + + pRef->uTotalPlyrs = iTotPlrs; + pRef->uDistIndex = iDistIndex; +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistMetaSetup + + \Description + Tells NetGameDist whether to enable meta-information sending, and + sets the mask & the version number for the meta-information. + + \Input *pRef - netgamelink module ref + \Input bSparse - enable or disable meta-information sending (disabling after enabling is currently not-supported on the client) + \Input uMask - set the mask for the meta-information + \Input uVersion - set the version number for the meta-information. + + \Version 08/29/11 (szhu) +*/ +/*************************************************************************************************F*/ +void NetGameDistMetaSetup(NetGameDistRefT *pRef, uint8_t bSparse, uint32_t uMask, uint32_t uVersion) +{ + NetPrintf(("netgamedist: [%p] NetGameDistMetaSetup Enabled: %s, Mask: 0x%x, Version: %d\n", + pRef, bSparse?"TRUE":"FALSE", uMask, uVersion)); + + pRef->bSparse = bSparse; + pRef->aMetaInfoToSend[pRef->uUpdateMetaInfoEnd].uMask = uMask; + pRef->aMetaInfoToSend[pRef->uUpdateMetaInfoEnd].uPlayerCount = _NetGameDistCountBits(pRef, uMask); + pRef->aMetaInfoToSend[pRef->uUpdateMetaInfoEnd].uVer = GMDIST_Modulo(uVersion, GMDIST_META_ARRAY_SIZE); + pRef->uUpdateMetaInfoEnd = GMDIST_Modulo(pRef->uUpdateMetaInfoEnd + 1, GMDIST_META_ARRAY_SIZE); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputPeek + + \Description + See if a completed packet is ready + + \Input *pRef - reference pointer + \Input *pType - will be filled with data type + \Input *pPeer - buffer sent by peer + \Input *pPlen - length of data in peer buffer. Must pass in the length of pPeer + + \Output + int32_t - zero=no data pending, negative=error, positive=data returned + + \Version 02/05/2007 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistInputPeek(NetGameDistRefT *pRef, uint8_t *pType, void *pPeer, int32_t *pPlen) +{ + uint8_t *pPsrc; + int32_t iLength; + uint8_t uInputKind; + int16_t iLengthSize = 1; + uint8_t uOffsetLengths; + uint8_t uOffsetBuffer; + uint8_t uOffsetTypes; + uint8_t uOffsetVersion; + uint16_t uPlayerCount = pRef->uTotalPlyrs; + uint8_t uVer = 0; + + + // if no remote data, do an update + if (pRef->InpBufData.iBeg == pRef->InpBufData.iEnd) + { + NetGameDistUpdate(pRef); + } + + // no packets from peer? + if (pRef->InpBufData.iBeg == pRef->InpBufData.iEnd) + { + return(0); + } + + // point to data buffer + pPsrc = pRef->InpBufData.pControllerIO+pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uPos; + + uInputKind = pPsrc[pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uLen - 1]; + + if (uInputKind == GAME_PACKET_INPUT_MULTI_FAT) + { + iLengthSize = 2; + } + + // find the location to lookup the version + uOffsetVersion = pRef->bRecvMulti ? 1 : 0; + + // extract the version number of the packet, and player count, if the server ever gave us metadata + if (pRef->bGotMetaInfo && pRef->bRecvMulti) + { + uVer = GMDIST_Modulo(pPsrc[uOffsetVersion], GMDIST_META_ARRAY_SIZE); + uPlayerCount = pRef->aMetaInfo[uVer].uPlayerCount; + pRef->uLastQueriedVersion = uVer; + } + + // always pack packets as if there was at least 2 players (to avoid negatively-sized fields) + if (pRef->bRecvMulti && (pRef->uTotalPlyrs == 1)) + { + uPlayerCount = 2; + } + + // compute offsets to various parts + uOffsetTypes = uOffsetVersion + (pRef->bGotMetaInfo ? 1 : 0); + uOffsetLengths = uOffsetTypes + (pRef->bRecvMulti ? (uPlayerCount / 2) : 1); + uOffsetBuffer = uOffsetLengths + (pRef->bRecvMulti ? (iLengthSize * (uPlayerCount - 2)) : 0); + + // copy over the data, -1 to skip the packet kind tacked at the end + iLength = pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uLen - uOffsetBuffer - 1; + if (pPeer != NULL) + { + //check the passed-in length to prevent overflow + if (iLength > *pPlen) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "NetGameDistInputPeek error. length is %d, *pLen is %d.", iLength, *pPlen); + + // fail if the buffer is insufficient + *pPlen = iLength; + return(GMDIST_OVERFLOW); + } + + ds_memcpy(pPeer, pPsrc + uOffsetBuffer, iLength); + } + + *pPlen = iLength; + + if (pType != NULL) + { + *pType = *(pPsrc + (pRef->bRecvMulti ? 1 : 0)); + } + + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] peeked type %d len %d\n", pRef, pType?*pType:0, pPlen?*pPlen:0)); + #endif + + // return success + return(pRef->aPacketId[pRef->InpBufData.iBeg]); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputQuery + + \Description + See if a completed packet is ready + + \Input *pRef - reference pointer + \Input *pOurs - buffer previously sent with NetGameDistInputLocal + \Input *pOlen - length of data in ours buffer + \Input *pPeer - buffer sent by peer + \Input *pPlen - length of data in peer buffer + + \Output + int32_t - zero=no data pending, negative=error, positive=data returned + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistInputQuery(NetGameDistRefT *pRef, void *pOurs, int32_t *pOlen, void *pPeer, int32_t *pPlen) +{ + void* aInputs[2]; + int32_t aLengths[2]; + uint8_t aTypes[2]; + int32_t iRet; + + if ( pRef->uTotalPlyrs != 2 ) + { + return(GMDIST_BADSETUP); + } + + // for a 2 player setup our index is always 0 and the peer is always at 1 + aInputs[0] = pOurs; + aInputs[1] = pPeer; + + iRet = NetGameDistInputQueryMulti(pRef, aTypes, aInputs, aLengths); + + *pOlen = aLengths[0]; + *pPlen = aLengths[1]; + + return(iRet); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputQueryMulti + + \Description + See if a completed packet is ready + + \Input *pRef - reference pointer + \Input *pDataTypes - array of data types (GMDIST_DATA_NONE, GMDIST_DATA_INPUT, GMDIST_DATA_INPUT_DROPPABLE, GMDIST_DATA_DISCONNECT). + \Input **ppInputs - array of pointers to inputs to receive. + \Input *pLen - array of lengths from the data received. + + \Output + int32_t - zero=no data pending, negative=error, positive=data returned + + \Version 02/08/07 (jrainy) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistInputQueryMulti(NetGameDistRefT *pRef, uint8_t *pDataTypes, void **ppInputs, int32_t *pLen) +{ + unsigned char *pOsrc; + uint32_t uIndex, uCount, uPos, uRecvLen; + unsigned char *pRecvBuf; + uint8_t *pLengths; + uint8_t *pTypePos; + uint8_t uDelta; + uint8_t uOffsetLengths; + uint8_t uOffsetBuffer; + uint8_t uOffsetTypes; + uint8_t uOffsetVersion; + uint32_t uOurInp; + uint16_t uPlayerCount = pRef->uTotalPlyrs; + uint8_t bCRCRequest = FALSE; + uint8_t uVer = 0; + + // if waiting on remote data, do an update + if (pRef->InpBufData.iBeg == pRef->InpBufData.iEnd) + { + NetGameDistUpdate(pRef); + } + + // check for inputs ready to process; in 2p mode we also require a non-empty output buffer to pair with + if ((pRef->InpBufData.iBeg == pRef->InpBufData.iEnd) || (!pRef->bActAsServer && !pRef->bRecvMulti && (GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset, PACKET_WINDOW) == pRef->OutBufData.iEnd))) + { + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] exiting NetGameDistInputQueryMulti() retval=0 tick=%d len=n/a type=n/a\n", pRef, NetTick())); + #endif + + return(0); + } + + if (ppInputs) + { + // access the first byte after the received data + uint8_t* pPacketKind = pRef->InpBufData.pControllerIO + pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uPos + pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uLen - 1; + int16_t iLengthSize = 1; + + if (*pPacketKind == GAME_PACKET_INPUT_MULTI_FAT) + { + iLengthSize = 2; + } + + // Go through the received packet and fill all the input data except ours, then copy our own from outlkup + + uCount = 0; + uPos = 0; + uRecvLen = pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uLen - 1; + pRecvBuf = pRef->InpBufData.pControllerIO+pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uPos; + uDelta = pRef->bRecvMulti ? pRecvBuf[0] : 1; + + uOffsetVersion = pRef->bRecvMulti ? 1 : 0; + + // extract the version number of the packet, and player count, if the server ever gave us metadata + if (pRef->bGotMetaInfo && pRef->bRecvMulti) + { + uVer = GMDIST_Modulo(((char *)pRecvBuf)[uOffsetVersion], GMDIST_META_ARRAY_SIZE); + uPlayerCount = pRef->aMetaInfo[uVer].uPlayerCount; + pRef->uLastQueriedVersion = uVer; + } + + // always pack packets as if there was at least 2 players (to avoid negatively-sized fields) + if (pRef->bRecvMulti && (uPlayerCount == 1)) + { + uPlayerCount = 2; + } + + // compute offsets to various parts + uOffsetTypes = uOffsetVersion + (pRef->bGotMetaInfo ? 1 : 0); + uOffsetLengths = uOffsetTypes + (pRef->bRecvMulti ? (uPlayerCount / 2) : 1); + uOffsetBuffer = uOffsetLengths + (pRef->bRecvMulti ? (iLengthSize * (uPlayerCount - 2)) : 0); + + pLengths = pRecvBuf + uOffsetLengths; + pTypePos = pRecvBuf + uOffsetTypes; + pRecvBuf += uOffsetBuffer; + + for (uIndex = 0; uIndex < pRef->uTotalPlyrs; uIndex++) + { + /* if this is not our own input and we are either + sending for everyone + or this is a player we are including in the current sparse multi-packet + then, add it in */ + if (uIndex != pRef->uDistIndex) + { + if (!pRef->bGotMetaInfo || (pRef->aMetaInfo[uVer].uMask & (1 << uIndex))) + { + /* compute the length of packet for player i. + last length is known (ours) second to last is implicit (total - known ones) */ + if (uCount == (uPlayerCount - 2u)) + { + pLen[uIndex] = uRecvLen - uPos - uOffsetBuffer; + } + else + { + if (iLengthSize == 2) + { + pLen[uIndex] = (pLengths[uCount * iLengthSize] * 256) + pLengths[uCount * iLengthSize + 1]; + } + else + { + pLen[uIndex] = pLengths[uCount]; + } + } + + // prevent invalid sizes if bogus data is received + if (pLen[uIndex] < 0) + { + pLen[uIndex] = 0; + } + + // clamp the total read length to the received buffer, to prevent overflow. + if (pLen[uIndex] + uPos + uOffsetBuffer > uRecvLen) + { + NetPrintfVerbose((pRef->uVerbose, 0, "netgamedist: Warning ! Buffer overrun. Packet trimmed.\n")); + pLen[uIndex] = uRecvLen - uPos - uOffsetBuffer; + } + + // compute the type of packet for player i. (nibbles) + if (uCount % 2) + { + pDataTypes[uIndex] = *pTypePos >> 4; + pTypePos++; + } + else + { + pDataTypes[uIndex] = *pTypePos & 0x0f; + } + + // copy the packet for player i. + if (((pDataTypes[uIndex] & ~GMDIST_DATA_CRC_REQUEST) == GMDIST_DATA_INPUT) || + ((pDataTypes[uIndex] & ~GMDIST_DATA_CRC_REQUEST) == GMDIST_DATA_INPUT_DROPPABLE)) + { + ds_memcpy(ppInputs[uIndex], pRecvBuf + uPos, pLen[uIndex]); + uPos += pLen[uIndex]; + } + else + { + pLen[uIndex] = 0; + } + + // if crc-checking is disabled locally, clear the "crc request" bit + if (!pRef->bCRCChallenges) + { + pDataTypes[uIndex] &= ~GMDIST_DATA_CRC_REQUEST; + } + + /* set a local variable if any of the input has a crc request in. + this is to set the "crc request" bit in local input too, to ease our customers job. */ + if (pDataTypes[uIndex] & GMDIST_DATA_CRC_REQUEST) + { + bCRCRequest = TRUE; + } + + uCount++; + } + else + { + pDataTypes[uIndex] = GMDIST_DATA_NODATA; + pLen[uIndex] = 0; + } + } + } + + if (uDelta) + { + uint8_t uDropIndex; + + // update dist process times for dropped inputs (inputs dropped are considered processed) + for (uDropIndex = 0; uDropIndex < (uDelta - 1) ; uDropIndex++, pRef->iDistProcCnt++) + { + uint32_t uCurInp = GMDIST_Modulo(pRef->InpBufData.iBeg + uDropIndex, PACKET_WINDOW); + pRef->iDistProcTimeTotal += NetTickDiff(NetTick(), pRef->OutBufData.IOLookUp[uCurInp].uInsertTime); + } + + // point to data buffers. Our local storage has just 1-byte type field + pRef->iIOOffset += (uDelta - 1); + uOurInp = GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset, PACKET_WINDOW); + pOsrc = pRef->OutBufData.pControllerIO + pRef->OutBufData.IOLookUp[uOurInp].uPos + 1; + + // copy over the data + if (pRef->OutBufData.IOLookUp[uOurInp].uLen != 0) + { + pLen[pRef->uDistIndex] = pRef->OutBufData.IOLookUp[uOurInp].uLen - 1; + ds_memcpy(ppInputs[pRef->uDistIndex], pOsrc, pLen[pRef->uDistIndex]); + pRef->iDistProcTimeTotal += NetTickDiff(NetTick(), pRef->OutBufData.IOLookUp[uOurInp].uInsertTime); + pRef->iDistProcCnt++; + pDataTypes[pRef->uDistIndex] = *(pRef->OutBufData.pControllerIO + pRef->OutBufData.IOLookUp[uOurInp].uPos); + } + else + { + NetPrintf(("netgamedist: detected invalid pairing index between input and output queue; ignoring")); + pLen[pRef->uDistIndex] = 0; + pDataTypes[pRef->uDistIndex] = GMDIST_DATA_NONE; + } + } + else + { + pRef->iIOOffset = GMDIST_Modulo(pRef->iIOOffset - 1, PACKET_WINDOW); + pDataTypes[pRef->uDistIndex] = GMDIST_DATA_NONE; + pLen[pRef->uDistIndex] = 0; + } + + if (bCRCRequest) + { + pDataTypes[pRef->uDistIndex] |= GMDIST_DATA_CRC_REQUEST; + } + } + + // calculate time in queue and add it to the total + pRef->iWaitTimeTotal += NetTickDiff(NetTick(), pRef->InpBufData.IOLookUp[pRef->InpBufData.iBeg].uInsertTime); + pRef->iInboundDequeueCnt++; + + // return item from buffer + pRef->InpBufData.iBeg = GMDIST_Modulo(pRef->InpBufData.iBeg + 1, PACKET_WINDOW); + // update global sequence number + pRef->uGlobalSeq += 1; + + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] exiting NetGameDistInputQueryMulti() retval=%d tick=%d\n", pRef, pRef->uGlobalSeq, NetTick())); + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, " ==== Dumping player-specific data returned in [OUT] params\n")); + if (ppInputs) + { + for (uIndex = 0; uIndex < pRef->uTotalPlyrs; uIndex++) + { + uint8_t uDataType = (pDataTypes[uIndex] & ~GMDIST_DATA_CRC_REQUEST); + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, " == Player %u --> len=%d type=%s\n", uIndex, pLen[uIndex], + _strNetGameDistDataTypes[(((uDataType >= GMDIST_DATA_NONE) && (uDataType <= GMDIST_DATA_NODATA)) ? uDataType : 0)])); + } + } + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, " ==== End of player-specific data dump\n")); + #endif + + // return the sequence number + return(pRef->uGlobalSeq); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputLocal + + \Description + Provide local input data + + \Input *pRef - reference pointer + \Input *pBuffer - controller data + \Input iLength - data length + + \Output + int32_t - negative=error (including GMDIST_OVERFLOW=overflow), positive=successfully sent or queued + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistInputLocal(NetGameDistRefT *pRef, void *pBuffer, int32_t iLength) +{ + if (!pBuffer) + { + return(_NetGameDistSendInput(pRef, NULL, iLength, 0)); + } + pRef->aMultiBuf[0] = GMDIST_DATA_INPUT; + ds_memcpy(pRef->aMultiBuf + 1, pBuffer, iLength); + return(_NetGameDistSendInput(pRef, pRef->aMultiBuf, iLength + 1, 1)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputLocalMulti + + \Description + Provide local input data for all players, including ours, which will be discarded + + \Input *pRef - reference pointer + \Input *pTypesArray - array of data types (GMDIST_DATA_INPUT_DROPPABLE, GMDIST_DATA_INPUT) + \Input **ppBuffer - array of controller data + \Input *pLengthsArray - array of data length + \Input iDelta - game team should pass in 1, other values only usable on server + + \Output + int32_t - negative=error (including GMDIST_OVERFLOW*=overflow), positive=successfully sent or queued + + \Version 02/08/07 (jrainy) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistInputLocalMulti(NetGameDistRefT *pRef, uint8_t *pTypesArray, void **ppBuffer, int32_t *pLengthsArray, int32_t iDelta) +{ + uint32_t uIndex, uPos, uCount; + uint8_t uOffsetLengths; + uint8_t uOffsetBuffer; + uint8_t uOffsetTypes; + uint8_t uOffsetVersion; + uint8_t *pLengths; + uint8_t *pTypePos; + uint8_t uOurType, uType; + uint32_t uSendSize; + int16_t iLengthSize = 1; + uint16_t uPlayerCount = pRef->bActAsServer ? pRef->uTotalPlyrs : 1; + uint16_t uLastPlayer = uPlayerCount; + int32_t iResult; + + // check if any input forces us to send fat multi-packets. + for (uIndex = 0; uIndex < uPlayerCount; uIndex++) + { + if (pLengthsArray[uIndex] > 0xFF) + { + iLengthSize = 2; + } + } + + // if we are sending sparse multipacket, adjust the player count accordingly + if (pRef->bSparse && pRef->bActAsServer) + { + uPlayerCount = pRef->aMetaInfoToSend[GMDIST_Modulo(pRef->uUpdateMetaInfoEnd - 1, GMDIST_META_ARRAY_SIZE)].uPlayerCount; + } + + // always pack multipacket as if there was at least 2 players (simplify the logic) + if (pRef->bActAsServer && (uPlayerCount == 1)) + { + uLastPlayer = 2; + uPlayerCount = 2; + } + + // compute the offset to various parts of the multipacket + uOffsetVersion = pRef->bActAsServer ? 1 : 0; + uOffsetTypes = uOffsetVersion + ((pRef->bSparse && pRef->bActAsServer) ? 1 : 0); + uOffsetLengths = uOffsetTypes + (pRef->bActAsServer ? (uPlayerCount / 2) : 1); + uOffsetBuffer = uOffsetLengths + (pRef->bActAsServer ? (iLengthSize * (uPlayerCount - 2)) : 0); + + // if server mode + if (pRef->bActAsServer) + { + // compute the total packet size + uSendSize = uOffsetBuffer; + for (uIndex = 0; uIndex < pRef->uTotalPlyrs; uIndex++) + { + if (uIndex != pRef->uDistIndex) + { + uSendSize += pLengthsArray[uIndex]; + } + } + + // bail out if the packet would overflow + if (uSendSize >= NETGAME_DATAPKT_MAXSIZE) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "Multipacket bigger than NETGAME_DATAPKT_MAXSIZE (%d). Discarding and reporting overflow.\n", NETGAME_DATAPKT_MAXSIZE); + NetPrintf(("netgamedist: [%p] Multipacket bigger than NETGAME_DATAPKT_MAXSIZE (%d). Discarding and reporting overflow.\n", pRef, NETGAME_DATAPKT_MAXSIZE)); + return(GMDIST_OVERFLOW_MULTI); + } + + // mark the version, if we are sending meta information + if (pRef->bSparse) + { + pRef->aMultiBuf[uOffsetVersion] = GMDIST_Modulo(pRef->aMetaInfoToSend[GMDIST_Modulo(pRef->uUpdateMetaInfoEnd - 1, GMDIST_META_ARRAY_SIZE)].uVer, GMDIST_META_ARRAY_SIZE); + } + + uOurType = (pTypesArray[pRef->uDistIndex] & ~GMDIST_DATA_CRC_REQUEST); + if (uOurType == GMDIST_DATA_NONE) + { + pRef->aMultiBuf[0] = 0; + } + else + { + if (((iDelta - 1) - pRef->iLastSentDelta) >= PACKET_WINDOW) + { + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "GMDIST_OVERFLOW_WINDOW (pRef->m_packetid[pRef->InpBufData.iBeg]-1) is %d, pRef->iLastSentDelta is %d.\n", (pRef->aPacketId[pRef->InpBufData.iBeg]-1), pRef->iLastSentDelta); + return(GMDIST_OVERFLOW_WINDOW); + } + + pRef->aMultiBuf[0] = (iDelta-1) - pRef->iLastSentDelta; + + pRef->iLastSentDelta = (iDelta-1); + } + pRef->aPacketId[pRef->InpBufData.iBeg] = 0; + + if (uOurType == GMDIST_DATA_NONE) + { + pRef->iIOOffset = GMDIST_Modulo(pRef->iIOOffset + 1, PACKET_WINDOW); + } + } + + pLengths = (unsigned char *)pRef->aMultiBuf + uOffsetLengths; + pTypePos = (unsigned char *)pRef->aMultiBuf + uOffsetTypes; + + uPos = uOffsetBuffer; + uCount = 0; + + for (uIndex = 0; uIndex < uLastPlayer; uIndex++) + { + uType = (pTypesArray[uIndex] & ~GMDIST_DATA_CRC_REQUEST); + + if (((uType == GMDIST_DATA_NONE) || (uType == GMDIST_DATA_DISCONNECT)) && !pRef->bActAsServer) + { + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY,"netgamedist: [%p] Exiting NetGameDistInputLocalMulti() because of invalid data type.\n", pRef)); + #endif + return(GMDIST_INVALID); + } + + // if sending multipackets, dont send input back to user who originally sent it + if (pRef->bActAsServer && (uIndex == pRef->uDistIndex)) + { + continue; + } + // if sending sparse multipackets, skip sending blank inputs for users that aren't in the game + if (pRef->bSparse && ((pRef->aMetaInfoToSend[GMDIST_Modulo(pRef->uUpdateMetaInfoEnd - 1, GMDIST_META_ARRAY_SIZE)].uMask & (1 << uIndex)) == 0)) + { + continue; + } + + // put the data in + ds_memcpy(pRef->aMultiBuf + uPos, ppBuffer[uIndex], pLengthsArray[uIndex]); + uPos += pLengthsArray[uIndex]; + + if (pRef->bActAsServer) + { + if (uCount < (uPlayerCount - 2u)) + { + // we assign the length here for normal packets + if (iLengthSize == 1) + { + pLengths[uCount] = pLengthsArray[uIndex]; + } + else + { + // and for fat multipackets + pLengths[uCount * 2] = pLengthsArray[uIndex] / 256; + pLengths[uCount * 2 + 1] = pLengthsArray[uIndex] % 256; + } + } + } + + if (uCount % 2) + { + *pTypePos = (*pTypePos & 0x0f) | ((pTypesArray[uIndex] << 4) & 0xf0); + pTypePos++; + } + else + { + *pTypePos = (pTypesArray[uIndex] & 0x0f); + } + uCount++; + } + + if (!NetGameDistStatus(pRef, '?cws', uPos, NULL, 0)) + { + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] Exiting NetGameDistInputLocalMulti() with return value GMDIST_OVERFLOW.\n", pRef)); + #endif + return(GMDIST_OVERFLOW); + } + + #if DIRTYCODE_LOGGING + { + uint8_t uDataType = (pTypesArray[0] & ~GMDIST_DATA_CRC_REQUEST); + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] NetGameDistInputLocalMulti() called with tick=%d len=%d type=%s\n", + pRef, NetTick(), pLengthsArray[0], _strNetGameDistDataTypes[(((uDataType >= GMDIST_DATA_NONE) && (uDataType <= GMDIST_DATA_NODATA)) ? uDataType : 0)])); + + } + #endif + + // increment output packet count + if ((iResult = _NetGameDistSendInput(pRef, pRef->aMultiBuf, uPos, iLengthSize)) > 0) + { + pRef->iOutboundPktCnt++; + } + + // pass the size of length used to the send input function + return(iResult); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputCheck + + \Description + Check input status (see how long till next) + + \Input *pRef - reference pointer + \Input *pSend - (optional) stores time until next packet should be sent + \Input *pRecv - (optional) stores whether data is available + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameDistInputCheck(NetGameDistRefT *pRef, int32_t *pSend, int32_t *pRecv) +{ + int32_t iRemain; + uint32_t uTick; + int32_t iInData; + int32_t iOutData; + + // call the status proc + (pRef->pStatProc)(pRef->pNetGameLinkRef, 'stat', 0, &pRef->NetGameLinkStats, sizeof(NetGameLinkStatT)); + + // get current time + uTick = NetTick(); + + // make sure next send time is initialized + if (pRef->uInpNext == 0) + { + pRef->uInpNext = uTick; + } + + // see if its time to recalc network parms + if (uTick > pRef->uInpCalc) + { + // set the number of unacknowledged packets permitted + // (this number must be large enough to cover the network latency + pRef->iInputWind = (pRef->NetGameLinkStats.late+pRef->iInputRate)/pRef->iInputRate; + + if (pRef->bRecvMulti) + { + pRef->iInputWind *= 2; + } + + if (pRef->iInputWind < pRef->iInputMini) + { + pRef->iInputWind = pRef->iInputMini; + } + if (pRef->iInputWind > pRef->iInputMaxi) + { + pRef->iInputWind = pRef->iInputMaxi; + } + if (pRef->iInputWind > 500/pRef->iInputRate) + { + pRef->iInputWind = 500/pRef->iInputRate; + } + // determine time till next update + pRef->uInpCalc = uTick + pRef->iInputRate; + } + + // figure out time remaining until next send + iRemain = pRef->uInpNext - uTick; + + // clamp to valid rate + if (iRemain < 0) + { + iRemain = 0; + } + if (iRemain > pRef->iInputRate *2) + { + iRemain = pRef->iInputRate *2; + } + + _NetGameDistCheckWindow(pRef, iRemain, uTick); + + // figure out time till next send + iRemain = pRef->uInpNext - uTick; + // clamp to valid rate + if (iRemain < 0) + { + iRemain = 0; + } + + // return time until next packet should be sent + if (pSend != NULL) + { + // make sure rate is set + if (pRef->iInputRate == 0) + { + // data is not initialized -- just return a delay + *pSend = 50; + } + else if ((pRef->OutBufData.iBeg != pRef->OutBufData.iEnd) || (!pRef->bRdyRecvRemote)) + { + // dont send while something in output queue + *pSend = 10; + } + else + { + *pSend = iRemain; + } + } + + // indicate if a packet is waiting + if (pRecv != NULL) + { + // if waiting on remote data, do an update + if (pRef->InpBufData.iBeg == pRef->InpBufData.iEnd) + { + NetGameDistUpdate(pRef); + } + // get input data queue length + iInData = GMDIST_Modulo(pRef->InpBufData.iEnd - pRef->InpBufData.iBeg, PACKET_WINDOW); + // get output data queue length (in multi-mode we assume one so we don't gate receiving due to an empty output queue) + iOutData = !pRef->bRecvMulti ? GMDIST_Modulo(pRef->OutBufData.iEnd - GMDIST_Modulo(pRef->InpBufData.iBeg + pRef->iIOOffset, PACKET_WINDOW), PACKET_WINDOW) : 1; + // write if data is available (non-zero=available) + *pRecv = (iInData < iOutData) ? iInData : iOutData; + } + + #if DIRTYCODE_LOGGING + _NetGameDistInputCheckLog(pRef, pSend, pRecv); + #endif +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputRate + + \Description + Set the input rate + + \Input *pRef - reference pointer + \Input iRate - new input rate + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameDistInputRate(NetGameDistRefT *pRef, int32_t iRate) +{ + // save rate if changed + if (iRate > 0) + { + pRef->iInputRate = iRate; + } +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistInputClear + + \Description + Flush the input queue. + + \Input *pRef - reference pointer + + \Notes + This must be done with independent synchronization before and after to avoid issues. + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameDistInputClear(NetGameDistRefT *pRef) +{ + // reset buffer pointers + pRef->OutBufData.iEnd = 0; + pRef->OutBufData.iBeg = 0; + pRef->InpBufData.iEnd = 0; + pRef->InpBufData.iBeg = 0; + + // reset sequence numbers + pRef->uLocalSeq = 0; + pRef->uGlobalSeq = 0; + + // reset the iooffset between the two queues. + pRef->iIOOffset = 0; + + // reset the send time + pRef->uInpNext = NetTick(); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistControl + + \Description + Set NetGameDist operation parameters + + \Input *pRef - reference pointer + \Input iSelect - item to tweak + \Input iValue - tweak value + \Input pValue - pointer tweak value + + \Output + int32_t - selector specific + + \Notes + + Selectors are: + + \verbatim + SELECTOR DESCRIPTION + 'clri' clear the local queue of inputs + 'crcs' enable/disable the reception of CRC challenges + 'lcrc' to provide the current CRC as request by NetGameDist + 'lrcv' local recv flow control. Set whether we are ready to receive or not. + 'lsnd' local send flow control. Set whether we are ready to send or not. + 'maxi' max window size clamp + 'mini' min window size clamp + 'spam' sets debug output verbosity (0...n) + \endverbatim + + \Version 11/18/08 (jrainy) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistControl(NetGameDistRefT *pRef, int32_t iSelect, int32_t iValue, void *pValue) +{ + if (iSelect == 'clri') + { + NetGameDistInputClear(pRef); + return(0); + } + if (iSelect == 'crcs') + { + pRef->bCRCChallenges = iValue; + return(0); + } + if (iSelect == 'lcrc') + { + pRef->bUpdateFlowCtrl = TRUE; + pRef->uLocalCRC = iValue; + pRef->bLocalCRCValid = TRUE; + return(0); + } + if (iSelect == 'lrcv') + { + if (iValue != pRef->bRdyRecv) + { + pRef->bUpdateFlowCtrl = TRUE; + pRef->bRdyRecv = iValue; + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] flow control change -> %s to receive\n", pRef, (pRef->bRdyRecv ? "ready" : "not ready"))); + } + return(pRef->bRdyRecv); + } + if (iSelect == 'lsnd') + { + if (iValue != pRef->bRdySend) + { + pRef->bUpdateFlowCtrl = TRUE; + pRef->bRdySend = iValue; + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] flow control change -> %s to send\n", pRef, (pRef->bRdySend ? "ready" : "not ready"))); + } + return(pRef->bRdySend); + } + if (iSelect == 'maxi') + { + if (iValue >= 0) + { + pRef->iInputMaxi = iValue; + } + return(pRef->iInputMaxi); + } + if (iSelect == 'mini') + { + if (iValue >= 0) + { + pRef->iInputMini = iValue; + } + return(pRef->iInputMini); + } + if (iSelect == 'spam') + { + pRef->uVerbose = iValue; + return(0); + } + // no action + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistUpdate + + \Description + Perform periodic tasks. + + \Input *pRef - reference pointer + + \Output + uint32_t - current sequence number + + \Notes + Application must call this every 100ms or so. + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +uint32_t NetGameDistUpdate(NetGameDistRefT *pRef) +{ + NetGamePacketT* pPacket = (NetGamePacketT*)&pRef->MaxPkt; + unsigned char *pData; + int32_t iNext, iCurrent; + uint32_t uIndex, uPos; + unsigned char *pExisting; + unsigned char *pIncoming; + + // update the input rate if needed + NetGameDistInputRate(pRef, 0); + + // send any pending game packets + NetGameDistInputLocal(pRef, NULL, 0); + + // grab incoming packets + while (( GMDIST_Modulo(pRef->InpBufData.iEnd + 1, PACKET_WINDOW) != pRef->InpBufData.iBeg) && (_NetGameDistInputPeekRecv(pRef, pPacket, 1) > 0)) + { + // dispatch the packet according to type + if ((pPacket->head.kind == GAME_PACKET_INPUT) || + (pPacket->head.kind == GAME_PACKET_INPUT_MULTI) || + (pPacket->head.kind == GAME_PACKET_INPUT_MULTI_FAT)) + { + pRef->iInboundPktCnt++; + + iNext = GMDIST_Modulo(pRef->InpBufData.iEnd + 1, PACKET_WINDOW); + if (pRef->InpBufData.iBeg != pRef->InpBufData.iEnd && !pRef->bRecvMulti && pRef->bActAsServer && pRef->pDropProc) + { + iCurrent = GMDIST_Modulo(pRef->InpBufData.iEnd - 1, PACKET_WINDOW); + pExisting = (pRef->InpBufData.pControllerIO + pRef->InpBufData.IOLookUp[iCurrent].uPos); + pIncoming = pPacket->body.data; + + if (pRef->pDropProc(pRef, pExisting + 1, pIncoming + 1, pExisting[0], pIncoming[0])) + { + iNext = pRef->InpBufData.iEnd; + pRef->iInboundDropPktCnt++; + pRef->InpBufData.iEnd = GMDIST_Modulo(pRef->InpBufData.iEnd - 1, PACKET_WINDOW); + } + } + + _ProcessPacketType(pRef, pPacket->head.kind); + // check for overflow + if (iNext == pRef->InpBufData.iBeg) + { + // ignore the packet + NetPrintf(("netgamedist: [%p] NetGameDistUpdate() - buffer overflow (queue full)!\n", pRef)); + ds_snzprintf(pRef->strErrorText, sizeof(pRef->strErrorText), "NetGameDistUpdate, buffer overflow (queue full)."); + + pRef->iErrCond = GMDIST_QUEUE_FULL; + return((unsigned)(-1)); + } + else + { + // point to data buffer + + // we currently write one byte past the buffer after reserving one extra byte + // the len set in the data structure includes this extra byte past the packet data + if (_SetDataPtr(pRef, &pRef->InpBufData, pPacket->head.len + 1)) + { + pData = pRef->InpBufData.pControllerIO+pRef->InpBufData.IOLookUp[pRef->InpBufData.iEnd].uPos; + pRef->InpBufData.IOLookUp[pRef->InpBufData.iEnd].uLen = pPacket->head.len + 1; + // copy into buffer + ds_memcpy(pData, pPacket->body.data, pPacket->head.len); + pData[pPacket->head.len] = pPacket->head.kind; + + pRef->aPacketId[pRef->InpBufData.iEnd] = pRef->iInboundPktCnt; + + // update queue time + pRef->InpBufData.IOLookUp[pRef->InpBufData.iEnd].uInsertTime = pPacket->head.when; + + // add item to buffer + pRef->InpBufData.iEnd = iNext; + + // display number of packets in buffer + #if TIMING_DEBUG + NetPrintf(("netgamedist: [%p] NetGameDistUpdate() inpbuf=%d packets\n", pRef, + GMDIST_Modulo(pRef->InpBufData.iEnd - pRef->InpBufData.iBeg, PACKET_WINDOW))); + #endif + } + else + { + NetPrintf(("netgamedist: [%p] NetGameDistUpdate() - buffer overflow (memory)!\n", pRef)); + pRef->iErrCond = GMDIST_QUEUE_MEMORY; + return((unsigned)(-1)); + } + } + } + else if (pPacket->head.kind == GAME_PACKET_STATS) + { + // clear the contents, if a player leaves the game we don't want their stats to be misrepresented + ds_memclr(pRef->aRecvStats, sizeof(pRef->aRecvStats)); + + for (uPos = 0, uIndex = 0; uPos < pPacket->head.len; uIndex += 1) + { + ds_memcpy(&pRef->aRecvStats[uIndex], pPacket->body.data + uPos, sizeof(*pRef->aRecvStats) ); + uPos += sizeof(*pRef->aRecvStats); + + pRef->aRecvStats[uIndex].late = SocketNtohs(pRef->aRecvStats[uIndex].late); + pRef->aRecvStats[uIndex].bps = SocketNtohs(pRef->aRecvStats[uIndex].bps); + } + } + else if (pPacket->head.kind == GAME_PACKET_INPUT_FLOW) + { + pRef->bRdySendRemote = pPacket->body.data[0]; + pRef->bRdyRecvRemote = pPacket->body.data[1]; + + if (pPacket->head.len >= 7) + { + if (pRef->MaxPkt.body.data[2]) + { + pRef->bRemoteCRCValid = TRUE; + pRef->uRemoteCRC = (pRef->MaxPkt.body.data[6] | + (pRef->MaxPkt.body.data[5] << 8) | + (pRef->MaxPkt.body.data[4] << 16) | + (pRef->MaxPkt.body.data[3] << 24)); + } + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pRef->uVerbose, NETGAMEDIST_VERBOSITY, "netgamedist: [%p] bRemoteCRCValid is %d, uRemoteCRC is %d\n", pRef, pRef->bRemoteCRCValid, pRef->uRemoteCRC)); + #endif + } + + NetPrintf(("netgamedist: [%p] Got GAME_PACKET_INPUT_FLOW (send %d recv %d)\n", pRef, pRef->bRdySendRemote, pRef->bRdyRecvRemote)); + } + else if (pPacket->head.kind == GAME_PACKET_INPUT_META) + { + // process meta-information about sparse multi-packets + uint8_t uVer; + uint32_t uMask; + + pRef->bGotMetaInfo = TRUE; + + uVer = GMDIST_Modulo(pPacket->body.data[0], GMDIST_META_ARRAY_SIZE); + uMask = ((pPacket->body.data[1] << 24) + + (pPacket->body.data[2] << 16) + + (pPacket->body.data[3] << 8) + + (pPacket->body.data[4])); + + NetPrintf(("netgamedist: Got GAME_PACKET_INPUT_META (version %d mask 0x%08x)\n", uVer, uMask)); + + pRef->aMetaInfo[uVer].uMask = uMask; + pRef->aMetaInfo[uVer].uPlayerCount = _NetGameDistCountBits(pRef, uMask); + pRef->aMetaInfo[uVer].uVer = uVer; + } + } + + // update link status + NetGameDistStatus(pRef, 'stat', 0, NULL, 0); + + // show the history + #if PING_DEBUG + _PingHistory(&pRef->NetGameLinkStats); + #endif + + // send meta-information as needed + if (pRef->uUpdateMetaInfoBeg != pRef->uUpdateMetaInfoEnd) + { + _NetGameDistSendMetaInfo(pRef); + } + + // send flow control updates as needed + if (pRef->bUpdateFlowCtrl) + { + _NetGameDistSendFlowUpdate(pRef); + } + + // return current sequence number + return(pRef->uGlobalSeq); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistSetProc + + \Description + Sets or override the various procedure pointers. + + \Input *pRef - reference pointer + \Input iKind - kind + \Input *pProc - proc + + \Notes + 'drop' should only be used on game servers + + \Version 02/27/07 (jrainy) +*/ +/*************************************************************************************************F*/ +void NetGameDistSetProc(NetGameDistRefT *pRef, int32_t iKind, void *pProc) +{ + switch(iKind) + { + case 'drop': + pRef->pDropProc = (NetGameDistDropProc *)pProc; + break; + case 'peek': + pRef->pPeekProc = (NetGameDistPeekProc *)pProc; + break; + case 'recv': + pRef->pRecvProc = (NetGameDistRecvProc *)pProc; + break; + case 'send': + pRef->pSendProc = (NetGameDistSendProc *)pProc; + break; + case 'stat': + pRef->pStatProc = (NetGameDistStatProc *)pProc; + break; + case 'link': + pRef->pLinkCtrlProc = (NetGameDistLinkCtrlProc *)pProc; + break; + } +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistSendStats + + \Description + Send stats. Meant to be used by the game server to send stats regarding all clients + + \Input *pRef - reference pointer + \Input *pStats - uTotalPlyrs-sized array of stats + + \Version 03/14/07 (jrainy) +*/ +/*************************************************************************************************F*/ +void NetGameDistSendStats(NetGameDistRefT *pRef, NetGameDistStatT *pStats) +{ + uint32_t uIndex; + int32_t iPos; + + pRef->MaxPkt.head.kind = GAME_PACKET_STATS; + pRef->MaxPkt.head.len = (uint16_t)sizeof(NetGameDistStatT) * pRef->uTotalPlyrs; + + iPos = 0; + for (uIndex = 0; uIndexuTotalPlyrs; uIndex++) + { + ds_memcpy(pRef->MaxPkt.body.data + iPos, &pStats[uIndex], sizeof(NetGameDistStatT)); + iPos += sizeof(NetGameDistStatT); + } + + pRef->pSendProc(pRef->pNetGameLinkRef, (NetGamePacketT*)&pRef->MaxPkt, 1); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistGetError + + \Description + return non-zero if there ever was an overflow + + \Input *pRef - reference pointer + + \Output + int32_t - 0 if there was no error, positive >0 otherwise. + + \Version 03/23/07 (jrainy) +*/ +/*************************************************************************************************F*/ +int32_t NetGameDistGetError(NetGameDistRefT *pRef) +{ + return(pRef->iErrCond); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistGetErrorText + + \Description + copies into the passed buffer the last error text. + + \Input *pRef - reference pointer + \Input *pErrorBuffer - buffer to write into + \Input iBufSize - buf size + + \Version 08/15/08 (jrainy) +*/ +/*************************************************************************************************F*/ +void NetGameDistGetErrorText(NetGameDistRefT *pRef, char *pErrorBuffer, int32_t iBufSize) +{ + int32_t iMinSize = sizeof(pRef->strErrorText); + + if (iBufSize < iMinSize) + { + iMinSize = iBufSize; + } + + strncpy(pErrorBuffer, pRef->strErrorText, iMinSize); + pErrorBuffer[iMinSize - 1] = 0; +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameDistResetError + + \Description + reset the overflow error condition to 0. + + \Input *pRef - reference pointer + + \Version 03/23/07 (jrainy) +*/ +/*************************************************************************************************F*/ +void NetGameDistResetError(NetGameDistRefT *pRef) +{ + pRef->iErrCond = 0; +} + + diff --git a/r5dev/thirdparty/dirtysdk/source/game/netgamedistserv.c b/r5dev/thirdparty/dirtysdk/source/game/netgamedistserv.c new file mode 100644 index 00000000..86e56fa2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/game/netgamedistserv.c @@ -0,0 +1,1651 @@ +/*H********************************************************************************/ +/*! + \File netgamedistserv.c + + \Description + Server module to handle 2+ NetGameDist connections in a client/server + architecture. + + \Copyright + Copyright (c) 2007 Electronic Arts Inc. + + \Version 02/01/2007 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/game/netgamepkt.h" +#include "DirtySDK/game/netgamedistserv.h" + +/*** Defines **********************************************************************/ + +#define MONITOR_HIGHWATER (0) +#define DISTSERV_MAX_MULTIPACKET_LENGTH (1100) +#define DISTSERV_MAX_FIXEDRATE_MULTIPLE (3) +#define DISTSERV_DEFAULT_FIXEDRATE (33) + +/*** Type Definitions *************************************************************/ + +//! client state +typedef struct NetGameDistServClientT +{ + NetGameDistRefT *pGameDist; //!< client dist ref + char strName[32]; //!< client name + uint8_t aLocalInput[NETGAME_DATAPKT_MAXSIZE]; //!< local input buffer (TODO - this will be a queue) + int32_t iLocalInputSize; //!< size of input data + int32_t iPeekKey; //!< delta at the time of our peek. + uint8_t bLocalInput; //!< whether we have local input or not + uint8_t iLocalInputType; //!< type of input + uint8_t bInitialized; //!< true if client is initialized + uint8_t bDisconnected; //!< whether the client is disconnected or not + int32_t iDisconnectReason; //!< why the disconnect + int32_t iCount; //!< total number of packets received + int32_t iCRC; //!< latest client-side CRC received + uint8_t uCRCValid; //!< whether we got a CRC response to the last request + uint8_t bRemoteRcv; //!< last 'rrcv' value (is client ready to recv) + uint8_t bRemoteSnd; //!< last 'rsnd' value (is client ready to send) + uint8_t bReallyPeeked; //!< indicate whether we peeked this client before InputLocalMulti this frame (to address delayed input scenarios) + uint32_t uNakSent; //!< previous nak sent stat + uint32_t uPackLost; //!< previous packet loss stat + char strDiscReason[GMDIST_ERROR_SIZE]; +} NetGameDistServClientT; + +//! client list +typedef struct NetGameDistServClientListT +{ + int32_t iNumClients; //!< current number of clients in list + int32_t iMaxClients; //!< max number of clients in list + NetGameDistServClientT Clients[1]; //!< variable-length array of clients +} NetGameDistServClientListT; + +//! module state +struct NetGameDistServT +{ + //! module memory group + int32_t iMemGroup; + void *pMemGroupUserData; + + //! verbose debug output level + int32_t iVerbosity; + + //! Last stats update time + uint32_t uLastStatsUpdate; + + // debugging + uint32_t aPeekTimes[32]; + uint32_t aRecvTimes[32]; + uint32_t aSendTimes[32]; + uint32_t aEscalate[32]; + int32_t iFixedRate; + + uint32_t uNbFramesNoData; + + uint32_t uCRCRate; //!< number of frames between challenges + uint32_t uCRCResponseLimit; //!< number of frames allowed to receive response + uint32_t uCRCRemainingFrames; //!< number of frames remaining until next challenge + uint32_t uCRCResponseCountdown; //!< number of frames remaining to accept CRC responses + + uint8_t uSendThreshold; + + #if MONITOR_HIGHWATER + int32_t iHighWaterInputQueue; + int32_t iHighWaterOutputQueue; + uint8_t uHighWaterChanged; + #endif + + int32_t iFirstClient; + uint32_t uLastFlowUpdateTime; + uint32_t uOutputMultiPacketCount; //!< total output dist multi packet + uint32_t uCurStatClientCount; //!< current stat client count + uint8_t bClientCountChanged; //!< did the client count change between stat sampling interval? + uint8_t bFlowEnabled; //!< whether all clients are ready to receive. + uint8_t bFlowEnabledChanged; //!< did flow control change between stat sampling interval? + uint8_t bNoInputMode; //!< TRUE means: outbound multipackets are no longer generated because inbound packets have not been coming in for multiple frames + uint8_t bNoInputModeChanged; //!< did no input mode change between stat sampling interval? + uint8_t uMultiPacketVersion; //!< sparse multi-packet version number. Changes when player list changes + uint8_t _pad[2]; + + NetGameDistServLoggingCbT *pLoggingCb; //!< logging callback + void *pUserData; //!< logging userdata + + //! variable-length array of clients -- must come last! + NetGameDistServClientListT ClientList; +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServLogPrintf + + \Description + Log printing for netgamedistserv module + + \Input *pDistServ - module state + \Input *pFormat - printf format string + \Input ... - variable argument list + + \Version 06/26/2019 (eesponda) +*/ +/********************************************************************************F*/ +static void _NetGameDistServLogPrintf(NetGameDistServT *pDistServ, const char *pFormat, ...) +{ + char strText[2048]; + int32_t iOffset = 0; + va_list Args; + + // format prefix + iOffset += ds_snzprintf(strText+iOffset, sizeof(strText)-iOffset, "netgamedistserv: "); + + // format output + va_start(Args, pFormat); + iOffset += ds_vsnprintf(strText+iOffset, sizeof(strText)-iOffset, pFormat, Args); + va_end(Args); + + // forward to callback if registered + if ((pDistServ != NULL) && (pDistServ->pLoggingCb != NULL)) + { + pDistServ->pLoggingCb(strText, pDistServ->pUserData); + } + else + { + NetPrintf(("%s", strText)); + } +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServLogPrintfVerbose + + \Description + Log printing for netgamedistserv module at varying verbosity levels + + \Input *pDistServ - module state + \Input iCheckLevel - the level we are checking our internal iVerbosity against + \Input *pFormat - printf format string + \Input ... - variable argument list + + \Version 06/26/2019 (eesponda) +*/ +/********************************************************************************F*/ +static void _NetGameDistServLogPrintfVerbose(NetGameDistServT *pDistServ, int32_t iCheckLevel, const char *pFormat, ...) +{ + char strText[2048]; + va_list Args; + + // no-op + if (pDistServ->iVerbosity <= iCheckLevel) + { + return; + } + + // format output + va_start(Args, pFormat); + ds_vsnprintf(strText, sizeof(strText), pFormat, Args); + va_end(Args); + + // format to the logging function + _NetGameDistServLogPrintf(pDistServ, "%s", strText); +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServSanityCheckIn + + \Description + Perform sanity checking on input. + + \Input *pDistServ - module state + \Input iClient - client to check + \Input iRet - result from NetGameDistInputPeek() + + \Version 01/01/2007 (jrainy) +*/ +/********************************************************************************F*/ +static void _NetGameDistServSanityCheckIn(NetGameDistServT *pDistServ, int32_t iClient, int32_t iRet) +{ + int32_t iDelay; + uint32_t uCurTick; + + uCurTick = NetTick(); + + // no need to perform sanity checking on input when flow is disabled + if (pDistServ->bFlowEnabled) + { + iDelay = uCurTick - pDistServ->aRecvTimes[iClient]; + if (iRet > 0) + { + pDistServ->aRecvTimes[iClient] = uCurTick; + + if (iDelay < 200) + { + pDistServ->aEscalate[iClient] = 0; + } + } + if ((iDelay > 200) && pDistServ->aRecvTimes[iClient] && (pDistServ->aEscalate[iClient] < 1)) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 0, "no input in 200 ms from client %d\n", iClient); + pDistServ->aEscalate[iClient] = 1; + } + else if ((iDelay > 500) && pDistServ->aRecvTimes[iClient] && (pDistServ->aEscalate[iClient] < 2)) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 0, "no input in 500 ms from client %d\n", iClient); + pDistServ->aEscalate[iClient] = 2; + } + else if ((iDelay > 1000) && pDistServ->aRecvTimes[iClient]) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 0, "no input in 1000 ms from client %d\n", iClient); + pDistServ->aRecvTimes[iClient] = uCurTick; + pDistServ->aEscalate[iClient] = 0; + } + + iDelay = uCurTick - pDistServ->aPeekTimes[iClient]; + pDistServ->aPeekTimes[iClient] = uCurTick; + if (iDelay > (pDistServ->iFixedRate * DISTSERV_MAX_FIXEDRATE_MULTIPLE)) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 0, "no peek in %d ms from client %d\n", iDelay, iClient); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServSanityCheckOut + + \Description + Perform sanity checking on output. + + \Input *pDistServ - module state + \Input iClient - client to check + \Input iRet - result from NetGameDistInputLocalMulti() + + \Version 01/01/2007 (jrainy) +*/ +/********************************************************************************F*/ +static void _NetGameDistServSanityCheckOut(NetGameDistServT *pDistServ, int32_t iClient, int32_t iRet) +{ + int32_t iDelay; + uint32_t uCurTick; + + uCurTick = NetTick(); + + iDelay = uCurTick - pDistServ->aSendTimes[iClient]; + if (iRet == 1) + { + pDistServ->aSendTimes[iClient] = uCurTick; + } + if ((iDelay > (pDistServ->iFixedRate * DISTSERV_MAX_FIXEDRATE_MULTIPLE)) && pDistServ->aSendTimes[iClient]) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 0, "no send in %d ms to client %d\n", iDelay, iClient); + pDistServ->aSendTimes[iClient] = uCurTick; + } +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServFlowControl + + \Description + Send flow control update from the server as appropriate + + \Input *pDistServ - module state + + \Version 12/03/2007 (jrainy) +*/ +/********************************************************************************F*/ +static void _NetGameDistServFlowControl(NetGameDistServT *pDistServ) +{ + int32_t iClient; + NetGameDistServClientT *pDistClient; + uint8_t bPrevFlowEnabled; //!< previous reported stat flow enabled boolean + + bPrevFlowEnabled = pDistServ->bFlowEnabled; + pDistServ->bFlowEnabled = TRUE; + + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + if (pDistClient->bInitialized && !pDistClient->bDisconnected) + { + if (!pDistClient->bRemoteRcv) + { + pDistServ->bFlowEnabled = FALSE; + } + } + } + + _NetGameDistServLogPrintf(pDistServ, "sending flow update lrcv %d\n", pDistServ->bFlowEnabled); + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + if (pDistClient->bInitialized && !pDistClient->bDisconnected) + { + NetGameDistControl(pDistClient->pGameDist, 'lrcv', pDistServ->bFlowEnabled, NULL); + } + } + + /* If flow control changed, flag the boolean used to remember that it happened at least once + during the metrics sampling interval. */ + if (bPrevFlowEnabled != pDistServ->bFlowEnabled) + { + if (pDistServ->bFlowEnabledChanged == FALSE) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 1, "metric sampling interval marked invalid for idpps and inputdrop because flow control changed (flow enabled = %d)\n", pDistServ->bFlowEnabled); + } + pDistServ->bFlowEnabledChanged = TRUE; + } +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServDiscClient + + \Description + Destroy game dist refs. + + \Input *pDistServ - module state + \Input *pDistClient - client to disconnect from + \Input iDistClient - index of client + \Input iReason - disconnection reason + + \Version 04/26/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static void _NetGameDistServDiscClient(NetGameDistServT *pDistServ, NetGameDistServClientT *pDistClient, int32_t iDistClient, int32_t iReason) +{ + // mark as disconnected + if (!pDistClient->bDisconnected) + { + _NetGameDistServLogPrintf(pDistServ, "disconnecting from client %d\n", iDistClient); + pDistClient->bDisconnected = TRUE; + pDistClient->iDisconnectReason = iReason; + } + else if (pDistClient->pGameDist != NULL) + { + _NetGameDistServLogPrintf(pDistServ, "warning -- client %d has gamedist but is disconnected\n", iDistClient); + } + + // destroy dist + if (pDistClient->pGameDist != NULL) + { + NetGameDistGetErrorText(pDistClient->pGameDist, pDistClient->strDiscReason, sizeof(pDistClient->strDiscReason)); + + _NetGameDistServLogPrintfVerbose(pDistServ, 0, "deleting dist ref for client %s/%d\n", pDistClient->strName, iDistClient); + NetGameDistDestroy(pDistClient->pGameDist); + pDistClient->pGameDist = NULL; + } + + _NetGameDistServFlowControl(pDistServ); +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistUpdateMulti + + \Description + Update Multi configuration when a client is added or removed + + \Input *pDistServ - module state + + \Version 02/13/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static void _NetGameDistUpdateMulti(NetGameDistServT *pDistServ) +{ + NetGameDistServClientT *pDistClient; + int32_t iClient; + int32_t iLastClient = 0; + uint32_t uMask = 0; + + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + if (pDistClient->bInitialized) + { + iLastClient = iClient; + + // set the bits in the mask for all initialized players + uMask |= (1 << iClient); + } + } + + // increment the multipacket version + pDistServ->uMultiPacketVersion++; + + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + if (pDistClient->pGameDist != NULL) + { + _NetGameDistServLogPrintf(pDistServ, "issuing NetGameDistMultiSetup %d %d\n", iClient, iLastClient + 1); + NetGameDistMultiSetup(pDistClient->pGameDist, iClient, iLastClient + 1); + + _NetGameDistServLogPrintf(pDistServ, "issuing NetGameDistMetaSetup 1 0x%0x %d\n", uMask, pDistServ->uMultiPacketVersion); + NetGameDistMetaSetup(pDistClient->pGameDist, 1, uMask, pDistServ->uMultiPacketVersion); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServDrop + + \Description + Default implementation of drop function. + + \Input *pLinkRef - module state + \Input *pExisting - input packet at end of the input queue + \Input *pIncoming - newly received input packet + \Input uTypeExisting - type of the packet in the queue + \Input uTypeIncoming - type of the new packet + + \Output + int8_t - TRUE if the input packet can be dropped, FALSE otherwise + + \Version 02/27/2007 (jrainy) +*/ +/********************************************************************************F*/ +static int8_t _NetGameDistServDrop(void *pLinkRef, void *pExisting, void *pIncoming, uint8_t uTypeExisting, uint8_t uTypeIncoming) +{ + return(uTypeExisting == GMDIST_DATA_INPUT_DROPPABLE); +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServHandleCRCResponses + + \Description + Handle the CRC responses. Check the current response status of all clients + + \Input *pDistServ - module state + + \Notes + Can only compare 64 different CRCs. If we have over 64 clients, the function + will still function correctly, as long as there is more than 64 different + CRCs being returned by the clients (highly unlikely). If there's too many + different CRCs received, we'll pick the one with majority from the 64 first + clients. + + \Version 03/23/2009 (jrainy) +*/ +/********************************************************************************F*/ +static void _NetGameDistServHandleCRCResponses(NetGameDistServT *pDistServ) +{ + NetGameDistServClientT *pDistClient; + int32_t iClient,iIndex; + uint8_t uWaiting = FALSE; + uint8_t uTied = FALSE; + int32_t iCRCs[64] = {0}; + int32_t iOccurences[64] = {0}; + int32_t iBestCRC = 0; + int32_t iBestOccurences = 0; + + // for all the current clients + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + // skip uninitialized/disconnected clients + if (!pDistClient->bInitialized || pDistClient->bDisconnected) + { + continue; + } + + // mark us waiting if this client didn't send its CRC yet + if (!pDistClient->uCRCValid) + { + uWaiting = TRUE; + } + else + { + // For all the CRC seen already + for(iIndex = 0; iIndex < 64; iIndex++) + { + // count the repeats + if (pDistClient->iCRC == iCRCs[iIndex]) + { + iOccurences[iIndex]++; + + if (iOccurences[iIndex] > iBestOccurences) + { + iBestOccurences = iOccurences[iIndex]; + iBestCRC = iCRCs[iIndex]; + } + + break; + } + else if (iOccurences[iIndex] == 0) + { + // remember the newly seen CRCs + iCRCs[iIndex] = pDistClient->iCRC; + iOccurences[iIndex] = 1; + + if (iOccurences[iIndex] > iBestOccurences) + { + iBestOccurences = iOccurences[iIndex]; + iBestCRC = iCRCs[iIndex]; + } + + break; + } + } + } + } + + // if the wait is over, (received all responses, or number of frames) + if (!uWaiting || (pDistServ->uCRCResponseCountdown == 0)) + { + for(iIndex = 0; iIndex < 64; iIndex++) + { + if ((iOccurences[iIndex] == iBestOccurences) && (iCRCs[iIndex] != iBestCRC)) + { + uTied = TRUE; + } + } + + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + // skip disconnected clients + if (!pDistClient->bInitialized || pDistClient->bDisconnected) + { + continue; + } + + if (!pDistClient->uCRCValid || (pDistClient->iCRC != iBestCRC) || uTied) + { + _NetGameDistServLogPrintf(pDistServ, "client %d failed CRC challenge: %d %d %d %d\n", iClient, pDistClient->uCRCValid, pDistClient->iCRC, iBestCRC, uTied); + + if (uTied) + { + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_DESYNCED_ALL_PLAYERS); + } + else + { + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_DESYNCED); + } + } + else + { + _NetGameDistServLogPrintfVerbose(pDistServ, 2, "client %d passed CRC challenge: %d\n", iClient, pDistClient->iCRC); + } + + pDistClient->uCRCValid = FALSE; + pDistClient->iCRC = 0; + } + + // go back to challenge mode + pDistServ->uCRCResponseCountdown = 0; + pDistServ->uCRCRemainingFrames = pDistServ->uCRCRate; + } +} + +/*F********************************************************************************/ +/*! + \Function _NetGameDistServPrepareInputs + + \Description + Prepare the inputs for sending a multipacket + + \Input *pDistServ - module state + \Input **pInputs - array of pointers to set to data buffers + \Input *pInputSizes - array of sizes to set + \Input *pInputTypes - array of types to set + \Input *pInputUsed - array of booleans, indicates whether a given player's input was used + \Input *pInputDropped - array of booleans, indicates whether a given player's input was dropped + + \Output + uint8_t - indicates whether any data was available and prepared + + \Version 09/22/2009 (jrainy) +*/ +/********************************************************************************F*/ +static uint8_t _NetGameDistServPrepareInputs(NetGameDistServT *pDistServ, void** pInputs, int32_t* pInputSizes, uint8_t* pInputTypes, uint8_t* pInputUsed, uint8_t* pInputDropped) +{ + int32_t iClient, iIndex; + int32_t iRunningLength, iRunningLengthDroppable; + int32_t iAllowed, iAllowedDroppable; + uint8_t bDataAvailable; + + NetGameDistServClientT *pDistClient; + + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pInputSizes[iClient] = 0; + pInputTypes[iClient] = GMDIST_DATA_NONE; + pInputUsed[iClient] = FALSE; + } + + iRunningLength = 0; + iRunningLengthDroppable = 0; + + for (iIndex = 0, bDataAvailable = FALSE; iIndex < pDistServ->ClientList.iMaxClients; iIndex++) + { + iClient = (iIndex + pDistServ->iFirstClient) % pDistServ->ClientList.iMaxClients; + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + if (pDistClient->bLocalInput) + { + if (pDistClient->iLocalInputType == GMDIST_DATA_INPUT_DROPPABLE) + { + iRunningLengthDroppable += pDistClient->iLocalInputSize; + } + else + { + if (iRunningLength + pDistClient->iLocalInputSize < DISTSERV_MAX_MULTIPACKET_LENGTH) + { + iRunningLength += pDistClient->iLocalInputSize; + } + } + } + } + + iAllowed = iRunningLength; + iAllowedDroppable = DISTSERV_MAX_MULTIPACKET_LENGTH - iAllowed; + iRunningLength = 0; + iRunningLengthDroppable = 0; + + for (iIndex = 0, bDataAvailable = FALSE; iIndex < pDistServ->ClientList.iMaxClients; iIndex++) + { + iClient = (iIndex + pDistServ->iFirstClient) % pDistServ->ClientList.iMaxClients; + pDistClient = &pDistServ->ClientList.Clients[iClient]; + pInputDropped[iClient] = FALSE; + + pInputs[iClient] = pDistClient->aLocalInput; + if (!pDistClient->bInitialized || pDistClient->bDisconnected) + { + pInputSizes[iClient] = 0; + pInputTypes[iClient] = GMDIST_DATA_DISCONNECT; + } + else if (pDistClient->bLocalInput) + { + if (pDistClient->iLocalInputType == GMDIST_DATA_INPUT_DROPPABLE) + { + iRunningLengthDroppable += pDistClient->iLocalInputSize; + if (iRunningLengthDroppable > iAllowedDroppable) + { + pInputDropped[iClient] = TRUE; + + _NetGameDistServLogPrintfVerbose(pDistServ, 1, "dropping packet for iClient %d, iIndex %d\n", iClient, iIndex); + continue; + } + } + else + { + iRunningLength += pDistClient->iLocalInputSize; + if (iRunningLength > iAllowed) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 1, "delaying packet for iClient %d, iIndex %d\n", iClient, iIndex); + continue; + } + } + + pInputSizes[iClient] = pDistClient->iLocalInputSize; + pInputTypes[iClient] = pDistClient->iLocalInputType; + bDataAvailable = TRUE; + } + + pInputUsed[iClient] = TRUE; + + pDistClient->iCount += pDistClient->iPeekKey; + } + + return(bDataAvailable); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function NetGameDistServCreate + + \Description + Create the NetGameDistServ module. + + \Input iMaxClients - maximum number of clients supported + \Input iVerbosity - debug verbose level + + \Output + NetGameDistServT * - module state, or NULL if create failed + + \Version 02/01/2007 (jbrookes) +*/ +/********************************************************************************F*/ +NetGameDistServT *NetGameDistServCreate(int32_t iMaxClients, int32_t iVerbosity) +{ + NetGameDistServT *pDistServ; + int32_t iMemSize; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // calculate memory size based on number of clients + iMemSize = sizeof(*pDistServ) + (sizeof(pDistServ->ClientList.Clients[0]) * (iMaxClients - 1)); + + // allocate and init module state + if ((pDistServ = DirtyMemAlloc(iMemSize, NETGAMEDISTSERV_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + _NetGameDistServLogPrintf(NULL, "could not allocate module state\n"); + return(NULL); + } + ds_memclr(pDistServ, iMemSize); + pDistServ->iMemGroup = iMemGroup; + pDistServ->pMemGroupUserData = pMemGroupUserData; + + // init other module state + pDistServ->ClientList.iMaxClients = iMaxClients; + pDistServ->iVerbosity = iVerbosity; + pDistServ->uSendThreshold = 4; + pDistServ->uLastFlowUpdateTime = NetTick(); + pDistServ->uCRCResponseCountdown = 0; + pDistServ->uCRCRemainingFrames = pDistServ->uCRCRate; + pDistServ->iFixedRate = DISTSERV_DEFAULT_FIXEDRATE; + + // return module ref to caller + return(pDistServ); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServDestroy + + \Description + Destroy the NetGameDistServ module. + + \Input *pDistServ - module state + + \Version 02/01/2007 (jbrookes) +*/ +/********************************************************************************F*/ +void NetGameDistServDestroy(NetGameDistServT *pDistServ) +{ + DirtyMemFree(pDistServ, NETGAMEDISTSERV_MEMID, pDistServ->iMemGroup, pDistServ->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServAddClient + + \Description + Add a client to the client list + + \Input *pDistServ - module state + \Input iClient - index of slot to add client to + \Input *pLinkRef - link ref to create dist with + \Input *pClientName - name of client + + \Output + int32_t - negative=error, else success + + \Version 02/05/2007 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetGameDistServAddClient(NetGameDistServT *pDistServ, int32_t iClient, NetGameLinkRefT *pLinkRef, const char *pClientName) +{ + // ref given client index + NetGameDistServClientT *pDistClient = &pDistServ->ClientList.Clients[iClient]; + + // make sure this slot isn't already taken + if (pDistClient->bInitialized && !pDistClient->bDisconnected) + { + _NetGameDistServLogPrintf(pDistServ, "skipping add of client to slot %d when slot is not empty\n", iClient); + return(-1); + } + + // save info and mark as initialized + ds_memclr(pDistClient, sizeof(*pDistClient)); + ds_strnzcpy(pDistClient->strName, pClientName, sizeof(pDistClient->strName)); + pDistClient->bInitialized = TRUE; + + // create dist ref for client + _NetGameDistServLogPrintfVerbose(pDistServ, 0, "creating dist ref for client %s\n", pDistClient->strName); + DirtyMemGroupEnter(pDistServ->iMemGroup, pDistServ->pMemGroupUserData); + pDistClient->pGameDist = NetGameDistCreate(pLinkRef, + (NetGameDistStatProc *)NetGameLinkStatus, + (NetGameDistSendProc *)NetGameLinkSend, + (NetGameDistRecvProc *)NetGameLinkRecv, + GMDIST_DEFAULT_BUFFERSIZE_IN * 20, + GMDIST_DEFAULT_BUFFERSIZE_OUT * 20); + DirtyMemGroupLeave(); + + NetGameDistSetServer(pDistClient->pGameDist, TRUE); + NetGameDistSetProc(pDistClient->pGameDist, 'drop', (void *)_NetGameDistServDrop); + + // update client count + pDistServ->ClientList.iNumClients++; + + // this call is required for dirtycast to announce "no longer receiving" upon join-in-progress + // DirtyCast will reenter the "receiving" state when the joiner explicitly uses NetGameDistControl('lrcv') + _NetGameDistServFlowControl(pDistServ); + + // update dist multi configuration + _NetGameDistUpdateMulti(pDistServ); + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServDelClient + + \Description + Delete a client from the client list + + \Input *pDistServ - module state + \Input iClient - index of slot to delete client from + + \Output + int32_t - negative=error, else success + + \Version 02/05/2007 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetGameDistServDelClient(NetGameDistServT *pDistServ, int32_t iClient) +{ + // ref given client index + NetGameDistServClientT *pDistClient; + + // make sure this is a valid index + if (iClient >= pDistServ->ClientList.iMaxClients) + { + _NetGameDistServLogPrintf(pDistServ, "skipping delete of client %d not in dist list\n", iClient); + return(-1); + } + + // ref client + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + // disconnect from client + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_DELETED); + + // clear slot in client list + ds_memclr(pDistClient, sizeof(*pDistClient)); + + // update client count + pDistServ->ClientList.iNumClients--; + + // this used to be done only while game was not started + // with meta-information, we can now do it at any time + _NetGameDistUpdateMulti(pDistServ); + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServDiscClient + + \Description + Mark the specified client as disconnect. This will prevent sends and update + + \Input *pDistServ - module state + \Input iClient - index of slot holding client to update + + \Output + int32_t - negative=error, else success + + \Version 03/15/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t NetGameDistServDiscClient(NetGameDistServT *pDistServ, int32_t iClient) +{ + // make sure this is a valid index + if (iClient >= pDistServ->ClientList.iMaxClients) + { + _NetGameDistServLogPrintf(pDistServ, "skipping disc of client %d not in dist list\n", iClient); + return(-1); + } + + // destroy connection refs and mark as disconnected + _NetGameDistServDiscClient(pDistServ, &pDistServ->ClientList.Clients[iClient], iClient, GMDIST_DISCONNECTED); + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServUpdateClient + + \Description + Update the Dist ref for the specified client. + + \Input *pDistServ - module state + \Input iClient - index of slot holding client to update + + \Output + int32_t - negative=disconnected, else zero + + \Version 02/05/2007 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t NetGameDistServUpdateClient(NetGameDistServT *pDistServ, int32_t iClient) +{ + NetGameDistServClientT *pDistClient = &pDistServ->ClientList.Clients[iClient]; + int32_t iDistErr=0; + + // don't update uninitialized slots + if (!pDistClient->bInitialized) + { + return(0); + } + + // don't update disconnected clients + if (pDistClient->bDisconnected) + { + return(pDistClient->iDisconnectReason ? pDistClient->iDisconnectReason : -1); + } + + // update client's dist ref + NetGameDistUpdate(pDistClient->pGameDist); + + // check for error + iDistErr = NetGameDistGetError(pDistClient->pGameDist); + if (iDistErr != 0) + { + _NetGameDistServLogPrintf(pDistServ, "NetGameDistGetError() returned %d\n", iDistErr); + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, iDistErr); + return(iDistErr); + } + + // return status + return(0); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServUpdate + + \Description + Update the DistServ module. This function is expected to be called at the + desired output rate. + + \Input *pDistServ - module state + + \Version 02/12/2007 (jrainy) +*/ +/********************************************************************************F*/ +void NetGameDistServUpdate(NetGameDistServT *pDistServ) +{ + NetGameDistServClientT *pDistClient; + int32_t iClient, iResult; + int32_t aInputSizes[GMDIST_MAX_CLIENTS]; + uint8_t aInputTypes[GMDIST_MAX_CLIENTS]; + uint8_t aInputUsed[GMDIST_MAX_CLIENTS]; + uint8_t aInputDropped[GMDIST_MAX_CLIENTS]; + NetGameDistStatT aStats[GMDIST_MAX_CLIENTS]; + NetGameLinkStatT Stat; + uint8_t bDataAvailable; + uint8_t bRemoteRcv, bRemoteSnd; + uint8_t uCRCevent = FALSE; + uint32_t uSendThreshold; + void *pInputs[GMDIST_MAX_CLIENTS]; + + int32_t iPeekLength[GMDIST_MAX_CLIENTS]; + int32_t iPeekResult[GMDIST_MAX_CLIENTS] = {0}; + uint8_t iType[GMDIST_MAX_CLIENTS]; + static char aLocalInput[GMDIST_MAX_CLIENTS][NETGAME_DATAPKT_MAXSIZE]; + uint8_t uNewFlow[GMDIST_MAX_CLIENTS]; + uint32_t uNumClientsInitConn = 0; + uint32_t uNow = NetTick(); + uint8_t bPrevNoInputMode; //!< previous no input mode + + // every 100 sends, send stats to clients + if ((pDistServ->uLastStatsUpdate == 0) || (NetTickDiff(uNow, pDistServ->uLastStatsUpdate) > 2000)) + { + pDistServ->uLastStatsUpdate = uNow; + + // gather stats for all connected clients + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + if (pDistClient->bInitialized && !pDistClient->bDisconnected) + { + NetGameDistStatus(pDistClient->pGameDist, 'stat', 0, &Stat, sizeof(NetGameLinkStatT)); + aStats[iClient].late = SocketHtons(Stat.late); + aStats[iClient].bps = SocketHtons(Stat.outbps); + aStats[iClient].pps = (uint8_t)Stat.outpps; + aStats[iClient].slen = (uint8_t)NetGameDistStatus(pDistClient->pGameDist, 'slen', 0, NULL, 0); + aStats[iClient].naksent = (uint8_t)(Stat.lnaksent - pDistClient->uNakSent); + aStats[iClient].plost = (uint8_t)(Stat.lpacklost - pDistClient->uPackLost); + pDistClient->uNakSent = Stat.lnaksent; + pDistClient->uPackLost = Stat.lpacklost; + } + else + { + ds_memclr(&aStats[iClient], sizeof(aStats[0])); + } + } + + // broadcast updated stat info to all connected clients + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + if(pDistClient->bInitialized && !pDistClient->bDisconnected) + { + NetGameDistSendStats(pDistClient->pGameDist, aStats); + } + } + } + + #if MONITOR_HIGHWATER + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + int32_t iRet; + + pDistClient = &pDistServ->ClientList.Clients[iClient]; + if(pDistClient->bInitialized && !pDistClient->bDisconnected) + { + iRet = GMDIST_Modulo(NetGameDistStatus(pDistClient->pGameDist, '?inp', 0, NULL, 0) - NetGameDistStatus(pDistClient->pGameDist, '?cmp', 0, NULL, 0), NetGameDistStatus(NULL, 'pwin', 0, NULL, 0)); + if (iRet > pDistServ->iHighWaterInputQueue) + { + pDistServ->iHighWaterInputQueue = iRet; + pDistServ->uHighWaterChanged = TRUE; + } + + iRet = GMDIST_Modulo(NetGameDistStatus(pDistClient->pGameDist, '?out', 0, NULL, 0) - NetGameDistStatus(pDistClient->pGameDist, '?snd', 0, NULL, 0), NetGameDistStatus(NULL, 'pwin', 0, NULL, 0)); + if (iRet > pDistServ->iHighWaterOutputQueue) + { + pDistServ->iHighWaterOutputQueue = iRet; + pDistServ->uHighWaterChanged = TRUE; + } + } + } + #endif + + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + if (pDistClient->bInitialized && !pDistClient->bDisconnected) + { + if (!pDistClient->bLocalInput) + { + int32_t iLength = sizeof(pDistClient->aLocalInput); + // check for incoming data on this dist + iResult = NetGameDistInputPeek(pDistClient->pGameDist, &pDistClient->iLocalInputType, pDistClient->aLocalInput, &iLength); + pDistClient->bReallyPeeked = TRUE; + _NetGameDistServSanityCheckIn(pDistServ, iClient, iResult); + + if (iResult > 0) + { + pDistClient->iLocalInputSize = iLength; + pDistClient->bLocalInput = TRUE; + pDistClient->iPeekKey = iResult; + } + else if (iResult < 0) + { + _NetGameDistServLogPrintf(pDistServ, "peek error for client %d\n", iClient); + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_PEEK_ERROR); + continue; + } + } + + bRemoteRcv = NetGameDistStatus(pDistClient->pGameDist,'rrcv', 0, NULL, 0); + bRemoteSnd = NetGameDistStatus(pDistClient->pGameDist,'rsnd', 0, NULL, 0); + + if ((bRemoteRcv != pDistClient->bRemoteRcv) || (bRemoteSnd != pDistClient->bRemoteSnd)) + { + pDistClient->bRemoteRcv = bRemoteRcv; + pDistClient->bRemoteSnd = bRemoteSnd; + + _NetGameDistServLogPrintf(pDistServ, "client %d got GAME_PACKET_INPUT_FLOW (send %d, recv %d)\n", iClient, pDistClient->bRemoteSnd, pDistClient->bRemoteRcv); + _NetGameDistServFlowControl(pDistServ); + pDistServ->uLastFlowUpdateTime = NetTick(); + } + } + } + + bDataAvailable = _NetGameDistServPrepareInputs(pDistServ, pInputs, aInputSizes, aInputTypes, aInputUsed, aInputDropped); + + if (!bDataAvailable) + { + pDistServ->uNbFramesNoData++; + } + else + { + pDistServ->uNbFramesNoData = 0; + } + + uSendThreshold = pDistServ->uSendThreshold; + + if (!pDistServ->bFlowEnabled) + { + uSendThreshold = 1; + } + + // don't send if there was no data available for a while + if ((uSendThreshold == 0) || (pDistServ->uNbFramesNoData < uSendThreshold)) + { + if (pDistServ->uCRCRemainingFrames) + { + if (pDistServ->bFlowEnabled) + { + pDistServ->uCRCRemainingFrames--; + } + if (pDistServ->uCRCRemainingFrames == 0) + { + int32_t iClientCount = 0; + + // if we happen to send to only one client, below, + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + if (pDistClient->bInitialized && (!pDistClient->bDisconnected)) + { + iClientCount++; + } + } + + if (iClientCount <= 1) + { + // don't send CRC request to single players + pDistServ->uCRCRemainingFrames = pDistServ->uCRCRate; + pDistServ->uCRCResponseCountdown = 0; + } + else + { + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + aInputTypes[iClient] |= GMDIST_DATA_CRC_REQUEST; + _NetGameDistServLogPrintfVerbose(pDistServ, 2, "setting GMDIST_DATA_CRC_REQUEST in aInputTypes[%d]\n", iClient); + } + + pDistServ->uCRCResponseCountdown = pDistServ->uCRCResponseLimit; + } + } + } + + bPrevNoInputMode = pDistServ->bNoInputMode; + pDistServ->bNoInputMode = FALSE; + + // scan through client list, and queue all inputs for sending + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + uNewFlow[iClient] = FALSE; + + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + // skip disconnected clients + if (!pDistClient->bInitialized || pDistClient->bDisconnected) + { + continue; + } + + uNumClientsInitConn++; + + if (pDistServ->uCRCResponseCountdown) + { + if (NetGameDistStatus(pDistClient->pGameDist, 'rcrc', 0, NULL, 0)) + { + pDistClient->iCRC = NetGameDistStatus(pDistClient->pGameDist, 'rcrc', 1, NULL, 0); + pDistClient->uCRCValid = TRUE; + + _NetGameDistServLogPrintfVerbose(pDistServ, 2, "got CRC response %d back from client %d\n", pDistClient->iCRC, iClient); + + uCRCevent = TRUE; + } + } + + if (!pDistClient->bReallyPeeked) + { + iPeekLength[iClient] = sizeof(aLocalInput[iClient]); + iPeekResult[iClient] = NetGameDistInputPeek(pDistClient->pGameDist, &(iType[iClient]), &(aLocalInput[iClient]), &(iPeekLength[iClient])); + + if (iPeekResult[iClient] > 0) + { + uNewFlow[iClient] = TRUE; + } + } + + pDistClient->bReallyPeeked = FALSE; + + // queue input into client + if ((iResult = NetGameDistInputLocalMulti(pDistClient->pGameDist, aInputTypes, pInputs, aInputSizes, pDistClient->iPeekKey)) == 1) + { + pDistServ->uOutputMultiPacketCount++; + _NetGameDistServLogPrintfVerbose(pDistServ, 2, "queued input into client %s\n", pDistClient->strName); + } + else + { + _NetGameDistServLogPrintf(pDistServ, "NetGameDistInputLocal() failed for client %s (err=%d)\n", pDistClient->strName, iResult); + + if (iResult == GMDIST_INVALID) + { + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_INPUTLOCAL_FAILED_INVALID); + } + else if (iResult == GMDIST_OVERFLOW_MULTI) + { + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_INPUTLOCAL_FAILED_MULTI); + } + else if (iResult == GMDIST_OVERFLOW_WINDOW) + { + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_INPUTLOCAL_FAILED_WINDOW); + } + else + { + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_INPUTLOCAL_FAILED); + } + continue; + } + + if (aInputUsed[iClient] || aInputDropped[iClient]) + { + pDistClient->bLocalInput = FALSE; + pDistClient->iPeekKey = 0; + } + + // sanity check output + _NetGameDistServSanityCheckOut(pDistServ, iClient, iResult); + + // advance the state + if ((iResult = NetGameDistInputQueryMulti(pDistClient->pGameDist, NULL, NULL, NULL)) > 0) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 2, "sent input %d\n", iResult); + } + else if (iResult) + { + _NetGameDistServLogPrintf(pDistServ, "NetGameDistInputQueryMulti() failed (err=%d)\n", iResult); + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_INPUTQUERY_FAILED); + continue; + } + + if (uNewFlow[iClient] && !aInputUsed[iClient]) + { + _NetGameDistServLogPrintf(pDistServ, "we're getting behind on large packet delaying for client %d\n", iClient); + _NetGameDistServDiscClient(pDistServ, pDistClient, iClient, GMDIST_INPUTQUERY_FAILED); + continue; + } + } + + /* If the client count changed, flag the boolean used to remember that it happened at least once + during the metrics sampling interval. */ + if (pDistServ->uCurStatClientCount != uNumClientsInitConn) + { + if (pDistServ->bClientCountChanged == FALSE) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 1, "metric sampling interval marked invalid for idpps, odmpps and inputdrop because client count changed (client count = %d)\n", pDistServ->uCurStatClientCount); + } + pDistServ->bClientCountChanged = TRUE; + pDistServ->uCurStatClientCount = uNumClientsInitConn; + } + + for (iClient = 0; iClient < pDistServ->ClientList.iMaxClients; iClient++) + { + pDistClient = &pDistServ->ClientList.Clients[iClient]; + + if (uNewFlow[iClient]) + { + pDistClient->iLocalInputType = iType[iClient]; + pDistClient->iLocalInputSize = iPeekLength[iClient]; + pDistClient->iPeekKey = iPeekResult[iClient]; + pDistClient->bLocalInput = TRUE; + + ds_memcpy_s(pDistClient->aLocalInput, sizeof(pDistClient->aLocalInput), aLocalInput[iClient], sizeof(aLocalInput[iClient])); + + _NetGameDistServSanityCheckIn(pDistServ, iClient, iPeekResult[iClient]); + } + } + + // if something occured, or we have a active countdown + if (pDistServ->uCRCResponseCountdown || uCRCevent) + { + // countdown + if (pDistServ->bFlowEnabled && pDistServ->uCRCResponseCountdown) + { + pDistServ->uCRCResponseCountdown--; + } + + // if something occured or we just reached zero. + if (uCRCevent || (pDistServ->uCRCResponseCountdown == 0)) + { + _NetGameDistServHandleCRCResponses(pDistServ); + } + } + } + else + { + bPrevNoInputMode = pDistServ->bNoInputMode; + pDistServ->bNoInputMode = TRUE; + } + + /* If 'no input mode' changed, flag the boolean used to remember that it happened at least once + during the metrics sampling interval. */ + if (bPrevNoInputMode != pDistServ->bNoInputMode) + { + if (pDistServ->bNoInputModeChanged == FALSE) + { + _NetGameDistServLogPrintfVerbose(pDistServ, 1, "metric sampling interval marked invalid for odmpps because no input mode changed (no input mode = %d)\n", pDistServ->bNoInputMode); + } + pDistServ->bNoInputModeChanged = TRUE; + } + + // $$ jrainy -- fixed up the below code to work without collapsing but introduced a very slight + // side-effect if we have a list with a big empty area (Alice, [Empty], [Empty], [Empty], [Empty], Bob) + // Bob will be able to get his oversized packets through more easily than Alice. + // $$ TODO: A better change would be to increment iFirstclient to the next allocated/initialized + // spot instead of by just 1. (minor for now) + if (pDistServ->ClientList.iMaxClients != 0) + { + pDistServ->iFirstClient = (pDistServ->iFirstClient + 1) % pDistServ->ClientList.iMaxClients; + } +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServHighWaterChanged + + \Description + Return whether the highwater mark changed, and the current highwater values. + + \Input *pDistServ - module state + \Input pHighWaterInputQueue - input queue + \Input pHighWaterOutputQueue - output queue + + \Output + uint8_t - whether the highwater mark changed since last call + + \Version 06/05/2008 (jrainy) +*/ +/********************************************************************************F*/ +uint8_t NetGameDistServHighWaterChanged(NetGameDistServT *pDistServ, int32_t* pHighWaterInputQueue, int32_t* pHighWaterOutputQueue) +{ + #if MONITOR_HIGHWATER + uint8_t bChanged = pDistServ->uHighWaterChanged; + + if (pHighWaterInputQueue != NULL) + { + *pHighWaterInputQueue = pDistServ->iHighWaterInputQueue; + } + if (pHighWaterOutputQueue != NULL) + { + *pHighWaterOutputQueue = pDistServ->iHighWaterOutputQueue; + } + + pDistServ->uHighWaterChanged = FALSE; + return(bChanged); + #else + return(FALSE); + #endif +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServExplainError + + \Description + return the lastest error reported by netgamedist, for client iClient + + \Input *pDistServ - module state + \Input iClient - client to get the error for. + + \Output + char * - the latest netgamedist error + + \Version 08/15/2008 (jrainy) +*/ +/********************************************************************************F*/ +char* NetGameDistServExplainError(NetGameDistServT *pDistServ, int32_t iClient) +{ + return(pDistServ->ClientList.Clients[iClient].strDiscReason); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServControl + + \Description + Control behavior of module. + + \Input *pDistServ - pointer to module state + \Input iControl - status selector + \Input iValue - control value + \Input iValue2 - control value + \Input *pValue - control value + + \Notes + iControl can be one of the following: + + \verbatim + 'crcc' - set the CRC challenge send rate in number of frames (0 to disable) + 'crcr' - set the CRC max response times in number of frames (0 makes it infinite) + 'mpty' - set the number(iValue) of empty frames needed to pause traffic + 'rate' - set the fixed rate the server has been configured to run at via iValue + 'rsta' - reset stat variables (flow control/no input mode bool) + \endverbatim + + \Version 03/17/2009 (jrainy) +*/ +/********************************************************************************F*/ +int32_t NetGameDistServControl(NetGameDistServT *pDistServ, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'crcc') + { + if ((uint32_t)iValue != pDistServ->uCRCRate) + { + //set the rate + pDistServ->uCRCRate = iValue; + + //start counting down + pDistServ->uCRCRemainingFrames = iValue; + _NetGameDistServLogPrintf(pDistServ, "setting CRC rate to %d\n", iValue); + + } + return(0); + } + if (iControl == 'crcr') + { + pDistServ->uCRCResponseLimit = iValue; + return(0); + } + if (iControl == 'mpty') + { + pDistServ->uSendThreshold = iValue; + return(0); + } + if (iControl == 'rate') + { + pDistServ->iFixedRate = iValue; + return(0); + } + if (iControl == 'rsta') + { + pDistServ->bClientCountChanged = FALSE; + pDistServ->bFlowEnabledChanged = FALSE; + pDistServ->bNoInputModeChanged = FALSE; + + return(0); + } + + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServStatus2 + + \Description + Get status information. + + \Input *pDistServ - pointer to module state + \Input iSelect - status selector + \Input iValue - selector specific + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'clnu' - returns the current client count used for stat (note this not the same as iNumClients in client list) + 'drop' - returns total dropped input packet count in pValue (uint32_t) for client index in iValue, the value is invalid if GameServerDistStatus() return -1; + 'ftim' - flow time. Time (ms) since the last flow control packet was received. + 'icnt' - returns total input dist packet count in pValue (uint32_t) for client index in iValue, the value is invalid if GameServerDistStatus() return -1; + 'ninp' - whether we are currently in the mode where no inputs are sent due to stall in input arrival + 'ocnt' - reutrns output dist multi packet total in pValue (uint32_t), the value is invalid if GameServerDistStatus() return -1; + \endverbatim + + \Version 09/18/2019 (tcho) +*/ +/********************************************************************************F*/ +int32_t NetGameDistServStatus2(NetGameDistServT *pDistServ, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + // total number of clients counted in the update loop + if (iSelect == 'clnu') + { + return(pDistServ->uCurStatClientCount); + } + + // total input packet dropped count for a client or total input dist packet count per client + if ((iSelect == 'drop') || (iSelect == 'icnt')) + { + uint32_t uInputDropCount; + uint32_t uInputCount; + NetGameDistServClientT *pDistClient; + + if ((iValue < 0) || (iValue >= pDistServ->ClientList.iMaxClients)) + { + // invalid index + return(-1); + } + + if ((pBuf == NULL) || (iBufSize < (int32_t)sizeof(uint32_t))) + { + // invalid output parameter + return(-2); + } + + pDistClient = &pDistServ->ClientList.Clients[iValue]; + + // only report a value if the client is initialized and connected + if (pDistClient->bInitialized && !pDistClient->bDisconnected) + { + if (iSelect == 'drop') + { + uInputDropCount = NetGameDistStatus(pDistClient->pGameDist, 'drop', 0, NULL, 0); + ds_memcpy(pBuf, &uInputDropCount, sizeof(uint32_t)); + } + + if (iSelect == 'icnt') + { + uInputCount = NetGameDistStatus(pDistClient->pGameDist, 'icnt', 0, NULL, 0); + ds_memcpy(pBuf, &uInputCount, sizeof(uint32_t)); + } + + if ((pDistServ->bClientCountChanged == TRUE) || (pDistServ->bFlowEnabledChanged == TRUE) || (pDistServ->bFlowEnabled == FALSE)) + { + /* value copied in pBuf is valid but sampling interval should be ignored because conditions are not met + to properly calculate per-client rate of drops or per-client rate of inbound inputs. */ + return(1); + } + + return(0); + } + else + { + ds_memclr(pBuf, iBufSize); + return(-3); + } + } + + if (iSelect == 'ftim') + { + return(NetTickDiff(NetTick(), pDistServ->uLastFlowUpdateTime)); + } + + if (iSelect == 'ninp') + { + return(pDistServ->bNoInputMode); + } + + // total output multi dist count per server + if (iSelect == 'ocnt') + { + if ((iValue < 0) || (iValue >= pDistServ->ClientList.iMaxClients)) + { + // invalid index + return(-1); + } + + if ((pBuf == NULL) || (iBufSize < (int32_t)sizeof(uint32_t))) + { + // invalid output parameter + return(-2); + } + + ds_memcpy(pBuf, &pDistServ->uOutputMultiPacketCount, sizeof(uint32_t)); + + if ((pDistServ->uCurStatClientCount == 0) || (pDistServ->bClientCountChanged == TRUE) || (pDistServ->bNoInputModeChanged == TRUE) || (pDistServ->bNoInputMode == TRUE)) + { + /* value copied in pBuf is valid but sampling interval should be ignored because conditions are not met + to properly calculate rate of outbound multipackets */ + return(1); + } + + return(0); + } + + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServStatus + + \Description + Get status information. + + \Input *pDistServ - pointer to module state + \Input iSelect - status selector + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + falls through to NetGameDistServStatus2() + + \Version 24/09/2012 (jrainy) first version +*/ +/********************************************************************************F*/ +int32_t NetGameDistServStatus(NetGameDistServT *pDistServ, int32_t iSelect, void *pBuf, int32_t iBufSize) +{ + return(NetGameDistServStatus2(pDistServ, iSelect, 0, pBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function NetGameDistServSetLoggingCallback + + \Description + Set the logging callback + + \Input *pDistServ - pointer to module state + \Input *pLoggingCb - logging callback + \Input *pUserData - logging userdata + + \Version 06/26/2019 (eesponda) +*/ +/********************************************************************************F*/ +void NetGameDistServSetLoggingCallback(NetGameDistServT *pDistServ, NetGameDistServLoggingCbT *pLoggingCb, void *pUserData) +{ + pDistServ->pLoggingCb = pLoggingCb; + pDistServ->pUserData = pUserData; +} diff --git a/r5dev/thirdparty/dirtysdk/source/game/netgamelink.c b/r5dev/thirdparty/dirtysdk/source/game/netgamelink.c new file mode 100644 index 00000000..247aa0b2 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/game/netgamelink.c @@ -0,0 +1,2193 @@ +/*H*************************************************************************************************/ +/*! + + \File netgamelink.c + + \Description + This module provides a packet layer peer-peer interface which utilizes + a lower layer "comm" module. This module is used either by netgamedist + (if it is being used) or can be accessed directly if custom online game + logic is being used. This module also calculates latency (ping) and + maintains statistics about the connection (number of bytes send/received). + The direct application interface is currently weak but will be improved. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 12/19/00 (GWS) First Version + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include /* memset/ds_memcpy */ + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/game/netgamepkt.h" +#include "DirtySDK/game/netgamelink.h" +#include "DirtySDK/comm/commall.h" + +/*** Defines ***************************************************************************/ + +#define NETGAMELINK_KEEPALIVE_TIME (500) + +//! min/max values for NetGameLink QoS settings +#define NETGAMELINK_QOS_DURATION_MIN (0) +#define NETGAMELINK_QOS_DURATION_MAX (10000) +#define NETGAMELINK_QOS_INTERVAL_MIN (10) +#define NETGAMELINK_QOS_PACKETSIZE_MIN (50) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! netgamelink internal state +struct NetGameLinkRefT +{ + //! port used to communicate with server + CommRef *pPort; + //! flag to indicate if we own port (if we need to destroy when done) + int32_t iPortOwner; + + //! module memory group + int32_t iMemGroup; + void *pMemGroupUserData; + + //! tick at which last packet was sent to server (in ms) + uint32_t uLastSend; + //! tick at which last sync packet was received from server (in ms) + uint32_t uLastRecv; + //! tick at which last sync packet contributed to latency average calculation + uint32_t uLastAvg; + //! last echo requested by server (used for rtt calc) + uint32_t uLastEcho; + //! last time a sync packet was sent + uint32_t uLastSync; + //! last time the history slot changed + uint32_t uLastHist; + + //! the time weighted rtt average + int32_t iRttAvg; + //! the time weighted rtt deviation + int32_t iRttDev; + + //! external status monitoring + NetGameLinkStatT NetGameLinkStats; + + //! stats over a one second period + int32_t iInpBytes; //!< input bytes + int32_t iInpPackets; //!< input packets + int32_t iInpRawBytes; //!< input raw bytes + int32_t iInpRawPackets; //!< input raw packets + int32_t iOverhead; //!< send overhead + int32_t iRcvOverhead; //!< recv overhead + int32_t iRcvRawBytes; //!< recv raw bytes + int32_t iRcvRawPackets; //!< recv raw packet (commudp recvs) + int32_t iRcvPackets; //!< recv packets + int32_t iRcvBytes; //!< recv bytes + + // track packets sent/received for sending to peer in sync packets + + //! stats - raw packets sent + int32_t iPackSent; + //! stats - raw packets received + int32_t iPackRcvd; + //! stats - remote->local packets lost + int32_t iR2LPackLost; + //! stats - naks sent + int32_t iNakSent; + + //! point to receive buffer + char *pBuffer; + //! length of data in receive buffer + int32_t iBufLen; + //! size of receive buffer + int32_t iBufMax; + + //! protect resources + NetCritT Crit; + //! count of missed accessed + int32_t iProcess; + + //! data for m_callproc + void *pCallData; + //! callback for incoming packet notification + void (*pCallProc)(void *pCalldata, int32_t iKind); + //! callback pending + int32_t iCallPending; + + //! count calls to NetGameLinkRecv() to limit callback access + int32_t iRecvProcCnt; + + //! send enable/disable + int32_t bSendEnabled; + + //! sync enable/disable + int32_t bSyncEnabled; + + //! list of active streams + NetGameLinkStreamT *m_stream; + + int32_t iStrmMaxPktSize; + + //! used for tracking QoS packets + uint32_t uQosSendInterval; + uint32_t uQosLastSendTime; + uint32_t uQosStartTime; + uint16_t uQosPacketsToSend; + uint16_t uQosPacketsToRecv; + uint16_t uQosPacketSize; + int32_t iQosCumulLate; + int32_t iQosLateCount; + int32_t iQosAvgLate; + + //! verbosity + int32_t iVerbosity; +}; + +typedef struct GameStreamT +{ + int32_t iKind; //!< block type + uint32_t uSync; //!< sync sequence number + int32_t iSent; //!< amount sent so far + int32_t iSize; //!< total block size + int32_t iSubchan; //!< subchannel index/ +} GameStreamT; + + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +// Public variables + + +/*** Private Functions *****************************************************************/ + + + +/*F*************************************************************************************/ +/*! + \Function _NetGameLinkUpdateLocalPacketLoss + + \Description + Calculate local packet loss based on a difference of remote packets sent and + local packets received. + + \Input uLPackRcvd - count of packets received locally + \Input uRPackSent - count of packets sent by remote peer (cumulative psnt + values of all received sync packets) + \Input uOldLPackLost - old count of packets lost + + \Output + uint32_t - updated count of packets lost + + \Notes + \verbatim + This function returns the number of packets lost at this end of the connection + since the beginning. It is obtained by subtracting uRPackSent (the number of + packets sent by the remote end - cumulative psnt values of all received sync + packets) from uLPackRcvd (the number of packets received locally). + + The following two scenarios indicate that a sync packet itself suffered packet + loss, and it relied on the packet resending mechanisms of commudp to finally + make it to us: + 1 - uLPackRcvd is larger than uRPackSent (subtracting them would return a + negative value) + 2 - uLPackRcvd is smaller than uRPackSent, but the last saved count of + packets lost is larger than the new one. + + For both scenarios, we skip updating the packets lost count to avoid a + possibly negative or under-valued packet loss calculation, and we deal with + that iteration as if no packet loss was detected. + + The assumption here is that the next sync packet that makes it to us without + being "resent" will end up updating the packet lost counters with a "coherent" + value as its "psnt" field will include all packets that were used for packet + retransmission. At the end, this is guaranteeing the coherency of the packets + lost count (non negative value and only increasing over time) by delaying its + update. + \endverbatim + + \Version 09/02/14 (mclouatre) +*/ +/*************************************************************************************F*/ +static uint32_t _NetGameLinkUpdateLocalPacketLoss(uint32_t uLPackRcvd, uint32_t uRPackSent, uint32_t uOldLPackLost) +{ + uint32_t uUpdatedLPackLost = uOldLPackLost; + + // only proceed if packet loss is not negative + if (uRPackSent > uLPackRcvd) + { + // only proceed if packet loss is larger than last calculated value + if ((uRPackSent - uLPackRcvd) > uOldLPackLost) + { + uUpdatedLPackLost = uRPackSent - uLPackRcvd; + } + } + + return(uUpdatedLPackLost); +} + +/*F*************************************************************************************/ +/*! + \Function _NetGameLinkGetSync + + \Description + Extract sync packet from buffer, and return a pointer to the sync packet location in the buffer + + \Input *pPacket - pointer to packet structure + \Input *pPacketData - pointer to packet data (used instead of packet buffer ref to allow split use) + \Input *pSync - [out] output buffer for extracted sync packet + + \Output + NetGamePacketSyncT * - pointer to start of sync packet in buffer, or NULL if invalid + + \Version 09/09/11 (jbrookes) +*/ +/*************************************************************************************F*/ +static NetGamePacketSyncT *_NetGameLinkGetSync(NetGamePacketT *pPacket, uint8_t *pPacketData, NetGamePacketSyncT *pSync) +{ + uint32_t uSyncSize=0; + // validate sync length byte is available + if (pPacket->head.len < 1) + { + NetPrintf(("netgamelink: received a sync packet with no data\n")); + return(NULL); + } + // validate sync size + if ((uSyncSize = (uint32_t)pPacketData[pPacket->head.len-1]) != sizeof(*pSync)) + { + NetPrintf(("netgamelink: received a sync with an invalid size (got=%d, expected=%d)\n", uSyncSize, sizeof(*pSync))); + return(NULL); + } + // validate packet is large enough to hold sync + if (pPacket->head.len < uSyncSize) + { + NetPrintf(("netgamelink: received a sync too large for packet (len=%d)\n", pPacket->head.len)); + return(NULL); + } + // locate sync at end of packet & subtract from packet length, copy to output + pPacket->head.len -= uSyncSize; + ds_memcpy(pSync, pPacketData+pPacket->head.len, uSyncSize); + // return sync packet pointer to caller + return((NetGamePacketSyncT *)(pPacketData+pPacket->head.len)); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkSendPacket + + \Description + Send a packet to the server + + \Input *pRef - reference pointer + \Input *pPacket - pointer to completed packet + \Input uCurrTick- current tick + + \Output + int32_t - bad error, zero=unable to send now, positive=sent + + \Notes + Adds timestamp, rtt and duplex information to packet + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameLinkSendPacket(NetGameLinkRefT *pRef, NetGamePacketT *pPacket, uint32_t uCurrTick) +{ + int32_t iResult; + int32_t iSynced = 0; + NetGamePacketSyncT sync; + uint32_t uPacketFlags, uPacketType; + uint32_t uPackSent=0, uPackRcvd=0, uLPktLost=0, uNakSent=0; + + // sending enabled? + if (pRef->bSendEnabled == FALSE) + { + NetPrintf(("netgamelink: warning -- trying to send packet over a sending-disabled link (%d bytes)\n", pPacket->head.len)); + return(1); + } + + // return error for oversized packets + if ((pPacket->head.len + NETGAME_DATAPKT_MAXTAIL) > pRef->pPort->maxwid) + { + NetPrintf(("netgamelink: oversized packet send (%d bytes)\n", pPacket->head.len)); + return(-1); + } + + /* see if we should add sync info: + if _NetGameLinkSendPacket was called explicitly to send a sync packet there is no need to make any further checks + otherwise if a reliable packet game was being sent and it is time to send a sync packet */ + if (((pPacket->head.kind & GAME_PACKET_SYNC) || ((pPacket->head.kind != GAME_PACKET_USER_UNRELIABLE) && (uCurrTick-pRef->uLastSync > NETGAMELINK_KEEPALIVE_TIME/2))) && (pRef->bSyncEnabled == TRUE)) + { + // build the sync packet + ds_memclr(&sync, sizeof(sync)); + + sync.size = sizeof(sync); + sync.echo = SocketHtonl(uCurrTick); + sync.repl = SocketHtonl(pRef->uLastEcho+(uCurrTick-pRef->uLastRecv)); + sync.late = SocketHtons((int16_t)((pRef->iRttAvg+pRef->iRttDev+1)/2)); + + uLPktLost = _NetGameLinkUpdateLocalPacketLoss(pRef->NetGameLinkStats.lpackrcvd, pRef->NetGameLinkStats.rpacksent, (unsigned)pRef->iR2LPackLost); + uPackSent = pRef->pPort->packsent; + uNakSent = pRef->pPort->naksent; + uPackRcvd = pRef->pPort->packrcvd; + + sync.plost = (uint8_t)(uLPktLost - pRef->iR2LPackLost); + sync.psnt = (uint8_t)(uPackSent - pRef->iPackSent); + sync.nsnt = (uint8_t)(uNakSent - pRef->iNakSent); + sync.prcvd = (uint8_t)(uPackRcvd - pRef->iPackRcvd); + + // if we have not received a sync packet from the remote peer, our repl field is not valid, so we tell the remote peer to ignore it + if (pRef->NetGameLinkStats.rpacksent == 0) + { + sync.flags |= NETGAME_SYNCFLAG_REPLINVALID; + } + + // piggyback on existing packet + ds_memcpy(pPacket->body.data+pPacket->head.len, &sync, sizeof(sync)); + pPacket->head.len += sizeof(sync); + pPacket->head.kind |= GAME_PACKET_SYNC; + iSynced = 1; + } + + // put type as last byte + pPacket->body.data[pPacket->head.len] = pPacket->head.kind; + // determine packet flags + uPacketType = pPacket->head.kind & ~GAME_PACKET_SYNC; + if (((uPacketType <= GAME_PACKET_ONE_BEFORE_FIRST) || (uPacketType >= GAME_PACKET_ONE_PAST_LAST)) && (pPacket->head.kind != GAME_PACKET_SYNC)) + { + NetPrintf(("netgamelink: warning -- send unrecognized packet kind %d\n", pPacket->head.kind)); + } + if (uPacketType == GAME_PACKET_USER_UNRELIABLE) + { + uPacketFlags = COMM_FLAGS_UNRELIABLE; + } + else if (uPacketType == GAME_PACKET_USER_BROADCAST) + { + uPacketFlags = COMM_FLAGS_UNRELIABLE|COMM_FLAGS_BROADCAST; + } + else + { + uPacketFlags = COMM_FLAGS_RELIABLE; + } + + // send the packet + iResult = pRef->pPort->Send(pRef->pPort, pPacket->body.data, pPacket->head.len + 1, uPacketFlags); + + // if we added sync info, remove it now + if (iSynced) + { + pPacket->head.len -= sizeof(sync); + pPacket->head.kind ^= GAME_PACKET_SYNC; + } + + // make sure send succeeded + if (iResult > 0) + { + // record the send time + pRef->uLastSend = uCurrTick; + // if it was a sync packet, update sync info + if (iSynced) + { + pRef->uLastSync = uCurrTick; + pRef->iPackSent = uPackSent; + pRef->iNakSent = uNakSent; + pRef->iPackRcvd = uPackRcvd; + pRef->iR2LPackLost = uLPktLost; + } + + // update the stats + pRef->iInpBytes += pPacket->head.len; + pRef->iInpPackets += 1; + pRef->NetGameLinkStats.sent += pPacket->head.len; + pRef->NetGameLinkStats.sentlast = pRef->uLastSend; + + // see if we should turn off send light + if ((pRef->NetGameLinkStats.sentshow != 0) && (pRef->uLastSend -pRef->NetGameLinkStats.sentshow > 100)) + { + pRef->NetGameLinkStats.sentshow = 0; + pRef->NetGameLinkStats.senthide = pRef->uLastSend; + } + + // see if we should turn on send light + if ((pRef->NetGameLinkStats.senthide != 0) && (pRef->uLastSend -pRef->NetGameLinkStats.senthide > 100)) + { + pRef->NetGameLinkStats.senthide = 0; + pRef->NetGameLinkStats.sentshow = pRef->uLastSend; + } + } + else + { +// NetPrintf(("GmLink: send failed! (kind=%d, len=%d, synced=%d)\n", +// packet->head.kind, packet->head.len, iSynced)); + } + + // return the result code + return(iResult); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkRecvPacket0 + + \Description + Process incoming data packet + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Output + int32_t - zero=no packet processed, positive=packet processed + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameLinkRecvPacket0(NetGameLinkRefT *pRef, uint32_t uCurrTick) +{ + int32_t iSize; + int16_t iDelta; + uint32_t uWhen, uKind; + NetGamePacketT *pPacket; + NetGameLinkHistT *pHistory; + + // calculate buffer space free, making sure to include packet header overhead (not sent, but queued) + if ((iSize = pRef->iBufMax -pRef->iBufLen -sizeof(pPacket->head)) <= 0) + { + return(0); + } + else if (iSize > pRef->pPort->maxwid) + { + iSize = pRef->pPort->maxwid; + } + + // setup packet buffer + pPacket = (NetGamePacketT *)(pRef->pBuffer +pRef->iBufLen); + + // see if packet is available + iSize = pRef->pPort->Recv(pRef->pPort, &pPacket->body, iSize, &uWhen); + if ((iSize <= 0) || (iSize > pRef->pPort->maxwid)) + { + #if DIRTYCODE_LOGGING + if (iSize != COMM_NODATA) + { + NetPrintf(("netgamelink: Recv() returned %d\n", iSize)); + } + #endif + return(0); + } + pPacket->head.len = iSize; + pPacket->head.size = 0; + pPacket->head.when = uWhen; + + pRef->NetGameLinkStats.tick = uCurrTick; + pRef->NetGameLinkStats.tickseqn += 1; + + // update the stats + pRef->NetGameLinkStats.rcvdlast = uCurrTick; + pRef->NetGameLinkStats.rcvd += pPacket->head.len; + pRef->iRcvBytes += pPacket->head.len; + pRef->iRcvPackets += 1; + + // see if we should turn off receive light + if ((pRef->NetGameLinkStats.rcvdshow != 0) && (uCurrTick-pRef->NetGameLinkStats.rcvdshow > 100)) + { + pRef->NetGameLinkStats.rcvdshow = 0; + pRef->NetGameLinkStats.rcvdhide = uCurrTick; + } + + // see if we should turn on receive light + if ((pRef->NetGameLinkStats.rcvdhide != 0) && (uCurrTick-pRef->NetGameLinkStats.rcvdhide > 100)) + { + pRef->NetGameLinkStats.rcvdhide = 0; + pRef->NetGameLinkStats.rcvdshow = uCurrTick; + } + + // extract the kind field and fix the length + pPacket->head.len -= 1; + pPacket->head.kind = pPacket->body.data[pPacket->head.len]; + + // get packet kind + uKind = pPacket->head.kind & ~GAME_PACKET_SYNC; + + // warn if kind is invalid + if (((uKind <= GAME_PACKET_ONE_BEFORE_FIRST) || (uKind >= GAME_PACKET_ONE_PAST_LAST)) && (pPacket->head.kind != GAME_PACKET_SYNC)) + { + NetPrintf(("netgamelink: warning -- recv unrecognized packet kind %d\n", pPacket->head.kind)); + return(1); + } + + // see if this packet contains timing info + if (pPacket->head.kind & GAME_PACKET_SYNC) + { + // get sync packet info + NetGamePacketSyncT Sync; + if (_NetGameLinkGetSync(pPacket, pPacket->body.data, &Sync) == NULL) + { + return(1); + } + + // remove sync bit + pPacket->head.kind ^= GAME_PACKET_SYNC; + + // update the latency stat + pRef->NetGameLinkStats.late = SocketNtohs(Sync.late); + + // calculate instantaneous rtt + if ((Sync.flags & NETGAME_SYNCFLAG_REPLINVALID) == 0) + { + /* delta = recvtime - sendtime + 1 + The +1 is needed because the peer may think the packet stayed in + its queue for 1ms while we may think it was returned within the + same millisecond. This happens because the clocks are not precisely + synchronized. Adding 1 avoids the issue without compromising precision. */ + iDelta = (int16_t)(uWhen - SocketNtohl(Sync.repl) + 1); + } + else + { + // remote peer has indicated their repl field is invalid, so we ignore sync latency info + iDelta = -1; + } + if ((iDelta >= 0) && (iDelta <= 2500)) + { + // figure out elapsed time since last packet + int32_t iElapsed = uWhen-pRef->uLastAvg; + if (iElapsed < 10) + { + iElapsed = 10; + } + pRef->uLastAvg = uWhen; + + // perform rtt calc using weighted time average + if (iElapsed < RTT_SAMPLE_PERIOD) + { + // figure out weight of existing data + int32_t iWeight = RTT_SAMPLE_PERIOD-iElapsed; + // figure deviation first since it uses average + int32_t iDeviate = iDelta - pRef->iRttAvg; + if (iDeviate < 0) + iDeviate = -iDeviate; + // calc weighted deviation + pRef->iRttDev = (iWeight*pRef->iRttDev+iElapsed*iDeviate)/RTT_SAMPLE_PERIOD; + // calc weighted average + pRef->iRttAvg = (iWeight*pRef->iRttAvg+iElapsed*iDelta)/RTT_SAMPLE_PERIOD; + } + else + { + // if more than our scale has elapsed, use this data + pRef->iRttAvg = iDelta; + pRef->iRttDev = 0; + } + + // save copy of ping in stats table + pRef->NetGameLinkStats.ping = pRef->iRttAvg+pRef->iRttDev; + pRef->NetGameLinkStats.pingavg = pRef->iRttAvg; + pRef->NetGameLinkStats.pingdev = pRef->iRttDev; + + // see if this is a new recod + if (uWhen-pRef->uLastHist >= PING_LENGTH) + { + // remember the update time + pRef->uLastHist = uWhen; + // advance to next ping slot + pRef->NetGameLinkStats.pingslot = (pRef->NetGameLinkStats.pingslot + 1) % PING_HISTORY; + pHistory = pRef->NetGameLinkStats.pinghist + pRef->NetGameLinkStats.pingslot; + + // save the information + pHistory->min = iDelta; + pHistory->max = iDelta; + pHistory->avg = iDelta; + pHistory->cnt = 1; + } + else + { + // update the information + pHistory = pRef->NetGameLinkStats.pinghist + pRef->NetGameLinkStats.pingslot; + if (iDelta < pHistory->min) + pHistory->min = iDelta; + if (iDelta > pHistory->max) + pHistory->max = iDelta; + pHistory->avg = ((pHistory->avg * pHistory->cnt) + iDelta) / (pHistory->cnt+1); + pHistory->cnt += 1; + } + + // save this update time + pRef->NetGameLinkStats.pingtick = uWhen; + } + else if ((Sync.flags & NETGAME_SYNCFLAG_REPLINVALID) == 0) + { + NetPrintf(("netgamelink: sync delta %d is out of range (when=0x%08x,sync.repl=0x%08x)\n", iDelta, uWhen, SocketNtohl(Sync.repl) )); + } + + // extract timing information + pRef->uLastRecv = uWhen; + pRef->uLastEcho = SocketNtohl(Sync.echo); + + // update remote peer's packets sent/recv stat tracker + if (pRef->NetGameLinkStats.rpacksent == 0) + { + /* + bias initial update by one to account for the commdup packet conveying this NetGameLinkSyncT packet not being in the count (but it should really be) + no need to do so afterwards because the count will include the previous packet for which the count is already biased + */ + pRef->NetGameLinkStats.rpacksent += 1; + } + pRef->NetGameLinkStats.rpacksent += Sync.psnt; + pRef->NetGameLinkStats.rnaksent += Sync.nsnt; + pRef->NetGameLinkStats.rpackrcvd += Sync.prcvd; + pRef->NetGameLinkStats.rpacklost += Sync.plost; + + // update packets received, packets saved, packets sent and packets lost; this is done here to keep in sync with rpacksent and rpackrcvd + pRef->NetGameLinkStats.lnaksent = pRef->pPort->naksent; + pRef->NetGameLinkStats.lpackrcvd = pRef->pPort->packrcvd; + pRef->NetGameLinkStats.lpacksaved = pRef->pPort->packsaved; + pRef->NetGameLinkStats.lpacksent = pRef->pPort->packsent; + pRef->NetGameLinkStats.lpacklost = _NetGameLinkUpdateLocalPacketLoss(pRef->NetGameLinkStats.lpackrcvd, pRef->NetGameLinkStats.rpacksent, pRef->NetGameLinkStats.lpacklost); + } + + // save packet if something remains (was not just sync) + if (pPacket->head.kind != 0) + { + pPacket->head.size = (sizeof(pPacket->head)+pPacket->head.len+3) & 0x7ffc; + pRef->iBufLen += pPacket->head.size; + } + return(1); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkRecvPacket + + \Description + Call _NetGameLinkRecvPacket0 if we haven't already + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Output + int32_t - zero=no packet processed, positive=packet processed + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameLinkRecvPacket(NetGameLinkRefT *pRef, uint32_t uCurrTick) +{ + int32_t iRetVal; + + // limit call depth to prevent a callback from calling us more than once + if (pRef->iRecvProcCnt > 0) + { + #if DIRTYCODE_LOGGING && 0 + NetPrintf(("netgamelink: m_recvprocct=%d, unable to call _NetGameLinkRecvPacket0\n", pRef->m_recvprocct)); + #endif + return(0); + } + + pRef->iRecvProcCnt = 1; + iRetVal = _NetGameLinkRecvPacket0(pRef, uCurrTick); + pRef->iRecvProcCnt = 0; + + return(iRetVal); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkProcess + + \Description + Main send/receive processing loop + + \Input *pRef - reference pointer + \Input uCurrTick- current tick + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +static void _NetGameLinkProcess(NetGameLinkRefT *pRef, uint32_t uCurrTick) +{ + uint32_t uRange; + + // grab any pending packets + while (_NetGameLinkRecvPacket(pRef, uCurrTick) > 0) + { + pRef->iCallPending += 1; + } + + // mark as processing complete + pRef->iProcess = 0; + + // handle stats update (once per second) + pRef->NetGameLinkStats.tick = uCurrTick; + pRef->NetGameLinkStats.tickseqn += 1; + uRange = uCurrTick - pRef->NetGameLinkStats.stattick; + if (uRange >= 1000) + { + // calc bytes per second, raw bytes per second, and network bytes per second + pRef->NetGameLinkStats.outbps = (pRef->iInpBytes *1000)/uRange; + pRef->NetGameLinkStats.outrps = (pRef->pPort->datasent-pRef->iInpRawBytes)*1000/uRange; + pRef->NetGameLinkStats.outnps = ((pRef->pPort->datasent-pRef->iInpRawBytes)+(pRef->pPort->overhead-pRef->iOverhead))*1000/uRange; + pRef->NetGameLinkStats.inrps = ((pRef->pPort->datarcvd-pRef->iRcvRawBytes)*1000)/uRange; + pRef->NetGameLinkStats.inbps = (pRef->iRcvBytes * 1000)/uRange; + pRef->NetGameLinkStats.innps = ((pRef->pPort->datarcvd-pRef->iRcvRawBytes)+(pRef->pPort->rcvoverhead-pRef->iRcvOverhead))*1000/uRange; + // calculate packets per second and raw packets per second + pRef->NetGameLinkStats.outpps = ((pRef->iInpPackets *1000)+500)/uRange; + pRef->NetGameLinkStats.outrpps = ((pRef->pPort->packsent-pRef->iInpRawPackets)*1000+500)/uRange; + pRef->NetGameLinkStats.inpps = ((pRef->iRcvPackets *1000)+500)/uRange; + pRef->NetGameLinkStats.inrpps = ((pRef->pPort->packrcvd - pRef->iRcvRawPackets)*1000+500)/uRange; + // reset tracking variables + pRef->iInpBytes = pRef->iInpPackets = 0; + pRef->iRcvBytes = pRef->iRcvPackets = 0; + pRef->iInpRawBytes = pRef->pPort->datasent; + pRef->iInpRawPackets = pRef->pPort->packsent; + pRef->iRcvRawPackets = pRef->pPort->packrcvd; + pRef->iRcvRawBytes = pRef->pPort->datarcvd; + pRef->iOverhead = pRef->pPort->overhead; + pRef->iRcvOverhead = pRef->pPort->rcvoverhead; + + // remember stat update time + pRef->NetGameLinkStats.stattick = uCurrTick; + } + + // see if we should turn off send light + if ((pRef->NetGameLinkStats.sentshow != 0) && (uCurrTick-pRef->NetGameLinkStats.sentshow > 100)) + { + pRef->NetGameLinkStats.sentshow = 0; + pRef->NetGameLinkStats.senthide = uCurrTick; + } + + // see if we should turn on send light + if ((pRef->NetGameLinkStats.senthide != 0) && (uCurrTick-pRef->NetGameLinkStats.senthide > 100) && (uCurrTick-pRef->NetGameLinkStats.sentlast < 50)) + { + pRef->NetGameLinkStats.senthide = 0; + pRef->NetGameLinkStats.sentshow = uCurrTick; + } + + // see if we should turn off receive light + if ((pRef->NetGameLinkStats.rcvdshow != 0) && (uCurrTick-pRef->NetGameLinkStats.rcvdshow > 100)) + { + pRef->NetGameLinkStats.rcvdshow = 0; + pRef->NetGameLinkStats.rcvdhide = uCurrTick; + } + + // see if we should turn on receive light + if ((pRef->NetGameLinkStats.rcvdhide != 0) && (uCurrTick-pRef->NetGameLinkStats.rcvdhide > 100) && (uCurrTick-pRef->NetGameLinkStats.rcvdlast < 50)) + { + pRef->NetGameLinkStats.rcvdhide = 0; + pRef->NetGameLinkStats.rcvdshow = uCurrTick; + } + + // send keepalive/sync if needed + if ((uCurrTick-pRef->uLastSend > NETGAMELINK_KEEPALIVE_TIME) && (pRef->bSyncEnabled == TRUE)) + { + // make sure we do not overflow output queue + int32_t iQueue = pRef->pPort->Send(pRef->pPort, NULL, 0, COMM_FLAGS_RELIABLE); + // the exact buffer limit is unimportant, but it needs something + // to avoid overrun during a semi-persistant failure + if ((iQueue > 0) && (iQueue < (pRef->pPort->maxout/4))) + { + // send sync packet + NetGamePacketT spacket; + spacket.head.kind = GAME_PACKET_SYNC; + spacket.head.len = 0; + _NetGameLinkSendPacket(pRef, &spacket, uCurrTick); + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkPrintStats + + \Description + Prints the current NetGameLink stats. + + \Input *pRef - reference pointer + + \Version 09/03/14 (mcorcoran) +*/ +/*************************************************************************************************F*/ +static void _NetGameLinkPrintStats(NetGameLinkRefT *pRef) +{ + NetGameLinkStatT NetGameLinkStats; + NetGameLinkStatus(pRef, 'stat', 0, &NetGameLinkStats, sizeof(NetGameLinkStats)); + + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: QoS Results ------------------------------------------------------------------------------\n")); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: latency over QoS period %d\n", pRef->iQosAvgLate)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: latency over last sampling period %d (sampl. prd = %d ms)\n", NetGameLinkStats.late, RTT_SAMPLE_PERIOD)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: when connection established %d\n", NetGameLinkStats.conn)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: when data most recently sent %d\n", NetGameLinkStats.sentlast)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: when data most recently received %d\n", NetGameLinkStats.rcvdlast)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: ping deviation %d\n", NetGameLinkStats.pingdev)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: ping average %d\n", NetGameLinkStats.pingavg)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of bytes sent %d\n", NetGameLinkStats.sent)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of bytes received %d\n", NetGameLinkStats.rcvd)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of packets sent to peer since start (at time = last inbound sync pkt) %d\n", NetGameLinkStats.lpacksent)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of packets sent to peer since start (at time = now) %d\n", NetGameLinkStats.lpacksent_now)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of packets received from peer since start %d\n", NetGameLinkStats.lpackrcvd)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of packets sent by peer (to us) since start %d\n", NetGameLinkStats.rpacksent)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of packets received by peer (from us) since start %d\n", NetGameLinkStats.rpackrcvd)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: local->remote packets lost: number of packets (from us) lost by peer since start %d\n", NetGameLinkStats.rpacklost)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: packets saved: number of packets recovered by via redundancy mechanisms %d\n", NetGameLinkStats.lpacksaved)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: number of NAKs sent by peer (to us) since start %d\n", NetGameLinkStats.rnaksent)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: packets per sec sent (user packets) %d\n", NetGameLinkStats.outpps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: raw packets per sec sent (packets sent to network) %d\n", NetGameLinkStats.outrpps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: bytes per sec sent (user data) %d\n", NetGameLinkStats.outbps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: raw bytes per sec sent (data sent to network) %d\n", NetGameLinkStats.outrps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: network bytes per sec sent (inrps + estimated UDP/Eth frame overhead) %d\n", NetGameLinkStats.outnps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: packets per sec received (user packets) %d\n", NetGameLinkStats.inpps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: raw packets per sec received (packets sent to network) %d\n", NetGameLinkStats.inrpps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: bytes per sec received (user data) %d\n", NetGameLinkStats.inbps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: raw bytes per sec received (data sent to network) %d\n", NetGameLinkStats.inrps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: network bytes per sec received (inrps + estimated UDP/Eth frame overhead) %d\n", NetGameLinkStats.innps)); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: ------------------------------------------------------------------------------------------\n")); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkNotify + + \Description + Main notification from lower layer + + \Input *pCommRef- generic comm pointer + \Input iEvent - event type + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +static void _NetGameLinkNotify(CommRef *pCommRef, int32_t iEvent) +{ + NetGameLinkRefT *pRef = (NetGameLinkRefT *)pCommRef->refptr; + + // make sure we are exclusive + if (NetCritTry(&pRef->Crit)) + { + // do the processing + _NetGameLinkProcess(pRef, NetTick()); + + // do callback if needed + if ((pRef->iBufLen > 0) && (pRef->pCallProc != NULL) && (pRef->iCallPending > 0)) + { + pRef->iCallPending = 0; + (pRef->pCallProc)(pRef->pCallData, 1); + } + + // free access + NetCritLeave(&pRef->Crit); + } + else + { + // count the miss + pRef->iProcess += 1; + if (pRef->iProcess > 0) + { +// NetPrintf(("netgamelink: missed %d events\n", pRef->iProcess)); + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkSendCallback + + \Description + Handle notification of send (or re-send) of data from comm layer. + + \Input *pComm - pointer to comm ref + \Input *pPacket - packet data that is being sent + \Input iPacketLen - packet length + \Input uCurrTick - current timestamp + + \Version 09/09/11 (jbrookes) +*/ +/*************************************************************************************************F*/ +static void _NetGameLinkSendCallback(CommRef *pComm, void *pPacket, int32_t iPacketLen, uint32_t uCurrTick) +{ + uint8_t *pPacketData = (uint8_t *)pPacket; + NetGamePacketSyncT Sync, *pSync; + NetGamePacketHeadT Head; + uint32_t uTickDiff; + + // does this packet include a sync packet? + if ((pPacketData[iPacketLen-1] & GAME_PACKET_SYNC) == 0) + { + return; + } + + // extract sync packet + Head.kind = pPacketData[iPacketLen-1]; + Head.len = iPacketLen-1; + if ((pSync = _NetGameLinkGetSync((NetGamePacketT *)&Head, pPacketData, &Sync)) != NULL) + { + // compare currtick with current tick + uTickDiff = NetTickDiff(uCurrTick, SocketNtohl(Sync.echo)); + + // refresh echo and repl and write back sync packet + Sync.echo = SocketHtonl(uCurrTick); + Sync.repl = SocketHtonl((SocketNtohl(Sync.repl) + uTickDiff)); + ds_memcpy(pSync, &Sync, sizeof(Sync)); + + /* Note: + Unlike echo and repl, other sync packet fields (psnt, plost, nsnt, prcvd,...) are intentionally not + refreshed here because they convey update of counts between two sync packets (they are delta values). + Any update in the values of these counters not included in this sync packet (regardless of this + packet being resent or not) will be safely covered by the next sync packet. */ + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkPollStream + + \Description + Poll to see if we should send stream data + + \Input *pRef - reference pointer + + \Output + int32_t - send count + + \Version 01/11/10 (jrainy) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameLinkPollStream(NetGameLinkRefT *pRef) +{ + int32_t iCount = 0, iSize, iResult; + NetGameMaxPacketT Packet; + GameStreamT Block; + NetGameLinkStreamT *pStream; + + // see if any stream has pending data + for (pStream = pRef->m_stream; pStream != NULL; pStream = pStream->pNext) + { + // see if anything to send + while (pStream->iOutProg < pStream->iOutSize) + { + int32_t iLimit; + + // get data block + ds_memcpy(&Block, pStream->pOutData+pStream->iOutProg, sizeof(Block)); + // setup data packet + Packet.head.kind = GAME_PACKET_LINK_STRM; + // set up packet 'size' field, which is size | subchannel + iSize = (Block.iSize & ~0xff000000) | ((Block.iSubchan & 0xff) << 24); + // store stream header fields in network order + Packet.body.strm.ident = SocketHtonl(pStream->iIdent); + Packet.body.strm.kind = SocketHtonl(Block.iKind); + Packet.body.strm.size = SocketHtonl(iSize); + iSize = Block.iSize -Block.iSent; + + // don't overflow our buffer + iLimit = (signed)sizeof(Packet.body.strm.data); + if (iSize > iLimit) + { + iSize = iLimit; + } + + // don't overflow the underlying layer max packet size + iLimit = pRef->iStrmMaxPktSize; + if (iSize > iLimit) + { + iSize = iLimit; + } + + ds_memcpy(Packet.body.strm.data, pStream->pOutData+pStream->iOutProg+sizeof(Block)+Block.iSent, iSize); + Packet.head.len = (uint16_t)(sizeof(Packet.body.strm)-sizeof(Packet.body.strm.data)+iSize); + // try and queue/send the packet + iResult = NetGameLinkSend(pRef, (NetGamePacketT *)&Packet, 1); + if (iResult < 0) + { + NetPrintf(("netgamelink: [%p] stream send failed for stream 0x%08x!\n", pRef, pStream)); + break; + } + else if (iResult == 0) + { + break; + } + // advance the sent count + Block.iSent += iSize; + if (Block.iSent != Block.iSize) + { + // save back updated block for next time (could just update .sent, but this is easier due to alignment) + ds_memcpy(pStream->pOutData+pStream->iOutProg, &Block, sizeof(Block)); + } + else + { + pStream->iOutProg += sizeof(Block)+Block.iSize; + // see if we should shift the buffer (more than 75% full and shift would gain >33%) + if ((pStream->iOutProg < pStream->iOutSize) && + (pStream->iOutSize*4 > pStream->iOutMaxm*3) && + (pStream->iOutProg*3 > pStream->iOutMaxm*1)) + { + // do the shift + memmove(pStream->pOutData, pStream->pOutData+pStream->iOutProg, pStream->iOutSize-pStream->iOutProg); + pStream->iOutSize -= pStream->iOutProg; + pStream->iOutProg = 0; + } + } + // count the send + ++iCount; + } + } + + // return send count + return(iCount); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkSendStream + + \Description + Let user queue up a buffer to send + + \Input *pStream - stream to send on + \Input iSubChan - subchannel stream is to be received on + \Input iKind - kind of data (used to determine if sync) + \Input *pBuffer - pointer to data to send + \Input iLength - length of data to send + + \Output + int32_t - negative=error, zero=busy (send again later), positive=sent + + \Version 01/11/10 (jrainy) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameLinkSendStream(NetGameLinkStreamT *pStream, int32_t iSubChan, int32_t iKind, void *pBuffer, int32_t iLength) +{ + GameStreamT Block; + + // if this is an orphaned stream, return an error + if (pStream->pClient == NULL) + { + return(-1); //todo + } + + // allow negative length to mean strlen(data)+1 + if (iLength < 0) + { + for (iLength = 0; ((char *)pBuffer)[iLength] != '\0'; ++iLength) + ; + ++iLength; + } + + // make sure send buffer does not exceed total input buffer + // (do this AFTER negative length check) + if (iLength >= pStream->iInpMaxm) + { + return(-1); + } + + // see if we can reset buffer pointers + if (pStream->iOutProg >= pStream->iOutSize) + { + pStream->iOutProg = pStream->iOutSize = 0; + } + + // make sure send buffer does not exceed current output buffer + if (iLength+sizeof(GameStreamT) >= (unsigned)pStream->iOutMaxm-pStream->iOutSize) + { + return(0); + } + + // if this is a sync, make sure we have space in sync buffer + if ((iKind > '~ ') && (iLength+sizeof(GameStreamT) >= (unsigned)pStream->iSynMaxm-pStream->iSynSize)) + { + return(0); + } + + // see if this is a length query + if (pBuffer == NULL) + { + // calc main buffer space + iLength = pStream->iOutMaxm-pStream->iOutSize; + // if this is sync, limit based on sync buffer + if ((iKind > '~ ') && (iLength > pStream->iSynMaxm-pStream->iSynSize)) + { + iLength = pStream->iSynMaxm-pStream->iSynSize; + } + // subtract packet header size + iLength -= sizeof(GameStreamT); + if (iLength < 0) + { + iLength = 0; + } + // return available size + return(iLength); + } + + // setup the header block + Block.iKind = iKind; + Block.uSync = 0; + Block.iSent = 0; + Block.iSize = iLength; + Block.iSubchan = iSubChan; + + // if sync, copy into sync buffer + if (iKind > '~ ') + { + // set sync sequence number + Block.uSync = 0; + // copy into sync byffer + ds_memcpy(pStream->pSynData+pStream->iSynSize, &Block, sizeof(Block)); + ds_memcpy(pStream->pSynData+pStream->iSynSize+sizeof(Block), pBuffer, iLength); + pStream->iSynSize += sizeof(Block)+iLength; + } + + // do a memmove just in case data source is already in this buffer + // (this is an optimization used for database access) + memmove(pStream->pOutData+pStream->iOutSize+sizeof(Block), pBuffer, iLength); + ds_memcpy(pStream->pOutData+pStream->iOutSize, &Block, sizeof(Block)); + pStream->iOutSize += sizeof(Block)+iLength; + + if (pStream->iOutSize > pStream->iHighWaterUsed) + { + pStream->iHighWaterUsed = pStream->iOutSize; + } + if ((pStream->iOutSize - pStream->iOutProg) > pStream->iHighWaterNeeded) + { + pStream->iHighWaterNeeded = pStream->iOutSize - pStream->iOutProg; + } + + // attempt immediate send + _NetGameLinkPollStream(pStream->pClient); + + // return send status + return(iLength); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkRecvStream + + \Description + Process a received stream packet + + \Input *pRef - reference pointer + \Input *pPacket - packet to process + + \Output + int32_t - -1=no match, -2=overflow error, 0=valid packet received + + \Version 01/11/10 (jrainy) +*/ +/*************************************************************************************************F*/ +static int32_t _NetGameLinkRecvStream(NetGameLinkRefT *pRef, NetGamePacketT *pPacket) +{ + int32_t iRes = -1, iSize; + NetGameLinkStreamT *pStream; + NetGameLinkStreamInpT *pInp; + int32_t iSubChannel; + + // see if this packet matches any of the streams + for (pStream = pRef->m_stream; pStream != NULL; pStream = pStream->pNext) + { + // see if this is the matching stream + if (pStream->iIdent == pPacket->body.strm.ident) + { + // extract size/subchannel from 'size' member + iSize = pPacket->body.strm.size & ~0xff000000; + iSubChannel = (unsigned)pPacket->body.strm.size >> 24; + if ((iSubChannel < 0) || (iSubChannel >= (pStream->iSubchan+1))) + { + NetPrintf(("netgamelink: [%p] warning, received packet on stream '%C' with invalid packet subchannel %d\n", pRef, pPacket->body.strm.ident, iSubChannel)); + return(-1); + } + // ref channel + pInp = pStream->pInp + iSubChannel; + // default to overflow error + iRes = GMDIST_OVERFLOW; + // validate the packet size + if (iSize <= pStream->iInpMaxm) + { + /* auto-clear packet if anything looks inconsistant -- note that this is expected behavior + for the first packet fragment in a sequence of packet fragments */ + if ((pPacket->body.strm.kind != pInp->iInpKind) || (iSize != pInp->iInpSize)) + { + // reset progress + pInp->iInpProg = 0; + // save basic packet header + pInp->iInpKind = pPacket->body.strm.kind; + pInp->iInpSize = iSize; + } + // figure out size of data in packet + iSize = pPacket->head.len-(sizeof(pPacket->body.strm)-sizeof(pPacket->body.strm.data)); + if (iSize > pInp->iInpSize-pInp->iInpProg) + { + NetPrintf(("netgamelink: [%p] clamped stream packet size from %d to %d on stream '%C'\n", pRef, iSize, pInp->iInpSize-pInp->iInpProg, pPacket->body.strm.ident)); + iSize = pInp->iInpSize-pInp->iInpProg; + } + // append to existing packet + ds_memcpy(pInp->pInpData+pInp->iInpProg, pPacket->body.strm.data, iSize); + pInp->iInpProg += iSize; + // see if packet is complete + if (pInp->iInpProg == pInp->iInpSize) + { + // deliver packet + if (pStream->Recv != NULL) + { + pStream->Recv(pStream, iSubChannel, pInp->iInpKind, pInp->pInpData, pInp->iInpSize); + } + else + { + NetPrintf(("netgamelink: [%p] no registered stream recv handler on stream '%C'\n", pRef, pPacket->body.strm.ident)); + } + // clear from buffer + pInp->iInpProg = 0; + } + // packet was valid + iRes = 0; + } + else + { + NetPrintf(("netgamelink: [%p] invalid stream packet size (%d >= %d) on stream '%C'\n", + pRef, pPacket->body.strm.size, pStream->iInpMaxm, pPacket->body.strm.ident)); + } + return(iRes); + } + } + + // didn't find the stream + NetPrintf(("netgamelink: [%p] could not find stream for stream packet with iIdent '%C'\n", pRef, pPacket->body.strm.ident)); + + // return result + return(iRes); +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkUpdateQos + + \Description + Send and receive QoS packets. + + \Input *pRef - reference pointer + + \Version 09/03/14 (mcorcoran) +*/ +/*************************************************************************************************F*/ +static void _NetGameLinkUpdateQos(NetGameLinkRefT *pRef) +{ + NetGameMaxPacketT QosPacket; + uint32_t uNow = NetTick(); + + // save QoS start time + if (pRef->uQosStartTime == 0) + { + pRef->uQosStartTime = uNow; + } + + // init QoS send time + if ((pRef->uQosPacketsToSend > 0) && (pRef->uQosLastSendTime == 0)) + { + /* We are now expecting QoS packet from the other end of the link. + Make this value non-zero to satisfy the while() condition below. */ + pRef->uQosPacketsToRecv = 1; + pRef->uQosLastSendTime = uNow - (pRef->uQosSendInterval+1); + } + + // check if it's time to send a QoS packet. + if ((pRef->uQosPacketsToSend > 0) && (NetTickDiff(uNow, pRef->uQosLastSendTime) > (signed)pRef->uQosSendInterval)) + { + // set qos last send time (reserve zero for uninitialized) + pRef->uQosLastSendTime = (uNow != 0) ? uNow : uNow-1; + pRef->uQosPacketsToSend -= 1; + + // create and send the packet + QosPacket.head.kind = GAME_PACKET_USER; + QosPacket.head.len = pRef->uQosPacketSize; + + ds_memclr(QosPacket.body.data, QosPacket.head.len); + + // the other end of the link needs to know how many more packets to expect. + QosPacket.body.data[0] = (uint8_t)(pRef->uQosPacketsToSend >> 8); + QosPacket.body.data[1] = (uint8_t)(pRef->uQosPacketsToSend >> 0); + + NetGameLinkSend(pRef, (NetGamePacketT*)&QosPacket, 1); + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: [%p] sent QoS packet, size(%d), remaining packets to send (%d)\n", pRef, QosPacket.head.len, pRef->uQosPacketsToSend)); + } + + while ((pRef->uQosPacketsToRecv > 0) && NetGameLinkRecv(pRef, (NetGamePacketT*)&QosPacket, 1, FALSE)) + { + // the other end of the link tells us how many more packets to expect (wait for before transitioning to active) + pRef->uQosPacketsToRecv = + (QosPacket.body.data[0] << 8) | + (QosPacket.body.data[1] << 0); + + NetPrintfVerbose((pRef->iVerbosity, 3, "netgamelink: [%p] received QoS packet, size(%d), remaining packets to recv (%d)\n", pRef, QosPacket.head.len, pRef->uQosPacketsToRecv)); + } + + // have we finished sending and receiving everything yet? + if ((pRef->uQosPacketsToSend == 0) && (pRef->uQosPacketsToRecv == 0)) + { + // we're done, print available stats + _NetGameLinkPrintStats(pRef); + } + + // only start averaging latency when 1 sample period is complete + if (NetTickDiff(uNow, pRef->uQosStartTime) > RTT_SAMPLE_PERIOD) + { + pRef->iQosCumulLate += pRef->NetGameLinkStats.late; + pRef->iQosLateCount++; + pRef->iQosAvgLate = pRef->iQosCumulLate / pRef->iQosLateCount; + } +} + +/*F*************************************************************************************************/ +/*! + \Function _NetGameLinkDrainRecvStream + + \Description + Need for streams handling. Received all data on the stream. + + \Input *pRef - reference pointer + + \Version 09/03/14 (mcorcoran) +*/ +/*************************************************************************************************F*/ +static void _NetGameLinkDrainRecvStream(NetGameLinkRefT *pRef) +{ + NetGameMaxPacketT Packet; + + while (NetGameLinkRecv2(pRef, (NetGamePacketT*)&Packet, 1, 1 << GAME_PACKET_LINK_STRM)) + { + // convert stream header from network to host order + Packet.body.strm.ident = SocketNtohl(Packet.body.strm.ident); + Packet.body.strm.kind = SocketNtohl(Packet.body.strm.kind); + Packet.body.strm.size = SocketNtohl(Packet.body.strm.size); + + // process the packet + _NetGameLinkRecvStream(pRef, (NetGamePacketT *)&Packet); + } +} + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkCreate + + \Description + Construct the game client + + \Input *pCommRef - the connection from NetGameUtilComplete() + \Input iOwner - if TRUE, NetGameLink will assume ownership of the port (ie, delete it when done) + \Input iBufLen - length of input buffer + + \Output + NetGameLinkRefT * - pointer to new NetGameLinkRef + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +NetGameLinkRefT *NetGameLinkCreate(void *pCommRef, int32_t iOwner, int32_t iBufLen) +{ + int32_t iIndex; + uint32_t uTick; + CommRef *pPort = pCommRef; + NetGameLinkRefT *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + NetGameMaxPacketT Packet; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pRef = DirtyMemAlloc(sizeof(*pRef), NETGAMELINK_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("netgamelink: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pRef, sizeof(*pRef)); + pRef->iMemGroup = iMemGroup; + pRef->pMemGroupUserData = pMemGroupUserData; + + // assign port info + pRef->pPort = pPort; + pRef->iPortOwner = iOwner; + + // allocate input buffer + if (iBufLen < 4096) + { + iBufLen = 4096; + } + pRef->pBuffer = DirtyMemAlloc(iBufLen, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + pRef->iBufMax = iBufLen; + + // reset the timing info + pRef->iRttAvg = 0; + pRef->iRttDev = 0; + + // setup connection time for reference + uTick = NetTick(); + pRef->NetGameLinkStats.conn = uTick; + pRef->NetGameLinkStats.senthide = uTick; + pRef->NetGameLinkStats.rcvdhide = uTick; + pRef->NetGameLinkStats.rcvdlast = uTick; + pRef->NetGameLinkStats.sentlast = uTick; + pRef->NetGameLinkStats.stattick = uTick; + pRef->NetGameLinkStats.isconn = FALSE; + pRef->NetGameLinkStats.isopen = FALSE; + + // fill ping history with bogus starting data + for (iIndex = 0; iIndex < PING_HISTORY; ++iIndex) + { + pRef->NetGameLinkStats.pinghist[iIndex].min = PING_DEFAULT; + pRef->NetGameLinkStats.pinghist[iIndex].max = PING_DEFAULT; + pRef->NetGameLinkStats.pinghist[iIndex].avg = PING_DEFAULT; + pRef->NetGameLinkStats.pinghist[iIndex].cnt = 1; + } + + // setup critical section + NetCritInit(&pRef->Crit, "netgamelink"); + + // setup for callbacks + pPort->refptr = pRef; + pPort->Callback(pPort, _NetGameLinkNotify); + pPort->SendCallback = _NetGameLinkSendCallback; + + // default sending and syncs to enabled + pRef->bSendEnabled = TRUE; + pRef->bSyncEnabled = TRUE; + + // calculate the max packet size based on the cudp max width (minus the stream overhead) + pRef->iStrmMaxPktSize = pPort->maxwid - (sizeof(Packet.body.strm) - sizeof(Packet.body.strm.data)); + + // other QoS items memset() to zero above + pRef->uQosSendInterval = NETGAMELINK_QOS_INTERVAL_MIN; + pRef->uQosPacketSize = NETGAME_DATAPKT_MAXSIZE; + + pRef->iVerbosity = 1; + + return(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkDestroy + + \Description + Destruct the game client + + \Input *pRef - reference pointer + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameLinkDestroy(NetGameLinkRefT *pRef) +{ + // dont need callback + pRef->pPort->Callback(pRef->pPort, NULL); + + // we own the port -- get rid of it + if (pRef->iPortOwner) + pRef->pPort->Destroy(pRef->pPort); + + // free receive buffer + DirtyMemFree(pRef->pBuffer, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + + // free critical section + NetCritKill(&pRef->Crit); + + // free our memory + DirtyMemFree(pRef, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkCallback + + \Description + Register a callback function + + \Input *pRef - reference pointer + \Input *pCallData - caller reference data + \Input *pCallProc - callback function + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameLinkCallback(NetGameLinkRefT *pRef, void *pCallData, void (*pCallProc)(void *pCallData, int32_t iKind)) +{ + pRef->pCallData = pCallData; + pRef->pCallProc = pCallProc; +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkStatus + + \Description + Return current link status + + \Input *pRef - reference pointer + \Input iSelect - selector + \Input iValue - input value + \Input pBuf - output buffer + \Input iBufSize - output buffer size + + \Output + int32_t - selector specific return value + + \Notes + iSelect can be one of the following: + + \verbatim + 'mwid' - returns the commudp packet max width field + 'qlat' - average latency calculated during the initial qos phase + 'sinf' - returns SocketInfo() with iValue being the status selector (iData param is unsupported) + 'slen' - returns current length of send queue + 'sque' - returns TRUE if send queue is empty, else FALSE + 'stat' - Fills out a NetGameLinkStatT with QoS info. pBuf=&NetGameLinkStatT, iBufSize=sizeof(NetGameLinkStatT) + \endverbatim + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameLinkStatus(NetGameLinkRefT *pRef, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + // read commudp mwid field + if (iSelect == 'mwid') + { + return(pRef->pPort->maxwid); + } + if (iSelect == 'qlat') + { + return(pRef->iQosAvgLate); + } + if ((iSelect == 'sinf') && (pRef->pPort != NULL) && (pRef->pPort->sockptr != NULL)) + { + return(SocketInfo(pRef->pPort->sockptr, iValue, 0, pBuf, iBufSize)); + } + if ((iSelect == 'sque') || (iSelect == 'slen')) + { + // get output queue position + int32_t iQueue = pRef->pPort->Send(pRef->pPort, NULL, 0, COMM_FLAGS_RELIABLE); + + if (iSelect == 'sque') + { + // if output queue position is one send queue is empty (assume error=empty) + iQueue = (iQueue > 0) ? (iQueue == 1) : TRUE; + } + return(iQueue); + } + if (iSelect == 'stat') + { + volatile uint32_t uSeqn; + uint32_t uPortStat; + + do + { + // remember pre-update ticks + uSeqn = pRef->NetGameLinkStats.tickseqn; + + // update tick is same as movement tick + pRef->NetGameLinkStats.tick = NetTick(); + + // if things changed during out assignment, do it again + } while (pRef->NetGameLinkStats.tickseqn != uSeqn); + + // update port status + uPortStat = pRef->pPort->Status(pRef->pPort); + pRef->NetGameLinkStats.isopen = ((uPortStat == COMM_CONNECTING) || (uPortStat == COMM_ONLINE)) ? TRUE : FALSE; + pRef->NetGameLinkStats.isopen = pRef->NetGameLinkStats.isopen && (pRef->uQosPacketsToSend == 0) && (pRef->uQosPacketsToRecv == 0); + + // update packet sent counter + pRef->NetGameLinkStats.lpacksent_now = pRef->pPort->packsent; + + // make sure user-provided buffer is large enough to receive a pointer + if ((pBuf != NULL) && (iBufSize >= (int32_t)sizeof(NetGameLinkStatT))) + { + ds_memcpy(pBuf, &pRef->NetGameLinkStats, sizeof(NetGameLinkStatT)); + return(0); + } + else + { + // unhandled + return(-1); + } + } + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkSend + + \Description + Handle incoming packet stream from upper layer + + \Input *pRef - reference pointer + \Input *pPkt - packet list (1 or more) + \Input iLen - size packet list (1=one packet) + + \Output + int32_t - negative=bad error, zero=unable to send now (overflow), positive=bytes sent + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameLinkSend(NetGameLinkRefT *pRef, NetGamePacketT *pPkt, int32_t iLen) +{ + int32_t iCnt = 0, iErr; + uint32_t uCurrTick = NetTick(); + + // get exclusive access + NetCritEnter(&pRef->Crit); + + // see if we need to handle missed event + if (pRef->iProcess > 0) + { + NetPrintf(("netgamelink: processing missed event\n")); + _NetGameLinkProcess(pRef, uCurrTick); + } + + // walk the packet list + while (iLen > 0) + { + if ((iErr = _NetGameLinkSendPacket(pRef, pPkt, uCurrTick)) <= 0) + { + // don't spam on overflow + if (iErr != 0) + { + NetPrintf(("netgamelink: error %d sending packet\n", iErr)); + } + // should _NetGameLinkSendPacket fail at first send attempt, return err to caller. + if (iCnt == 0) + { + iCnt = iErr; + } + break; + } + + // calc packet size for them + pPkt->head.size = (sizeof(pPkt->head)+pPkt->head.len+sizeof(NetGamePacketSyncT)+3)&0x7ffc; + // count the packet size + iCnt += pPkt->head.size; + // stop if this was single packet + if (iLen == 1) + { + break; + } + + // skip to next packet + iLen -= pPkt->head.size; + pPkt = (NetGamePacketT *)(((char *)pPkt)+pPkt->head.size); + } + + // release exclusive access + NetCritLeave(&pRef->Crit); + + // return bytes sent + return(iCnt); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkPeek2 + + \Description + Peek into the buffer, for the first packet type matching mask + + \Input *pRef - reference pointer + \Input **ppPkt - (optional) storage for pointer to current packet data + \Input uMask - which packet types we want. bitmask, addressed by GAME_PACKET values + + \Output + int32_t - buffer size + + \Version 08/08/11 (jrainy) +*/ +/*************************************************************************************************F*/ +int32_t NetGameLinkPeek2(NetGameLinkRefT *pRef, NetGamePacketT **ppPkt, uint32_t uMask) +{ + int32_t iBufLen = pRef->iBufLen; + int32_t iIndex, iKind; + NetGamePacketT *pPkt0; + int32_t iPktSize = 0; + + // I do not understand the reason for && (iBufLen > 0). + // Kept to maintain current behaviour + // TODO: investigate + if ((ppPkt != NULL) && (iBufLen > 0)) + { + // while going through our buffer of received packets + for (iIndex = 0; iIndex < pRef->iBufLen;) + { + // extract size of current head packet + pPkt0 = (NetGamePacketT *)(pRef->pBuffer + iIndex); + iPktSize = pPkt0->head.size; + + // if the current packet matches what we want + + iKind = (((NetGamePacketT *)(pRef->pBuffer + iIndex))->head.kind) & ~GAME_PACKET_SYNC; + + if (uMask & (1 << iKind)) + { + *ppPkt = pPkt0; + break; + } + else + { + // skip over the non-matching packets. + iIndex += iPktSize; + } + } + } + else if (ppPkt != NULL) + { + *ppPkt = NULL; + } + + + // return buffer size + return(iBufLen); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkPeek + + \Description + Peek into the buffer + + \Input *pRef - reference pointer + \Input **ppPkt - (optional) storage for pointer to current packet data + + \Output + int32_t - buffer size + + \Version 12/19/00 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameLinkPeek(NetGameLinkRefT *pRef, NetGamePacketT **ppPkt) +{ + return(NetGameLinkPeek2(pRef, ppPkt, (unsigned)~0)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkRecv2 + + \Description + Outgoing packet stream to upper layer + + \Input *pRef - reference pointer + \Input *pBuf - storage for packet list (1 or more) + \Input iLen - size packet list (1=one packet) + \Input uMask - which packet types we wanted. bitmask, addressed by GAME_PACKET values + + \Output + int32_t - 0=no data available, else number of packets received + + \Version 01/11/10 (jrainy) +*/ +/*************************************************************************************************F*/ +int32_t NetGameLinkRecv2(NetGameLinkRefT *pRef, NetGamePacketT *pBuf, int32_t iLen, uint32_t uMask) +{ + NetGamePacketT *pPkt; + uint32_t uCurrTick = NetTick(); + int32_t iIndex, iKind; + int32_t iLenRead = 0; + int32_t iPktSize = 0; + + // disable callback + NetCritEnter(&pRef->Crit); + + // if no data in queue, check with lower layer + if (pRef->iBufLen == 0) + { + while (_NetGameLinkRecvPacket(pRef, uCurrTick) > 0) + ; + } + + // see if there is anything to process + if (pRef->iBufLen > 0) + { + // while going through our buffer of received packets + for (iIndex = 0; iIndex < pRef->iBufLen;) + { + // extract size of current head packet + pPkt = (NetGamePacketT *)(pRef->pBuffer + iIndex); + iPktSize = pPkt->head.size; + + // don't access pkt past here, as the memmove will overwrite it + + // if the current packet matches what we want + + iKind = (((NetGamePacketT *)(pRef->pBuffer + iIndex))->head.kind) & ~GAME_PACKET_SYNC; + + if (uMask & (1 << iKind)) + { + // if we can fit it in + if ((iPktSize <= iLen) || (iLen == 1)) + { + // copy the packet to return buffer + ds_memcpy(pBuf, pRef->pBuffer + iIndex, iPktSize); + iLen -= iPktSize; + pBuf = (NetGamePacketT *)(((char*)pBuf) + iPktSize); + iLenRead += iPktSize; + + // remove from our list of received packets + memmove(pRef->pBuffer + iIndex, pRef->pBuffer + iIndex + iPktSize, pRef->iBufLen - iIndex - iPktSize); + pRef->iBufLen -= iPktSize; + + // if len was 1, it's now negative, we got our packet, bail out + if (iLen < 0) + { + break; + } + } + else + { + // bail out, we got all we could + break; + } + } + else + { + // skip over the non-matching packets. + iIndex += iPktSize; + } + } + } + + // enable access + NetCritLeave(&pRef->Crit); + + // return length + return(iLenRead); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkRecv + + \Description + Outgoing packet stream to upper layer + + \Input *pRef - reference pointer + \Input *pBuf - storage for packet list (1 or more) + \Input iLen - size packet list (1=one packet) + \Input bDist - whether dist packets are to be received. Use FALSE. + + \Output + int32_t - 0=no data available, else number of packets received + + \Version 01/11/10 (jrainy) +*/ +/*************************************************************************************************F*/ +int32_t NetGameLinkRecv(NetGameLinkRefT *pRef, NetGamePacketT *pBuf, int32_t iLen, uint8_t bDist) +{ + uint32_t uDistMask = (1 << GAME_PACKET_INPUT) | + (1 << GAME_PACKET_INPUT_MULTI) | + (1 << GAME_PACKET_INPUT_MULTI_FAT) | + (1 << GAME_PACKET_STATS) | + (1 << GAME_PACKET_INPUT_FLOW) | + (1 << GAME_PACKET_INPUT_META); + + return(NetGameLinkRecv2(pRef, pBuf, iLen, (bDist ? uDistMask : ~uDistMask) & ~(1 << GAME_PACKET_LINK_STRM))); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkControl + + \Description + NetGameLink control function. Different selectors control different behaviors. + + \Input *pRef - reference pointer + \Input iControl - control selector + \Input iValue - selector-specific value + \Input pValue - selector-specific pointer + + \Output + int32_t - selector specific, or negative if failure + + \Notes + iControl can be one of the following: + + \verbatim + lqos: set individual QoS packet size + rlmt: set redundant packet size limit + send: enable/disable sending + spam: set debug verbosity level + sqos: set QoS duration and interval + sync: enable/disable sync packets + \endverbatim + + Unhandled selectors are passed through to the underlying comm module + + \Version 07/07/03 (jbrookes) +*/ +/*************************************************************************************************F*/ +int32_t NetGameLinkControl(NetGameLinkRefT *pRef, int32_t iControl, int32_t iValue, void *pValue) +{ + // set individual QoS packet size + if (iControl == 'lqos') + { + if ((iValue < NETGAMELINK_QOS_PACKETSIZE_MIN) || (iValue > NETGAME_DATAPKT_MAXSIZE)) + { + NetPrintf(("netgamelink: [%p] invalid QoS packet size specified (%d). QoS packets must be >= %d and <= %d\n", pRef, iValue, NETGAMELINK_QOS_PACKETSIZE_MIN, NETGAME_DATAPKT_MAXSIZE)); + iValue = (iValue < NETGAMELINK_QOS_PACKETSIZE_MIN ? NETGAMELINK_QOS_PACKETSIZE_MIN : iValue); + iValue = (iValue > NETGAME_DATAPKT_MAXSIZE ? NETGAME_DATAPKT_MAXSIZE : iValue); + } + NetPrintf(("netgamelink: [%p] QoS packet size set to %d\n", pRef, iValue)); + pRef->uQosPacketSize = iValue; + return(0); + } + // set redundant packet size limit + if (iControl == 'rlmt') + { + NetPrintf(("netgamelink: [%p] updating redundant packet size limit for underlying comm (%d)\n", pRef, iValue)); + return(pRef->pPort->Control(pRef->pPort, iControl, iValue, pValue)); + } + // set send/recv value + if (iControl == 'send') + { + pRef->bSendEnabled = iValue; + return(1); + } + // set debug verbosity level + if (iControl == 'spam') + { + if (iValue >= 0 && iValue <= 5) + { + pRef->iVerbosity = iValue; + return(1); + } + return(0); + } + // set QoS duration and interval + if (iControl == 'sqos') + { + int32_t iValue2 = *(int32_t*)pValue; + + if (pRef->uQosLastSendTime != 0) + { + NetPrintf(("netgamelink: [%p] cannot change QoS duration or interval while QoS is in progress or is already finished.", pRef)); + return(-1); + } + + if ((iValue < NETGAMELINK_QOS_DURATION_MIN) || (iValue > NETGAMELINK_QOS_DURATION_MAX)) + { + NetPrintf(("netgamelink: [%p] invalid QoS duration period provided (%d). QoS duration must be >= %d and <= %s ms\n", pRef, iValue, NETGAMELINK_QOS_DURATION_MIN, NETGAMELINK_QOS_DURATION_MAX)); + iValue = (iValue < NETGAMELINK_QOS_DURATION_MIN ? NETGAMELINK_QOS_DURATION_MIN : iValue); + iValue = (iValue > NETGAMELINK_QOS_DURATION_MAX ? NETGAMELINK_QOS_DURATION_MAX : iValue); + } + if ((iValue != 0) && ((iValue2 < NETGAMELINK_QOS_INTERVAL_MIN) || (iValue2 > iValue))) + { + NetPrintf(("netgamelink: [%p] invalid QoS interval provided (%d). QoS interval must be >= %d and <= 'QoS duration period'\n", pRef, iValue2, NETGAMELINK_QOS_INTERVAL_MIN)); + iValue2 = (iValue2 < NETGAMELINK_QOS_INTERVAL_MIN ? NETGAMELINK_QOS_INTERVAL_MIN : iValue2); + iValue2 = (iValue2 > iValue ? iValue : iValue2); + } + NetPrintf(("netgamelink: [%p] QoS duration period set to %d ms %s\n", pRef, iValue, ((iValue == 0) ? "(QoS disabled)" : ""))); + + pRef->uQosPacketsToSend = (iValue == 0 ? 0 : iValue / iValue2); + NetPrintf(("netgamelink: [%p] QoS packet count set to %d packets %s\n", pRef, pRef->uQosPacketsToSend, ((pRef->uQosPacketsToSend == 0) ? "(QoS disabled)" : ""))); + + pRef->uQosSendInterval = iValue2; + NetPrintf(("netgamelink: [%p] QoS interval set to %d ms\n", pRef, pRef->uQosSendInterval)); + + return(0); + } + // queue checks (Deprecated use netgamelinkstatus versions instead) $$todo Remove in future release + if ((iControl == 'sque') || (iControl == 'slen')) + { + // get output queue position + int32_t iQueue = pRef->pPort->Send(pRef->pPort, NULL, 0, COMM_FLAGS_RELIABLE); + + if (iControl == 'sque') + { + // if output queue position is one send queue is empty (assume error=empty) + iQueue = (iQueue > 0) ? (iQueue == 1) : TRUE; + } + return(iQueue); + } + // enable/disable sync packets + if (iControl == 'sync') + { + pRef->bSyncEnabled = iValue; + return(0); + } + // unhandled; pass through to comm module if available + return((pRef->pPort != NULL) ? pRef->pPort->Control(pRef->pPort, iControl, iValue, pValue) : -1); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkUpdate + + \Description + Provides NetGameLink with time, at regular interval. Need for streams handling. + Also handles QoS stage when during link startup. + + \Input *pRef - reference pointer + + \Output + uint32_t - zero + + \Version 01/11/10 (jrainy) +*/ +/*************************************************************************************************F*/ +uint32_t NetGameLinkUpdate(NetGameLinkRefT *pRef) +{ + // Are we currently in the QoS phase? + if ((pRef->uQosPacketsToSend > 0) || (pRef->uQosPacketsToRecv > 0)) + { + // Send and receive QoS packets + _NetGameLinkUpdateQos(pRef); + } + else + { + // Handle normal link data + _NetGameLinkDrainRecvStream(pRef); + + _NetGameLinkPollStream(pRef); + } + + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkCreateStream + + \Description + Allocate a stream + + \Input *pRef - module state reference + \Input iSubChan - number of subchannels (zero for a normal stream) + \Input iIdent - unique stream iIdent + \Input iInpLen - size of input buffer + \Input iOutLen - size of output buffer + \Input iSynLen - size of sync buffer + + \Output + NetGameLinkStreamT * - new stream, or NULL if the iIdent was not unique + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +NetGameLinkStreamT *NetGameLinkCreateStream(NetGameLinkRefT *pRef, int32_t iSubChan, int32_t iIdent, int32_t iInpLen, int32_t iOutLen, int32_t iSynLen) +{ + NetGameLinkStreamT *pStream; + int32_t iInpSize; + char *pInpData; + + // make sure the pipe identifier is unique + for (pStream = pRef->m_stream; pStream != NULL; pStream = pStream->pNext) + { + // dont create a duplicate stream + if (pStream->iIdent == iIdent) + { + NetPrintf(("netgamelink: [%p] error -- attempting to create duplicate stream '%c%c%c%c'\n", + pRef, (uint8_t)(iIdent>>24), (uint8_t)(iIdent>>16), (uint8_t)(iIdent>>8), (uint8_t)iIdent)); + return(NULL); + } + } + + // allocate a pipe record + if ((pStream = (NetGameLinkStreamT *) DirtyMemAlloc(sizeof(*pStream), NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) == NULL) + { + NetPrintf(("netgamelink: [%p] unable to allocate stream\n", pRef)); + return(NULL); + } + ds_memclr(pStream, sizeof(*pStream)); + + // setup the data fields + pStream->pClient = pRef; + pStream->iIdent = iIdent; + pStream->iSubchan = iSubChan; + pStream->Send = _NetGameLinkSendStream; + pStream->Recv = NULL; + + // allocate input buffer plus input buffer tracking structure(s) + pStream->iInpMaxm = iInpLen; + iInpSize = sizeof(*pStream->pInp) * (pStream->iSubchan + 1); + pStream->pInp = DirtyMemAlloc(iInpSize + (iInpLen * (pStream->iSubchan + 1)), NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + ds_memclr(pStream->pInp, iInpSize); + for (iSubChan = 0, pInpData = (char *)pStream->pInp + iInpSize; iSubChan < pStream->iSubchan+1; iSubChan++) + { + pStream->pInp[iSubChan].pInpData = pInpData; + pInpData += iInpLen; + } + + // allocate output buffer + if (iOutLen < iInpLen) + { + iOutLen = iInpLen; + } + pStream->iOutMaxm = iOutLen; + pStream->pOutData = DirtyMemAlloc(iOutLen, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + + // allocate sync buffer + pStream->iSynMaxm = iSynLen; + if (iSynLen > 0) + { + pStream->pSynData = DirtyMemAlloc(iSynLen, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + } + + // put into list + pStream->pNext = pRef->m_stream; + pRef->m_stream = pStream; + return(pStream); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameLinkDestroyStream + + \Description + Destroy a stream + + \Input *pRef - reference pointer + \Input *pStream - pointer to stream to destroy + + \Version 12/20/00 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameLinkDestroyStream(NetGameLinkRefT *pRef, NetGameLinkStreamT *pStream) +{ + NetGameLinkStreamT **ppLink; + + // make sure stream is valid + if (pStream != NULL) + { + // if stream is active, remove the link + if (pStream->pClient != NULL) + { + // locate the stream in the list for removal + for (ppLink = &pStream->pClient->m_stream; *ppLink != pStream; ppLink = &((*ppLink)->pNext)) + { + // see if at end of list + if (*ppLink == NULL) + { + return; + } + } + // remove stream from list + *ppLink = pStream->pNext; + } + + // now the tricky part-- make sure nobody is still processing this stream + + // release the resources + if (pStream->pSynData != NULL) + { + DirtyMemFree(pStream->pSynData, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + } + DirtyMemFree(pStream->pInp, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(pStream->pOutData, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(pStream, NETGAMELINK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + } +} + diff --git a/r5dev/thirdparty/dirtysdk/source/game/netgameutil.c b/r5dev/thirdparty/dirtysdk/source/game/netgameutil.c new file mode 100644 index 00000000..a9fae798 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/game/netgameutil.c @@ -0,0 +1,711 @@ +/*H*************************************************************************************************/ +/*! + + \File netgameutil.c + + \Description + This module provides the setup required to bring peer-peer networking + online. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2001-2002. ALL RIGHTS RESERVED. + + \Version 1.0 01/09/01 (GWS) First Version + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/comm/commall.h" +#include "DirtySDK/comm/commudp.h" +#include "DirtySDK/comm/commsrp.h" +#include "DirtySDK/proto/protoadvt.h" +#include "DirtySDK/game/netgameutil.h" +#include "DirtySDK/game/netgamepkt.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! netgameutil internal state +struct NetGameUtilRefT +{ + //! module memory group + int32_t memgroup; + void *memgrpusrdata; + + //! mac->name translation table + char *table; + //! class (unique to app) + char kind[32]; + //! service address list (64-->128 7/25/05 to fix ConnApi overrun GWS) + char addr[128]; + + //! advert ref, for broadcasting hosting info + ProtoAdvtRef *advt; + //! advertising ref, for connecting + ProtoAdvtRef *find; + + //! hosting status: 0=hosting, 1=joining + int32_t hosting; + + //! host ip + uint32_t hostip; + //! host port + uint32_t hostport; + //! peer ip + uint32_t peerip; + //! peer port + uint32_t peerport; + + //! socket ref + SocketT *pSocket; + + //! max packet width + int32_t maxwid; + + //! size of send buffer in packets + int32_t maxout; + + //! size of receive buffer in packets + int32_t maxinp; + + //! unacknowledged packet window + int32_t unacklimit; + + //! advertising frequency, in seconds + int32_t advtfreq; + + //! client identifier (zero == none) + int32_t clientid; + + //! remote client identifier + int32_t rclientid; + + //! metatype + int32_t metatype; + + //! construct function (used for AUTO mode) + CommAllConstructT *pConstruct; + + //! commref of connection, or NULL if no connection + CommRef *comm; + + uint8_t uLocalAdvt; +}; + + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +// Constants + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function _NetGameUtilAdvtConstruct + + \Description + Create the ProtoAdvt module. + + \Input *pRef - pointer to module state + \Input iSize - ProtoAdvtConstruct() parameter + \Input bConnect - TRUE if constructing advt ref for connecting, FALSE if constructing advt ref for broadcasting hosting info + + \Output + ProtoAdvtRefT * - pointer to new advertising module + + \Version 10/13/2005 (jbrookes) +*/ +/*************************************************************************************F*/ +static ProtoAdvtRef *_NetGameUtilAdvtConstruct(NetGameUtilRefT *pRef, int32_t iSize, uint32_t bConnect) +{ + ProtoAdvtRef **ppAdvt = bConnect ? &pRef->find : &pRef->advt; + + if (*ppAdvt == NULL) + { + DirtyMemGroupEnter(pRef->memgroup, pRef->memgrpusrdata); + *ppAdvt = ProtoAdvtConstruct(iSize); + DirtyMemGroupLeave(); + } + return(*ppAdvt); +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilCreate + + \Description + Construct the game setup module + + \Output + NetGameUtilRefT * - reference pointer + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +NetGameUtilRefT *NetGameUtilCreate(void) +{ + NetGameUtilRefT *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pRef = DirtyMemAlloc(sizeof(*pRef), NETGAMEUTIL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("netgameutil: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pRef, sizeof(*pRef)); + pRef->memgroup = iMemGroup; + pRef->memgrpusrdata = pMemGroupUserData; + + // set default comm buffer parameters + NetGameUtilControl(pRef, 'mwid', NETGAME_DATAPKT_DEFSIZE); + NetGameUtilControl(pRef, 'minp', NETGAME_DATABUF_MAXSIZE); + NetGameUtilControl(pRef, 'mout', NETGAME_DATABUF_MAXSIZE); + + // return state + return(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilDestroy + + \Description + Destroy the game setup module + + \Input *ref - reference pointer + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameUtilDestroy(NetGameUtilRefT *ref) +{ + // reset + NetGameUtilReset(ref); + // done with local state + DirtyMemFree(ref, NETGAMEUTIL_MEMID, ref->memgroup, ref->memgrpusrdata); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilReset + + \Description + Reset the game setup module + + \Input *ref - reference pointer + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameUtilReset(NetGameUtilRefT *ref) +{ + // release comm module + if (ref->comm != NULL) + { + ref->comm->Destroy(ref->comm); + ref->comm = NULL; + } + // kill advertisements + if (ref->find != NULL) + { + ProtoAdvtDestroy(ref->find); + ref->find = NULL; + } + if (ref->advt != NULL) + { + ProtoAdvtDestroy(ref->advt); + ref->advt = NULL; + } + + // clear construct ref, if any + ref->pConstruct = NULL; +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilControl + + \Description + Set internal GameUtil parameters. + + \Input *pRef - reference pointer + \Input iKind - selector + \Input iValue - value to set + + \Notes + Selectors: + + \verbatim + 'advf': set advertising frequency, in seconds (call before calling NetGameUtilAdvert) + 'clid': set client identifier* (for use with gameservers) + 'locl': enable or disable query of local adverts + 'meta': set metatype (default=0) + 'minp': set receive buffer size, in packets* + 'mout': set send buffer size, in packets* + 'mwid': set maximum packet width (must be <= NETGAME_DATAPKT_MAXSIZE)* + * must be set before NetGameLinkConnect() is called to be effective. + \endverbatim + + \Version 11/12/03 (JLB) +*/ +/*************************************************************************************************F*/ +void NetGameUtilControl(NetGameUtilRefT *pRef, int32_t iKind, int32_t iValue) +{ + if (iKind == 'clid') + { + pRef->clientid = iValue; + NetPrintf(("netgameutil: setting clid=0x%08x\n", pRef->clientid)); + } + if (iKind == 'rcid') + { + pRef->rclientid = iValue; + NetPrintf(("netgameutil: setting rcid=0x%08x\n", pRef->rclientid)); + } + if (iKind == 'locl') + { + pRef->uLocalAdvt = iValue; + NetPrintf(("netgameutil: setting uLocalAdvt=%d\n", pRef->uLocalAdvt)); + } + if (iKind == 'meta') + { + pRef->metatype = iValue; + NetPrintf(("netgameutil: setting meta=0x%08x\n", pRef->metatype)); + } + if (iKind == 'mwid') + { + if (iValue <= NETGAME_DATAPKT_MAXSIZE) + { + pRef->maxwid = iValue+NETGAME_DATAPKT_MAXTAIL; + NetPrintf(("netgameutil: setting mwid=%d\n", pRef->maxwid)); + } + else + { + NetPrintf(("netgameutil: mwid value of %d is too large\n", iValue)); + } + } + if (iKind == 'minp') + { + pRef->maxinp = iValue; + NetPrintf(("netgameutil: setting minp=%d\n", pRef->maxinp)); + } + if (iKind == 'mout') + { + pRef->maxout = iValue; + NetPrintf(("netgameutil: setting mout=%d\n", pRef->maxout)); + } + if (iKind == 'ulmt') + { + pRef->unacklimit = iValue; + NetPrintf(("netgameutil: setting unacklimit=%d\n", pRef->unacklimit)); + } + + if (iKind == 'advf') + { + pRef->advtfreq = iValue; + NetPrintf(("netgameutil: setting advf=%d\n", pRef->advtfreq)); + } + + // if selector is unhandled, and a comm func is available, pass it on down + if ((pRef->comm != NULL) && (pRef->comm->Control != NULL)) + { + pRef->comm->Control(pRef->comm, iKind, iValue, NULL); + } +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilConnect + + \Description + Establish a connection (connect/listen) + + \Input *ref - reference pointer + \Input conn - connect mode (NETGAME_CONN_*) | comm type (NETGAME_CONN_*) + \Input *addr - service address list + \Input *pConstruct - comm construct function + + \Output + int32_t - zero=success, negative=failure + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameUtilConnect(NetGameUtilRefT *ref, int32_t conn, const char *addr, CommAllConstructT *pConstruct) +{ + int32_t iErr = 0; + + // make sure user specified connect and/or listen, and a valid protocol + if (((conn & NETGAME_CONN_AUTO) == 0) || (pConstruct == NULL)) + { + NetPrintf(("netgameutil: invalid conn param\n")); + return(-100); // using hundred range to not conflic with COMM_* error codes that iErr can be assigned with. + } + + // save the address for later + ds_strnzcpy(ref->addr, addr, sizeof(ref->addr)); + // save host/join mode + ref->hosting = ((conn & NETGAME_CONN_CONNECT) ? 1 : 0); + + NetPrintf(("netgameutil: connect %d %s\n", conn, addr)); + + // release previous comm module + if (ref->comm != NULL) + { + ref->comm->Destroy(ref->comm); + } + + // handle auto mode + if ((conn & NETGAME_CONN_AUTO) == NETGAME_CONN_AUTO) + { + // save construct ref + ref->pConstruct = pConstruct; + // make sure advertising is running + if (_NetGameUtilAdvtConstruct(ref, 8, TRUE) == NULL) + { + NetPrintf(("netgameutil: NetGameUtilConnect() failed to create the ProtoAdvt module.\n")); + return(-101); // using hundred range to not conflic with COMM_* error codes that iErr can be assigned with. + } + ProtoAdvtAnnounce(ref->find, "GmUtil", addr, "", "TCP:~1:1024\tUDP:~1:1024", 0); + return(0); + } + + // mark modules as created by us with our memgroup + DirtyMemGroupEnter(ref->memgroup, ref->memgrpusrdata); + + // create comm module + ref->comm = pConstruct(ref->maxwid, ref->maxinp, ref->maxout); + + // start connect/listen + if (ref->comm != NULL) + { + if (ref->comm->Control != NULL) + { + ref->comm->Control(ref->comm, 'clid', ref->clientid, NULL); + ref->comm->Control(ref->comm, 'rcid', ref->rclientid, NULL); + ref->comm->Control(ref->comm, 'meta', ref->metatype, NULL); + + if (ref->unacklimit != 0) + { + ref->comm->Control(ref->comm, 'ulmt', ref->unacklimit, NULL); + } + } + if (conn & NETGAME_CONN_CONNECT) + { + iErr = ref->comm->Connect(ref->comm, addr); + } + else if (conn & NETGAME_CONN_LISTEN) + { + iErr = ref->comm->Listen(ref->comm, addr); + } + ref->pSocket = ref->comm->sockptr; + // get host ip/port info from commref + ref->hostip = ref->comm->hostip; + ref->hostport = ref->comm->hostport; + } + + DirtyMemGroupLeave(); + return(iErr); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilComplete + + \Description + Check for connection complete + + \Input *ref - reference pointer + + \Output + void * - connection pointer (NULL is no connection) + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +void *NetGameUtilComplete(NetGameUtilRefT *ref) +{ + // see if we are in find mode + if (ref->find != NULL) + { + // if not connecting, see if we can locate someone + if (ref->comm == NULL) + { + char text[256]; + uint32_t peer, host; + peer = ProtoAdvtLocate(ref->find, "GmUtil", ref->addr, &host, 0); + if (peer != 0) + { + NetPrintf(("netgameutil: located peer=%08x, host=%08x\n", peer, host)); + if (peer > host) + { + ref->hostip = peer; + ref->peerip = host; + ds_snzprintf(text, sizeof(text), "%d.%d.%d.%d%s", + (unsigned char)(peer>>24), (unsigned char)(peer>>16), + (unsigned char)(peer>> 8), (unsigned char)(peer>>0), + ref->addr); + NetGameUtilConnect(ref, NETGAME_CONN_CONNECT, text, ref->pConstruct); + } + else + { + ref->hostip = host; + ref->peerip = peer; + ds_strnzcpy(text, ref->addr, sizeof(text)); + NetGameUtilConnect(ref, NETGAME_CONN_LISTEN, text, ref->pConstruct); + } + } + } + } + + // check for a connect + if ((ref->comm != NULL) && (ref->comm->Status(ref->comm) == COMM_ONLINE)) + { + // get peer ip/port info from commref + ref->peerip = ref->comm->peerip; + ref->peerport = ref->comm->peerport; + + // stop any advertising + if (ref->advt != NULL) + { + ProtoAdvtDestroy(ref->advt); + ref->advt = NULL; + } + if (ref->find != NULL) + { + ProtoAdvtDestroy(ref->find); + ref->find = NULL; + } + + NetPrintf(("netgameutil: connection complete\n")); + return(ref->comm); + } + else + { + return(NULL); + } +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilStatus + + \Description + Return status info + + \Input *ref - reference pointer + \Input iSelect - info selector + \Input *pBuf - [out] output buffer + \Input iBufSize - size of output buffer + + \Output + int32_t - status info + + \Notes + iSelect can be one of the following: + + \verbatim + 'host' - TRUE if hosting, else FALSE + 'join' - TRUE if joining, else FALSE + 'hoip' - host ip + 'hprt' - host port + 'peip' - peer ip + 'pprt' - peer port + 'pkrc' - packet received + 'sock' - SocketT socket pointer (in pBuf) + \endverbatim + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameUtilStatus(NetGameUtilRefT *ref, int32_t iSelect, void *pBuf, int32_t iBufSize) +{ + // return host status + if (iSelect == 'host') + { + return(ref->hosting == 0); + } + if (iSelect == 'join') + { + return(ref->hosting == 1); + } + if (iSelect == 'hoip') + { + return(ref->hostip); + } + if (iSelect == 'hprt') + { + return(ref->hostport); + } + if (iSelect == 'pkrc') + { + return(ref->comm->bpackrcvd); + } + if (iSelect == 'peip') + { + return(ref->peerip); + } + if (iSelect == 'pprt') + { + return(ref->peerport); + } + if ((iSelect == 'sock') && (iBufSize == (signed)sizeof(ref->pSocket))) + { + ds_memcpy(pBuf, &ref->pSocket, sizeof(ref->pSocket)); + return(sizeof(ref->pSocket)); + } + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilAdvert + + \Description + Send out an advertisement + + \Input *ref - reference pointer + \Input *kind - class (unique to app) + \Input *name - name to broadcast + \Input *note - notes + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameUtilAdvert(NetGameUtilRefT *ref, const char *kind, const char *name, const char *note) +{ + // see if we need to create module + if (_NetGameUtilAdvtConstruct(ref, 16, FALSE) == NULL) + { + NetPrintf(("netgameutil: NetGameUtilAdvert() failed to create the ProtoAdvt module.\n")); + return; + } + + // save the kind for future queries + ds_strnzcpy(ref->kind, kind, sizeof(ref->kind)); + + // start advertising + ProtoAdvtAnnounce(ref->advt, kind, name, note, "TCP:~1:1024\tUDP:~1:1024", ref->advtfreq); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilWithdraw + + \Description + Withdraw given advertisement + + \Input *ref - reference pointer + \Input *kind - advert kind + \Input *name - advert name + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +void NetGameUtilWithdraw(NetGameUtilRefT *ref, const char *kind, const char *name) +{ + if (ref->advt != NULL) + { + ProtoAdvtCancel(ref->advt, kind, name); + } +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilLocate + + \Description + Find ip address of a specific advertisement + + \Input *ref - reference pointer + \Input *kind - class (unique to app) + \Input *name - advertisement to look for + + \Output + uint32_t - ip address of advertiser or zero if no match + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +uint32_t NetGameUtilLocate(NetGameUtilRefT *ref, const char *kind, const char *name) +{ + // auto-create the advert module if needed + if (_NetGameUtilAdvtConstruct(ref, 16, FALSE) == NULL) + { + NetPrintf(("netgameutil: NetGameUtilLocate() failed to create the ProtoAdvt module.\n")); + return(0); + } + + // allow use of default kind + if (kind == NULL) + { + kind = ref->kind; + } + + // pass to advertising module + return(ProtoAdvtLocate(ref->advt, kind, name, NULL, 0)); +} + +/*F*************************************************************************************************/ +/*! + \Function NetGameUtilQuery + + \Description + Return a list of all advertisements + + \Input *ref - reference pointer + \Input *kind - class (unique to app) + \Input *buf - target buffer + \Input max - target buffer length + + \Output + int32_t - number of matching ads + + \Version 01/09/01 (GWS) +*/ +/*************************************************************************************************F*/ +int32_t NetGameUtilQuery(NetGameUtilRefT *ref, const char *kind, char *buf, int32_t max) +{ + // auto-create the advert module if needed + if (_NetGameUtilAdvtConstruct(ref, 16, FALSE) == NULL) + { + NetPrintf(("netgameutil: NetGameUtilQuery() failed to create the ProtoAdvt module.\n")); + return(0); + } + + // allow use of default kind + if (kind == NULL) + { + kind = ref->kind; + } + + // pass to advert module + return(ProtoAdvtQuery(ref->advt, kind, "", buf, max, ref->uLocalAdvt)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/graph/dirtygif.c b/r5dev/thirdparty/dirtysdk/source/graph/dirtygif.c new file mode 100644 index 00000000..3f22514e --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/graph/dirtygif.c @@ -0,0 +1,1063 @@ +/*H********************************************************************************/ +/*! + \File dirtygif.c + + \Description + Routines to decode a GIF into a raw image and palette. These routines + were written from scratch based on the published specifications and visual + inspection of some public-domain decoders. + + \Notes + References + GIF specification: https://www.w3.org/Graphics/GIF/spec-gif89a.txt + + \Copyright + Copyright (c) 2003-2020 Electronic Arts Inc. + + \Version 11/13/2003 (jbrookes) First Version + \Version 01/28/2020 (jbrookes) Added multiframe (animated) support +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/graph/dirtygif.h" + +/*** Defines **********************************************************************/ + +#define DIRTYGIF_VERBOSE (DIRTYCODE_LOGGING && FALSE) //!< enable verbose logging + +#define DIRTYGIF_MAXBITS (12) //!< max LZW code size +#define DIRTYGIF_MAXCODE ((1 << DIRTYGIF_MAXBITS) - 1) //!< max code value + +/*** Macros ***********************************************************************/ + +//! macro to validate that enough space exists in the file for a particular header +#define DIRTYGIF_Validate(_pData, _pEnd, _size, _retcode) \ +{ \ + if (((_pEnd) - (_pData)) < (_size)) \ + { \ + return(_retcode); \ + } \ +} + +#define DIRTYGIF_ClrCode(_iSize) (1 << (iSize) + 0) +#define DIRTYGIF_EndCode(_iSize) (1 << (iSize) + 1) +#define DIRTYGIF_NewCode(_iSize) (1 << (iSize) + 2) + +/*** Type Definitions *************************************************************/ + +//! decoder state +typedef struct DirtyGifDecoderT +{ + int32_t iCurCodeSize; //!< current code size, in bits + int32_t iClearCode; //!< value of a clear code + int32_t iEndingCode; //!< value of an ending code + int32_t iNewCodes; //!< first available code + int32_t iTopSlot; //!< highest code for current code size + int32_t iSlot; //!< last read code + + int32_t iBytesLeft; //!< number of bytes left in block + int32_t iBitsLeft; //!< number of bits left in block + uint8_t uCurByte; //!< current byte + const uint8_t *pByteStream; //!< pointer to byte stream + + uint8_t uSuffixTable[DIRTYGIF_MAXCODE+1]; //!< suffix table + uint32_t uPrefixTable[DIRTYGIF_MAXCODE+1]; //!< prefix table + + uint8_t uStack[DIRTYGIF_MAXCODE+1]; //!< code stack + uint8_t *pStackPtr; //!< current stack pointer + uint8_t *pStackEnd; //!< end of stack + + int32_t iCode; //!< most recent table code + int32_t iCodeBits; //!< number of bits used for code + int32_t iCode1; + int32_t iCode0; + int32_t iCodeRaw; //!< raw code read from data + + const uint8_t *pCodeData; //!< current image data pointer + const uint8_t *pEndCodeData; //!< end of image data + int32_t uHeight; //!< image height + int32_t uWidth; //!< image width + uint8_t *pScanLine; //!< start of current scan line + uint8_t *pScanLineEnd; //!< end of current scan line + uint8_t *pBufferEnd; //!< end of current buffer line + int32_t iPass; //!< pass for interlaced images + int32_t iIndex; //!< which row within pass + int32_t iStep; //!< interlace step + int32_t iBufHeight; //!< output buffer height + uint8_t *pImageData; //!< output image buffer (byte array) + +} DirtyGifDecoderT; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +static const int32_t stepsize[] = { 8, 8, 4, 2, 0, 1, 0 }; +static const int32_t startrow[] = { 0, 4, 2, 1, 0, 0, 0 }; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _DirtyGifParseColorTable + + \Description + Parse the color table in GIF header. + + \Input *ppColorTable- [out] storage for color table pointer + \Input *pNumColors - [out] storage for color table size + \Input *pGifData - pointer to current location parsing GIF data + \Input *pGifEnd - pointer past end of GIF data + \Input uBits - byte containing color table info + + \Output + const uint8_t * - pointer to gif data after color table (if present) + + \Version 11/13/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_DirtyGifParseColorTable(const uint8_t **ppColorTable, uint16_t *pNumColors, const uint8_t *pGifData, const uint8_t *pGifEnd, uint32_t uBits) +{ + if (uBits & 0x80) + { + // get number of colors + *pNumColors = 2 << (uBits & 0x7); + + // validate color table + if ((pGifEnd-pGifData) < (signed)(*pNumColors*3)) + { + return(NULL); + } + + // ref color table + NetPrintfVerbose((DIRTYGIF_VERBOSE, 0, "dirtygif: pColorTable=%p\n", pGifData)); + *ppColorTable = pGifData; + pGifData += *pNumColors*3; + } + else + { + *ppColorTable = NULL; + *pNumColors = 0; + } + + return(pGifData); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifParseHeader + + \Description + Parse the GIF header. + + \Input *pGifHdr - [out] pointer to GIF header to fill in + \Input *pGifData - pointer to GIF data + \Input *pGifEnd - pointer past the end of GIF data + \Input *pFrames - [out] storage for list of frames or NULL if none + \Input uNumFrames - size of output frame array or zero if no frames output + + \Output + int32_t - nonnegative=success, negative=error + + \Version 01/22/2020 (jbrookes) Rewrote for more advanced parsing +*/ +/********************************************************************************F*/ +static int32_t _DirtyGifParseHeader(DirtyGifHdrT *pGifHdr, const uint8_t *pGifData, const uint8_t *pGifEnd, DirtyGifHdrT *pFrames, uint32_t uNumFrames) +{ + uint8_t uSize, uKind; + uint16_t uDelay; + uint8_t uTransColor=0, bHasAlpha=FALSE; + + // iClearCode header + ds_memclr(pGifHdr, sizeof(*pGifHdr)); + + // validate and skip signature + DIRTYGIF_Validate(pGifData, pGifEnd, 6, -1); + if (memcmp(pGifData, "GIF87a", 6) && memcmp(pGifData, "GIF89a", 6)) + { + return(-2); + } + pGifData += 6; + + // get logical screen width and height + pGifHdr->uWidth = pGifData[0] | pGifData[1] << 8; + pGifHdr->uHeight = pGifData[2] | pGifData[3] << 8; + NetPrintfVerbose((DIRTYGIF_VERBOSE, 0, "dirtygif: logical screen w=%d h=%d\n", pGifHdr->uWidth, pGifHdr->uHeight)); + + // validate logical screen descriptor + DIRTYGIF_Validate(pGifData, pGifEnd, 7, -3); + + // parse global color table info from logical screen descriptor + pGifData = _DirtyGifParseColorTable(&pGifHdr->pColorTable, &pGifHdr->uNumColors, pGifData+7, pGifEnd, pGifData[4]); + + // process blocks + for (uSize = 0, uDelay = 0; ((pGifEnd - pGifData) > 2); ) + { + uKind = pGifData[0]; + + // parse extension + if (uKind == 0x21) + { + uKind = pGifData[1]; + uSize = pGifData[2]; + pGifData += 3; + + NetPrintfVerbose((DIRTYGIF_VERBOSE, 0, "dirtygif: parsing extension block type 0x%02x (size=%d)\n", uKind, uSize)); + + // validate extension + DIRTYGIF_Validate(pGifData, pGifEnd, uSize + 1, -5); + + // parse graphic control extension block + if (uKind == 0xf9) + { + // get alpha + bHasAlpha = pGifData[0] & 0x1; + uTransColor = pGifData[3]; + // get delay time and convert from hundreths of a second to milliseconds + uDelay = (pGifData[1]|(pGifData[2]<<8))*10; + NetPrintfVerbose((DIRTYGIF_VERBOSE, 0, "dirtygif: delay=%d alpha=%d color=%d\n", uDelay, bHasAlpha, uTransColor)); + // skip size plus trailer + pGifData += uSize + 1; + } + // skip application extension block + else if (uKind == 0xff) + { + for (; uSize != 0; uSize = *pGifData++) + { + pGifData += uSize; + } + } + // unhandled extension... just skip it + else + { + pGifData += uSize + 1; + } + } + // parse image block + else if (uKind == 0x2c) + { + DirtyGifHdrT FrameInfo; + uint32_t uBlockCount; + uint8_t uBlockSize; + + DIRTYGIF_Validate(pGifData, pGifEnd, 10, -7); + + // get width and height from image descriptor + ds_memclr(&FrameInfo, sizeof(FrameInfo)); + FrameInfo.uLeft = pGifData[1] | pGifData[2] << 8; + FrameInfo.uTop = pGifData[3] | pGifData[4] << 8; + FrameInfo.uWidth = pGifData[5] | pGifData[6] << 8; + FrameInfo.uHeight = pGifData[7] | pGifData[8] << 8; + FrameInfo.uDelay = uDelay; + FrameInfo.bHasAlpha = bHasAlpha; + FrameInfo.uTransColor = uTransColor; + + // determine if it is an interlaced image + FrameInfo.bInterlaced = (pGifData[9] & 0x40) != 0; + + // parse local color table info from image descriptor + pGifData = _DirtyGifParseColorTable(&FrameInfo.pColorTable, &FrameInfo.uNumColors, &pGifData[10], pGifEnd, pGifData[9]); + + // save image start + FrameInfo.pImageData = pGifData++; + + // parse through image blocks to find end + for (uBlockSize = 0xff, uBlockCount = 0; (pGifData < pGifEnd) && (uBlockSize != 0); pGifData += uBlockSize) + { + uBlockSize = *pGifData++; + } + // save image end + FrameInfo.pImageEnd = pGifData; + + // if first image, copy frame info to main gif header info + if (pGifHdr->uNumFrames == 0) + { + pGifHdr->pImageData = FrameInfo.pImageData; + pGifHdr->pImageEnd = FrameInfo.pImageEnd; + pGifHdr->uWidth = FrameInfo.uWidth; + pGifHdr->uHeight = FrameInfo.uHeight; + pGifHdr->bInterlaced = FrameInfo.bInterlaced; + pGifHdr->uTransColor = FrameInfo.uTransColor; + pGifHdr->bHasAlpha = FrameInfo.bHasAlpha; + } + + // if we have a frames list, write to that + if ((pFrames != NULL) && (pGifHdr->uNumFrames < uNumFrames)) + { + ds_memcpy_s(&pFrames[pGifHdr->uNumFrames], sizeof(*pFrames), &FrameInfo, sizeof(FrameInfo)); + } + + // log frame + NetPrintfVerbose((DIRTYGIF_VERBOSE, 0, "dirtygif: frame %02d] t=%d l=%d w=%d h=%d i=%d bc=%d\n", pGifHdr->uNumFrames, FrameInfo.uTop, FrameInfo.uLeft, FrameInfo.uWidth, FrameInfo.uHeight, FrameInfo.bInterlaced, uBlockCount)); + + // track frame count + pGifHdr->uNumFrames += 1; + } + else + { + // invalid block kind + NetPrintf(("dirtygif: invalid block kind 0x%02x\n", uKind)); + return(-6); + } + } + + NetPrintfVerbose((DIRTYGIF_VERBOSE, 0, "dirtygif: nFrames=%d\n", pGifHdr->uNumFrames)); + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifResetDecoder + + \Description + Reset the LZW decoder. + + \Input *pDecoder - pointer to decoder state + + \Version 11/20/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static void _DirtyGifResetDecoder(DirtyGifDecoderT *pDecoder) +{ + pDecoder->iCurCodeSize = pDecoder->iCodeBits + 1; + pDecoder->iSlot = pDecoder->iNewCodes; + pDecoder->iTopSlot = 1 << pDecoder->iCurCodeSize; + pDecoder->iCode = 0; +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifInitDecoder + + \Description + Init the LZW decoder. + + \Input *pDecoder - pointer to decoder state + \Input *pGifHdr - pointer to GIF header + \Input *pImageData - pointer to output pixel buffer + \Input iStep - output step size + \Input iHeight - output buffer height + + \Version 11/20/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyGifInitDecoder(DirtyGifDecoderT *pDecoder, DirtyGifHdrT *pGifHdr, uint8_t *pImageData, int32_t iStep, int32_t iHeight) +{ + ds_memclr(pDecoder, sizeof(*pDecoder)); + pDecoder->pCodeData = pGifHdr->pImageData; + pDecoder->pEndCodeData = pGifHdr->pImageEnd; + pDecoder->uHeight = pGifHdr->uHeight; + pDecoder->uWidth = pGifHdr->uWidth; + pDecoder->iStep = iStep; + pDecoder->iBufHeight = iHeight; + pDecoder->pImageData = pImageData; + + // set starting imaging location + pDecoder->iPass = (pGifHdr->bInterlaced ? 0 : 5); + pDecoder->iIndex = startrow[pDecoder->iPass]; + pDecoder->pScanLine = NULL; + + // get the initial code length + if (pDecoder->pCodeData == pDecoder->pEndCodeData) + { + return(-2); + } + pDecoder->iCodeBits = *pDecoder->pCodeData++; + if ((pDecoder->iCodeBits < 2) || (pDecoder->iCodeBits > 9)) + { + return(-2); + } + + // setup for decoding + pDecoder->iBytesLeft = pDecoder->iBitsLeft = 0; + pDecoder->iClearCode = 1 << pDecoder->iCodeBits; + pDecoder->iEndingCode = pDecoder->iClearCode + 1; + pDecoder->iNewCodes = pDecoder->iEndingCode + 1; + _DirtyGifResetDecoder(pDecoder); + + // protect against missing iClearCode code + pDecoder->iCode0 = pDecoder->iCode1 = 0; + + // init stack + pDecoder->pStackPtr = pDecoder->uStack; + pDecoder->pStackEnd = pDecoder->uStack+sizeof(pDecoder->uStack); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifGetByte + + \Description + Get next byte from current block. + + If the current block is exhausted, this function advances to the next block, and + returns the first byte from that block. + + \Input *pDecoder - pointer to decoder state + + \Output + int32_t - zero + + \Version 11/20/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyGifGetByte(DirtyGifDecoderT *pDecoder) +{ + // have we consumed all bytes in this block? + if (pDecoder->iBytesLeft <= 0) + { + // get byte length of next block + pDecoder->iBytesLeft = *pDecoder->pCodeData++; + + // validate block + DIRTYGIF_Validate(pDecoder->pCodeData, pDecoder->pEndCodeData, pDecoder->iBytesLeft, -1); + + // point to bytestream for this block + pDecoder->pByteStream = pDecoder->pCodeData; + + // skip decode pointer past block + pDecoder->pCodeData += pDecoder->iBytesLeft; + } + + // get next byte from block + pDecoder->uCurByte = *pDecoder->pByteStream++; + pDecoder->iBytesLeft--; + + // return success to caller + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifGetNextCode + + \Description + Get next code from the current block. + + \Input *pDecoder - pointer to decoder state + + \Output + int32_t - TRUE if we should continue decoding, else FALSE if we've + reached the end code. + + \Version 11/20/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyGifGetNextCode(DirtyGifDecoderT *pDecoder) +{ + // out of bits? + if (pDecoder->iBitsLeft == 0) + { + if (_DirtyGifGetByte(pDecoder) < 0) + { + return(-1); + } + + pDecoder->iBitsLeft += 8; + } + + // add from bit bucket + pDecoder->iCodeRaw = pDecoder->uCurByte >> (8 - pDecoder->iBitsLeft); + + // fill out the code + while (pDecoder->iCurCodeSize > pDecoder->iBitsLeft) + { + if (_DirtyGifGetByte(pDecoder) < 0) + { + return(-1); + } + + pDecoder->iCodeRaw |= pDecoder->uCurByte << pDecoder->iBitsLeft; + pDecoder->iBitsLeft += 8; + } + + // update bits left, and mask to code range + pDecoder->iBitsLeft -= pDecoder->iCurCodeSize; + pDecoder->iCodeRaw &= (1 << pDecoder->iCurCodeSize) - 1; + + // check for end of data + return((pDecoder->iCodeRaw == pDecoder->iEndingCode) ? 0 : 1); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifUpdateDecoder + + \Description + Update decoder state. + + \Input *pDecoder - pointer to decoder state + + \Version 11/20/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static void _DirtyGifUpdateDecoder(DirtyGifDecoderT *pDecoder) +{ + // set up for decode + pDecoder->iCode = pDecoder->iCodeRaw; + + // if we get a bogus code use the last valid code read instead + if (pDecoder->iCode >= pDecoder->iSlot) + { + pDecoder->iCode = pDecoder->iCode0; + if (pDecoder->pStackPtr < pDecoder->pStackEnd) + { + *pDecoder->pStackPtr++ = (uint8_t)pDecoder->iCode1; + } + } + + // push characters onto stack + while (pDecoder->iCode >= pDecoder->iNewCodes) + { + if (pDecoder->pStackPtr >= pDecoder->pStackEnd) + { + // overflow error + break; + } + *pDecoder->pStackPtr++ = pDecoder->uSuffixTable[pDecoder->iCode]; + pDecoder->iCode = pDecoder->uPrefixTable[pDecoder->iCode]; + } + + // push last char onto the uStack + if (pDecoder->pStackPtr < pDecoder->pStackEnd) + { + *pDecoder->pStackPtr++ = (uint8_t)pDecoder->iCode; + } + + // set up new prefix and uSuffixTable + if (pDecoder->iSlot < pDecoder->iTopSlot) + { + pDecoder->iCode1 = pDecoder->iCode; + pDecoder->uSuffixTable[pDecoder->iSlot] = (uint8_t)pDecoder->iCode1; + pDecoder->uPrefixTable[pDecoder->iSlot++] = pDecoder->iCode0; + pDecoder->iCode0 = pDecoder->iCodeRaw; + } + + // if required iSlot number is greater than bit size allows, increase bit size (up to 12 bits) + if ((pDecoder->iSlot >= pDecoder->iTopSlot) && (pDecoder->iCurCodeSize < 12)) + { + pDecoder->iTopSlot <<= 1; + pDecoder->iCurCodeSize++; + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifUpdateBitmap + + \Description + Write decoded string to output bitmap buffer. + + \Input *pDecoder - pointer to decoder state + \Input bVflip - if TRUE, flip image vertically + + \Output + int32_t - one to continue, zero to abort + + \Version 11/20/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyGifUpdateBitmap(DirtyGifDecoderT *pDecoder, uint32_t bVflip) +{ + uint8_t cPixel; + + // pop decoded string off stack into output buffer + for ( ; pDecoder->pStackPtr > pDecoder->uStack; ) + { + // see if we need to calc scanline start + if (pDecoder->pScanLine == NULL) + { + // if we've hit the output buffer height, quit early + if (pDecoder->iIndex == pDecoder->iBufHeight) + { + return(0); + } + + // calc the new line start + if (bVflip) + { + pDecoder->pScanLine = pDecoder->pImageData + ((pDecoder->uHeight-1) * pDecoder->iStep); + if (pDecoder->iIndex < pDecoder->uHeight) + { + pDecoder->pScanLine -= pDecoder->iStep*((pDecoder->uHeight-1)-pDecoder->iIndex); + } + } + else + { + pDecoder->pScanLine = pDecoder->pImageData; + if (pDecoder->iIndex < pDecoder->uHeight) + { + pDecoder->pScanLine += pDecoder->iStep*((pDecoder->uHeight-1)-pDecoder->iIndex); + } + } + + pDecoder->pScanLineEnd = pDecoder->pScanLine+pDecoder->uWidth; + pDecoder->pBufferEnd = pDecoder->pScanLine+pDecoder->iStep; + } + + // read pixel from stack + cPixel = *(--pDecoder->pStackPtr); + + // put translated pixel into output buffer + if (pDecoder->pScanLine < pDecoder->pBufferEnd) + { + *pDecoder->pScanLine = cPixel; + } + pDecoder->pScanLine += 1; + + // see if we are at end of scanline + if (pDecoder->pScanLine == pDecoder->pScanLineEnd) + { + // if width and step size differ, zero fill extra + if (pDecoder->uWidth < pDecoder->iStep) + { + *pDecoder->pScanLine++ = 0x00; + } + + // see if we are done with this pass + if (((pDecoder->iIndex += stepsize[pDecoder->iPass]) >= pDecoder->uHeight) && (stepsize[pDecoder->iPass] > 0)) + { + pDecoder->iIndex = startrow[++pDecoder->iPass]; + } + + // invalidate the scanline pointer + pDecoder->pScanLine = NULL; + } + } + + // normal return condition + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyGifDecodeImage32 + + \Description + Decode a GIF image into a 32bit ARGB direct-color bitmap + + \Input *pGifHdr - pointer to header describing gif to decode + \Input *pImageData - [out] pointer to buffer to write decoded image data to + \Input *pImageDataPrev - pointer to buffer with previous image, or NULL if first image + \Input *p8BitImage - pointer to scratch buffer for 8bit image decoding, or NULL + \Input iBufWidth - width of output buffer in pixels + \Input iBufHeight - height of output buffer in pixels + \Input bVflip - if TRUE, flip image vertically + + \Output + int32_t - positive=number of bytes decoded, negative=error + + \Version 01/28/2020 (jbrookes) Rewrote to handle animated gif frames +*/ +/********************************************************************************F*/ +static int32_t _DirtyGifDecodeImage32(DirtyGifHdrT *pGifHdr, uint8_t *pImageData, const uint8_t *pImageDataPrev, uint8_t *p8BitImage, int32_t iBufWidth, int32_t iBufHeight, uint32_t bVflip) +{ + uint8_t aPaletteData[256][4]; + uint8_t *pSrc, *pDst; + const uint8_t *pSrc32; + int32_t i8BitSize, iWidth, iHeight; + uint8_t bAlpha, bInFrame; + int32_t iError; + + // first, decode palette info + if ((iError = DirtyGifDecodePalette(pGifHdr, (uint8_t *)aPaletteData, (uint8_t *)aPaletteData + sizeof(aPaletteData), 0xff)) < 0) + { + return(iError); + } + + // if we didn't get an 8bit decode buffer, put it at the end of the output buffer + if (p8BitImage == NULL) + { + // calculate size of decoded 8bit image + i8BitSize = pGifHdr->uWidth * pGifHdr->uHeight; + // locate 8bit image at end of buffer + p8BitImage = pImageData + (iBufWidth * iBufHeight * 4) - i8BitSize; + } + + // decode the image + if ((iError = DirtyGifDecodeImage(pGifHdr, p8BitImage, pGifHdr->uWidth, pGifHdr->uHeight, bVflip)) < 0) + { + return(iError); + } + + // now translate the 8bit image to 32bits + for (pSrc = p8BitImage, iHeight = 0; iHeight < iBufHeight; iHeight += 1) + { + for (iWidth = 0; iWidth < iBufWidth; iWidth += 1) + { + // get palette index and read if we have an alpha + bAlpha = pGifHdr->bHasAlpha && (*pSrc == pGifHdr->uTransColor); + // see if our pixel is in frame or not + bInFrame = ((iWidth >= pGifHdr->uLeft) && (iWidth < (pGifHdr->uLeft + pGifHdr->uWidth)) && + (iHeight >= pGifHdr->uTop) && (iHeight < (pGifHdr->uTop + pGifHdr->uHeight))) || + (pImageDataPrev == NULL); + // locate output + pDst = pImageData + ((iHeight * iBufWidth) + iWidth) * 4; + // write output + if (bInFrame && !bAlpha) + { + pDst[0] = aPaletteData[*pSrc][3]; + pDst[1] = aPaletteData[*pSrc][0]; + pDst[2] = aPaletteData[*pSrc][1]; + pDst[3] = aPaletteData[*pSrc][2]; + } + else if (pImageData != pImageDataPrev) + { + pSrc32 = pImageDataPrev + ((iHeight * iBufWidth) + iWidth) * 4; + pDst[0] = pSrc32[0]; + pDst[1] = pSrc32[1]; + pDst[2] = pSrc32[2]; + pDst[3] = pSrc32[3]; + } + + // advance source pointer if we're in frame + if (bInFrame) + { + pSrc += 1; + } + } + } + + // return success + return(0); +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function DirtyGifIdentify + + \Description + Identify if input image is a GIF image. + + \Input *pImageData - pointer to image data + \Input uImageLen - size of image data + + \Output + int32_t - TRUE if a GIF, else FALSE + + \Version 03/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifIdentify(const uint8_t *pImageData, uint32_t uImageLen) +{ + // make sure we have enough data + if (uImageLen < 6) + { + return(0); + } + // see of we're a GIF + if (memcmp(pImageData, "GIF87a", 6) && memcmp(pImageData, "GIF89a", 6)) + { + return(0); + } + return(1); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGifParse + + \Description + Parse GIF header. + + \Input *pGifHdr - [out] pointer to GIF header to fill in + \Input *pGifData - pointer to GIF data + \Input *pGifEnd - pointer past the end of GIF data + + \Version 11/13/2003 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifParse(DirtyGifHdrT *pGifHdr, const uint8_t *pGifData, const uint8_t *pGifEnd) +{ + return(_DirtyGifParseHeader(pGifHdr, pGifData, pGifEnd, NULL, 0)); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGifParseEx + + \Description + Parse GIF header, with extended info + + \Input *pGifHdr - [out] pointer to GIF header to fill in + \Input *pGifData - pointer to GIF data + \Input *pGifEnd - pointer past the end of GIF data + \Input *pFrames - [out] storage for list of frames + \Input uNumFrames - size of output frame array; zero if unknown + + \Version 01/16/2020 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifParseEx(DirtyGifHdrT *pGifHdr, const uint8_t *pGifData, const uint8_t *pGifEnd, DirtyGifHdrT *pFrames, uint32_t uNumFrames) +{ + return(_DirtyGifParseHeader(pGifHdr, pGifData, pGifEnd, pFrames, uNumFrames)); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGifDecodePalette + + \Description + Decode a GIF palette into an RGBA palette. + + \Input *pGifHdr - pointer to GIF header + \Input *pPalette - [out] pointer to output for RGBA palette + \Input *pPaletteEnd - pointer past end of RGBA output buffer + \Input uAlpha - alpha value to use for normal pixels + + \Version 11/13/2003 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifDecodePalette(DirtyGifHdrT *pGifHdr, uint8_t *pPalette, uint8_t *pPaletteEnd, uint8_t uAlpha) +{ + const uint8_t *pColorTable; + uint8_t *pPalStart = pPalette; + + // validate parameters + if ((pGifHdr->pColorTable == NULL) || (pPalette == NULL)) + { + return(-1); + } + + // extract palette colors + for (pColorTable = pGifHdr->pColorTable; pPalette < pPaletteEnd; pPalette += 4, pColorTable += 3) + { + pPalette[0] = pColorTable[0]; + pPalette[1] = pColorTable[1]; + pPalette[2] = pColorTable[2]; + pPalette[3] = uAlpha; + } + + // handle alpha transparency + if (pGifHdr->bHasAlpha) + { + uint8_t *pAlphaColor = pPalStart + (pGifHdr->uTransColor * 4); + if (pAlphaColor < pPaletteEnd) + { + pAlphaColor[3] = 0x00; + } + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGifDecodeImage + + \Description + Decode a GIF image into an 8bit paletteized bitmap. + + \Input *pGifHdr - pointer to header describing gif to decode + \Input *pImageData - [out] pointer to buffer to write decoded image data to + \Input iBufWidth - width of output buffer + \Input iBufHeight - height of output buffer + \Input bVflip - if TRUE, flip image vertically + + \Output + int32_t - positive=number of bytes decoded, negative=error + + \Version 11/13/2003 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifDecodeImage(DirtyGifHdrT *pGifHdr, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight, uint32_t bVflip) +{ + DirtyGifDecoderT Decoder, *pDecoder = &Decoder; + + // init the decoder + if (_DirtyGifInitDecoder(pDecoder, pGifHdr, pImageData, iBufWidth, iBufHeight) < 0) + { + return(-1); + } + + // decode the image + for (;;) + { + // get next code + if (_DirtyGifGetNextCode(pDecoder) <= 0) + { + break; + } + + // iClearCode code? + if (pDecoder->iCodeRaw == pDecoder->iClearCode) + { + // reset the decoder + _DirtyGifResetDecoder(pDecoder); + + // get code following iClearCode code + if (_DirtyGifGetNextCode(pDecoder) <= 0) + { + break; + } + + // if code is out of range, set code=0 to protect against broken encoders + if (pDecoder->iCodeRaw >= pDecoder->iSlot) + { + pDecoder->iCodeRaw = 0; + } + + // update + pDecoder->iCode0 = pDecoder->iCode1 = pDecoder->iCodeRaw; + + // push the single value onto stack + if (pDecoder->pStackPtr < pDecoder->pStackEnd) + { + *pDecoder->pStackPtr++ = (uint8_t)pDecoder->iCodeRaw; + } + } + else + { + // decode code to stack and update decoder + _DirtyGifUpdateDecoder(pDecoder); + } + + // write any decoded codes into bitmap + if (_DirtyGifUpdateBitmap(pDecoder, bVflip) == 0) + { + // buffer is too small in height - bail early + break; + } + } + + // number number of bytes processed + return((int32_t)(pDecoder->pCodeData - pGifHdr->pImageData)); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGifDecodeImage32 + + \Description + Decode a GIF image into a 32bit ARGB direct-color bitmap. + + \Input *pGifHdr - pointer to header describing gif to decode + \Input *pImageData - [out] pointer to buffer to write decoded image data to + \Input iBufWidth - width of output buffer in pixels + \Input iBufHeight - height of output buffer in pixels + \Input bVflip - if TRUE, flip image vertically + + \Output + int32_t - positive=number of bytes decoded, negative=error + + \Notes + This version may not always decode the first frame of a multi-frame GIF + correctly; use DirtyGifDecodeImage32Multi() with iNumFrames=1 instead. + + \Version 03/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifDecodeImage32(DirtyGifHdrT *pGifHdr, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight, uint32_t bVflip) +{ + return(_DirtyGifDecodeImage32(pGifHdr, pImageData, NULL, NULL, iBufWidth, iBufHeight, bVflip)); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGifDecodeImage32Multi + + \Description + Decode a GIF image into a multiple 32bit ARGB direct-color bitmaps + + \Input *pGifHdr - pointer to header describing gif to decode + \Input *pFrameInfo - frame info + \Input *pImageData - [out] pointer to buffer to write decoded images to + \Input iBufWidth - width of output buffer in pixels + \Input iBufHeight - height of output buffer in pixels + \Input iNumFrames - number of image frames to decode + \Input bVflip - if TRUE, flip image vertically + + \Output + int32_t - positive=number of bytes decoded, negative=error + + \Version 01/28/2020 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifDecodeImage32Multi(DirtyGifHdrT *pGifHdr, DirtyGifHdrT *pFrameInfo, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight, int32_t iNumFrames, uint32_t bVflip) +{ + int32_t iFrame, iFrameSize, iResult; + DirtyGifHdrT GifHdr; + uint8_t *pImageDataPrev; + + for (iFrame = 0, iResult = 0, iFrameSize = iBufWidth*iBufHeight*4, pImageDataPrev = NULL; (iFrame < iNumFrames) && (iResult == 0); iFrame += 1) + { + // copy frame info + ds_memcpy_s(&GifHdr, sizeof(GifHdr), &pFrameInfo[iFrame], sizeof(pFrameInfo[iFrame])); + + // use global color table if not set for this frame + if (pFrameInfo[iFrame].pColorTable == NULL) + { + GifHdr.pColorTable = pGifHdr->pColorTable; + GifHdr.uNumColors = pGifHdr->uNumColors; + } + + // decode to current buffer + iResult = _DirtyGifDecodeImage32(&GifHdr, pImageData + iFrame*iFrameSize, pImageDataPrev, NULL, iBufWidth, iBufHeight, bVflip); + + // remember current frame for next decode + pImageDataPrev = pImageData + iFrame*iFrameSize; + } + + // return most recent result code + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGifDecodeImage32Frame + + \Description + Decode a specific frame of a GIF image into a single 32bit ARGB direct-color + bitmap. The frame order must be from the first frame to the last in sequence + and the image data output buffer must be preserved between calls, except when + decoding the first frame. + + \Input *pGifHdr - pointer to header describing gif to decode + \Input *pFrameInfo - frame info + \Input *pImageData - [out] pointer to buffer to write decoded image to + \Input *p8BitImage - pointer to scratch buffer for 8bit image decoding + \Input iBufWidth - width of output buffer in pixels + \Input iBufHeight - height of output buffer in pixels + \Input iFrame - frame to decode + \Input iNumFrames - number of image frames to decode + \Input bVflip - if TRUE, flip image vertically + + \Output + int32_t - positive=numb + + \Version 02/06/2020 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGifDecodeImage32Frame(DirtyGifHdrT *pGifHdr, DirtyGifHdrT *pFrameInfo, uint8_t *pImageData, uint8_t *p8BitImage, int32_t iBufWidth, int32_t iBufHeight, int32_t iFrame, int32_t iNumFrames, uint32_t bVflip) +{ + DirtyGifHdrT GifHdr; + + // copy frame info for the frame we want to decode + ds_memcpy_s(&GifHdr, sizeof(GifHdr), &pFrameInfo[iFrame], sizeof(pFrameInfo[iFrame])); + + // use global color table if not set for this frame + if (pFrameInfo[iFrame].pColorTable == NULL) + { + GifHdr.pColorTable = pGifHdr->pColorTable; + GifHdr.uNumColors = pGifHdr->uNumColors; + } + + // decode to current buffer and return result to caller + return(_DirtyGifDecodeImage32(&GifHdr, pImageData, pImageData, p8BitImage, iBufWidth, iBufHeight, bVflip)); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/graph/dirtygraph.c b/r5dev/thirdparty/dirtysdk/source/graph/dirtygraph.c new file mode 100644 index 00000000..400948e4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/graph/dirtygraph.c @@ -0,0 +1,416 @@ +/*H********************************************************************************/ +/*! + \File dirtygraph.c + + \Description + Routines for decoding an encoded graphics image. + + \Copyright + Copyright (c) 2006-2020 Electronic Arts Inc. + + \Version 03/09/2006 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/graph/dirtygraph.h" +#include "DirtySDK/graph/dirtygif.h" +#include "DirtySDK/graph/dirtyjpg.h" +#include "DirtySDK/graph/dirtypng.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct DirtyGraphBufferT +{ + uint8_t *pBuffer; + int32_t iBufWidth; + int32_t iBufHeight; +} DirtyGraphBufferT; + +struct DirtyGraphRefT +{ + //! module memory group + int32_t iMemGroup; + void *pMemGroupUserData; + + DirtyGifHdrT *pGifFrameInfo; + DirtyGraphBufferT GifImage8; + DirtyJpgStateT *pJpg; + DirtyPngStateT *pPng; + DirtyGifHdrT GifHdr; + DirtyJpgHdrT JpgHdr; + DirtyPngHdrT PngHdr; +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function DirtyGraphCreate + + \Description + Create the DirtyGraph module. + + \Output + DirtyGraphStateT * - module state, or NULL if unable to create + + \Version 03/09/2006 (jbrookes) First Version +*/ +/********************************************************************************F*/ +DirtyGraphRefT *DirtyGraphCreate(void) +{ + DirtyGraphRefT *pDirtyGraph; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pDirtyGraph = DirtyMemAlloc(sizeof(*pDirtyGraph), DIRTYGRAPH_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtygraph: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pDirtyGraph, sizeof(*pDirtyGraph)); + pDirtyGraph->iMemGroup = iMemGroup; + pDirtyGraph->pMemGroupUserData = pMemGroupUserData; + + // allocate jpeg module state + if ((pDirtyGraph->pJpg = DirtyJpgCreate()) == NULL) + { + NetPrintf(("dirtygraph: unable to allocate jpg module state\n")); + } + + // allocate png module state + if ((pDirtyGraph->pPng = DirtyPngCreate()) == NULL) + { + NetPrintf(("dirtygraph: unable to allocate png module state\n")); + } + + // return module state ref + return(pDirtyGraph); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGraphDestroy + + \Description + Destroy the DirtyGraph module. + + \Input *pDirtyGraph - pointer to module state + + \Output + None. + + \Version 03/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyGraphDestroy(DirtyGraphRefT *pDirtyGraph) +{ + // destroy gif 8bit frame buffer? + if (pDirtyGraph->GifImage8.pBuffer != NULL) + { + DirtyMemFree(pDirtyGraph->GifImage8.pBuffer, DIRTYGRAPH_MEMID, pDirtyGraph->iMemGroup, pDirtyGraph->pMemGroupUserData); + } + // destroy gif frame info? + if (pDirtyGraph->pGifFrameInfo != NULL) + { + DirtyMemFree(pDirtyGraph->pGifFrameInfo, DIRTYGRAPH_MEMID, pDirtyGraph->iMemGroup, pDirtyGraph->pMemGroupUserData); + } + // destroy jpeg module? + if (pDirtyGraph->pJpg != NULL) + { + DirtyJpgDestroy(pDirtyGraph->pJpg); + } + // destroy png module? + if (pDirtyGraph->pPng != NULL) + { + DirtyPngDestroy(pDirtyGraph->pPng); + } + // free module state + DirtyMemFree(pDirtyGraph, DIRTYGRAPH_MEMID, pDirtyGraph->iMemGroup, pDirtyGraph->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGraphDecodeHeader + + \Description + Decode an image header + + \Input *pDirtyGraph - pointer to module state + \Input *pInfo - [out] storage for image info + \Input *pImageData - pointer to input image info + \Input uImageLen - size of input image + + \Output + int32_t - negative=error, else success + + \Version 03/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGraphDecodeHeader(DirtyGraphRefT *pDirtyGraph, DirtyGraphInfoT *pInfo, const uint8_t *pImageData, uint32_t uImageLen) +{ + int32_t iError, iResult=-1; + + // init info structure + ds_memclr(pInfo, sizeof(*pInfo)); + pInfo->pImageData = pImageData; + pInfo->uLength = uImageLen; + + // try and identify image + if (DirtyGifIdentify(pImageData, uImageLen)) + { + if ((iError = DirtyGifParse(&pDirtyGraph->GifHdr, pImageData, pImageData+uImageLen)) == 0) + { + pInfo->uType = DIRTYGRAPH_IMAGETYPE_GIF; + pInfo->iWidth = pDirtyGraph->GifHdr.uWidth; + pInfo->iHeight = pDirtyGraph->GifHdr.uHeight; + pInfo->uNumFrames = pDirtyGraph->GifHdr.uNumFrames; + // if we have a previous gif frame buffer, destroy it now + if (pDirtyGraph->pGifFrameInfo != NULL) + { + DirtyMemFree(pDirtyGraph->pGifFrameInfo, DIRTYGRAPH_MEMID, pDirtyGraph->iMemGroup, pDirtyGraph->pMemGroupUserData); + } + // now allocate and parse frame data + if ((pDirtyGraph->pGifFrameInfo = DirtyMemAlloc(sizeof(*pDirtyGraph->pGifFrameInfo)*pInfo->uNumFrames, DIRTYGRAPH_MEMID, pDirtyGraph->iMemGroup, pDirtyGraph->pMemGroupUserData)) != NULL) + { + iResult = DirtyGifParseEx(&pDirtyGraph->GifHdr, pImageData, pImageData+uImageLen, pDirtyGraph->pGifFrameInfo, pInfo->uNumFrames); + } + else + { + NetPrintf(("dirtygraph: unable to allocate memory for gif frames list\n")); + iResult = -1; + } + } + else + { + NetPrintf(("dirtygraph: error %d parsing gif image\n", iError)); + } + + } + else if ((pDirtyGraph->pJpg != NULL) && DirtyJpgIdentify(pDirtyGraph->pJpg, pImageData, uImageLen)) + { + if ((iError = DirtyJpgDecodeHeader(pDirtyGraph->pJpg, &pDirtyGraph->JpgHdr, pImageData, uImageLen)) == DIRTYJPG_ERR_NONE) + { + pInfo->uType = DIRTYGRAPH_IMAGETYPE_JPG; + pInfo->iWidth = pDirtyGraph->JpgHdr.uWidth; + pInfo->iHeight = pDirtyGraph->JpgHdr.uHeight; + pInfo->uNumFrames = 1; + iResult = 0; + } + else + { + NetPrintf(("dirtygraph: error %d parsing jpg image\n", iError)); + } + } + else if ((pDirtyGraph->pPng != NULL) && DirtyPngIdentify(pImageData, uImageLen)) + { + if ((iError = DirtyPngParse(pDirtyGraph->pPng, &pDirtyGraph->PngHdr, pImageData, pImageData+uImageLen)) == DIRTYPNG_ERR_NONE) + { + pInfo->uType = DIRTYGRAPH_IMAGETYPE_PNG; + pInfo->iWidth = pDirtyGraph->PngHdr.uWidth; + pInfo->iHeight = pDirtyGraph->PngHdr.uHeight; + pInfo->uNumFrames = 1; + iResult = 0; + } + else + { + NetPrintf(("dirtygraph: error %d parsing png image\n", iError)); + } + } + else + { + NetPrintf(("dirtygraph: cannot parse image of unknown type\n")); + pInfo->uType = DIRTYGRAPH_IMAGETYPE_UNKNOWN; + pInfo->pImageData = NULL; + pInfo->uLength = 0; + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGraphGetImageInfo + + \Description + Get extended information about an image + + \Input *pDirtyGraph - pointer to module state + \Input *pInfo - image information (filled in by DirtyGraphDecodeHeader) + \Input iSelect - info selector + \Input *pBuffer - [out] selector output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector return + + \Notes + Selectors are: + + \verbatim + 'anim' fills buffer with int32_t anim info (delay) in milliseconds, and returns number of frames + \endverbatim + + \Version 01/27/2020 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGraphGetImageInfo(DirtyGraphRefT *pDirtyGraph, DirtyGraphInfoT *pInfo, int32_t iSelect, void *pBuffer, int32_t iBufSize) +{ + if ((iSelect == 'anim') && (pInfo->uType == DIRTYGRAPH_IMAGETYPE_GIF) && (pBuffer != NULL) && (iBufSize == (signed)(pInfo->uNumFrames*sizeof(int32_t)))) + { + int32_t *pDelayBuf = (int32_t *)pBuffer, iFrame; + // collect delay info in integer output array + for (iFrame = 0; iFrame < pInfo->uNumFrames; iFrame += 1) + { + pDelayBuf[iFrame] = pDirtyGraph->pGifFrameInfo[iFrame].uDelay; + } + // return count of delay frame values copied to output buffer + return(iFrame); + } + // unhandled + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function DirtyGraphDecodeImage + + \Description + Decode an image into the given output buffer. + + \Input *pDirtyGraph - pointer to module state + \Input *pInfo - image information (filled in by DirtyGraphDecodeHeader) + \Input *pImageBuf - [out] pointer to buffer to store decoded image + \Input iBufWidth - width of output buffer + \Input iBufHeight - height of output buffer + + \Output + int32_t - negative=error, else success + + \Version 03/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGraphDecodeImage(DirtyGraphRefT *pDirtyGraph, DirtyGraphInfoT *pInfo, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight) +{ + if (pInfo->uType == DIRTYGRAPH_IMAGETYPE_GIF) + { + return(DirtyGifDecodeImage32Multi(&pDirtyGraph->GifHdr, pDirtyGraph->pGifFrameInfo, pImageBuf, iBufWidth, iBufHeight, 1, TRUE)); + } + else if (pInfo->uType == DIRTYGRAPH_IMAGETYPE_JPG) + { + return(DirtyJpgDecodeImage(pDirtyGraph->pJpg, &pDirtyGraph->JpgHdr, pImageBuf, iBufWidth, iBufHeight)); + } + else if (pInfo->uType == DIRTYGRAPH_IMAGETYPE_PNG) + { + return(DirtyPngDecodeImage(pDirtyGraph->pPng, &pDirtyGraph->PngHdr, pImageBuf, iBufWidth, iBufHeight)); + } + else + { + NetPrintf(("dirtygraph: cannot decode image of unknown type\n")); + return(-1); + } +} + +/*F********************************************************************************/ +/*! + \Function DirtyGraphDecodeImageMulti + + \Description + Decode a multiframe image into an array of images in the given output buffer. + + \Input *pDirtyGraph - pointer to module state + \Input *pInfo - image information (filled in by DirtyGraphDecodeHeader) + \Input *pImageBuf - [out] pointer to buffer to store decoded images + \Input iBufWidth - width of output buffer + \Input iBufHeight - height of output buffer + + \Output + int32_t - negative=error, else success + + \Version 01/28/2020 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGraphDecodeImageMulti(DirtyGraphRefT *pDirtyGraph, DirtyGraphInfoT *pInfo, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight) +{ + if ((pInfo->uType == DIRTYGRAPH_IMAGETYPE_GIF) && (pDirtyGraph->pGifFrameInfo != NULL)) + { + return(DirtyGifDecodeImage32Multi(&pDirtyGraph->GifHdr, pDirtyGraph->pGifFrameInfo, pImageBuf, iBufWidth, iBufHeight, pDirtyGraph->GifHdr.uNumFrames, TRUE)); + } + else + { + NetPrintf(("dirtygraph: multi-image decoding not supported for image type\n")); + return(-1); + } +} + +/*F********************************************************************************/ +/*! + \Function DirtyGraphDecodeImageFrame + + \Description + Decode a multiframe image into the given output buffer. The frame order must + be from the first frame to the last in sequence and the image data output + buffer must be preserved between calls, except when decoding the first frame. + + \Input *pDirtyGraph - pointer to module state + \Input *pInfo - image information (filled in by DirtyGraphDecodeHeader) + \Input *pImageBuf - [out] pointer to buffer to store decoded image + \Input iBufWidth - width of output buffer + \Input iBufHeight - height of output buffer + \Input iFrame - frame to decode + + \Output + int32_t - negative=error, else success + + \Version 02/06/2020 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyGraphDecodeImageFrame(DirtyGraphRefT *pDirtyGraph, DirtyGraphInfoT *pInfo, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight, int32_t iFrame) +{ + if ((pInfo->uType == DIRTYGRAPH_IMAGETYPE_GIF) && (pDirtyGraph->pGifFrameInfo != NULL)) + { + // if eight-bit frame cache exists but the frame size has changed, kill it + if ((pDirtyGraph->GifImage8.pBuffer != NULL) && ((pDirtyGraph->GifImage8.iBufWidth != iBufWidth) || (pDirtyGraph->GifImage8.iBufHeight != iBufHeight))) + { + DirtyMemFree(pDirtyGraph->GifImage8.pBuffer, DIRTYGRAPH_MEMID, pDirtyGraph->iMemGroup, pDirtyGraph->pMemGroupUserData); + pDirtyGraph->GifImage8.pBuffer = NULL; + } + // allocate eight-bit frame cache + if (pDirtyGraph->GifImage8.pBuffer == NULL) + { + if ((pDirtyGraph->GifImage8.pBuffer = DirtyMemAlloc(iBufWidth * iBufHeight, DIRTYGRAPH_MEMID, pDirtyGraph->iMemGroup, pDirtyGraph->pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtygraph: could not allocate 8bit decode buffer\n")); + return(-1); + } + pDirtyGraph->GifImage8.iBufWidth = iBufWidth; + pDirtyGraph->GifImage8.iBufHeight = iBufHeight; + } + // decode the frame and return result to caller + return(DirtyGifDecodeImage32Frame(&pDirtyGraph->GifHdr, pDirtyGraph->pGifFrameInfo, pImageBuf, pDirtyGraph->GifImage8.pBuffer, iBufWidth, iBufHeight, iFrame, pDirtyGraph->GifHdr.uNumFrames, TRUE)); + } + else + { + NetPrintf(("dirtygraph: multi-image decoding not supported for image type\n")); + return(-1); + } +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/source/graph/dirtyjpg.c b/r5dev/thirdparty/dirtysdk/source/graph/dirtyjpg.c new file mode 100644 index 00000000..1fd40654 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/graph/dirtyjpg.c @@ -0,0 +1,1967 @@ +/*H********************************************************************************/ +/*! + \File dirtyjpg.c + + \Description + Implements JFIF/EXIF image decoding to an output 32bit buffer. This decoder + does not support progressive, lossless, or arithmetic options. + + \Notes + References: + [1] http://www.w3.org/Graphics/JPEG/jfif3.pdf + [2] http://www.w3.org/Graphics/JPEG/itu-t81.pdf + [3] http://class.ece.iastate.edu/ee528/Reading%20material/JPEG_File_Format.pdf + [4] http://www.bsdg.org/SWAG/GRAPHICS/0143.PAS.html + [5] http://www.exif.org/Exif2-2.PDF + + \Copyright + Copyright (c) 2006 Electronic Arts Inc. + + \Version 02/23/2006 (jbrookes) First version, based on gshaefer code +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/graph/dirtyjpg.h" + +/*** Defines **********************************************************************/ + +#define DIRTYJPG_DEBUG (TRUE) +#define DIRTYJPG_VERBOSE (DIRTYJPG_DEBUG && FALSE) +#define DIRTYJPG_DEBUG_HUFF (DIRTYJPG_DEBUG && FALSE) +#define DIRTYJPG_DEBUG_IDCT (DIRTYJPG_DEBUG && FALSE) + +#if !DIRTYJPG_DEBUG_IDCT + #define _PrintMatrix16(_pMatrix, _pTitle) {;} +#endif + +#define MCU_SIZE (8*8*4) // 8x8 32-bit +#define MCU_MAXSIZE (MCU_SIZE*4*4) // max size is 4x4 blocks + +// LLM constants +#define DCTSIZE 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#define FIX_0_298631336 ((int32_t) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((int32_t) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((int32_t) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((int32_t) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((int32_t) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((int32_t) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((int32_t) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((int32_t) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((int32_t) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((int32_t) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((int32_t) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((int32_t) 25172) /* FIX(3.072711026) */ + +/*** Type Definitions *************************************************************/ + +//! quantization/matrix +typedef uint16_t QuantTableT[64]; + +//! huffman decode table head +typedef struct HuffHeadT +{ + uint16_t uShift; //!< number of shifts to normalize + uint16_t uMinCode; //!< min code in this table + uint16_t uMaxCode; //!< max code in this table + uint16_t uOffset; //!< offset to value/shift data +} HuffHeadT; + +//! huffman decode table +typedef struct HuffTableT +{ + HuffHeadT Head[10]; //!< table code range + uint8_t Code[512]; //!< code lookup + uint8_t Step[512]; //!< code length lookup +} HuffTableT; + +//! component table +typedef struct CompTableT +{ + uint16_t uMcuHorz; //!< number of 8x8 blocks horizontally in an mcu + uint16_t uMcuVert; //!< number of 8x8 blocks vertically in an mcu + uint16_t uCompHorz; //!< horizontal sampling factor + uint16_t uCompVert; //!< vertical sampling factor + uint16_t uExpHorz; //!< horizontal expansion factor + uint16_t uExpVert; //!< vertical expansion factor + + uint32_t uStepHorz0; //!< horizontal step + uint32_t uStepVert0; //!< vertical step + uint32_t uStepHorz1; //!< horizontal scaled step + uint32_t uStepVert1; //!< vertical scaled stemp + uint32_t uStepHorz8; //!< horizontal step*8 + uint32_t uStepVert8; //!< horizontal step*8 + uint32_t uStepHorzM; //!< horizontal mcu step + uint32_t uStepVertM; //!< vertical mcu step + + uint16_t uQuantNum; //!< quant table to use + + HuffTableT *pACHuff; //!< pointer to current AC huffman decode table + HuffTableT *pDCHuff; //!< pointer to current DC huffman decode table + + void (*pExpand)(DirtyJpgStateT *pState, struct CompTableT *pCompTable, uint32_t uOutOffset); + void (*pInvert)(DirtyJpgStateT *pState, struct CompTableT *pCompTable, uint32_t uOutOffset); + + QuantTableT Matrix; //!< working matrix for decoding this component + QuantTableT Quant; //!< current quant table + +} CompTableT; + +//! module state +struct DirtyJpgStateT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + int32_t iLastError; //!< previous error + uint32_t uLastOffset; //!< offset where decoding previously stopped + + uint16_t uVersion; //!< JFIF file version + uint16_t uAspectRatio; //!< image aspect ratio + uint16_t uAspectHorz; //!< horizontal aspect ratio + uint16_t uAspectVert; //!< vertical aspect ratio + + uint16_t uImageWidth; //!< width of encoded image + uint16_t uImageHeight; //!< height of encoded image + + uint16_t uMcuHorz; //!< maxumum horizontal MCU, out of all components + uint16_t uMcuHorzM; //!< number of MCUs to cover image horizontall + uint16_t uMcuVert; //!< maximum vertical MCU, out of all components + uint16_t uMcuVertM; //!< number of MCUs to cover image vertically + + uint32_t uBitfield; //!< current bit buffer + uint32_t uBitsAvail; //!< number of bits in bit buffer + uint32_t uBitsConsumed; //!< number of bits consumed from bitstream + uint32_t uBytesRead; //!< number of bytes read from bitstream + + CompTableT CompTable[4]; //!< component tables + QuantTableT QuantTable[16]; //!< quantization tables + HuffTableT HuffTable[8]; //!< huffman decode tables + uint8_t aMCU[MCU_MAXSIZE]; //!< mcu decode buffer + + const uint8_t *pCompData; //!< pointer to compressed component data + + uint8_t *pImageBuf; //!< pointer to output image buffer (allocated by caller) + const uint8_t *pSrcFinal; + + uint32_t uBufWidth; //!< width of buffer to decode into + uint32_t uBufHeight; //!< height of buffer to decode into + + uint8_t bRestart; //!< if TRUE, a restart marker has been processed + uint8_t _pad[3]; +}; + +/*** Variables ********************************************************************/ + +//! helper table to negate a value based on its bitsize +static const uint16_t _ZagAdj_adjust[] = +{ + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, + 0x0fff, 0x1fff, 0x3fff, 0x7fff +}; + +//! jpeg zig-zag sequence table +static const uint16_t _ZagAdj_zag[] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static const int16_t _Mult07_14h[] = +{ + 1462, 1451, 1439, 1428, 1416, 1405, 1394, 1382, 1371, 1359, 1348, 1336, 1325, 1314, 1302, 1291, + 1279, 1268, 1256, 1245, 1234, 1222, 1211, 1199, 1188, 1176, 1165, 1154, 1142, 1131, 1119, 1108, + 1096, 1085, 1074, 1062, 1051, 1039, 1028, 1016, 1005, 994, 982, 971, 959, 948, 936, 925, + 914, 902, 891, 879, 868, 856, 845, 834, 822, 811, 799, 788, 776, 765, 754, 742, + 731, 719, 708, 697, 685, 674, 662, 651, 639, 628, 617, 605, 594, 582, 571, 559, + 548, 537, 525, 514, 502, 491, 479, 468, 457, 445, 434, 422, 411, 399, 388, 377, + 365, 354, 342, 331, 319, 308, 297, 285, 274, 262, 251, 239, 228, 217, 205, 194, + 182, 171, 159, 148, 137, 125, 114, 102, 91, 79, 68, 57, 45, 34, 22, 11, + 0, -11, -22, -34, -45, -57, -68, -79, -91, -102, -114, -125, -137, -148, -159, -171, + -182, -194, -205, -217, -228, -239, -251, -262, -274, -285, -297, -308, -319, -331, -342, -354, + -365, -377, -388, -399, -411, -422, -434, -445, -457, -468, -479, -491, -502, -514, -525, -537, + -548, -559, -571, -582, -594, -605, -617, -628, -639, -651, -662, -674, -685, -697, -708, -719, + -731, -742, -754, -765, -776, -788, -799, -811, -822, -834, -845, -856, -868, -879, -891, -902, + -914, -925, -936, -948, -959, -971, -982, -994,-1005,-1016,-1028,-1039,-1051,-1062,-1074,-1085, +-1096,-1108,-1119,-1131,-1142,-1154,-1165,-1176,-1188,-1199,-1211,-1222,-1234,-1245,-1256,-1268, +-1279,-1291,-1302,-1314,-1325,-1336,-1348,-1359,-1371,-1382,-1394,-1405,-1416,-1428,-1439,-1451 +}; + +static const int16_t _Mult07_14l[] = +{ + -179, -178, -176, -175, -173, -172, -171, -169, -168, -166, -165, -164, -162, -161, -159, -158, + -157, -155, -154, -152, -151, -150, -148, -147, -145, -144, -143, -141, -140, -138, -137, -135, + -134, -133, -131, -130, -128, -127, -126, -124, -123, -121, -120, -119, -117, -116, -114, -113, + -112, -110, -109, -107, -106, -105, -103, -102, -100, -99, -98, -96, -95, -93, -92, -91, + -89, -88, -86, -85, -84, -82, -81, -79, -78, -77, -75, -74, -72, -71, -70, -68, + -67, -65, -64, -63, -61, -60, -58, -57, -56, -54, -53, -51, -50, -49, -47, -46, + -44, -43, -42, -40, -39, -37, -36, -35, -33, -32, -30, -29, -28, -26, -25, -23, + -22, -21, -19, -18, -16, -15, -14, -12, -11, -9, -8, -7, -5, -4, -2, -1, + 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 16, 18, 19, 21, + 22, 23, 25, 26, 28, 29, 30, 32, 33, 35, 36, 37, 39, 40, 42, 43, + 44, 46, 47, 49, 50, 51, 53, 54, 56, 57, 58, 60, 61, 63, 64, 65, + 67, 68, 70, 71, 72, 74, 75, 77, 78, 79, 81, 82, 84, 85, 86, 88, + 89, 91, 92, 93, 95, 96, 98, 99, 100, 102, 103, 105, 106, 107, 109, 110, + 112, 113, 114, 116, 117, 119, 120, 121, 123, 124, 126, 127, 128, 130, 131, 133, + 134, 135, 137, 138, 140, 141, 143, 144, 145, 147, 148, 150, 151, 152, 154, 155, + 157, 158, 159, 161, 162, 164, 165, 166, 168, 169, 171, 172, 173, 175, 176, 178 +}; + +static const int16_t _Mult17_03h[] = +{ + -226, -225, -223, -221, -219, -217, -216, -214, -212, -210, -209, -207, -205, -203, -202, -200, + -198, -196, -194, -193, -191, -189, -187, -186, -184, -182, -180, -178, -177, -175, -173, -171, + -170, -168, -166, -164, -163, -161, -159, -157, -155, -154, -152, -150, -148, -147, -145, -143, + -141, -139, -138, -136, -134, -132, -131, -129, -127, -125, -124, -122, -120, -118, -116, -115, + -113, -111, -109, -108, -106, -104, -102, -101, -99, -97, -95, -93, -92, -90, -88, -86, + -85, -83, -81, -79, -77, -76, -74, -72, -70, -69, -67, -65, -63, -62, -60, -58, + -56, -54, -53, -51, -49, -47, -46, -44, -42, -40, -38, -37, -35, -33, -31, -30, + -28, -26, -24, -23, -21, -19, -17, -15, -14, -12, -10, -8, -7, -5, -3, -1, + 0, 1, 3, 5, 7, 8, 10, 12, 14, 15, 17, 19, 21, 23, 24, 26, + 28, 30, 31, 33, 35, 37, 38, 40, 42, 44, 46, 47, 49, 51, 53, 54, + 56, 58, 60, 62, 63, 65, 67, 69, 70, 72, 74, 76, 77, 79, 81, 83, + 85, 86, 88, 90, 92, 93, 95, 97, 99, 101, 102, 104, 106, 108, 109, 111, + 113, 115, 116, 118, 120, 122, 124, 125, 127, 129, 131, 132, 134, 136, 138, 139, + 141, 143, 145, 147, 148, 150, 152, 154, 155, 157, 159, 161, 163, 164, 166, 168, + 170, 171, 173, 175, 177, 178, 180, 182, 184, 186, 187, 189, 191, 193, 194, 196, + 198, 200, 202, 203, 205, 207, 209, 210, 212, 214, 216, 217, 219, 221, 223, 225 +}; + +static const int16_t _Mult17_03l[] = +{ + 704, 699, 693, 688, 682, 677, 671, 666, 660, 655, 649, 644, 638, 633, 627, 622, + 616, 611, 605, 600, 594, 589, 583, 578, 572, 567, 561, 556, 550, 545, 539, 534, + 528, 523, 517, 512, 506, 501, 495, 490, 484, 479, 473, 468, 462, 457, 451, 446, + 440, 434, 429, 423, 418, 412, 407, 401, 396, 390, 385, 379, 374, 368, 363, 357, + 352, 346, 341, 335, 330, 324, 319, 313, 308, 302, 297, 291, 286, 280, 275, 269, + 264, 258, 253, 247, 242, 236, 231, 225, 220, 214, 209, 203, 198, 192, 187, 181, + 176, 170, 165, 159, 154, 148, 143, 137, 132, 126, 121, 115, 110, 104, 99, 93, + 88, 82, 77, 71, 66, 60, 55, 49, 44, 38, 33, 27, 22, 16, 11, 5, + 0, -5, -11, -16, -22, -27, -33, -38, -44, -49, -55, -60, -66, -71, -77, -82, + -88, -93, -99, -104, -110, -115, -121, -126, -132, -137, -143, -148, -154, -159, -165, -170, + -176, -181, -187, -192, -198, -203, -209, -214, -220, -225, -231, -236, -242, -247, -253, -258, + -264, -269, -275, -280, -286, -291, -297, -302, -308, -313, -319, -324, -330, -335, -341, -346, + -352, -357, -363, -368, -374, -379, -385, -390, -396, -401, -407, -412, -418, -423, -429, -434, + -440, -446, -451, -457, -462, -468, -473, -479, -484, -490, -495, -501, -506, -512, -517, -523, + -528, -534, -539, -545, -550, -556, -561, -567, -572, -578, -583, -589, -594, -600, -605, -611, + -616, -622, -627, -633, -638, -644, -649, -655, -660, -666, -671, -677, -682, -688, -693, -699 +}; + +//! 8-bit table for clamping range [-256...511]->[0...255] +static const uint8_t _Ranger8[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +//! offset clamp table to zero +static const uint8_t *_pRanger8 = _Ranger8+256; + + +/*** Private Functions ************************************************************/ + +#if DIRTYJPG_DEBUG_IDCT +/*F********************************************************************************/ +/*! + \Function _PrintMatrix16 + + \Description + Print 16bit 8x8 matrix to debug output. + + \Input *pMatrix - pointer to matrix to print + \Input *pTitle - output title + + \Version 03/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _PrintMatrix16(int16_t *pMatrix, const char *pTitle) +{ + int32_t ctr; + + NetPrintf(("%s\n", pTitle)); + for (ctr = 0; ctr < 64; ctr += 8) + { + NetPrintf(("%+10d %+10d %+10d %+10d %+10d %+10d %+10d %+10d\n", + pMatrix[ctr+0], pMatrix[ctr+1], pMatrix[ctr+2], pMatrix[ctr+3], + pMatrix[ctr+4], pMatrix[ctr+5], pMatrix[ctr+6], pMatrix[ctr+7])); + } +} +#endif + +/* + + Huffman decoding routines + +*/ + +/*F********************************************************************************/ +/*! + \Function _ReadByte + + \Description + Read a byte into the bitbuffer, handling any dle bytes. + + \Input *pState - state ref + \Input *pData - compressed data base pointer + \Input *pValue - byte read + + \Output + uint32_t - number of bytes consumed + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ReadByte(DirtyJpgStateT *pState, const uint8_t *pData, uint8_t *pValue) +{ + uint32_t uValue, uOffset=0; + + // protect against reading past the end of the buffer, in which case we just return a null byte + if ((pData+uOffset) == pState->pSrcFinal) + { + *pValue = 0; + return(1); + } + + // read a byte + *pValue = uValue = pData[uOffset++]; + + // eat marker data + while (uValue == 0xff) + { + uValue = pData[uOffset++]; + pState->uBitsConsumed += 8; + + // restart marker? + if ((uValue >= 0xd0) && (uValue <= 0xd7)) + { + #if DIRTYJPG_DEBUG_HUFF + NetPrintf(("dirtyjpg: restart marker %d\n", uValue&0xf)); + #endif + // remember that there was a restart marker + pState->bRestart = TRUE; + } + } + + return(uOffset); +} + +/*F********************************************************************************/ +/*! + \Function _ExtractBits + + \Description + Read bits from bitstream. + + \Input *pState - state ref + \Input *pData - compressed data base pointer + \Input uBitSize - number of bits to read + \Input *pSign - positive if leading bit of extracted data is not set, else negative + \Input bAdvance - if TRUE, advance the bit offset + + \Output + uint32_t - extracted value + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ExtractBits(DirtyJpgStateT *pState, const uint8_t *pData, uint32_t uBitSize, int32_t *pSign, uint8_t bAdvance) +{ + uint32_t uValue, uBytesRead; + uint8_t uByte; + + // load data into bit buffer + while ((pState->uBitsAvail < 16) && (pState->bRestart != TRUE)) + { + uBytesRead = _ReadByte(pState, pData+pState->uBytesRead, &uByte); + pState->uBytesRead += uBytesRead; + pState->uBitfield |= uByte << (24-pState->uBitsAvail); + pState->uBitsAvail += 8; + } + + // determine sign of extracted value + if (pSign) + { + *pSign = (pState->uBitfield & 0x80000000) ? -1 : 1; + } + + // take top bits + uValue = pState->uBitfield >> (32-uBitSize); + if (bAdvance) + { + pState->uBitfield <<= uBitSize; + pState->uBitsAvail -= uBitSize; + pState->uBitsConsumed += uBitSize; + } + + // return extracted value to caller + return(uValue); +} + +/*F********************************************************************************/ +/*! + \Function _HuffDecode + + \Description + Decode a huffman-encoded value + + \Input *pState - state ref + \Input *pHuffTable - pointer to huffman table to use for decode + \Input *pData - compressed data base pointer + + \Output + uint32_t - decoded value + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _HuffDecode(DirtyJpgStateT *pState, HuffTableT *pHuffTable, const uint8_t *pData) +{ + HuffHeadT *pHuffHead; + uint32_t uValue; + + // extract 16 bits, but don't advance bit offset + uValue = _ExtractBits(pState, pData, 16, NULL, FALSE); + + // find table range for this value + for (pHuffHead = pHuffTable->Head; uValue >= pHuffHead->uMaxCode; pHuffHead += 1) + { + if (pHuffHead->uShift == 0) + { + NetPrintf(("dirtyjpg: decode error\n")); + return(0xffffffff); + } + } + #if DIRTYJPG_DECODE_HUFF + NetPrintf(("dirtyjpg: using range %d\n", pHuffHead-pHuffTable->Head)); + #endif + + // subtract mincode to get offset into table + uValue -= pHuffHead->uMinCode; + // shift down to discard extra bits + uValue >>= pHuffHead->uShift; + // offset by the start of this table into code/step buffer + uValue += pHuffHead->uOffset; + + // lookup skip value and consume bits + _ExtractBits(pState, pData, pHuffTable->Step[uValue], NULL, TRUE); + + #if DIRTYJPG_DEBUG_HUFF + NetPrintf(("_decode: 0x%02x->s%d,c%02x\n", uValue, pHuffTable->Step[uValue], pHuffTable->Code[uValue])); + #endif + + // lookup code value + uValue = pHuffTable->Code[uValue]; + + // return decoded value to caller + return(uValue); +} + +/*F********************************************************************************/ +/*! + \Function _UnpackComp + + \Description + Unnpack the next sample into a component record's matrix + + \Input *pState - state ref + \Input *pCompTable - pointer to component table + \Input *pData - compressed data base pointer + + \Output + int32_t - negative=error, else success + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _UnpackComp(DirtyJpgStateT *pState, CompTableT *pCompTable, const uint8_t *pData) +{ + uint16_t *pMatrix = (uint16_t *)&pCompTable->Matrix; + uint16_t *pQuant = (uint16_t *)&pCompTable->Quant; + uint32_t uCodeLen, uElemIdx, uValue=0; + int32_t iSign = 0; + + // decode a token (get code length) + if ((uCodeLen = _HuffDecode(pState, pCompTable->pDCHuff, pData)) == 0xffffffff) + { + return(uCodeLen); + } + else if (uCodeLen != 0) + { + // get the raw value + uValue = _ExtractBits(pState, pData, uCodeLen, &iSign, TRUE); + // if non-negative, adjust to negative value + if (iSign > 0) + { + uValue -= _ZagAdj_adjust[uCodeLen]; + } + #if DIRTYJPG_DEBUG_HUFF + NetPrintf(("dc=0x%02x\n", uValue)); + #endif + // quantify the difference + uValue *= pQuant[0]; + } + + // accumulate and save the dc coefficient + uValue += pMatrix[0]; + pMatrix[0] = uValue; + + // clear the rest of the matrix + ds_memclr(&pMatrix[1], sizeof(pCompTable->Matrix)-sizeof(pMatrix[0])); + + // decode ac coefficients + for (uElemIdx = 1; uElemIdx < 64; ) + { + // decode ac code + if ((uValue = _HuffDecode(pState, pCompTable->pACHuff, pData)) == 0xffffffff) + { + return(uValue); + } + + // get vli length + if ((uCodeLen = uValue & 0xf) == 0) + { + // end of block? + if (uValue != 0xf0) + { + return(0); + } + // skip 16 (zero skip) + uElemIdx += 16; + } + else + { + // skip count + uValue >>= 4; + // advance matrix offset (zero skip) + uElemIdx = (uElemIdx+uValue)&63; + // get the raw value and advance the bit offset + uValue = _ExtractBits(pState, pData, uCodeLen, &iSign, TRUE); + // if non-negative, adjust to negative value + if (iSign > 0) + { + uValue -= _ZagAdj_adjust[uCodeLen]; + } + #if DIRTYJPG_DEBUG_HUFF + NetPrintf(("ac=0x%02x\n", uValue)); + #endif + // quantify the value + uValue *= pQuant[uElemIdx]; + // store the quantized value, with zag + pMatrix[_ZagAdj_zag[uElemIdx]] = uValue; + // increment index + uElemIdx += 1; + } + } + + // return success + return(0); +} + + +/* + + Image coefficient expansion routines + +*/ + +/*F********************************************************************************/ +/*! + \Function _Expand0 + + \Description + Expand buffered component scan line data (no expansion). + + \Input *pState - state ref + \Input *pCompTable - pointer to component table + \Input uOutOffset - offset in mcu buffer + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _Expand0(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset) +{ +} + +/*F********************************************************************************/ +/*! + \Function _Expand3hv + + \Description + Expand buffered component scan line data (2x2 special case). + + \Input *pState - state ref + \Input *pCompTable - pointer to component table + \Input uOutOffset - offset in mcu buffer + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _Expand3hv(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset) +{ + uint32_t uStepV0 = pCompTable->uStepVert0; + uint32_t uStepVM = pCompTable->uStepVertM; + uint32_t uStepH0 = pCompTable->uStepHorz0; + uint8_t *pData = pState->aMCU + uOutOffset; + uint8_t *pEndRow, *pEndCol; + uint32_t uData; + + for (pEndRow = pData + uStepVM; pData != pEndRow;) + { + for (pEndCol = pData + uStepV0; pData != pEndCol;) + { + uData = *pData; // get upper left + pData[uStepV0] = uData; // save lower-left + pData += uStepH0; + pData[0] = uData; // save upper-right + pData[uStepV0] = uData; // save lower-right + pData += uStepH0; + } + + // skip extra col + pData += uStepV0; + } +} + +/*F********************************************************************************/ +/*! + \Function _ExpandAny + + \Description + Expand buffered component scan line data (general case). + + \Input *pState - state ref + \Input *pCompTable - pointer to component table + \Input uOutOffset - offset in mcu buffer + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ExpandAny(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset) +{ + uint32_t uData, uExpHorz, uExpVert, uRptCnt; + uint32_t uStepV0 = pCompTable->uStepVert0; + uint32_t uStepVM = pCompTable->uStepVertM; + uint32_t uStepH0 = pCompTable->uStepHorz0; + uint8_t *pData, *pEndRow, *pEndCol; + + // get zero-relative vertical expansion factor + if ((uExpVert = pCompTable->uExpVert - 1) != 0) + { + pData = pState->aMCU + uOutOffset; + for (pEndCol = pData + uStepV0; pData < pEndCol; pData += pCompTable->uStepHorz1) + { + uint8_t *pTemp = pData; + for (pEndRow = pData + uStepVM; pData < pEndRow; ) + { + // get source data and index past it + uData = *pData; + pData += uStepV0; + + // expand the data + for (uRptCnt = 0; uRptCnt < uExpVert; uRptCnt += 1, pData += uStepV0) + { + *pData = uData; + } + } + pData = pTemp; + } + } + + // get zero-relative horizontal expansion factor + if ((uExpHorz = pCompTable->uExpHorz - 1) != 0) + { + pData = pState->aMCU + uOutOffset; + for (pEndRow = pData + uStepVM; pData < pEndRow; ) + { + // get source data and index past it + uData = *pData; + pData += uStepH0; + + // expand the data + for (uRptCnt = 0; uRptCnt < uExpHorz; uRptCnt += 1, pData += uStepH0) + { + *pData = uData; + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _TransLLM + + \Description + Perform iDCT and quantify (component record matrix -> mcu buffer). + + \Input *pState - state ref + \Input *pCompTable - pointer to component table + \Input uOutOffset - offset in mcu buffer + + \Version 03/03/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _TransLLM(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset) +{ + uint32_t uStepH1, uStepV1, uRow; + uint8_t *pDataDest; + QuantTableT WorkingMatrix; + #if DIRTYJPG_DEBUG_IDCT + QuantTableT WorkingMatrix2; + #endif + int16_t *pWorking, *pMatrix; + const uint8_t *pRanger8 = _pRanger8 + 128; + int32_t tmp0, tmp1, tmp2, tmp3; + int32_t tmp10, tmp11, tmp12, tmp13; + int32_t z1, z2, z3, z4, z5; + + // point to dst data + if ((pDataDest = pState->aMCU) == NULL) + { + return; + } + + // increment to offset + pDataDest += uOutOffset; + + // ref step values + uStepH1 = pCompTable->uStepHorz1; + uStepV1 = pCompTable->uStepVert1; + + // debug input + _PrintMatrix16((int16_t *)&pCompTable->Matrix, "iDCT input matrix"); + + // do eight columns + for (uRow = 0, pWorking=(int16_t*)&WorkingMatrix, pMatrix=(int16_t*)&pCompTable->Matrix; uRow < 8; uRow++) + { + /* Due to quantization, we will usually find that many of the input + coefficients are zero, especially the AC terms. We can exploit this + by short-circuiting the IDCT calculation for any column in which all + the AC terms are zero. In that case each output is equal to the + DC coefficient (with scale factor as needed). + With typical images and quantization tables, half or more of the + column DCT calculations can be simplified this way. */ + if ((pMatrix[DCTSIZE*1] | pMatrix[DCTSIZE*2] | pMatrix[DCTSIZE*3] | + pMatrix[DCTSIZE*4] | pMatrix[DCTSIZE*5] | pMatrix[DCTSIZE*6] | + pMatrix[DCTSIZE*7]) == 0) + { + /* AC terms all zero */ + int16_t dcval = (int16_t) (pMatrix[DCTSIZE*0] << PASS1_BITS); + + pWorking[DCTSIZE*0] = dcval; + pWorking[DCTSIZE*1] = dcval; + pWorking[DCTSIZE*2] = dcval; + pWorking[DCTSIZE*3] = dcval; + pWorking[DCTSIZE*4] = dcval; + pWorking[DCTSIZE*5] = dcval; + pWorking[DCTSIZE*6] = dcval; + pWorking[DCTSIZE*7] = dcval; + + pMatrix++; // advance pointers to next column + pWorking++; + continue; + } + + // Even part: reverse the even part of the forward DCT. The rotator is sqrt(2)*c(-6). + z2 = pMatrix[DCTSIZE*2]; + z3 = pMatrix[DCTSIZE*6]; + + z1 = (z2 + z3) * FIX_0_541196100; + tmp2 = z1 + (z3 * -FIX_1_847759065); + tmp3 = z1 + (z2 * FIX_0_765366865); + + z2 = pMatrix[DCTSIZE*0]; + z3 = pMatrix[DCTSIZE*4]; + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ + tmp0 = pMatrix[DCTSIZE*7]; + tmp1 = pMatrix[DCTSIZE*5]; + tmp2 = pMatrix[DCTSIZE*3]; + tmp3 = pMatrix[DCTSIZE*1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = (z3 + z4) * FIX_1_175875602; // sqrt(2) * c3 + + tmp0 = tmp0 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7) + tmp1 = tmp1 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7) + tmp2 = tmp2 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7) + tmp3 = tmp3 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7) + z1 = z1 * -FIX_0_899976223; // sqrt(2) * (c7-c3) + z2 = z2 * -FIX_2_562915447; // sqrt(2) * (-c1-c3) + z3 = z3 * -FIX_1_961570560; // sqrt(2) * (-c3-c5) + z4 = z4 * -FIX_0_390180644; // sqrt(2) * (c5-c3) + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + // Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 + pWorking[DCTSIZE*0] = (int16_t) ((tmp10 + tmp3) >> (CONST_BITS-PASS1_BITS)); + pWorking[DCTSIZE*7] = (int16_t) ((tmp10 - tmp3) >> (CONST_BITS-PASS1_BITS)); + pWorking[DCTSIZE*1] = (int16_t) ((tmp11 + tmp2) >> (CONST_BITS-PASS1_BITS)); + pWorking[DCTSIZE*6] = (int16_t) ((tmp11 - tmp2) >> (CONST_BITS-PASS1_BITS)); + pWorking[DCTSIZE*2] = (int16_t) ((tmp12 + tmp1) >> (CONST_BITS-PASS1_BITS)); + pWorking[DCTSIZE*5] = (int16_t) ((tmp12 - tmp1) >> (CONST_BITS-PASS1_BITS)); + pWorking[DCTSIZE*3] = (int16_t) ((tmp13 + tmp0) >> (CONST_BITS-PASS1_BITS)); + pWorking[DCTSIZE*4] = (int16_t) ((tmp13 - tmp0) >> (CONST_BITS-PASS1_BITS)); + + pMatrix++; // advance pointers to next column + pWorking++; + } + + // debug first pass + _PrintMatrix16((int16_t *)&WorkingMatrix, "iDCT pass one"); + + /* Pass 2: process rows from work array, store into output array. + Note that we must descale the results by a factor of 8 == 2**3, + and also undo the PASS1_BITS scaling. */ + for (uRow = 0, pWorking=(int16_t*)&WorkingMatrix; uRow < DCTSIZE; uRow++, pDataDest += uStepV1) + { + // Even part: reverse the even part of the forward DCT. The rotator is sqrt(2)*c(-6). + z2 = (int32_t) pWorking[2]; + z3 = (int32_t) pWorking[6]; + + z1 = (z2 + z3) * FIX_0_541196100; + tmp2 = z1 + (z3 * -FIX_1_847759065); + tmp3 = z1 + (z2 * FIX_0_765366865); + + tmp0 = ((int32_t) pWorking[0] + (int32_t) pWorking[4]) << CONST_BITS; + tmp1 = ((int32_t) pWorking[0] - (int32_t) pWorking[4]) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ + tmp0 = (int32_t) pWorking[7]; + tmp1 = (int32_t) pWorking[5]; + tmp2 = (int32_t) pWorking[3]; + tmp3 = (int32_t) pWorking[1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = (z3 + z4) * FIX_1_175875602; // sqrt(2) * c3 + + tmp0 = tmp0 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7) + tmp1 = tmp1 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7) + tmp2 = tmp2 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7) + tmp3 = tmp3 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7) + z1 = z1 * -FIX_0_899976223; // sqrt(2) * (c7-c3) + z2 = z2 * -FIX_2_562915447; // sqrt(2) * (-c1-c3) + z3 = z3 * -FIX_1_961570560; // sqrt(2) * (-c3-c5) + z4 = z4 * -FIX_0_390180644; // sqrt(2) * (c5-c3) + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + #if DIRTYJPG_DEBUG_IDCT + WorkingMatrix2[0+(uRow*8)] = (((tmp10 + tmp3) >> (CONST_BITS+PASS1_BITS+3))); + WorkingMatrix2[7+(uRow*8)] = (((tmp10 - tmp3) >> (CONST_BITS+PASS1_BITS+3))); + WorkingMatrix2[1+(uRow*8)] = (((tmp11 + tmp2) >> (CONST_BITS+PASS1_BITS+3))); + WorkingMatrix2[6+(uRow*8)] = (((tmp11 - tmp2) >> (CONST_BITS+PASS1_BITS+3))); + WorkingMatrix2[2+(uRow*8)] = (((tmp12 + tmp1) >> (CONST_BITS+PASS1_BITS+3))); + WorkingMatrix2[5+(uRow*8)] = (((tmp12 - tmp1) >> (CONST_BITS+PASS1_BITS+3))); + WorkingMatrix2[3+(uRow*8)] = (((tmp13 + tmp0) >> (CONST_BITS+PASS1_BITS+3))); + WorkingMatrix2[4+(uRow*8)] = (((tmp13 - tmp0) >> (CONST_BITS+PASS1_BITS+3))); + #endif + + pDataDest[0*uStepH1] = pRanger8[(((tmp10 + tmp3) >> (CONST_BITS+PASS1_BITS+3)))]; + pDataDest[7*uStepH1] = pRanger8[(((tmp10 - tmp3) >> (CONST_BITS+PASS1_BITS+3)))]; + pDataDest[1*uStepH1] = pRanger8[(((tmp11 + tmp2) >> (CONST_BITS+PASS1_BITS+3)))]; + pDataDest[6*uStepH1] = pRanger8[(((tmp11 - tmp2) >> (CONST_BITS+PASS1_BITS+3)))]; + pDataDest[2*uStepH1] = pRanger8[(((tmp12 + tmp1) >> (CONST_BITS+PASS1_BITS+3)))]; + pDataDest[5*uStepH1] = pRanger8[(((tmp12 - tmp1) >> (CONST_BITS+PASS1_BITS+3)))]; + pDataDest[3*uStepH1] = pRanger8[(((tmp13 + tmp0) >> (CONST_BITS+PASS1_BITS+3)))]; + pDataDest[4*uStepH1] = pRanger8[(((tmp13 - tmp0) >> (CONST_BITS+PASS1_BITS+3)))]; + + pWorking += DCTSIZE; // advance pointer to next row + } + + // debug second pass + _PrintMatrix16((int16_t *)&WorkingMatrix2, "iDCT pass two"); +} + +/*F********************************************************************************/ +/*! + \Function _InitComp + + \Description + Init component table. + + \Input *pState - state ref + \Input *pFrame - pointer to SOS (start of scan) frame + + \Version 03/01/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _InitComp(DirtyJpgStateT *pState, const uint8_t *pFrame) +{ + CompTableT *pCompTable; + QuantTableT *pQuantTable; + uint32_t uExpFunc; + + // ref comp table + pCompTable = &pState->CompTable[pFrame[0]]; + + // ref huffman tables + pCompTable->pDCHuff = &pState->HuffTable[0+(pFrame[1]>>4)]; + pCompTable->pACHuff = &pState->HuffTable[4+(pFrame[1]&3)]; + #if DIRTYJPG_DEBUG_HUFF + NetPrintf(("dirtyjpg: using dc=%d ac=%d\n", pFrame[1]>>4, pFrame[1]&3)); + #endif + + // reset differential encoding + pCompTable->Matrix[0] = 0; + + // determine expansion function + uExpFunc = ((pCompTable->uExpVert - 1) << 2) + (pCompTable->uExpHorz - 1); + + // ref expansion table + if (uExpFunc == 0) + { + pCompTable->pExpand = _Expand0; + } + else if (uExpFunc == 0x0101) + { + pCompTable->pExpand = _Expand3hv; + } + else + { + pCompTable->pExpand = _ExpandAny; + } + + // get quantify table pointer + pQuantTable = &pState->QuantTable[pCompTable->uQuantNum]; + + // copy the quantify table + ds_memcpy_s(pCompTable->Quant, sizeof(pCompTable->Quant), pQuantTable, sizeof(*pQuantTable)); + + // ref inverse function + pCompTable->pInvert = _TransLLM; +} + +/*F********************************************************************************/ +/*! + \Function _PutColor + + \Description + Convert an MCU from YCbCr to ARGB ($$tmp assume color) + + \Input *pState - state ref + \Input *pCompTable - pointer to component table + \Input uDstHOff - horizontal mcu offset + \Input uDstVOff - vertical mcu offset + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _PutColor(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uDstHOff, uint32_t uDstVOff) +{ + uint32_t uSrcH, uSrcV, uStepSrcH, uStepSrcV, uMaxSrcH, uMaxSrcV; + uint32_t uDstH, uDstV, uStepDstH, uStepDstV, uMaxDstH, uMaxDstV; + const uint8_t *pDataSrc; + uint8_t *pDataDst; + int16_t Y, Cb_hi, Cb_lo, Cr_hi, Cr_lo; + uint32_t R, G, B; + + // ref source step values + uStepSrcH = pCompTable->uStepHorz0; + uMaxSrcH = pCompTable->uStepHorzM; + uStepSrcV = pCompTable->uStepVert0; + uMaxSrcV = pCompTable->uStepVertM; + + // ref dest step values + uStepDstH = 4; // ARGB + uMaxDstH = uStepDstH * pState->uBufWidth; + uStepDstV = uStepDstH * pState->uBufWidth; + uMaxDstV = uStepDstV * pState->uBufHeight; + + // scale mcu step values to get pixel offsets + uDstHOff *= 8*4*pState->uMcuHorz; + uDstVOff *= uMaxDstH * 8 * pState->uMcuVert; + + // transform the data + for (uSrcV = 0, uDstV = uDstVOff; (uSrcV < uMaxSrcV) && (uDstV < uMaxDstV); uSrcV += uStepSrcV, uDstV += uStepDstV) + { + for (uSrcH = 0, uDstH = uDstHOff; (uSrcH < uMaxSrcH) && (uDstH < uMaxDstH); uSrcH += uStepSrcH, uDstH += uStepDstH) + { + pDataSrc = pState->aMCU + uSrcH + uSrcV; + pDataDst = pState->pImageBuf + uDstH + uDstV; + + // get YCbCr components + Y = pDataSrc[0]; + Cb_hi = _Mult17_03h[pDataSrc[1]]; // get 1.772(Cb-128) + Cb_lo = _Mult17_03l[pDataSrc[1]]; // get -0.34414(Cb-128) + Cr_hi = _Mult07_14h[pDataSrc[2]]; // get -0.71414(Cr-128) + Cr_lo = _Mult07_14l[pDataSrc[2]]; // get 1.402(Cr-128) + + // convert to RGB +#ifdef __SNC__ + R = *((uint8_t *)(_pRanger8 + Y + Cr_lo)); // Y + 1.402(Cr-128) + G = *((uint8_t *)(_pRanger8 + Y + ((Cr_hi + Cb_lo) >> 4))); // Y - 0.71414(Cr-128) - 0.34414(Cb-128) + B = *((uint8_t *)(_pRanger8 + Y + Cb_hi)); // Y + 1.772(Cb-128) +#else + R = _pRanger8[Y + Cr_lo]; // Y + 1.402(Cr-128) + G = _pRanger8[Y + ((Cr_hi + Cb_lo) >> 4)]; // Y - 0.71414(Cr-128) - 0.34414(Cb-128) + B = _pRanger8[Y + Cb_hi]; // Y + 1.772(Cb-128) +#endif + // store in output buffer + pDataDst[0] = 0xff; + pDataDst[1] = R; + pDataDst[2] = G; + pDataDst[3] = B; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _DoRST + + \Description + Restart decoder after an RST marker was encountered. + + \Input *pState - state ref + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _DoRST(DirtyJpgStateT *pState) +{ + uint32_t uComp; + + // reset bitbuffer + pState->uBitsConsumed += pState->uBitsAvail; + pState->uBitsAvail = 0; + pState->uBitfield = 0; + + // reset dc for all components + for (uComp = 0; uComp < sizeof(pState->CompTable)/sizeof(pState->CompTable[0]); uComp++) + { + pState->CompTable[uComp].Matrix[0] = 0; + } + + // done the restart, clear the flag + pState->bRestart = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _GetMCU + + \Description + Decode an MCU into MCU buffer. + + \Input *pState - state ref + \Input *pFrame - pointer to SOS (start of scan) component frame data + \Input uCompCount - number of components (must be 1 or 3) + + \Output + uint32_t - number of bits consumed from the bitstream, or -1 + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _GetMCU(DirtyJpgStateT *pState, const uint8_t *pFrame, uint32_t uCompCount) +{ + uint32_t uCompIdx, uCompVert, uCompHorz, uOutOffset; + CompTableT *pCompTable; + + // unpack and translate all components + for (uCompIdx = 0; uCompIdx < uCompCount; uCompIdx += 1) + { + // ref comp table + uint32_t uIndex = pFrame[uCompIdx*2]; + if (uIndex > 3) + { + continue; + } + pCompTable = &pState->CompTable[uIndex]; + + // unpack and translate component + for (uOutOffset = uCompIdx, uCompVert = 0; uCompVert < pCompTable->uCompVert; uCompVert += 1) + { + // save start of row + uint32_t uTmpOffset = uOutOffset; + + // unpack and translate row + for (uCompHorz = 0; uCompHorz < pCompTable->uCompHorz; uCompHorz += 1) + { + // unpack a component into record matrix + if (_UnpackComp(pState, pCompTable, pState->pCompData) == 0xffffffff) + { + return(0xffffffff); + } + + // translate the component into mcu buffer + pCompTable->pInvert(pState, pCompTable, uOutOffset); + + // increment to next component + uOutOffset += pCompTable->uStepHorz8; + } + + // increment to next output row + uOutOffset = uTmpOffset + pCompTable->uStepVert8; + } + + // expand component + pCompTable->pExpand(pState, pCompTable, uCompIdx); + } + + // if there was a restart, do it now + if (pState->bRestart) + { + _DoRST(pState); + } + + // return updated bit offset + return(pState->uBitsConsumed); +} + +/*F********************************************************************************/ +/*! + \Function _ParseDQT + + \Description + Parse a DQT (quantization) table. + + \Input *pState - state ref + \Input *pFrame - pointer to DQT frame data + \Input *pFinal - end of DQT frame + + \Output + int32_t - DIRTYJPG_ERR_* + + \Version 02/23/2006 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _ParseDQT(DirtyJpgStateT *pState, const uint8_t *pFrame, const uint8_t *pFinal) +{ + uint32_t uCount; + uint32_t uIndex; + uint32_t uMask = 0; + uint32_t uShift = 0; + uint32_t uSkip = 1; + + // process tables + while (pFrame < pFinal) + { + // decode index + uIndex = pFrame[0] & 15; + + // if 16-bit encoded, setup to shift first byte and unmask second + if (pFrame[0] >= 16) + { + uMask = 255; + uShift = 8; + uSkip = 2; + } + + // skip the header + pFrame += 1; + + // decode and copy the table + for (uCount = 0; uCount < 64; ++uCount) + { + pState->QuantTable[uIndex][uCount] = (pFrame[0]<HuffTable[(pFrame[0]>>2) | (pFrame[0]&3)]; + + // offset to value/shift lookup + uOffset = 0; + + // figure out max code based on first 8 huffman tables (1-8 bits consolidated) + for (uSize=1, uCode=0; uSize < 8; uSize++) + { + uCode += pFrame[uSize]; + uCode += uCode; + } + uCode += pFrame[uSize]; + + // setup first table entry + pHead = &pTable->Head[0]; + pHead->uOffset = uOffset; + // initial table is larger than the rest (based on code size) + uOffset += uCode; + pHead->uMaxCode = uCode; + pHead->uShift = uSize; + pHead += 1; + + // do remaining tables + while (++uSize < 17) + { + uCode += uCode; + uCode += pFrame[uSize]; + + // only add header if it contains data + if (pFrame[uSize] > 0) + { + pHead->uOffset = uOffset; + // other tables are sized based on exact code counts + uOffset += pFrame[uSize]; + pHead->uMaxCode = uCode; + pHead->uShift = uSize; + pHead += 1; + } + } + + // terminate header list + pHead->uShift = 0; + pHead->uMaxCode = uCode*2; + pHead->uOffset = uOffset; // so we can calc length of final table + + // previous max code + uCode = 0; + // start with one-bit codes + uSize = 1; + // copy value/shift data to all the tables + pData = pFrame+17; + for (pHead = &pTable->Head[0]; pHead->uShift != 0; ++pHead) + { + // change from size to shift count + uShift = pHead->uShift; + pHead->uShift = 16-uShift; + + // save the previous max code as our min code + pHead->uMinCode = uCode; + // get current max code + uCode = pHead->uMaxCode; + // right align the bits + uCode <<= (16-uShift); + // save the new right-aligned max code + pHead->uMaxCode = uCode; + + // fill out the data portion (walk current data offset to next data offset) + for (uCount = pHead[0].uOffset; uCount < pHead[1].uOffset;) + { + // copy over the huffman values + for (uIndex = 0; uIndex < pFrame[uSize]; ++uIndex) + { + // repeat count is variable in table 0 but always 1 for tables 1+ + uint32_t uRepeat = (1 << uShift) >> uSize; + // copy the huffman value into the appropriate slots + ds_memset(pTable->Code+uCount, *pData, uRepeat); + // copy the corresponding shift counts into the matching slots + ds_memset(pTable->Step+uCount, uSize, uRepeat); + uCount += uRepeat; + pData += 1; + } + uSize += 1; + } + } + + // move to end of data + pFrame = pData; + } + + // make sure all data was processed + return(pFrame == pFinal ? 0 : DIRTYJPG_ERR_BADDHT); +} + +/*F********************************************************************************/ +/*! + \Function _ParseSOF0 + + \Description + Parse a SOF0 (start of frame 0) frame. + + \Input *pState - state ref + \Input *pFrame - pointer to sof0 frame data + \Input *pFinal - end of sof0 frame + + \Output + int32_t - DIRTYJPG_ERR_* + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ParseSOF0(DirtyJpgStateT *pState, const uint8_t *pFrame, const uint8_t *pFinal) +{ + uint32_t uComp, uCompCount; + uint32_t uHorzSampFactor, uVertSampFactor; + CompTableT *pCompTable; + const uint8_t *pComp; + + // validate 8bits per component + if (pFrame[0] != 8) + { + return(DIRTYJPG_ERR_BITDEPTH); + } + + pState->uImageHeight = (pFrame[1]<<8)|(pFrame[2]<<0); + pState->uImageWidth = (pFrame[3]<<8)|(pFrame[4]<<0); + uCompCount = pFrame[5]; + + // only support three components + if (uCompCount > 3) + { + return(DIRTYJPG_ERR_BADSOF0); + } + + // parse components to find image mcu + for (pComp = pFrame+6, uComp = 0; uComp < uCompCount; uComp++, pComp += 3) + { + uVertSampFactor = pComp[1] & 0xf; + if (pState->uMcuVert < uVertSampFactor) + { + pState->uMcuVert = uVertSampFactor; + } + uHorzSampFactor = pComp[1] >> 4; + if (pState->uMcuHorz < uHorzSampFactor) + { + pState->uMcuHorz = uHorzSampFactor; + } + } + + // calculate image dimensions in terms of number of mcu blocks + pState->uMcuHorzM = (pState->uImageWidth + (8*pState->uMcuHorz)-1) / (pState->uMcuHorz*8); + pState->uMcuVertM = (pState->uImageHeight + (8*pState->uMcuVert)-1) / (pState->uMcuVert*8); + + // parse components to fill in comp table + for (pComp = pFrame+6, uComp = 0; uComp < uCompCount; uComp++, pComp += 3) + { + // skip invalid tabls + if (pComp[0] > 3) + { + NetPrintf(("dirtyjpg: skipping invalid comp table index\n")); + continue; + } + + // ref comp table + pCompTable = &pState->CompTable[pComp[0]]; + + // init mcu + pCompTable->uMcuHorz = pState->uMcuHorz; + pCompTable->uMcuVert = pState->uMcuVert; + + // get sampling factors + pCompTable->uCompVert = pComp[1] & 0xf; + pCompTable->uCompHorz = pComp[1] >> 4; + + // compute expansion factors + pCompTable->uExpHorz = pCompTable->uMcuHorz / pCompTable->uCompHorz; + pCompTable->uExpVert = pCompTable->uMcuVert / pCompTable->uCompVert; + + // calculate step based on aligned size + pCompTable->uStepHorz0 = 4; + pCompTable->uStepVert0 = pCompTable->uMcuHorz * 8 * 4; + + // compute scaled sample step sizes + pCompTable->uStepHorz1 = pCompTable->uStepHorz0 * pCompTable->uExpHorz; + pCompTable->uStepVert1 = pCompTable->uStepVert0 * pCompTable->uExpVert; + + // compute step * 8 + pCompTable->uStepHorz8 = pCompTable->uStepHorz1 * 8; + pCompTable->uStepVert8 = pCompTable->uStepVert1 * 8; + + // compute mcu step sizes + pCompTable->uStepHorzM = (pCompTable->uCompHorz * 8) * pCompTable->uStepHorz1; + pCompTable->uStepVertM = (pCompTable->uCompVert * 8) * pCompTable->uStepVert1; + + // reference quantization table + pCompTable->uQuantNum = pComp[2]&0x3; + } + + // index past comp table + pFrame = pComp; + + return(pFrame == pFinal ? 0 : DIRTYJPG_ERR_BADSOF0); +} + +/*F********************************************************************************/ +/*! + \Function _ParseSOS + + \Description + Parse the SOS (Start of Scan) frame + + \Input *pState - state ref + \Input *pFrame - pointer to SOS (start of scan) frame data + \Input *pFinal - end of frame data + + \Output + uint8_t * - pointer past end of frame, or NULL if there was an error + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_ParseSOS(DirtyJpgStateT *pState, const uint8_t *pFrame, const uint8_t *pFinal) +{ + uint32_t uCompIdx, uCompCount, uMcuH, uMcuV; + const uint8_t *pCompData; + + // point to compressed data + pCompData = pFrame - 2; + pCompData = pCompData + ((pCompData[0] << 8) | pCompData[1]); + + // save compressed data + pState->pCompData = pCompData; + + // get component count and skip to next byte + uCompCount = *pFrame++; + + // init component tables + for (uCompIdx = 0; uCompIdx < uCompCount; uCompIdx++) + { + _InitComp(pState, &pFrame[uCompIdx*2]); + } + + // decode mcus and blit them to the output buffer + for (uMcuV = 0; uMcuV < pState->uMcuVertM; uMcuV++) + { + for (uMcuH = 0; uMcuH < pState->uMcuHorzM; uMcuH++) + { + // process an MCU + if (_GetMCU(pState, pFrame, uCompCount) == 0xffffffff) + { + return(NULL); + } + + // put the colors into buffer + _PutColor(pState, &pState->CompTable[1], uMcuH, uMcuV); + } + } + + // return number of bytes consumed + return(pState->pCompData + (pState->uBitsConsumed+7)/8); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyJpgValidate + + \Description + Validate we have a supported jpeg file + + \Input *pState - state ref + \Input *pImage - pointer to image data + \Input uLength - size of image data + + \Output + int32_t - DIRTYJPG_ERR_* + + \Version 07/30/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyJpgValidate(DirtyJpgStateT *pState, const uint8_t *pImage, uint32_t uLength) +{ + // validate SOI header + if ((uLength < 16) || (pImage[0] != 0xff) || (pImage[1] != 0xd8)) + { + return(DIRTYJPG_ERR_TOOSHORT); + } + // validate app header + if ((pImage[2] != 0xff) || ((pImage[3] != 0xe0) && (pImage[3] != 0xe1))) + { + return(DIRTYJPG_ERR_NOMAGIC); + } + // validate format marker + if (memcmp(pImage+6, "JFIF", 4) && memcmp(pImage+6, "Exif", 4)) + { + return(DIRTYJPG_ERR_NOFORMAT); + } + return(DIRTYJPG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyJpgDecodeParse + + \Description + Parse the JFIF image data. + + \Input *pState - state ref + \Input *pImage - pointer to image data + \Input uLength - size of image data + \Input bIdentify - identify if this is a JFIF image or not + + \Output + int32_t - DIRTYJPG_ERR_* + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _DirtyJpgDecodeParse(DirtyJpgStateT *pState, const uint8_t *pImage, uint32_t uLength, uint8_t bIdentify) +{ + int32_t iError = 0; + const uint8_t *pStart=pImage, *pFinal = pImage+uLength; + uint8_t uType; + uint32_t uSize; + + // save pointer past end of input + pState->pSrcFinal = pFinal; + + // decode all the frames + while (pImage < pFinal) + { + // get the frame info + uType = *pImage++; + + // gobble alignment byte + if (uType == 0xff) + { + continue; + } + + // get size (no size for eoi) + uSize = (uType != 0xd9) ? (pImage[0]<<8)|(pImage[1]<<0) : 0; + + // validate the frame size + if ((pImage+uSize) > pFinal) + { + iError = DIRTYJPG_ERR_ENDOFDATA; + break; + } + + #if DIRTYJPG_VERBOSE + NetPrintf(("dirtyjpg: frame=0x%02x size=%d\n", uType, uSize)); + #endif + + // parse the frame + switch (uType) + { + // app0 frame + case 0xe0: + { + // verify its got JFIF header + if ((pImage[2] == 'J') && (pImage[3] == 'F') && (pImage[4] == 'I') && (pImage[5] == 'F')) + { + // extract the version and make sure we support it + pState->uVersion = (pImage[7]<<8)|(pImage[8]>>0); + if ((pState->uVersion < 0x0100) || (pState->uVersion > 0x0102)) + { + iError = DIRTYJPG_ERR_BADVERS; + break; + } + + // extract & save aspect info + pState->uAspectRatio = pImage[9]; + pState->uAspectHorz = (pImage[10]<<8)|(pImage[11]<<0); + pState->uAspectVert = (pImage[12]<<8)|(pImage[13]<<0); + + // if just doing identification, bail + if (bIdentify) + { + return(DIRTYJPG_ERR_NONE); + } + break; + } + } + // dqt frame + case 0xdb: + { + iError = _ParseDQT(pState, pImage+2, pImage+uSize); + break; + } + // dht frame + case 0xc4: + { + iError = _ParseDHT(pState, pImage+2, pImage+uSize); + break; + } + // sof0 frame (baseline jpeg) + case 0xc0: + { + iError = _ParseSOF0(pState, pImage+2, pImage+uSize); + break; + } + // sof2 frame (progressive jpeg) + case 0xc2: + NetPrintf(("dirtyjpg: warning; SOF2 frame indicates a progressively encoded image, which is not supported\n")); + iError = DIRTYJPG_ERR_NOSUPPORT; + break; + + // sos (start of scan) frame + case 0xda: + { + if (pState->pImageBuf != NULL) + { + pImage = _ParseSOS(pState, pImage+2, pImage+uSize); + if (pImage == NULL) + { + iError = DIRTYJPG_ERR_DECODER; + } + } + else + { + iError = DIRTYJPG_ERR_NOBUFFER; + pState->uLastOffset = (uint32_t)(pImage-pStart); + } + break; + } + // skip "no action" frames + case 0xd9: // eoi frame + case 0xfe: // com frame + { + break; + } + + default: + if ((uType & 0xf0) != 0xe0) + { + NetPrintf(("dirtyjpg: ignoring unrecognized frame type 0x%02x\n", uType)); + } + #if DIRTYJPG_VERBOSE + else + { + NetPrintf(("dirtyjpg: ignoring application-specific frame type 0x%02x\n", uType)); + } + #endif + break; + } + + // bail if we got an error + if (iError != 0) + { + break; + } + + // move to next record + pImage += uSize; + } + + // save last error + pState->iLastError = iError; + return(iError); +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function DirtyJpgCreate + + \Description + Create the DirtyJpg module state + + \Output + DirtyJpgStateT * - pointer to new ref, or NULL if error + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +DirtyJpgStateT *DirtyJpgCreate(void) +{ + DirtyJpgStateT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pState = DirtyMemAlloc(sizeof(*pState), DIRTYJPG_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtyjpg: error allocating module state\n")); + return(NULL); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + + // return the state pointer + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function DirtyJpgReset + + \Description + Reset the DirtyJpg module state + + \Input *pState - pointer to module state + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyJpgReset(DirtyJpgStateT *pState) +{ + pState->uLastOffset = 0; + + pState->uBitfield = 0; + pState->uBitsAvail = 0; + pState->uBitsConsumed = 0; + pState->uBytesRead = 0; + + // clear all comp tables + ds_memclr(pState->CompTable, sizeof(pState->CompTable)); + + // clear all quant tables + ds_memclr(pState->QuantTable, sizeof(pState->QuantTable)); + + // clear all huffman tables + ds_memclr(pState->HuffTable, sizeof(pState->HuffTable)); + + // set the image buffer to NULL + pState->pImageBuf = NULL; + + pState->bRestart = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function DirtyJpgDestroy + + \Description + Destroy the DirtyJpg module + + \Input *pState - pointer to module state + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void DirtyJpgDestroy(DirtyJpgStateT *pState) +{ + DirtyMemFree(pState, DIRTYJPG_MEMID, pState->iMemGroup, pState->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function DirtyJpgIdentify + + \Description + Identify if input image is a JPG (JFIF) image. + + \Input *pState - jpg module state + \Input *pImage - pointer to image data + \Input uLength - size of image data + + \Output + int32_t - TRUE if a JPG, else FALSE + + \Version 03/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyJpgIdentify(DirtyJpgStateT *pState, const uint8_t *pImage, uint32_t uLength) +{ + int32_t iResult; + + // make sure it's a supported jpeg format + if ((iResult = _DirtyJpgValidate(pState, pImage, uLength)) == DIRTYJPG_ERR_NONE) + { + // run the parser + iResult = _DirtyJpgDecodeParse(pState, pImage+2, uLength-2, TRUE); + } + + // return true if it's a JFIF jpg, else false + return((iResult == DIRTYJPG_ERR_NOBUFFER) || (iResult == DIRTYJPG_ERR_NONE)); +} + +/*F********************************************************************************/ +/*! + \Function DirtyJpgDecodeHeader + + \Description + Parse JPG header. + + \Input *pState - pointer to module state + \Input *pJpgHdr - pointer to jpg header + \Input *pImage - pointer to image buf + \Input uLength - size of input image + + \Output + int32_t - DIRTYJPG_ERR_* + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyJpgDecodeHeader(DirtyJpgStateT *pState, DirtyJpgHdrT *pJpgHdr, const uint8_t *pImage, uint32_t uLength) +{ + int32_t iResult; + + // reset internal state + DirtyJpgReset(pState); + + // make sure it's a supported jpeg format + if ((iResult = _DirtyJpgValidate(pState, pImage, uLength)) < 0) + { + return(iResult); + } + + // run the parser -- we should get a no buffer error + if ((iResult = _DirtyJpgDecodeParse(pState, pImage+2, uLength-2, FALSE)) == DIRTYJPG_ERR_NOBUFFER) + { + // this is expected during header parsing -- save info and return no error + pJpgHdr->pImageData = pImage; + pJpgHdr->uLength = uLength; + pJpgHdr->uWidth = pState->uImageWidth; + pJpgHdr->uHeight = pState->uImageHeight; + iResult = 0; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function DirtyJpgDecodeImage + + \Description + Parse JPG image. + + \Input *pState - pointer to module state + \Input *pJpgHdr - pointer to jpg header + \Input *pImageBuf - pointer to image buf + \Input iBufWidth - image buf width + \Input iBufHeight - image buf height + + \Output + int32_t - DIRTYJPG_ERR_* + + \Version 02/23/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t DirtyJpgDecodeImage(DirtyJpgStateT *pState, DirtyJpgHdrT *pJpgHdr, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight) +{ + int32_t iError; + + // make sure we are in proper state + if (pState->iLastError != DIRTYJPG_ERR_NOBUFFER) + { + return(DIRTYJPG_ERR_BADSTATE); + } + + // setup the output buffer + pState->pImageBuf = pImageBuf; + pState->uBufWidth = (unsigned)iBufWidth; + pState->uBufHeight = (unsigned)iBufHeight; + + // resume parsing at last spot + iError = _DirtyJpgDecodeParse(pState, pJpgHdr->pImageData + pState->uLastOffset, pJpgHdr->uLength-pState->uLastOffset, FALSE); + return(iError); +} diff --git a/r5dev/thirdparty/dirtysdk/source/graph/dirtypng.c b/r5dev/thirdparty/dirtysdk/source/graph/dirtypng.c new file mode 100644 index 00000000..8af861e8 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/graph/dirtypng.c @@ -0,0 +1,2829 @@ +/*H********************************************************************************/ +/*! + \File dirtypng.c + + \Description + Routines to decode a PNG into a raw image and palette. These routines + were written from scratch based on the published specifications. The + functions and style were heavily based on the dirtygif.c and dirtyjpeg.c + files coded by James Brookes. + + \Notes + References: + Info page: http://www.libpng.org/pub/png/ + Sample images: http://www.libpng.org/pub/png/pngsuite.html + W3C Specification: http://www.libpng.org/pub/png/spec/iso/index-object.html + + \Copyright + Copyright (c) 2007 Electronic Arts Inc. + + \Version 05/10/2007 (cadam) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/graph/dirtypng.h" + +/*** Defines **********************************************************************/ + +#define DIRTYPNG_DEBUG (FALSE) +#define DIRTYPNG_DEBUG_CHUNKS (DIRTYPNG_DEBUG && FALSE) +#define DIRTYPNG_DEBUG_ZLIB (DIRTYPNG_DEBUG && FALSE) +#define DIRTYPNG_DEBUG_DEFLATE (DIRTYPNG_DEBUG && FALSE) +#define DIRTYPNG_DEBUG_DYNAMIC (DIRTYPNG_DEBUG && FALSE) +#define DIRTYPNG_DEBUG_INFLATE (DIRTYPNG_DEBUG && FALSE) +#define DIRTYPNG_DEBUG_LDCODES (DIRTYPNG_DEBUG && FALSE) + +//! define set lengths +#define TYPESTART 4 +#define DATASTART 8 +#define HEADER_LEN 25 +#define SIGNATURE_LEN 8 + +//! little endian of the 32-bit CRC polynomial 1110 1101 1011 1000 1000 0011 0010 0000 (1) +#define POLYNOMIAL 0xedb88320 + +//! define max values for HLIT + 257, HDIST + 1, and HCLEN + 4 +#define MAXLIT 286 +#define MAXDIST 32 +#define MAXCLEN 19 + +/*** Macros ***********************************************************************/ + +//! macro to validate that enough space exists in the file for a particular header +#define DIRTYPNG_Validate(_pData, _pEnd, _size, _retcode) \ +{ \ + if (((_pEnd) - (_pData)) < (_size)) \ + { \ + return(_retcode); \ + } \ +} + +#define DIRTYPNG_GetInt32(_pData, _pOffset) \ + ((_pData[_pOffset] << 24) | (_pData[_pOffset+1] << 16) | (_pData[_pOffset+2] << 8) | _pData[_pOffset+3]) + +/*** Type Definitions *************************************************************/ + +//! huffman decode tables +typedef struct HuffLitTableT +{ + uint16_t uNoCodes; + uint16_t uMinCode; + uint16_t uMaxCode; + uint16_t Codes[MAXLIT]; +} HuffLitTableT; + +typedef struct HuffDistTableT +{ + uint16_t uNoCodes; + uint16_t uMinCode; + uint16_t uMaxCode; + uint16_t Codes[MAXDIST]; +} HuffDistTableT; + +typedef struct HuffCLenTableT +{ + uint16_t uNoCodes; + uint16_t uMinCode; + uint16_t uMaxCode; + uint16_t Codes[MAXCLEN]; +} HuffCLenTableT; + +typedef struct PngPaletteT +{ + uint32_t uNumColors; + uint8_t aColor[256][3]; +} PngPaletteT; + +//! module state +struct DirtyPngStateT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData;//!< user data associated with mem group + + uint32_t uBufWidth; //!< width of buffer to decode into + uint32_t uBufHeight; //!< height of buffer to decode into + + uint32_t uWidthOffset; //!< offset for which byte of the current scanline we are at + uint32_t uHeightOffset; //!< offset for which scanline of the buffer we are at + uint32_t uImageOffset; //!< current offset of the output buffer + + uint32_t uDataBits; //!< bits grabbed from the IDAT data + uint32_t uByteOffset; //!< byte offset from which the next data should be read + uint32_t uIdatStart; //!< byte offset from which the current IDAT data started + uint32_t uIdatLength; //!< length of the current IDAT chunk data being read + + uint16_t uCountIdat; //!< number of IDAT chunks parsed + + uint16_t uHLit; //!< number of literal/length codes + uint16_t uHDist; //!< number of distance codes + uint16_t uHCLen; //!< number of code length codes + + uint16_t uWindowLength; //!< current length of the window + uint16_t uWindowOffset; //!< current offset in the window + uint16_t uWindowSize; //!< window size to work with + + uint16_t uScanlineWidth; //!< width of the scanline in bytes + uint16_t uCurrentScanline; //!< current scanline that is to be written to the output buffer + + uint8_t uFilterType; //!< filter type + + uint8_t uCurrentPass; //!< current pass of the image being parsed + + uint8_t uPixelWidth; //!< number of bytes per pixel + + uint8_t uWindowLog; //!< log base 2 of the window size minus 8 + + uint8_t uChunkTime; //!< tIME chunk parsed + uint8_t uChunkPhys; //!< pHYs chunk parsed + uint8_t uChunkIccp; //!< iCCP or sRGB chunk parsed + uint8_t uChunkSbit; //!< sBIT chunk parsed + uint8_t uChunkGama; //!< gAMA chunk parsed + uint8_t uChunkChrm; //!< cHRM chunk parsed + uint8_t uChunkPlte; //!< PLTE chunk parsed + uint8_t uChunkTrns; //!< tRNS chunk parsed + uint8_t uChunkHist; //!< hIST chunk parsed + uint8_t uChunkBkgd; //!< bKGD chunk parsed + uint8_t uChunkIdat; //!< IDAT chunk parsed + uint8_t uParsingIdat; //!< last chunk parsed was an IDAT chunk + + uint8_t uBType; //!< compression type for the block + uint8_t uBlockEnd; //!< done reading the block + uint8_t uBitsLeft; //!< bits left in uDataBits + + uint8_t uLitMinBits; //!< smallest bit length of the literal/length codes + uint8_t uDistMinBits; //!< smallest bit length of the distance codes + + PngPaletteT Palette; + + uint8_t uCRCTableGenerated; //!< CRC table generated + uint32_t CRCTable[256]; //!< CRC table + + uint32_t InterlacePassPixels[7]; //!< number of pixels per scanline for each pass + uint32_t InterlaceScanlines[7]; //!< number of scanlines per pass + + uint8_t SlidingWindow[32768]; //!< sliding window + + HuffLitTableT LitTables[16]; //!< huffman tables for the literal/length codes + HuffDistTableT DistTables[16]; //!< huffman tables for the distance codes + HuffCLenTableT CLenTables[8]; //!< huffman tables for the code length alphabet + + uint8_t *pPrevScanline; //!< pointer to the previous scanline + uint8_t *pCurrScanline; //!< pointer to the current scanline + uint8_t *pScanlines[2]; //!< pointers to the two scanlines + + uint8_t *pImageBuf; //!< pointer to output image buffer (allocated by caller) +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +//! code lengths and how many extra bits to grab +static const uint16_t _HLIT_Codes[] = +{ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, + 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 +}; +static const uint8_t _HLIT_Bits[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 +}; + +//! code start distances and how many extra bits to grab +static const uint16_t _HDIST_Codes[] = +{ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, + 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, + 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 +}; +static const uint8_t _HDIST_Bits[] = +{ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +//! code length value order for the code length alphabet +static const uint8_t _HCLEN_Codes[] = +{ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15 +}; + +/*** Private Functions ************************************************************/ + +#if DIRTYPNG_DEBUG_CHUNKS +/*F********************************************************************************/ +/*! + \Function _DirtyPngPrintChunk + + \Description + Display the Chunk Information. Used only for debugging. + + \Input *pPngData - pointer to the PNG data + \Input uLength - length of the chunk to print + \Input uShowData - boolean (show data or not) + + Version 02/06/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngPrintChunk(const uint8_t *pPngData, uint32_t uLength, uint8_t uShowData) +{ + NetPrintf(("dirtypng: chunk type=%c%c%c%c, len=%d, crc=0x%08x\n", pPngData[TYPESTART], pPngData[TYPESTART+1], pPngData[TYPESTART+2], pPngData[TYPESTART+3], + uLength, DIRTYPNG_GetInt32(pPngData, uLength+DATASTART))); + if (uShowData != 0) + { + NetPrintMem(pPngData+DATASTART, uLength, "chunk data"); + } +} +#endif + +#if DIRTYPNG_DEBUG_DYNAMIC +/*F********************************************************************************/ +/*! + \Function _DirtyPngPrintLitTables + + \Description + Print the Literal/Length Tables. Used only for debugging. + + \Input *pState - pointer to the module state + + Version 02/09/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngPrintLitTables(DirtyPngStateT *pState) +{ + uint16_t u, v, uNumCodes; + + NetPrintf(("dirtypng: Literal/Length Table\n")); + for (u = 0; u < 16; u++) + { + if (pState->LitTables[u].uNoCodes) + { + NetPrintf(("dirtypng: NULL\n")); + } + else + { + uNumCodes = pState->LitTables[u].uMaxCode - pState->LitTables[u].uMinCode + 1; + + NetPrintf(("dirtypng: uMinCode %u : ", pState->LitTables[u].uMinCode)); + NetPrintf(("uMaxCode %u : ", pState->LitTables[u].uMaxCode)); + for (v = 0; v < uNumCodes; v++) + { + NetPrintf(("%u ", pState->LitTables[u].Codes[v])); + } + NetPrintf(("\n")); + } + } + NetPrintf(("\n")); +} +#endif + +#if DIRTYPNG_DEBUG_DYNAMIC +/*F********************************************************************************/ +/*! + \Function _DirtyPngPrintDistTables + + \Description + Print the Distance Tables. Used only for debugging. + + \Input *pState - pointer to the module state + + Version 02/09/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngPrintDistTables(DirtyPngStateT *pState) +{ + uint16_t u, v, uNumCodes; + + NetPrintf(("dirtypng: Distance Table\n")); + for (u = 0; u < 16; u++) + { + if (pState->DistTables[u].uNoCodes) + { + NetPrintf(("dirtypng: NULL\n")); + } + else + { + uNumCodes = pState->DistTables[u].uMaxCode - pState->DistTables[u].uMinCode + 1; + + NetPrintf(("dirtypng: uMinCode %u : ", pState->DistTables[u].uMinCode)); + NetPrintf(("uMaxCode %u : ", pState->DistTables[u].uMaxCode)); + for (v = 0; v < uNumCodes; v++) + { + NetPrintf(("%u ", pState->DistTables[u].Codes[v])); + } + NetPrintf(("\n")); + } + } + NetPrintf(("\n")); +} +#endif + +#if DIRTYPNG_DEBUG_DYNAMIC +/*F********************************************************************************/ +/*! + \Function _DirtyPngPrintCLenTables + + \Description + Print the Code Length Tables. Used only for debugging. + + \Input *pState - pointer to the module state + + \Output + None. + + Version 02/09/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngPrintCLenTables(DirtyPngStateT *pState) +{ + uint16_t u, v, uNumCodes; + + NetPrintf(("dirtypng: Code Length Table\n")); + for (u = 0; u < 8; u++) + { + if (pState->CLenTables[u].uNoCodes) + { + NetPrintf(("dirtypng: NULL\n")); + } + else + { + uNumCodes = pState->CLenTables[u].uMaxCode - pState->CLenTables[u].uMinCode + 1; + + NetPrintf(("dirtypng: uMinCode %u : ", pState->CLenTables[u].uMinCode)); + NetPrintf(("uMaxCode %u : ", pState->CLenTables[u].uMaxCode)); + for (v = 0; v < uNumCodes; v++) + { + NetPrintf(("%u ", pState->CLenTables[u].Codes[v])); + } + NetPrintf(("\n")); + } + } + NetPrintf(("\n")); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _DirtyPngGenerateCRCTable + + \Description + Generate the CRC Table by calculating the CRC for all byte values possible. + + \Input *pState - pointer to the module state + + Version 05/02/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngGenerateCRCTable(DirtyPngStateT *pState) +{ + uint32_t uCRC, uByteValue, uBit; + + for (uByteValue = 0; uByteValue < 256; uByteValue++) + { + uCRC = uByteValue; + + for (uBit = 0; uBit < 8; uBit++) + { + if (uCRC & 1) + { + uCRC = (uCRC >> 1) ^ POLYNOMIAL; + } + else + { + uCRC >>= 1; + } + } + + pState->CRCTable[uByteValue] = uCRC; + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngCheckCRC + + \Description + Calculate the CRC value for the chunk and compare to the received CRC. + + \Input *pState - pointer to the module state + \Input *pPngData - pointer to the PNG data + \Input uLength - length of the chunk being checked + + \Output + int32_t - nonnegative=success, negative=error + + Version 02/06/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngCheckCRC(DirtyPngStateT *pState, const uint8_t *pPngData, uint32_t uLength) +{ + uint32_t uLen, uChunkCRC; + // PNG requires the CRC initially be set to all 1s + uint32_t uCRC = 0xFFFFFFFF; + + for (uLen = 0; uLen < uLength+TYPESTART; uLen++) + { + uCRC = uCRC ^ pPngData[uLen+TYPESTART]; + + uCRC = pState->CRCTable[uCRC & 0x000000FF] ^ (uCRC >> 8); + } + + uChunkCRC = DIRTYPNG_GetInt32(pPngData, uLength+DATASTART); + + // PNG requires that the calculated CRC's 1s complement is what gets stored for each chunk + if (~uCRC == uChunkCRC) + { + return(TRUE); + } + else + { + return(FALSE); + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngReset + + \Description + Reset the DirtyPngState structure. + + \Input *pState - pointer to the module state + + Version 05/02/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngReset(DirtyPngStateT *pState) +{ + pState->uWidthOffset = 0; + pState->uHeightOffset = 0; + pState->uImageOffset = 0; + + pState->uDataBits = 0; + + pState->uWindowLength = 0; + pState->uWindowOffset = 0; + + pState->uCurrentScanline = 0; + + pState->uFilterType = (uint8_t)-1; + + pState->uCurrentPass = 0; + + pState->uChunkTime = FALSE; + pState->uChunkPhys = FALSE; + pState->uChunkIccp = FALSE; + pState->uChunkSbit = FALSE; + pState->uChunkGama = FALSE; + pState->uChunkChrm = FALSE; + pState->uChunkPlte = FALSE; + pState->uChunkTrns = FALSE; + pState->uChunkHist = FALSE; + pState->uChunkBkgd = FALSE; + pState->uChunkIdat = FALSE; + pState->uParsingIdat = FALSE; + + pState->uBitsLeft = 0; + + ds_memclr(pState->InterlacePassPixels, sizeof(pState->InterlacePassPixels)); + ds_memclr(pState->InterlaceScanlines, sizeof(pState->InterlaceScanlines)); + + ds_memclr(pState->LitTables, sizeof(pState->LitTables)); + ds_memclr(pState->DistTables, sizeof(pState->DistTables)); + ds_memclr(pState->CLenTables, sizeof(pState->CLenTables)); + + if (pState->uCRCTableGenerated == 0) + { + _DirtyPngGenerateCRCTable(pState); + pState->uCRCTableGenerated = 1; + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngParseHeader + + \Description + Parse the PNG header. + + \Input pState - module state + \Input *pPngHdr - [out] pointer to PNG header to fill in + \Input *pPngData - pointer to PNG data + \Input *pPngEnd - pointer past the end of PNG data + + \Output + int32_t - nonnegative=success, negative=error + + \Version 02/06/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngParseHeader(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr, const uint8_t *pPngData, const uint8_t *pPngEnd) +{ + // iClearCode header + ds_memclr(pPngHdr, sizeof(*pPngHdr)); + + // skip the PNG signature + pPngData += SIGNATURE_LEN; + + // validate the IHDR chunk + DIRTYPNG_Validate(pPngData, pPngEnd, HEADER_LEN, DIRTYPNG_ERR_TOOSHORT); + + // make sure the length field specifies a chunk length of 13 + if (DIRTYPNG_GetInt32(pPngData, 0) != HEADER_LEN-12) + { + return(DIRTYPNG_ERR_TOOSHORT); + } + + #if DIRTYPNG_DEBUG_CHUNKS + _DirtyPngPrintChunk(pPngData, HEADER_LEN-12, 1); + #endif + + // check that the CRC value of the chunk is valid + if (_DirtyPngCheckCRC(pState, pPngData, HEADER_LEN-12) != TRUE) + { + return(DIRTYPNG_ERR_BADCRC); + } + + // verify that the chunk type is IHDR + if (memcmp(pPngData+4, "IHDR", 4)) + { + return(DIRTYPNG_ERR_BADTYPE); + } + + // get width and height from image descriptor + pPngHdr->uWidth = DIRTYPNG_GetInt32(pPngData, DATASTART); + pPngHdr->uHeight = DIRTYPNG_GetInt32(pPngData, DATASTART+4); + + // get bit depth and colour type from the image descriptor + pPngHdr->iBitDepth = pPngData[DATASTART+8]; + pPngHdr->iColourType = pPngData[DATASTART+9]; + + // verify that the Bit Depth and Colour type combination is valid + // check Table 11.1 of the PNG Specification for valid combinations + switch(pPngHdr->iColourType) + { + case 0: + pState->uPixelWidth = 1; + if ((pPngHdr->iBitDepth != 1) && (pPngHdr->iBitDepth != 2) && (pPngHdr->iBitDepth != 4) && + (pPngHdr->iBitDepth != 8)) + { + return(DIRTYPNG_ERR_BADDEPTH); + } + break; + case 2: + pState->uPixelWidth = 3; + if (pPngHdr->iBitDepth != 8) + { + return(DIRTYPNG_ERR_BADDEPTH); + } + break; + case 3: + pState->uPixelWidth = 1; + break; + case 4: + pState->uPixelWidth = 2; + if (pPngHdr->iBitDepth != 8) + { + return(DIRTYPNG_ERR_BADDEPTH); + } + break; + case 6: + pState->uPixelWidth = 4; + if (pPngHdr->iBitDepth != 8) + { + return(DIRTYPNG_ERR_BADDEPTH); + } + break; + default: + return(DIRTYPNG_ERR_BADCOLOR); + } + + // get compression, filter, and interlace methods from the image descriptor + pPngHdr->iCompression = pPngData[DATASTART+10]; + pPngHdr->iFilter = pPngData[DATASTART+11]; + pPngHdr->iInterlace = pPngData[DATASTART+12]; + + // verify that each method specified is valid + if (pPngHdr->iCompression != 0) + { + return(DIRTYPNG_ERR_BADCOMPR); + } + if (pPngHdr->iFilter != 0) + { + return(DIRTYPNG_ERR_BADFILTR); + } + if ((pPngHdr->iInterlace != 0) && (pPngHdr->iInterlace != 1)) + { + return(DIRTYPNG_ERR_BADINTRL); + } + + // setup the interlace arrays + if (pPngHdr->iInterlace == 0) + { + pState->InterlacePassPixels[0] = pPngHdr->uWidth; + pState->InterlaceScanlines[0] = pPngHdr->uHeight; + } + else + { + // compute how many pixels per scanline for each pass + pState->InterlacePassPixels[0] = (pPngHdr->uWidth + 7) / 8; + pState->InterlacePassPixels[1] = (pPngHdr->uWidth + 3) / 8; + pState->InterlacePassPixels[2] = (pPngHdr->uWidth + 3) / 4; + pState->InterlacePassPixels[3] = (pPngHdr->uWidth + 1) / 4; + pState->InterlacePassPixels[4] = (pPngHdr->uWidth + 1) / 2; + pState->InterlacePassPixels[5] = pPngHdr->uWidth / 2; + pState->InterlacePassPixels[6] = pPngHdr->uWidth; + + // compute how many scanlines per pass + pState->InterlaceScanlines[0] = (pPngHdr->uHeight + 7) / 8; + pState->InterlaceScanlines[1] = (pPngHdr->uHeight + 7) / 8; + pState->InterlaceScanlines[2] = (pPngHdr->uHeight + 3) / 8; + pState->InterlaceScanlines[3] = (pPngHdr->uHeight + 3) / 4; + pState->InterlaceScanlines[4] = (pPngHdr->uHeight + 1) / 4; + pState->InterlaceScanlines[5] = (pPngHdr->uHeight + 1) / 2; + pState->InterlaceScanlines[6] = pPngHdr->uHeight / 2; + } + + // return success + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngParseChunks + + \Description + Parse the PNG chunks. + + \Input *pState - pointer to the module state + \Input *pPngHdr - [out] pointer to PNG header to fill in + \Input *pPngData - pointer to PNG data + \Input *pPngEnd - pointer past the end of PNG data + + \Output + int32_t - nonnegative=success, negative=error + + \Version 02/06/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngParseChunks(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr, const uint8_t *pPngData, const uint8_t *pPngEnd) +{ + char strChunkType[5]; + uint32_t uLength; + int32_t iEndReached = FALSE; + + pPngData += SIGNATURE_LEN + HEADER_LEN; + + do + { + //! make sure there is enough data left for another chunk + DIRTYPNG_Validate(pPngData, pPngEnd, 12, DIRTYPNG_ERR_TOOSHORT); + + // get the chunk length + uLength = DIRTYPNG_GetInt32(pPngData, 0); + + DIRTYPNG_Validate(pPngData, pPngEnd, (int32_t)uLength+12, DIRTYPNG_ERR_TOOSHORT); + + // copy the chunk type value from the PNG byte stream + ds_memclr(strChunkType, sizeof(strChunkType)); + ds_strnzcpy(strChunkType, (const char *)pPngData+TYPESTART, (int32_t)sizeof(strChunkType)); + + // calculate the chunk CRC and compare with the received CRC + if (_DirtyPngCheckCRC(pState, pPngData, uLength) == FALSE) + { + return(DIRTYPNG_ERR_BADCRC); + } + + #if DIRTYPNG_DEBUG_CHUNKS + if (memcmp(strChunkType, "IDAT", 4) == 0) + { + _DirtyPngPrintChunk(pPngData, uLength, 0); + } + else + { + _DirtyPngPrintChunk(pPngData, uLength, 1); + } + #endif + + // check the chunk type and call its parser if required + // checks to make sure the chunks occur in order and duplicates + // of chunks where multiples are not allowed do not exist + if (memcmp(strChunkType, "IDAT", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: parsing chunk type = %s\n", strChunkType)); + #endif + + // make sure the IDAT chunks are sequential + if (pState->uChunkIdat && !pState->uParsingIdat) + { + return(DIRTYPNG_ERR_BADORDER); + } + + if (!pState->uChunkIdat) + { + pPngHdr->pImageData = pPngData; + pState->uChunkIdat = TRUE; + pState->uCountIdat = 1; + pState->uParsingIdat = TRUE; + } + else + { + pState->uCountIdat++; + } + } + else if (memcmp(strChunkType, "IEND", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: parsing chunk type = %s\n", strChunkType)); + #endif + + // make sure there was at least one IDAT chunk + if (!pState->uChunkIdat) + { + return(DIRTYPNG_ERR_TYPEMISS); + } + + if (pState->uParsingIdat) + { + pPngHdr->pImageEnd = pPngData; + pState->uParsingIdat = FALSE; + } + + iEndReached = TRUE; + } + else if (memcmp(strChunkType, "PLTE", 4) == 0) + { + if (pState->uChunkIdat || pState->uChunkTrns || pState->uChunkBkgd) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkPlte) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + // calculate palette size + pState->Palette.uNumColors = uLength/3; + // palette length must be a multiple of three and no larger than 256 + if ((((pState->Palette.uNumColors)*3) != uLength) || (uLength > (256*3))) + { + NetPrintf(("dirtypng: palette size of %d is invalid\n", uLength)); + return(DIRTYPNG_ERR_BADDEPTH); + } + // copy palette data + ds_memcpy_s(pState->Palette.aColor, sizeof(pState->Palette.aColor), pPngData+DATASTART, uLength); + // mark as parsed, but only if this is a paletted image; otherwise the palette is optional and we treat the image as nonpaletted + if (pState->uPixelWidth == 1) + { + pState->uChunkPlte = TRUE; + } + } + else if (memcmp(strChunkType, "tRNS", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkTrns) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkTrns = TRUE; + } + else if (memcmp(strChunkType, "cHRM", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat || pState->uChunkPlte) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkChrm) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkChrm = TRUE; + } + else if (memcmp(strChunkType, "gAMA", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat || pState->uChunkPlte) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkGama) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkGama = TRUE; + } + else if (memcmp(strChunkType, "iCCP", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat || pState->uChunkPlte) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkIccp) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkIccp = TRUE; + } + else if (memcmp(strChunkType, "sBIT", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat || pState->uChunkPlte) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkSbit) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkSbit = TRUE; + } + else if (memcmp(strChunkType, "sRGB", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat || pState->uChunkPlte) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkIccp) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkIccp = TRUE; + } + else if (memcmp(strChunkType, "tEXt", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uParsingIdat) + { + pPngHdr->pImageEnd = pPngData; + pState->uParsingIdat = FALSE; + } + } + else if (memcmp(strChunkType, "zTXt", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uParsingIdat) + { + pPngHdr->pImageEnd = pPngData; + pState->uParsingIdat = FALSE; + } + } + else if (memcmp(strChunkType, "iTXt", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uParsingIdat) + { + pPngHdr->pImageEnd = pPngData; + pState->uParsingIdat = FALSE; + } + } + else if (memcmp(strChunkType, "bKGD", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkBkgd) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkBkgd = TRUE; + } + else if (memcmp(strChunkType, "hIST", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (!pState->uChunkPlte) + { + return(DIRTYPNG_ERR_TYPEMISS); + } + if (pState->uChunkIdat) + { + return(DIRTYPNG_ERR_BADORDER); + } + } + else if (memcmp(strChunkType, "pHYs", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat) + { + return(DIRTYPNG_ERR_BADORDER); + } + if (pState->uChunkPhys) + { + return(DIRTYPNG_ERR_TYPEDUPL); + } + + pState->uChunkPhys = TRUE; + } + else if (memcmp(strChunkType, "sPLT", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uChunkIdat) + { + return(DIRTYPNG_ERR_BADORDER); + } + } + else if (memcmp(strChunkType, "tIME", 4) == 0) + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unsupported chunk type = %s\n", strChunkType)); + #endif + + if (pState->uParsingIdat) + { + pPngHdr->pImageEnd = pPngData; + pState->uParsingIdat = FALSE; + } + + pState->uChunkTime = TRUE; + } + else + { + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: unknown chunk type = %s\n", strChunkType)); + #endif + + // if the 5th bit of the first byte of the chunk is 0 it is + // a critical chunk and it cannot be skipped, return an error + if (!(strChunkType[0] & 0x20)) + { + return(DIRTYPNG_ERR_UNKNCRIT); + } + } + + pPngData += uLength + 12; + } while (!iEndReached); + + #if DIRTYPNG_DEBUG_CHUNKS + NetPrintf(("dirtypng: Parsing Complete\n\n")); + #endif + + // return success + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngGetNextBytes + + \Description + Check if there is another iBytes bytes available and add it to DataBits. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + \Input iBytes - byte count + + \Output + int32_t - nonnegative=success, negative=error + + \Version 02/14/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngGetNextBytes(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr, int32_t iBytes) +{ + int32_t i; + + for (i = 0; i < iBytes; i++) + { + pState->uByteOffset++; + + if (pState->uByteOffset == pState->uIdatStart + pState->uIdatLength) + { + pState->uCountIdat--; + + if (!pState->uCountIdat && !pState->uBlockEnd) + { + return(DIRTYPNG_ERR_NOBLKEND); + } + else if (pState->uCountIdat) + { + pState->uIdatLength = DIRTYPNG_GetInt32(pPngHdr->pImageData, pState->uByteOffset+4); + pState->uIdatStart = pState->uByteOffset = pState->uByteOffset + 12; + } + } + + pState->uDataBits += ((uint32_t)(pPngHdr->pImageData[pState->uByteOffset]) << pState->uBitsLeft); + pState->uBitsLeft += 8; + } + + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngRemoveBits + + \Description + Remove the specified number of bits from the DataBits. + + \Input *pState - pointer to the module state + \Input iBits - bit count + + \Version 02/14/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngRemoveBits(DirtyPngStateT *pState, int32_t iBits) +{ + pState->uDataBits >>= iBits; + pState->uBitsLeft -= iBits; +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngReverseBits + + \Description + Reverse the bits in a uint16_t. + + \Input uValue - uint16_t to reverse + \Input uBits - number of bits to reverse + + \Output + uint16_t - the reversed value of the passed in uint16_t + + \Version 02/14/2007 (cadam) +*/ +/********************************************************************************F*/ +static uint16_t _DirtyPngReverseBits(uint16_t uValue, uint16_t uBits) +{ + uint16_t u, uReversed = 0; + + uReversed = uValue & 1; + + for (u = 1; u < uBits; u++) + { + uReversed <<= 1; + uValue >>= 1; + uReversed |= (uValue & 1); + } + + return(uReversed); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngParseZlibInfo + + \Description + Parse the zlib CMF and FLG bytes. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Output + int32_t - nonnegative=success, negative=error + + \Version 02/13/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngParseZlibInfo(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + #if DIRTYPNG_DEBUG_ZLIB + NetPrintf(("\n\ndirtypng: parsing zlib info\n")); + #endif + + // make sure there are at least enough space for the CMF and FLG bytes + if ((pState->uIdatLength = DIRTYPNG_GetInt32(pPngHdr->pImageData, 0)) < 2) + { + return(DIRTYPNG_ERR_TOOSHORT); + } + + pState->uIdatStart = DATASTART; + + // make sure bits 0 to 3 of the CMF byte (CM) contains a value of 8 (Most-Significant Bit first) + if ((pPngHdr->pImageData[DATASTART] & 0x0f) != 8) + { + return(DIRTYPNG_ERR_BADCM); + } + + #if DIRTYPNG_DEBUG_ZLIB + NetPrintf(("dirtypng: CM=%u\n", pPngHdr->pImageData[DATASTART] & 0x0f)); + #endif + + // make sure bits 4 to 7 of the CMF byte (CINFO) specifies a value of 7 or less + pState->uWindowLog = pPngHdr->pImageData[DATASTART] >> 4; + if (pState->uWindowLog >= 8) + { + return(DIRTYPNG_ERR_BADCI); + } + pState->uWindowSize = 1 << (pState->uWindowLog + 8); + + #if DIRTYPNG_DEBUG_ZLIB + NetPrintf(("dirtypng: CINFO=%u\n\n", pPngHdr->pImageData[DATASTART] >> 4)); + #endif + + // the CMF byte * 256 + the FLG byte should be a multiple of 31 + if ((((uint16_t)pPngHdr->pImageData[DATASTART]) << 8 | pPngHdr->pImageData[DATASTART+1]) % 31 != 0) + { + return(DIRTYPNG_ERR_BADFLG); + } + + if (pPngHdr->pImageData[DATASTART+1] & 0x20) + { + return(DIRTYPNG_ERR_FDICTSET); + } + + pState->uByteOffset = DATASTART + 1; + + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngParseDeflateInfo + + \Description + Parse the deflate information. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Output + int32_t - nonnegative=success, negative=error + + \Version 02/13/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngParseDeflateInfo(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + int32_t iRetVal; + + #if DIRTYPNG_DEBUG_DEFLATE + NetPrintf(("dirtypng: parsing deflate info\n")); + #endif + + if (pState->uBitsLeft < 3) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + iRetVal = pState->uDataBits & 1; + _DirtyPngRemoveBits(pState, 1); + + pState->uBType = pState->uDataBits & ((1 << 2) - 1); + _DirtyPngRemoveBits(pState, 2); + + #if DIRTYPNG_DEBUG_DEFLATE + NetPrintf(("dirtypng: BFINAL=%u\n", iRetVal)); + NetPrintf(("dirtypng: BTYPE=%u\n\n", pState->uBType)); + #endif + + if (pState->uBType == 3) + { + iRetVal = DIRTYPNG_ERR_BADBTYPE; + } + + return(iRetVal); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngWriteScanline + + \Description + Write the original scanline into the output buffer. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Version 05/11/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngWriteScanline(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + uint32_t u = 0, uScanlineOffset = 0, uScanlineWidth, uPixelWidth; + uint8_t uShiftsPerByte = 8 / pPngHdr->iBitDepth; + uint8_t uShift = uShiftsPerByte - 1; + uint8_t uBits = (1 << pPngHdr->iBitDepth) - 1; + + uPixelWidth = !pState->uChunkPlte ? pState->uPixelWidth : 4; + uScanlineWidth = uPixelWidth * pState->InterlacePassPixels[pState->uCurrentPass]; + + if (pPngHdr->iInterlace == 1) + { + uint32_t uA7HeightOffset = 0, uA7WidthOffset = 0, uA7WidthShift = 0; + + // setup the width shift, width offset, and height offset for the current Adam7 scanline + switch(pState->uCurrentPass) + { + case 0: + uA7HeightOffset = 8 * pState->uCurrentScanline * uPixelWidth * pPngHdr->uWidth; + uA7WidthShift = 7 * uPixelWidth; + break; + case 1: + uA7WidthOffset = 4 * uPixelWidth; + uA7HeightOffset = 8 * pState->uCurrentScanline * uPixelWidth * pPngHdr->uWidth; + uA7WidthShift = 7 * uPixelWidth; + break; + case 2: + uA7HeightOffset = (8 * pState->uCurrentScanline + 4) * uPixelWidth * pPngHdr->uWidth; + uA7WidthShift = 3 * uPixelWidth; + break; + case 3: + uA7WidthOffset = 2 * uPixelWidth; + uA7HeightOffset = 4 * pState->uCurrentScanline * uPixelWidth * pPngHdr->uWidth; + uA7WidthShift = 3 * uPixelWidth; + break; + case 4: + uA7HeightOffset = (4 * pState->uCurrentScanline + 2 )* uPixelWidth * pPngHdr->uWidth; + uA7WidthShift = uPixelWidth; + break; + case 5: + uA7WidthOffset = 1 * uPixelWidth; + uA7HeightOffset = 2 * pState->uCurrentScanline * uPixelWidth * pPngHdr->uWidth; + uA7WidthShift = uPixelWidth; + break; + case 6: + uA7HeightOffset = (2 * pState->uCurrentScanline + 1)* uPixelWidth * pPngHdr->uWidth; + break; + } + + while (u < uScanlineWidth) + { + uint32_t uValue = ((pState->pCurrScanline[uScanlineOffset] >> (pPngHdr->iBitDepth * uShift)) & uBits) * (255 / uBits); + uint32_t uOffset = uA7HeightOffset + uA7WidthOffset; + + if (!pState->uChunkPlte) + { + pState->pImageBuf[uOffset] = uValue; + pState->uImageOffset += 1; + uA7WidthOffset += 1; + u += 1; + } + else + { + pState->pImageBuf[uOffset+0] = 0xff; + pState->pImageBuf[uOffset+1] = pState->Palette.aColor[uValue][0]; + pState->pImageBuf[uOffset+2] = pState->Palette.aColor[uValue][1]; + pState->pImageBuf[uOffset+3] = pState->Palette.aColor[uValue][2];; + pState->uImageOffset += 4; + uA7WidthOffset += 4; + u += 4; + } + + if (uShift == 0) + { + uShift = uShiftsPerByte - 1; + uScanlineOffset++; + } + else + { + uShift--; + } + + // for each pixel written make sure to shift to the next pixel offset for the current Adam7 scanline + if (u % uPixelWidth == 0) + { + uA7WidthOffset += uA7WidthShift; + } + } + } + else + { + while (u < uScanlineWidth) + { + uint8_t uValue = ((pState->pCurrScanline[uScanlineOffset] >> (pPngHdr->iBitDepth * uShift)) & uBits) * (255 / uBits); + + if (!pState->uChunkPlte) + { + pState->pImageBuf[pState->uImageOffset] = uValue; + pState->uImageOffset += 1; + u += 1; + } + else + { + pState->pImageBuf[pState->uImageOffset+0] = 0xff; + pState->pImageBuf[pState->uImageOffset+1] = pState->Palette.aColor[uValue][0]; + pState->pImageBuf[pState->uImageOffset+2] = pState->Palette.aColor[uValue][1]; + pState->pImageBuf[pState->uImageOffset+3] = pState->Palette.aColor[uValue][2]; + pState->uImageOffset += 4; + u += 4; + } + + if (uShift == 0) + { + uShift = uShiftsPerByte - 1; + uScanlineOffset++; + } + else + { + uShift--; + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngGetPaeth + + \Description + Get the distance total from the bits after the length code. + + \Input a - the byte one pixel to the left of x + \Input b - the byte at the offset of x in the previous scanline + \Input c - the byte at the offset of a in the previous scanline + + \Output + uint8_t - nonnegative=success, zero=error + + \Version 02/13/2007 (cadam) +*/ +/********************************************************************************F*/ +static uint8_t _DirtyPngGetPaeth(uint8_t a, uint8_t b, uint8_t c) +{ + int16_t p, pa, pb, pc; + + p = (int16_t)a + (int16_t)b - (int16_t)c; + pa = p - (int16_t)a; + pb = p - (int16_t)b; + pc = p - (int16_t)c; + + if (pa < 0) + { + pa = -pa; + } + if (pb < 0) + { + pb = -pb; + } + if (pc < 0) + { + pc = -pc; + } + + if (pa <= pb && pa <= pc) + { + return(a); + } + else if (pb <= pc) + { + return(b); + } + else + { + return(c); + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngFilterLine + + \Description + Reconstruct the original line. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Version 05/07/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngFilterLine(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + uint8_t *pTempScanline; + uint32_t u, uScanlineEnd, uOffsetX = 0; + + uScanlineEnd = (pState->uPixelWidth * pState->InterlacePassPixels[pState->uCurrentPass] * pPngHdr->iBitDepth + 8 - pPngHdr->iBitDepth) / 8; + + switch(pState->uFilterType) + { + case 1: + uOffsetX = pState->uPixelWidth; + while (uOffsetX != uScanlineEnd) + { + pState->pCurrScanline[uOffsetX] = pState->pCurrScanline[uOffsetX] + pState->pCurrScanline[uOffsetX - pState->uPixelWidth]; + uOffsetX++; + } + break; + case 2: + while (uOffsetX != uScanlineEnd) + { + pState->pCurrScanline[uOffsetX] = pState->pCurrScanline[uOffsetX] + pState->pPrevScanline[uOffsetX]; + uOffsetX++; + } + break; + case 3: + for (u = 0; u < pState->uPixelWidth; u++) + { + pState->pCurrScanline[uOffsetX] = pState->pCurrScanline[uOffsetX] + (pState->pPrevScanline[uOffsetX] >> 1); + uOffsetX++; + } + while (uOffsetX != uScanlineEnd) + { + pState->pCurrScanline[uOffsetX] = pState->pCurrScanline[uOffsetX] + (uint8_t)(((uint16_t)pState->pCurrScanline[uOffsetX - pState->uPixelWidth] + + (uint16_t)pState->pPrevScanline[uOffsetX]) >> 1); + uOffsetX++; + } + break; + case 4: + for (u = 0; u < pState->uPixelWidth; u++) + { + pState->pCurrScanline[uOffsetX] = pState->pCurrScanline[uOffsetX] + pState->pPrevScanline[uOffsetX]; + uOffsetX++; + } + while (uOffsetX != uScanlineEnd) + { + pState->pCurrScanline[uOffsetX] = pState->pCurrScanline[uOffsetX] + _DirtyPngGetPaeth(pState->pCurrScanline[uOffsetX - pState->uPixelWidth], + pState->pPrevScanline[uOffsetX], + pState->pPrevScanline[uOffsetX - pState->uPixelWidth]); + uOffsetX++; + } + break; + }; + + // write the scanline to the image buffer + _DirtyPngWriteScanline(pState, pPngHdr); + + // swap the scanlines then clear the current + pTempScanline = pState->pPrevScanline; + pState->pPrevScanline = pState->pCurrScanline; + pState->pCurrScanline = pTempScanline; + ds_memclr(pState->pCurrScanline, pState->uScanlineWidth); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngNoCompBlock + + \Description + Parse the length field and validate, then copy the data to the image buffer + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Output + int32_t - nonnegative=success, negative=error + + \Version 05/02/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngNoCompBlock(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + int32_t iRetVal; + uint16_t u, uBlockLen; + + _DirtyPngRemoveBits(pState, pState->uBitsLeft); + + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 4)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + + uBlockLen = (uint16_t)(pState->uDataBits & 0xffff); + _DirtyPngRemoveBits(pState, 16); + + if (uBlockLen != (uint16_t)~pState->uDataBits) + { + return(DIRTYPNG_ERR_BADBLKLEN); + } + + _DirtyPngRemoveBits(pState, pState->uBitsLeft); + + for (u = 0; u < uBlockLen; u++) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + + pState->SlidingWindow[pState->uWindowOffset++] = (uint8_t)pState->uDataBits; + + // wrap around the window if needed + if (pState->uWindowOffset == pState->uWindowSize) + { + pState->uWindowOffset = 0; + } + + // increment the uWindowLength if we haven't already reached the max length + if (pState->uWindowLength < pState->uWindowSize) + { + pState->uWindowLength++; + } + + // skip the filter byte + if (pState->uWidthOffset == 0) + { + pState->uFilterType = (int8_t)pState->uDataBits; + // make sure the filter type is valid + if (pState->uFilterType > 4) + { + return(DIRTYPNG_ERR_BADTYPE); + } + pState->uWidthOffset++; + } + else + { + pState->pCurrScanline[pState->uWidthOffset++ - 1] = (uint8_t)pState->uDataBits; + + if (pState->uWidthOffset - 1 == (pState->uPixelWidth * pState->InterlacePassPixels[pState->uCurrentPass] * pPngHdr->iBitDepth + 8 - pPngHdr->iBitDepth) / 8) + { + pState->uWidthOffset = 0; + _DirtyPngFilterLine(pState, pPngHdr); + + pState->InterlaceScanlines[pState->uCurrentPass]--; + pState->uCurrentScanline++; + + if (pState->InterlaceScanlines[pState->uCurrentPass] == 0) + { + pState->uCurrentPass++; + pState->uCurrentScanline = 0; + ds_memclr(pState->pPrevScanline, pState->uScanlineWidth); + } + while ((pState->uCurrentPass < 7) && (pState->InterlaceScanlines[pState->uCurrentPass] == 0)) + { + pState->uCurrentPass++; + } + + if ((pState->uCurrentPass == 7) && (u + 1 != uBlockLen)) + { + return(DIRTYPNG_ERR_INVFILE); + } + } + } + + _DirtyPngRemoveBits(pState, pState->uBitsLeft); + } + + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngFixedBlock + + \Description + Use the default huffman codes. These codes are specified in section 3.2.6 of rfc1951. + Literal Information: + 0-143 00110000->10111111 (8 bits) + 144-255 110010000->111111111 (9 bits) + 256-279 0000000->0010111 (7 bits) + 280-287 11000000->11000111 (8 bits) + Distance Information: + 0-31 00000->11111 (5 bits) + Distance codes 30 and 31 will never appear in the compressed data + + \Input *pState - pointer to the module state + + \Output + int32_t - nonnegative=success, negative=error + + \Version 02/13/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngFixedBlock(DirtyPngStateT *pState) +{ + // form the table with the fixed codes-literal value combinations + // found in section 3.2.6 of RFC1951 + uint16_t u, uCodeValue; + + pState->uLitMinBits = 7; + pState->uDistMinBits = 5; + + // set no codes to 1 for all bit lengths. 7, 8 and 9 bit codes will get set to zero later. + for (u = 0; u < 16; u++) + { + pState->LitTables[u].uNoCodes = 1; + pState->DistTables[u].uNoCodes = 1; + } + + // setup the literal/length table + pState->LitTables[7].uNoCodes = 0; + pState->LitTables[7].uMinCode = 0; + pState->LitTables[7].uMaxCode = 23; + + uCodeValue = 256; + for (u = 0; u < 24; u++) + { + pState->LitTables[7].Codes[u] = uCodeValue++; + } + + pState->LitTables[8].uNoCodes = 0; + pState->LitTables[8].uMinCode = 48; + pState->LitTables[8].uMaxCode = 199; + + uCodeValue = 0; + for (u = 0; u < 144; u++) + { + pState->LitTables[8].Codes[u] = uCodeValue++; + } + uCodeValue = 280; + for (; u < 152; u++) + { + pState->LitTables[8].Codes[u] = uCodeValue++; + } + + pState->LitTables[9].uNoCodes = 0; + pState->LitTables[9].uMinCode = 400; + pState->LitTables[9].uMaxCode = 511; + + uCodeValue = 144; + for (u = 0; u < 112; u++) + { + pState->LitTables[9].Codes[u] = uCodeValue++; + } + + // setup the distance table + pState->DistTables[5].uNoCodes = 0; + pState->DistTables[5].uMinCode = 0; + pState->DistTables[5].uMaxCode = 29; // 29 since 30 and 31 shouldn't occur + + for (u = 0; u < 30; u++) + { + pState->DistTables[5].Codes[u] = u; + } + + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngDynamicBlock + + \Description + Parse and setup the dynamic huffman table + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Output + int32_t - nonnegative=success, negative=error + + \Version 02/13/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngDynamicBlock(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + int32_t iRetVal; + uint16_t u, v, uReversed, uMinimumBits = 0; + uint16_t uCodeFound, uCodeOffset, uCodeOverlap = 0, uCodeRepeat = 0, uCodeValue, uPrevCode = 0; + uint8_t CodeLength[MAXCLEN]; + uint16_t CodeCount[MAXCLEN]; + + ds_memclr(CodeLength, MAXCLEN); + ds_memclr(CodeCount, MAXCLEN * 2); + + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, ((14 - pState->uBitsLeft) >> 3) + 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + + pState->uHLit = (pState->uDataBits & 0x1f) + 257; + _DirtyPngRemoveBits(pState, 5); + + pState->uHDist = (pState->uDataBits & 0x1f) + 1; + _DirtyPngRemoveBits(pState, 5); + + pState->uHCLen = (pState->uDataBits & 0x0f) + 4; + _DirtyPngRemoveBits(pState, 4); + + if (pState->uHLit > 286) + { + return(DIRTYPNG_ERR_MAXCODES); + } + + #if DIRTYPNG_DEBUG_DYNAMIC + NetPrintf(("dirtypng: parsing Dynamic Block Info\n")); + NetPrintf(("dirtypng: HLit=%u\n", pState->uHLit)); + NetPrintf(("dirtypng: HDist=%u\n", pState->uHDist)); + NetPrintf(("dirtypng: HCLen=%u\n\n", pState->uHCLen)); + #endif + + // get the length codes for the code length alphabet + for (u = 0; u < pState->uHCLen; u++) + { + if (pState->uBitsLeft < 3) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + CodeLength[_HCLEN_Codes[u]] = pState->uDataBits & 7; + _DirtyPngRemoveBits(pState, 3); + CodeCount[CodeLength[_HCLEN_Codes[u]]]++; + } + for ( ; u < 19; u++) + { + CodeLength[_HCLEN_Codes[u]] = 0; + } + + uCodeValue = 0; + // zero code lengths are ignored + pState->CLenTables[0].uNoCodes = 1; + for (u = 1; u < 8; u++) + { + if (CodeCount[u] > 0) + { + if (uMinimumBits == 0) + { + uMinimumBits = u; + } + pState->CLenTables[u].uMinCode = uCodeValue; + pState->CLenTables[u].uMaxCode = uCodeValue - 1; + uCodeValue = (uCodeValue + CodeCount[u]) << 1; + pState->CLenTables[u].uNoCodes = 0; + } + else + { + uCodeValue <<= 1; + pState->CLenTables[u].uNoCodes = 1; + } + } + + // if there are no code lengths set return a NOCODES error + if (uMinimumBits == 0) + { + return(DIRTYPNG_ERR_NOCODES); + } + + for (u = 0; u < MAXCLEN; u++) + { + if (CodeLength[u] > 0) + { + uCodeOffset = pState->CLenTables[CodeLength[u]].uMaxCode - pState->CLenTables[CodeLength[u]].uMinCode + 1; + pState->CLenTables[CodeLength[u]].Codes[uCodeOffset] = u; + pState->CLenTables[CodeLength[u]].uMaxCode++; + } + } + + #if DIRTYPNG_DEBUG_DYNAMIC + NetPrintf(("dirtypng: CodeLength")); + for (u = 0; u < MAXCLEN; u++) + { + if (u % 5 == 0) + { + NetPrintf(("\ndirtypng: ")); + } + + NetPrintf(("%u ", CodeLength[u])); + } + NetPrintf(("\n\n")); + + _DirtyPngPrintCLenTables(pState); + #endif + + // reset the code count + ds_memclr(CodeCount, MAXCLEN * 2); + + // construct the literal/length and distance tables + for (u = 0; u < pState->uHLit + pState->uHDist; u++) + { + v = uMinimumBits; + uCodeFound = 0; + + do + { + while ((pState->CLenTables[v].uNoCodes == 1) && (v < 8)) + { + v++; + } + if (v == 8) + { + return(DIRTYPNG_ERR_BADCODE); + } + + if (pState->uBitsLeft < v) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + uReversed = _DirtyPngReverseBits(pState->uDataBits, v); + + if ((uReversed >= pState->CLenTables[v].uMinCode) && (uReversed <= pState->CLenTables[v].uMaxCode)) + { + // code found so remove the bits + _DirtyPngRemoveBits(pState, v); + + uCodeFound = 1; + + uCodeValue = pState->CLenTables[v].Codes[uReversed - pState->CLenTables[v].uMinCode]; + + // details of the code length values found in RFC1951 + if ((uCodeValue > 0) && (uCodeValue < 16)) + { + if (u < pState->uHLit) + { + pState->LitTables[uCodeValue].Codes[CodeCount[uCodeValue]++] = u; + } + else + { + if (uCodeOverlap == 0) + { + // set the min/max codes for the literal/length Tables + uCodeOffset = 0; + pState->uLitMinBits = 0; + pState->LitTables[0].uNoCodes = 1; + for (v = 1; v < 16; v++) + { + if (CodeCount[v] != 0) + { + if (pState->uLitMinBits == 0) + { + pState->uLitMinBits = v; + } + pState->LitTables[v].uMinCode = uCodeOffset; + pState->LitTables[v].uMaxCode = CodeCount[v] + uCodeOffset - 1; + uCodeOffset = (CodeCount[v] + uCodeOffset) << 1; + pState->LitTables[v].uNoCodes = 0; + } + else + { + uCodeOffset <<= 1; + pState->LitTables[v].uNoCodes = 1; + } + } + + // reset the code count + ds_memclr(CodeCount, MAXCLEN * 2); + + uCodeOverlap = 1; + } + + pState->DistTables[uCodeValue].Codes[CodeCount[uCodeValue]++] = u - pState->uHLit; + } + + uPrevCode = uCodeValue; + } + else if (uCodeValue == 16) + { + // a code length of 16 cannot occur on the first code + if (u == 0) + { + return(DIRTYPNG_ERR_BADCODE); + } + + if (pState->uBitsLeft < 2) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + uCodeRepeat = 3 + (pState->uDataBits & ((1 << 2) - 1)); + _DirtyPngRemoveBits(pState, 2); + } + else if (uCodeValue == 17) + { + if (pState->uBitsLeft < 3) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + uPrevCode = 0; + uCodeRepeat = 3 + (pState->uDataBits & ((1 << 3) - 1)); + _DirtyPngRemoveBits(pState, 3); + } + else if (uCodeValue == 18) + { + if (pState->uBitsLeft < 7) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + uPrevCode = 0; + uCodeRepeat = 11 + (pState->uDataBits & ((1 << 7) - 1)); + _DirtyPngRemoveBits(pState, 7); + } + + if (uCodeRepeat > 0) + { + // check to make sure the repeat value is valid + if ((u + uCodeRepeat) > (pState->uHLit + pState->uHDist)) + { + return(DIRTYPNG_ERR_BADCODE); + } + + if (uPrevCode == 0) + { + u += uCodeRepeat - 1; + uCodeRepeat = 0; + } + else + { + while (uCodeRepeat != 0) + { + if (u < pState->uHLit) + { + pState->LitTables[uPrevCode].Codes[CodeCount[uPrevCode]++] = u; + } + else + { + if (uCodeOverlap == 0) + { + // set the min/max codes for the literal/length Tables + uCodeOffset = 0; + pState->uLitMinBits = 0; + pState->LitTables[0].uNoCodes = 1; + for (v = 1; v < 16; v++) + { + if (CodeCount[v] != 0) + { + if (pState->uLitMinBits == 0) + { + pState->uLitMinBits = v; + } + pState->LitTables[v].uMinCode = uCodeOffset; + pState->LitTables[v].uMaxCode = CodeCount[v] + uCodeOffset - 1; + uCodeOffset = (CodeCount[v] + uCodeOffset) << 1; + pState->LitTables[v].uNoCodes = 0; + } + else + { + uCodeOffset <<= 1; + pState->LitTables[v].uNoCodes = 1; + } + } + + // there should always be at least one code + if (pState->uLitMinBits == 0) + { + return(DIRTYPNG_ERR_NOCODES); + } + + pState->DistTables[uPrevCode].Codes[0] = u - pState->uHLit; + + // reset the code count + ds_memclr(CodeCount, MAXCLEN * 2); + + CodeCount[uPrevCode] = 1; + uCodeOverlap = 1; + } + else + { + pState->DistTables[uPrevCode].Codes[CodeCount[uPrevCode]++] = u - pState->uHLit; + } + } + + uCodeRepeat--; + u++; + } + + // make sure to decrement u by one since the loop increments it at the end + u--; + } + } + } + + v++; + } while ((uCodeFound == 0) && (v < 8)); + + if (uCodeFound == 0) + { + return(DIRTYPNG_ERR_BADCODE); + } + } + + // set the min/max codes for the distance tables + uCodeOffset = 0; + pState->uDistMinBits = 0; + pState->DistTables[0].uNoCodes = 1; + for (u = 1; u < 16; u++) + { + if (CodeCount[u] != 0) + { + if (pState->uDistMinBits == 0) + { + pState->uDistMinBits = u; + } + pState->DistTables[u].uMinCode = uCodeOffset; + pState->DistTables[u].uMaxCode = CodeCount[u] + uCodeOffset - 1; + uCodeOffset = (CodeCount[u] + uCodeOffset) << 1; + pState->DistTables[u].uNoCodes = 0; + } + else + { + uCodeOffset <<= 1; + pState->DistTables[u].uNoCodes = 1; + } + } + + if (pState->uDistMinBits == 0) + { + return(DIRTYPNG_ERR_NOCODES); + } + + #if DIRTYPNG_DEBUG_DYNAMIC + _DirtyPngPrintLitTables(pState); + _DirtyPngPrintDistTables(pState); + #endif + + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngGetDistance + + \Description + Get the distance total from the bits after the length code. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Output + uint32_t - nonnegative=success, zero=error + + \Version 02/13/2007 (cadam) +*/ +/********************************************************************************F*/ +static uint32_t _DirtyPngGetDistance(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + uint16_t u, uDistance, uExtraBits, uParsedCode; + + u = pState->uDistMinBits; + + do + { + while ((pState->DistTables[u].uNoCodes == 1) && (u < 16)) + { + u++; + } + if (u == 16) + { + return(0); + } + + if (pState->uBitsLeft < u) + { + if (_DirtyPngGetNextBytes(pState, pPngHdr, (((u - pState->uBitsLeft) >> 3) + 1)) != DIRTYPNG_ERR_NONE) + { + return(0); + } + } + + uParsedCode = _DirtyPngReverseBits(pState->uDataBits, u); + + if ((uParsedCode >= pState->DistTables[u].uMinCode) && (uParsedCode <= pState->DistTables[u].uMaxCode)) + { + _DirtyPngRemoveBits(pState, u); + + uParsedCode = pState->DistTables[u].Codes[uParsedCode - pState->DistTables[u].uMinCode]; + + #if DIRTYPNG_DEBUG_LDCODES + NetPrintf(("\ndistance: code=%u", uParsedCode)); + #endif + + if (uParsedCode > 29) + { + return(0); + } + + uDistance = _HDIST_Codes[uParsedCode]; + uExtraBits = _HDIST_Bits[uParsedCode]; + + #if DIRTYPNG_DEBUG_LDCODES + NetPrintf((" base=%u bits=%u", uDistance, uExtraBits)); + #endif + + if (uExtraBits > 0) + { + if (pState->uBitsLeft < uExtraBits) + { + if (_DirtyPngGetNextBytes(pState, pPngHdr, (((uExtraBits - pState->uBitsLeft) >> 3) + 1)) != + DIRTYPNG_ERR_NONE) + { + return(0); + } + } + + uDistance += (pState->uDataBits & ((1 << uExtraBits) - 1)); + _DirtyPngRemoveBits(pState, uExtraBits); + } + + #if DIRTYPNG_DEBUG_LDCODES + NetPrintf((" total=%u\n", uDistance)); + #endif + + return(uDistance); + } + + u++; + } while (u < 16); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngCompBlock + + \Description + Parse the compressed codes, then copy the data to the image buffer + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Output + int32_t - nonnegative=success, negative=error + + \Version 05/04/2007 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _DirtyPngCompBlock(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + int32_t iRetVal = -1; + uint16_t u, v, uExtraBits, uFoundCode = 0, uLength, uParsedCode; + uint16_t uDistance, uDistStart, uDistEnd, uDistOffset; + + #if DIRTYPNG_DEBUG_INFLATE + uint16_t uCountFound = 0; + #endif + + pState->uBlockEnd = 0; + + #if DIRTYPNG_DEBUG_INFLATE + NetPrintf(("dirtypng: byte offset=%u\n", pState->uByteOffset)); + #endif + + do + { + u = pState->uLitMinBits; + + uFoundCode = 0; + + do + { + while ((pState->LitTables[u].uNoCodes == 1) && (u < 16)) + { + u++; + } + if (u == 16) + { + return(DIRTYPNG_ERR_BADCODE); + } + + if (pState->uBitsLeft < u) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, (((u - pState->uBitsLeft) >> 3) + 1))) != + DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + uParsedCode = _DirtyPngReverseBits(pState->uDataBits, u); + + if ((uParsedCode >= pState->LitTables[u].uMinCode) && (uParsedCode <= pState->LitTables[u].uMaxCode)) + { + _DirtyPngRemoveBits(pState, u); + + uFoundCode = 1; + + uParsedCode = pState->LitTables[u].Codes[uParsedCode - pState->LitTables[u].uMinCode]; + + if (uParsedCode < 256) + { + if (pState->uCurrentPass == 7) + { + return(DIRTYPNG_ERR_INVFILE); + } + + #if DIRTYPNG_DEBUG_INFLATE + if (uCountFound % 20 == 0) + { + NetPrintf(("\ndirtypng: ")); + } + NetPrintf(("%u ", uParsedCode)); + uCountFound++; + #endif + + pState->SlidingWindow[pState->uWindowOffset++] = (uint8_t)(uParsedCode & 0xff); + + // wrap around the window if needed + if (pState->uWindowOffset == pState->uWindowSize) + { + pState->uWindowOffset = 0; + } + + // increment the uWindowLength if we haven't already reached the max length + if (pState->uWindowLength < pState->uWindowSize) + { + pState->uWindowLength++; + } + + // skip the filter byte + if (pState->uWidthOffset == 0) + { + pState->uFilterType = (uint8_t)(uParsedCode & 0xff); + // make sure the filter type is valid + if (pState->uFilterType > 4) + { + return(DIRTYPNG_ERR_BADTYPE); + } + pState->uWidthOffset++; + } + else + { + pState->pCurrScanline[pState->uWidthOffset++ - 1] = (uint8_t)(uParsedCode & 0xff); + + if (pState->uWidthOffset - 1 == (pState->uPixelWidth * pState->InterlacePassPixels[pState->uCurrentPass] * pPngHdr->iBitDepth + 8 - pPngHdr->iBitDepth) / 8) + { + pState->uWidthOffset = 0; + _DirtyPngFilterLine(pState, pPngHdr); + + pState->InterlaceScanlines[pState->uCurrentPass]--; + pState->uCurrentScanline++; + + if (pState->InterlaceScanlines[pState->uCurrentPass] == 0) + { + pState->uCurrentPass++; + pState->uCurrentScanline = 0; + ds_memclr(pState->pPrevScanline, pState->uScanlineWidth); + } + while ((pState->uCurrentPass < 7) && (pState->InterlaceScanlines[pState->uCurrentPass] == 0)) + { + pState->uCurrentPass++; + } + } + } + } + else if ((uParsedCode > 256) && (uParsedCode < 286)) + { + if (pState->uCurrentPass == 7) + { + return(DIRTYPNG_ERR_INVFILE); + } + + #if DIRTYPNG_DEBUG_LDCODES + NetPrintf(("\nlength: code=%u", uParsedCode)); + #endif + + uParsedCode -= 257; + + uLength = _HLIT_Codes[uParsedCode]; + uExtraBits = _HLIT_Bits[uParsedCode]; + + #if DIRTYPNG_DEBUG_LDCODES + NetPrintf((" base=%u bits=%u", uLength, uExtraBits)); + #endif + + if (uExtraBits > 0) + { + if (pState->uBitsLeft < uExtraBits) + { + if ((iRetVal = _DirtyPngGetNextBytes(pState, pPngHdr, 1)) != DIRTYPNG_ERR_NONE) + { + return(iRetVal); + } + } + + uLength += (pState->uDataBits & ((1 << uExtraBits) - 1)); + _DirtyPngRemoveBits(pState, uExtraBits); + } + + #if DIRTYPNG_DEBUG_LDCODES + NetPrintf((" total=%u", uLength)); + #endif + + if ((uDistance = _DirtyPngGetDistance(pState, pPngHdr)) == 0) + { + return(DIRTYPNG_ERR_BADCODE); + } + + if (uDistance > pState->uWindowLength) + { + return(DIRTYPNG_ERR_BADCODE); + } + + uDistEnd = pState->uWindowOffset; + if (pState->uWindowOffset < uDistance) + { + uDistOffset = pState->uWindowSize + pState->uWindowOffset - uDistance; + } + else + { + uDistOffset = pState->uWindowOffset - uDistance; + } + uDistStart = uDistOffset; + + for (v = 0; v < uLength; v++) + { + if (pState->uCurrentPass == 7) + { + return(DIRTYPNG_ERR_INVFILE); + } + + #if DIRTYPNG_DEBUG_INFLATE + if (uCountFound % 20 == 0) + { + NetPrintf(("\ndirtypng: ")); + } + NetPrintf(("%u ", pState->SlidingWindow[uDistOffset])); + uCountFound++; + #endif + + pState->SlidingWindow[pState->uWindowOffset++] = pState->SlidingWindow[uDistOffset]; + + if (pState->uWindowOffset == pState->uWindowSize) + { + pState->uWindowOffset = 0; + } + + // increment the uWindowLength if we haven't already reached the max length + if (pState->uWindowLength < pState->uWindowSize) + { + pState->uWindowLength++; + } + + // skip the filter byte + if (pState->uWidthOffset == 0) + { + pState->uFilterType = pState->SlidingWindow[uDistOffset++]; + // make sure the filter type is valid + if (pState->uFilterType > 4) + { + return(DIRTYPNG_ERR_BADTYPE); + } + pState->uWidthOffset++; + } + else + { + pState->pCurrScanline[pState->uWidthOffset++ - 1] = pState->SlidingWindow[uDistOffset++]; + + if (pState->uWidthOffset == (pState->uPixelWidth * pState->InterlacePassPixels[pState->uCurrentPass] * pPngHdr->iBitDepth + 8 - pPngHdr->iBitDepth) / 8 + 1) + { + pState->uWidthOffset = 0; + _DirtyPngFilterLine(pState, pPngHdr); + + pState->InterlaceScanlines[pState->uCurrentPass]--; + pState->uCurrentScanline++; + + if (pState->InterlaceScanlines[pState->uCurrentPass] == 0) + { + pState->uCurrentPass++; + pState->uCurrentScanline = 0; + ds_memclr(pState->pPrevScanline, pState->uScanlineWidth); + } + while ((pState->uCurrentPass < 7) && (pState->InterlaceScanlines[pState->uCurrentPass] == 0)) + { + pState->uCurrentPass++; + } + } + } + + if (uDistOffset == pState->uWindowSize) + { + uDistOffset = 0; + } + if (uDistOffset == uDistEnd) + { + uDistOffset = uDistStart; + } + } + } + else if (uParsedCode >= 286) + { + return(DIRTYPNG_ERR_BADCODE); + } + } + + u++; + } while ((uFoundCode == 0) && (u < 16)); + + if (uFoundCode == 0) + { + return(DIRTYPNG_ERR_BADCODE); + } + } while (uParsedCode != 256); + + pState->uBlockEnd = 1; + + #if DIRTYPNG_DEBUG_INFLATE + NetPrintf(("dirtypng: byte offset=%u\n", pState->uByteOffset)); + #endif + + return(DIRTYPNG_ERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngConvertImage + + \Description + Convert the image to the expected ordering of DirtyGraph. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + + \Version 02/13/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngConvertImage(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr) +{ + uint8_t u = 0, uTemp = 0; + uint32_t uInOffset, uOutOffset, uPixelWidth = pState->uPixelWidth; + + switch(pPngHdr->iColourType) + { + case 0: + uInOffset = pState->uImageOffset - 1; + uPixelWidth += 3; + uOutOffset = pState->uBufHeight * uPixelWidth * pState->uBufWidth - 1; + + while (uInOffset > 0) + { + pState->pImageBuf[uOutOffset-2] = pState->pImageBuf[uOutOffset-1] = pState->pImageBuf[uOutOffset] = pState->pImageBuf[uInOffset--]; + pState->pImageBuf[uOutOffset-3] = 0xff; + + uOutOffset -= 4; + } + + break; + case 2: + uInOffset = pState->uImageOffset - 1; + uPixelWidth++; + uOutOffset = pState->uBufHeight * uPixelWidth * pState->uBufWidth - 1; + + while (uInOffset > 0) + { + if (uOutOffset % uPixelWidth == 0) + { + pState->pImageBuf[uOutOffset] = 0xff; + } + else + { + pState->pImageBuf[uOutOffset] = pState->pImageBuf[uInOffset--]; + } + + uOutOffset--; + } + + pState->pImageBuf[0] = 0xff; + break; + case 4: + uInOffset = pState->uImageOffset - 1; + uPixelWidth += 2; + uOutOffset = pState->uBufHeight * uPixelWidth * pState->uBufWidth - 1; + + while (uInOffset > 0) + { + uTemp = pState->pImageBuf[uInOffset--]; + pState->pImageBuf[uOutOffset-2] = pState->pImageBuf[uOutOffset-1] = pState->pImageBuf[uOutOffset] = pState->pImageBuf[uInOffset]; + pState->pImageBuf[uOutOffset-3] = uTemp; + + if (uInOffset == 0) + { + break; + } + + uInOffset--; + uOutOffset -= 4; + } + + break; + case 6: + uOutOffset = pState->uBufHeight * uPixelWidth * pState->uBufWidth - 1; + + while (uOutOffset > 0) + { + uTemp = pState->pImageBuf[uOutOffset]; + for (u = 0; u < uPixelWidth - 1; u++) + { + pState->pImageBuf[uOutOffset] = pState->pImageBuf[uOutOffset - 1]; + uOutOffset--; + } + pState->pImageBuf[uOutOffset] = uTemp; + + if (uOutOffset == 0) + { + break; + } + uOutOffset--; + } + break; + } +} + +/*F********************************************************************************/ +/*! + \Function _DirtyPngFreeScanlines + + \Description + Free the scanline data + + \Input *pState - pointer to module state + + \Version 05/22/2007 (cadam) +*/ +/********************************************************************************F*/ +static void _DirtyPngFreeScanlines(DirtyPngStateT *pState) +{ + if (pState->pPrevScanline != NULL) + { + DirtyMemFree(pState->pPrevScanline, DIRTYPNG_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pPrevScanline = NULL; + } + if (pState->pCurrScanline != NULL) + { + DirtyMemFree(pState->pCurrScanline, DIRTYPNG_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pCurrScanline = NULL; + } +} + +/*** Public Functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function DirtyPngCreate + + \Description + Create the DirtyPng module state + + \Output + DirtyPngStateT * - pointer to new ref, or NULL if error + + \Version 02/07/2007 (cadam) +*/ +/********************************************************************************F*/ +DirtyPngStateT *DirtyPngCreate(void) +{ + DirtyPngStateT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pState = DirtyMemAlloc(sizeof(*pState), DIRTYPNG_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtypng: error allocating module state\n")); + return(NULL); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + + // return the state pointer + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function DirtyPngDestroy + + \Description + Destroy the DirtyPng module + + \Input *pState - pointer to module state + + \Version 05/01/2007 (cadam) +*/ +/********************************************************************************F*/ +void DirtyPngDestroy(DirtyPngStateT *pState) +{ + DirtyMemFree(pState, DIRTYPNG_MEMID, pState->iMemGroup, pState->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function DirtyPngIdentify + + \Description + Identify if input image is a PNG image. + + \Input *pImageData - pointer to image data + \Input uImageLen - size of image data + + \Output + int32_t - TRUE if a PNG, else FALSE + + \Version 02/05/2007 (cadam) +*/ +/********************************************************************************F*/ +int32_t DirtyPngIdentify(const uint8_t *pImageData, uint32_t uImageLen) +{ + // make sure we have enough data + if (uImageLen < SIGNATURE_LEN) + { + return(FALSE); + } + // see of we're a PNG + // the first eight bytes of a PNG file contain the decimal values: 137 80 78 71 13 10 26 10 + if ((pImageData[0] != 0x89) || (pImageData[1] != 0x50) || (pImageData[2] != 0x4E) || (pImageData[3] != 0x47) || + (pImageData[4] != 0x0D) || (pImageData[5] != 0x0A) || (pImageData[6] != 0x1A) || (pImageData[7] != 0x0A)) + { + return(FALSE); + } + + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function DirtyPngParse + + \Description + Parse PNG header. + + \Input *pState - pointer to the module state + \Input *pPngHdr - [out] pointer to PNG header to fill in + \Input *pPngData - pointer to PNG data + \Input *pPngEnd - pointer past the end of PNG data + + \Version 02/06/2007 (cadam) +*/ +/********************************************************************************F*/ +int32_t DirtyPngParse(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr, const uint8_t *pPngData, const uint8_t *pPngEnd) +{ + int32_t iRetVal; + + // reset internal state + _DirtyPngReset(pState); + + iRetVal = _DirtyPngParseHeader(pState, pPngHdr, pPngData, pPngEnd); + + if (iRetVal == 0) + { + iRetVal = _DirtyPngParseChunks(pState, pPngHdr, pPngData, pPngEnd); + } + + return(iRetVal); +} + + +/*F********************************************************************************/ +/*! + \Function DirtyPngDecodeImage + + \Description + Decode a PNG image into an 8bit paletteized bitmap. + + \Input *pState - pointer to the module state + \Input *pPngHdr - pointer to header describing png to decode + \Input *pImageData - [out] pointer to buffer to write decoded image data to + \Input iBufWidth - width of output buffer + \Input iBufHeight - height of output buffer + + \Output + int32_t - positive=number of bytes decoded, negative=error + + \Version 05/01/2007 (cadam) +*/ +/********************************************************************************F*/ +int32_t DirtyPngDecodeImage(DirtyPngStateT *pState, DirtyPngHdrT *pPngHdr, uint8_t *pImageData, int32_t iBufWidth, int32_t iBufHeight) +{ + int32_t iLastBlock, iResult; + + pState->pImageBuf = pImageData; + pState->uBufWidth = (uint32_t)iBufWidth; + pState->uBufHeight = (uint32_t)iBufHeight; + + if ((iResult = _DirtyPngParseZlibInfo(pState, pPngHdr)) != DIRTYPNG_ERR_NONE) + { + return(iResult); + } + + // allocate memory for the previous and current scanlines + pState->uScanlineWidth = (pState->uPixelWidth * pPngHdr->uWidth * pPngHdr->iBitDepth + 8 - pPngHdr->iBitDepth) / 8; + if ((pState->pScanlines[0] = DirtyMemAlloc(pState->uScanlineWidth, DIRTYPNG_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtypng: error allocating previous scanline\n")); + return(DIRTYPNG_ERR_ALLOCFAIL); + } + ds_memclr(pState->pScanlines[0], pState->uScanlineWidth); + pState->pPrevScanline = pState->pScanlines[0]; + if ((pState->pScanlines[1] = DirtyMemAlloc(pState->uScanlineWidth, DIRTYPNG_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("dirtypng: error allocating current scanline\n")); + DirtyMemFree(pState->pPrevScanline, DIRTYPNG_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pPrevScanline = NULL; + return(DIRTYPNG_ERR_ALLOCFAIL); + } + ds_memclr(pState->pScanlines[1], pState->uScanlineWidth); + pState->pCurrScanline = pState->pScanlines[1]; + + do + { + iLastBlock = _DirtyPngParseDeflateInfo(pState, pPngHdr); + + if (iLastBlock < 0) + { + _DirtyPngFreeScanlines(pState); + return(iLastBlock); + } + + switch(pState->uBType) + { + case 0: + if ((iResult = _DirtyPngNoCompBlock(pState, pPngHdr)) != DIRTYPNG_ERR_NONE) + { + _DirtyPngFreeScanlines(pState); + return(iResult); + } + break; + case 1: + if ((iResult = _DirtyPngFixedBlock(pState)) != DIRTYPNG_ERR_NONE) + { + _DirtyPngFreeScanlines(pState); + return(iResult); + } + if ((iResult = _DirtyPngCompBlock(pState, pPngHdr)) != DIRTYPNG_ERR_NONE) + { + _DirtyPngFreeScanlines(pState); + return(iResult); + } + break; + case 2: + if ((iResult = _DirtyPngDynamicBlock(pState, pPngHdr)) != DIRTYPNG_ERR_NONE) + { + _DirtyPngFreeScanlines(pState); + return(iResult); + } + if ((iResult = _DirtyPngCompBlock(pState, pPngHdr)) != DIRTYPNG_ERR_NONE) + { + _DirtyPngFreeScanlines(pState); + return(iResult); + } + break; + } + } while (iLastBlock != TRUE); + + _DirtyPngConvertImage(pState, pPngHdr); + + // _DirtyPngCheckAlder() - Needs to be coded; + + // free the previous and current scanlines + _DirtyPngFreeScanlines(pState); + + return(DIRTYPNG_ERR_NONE); +} diff --git a/r5dev/thirdparty/dirtysdk/source/misc/qosclient.c b/r5dev/thirdparty/dirtysdk/source/misc/qosclient.c new file mode 100644 index 00000000..20a02f43 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/misc/qosclient.c @@ -0,0 +1,2728 @@ +/*H********************************************************************************/ +/*! + \File qosclient.c + + \Description + This module implements the client API for the quality of service. + + \Copyright + Copyright (c) 2017 Electronic Arts Inc. + + \Version 2.0 06/05/2017 (cvienneau) Re-write of qosapi +*/ +/********************************************************************************H*/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/misc/qoscommon.h" +#include "DirtySDK/misc/qosclient.h" +#include "DirtySDK/proto/protoname.h" +#include "DirtySDK/proto/protohttp2.h" + +/*** Defines **********************************************************************/ +//!< qosclient default listen port +#if !DIRTYCODE_XBOXONE +#define QOS_CLIENT_DEFAULT_LISTENPORT (7673) +#else +#define QOS_CLIENT_DEFAULT_LISTENPORT (0) +#endif + +#define QOS_CLIENT_SIMULATE_PACKET_LOSS (0) //!< for testing it can often be handy to lose some probes + +//!< passed to SocketControl 'rbuf' +#define QOS_CLIENT_DEFAULT_RECEIVE_BUFF_SIZE (16*1024) + +//!< passed to SocketControl 'sbuf' +#define QOS_CLIENT_DEFAULT_SEND_BUFF_SIZE (16*1024) + +/*! passed to SocketControl 'pque' + By default non-virtual sockets have a 1-deep packet queue. When _QosClientRecvCB() fails + to enter pQosClient->ThreadCrit we need space to buffer the packet. If extracting packets + from the socket receive buffer is delayed, then rtt time calculated from those probe replies + ends up being incorrectly higher than it should be. + + Testing has shown that with 6 ping sites providing ~equal ping times a packet queue of 2 is sometimes required. + If pQosClient->ThreadCrit fails to enter 50% of the time, approximately a 'pque' of 6 is required. + With pQosClient->ThreadCrit failing 100% of the time, an 11 deep 'pque' successfully allows the updating via NetConnIdle to handle it.*/ +#define QOS_CLIENT_DEFAULT_PACKET_QUEUE_SIZE (12) + +//!< qosclient timeout for socket idle callback (0 to disable the idle callback) +#define QOS_CLIENT_SOCKET_IDLE_RATE (0) + +//!< the minimum amount of time to have passed since the last update for us to consider our updates to have been stalled +#define QOS_CLIENT_DEFAULT_STALL_WINDOW (500) + +//!< the maximum amount of time to have passed since we started the qos process before we just bail out +#define QOS_CLIENT_DEFAULT_MAX_QOS_PROCESS_TIME (60000) + +//!< easily switch from https to http for testing, prod will always be https +#define QOS_CLIENT_DEFAULT_USE_HTTPS (TRUE) + +//!< every module state transition in DIRTYAPI_QOS should have a unique entry here and later can be found in the hResult +enum +{ + QOS_CLIENT_MODULE_STATUS_INIT = 2000, + QOS_CLIENT_MODULE_STATUS_PROCESS_STARTED, + QOS_CLIENT_MODULE_STATUS_TEST_CONFIG_RECIEVED, + QOS_CLIENT_MODULE_STATUS_RAW_RESULTS_READY, + QOS_CLIENT_MODULE_STATUS_RESULTS_RECEIVED, + QOS_CLIENT_MODULE_STATUS_REPORT_COMPLETE, + + QOS_CLIENT_MODULE_STATUS_UNSET = 0, + + QOS_CLIENT_MODULE_STATUS_ERROR_TEST_CONFIG_UNUSABLE = -2000, + QOS_CLIENT_MODULE_STATUS_ERROR_RPC_DECODE, + QOS_CLIENT_MODULE_STATUS_ERROR_RPC_ENCODE, + QOS_CLIENT_MODULE_STATUS_ERROR_UNKOWN_STATE, + QOS_CLIENT_MODULE_STATUS_ERROR_TEST_CONFIG_PRODUCED_NO_REQUESTS, + QOS_CLIENT_MODULE_STATUS_ERROR_FAILED_ALLOC_RECV_BUFFER, + QOS_CLIENT_MODULE_STATUS_ERROR_RECV_BUFFER_NEEDED_TOO_LARGE, + QOS_CLIENT_MODULE_STATUS_ERROR_FAILED_ALLOC_SEND_BUFFER, + QOS_CLIENT_MODULE_STATUS_ERROR_FAILED_ALLOC_SEND_BUFFER_FATAL, + QOS_CLIENT_MODULE_STATUS_ERROR_SEND_BUFFER_NEEDED_TOO_LARGE, + QOS_CLIENT_MODULE_STATUS_ERROR_SERIALIZED_PACKET_UNEXPECTED_SIZE, + QOS_CLIENT_MODULE_STATUS_ERROR_TIMEOUT, + QOS_CLIENT_MODULE_STATUS_ERROR_UNRECOVERED_ERROR + +} eQosClientModuleStatus; + +//!< every request state transition in DIRTYAPI_QOS should have a unique entry here and later can be found in the hResult +enum +{ + QOS_CLIENT_REQUEST_STATUS_INIT = 1000, + QOS_CLIENT_REQUEST_STATUS_SOCKET_LOOKUP_SUCCESS, + QOS_CLIENT_REQUEST_STATUS_INIT_SYNC_SUCCESS, + QOS_CLIENT_REQUEST_STATUS_SEND_NEXT_PROBE, + QOS_CLIENT_REQUEST_STATUS_SEND_LOST_PROBES, + QOS_CLIENT_REQUEST_STATUS_SEND_SUCCESS, + QOS_CLIENT_REQUEST_STATUS_SEND_TRY_AGAIN, + QOS_CLIENT_REQUEST_STATUS_COMPLETE_ACCEPTABLE, + QOS_CLIENT_REQUEST_STATUS_COMPLETE, + //add new positive states here + + QOS_CLIENT_REQUEST_STATUS_UNSET = 0, //unused valued + + QOS_CLIENT_REQUEST_STATUS_ERROR_SOCKET_LOOKUP_ALLOC = -1000, + QOS_CLIENT_REQUEST_STATUS_ERROR_SOCKET_LOOKUP_UPDATE, + QOS_CLIENT_REQUEST_STATUS_ERROR_TIMEOUT, + QOS_CLIENT_REQUEST_STATUS_ERROR_SENT_MAX_PROBES, + QOS_CLIENT_REQUEST_STATUS_ERROR_SITE_INVALID, + QOS_CLIENT_REQUEST_STATUS_ERROR_TEST_INVALID, + QOS_CLIENT_REQUEST_STATUS_ERROR_EXTERNAL_ADDRESS_MISMATCH, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_VERSION, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_UP_TOO_SMALL, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_DOWN_TOO_SMALL, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_UP_TOO_LARGE, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_DOWN_TOO_LARGE, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_MY_ADDRESS, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_SEVER_TIME, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_DOWN_TOO_MANY, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_UP_TOO_MANY, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_SERVICE_REQUEST_ID, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_ALREADY_COMPLETE, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_ALREADY_RECEIVED, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_HMAC, + QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_PROTOCOL, + QOS_CLIENT_REQUEST_STATUS_ERROR_TIMEOUT_PARTIAL + //add new negative states here + +} eQosClientRequestStatus; + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef enum QosClientRequestStateE +{ + REQUEST_STATE_INIT = 0, //!< prepare configuration, usually DNS resolve the server address + REQUEST_STATE_INIT_SYNC, //!< attempt to start all tests at the same time, wait for other tests to finish their init before proceeding to the send state + REQUEST_STATE_SEND, //!< send a series of probes to the target + REQUEST_STATE_RECEIVE, //!< wait for probes/response from target + REQUEST_STATE_COMPLETE, //!< succeeded or failed, callback the user and clean up + REQUEST_STATE_ERROR //!< equivalent to REQUEST_STATE_COMPLETE, but sets failure hResult error code bit +} QosClientRequestStateE; + +typedef enum QosClientModuleStateE +{ + MODULE_STATE_IDLE = 0, //!< do nothing + MODULE_STATE_INIT_COORDINATOR_COMM, //!< send a message to the coordinator announcing who we are, and any results we have so far + MODULE_STATE_UPDATE_COORDINATOR_COMM, //!< wait for response from the coordinator + MODULE_STATE_PROBE, //!< perform the tests that the coordinator has requested, once tests are completed typically communicate with the coordinator again to provide results and get next steps + MODULE_STATE_REPORT, //!< the coordinator is done with us, report results to the client + MODULE_STATE_ERROR, //!< an error has happened preventing us from doing our tests, report to the coordinator immediately + MODULE_STATE_FATAL //!< an unrecoverable error has happened, such as coordinator communication failure, halt all further actions +} QosClientModuleStateE; + +//! A QOS test is used to gather various metrics for a given ping site +typedef struct QosClientRequestT +{ + struct QosClientRequestT *pNext; //!< link to the next record + + //info about the target the test is being performed against + QosCommonSiteT site; //!< site we want to send the QOS probes to + HostentT *pQosServerHost; //!< host name lookup for the QOS server + struct sockaddr serverAddr; //!< address we want to send the QOS probes to + + // info about how the test is to be performed + QosCommonTestT test; //!< parameters describing the test + uint32_t uServiceRequestID; //!< id from the server to distinguish different clients + uint16_t uClientRequestID; //!< id generated by this client to distinguish different requests + + //storage for the results + QosCommonRawResultsT rawResults; //!< we'll store all the info we are going to send back to the coordinator here + QosCommonAddrT probeExternalAddr; //!< clients external address, and the location the server will be sending probes to. Start with to clientAddressFromCoordinator, but update to ClientAddressFromServer if available, they will likely be the same. + + //request status + QosClientRequestStateE eState; //!< the current state the request is in + uint32_t uProbesToSend; //!< the number of probes we will send total, including retries + uint32_t uWhenStarted; //!< time we started working on this request + uint32_t uWhenLastSent; //!< the last time we sent a probe for this request + uint32_t uWhenCompleted; //!< time we finished working on this request + uint32_t uCreationCount; //!< typically 0, but if a request generates a new request we increment + int16_t iLastProbeValidationError; //!< what was the last thing to go wrong when receiving probes for this request +} QosClientRequestT; + +//! current module state +struct QosClientRefT +{ + // module memory group + int32_t iMemGroup; //!< module memory group id + void *pMemGroupUserData; //!< user data associated with memory group + + // return results to the user of QosClient + QosClientCallbackT *pCallback; //!< callback function to call to when results are available + void *pUserData; //!< user specified data to pass to the callback + + // QosCoordinator communication + char strCoordinatorAddr[QOS_COMMON_MAX_URL_LENGTH]; //!< the url for the coordinator + QosCommonClientToCoordinatorRequestT rawResultStore; //!< contains the data that needs to be provided to the coordinator + QosCommonProcessedResultsT finalResults; //!< contains the result data which is ready for consumption by the client + QosCommonAddrT clientAddressFromCoordinator; //!< external address from the coordinators prospective + ProtoHttp2RefT *pProtoHttp; //!< http module, used for service requests to the QOS server + int32_t iHttpStreamId; //!< stream identifier generated on post + uint8_t *pHttpRecvBuff; //!< buffer we will receive http responses into, this need to be a member since sometimes the whole message can't come in one call (PROTOHTTP_RECVWAIT) + uint8_t *pHttpSendBuff; //!< buffer we will send http messages from, typically it will be in one shot, but if the message is particularly large multiple ProtoHttpSend calls may be needed + uint8_t *pHttpSendProgress; //!< pointer into the send buffer where rpc data is currently being sent from + uint32_t uHttpRecvBuffSize; //!< the current allocated size of pHttpRecvBuff + uint32_t uCurrentHttpRecvSize; //!< the amount of data stored in pHttpRecvBuff + uint32_t uHttpSendBuffSize; //!< the current allocated size of pHttpSendBuff + uint32_t uHttpBytesToSend; //!< total rpc body size to send to the coordinator + int32_t iHttpSoLinger; //!< passed down to the socket as a SO_LINGER option if its >= 0, a 0 value will abort the socket on disconnection with no TIME_WAIT state + uint16_t uCoordinatorPort; //!< port of the coordinator + uint8_t bUseHttps; //!< true if communication is done via https otherwise http + uint8_t bIgnoreSSLErrors; //!< if true ssl cert errors will be ignored + + // udp probe socket management + SocketT *pUdpSocket; //!< socket pointer + QosCommonAddrT localUdpAddrInfo; //!< information about the local address we bound to + uint32_t uUdpReceiveBuffSize; //!< passed to SocketControl 'rbuf' + uint32_t uUdpSendBuffSize; //!< passed to SocketControl 'sbuf' + uint32_t uUdpPacketQueueSize; //!< passed to SocketControl 'pque' defaults QOS_CLIENT_DEFAULT_PACKET_QUEUE_SIZE (12) + uint16_t uUdpRequestedListenPort; //!< port we want to listen on, we receive probes from the server here + uint16_t uUdpCurrentListenPort; //!< port we are currently listening on (we may not get the port we wanted to listen on) + int8_t bDeferredRecv; //!< data available for reading, _QosClientUpdate should take care of it + + // request management + QosClientRequestT *pRequestQueue; //!< linked list of requests pending + NetCritT ThreadCrit; //!< critical section, we lock receive thread, update, and public APIs to ensure safety with one accessors to the module at a time, since everything access the pRequestQueue + QosClientModuleStateE eModuleState; //!< what stage of the QOS procedure are we currently in + uint32_t uInitSyncTime; //!< the time when a series of tests was initialized, used a base time to see if initialization is taking a long time + uint32_t uFinalResultsRecvTime; //!< the time when the coordinator processed results arrived on this console, used to time if we want to retry based off uTimeTillRetry + uint32_t uErrorMessageCount; //!< the number of consecutive messages we received from the coordinator that resulted in an error state. + uint16_t uNextClientRequestID; //!< current request id, incremented every time a new request is made + + // update management + uint32_t uStallWindow; //!< the amount of time to pass between updates before we consider it stalled + uint32_t uQosProcessStartTime; //!< the amount of time to have passed since calling QosClientStart + uint32_t uMaxQosProcessTime; //!< the maximum amount of time to let qos run before bailing out completely + uint32_t uLastUpdateTime; //!< NetTick of the last time update was called for this module + + int16_t iSpamValue; //!< the current logging level +}; + +/*** Function Prototypes **********************************************************/ + +static int32_t _QosClientRecvCB(SocketT *pSocket, int32_t iFlags, void *pData); +static uint8_t* _QosClientAllocHttpRecvBuff(QosClientRefT *pQosClient, uint32_t uSize); +static uint8_t* _QosClientAllocHttpSendBuff(QosClientRefT *pQosClient, uint32_t uSize); +static int32_t _QosClientDestroyRequest(QosClientRefT *pQosClient, uint32_t uRequestId); + +/*** Variables ********************************************************************/ +#if DIRTYCODE_LOGGING +const char *_strQosClientRequestState[] = //!< keep in sync with QosClientRequestStateE +{ + "REQUEST_STATE_INIT ", + "REQUEST_STATE_INIT_SYNC", + "REQUEST_STATE_SEND ", + "REQUEST_STATE_RECEIVE ", + "REQUEST_STATE_COMPLETE ", + "REQUEST_STATE_ERROR " +}; + +const char *_strQosClientModuleState[] = //!< keep in sync with QosClientModuleStateE +{ + "MODULE_STATE_IDLE ", + "MODULE_STATE_INIT_COORDINATOR_COMM ", + "MODULE_STATE_UPDATE_COORDINATOR_COMM ", + "MODULE_STATE_PROBE ", + "MODULE_STATE_REPORT", + "MODULE_STATE_ERROR ", + "MODULE_STATE_FATAL " +}; + +const char *_strQosCommonFirewallType[] = //!< keep in sync with QosCommonFirewallTypeE +{ + "QOS_COMMON_FIREWALL_UNKNOWN ", + "QOS_COMMON_FIREWALL_OPEN ", + "QOS_COMMON_FIREWALL_MODERATE ", + "QOS_COMMON_FIREWALL_STRICT ", + "QOS_COMMON_FIREWALL_NUMNATTYPES" +}; +#endif + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _QosClientRequestStateTransition + + \Description + Update the requests state, status and current error code. + + \Input *pQosClient - module state + \Input *pRequest - pointer to the request + \Input eState - new state to transition to, REQUEST_STATE_* + \Input uModule - module producing the hResult code, DIRTYAPI_* + \Input iCode - status/error code to use in hResult + \Input iLogLevel - the log level this state transition should be logged at, equivalent to NetPrintfVerbose((pQosClient->iSpamValue, iLogLevel, ... + + \Version 09/19/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientRequestStateTransition(QosClientRefT *pQosClient, QosClientRequestT *pRequest, QosClientRequestStateE eState, uint32_t uModule, int16_t iCode, int32_t iLogLevel) +{ + pRequest->rawResults.hResult = DirtyErrGetHResult(uModule, iCode, eState == REQUEST_STATE_ERROR); + NetPrintfVerbose((pQosClient->iSpamValue, iLogLevel, "qosclient: request[%02d] transitioning from %s-> %s (0x%08x, %s, %s)\n", pRequest->uClientRequestID, _strQosClientRequestState[pRequest->eState], _strQosClientRequestState[eState], pRequest->rawResults.hResult, pRequest->site.strSiteName, pRequest->test.strTestName)); + + if (eState == REQUEST_STATE_ERROR) + { + pRequest->eState = REQUEST_STATE_COMPLETE; + } + else + { + pRequest->eState = eState; + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientModuleStateTransition + + \Description + Update the modules hResult, and transition to another state. + + \Input *pQosClient - module state + \Input eState - new state to transition to, MODULE_STATE_* + \Input uModule - module producing the hResult code, DIRTYAPI_* + \Input iCode - status/error code to use in hResult + \Input iLogLevel - the log level this state transition should be logged at, equivalent to NetPrintfVerbose((pQosClient->iSpamValue, iLogLevel, ... + + \Version 09/19/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientModuleStateTransition(QosClientRefT *pQosClient, QosClientModuleStateE eState, uint32_t uModule, int16_t iCode, int32_t iLogLevel) +{ + pQosClient->rawResultStore.clientModifiers.hResult = DirtyErrGetHResult(uModule, iCode, ((eState == MODULE_STATE_ERROR) || (eState == MODULE_STATE_FATAL))); + NetPrintfVerbose((pQosClient->iSpamValue, iLogLevel, "qosclient: process transitioning from %s-> %s (0x%08x)\n", _strQosClientModuleState[pQosClient->eModuleState], _strQosClientModuleState[eState], pQosClient->rawResultStore.clientModifiers.hResult, pQosClient->rawResultStore.clientModifiers.hResult)); + + // if we were sending to the coordinator, and we aren't anymore, we no longer need the send buffer + if ((pQosClient->eModuleState == MODULE_STATE_INIT_COORDINATOR_COMM) && (eState != MODULE_STATE_INIT_COORDINATOR_COMM)) + { + _QosClientAllocHttpSendBuff(pQosClient, 0); + } + + // if we were receiving/reporting results from the coordinator, and we aren't anymore, we no longer need the receive buffer + if ((pQosClient->eModuleState == MODULE_STATE_REPORT) && (eState != MODULE_STATE_REPORT)) + { + _QosClientAllocHttpRecvBuff(pQosClient, 0); + } + + // if we are no longer active, give the port back to the system, if we do qos again we'll attempt to obtain it again + if ((pQosClient->eModuleState != MODULE_STATE_IDLE) && (eState == MODULE_STATE_IDLE)) + { + if (pQosClient->pUdpSocket != NULL) + { + SocketClose(pQosClient->pUdpSocket); + pQosClient->pUdpSocket = NULL; + } + } + + // fatal errors clean up and call the callback, we can't talk to the coordinator anymore + if (eState == MODULE_STATE_FATAL) + { + //free any of our buffers, they would likely need to be re-created anyways + _QosClientAllocHttpSendBuff(pQosClient, 0); + _QosClientAllocHttpRecvBuff(pQosClient, 0); + + //if the module has gone into an error state we probably don't have any requests (because most module level errors happen before we would) + // but if we did we should get rid of them since the only course of action would be to start the qos process again. + while (pQosClient->pRequestQueue != NULL) + { + _QosClientDestroyRequest(pQosClient, pQosClient->pRequestQueue->uClientRequestID); + } + pQosClient->eModuleState = MODULE_STATE_REPORT; + return; + } + + // errors tell the coordinator about it right away, keep whatever results we can and let the coordinator figgure out what to do + if (eState == MODULE_STATE_ERROR) + { + pQosClient->eModuleState = MODULE_STATE_INIT_COORDINATOR_COMM; + return; + } + + //if nothing else has returned set the new state + pQosClient->eModuleState = eState; +} + +/*F********************************************************************************/ +/*! + \Function _QosClientUpdateLocalAddress + + \Description + Update the local address used for udp communication. + + \Input *pQosClient - pointer to module state + + \Version 09/06/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientUpdateLocalAddress(QosClientRefT *pQosClient) +{ +#if DIRTYCODE_LOGGING + char addrBuff[128]; +#endif + // if we haven't set the local address information, do so now + if (pQosClient->localUdpAddrInfo.uFamily == 0) + { + //xbox and ps4 http don't have the api to read the local addr from protohttp, but they also don't support ipv6 or have multiple nics so we can do something simpler + #if defined(DIRTYCODE_PS4) || defined(DIRTYCODE_XBOXONE) + pQosClient->localUdpAddrInfo.uFamily = AF_INET; + pQosClient->localUdpAddrInfo.addr.v4 = SocketGetLocalAddr(); + #else + struct sockaddr LocalAddr; + if (ProtoHttp2Status(pQosClient->pProtoHttp, 0, 'ladd', &LocalAddr, sizeof(LocalAddr)) == 0) + { + //if the address family of the cached value is 0, it hasn't actually been cached yet + if (LocalAddr.sa_family != 0) + { + // cache off the info about the address we are bound to, this will be sent to the coordinator as our pQosClient->rawResultStore.clientAddressInternal + // it needs caching since we will clear the rawResultStore if multiple tests are done, but we only bind the socket once + // technically this is the address used for the http communication, but it can be difficult to get the udp address so we will presume they will be the same. + QosCommonConvertAddr(&pQosClient->localUdpAddrInfo, &LocalAddr); + } + } + else + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: localUdpAddrInfo failed to get cached local address.\n")); + } + #endif + + // change the info to be shared if we have updated the local address info + if (pQosClient->localUdpAddrInfo.uFamily != 0) + { + pQosClient->localUdpAddrInfo.uPort = pQosClient->uUdpCurrentListenPort; //override the port to the one we know the udp address was bound to + pQosClient->rawResultStore.clientModifiers.clientAddressInternal = pQosClient->localUdpAddrInfo; //set the local address information to be sent on next coordinator communication + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: localUdpAddrInfo address updated to %s\n", QosCommonAddrToString(&pQosClient->localUdpAddrInfo, addrBuff, sizeof(addrBuff)))); + } + } + + // if the port from the udp socket doesn't match what we have cached update it + if (pQosClient->localUdpAddrInfo.uPort != pQosClient->uUdpCurrentListenPort) + { + pQosClient->localUdpAddrInfo.uPort = pQosClient->uUdpCurrentListenPort; //override the port to the one we know the udp address was bound to + pQosClient->rawResultStore.clientModifiers.clientAddressInternal = pQosClient->localUdpAddrInfo; //set the local address information to be sent on next coordinator communication + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: localUdpAddrInfo port updated to %s\n", QosCommonAddrToString(&pQosClient->localUdpAddrInfo, addrBuff, sizeof(addrBuff)))); + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientSocketOpen + + \Description + Open QosClient socket + + \Input *pQosClient - pointer to module state + + \Output + SocketT * - pointer to socket + + \Version 09/13/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static SocketT *_QosClientSocketOpen(QosClientRefT *pQosClient) +{ + struct sockaddr LocalAddr; + int32_t iResult; + SocketT *pSocket; + + // set the port to the default value if 0 + if (pQosClient->uUdpRequestedListenPort == 0) + { + pQosClient->uUdpRequestedListenPort = QOS_CLIENT_DEFAULT_LISTENPORT; + } + + // create the socket + if ((pSocket = SocketOpen(AF_INET, SOCK_DGRAM, 0)) == NULL) + { + NetPrintf(("qosclient: could not allocate socket\n")); + return(NULL); + } + + // make sure socket receive buffer is large enough to queue up + // multiple probe responses (worst case: 10 probes * 1200 bytes) + SocketControl(pSocket, 'rbuf', pQosClient->uUdpReceiveBuffSize, NULL, NULL); + SocketControl(pSocket, 'sbuf', pQosClient->uUdpSendBuffSize, NULL, NULL); + SocketControl(pSocket, 'pque', pQosClient->uUdpPacketQueueSize, NULL, NULL); + + SockaddrInit(&LocalAddr, AF_INET); + SockaddrInSetPort(&LocalAddr, pQosClient->uUdpRequestedListenPort); + + // bind the socket + if ((iResult = SocketBind(pSocket, &LocalAddr, sizeof(LocalAddr))) != SOCKERR_NONE) + { + NetPrintf(("qosclient: error %d binding socket to port %d, trying random\n", iResult, pQosClient->uUdpRequestedListenPort)); + SockaddrInSetPort(&LocalAddr, 0); + if ((iResult = SocketBind(pSocket, &LocalAddr, sizeof(LocalAddr))) != SOCKERR_NONE) + { + NetPrintf(("qosclient: error %d binding socket to listen\n", iResult)); + SocketClose(pSocket); + return(NULL); + } + } + + // set the current listen port + SocketInfo(pSocket, 'bind', 0, &LocalAddr, sizeof(LocalAddr)); + pQosClient->uUdpCurrentListenPort = SockaddrInGetPort(&LocalAddr); + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: bound probe socket to port %u\n", pQosClient->uUdpCurrentListenPort)); + + //update the clients local address, as new info becomes available (we just discovered the port) + _QosClientUpdateLocalAddress(pQosClient); + + // set the callback + SocketCallback(pSocket, CALLB_RECV, QOS_CLIENT_SOCKET_IDLE_RATE, pQosClient, &_QosClientRecvCB); + // return to caller + return(pSocket); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientDestroyRequest + + \Description + Remove request from queue and free any memory associated with the request. + + \Input *pQosClient - pointer to module state + \Input uRequestId - id of the request to destroy + + \Output + int32_t - 0 if successful, negative otherwise + + \Version 04/11/2008 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _QosClientDestroyRequest(QosClientRefT *pQosClient, uint32_t uRequestId) +{ + QosClientRequestT *pQosClientRequest, **ppQosClientRequest; + + // find request in queue + for (ppQosClientRequest = &pQosClient->pRequestQueue; *ppQosClientRequest != NULL; ppQosClientRequest = &(*ppQosClientRequest)->pNext) + { + // found the request to destroy? + if ((*ppQosClientRequest)->uClientRequestID == uRequestId) + { + // set the request + pQosClientRequest = *ppQosClientRequest; + + // dequeue + *ppQosClientRequest = (*ppQosClientRequest)->pNext; + + // destroy host name lookup if we have one in progress + if (pQosClientRequest->pQosServerHost != NULL) + { + pQosClientRequest->pQosServerHost->Free(pQosClientRequest->pQosServerHost); + pQosClientRequest->pQosServerHost = NULL; + } + + // free memory + DirtyMemFree(pQosClientRequest, QOS_CLIENT_MEMID, pQosClient->iMemGroup, pQosClient->pMemGroupUserData); + + return(0); + } + } + + // specified request not found + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientGetRequest + + \Description + Get a request by request ID. + + \Input *pQosClient - pointer to module state + \Input uRequestId - id of the request to get + + \Output + QosClientRequestT * - pointer to request or NULL if not found + + \Version 08/16/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static QosClientRequestT *_QosClientGetRequest(QosClientRefT *pQosClient, uint32_t uRequestId) +{ + QosClientRequestT *pRequest; + + // find request in queue + for (pRequest = pQosClient->pRequestQueue; pRequest != NULL; pRequest = pRequest->pNext) + { + // found the request? + if (pRequest->uClientRequestID == uRequestId) + { + return(pRequest); + } + } + // did not find the request + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientValidateTest + + \Description + Validate that a test contains all the required information. + + \Input *pQosClient - pointer to module state + \Input *pTest - pointer test to be validated + + \Output + int32_t - 0 if valid test, negative otherwise + + \Version 06/05/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _QosClientValidateTest(QosClientRefT *pQosClient, QosCommonTestT *pTest) +{ + if ((strcmp(pTest->strSiteName, "") == 0) || (strcmp(pTest->strTestName, "") == 0)) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: test %s skipped, incomplete information, strSiteName=%s.\n", pTest->strTestName, pTest->strSiteName)); + return(-1); + } + + if (!((pTest->uProbeCountUp == 1) || (pTest->uProbeCountDown == 1))) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: test %s skipped, uProbeCountUp(%d) or uProbeCountDown(%d) must be 1, we do not support many to many probes on this client.\n", pTest->strTestName, pTest->uProbeCountUp, pTest->uProbeCountDown)); + return(-2); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientValidateSite + + \Description + Validate that a site contains all the required information. + + \Input *pQosClient - pointer to module state + \Input *pSite - pointer site to be validated + + \Output + int32_t - 0 if valid site, negative otherwise + + \Version 06/05/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _QosClientValidateSite(QosClientRefT *pQosClient, QosCommonSiteT *pSite) +{ + // validate that the site has all the info we require + if ((pSite->uProbePort == 0) || (strcmp(pSite->strSiteName, "") == 0) || (strcmp(pSite->strProbeAddr, "") == 0)) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: site skipped, did not have complete information; site:%s, addr:%s:%d\n", pSite->strSiteName, pSite->strProbeAddr, pSite->uProbePort)); + return(-1); + } + + if (!QosCommonIsCompatibleProbeVersion(pSite->uProbeVersion)) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: site provided doesn't have a compatible version. %d, vs %d.\n", pSite->uProbeVersion, QOS_COMMON_PROBE_VERSION)); + return(-1); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientControlStrToControlInt + + \Description + Convert a string to a DirtySDK control selector, ie "abcd" = 'abcd'. + + \Input *pStr - string to convert + + \Output + int32_t - the integer value of the four character code + + \Version 06/05/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _QosClientControlStrToControlInt(const char* pStr) +{ + //control configs must be four characters long + if (strlen(pStr) == 4) + { + int32_t controlInt = 0; + // extract the type + controlInt = pStr[3]; + controlInt |= pStr[2] << 8; + controlInt |= pStr[1] << 16; + controlInt |= pStr[0] << 24; + return(controlInt); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientApplyControlConfigs + + \Description + Apply any QosClientControl selector overrides that coordinator has requested. + + \Input *pQosClient - pointer to module state + \Input *pConfig - collection of config overrides provided by the coordinator + + \Output + int32_t - number of config overrides applied. + + \Version 06/05/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _QosClientApplyControlConfigs(QosClientRefT *pQosClient, QosCommonClientConfigT *pConfig) +{ + int32_t i; + for (i = 0; i < pConfig->uNumControlConfigs; i++) + { + uint32_t controlInt = _QosClientControlStrToControlInt(pConfig->aControlConfigs[i].strControl); + QosClientControl(pQosClient, controlInt, pConfig->aControlConfigs[i].iValue, pConfig->aControlConfigs[i].strValue); + } + return(i); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientCreateRequest + + \Description + Enqueue a test to be performed against a ping site + + \Input *pQosClient - module state + \Input *pSite - information on what ping site to perform the test against + \Input *pTest - information on what settings to use while doing the test + \Input uServiceRequestID - id for a batch of requests, provided by the coordinator + + \Output + int32_t - request id, negative on failure. + + \Version 03/31/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _QosClientCreateRequest(QosClientRefT *pQosClient, QosCommonSiteT *pSite, QosCommonTestT *pTest, uint32_t uServiceRequestID) +{ + QosClientRequestT *pRequest, **ppQosClientRequest; + + NetCritEnter(&pQosClient->ThreadCrit); + // if the module's socket hasn't yet been allocated (do this a bit lazy so we can possibly configure the socket after creation) + if ((pQosClient->pUdpSocket == NULL) && ((pQosClient->pUdpSocket = _QosClientSocketOpen(pQosClient)) == NULL)) + { + NetPrintf(("qosclient: error opening listening socket.\n")); + NetCritLeave(&pQosClient->ThreadCrit); + return(-1); + } + + // allocate a request + if ((pRequest = (QosClientRequestT*)DirtyMemAlloc(sizeof(*pRequest), QOS_CLIENT_MEMID, pQosClient->iMemGroup, pQosClient->pMemGroupUserData)) == NULL) + { + NetPrintf(("qosclient: error allocating request.\n")); + NetCritLeave(&pQosClient->ThreadCrit); + return(-1); + } + ds_memclr(pRequest, sizeof(*pRequest)); + + // find end of queue and append + for (ppQosClientRequest = &pQosClient->pRequestQueue; *ppQosClientRequest != NULL; ppQosClientRequest = &(*ppQosClientRequest)->pNext) + { + } + *ppQosClientRequest = pRequest; + + // set the request values + pRequest->uServiceRequestID = uServiceRequestID; + pRequest->uClientRequestID = pQosClient->uNextClientRequestID++; + + pRequest->site = *pSite; + pRequest->test = *pTest; + + pRequest->uProbesToSend = pRequest->test.uProbeCountUp; + pRequest->uWhenStarted = NetTick(); + ds_strnzcpy(pRequest->rawResults.strSiteName, pRequest->site.strSiteName, sizeof(pRequest->rawResults.strSiteName)); + ds_strnzcpy(pRequest->rawResults.strTestName, pRequest->test.strTestName, sizeof(pRequest->rawResults.strTestName)); + + if (_QosClientValidateSite(pQosClient, &pRequest->site) < 0) + { + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_ERROR_SITE_INVALID, 0); + } + else if (_QosClientValidateTest(pQosClient, &pRequest->test) < 0) + { + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_ERROR_TEST_INVALID, 0); + } + else + { + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_INIT, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_INIT, 0); + } + + NetCritLeave(&pQosClient->ThreadCrit); + return(pRequest->uClientRequestID); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientGetOsFirewallType + + \Description + Queries DirtySDK to see if there is a NAT override to use in place of the value obtained by QOS + + \Output + QosCommonFirewallTypeE - NAT type. + + \Version 06/05/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static QosCommonFirewallTypeE _QosClientGetOsFirewallType(void) +{ + #if defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) + int32_t iResult = 0; + int32_t iNatType; + + iResult = NetConnStatus('natt', 0, &iNatType, sizeof(iNatType)); + if (iResult < 0) + { + NetPrintf(("qosclient: _QosClientGetOsFirewallType() error retrieving NAT type\n")); + return(QOS_COMMON_FIREWALL_UNKNOWN); + } + + if (iNatType == 0) + { + return(QOS_COMMON_FIREWALL_OPEN); + } + else if (iNatType == 1) + { + return(QOS_COMMON_FIREWALL_MODERATE); + } + else if (iNatType == 2) + { + return(QOS_COMMON_FIREWALL_STRICT); + } + + NetPrintf(("qosclient: _QosClientGetOsFirewallType() unknown error\n")); + return(QOS_COMMON_FIREWALL_UNKNOWN); + #else + return(QOS_COMMON_FIREWALL_UNKNOWN); + #endif +} + +/*F********************************************************************************/ +/*! + \Function _QosClientOverrideFinalResults + + \Description + Give the QosClient an opportunity to do final tweaks to the results before + providing the results to the game client. Currently not used. + + \Input *pQosClient - module state + + \Version 03/31/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientOverrideFinalResults(QosClientRefT *pQosClient) +{ +} + +/*F********************************************************************************/ +/*! + \Function _QosClientPurgeCurrentResults + + \Description + Clear any raw results we might be hanging on to + + \Input *pQosClient - pointer to module state + + \Version 03/16/2018 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientPurgeCurrentResults(QosClientRefT *pQosClient) +{ + pQosClient->rawResultStore.uNumResults = 0; + ds_memclr(pQosClient->rawResultStore.aResults, sizeof(pQosClient->rawResultStore.aResults)); + pQosClient->rawResultStore.clientModifiers.uStallCountCoordinator = 0; + pQosClient->rawResultStore.clientModifiers.uStallCountProbe = 0; + pQosClient->rawResultStore.clientModifiers.uStallDurationCoordinator = 0; + pQosClient->rawResultStore.clientModifiers.uStallDurationProbe = 0; +} + +/*F********************************************************************************/ +/*! + \Function _QosClientProcessCoordinatorResponse + + \Description + Parses the rpc response from the coordinator. + + \Input *pQosClient - pointer to module state + + \Output + int32_t - negative on error, 0 on success + + \Version 03/31/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _QosClientProcessCoordinatorResponse(QosClientRefT *pQosClient) +{ + int32_t iRet = 0; + QosCommonCoordinatorToClientResponseT response; + + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: <-- http response:\n")); + #if DIRTYCODE_LOGGING + if (pQosClient->iSpamValue >= 2) + { + NetPrintMem(pQosClient->pHttpRecvBuff, pQosClient->uCurrentHttpRecvSize, "CoordinatorToClientResponse"); + } + #endif + + if (QosCommonCoordinatorToClientResponseDecode(&response, pQosClient->pHttpRecvBuff, pQosClient->uCurrentHttpRecvSize) == 0) + { + int32_t testIdx, siteIdx; + pQosClient->uInitSyncTime = NetTick(); + + // detailed print + QosCommonCoordinatorToClientPrint(&response, pQosClient->iSpamValue); + + //validate the service id is what we expect it to be + if ((pQosClient->rawResultStore.uServiceRequestID != 0) && (pQosClient->rawResultStore.uServiceRequestID != response.uServiceRequestID)) + { + NetPrintf(("qosclient: warning, expected uServiceRequestID %d, but got %d.\n", pQosClient->rawResultStore.uServiceRequestID, response.uServiceRequestID)); + } + pQosClient->rawResultStore.uServiceRequestID = response.uServiceRequestID; + pQosClient->clientAddressFromCoordinator = response.configuration.clientAddressFromCoordinator; + + //apply the client configs if there are any, this may change many behaviors + if (response.configuration.uNumControlConfigs > 0) + { + _QosClientApplyControlConfigs(pQosClient, &response.configuration); + } + + if (response.results.uNumResults > 0) + { + //read our results and complete the QOS process + pQosClient->finalResults = response.results; + pQosClient->uFinalResultsRecvTime = NetTick(); + _QosClientOverrideFinalResults(pQosClient); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_REPORT, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_RESULTS_RECEIVED, 0); + } + else if ((response.configuration.uNumQosTests > 0) && (response.configuration.uNumSites > 0)) + { + // get rid of existing raw results, so when the new tests finish we don't send the old raw results to the coordinator again + if (pQosClient->rawResultStore.uNumResults > 0) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: purging %d existing raw results, continuing with multi-phased qos test.\n", pQosClient->rawResultStore.uNumResults)); + _QosClientPurgeCurrentResults(pQosClient); + } + + //convert this collection of information we have into a bunch of discrete request + for (testIdx = 0; testIdx < response.configuration.uNumQosTests; testIdx++) + { + for (siteIdx = 0; siteIdx < response.configuration.uNumSites; siteIdx++) + { + // if the test is against all sties, or if the site is contained in the list of sites the test should be performed against + if ((strcmp(response.configuration.aQosTests[testIdx].strSiteName, "ALL") == 0) || (strstr(response.configuration.aQosTests[testIdx].strSiteName, response.configuration.aSites[siteIdx].strSiteName) != NULL)) + { + _QosClientCreateRequest(pQosClient, &response.configuration.aSites[siteIdx], &response.configuration.aQosTests[testIdx], response.uServiceRequestID); + } + } + } + if (pQosClient->pRequestQueue != NULL) + { + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_PROBE, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_TEST_CONFIG_RECIEVED, 0); + } + else + { + NetPrintf(("qosclient: error, message from QosCoordinator did not result in any actionable requests.\n")); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_TEST_CONFIG_PRODUCED_NO_REQUESTS, 0); + iRet = -1; + } + } + else + { + NetPrintf(("qosclient: error, message from QosCoordinator was not actionable; uNumQosTests:%d, uNumSites:%d.\n", response.configuration.uNumQosTests, response.configuration.uNumSites)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_TEST_CONFIG_UNUSABLE, 0); + iRet = -1; + } + } + else + { + NetPrintf(("qosclient: error, QosCommonCoordinatorToClientResponseDecode failed.\n")); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_RPC_DECODE, 0); + iRet = -1; + } + return(iRet); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientRequestInit + + \Description + Do any preparations such as the dns resolve of the QOS server before beginning + to send probes. + + \Input *pQosClient - pointer to module state + \Input *pRequest - pointer to the request which contains the site info + + \Version 03/31/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientRequestInit(QosClientRefT *pQosClient, QosClientRequestT *pRequest) +{ + // check if we need to do a lookup of the address + if (pRequest->pQosServerHost == 0) + { + if ((pRequest->pQosServerHost = SocketLookup(pRequest->site.strProbeAddr, 30*1000)) == NULL) + { + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_PROTO_HTTP, QOS_CLIENT_REQUEST_STATUS_ERROR_SOCKET_LOOKUP_ALLOC, 0); + } + } + else // we are in the process of looking up the address + { + // check if we are done resolving the host name + if (pRequest->pQosServerHost->Done(pRequest->pQosServerHost) == TRUE) + { + if (pRequest->pQosServerHost->addr != 0) + { + // we finished successfully, update the address from the lookup + SockaddrInit(&pRequest->serverAddr, AF_INET); + SockaddrInSetAddr(&pRequest->serverAddr, pRequest->pQosServerHost->addr); + SockaddrInSetPort(&pRequest->serverAddr, pRequest->site.uProbePort); + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_INIT_SYNC, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_SOCKET_LOOKUP_SUCCESS, 0); + } + else + { + // we finished but we don't have a valid address + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_PROTO_HTTP, QOS_CLIENT_REQUEST_STATUS_ERROR_SOCKET_LOOKUP_UPDATE, 0); + } + + // we are done with the address lookup, free the resources + pRequest->pQosServerHost->Free(pRequest->pQosServerHost); + pRequest->pQosServerHost = NULL; + } + } + +} + +/*F********************************************************************************/ +/*! + \Function _QosClientRequestWaitForInit + + \Description + Transition to the send state if no other request are in the init state. + We prefer to start all tests at the same time because if the users connection + suffers from periodic lag spikes, this will produce a more "fair" result. Otherwise what + might be the best ping site can suffer from a random occurrence, and a less ideal ping + site which was lucky enough to avoid the lag spike will come out as the best ping site. + Now all ping sites should avoid or suffer from the same lag spike since they are occurring + at the same time. + + \Input *pQosClient - pointer to module state + \Input *pRequest - pointer to the request + + \Version 08/11/2016 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientRequestWaitForInit(QosClientRefT *pQosClient, QosClientRequestT *pRequest) +{ + QosClientRequestT *pRequestToCheck; + + if (NetTickDiff(NetTick(), pQosClient->uInitSyncTime) > pRequest->test.uInitSyncTimeout) //too much time passed + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] giving up on waiting for init, %dms have already passed.\n", pRequest->uClientRequestID, pRequest->test.uInitSyncTimeout)); + } + else + { + for (pRequestToCheck = pQosClient->pRequestQueue; pRequestToCheck != NULL; pRequestToCheck = pRequestToCheck->pNext) + { + if (pRequestToCheck->eState == REQUEST_STATE_INIT) + { + return;//one of the requests is still waiting to resolve the target address, lets continue to wait so that we can all measure connection metrics at the same time + } + } + } + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_SEND, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_INIT_SYNC_SUCCESS, 0); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientRequestSendProbes + + \Description + Sends probes to the request's target as necessary. + + \Input *pQosClient - pointer to module state + \Input *pRequest - pointer to the request + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientRequestSendProbes(QosClientRefT *pQosClient, QosClientRequestT *pRequest) +{ + uint32_t uSendCounter; + int32_t iResult = 0; + uint32_t uProbeCountToSend = 1; //send probes one at a time, unless uMinTimeBetwenProbes == 0 + uint8_t aSendBuff[QOS_COMMON_MAX_PACKET_SIZE]; + QosCommonProbePacketT probe; + + // figure out what external address best describes where the server will be sending its probes to, for security + // server will verify the probes are coming from this address, we switch to address from server if its available + if (pRequest->rawResults.clientAddressFromServer.uFamily == 0) + { + pRequest->probeExternalAddr = pQosClient->clientAddressFromCoordinator; + } + else + { + pRequest->probeExternalAddr = pRequest->rawResults.clientAddressFromServer; + } + + ds_memclr(&probe, sizeof(probe)); + ds_memclr(aSendBuff, sizeof(aSendBuff)); + + probe.uProtocol = QOS_COMMON_PROBE_PROTOCOL_ID; + probe.uVersion = QOS_COMMON_PROBE_VERSION; + probe.uServiceRequestId = pRequest->uServiceRequestID; + //probe.uServerReceiveTime; server use only + //probe.uServerSendDuration; server use only + probe.uProbeSizeUp = pRequest->test.uProbeSizeUp; + probe.uProbeSizeDown = pRequest->test.uProbeSizeDown; + //probe.uProbeCountUp = pRequest->test.uProbeCountUp; //modified for each probe in loop below + probe.uProbeCountDown = pRequest->test.uProbeCountDown; + probe.uClientRequestId = pRequest->uClientRequestID; + probe.clientAddressFromService = pRequest->probeExternalAddr; + probe.uExpectedProbeCountUp = pRequest->test.uProbeCountUp; + //probe.additionalBytes filled with memset send buffer + + // should we send all the probes in a burst? + if (pRequest->test.uMinTimeBetwenProbes == 0) + { + uProbeCountToSend = pRequest->uProbesToSend - pRequest->rawResults.uProbeCountUp; //target number of probes to send - the number of probes we already sent + } + + // send probes + for (uSendCounter = 0; uSendCounter < uProbeCountToSend; uSendCounter++) + { + if (pRequest->rawResults.uProbeCountUp >= QOS_COMMON_MAX_PROBE_COUNT) + { + //this can happen in a BW up test since we send a burst, but if we do lets bail + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] max probes have already been sent, not sending additional probes.\n", pRequest->uClientRequestID, &pRequest->serverAddr)); + //we don't really want to error immediately, better let it timeout so we can have a chance to receive any incoming probes (though likely is going to be a timeout scenario) + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_RECEIVE, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_ERROR_SENT_MAX_PROBES, 0); + pRequest->uWhenLastSent = NetTick(); + return; + } + + probe.uProbeCountUp = pRequest->rawResults.uProbeCountUp; + + //probe is now complete, serialize it and sign it, into the send buffer (a place where there will be extra space for the additional bandwidth bytes) + if (QosCommonSerializeProbePacket(aSendBuff, sizeof(aSendBuff), &probe, pRequest->site.aSecureKey) != QOS_COMMON_MIN_PACKET_SIZE) + { + NetPrintf(("qosclient: error serialized packet is not the expected size.\n")); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_SERIALIZED_PACKET_UNEXPECTED_SIZE, 0); + return; + } + + iResult = SocketSendto(pQosClient->pUdpSocket, (const char *)aSendBuff, probe.uProbeSizeUp, 0, &pRequest->serverAddr, sizeof(pRequest->serverAddr)); + NetPrintfVerbose((pQosClient->iSpamValue, 2, "qosclient: request[%02d] sent probe %d to %A (result=%d)\n", pRequest->uClientRequestID, pRequest->rawResults.uProbeCountUp, &pRequest->serverAddr, iResult)); + + if (iResult == probe.uProbeSizeUp) //check to see that we were successful in our last send + { + pRequest->rawResults.aProbeResult[pRequest->rawResults.uProbeCountUp].uClientSendTime = NetTick(); + pRequest->rawResults.uProbeCountUp++; + if (pRequest->rawResults.uProbeCountUp >= QOS_COMMON_MAX_PROBE_COUNT) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] max probes have now been sent, no additional probes will be sent.\n", pRequest->uClientRequestID, &pRequest->serverAddr)); + } + } + else + { + break; //some failure deal with it below + } + } + + if (iResult == probe.uProbeSizeUp) //check to see that we were successful in our last send + { + NetPrintfVerbose((pQosClient->iSpamValue, 1, "qosclient: request[%02d] sent %d probes to address %A\n", pRequest->uClientRequestID, uProbeCountToSend, &pRequest->serverAddr)); + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_RECEIVE, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_SEND_SUCCESS, 1); + pRequest->uWhenLastSent = NetTick(); + } + else if (iResult == 0) // need to try again + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] needs to try again to send probe to %A\n", pRequest->uClientRequestID, &pRequest->serverAddr)); + // transition anyways, let the re-issue logic handle the missing probes + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_RECEIVE, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_SEND_TRY_AGAIN, 0); + pRequest->uWhenLastSent = NetTick(); + } + else if (iResult < 0) // an error + { + NetPrintf(("qosclient: request[%02d] failed to send probe %d to address %A (err=%d)\n", pRequest->uClientRequestID, pRequest->rawResults.uProbeCountUp, &pRequest->serverAddr, iResult)); + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_SOCKET, iResult, 0); + } + else // unexpected result + { + NetPrintf(("qosclient: request[%02d] unexpected result for probe %d to address %A (err=%d)\n", pRequest->uClientRequestID, pRequest->rawResults.uProbeCountUp, &pRequest->serverAddr, iResult)); + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_SOCKET, iResult, 0); + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientValidatePacketQueue + + \Description + Ensure the packet queue 'pque' is in a healthy state, if its not print warnings. + + \Input *pQosClient - pointer to module state + + \Output + uint32_t - amount of space left in packet queue + + \Version 03/31/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static uint32_t _QosClientValidatePacketQueue(QosClientRefT *pQosClient) +{ + if (pQosClient->pUdpSocket != NULL) + { + //validate that the packet queue is healthy, it can be difficult to detect so we have special logging to check + int32_t iQueueSize = SocketInfo(pQosClient->pUdpSocket, 'psiz', 0, NULL, 0); + int32_t iQueueHighWater = SocketInfo(pQosClient->pUdpSocket, 'pmax', 0, NULL, 0); + +#if DIRTYCODE_LOGGING + int32_t iPacketDrop = SocketInfo(pQosClient->pUdpSocket, 'pdrp', 0, NULL, 0); + + //if we are getting close to using all of the packet queue complain always + if (iQueueHighWater >= (iQueueSize - 2)) + { + NetPrintf(("qosclient: warning %d/%d packet queue high watermark, lost packets %d.\n", iQueueHighWater, iQueueSize, iPacketDrop)); + } + else + { + NetPrintfVerbose((pQosClient->iSpamValue, 2, "qosclient: %d/%d packet queue high watermark, lost packets %d.\n", iQueueHighWater, iQueueSize, iPacketDrop)); + } +#endif + return(iQueueSize - iQueueHighWater); + } + + //if we don't have a socket, just return the default + return(QOS_CLIENT_DEFAULT_PACKET_QUEUE_SIZE); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientAllocHttpSendBuff + + \Description + Allocate a send buffer of the specified size. + + \Input *pQosClient - module state + \Input uSize - the size the send buffer should be + + \Output + char* - success, pointer to pQosClient->pHttpSendBuff; fail, NULL + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static uint8_t* _QosClientAllocHttpSendBuff(QosClientRefT *pQosClient, uint32_t uSize) +{ + DirtyMemGroupEnter(pQosClient->iMemGroup, pQosClient->pMemGroupUserData); + + // if the user is asking for a 0 size buffer they really just want to deallocate it + if (uSize == 0) + { + if (pQosClient->pHttpSendBuff != NULL) + { + DirtyMemFree(pQosClient->pHttpSendBuff, QOS_CLIENT_MEMID, pQosClient->iMemGroup, pQosClient->pMemGroupUserData); + pQosClient->pHttpSendBuff = NULL; + } + DirtyMemGroupLeave(); + pQosClient->uHttpSendBuffSize = 0; + pQosClient->uHttpBytesToSend = 0; + return(NULL); + } + + // if the current size is good enough just use it + if (pQosClient->uHttpSendBuffSize >= uSize) + { + DirtyMemGroupLeave(); + return(pQosClient->pHttpSendBuff); + } + + // if we are going to allocate something, deallocate what we already have, if we had something + _QosClientAllocHttpSendBuff(pQosClient, 0); //this doesn't move the old data to the new buffer, but that isn't behavior we need right now + + // check to see the amount of space being asked for is sane + if (uSize > QOS_COMMON_MAX_RPC_BODY_SIZE) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: purging %d existing raw results, the required send buffer is too large.\n", pQosClient->rawResultStore.uNumResults)); + _QosClientPurgeCurrentResults(pQosClient); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_SEND_BUFFER_NEEDED_TOO_LARGE, 0); + DirtyMemGroupLeave(); + return(NULL); + } + + // allocate the amount of space that was asked for + if ((pQosClient->pHttpSendBuff = DirtyMemAlloc(uSize, QOS_CLIENT_MEMID, pQosClient->iMemGroup, pQosClient->pMemGroupUserData)) == NULL) + { + //try again with no results + if (pQosClient->rawResultStore.uNumResults > 0) + { + NetPrintf(("qosclient: purging %d existing raw results, error failed to allocate send buffer(%d)\n", pQosClient->rawResultStore.uNumResults, uSize)); + _QosClientPurgeCurrentResults(pQosClient); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_FAILED_ALLOC_SEND_BUFFER, 0); + } + else // if we can't allocate a buffer with no resutls, its fatal + { + NetPrintf(("qosclient: error failed to allocate send buffer(%d)\n", uSize)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_FAILED_ALLOC_SEND_BUFFER_FATAL, 0); + } + DirtyMemGroupLeave(); + return(NULL); + } + pQosClient->uHttpSendBuffSize = uSize; + pQosClient->uHttpBytesToSend = 0; + DirtyMemGroupLeave(); + return(pQosClient->pHttpSendBuff); +} + + +/*F********************************************************************************/ +/*! + \Function _QosClientAutoStart + + \Description + The coordinator may have told us to run a tests again after some delay, + if it wasn't satisfied with the results it generated. + + \Input *pQosClient - pointer to module state + + \Version 08/02/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientAutoStart(QosClientRefT *pQosClient) +{ + // see if the coordinator has told us to start a QOS process ourselves + if (pQosClient->finalResults.uTimeTillRetry != 0) + { + if (NetTickDiff(NetTick(), pQosClient->uFinalResultsRecvTime) > (int32_t)pQosClient->finalResults.uTimeTillRetry) + { + //save off the last profile name to pass back in + char strQosProfile[QOS_COMMON_MAX_RPC_STRING]; + ds_snzprintf(strQosProfile, sizeof(strQosProfile), "%s", pQosClient->rawResultStore.strQosProfile); + QosClientStart(pQosClient, pQosClient->strCoordinatorAddr, pQosClient->uCoordinatorPort, strQosProfile); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientMessageCoordinator + + \Description + Send message to the coordinator letting it know the current state of the + QoS process. + + \Input *pQosClient - pointer to module state + + \Version 03/31/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientMessageCoordinator(QosClientRefT *pQosClient) +{ + char strUrl[QOS_COMMON_MAX_URL_LENGTH]; + + ProtoHttp2Update(pQosClient->pProtoHttp); + + // we don't have a send in progress yet + if (pQosClient->uHttpBytesToSend == 0) + { + // get a send buffer + if (_QosClientAllocHttpSendBuff(pQosClient, QosCommonClientToCoordinatorEstimateEncodedSize(&pQosClient->rawResultStore)) == NULL) + { + //_QosClientAllocHttpSendBuff will have state transitioned if it failed + return; + } + + // add any last minute data + pQosClient->rawResultStore.clientModifiers.uPacketQueueRemaining = _QosClientValidatePacketQueue(pQosClient); // check if packet queue sizes are becoming a problem + pQosClient->rawResultStore.clientModifiers.eOSFirewallType = _QosClientGetOsFirewallType(); // tell the coordinator what the client thinks about the firewall + + // encode the rpc into the send buffer + if ((pQosClient->pHttpSendProgress = QosCommonClientToCoordinatorRequestEncode(&pQosClient->rawResultStore, pQosClient->pHttpSendBuff, pQosClient->uHttpSendBuffSize, &pQosClient->uHttpBytesToSend)) != NULL) + { + int32_t iResult; + const char *pStrHttpMode = "https"; + if (pQosClient->bUseHttps != TRUE) + { + pStrHttpMode = "http"; + } + ds_snzprintf(strUrl, sizeof(strUrl), "%s://%s:%u%s", pStrHttpMode, pQosClient->strCoordinatorAddr, pQosClient->uCoordinatorPort, QOS_COMMON_CLIENT_URL); + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: --> sending http request:\n", strUrl)); + #if DIRTYCODE_LOGGING + if (pQosClient->iSpamValue >= 2) + { + NetPrintMem(pQosClient->pHttpSendBuff, pQosClient->uHttpBytesToSend, "ClientToCoordinatorRequest"); + } + QosCommonClientToCoordinatorPrint(&pQosClient->rawResultStore, pQosClient->iSpamValue); + #endif + + // we shouldn't have an active stream right now, but its possible through some error paths that it hasn't been cleaned up + if (pQosClient->iHttpStreamId != 0) + { + NetPrintf(("qosclient: discarding stream %d.\n", pQosClient->iHttpStreamId)); + ProtoHttp2StreamFree(pQosClient->pProtoHttp, pQosClient->iHttpStreamId); + pQosClient->iHttpStreamId = 0; + } + + // start the send + if ((iResult = ProtoHttp2Request(pQosClient->pProtoHttp, strUrl, NULL, PROTOHTTP2_STREAM_BEGIN, PROTOHTTP_REQUESTTYPE_POST, &pQosClient->iHttpStreamId)) >= 0) + { + pQosClient->pHttpSendProgress += iResult; + pQosClient->uHttpBytesToSend -= iResult; + //if uHttpBytesToSend is 0 we are done, otherwise there is still more data that needs to be sent, so stay in this state to do more ProtoHttpSends + if (pQosClient->uHttpBytesToSend == 0) + { + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_UPDATE_COORDINATOR_COMM, DIRTYAPI_PROTO_HTTP, iResult, 0); + } + } + else + { + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_PROTO_HTTP, iResult, 0); + } + } + else + { + NetPrintf(("qosclient: error, unable to QosCommonClientToCoordinatorRequestEncode.\n")); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_RPC_ENCODE, 0); + } + } + //proceed with sending of stream + else + { + int32_t iResult; + + if ((iResult = ProtoHttp2Send(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, pQosClient->pHttpSendProgress, pQosClient->uHttpBytesToSend)) >= 0) + { + pQosClient->pHttpSendProgress += iResult; + pQosClient->uHttpBytesToSend -= iResult; + //if uHttpBytesToSend is 0 we are done, otherwise there is still more data that needs to be sent, so stay in this state to do more ProtoHttpSends + if (pQosClient->uHttpBytesToSend == 0) + { + ProtoHttp2Send(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, NULL, PROTOHTTP2_STREAM_END); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_UPDATE_COORDINATOR_COMM, DIRTYAPI_PROTO_HTTP, iResult, 0); + } + } + else + { + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_PROTO_HTTP, iResult, 0); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientValidateProbe + + \Description + Validate that a probe packet meets all the requirements to be processed. + + \Input *pQosClient - pointer to module state + \Input *pProbe - pointer probe to be validated + + \Output + QosClientRequestT * - the request the probe belongs to, or NULL if validation fails. + + \Version 06/05/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static QosClientRequestT* _QosClientValidateProbe(QosClientRefT *pQosClient, QosCommonProbePacketT* pProbe) +{ + QosClientRequestT *pRequest = NULL; + + // get request matching id + if ((pRequest = _QosClientGetRequest(pQosClient, pProbe->uClientRequestId)) == NULL) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] could not be found, ignoring.\n", pProbe->uClientRequestId)); + return(NULL); + } + + if (pProbe->uProtocol != QOS_COMMON_PROBE_PROTOCOL_ID) + { + NetPrintf(("qosclient: error probe packet not expected protocol (%d).\n", pProbe->uProtocol)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_PROTOCOL; + return(NULL); + } + + if (!QosCommonIsCompatibleProbeVersion(pProbe->uVersion)) + { + NetPrintf(("qosclient: error probe packet not compatible version (%d).\n", pProbe->uVersion)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_VERSION; + return(NULL); + } + + // make sure the probe isn't smaller than could be possible + if (pProbe->uProbeSizeUp < QOS_COMMON_MIN_PACKET_SIZE) + { + NetPrintf(("qosclient: error uProbeSizeUp=%d, is smaller than QOS_COMMON_MIN_PACKET_SIZE=%d\n", pProbe->uProbeSizeUp, QOS_COMMON_MIN_PACKET_SIZE)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_UP_TOO_SMALL; + return(NULL); + } + + if (pProbe->uProbeSizeDown < QOS_COMMON_MIN_PACKET_SIZE) + { + NetPrintf(("qosclient: error uProbeSizeDown=%d, is smaller than QOS_COMMON_MIN_PACKET_SIZE=%d\n", pProbe->uProbeSizeDown, QOS_COMMON_MIN_PACKET_SIZE)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_DOWN_TOO_SMALL; + return(NULL); + } + + // make sure the probe isn't larger than could be possible + if (pProbe->uProbeSizeUp > QOS_COMMON_MAX_PACKET_SIZE) + { + NetPrintf(("qosclient: error uProbeSizeUp=%d, is larger than QOS_COMMON_MAX_PACKET_SIZE=%d\n", pProbe->uProbeSizeUp, QOS_COMMON_MAX_PACKET_SIZE)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_UP_TOO_LARGE; + return(NULL); + } + + if (pProbe->uProbeSizeDown > QOS_COMMON_MAX_PACKET_SIZE) + { + NetPrintf(("qosclient: error uProbeSizeDown=%d, is larger than QOS_COMMON_MAX_PACKET_SIZE=%d\n", pProbe->uProbeSizeDown, QOS_COMMON_MAX_PACKET_SIZE)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_DOWN_TOO_LARGE; + return(NULL); + } + + // make sure the incoming probe doesn't contain values we don't expect + if (!(pProbe->clientAddressFromService.uFamily == AF_INET || pProbe->clientAddressFromService.uFamily == AF_INET6)) + { + NetPrintf(("qosclient: error unexpected externalAddress.\n")); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_MY_ADDRESS; + return(NULL); + } + + if (pProbe->uServerReceiveTime == 0) + { + NetPrintf(("qosclient: error, expected incoming probe expected uServerReceiveTime(%d), to be set.\n", pProbe->uServerReceiveTime)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_SEVER_TIME; + return(NULL); + } + + // make sure the probe counts are within expected limits + if (pProbe->uProbeCountDown > QOS_COMMON_MAX_PROBE_COUNT) + { + NetPrintf(("qosclient: error, uProbeCountDown(%d) higher than expected.\n", pProbe->uProbeCountDown)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_DOWN_TOO_MANY; + return(NULL); + } + + if (pProbe->uProbeCountUp > QOS_COMMON_MAX_PROBE_COUNT) + { + NetPrintf(("qosclient: error, uProbeCountUp(%d) higher than expected.\n", pProbe->uProbeCountUp)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_UP_TOO_MANY; + return(NULL); + } + + // validate that this request is part of the right service request + if (pRequest->uServiceRequestID != pProbe->uServiceRequestId) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] uServiceRequestId's did not match request(%d), probe(%d).\n", pProbe->uClientRequestId)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_SERVICE_REQUEST_ID; + return(NULL); + } + + // make sure it's not failed or timed out + if (pRequest->eState == REQUEST_STATE_COMPLETE) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] response received but ignored, since the request is already completed.\n", pRequest->uClientRequestID)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_ALREADY_COMPLETE; + return(NULL); + } + + // validate we haven't already received the probe for this slot (we will only know the server receive time if we have received it here on the client) + if (pRequest->rawResults.aProbeResult[pProbe->uProbeCountDown].uServerReceiveTime != 0) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] response received but ignored, because we already received a probe to match the one sent uProbeCount(%d).\n", pRequest->uClientRequestID, pProbe->uProbeCountDown)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_ALREADY_RECEIVED; + return(NULL); + } + + return(pRequest); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientSaveProbeResults + + \Description + Store the results from receiving a probe, when all probes have been received the + request is done. Once all requests are done results will be shared with the + coordinator. + + \Input *pQosClient - module state + \Input *pRequest - request associated to the probe + \Input *pProbe - incoming probe + \Input *pRecvAddr - address the probe came from (QOS server's address) + + \Version 06/05/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientSaveProbeResults(QosClientRefT *pQosClient, QosClientRequestT *pRequest, QosCommonProbePacketT *pProbe, struct sockaddr *pRecvAddr) +{ + //update the result of the request + pRequest->rawResults.aProbeResult[pProbe->uProbeCountDown].uServerReceiveTime = pProbe->uServerReceiveTime; + pRequest->rawResults.aProbeResult[pProbe->uProbeCountDown].uServerSendDelta = pProbe->uServerSendDelta; + + // we didn't actually send a probe to match this receive, so use the send time of the first probe + if (pRequest->test.uProbeCountUp == 1) + { + pRequest->rawResults.aProbeResult[pProbe->uProbeCountDown].uClientSendTime = pRequest->rawResults.aProbeResult[0].uClientSendTime; + pRequest->rawResults.aProbeResult[pProbe->uProbeCountDown].uClientReceiveDelta = NetTickDiff(SockaddrInGetMisc(pRecvAddr), pRequest->rawResults.aProbeResult[0].uClientSendTime); + } + else + { + pRequest->rawResults.aProbeResult[pProbe->uProbeCountDown].uClientReceiveDelta = NetTickDiff(SockaddrInGetMisc(pRecvAddr), pRequest->rawResults.aProbeResult[pProbe->uProbeCountDown].uClientSendTime); + } + + pRequest->rawResults.clientAddressFromServer = pProbe->clientAddressFromService; //this would be the same from every probe for a given requests + + pRequest->rawResults.uProbeCountDown++; + if (pRequest->rawResults.uProbeCountHighWater < pProbe->uProbeCountDown) + { + pRequest->rawResults.uProbeCountHighWater = pProbe->uProbeCountDown; + } + + if (QosCommonIsAddrEqual(&pRequest->rawResults.clientAddressFromServer, &pRequest->probeExternalAddr) == FALSE) + { +#if DIRTYCODE_LOGGING + char addr1Buff[128]; + char addr2Buff[128]; +#endif + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] QOS server does not agree that client external address is %s, it should be %s.\n", pRequest->uClientRequestID, QosCommonAddrToString(&pRequest->rawResults.clientAddressFromServer, addr1Buff, sizeof(addr1Buff)), QosCommonAddrToString(&pRequest->probeExternalAddr, addr2Buff, sizeof(addr2Buff)))); + // the coordinator and the QOS server disagree on what the external address of the client is, this will be ok for many test types + // however the QOS server will not take part in sending multiple packets or large packets to a target that it isn't confident it has the right address + // in case of ip/spoofing packet replay is attempting to generate a DDOS attack + // if we would like to do something like check our down bandwidth we must update our external address to agree with the QOS server who is performing the test + if ((pRequest->test.uProbeCountDown > 1) || (pRequest->test.uProbeSizeDown > QOS_COMMON_MIN_PACKET_SIZE)) + { + // if we want to update to the correct address, lets just make a whole new request so that we don't potentially have older probes on the wire interfering with our new results + // but in case there is some real world internet scenario where the address never match up and we get caught in a loop creating requests, we should only do this max once per request + if (pRequest->uCreationCount == 0) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] creating new request with updated external address(%s->%s).\n", pRequest->uClientRequestID, QosCommonAddrToString(&pRequest->rawResults.clientAddressFromServer, addr1Buff, sizeof(addr1Buff)), QosCommonAddrToString(&pRequest->probeExternalAddr, addr2Buff, sizeof(addr2Buff)))); + + // mostly the new request is a copy of the old request + int32_t iRequestID = _QosClientCreateRequest(pQosClient, &pRequest->site, &pRequest->test, pRequest->uServiceRequestID); + QosClientRequestT *pNewRequest = _QosClientGetRequest(pQosClient, iRequestID); + + //the probes will be sent containing the address the QOS server expects to see, probeExternalAddr selects clientAddressFromServer over clientAddressFromCoordinator + pNewRequest->rawResults.clientAddressFromServer = pRequest->rawResults.clientAddressFromServer; + + // so we don't do this again if it fails again for some reason + pNewRequest->uCreationCount++; + } + + // let the old request complete early, this should prevent us from processing more of the probes from the old request + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_ERROR_EXTERNAL_ADDRESS_MISMATCH, 0); + } + } + + //now that we've processed the probe, determine if we are ready to move on to the next state + if (pRequest->rawResults.uProbeCountDown >= (pRequest->test.uProbeCountDown * pRequest->test.uProbeCountUp)) + { + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_COMPLETE, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_COMPLETE, 0); + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientRecvResponse + + \Description + Process a receive response. + + \Input *pQosClient - module state + \Input *pProbe - received probe + \Input *pRecvAddr - source of packet data + + \Version 08/15/2011 (jbrookes) re-factored from _QosClientRecv() +*/ +/********************************************************************************F*/ +static void _QosClientRecvResponse(QosClientRefT *pQosClient, QosCommonProbePacketT *pProbe, struct sockaddr *pRecvAddr) +{ + QosClientRequestT *pRequest; + +#if QOS_CLIENT_SIMULATE_PACKET_LOSS + static uint32_t uTestCount=0; + uTestCount++; + if ((uTestCount%4) == 0) + { + NetPrintf(("qosclient: request[%02d] pretending probe %d was lost.\n", pProbe->uClientRequestId, pProbe->uProbeCountDown)); + return; + } +#endif + + // make sure the probe is valid and get matching request + if ((pRequest = _QosClientValidateProbe(pQosClient, pProbe)) == NULL) + { + //Validation function would have printed + return; + } + + //save the timing info from the probe and state transition if necessary + _QosClientSaveProbeResults(pQosClient, pRequest, pProbe, pRecvAddr); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientRecv + + \Description + Process QosClient socket data. + + \Input *pSocket - pointer to module socket + \Input *pData - pointer to module state + + \Output + int32_t - zero + + \Version 08/08/2011 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _QosClientRecv(SocketT *pSocket, void *pData) +{ + QosClientRefT *pQosClient = (QosClientRefT *)pData; + struct sockaddr_storage recvAddr; + int32_t iAddrLen, iRecvLen; + uint8_t aRecvBuff[QOS_COMMON_MAX_PACKET_SIZE]; + SockaddrInit((struct sockaddr*)&recvAddr, AF_INET); + + NetCritEnter(&pQosClient->ThreadCrit); + while ((iRecvLen = SocketRecvfrom(pSocket, (char*)aRecvBuff, sizeof(aRecvBuff), 0, (struct sockaddr*)&recvAddr, &iAddrLen)) > 0) + { + if (iRecvLen >= QOS_COMMON_MIN_PACKET_SIZE) + { + QosClientRequestT *pRequest = NULL; + QosCommonProbePacketT probe; + uint16_t uClientRequestID = QosCommonDeserializeClientRequestId(aRecvBuff); //we need to get the client request id from the packet so we can look up the secure key to authenticate the rest of the probe + NetPrintfVerbose((pQosClient->iSpamValue, 2, "qosclient: request[%02d] received probe (iRecvLen=%d).\n", uClientRequestID, iRecvLen)); + + if ((pRequest = _QosClientGetRequest(pQosClient, uClientRequestID)) != NULL) + { + if (QosCommonDeserializeProbePacket(&probe, aRecvBuff, pRequest->site.aSecureKey, NULL) == 0) + { + _QosClientRecvResponse(pQosClient, &probe, (struct sockaddr*)&recvAddr); + } + else + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] hmac test failed.\n", pRequest->uClientRequestID)); + pRequest->iLastProbeValidationError = QOS_CLIENT_REQUEST_STATUS_ERROR_PROBE_HMAC; + } + } + else + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] QosCommonDeserializeClientRequestId() could not be found, ignoring.\n", uClientRequestID)); + } + } + else + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: Discarding probe packet that was too small, %d bytes.\n", iRecvLen)); + } + } + NetCritLeave(&pQosClient->ThreadCrit); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientRecvCB + + \Description + QosClient socket callback. + + \Input *pSocket - pointer to module socket + \Input iFlags - ignored + \Input *pData - pointer to module state + + \Output + int32_t - zero + + \Version 04/11/2008 (cadam) +*/ +/********************************************************************************F*/ +static int32_t _QosClientRecvCB(SocketT *pSocket, int32_t iFlags, void *pData) +{ + QosClientRefT *pQosClient = (QosClientRefT *)pData; + // make sure we own resources + if (NetCritTry(&pQosClient->ThreadCrit)) + { + _QosClientRecv(pSocket, pData); + // release resources + NetCritLeave(&pQosClient->ThreadCrit); + } + else + { + pQosClient->bDeferredRecv = TRUE; + } + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function _QosClientUpdateUdpProbes + + \Description + Receive any incoming probes + + \Input *pQosClient - module state + + \Version 09/19/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientUpdateUdpProbes(QosClientRefT *pQosClient) +{ + /* + check if we need to poll for data. we need to do this if: + 1. this platform does not implement an async receive thread + 2. there's a deferred receive call issued by async receive thread + */ + if (pQosClient->pUdpSocket != NULL) + { + if (pQosClient->bDeferredRecv) + { + pQosClient->bDeferredRecv = FALSE; + _QosClientRecv(pQosClient->pUdpSocket, pQosClient); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientAllocHttpRecvBuff + + \Description + Allocate a receive buffer of the specified size. + + \Input *pQosClient - module state + \Input uSize - the size the receive buffer should be + + \Output + char* - success, pointer to pQosClient->pHttpRecvBuff; fail, NULL + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static uint8_t* _QosClientAllocHttpRecvBuff(QosClientRefT *pQosClient, uint32_t uSize) +{ + uint8_t *pTemp; + DirtyMemGroupEnter(pQosClient->iMemGroup, pQosClient->pMemGroupUserData); + + // if the user is asking for a 0 size buffer they really just want to deallocate it + if (uSize == 0) + { + if (pQosClient->pHttpRecvBuff != NULL) + { + DirtyMemFree(pQosClient->pHttpRecvBuff, QOS_CLIENT_MEMID, pQosClient->iMemGroup, pQosClient->pMemGroupUserData); + pQosClient->pHttpRecvBuff = NULL; + } + DirtyMemGroupLeave(); + pQosClient->uHttpRecvBuffSize = 0; + pQosClient->uCurrentHttpRecvSize = 0; + return(NULL); + } + + // if the current size is good enough just use it + if (pQosClient->uHttpRecvBuffSize >= uSize) + { + DirtyMemGroupLeave(); + return(pQosClient->pHttpRecvBuff); + } + + // check to see the amount of space being asked for is sane + if (uSize > QOS_COMMON_MAX_RPC_BODY_SIZE) + { + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_RECV_BUFFER_NEEDED_TOO_LARGE, 0); + DirtyMemGroupLeave(); + return(NULL); + } + + + // allocate the amount of space that was asked for to a temporary variable + if ((pTemp = DirtyMemAlloc(uSize, QOS_CLIENT_MEMID, pQosClient->iMemGroup, pQosClient->pMemGroupUserData)) == NULL) + { + NetPrintf(("qosclient: error failed to allocate receive buffer(%d)\n", uSize)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_FAILED_ALLOC_RECV_BUFFER, 0); + DirtyMemGroupLeave(); + return(NULL); + } + + // move old data to the new buffer, and get rid of the old buffer (if there was one) + if (pQosClient->pHttpRecvBuff) + { + ds_memcpy(pTemp, pQosClient->pHttpRecvBuff, pQosClient->uHttpRecvBuffSize); + _QosClientAllocHttpRecvBuff(pQosClient, 0); + } + + pQosClient->pHttpRecvBuff = pTemp; + pQosClient->uHttpRecvBuffSize = uSize; + DirtyMemGroupLeave(); + return(pQosClient->pHttpRecvBuff); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientUpdateHttp + + \Description + Receive any incoming http responses + + \Input *pQosClient - module state + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientUpdateHttp(QosClientRefT *pQosClient) +{ + int32_t iResult; + + + //service http module since we are expecting bytes to come in + ProtoHttp2Update(pQosClient->pProtoHttp); + + //update the clients local address, as new info becomes available + _QosClientUpdateLocalAddress(pQosClient); + + if (_QosClientAllocHttpRecvBuff(pQosClient, QOS_COMMON_DEFAULT_RPC_BODY_SIZE) == NULL) + { + // _QosClientAllocHttpRecvBuff will state transition on an error + return; + } + + if ((iResult = ProtoHttp2RecvAll(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, pQosClient->pHttpRecvBuff, pQosClient->uHttpRecvBuffSize)) >= 0) + { + int32_t iHttpStatusCode = ProtoHttp2Status(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, 'code', NULL, 0); + if (iHttpStatusCode == PROTOHTTP_RESPONSE_SUCCESSFUL) + { + pQosClient->uCurrentHttpRecvSize = iResult; + // parse the http response + if (_QosClientProcessCoordinatorResponse(pQosClient) >= 0) + { + //we state transition inside _QosClientProcessCoordinatorResponse + ProtoHttp2StreamFree(pQosClient->pProtoHttp, pQosClient->iHttpStreamId); + pQosClient->iHttpStreamId = 0; + pQosClient->uErrorMessageCount = 0; + } + else + { + pQosClient->uErrorMessageCount++; + if (pQosClient->uErrorMessageCount > 1) + { + NetPrintf(("qosclient: error we have had %d consecutive errors from the coordinator, going fatal.\n", pQosClient->uErrorMessageCount)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_UNRECOVERED_ERROR, 0); + } + } + } + else + { + NetPrintf(("qosclient: error http status %d\n", iHttpStatusCode)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_PROTO_HTTP, iHttpStatusCode, 0); + } + } + else if (iResult == PROTOHTTP2_RECVWAIT) + { + NetPrintfVerbose((pQosClient->iSpamValue, 1, "qosclient: [%p] Waiting for complete http response to arrive.\n", pQosClient)); + } + else if (iResult == PROTOHTTP2_RECVBUFF) + { + uint32_t uNewBuffSize = 2 * pQosClient->uHttpRecvBuffSize; + NetPrintfVerbose((pQosClient->iSpamValue, 1, "qosclient: [%p] Insufficient buffer space to receive, increasing space from %u to %u.\n", pQosClient, pQosClient->uHttpRecvBuffSize, uNewBuffSize)); + _QosClientAllocHttpRecvBuff(pQosClient, uNewBuffSize); + } + else if (iResult < 0) + { + int32_t hResult = ProtoHttp2Status(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, 'hres', NULL, 0); + if (hResult >= 0) //in this case there was no socket or ssl error + { + NetPrintf(("qosclient: error ProtoHttpRecvAll failed iResult=%d\n", iResult)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_PROTO_HTTP, iResult, 0); + } + else + { + int16_t iCode = 0; + + //we just want to get the error code from the hResult so we can include it in the hResult generated in _QosClientModuleStateTransition + DirtyErrDecodeHResult(hResult, NULL, &iCode, NULL, NULL); + + NetPrintf(("qosclient: error ProtoHttpRecvAll failed hResult=%p\n", hResult)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_PROTO_HTTP, iCode, 0); + } + } +} + + +/*F********************************************************************************/ +/*! + \Function _QosClientRequestReceiveUpdate + + \Description + Check to see if we should send a probe or we need to re-send any probes. + If so transition back into the send state. + + \Input *pQosClient - module state + \Input *pRequest - request being processes + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientRequestReceiveUpdate(QosClientRefT *pQosClient, QosClientRequestT *pRequest) +{ + // don't bother sending anything more if we already have sent all we can, unless the responses are already on the wire this will likely timeout + if (pRequest->rawResults.uProbeCountUp < QOS_COMMON_MAX_PROBE_COUNT) + { + uint32_t currentTime = NetTick(); + // there are two reasons why we might transition back to REQUEST_STATE_SEND + // 1 the common case is that enough time has lapsed in between probes + if ((pRequest->rawResults.uProbeCountUp < pRequest->uProbesToSend) && //we haven't sent all probes yet + (NetTickDiff(currentTime, pRequest->uWhenLastSent) > (int32_t)pRequest->test.uMinTimeBetwenProbes) //too much time passed + ) + { + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_SEND, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_SEND_NEXT_PROBE, 1); + } + // 2 too much time may have laps before receiving responses in which case we want to send more probes then initially planned (we likely lost probes on the wire) + if ((pRequest->rawResults.uProbeCountDown < (pRequest->test.uProbeCountUp * pRequest->test.uProbeCountDown)) && //we haven't received the number of responses we want, and + (NetTickDiff(currentTime, pRequest->uWhenLastSent) > (int32_t)pRequest->test.uTimeTillResend) //too much time passed + ) + { + int32_t iMissingProbes = ((pRequest->test.uProbeCountUp * pRequest->test.uProbeCountDown) - pRequest->rawResults.uProbeCountDown); + if (iMissingProbes > pRequest->test.uAcceptableLostProbeCount) + { + int32_t iAdditionalProbes = (iMissingProbes / pRequest->test.uProbeCountDown); // we expect uProbeCountDown per uProbesToSend + if (iAdditionalProbes <= 0) //there needs to be at least one missing probe + { + iAdditionalProbes = 1; + } + pRequest->uProbesToSend += (iAdditionalProbes + pRequest->test.uResendExtraProbeCount); //the test may say to send even more probes if missing probes are found + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_SEND, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_SEND_LOST_PROBES, 0); + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] re-sending %d new probes.\n", pRequest->uClientRequestID, iAdditionalProbes)); + } + else + { + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_COMPLETE, DIRTYAPI_QOS, QOS_CLIENT_REQUEST_STATUS_COMPLETE_ACCEPTABLE, 0); + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientRequestComplete + + \Description + Finish the request; call back the user, and delete the request. + + \Input *pQosClient - module ref + \Input *pRequest - request being completed + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientRequestComplete(QosClientRefT *pQosClient, QosClientRequestT *pRequest) +{ + pRequest->uWhenCompleted = NetTick(); + pQosClient->rawResultStore.aResults[pQosClient->rawResultStore.uNumResults] = pRequest->rawResults; + pQosClient->rawResultStore.uNumResults++; + + _QosClientDestroyRequest(pQosClient, pRequest->uClientRequestID); + + // see if there are currently no requests left, if so tell the coordinator about what happened + if (pQosClient->pRequestQueue == NULL) + { + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_INIT_COORDINATOR_COMM, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_RAW_RESULTS_READY, 0); + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientCheckRequestForTimeout + + \Description + Check to see if the request should be completed due to timeout, if so transition it to timeout. + + \Input *pQosClient - module ref + \Input *pRequest - request being checked + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientCheckRequestForTimeout(QosClientRefT *pQosClient, QosClientRequestT *pRequest) +{ + //check to see if the request has timed out + if (NetTickDiff(NetTick(), pRequest->uWhenStarted) > (int32_t)pRequest->test.uTimeout) + { + int16_t iErrorCode = QOS_CLIENT_REQUEST_STATUS_ERROR_TIMEOUT; + if (pRequest->rawResults.uProbeCountDown > 0) + { + iErrorCode = QOS_CLIENT_REQUEST_STATUS_ERROR_TIMEOUT_PARTIAL; + } + if (pRequest->iLastProbeValidationError != 0) + { + iErrorCode = pRequest->iLastProbeValidationError; + } + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: request[%02d] timed out (%d ms), possible cause %d.\n", pRequest->uClientRequestID, NetTickDiff(NetTick(), pRequest->uWhenStarted), iErrorCode)); + _QosClientRequestStateTransition(pQosClient, pRequest, REQUEST_STATE_ERROR, DIRTYAPI_QOS, iErrorCode, 0); + } +} + +/*F********************************************************************************/ +/*! + \Function _QosClientReport + + \Description + Display a summary report and pass the info on to the client + + \Input *pQosClient - module ref + + \Version 09/15/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientReport(QosClientRefT *pQosClient) +{ + //save the hResult that was generated while doing this request to pass to the user + pQosClient->finalResults.hResult = pQosClient->rawResultStore.clientModifiers.hResult; + #if DIRTYCODE_LOGGING + uint32_t uSiteIndex; + char addrBuff[128]; + + uint32_t uProcessTime = NetTickDiff(NetTick(), pQosClient->uQosProcessStartTime); + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: CLIENT(hResult:0x%08x, external addr:%s, fw:%s, time:%ums)\n", + pQosClient->finalResults.hResult, + QosCommonAddrToString(&pQosClient->finalResults.clientExternalAddress, addrBuff, sizeof(addrBuff)), + _strQosCommonFirewallType[pQosClient->finalResults.eFirewallType], + uProcessTime)); + + for (uSiteIndex = 0; uSiteIndex < pQosClient->finalResults.uNumResults; uSiteIndex++) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: SITE(%d, %8s) RESULT(hResult:0x%08x, upbps:%9u, downbps:%9u, rtt:%11u)\n", + uSiteIndex, + pQosClient->finalResults.aTestResults[uSiteIndex].strSiteName, + pQosClient->finalResults.aTestResults[uSiteIndex].hResult, + pQosClient->finalResults.aTestResults[uSiteIndex].uUpbps, + pQosClient->finalResults.aTestResults[uSiteIndex].uDownbps, + pQosClient->finalResults.aTestResults[uSiteIndex].uMinRTT + )); + } + #endif + + pQosClient->uQosProcessStartTime = 0; + if (pQosClient->pCallback) + { + pQosClient->pCallback(pQosClient, &pQosClient->finalResults, pQosClient->pUserData); + } + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_IDLE, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_REPORT_COMPLETE, 0); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientUpdateRequest + + \Description + Process the request for any given state. + + \Input *pQosClient - module ref + \Input *pRequest - request being updated + + \Output + int8_t - TRUE if the request has been destroyed. + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +static uint8_t _QosClientUpdateRequest(QosClientRefT *pQosClient, QosClientRequestT *pRequest) +{ + if (pRequest->eState == REQUEST_STATE_INIT) + { + _QosClientRequestInit(pQosClient, pRequest); + } + else if (pRequest->eState == REQUEST_STATE_INIT_SYNC) + { + _QosClientRequestWaitForInit(pQosClient, pRequest); + } + else if (pRequest->eState == REQUEST_STATE_SEND) + { + _QosClientRequestSendProbes(pQosClient, pRequest); + } + else if (pRequest->eState == REQUEST_STATE_RECEIVE) + { + _QosClientRequestReceiveUpdate(pQosClient, pRequest); + } + else if (pRequest->eState == REQUEST_STATE_COMPLETE) + { + _QosClientRequestComplete(pQosClient, pRequest); + return(TRUE); + } + else + { + NetPrintf(("qosclient: request[%02d] error request in unexpected state.\n", pRequest->uClientRequestID)); + } + + _QosClientCheckRequestForTimeout(pQosClient, pRequest); + + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _QosClientCheckUpdateTimings + + \Description + See if we are being pumped frequently enough, if we aren't it can adversely + effect the timeouts. + + \Input *pQosClient - module ref + + \Version 11/28/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosClientCheckUpdateTimings(QosClientRefT *pQosClient) +{ + uint32_t uCurrentTime = NetTick(); + + //check to see if we are being updated at a reasonable rate + uint32_t uUpateElapsedTime = NetTickDiff(uCurrentTime, pQosClient->uLastUpdateTime); + if (uUpateElapsedTime >= pQosClient->uStallWindow) + { + if ((pQosClient->eModuleState == MODULE_STATE_INIT_COORDINATOR_COMM) || (pQosClient->eModuleState == MODULE_STATE_UPDATE_COORDINATOR_COMM)) + { + pQosClient->rawResultStore.clientModifiers.uStallCountCoordinator++; + pQosClient->rawResultStore.clientModifiers.uStallDurationCoordinator += uUpateElapsedTime; + + } + else if (pQosClient->eModuleState == MODULE_STATE_PROBE) + { + pQosClient->rawResultStore.clientModifiers.uStallCountProbe++; + pQosClient->rawResultStore.clientModifiers.uStallDurationProbe += uUpateElapsedTime; + } + NetPrintfVerbose((pQosClient->iSpamValue, 1, "qosclient: an update stall of %dms was detected.\n", uUpateElapsedTime)); + } + pQosClient->uLastUpdateTime = uCurrentTime; + + //check for overall timeout + if (pQosClient->uQosProcessStartTime != 0) + { + if (NetTickDiff(uCurrentTime, pQosClient->uQosProcessStartTime) > (int32_t)pQosClient->uMaxQosProcessTime) + { + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: the qos process has timed out after %ums.\n", pQosClient->uMaxQosProcessTime)); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_FATAL, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_TIMEOUT, 0); + } + } +} + + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function QosClientUpdate + + \Description + Allow the module to do processing. + + \Input *pQosClient - point to module state + + \Version 06/21/2017 (cvienneau) +*/ +/********************************************************************************F*/ +void QosClientUpdate(QosClientRefT *pQosClient) +{ + QosClientRequestT *pRequest; + + if (!NetCritTry(&pQosClient->ThreadCrit)) + { + return; + } + + _QosClientCheckUpdateTimings(pQosClient); + + if (pQosClient->eModuleState == MODULE_STATE_IDLE) + { + _QosClientAutoStart(pQosClient); + } + else if (pQosClient->eModuleState == MODULE_STATE_INIT_COORDINATOR_COMM) + { + //tell the coordinator where we are at, this could be just who we are, or may include the results we have so far + _QosClientMessageCoordinator(pQosClient); + } + else if (pQosClient->eModuleState == MODULE_STATE_UPDATE_COORDINATOR_COMM) + { + // if there is any pending http action process it now + _QosClientUpdateHttp(pQosClient); + } + else if (pQosClient->eModuleState == MODULE_STATE_PROBE) + { + // receive data for any incoming probes + // note that probes may also be received by the socket receive thread + _QosClientUpdateUdpProbes(pQosClient); + + //update the state of all the request + for (pRequest = pQosClient->pRequestQueue; pRequest != NULL; pRequest = pRequest->pNext) + { + // update based off recent communication udp or http + if(_QosClientUpdateRequest(pQosClient, pRequest) == TRUE) + { + break;//on TRUE a request has been destroyed the list is no longer valid + } + } + } + else if (pQosClient->eModuleState == MODULE_STATE_REPORT) + { + _QosClientReport(pQosClient); + } + else + { + NetPrintf(("qosclient: error unknown eModuleState state.\n")); + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_ERROR, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_ERROR_UNKOWN_STATE, 0); + } + + NetCritLeave(&pQosClient->ThreadCrit); +} + +/*F********************************************************************************/ +/*! + \Function QosClientCreate + + \Description + Create the QosClient module. + + \Input *pCallback - callback function to use when a status change is detected + \Input *pUserData - use data to be set for the callback + \Input uListenPort - set the port incoming probes will go to, pass 0 to use defaults + + \Output + QosClientRefT * - state pointer on success, NULL on failure + + \Version 04/07/2008 (cadam) +*/ +/********************************************************************************F*/ +QosClientRefT *QosClientCreate(QosClientCallbackT *pCallback, void *pUserData, uint16_t uListenPort) +{ + QosClientRefT *pQosClient; + void *pMemGroupUserData; + int32_t iMemGroup; + + // Query current memory group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + DirtyMemGroupEnter(iMemGroup, pMemGroupUserData); + + // allocate and init module state + if ((pQosClient = (QosClientRefT*)DirtyMemAlloc(sizeof(*pQosClient), QOS_CLIENT_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + DirtyMemGroupLeave(); + NetPrintf(("qosclient: could not allocate module state\n")); + return(NULL); + } + + //init member variables + ds_memclr(pQosClient, sizeof(*pQosClient)); + pQosClient->iMemGroup = iMemGroup; + pQosClient->pMemGroupUserData = pMemGroupUserData; + pQosClient->iSpamValue = 1; + pQosClient->uUdpRequestedListenPort = uListenPort; + pQosClient->iHttpSoLinger = -1; + pQosClient->bUseHttps = QOS_CLIENT_DEFAULT_USE_HTTPS; + pQosClient->pCallback = pCallback; + pQosClient->pUserData = pUserData; + pQosClient->uUdpReceiveBuffSize = QOS_CLIENT_DEFAULT_RECEIVE_BUFF_SIZE; + pQosClient->uUdpSendBuffSize = QOS_CLIENT_DEFAULT_SEND_BUFF_SIZE; + pQosClient->uUdpPacketQueueSize = QOS_CLIENT_DEFAULT_PACKET_QUEUE_SIZE; + pQosClient->uStallWindow = QOS_CLIENT_DEFAULT_STALL_WINDOW; + pQosClient->uMaxQosProcessTime = QOS_CLIENT_DEFAULT_MAX_QOS_PROCESS_TIME; + pQosClient->uLastUpdateTime = NetTick(); + //transition to starting state + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_IDLE, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_INIT, 0); + + //setup protoHttp for communication with the coordinator + if ((pQosClient->pProtoHttp = ProtoHttp2Create(4096)) == NULL) //we shouldn't need much space + { + DirtyMemGroupLeave(); + NetPrintf(("qosclient: could not create the protohttp module.\n")); + QosClientDestroy(pQosClient); + return(NULL); + } + DirtyMemGroupLeave(); + ProtoHttp2Control(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, 'apnd', 0, 0, NULL); + ProtoHttp2Control(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, 'apnd', 0, 0, (void *)"te: trailers\r\ncontent-type: application/grpc\r\n"); + + // need to sync access to data + NetCritInit(&pQosClient->ThreadCrit, "qosclient"); + + // return module state to caller + return(pQosClient); +} + +/*F********************************************************************************/ +/*! + \Function QosClientStart + + \Description + Request that the QOS coordinator provide setting to the client, the client will + then perform tests based off those settings. Results will be provided to game code + via the callback set in QosClientCreate. + + \Input *pQosClient - module ref + \Input *pStrCoordinatorAddr - url of the QOS coordinator + \Input uPort - port of the QOS coordinator + \Input *pStrQosProfile - what profile should the QOS coordinator use for settings, likely a service name like "fifa-pc-20147" + + \Version 11/07/2014 (cvienneau) +*/ +/********************************************************************************F*/ +void QosClientStart(QosClientRefT *pQosClient, const char *pStrCoordinatorAddr, uint16_t uPort, const char *pStrQosProfile) +{ + NetCritEnter(&pQosClient->ThreadCrit); + + //only start if we haven't already got something on the go + if (pQosClient->eModuleState == MODULE_STATE_IDLE) + { + //clear out any existing state + ds_memclr(&pQosClient->finalResults, sizeof(pQosClient->finalResults)); + ds_memclr(&pQosClient->rawResultStore, sizeof(pQosClient->rawResultStore)); + pQosClient->uHttpBytesToSend = 0; + pQosClient->uErrorMessageCount = 0; + pQosClient->uQosProcessStartTime = NetTick(); + + //set info about the client + ds_snzprintf(pQosClient->rawResultStore.strQosProfile, sizeof(pQosClient->rawResultStore.strQosProfile), pStrQosProfile); + pQosClient->rawResultStore.uProbeVersion = QOS_COMMON_PROBE_VERSION; + pQosClient->rawResultStore.clientModifiers.clientAddressInternal = pQosClient->localUdpAddrInfo; + ds_snzprintf(pQosClient->rawResultStore.clientModifiers.strPlatform, sizeof(pQosClient->rawResultStore.clientModifiers.strPlatform), DIRTYCODE_PLATNAME_SHORT); + + //set info about the coordinator + ds_snzprintf(pQosClient->strCoordinatorAddr, sizeof(pQosClient->strCoordinatorAddr), pStrCoordinatorAddr); + pQosClient->uCoordinatorPort = uPort; + + //make use of any control selectors that were set before we start + ProtoHttp2Control(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, 'spam', pQosClient->iSpamValue + 1, 0, NULL); + ProtoHttp2Control(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, 'ncrt', pQosClient->bIgnoreSSLErrors, 0, NULL); + + //start communication with the coordinator + _QosClientModuleStateTransition(pQosClient, MODULE_STATE_INIT_COORDINATOR_COMM, DIRTYAPI_QOS, QOS_CLIENT_MODULE_STATUS_PROCESS_STARTED, 0); + } + else + { + NetPrintf(("qosclient: QosClientStart() skipped because the QosClient is already busy.\n")); + } + NetCritLeave(&pQosClient->ThreadCrit); +} + +/*F********************************************************************************/ +/*! + \Function QosClientControl + + \Description + QosClient control function. Different selectors control different behaviors. + + \Input *pQosClient - module state + \Input iControl - control selector + \Input iValue - selector specific data + \Input pValue - selector specific data + + \Output + int32_t - control specific + + \Notes + iControl can be one of the following: + + \verbatim + 'cbfp' - set callback function pointer - pValue=pCallback + 'lprt' - set the port to use for the QoS listen port (must be set before calling listen/request), same as the value passed to QosClientCreate + 'maxt' - set the amount of ms before we give up on the current qos process + 'ncrt' - passed to ProtoHttpControl, if TRUE will proceed even if ssl cert errors are detected (defaults FALSE) + 'pque' - passed to SocketControl 'pque' if not 0, otherwise the number of packets will be passed to SocketControl 'pque' + 'rbuf' - passed to SocketControl 'rbuf' + 'sbuf' - passed to SocketControl 'sbuf' + 'soli' - passed down to the socket as a SO_LINGER option if its >= 0, a 0 value will abort the socket on disconnection with no TIME_WAIT state + 'spam' - set the verbosity of the module, default 1 (0 errors, 1 debug info, 2 extended debug info, 3 per probe info) + 'stwi' - set the amount of ms before a stall is counted + 'ussl' - if TRUE use https else http for setup communication (defaults TRUE) + + \endverbatim + + \Version 04/07/2008 (cadam) +*/ +/********************************************************************************F*/ +int32_t QosClientControl(QosClientRefT *pQosClient, int32_t iControl, int32_t iValue, void *pValue) +{ + int32_t iRet = 0; + NetPrintfVerbose((pQosClient->iSpamValue, 0, "qosclient: QosClientControl('%C'), (%d)\n", iControl, iValue)); + + NetCritEnter(&pQosClient->ThreadCrit); + + if (iControl == 'cbfp') + { + // set callback function pointer + pQosClient->pCallback = (QosClientCallbackT *)pValue; + } + else if (iControl == 'lprt') + { + pQosClient->uUdpRequestedListenPort = iValue; + } + else if (iControl == 'maxt') + { + pQosClient->uMaxQosProcessTime = iValue; + } + else if (iControl == 'ncrt') + { + pQosClient->bIgnoreSSLErrors = iValue; + } + else if (iControl == 'pque') + { + pQosClient->uUdpPacketQueueSize = iValue; + } + else if (iControl == 'rbuf') + { + pQosClient->uUdpReceiveBuffSize = iValue; + } + else if (iControl == 'sbuf') + { + pQosClient->uUdpSendBuffSize = iValue; + } + else if (iControl == 'soli') + { + pQosClient->iHttpSoLinger = iValue; + } + else if (iControl == 'spam') + { + pQosClient->iSpamValue = iValue; + } + else if (iControl == 'stwi') + { + pQosClient->uStallWindow = iValue; + } + else if (iControl == 'ussl') + { + pQosClient->bUseHttps = iValue; + } + else + { + iRet = -1; + } + + NetCritLeave(&pQosClient->ThreadCrit); + return(iRet); +} + +/*F********************************************************************************/ +/*! + \Function QosClientStatus + + \Description + Return quality of service information. + + \Input *pQosClient - module state + \Input iSelect - output selector + \Input iData - selector specific + \Input *pBuf - [out] pointer to output buffer + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'clpt' - get the current listen port of the QoS socket + 'hres' - get the current hResult value for the module + 'lprt' - get the port to use for the QoS listen port + 'maxt' - get the maximum time we will allow the qos process to run + 'ncrt' - if TRUE we will ignore ssl errors for setup communication + 'pque' - get the value passed to SocketControl 'pque' + 'rbuf' - get the value passed to SocketControl 'rbuf' + 'rpro' - get the processed results, the same results that are returned in the callback, pass in a QosCommonProcessedResultsT + 'rraw' - get the raw results, the results that are analyzed by the coordinator, pass in a QosCommonClientToCoordinatorRequestT + 'rrpc' - get the result rpc data, the processed results from the coordinator, only valid during the completion callback. returns length of data, and populates pBuf if a buffer of enough space is provided. + 'sbuf' - get the value passed to SocketControl 'sbuf' + 'soli' - get the value passed to ProtoHttp 'soli' + 'spam' - get the verbosity of the module + 'stwi' - get the duration of time to pass, to consider a updating to have been stalled + 'ussl' - if TRUE use https else http for setup communication + + \endverbatim + + \Version 04/07/2008 (cadam) +*/ +/********************************************************************************F*/ +int32_t QosClientStatus(QosClientRefT *pQosClient, int32_t iSelect, int32_t iData, void *pBuf, int32_t iBufSize) +{ + int32_t iRet = 0; + NetPrintfVerbose((pQosClient->iSpamValue, 1, "qosclient: QosClientStatus('%C'), (%d)\n", iSelect, iData)); + + NetCritEnter(&pQosClient->ThreadCrit); + + if (iSelect == 'clpt') + { + iRet = pQosClient->localUdpAddrInfo.uPort; + } + if (iSelect == 'hres') + { + iRet = pQosClient->rawResultStore.clientModifiers.hResult; + } + else if (iSelect == 'lprt') + { + iRet = pQosClient->uUdpRequestedListenPort; + } + else if (iSelect == 'maxt') + { + iRet = pQosClient->uMaxQosProcessTime; + } + else if (iSelect == 'ncrt') + { + iRet = pQosClient->bIgnoreSSLErrors; + } + else if (iSelect == 'pque') + { + iRet = pQosClient->uUdpPacketQueueSize; + } + else if (iSelect == 'rbuf') + { + iRet = pQosClient->uUdpReceiveBuffSize; + } + else if ((iSelect == 'rpro') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(QosCommonProcessedResultsT))) + { + ds_memcpy(pBuf, &pQosClient->finalResults, sizeof(QosCommonProcessedResultsT)); + iRet = 1; + } + else if ((iSelect == 'rraw') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(QosCommonClientToCoordinatorRequestT))) + { + ds_memcpy(pBuf, &pQosClient->rawResultStore, sizeof(QosCommonClientToCoordinatorRequestT)); + iRet = 1; + } + else if (iSelect == 'rrpc') + { + iRet = 0; + if (pQosClient->pHttpRecvBuff) + { + iRet = (int32_t)pQosClient->uCurrentHttpRecvSize; + if ((pBuf != NULL) && (iBufSize >= iRet)) + { + ds_memcpy(pBuf, pQosClient->pHttpRecvBuff, iRet); + } + } + } + else if (iSelect == 'sbuf') + { + iRet = pQosClient->uUdpSendBuffSize; + } + else if (iSelect == 'soli') + { + iRet = pQosClient->iHttpSoLinger; + } + else if (iSelect == 'spam') + { + iRet = pQosClient->iSpamValue; + } + else if (iSelect == 'stwi') + { + iRet = pQosClient->uStallWindow; + } + else if (iSelect == 'ussl') + { + iRet = pQosClient->bUseHttps; + } + else + { + iRet = -1; + } + + NetCritLeave(&pQosClient->ThreadCrit); + return(iRet); +} + +/*F********************************************************************************/ +/*! + \Function QosClientDestroy + + \Description + Destroy the QosClient module. + + \Input *pQosClient - module state + + \Version 04/07/2008 (cadam) +*/ +/********************************************************************************F*/ +void QosClientDestroy(QosClientRefT *pQosClient) +{ + NetCritEnter(&pQosClient->ThreadCrit); + + if (pQosClient->pUdpSocket != NULL) + { + SocketClose(pQosClient->pUdpSocket); + pQosClient->pUdpSocket = NULL; + } + + if (pQosClient->pProtoHttp != NULL) + { + // close the socket asap when destroyed (necessary for stress testing to not leak resources too long) + if (pQosClient->iHttpSoLinger >= 0) + { + ProtoHttp2Control(pQosClient->pProtoHttp, pQosClient->iHttpStreamId, 'soli', pQosClient->iHttpSoLinger, 0, NULL); + } + ProtoHttp2Destroy(pQosClient->pProtoHttp); + pQosClient->pProtoHttp = NULL; + + _QosClientAllocHttpRecvBuff(pQosClient, 0); + _QosClientAllocHttpSendBuff(pQosClient, 0); + } + + while (pQosClient->pRequestQueue != NULL) + { + _QosClientDestroyRequest(pQosClient, pQosClient->pRequestQueue->uClientRequestID); + } + + NetCritLeave(&pQosClient->ThreadCrit); + NetCritKill(&pQosClient->ThreadCrit); + + // release module memory + DirtyMemFree(pQosClient, QOS_CLIENT_MEMID, pQosClient->iMemGroup, pQosClient->pMemGroupUserData); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/misc/qoscommon.c b/r5dev/thirdparty/dirtysdk/source/misc/qoscommon.c new file mode 100644 index 00000000..aaa94753 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/misc/qoscommon.c @@ -0,0 +1,1335 @@ +/*H********************************************************************************/ +/*! + \File qoscommon.c + + \Description + This code implements shared items between client and server of the QOS system. + + \Copyright + Copyright (c) 2016 Electronic Arts Inc. + +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/misc/qoscommon.h" +#include "DirtySDK/util/binary7.h" +#include "DirtySDK/crypt/crypthash.h" +#include "DirtySDK/crypt/crypthmac.h" + +#include "DirtySDK/util/protobufcommon.h" +#include "DirtySDK/util/protobufwrite.h" +#include "DirtySDK/util/protobufread.h" + +/*** Defines **********************************************************************/ + +//!< Limits put on values when decoding rpcs + +#define QOS_COMMON_MAX_INIT_SYNC_TIME (5000) //!< max time to wait for all the tests to start at the same time, after this time the tests will continue without synchronizing +#define QOS_COMMON_MAX_TIME_BETWEEN_PROBES (1000) //!< time between each probe send for a given test +#define QOS_COMMON_MIN_PROBE_COUNT (1) //!< the min amount of probes used for a given test, note QOS_COMMON_MAX_PROBE_COUNT is defined in qoscommon.h +#define QOS_COMMON_MAX_RESEND_EXTRA_PROBES (QOS_COMMON_MAX_PROBE_COUNT) //!< if we think we lost probes, should we send some extras to reduce the chances of losing some of those too +#define QOS_COMMON_MIN_TIMEOUT (7000) //!< min qosclient request timeout period, the test is considered failed if this much time passed +#define QOS_COMMON_MAX_TIMEOUT (20000) //!< max qosclient request timeout period, the test is considered failed if this much time passed +#define QOS_COMMON_MIN_RESEND_TIME (200) //!< qosclient time till we decide probes have been lost, so send more probes then initially planned +#define QOS_COMMON_MAX_RESEND_TIME (QOS_COMMON_MAX_TIMEOUT) //!< qosclient time till we decide probes have been lost, so send more probes then initially planned +#define QOS_COMMON_MAX_ACCEPTABLE_LOST_PROBES (QOS_COMMON_MAX_PROBE_COUNT) //!< don't bother resend new probes if we've only lost x number of probes +#define QOS_COMMON_RPC_HEADER_SIZE (1) //! one byte is needed to make a grpc header on top of what is done in protobuff +#define QOS_COMMON_ENABLE_HMAC (1) //! be sure that hmac is enabled except for special debug cases + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ +// Note that all of the below enums must be kept in sync with the proto files +// that belong to the coordinator. + +//message ClientRequest +enum +{ + CLIENT_TO_COORDINATOR_REQUEST_ARESULTS = 1, + CLIENT_TO_COORDINATOR_REQUEST_PROFILE, + CLIENT_TO_COORDINATOR_REQUEST_SERVICE_ID, + CLIENT_TO_COORDINATOR_REQUEST_PROBE_VERSION, + CLIENT_TO_COORDINATOR_REQUEST_MODIFIERS +}eClientToCoordinatorRequestFields; + +//message ClientModifierFields +enum +{ + CLIENT_MODIFIER_INTERNAL_ADDRESS = 1, + CLIENT_MODIFIER_PLATFORM, + CLIENT_MODIFIER_OS_FIREWALL, + CLIENT_MODIFIER_PACKET_QUEUE_REMAINING, + CLIENT_MODIFIER_STALL_COUNT_COMM, + CLIENT_MODIFIER_STALL_DURATION_COMM, + CLIENT_MODIFIER_STALL_COUNT_PROBE, + CLIENT_MODIFIER_STALL_DURATION_PROBE, + CLIENT_MODIFIER_HRESULT +}eQosCommonClientModifiersFields; + +//message ClientRawResult +enum +{ + RAW_RESULTS_ARESULT = 1, + RAW_RESULTS_CLIENT_ADDRESS_FROM_SERVER, + RAW_RESULTS_SITE_NAME, + RAW_RESULTS_TEST_NAME, + RAW_RESULTS_HRESULT +}eQosCommonRawResultsFields; + +//message ProbeResult +enum +{ + PROBE_RESULT_CLIENT_SEND_TIME = 1, + PROBE_RESULT_SERVER_RECV_TIME, + PROBE_RESULT_SERVER_SEND_DELTA, + PROBE_RESULT_CLIENT_RECV_DELTA +}eQosCommonProbeResultFields; + +//message ClientResponse +enum +{ + COORDINATOR_TO_CLIENT_RESPONSE_STATUS = 1, + COORDINATOR_TO_CLIENT_RESPONSE_SERVICE_ID, + COORDINATOR_TO_CLIENT_RESPONSE_CLIENT_ADDR_FROM_COORDINATOR, + COORDINATOR_TO_CLIENT_RESPONSE_FIREWALL_TYPE, + COORDINATOR_TO_CLIENT_RESPONSE_TIME_TILL_RETRY, + COORDINATOR_TO_CLIENT_RESPONSE_CLIENT_EXTERNAL_ADDRESS, + COORDINATOR_TO_CLIENT_RESPONSE_APINGSITES, + COORDINATOR_TO_CLIENT_RESPONSE_ACONTROL_CONFIGS, + COORDINATOR_TO_CLIENT_RESPONSE_ATESTS, + COORDINATOR_TO_CLIENT_RESPONSE_ASITE_RESULTS +}eCoordinatorToClientResponseFields; + +//message PingSite +enum +{ + SITE_NAME = 1, + SITE_ADDR, + SITE_PORT, + SITE_KEY, + SITE_PROBE_VERSION +}eQosCommonSiteFields; + +//message ControlConfig +enum +{ + CONTROL_CONFIG_NAME = 1, + CONTROL_CONFIG_I_VALUE, + CONTROL_CONFIG_STR_VALUE +}eQosCommonControlConfigFields; + +//message QosTest +enum +{ + TEST_NAME = 1, + TEST_SITE, + TEST_PROBE_COUNT_UP, + TEST_PROBE_COUNT_DOWN, + TEST_PROBE_SIZE_UP, + TEST_PROBE_SIZE_DOWN, + TEST_TIMEOUT, + TEST_MINTIME_BETWEEN_PROBES, + TEST_TIME_TILL_RESEND, + TEST_RESEND_EXTRA_PROBE_COUNT, + TEST_ACCEPTABLE_PROBE_LOSS, + TEST_INIT_SYNC_TIME +}eQosCommonTestFields; + +//message TestResult +enum +{ + TEST_RESULT_SITE = 1, + TEST_RESULT_RTT, + TEST_RESULT_UP_BPS, + TEST_RESULT_DOWN_BPS, + TEST_RESULT_HRESULT +}eQosCommonTestResultFields; + +//message ServerRegistrationRequest +enum +{ + SERVER_TO_COORDINATOR_REQUEST_SITE = 1, + SERVER_TO_COORDINATOR_REQUEST_ADDR, + SERVER_TO_COORDINATOR_REQUEST_POOL, + SERVER_TO_COORDINATOR_REQUEST_KEY, + SERVER_TO_COORDINATOR_REQUEST_PORT, + SERVER_TO_COORDINATOR_REQUEST_CAPCAITY_SEC, + SERVER_TO_COORDINATOR_REQUEST_LAST_LOAD_SEC, + SERVER_TO_COORDINATOR_REQUEST_PROBE_VERSION, + SERVER_TO_COORDINATOR_REQUEST_UPDATE_INTERVAL, + SERVER_TO_COORDINATOR_REQUEST_SHUTTING_DOWN +}eServerToCoordinatorRequestFields; + +enum +{ + COORDINATOR_TO_SERVER_RESPONSE_STATUS = 1, + COORDINATOR_TO_SERVER_RESPONSE_REGISTRATION_MESSAGE, + COORDINATOR_TO_SERVER_RESPONSE_MIN_SERVICE_ID +}eCoordinatorToServerResponseFields; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _QosCommonScrubProbeResults + + \Description + Time values are kind of meaningless outside of the box they are generated on + here we try to make them more readable and take less space by trimming them to + lower values. + + \Input *pIn - input values + \Input *pOut - [out] output values + \Input uHightWater - max array index used + + \Version 08/02/2017 (cvienneau) +*/ +/********************************************************************************F*/ +static void _QosCommonScrubProbeResults(const QosCommonProbeResultT* pIn, QosCommonProbeResultT* pOut, uint32_t uHightWater) +{ + //scan data for lowest client side time and lowest server side time + uint32_t uMinClientTime = (uint32_t)-1; + uint32_t uMinServerTime = (uint32_t)-1; + uint32_t uProbeIndex; + for (uProbeIndex = 0; uProbeIndex <= uHightWater; uProbeIndex++) + { + if ((pIn[uProbeIndex].uClientSendTime != 0) && (pIn[uProbeIndex].uClientSendTime < uMinClientTime)) + { + uMinClientTime = pIn[uProbeIndex].uClientSendTime; + } + + if ((pIn[uProbeIndex].uServerReceiveTime != 0) && (pIn[uProbeIndex].uServerReceiveTime < uMinServerTime)) + { + uMinServerTime = pIn[uProbeIndex].uServerReceiveTime; + } + } + + //zeros are used when probes are missing, so we want to use 1 instead. + uMinClientTime--; + uMinServerTime--; + + //pass over data again to reduce values + for (uProbeIndex = 0; uProbeIndex <= uHightWater; uProbeIndex++) + { + if (pIn[uProbeIndex].uClientSendTime != 0) + { + pOut[uProbeIndex].uClientSendTime = pIn[uProbeIndex].uClientSendTime - uMinClientTime; + } + else + { + pOut[uProbeIndex].uClientSendTime = UINT32_MAX; + } + + if (pIn[uProbeIndex].uServerReceiveTime != 0) + { + pOut[uProbeIndex].uServerReceiveTime = pIn[uProbeIndex].uServerReceiveTime - uMinServerTime; + //copy the delta values out of the original array deltas don't need re-basing + pOut[uProbeIndex].uClientReceiveDelta = pIn[uProbeIndex].uClientReceiveDelta; + pOut[uProbeIndex].uServerSendDelta = pIn[uProbeIndex].uServerSendDelta; + } + else + { + //if the server didn't receive the probe put error sentinel values in for everything + pOut[uProbeIndex].uServerReceiveTime = UINT32_MAX; + pOut[uProbeIndex].uClientReceiveDelta = UINT16_MAX; + pOut[uProbeIndex].uServerSendDelta = UINT16_MAX; + } + + } +} + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function QosCommonClientToCoordinatorEstimateEncodedSize + + \Description + Estimate how much space will be needed to encode the QosCommonClientToCoordinatorRequestT. + + \Input *pClientToCoordinatorRequest - structure to read values from + + \Output + int32_t - negative on error or, size on success + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +int32_t QosCommonClientToCoordinatorEstimateEncodedSize(const QosCommonClientToCoordinatorRequestT *pClientToCoordinatorRequest) +{ + // the basic introduction message with no results fits under 200 bytes, lets say 1k as a min anyways + int32_t iEstimatedSize = 1024; + // my own local test shows ~600 bytes for 3 results, approximately 200 bytes per result, but if the tests used more probes (i used 10) + // i could see this easily be 600b, lets just say 1k per result for plenty of wiggle room + iEstimatedSize = iEstimatedSize + (1024 * pClientToCoordinatorRequest->uNumResults); + + return(iEstimatedSize); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonClientToCoordinatorRequestEncode + + \Description + Encode the QosCommonClientToCoordinatorRequestT into a buffer, which + gives the coordinator information on who the client is and what test + results it might already have. + + \Input *pClientToCoordinatorRequest - structure to read values from + \Input *pBuffer - [out] buffer we are writing to + \Input uBuffSize - max size of buffer + \Input *pOutSize - [out] output size of encoded data + + \Output + uint8_t* - NULL on error or, pointer to filled buffer on success + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t* QosCommonClientToCoordinatorRequestEncode(const QosCommonClientToCoordinatorRequestT *pClientToCoordinatorRequest, uint8_t *pBuffer, uint32_t uBuffSize, uint32_t *pOutSize) +{ + char strTemp[QOS_COMMON_MAX_RPC_STRING]; + uint32_t uIndex, uIndexProbe; + int32_t iError = 0; + + ds_memclr(pBuffer, QOS_COMMON_RPC_HEADER_SIZE); + ProtobufWriteRefT *pEncoder = ProtobufWriteCreate(pBuffer + QOS_COMMON_RPC_HEADER_SIZE, uBuffSize - QOS_COMMON_RPC_HEADER_SIZE, TRUE); + + if (pEncoder) + { + iError |= ProtobufWriteString(pEncoder, pClientToCoordinatorRequest->strQosProfile, (int32_t)strlen(pClientToCoordinatorRequest->strQosProfile), CLIENT_TO_COORDINATOR_REQUEST_PROFILE); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->uServiceRequestID, CLIENT_TO_COORDINATOR_REQUEST_SERVICE_ID); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->uProbeVersion, CLIENT_TO_COORDINATOR_REQUEST_PROBE_VERSION); + + iError |= ProtobufWriteMessageBegin(pEncoder, CLIENT_TO_COORDINATOR_REQUEST_MODIFIERS); + QosCommonAddrToString(&pClientToCoordinatorRequest->clientModifiers.clientAddressInternal, strTemp, sizeof(strTemp)); + iError |= ProtobufWriteString(pEncoder, strTemp, (int32_t)strlen(strTemp), CLIENT_MODIFIER_INTERNAL_ADDRESS); + iError |= ProtobufWriteString(pEncoder, pClientToCoordinatorRequest->clientModifiers.strPlatform, (int32_t)strlen(pClientToCoordinatorRequest->clientModifiers.strPlatform), CLIENT_MODIFIER_PLATFORM); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->clientModifiers.eOSFirewallType, CLIENT_MODIFIER_OS_FIREWALL); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->clientModifiers.uPacketQueueRemaining, CLIENT_MODIFIER_PACKET_QUEUE_REMAINING); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->clientModifiers.uStallCountCoordinator, CLIENT_MODIFIER_STALL_COUNT_COMM); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->clientModifiers.uStallCountProbe, CLIENT_MODIFIER_STALL_COUNT_PROBE); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->clientModifiers.uStallDurationCoordinator, CLIENT_MODIFIER_STALL_DURATION_COMM); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->clientModifiers.uStallDurationProbe, CLIENT_MODIFIER_STALL_DURATION_PROBE); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->clientModifiers.hResult, CLIENT_MODIFIER_HRESULT); + iError |= ProtobufWriteMessageEnd(pEncoder); //end of CLIENT_TO_COORDINATOR_REQUEST_MODIFIERS + + // add all current results + if ((iError == 0) && (pClientToCoordinatorRequest->uNumResults > 0)) + { + for (uIndex = 0; uIndex < pClientToCoordinatorRequest->uNumResults; uIndex++) + { + iError |= ProtobufWriteMessageBegin(pEncoder, CLIENT_TO_COORDINATOR_REQUEST_ARESULTS); + iError |= ProtobufWriteVarint(pEncoder, pClientToCoordinatorRequest->aResults[uIndex].hResult, RAW_RESULTS_HRESULT); + iError |= ProtobufWriteString(pEncoder, pClientToCoordinatorRequest->aResults[uIndex].strSiteName, (int32_t)strlen(pClientToCoordinatorRequest->aResults[uIndex].strSiteName), RAW_RESULTS_SITE_NAME); + iError |= ProtobufWriteString(pEncoder, pClientToCoordinatorRequest->aResults[uIndex].strTestName, (int32_t)strlen(pClientToCoordinatorRequest->aResults[uIndex].strTestName), RAW_RESULTS_TEST_NAME); + QosCommonAddrToString(&pClientToCoordinatorRequest->aResults[uIndex].clientAddressFromServer, strTemp, sizeof(strTemp)); + iError |= ProtobufWriteString(pEncoder, strTemp, (int32_t)strlen(strTemp), RAW_RESULTS_CLIENT_ADDRESS_FROM_SERVER); + + if ((iError == 0) && (pClientToCoordinatorRequest->aResults[uIndex].uProbeCountDown > 0)) + { + //make an array the same size as the one contained in the struct + QosCommonProbeResultT aScrubedProbeResults[(sizeof(pClientToCoordinatorRequest->aResults[uIndex].aProbeResult) / sizeof(pClientToCoordinatorRequest->aResults[uIndex].aProbeResult[0]))]; + + //lower the values used to something more human readable + _QosCommonScrubProbeResults(pClientToCoordinatorRequest->aResults[uIndex].aProbeResult, aScrubedProbeResults, pClientToCoordinatorRequest->aResults[uIndex].uProbeCountHighWater); + // info on individual probes + for (uIndexProbe = 0; uIndexProbe <= pClientToCoordinatorRequest->aResults[uIndex].uProbeCountHighWater; uIndexProbe++) //this list may have spaces if some probes were not received + { + iError |= ProtobufWriteMessageBegin(pEncoder, RAW_RESULTS_ARESULT); + iError |= ProtobufWriteVarint(pEncoder, aScrubedProbeResults[uIndexProbe].uClientSendTime, PROBE_RESULT_CLIENT_SEND_TIME); + iError |= ProtobufWriteVarint(pEncoder, aScrubedProbeResults[uIndexProbe].uServerReceiveTime, PROBE_RESULT_SERVER_RECV_TIME); + iError |= ProtobufWriteVarint(pEncoder, aScrubedProbeResults[uIndexProbe].uServerSendDelta, PROBE_RESULT_SERVER_SEND_DELTA); + iError |= ProtobufWriteVarint(pEncoder, aScrubedProbeResults[uIndexProbe].uClientReceiveDelta, PROBE_RESULT_CLIENT_RECV_DELTA); + iError |= ProtobufWriteMessageEnd(pEncoder); //end of probe result + } + } + iError |= ProtobufWriteMessageEnd(pEncoder); //end of result + } + } + *pOutSize = ProtobufWriteDestroy(pEncoder) + QOS_COMMON_RPC_HEADER_SIZE; + } + else + { + iError |= -1; + *pOutSize = 0; + } + return((iError == 0) ? pBuffer : NULL); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonClientToCoordinatorPrint + + \Description + Print the QosCommonClientToCoordinatorRequestT to tty. + + \Input *pClientToCoordinatorRequest - structure to print + \Input uLogLevel - log level to control spam + + \Version 03/03/2018 (cvienneau) +*/ +/********************************************************************************F*/ +void QosCommonClientToCoordinatorPrint(const QosCommonClientToCoordinatorRequestT *pClientToCoordinatorRequest, uint32_t uLogLevel) +{ +#if DIRTYCODE_LOGGING + char strTemp[QOS_COMMON_MAX_RPC_STRING]; + uint32_t uIndex, uIndexProbe; + + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.strQosProfile: %s\n", pClientToCoordinatorRequest->strQosProfile)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.uServiceRequestID: %u\n", pClientToCoordinatorRequest->uServiceRequestID)); + NetPrintfVerbose((uLogLevel, 1, "qoscommon: cl2co.uProbeVersion: %u\n", pClientToCoordinatorRequest->uProbeVersion)); + + QosCommonAddrToString(&pClientToCoordinatorRequest->clientModifiers.clientAddressInternal, strTemp, sizeof(strTemp)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.cm.clientAddressInternal: %s\n", strTemp)); + NetPrintfVerbose((uLogLevel, 1, "qoscommon: cl2co.cm.strPlatform: %s\n", pClientToCoordinatorRequest->clientModifiers.strPlatform)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.cm.eOSFirewallType: %u\n", pClientToCoordinatorRequest->clientModifiers.eOSFirewallType)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.cm.uPacketQueueRemaining: %u\n", pClientToCoordinatorRequest->clientModifiers.uPacketQueueRemaining)); + NetPrintfVerbose((uLogLevel, 1, "qoscommon: cl2co.cm.uStallCountCoordinator: %u\n", pClientToCoordinatorRequest->clientModifiers.uStallCountCoordinator)); + NetPrintfVerbose((uLogLevel, 1, "qoscommon: cl2co.cm.uStallCountProbe: %u\n", pClientToCoordinatorRequest->clientModifiers.uStallCountProbe)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.cm.uStallDurationCoordinator: %u\n", pClientToCoordinatorRequest->clientModifiers.uStallDurationCoordinator)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.cm.uStallDurationProbe: %u\n", pClientToCoordinatorRequest->clientModifiers.uStallDurationProbe)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.cm.hResult: 0x%08x\n", pClientToCoordinatorRequest->clientModifiers.hResult)); + + // add all current results + for (uIndex = 0; uIndex < pClientToCoordinatorRequest->uNumResults; uIndex++) + { + NetPrintfVerbose((uLogLevel, 0, "qoscommon: cl2co.aResults[%u] site: %s, test: %s, hresult: 0x%08x, up: %u, down: %u, high: %u, addr: %s\n", + uIndex, + pClientToCoordinatorRequest->aResults[uIndex].strSiteName, + pClientToCoordinatorRequest->aResults[uIndex].strTestName, + pClientToCoordinatorRequest->aResults[uIndex].hResult, + pClientToCoordinatorRequest->aResults[uIndex].uProbeCountUp, + pClientToCoordinatorRequest->aResults[uIndex].uProbeCountDown, + pClientToCoordinatorRequest->aResults[uIndex].uProbeCountHighWater, + QosCommonAddrToString(&pClientToCoordinatorRequest->aResults[uIndex].clientAddressFromServer, strTemp, sizeof(strTemp)) + )); + + if (pClientToCoordinatorRequest->aResults[uIndex].uProbeCountDown > 0) + { + //make an array the same size as the one contained in the struct + QosCommonProbeResultT aScrubedProbeResults[(sizeof(pClientToCoordinatorRequest->aResults[uIndex].aProbeResult) / sizeof(pClientToCoordinatorRequest->aResults[uIndex].aProbeResult[0]))]; + + //lower the values used to something more human readable + _QosCommonScrubProbeResults(pClientToCoordinatorRequest->aResults[uIndex].aProbeResult, aScrubedProbeResults, pClientToCoordinatorRequest->aResults[uIndex].uProbeCountHighWater); + // info on individual probes + for (uIndexProbe = 0; uIndexProbe <= pClientToCoordinatorRequest->aResults[uIndex].uProbeCountHighWater; uIndexProbe++) //this list may have spaces if some probes were not received + { + // only print the first probe, unless we have higher logging level set. + int32_t uLocalLogLevel = 0; + if (uIndexProbe > 0) + { + uLocalLogLevel = 1; + } + NetPrintfVerbose((uLogLevel, uLocalLogLevel, "qoscommon: cl2co.aResults[%d].aProbeResult[%d] csend: %u, srcv: %u, sdelta: %u, cdelta: %u\n", uIndex, uIndexProbe, + aScrubedProbeResults[uIndexProbe].uClientSendTime, + aScrubedProbeResults[uIndexProbe].uServerReceiveTime, + aScrubedProbeResults[uIndexProbe].uServerSendDelta, + aScrubedProbeResults[uIndexProbe].uClientReceiveDelta)); + } + } + } +#endif +} + + +/*F********************************************************************************/ +/*! + \Function QosCommonCoordinatorToClientResponseDecode + + \Description + Decodes the buffer, into a QosCommonCoordinatorToClientResponseT, which + gives the client instructions on what it should be doing next. + + \Input *pResponse - [out] structure to write values to + \Input *pBuffer - buffer we are reading from + \Input uBuffSize - max size of pBuffer + + \Output + int32_t - -1 on error 0 otherwise + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +int32_t QosCommonCoordinatorToClientResponseDecode(QosCommonCoordinatorToClientResponseT *pResponse, const uint8_t *pBuffer, uint32_t uBuffSize) +{ + int32_t iMsgSize; + uint8_t bError = FALSE; + + ds_memclr(pResponse, sizeof(QosCommonCoordinatorToClientResponseT)); + pBuffer = ProtobufCommonReadSize(pBuffer + QOS_COMMON_RPC_HEADER_SIZE, uBuffSize - QOS_COMMON_RPC_HEADER_SIZE, &iMsgSize); + + if (pBuffer) + { + ProtobufReadT Reader, ReaderValue; + const uint8_t *pCurrentRepeat; + char strTempAddr[QOS_COMMON_MAX_RPC_STRING]; + ProtobufReadInit(&Reader, pBuffer, iMsgSize); + + pResponse->uServiceRequestID = (uint32_t)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_SERVICE_ID)); + ProtobufReadString(&Reader, ProtobufReadFind(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_CLIENT_ADDR_FROM_COORDINATOR), strTempAddr, sizeof(strTempAddr)); + QosCommonStringToAddr(strTempAddr, &pResponse->configuration.clientAddressFromCoordinator); + + //most will only be valid if results have been calculated, but we don't know what magic the coordinator might use + pResponse->results.eFirewallType = (QosCommonFirewallTypeE)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_FIREWALL_TYPE)); + pResponse->results.uTimeTillRetry = (uint32_t)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_TIME_TILL_RETRY)); + ProtobufReadString(&Reader, ProtobufReadFind(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_CLIENT_EXTERNAL_ADDRESS), strTempAddr, sizeof(strTempAddr)); + QosCommonStringToAddr(strTempAddr, &pResponse->results.clientExternalAddress); + + pResponse->configuration.uNumSites = 0; + pCurrentRepeat = NULL; + while ((pCurrentRepeat = ProtobufReadFind2(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_APINGSITES, pCurrentRepeat)) != NULL) + { + if (pResponse->configuration.uNumSites >= QOS_COMMON_MAX_SITES) + { + bError = TRUE; + break; + } + pCurrentRepeat = ProtobufReadMessage(&Reader, pCurrentRepeat, &ReaderValue); + ProtobufReadString(&ReaderValue, ProtobufReadFind(&ReaderValue, SITE_NAME), pResponse->configuration.aSites[pResponse->configuration.uNumSites].strSiteName, sizeof(pResponse->configuration.aSites[pResponse->configuration.uNumSites].strSiteName)); + ProtobufReadString(&ReaderValue, ProtobufReadFind(&ReaderValue, SITE_ADDR), pResponse->configuration.aSites[pResponse->configuration.uNumSites].strProbeAddr, sizeof(pResponse->configuration.aSites[pResponse->configuration.uNumSites].strProbeAddr)); + ProtobufReadBytes(&ReaderValue, ProtobufReadFind(&ReaderValue, SITE_KEY), pResponse->configuration.aSites[pResponse->configuration.uNumSites].aSecureKey, sizeof(pResponse->configuration.aSites[pResponse->configuration.uNumSites].aSecureKey)); + pResponse->configuration.aSites[pResponse->configuration.uNumSites].uProbePort = ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, SITE_PORT)); + pResponse->configuration.aSites[pResponse->configuration.uNumSites].uProbeVersion = ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, SITE_PROBE_VERSION)); + pResponse->configuration.uNumSites++; + } + + pResponse->configuration.uNumControlConfigs = 0; + pCurrentRepeat = NULL; + while ((pCurrentRepeat = ProtobufReadFind2(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_ACONTROL_CONFIGS, pCurrentRepeat)) != NULL) + { + if (pResponse->configuration.uNumControlConfigs >= QOS_COMMON_MAX_CONTROL_CONFIGS) + { + bError = TRUE; + break; + } + pCurrentRepeat = ProtobufReadMessage(&Reader, pCurrentRepeat, &ReaderValue); + ProtobufReadString(&ReaderValue, ProtobufReadFind(&ReaderValue, CONTROL_CONFIG_NAME), pResponse->configuration.aControlConfigs[pResponse->configuration.uNumControlConfigs].strControl, sizeof(pResponse->configuration.aControlConfigs[pResponse->configuration.uNumControlConfigs].strControl)); + pResponse->configuration.aControlConfigs[pResponse->configuration.uNumControlConfigs].iValue = (int32_t)ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, CONTROL_CONFIG_I_VALUE)); + ProtobufReadString(&ReaderValue, ProtobufReadFind(&ReaderValue, CONTROL_CONFIG_STR_VALUE), pResponse->configuration.aControlConfigs[pResponse->configuration.uNumControlConfigs].strValue, sizeof(pResponse->configuration.aControlConfigs[pResponse->configuration.uNumControlConfigs].strValue)); + pResponse->configuration.uNumControlConfigs++; + } + + pResponse->configuration.uNumQosTests = 0; + pCurrentRepeat = NULL; + while ((pCurrentRepeat = ProtobufReadFind2(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_ATESTS, pCurrentRepeat)) != NULL) + { + if (pResponse->configuration.uNumQosTests >= QOS_COMMON_MAX_TESTS) + { + bError = TRUE; + break; + } + pCurrentRepeat = ProtobufReadMessage(&Reader, pCurrentRepeat, &ReaderValue); + ProtobufReadString(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_NAME), pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].strTestName, sizeof(pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].strTestName)); + ProtobufReadString(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_SITE), pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].strSiteName, sizeof(pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].strSiteName)); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uProbeCountUp = DS_CLAMP(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_PROBE_COUNT_UP)), QOS_COMMON_MIN_PROBE_COUNT, QOS_COMMON_MAX_PROBE_COUNT); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uProbeCountDown = DS_CLAMP(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_PROBE_COUNT_DOWN)), QOS_COMMON_MIN_PROBE_COUNT, QOS_COMMON_MAX_PROBE_COUNT); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uProbeSizeUp = DS_CLAMP(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_PROBE_SIZE_UP)), QOS_COMMON_MIN_PACKET_SIZE, QOS_COMMON_MAX_PACKET_SIZE); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uProbeSizeDown = DS_CLAMP(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_PROBE_SIZE_DOWN)), QOS_COMMON_MIN_PACKET_SIZE, QOS_COMMON_MAX_PACKET_SIZE); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uTimeout = DS_CLAMP(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_TIMEOUT)), QOS_COMMON_MIN_TIMEOUT, QOS_COMMON_MAX_TIMEOUT); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uMinTimeBetwenProbes = DS_MIN(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_MINTIME_BETWEEN_PROBES)), QOS_COMMON_MAX_TIME_BETWEEN_PROBES); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uTimeTillResend = DS_CLAMP(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_TIME_TILL_RESEND)), QOS_COMMON_MIN_RESEND_TIME, QOS_COMMON_MAX_RESEND_TIME); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uResendExtraProbeCount = DS_MIN(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_RESEND_EXTRA_PROBE_COUNT)), QOS_COMMON_MAX_RESEND_EXTRA_PROBES); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uAcceptableLostProbeCount = DS_MIN(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_ACCEPTABLE_PROBE_LOSS)), QOS_COMMON_MAX_ACCEPTABLE_LOST_PROBES); + pResponse->configuration.aQosTests[pResponse->configuration.uNumQosTests].uInitSyncTimeout = DS_MIN(ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_INIT_SYNC_TIME)), QOS_COMMON_MAX_INIT_SYNC_TIME); + pResponse->configuration.uNumQosTests++; + } + + pResponse->results.uNumResults = 0; + pCurrentRepeat = NULL; + while ((pCurrentRepeat = ProtobufReadFind2(&Reader, COORDINATOR_TO_CLIENT_RESPONSE_ASITE_RESULTS, pCurrentRepeat)) != NULL) + { + if (pResponse->results.uNumResults >= QOS_COMMON_MAX_SITES) + { + bError = TRUE; + break; + } + pCurrentRepeat = ProtobufReadMessage(&Reader, pCurrentRepeat, &ReaderValue); + ProtobufReadString(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_RESULT_SITE), pResponse->results.aTestResults[pResponse->results.uNumResults].strSiteName, sizeof(pResponse->results.aTestResults[pResponse->results.uNumResults].strSiteName)); + pResponse->results.aTestResults[pResponse->results.uNumResults].uMinRTT = (uint32_t)ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_RESULT_RTT)); + pResponse->results.aTestResults[pResponse->results.uNumResults].uUpbps = (uint32_t)ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_RESULT_UP_BPS)); + pResponse->results.aTestResults[pResponse->results.uNumResults].uDownbps = (uint32_t)ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_RESULT_DOWN_BPS)); + pResponse->results.aTestResults[pResponse->results.uNumResults].hResult = (uint32_t)ProtobufReadVarint(&ReaderValue, ProtobufReadFind(&ReaderValue, TEST_RESULT_HRESULT)); + pResponse->results.uNumResults++; + } + } + else + { + bError = TRUE; + } + + return(bError ? -1 : 0); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonCoordinatorToClientPrint + + \Description + Print the QosCommonCoordinatorToClientResponseT to tty. + + \Input *pResponse - structure to print + \Input uLogLevel - log level to control spam + + \Version 03/03/2018 (cvienneau) +*/ +/********************************************************************************F*/ +void QosCommonCoordinatorToClientPrint(const QosCommonCoordinatorToClientResponseT *pResponse, uint32_t uLogLevel) +{ +#if DIRTYCODE_LOGGING + char strTemp[QOS_COMMON_MAX_RPC_STRING]; + uint32_t uIndex; + + //todo set proper log levels of these prints + + NetPrintfVerbose((uLogLevel, 0, "qoscommon: co2cl.uServiceRequestID: %u\n", pResponse->uServiceRequestID)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: co2cl.configuration.clientAddressFromCoordinator: %s\n", QosCommonAddrToString(&pResponse->configuration.clientAddressFromCoordinator, strTemp, sizeof(strTemp)))); + NetPrintfVerbose((uLogLevel, 1, "qoscommon: co2cl.results.eFirewallType: %u\n", pResponse->results.eFirewallType)); + NetPrintfVerbose((uLogLevel, 0, "qoscommon: co2cl.results.uTimeTillRetry: %u\n", pResponse->results.uTimeTillRetry)); + NetPrintfVerbose((uLogLevel, 1, "qoscommon: co2cl.results.clientExternalAddress: %s\n", QosCommonAddrToString(&pResponse->results.clientExternalAddress, strTemp, sizeof(strTemp)))); + + for (uIndex = 0; uIndex < pResponse->configuration.uNumSites; uIndex++) + { + NetPrintfVerbose((uLogLevel, 0, "qoscommon: co2cl.configuration.aSites[%d] %s, %s:%u, %u\n", + uIndex, + pResponse->configuration.aSites[uIndex].strSiteName, + pResponse->configuration.aSites[uIndex].strProbeAddr, + pResponse->configuration.aSites[uIndex].uProbePort, + pResponse->configuration.aSites[uIndex].uProbeVersion)); + //NetPrintfVerbose((uLogLevel, 0, "qoscommon: co2cl.configuration.aSites[%d].aSecureKey: X\n", pResponse->configuration.aSites[uIndex].aSecureKey)); + } + + for (uIndex = 0; uIndex < pResponse->configuration.uNumControlConfigs; uIndex++) + { + NetPrintfVerbose((uLogLevel, 0, "qoscommon: co2cl.configuration.aControlConfigs[%u]: '%s', %d, %s\n", + uIndex, + pResponse->configuration.aControlConfigs[uIndex].strControl, + pResponse->configuration.aControlConfigs[uIndex].iValue, + pResponse->configuration.aControlConfigs[uIndex].strValue)); + } + + for (uIndex = 0; uIndex < pResponse->configuration.uNumQosTests; uIndex++) + { + NetPrintfVerbose((uLogLevel, 0, "qoscommon: co2cl.configuration.aQosTests[%u]: %s, %s, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u\n", + uIndex, + pResponse->configuration.aQosTests[uIndex].strTestName, + pResponse->configuration.aQosTests[uIndex].strSiteName, + pResponse->configuration.aQosTests[uIndex].uProbeCountUp, + pResponse->configuration.aQosTests[uIndex].uProbeCountDown, + pResponse->configuration.aQosTests[uIndex].uProbeSizeUp, + pResponse->configuration.aQosTests[uIndex].uProbeSizeDown, + pResponse->configuration.aQosTests[uIndex].uTimeout, + pResponse->configuration.aQosTests[uIndex].uMinTimeBetwenProbes, + pResponse->configuration.aQosTests[uIndex].uTimeTillResend, + pResponse->configuration.aQosTests[uIndex].uResendExtraProbeCount, + pResponse->configuration.aQosTests[uIndex].uAcceptableLostProbeCount, + pResponse->configuration.aQosTests[uIndex].uInitSyncTimeout)); + } + + for (uIndex = 0; uIndex < pResponse->results.uNumResults; uIndex++) + { + NetPrintfVerbose((uLogLevel, 1, "qoscommon: co2cl.results.aTestResults[%d]: %s, %u, %u, %u, 0x%08x\n", + uIndex, + pResponse->results.aTestResults[uIndex].strSiteName, + pResponse->results.aTestResults[uIndex].uMinRTT, + pResponse->results.aTestResults[uIndex].uUpbps, + pResponse->results.aTestResults[uIndex].uDownbps, + pResponse->results.aTestResults[uIndex].hResult)); + } +#endif +} + + +/*F********************************************************************************/ +/*! + \Function QosCommonServerToCoordinatorRequestEncode + + \Description + Encode QosCommonServerToCoordinatorRequestT into a buffer, which either + tells the coordinator we are a new server to be added to the available pool + or as a heartbeat and update of the secure key. + + \Input *pServerRegistrationRequest - structure to read values from + \Input *pBuffer - [out] buffer we are writing to + \Input uBuffSize - size of buffer + \Input *pOutSize - [out] output size of encoded data + + \Output + uint8_t* - NULL on error, pointer to filled buffer on success + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t* QosCommonServerToCoordinatorRequestEncode(const QosCommonServerToCoordinatorRequestT *pServerRegistrationRequest, uint8_t *pBuffer, uint32_t uBuffSize, uint32_t *pOutSize) +{ + int32_t iError = 0; + + //first byte is grpc header + ds_memclr(pBuffer, QOS_COMMON_RPC_HEADER_SIZE); + ProtobufWriteRefT *pEncoder = ProtobufWriteCreate(pBuffer + QOS_COMMON_RPC_HEADER_SIZE, uBuffSize - QOS_COMMON_RPC_HEADER_SIZE, TRUE); + + if (pEncoder) + { + iError |= ProtobufWriteString(pEncoder, pServerRegistrationRequest->strSiteName, (int32_t)strlen(pServerRegistrationRequest->strSiteName), SERVER_TO_COORDINATOR_REQUEST_SITE); + iError |= ProtobufWriteString(pEncoder, pServerRegistrationRequest->strPool, (int32_t)strlen(pServerRegistrationRequest->strPool), SERVER_TO_COORDINATOR_REQUEST_POOL); + iError |= ProtobufWriteString(pEncoder, pServerRegistrationRequest->strAddr, (int32_t)strlen(pServerRegistrationRequest->strAddr), SERVER_TO_COORDINATOR_REQUEST_ADDR); + iError |= ProtobufWriteBytes(pEncoder, pServerRegistrationRequest->aSecureKey, sizeof(pServerRegistrationRequest->aSecureKey), SERVER_TO_COORDINATOR_REQUEST_KEY); + iError |= ProtobufWriteVarint(pEncoder, pServerRegistrationRequest->uPort, SERVER_TO_COORDINATOR_REQUEST_PORT); + iError |= ProtobufWriteVarint(pEncoder, pServerRegistrationRequest->uCapacityPerSec, SERVER_TO_COORDINATOR_REQUEST_CAPCAITY_SEC); + iError |= ProtobufWriteVarint(pEncoder, pServerRegistrationRequest->uLastLoadPerSec, SERVER_TO_COORDINATOR_REQUEST_LAST_LOAD_SEC); + iError |= ProtobufWriteVarint(pEncoder, pServerRegistrationRequest->uProbeVersion, SERVER_TO_COORDINATOR_REQUEST_PROBE_VERSION); + iError |= ProtobufWriteVarint(pEncoder, pServerRegistrationRequest->uUpdateInterval, SERVER_TO_COORDINATOR_REQUEST_UPDATE_INTERVAL); + iError |= ProtobufWriteVarint(pEncoder, pServerRegistrationRequest->bShuttingDown, SERVER_TO_COORDINATOR_REQUEST_SHUTTING_DOWN); + *pOutSize = ProtobufWriteDestroy(pEncoder) + QOS_COMMON_RPC_HEADER_SIZE; + } + else + { + iError |= -1; + *pOutSize = 0; + } + return((iError == 0) ? pBuffer : NULL); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonCoordinatorToServerResponseDecode + + \Description + Decode buffer into a QosCommonCoordinatorToServerResponseT, which + tells the server any registration status. + + \Input *pServerRegistrationResponse - [out] structure to write values to + \Input *pBuffer - buffer we are reading from + \Input uBuffSize - size of buffer + + \Output + int32_t - -1 on err, 0 on success + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +int32_t QosCommonCoordinatorToServerResponseDecode(QosCommonCoordinatorToServerResponseT *pServerRegistrationResponse, const uint8_t *pBuffer, uint32_t uBuffSize) +{ + int32_t iMsgSize; + uint8_t bError = FALSE; + + ds_memclr(pServerRegistrationResponse, sizeof(QosCommonCoordinatorToServerResponseT)); + pBuffer = ProtobufCommonReadSize(pBuffer + QOS_COMMON_RPC_HEADER_SIZE, uBuffSize - QOS_COMMON_RPC_HEADER_SIZE, &iMsgSize); + + if (pBuffer) + { + ProtobufReadT Reader; + ProtobufReadInit(&Reader, pBuffer, iMsgSize); + //note we don't do anything with COORDINATOR_TO_SERVER_RESPONSE_STATUS + ProtobufReadString(&Reader, ProtobufReadFind(&Reader, COORDINATOR_TO_SERVER_RESPONSE_REGISTRATION_MESSAGE), pServerRegistrationResponse->strRegistrationMessage, sizeof(pServerRegistrationResponse->strRegistrationMessage)); + pServerRegistrationResponse->uMinServiceRequestID = (uint32_t)ProtobufReadVarint(&Reader, ProtobufReadFind(&Reader, COORDINATOR_TO_SERVER_RESPONSE_MIN_SERVICE_ID)); + } + else + { + bError = TRUE; + } + + return(bError ? -1 : 0); +} + + +/*F********************************************************************************/ +/*! + \Function QosCommonAddrToString + + \Description + Convert a address into a human readable easily pars-able string. + + \Input *pAddr - address to convert + \Input *pBuffer - [out] buffer to write to + \Input iBufSize - size of buffer + + \Output + char* - the pBuffer pointer that was written to + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +char* QosCommonAddrToString(const QosCommonAddrT *pAddr, char *pBuffer, int32_t iBufSize) +{ + //we build a sockaddr, just so we can use our library print function, which saves us some headache + // if we wanted more details take a look at _ds_sockaddrtostr() + if (pAddr->uFamily == AF_INET) + { + struct sockaddr sockAddr; + SockaddrInit(&sockAddr, AF_INET); + SockaddrInSetAddr(&sockAddr, pAddr->addr.v4); + SockaddrInSetPort(&sockAddr, pAddr->uPort); + ds_snzprintf(pBuffer, iBufSize, "v4%A", &sockAddr); + } + #ifndef DIRTYCODE_NX + else if (pAddr->uFamily == AF_INET6) + { + struct sockaddr_in6 sockAddr6; + SockaddrInit6(&sockAddr6, AF_INET6); + ds_memcpy(&sockAddr6.sin6_addr, &(pAddr->addr.v6), sizeof(sockAddr6.sin6_addr)); + sockAddr6.sin6_port = pAddr->uPort; + ds_snzprintf(pBuffer, iBufSize, "v6%A", &sockAddr6); + } + #endif + else + { + ds_snzprintf(pBuffer, iBufSize, "na[0]:0"); + } + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonStringToAddr + + \Description + Convert a string generated by QosCommonAddrToString into a QosCommonAddrT + + \Input *pStrIn - text to convert + \Input *pOutAddr - [out] address to write to + + \Output + int32_t - -1 on err, 0 on success + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +int32_t QosCommonStringToAddr(char *pStrIn, QosCommonAddrT *pOutAddr) +{ + int32_t iRet = -1; + int32_t iCurrentToken = 0; + char *pSave = NULL; + char *pTokenTemp = NULL; + char *pToken0 = NULL; + char *pToken1 = NULL; + char *pToken2 = NULL; + + ds_memclr(pOutAddr, sizeof(QosCommonAddrT)); + pTokenTemp = ds_strtok_r(pStrIn, "[]", &pSave); + while (pTokenTemp != NULL) + { + //process the token into a variable based off the token count + if (iCurrentToken == 0) + { + pToken0 = pTokenTemp; + } + else if (iCurrentToken == 1) + { + pToken1 = pTokenTemp; + } + else if (iCurrentToken == 2) + { + pToken2 = pTokenTemp; + } + pTokenTemp = ds_strtok_r(NULL, "[]", &pSave); + iCurrentToken++; + } + + if ((pToken0 != NULL) && (ds_strnicmp(pToken0, "v4", 2) == 0)) + { + pOutAddr->uFamily = AF_INET; + if (pToken1 != NULL) + { + struct sockaddr tempAddr; //doing this so i can make use of SockaddrInSetAddrText + SockaddrInit(&tempAddr, AF_INET); + SockaddrInSetAddrText(&tempAddr, pToken1); + pOutAddr->addr.v4 = SockaddrInGetAddr(&tempAddr); + iRet = 0; + } + if (pToken2 != NULL) + { + pOutAddr->uPort = atoi(pToken2 + 1); // +1 to move past the ':' character + } + } + #ifndef DIRTYCODE_NX + else if ((pToken0 != NULL) && (ds_strnicmp(pToken0, "v6", 2) == 0)) + { + pOutAddr->uFamily = AF_INET6; + if (pToken1 != NULL) + { + struct sockaddr_in6 tempAddr; //doing this so i can make use of SockaddrInSetAddrText + SockaddrInit6(&tempAddr, AF_INET6); + SockaddrInSetAddrText((struct sockaddr *)&tempAddr, pToken1); //this will translate the string into the bytes it needs to be + ds_memcpy(&(pOutAddr->addr.v6), &tempAddr.sin6_addr, sizeof(pOutAddr->addr.v6)); //write the bytes into our structure + iRet = 0; + } + if (pToken2 != NULL) + { + pOutAddr->uPort = atoi(pToken2 + 1); // +1 to move past the ':' character + } + } + #endif + return(iRet); +} +/*F********************************************************************************/ +/*! + \Function QosCommonConvertAddr + + \Description + Determine if pSourceAddr is remapped, if so fetch the real info and + copy the common fields from pSourceAddr to pTargetAddr. + + \Input *pTargetAddr - [out] structure to store the fields we are interested in + \Input *pSourceAddr - address to copy from + + \Version 06/27/2017 (cvienneau) +*/ +/********************************************************************************F*/ +void QosCommonConvertAddr(QosCommonAddrT *pTargetAddr, struct sockaddr *pSourceAddr) +{ + #ifndef DIRTYCODE_NX + struct sockaddr_in6 SockAddr6; + + //check to see if the ipv4 address coming in is a remapped ipv6 addr, if it is get the real ipv6 info + if (pSourceAddr->sa_family == AF_INET) + { + uint32_t uAddr = SockaddrInGetAddr(pSourceAddr); + if (SocketInfo(NULL, '?ip6', uAddr, &SockAddr6, sizeof(SockAddr6)) == 1) + { + pSourceAddr = (struct sockaddr *)&SockAddr6; //it is an ipv6 remapped addr, use the ipv6 info below. + } + } + #endif + + pTargetAddr->uFamily = pSourceAddr->sa_family; + + if (pTargetAddr->uFamily == AF_INET) + { + pTargetAddr->addr.v4 = SockaddrInGetAddr(pSourceAddr); + pTargetAddr->uPort = SockaddrInGetPort(pSourceAddr); + } + + #ifndef DIRTYCODE_NX + else if (pTargetAddr->uFamily == AF_INET6) + { + struct sockaddr_in6 * pAddr6 = (struct sockaddr_in6 *)pSourceAddr; + ds_memcpy(&(pTargetAddr->addr.v6), &pAddr6->sin6_addr, sizeof(pTargetAddr->addr.v6)); + pTargetAddr->uPort = pAddr6->sin6_port; + } + #endif +} + + +/*F********************************************************************************/ +/*! + \Function QosCommonIsAddrEqual + + \Description + Compare if two addresses belong to the same machine (ignore port). + + \Input *pAddr1 - first address to compare + \Input *pAddr2 - second address to compare + + \Output + uint8_t - TRUE if addresses are equal + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t QosCommonIsAddrEqual(QosCommonAddrT *pAddr1, QosCommonAddrT *pAddr2) +{ + if (pAddr1->uFamily == pAddr2->uFamily) + { + if (pAddr1->uFamily == AF_INET) + { + if (pAddr1->addr.v4 == pAddr2->addr.v4) + { + return(TRUE); + } + } + #ifndef DIRTYCODE_NX + else if (pAddr1->uFamily == AF_INET6) + { + //just compare the address portion of the struct + if (memcmp(&pAddr1->addr.v6, + &pAddr2->addr.v6, + sizeof(pAddr1->addr.v6)) == 0) + { + return(TRUE); + } + } + #endif + } + + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonIsRemappedAddrEqual + + \Description + Retrieve the address remap for pAddr1 if it exists (would be the case for ipv6) + and compare it to the v4 or already remapped pAddr2 to see if they belong to the same + machine. + + \Input *pAddr1 - first address to compare + \Input *pAddr2 - second address to compare + + \Output + uint8_t - TRUE if addresses are equal + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t QosCommonIsRemappedAddrEqual(QosCommonAddrT *pAddr1, struct sockaddr *pAddr2) +{ + uint32_t uAddr1v4; + uint32_t uAddr2v4; + + if (pAddr2->sa_family != AF_INET) + { + return(FALSE); //we expect this address to be ipv4 or a remapped ipv6, so we shouldn't see anything other than AF_INET + } + uAddr2v4 = SockaddrInGetAddr(pAddr2); + + if (pAddr1->uFamily == AF_INET) + { + uAddr1v4 = pAddr1->addr.v4; + } + #ifndef DIRTYCODE_NX + else if (pAddr1->uFamily == AF_INET6) + { + struct sockaddr_in6 addr6; + SockaddrInit6(&addr6, AF_INET6); + ds_memcpy(&addr6.sin6_addr, &(pAddr1->addr.v6), sizeof(addr6.sin6_addr)); + addr6.sin6_port = pAddr1->uPort; + uAddr1v4 = SocketControl(NULL, '+ip6', sizeof(addr6), &addr6, NULL); + //todo when will things be removed from the address map? + } + #endif + else + { + return(FALSE); // we don't know the type, they're not equal + } + + if (uAddr1v4 == uAddr2v4) + { + return(TRUE); + } + + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonSerializeProbePacket + + \Description + Write the contents of a QosCommonProbePacketT struct to a buffer, sign it for authenticity + in preparation for sending it on the wire. + + \Input *pOutBuff - [out] buffer the packet will be written to + \Input uBuffSize - size of the buffer the packet is being written to, must be at least QOS_COMMON_MIN_PACKET_SIZE + \Input *pInPacket - the structure containing the probe packet information + \Input *pSecureKey- the secure key used to sign the probe as being authentic + + \Output + uint8_t - number of bytes written to the buffer + + \Version 07/06/2017 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t QosCommonSerializeProbePacket(uint8_t *pOutBuff, uint32_t uBuffSize, const QosCommonProbePacketT *pInPacket, uint8_t *pSecureKey) +{ + /* + This is the order we are going to serialize in + uint32_t uProtocol; //!< QOS 2.0 packets will always contain 'qos2' for easy identification. + uint16_t uVersion; //!< uniquely identifies protocol + uint32_t uServiceRequestId; //!< provided by the QosCoordinator, unique to the client doing multiple QOS actions, used to identify which server resources this client is using + uint16_t uClientRequestId; //!< provided by the client, unique to a particular QOS action, used to pair request and responses together + uint32_t uServerReceiveTime; //!< time the server received this probe from the client + uint16_t uServerSendDelta; //!< delta before the server sent this probe response + uint16_t uProbeSizeUp; //!< indicates how big this probe is, including any padding for bandwidth + uint16_t uProbeSizeDown; //!< indicates how big this probe is, including any padding for bandwidth + uint8_t uProbeCountUp; //!< count index of this probe + uint8_t uProbeCountDown; //!< count index of this probe + QosCommonAddrT clientAddressFromService; //!< initially the client address from coordinator, however address from server prospective takes precedence, used to authenticate the packet is coming from the address that generated it + uint8_t aHmac[QOS_COMMON_HMAC_SIZE];//!< when combined with the secure key identifies this packet as coming from a real QOS client + */ + if (uBuffSize >= QOS_COMMON_MIN_PACKET_SIZE) + { + uint32_t uTemp32; + uint16_t uTemp16; + uint8_t *pWrite = pOutBuff; + + uTemp32 = SocketHtonl(pInPacket->uProtocol); ds_memcpy(pWrite, &uTemp32, sizeof(uint32_t)); pWrite += sizeof(uint32_t); //4 + uTemp16 = SocketHtons(pInPacket->uVersion); ds_memcpy(pWrite, &uTemp16, sizeof(uint16_t)); pWrite += sizeof(uint16_t); //2 + uTemp32 = SocketHtonl(pInPacket->uServiceRequestId); ds_memcpy(pWrite, &uTemp32, sizeof(uint32_t)); pWrite += sizeof(uint32_t); //4 + uTemp16 = SocketHtons(pInPacket->uClientRequestId); ds_memcpy(pWrite, &uTemp16, sizeof(uint16_t)); pWrite += sizeof(uint16_t); //2 + uTemp32 = SocketHtonl(pInPacket->uServerReceiveTime); ds_memcpy(pWrite, &uTemp32, sizeof(uint32_t)); pWrite += sizeof(uint32_t); //4 + uTemp16 = SocketHtons(pInPacket->uServerSendDelta); ds_memcpy(pWrite, &uTemp16, sizeof(uint16_t)); pWrite += sizeof(uint16_t); //2 + uTemp16 = SocketHtons(pInPacket->uProbeSizeUp); ds_memcpy(pWrite, &uTemp16, sizeof(uint16_t)); pWrite += sizeof(uint16_t); //2 + uTemp16 = SocketHtons(pInPacket->uProbeSizeDown); ds_memcpy(pWrite, &uTemp16, sizeof(uint16_t)); pWrite += sizeof(uint16_t); //2 + /*pInPacket->uProbeCountUp;*/ ds_memcpy(pWrite, &pInPacket->uProbeCountUp, sizeof(uint8_t)); pWrite += sizeof(uint8_t); //1 + /*pInPacket->uProbeCountDown;*/ ds_memcpy(pWrite, &pInPacket->uProbeCountDown, sizeof(uint8_t)); pWrite += sizeof(uint8_t); //1 + /*pInPacket->uExpectedProbeCountUp;*/ ds_memcpy(pWrite, &pInPacket->uExpectedProbeCountUp, sizeof(uint8_t)); pWrite += sizeof(uint8_t); //1 + uTemp16 = SocketHtons(pInPacket->clientAddressFromService.uFamily); ds_memcpy(pWrite, &uTemp16, sizeof(uint16_t)); pWrite += sizeof(uint16_t); //2 + uTemp16 = SocketHtons(pInPacket->clientAddressFromService.uPort); ds_memcpy(pWrite, &uTemp16, sizeof(uint16_t)); pWrite += sizeof(uint16_t); //2 + uTemp32 = SocketHtonl(pInPacket->clientAddressFromService.addr.v6.aDwords[0]); ds_memcpy(pWrite, &uTemp32, sizeof(uint32_t)); pWrite += sizeof(uint32_t); //4 + uTemp32 = SocketHtonl(pInPacket->clientAddressFromService.addr.v6.aDwords[1]); ds_memcpy(pWrite, &uTemp32, sizeof(uint32_t)); pWrite += sizeof(uint32_t); //4 + uTemp32 = SocketHtonl(pInPacket->clientAddressFromService.addr.v6.aDwords[2]); ds_memcpy(pWrite, &uTemp32, sizeof(uint32_t)); pWrite += sizeof(uint32_t); //4 + uTemp32 = SocketHtonl(pInPacket->clientAddressFromService.addr.v6.aDwords[3]); ds_memcpy(pWrite, &uTemp32, sizeof(uint32_t)); pWrite += sizeof(uint32_t); //4 + //total 45 bytes == QOS_COMMON_SIZEOF_PROBE_DATA + //generate hmac, it is just bytes, we don't need to hton them + //hash the probe values with the secure key to sign the probe as being authentic + CryptHmacCalc(pWrite, QOS_COMMON_HMAC_SIZE, pOutBuff, QOS_COMMON_SIZEOF_PROBE_DATA, pSecureKey, QOS_COMMON_SECURE_KEY_LENGTH, QOS_COMMON_HMAC_TYPE); + pWrite += QOS_COMMON_HMAC_SIZE; + return(pWrite - pOutBuff); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonDeserializeClientRequestId + + \Description + Read the uClientRequestId from the packet without doing any validation. + + \Input *pInBuff - buffer the packet will be read from + + \Output + uint16_t - the uClientRequestId field from a serialized QosCommonProbePacketT + + \Version 07/06/2017 (cvienneau) + */ +/********************************************************************************F*/ +uint16_t QosCommonDeserializeClientRequestId(uint8_t *pInBuff) +{ + //uClientRequestId is the 10'th byte in the serialized packet + return(SocketNtohs(*(uint16_t*)(pInBuff+10))); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonDeserializeServiceRequestId + + \Description + Read the uServiceRequestId from the packet without doing any validation. + + \Input *pInBuff - buffer the packet will be read from to + + \Output + uint32_t - the uServiceRequestId field from a serialized QosCommonProbePacketT + + \Version 07/06/2017 (cvienneau) +*/ +/********************************************************************************F*/ +uint32_t QosCommonDeserializeServiceRequestId(uint8_t *pInBuff) +{ + //uServiceRequestId is the 6'th byte in the serialized packet + return(SocketNtohl(*(uint32_t*)(pInBuff + 6))); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonDeserializeProbePacket + + \Description + Authenticate and read a QosCommonProbePacketT from the provided buffer. + + \Input *pOutPacket - [out] struct to contain the deserailzed probe + \Input *pInBuff - buffer the probe is read from + \Input *pSecureKey1 - secure key used to authenticate the probe data + \Input *pSecureKey2 - optional alternative, secure key used to authenticate the probe data + + \Output + uint32_t - 0=first secure key succeeded, 1=second secure key succeeded, else failure + + \Version 07/06/2017 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t QosCommonDeserializeProbePacket(QosCommonProbePacketT *pOutPacket, uint8_t *pInBuff, uint8_t *pSecureKey1, uint8_t *pSecureKey2) +{ + uint8_t uRet = 0; + +#if QOS_COMMON_ENABLE_HMAC + uint8_t aHmac[QOS_COMMON_HMAC_SIZE]; + + //validate the hmac, which is at the end of the packet + //generate what we think the hmac should be + CryptHmacCalc(aHmac, QOS_COMMON_HMAC_SIZE, pInBuff, QOS_COMMON_SIZEOF_PROBE_DATA, pSecureKey1, QOS_COMMON_SECURE_KEY_LENGTH, QOS_COMMON_HMAC_TYPE); + if (memcmp(aHmac, pInBuff + QOS_COMMON_SIZEOF_PROBE_DATA, QOS_COMMON_HMAC_SIZE) != 0) + { + //first secure key failed, try with the backup secure key if one was provided + if (pSecureKey2 != NULL) + { + CryptHmacCalc(aHmac, QOS_COMMON_HMAC_SIZE, pInBuff, QOS_COMMON_SIZEOF_PROBE_DATA, pSecureKey2, QOS_COMMON_SECURE_KEY_LENGTH, QOS_COMMON_HMAC_TYPE); + if (memcmp(aHmac, pInBuff + QOS_COMMON_SIZEOF_PROBE_DATA, QOS_COMMON_HMAC_SIZE) != 0) + { + return(2); //failed authentication + } + else + { + uRet = 1; //first hmac didn't succeed but second one did + } + } + else + { + return(2); //failed authentication + } + } +#endif + + QosCommonDeserializeProbePacketInsecure(pOutPacket, pInBuff); + return(uRet); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonDeserializeProbePacketInsecure + + \Description + Read a QosCommonProbePacketT from the provided buffer. + + \Input *pOutPacket - [out] struct to contain the deserailzed probe + \Input *pInBuff - buffer the probe is read from + + \Version 07/06/2017 (cvienneau) +*/ +/********************************************************************************F*/ +void QosCommonDeserializeProbePacketInsecure(QosCommonProbePacketT *pOutPacket, uint8_t *pInBuff) +{ + /* + This is the order we are going to deserialize from + uint32_t uProtocol; //!< QOS 2.0 packets will always contain 'qos2' for easy identification. + uint16_t uVersion; //!< uniquely identifies protocol + uint32_t uServiceRequestId; //!< provided by the QosCoordinator, unique to the client doing multiple QOS actions, used to identify which server resources this client is using + uint16_t uClientRequestId; //!< provided by the client, unique to a particular QOS action, used to pair request and responses together + uint32_t uServerReceiveTime; //!< time the server received this probe from the client + uint32_t uServerSendDelta; //!< delta before the server sent this probe response + uint16_t uProbeSizeUp; //!< indicates how big this probe is, including any padding for bandwidth + uint16_t uProbeSizeDown; //!< indicates how big this probe is, including any padding for bandwidth + uint8_t uProbeCountUp; //!< count index of this probe + uint8_t uProbeCountDown; //!< count index of this probe + QosCommonAddrT clientAddressFromService; //!< initially the client address from coordinator, however address from server prospective takes precedence, used to authenticate the packet is coming from the address that generated it + uint8_t aHmac[QOS_COMMON_HMAC_SIZE];//!< when combined with the secure key identifies this packet as coming from a real QOS client + */ + pOutPacket->uProtocol = SocketNtohl(*(uint32_t*)pInBuff); pInBuff += sizeof(uint32_t); //4 + pOutPacket->uVersion = SocketNtohs(*(uint16_t*)pInBuff); pInBuff += sizeof(uint16_t); //2 + pOutPacket->uServiceRequestId = SocketNtohl(*(uint32_t*)pInBuff); pInBuff += sizeof(uint32_t); //4 + pOutPacket->uClientRequestId = SocketNtohs(*(uint16_t*)pInBuff); pInBuff += sizeof(uint16_t); //2 + pOutPacket->uServerReceiveTime = SocketNtohl(*(uint32_t*)pInBuff); pInBuff += sizeof(uint32_t); //4 + pOutPacket->uServerSendDelta = SocketNtohl(*(uint16_t*)pInBuff); pInBuff += sizeof(uint16_t); //2 + pOutPacket->uProbeSizeUp = SocketNtohs(*(uint16_t*)pInBuff); pInBuff += sizeof(uint16_t); //2 + pOutPacket->uProbeSizeDown = SocketNtohs(*(uint16_t*)pInBuff); pInBuff += sizeof(uint16_t); //2 + pOutPacket->uProbeCountUp = *pInBuff; pInBuff += sizeof(uint8_t); //1 + pOutPacket->uProbeCountDown = *pInBuff; pInBuff += sizeof(uint8_t); //1 + pOutPacket->uExpectedProbeCountUp = *pInBuff; pInBuff += sizeof(uint8_t); //1 + pOutPacket->clientAddressFromService.uFamily = SocketNtohs(*(uint16_t*)pInBuff); pInBuff += sizeof(uint16_t); //2 + pOutPacket->clientAddressFromService.uPort = SocketNtohs(*(uint16_t*)pInBuff); pInBuff += sizeof(uint16_t); //2 + pOutPacket->clientAddressFromService.addr.v6.aDwords[0] = SocketNtohl(*(uint32_t*)pInBuff); pInBuff += sizeof(uint32_t); //4 + pOutPacket->clientAddressFromService.addr.v6.aDwords[1] = SocketNtohl(*(uint32_t*)pInBuff); pInBuff += sizeof(uint32_t); //4 + pOutPacket->clientAddressFromService.addr.v6.aDwords[2] = SocketNtohl(*(uint32_t*)pInBuff); pInBuff += sizeof(uint32_t); //4 + pOutPacket->clientAddressFromService.addr.v6.aDwords[3] = SocketNtohl(*(uint32_t*)pInBuff); pInBuff += sizeof(uint32_t); //4 +} + +/*F********************************************************************************/ +/*! + \Function QosCommonMakeVersion + + \Description + Make a version number out of a major and minor version parts + + \Input uMajor - major byte, indicates non-backwards compatible changes + \Input uMinor - minor byte, indicates bug fixes + + \Output + uint16_t - 2 bytes representing a version. + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +uint16_t QosCommonMakeVersion(uint8_t uMajor, uint8_t uMinor) +{ + uint16_t uVersion = (uMajor << 8) + uMinor; + return(uVersion); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonGetVersion + + \Description + Split a version number into major and minor version parts + + \Input uVersion - full version, to be split into major and minor components + \Input *uMajor - [out] major byte, indicates non-backwards compatible changes + \Input *uMinor - [out] minor byte, indicates bug fixes + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +void QosCommonGetVersion(uint16_t uVersion, uint8_t *uMajor, uint8_t *uMinor) +{ + *uMajor = (uVersion >> 8); + *uMinor = (uVersion & 0x00FF); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonIsCompatibleVersion + + \Description + Compare the major portion of two version number to see if they are compatible. + + \Input uVersion1 - first version + \Input uVersion2 - second version + + \Output + uint8_t - TRUE if they are compatible + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t QosCommonIsCompatibleVersion(uint16_t uVersion1, uint16_t uVersion2) +{ + uint8_t uMajor1 = (uVersion1 >> 8); + uint8_t uMajor2 = (uVersion2 >> 8); + if (uMajor1 == uMajor2) + { + return(TRUE); + } + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function QosCommonIsCompatibleProbeVersion + + \Description + Compare if probes of the passed in version are compatible with our version of code. + + \Input uVersion - version of probe + + \Output + uint8_t - TRUE if they are compatible + + \Version 12/09/2016 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t QosCommonIsCompatibleProbeVersion(uint16_t uVersion) +{ + uint8_t uMajor = (uVersion >> 8); + if (uMajor == QOS_COMMON_PROBE_VERSION_MAJOR) + { + return(TRUE); + } + return(FALSE); +} diff --git a/r5dev/thirdparty/dirtysdk/source/misc/userapi.c b/r5dev/thirdparty/dirtysdk/source/misc/userapi.c new file mode 100644 index 00000000..2f2a7c80 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/misc/userapi.c @@ -0,0 +1,1052 @@ +/*H*************************************************************************************************/ +/*! + \File userapi.c + + \Description + Expose first party players' information + + \Copyright + Copyright (c) Electronic Arts 2001-2013 + + \Version 05/10/2013 (mcorcoran) First Version +*/ +/*************************************************************************************************H*/ + + +/*** Include files ********************************************************************************/ + +#include + +#include "DirtySDK/misc/userapi.h" +#include "userapipriv.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +/*** Defines **************************************************************************************/ + +/*** Macros ***************************************************************************************/ + +/*** Type Definitions *****************************************************************************/ + +/*** Function Prototypes **************************************************************************/ + +static void _ClearContext(UserApiRefT *pRef, int32_t iUserIndex); + +/*** Variables ************************************************************************************/ + +/*** Private Functions ****************************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function _UserApiFreeCallback + + \Description + Callback registered with the netconn external cleanup mechanism. It proceeds with destroying + the UserApi instance only when there is no longer an internal async operation in progress + and the memory can safely be freed. + + \Input *pMem - pointer to the UserApi memory buffer + + \Output + int32_t - zero=success; -1=try again; other negative=error + + \Version 09/25/2013 (amakoukji) +*/ +/*************************************************************************************************F*/ +static int32_t _UserApiFreeCallback(void *pMem) +{ + UserApiRefT *pRef = (UserApiRefT*)pMem; + + if ((pRef->pPlatformData != NULL) && (UserApiPlatDestroyData(pRef, pRef->pPlatformData) < 0)) + { + return(-1); + } + + NetCritKill(&pRef->crit); + NetCritKill(&pRef->postCrit); + DirtyMemFree(pRef, USERAPI_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function _ClearContext + + \Description + Clears a user context + + \Input *pRef - pointer to module ref if successful, NULL otherwise. + \Input iUserIndex - user index + + \Version 12/11/2013 (amakoukji) - First version +*/ +/*************************************************************************************************F*/ +void _ClearContext(UserApiRefT *pRef, int32_t iUserIndex) +{ + NetCritEnter(&pRef->crit); + pRef->UserContextList[iUserIndex].iTotalRequested = 0; + pRef->UserContextList[iUserIndex].iTotalReceived = 0; + pRef->UserContextList[iUserIndex].iTotalErrors = 0; + + pRef->UserPresenceList[iUserIndex].iTotalRequested = 0; + pRef->UserPresenceList[iUserIndex].iTotalReceived = 0; + pRef->UserPresenceList[iUserIndex].iTotalErrors = 0; + + pRef->UserRichPresenceList[iUserIndex].iTotalRequested = 0; + pRef->UserRichPresenceList[iUserIndex].iTotalReceived = 0; + pRef->UserRichPresenceList[iUserIndex].iTotalErrors = 0; + + pRef->iLookupUsersLength[iUserIndex] = -1; + pRef->iLookupUserIndex[iUserIndex] = -1; + pRef->iLookupsSent[iUserIndex] = 0; + pRef->bLookupUserAvailable[iUserIndex] = TRUE; + + pRef->bAvailableDataIndex[iUserIndex] = FALSE; + pRef->bAvailableDataIndexPresence[iUserIndex] = FALSE; + pRef->bAvailableDataIndexRichPresence[iUserIndex] = FALSE; + + if (pRef->aLookupUsers[iUserIndex]) + { + DirtyMemFree(pRef->aLookupUsers[iUserIndex], USERAPI_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + pRef->aLookupUsers[iUserIndex] = NULL; + } + NetCritLeave(&pRef->crit); +} + +/*F*************************************************************************************************/ +/*! + \Function _UserApiTriggerCallback + + \Description + Called by update function in the platform specific UserApi modules when a profile + or error is ready. + + \Input *pRef - pointer to UserApiT module reference. + \Input *uUserIndex - The index of the user associated with this profile request. + \Input *eError - A UserApiEventErrorE indicating success for the failure reason. + \Input eType - type of callback + \Input *pUserData - pointer to a populated UserApiUserDataT + + \Output + void + + \Version 05/10/2013 (mcorcoran) - First version + \Version 12/10/2013 (amakoukji) - Second version for UserApi second pass +*/ +/*************************************************************************************************F*/ +void _UserApiTriggerCallback(UserApiRefT *pRef, uint32_t uUserIndex, UserApiEventErrorE eError, UserApiEventTypeE eType, UserApiUserDataT *pUserData) +{ + UserApiEventDataT EventData; + NetCritT *pCrit = &pRef->crit; + + NetCritEnter(pCrit); + + EventData.eError = eError; + EventData.eEventType = eType; + EventData.uUserIndex = (uint32_t)uUserIndex; + ds_memcpy(&EventData.EventDetails.UserData, pUserData, sizeof(UserApiUserDataT)); + + if (eType == USERAPI_EVENT_END_OF_LIST) + { + EventData.EventDetails.EndOfList.iTotalRequested = pRef->UserContextList[uUserIndex].iTotalRequested + pRef->UserPresenceList[uUserIndex].iTotalRequested + pRef->UserRichPresenceList[uUserIndex].iTotalRequested; + EventData.EventDetails.EndOfList.iTotalReceived = pRef->UserContextList[uUserIndex].iTotalReceived + pRef->UserPresenceList[uUserIndex].iTotalReceived + pRef->UserRichPresenceList[uUserIndex].iTotalReceived; + EventData.EventDetails.EndOfList.iTotalErrors = pRef->UserContextList[uUserIndex].iTotalErrors + pRef->UserPresenceList[uUserIndex].iTotalErrors + pRef->UserRichPresenceList[uUserIndex].iTotalErrors; + + if (EventData.EventDetails.EndOfList.iTotalErrors > 0) + { + EventData.eError = USERAPI_ERROR_REQUEST_FAILED; + } + } + + // the actual callback + if (pRef->pUserCallback[uUserIndex] != NULL) + { + pRef->pUserCallback[uUserIndex](pRef, &EventData, pRef->pUserData[uUserIndex]); + } + + NetCritLeave(pCrit); +} + + +/*** Public Functions *****************************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function UserApiCreate + + \Description + Starts UserApi. + + \Output + UserApiRefT* - pointer to module ref if successful, NULL otherwise. + + \Version 05/10/2013 (mcorcoran) - First version +*/ +/*************************************************************************************************F*/ +UserApiRefT * UserApiCreate(void) +{ + UserApiRefT *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + uint32_t iIndex; + + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pRef = (UserApiRefT*)DirtyMemAlloc(sizeof(*pRef), USERAPI_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("userapi: [%p] failed to allocate module state.\n", pRef)); + return(NULL); + } + ds_memclr(pRef, sizeof(*pRef)); + pRef->iMemGroup = iMemGroup; + pRef->pMemGroupUserData = pMemGroupUserData; + + pRef->bShuttingDown = FALSE; + + for (iIndex = 0; iIndex < NETCONN_MAXLOCALUSERS; ++iIndex) + { + pRef->iLookupUsersLength[iIndex] = -1; + pRef->iLookupUserIndex[iIndex] = -1; + pRef->bLookupUserAvailable[iIndex] = TRUE; + pRef->aLookupUsers[iIndex] = NULL; + + pRef->pUserCallback[iIndex] = NULL; + pRef->pPostCallback[iIndex] = NULL; + pRef->pUserData[iIndex] = NULL; + pRef->pUserDataPost[iIndex] = NULL; + pRef->uUserDataMask[iIndex] = 0; + pRef->bAvailableDataIndex[iIndex] = 0; + pRef->bAvailableDataIndexPresence[iIndex] = 0; + pRef->bAvailableDataIndexRichPresence[iIndex] = 0; + pRef->bAvailableDataIndexRMP[iIndex] = FALSE; + } + + for (iIndex = 0; iIndex < USERAPI_NOTIFY_LIST_MAX_SIZE; ++iIndex) + { + pRef->PresenceNotification[iIndex].pCallback = NULL; + pRef->PresenceNotification[iIndex].pUserData = NULL; + pRef->TitleNotification[iIndex].pCallback = NULL; + pRef->TitleNotification[iIndex].pUserData = NULL; + pRef->RichPresenceNotification[iIndex].pCallback = NULL; + pRef->RichPresenceNotification[iIndex].pUserData = NULL; + pRef->ProfileUpdateNotification[iIndex].pCallback = NULL; + pRef->ProfileUpdateNotification[iIndex].pUserData = NULL; + } + pRef->bPresenceNotificationStarted = FALSE; + pRef->bTitleNotificationStarted = FALSE; + pRef->bRichPresenceNotificationStarted = FALSE; + pRef->bProfileUpdateNotificationStarted = FALSE; + + NetCritInit(&pRef->crit, "UserApi"); + NetCritInit(&pRef->postCrit, "UserApiPost"); + + pRef->pPlatformData = UserApiPlatCreateData(pRef); + if (pRef->pPlatformData == NULL) + { + NetPrintf(("userapi: [%p] failed to create platform data.\n", pRef)); + UserApiDestroy(pRef); + return(NULL); + } + + // return the module ref + return(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiDestroy + + \Description + Starts shutting down module. Module will not accept new requests, and will abort ongoing ones. Also, + registered UserApiCallbackT callback will not be called even if there is data available for processing. + + \Input *pRef - pointer to UserApiT module reference + + \Output + int32_t - 0 if successful. Otherwise -1 + + \Version 05/10/2013 (mcorcoran) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiDestroy(UserApiRefT *pRef) +{ + int32_t iIndex = 0; + pRef->bShuttingDown = TRUE; + NetCritEnter(&pRef->crit); + + for (; iIndex < NETCONN_MAXLOCALUSERS; ++iIndex) + { + pRef->bAvailableDataIndex[iIndex] = 0; + pRef->bAvailableDataIndexPresence[iIndex] = 0; + pRef->bAvailableDataIndexRichPresence[iIndex] = 0; + pRef->bAvailableDataIndexRMP[iIndex] = FALSE; + } + + NetCritLeave(&pRef->crit); + + if (_UserApiFreeCallback((void*)pRef) < 0) + { + NetPrintf(("userapi: [%p] destroy deferred to netconn due to pending async operation.\n", pRef)); + NetConnControl('recu', 0, 0, (void *)_UserApiFreeCallback, pRef); + } + + // $todo: change return type to void and remove the return statement + return(0); +} + +/*F********************************************************************************/ +/*! + \Function UserApiStatus + + \Description + Get status information. + + \Input *pRef - pointer to module state + \Input iSelect - status selector + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - Return 0 if successful. Otherwise a selector specific error + + \Notes + There is currently nothing to query with this module. This is a placeholder for future implementations. + + \verbatim + \endverbatim + + \Version 05/10/2013 (mcorcoran) - First version +*/ +/********************************************************************************F*/ +int32_t UserApiStatus(UserApiRefT *pRef, int32_t iSelect, void *pBuf, int32_t iBufSize) +{ + NetPrintf(("userapi: unhandled status selector '%C'\n", iSelect)); + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function UserApiControl + + \Description + Control behavior of module. + + \Input *pRef - pointer to module state + \Input iControl - status selector + \Input iValue - control value + \Input iValue2 - control value + \Input *pValue - control value + + \Output + int32_t - Return 0 if successful. Otherwise a selector specific error + + \Notes + iControl can be one of the following: + + \verbatim + 'abrt' - Abort the current request associated with the user at index iValue + 'avsz' - (PS4 only) Set which avatar size will be retrieved. iValue = 's' for small, 'm' for medium and 'l' for big. 's' is the default value, and this is just functional for PS4. + \endverbatim + + \Version 05/10/2013 (mcorcoran) - First version +*/ +/********************************************************************************F*/ +int32_t UserApiControl(UserApiRefT *pRef, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'abrt') + { + if ((iValue < 0) || (iValue >= NETCONN_MAXLOCALUSERS)) + { + NetPrintf(("userapi: [%p] iValue(%d) is not a valid user index\n", pRef, iValue)); + return(-1); + } + + pRef->pUserCallback[iValue] = NULL; + pRef->pUserData[iValue] = NULL; + + UserApiPlatAbortRequests(pRef, iValue); + + pRef->UserContextList[iValue].iTotalRequested = 0; + pRef->UserContextList[iValue].iTotalReceived = 0; + pRef->UserContextList[iValue].iTotalErrors = 0; + + pRef->iLookupUsersLength[iValue] = -1; + pRef->iLookupUserIndex[iValue] = -1; + pRef->bLookupUserAvailable[iValue] = TRUE; + if (pRef->aLookupUsers[iValue]) + { + DirtyMemFree(pRef->aLookupUsers[iValue], USERAPI_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + pRef->aLookupUsers[iValue] = NULL; + } + + return(0); + } + else + { + int32_t iRet; + if ((iRet = UserApiPlatControl(pRef, iControl, iValue, iValue2, pValue)) < 0) + { + NetPrintf(("userapi: unhandled control selector '%C'\n", iControl)); + } + + return(iRet); + } +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiRequestProfilesAsync + + \Description + Starts the process to retrieve user information of players. pLookupUsers is a pointer to the first DirtyUserT, and iLookupUsersLength is the number of DirtyUserTs. + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - Index used to specify which local user owns the request + \Input *pLookupUsers - Pointer to first DityUserT in an array of iLookupUsersLength elements + \Input iLookupUsersLength - Number of elements in the pLookupUsers array + \Input *pCallback - Callback that is going to be called when responses for these requests are received + \Input *pUserData - This pointer is going to be passed to the callback when it is called. This parameter can be NULL + + \Output + int32_t - Return 0 if successful, -1 otherwise. + + \Version 05/10/2013 (mcorcoran) - First version + \Version 09/12/2013 (amakoukji) - Second pass with presence +*/ +/*************************************************************************************************F*/ +int32_t UserApiRequestProfilesAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUsers, int32_t iLookupUsersLength, UserApiCallbackT *pCallback, void *pUserData) +{ + int32_t iRet; + + if (pLookupUsers == NULL || iLookupUsersLength <= 0) + { + NetPrintf(("userapi: [%p] invalid pointer to DirtyUserT list\n", pRef)); + return(-1); + } + + if (pCallback == NULL) + { + NetPrintf(("userapi: [%p] invalid pointer to callback\n", pRef)); + return(-3); + } + + NetCritEnter(&pRef->crit); + + if (pRef->UserContextList[uUserIndex].iTotalRequested > 0 || pRef->UserPresenceList[uUserIndex].iTotalRequested > 0) + { + NetPrintf(("userapi: [%p] module is already handling a request for user (%u)\n", pRef, uUserIndex)); + NetCritLeave(&pRef->crit); + return(-2); + } + + pRef->UserContextList[uUserIndex].iTotalRequested = iLookupUsersLength; + pRef->UserContextList[uUserIndex].iTotalReceived = 0; + pRef->UserContextList[uUserIndex].iTotalErrors = 0; + pRef->pUserCallback[uUserIndex] = pCallback; + pRef->pPostCallback[uUserIndex] = NULL; + pRef->pUserData[uUserIndex] = pUserData; + pRef->uUserDataMask[uUserIndex] = USERAPI_MASK_PROFILES; // Mark as batch + + iRet = UserApiPlatRequestProfile(pRef, uUserIndex, pLookupUsers, iLookupUsersLength); + + // clear the context if the request was not successful + if (iRet < 0) + { + pRef->UserContextList[uUserIndex].iTotalRequested = 0; + pRef->UserContextList[uUserIndex].iTotalReceived = 0; + pRef->UserContextList[uUserIndex].iTotalErrors = 0; + pRef->pUserCallback[uUserIndex] = NULL; + pRef->pPostCallback[uUserIndex] = NULL; + pRef->pUserData[uUserIndex] = NULL; + pRef->uUserDataMask[uUserIndex] = 0; + } + + NetCritLeave(&pRef->crit); + + return(iRet); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiRequestProfileAsync + + \Description + Starts the process to retrieve profile information of players. pLookupUser is a pointer to DirtyUserT. + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - Index used to specify which local user owns the request + \Input *pLookupUser - Pointer to DityUserT + \Input *pCallback - Callback that is going to be called when responses for these requests are received + \Input uUserDataMask - A mask value defining which elements are needed; USERAPI_MASK_* + \Input *pUserData - This pointer is going to be passed to the callback when it is called. This parameter can be NULL + + \Output + int32_t - Return 0 if successful, -1 otherwise. + + \Version 09/12/2013 (amakoukji) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiRequestProfileAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUser, UserApiCallbackT *pCallback, uint32_t uUserDataMask, void *pUserData) +{ + return(UserApiRequestPresenceAsync(pRef, uUserIndex, pLookupUser, pCallback, uUserDataMask, pUserData)); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiRequestPresenceAsync + + \Description + Starts the process to retrieve profile information of players. pLookupUser is a pointer to DirtyUserT. + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - Index used to specify which local user owns the request + \Input *pLookupUser - Pointer to DityUserT + \Input *pCallback - Callback that is going to be called when responses for these requests are received + \Input uUserDataMask - A mask value defining which elements are needed; USERAPI_MASK_* + \Input *pUserData - This pointer is going to be passed to the callback when it is called. This parameter can be NULL + + \Output + int32_t - Return 0 if successful, -1 otherwise. + + \Version 09/12/2013 (amakoukji) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiRequestPresenceAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUser, UserApiCallbackT *pCallback, uint32_t uUserDataMask, void *pUserData) +{ + // Start 2 seperate queries, one for a single profile and another for presence / rich presence if necessary + int32_t iReturn = 0; + if (uUserDataMask == 0) + { + // nothing to do + NetPrintf(("userapi: [%p] query without a mask value submitted (user %u)\n", pRef, uUserIndex)); + return(-4); + } + + if (pLookupUser == NULL) + { + NetPrintf(("userapi: [%p] invalid pointer to DirtyUserT list\n", pRef)); + return(-5); + } + + if (pCallback == NULL) + { + NetPrintf(("userapi: [%p] invalid pointer to callback\n", pRef)); + return(-3); + } + + + NetCritEnter(&pRef->crit); + + if (pRef->UserContextList[uUserIndex].iTotalRequested > 0 || pRef->UserPresenceList[uUserIndex].iTotalRequested > 0) + { + NetPrintf(("userapi: [%p] module is already handling a request for user (%u)\n", pRef, uUserIndex)); + NetCritLeave(&pRef->crit); + return(-2); + } + + pRef->pUserCallback[uUserIndex] = pCallback; + pRef->pPostCallback[uUserIndex] = NULL; + pRef->pUserData[uUserIndex] = pUserData; + pRef->uUserDataMask[uUserIndex] = uUserDataMask; + + if ( uUserDataMask & USERAPI_MASK_PROFILE) + { + pRef->UserContextList[uUserIndex].iTotalRequested = 1; + pRef->UserContextList[uUserIndex].iTotalReceived = 0; + pRef->UserContextList[uUserIndex].iTotalErrors = 0; + + if ((iReturn = UserApiPlatRequestProfile(pRef, uUserIndex, pLookupUser, 1)) < 0) + { + _ClearContext(pRef, (int32_t)uUserIndex); + NetCritLeave(&pRef->crit); + return(iReturn); + } + } + + if ( (uUserDataMask & USERAPI_MASK_PRESENCE)) + { + pRef->UserPresenceList[uUserIndex].iTotalRequested = 1; + pRef->UserPresenceList[uUserIndex].iTotalReceived = 0; + pRef->UserPresenceList[uUserIndex].iTotalErrors = 0; + + if ((iReturn = UserApiPlatRequestPresence(pRef, uUserIndex, pLookupUser)) < 0) + { + _ClearContext(pRef, (int32_t)uUserIndex); + NetCritLeave(&pRef->crit); + return(iReturn); + } + } + + if ((uUserDataMask & USERAPI_MASK_RICH_PRESENCE)) + { + pRef->UserRichPresenceList[uUserIndex].iTotalRequested = 1; + pRef->UserRichPresenceList[uUserIndex].iTotalReceived = 0; + pRef->UserRichPresenceList[uUserIndex].iTotalErrors = 0; + + if ((iReturn = UserApiPlatRequestRichPresence(pRef, uUserIndex, pLookupUser)) < 0) + { + _ClearContext(pRef, (int32_t)uUserIndex); + NetCritLeave(&pRef->crit); + return(iReturn); + } + } + + NetCritLeave(&pRef->crit); + + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiRequestRichPresenceAsync + + \Description + Starts the process to retrieve rich presence information of players. + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - Index used to specify which local user owns the request + \Input *pLookupUser - Pointer to DirtyUserT + \Input *pCallback - Callback that is going to be called when responses for these requests are received + \Input uUserDataMask - A mask value defining which elements are needed; USERAPI_MASK_* + \Input *pUserData - This pointer is going to be passed to the callback when it is called. This parameter can be NULL + + \Output + int32_t - Return 0 if successful, -1 otherwise. + + \Notes + For PS4: + strData contains GameStatus + pData == NULL means delete the rich presence + For XB1: + strData contains the Rich Presence String as defined in your Service Config Workbook + More complex rich presence requests for XB1 will need to be set through their API directly + + \Version 09/12/2013 (amakoukji) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiRequestRichPresenceAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUser, UserApiCallbackT *pCallback, uint32_t uUserDataMask, void *pUserData) +{ + return(UserApiRequestProfileAsync(pRef, uUserIndex, pLookupUser, pCallback, uUserDataMask , pUserData)); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiPostRecentlyMetAsync + + \Description + Starts the process to post that a certain player was recently encountered. + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - Index used to specify which local user owns the request + \Input *pPlayerMet - Pointer to the DityUserT to add to the recently met players list + \Input *pAdditionalInfo - Pointer to additional info container which a platform may require + \Input *pCallback - Callback that is going to be called when responses for these requests are received + \Input *pUserData - This pointer is going to be passed to the callback when it is called. This parameter can be NULL + + \Output + int32_t - Return 0 if successful, -1 otherwise. + + \Version 06/16/2013 (amakoukji) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiPostRecentlyMetAsync(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pPlayerMet, void *pAdditionalInfo, UserApiPostCallbackT *pCallback, void *pUserData) +{ + int32_t result = 0; + if (pRef == NULL) + { + NetPrintf(("userapi: UserApiRecentlyMetAsync() invalid reference pointer\n")); + return(-1); + } + + if (uUserIndex >= NETCONN_MAXLOCALUSERS) + { + NetPrintf(("userapi: [%p] module, invalid user index (%d)\n", pRef, uUserIndex)); + return(-1); + } + + if (pPlayerMet == NULL) + { + NetPrintf(("userapi: [%p] module, UserApiRecentlyMetAsync() invalid DirtyUserT pointer [%p]\n", pRef, pPlayerMet)); + return(-1); + } + + NetCritEnter(&pRef->postCrit); + + if (pRef->UserRmpList[uUserIndex].iTotalRequested > 0) + { + NetPrintf(("userapi: [%p] module is already handling a recently met player request for user (%d)\n", pRef, uUserIndex)); + NetCritLeave(&pRef->postCrit); + return(-2); + } + + pRef->UserRmpList[uUserIndex].iTotalRequested = 1; + pRef->UserRmpList[uUserIndex].iTotalReceived = 0; + pRef->UserRmpList[uUserIndex].iTotalErrors = 0; + pRef->pPostCallback[uUserIndex] = pCallback; + pRef->pUserDataPost[uUserIndex] = pUserData; + + result = UserApiPlatRequestRecentlyMet(pRef, (int32_t)uUserIndex, pPlayerMet, pAdditionalInfo); + + if (result < 0) + { + // An error occured, reset + pRef->UserRmpList[uUserIndex].iTotalRequested = 0; + pRef->UserRmpList[uUserIndex].iTotalReceived = 0; + pRef->UserRmpList[uUserIndex].iTotalErrors = 0; + pRef->pPostCallback[uUserIndex] = NULL; + pRef->pUserDataPost[uUserIndex] = NULL; + } + + NetCritLeave(&pRef->postCrit); + + return(result); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiPostRichPresenceAsync + + \Description + Starts the process to post that a certain player was recently encountered. + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - Index used to specify which local user owns the request + \Input *pData - Rich Presence to post + \Input *pCallback - Callback that is going to be called when responses for these requests are received + \Input *pUserData - This pointer is going to be passed to the callback when it is called. This parameter can be NULL + + \Output + int32_t - Return 0 if successful, -1 otherwise. + + \Version 06/16/2013 (amakoukji) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiPostRichPresenceAsync(UserApiRefT *pRef, uint32_t uUserIndex, UserApiRichPresenceT *pData, UserApiPostCallbackT *pCallback, void *pUserData) +{ + int32_t result = 0; + if (pRef == NULL) + { + NetPrintf(("userapi: UserApiPostRichPresenceAsync() invalid reference pointer\n")); + return(-1); + } + + if (uUserIndex >= NETCONN_MAXLOCALUSERS) + { + NetPrintf(("userapi: [%p] module, invalid user index (%d)\n", pRef, uUserIndex)); + return(-2); + } + + NetCritEnter(&pRef->postCrit); + + if (pRef->UserRmpList[uUserIndex].iTotalRequested > 0) + { + NetPrintf(("userapi: [%p] module is already handling a recently met player request for user (%d)\n", pRef, uUserIndex)); + NetCritLeave(&pRef->postCrit); + return(-2); + } + + pRef->UserRmpList[uUserIndex].iTotalRequested = 1; + pRef->UserRmpList[uUserIndex].iTotalReceived = 0; + pRef->UserRmpList[uUserIndex].iTotalErrors = 0; + pRef->pPostCallback[uUserIndex] = pCallback; + pRef->pUserDataPost[uUserIndex] = pUserData; + + result = UserApiPlatRequestPostRichPresence(pRef, (int32_t)uUserIndex, pData); + + NetCritLeave(&pRef->postCrit); + + return(result); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiRegisterUpdateEvent + + \Description + Register for notifications from first part + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - Index used to specify which local user owns the request + \Input eType - type of event to register for + \Input *pNotifyCb - Callback that is going to be called when responses for these requests are received + \Input *pUserData - This pointer is going to be passed to the callback when it is called. This parameter can be NULL + + \Output + int32_t - Return 0 if successful, -1 otherwise. + + \Version 06/16/2013 (amakoukji) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiRegisterUpdateEvent(UserApiRefT *pRef, uint32_t uUserIndex, UserApiNotifyTypeE eType, UserApiUpdateCallbackT *pNotifyCb, void *pUserData) +{ + int32_t iReturn = USERAPI_ERROR_OK; + UserApiNotificationT (*pList)[USERAPI_NOTIFY_LIST_MAX_SIZE] = NULL; + uint8_t bNeedsInit = TRUE; + int32_t i = 0; + + if (eType == USERAPI_NOTIFY_PRESENCE_UPDATE) + { + pList = &pRef->PresenceNotification; + if (pRef->bPresenceNotificationStarted) + { + bNeedsInit = FALSE; + } + pRef->bPresenceNotificationStarted = TRUE; + } + else if (eType == USERAPI_NOTIFY_TITLE_UPDATE) + { + pList = &pRef->TitleNotification; + if (pRef->bTitleNotificationStarted) + { + bNeedsInit = FALSE; + } + pRef->bTitleNotificationStarted = TRUE; + } + else if (eType == USERAPI_NOTIFY_RICH_PRESENCE_UPDATE) + { + pList = &pRef->RichPresenceNotification; + if (pRef->bRichPresenceNotificationStarted) + { + bNeedsInit = FALSE; + } + pRef->bRichPresenceNotificationStarted = TRUE; + } + else if (eType == USERAPI_NOTIFY_PROFILE_UPDATE) + { + pList = &pRef->ProfileUpdateNotification; + if (pRef->bProfileUpdateNotificationStarted) + { + bNeedsInit = FALSE; + } + pRef->bProfileUpdateNotificationStarted = TRUE; + } + + else + { + return(USERAPI_ERROR_UNSUPPORTED); + } + + if (pList == NULL) + { + return(USERAPI_ERROR_FULL); + } + + if (bNeedsInit) + { + iReturn = UserApiPlatRegisterUpdateEvent(pRef, uUserIndex, eType); + } + + if (iReturn >= 0) + { + for (i = 0; i < USERAPI_NOTIFY_LIST_MAX_SIZE; ++i) + { + if ((*pList)[i].pCallback == pNotifyCb && (*pList)[i].pUserData == pUserData && (*pList)[i].uUserIndex == uUserIndex) + { + // callback already setup + break; + } + + if ((*pList)[i].pCallback == NULL) + { + (*pList)[i].pCallback = pNotifyCb; + (*pList)[i].pUserData = pUserData; + (*pList)[i].uUserIndex = uUserIndex; + break; + } + } + } + + return(iReturn); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiCancel + + \Description + Cancel all queries to the 1st party. + + \Input *pRef - pointer to UserApiT module reference + \Input uUserIndex - index of user associated with this request + + \Output + int32_t - result of abort request; see UserApiPlatAbortRequests() + + \Version 05/10/2013 (mcorcoran) - First version +*/ +/*************************************************************************************************F*/ +int32_t UserApiCancel(UserApiRefT *pRef, uint32_t uUserIndex) +{ + _ClearContext(pRef, (int32_t)uUserIndex); + pRef->pUserCallback[uUserIndex] = NULL; + pRef->pPostCallback[uUserIndex] = NULL; + pRef->pUserDataPost[uUserIndex] = NULL; + return(UserApiPlatAbortRequests(pRef, (int32_t)uUserIndex)); +} + +/*F*************************************************************************************************/ +/*! + \Function UserApiUpdate + + \Description + Update the internal state of the module, and call registered UserApiCallbackT callback if there are GamerCard/Profile responses available. This function should be called periodically. + + \Input *pRef - pointer to UserApiT module reference + + \Version 05/10/2013 (mcorcoran) - First version +*/ +/*************************************************************************************************F*/ +void UserApiUpdate(UserApiRefT *pRef) +{ + UserApiUserDataT UserData; + UserApiProfileT *pProfileData = &UserData.Profile; + UserApiPresenceT *pPresenceData = &UserData.Presence; + UserApiRichPresenceT *pRichPresenceData = &UserData.RichPresence; + int32_t iProcessingResult = 0; + int32_t i = 0; + int32_t iInnerLoop = 0; + + if (pRef == NULL) + { + NetPrintf(("userapi: invalid reference pointer\n")); + return; + } + + UserApiPlatUpdate(pRef); + + for (i = 0; i < NETCONN_MAXLOCALUSERS; ++i) + { + NetCritEnter(&pRef->crit); + + ds_memclr(&UserData, sizeof(UserData)); + UserData.uUserDataMask = pRef->uUserDataMask[i]; + + // Check for batch profile fetch. + // This is treated seperately because it never needs to wait for other 1st party requests to finish + if (pRef->uUserDataMask[i] == USERAPI_MASK_PROFILES) + { + if (pRef->bAvailableDataIndex[i] > 0) + { + iProcessingResult = _UserApiProcessProfileResponse(pRef, i, TRUE, pProfileData, &UserData); + + // If the processing failed report it and cancel all further 1st party requests + if (iProcessingResult < 0) + { + _UserApiTriggerCallback(pRef, i, iProcessingResult, USERAPI_EVENT_END_OF_LIST, &UserData); + _ClearContext(pRef, i); + } + // If all the requested results have been accounted for send the "list end" callback + else if (pRef->UserContextList[i].iTotalRequested == (pRef->UserContextList[i].iTotalReceived + pRef->UserContextList[i].iTotalErrors)) + { + _UserApiTriggerCallback(pRef, i, USERAPI_ERROR_OK, USERAPI_EVENT_END_OF_LIST, &UserData); + _ClearContext(pRef, i); + } + // If all requests are not complete for the user, mark the lookup available as true in order to continue + else + { + pRef->bLookupUserAvailable[i] = TRUE; + } + + pRef->bAvailableDataIndex[i] = FALSE; + pRef->bAvailableDataIndexPresence[i] = FALSE; + pRef->bAvailableDataIndexRichPresence[i] = FALSE; + } + } + + // then check individual requests which may need to wait for several 1st party queries to finish + else if ((pRef->uUserDataMask[i] & USERAPI_MASK_PROFILE) || (pRef->uUserDataMask[i] & USERAPI_MASK_PRESENCE) || (pRef->uUserDataMask[i] & USERAPI_MASK_RICH_PRESENCE)) + { + if ( ((pRef->bAvailableDataIndex[i] > 0 && (pRef->uUserDataMask[i] & USERAPI_MASK_PROFILE)) || !(pRef->uUserDataMask[i] & USERAPI_MASK_PROFILE)) + && ((pRef->bAvailableDataIndexPresence[i] > 0 && (pRef->uUserDataMask[i] & USERAPI_MASK_PRESENCE)) || !(pRef->uUserDataMask[i] & USERAPI_MASK_PRESENCE)) + && ((pRef->bAvailableDataIndexRichPresence[i] > 0 && (pRef->uUserDataMask[i] & USERAPI_MASK_RICH_PRESENCE)) || !(pRef->uUserDataMask[i] & USERAPI_MASK_RICH_PRESENCE)) ) + { + // At this point all 1st party queries are complete + // Parse the data, report it to the user and reset + iProcessingResult = 0; + + if (pRef->uUserDataMask[i] & USERAPI_MASK_PROFILE) + { + iProcessingResult = _UserApiProcessProfileResponse(pRef, i, FALSE, pProfileData, &UserData); + } + + if (pRef->uUserDataMask[i] & USERAPI_MASK_PRESENCE && iProcessingResult >= 0) + { + iProcessingResult = _UserApiProcessPresenceResponse(pRef, i, pPresenceData, &UserData); + } + + if (pRef->uUserDataMask[i] & USERAPI_MASK_RICH_PRESENCE && iProcessingResult >= 0) + { + iProcessingResult = _UserApiProcessRichPresenceResponse(pRef, i, pRichPresenceData, &UserData); + } + + // Callback + _UserApiTriggerCallback(pRef, i, (iProcessingResult >= 0) ? USERAPI_ERROR_OK : iProcessingResult, + (iProcessingResult >= 0) ? USERAPI_EVENT_DATA : USERAPI_EVENT_END_OF_LIST, &UserData); + + // Clean up + _ClearContext(pRef, i); + pRef->bAvailableDataIndex[i] = FALSE; + pRef->bAvailableDataIndexPresence[i] = FALSE; + pRef->bAvailableDataIndexRichPresence[i] = FALSE; + } + } + + NetCritLeave(&pRef->crit); + } + + // Handle Posted responses + NetCritEnter(&pRef->postCrit); + for (i = 0; i < NETCONN_MAXLOCALUSERS; ++i) + { + + if (pRef->bAvailableDataIndexRMP[i] != FALSE) + { + _UserApiTriggerPostCallback(pRef, i); + + // Clear context + pRef->UserRmpList[i].iTotalRequested = 0; + pRef->UserRmpList[i].iTotalReceived = 0; + pRef->UserRmpList[i].iTotalErrors = 0; + pRef->bAvailableDataIndexRMP[i] = FALSE; + } + } + + // finally process notifications from Sony + if (pRef->UserApiNotifyEvent[0].pNotificationData != NULL) + { + int32_t iOuterLoop = 0; + // at least one notification is in the list, do processing + for (; iOuterLoop < USERAPI_MAX_QUEUED_NOTIFICATIONS; ++iOuterLoop) + { + if (pRef->UserApiNotifyEvent[iOuterLoop].pNotificationData != NULL) + { + uint32_t uNotficationUserId = pRef->UserApiNotifyEvent[iOuterLoop].uUserIndex; + // dispatch notifications + for (iInnerLoop = 0; iInnerLoop < USERAPI_NOTIFY_LIST_MAX_SIZE; ++iInnerLoop) + { + if ((*(pRef->UserApiNotifyEvent[iOuterLoop].pNotificationList))[iInnerLoop].pCallback != NULL) + { + // if the notification is for the requestor of the callback + if (uNotficationUserId == (*(pRef->UserApiNotifyEvent[iOuterLoop].pNotificationList))[iInnerLoop].uUserIndex) + { + (*(pRef->UserApiNotifyEvent[iOuterLoop].pNotificationList))[iInnerLoop].pCallback(pRef, + pRef->UserApiNotifyEvent[iOuterLoop].pNotificationType, + pRef->UserApiNotifyEvent[iOuterLoop].pNotificationData, + (*(pRef->UserApiNotifyEvent[iOuterLoop].pNotificationList))[iInnerLoop].pUserData); + } + } + } + + // clean up + DirtyMemFree(pRef->UserApiNotifyEvent[iOuterLoop].pNotificationData, USERLISTAPI_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + pRef->UserApiNotifyEvent[iOuterLoop].pNotificationData = NULL; + pRef->UserApiNotifyEvent[iOuterLoop].pNotificationList = NULL; + } + } + } + + NetCritLeave(&pRef->postCrit); + +} diff --git a/r5dev/thirdparty/dirtysdk/source/misc/userapipriv.h b/r5dev/thirdparty/dirtysdk/source/misc/userapipriv.h new file mode 100644 index 00000000..a59b2635 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/misc/userapipriv.h @@ -0,0 +1,149 @@ +/*H*************************************************************************************************/ +/*! + + \File userapipriv.h + + \Description + Expose first party player information + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2001-2013. ALL RIGHTS RESERVED. + + \Version 05/10/2013 (mcorcoran) First Version + +*/ +/*************************************************************************************************H*/ + +#ifndef _userapipriv_h +#define _userapipriv_h + +/*** Include files ********************************************************************************/ + +#include "DirtySDK/misc/userapi.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/netconn.h" + +/*** Defines **************************************************************************************/ + +#define USERAPI_MAX_QUEUED_NOTIFICATIONS (100) + +/*** Macros ***************************************************************************************/ + +/*** Type Definitions *****************************************************************************/ + +typedef struct UserApiUserContextT +{ + int32_t iTotalRequested; //!< the total number of user profiles that are being looked up + int32_t iTotalReceived; //!< the number of user profiles received + int32_t iTotalErrors; //!< the number erros that have occured +} UserApiUserContextT; + +typedef struct UserApiUserContextRMPT +{ + int32_t iTotalRequested; //!< the total number of user profiles that are being looked up + int32_t iTotalReceived; //!< the number of user profiles received + int32_t iTotalErrors; //!< the number erros that have occured + UserApiPostCallbackT *pUserCallback; //!< callback to user code that will be called when data is available during a call to UserApiUpdate() + void *pUserData; //!< user data for the callback +} UserApiUserContextRMPT; + +typedef struct UserApiNotificationT +{ + UserApiUpdateCallbackT *pCallback; //!< function address + void *pUserData; //!< user data to return + uint32_t uUserIndex; //!< user index of the requester +} UserApiNotificationT; + +typedef struct UserApiNotifyEventT +{ + UserApiNotifyDataT *pNotificationData; + UserApiNotificationT (*pNotificationList)[]; + UserApiNotifyTypeE pNotificationType; + uint32_t uUserIndex; +} UserApiNotifyEventT; + +typedef struct UserApiPlatformDataT UserApiPlatformDataT; + +struct UserApiRefT +{ + int32_t iMemGroup; //!< dirtymem memory group + void *pMemGroupUserData; //!< dirtymem memory group user data + NetCritT crit; //!< sychronize shared data between the threads for profiles + NetCritT postCrit; //!< sychronize shared data between the threads for POSTing data + uint8_t bShuttingDown; + + UserApiUserContextT UserContextList[NETCONN_MAXLOCALUSERS]; //!< per local user data for profile requests + UserApiUserContextT UserPresenceList[NETCONN_MAXLOCALUSERS]; //!< per local user data for presence requests + UserApiUserContextT UserRichPresenceList[NETCONN_MAXLOCALUSERS]; //!< per local user data for presence requests + UserApiUserContextT UserRmpList[NETCONN_MAXLOCALUSERS]; //!< per local user data for recently met player requests + UserApiCallbackT *pUserCallback[NETCONN_MAXLOCALUSERS]; //!< callback to user code that will be called when data is available during a call to UserApiUpdate() + UserApiPostCallbackT *pPostCallback[NETCONN_MAXLOCALUSERS]; //!< callback to user code that will be called when data is available for POSTs + void *pUserData[NETCONN_MAXLOCALUSERS]; //!< user data for the callback + void *pUserDataPost[NETCONN_MAXLOCALUSERS]; //!< user data for the callback for POSTs + uint32_t uUserDataMask[NETCONN_MAXLOCALUSERS]; //!> request mask + + volatile uint8_t bAvailableDataIndex[NETCONN_MAXLOCALUSERS]; //!< mask denoting which user has data waiting to pick up + volatile uint8_t bAvailableDataIndexPresence[NETCONN_MAXLOCALUSERS]; //!< mask denoting which user has data waiting to pick up + volatile uint8_t bAvailableDataIndexRichPresence[NETCONN_MAXLOCALUSERS]; //!< mask denoting which user has data waiting to pick up + volatile uint8_t bAvailableDataIndexRMP[NETCONN_MAXLOCALUSERS]; //!< mask denoting which user has data waiting to pick up + UserApiPlatformDataT *pPlatformData; + + DirtyUserT *aLookupUsers[NETCONN_MAXLOCALUSERS]; + int32_t iLookupUsersLength[NETCONN_MAXLOCALUSERS]; + int32_t iLookupUserIndex[NETCONN_MAXLOCALUSERS]; + uint8_t bLookupUserAvailable[NETCONN_MAXLOCALUSERS]; + int32_t iLookupsSent[NETCONN_MAXLOCALUSERS]; + uint8_t bLookupRmpAvailable[NETCONN_MAXLOCALUSERS]; + + // Callbacks + UserApiNotificationT PresenceNotification[USERAPI_NOTIFY_LIST_MAX_SIZE]; + UserApiNotificationT TitleNotification[USERAPI_NOTIFY_LIST_MAX_SIZE]; + UserApiNotificationT RichPresenceNotification[USERAPI_NOTIFY_LIST_MAX_SIZE]; + UserApiNotificationT ProfileUpdateNotification[USERAPI_NOTIFY_LIST_MAX_SIZE]; + uint8_t bPresenceNotificationStarted; + uint8_t bTitleNotificationStarted; + uint8_t bRichPresenceNotificationStarted; + uint8_t bProfileUpdateNotificationStarted; + UserApiNotifyEventT UserApiNotifyEvent[USERAPI_MAX_QUEUED_NOTIFICATIONS]; +}; + +/*** Function Prototypes **************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +UserApiPlatformDataT *UserApiPlatCreateData(UserApiRefT *pRef); +int32_t UserApiPlatDestroyData(UserApiRefT *pRef, UserApiPlatformDataT *pPlatformData); +int32_t UserApiPlatRequestProfile(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUsers, int32_t iLookupUsersLength); +int32_t UserApiPlatAbortRequests(UserApiRefT *pRef, uint32_t uUserIndex); +int32_t UserApiPlatRequestPresence(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUsers); +int32_t UserApiPlatRequestRichPresence(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pLookupUser); +int32_t UserApiPlatRequestRecentlyMet(UserApiRefT *pRef, uint32_t uUserIndex, DirtyUserT *pPlayerMet, void *pAdditionalInfo); +int32_t UserApiPlatRequestPostRichPresence(UserApiRefT *pRef, uint32_t uUserIndex, UserApiRichPresenceT *pData); + +int32_t _UserApiPlatAbortPostRequests(UserApiRefT *pRef, uint32_t uUserIndex); + +int32_t _UserApiProcessProfileResponse(UserApiRefT *pRef, int32_t uUserIndex, uint8_t bBatch, UserApiProfileT *ProfileData, UserApiUserDataT *pUserData); +int32_t _UserApiProcessPresenceResponse(UserApiRefT *pRef, int32_t uUserIndex, UserApiPresenceT *pPresenceData, UserApiUserDataT *pUserData); +int32_t _UserApiProcessRichPresenceResponse(UserApiRefT *pRef, int32_t uUserIndex, UserApiRichPresenceT *pRichPresenceData, UserApiUserDataT *pUserData); +int32_t _UserApiProcessRmpResponse(UserApiRefT *pRef, uint32_t uUserIndex); + +void _UserApiTriggerCallback(UserApiRefT *pRef, uint32_t uUserIndex, UserApiEventErrorE eError, UserApiEventTypeE eType, UserApiUserDataT *pUserData); +void _UserApiTriggerPostCallback(UserApiRefT *pRef, uint32_t uUserIndex); + +int32_t UserApiPlatRegisterUpdateEvent(UserApiRefT *pRef, uint32_t uUserIndex, UserApiNotifyTypeE eType); + +//!< Control behavior of module +int32_t UserApiPlatControl(UserApiRefT *pRef, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +int32_t UserApiPlatUpdate(UserApiRefT *pRef); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/r5dev/thirdparty/dirtysdk/source/misc/weblog.c b/r5dev/thirdparty/dirtysdk/source/misc/weblog.c new file mode 100644 index 00000000..4c1199bc --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/misc/weblog.c @@ -0,0 +1,448 @@ +/*H********************************************************************************/ +/*! + \File weblog.c + + \Description + Captures DirtySDK debug output and posts it to a webserver where the output + can be retrieved. This is useful when debugging on a system with no + debugging capability or in a "clean room" environment, for example. Two + basic mechanisms are employed; the first is a NetPrint debug hook to capture + all debug output, the second a stand-alone WebOfferPrintf() function that + can be used more selectively. + + \Notes + A small local buffer is required to store the output before it is submitted + to ProtoHttp for sending. This is to avoid reentrancy issues where debug + output from ProtoHttp or lower-level modules (ProtoSSL, Crypt*, etc) would + put us into an infinite recursion. Instead the text is simply buffered and + flushed at regular intervals by the WebLogUpdate() function. + + \Copyright + Copyright (c) 2008 Electronic Arts Inc. + + \Version 05/06/2008 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/misc/weblog.h" +#include "DirtySDK/proto/protohttp.h" + + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +struct WebLogRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + ProtoHttpRefT *pProtoHttp; //!< http module ref + NetCritT WebCrit; //!< critical section to guard weblog buffer + int32_t iBufLen; //!< string buffer length + char strWebHost[128]; //!< webhost to post to + char strWebUrl[128]; //!< weburl for post + + uint8_t bLogging; //!< TRUE if logging is enabled, else FALSE + uint8_t bPosting; //!< TRUE if in posting state, else FALSE + uint8_t bUpdating; //!< TRUE if in WebLogUpdate(), else FALSE + uint8_t _pad; + + char strText[1]; //!< variable-length string buffer (must come last!) +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _WebLogFlush + + \Description + Flush weblog text to ProtoHttp. + + \Input *pWebLog - module state + + \Output + None. + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +static void _WebLogFlush(WebLogRefT *pWebLog) +{ + int32_t iSendSize = (int32_t)strlen(pWebLog->strText), iSentSize; + + // if nothing to do, don't send + if (iSendSize == 0) + { + return; + } + + // if logging is enabled but we aren't posting yet, start posting + if ((pWebLog->bLogging == TRUE) && (pWebLog->bPosting == FALSE)) + { + char strUrl[256]; + // format url + ds_snzprintf(strUrl, sizeof(strUrl), "http://%s%s", pWebLog->strWebHost, pWebLog->strWebUrl); + // make client timeout very long (one hour) + ProtoHttpControl(pWebLog->pProtoHttp, 'time', 60*60*1000, 0, NULL); + //$$ hack -- set keepalive to 2 so we don't get a Connection: Close header + ProtoHttpControl(pWebLog->pProtoHttp, 'keep', 2, 0, NULL); + // start the post request + ProtoHttpPost(pWebLog->pProtoHttp, strUrl, NULL, -1, FALSE); + pWebLog->bPosting = TRUE; + } + + // send the data + iSentSize = ProtoHttpSend(pWebLog->pProtoHttp, pWebLog->strText, iSendSize); + + // remove sent data from buffer + if (iSentSize > 0) + { + if (iSentSize == iSendSize) + { + // clear the buffer + pWebLog->strText[0] = '\0'; + + // if we aren't logging, end the transaction + if (pWebLog->bLogging == FALSE) + { + NetPrintf(("weblog: ending streaming transmission\n")); + ProtoHttpSend(pWebLog->pProtoHttp, NULL, 0); + } + } + else + { + // contract buffer (include null character) + memmove(pWebLog->strText, pWebLog->strText + iSentSize, iSendSize - iSentSize + 1); + } + } + else if (iSentSize < 0) + { + NetPrintf(("weblog: error %d trying to send; resetting state to try a new post operation\n", iSentSize)); + pWebLog->bPosting = FALSE; + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function WebLogCreate + + \Description + Create the WebLog module. + + \Input iBufSize - local text buffer size + + \Output + WebLogRefT * - pointer to module state, or NULL + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +WebLogRefT *WebLogCreate(int32_t iBufSize) +{ + WebLogRefT *pWebLog; + int32_t iModuleSize; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // enforce minimum buffer size + if (iBufSize < 4096) + { + iBufSize = 4096; + } + + // allocate and init module state + iModuleSize = sizeof(*pWebLog) + iBufSize - 1; + if ((pWebLog = DirtyMemAlloc(iModuleSize, WEBLOG_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("weblog: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pWebLog, iModuleSize); + pWebLog->iMemGroup = iMemGroup; + pWebLog->pMemGroupUserData = pMemGroupUserData; + pWebLog->iBufLen = iBufSize; + + /* Set default webhost -- easo.stest.ea.com is the VIP address for the EAWS stest web cluster + and should be accessible from any environment. Port 8080 on the VIP is configured to forward + directly to port 8001, which hits Tomcat directly (and bypasses Apache). This is required + because the EAWS version of Apache has a bug when proxying a chunked upload that causes the + chunked encoding to be stripped but a Content-Length: not applied to the forwarded upload */ + ds_strnzcpy(pWebLog->strWebHost, "easo.stest.ea.com:8080", sizeof(pWebLog->strWebHost)); + #if 0 + // stesteasoweb01 bypasses the VIP but is an internal address + ds_strnzcpy(pWebLog->strWebHost, "stesteasoweb01.pt.abn-iad.ea.com:8001", sizeof(pWebLog->strWebHost)); + // eggplant is the dev EAWS server + ds_strnzcpy(pWebLog->strWebHost, "eggplant.online.ea.com", sizeof(pWebLog->strWebHost)); + // eggplant:8001 bypasses Apache and goes directly to Tomcat + ds_strnzcpy(pWebLog->strWebHost, "eggplant.online.ea.com:8001", sizeof(pWebLog->strWebHost)); + #endif + + // set default URL + ds_strnzcpy(pWebLog->strWebUrl, "/tool/easo/logrequest.jsp", sizeof(pWebLog->strWebUrl)); + + // init critical section + NetCritInit(&pWebLog->WebCrit, "WebLog"); + + // create ProtoHttp ref + if ((pWebLog->pProtoHttp = ProtoHttpCreate(4*1024)) == NULL) + { + NetPrintf(("weblog: could not allocate http ref\n")); + WebLogDestroy(pWebLog); + return(NULL); + } + + // return module state to caller + return(pWebLog); +} + +/*F********************************************************************************/ +/*! + \Function WebLogConfigure + + \Description + Configure weblog parameters + + \Input *pWebLog - weblog module state + \Input *pServer - server to post log to (NULL to retain current value) + \Input *pUrl - url to post log to (NULL to retain current value) + + \Output + None. + + \Version 05/14/2008 (jbrookes) +*/ +/********************************************************************************F*/ +void WebLogConfigure(WebLogRefT *pWebLog, const char *pServer, const char *pUrl) +{ + if (pServer != NULL) + { + ds_strnzcpy(pWebLog->strWebHost, pServer, sizeof(pWebLog->strWebHost)); + } + if (pUrl != NULL) + { + ds_strnzcpy(pWebLog->strWebUrl, pUrl, sizeof(pWebLog->strWebUrl)); + } +} + +/*F********************************************************************************/ +/*! + \Function WebLogStart + + \Description + Starts logging + + \Input *pWebLog - weblog module state + + \Output + None. + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +void WebLogStart(WebLogRefT *pWebLog) +{ + // see if we are already started + if (pWebLog->bLogging) + { + NetPrintf(("weblog: start called when already started\n")); + return; + } + + // start the logging + pWebLog->bLogging = TRUE; + NetPrintf(("weblog: starting log operation\n")); +} + +/*F********************************************************************************/ +/*! + \Function WebLogStop + + \Description + Stop logging and close close the current WebLog transaction (if any) + + \Input *pWebLog - weblog module state + + \Output + None. + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +void WebLogStop(WebLogRefT *pWebLog) +{ + // see if we are already stopped + if (!pWebLog->bLogging) + { + NetPrintf(("weblog: stop called when already stopped\n")); + return; + } + + // stop the logging + NetPrintf(("weblog: stopping log operation\n")); + pWebLog->bLogging = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function WebLogDestroy + + \Description + Destroy the WebLog module. + + \Input *pWebLog - pointer to weblog module to destroy + + \Output + None. + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +void WebLogDestroy(WebLogRefT *pWebLog) +{ + NetCritKill(&pWebLog->WebCrit); + if (pWebLog->pProtoHttp != NULL) + { + ProtoHttpDestroy(pWebLog->pProtoHttp); + } + DirtyMemFree(pWebLog, WEBLOG_MEMID, pWebLog->iMemGroup, pWebLog->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function WebLogDebugHook + + \Description + WebLog NetPrintf debug hook. + + \Input *pUserData - user data + \Input *pText - debug text + + \Output + int32_t - zero to suppress debug output, else do not suppress + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t WebLogDebugHook(void *pUserData, const char *pText) +{ + WebLogRefT *pWebLog = (WebLogRefT *)pUserData; + WebLogPrintf(pWebLog, "%s", pText); + return(1); +} + +/*F********************************************************************************/ +/*! + \Function WebLogPrintf + + \Description + Print into the weblog buffer. + + \Input *pWebLog - weblog module state + \Input *pFormat - format string + \Input ... - variable argument listing + + \Output + None. + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +void WebLogPrintf(WebLogRefT *pWebLog, const char *pFormat, ...) +{ + static char strText[4096]; + va_list pFmtArgs; + + // ensure serial access to text buffer and weblog buffer + NetCritEnter(&pWebLog->WebCrit); + + // format text + va_start(pFmtArgs, pFormat); + ds_vsnzprintf(strText, sizeof(strText), pFormat, pFmtArgs); + va_end(pFmtArgs); + + // queue the text for sending if we are logging + if (pWebLog->bLogging && !pWebLog->bUpdating) + { + ds_strnzcat(pWebLog->strText, strText, pWebLog->iBufLen); + } + + // release mutex + NetCritLeave(&pWebLog->WebCrit); +} + +/*F********************************************************************************/ +/*! + \Function WebLogControl + + \Description + Control weblog behavior + + \Input *pWebLog - weblog module state + \Input iSelect - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + Unhandled selectors are passed through to ProtoHttpControl() + + \Version 05/13/2008 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t WebLogControl(WebLogRefT *pWebLog, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue) +{ + return(ProtoHttpControl(pWebLog->pProtoHttp, iSelect, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function WebLogUpdate + + \Description + Update the WebLog module + + \Input *pWebLog - weblog module state + + \Output + None. + + \Version 05/06/2008 (jbrookes) +*/ +/********************************************************************************F*/ +void WebLogUpdate(WebLogRefT *pWebLog) +{ + NetCritEnter(&pWebLog->WebCrit); + // remember we are updating so weblog doesn't log itself + pWebLog->bUpdating = TRUE; + // flush data to protohttp + _WebLogFlush(pWebLog); + // update ProtoHttp + ProtoHttpUpdate(pWebLog->pProtoHttp); + // no longer updating + pWebLog->bUpdating = FALSE; + NetCritLeave(&pWebLog->WebCrit); +} diff --git a/r5dev/thirdparty/dirtysdk/source/platform/plat-str.c b/r5dev/thirdparty/dirtysdk/source/platform/plat-str.c new file mode 100644 index 00000000..098a50d0 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/platform/plat-str.c @@ -0,0 +1,1877 @@ +/*H********************************************************************************/ +/*! + \File plat-str.c + + \Description + This file provides platform independent versions of some standard-C + functions that are not "standard" across platforms and/or fixes + implementation problems with the standard versions (such as consistent + termination). + + \Copyright + Copyright (c) 2005-2011 Electronic Arts Inc. + + \Version 01/11/2005 (gschaefer) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include +#include // tolower + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynet.h" // sockaddr + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +//! lowercase hex translation table +static const char _ds_strhexlower[16] = "0123456789abcdef"; +//! uppercase hex translation table +static const char _ds_strhexupper[16] = "0123456789ABCDEF"; + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _ds_writechar + + \Description + Write a character to output buffer, if there is room + + \Input *pBuffer - [out] output buffer + \Input iLength - size of output buffer + \Input cWrite - character to write to output + \Input iOutput - offset in output buffer to write to + + \Output + int32_t - iOutput+1 + + \Version 02/14/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ds_writechar(char *pBuffer, int32_t iLength, char cWrite, int32_t iOutput) +{ + if (iOutput < iLength) + { + pBuffer[iOutput] = cWrite; + } + return(iOutput+1); +} + +/*F********************************************************************************/ +/*! + \Function _ds_writestr + + \Description + Write string to output buffer, supporting a source of chars or wchars and a + destination of chars. As per C99 specifications, if the output does not fit in + the given buffer, this function will return the number of characters that would + have been written if the buffer were big enough, but will truncate the string to + preserve buffer integrity. + + \Input *pBuffer - [out] output buffer + \Input iLength - size of output buffer + \Input *pString - string to print + \Input iOutput - offset in output buffer to print to + \Input bWideChar - if TRUE, input string uses wide (wchar_t) chars + + \Output + int32_t - number of characters written to output buffer + + \Version 12/14/2011 (jbrookes) Extracted from _ds_printstr(), added wchar support +*/ +/********************************************************************************F*/ +static int32_t _ds_writestr(char *pBuffer, int32_t iLength, const char *pString, int32_t iOutput, uint8_t bWideChar) +{ + const wchar_t *pWideStr; + int32_t iIndex; + + if (!bWideChar) + { + for (iIndex = 0; pString[iIndex] != '\0'; iIndex += 1) + { + iOutput = _ds_writechar(pBuffer, iLength, pString[iIndex], iOutput); + } + } + else + { + for (iIndex = 0, pWideStr = (const wchar_t *)pString; pWideStr[iIndex] != '\0\0'; iIndex += 1) + { + iOutput = _ds_writechar(pBuffer, iLength, (char)pWideStr[iIndex], iOutput); + } + } + return(iOutput); +} + +/*F********************************************************************************/ +/*! + \Function _ds_printstr + + \Description + Print string to output buffer, with leading character/justification/padding. + As per C99 specifications, if the output does not fit in the given buffer, + this function will return the number of characters that would have been written + if the buffer were big enough, but will truncate the string to preserve buffer + integrity. + + \Input *pBuffer - [out] output buffer + \Input iLength - size of output buffer + \Input *pString - string to print + \Input iOutput - offset in output buffer to print to + \Input iWidth - field width + \Input bRightJust - if TRUE, right-justify output + \Input bWideChar - if TRUE, input string uses wide (wchar_t) chars + \Input cLead - leading character or zero if none + \Input cPad - character to pad output with if width is larger than string length + + \Output + int32_t - number of characters written to output buffer + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ds_printstr(char *pBuffer, int32_t iLength, const char *pString, int32_t iOutput, int32_t iWidth, uint8_t bRightJust, uint8_t bWideChar, char cPad, char cLead) +{ + int32_t iStrLen; + // allow null pointer for source string + if (pString == NULL) + { + pString = "(null)"; + } + // calculate field width + if (iWidth > 0) + { + iStrLen = (int32_t) strlen(pString); + iWidth = (iStrLen < iWidth) ? iWidth - iStrLen : 0; + } + // handle right justification + if (bRightJust) + { + // handle lead character + if (cLead != 0) + { + // only add the lead character if we are not padding with spaces + if (cPad != ' ') + { + iOutput = _ds_writechar(pBuffer, iLength, cLead, iOutput); + cLead = 0; + } + // always decrement width + if (iWidth > 0) + { + iWidth -= 1; + } + } + for ( ; iWidth > 0; iWidth -= 1) + { + iOutput = _ds_writechar(pBuffer, iLength, cPad, iOutput); + } + } + // handle lead character + if (cLead != 0) + { + iOutput = _ds_writechar(pBuffer, iLength, cLead, iOutput); + // only decrement width if we are not right justfied, in which case we've already done it + if ((bRightJust == FALSE) && (iWidth > 0)) + { + iWidth -= 1; + } + } + // write string to buffer + iOutput = _ds_writestr(pBuffer, iLength, pString, iOutput, bWideChar); + // handle left justification + for ( ; iWidth > 0; iWidth -= 1) + { + iOutput = _ds_writechar(pBuffer, iLength, cPad, iOutput); + } + return(iOutput); +} + +/*F********************************************************************************/ +/*! + \Function _ds_uinttostr + + \Description + Convert an input unsigned integer into a string. + + \Input *pBuffer - [out] output buffer + \Input iBufSize - size of output buffer + \Input uInteger - unsigned integer to print + \Input uBase - output integer base + \Input *pHexTbl - pointer to hex conversion table (upper or lower case) + + \Output + char * - pointer to output string + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_uinttostr(char *pBuffer, int32_t iBufSize, uint64_t uInteger, uint32_t uBase, const char *pHexTbl) +{ + int32_t iIndex = iBufSize-1; + uint32_t uDigit; + + pBuffer[iIndex--] = '\0'; + + if (uInteger != 0) + { + for ( ; ; iIndex -= 1) + { + uDigit = uInteger % uBase; + pBuffer[iIndex] = pHexTbl[uDigit]; + if ((uInteger /= uBase) == 0) + { + break; + } + } + } + else + { + pBuffer[iIndex] = '0'; + } + + return(pBuffer+iIndex); +} + +/*F********************************************************************************/ +/*! + \Function _ds_inttostr + + \Description + Convert an input integer into a string. + + \Input *pBuffer - [out] output buffer + \Input iBufSize - size of output buffer + \Input iInteger - integer to print + \Input uBase - output integer base + \Input *pHexTbl - pointer to hex conversion table (upper or lower case) + \Input bPrintPlus - if TRUE, include + character on positive integers + \Input *pLead - [out] leading character to print (+/- or none) + + \Output + char * - pointer to output string + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_inttostr(char *pBuffer, int32_t iBufSize, int64_t iInteger, uint32_t uBase, const char *pHexTbl, uint8_t bPrintPlus, char *pLead) +{ + uint8_t bNegative = FALSE; + if (iInteger < 0) + { + bNegative = TRUE; + iInteger = -iInteger; + } + pBuffer = _ds_uinttostr(pBuffer, iBufSize, (uint64_t)iInteger, uBase, pHexTbl); + if (bNegative) + { + *pLead = '-'; + } + else if (bPrintPlus) + { + *pLead = '+'; + } + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _ds_fcvt + + \Description + Convert a double into a string (like fcvt()). + + \Input *pBuffer - [out] output buffer + \Input iBufSize - size of output buffer + \Input fDouble - double to print + \Input iPrecision - number of digits to include in mantissa + \Input *pDecimalPos - [out] storage for decimal point offset + \Input *pSign - [out] 0=positive, 1=negative + + \Output + char * - pointer to output string + + \Notes + Format of a double + seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff (1 11 52) + + \Version 05/03/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_fcvt(char *pBuffer, int32_t iBufSize, double fDouble, int32_t iPrecision, int32_t *pDecimalPos, int32_t *pSign) +{ + char strFloat[128] = "0", strInt[128], *pStrInt, *pStrFloat, *pStrFloatRnd, *pStrFloatStrt; + uint64_t uDouble, uMantissa, uDivisor, uDigit = 0; + int32_t iExponent, iFloatBufSize = sizeof(strFloat) - 1, iShift; + uint32_t uSign, uUpper; + + ds_memcpy_s(&uDouble, sizeof(uDouble), &fDouble, sizeof(fDouble)); + uUpper = (uint32_t)(uDouble >> 32); + iExponent = (int32_t)((uUpper >> 20) & 0x7ff) - 1023; + uMantissa = ((uDouble << 12) >> 12) | ((uint64_t)1 << 52); + uSign = uUpper >> 31; + + *pSign = uSign; + *pDecimalPos = 0; + pStrFloat = pStrFloatStrt = &strFloat[1]; + + // right-bias mantissa + for (iShift = 0; (uMantissa & 1) != 1 && (52-iExponent-iShift) > 0; iShift += 1) + { + uMantissa >>= 1; + } + + // give us a little more room to handle very large numbers + if (iExponent > 52) + { + if ((iShift = iExponent - 52) > 11) + { + // too big to fit in a 64bit integer + ds_strnzcpy(pBuffer, "(BIG)", iBufSize); + return(pBuffer); + } + // left-bias mantissa to reduce exponent + uMantissa <<= iShift; + iExponent -= iShift; + iShift = 0; + } + + // for very small numbers; right-shift (losing precision) until our integer divisor is within range + for ( ; (52-iExponent-iShift) > 62; iShift += 1) + { + uMantissa >>= 1; + } + + uDivisor = (uint64_t)1 << (52-iExponent-iShift); + + // integer part + if (uMantissa >= uDivisor) + { + uDigit = uMantissa / uDivisor; + pStrInt = _ds_uinttostr(strInt, sizeof(strInt), uDigit, 10, _ds_strhexlower); + ds_strnzcpy(pStrFloat, pStrInt, iFloatBufSize); + *pDecimalPos = (int32_t)strlen(pStrInt); + pStrFloat += *pDecimalPos; + uMantissa -= uDigit*uDivisor; + uDigit = 0; + } + + // fractional part + for ( ; uMantissa > 0; iPrecision -= 1) + { + uMantissa = uMantissa * 10; + uDigit = uMantissa / uDivisor; + if (iPrecision == 0) + { + break; + } + *pStrFloat++ = (uint8_t)(uDigit + '0'); + uMantissa -= uDigit*uDivisor; + } + + // round? + if ((iPrecision == 0) && (uDigit >= 5)) + { + // null-terminate + *pStrFloat = '\0'; + + for (pStrFloatRnd = pStrFloat - 1; ; pStrFloatRnd -= 1) + { + *pStrFloatRnd += 1; + if (*pStrFloatRnd <= '9') + { + break; + } + *pStrFloatRnd = '0'; + } + + if (pStrFloatRnd == strFloat) + { + pStrFloatStrt = strFloat; + *pDecimalPos += 1; + } + } + else + { + // pad with zeros + for ( ; iPrecision > 0; iPrecision -= 1) + { + *pStrFloat++ = '0'; + } + + // null-terminate + *pStrFloat++ = '\0'; + } + + // copy to output and return to caller + ds_strnzcpy(pBuffer, pStrFloatStrt, iBufSize); + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _ds_floattostr + + \Description + Convert an 32bit floating point number into a string. + + \Input *pBuffer - [out] output buffer + \Input iBufSize - size of output buffer + \Input fDouble - double to print + \Input iPrecision - number of digits to include in mantissa + \Input bPrintPlus - if TRUE, include + character on positive integers + \Input *pLead - [out] leading character to print (+/- or none) + + \Output + char * - pointer to output string + + \Version 05/03/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_floattostr(char *pBuffer, int32_t iBufSize, double fDouble, int32_t iPrecision, uint32_t bPrintPlus, char *pLead) +{ + int32_t iDecimalPos, iSign, iIndex = 0; + char strFloat[128], strFmtFloat[128]; + + // default precision + if (iPrecision < 0) + { + iPrecision = 6; //c99 default + } + + // float to string +#if 0 + ds_strnzcpy(strFloat, fcvt(fDouble, iPrecision, &iDecimalPos, &iSign), sizeof(strFloat)); +#else + _ds_fcvt(strFloat, sizeof(strFloat), fDouble, iPrecision, &iDecimalPos, &iSign); +#endif + + // handle sign + if (iSign != 0) + { + *pLead = '-'; + } + else if (bPrintPlus) + { + *pLead = '+'; + } + + // copy integer portion + if (iDecimalPos > 0) + { + ds_strsubzcpy(&strFmtFloat[iIndex], sizeof(strFmtFloat) - iIndex, strFloat, iDecimalPos); + iIndex += iDecimalPos; + } + else + { + strFmtFloat[iIndex++] = '0'; + } + + // add decimal point and fractional portion + if (strFloat[iDecimalPos] != '\0') + { + strFmtFloat[iIndex++] = '.'; + } + for ( ; iDecimalPos < 0 && iPrecision > 0; iDecimalPos += 1, iPrecision -= 1) + { + strFmtFloat[iIndex++] = '0'; + } + if (iDecimalPos >= 0) + { + ds_strnzcpy(&strFmtFloat[iIndex], &strFloat[iDecimalPos], sizeof(strFmtFloat)-iIndex); + } + else + { + strFmtFloat[iIndex++] = '\0'; + } + + // copy to buffer + ds_strnzcpy(pBuffer, strFmtFloat, iBufSize); + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _ds_ptrtostr + + \Description + Convert an input integer into a string. + + \Input *pBuffer - [out] output buffer + \Input iBufSize - size of output buffer + \Input *pPointer - pointer value to print + + \Output + char * - pointer to output string + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_ptrtostr(char *pBuffer, int32_t iBufSize, void *pPointer) +{ + if (pPointer == NULL) + { + ds_strnzcpy(pBuffer, "(null)", iBufSize); + return(pBuffer); + } + pBuffer = _ds_uinttostr(pBuffer, iBufSize, (uintptr_t)pPointer, 16, _ds_strhexlower); + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _ds_sockaddrtostr + + \Description + Convert an input sockaddr into string format (supports AF_INET and AF_INET6) + Important on the nx only AF_INET addresses arew supported + + \Input *pBuffer - [out] output buffer + \Input iBufSize - size of output buffer + \Input *pSockAddr - address to print + + \Output + char * - pointer to output string + + \Notes + Reference: http://tools.ietf.org/html/rfc5952#section-4 + + \Version 04/24/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_sockaddrtostr(char *pBuffer, int32_t iBufSize, struct sockaddr *pSockAddr) +{ + char *pBufStart = pBuffer; + char strPort[16] = "", strAddr[48]; + uint16_t uPort = 0; + + // format port string, if non-zero + #ifdef DIRTYCODE_NX + uPort = SockaddrInGetPort(pSockAddr); + #else + struct sockaddr_in6 *pSockAddr6 = (struct sockaddr_in6 *)pSockAddr; + uPort = (pSockAddr->sa_family == AF_INET) ? SockaddrInGetPort(pSockAddr) : SocketHtons(pSockAddr6->sin6_port); + #endif + + if (uPort != 0) + { + ds_snzprintf(strPort, sizeof(strPort), ":%d", uPort); + } + // format the address + ds_snzprintf(pBuffer, iBufSize, "[%s]%s", SockaddrInGetAddrText(pSockAddr, strAddr, sizeof(strAddr)), strPort); + + // return buffer to caller + return(pBufStart); +} + + +/*F********************************************************************************/ +/*! + \Function _ds_fourcctostr + + \Description + Convert an input fourcc code into a string. Unprintable characters + are converted to asterisks ('*'). + + \Input *pBuffer - [out] output buffer + \Input iBufSize - size of output buffer + \Input uFourCC - fourcc code to print + + \Output + char * - pointer to output string + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_fourcctostr(char *pBuffer, int32_t iBufSize, uint32_t uFourCC) +{ + int32_t iCode; + for (iCode = 3; iCode >= 0; iCode -= 1) + { + pBuffer[iCode] = (char)(uFourCC & 0xff); + uFourCC >>= 8; + if (!isprint((uint8_t)pBuffer[iCode])) + { + pBuffer[iCode] = '*'; + } + } + pBuffer[4] = '\0'; + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _ds_vsnprintf + + \Description + Replacement for vsnprintf, with some modifications. + + \Input *pBuffer - output buffer + \Input iLength - size of output buffer + \Input *pFormat - format string + \Input Args - variable argument list + + \Output + int32_t - number of characters formatted; if > than the length, output + is truncated and the buffer is null-terminated unless iLength<=0 + + \Notes + Unlike a standard implementation of vsnprintf(), this version always + null-terminates unless the buffer size is zero. Overflow semantics follow + the C99 standard. + + \verbatim + Most ISO printf functionality is supported, with the following exceptions: + + - Floating point is supported with the following limitations: + - All floating-point formatters behave like 'f' + - Very large (> 2^63) floating point numbers will return (BIG) + - The # flag is not supported, although it is consumed. + - Length is not supported, although length specifiers are consumed. + + Additionally, the following extension types replace their normal meaning: + + - %a: print 32-bit address in dot notation (32-bit integer) + - %A: print a sockaddr (IPv4 or IPv6) + - %C: print fourcc code (32-bit integer) + \endverbatim + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ds_vsnprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args) +{ + int32_t iInput, iOutput, iTerm; + char strTempBuf[64], *pString; + char cInput; + + // handle format list + for (iInput = 0, iOutput = 0; ; ) + { + // read input character + if ((cInput = pFormat[iInput++]) == '\0') + { + break; + } + + // process a format specifier + if (cInput == '%') + { + int32_t iPrecision = -1, iSize = 4, iWidth = 0; + uint8_t bRightJust = TRUE, bPrintPlus = FALSE, bWideChar = FALSE; + char cPad = ' ', cLead = 0; + + // handle end of string + if (pFormat[iInput] == '\0') + { + break; + } + + // process %% + if (pFormat[iInput] == '%') + { + iOutput = _ds_writechar(pBuffer, iLength, pFormat[iInput], iOutput); + iInput += 1; + continue; + } + // process flags + if (pFormat[iInput] == '-') + { + iInput += 1; + bRightJust = FALSE; + } + if (pFormat[iInput] == '+') + { + iInput += 1; + bPrintPlus = TRUE; + } + if (pFormat[iInput] == '#') + { + // unsupported + iInput += 1; + } + if (pFormat[iInput] == '0') + { + iInput += 1; + cPad = '0'; + } + // process width + if (pFormat[iInput] == '*') + { + iInput += 1; + iWidth = va_arg(Args, int); + } + for ( ; (pFormat[iInput] >= '0') && (pFormat[iInput] <= '9'); iInput += 1) + { + iWidth *= 10; + iWidth += pFormat[iInput] - '0'; + } + + // precision specifier? + if (pFormat[iInput] == '.') + { + // process period and precision width + for (iPrecision = 0, iInput += 1; (pFormat[iInput] >= '0') && (pFormat[iInput] <= '9'); iInput += 1) + { + iPrecision *= 10; + iPrecision += pFormat[iInput] - '0'; + } + + // process asterisk (arg width) if present + if (pFormat[iInput] == '*') + { + iInput += 1; + iWidth = va_arg(Args, int); + } + } + + // check for length specifier + cInput = pFormat[iInput++]; + if ((cInput == 'h') || (cInput == 'l') || (cInput == 'L') || (cInput == 'z') || (cInput == 'j') || (cInput == 't') || (cInput == 'q') || (cInput == 'I')) + { + if (cInput == 'h') + { + iSize = 2; + } + if (cInput == 'q') + { + iSize = 8; + } + if ((cInput == 'l') && (pFormat[iInput] == 's')) + { + iSize = 8; + } + if (cInput == 'I') + { + if ((pFormat[iInput] == '6') && (pFormat[iInput+1] == '4')) + { + iSize = 8; + } + iInput += 2; + } + cInput = pFormat[iInput++]; + if ((cInput == 'h') || (cInput == 'l')) + { + if (cInput == 'h') + { + iSize = 1; + } + if (cInput == 'l') + { + iSize = 8; + } + cInput = pFormat[iInput++]; + } + } + + // process type specifier + switch(cInput) + { + case 'a': + { + uint32_t uAddress = va_arg(Args, unsigned int); + pString = SocketInAddrGetText(uAddress, strTempBuf, sizeof(strTempBuf)); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + case 'A': + { + struct sockaddr *pSockAddr = va_arg(Args, struct sockaddr *); + pString = _ds_sockaddrtostr(strTempBuf, sizeof(strTempBuf), pSockAddr); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + case 'c': + { + strTempBuf[0] = (char)va_arg(Args, int); + strTempBuf[1] = '\0'; + iOutput = _ds_printstr(pBuffer, iLength, strTempBuf, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + case 'C': + { + uint32_t uFourCC = va_arg(Args, unsigned int); + pString = _ds_fourcctostr(strTempBuf, sizeof(strTempBuf), uFourCC); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + case 'd': + case 'i': + { + int64_t iInteger = (iSize <= 4) ? va_arg(Args, int) : va_arg(Args, long long); + pString = _ds_inttostr(strTempBuf, sizeof(strTempBuf), iInteger, 10, _ds_strhexlower, bPrintPlus, &cLead); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, cLead); + } + break; + + case 'n': + { + int *pInteger = va_arg(Args, int *); + *pInteger = iOutput; + } + break; + + case 'o': + { + uint64_t uInteger = (iSize <= 4) ? va_arg(Args, unsigned int) : va_arg(Args, unsigned long long); + pString = _ds_uinttostr(strTempBuf, sizeof(strTempBuf), uInteger, 8, _ds_strhexlower); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + case 'p': + { + void *pPointer = va_arg(Args, void *); + if (pPointer != NULL) + { + iWidth = sizeof(void *) * 2 + 1; // always print full width (+1 for the leading '$') + cLead = '$'; + } + pString = _ds_ptrtostr(strTempBuf, sizeof(strTempBuf), pPointer); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, '0', cLead); // always print leading zeros + } + break; + + case 'S': + iSize = 8; + case 's': + { + pString = va_arg(Args, char *); + bWideChar = (iSize == 8) ? TRUE : FALSE; + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + case 'u': + { + uint64_t uInteger = (iSize <= 4) ? va_arg(Args, unsigned int) : va_arg(Args, unsigned long long); + pString = _ds_uinttostr(strTempBuf, sizeof(strTempBuf), uInteger, 10, _ds_strhexlower); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + case 'x': + case 'X': + { + uint64_t uInteger = (iSize <= 4) ? va_arg(Args, unsigned int) : va_arg(Args, unsigned long long); + pString = _ds_uinttostr(strTempBuf, sizeof(strTempBuf), uInteger, 16, cInput == 'x' ? _ds_strhexlower : _ds_strhexupper); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, 0); + } + break; + + // all floating-point handled like 'f' + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + { + double fDouble = va_arg(Args, double); + pString = _ds_floattostr(strTempBuf, sizeof(strTempBuf), fDouble, iPrecision, bPrintPlus, &cLead); + iOutput = _ds_printstr(pBuffer, iLength, pString, iOutput, iWidth, bRightJust, bWideChar, cPad, cLead); + } + break; + + default: + { + // should not hit this; unsupported type specifier? + } + break; + } + } + else + { + iOutput = _ds_writechar(pBuffer, iLength, cInput, iOutput); + } + } + + // locate terminating null + iTerm = (iOutput < iLength) ? iOutput : iLength-1; + + // terminate if possible + if (iLength > 0) + { + pBuffer[iTerm] = '\0'; + } + // return length of output to caller + return(iOutput); +} + +/*F********************************************************************************/ +/*! + \Function _ds_strcmpwc + + \Description + String compare with wildcard matching ('*' only). + + \Input *pString1 - string to match + \Input *pStrWild - wildcard string + \Input bNoCase - TRUE if a case-insensitive comparison should be performed + + \Output + int32_t - see stricmp + + \Version 09/18/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ds_strcmpwc(const char *pString1, const char *pStrWild, uint8_t bNoCase) +{ + int32_t r; + char c1, c2; + + do { + c1 = *pString1++; + c2 = *pStrWild; + if (bNoCase) + { + if ((c1 >= 'A') && (c1 <= 'Z')) + { + c1 ^= 32; + } + if ((c2 >= 'A') && (c2 <= 'Z')) + { + c2 ^= 32; + } + } + if ((c2 == '*') && (c1 != '\0')) + { + if ((r = _ds_strcmpwc(pString1, pStrWild+1, bNoCase)) == 0) + { + break; + } + r = 0; + } + else + { + pStrWild += 1; + r = c1-c2; + } + } while ((c1 != '\0') && (c2 != '\0') && (r == 0)); + + return(r); +} + +/*F********************************************************************************/ +/*! + \Function _ds_fmtoctstring + + \Description + Format a binary blob as hexadecmial text + + \Input *pOutput - [out] outbuffer for text + \Input iOutLen - size of output buffer + \Input *pInput - input binary data + \Input iInpLen - size of input data + \Input *pHexTbl - hex table to use for translation + + \Output + char * - pointer to output buffer + + \Version 04/11/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_ds_fmtoctstring(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen, const char *pHexTbl) +{ + int32_t iInput, iOutput; + + for (iInput = 0, iOutput = 0, iOutLen -= 1; (iInput < iInpLen) && (iOutput < (iOutLen-1)); iInput += 1, iOutput += 2) + { + uint8_t cByte = pInput[iInput]; + pOutput[iOutput+0] = pHexTbl[cByte >> 4]; + pOutput[iOutput+1] = pHexTbl[cByte & 0xf]; + } + pOutput[iOutput] = '\0'; + + return(pOutput); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ds_strnlen + + \Description + Replacement for strnlen. + + \Input *pBuffer - buffer + \Input iLength - size of buffer + + \Output + int32_t - number of characters or iLength if no '\0' is present. + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_strnlen(const char *pBuffer, int32_t iLength) +{ + int32_t iLen = 0; + + while (iLen < iLength && *(pBuffer+iLen) != '\0') + { + ++iLen; + } + + return(iLen); +} + +/*F********************************************************************************/ +/*! + \Function ds_vsnprintf + + \Description + Replacement for vsnprintf, with some modifications. + + \Input *pBuffer - output buffer + \Input iLength - size of output buffer + \Input *pFormat - format string + \Input Args - variable argument list + + \Output + int32_t - number of characters formatted; if > than the length, output + is truncated and the buffer is null-terminated unless iLength<=0. + + \Notes + Unlike standard vsnprintf, this version always null-terminates if the buffer + is not zero-sized. + + \Version 10/28/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_vsnprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args) +{ + return(_ds_vsnprintf(pBuffer, iLength, pFormat, Args)); +} + +/*F********************************************************************************/ +/*! + \Function ds_vsnzprintf + + \Description + Replacement for vsnprintf that always includes a terminator at the end of + the string. + + \Input *pBuffer - output buffer + \Input iLength - size of output buffer + \Input *pFormat - format string + \Input Args - variable argument list + + \Output + int32_t - number of characters formatted; if > than the length, output + is truncated and the buffer is null-terminated unless iLength<=0. + + \Version 02/15/2011 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_vsnzprintf(char *pBuffer, int32_t iLength, const char *pFormat, va_list Args) +{ + return(_ds_vsnprintf(pBuffer, iLength, pFormat, Args)); +} + +/*F********************************************************************************/ +/*! + \Function ds_snzprintf + + \Description + Replacement for snprintf that always includes a terminator at the end of + the string. + + \Input *pBuffer - output buffer + \Input iLength - size of output buffer + \Input *pFormat - format string + \Input ... - variable argument list + + \Output + int32_t - number of characters formatted; if > than the length, output + is truncated and the buffer is null-terminated unless iLength<=0. + + \Version 02/15/2011 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_snzprintf(char *pBuffer, int32_t iLength, const char *pFormat, ...) +{ + int32_t iResult; + va_list args; + + // format the text + va_start(args, pFormat); + iResult = _ds_vsnprintf(pBuffer, iLength, pFormat, args); + va_end(args); + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ds_fmtoctstring + + \Description + Format a binary blob as hexadecmial text + + \Input *pOutput - [out] outbuffer for text + \Input iOutLen - size of output buffer + \Input *pInput - input binary data + \Input iInpLen - size of input data + + \Output + char * - pointer to output buffer + + \Version 04/11/2017 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_fmtoctstring(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen) +{ + return(_ds_fmtoctstring(pOutput, iOutLen, pInput, iInpLen, "0123456789ABCDEF")); +} + +/*F********************************************************************************/ +/*! + \Function ds_fmtoctstring_lc + + \Description + Format a binary blob as hexadecmial text, with the alphabetic characters + being lowercase + + \Input *pOutput - [out] outbuffer for text + \Input iOutLen - size of output buffer + \Input *pInput - input binary data + \Input iInpLen - size of input data + + \Output + char * - pointer to output buffer + + \Version 04/11/2017 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_fmtoctstring_lc(char *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen) +{ + return(_ds_fmtoctstring(pOutput, iOutLen, pInput, iInpLen, "0123456789abcdef")); +} + +/*F********************************************************************************/ +/*! + \Function ds_strtolower + + \Description + Convert given string to lower case + + \Input *pString - string to convert + + \Output + char * - pointer to string + + \Version 12/28/2019 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_strtolower(char *pString) +{ + char *pStrStart = pString; + for (; *pString != '\0'; pString += 1) + { + if ((*pString >= 'A') && (*pString <= 'Z')) + { + *pString += 0x20; + } + } + return(pStrStart); +} + +/*F********************************************************************************/ +/*! + \Function ds_strtoupper + + \Description + Convert given string to upper case + + \Input *pString - string to convert + + \Output + char * - pointer to string + + \Version 12/28/2019 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_strtoupper(char *pString) +{ + char *pStrStart = pString; + for (; *pString != '\0'; pString += 1) + { + if ((*pString >= 'a') && (*pString <= 'z')) + { + *pString -= 0x20; + } + } + return(pStrStart); +} + +/*F********************************************************************************/ +/*! + \Function ds_strtrim + + \Description + Removes excess white space before and after values, and converts sequential + spaces to a single space. + + \Input *pString - [out] string to trim + + \Output + char * - pointer to string + + \Version 12/28/2018 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_strtrim(char *pString) +{ + char *pStrStart = pString, *pStrTrim; + // skip leading whitespace + for (; *pString == ' '; pString += 1) + ; + // copy characters, converting sequential spaces into a single space + for (pStrTrim = pString; *pString != '\0'; ) + { + *pStrTrim++ = *pString; + if (*pString == ' ') + { + // eat multiple spaces + for (; *pString == ' '; pString += 1) + ; + } + else + { + pString += 1; + } + } + // terminate string + *pString = '\0'; + // return start of string + return(pStrStart); +} + +/*F********************************************************************************/ +/*! + \Function ds_strlistinsert + + \Description + Insert string into string list buffer in sorted order, terminated by + the specified terminator. The list terminator may not be nul and it must + be a character that does not appear in strings inserted in the list. + + \Input *pStrList - [in/out] buffer to hold string list + \Input iListBufLen - length of list buffer + \Input *pString - string to insert in string list + \Input cTerm - string terminator + + \Output + int32_t - positive on success, zero if string could not be inserted + + \Version 12/29/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_strlistinsert(char *pStrList, int32_t iListBufLen, const char *pString, char cTerm) +{ + char *pStrInsert, *pStrSkip; + int32_t iStrLen, iListLen; + + // get length of string we're inserting + iStrLen = (int32_t)strlen(pString); + // get length of strlist + iListLen = (int32_t)strlen(pStrList); + // make sure we have room to insert + if (iListBufLen < (iListLen+iStrLen+1)) + { + return(0); + } + + // find insertion point + for (pStrInsert = pStrSkip = pStrList; pStrSkip != NULL; ) + { + if (ds_stricmp(pStrInsert, pString) >= 0) + { + break; + } + pStrInsert = pStrSkip; + if ((pStrSkip = strchr(pStrSkip, cTerm)) != NULL) + { + pStrSkip += 1; + } + } + + // make room for formatted header if necessary + if (*pStrInsert != '\0') + { + // get length of list from here to end of the buffer + iListLen = (int32_t)strlen(pStrInsert); + // make room for string, plus terminator character + memmove(pStrInsert+iStrLen+1, pStrInsert, iListLen); + // point to insertion point + iListBufLen -= pStrInsert-pStrList; + pStrList = pStrInsert; + } + else + { + // end of list, so null terminate + pStrInsert[iStrLen+1] = '\0'; + } + + // copy formatted header into list and terminate it + ds_memcpy_s(pStrInsert, iListBufLen, pString, iStrLen); + pStrInsert[iStrLen] = cTerm; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function ds_stristr + + \Description + Case insensitive substring search + + \Input pHaystack - see stristr + \Input pNeedle - see stristr + + \Output see stristr + + \Version 01/25/2005 (gschaefer) +*/ +/********************************************************************************F*/ +char *ds_stristr(const char *pHaystack, const char *pNeedle) +{ + int32_t iFirst; + int32_t iIndex; + + // make sure strings are valid + if ((pHaystack != NULL) && (*pHaystack != 0) && (pNeedle != NULL) && (*pNeedle != 0)) + { + iFirst = tolower((unsigned char)*pNeedle); + for (; *pHaystack != 0; ++pHaystack) + { + // small optimization on first character search + if (tolower((unsigned char)*pHaystack) == iFirst) + { + for (iIndex = 1;; ++iIndex) + { + if (pNeedle[iIndex] == 0) + { + return((char *)pHaystack); + } + if (pHaystack[iIndex] == 0) + { + break; + } + if (tolower((unsigned char)pHaystack[iIndex]) != tolower((unsigned char)pNeedle[iIndex])) + { + break; + } + } + } + } + } + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function ds_stricmp + + \Description + Case insensitive string compare + + \Input *pString1 - see stricmp + \Input *pString2 - see stricmp + + \Output see stricmp + + \Version 01/25/2005 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ds_stricmp(const char *pString1, const char *pString2) +{ + int32_t r; + char c1, c2; + + do { + c1 = *pString1++; + if ((c1 >= 'A') && (c1 <= 'Z')) + c1 ^= 32; + c2 = *pString2++; + if ((c2 >= 'A') && (c2 <= 'Z')) + c2 ^= 32; + r = c1-c2; + } while ((c1 != 0) && (r == 0)); + + return(r); +} + +/*F********************************************************************************/ +/*! + \Function ds_strnicmp + + \Description + Case insensitive string compare of first N characters + + \Input *pString1 - see strnicmp + \Input *pString2 - see strnicmp + \Input uCount - see strnicmp + + \Output see strnicmp + + \Version 01/25/2005 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ds_strnicmp(const char *pString1, const char *pString2, uint32_t uCount) +{ + int32_t r; + char c1, c2; + uint32_t uPos; + + if (uCount == 0) + return(0); + + uPos = 0; + do { + c1 = *pString1++; + if ((c1 >= 'A') && (c1 <= 'Z')) + c1 ^= 32; + c2 = *pString2++; + if ((c2 >= 'A') && (c2 <= 'Z')) + c2 ^= 32; + r = c1-c2; + uPos++; + } while ((c1 != 0) && (r == 0) && (uPos < uCount)); + + return(r); +} + +/*F********************************************************************************/ +/*! + \Function ds_strcmpwc + + \Description + String compare with wildcard matching ('*' only). + + \Input *pString1 - see stricmp + \Input *pStrWild - match string including wildcard(s) + + \Output see stricmp + + \Version 09/18/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_strcmpwc(const char *pString1, const char *pStrWild) +{ + return(_ds_strcmpwc(pString1, pStrWild, FALSE)); +} + +/*F********************************************************************************/ +/*! + \Function ds_stricmpwc + + \Description + String compare with wildcard matching ('*' only). + + \Input *pString1 - see stricmp + \Input *pStrWild - match string including wildcard(s) + + \Output see stricmp + + \Version 09/18/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_stricmpwc(const char *pString1, const char *pStrWild) +{ + return(_ds_strcmpwc(pString1, pStrWild, TRUE)); +} + +/*F********************************************************************************/ +/*! + \Function ds_strnzcpy + + \Description + Always terminated strncpy function + + \Input *pDest - [out] output for new string + \Input *pSource - pointer to input string to copy from + \Input iCount - length of output buffer + + \Output + char * - pointer to output buffer + + \Notes + Unlike strncpy(), the destination buffer is not filled with zeros if the + string being copied does not fill it. + + \Version 01/11/2017 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_strnzcpy(char *pDest, const char *pSource, int32_t iCount) +{ + int32_t iIndex; + + // make sure buffer has enough room + if (--iCount < 0) + { + return(0); + } + + // copy the string + for (iIndex = 0; (iIndex < iCount) && (pSource[iIndex] != '\0'); iIndex += 1) + { + pDest[iIndex] = pSource[iIndex]; + } + + // write null terminator and return number of bytes written + pDest[iIndex] = '\0'; + return(pDest); +} + +/*F********************************************************************************/ +/*! + \Function ds_strsubzcpy + + \Description + Copy a substring from pSrc into the output buffer. The output string + is guaranteed to be null-terminated. + + \Input *pDst - [out] output for new string + \Input iDstLen - length of output buffer + \Input *pSrc - pointer to input string to copy from + \Input iSrcLen - number of characters to copy from input string + + \Output + int32_t - number of characters written, excluding null character + + \Version 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_strsubzcpy(char *pDst, int32_t iDstLen, const char *pSrc, int32_t iSrcLen) +{ + int32_t iIndex; + + // make sure buffer has enough room + if (--iDstLen < 0) + { + return(0); + } + + // copy the string + for (iIndex = 0; (iIndex < iSrcLen) && (iIndex < iDstLen) && (pSrc[iIndex] != '\0'); iIndex++) + { + pDst[iIndex] = pSrc[iIndex]; + } + + // write null terminator and return number of bytes written + pDst[iIndex] = '\0'; + return(iIndex); +} + +/*F********************************************************************************/ +/*! + \Function ds_strnzcat + + \Description + Concatenate the string pointed to by pSrc to the string pointed to by pDst. + A maximum of iDstLen-1 characters are copied, and the resulting string is + guaranteed to be null-terminated. + + \Input *pDst - [out] output for new string + \Input *pSrc - pointer to input string to copy from + \Input iDstLen - size of output buffer + + \Output + int32_t - number of characters in pDst, excluding null character + + \Version 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_strnzcat(char *pDst, const char *pSrc, int32_t iDstLen) +{ + int32_t iDst, iSrc; + + // make sure buffer has enough room + if (--iDstLen < 0) + { + return(0); + } + + // find end of string + for (iDst = 0; (iDst < iDstLen) && (pDst[iDst] != '\0'); iDst++) + ; + + // copy the string + for (iSrc = 0; (iDst < iDstLen) && (pSrc[iSrc] != '\0'); iSrc++, iDst++) + { + pDst[iDst] = pSrc[iSrc]; + } + + // write null terminator and return updated length of string + pDst[iDst] = '\0'; + return(iDst); +} + +/*F********************************************************************************/ +/*! + \Function ds_strsubzcat + + \Description + Concatenate the substring pointed to by pSrc and with a length of iSrcLen + to the string pointed to by pDst. A maximum of iDstLen-1 or iSrcLen + characters are copied, whichever is smaller, and the resulting string is + guaranteed to be null-terminated. + + \Input *pDst - [out] output for new string + \Input iDstLen - size of output buffer + \Input *pSrc - pointer to input string to copy from + \Input iSrcLen - size of substring pointed to by pSrc. + + \Output + int32_t - number of characters in pDst, excluding null character + + \Version 09/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_strsubzcat(char *pDst, int32_t iDstLen, const char *pSrc, int32_t iSrcLen) +{ + int32_t iDst, iSrc; + + // make sure buffer has enough room + if (--iDstLen < 0) + { + return(0); + } + + // find end of string + for (iDst = 0; (iDst < iDstLen) && (pDst[iDst] != '\0'); iDst++) + ; + + // copy the string + for (iSrc = 0; (iDst < iDstLen) && (iSrc < iSrcLen) && (pSrc[iSrc] != '\0'); iSrc++, iDst++) + { + pDst[iDst] = pSrc[iSrc]; + } + + // write null terminator and return updated length of string + pDst[iDst] = '\0'; + return(iDst); + +} + +/*F********************************************************************************/ +/*! + \Function ds_strsplit + + \Description + Splits string on a delimiter + + \Input *pSrc - the string we are splitting + \Input cDelimiter - delimited to split on + \Input *pDst - where we are writing the data split + \Input iDstSize - size of dest string + \Input **pNewSrc - used to save position of iteration + + \Output + int32_t - length of destination or zero if nothing parsed + + \Version 08/04/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ds_strsplit(const char *pSrc, char cDelimiter, char *pDst, int32_t iDstSize, const char **pNewSrc) +{ + const char *pLocation; + int32_t iSrcSize; + + // check for validity of inputs + if ((pSrc == NULL) || (*pSrc == '\0')) + { + return(0); + } + iSrcSize = (int32_t)strlen(pSrc); + + /* if we start with delimiter skip it + this happens when you do multiple parses */ + if (pSrc[0] == cDelimiter) + { + pSrc += 1; + iSrcSize -= 1; + (*pNewSrc) += 1; + } + + // terminate the destination + if (pDst != NULL && iDstSize > 0) + { + pDst[0] = '\0'; + } + + /* write the value to the destination (if not null) + when length is less than max, add 1 for nul terminator */ + if ((pLocation = strchr(pSrc, cDelimiter)) != NULL) + { + const int32_t iCount = (int32_t)(pLocation - pSrc); + (*pNewSrc) += iCount; + if (pDst != NULL) + { + ds_strnzcpy(pDst, pSrc, DS_MIN(iDstSize, iCount+1)); + } + return(iCount); + } + else + { + (*pNewSrc) += iSrcSize; + if (pDst != NULL) + { + ds_strnzcpy(pDst, pSrc, DS_MIN(iDstSize, iSrcSize+1)); + } + return(iSrcSize); + } +} + +/*F********************************************************************************/ +/*! + \Function ds_strtok_r + + \Description + Reentrant version of strtok + + \Input *pStr - input string + \Input *pDelim - delimiter + \Input **ppSavePtr - variable used to save down the state + + \Output + char* - reutrns the pointer to the next token + + \Version 09/07/2018 (tcho) +*/ +/********************************************************************************F*/ +char* ds_strtok_r(char *pStr, const char *pDelim, char **ppSavePtr) +{ + #if defined(DIRTYCODE_NX) + char *pEnd; + + if (pStr == NULL) + { + pStr = *ppSavePtr; + } + + if (*pStr == '\0') + { + *ppSavePtr = pStr; + return(NULL); + } + + // scan leading character + pStr += strspn(pStr, pDelim); + if (*pStr == '\0') + { + *ppSavePtr = pStr; + return(NULL); + } + + // find end of token + pEnd = pStr + strcspn(pStr, pDelim); + if (*pEnd == '\0') + { + *ppSavePtr = pEnd; + return(pStr); + } + + *pEnd = '\0'; + *ppSavePtr = pEnd + 1; + return pStr; + #elif defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + return strtok_s(pStr, pDelim, ppSavePtr); + #else + return strtok_r(pStr, pDelim, ppSavePtr); + #endif +} +/*F********************************************************************************/ +/*! + \Function ds_memcpy + + \Description + Replacement for memcpy. + + \Input *pDst - [out] output for new string + \Input *pSrc - pointer to input string to copy from + \Input iDstLen - size of substring pointed to by pSrc. + + \Version 07/15/2014 (amakoukji) +*/ +/********************************************************************************F*/ +void ds_memcpy(void *pDst, const void *pSrc, int32_t iDstLen) +{ + memcpy(pDst, pSrc, iDstLen); +} + +/*F********************************************************************************/ +/*! + \Function ds_memcpy_s + + \Description + Replacement for memcpy_s. + + \Input *pDst - [out] output for new string + \Input iDstLen - size of output buffer + \Input *pSrc - pointer to input string to copy from + \Input iSrcLen - size of substring pointed to by pSrc. + + \Version 07/15/2014 (amakoukji) +*/ +/********************************************************************************F*/ +void ds_memcpy_s(void *pDst, int32_t iDstLen, const void *pSrc, int32_t iSrcLen) +{ + iDstLen = DS_MIN(iDstLen, iSrcLen); + memcpy(pDst, pSrc, iDstLen); +} + +/*F********************************************************************************/ +/*! + \Function ds_memclr + + \Description + Memset that always clears the memory with zero + + \Input *pMem - memory that is being cleared + \Input iCount - the number of bytes to clear + + \Version 01/06/2017 (eesponda) +*/ +/********************************************************************************F*/ +void ds_memclr(void *pMem, int32_t iCount) +{ + memset(pMem, 0, iCount); +} + +/*F********************************************************************************/ +/*! + \Function ds_memset + + \Description + Replacement for memset + + \Input *pMem - memory that is being cleared + \Input iValue - value to clear the memory with + \Input iCount - the number of bytes to clear + + \Version 01/06/2017 (eesponda) +*/ +/********************************************************************************F*/ +void ds_memset(void *pMem, int32_t iValue, int32_t iCount) +{ + memset(pMem, iValue, iCount); +} diff --git a/r5dev/thirdparty/dirtysdk/source/platform/plat-time.c b/r5dev/thirdparty/dirtysdk/source/platform/plat-time.c new file mode 100644 index 00000000..3cab94b9 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/platform/plat-time.c @@ -0,0 +1,720 @@ +/*H********************************************************************************/ +/*! + \File plat-time.c + + \Description + This module implements variations on the standard library time functions. + The originals had a number of problems with internal static storage, + bizarre parameter passing (pointers to input values instead of the + actual value) and time_t which is different on different platforms. + + All these functions work with uint32_t values which provide a time + range of 1970-2107. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 01/11/2005 (gschaefer) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" + +#if defined(DIRTYCODE_PS4) +#include +#elif defined(DIRTYCODE_PC) || defined(DIRTYCODE_XBOXONE) +#include +#include +#include +#include +#elif defined (DIRTYCODE_LINUX) || defined(DIRTYCODE_ANDROID) || defined(DIRTYCODE_APPLEIOS) || defined(DIRTYCODE_APPLEOSX) +#include +#endif + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +static const char *_PlatTime_strWday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; +static const char *_PlatTime_strMonth[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; + +/*** Private functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _ds_strtoint + + \Description + Converts a string number to an integer. Does not support sign. + + \Input *pStr - pointer to int to read + \Input *pValue - [out] storage for result + \Input iMaxDigits - max number of digits to convert + + \Output + const char * - pointer past end of number + + \Version 12/13/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_ds_strtoint(const char *pStr, int32_t *pValue, int32_t iMaxDigits) +{ + int32_t iNum, iDigit; + for (iNum = 0, iDigit = 0; ((*pStr >= '0') && (*pStr <= '9') && (iDigit < iMaxDigits)); pStr += 1, iDigit += 1) + { + iNum = (iNum * 10) + (*pStr & 15); + } + *pValue = iNum; + return(pStr); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ds_timeinsecs + + \Description + Return time in seconds, or zero if not available + + \Output uint64_t - number of elapsed seconds since Jan 1, 1970 + + \Version 01/12/2005 (gschaefer) +*/ +/********************************************************************************F*/ +uint64_t ds_timeinsecs(void) +{ + return(NetTime()); +} + +/*F********************************************************************************/ +/*! + \Function ds_timezone + + \Description + This function returns the current system timezone as its offset from GMT in + seconds. There is no direct equivilent function in the standard C libraries. + + \Output int32_t - local timezone offset from GMT in seconds + + \Notes + The intent is to determine the timezone, so the offset intentionally does + not take daylight savings into account. + Do not use for GMT time determination. + + \Version 11/06/2002 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ds_timezone(void) +{ + time_t iGmt, iLoc; + static int32_t iZone = -1; + + // just calc the timezone one time + if (iZone == -1) + { + time_t uTime = time(0); + struct tm *pTm, TmTime; + + // convert to gmt time + pTm = gmtime(&uTime); + iGmt = (uint32_t)mktime(pTm); + + // convert to local time + pTm = ds_localtime(&TmTime,(uint32_t)uTime); + iLoc = (uint32_t)mktime(pTm); + + // calculate timezone difference + iZone = (int32_t)(iLoc - iGmt); + } + + // return the timezone offset in seconds + return(iZone); +} + +/*F********************************************************************************/ +/*! + \Function ds_localtime + + \Description + This converts the input GMT time to the local time as specified by the + system clock. This function follows the re-entrant localtime_r function + signature. + + \Input *pTm - storage for localtime output + \Input elap - GMT time + + \Output + struct tm * - pointer to localtime result + + \Version 04/23/2008 (jbrookes) +*/ +/********************************************************************************F*/ +struct tm *ds_localtime(struct tm *pTm, uint64_t elap) +{ + return(NetLocalTime(pTm, elap)); +} + +/*F********************************************************************************/ +/*! + \Function ds_secstotime + + \Description + Convert elapsed seconds to discrete time components. This is essentially a + ds_localtime() replacement with better syntax that is available on all platforms. + + \Input *pTm - target component record + \Input elap - epoch time input + + \Output struct tm * - returns tm if successful, NULL if failed + + \Version 01/23/2000 (gschaefer) +*/ +/********************************************************************************F*/ +struct tm *ds_secstotime(struct tm *pTm, uint64_t elap) +{ + int32_t year, leap, next, days, secs; + const int32_t *mon; + + // table to find days per month + static const int32_t dayspermonth[24] = { + 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, // leap + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // norm + }; + // table to find day of the week + static const int32_t wday[] = { + 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 + }; + + // divide out secs within day and days + // (we ignore leap-seconds cause it requires tables and nasty stuff) + days = (int32_t)(elap / (24*60*60)); + secs = (int32_t)(elap % (24*60*60)); + + // store the time info + pTm->tm_sec = secs % 60; + secs /= 60; + pTm->tm_min = secs % 60; + secs /= 60; + pTm->tm_hour = secs; + + // determine what year we are in + for (year = 1970;; year = next) { + // calc the length of the year in days + leap = (((year & 3) == 0) && (((year % 100 != 0) || ((year % 400) == 0))) ? 366 : 365); + // see if date is within this year + if (days < leap) + break; + + // estimate target year assuming every year is a leap year + // (this may underestimate the year, but will never overestimate) + next = year + (days / 366); + + // make sure year got changed and force if it did not + /// (can happen on dec 31 of non-leap year) + if (next == year) + ++next; + + // subtract out normal days/year + days -= (next - year) * 365; + // add in leap years from previous guess + days += ((year-1)/4 - (year-1)/100 + (year-1)/400); + // subtract out leap years since new guess + days -= ((next-1)/4 - (next-1)/100 + (next-1)/400); + } + + // save the year and day within year + pTm->tm_year = year - 1900; + pTm->tm_yday = days; + // calc the month + mon = dayspermonth + 12*(leap&1); + for (pTm->tm_mon = 0; days >= *mon; pTm->tm_mon += 1) + days -= *mon++; + // save the days + pTm->tm_mday = days + 1; + // calculate weekday using Sakamoto's algorithm, adjusted for m=[0...11] + year -= pTm->tm_mon < 2; + pTm->tm_wday = (year + year/4 - year/100 + year/400 + wday[pTm->tm_mon] + pTm->tm_mday) % 7; + // clear dst + pTm->tm_isdst = 0; + + // return pointer to argument to make it closer to ds_localtime() + return(pTm); +} + +/*F********************************************************************************/ +/*! + \Function ds_timetosecs + + \Description + Convert discrete components to elapsed time. + + \Input *pTm - source component record + + \Output + uint32_t - zero=failure, else epoch time + + \Version 01/23/2000 (gschaefer) +*/ +/********************************************************************************F*/ +uint64_t ds_timetosecs(const struct tm *pTm) +{ + uint64_t min, max, mid; + struct tm cmp; + int32_t res; + + /* do a binary search using ds_secstotime to encode prospective time_t values. + though iterative, it requires a max of 32 iterations which is actually pretty + good considering the complexity of the calculation (this allows all the + time nastiness to remain in a single function). */ + + /* perform binary search (set max to 1/1/3000 to reduce 64-bit search space + and prevent overflow in 32bit days/secs calculations in ds_secstotime */ + for (min = 0, mid = 0, res = 0, max = 32503680000; min <= max; ) + { + /* test at midpoint -- since these are large unsigned values, they can overflow + in the (min+max)/2 case hense the individual divide and lost bit recovery */ + mid = (min/2)+(max/2)+(min&max&1); + // do the time conversion + ds_secstotime(&cmp, mid); + // do the compare + if ((res = (cmp.tm_year - pTm->tm_year)) == 0) { + if ((res = (cmp.tm_mon - pTm->tm_mon)) == 0) { + if ((res = (cmp.tm_mday - pTm->tm_mday)) == 0) { + if ((res = (cmp.tm_hour - pTm->tm_hour)) == 0) { + if ((res = (cmp.tm_min - pTm->tm_min)) == 0) { + if ((res = cmp.tm_sec - pTm->tm_sec) == 0) { + // got an exact match! + break; + } + } + } + } + } + } + + // force break once min/max converge (cannot do this within for condition as res will not be setup correctly) + if (min == max) + break; + + // narrow the search range + if (res > 0) + max = mid-1; + else + min = mid+1; + } + + // return converted time or zero if failed + return((res == 0) ? mid : 0); +} + +/*F********************************************************************************/ +/*! + \Function ds_plattimetotime + + \Description + This converts the input platform-specific time data structure to the + generic time data structure. + + \Input *pTm - generic time data structure to be filled by the function + \Input *pPlatTime - pointer to the platform-specific data structure + + \Output + struct tm * - NULL=failure; else pointer to user-provided generic time data structure + + \Notes + See NetPlattimeToTime() for input data format + + \Version 05/08/2010 (mclouatre) +*/ +/********************************************************************************F*/ +struct tm *ds_plattimetotime(struct tm *pTm, void *pPlatTime) +{ + return(NetPlattimeToTime(pTm, pPlatTime)); +} + +/*F********************************************************************************/ +/*! + \Function ds_plattimetotimems + + \Description + This function retrieves the current date time and fills in the + generic time data structure prrovided. It has the option of returning millisecond + which is not part of the generic time data structure + + \Input *pTm - generic time data structure to be filled by the function + \Input *pImSec - output param for milisecond to be filled by the function (optional can be NULL) + + \Output + struct tm * - NULL=failure; else pointer to user-provided generic time data structure + + \Version 09/16/2014 (tcho) +*/ +/********************************************************************************F*/ +struct tm *ds_plattimetotimems(struct tm *pTm , int32_t *pImSec) +{ + return(NetPlattimeToTimeMs(pTm, pImSec)); +} + +/*F********************************************************************************/ +/*! + \Function ds_timetostr + + \Description + Converts a date formatted in a number of common Unix and Internet formats + and convert to a struct tm. + + \Input *pTm - input time stucture to be converted to string + \Input eConvType - user-selected conversion type + \Input bLocalTime - whether input time is local time or UTC 0 offset time. + \Input *pStrBuf - user-provided buffer to be filled with datetime string + \Input iBufSize - size of output buffer (must be at least 18 bytes to receive null-terminated yyyy-MM-ddTHH:mm:ssZ + + \Output + char * - zero=failure, else epoch time + + \Version 07/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_timetostr(const struct tm *pTm, TimeToStringConversionTypeE eConvType, uint8_t bLocalTime, char *pStrBuf, int32_t iBufSize) +{ + switch(eConvType) + { + case TIMETOSTRING_CONVERSION_ISO_8601: + ds_snzprintf(pStrBuf, iBufSize, "%04d-%02d-%02dT%02d:%02d:%02d%s", + pTm->tm_year+1900, pTm->tm_mon+1, pTm->tm_mday, + pTm->tm_hour, pTm->tm_min, pTm->tm_sec, + !bLocalTime ? "Z" : ""); + break; + case TIMETOSTRING_CONVERSION_ISO_8601_BASIC: + ds_snzprintf(pStrBuf, iBufSize, "%04d%02d%02dT%02d%02d%02d%s", + pTm->tm_year+1900, pTm->tm_mon+1, pTm->tm_mday, + pTm->tm_hour, pTm->tm_min, pTm->tm_sec, + !bLocalTime ? "Z" : ""); + break; + case TIMETOSTRING_CONVERSION_RFC_0822: // e.g. Wed, 15 Nov 1995 04:58:08 GMT + { + uint32_t tm_wday = ((unsigned)pTm->tm_wday < 7) ? pTm->tm_wday : 7; + uint32_t tm_mon = ((unsigned)pTm->tm_mon < 12) ? pTm->tm_mon : 12; + ds_snzprintf(pStrBuf, iBufSize, "%s, %2d %s %4d %02d:%02d:%02d GMT", _PlatTime_strWday[tm_wday], pTm->tm_mday, + _PlatTime_strMonth[tm_mon], pTm->tm_year+1900, pTm->tm_hour, pTm->tm_min, pTm->tm_sec); + break; + } + default: + // unsupported conversion type + pStrBuf = NULL; + break; + } + + return(pStrBuf); +} + +/*F********************************************************************************/ +/*! + \Function ds_secstostr + + \Description + Converts a datetime in epoch format to string + + \Input elap - epoch time to convert to string + \Input eConvType - user-selected conversion type + \Input bLocalTime - whether input time is local time or UTC 0 offset time. + \Input *pStrBuf - user-provided buffer to be filled with datetime string + \Input iBufSize - size of output buffer (must be at least 18 bytes to receive null-terminated yyyy-MM-ddTHH:mm:ssZ + + \Output + char * - zero=failure, else epoch time + + \Version 02/26/2014 (jbrookes) +*/ +/********************************************************************************F*/ +char *ds_secstostr(uint64_t elap, TimeToStringConversionTypeE eConvType, uint8_t bLocalTime, char *pStrBuf, int32_t iBufSize) +{ + struct tm TmTime; + return(ds_timetostr(ds_secstotime(&TmTime, elap), eConvType, bLocalTime, pStrBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function ds_strtotime + + \Description + Converts a date formatted in a number of common Unix and Internet formats + and convert to a struct tm. + + \Input *pStr - textual date string + + \Output uint32_t - zero=failure, else epoch time + + \Notes + \verbatim + The following time formats are supported: + Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 + Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format + + The following format is mentioned as needing to be readable by HTTP + but is not currently supported by this function: + Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 + \endverbatim + + \Version 11/01/2002 (gschaefer) +*/ +/********************************************************************************F*/ +uint64_t ds_strtotime(const char *pStr) +{ + int32_t i; + const char *s; + struct tm tm; + + // reset the fields + ds_memset(&tm, -1, sizeof(tm)); + + // skip past any white space + while ((*pStr != 0) && (*pStr <= ' ')) + { + pStr++; + } + + // see if starts with day of week (RFC 822/1123, asctime) + for (i = 0; (s=_PlatTime_strWday[i]) != 0; ++i) + { + if ((pStr[0] == s[0]) && (pStr[1] == s[1]) && (pStr[2] == s[2])) + { + tm.tm_wday = i; + // skip name of weekday + while ((*pStr != ',') && (*pStr != ' ') && (*pStr != 0)) + ++pStr; + // skip the divider + while ((*pStr == ',') || (*pStr == ' ')) + ++pStr; + break; + } + } + + // check for mmm dd (asctime) + if ((*pStr < '0') || (*pStr > '9')) + { + for (i = 0; (s=_PlatTime_strMonth[i]) != 0; ++i) + { + // look for a match + if ((pStr[0] != s[0]) || (pStr[1] != s[1]) || (pStr[2] != s[2])) + continue; + // found the month + tm.tm_mon = i; + // skip to the digits + while ((*pStr != 0) && ((*pStr < '0') || (*pStr > '9'))) + ++pStr; + // get the day of month + for (i = 0; ((*pStr >= '0') && (*pStr <= '9')); ++pStr) + i = (i * 10) + (*pStr & 15); + if (i > 0) + tm.tm_mday = i; + break; + } + } + + // check for dd mmm (RFC 822/1123) + if ((tm.tm_mon < 0) && (pStr[0] >= '0') && (pStr[0] <= '9') && + ((pStr[1] > '@') || (pStr[2] > '@') || (pStr[3] > '@'))) + { + // get the day + for (i = 0; ((*pStr >= '0') && (*pStr <= '9')); ++pStr) + i = (i * 10) + (*pStr & 15); + tm.tm_mday = i; + while (*pStr < '@') + ++pStr; + // get the month + for (i = 0; (s=_PlatTime_strMonth[i]) != 0; ++i) + { + // look for a match + if ((pStr[0] != s[0]) || (pStr[1] != s[1]) || (pStr[2] != s[2])) + continue; + tm.tm_mon = i; + while ((*pStr != 0) && (*pStr != ' ')) + ++pStr; + break; + } + } + + // check for xx/xx or xx/xx/xx (???) + if ((*pStr >= '0') && (*pStr <= '9') && (tm.tm_mon < 0)) + { + // get the month + for (i = 0; ((*pStr >= '0') && (*pStr <= '9')); ++pStr) + i = (i * 10) + (*pStr & 15); + tm.tm_mon = i - 1; + if (*pStr != 0) + ++pStr; + // get the day + for (i = 0; ((*pStr >= '0') && (*pStr <= '9')); ++pStr) + i = (i * 10) + (*pStr & 15); + tm.tm_mday = i; + if (*pStr != 0) + ++pStr; + } + + // check for year (RFC 822/1123) + while ((*pStr != 0) && ((*pStr < '0') || (*pStr > '9'))) + ++pStr; + // see if the year is here + if ((pStr[0] >= '0') && (pStr[0] <= '9') && (pStr[1] != ':') && (pStr[2] != ':')) + { + for (i = 0; ((*pStr >= '0') && (*pStr <= '9')); ++pStr) + i = (i * 10) + (*pStr & 15); + if (i > 999) + tm.tm_year = i; + else if (i >= 50) + tm.tm_year = 1900+i; + else + tm.tm_year = 2000+i; + // find next digit sequence + while ((*pStr != 0) && ((*pStr < '0') || (*pStr > '9'))) + ++pStr; + } + + // save the hour (RFC 822/1123, asctime) + if ((*pStr >= '0') && (*pStr <= '9')) + { + i = (*pStr++ & 15); + if ((*pStr >= '0') && (*pStr <= '9')) + i = (i * 10) + (*pStr++ & 15); + tm.tm_hour = i; + if (*pStr == ':') + ++pStr; + } + + // save the minute (RFC 822/1123, asctime) + if ((*pStr >= '0') && (*pStr <= '9')) + { + i = (*pStr++ & 15); + if ((*pStr >= '0') && (*pStr <= '9')) + i = (i * 10) + (*pStr++ & 15); + tm.tm_min = i; + if (*pStr == ':') + ++pStr; + } + + // save the second (if present) (RFC 822/1123, asctime) + if ((*pStr >= '0') && (*pStr <= '9')) + { + i = (*pStr++ & 15); + if ((*pStr >= '0') && (*pStr <= '9')) + i = (i * 10) + (*pStr++ & 15); + tm.tm_sec = i; + } + + // see if year is still remaining (asctime) + if (tm.tm_year < 0) + { + // see if any digits left + while ((*pStr != 0) && ((*pStr < '0') || (*pStr > '9'))) + ++pStr; + for (i = 0; ((*pStr >= '0') && (*pStr <= '9')); ++pStr) + i = (i * 10) + (*pStr & 15); + if (i > 999) + tm.tm_year = i; + } + + // make year relative to 1900 (really dumb) + if (tm.tm_year > 1900) + tm.tm_year -= 1900; + + // convert from struct tm to uint32_t and return to caller + return(ds_timetosecs(&tm)); +} + +/*F********************************************************************************/ +/*! + \Function ds_strtotime2 + + \Description + Converts a date formatted in a number of common Unix and Internet formats + and convert to a struct tm. + + \Input *pStr - textual date string + \Input eConvType - time format to convert from + + \Output + uint32_t - zero=failure, else epoch time + + \Notes + For supported conversion types other than ISO_8601, see documentation + for ds_strtotime(). + + \Version 12/13/2012 (jbrookes) +*/ +/********************************************************************************F*/ +uint64_t ds_strtotime2(const char *pStr, TimeToStringConversionTypeE eConvType) +{ + uint64_t uTime = 0; + struct tm tm; + + if (eConvType == TIMETOSTRING_CONVERSION_ISO_8601) + { + // format: YYYY-MM-DDTHH:MM:SSZ + if (strlen(pStr) < 19) // 'Z' is optional and ignored + { + return(0); + } + // read the date/time + pStr = _ds_strtoint(pStr, &tm.tm_year, 4) + 1; // get the year, skip hyphen + pStr = _ds_strtoint(pStr, &tm.tm_mon, 2) + 1; // get the month, skip hyphen + pStr = _ds_strtoint(pStr, &tm.tm_mday, 2) + 1; // get the day, skip 'T' + pStr = _ds_strtoint(pStr, &tm.tm_hour, 2) + 1; // get the hour, skip ':' + pStr = _ds_strtoint(pStr, &tm.tm_min, 2) + 1; // get the minute, skip ':' + _ds_strtoint(pStr, &tm.tm_sec, 2); // get the second + // adjust year and month + tm.tm_year -= 1900; + tm.tm_mon -= 1; + // convert from struct tm to uint32_t and return to caller + uTime = ds_timetosecs(&tm); + } + else if ((eConvType == TIMETOSTRING_CONVERSION_ASN1_UTCTIME) || (eConvType == TIMETOSTRING_CONVERSION_ASN1_GENTIME)) + { + /* GeneralizedTime: .fff is optional + Local Time: YYYYMMDDHHMMSS.fff + UTC Time: YYYYMMDDHHMMSS.fffZ + Difference Time: YYYYMMDDHHMMSS.fff+-HHMM + UTCTime: like GeneralizedTime, but accuracy is to one minute or one second (no .fff, SS is optional, Z is implicit and not included). + Local Time: YYYYMMDDHHMMSS + UTC Time: YYMMDDHHMMSS + Difference Time: YYYYMMDDHHMMSS+-HHMM + Note: Fractional time, difference time, and UTC character, if present, are ignored */ + if (eConvType == TIMETOSTRING_CONVERSION_ASN1_UTCTIME) + { + pStr = _ds_strtoint(pStr, &tm.tm_year, 2); + tm.tm_year += (tm.tm_year < 70) ? 2000 : 1900; // 2-year UTC time represents from 1970 to 2069 + } + else + { + pStr = _ds_strtoint(pStr, &tm.tm_year, 4); + } + pStr = _ds_strtoint(pStr, &tm.tm_mon, 2); + pStr = _ds_strtoint(pStr, &tm.tm_mday, 2); + pStr = _ds_strtoint(pStr, &tm.tm_hour, 2); + pStr = _ds_strtoint(pStr, &tm.tm_min, 2); + _ds_strtoint(pStr, &tm.tm_sec, 2); + // adjust year and month + tm.tm_year -= 1900; + tm.tm_mon -= 1; + // convert from struct tm to uint32_t and return to caller + uTime = ds_timetosecs(&tm); + } + else + { + uTime = ds_strtotime(pStr); + } + return(uTime); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protoadvt.c b/r5dev/thirdparty/dirtysdk/source/proto/protoadvt.c new file mode 100644 index 00000000..6ce21eee --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protoadvt.c @@ -0,0 +1,918 @@ +/*H*************************************************************************************/ +/*! + \File protoadvt.c + + \Description + This advertising module provides a relatively simple multi-protocol + distributed name server architecture utilizing the broadcast capabilities + of UDP and IPX. Once the module is instantiated, it can be used as both + an advertiser (server) and watcher (client) simultaneously. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 02/17/99 (GWS) Original version + \Version 1.1 02/25/99 (GWS) Alpha release + \Version 1.2 07/27/99 (GWS) Initial release + \Version 1.3 10/28/99 (GWS) Message queue elimination + \Version 1.4 07/10/00 (GWS) Windock2 dependency removal + \Version 1.5 12/11/00 (GWS) Ported to Dirtysock + \Version 2.0 03/28/03 (GWS) Made more robust with double-broadcast sends + \Version 2.1 06/16/03 (JLB) Made string comparisons case-insensitive +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/proto/protoadvt.h" + +#if defined(DIRTYCODE_PS4) +#include +#endif +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +//! raw broadcast packet format +typedef struct ProtoAdvtPacketT +{ + //! static packet identifier + uint8_t ident[3]; + //! single char freq (avoid byte ordering issues) + uint8_t freq; + //! advertisement sequence number + uint8_t seqn[4]; + //! the kind of service + char kind[32]; + //! the name of this particular service + char name[32]; + //! misc notes about service + char note[192]; + //! list of service addresses + char addr[120]; +} ProtoAdvtPacketT; + +//! a list of services +typedef struct ServListT +{ + //! actual packet data + ProtoAdvtPacketT packet; + //! timeout until item expires or is rebroadcast + uint32_t timeout; + //! last internet address associated with service + char inetdot[16]; + uint32_t inetval; + uint32_t hostval; + //! flag to indicate if packet has local origin + int32_t local; + //! link to next item in list + struct ServListT *next; +} ServListT; + +//! local module reference +struct ProtoAdvtRef +{ + //! control access to resources + NetCritT crit; + //! list of services to announce + ServListT *announce; + //! known service list + ServListT *snoopbuf; + ServListT *snoopend; + //! dirtysock memory group + int32_t memgroup; + void *memgrpusrdata; + //! seeding is needed + int32_t seed; + //! number of users + int32_t usage; + //! active socket + SocketT *sock; + //! broadcast address + struct sockaddr addr; + //! indicate that snoop buffer changed + int32_t snoop; +}; + + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +static int32_t g_count = 0; +static ProtoAdvtRef *g_ref = NULL; + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function _ProtoAdvtSendPacket + + \Description + Sends a ProtoAdvt packet. + + \Input *pRef - module ref + \Input *pPacket - packet to send + + \Output + int32_t - return result from SocketSendto() + + \Version 11/30/2006 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _ProtoAdvtSendPacket(ProtoAdvtRef *pRef, const ProtoAdvtPacketT *pPacket) +{ + int32_t iPacketSize = sizeof(*pPacket); + return(SocketSendto(pRef->sock, (const char *)pPacket, iPacketSize, 0, &pRef->addr, sizeof(pRef->addr))); +} + +/*F*************************************************************************************/ +/*! + \Function _ProtoAdvtRecvPacket + + \Description + Receives a ProtoAdvt packet. Variable-length string fields are unpacked from + the compacted sent packet to produce the fixed-length result packet. + + \Input *pRef - module ref + \Input *pPacket - packet buffer to receive to + \Input *pFrom - sender's address + + \Output + int32_t - return result from SocketRecvfrom() + + \Version 11/30/2006 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _ProtoAdvtRecvPacket(ProtoAdvtRef *pRef, ProtoAdvtPacketT *pPacket, struct sockaddr *pFrom) +{ + int32_t iFromLen = sizeof(*pFrom); + return(SocketRecvfrom(pRef->sock, (char *)pPacket, sizeof(*pPacket), 0, pFrom, &iFromLen)); +} + +/*F*************************************************************************************/ +/*! + \Function _ProtoAdvtRequestSeed + + \Description + Send a seed request + + \Input *pRef - module ref + + \Version 03/28/03 (GWS) +*/ +/*************************************************************************************F*/ +static void _ProtoAdvtRequestSeed(ProtoAdvtRef *pRef) +{ + ProtoAdvtPacketT Packet; + + // setup a query packet + ds_memclr(&Packet, sizeof(Packet)); + Packet.ident[0] = ADVERT_PACKET_IDENTIFIER[0]; + Packet.ident[1] = ADVERT_PACKET_IDENTIFIER[1]; + Packet.ident[2] = ADVERT_PACKET_IDENTIFIER[2]; + Packet.kind[0] = '?'; + + // do the send + _ProtoAdvtSendPacket(pRef, &Packet); +} + + +/*F*************************************************************************************/ +/*! + \Function _ProtoAdvtCallback + + \Description + Main worker callback + + \Input *sock - connection socket pointer + \Input flags - unused + \Input *_ref - ProtoAdvtRef pointer + + \Output int32_t - zero + + \Version 11/01/02 (GWS) +*/ +/*************************************************************************************F*/ +static int32_t _ProtoAdvtCallback(SocketT *sock, int32_t flags, void *_ref) +{ + int32_t len; + int32_t local; + struct sockaddr base; + struct sockaddr from; + ServListT *list, *item, **link; + ProtoAdvtPacketT packet; + ProtoAdvtRef *ref = (ProtoAdvtRef *) _ref; + uint32_t tick; + + // make sure we own resources + if (!NetCritTry(&ref->crit)) + { + NetPrintf(("protoadvt: could not acquire critical section\n")); + return(0); + } + + // process all pending packets + while (ref->sock != NULL) + { + // request seeding if needed + if (ref->seed > 0) + { + ref->seed = 0; + _ProtoAdvtRequestSeed(ref); + } + + // try and receive a packet + packet.kind[0] = '\0'; + SockaddrInit(&from, AF_INET); + len = _ProtoAdvtRecvPacket(ref, &packet, &from); + if ((len <= 0) || (packet.kind[0] == '\0')) + { + break; + } + + // get local address we would have used + SockaddrInit(&base, AF_INET); + SocketHost(&base, sizeof(base), &from, sizeof(from)); + + // see if packet was sent locally + local = 0; + if ((base.sa_family == from.sa_family) && (from.sa_family == AF_INET)) + local = (memcmp(&from.sa_data, &base.sa_data, 6) == 0); + + // ignore packets without proper identifier + if ((packet.ident[0] != ADVERT_PACKET_IDENTIFIER[0]) || + (packet.ident[1] != ADVERT_PACKET_IDENTIFIER[1]) || + (packet.ident[2] != ADVERT_PACKET_IDENTIFIER[2])) + { + continue; + } + + // handle seed request first + if ((packet.kind[0] == '?') && (packet.kind[1] == 0)) + { + tick = NetTick() + 1000; + // reduce timeouts for outgoing packets + for (list = ref->announce; list != NULL; list = list->next) + { + // reduce timeout down to 1 second + // (helps prevent multiple seed requests from causing storm) + if (list->timeout > tick) + list->timeout = tick; + } + // get next packet + continue; + } + + // make sure name & kind are set + if (packet.name[0] == 0) + { + continue; + } + + // process the packet + item = NULL; + for (list = ref->snoopbuf; list != ref->snoopend; ++list) + { + // check for unused block + if ((item == NULL) && (list->packet.name[0] == 0)) + { + item = list; + } + // see if we already have this packet + if ((ds_stricmp(packet.kind, list->packet.kind) == 0) && + (ds_stricmp(packet.name, list->packet.name) == 0) && + (memcmp(packet.seqn, list->packet.seqn, sizeof(packet.seqn)) == 0)) + { + break; + } + } + + // see if this is a new packet + if ((list == ref->snoopend) && (item != NULL)) + { + list = item; + ds_memclr(list, sizeof(*list)); + // populate with kind/name/seqn (never change) + ds_memcpy_s(list->packet.seqn, sizeof(list->packet.seqn), packet.seqn, sizeof(packet.seqn)); + ds_strnzcpy(list->packet.kind, packet.kind, sizeof(list->packet.kind)); + ds_strnzcpy(list->packet.name, packet.name, sizeof(list->packet.name)); + ds_strnzcpy(list->packet.note, packet.note, sizeof(list->packet.note)); + // indicate snoop buffer changed + ref->snoop += 1; + NetPrintf(("protoadvt: added new advert name=%s freq=%d addr=%a\n", list->packet.name, packet.freq, SockaddrInGetAddr(&from))); + } + + // update the record (assuming we found/allocated) + if (list != ref->snoopend) + { + ds_strnzcpy(list->packet.addr, packet.addr, sizeof(list->packet.addr)); + ds_strnzcpy(list->packet.note, packet.note, sizeof(list->packet.note)); + list->timeout = NetTick()+2*(packet.freq*1000)+1000; + list->local = local; + list->inetval = SockaddrInGetAddr(&from); + list->hostval = SockaddrInGetAddr(&base); + SockaddrInGetAddrText(&from, list->inetdot, sizeof(list->inetdot)); + // indicate snoop buffer changed + ref->snoop += 1; + } + // loop checks for another packet + } + + // check for expired services + tick = NetTick(); + for (list = ref->snoopbuf; list != ref->snoopend; ++list) + { + if (list->packet.name[0] != 0) + { + // see if anything has expired + if ((list->packet.addr[0] == 0) || (list->timeout == 0) || (tick > list->timeout)) + { + NetPrintf(("protoadvt: expiring advert name=%s\n", list->packet.name)); + list->packet.name[0] = 0; + list->packet.kind[0] = 0; + // indicate snoop buffer changed + ref->snoop += 1; + } + } + } + + // check for expired announcements + for (link = &ref->announce; (*link) != NULL;) + { + // see if it has expired + if ((*link)->timeout == 0) + { + item = *link; + *link = (*link)->next; + // send out a cancelation notice + NetPrintf(("protoadvt: canceling announcement name=%s\n", item->packet.name)); + item->packet.addr[0] = '\0'; + // send out notices + _ProtoAdvtSendPacket(ref, &item->packet); + // done with the record + DirtyMemFree(item, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata); + } + else + { + // move to next item + link = &(*link)->next; + } + } + + // see if anything needs to be sent + tick = NetTick(); + for (list = ref->announce; list != NULL; list = list->next) + { + // see if its time to send + if (tick > list->timeout) + { + // broadcast packet + _ProtoAdvtSendPacket(ref, &list->packet); + // update send time + list->timeout = tick + list->packet.freq*1000; + } + } + + // done with update + NetCritLeave(&ref->crit); + return(0); +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function ProtoAdvtAnnounce + + \Description + Advertise a service as available + + \Input *ref - reference pointer + \Input *kind - service class (max 32 characters including NUL) + \Input *name - service name (usually game name, max 32 characters including NUL) + \Input *note - service note (max 32 characters including NUL) + \Input *addr - service address list (max 120 characters, including NUL) + \Input freq - announcement frequency, in seconds (can be in the range [2...250]) + + \Output + int32_t - <0 = error, 0=ok + + \Version 11/01/02 (GWS) +*/ +/*************************************************************************************F*/ +int32_t ProtoAdvtAnnounce(ProtoAdvtRef *ref, const char *kind, const char *name, + const char *note, const char *addr, int32_t freq) +{ + ServListT *list; + ServListT *item; + + // clamp the freqeuncy to valid range + if (freq == 0) + freq = 30; + if (freq < 2) + freq = 2; + if (freq > 250) + freq = 250; + + // validate input strings + if ((kind == NULL) || (kind[0] == '\0')) + { + NetPrintf(("protoadvt: error, invalid kind\n")); + return(-1); + } + if ((name == NULL) || (name[0] == '\0')) + { + NetPrintf(("protoadvt: error, invalid name\n")); + return(-2); + } + if (note == NULL) + { + NetPrintf(("protoadvt: error, invalid note\n")); + return(-3); + } + if (addr == NULL) + { + NetPrintf(("protoadvt: error, invalid addr\n")); + return(-4); + } + + // see if service is already listed + for (list = ref->announce; list != NULL; list = list->next) + { + // check for dupl + if ((ds_stricmp(kind, list->packet.kind) == 0) && + (ds_stricmp(name, list->packet.name) == 0)) + { + // update the addr field if necessary + if (ds_stricmp(addr, list->packet.addr) != 0) + { + // update address & force immediate broadcast of new info + ds_strnzcpy(list->packet.addr, addr, sizeof(list->packet.addr)); + list->timeout = NetTick()-1; + } + + // update the note field if necessary + if (ds_stricmp(note, list->packet.note) != 0) + { + // update note & force immediate broadcast of new info + ds_strnzcpy(list->packet.note, note, sizeof(list->packet.note)); + list->timeout = NetTick()-1; + } + // all done + return(0); + } + } + + // build a packet + item = DirtyMemAlloc(sizeof(*item), PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata); + ds_memclr(item, sizeof(*item)); + item->timeout = NetTick(); + item->packet.ident[0] = ADVERT_PACKET_IDENTIFIER[0]; + item->packet.ident[1] = ADVERT_PACKET_IDENTIFIER[1]; + item->packet.ident[2] = ADVERT_PACKET_IDENTIFIER[2]; + item->packet.freq = (unsigned char) freq; + item->packet.seqn[0] = (unsigned char)(item->timeout >> 24); + item->packet.seqn[1] = (unsigned char)(item->timeout >> 16); + item->packet.seqn[2] = (unsigned char)(item->timeout >> 8); + item->packet.seqn[3] = (unsigned char)(item->timeout >> 0); + ds_strnzcpy(item->packet.kind, kind, sizeof(item->packet.kind)); + ds_strnzcpy(item->packet.name, name, sizeof(item->packet.name)); + ds_strnzcpy(item->packet.addr, addr, sizeof(item->packet.addr)); + ds_strnzcpy(item->packet.note, note, sizeof(item->packet.note)); + NetPrintf(("protoadvt: broadcasting new announcement name=%s freq=%d\n", item->packet.name, item->packet.freq)); + + // send an immediate copy + _ProtoAdvtSendPacket(ref, &item->packet); + // schedule next send in 250ms + item->timeout = NetTick()+250; + + // make sure we own resources + NetCritEnter(&ref->crit); + // add new item to list + item->next = ref->announce; + ref->announce = item; + // release resources + NetCritLeave(&ref->crit); + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function ProtoAdvtCancel + + \Description + Cancel server advertisement + + \Input *ref - reference pointer + \Input *kind - service kind + \Input *name - service name (usually game name, NULL=wildcard) + + \Output + int32_t - <0=error, 0=ok + + \Version 11/01/02 (GWS) +*/ +/*************************************************************************************F*/ +int32_t ProtoAdvtCancel(ProtoAdvtRef *ref, const char *kind, const char *name) +{ + ServListT *list; + + // make sure we own resources + NetCritEnter(&ref->crit); + + // locate the item in the announcement list + for (list = ref->announce; list != NULL; list = list->next) + { + // see if we got a match + if (((kind == NULL) || (ds_stricmp(kind, list->packet.kind) == 0)) && + ((name == NULL) || (ds_stricmp(name, list->packet.name) == 0))) + { + // mark as deleted + list->timeout = 0; + break; + } + } + + // release resources + NetCritLeave(&ref->crit); + + // was item found? + return(list == NULL ? -1 : 0); +} + + +/*F*************************************************************************************/ +/*! + \Function ProtoAdvtQuery + + \Description + Query for available services + + \Input *ref - reference pointer + \Input *kind - service kind + \Input *proto - protocol + \Input *buffer - target buffer + \Input buflen - target length + \Input local - if zero, exclude local lists + + \Output + int32_t - number of matches + + \Version 11/01/02 (GWS) +*/ +/*************************************************************************************F*/ +int32_t ProtoAdvtQuery(ProtoAdvtRef *ref, const char *kind, const char *proto, + char *buffer, int32_t buflen, int32_t local) +{ + char *s, *t, *u; + char addr[256] = ""; + char record[512]; + int32_t count = 0; + ServListT *list; + + // establish min buffer size + if (buflen < 5) + return(-1); + + // default to empty buffer + buffer[0] = 0; + + // see what matching services we know about + for (list = ref->snoopbuf; list != ref->snoopend; ++list) + { + // skip empty entries + if (list->packet.name[0] == 0) + continue; + // see if the kind matches + if (ds_stricmp(kind, list->packet.kind) != 0) + continue; + // exclude locals if not wanted + if ((!local) && (list->local)) + continue; + // parse the address choices + for (s = list->packet.addr; *s != 0;) + { + // parse out the address + for (t = addr; (*s != 0) && (*s != '\t');) + *t++ = *s++; + *t++ = 0; + if (*s == '\t') + ++s; + // make sure address looks valid + if ((strlen(addr) < 5) || (addr[3] != ':')) + continue; + // see if this protocol type is wanted by caller + addr[3] = 0; + if ((proto[0] != 0) && (strstr(proto, addr) == NULL)) + continue; + addr[3] = ':'; + // compile data into a record + ds_strnzcpy(record, list->packet.name, sizeof(record)); + for (t = record; *t != 0; ++t) + ; + *t++ = '\t'; + // copy over notes field + strcpy(t, list->packet.note); + while (*t != 0) + ++t; + *t++ = '\t'; + // append the address + for (u = addr; *u != 0; ++u) + { + // check for inet substitution + if ((u[0] == '~') && (u[1] == '1')) + { + // make sure inet address is known + if (list->inetdot[0] == 0) + break; + // copy over the address + strcpy(t, list->inetdot); + while (*t != 0) + ++t; + ++u; + continue; + } + // check for ipx substitution + if ((u[0] == '~') && (u[1] == '2')) + { + // no ipx support + break; + } + // raw copy + *t++ = *u; + } + // see if copy was canceled (substitution error) + if (*u != 0) + continue; + // terminate the record + *t++ = '\n'; + *t = 0; + // see if the record will fit into output buffer + if (strlen(record)+5 > (unsigned) buflen) + { + // indicate an overflow + *buffer++ = '.'; + *buffer++ = '.'; + *buffer++ = '.'; + *buffer++ = '\n'; + *buffer = 0; + return(count); + } + // append the record to the buffer + strcpy(buffer, record); + buffer += (int32_t)strlen(record); + buflen -= (int32_t)strlen(record); + ++count; + } + } + + // return count of items + return(count); +} + + +/*F*************************************************************************************/ +/*! + \Function ProtoAdvtLocate + + \Description + Locate a specific advertisement and return advertisers address (UDP only) + + \Input *ref - reference pointer + \Input *kind - service kind (NULL=any) + \Input *name - service name (usually game name, NULL=wildcard) + \Input *host - pointer to buffer for host (our) address, or NULL + \Input defval - value returned if module is not set + + \Output + uint32_t - defval if ref is NULL, else first matching internet address + + \Version 11/01/02 (GWS) +*/ +/*************************************************************************************F*/ +uint32_t ProtoAdvtLocate(ProtoAdvtRef *ref, const char *kind, const char *name, + uint32_t *host, uint32_t defval) +{ + ServListT *list; + + // just return default if module is not set + if (ref == NULL) + return(defval); + + // see what matching services we know about + for (list = ref->snoopbuf; list != ref->snoopend; ++list) + { + // make sure service is valid + if (list->packet.name[0] == 0) + continue; + // exclude locals + if (list->local) + continue; + // see if the kind matches + if ((kind != NULL) && (kind[0] != 0) && (ds_stricmp(kind, list->packet.kind) != 0)) + continue; + // make sure the name matches + if ((name != NULL) && (name[0] != 0) && (ds_stricmp(list->packet.name, name) != 0)) + continue; + // make sure there is an internet address + if (list->inetval == 0) + continue; + // return host (our) address if they want it + if (host != NULL) + *host = list->hostval; + // return the internet address + defval = list->inetval; + break; + } + + // return default value + return(defval); +} + + +/*F*************************************************************************************/ +/*! + \Function ProtoAdvtConstruct + + \Description + Construct an advertising agent + + \Input limit - size of snoop buffer (min 4) + + \Output + ProtoAdvtRef * - construct ref (passed to other routines) + + \Version 11/01/02 (GWS) +*/ +/*************************************************************************************F*/ +ProtoAdvtRef *ProtoAdvtConstruct(int32_t limit) +{ + SocketT *sock; + struct sockaddr bindaddr; + struct sockaddr peeraddr; + ProtoAdvtRef *ref; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // see if already allocated + if (g_ref != NULL) + { + ++g_count; + return(g_ref); + } + + // allocate class storage + ref = DirtyMemAlloc(sizeof(*ref), PROTOADVT_MEMID, iMemGroup, pMemGroupUserData); + if (ref == NULL) + return(NULL); + ds_memclr(ref, sizeof(*ref)); + ref->memgroup = iMemGroup; + ref->memgrpusrdata = pMemGroupUserData; + + // allocate snooped advertisement buffer + if (limit < 4) + limit = 4; + ref->snoopbuf = DirtyMemAlloc(limit*sizeof(ref->snoopbuf[0]), PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata); + if (ref->snoopbuf == NULL) + { + DirtyMemFree(ref, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata); + return(NULL); + } + ref->snoopend = ref->snoopbuf+limit; + ds_memclr(ref->snoopbuf, (int32_t)((char *)ref->snoopend-(char *)ref->snoopbuf)); + + // set initial revision value + ref->snoop = 1; + + // create the actual socket + sock = SocketOpen(AF_INET, SOCK_DGRAM, 0); + if (sock == NULL) + { + DirtyMemFree(ref, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata); + return(NULL); + } + + // mark as available for sharing + g_ref = ref; + g_count = 1; + + // need to sync access to data + NetCritInit(&ref->crit, "protoadvt"); + // limit access during setup + NetCritEnter(&ref->crit); + + // bind with local address + SockaddrInit(&bindaddr, AF_INET); + SockaddrInSetPort(&bindaddr, ADVERT_BROADCAST_PORT_UDP); + SocketBind(sock, &bindaddr, sizeof(bindaddr)); + + // connect to remote address + SockaddrInit(&peeraddr, AF_INET); + SockaddrInSetPort(&peeraddr, ADVERT_BROADCAST_PORT_UDP); + SockaddrInSetAddr(&peeraddr, INADDR_BROADCAST); + + // note: though it would be easier to use connect to bind the peer address + // to the socket, it will not work because winsock will not deliver an + // incoming packet to a socket connected to the sending port. therefore, + // the address must be maintained separately and used with sendto. + // (logic unchanged for dirtysock since it is already coded this way) + ds_memcpy_s(&ref->addr, sizeof(ref->addr), &peeraddr, sizeof(peeraddr)); + + // broadcast request for available server info (seed database) + ref->seed = 1; + // make the socket available + ref->sock = sock; + // bind the callback function + SocketCallback(ref->sock, CALLB_RECV, 100, ref, &_ProtoAdvtCallback); + + // done with setup + NetCritLeave(&ref->crit); + + // immediately send a seed request + // (the idle process will send a second within 100ms) + _ProtoAdvtRequestSeed(ref); + return(ref); +} + + +/*F*************************************************************************************/ +/*! + \Function ProtoAdvtDestroy + + \Description + Destruct an advertising agent + + \Input *ref - construct ref + + \Version 11/01/02 (GWS) +*/ +/*************************************************************************************F*/ +void ProtoAdvtDestroy(ProtoAdvtRef *ref) +{ + SocketT *sock; + + // make sure what is valid + if (ref == NULL) + { + return; + } + + // see if we are last + if (g_count > 1) + { + --g_count; + return; + } + + // destroy the class + g_ref = NULL; + g_count = 0; + + // cancel all announcements + while (ref->announce != NULL) + { + ProtoAdvtPacketT *packet = &ref->announce->packet; + ProtoAdvtCancel(ref, packet->kind, packet->name); + _ProtoAdvtCallback(ref->sock, 0, ref); + } + + // make sure we own resources + NetCritEnter(&ref->crit); + + // do the shutdown + sock = ref->sock; + ref->sock = NULL; + + // release resources + NetCritLeave(&ref->crit); + + // dispose of socket + SocketClose(sock); + + // done with semaphore + NetCritKill(&ref->crit); + + // done with ref + DirtyMemFree(ref->snoopbuf, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata); + DirtyMemFree(ref, PROTOADVT_MEMID, ref->memgroup, ref->memgrpusrdata); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protohttp.c b/r5dev/thirdparty/dirtysdk/source/proto/protohttp.c new file mode 100644 index 00000000..27d11f25 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protohttp.c @@ -0,0 +1,3363 @@ +/*H********************************************************************************/ +/*! + \File protohttp.c + + \Description + This module implements an HTTP client that can perform basic transactions + (get/put) with an HTTP server. It conforms to but does not fully implement + the 1.1 HTTP spec (http://www.w3.org/Protocols/rfc2616/rfc2616.html), and + allows for secure HTTP transactions as well as insecure transactions. + + \Copyright + Copyright (c) Electronic Arts 2000-2004. ALL RIGHTS RESERVED. + + \Version 0.5 02/21/2000 (gschaefer) First Version + \Version 1.0 12/07/2000 (gschaefer) Added PS2/Dirtysock support + \Version 1.1 03/03/2004 (sbevan) Rewrote to use ProtoSSL, added limited Post support. + \Version 1.2 11/18/2004 (jbrookes) Refactored, updated to HTTP 1.1, added full Post support. +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protohttp.h" + +/*** Defines **********************************************************************/ + +//! default ProtoHttp timeout +#define PROTOHTTP_TIMEOUT_DEFAULT (30*1000) + +//! default maximum allowed redirections +#define PROTOHTTP_MAXREDIRECT (3) + +//! size of "last-received" header cache +#define PROTOHTTP_HDRCACHESIZE (1024) + +//! protohttp revision number (maj.min) +#define PROTOHTTP_VERSION (0x0103) // update this for major bug fixes or protocol additions/changes + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! http module state +struct ProtoHttpRefT +{ + ProtoSSLRefT *pSsl; //!< ssl module + + ProtoHttpCustomHeaderCbT *pCustomHeaderCb; //!< global callback for modifying request header + ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb; //!< global callback for viewing recv header on recepit + void *pCallbackRef; //!< user ref for callback + + ProtoHttpWriteCbT *pWriteCb; //!< optional data write callback + ProtoHttpCustomHeaderCbT *pReqCustomHeaderCb; //!< optional request custom header callback + ProtoHttpReceiveHeaderCbT *pReqReceiveHeaderCb; //!< optional request receive header callback + void *pUserData; //!< user data for callback + + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData;//!< user data associated with mem group + + NetCritT HttpCrit; //!< critical section for guarding update from send/recv + + ProtoHttpRequestTypeE eRequestType; //!< request type of current request + int32_t iPort; //!< server port + int32_t iBasePort; //!< base port (used for partial urls) + int32_t iProxiedPort; //!< port of proxied host + int32_t iSecure; //!< secure connection + int32_t iBaseSecure; //!< base security setting (used for partial urls) + int32_t iProxiedSecure; //!< true if proxied connection is secure + + enum + { + ST_IDLE, //!< idle + ST_CONN, //!< connecting + ST_SEND, //!< sending buffered data + ST_RESP, //!< waiting for initial response (also sending any data not buffered if POST or PUT) + ST_HEAD, //!< getting header + ST_BODY, //!< getting body + ST_DONE, //!< transaction success + ST_FAIL //!< transaction failed + } eState; //!< current state + + int32_t iSslFail; //!< ssl failure code, if any + int32_t iHresult; //!< ssl hresult code, if any + int32_t iHdrCode; //!< result code + int32_t iHdrDate; //!< last modified date + + int32_t iHeadSize; //!< size of head data + int64_t iPostSize; //!< amount of data being sent in a POST or PUT operation + int64_t iBodySize; //!< size of body data + int64_t iBodyRcvd; //!< size of body data received by caller + int32_t iRecvSize; //!< amount of data received by ProtoHttpRecvAll + int32_t iRecvRslt; //!< last receive result + + char *pInpBuf; //!< input buffer + int32_t iInpMax; //!< maximum buffer size + int32_t iInpOff; //!< offset into buffer + int32_t iInpLen; //!< total length in buffer + int64_t iInpCnt; //!< ongoing count + int32_t iInpOvr; //!< input overflow amount + int32_t iChkLen; //!< chunk length (if chunked encoding) + int32_t iHdrLen; //!< length of header(s) queued for sending + int32_t iHdrOff; //!< temp offset used when receiving header + + char *pInpBufTmp; //!< temp storage for input buffer pointer when using connect flow + int32_t iInpLenTmp; //!< temp storage for input buffer length when using connect flow + + int32_t iNumRedirect; //!< number of redirections processed + int32_t iMaxRedirect; //!< maximum number of redirections allowed + + uint32_t uTimeout; //!< protocol timeout + uint32_t uTimer; //!< timeout timer + int32_t iKeepAlive; //!< indicate if we should try to use keep-alive + int32_t iKeepAliveDflt; //!< keep-alive default (keep-alive will be reset to this value; can be overridden by user) + + char *pAppendHdr; //!< append header buffer pointer + int32_t iAppendLen; //!< size of append header buffer + + char strHdr[PROTOHTTP_HDRCACHESIZE]; //!< storage for most recently received HTTP header + char strRequestHdr[PROTOHTTP_HDRCACHESIZE]; //!< storage for most recent HTTP request header + char strConnectHdr[256]; //!< temp storage for connect header when using connect flow + char strHost[256]; //!< server name + char strBaseHost[256]; //!< base server name (used for partial urls) + char strProxy[256]; //!< proxy server name/address (including port) + char strProxiedHost[256]; //!< hostname of server we are connecting to through proxy + + uint8_t bTimeout; //!< boolean indicating whether a timeout occurred or not + uint8_t bChunked; //!< if TRUE, transfer is chunked + uint8_t bHeadOnly; //!< if TRUE, only get header + uint8_t bCloseHdr; //!< server wants close after this + uint8_t bClosed; //!< connection has been closed + uint8_t bConnOpen; //!< connection is open + uint8_t iVerbose; //!< debug output verbosity + uint8_t bVerifyHdr; //!< perform header type verification + uint8_t bHttp1_0; //!< TRUE if HTTP/1.0, else FALSE + uint8_t bCompactRecv; //!< compact receive buffer + uint8_t bInfoHdr; //!< TRUE if a new informational header has been cached; else FALSE + uint8_t bNewConnection; //!< TRUE if a new connection should be used, else FALSE (if using keep-alive) + uint8_t bPipelining; //!< TRUE if pipelining is enabled, else FALSE + uint8_t bPipeGetNext; //!< TRUE if we should proceed to next pipelined result, else FALSE + int8_t iPipedRequests; //!< number of pipelined requests + uint8_t bPipedRequestsLost; //!< TRUE if pipelined requests were lost due to a premature close + uint8_t bReuseOnPost; //!< TRUE if reusing a previously established connection on PUT/POST is allowed, else FALSE + uint8_t bConnProxy; //!< if true, executing secure proxy connect flow + uint8_t bUpgradeSSL; //!< upgrade connection to SSL after connect + +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private variables + +// update this when PROTOHTTP_NUMREQUESTTYPES changes +static const char _ProtoHttp_strRequestNames[PROTOHTTP_NUMREQUESTTYPES][16] = +{ + "HEAD", "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "CONNECT" +}; + +//! global proxy; if this is set all ProtoHttp refs will use this as their proxy +static char _ProtoHttp_strGlobalProxy[256] = ""; + + +// Public variables + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpApplyBaseUrl + + \Description + Apply base url elements (if set) to any url elements not specified (relative + url support). + + \Input *pState - module state + \Input *pKind - parsed http kind ("http" or "https") + \Input *pHost - [in/out] parsed URL host + \Input iHostSize - size of pHost buffer + \Input *pPort - [in/out] parsed port + \Input *pSecure - [in/out] parsed security (0 or 1) + \Input bPortSpecified - TRUE if a port is explicitly specified in the url, else FALSE + + \Output + uint32_t - non-zero if changed, else zero + + \Version 02/03/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ProtoHttpApplyBaseUrl(ProtoHttpRefT *pState, const char *pKind, char *pHost, int32_t iHostSize, int32_t *pPort, int32_t *pSecure, uint8_t bPortSpecified) +{ + uint8_t bChanged = FALSE; + if ((*pHost == '\0') && (pState->strBaseHost[0] != '\0')) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: host not present; setting to %s\n", pState->strBaseHost)); + ds_strnzcpy(pHost, pState->strBaseHost, iHostSize); + bChanged = TRUE; + } + if ((bPortSpecified == FALSE) && (pState->iBasePort != 0)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: port not present; setting to %d\n", pState->iBasePort)); + *pPort = pState->iBasePort; + bChanged = TRUE; + } + if (*pKind == '\0') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: kind (protocol) not present; setting to %d\n", pState->iBaseSecure)); + *pSecure = pState->iBaseSecure; + // if our port setting is default and incompatible with our security setting, override it + if (((*pPort == 80) && (*pSecure == 1)) || ((*pPort == 443) && (*pSecure == 0))) + { + *pPort = *pSecure ? 443 : 80; + } + bChanged = TRUE; + } + return(bChanged); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpClose + + \Description + Close connection to server, if open. + + \Input *pState - module state + \Input *pReason - reason connection is being closed (for debug output) + + \Output + None. + + \Version 10/07/2005 (jbrookes) First Version +*/ +/********************************************************************************F*/ +static void _ProtoHttpClose(ProtoHttpRefT *pState, const char *pReason) +{ + if (pState->bClosed) + { + // already issued disconnect, don't need to do it again + return; + } + + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] closing connection: %s\n", pState, pReason)); + ProtoSSLDisconnect(pState->pSsl); + pState->bCloseHdr = FALSE; + pState->bConnOpen = FALSE; + pState->bClosed = TRUE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpReset + + \Description + Reset state before a transaction request. + + \Input *pState - reference pointer + + \Output + None. + + \Version 11/22/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +static void _ProtoHttpReset(ProtoHttpRefT *pState) +{ + ds_memclr(pState->strHdr, sizeof(pState->strHdr)); + ds_memclr(pState->strRequestHdr, sizeof(pState->strRequestHdr)); + pState->eState = ST_IDLE; + pState->iSslFail = 0; + pState->iHresult = 0; + pState->iHdrCode = -1; + pState->iHdrDate = 0; + pState->iHeadSize = 0; + pState->iBodySize = pState->iBodyRcvd = 0; + pState->iRecvSize = 0; + pState->iInpOff = 0; + pState->iInpLen = 0; + pState->iInpOvr = 0; + pState->iChkLen = 0; + pState->bTimeout = FALSE; + pState->bChunked = FALSE; + pState->bClosed = FALSE; + pState->bHeadOnly = FALSE; + pState->bPipeGetNext = FALSE; + pState->bPipedRequestsLost = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpFreeInputBuf + + \Description + Free input buffer + + \Input *pState - reference pointer + + \Version 06/20/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpFreeInputBuf(ProtoHttpRefT *pState) +{ + char *pInpBuf = (pState->pInpBuf != pState->strConnectHdr) ? pState->pInpBuf : pState->pInpBufTmp; + DirtyMemFree(pInpBuf, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpSetAppendHeader + + \Description + Set given string as append header, allocating memory as required. + + \Input *pState - reference pointer + \Input *pAppendHdr - append header string + + \Output + int32_t - zero=success, else error + + \Version 11/11/2004 (jbrookes) Split/combined from ProtoHttpGet() and ProtoHttpPost() +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpSetAppendHeader(ProtoHttpRefT *pState, const char *pAppendHdr) +{ + int32_t iAppendBufLen, iAppendStrLen; + + // check for empty append string, in which case we free the buffer + if ((pAppendHdr == NULL) || (*pAppendHdr == '\0')) + { + if (pState->pAppendHdr != NULL) + { + DirtyMemFree(pState->pAppendHdr, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pAppendHdr = NULL; + } + pState->iAppendLen = 0; + return(0); + } + + // check to see if append header is already set + if ((pState->pAppendHdr != NULL) && (!strcmp(pAppendHdr, pState->pAppendHdr))) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: ignoring set of append header '%s' that is already set\n", pAppendHdr)); + return(0); + } + + // get append header length + iAppendStrLen = (int32_t)strlen(pAppendHdr); + // append buffer size includes null and space for \r\n if not included by submitter + iAppendBufLen = iAppendStrLen + 3; + + // see if we need to allocate a new buffer + if (iAppendBufLen > pState->iAppendLen) + { + if (pState->pAppendHdr != NULL) + { + DirtyMemFree(pState->pAppendHdr, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + if ((pState->pAppendHdr = DirtyMemAlloc(iAppendBufLen, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) != NULL) + { + pState->iAppendLen = iAppendBufLen; + } + else + { + NetPrintf(("protohttp: could not allocate %d byte buffer for append header\n", iAppendBufLen)); + pState->iAppendLen = 0; + return(-1); + } + } + + // copy append header + ds_strnzcpy(pState->pAppendHdr, pAppendHdr, iAppendStrLen+1); + + // if append header is not \r\n terminated, do it here + if (pAppendHdr[iAppendStrLen-2] != '\r' || pAppendHdr[iAppendStrLen-1] != '\n') + { + ds_strnzcat(pState->pAppendHdr, "\r\n", pState->iAppendLen); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpFormatRequestHeader + + \Description + Format a request header based on given input data. + + \Input *pState - reference pointer + \Input *pUrl - pointer to user-supplied url + \Input *pHost - pointer to hostname + \Input iPort - port, or zero if unspecified + \Input iSecure - 1=enabled, 0=disabled + \Input *pRequest - pointer to request type ("GET", "HEAD", "POST", "PUT") + \Input iDataLen - size of included data; zero if none, negative if streaming put/post + + \Output + int32_t - zero=success, else error + + \Version 11/11/2004 (jbrookes) Split/combined from ProtoHttpGet() and ProtoHttpPost() +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpFormatRequestHeader(ProtoHttpRefT *pState, const char *pUrl, const char *pHost, int32_t iPort, int32_t iSecure, const char *pRequest, int64_t iDataLen) +{ + int32_t iInpMax, iOffset = 0; + const char *pUrlSlash; + char *pInpBuf; + ProtoHttpCustomHeaderCbT *pCustomHeaderCb; + void *pUserData; + + // if url is empty or isn't preceded by a slash, put one in + pUrlSlash = (*pUrl != '/') ? "/" : ""; + + // set up for header formatting + pInpBuf = pState->pInpBuf + pState->iInpLen; + iInpMax = pState->iInpMax - pState->iInpLen; + if (pState->iInpLen != 0) + { + pState->iPipedRequests += 1; + } + + // format request header + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "%s %s%s HTTP/1.1\r\n", pRequest, pUrlSlash, pUrl); + if ((iSecure && (iPort == 443)) || (iPort == 80)) + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "Host: %s\r\n", pHost); + } + else + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "Host: %s:%d\r\n", pHost, iPort); + } + if (iDataLen == -1) + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "Transfer-Encoding: chunked\r\n"); + } + else if ((iDataLen > 0) || (pState->eRequestType == PROTOHTTP_REQUESTTYPE_PUT) || (pState->eRequestType == PROTOHTTP_REQUESTTYPE_PATCH) || (pState->eRequestType == PROTOHTTP_REQUESTTYPE_POST)) + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "Content-Length: %qd\r\n", iDataLen); + } + if (pState->iKeepAlive == 0) + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "Connection: Close\r\n"); + } + if ((pState->pAppendHdr == NULL) || !ds_stristr(pState->pAppendHdr, "User-Agent:")) + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "User-Agent: ProtoHttp %d.%d/DS %d.%d.%d.%d.%d (" DIRTYCODE_PLATNAME ")\r\n", + (PROTOHTTP_VERSION>>8)&0xff, PROTOHTTP_VERSION&0xff, DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH); + } + if ((pState->pAppendHdr == NULL) || (pState->pAppendHdr[0] == '\0')) + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "Accept: */*\r\n"); + } + else + { + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "%s", pState->pAppendHdr); + } + + // request level callback takes priority to global + if ((pCustomHeaderCb = pState->pReqCustomHeaderCb) != NULL) + { + pUserData = pState->pUserData; + } + else + { + pCustomHeaderCb = pState->pCustomHeaderCb; + pUserData = pState->pCallbackRef; + } + + // call custom header format callback, if specified + if (pCustomHeaderCb != NULL) + { + if ((iOffset = pCustomHeaderCb(pState, pInpBuf, iInpMax, NULL, 0, pUserData)) < 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: custom header callback error %d\n", iOffset)); + return(iOffset); + } + if (iOffset == 0) + { + iOffset = (int32_t)strlen(pInpBuf); + } + } + + // append header terminator and return header length + iOffset += ds_snzprintf(pInpBuf+iOffset, iInpMax-iOffset, "\r\n"); + + // make sure we were able to complete the header + if (iOffset > iInpMax) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: not enough buffer to format request header (have %d, need %d)\n", iInpMax, iOffset)); + pState->iInpOvr = iOffset; + return(PROTOHTTP_MINBUFF); + } + + // save a copy of the header + ds_strnzcpy(pState->strRequestHdr, pInpBuf, sizeof(pState->strRequestHdr)); + + // update buffer size + pState->iInpLen += iOffset; + + // save updated header size + pState->iHdrLen = pState->iInpLen; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpFormatConnectHeader + + \Description + Format a proxy connect request header as per + https://tools.ietf.org/html/rfc7231#section-4.3.6. + + \Input *pState - reference pointer + \Input *pStrHost - pointer to proxy hostname + \Input iPort - proxy port + + \Version 05/30/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpFormatConnectHeader(ProtoHttpRefT *pState, const char *pStrHost, int32_t iPort) +{ + int32_t iOffset = 0, iInpMax; + + // save current input buffer info + pState->pInpBufTmp = pState->pInpBuf; + pState->iInpLenTmp = pState->iInpLen; + + // point to temp connect header + pState->pInpBuf = pState->strConnectHdr; + iInpMax = sizeof(pState->strConnectHdr); + + // format request header + iOffset += ds_snzprintf(pState->pInpBuf + iOffset, iInpMax - iOffset, "%s %s:%d HTTP/1.1\r\n", _ProtoHttp_strRequestNames[PROTOHTTP_REQUESTTYPE_CONNECT], pStrHost, iPort); + iOffset += ds_snzprintf(pState->pInpBuf + iOffset, iInpMax - iOffset, "Host: %s:%d\r\n", pStrHost, iPort); + // append header terminator and return header length + iOffset += ds_snzprintf(pState->pInpBuf + iOffset, iInpMax - iOffset, "\r\n"); + // update buffer size + pState->iInpLen = iOffset; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpFormatRequest + + \Description + Format a request into the local buffer. + + \Input *pState - reference pointer + \Input *pUrl - pointer to user-supplied url + \Input *pData - pointer to data to include with request, or NULL + \Input iDataLen - size of data pointed to by pData, or zero if no data + \Input eRequestType - type of request (PROTOHTTP_REQUESTTYPE_*) + + \Output + int32_t - bytes of userdata included in request + + \Version 10/07/2005 (jbrookes) Split/combined from ProtoHttpGet() and ProtoHttpPost() +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpFormatRequest(ProtoHttpRefT *pState, const char *pUrl, const char *pData, int64_t iDataLen, ProtoHttpRequestTypeE eRequestType) +{ + char strHost[sizeof(pState->strHost)], strKind[8]; + int32_t iPort, iResult, iSecure; + int32_t eState = pState->eState; + uint8_t bPortSpecified; + const char *pStrProxy; + + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] %s %s\n", pState, _ProtoHttp_strRequestNames[eRequestType], pUrl)); + pState->eRequestType = eRequestType; + + // reset various state + if (pState->eState != ST_IDLE) + { + _ProtoHttpReset(pState); + } + + // restore input buffer, if set up for proxy + if (pState->pInpBuf == pState->strConnectHdr) + { + pState->pInpBuf = pState->pInpBufTmp; + } + + // use global proxy if set, otherwise use state-local proxy + pStrProxy = (_ProtoHttp_strGlobalProxy[0] != '\0') ? _ProtoHttp_strGlobalProxy : pState->strProxy; + + // assume we don't want a new connection to start with (if this is a pipelined request, don't override the original selection) + if (pState->iInpLen == 0) + { + pState->bNewConnection = FALSE; + } + + // parse the url for kind, host, and port + if (pStrProxy[0] == '\0') + { + pUrl = ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure, &bPortSpecified); + } + else + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: using proxy server %s\n", pStrProxy)); + ProtoHttpUrlParse2(pStrProxy, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure, &bPortSpecified); + } + + // fill in any missing info (relative url) if available + if (_ProtoHttpApplyBaseUrl(pState, strKind, strHost, sizeof(strHost), &iPort, &iSecure, bPortSpecified) != 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] %s %s://%s:%d%s\n", pState, _ProtoHttp_strRequestNames[eRequestType], + iSecure ? "https" : "http", strHost, iPort, pUrl)); + } + + // determine if host, port, or security settings have changed since the previous request + if ((iSecure != pState->iSecure) || (ds_stricmp(strHost, pState->strHost) != 0) || (iPort != pState->iPort)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] requesting new connection -- url change to %s\n", pState, strHost)); + + // reset keep-alive + pState->iKeepAlive = pState->iKeepAliveDflt; + + // save new server/port/security state + ds_strnzcpy(pState->strHost, strHost, sizeof(pState->strHost)); + pState->iPort = iPort; + pState->iSecure = iSecure; + + // make sure we use a new connection + pState->bNewConnection = TRUE; + } + + // check to see if previous connection (if any) is still active + if ((pState->bNewConnection == FALSE) && (ProtoSSLStat(pState->pSsl, 'stat', NULL, 0) < 0)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] requesting new connection -- previous connection was closed\n", pState)); + pState->bNewConnection = TRUE; + } + + // check to make sure we are in a known valid state + if ((pState->bNewConnection == FALSE) && (eState != ST_IDLE) && (eState != ST_DONE)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] requesting new connection -- current state of %d does not allow connection reuse\n", pState, eState)); + pState->bNewConnection = TRUE; + } + + // if executing put/post, check to see if connection reuse on request is allowed + if ((pState->bNewConnection == FALSE) && (pState->bReuseOnPost == FALSE) && ((eRequestType == PROTOHTTP_REQUESTTYPE_PUT) || (eRequestType == PROTOHTTP_REQUESTTYPE_PATCH) || (eRequestType == PROTOHTTP_REQUESTTYPE_POST))) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] requesting new connection -- reuse on put/post disabled\n", pState)); + pState->bNewConnection = TRUE; + } + + // if using a proxy server, parse original url to get target host and port for Host header + if (pStrProxy[0] != '\0') + { + pUrl = ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure, &bPortSpecified); + if ((ds_stricmp(pState->strProxiedHost, strHost)) || (pState->iProxiedPort != iPort) || (pState->iProxiedSecure != iSecure)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] requesting new connection -- proxy change\n", pState)); + pState->bNewConnection = TRUE; + } + ds_strnzcpy(pState->strProxiedHost, strHost, sizeof(pState->strProxiedHost)); + pState->iProxiedPort = iPort; + pState->iProxiedSecure = iSecure; + } + else + { + pState->strProxiedHost[0] = '\0'; + } + + // format the request header + if ((iResult = _ProtoHttpFormatRequestHeader(pState, pUrl, strHost, iPort, iSecure, _ProtoHttp_strRequestNames[eRequestType], iDataLen)) < 0) + { + return(iResult); + } + + // append data to header? + if ((pData != NULL) && (iDataLen > 0)) + { + // see how much data will fit into the buffer + if (iDataLen > (pState->iInpMax - pState->iInpLen)) + { + iDataLen = (pState->iInpMax - pState->iInpLen); + } + + // copy data into buffer (must happen after _ProtoHttpFormatRequestHeader()) + ds_memcpy(pState->pInpBuf + pState->iInpLen, pData, (int32_t)iDataLen); + pState->iInpLen += iDataLen; + } + else if (iDataLen < 0) + { + // for a streaming post, return no data written + iDataLen = 0; + } + + // set headonly status + pState->bHeadOnly = (eRequestType == PROTOHTTP_REQUESTTYPE_HEAD) ? TRUE : FALSE; + + // handle connect flow when using a secure proxy + if ((pStrProxy[0] != '\0') && iSecure) + { + _ProtoHttpFormatConnectHeader(pState, strHost, iPort); + pState->bConnProxy = TRUE; + } + else + { + pState->bConnProxy = FALSE; + } + + return((int32_t)iDataLen); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpSendRequest + + \Description + Send a request (already formatted in buffer) to the server. + + \Input *pState - reference pointer + + \Output + None. + + \Version 05/19/2009 (jbrookes) Split from _ProtoHttpFormatRequest() +*/ +/********************************************************************************F*/ +static void _ProtoHttpSendRequest(ProtoHttpRefT *pState) +{ + int32_t iResult; + char cTest; + + /* if we still want to reuse the current connection, try and receive on it and + make sure it is in a valid state (not an error state and no data to be read) */ + if (pState->bNewConnection == FALSE) + { + if ((iResult = ProtoSSLRecv(pState->pSsl, &cTest, sizeof(cTest))) > 0) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] requesting new connection -- receive on previous connection returned data (0x%02x)\n", pState, (uint8_t)cTest)); + pState->bNewConnection = TRUE; + } + else if (iResult < 0) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] requesting new connection -- received %d error response from previous connection\n", pState, iResult)); + pState->bNewConnection = TRUE; + } + } + + // handle proxy connections + if (pState->bConnProxy) + { + // restore user request if we're in the proxy connect flow and we're already connected + if (!pState->bNewConnection) + { + NetPrintf(("protohttp: [%p] bypassing proxy connect (already connected)\n", pState)); + pState->pInpBuf = pState->pInpBufTmp; + pState->iInpLen = pState->iInpLenTmp; + pState->bUpgradeSSL = FALSE; + } + else + { + // if a new proxy connection, mark for SSL upgrade + NetPrintf(("protohttp: [%p] new proxy connection\n", pState)); + pState->bUpgradeSSL = TRUE; + } + } + + // set connection timeout + pState->uTimer = NetTick() + pState->uTimeout; + + // see if we need a new connection + if (pState->bNewConnection == TRUE) + { + // close the existing connection, if not already closed + _ProtoHttpClose(pState, "new connection"); + + // start connect + NetPrintfVerbose((pState->iVerbose, 2, "protohttp: [%p] connect start (tick=%u)\n", pState, NetTick())); + ProtoSSLConnect(pState->pSsl, pState->iSecure, pState->strHost, 0, pState->iPort); + pState->eState = ST_CONN; + pState->bClosed = FALSE; + } + else + { + // advance state + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] reusing previous connection (keep-alive)\n", pState)); + pState->eState = ST_SEND; + } + + // if we requested a connection close, the server may not tell us, so remember it here + if (pState->iKeepAlive == 0) + { + pState->bCloseHdr = TRUE; + } + + // count the attempt + pState->iKeepAlive += 1; + + // call the update routine just in case operation can complete + ProtoHttpUpdate(pState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpRetrySendRequest + + \Description + If the connection was a keep-alive connection and the request method was + idempotent (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2 + for a definition of idempotent), we automatically re-issue the request one + time on a fresh connection. + + \Input *pState - reference pointer + + \Output + uint32_t - zero=did not reissue request, else we did + + \Version 07/14/2009 (jbrookes) Split from ProtoHttpUpdate() +*/ +/********************************************************************************F*/ +static uint32_t _ProtoHttpRetrySendRequest(ProtoHttpRefT *pState) +{ + // if this was not a keep-alive connection, we do not retry + if (pState->bNewConnection == TRUE) + { + return(0); + } + // if this was a POST request, we do not retry as the method is not idempotent + if (pState->eRequestType == PROTOHTTP_REQUESTTYPE_POST) + { + NetPrintf(("protohttp: cannot execute automatic retry of post request on keep-alive connection\n")); + return(0); + } + + // retry the connection + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] request failure on keep-alive connection; retrying\n", pState)); + _ProtoHttpClose(pState, "retry"); + + // rewind buffer pointers to resend header + pState->iInpLen = pState->iHdrLen; + pState->iInpOff = 0; + + /* set keep-alive so we don't try another reconnect attempt, but we do + request keep-alive on any further requests if this one succeeds */ + pState->iKeepAlive = 1; + + // reconnect + ProtoSSLConnect(pState->pSsl, pState->iSecure, pState->strHost, 0, pState->iPort); + pState->eState = ST_CONN; + pState->bClosed = FALSE; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpResizeBuffer + + \Description + Resize the buffer + + \Input *pState - reference pointer + \Input iBufMax - new buffer size + + \Output + int32_t - zero=success, else failure + + \Version 02/21/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpResizeBuffer(ProtoHttpRefT *pState, int32_t iBufMax) +{ + int32_t iCopySize; + char *pInpBuf; + + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] resizing input buffer from %d to %d bytes\n", pState, pState->iInpMax, iBufMax)); + if ((pInpBuf = DirtyMemAlloc(iBufMax, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp: [%p] could not resize input buffer\n", pState)); + return(-1); + } + + // calculate size of data to copy from old buffer to new + if ((iCopySize = pState->iInpLen - pState->iInpOff) > iBufMax) + { + NetPrintf(("protohttp: [%p] warning; resize of input buffer is losing %d bytes of data\n", pState, iCopySize - iBufMax)); + iCopySize = iBufMax; + } + // copy valid contents of input buffer, if any, to new buffer + ds_memcpy(pInpBuf, pState->pInpBuf+pState->iInpOff, iCopySize); + + // dispose of input buffer + _ProtoHttpFreeInputBuf(pState); + + // update buffer variables + pState->pInpBuf = pInpBuf; + pState->iInpOff = 0; + pState->iInpLen = iCopySize; + pState->iInpMax = iBufMax; + + // clear overflow count + pState->iInpOvr = 0; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpCompactBuffer + + \Description + Compact the buffer + + \Input *pState - reference pointer + + \Output + int32_t - amount of space freed by compaction + + \Version 07/02/2009 (jbrookes) Extracted from ProtoHttpRecv() +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpCompactBuffer(ProtoHttpRefT *pState) +{ + int32_t iCompacted = 0; + // make sure it needs compacting + if (pState->iInpOff > 0) + { + // compact the buffer + if (pState->iInpOff < pState->iInpLen) + { + memmove(pState->pInpBuf, pState->pInpBuf+pState->iInpOff, pState->iInpLen-pState->iInpOff); + iCompacted = pState->iInpOff; + } + pState->iInpLen -= pState->iInpOff; + pState->iInpOff = 0; + pState->bCompactRecv = FALSE; + } + return(iCompacted); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpParseHeader + + \Description + Parse incoming HTTP header. The HTTP header is presumed to be at the + beginning of the input buffer. + + \Input *pState - reference pointer + + \Output + int32_t - negative=not ready or error, else success + + \Version 11/12/2004 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpParseHeader(ProtoHttpRefT *pState) +{ + char *s = pState->pInpBuf; + char *t = pState->pInpBuf+pState->iInpLen-3; + char strTemp[128]; + ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb; + void *pUserData; + + // scan for blank line marking body start + while ((s != t) && ((s[0] != '\r') || (s[1] != '\n') || (s[2] != '\r') || (s[3] != '\n'))) + { + s++; + } + if (s == t) + { + // header is incomplete + return(-1); + } + + // save the head size + pState->iHeadSize = (int32_t)(s+4-pState->pInpBuf); + // terminate header for easy parsing + s[2] = s[3] = 0; + + // make sure the header is valid + if (pState->bVerifyHdr) + { + if (strncmp(pState->pInpBuf, "HTTP", 4)) + { + // header is invalid + NetPrintf(("protohttp: [%p] invalid result type\n", pState)); + pState->eState = ST_FAIL; + return(-2); + } + } + + // detect if it is a 1.0 response + pState->bHttp1_0 = !strncmp(pState->pInpBuf, "HTTP/1.0", 8); + + // parse header code + pState->iHdrCode = ProtoHttpParseHeaderCode(pState->pInpBuf); + + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] received %d response (%d bytes)\n", pState, pState->iHdrCode, pState->iHeadSize)); + if (pState->iVerbose > 1) + { + NetPrintWrap(pState->pInpBuf, 80); + } + #endif + + // parse content-length field + if (ProtoHttpGetHeaderValue(pState, pState->pInpBuf, "content-length", strTemp, sizeof(strTemp), NULL) != -1) + { + pState->iBodySize = ds_strtoll(strTemp, NULL, 10); + pState->bChunked = FALSE; + } + else + { + pState->iBodySize = -1; + } + + // parse last-modified header + if (ProtoHttpGetHeaderValue(pState, pState->pInpBuf, "last-modified", strTemp, sizeof(strTemp), NULL) != -1) + { + pState->iHdrDate = (int32_t)ds_strtotime(strTemp); + } + else + { + pState->iHdrDate = 0; + } + + // parse transfer-encoding header + if (ProtoHttpGetHeaderValue(pState, pState->pInpBuf, "transfer-encoding", strTemp, sizeof(strTemp), NULL) != -1) + { + pState->bChunked = !ds_stricmp(strTemp, "chunked"); + } + + // parse connection header + if (pState->bCloseHdr == FALSE) + { + ProtoHttpGetHeaderValue(pState, pState->pInpBuf, "connection", strTemp, sizeof(strTemp), NULL); + pState->bCloseHdr = !ds_stricmp(strTemp, "close"); + // if server is closing the connection and we are expecting subsequent piped results, we should not expect to get them + if (pState->bCloseHdr && (pState->iPipedRequests > 0)) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] lost %d piped requests due to server connection-close request\n", pState, pState->iPipedRequests)); + pState->iPipedRequests = 0; + pState->bPipedRequestsLost = TRUE; + } + } + + // note if it is an informational header + pState->bInfoHdr = PROTOHTTP_GetResponseClass(pState->iHdrCode) == PROTOHTTP_RESPONSE_INFORMATIONAL; + + // copy header to header cache + ds_strnzcpy(pState->strHdr, pState->pInpBuf, sizeof(pState->strHdr)); + + // request level callback takes priority to global + if ((pReceiveHeaderCb = pState->pReqReceiveHeaderCb) != NULL) + { + pUserData = pState->pUserData; + } + else + { + pReceiveHeaderCb = pState->pReceiveHeaderCb; + pUserData = pState->pCallbackRef; + } + + // trigger recv header user callback, if specified (and if this wasn't a proxy connect request) + if ((pReceiveHeaderCb != NULL) && (!pState->bConnProxy || (pState->pInpBufTmp == NULL))) + { + pReceiveHeaderCb(pState, pState->pInpBuf, (uint32_t)strlen(pState->pInpBuf), pUserData); + } + + // remove header from input buffer + pState->iInpOff = pState->iHeadSize; + pState->iInpCnt = pState->iInpLen - pState->iHeadSize; + + // header successfully parsed + return(0); +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _ProtoHttpDisplayHeader + + \Description + Display header to debug output [DEBUG ONLY] + + \Input *pState - reference pointer + + \Version 05/03/2010 (jbrookes) Refactored out of ProtoHttpUpdate() +*/ +/********************************************************************************F*/ +static void _ProtoHttpDisplayHeader(ProtoHttpRefT *pState) +{ + // if we just sent a header, display header to debug output + if (pState->iVerbose > 1) + { + int32_t iRequestType; + for (iRequestType = 0; iRequestType < PROTOHTTP_NUMREQUESTTYPES; iRequestType += 1) + { + if (!strncmp(pState->pInpBuf, _ProtoHttp_strRequestNames[iRequestType], strlen(_ProtoHttp_strRequestNames[iRequestType]))) + { + char *pHdrEnd = pState->pInpBuf + pState->iHdrLen; + char cHdrChr = *pHdrEnd; + *pHdrEnd = '\0'; + NetPrintf(("protohttp: [%p] sent request:\n", pState)); + NetPrintfVerbose((pState->iVerbose, 2, "protohttp: [%p] tick=%u\n", pState, NetTick())); + NetPrintWrap(pState->pInpBuf, 80); + *pHdrEnd = cHdrChr; + break; + } + } + } +} +#endif + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpProcessInfoHeader + + \Description + Handles an informational response header (response code=1xx) + + \Input *pState - reference pointer + + \Output + None. + + \Version 05/15/2008 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static void _ProtoHttpProcessInfoHeader(ProtoHttpRefT *pState) +{ + // ignore the response + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] ignoring %d response from server\n", pState, pState->iHdrCode)); + + // remove header from input buffer + memmove(pState->pInpBuf, pState->pInpBuf+pState->iInpOff, pState->iInpLen-pState->iInpOff); + pState->iInpLen -= pState->iInpOff; + // reset processing offset + pState->iInpOff = 0; + + // reset state to process next header + pState->eState = ST_HEAD; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpProcessRedirect + + \Description + Handle redirection header (response code=3xx) + + \Input *pState - reference pointer + + \Notes + A maximum of PROTOHTTP_MAXREDIRECT redirections is allowed. Any further + redirection attempts will result in a failure state. A redirection + Location url is limited based on the size of the http receive buffer. + + Auto-redirection is implemented as specified by RFC: + (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3); + if auto-redirection is not performed, processing ends and it is the + responsibility of the application to recognize the 3xx result code + and handle it accordingly. + + Auto-redirection can be disabled by setting the maximum number of + redirections allowed to zero. + + \Version 11/12/2004 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static void _ProtoHttpProcessRedirect(ProtoHttpRefT *pState) +{ + char strHost[sizeof(pState->strHost)], strKind[PROTOHTTPUTIL_SCHEME_MAX]; + int32_t iPort, iResult, iSecure, iUrlLen; + char *pUrlBuf; + + // do not auto-redirect multiplechoices or notmodified responses + if ((pState->iHdrCode == PROTOHTTP_RESPONSE_MULTIPLECHOICES) || (pState->iHdrCode == PROTOHTTP_RESPONSE_NOTMODIFIED)) + { + return; + } + // do not auto-redirect responses that are not head or get requests, and are not a SEEOTHER response + if ((pState->eRequestType != PROTOHTTP_REQUESTTYPE_GET) && (pState->eRequestType != PROTOHTTP_REQUESTTYPE_HEAD)) + { + /* As per HTTP 1.1 RFC, 303 SEEOTHER POST requests may be auto-redirected to a GET requset. 302 FOUND responses + to a POST request are not supposed to be auto-redirected; however, there is a note in the RFC indicating that + this is a common client behavior, and it is additionally a common server behavior to use 302 even when automatic + redirection is intended, as some clients do not support 303 SEEOTHER. Therefore, we perform auto-redirection + on 302 FOUND responses to POST requests with a GET request for compatibility with servers that choose this + behavior */ + if ((pState->iHdrCode == PROTOHTTP_RESPONSE_FOUND) || (pState->iHdrCode == PROTOHTTP_RESPONSE_SEEOTHER)) + { + pState->eRequestType = PROTOHTTP_REQUESTTYPE_GET; + pState->iPostSize = 0; + } + else + { + return; + } + } + + // get size of location header + if ((iUrlLen = ProtoHttpGetLocationHeader(pState, pState->pInpBuf, NULL, 0, NULL)) <= 0) + { + NetPrintf(("protohttp: [%p] no location included in redirect header\n", pState)); + pState->eState = ST_FAIL; + return; + } + + // get url at the end of input buffer + pUrlBuf = pState->pInpBuf + pState->iInpMax - iUrlLen; + if (ProtoHttpGetLocationHeader(pState, pState->pInpBuf, pUrlBuf, iUrlLen, NULL) != 0) + { + NetPrintf(("protohttp: [%p] failed to get location header url", pState)); + pState->eState = ST_FAIL; + return; + } + + // parse url for protocol + ProtoHttpUrlParse(pUrlBuf, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure); + // only auto-redirect if http/s protocol + if ((ds_stricmp(strKind, "https") != 0) && (ds_stricmp(strKind, "http") != 0)) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] skipping auto-redirection of non-http protocol '%s'\n", pState, strKind)); + return; + } + + // process based on max redirections allowed; zero=disabled + if (pState->iMaxRedirect == 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] auto-redirection disabled\n", pState)); + return; + } + else if (++pState->iNumRedirect > pState->iMaxRedirect) + { + NetPrintf(("protohttp: [%p] maximum number of redirections (%d) exceeded\n", pState, pState->iMaxRedirect)); + pState->eState = ST_FAIL; + return; + } + + // close connection? + if (pState->bCloseHdr) + { + _ProtoHttpClose(pState, "server request"); + } + + // clear piped result count + pState->iPipedRequests = 0; + pState->bPipedRequestsLost = FALSE; + + // format redirection request + if ((iResult = _ProtoHttpFormatRequest(pState, pUrlBuf, NULL, 0, pState->eRequestType)) < 0) + { + NetPrintf(("protohttp: redirect header format request failed\n")); + pState->eState = ST_FAIL; + return; + } + // send redirection request + _ProtoHttpSendRequest(pState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpChunkProcess + + \Description + Process output if chunked encoding. + + \Input *pState - reference pointer + \Input iBufMax - maximum number of bytes to return (buffer size) + + \Output + int32_t - number of bytes available + + \Notes + Does not support anything but chunked encoding. Does not support optional + end-transfer header (anything past the terminating 0 chunk is discarded). + + \Version 04/05/2005 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpChunkProcess(ProtoHttpRefT *pState, int32_t iBufMax) +{ + int32_t iChkSize, iLen, iLenInt; + + // if no new data, bail + if (pState->iInpLen == pState->iInpOff) + { + return(0); + } + + // see if we are starting a new chunk + if (pState->iChkLen == 0) + { + char *s = pState->pInpBuf+pState->iInpOff, *s2; + char *t = pState->pInpBuf+pState->iInpLen-1; + + // make sure we have a complete chunk header + for (s2=s; (s2 < t) && ((s2[0] != '\r') || (s2[1] != '\n')); s2++) + ; + if (s2 == t) + { + if (pState->iInpLen == pState->iInpMax) + { + // tell ProtoHttpRecv() to compact recv buffer next time around + pState->bCompactRecv = TRUE; + } + return(0); + } + + // get the chunk length + if ((pState->iChkLen = (int32_t)strtol(s, NULL, 16)) == 0) + { + // terminating chunk - clear the buffer and set state to done + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] parsed end chunk\n", pState)); + pState->iInpOff += s2-s+4; // remove chunk header plus terminating crlf + pState->iBodySize = pState->iBodyRcvd; + pState->eState = ST_DONE; + + // return no data + return(0); + } + else + { + NetPrintfVerbose((pState->iVerbose, 2, "protohttp: [%p] parsed chunk size=%d\n", pState, pState->iChkLen)); + } + + // remove header from input + pState->iInpOff += s2-s+2; + } + + // calculate length + iLenInt = pState->iInpLen - pState->iInpOff; + iLen = (iLenInt > iBufMax) ? iBufMax : iLenInt; + + // have we got all the data? + if (iLen >= pState->iChkLen) + { + /* got chunk and trailer, so consume it. we use the internal data size rather than the data size + that is constrained by the user buffer size as the user isn't going to read the chunk data and + therefore must not be limited by a requirement to read it. not doing so results in the user not + being able to consume the whole chunk if their buffer does not have room for the chunk trailer */ + if (iLenInt >= (pState->iChkLen+2)) + { + // reset chunk length + iChkSize = pState->iChkLen; + pState->iChkLen = 0; + } + else + { + if (pState->iChkLen > 1) + { + /* consume data and compact recv buffer to make room for the trailer; however we leave one + byte unread to cover the case where the chunk trailer spans a recv boundary, and where the + next byte is not available to read from the network. in such a case we wait until the chunk + trailer is completely available before finishing the chunk, otherwise we end up reading the + one byte of chunk trailer as if it were the next chunk size */ + iChkSize = pState->iChkLen-1; + pState->iChkLen -= iChkSize; + } + else + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] waiting for chunk trailer\n", pState)); + iChkSize = 0; + } + // tell ProtoHttpRecv() to compact recv buffer next time around + pState->bCompactRecv = TRUE; + } + } + else + { + iChkSize = iLen; + pState->iChkLen -= iChkSize; + } + + return(iChkSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpSend + + \Description + Try and send some data. If data is sent, the timout value is updated. + + \Input *pState - reference pointer + \Input *pStrBuf - pointer to buffer to send from + \Input iSize - amount of data to try and send + + \Output + int32_t - negative=error, else number of bytes sent + + \Version 11/18/2004 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpSend(ProtoHttpRefT *pState, const char *pStrBuf, int32_t iSize) +{ + int32_t iResult; + + // try and send some data + if ((iResult = ProtoSSLSend(pState->pSsl, pStrBuf, iSize)) > 0) + { + #if DIRTYCODE_LOGGING + if (pState->iVerbose > 2) + { + NetPrintf(("protohttp: [%p] sent %d bytes\n", pState, iResult)); + } + if (pState->iVerbose > 3) + { + NetPrintMem(pStrBuf, iResult, "http-send"); + } + #endif + + // sent data, so update timeout + pState->uTimer = NetTick() + pState->uTimeout; + } + else if (iResult < 0) + { + NetPrintf(("protohttp: [%p] error %d sending %d bytes\n", pState, iResult, iSize)); + pState->eState = ST_FAIL; + pState->iSslFail = ProtoSSLStat(pState->pSsl, 'fail', NULL, 0); + pState->iHresult = ProtoSSLStat(pState->pSsl, 'hres', NULL, 0); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpSendBuff + + \Description + Send data queued in buffer. + + \Input *pState - reference pointer + + \Output + int32_t - negative=error, else number of bytes sent + + \Version 01/29/2010 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpSendBuff(ProtoHttpRefT *pState) +{ + int32_t iResult, iSentSize = 0; + if ((iResult = _ProtoHttpSend(pState, pState->pInpBuf+pState->iInpOff, pState->iInpLen)) > 0) + { + pState->iInpOff += iResult; + pState->iInpLen -= iResult; + if (pState->iInpLen == 0) + { + pState->iInpOff = 0; + } + iSentSize = iResult; + } + else if (iResult < 0) + { + NetPrintf(("protohttp: [%p] failed to send request data (err=%d)\n", pState, iResult)); + pState->iInpLen = 0; + pState->eState = ST_FAIL; + iSentSize = -1; + } + return(iSentSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpFormatChunk + + \Description + Format source data into chunked format, ready to be sent. + + \Input *pState - reference pointer + \Input *pStrBuf - pointer to buffer to send from + \Input iSize - amount of data to try and send (zero for stream completion) + + \Output + int32_t - negative=space required, else number of bytes of user data sent + + \Notes + Space is reserved for an end chunk to be buffered so the ProtoHttpSend() + call indicating the stream is complete does not ever need to be retried. + + \Version 03/21/2014 (jbrookes) Refactored from _ProtoHttpSendChunk() +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpFormatChunk(ProtoHttpRefT *pState, const char *pStrBuf, int32_t iSize) +{ + char *pInpBuf = pState->pInpBuf + pState->iInpLen + pState->iInpOff; + int32_t iInpMax = pState->iInpMax - pState->iInpLen - pState->iInpOff; + int32_t iSendSize, iSentSize; + + // make sure we have enough room for a max chunk header, chunk data, *and* possible end chunk + if ((iSendSize = iSize) > 0) + { + if (iSendSize > (iInpMax - (6+2+2 + 1+2+2))) + { + iSendSize = (iInpMax - (6+2+2 + 1+2+2)); + } + } + else + { + pState->iPostSize = 0; + } + + // if we have room to buffer chunk data, or this is the end chunk, do it + if ((iSendSize > 0) || (iSize == 0)) + { + // format chunk into buffer + iSentSize = ds_snzprintf(pInpBuf, iInpMax, "%x\r\n", iSendSize); + if (iSendSize > 0) + { + ds_memcpy(pInpBuf+iSentSize, pStrBuf, iSendSize); + iSentSize += iSendSize; + } + iSentSize += ds_snzprintf(pInpBuf+iSentSize, iInpMax, "\r\n"); + + // add chunk to length + pState->iInpLen += iSentSize; + } + else + { + iSendSize = 0; + } + // return size of data buffered to caller + return(iSendSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpSendChunk + + \Description + Send the specified data using chunked transfer encoding. + + \Input *pState - reference pointer + \Input *pStrBuf - pointer to buffer to send from + \Input iSize - amount of data to try and send (zero for stream completion) + + \Output + int32_t - negative=error, else number of bytes of user data sent + + \Notes + $$TODO - update + Unlike _ProtoHttpSend(), which simply passes the data to ProtoSSL and returns + the amount of data that was accepted, this function buffers one or more chunks + of data, up to the buffer limit. It tries to send the buffered data immediately, + however if the band the data is sent by ProtoHttpUpdate(). + This guarantees that a chunk will be sent correctly even if a partial send + occurs. + + \Version 05/07/2008 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpSendChunk(ProtoHttpRefT *pState, const char *pStrBuf, int32_t iSize) +{ + int32_t iBuffSize, iCompSize; + + // try to buffer chunk data + if ((iBuffSize = _ProtoHttpFormatChunk(pState, pStrBuf, iSize)) < 0) + { + // try compacting send buffer to make room for more data + if ((iCompSize = _ProtoHttpCompactBuffer(pState)) > 0) + { + // buffer was compacted, retry + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] compacted send buffer (%d bytes)\n", pState, iCompSize)); + // try to buffer chunk data again + iBuffSize = _ProtoHttpFormatChunk(pState, pStrBuf, iSize); + } + } + + // try to send any buffered data + _ProtoHttpSendBuff(pState); + + // return buffered data size to caller + return((iBuffSize > 0) ? iBuffSize : 0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpRecvData + + \Description + Try and receive some data. If data is received, the timout value is + updated. + + \Input *pState - reference pointer + \Input *pStrBuf - [out] pointer to buffer to receive into + \Input iSize - amount of data to try and receive + + \Output + int32_t - negative=error, else success + + \Version 11/12/2004 (jbrookes) First Version. +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpRecvData(ProtoHttpRefT *pState, char *pStrBuf, int32_t iSize) +{ + // if we have no buffer space, don't try to receive + if (iSize == 0) + { + return(0); + } + + // try and receive some data + if ((pState->iRecvRslt = ProtoSSLRecv(pState->pSsl, pStrBuf, iSize)) > 0) + { + #if DIRTYCODE_LOGGING + if (pState->iVerbose > 2) + { + NetPrintf(("protohttp: [%p] recv %d bytes\n", pState, pState->iRecvRslt)); + } + if (pState->iVerbose > 3) + { + NetPrintMem(pStrBuf, pState->iRecvRslt, "http-recv"); + } + #endif + + // received data, so update timeout + pState->uTimer = NetTick() + pState->uTimeout; + } + return(pState->iRecvRslt); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpHeaderRecvFirstLine + + \Description + Try and receive the first line of the response header. We receive into + the header cache buffer directly so we can avoid conflicting with possible + use of the input buffer sending streaming data. This allows us to receive + while we are sending, which is useful in detecting situations where the + server responsds with an error in the middle of a streaming post transaction. + + \Input *pState - reference pointer + + \Output + int32_t - negative=error, zero=pending, else success + + \Version 01/13/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpHeaderRecvFirstLine(ProtoHttpRefT *pState) +{ + int32_t iResult; + for (iResult = 1; (iResult == 1) && (pState->iHdrOff < 64); ) // hard-coded max limit in case this is not http + { + if ((iResult = _ProtoHttpRecvData(pState, pState->strHdr+pState->iHdrOff, 1)) == 1) + { + pState->iHdrOff += 1; + if ((pState->strHdr[pState->iHdrOff-2] == '\r') && (pState->strHdr[pState->iHdrOff-1] == '\n')) + { + // we've received the first line of the response header... get response code + int32_t iHdrCode = ProtoHttpParseHeaderCode(pState->strHdr); + + /* if this is a streaming post, check the response code. we do this because a server might + abort a streaming upload prematurely if the file size is too large and this allows the + client to stop sending gracefully; typically the server will issue a forceful reset if + the client keeps sending data after being sent notification */ + if ((pState->iPostSize == -1) && (iHdrCode != PROTOHTTP_RESPONSE_CONTINUE) && (PROTOHTTP_GetResponseClass(iHdrCode) != PROTOHTTP_RESPONSE_SUCCESSFUL)) + { + NetPrintf(("protohttp [%p] got unexpected response %d during streaming post; aborting send\n", pState, iHdrCode)); + pState->iPostSize = 0; + } + break; + } + } + } + // return result unless we are in the middle of a streaming post + return((pState->iPostSize != -1) ? iResult : 0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpHeaderProcess + + \Description + Check for header completion and process header data + + \Input *pState - reference pointer + + \Version 05/03/2012 (jbrookes) Refactored out of ProtoHttpUpdate() +*/ +/********************************************************************************F*/ +static void _ProtoHttpHeaderProcess(ProtoHttpRefT *pState) +{ + // try parsing header + if (_ProtoHttpParseHeader(pState) < 0) + { + // was there a prior SOCKERR_CLOSED error? + if (pState->iRecvRslt < 0) + { + NetPrintf(("protohttp: [%p] ST_HEAD append got ST_FAIL (err=%d, len=%d)\n", pState, pState->iRecvRslt, pState->iInpLen)); + pState->eState = ST_FAIL; + } + // if the buffer is full, we don't have enough room to receive the header + if (pState->iInpLen == pState->iInpMax) + { + if (pState->iInpOvr == 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] input buffer too small for response header\n", pState)); + } + pState->iInpOvr = pState->iInpLen+1; + } + return; + } + + /* workaround for non-compliant 1.0 servers - some 1.0 servers specify a + Content Length of zero invalidly. if the response is a 1.0 response + and the Content Length is zero, and we've gotten body data, we set + the Content Length to -1 (indeterminate) */ + if ((pState->bHttp1_0 == TRUE) && (pState->iBodySize == 0) && (pState->iInpCnt > 0)) + { + pState->iBodySize = -1; + } + + // handle final processing + if ((pState->bHeadOnly == TRUE) || (pState->iHdrCode == PROTOHTTP_RESPONSE_NOCONTENT) || (pState->iHdrCode == PROTOHTTP_RESPONSE_NOTMODIFIED)) + { + // only needed the header (or response did not include a body; see HTTP RFC 1.1 section 4.4) -- all done + pState->eState = ST_DONE; + } + else if ((pState->iBodySize >= 0) && (pState->iInpCnt >= pState->iBodySize)) + { + // we got entire body with header -- all done + pState->eState = ST_DONE; + } + else + { + // wait for rest of body + pState->eState = ST_BODY; + } + + // handle response code? + if (PROTOHTTP_GetResponseClass(pState->iHdrCode) == PROTOHTTP_RESPONSE_INFORMATIONAL) + { + _ProtoHttpProcessInfoHeader(pState); + } + else if (PROTOHTTP_GetResponseClass(pState->iHdrCode) == PROTOHTTP_RESPONSE_REDIRECTION) + { + _ProtoHttpProcessRedirect(pState); + } + + /* handle upgrade to ssl after connect; note we have to check state because a redirection processed immediately + above can issue a new proxy connection requiring an upgrade, but it won't be in the right state yet */ + if (pState->bUpgradeSSL && (pState->eState == ST_BODY)) + { + if (ProtoSSLControl(pState->pSsl, 'secu', 0, 0, NULL) < 0) + { + NetPrintf(("protossl: failed to upgrade connection to SSL\n")); + pState->eState = ST_FAIL; + return; + } + + NetPrintf(("protossl: upgrading connection to SSL\n")); + ProtoSSLControl(pState->pSsl, 'host', 0, 0, pState->strProxiedHost); + + // restore and send user request + pState->pInpBuf = pState->pInpBufTmp; + pState->iInpLen = pState->iInpLenTmp; + pState->iInpOff = 0; + pState->pInpBufTmp = NULL; + pState->iInpLenTmp = 0; + + // send the request + pState->bNewConnection = FALSE; + pState->eState = ST_CONN; + + // upgrade completed + pState->bUpgradeSSL = FALSE; + // set keep-alive + pState->iKeepAlive += 1; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpRecvBody + + \Description + Attempt to recevive and process body data + + \Input *pState - reference pointer + + \Output + int32_t - zero=no data available + + \Version 05/03/2012 (jbrookes) Refactored out of ProtoHttpUpdate() +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpRecvBody(ProtoHttpRefT *pState) +{ + int32_t iResult; + + // reset pointers if needed + if ((pState->iInpLen > 0) && (pState->iInpOff == pState->iInpLen)) + { + pState->iInpOff = pState->iInpLen = 0; + } + + // check for data + iResult = pState->iInpMax - pState->iInpLen; + if (iResult <= 0) + { + // always return zero bytes since buffer is full + iResult = 0; + } + else + { + // try to add to buffer + iResult = _ProtoHttpRecvData(pState, pState->pInpBuf+pState->iInpLen, iResult); + } + if (iResult == 0) + { + return(0); + } + + // check for connection close + if ((iResult == SOCKERR_CLOSED) && ((pState->iBodySize == -1) || (pState->iBodySize == pState->iInpCnt))) + { + if (!pState->bChunked) + { + pState->iBodySize = pState->iInpCnt; + } + pState->bCloseHdr = TRUE; + pState->eState = ST_DONE; + } + else if (iResult > 0) + { + // add to buffer + pState->iInpLen += iResult; + pState->iInpCnt += iResult; + + // check for end of body + if ((pState->iBodySize >= 0) && (pState->iInpCnt >= pState->iBodySize)) + { + pState->eState = ST_DONE; + } + } + else if (iResult < 0) + { + NetPrintf(("protohttp: [%p] ST_FAIL (err=%d)\n", (uintptr_t)pState, iResult)); + pState->eState = ST_FAIL; + pState->iSslFail = ProtoSSLStat(pState->pSsl, 'fail', NULL, 0); + pState->iHresult = ProtoSSLStat(pState->pSsl, 'hres', NULL, 0); + } + + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpRecv + + \Description + Return the actual url data. + + \Input *pState - reference pointer + \Input *pBuffer - buffer to store data in + \Input iBufMin - minimum number of bytes to return (returns zero if this number is not available) + \Input iBufMax - maximum number of bytes to return (buffer size) + + \Output + int32_t - negative=error, zero=no data available or bufmax <= 0, positive=number of bytes read + + \Version 04/12/2000 (gschaefer) First Version +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpRecv(ProtoHttpRefT *pState, char *pBuffer, int32_t iBufMin, int32_t iBufMax) +{ + int32_t iLen; + + // early out for failure result + if (pState->eState == ST_FAIL) + { + int32_t iResult = PROTOHTTP_RECVFAIL; + if (pState->iNumRedirect > pState->iMaxRedirect) + { + iResult = PROTOHTTP_RECVRDIR; + } + else if (pState->bTimeout) + { + iResult = PROTOHTTP_TIMEOUT; + } + return(iResult); + } + // check for input buffer too small for header + if (pState->iInpOvr > 0) + return(PROTOHTTP_MINBUFF); + // waiting for data + if ((pState->eState != ST_BODY) && (pState->eState != ST_DONE)) + return(PROTOHTTP_RECVWAIT); + + // if they only wanted head, thats all they get + if (pState->bHeadOnly == TRUE) + return(PROTOHTTP_RECVHEAD); + + // if they are querying only for done state when no more data is available to be read + if((iBufMax == 0) && ((pState->eState == ST_DONE) && (pState->iBodyRcvd == pState->iBodySize))) + return(PROTOHTTP_RECVDONE); + + // make sure range is valid + if (iBufMax < 1) + return(0); + + // clamp the range + if (iBufMin < 1) + iBufMin = 1; + if (iBufMax < iBufMin) + iBufMax = iBufMin; + if (iBufMin > pState->iInpMax) + iBufMin = pState->iInpMax; + if (iBufMax > pState->iInpMax) + iBufMax = pState->iInpMax; + + // see if we need to shift buffer + if ((iBufMin > pState->iInpMax-pState->iInpOff) || (pState->bCompactRecv == TRUE)) + { + // compact receive buffer + _ProtoHttpCompactBuffer(pState); + // give chance to get more data + _ProtoHttpRecvBody(pState); + } + + // figure out how much data is available + if (pState->bChunked == TRUE) + { + iLen = _ProtoHttpChunkProcess(pState, iBufMax); + } + else if ((iLen = (pState->iInpLen - pState->iInpOff)) > iBufMax) + { + iLen = iBufMax; + } + + // check for end of data + if ((iLen == 0) && (pState->eState == ST_DONE)) + { + return(PROTOHTTP_RECVDONE); + } + + // special check for responses with trailing piped data + if (pState->iPipedRequests > 0) + { + // check for end of this transaction + if (pState->iBodyRcvd == pState->iBodySize) + { + return(PROTOHTTP_RECVDONE); + } + // clamp available data to body size + if ((pState->iBodySize != -1) && (iLen > (int32_t)(pState->iBodySize - pState->iBodyRcvd))) + { + iLen = (int32_t)(pState->iBodySize - pState->iBodyRcvd); + } + } + + // see if there is enough to return + if ((iLen >= iBufMin) || (pState->iInpCnt == pState->iBodySize)) + { + // return data to caller + if (pBuffer != NULL) + { + ds_memcpy(pBuffer, pState->pInpBuf+pState->iInpOff, iLen); + + #if DIRTYCODE_LOGGING + if (pState->iVerbose > 3) + { + NetPrintf(("protohttp: [%p] read %d bytes\n", pState, iLen)); + } + if (pState->iVerbose > 4) + { + NetPrintMem(pBuffer, iLen, "http-read"); + } + #endif + } + pState->iInpOff += iLen; + pState->iBodyRcvd += iLen; + + // if we're at the end of a chunk, skip trailing crlf + if ((pState->bChunked == TRUE) && (pState->iChkLen == 0)) + { + pState->iInpOff += 2; + } + + // return bytes read + return(iLen); + } + + // nothing available + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpWriteCbProcess + + \Description + User write callback processing, if write callback is set + + \Input *pState - reference pointer + + \Version 05/03/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpWriteCbProcess(ProtoHttpRefT *pState) +{ + ProtoHttpWriteCbInfoT WriteCbInfo; + int32_t iResult; + + ds_memclr(&WriteCbInfo, sizeof(WriteCbInfo)); + WriteCbInfo.eRequestType = pState->eRequestType; + WriteCbInfo.eRequestResponse = (ProtoHttpResponseE)pState->iHdrCode; + + /* download more data when the following are true: we are in the body state or we are in the done state but we haven't received + everything. note, the body size is negative for chunked transfers when we haven't processed the end chunk. */ + if ((pState->eState == ST_BODY) || ((pState->eState == ST_DONE) && ((pState->iBodySize < 0) || (pState->iBodyRcvd < pState->iBodySize)))) + { + char strTempRecv[1024]; + while ((iResult = _ProtoHttpRecv(pState, strTempRecv, 1, sizeof(strTempRecv))) > 0) + { + pState->pWriteCb(pState, &WriteCbInfo, strTempRecv, iResult, pState->pUserData); + } + } + + if (pState->eState > ST_BODY) + { + if (pState->eState == ST_DONE) + { + pState->pWriteCb(pState, &WriteCbInfo, "", pState->bHeadOnly ? PROTOHTTP_HEADONLY : PROTOHTTP_RECVDONE, pState->pUserData); + } + if (pState->eState == ST_FAIL) + { + pState->pWriteCb(pState, &WriteCbInfo, "", pState->bTimeout ? PROTOHTTP_TIMEOUT : PROTOHTTP_RECVFAIL, pState->pUserData); + } + pState->pWriteCb = NULL; + pState->pReqCustomHeaderCb = NULL; + pState->pReqReceiveHeaderCb = NULL; + pState->pUserData = NULL; + } +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoHttpCreate + + \Description + Allocate module state and prepare for use + + \Input iBufSize - length of receive buffer + + \Output + ProtoHttpRefT * - pointer to module state, or NULL + + \Version 04/12/2000 (gschaefer) First Version +*/ +/********************************************************************************F*/ +ProtoHttpRefT *ProtoHttpCreate(int32_t iBufSize) +{ + ProtoHttpRefT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // clamp the buffer size + if (iBufSize < 4096) + { + iBufSize = 4096; + } + + // allocate the resources + if ((pState = DirtyMemAlloc(sizeof(*pState), PROTOHTTP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pState, sizeof(*pState)); + // save memgroup (will be used in ProtoHttpDestroy) + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + + // allocate ssl module + if ((pState->pSsl = ProtoSSLCreate()) == NULL) + { + NetPrintf(("protohttp: [%p] unable to allocate ssl module\n", pState)); + ProtoHttpDestroy(pState); + return(NULL); + } + if ((pState->pInpBuf = DirtyMemAlloc(iBufSize, PROTOHTTP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp: [%p] unable to allocate protohttp buffer\n", pState)); + ProtoHttpDestroy(pState); + return(NULL); + } + + // init crit + NetCritInit(&pState->HttpCrit, "ProtoHttp"); + + // save parms & set defaults + pState->eState = ST_IDLE; + pState->iInpMax = iBufSize; + pState->uTimeout = PROTOHTTP_TIMEOUT_DEFAULT; + pState->bVerifyHdr = TRUE; + pState->iVerbose = 1; + pState->iMaxRedirect = PROTOHTTP_MAXREDIRECT; + + // return the state + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpCallback + + \Description + Set header callbacks. + + \Input *pState - module state + \Input *pCustomHeaderCb - pointer to custom send header callback (may be NULL) + \Input *pReceiveHeaderCb- pointer to recv header callback (may be NULL) + \Input *pCallbackRef - user-supplied callback ref (may be NULL) + + \Notes + The ProtHttpCustomHeaderCbt callback is used to allow customization of + the HTTP header before sending. It is more powerful than the append + header functionality, allowing to make changes to any part of the header + before it is sent. The callback should return a negative code if an error + occurred, zero can be returned if the application wants ProtoHttp to + calculate the header size, or the size of the header can be returned if + the application has already calculated it. The header should *not* be + terminated with the double \\r\\n that indicates the end of the entire + header, as protohttp appends itself. + + The ProtoHttpReceiveHeaderCbT callback is used to view the header + immediately on reception. It can be used when the built-in header + cache (retrieved with ProtoHttpStatus('htxt') is too small to hold + the entire header received. It is also possible with this method + to view redirection response headers that cannot be retrieved + normally. This can be important if, for example, the application + wishes to attach new cookies to a redirection response. The + custom response header and custom header callback can be used in + conjunction to implement this type of functionality. + + \Version 1.0 02/16/2007 (jbrookes) First Version +*/ +/********************************************************************************F*/ +void ProtoHttpCallback(ProtoHttpRefT *pState, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pCallbackRef) +{ + pState->pCustomHeaderCb = pCustomHeaderCb; + pState->pReceiveHeaderCb = pReceiveHeaderCb; + pState->pCallbackRef = pCallbackRef; +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpDestroy + + \Description + Destroy the module and release its state + + \Input *pState - reference pointer + + \Output + None. + + \Version 04/12/2000 (gschaefer) First Version +*/ +/********************************************************************************F*/ +void ProtoHttpDestroy(ProtoHttpRefT *pState) +{ + if (pState->pSsl != NULL) + { + ProtoSSLDestroy(pState->pSsl); + } + _ProtoHttpFreeInputBuf(pState); + if (pState->pAppendHdr != NULL) + { + DirtyMemFree(pState->pAppendHdr, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + NetCritKill(&pState->HttpCrit); + DirtyMemFree(pState, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpGet + + \Description + Initiate an HTTP transfer. Pass in a URL and the module starts a transfer + from the appropriate server. + + \Input *pState - reference pointer + \Input *pUrl - the url to retrieve + \Input bHeadOnly - if TRUE only get header + + \Output + int32_t - negative=failure, else success + + \Version 04/12/2000 (gschaefer) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpGet(ProtoHttpRefT *pState, const char *pUrl, uint32_t bHeadOnly) +{ + int32_t iResult; + + // reset redirection count + pState->iNumRedirect = 0; + + // format the request + if (pUrl != NULL) + { + if ((iResult = _ProtoHttpFormatRequest(pState, pUrl, NULL, 0, bHeadOnly ? PROTOHTTP_REQUESTTYPE_HEAD : PROTOHTTP_REQUESTTYPE_GET)) < 0) + { + return(iResult); + } + } + // issue the request + if (!pState->bPipelining || (pUrl == NULL)) + { + _ProtoHttpSendRequest(pState); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpRecv + + \Description + Return the actual url data. + + \Input *pState - reference pointer + \Input *pBuffer - buffer to store data in + \Input iBufMin - minimum number of bytes to return (returns zero if this number is not available) + \Input iBufMax - maximum number of bytes to return (buffer size) + + \Output + int32_t - negative=error, zero=no data available or bufmax <= 0, positive=number of bytes read + + \Version 04/12/2000 (gschaefer) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpRecv(ProtoHttpRefT *pState, char *pBuffer, int32_t iBufMin, int32_t iBufMax) +{ + int32_t iResult; + + NetCritEnter(&pState->HttpCrit); + iResult = _ProtoHttpRecv(pState, pBuffer, iBufMin, iBufMax); + NetCritLeave(&pState->HttpCrit); + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpRecvAll + + \Description + Return all of the url data. + + \Input *pState - reference pointer + \Input *pBuffer - buffer to store data in + \Input iBufSize - size of buffer + + \Output + int32_t - PROTOHTTP_RECV*, or positive=bytes in response + + \Version 12/14/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpRecvAll(ProtoHttpRefT *pState, char *pBuffer, int32_t iBufSize) +{ + int32_t iRecvMax, iRecvResult; + + // try to read as much data as possible (leave one byte for null termination) + for (iRecvMax = iBufSize-1; (iRecvResult = ProtoHttpRecv(pState, pBuffer + pState->iRecvSize, 1, iRecvMax - pState->iRecvSize)) > 0; ) + { + // add to amount received + pState->iRecvSize += iRecvResult; + } + + // check response code + if (iRecvResult == PROTOHTTP_RECVDONE) + { + // null-terminate response & return completion success + if ((pBuffer != NULL) && (iBufSize > 0)) + { + pBuffer[pState->iRecvSize] = '\0'; + } + iRecvResult = pState->iRecvSize; + } + else if ((iRecvResult < 0) && (iRecvResult != PROTOHTTP_RECVWAIT)) + { + // an error occurred + NetPrintf(("protohttp: [%p] error %d receiving response\n", pState, iRecvResult)); + } + else if (iRecvResult == 0) + { + iRecvResult = (pState->iRecvSize < iRecvMax) ? PROTOHTTP_RECVWAIT : PROTOHTTP_RECVBUFF; + } + + // return result to caller + return(iRecvResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpPost + + \Description + Initiate transfer of data to to the server via a HTTP POST command. + + \Input *pState - reference pointer + \Input *pUrl - the URL that identifies the POST action. + \Input *pData - pointer to URL data (optional, may be NULL) + \Input iDataSize - size of data being uploaded (see Notes below) + \Input bDoPut - if TRUE, do a PUT instead of a POST + + \Output + int32_t - negative=failure, else number of data bytes sent + + \Notes + Any data that is not sent as part of the Post transaction should be sent + with ProtoHttpSend(). ProtoHttpSend() should be called at poll rate (i.e. + similar to how often ProtoHttpUpdate() is called) until all of the data has + been sent. The amount of data bytes actually sent is returned by the + function. + + If pData is not NULL and iDataSize is less than or equal to zero, iDataSize + will be recalculated as the string length of pData, to allow for easy string + sending. + + If pData is NULL and iDataSize is negative, the transaction is assumed to + be a streaming transaction and a chunked transfer will be performed. A + subsequent call to ProtoHttpSend() with iDataSize equal to zero will end + the transaction. + + \Version 03/03/2004 (sbevan) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpPost(ProtoHttpRefT *pState, const char *pUrl, const char *pData, int64_t iDataSize, uint32_t bDoPut) +{ + int32_t iDataSent; + // allow for easy string sending + if ((pData != NULL) && (iDataSize <= 0)) + { + iDataSize = (int32_t)strlen(pData); + } + // save post size (or -1 to indicate that this is a variable-length stream) + pState->iPostSize = iDataSize; + // make the request + if ((iDataSent = _ProtoHttpFormatRequest(pState, pUrl, pData, iDataSize, bDoPut ? PROTOHTTP_REQUESTTYPE_PUT : PROTOHTTP_REQUESTTYPE_POST)) >= 0) + { + // send the request + _ProtoHttpSendRequest(pState); + } + return(iDataSent); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpSend + + \Description + Send data during an ongoing Post transaction. + + \Input *pState - reference pointer + \Input *pData - pointer to data to send + \Input iDataSize - size of data being sent + + \Output + int32_t - negative=failure, else number of data bytes sent + + \Version 11/18/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpSend(ProtoHttpRefT *pState, const char *pData, int32_t iDataSize) +{ + int32_t iResult; + + // make sure we are in a sending state + if (pState->eState < ST_RESP) + { + // not ready to send data yet + return(0); + } + else if (pState->eState != ST_RESP) + { + // if we're past ST_RESP, an error occurred. + return(-1); + } + + /* clamp to max ProtoHttp buffer size - even though + we don't queue the data in the ProtoHttp buffer, this + gives us a reasonable max size to send in one chunk */ + if (iDataSize > pState->iInpMax) + { + iDataSize = pState->iInpMax; + } + + // get sole access to httpcrit + NetCritEnter(&pState->HttpCrit); + // send the data + iResult = (pState->iPostSize < 0) ? _ProtoHttpSendChunk(pState, pData, iDataSize) : _ProtoHttpSend(pState, pData, iDataSize); + // release access to httpcrit + NetCritLeave(&pState->HttpCrit); + + // return result + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpDelete + + \Description + Request deletion of a server-based resource. + + \Input *pState - reference pointer + \Input *pUrl - Url describing reference to delete + + \Output + int32_t - negative=failure, zero=success + + \Version 06/01/2009 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpDelete(ProtoHttpRefT *pState, const char *pUrl) +{ + int32_t iResult; + + // reset redirection count + pState->iNumRedirect = 0; + + // format the request + if ((iResult = _ProtoHttpFormatRequest(pState, pUrl, NULL, 0, PROTOHTTP_REQUESTTYPE_DELETE)) >= 0) + { + // issue the request + _ProtoHttpSendRequest(pState); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpOptions + + \Description + Request options from a server. + + \Input *pState - reference pointer + \Input *pUrl - Url describing reference to get options on + + \Output + int32_t - negative=failure, zero=success + + \Version 07/14/2010 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpOptions(ProtoHttpRefT *pState, const char *pUrl) +{ + int32_t iResult; + + // reset redirection count + pState->iNumRedirect = 0; + + // format the request + if ((iResult = _ProtoHttpFormatRequest(pState, pUrl, NULL, 0, PROTOHTTP_REQUESTTYPE_OPTIONS)) >= 0) + { + // issue the request + _ProtoHttpSendRequest(pState); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpPatch + + \Description + Initiate transfer of data to to the server via a HTTP PATCH command. + + \Input *pState - reference pointer + \Input *pUrl - the URL that identifies the POST action. + \Input *pData - pointer to URL data (optional, may be NULL) + \Input iDataSize - size of data being uploaded (see Notes below) + + \Output + int32_t - negative=failure, else number of data bytes sent + + \Notes + Any data that is not sent as part of the Patch transaction should be sent + with ProtoHttpSend(). ProtoHttpSend() should be called at poll rate (i.e. + similar to how often ProtoHttpUpdate() is called) until all of the data has + been sent. The amount of data bytes actually sent is returned by the + function. + + If pData is not NULL and iDataSize is less than or equal to zero, iDataSize + will be recalculated as the string length of pData, to allow for easy string + sending. + + If pData is NULL and iDataSize is negative, the transaction is assumed to + be a streaming transaction and a chunked transfer will be performed. A + subsequent call to ProtoHttpSend() with iDataSize equal to zero will end + the transaction. + + \Version 01/01/2017 (amakoukji) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpPatch(ProtoHttpRefT *pState, const char *pUrl, const char *pData, int64_t iDataSize) +{ + int32_t iDataSent; + // allow for easy string sending + if ((pData != NULL) && (iDataSize <= 0)) + { + iDataSize = (int32_t)strlen(pData); + } + // save post size (or -1 to indicate that this is a variable-length stream) + pState->iPostSize = iDataSize; + // make the request + if ((iDataSent = _ProtoHttpFormatRequest(pState, pUrl, pData, iDataSize, PROTOHTTP_REQUESTTYPE_PATCH)) >= 0) + { + // send the request + _ProtoHttpSendRequest(pState); + } + return(iDataSent); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpRequestCb2 + + \Description + Initiate an HTTP transfer. Pass in a URL and the module starts a transfer + from the appropriate server. Use ProtoHttpRequest() macro wrapper if + callbacks are not required. + + \Input *pState - reference pointer + \Input *pUrl - the url to retrieve + \Input *pData - user data to send to server (PUT and POST only) + \Input iDataSize - size of user data to send to server (PUT and POST only) + \Input eRequestType - request type to make + \Input *pWriteCb - write callback (optional) + \Input *pCustomHeaderCb - custom header callback (optional) + \Input *pReceiveHeaderCb - receive header callback (optional) + \Input *pUserData - callback user data (optional) + + \Output + int32_t - negative=failure, zero=success, >0=number of data bytes sent (PUT and POST only) + + \Version 09/11/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpRequestCb2(ProtoHttpRefT *pState, const char *pUrl, const char *pData, int64_t iDataSize, ProtoHttpRequestTypeE eRequestType, ProtoHttpWriteCbT *pWriteCb, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData) +{ + int32_t iResult; + + // save callbacks + pState->pWriteCb = pWriteCb; + pState->pReqCustomHeaderCb = pCustomHeaderCb; + pState->pReqReceiveHeaderCb = pReceiveHeaderCb; + pState->pUserData = pUserData; + + // make the request + if ((eRequestType == PROTOHTTP_REQUESTTYPE_GET) || (eRequestType == PROTOHTTP_REQUESTTYPE_HEAD)) + { + iResult = ProtoHttpGet(pState, pUrl, eRequestType == PROTOHTTP_REQUESTTYPE_HEAD); + } + else if ((eRequestType == PROTOHTTP_REQUESTTYPE_PUT) || (eRequestType == PROTOHTTP_REQUESTTYPE_POST)) + { + iResult = ProtoHttpPost(pState, pUrl, pData, iDataSize, eRequestType == PROTOHTTP_REQUESTTYPE_PUT); + } + else if (eRequestType == PROTOHTTP_REQUESTTYPE_DELETE) + { + iResult = ProtoHttpDelete(pState, pUrl); + } + else if (eRequestType == PROTOHTTP_REQUESTTYPE_OPTIONS) + { + iResult = ProtoHttpOptions(pState, pUrl); + } + else if (eRequestType == PROTOHTTP_REQUESTTYPE_PATCH) + { + iResult = ProtoHttpPatch(pState, pUrl, pData, iDataSize); + } + else + { + NetPrintf(("protohttp: [%p] unrecognized request type %d\n", pState, eRequestType)); + iResult = -1; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpAbort + + \Description + Abort current operation, if any. + + \Input *pState - reference pointer + + \Output + None. + + \Version 12/01/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +void ProtoHttpAbort(ProtoHttpRefT *pState) +{ + // acquire sole access to http crit + NetCritEnter(&pState->HttpCrit); + + // terminate current connection, if any + _ProtoHttpClose(pState, "abort"); + + // reset state + _ProtoHttpReset(pState); + + // release sole access to http crit + NetCritLeave(&pState->HttpCrit); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpSetBaseUrl + + \Description + Set base url that will be used for any relative url references. + + \Input *pState - module state + \Input *pUrl - base url + + \Output + None + + \Version 02/03/2010 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoHttpSetBaseUrl(ProtoHttpRefT *pState, const char *pUrl) +{ + char strHost[sizeof(pState->strHost)], strKind[8]; + int32_t iPort, iSecure; + uint8_t bPortSpecified; + + // parse the url for kind, host, and port + ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure, &bPortSpecified); + + // set base info + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] setting base url to %s://%s:%d\n", pState, iSecure ? "https" : "http", strHost, iPort)); + ds_strnzcpy(pState->strBaseHost, strHost, sizeof(pState->strBaseHost)); + pState->iBasePort = iPort; + pState->iBaseSecure = iSecure; +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpGetLocationHeader + + \Description + Get location header from the input buffer. The Location header requires + some special processing. + + \Input *pState - reference pointer + \Input *pInpBuf - buffer holding header text + \Input *pBuffer - [out] output buffer for parsed location header, null for size request + \Input iBufSize - size of output buffer, zero for size request + \Input **pHdrEnd- [out] pointer past end of parsed header (optional) + + \Output + int32_t - negative=not found or not enough space, zero=success, positive=size query result + + \Version 03/24/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpGetLocationHeader(ProtoHttpRefT *pState, const char *pInpBuf, char *pBuffer, int32_t iBufSize, const char **pHdrEnd) +{ + const char *pLocHdr; + int32_t iLocLen, iLocPreLen=0; + + // get a pointer to header + if ((pLocHdr = ProtoHttpFindHeaderValue(pInpBuf, "location")) == NULL) + { + return(-1); + } + + /* according to RFC location headers should be absolute, but some webservers respond with relative + URL's. we assume any url that does not include "://" is a relative url, and if we find one, we + assume the request keeps the same security, port, and host as the previous request */ + if ((pState != NULL) && (!strstr(pLocHdr, "://"))) + { + char strTemp[288]; // space for max DNS name (253 chars) plus max http url prefix + int32_t iPort, iSecure; + char *pStrHost; + + // get host info; if we're using a proxy we need to look at the proxied* fields + if (pState->strProxiedHost[0] != '\0') + { + pStrHost = pState->strProxiedHost; + iPort = pState->iProxiedPort; + iSecure = pState->iProxiedSecure; + } + else + { + pStrHost = pState->strHost; + iPort = pState->iPort; + iSecure = pState->iSecure; + } + + // format http url prefix + if ((pState->iSecure && (iPort == 443)) || (iPort == 80)) + { + ds_snzprintf(strTemp, sizeof(strTemp), "%s://%s", iSecure ? "https" : "http", pStrHost); + } + else + { + ds_snzprintf(strTemp, sizeof(strTemp), "%s://%s:%d", iSecure ? "https" : "http", pStrHost, iPort); + } + + // make sure relative URL includes '/' as the first character, required when we format the redirection url + if (*pLocHdr != '/') + { + ds_strnzcat(strTemp, "/", sizeof(strTemp)); + } + + // calculate url prefix length + iLocPreLen = (int32_t)strlen(strTemp); + + // copy url prefix text if a buffer is specified + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, strTemp, iBufSize); + pBuffer = (char *)((uint8_t *)pBuffer + iLocPreLen); + iBufSize -= iLocPreLen; + } + } + + // extract location header and return size + iLocLen = ProtoHttpExtractHeaderValue(pLocHdr, pBuffer, iBufSize, pHdrEnd); + // if it's a size request add in (possible) url header length + if ((pBuffer == NULL) && (iBufSize == 0)) + { + iLocLen += iLocPreLen; + } + + // return to caller + return(iLocLen); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpControl + + \Description + ProtoHttp control function. Different selectors control different behaviors. + + \Input *pState - reference pointer + \Input iSelect - control selector ('time') + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR DESCRIPTION + 'apnd' The given buffer will be appended to future headers sent + by ProtoHttp. Note that the User-Agent and Accept lines + in the default header will be replaced, so if these lines + are desired, they should be supplied in the append header. + 'disc' Close current connection, if any. + 'hver' Sets header type verification: zero=disabled, else enabled + 'ires' Resize input buffer + 'keep' Sets keep-alive; zero=disabled, else enabled + 'gpxy' Set global proxy server to use for all protohttp refs + 'pipe' Sets pipelining; zero=disabled, else enabled + 'pnxt' Call to proceed to next piped result + 'prxy' Set proxy server to use for this ref + 'rmax' Sets maximum number of redirections (default=3; 0=disable) + 'rput' Sets connection-reuse on put/post (TRUE=enabled, default FALSE) + 'spam' Sets debug output verbosity (0...n) + 'time' Sets ProtoHttp client timeout in milliseconds (default=30s) + \endverbatim + + Unhandled selectors are passed on to ProtoSSL. + + \Version 11/12/2004 (jbrookes) First Version +*/ +/*******************************************************************************F*/ +int32_t ProtoHttpControl(ProtoHttpRefT *pState, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iSelect == 'apnd') + { + return(_ProtoHttpSetAppendHeader(pState, (const char *)pValue)); + } + if (iSelect == 'disc') + { + _ProtoHttpClose(pState, "user request"); + return(0); + } + if (iSelect == 'hver') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] header type verification %s\n", pState, iValue ? "enabled" : "disabled")); + pState->bVerifyHdr = iValue; + } + if (iSelect == 'ires') + { + return(_ProtoHttpResizeBuffer(pState, iValue)); + } + if (iSelect == 'keep') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] setting keep-alive to %d\n", pState, iValue)); + pState->iKeepAlive = pState->iKeepAliveDflt = iValue; + return(0); + } + if (iSelect == 'gpxy') + { + NetPrintf(("protohttp: setting %s as global proxy server\n", pValue)); + ds_strnzcpy(_ProtoHttp_strGlobalProxy, pValue, sizeof(_ProtoHttp_strGlobalProxy)); + return(0); + } + if (iSelect == 'pipe') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] pipelining %s\n", pState, iValue ? "enabled" : "disabled")); + pState->bPipelining = iValue ? TRUE : FALSE; + return(0); + } + if (iSelect == 'pnxt') + { + NetPrintfVerbose((pState->iVerbose, 2, "protohttp: [%p] proceeding to next pipeline result\n", pState)); + pState->bPipeGetNext = TRUE; + return(0); + } + if (iSelect == 'prxy') + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] setting %s as proxy server\n", pState, pValue)); + ds_strnzcpy(pState->strProxy, pValue, sizeof(pState->strProxy)); + return(0); + } + if (iSelect == 'rmax') + { + pState->iMaxRedirect = iValue; + return(0); + } + if (iSelect == 'rput') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] connection reuse on put/post %s\n", pState, iValue ? "enabled" : "disabled")); + pState->bReuseOnPost = iValue ? TRUE : FALSE; + return(0); + } + if (iSelect == 'spam') + { + pState->iVerbose = iValue; + // fall through to protossl + } + if (iSelect == 'time') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] setting timeout to %d ms\n", pState, iValue)); + pState->uTimeout = (unsigned)iValue; + return(0); + } + // unhandled -- pass on to ProtoSSL + return(ProtoSSLControl(pState->pSsl, iSelect, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpStatus + + \Description + Return status of current HTTP transfer. Status type depends on selector. + + \Input *pState - reference pointer + \Input iSelect - info selector (see Notes) + \Input *pBuffer - [out] storage for selector-specific output + \Input iBufSize - size of buffer + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR RETURN RESULT + 'body' negative=failed or pending, else size of body (for 64bit size, get via pBuffer) + 'code' negative=no response, else server response code (ProtoHttpResponseE) + 'data' negative=failed, zero=pending, positive=amnt of data ready + 'date' last-modified date, if available + 'done' negative=failed, zero=pending, positive=done + 'essl' returns protossl error state + 'head' negative=failed or pending, else size of header + 'host' current host copied to output buffer + 'hres' return hResult containing either the socket error or ssl error or the http status code + 'htxt' current received http header text copied to output buffer + 'info' copies most recent info header received, if any, to output buffer (one time only) + 'imax' size of input buffer + 'iovr' returns input buffer overflow size (valid on PROTOHTTP_MINBUFF result code) + 'plst' return whether any piped requests were lost due to a premature close + 'port' current port + 'rtxt' most recent http request header text copied to output buffer + 'rmax' returns currently configured max redirection count + 'time' TRUE if the client timed out the connection, else FALSE + \endverbatim + + Unhandled selectors are passed on to ProtoSSL. + + \Version 04/12/2000 (gschaefer) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpStatus(ProtoHttpRefT *pState, int32_t iSelect, void *pBuffer, int32_t iBufSize) +{ + // return protossl error state (we cache this since we reset the state when we disconnect on error) + if (iSelect == 'essl') + { + return(pState->iSslFail); + } + + // return current host + if (iSelect == 'host') + { + ds_strnzcpy(pBuffer, pState->strHost, iBufSize); + return(0); + } + + // return hResult containing either the socket error or ssl error or the http status code + if (iSelect == 'hres') + { + if (pState->iHdrCode > 0) + { + return(DirtyErrGetHResult(DIRTYAPI_PROTO_HTTP, pState->iHdrCode, (pState->iHdrCode >= PROTOHTTP_RESPONSE_CLIENTERROR) ? TRUE : FALSE)); + } + else + { + return(pState->iHresult); + } + } + + // return size of input buffer + if (iSelect == 'imax') + { + return(pState->iInpMax); + } + + // return input overflow amount (valid after PROTOHTTP_MINBUFF result code) + if (iSelect == 'iovr') + { + return(pState->iInpOvr); + } + + // return piped requests lost status + if (iSelect == 'plst') + { + return(pState->bPipedRequestsLost); + } + + // return current port + if (iSelect == 'port') + { + return(pState->iPort); + } + + // return current redirection max + if (iSelect == 'rmax') + { + return(pState->iMaxRedirect); + } + + // return most recent http request header text + if (iSelect == 'rtxt') + { + ds_strnzcpy(pBuffer, pState->strRequestHdr, iBufSize); + return(0); + } + + // done check: negative=failed, zero=pending, positive=done + if (iSelect == 'done') + { + if (pState->eState == ST_FAIL) + return(-1); + if (pState->eState == ST_DONE) + return(1); + return(0); + } + + // data check: negative=failed, zero=pending, positive=data ready + if (iSelect == 'data') + { + if (pState->eState == ST_FAIL) + return(-1); + if ((pState->eState == ST_BODY) || (pState->eState == ST_DONE)) + return(pState->iInpLen); + return(0); + } + + // return response code + if (iSelect == 'code') + return(pState->iHdrCode); + + // return timeout indicator + if (iSelect == 'time') + return(pState->bTimeout); + + // copies info header (if available) to output buffer + if (iSelect == 'info') + { + if (pState->bInfoHdr) + { + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, pState->strHdr, iBufSize); + } + pState->bInfoHdr = FALSE; + return(pState->iHdrCode); + } + return(0); + } + + // the following selectors early-out with errors in failure states + if ((iSelect == 'body') || (iSelect == 'date') || (iSelect == 'head') || (iSelect == 'htxt')) + { + // check the state + if (pState->eState == ST_FAIL) + return(-1); + if ((pState->eState != ST_BODY) && (pState->eState != ST_DONE)) + return(-2); + + // parse the tokens + if (iSelect == 'head') + { + return(pState->iHeadSize); + } + if (iSelect == 'body') + { + if ((pBuffer != NULL) && (iBufSize == sizeof(pState->iBodySize))) + { + ds_memcpy(pBuffer, &pState->iBodySize, iBufSize); + } + return((int32_t)pState->iBodySize); + } + if (iSelect == 'date') + { + return(pState->iHdrDate); + } + if (iSelect == 'htxt') + { + ds_strnzcpy(pBuffer, pState->strHdr, iBufSize); + return(0); + } + } + + // pass down to unhandled selectors to ProtoSSL + return(ProtoSSLStat(pState->pSsl, iSelect, pBuffer, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpCheckKeepAlive + + \Description + Checks whether a request for the given Url would be a keep-alive transaction. + + \Input *pState - reference pointer + \Input *pUrl - Url to check + + \Output + int32_t - TRUE if a request to this Url would use keep-alive, else FALSE + + \Version 05/12/2009 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpCheckKeepAlive(ProtoHttpRefT *pState, const char *pUrl) +{ + char strHost[sizeof(pState->strHost)], strKind[8]; + int32_t iPort, iSecure; + uint8_t bPortSpecified; + + // parse the url + ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure, &bPortSpecified); + + // refresh open status + if (pState->bConnOpen && (ProtoSSLStat(pState->pSsl, 'stat', NULL, 0) <= 0)) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp: [%p] check for keep-alive detected connection close\n", pState)); + pState->bConnOpen = FALSE; + } + + // see if a request for this url would use keep-alive + if (pState->bConnOpen && (pState->iKeepAlive > 0) && (pState->iPort == iPort) && (pState->iSecure == iSecure) && !ds_stricmp(pState->strHost, strHost)) + { + return(1); + } + else + { + return(0); + } +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUpdate + + \Description + Give time to module to do its thing (should be called periodically to + allow module to perform work) + + \Input *pState - reference pointer + + \Output + None. + + \Version 04/12/2000 (gschaefer) First Version +*/ +/********************************************************************************F*/ +void ProtoHttpUpdate(ProtoHttpRefT *pState) +{ + int32_t iResult; + + // give time to comm module + ProtoSSLUpdate(pState->pSsl); + + // acquire sole access to http crit + NetCritEnter(&pState->HttpCrit); + + // check for timeout + if ((pState->eState != ST_IDLE) && (pState->eState != ST_DONE) && (pState->eState != ST_FAIL)) + { + if (NetTickDiff(NetTick(), pState->uTimer) >= 0) + { + NetPrintf(("protohttp: [%p] server timeout (state=%d)\n", pState, pState->eState)); + pState->eState = ST_FAIL; + pState->bTimeout = TRUE; + } + } + + // see if connection is complete + if (pState->eState == ST_CONN) + { + iResult = ProtoSSLStat(pState->pSsl, 'stat', NULL, 0); + if (iResult > 0) + { + pState->uTimer = NetTick() + pState->uTimeout; + pState->eState = ST_SEND; + pState->bConnOpen = TRUE; + } + if (iResult < 0) + { + NetPrintf(("protohttp: [%p] ST_CONN got ST_FAIL (err=%d)\n", pState, iResult)); + pState->eState = ST_FAIL; + pState->iSslFail = ProtoSSLStat(pState->pSsl, 'fail', NULL, 0); + pState->iHresult = ProtoSSLStat(pState->pSsl, 'hres', NULL, 0); + } + } + + // send buffered header+data to webserver + if (pState->eState == ST_SEND) + { + if (((iResult = _ProtoHttpSendBuff(pState)) > 0) && (pState->iInpLen == 0)) + { + #if DIRTYCODE_LOGGING + _ProtoHttpDisplayHeader(pState); + #endif + pState->iInpOff = 0; + pState->iHdrOff = 0; + pState->eState = ST_RESP; + } + } + + // wait for initial response + if (pState->eState == ST_RESP) + { + // try to send any remaining buffered data + _ProtoHttpSendBuff(pState); + + // try for the first line of http response + if ((iResult = _ProtoHttpHeaderRecvFirstLine(pState)) > 0) + { + // copy first line of header to input buffer and proceed + ds_strnzcpy(pState->pInpBuf, pState->strHdr, pState->iHdrOff+1); + pState->iInpLen = pState->iHdrOff; + pState->eState = ST_HEAD; + } + else if ((iResult < 0) && !_ProtoHttpRetrySendRequest(pState)) + { + NetPrintf(("protohttp: [%p] ST_HEAD byte0 got ST_FAIL (err=%d)\n", pState, iResult)); + pState->iInpLen = 0; + pState->eState = ST_FAIL; + } + } + + // try to receive header data + if (pState->eState == ST_HEAD) + { + // append data to buffer + if ((iResult = _ProtoHttpRecvData(pState, pState->pInpBuf+pState->iInpLen, pState->iInpMax - pState->iInpLen)) > 0) + { + pState->iInpLen += iResult; + } + // deal with network error (defer handling closed state to next block, to allow pipelined receives of already buffered transactions) + if ((iResult < 0) && ((iResult != SOCKERR_CLOSED) || (pState->iInpLen <= 4))) + { + NetPrintf(("protohttp: [%p] ST_HEAD append got ST_FAIL (err=%d, len=%d)\n", pState, iResult, pState->iInpLen)); + pState->eState = ST_FAIL; + pState->iSslFail = ProtoSSLStat(pState->pSsl, 'fail', NULL, 0); + pState->iHresult = ProtoSSLStat(pState->pSsl, 'hres', NULL, 0); + } + } + + // check for header completion, process it + if ((pState->eState == ST_HEAD) && (pState->iInpLen > 4)) + { + _ProtoHttpHeaderProcess(pState); + } + + // parse incoming body data + while ((pState->eState == ST_BODY) && (_ProtoHttpRecvBody(pState) != 0)) + ; + + // write callback processing + if (pState->pWriteCb != NULL) + { + _ProtoHttpWriteCbProcess(pState); + } + + // force disconnect in failure state + if (pState->eState == ST_FAIL) + { + _ProtoHttpClose(pState, "error"); + } + + // handle completion + if (pState->eState == ST_DONE) + { + if (pState->bPipelining && (pState->iPipedRequests > 0)) + { + // are we ready to proceed? + if ((pState->iBodyRcvd == pState->iBodySize) && pState->bPipeGetNext) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp: [%p] handling piped request\n", pState)); + _ProtoHttpCompactBuffer(pState); + pState->eState = ST_HEAD; + pState->iHeadSize = pState->iBodySize = pState->iBodyRcvd = 0; + pState->iPipedRequests -= 1; + pState->bPipeGetNext = FALSE; + } + } + else if (pState->bCloseHdr) + { + _ProtoHttpClose(pState, "server request"); + } + + // check for keep-alive connection close by server + if (pState->bConnOpen && (ProtoSSLStat(pState->pSsl, 'stat', NULL, 0) <= 0)) + { + _ProtoHttpClose(pState, "server close"); + } + } + + // release access to httpcrit + NetCritLeave(&pState->HttpCrit); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpSetCACert + + \Description + Add one or more X.509 CA certificates to the trusted list. A + certificate added will be available to all HTTP instances for + the lifetime of the application. This function can add one or more + PEM certificates or a single DER certificate. + + \Input *pCACert - pointer to certificate data + \Input iCertSize- certificate size in bytes + + \Output + int32_t - negative=failure, zero=success + + \Version 01/13/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpSetCACert(const uint8_t *pCACert, int32_t iCertSize) +{ + return(ProtoSSLSetCACert(pCACert, iCertSize)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpSetCACert2 + + \Description + Add one or more X.509 CA certificates to the trusted list. A + certificate added will be available to all HTTP instances for + the lifetime of the application. This function can add one or more + PEM certificates or a single DER certificate. + + This version of the function does not validate the CA at load time. + The X509 certificate data will be copied and retained until the CA + is validated, either by use of ProtoHttpValidateAllCA() or by the CA + being used to validate a certificate. + + \Input *pCACert - pointer to certificate data + \Input iCertSize- certificate size in bytes + + \Output + int32_t - negative=failure, zero=success + + \Version 04/21/2011 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpSetCACert2(const uint8_t *pCACert, int32_t iCertSize) +{ + return(ProtoSSLSetCACert2(pCACert, iCertSize)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpValidateAllCA + + \Description + Validate all CA that have been added but not yet been validated. Validation + is a one-time process and disposes of the X509 certificate that is retained + until validation. + + \Output + int32_t - negative=failure, zero=success + + \Version 04/21/2011 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpValidateAllCA(void) +{ + return(ProtoSSLValidateAllCA()); +} + +/*F*************************************************************************/ +/*! + \Function ProtoHttpClrCACerts + + \Description + Clears all dynamic CA certs from the list. + + \Version 01/14/2009 (jbrookes) +*/ +/**************************************************************************F*/ +void ProtoHttpClrCACerts(void) +{ + ProtoSSLClrCACerts(); +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protohttp2.c b/r5dev/thirdparty/dirtysdk/source/proto/protohttp2.c new file mode 100644 index 00000000..915416b9 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protohttp2.c @@ -0,0 +1,3758 @@ +/*H********************************************************************************/ +/*! + \File protohttp2.c + + \Description + This module implements an HTTP/2 client that can perform basic transactions + with an HTTP/2 server. It conforms to but does not fully implement + the 2.0 HTTP spec (https://tools.ietf.org/html/rfc7540), and + allows for secure HTTP transactions as well as insecure transactions. + + \Copyright + Copyright (c) Electronic Arts 2016-2018. ALL RIGHTS RESERVED. + + \Todo + Implement HTTP/2 proxy support (via CONNECT) when we find a proxy that supports + it. + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/dirtydefs.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/util/hpack.h" +#include "DirtySDK/proto/protohttp2.h" + +/*** Defines **********************************************************************/ + +//! size of a http2 header (24-bit length, 8-bit flag, 8-bit type and 31-bit stream id) +#define PROTOHTTP2_HEADER_SIZE (9) + +//! size of http2 setting (16-bit key and 32-bit value) +#define PROTOHTTP2_SETTING_SIZE (6) + +//! size of http2 ping (8 octets of opaque data) +#define PROTOHTTP2_PING_SIZE (8) + +//! size of http2 goaway (31-bit stream id and 32-bit error code) +#define PROTOHTTP2_GOAWAY_SIZE (8) + +//! size of http2 rst_stream (32-bit error code) +#define PROTOHTTP2_RST_STREAM_SIZE (4) + +//! size of http2 window update (31-bit window size increment) +#define PROTOHTTP2_WINDOW_UPDATE_SIZE (4) + +//! size of the http2 priority frame (1 bit exclusive, 31-bit stream dependency, 8-bit weight) +#define PROTOHTTP2_PRIORITY_SIZE (5) + +//! reserved size in the buffer for a header + important frames (ping/settings/rst_stream) +#define PROTOHTTP2_RESERVED_SIZE \ + ((PROTOHTTP2_HEADER_SIZE*4)+(PROTOHTTP2_SETTING_SIZE*SETTINGS_NUM)+PROTOHTTP2_PING_SIZE+PROTOHTTP2_RST_STREAM_SIZE) + +// flags for http frames +#define PROTOHTTP2_FLAG_ACK (1 << 0) //!< acknowledge ping / settings +#define PROTOHTTP2_FLAG_END_STREAM (1 << 0) //!< indicates end data chunk +#define PROTOHTTP2_FLAG_END_HEADERS (1 << 2) //!< indicates end headers, ie: no continuation frames +#define PROTOHTTP2_FLAG_PADDED (1 << 3) //!< indicates data / header is padded so look for pad length +#define PROTOHTTP2_FLAG_PRIORITY (1 << 5) //!< indicates priority information + +//! size of sent/received header cache +#define PROTOHTTP2_HDRCACHESIZE (1024*2) + +//! size of the receive window +#define PROTOHTTP2_WINDOWSIZE (1024*16) + +//! module version (maj.min) +#define PROTOHTTP2_VERSION (0x0200) + +//! maximum number of concurrent streams supported +#define PROTOHTTP2_MAX_STREAMS (100) + +//! default protocol timeout +#define PROTOHTTP2_TIMEOUT_DEFAULT (30*1000) + +//! minimum frame size +#define PROTOHTTP2_FRAMESIZE_MIN (1 << 14) + +//! maximum frame size +#define PROTOHTTP2_FRAMESIZE_MAX ((1 << 24) - 1) + +//! maximum flow-control window size +#define PROTOHTTP2_WINDOWSIZE_MAX ((1u << 31) - 1) + +/*** Macros ***********************************************************************/ + +//! calculates how much free space we have for encoding data/header frames (ie: user frames) +#define PROTOHTTP2_CalculateFreeSpace(pState) ((pState)->iOutMax-(pState)->iOutLen-PROTOHTTP2_RESERVED_SIZE) + +//! clamps the maximum frame size (16k-16m) +#define PROTOHTTP2_ClampFrameSize(iValue) (DS_CLAMP((iValue), PROTOHTTP2_FRAMESIZE_MIN, PROTOHTTP2_FRAMESIZE_MAX)) + +#if DIRTYCODE_LOGGING + // helper macro to print the frame type + #define PROTOHTTP2_GetFrameTypeStr(uType) ((uType) <= FRAMETYPE_LAST ? _ProtoHttp2_strFrameType[(uType)] : "UNKNOWN") +#endif + +/*** Type Definitions *************************************************************/ + +//! settings supported on the http2 connection +enum +{ + SETTINGS_HEADER_TABLE_SIZE = 1, //!< the maximum size of the header compression table used to decode header blocks + SETTINGS_ENABLE_PUSH, //!< used to disable server push + SETTINGS_MAX_CONCURRENT_STREAM, //!< the maximum number of concurrent streams that the sender will allow + SETTINGS_INITIAL_WINDOW_SIZE, //!< the sender's initial window size (in octets) for stream-level flow control + SETTINGS_MAX_FRAME_SIZE, //!< the size of the largest frame payload that the sender is willing to receive + SETTINGS_MAX_HEADER_LIST_SIZE, //!< the maximum size of header list that the sender is prepared to accept + + SETTINGS_NUM = SETTINGS_MAX_HEADER_LIST_SIZE +}; + +//! the different types of http2 frames we expect +typedef enum FrameTypeE +{ + FRAMETYPE_DATA, //!< convey arbitrary, variable-length sequences of octets associated with a stream + FRAMETYPE_HEADERS, //!< is used to open a stream, and additionally carries a header block fragment + FRAMETYPE_PRIORITY, //!< specifies the sender-advised priority of a stream + FRAMETYPE_RST_STREAM, //!< allows for immediate termination of a stream + FRAMETYPE_SETTINGS, //!< conveys configuration parameters that affect how endpoints communicate + FRAMETYPE_PUSH_PROMISE, //!< used to notify the peer endpoint in advance of streams the sender intends to initiate + FRAMETYPE_PING, //!< for measuring a minimal RTT from the sender, as well as determining whether an idle connection is still functional + FRAMETYPE_GOAWAY, //!< is used to initiate shutdown of a connection or to signal serious error conditions + FRAMETYPE_WINDOW_UPDATE,//!< used to implement flow control + FRAMETYPE_CONTINUATION, //!< used to continue a sequence of header block fragments + FRAMETYPE_LAST = FRAMETYPE_CONTINUATION +} FrameTypeE; + +//! the different types of errors we send in RST_STREAM/GOAWAY frames +typedef enum ErrorTypeE +{ + ERRORTYPE_NO_ERROR, //!< graceful shutdown + ERRORTYPE_PROTOCOL_ERROR, //!< protocol error detected + ERRORTYPE_INTERNAL_ERROR, //!< implementation fault + ERRORTYPE_FLOW_CONTROL_ERROR, //!< flow-control limits exceeded + ERRORTYPE_SETTINGS_TIMEOUT, //!< settings not acknowledged + ERRORTYPE_STREAM_CLOSED, //!< frame received for closed stream + ERRORTYPE_FRAME_SIZE_ERROR, //!< frame size incorrect + ERRORTYPE_REFUSED_STREAM, //!< stream not processed + ERRORTYPE_CANCEL, //!< stream cancelled + ERRORTYPE_COMPRESSION_ERROR, //!< compression state not updated + ERRORTYPE_CONNECT_ERROR, //!< TCP connection error for CONNECT method + ERRORTYPE_ENHANCE_YOUR_CALM, //!< processing capacity exceeded + ERRORTYPE_INADEQUATE_SECURITY, //!< negotiated TLS parameters not acceptable + ERRORTYPE_HTTP_1_1_REQUIRED //!< use HTTP/1.1 for the request +} ErrorTypeE; + +//! header embedded into every http2 frame +typedef struct FrameHeaderT +{ + uint32_t uLength; //!< length of the payload (24 bits) + uint8_t uType; //!< the frame type corresponds to the FrameTypeE enum (8 bits) + uint8_t uFlags; //!< the frame flags, this value depends on the frame type (8 bits) + uint8_t uPadding; //!< amount of padding (8 bits) + uint8_t bSkipFrame;//!< used for receive processing, not sent on the wire + int32_t iStreamId; //!< identifier of the stream (31 bits) +} FrameHeaderT; + +//! settings information +typedef struct SettingsT +{ + uint32_t uHeaderTableSize; //!< SETTINGS_HEADER_TABLE_SIZE + uint32_t uEnablePush; //!< SETTINGS_ENABLE_PUSH + uint32_t uMaxConcurrentStream; //!< SETTINGS_MAX_CONCURRENT_STREAM + uint32_t uInitialWindowSize; //!< SETTINGS_INITIAL_WINDOW_SIZE + uint32_t uMaxFrameSize; //!< SETTINGS_MAX_FRAME_SIZE + uint32_t uMaxHeaderListSize; //!< SETTINGS_MAX_HEADER_LIST_SIZE +} SettingsT; + +//! stream information +typedef struct StreamInfoT +{ + int32_t iStreamId; //!< stream identifier + ProtoHttp2StreamStateE eState; //!< current state of the stream + + ProtoHttpRequestTypeE eRequestType; //!< request type of current request + ProtoHttpResponseE eResponseCode; //!< HTTP error code from the response + ErrorTypeE eErrorType; //!< cached error code from a rst_stream + + char *pHeader; //!< storage for response header (uncompressed) + int32_t iHeaderLen; //!< length of the response header + + char strRequestHeader[PROTOHTTP2_HDRCACHESIZE]; //!< storage for cached request header (uncompressed) + + uint8_t *pData; //!< storage for data frames used for polling + int32_t iDataLen; //!< length of the data frame storage + int32_t iDataMax; //!< maximum size of the data frame storage + + int32_t iLocalWindow; //!< size of the stream wide receive window (local) + int32_t iPeerWindow; //!< size of the stream wide receive window (remote) + + ProtoHttp2WriteCbT *pWriteCb; //!< user write callback pointer + ProtoHttp2CustomHeaderCbT *pCustomHeaderCb; //!< custom header callback pointer + ProtoHttp2ReceiveHeaderCbT *pReceiveHeaderCb; //!< receive header callback pointer + void *pUserData; //!< user data to pass along with callback + + int64_t iBodySize; //!< size of body data + int64_t iBodyReceived; //!< size of body data received by caller + int32_t iRecvSize; //!< amount of data received by ProtoHttp2RecvAll + int32_t iHdrDate; //!< last modified date +} StreamInfoT; + +//! http2 module state +struct ProtoHttp2RefT +{ + ProtoSSLRefT *pSsl; //!< ssl module used for communication + + //! memgroup data + int32_t iMemGroup; + void *pMemGroupUserData; + + uint8_t *pOutBuf; //!< send buffer + int32_t iOutMax; //!< size of send buffer + int32_t iOutLen; //!< length in send buffer + int32_t iOutOff; //!< offset into the send buffer + uint8_t *pInpBuf; //!< recv buffer + int32_t iInpMax; //!< size of recv buffer + int32_t iInpLen; //!< length in recv buffer + + int32_t iLocalWindow; //!< size of the connection wide receive window (local) + int32_t iPeerWindow; //!< size of the connection wide receive window (remote) + + //! callback data + ProtoHttp2CustomHeaderCbT *pCustomHeaderCb; + ProtoHttp2ReceiveHeaderCbT *pReceiveHeaderCb; + void *pCallbackRef; + + int32_t iStreamId; //!< identifier of the current stream id for new requests + int32_t iVerbose; //!< logging verbosity + int32_t iSslFail; //!< ssl failure code, if any + int32_t iHresult; //!< ssl hresult code, if any + uint32_t uSettingsTimer; //!< timeout for the peer to acknowledge our settings + uint32_t uPingTimer; //!< timeout for the peer to acknowledge our ping + uint32_t uTimer; //!< timeout timer + uint32_t uTimeout; //!< protocol timeout + NetCritT HttpCrit; //!< critical section for guarding update from send/recv + + //! settings based on ProtoHttp2SettingsE + SettingsT PeerSettings; //!< settings advertised by our peer + SettingsT TempSettings; //!< settings we will advertise to our peer + SettingsT LocalSettings; //!< settings we have advertised and were ack'd + + char strHost[256]; //!< server name + char strBaseHost[256]; //!< base server name (used for partial urls) + int32_t iPort; //!< server port + int32_t bSecure; //!< secure connection? + int32_t iBasePort; //!< base port (used for partial urls) + int32_t bBaseSecure; //!< base security settings (used for partial urls) + + enum + { + ST_IDLE, //!< default state + ST_CONN, //!< connecting to the server + ST_ACTIVE, //!< active connection + ST_FAIL //!< connection failure + } eState; //!< current state of the module + ErrorTypeE eErrorType; //!< cached error when handling peer frames (goaway related) + + HpackRefT *pEncoder; //!< encoder context + HpackRefT *pDecoder; //!< decoder context + uint8_t bHuffman; //!< use huffman encoding? (default=FALSE) + uint8_t bSettingsRecv; //!< have we received any settings from our peer? + uint8_t bTimeout; //!< timeout indicator + uint8_t _pad; + + char *pAppendHdr; //!< append header ('apnd' control) buffer + int32_t iAppendLen; //!< size of append header + + int32_t iNumStreams; //!< current number of active streams + StreamInfoT Streams[PROTOHTTP2_MAX_STREAMS]; //!< list of stream info +}; + +/*** Variables ********************************************************************/ + +//! used when establishing a connection to try to cause failure with non http2 endpoints +static const uint8_t _ProtoHttp2_ConnectionPreface[24] = +{ + 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, + 0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, + 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a +}; + +#if DIRTYCODE_LOGGING +//! frame types for logging purposes +static const char *_ProtoHttp2_strFrameType[] = +{ + "DATA", + "HEADERS", + "PRIORITY", + "RST_STREAM", + "SETTINGS", + "PUSH_PROMISE", + "PING", + "GOAWAY", + "WINDOW_UPDATE", + "CONTINUATION" +}; + +//! settings names for logging purposes +static const char *_ProtoHttp2_strSettingName[SETTINGS_NUM] = +{ + "SETTINGS_HEADER_TABLE_SIZE", + "SETTINGS_ENABLE_PUSH", + "SETTINGS_MAX_CONCURRENT_STREAM", + "SETTINGS_INITIAL_WINDOW_SIZE", + "SETTINGS_MAX_FRAME_SIZE", + "SETTINGS_MAX_HEADER_LIST_SIZE" +}; + +//! protohttp2 state for logging +static const char *_ProtoHttp2_strState[] = +{ + "ST_IDLE", + "ST_CONN", + "ST_ACTIVE", + "ST_FAIL" +}; +#endif + +//! error types to string mapping +static const char *_ProtoHttp2_strErrorType[] = +{ + "NO_ERROR", + "PROTOCOL_ERROR", + "INTERNAL_ERROR", + "FLOW_CONTROL_ERROR", + "SETTINGS_TIMEOUT", + "STREAM_CLOSED", + "FRAME_SIZE_ERROR", + "REFUSED_STREAM", + "CANCEL", + "COMPRESSION_ERROR", + "CONNECT_ERROR", + "ENHANCE_YOUR_CALM", + "INADEQUATE_SECURITY", + "HTTP_1_1_REQUIRED" +}; + +//! default settings based on rfc +static const SettingsT _ProtoHttp2_DefaultSettings = +{ + 0x1000, //!< SETTINGS_HEADER_TABLE_SIZE + 0x1, //!< SETTINGS_ENABLE_PUSH + 0x7fffffff, //!< SETTINGS_MAX_CONCURRENT_STREAM + 0xffff, //!< SETTINGS_INITIAL_WINDOW_SIZE + PROTOHTTP2_FRAMESIZE_MIN, //!< SETTINGS_MAX_FRAME_SIZE + 0x7fffffff //!< SETTINGS_MAX_HEADER_LIST_SIZE +}; + +//! http request names +static const char *_ProtoHttp2_strRequestNames[PROTOHTTP_NUMREQUESTTYPES] = +{ + "HEAD", "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "CONNECT" +}; + +/*** Private Functions ************************************************************/ + +static void _ProtoHttp2StreamInfoCleanup(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo); +static void _ProtoHttp2PrepareRstStream(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, ErrorTypeE eErrorType, const char *pMessage); + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2Close + + \Description + Closes the connection to the peer + + \Input *pState - module state + \Input *pReason - string representation of why we are closing the connection + + \Version 09/28/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2Close(ProtoHttp2RefT *pState, const char *pReason) +{ + if (ProtoSSLStat(pState->pSsl, 'stat', NULL, 0) >= 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] closing connection: %s\n", pState, pReason)); + ProtoSSLDisconnect(pState->pSsl); + pState->eState = ST_FAIL; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2StreamCloseOnError + + \Description + Sets the stream state to closed and fires a failure write callback + + \Input *pState - module state + \Input *pStreamInfo - stream information we are updating + + \Version 01/05/2017 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2StreamCloseOnError(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo) +{ + // if the stream already closed nothing left to do + if (pStreamInfo->eState == STREAMSTATE_CLOSED) + { + return; + } + pStreamInfo->eState = STREAMSTATE_CLOSED; + + // if we have a write callback, let the user know that we had an error occur + if (pStreamInfo->pWriteCb != NULL) + { + ProtoHttp2WriteCbInfoT CbInfo; + CbInfo.iStreamId = pStreamInfo->iStreamId; + CbInfo.eRequestType = pStreamInfo->eRequestType; + CbInfo.eRequestResponse = pStreamInfo->eResponseCode; + + pStreamInfo->pWriteCb(pState, &CbInfo, NULL, pState->bTimeout ? PROTOHTTP2_TIMEOUT : PROTOHTTP2_RECVFAIL, pStreamInfo->pUserData); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2Send + + \Description + Try and send some data + + \Input *pState - reference pointer + \Input *pBuf - pointer to buffer to send from + \Input iBufSize - amount of data to try and send + + \Output + int32_t - negative=error, else number of bytes sent + + \Version 11/02/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2Send(ProtoHttp2RefT *pState, const uint8_t *pBuf, int32_t iBufSize) +{ + int32_t iResult, iStream; + + // try and send some data + iResult = ProtoSSLSend(pState->pSsl, (const char *)pBuf, iBufSize); + if (iResult > 0) + { + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] sent %d bytes\n", pState, iResult)); + if (pState->iVerbose > 3) + { + NetPrintMem(pBuf, iResult, "http2-send"); + } + #endif + pState->uTimer = NetTick() + pState->uTimeout; + } + else if (iResult < 0) + { + NetPrintf(("protohttp2: [%p] error %d sending %d bytes\n", pState, iResult, iBufSize)); + pState->eState = ST_FAIL; + pState->iSslFail = ProtoSSLStat(pState->pSsl, 'fail', NULL, 0); + pState->iHresult = ProtoSSLStat(pState->pSsl, 'hres', NULL, 0); + + // close all the streams that are currently active + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + _ProtoHttp2StreamCloseOnError(pState, &pState->Streams[iStream]); + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2Recv + + \Description + Try and recv some data + + \Input *pState - reference pointer + \Input *pBuf - pointer to buffer to recv to + \Input iBufSize - amount of data we can accept + + \Output + int32_t - negative=error, else number of bytes recv + + \Version 11/02/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2Recv(ProtoHttp2RefT *pState, uint8_t *pBuf, int32_t iBufSize) +{ + int32_t iResult, iStream; + + // try and send some data + iResult = ProtoSSLRecv(pState->pSsl, (char *)pBuf, iBufSize); + if (iResult > 0) + { + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] recv %d bytes\n", pState, iResult)); + if (pState->iVerbose > 3) + { + NetPrintMem(pBuf, iResult, "http2-recv"); + } + #endif + pState->uTimer = NetTick() + pState->uTimeout; + } + else if (iResult < 0) + { + NetPrintf(("protohttp2: [%p] %s got ST_FAIL (err=%d)\n", pState, _ProtoHttp2_strState[pState->eState], iResult)); + pState->eState = ST_FAIL; + pState->iSslFail = ProtoSSLStat(pState->pSsl, 'fail', NULL, 0); + pState->iHresult = ProtoSSLStat(pState->pSsl, 'hres', NULL, 0); + + // close all the streams that are currently active + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + _ProtoHttp2StreamCloseOnError(pState, &pState->Streams[iStream]); + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2ApplyBaseUrl + + \Description + Apply base url elements (if set) to any url elements not specified (relative + url support). + + \Input *pState - module state + \Input *pKind - parsed http kind ("http" or "https") + \Input *pHost - [in/out] parsed URL host + \Input iHostSize - size of pHost buffer + \Input *pPort - [in/out] parsed port + \Input *pSecure - [in/out] parsed security (0 or 1) + \Input bPortSpecified - TRUE if a port is explicitly specified in the url, else FALSE + + \Output + uint8_t - non-zero if changed, else zero + + \Version 02/03/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t _ProtoHttp2ApplyBaseUrl(ProtoHttp2RefT *pState, const char *pKind, char *pHost, int32_t iHostSize, int32_t *pPort, int32_t *pSecure, uint8_t bPortSpecified) +{ + uint8_t bChanged = FALSE; + if ((*pHost == '\0') && (pState->strBaseHost[0] != '\0')) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] host not present; setting to %s\n", pState, pState->strBaseHost)); + ds_strnzcpy(pHost, pState->strBaseHost, iHostSize); + bChanged = TRUE; + } + if ((bPortSpecified == FALSE) && (pState->iBasePort != 0)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] port not present; setting to %d\n", pState, pState->iBasePort)); + *pPort = pState->iBasePort; + bChanged = TRUE; + } + if (*pKind == '\0') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] kind (protocol) not present; setting to %d\n", pState, pState->bBaseSecure)); + *pSecure = pState->bBaseSecure; + // if our port setting is default and incompatible with our security setting, override it + if (((*pPort == 80) && (*pSecure == TRUE)) || ((*pPort == 443) && (*pSecure == FALSE))) + { + *pPort = *pSecure ? 443 : 80; + } + bChanged = TRUE; + } + return(bChanged); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2Reset + + \Description + Reset the internal state to before we connected to a peer + + \Input *pState - module state + + \Version 10/31/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2Reset(ProtoHttp2RefT *pState) +{ + int32_t iStream; + + // set peer's default setting values (these are overwritten on connection establishment) + ds_memcpy(&pState->PeerSettings, &_ProtoHttp2_DefaultSettings, sizeof(pState->PeerSettings)); + // set my local default setting values + ds_memcpy(&pState->LocalSettings, &_ProtoHttp2_DefaultSettings, sizeof(pState->LocalSettings)); + + // set my updated local settings that we will advertise during connection establishment + ds_memcpy(&pState->TempSettings, &_ProtoHttp2_DefaultSettings, sizeof(pState->TempSettings)); + pState->TempSettings.uEnablePush = 0; // tell the peer that it cannot push + + /* tell peer the size of header we are willing to support (uncompressed) + note: if the size is larger than the size of max header list you will + need to look into supporting continuation frames */ + pState->TempSettings.uMaxHeaderListSize = PROTOHTTP2_HDRCACHESIZE; + + // update the size of the recv window + pState->iLocalWindow = pState->TempSettings.uInitialWindowSize = PROTOHTTP2_WINDOWSIZE; + pState->iPeerWindow = pState->PeerSettings.uInitialWindowSize; + + // update the maximum frame size based on our input buffer + if (pState->TempSettings.uMaxFrameSize < (uint32_t)(pState->iInpMax-PROTOHTTP2_RESERVED_SIZE)) + { + pState->TempSettings.uMaxFrameSize = (uint32_t)(pState->iInpMax-PROTOHTTP2_RESERVED_SIZE); + } + + // clear the dynamic tables + HpackClear(pState->pDecoder); + HpackClear(pState->pEncoder); + + // set the stream id to 1 (initial state) + pState->iStreamId = 1; + + // clean up the number of tracked streams + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + _ProtoHttp2StreamInfoCleanup(pState, &pState->Streams[iStream]); + } + pState->iNumStreams = 0; + + // set the default state + pState->eState = ST_IDLE; + pState->eErrorType = ERRORTYPE_NO_ERROR; + pState->bTimeout = FALSE; + + // reset the sizes of the buffers + pState->iOutLen = pState->iInpLen = 0; + // reset the offset + pState->iOutOff = 0; + + // reset variables dealing with settings + pState->bSettingsRecv = FALSE; + pState->uSettingsTimer = 0; + + // reset ping timer + pState->uPingTimer = 0; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2StreamInfoGet + + \Description + Retrieves the stream information by identifier + + \Input *pState - module state + \Input iStreamId - identifier used for the lookup + + \Output + StreamInfoT * - pointer to stream information or NULL if not found + + \Version 10/26/2016 (eesponda) +*/ +/********************************************************************************F*/ +static StreamInfoT *_ProtoHttp2StreamInfoGet(ProtoHttp2RefT *pState, int32_t iStreamId) +{ + StreamInfoT *pStreamInfo = NULL; + int32_t iStream; + + // iterate through stream info and look for a match + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + if (pState->Streams[iStream].iStreamId == iStreamId) + { + pStreamInfo = &pState->Streams[iStream]; + break; + } + } + + // return info to caller, or NULL if no match + return(pStreamInfo); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2StreamInfoCleanup + + \Description + Cleanup any dynamic memory consumed by the stream info struct + + \Input *pState - module state + \Input *pStreamInfo - pointer to the structure needing cleanup + + \Version 10/26/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2StreamInfoCleanup(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo) +{ + if (pStreamInfo->pHeader != NULL) + { + DirtyMemFree(pStreamInfo->pHeader, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pStreamInfo->pHeader = NULL; + } + if (pStreamInfo->pData != NULL) + { + DirtyMemFree(pStreamInfo->pData, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pStreamInfo->pData = NULL; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2DecodeFrameHeader + + \Description + Decodes the header of a http2 frame + + \Input *pState - module state + \Input *pBuf - the buffer we are pasing the data out of + \Input iBufLen - the size of the buffer + \Input *pHeader - [out] location we are parsing into + \Input **pStreamInfo- [out] stream info for frames that include non-zero streams + + \Output + const uint8_t * - the new location of the buffer after parsing or NULL + + \Version 09/28/2016 (eesponda) +*/ +/********************************************************************************F*/ +static const uint8_t *_ProtoHttp2DecodeFrameHeader(ProtoHttp2RefT *pState, const uint8_t *pBuf, int32_t iBufLen, FrameHeaderT *pHeader, StreamInfoT **pStreamInfo) +{ + // make sure we can parse the whole header + if (iBufLen < PROTOHTTP2_HEADER_SIZE) + { + return(NULL); + } + // make sure that we in a valid state, in the case that a previous frame in the current receive caused an error + if (pState->eErrorType != ERRORTYPE_NO_ERROR) + { + return(NULL); + } + + // decode 24-bit length + pHeader->uLength = *pBuf++ << 16; + pHeader->uLength |= *pBuf++ << 8; + pHeader->uLength |= *pBuf++; + // decode 8-bit type + pHeader->uType = *pBuf++; + // decode 8-bit flags + pHeader->uFlags = *pBuf++; + // decode 31-bit stream id (ignoring the high bit) + pHeader->iStreamId = (*pBuf++ & 0x7f) << 24; + pHeader->iStreamId |= *pBuf++ << 16; + pHeader->iStreamId |= *pBuf++ << 8; + pHeader->iStreamId |= *pBuf++; + // set the skip frame to false by default + pHeader->bSkipFrame = FALSE; + + /* ref: https://tools.ietf.org/html/rfc7540#section-5.1.1 + An endpoint that receives an unexpected stream identifier MUST respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if ((pHeader->iStreamId > 0) && ((*pStreamInfo = _ProtoHttp2StreamInfoGet(pState, pHeader->iStreamId)) == NULL)) + { + /* ignore frames that are associated to streams that are no longer tracked. the rfc has more detailed rules, which can be tricky to implement. + just ignoring should cover our bases since this is an unexpected case. */ + if (pHeader->iStreamId < pState->iStreamId) + { + pHeader->bSkipFrame = TRUE; + } + else + { + NetPrintf(("protohttp2: [%p] received unrecogized stream identifier (0x%08x), closing connection\n", pState, pHeader->iStreamId)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + return(NULL); + } + } + + /* ref: https://tools.ietf.org/html/rfc7540#section-4.2 + An endpoint MUST send an error code of FRAME_SIZE_ERROR if a frame exceeds the size defined in SETTINGS_MAX_FRAME_SIZE. + A frame size error in a frame that could alter the state of the entire connection MUST be treated as a connection error (Section 5.4.1); this includes any frame + carrying a header block (Section 4.3) (that is, HEADERS, PUSH_PROMISE, and CONTINUATION), SETTINGS, and any frame with a stream identifier of 0. */ + if (pHeader->uLength > pState->LocalSettings.uMaxFrameSize) + { + if ((pHeader->iStreamId == 0) || (pHeader->uType == FRAMETYPE_HEADERS) || (pHeader->uType == FRAMETYPE_PUSH_PROMISE) || (pHeader->uType == FRAMETYPE_CONTINUATION)) + { + NetPrintf(("protohttp2: [%p] received frame with size exceeding maximum on connection impacting frame, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_FRAME_SIZE_ERROR; + return(NULL); + } + else + { + _ProtoHttp2PrepareRstStream(pState, *pStreamInfo, ERRORTYPE_FRAME_SIZE_ERROR, "received frame header with invalid size"); + pHeader->bSkipFrame = TRUE; + return(NULL); + } + } + + // make sure the whole frame has arrived, since anything past this we assume that we have received more than the PROTOHTTP2_HEADER_SIZE + if ((iBufLen-PROTOHTTP2_HEADER_SIZE) < (int32_t)pHeader->uLength) + { + return(NULL); + } + + /* ref: https://tools.ietf.org/html/rfc7540#section-4.1 + Flags that have no defined semantics for a particular frame type MUST be ignored and MUST be left unset (0x0) when sending. */ + if (((pHeader->uType == FRAMETYPE_HEADERS) || (pHeader->uType == FRAMETYPE_DATA) || (pHeader->uType == FRAMETYPE_PUSH_PROMISE)) && ((pHeader->uFlags & PROTOHTTP2_FLAG_PADDED) != 0)) + { + /* decode 8-bit pad length if present, add 1 for the length field itself; this is technically part of the frame payload but we do it here at a + central location */ + pHeader->uPadding = (*pBuf++) + 1; + + /* ref: https://tools.ietf.org/html/rfc7540#section-6.1 + The total number of padding octets is determined by the value of the Pad Length field. If the length of the padding is the length of the + frame payload or greater, the recipient MUST treat this as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if (pHeader->uPadding < pHeader->uLength) + { + pHeader->uLength -= pHeader->uPadding; + } + else + { + NetPrintf(("protohttp2: [%p] receive padding that is greater than the size of the frame, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + return(NULL); + } + } + else + { + pHeader->uPadding = 0; + } + + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] received frame (len=%u, type=%s(%u), flags=%u, stream=0x%08x, padding=%u, skip=%s)\n", + pState, pHeader->uLength, PROTOHTTP2_GetFrameTypeStr(pHeader->uType), pHeader->uType, pHeader->uFlags, pHeader->iStreamId, pHeader->uPadding, + pHeader->bSkipFrame ? "TRUE" : "FALSE")); + #endif + return(pBuf); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeFrameHeader + + \Description + Encodes the header of a http2 frame + + \Input *pState - module state + \Input *pBuf - the buffer we are writing the data to + \Input *pHeader - the header data we are writing + + \Version 09/29/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2EncodeFrameHeader(ProtoHttp2RefT *pState, uint8_t *pBuf, const FrameHeaderT *pHeader) +{ + // encode 24-bit length + *pBuf++ = (uint8_t)(pHeader->uLength >> 16); + *pBuf++ = (uint8_t)(pHeader->uLength >> 8); + *pBuf++ = (uint8_t)(pHeader->uLength); + // encode 8-bit type + *pBuf++ = pHeader->uType; + // encode 8-bit flags + *pBuf++ = pHeader->uFlags; + // encode 31-bit stream id + *pBuf++ = (uint8_t)(pHeader->iStreamId >> 24); + *pBuf++ = (uint8_t)(pHeader->iStreamId >> 16); + *pBuf++ = (uint8_t)(pHeader->iStreamId >> 8); + *pBuf++ = (uint8_t)(pHeader->iStreamId); + + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] encoding frame (len=%u, type=%s(%u), flags=%u, stream=0x%08x)\n", + pState, pHeader->uLength, _ProtoHttp2_strFrameType[pHeader->uType], pHeader->uType, pHeader->uFlags, pHeader->iStreamId)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2DecodeSettings + + \Description + Decodes the settings frame and saves the settings + + \Input *pState - module state + \Input *pBuf - the buffer we are parsing the data from + \Input *pHeader - the header data for the frame + + \Output + uint8_t - result of validation of the settings we received (TRUE=success, FALSE=failure) + + \Version 09/29/2016 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _ProtoHttp2DecodeSettings(ProtoHttp2RefT *pState, const uint8_t *pBuf, const FrameHeaderT *pHeader) +{ + // if we received an ACK, commit our temp settings + if ((pHeader->uFlags & PROTOHTTP2_FLAG_ACK) != 0) + { + ds_memcpy(&pState->LocalSettings, &pState->TempSettings, sizeof(pState->LocalSettings)); + } + else + { + int32_t iOffset; + for (iOffset = 0; iOffset < (int32_t)pHeader->uLength; iOffset += PROTOHTTP2_SETTING_SIZE) + { + uint16_t uKey; + uint32_t uValue; + + // decode settings key + uKey = *pBuf++ << 8; + uKey |= *pBuf++; + // decode settings value + uValue = *pBuf++ << 24; + uValue |= *pBuf++ << 16; + uValue |= *pBuf++ << 8; + uValue |= *pBuf++; + + switch (uKey) + { + case SETTINGS_ENABLE_PUSH: + { + /* ref: https://tools.ietf.org/html/rfc7540#section-6.5.2 + Any value other than 0 or 1 MUST be treated as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if ((uValue != 0) && (uValue != 1)) + { + NetPrintf(("protohttp2: [%p] received SETTINGS_ENABLE_PUSH with a value other than 0 or 1, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + return(FALSE); + } + pState->PeerSettings.uEnablePush = uValue; + break; + } + case SETTINGS_HEADER_TABLE_SIZE: pState->PeerSettings.uHeaderTableSize = uValue; break; + case SETTINGS_INITIAL_WINDOW_SIZE: + { + /* ref: https://tools.ietf.org/html/rfc7540#section-6.5.2 + Values above the maximum flow-control window size of 2^31-1 MUST be treated as a connection error (Section 5.4.1) of type + FLOW_CONTROL_ERROR. */ + if (uValue > PROTOHTTP2_WINDOWSIZE_MAX) + { + NetPrintf(("protohttp2: [%p] received SETTINGS_INITIAL_WINDOW_SIZE with a value above our maximum window, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_FLOW_CONTROL_ERROR; + return(FALSE); + } + pState->PeerSettings.uInitialWindowSize = uValue; + break; + } + case SETTINGS_MAX_CONCURRENT_STREAM: pState->PeerSettings.uMaxConcurrentStream = uValue; break; + case SETTINGS_MAX_FRAME_SIZE: + { + /* ref: https://tools.ietf.org/html/rfc7540#section-6.5.2 + The value advertised by an endpoint MUST be between this initial value and the maximum allowed frame size (2^24-1 or 16,777,215 octets), inclusive. + Values outside this range MUST be treated as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if ((uValue < PROTOHTTP2_FRAMESIZE_MIN) || (uValue > PROTOHTTP2_FRAMESIZE_MAX)) + { + NetPrintf(("protohttp2: [%p] received SETTINGS_MAX_FRAME_SIZE with a value outside our valid frame size range, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + return(FALSE); + } + pState->PeerSettings.uMaxFrameSize = uValue; + break; + } + case SETTINGS_MAX_HEADER_LIST_SIZE: pState->PeerSettings.uMaxHeaderListSize = uValue; break; + default: NetPrintf(("protohttp2: [%p] unhandled settings key %u\n", pState, uKey)); break; + } + #if DIRTYCODE_LOGGING + if ((uKey >= SETTINGS_HEADER_TABLE_SIZE) && (uKey <= SETTINGS_MAX_HEADER_LIST_SIZE)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] received setting (%s=%u)\n", + pState, _ProtoHttp2_strSettingName[uKey-1], uValue)); + } + #endif + } + } + + // success + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2WriteSetting + + \Description + Writes a setting into the payload + + \Input *pState - module state + \Input *pBuf - [out] the buffer we are writing the data to + \Input *pHeader - the frame header we are updating while writing + \Input uSettingKey - the key for the setting + \Input uSettingValue - the value for the setting + + \Output + int32_t - amount of data written + + \Version 10/24/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2WriteSetting(ProtoHttp2RefT *pState, uint8_t *pBuf, FrameHeaderT *pHeader, uint16_t uSettingKey, uint32_t uSettingValue) +{ + pHeader->uLength += PROTOHTTP2_SETTING_SIZE; + + *pBuf++ = (uint8_t)(uSettingKey >> 8); + *pBuf++ = (uint8_t)(uSettingKey); + *pBuf++ = (uint8_t)(uSettingValue >> 24); + *pBuf++ = (uint8_t)(uSettingValue >> 16); + *pBuf++ = (uint8_t)(uSettingValue >> 8); + *pBuf++ = (uint8_t)(uSettingValue); + + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] encode setting (%s=%u)\n", + pState, _ProtoHttp2_strSettingName[uSettingKey-1], uSettingValue)); + + return(PROTOHTTP2_SETTING_SIZE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeSettings + + \Description + Encodes the a settings frame + + \Input *pState - module state + \Input bAck - are we acknowledging or updating settings? + \Input *pBuf - [out] the buffer we are writing the data to + \Input iBufLen - the length of the buffer + + \Output + int32_t - amount of data written + + \Version 09/29/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2EncodeSettings(ProtoHttp2RefT *pState, uint8_t bAck, uint8_t *pBuf, int32_t iBufLen) +{ + FrameHeaderT Header; + uint8_t *pFrameHeader; + + // make sure we have enough room for the maximum amount of settings + if (iBufLen < (PROTOHTTP2_HEADER_SIZE + (PROTOHTTP2_SETTING_SIZE * SETTINGS_NUM))) + { + return(0); + } + + // setup defaults + ds_memclr(&Header, sizeof(Header)); + Header.uType = FRAMETYPE_SETTINGS; + + // save the header location and advance the buffer + pFrameHeader = pBuf; + pBuf += PROTOHTTP2_HEADER_SIZE; + + // if we need to acknowledge the peer's setting and our settings have not changed set the appropriate flags + if (bAck == TRUE) + { + Header.uFlags |= PROTOHTTP2_FLAG_ACK; + } + // otherwise, send our settings to the peer + else + { + if (pState->TempSettings.uHeaderTableSize != pState->LocalSettings.uHeaderTableSize) + { + pBuf += _ProtoHttp2WriteSetting(pState, pBuf, &Header, SETTINGS_HEADER_TABLE_SIZE, pState->TempSettings.uHeaderTableSize); + } + if (pState->TempSettings.uEnablePush != pState->LocalSettings.uEnablePush) + { + pBuf += _ProtoHttp2WriteSetting(pState, pBuf, &Header, SETTINGS_ENABLE_PUSH, pState->TempSettings.uEnablePush); + } + if (pState->TempSettings.uMaxConcurrentStream != pState->LocalSettings.uMaxConcurrentStream) + { + pBuf += _ProtoHttp2WriteSetting(pState, pBuf, &Header, SETTINGS_MAX_CONCURRENT_STREAM, pState->TempSettings.uMaxConcurrentStream); + } + if (pState->TempSettings.uInitialWindowSize != pState->LocalSettings.uInitialWindowSize) + { + pBuf += _ProtoHttp2WriteSetting(pState, pBuf, &Header, SETTINGS_INITIAL_WINDOW_SIZE, pState->TempSettings.uInitialWindowSize); + } + if (pState->TempSettings.uMaxFrameSize != pState->LocalSettings.uMaxFrameSize) + { + pBuf += _ProtoHttp2WriteSetting(pState, pBuf, &Header, SETTINGS_MAX_FRAME_SIZE, pState->TempSettings.uMaxFrameSize); + } + if (pState->TempSettings.uMaxHeaderListSize != pState->LocalSettings.uMaxHeaderListSize) + { + _ProtoHttp2WriteSetting(pState, pBuf, &Header, SETTINGS_MAX_HEADER_LIST_SIZE, pState->TempSettings.uMaxHeaderListSize); + } + + // ensure we actually wrote any settings + if (Header.uLength == 0) + { + return(0); + } + } + + // encode the frame header, by this point we know we have enough space + _ProtoHttp2EncodeFrameHeader(pState, pFrameHeader, &Header); + + return(Header.uLength+PROTOHTTP2_HEADER_SIZE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeHeaders + + \Description + Encodes the a headers frame + + \Input *pState - module state + \Input *pStreamInfo - information about the stream (id, etc) + \Input bEndStream - does this request contain data? + \Input *pHeader - the header we are using to encode + + \Output + int32_t - zero=success, negative=failure + + \Version 10/26/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2EncodeHeaders(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, uint8_t bEndStream, const char *pHeader) +{ + FrameHeaderT Header; + uint8_t *pFrameHeader, *pBuf; + int32_t iBufMax, iBufLen, iResult; + + pBuf = pState->pOutBuf+pState->iOutLen; + iBufLen = PROTOHTTP2_CalculateFreeSpace(pState); + iBufMax = iBufLen-PROTOHTTP2_HEADER_SIZE; + + // make sure we at least have enough space for frame header + if (iBufMax <= 0) + { + return(PROTOHTTP2_MINBUFF); + } + + // setup the frame header + ds_memclr(&Header, sizeof(Header)); + Header.uType = FRAMETYPE_HEADERS; + Header.iStreamId = pStreamInfo->iStreamId; + Header.uFlags |= PROTOHTTP2_FLAG_END_HEADERS; + if (bEndStream == TRUE) + { + Header.uFlags |= PROTOHTTP2_FLAG_END_STREAM; + } + + // save the header location and advance the buffer + pFrameHeader = pBuf; + pBuf += PROTOHTTP2_HEADER_SIZE; + + // encode the header info the buffer + iResult = HpackEncode(pState->pEncoder, pHeader, pBuf, iBufMax, pState->bHuffman); + if ((iResult > 0) && (iResult < iBufMax)) + { + Header.uLength = (uint32_t)iResult; + + // encode the frame header, by this point we know we have enough space + _ProtoHttp2EncodeFrameHeader(pState, pFrameHeader, &Header); + + // update the stream state + if (pStreamInfo->eState == STREAMSTATE_IDLE) + { + pStreamInfo->eState = (bEndStream == TRUE) ? STREAMSTATE_HALF_CLOSED_LOCAL : STREAMSTATE_OPEN; + } + + // advance the buffer + pState->iOutLen += Header.uLength+PROTOHTTP2_HEADER_SIZE; + return(0); + } + + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeData + + \Description + Encodes the a data frame + + \Input *pState - module state + \Input *pStreamInfo - information about the stream (id, etc) + \Input *pInput - the payload data we are sending + \Input iInpLen - the length of the payload data + \Input bEndStream - will we be sending any more data frames? + + \Output + int32_t - amount of data written + + \Version 10/26/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2EncodeData(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, const uint8_t *pInput, int32_t iInpLen, uint8_t bEndStream) +{ + FrameHeaderT Header; + uint8_t *pOutput = pState->pOutBuf+pState->iOutLen; + // if we are ending the stream we pull we include our reserved space + int32_t iOutLen = !bEndStream ? PROTOHTTP2_CalculateFreeSpace(pState) : pState->iOutMax-pState->iOutLen; + + // make sure we have enough space to encode the frame header plus some more + if (iOutLen <= PROTOHTTP2_HEADER_SIZE) + { + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] not enough room in the output buffer to encode data (free-space=%d)\n", + pState, iOutLen)); + return(PROTOHTTP2_MINBUFF); + } + iOutLen -= PROTOHTTP2_HEADER_SIZE; + + /* if we cannot fit the whole user payload, then fill the remainder of the buffer + don't set the end stream flag as more data needs to be sent */ + if (iInpLen > iOutLen) + { + iInpLen = iOutLen; + bEndStream = FALSE; + } + // make sure we have enough space in the window + if ((pState->iPeerWindow-iInpLen < 0) || (pStreamInfo->iPeerWindow-iInpLen < 0)) + { + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] not encoding data as the server receive window is out of space (stream=0x%08x, conn window=%d, stream window=%d)\n", + pState, pStreamInfo->iStreamId, pState->iPeerWindow, pStreamInfo->iPeerWindow)); + return(0); + } + + // setup the frame header + ds_memclr(&Header, sizeof(Header)); + Header.uType = FRAMETYPE_DATA; + Header.iStreamId = pStreamInfo->iStreamId; + Header.uLength = iInpLen; + + // end the stream if necessary + if (bEndStream == TRUE) + { + Header.uFlags |= PROTOHTTP2_FLAG_END_STREAM; + } + + // encode the frame header + _ProtoHttp2EncodeFrameHeader(pState, pOutput, &Header); + pOutput += PROTOHTTP2_HEADER_SIZE; + + // encode the data + ds_memcpy_s(pOutput, iOutLen, pInput, iInpLen); + + // update the stream state + if (bEndStream == TRUE) + { + if (pStreamInfo->eState == STREAMSTATE_OPEN) + { + pStreamInfo->eState = STREAMSTATE_HALF_CLOSED_LOCAL; + } + else if (pStreamInfo->eState == STREAMSTATE_HALF_CLOSED_REMOTE) + { + pStreamInfo->eState = STREAMSTATE_CLOSED; + } + } + + // advance the windows + pState->iPeerWindow -= iInpLen; + pStreamInfo->iPeerWindow -= iInpLen; + + // advance buffer + pState->iOutLen += iInpLen+PROTOHTTP2_HEADER_SIZE; + + // return amount of user payload data written to the output buffer + return(iInpLen); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeWindowUpdate + + \Description + Encodes the a window update frame + + \Input *pState - module state + \Input iStreamId - stream we are sending the window update for + \Input iIncrement - window increment we are sending for the stream + \Input *pWindow - [out] window we are incrementing + + \Output + int32_t - zero=success, negative=error + + \Notes + StreamId of 0 is reserved for updating the window of the entire + connection + + \Version 10/26/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2EncodeWindowUpdate(ProtoHttp2RefT *pState, int32_t iStreamId, int32_t iIncrement, int32_t *pWindow) +{ + FrameHeaderT Header; + const int32_t iPayloadSize = PROTOHTTP2_WINDOW_UPDATE_SIZE+PROTOHTTP2_HEADER_SIZE; + uint8_t *pBuf = pState->pOutBuf+pState->iOutLen; + int32_t iBufLen = pState->iOutMax-pState->iOutLen; + + // make sure we have enough space to encode + if (iBufLen < iPayloadSize) + { + return(PROTOHTTP2_MINBUFF); + } + + // setup the frame header + ds_memclr(&Header, sizeof(Header)); + Header.uType = FRAMETYPE_WINDOW_UPDATE; + Header.iStreamId = iStreamId; + Header.uLength = PROTOHTTP2_WINDOW_UPDATE_SIZE; + + // encode the frame header + _ProtoHttp2EncodeFrameHeader(pState, pBuf, &Header); + pBuf += PROTOHTTP2_HEADER_SIZE; + + // encode the data + *pBuf++ = (uint8_t)(iIncrement >> 24); + *pBuf++ = (uint8_t)(iIncrement >> 16); + *pBuf++ = (uint8_t)(iIncrement >> 8); + *pBuf++ = (uint8_t)(iIncrement); + + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] incrementing the window (stream=0x%08x, size=%d, increment=%d)\n", pState, iStreamId, *pWindow, iIncrement)); + *pWindow += iIncrement; + + // advance the buffer + pState->iOutLen += iPayloadSize; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodePing + + \Description + Encodes a ping frame + + \Input *pState - module state + \Input bAck - are we acknowledging a ping? + \Input *pInput - the opaque data we send + + \Output + int32_t - zero=success, negative=error + + \Version 10/26/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2EncodePing(ProtoHttp2RefT *pState, uint8_t bAck, const uint8_t *pInput) +{ + FrameHeaderT Header; + const int32_t iPayloadSize = PROTOHTTP2_PING_SIZE+PROTOHTTP2_HEADER_SIZE; + uint8_t *pOutput = pState->pOutBuf+pState->iOutLen; + int32_t iOutLen = pState->iOutMax-pState->iOutLen; + + // make sure we have enough space to encode + if (iOutLen < iPayloadSize) + { + return(PROTOHTTP2_MINBUFF); + } + + // setup the frame header + ds_memclr(&Header, sizeof(Header)); + Header.uType = FRAMETYPE_PING; + Header.uLength = PROTOHTTP2_PING_SIZE; + if (bAck == TRUE) + { + Header.uFlags |= PROTOHTTP2_FLAG_ACK; + } + + // encode the frame header + _ProtoHttp2EncodeFrameHeader(pState, pOutput, &Header); + pOutput += PROTOHTTP2_HEADER_SIZE; + + // encode the opaque data + ds_memcpy(pOutput, pInput, PROTOHTTP2_PING_SIZE); + // advance the buffer + pState->iOutLen += iPayloadSize; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeGoAway + + \Description + Encodes a goaway frame + + \Input *pState - module state + \Input uErrorCode - error code being sent with frame + + \Notes + This function cannot fail since we know that a goaway frame will always + fit within our output buffer. Since we are closing the connection right + after we can disregard any data already encoded there within. + + \Version 10/31/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2EncodeGoAway(ProtoHttp2RefT *pState, uint32_t uErrorCode) +{ + FrameHeaderT Header; + uint8_t *pBuf = pState->pOutBuf; + const int32_t iPayloadSize = PROTOHTTP2_GOAWAY_SIZE+PROTOHTTP2_HEADER_SIZE; + // the variable points to the next stream id so deduct 2 to get last assigned stream id + const int32_t iLastStreamId = pState->iStreamId - 2; + + // setup the frame header + ds_memclr(&Header, sizeof(Header)); + Header.uType = FRAMETYPE_GOAWAY; + Header.uLength = PROTOHTTP2_GOAWAY_SIZE; + + // encode the frame header + _ProtoHttp2EncodeFrameHeader(pState, pBuf, &Header); + pBuf += PROTOHTTP2_HEADER_SIZE; + + // encode last-stream-id + *pBuf++ = (uint8_t)(iLastStreamId >> 24); + *pBuf++ = (uint8_t)(iLastStreamId >> 16); + *pBuf++ = (uint8_t)(iLastStreamId >> 8); + *pBuf++ = (uint8_t)(iLastStreamId); + // encode error code + *pBuf++ = (uint8_t)(uErrorCode >> 24); + *pBuf++ = (uint8_t)(uErrorCode >> 16); + *pBuf++ = (uint8_t)(uErrorCode >> 8); + *pBuf++ = (uint8_t)(uErrorCode); + + pState->iOutLen = iPayloadSize; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeRstStream + + \Description + Encodes a rst_stream frame + + \Input *pState - module state + \Input iStreamId - the stream we are resetting + \Input uErrorCode - the error that caused the reset + + \Output + int32_t - zero=success, negative=failure + + \Version 11/03/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2EncodeRstStream(ProtoHttp2RefT *pState, int32_t iStreamId, uint32_t uErrorCode) +{ + FrameHeaderT Header; + const int32_t iPayloadSize = PROTOHTTP2_RST_STREAM_SIZE+PROTOHTTP2_HEADER_SIZE; + uint8_t *pBuf = pState->pOutBuf+pState->iOutLen; + int32_t iBufSize = pState->iOutMax-pState->iOutLen; + + // make sure we have enough space to encode + if (iBufSize < iPayloadSize) + { + return(PROTOHTTP2_MINBUFF); + } + + // setup the frame header + ds_memclr(&Header, sizeof(Header)); + Header.uType = FRAMETYPE_RST_STREAM; + Header.uLength = PROTOHTTP2_RST_STREAM_SIZE; + Header.iStreamId = iStreamId; + + // encode the frame header + _ProtoHttp2EncodeFrameHeader(pState, pBuf, &Header); + pBuf += PROTOHTTP2_HEADER_SIZE; + + // encode the error code + *pBuf++ = (uint8_t)(uErrorCode >> 24); + *pBuf++ = (uint8_t)(uErrorCode >> 16); + *pBuf++ = (uint8_t)(uErrorCode >> 8); + *pBuf++ = (uint8_t)(uErrorCode); + + // advance the buffer + pState->iOutLen += iPayloadSize; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2SendGoAway + + \Description + Sends goaway frame to peer to signal closing the connection + + \Input *pState - module state + \Input eErrorType - the error we are sending in the goaway + \Input *pReason - logging reason we are closing the connection + + \Version 10/31/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2SendGoAway(ProtoHttp2RefT *pState, ErrorTypeE eErrorType, const char *pReason) +{ + // only send this error if still connected + if (ProtoSSLStat(pState->pSsl, 'stat', NULL, 0) >= 0) + { + int32_t iStream; + + // encode and send go away + _ProtoHttp2EncodeGoAway(pState, eErrorType); + _ProtoHttp2Send(pState, pState->pOutBuf, pState->iOutLen); + + // close all the streams that are currently active + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + _ProtoHttp2StreamCloseOnError(pState, &pState->Streams[iStream]); + } + + // close the connection + _ProtoHttp2Close(pState, pReason); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2PrepareRstStream + + \Description + Prepares the rst_stream to be sent on the next update + + \Input *pState - module state + \Input *pStreamInfo - information about stream we are resetting + \Input eErrorType - reason for closing the connection + \Input *pMessage - logging message about why we are resetting + + \Version 11/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2PrepareRstStream(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, ErrorTypeE eErrorType, const char *pMessage) +{ + NetPrintf(("protohttp2: [%p] %s, sending %s to peer\n", pState, pMessage, _ProtoHttp2_strErrorType[eErrorType])); + if (_ProtoHttp2EncodeRstStream(pState, pStreamInfo->iStreamId, eErrorType) != 0) + { + NetPrintf(("protohttp2: [%p] not enough space in output buffer to encode rst_stream\n", pState)); + } + _ProtoHttp2StreamCloseOnError(pState, pStreamInfo); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2ParseHeader + + \Description + Decodes and parses the header frame information + + \Input *pState - module state + \Input *pStreamInfo - stream information + \Input *pBuf - the buffer we are parsing the data from + \Input iBufLen - length of the buffer + + \Output + int32_t - success=zero, failure=negative + + \Version 10/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2ParseHeader(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, const uint8_t *pBuf, int32_t iBufLen) +{ + char strValue[128], *pHeader; + int32_t iResult, iHeaderMax; + ProtoHttp2ReceiveHeaderCbT *pReceiveHeaderCb; + void *pUserData; + const uint8_t bParse = pStreamInfo->iHeaderLen == 0; /* if this is the first header, we need to parse */ + + // allocate memory for uncompressed headers + if ((pStreamInfo->pHeader == NULL) && ((pStreamInfo->pHeader = (char *)DirtyMemAlloc(pState->LocalSettings.uMaxHeaderListSize, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL)) + { + NetPrintf(("protohttp2: [%p] could not allocate space for header list\n", pState)); + return(-1); + } + // point to the memory we are updating + pHeader = pStreamInfo->pHeader+pStreamInfo->iHeaderLen; + iHeaderMax = pState->LocalSettings.uMaxHeaderListSize-pStreamInfo->iHeaderLen; + + // decode the header and write the information to our cache + if ((iResult = HpackDecode(pState->pDecoder, pBuf, iBufLen, pHeader, iHeaderMax)) >= 0) + { + pStreamInfo->iHeaderLen += iResult; + } + else + { + NetPrintf(("protohttp2: [%p] could not decode the incoming headers\n", pState)); + return(-1); + } + + /* if we already received an initial header, we don't need to reparse this data as + we are just handling trailing header fields after the last data chunk. + these fields _should_ not contain any of the headers we are parsing for */ + if (bParse == TRUE) + { + // parse header code + pStreamInfo->eResponseCode = ProtoHttpParseHeaderCode(pStreamInfo->pHeader); + + // parse content-length field + if (ProtoHttpGetHeaderValue(pState, pStreamInfo->pHeader, "content-length", strValue, sizeof(strValue), NULL) != -1) + { + pStreamInfo->iBodySize = ds_strtoll(strValue, NULL, 10); + } + else + { + pStreamInfo->iBodySize = -1; + } + + // parse last-modified header + if (ProtoHttpGetHeaderValue(pState, pStreamInfo->pHeader, "last-modified", strValue, sizeof(strValue), NULL) != -1) + { + pStreamInfo->iHdrDate = (int32_t)ds_strtotime(strValue); + } + + #if DIRTYCODE_LOGGING + // if this is a redirect, we don't support it log something + if (PROTOHTTP_GetResponseClass(pStreamInfo->eResponseCode) == PROTOHTTP_RESPONSE_REDIRECTION) + { + NetPrintf(("protohttp2: [%p] auto-redirection not supported, if you want to follow the redirect please issue a new request\n", pState)); + } + #endif + + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] received %d response (%d bytes)\n", pState, pStreamInfo->eResponseCode, pStreamInfo->iHeaderLen)); + } + + #if DIRTYCODE_LOGGING + if (pState->iVerbose > 1) + { + NetPrintf(("protohttp2: [%p] received response header:\n", pState)); + NetPrintWrap(pHeader, 80); + } + #endif + + // request level callback takes priority to global + if ((pReceiveHeaderCb = pStreamInfo->pReceiveHeaderCb) != NULL) + { + pUserData = pStreamInfo->pUserData; + } + else + { + pReceiveHeaderCb = pState->pReceiveHeaderCb; + pUserData = pState->pCallbackRef; + } + + // if we have a receive header callback, invoke it + if (pReceiveHeaderCb != NULL) + { + pReceiveHeaderCb(pState, pStreamInfo->iStreamId, pHeader, iResult, pUserData); + } + + // header successfully parsed + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2HandleData + + \Description + Handles the incoming data frame + + \Input *pState - module state + \Input *pBuf - the buffer we are parsing the data from + \Input *pHeader - the header data for the frame + \Input *pStreamInfo - information about associated stream + + \Version 11/04/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2HandleData(ProtoHttp2RefT *pState, const uint8_t *pBuf, const FrameHeaderT *pHeader, StreamInfoT *pStreamInfo) +{ + uint8_t bEndStream = FALSE; + + // check the state of the stream to make sure we are in correct state + if ((pStreamInfo->eState != STREAMSTATE_OPEN) && (pStreamInfo->eState != STREAMSTATE_HALF_CLOSED_LOCAL)) + { + _ProtoHttp2PrepareRstStream(pState, pStreamInfo, ERRORTYPE_STREAM_CLOSED, "received data frame when in unexpected state"); + return; + } + + // update the stream data based on the flags + if ((pHeader->uFlags & PROTOHTTP2_FLAG_END_STREAM) != 0) + { + if (pStreamInfo->eState == STREAMSTATE_OPEN) + { + pStreamInfo->eState = STREAMSTATE_HALF_CLOSED_REMOTE; + } + else if (pStreamInfo->eState == STREAMSTATE_HALF_CLOSED_LOCAL) + { + pStreamInfo->eState = STREAMSTATE_CLOSED; + } + bEndStream = TRUE; + } + + // if we have a write callback, invoke it + if (pStreamInfo->pWriteCb != NULL) + { + ProtoHttp2WriteCbInfoT CbInfo; + CbInfo.iStreamId = pStreamInfo->iStreamId; + CbInfo.eRequestType = pStreamInfo->eRequestType; + CbInfo.eRequestResponse = pStreamInfo->eResponseCode; + + pStreamInfo->pWriteCb(pState, &CbInfo, pBuf, pHeader->uLength, pStreamInfo->pUserData); + pState->iLocalWindow -= pHeader->uLength; + pStreamInfo->iLocalWindow -= pHeader->uLength; + + // update amount of data received + pStreamInfo->iBodyReceived += pHeader->uLength; + + if (bEndStream == TRUE) + { + // update body size now that stream is complete + pStreamInfo->iBodySize = pStreamInfo->iBodyReceived; + pStreamInfo->pWriteCb(pState, &CbInfo, NULL, PROTOHTTP2_RECVDONE, pStreamInfo->pUserData); + } + } + else + { + // allocate space if necessary (size of the window) + if ((pStreamInfo->pData == NULL) && ((pStreamInfo->pData = (uint8_t *)DirtyMemAlloc(pStreamInfo->iDataMax, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL)) + { + _ProtoHttp2PrepareRstStream(pState, pStreamInfo, ERRORTYPE_INTERNAL_ERROR, "failed to allocate space for storing data"); + return; + } + // make sure the peer did not exceed the window + if (pStreamInfo->iDataLen+(int32_t)pHeader->uLength <= pStreamInfo->iDataMax) + { + ds_memcpy(pStreamInfo->pData+pStreamInfo->iDataLen, pBuf, pHeader->uLength); + pStreamInfo->iDataLen += pHeader->uLength; + pState->iLocalWindow -= pHeader->uLength; + pStreamInfo->iLocalWindow -= pHeader->uLength; + } + else + { + _ProtoHttp2PrepareRstStream(pState, pStreamInfo, ERRORTYPE_FLOW_CONTROL_ERROR, "peer exceeded the receive window"); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2HandleHeaders + + \Description + Handles the incoming headers frame + + \Input *pState - module state + \Input *pBuf - the buffer we are parsing the data from + \Input *pHeader - the header data for the frame + \Input *pStreamInfo - information about associated stream + + \Version 11/04/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2HandleHeaders(ProtoHttp2RefT *pState, const uint8_t *pBuf, const FrameHeaderT *pHeader, StreamInfoT *pStreamInfo) +{ + uint8_t bEndStream = FALSE; + int32_t iBufRead = 0; + + if ((pStreamInfo->eState != STREAMSTATE_OPEN) && (pStreamInfo->eState != STREAMSTATE_HALF_CLOSED_LOCAL)) + { + _ProtoHttp2PrepareRstStream(pState, pStreamInfo, ERRORTYPE_STREAM_CLOSED, "received header frames when stream is in unexpected state"); + return; + } + + // update the stream data based on the flags + if ((pHeader->uFlags & PROTOHTTP2_FLAG_END_STREAM) != 0) + { + if (pStreamInfo->eState == STREAMSTATE_OPEN) + { + pStreamInfo->eState = STREAMSTATE_HALF_CLOSED_REMOTE; + } + else if (pStreamInfo->eState == STREAMSTATE_HALF_CLOSED_LOCAL) + { + pStreamInfo->eState = STREAMSTATE_CLOSED; + } + bEndStream = TRUE; + } + + // skip the priority frame data if present + if ((pHeader->uFlags & PROTOHTTP2_FLAG_PRIORITY) != 0) + { + iBufRead += PROTOHTTP2_PRIORITY_SIZE; + } + + // if this is the end of the stream we can parse our headers + if ((pHeader->uFlags & PROTOHTTP2_FLAG_END_HEADERS) != 0) + { + if (_ProtoHttp2ParseHeader(pState, pStreamInfo, pBuf+iBufRead, pHeader->uLength-iBufRead) != 0) + { + _ProtoHttp2PrepareRstStream(pState, pStreamInfo, ERRORTYPE_COMPRESSION_ERROR, "failed to decompress header"); + } + } + else + { + NetPrintf(("protohttp2: [%p] expected end of the header as our maximum header list fits within a single frame, closing connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + } + + // if we have a write callback, invoke it + if ((pStreamInfo->pWriteCb != NULL) && (bEndStream == TRUE)) + { + ProtoHttp2WriteCbInfoT CbInfo; + CbInfo.iStreamId = pStreamInfo->iStreamId; + CbInfo.eRequestType = pStreamInfo->eRequestType; + CbInfo.eRequestResponse = pStreamInfo->eResponseCode; + + // update body size now that stream is complete + pStreamInfo->iBodySize = pStreamInfo->iBodyReceived; + pStreamInfo->pWriteCb(pState, &CbInfo, NULL, PROTOHTTP2_RECVDONE, pStreamInfo->pUserData); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2HandleFrame + + \Description + Handles the incoming http2 frame + + \Input *pState - module state + \Input *pBuf - the buffer we are parsing the data from + \Input *pHeader - the header data for the frame + \Input *pStreamInfo - stream information + + \Version 09/29/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2HandleFrame(ProtoHttp2RefT *pState, const uint8_t *pBuf, const FrameHeaderT *pHeader, StreamInfoT *pStreamInfo) +{ + int32_t iOffset; + + // don't do any processing if we were told to skip + if (pHeader->bSkipFrame == TRUE) + { + return; + } + + if (pHeader->uType == FRAMETYPE_SETTINGS) + { + const int32_t iPrevWindow = pState->PeerSettings.uInitialWindowSize; /* save previous window setting for calculation */ + + /* ref: https://tools.ietf.org/html/rfc7540#section-6.5 + SETTINGS frames always apply to a connection, never a single stream. The stream identifier for a SETTINGS frame MUST be zero (0x0). + If an endpoint receives a SETTINGS frame whose stream identifier field is anything other than 0x0, the endpoint MUST respond with + a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if (pHeader->iStreamId != 0) + { + NetPrintf(("protohttp2: [%p] received settings frame with stream id not 0, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + return; + } + /* ref: https://tools.ietf.org/html/rfc7540#section-6.5 + A SETTINGS frame with a length other than a multiple of 6 octets MUST be treated as a connection error (Section 5.4.1) of type + FRAME_SIZE_ERROR. */ + else if ((pHeader->uLength % PROTOHTTP2_SETTING_SIZE) != 0) + { + NetPrintf(("protohttp2: [%p] received settings frame with invalid size %d, closing the connection\n", pState, pHeader->uLength)); + pState->eErrorType = ERRORTYPE_FRAME_SIZE_ERROR; + return; + } + + // decode the settings and update what we have saved + if (_ProtoHttp2DecodeSettings(pState, pBuf, pHeader) == FALSE) + { + return; + } + + // if not an acknowledgement, apply settings and send acknowledgement + if ((pHeader->uFlags & PROTOHTTP2_FLAG_ACK) == 0) + { + int32_t iStream; + + // encode the acknowledge + if ((iOffset = _ProtoHttp2EncodeSettings(pState, TRUE, pState->pOutBuf+pState->iOutLen, pState->iOutMax-pState->iOutLen)) != 0) + { + pState->iOutLen += iOffset; + } + + // resize the encoder dynamic table to match the peer, send dynamic table update if needed + HpackResize(pState->pEncoder, pState->PeerSettings.uHeaderTableSize, pState->bSettingsRecv); + pState->bSettingsRecv = TRUE; + + // recalculate the stream windows based on new settings and how much we consumed + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + StreamInfoT *pStream = &pState->Streams[iStream]; + if ((pStream->eState == STREAMSTATE_OPEN) || (pStream->eState == STREAMSTATE_HALF_CLOSED_LOCAL)) + { + pStream->iPeerWindow = pState->PeerSettings.uInitialWindowSize - (iPrevWindow - pStream->iPeerWindow); + } + } + } + else + { + // received an acknowledgement, disable timer + pState->uSettingsTimer = 0; + } + } + else if (pHeader->uType == FRAMETYPE_RST_STREAM) + { + uint32_t uErrorCode; + + /* ref: https://tools.ietf.org/html/rfc7540#section-6.4 + RST_STREAM frames MUST be associated with a stream. If a RST_STREAM frame is received with a stream identifier of 0x0, the recipient MUST + treat this as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if (pHeader->iStreamId == 0) + { + NetPrintf(("protohttp2: [%p] received rst_stream frame with stream id 0, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + return; + } + /* ref: https://tools.ietf.org/html/rfc7540#section-6.4 + A RST_STREAM frame with a length other than 4 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR. */ + else if (pHeader->uLength != PROTOHTTP2_RST_STREAM_SIZE) + { + NetPrintf(("protohttp2: [%p] received rst_stream frame with invalid size %d, closing the connection\n", pState, pHeader->uLength)); + pState->eErrorType = ERRORTYPE_FRAME_SIZE_ERROR; + return; + } + + // decode error code + uErrorCode = *pBuf++ << 24; + uErrorCode |= *pBuf++ << 16; + uErrorCode |= *pBuf++ << 8; + uErrorCode |= *pBuf++; + + NetPrintf(("protohttp2: [%p] received rst stream (error=%s)\n", pState, _ProtoHttp2_strErrorType[uErrorCode])); + + // cache the error code + pStreamInfo->eErrorType = (ErrorTypeE)uErrorCode; + + // close the stream and fire any callbacks needed + _ProtoHttp2StreamCloseOnError(pState, pStreamInfo); + } + else if (pHeader->uType == FRAMETYPE_GOAWAY) + { + int32_t iStreamId = 0, iStream; + uint32_t uErrorCode; + char strDebugData[256] = ""; + + /* ref: https://tools.ietf.org/html/rfc7540#section-6.8 + The GOAWAY frame applies to the connection, not a specific stream. An endpoint MUST treat a GOAWAY frame with a stream identifier other + than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if (pHeader->iStreamId != 0) + { + NetPrintf(("protohttp2: [%p] received goaway frame with stream id not 0, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + } + /* ref: https://tools.ietf.org/html/rfc7540#section-6.8 + the GOAWAY frame doesn't have any requirements about frame size but I need to ensure that we at least received the required amount of data */ + else if (pHeader->uLength < PROTOHTTP2_GOAWAY_SIZE) + { + NetPrintf(("protohttp2: [%p] received goway frame with invalid size %d, closing the connection\n", pState, pHeader->uLength)); + pState->eErrorType = ERRORTYPE_FRAME_SIZE_ERROR; + } + + // if we can decode it since no error occured, do that and save the error type + if (pState->eErrorType == ERRORTYPE_NO_ERROR) + { + // decode last-stream-id + iStreamId = *pBuf++ << 24; + iStreamId |= *pBuf++ << 16; + iStreamId |= *pBuf++ << 8; + iStreamId |= *pBuf++; + // decode error code + uErrorCode = *pBuf++ << 24; + uErrorCode |= *pBuf++ << 16; + uErrorCode |= *pBuf++ << 8; + uErrorCode |= *pBuf++; + // decode debug data + ds_strsubzcpy(strDebugData, sizeof(strDebugData), (const char *)pBuf, pHeader->uLength-PROTOHTTP2_GOAWAY_SIZE); + + // cache the error the peer sent + pState->eErrorType = (ErrorTypeE)uErrorCode; + } + NetPrintf(("protohttp2: [%p] received goaway frame (stream=0x%08x, error=%s, debug=%s)\n", pState, iStreamId, _ProtoHttp2_strErrorType[pState->eErrorType], strDebugData)); + + // close all the streams + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + _ProtoHttp2StreamCloseOnError(pState, &pState->Streams[iStream]); + } + + // peer told us to go away, set the state and (attempt to) close the connection + pState->eState = ST_FAIL; + _ProtoHttp2Close(pState, "goaway"); + } + else if (pHeader->uType == FRAMETYPE_PING) + { + /* ref: https://tools.ietf.org/html/rfc7540#section-6.7 + PING frames are not associated with any individual stream. If a PING frame is received with a stream identifier field value other than + 0x0, the recipient MUST respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if (pHeader->iStreamId != 0) + { + NetPrintf(("protohttp2: [%p] received ping frame with stream id not 0, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + } + /* ref: https://tools.ietf.org/html/rfc7540#section-6.7 + Receipt of a PING frame with a length field value other than 8 MUST be treated as a connection error (Section 5.4.1) of type + FRAME_SIZE_ERROR. */ + else if (pHeader->uLength != PROTOHTTP2_PING_SIZE) + { + NetPrintf(("protohttp2: [%p] received ping frame with incorrect length %d, closing the connection\n", pState, pHeader->uLength)); + pState->eErrorType = ERRORTYPE_FRAME_SIZE_ERROR; + } + else if ((pHeader->uFlags & PROTOHTTP2_FLAG_ACK) == 0) + { + // encode ping acknowledgement + if ((iOffset = _ProtoHttp2EncodePing(pState, TRUE, pBuf)) != 0) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] not enough space to encode ping frame into output buffer\n", pState)); + } + } + else + { + // we received an acknowlegement, reset timer + pState->uPingTimer = 0; + } + } + else if (pHeader->uType == FRAMETYPE_HEADERS) + { + /* ref: https://tools.ietf.org/html/rfc7540#section-6.2 + HEADERS frames MUST be associated with a stream. If a HEADERS frame is received whose stream identifier field is 0x0, the recipient MUST + respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if (pHeader->iStreamId != 0) + { + _ProtoHttp2HandleHeaders(pState, pBuf, pHeader, pStreamInfo); + } + else + { + NetPrintf(("protohttp2: [%p] received headers frame with stream id 0, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + } + } + else if (pHeader->uType == FRAMETYPE_DATA) + { + /* ref: https://tools.ietf.org/html/rfc7540#section-6.1 + DATA frames MUST be associated with a stream. If a DATA frame is received whose stream identifier field is 0x0, the recipient MUST + respond with a connection error (Section 5.4.1) of type PROTOCOL_ERROR. */ + if (pHeader->iStreamId != 0) + { + _ProtoHttp2HandleData(pState, pBuf, pHeader, pStreamInfo); + } + else + { + NetPrintf(("protohttp2: [%p] received data frame with stream id 0, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + } + } + else if (pHeader->uType == FRAMETYPE_WINDOW_UPDATE) + { + int32_t iIncrement; + + /* ref: https://tools.ietf.org/html/rfc7540#section-6.9 + A WINDOW_UPDATE frame with a length other than 4 octets MUST be treated as a connection error (Section 5.4.1) of type FRAME_SIZE_ERROR. */ + if (pHeader->uLength != PROTOHTTP2_WINDOW_UPDATE_SIZE) + { + NetPrintf(("protohttp2: [%p] received window update frame with incorrect length %d, closing the connection\n", pState, pHeader->uLength)); + pState->eErrorType = ERRORTYPE_FRAME_SIZE_ERROR; + return; + } + + // decode 31-bit window size increment (ignoring the high bit) + iIncrement = (*pBuf++ & 0x7f) << 24; + iIncrement |= *pBuf++ << 16; + iIncrement |= *pBuf++ << 8; + iIncrement |= *pBuf++; + + // make sure the window size increment is valid + if (iIncrement != 0) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] received window update (stream=0x%08x, increment=%d)\n", pState, pHeader->iStreamId, iIncrement)); + if (pHeader->iStreamId == 0) + { + pState->iPeerWindow += iIncrement; + } + else + { + pStreamInfo->iPeerWindow += iIncrement; + } + } + else + { + /* ref: https://tools.ietf.org/html/rfc7540#section-6.9 + A receiver MUST treat the receipt of a WINDOW_UPDATE frame with an flow-control window increment of 0 as a stream error (Section 5.4.2) + of type PROTOCOL_ERROR; errors on the connection flow-control window MUST be treated as a connection error (Section 5.4.1). */ + if (pHeader->iStreamId == 0) + { + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + NetPrintf(("protohttp2: [%p] received invalid connection wide window size increment, closing connection\n", pState)); + } + else + { + _ProtoHttp2PrepareRstStream(pState, pStreamInfo, ERRORTYPE_PROTOCOL_ERROR, "received invalid stream window size increment"); + } + } + } + else if (pHeader->uType == FRAMETYPE_CONTINUATION) + { + /* we should never receive continuation as our header list maximum + is well below what we can send in a frame */ + NetPrintf(("protohttp2: [%p] received continuation frame when not expected, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + } + else if (pHeader->uType == FRAMETYPE_PUSH_PROMISE) + { + /* we disabled push in our settings so if we receive this type of frame + it is a protocol level error */ + NetPrintf(("protohttp2: [%p] received push promise frame when push was disabled, closing the connection\n", pState)); + pState->eErrorType = ERRORTYPE_PROTOCOL_ERROR; + } + + /* ref: https://tools.ietf.org/html/rfc7540#section-4.1 + Implementations MUST ignore and discard any frame that has a type that is unknown. */ +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2FormatRequestHeader + + \Description + Format a request header based on given input data. + + \Input *pState - module state + \Input *pStreamInfo - stream information for the request + \Input *pUrl - pointer to user-supplied url + \Input iDataLen - size of included data; zero if none, negative if streaming put/post + \Input **pOutHdr - [out] output of the formatting + + \Output + int32_t - zero=success, else error + + \Version 10/24/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2FormatRequestHeader(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, const char *pUrl, int32_t iDataLen, char **pOutHdr) +{ + const char *pUrlSlash; + char *pHeader; + int32_t iHeaderLen = 0, iHeaderMax; + ProtoHttp2CustomHeaderCbT *pCustomHeaderCb; + void *pUserData; + + // if url is empty or isn't preceded by a slash, put one in + pUrlSlash = (*pUrl != '/') ? "/" : ""; + + // set up for header formatting + pHeader = (char *)pState->pOutBuf + pState->iOutLen; + iHeaderMax = pState->iOutMax - pState->iOutLen; + + // format request header + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, ":method: %s\r\n", _ProtoHttp2_strRequestNames[pStreamInfo->eRequestType]); + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, ":path: %s%s\r\n", pUrlSlash, pUrl); + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, ":scheme: %s\r\n", pState->bSecure ? "https" : "http"); + if ((pState->bSecure && (pState->iPort == 443)) || (pState->iPort == 80)) + { + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, ":authority: %s\r\n", pState->strHost); + } + else + { + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, ":authority: %s:%d\r\n", pState->strHost, pState->iPort); + } + if (iDataLen > 0) + { + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, "content-length: %qd\r\n", iDataLen); + } + if ((pState->pAppendHdr == NULL) || !ds_stristr(pState->pAppendHdr, "user-agent:")) + { + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, "user-agent: ProtoHttp %d.%d/DS %d.%d.%d.%d.%d (" DIRTYCODE_PLATNAME ")\r\n", + (PROTOHTTP2_VERSION >> 8) & 0xFF, PROTOHTTP2_VERSION & 0xFF, DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH); + } + if ((pState->pAppendHdr == NULL) || (pState->pAppendHdr[0] == '\0')) + { + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, "accept: */*\r\n"); + } + else + { + iHeaderLen += ds_snzprintf(pHeader+iHeaderLen, iHeaderMax-iHeaderLen, "%s", pState->pAppendHdr); + } + + // null-terminate the buffer + pHeader[iHeaderLen++] = '\0'; + + // request level callback takes priority to global + if ((pCustomHeaderCb = pStreamInfo->pCustomHeaderCb) != NULL) + { + pUserData = pStreamInfo->pUserData; + } + else + { + pCustomHeaderCb = pState->pCustomHeaderCb; + pUserData = pState->pCallbackRef; + } + + // call custom header format callback, if specified + if (pCustomHeaderCb != NULL) + { + if ((iHeaderLen = pCustomHeaderCb(pState, pHeader, iHeaderMax, NULL, 0, pUserData)) < 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] custom header callback error %d\n", pState, iHeaderLen)); + return(iHeaderLen); + } + if (iHeaderLen == 0) + { + iHeaderLen = (int32_t)strlen(pHeader); + } + } + + // make sure we were able to complete the header + if (iHeaderLen > iHeaderMax) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] not enough buffer to format request header (have %d, need %d)\n", pState, iHeaderMax, iHeaderLen)); + return(PROTOHTTP2_MINBUFF); + } + // make sure that the size doesn't exceed what the server supports + else if (iHeaderLen > (signed)pState->PeerSettings.uMaxHeaderListSize) + { + NetPrintf(("protohttp2: [%p] the formatted (uncompressed) header exceeds the size that the server supports (have %u, need %d)\n", + pState, pState->PeerSettings.uMaxHeaderListSize, iHeaderLen)); + return(-1); + } + + // save a copy of the header + ds_strnzcpy(pStreamInfo->strRequestHeader, pHeader, sizeof(pStreamInfo->strRequestHeader)); + + #if DIRTYCODE_LOGGING + if (pState->iVerbose > 1) + { + NetPrintf(("protohttp2: [%p] sending request:\n", pState)); + NetPrintWrap(pHeader, 80); + } + #endif + + /* allocate a temporary buffer to store the uncompressed buffer. we cannot use the current spot we have written the + data to because the compress header needs to be written to the same place before being sent on the wire. + this will not exceed our frame size */ + if ((*pOutHdr = (char *)DirtyMemAlloc(iHeaderLen+1, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp2: [%p] could not allocate space for request header compression\n", pState)); + return(-1); + } + ds_strnzcpy(*pOutHdr, pHeader, iHeaderLen+1); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2SetAppendHeader + + \Description + Set given string as append header, allocating memory as required + + \Input *pState - module state + \Input *pAppendHdr - append header string + + \Output + int32_t - zero=success, else error + + \Version 10/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2SetAppendHeader(ProtoHttp2RefT *pState, const char *pAppendHdr) +{ + int32_t iAppendBufLen, iAppendStrLen; + + // check for empty append string, in which case we free the buffer + if ((pAppendHdr == NULL) || (*pAppendHdr == '\0')) + { + if (pState->pAppendHdr != NULL) + { + DirtyMemFree(pState->pAppendHdr, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pAppendHdr = NULL; + } + pState->iAppendLen = 0; + return(0); + } + + // check to see if append header is already set + if ((pState->pAppendHdr != NULL) && (strcmp(pAppendHdr, pState->pAppendHdr) == 0)) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] ignoring set of append header '%s' that is already set\n", pState, pAppendHdr)); + return(0); + } + + // get append header length + iAppendStrLen = (int32_t)strlen(pAppendHdr); + // append buffer size includes null and space for \r\n if not included by submitter + iAppendBufLen = iAppendStrLen + 3; + + // see if we need to allocate a new buffer + if (iAppendBufLen > pState->iAppendLen) + { + if (pState->pAppendHdr != NULL) + { + DirtyMemFree(pState->pAppendHdr, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + if ((pState->pAppendHdr = DirtyMemAlloc(iAppendBufLen, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) != NULL) + { + pState->iAppendLen = iAppendBufLen; + } + else + { + NetPrintf(("protohttp2: [%p] could not allocate %d byte buffer for append header\n", pState, iAppendBufLen)); + pState->iAppendLen = 0; + return(-1); + } + } + + // copy append header + ds_strnzcpy(pState->pAppendHdr, pAppendHdr, iAppendStrLen+1); + + // if append header is not \r\n terminated, do it here + if ((pAppendHdr[iAppendStrLen-2] != '\r') || (pAppendHdr[iAppendStrLen-1] != '\n')) + { + ds_strnzcat(pState->pAppendHdr, "\r\n", pState->iAppendLen); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2ResizeInputBuffer + + \Description + Resize the input buffer + + \Input *pState - module state + \Input iBufMax - new buffer size + + \Output + int32_t - zero=success, else error + + \Version 11/14/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2ResizeInputBuffer(ProtoHttp2RefT *pState, int32_t iBufMax) +{ + int32_t iCopySize; + uint8_t *pInpBuf; + + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] resizing input buffer from %d to %d bytes\n", pState, pState->iInpMax, iBufMax)); + if ((pInpBuf = (uint8_t *)DirtyMemAlloc(iBufMax, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp2: [%p] could not resize input buffer\n", pState)); + return(-1); + } + + // calculate size of data to copy from old buffer to new + if ((iCopySize = pState->iInpLen) > iBufMax) + { + NetPrintf(("protohttp2: [%p] warning; resize of input buffer is losing %d bytes of data\n", pState, iCopySize - iBufMax)); + iCopySize = iBufMax; + } + // copy valid contents of input buffer, if any, to new buffer + ds_memcpy(pInpBuf, pState->pInpBuf, iCopySize); + + // dispose of old buffer + DirtyMemFree(pState->pInpBuf, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + // update buffer variables + pState->pInpBuf = pInpBuf; + pState->iInpLen = iCopySize; + pState->iInpMax = iBufMax; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2CheckSettings + + \Description + Handle all setting related synchronization + + \Input *pState - module state + + \Notes + This function will check to make sure that we get our settings + acknowledgement from our peer in time. It also checks to see + if we need to send a new settings frame to our peer, in this + case it will handle the encoding into our output buffer. + + \Version 11/15/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2CheckSettings(ProtoHttp2RefT *pState) +{ + // check for settings timeout + if (pState->uSettingsTimer != 0) + { + if (NetTickDiff(NetTick(), pState->uSettingsTimer) > 0) + { + NetPrintf(("protohttp2: [%p] peer did not send settings in time, closing connection\n", pState)); + pState->eErrorType = ERRORTYPE_SETTINGS_TIMEOUT; + } + } + // otherwise check if we need to synchronize our settings + else if (memcmp(&pState->LocalSettings, &pState->TempSettings, sizeof(pState->TempSettings)) != 0) + { + int32_t iOffset; + if ((iOffset = _ProtoHttp2EncodeSettings(pState, FALSE, pState->pOutBuf+pState->iOutLen, pState->iOutMax-pState->iOutLen)) != 0) + { + pState->iOutLen += iOffset; + pState->uSettingsTimer = NetTick() + PROTOHTTP2_TIMEOUT; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2CheckWindows + + \Description + Check the connection and stream wide windows to send increments when + necessary + + \Input *pState - module state + + \Version 11/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2CheckWindows(ProtoHttp2RefT *pState) +{ + int32_t iStream; + + // check the connection wide window, if so increment a large amount to handle our multiple streams + if (pState->iLocalWindow <= (PROTOHTTP2_MAX_STREAMS*PROTOHTTP2_WINDOWSIZE)) + { + if (_ProtoHttp2EncodeWindowUpdate(pState, 0, PROTOHTTP2_MAX_STREAMS*PROTOHTTP2_WINDOWSIZE, &pState->iLocalWindow) != 0) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] not enough space in the output buffer to encode window update for connection\n", pState)); + } + } + + // check the stream wide windows + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + StreamInfoT *pStreamInfo = &pState->Streams[iStream]; + + if (pStreamInfo->iLocalWindow <= PROTOHTTP2_WINDOWSIZE) + { + if (_ProtoHttp2EncodeWindowUpdate(pState, pStreamInfo->iStreamId, PROTOHTTP2_WINDOWSIZE, &pStreamInfo->iLocalWindow) != 0) + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] not enough space in the output buffer to encode window update for stream\n", pState)); + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2CheckActivityTimeout + + \Description + If we lack i/o activity ping the server to make sure the connection is still + active. If the don't receive an ack in time we then close the connection. + + \Input *pState - module state + + \Version 08/13/2018 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttp2CheckActivityTimeout(ProtoHttp2RefT *pState) +{ + /* if the ping timer isn't active check if our connection has been idle, if so then lets ping the server to see if + we have a valid connection. */ + if (pState->uPingTimer == 0) + { + if (NetTickDiff(NetTick(), pState->uTimer) > 0) + { + uint8_t aInput[PROTOHTTP2_PING_SIZE]; + + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] idle after %us, sending ping to check if connection is still active\n", + pState, pState->uTimeout/1000)); + + // encode ping to check if connection is active + if (_ProtoHttp2EncodePing(pState, FALSE, aInput) == 0) + { + pState->uPingTimer = NetTick() + PROTOHTTP2_TIMEOUT_DEFAULT; + } + else + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] not enough space to encode ping frame into output buffer\n", pState)); + } + } + } + // otherwise if the ping timer is active, close the connection if the server doesn't acknowledge in time + else if (NetTickDiff(NetTick(), pState->uPingTimer) > 0) + { + NetPrintf(("protohttp2: [%p] peer did not acknowledge ping in time, closing connection\n", pState)); + _ProtoHttp2Close(pState, "ping timeout"); + pState->bTimeout = TRUE; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2Read + + \Description + Read data out of the stream's buffer + + \Input *pState - module state + \Input *pStreamInfo - stream we are reading from + \Input *pBuffer - buffer to store data in + \Input iBufMin - minimum number of bytes to return + \Input iBufMax - maximum number of bytes to return (buffer size) + + \Output + int32_t - negative=error, zero=no data available or bufmax <= 0, positive=number of bytes read + + \Version 11/23/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2Read(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, uint8_t *pBuffer, int32_t iBufMin, int32_t iBufMax) +{ + int32_t iLen; + + // early out for failure result + if ((pState->eState == ST_FAIL) || ((pStreamInfo->eState == STREAMSTATE_CLOSED) && (pStreamInfo->eErrorType != ERRORTYPE_NO_ERROR))) + { + return(pState->bTimeout ? PROTOHTTP2_TIMEOUT : PROTOHTTP2_RECVFAIL); + } + // if they only wanted head that is what we will return + if ((pStreamInfo->iHeaderLen > 0) && (pStreamInfo->eRequestType == PROTOHTTP_REQUESTTYPE_HEAD)) + { + return(PROTOHTTP2_RECVHEAD); + } + // if we haven't sent the headers yet we cannot expect to receive any data + if (pStreamInfo->eState == STREAMSTATE_IDLE) + { + return(PROTOHTTP2_RECVWAIT); + } + + // if they are querying only for done state when no more data is available to be read + if ((iBufMax == 0) && ((pStreamInfo->eState == STREAMSTATE_HALF_CLOSED_REMOTE) || (pStreamInfo->eState == STREAMSTATE_CLOSED)) && (pStreamInfo->iBodyReceived == pStreamInfo->iBodySize)) + { + return(PROTOHTTP2_RECVDONE); + } + + // make sure the range is valid + if (iBufMax < 1) + { + return(0); + } + // clamp the range + iBufMin = DS_MAX(1, iBufMin); + iBufMax = DS_MAX(iBufMin, iBufMax); + iBufMin = DS_MIN(iBufMin, pStreamInfo->iDataMax); + iBufMax = DS_MIN(iBufMax, pStreamInfo->iDataMax); + + // figure out how much data is available + iLen = DS_MIN(pStreamInfo->iDataLen, iBufMax); + + // check for end of data + if ((iLen == 0) && ((pStreamInfo->eState == STREAMSTATE_HALF_CLOSED_REMOTE) || (pStreamInfo->eState == STREAMSTATE_CLOSED))) + { + // update body size now that stream is complete + pStreamInfo->iBodySize = pStreamInfo->iBodyReceived; + return(PROTOHTTP2_RECVDONE); + } + + // see if there is enough to return + if (iLen >= iBufMin) + { + // return data to caller + if (pBuffer != NULL) + { + ds_memcpy(pBuffer, pStreamInfo->pData, iLen); + + #if DIRTYCODE_LOGGING + NetPrintfVerbose((pState->iVerbose, 2, "protohttp2: [%p] read %d bytes\n", pState, iLen)); + if (pState->iVerbose > 3) + { + NetPrintMem(pBuffer, iLen, "http2-read"); + } + #endif + } + pStreamInfo->iBodyReceived += iLen; + + memmove(pStreamInfo->pData, pStreamInfo->pData+iLen, pStreamInfo->iDataLen-iLen); + pStreamInfo->iDataLen -= iLen; + + // return bytes read + return(iLen); + } + + // nothing available + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttp2EncodeRequest + + \Description + Encode the request into the output buffer (headers / data) + + \Input *pState - module state + \Input *pStreamInfo - stream we are reading from + \Input *pUrl - address we are sending the request to + \Input *pData - data we are encoding + \Input iDataSize - size of the data we are encoding + + \Output + int32_t - negative=error, otherwise amount of user data encoded + + \Version 11/23/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttp2EncodeRequest(ProtoHttp2RefT *pState, StreamInfoT *pStreamInfo, const char *pUrl, const uint8_t *pData, int32_t iDataSize) +{ + int32_t iResult, iOutLen; + char *pOutHdr = NULL; + + // save the outlen in case of error + iOutLen = pState->iOutLen; + + // format the request headers + if ((iResult = _ProtoHttp2FormatRequestHeader(pState, pStreamInfo, pUrl, iDataSize, &pOutHdr)) < 0) + { + return(iResult); + } + + // encode the request headers + if ((iResult = _ProtoHttp2EncodeHeaders(pState, pStreamInfo, iDataSize == 0, pOutHdr)) < 0) + { + NetPrintf(("protohttp2: [%p] not enough room in the output buffer to encode headers\n", pState)); + } + // encode the data if any exist + else if ((pData != NULL) && (iDataSize > 0)) + { + if ((iResult = _ProtoHttp2EncodeData(pState, pStreamInfo, pData, iDataSize, TRUE)) < 0) + { + pState->iOutLen = iOutLen; + } + } + + // cleanup temporary buffer + if (pOutHdr != NULL) + { + DirtyMemFree(pOutHdr, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + + // return result back to user + return(iResult); +} + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Create + + \Description + Allocate module state and prepare for use + + \Input iBufSize - length of recv buffer + + \Output + ProtoHttp2RefT *- pointer to module state, or NULL + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +ProtoHttp2RefT *ProtoHttp2Create(int32_t iBufSize) +{ + ProtoHttp2RefT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query memgroup data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // clamp the buffer size based on the default frame size and maximum frame size + iBufSize = PROTOHTTP2_ClampFrameSize(iBufSize); + + // allocate state + if ((pState = (ProtoHttp2RefT *)DirtyMemAlloc(sizeof(*pState), PROTOHTTP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp2: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + pState->iVerbose = 1; + pState->uTimeout = PROTOHTTP2_TIMEOUT_DEFAULT; + pState->bHuffman = TRUE; + + // set the buffer sizes to the defaults, adding the additional space for a frame header + pState->iInpMax = iBufSize+PROTOHTTP2_RESERVED_SIZE; + pState->iOutMax = _ProtoHttp2_DefaultSettings.uMaxFrameSize+PROTOHTTP2_RESERVED_SIZE; + + // allocate ssl state + if ((pState->pSsl = ProtoSSLCreate()) == NULL) + { + NetPrintf(("protohttp2: [%p] unable to allocate ssl module state\n", pState)); + ProtoHttp2Destroy(pState); + return(NULL); + } + ProtoSSLControl(pState->pSsl, 'alpn', 0, 0, (void *)"h2"); // tell protossl to advertise the http2 protocol + ProtoSSLControl(pState->pSsl, 'snod', TRUE, 0, NULL); // set TCP_NODELAY on the SSL socket + + // allocate input buffer + if ((pState->pInpBuf = (uint8_t *)DirtyMemAlloc(pState->iInpMax, PROTOHTTP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp2: [%p] unable to allocate protohttp2 input buffer\n", pState)); + ProtoHttp2Destroy(pState); + return(NULL); + } + ds_memclr(pState->pInpBuf, pState->iInpMax); + + // allocate output buffer + if ((pState->pOutBuf = (uint8_t *)DirtyMemAlloc(pState->iOutMax, PROTOHTTP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protohttp2: [%p] unable to allocate protohttp2 output buffer\n", pState)); + ProtoHttp2Destroy(pState); + return(NULL); + } + ds_memclr(pState->pOutBuf, pState->iOutMax); + + // create the encoder context + if ((pState->pEncoder = HpackCreate(_ProtoHttp2_DefaultSettings.uHeaderTableSize, FALSE)) == NULL) + { + NetPrintf(("protohttp2: [%p] unable to create encoder context\n", pState)); + ProtoHttp2Destroy(pState); + return(NULL); + } + // create the decoder context + if ((pState->pDecoder = HpackCreate(_ProtoHttp2_DefaultSettings.uHeaderTableSize, TRUE)) == NULL) + { + NetPrintf(("protohttp2: [%p] unable to create decoder context\n", pState)); + ProtoHttp2Destroy(pState); + return(NULL); + } + + // init crit + NetCritInit(&pState->HttpCrit, "ProtoHttp2"); + + // reset the state to default + _ProtoHttp2Reset(pState); + + // return the state + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Update + + \Description + Give time to module to do its thing (should be called periodically to + allow module to perform work) + + \Input *pState - module state + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +void ProtoHttp2Update(ProtoHttp2RefT *pState) +{ + int32_t iResult; + + // give time to ssl module + ProtoSSLUpdate(pState->pSsl); + + // acquire sole access to http crit + NetCritEnter(&pState->HttpCrit); + + // see if the connection is complete + if (pState->eState == ST_CONN) + { + int32_t iStream; + + iResult = ProtoSSLStat(pState->pSsl, 'stat', NULL, 0); + if (iResult > 0) + { + /* just use some stack space to send this connection preface instead + of having to juggle with our output buffer (that might have data) */ + uint8_t aConnectionPreface[128]; + int32_t iOffset = 0; + + // add the connection preface to the buffer + ds_memcpy(aConnectionPreface, _ProtoHttp2_ConnectionPreface, sizeof(_ProtoHttp2_ConnectionPreface)); + iOffset += sizeof(_ProtoHttp2_ConnectionPreface); + + // encode our settings + iOffset += _ProtoHttp2EncodeSettings(pState, FALSE, aConnectionPreface+iOffset, sizeof(aConnectionPreface)-iOffset); + + // send the connection preface+settings and wait for settings from server + _ProtoHttp2Send(pState, aConnectionPreface, iOffset); + pState->eState = ST_ACTIVE; + pState->uSettingsTimer = NetTick() + PROTOHTTP2_TIMEOUT_DEFAULT; + } + else if (iResult < 0) + { + NetPrintf(("protohttp2: [%p] ST_CONN got ST_FAIL (err=%d)\n", pState, iResult)); + pState->eState = ST_FAIL; + pState->iSslFail = ProtoSSLStat(pState->pSsl, 'fail', NULL, 0); + pState->iHresult = ProtoSSLStat(pState->pSsl, 'hres', NULL, 0); + } + else if (NetTickDiff(NetTick(), pState->uTimer) >= 0) + { + NetPrintf(("protohttp2: [%p] timed out while establishing connection\n", pState)); + _ProtoHttp2Close(pState, "timeout"); + pState->bTimeout = TRUE; + } + + // if a failure occured when establishing the connection handle closing our streams + for (iStream = 0; (pState->eState == ST_FAIL) && (iStream < pState->iNumStreams); iStream += 1) + { + _ProtoHttp2StreamCloseOnError(pState, &pState->Streams[iStream]); + } + } + /* otherwise we are connected so let's try to send / receive data from our peer + we will act on the data depending our state */ + else if ((pState->eState > ST_CONN) && (pState->eState < ST_FAIL)) + { + // send any data if we have any + if (pState->iOutLen > 0) + { + // send as much data as we can and update our offset accordingly + if ((iResult = _ProtoHttp2Send(pState, pState->pOutBuf+pState->iOutOff, pState->iOutLen-pState->iOutOff)) > 0) + { + pState->iOutOff += iResult; + } + // if we sent all our data, clear our length/offset + if (pState->iOutOff == pState->iOutLen) + { + pState->iOutLen = pState->iOutOff = 0; + } + } + + // receive new data from our peer + if ((iResult = _ProtoHttp2Recv(pState, pState->pInpBuf+pState->iInpLen, pState->iInpMax-pState->iInpLen)) > 0) + { + FrameHeaderT Header; + const uint8_t *pBuf; + StreamInfoT *pStreamInfo = NULL; + + // increment the offset into the input buffer + pState->iInpLen += iResult; + + // decode as many frames as possible + while ((pBuf = _ProtoHttp2DecodeFrameHeader(pState, pState->pInpBuf, pState->iInpLen, &Header, &pStreamInfo)) != NULL) + { + const uint32_t uFrameLength = Header.uLength+Header.uPadding+PROTOHTTP2_HEADER_SIZE; + + // handle the frame + _ProtoHttp2HandleFrame(pState, pBuf, &Header, pStreamInfo); + + /* advance the buffer past the frame and update new length + note: we need to take advantage of as much space as possible in our buffer. + for that reason we cannot just use an offset into the buffer like we + do on the send side. */ + memmove(pState->pInpBuf, pState->pInpBuf+uFrameLength, pState->iInpLen-uFrameLength); + pState->iInpLen -= uFrameLength; + } + } + + // handle settings synchronization + _ProtoHttp2CheckSettings(pState); + + // check our receive windows and send updates if needed + _ProtoHttp2CheckWindows(pState); + + // check and handle if we lack i/o activity + _ProtoHttp2CheckActivityTimeout(pState); + + // attempt to handle any connection wide error + if (pState->eErrorType != ERRORTYPE_NO_ERROR) + { + _ProtoHttp2SendGoAway(pState, pState->eErrorType, _ProtoHttp2_strErrorType[pState->eErrorType]); + } + } + + // release access to http crit + NetCritLeave(&pState->HttpCrit); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Destroy + + \Description + Destroy the module and release its state + + \Input *pState - module state + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +void ProtoHttp2Destroy(ProtoHttp2RefT *pState) +{ + int32_t iStream; + + // try to gracefully close the connection + ProtoHttp2Close(pState); + + // clean up append header memory + _ProtoHttp2SetAppendHeader(pState, NULL); + + // cleanup stream info memory + for (iStream = 0; iStream < pState->iNumStreams; iStream += 1) + { + _ProtoHttp2StreamInfoCleanup(pState, &pState->Streams[iStream]); + } + + NetCritKill(&pState->HttpCrit); + + if (pState->pDecoder != NULL) + { + HpackDestroy(pState->pDecoder); + pState->pDecoder = NULL; + } + if (pState->pEncoder != NULL) + { + HpackDestroy(pState->pEncoder); + pState->pEncoder = NULL; + } + if (pState->pInpBuf != NULL) + { + DirtyMemFree(pState->pInpBuf, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pInpBuf = NULL; + } + if (pState->pOutBuf != NULL) + { + DirtyMemFree(pState->pOutBuf, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pOutBuf = NULL; + } + if (pState->pSsl != NULL) + { + ProtoSSLDestroy(pState->pSsl); + pState->pSsl = NULL; + } + DirtyMemFree(pState, PROTOHTTP_MEMID, pState->iMemGroup, pState->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Callback + + \Description + Set header callbacks. + + \Input *pState - module state + \Input *pCustomHeaderCb - pointer to custom send header callback (may be NULL) + \Input *pReceiveHeaderCb- pointer to recv header callback (may be NULL) + \Input *pUserData - user-supplied callback ref (may be NULL) + + \Notes + The ProtoHttpCustomHeaderCbT callback is used to allow customization of + the HTTP header before sending. It is more powerful than the append + header functionality, allowing to make changes to any part of the header + before it is sent. The callback should return a negative code if an error + occurred, zero can be returned if the application wants ProtoHttp to + calculate the header size, or the size of the header can be returned if + the application has already calculated it. The header should *not* be + terminated with the double \\r\\n that indicates the end of the entire + header, as protohttp appends itself. + + The ProtoHttpReceiveHeaderCbT callback is used to view the header + immediately on reception. It can be used when the built-in header + cache (retrieved with ProtoHttpStatus('htxt') is too small to hold + the entire header received. It is also possible with this method + to view redirection response headers that cannot be retrieved + normally. This can be important if, for example, the application + wishes to attach new cookies to a redirection response. The + custom response header and custom header callback can be used in + conjunction to implement this type of functionality. + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +void ProtoHttp2Callback(ProtoHttp2RefT *pState, ProtoHttp2CustomHeaderCbT *pCustomHeaderCb, ProtoHttp2ReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData) +{ + pState->pCustomHeaderCb = pCustomHeaderCb; + pState->pReceiveHeaderCb = pReceiveHeaderCb; + pState->pCallbackRef = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Request + + \Description + Initiate an HTTP transfer without callback. Pass in a URL and the module + starts a transfer from the appropriate server. + + \Input *pState - module state + \Input *pUrl - the url to retrieve + \Input *pData - user data to send to server (PUT and POST only) + \Input iDataSize - size of user data to send to server (PUT and POST only) + \Input eRequestType - request type to make + \Input *pStreamId - [out] identifier tied to the request + + \Output + int32_t - negative=failure, zero=success, >0=number of data bytes sent (PUT and POST only) + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2Request(ProtoHttp2RefT *pState, const char *pUrl, const uint8_t *pData, int32_t iDataSize, ProtoHttpRequestTypeE eRequestType, int32_t *pStreamId) +{ + return(ProtoHttp2RequestCb2(pState, pUrl, pData, iDataSize, eRequestType, pStreamId, NULL, NULL, NULL, NULL)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2RequestCb + + \Description + Initiate an HTTP transfer with write callback. Pass in a URL and the module starts + a transfer from the appropriate server. + + \Input *pState - module state + \Input *pUrl - the url to retrieve + \Input *pData - user data to send to server (PUT and POST only) + \Input iDataSize - size of user data to send to server (PUT and POST only) + \Input eRequestType - request type to make + \Input *pStreamId - [out] identifier tied to the request + \Input *pWriteCb - write callback (optional) + \Input *pUserData - write callback user data (optional) + + \Output + int32_t - negative=failure, zero=success, >0=number of data bytes sent (PUT and POST only) + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2RequestCb(ProtoHttp2RefT *pState, const char *pUrl, const uint8_t *pData, int32_t iDataSize, ProtoHttpRequestTypeE eRequestType, int32_t *pStreamId, ProtoHttp2WriteCbT *pWriteCb, void *pUserData) +{ + return(ProtoHttp2RequestCb2(pState, pUrl, pData, iDataSize, eRequestType, pStreamId, pWriteCb, NULL, NULL, pUserData)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2RequestCb2 + + \Description + Initiate an HTTP transfer with callbacks. Pass in a URL and the module starts + a transfer from the appropriate server. + + \Input *pState - module state + \Input *pUrl - the url to retrieve + \Input *pData - user data to send to server (PUT and POST only) + \Input iDataSize - size of user data to send to server (PUT and POST only) + \Input eRequestType - request type to make + \Input *pStreamId - [out] identifier tied to the request + \Input *pWriteCb - write callback (optional) + \Input *pCustomHeaderCb - custom header callback (optional) + \Input *pReceiveHeaderCb- receive header callback (optional) + \Input *pUserData - write callback user data (optional) + + \Output + int32_t - negative=failure, zero=success, >0=number of data bytes sent (PUT and POST only) + + \Version 09/11/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2RequestCb2(ProtoHttp2RefT *pState, const char *pUrl, const uint8_t *pData, int32_t iDataSize, ProtoHttpRequestTypeE eRequestType, int32_t *pStreamId, ProtoHttp2WriteCbT *pWriteCb, ProtoHttp2CustomHeaderCbT *pCustomHeaderCb, ProtoHttp2ReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData) +{ + char strKind[8], strHost[sizeof(pState->strHost)]; + int32_t iPort, bSecure, iResult = 0; + uint8_t bPortSpecified; + StreamInfoT StreamInfo; + + // make sure we can create a new stream + if (pState->iNumStreams >= PROTOHTTP2_MAX_STREAMS) + { + NetPrintf(("protohttp2: [%p] exceeded maximum number of concurrent stream\n", pState)); + return(-1); + } + if (pStreamId == NULL) + { + NetPrintf(("protohttp2: [%p] stream identifier output is NULL (required to save identifier)\n", pState)); + return(-2); + } + // make sure we can even attempt to fit the frame + if ((iDataSize > 0) && ((uint32_t)iDataSize > pState->LocalSettings.uMaxFrameSize)) + { + NetPrintf(("protohttp2: [%p] data size exceeds size of the frame, use streaming instead\n", pState)); + return(-3); + } + + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] %s %s\n", pState, _ProtoHttp2_strRequestNames[eRequestType], pUrl)); + + // parse the url for kind, host and port + pUrl = ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &bSecure, &bPortSpecified); + + // fill in any missing info (relative url) if available + if (_ProtoHttp2ApplyBaseUrl(pState, strKind, strHost, sizeof(strHost), &iPort, &bSecure, bPortSpecified) != 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] %s %s://%s:%d%s\n", pState, _ProtoHttp2_strRequestNames[eRequestType], + bSecure ? "https" : "http", strHost, iPort, pUrl)); + } + + // are we still connected, connected to same endpoint and have valid stream id? + if ((pState->eState > ST_IDLE) && (pState->eState < ST_FAIL) && (bSecure == pState->bSecure) && (ds_stricmp(strHost, pState->strHost) == 0) && (pState->iStreamId > 0)) + { + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] reusing previous connection\n", pState)); + } + else + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] request new connection -- url change to %s\n", pState, strHost)); + + // save new server/port/security state + ds_strnzcpy(pState->strHost, strHost, sizeof(pState->strHost)); + pState->iPort = iPort; + pState->bSecure = bSecure; + + // send goaway if necessary & close + _ProtoHttp2SendGoAway(pState, ERRORTYPE_NO_ERROR, "new connection"); + + // reset the state to default + _ProtoHttp2Reset(pState); + + // start connect + NetPrintfVerbose((pState->iVerbose, 0, "protohttp2: [%p] connect start (tick=%u)\n", pState, NetTick())); + ProtoSSLConnect(pState->pSsl, pState->bSecure, pState->strHost, 0, pState->iPort); + pState->eState = ST_CONN; + pState->uTimer = NetTick() + pState->uTimeout; + } + + // setup the stream info + ds_memclr(&StreamInfo, sizeof(StreamInfo)); + StreamInfo.iStreamId = *pStreamId = pState->iStreamId; + StreamInfo.eResponseCode = PROTOHTTP_RESPONSE_PENDING; + StreamInfo.eRequestType = eRequestType; + StreamInfo.pWriteCb = pWriteCb; + StreamInfo.pCustomHeaderCb = pCustomHeaderCb; + StreamInfo.pReceiveHeaderCb = pReceiveHeaderCb; + StreamInfo.pUserData = pUserData; + StreamInfo.iDataMax = StreamInfo.iLocalWindow = PROTOHTTP2_WINDOWSIZE; /* we never update our window size at runtime so assume this value */ + StreamInfo.iPeerWindow = pState->PeerSettings.uInitialWindowSize; + + NetCritEnter(&pState->HttpCrit); + // attempt to encode request + if ((iResult = _ProtoHttp2EncodeRequest(pState, &StreamInfo, pUrl, pData, iDataSize)) < 0) + { + _ProtoHttp2StreamInfoCleanup(pState, &StreamInfo); + *pStreamId = PROTOHTTP2_INVALID_STREAMID; + } + else + { + // add the stream information for tracking + ds_memcpy(&pState->Streams[pState->iNumStreams], &StreamInfo, sizeof(StreamInfo)); + pState->iNumStreams += 1; + + // increment to next stream id + pState->iStreamId += 2; + } + NetCritLeave(&pState->HttpCrit); + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Send + + \Description + Send data during an ongoing request + + \Input *pState - module state + \Input iStreamId - identifier of the stream to send the data on + \Input *pData - pointer to data to send + \Input iDataSize - size of data being sent + + \Output + int32_t - negative=PROTOHTTP2_MINBUFF or failure otherwise number of data + bytes sent + + \Notes + In the case of PROTOHTTP2_MINBUFF we do not have enough space to encode + the frame header. We need to return a special code due to the fact when sending + a zero sized end stream we need to have a way to tell if it was actually encoded + into the buffer. In this case you should retry to send on the next frame. + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2Send(ProtoHttp2RefT *pState, int32_t iStreamId, const uint8_t *pData, int32_t iDataSize) +{ + int32_t iResult = -1; + StreamInfoT *pStreamInfo; + + if ((pStreamInfo = _ProtoHttp2StreamInfoGet(pState, iStreamId)) != NULL) + { + // make sure the stream is open to allow us to send data + if (pStreamInfo->eState < STREAMSTATE_OPEN) + { + // not ready to send data yet + return(0); + } + else if ((pStreamInfo->eState != STREAMSTATE_OPEN) && (pStreamInfo->eState != STREAMSTATE_HALF_CLOSED_REMOTE)) + { + // we have already finished sending, an error occured + return(-1); + } + + // encode the data into the output buffer + NetCritEnter(&pState->HttpCrit); + iResult = _ProtoHttp2EncodeData(pState, pStreamInfo, pData, iDataSize, (iDataSize == PROTOHTTP2_STREAM_END)); + NetCritLeave(&pState->HttpCrit); + + /* if we don't have enough space to encode the buffer but we are not ending the stream, we can just send zero + bytes written. the only reason we send MINBUFF back is to make sure on zero sized payloads we have a way to + indiciate to try again */ + if ((iResult < 0) && (iDataSize != PROTOHTTP2_STREAM_END)) + { + iResult = 0; + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Recv + + \Description + Return the actual url data. + + \Input *pState - module state + \Input iStreamId - identifier of the stream to recv data from + \Input *pBuffer - buffer to store data in + \Input iBufMin - minimum number of bytes to return + \Input iBufMax - maximum number of bytes to return (buffer size) + + \Output + int32_t - negative=error, zero=no data available or bufmax <= 0, positive=number of bytes read + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2Recv(ProtoHttp2RefT *pState, int32_t iStreamId, uint8_t *pBuffer, int32_t iBufMin, int32_t iBufMax) +{ + StreamInfoT *pStreamInfo; + int32_t iResult = PROTOHTTP2_RECVFAIL; + + if ((pStreamInfo = _ProtoHttp2StreamInfoGet(pState, iStreamId)) != NULL) + { + NetCritEnter(&pState->HttpCrit); + iResult = _ProtoHttp2Read(pState, pStreamInfo, pBuffer, iBufMin, iBufMax); + NetCritLeave(&pState->HttpCrit); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2RecvAll + + \Description + Return all of the url data. + + \Input *pState - module state + \Input iStreamId - identifier of the stream to recv data from + \Input *pBuffer - buffer to store data in + \Input iBufSize - size of buffer + + \Output + int32_t - PROTOHTTP_RECV*, or positive=bytes in response + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2RecvAll(ProtoHttp2RefT *pState, int32_t iStreamId, uint8_t *pBuffer, int32_t iBufSize) +{ + StreamInfoT *pStreamInfo; + int32_t iRecvMax = iBufSize-1, iRecvResult = PROTOHTTP2_RECVFAIL; + + if ((pStreamInfo = _ProtoHttp2StreamInfoGet(pState, iStreamId)) != NULL) + { + NetCritEnter(&pState->HttpCrit); + // try to receive as much as possible, adding to amount received + while ((iRecvResult = _ProtoHttp2Read(pState, pStreamInfo, pBuffer+pStreamInfo->iRecvSize, 1, iRecvMax-pStreamInfo->iRecvSize)) > 0) + { + pStreamInfo->iRecvSize += iRecvResult; + } + NetCritLeave(&pState->HttpCrit); + } + + // check the response code + if (iRecvResult == PROTOHTTP2_RECVDONE) + { + pBuffer[pStreamInfo->iRecvSize] = 0; + iRecvResult = pStreamInfo->iRecvSize; + } + else if ((iRecvResult < 0) && (iRecvResult != PROTOHTTP2_RECVWAIT)) + { + // an error occured + NetPrintf(("protohttp2: [%p] error %d receiving response\n", pState, iRecvResult)); + } + else if (iRecvResult == 0) + { + iRecvResult = (pStreamInfo->iRecvSize < iRecvMax) ? PROTOHTTP2_RECVWAIT : PROTOHTTP2_RECVBUFF; + } + + // return result to caller + return(iRecvResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Abort + + \Description + Abort current operation, if any. + + \Input *pState - module state + \Input iStreamId - identifier of the stream to cancel + + \Notes + This will send a RST_STREAM frame to the peer endpoint with the + CANCEL (0x8) error code + + \Version 11/03/2016 (eesponda) +*/ +/********************************************************************************F*/ +void ProtoHttp2Abort(ProtoHttp2RefT *pState, int32_t iStreamId) +{ + StreamInfoT *pStreamInfo; + + // find stream id and make sure the stream is open to allow us to send data + if (((pStreamInfo = _ProtoHttp2StreamInfoGet(pState, iStreamId)) != NULL) && (pStreamInfo->eState > STREAMSTATE_IDLE) && (pStreamInfo->eState < STREAMSTATE_CLOSED)) + { + NetCritEnter(&pState->HttpCrit); + _ProtoHttp2PrepareRstStream(pState, pStreamInfo, ERRORTYPE_CANCEL, "cancelling the stream"); + NetCritLeave(&pState->HttpCrit); + } +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Close + + \Description + Close the connection to the server + + \Input *pState - module state + + \Notes + This will send a GOAWAY frame to the peer endpoint with the + NO_ERROR (0x0) error code + + \Version 11/22/2016 (eesponda) +*/ +/********************************************************************************F*/ +void ProtoHttp2Close(ProtoHttp2RefT *pState) +{ + // if not connected then nothing left to do + if ((pState->eState == ST_IDLE) || (pState->eState == ST_FAIL)) + { + return; + } + + _ProtoHttp2SendGoAway(pState, ERRORTYPE_NO_ERROR, "user request"); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Status + + \Description + Get status information + + \Input *pState - module state + \Input iStreamId - stream identifier used by certain selectors + \Input iSelect - info selector (see Notes) + \Input *pBuffer - [out] storage for selector-specific output + \Input iBufSize - size of buffer + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR RETURN RESULT + 'body' negative=failed or pending, else size of body (for 64bit size, get via pBuffer) + 'code' negative=no response, else server response code (ProtoHttpResponseE) + 'date' returns last-modified data, if available + 'done' returns status of request negative=failed, zero=pending or positive=done + 'essl' returns protossl error state + 'head' returns header size negative=failed or pending, otherwise header size + 'host' current host copied to output buffer + 'hres' returns hResult containing either the socket error, ssl error, or http status code + 'htxt' response header for stream identified by iStreamId via output buffer + 'imax' returns size of input buffer + 'nstm' returns the number of active streams + 'port' returns the current port + 'rtxt' most http request header text copied to output buffer for stream + 'rtyp' returns the request type for the stream + 'strm' returns the state of the stream identified by iStreamId as ProtoHttp2StreamStateE + 'time' TRUE if the client timed out the connection, else FALSE + \endverbatim + + Unhandled selectors are passed on to ProtoSSL. + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2Status(ProtoHttp2RefT *pState, int32_t iStreamId, int32_t iSelect, void *pBuffer, int32_t iBufSize) +{ + const StreamInfoT *pStreamInfo; + + // return protossl error state (we cache this since we reset the state when we disconnect an error) + if (iSelect == 'essl') + { + return(pState->iSslFail); + } + // return current host + if (iSelect == 'host') + { + ds_strnzcpy(pBuffer, pState->strHost, iBufSize); + return(0); + } + // return hresult containing either the socket error, ssl error or http status code + if (iSelect == 'hres') + { + // validate stream id, stream information and status code + if ((iStreamId > 0) && ((pStreamInfo = _ProtoHttp2StreamInfoGet(pState, iStreamId)) != NULL) && (pStreamInfo->eResponseCode > 0)) + { + return(DirtyErrGetHResult(DIRTYAPI_PROTO_HTTP, pStreamInfo->eResponseCode, (pStreamInfo->eResponseCode >= PROTOHTTP_RESPONSE_CLIENTERROR) ? TRUE : FALSE)); + } + else + { + return(pState->iHresult); + } + } + // return size of input buffer + if (iSelect == 'imax') + { + return(pState->iInpMax); + } + // return number of active streams + if (iSelect == 'nstm') + { + return(pState->iNumStreams); + } + // return current port + if (iSelect == 'port') + { + return(pState->iPort); + } + // return timeout indicator + if (iSelect == 'time') + { + return(pState->bTimeout); + } + + // attempt to get the stream information for the below selectors where valid stream is required + if ((iStreamId <= 0) || ((pStreamInfo = _ProtoHttp2StreamInfoGet(pState, iStreamId)) == NULL)) + { + // pass down to unhandled selectors with no stream specified to ProtoSSL + return(ProtoSSLStat(pState->pSsl, iSelect, pBuffer, iBufSize)); + } + + // return response code + if (iSelect == 'code') + { + return(pStreamInfo->eResponseCode); + } + // return the header data + if (iSelect == 'date') + { + return(pStreamInfo->iHdrDate); + } + // done check: negative=failed, zero=pending, positive=done + if (iSelect == 'done') + { + if (pState->eState == ST_FAIL) + { + return(-1); + } + if (pStreamInfo->eState == STREAMSTATE_CLOSED) + { + return(1); + } + return(0); + } + // return the request header text + if (iSelect == 'rtxt') + { + ds_strnzcpy(pBuffer, pStreamInfo->strRequestHeader, iBufSize); + return(0); + } + // return the request type + if (iSelect == 'rtyp') + { + return(pStreamInfo->eRequestType); + } + // return the current state of the stream + if (iSelect == 'strm') + { + return(pStreamInfo->eState); + } + + /* check the state: + if failure then nothing is happening, thus nothing left to do + otherwise if we have yet to receive our headers then the remaining data has + not been filled out yet */ + if (pState->eState == ST_FAIL) + { + return(-1); + } + if (pStreamInfo->eResponseCode == PROTOHTTP_RESPONSE_PENDING) + { + return(-2); + } + + // negative=failed or pending, else size of body (for 64bit size, get via pBuffer) + if (iSelect == 'body') + { + if ((pBuffer != NULL) && (iBufSize == sizeof(pStreamInfo->iBodySize))) + { + ds_memcpy(pBuffer, &pStreamInfo->iBodySize, iBufSize); + } + return((int32_t)pStreamInfo->iBodySize); + } + // return size of the header + if (iSelect == 'head') + { + return(pStreamInfo->iHeaderLen); + } + // return the response header text + if (iSelect == 'htxt') + { + ds_strnzcpy(pBuffer, pStreamInfo->pHeader, iBufSize); + return(0); + } + + // unhandled selector + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2Control + + \Description + ProtoHttp2 control function. Different selectors control different behaviors. + + \Input *pState - module state + \Input iStreamId - stream identifier used by certain controls + \Input iControl - control selector (see Notes) + \Input iValue - control specific + \Input iValue2 - control specific + \Input *pValue - control specific + + \Output + int32_t - control specific + + \Notes + Selectors are: + + \verbatim + SELECTOR DESCRIPTION + 'apnd' The given buffer will be appended to future headers sent + by ProtoHttp2. Note that the User-Agent and Accept lines + in the default header will be replaced, so if these lines + are desired, they should be supplied to the append header. + 'huff' Sets the use of huffman encoding for strings (default=TRUE) + 'ires' Resize input buffer + 'spam' Sets debug output verbosity (0..n) + 'time' Sets ProtoHttp client timeout in milliseconds (default=30s) + \endverbatim + + Unhandled selectors are passed on to ProtoSSL. + + \Version 09/27/2016 (eesponda) +*/ +/*******************************************************************************F*/ +int32_t ProtoHttp2Control(ProtoHttp2RefT *pState, int32_t iStreamId, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'apnd') + { + return(_ProtoHttp2SetAppendHeader(pState, (const char *)pValue)); + } + if (iControl == 'huff') + { + pState->bHuffman = (uint8_t)iValue; + return(0); + } + if (iControl == 'ires') + { + // clamp the input buffer size + iValue = PROTOHTTP2_ClampFrameSize(iValue); + + // attempt to resize the buffer if necessary + if (((uint32_t)iValue != pState->TempSettings.uMaxFrameSize) && (_ProtoHttp2ResizeInputBuffer(pState, iValue+PROTOHTTP2_RESERVED_SIZE) == 0)) + { + // set the temp settings to be sent to our peer + pState->TempSettings.uMaxFrameSize = (uint32_t)iValue; + return(0); + } + return(-1); + } + if (iControl == 'spam') + { + pState->iVerbose = iValue; + // fall through to protossl + } + if (iControl == 'time') + { + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] setting timeout to %d ms\n", pState, iValue)); + pState->uTimeout = (unsigned)iValue; + return(0); + } + + // unhandled control, fallthrough to protossl + return(ProtoSSLControl(pState->pSsl, iControl, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2SetBaseUrl + + \Description + Set base url that will be used for any relative url references. + + \Input *pState - module state + \Input *pUrl - base url + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +void ProtoHttp2SetBaseUrl(ProtoHttp2RefT *pState, const char *pUrl) +{ + char strKind[8]; + uint8_t bPortSpecified; + + // parse the url for kind, host and port + ProtoHttpUrlParse2(pUrl, strKind, sizeof(strKind), pState->strBaseHost, sizeof(pState->strBaseHost), + &pState->iBasePort, &pState->bBaseSecure, &bPortSpecified); + + NetPrintfVerbose((pState->iVerbose, 1, "protohttp2: [%p] setting base url to %s://%s:%d\n", + pState, pState->bBaseSecure ? "https" : "http", pState->strBaseHost, pState->iBasePort)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2GetLocationHeader + + \Description + Get location header from the input buffer. The Location header requires + some special processing. + + \Input *pState - reference pointer + \Input *pInpBuf - buffer holding header text + \Input *pBuffer - [out] output buffer for parsed location header, null for size request + \Input iBufSize - size of output buffer, zero for size request + \Input **pHdrEnd- [out] pointer past end of parsed header (optional) + + \Output + int32_t - negative=not found or not enough space, zero=success, positive=size query result + + \Version 09/27/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttp2GetLocationHeader(ProtoHttp2RefT *pState, const char *pInpBuf, char *pBuffer, int32_t iBufSize, const char **pHdrEnd) +{ + const char *pLocHdr; + int32_t iLocLen, iLocPreLen=0; + + // get a pointer to header + if ((pLocHdr = ProtoHttpFindHeaderValue(pInpBuf, "location")) == NULL) + { + return(-1); + } + + /* according to RFC location headers should be absolute, but some webservers respond with relative + URL's. we assume any url that does not include "://" is a relative url, and if we find one, we + assume the request keeps the same security, port, and host as the previous request */ + if ((pState != NULL) && (!strstr(pLocHdr, "://"))) + { + char strTemp[288]; // space for max DNS name (253 chars) plus max http url prefix + + // format http url prefix + if ((pState->bSecure && (pState->iPort == 443)) || (pState->iPort == 80)) + { + ds_snzprintf(strTemp, sizeof(strTemp), "%s://%s", pState->bSecure ? "https" : "http", pState->strHost); + } + else + { + ds_snzprintf(strTemp, sizeof(strTemp), "%s://%s:%d", pState->bSecure ? "https" : "http", pState->strHost, pState->iPort); + } + + // make sure relative URL includes '/' as the first character, required when we format the redirection url + if (*pLocHdr != '/') + { + ds_strnzcat(strTemp, "/", sizeof(strTemp)); + } + + // calculate url prefix length + iLocPreLen = (int32_t)strlen(strTemp); + + // copy url prefix text if a buffer is specified + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, strTemp, iBufSize); + pBuffer = (char *)((uint8_t *)pBuffer + iLocPreLen); + iBufSize -= iLocPreLen; + } + } + + // extract location header and return size + iLocLen = ProtoHttpExtractHeaderValue(pLocHdr, pBuffer, iBufSize, pHdrEnd); + // if it's a size request add in (possible) url header length + if ((pBuffer == NULL) && (iBufSize == 0)) + { + iLocLen += iLocPreLen; + } + + // return to caller + return(iLocLen); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttp2StreamFree + + \Description + Removes the stream information for the list by identifier + + \Input *pState - module state + \Input iStreamId - identifier of the stream info to remove + + \Version 11/01/2016 (eesponda) +*/ +/********************************************************************************F*/ +void ProtoHttp2StreamFree(ProtoHttp2RefT *pState, int32_t iStreamId) +{ + StreamInfoT *pStreamInfo; + + if ((pStreamInfo = _ProtoHttp2StreamInfoGet(pState, iStreamId)) != NULL) + { + // get index based on pointer + int32_t iStream = (int32_t)(pStreamInfo - pState->Streams); + + #if DIRTYCODE_LOGGING + if (pStreamInfo->eState != STREAMSTATE_CLOSED) + { + NetPrintf(("protohttp2: [%p] warning: freeing stream state for a stream that is not yet closed\n", pState)); + } + #endif + + // cleanup any dynamic memory + _ProtoHttp2StreamInfoCleanup(pState, pStreamInfo); + + // remove entry from stream list + if (iStream != (pState->iNumStreams-1)) + { + int32_t iNumMove = (pState->iNumStreams-1) - iStream; + + // move the stream info to remove the gap + memmove(pStreamInfo, pStreamInfo+1, iNumMove * sizeof(*pStreamInfo)); + } + // decrement count + pState->iNumStreams -= 1; + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protohttpmanager.c b/r5dev/thirdparty/dirtysdk/source/proto/protohttpmanager.c new file mode 100644 index 00000000..23ec48c5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protohttpmanager.c @@ -0,0 +1,2773 @@ +/*H********************************************************************************/ +/*! + \File protohttpmanager.c + + \Description + High-level module designed to create and manage a pool of ProtoHttp refs. A + client application can submit rapid-fire http requests and ProtoHttpManager + will distribute them efficiently across the ref pool internally, queuing + them for efficient use of keep-alive and pipelining requests where possible. + + \Notes + Pipelining resources: + + http://www.mozilla.org/projects/netlib/http/pipelining-faq.html + http://www.w3.org/Protocols/HTTP/Performance/Pipeline.html + + \Todo + Validate POST support + Pipelining: + - handle fewer responses than expected (?) + + \Copyright + Copyright (c) Electronic Arts 2009-2011. + + \Version 1.0 05/20/2009 (jbrookes) First Version +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protohttpmanager.h" + +/*** Defines **********************************************************************/ + +//! maximum number of protohttp refs that can be allocated +#ifdef DIRTYCODE_NX +#define HTTPMANAGER_MAXREFS (48) +#else +#define HTTPMANAGER_MAXREFS (64) +#endif + +//! maximum number of protohttp commands that can be queued for execution +#define HTTPMANAGER_MAXCMDS (256) + +//! maximum number of protohttp commands that can be queued in a given httpref +#define HTTPMANAGER_MAXREFQUEUE (16) + +//! define for final-mode diagnostic printing +#define HTTPMANAGER_FINALDEBUG (!DIRTYCODE_DEBUG && FALSE) + +#if HTTPMANAGER_FINALDEBUG +#if defined(DIRTYCODE_PC) +#include // OutputDebugStringA +#else +#include +#endif +#endif + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef enum HttpManagerHttpCmdStateE +{ + HTTPMANAGER_CMDSTATE_IDLE = 0, //!< unallocated + HTTPMANAGER_CMDSTATE_WAIT, //!< queued and waiting to start + HTTPMANAGER_CMDSTATE_PIPE, //!< pipelined + HTTPMANAGER_CMDSTATE_ACTV, //!< active + HTTPMANAGER_CMDSTATE_DONE, //!< complete + HTTPMANAGER_CMDSTATE_FAIL //!< queued execution of command failed +} HttpManagerHttpCmdStateE; + +typedef enum HttpManagerHttpRefStateE +{ + HTTPMANAGER_REFSTATE_NONE = 0, //!< no ref available + HTTPMANAGER_REFSTATE_IDLE, //!< ref is available + HTTPMANAGER_REFSTATE_BUSY, //!< ref is in use +} HttpManagerHttpRefStateE; + +//! http ref info +typedef struct HttpManagerHttpRefT +{ + ProtoHttpRefT *pProtoHttp; //!< http ref + struct HttpManagerHttpCmdT *HttpCmdQueue[HTTPMANAGER_MAXREFQUEUE]; //!< http command queue + uint32_t uLastTick; //!< last timestamp ref was accessed + uint8_t uHttpState; //!< http ref state + int8_t iTransactions; //!< number of transactions queued + int8_t iCurTransaction; //!< current transaction counter (used for callbacks) + int8_t _pad; +} HttpManagerHttpRefT; + +//! http cmd info +typedef struct HttpManagerHttpCmdT +{ + HttpManagerRefT *pHttpManager; //!< HttpManager ref, required by ProtoHttp callback + HttpManagerHttpRefT *pHttpRef; //!< pointer to http ref used for this transaction + int32_t iHttpHandle; //!< handle associated with this transaction (zero means unallocated) + int32_t iTimeout; //!< timeout to set for http ref (zero=do not set) + void *pCallbackRef; //!< user callback ref + ProtoHttpWriteCbT *pWriteCb; //!< write callback + ProtoHttpCustomHeaderCbT *pCustomHeaderCb; //!< custom header callback + ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb; //!< receive header callback + void *pUserData; //!< callback user data + const char *pUrl; //!< request url + char *pAppendHdr; //!< append header (optional) + int32_t iResult; //!< transaction result + uint32_t uQueueTick; //!< tick request was queued at + uint32_t uIssueTick; //!< tick request was issued at + uint32_t uComplTick; //!< tick request was completed at + uint64_t uBytesXfer; //!< bytes transferred + uint8_t uRequestType; //!< ProtoHttpRequestTypeE + uint8_t uState; //!< transaction state + uint8_t bKeepAlive; //!< TRUE if this command was queued with keep-alive + uint8_t bCopiedUrl; //!< TRUE if the url was copied +} HttpManagerHttpCmdT; + +//! http module state +struct HttpManagerRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData;//!< user data associated with mem group + + // user callback info + ProtoHttpCustomHeaderCbT *pCustomHeaderCb; //!< callback for modifying request header + ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb; //!< callback for viewing recv header on recepit + + //! current handle + int32_t iHttpHandle; + + //! module debuglevel + int32_t iVerbose; + + //! transaction stats + HttpManagerStatT HttpManagerStats; + + // module configuration variables + uint8_t bPipelining; //!< pipelining enable/disable + uint8_t bKeepalive; //!< keep-alive enable/disable + uint8_t bPipeWithoutKeepAlive; //!< if TRUE, will pipeline without a prior keep-alive connection + uint8_t bCopyUrl; //!< if TRUE, alloc memory for and copy url; else rely on caller to persist + int8_t iMaxPipedUrls; //!< maximum number of Urls that may be piped in a single request + uint8_t bAutoUpdate; //!< TRUE if auto-update enabled (default), else FALSE + uint8_t bXhttpEnabled; //!< XHTTP enabled (Xbox 360 only) + uint8_t _pad; + + //! number of available http refs + int32_t iHttpNumRefs; + //! protohttp buffer size + int32_t iHttpBufSize; + + //! global append header (optional) + char *pAppendHdr; + + //! protohttp module pool + HttpManagerHttpRefT HttpRefs[HTTPMANAGER_MAXREFS]; + + //! protohttp command pool + HttpManagerHttpCmdT HttpCmds[HTTPMANAGER_MAXCMDS]; +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private variables + +#if HTTPMANAGER_FINALDEBUG +int32_t _HttpManager_iStartTick = 0; +#endif + +// Public variables + + +/*** Private Functions ************************************************************/ + +#if HTTPMANAGER_FINALDEBUG +/*F********************************************************************************/ +/*! + \Function _HttpManagerPrintfCode + + \Description + Special final-mode PC debugging hook to display netprintf debug output. + + \Input *pFormat - format string + \Input ... - variable-length arg list + + \Output + int32_t - zero + + \Version 05/22/2009 (jbrookes) +*/ +/********************************************************************************F*/ +#undef NetPrintf +#define NetPrintf(_x) _HttpManagerPrintfCode _x +static int32_t _HttpManagerPrintfCode(const char *pFormat, ...) +{ + va_list pFmtArgs; + char strText[4096]; + char strTick[16]; + const char *pText = strText; + + va_start(pFmtArgs, pFormat); + // check for simple formatting + if ((pFormat[0] == '%') && (pFormat[1] == 's') && (pFormat[2] == 0)) + { + pText = va_arg(pFmtArgs, const char *); + } + else + { + vsprintf(strText, pFormat, pFmtArgs); + } + va_end(pFmtArgs); + + ds_snzprintf(strTick, sizeof(strTick), "[%d] ", NetTick()-_HttpManager_iStartTick); + #if defined(DIRTYCODE_PC) + OutputDebugStringA(strTick); + OutputDebugStringA(pText); + #else + printf("%s%s", strTick, pText); + #endif + return(0); +} +#endif + +#if HTTPMANAGER_FINALDEBUG +/*F********************************************************************************/ +/*! + \Function _HttpManagerPrintfVerboseCode + + \Description + Special final-mode PC debugging hook to display netprintfverbose debug output. + + \Input iVerbosityLevel - module verbosity level + \Input iCheckLevel - verbosity to check against for this statement + \Input *pFormat - format string + \Input ... - variable-length arg list + + \Output + int32_t - zero + + \Version 05/26/2009 (jbrookes) +*/ +/********************************************************************************F*/ +#undef NetPrintfVerbose +#define NetPrintfVerbose(_x) _HttpManagerPrintfVerboseCode _x +static int32_t _HttpManagerPrintfVerboseCode(int32_t iVerbosityLevel, int32_t iCheckLevel, const char *pFormat, ...) +{ + va_list pFmtArgs; + char strText[4096]; + char strTick[16]; + const char *pText = strText; + + va_start(pFmtArgs, pFormat); + // check for simple formatting + if ((pFormat[0] == '%') && (pFormat[1] == 's') && (pFormat[2] == 0)) + { + pText = va_arg(pFmtArgs, const char *); + } + else + { + vsprintf(strText, pFormat, pFmtArgs); + } + va_end(pFmtArgs); + + if (iCheckLevel < iVerbosityLevel) + { + ds_snzprintf(strTick, sizeof(strTick), "[%d] ", NetTick()-_HttpManager_iStartTick); + #if defined(DIRTYCODE_PC) + OutputDebugStringA(strTick); + OutputDebugStringA(pText); + #else + printf("%s%s", strTick, pText); + #endif + } + return(0); +} +#endif + +#if DIRTYCODE_LOGGING || HTTPMANAGER_FINALDEBUG +/*F********************************************************************************/ +/*! + \Function _HttpManagerDisplayStats + + \Description + Display transfer stats for module. + + \Input *pHttpManager - httpmanager state + + \Version 02/21/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerDisplayStats(HttpManagerRefT *pHttpManager) +{ + // display stats + NetPrintf(("httpmanager: transactions: %d\n", pHttpManager->HttpManagerStats.uNumTransactions)); + if (pHttpManager->HttpManagerStats.uNumTransactions > 0) + { + NetPrintf(("httpmanager: keepalive transactions: %d\n", pHttpManager->HttpManagerStats.uNumKeepAliveTransactions)); + NetPrintf(("httpmanager: pipelined transactions: %d\n", pHttpManager->HttpManagerStats.uNumPipelinedTransactions)); + NetPrintf(("httpmanager: max active transactions: %d\n", pHttpManager->HttpManagerStats.uMaxActiveTransactions)); + NetPrintf(("httpmanager: max queued transactions: %d\n", pHttpManager->HttpManagerStats.uMaxQueuedTransactions)); + NetPrintf(("httpmanager: sum queue wait time: %dms\n", pHttpManager->HttpManagerStats.uSumQueueWaitLatency)); + NetPrintf(("httpmanager: avg queue wait time: %dms\n", pHttpManager->HttpManagerStats.uSumQueueWaitLatency/pHttpManager->HttpManagerStats.uNumTransactions)); + NetPrintf(("httpmanager: max queue wait time: %dms\n", pHttpManager->HttpManagerStats.uMaxQueueWaitLatency)); + NetPrintf(("httpmanager: sum queue free time: %dms\n", pHttpManager->HttpManagerStats.uSumQueueFreeLatency)); + NetPrintf(("httpmanager: avg queue free time: %dms\n", pHttpManager->HttpManagerStats.uSumQueueFreeLatency/pHttpManager->HttpManagerStats.uNumTransactions)); + NetPrintf(("httpmanager: max queue free time: %dms\n", pHttpManager->HttpManagerStats.uMaxQueueFreeLatency)); + NetPrintf(("httpmanager: total bytes transferred: %qd\n", pHttpManager->HttpManagerStats.uTransactionBytes)); + NetPrintf(("httpmanager: total transaction time: %d\n", pHttpManager->HttpManagerStats.uTransactionTime)); + NetPrintf(("httpmanager: avg bytes per second %.2f\n", (float)pHttpManager->HttpManagerStats.uTransactionBytes*1000.0f/(float)pHttpManager->HttpManagerStats.uTransactionTime)); + NetPrintf(("httpmanager: avg transaction size %.2f\n", (float)pHttpManager->HttpManagerStats.uTransactionBytes/(float)pHttpManager->HttpManagerStats.uNumTransactions)); + } +} +#endif + +/*F********************************************************************************/ +/*! + \Function _HttpManagerResetStats + + \Description + Reset transfer stats for module. + + \Input *pHttpManager - httpmanager state + + \Version 07/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerResetStats(HttpManagerRefT *pHttpManager) +{ + ds_memclr(&pHttpManager->HttpManagerStats, sizeof(pHttpManager->HttpManagerStats)); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerUrlCompare + + \Description + Compares base elements of given Urls (kind, host, port, security) and + returns whether they are identical or not. + + \Input *pUrlA - Url to compare + \Input *pUrlB - Url to compare + + \Output + uint32_t - zero=same, else different + + \Version 07/01/2009 (jbrookes) First Version +*/ +/********************************************************************************F*/ +static uint32_t _HttpManagerUrlCompare(const char *pUrlA, const char *pUrlB) +{ + char strKindA[16], strKindB[16], strHostA[128], strHostB[128]; + int32_t iPortA, iPortB, iSecureA, iSecureB; + + ProtoHttpUrlParse(pUrlA, strKindA, sizeof(strKindA), strHostA, sizeof(strHostA), &iPortA, &iSecureA); + ProtoHttpUrlParse(pUrlB, strKindB, sizeof(strKindB), strHostB, sizeof(strHostB), &iPortB, &iSecureB); + + return(!((ds_stricmp(strKindA, strKindB) == 0) && (ds_stricmp(strHostA, strHostB) == 0) && (iPortA == iPortB) && (iSecureA == iSecureB))); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerPipelineCheck + + \Description + See if specified requests will pipeline. + + \Input *pHttpCmd - previously issued command + \Input *pHttpCmd2 - command to check for piping with previous command + + \Output + int32_t - zero=will not pipe; else will pipe + + \Version 07/15/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerPipelineCheck(HttpManagerHttpCmdT *pHttpCmd, HttpManagerHttpCmdT *pHttpCmd2) +{ + // make sure request can be pipelined + if ((pHttpCmd2->uRequestType != PROTOHTTP_REQUESTTYPE_GET) && (pHttpCmd2->uRequestType != PROTOHTTP_REQUESTTYPE_HEAD)) + { + return(0); + } + + // make sure urls will pipe + return(_HttpManagerUrlCompare(pHttpCmd->pUrl, pHttpCmd2->pUrl) == 0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerPipelineReset + + \Description + Reset pipe following a request failure of some kind or another with subsequent + piped results that need to be reissued. + + \Input *pHttpManager - reference pointer + \Input *pHttpRef - http ref piped transactions we need to consider for reset are queued on + \Input iHttpCmdFirst - first command to reset + + \Version 07/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerPipelineReset(HttpManagerRefT *pHttpManager, HttpManagerHttpRefT *pHttpRef, int32_t iHttpCmdFirst) +{ + HttpManagerHttpCmdT *pHttpCmd = NULL; + int32_t iHttpCmd; + + // reset commands for reissue + for (iHttpCmd = iHttpCmdFirst ; iHttpCmd < pHttpRef->iTransactions; iHttpCmd += 1) + { + pHttpCmd = pHttpRef->HttpCmdQueue[iHttpCmd]; + if ((pHttpCmd->uState == HTTPMANAGER_CMDSTATE_ACTV) || (pHttpCmd->uState == HTTPMANAGER_CMDSTATE_PIPE)) + { + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: reset handle %d for re-issue\n", pHttpCmd->iHttpHandle)); + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_WAIT; + pHttpCmd->bKeepAlive = FALSE; + } + else + { + break; + } + } + if ((iHttpCmd - iHttpCmdFirst) > 0) + { + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: reset %d requests on ref %2d for re-issue\n", iHttpCmd - iHttpCmdFirst, pHttpRef - pHttpCmd->pHttpManager->HttpRefs)); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerCustomHeaderCb + + \Description + Handle ProtoHttp custom header send callback, and pass it on to caller-registered + callback handler with handle-specific callback user data pointer. + + \Input *pState - protohttp state + \Input *pHeader - request header + \Input uHeaderSize - amount of space available for header (including current header text) + \Input *pData - data to be appended to header + \Input iDataLen - total size of data + \Input *pUserRef - HttpManagerHttpRefT for this request + + \Output + int32_t - user callback result + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerCustomHeaderCb(ProtoHttpRefT *pState, char *pHeader, uint32_t uHeaderSize, const char *pData, int64_t iDataLen, void *pUserRef) +{ + HttpManagerHttpRefT *pHttpRef = (HttpManagerHttpRefT *)pUserRef; + HttpManagerHttpCmdT *pHttpCmd = pHttpRef->HttpCmdQueue[pHttpRef->iCurTransaction]; + NetPrintfVerbose((pHttpCmd->pHttpManager->iVerbose, 1, "httpmanager: calling custom header callback with userref 0x%08x for handle %d\n", (uintptr_t)pHttpCmd->pCallbackRef, pHttpCmd->iHttpHandle)); + return(pHttpCmd->pHttpManager->pCustomHeaderCb(pState, pHeader, uHeaderSize, pData, iDataLen, pHttpCmd->pCallbackRef)); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerReceiveHeaderCb + + \Description + Handle ProtoHttp header receive callback, and pass it on to caller-registered + callback handler with handle-specific callback user data pointer. + + \Input *pState - protohttp state + \Input *pHeader - response header + \Input uHeaderSize - size of response header + \Input *pUserRef - HttpManagerHttpRefT for this request + + \Output + int32_t - user callback result + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerReceiveHeaderCb(ProtoHttpRefT *pState, const char *pHeader, uint32_t uHeaderSize, void *pUserRef) +{ + HttpManagerHttpRefT *pHttpRef = (HttpManagerHttpRefT *)pUserRef; + HttpManagerHttpCmdT *pHttpCmd = pHttpRef->HttpCmdQueue[pHttpRef->iCurTransaction]; + HttpManagerRefT *pHttpManager = pHttpCmd->pHttpManager; + int32_t iResult, iHttpCmd = -1; + ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb; + void *pUserData; + + /* early out if pHttpManager is null; fifa encountred an issue + In theory, if HttpManagerAlloc/HttpManagerFree are being called from different thread + than ProtoHttpUpdate. When HttpManagerFree is called it clears out a structure HttpManagerHttpCmdT that contains the + pointer to pHttpManager, it is then a race condition on when the header callback is called causing a crash. */ + if (pHttpManager == NULL) + { + NetPrintf(("httpmanager: skipping _HttpManagerReceiveHeaderCb because pHttpManager is null\n")); + return(0); + } + + // check for an error response, and handle if it will affect piped commands + iResult = ProtoHttpStatus(pState, 'code', NULL, 0); + if (PROTOHTTP_GetResponseClass(iResult) != PROTOHTTP_RESPONSE_SUCCESSFUL) + { + if (PROTOHTTP_GetResponseClass(iResult) == PROTOHTTP_RESPONSE_REDIRECTION) + { + char strLocation[1024]; + ProtoHttpGetLocationHeader(pState, pHeader, strLocation, sizeof(strLocation), NULL); + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] redirect ref %d url=%s (%d)\n", pHttpCmd->iHttpHandle, + pHttpCmd->pHttpRef - pHttpManager->HttpRefs, strLocation, iResult)); + } + else + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] redirect ref %d (%d)\n", pHttpCmd->iHttpHandle, + pHttpCmd->pHttpRef - pHttpManager->HttpRefs, iResult)); + } + + /* if this transaction timed out (from a keep-alive connection that was closed) mark it for reissue, + otherwise, we just look for subsequent piped results that need to be reissued */ + if (iResult == PROTOHTTP_RESPONSE_REQUESTTIMEOUT) + { + iHttpCmd = 0; + } + if (PROTOHTTP_GetResponseClass(iResult) == PROTOHTTP_RESPONSE_REDIRECTION) + { + iHttpCmd = 1; + } + } + + // check for loss of piped commands due to premature close + if (ProtoHttpStatus(pState, 'plst', NULL, 0) == TRUE) + { + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: piped commands lost due to premature close on handle %d\n", pHttpCmd->iHttpHandle)); + iHttpCmd = 1; + + // downgrade our pipelining support $$todo$$ - this should be tracked per site + if ((PROTOHTTP_GetResponseClass(iResult) != PROTOHTTP_RESPONSE_REDIRECTION) && (pHttpManager->bPipeWithoutKeepAlive)) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager; disabling pipelining without keep-alive\n")); + pHttpManager->bPipeWithoutKeepAlive = FALSE; + } + } + + // reset pipe if necessary + if (iHttpCmd != -1) + { + _HttpManagerPipelineReset(pHttpManager, pHttpRef, iHttpCmd); + } + + // if we have a 408 timeout response, reset ref to idle state, so we can reissue the request + if (iResult == PROTOHTTP_RESPONSE_REQUESTTIMEOUT) + { + pHttpRef->uHttpState = HTTPMANAGER_REFSTATE_IDLE; + // since we are going to re-issue this request, we do not forward the 408 response on to the application + return(0); + } + + // request level callback takes priority to global + if ((pReceiveHeaderCb = pHttpCmd->pReceiveHeaderCb) != NULL) + { + pUserData = pHttpCmd->pUserData; + } + else + { + pReceiveHeaderCb = pHttpManager->pReceiveHeaderCb; + pUserData = pHttpCmd->pCallbackRef; + } + + // forward to caller's receive callback, if installed + if (pReceiveHeaderCb != NULL) + { + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: calling receive header callback with userref 0x%08x for handle %d\n", (uintptr_t)pUserData, pHttpCmd->iHttpHandle)); + return(pReceiveHeaderCb(pState, pHeader, uHeaderSize, pUserData)); + } + else + { + return(0); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerWriteCbProcess + + \Description + User write callback processing, if write callback is set + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - http cmd + + \Version 07/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerWriteCbProcess(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd) +{ + ProtoHttpWriteCbInfoT WriteCbInfo; + char strTempRecv[1024]; + int32_t iResult; + + ds_memclr(&WriteCbInfo, sizeof(WriteCbInfo)); + WriteCbInfo.eRequestType = (ProtoHttpRequestTypeE)pHttpCmd->uRequestType; + WriteCbInfo.eRequestResponse = PROTOHTTP_RESPONSE_PENDING; + + while ((iResult = HttpManagerRecv(pHttpManager, pHttpCmd->iHttpHandle, strTempRecv, 1, sizeof (strTempRecv))) > 0) + { + pHttpCmd->pWriteCb(pHttpCmd->pHttpRef->pProtoHttp, &WriteCbInfo, strTempRecv, iResult, pHttpCmd->pUserData); + } + + if ((iResult == PROTOHTTP_RECVDONE) || (iResult == PROTOHTTP_RECVHEAD) || (iResult == PROTOHTTP_RECVFAIL)) + { + if (iResult != PROTOHTTP_RECVFAIL) + { + WriteCbInfo.eRequestResponse = (ProtoHttpResponseE)ProtoHttpStatus(pHttpCmd->pHttpRef->pProtoHttp, 'code', NULL, 0); + pHttpCmd->pWriteCb(pHttpCmd->pHttpRef->pProtoHttp, &WriteCbInfo, "", iResult, pHttpCmd->pUserData); + } + else + { + pHttpCmd->pWriteCb(pHttpCmd->pHttpRef->pProtoHttp, &WriteCbInfo, "", PROTOHTTP_RECVFAIL, pHttpCmd->pUserData); + } + pHttpCmd->pWriteCb = NULL; + pHttpCmd->pCustomHeaderCb = NULL; + pHttpCmd->pReceiveHeaderCb = NULL; + pHttpCmd->pUserData = NULL; + } + + +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerSetAppendHeader + + \Description + Set append header, either on a global basis (pHttpCmd=NULL) or for a + specific HTTP command. + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - cmd to set append header for, or NULL to set global append header + \Input *pAppendHdr - append header to set + + \Output + int32_t - negative=failure, else success + + \Version 07/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerSetAppendHeader(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd, const char *pAppendHdr) +{ + char **ppAppendHdr; + + // point to append header we are operating on + ppAppendHdr = (pHttpCmd != NULL) ? &pHttpCmd->pAppendHdr : &pHttpManager->pAppendHdr; + + // if there is a previous append header, free it + if (*ppAppendHdr != NULL) + { + DirtyMemFree(*ppAppendHdr, HTTPMGR_MEMID, pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData); + *ppAppendHdr = NULL; + } + // set new append header, if specified + if ((pAppendHdr != NULL) && (*pAppendHdr != '\0')) + { + int32_t iHdrLen = (int32_t)strlen(pAppendHdr); + // allocate memory to buffer append header + if ((*ppAppendHdr = DirtyMemAlloc(iHdrLen+1, HTTPMGR_MEMID, pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData)) == NULL) + { + NetPrintf(("httpmanager: could not allocate %d bytes for append header for handle %d\n", iHdrLen+1, (pHttpCmd != NULL) ? pHttpCmd->iHttpHandle: -1)); + return(-1); + } + // copy to append header + ds_strnzcpy(*ppAppendHdr, pAppendHdr, iHdrLen+1); + } + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerApplyAppendHeader + + \Description + Applies append header to the specified command. The append header set will + be the command-specific header if set, else it will be the global append + header if set. If neither is set, the append header is cleared. + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - cmd to get append header for + + \Version 07/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerApplyAppendHeader(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd) +{ + char *pAppendHdr = pHttpCmd->pAppendHdr; + if (pAppendHdr == NULL) + { + pAppendHdr = pHttpManager->pAppendHdr; + } + ProtoHttpControl(pHttpCmd->pHttpRef->pProtoHttp, 'apnd', 0, 0, pAppendHdr); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerAllocHandle + + \Description + Allocate a new handle. Valid handles range from [1...7ffffff]. + + \Input *pHttpManager - reference pointer + + \Output + int32_t - new handle + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerAllocHandle(HttpManagerRefT *pHttpManager) +{ + int32_t iHttpHandle = pHttpManager->iHttpHandle; + pHttpManager->iHttpHandle = (pHttpManager->iHttpHandle + 1) & 0x7fffffff; + return(iHttpHandle); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerAllocCmd + + \Description + Allocate a new command. A command represents a single HTTP transaction request. + + \Input *pHttpManager - reference pointer + + \Output + HttpManagerHttpCmdT * - new command + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static HttpManagerHttpCmdT *_HttpManagerAllocCmd(HttpManagerRefT *pHttpManager) +{ + HttpManagerHttpCmdT *pHttpCmd; + int32_t iHttpCmd; + + for (iHttpCmd = 0, pHttpCmd = NULL; iHttpCmd < HTTPMANAGER_MAXCMDS; iHttpCmd += 1) + { + if (pHttpManager->HttpCmds[iHttpCmd].iHttpHandle == 0) + { + pHttpCmd = &pHttpManager->HttpCmds[iHttpCmd]; + ds_memclr(pHttpCmd, sizeof(*pHttpCmd)); + pHttpCmd->pHttpManager = pHttpManager; + pHttpCmd->iHttpHandle = _HttpManagerAllocHandle(pHttpManager); + break; + } + } + return(pHttpCmd); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerAllocRef + + \Description + Allocate an http ref. An http ref represents a single ProtoHttp module. In + HttpManager, all ProtoHttp refs are created at startup; this function simply + finds an appropriate ref to use for the specified transaction request. + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - command + + \Output + HttpManagerHttpRefT * - new command + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static HttpManagerHttpRefT *_HttpManagerAllocRef(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd) +{ + HttpManagerHttpRefT *pHttpRef, *pAllocRef = NULL; + HttpManagerHttpCmdT *pHttpPipeCmd; + int32_t iHttpCmd, iHttpRef; + char strKind[16], strHost[128]; + int32_t iPort, iSecure; + + // parse Url into component parts + ProtoHttpUrlParse(pHttpCmd->pUrl, strKind, sizeof(strKind), strHost, sizeof(strHost), &iPort, &iSecure); + + // first pass, we try to find a ref we can pipeline this request on + if (pHttpManager->bPipelining) + { + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + // get ref + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + + // iterate through queued transactions, if any + for (iHttpCmd = 0; iHttpCmd < pHttpRef->iTransactions; iHttpCmd += 1) + { + // ref http command we might be able to pipe against + pHttpPipeCmd = pHttpRef->HttpCmdQueue[iHttpCmd]; + if ((pHttpPipeCmd->uState == HTTPMANAGER_CMDSTATE_WAIT) && ((pHttpPipeCmd->bKeepAlive == TRUE) || (pHttpManager->bPipeWithoutKeepAlive == TRUE))) + { + if (_HttpManagerPipelineCheck(pHttpPipeCmd, pHttpCmd) && (pHttpRef->iTransactions < HTTPMANAGER_MAXREFQUEUE)) + { + if (iHttpCmd == (pHttpRef->iTransactions - 1)) + { + pAllocRef = pHttpRef; + pHttpCmd->bKeepAlive = TRUE; + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] queued ref %d count=%d pipe=1 url=%s\n", pHttpCmd->iHttpHandle, iHttpRef, pAllocRef->iTransactions, pHttpCmd->pUrl)); + break; + } + } + } + } + } + } + + // if not pipelined + if (pAllocRef == NULL) + { + uint8_t aKeepAliveState[HTTPMANAGER_MAXREFS]; + int32_t iBestMatchIdx, iBestMatchTick, iHttpRefTick, iCurTick; + int32_t iQueueDepth; + + // build list of keep-alive matches (whether a ref will try keep-alive with the specified Url) for all http refs + ds_memclr(aKeepAliveState, sizeof(aKeepAliveState)); + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + if ((pHttpRef->uHttpState != HTTPMANAGER_REFSTATE_NONE) && ProtoHttpCheckKeepAlive(pHttpRef->pProtoHttp, pHttpCmd->pUrl)) + { + aKeepAliveState[iHttpRef] = TRUE; + } + } + + // iterate through ref list in order from least heavily loaded (queue size) to most heavily loaded + for (iQueueDepth = 0; (pAllocRef == NULL) && (iQueueDepth < HTTPMANAGER_MAXREFQUEUE); iQueueDepth += 1) + { + // first, try to find a ref at this depth with keep-alive potential + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + if ((pHttpRef->iTransactions == iQueueDepth) && (aKeepAliveState[iHttpRef] == TRUE)) + { + // we have a match + pAllocRef = pHttpRef; + pHttpCmd->bKeepAlive = TRUE; + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] queued ref %d count=%d keep=1 url=%s\n", pHttpCmd->iHttpHandle, iHttpRef, pAllocRef->iTransactions, pHttpCmd->pUrl)); + break; + } + } + // did we find one? + if (pAllocRef != NULL) + { + break; + } + + // second pass, just look for the least recently used ref at this depth + for (iHttpRef = 0, iBestMatchIdx = -1, iBestMatchTick = -1, iCurTick = NetTick(); iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + iHttpRefTick = NetTickDiff(iCurTick, pHttpRef->uLastTick); + if ((pHttpRef->iTransactions == iQueueDepth) && (iHttpRefTick > iBestMatchTick)) + { + // consider this for a match + iBestMatchTick = iHttpRefTick; + iBestMatchIdx = iHttpRef; + } + } + // if we found a ref, use it + if (iBestMatchIdx >= 0) + { + pAllocRef = &pHttpManager->HttpRefs[iBestMatchIdx]; + pHttpCmd->bKeepAlive = FALSE; + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] queued ref %d count=%d keep=0 url=%s\n", pHttpCmd->iHttpHandle, iBestMatchIdx, pAllocRef->iTransactions, pHttpCmd->pUrl)); + break; + } + } + } + + // if we allocated a ref + if (pAllocRef != NULL) + { + // update transaction stats + if (pAllocRef->iTransactions > 0) + { + pHttpManager->HttpManagerStats.uNumQueuedTransactions += 1; + if (pHttpManager->HttpManagerStats.uMaxQueuedTransactions < pHttpManager->HttpManagerStats.uNumQueuedTransactions) + { + pHttpManager->HttpManagerStats.uMaxQueuedTransactions = pHttpManager->HttpManagerStats.uNumQueuedTransactions; + } + } + if (pHttpCmd->bKeepAlive) + { + pHttpManager->HttpManagerStats.uNumKeepAliveTransactions += 1; + } + + // bind ref to cmd + pHttpCmd->pHttpRef = pAllocRef; + // add command to ref queue + pHttpCmd->pHttpRef->HttpCmdQueue[pHttpCmd->pHttpRef->iTransactions++] = pHttpCmd; + // update last access time + pHttpCmd->pHttpRef->uLastTick = pHttpCmd->uQueueTick; + } + return(pAllocRef); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerGetCmd + + \Description + Find http command referenced by specified handle. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + + \Output + HttpManagerHttpCmdT * - requested command + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static HttpManagerHttpCmdT *_HttpManagerGetCmd(HttpManagerRefT *pHttpManager, int32_t iHandle) +{ + HttpManagerHttpCmdT *pHttpCmd; + int32_t iHttpCmd; + + // walk cmd list and find transaction with given handle + for (iHttpCmd = 0, pHttpCmd = NULL; iHttpCmd < HTTPMANAGER_MAXCMDS; iHttpCmd += 1) + { + if (pHttpManager->HttpCmds[iHttpCmd].iHttpHandle == iHandle) + { + pHttpCmd = &pHttpManager->HttpCmds[iHttpCmd]; + break; + } + } + return(pHttpCmd); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerUpdateTransactionStats + + \Description + Update stats based on a single transaction request. + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - transaction command + + \Version 05/25/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerUpdateTransactionStats(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd) +{ + uint32_t uLatency; + + pHttpManager->HttpManagerStats.uNumActiveTransactions += 1; + if (pHttpManager->HttpManagerStats.uMaxActiveTransactions < pHttpManager->HttpManagerStats.uNumActiveTransactions) + { + pHttpManager->HttpManagerStats.uMaxActiveTransactions = pHttpManager->HttpManagerStats.uNumActiveTransactions; + } + pHttpCmd->uIssueTick = NetTick(); + uLatency = NetTickDiff(pHttpCmd->uIssueTick, pHttpCmd->uQueueTick); + if (pHttpManager->HttpManagerStats.uMaxQueueWaitLatency < uLatency) + { + pHttpManager->HttpManagerStats.uMaxQueueWaitLatency = uLatency; + } + pHttpManager->HttpManagerStats.uSumQueueWaitLatency += uLatency; + pHttpManager->HttpManagerStats.uNumTransactions += 1; +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerResizeInputBuffer + + \Description + After PROTOHTTP_MINBUFF response, increase the input buffer size for the given + ProtoHttp ref to allow the request buffer space to be reissued successfully. + + \Input *pHttpManager - reference pointer + \Input *pHttpRef - http ref to resize + + \Output + int32_t - zero=success, else failure + + \Version 02/15/20011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerResizeInputBuffer(HttpManagerRefT *pHttpManager, HttpManagerHttpRefT *pHttpRef) +{ + int32_t iBuffSize, iCurrSize, iReqdSize; + + // get current and required sizes + if ((iCurrSize = ProtoHttpStatus(pHttpRef->pProtoHttp, 'imax', NULL, 0)) <= 0) + { + // something wrong with HTTP ref... can happen if HTTP ref is tromped, so we bail out here to prevent infinite loop below + NetPrintf(("httpmanager: ProtoHttpStats(0x%08x, 'imax') returned %d\n", (uintptr_t)pHttpRef->pProtoHttp, iCurrSize)); + return(-1); + } + iReqdSize = ProtoHttpStatus(pHttpRef->pProtoHttp, 'iovr', NULL, 0); + + // calc new buffer size in increments of original buffer size + for (iBuffSize = iCurrSize; iBuffSize < iReqdSize; iBuffSize += iCurrSize) + ; + + // resize the input buffer + return(ProtoHttpControl(pHttpRef->pProtoHttp, 'ires', iBuffSize, 0, NULL)); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestIssue + + \Description + Issue an HTTP request + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - transaction command + \Input *pData - data associated with command, or NULL if none + \Input iDataSize - size of data associated with command, or zero if none + + \Output + int32_t - ProtoHttpRequest() result + + \Version 05/25/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerRequestIssue(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd, const char *pData, int64_t iDataSize) +{ + int32_t iResult; + + // initiate the request, we only specify the custom header callback as the rest are implemented within this module + if ((iResult = ProtoHttpRequestCb2(pHttpCmd->pHttpRef->pProtoHttp, pHttpCmd->pUrl, pData, iDataSize, (ProtoHttpRequestTypeE)pHttpCmd->uRequestType, NULL, pHttpCmd->pCustomHeaderCb, NULL, pHttpCmd->pUserData)) < 0) + { + return(iResult); + } + + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] started ref %d url=%s\n", pHttpCmd->iHttpHandle, pHttpCmd->pHttpRef - pHttpManager->HttpRefs, pHttpCmd->pUrl)); + + // if pipelining is enabled, a ProtoHttpGet request with NULL Url is required to flush the previous request + if (pHttpManager->bPipelining && ((pHttpCmd->uRequestType == PROTOHTTP_REQUESTTYPE_HEAD) || (pHttpCmd->uRequestType == PROTOHTTP_REQUESTTYPE_GET))) + { + ProtoHttpGet(pHttpCmd->pHttpRef->pProtoHttp, NULL, 0); + } + + // update stats + _HttpManagerUpdateTransactionStats(pHttpManager, pHttpCmd); + + // update status + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_ACTV; + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestStart + + \Description + Set up any initial transaction-specific settings and issue a GET or HEAD + request. + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - transaction command + \Input *pData - pointer to URL data (optional, may be NULL) + \Input iDataSize - size of data being uploaded (see Notes) + + \Output + int32_t - ProtoHttpRequest() result + + \Version 05/21/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerRequestStart(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd, const char *pData, int64_t iDataSize) +{ + int32_t iResult; + + // make sure ref is idle + #if DIRTYCODE_LOGGING + if (pHttpCmd->pHttpRef->uHttpState != HTTPMANAGER_REFSTATE_IDLE) + { + NetPrintf(("httpmanager: error; get request issued on non-idle ref\n")); + } + #endif + + // explicitly disable keep-alive if global setting has it unavailable + if (pHttpManager->bKeepalive == FALSE) + { + ProtoHttpControl(pHttpCmd->pHttpRef->pProtoHttp, 'keep', 0, 0, NULL); + } + // set timeout if requested + if (pHttpCmd->iTimeout != 0) + { + ProtoHttpControl(pHttpCmd->pHttpRef->pProtoHttp, 'time', pHttpCmd->iTimeout, 0, NULL); + } + // apply append header + _HttpManagerApplyAppendHeader(pHttpManager, pHttpCmd); + + // update httpref status + pHttpCmd->pHttpRef->uHttpState = HTTPMANAGER_REFSTATE_BUSY; + pHttpCmd->pHttpRef->iCurTransaction = 0; + + // initiate the request + if ((iResult = _HttpManagerRequestIssue(pHttpManager, pHttpCmd, pData, iDataSize)) < 0) + { + // if there wasn't enough input buffer space to store the request, realloc the buffer and try again + if ((iResult == PROTOHTTP_MINBUFF) && (_HttpManagerResizeInputBuffer(pHttpManager, pHttpCmd->pHttpRef) == 0)) + { + // resized input buffer; try to issue the request again + iResult = _HttpManagerRequestIssue(pHttpManager, pHttpCmd, pData, iDataSize); + } + + // fail on error + if (iResult < 0) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: ProtoHttpRequest() returned %d on handle %d url=%s\n", iResult, pHttpCmd->iHttpHandle, pHttpCmd->pUrl)); + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_FAIL; + } + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestCheckCompletion + + \Description + Check for transaction completion, and update state and stats accordingly. + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - transaction command + + \Version 07/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerRequestCheckCompletion(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd) +{ + HttpManagerHttpRefT *pHttpRef = pHttpCmd->pHttpRef; + int32_t iHeadSize, iStatus; + #if DIRTYCODE_LOGGING + int32_t iRsltCode = 0; + #endif + + // are we done yet? + if ((iStatus = ProtoHttpStatus(pHttpRef->pProtoHttp, 'done', NULL, 0)) == 0) + { + // not done yet + return; + } + else if (iStatus == 1) + { + int64_t iBodySize; + ProtoHttpStatus(pHttpRef->pProtoHttp, 'body', &iBodySize, sizeof(iBodySize)); + if (iBodySize != (signed)pHttpCmd->uBytesXfer) + { + // transaction is complete, but data has not all been received by caller + return; + } + + #if DIRTYCODE_LOGGING + iRsltCode = ProtoHttpStatus(pHttpRef->pProtoHttp, 'code', NULL, 0); + #endif + } + + // remember completion time and mark transaction as complete + pHttpCmd->uComplTick = NetTick(); + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_DONE; + + // get size of header + if ((iHeadSize = ProtoHttpStatus(pHttpRef->pProtoHttp, 'head', NULL, 0)) < 0) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: could not get header size for stat tracking (err=%d)\n", iHeadSize)); + + // downgrade our pipelining support $$todo$$ - this should be tracked per site + if (pHttpManager->bPipeWithoutKeepAlive) + { + /*$$todo: this can happen for other reasons (e.g. cert untrusted on a secure connection) + but we should only be downgrading pipe without keep-alive if we get a generic socket + failure or a premature Connection: Close */ + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager; disabling pipelining without keep-alive\n")); + pHttpManager->bPipeWithoutKeepAlive = FALSE; + } + + // reset the pipeline + _HttpManagerPipelineReset(pHttpManager, pHttpRef, 1); + + iHeadSize = 0; + } + + // update transaction stats + pHttpCmd->uBytesXfer += (unsigned)iHeadSize; + pHttpManager->HttpManagerStats.uTransactionBytes += pHttpCmd->uBytesXfer; + pHttpManager->HttpManagerStats.uTransactionTime += NetTickDiff(pHttpCmd->uComplTick, pHttpCmd->uQueueTick); + + // log transaction results + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] %s ref %d url=%s (%d): %qd bytes in %dms, %.2fk/sec)\n", pHttpCmd->iHttpHandle, + (iStatus == 1) ? "complete" : "failed ", pHttpRef - pHttpManager->HttpRefs, pHttpCmd->pUrl, iRsltCode, pHttpCmd->uBytesXfer, NetTickDiff(pHttpCmd->uComplTick, pHttpCmd->uIssueTick), + ((float)pHttpCmd->uBytesXfer * 1000.0f) / (float)(NetTickDiff(pHttpCmd->uComplTick, pHttpCmd->uIssueTick)*1024))); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestGetCmdQueueIdx + + \Description + Get index of given command in httpref command queue. + + \Input *pHttpManager - reference pointer + \Input *pHttpRef - http ref + \Input *pHttpCmd - transaction command + + \Output + int32_t - index of command in command queue; -1 if not in queue + + \Version 07/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerRequestGetCmdQueueIdx(HttpManagerRefT *pHttpManager, HttpManagerHttpRefT *pHttpRef, HttpManagerHttpCmdT *pHttpCmd) +{ + int32_t iHttpCmd; + for (iHttpCmd = 0; (iHttpCmd < HTTPMANAGER_MAXREFQUEUE) && (pHttpRef->HttpCmdQueue[iHttpCmd] != pHttpCmd); iHttpCmd += 1) + ; + if (iHttpCmd == HTTPMANAGER_MAXREFQUEUE) + { + NetPrintf(("httpmanager: could not find handle %d in ref %d command queue!\n", pHttpCmd->iHttpHandle, pHttpRef-pHttpManager->HttpRefs)); + return(-1); + } + return(iHttpCmd); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestDelCmdFromQueue + + \Description + Remove command from command queue and update ref state accordingly + + \Input *pHttpManager - reference pointer + \Input *pHttpRef - httpref pointer + \Input *pHttpCmd - transaction command + + \Todo + Possible issues that need to be considered: + 1) out-of-order command deletion may not be handled correctly + + \Version 07/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerRequestDelCmdFromQueue(HttpManagerRefT *pHttpManager, HttpManagerHttpRefT *pHttpRef, HttpManagerHttpCmdT *pHttpCmd) +{ + int32_t iHttpCmd; + + // update transaction count + pHttpRef->iTransactions -= 1; + + // update ref state + if ((pHttpRef->iTransactions == 0) || (pHttpRef->HttpCmdQueue[1]->uState == HTTPMANAGER_CMDSTATE_WAIT)) + { + pHttpRef->uHttpState = HTTPMANAGER_REFSTATE_IDLE; + } + + // update stat tracking + if (pHttpRef->iTransactions > 0) + { + pHttpManager->HttpManagerStats.uNumQueuedTransactions -= 1; + } + if (pHttpManager->HttpManagerStats.uNumActiveTransactions > 0) + { + pHttpManager->HttpManagerStats.uNumActiveTransactions -= 1; + } + + // sanity check -- should never have an iCurTransaction != 0 here + if (pHttpRef->iCurTransaction != 0) + { + NetPrintf(("httpmanager: warning -- current transaction index is not zero!\n", pHttpRef->iCurTransaction)); + } + + // find ourselves in the HttpRef queue + if ((iHttpCmd = _HttpManagerRequestGetCmdQueueIdx(pHttpManager, pHttpRef, pHttpCmd)) < 0) + { + NetPrintf(("httpmanager: error -- could not find handle %d in ref %d queue\n", pHttpCmd->iHttpHandle, pHttpRef - pHttpManager->HttpRefs)); + return; + } + + // remove command from ref command queue + if (iHttpCmd < pHttpRef->iTransactions) + { + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: contracting transaction queue for ref %d\n", pHttpCmd->pHttpRef - pHttpManager->HttpRefs)); + memmove(&pHttpRef->HttpCmdQueue[iHttpCmd], &pHttpRef->HttpCmdQueue[iHttpCmd+1], sizeof(pHttpRef->HttpCmdQueue[0]) * (pHttpRef->iTransactions - iHttpCmd)); + } + pHttpRef->HttpCmdQueue[pHttpRef->iTransactions] = NULL; +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestPipe + + \Description + Pipeline the given request. + + \Input *pHttpManager - reference pointer + \Input *pHttpRef - http ref to use for transaction + \Input *pHttpCmd - transaction command + + \Output + int32_t - ProtoHttpRequest() result + + \Version 07/01/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerRequestPipe(HttpManagerRefT *pHttpManager, HttpManagerHttpRefT *pHttpRef, HttpManagerHttpCmdT *pHttpCmd) +{ + const char *pUrl; + uint32_t uRequestType; + int32_t iResult; + + // set up Url and request type + if (pHttpCmd != NULL) + { + pUrl = pHttpCmd->pUrl; + uRequestType = pHttpCmd->uRequestType; + } + else + { + pUrl = NULL; + uRequestType = 0; + } + + // explicitly disable keep-alive if global setting has it unavailable + if (pHttpManager->bKeepalive == FALSE) + { + ProtoHttpControl(pHttpRef->pProtoHttp, 'keep', 0, 0, NULL); + } + + /* set current transaction index. we have to do this before ProtoHttpRequest() as that + will call into the custom header callback, which relies on the index being accurate. */ + if ((pHttpRef->uHttpState == HTTPMANAGER_REFSTATE_IDLE) || (pUrl == NULL)) + { + pHttpRef->iCurTransaction = 0; + } + else + { + pHttpRef->iCurTransaction += 1; + } + + // set timeout if requested (only for the first request, since this is a global setting) + if ((pHttpCmd != NULL) && (pHttpCmd->iTimeout != 0) && (pHttpRef->iCurTransaction == 0)) + { + ProtoHttpControl(pHttpCmd->pHttpRef->pProtoHttp, 'time', pHttpCmd->iTimeout, 0, NULL); + } + + // apply append header as long as this isn't a pipeline flush + if (pHttpCmd != NULL) + { + _HttpManagerApplyAppendHeader(pHttpManager, pHttpCmd); + } + + // initiate the request + if ((iResult = ProtoHttpRequest(pHttpRef->pProtoHttp, pUrl, NULL, 0, (ProtoHttpRequestTypeE)uRequestType)) >= 0) + { + if (pHttpCmd != NULL) + { + // update stats + _HttpManagerUpdateTransactionStats(pHttpManager, pHttpCmd); + + // if this is the first request on this ref, mark it as busy and set up the first transaction handler + if (pHttpRef->uHttpState == HTTPMANAGER_REFSTATE_IDLE) + { + pHttpRef->uHttpState = HTTPMANAGER_REFSTATE_BUSY; + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_ACTV; + } + else + { + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_PIPE; + pHttpManager->HttpManagerStats.uNumPipelinedTransactions += 1; + } + } + /* if this was a flush command, reset current command, now that all of the headers + have been formatted, so received header callbacks will get correct ref */ + else + { + pHttpRef->iCurTransaction = 0; + } + } + else if (pHttpRef->iCurTransaction > 0) + { + pHttpRef->iCurTransaction -= 1; + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestPipeUpdateQueue + + \Description + Update queue upon completion of a piped request. + + \Input *pHttpManager - reference pointer + \Input *pHttpRef - http ref to use for transaction + \Input *pHttpCmd - transaction command + + \Version 07/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerRequestPipeUpdateQueue(HttpManagerRefT *pHttpManager, HttpManagerHttpRefT *pHttpRef, HttpManagerHttpCmdT *pHttpCmd) +{ + if ((pHttpRef->iTransactions > 0) && (pHttpRef->HttpCmdQueue[pHttpRef->iCurTransaction]->uState == HTTPMANAGER_CMDSTATE_PIPE)) + { + // if the previous command completed successfully, set state of next piped transaction to ACTV + if (pHttpCmd->uState == HTTPMANAGER_CMDSTATE_DONE) + { + // set next handle to active state + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: handle %d set to active state\n", pHttpRef->HttpCmdQueue[pHttpRef->iCurTransaction]->iHttpHandle)); + pHttpRef->HttpCmdQueue[pHttpRef->iCurTransaction]->uState = HTTPMANAGER_CMDSTATE_ACTV; + // allow protohttp to get the next piped transaction + ProtoHttpControl(pHttpRef->pProtoHttp, 'pnxt', 0, 0,NULL); + } + else // previous command did not complete; so we have to reset the pipe + { + int32_t iHttpCmd; + /* if this transaction was in a sequence of pipelined transactions, we need + to reset the state of the following transactions so they will be reissued */ + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: resetting state of piped requests following deletion of handle %d in state %d\n", + pHttpCmd->iHttpHandle, pHttpCmd->uState)); + for (iHttpCmd = pHttpRef->iCurTransaction; iHttpCmd < pHttpRef->iTransactions; iHttpCmd += 1) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: handle %d reset from state %d to state %d\n", pHttpRef->HttpCmdQueue[iHttpCmd]->iHttpHandle, + pHttpRef->HttpCmdQueue[iHttpCmd]->uState, HTTPMANAGER_CMDSTATE_WAIT)); + pHttpRef->HttpCmdQueue[iHttpCmd]->uState = HTTPMANAGER_CMDSTATE_WAIT; + } + // reset httpref to idle status + pHttpRef->uHttpState = HTTPMANAGER_REFSTATE_IDLE; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerCmdReleaseRef + + \Description + Releases an HttpRef from the HttpCmd it is associated with. This allows + any queued or piped transactions waiting on this ref to proceed. + + \Input *pHttpManager - reference pointer + \Input *pHttpCmd - http command + + \Version 07/10/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerCmdReleaseRef(HttpManagerRefT *pHttpManager, HttpManagerHttpCmdT *pHttpCmd) +{ + uint32_t uFreeLatency; + + // no ref allocated? + if (pHttpCmd->pHttpRef == NULL) + { + return; + } + + // remove command from command queue + _HttpManagerRequestDelCmdFromQueue(pHttpManager, pHttpCmd->pHttpRef, pHttpCmd); + + // update queue for piped operations + _HttpManagerRequestPipeUpdateQueue(pHttpManager, pHttpCmd->pHttpRef, pHttpCmd); + + // if our request is currently active, abort it + if (ProtoHttpStatus(pHttpCmd->pHttpRef->pProtoHttp, 'done', NULL, 0) == 0) + { + ProtoHttpAbort(pHttpCmd->pHttpRef->pProtoHttp); + } + + // update time spent waiting to be freed + if ((pHttpCmd->uComplTick != 0) && (pHttpCmd->uState != HTTPMANAGER_CMDSTATE_FAIL)) + { + uFreeLatency = NetTickDiff(NetTick(), pHttpCmd->uComplTick); + } + else + { + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: warning -- no completion timestamp for handle %d being deleted (%dms since queued)\n", + pHttpCmd->iHttpHandle, NetTickDiff(NetTick(), pHttpCmd->uQueueTick))); + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: url=%s\n", pHttpCmd->pUrl)); + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: state=%d\n", pHttpCmd->uState)); + NetPrintfVerbose((pHttpManager->iVerbose, 1, "httpmanager: data=%qd\n", pHttpCmd->uBytesXfer)); + uFreeLatency = 0; + } + pHttpManager->HttpManagerStats.uSumQueueFreeLatency += uFreeLatency; + if (pHttpManager->HttpManagerStats.uMaxQueueFreeLatency < uFreeLatency) + { + pHttpManager->HttpManagerStats.uMaxQueueFreeLatency = uFreeLatency; + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerPipeline + + \Description + Try to pipeline a set of requests. + + \Input *pHttpManager - reference pointer + \Input *pHttpRef - ref to execute commands on + \Input *pHttpCmd - first command in ref command queue + + \Output + int32_t - zero=no requests issued + + \Version 06/08/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerPipeline(HttpManagerRefT *pHttpManager, HttpManagerHttpRefT *pHttpRef, HttpManagerHttpCmdT *pHttpCmd) +{ + HttpManagerHttpCmdT *pHttpCmd2; + int32_t iHttpCmd, iResult; + + // not pipelining? + if (!pHttpManager->bPipelining) + { + return(0); + } + + // make sure request type can be pipelined + if ((pHttpCmd->uRequestType != PROTOHTTP_REQUESTTYPE_GET) && (pHttpCmd->uRequestType != PROTOHTTP_REQUESTTYPE_HEAD)) + { + return(0); + } + + // if we require keep-alive to pipeline, check keep-alive status + if (!pHttpManager->bPipeWithoutKeepAlive && (ProtoHttpCheckKeepAlive(pHttpRef->pProtoHttp, pHttpCmd->pUrl) == 0)) + { + return(0); + } + + // execute the first transaction + if ((iResult = _HttpManagerRequestPipe(pHttpManager, pHttpRef, pHttpCmd)) >= 0) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] started ref %d url=%s\n", pHttpCmd->iHttpHandle, pHttpRef - pHttpManager->HttpRefs, pHttpCmd->pUrl)); + + // iterate through transaction queue and issue any pending requests that are queued for pipelining + for (iHttpCmd = 1; (iHttpCmd < pHttpRef->iTransactions) && (iHttpCmd < pHttpManager->iMaxPipedUrls); iHttpCmd += 1) + { + // ref next transaction in queue + pHttpCmd2 = pHttpRef->HttpCmdQueue[iHttpCmd]; + + // check to see requests pipeline, and if so try and pipeline it + if (_HttpManagerPipelineCheck(pHttpCmd, pHttpCmd2) == 0) + { + break; + } + if ((iResult = _HttpManagerRequestPipe(pHttpManager, pHttpRef, pHttpCmd2)) < 0) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: could not pipe handle %d; request will be retried on a new ref\n", pHttpCmd2->iHttpHandle)); + break; + } + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: [%d] piped ref %d url=%s\n", pHttpCmd2->iHttpHandle, pHttpRef - pHttpManager->HttpRefs, pHttpCmd2->pUrl)); + + // update trailing ref + pHttpCmd = pHttpCmd2; + } + + // issue queued request(s) + _HttpManagerRequestPipe(pHttpManager, pHttpRef, NULL); + } + else if ((iResult == PROTOHTTP_MINBUFF) && (_HttpManagerResizeInputBuffer(pHttpManager, pHttpRef) == 0)) + { + // resized input buffer, let _HttpManagerRequestCb() re-issue it for us without pipelining + return(0); + } + else + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: ProtoHttpRequest() returned %d on handle %d url=%s\n", iResult, pHttpCmd->iHttpHandle, pHttpCmd->pUrl)); + pHttpRef->uHttpState = HTTPMANAGER_REFSTATE_BUSY; + pHttpRef->iCurTransaction = 0; + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_FAIL; + } + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerRequestCb + + \Description + Handle an HTTP request. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + \Input *pUrl - the URL that identifies the POST action. + \Input *pData - pointer to URL data (optional, may be NULL) + \Input iDataSize - size of data being uploaded + \Input eRequestType - http request type + \Input *pWriteCb - write callback (optional) + \Input *pCustomHeaderCb - custom header callback (optional) + \Input *pReceiveHeaderCb- receive header callback (optional) + \Input *pUserData - user data for callbacks (optional) + + \Output + int32_t - negative=error, else number of bytes written + + \Version 06/08/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerRequestCb(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl, const char *pData, int64_t iDataSize, ProtoHttpRequestTypeE eRequestType, ProtoHttpWriteCbT *pWriteCb, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData) +{ + HttpManagerHttpCmdT *pHttpCmd; + + // get referenced transaction + if ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL) + { + NetPrintf(("httpmanager: unrecognized transaction %d in request\n", iHandle)); + return(-1); + } + // remember request timestamp + pHttpCmd->uQueueTick = NetTick(); + // store request info + pHttpCmd->uRequestType = (uint8_t)eRequestType; + pHttpCmd->pWriteCb = pWriteCb; + pHttpCmd->pCustomHeaderCb = pCustomHeaderCb; + pHttpCmd->pReceiveHeaderCb = pReceiveHeaderCb; + pHttpCmd->pUserData = pUserData; + + // make a copy of url? + if (pHttpManager->bCopyUrl) + { + int32_t iStrLen = (int32_t)strlen(pUrl); + pHttpCmd->pUrl = DirtyMemAlloc(iStrLen+1, HTTPMGR_MEMID, pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData); + ds_strnzcpy((char *)pHttpCmd->pUrl, pUrl, iStrLen+1); + pHttpCmd->bCopiedUrl = TRUE; + } + else + { + pHttpCmd->pUrl = pUrl; + pHttpCmd->bCopiedUrl = FALSE; + } + + // allocate a ref for the transaction + if (_HttpManagerAllocRef(pHttpManager, pHttpCmd) == NULL) + { + // unable to allocate ref, but HttpManager will try again + return(0); + } + + /* if this is the only transaction queued and we are not pipelining, or the request type + is not a GET or a HEAD (the only request types we pipeline) */ + if (((pHttpCmd->pHttpRef->iTransactions == 1) && !pHttpManager->bPipelining) || + ((eRequestType != PROTOHTTP_REQUESTTYPE_GET) && (eRequestType != PROTOHTTP_REQUESTTYPE_HEAD))) + { + // issue the request + return(_HttpManagerRequestStart(pHttpManager, pHttpCmd, pData, iDataSize)); + } + else + { + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_WAIT; + return(0); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerIdle + + \Description + NetConn idle function to update the HttpManager module. + + \Input *pData - pointer to module state + \Input uTick - current tick count + + \Notes + This function is installed as a NetConn Idle function. NetConnIdle() + must be regularly polled for this function to be called. + + \Version 1.0 07/23/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerIdle(void *pData, uint32_t uTick) +{ + HttpManagerRefT *pHttpManager = (HttpManagerRefT *)pData; + if (pHttpManager->bAutoUpdate) + { + HttpManagerUpdate(pHttpManager); + } +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerCreateRef + + \Description + Create a new http ref + + \Input *pHttpManager - module state + \Input iHttpRef - index of ref to create + + \Output + int32_t - zero=success, negative=failure + + \Version 01/31/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerCreateRef(HttpManagerRefT *pHttpManager, int32_t iHttpRef) +{ + HttpManagerHttpRefT *pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + + // create protohttp ref + if ((pHttpRef->pProtoHttp = ProtoHttpCreate(pHttpManager->iHttpBufSize)) == NULL) + { + NetPrintf(("httpmanager: could not allocate http ref %d\n", iHttpRef)); + return(-1); + } + // temporarily snuff debug output + ProtoHttpControl(pHttpRef->pProtoHttp, 'spam', 0, 0, NULL); + + // default keep-alive to enabled + ProtoHttpControl(pHttpRef->pProtoHttp, 'keep', 1, 0, NULL); + // set pipelining enable/disable + ProtoHttpControl(pHttpRef->pProtoHttp, 'pipe', pHttpManager->bPipelining, 0, NULL); + // set up callbacks + ProtoHttpCallback(pHttpRef->pProtoHttp, pHttpManager->pCustomHeaderCb ? _HttpManagerCustomHeaderCb : NULL, _HttpManagerReceiveHeaderCb, pHttpRef); + // set default debug level + ProtoHttpControl(pHttpRef->pProtoHttp, 'spam', pHttpManager->iVerbose, 0, NULL); + + // init to idle state + pHttpRef->uHttpState = HTTPMANAGER_REFSTATE_IDLE; + // init last access time + pHttpRef->uLastTick = NetTick(); + // return success to caller + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerDestroyRef + + \Description + Destroy an allocated http ref + + \Input *pHttpManager - module state + \Input iHttpRef - index of ref to destroy + + \Version 01/31/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static void _HttpManagerDestroyRef(HttpManagerRefT *pHttpManager, int32_t iHttpRef) +{ + HttpManagerHttpRefT *pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + HttpManagerHttpCmdT *pHttpCmd; + int32_t iTransaction; + + // early out if ref is null + if (pHttpRef->pProtoHttp == NULL) + { + return; + } + + // invalidate any commands that referenced this ref + for (iTransaction = 0; iTransaction < pHttpRef->iTransactions; iTransaction += 1) + { + pHttpCmd = pHttpRef->HttpCmdQueue[iTransaction]; + if (pHttpCmd->pHttpRef == pHttpRef) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: destroying active ref %2d; handle %d is being terminated\n", iHttpRef, pHttpCmd->iHttpHandle)); + pHttpCmd->pHttpRef = NULL; + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_FAIL; + } + } + + // destroy ref + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: destroying http ref %d\n", iHttpRef)); + ProtoHttpDestroy(pHttpRef->pProtoHttp); + // clear state + ds_memclr(pHttpRef, sizeof(*pHttpRef)); +} + +/*F********************************************************************************/ +/*! + \Function _HttpManagerSizeRefPool + + \Description + Create or destroy ProtoHttp refs based on current pool size and new pool size + + \Input *pHttpManager - module state + \Input iHttpNumRefs - number of desired protohttp refs + + \Output + int32_t - zero=success, negative=failure + + \Version 01/31/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _HttpManagerSizeRefPool(HttpManagerRefT *pHttpManager, int32_t iHttpNumRefs) +{ + int32_t iHttpRef; + + // validate request size + if (iHttpNumRefs > HTTPMANAGER_MAXREFS) + { + NetPrintf(("httpmanager: clamping 'pool' request to max %d refs\n", HTTPMANAGER_MAXREFS)); + iHttpNumRefs = HTTPMANAGER_MAXREFS; + } + else if (iHttpNumRefs < 1) + { + NetPrintf(("httpmanager: clamping 'pool' request to min of one ref\n")); + iHttpNumRefs = 1; + } + + // increase size of ref pool? + if (pHttpManager->iHttpNumRefs < iHttpNumRefs) + { + int32_t iResult; + DirtyMemGroupEnter(pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData); + for (iHttpRef = pHttpManager->iHttpNumRefs, iResult = 0; iHttpRef < iHttpNumRefs; iHttpRef += 1) + { + if ((iResult = _HttpManagerCreateRef(pHttpManager, iHttpRef)) < 0) + { + break; + } + } + DirtyMemGroupLeave(); + if (iResult < 0) + { + // could not allocate http ref + return(-1); + } + } + else if (pHttpManager->iHttpNumRefs > iHttpNumRefs) // decrease size of ref pool + { + for (iHttpRef = pHttpManager->iHttpNumRefs - 1; iHttpRef >= iHttpNumRefs; iHttpRef -= 1) + { + _HttpManagerDestroyRef(pHttpManager, iHttpRef); + } + } + + // save new ref count, return success + pHttpManager->iHttpNumRefs = iHttpNumRefs; + return(0); +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function HttpManagerCreate + + \Description + Allocate module state and prepare for use + + \Input iHttpBufSize - length of receive buffer for each protohttp ref + \Input iHttpNumRefs - number of protohttp modules to allocate + + \Output + HttpManagerRefT * - pointer to module state, or NULL + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +HttpManagerRefT *HttpManagerCreate(int32_t iHttpBufSize, int32_t iHttpNumRefs) +{ + HttpManagerRefT *pHttpManager; + void *pMemGroupUserData; + int32_t iMemGroup; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // clamp refcount + if (iHttpNumRefs > HTTPMANAGER_MAXREFS) + { + NetPrintf(("httpmanager: %d requested refs exceeds max of %d; clamping\n", iHttpNumRefs, HTTPMANAGER_MAXREFS)); + iHttpNumRefs = HTTPMANAGER_MAXREFS; + } + + // allocate the module state + if ((pHttpManager = DirtyMemAlloc(sizeof(*pHttpManager), HTTPMGR_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("httpmanager: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pHttpManager, sizeof(*pHttpManager)); + + // save parms & set defaults + pHttpManager->iMemGroup = iMemGroup; + pHttpManager->pMemGroupUserData = pMemGroupUserData; + pHttpManager->iHttpBufSize = iHttpBufSize; + pHttpManager->iHttpHandle = 1; // zero is reserved as invalid +#if !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_PS4) && !defined(DIRTYCODE_GDK) + pHttpManager->bPipelining = TRUE; +#endif + pHttpManager->bKeepalive = TRUE; + pHttpManager->bPipeWithoutKeepAlive = TRUE; + pHttpManager->iMaxPipedUrls = 4; + pHttpManager->bCopyUrl = TRUE; + pHttpManager->bAutoUpdate = TRUE; + pHttpManager->iVerbose = 1; + + // allocate protohttp refs + if (_HttpManagerSizeRefPool(pHttpManager, iHttpNumRefs) < 0) + { + NetPrintf(("httpmanager: could not allocate ref pool\n")); + HttpManagerDestroy(pHttpManager); + return(NULL); + } + + // add httpmanager task handle + NetConnIdleAdd(_HttpManagerIdle, pHttpManager); + + #if HTTPMANAGER_FINALDEBUG + _HttpManager_iStartTick = NetTick(); + #endif + + // return new module state + return(pHttpManager); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerCallback + + \Description + Set header callbacks. + + \Input *pHttpManager - module state + \Input *pCustomHeaderCb - pointer to custom send header callback + \Input *pReceiveHeaderCb- pointer to recv header callback + + \Notes + See ProtoHttpCallback documentation for a description of the callbacks + specified here. + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void HttpManagerCallback(HttpManagerRefT *pHttpManager, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb) +{ + HttpManagerHttpRefT *pHttpRef; + int32_t iHttpRef; + + // save callback info + pHttpManager->pCustomHeaderCb = pCustomHeaderCb; + pHttpManager->pReceiveHeaderCb = pReceiveHeaderCb; + + // update protohttp refs with callback info + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + ProtoHttpCallback(pHttpRef->pProtoHttp, pHttpManager->pCustomHeaderCb ? _HttpManagerCustomHeaderCb : NULL, _HttpManagerReceiveHeaderCb, pHttpRef); + } +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerDestroy + + \Description + Destroy the module and release its state + + \Input *pHttpManager - reference pointer + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void HttpManagerDestroy(HttpManagerRefT *pHttpManager) +{ + HttpManagerHttpCmdT *pHttpCmd; + int32_t iHttpCmd, iHttpRef; + + // del httpmanager task handle + NetConnIdleDel(_HttpManagerIdle, pHttpManager); + + // if append header is set, free it + if (pHttpManager->pAppendHdr != NULL) + { + DirtyMemFree(pHttpManager->pAppendHdr, HTTPMGR_MEMID, pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData); + } + + // destroy protohttp modules + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + _HttpManagerDestroyRef(pHttpManager, iHttpRef); + } + + // clean up command list + for (iHttpCmd = 0; iHttpCmd < HTTPMANAGER_MAXCMDS; iHttpCmd += 1) + { + pHttpCmd = &pHttpManager->HttpCmds[iHttpCmd]; + if (pHttpCmd->iHttpHandle != 0) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: warning; handle %d not cleaned up\n", pHttpCmd->iHttpHandle)); + HttpManagerFree(pHttpManager, pHttpCmd->iHttpHandle); + } + } + + // display stats + #if DIRTYCODE_LOGGING || HTTPMANAGER_FINALDEBUG + if (pHttpManager->iVerbose > 0) + { + _HttpManagerDisplayStats(pHttpManager); + } + #endif + + // destroy module state + DirtyMemFree(pHttpManager, HTTPMGR_MEMID, pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerAlloc + + \Description + Allocate a new transaction handle + + \Input *pHttpManager - reference pointer + + \Output + int32_t - negative=failure, else transaction handle + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t HttpManagerAlloc(HttpManagerRefT *pHttpManager) +{ + HttpManagerHttpCmdT *pHttpCmd; + + // get an unallocated ProtoHttp ref + if ((pHttpCmd = _HttpManagerAllocCmd(pHttpManager)) == NULL) + { + NetPrintf(("httpmanager: could not allocate new http transaction for HttpManagerGet() request\n")); + return(-1); + } + // return transaction handle to caller + NetPrintfVerbose((pHttpManager->iVerbose, 2, "httpmanager: allocated handle %d\n", pHttpCmd->iHttpHandle)); + return(pHttpCmd->iHttpHandle); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerFree + + \Description + Release a new transaction handle + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void HttpManagerFree(HttpManagerRefT *pHttpManager, int32_t iHandle) +{ + HttpManagerHttpCmdT *pHttpCmd; + + // get referenced transaction + if ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL) + { + NetPrintf(("httpmanager: unrecognized transaction %d in HttpManagerFree()\n", iHandle)); + return; + } + NetPrintfVerbose((pHttpManager->iVerbose, 2, "httpmanager: releasing handle %d\n", iHandle)); + + // release the ref + _HttpManagerCmdReleaseRef(pHttpManager, pHttpCmd); + // free url, if it was copied + if (pHttpCmd->bCopiedUrl == TRUE) + { + if (pHttpCmd->pUrl != NULL) + { + DirtyMemFree((void *)pHttpCmd->pUrl, HTTPMGR_MEMID, pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData); + } + else + { + NetPrintf(("httpmanager: warning; copied url null before free\n")); + } + } + // free append buffer, if present + if (pHttpCmd->pAppendHdr != NULL) + { + DirtyMemFree(pHttpCmd->pAppendHdr, HTTPMGR_MEMID, pHttpManager->iMemGroup, pHttpManager->pMemGroupUserData); + } + + // clear ref + ds_memclr(pHttpCmd, sizeof(*pHttpCmd)); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerGet + + \Description + Initiate an HTTP transaction. Pass in a URL and the module starts a transfer + from the appropriate server. + + \Input *pHttpManager - module state + \Input iHandle - transaction handle + \Input *pUrl - the url to retrieve + \Input bHeadOnly - if TRUE only get header + + \Output + int32_t - negative=failure, else success + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t HttpManagerGet(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl, uint32_t bHeadOnly) +{ + return(_HttpManagerRequestCb(pHttpManager, iHandle, pUrl, NULL, 0, bHeadOnly ? PROTOHTTP_REQUESTTYPE_HEAD : PROTOHTTP_REQUESTTYPE_GET, NULL, NULL, NULL, NULL)); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerRecv + + \Description + Return the actual url data. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle (returned by HttpManagerGet()) + \Input *pBuffer - buffer to store data in + \Input iBufMin - minimum number of bytes to return (returns zero if this number is not available) + \Input iBufMax - maximum number of bytes to return (buffer size) + + \Output + int32_t - negative=error, zero=no data available or bufmax <= 0, positive=number of bytes read + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t HttpManagerRecv(HttpManagerRefT *pHttpManager, int32_t iHandle, char *pBuffer, int32_t iBufMin, int32_t iBufMax) +{ + HttpManagerHttpCmdT *pHttpCmd; + + // get referenced transaction + if ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL) + { + NetPrintf(("httpmanager: unrecognized transaction %d in HttpManagerRecv()\n", iHandle)); + return(-1); + } + + // wait until active + if (pHttpCmd->uState < HTTPMANAGER_CMDSTATE_ACTV) + { + return(0); + } + + // early-out if queued execution of request failed + if (pHttpCmd->uState == HTTPMANAGER_CMDSTATE_FAIL) + { + return(PROTOHTTP_RECVFAIL); + } + + // update the ref + ProtoHttpUpdate(pHttpCmd->pHttpRef->pProtoHttp); + + // execute the receive + if ((pHttpCmd->iResult = ProtoHttpRecv(pHttpCmd->pHttpRef->pProtoHttp, pBuffer, iBufMin, iBufMax)) > 0) + { + // track bytes received + pHttpCmd->uBytesXfer += (unsigned)pHttpCmd->iResult; + } + else if ((pHttpCmd->iResult == PROTOHTTP_MINBUFF) && (_HttpManagerResizeInputBuffer(pHttpManager, pHttpCmd->pHttpRef) == 0)) + { + // resized input buffer; swallow the error result and try again next go-around + pHttpCmd->iResult = 0; + } + + // check for transaction completion + if (pHttpCmd->uState == HTTPMANAGER_CMDSTATE_ACTV) + { + _HttpManagerRequestCheckCompletion(pHttpManager, pHttpCmd); + } + + // update last access time + { + uint32_t uCurTick = NetTick(); + if (NetTickDiff(uCurTick, pHttpCmd->pHttpRef->uLastTick) > 1000) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: warning -- ref %d last updated %dms previous (receive)\n", pHttpCmd->pHttpRef - pHttpManager->HttpRefs, NetTickDiff(uCurTick, pHttpCmd->pHttpRef->uLastTick))); + } + pHttpCmd->pHttpRef->uLastTick = uCurTick; + } + + // return result to caller + return(pHttpCmd->iResult); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerRecvAll + + \Description + Return all of the url data. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle (returned by HttpManagerGet()) + \Input *pBuffer - buffer to store data in + \Input iBufSize - size of buffer + + \Output + int32_t - PROTOHTTP_RECV*, or positive=bytes in response + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t HttpManagerRecvAll(HttpManagerRefT *pHttpManager, int32_t iHandle, char *pBuffer, int32_t iBufSize) +{ + HttpManagerHttpCmdT *pHttpCmd; + + // get referenced transaction + if ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL) + { + NetPrintf(("httpmanager: unrecognized transaction %d in HttpManagerRecv()\n", iHandle)); + return(-1); + } + + // if transaction is not active, return no data available + if (pHttpCmd->uState != HTTPMANAGER_CMDSTATE_ACTV) + { + return(0); + } + + // pass through to http ref + if ((pHttpCmd->iResult = ProtoHttpRecvAll(pHttpCmd->pHttpRef->pProtoHttp, pBuffer, iBufSize)) == PROTOHTTP_MINBUFF) + { + // increase the input buffer size, for next go-around + if (_HttpManagerResizeInputBuffer(pHttpManager, pHttpCmd->pHttpRef) == 0) + { + // swallow the error result and try again next go-around + pHttpCmd->iResult = 0; + } + } + // return result to caller + return(pHttpCmd->iResult); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerPost + + \Description + Initiate transfer of data to to the server via a HTTP POST command. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + \Input *pUrl - the URL that identifies the POST action. + \Input *pData - pointer to URL data (optional, may be NULL) + \Input iDataSize - size of data being uploaded (see Notes) + \Input bDoPut - if TRUE, do a PUT instead of a POST + + \Output + int32_t - negative=failure, else number of data bytes sent + + \Notes + See ProtoHttpPost() documentation. + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t HttpManagerPost(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl, const char *pData, int64_t iDataSize, uint32_t bDoPut) +{ + return(_HttpManagerRequestCb(pHttpManager, iHandle, pUrl, pData, iDataSize, bDoPut ? PROTOHTTP_REQUESTTYPE_PUT : PROTOHTTP_REQUESTTYPE_POST, NULL, NULL, NULL, NULL)); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerSend + + \Description + Send data during an ongoing Post transaction. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + \Input *pData - pointer to data to send + \Input iDataSize - size of data being sent + + \Output + int32_t - negative=failure, else number of data bytes sent + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t HttpManagerSend(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pData, int32_t iDataSize) +{ + HttpManagerHttpCmdT *pHttpCmd; + + // get referenced transaction + if ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL) + { + NetPrintf(("httpmanager: unrecognized transaction %d in HttpManagerRecv()\n", iHandle)); + return(-1); + } + + // if transaction is not active, return no data available + if (pHttpCmd->uState != HTTPMANAGER_CMDSTATE_ACTV) + { + return(0); + } + + // pass through to http ref + return(ProtoHttpSend(pHttpCmd->pHttpRef->pProtoHttp, pData, iDataSize)); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerRequestCb2 + + \Description + Make an HTTP request. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + \Input *pUrl - the URL that identifies the POST action. + \Input *pData - pointer to URL data (optional, may be NULL) + \Input iDataSize - size of data being uploaded + \Input eRequestType - http request type + \Input *pWriteCb - write callback (optional) + \Input *pCustomHeaderCb - custom header callback (optional) + \Input *pReceiveHeaderCb- receive header callback (optional) + \Input *pUserData - user data for write callback + + \Output + int32_t - negative=error, else number of bytes written + + \Version 09/11/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t HttpManagerRequestCb2(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl, const char *pData, int64_t iDataSize, ProtoHttpRequestTypeE eRequestType, ProtoHttpWriteCbT *pWriteCb, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData) +{ + return(_HttpManagerRequestCb(pHttpManager, iHandle, pUrl, pData, iDataSize, eRequestType, pWriteCb, pCustomHeaderCb, pReceiveHeaderCb, pUserData)); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerSetBaseUrl + + \Description + Set base url that will be used for any relative url references. + + \Input *pHttpManager - reference pointer + \Input iHandle - handle to set base url for + \Input *pUrl - base url + + \Version 02/03/2010 (jbrookes) +*/ +/********************************************************************************F*/ +void HttpManagerSetBaseUrl(HttpManagerRefT *pHttpManager, int32_t iHandle, const char *pUrl) +{ + HttpManagerHttpCmdT *pHttpCmd; + // get referenced transaction + if ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL) + { + NetPrintf(("httpmanager: unrecognized transaction %d in HttpManagerSetBaseUrl()\n", iHandle)); + return; + } + // pass through to http ref + if ((pHttpCmd->pHttpRef != NULL) && (pHttpCmd->pHttpRef->pProtoHttp != NULL)) + { + ProtoHttpSetBaseUrl(pHttpCmd->pHttpRef->pProtoHttp, pUrl); + } +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerControl + + \Description + HttpManager control function. Different selectors control different + behaviors. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + \Input iSelect - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + If a handle is specified (iHandle > 0), HttpManagerControl() will forward + the call to ProtoHttpControl() for the appropriate ref. HttpManager control + selectors are as follows: + + \verbatim + SELECTOR DESCRIPTION + 'apnd' Header append feature (see ProtoHttp doc for details). If a + handle is specified the scope is command-specific, otherwise + it is global. A command-specific append header takes + precedence over a global append header. + 'auto' enable/disable auto-update (polling of HttpManagerUpdate by + NetConnIdle(); default enabled) + 'cbup' sets callback user data pointer (pValue=callback) + 'copy' sets if urls are copied internally or not (default=true) + 'maxp' sets max pipeline depth (default 4) + 'pipe' pipelining enable/disable + 'pwka' pipelining without keep-alive enable/disable + 'spam' manager-level debug verbosity + 'stcl' clear httpmanager stats. + \endverbatim + + \Version 05/20/2009 (jbrookes) +*/ +/*******************************************************************************F*/ +int32_t HttpManagerControl(HttpManagerRefT *pHttpManager, int32_t iHandle, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue) +{ + HttpManagerHttpCmdT *pHttpCmd = NULL; + int32_t iHttpRef; + + // if we are given a handle, resolve http cmd + if ((iHandle > 0) && ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL)) + { + NetPrintf(("httpmanager: HttpManagerControl(%d, '%C') failed; unrecognized handle\n", iHandle, iSelect)); + return(-1); + } + + // commands that work with or without a cmd + + // set append header + if (iSelect == 'apnd') + { + return(_HttpManagerSetAppendHeader(pHttpManager, pHttpCmd, pValue)); + } + + // command-specific selector + if (pHttpCmd != NULL) + { + // put httpmanager control selectors here + if (pHttpCmd->uState == HTTPMANAGER_CMDSTATE_IDLE) + { + // set callback user data pointer + if (iSelect == 'cbup') + { + NetPrintfVerbose((pHttpManager->iVerbose, 2, "httpmanager: setting callback for handle %d to 0x%08x\n", iHandle, pValue)); + pHttpCmd->pCallbackRef = pValue; + return(0); + } + // set timeout + if (iSelect == 'time') + { + pHttpCmd->iTimeout = iValue; + return(0); + } + } + + // if it's not an httpmanager control selector, and we have a ProtoHttp ref, pass it through to ProtoHttp + if ((pHttpCmd->pHttpRef != NULL) && (pHttpCmd->pHttpRef->pProtoHttp != NULL)) + { + return(ProtoHttpControl(pHttpCmd->pHttpRef->pProtoHttp, iSelect, iValue, iValue2, pValue)); + } + } + else // global (not command-specific) selectors + { + // enable/disable auto-update (polling of HttpManagerUpdate by NetConnIdle()) + if (iSelect == 'auto') + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: auto-update %s\n", iValue ? "enabled" : "disabled")); + pHttpManager->bAutoUpdate = iValue ? TRUE : FALSE; + return(0); + } + // copy url setting enable/disable + if (iSelect == 'copy') + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: urlcopy %s\n", iValue ? "enabled" : "disabled")); + pHttpManager->bCopyUrl = iValue ? TRUE : FALSE; + return(0); + } + // set max pipeline depth + if (iSelect == 'maxp') + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: setting max pipeline depth to %d\n", iValue)); + pHttpManager->iMaxPipedUrls = (int8_t)iValue; + return(0); + } + // set pipelining setting + #if !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_PS4) && !defined(DIRTYCODE_GDK) + if (iSelect == 'pipe') + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: pipelining %s\n", iValue ? "enabled" : "disabled")); + pHttpManager->bPipelining = iValue ? TRUE : FALSE; + // intentional fall-through to pass down to protohttp refs + } + #endif + if (iSelect == 'pool') + { + return(_HttpManagerSizeRefPool(pHttpManager, iValue)); + } + // set pipelining without keep-alive + if (iSelect == 'pwka') + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: pipelining without keep-alive %s\n", iValue ? "enabled" : "disabled")); + pHttpManager->bPipeWithoutKeepAlive = iValue ? TRUE : FALSE; + return(0); + } + // manager-level debug verbosity + if (iSelect == 'spam') + { + pHttpManager->iVerbose = iValue; + if (iValue > 0) + { + // set protohttp spam level one lower than httpmanager's + iValue -= 1; + } + // intentional fall-through to pass down to protohttp refs + } + // clear stats + if (iSelect == 'stcl') + { + _HttpManagerResetStats(pHttpManager); + return(0); + } + + // if we haven't gotten it by now, it's an option to be applied to all http refs + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + if (pHttpManager->HttpRefs[iHttpRef].pProtoHttp != NULL) + { + ProtoHttpControl(pHttpManager->HttpRefs[iHttpRef].pProtoHttp, iSelect, iValue, iValue2, pValue); + } + } + return(0); + } + // unhandled + NetPrintf(("httpmanager: HttpManagerControl(%d, '%C') unhandled\n", iHandle, iSelect)); + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerStatus + + \Description + Return status of current HTTP transfer. Status type depends on selector. + + \Input *pHttpManager - reference pointer + \Input iHandle - transaction handle + \Input iSelect - info selector (see Notes) + \Input *pBuffer - [in/out] input and/or storage for selector-specific output + \Input iBufSize - size of buffer + + \Output + int32_t - selector specific + + \Notes + If a handle is specified, HttpManagerStatus() will call ProtoHttpStatus() for + the appropriate ref. HttpManager status selectors are as follows: + + \verbatim + SELECTOR DESCRIPTION + 'busy' Returns number of busy refs + 'hndl' Get HttpManager handle from ProtoHttp ref (passed in pBuffer) + 'href' Get ProtoHttp ref from HttpManager handle (returned in pBuffer, if specified) + 'stat' If pBuffer is NULL, display httpmanager stats (debug only); else copy stats to output buffer + 'urls' Copies url from specified command into output buffer + \endverbatim + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t HttpManagerStatus(HttpManagerRefT *pHttpManager, int32_t iHandle, int32_t iSelect, void *pBuffer, int32_t iBufSize) +{ + // if the user wants status of a specific transaction? + if (iHandle > 0) + { + HttpManagerHttpCmdT *pHttpCmd; + + // get referenced transaction + if ((pHttpCmd = _HttpManagerGetCmd(pHttpManager, iHandle)) == NULL) + { + NetPrintf(("httpmanager: HttpManagerStatus(%d, '%C') failed; unrecognized handle\n", iHandle, iSelect)); + return(-1); + } + + // get href from handle + if (iSelect == 'href') + { + if ((pHttpCmd->pHttpRef != NULL) && (pHttpCmd->pHttpRef->pProtoHttp != NULL)) + { + if ((pBuffer != NULL) && (iBufSize == sizeof(void *))) + { + ds_memcpy(pBuffer, &pHttpCmd->pHttpRef->pProtoHttp, sizeof(void *)); + } + return(0); + } + return(-1); + } + + // copy url to status buffer + if (iSelect == 'urls') + { + ds_strnzcpy(pBuffer, pHttpCmd->pUrl, iBufSize); + return(0); + } + + // only allow status of active transactions (otherwise this transaction does not own the ref) + if (pHttpCmd->uState < HTTPMANAGER_CMDSTATE_ACTV) + { + if ((iSelect == 'done') || (iSelect == 'data') || (iSelect == 'time')) + { + return(0); + } + if ((iSelect == 'body') || (iSelect == 'code') || (iSelect == 'htxt')) + { + return(-1); + } + NetPrintf(("httpmanager: HttpManagerStatus(%d, '%C') failed; handle not active or done (state=%d)\n", iHandle, iSelect, pHttpCmd->uState)); + return(-1); + } + + // if it's not an httpmanager status selector, and we have a ProtoHttp ref, pass it through to ProtoHttp + if ((pHttpCmd->pHttpRef != NULL) && (pHttpCmd->pHttpRef->pProtoHttp != NULL)) + { + return(ProtoHttpStatus(pHttpCmd->pHttpRef->pProtoHttp, iSelect, pBuffer, iBufSize)); + } + } + else + { + // return count of the number of busy refs + if (iSelect == 'busy') + { + HttpManagerHttpRefT *pHttpRef; + int32_t iHttpRef, iBusyRefs; + + for (iHttpRef = 0, iBusyRefs = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + if (pHttpRef->uHttpState == HTTPMANAGER_REFSTATE_BUSY) + { + iBusyRefs += 1; + } + } + return(iBusyRefs); + } + // get handle from href + if (iSelect == 'hndl') + { + if ((pBuffer != NULL) && (iBufSize == sizeof(void *))) + { + HttpManagerHttpRefT *pHttpRef; + ProtoHttpRefT *pProtoHttpFind; + int32_t iHttpRef; + + // get httpref from buffer + ds_memcpy(&pProtoHttpFind, pBuffer, sizeof(void *)); + + // find it + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + if ((pHttpRef->pProtoHttp == pProtoHttpFind) && (pHttpRef->HttpCmdQueue[pHttpRef->iCurTransaction] != NULL)) + { + // return active handle + return(pHttpRef->HttpCmdQueue[pHttpRef->iCurTransaction]->iHttpHandle); + } + } + } + // not found + return(-1); + } + // display/fetch stats + if (iSelect == 'stat') + { + if (pBuffer != NULL) + { + if (iBufSize == sizeof(pHttpManager->HttpManagerStats)) + { + ds_memcpy(pBuffer, &pHttpManager->HttpManagerStats, sizeof(pHttpManager->HttpManagerStats)); + return(0); + } + else + { + return(-1); + } + } + #if DIRTYCODE_LOGGING || HTTPMANAGER_FINALDEBUG + else + { + _HttpManagerDisplayStats(pHttpManager); + } + #endif + return(0); + } + } + // unhandled + NetPrintf(("httpmanager: HttpManagerStatus(%d, '%C') unhandled\n", iHandle, iSelect)); + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function HttpManagerUpdate + + \Description + Give time to module to do its thing (should be called periodically to + allow module to perform work) + + \Input *pHttpManager - reference pointer + + \Version 05/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void HttpManagerUpdate(HttpManagerRefT *pHttpManager) +{ + HttpManagerHttpRefT *pHttpRef; + HttpManagerHttpCmdT *pHttpCmd; + int32_t iHttpCmd, iHttpRef; + + // look for unassigned commands (these happen when we make a request, if we have filled up our available HttpRef command slots) + for (iHttpCmd = 0; iHttpCmd < HTTPMANAGER_MAXCMDS; iHttpCmd += 1) + { + pHttpCmd = &pHttpManager->HttpCmds[iHttpCmd]; + if ((pHttpCmd->pUrl != NULL) && (pHttpCmd->uState == HTTPMANAGER_CMDSTATE_IDLE) && (pHttpCmd->pHttpRef == NULL)) + { + // allocate a ref for the transaction + if (_HttpManagerAllocRef(pHttpManager, pHttpCmd) == NULL) + { + break; + } + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: deferred alloc for handle %d\n", pHttpCmd->iHttpHandle)); + // promote to wait state + pHttpCmd->uState = HTTPMANAGER_CMDSTATE_WAIT; + } + } + + // update protohttp modules + for (iHttpRef = 0; iHttpRef < pHttpManager->iHttpNumRefs; iHttpRef += 1) + { + uint32_t uCurTick; + pHttpRef = &pHttpManager->HttpRefs[iHttpRef]; + + // check for idle ref that has one or more transactions queued + if ((pHttpRef->uHttpState == HTTPMANAGER_REFSTATE_IDLE) && (pHttpRef->iTransactions > 0)) + { + // reference first queued transaction + pHttpCmd = pHttpRef->HttpCmdQueue[0]; + + // try to pipeline the command; if we can't, start the request + if (_HttpManagerPipeline(pHttpManager, pHttpRef, pHttpCmd) == 0) + { + // execute first queued transaction + _HttpManagerRequestStart(pHttpManager, pHttpCmd, NULL, 0); + } + } + + // update refs + uCurTick = NetTick(); + ProtoHttpUpdate(pHttpRef->pProtoHttp); + + if (NetTickDiff(uCurTick, pHttpRef->uLastTick) > 1000) + { + NetPrintfVerbose((pHttpManager->iVerbose, 0, "httpmanager: warning -- ref %d last updated %dms previous (update)\n", iHttpRef, NetTickDiff(uCurTick, pHttpRef->uLastTick))); + } + pHttpRef->uLastTick = uCurTick; + } + + // process any refs with write callbacks to update + for (iHttpCmd = 0; iHttpCmd < HTTPMANAGER_MAXCMDS; iHttpCmd += 1) + { + pHttpCmd = &pHttpManager->HttpCmds[iHttpCmd]; + if ((pHttpCmd->pWriteCb != NULL) && (pHttpCmd->uState == HTTPMANAGER_CMDSTATE_ACTV)) + { + _HttpManagerWriteCbProcess(pHttpManager, pHttpCmd); + } + } + +} + diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protohttpserv.c b/r5dev/thirdparty/dirtysdk/source/proto/protohttpserv.c new file mode 100644 index 00000000..cb8ee4ca --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protohttpserv.c @@ -0,0 +1,2071 @@ +/*H********************************************************************************/ +/*! + \File protohttpserv.c + + \Description + This module implements an HTTP server that can perform basic transactions + (get/put) with an HTTP client. It conforms to but does not fully implement + the 1.1 HTTP spec (http://www.w3.org/Protocols/rfc2616/rfc2616.html), and + allows for secure HTTP transactions as well as insecure transactions. + + \Copyright + Copyright (c) Electronic Arts 2013 + + \Version 1.0 12/11/2013 (jbrookes) Initial version, based on HttpServ tester2 module +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" // NetConnSleep +#include "DirtySDK/proto/protohttputil.h" +#include "DirtySDK/proto/protossl.h" + +#include "DirtySDK/proto/protohttpserv.h" + +/*** Defines **********************************************************************/ + +#define HTTPSERV_VERSION (0x0100) //!< httpserv revision number (maj.min); update this for major bug fixes or protocol additions/changes +#define HTTPSERV_DISCTIME (5*1000) //!< give five seconds after we disconnect before destroying network resources +#define HTTPSERV_CHUNKWAITTEST (0) //!< this tests a corner case in protohttp chunk receive where only one byte of the chunk trailer is available +#define HTTPSERV_IDLETIMEOUT_DEFAULT (5*60*1000) //!< default keep-alive timeout +#define HTTPSERV_REQUESTTIMEOUT_DEFAULT (30*1000) //!< default request timeout +#define HTTPSERV_BUFSIZE_DEFAULT (16000) //!< the most user payload we can send in a single SSL packet + +/*** Function Prototypes **********************************************************/ + +/*** Type Definitions *************************************************************/ + +//! httpserv response table +typedef struct ProtoHttpServRespT +{ + ProtoHttpResponseE eResponse; + const char *pResponseText; +}ProtoHttpServRespT; + +//! httpserv transaction state for a single transaction +typedef struct ProtoHttpServThreadT +{ + struct ProtoHttpServThreadT *pNext; + ProtoSSLRefT *pProtoSSL; + + struct sockaddr RequestAddr; + + ProtoHttpServRequestT RequestInfo; //!< user data associated with this thread via request callback (for handing request) + ProtoHttpServResponseT ResponseInfo; //!< user data associated with this thread via request callback (for replying) + + char *pBuffer; + int32_t iBufMax; + int32_t iBufLen; + int32_t iBufOff; + int32_t iChkLen; + int32_t iChkRcv; + int32_t iChkOvr; //!< chunk overhead + int32_t iIdleTimer; + int32_t iDisconnectTimer; + + int64_t iContentSent; + + char strUserAgent[256]; + + uint8_t bConnected; + uint8_t bReceivedHeader; + uint8_t bParsedHeader; + uint8_t bProcessedRequest; + uint8_t bReceivedBody; + uint8_t bFormattedHeader; + uint8_t bSentHeader; + uint8_t bSentBody; + uint8_t bConnectionClose; + uint8_t bConnectionKeepAlive; + uint8_t bDisconnecting; + uint8_t uHttpThreadId; + uint8_t bHttp1_0; + uint8_t bChunked; + uint8_t _pad[2]; +} ProtoHttpServThreadT; + +//! httpserv module state +struct ProtoHttpServRefT +{ + ProtoSSLRefT *pListenSSL; + + int32_t iMemGroup; + void *pMemGroupUserData; + + ProtoHttpServRequestCbT *pRequestCb; + ProtoHttpServReceiveCbT *pReceiveCb; + ProtoHttpServSendCbT *pSendCb; + ProtoHttpServHeaderCbT *pHeaderCb; + ProtoHttpServLogCbT *pLogCb; + void *pUserData; + + char *pServerCert; + int32_t iServerCertLen; + char *pServerKey; + int32_t iServerKeyLen; + int32_t iSecure; + uint16_t uSslVersion; + uint16_t uSslVersionMin; + uint32_t uSslCiphers; + int32_t iClientCertLvl; + int32_t iIdleTimeout; + int32_t iRequestTimeout; + uint16_t uDebugLevel; + + char strServerName[128]; + char strAlpn[128]; + + ProtoHttpServThreadT *pThreadListHead; + ProtoHttpServThreadT *pThreadListTail; + uint8_t uCurrThreadId; +}; + +/*** Variables ********************************************************************/ + +//! http request names +static const char *_ProtoHttpServ_strRequestNames[PROTOHTTP_NUMREQUESTTYPES] = +{ + "HEAD", "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", NULL +}; + +//! http response name table +static const ProtoHttpServRespT _ProtoHttpServ_Responses[] = +{ + // 1xx - informational reponse + { PROTOHTTP_RESPONSE_CONTINUE, "Continue" }, //!< continue with request, generally ignored + { PROTOHTTP_RESPONSE_SWITCHPROTO, "Switching Protocols" }, //!< 101 - OK response to client switch protocol request + // 2xx - success response + { PROTOHTTP_RESPONSE_OK, "OK" }, //!< client's request was successfully received, understood, and accepted + { PROTOHTTP_RESPONSE_CREATED, "Created" } , //!< new resource has been created + { PROTOHTTP_RESPONSE_ACCEPTED, "Accepted" } , //!< request accepted but not complete + { PROTOHTTP_RESPONSE_NONAUTH, "Non-Authoritative Information" }, //!< non-authoritative info (ok) + { PROTOHTTP_RESPONSE_NOCONTENT, "No Content" } , //!< request fulfilled, no message body + { PROTOHTTP_RESPONSE_RESETCONTENT, "Reset Content" } , //!< request success, reset document view + { PROTOHTTP_RESPONSE_PARTIALCONTENT, "Partial Content" } , //!< server has fulfilled partial GET request + // 3xx - redirection response + { PROTOHTTP_RESPONSE_MULTIPLECHOICES, "Multiple Choices" }, //!< requested resource corresponds to a set of representations + { PROTOHTTP_RESPONSE_MOVEDPERMANENTLY, "Moved Permanently" }, //!< requested resource has been moved permanently to new URI + { PROTOHTTP_RESPONSE_FOUND, "Found" }, //!< requested resources has been moved temporarily to a new URI + { PROTOHTTP_RESPONSE_SEEOTHER, "See Other" }, //!< response can be found under a different URI + { PROTOHTTP_RESPONSE_NOTMODIFIED, "Not Modified" }, //!< response to conditional get when document has not changed + { PROTOHTTP_RESPONSE_USEPROXY, "Use Proxy" }, //!< requested resource must be accessed through proxy + { PROTOHTTP_RESPONSE_TEMPREDIRECT, "Temporary Redirect" }, //!< requested resource resides temporarily under a different URI + // 4xx - client error response + { PROTOHTTP_RESPONSE_BADREQUEST, "Bad Request" }, //!< request could not be understood by server due to malformed syntax + { PROTOHTTP_RESPONSE_UNAUTHORIZED, "Unauthorized" }, //!< request requires user authorization + { PROTOHTTP_RESPONSE_PAYMENTREQUIRED, "Payment Required" }, //!< reserved for future user + { PROTOHTTP_RESPONSE_FORBIDDEN, "Forbidden" }, //!< request understood, but server will not fulfill it + { PROTOHTTP_RESPONSE_NOTFOUND, "Not Found" }, //!< Request-URI not found + { PROTOHTTP_RESPONSE_METHODNOTALLOWED, "Method Not Allowed" }, //!< method specified in the Request-Line is not allowed + { PROTOHTTP_RESPONSE_NOTACCEPTABLE, "Not Acceptable" }, //!< resource incapable of generating content acceptable according to accept headers in request + { PROTOHTTP_RESPONSE_PROXYAUTHREQ, "Proxy Authentication Required" }, //!< client must first authenticate with proxy + { PROTOHTTP_RESPONSE_REQUESTTIMEOUT, "Request Timeout" }, //!< client did not produce response within server timeout + { PROTOHTTP_RESPONSE_CONFLICT, "Conflict" }, //!< request could not be completed due to a conflict with current state of the resource + { PROTOHTTP_RESPONSE_GONE, "Gone" }, //!< requested resource is no longer available and no forwarding address is known + { PROTOHTTP_RESPONSE_LENGTHREQUIRED, "Length Required" }, //!< a Content-Length header was not specified and is required + { PROTOHTTP_RESPONSE_PRECONFAILED, "Precondition Failed" }, //!< precondition given in request-header field(s) failed + { PROTOHTTP_RESPONSE_REQENTITYTOOLARGE, "Request Entity Too Large" }, //!< request entity is larger than the server is able or willing to process + { PROTOHTTP_RESPONSE_REQURITOOLONG, "Request-URI Too Long" }, //!< Request-URI is longer than the server is willing to interpret + { PROTOHTTP_RESPONSE_UNSUPPORTEDMEDIA, "Unsupported Media Type" }, //!< request entity is in unsupported format + { PROTOHTTP_RESPONSE_REQUESTRANGE, "Requested Range Not Satisfiable" },//!< invalid range in Range request header + { PROTOHTTP_RESPONSE_EXPECTATIONFAILED, "Expectation Failed" }, //!< expectation in Expect request-header field could not be met by server + // 5xx - server error response + { PROTOHTTP_RESPONSE_INTERNALSERVERERROR, "Internal Server Error" }, //!< an unexpected condition prevented the server from fulfilling the request + { PROTOHTTP_RESPONSE_NOTIMPLEMENTED, "Not Implemented" }, //!< the server does not support the functionality required to fulfill the request + { PROTOHTTP_RESPONSE_BADGATEWAY, "Bad Gateway" }, //!< invalid response from gateway server + { PROTOHTTP_RESPONSE_SERVICEUNAVAILABLE, "Service Unavailable" }, //!< unable to handle request due to a temporary overloading or maintainance + { PROTOHTTP_RESPONSE_GATEWAYTIMEOUT, "Gateway Timeout" }, //!< gateway or DNS server timeout + { PROTOHTTP_RESPONSE_HTTPVERSUNSUPPORTED, "HTTP Version Not Supported" }, //!< the server does not support the HTTP protocol version that was used in the request + { PROTOHTTP_RESPONSE_PENDING, "Unknown" }, //!< unknown response code +}; + +/*** Private Functions ************************************************************/ + +static int32_t _ProtoHttpServFormatHeader(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread); +static int32_t _ProtoHttpServUpdateSendHeader(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread); + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServLogPrintf + + \Description + Log printing for HttpServ server (compiles in all builds, unlike debug output). + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread log entry is for (may be NULL) + \Input *pFormat - printf format string + \Input ... - variable argument list + + \Version 08/03/2007 (jbrookes) Borrowed from DirtyCast +*/ +/********************************************************************************F*/ +static void _ProtoHttpServLogPrintf(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread, const char *pFormat, ...) +{ + char strText[2048]; + int32_t iOffset; + va_list Args; + + // format prefix to output buffer + if (pHttpThread != NULL) + { + iOffset = ds_snzprintf(strText, sizeof(strText), "protohttpserv: [%p][%02x] ", pHttpServ, pHttpThread->uHttpThreadId); + } + else + { + iOffset = ds_snzprintf(strText, sizeof(strText), "protohttpserv: [%p] ", pHttpServ); + } + + // format output + va_start(Args, pFormat); + ds_vsnprintf(strText+iOffset, sizeof(strText)-iOffset, pFormat, Args); + va_end(Args); + + // forward to callback, or print if no callback installed + if ((pHttpServ != NULL) && (pHttpServ->pLogCb != NULL)) + { + pHttpServ->pLogCb(strText, pHttpServ->pUserData); + } + else + { + NetPrintf(("%s", strText)); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServLogPrintfVerbose + + \Description + Log printing for HttpServ server (compiles in all builds, unlike debug output) + with varying verbosity levels + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread log entry is for (may be NULL) + \Input uCheckLevel - the level we are checking our internal uDebugLevel against + \Input *pFormat - printf format string + \Input ... - variable argument list + + \Version 05/02/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _ProtoHttpServLogPrintfVerbose(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread, uint16_t uCheckLevel, const char *pFormat, ...) +{ + char strText[2048]; + va_list Args; + + if (pHttpServ->uDebugLevel > uCheckLevel) + { + // format output + va_start(Args, pFormat); + ds_vsnprintf(strText, sizeof(strText), pFormat, Args); + va_end(Args); + + // log this information + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "%s", strText); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServGetThreadFromId + + \Description + Get HttpServ thread based on specified thread id + + \Input *pHttpServ - module state + \Input uHttpThreadId - thread id + + \Output + ProtoHttpServThread * - requested thread, or null if no match + + \Version 03/28/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static ProtoHttpServThreadT *_ProtoHttpServGetThreadFromId(ProtoHttpServRefT *pHttpServ, uint32_t uHttpThreadId) +{ + ProtoHttpServThreadT *pHttpThread; + // find thread with matching id + for (pHttpThread = pHttpServ->pThreadListHead; (pHttpThread != NULL) && (pHttpThread->uHttpThreadId != uHttpThreadId); pHttpThread = pHttpThread->pNext) + ; + // return thead to caller if found, else null + return(pHttpThread); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServGetResponseText + + \Description + Return response text for specified response code + + \Input eResponseCode - code to get text for + + \Output + const char * - response text + + \Version 09/13/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_ProtoHttpServGetResponseText(ProtoHttpResponseE eResponseCode) +{ + int32_t iResponse; + for (iResponse = 0; _ProtoHttpServ_Responses[iResponse].eResponse != PROTOHTTP_RESPONSE_PENDING; iResponse += 1) + { + if (_ProtoHttpServ_Responses[iResponse].eResponse == eResponseCode) + { + break; + } + } + return(_ProtoHttpServ_Responses[iResponse].pResponseText); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServSSLCreate + + \Description + Create ProtoSSL listen object + + \Input *pHttpServ - module state + \Input uPort - port to bind + \Input bReuseAddr - TRUE to set SO_REUSEADDR, else FALSE + \Input uFlags - flag field, use PROTOHTTPSERV_FLAG_* + + \Output + ProtoSSLRefT * - socket ref, or NULL + + \Version 10/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static ProtoSSLRefT *_ProtoHttpServSSLCreate(ProtoHttpServRefT *pHttpServ, uint16_t uPort, uint8_t bReuseAddr, uint32_t uFlags) +{ + struct sockaddr BindAddr; + ProtoSSLRefT *pProtoSSL; + int32_t iResult; + + // create the protossl ref + if ((pProtoSSL = ProtoSSLCreate()) == NULL) + { + return(NULL); + } + + // enable reuseaddr + if (bReuseAddr) + { + ProtoSSLControl(pProtoSSL, 'radr', 1, 0, NULL); + } + + // bind ssl to specified port + SockaddrInit(&BindAddr, AF_INET); + if (uFlags & PROTOHTTPSERV_FLAG_LOOPBACK) + { + SockaddrInSetAddr(&BindAddr, INADDR_LOOPBACK); + } + SockaddrInSetPort(&BindAddr, uPort); + if ((iResult = ProtoSSLBind(pProtoSSL, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE) + { + _ProtoHttpServLogPrintf(pHttpServ, NULL, "error %d binding to port\n", iResult); + ProtoSSLDestroy(pProtoSSL); + return(NULL); + } + + // return ref to caller + return(pProtoSSL); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServThreadAlloc + + \Description + Allocate and initialize an HttpThread object + + \Input *pHttpServ - module state + \Input *pProtoSSL - ProtoSSL ref for this thread + \Input *pRequestAddr - address connection request was made from + + \Output + ProtoHttpServThreadT * - newly allocated HttpThread object, or NULL on failure + + \Version 03/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static ProtoHttpServThreadT *_ProtoHttpServThreadAlloc(ProtoHttpServRefT *pHttpServ, ProtoSSLRefT *pProtoSSL, struct sockaddr *pRequestAddr) +{ + ProtoHttpServThreadT *pHttpThread; + // allocate and init thread memory + if ((pHttpThread = DirtyMemAlloc(sizeof(*pHttpThread), HTTPSERV_MEMID, pHttpServ->iMemGroup, pHttpServ->pMemGroupUserData)) == NULL) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "unable to alloc http thread\n"); + return(NULL); + } + ds_memclr(pHttpThread, sizeof(*pHttpThread)); + // allocate http thread streaming buffer + if ((pHttpThread->pBuffer = DirtyMemAlloc(HTTPSERV_BUFSIZE_DEFAULT, HTTPSERV_MEMID, pHttpServ->iMemGroup, pHttpServ->pMemGroupUserData)) == NULL) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "unable to alloc http thread buffer\n"); + return(NULL); + } + // init thread members + pHttpThread->pProtoSSL = pProtoSSL; + pHttpThread->uHttpThreadId = pHttpServ->uCurrThreadId++; + ds_memcpy_s(&pHttpThread->RequestAddr, sizeof(pHttpThread->RequestAddr), pRequestAddr, sizeof(*pRequestAddr)); + pHttpThread->iBufMax = HTTPSERV_BUFSIZE_DEFAULT; + // return ref to caller + return(pHttpThread); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServThreadFree + + \Description + Free an HttpThread object + + \Input *pHttpServ - module state + \Input *pHttpThread - HttpThread object to free + + \Version 03/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpServThreadFree(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 2, "destroying http thread\n"); + if (pHttpThread->pProtoSSL != NULL) + { + ProtoSSLDestroy(pHttpThread->pProtoSSL); + } + if (pHttpThread->pBuffer != NULL) + { + DirtyMemFree(pHttpThread->pBuffer, HTTPSERV_MEMID, pHttpServ->iMemGroup, pHttpServ->pMemGroupUserData); + } + DirtyMemFree(pHttpThread, HTTPSERV_MEMID, pHttpServ->iMemGroup, pHttpServ->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServThreadReallocBuf + + \Description + Reallocate buffer for HttpThread object + + \Input *pHttpServ - module state + \Input *pHttpThread - HttpThread object to realloc buffer for + \Input iBufSize - size to reallocate to + + \Version 03/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServThreadReallocBuf(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread, int32_t iBufSize) +{ + char *pBuffer; + if ((pBuffer = DirtyMemAlloc(iBufSize, HTTPMGR_MEMID, pHttpServ->iMemGroup, pHttpServ->pMemGroupUserData)) == NULL) + { + return(-1); + } + ds_memcpy(pBuffer, pHttpThread->pBuffer+pHttpThread->iBufOff, pHttpThread->iBufLen-pHttpThread->iBufOff); + DirtyMemFree(pHttpThread->pBuffer, HTTPMGR_MEMID, pHttpServ->iMemGroup, pHttpServ->pMemGroupUserData); + pHttpThread->pBuffer = pBuffer; + pHttpThread->iBufLen -= pHttpThread->iBufOff; + pHttpThread->iBufOff = 0; + pHttpThread->iBufMax = iBufSize; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServDisconnect + + \Description + Disconnect from a client + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + \Input bGraceful - TRUE=graceful disconnect, FALSE=hard close + + \Version 10/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpServDisconnect(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread, uint8_t bGraceful) +{ + if (bGraceful) + { + ProtoSSLDisconnect(pHttpThread->pProtoSSL); + pHttpThread->iDisconnectTimer = NetTick(); + } + else + { + pHttpThread->bConnected = FALSE; + pHttpThread->iDisconnectTimer = NetTick() - (HTTPSERV_DISCTIME+1); + } + pHttpThread->bDisconnecting = TRUE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServResetState + + \Description + Reset state after a transaction is completed + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Version 10/26/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpServResetState(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + ds_memclr(&pHttpThread->RequestInfo, sizeof(pHttpThread->RequestInfo)); + ds_memclr(&pHttpThread->ResponseInfo, sizeof(pHttpThread->ResponseInfo)); + + pHttpThread->bReceivedHeader = FALSE; + pHttpThread->bReceivedBody = FALSE; + pHttpThread->bSentBody = FALSE; + pHttpThread->iContentSent = 0; + pHttpThread->iBufLen = 0; + pHttpThread->iBufOff = 0; + pHttpThread->iChkLen = 0; + pHttpThread->iChkOvr = 0; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServParseHeader + + \Description + Parse a received header + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, negative=failure + + \Version 09/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServParseHeader(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + const char *pHdr, *pEnd; + char strName[128], strValue[4*1024], strMethod[8]; + int32_t iResult; + + // parse http first line for method, url, query, and version + pHdr = ProtoHttpUtilParseRequest(pHttpThread->RequestInfo.strHeader, strMethod, sizeof(strMethod), + pHttpThread->RequestInfo.strUrl, sizeof(pHttpThread->RequestInfo.strUrl), + pHttpThread->RequestInfo.strQuery, sizeof(pHttpThread->RequestInfo.strQuery), + &pHttpThread->RequestInfo.eRequestType, &pHttpThread->bHttp1_0); + // make sure it's a valid request + if (pHdr == NULL) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "invalid request headers\n"); + pHttpThread->ResponseInfo.eResponseCode = PROTOHTTP_RESPONSE_BADREQUEST; + return(-1); + } + + // update address + pHttpThread->RequestInfo.uAddr = SockaddrInGetAddr(&pHttpThread->RequestAddr); + + // log request info + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "url=%s\n", pHttpThread->RequestInfo.strUrl); + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "query=%s\n", pHttpThread->RequestInfo.strQuery); + + // detect if it is a 1.0 response; if so we default to closing the connection + if (pHttpThread->bHttp1_0) + { + pHttpThread->bConnectionClose = TRUE; + } + + // update the header so we dont have the unnecessary information + memmove(pHttpThread->RequestInfo.strHeader, pHdr, pHdr - pHttpThread->RequestInfo.strHeader); + + // parse header + for (iResult = 0; iResult >= 0; ) + { + // get next header name and value from header buffer + if ((iResult = ProtoHttpGetNextHeader(NULL, pHdr, strName, sizeof(strName), strValue, sizeof(strValue), &pEnd)) == 0) + { + // process header + if (!ds_stricmp(strName, "connection")) + { + if (!ds_stricmp(strValue, "close")) + { + pHttpThread->bConnectionClose = TRUE; + } + else if (!ds_stricmp(strValue, "keep-alive")) + { + pHttpThread->bConnectionKeepAlive = TRUE; + } + } + else if (!ds_stricmp(strName, "content-length") && !pHttpThread->bChunked) + { + pHttpThread->RequestInfo.iContentLength = ds_strtoll(strValue, NULL, 10); + } + else if (!ds_stricmp(strName, "content-type")) + { + ds_strnzcpy(pHttpThread->RequestInfo.strContentType, strValue, sizeof(pHttpThread->RequestInfo.strContentType)); + } + else if (!ds_stricmp(strName, "transfer-encoding")) + { + if ((pHttpThread->bChunked = !ds_stricmp(strValue, "chunked")) == TRUE) + { + pHttpThread->RequestInfo.iContentLength = -1; + } + } + else if (!ds_stricmp(strName, "user-agent")) + { + ds_strnzcpy(pHttpThread->strUserAgent, strValue, sizeof(pHttpThread->strUserAgent)); + } + else if (!ds_stricmp(strName, "expect")) + { + /* + RFC-2616 8.2.3 Use of 100 (Continue) Status + Upon receiving a request which includes an Expect request-header + field with the 100-continue expectation, an origin server MUST + either response with 100 (Continue) status and continue to + read from the input stream, or respond with a final status code. + The origin server MUST NOT wait for the request body buffer before + the 100 (Continue) response. If it responds with a final status + code, it MAY close the transport connection or it MAY continue + to read and discard and discard the rest of the request. It + MUST NOT perform the requested method of it returns a final + status code + + Since we support large payloads we will just respond with 100 + and continue to process the body + */ + if (!ds_stricmp(strValue, "100-continue")) + { + /* Currently we don't have a way to send a response back in the + flow and continue to process. To do this I'm just going to + set the response code, format and send the header then reset + the state */ + pHttpThread->ResponseInfo.eResponseCode = PROTOHTTP_RESPONSE_CONTINUE; + _ProtoHttpServFormatHeader(pHttpServ, pHttpThread); + _ProtoHttpServUpdateSendHeader(pHttpServ, pHttpThread); + pHttpThread->bFormattedHeader = FALSE; + pHttpThread->bSentHeader = FALSE; + } + } + else + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 2, "unprocessed header '%s'='%s'\n", strName, strValue); + } + } + + // move to next header + pHdr = pEnd; + } + + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 0, "processing %s %s from %a:%d user-agent=%s\n", + _ProtoHttpServ_strRequestNames[pHttpThread->RequestInfo.eRequestType], pHttpThread->RequestInfo.strUrl, + SockaddrInGetAddr(&pHttpThread->RequestAddr), SockaddrInGetPort(&pHttpThread->RequestAddr), + pHttpThread->strUserAgent); + + if ((iResult = pHttpServ->pHeaderCb(&pHttpThread->RequestInfo, &pHttpThread->ResponseInfo, pHttpServ->pUserData)) < 0) + { + return(-1); + } + + pHttpThread->bParsedHeader = TRUE; + pHttpThread->bProcessedRequest = FALSE; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServFormatHeader + + \Description + Format response header + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, negative=failure + + \Version 09/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServFormatHeader(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + struct tm TmTime; + char strTime[64]; + int32_t iBufLen, iBufMax = pHttpThread->iBufMax; + + // format a basic response header + iBufLen = ds_snzprintf(pHttpThread->pBuffer, iBufMax, "HTTP/1.1 %d %s\r\n", pHttpThread->ResponseInfo.eResponseCode, + _ProtoHttpServGetResponseText(pHttpThread->ResponseInfo.eResponseCode)); + + // date header; specify current GMT time + ds_secstotime(&TmTime, ds_timeinsecs()); + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Date: %s\r\n", + ds_timetostr(&TmTime, TIMETOSTRING_CONVERSION_RFC_0822, FALSE, strTime, sizeof(strTime))); + + // redirection? + if (pHttpThread->ResponseInfo.strLocation[0] != '\0') + { + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Location: %s\r\n", pHttpThread->ResponseInfo.strLocation); + } + if (pHttpThread->ResponseInfo.iContentLength > 0) + { + // set content type + if (*pHttpThread->ResponseInfo.strContentType != '\0') + { + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Content-type: %s\r\n", pHttpThread->ResponseInfo.strContentType); + } + + // set content length or transfer-encoding + if (pHttpThread->ResponseInfo.iChunkLength == 0) + { + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Content-length: %qd\r\n", pHttpThread->ResponseInfo.iContentLength); + } + else + { + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Transfer-Encoding: Chunked\r\n"); + } + } + else + { + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Content-length: 0\r\n"); + } + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Server: %s/ProtoHttpServ %d.%d/DS %d.%d.%d.%d.%d (" DIRTYCODE_PLATNAME ")\r\n", + pHttpServ->strServerName, (HTTPSERV_VERSION>>8)&0xff, HTTPSERV_VERSION&0xff, DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, + DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH); + if (pHttpThread->bConnectionClose) + { + iBufLen += ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "Connection: Close\r\n"); + } + + iBufLen += ds_strsubzcat(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, pHttpThread->ResponseInfo.strHeader, pHttpThread->ResponseInfo.iHeaderLen); + + // format footer, update thread data + pHttpThread->iBufLen = ds_snzprintf(pHttpThread->pBuffer+iBufLen, iBufMax-iBufLen, "\r\n") + iBufLen; + pHttpThread->iBufOff = 0; + pHttpThread->bFormattedHeader = TRUE; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServProcessRequest + + \Description + Process request + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + uint8_t - true=request complete, false=request still pending +*/ +/********************************************************************************F*/ +static uint8_t _ProtoHttpServProcessRequest(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iResult; + + // make user callback + if ((iResult = pHttpServ->pRequestCb(&pHttpThread->RequestInfo, &pHttpThread->ResponseInfo, pHttpServ->pUserData)) < 0) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "error %d processing request\n", iResult); + pHttpThread->bConnectionClose = TRUE; + return(TRUE); + } + // if the request is not complete return + else if (iResult != 0) + { + return(FALSE); + } + + // if we are not transferring anymore data and the user doesn't want to keep the connection open, we can close the connection + if (pHttpThread->ResponseInfo.iContentLength == 0 && !pHttpThread->bConnectionKeepAlive) + { + pHttpThread->bConnectionClose = TRUE; + } + + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServUpdateListen + + \Description + Update HttpServ while listening for a new connection + + \Input *pHttpServ - module state + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 09/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServUpdateListen(ProtoHttpServRefT *pHttpServ) +{ + ProtoSSLRefT *pProtoSSL; + ProtoHttpServThreadT *pHttpThread; + struct sockaddr RequestAddr; + int32_t iAddrLen = sizeof(RequestAddr); + + // don't try Accept() if poll hint says nothing to read +#if 0 //$$ TODO - fix this to work on linux + if (ProtoSSLStat(pHttpServ->pListenSSL, 'read', NULL, 0) == 0) + { + return(0); + } +#endif + // accept incoming connection + SockaddrInit(&RequestAddr, AF_INET); + if ((pProtoSSL = ProtoSSLAccept(pHttpServ->pListenSSL, pHttpServ->iSecure, &RequestAddr, &iAddrLen)) == NULL) + { + return(0); + } + // allocate an http thread + if ((pHttpThread = _ProtoHttpServThreadAlloc(pHttpServ, pProtoSSL, &RequestAddr)) == NULL) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "unable to alloc http thread\n"); + ProtoSSLDestroy(pProtoSSL); + return(0); + } + + // is the thread list empty? + if (pHttpServ->pThreadListHead == NULL) + { + pHttpServ->pThreadListHead = pHttpThread; // insert newly allocated httpthread as the first entry in the list + } + else + { + pHttpServ->pThreadListTail->pNext = pHttpThread; // enqueue newly allocated httpthread after the current tail entry in the list + } + pHttpServ->pThreadListTail = pHttpThread; // mark newly allocated httpthread as then new tail entry in the list + + // log connecting request + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "connecting to %a:%d (%s)\n", SockaddrInGetAddr(&pHttpThread->RequestAddr), + SockaddrInGetPort(&pHttpThread->RequestAddr), pHttpServ->iSecure ? "secure" : "insecure"); + + // set server certificate and private key, if specified + if (pHttpServ->iSecure) + { + ProtoSSLControl(pHttpThread->pProtoSSL, 'scrt', pHttpServ->iServerCertLen, 0, pHttpServ->pServerCert); + ProtoSSLControl(pHttpThread->pProtoSSL, 'skey', pHttpServ->iServerKeyLen, 0, pHttpServ->pServerKey); + ProtoSSLControl(pHttpThread->pProtoSSL, 'ccrt', pHttpServ->iClientCertLvl, 0, NULL); + if (pHttpServ->uSslVersionMin != 0) + { + ProtoSSLControl(pHttpThread->pProtoSSL, 'vmin', pHttpServ->uSslVersionMin, 0, NULL); + } + if (pHttpServ->uSslVersion != 0) + { + ProtoSSLControl(pHttpThread->pProtoSSL, 'vers', pHttpServ->uSslVersion, 0, NULL); + } + ProtoSSLControl(pHttpThread->pProtoSSL, 'ciph', pHttpServ->uSslCiphers, 0, NULL); + if (pHttpServ->strAlpn[0] != '\0') + { + ProtoSSLControl(pHttpThread->pProtoSSL, 'alpn', 0, 0, pHttpServ->strAlpn); + } + } + pHttpThread->bConnected = FALSE; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServSocketClose + + \Description + Handle socket close on connect/send/recv + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + \Input iResult - socket operation result + \Input *pOperation - operation type (conn, recv, send) + + \Version 10/31/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpServSocketClose(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread, int32_t iResult, const char *pOperation) +{ + ProtoSSLAlertDescT AlertDesc; + int32_t iAlert; + + if ((iAlert = ProtoSSLStat(pHttpThread->pProtoSSL, 'alrt', &AlertDesc, sizeof(AlertDesc))) != 0) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 0, "%s ssl alert %s (sslerr=%d, hResult=0x%08x); closing connection\n", (iAlert == 1) ? "recv" : "sent", + AlertDesc.pAlertDesc, ProtoSSLStat(pHttpThread->pProtoSSL, 'fail', NULL, 0), ProtoSSLStat(pHttpThread->pProtoSSL, 'hres', NULL, 0)); + } + else + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 0, "%s returned %d (sockerr=%d, sslerr=%d, hResult=0x%08x); closing connection\n", pOperation, iResult, + ProtoSSLStat(pHttpThread->pProtoSSL, 'serr', NULL, 0), ProtoSSLStat(pHttpThread->pProtoSSL, 'fail', NULL, 0), + ProtoSSLStat(pHttpThread->pProtoSSL, 'hres', NULL, 0)); + } + + _ProtoHttpServDisconnect(pHttpServ, pHttpThread, 0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServUpdateConnect + + \Description + Update HttpServ while establishing a new connection + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 09/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServUpdateConnect(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iStat; + + if ((iStat = ProtoSSLStat(pHttpThread->pProtoSSL, 'stat', NULL, 0)) > 0) + { + if (pHttpServ->iSecure) + { + char strTlsVersion[16], strCipherSuite[32], strResumed[] = " (resumed)"; + ProtoSSLStat(pHttpThread->pProtoSSL, 'vers', strTlsVersion, sizeof(strTlsVersion)); + ProtoSSLStat(pHttpThread->pProtoSSL, 'ciph', strCipherSuite, sizeof(strCipherSuite)); + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "connected to %a:%d using %s and %s%s\n", + SockaddrInGetAddr(&pHttpThread->RequestAddr), SockaddrInGetPort(&pHttpThread->RequestAddr), + strTlsVersion, strCipherSuite, ProtoSSLStat(pHttpThread->pProtoSSL, 'resu', NULL, 0) ? strResumed : ""); + } + else + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "connected to %a:%d (insecure)\n", + SockaddrInGetAddr(&pHttpThread->RequestAddr), SockaddrInGetPort(&pHttpThread->RequestAddr)); + } + pHttpThread->bConnected = TRUE; + pHttpThread->iIdleTimer = NetTick(); + return(1); + } + else if (iStat < 0) + { + _ProtoHttpServSocketClose(pHttpServ, pHttpThread, iStat, "conn"); + return(-1); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServUpdateRecvHeader + + \Description + Update HttpServ while receiving header + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 09/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServUpdateRecvHeader(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iResult; + + if ((iResult = ProtoSSLRecv(pHttpThread->pProtoSSL, pHttpThread->pBuffer+pHttpThread->iBufLen, pHttpThread->iBufMax-pHttpThread->iBufLen)) > 0) + { + char *pHdrEnd; + int32_t iHdrLen; + + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "recv %d bytes\n", iResult); + + // update received data size + pHttpThread->iBufLen += iResult; + + // reset idle timeout + pHttpThread->iIdleTimer = NetTick(); + + // check for header termination + if ((pHdrEnd = strstr(pHttpThread->pBuffer, "\r\n\r\n")) != NULL) + { + // we want to keep the final header EOL + pHdrEnd += 2; + + // copy header to buffer + iHdrLen = (int32_t)(pHdrEnd - pHttpThread->pBuffer); + ds_strsubzcpy(pHttpThread->RequestInfo.strHeader, sizeof(pHttpThread->RequestInfo.strHeader), pHttpThread->pBuffer, iHdrLen); + if (pHttpServ->uDebugLevel > 1) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "received %d byte header\n", iHdrLen); + NetPrintWrap(pHttpThread->RequestInfo.strHeader, 132); + } + + // skip header/body seperator + pHdrEnd += 2; + iHdrLen += 2; + + // remove header from buffer + memmove(pHttpThread->pBuffer, pHdrEnd, pHttpThread->iBufLen - iHdrLen); + pHttpThread->iBufLen -= iHdrLen; + pHttpThread->pBuffer[pHttpThread->iBufLen] = '\0'; + + // we've received the header + pHttpThread->bReceivedHeader = TRUE; + + // reset for next state + pHttpThread->bConnectionClose = FALSE; + pHttpThread->bParsedHeader = FALSE; + pHttpThread->bFormattedHeader = FALSE; + pHttpThread->bSentHeader = FALSE; + } + + // return whether we have parsed the header or not + iResult = pHttpThread->bReceivedHeader ? 1 : 0; + } + else if (iResult < 0) + { + _ProtoHttpServSocketClose(pHttpServ, pHttpThread, iResult, "recv"); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServProcessData + + \Description + Process received data + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 03/20/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServProcessData(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iDataSize; + if ((iDataSize = pHttpServ->pReceiveCb(&pHttpThread->RequestInfo, pHttpThread->pBuffer+pHttpThread->iBufOff, pHttpThread->iBufLen-pHttpThread->iBufOff, pHttpServ->pUserData)) > 0) + { + pHttpThread->RequestInfo.iContentRecv += iDataSize; + pHttpThread->iBufOff += iDataSize; + + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "wrote %d bytes to file (%qd total)\n", iDataSize, pHttpThread->RequestInfo.iContentRecv); + + // if we've written all of the data reset buffer pointers + if (pHttpThread->iBufOff == pHttpThread->iBufLen) + { + pHttpThread->iBufOff = 0; + pHttpThread->iBufLen = 0; + } + } + return(iDataSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServCompactBuffer + + \Description + Compact the buffer + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Version 12/06/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpServCompactBuffer(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + // compact the buffer + if (pHttpThread->iBufOff < pHttpThread->iBufLen) + { + memmove(pHttpThread->pBuffer, pHttpThread->pBuffer+pHttpThread->iBufOff, pHttpThread->iBufLen-pHttpThread->iBufOff); + } + pHttpThread->iBufLen -= pHttpThread->iBufOff; + pHttpThread->iBufOff = 0; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServProcessChunkData + + \Description + Process received chunk data + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 03/20/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServProcessChunkData(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iChkLeft, iDataSize; + + // do we need a new chunk header? + if (pHttpThread->iChkLen == 0) + { + char *s = pHttpThread->pBuffer+pHttpThread->iBufOff, *s2; + char *t = pHttpThread->pBuffer+pHttpThread->iBufLen-1; + + // make sure we have a complete chunk header + for (s2=s; (s2 < t) && ((s2[0] != '\r') || (s2[1] != '\n')); s2++) + ; + if (s2 == t) + { + // if we're out of room, compact buffer + if (pHttpThread->iBufLen == pHttpThread->iBufMax) + { + _ProtoHttpServCompactBuffer(pHttpServ, pHttpThread); + } + return(0); + } + + // read chunk header; handle end of input + if ((pHttpThread->iChkLen = (int32_t)strtol(s, NULL, 16)) == 0) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "processing end chunk\n"); + pHttpThread->iBufOff = 0; + pHttpThread->iBufLen = 0; + pHttpThread->RequestInfo.iContentLength = pHttpThread->RequestInfo.iContentRecv; + return(0); + } + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "processing %d byte chunk\n", pHttpThread->iChkLen); + + // reset chunk read counter + pHttpThread->iChkRcv = 0; + + // consume chunk header and \r\n trailer + pHttpThread->iBufOff += s2-s+2; + } + + // if chunk size plus trailer is bigger than our buffer, realloc our buffer larger to accomodate, so we can buffer an entire chunk in our buffer + if ((pHttpThread->iChkLen+2) > pHttpThread->iBufMax) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 0, "increasing buffer to handle %d byte chunk\n", pHttpThread->iChkLen); + if (_ProtoHttpServThreadReallocBuf(pHttpServ, pHttpThread, pHttpThread->iChkLen+2) < 0) // chunk plus trailer + { + return(-1); + } + } + + // get how much of the chunk we have left to read + iChkLeft = pHttpThread->iChkLen - pHttpThread->iChkRcv; + + // get data size avaialble; make sure we have all of the remaining data and chunk trailer available + if ((iDataSize = pHttpThread->iBufLen-pHttpThread->iBufOff) < (iChkLeft+2)) + { + // compact buffer to make room for more data + _ProtoHttpServCompactBuffer(pHttpServ, pHttpThread); + return(0); + } + // clamp data size to whatever we have left to read + iDataSize = iChkLeft; + + // write the data + if ((iDataSize = pHttpServ->pReceiveCb(&pHttpThread->RequestInfo, pHttpThread->pBuffer+pHttpThread->iBufOff, iDataSize, pHttpServ->pUserData)) > 0) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "wrote %d bytes to file (%qd total)\n", iDataSize, pHttpThread->RequestInfo.iContentRecv+iDataSize); + + pHttpThread->RequestInfo.iContentRecv += iDataSize; + pHttpThread->iBufOff += iDataSize; + pHttpThread->iChkRcv += iDataSize; + + // did we consume all of the chunk data? + if (pHttpThread->iChkRcv == pHttpThread->iChkLen) + { + // consume chunk trailer, reset chunk length + pHttpThread->iBufOff += 2; + pHttpThread->iChkLen = 0; + } + + // if we've written all of the data reset buffer pointers + if (pHttpThread->iBufOff == pHttpThread->iBufLen) + { + pHttpThread->iBufOff = 0; + pHttpThread->iBufLen = 0; + } + } + return(iDataSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServUpdateRecvBody + + \Description + Update HttpServ while receiving body + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 09/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServUpdateRecvBody(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iDataSize, iResult; + + while ((pHttpThread->RequestInfo.iContentRecv < pHttpThread->RequestInfo.iContentLength) || (pHttpThread->RequestInfo.iContentLength == -1)) + { + // need new data? + while (pHttpThread->iBufLen < (signed)pHttpThread->iBufMax) + { + iResult = ProtoSSLRecv(pHttpThread->pProtoSSL, pHttpThread->pBuffer + pHttpThread->iBufLen, pHttpThread->iBufMax - pHttpThread->iBufLen); + if (iResult > 0) + { + pHttpThread->iBufLen += iResult; + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "recv %d bytes\n", iResult); + // reset idle timeout + pHttpThread->iIdleTimer = NetTick(); + } + else if ((iResult == SOCKERR_CLOSED) && (pHttpThread->RequestInfo.iContentLength == -1)) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "keep-alive connection closed by remote host\n"); + pHttpThread->RequestInfo.iContentLength = pHttpThread->RequestInfo.iContentRecv + pHttpThread->iBufLen; + break; + } + else if (iResult < 0) + { + _ProtoHttpServSocketClose(pHttpServ, pHttpThread, iResult, "recv"); + return(-2); + } + else + { + break; + } + } + + // if we have data, write it out + if (pHttpThread->iBufLen > 0) + { + if (!pHttpThread->bChunked) + { + // write out as much data as we can + iDataSize = _ProtoHttpServProcessData(pHttpServ, pHttpThread); + } + else + { + // write out as many chunks as we have + while((iDataSize = _ProtoHttpServProcessChunkData(pHttpServ, pHttpThread)) > 0) + ; + } + + if (iDataSize < 0) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "error %d writing to file\n", iDataSize); + pHttpThread->ResponseInfo.eResponseCode = PROTOHTTP_RESPONSE_INTERNALSERVERERROR; + return(-1); + } + } + else + { + break; + } + } + + // check for upload completion + if (pHttpThread->RequestInfo.iContentRecv == pHttpThread->RequestInfo.iContentLength) + { + // done receiving response + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "received upload of %qd bytes\n", pHttpThread->RequestInfo.iContentRecv); + + // trigger callback to indicate transaction is complete + pHttpServ->pReceiveCb(&pHttpThread->RequestInfo, NULL, 0, pHttpServ->pUserData); + + // done with response + pHttpThread->bReceivedBody = TRUE; + pHttpThread->ResponseInfo.eResponseCode = PROTOHTTP_RESPONSE_CREATED; + return(1); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServUpdateSendHeader + + \Description + Update HttpServ while sending header + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 09/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServUpdateSendHeader(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iResult; + + // send data to requester + if ((iResult = ProtoSSLSend(pHttpThread->pProtoSSL, pHttpThread->pBuffer + pHttpThread->iBufOff, pHttpThread->iBufLen - pHttpThread->iBufOff)) > 0) + { + pHttpThread->iBufOff += iResult; + + // reset idle timeout + pHttpThread->iIdleTimer = NetTick(); + + // are we done? + if (pHttpThread->iBufOff == pHttpThread->iBufLen) + { + if (pHttpServ->uDebugLevel > 1) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "sent %d byte header\n", pHttpThread->iBufOff); + NetPrintWrap(pHttpThread->pBuffer, 132); + } + if (pHttpThread->ResponseInfo.iContentLength == 0) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 0, "sent %d response (no body)\n", pHttpThread->ResponseInfo.eResponseCode); + } + pHttpThread->bSentHeader = TRUE; + pHttpThread->iBufOff = 0; + pHttpThread->iBufLen = 0; + iResult = 1; + } + else + { + iResult = 0; + } + } + else if (iResult < 0) + { + _ProtoHttpServSocketClose(pHttpServ, pHttpThread, iResult, "send"); + pHttpThread->iBufOff = pHttpThread->iBufLen; + return(-1); + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServUpdateSendBody + + \Description + Update HttpServ while sending response + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Output + int32_t - positive=success, zero=in progress, negative=failure + + \Version 09/12/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpServUpdateSendBody(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + int32_t iResult, iReadOff, iReadMax; + uint8_t bError; + + // reserve space for chunk header + if (pHttpThread->ResponseInfo.iChunkLength != 0) + { + iReadOff = 10; + iReadMax = pHttpThread->ResponseInfo.iChunkLength; + } + else + { + iReadOff = 0; + iReadMax = pHttpThread->iBufMax; + } + + // while there is content to be sent + for (bError = FALSE; pHttpThread->iContentSent < pHttpThread->ResponseInfo.iContentLength; ) + { + // need new data? + if (pHttpThread->iBufOff == pHttpThread->iBufLen) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 2, "sent=%qd len=%qd\n", pHttpThread->iContentSent, pHttpThread->ResponseInfo.iContentLength); + // reset chunk overhead tracker + pHttpThread->iChkOvr = 0; + + // read the data + if ((pHttpThread->iBufLen = pHttpServ->pSendCb(&pHttpThread->ResponseInfo, pHttpThread->pBuffer+iReadOff, iReadMax-iReadOff, pHttpServ->pUserData)) > 0) + { + pHttpThread->ResponseInfo.iContentRead += pHttpThread->iBufLen; + pHttpThread->iBufOff = 0; + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 2, "read %d bytes from file (%qd total)\n", pHttpThread->iBufLen, pHttpThread->ResponseInfo.iContentRead); + + // if chunked, fill in chunk header based on amount of data read + if (pHttpThread->ResponseInfo.iChunkLength != 0) + { + char strHeader[11]; + + // format chunk header before data + ds_snzprintf(strHeader, sizeof(strHeader), "%08x\r\n", pHttpThread->iBufLen); + ds_memcpy(pHttpThread->pBuffer, strHeader, iReadOff); + pHttpThread->iBufLen += iReadOff; + pHttpThread->iChkOvr += iReadOff; + + // add chunk trailer after data + pHttpThread->pBuffer[pHttpThread->iBufLen+0] = '\r'; + pHttpThread->pBuffer[pHttpThread->iBufLen+1] = '\n'; + pHttpThread->iBufLen += 2; + pHttpThread->iChkOvr += 2; + + // if this is the last of the data, add terminating chunk + if (pHttpThread->ResponseInfo.iContentRead == pHttpThread->ResponseInfo.iContentLength) + { + ds_snzprintf(strHeader, sizeof(strHeader), "%08x\r\n", 0); + ds_memcpy(pHttpThread->pBuffer+pHttpThread->iBufLen, strHeader, iReadOff); + pHttpThread->iBufLen += iReadOff; + pHttpThread->iChkOvr += iReadOff; + + pHttpThread->pBuffer[pHttpThread->iBufLen+0] = '\r'; + pHttpThread->pBuffer[pHttpThread->iBufLen+1] = '\n'; + pHttpThread->iBufLen += 2; + pHttpThread->iChkOvr += 2; + } + } + } + else + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "error %d reading from file\n", pHttpThread->iBufLen); + _ProtoHttpServDisconnect(pHttpServ, pHttpThread, FALSE); + bError = TRUE; + break; + } + } + + // do we have buffered data to send? + if (pHttpThread->iBufOff < pHttpThread->iBufLen) + { + int32_t iSendLen = pHttpThread->iBufLen - pHttpThread->iBufOff; + #if HTTPSERV_CHUNKWAITTEST + if (iSendLen > 1) iSendLen -= 1; + #endif + iResult = ProtoSSLSend(pHttpThread->pProtoSSL, pHttpThread->pBuffer + pHttpThread->iBufOff, iSendLen); + if (iResult > 0) + { + pHttpThread->iBufOff += iResult; + pHttpThread->iContentSent += iResult; + if (pHttpThread->iChkOvr > 0) + { + pHttpThread->iContentSent -= pHttpThread->iChkOvr; + pHttpThread->iChkOvr = 0; + } + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 2, "sent %d bytes (%qd total)\n", iResult, pHttpThread->iContentSent); + // reset idle timeout + pHttpThread->iIdleTimer = NetTick(); + #if HTTPSERV_CHUNKWAITTEST + NetConnSleep(1000); + #endif + } + else if (iResult < 0) + { + _ProtoHttpServSocketClose(pHttpServ, pHttpThread, iResult, "send"); + bError = TRUE; + break; + } + else + { + break; + } + } + } + + // check for send completion + if ((pHttpThread->iContentSent == pHttpThread->ResponseInfo.iContentLength) || (bError == TRUE)) + { + // done sending response + if (pHttpThread->ResponseInfo.iContentLength != 0) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 0, "sent %d response (%qd bytes)\n", pHttpThread->ResponseInfo.eResponseCode, pHttpThread->iContentSent); + } + + // trigger callback to indicate transaction is complete + pHttpServ->pSendCb(&pHttpThread->ResponseInfo, NULL, 0, pHttpServ->pUserData); + + // done with response + pHttpThread->bSentBody = TRUE; + return(1); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpServUpdateThread + + \Description + Update an HttpServ thread + + \Input *pHttpServ - module state + \Input *pHttpThread - http thread (connection-specific) + + \Version 09/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoHttpServUpdateThread(ProtoHttpServRefT *pHttpServ, ProtoHttpServThreadT *pHttpThread) +{ + uint32_t uCurTick = NetTick(); + int32_t iTimeout; + + // if no thread, or we are disconnected, nothing to update + if (pHttpThread->pProtoSSL == NULL) + { + return; + } + + // update ProtoSSL object + ProtoSSLUpdate(pHttpThread->pProtoSSL); + + // poll for connection completion + if ((pHttpThread->bConnected == FALSE) && (_ProtoHttpServUpdateConnect(pHttpServ, pHttpThread) <= 0)) + { + return; + } + + // get timeout based on whether we are processing a request or not + iTimeout = pHttpThread->bReceivedHeader ? pHttpServ->iRequestTimeout : pHttpServ->iIdleTimeout; + + // see if we should timeout the connection + if (NetTickDiff(uCurTick, pHttpThread->iIdleTimer) > iTimeout) + { + if (pHttpThread->bReceivedHeader == TRUE) + { + _ProtoHttpServLogPrintf(pHttpServ, pHttpThread, "closing connection (request timeout)\n"); + } + else + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 1, "closing connection (idle timeout)\n"); + } + _ProtoHttpServDisconnect(pHttpServ, pHttpThread, FALSE); + return; + } + + // receive the header + if ((pHttpThread->bReceivedHeader == FALSE) && (_ProtoHttpServUpdateRecvHeader(pHttpServ, pHttpThread) <= 0)) + { + return; + } + + // if disconnecting, early out (this is intentionally after recv) + if (pHttpThread->bDisconnecting) + { + return; + } + + // parse headers + if ((pHttpThread->bParsedHeader == FALSE) && (_ProtoHttpServParseHeader(pHttpServ, pHttpThread) < 0)) + { + // If parsing header failed do not continue to do any processing, + // send a response back and close the connection + pHttpThread->bParsedHeader = TRUE; + pHttpThread->bReceivedBody = TRUE; + pHttpThread->bProcessedRequest = TRUE; + pHttpThread->bConnectionClose = TRUE; + return; + } + + // if data needed to download + if (pHttpThread->RequestInfo.iContentLength > 0 || pHttpThread->RequestInfo.iContentLength == -1) + { + if (pHttpThread->bReceivedBody == FALSE) + { + int32_t iResult; + if ((iResult = _ProtoHttpServUpdateRecvBody(pHttpServ, pHttpThread)) < 0) + { + // If receiving/processing the body failed do not continue to receive, + // send a response back and close the connection + pHttpThread->bReceivedBody = TRUE; + pHttpThread->bProcessedRequest = TRUE; + pHttpThread->bConnectionClose = TRUE; + return; + } + else if (iResult == 0) + { + return; + } + } + } + + // process the request + if (pHttpThread->bProcessedRequest == FALSE) + { + if (_ProtoHttpServProcessRequest(pHttpServ, pHttpThread) == FALSE) + { + return; + } + // mark that we've processed the request + pHttpThread->bProcessedRequest = TRUE; + } + + // format response header + if ((pHttpThread->bFormattedHeader == FALSE) && (_ProtoHttpServFormatHeader(pHttpServ, pHttpThread) < 0)) + { + return; + } + + // send response header + if ((pHttpThread->bSentHeader == FALSE) && (_ProtoHttpServUpdateSendHeader(pHttpServ, pHttpThread) <= 0)) + { + return; + } + + // process body data, if any + if (pHttpThread->ResponseInfo.iContentLength > 0 || pHttpThread->ResponseInfo.iChunkLength > 0) + { + if ((pHttpThread->bSentBody == FALSE) && (_ProtoHttpServUpdateSendBody(pHttpServ, pHttpThread) <= 0)) + { + return; + } + } + + // make sure we've sent all of the data + if ((pHttpThread->pProtoSSL != NULL) && (ProtoSSLStat(pHttpThread->pProtoSSL, 'send', NULL, 0) > 0)) + { + return; + } + + // if no keep-alive initiate disconnection + if (pHttpThread->bConnectionClose == TRUE) + { + _ProtoHttpServLogPrintfVerbose(pHttpServ, pHttpThread, 0, "closing connection\n"); + _ProtoHttpServDisconnect(pHttpServ, pHttpThread, TRUE); + } + + // reset transaction state + _ProtoHttpServResetState(pHttpServ, pHttpThread); +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServCreate + + \Description + Create an HttpServ instance + + \Input iPort - port to listen on + \Input iSecure - TRUE if secure, else FALSE + \Input *pName - name of server, used in Server: header + + \Output + ProtoHttpServRefT * - pointer to state, or null on failure + + \Version 12/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoHttpServRefT *ProtoHttpServCreate(int32_t iPort, int32_t iSecure, const char *pName) +{ + return(ProtoHttpServCreate2(iPort, iSecure, pName, 0)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServCreate2 + + \Description + Create an HttpServ instance + + \Input iPort - port to listen on + \Input iSecure - TRUE if secure, else FALSE + \Input *pName - name of server, used in Server: header + \Input uFlags - flag field, use PROTOHTTPSERV_FLAG_* + + \Output + ProtoHttpServRefT * - pointer to state, or null on failure + + \Version 02/22/2016 (amakoukji) +*/ +/********************************************************************************F*/ +ProtoHttpServRefT *ProtoHttpServCreate2(int32_t iPort, int32_t iSecure, const char *pName, uint32_t uFlags) +{ + ProtoHttpServRefT *pHttpServ; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate module state + if ((pHttpServ = DirtyMemAlloc(sizeof(*pHttpServ), HTTPSERV_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + _ProtoHttpServLogPrintf(NULL, NULL, "unable to allocate module state\n"); + return(NULL); + } + ds_memclr(pHttpServ, sizeof(*pHttpServ)); + // save memgroup (will be used in ProtoHttpDestroy) + pHttpServ->iMemGroup = iMemGroup; + pHttpServ->pMemGroupUserData = pMemGroupUserData; + pHttpServ->uDebugLevel = 1; + + // create a protossl listen ref + if ((pHttpServ->pListenSSL = _ProtoHttpServSSLCreate(pHttpServ, iPort, TRUE, uFlags)) == NULL) + { + _ProtoHttpServLogPrintf(pHttpServ, NULL, "could not create ssl ref on port %d\n", iPort); + ProtoHttpServDestroy(pHttpServ); + return(NULL); + } + // start listening + if (ProtoHttpServListen(pHttpServ, 2) != SOCKERR_NONE) + { + ProtoHttpServDestroy(pHttpServ); + return(NULL); + } + + // save settings + pHttpServ->iSecure = iSecure; + ds_strnzcpy(pHttpServ->strServerName, pName, sizeof(pHttpServ->strServerName)); + + // initialize default values + pHttpServ->uCurrThreadId = 1; + pHttpServ->uSslCiphers = PROTOSSL_CIPHER_ALL; + pHttpServ->iIdleTimeout = HTTPSERV_IDLETIMEOUT_DEFAULT; + pHttpServ->iRequestTimeout = HTTPSERV_REQUESTTIMEOUT_DEFAULT; + + // log success + _ProtoHttpServLogPrintfVerbose(pHttpServ, NULL, 0, "listening on port %d (%s)\n", iPort, iSecure ? "secure" : "insecure"); + + // return new ref to caller + return(pHttpServ); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServListen + + \Description + Listens on the listen socket with the specified settings + + \Input *pHttpServ - module state + \Input iBacklog - listen backlog setting + + \Output + int32_t - result of the listen call + + \Notes + This function is not required to be called for the server to work. + The create functions call it on your behalf with our default backlog, + this is only required for users that want to update settings. + + \Version 05/02/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpServListen(ProtoHttpServRefT *pHttpServ, int32_t iBacklog) +{ + int32_t iResult; + + // start listening + if ((iResult = ProtoSSLListen(pHttpServ->pListenSSL, iBacklog)) != SOCKERR_NONE) + { + _ProtoHttpServLogPrintf(pHttpServ, NULL, "error listening on socket (err=%d)\n", iResult); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServDestroy + + \Description + Destroy an HttpServ instance + + \Input *pHttpServ - module state to destroy + + \Version 12/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoHttpServDestroy(ProtoHttpServRefT *pHttpServ) +{ + ProtoHttpServThreadT *pHttpThread, *pHttpThreadNext; + + if (pHttpServ->pListenSSL != NULL) + { + ProtoSSLDestroy(pHttpServ->pListenSSL); + } + + for (pHttpThread = pHttpServ->pThreadListHead; pHttpThread != NULL; pHttpThread = pHttpThreadNext) + { + pHttpThreadNext = pHttpThread->pNext; + _ProtoHttpServThreadFree(pHttpServ, pHttpThread); + } + + DirtyMemFree(pHttpServ, HTTPSERV_MEMID, pHttpServ->iMemGroup, pHttpServ->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServCallback + + \Description + Set required callback functions for HttpServ to use. + + \Input *pHttpServ - module state + \Input *pRequestCb - request handler callback + \Input *pReceiveCb - inbound data handler callback + \Input *pSendCb - outbound data handler callback + \Input *pHeaderCb - inbound header callback + \Input *pLogCb - logging function to use (optional) + \Input *pUserData - user data for callbacks + + \Version 12/11/2013 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoHttpServCallback(ProtoHttpServRefT *pHttpServ, ProtoHttpServRequestCbT *pRequestCb, ProtoHttpServReceiveCbT *pReceiveCb, ProtoHttpServSendCbT *pSendCb, ProtoHttpServHeaderCbT *pHeaderCb, ProtoHttpServLogCbT *pLogCb, void *pUserData) +{ + pHttpServ->pRequestCb = pRequestCb; + pHttpServ->pReceiveCb = pReceiveCb; + pHttpServ->pSendCb = pSendCb; + pHttpServ->pHeaderCb = pHeaderCb; + pHttpServ->pLogCb = pLogCb; + pHttpServ->pUserData = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServControl + + \Description + Set behavior of module, based on selector input + + \Input *pHttpServ - reference pointer + \Input iSelect - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR DESCRIPTION + 'alpn' Set alpn string (pValue=string) + 'ccrt' Set client certificate level (0=disabled, 1=requested, 2=required) + 'ciph' Set supported cipher suites (iValue=PROTOSSL_CIPHER_*; default=PROTOSSL_CIPHER_ALL) + 'idle' Set idle timeout (iValue=timeout; default=5 minutes) + 'scrt' Set certificate (pValue=cert, iValue=len) + 'skey' Set private key (pValue=key, iValue=len) + 'spam' Set debug level (iValue=level) + 'time' Set request timeout in milliseconds (iValue=timeout; default=30 seconds) + 'vers' Set server-supported maximum SSL version (iValue=version; default=ProtoSSL default) + 'vmin' Set server-required minimum SSL version (iValue=version; default=ProtoSSL default) + \endverbatim + + \Version 12/12/2013 (jbrookes) +*/ +/*******************************************************************************F*/ +int32_t ProtoHttpServControl(ProtoHttpServRefT *pHttpServ, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iSelect == 'alpn') + { + ds_strnzcpy(pHttpServ->strAlpn, (const char *)pValue, sizeof(pHttpServ->strAlpn)); + return(0); + } + if (iSelect == 'ccrt') + { + pHttpServ->iClientCertLvl = iValue; + return(0); + } + if (iSelect == 'ciph') + { + pHttpServ->uSslCiphers = iValue; + return(0); + } + if (iSelect == 'idle') + { + pHttpServ->iIdleTimeout = iValue; + return(0); + } + if (iSelect == 'scrt') + { + pHttpServ->pServerCert = (char *)pValue; + pHttpServ->iServerCertLen = iValue; + return(0); + } + if (iSelect == 'skey') + { + pHttpServ->pServerKey = (char *)pValue; + pHttpServ->iServerKeyLen = iValue; + return(0); + } + if (iSelect == 'spam') + { + pHttpServ->uDebugLevel = (uint16_t)iValue; + return(0); + } + if (iSelect == 'time') + { + pHttpServ->iRequestTimeout = iValue; + return(0); + } + if (iSelect == 'vers') + { + pHttpServ->uSslVersion = iValue; + return(0); + } + if (iSelect == 'vmin') + { + pHttpServ->uSslVersionMin = iValue; + return(0); + } + // unsupported + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServControl2 + + \Description + Set behavior of module, based on selector input + + \Input *pHttpServ - reference pointer + \Input iThread - thread to apply control to, or -1 for a global setting + \Input iSelect - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + See ProtoHttpServControl for global settings. Thread-relative control + selectors are passed through to ProtoSSL. + + \Version 03/28/2018 (jbrookes) +*/ +/*******************************************************************************F*/ +int32_t ProtoHttpServControl2(ProtoHttpServRefT *pHttpServ, int32_t iThread, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue) +{ + ProtoHttpServThreadT *pHttpThread; + + if (iThread == -1) + { + return(ProtoHttpServControl(pHttpServ, iSelect, iValue, iValue2, pValue)); + } + + if ((pHttpThread = _ProtoHttpServGetThreadFromId(pHttpServ, iThread)) == NULL) + { + _ProtoHttpServLogPrintf(pHttpServ, NULL, "invalid thread id %d\n", iThread); + return(-1); + } + + return(ProtoSSLControl(pHttpThread->pProtoSSL, iSelect, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServStatus + + \Description + Return status of module, based on selector input + + \Input *pHttpServ - module state + \Input iSelect - info selector (see Notes) + \Input *pBuffer - [out] storage for selector-specific output + \Input iBufSize - size of buffer + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR RETURN RESULT + \endverbatim + + \Version 12/12/2013 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpServStatus(ProtoHttpServRefT *pHttpServ, int32_t iSelect, void *pBuffer, int32_t iBufSize) +{ + // unsupported selector + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpServUpdate + + \Description + Update HttpServ module + + \Input *pHttpServ - module state + + \Version 10/30/2013 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoHttpServUpdate(ProtoHttpServRefT *pHttpServ) +{ + ProtoHttpServThreadT *pHttpThread, *pHttpThreadNext, *pHttpThreadPrevious = NULL, **ppHttpThread; + + // if we don't have a listen object don't do anything at all + if (pHttpServ->pListenSSL == NULL) + { + return; + } + + // listen for a response + _ProtoHttpServUpdateListen(pHttpServ); + + // update http threads + for (pHttpThread = pHttpServ->pThreadListHead; pHttpThread != NULL; pHttpThread = pHttpThread->pNext) + { + _ProtoHttpServUpdateThread(pHttpServ, pHttpThread); + } + + // clean up expired threads + for (ppHttpThread = &pHttpServ->pThreadListHead; *ppHttpThread != NULL; ) + { + pHttpThread = *ppHttpThread; + if (pHttpThread->bDisconnecting && (NetTickDiff(NetTick(), pHttpThread->iDisconnectTimer) > HTTPSERV_DISCTIME)) + { + pHttpThreadNext = pHttpThread->pNext; + + // if dealing with tail of list, move tail pointer to previous entry (or NULL it if tail is last entry in list) + if (pHttpThread == pHttpServ->pThreadListTail) + { + pHttpServ->pThreadListTail = pHttpThreadPrevious; + if (pHttpServ->pThreadListTail != NULL) + { + pHttpServ->pThreadListTail->pNext = NULL; + } + } + + _ProtoHttpServThreadFree(pHttpServ, pHttpThread); + *ppHttpThread = pHttpThreadNext; + } + else + { + pHttpThreadPrevious = pHttpThread; + ppHttpThread = &(*ppHttpThread)->pNext; + } + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protohttputil.c b/r5dev/thirdparty/dirtysdk/source/proto/protohttputil.c new file mode 100644 index 00000000..93ce26ce --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protohttputil.c @@ -0,0 +1,872 @@ +/*H********************************************************************************/ +/*! + \File protohttputil.c + + \Description + This module implements HTTP support routines such as URL processing, header + parsing, etc. + + \Copyright + Copyright (c) Electronic Arts 2000-2012. + + \Version 1.0 12/18/2012 (jbrookes) Refactored into separate module from protohttp. +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/proto/protohttputil.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private variables + +//! http request names +static const char *_ProtoHttpUtil_strRequestNames[PROTOHTTP_NUMREQUESTTYPES] = +{ + "HEAD", "GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "CONNECT" +}; + + +// Public variables + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _WriteChar + + \Description + Write a character to output buffer, if there is room + + \Input *pBuffer - [out] output buffer + \Input cWrite - character to write to output + \Input iOffset - offset in output buffer to write to + \Input iLength - size of output buffer + + \Output + int32_t - iOffset+1 + + \Version 02/20/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _WriteChar(char *pBuffer, char cWrite, int32_t iOffset, int32_t iLength) +{ + if (iOffset < iLength) + { + pBuffer[iOffset] = cWrite; + } + return(iOffset+1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoHttpGetHeaderFieldName + + \Description + Extract header value from the designated header field. Passing pOutBuf=null + and iOutLen=0 results in returning the size required to store the field, + null-inclusive. + + \Input *pInpBuf - pointer to header text + \Input *pOutBuf - [out] output for header field name + \Input iOutLen - size of output buffer + + \Output + int32_t - negative=error, positive=success + + \Version 03/17/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoHttpGetHeaderFieldName(const char *pInpBuf, char *pOutBuf, int32_t iOutLen) +{ + int32_t iOutSize = 0; + + /* http/2 has some header names that start with a ':' character + so just write that out and continue to parsing */ + if (pInpBuf[iOutSize] == ':') + { + pOutBuf[iOutSize] = pInpBuf[iOutSize]; + iOutSize += 1; + } + + for (; (iOutSize < iOutLen) && (pInpBuf[iOutSize] != ':') && (pInpBuf[iOutSize] != '\0'); iOutSize += 1) + { + pOutBuf[iOutSize] = pInpBuf[iOutSize]; + } + if (iOutSize == iOutLen) + { + return(-1); + } + pOutBuf[iOutSize] = '\0'; + return(iOutSize); +} + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUtilParseRequest + + \Description + Parse HTTP request line from the format as sent on the wire. + + \Input *pStrHeader - header buffer to parse for info + \Input *pStrMethod - [out] buffer to store request method string + \Input iMethodLen - size of method buffer + \Input *pStrUri - [out] buffer to store request uri + \Input iUriLen - size of uri buffer + \Input *pStrQuery - [out] buffer to store request query + \Input iQueryLen - size of query buffer + \Input *pRequestType - [out] storage for request type (optional) + \Input *pHttp1_0 - [out] storage for http1.0 boolean + + \Output + const char * - pointer to first header, or NULL on error + + \Version 01/03/2019 (jbrookes) From _ProtoHttpServParseHeader +*/ +/********************************************************************************F*/ +const char *ProtoHttpUtilParseRequest(const char *pStrHeader, char *pStrMethod, int32_t iMethodLen, char *pStrUri, int32_t iUriLen, char *pStrQuery, int32_t iQueryLen, ProtoHttpRequestTypeE *pRequestType, uint8_t *pHttp1_0) +{ + const char *pHdr, *pEnd; + int32_t iType; + + // process request type + for (iType = 0; iType < PROTOHTTP_NUMREQUESTTYPES; iType += 1) + { + if (!ds_strnicmp(_ProtoHttpUtil_strRequestNames[iType], pStrHeader, (int32_t)strlen(_ProtoHttpUtil_strRequestNames[iType]))) + { + break; + } + } + // make sure request type is valid + if (iType == PROTOHTTP_NUMREQUESTTYPES) + { + return(NULL); + } + // save request type + ds_strnzcpy(pStrMethod, _ProtoHttpUtil_strRequestNames[iType], iMethodLen); + if (pRequestType != NULL) + { + *pRequestType = (ProtoHttpRequestTypeE)iType; + } + + // find url + if ((pHdr = strchr(pStrHeader, '/')) == NULL) + { + // not a valid header + return(NULL); + } + // find end of url + if ((pEnd = strchr(pHdr, '?')) == NULL) + { + if ((pEnd = strchr(pHdr, ' ')) == NULL) + { + // not a valid header + return(NULL); + } + } + // copy the uri + ds_strsubzcpy(pStrUri, iUriLen, pHdr, (int32_t)(pEnd-pHdr)); + + // if the beginning of the query string was found, set that to the beginning so we can parse out the query string + if (*pEnd == '?') + { + pHdr = pEnd; + if ((pEnd = strchr(pHdr, ' ')) == NULL) + { + // not a valid header + return(NULL); + } + // copy the query string + ds_strsubzcpy(pStrQuery, iQueryLen, pHdr, (int32_t)(pEnd-pHdr)); + } + else + { + *pStrQuery = '\0'; + } + + // detect if it is a 1.0 response; if so we default to closing the connection + if (pHttp1_0 != NULL) + { + *pHttp1_0 = !ds_strnicmp(pEnd+1, "HTTP/1.0", 8) ? TRUE : FALSE; + } + + // skip to first header + if ((pHdr = strstr(pEnd, "\r\n")) == NULL) + { + // not a valid header + return(NULL); + } + // return pointer to first header + return(pHdr); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpExtractHeaderValue + + \Description + Extract header value from the designated header field. Passing pOutBuf=null + and iOutLen=0 results in returning the size required to store the field, + null-inclusive. + + \Input *pInpBuf - pointer to header text + \Input *pOutBuf - [out] output for header field value, null for size query + \Input iOutLen - size of output buffer or zero for size query + \Input **ppHdrEnd- [out] pointer past end of parsed header (optional) + + \Output + int32_t - negative=not found or not enough space, zero=success, positive=size query result + + \Version 03/17/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpExtractHeaderValue(const char *pInpBuf, char *pOutBuf, int32_t iOutLen, const char **ppHdrEnd) +{ + int32_t iValLen; + + // no input, nothing to look for + if (pInpBuf == NULL) + { + return(-1); + } + + // calc size and copy to buffer, if specified + for (iValLen = 0; *pInpBuf != '\0' ; pInpBuf += 1, iValLen += 1) + { + // check for EOL (CRLF) + if ((pInpBuf[0] == '\r') && (pInpBuf[1] == '\n')) + { + // check next character -- if whitespace we have a line continuation + if ((pInpBuf[2] == ' ') || (pInpBuf[2] == '\t')) + { + // skip CRLF and LWS + for (pInpBuf += 3; (*pInpBuf == ' ') || (*pInpBuf == '\t'); pInpBuf += 1) + ; + } + else // done + { + break; + } + } + + // copy to output buffer + if (pOutBuf != NULL) + { + pOutBuf[iValLen] = *pInpBuf; + if ((iValLen+1) >= iOutLen) + { + *pOutBuf = '\0'; + return(-1); + } + } + } + // set header end pointer, if requested + if (ppHdrEnd != NULL) + { + *ppHdrEnd = pInpBuf; + } + // if a copy request, null-terminate and return success + if (pOutBuf != NULL) + { + pOutBuf[iValLen] = '\0'; + return(0); + } + // if a size inquiry, return value length (null-inclusive) + return(iValLen+1); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpParseHeaderCode + + \Description + Parse header response code from HTTP header. + + \Input *pHdrBuf - pointer to first line of HTTP header + + \Output + int32_t - parsed header code + + \Version 01/12/2010 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpParseHeaderCode(const char *pHdrBuf) +{ + int32_t iHdrCode; + + // skip to result code + while ((*pHdrBuf != '\r') && (*pHdrBuf > ' ')) + { + pHdrBuf += 1; + } + while ((*pHdrBuf != '\r') && (*pHdrBuf <= ' ')) + { + pHdrBuf += 1; + } + // grab the result code + for (iHdrCode = 0; (*pHdrBuf >= '0') && (*pHdrBuf <= '9'); pHdrBuf += 1) + { + iHdrCode = (iHdrCode * 10) + (*pHdrBuf & 15); + } + return(iHdrCode); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpFindHeaderValue + + \Description + Finds the header field value for the designated header field. The input + header text to find should not include formatting (leading \n or trailing + colon). + + \Input *pHdrBuf - input buffer + \Input *pHeaderText - header field to find + + \Output + char * - pointer to header value text or null if not found + + \Version 03/17/2009 (jbrookes) +*/ +/********************************************************************************F*/ +const char *ProtoHttpFindHeaderValue(const char *pHdrBuf, const char *pHeaderText) +{ + char strSearchText[64]; + const char *pFoundText; + + ds_snzprintf(strSearchText, sizeof(strSearchText), "\n%s:", pHeaderText); + if ((pFoundText = ds_stristr(pHdrBuf, strSearchText)) != NULL) + { + // skip header field name + pFoundText += strlen(strSearchText); + + // skip any leading white-space + while ((*pFoundText != '\0') && (*pFoundText != '\r') && (*pFoundText <= ' ')) + { + pFoundText += 1; + } + } + + return(pFoundText); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpGetHeaderValue + + \Description + Extract header value for the header field specified by pName from pHdrBuf. + Passing pOutBuf=null and iOutLen=0 results in returning the size required + to store the field, null-inclusive. + + \Input *pState - module state + \Input *pHdrBuf - header text + \Input *pName - header field name (case-insensitive) + \Input *pBuffer - [out] pointer to buffer to store extracted header value (null for size query) + \Input iBufSize - size of output buffer (zero for size query) + \Input **pHdrEnd- [out] pointer past end of parsed header (optional) + + \Output + int32_t - negative=not found or not enough space, zero=success, positive=size query result + + \Version 03/24/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpGetHeaderValue(void *pState, const char *pHdrBuf, const char *pName, char *pBuffer, int32_t iBufSize, const char **pHdrEnd) +{ + int32_t iResult = -1; + + // check for location header request which is specially handled + if ((pHdrBuf = ProtoHttpFindHeaderValue(pHdrBuf, pName)) != NULL) + { + // extract the header + iResult = ProtoHttpExtractHeaderValue(pHdrBuf, pBuffer, iBufSize, pHdrEnd); + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpGetNextHeader + + \Description + Get the next header name/value pair from the header buffer. pHdrBuf should + initially be pointing to the start of the header text buffer ("HTTP...") and + this function can then be iteratively called to extract the individual header + fields from the header one by one. pHdrBuf could also start as the result + of a call to ProtoHttpGetHeaderValue() if desired. If the start of the + buffer is not the end of line marker, it is assumed to be the header to + retrieve. + + \Input *pState - module state + \Input *pHdrBuf - header text + \Input *pName - [out] header field name + \Input iNameSize- size of header field name buffer + \Input *pValue - [out] pointer to buffer to store extracted header value (null for size query) + \Input iValSize - size of output buffer (zero for size query) + \Input **pHdrEnd- [out] pointer past end of parsed header + + \Output + int32_t - negative=not found or not enough space, zero=success, positive=size query result + + \Version 03/24/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpGetNextHeader(void *pState, const char *pHdrBuf, char *pName, int32_t iNameSize, char *pValue, int32_t iValSize, const char **pHdrEnd) +{ + int32_t iNameLen, iResult; + + // is this a first call? + if (!strncmp(pHdrBuf, "HTTP", 4)) + { + // skip http header + for ( ; *pHdrBuf != '\r' && *pHdrBuf != '\0'; pHdrBuf += 1) + ; + if (*pHdrBuf == '\0') + { + return(-1); + } + } + + // skip crlf from previous line? + if ((pHdrBuf[0] == '\r') && (pHdrBuf[1] == '\n')) + { + pHdrBuf += 2; + } + + // get header name + if ((iNameLen = _ProtoHttpGetHeaderFieldName(pHdrBuf, pName, iNameSize)) <= 0) + { + return(-1); + } + // skip header name and leading whitespace + for (pHdrBuf += iNameLen + 1; (*pHdrBuf != '\0') && (*pHdrBuf != '\r') && (*pHdrBuf <= ' '); pHdrBuf += 1) + ; + + // get header value + iResult = ProtoHttpExtractHeaderValue(pHdrBuf, pValue, iValSize, pHdrEnd); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUrlEncodeIntParm + + \Description + Url-encode an integer parameter. + + \Input *pBuffer - [out] pointer to buffer to append parameter to + \Input iLength - length of buffer + \Input *pParm - pointer to parameter (not encoded) + \Input iValue - integer to encode + + \Output + int32_t - number of characters (excluding null) written + + \Notes + See ProtoHttpUrlEncodeStrParm2 for notes on output + + \Version 11/30/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpUrlEncodeIntParm(char *pBuffer, int32_t iLength, const char *pParm, int32_t iValue) +{ + char strValue[32]; + // format the value + ds_snzprintf(strValue, sizeof(strValue), "%d", iValue); + // encode it + return(ProtoHttpUrlEncodeStrParm(pBuffer, iLength, pParm, strValue)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUrlEncodeStrParm + + \Description + Url-encode a string parameter. + + \Input *pBuffer - [out] pointer to buffer to append parameter to + \Input iLength - length of buffer + \Input *pParm - pointer to url parameter (not encoded) + \Input *pData - string to encode + + \Output + int32_t - negative=failure, zero=success + + \Notes + See ProtoHttpUrlEncodeStrParm2 for notes on output + + \Version 11/30/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t ProtoHttpUrlEncodeStrParm(char *pBuffer, int32_t iLength, const char *pParm, const char *pData) +{ + // table of "safe" characters + // 0=hex encode, non-zero=direct encode, @-O=valid hex digit (&15 to get value) + static char _strSafe[256] = + "0000000000000000" "0000000000000000" // 0x00 - 0x1f + "0000000000000000" "@ABCDEFGHI000000" // 0x20 - 0x3f (allow digits) + "0JKLMNO111111111" "1111111111100000" // 0x40 - 0x5f (allow uppercase) + "0JKLMNO111111111" "1111111111100000" // 0x60 - 0x7f (allow lowercase) + "0000000000000000" "0000000000000000" // 0x80 - 0x9f + "0000000000000000" "0000000000000000" // 0xa0 - 0xbf + "0000000000000000" "0000000000000000" // 0xc0 - 0xdf + "0000000000000000" "0000000000000000"; // 0xe0 - 0xff} + + return(ProtoHttpUrlEncodeStrParm2(pBuffer, iLength, pParm, pData, _strSafe)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUrlEncodeStrParm2 + + \Description + Url-encode a string parameter. + + \Input *pBuffer - [out] pointer to buffer to append parameter to + \Input iLength - length of buffer + \Input *pParm - pointer to url parameter (not encoded) + \Input *pData - string to encode + \Input *pStrSafe- safe string table + + \Output + int32_t - number of characters (excluding null) formatted (see note) + + \Notes + Always null-terminates if the buffer is not zero-sized. The return value + is the number of characters (excluding the terminating null byte) which + would be written to the final string regardless of whether enough space + is available or not. Therefore, a return value greater than the size + of the buffer indicates the output is truncated. + + \Version 11/30/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpUrlEncodeStrParm2(char *pBuffer, int32_t iLength, const char *pParm, const char *pData, const char *pStrSafe) +{ + // hex translation table + static const char _strHex[16] = "0123456789ABCDEF"; + int32_t iOffset, iTerm; + + // locate the append point + for (iOffset = 0; (pBuffer[iOffset] != '\0') && (iOffset < iLength); iOffset += 1) + ; + + // add in the parameter (non encoded) + for (; *pParm != '\0'; pParm += 1) + { + iOffset = _WriteChar(pBuffer, *pParm, iOffset, iLength); + } + + // add in the encoded data + for (; *pData != '\0'; pData += 1) + { + uint8_t ch = *pData; + // see how we need to encode this + if (pStrSafe[ch] == '0') + { + iOffset = _WriteChar(pBuffer, '%', iOffset, iLength); + iOffset = _WriteChar(pBuffer, _strHex[ch>>4], iOffset, iLength); + iOffset = _WriteChar(pBuffer, _strHex[ch&15], iOffset, iLength); + } + else + { + iOffset = _WriteChar(pBuffer, ch, iOffset, iLength); + } + } + + // locate terminating null + iTerm = (iOffset < iLength) ? iOffset : iLength-1; + + // terminate if possible + if (iLength > 0) + { + pBuffer[iTerm] = '\0'; + } + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUrlDecodeStrParm + + \Description + Url-decode a string parameter. + + \Input *pBuffer - pointer to the buffer with the url-encoded string + \Input *pData - [out] location to write the url-decoded string + \Input iDataSize- length of pData + + \Output + int32_t - negative=failure, zero=success + + \Version 08/25/2015 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoHttpUrlDecodeStrParm(const char *pBuffer, char *pData, int32_t iDataSize) +{ + int32_t iInpLen = (int32_t)strlen(pBuffer); + int32_t iInpOff, iOutOff; + + // save room for null termination of output + iDataSize -= 1; + + for (iInpOff = 0, iOutOff = 0; (pBuffer[iInpOff] != '\0') && (iOutOff < iDataSize); iInpOff += 1) + { + const char ch = pBuffer[iInpOff]; + if ((ch == '%') && (iInpOff < (iInpLen-2))) + { + // save the base 16 number in a string to be converted to an character + char aTemp[3] = { '\0' }; //!< 2 character + null term + aTemp[0] = pBuffer[iInpOff+1]; + aTemp[1] = pBuffer[iInpOff+2]; + iInpOff += 2; + + // convert the base 16 number to a character and write it to the output string + pData[iOutOff++] = (char)strtol(aTemp, NULL, 16 /* Base */); + } + else + { + pData[iOutOff++] = ch; + } + } + + // null-terminate + pData[iOutOff] = '\0'; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUrlParse + + \Description + Parses a Url into component parts. + + \Input *pUrl - Url to parse + \Input *pKind - [out] http request kind + \Input iKindSize - size of kind output buffer + \Input *pHost - [out] host name + \Input iHostSize - size of host output buffer + \Input *pPort - [out] request port + \Input *pSecure - [out] request security + + \Output + const char * - pointer past end of parsed Url + + \Version 07/01/2009 (jbrookes) First Version +*/ +/********************************************************************************F*/ +const char *ProtoHttpUrlParse(const char *pUrl, char *pKind, int32_t iKindSize, char *pHost, int32_t iHostSize, int32_t *pPort, int32_t *pSecure) +{ + uint8_t bPortSpecified; + // parse the url into component parts + return(ProtoHttpUrlParse2(pUrl, pKind, iKindSize, pHost, iHostSize, pPort, pSecure, &bPortSpecified)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpUrlParse2 + + \Description + Parse information from given URL. + + \Input *pUrl - pointer to input URL to parse + \Input *pKind - [out] storage for parsed URL kind (eg "http", "https") + \Input iKindSize - size of pKind buffer + \Input *pHost - [out] storage for parsed URL host + \Input iHostSize - size of pHost buffer + \Input *pPort - [out] storage for parsed port + \Input *pSecure - [out] storage for parsed security (0 or 1) + \Input *pPortSpecified - [out] storage for whether port was specified + + \Output + const char * - pointer to URL arguments + + \Notes + URI RFC http://www.ietf.org/rfc/rfc2396.txt + + \Version 11/10/2004 (jbrookes) Split/combined from ProtoHttpGet() and ProtoHttpPost() +*/ +/********************************************************************************F*/ +const char *ProtoHttpUrlParse2(const char *pUrl, char *pKind, int32_t iKindSize, char *pHost, int32_t iHostSize, int32_t *pPort, int32_t *pSecure, uint8_t *pPortSpecified) +{ + char strKind[PROTOHTTPUTIL_SCHEME_MAX] = ""; + const char *s; + int32_t i, iPort; + + // skip any leading white-space + for ( ; (*pUrl != 0) && (*pUrl <= ' '); pUrl += 1) + ; + + // see if there is a protocol reference (cf rfc2396 sec 3.1) + for (s = pUrl; isalnum(*s) || (*s == '+') || (*s == '-') || (*s == '.'); s++) + ; + if (*s == ':') + { + // copy over the protocol kind + ds_strsubzcpy(strKind, sizeof(strKind), pUrl, (int32_t)(s-pUrl)); + pUrl += (s-pUrl)+1; + } + ds_strnzcpy(pKind, strKind, iKindSize); + + // determine if secure connection + *pSecure = (ds_stricmp(pKind, "https") == 0); + + // skip past any white space + for ( ; (*pUrl != 0) && (*pUrl <= ' '); pUrl += 1) + ; + + // skip optional // + if ((pUrl[0] == '/') && (pUrl[1] == '/')) + { + pUrl += 2; + } + + // extract the server name + for (i = 0; i < (iHostSize-1); i++) + { + // make sure the data is valid + if ((*pUrl <= ' ') || (*pUrl == '/') || (*pUrl == '?') || (*pUrl == ':')) + { + break; + } + pHost[i] = *pUrl++; + } + pHost[i] = '\0'; + + // extract the port if included + iPort = 0; + if (*pUrl == ':') + { + for (pUrl++, iPort = 0; (*pUrl >= '0') && (*pUrl <= '9'); pUrl++) + { + iPort = (iPort * 10) + (*pUrl & 15); + } + } + // if port is unspecified, set it here to a standard port + if (iPort == 0) + { + iPort = *pSecure ? 443 : 80; + *pPortSpecified = FALSE; + } + else + { + *pPortSpecified = TRUE; + } + *pPort = iPort; + + // skip past white space to arguments + for ( ; (*pUrl != 0) && (*pUrl <= ' '); pUrl += 1) + ; + + // return pointer to arguments + return(pUrl); +} + +/*F********************************************************************************/ +/*! + \Function ProtoHttpGetNextParam + + \Description + Get the next query param name/value pair from the uri buffer. This function + can be iteratively called to extra the individual params one by one. + + \Input *pState - module state + \Input *pUriBuf - path text + \Input *pName - [out] query key name + \Input iNameSize- size of query key name buffer + \Input *pValue - [out] pointer to buffer to store extracted query value + \Input iValSize - size of output buffer + \Input **pUriEnd- [out] pointer past end of parsed query + + \Output + int32_t - negative=not found or not enough space, zero=success +*/ +/********************************************************************************F*/ +int32_t ProtoHttpGetNextParam(void *pState, const char *pUriBuf, char *pName, int32_t iNameSize, char *pValue, int32_t iValSize, const char **pUriEnd) +{ + const char* pEnd = NULL; + + // check if there is anything left to parse + if (pUriBuf[0] == '\0') + { + return(-1); + } + + // try to find the '?' character denoting the beginning + // of the query string + // if found set the new beginning one character after + // otherwise we parse as it has already been removed + if ((pEnd = strchr(pUriBuf, '?')) != NULL) + { + pUriBuf = pEnd + 1; + } + + // try to find the end of the key + if ((pEnd = strchr(pUriBuf, '=')) == NULL) + { + return(-1); + } + + // copy the value and set the new position + // we are skipping an extra character for '=' + ds_strsubzcpy(pName, iNameSize, pUriBuf, (int32_t)(pEnd-pUriBuf)); + pUriBuf = pEnd + 1; + + // try to find the end of the value + if ((pEnd = strchr(pUriBuf, '&')) == NULL) + { + // go to the end of the string + if ((pEnd = strchr(pUriBuf, '\0')) == NULL) + { + return(-1); + } + + // copy the value and set the new position + // we are not skipping an extra character as we have reached + // the end of the string + ds_strsubzcpy(pValue, iValSize, pUriBuf, (int32_t)(pEnd-pUriBuf)); + pUriBuf = pEnd; + } + else + { + // copy the value and set the new position + // we are skipping an extra character for '&' + ds_strsubzcpy(pValue, iValSize, pUriBuf, (int32_t)(pEnd-pUriBuf)); + pUriBuf = pEnd + 1; + } + + // save the location + if (pUriEnd != NULL) + { + *pUriEnd = pUriBuf; + } + + return(0); +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protomangle.c b/r5dev/thirdparty/dirtysdk/source/proto/protomangle.c new file mode 100644 index 00000000..327f753d --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protomangle.c @@ -0,0 +1,1129 @@ +/*H*************************************************************************************************/ +/*! + \File protomangle.c + + \Description + This module encapsulates client services for use of the EA.Com Demangler service. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2003. ALL RIGHTS RESERVED. + + \Version 1.0 04/03/03 (JLB) First Version +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtycert.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/proto/protomangle.h" + +/*** Defines ***************************************************************************/ + +#define PROTOMANGLE_VERBOSE (DIRTYCODE_DEBUG && TRUE) + +#define PROTOMANGLE_RAND_PORTBASE (2000) + +//! range used for random port generation (used to be 8000 but that proved to conflict with Sony port range restriction on PS4) +#define PROTOMANGLE_RAND_PORTRANGE (6000) + +#define PROTOMANGLE_HTTP_BUFSIZE (1024) + +#define PROTOMANGLE_HTTP_TIMEOUT (60 * 1000) + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +typedef enum ProtoMangleResponseE +{ + PROTOMANGLE_RESP_INVALID = -1, + PROTOMANGLE_RESP_SUCCESS, + PROTOMANGLE_RESP_PROBE, + PROTOMANGLE_RESP_FAILURE, + + PROTOMANGLE_NUMRESP +} ProtoMangleResponseE; + +//! parsed probe request data +typedef struct ProtoMangleProbeT +{ + int32_t iCurCnt; //!< current probe iteration + int32_t iSndCnt; //!< number of probes to send + int32_t iAddr; //!< peer address + int32_t iPort; //!< peer port + int32_t iSrcPort; //!< local port used to bind (if -1, bind a random port) + int32_t iId; //!< transaction ID + char strTag[64]; //!< response tag +} ProtoMangleProbeT; + +//! internal module state +struct ProtoMangleRefT //!< module state +{ + ProtoHttpRefT *pHttp; //!< http module + + SocketT *pProbeSock; //!< socket for sending UDP probes + SocketT *pSharedProbeSock; //!< shared socket, if any + + //! module memory group + int32_t iMemGroup; + void *pMemGroupUserData; + + int32_t iHostAddr; //!< local address + int32_t iGamePort; //!< game port + + int32_t iPeerAddr; //!< peer address (when eMangleState==ST_MNGL_DONE) + int32_t iPeerPort; //!< peer port to connect to (when eMangleState==ST_MNGL_DONE) + + char strCookie[PROTOMANGLE_STRCOOKIE_MAX]; //!< session cookie + char strGameID[PROTOMANGLE_STRGAMEID_MAX]; //!< game feature ID + char strLKey[PROTOMANGLE_STRLKEY_MAX]; //!< user LKey + char strServer[PROTOMANGLE_STRSERVER_MAX]; //!< server name + int32_t iServerPort; //!< server port + + ProtoMangleResponseE eResponse; //!< most recent server response + + int32_t iRandPort; //!< 'random' port to bind if no local port requested + + enum + { + ST_MNGL_IDLE, //!< created + ST_MNGL_WAIT, //!< waiting for response from HTTP server + ST_MNGL_DONE, //!< successful completion + ST_MNGL_FAIL, //!< failure condition + ST_MNGL_REPT //!< reporting + } eMangleState; + + int32_t iRecvSize; //!< current receive size + char strBuf[PROTOMANGLE_HTTP_BUFSIZE]; +}; + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +//! response codes +static const char *_ProtoMangle_pResponseTable[PROTOMANGLE_NUMRESP] = +{ + "success", // PROTOMANGLE_RESP_SUCCESS + "probe", // PROTOMANGLE_RESP_PROBE + "failure" // PROTOMANGLE_RESP_FAILURE +}; + +//! report codes +static const char *_ProtoMangle_pReportTable[PROTOMANGLE_NUMSTATUS] = +{ + "connected", // PROTOMANGLE_STATUS_CONNECTED + "failed" // PROTOMANGLE_STATUS_FAILED +}; + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleSockClose + + \Description + Safe socket close of a probe socket + + \Input *pRef - module state + \Input bRelease - whether shared probe socket should be release or not + + \Version 01/14/05 (JLB) +*/ +/*************************************************************************************************F*/ +static void _ProtoMangleSockClose(ProtoMangleRefT *pRef, uint32_t bRelease) +{ + if (pRef->pProbeSock != NULL) + { + if (pRef->pProbeSock != pRef->pSharedProbeSock) + { + NetPrintf(("protomangle: [%p] closing probe socket %p\n", pRef, pRef->pProbeSock)); + SocketShutdown(pRef->pProbeSock, SOCK_NOSEND); + SocketClose(pRef->pProbeSock); + pRef->pProbeSock = NULL; + } + else if (bRelease) + { + NetPrintf(("protomangle: [%p] releasing shared probe socket %p\n", pRef, pRef->pProbeSock)); + SocketRelease(pRef->pProbeSock); + pRef->pProbeSock = pRef->pSharedProbeSock = NULL; + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleHttpPost + + \Description + Send a POST request to HTTP server. + + \Input *pRef - module state + \Input *pUrl - url to post to + \Input *pCookie - session cookie + \Input *pData - POST data + + \Output + int32_t - zero=success, negative=failure + + \Version 07/18/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _ProtoMangleHttpPost(ProtoMangleRefT *pRef, const char *pUrl, const char *pCookie, const char *pData) +{ + char strBuffer[256]; + + // setup the extra header fields + ds_snzprintf(strBuffer, sizeof(strBuffer), + "Cookie: sessionID=%s\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n", + pCookie); + + // set extra header fields + ProtoHttpControl(pRef->pHttp, 'apnd', 0, 0, strBuffer); + + // setup the url + ds_snzprintf(strBuffer, sizeof(strBuffer), "http://%s:%d/%s", pRef->strServer, pRef->iServerPort, pUrl); + + // execute the post + return(ProtoHttpPost(pRef->pHttp, strBuffer, pData, (int32_t)strlen(pData), FALSE)); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleHttpRequest + + \Description + Issue HTTP request to the demangler server. + + \Input *pRef - module state + + \Version 1.0 04/08/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static void _ProtoMangleHttpRequest(ProtoMangleRefT *pRef) +{ + char strAppend[256], strUrl[256 + (3 * DIRTYCERT_SERVICENAME_SIZE)], strAddrText[20], strServiceName[DIRTYCERT_SERVICENAME_SIZE]; + int32_t iFormatted = 0; + + if (DirtyCertStatus('snam', strServiceName, DIRTYCERT_SERVICENAME_SIZE) < 0) + { + ds_snzprintf(strServiceName, sizeof(strServiceName), "invalid"); + } + + // setup the url + iFormatted = ds_snzprintf(strUrl, sizeof(strUrl), "http://%s:%d/getPeerAddress?myIP=%s&myPort=%d&version=1.0", + pRef->strServer, pRef->iServerPort, SocketInAddrGetText(pRef->iHostAddr, strAddrText, sizeof(strAddrText)), pRef->iGamePort); + + // add service name + ProtoHttpUrlEncodeStrParm(strUrl + iFormatted, sizeof(strUrl) - iFormatted, "&gameID=", strServiceName); + + // setup the extra header fields + ds_snzprintf(strAppend, sizeof(strAppend), "Cookie: sessionID=%s\r\n", pRef->strCookie); + + // set extra header fields + ProtoHttpControl(pRef->pHttp, 'apnd', 0, 0, strAppend); + + // send the request + ProtoHttpGet(pRef->pHttp, strUrl, FALSE); + + // set state + pRef->iRecvSize = 0; + pRef->eMangleState = ST_MNGL_WAIT; +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleGetParm + + \Description + Parse a parameter from a demangler server response. + + \Input *pDst - pointer to buffer for parsed parm + \Input iBufLen - length of destination buffer + \Input *pSrc - pointer to server reseponse + \Input *pParmName - name of parameter to parse + \Input bParseCount - if TRUE, parse response count + + \Output + int32_t - response count (one if bParseCount==FALSE), or negative if parmname not found + + \Version 1.0 04/08/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static int32_t _ProtoMangleGetParm(char *pDst, int32_t iBufLen, const char *pSrc, const char *pParmName, int32_t bParseCount) +{ + const char *pParm; + int32_t iStr, iId = -1; + + // clear the destination buffer + pDst[0] = '\0'; + + // make sure we have room for terminating NULL character + iBufLen--; + + // look for parm + if ((pParm = strstr(pSrc, pParmName)) != NULL) + { + // parse count? + if (bParseCount == TRUE) + { + char *pId; + + if ((pId = strchr(pParm, '-')) != NULL) + { + iId = pId[1] - '0'; + if ((iId < 0) || (iId > 9)) + { + iId = -1; + } + } + } + else + { + iId = TRUE; + } + + // look for value + if ((pParm = strchr(pParm, '=')) != NULL) + { + // found string; make a copy of it + for (iStr = 0, pParm++; (iStr < iBufLen) && (pParm[iStr] != '\r') && (pParm[iStr] != '\n'); iStr++) + { + pDst[iStr] = pParm[iStr]; + } + + // NULL terminate + pDst[iStr++] = '\0'; + } + } + + return(iId); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleParseAdvance + + \Description + Advance past current parm block + + \Input *pSrc - pointer to current block + \Input *pTerminator - termination character + + \Output + const char* - pointer to next block, or NULL if there is none + + \Notes + Parm blocks are terminated by double LFs. + + \Version 1.0 04/09/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static const char *_ProtoMangleParseAdvance(const char *pSrc, const char *pTerminator) +{ + if ((pSrc = strstr(pSrc, pTerminator)) != NULL) + { + while(*pSrc == '\n') + { + pSrc++; + } + } + + return(pSrc); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleParseResponseType + + \Description + Determine type of demangler server response. + + \Input *pSrc - module state + \Input *pResponse - storage for parsed response type + + \Output + const char * - pointer to next entry + + \Version 1.0 04/08/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static const char *_ProtoMangleParseResponseType(const char *pSrc, ProtoMangleResponseE *pResponse) +{ + char strCmd[32]; + int32_t iResp; + + *pResponse = PROTOMANGLE_RESP_INVALID; + if (_ProtoMangleGetParm(strCmd, sizeof(strCmd), pSrc, "status", FALSE)) + { + for (iResp = 0; iResp < PROTOMANGLE_NUMRESP; iResp++) + { + if (!strncmp(strCmd, _ProtoMangle_pResponseTable[iResp], strlen(_ProtoMangle_pResponseTable[iResp]))) + { + *pResponse = (ProtoMangleResponseE)iResp; + break; + } + } + } + + return(_ProtoMangleParseAdvance(pSrc, "\n")); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleParseSuccess + + \Description + Parse demangler server 'success' response. + + \Input *pRef - module state + \Input *pStr - server response string + + \Output + int32_t - TRUE if successfully parsed, else FALSE + + \Version 1.0 04/09/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static int32_t _ProtoMangleParseSuccess(ProtoMangleRefT *pRef, const char *pStr) +{ + char strParm[32]; + int32_t bLinesParsed = 0; + + if ((_ProtoMangleGetParm(strParm, sizeof(strParm), pStr, "peerIP", FALSE)) >= 0) + { + if ((pRef->iPeerAddr = SocketInTextGetAddr(strParm)) != 0) + { + bLinesParsed++; + } + } + + if ((_ProtoMangleGetParm(strParm, sizeof(strParm), pStr, "peerPort", FALSE)) >= 0) + { + pRef->iPeerPort = atoi(strParm); + bLinesParsed++; + } + + return(bLinesParsed == 2); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleParseProbeRequest + + \Description + Parse a 'probe' demangler server request. + + \Input *pProbe - pointer to probe structure to fill in + \Input *pRef - module ref + \Input *pSrc - server response to parse + + \Output + int32_t - TRUE if response parsed, else FALSE + + \Version 1.0 04/08/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static int32_t _ProtoMangleParseProbeRequest(ProtoMangleProbeT *pProbe, ProtoMangleRefT *pRef, const char *pSrc) +{ + int32_t bLinesParsed = 0, iParm; + char strParm[64]; + + if (pSrc == NULL) + { + return(FALSE); + } + + // init + pProbe->iCurCnt = 0; + pProbe->iSrcPort = -1; + pProbe->iId = -1; + + // parse target address + if ((pProbe->iId = _ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "targetIP", TRUE)) >= 0) + { + if ((pProbe->iAddr = SocketInTextGetAddr(strParm)) != 0) + { + bLinesParsed++; + } + } + + if (pProbe->iId < 0) + { + return(FALSE); + } + + // parse target port + if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "targetPort", TRUE) == pProbe->iId) + { + if ((pProbe->iPort = atoi(strParm)) > 0) + { + bLinesParsed++; + } + } + + // parse request tag + if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "tag", TRUE) == pProbe->iId) + { + ds_strnzcpy(pProbe->strTag, strParm, sizeof(pProbe->strTag)); + bLinesParsed++; + } + + // parse send count + if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "sendCount", TRUE) == pProbe->iId) + { + if (((iParm = atoi(strParm)) > 1) && (iParm < 32)) + { + pProbe->iSndCnt = iParm; + bLinesParsed++; + } + } + + // parse source port (optional) + if (_ProtoMangleGetParm(strParm, sizeof(strParm), pSrc, "sourcePort", TRUE) == pProbe->iId) + { + if ((iParm = atoi(strParm)) > 0) + { + #ifdef DIRTYCODE_PS4 + if (iParm > 32767) + { + int32_t iParmOriginal = iParm; + iParm &= 36863; + if (iParmOriginal != iParm) + { + NetPrintf(("protomangle: using port %d instead of %d.\n", iParm, iParmOriginal)); + } + } + #endif + pProbe->iSrcPort = iParm; + } + } + + return(bLinesParsed == 4); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleSetupProbeSocket + + \Description + Bind probe socket. + + \Input *pRef - module state + \Input *pProbe - probe parameters + + \Output + int32_t - local port bound to socket, or negative on error + + \Version 1.0 04/09/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static int32_t _ProtoMangleSetupProbeSocket(ProtoMangleRefT *pRef, ProtoMangleProbeT *pProbe) +{ + struct sockaddr BindAddr; + + // tear down previous socket if we're not using shared socket + _ProtoMangleSockClose(pRef, FALSE); + + // if we have a shared socket and this is a request for the shared socket port, set it and return + if ((pRef->pSharedProbeSock != NULL) && (pProbe->iSrcPort == pRef->iGamePort)) + { + NetPrintf(("protomangle: [%p] using shared socket ref %p to probe port %d\n", pRef, pRef->pSharedProbeSock, pRef->iGamePort)); + pRef->pProbeSock = pRef->pSharedProbeSock; + } + else + { + // recreate probe socket + if ((pRef->pProbeSock = SocketOpen(AF_INET, SOCK_DGRAM, 0)) == NULL) + { + NetPrintf(("protomangle: [%p] error creating probe socket\n", pRef)); + return(-1); + } + + // set up local binding + SockaddrInit(&BindAddr, AF_INET); + if (pProbe->iSrcPort != -1) + { + SockaddrInSetPort(&BindAddr, pProbe->iSrcPort); + } + else + { + SockaddrInSetPort(&BindAddr, pRef->iRandPort++); + } + + // bind locally + if (SocketBind(pRef->pProbeSock, &BindAddr, sizeof(BindAddr)) == SOCKERR_NONE) + { + NetPrintf(("protomangle: [%p] created probe socket %p bound to port %d\n", pRef, pRef->pProbeSock, SockaddrInGetPort(&BindAddr))); + } + else + { + NetPrintf(("protomangle: [%p] error binding probe socket %p to port %d\n", pRef, pRef->pProbeSock, SockaddrInGetPort(&BindAddr))); + // tear down socket since binding failed + _ProtoMangleSockClose(pRef, FALSE); + return(-1); + } + } + + // return local port to caller + SocketInfo(pRef->pProbeSock, 'bind', 0, &BindAddr, sizeof(BindAddr)); + return(SockaddrInGetPort(&BindAddr)); +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleSendProbe + + \Description + Send UDP probe packet(s) to demangler server. + + \Input *pRef - module state + \Input *pProbe - probe parameters + \Input iSrcPort - source port + \Input iProbeReq - probe request count from current request list + + \Version 04/08/03 (JLB) +*/ +/*************************************************************************************************F*/ +static void _ProtoMangleSendProbe(ProtoMangleRefT *pRef, ProtoMangleProbeT *pProbe, int32_t iSrcPort, int32_t iProbeReq) +{ + struct sockaddr PeerAddr; + char strMesg[128], strAddrText[20]; + int32_t iErr, iMsgSize; + + // set up target + SockaddrInit(&PeerAddr, AF_INET); + SockaddrInSetAddr(&PeerAddr, pProbe->iAddr); + SockaddrInSetPort(&PeerAddr, pProbe->iPort); + + // format probe package + ds_snzprintf(strMesg, sizeof(strMesg), "sourceIP=%s\r\nsourcePort=%d\r\ntag=%s\r\nsendCount=%d\r\n", + SocketInAddrGetText(pRef->iHostAddr, strAddrText, sizeof(strAddrText)), iSrcPort, pProbe->strTag, pProbe->iCurCnt); + iMsgSize = (int32_t)strlen(strMesg)+1; + + // send probe + iErr = SocketSendto(pRef->pProbeSock, strMesg, iMsgSize, 0, &PeerAddr, sizeof(PeerAddr)); + + #if PROTOMANGLE_VERBOSE + if (iErr == iMsgSize) + { + NetPrintf(("protomangle: [%p] success ", pRef)); + } + else + { + NetPrintf(("protomangle: [%p] error %d ", pRef, iErr)); + } + NetPrintf(("sending probe %d/%d (%d) from port %d\n", pProbe->iCurCnt, pProbe->iSndCnt, iProbeReq, iSrcPort)); + #else + (void)iErr; + #endif +} + +/*F*************************************************************************************************/ +/*! + \Function _ProtoMangleHandleProbeRequest + + \Description + Parse a probe request block, and issue any probes requested. + + \Input *pRef - module state + \Input *pSrc - probe request string + + \Output + int32_t - zero on success, negative on failure + + \Version 1.0 04/08/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static int32_t _ProtoMangleHandleProbeRequest(ProtoMangleRefT *pRef, const char *pSrc) +{ + ProtoMangleProbeT Probe; + int32_t iProbeReq, iSrcPort; + + // parse probe request list, + for (iProbeReq = 1; _ProtoMangleParseProbeRequest(&Probe, pRef, pSrc) != FALSE; iProbeReq++) + { + // make sure sequence IDs match + if (iProbeReq != Probe.iId) + { + NetPrintf(("protomangle: [%p] warning, probe sequence mismatch\n", pRef)); + } + + // init probe socket + if ((iSrcPort = _ProtoMangleSetupProbeSocket(pRef, &Probe)) < 0) + { + return(-1); + } + + // send iSndCnt probes + for (Probe.iCurCnt = 0; Probe.iCurCnt < Probe.iSndCnt; Probe.iCurCnt++) + { + _ProtoMangleSendProbe(pRef, &Probe, iSrcPort, iProbeReq); + } + + // advance to next response block + pSrc = _ProtoMangleParseAdvance(pSrc,"\n\n"); + } + + return(0); +} + + +/*** Public Functions ******************************************************************/ + + + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleCreate + + \Description + Allocate module state and prepare for use + + \Input *pServer - pointer to server name + \Input iPort - server port + \Input *pGameID - pointer to game feature ID string + \Input *pLKey - pointer to user LKey + + \Output + ProtoMangleRefT * - reference pointer (must be passed to all other functions) + + \Version 1.0 04/03/03 (JLB) First Version + \Version 1.1 06/16/03 (JLB) Added server parms as arguments +*/ +/*************************************************************************************************F*/ +ProtoMangleRefT *ProtoMangleCreate(const char *pServer, int32_t iPort, const char *pGameID, const char *pLKey) +{ + ProtoMangleRefT *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + NetPrintf(("protomangle: server:%s port:%d gameid:%s lkey:%s\n", pServer, iPort, pGameID, pLKey)); + + // allocate and init module state + if ((pRef = DirtyMemAlloc(sizeof(*pRef), PROTOMANGLE_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protomangle: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pRef, sizeof(*pRef)); + pRef->iMemGroup = iMemGroup; + pRef->pMemGroupUserData = pMemGroupUserData; + + // create http module + if ((pRef->pHttp = ProtoHttpCreate(PROTOMANGLE_HTTP_BUFSIZE)) == NULL) + { + DirtyMemFree(pRef, PROTOMANGLE_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + return(NULL); + } + + // since we know we are going to have an ongoing transaction, enable keep-alive from the beginning + ProtoHttpControl(pRef->pHttp, 'keep', 1, 0, NULL); + + // set default http timeout + ProtoHttpControl(pRef->pHttp, 'time', PROTOMANGLE_HTTP_TIMEOUT, 0, NULL); + + // generate 'random' port in range + pRef->iRandPort = (NetTick() % PROTOMANGLE_RAND_PORTRANGE) + PROTOMANGLE_RAND_PORTBASE; + + // save server parms, gameID, LKey + ds_strnzcpy(pRef->strServer, pServer, sizeof(pRef->strServer)); + pRef->iServerPort = iPort; + ds_strnzcpy(pRef->strGameID, pGameID, sizeof(pRef->strGameID)); + ds_strnzcpy(pRef->strLKey, pLKey, sizeof(pRef->strLKey)); + + // return ref to caller + return(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleDestroy + + \Description + Destroy the module and release its state + + \Input *pRef - reference pointer + + \Version 1.0 04/03/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +void ProtoMangleDestroy(ProtoMangleRefT *pRef) +{ + NetPrintf(("protomangle: [%p] shutting down\n", pRef)); + + // close probe socket, if we own it, else release it + _ProtoMangleSockClose(pRef, TRUE); + + // shut down http module + ProtoHttpDestroy(pRef->pHttp); + + // release ref + DirtyMemFree(pRef, PROTOMANGLE_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleConnect2 + + \Description + Initiate transaction with demangler service + + \Input *pRef - module state + \Input iMyPort - game port + \Input *pSessID - pointer to unique session ID (must be identical between clients) + \Input iSessIDLen - lenght of SessID string + + \Version 01/16/13 mclouatre +*/ +/*************************************************************************************************F*/ +void ProtoMangleConnect2(ProtoMangleRefT *pRef, int32_t iMyPort, const char *pSessID, int32_t iSessIDLen) +{ + // save port/addr + pRef->iHostAddr = SocketGetLocalAddr(); + pRef->iGamePort = iMyPort; + + // save cookie + ds_strnzcpy(pRef->strCookie, pSessID, sizeof(pRef->strCookie)); + + /* close previous http socket to prevent http connections spanning + sessions, which doesn't work with the demangler load balancer */ + ProtoHttpControl(pRef->pHttp, 'disc', 0, 0, NULL); + + // fire off initial request + _ProtoMangleHttpRequest(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleConnect + + \Description + Initiate transaction with demangler service + + \Input *pRef - module state + \Input iMyPort - game port + \Input *pSessID - pointer to unique session ID (must be identical between clients) + + \Version 04/07/03 jbrookes +*/ +/*************************************************************************************************F*/ +void ProtoMangleConnect(ProtoMangleRefT *pRef, int32_t iMyPort, const char *pSessID) +{ + ProtoMangleConnect2(pRef, iMyPort, pSessID, (int32_t)strlen(pSessID)); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleConnectSocket + + \Description + Initiate transaction with demangler service, using the given shared socket ref for + UDP probes that are to be sent out over the game port. + + \Input *pRef - module state + \Input uSockRef - socket reference to use for UDP probes sent over game port + \Input *pSessID - pointer to unique session ID (must be identical between clients) + + \Version 1.0 08/24/04 (JLB) First Version +*/ +/*************************************************************************************************F*/ +void ProtoMangleConnectSocket(ProtoMangleRefT *pRef, intptr_t uSockRef, const char *pSessID) +{ + struct sockaddr SockAddr; + + // import socket + pRef->pSharedProbeSock = SocketImport(uSockRef); + if (pRef->pSharedProbeSock == NULL) + { + NetPrintf(("protomangle: [%p] unable to import sockref 0x%08x\n", pRef, uSockRef)); + return; + } + + // save addr and socket ref + pRef->iHostAddr = SocketGetLocalAddr(); + + // look up and save bind port + SocketInfo(pRef->pSharedProbeSock, 'bind', 0, &SockAddr, sizeof(SockAddr)); + pRef->iGamePort = SockaddrInGetPort(&SockAddr); + + NetPrintf(("protomangle: [%p] created shared socket using platform socket ref %p bound to port %d\n", pRef, pRef->pSharedProbeSock, pRef->iGamePort)); + + // save cookie + ds_strnzcpy(pRef->strCookie, pSessID, sizeof(pRef->strCookie)); + + /* close previous http socket to prevent http connections spanning + sessions, which doesn't work with the demangler load balancer */ + ProtoHttpControl(pRef->pHttp, 'disc', 0, 0, NULL); + + // fire off initial reques + _ProtoMangleHttpRequest(pRef); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleUpdate + + \Description + Update module state + + \Input *pRef - module reference + + \Version 04/07/03 (JLB) +*/ +/*************************************************************************************************F*/ +void ProtoMangleUpdate(ProtoMangleRefT *pRef) +{ + // handle updates to connect process + if (pRef->eMangleState == ST_MNGL_WAIT) + { + int32_t iRecvLen; + + // update HTTP request + ProtoHttpUpdate(pRef->pHttp); + + // poll for completion + if ((iRecvLen = ProtoHttpRecvAll(pRef->pHttp, pRef->strBuf, sizeof(pRef->strBuf))) >= 0) + { + const char *pStr; + + // get data + NetPrintf(("protomangle: [%p] server response\n", pRef)); + NetPrintWrap(pRef->strBuf, 80); + + // parse response type + pStr = _ProtoMangleParseResponseType(pRef->strBuf, &pRef->eResponse); + switch(pRef->eResponse) + { + case PROTOMANGLE_RESP_SUCCESS: + // parse success result + pRef->eMangleState = _ProtoMangleParseSuccess(pRef, pStr) ? ST_MNGL_DONE : ST_MNGL_FAIL; + break; + case PROTOMANGLE_RESP_PROBE: + // parse and issue probes + if (_ProtoMangleHandleProbeRequest(pRef, pStr) >= 0) + { + // request server update + _ProtoMangleHttpRequest(pRef); + break; + } + // intentional fall-through in failure condition + case PROTOMANGLE_RESP_FAILURE: + case PROTOMANGLE_RESP_INVALID: + default: + pRef->eMangleState = ST_MNGL_FAIL; + break; + } + } + else if ((iRecvLen < 0) && (iRecvLen != PROTOHTTP_RECVWAIT)) + { + pRef->eMangleState = ST_MNGL_FAIL; + } + } + + // handle response to report + if (pRef->eMangleState == ST_MNGL_REPT) + { + // update HTTP request + ProtoHttpUpdate(pRef->pHttp); + + // done? + if (ProtoHttpStatus(pRef->pHttp, 'done', NULL, 0) != 0) + { + pRef->eMangleState = (ProtoHttpStatus(pRef->pHttp, 'code', NULL, 0) == 200) ? ST_MNGL_DONE : ST_MNGL_FAIL; + } + } +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleComplete + + \Description + Returns TRUE and fills in address and port if Demangler has finished operation successfully. + + \Input *pRef - module state + \Input *pAddr - storage for address (result==TRUE) + \Input *pPort - storage for port (result==TRUE) + + \Output + int32_t - TRUE if complete, else FALSE, or <0 to indicate an error + + \Version 1.0 04/09/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +int32_t ProtoMangleComplete(ProtoMangleRefT *pRef, int32_t *pAddr, int32_t *pPort) +{ + if (pRef->eMangleState == ST_MNGL_DONE) + { + // close probe socket + _ProtoMangleSockClose(pRef, TRUE); + + // return address and port to caller + if ((pAddr != NULL) && (pPort != NULL)) + { + *pAddr = pRef->iPeerAddr; + *pPort = pRef->iPeerPort; + } + + // reset to IDLE state + pRef->eMangleState = ST_MNGL_IDLE; + return(TRUE); + } + else if ((pRef->eMangleState == ST_MNGL_WAIT) || (pRef->eMangleState == ST_MNGL_REPT)) + { + return(FALSE); + } + else + { + pRef->eMangleState = ST_MNGL_IDLE; + return(-1); + } +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleReport + + \Description + Post result report to server. + + \Input *pRef - mangle ref + \Input eStatus - connection status + \Input iLatency - peer latency (optional, -1 to not send) + + \Output + int32_t - zero=success, negative=failure + + \Notes + Report is of the form: + + POST http://demangler.ea.com:3658/connectionStatus + Cookie: sessionID=6ba7b810-9dad-11d1-80b4-00c04fd430c8 + myIP=10.14.221.1&myPort=3250&version=1.0&status=connected&gameFeatureID=madden-ps2-2004&latency=80 + + \Version 1.0 07/18/03 (JLB) First Version +*/ +/*************************************************************************************************F*/ +int32_t ProtoMangleReport(ProtoMangleRefT *pRef, ProtoMangleStatusE eStatus, int32_t iLatency) +{ + char strData[128 + (3 * DIRTYCERT_SERVICENAME_SIZE)], strAddrText[20], strServiceName[DIRTYCERT_SERVICENAME_SIZE]; + int32_t iFormatted = 0; + + // make sure status is valid + if (eStatus > PROTOMANGLE_STATUS_FAILED) + { + return(-1); + } + + if (DirtyCertStatus('snam', strServiceName, DIRTYCERT_SERVICENAME_SIZE) < 0) + { + ds_snzprintf(strServiceName, sizeof(strServiceName), "invalid"); + } + + // format response to demangler server + iFormatted = ds_snzprintf(strData, sizeof(strData), "myIP=%s&myPort=%d&version=1.0&status=%s&gameFeatureID=%s\n", + SocketInAddrGetText(pRef->iHostAddr, strAddrText, sizeof(strAddrText)), pRef->iPeerPort, + _ProtoMangle_pReportTable[eStatus], pRef->strGameID); + + // add service name + ProtoHttpUrlEncodeStrParm(strData + iFormatted, sizeof(strData) - iFormatted, "&gameID=", strServiceName); + + // add latency info, if included + if (iLatency >= 0) + { + char strData2[32]; + ds_snzprintf(strData2, sizeof(strData2), "&latency=%d", iLatency); + ds_strnzcat(strData, strData2, sizeof(strData)); + } + + // post result to demangler server + _ProtoMangleHttpPost(pRef, "connectionStatus", pRef->strCookie, strData); + + // set state & return success + pRef->eMangleState = ST_MNGL_REPT; + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleControl + + \Description + ProtoMangle control function. + + \Input *pRef - mangle ref + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Version 05/12/05 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t ProtoMangleControl(ProtoMangleRefT *pRef, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue) +{ + if (iControl == 'abrt') + { + // close probe socket + _ProtoMangleSockClose(pRef, TRUE); + + // reset to IDLE state + pRef->eMangleState = ST_MNGL_IDLE; + return(0); + } + if (iControl == 'time') + { + ProtoHttpControl(pRef->pHttp, 'time', iValue, 0, NULL); + return(0); + } + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoMangleStatus + + \Description + Return module status based on input selector. + + \Input *pRef - mangle ref + \Input iSelect - status selector + \Input *pBuf - [out] storage for output data + \Input iBufSize - size of output buffer + + \Version 1.0 01/13/05 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t ProtoMangleStatus(ProtoMangleRefT *pRef, int32_t iSelect, void *pBuf, int32_t iBufSize) +{ + if (iSelect == 'idle') + { + return(pRef->eMangleState == ST_MNGL_IDLE); + } + if (iSelect == 'time') + { + return(ProtoHttpStatus(pRef->pHttp, 'time', NULL, 0)); + } + // unhandled + return(-1); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protoname.c b/r5dev/thirdparty/dirtysdk/source/proto/protoname.c new file mode 100644 index 00000000..aff3249c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protoname.c @@ -0,0 +1,136 @@ +/*H*************************************************************************************************/ +/*! + + \File protoname.c + + \Description + This module provides name lookup services via DNS. It is platform indepedent + and can be used to resolve names for use with other protocol modules. At this + point, name support is being removed from other protocol modules so it can be + centralized here. + + \Notes + None. + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 03/19/02 (GWS) First Version + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protoname.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function _ProtoNameSync + + \Description + Synchronous (blocking) lookup. + + \Input *pName - pointer to name + \Input iTimeout - timeout in millseconds + + \Output + uint32_t - 0=failed, else the IP address + + \Version 1.0 03/19/02 (GWS) First Version +*/ +/*************************************************************************************************F*/ +static uint32_t _ProtoNameSync(const char *pName, int32_t iTimeout) +{ + HostentT *pLookup; + uint32_t uAddr = 0; + + // start the query + pLookup = SocketLookup(pName, iTimeout); + if (pLookup != NULL) + { + // wait for completion + while (!pLookup->Done(pLookup)) + { + // give away some time + NetConnSleep(10); + } + + // get the address + uAddr = pLookup->addr; + pLookup->Free(pLookup); + } + + // return the address + return(uAddr); +} + + +/*** Public functions *****************************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function ProtoNameAsync + + \Description + Async lookup of a domain name. Same as SocketLookup + but deals with platform specific issues (i.e., this + works on PS2/EE while calling SocketLookup directly + would not). + + \Input *pName - pointer to name + \Input iTimeout - timeout in millseconds + + \Output + HostentT * - HostentT structure pointer + + \Version 1.0 03/19/02 (GWS) First Version +*/ +/*************************************************************************************************F*/ +HostentT *ProtoNameAsync(const char *pName, int32_t iTimeout) +{ + return(SocketLookup(pName, iTimeout)); +} + +/*F*************************************************************************************************/ +/*! + \Function ProtoNameSync + + \Description + Synchronous (blocking) lookup of a domain name. + + \Input *pName - pointer to name + \Input iTimeout - timeout in millseconds + + \Output + uint32_t - 0=failed, else the IP address + + \Version 1.0 03/19/02 (GWS) First Version +*/ +/*************************************************************************************************F*/ +uint32_t ProtoNameSync(const char *pName, int32_t iTimeout) +{ + return(_ProtoNameSync(pName, iTimeout)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protossl.c b/r5dev/thirdparty/dirtysdk/source/proto/protossl.c new file mode 100644 index 00000000..78d47d84 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protossl.c @@ -0,0 +1,14611 @@ +/*H********************************************************************************/ +/*! + \File protossl.c + + \Description + This module is a from-scratch TLS implementation. It does not use any + third-party code of any kind and was developed entirely by EA. + + \Notes + References: + TLS1.0 RFC: https://tools.ietf.org/html/rfc2246 + TLS1.1 RFC: https://tools.ietf.org/html/rfc4346 + TLS1.2 RFC: https://tools.ietf.org/html/rfc5246 + TLS1.3 RFC: https://tools.ietf.org/html/rfc8446 + ASN.1 encoding rules: https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + + \Copyright + Copyright (c) Electronic Arts 2002-2018 + + \Version 03/08/2002 (gschaefer) Initial SSL 2.0 implementation + \Version 03/03/2004 (sbevan) Added certificate validation + \Version 11/05/2005 (gschaefer) Rewritten to follow SSL 3.0 specification + \Version 10/12/2012 (jbrookes) Added support for TLS1.0 & TLS1.1 + \Version 10/20/2013 (jbrookes) Added server handshake, client cert support + \Version 11/06/2013 (jbrookes) Added support for TLS1.2 + \Version 03/31/2017 (eesponda) Added support for EC ciphers + \Version 03/28/2018 (jbrookes) Added support for TLS1.3 + \Version 08/15/2018 (jbrookes) Removed SSLv3 & RC4 ciphers +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtycert.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/crypt/cryptdef.h" +#include "DirtySDK/crypt/cryptaes.h" +#include "DirtySDK/crypt/cryptchacha.h" +#include "DirtySDK/crypt/cryptcurve.h" +#include "DirtySDK/crypt/cryptgcm.h" +#include "DirtySDK/crypt/crypthash.h" +#include "DirtySDK/crypt/crypthmac.h" +#include "DirtySDK/crypt/cryptmd5.h" +#include "DirtySDK/crypt/cryptrand.h" +#include "DirtySDK/crypt/cryptrsa.h" +#include "DirtySDK/crypt/cryptsha1.h" +#include "DirtySDK/util/base64.h" + +#include "DirtySDK/proto/protossl.h" + +#include "cryptrandpriv.h" + +/*** Defines **********************************************************************/ + +#define DEBUG_RAW_DATA (DIRTYCODE_LOGGING && 0) // display raw debug data +#define DEBUG_ENC_PERF (DIRTYCODE_LOGGING && 0) // show verbose crypto performance +#define DEBUG_MSG_LIST (DIRTYCODE_LOGGING && 0) // list the message states +#define DEBUG_ALL_OBJS (DIRTYCODE_LOGGING && 0) // display all headers/objects while parsing +#define DEBUG_VAL_CERT (DIRTYCODE_LOGGING && 0) // display verbose certificate validation info +#define DEBUG_RES_SESS (DIRTYCODE_LOGGING && 0) // display verbose session resume info +#define DEBUG_MOD_PRNT (DIRTYCODE_LOGGING && 0) // display public key modulus when parsing certificate (useful when adding new CA) + +#define SSL_VERBOSE_DEFAULT (1) + +#define SSL_CACERTFLAG_NONE (0) +#define SSL_CACERTFLAG_GOSCA (1) //!< identifies a GOS CA, for use on internal (*.ea.com) servers only +#define SSL_CACERTFLAG_CAPROVIDER (2) //!< identifies a CA that can function as a CA provider (used for cert loading) + +#define SSL_ERR_GOSCA_INVALIDUSE (-100) //!< error indicating invalid use of a GOS CA to access a non ea.com domain +#define SSL_ERR_CERT_INVALIDDATE (-101) //!< error indicating a CA certificate is expired +#define SSL_ERR_CAPROVIDER_INVALID (-102) //!< attempt to use an invalid CA for CA provider use + +#define SSL_MIN_PACKET 5 // smallest packet (5 bytes framing) +#define SSL_CRYPTO_PAD 2048 // maximum crypto overhead ref: http://tools.ietf.org/html/rfc5246#section-6.2.3 +#define SSL_RAW_PACKET 16384 // max raw data size ref: http://tools.ietf.org/html/rfc5246#section-6.2.1 +#define SSL_RCVMAX_PACKET ((SSL_RAW_PACKET+SSL_CRYPTO_PAD)*2) // max recv packet buffer; sized to allow a handshake packet up to twice the ssl frame size +#define SSL_SNDMAX_PACKET (SSL_RAW_PACKET) // max send packet buffer +#define SSL_SNDOVH_PACKET (384) // reserve space for header/mac in send packet +#define SSL_SNDLIM_PACKET (SSL_SNDMAX_PACKET-SSL_SNDOVH_PACKET) // max send user payload size + +#define SSL_KEYBLOCK_LEN (512) //!< length of keyblock that holds generated key material/tls1.3 secrets/keys/ivs +#define SSL_KEYMATERIAL_LEN (256) //!< length of key material we generate for TLS1.2 and prior + +#define SSL_SIG_MAX (512) //!< max signature size (4096 bit) + +#define SSL3_SESSION_TICKET_MAX (1536) //!< max session ticket size +#define SSL3_SESSION_NONCE_MAX (256) //!< max session ticket nonce size + +#define SSL_SESSHIST_MAX (32) // max number of previous sessions that will be tracked for possible later resumption +#define SSL_SESSVALIDTIME_MAX (2*60*60*1000) // session validity cached for a max of two hours (tls1.2 and prior) +#define SSL_SESSID_SIZE (32) + +#define SSL_CERTVALID_MAX (32) +#define SSL_CERTVALIDTIME_MAX (2*60*60*1000) // certificate validity cached for a max of two hours + +#define SSL_FINGERPRINT_SIZE (CRYPTSHA1_HASHSIZE) + +// min/default/max protocol versions supported +#define SSL3_VERSION_MIN (PROTOSSL_VERSION_TLS1_0) +#define SSL3_VERSION (PROTOSSL_VERSION_TLS1_2) +#define SSL3_VERSION_MAX (PROTOSSL_VERSION_TLS1_3) + +// internal names for TLS protocol versions +#define SSL3_SSLv3 (0x0300) // defined internally strictly to identify SSLv3 cipher suites that are still supported +#define SSL3_TLS1_0 (PROTOSSL_VERSION_TLS1_0) +#define SSL3_TLS1_1 (PROTOSSL_VERSION_TLS1_1) +#define SSL3_TLS1_2 (PROTOSSL_VERSION_TLS1_2) +#define SSL3_TLS1_3 (PROTOSSL_VERSION_TLS1_3) + +// TLS record types +#define SSL3_REC_CIPHER 20 // cipher change record +#define SSL3_REC_ALERT 21 // alert record +#define SSL3_REC_HANDSHAKE 22 // handshake record +#define SSL3_REC_APPLICATION 23 // application data record + +// TLS handshake header length +#define SSL3_MSG_HEADER_SIZE 4 + +// TLS handshake message types; see https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7 +#define SSL3_MSG_HELLO_REQUEST 0 // hello request +#define SSL3_MSG_CLIENT_HELLO 1 // client hello +#define SSL3_MSG_SERVER_HELLO 2 // server hello +#define SSL3_MSG_NEW_SESSION_TICKET 4 // session ticket +#define SSL3_MSG_END_OF_EARLY_DATA 5 // end of early data (tls1.3) +#define SSL3_MSG_ENCRYPTED_EXTENSIONS 8 // encrypted extensions (tls1.3) +#define SSL3_MSG_CERTIFICATE 11 // certificate +#define SSL3_MSG_SERVER_KEY 12 // server key data +#define SSL3_MSG_CERT_REQ 13 // certificate request +#define SSL3_MSG_SERVER_DONE 14 // server handshake done +#define SSL3_MSG_CERT_VERIFY 15 // certificate verify +#define SSL3_MSG_CLIENT_KEY 16 // client key data +#define SSL3_MSG_FINISHED 20 // handshake finished +#define SSL3_MSG_KEY_UPDATE 24 // key update (tls1.3) +#define SSL3_MSG_MESSAGE_HASH 254 // synthetic message for HRR + +// MAC types (ident equals size) +#define SSL3_MAC_NULL 0 // none +#define SSL3_MAC_MD5 16 // md5 +#define SSL3_MAC_SHA 20 // sha1 +#define SSL3_MAC_SHA256 32 // sha2-256 +#define SSL3_MAC_SHA384 48 // sha2-384 +#define SSL3_MAC_SHA512 64 // sha2-512 +#define SSL3_MAC_MAXSIZE (CRYPTHASH_MAXDIGEST) // maximum MAC size + +// PRF types +#define SSL3_PRF_NULL 0 // no cipher-suite defined prf +#define SSL3_PRF_SHA256 CRYPTHASH_SHA256 +#define SSL3_PRF_SHA384 CRYPTHASH_SHA384 + +// key types +#define SSL3_KEY_NULL 0 // no record key exchange +#define SSL3_KEY_RSA 1 // use rsa +#define SSL3_KEY_ECDHE 2 // use ecdhe + +// signing types +#define SSL3_SIGN_RSA 1 +#define SSL3_SIGN_ECDSA 64 + +// key lengths +#define SSL3_KEYLEN_NULL 0 +#define SSL3_KEYLEN_128 16 // 128-bit key length in bytes +#define SSL3_KEYLEN_256 32 // 256-bit key length in bytes + +// cipher types +#define SSL3_ENC_NULL 0 // no record encryption +#define SSL3_ENC_AES 1 // use aes +#define SSL3_ENC_GCM 2 // use gcm +#define SSL3_ENC_CHACHA 3 // use chacha + +// hash ids +#define SSL3_HASHID_NONE (0) +#define SSL3_HASHID_MD5 (1) +#define SSL3_HASHID_SHA1 (2) +#define SSL3_HASHID_SHA224 (3) +#define SSL3_HASHID_SHA256 (4) +#define SSL3_HASHID_SHA384 (5) +#define SSL3_HASHID_SHA512 (6) +#define SSL3_HASHID_MAX (SSL3_HASHID_SHA512) + +// signature algorithm ids +#define SSL3_SIGALG_NONE (0) +#define SSL3_SIGALG_RSA (1) +#define SSL3_SIGALG_DSA (2) +#define SSL3_SIGALG_ECDSA (3) + +// signature verification schemes +#define SSL3_SIGVERIFY_NONE (0) +#define SSL3_SIGVERIFY_RSA_PKCS1 (1) +#define SSL3_SIGVERIFY_RSA_PSS (2) + +// curve types +enum +{ + SSL3_CURVE_NONE = 0xff, + SSL3_CURVE_SECP256R1 = 0, + SSL3_CURVE_X25519, + SSL3_CURVE_SECP384R1, + SSL3_CURVE_X448, + SSL3_NUM_CURVES +}; +// index of default ec +#define SSL3_CURVE_DEFAULT (SSL3_CURVE_SECP256R1) +// index of max ec +#define SSL3_CURVE_MAX (SSL3_CURVE_X448) + +/* signature schemes (tls1.3, but overlaps with previous signature algorithms); see + https://tools.ietf.org/html/rfc8446#section-4.2.3 */ + +// RSASSA-PKCS1-v1_5 algorithms +#define SSL3_SIGSCHEME_RSA_PKCS1_MD5 (0x0101) // deprecated +#define SSL3_SIGSCHEME_RSA_PKCS1_SHA1 (0x0201) // legacy +#define SSL3_SIGSCHEME_RSA_PKCS1_SHA256 (0x0401) +#define SSL3_SIGSCHEME_RSA_PKCS1_SHA384 (0x0501) +#define SSL3_SIGSCHEME_RSA_PKCS1_SHA512 (0x0601) +// ECDSA algorithms +#define SSL3_SIGSCHEME_ECDSA_SHA1 (0x0203) // legacy, required for TLS1.0/1.1 ECDSA cipher suites +#define SSL3_SIGSCHEME_ECDSA_SHA256 (0x0403) +#define SSL3_SIGSCHEME_ECDSA_SHA384 (0x0503) +#define SSL3_SIGSCHEME_ECDSA_SHA512 (0x0603) +// RSASSA-PSS algorithms with public key OID rsaEncryption +#define SSL3_SIGSCHEME_RSA_PSS_RSAE_SHA256 (0x0804) +#define SSL3_SIGSCHEME_RSA_PSS_RSAE_SHA384 (0x0805) +#define SSL3_SIGSCHEME_RSA_PSS_RSAE_SHA512 (0x0806) +// EdDSA algorithms +#define SSL3_SIGSCHEME_EDDSA_ED25519 (0x0807) // unsupported +#define SSL3_SIGSCHEME_EDDSA_ED448 (0x0808) // unsupported +// RSASSA-PSS algorithms with public key OID RSASSA-PSS +#define SSL3_SIGSCHEME_RSA_PSS_PSS_SHA256 (0x0809) +#define SSL3_SIGSCHEME_RSA_PSS_PSS_SHA384 (0x080a) +#define SSL3_SIGSCHEME_RSA_PSS_PSS_SHA512 (0x080b) + +// curve types +#define SSL3_CURVETYPE_EXPLICIT_PRIME (1) +#define SSL3_CURVETYPE_EXPLICIT_CHAR (2) +#define SSL3_CURVETYPE_NAMED_CURVE (3) + +// TLS alert defines + +// alert level +#define SSL3_ALERT_LEVEL_WARNING 1 +#define SSL3_ALERT_LEVEL_FATAL 2 + +// alert identifiers +#define SSL3_ALERT_DESC_CLOSE_NOTIFY 0 +#define SSL3_ALERT_DESC_UNEXPECTED_MESSAGE 10 +#define SSL3_ALERT_DESC_BAD_RECORD_MAC 20 +#define SSL3_ALERT_DESC_DECRYPTION_FAILED 21 // reserved +#define SSL3_ALERT_DESC_RECORD_OVERFLOW 22 +#define SSL3_ALERT_DESC_DECOMPRESSION_FAILURE 30 // reserved +#define SSL3_ALERT_DESC_HANDSHAKE_FAILURE 40 +#define SSL3_ALERT_DESC_NO_CERTIFICATE 41 // reserved +#define SSL3_ALERT_DESC_BAD_CERTFICIATE 42 +#define SSL3_ALERT_DESC_UNSUPPORTED_CERTIFICATE 43 +#define SSL3_ALERT_DESC_CERTIFICATE_REVOKED 44 +#define SSL3_ALERT_DESC_CERTIFICATE_EXPIRED 45 +#define SSL3_ALERT_DESC_CERTIFICATE_UNKNOWN 46 +#define SSL3_ALERT_DESC_ILLEGAL_PARAMETER 47 +// the following alert types are all TLS only +#define SSL3_ALERT_DESC_UNKNOWN_CA 48 +#define SSL3_ALERT_DESC_ACCESS_DENIED 49 +#define SSL3_ALERT_DESC_DECODE_ERROR 50 +#define SSL3_ALERT_DESC_DECRYPT_ERROR 51 +#define SSL3_ALERT_DESC_EXPORT_RESTRICTION 60 // reserved +#define SSL3_ALERT_DESC_PROTOCOL_VERSION 70 +#define SSL3_ALERT_DESC_INSUFFICIENT_SECURITY 71 +#define SSL3_ALERT_DESC_INTERNAL_ERROR 80 +#define SSL3_ALERT_DESC_INAPPROPRIATE_FALLBACK 86 +#define SSL3_ALERT_DESC_USER_CANCELLED 90 +#define SSL3_ALERT_DESC_NO_RENEGOTIATION 100 // reserved +#define SSL3_ALERT_DESC_MISSING_EXTENSION 109 +#define SSL3_ALERT_DESC_UNSUPPORTED_EXTENSION 110 +#define SSL3_ALERT_DESC_CERTIFICATE_UNOBTAINABLE 111 +#define SSL3_ALERT_DESC_UNRECOGNIZED_NAME 112 +#define SSL3_ALERT_DESC_BAD_CERTIFICATE_STATUS 113 +#define SSL3_ALERT_DESC_BAD_CERTIFICATE_HASH 114 +#define SSL3_ALERT_DESC_UNKNOWN_PSK_IDENTITY 115 +#define SSL3_ALERT_DESC_CERTIFICATE_REQUIRED 116 +#define SSL3_ALERT_DESC_NO_APPLICATION_PROTOCOL 120 + +// certificate setup +#define SSL_CERT_X509 1 + +// tls extension types +#define SSL_EXTN_SERVER_NAME (0x0000) //!< identifier for server name extension +#define SSL_EXTN_ELLIPTIC_CURVES (0x000a) //!< identifier for elliptic curves extension and supported curves +#define SSL_EXTN_SIGNATURE_ALGORITHMS (0x000d) //!< identifier for signature algorithms extension +#define SSL_EXTN_ALPN (0x0010) //!< identifier for ALPN (Application Level Protocol Negotiation) extension +#define SSL_EXTN_PRE_SHARED_KEY (0x0029) //!< identifier for pre_shared_key extension +#define SSL_EXTN_EARLY_DATA (0x002a) //!< identifier for early_data extension (not supported) +#define SSL_EXTN_SUPPORTED_VERSIONS (0x002b) //!< identifier for supported_versions extension +#define SSL_EXTN_COOKIE (0x002c) //!< identifier for cookie extension +#define SSL_EXTN_PSK_MODES (0x002d) //!< identifier for psk_key_exchange_modes extension +#define SSL_EXTN_CERT_AUTH (0x002f) //!< identifier for certificate_authorities extension (not supported) +#define SSL_EXTN_OID_FILTER (0x0030) //!< idetntifer for oid_filter extension (not supported) +#define SSL_EXTN_POST_HANDSHAKE_AUTH (0x0031) //!< identifier for post_handshake_auth extension (not supported) +#define SSL_EXTN_SIGALGS_CERT (0x0032) //!< identifier for signature_algorithms_cert extension (not supported) +#define SSL_EXTN_KEY_SHARE (0x0033) //!< identifier for key_share extension +#define SSL_EXTN_MAX (SSL_EXTN_KEY_SHARE) //!< max defined extension; renegotiation_info is special case + +#define SSL_EXTN_RENEGOTIATION_INFO (0xff01) //!< renegotiation_info extension (not supported) + +#define SSL_EXTN_ALL (0xffff) //!< special value used to indicate all extensions, not a bitfield + +// ALPN defines +#define SSL_ALPN_MAX_PROTOCOLS (4) //!< max supported ALPN protocols + +// tls states +enum +{ + // network states + ST_IDLE = 0, + ST_ADDR, + ST_CONN, + ST_WAIT_CONN, + ST_WAIT_CA, // waiting for the CA to be fetched + + // ssl states + ST3_SEND_HELLO = 20, + ST3_RECV_HELLO, + ST3_SEND_HELLO_RETRY, + ST3_SEND_EXTN, + ST3_SEND_CERT, + ST3_SEND_CERT_REQ, + ST3_SEND_KEY, + ST3_SEND_DONE, + ST3_SEND_VERIFY, + ST3_SEND_CHANGE, + ST3_SEND_FINISH, + ST3_RECV_CHANGE, + ST3_RECV_FINISH, + // post-finish states + ST3_SEND_HELLO_REQUEST, + ST3_PROC_ASYNC, // synthetic state for iterative async operations + ST3_SECURE, + ST_UNSECURE, + + // failure states + ST_FAIL = 0x1000, + ST_FAIL_DNS, + ST_FAIL_CONN, + ST_FAIL_CONN_SSL2, + ST_FAIL_CONN_NOTSSL, + ST_FAIL_CONN_MINVERS, + ST_FAIL_CONN_MAXVERS, + ST_FAIL_CONN_NOCIPHER, + ST_FAIL_CONN_NOCURVE, + ST_FAIL_CERT_NONE, + ST_FAIL_CERT_INVALID, + ST_FAIL_CERT_HOST, + ST_FAIL_CERT_NOTRUST, + ST_FAIL_CERT_BADDATE, + ST_FAIL_SETUP, + ST_FAIL_SECURE, + ST_FAIL_CERT_REQUEST +}; + +// ASN.1 definitions +#define ASN_CLASS_UNIV 0x00 +#define ASN_CLASS_APPL 0x40 +#define ASN_CLASS_CONT 0x80 +#define ASN_CLASS_PRIV 0xc0 + +#define ASN_PRIMITIVE 0x00 +#define ASN_CONSTRUCT 0x20 + +#define ASN_TYPE_BOOLEAN 0x01 +#define ASN_TYPE_INTEGER 0x02 +#define ASN_TYPE_BITSTRING 0x03 +#define ASN_TYPE_OCTSTRING 0x04 +#define ASN_TYPE_NULL 0x05 +#define ASN_TYPE_OBJECT 0x06 +#define ASN_TYPE_UTF8STR 0x0c +#define ASN_TYPE_SEQN 0x10 +#define ASN_TYPE_SET 0x11 +#define ASN_TYPE_PRINTSTR 0x13 +#define ASN_TYPE_T61 0x14 +#define ASN_TYPE_IA5 0x16 +#define ASN_TYPE_UTCTIME 0x17 +#define ASN_TYPE_GENERALIZEDTIME 0x18 +#define ASN_TYPE_UNICODESTR 0x1e + +#define ASN_IMPLICIT_TAG 0x80 +#define ASN_EXPLICIT_TAG 0xa0 + +enum { + ASN_OBJ_NONE = 0, + ASN_OBJ_COUNTRY, + ASN_OBJ_STATE, + ASN_OBJ_CITY, + ASN_OBJ_ORGANIZATION, + ASN_OBJ_UNIT, + ASN_OBJ_COMMON, + ASN_OBJ_SUBJECT_ALT, + ASN_OBJ_BASIC_CONSTRAINTS, + ASN_OBJ_MD5, + ASN_OBJ_SHA1, + ASN_OBJ_SHA256, + ASN_OBJ_SHA384, + ASN_OBJ_SHA512, + ASN_OBJ_RSA_PKCS_KEY, + ASN_OBJ_ECDSA_KEY, + ASN_OBJ_RSA_PKCS_MD5, + ASN_OBJ_RSA_PKCS_SHA1, + ASN_OBJ_RSA_PKCS_SHA256, + ASN_OBJ_RSA_PKCS_SHA384, + ASN_OBJ_RSA_PKCS_SHA512, + ASN_OBJ_RSASSA_PSS, + ASN_OBJ_ECDSA_SHA256, + ASN_OBJ_ECDSA_SHA384, + ASN_OBJ_ECDSA_SHA512, + ASN_OBJ_ECDSA_SECP256R1, + ASN_OBJ_ECDSA_SECP384R1, + ASN_OBJ_ECDSA_SECP521R1, + ASN_OBJ_PKCS1_MGF1, + ASN_OBJ_COUNT, +}; + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! ASN object table +typedef struct ASNObjectT +{ + int32_t iType; //!< symbolic type + int32_t iSize; //!< size of object + uint8_t strData[16]; //!< object identifier +} ASNObjectT; + +//! generic binary certificate container plus some parsed certificate info +typedef struct CertificateDataT +{ + int32_t iCrvType; + int32_t iKeyType; + int32_t iSigType; + int32_t iCertSize; + uint8_t aCertData[1]; +} CertificateDataT; + +//! structure holding .pem certificate signature begin/end strings +typedef struct CertificateSignatureT +{ + const char *pCertBeg; + const char *pCertEnd; + uint32_t uCertType; +} CertificateSignatureT; + +//! note: this layout differs somewhat from x.509, but I think it +//! makes more sense this way +typedef struct X509CertificateT +{ + ProtoSSLCertIdentT Issuer; //!< certificate was issued by (matches subject in another cert) + ProtoSSLCertIdentT Subject; //!< certificate was issued for this site/authority + + char strGoodFrom[32]; //!< good from this date + char strGoodTill[32]; //!< until this date + uint64_t uGoodFrom; + uint64_t uGoodTill; + + const uint8_t *pSubjectAlt; //!< subject alternative name, if present + int32_t iSubjectAltLen; //!< subject alternative length + + int32_t iSerialSize; //!< certificate serial number + uint8_t SerialData[32]; //!< certificate serial data + + int32_t iSigType; //!< digital signature type + int32_t iSigHash; //!< signature hash algorithm + int32_t iSigSize; //!< size of signature data + int32_t iSigSalt; //!< salt length when signature algorithm is rsassa-pss + int32_t iMgfHash; //!< message-generation function hash when signature algorithm is rsassa-pss + uint8_t SigData[SSL_SIG_MAX]; //!< digital signature data + + int32_t iKeyType; //!< public key algorithm type + int32_t iCrvType; //!< type of curve, if keytype==ecdsa + int32_t iKeyModSize; //!< size of public key modulus (rsa) / curve data (ecdsa) + uint8_t KeyModData[SSL_SIG_MAX];//!< public key modulus + int32_t iKeyExpSize; //!< size of public key exponent + uint8_t KeyExpData[16]; //!< public key exponent + + // iMaxHeight is valid only if iCertIsCA is set + int32_t iCertIsCA; //!< whether this cert can be used as a CA cert + int32_t iMaxHeight; //!< the pathLenConstraints of a CA, 0 means no limit/field not present + + int32_t iHashSize; //!< size of certificate signature hash + CryptHashTypeE eHashType; //!< type of certificate signature hash + uint8_t HashData[CRYPTHASH_MAXDIGEST]; +} X509CertificateT; + +//! private key definition +typedef struct X509PrivateKeyT +{ + CryptBinaryObjT Modulus; //!< key modulus + CryptBinaryObjT PublicExponent; //!< public key exponent + CryptBinaryObjT PrivateExponent; //!< private key exponent + CryptBinaryObjT PrimeP; //!< prime factor (p) of modulus + CryptBinaryObjT PrimeQ; //!< prime factor (q) of modulus + CryptBinaryObjT ExponentP; //!< exponent d mod p-1 + CryptBinaryObjT ExponentQ; //!< exponent d mod q-1 + CryptBinaryObjT Coefficient; //!< inverse of q mod p + char strPrivKeyData[4096]; //!< buffer to hold private key data +} X509PrivateKeyT; + +//! defines data for formatting/processing Finished packet +struct SSLFinished +{ + char strLabel[16]; + uint8_t uNextState[2]; // [not resuming, resuming] +}; + +//! define data for handshake transitions +typedef struct SSLHandshakeMapT +{ + int8_t iCurMsg; + int8_t iNxtMsg; +} SSLHandshakeMapT; + +//! minimal certificate authority data (just enough to validate another certificate) +typedef struct ProtoSSLCACertT +{ + ProtoSSLCertIdentT Subject; //!< identity of this certificate authority + uint32_t uFlags; //!< SSL_CACERTFLAG_* + + int32_t iKeyType; //!< public key algorithm type + int32_t iCrvType; //!< type of curve, if keytype==ecdsa + + int32_t iKeyModSize; //!< size of public key modulus + const uint8_t *pKeyModData; //!< public key modulus for signature verification + + int32_t iKeyExpSize; //!< size of public key exponent + uint8_t KeyExpData[16]; //!< public key exponent + + int32_t iMemGroup; //!< memgroup cert was allocated with (zero == static) + void *pMemGroupUserData; //!< memgroup user data + + X509CertificateT *pX509Cert; //!< X509 certificate, if this cert has not yet been validated + struct ProtoSSLCACertT *pNext; //!< link to next cert in list +} ProtoSSLCACertT; + +//! async op function pointer type +typedef int32_t (AsyncOpT)(ProtoSSLRefT *pState); + +//! info for async op +typedef struct AsyncOpInfoT +{ + AsyncOpT *pAsyncOp; //!< async operation to execute, when in ASYNC state + int32_t iNextState; //!< next state, when in ASYNC + int32_t iFailState; //!< fail state, if async op fails + int32_t iFailAlert; //!< fail alert, if async op fails +} AsyncOpInfoT; + +//! cipher parameter lookup structure +typedef struct CipherSuiteT +{ + uint16_t uIdent; //!< two-byte identifier + uint16_t uMinVers; //!< minimum required SSL version + uint8_t uKey; //!< key exchange algorithm (SSL3_KEY_*) + uint8_t uLen; //!< key length (SSL3_KEYLEN_*) + uint8_t uSig; //!< signature algorithm (SSL3_SIGALG_*) + uint8_t uEnc; //!< encryption algorithm (SSL3_ENC_*) + uint8_t uMac; //!< MAC digest size (SSL3_MAC_*) + uint8_t uMacType; //!< MAC digest type (CRYPTHASH_*) + uint8_t uVecSize; //!< explicit IV size + uint8_t uPrfType; //!< cipher-suite PRF type (SSL3_PRF_*) + // no explicit pad here to make static initialization cleaner + uint32_t uId; //!< PROTOSSL_CIPHER_* + char strName[66]; //!< cipher name +} CipherSuiteT; + +//! elliptic curve used for key exchange +typedef struct EllipticCurveT +{ + uint16_t uIdent; //!< two-byte identifier + uint16_t uId; //!< PROTOSSL_CURVE_* + char strName[32]; //!< curve name +} EllipticCurveT; + +//! session info, used for tls1.2 and prior resume +typedef struct SessionInfoT +{ + uint16_t uSslVersion; //!< ssl version used for connection + uint16_t uCipherId; //!< cipher used for connection + uint8_t SessionId[SSL_SESSID_SIZE]; //!< session id used to identify session + uint8_t MasterSecret[48]; //!< master secret generated for session +} SessionInfoT; + +//! session ticket data (tls1.3 session tickets only, not supported in previous versions) +typedef struct SessionTicketT +{ + CryptHashTypeE eHashType; + uint32_t uRecvTime; + uint32_t uLifetime; + uint32_t uAgeAdd; + uint32_t uMaxEarlyDataSize; + uint16_t uTickLen; + uint16_t uExtnLen; + uint8_t uNonceLen; + uint8_t aResumeKey[CRYPTHASH_MAXDIGEST]; + uint8_t aTicketNonce[SSL3_SESSION_NONCE_MAX]; + uint8_t aTicketData[SSL3_SESSION_TICKET_MAX]; +} SessionTicketT; + +//! secure session history, used for ssl resume +typedef struct SessionHistoryT +{ + char strHost[256]; //!< hostname for the session + uint16_t uPort; //!< port for the session + uint8_t bSessionTicket; //!< TRUE if session ticket (tls1.3) else tls1.2-style resume info + uint8_t _pad; + uint32_t uSessionUseTick; //!< tick when session was last used + uint32_t uSessionExpTick; //!< tick when session will expire + union + { + SessionInfoT SessionInfo; + SessionTicketT SessionTicket; + }; +} SessionHistoryT; + +//! certificate validation history, used to skip validation of certificates we've already valdiated +typedef struct CertValidHistoryT +{ + uint32_t uCertValidTick; //!< tick when certificate was validated + uint32_t uCertSize; //!< size of certificate data + uint8_t FingerprintId[SSL_FINGERPRINT_SIZE]; //!< certificate fingerprint +} CertValidHistoryT; + +//! alpn extension protocol information +typedef struct AlpnProtocolT +{ + uint8_t uLength; + uint8_t _pad[3]; + char strName[256]; +} AlpnProtocolT; + +//! signature algorithm information +typedef struct SignatureAlgorithmT +{ + uint8_t uHashId; + uint8_t uSigAlg; +} SignatureAlgorithmT; + +//! signature scheme +typedef struct SignatureSchemeT +{ + uint16_t uIdent; //!< two-byte signature scheme identifier + SignatureAlgorithmT SigAlg; //!< hash and signature algorithm used by the signature scheme + uint8_t uVerifyScheme; //!< verification algorithm (RSA signature schemes only) + uint8_t uOidType; //!< certificate oid used for signature scheme selection +} SignatureSchemeT; + +//! signature generate/verify data for async op +typedef struct SignatureVerifyT +{ + const SignatureSchemeT *pSigScheme; + const X509PrivateKeyT *pPrivateKey; + uint8_t DsaContext[CRYPTCURVE_DSA_MAXSTATE]; + uint8_t aSigData[SSL_SIG_MAX]; + int32_t iSigSize; + uint8_t aHashDigest[CRYPTHASH_MAXDIGEST]; + int32_t iHashSize; + CryptHashTypeE eHashType; + uint8_t aKeyData[128]; + int32_t iKeySize; + int32_t iNextState; + uint8_t bEccContextInitialized; + uint8_t _pad[3]; +} SignatureVerifyT; + +//! extra state information required for secure connections (not allocated for unsecure connections) +typedef struct SecureStateT +{ + uint64_t uSendSeqn; //!< send sequence number + uint64_t uRecvSeqn; //!< recv sequence number + + uint32_t uTimer; //!< base of setup timing + + int32_t iSendProg; //!< progress within send + int32_t iSendSize; //!< total bytes to send + + int32_t iRecvHead; //!< progress receiving packet header + int32_t iRecvProg; //!< progress receiving packet data + int32_t iRecvSize; //!< total bytes to receive (tls record size) + int32_t iRecvBase; //!< progress decrypting packet data + int32_t iRecvHshkProg; //!< progress processing handshake messages + int32_t iRecvHshkSize; //!< size of current handshake message being processed (including header) + + const CipherSuiteT *pCipher; //!< selected cipher suite + const EllipticCurveT *pEllipticCurve; //!< selected elliptic curve + const SignatureSchemeT *pSigScheme; //!< selected signature scheme + uint32_t uSentCiphers; //!< bitmask of ciphers that were sent in ClientHello + uint16_t uRetryCipher; //!< cipher ident sent by HelloRetryRequest (tls1.3) + uint16_t _pad16; + + SignatureVerifyT SigVerify; + + uint8_t ClientRandom[32]; //!< clients random seed + uint8_t ServerRandom[32]; //!< servers random seed + uint8_t SessionId[SSL_SESSID_SIZE]; //!< session id + uint16_t uSslVersion; //!< ssl version of connection + uint16_t uSslClientVersion; //!< client-requested ssl version (validated in premaster secret) + + uint8_t bSessionResume; //!< true if session is resumed during handshaking + uint8_t bSendSecure; //!< true if sending secure + uint8_t bRecvSecure; //!< true if receiving secure + uint8_t bDateVerifyFailed; //!< true if date verification of a cert in chain failed + uint8_t bRSAContextInitialized; //!< true if we have initialized the RSAContext + uint8_t bEccContextInitialized; //!< true if we have initialized the EccContext + uint8_t bEccKeyGenerated; //!< true if an Ecc Public Key has been generated + uint8_t bSigGenerated; //!< true if signature source has been generated + uint8_t bRecvProc; //!< true if packet has been processed by _RecvPacket() + uint8_t bHelloRetry; //!< true if we're in a HelloRetryRequest flow (tls1.3) + uint8_t bSentCert; //!< true if a certificate was sent in handshaking + uint8_t bRecvCert; //!< true if a certificate was received in handshaking + uint8_t bSentSessionId; //!< true if we sent a session identifier in clienthello + uint8_t bRenegotiationInfo; //!< true if renegotation info was sent by peer + uint8_t uPubKeyLength; //!< length of pubkey used for ECDHE key exchange + int8_t iCurMsg; //!< current handshake message id, used for flow verification + + uint8_t PubKey[256]; //!< peer's public key, used in ECDHE key exchange (note that in the server flow, the server's public key lives here temporarily) + uint8_t PreMasterKey[48]; //!< pre-master-key + uint8_t MasterKey[48]; //!< master key + uint8_t PreSharedKey[CRYPTHASH_MAXDIGEST]; //!< PSK for tls1.3, used in resume flow + + CryptRSAT RSAContext; + + uint8_t KeyBlock[SSL_KEYBLOCK_LEN]; //!< key block + uint8_t *pServerMAC; //!< server mac secret + uint8_t *pClientMAC; //!< client mac secret + uint8_t *pServerKey; //!< server key secret + uint8_t *pClientKey; //!< client key secret + uint8_t *pServerInitVec; //!< init vector (CBC ciphers) + uint8_t *pClientInitVec; //!< init vector (CBC ciphers) + uint8_t *pServerSecret; //!< tls1.3 server early/handshake/application/... secret + uint8_t *pClientSecret; //!< tls1.3 client early/handshake/application/... secret + uint8_t *pResumeSecret; //!< tls1.3 resumption secret + + const uint8_t *pCookie; //!< pointer to cookie in receive buffer (tls1.3 extension) + + CryptMD5T HandshakeMD5; //!< MD5 of all handshake data + CryptSha1T HandshakeSHA; //!< SHA of all handshake data + CryptSha2T HandshakeSHA256; //!< SHA256 of all handshake data + CryptSha2T HandshakeSHA384; //!< SHA384 of all handshake data + CryptSha2T HandshakeSHA512; //!< SHA512 of all handshake data + uint8_t aFinishHash[CRYPTHASH_MAXDIGEST]; //!< handshake data storage for tls1.3 flow + + uint8_t aClientHelloHash[CRYPTSHA256_HASHSIZE]; //!< hash of sent clienthello; used in HelloRetryRequest flow + + CryptAesT ReadAes; //!< aes read cipher state + CryptAesT WriteAes; //!< aes write cipher state + + CryptGcmT ReadGcm; //!< gcm read cipher state + CryptGcmT WriteGcm; //!< gcm write cipher state + + CryptChaChaT ReadChaCha; //!< chacha read cipher state + CryptChaChaT WriteChaCha; //!< chacha write cipher state + + X509CertificateT Cert; //!< the x509 certificate + + uint8_t EccContext[CRYPTCURVE_DH_MAXSTATE]; //!< elliptic curve state + + char strAlpnProtocol[256]; //!< protocol negotiated using the alpn extension + + uint8_t RecvHead[SSL_MIN_PACKET]; //!< buffer to receive the SSL packet header + uint8_t SendData[SSL_SNDMAX_PACKET]; //!< put at end to make references efficient, include space for debug fence + uint8_t RecvData[SSL_RCVMAX_PACKET]; //!< put at end to make references efficient, include space for debug fence +} SecureStateT; + +//! module state +struct ProtoSSLRefT +{ + SocketT *pSock; //!< comm socket + HostentT *pHost; //!< host entry + + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + NetCritT SecureCrit; //!< for guarding multithreaded access to secure state + + char strHost[256]; //!< host that we connect to. + struct sockaddr PeerAddr; //!< peer info + struct sockaddr LocalAddr; //!< cached value of our local address being used, some connections are very short lived making it otherwise difficult to read the local addr info reliably + + AsyncOpInfoT AsyncInfo; //!< info for async op execution + + int32_t iState; //!< protocol state + int32_t iClosed; //!< socket closed flag + SecureStateT *pSecure; //!< secure state reference + X509CertificateT *pCertToVal; //!< server cert to be validated (used in ST_WAIT_CA state) + ProtoSSLCertInfoT CertInfo; //!< certificate info (used on failure) + CertificateDataT *pCertificate; //!< server/client certificate + char *pPrivateKey; //!< server/client private key + int32_t iPrivateKeyLen; //!< private key length + + uint32_t uEnabledCiphers; //!< enabled ciphers + uint32_t uEnabledCurves; //!< enabled curves + int32_t iRecvBufSize; //!< TCP recv buffer size; 0=default + int32_t iSendBufSize; //!< TCP send buffer size; 0=default + int32_t iLastSocketError; //!< Last socket error before closing the socket + int32_t iCARequestId; //!< CA request id (valid if positive) + + int32_t iMaxSendRate; //!< max send rate (0=uncapped) + int32_t iMaxRecvRate; //!< max recv rate (0=uncapped) + + uint16_t uSslVersion; //!< ssl version application wants us to use + uint16_t uSslVersionMin; //!< minimum ssl version application will accept + + uint8_t bAllowAnyCert; //!< bypass certificate validation + uint8_t bSessionResumeEnabled; //!< trueif session resume is enabled (default) + uint8_t bServer; //!< true if server, else client + uint8_t bReuseAddr; //!< if true set SO_REUSEADDR + uint8_t bNoDelay; //!< if true set TCP_NODELAY on the socket + uint8_t bKeepAlive; //!< if true override tcp keep-alive (disabled by default) + uint8_t bAsyncRecv; //!< if true enable async receive on the ssl socket + int8_t iClientCertLevel; //!< 0=no client cert required; 1=client cert requested, 2=client cert required + uint8_t uHelloExtn; //!< enabled ClientHello extensions + int8_t iCurveDflt; //!< default elliptic curve to use in ClientHello (tls1.3) + int8_t iVerbose; //!< spam level + + uint8_t bCertInfoSet; //!< true if cert info has been set + uint8_t uAlertLevel; //!< level of most recent alert + uint8_t uAlertValue; //!< value of most recent alert + uint8_t bAlertSent; //!< true if most recent alert was sent, else false if it was received + uint8_t _pad; + + uint32_t uKeepAliveTime; //!< tcp keep-alive time; 0=default + + /* for alpn extension; + on the client: these are the protocol preferences + on the server: these are the protocols it supports */ + AlpnProtocolT aAlpnProtocols[SSL_ALPN_MAX_PROTOCOLS]; + uint16_t uNumAlpnProtocols; //!< the number of alpn protocols in the list + uint16_t uAlpnExtensionLength; //!< the size of the list we encode in ClientHello (we calculate when we build the list) +}; + +//! global state +typedef struct ProtoSSLStateT +{ + //! critical section for locking access to state memory + NetCritT StateCrit; + + //! previous session info, used for secure session share/resume + SessionHistoryT SessionHistory[SSL_SESSHIST_MAX]; + + //! validated certificate info + CertValidHistoryT CertValidHistory[SSL_CERTVALID_MAX]; + + // allocation identifiers + int32_t iMemGroup; + void *pMemGroupUserData; + + /* global default settings */ + + int32_t iDfltVers; //!< global version setting + int32_t iDfltMinVers; //!< global min version setting + int32_t iDfltCiph; //!< global cipher setting + int32_t iDfltCurves; //!< global curve setting +}ProtoSSLStateT; + +/*** Function Prototypes **********************************************************/ + +// issue a CA request +static int32_t _ProtoSSLInitiateCARequest(ProtoSSLRefT *pState); + +// update recv handshake hash +static void _ProtoSSLRecvHandshakeFinish(ProtoSSLRefT *pState); + +// set async execution +static int32_t _ProtoSSLUpdateSetAsyncState(ProtoSSLRefT *pState, AsyncOpT *pAsyncOp, int32_t iNextState, int32_t iFailState, int32_t iFailAlert); + +/*** Variables ********************************************************************/ + +static ProtoSSLStateT *_ProtoSSL_pState = NULL; + +//! ServerRandom value that identifies a ServerHello as a HelloRetryRequest +static const uint8_t _SSL3_HelloRetryRequestRandom[] = +{ + 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91, + 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c +}; + +//! ServerRandom trailing eight bytes that identify an illegitimate downgrade from tls1.3 to tls1.2 +static const uint8_t _SSL3_ServerRandomDowngrade12[] = +{ + 0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01 +}; +//! ServerRandom trailing eight bytes that identify an illegitimate downgrade from tls1.3 to tls1.1 or previous +static const uint8_t _SSL3_ServerRandomDowngrade11[] = +{ + 0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00 +}; + +//! supported ssl cipher suites in order of preference; see http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 +static const CipherSuiteT _SSL3_CipherSuite[] = +{ + // TLS1.3 cipher suites + { 0x1301, SSL3_TLS1_3, SSL3_KEY_NULL, SSL3_KEYLEN_128, SSL3_SIGALG_NONE, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 12, SSL3_PRF_SHA256, PROTOSSL_CIPHER_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256" }, // TLS1.3 suite 2: gcm128+sha256 + { 0x1302, SSL3_TLS1_3, SSL3_KEY_NULL, SSL3_KEYLEN_256, SSL3_SIGALG_NONE, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 12, SSL3_PRF_SHA384, PROTOSSL_CIPHER_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384" }, // TLS1.3 suite 1: gcm256+sha384 + { 0x1303, SSL3_TLS1_3, SSL3_KEY_NULL, SSL3_KEYLEN_256, SSL3_SIGALG_NONE, SSL3_ENC_CHACHA, SSL3_MAC_NULL, CRYPTHASH_NULL, 12, SSL3_PRF_SHA256, PROTOSSL_CIPHER_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256" }, // TLS1.3 suite 3: chacha20_poly1305+sha256 + // TLS1.2 AEAD cipher suites + { 0xc02b, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_128, SSL3_SIGALG_ECDSA, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 4, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" }, // ecdhe+ecdsa+gcm+sha256 + { 0xc02c, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_ECDSA, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 4, SSL3_PRF_SHA384, PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" }, // ecdhe+ecdsa+gcm+sha384 + { 0xcca9, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_ECDSA, SSL3_ENC_CHACHA, SSL3_MAC_NULL, CRYPTHASH_NULL, 12, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" }, // ecdhe+ecdsa+chacha+sha256 + { 0xc02f, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_128, SSL3_SIGALG_RSA, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 4, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" }, // ecdhe+rsa+gcm+sha256 + { 0xc030, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_RSA, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 4, SSL3_PRF_SHA384, PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" }, // ecdhe+rsa+gcm+sha384 + { 0xcca8, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_RSA, SSL3_ENC_CHACHA, SSL3_MAC_NULL, CRYPTHASH_NULL, 12, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" }, // ecdhe+rsa+chacha+sha256 + { 0x009c, SSL3_TLS1_2, SSL3_KEY_RSA, SSL3_KEYLEN_128, SSL3_SIGALG_RSA, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 4, SSL3_PRF_SHA256, PROTOSSL_CIPHER_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256" }, // suite 156: rsa+gcm+sha256 + { 0x009d, SSL3_TLS1_2, SSL3_KEY_RSA, SSL3_KEYLEN_256, SSL3_SIGALG_RSA, SSL3_ENC_GCM, SSL3_MAC_NULL, CRYPTHASH_NULL, 4, SSL3_PRF_SHA384, PROTOSSL_CIPHER_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384" }, // suite 157: rsa+gcm+sha384 + // TLS1.2 cipher suites + { 0xc023, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_128, SSL3_SIGALG_ECDSA, SSL3_ENC_AES, SSL3_MAC_SHA256, CRYPTHASH_SHA256, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256" }, // ecdhe+ecdsa+aes+sha256 + { 0xc024, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_ECDSA, SSL3_ENC_AES, SSL3_MAC_SHA384, CRYPTHASH_SHA384, 16, SSL3_PRF_SHA384, PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384" }, // ecdhe+ecdsa+aes+sha384 + { 0xc027, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_128, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA256, CRYPTHASH_SHA256, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" }, // ecdhe+rsa+aes+sha256 + { 0xc028, SSL3_TLS1_2, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA384, CRYPTHASH_SHA384, 16, SSL3_PRF_SHA384, PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" }, // ecdhe+rsa+aes+sha384 + { 0x003c, SSL3_TLS1_2, SSL3_KEY_RSA, SSL3_KEYLEN_128, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA256, CRYPTHASH_SHA256, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256" }, // suite 60: rsa+aes+sha256 + { 0x003d, SSL3_TLS1_2, SSL3_KEY_RSA, SSL3_KEYLEN_256, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA256, CRYPTHASH_SHA256, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256" }, // suite 61: rsa+aes+sha256 + // TLS1.0 cipher suites + { 0xc009, SSL3_TLS1_0, SSL3_KEY_ECDHE, SSL3_KEYLEN_128, SSL3_SIGALG_ECDSA, SSL3_ENC_AES, SSL3_MAC_SHA, CRYPTHASH_SHA1, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA" }, // ecdhe+ecdsa+aes+sha + { 0xc00a, SSL3_TLS1_0, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_ECDSA, SSL3_ENC_AES, SSL3_MAC_SHA, CRYPTHASH_SHA1, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA" }, // ecdhe+ecdsa+aes+sha + { 0xc013, SSL3_TLS1_0, SSL3_KEY_ECDHE, SSL3_KEYLEN_128, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA, CRYPTHASH_SHA1, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" }, // ecdhe+rsa+aes+sha + { 0xc014, SSL3_TLS1_0, SSL3_KEY_ECDHE, SSL3_KEYLEN_256, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA, CRYPTHASH_SHA1, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA" }, // ecdhe+rsa+aes+sha + // SSLv3 cipher suites + { 0x002f, SSL3_SSLv3, SSL3_KEY_RSA, SSL3_KEYLEN_128, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA, CRYPTHASH_SHA1, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA" }, // suite 47: rsa+aes+sha + { 0x0035, SSL3_SSLv3, SSL3_KEY_RSA, SSL3_KEYLEN_256, SSL3_SIGALG_RSA, SSL3_ENC_AES, SSL3_MAC_SHA, CRYPTHASH_SHA1, 16, SSL3_PRF_SHA256, PROTOSSL_CIPHER_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA" }, // suite 53: rsa+aes+sha +}; + +//! supported elliptic curves; see http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 +static const EllipticCurveT _SSL3_EllipticCurves[SSL3_NUM_CURVES] = +{ + // curve 23: P-256 + { CRYPTCURVE_SECP256R1, PROTOSSL_CURVE_SECP256R1, "SECP256R1" }, + // curve 29: X25519 + { CRYPTCURVE_X25519, PROTOSSL_CURVE_X25519, "X25519" }, + // curve 24: P-384 + { CRYPTCURVE_SECP384R1, PROTOSSL_CURVE_SECP384R1, "SECP384R1" }, + // curve 30: X448 + { CRYPTCURVE_X448, PROTOSSL_CURVE_X448, "X448" } +}; + +//! signature schemes; see https://tools.ietf.org/html/rfc8446#section-4.2.3 +static const SignatureSchemeT _SSL3_SignatureSchemes[] = +{ + { SSL3_SIGSCHEME_ECDSA_SHA256, { SSL3_HASHID_SHA256, SSL3_SIGALG_ECDSA }, SSL3_SIGVERIFY_NONE, ASN_OBJ_ECDSA_SECP256R1 }, + { SSL3_SIGSCHEME_RSA_PSS_PSS_SHA256, { SSL3_HASHID_SHA256, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PSS, ASN_OBJ_RSASSA_PSS }, + { SSL3_SIGSCHEME_RSA_PSS_RSAE_SHA256, { SSL3_HASHID_SHA256, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PSS, ASN_OBJ_RSA_PKCS_KEY }, + { SSL3_SIGSCHEME_RSA_PKCS1_SHA256, { SSL3_HASHID_SHA256, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PKCS1, ASN_OBJ_RSA_PKCS_KEY }, + { SSL3_SIGSCHEME_ECDSA_SHA384, { SSL3_HASHID_SHA384, SSL3_SIGALG_ECDSA }, SSL3_SIGVERIFY_NONE, ASN_OBJ_ECDSA_SECP384R1 }, + { SSL3_SIGSCHEME_RSA_PSS_PSS_SHA384, { SSL3_HASHID_SHA384, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PSS, ASN_OBJ_RSASSA_PSS }, + { SSL3_SIGSCHEME_RSA_PSS_RSAE_SHA384, { SSL3_HASHID_SHA384, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PSS, ASN_OBJ_RSA_PKCS_KEY }, + { SSL3_SIGSCHEME_RSA_PKCS1_SHA384, { SSL3_HASHID_SHA384, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PKCS1, ASN_OBJ_RSA_PKCS_KEY }, + { SSL3_SIGSCHEME_RSA_PSS_PSS_SHA512, { SSL3_HASHID_SHA512, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PSS, ASN_OBJ_RSASSA_PSS }, + { SSL3_SIGSCHEME_RSA_PSS_RSAE_SHA512, { SSL3_HASHID_SHA512, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PSS, ASN_OBJ_RSA_PKCS_KEY }, + { SSL3_SIGSCHEME_RSA_PKCS1_SHA512, { SSL3_HASHID_SHA512, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PKCS1, ASN_OBJ_RSA_PKCS_KEY }, + { SSL3_SIGSCHEME_ECDSA_SHA1, { SSL3_HASHID_SHA1, SSL3_SIGALG_ECDSA }, SSL3_SIGVERIFY_NONE, ASN_OBJ_ECDSA_KEY }, + { SSL3_SIGSCHEME_RSA_PKCS1_SHA1, { SSL3_HASHID_SHA1, SSL3_SIGALG_RSA }, SSL3_SIGVERIFY_RSA_PKCS1, ASN_OBJ_RSA_PKCS_KEY } +}; + +//! handshake transition validation map for TLS1.2 and prior clients; see https://tools.ietf.org/html/rfc5246#section-7.3 +static const SSLHandshakeMapT _SSL3_ClientRecvMsgMap[] = +{ + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_SERVER_HELLO }, + { SSL3_MSG_SERVER_HELLO, SSL3_MSG_CERTIFICATE }, + { SSL3_MSG_SERVER_HELLO, SSL3_MSG_FINISHED }, + { SSL3_MSG_CERTIFICATE, SSL3_MSG_SERVER_KEY }, + { SSL3_MSG_CERTIFICATE, SSL3_MSG_CERT_REQ }, + { SSL3_MSG_CERTIFICATE, SSL3_MSG_SERVER_DONE }, + { SSL3_MSG_SERVER_KEY, SSL3_MSG_CERT_REQ }, + { SSL3_MSG_SERVER_KEY, SSL3_MSG_SERVER_DONE }, + { SSL3_MSG_CERT_REQ, SSL3_MSG_SERVER_DONE }, + { SSL3_MSG_SERVER_DONE, SSL3_MSG_FINISHED }, + { -1, -1 } +}; +//! handshake transition validation map for TLS1.2 and prior servers +static const SSLHandshakeMapT _SSL3_ServerRecvMsgMap[] = +{ + { 0, SSL3_MSG_CLIENT_HELLO }, + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_CERTIFICATE }, + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_CLIENT_KEY }, + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_FINISHED }, + { SSL3_MSG_CERTIFICATE, SSL3_MSG_CLIENT_KEY }, + { SSL3_MSG_CLIENT_KEY, SSL3_MSG_CERT_VERIFY }, + { SSL3_MSG_CLIENT_KEY, SSL3_MSG_FINISHED }, + { SSL3_MSG_CERT_VERIFY, SSL3_MSG_FINISHED }, + { -1, -1 } +}; + +//! handshake transition validation map for TLS1.3 clients; see https://tools.ietf.org/html/rfc8446#section-2 +static const SSLHandshakeMapT _SSL3_ClientRecvMsgMap_13[] = +{ + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_SERVER_HELLO }, + { SSL3_MSG_SERVER_HELLO, SSL3_MSG_ENCRYPTED_EXTENSIONS }, + { SSL3_MSG_SERVER_HELLO, SSL3_MSG_FINISHED }, + { SSL3_MSG_ENCRYPTED_EXTENSIONS, SSL3_MSG_CERT_REQ }, + { SSL3_MSG_ENCRYPTED_EXTENSIONS, SSL3_MSG_CERTIFICATE }, + { SSL3_MSG_ENCRYPTED_EXTENSIONS, SSL3_MSG_FINISHED }, + { SSL3_MSG_CERT_REQ, SSL3_MSG_CERTIFICATE }, + { SSL3_MSG_CERTIFICATE, SSL3_MSG_CERT_VERIFY }, + { SSL3_MSG_CERT_VERIFY, SSL3_MSG_FINISHED }, + { SSL3_MSG_FINISHED, SSL3_MSG_NEW_SESSION_TICKET }, + { SSL3_MSG_NEW_SESSION_TICKET, SSL3_MSG_NEW_SESSION_TICKET }, + { -1, -1 } +}; +//! handshake transition validation map for TLS1.3 servers +static const SSLHandshakeMapT _SSL3_ServerRecvMsgMap_13[] = +{ + { 0, SSL3_MSG_CLIENT_HELLO }, + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_CLIENT_HELLO }, + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_CERTIFICATE }, + { SSL3_MSG_CLIENT_HELLO, SSL3_MSG_FINISHED }, + { SSL3_MSG_CERTIFICATE, SSL3_MSG_CERT_VERIFY }, + { SSL3_MSG_CERTIFICATE, SSL3_MSG_FINISHED }, + { SSL3_MSG_CERT_VERIFY, SSL3_MSG_FINISHED }, + { -1, -1 } +}; + +//! .pem certificate signature identifiers; see https://tools.ietf.org/html/rfc7468 +static const CertificateSignatureT _SSL3_CertSignatures[] = +{ + { "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----", ASN_OBJ_RSA_PKCS_KEY }, // pkcs#1 certificate + { "-----BEGIN X509 CERTIFICATE-----", "-----END X509 CERTIFICATE-----", ASN_OBJ_RSA_PKCS_KEY }, // pkcs#1 certificate (alternate/obsolete form) + { "-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----", ASN_OBJ_RSA_PKCS_KEY }, // pkcs#1 private key + { "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----", ASN_OBJ_RSASSA_PSS }, // pkcs#8 private key (rsa-pss) + { "-----BEGIN EC PRIVATE KEY-----", "-----END EC PRIVATE KEY-----", ASN_OBJ_ECDSA_KEY } // pkcs#8 private key (ecdsa) +}; + +//! translation table for SSL3_HASHID_* to CryptHashE type +static const CryptHashTypeE _SSL3_HashIdToCrypt[SSL3_HASHID_MAX+1] = +{ + CRYPTHASH_NULL, // SSL3_HASHID_NONE + CRYPTHASH_MD5, // SSL3_HASHID_MD5 + CRYPTHASH_SHA1, // SSL3_HASHID_SHA1 + CRYPTHASH_SHA224, // SSL3_HASHID_SHA224 + CRYPTHASH_SHA256, // SSL3_HASHID_SHA256 + CRYPTHASH_SHA384, // SSL3_HASHID_SHA384 + CRYPTHASH_SHA512 // SSL3_hashid_sha512 +}; + +//! translation table for SSL3_HASHID_* to ASN hash object identifier; ASN_OBJ_NONE means unsupported +static const uint32_t _SSL3_CrypttoASN[CRYPTHASH_NUMHASHES] = +{ + ASN_OBJ_NONE, // CRYPTHASH_NULL + ASN_OBJ_NONE, // CRYPTHASH_MURMUR3 + ASN_OBJ_MD5, // CRYPTHASH_MD5 + ASN_OBJ_SHA1, // CRYPTHASH_SHA1 + ASN_OBJ_NONE, // CRYPTHASH_SHA224 + ASN_OBJ_SHA256, // CRYPTHASH_SHA256 + ASN_OBJ_SHA384, // CRYPTHASH_SHA384 + ASN_OBJ_SHA512 // CRYPTHASH_SHA512 +}; + +//! ssl version name table +static const char *_SSL3_strVersionNames[] = +{ + "SSLv3", // defined here to retain easy version number to name translation, do not remove + "TLSv1", + "TLSv1.1", + "TLSv1.2", + "TLSv1.3" +}; + +// signature types - subtract type from ASN_OBJ_RSA_PKCS_MD5 to get offset for this table +static const char *_SSL3_strSignatureTypes[] = +{ + "md5WithRSAEncryption", + "sha1WithRSAEncryption", + "sha256WithRSAEncryption", + "sha384WithRSAEncryption", + "sha512WithRSAEncryption", + "ecdsa-with-SHA256", + "ecdsa-with-SHA384", + "ecdsa-with-SHA512" +}; + +#if DIRTYCODE_LOGGING +#if DEBUG_ALL_OBJS +static const char *_SSL3_strAsnTypes[] = +{ + "ASN_00", + "ASN_TYPE_BOOLEAN", // 0x01 + "ASN_TYPE_INTEGER", // 0x02 + "ASN_TYPE_BITSTRING", // 0x03 + "ASN_TYPE_OCTSTRING", // 0x04 + "ASN_TYPE_NULL", // 0x05 + "ASN_TYPE_OBJECT", // 0x06 + "ASN_07", "ASN_08", "ASN_09", + "ASN_0A", "ASN_0B", + "ASN_TYPE_UTF8STR", // 0x0c + "ASN_0D", "ASN_0E", "ASN_0F", + "ASN_TYPE_SEQN", // 0x10 + "ASN_TYPE_SET", // 0x11 + "ASN_12", // 0x12 + "ASN_TYPE_PRINTSTR", // 0x13 + "ASN_TYPE_T61", // 0x14 (Teletex string) + "ASN_15", + "ASN_TYPE_IA5", // 0x16 (IA5 string) + "ASN_TYPE_UTCTIME", // 0x17 + "ASN_TYPE_GENERALIZEDTIME", // 0x18 + "ASN_19", "ASN_1A", "ASN_1B", + "ASN_1C", "ASN_1D", + "ASN_TYPE_UNICODESTR", // 0x1e + "ASN_1F", +}; +static const char *_SSL3_strAsnObjs[ASN_OBJ_COUNT] = +{ + "ASN_OBJ_NONE", + "ASN_OBJ_COUNTRY", + "ASN_OBJ_STATE", + "ASN_OBJ_CITY", + "ASN_OBJ_ORGANIZATION", + "ASN_OBJ_UNIT", + "ASN_OBJ_COMMON", + "ASN_OBJ_SUBJECT_ALT", + "ASN_OBJ_BASIC_CONSTRAINTS", + "ASN_OBJ_MD5", + "ASN_OBJ_SHA1", + "ASN_OBJ_SHA256", + "ASN_OBJ_SHA384", + "ASN_OBJ_SHA512", + "ASN_OBJ_RSA_PKCS_KEY", + "ASN_OBJ_ECDSA_KEY", + "ASN_OBJ_RSA_PKCS_MD5", + "ASN_OBJ_RSA_PKCS_SHA1", + "ASN_OBJ_RSA_PKCS_SHA256", + "ASN_OBJ_RSA_PKCS_SHA384", + "ASN_OBJ_RSA_PKCS_SHA512", + "ASN_OBJ_RSASSA_PSS", + "ASN_OBJ_ECDSA_SHA256", + "ASN_OBJ_ECDSA_SHA384", + "ASN_OBJ_ECDSA_SHA512", + "ASN_OBJ_ECDSA_SECP256R1", + "ASN_OBJ_ECDSA_SECP384R1", + "ASN_OBJ_ECDSA_SECP512R1", + "ASN_OBJ_PKCS1_MGF1" +}; +#endif // DEBUG_ALL_OBJS + +// tls extension names +static const char *_SSL3_strExtensionNames[] = +{ + "server_name", + "max_fragment_length", + "client_certificate_url", + "trusted_ca_keys", + "truncated_hmac", + "status_request", + "user_mapping", + "client_authz", + "server_authz", + "cert_type", + "supported_groups", // (renamed from "elliptic_curves") + "ec_point_formats", + "srp", + "signature_algorithms", + "use_srtp", + "heartbeat", + "application_layer_protocol_negotiation", + "status_request_v2", + "signed_certificate_timestamp", + "client_certificate_type", + "server_certificate_type", + "padding", + "encrypt_then_mac", + "extended_master_secret", + "token_binding", + "cached_info", + "unassigned", "unassigned", "unassigned", "unassigned", "unassigned", "unassigned", "unassigned", "unassigned", "unassigned", + "SessionTicket TLS", + "unassigned", "unassigned", "unassigned", "unassigned", "unassigned", + "pre_shared_key", + "early_data", + "supported_versions", + "cookie", + "psk_key_exchange_modes", + "unassigned", + "certificate_authorities", + "oid_filter", + "post_handshake_auth", + "signature_algorithms_cert", + "key_share", +}; + +#endif // DIRTYCODE_LOGGING + +//! alert description table; see https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml +static const ProtoSSLAlertDescT _ProtoSSL_AlertList[] = +{ + { SSL3_ALERT_DESC_CLOSE_NOTIFY, "close notify" }, // 0 + { SSL3_ALERT_DESC_UNEXPECTED_MESSAGE, "unexpected message" }, // 10 + { SSL3_ALERT_DESC_BAD_RECORD_MAC, "bad record mac" }, // 20 + { SSL3_ALERT_DESC_DECRYPTION_FAILED, "decryption failed" }, // 21 - reserved + { SSL3_ALERT_DESC_RECORD_OVERFLOW, "record overflow" }, // 22 + { SSL3_ALERT_DESC_DECOMPRESSION_FAILURE, "decompression failure" }, // 30 - reserved + { SSL3_ALERT_DESC_HANDSHAKE_FAILURE, "handshake failure" }, // 40 + { SSL3_ALERT_DESC_NO_CERTIFICATE, "no certificate" }, // 41 - reserved + { SSL3_ALERT_DESC_BAD_CERTFICIATE, "bad certificate" }, // 42 + { SSL3_ALERT_DESC_UNSUPPORTED_CERTIFICATE, "unsupported certificate" }, // 43 + { SSL3_ALERT_DESC_CERTIFICATE_REVOKED, "certificate revoked" }, // 44 + { SSL3_ALERT_DESC_CERTIFICATE_EXPIRED, "certificate expired" }, // 45 + { SSL3_ALERT_DESC_CERTIFICATE_UNKNOWN, "certificate unknown" }, // 46 + { SSL3_ALERT_DESC_ILLEGAL_PARAMETER, "illegal parameter" }, // 47 + // the following alert types are all TLS only + { SSL3_ALERT_DESC_UNKNOWN_CA, "unknown ca" }, // 48 + { SSL3_ALERT_DESC_ACCESS_DENIED, "access denied" }, // 49 + { SSL3_ALERT_DESC_DECODE_ERROR, "decode error" }, // 50 + { SSL3_ALERT_DESC_DECRYPT_ERROR, "decrypt error" }, // 51 + { SSL3_ALERT_DESC_EXPORT_RESTRICTION, "export restriction" }, // 60 - reserved + { SSL3_ALERT_DESC_PROTOCOL_VERSION, "protocol version" }, // 70 + { SSL3_ALERT_DESC_INSUFFICIENT_SECURITY, "insufficient security" }, // 71 + { SSL3_ALERT_DESC_INTERNAL_ERROR, "internal error" }, // 80 + { SSL3_ALERT_DESC_INAPPROPRIATE_FALLBACK, "inappropriate fallback" }, // 86 + { SSL3_ALERT_DESC_USER_CANCELLED, "user cancelled" }, // 90 + { SSL3_ALERT_DESC_NO_RENEGOTIATION, "no renegotiation" }, // 100 - reserved + { SSL3_ALERT_DESC_MISSING_EXTENSION, "missing extension" }, // 109 + { SSL3_ALERT_DESC_UNSUPPORTED_EXTENSION, "unsupported extension" }, // 110 + // alert extensions; see http://tools.ietf.org/html/rfc6066#section-9 + { SSL3_ALERT_DESC_CERTIFICATE_UNOBTAINABLE, "certificate unobtainable" }, // 111 + { SSL3_ALERT_DESC_UNRECOGNIZED_NAME, "unrecognized name" }, // 112 + { SSL3_ALERT_DESC_BAD_CERTIFICATE_STATUS, "bad certificate status" }, // 113 + { SSL3_ALERT_DESC_BAD_CERTIFICATE_HASH, "bad certificate hash" }, // 114 + // alert extension; see http://tools.ietf.org/html/rfc4279#section-6 + { SSL3_ALERT_DESC_UNKNOWN_PSK_IDENTITY, "unknown psk identify" }, // 115 + // alert extension; see https://tools.ietf.org/html/rfc8446#section-4.4.2.4 + { SSL3_ALERT_DESC_CERTIFICATE_REQUIRED, "certificate required" }, // 116 + // alert extension; see http://tools.ietf.org/html/rfc7301#section-3.2 + { SSL3_ALERT_DESC_NO_APPLICATION_PROTOCOL, "no application protocol" }, // 120 + { -1, NULL }, // list terminator +}; + +// ASN object identification table +static const struct ASNObjectT _SSL_ObjectList[] = +{ + { ASN_OBJ_COUNTRY, 3, { 0x55, 0x04, 0x06 } }, + { ASN_OBJ_CITY, 3, { 0x55, 0x04, 0x07 } }, + { ASN_OBJ_STATE, 3, { 0x55, 0x04, 0x08 } }, + { ASN_OBJ_ORGANIZATION, 3, { 0x55, 0x04, 0x0a } }, + { ASN_OBJ_UNIT, 3, { 0x55, 0x04, 0x0b } }, + { ASN_OBJ_COMMON, 3, { 0x55, 0x04, 0x03 } }, + { ASN_OBJ_SUBJECT_ALT, 3, { 0x55, 0x1d, 0x11 } }, + { ASN_OBJ_BASIC_CONSTRAINTS, 3, { 0x55, 0x1d, 0x13 } }, + + // OBJ_md5 - OID 1.2.840.113549.2.5 + { ASN_OBJ_MD5, 8, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 } }, + // OBJ_sha1 - OID 1.3.14.3.2.26 + { ASN_OBJ_SHA1, 5, { 0x2b, 0x0e, 0x03, 0x02, 0x1a } }, + // OBJ_sha256 - OID 2.16.840.1.101.3.4.2.1 + { ASN_OBJ_SHA256, 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 } }, + // OBJ_sha384 - OID 2.16.840.1.101.3.4.2.2 + { ASN_OBJ_SHA384, 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 } }, + // OBJ_sha512 - OID 2.16.840.1.101.3.4.2.3 + { ASN_OBJ_SHA512, 9, { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 } }, + + // RSA (PKCS #1 v1.5) key transport algorithm, OID 1.2.840.113349.1.1.1 + { ASN_OBJ_RSA_PKCS_KEY, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 } }, + // RSA (PKCS #1 v1.5) with MD5 signature, OID 1.2.840.113549.1.1.4 + { ASN_OBJ_RSA_PKCS_MD5, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 } }, + // RSA (PKCS #1 v1.5) with SHA-1 signature; sha1withRSAEncryption OID 1.2.840.113549.1.1.5 + { ASN_OBJ_RSA_PKCS_SHA1, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 } }, + /* the following are obsolete alternate definitions of sha1withRSAEncryption; we define them + here for compatibility, because some certificates are still generated with these ids + (by makecert.exe, included with WindowsSDK, for example) */ + // RSA (PKCS #1 v1.5) with SHA-1 signature; sha-1WithRSAEncryption (obsolete) OID 1.3.14.3.2.29 + { ASN_OBJ_RSA_PKCS_SHA1, 5, { 0x2b, 0x0e, 0x03, 0x02, 0x1d } }, + // RSA (PKCS #1 v1.5) with SHA-1 signature; rsaSignatureWithsha1 (obsolete) OID 1.3.36.3.3.1.2 + { ASN_OBJ_RSA_PKCS_SHA1, 5, { 0x2b, 0x24, 0x03, 0x03, 0x01, 0x02 } }, + + // PKCS-MGF1 1.2.840.113549.1.1.8 + { ASN_OBJ_PKCS1_MGF1, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08 } }, + // RSASSA-PSS PKCS#1 1.2.840.113549.1.1.10 + { ASN_OBJ_RSASSA_PSS, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a } }, + + /* sha2+rsa combinations */ + // RSA (PKCS #1 v1.5) with SHA-256 signature; sha256withRSAEncryption OID 1.2.840.113549.1.1.11 + { ASN_OBJ_RSA_PKCS_SHA256, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b } }, + // RSA (PKCS #1 v1.5) with SHA-384 signature; sha384withRSAEncryption OID 1.2.840.113549.1.1.12 + { ASN_OBJ_RSA_PKCS_SHA384, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c } }, + // RSA (PKCS #1 v1.5) with SHA-512 signature; sha512withRSAEncryption OID 1.2.840.113549.1.1.13 + { ASN_OBJ_RSA_PKCS_SHA512, 9, { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d } }, + + // ecdsa key PKCS#8 1.2.840.10045.2.1 + { ASN_OBJ_ECDSA_KEY, 7, { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 } }, + // prime256v1/secp256r1 1.2.840.10045.3.1.7 + { ASN_OBJ_ECDSA_SECP256R1, 8, {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 } }, + // ecdsa-with-SHA256 1.2.840.10045.4.3.2 + { ASN_OBJ_ECDSA_SHA256, 8, {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 } }, + // ecdsa-with-SHA384 1.2.840.10045.4.3.3 + { ASN_OBJ_ECDSA_SHA384, 8, {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 } }, + // ecdsa-with-SHA512 1.2.840.10045.4.3.4 + { ASN_OBJ_ECDSA_SHA512, 8, {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 } }, + + // prime384v1/secp384r1 1.3.132.0.34 + { ASN_OBJ_ECDSA_SECP384R1, 5, {0x2b, 0x81, 0x04, 0x00, 0x22 } }, + // prime521v1/secp521r1 1.3.132.0.35 + { ASN_OBJ_ECDSA_SECP521R1, 5, {0x2b, 0x81, 0x04, 0x00, 0x23 } }, + + // array terminator + { ASN_OBJ_NONE, 0, { 0 } } +}; + +// The 2048-bit public key modulus for 2015 GOS CA Cert signed with sha256 and an exponent of 65537 +static const uint8_t _ProtoSSL_GOS2015ServerModulus2048[] = +{ + 0xcc, 0x6d, 0x54, 0xb6, 0xf4, 0xe4, 0x84, 0xe7, 0x20, 0x76, 0x02, 0xd7, 0x97, 0x48, 0x75, 0x7e, + 0x7e, 0xfb, 0x43, 0x9c, 0x3d, 0xa1, 0x96, 0x47, 0xc1, 0x5d, 0x07, 0x8b, 0x30, 0x73, 0xbf, 0x9d, + 0xfe, 0x75, 0x94, 0x55, 0x21, 0xd0, 0x88, 0x74, 0x66, 0x4c, 0xa2, 0xb7, 0xfe, 0x9f, 0xc0, 0x3b, + 0xf0, 0x60, 0xa0, 0xdb, 0x08, 0x33, 0x2b, 0x6e, 0xf8, 0x02, 0x05, 0xb9, 0x87, 0x9d, 0xac, 0x65, + 0xd5, 0x06, 0x9d, 0x05, 0xe8, 0xd1, 0xb6, 0xf5, 0xde, 0x7d, 0xa5, 0xa4, 0x7d, 0x8a, 0xcb, 0x99, + 0x31, 0xb6, 0x85, 0x9b, 0xa2, 0xce, 0x39, 0xe2, 0x8c, 0x65, 0xaa, 0x07, 0xfc, 0x15, 0x33, 0x07, + 0x00, 0xd1, 0x72, 0x15, 0x13, 0x0d, 0x87, 0x0f, 0x5c, 0xa2, 0x5e, 0xd0, 0xd5, 0xbf, 0xd9, 0x03, + 0x32, 0x62, 0xaf, 0xf5, 0xef, 0x53, 0x36, 0xa8, 0x34, 0xda, 0xb6, 0xa3, 0xec, 0x5c, 0x6a, 0xc0, + 0x67, 0xf8, 0xbe, 0x37, 0x9f, 0xb3, 0xc8, 0x2d, 0xf0, 0x36, 0x4a, 0x6f, 0x6b, 0x06, 0xee, 0xb7, + 0x85, 0xf2, 0x7f, 0x73, 0x6c, 0x01, 0x84, 0x83, 0xe4, 0xda, 0x46, 0xd0, 0x23, 0x9a, 0x6d, 0xf1, + 0x77, 0x7c, 0x05, 0x81, 0x90, 0x4f, 0x6a, 0x44, 0x83, 0x78, 0x3b, 0x71, 0xad, 0x12, 0xc0, 0x48, + 0xc8, 0x73, 0x89, 0xf1, 0x98, 0x78, 0x7b, 0xb4, 0x08, 0x4a, 0xba, 0xe8, 0x59, 0x57, 0xe2, 0xfc, + 0x29, 0xac, 0xbf, 0xf5, 0xa2, 0x9d, 0x4f, 0x2c, 0x64, 0xdc, 0xd7, 0x92, 0x19, 0x1c, 0xc5, 0xfa, + 0xdb, 0x92, 0xc0, 0x90, 0x4b, 0xa8, 0xe9, 0xf2, 0x0d, 0x94, 0x1a, 0xb2, 0x5f, 0xdd, 0x33, 0xae, + 0xff, 0x66, 0x90, 0x97, 0xb2, 0xa8, 0xa5, 0x1b, 0xfa, 0x6f, 0x41, 0xb2, 0x84, 0xba, 0x52, 0x34, + 0x97, 0x4a, 0xd3, 0xc7, 0xb2, 0x3f, 0xdd, 0xdb, 0xc9, 0xb1, 0x13, 0x82, 0x77, 0xe8, 0x6a, 0xcd +}; + +// the 256 bit public key curve data for 2019 GS CA cert signed with sha384 +static const uint8_t _ProtoSSL_GS2019ServerKey[] = +{ + 0x04, 0x13, 0x3f, 0x21, 0x93, 0x86, 0xf7, 0x65, 0xc4, 0x7f, 0x8c, 0x1c, 0xef, 0x49, 0xa8, 0x2a, + 0x32, 0xe3, 0x6c, 0xd4, 0xd0, 0x12, 0x9a, 0x1e, 0x18, 0x10, 0xce, 0xd3, 0xb0, 0xd8, 0x1e, 0xff, + 0xcc, 0x5b, 0x73, 0x5b, 0xc7, 0x5b, 0xeb, 0x0b, 0xf4, 0x06, 0x04, 0x0e, 0x1c, 0x27, 0xd6, 0x87, + 0x31, 0xde, 0x68, 0x7d, 0xdb, 0xfa, 0x03, 0x32, 0x89, 0x2a, 0x30, 0x5d, 0xb3, 0xf8, 0xc2, 0xeb, + 0xa4 +}; + +// The 2048-bit modulus for the VeriSign 2006 CA Cert signed with sha1 and an exponent of 65537 +static const uint8_t _ProtoSSL_VeriSign2006ServerModulus[] = +{ + 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35, 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, + 0x7c, 0xbc, 0x3c, 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3, 0x64, + 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22, 0xe8, 0x2a, 0xaa, 0xa6, 0x42, + 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1, 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, + 0xef, 0x43, 0xdb, 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0, 0xc3, + 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85, 0x26, 0xe5, 0x2b, 0x8f, 0x1b, + 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33, 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, + 0xe8, 0x70, 0x51, 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74, 0xdb, + 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0, 0xf4, 0xa2, 0x25, 0xf2, 0xaf, + 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06, 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, + 0xb5, 0x19, 0xff, 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4, 0xd7, + 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19, 0x1d, 0x1c, 0x40, 0xcb, 0x74, + 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe, 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, + 0x8d, 0x63, 0x47, 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5, 0x95, + 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14, 0x08, 0x7e, 0xe5, 0x3f, 0x9f, + 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f, 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15 +}; + +// The 2048-bit public key modulus for DigiCert Global Root CA +static const uint8_t _ProtoSSL_DigiCertServerModulus[] = +{ + 0xE2, 0x3B, 0xE1, 0x11, 0x72, 0xDE, 0xA8, 0xA4, 0xD3, 0xA3, 0x57, 0xAA, 0x50, 0xA2, 0x8F, 0x0B, + 0x77, 0x90, 0xC9, 0xA2, 0xA5, 0xEE, 0x12, 0xCE, 0x96, 0x5B, 0x01, 0x09, 0x20, 0xCC, 0x01, 0x93, + 0xA7, 0x4E, 0x30, 0xB7, 0x53, 0xF7, 0x43, 0xC4, 0x69, 0x00, 0x57, 0x9D, 0xE2, 0x8D, 0x22, 0xDD, + 0x87, 0x06, 0x40, 0x00, 0x81, 0x09, 0xCE, 0xCE, 0x1B, 0x83, 0xBF, 0xDF, 0xCD, 0x3B, 0x71, 0x46, + 0xE2, 0xD6, 0x66, 0xC7, 0x05, 0xB3, 0x76, 0x27, 0x16, 0x8F, 0x7B, 0x9E, 0x1E, 0x95, 0x7D, 0xEE, + 0xB7, 0x48, 0xA3, 0x08, 0xDA, 0xD6, 0xAF, 0x7A, 0x0C, 0x39, 0x06, 0x65, 0x7F, 0x4A, 0x5D, 0x1F, + 0xBC, 0x17, 0xF8, 0xAB, 0xBE, 0xEE, 0x28, 0xD7, 0x74, 0x7F, 0x7A, 0x78, 0x99, 0x59, 0x85, 0x68, + 0x6E, 0x5C, 0x23, 0x32, 0x4B, 0xBF, 0x4E, 0xC0, 0xE8, 0x5A, 0x6D, 0xE3, 0x70, 0xBF, 0x77, 0x10, + 0xBF, 0xFC, 0x01, 0xF6, 0x85, 0xD9, 0xA8, 0x44, 0x10, 0x58, 0x32, 0xA9, 0x75, 0x18, 0xD5, 0xD1, + 0xA2, 0xBE, 0x47, 0xE2, 0x27, 0x6A, 0xF4, 0x9A, 0x33, 0xF8, 0x49, 0x08, 0x60, 0x8B, 0xD4, 0x5F, + 0xB4, 0x3A, 0x84, 0xBF, 0xA1, 0xAA, 0x4A, 0x4C, 0x7D, 0x3E, 0xCF, 0x4F, 0x5F, 0x6C, 0x76, 0x5E, + 0xA0, 0x4B, 0x37, 0x91, 0x9E, 0xDC, 0x22, 0xE6, 0x6D, 0xCE, 0x14, 0x1A, 0x8E, 0x6A, 0xCB, 0xFE, + 0xCD, 0xB3, 0x14, 0x64, 0x17, 0xC7, 0x5B, 0x29, 0x9E, 0x32, 0xBF, 0xF2, 0xEE, 0xFA, 0xD3, 0x0B, + 0x42, 0xD4, 0xAB, 0xB7, 0x41, 0x32, 0xDA, 0x0C, 0xD4, 0xEF, 0xF8, 0x81, 0xD5, 0xBB, 0x8D, 0x58, + 0x3F, 0xB5, 0x1B, 0xE8, 0x49, 0x28, 0xA2, 0x70, 0xDA, 0x31, 0x04, 0xDD, 0xF7, 0xB2, 0x16, 0xF2, + 0x4C, 0x0A, 0x4E, 0x07, 0xA8, 0xED, 0x4A, 0x3D, 0x5E, 0xB5, 0x7F, 0xA3, 0x90, 0xC3, 0xAF, 0x27 +}; + +// only certificates from these authorities are supported +static ProtoSSLCACertT _ProtoSSL_CACerts[] = +{ + // gos2015 CA + { { "US", "California", "Redwood City", "Electronic Arts, Inc.", + "Global Online Studio/emailAddress=GOSDirtysockSupport@ea.com", + "GOS 2015 Certificate Authority" }, + SSL_CACERTFLAG_GOSCA|SSL_CACERTFLAG_CAPROVIDER, ASN_OBJ_RSA_PKCS_SHA256, 0, + sizeof(_ProtoSSL_GOS2015ServerModulus2048), _ProtoSSL_GOS2015ServerModulus2048, + 3, { 0x01, 0x00, 0x01 }, + 0, NULL, NULL, &_ProtoSSL_CACerts[1] }, + // gos2019 CA + { { "US", "California", "Redwood City", "Electronic Arts, Inc.", + "EADP Gameplay Services", + "Gameplay Services 2019 Certificate Authority" }, + SSL_CACERTFLAG_GOSCA, ASN_OBJ_ECDSA_KEY, ASN_OBJ_ECDSA_SECP256R1, + sizeof(_ProtoSSL_GS2019ServerKey), _ProtoSSL_GS2019ServerKey, + 0, { 0x00 }, + 0, NULL, NULL, &_ProtoSSL_CACerts[2] }, + // verisign 2006 CA + { { "US", "", "", "VeriSign, Inc.", + "VeriSign Trust Network, (c) 2006 VeriSign, Inc. - For authorized use only", + "VeriSign Class 3 Public Primary Certification Authority - G5" }, + SSL_CACERTFLAG_NONE, ASN_OBJ_RSA_PKCS_SHA1, 0, + 256, _ProtoSSL_VeriSign2006ServerModulus, + 3, { 0x01, 0x00, 0x01 }, + 0, NULL, NULL, &_ProtoSSL_CACerts[3]}, + // digicert CA + { { "US", "", "", "DigiCert Inc", "www.digicert.com", "DigiCert Global Root CA" }, + SSL_CACERTFLAG_NONE, ASN_OBJ_RSA_PKCS_SHA256, 0, + 256, _ProtoSSL_DigiCertServerModulus, + 3, { 0x01, 0x00, 0x01 }, + 0, NULL, NULL, NULL }, +}; + + +/*** Private functions ************************************************************/ + +/* + safe (bounded) reading +*/ + +/*F********************************************************************************/ +/*! + \Function _SafeRead8 + + \Description + Read a uint8_t from buffer + + \Input *pData - pointer to buffer to read from + \Input *pDataEnd - pointer to end of buffer + + \Output + uint8_t - value from buffer, or zero if buffer overrun + + \Version 01/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t _SafeRead8(const uint8_t *pData, const uint8_t *pDataEnd) +{ + uint8_t u8 = ((pData+1) <= pDataEnd) ? pData[0] : 0; + return(u8); +} + +/*F********************************************************************************/ +/*! + \Function _SafeRead16 + + \Description + Read a uint16_t from buffer in network order + + \Input *pData - pointer to buffer to read from + \Input *pDataEnd - pointer to end of buffer + + \Output + uint16_t - value from buffer, or zero if buffer overrun + + \Version 01/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static uint16_t _SafeRead16(const uint8_t *pData, const uint8_t *pDataEnd) +{ + uint16_t u16 = ((pData+2) <= pDataEnd) ? ((uint16_t)pData[0]<<8)|((uint16_t)pData[1]) : 0; + return(u16); +} + +/*F********************************************************************************/ +/*! + \Function _SafeRead24 + + \Description + Read a 24bit value from buffer in network order + + \Input *pData - pointer to buffer to read from + \Input *pDataEnd - pointer to end of buffer + + \Output + uint32_t - value from buffer, or zero if buffer overrun + + \Version 01/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _SafeRead24(const uint8_t *pData, const uint8_t *pDataEnd) +{ + uint32_t u32 = ((pData+3) <= pDataEnd) ? ((uint32_t)pData[0]<<16)|((uint32_t)pData[1]<<8)|((uint32_t)pData[2]) : 0; + return(u32); +} + +/*F********************************************************************************/ +/*! + \Function _SafeRead32 + + \Description + Read a uint32_t from buffer in network order + + \Input *pData - pointer to buffer to read from + \Input *pDataEnd - pointer to end of buffer + + \Output + uint32_t - value from buffer, or zero if buffer overrun + + \Version 01/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _SafeRead32(const uint8_t *pData, const uint8_t *pDataEnd) +{ + uint32_t u32 = ((pData+4) <= pDataEnd) ? ((uint32_t)pData[0]<<24)|((uint32_t)pData[1]<<16)|((uint32_t)pData[2]<<8)|((uint32_t)pData[3]) : 0; + return(u32); +} + +/*F********************************************************************************/ +/*! + \Function _SafeReadBytes + + \Description + Read specified bytes from buffer + + \Input *pBuffer - [out] buffer to write to + \Input uBufSize - size of output buffe + \Input *pData - pointer to buffer to read from + \Input uNumBytes - number of bytes to read + \Input *pDataEnd - pointer to end of buffer + + \Version 01/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SafeReadBytes(uint8_t *pBuffer, uint32_t uBufSize, const uint8_t *pData, uint32_t uNumBytes, const uint8_t *pDataEnd) +{ + if ((pData+uNumBytes) <= pDataEnd) + { + ds_memcpy_s(pBuffer, uBufSize, pData, uNumBytes); + } + else + { + ds_memclr(pBuffer, uBufSize); + } +} + +/*F********************************************************************************/ +/*! + \Function _SafeReadString + + \Description + Read length-delimited string from buffer + + \Input *pBuffer - [out] string buffer to write to + \Input uBufSize - size of output buffe + \Input *pString - pointer to source to read from + \Input uStrLen - length of source string + \Input *pDataEnd - pointer to end of buffer + + \Version 01/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SafeReadString(char *pBuffer, uint32_t uBufSize, const char *pString, uint32_t uStrLen, const uint8_t *pDataEnd) +{ + if ((pString+uStrLen) <= (const char *)pDataEnd) + { + ds_strsubzcpy(pBuffer, uBufSize, pString, uStrLen); + } + else + { + ds_memclr(pBuffer, uBufSize); + } +} + +/* + asn.1 parsing routines +*/ + +/*F********************************************************************************/ +/*! + \Function _AsnGetHashType + + \Description + Get CryptHashE from ASN.1 hash object type + + \Input iHashType - asn.1 hash type + + \Output + CryptHashTypeE - hash type + + \Version 11/28/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static CryptHashTypeE _AsnGetHashType(int32_t iHashType) +{ + CryptHashTypeE eHashType; + // convert from asn object type to crypt hash type + switch (iHashType) + { + case ASN_OBJ_MD5: + eHashType = CRYPTHASH_MD5; + break; + case ASN_OBJ_SHA1: + eHashType = CRYPTHASH_SHA1; + break; + case ASN_OBJ_SHA256: + eHashType = CRYPTHASH_SHA256; + break; + case ASN_OBJ_SHA384: + eHashType = CRYPTHASH_SHA384; + break; + case ASN_OBJ_SHA512: + eHashType = CRYPTHASH_SHA512; + break; + default: + eHashType = CRYPTHASH_NULL; + break; + } + // return hash type to caller + return(eHashType); +} + +/*F********************************************************************************/ +/*! + \Function _AsnGetObject + + \Description + Return OID based on type + + \Input iType - Type of OID (ASN_OBJ_*) + + \Output + ASNObjectT *- pointer to OID, or NULL if not found + + \Version 03/18/2015 (jbrookes) +*/ +/********************************************************************************F*/ +static const ASNObjectT *_AsnGetObject(int32_t iType) +{ + const ASNObjectT *pObject; + int32_t iIndex; + + // locate the matching type + for (iIndex = 0, pObject = NULL; _SSL_ObjectList[iIndex].iType != ASN_OBJ_NONE; iIndex += 1) + { + if (_SSL_ObjectList[iIndex].iType == iType) + { + pObject = &_SSL_ObjectList[iIndex]; + break; + } + } + + return(pObject); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseHeader + + \Description + Parse an asn.1 header + + \Input *pData - pointer to header data to parse + \Input *pLast - pointer to end of header + \Input *pType - pointer to storage for header type + \Input *pSize - pointer to storage for data size + + \Output uint8_t * - pointer to next block, or NULL if end of stream + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseHeader(const uint8_t *pData, const uint8_t *pLast, int32_t *pType, int32_t *pSize) +{ + int32_t iCnt; + uint32_t uLen; + int32_t iType, iSize; + + // reset the output + if (pSize != NULL) + { + *pSize = 0; + } + if (pType != NULL) + { + *pType = 0; + } + + /* handle end of data (including early termination due to data truncation or + invalid data) and make sure we have at least enough data for the type and + size */ + if ((pData == NULL) || (pData+2 > pLast)) + { + return(NULL); + } + + // get the type + iType = *pData++; + if (pType != NULL) + { + *pType = iType; + } + + // figure the length + if ((uLen = *pData++) > 127) + { + // get number of bytes + iCnt = (uLen & 127); + // validate length (do not allow overflow) and make sure there is enough data + if ((iCnt > (int32_t)sizeof(uLen)) || ((pData + iCnt) > pLast)) + { + return(NULL); + } + // calc the length + for (uLen = 0; iCnt > 0; --iCnt) + { + uLen = (uLen << 8) | *pData++; + } + } + iSize = (signed)uLen; + // validate record size + if ((iSize < 0) || ((pData+iSize) > pLast)) + { + return(NULL); + } + // save the size + if (pSize != NULL) + { + *pSize = iSize; + } + + #if DEBUG_ALL_OBJS + NetPrintf(("protossl: _AsnParseHeader type=%s (0x%02x) size=%d\n", + _SSL3_strAsnTypes[iType&~(ASN_CONSTRUCT|ASN_IMPLICIT_TAG|ASN_EXPLICIT_TAG)], + iType, iSize)); + #if DEBUG_RAW_DATA + NetPrintMem(pData, iSize, _SSL3_strAsnTypes[iType&~(ASN_CONSTRUCT|ASN_IMPLICIT_TAG|ASN_EXPLICIT_TAG)]); + #endif + #endif + + // return pointer to next + return(pData); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseHeaderType + + \Description + Parse an asn.1 header of specified type + + \Input *pData - pointer to header data to parse + \Input *pLast - pointer to end of header + \Input iType - type of header to extract + \Input *pSize - pointer to storage for data size + + \Output uint8_t * - pointer to next block, or NULL if end of stream or unexpected type + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseHeaderType(const uint8_t *pData, const uint8_t *pLast, int32_t iType, int32_t *pSize) +{ + int32_t iTypeParsed; + pData = _AsnParseHeader(pData, pLast, &iTypeParsed, pSize); + if (iTypeParsed != iType) + { + return(NULL); + } + return(pData); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseObject + + \Description + Parse an object type + + \Input *pData - pointer to object + \Input iSize - size of object + + \Output + int32_t - type of object; zero if object is unrecognized + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _AsnParseObject(const uint8_t *pData, int32_t iSize) +{ + int32_t iType = 0; + int32_t iIndex; + + // locate the matching type + for (iIndex = 0; _SSL_ObjectList[iIndex].iType != ASN_OBJ_NONE; ++iIndex) + { + if ((iSize >= _SSL_ObjectList[iIndex].iSize) && (memcmp(pData, _SSL_ObjectList[iIndex].strData, _SSL_ObjectList[iIndex].iSize) == 0)) + { + // save the type and return it + iType = _SSL_ObjectList[iIndex].iType; + + #if DEBUG_ALL_OBJS + NetPrintf(("protossl: _AsnParseObject obj=%s (%d)\n", _SSL3_strAsnObjs[iType], iType)); + #if DEBUG_RAW_DATA + NetPrintMem(pData, _SSL_ObjectList[iIndex].iSize, _SSL3_strAsnObjs[iType]); + #endif + #endif + + break; + } + } + + #if DEBUG_ALL_OBJS + if (iType == 0) + { + NetPrintMem(pData, iSize, "unrecognized asn.1 object"); + } + #endif + + return(iType); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseString + + \Description + Extract a string + + \Input *pData - pointer to data to extract string from + \Input iSize - size of source data + \Input *pString - pointer to buffer to copy string into + \Input iLength - size of buffer + \Input iType - ASN string type + + \Notes + The number of characters copied will be whichever is smaller between + iSize and iLength-1. If iLength-1 is greater than iSize (ie, the buffer + is larger than the source string) the string output in pString will be + NULL-terminated. + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static void _AsnParseString(const uint8_t *pData, int32_t iSize, char *pString, int32_t iLength, int32_t iType) +{ + if (iType != ASN_TYPE_UNICODESTR) + { + for (; (iSize > 0) && (iLength > 1); --iSize, --iLength) + { + *pString++ = *pData++; + } + if (iLength > 0) + { + *pString = 0; + } + } + else // we do a straight conversion to ASCII here + { + for (pData += 1; (iSize > 0) && (iLength > 1); iSize -= 2, iLength -= 1) + { + *pString++ = *pData; + pData += 2; + } + if (iLength > 0) + { + *pString = 0; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseStringMulti + + \Description + Extract a string, appending to output instead of overwriting. + + \Input *pData - pointer to data to extract string from + \Input iSize - size of source data + \Input *pString - pointer to buffer to copy string into + \Input iLength - size of buffer + + \Notes + The number of characters copied will be whichever is smaller between + iSize and iLength-1. If iLength-1 is greater than iSize (ie, the buffer + is larger than the source string) the string output in pString will be + NULL-terminated. + + \Version 03/25/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AsnParseStringMulti(const uint8_t *pData, int32_t iSize, char *pString, int32_t iLength) +{ + // find append point + for (; (*pString != '\0') && (iLength > 1); --iLength) + { + pString += 1; + } + // extract + for (; (iSize > 0) && (iLength > 1); --iSize, --iLength) + { + *pString++ = *pData++; + } + // terminate + if (iLength > 0) + { + *pString = '\0'; + } +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseIdent + + \Description + Extract an Ident (subject/issuer) from certificate + + \Input *pData - pointer to data to extract string from + \Input iSize - size of source data + \Input *pIdent - [out] storage for parsed ident fields + + \Output + const uint8_t * - pointer past end of ident + + \Version 10/09/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseIdent(const uint8_t *pData, int32_t iSize, ProtoSSLCertIdentT *pIdent) +{ + int32_t iObjType, iType; + const uint8_t *pDataEnd; + + for (iObjType = 0, pDataEnd = pData+iSize; (pData = _AsnParseHeader(pData, pDataEnd, &iType, &iSize)) != NULL; ) + { + // skip past seqn/set references + if ((iType == ASN_TYPE_SEQN+ASN_CONSTRUCT) || (iType == ASN_TYPE_SET+ASN_CONSTRUCT)) + { + continue; + } + if (iType == ASN_TYPE_OBJECT+ASN_PRIMITIVE) + { + iObjType = _AsnParseObject(pData, iSize); + } + if ((iType == ASN_TYPE_PRINTSTR+ASN_PRIMITIVE) || (iType == ASN_TYPE_UTF8STR+ASN_PRIMITIVE) || (iType == ASN_TYPE_T61+ASN_PRIMITIVE) || (iType == ASN_TYPE_UNICODESTR+ASN_PRIMITIVE)) + { + if (iObjType == ASN_OBJ_COUNTRY) + { + _AsnParseString(pData, iSize, pIdent->strCountry, sizeof(pIdent->strCountry), iType); + } + if (iObjType == ASN_OBJ_STATE) + { + _AsnParseString(pData, iSize, pIdent->strState, sizeof(pIdent->strState), iType); + } + if (iObjType == ASN_OBJ_CITY) + { + _AsnParseString(pData, iSize, pIdent->strCity, sizeof(pIdent->strCity), iType); + } + if (iObjType == ASN_OBJ_ORGANIZATION) + { + _AsnParseString(pData, iSize, pIdent->strOrg, sizeof(pIdent->strOrg), iType); + } + if (iObjType == ASN_OBJ_UNIT) + { + if (pIdent->strUnit[0] != '\0') + { + ds_strnzcat(pIdent->strUnit, ", ", sizeof(pIdent->strUnit)); + } + _AsnParseStringMulti(pData, iSize, pIdent->strUnit, sizeof(pIdent->strUnit)); + } + if (iObjType == ASN_OBJ_COMMON) + { + _AsnParseString(pData, iSize, pIdent->strCommon, sizeof(pIdent->strCommon), iType); + } + iObjType = 0; + } + pData += iSize; + } + + return(pDataEnd); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseDate + + \Description + Parse and extract a date object from ASN.1 certificate + + \Input *pData - pointer to header data + \Input iSize - size of object + \Input *pBuffer - [out] output for date string object + \Input iBufSize - size of output buffer + \Input *pDateTime - [out] storage for converted time, optional + + \Output + const uint8_t * - pointer past object, or NULL if an error occurred + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseDate(const uint8_t *pData, int32_t iSize, char *pBuffer, int32_t iBufSize, uint64_t *pDateTime) +{ + int32_t iType; + if (((pData = _AsnParseHeader(pData, pData+iSize, &iType, &iSize)) == NULL) || ((iType != ASN_TYPE_UTCTIME) && (iType != ASN_TYPE_GENERALIZEDTIME))) + { + return(NULL); + } + _AsnParseString(pData, iSize, pBuffer, iBufSize, iType); + if (pDateTime != NULL) + { + *pDateTime = ds_strtotime2(pBuffer, iType == ASN_TYPE_UTCTIME ? TIMETOSTRING_CONVERSION_ASN1_UTCTIME : TIMETOSTRING_CONVERSION_ASN1_GENTIME); + } + pData += iSize; + return(pData); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseBinaryPtr + + \Description + Parse and extract a binary object from ASN.1 certificate + + \Input *pData - pointer to header data + \Input *pLast - pointer to end of data + \Input iType - type of data we are expecting + \Input **ppObj - [out] storage for pointer to binary object + \Input *pObjSize - [out] storage for size of binary object + \Input *pName - name of object (used for debug output) + + \Output + const uint8_t * - pointer past object, or NULL if an error occurred + + \Version 11/18/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseBinaryPtr(const uint8_t *pData, const uint8_t *pLast, int32_t iType, uint8_t **ppObj, int32_t *pObjSize, const char *pName) +{ + int32_t iSize; + if ((pData = _AsnParseHeaderType(pData, pLast, iType, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseBinary: could not get %s\n", pName)); + return(NULL); + } + // skip leading zero if present + if ((iSize > 0) && (*pData == '\0')) + { + pData += 1; + iSize -= 1; + } + // save object pointer + *ppObj = (uint8_t *)pData; + // save object size + *pObjSize = iSize; + // skip object + return(pData+iSize); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseBinary + + \Description + Parse and extract a binary object from ASN.1 certificate + + \Input *pData - pointer to header data + \Input *pLast - pointer to end of data + \Input iType - type of data we are expecting + \Input *pBuffer - [out] output for binary object (may be null to skip) + \Input iBufSize - size of output buffer + \Input *pOutSize - [out] output for binary object size + \Input *pName - name of object (used for debug output) + + \Output + const uint8_t * - pointer past object, or NULL if an error occurred + + \Version 10/10/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseBinary(const uint8_t *pData, const uint8_t *pLast, int32_t iType, uint8_t *pBuffer, int32_t iBufSize, int32_t *pOutSize, const char *pName) +{ + const uint8_t *pObjData = NULL, *pNext; + // parse object info + pNext = _AsnParseBinaryPtr(pData, pLast, iType, (uint8_t **)&pObjData, pOutSize, pName); + // save data + if ((pObjData != NULL) && (pBuffer != NULL)) + { + // validate size + if (*pOutSize > iBufSize) + { + NetPrintf(("protossl: _AsnParseBinary: %s is too large (size=%d, max=%d)\n", pName, *pOutSize, iBufSize)); + return(NULL); + } + ds_memcpy(pBuffer, pObjData, *pOutSize); + } + // skip object + return(pNext); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseOptional + + \Description + Parse optional object of ASN.1 certificate + + \Input *pData - pointer to header data + \Input iSize - size of header + \Input *pCert - pointer to certificate to fill in from header data + + \Output + int32_t - negative=error, zero=no error + + \Version 10/10/2012 (jbrookes) Extracted from _AsnParseCertificate() +*/ +/********************************************************************************F*/ +static int32_t _AsnParseOptional(const uint8_t *pData, int32_t iSize, X509CertificateT *pCert) +{ + const uint8_t *pLast; + int32_t iCritical, iObjType, iLen, iType; + + for (iCritical = iObjType = 0, pLast = pData+iSize; (pData = _AsnParseHeader(pData, pLast, &iType, &iSize)) != NULL; ) + { + // ignore seqn/set references + if ((iType == ASN_TYPE_SEQN+ASN_CONSTRUCT) || (iType == ASN_TYPE_SET+ASN_CONSTRUCT)) + { + continue; + } + + // parse the object type + if (iType == ASN_TYPE_OBJECT+ASN_PRIMITIVE) + { + iObjType = _AsnParseObject(pData, iSize); + } + + // parse a subject alternative name (SAN) object + if (iObjType == ASN_OBJ_SUBJECT_ALT) + { + if (iType == ASN_TYPE_OCTSTRING+ASN_PRIMITIVE) + { + // save reference to subject alternative blob + pCert->iSubjectAltLen = iSize; + pCert->pSubjectAlt = pData; + } + } + + // parse a basic constraints object + if (iObjType == ASN_OBJ_BASIC_CONSTRAINTS) + { + // obj_basic_constraints: [boolean: critical][integer: pathLenConstraints] + if (iType == ASN_TYPE_OCTSTRING+ASN_PRIMITIVE) + { + // if no critical flag is present, mark it as critical. ex: https://www.wellsfargo.com/ + if (iCritical == 0) + { + iCritical = 1; + } + // do not add pData for oct + continue; + } + + if ((iType == ASN_TYPE_BOOLEAN+ASN_PRIMITIVE) && (iSize == 1)) + { + if (!iCritical) + { + // check if the critical flag is set (the basic constraints MUST be critical) + if ((iCritical = *pData) == 0) + { + return(-1); + } + } + else + { + // this is the flag to indicate whether it's a CA + pCert->iCertIsCA = (*pData != 0) ? TRUE : FALSE; + } + } + + if ((iType == ASN_TYPE_INTEGER+ASN_PRIMITIVE) && (pCert->iCertIsCA != FALSE) && (iSize <= (signed)sizeof(pCert->iMaxHeight))) + { + for (iLen = 0; iLen < iSize; iLen++) + { + pCert->iMaxHeight = (pCert->iMaxHeight << 8) | pData[iLen]; + } + /* As per http://tools.ietf.org/html/rfc2459#section-4.2.1.10: A value of zero indicates that only an end-entity certificate + may follow in the path. Where it appears, the pathLenConstraint field MUST be greater than or equal to zero. Where pathLenConstraint + does not appear, there is no limit to the allowed length of the certification path. In our case (iMaxHeight), a value of zero means + no pathLenConstraint is present, 1 means the pathLenConstraint is 0, 2 means the pathLenConstraint is 1, ... */ + //$$ todo - https://tools.ietf.org/html/rfc5280#section-4.2.1.9 updates this to add the keyCertSign bit in the key usage extension as a requirement + if (pCert->iMaxHeight++ < 0) + { + return(-2); + } + } + } + pData += iSize; + } + if (pCert->pSubjectAlt != NULL) + { + NetPrintfVerbose((DEBUG_VAL_CERT, 0, "protossl: parsed SAN; length=%d\n", pCert->iSubjectAltLen)); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseSequencedObject + + \Description + Parse a sequenced object + + \Input *pData - pointer to object to parse + \Input *pLast - pointer to end of data + \Input *pObjType - [out] storage for parsed object type + + \Output + uint8_t * - pointer past end of object, or NULL + + \Version 11/21/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseSequencedObject(const uint8_t *pData, const uint8_t *pLast, int32_t *pObjType) +{ + int32_t iSize; + if ((pData = _AsnParseHeaderType(pData, pLast, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + return(NULL); + } + if ((pData = _AsnParseHeaderType(pData, pLast, ASN_TYPE_OBJECT+ASN_PRIMITIVE, &iSize)) == NULL) + { + return(NULL); + } + if ((*pObjType = _AsnParseObject(pData, iSize)) < 0) + { + return(NULL); + } + return(pData+iSize); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseSignatureAlgorithms + + \Description + Parse Signature Algorithms object of ASN.1 certificate + + \Input *pData - pointer to header data + \Input *pLast - pointer to end of data + \Input *pCert - pointer to certificate to fill in from sigalg object + + \Output + uint8_t * - pointer past end of object + + \Version 11/21/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_AsnParseSignatureAlgorithms(const uint8_t *pData, const uint8_t *pLast, X509CertificateT *pCert) +{ + int32_t iMgfFunc, iSize; + const uint8_t *pData2; + + // parse signature algorithm identifier + if ((pData = _AsnParseHeaderType(pData, pLast, ASN_TYPE_OBJECT+ASN_PRIMITIVE, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get signature algorithm identifier\n")); + return(NULL); + } + pCert->iSigType = _AsnParseObject(pData, iSize); + pData += iSize; + + // RSA-PKCS? + if ((pCert->iSigType >= ASN_OBJ_RSA_PKCS_MD5) && (pCert->iSigType <= ASN_OBJ_RSA_PKCS_SHA512)) + { + // get sig hash type from sig type + pCert->iSigHash = pCert->iSigType-ASN_OBJ_RSA_PKCS_MD5+ASN_OBJ_MD5; + return(pData); + } + else if (pCert->iSigType == ASN_OBJ_ECDSA_SHA256) + { + pCert->iSigHash = ASN_OBJ_SHA256; + return(pData); + } + else if (pCert->iSigType == ASN_OBJ_ECDSA_SHA384) + { + pCert->iSigHash = ASN_OBJ_SHA384; + return(pData); + } + else if (pCert->iSigType != ASN_OBJ_RSASSA_PSS) + { + NetPrintf(("protossl: unsupported signature algorithm %d\n", pCert->iSigType)); + return(NULL); + } + + // set default parameter types, which are used when only partial settings are specified + pCert->iSigHash = ASN_OBJ_SHA1; + pCert->iSigSalt = 20; + iMgfFunc = ASN_OBJ_PKCS1_MGF1; + pCert->iMgfHash = ASN_OBJ_SHA1; + + // parse rsassa-pss parameter sequence; see https://tools.ietf.org/html/rfc3447#appendix-A.2.3 + if ((pData = _AsnParseHeaderType(pData, pLast, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not parse rsassa_pss parameter sequence\n")); + return(NULL); + } + + // parse optional signature hash object + if ((pData2 = _AsnParseHeaderType(pData, pLast, ASN_EXPLICIT_TAG+ASN_PRIMITIVE+0, &iSize)) != NULL) + { + if ((pData = _AsnParseSequencedObject(pData2, pLast, &pCert->iSigHash)) == NULL) + { + NetPrintf(("protossl: _AsnParseSignatureAlgorithm: error parsing rsassa_pss hash parameter\n")); + return(NULL); + } + } + + // parse optional mgf+hash object + if ((pData2 = _AsnParseHeaderType(pData, pLast, ASN_EXPLICIT_TAG+ASN_PRIMITIVE+1, &iSize)) != NULL) + { + // parse mgf + if (((pData2 = _AsnParseSequencedObject(pData2, pLast, &iMgfFunc)) == NULL) || (iMgfFunc != ASN_OBJ_PKCS1_MGF1)) + { + NetPrintf(("protossl: _AsnParseSignatureAlgorithm: error parsing rsassa_pss mgf parameter")); + return(NULL); + } + // parse mgf hash + if ((pData = _AsnParseSequencedObject(pData2, pLast, &pCert->iMgfHash)) == NULL) + { + NetPrintf(("protossl: _AsnParseSignatureAlgorithm: error parsing rsassa_pss hash parameter\n")); + return(NULL); + } + } + + // look for explicit signature parameters (optional) + if ((pData2 = _AsnParseHeaderType(pData, pLast, ASN_EXPLICIT_TAG+ASN_PRIMITIVE+2, &iSize)) != NULL) + { + int32_t iSigSaltLen = 0; + uint8_t uSigSalt = 0; + + // parse salt length + if (((pData = _AsnParseBinary(pData2, pLast, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &uSigSalt, sizeof(uSigSalt), &iSigSaltLen, "salt length")) == NULL) || (iSigSaltLen != 1)) + { + return(NULL); + } + pCert->iSigSalt = uSigSalt; + } + + #if DEBUG_VAL_CERT + NetPrintf(("protossl: rsassa-pss parameters\n")); + NetPrintf(("protossl: iSigType=%d\n", pCert->iSigType)); + NetPrintf(("protossl: iSigHash=%d\n", pCert->iSigHash)); + NetPrintf(("protossl: iMgfFunc=%d\n", iMgfFunc)); + NetPrintf(("protossl: iMgfHash=%d\n", pCert->iMgfHash)); + NetPrintf(("protossl: iSigSalt=%d\n", pCert->iSigSalt)); + #endif + + // return pointer past end of data + return(pData); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParseCertificate + + \Description + Parse an x.509 certificate in ASN.1 format + + \Input *pCert - pointer to certificate to fill in from header data + \Input *pData - pointer to header data + \Input iSize - size of header + + \Output + int32_t - negative=error, zero=no error + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _AsnParseCertificate(X509CertificateT *pCert, const uint8_t *pData, int32_t iSize) +{ + const uint8_t *pInfData; + const uint8_t *pInfSkip; + const uint8_t *pSigSkip; + const uint8_t *pKeySkip; + const uint8_t *pKeyData; + const uint8_t *pLast = pData+iSize; + const CryptHashT *pHash; + int32_t iKeySize, iType; + + // clear the certificate + ds_memclr(pCert, sizeof(*pCert)); + + // process the base sequence + if ((pData = _AsnParseHeaderType(pData, pLast, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not parse base sequence\n")); + return(-1); + } + + // process the info sequence + if ((pData = _AsnParseHeaderType(pInfData = pData, pLast, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not parse info sequence\n")); + return(-2); + } + pInfSkip = pData+iSize; + + // skip any non-integer tag (optional version) + if (*pData != ASN_TYPE_INTEGER+ASN_PRIMITIVE) + { + if ((pData = _AsnParseHeader(pData, pLast, NULL, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not skip non-integer tag\n")); + return(-3); + } + pData += iSize; + } + + // grab the certificate serial number + if (((pData = _AsnParseHeader(pData, pInfSkip, &iType, &iSize)) == NULL) || ((unsigned)iSize > sizeof(pCert->SerialData))) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get certificate serial number (iSize=%d)\n", iSize)); + return(-4); + } + pCert->iSerialSize = iSize; + ds_memcpy(pCert->SerialData, pData, iSize); + pData += iSize; + + // find signature algorithm + if ((pData = _AsnParseHeaderType(pData, pInfSkip, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get signature algorithm\n")); + return(-5); + } + pSigSkip = pData+iSize; + + // get the signature algorithm type + if ((pData = _AsnParseHeaderType(pData, pInfSkip, ASN_TYPE_OBJECT+ASN_PRIMITIVE, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get signature algorithm type\n")); + return(-6); + } + if ((pCert->iSigType = _AsnParseObject(pData, iSize)) == ASN_OBJ_NONE) + { + NetPrintMem(pData, iSize, "protossl: unsupported signature algorithm"); + return(-7); + } + + // parse issuer + if ((pData = _AsnParseHeaderType(pSigSkip, pInfSkip, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get issuer\n")); + return(-8); + } + pData = _AsnParseIdent(pData, iSize, &pCert->Issuer); + + // parse the validity info + if ((pData = _AsnParseHeaderType(pData, pInfSkip, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get validity info\n")); + return(-9); + } + + // get validity dates + if ((pData = _AsnParseDate(pData, iSize, pCert->strGoodFrom, sizeof(pCert->strGoodFrom), &pCert->uGoodFrom)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get from date\n")); + return(-10); + } + if ((pData = _AsnParseDate(pData, iSize, pCert->strGoodTill, sizeof(pCert->strGoodTill), &pCert->uGoodTill)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get to date\n")); + return(-11); + } + #if DEBUG_VAL_CERT + { + char strTimeFrom[32], strTimeTill[32]; + NetPrintf(("protossl: certificate valid from %s to %s\n", + ds_secstostr(pCert->uGoodFrom, TIMETOSTRING_CONVERSION_RFC_0822, FALSE, strTimeFrom, sizeof(strTimeFrom)), + ds_secstostr(pCert->uGoodTill, TIMETOSTRING_CONVERSION_RFC_0822, FALSE, strTimeTill, sizeof(strTimeTill)))); + } + #endif + + // get subject + if ((pData = _AsnParseHeaderType(pData, pInfSkip, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get subject\n")); + return(-12); + } + pData = _AsnParseIdent(pData, iSize, &pCert->Subject); + + // parse the public key + if ((pData = _AsnParseHeaderType(pData, pInfSkip, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get public key\n")); + return(-13); + } + + // find the key algorithm sequence + if ((pData = _AsnParseHeaderType(pData, pInfSkip, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get key algorithm sequence\n")); + return(-14); + } + pKeySkip = pData+iSize; + + // grab the public key algorithm + if ((pData = _AsnParseHeaderType(pData, pKeySkip, ASN_TYPE_OBJECT+ASN_PRIMITIVE, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get public key algorithm\n")); + return(-15); + } + pCert->iKeyType = _AsnParseObject(pData, iSize); + + // if ecdsa, grab the curve type before we move past the public key object + if (pCert->iKeyType == ASN_OBJ_ECDSA_KEY) + { + if ((pData = _AsnParseHeaderType(pData+iSize, pKeySkip, ASN_TYPE_OBJECT+ASN_PRIMITIVE, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get ecdsa curve type\n")); + return(-15); + } + pCert->iCrvType = _AsnParseObject(pData, iSize); + } + + // find the actual public key + if (((pData = _AsnParseHeaderType(pKeySkip, pLast, ASN_TYPE_BITSTRING+ASN_PRIMITIVE, &iSize)) == NULL) || (iSize < 1)) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get actual public key\n")); + return(-16); + } + + // skip the "extra bits" indicator and save keydata ref + pKeyData = pData+1; + iKeySize = iSize-1; + pData += iSize; + + // parse optional info object, if present; see https://tools.ietf.org/html/rfc5280#section-4.1 + if (((pData = _AsnParseHeaderType(pData, pInfSkip, ASN_EXPLICIT_TAG+3, &iSize)) != NULL) && (_AsnParseOptional(pData, iSize, pCert) < 0)) + { + return(-17); + } + + // parse signature algorithm sequence + if ((pData = _AsnParseHeaderType(pInfSkip, pLast, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get signature algorithm sequence\n")); + return(-18); + } + pSigSkip = pData+iSize; + + // parse signature algorithms + if ((pData = _AsnParseSignatureAlgorithms(pData, pLast, pCert)) == NULL) + { + return(-19); + } + + // parse the signature data + if ((pCert->iSigType == ASN_OBJ_ECDSA_SHA256) || (pCert->iSigType == ASN_OBJ_ECDSA_SHA384) || (pCert->iSigType == ASN_OBJ_ECDSA_SHA512)) + { + // find the bitstring object + if ((pData = _AsnParseHeaderType(pSigSkip, pLast, ASN_TYPE_BITSTRING+ASN_PRIMITIVE, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get signature algorithm identifier\n")); + return(-20); + } + // skip leading pad byte; note we assume it is zero (no padding) + pData += 1; + iSize -= 1; + // save signature object data; we will parse it later + ds_memcpy_s(pCert->SigData, sizeof(pCert->SigData), pData, iSize); + pCert->iSigSize = iSize; + } + else if ((pData = _AsnParseBinary(pSigSkip, pLast, ASN_TYPE_BITSTRING+ASN_PRIMITIVE, pCert->SigData, sizeof(pCert->SigData), &pCert->iSigSize, "signature data")) == NULL) + { + return(-21); + } + + // parse the public key data (extract modulus & exponent) + if ((pCert->iKeyType == ASN_OBJ_RSA_PKCS_KEY) || (pCert->iKeyType == ASN_OBJ_RSASSA_PSS)) + { + pLast = pKeyData+iKeySize; + + // parse the sequence + if ((pData = _AsnParseHeaderType(pKeyData, pLast, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParseCertificate: could not get public key sequence\n")); + return(-22); + } + // parse the key modulus + if ((pData = _AsnParseBinary(pData, pLast, ASN_TYPE_INTEGER+ASN_PRIMITIVE, pCert->KeyModData, sizeof(pCert->KeyModData), &pCert->iKeyModSize, "key modulus")) == NULL) + { + return(-23); + } + #if DEBUG_MOD_PRNT + NetPrintMem(pCert->KeyModData, pCert->iKeyModSize, "public key modulus"); + #endif + // parse the key exponent + if ((pData = _AsnParseBinary(pData, pLast, ASN_TYPE_INTEGER+ASN_PRIMITIVE, pCert->KeyExpData, sizeof(pCert->KeyExpData), &pCert->iKeyExpSize, "key exponent")) == NULL) + { + return(-24); + } + } + else if (pCert->iKeyType == ASN_OBJ_ECDSA_KEY) + { + // copy the curve data; unlike rsa, keydata points to the actual data + ds_memcpy_s(pCert->KeyModData, sizeof(pCert->KeyModData), pKeyData, iKeySize); + pCert->iKeyModSize = iKeySize; + #if DEBUG_MOD_PRNT + NetPrintMem(pCert->KeyModData, pCert->iKeyModSize, "public key curve data"); + #endif + } + else + { + NetPrintf(("protossl: _AsnParseCertificate: unrecognized key type %d\n", pCert->iKeyType)); + return(-25); + } + + // get cert hash type + pCert->eHashType = _AsnGetHashType(pCert->iSigHash); + + // generate the certificate hash + if ((pHash = CryptHashGet(pCert->eHashType)) != NULL) + { + // do the hash + uint8_t aContext[CRYPTHASH_MAXSTATE]; + pHash->Init(aContext, pHash->iHashSize); + pHash->Update(aContext, pInfData, (int32_t)(pInfSkip-pInfData)); + pHash->Final(aContext, pCert->HashData, pHash->iHashSize); + // save hash size + pCert->iHashSize = pHash->iHashSize; + } + else + { + NetPrintf(("protossl: could not get hash type %d\n", pCert->iSigHash)); + return(-26); + } + + // certificate parsed successfully + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParsePrivateKeyRSA + + \Description + Parse public key modulus and public or private key exponent from private + key certificate. + + \Input *pPrivateKeyData - private key certificate data in base64 form + \Input iPrivateKeyLen - length of private key certificate data + \Input *pPrivateKey - [out] parsed private key data + + \Output + int32_t - private key certificate binary (decoded) size, or negative on error + + \Notes + \verbatim + RSAPrivateKey ::= SEQUENCE { + version Version, + modulus INTEGER, + publicExponent INTEGER, + privateExponent INTEGER, + primeP INTEGER, + primeQ INTEGER, + exponentP INTEGER, + exponentQ INTEGER, + coefficient INTEGER + } + \endverbatim + + \Version 03/15/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AsnParsePrivateKeyRSA(const char *pPrivateKeyData, int32_t iPrivateKeyLen, X509PrivateKeyT *pPrivateKey) +{ + int32_t iPrivKeySize, iSize, iSize2, iTemp = 0; + const uint8_t *pData, *pData2; + + // clear output structure + ds_memclr(pPrivateKey, sizeof(*pPrivateKey)); + + // base64 decode into buffer + if ((iPrivKeySize = Base64Decode3(pPrivateKeyData, iPrivateKeyLen, pPrivateKey->strPrivKeyData, sizeof(pPrivateKey->strPrivKeyData))) == 0) + { + NetPrintf(("protossl: _AsnParsePrivateKey: could not decode private key\n")); + return(-2); + } + // parse the sequence + if ((pData = _AsnParseHeaderType((uint8_t *)pPrivateKey->strPrivKeyData, (uint8_t *)pPrivateKey->strPrivKeyData+iPrivKeySize, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParsePrivateKey: could not get private key sequence\n")); + return(-3); + } + // skip the version + if ((pData = _AsnParseBinary(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, NULL, 0, &iTemp, "version")) == NULL) + { + return(-4); + } + // check for sequence... if there is one, this is an RSA-PSS PKCS#8 certificate with some additional stuff we need to skip + if ((pData2 = _AsnParseHeaderType(pData, pData+iSize, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize2)) != NULL) + { + if ((pData = _AsnParseHeaderType(pData2, pData2+iSize2, ASN_TYPE_OBJECT+ASN_PRIMITIVE, &iSize2)) == NULL) + { + return(-5); + } + if (_AsnParseObject(pData, iSize2) != ASN_OBJ_RSASSA_PSS) + { + return(-6); + } + pData += iSize2; + if ((pData = _AsnParseHeaderType(pData, pData+iSize, ASN_TYPE_OCTSTRING+ASN_PRIMITIVE, &iSize)) == NULL) + { + return(-7); + } + if ((pData = _AsnParseHeaderType(pData, pData+iSize, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + return(-8); + } + if ((pData = _AsnParseBinary(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, NULL, 0, &iTemp, "zero")) == NULL) + { + return(-9); + } + } + // parse public key modulus + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->Modulus.pObjData, &pPrivateKey->Modulus.iObjSize, "key modulus")) == NULL) + { + return(-10); + } + // parse public key exponent + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->PublicExponent.pObjData, &pPrivateKey->PublicExponent.iObjSize, "public key exponent")) == NULL) + { + return(-11); + } + // parse private key exponent + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->PrivateExponent.pObjData, &pPrivateKey->PrivateExponent.iObjSize, "private key exponent")) == NULL) + { + return(-12); + } + // parse primeP + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->PrimeP.pObjData, &pPrivateKey->PrimeP.iObjSize, "primeP")) == NULL) + { + return(-13); + } + // parse primeQ + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->PrimeQ.pObjData, &pPrivateKey->PrimeQ.iObjSize, "primeQ")) == NULL) + { + return(-14); + } + // parse exponentP + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->ExponentP.pObjData, &pPrivateKey->ExponentP.iObjSize, "exponentP")) == NULL) + { + return(-15); + } + // parse exponentQ + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->ExponentQ.pObjData, &pPrivateKey->ExponentQ.iObjSize, "exponentQ")) == NULL) + { + return(-16); + } + // parse coefficient + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, &pPrivateKey->Coefficient.pObjData, &pPrivateKey->Coefficient.iObjSize, "coefficient")) == NULL) + { + return(-17); + } + return(iPrivKeySize); +} + +/*F********************************************************************************/ +/*! + \Function _AsnParsePrivateKeyEcdsa + + \Description + Parse public key modulus and public or private key exponent from private + key certificate. + + \Input *pPrivateKeyData - private key certificate data in base64 form + \Input iPrivateKeyLen - length of private key certificate data + \Input *pPrivateKey - [out] parsed private key data + + \Output + int32_t - private key certificate binary (decoded) size, or negative on error + + \Notes + + ref: https://tools.ietf.org/html/rfc5915#section-3 + + ECPrivateKey ::= SEQUENCE { + version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + privateKey OCTET STRING, + parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + publicKey [1] BIT STRING OPTIONAL + } + + \Version 03/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AsnParsePrivateKeyEcdsa(const char *pPrivateKeyData, int32_t iPrivateKeyLen, X509PrivateKeyT *pPrivateKey) +{ + int32_t iPrivKeySize, iSize, iTemp = 0; + const uint8_t *pData; + + // clear output structure + ds_memclr(pPrivateKey, sizeof(*pPrivateKey)); + + // decode private key into private key buffer + if ((iPrivKeySize = Base64Decode3(pPrivateKeyData, iPrivateKeyLen, pPrivateKey->strPrivKeyData, sizeof(pPrivateKey->strPrivKeyData))) == 0) + { + NetPrintf(("protossl: _AsnParsePrivateKey: could not decode private key\n")); + return(-2); + } + // parse the sequence + if ((pData = _AsnParseHeaderType((uint8_t *)pPrivateKey->strPrivKeyData, (uint8_t *)pPrivateKey->strPrivKeyData+iPrivKeySize, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + NetPrintf(("protossl: _AsnParsePrivateKey: could not get private key sequence\n")); + return(-3); + } + // skip the version + if ((pData = _AsnParseBinary(pData, pData+iSize, ASN_TYPE_INTEGER+ASN_PRIMITIVE, NULL, 0, &iTemp, "version")) == NULL) + { + return(-4); + } + // parse the private key; we store it in the Modulus field + if ((pData = _AsnParseBinaryPtr(pData, pData+iSize, ASN_TYPE_OCTSTRING+ASN_PRIMITIVE, &pPrivateKey->Modulus.pObjData, &pPrivateKey->Modulus.iObjSize, "key modulus")) == NULL) + { + return(-5); + } + return(iPrivKeySize); +} + +/* + asn.1 writing +*/ + +/*F********************************************************************************/ +/*! + \Function _AsnWriteBn + + \Description + Writes a BigNumber as an ASN.1 object + + \Input *pBuffer - [out] storage for generated object + \Input iBufSize - size of output buffer + \Input *pBn - BigNumber to write + \Input uAsnType - ASN.1 object type to write + + \Output + uint8_t * - pointer past end of written object + + \Version 03/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t *_AsnWriteBn(uint8_t *pBuffer, int32_t iBufSize, const CryptBnT *pBn, uint8_t uAsnType) +{ + int32_t iByteLen = CryptBnByteLen(pBn); + uint8_t *pSize; + *pBuffer++ = uAsnType; + pSize = pBuffer++; + if ((uAsnType & (ASN_TYPE_BITSTRING|ASN_TYPE_INTEGER)) && (CryptBnBitTest(pBn, (iByteLen*8)-1))) + { + *pBuffer++ = 0; + } + CryptBnFinal(pBn, pBuffer, iByteLen); + pBuffer += iByteLen; + *pSize = pBuffer-pSize-1; + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _AsnWriteSignatureEcdsa + + \Description + Writes an ASN.1 Elliptic Curve signature + + \Input *pSigData - [out] storage for generated ASN.1 encoded signature + \Input iSigSize - size of output buffer + \Input *pSignature - signature to write + + \Output + int32_t - size of written signature + + \Version 03/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AsnWriteSignatureEcdsa(uint8_t *pSigData, int32_t iSigSize, CryptEccPointT *pSignature) +{ + uint8_t *pSigOut = pSigData, *pSigLen; + // write the signature to output buffer + *pSigOut++ = ASN_CONSTRUCT|ASN_TYPE_SEQN; + // save pointer to length + pSigLen = pSigOut++; + // add signature.x + pSigOut = _AsnWriteBn(pSigOut, iSigSize, &pSignature->X, ASN_TYPE_INTEGER|ASN_PRIMITIVE); + // add signature.y + pSigOut = _AsnWriteBn(pSigOut, iSigSize, &pSignature->Y, ASN_TYPE_INTEGER|ASN_PRIMITIVE); + // write construct length (does not include type or length) + *pSigLen = pSigOut-pSigData-2; + // return total size + return(*pSigLen+2); +} + +/*F********************************************************************************/ +/*! + \Function _AsnWriteDigitalHashObject + + \Description + Generate a DigitalHash object + + \Input *pBuffer - [out] storage for generated digitalhash object + \Input iBufSize - size of output buffer + \Input *pHashData - pointer to hash data to embed in the object (may be NULL) + \Input iHashSize - size of hash data + \Input eHashType - type of hash + + \Output + int32_t - size of generated object + + \Version 04/30/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AsnWriteDigitalHashObject(uint8_t *pBuffer, int32_t iBufSize, const uint8_t *pHashData, int32_t iHashSize, CryptHashTypeE eHashType) +{ + const ASNObjectT *pObject; + uint8_t *pBufStart = pBuffer; + + // make sure there's space + if (iBufSize < (CRYPTHASH_MAXDIGEST+64)) + { + NetPrintf(("protossl: insufficient space for digital hash object\n")); + return(0); + } + // get ASN object to encode + if ((pObject = _AsnGetObject(_SSL3_CrypttoASN[eHashType])) == NULL) + { + NetPrintf(("protossl: failed to get digital hash ASN object\n")); + return(0); + } + + /* generate DER-encoded DigitalHash object as per http://tools.ietf.org/html/rfc5246#section-4.7; note that + this code assumes the total object size is 127 bytes or fewer as it uses single-byte length encoding. + format defined by https://tools.ietf.org/html/rfc8017#section-9.2 */ + *pBuffer++ = ASN_CONSTRUCT | ASN_TYPE_SEQN; + *pBuffer++ = (2) + (2 + pObject->iSize) + (2) + (2 + iHashSize); + *pBuffer++ = ASN_CONSTRUCT | ASN_TYPE_SEQN; + *pBuffer++ = (2 + pObject->iSize) + (2); + // add hash object + *pBuffer++ = ASN_TYPE_OBJECT; + *pBuffer++ = pObject->iSize; + // add asn.1 object identifier for hash + ds_memcpy(pBuffer, pObject->strData, pObject->iSize); + pBuffer += pObject->iSize; + // add null object + *pBuffer++ = ASN_TYPE_NULL; + *pBuffer++ = 0x00; + // add hash object (octet string) + *pBuffer++ = ASN_TYPE_OCTSTRING; + *pBuffer++ = iHashSize; + // add hash data + if (pHashData != NULL) + { + ds_memcpy(pBuffer, pHashData, iHashSize); + pBuffer += iHashSize; + } + + // return final object size + return(pBuffer - pBufStart); +} + +/* + certificate functions +*/ + +/*F********************************************************************************/ +/*! + \Function _CertificateFindSignature + + \Description + Find PEM signature in the input data, if it exists, and return a + pointer to the encapsulated data. + + \Input *pCertData - pointer to data to scan + \Input iCertSize - size of input data + \Input *pSigText - signature text to find + + \Output + const uint8_t * - pointer to data, or null if not found + + \Version 01/14/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_CertificateFindSignature(const uint8_t *pCertData, int32_t iCertSize, const char *pSigText) +{ + int32_t iSigLen = (int32_t)strlen(pSigText); + int32_t iCertIdx; + + for (iCertIdx = 0; iCertIdx < iCertSize; iCertIdx += 1) + { + if ((pCertData[iCertIdx] == *pSigText) && ((iCertSize - iCertIdx) >= iSigLen)) + { + if (!strncmp((const char *)pCertData+iCertIdx, pSigText, iSigLen)) + { + return(pCertData+iCertIdx); + } + } + } + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function _CertificateFindData + + \Description + Finds PEM signature in the input data, if it exists, and stores the + offsets to the beginning and end of the embedded signature data. + + \Input *pCertData - pointer to data to scan + \Input iCertSize - size of input data + \Input **pCertBeg - [out] storage for index to beginning of certificate data + \Input **pCertEnd - [out] storage for index to end of certificate data + \Input *pCertType - [out] storage for certificate type (ASN_OBJ_\*) + + \Output + int32_t - 0=did not find certificate, else did find certificate + + \Version 01/14/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CertificateFindData(const uint8_t *pCertData, int32_t iCertSize, const uint8_t **pCertBeg, const uint8_t **pCertEnd, uint32_t *pCertType) +{ + const CertificateSignatureT *pCertSig; + int32_t iCertSig; + + for (iCertSig = 0; iCertSig < (signed)(sizeof(_SSL3_CertSignatures)/sizeof(_SSL3_CertSignatures[0])); iCertSig += 1) + { + pCertSig = &_SSL3_CertSignatures[iCertSig]; + + // make sure "end-cert" occurs after start since we support bundles + if (((*pCertBeg = _CertificateFindSignature(pCertData, iCertSize, pCertSig->pCertBeg)) != NULL) && + ((*pCertEnd = _CertificateFindSignature(*pCertBeg, (int32_t)(pCertData+iCertSize-*pCertBeg), pCertSig->pCertEnd)) != NULL)) + { + // skip begin signature + *pCertBeg += strlen(pCertSig->pCertBeg); + // write cert type + *pCertType = pCertSig->uCertType; + // return size to caller + return((int32_t)(*pCertEnd-*pCertBeg)); + } + } + + // if no signature, assume it's just straight base64 pkcs1 + *pCertBeg = pCertData; + *pCertEnd = pCertData + iCertSize; + *pCertType = ASN_OBJ_RSA_PKCS_KEY; + return(iCertSize); +} + +/*F********************************************************************************/ +/*! + \Function _CertificateDecodePrivate + + \Description + Parse public key modulus and public or private key exponent from private + key certificate. + + \Input *pPrivateKeyData - private key certificate data in base64 form + \Input iPrivateKeyLen - length of private key certificate data + \Input *pPrivateKey - [out] parsed private key data + + \Output + int32_t - private key certificate binary (decoded) size, or negative on error + + \Version 03/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CertificateDecodePrivate(const char *pPrivateKeyData, int32_t iPrivateKeyLen, X509PrivateKeyT *pPrivateKey) +{ + const uint8_t *pCertBeg, *pCertEnd; + int32_t iCertSize, iResult; + uint32_t uCertType; + + // make sure we have key data first + if (pPrivateKeyData == NULL) + { + NetPrintf(("protossl: _AsnParsePrivateKey: no private key set\n")); + return(-1); + } + + // find the base64-encoded certificate data + iCertSize = _CertificateFindData((const uint8_t *)pPrivateKeyData, iPrivateKeyLen, &pCertBeg, &pCertEnd, &uCertType); + + // parse ecdsa or rsa? + if (uCertType == ASN_OBJ_ECDSA_KEY) + { + iResult = _AsnParsePrivateKeyEcdsa((const char *)pCertBeg, iCertSize, pPrivateKey); + } + else + { + iResult = _AsnParsePrivateKeyRSA((const char *)pCertBeg, iCertSize, pPrivateKey); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _CertificateDecodePublic + + \Description + Decode the specified PEM certificate into binary format, parse and extract some information + + \Input *pState - module state reference + \Input *pPemData - certificate to decode + \Input iPemSize - certificate length + + \Output + CertificateDataT * - pointer to new certificate data, or null + + \Version 03/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static CertificateDataT *_CertificateDecodePublic(ProtoSSLRefT *pState, const uint8_t *pPemData, int32_t iPemSize) +{ + const uint8_t *pCertBeg, *pCertEnd; + CertificateDataT *pCertificate = NULL; + X509CertificateT Cert; + int32_t iCertSize; + uint32_t uCertType; + + // find the base64-encoded certificate data + iPemSize = _CertificateFindData(pPemData, iPemSize, &pCertBeg, &pCertEnd, &uCertType); + // get decoded size + iCertSize = Base64Decode3((const char *)pCertBeg, iPemSize, NULL, 0x7fffffff); + // allocate certificate buffer + if ((iCertSize > 0) && ((pCertificate = DirtyMemAlloc(sizeof(*pCertificate)+iCertSize, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) != NULL)) + { + // base64 decode to binary + Base64Decode3((const char *)pCertBeg, iPemSize, (char *)pCertificate->aCertData, iCertSize); + // parse the certificate + _AsnParseCertificate(&Cert, pCertificate->aCertData, iCertSize); + // save some cert info + pCertificate->iCertSize = iCertSize; + pCertificate->iCrvType = Cert.iCrvType; + pCertificate->iKeyType = Cert.iKeyType; + pCertificate->iSigType = Cert.iSigType; + } + return(pCertificate); +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _CertificateDebugFormatIdent + + \Description + Print cert ident info to debug output. + + \Input *pIdent - cert ident info + \Input *pStrBuf - [out] buffer to format cert ident into + \Input iBufLen - length of buffer + + \Output + char * - pointer to result string + + \Version 01/13/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_CertificateDebugFormatIdent(const ProtoSSLCertIdentT *pIdent, char *pStrBuf, int32_t iBufLen) +{ + ds_snzprintf(pStrBuf, iBufLen, "C=%s, ST=%s, L=%s, O=%s, OU=%s, CN=%s", + pIdent->strCountry, pIdent->strState, pIdent->strCity, + pIdent->strOrg, pIdent->strUnit, pIdent->strCommon); + return(pStrBuf); +} +#else +#define _CertificateDebugFormatIdent(__pIdent, __pStrBuf, __iBufLen) +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _CertificateDebugPrint + + \Description + Print cert ident info to debug output. + + \Input *pCert - cert to print debug info for + \Input *pMessage - string identifier (may be null) + + \Version 01/13/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CertificateDebugPrint(const X509CertificateT *pCert, const char *pMessage) +{ + char strCertIdent[1024]; + #if DEBUG_VAL_CERT + char strTimeFrom[32], strTimeTill[32]; + char strMaxHeight[32]; + #endif + if (pMessage != NULL) + { + NetPrintf(("protossl: %s\n", pMessage)); + } + NetPrintf(("protossl: issuer: %s\n", _CertificateDebugFormatIdent(&pCert->Issuer, strCertIdent, sizeof(strCertIdent)))); + NetPrintf(("protossl: subject: %s\n", _CertificateDebugFormatIdent(&pCert->Subject, strCertIdent, sizeof(strCertIdent)))); + #if DEBUG_VAL_CERT + NetPrintf(("protossl: valid: from %s to %s\n", + ds_secstostr(pCert->uGoodFrom, TIMETOSTRING_CONVERSION_RFC_0822, FALSE, strTimeFrom, sizeof(strTimeFrom)), + ds_secstostr(pCert->uGoodTill, TIMETOSTRING_CONVERSION_RFC_0822, FALSE, strTimeTill, sizeof(strTimeTill)))); + NetPrintf(("protossl: keytype: %d\n", pCert->iKeyType)); + NetPrintf(("protossl: keyexp size: %d\n", pCert->iKeyExpSize)); + NetPrintf(("protossl: keymod size: %d\n", pCert->iKeyModSize)); + NetPrintf(("protossl: sigtype: %d\n", pCert->iSigType)); + NetPrintf(("protossl: sigsize: %d\n", pCert->iSigSize)); + ds_snzprintf(strMaxHeight, sizeof(strMaxHeight), "%d", pCert->iMaxHeight - 1); + NetPrintf(("protossl: CertIsCA(%d): pathLenConstraints(%s)\n", pCert->iCertIsCA, (pCert->iMaxHeight == 0) ? "unlimited" : strMaxHeight)); + #endif +} +#else +#define _CertificateDebugPrint(__pCert, __pMessage) +#endif + +/*F********************************************************************************/ +/*! + \Function _CertificateSetFailureInfo + + \Description + Set the certificate info (used on failure) + + \Input *pState - module state (may be NULL) + \Input *pCert - pointer to certificate to fill in from header data (may be NULL) + \Input bIssuer - if TRUE copy issuer, else subject + + \Version 01/24/2012 (szhu) +*/ +/********************************************************************************F*/ +static void _CertificateSetFailureInfo(ProtoSSLRefT *pState, X509CertificateT *pCert, uint8_t bIssuer) +{ + if ((pState != NULL) && (pCert != NULL) && (pState->bCertInfoSet == FALSE)) + { + const ProtoSSLCertIdentT *pCertIdent = bIssuer ? &pCert->Issuer : &pCert->Subject; + ds_memcpy_s(&pState->CertInfo.Ident, sizeof(pState->CertInfo.Ident), pCertIdent, sizeof(*pCertIdent)); + pState->CertInfo.iKeyModSize = pCert->iSigSize; // CACert::KeyModSize == Cert::SigSize + pState->bCertInfoSet = TRUE; + } +} + +/*F********************************************************************************/ +/*! + \Function _CertificateCompareIdent + + \Description + Compare two identities + + \Input *pIdent1 - pointer to ProtoSSLCertIdentT structure + \Input *pIdent2 - pointer to ProtoSSLCertIdentT structure + \Input bMatchUnit - if TRUE compare the Unit string + + \Output + int32_t - zero=match, non-zero=no-match + + \Notes + Unit is considered an optional match criteria by openssl. + + \Version 03/11/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CertificateCompareIdent(const ProtoSSLCertIdentT *pIdent1, const ProtoSSLCertIdentT *pIdent2, uint8_t bMatchUnit) +{ + int32_t iResult; + + #if DEBUG_VAL_CERT + char strIdent1[256], strIdent2[256]; + _CertificateDebugFormatIdent(pIdent1, strIdent1, sizeof(strIdent1)); + _CertificateDebugFormatIdent(pIdent2, strIdent2, sizeof(strIdent2)); + NetPrintf(("protossl: comparing '%s' to '%s'\n", strIdent2, strIdent1)); + #endif + + iResult = strcmp(pIdent1->strCountry, pIdent2->strCountry) != 0; + iResult += strcmp(pIdent1->strState, pIdent2->strState) != 0; + iResult += strcmp(pIdent1->strCity, pIdent2->strCity) != 0; + iResult += strcmp(pIdent1->strOrg, pIdent2->strOrg) != 0; + iResult += strcmp(pIdent1->strCommon, pIdent2->strCommon) != 0; + if (bMatchUnit) + { + iResult += strcmp(pIdent1->strUnit, pIdent2->strUnit) != 0; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _CertificateMatchHostname + + \Description + Perform wildcard case-insensitive string comparison of given input + strings. This implementation assumes the first string does not + include a wildcard character and the second string includes exactly + zero or one wildcard characters, which must be an asterisk. The + wildcard is intentionally limited as per the notes below to only + match a single domain fragment. + + \Input *pString1 - first string to compare + \Input *pString2 - second string to compare + + \Output + int32_t - like strcmp() + + \Notes + As specified per RFC 2818: + + \verbatim + Matching is performed using the matching rules specified by + [RFC2459]. If more than one identity of a given type is present in + the certificate (e.g., more than one dNSName name, a match in any one + of the set is considered acceptable.) Names may contain the wildcard + character * which is considered to match any single domain name + component or component fragment. E.g., *.a.com matches foo.a.com but + not bar.foo.a.com. f*.com matches foo.com but not bar.com. + \endverbatim + + \Version 03/19/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CertificateMatchHostname(const char *pString1, const char *pString2) +{ + int32_t r; + char c1, c2; + + do { + c1 = *pString1++; + if ((c1 >= 'A') && (c1 <= 'Z')) + c1 ^= 32; + c2 = *pString2; + if ((c2 >= 'A') && (c2 <= 'Z')) + c2 ^= 32; + if ((c2 == '*') && (c1 != '.') && (c1 != '\0')) + { + r = _CertificateMatchHostname(pString1, pString2+1); + if (r == 0) + { + break; + } + r = 0; + } + else + { + pString2 += 1; + r = c1-c2; + } + } while ((c1 != 0) && (c2 != 0) && (r == 0)); + + return(r); +} + +/*F********************************************************************************/ +/*! + \Function _CertificateMatchSubjectAlternativeName + + \Description + Takes as input a hostname and Subject Alternative Name object, + returns zero if the hostname matches a SAN object entry and non-zero + otherwise. + + \Input *pStrHost - pointer to hostname to compare against + \Input *pSubject - pointer to subject alternative object + \Input iSubjectAltLen - length of subject alternative object + + \Output + int32_t - like strcmp() + + \Version 11/01/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CertificateMatchSubjectAlternativeName(const char *pStrHost, const uint8_t *pSubject, int32_t iSubjectAltLen) +{ + const uint8_t *pSubjectEnd = pSubject + iSubjectAltLen; + char strCompare[256], *pStrCompare; + int32_t iType, iSize; + + // parse the wrapper object + pSubject = _AsnParseHeader(pSubject, pSubjectEnd, &iType, &iSize); + + // check against subject alternative strings + for (pSubjectEnd = pSubject + iSubjectAltLen; pSubject != NULL; pSubject += iSize) + { + // parse the object + pSubject = _AsnParseHeader(pSubject, pSubjectEnd, &iType, &iSize); + + // if it's not a dnsname object, skip it + if (iType != ASN_IMPLICIT_TAG+2) + { + continue; + } + + // make a copy of the comparison string + ds_strsubzcpy(strCompare, sizeof(strCompare), (const char *)pSubject, iSize); + + // skip leading whitespace, if any + for (pStrCompare = strCompare; ((*pStrCompare > 0) && (*pStrCompare <= ' ')); pStrCompare += 1) + ; + + // do the name compare + if (_CertificateMatchHostname(pStrHost, pStrCompare) == 0) + { + return(0); + } + } + + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _CertificateGetSigAlg + + \Description + Get signature algorithm to use based on certificate key type + + \Input *pCertificate - certificate to get signature algorithm for + + \Output + uint32_t uCertSigAlg - signature algorithm + + \Version 05/12/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _CertificateGetSigAlg(const CertificateDataT *pCertificate) +{ + uint32_t uCertSigAlg = SSL3_SIGALG_NONE; + if (pCertificate != NULL) + { + uCertSigAlg = (pCertificate->iKeyType == ASN_OBJ_ECDSA_KEY) ? SSL3_SIGALG_ECDSA : SSL3_SIGALG_RSA; + } + return(uCertSigAlg); +} + +/* + pkcs1 verification and generation +*/ + +/*F********************************************************************************/ +/*! + \Function _Pkcs1VerifyRSAES + + \Description + Validate RSAES-PKCS1-v1_5 padding as defined by + https://tools.ietf.org/html/rfc8017#section-7.2. + + \Input *pData - data to validate + \Input iKeySize - key modulus size, in bytes + \Input iSecretSize - premaster secret size, in bytes + + \Output + int32_t - zero=invalid, else valid + + \Version 02/18/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _Pkcs1VerifyRSAES(const uint8_t *pData, int32_t iKeySize, int32_t iSecretSize) +{ + int32_t iIndex; + uint8_t bValid = TRUE; + + // validate signature + if ((pData[0] != 0) || (pData[1] != 2)) + { + NetPrintf(("protossl: pkcs1 signature invalid\n")); + bValid = FALSE; + } + // validate random padding; must be non-zero + for (iIndex = 2; iIndex < iKeySize-iSecretSize-1; iIndex += 1) + { + if (pData[iIndex] == 0) + { + NetPrintf(("protossl: pkcs1 padding validation found zero byte\n")); + bValid = FALSE; + break; + } + } + // validate zero byte before premaster key data + if (pData[iKeySize-iSecretSize-1] != 0) + { + NetPrintf(("protossl: pkcs1 premaster key padding not zero\n")); + bValid = FALSE; + } + // return result + return(bValid); +} + +/*F********************************************************************************/ +/*! + \Function _Pkcs1VerifyEMSA + + \Description + As per https://tools.ietf.org/html/rfc5246#section-4.7 validate + EMSA-PKCS1-v1_5 padding, and return pointer to signature hash if valid. + Format defined by https://tools.ietf.org/html/rfc8017#section-9.2. + + \Input *pData - data to validate + \Input iSigSize - signature size, in bytes + \Input iHashSize - signature hash size, in bytes + \Input eHashType - signature hash type (if CRYPTHASH_NULL, don't validate hash object) + + \Output + uint8_t * - pointer to signature data, or NULL if invalid + + \Version 04/27/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static const uint8_t *_Pkcs1VerifyEMSA(const uint8_t *pData, int32_t iSigSize, int32_t iHashSize, CryptHashTypeE eHashType) +{ + uint8_t aHashObj[CRYPTHASH_MAXDIGEST+64]; + int32_t iIndex, iSize; + + // validate EMSA signature + if ((pData[0] != 0) || (pData[1] != 1)) + { + return(NULL); + } + // validate padding; must be 0xff + for (iIndex = 2; (iIndex < (iSigSize-iHashSize)) && (pData[iIndex] == 0xff); iIndex += 1) + ; + // validate zero byte immediately preceding signature hash object + if (pData[iIndex++] != 0) + { + return(NULL); + } + + // validate the digital hash object that wraps the signature hash + if (eHashType != CRYPTHASH_NULL) + { + // generate digitahash object we expect, and validate we got it + if (((iSize = _AsnWriteDigitalHashObject(aHashObj, sizeof(aHashObj), NULL, iHashSize, eHashType)) == 0) || (memcmp(aHashObj, pData + iIndex, iSize))) + { + return(NULL); + } + iIndex += iSize; + } + + // make sure we validated the entire signature, minus the hash itself + if ((iIndex+iHashSize) != iSigSize) + { + return(NULL); + } + + // return pointer to signature hash + return(pData+iIndex); +} + +/*F********************************************************************************/ +/*! + \Function _Pkcs1DoMGF1 + + \Description + Implements MFG1 as per https://tools.ietf.org/html/rfc8017#appendix-B.2.1 + + \Input *pOutput - [out] output of mgf + \Input iOutputLen - length of output buf + \Input *pInput - mgf input + \Input iInputLen - length of mgf input + \Input eHashType - mgf hash type + + \Version 05/11/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _Pkcs1DoMGF1(uint8_t *pOutput, int32_t iOutputLen, const uint8_t *pInput, int32_t iInputLen, CryptHashTypeE eHashType) +{ + uint8_t aHashState[CRYPTHASH_MAXSTATE], aCounter[4]; + const CryptHashT *pHash; + int32_t iHashLen; + uint32_t uRound; + + // get hash object and length + if ((pHash = CryptHashGet(eHashType)) == NULL) + { + return; + } + iHashLen = CryptHashGetSize(eHashType); + + // do the hashing + for (uRound = 0; iOutputLen > 0; uRound += 1, pOutput += iHashLen, iOutputLen -= iHashLen) + { + aCounter[0] = (uint8_t)(uRound >> 24); + aCounter[1] = (uint8_t)(uRound >> 16); + aCounter[2] = (uint8_t)(uRound >> 8); + aCounter[3] = (uint8_t)(uRound); + + pHash->Init(aHashState, iHashLen); + pHash->Update(aHashState, pInput, iHashLen); + pHash->Update(aHashState, aCounter, sizeof(aCounter)); + pHash->Final(aHashState, pOutput, iHashLen); + } +} + +/*F********************************************************************************/ +/*! + \Function _Pkcs1VerifyEMSA_PSS + + \Description + As per https://tools.ietf.org/html/rfc8017#section-9.1.2 validate + EMSA-PSS envelope, and compare against the specified hash value. + + \Input *pSigData - signature data + \Input iSigSize - signature size, in bytes + \Input eSigHash - signature hash + \Input iSaltSize - salt size, in bytes + \Input *pMsgHash - message hash data + \Input iHashSize - signature hash size, in bytes + \Input eMgfHash - mgf hash type + + \Output + uint32_t - zero=verify failure, else success + + \Notes + This function deliberately uses coding standards and comments matching + the specification to make it easier to compare while reading the specs. + + The maximum salt length supported is 128 bytes (2*CRYPTHASH_MAXDIGEST) + + \Version 05/11/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _Pkcs1VerifyEMSA_PSS(const uint8_t *pSigData, int32_t iSigSize, CryptHashTypeE eSigHash, int32_t iSaltSize, const uint8_t *pMsgHash, int32_t iHashSize, CryptHashTypeE eMgfHash) +{ + uint8_t maskedDB[SSL_SIG_MAX], dbMask[SSL_SIG_MAX], DB[SSL_SIG_MAX], H[CRYPTHASH_MAXDIGEST], salt[CRYPTHASH_MAXDIGEST*2]; + const CryptHashT *pHash = CryptHashGet(eSigHash); + uint8_t aHashState[CRYPTHASH_MAXSTATE]; + uint8_t Mprime[CRYPTHASH_MAXDIGEST*3 + 8], Hprime[CRYPTHASH_MAXDIGEST]; + int32_t iDB, iDBLen; + uint8_t uCheck; + + // if the rightmost octet of EM does not have hexadecimal value 0xbc, output "inconsistent" and stop. + if (pSigData[iSigSize-1] != 0xBC) + { + NetPrintf(("protossl: signature failed identity check\n")); + return(0); + } + + // let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and let H be the next hLen octets. + iDBLen = iSigSize - iHashSize - 1; + ds_memcpy_s(maskedDB, sizeof(maskedDB), pSigData, iDBLen); + ds_memcpy_s(H, sizeof(H), pSigData + iDBLen, iHashSize); + + /* if the leftmost 8emLen - emBits bits of the leftmost octet in maskedDB are not all equal to zero, output "inconsistent" and stop. + note that we assume our modulus size here is a multiple of eight bits */ + if (maskedDB[0] & 0x80) + { + NetPrintf(("protossl: signature failed masked validity check\n")); + return(0); + } + + // let dbMask = MGF(H, emLen - hLen - 1). + _Pkcs1DoMGF1(dbMask, iDBLen, H, iDBLen, eSigHash); + + // let DB = maskedDB \xor dbMask. + for (iDB = 0; iDB < iDBLen; iDB += 1) + { + DB[iDB] = maskedDB[iDB] ^ dbMask[iDB]; + } + + /* set the leftmost 8emLen - emBits bits of the leftmost octet in DB to zero. + note that we assume our modulus size here is a multiple of eight bits */ + DB[0] &= 0x7f; + + #if DEBUG_RAW_DATA + NetPrintMem(DB, iDBLen, "certificate verify unmasked data"); + #endif + + /* if the emLen - hLen - sLen - 2 leftmost octets of DB are not zero or if the octet at position emLen - hLen - sLen - 1 + (the leftmost position is "position 1") does not have hexadecimal value 0x01, output "inconsistent" and stop. */ + for (iDB = 0, uCheck = 0; iDB < (iDBLen - iSaltSize - 1); iDB += 1) + uCheck |= DB[iDB]; + if ((uCheck != 0) || (DB[iDB] != 1)) + { + NetPrintf(("protossl: signature failed padding validity check\n")); + return(0); + } + + // validate salt length + if (iSaltSize > (signed)sizeof(salt)) + { + NetPrintf(("protossl: salt length %d too big in signature verify\n", iSaltSize)); + return(0); + } + // let salt be the last sLen octets of DB. + ds_memcpy_s(salt, sizeof(salt), DB + iDBLen - iSaltSize, iSaltSize); + + // let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt + ds_memclr(Mprime, 8); + ds_memcpy(Mprime + 8, pMsgHash, iHashSize); + ds_memcpy(Mprime + 8 + iHashSize, salt, iSaltSize); + + // let H' = Hash(M'), an octet string of length hLen. + pHash->Init(aHashState, iHashSize); + pHash->Update(aHashState, Mprime, 8 + iHashSize + iSaltSize); + pHash->Final(aHashState, Hprime, iHashSize); + + #if DEBUG_RAW_DATA + NetPrintMem(H, iHashSize, "H"); + NetPrintMem(Hprime, iHashSize, "Hprime"); + NetPrintMem(pMsgHash, iHashSize, "MsgHash"); + NetPrintMem(salt, iSaltSize, "salt"); + #endif + + // if H = H', output "consistent". Otherwise, output "inconsistent". + if (memcmp(H, Hprime, iHashSize)) + { + NetPrintf(("protossl: signature failed hash validity check\n")); + return(0); + } + + // success + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _Pkcs1EncodeEMSA_PSS + + \Description + As per https://tools.ietf.org/html/rfc8017#section-9.1.1 encode + specified hash data in an EMSA-PSS envelope. + + \Input *pBuffer - [out] storage for encoded message + \Input iBufSize - size of output buffer + \Input *pHashData - message hash + \Input iHashSize - size of hash + \Input eHashType - hash type + + \Output + int32_t - size of encoded message, or negative on error + + \Notes + This function deliberately uses coding standards and comments matching + the specification to make it easier to compare while reading the specs. + + \Version 05/16/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _Pkcs1EncodeEMSA_PSS(uint8_t *pBuffer, int32_t iBufSize, const uint8_t *pHashData, int32_t iHashSize, CryptHashTypeE eHashType) +{ + uint8_t maskedDB[SSL_SIG_MAX], dbMask[SSL_SIG_MAX], DB[SSL_SIG_MAX]; + uint8_t aSaltBuf[CRYPTHASH_MAXDIGEST], Mprime[(CRYPTHASH_MAXDIGEST*2)+8], H[(CRYPTHASH_MAXDIGEST*2)+8], *pTemp; + const CryptHashT *pHash = CryptHashGet(eHashType); + uint8_t aHashState[CRYPTHASH_MAXSTATE]; + int32_t iDB, iDBLen = iBufSize - iHashSize - 1; + + // if emLen < hLen + sLen + 2, output "encoding error" and stop. + if (iBufSize < ((iHashSize*2)+2)) + { + return(-1); + } + // generate a random octet string salt of length sLen; if sLen = 0, then salt is the empty string. + CryptRandGet(aSaltBuf, sizeof(aSaltBuf)); + // let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt + pTemp = Mprime; + ds_memclr(pTemp, 8); + pTemp += 8; + ds_memcpy(pTemp, pHashData, iHashSize); + pTemp += iHashSize; + ds_memcpy(pTemp, aSaltBuf, iHashSize); + + // let H = Hash(M'), an octet string of length hLen. + pHash->Init(aHashState, iHashSize); + pHash->Update(aHashState, Mprime, 8 + iHashSize + iHashSize); + pHash->Final(aHashState, H, iHashSize); + + #if DEBUG_RAW_DATA + NetPrintMem(H, iHashSize, "H"); + NetPrintMem(pHashData, iHashSize, "MsgHash"); + NetPrintMem(aSaltBuf, iHashSize, "salt"); + #endif + + // generate an octet string PS consisting of emLen - sLen - hLen - 2 zero octets. The length of PS may be 0. + ds_memclr(DB, iBufSize - (iHashSize*2) - 2); + pTemp = DB + iBufSize - (iHashSize*2) - 2; + + // let DB = PS || 0x01 || salt; DB is an octet string of length emLen - hLen - 1. + *pTemp++ = 0x01; + ds_memcpy(pTemp, aSaltBuf, iHashSize); + + // let dbMask = MGF(H, emLen - hLen - 1). + _Pkcs1DoMGF1(dbMask, iDBLen, H, iDBLen, eHashType); + + // let maskedDB = DB \xor dbMask. + for (iDB = 0; iDB < iDBLen; iDB += 1) + { + maskedDB[iDB] = DB[iDB] ^ dbMask[iDB]; + } + + /* set the leftmost 8emLen - emBits bits of the leftmost octet in maskedDB to zero. + note that we assume our modulus size here is a multiple of eight bits */ + maskedDB[0] &= 0x7f; + + // let EM = maskedDB || H || 0xbc. + pTemp = pBuffer; + ds_memcpy(pTemp, maskedDB, iDBLen); + pTemp += iDBLen; + ds_memcpy(pTemp, H, iHashSize); + pTemp += iHashSize; + *pTemp++ = 0xbc; + + // output EM + return(pTemp-pBuffer); +} + +/* + session history +*/ + +/*F********************************************************************************/ +/*! + \Function _SessionHistorySet + + \Description + Set entry in session history buffer + + \Input *pSecure - secure state + \Input *pSessHist - session history entry to set + \Input *pSessTick - session ticket for session (if tls1.3; else NULL) + \Input *pHostName - hostname session is associated with + \Input uPort - port session is associated with + \Input *pSessionId - session id of session + \Input *pMasterSecret- master secret associated with session + \Input uCurTick - current tick count + + \Version 03/15/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SessionHistorySet(SecureStateT *pSecure, SessionHistoryT *pSessHist, SessionTicketT *pSessTick, const char *pHostName, uint16_t uPort, const uint8_t *pSessionId, const uint8_t *pMasterSecret, uint32_t uCurTick) +{ + // save hostname & port + ds_strnzcpy(pSessHist->strHost, pHostName, sizeof(pSessHist->strHost)); + pSessHist->uPort = uPort; + // set access tick + pSessHist->uSessionUseTick = uCurTick; + // set expire tick; add a 250ms fudge factor to make sure we don't send an expiring ticket + pSessHist->uSessionExpTick = (pSessTick != NULL) ? pSessTick->uLifetime * 1000 : SSL_SESSVALIDTIME_MAX; + pSessHist->uSessionExpTick += uCurTick-250; + + if (pSessTick != NULL) + { + pSessHist->bSessionTicket = TRUE; + ds_memcpy_s(&pSessHist->SessionTicket, sizeof(pSessHist->SessionTicket), pSessTick, sizeof(*pSessTick)); + } + else + { + SessionInfoT *pSessInfo = &pSessHist->SessionInfo; + pSessHist->bSessionTicket = FALSE; + pSessInfo->uSslVersion = pSecure->uSslVersion; + pSessInfo->uCipherId = pSecure->pCipher->uIdent; + ds_memcpy(pSessInfo->SessionId, pSessionId, sizeof(pSessInfo->SessionId)); + ds_memcpy(pSessInfo->MasterSecret, pMasterSecret, sizeof(pSessInfo->MasterSecret)); + } +} + +/*F********************************************************************************/ +/*! + \Function _SessionHistoryGet + + \Description + Returns pointer to specified session history entry, or NULL if not found. + + \Input *pHostName - hostname associated with session (may be null) + \Input uPort - port associated with session (if using hostname) + \Input *pSessionId - session id to look for (may be null) + + \Output + SessionHistoryT * - session history entry, or NULL if not found + + \Version 03/15/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static SessionHistoryT *_SessionHistoryGet(const char *pHostName, uint16_t uPort, const uint8_t *pSessionId) +{ + SessionHistoryT *pSessHist, *pSessInfo; + ProtoSSLStateT *pState = _ProtoSSL_pState; + const uint8_t aZeroSessionId[32] = { 0 }; + int32_t iSess; + + #if DIRTYCODE_DEBUG + if (pState == NULL) + { + NetPrintf(("protossl: warning, protossl not initialized, session history feature is disabled\n")); + return(NULL); + } + #endif + + // exclude NULL sessionId + if ((pSessionId != NULL) && !memcmp(pSessionId, aZeroSessionId, sizeof(aZeroSessionId))) + { + return(NULL); + } + + // search session history for a match + for (iSess = 0, pSessInfo = NULL; (iSess < SSL_SESSHIST_MAX) && (pSessInfo == NULL); iSess += 1) + { + pSessHist = &pState->SessionHistory[iSess]; + + // make sure the ticket isn't expired + if (NetTickDiff(NetTick(), pSessHist->uSessionExpTick) > 0) + { + NetPrintfVerbose((DEBUG_RES_SESS, 0, "protossl: expiring session for %s:%d\n", pSessHist->strHost, pSessHist->uPort)); + continue; + } + + // check for resume by hostname/port + if ((pHostName != NULL) && (!strcmp(pSessHist->strHost, pHostName)) && (pSessHist->uPort == uPort)) + { + pSessInfo = pSessHist; + } + // check for resume by sessionId (tls1.2 and prior) + if ((pSessionId != NULL) && (!memcmp(pSessHist->SessionInfo.SessionId, pSessionId, sizeof(pSessHist->SessionInfo.SessionId)))) + { + pSessInfo = pSessHist; + } + } + + // return session (or null) to caller + return(pSessInfo); +} + +/*F********************************************************************************/ +/*! + \Function _SessionHistoryAdd + + \Description + Save a new session in the session history buffer + + \Input *pSecure - secure state + \Input *pHostName - hostname session is for + \Input uPort - port session is for + \Input *pSessTick - session ticket for session, if tls1.3 + \Input *pSessionId - session id for session + \Input iSessionLen - length of session + \Input *pMasterSecret - master secret associated with session + + \Version 03/15/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SessionHistoryAdd(SecureStateT *pSecure, const char *pHostName, uint16_t uPort, SessionTicketT *pSessTick, const uint8_t *pSessionId, uint32_t iSessionLen, const uint8_t *pMasterSecret) +{ + ProtoSSLStateT *pState = _ProtoSSL_pState; + SessionHistoryT *pSessHist; + int32_t iSess, iSessOldest; + int32_t iTickDiff, iTickDiffMax; + uint32_t uCurTick = NetTick(); + + #if DIRTYCODE_DEBUG + if (pState == NULL) + { + NetPrintf(("protossl: warning, protossl not initialized, session history feature is disabled\n")); + return; + } + #endif + + NetCritEnter(&pState->StateCrit); + + // see if we already have a session for this peer + for (iSess = 0; iSess < SSL_SESSHIST_MAX; iSess += 1) + { + pSessHist = &pState->SessionHistory[iSess]; + // already have this peer in our history? + if (!strcmp(pSessHist->strHost, pHostName) && (pSessHist->uPort == uPort)) + { + NetPrintfVerbose((DEBUG_RES_SESS, 0, "protossl: updating session history entry %d (host=%s:%d)\n", iSess, pHostName, uPort)); + _SessionHistorySet(pSecure, pSessHist, pSessTick, pHostName, uPort, pSessionId, pMasterSecret, uCurTick); + NetCritLeave(&pState->StateCrit); + return; + } + } + + // try to find an unallocated session + for (iSess = 0; iSess < SSL_SESSHIST_MAX; iSess += 1) + { + pSessHist = &pState->SessionHistory[iSess]; + // empty slot? + if (pSessHist->strHost[0] == '\0') + { + NetPrintfVerbose((DEBUG_RES_SESS, 0, "protossl: adding session history entry %d (host=%s:%d)\n", iSess, pHostName, uPort)); + _SessionHistorySet(pSecure, pSessHist, pSessTick, pHostName, uPort, pSessionId, pMasterSecret, uCurTick); + NetCritLeave(&pState->StateCrit); + return; + } + } + + // find the oldest session + for (iSess = 0, iTickDiffMax = 0, iSessOldest = 0; iSess < SSL_SESSHIST_MAX; iSess += 1) + { + pSessHist = &pState->SessionHistory[iSess]; + // find least recently updated session + if ((iTickDiff = NetTickDiff(uCurTick, pSessHist->uSessionUseTick)) > iTickDiffMax) + { + iTickDiffMax = iTickDiff; + iSessOldest = iSess; + } + } + + NetPrintfVerbose((DEBUG_RES_SESS, 0, "protossl: replacing session history entry %d (host=%s:%d)\n", iSessOldest, pHostName, uPort)); + _SessionHistorySet(pSecure, &pState->SessionHistory[iSessOldest], pSessTick, pHostName, uPort, pSessionId, pMasterSecret, uCurTick); + + NetCritLeave(&pState->StateCrit); +} + +/*F********************************************************************************/ +/*! + \Function _SessionHistoryInvalidate + + \Description + Invalidate specified session from session history cache + + \Input *pHostName - hostname of session to invalidate + \Input uPort - port of session to invalidate + + \Version 02/04/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _SessionHistoryInvalidate(const char *pHostName, uint16_t uPort) +{ + SessionHistoryT *pSessInfo = NULL; + ProtoSSLStateT *pState = _ProtoSSL_pState; + + #if DIRTYCODE_DEBUG + if (pState == NULL) + { + NetPrintf(("protossl: warning, protossl not initialized, session history feature is disabled\n")); + return; + } + #endif + + // acquire access to session history + NetCritEnter(&pState->StateCrit); + + // find session history + if ((pSessInfo = _SessionHistoryGet(pHostName, uPort, NULL)) != NULL) + { + // reset entry + NetPrintf(("protossl: invalidating session entry\n")); + ds_memclr(pSessInfo, sizeof(*pSessInfo)); + } + + // release access to session history, return to caller + NetCritLeave(&pState->StateCrit); +} + +/*F********************************************************************************/ +/*! + \Function _SessionHistoryGetInfo + + \Description + Check to see if we can find a session for the specified address or session + id in our history buffer, and return sesession info if found. + + \Input *pSessBuff - [out] storage for session history entry + \Input *pHostName - hostname of session (may be null) + \Input uPort - port of session (if hostname is not null) + \Input *pSessionId - session id to look for (may be null) + + \Output + SessionHistoryT * - session history entry, or NULL if not found + + \Version 03/15/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static SessionHistoryT *_SessionHistoryGetInfo(SessionHistoryT *pSessBuff, const char *pHostName, uint16_t uPort, const uint8_t *pSessionId) +{ + SessionHistoryT *pSessInfo = NULL; + ProtoSSLStateT *pState = _ProtoSSL_pState; + + #if DIRTYCODE_DEBUG + if (pState == NULL) + { + NetPrintf(("protossl: warning, protossl not initialized, session history feature is disabled\n")); + return(NULL); + } + #endif + + // acquire access to session history + NetCritEnter(&pState->StateCrit); + + // find session history + if ((pSessInfo = _SessionHistoryGet(pHostName, uPort, pSessionId)) != NULL) + { + // update timestamp + pSessInfo->uSessionUseTick = NetTick(); + // copy to caller + ds_memcpy(pSessBuff, pSessInfo, sizeof(*pSessBuff)); + pSessInfo = pSessBuff; + } + + // release access to session history, return to caller + NetCritLeave(&pState->StateCrit); + return(pSessInfo); +} + +/* + certificate validation vistory +*/ + +/*F********************************************************************************/ +/*! + \Function _CertValidHistorySet + + \Description + Set entry in certvalid history buffer + + \Input *pCertHist - session history entry to set + \Input *pFingerprintId - fingerprint for certificates validated + \Input uCertSize - certificate size + \Input uCurTick - current tick count + + \Version 04/29/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CertValidHistorySet(CertValidHistoryT *pCertHist, const uint8_t *pFingerprintId, uint32_t uCertSize, uint32_t uCurTick) +{ + // update fingerprint + ds_memcpy(pCertHist->FingerprintId, pFingerprintId, sizeof(pCertHist->FingerprintId)); + // save cert size + pCertHist->uCertSize = uCertSize; + // set timestamp + pCertHist->uCertValidTick = uCurTick; +} + +/*F********************************************************************************/ +/*! + \Function _CertValidHistoryGet + + \Description + Returns pointer to specified session history entry, or NULL if not found. + + \Input *pFingerprintId - certificate fingerprint + \Input uCertSize - size of certificate to check in validity history + + \Output + CertValidHistoryT * - certificate validation history entry, or NULL if not found + + \Version 04/29/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static CertValidHistoryT *_CertValidHistoryGet(const uint8_t *pFingerprintId, uint32_t uCertSize) +{ + CertValidHistoryT *pCertHist, *pCertInfo; + ProtoSSLStateT *pState = _ProtoSSL_pState; + const uint8_t aZeroFingerprintId[SSL_FINGERPRINT_SIZE] = { 0 }; + uint32_t uCurTick = NetTick(); + int32_t iCert; + + #if DIRTYCODE_DEBUG + if (pState == NULL) + { + NetPrintf(("protossl: warning, protossl not initialized, certificate validation history feature is disabled\n")); + return(NULL); + } + #endif + + // exclude NULL fingerprintId request + if ((pFingerprintId == NULL) || !memcmp(pFingerprintId, aZeroFingerprintId, sizeof(aZeroFingerprintId))) + { + return(NULL); + } + + // find certificate history + for (iCert = 0, pCertInfo = NULL; iCert < SSL_CERTVALID_MAX; iCert += 1) + { + // ref history entry + pCertHist = &pState->CertValidHistory[iCert]; + // skip null entries + if (pCertHist->uCertSize == 0) + { + continue; + } + // check for expiration + if (NetTickDiff(uCurTick, pCertHist->uCertValidTick) > SSL_CERTVALIDTIME_MAX) + { + NetPrintfVerbose((DEBUG_VAL_CERT, 0, "protossl: expiring certificate validity for entry %d\n", iCert)); + ds_memclr(pCertHist, sizeof(*pCertHist)); + continue; + } + // check for match + if ((pCertHist->uCertSize == uCertSize) && (!memcmp(pCertHist->FingerprintId, pFingerprintId, sizeof(pCertHist->FingerprintId)))) + { + pCertInfo = pCertHist; + break; + } + } + + // return session (or null) to caller + return(pCertInfo); +} + +/*F********************************************************************************/ +/*! + \Function _CertValidHistoryAdd + + \Description + Added fingerprint of validated certificate to certificate validity history buffer + + \Input *pFingerprintId - certificate fingerprint + \Input uCertSize - size of certificate + + \Version 03/15/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _CertValidHistoryAdd(const uint8_t *pFingerprintId, uint32_t uCertSize) +{ + ProtoSSLStateT *pState = _ProtoSSL_pState; + CertValidHistoryT *pCertHist; + int32_t iCert, iCertOldest; + int32_t iTickDiff, iTickDiffMax; + uint32_t uCurTick = NetTick(); + + #if DIRTYCODE_DEBUG + if (pState == NULL) + { + NetPrintf(("protossl: warning, protossl not initialized, certificate validation history feature is disabled\n")); + return; + } + #endif + + NetCritEnter(&pState->StateCrit); + + // try to find an unallocated session + for (iCert = 0; iCert < SSL_CERTVALID_MAX; iCert += 1) + { + pCertHist = &pState->CertValidHistory[iCert]; + // empty slot? + if (pCertHist->uCertSize == 0) + { + #if DEBUG_VAL_CERT + NetPrintf(("protossl: adding certificate validation history entry %d\n", iCert)); + #endif + _CertValidHistorySet(pCertHist, pFingerprintId, uCertSize, uCurTick); + NetCritLeave(&pState->StateCrit); + return; + } + } + + // find the oldest session + for (iCert = 0, iTickDiffMax = 0, iCertOldest = 0; iCert < SSL_CERTVALID_MAX; iCert += 1) + { + pCertHist = &pState->CertValidHistory[iCert]; + // find least recently updated session + if ((iTickDiff = NetTickDiff(uCurTick, pCertHist->uCertValidTick)) > iTickDiffMax) + { + iTickDiffMax = iTickDiff; + iCertOldest = iCert; + } + } + + NetPrintfVerbose((DEBUG_VAL_CERT, 0, "protossl: replacing certificate validation entry %d\n", iCertOldest)); + _CertValidHistorySet(&pState->CertValidHistory[iCertOldest], pFingerprintId, uCertSize, uCurTick); + + NetCritLeave(&pState->StateCrit); +} + +/*F********************************************************************************/ +/*! + \Function _CertValidHistoryCheck + + \Description + Check to see if certificate fingerprint is in certificiate validity history + + \Input *pFingerprintId - certificate fingerprint + \Input uCertSize - size of certificate + + \Output + int32_t - zero=not validated, else validated + + \Version 04/29/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _CertValidHistoryCheck(const uint8_t *pFingerprintId, uint32_t uCertSize) +{ + ProtoSSLStateT *pState = _ProtoSSL_pState; + CertValidHistoryT *pCertInfo; + int32_t iResult = 0; + + #if DIRTYCODE_DEBUG + if (pState == NULL) + { + NetPrintf(("protossl: warning, protossl not initialized, certificate validation history feature is disabled\n")); + return(0); + } + #endif + + // acquire access to session history + NetCritEnter(&pState->StateCrit); + + // find certificate validity history + if ((pCertInfo = _CertValidHistoryGet(pFingerprintId, uCertSize)) != NULL) + { + iResult = 1; + NetPrintfVerbose((DEBUG_VAL_CERT, 0, "protossl: certificate validation deferred to cache\n")); + } + + // release access to session history, return to caller + NetCritLeave(&pState->StateCrit); + return(iResult); +} + +/* + digital signature generation and verification +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLEccInitContext + + \Description + Initialize ECC context for key exchange and retrieve the dh interface + + \Input *pSecure - secure state + \Input *pEllipticCurve - curve to init state with + + \Output + const CryptCurveDhT * - the found dh interface or NULL if not found for curve + + \Version 03/14/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const CryptCurveDhT *_ProtoSSLEccInitContext(SecureStateT *pSecure, const EllipticCurveT *pEllipticCurve) +{ + const CryptCurveDhT *pEcc; + + // find the dh interface, this _should not_ fail if everything is correctly configured + if ((pEcc = CryptCurveGetDh(pEllipticCurve->uIdent)) == NULL) + { + NetPrintf(("protossl: could not find curve %s (ident=0x%04x), mismatch between protossl and cryptcurve?\n", + pEllipticCurve->strName, pEllipticCurve->uIdent)); + return(NULL); + } + if (pSecure->bEccContextInitialized) + { + return(pEcc); + } + // init the state + if (pEcc->Init(pSecure->EccContext, pEllipticCurve->uIdent) == 0) + { + pSecure->bEccContextInitialized = TRUE; + } + else + { + NetPrintf(("protossl: initialize failed for curve %s (ident=0x%04x)\n", pEllipticCurve->strName, pEllipticCurve->uIdent)); + } + return(pEcc); +} + +/*F********************************************************************************/ +/* + \Function _ProtoSSLDsaInitContext + + \Description + Gets the interface for the ecdsa curve that we use for signing + and verifying + + \Input *pSigVerify - the signature verify state + \Input iCrvType - the asn identifier for the ecdsa curve + + \Output + const CryptCurveDsaT * - the interface or NULL if not found + + \Version 05/10/2018 (eesponda) +*/ +/********************************************************************************F*/ +static const CryptCurveDsaT *_ProtoSSLDsaInitContext(SignatureVerifyT *pSigVerify, int32_t iCrvType) +{ + const CryptCurveDsaT *pDsa; + int32_t iCurveIdent = 0; + + // map the ASN to the curve we want to use + if (iCrvType == ASN_OBJ_ECDSA_SECP256R1) + { + iCurveIdent = CRYPTCURVE_SECP256R1; + } + else if (iCrvType == ASN_OBJ_ECDSA_SECP384R1) + { + iCurveIdent = CRYPTCURVE_SECP384R1; + } + + // find the dsa interface, this _should not_ fail if everything is correctly configured + if ((pDsa = CryptCurveGetDsa(iCurveIdent)) == NULL) + { + NetPrintf(("protossl: could not find curve ident 0x%04x for dsa, mismatch between protossl and cryptcurve?\n", + iCurveIdent)); + return(NULL); + } + if (pSigVerify->bEccContextInitialized) + { + return(pDsa); + } + + // attempt to initialize, catch failure in case we get an unsupported curve + if (pDsa->Init(pSigVerify->DsaContext, iCurveIdent) == 0) + { + pSigVerify->bEccContextInitialized = TRUE; + } + else + { + NetPrintf(("protossl: initialize failed for dsa curve (ident=0x%04x)\n", iCurveIdent)); + } + return(pDsa); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateSignatureHash + + \Description + Generate a signature for key exchange + + \Input *pSecure - secure state + \Input *pHash - signature hash algorithm + \Input *pHashDigest - [out] buffer for signature hash + \Input *pData - data to sign + \Input iDataLen - length of data + + \Output + int32_t - size of signature hash + + \Version 02/17/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateSignatureHash(const SecureStateT *pSecure, const CryptHashT *pHash, uint8_t *pHashDigest, const uint8_t *pData, int32_t iDataLen) +{ + uint8_t HashState[CRYPTHASH_MAXSTATE]; + pHash->Init(HashState, pHash->iHashSize); + pHash->Update(HashState, pSecure->ClientRandom, sizeof(pSecure->ClientRandom)); + pHash->Update(HashState, pSecure->ServerRandom, sizeof(pSecure->ServerRandom)); + pHash->Update(HashState, pData, iDataLen); + pHash->Final(HashState, pHashDigest, pHash->iHashSize); + return(pHash->iHashSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateSignatureEcdsa + + \Description + Generate an ECDSA signature; expected to be called iteratively + + \Input *pSecure - secure state + \Input *pSigData - [out] signature data buffer + \Input iSigSize - signature data size + \Input *pPrivateKey - ECC private key data + \Input iKeySize - size of key + \Input iCrvType - curve type + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 03/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateSignatureEcdsa(SecureStateT *pSecure, const uint8_t *pSigData, int32_t iSigSize, const uint8_t *pPrivateKey, int32_t iKeySize, int32_t iCrvType) +{ + CryptEccPointT Signature; + int32_t iResult; + CryptBnT PrivKey; + uint32_t uCryptUsecs; + SignatureVerifyT *pSigVerify = &pSecure->SigVerify; + const CryptCurveDsaT *pDsa; + + // get the dsa functions + if ((pDsa = _ProtoSSLDsaInitContext(pSigVerify, iCrvType)) == NULL) + { + return(-1); + } + // if the state is not initialized then assume a failure + if (!pSigVerify->bEccContextInitialized) + { + return(-1); + } + + // init private key + CryptBnInitFrom(&PrivKey, -1, pPrivateKey, iKeySize); + + // sign digest with the private key + if ((iResult = pDsa->Sign(pSigVerify->DsaContext, &PrivKey, pSigData, iSigSize, &Signature, &uCryptUsecs)) != 1) + { + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (ecdsa sig encrypt) %dms\n", uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + pSigVerify->iSigSize = _AsnWriteSignatureEcdsa(pSigVerify->aSigData, sizeof(pSigVerify->aSigData), &Signature); + pSigVerify->bEccContextInitialized = FALSE; + } + return(iResult); +} +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateSignatureRSA + + \Description + Generate an RSA signature; expected to be called iteratively + + \Input *pSecure - secure state + \Input *pSigData - signature data + \Input iSigSize - signature data size + \Input *pPrivateKey - private key used to sign + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 03/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateSignatureRSA(SecureStateT *pSecure, const uint8_t *pSigData, int32_t iSigSize, const X509PrivateKeyT *pPrivateKey) +{ + // set up for rsa encryption + if (!pSecure->bRSAContextInitialized) + { + // setup to encrypt signature object using private key + if (CryptRSAInit2(&pSecure->RSAContext, pPrivateKey->Modulus.iObjSize, &pPrivateKey->PrimeP, &pPrivateKey->PrimeQ, &pPrivateKey->ExponentP, &pPrivateKey->ExponentQ, &pPrivateKey->Coefficient) != 0) + { + NetPrintf(("protossl: RSA init failed on private key encrypt\n")); + return(-1); + } + // encrypt signature object + if ((pSecure->uSslVersion < SSL3_TLS1_2) || (pSecure->pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PKCS1)) + { + CryptRSAInitPrivate(&pSecure->RSAContext, pSigData, iSigSize); + } + else + { + CryptRSAInitSignature(&pSecure->RSAContext, pSigData, iSigSize); + } + pSecure->bRSAContextInitialized = TRUE; + } + + /* encrypt signature hash; break it up into chunks of 64 to prevent blocking thread entire time. + we don't do them one at a time because for a typical private key that would be 2048 calls and + would incur unacceptable overhead, so this is a compromise between doing it all at once and + doing it one step at a time */ + if (CryptRSAEncrypt(&pSecure->RSAContext, 64) > 0) + { + return(1); + } + + // copy data to signature output + pSecure->SigVerify.iSigSize = pSecure->RSAContext.iKeyModSize; + ds_memcpy_s(pSecure->SigVerify.aSigData, sizeof(pSecure->SigVerify.aSigData), pSecure->RSAContext.EncryptBlock, pSecure->SigVerify.iSigSize); + + // done + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (rsa sig encrypt) %dms (%d calls)\n", + pSecure->RSAContext.uCryptMsecs, pSecure->RSAContext.uNumExpCalls)); + pSecure->uTimer += pSecure->RSAContext.uCryptMsecs; + pSecure->bRSAContextInitialized = FALSE; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateSignatureAsync + + \Description + Generate a digital signature; expected to be called iteratively + by the async update function. + + \Input *pState - module state + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 03/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateSignatureAsync(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + SignatureVerifyT *pSigVerify = &pSecure->SigVerify; + int32_t iResult; + + if ((pSigVerify->pSigScheme != NULL) && (pSigVerify->pSigScheme->SigAlg.uSigAlg == SSL3_SIGALG_ECDSA)) + { + iResult = _ProtoSSLGenerateSignatureEcdsa(pSecure, pSigVerify->aSigData, pSigVerify->iSigSize, pSigVerify->aKeyData, pSigVerify->iKeySize, (pSigVerify->iKeySize == 32) ? ASN_OBJ_ECDSA_SECP256R1 : ASN_OBJ_ECDSA_SECP384R1); + } + else + { + iResult = _ProtoSSLGenerateSignatureRSA(pSecure, pSigVerify->aSigData, pSigVerify->iSigSize, pSigVerify->pPrivateKey); + } + if (iResult <= 0) + { + if (iResult < 0) + { + NetPrintf(("protossl: %s signature generate failed\n", pSigVerify->pSigScheme->SigAlg.uSigAlg == SSL3_SIGALG_ECDSA ? "ecdsa" : "rsa")); + } + pSigVerify->pSigScheme = NULL; + pSecure->bSigGenerated = TRUE; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateSignature + + \Description + Starts async operation to generate a digital signature + + \Input *pState - module state + \Input *pSecure - secure state + \Input *pSigScheme - signature scheme to use + \Input *pSigData - signature to sign + \Input iSigSize - signature size + \Input *pPrivateKey - private key + \Input iNextState - next state to transition to after async op + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 03/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateSignature(ProtoSSLRefT *pState, SecureStateT *pSecure, const SignatureSchemeT *pSigScheme, const uint8_t *pSigData, int32_t iSigSize, X509PrivateKeyT *pPrivateKey, int32_t iNextState) +{ + SignatureVerifyT *pSigVerify = &pSecure->SigVerify; + + pSigVerify->pSigScheme = pSigScheme; + ds_memcpy_s(pSigVerify->aSigData, sizeof(pSigVerify->aSigData), pSigData, iSigSize); + pSigVerify->iSigSize = iSigSize; + pSigVerify->pPrivateKey = pPrivateKey; + pSigVerify->iNextState = iNextState; + pSigVerify->bEccContextInitialized = FALSE; + if (pSigScheme->SigAlg.uSigAlg == SSL3_SIGALG_ECDSA) + { + pSigVerify->iKeySize = pPrivateKey->Modulus.iObjSize; + ds_memcpy_s(pSigVerify->aKeyData, sizeof(pSigVerify->aKeyData), pPrivateKey->Modulus.pObjData, pSigVerify->iKeySize); + } + + return(_ProtoSSLUpdateSetAsyncState(pState, _ProtoSSLGenerateSignatureAsync, iNextState, ST_FAIL_SETUP, SSL3_ALERT_DESC_INTERNAL_ERROR)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLVerifySignatureEcdsa + + \Description + Verify an ECDSA signature; expected to be called iteratively + + \Input *pSecure - secure state + \Input *pSigData - signature data + \Input iSigSize - signature data size + \Input *pHashData - hash to validate + \Input iHashSize - hash size + \Input *pPubKey - ECC public key data + \Input iPubKeySize - size of key + \Input iCrvType - curve type + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 02/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLVerifySignatureEcdsa(SecureStateT *pSecure, const uint8_t *pSigData, int32_t iSigSize, const uint8_t *pHashData, int32_t iHashSize, const uint8_t *pPubKey, int32_t iPubKeySize, int32_t iCrvType) +{ + CryptEccPointT PubKey, Signature; + const uint8_t *pData = pSigData, *pLast = pSigData+iSigSize; + SignatureVerifyT *pSigVerify = &pSecure->SigVerify; + const CryptCurveDsaT *pDsa; + uint8_t aSigData[128]; + int32_t iSize, iResult; + uint32_t uCryptUsecs; + + // get the dsa functions + if ((pDsa = _ProtoSSLDsaInitContext(pSigVerify, iCrvType)) == NULL) + { + return(-1); + } + // if the state is not initialized then assume a failure + if (!pSigVerify->bEccContextInitialized) + { + return(-1); + } + + // init public key + pDsa->PointInit(&PubKey, pPubKey, iPubKeySize); + + // parse the sequence + if ((pData = _AsnParseHeaderType(pData, pLast, ASN_TYPE_SEQN+ASN_CONSTRUCT, &iSize)) == NULL) + { + return(-1); + } + // parse and set the first component + if ((pData = _AsnParseBinary(pData, pLast, ASN_TYPE_INTEGER+ASN_PRIMITIVE, aSigData, sizeof(aSigData), &iSize, "signature data x")) == NULL) + { + return(-1); + } + CryptBnInitFrom(&Signature.X, -1, aSigData, iSize); + // parse and set the second component + if ((pData = _AsnParseBinary(pData, pLast, ASN_TYPE_INTEGER+ASN_PRIMITIVE, aSigData, sizeof(aSigData), &iSize, "signature data y")) == NULL) + { + return(-1); + } + CryptBnInitFrom(&Signature.Y, -1, aSigData, iSize); + + // do the verify + if ((iResult = pDsa->Verify(pSigVerify->DsaContext, &PubKey, pHashData, iHashSize, &Signature, &uCryptUsecs)) != 1) + { + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (ecdsa sig verify) %dms\n", uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + pSigVerify->bEccContextInitialized = FALSE; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLVerifySignatureRSA + + \Description + Verify an RSA signature; expected to be called iteratively + + \Input *pSecure - secure state + \Input *pSigData - signature data + \Input iSigSize - signature data size + \Input iSigType - signature type + \Input *pHashData - hash to validate + \Input iHashSize - hash size + \Input eHashType - hash type + \Input iSigSalt - signature salt length + \Input eMgfHash - signature message generation function (mgf) + \Input *pKeyModData - rsa public key modulus + \Input iKeyModSize - key modulus length + \Input *pKeyExpData - rsa public key exponent + \Input iKeyExpSize - key exponent length + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 07/22/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLVerifySignatureRSA(SecureStateT *pSecure, const uint8_t *pSigData, int32_t iSigSize, int32_t iSigType, const uint8_t *pHashData, int32_t iHashSize, CryptHashTypeE eHashType, int32_t iSigSalt, CryptHashTypeE eMgfHash, const uint8_t *pKeyModData, int32_t iKeyModSize, const uint8_t *pKeyExpData, int32_t iKeyExpSize) +{ + int32_t iResult; + + // set up for rsa encryption + if (!pSecure->bRSAContextInitialized) + { + if (CryptRSAInit(&pSecure->RSAContext, pKeyModData, iKeyModSize, pKeyExpData, iKeyExpSize)) + { + return(-1); + } + CryptRSAInitSignature(&pSecure->RSAContext, pSigData, iSigSize); + pSecure->bRSAContextInitialized = TRUE; + } + // do a round of encryption + if ((iResult = CryptRSAEncrypt(&pSecure->RSAContext, 16)) > 0) + { + return(iResult); + } + pSecure->bRSAContextInitialized = FALSE; + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (rsa sig verify) %dms\n", pSecure->RSAContext.uCryptMsecs)); + pSecure->uTimer += pSecure->RSAContext.uCryptMsecs; + + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->RSAContext.EncryptBlock, pSecure->RSAContext.iKeyModSize, "decrypted signature"); + #endif + + // validate decrypted signature + if (iSigType != ASN_OBJ_RSASSA_PSS) + { + iResult = -1; + // extract hash data from signature block + if ((pSigData = _Pkcs1VerifyEMSA(pSecure->RSAContext.EncryptBlock, iSigSize, iHashSize, eHashType)) != NULL) + { + // compare hash data with precalculated certificate body hash + iResult = !memcmp(pHashData, pSigData, iHashSize) ? 0 : -1; + } + } + else + { + // extract and validate signature hash + iResult = _Pkcs1VerifyEMSA_PSS(pSecure->RSAContext.EncryptBlock, iKeyModSize, eHashType, iSigSalt, pHashData, iHashSize, eMgfHash) ? 0 : -1; + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLVerifySignatureAsync + + \Description + Verify a digital signature; expected to be called iteratively + by the async update function. + + \Input *pState - module state + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 02/16/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLVerifySignatureAsync(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + SignatureVerifyT *pSigVerify = &pSecure->SigVerify; + int32_t iResult; + + if ((pSigVerify->pSigScheme != NULL) && (pSigVerify->pSigScheme->SigAlg.uSigAlg == SSL3_SIGALG_ECDSA)) + { + iResult = _ProtoSSLVerifySignatureEcdsa(pSecure, pSigVerify->aSigData, pSigVerify->iSigSize, pSigVerify->aHashDigest, pSigVerify->iHashSize, pSecure->Cert.KeyModData, pSecure->Cert.iKeyModSize, pSecure->Cert.iCrvType); + } + else + { + iResult = _ProtoSSLVerifySignatureRSA(pSecure, pSigVerify->aSigData, pSigVerify->iSigSize, (pSigVerify->pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PSS) ? ASN_OBJ_RSASSA_PSS : ASN_OBJ_RSA_PKCS_SHA256, + pSigVerify->aHashDigest, pSigVerify->iHashSize, pSigVerify->eHashType, pSigVerify->iHashSize, pSigVerify->eHashType, pSecure->Cert.KeyModData, pSecure->Cert.iKeyModSize, pSecure->Cert.KeyExpData, + pSecure->Cert.iKeyExpSize); + } + if (iResult <= 0) + { + if (iResult < 0) + { + NetPrintf(("protossl: %s signature verify failed\n", pSigVerify->pSigScheme->SigAlg.uSigAlg == SSL3_SIGALG_ECDSA ? "ecdsa" : "rsa")); + } + pSigVerify->pSigScheme = NULL; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLVerifySignature + + \Description + Starts async operation to verify a digital signature + + \Input *pState - module state + \Input *pSecure - secure state + \Input *pSigScheme - signature scheme to use + \Input *pSigData - signature data + \Input iSigSize - signature data size + \Input *pHashData - hash to validate + \Input iHashSize - hash size + \Input eHashType - hash type + \Input iNextState - next state to transition to after async op + + \Output + int32_t - one=call again, zero=success, negative=failure + + \Version 02/16/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLVerifySignature(ProtoSSLRefT *pState, SecureStateT *pSecure, const SignatureSchemeT *pSigScheme, const uint8_t *pSigData, int32_t iSigSize, const uint8_t *pHashData, int32_t iHashSize, CryptHashTypeE eHashType, int32_t iNextState) +{ + SignatureVerifyT *pSigVerify = &pSecure->SigVerify; + + pSigVerify->pSigScheme = pSigScheme; + ds_memcpy_s(pSigVerify->aSigData, sizeof(pSigVerify->aSigData), pSigData, iSigSize); + pSigVerify->iSigSize = iSigSize; + ds_memcpy_s(pSigVerify->aHashDigest, sizeof(pSigVerify->aHashDigest), pHashData, iHashSize); + pSigVerify->iHashSize = iHashSize; + pSigVerify->eHashType = eHashType; + pSigVerify->iNextState = iNextState; + pSigVerify->bEccContextInitialized = FALSE; + + return(_ProtoSSLUpdateSetAsyncState(pState, _ProtoSSLVerifySignatureAsync, iNextState, ST_FAIL_SETUP, SSL3_ALERT_DESC_DECRYPT_ERROR)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLVerifyCertificateSignature + + \Description + Check an X.509 signature for validity; BLOCKING + + \Input *pSecure - secure state + \Input *pCert - pointer to certificate to validate + \Input *pKeyModData - pointer to key modulus data + \Input iKeyModSize - size of key modulus data + \Input *pKeyExpData - pointer to key exponent data + \Input iKeyExpSize - size of key exponent data + \Input iKeyType - key type + \Input iCrvType - key curve type, if ec + + \Output + int32_t - zero=success, else failure + + \Version 07/22/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLVerifyCertificateSignature(SecureStateT *pSecure, X509CertificateT *pCert, const uint8_t *pKeyModData, int32_t iKeyModSize, const uint8_t *pKeyExpData, int32_t iKeyExpSize, int32_t iKeyType, int32_t iCrvType) +{ + int32_t iResult; + + NetPrintfVerbose((DEBUG_VAL_CERT, 0, "protossl: verifying signature\n")); + + // if ecdsa + if (iKeyType == ASN_OBJ_ECDSA_KEY) + { + // verify the signature + while((iResult = _ProtoSSLVerifySignatureEcdsa(pSecure, pCert->SigData, pCert->iSigSize, pCert->HashData, pCert->iHashSize, pKeyModData, iKeyModSize, iCrvType)) > 0) + ; + } + else + { + while((iResult = _ProtoSSLVerifySignatureRSA(pSecure, pCert->SigData, pCert->iSigSize, pCert->iSigType, pCert->HashData, pCert->iHashSize, pCert->eHashType, pCert->iSigSalt, + _AsnGetHashType(pCert->iMgfHash), pKeyModData, iKeyModSize, pKeyExpData, iKeyExpSize)) > 0) + ; + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLVerifyCertificate + + \Description + Verify a previously parsed x.509 certificate + + \Input *pState - module state (may be NULL) + \Input *pSecure - secure state + \Input *pCert - pointer to certificate to fill in from header data + \Input bCertIsCA - is the certificate a CA? + + \Output int32_t - zero=success, positive=duplicate, else error + + \Version 03/11/2009 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLVerifyCertificate(ProtoSSLRefT *pState, SecureStateT *pSecure, X509CertificateT *pCert, uint8_t bCertIsCA) +{ + int32_t iResult = 0; + + // if self-signed permitted and certificate is self-signed, then point to its key info (mod+exp) + if ((bCertIsCA == TRUE) && (_CertificateCompareIdent(&pCert->Subject, &pCert->Issuer, TRUE) == 0)) + { + if (_ProtoSSLVerifyCertificateSignature(pSecure, pCert, pCert->KeyModData, pCert->iKeyModSize, pCert->KeyExpData, pCert->iKeyExpSize, pCert->iKeyType, pCert->iCrvType) != 0) + { + NetPrintf(("protossl: _VerifyCertificate: signature hash mismatch on self-signed cert\n")); + _CertificateDebugPrint(pCert, "failed cert"); + return(-50); + } + } + else + { + ProtoSSLCACertT *pCACert; + + #if DIRTYCODE_LOGGING + if ((bCertIsCA == TRUE) && (pCert->iCertIsCA == FALSE)) // when ProtoSSLSetCACert* is called + { + NetPrintf(("protossl: _VerifyCertificate: warning --- trying to add a non-CA cert as a CA.\n")); + _CertificateDebugPrint(pCert, "warning cert"); + } + #endif + + // locate a matching CA + for (pCACert = _ProtoSSL_CACerts; pCACert != NULL; pCACert = pCACert->pNext) + { + // first, compare to see if our subject matches their issuer + if ((_CertificateCompareIdent(&pCACert->Subject, &pCert->Issuer, (bCertIsCA||pCert->iCertIsCA)) != 0) || + ((pCACert->iKeyModSize != pCert->iSigSize) && (pCert->iKeyType != ASN_OBJ_ECDSA_KEY))) + { + continue; + } + + // verify against this CA + if (_ProtoSSLVerifyCertificateSignature(pSecure, pCert, pCACert->pKeyModData, pCACert->iKeyModSize, pCACert->KeyExpData, pCACert->iKeyExpSize, pCACert->iKeyType, pCACert->iCrvType) != 0) + { + continue; + } + + NetPrintfVerbose((DEBUG_VAL_CERT, 0, "protossl: verifying succeeded\n")); + + // special processing when validating against a GOS CA + if (pCACert->uFlags & SSL_CACERTFLAG_GOSCA) + { + // make sure the domain of the certificate is an EA domain + if (ds_stricmpwc(pCert->Subject.strCommon, "*.ea.com") && ds_stricmpwc(pCert->Subject.strCommon, "*.easports.com")) + { + return(SSL_ERR_GOSCA_INVALIDUSE); + } + // ignore date range validation failure + if (pSecure->bDateVerifyFailed) + { + NetPrintf(("protossl: ignoring date range validation failure for GOS CA\n")); + pSecure->bDateVerifyFailed = FALSE; + } + } + // only CAPROVIDER CAs can be used against gosca + if (!ds_stricmpwc(pCert->Subject.strCommon, "gosca.ea.com") && !(pCACert->uFlags & SSL_CACERTFLAG_CAPROVIDER)) + { + return(SSL_ERR_CAPROVIDER_INVALID); + } + + // if the CA hasn't been verified already, do that now + if (pCACert->pX509Cert != NULL) + { + if ((iResult = _ProtoSSLVerifyCertificate(pState, pSecure, pCACert->pX509Cert, TRUE)) == 0) + { + #if DIRTYCODE_LOGGING + char strIdentSubject[512], strIdentIssuer[512]; + int32_t iVerbose = (pState != NULL) ? pState->iVerbose : 0; + NetPrintfVerbose((iVerbose, 0, "protossl: ca (%s) validated by ca (%s)\n", _CertificateDebugFormatIdent(&pCACert->pX509Cert->Subject, strIdentSubject, sizeof(strIdentSubject)), + _CertificateDebugFormatIdent(&pCACert->pX509Cert->Issuer, strIdentIssuer, sizeof(strIdentIssuer)))); + #endif + + // cert successfully verified + DirtyMemFree(pCACert->pX509Cert, PROTOSSL_MEMID, pCACert->iMemGroup, pCACert->pMemGroupUserData); + pCACert->pX509Cert = NULL; + } + else + { + _CertificateSetFailureInfo(pState, pCACert->pX509Cert, TRUE); + continue; + } + } + + // verified + break; + } + if (pCACert == NULL) + { + #if DIRTYCODE_LOGGING + // output debug logging only if we're manually loading a CA cert + if (bCertIsCA) + { + _CertificateDebugPrint(pCert, "_VerifyCertificate() -- no CA available for this certificate"); + } + #endif + _CertificateSetFailureInfo(pState, pCert, TRUE); + return(-51); + } + } + // success + return(iResult); +} + +/* + hmac, building tls1 keys and key material, generating and verifying mac +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLDoHmac + + \Description + Calculates HMAC using CryptHmac + + \Input *pBuffer - [out] storage for generated MAC + \Input iBufLen - size of output + \Input *pMessage - input data to hash + \Input iMessageLen - size of input data + \Input *pMessage2 - more input data to hash (optional) + \Input iMessageLen2 - size of more input data (if pMessage2 != NULL) + \Input pKey - seed + \Input iKeyLen - seed length + \Input eHashType - hash type + + \Version 10/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLDoHmac(uint8_t *pBuffer, int32_t iBufLen, const uint8_t *pMessage, int32_t iMessageLen, const uint8_t *pMessage2, int32_t iMessageLen2, const uint8_t *pKey, int32_t iKeyLen, CryptHashTypeE eHashType) +{ + if (pMessage2 != NULL) + { + CryptHmacMsgT Message[2]; + Message[0].pMessage = pMessage; + Message[0].iMessageLen = iMessageLen; + Message[1].pMessage = pMessage2; + Message[1].iMessageLen = iMessageLen2; + CryptHmacCalcMulti(pBuffer, iBufLen, Message, 2, pKey, iKeyLen, eHashType); + } + else + { + CryptHmacCalc(pBuffer, iBufLen, pMessage, iMessageLen, pKey, iKeyLen, eHashType); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLDoPHash + + \Description + Implements P_hash as defined in https://tools.ietf.org/html/rfc5246#section-5 + + \Input *pBuffer - [out] storage for P_hash result + \Input iOutLen - size of output expected + \Input *pSecret - secret + \Input iSecretLen - length of secret + \Input *pSeed - seed + \Input iSeedLen - length of seed + \Input eHashType - hash type + + \Version 10/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLDoPHash(uint8_t *pBuffer, int32_t iOutLen, const uint8_t *pSecret, int32_t iSecretLen, const uint8_t *pSeed, int32_t iSeedLen, CryptHashTypeE eHashType) +{ + uint8_t aWork[128]; + int32_t iHashSize = CryptHashGetSize(eHashType); + + // A(1) + _ProtoSSLDoHmac(aWork, sizeof(aWork), pSeed, iSeedLen, NULL, 0, pSecret, iSecretLen, eHashType); + ds_memcpy(aWork+iHashSize, pSeed, iSeedLen); + _ProtoSSLDoHmac(pBuffer, iOutLen, aWork, iSeedLen+iHashSize, NULL, 0, pSecret, iSecretLen, eHashType); + + // A(n) + while (iOutLen > iHashSize) + { + uint8_t aWork2[128]; + + pBuffer += iHashSize; + iOutLen -= iHashSize; + + _ProtoSSLDoHmac(aWork2, sizeof(aWork2), aWork, iHashSize, NULL, 0, pSecret, iSecretLen, eHashType); + ds_memcpy(aWork, aWork2, iHashSize); + _ProtoSSLDoHmac(pBuffer, iOutLen, aWork, iSeedLen+iHashSize, NULL, 0, pSecret, iSecretLen, eHashType); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLDoPRF + + \Description + Implements PRF as defined in https://tools.ietf.org/html/rfc5246#section-5 + + \Input *pBuffer - [out] storage for P_hash result + \Input iOutLen - size of output expected + \Input *pSecret - secret + \Input iSecretLen - length of secret + \Input *pSeed - seed + \Input iSeedLen - length of seed + + \Version 10/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLDoPRF(uint8_t *pBuffer, int32_t iOutLen, const uint8_t *pSecret, int32_t iSecretLen, const uint8_t *pSeed, int32_t iSeedLen) +{ + uint8_t aMD5Buf[SSL_KEYMATERIAL_LEN], aSHABuf[SSL_KEYMATERIAL_LEN]; + int32_t iLS = iSecretLen/2; // split secret in two + int32_t iBufCnt; + iLS += iSecretLen & 1; // handle odd secret lengths + + // execute md5 p_hash + _ProtoSSLDoPHash(aMD5Buf, iOutLen, pSecret, iLS, pSeed, iSeedLen, CRYPTHASH_MD5); + + // execute sha1 p_hash + _ProtoSSLDoPHash(aSHABuf, iOutLen, pSecret+iLS, iLS, pSeed, iSeedLen, CRYPTHASH_SHA1); + + // execute XOR of MD5 and SHA hashes + for (iBufCnt = 0; iBufCnt < iOutLen; iBufCnt += 1) + { + pBuffer[iBufCnt] = aMD5Buf[iBufCnt] ^ aSHABuf[iBufCnt]; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLBuildKey + + \Description + Builds key material/master secret for TLS versions prior to 1.3 + + \Input *pState - module state reference + \Input *pBuffer - [out] output for generated key data + \Input iBufSize - size of output buffer + \Input *pSource - source data + \Input iSourceLen - length of source data + \Input *pRandomA - random data + \Input *pRandomB - random data + \Input iRandomLen - length of random data + \Input *pLabel - label (TLS1 PRF only) + \Input uSslVersion - ssl version being used + + \Version 10/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLBuildKey(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufSize, uint8_t *pSource, int32_t iSourceLen, uint8_t *pRandomA, uint8_t *pRandomB, int32_t iRandomLen, const char *pLabel, uint16_t uSslVersion) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t aWork[128]; // must hold at least 13+32+32 bytes + + ds_strnzcpy((char *)aWork, pLabel, sizeof(aWork)); + ds_memcpy(aWork+13, pRandomA, iRandomLen); + ds_memcpy(aWork+45, pRandomB, iRandomLen); + if (uSslVersion < SSL3_TLS1_2) + { + // ref http://tools.ietf.org/html/rfc2246#section-6.3 + _ProtoSSLDoPRF(pBuffer, iBufSize, pSource, iSourceLen, aWork, 77); + } + else + { + // ref https://tools.ietf.org/html/rfc5246#section-6.3; tls1.2 drops md5+sha1 prf and uses the cipher prf hash directly instead + _ProtoSSLDoPHash(pBuffer, iBufSize, pSource, iSourceLen, aWork, 77, (CryptHashTypeE)pSecure->pCipher->uPrfType); + } + + if (pBuffer == pSecure->MasterKey) + { + #if DIRTYCODE_LOGGING + char strBuf1[65], strBuf2[97]; + #endif + + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey), "PreMaster"); + NetPrintMem(pSecure->MasterKey, sizeof(pSecure->MasterKey), "Master"); + #endif + + // log params for wireshark decrypt + NetPrintfVerbose((pState->iVerbose, 0, "protossl: CLIENT_RANDOM %s %s\n", + ds_fmtoctstring(strBuf1, sizeof(strBuf1), pSecure->ClientRandom, sizeof(pSecure->ClientRandom)), + ds_fmtoctstring(strBuf2, sizeof(strBuf2), pBuffer, iBufSize))); + + // get rid of premaster secret from memory + ds_memclr(pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey)); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLBuildKeyMaterial + + \Description + Build and distribute key material from master secret, server + random, and client random. Used for TLS1.2 and prior. + + \Input *pState - module state reference + + \Version 03/19/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLBuildKeyMaterial(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t *pData; + + #if DEBUG_RAW_DATA + NetPrintf(("protossl: building key material\n")); + NetPrintMem(pSecure->MasterKey, sizeof(pSecure->MasterKey), "MasterKey"); + NetPrintMem(pSecure->ServerRandom, sizeof(pSecure->ServerRandom), "ServerRandom"); + NetPrintMem(pSecure->ClientRandom, sizeof(pSecure->ClientRandom), "ClientRandom"); + #endif + + // build key material; limit to SSL_KEYMATERIAL_LEN for upstream code even though the keyblock is bigger + _ProtoSSLBuildKey(pState, pSecure->KeyBlock, SSL_KEYMATERIAL_LEN, pSecure->MasterKey, sizeof(pSecure->MasterKey), + pSecure->ServerRandom, pSecure->ClientRandom, sizeof(pSecure->ServerRandom), "key expansion", + pSecure->uSslVersion); + + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->KeyBlock, sizeof(pSecure->KeyBlock), "KeyExpansion"); + #endif + + // distribute the key material + pData = pSecure->KeyBlock; + pSecure->pClientMAC = pData; + pData += pSecure->pCipher->uMac; + pSecure->pServerMAC = pData; + pData += pSecure->pCipher->uMac; + pSecure->pClientKey = pData; + pData += pSecure->pCipher->uLen; + pSecure->pServerKey = pData; + pData += pSecure->pCipher->uLen; + pSecure->pClientInitVec = pData; + pData += pSecure->pCipher->uVecSize; + pSecure->pServerInitVec = pData; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateMacSource + + \Description + Build the MAC (Message Authentication Code) source data + + \Input *pBuffer - [out] storage for generated MAC + \Input uSeqn - sequence number + \Input uType - message type + \Input uSslVers - ssl version + \Input uSize - data size + + \Output + uint8_t * - pointer past end of generated MAC + + \Version 10/11/2012 (jbrookes) Refactored and added TLS support +*/ +/********************************************************************************F*/ +static uint8_t *_ProtoSSLGenerateMacSource(uint8_t *pBuffer, uint64_t uSeqn, uint32_t uType, uint32_t uSslVers, uint32_t uSize) +{ + *pBuffer++ = (uint8_t)((uSeqn>>56)&255); + *pBuffer++ = (uint8_t)((uSeqn>>48)&255); + *pBuffer++ = (uint8_t)((uSeqn>>40)&255); + *pBuffer++ = (uint8_t)((uSeqn>>32)&255); + *pBuffer++ = (uint8_t)((uSeqn>>24)&255); + *pBuffer++ = (uint8_t)((uSeqn>>16)&255); + *pBuffer++ = (uint8_t)((uSeqn>>8)&255); + *pBuffer++ = (uint8_t)((uSeqn>>0)&255); + *pBuffer++ = (uint8_t)uType; + *pBuffer++ = (uint8_t)(uSslVers>>8); + *pBuffer++ = (uint8_t)(uSslVers>>0); + *pBuffer++ = (uint8_t)(uSize>>8); + *pBuffer++ = (uint8_t)(uSize>>0); + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateMac + + \Description + Generate MAC for secure send when using non-AEAD ciphers + + \Input *pSecure - secure state + \Input *pSend - data to authenticate + \Input iSize - size of data to authenticate + \Input bServer - TRUE if operating as server, else FALSE + + \Output + int32_t - size of authenticated data plus MAC + + \Version 03/02/2018 (jbrookes) Refactored from _ProtoSSLSendPacket() +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateMac(SecureStateT *pSecure, uint8_t *pSend, int32_t iSize, uint8_t bServer) +{ + uint8_t MacTemp[CRYPTHASH_MAXDIGEST], *pMacTemp; + + // generate the mac source + pMacTemp = _ProtoSSLGenerateMacSource(MacTemp, pSecure->uSendSeqn, pSecure->SendData[0], pSecure->uSslVersion, (uint32_t)iSize); + + // hash the mac and append to send data + _ProtoSSLDoHmac(pSend+iSize, pSecure->pCipher->uMac, MacTemp, (int32_t)(pMacTemp-MacTemp), pSend, iSize, + bServer ? pSecure->pServerMAC : pSecure->pClientMAC, + pSecure->pCipher->uMac, (CryptHashTypeE)pSecure->pCipher->uMacType); + + // return size+mac to caller + return(iSize+pSecure->pCipher->uMac); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLVerifyMac + + \Description + Verify MAC for secure recv when using non-AEAD ciphers + + \Input *pSecure - secure state + \Input iSize - size of data to authenticate + \Input bServer - TRUE if operating as server, else FALSE + \Input *pBadMac - [in/out] bad MAC flag + + \Output + int32_t - zero=success, negative=failure + + \Version 03/02/2018 (jbrookes) Refactored from _ProtoSSLSendPacket() +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLVerifyMac(SecureStateT *pSecure, int32_t iSize, uint8_t bServer, uint8_t *pBadMac) +{ + uint8_t MacTemp[CRYPTHASH_MAXDIGEST], *pMacTemp; + uint8_t bBadMac; + + // make sure there is room for mac + if (iSize >= (int32_t)pSecure->pCipher->uMac) + { + iSize -= pSecure->pCipher->uMac; + // remove the mac from size + pSecure->iRecvProg = pSecure->iRecvSize = pSecure->iRecvBase+iSize; + } + else + { + NetPrintf(("protossl: _ProtoSSLVerifyMac: no room for mac (%d < %d)\n", iSize, pSecure->pCipher->uMac)); + *pBadMac = TRUE; + return(-1); + } + + // generate MAC source + pMacTemp = _ProtoSSLGenerateMacSource(MacTemp, pSecure->uRecvSeqn, pSecure->RecvHead[0], pSecure->uSslVersion, (uint32_t)iSize); + + // do the hash + _ProtoSSLDoHmac(MacTemp, pSecure->pCipher->uMac, MacTemp, (int32_t)(pMacTemp-MacTemp), + pSecure->RecvData+pSecure->iRecvBase, pSecure->iRecvSize-pSecure->iRecvBase, + bServer ? pSecure->pClientMAC : pSecure->pServerMAC, + pSecure->pCipher->uMac, (CryptHashTypeE)pSecure->pCipher->uMacType); + + // validate MAC + bBadMac = memcmp(MacTemp, pSecure->RecvData+pSecure->iRecvSize, pSecure->pCipher->uMac) ? TRUE : FALSE; + // accumulate MAC flag + *pBadMac |= bBadMac; + // return success or failure to caller + return(!(*pBadMac) ? 0 : -1); +} + +/* + handshake hash management +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHandshakeHashInit + + \Description + Init handshake hash states + + \Input *pSecure - secure state ref + + \Version 03/21/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLHandshakeHashInit(SecureStateT *pSecure) +{ + CryptMD5Init(&pSecure->HandshakeMD5); + CryptSha1Init(&pSecure->HandshakeSHA); + CryptSha2Init(&pSecure->HandshakeSHA256, SSL3_MAC_SHA256); + CryptSha2Init(&pSecure->HandshakeSHA384, SSL3_MAC_SHA384); + CryptSha2Init(&pSecure->HandshakeSHA512, SSL3_MAC_SHA512); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHandshakeHashUpdate + + \Description + Update handshake hash with specified data + + \Input *pSecure - secure state ref + \Input *pData - data to add to hash + \Input iSize - size of data to add + \Input *pLabel - debug label + + \Version 03/21/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLHandshakeHashUpdate(SecureStateT *pSecure, const uint8_t *pData, int32_t iSize, const char *pLabel) +{ + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: %s handshake update (size=%d)\n", pLabel, iSize)); + CryptMD5Update(&pSecure->HandshakeMD5, pData, iSize); + CryptSha1Update(&pSecure->HandshakeSHA, pData, iSize); + CryptSha2Update(&pSecure->HandshakeSHA256, pData, iSize); + CryptSha2Update(&pSecure->HandshakeSHA384, pData, iSize); + CryptSha2Update(&pSecure->HandshakeSHA512, pData, iSize); + + #if DEBUG_RAW_DATA + NetPrintMem(pData, iSize, "handshake data"); + #endif +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHandshakeHashGet + + \Description + Get the current handshake hash, for the specified hash type + + \Input *pSecure - secure state ref + \Input eHashType - hash type to get + \Input *pBuffer - [out] buffer to store hash + \Input iBufSize - size of output buffer (should be bigger than hash size) + + \Output + int32_t - size of hash data, or zero on failure + + \Version 03/21/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHandshakeHashGet(SecureStateT *pSecure, CryptHashTypeE eHashType, uint8_t *pBuffer, int32_t iBufSize) +{ + CryptMD5T MD5Context; + CryptSha1T SHA1Context; + CryptSha2T SHA2Context; + int32_t iHashSize; + + if ((iHashSize = CryptHashGetSize(eHashType)) < 0) + { + NetPrintf(("protossl: handshake hash type %d unknown\n", eHashType)); + ds_memclr(pBuffer, iBufSize); + return(0); + } + else if (iHashSize > iBufSize) + { + NetPrintf(("protossl: handshake hash too large for buffer; truncating\n")); + iHashSize = iBufSize; + } + + switch (eHashType) + { + case CRYPTHASH_MD5: + ds_memcpy_s(&MD5Context, sizeof(MD5Context), &pSecure->HandshakeMD5, sizeof(pSecure->HandshakeMD5)); + CryptMD5Final(&MD5Context, pBuffer, iHashSize); + break; + case CRYPTHASH_SHA1: + ds_memcpy_s(&SHA1Context, sizeof(SHA1Context), &pSecure->HandshakeSHA, sizeof(pSecure->HandshakeSHA)); + CryptSha1Final(&SHA1Context, pBuffer, iHashSize); + break; + case CRYPTHASH_SHA256: + ds_memcpy(&SHA2Context, &pSecure->HandshakeSHA256, sizeof(pSecure->HandshakeSHA256)); + CryptSha2Final(&SHA2Context, pBuffer, iHashSize); + break; + case CRYPTHASH_SHA384: + ds_memcpy(&SHA2Context, &pSecure->HandshakeSHA384, sizeof(pSecure->HandshakeSHA384)); + CryptSha2Final(&SHA2Context, pBuffer, iHashSize); + break; + case CRYPTHASH_SHA512: + ds_memcpy(&SHA2Context, &pSecure->HandshakeSHA512, sizeof(pSecure->HandshakeSHA512)); + CryptSha2Final(&SHA2Context, pBuffer, iHashSize); + break; + default: + break; + } + + return(iHashSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHandshakeHashInject + + \Description + Inject synthetic message hash in Hello Retry Request as per + https://tools.ietf.org/html/rfc8446#section-4.4.1 + + \Input *pSecure - secure state ref + \Input eHashType - hash type to update + + \Version 08/10/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLHandshakeHashInject(SecureStateT *pSecure, CryptHashTypeE eHashType) +{ + uint8_t aHash[CRYPTHASH_MAXDIGEST], aHead[4]; + CryptSha2T *pHandshakeCtx; + int32_t iHashSize = CryptHashGetSize(eHashType); + + // get handshake context + pHandshakeCtx = (eHashType == CRYPTHASH_SHA256) ? &pSecure->HandshakeSHA256 : &pSecure->HandshakeSHA384; + + // get and reinit current hash + CryptSha2Final(pHandshakeCtx, aHash, iHashSize); + CryptSha2Init(pHandshakeCtx, iHashSize); + + // create synthetic message header + aHead[0] = SSL3_MSG_MESSAGE_HASH; + aHead[1] = 0; + aHead[2] = 0; + aHead[3] = (uint8_t)iHashSize; + + #if DEBUG_RAW_DATA + NetPrintMem(aHead, sizeof(aHead), "msghead"); + NetPrintMem(aHash, iHashSize, "msghash"); + #endif + + // hash the message header + CryptSha2Update(pHandshakeCtx, aHead, sizeof(aHead)); + // and the message hash + CryptSha2Update(pHandshakeCtx, aHash, iHashSize); +} + +/* + tls 1.3 key schedule +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHkdfExtract + + \Description + Implements HKDF-Extract as defined in rfc 5869; see + https://tools.ietf.org/html/rfc5869#section-2.2 + + \Input *pOutput - [out] output of extract operation + \Input iOutputLen - desired length of output + \Input *pSalt - salt input to extract + \Input iSaltLen - salt length + \Input *pKey - key input to extract + \Input iKeyLen - length of key + \Input eHashType - hash to use for extract + + \Version 03/08/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLHkdfExtract(uint8_t *pOutput, int32_t iOutputLen, const uint8_t *pSalt, int32_t iSaltLen, const uint8_t *pKey, int32_t iKeyLen, CryptHashTypeE eHashType) +{ + // the RFC defines this as HMAC-Hash(salt, key) but our CryptHmac params are reversed + CryptHmacCalc(pOutput, iOutputLen, pKey, iKeyLen, pSalt, iSaltLen, eHashType); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHkdfExpand + + \Description + Implements HKDF-Expand as defined in rfc 5869; see + https://tools.ietf.org/html/rfc5869#section-2.3 + + \Input *pOutput - [out] output of expand operation + \Input iOutputLen - desired length of output + \Input *pKey - key input to expand (prk) + \Input iKeyLen - length of key + \Input *pInfo - info input to expand + \Input iInfoLen - length of info + \Input eHashType - hash to use for expand + + \Version 03/08/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLHkdfExpand(uint8_t *pOutput, int32_t iOutputLen, const uint8_t *pKey, int32_t iKeyLen, const uint8_t *pInfo, int32_t iInfoLen, CryptHashTypeE eHashType) +{ + uint32_t uRound, uNumRounds; + int32_t iHashLen = CryptHashGetSize(eHashType); + uint8_t uPrev, *pPrev; + uint32_t uPrevLen; + CryptHmacMsgT Message[3]; + uint8_t uCount; + + // N = ceil(L/HashLen) + uNumRounds = (iOutputLen/ iHashLen); + uNumRounds += iOutputLen % iHashLen != 0 ? 1 : 0; + // T(0) = empty string (zero length) + uPrev = 0; + pPrev = &uPrev; + uPrevLen = 0; + // T(1) ... T(N) + for (uRound = 1; uRound <= uNumRounds; uRound += 1, pOutput += iHashLen, iOutputLen -= iHashLen) + { + uCount = (uint8_t)uRound; + + Message[0].pMessage = pPrev; + Message[0].iMessageLen = uPrevLen; + Message[1].pMessage = pInfo; + Message[1].iMessageLen = iInfoLen; + Message[2].pMessage = &uCount; + Message[2].iMessageLen = sizeof(uCount); + CryptHmacCalcMulti(pOutput, iOutputLen, Message, sizeof(Message)/sizeof(Message[0]), pKey, iKeyLen, eHashType); + + pPrev = pOutput; + uPrevLen = iHashLen; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHkdfExpandLabel + + \Description + Implements HKDF-Expand-Label as per + https://tools.ietf.org/html/rfc8446#section-7.1 + + \Input *pOutput - [out] output of expand operation + \Input iOutputLen - desired length of output + \Input *pKey - key input to expand (ikm) + \Input iKeyLen - length of key + \Input *pLabel - string context label + \Input *pHashValue - hash data + \Input iHashLen - hash data length + \Input eHashType - hash to use for label expand + + \Output + int32_t - length of output + + \Version 03/08/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHkdfExpandLabel(uint8_t *pOutput, int32_t iOutputLen, const uint8_t *pKey, int32_t iKeyLen, const char *pLabel, const uint8_t *pHashValue, int32_t iHashLen, CryptHashTypeE eHashType) +{ + const char *_strLabelPrefix = "tls13 "; + const int32_t iPrefixLen = (int32_t)strlen(_strLabelPrefix); + int32_t iLabelLen = (int32_t)strlen(pLabel); + uint8_t aInfoBuf[1024]; + int32_t iInfoLen = 0, iBufLen = sizeof(aInfoBuf); + + // HkdfLabel.length -- secret key length u16 + aInfoBuf[iInfoLen++] = (uint8_t)(iOutputLen >> 8); + aInfoBuf[iInfoLen++] = (uint8_t)(iOutputLen); + // HkdfLabelT.label -- length-prefixed string + aInfoBuf[iInfoLen++] = (uint8_t)(iLabelLen + iPrefixLen); + iInfoLen += ds_snzprintf((char *)aInfoBuf + iInfoLen, iBufLen - iInfoLen, "%s%s", _strLabelPrefix, pLabel); + // HkdfLabelT.hash_value -- hash data + aInfoBuf[iInfoLen++] = (uint8_t)iHashLen; + ds_memcpy_s(aInfoBuf + iInfoLen, iBufLen - iInfoLen, pHashValue, iHashLen); + iInfoLen += iHashLen; + + // do the expand + if (iInfoLen <= iBufLen) + { + _ProtoSSLHkdfExpand(pOutput, iOutputLen, pKey, iKeyLen, aInfoBuf, iInfoLen, eHashType); + #if DEBUG_RAW_DATA + NetPrintMem(pKey, iKeyLen, "key"); + NetPrintMem(aInfoBuf, iInfoLen, "info"); + NetPrintMem(pOutput, iOutputLen, "output"); + #endif + } + else + { + NetPrintf(("protossl: buffer too small in Hkdf expansion\n")); + } + // return size of output + return(iOutputLen); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLDeriveSecret + + \Description + Implements Derive-Secret as per + https://tools.ietf.org/html/rfc8446#section-7.1 + + \Input *pSecure - secure state + \Input *pOutput - [out] output of expand operation + \Input iOutputLen - desired length of output + \Input *pKey - key input to expand (ikm) + \Input iKeyLen - length of key + \Input *pLabel - string context label + \Input eHashType - hash to use for label expand + \Input iHashLen - hash data length (zero for empty-message hash) + \Input *pHashData - hash data to use (NULL to use current handshake hash) + + \Output + int32_t - output length of secret + + \Version 03/08/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLDeriveSecret(SecureStateT *pSecure, uint8_t *pOutput, int32_t iOutputLen, const uint8_t *pKey, int32_t iKeyLen, const char *pLabel, CryptHashTypeE eHashType, int32_t iHashLen, const uint8_t *pHashData) +{ + uint8_t aHashBuf[CRYPTHASH_MAXDIGEST]; + + // zero hash length indicates a request for an empty-message hash + if (iHashLen == 0) + { + const CryptHashT *pHash = CryptHashGet(eHashType); + uint8_t aHashState[CRYPTHASH_MAXSTATE]; + iHashLen = CryptHashGetSize(eHashType); + pHash->Init(aHashState, iHashLen); + pHash->Final(aHashState, aHashBuf, iHashLen); + pHashData = aHashBuf; + } + else if (pHashData == NULL) + { + // get handshake hash data + _ProtoSSLHandshakeHashGet(pSecure, eHashType, aHashBuf, sizeof(aHashBuf)); + pHashData = aHashBuf; + } + #if DEBUG_RAW_DATA + NetPrintMem(pHashData, iHashLen, "handshake hash"); + #endif + + // do the expand & return length of output + _ProtoSSLHkdfExpandLabel(pOutput, iOutputLen, pKey, iKeyLen, pLabel, pHashData, iHashLen, eHashType); + return(iOutputLen); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLBuildSecrets + + \Description + Builds early, handshake, and master secrets as per + https://tools.ietf.org/html/rfc8446#section-7.1 + + \Input *pSecure - secure state + \Input *pHandshakeSecret - [out] buffer to hold handshake secret + \Input *pMasterSecret - [out] buffer to hold master secret + \Input *pKey - ECDHE key used to build handshake/master secret + \Input iKeyLen - length of key + \Input eHashType - hash to use for label expand + \Input iHashLen - hash data length + + \Version 03/21/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLBuildSecrets(SecureStateT *pSecure, uint8_t *pHandshakeSecret, uint8_t *pMasterSecret, uint8_t *pKey, int32_t iKeyLen, CryptHashTypeE eHashType, int32_t iHashLen) +{ + uint8_t aZeroBuf[CRYPTHASH_MAXDIGEST], aWorkBuf[CRYPTHASH_MAXDIGEST], aWorkBuf2[CRYPTHASH_MAXDIGEST]; + + // create zero buf + ds_memclr(aZeroBuf, iHashLen); + + // HKDF-Extract(PSK|0, 0) (Early Secret) + _ProtoSSLHkdfExtract(aWorkBuf, iHashLen, aZeroBuf, iHashLen, pSecure->bSessionResume ? pSecure->PreSharedKey : aZeroBuf, iHashLen, eHashType); + #if DEBUG_RAW_DATA + NetPrintMem(aWorkBuf, iHashLen, "early secret"); + #endif + + // Derive-Secret(., "derived", "") + _ProtoSSLDeriveSecret(pSecure, aWorkBuf2, iHashLen, aWorkBuf, iHashLen, "derived", eHashType, 0, NULL); + // HKDF-Extract(salt, ECDHE) (Handshake Secret) + _ProtoSSLHkdfExtract(pHandshakeSecret, iHashLen, aWorkBuf2, iHashLen, pKey, iKeyLen, eHashType); + #if DEBUG_RAW_DATA + NetPrintMem(pHandshakeSecret, iHashLen, "handshake secret"); + #endif + + // Derive-Secret(., "derived", "") + _ProtoSSLDeriveSecret(pSecure, aWorkBuf2, iHashLen, pHandshakeSecret, iHashLen, "derived", eHashType, 0, NULL); + // HKDF-Extract(salt, 0) (Master Secret) + _ProtoSSLHkdfExtract(pMasterSecret, iHashLen, aWorkBuf2, iHashLen, aZeroBuf, iHashLen, eHashType); + #if DEBUG_RAW_DATA + NetPrintMem(pMasterSecret, iHashLen, "master secret"); + #endif +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLBuildHandshakeKey + + \Description + Builds TLS1.3 secrets and keys for handshake as per + https://tools.ietf.org/html/rfc8446#section-7.1 + + \Input *pState - protossl ref + + \Output + int32_t - result of curve secret function + + \Notes + Secrets, keys, and IVs are stored in the KeyBlock of the SecureStateT + as follows. When more than one version of an item exists, later + versions overwrite earlier versions in place: + + master_secret + + server_secret (early, handshake, application_traffic) + client_secret (early, handshake, application_traffic) + + server_write_key (handshake, application) + server_write_iv (handshake, application) + client_write_key (handshake, application) + client_write_iv (handshake, application) + + resumption_master_secret + + Currently, only EC(DHE) key exchange is supported. + + \Version 03/21/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLBuildHandshakeKey(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + const CryptCurveDhT *pEcc; + CryptHashTypeE eHashType = pSecure->pCipher->uPrfType; + int32_t iHashLen = CryptHashGetSize(eHashType); + uint8_t *pKeyBlock = pSecure->KeyBlock; + uint8_t aHandshakeSecret[CRYPTHASH_MAXDIGEST]; + uint8_t uZero = 0; + CryptEccPointT PublicKey; + int32_t iResult, iSecretSize = 0; + + // init context with key exchange private key, if we have not already done so + if ((pEcc = _ProtoSSLEccInitContext(pSecure, pSecure->pEllipticCurve)) != NULL) + { + uint32_t uCryptUsecs; + + // derive EC(DHE) key for handshake traffic encryption + pEcc->PointInit(&PublicKey, pSecure->PubKey, pSecure->uPubKeyLength); + if ((iResult = pEcc->Secret(&pSecure->EccContext, &PublicKey, NULL, &uCryptUsecs)) > 0) + { + return(iResult); + } + iSecretSize = pEcc->PointFinal(pSecure->EccContext, NULL, TRUE, pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey)); + // done with ECC state + pSecure->bEccContextInitialized = FALSE; + + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (generate key for traffic secret) %dms\n", uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + } + + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->PreMasterKey, iSecretSize, "ec(dhe) key"); + #endif + + // build main secrets used to derive keying secrets; we save the master secret for later generation of application key info + _ProtoSSLBuildSecrets(pSecure, aHandshakeSecret, pKeyBlock, pSecure->PreMasterKey, iSecretSize, eHashType, iHashLen); + pKeyBlock += iHashLen; + + // build server and client handshake secrets: https://tools.ietf.org/html/rfc8446#section-7.1 + + // build server handshake secret + pKeyBlock += _ProtoSSLDeriveSecret(pSecure, pSecure->pServerSecret = pKeyBlock, iHashLen, aHandshakeSecret, iHashLen, "s hs traffic", eHashType, iHashLen, NULL); + + // build client handshake secret + pKeyBlock += _ProtoSSLDeriveSecret(pSecure, pSecure->pClientSecret = pKeyBlock, iHashLen, aHandshakeSecret, iHashLen, "c hs traffic", eHashType, iHashLen, NULL); + + // build server and client handshake key material: https://tools.ietf.org/html/rfc8446#section-7.3 + + // [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) + pKeyBlock += _ProtoSSLHkdfExpandLabel(pSecure->pServerKey = pKeyBlock, pSecure->pCipher->uLen, pSecure->pServerSecret, iHashLen, "key", &uZero, 0, eHashType); + // [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) -- NOTE that iv_length of 12 is the full iv size for the cipher, as the iv is fully derived in TLS1.3 + pKeyBlock += _ProtoSSLHkdfExpandLabel(pSecure->pServerInitVec = pKeyBlock, 12, pSecure->pServerSecret, iHashLen, "iv", &uZero, 0, eHashType); + + // [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) + pKeyBlock += _ProtoSSLHkdfExpandLabel(pSecure->pClientKey = pKeyBlock, pSecure->pCipher->uLen, pSecure->pClientSecret, iHashLen, "key", &uZero, 0, eHashType); + // [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) -- see note above + pKeyBlock += _ProtoSSLHkdfExpandLabel(pSecure->pClientInitVec = pKeyBlock, 12, pSecure->pClientSecret, iHashLen, "iv", &uZero, 0, eHashType); + + // save pointer to buffer for resumption secret, derived later + pSecure->pResumeSecret = pKeyBlock; + + // log params for wireshark decrypt + #if DIRTYCODE_LOGGING + if (pState->iVerbose > 0) + { + char strBuf1[128], strBuf2[128]; + NetPrintf(("protossl: [%p] CLIENT_RANDOM %s %s\n", pState, ds_fmtoctstring(strBuf1, sizeof(strBuf1), pSecure->ClientRandom, sizeof(pSecure->ClientRandom)), + ds_fmtoctstring(strBuf2, sizeof(strBuf2), pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey)))); + NetPrintf(("protossl: [%p] CLIENT_HANDSHAKE_TRAFFIC_SECRET %s %s\n", pState, ds_fmtoctstring(strBuf1, sizeof(strBuf1), pSecure->ClientRandom, sizeof(pSecure->ClientRandom)), + ds_fmtoctstring(strBuf2, sizeof(strBuf2), pSecure->pClientSecret, iHashLen))); + NetPrintf(("protossl: [%p] SERVER_HANDSHAKE_TRAFFIC_SECRET %s %s\n", pState, ds_fmtoctstring(strBuf1, sizeof(strBuf1), pSecure->ClientRandom, sizeof(pSecure->ClientRandom)), + ds_fmtoctstring(strBuf2, sizeof(strBuf2), pSecure->pServerSecret, iHashLen))); + } + #endif + + // init secure state with handshake traffic keys + if (pSecure->pCipher->uEnc == SSL3_ENC_GCM) + { + CryptGcmInit(&pSecure->ReadGcm, pState->bServer ? pSecure->pClientKey : pSecure->pServerKey, pSecure->pCipher->uLen); + CryptGcmInit(&pSecure->WriteGcm, pState->bServer ? pSecure->pServerKey : pSecure->pClientKey, pSecure->pCipher->uLen); + } + else + { + CryptChaChaInit(&pSecure->ReadChaCha, pState->bServer ? pSecure->pClientKey : pSecure->pServerKey, pSecure->pCipher->uLen); + CryptChaChaInit(&pSecure->WriteChaCha, pState->bServer ? pSecure->pServerKey : pSecure->pClientKey, pSecure->pCipher->uLen); + } + // sending and receiving secure from this point + pSecure->bSendSecure = pSecure->bRecvSecure = TRUE; + pSecure->uSendSeqn = pSecure->uRecvSeqn = 0; + + // done + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLBuildApplicationKey + + \Description + Builds TLS1.3 secrets and keys for application data as per + https://tools.ietf.org/html/rfc8446#section-7.1 + + \Input *pState - protossl ref + \Input *pSecure - secure state + + \Notes + See _ProtoSSLBuildHandshakeKey notes for keyblock layout + + \Version 03/22/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLBuildApplicationKey(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + CryptHashTypeE eHashType = pSecure->pCipher->uPrfType; + int32_t iHashLen = CryptHashGetSize(eHashType); + uint8_t uZero = 0; + + // build server application traffic secret (note this overwrites the server handshake traffic secret) + _ProtoSSLDeriveSecret(pSecure, pSecure->pServerSecret, iHashLen, pSecure->KeyBlock, iHashLen, "s ap traffic", eHashType, iHashLen, pSecure->aFinishHash); + + // build client application traffic secret (note this overwrites the client handshake traffic secret) + _ProtoSSLDeriveSecret(pSecure, pSecure->pClientSecret, iHashLen, pSecure->KeyBlock, iHashLen, "c ap traffic", eHashType, iHashLen, pSecure->aFinishHash); + + // log params for wireshark decrypt + #if DIRTYCODE_LOGGING + if (pState->iVerbose > 0) + { + char strBuf1[128], strBuf2[128]; + NetPrintf(("protossl: [%p] CLIENT_TRAFFIC_SECRET_0 %s %s\n", pState, ds_fmtoctstring(strBuf1, sizeof(strBuf1), pSecure->ClientRandom, sizeof(pSecure->ClientRandom)), + ds_fmtoctstring(strBuf2, sizeof(strBuf2), pSecure->pClientSecret, iHashLen))); + NetPrintf(("protossl: [%p] SERVER_TRAFFIC_SECRET_0 %s %s\n", pState, ds_fmtoctstring(strBuf1, sizeof(strBuf1), pSecure->ClientRandom, sizeof(pSecure->ClientRandom)), + ds_fmtoctstring(strBuf2, sizeof(strBuf2), pSecure->pServerSecret, iHashLen))); + } + #endif + + // [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) + _ProtoSSLHkdfExpandLabel(pSecure->pServerKey, pSecure->pCipher->uLen, pSecure->pServerSecret, iHashLen, "key", &uZero, 0, eHashType); + // [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) -- NOTE that iv_length of 12 is the full iv size for the cipher, as the iv is fully derived in TLS1.3 + _ProtoSSLHkdfExpandLabel(pSecure->pServerInitVec, 12, pSecure->pServerSecret, iHashLen, "iv", &uZero, 0, eHashType); + + // [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) + _ProtoSSLHkdfExpandLabel(pSecure->pClientKey, pSecure->pCipher->uLen, pSecure->pClientSecret, iHashLen, "key", &uZero, 0, eHashType); + // [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length) -- see note above + _ProtoSSLHkdfExpandLabel(pSecure->pClientInitVec, 12, pSecure->pClientSecret, iHashLen, "iv", &uZero, 0, eHashType); + + // build resumption master secret + _ProtoSSLDeriveSecret(pSecure, pSecure->pResumeSecret, iHashLen, pSecure->KeyBlock, iHashLen, "res master", eHashType, iHashLen, NULL); + + #if DEBUG_RES_SESS + NetPrintMem(pSecure->KeyBlock, iHashLen, "master secret"); + NetPrintMem(pSecure->pResumeSecret, iHashLen, "resumption_master_secret"); + #endif + + // reinit secure state with application traffic keys + if (pSecure->pCipher->uEnc == SSL3_ENC_GCM) + { + CryptGcmInit(&pSecure->ReadGcm, pState->bServer ? pSecure->pClientKey : pSecure->pServerKey, pSecure->pCipher->uLen); + CryptGcmInit(&pSecure->WriteGcm, pState->bServer ? pSecure->pServerKey : pSecure->pClientKey, pSecure->pCipher->uLen); + } + else + { + CryptChaChaInit(&pSecure->ReadChaCha, pState->bServer ? pSecure->pClientKey : pSecure->pServerKey, pSecure->pCipher->uLen); + CryptChaChaInit(&pSecure->WriteChaCha, pState->bServer ? pSecure->pServerKey : pSecure->pClientKey, pSecure->pCipher->uLen); + } + // sending and receiving secure from this point + pSecure->bSendSecure = pSecure->bRecvSecure = TRUE; + pSecure->uSendSeqn = pSecure->uRecvSeqn = 0; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLCalcResumeBinder + + \Description + Calculate the psk binder as per + https://tools.ietf.org/html/rfc8446#section-4.2.11.2 + + \Input *pState - protossl ref + \Input *pBuffer - [out] buffer to store calculated binder + \Input iBufLen - size of output buffer + \Input *pSessTick - session ticket + \Input *pHshkMsgBuf - handshake message buffer + \Input iHshkMsgLen - length of handshake message + \Input iHashSize - binder hash size + + \Output + uint8_t * - pointer past end of binder + + \Version 12/19/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t *_ProtoSSLCalcResumeBinder(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen, const SessionTicketT *pSessTick, const uint8_t *pHshkMsgBuf, int32_t iHshkMsgLen, int32_t iHashSize) +{ + uint8_t aZeroBuf[CRYPTHASH_MAXDIGEST], aWorkBuf[CRYPTHASH_MAXDIGEST], aWorkBuf2[CRYPTHASH_MAXDIGEST], aBinderKey[CRYPTHASH_MAXDIGEST]; + uint8_t aHash[CRYPTHASH_MAXDIGEST], aHashCtx[CRYPTHASH_MAXSTATE]; + uint8_t aMsgHead[] = { SSL3_MSG_CLIENT_HELLO, 0, 0, 0 }; + CryptSha2T *pBinderCtx = (CryptSha2T *)aHashCtx; + SecureStateT *pSecure = pState->pSecure; + + // calculate buffer length - this includes the binders we haven't written out yet + iBufLen = iHshkMsgLen + iHashSize + 3; + // set in clienthello header + aMsgHead[2] = (uint8_t)(iBufLen>>8); + aMsgHead[3] = (uint8_t)(iBufLen>>0); + + // create zero buf + ds_memclr(aZeroBuf, iHashSize); + + // if we're in a HelloRetryRequest flow, we need to include the original [ClientHello...HelloRetryRequest] in the binder hash + if (pSecure->bHelloRetry) + { + // get handshake context + CryptSha2T *pHandshakeCtx = (pSessTick->eHashType == CRYPTHASH_SHA256) ? &pSecure->HandshakeSHA256 : &pSecure->HandshakeSHA384; + // copy to binder hash + ds_memcpy_s(pBinderCtx, sizeof(*pBinderCtx), pHandshakeCtx, sizeof(*pHandshakeCtx)); + } + else + { + // init binder hash + CryptSha2Init(pBinderCtx, iHashSize); + } + // hash the ClientHello message header + CryptSha2Update(pBinderCtx, aMsgHead, sizeof(aMsgHead)); + // hash the ClientHello message body up to binders list + CryptSha2Update(pBinderCtx, pHshkMsgBuf, iHshkMsgLen); + // get binder hash + CryptSha2Final(pBinderCtx, aHash, iHashSize); + + #if DEBUG_RES_SESS + #if DEBUG_RAW_DATA + NetPrintMem(aMsgHead, sizeof(aMsgHead), "message head"); + NetPrintMem(pHshkMsgBuf, iHshkMsgLen, "message body"); + #endif + NetPrintMem(aHash, iHashSize, "binder hash"); + #endif + + // calculate the binder + + // resumption_key = HKDF-Expand-Label(resumption_key, "resumption", ticket_nonce, ticket_nonce.len) + _ProtoSSLHkdfExpandLabel(pSecure->PreSharedKey, iHashSize, pSessTick->aResumeKey, iHashSize, "resumption", pSessTick->aTicketNonce, pSessTick->uNonceLen, pSessTick->eHashType); + // HKDF-Extract(0, PSK) (Early Secret) + _ProtoSSLHkdfExtract(aWorkBuf, iHashSize, aZeroBuf, iHashSize, pSecure->PreSharedKey, iHashSize, pSessTick->eHashType); + // Derive-Secret(., "res binder", "") + _ProtoSSLDeriveSecret(pSecure, aWorkBuf2, iHashSize, aWorkBuf, iHashSize, "res binder", pSessTick->eHashType, 0, NULL); + // finished_key = HKDF-Expand-Label(binder_key, "finished", "", Hash.length) + _ProtoSSLHkdfExpandLabel(aBinderKey, iHashSize, aWorkBuf2, iHashSize, "finished", NULL, 0, pSessTick->eHashType); + // verify_data = HMAC(finished_key, Hash(ClientHello*) + _ProtoSSLHkdfExtract(pBuffer, iHashSize, aBinderKey, iHashSize, aHash, iHashSize, pSessTick->eHashType); + + #if DEBUG_RES_SESS + NetPrintMem(pSecure->PreSharedKey, iHashSize, "PSK"); + NetPrintMem(aWorkBuf, iHashSize, "early_secret"); + NetPrintMem(aWorkBuf2, iHashSize, "binder key"); + NetPrintMem(aBinderKey, iHashSize, "finished key"); + NetPrintMem(pBuffer, iHashSize, "verify_data"); + #endif + + // return pointer past end of binder + return(pBuffer+iHashSize); +} + +/* + encryption and decryption +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAesEncrypt + + \Description + Encrypt data with AES + + \Input *pSecure - secure state + \Input *pSend - data to encrypt + \Input iSize - size of data to encrypt + + \Output + int32_t - size of encrypted data + + \Version 01/14/2018 (jbrookes) Refactored from _ProtoSSLSendPacket() +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLAesEncrypt(SecureStateT *pSecure, uint8_t *pSend, int32_t iSize) +{ + int32_t iPadBytes; + + // calculate padding + if ((iPadBytes = 16 - (iSize % 16)) == 0) + { + iPadBytes = 16; + } + + // set the padding data + ds_memset(pSend+iSize, iPadBytes-1, iPadBytes); + iSize += iPadBytes; + + // fill in the explict IV for TLS1.1 + if (pSecure->uSslVersion >= SSL3_TLS1_1) + { + pSend -= 16; + CryptRandGet(pSend, 16); + #if DEBUG_RAW_DATA + NetPrintMem(pSend, 16, "explicit IV"); + #endif + iSize += 16; + } + + // do the encryption + CryptAesEncrypt(&pSecure->WriteAes, pSend, iSize); + + // return encrypted size to caller + return(iSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAesDecrypt + + \Description + Decrypt data with AES + + \Input *pSecure - secure state + \Input *pRecv - data to decrypt + \Input iSize - size of data to decrypt + \Input *pBadMac - [out] bad mac flag + + \Output + int32_t - size of decrypted data, or -1 on error + + \Version 01/14/2018 (jbrookes) Refactored from _RecvPacket() +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLAesDecrypt(SecureStateT *pSecure, uint8_t *pRecv, int32_t iSize, uint8_t *pBadMac) +{ + int32_t iPad, iPadBytes, iPadStart; + + // decrypt the data + CryptAesDecrypt(&pSecure->ReadAes, pRecv, iSize); + + // if TLS1.1 or greater, skip explicit IV + if ((pSecure->uSslVersion >= SSL3_TLS1_1) && (pSecure->iRecvSize >= 16)) + { + pSecure->iRecvBase += 16; + pRecv += 16; + iSize -= 16; + } + + // read number of pad bytes + iPadBytes = pRecv[iSize-1]; + + /* As per http://tools.ietf.org/html/rfc5246#section-6.2.3.2, padding may be up to 255 bytes + in length. Each uint8 in the padding data vector MUST be filled with the padding length + value, padding MUST be checked, and a padding error MUST result in a bad_record_mac + alert. To eliminate a possible timing attack, we note the error here but wait until + after the MAC is generated to report it. */ + for (iPad = 0, iPadStart = iSize-iPadBytes-1; iPad < iPadBytes; iPad += 1) + { + if (pRecv[iPadStart+iPad] != iPadBytes) + { + #if DIRTYCODE_LOGGING + if (!(*pBadMac)) + { + NetPrintf(("protossl: _ProtoSSLAesDecrypt bad padding (len=%d)\n", iPadBytes)); + NetPrintMem(pRecv+iSize-iPadBytes-1, iPadBytes, "_ProtoSSLAesDecrypt padding"); + } + #endif + *pBadMac = TRUE; + iSize = -1; + break; + } + } + + // remove pad if validation was successful + if (!(*pBadMac)) + { + iSize -= iPadBytes+1; + } + // return size to caller + return(iSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAeadGenerateNonce + + \Description + Generate AEAD Nonce (IV) + + \Input *pSecure - secure state + \Input *pBuffer - [out] storage for generated nonce + \Input uSeqn - sequence number + \Input *pSeqn - sequence pointer (NULL to use sequence number) + \Input *pInitVec - implicit initialization vector (IV) + + \Version 07/08/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLAeadGenerateNonce(SecureStateT *pSecure, uint8_t *pBuffer, uint64_t uSeqn, const uint8_t *pSeqn, const uint8_t *pInitVec) +{ + if ((pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) && (pSecure->pCipher->uEnc != SSL3_ENC_CHACHA)) + { + *pBuffer++ = *pInitVec++; + *pBuffer++ = *pInitVec++; + *pBuffer++ = *pInitVec++; + *pBuffer++ = *pInitVec++; + if (pSeqn == NULL) + { + *pBuffer++ = (uint8_t)((uSeqn>>56)&255); + *pBuffer++ = (uint8_t)((uSeqn>>48)&255); + *pBuffer++ = (uint8_t)((uSeqn>>40)&255); + *pBuffer++ = (uint8_t)((uSeqn>>32)&255); + *pBuffer++ = (uint8_t)((uSeqn>>24)&255); + *pBuffer++ = (uint8_t)((uSeqn>>16)&255); + *pBuffer++ = (uint8_t)((uSeqn>>8)&255); + *pBuffer++ = (uint8_t)((uSeqn>>0)&255); + } + else + { + ds_memcpy(pBuffer, pSeqn, 8); + } + } + else + { + /* tls1.3 (and chacha) nonce generated as per: https://tools.ietf.org/html/rfc8446#section-5.3; + the 64-bit record number is extended to iv_length, and xor'd with iv */ + uint32_t uCount; + pBuffer[ 0] = 0; + pBuffer[ 1] = 0; + pBuffer[ 2] = 0; + pBuffer[ 3] = 0; + pBuffer[ 4] = (uint8_t)((uSeqn>>56)&255); + pBuffer[ 5] = (uint8_t)((uSeqn>>48)&255); + pBuffer[ 6] = (uint8_t)((uSeqn>>40)&255); + pBuffer[ 7] = (uint8_t)((uSeqn>>32)&255); + pBuffer[ 8] = (uint8_t)((uSeqn>>24)&255); + pBuffer[ 9] = (uint8_t)((uSeqn>>16)&255); + pBuffer[10] = (uint8_t)((uSeqn>>8)&255); + pBuffer[11] = (uint8_t)((uSeqn>>0)&255); + for (uCount = 0; uCount < 12; uCount += 1) + { + pBuffer[uCount] ^= pInitVec[uCount]; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAeadEncrypt + + \Description + Encrypt data with an AEAD cipher + + \Input *pSecure - secure state + \Input *pSend - data to encrypt + \Input iSize - size of data to encrypt + \Input bServer - TRUE if server, else FALSE + + \Output + int32_t - size of encrypted data + + \Version 01/14/2018 (jbrookes) Refactored from _ProtoSSLSendPacket() +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLAeadEncrypt(SecureStateT *pSecure, uint8_t *pSend, int32_t iSize, uint8_t bServer) +{ + uint8_t AeadData[13], AeadNonce[12], AeadTag[16]; + int32_t iAeadDataSize; + + // change content type to application_data, append content type to output + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + uint8_t uContentType = pSecure->SendData[0]; + pSecure->SendData[0] = SSL3_REC_APPLICATION; + pSend[iSize++] = uContentType; + } + + // generate AEAD additional data + if (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + // generate aead data (13 bytes, matches TLS MAC) + _ProtoSSLGenerateMacSource(AeadData, pSecure->uSendSeqn, pSecure->SendData[0], pSecure->uSslVersion, (uint32_t)iSize); + iAeadDataSize = sizeof(AeadData); + } + else + { + /* as per https://tools.ietf.org/html/rfc8446#section-5.2, tls1.3 AEAD data is the record header. + we already have the first three bytes of the record header formatted in SendData but send length isn't written + yet, so we calculate it here as the input size plus the tag size */ + int32_t iSendSize = iSize + sizeof(AeadTag); + AeadData[0] = pSecure->SendData[0]; + AeadData[1] = pSecure->SendData[1]; + AeadData[2] = pSecure->SendData[2]; + AeadData[3] = (uint8_t)(iSendSize>>8); + AeadData[4] = (uint8_t)(iSendSize>>0); + iAeadDataSize = 5; + #if DEBUG_RAW_DATA + NetPrintMem(AeadData, iAeadDataSize, "AeadData-send"); + #endif + } + + // generate nonce + if ((pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) && (pSecure->pCipher->uEnc != SSL3_ENC_CHACHA)) + { + // generate nonce (4 bytes implicit salt from the IV; 8 bytes explicit, we use sequence) + _ProtoSSLAeadGenerateNonce(pSecure, AeadNonce, pSecure->uSendSeqn, NULL, bServer ? pSecure->pServerInitVec : pSecure->pClientInitVec); + // write explicit IV to output; this is not encrypted + ds_memcpy(pSend-8, AeadNonce+4, 8); + #if DEBUG_RAW_DATA + NetPrintMem(pSend-8, 8, "explicit IV"); + #endif + } + else + { + // generate tls1.3/chacha nonce + _ProtoSSLAeadGenerateNonce(pSecure, AeadNonce, pSecure->uSendSeqn, NULL, bServer ? pSecure->pServerInitVec : pSecure->pClientInitVec); + } + + // do the encryption + if (pSecure->pCipher->uEnc == SSL3_ENC_GCM) + { + iSize = CryptGcmEncrypt(&pSecure->WriteGcm, pSend, iSize, AeadNonce, sizeof(AeadNonce), AeadData, iAeadDataSize, AeadTag, sizeof(AeadTag)); + } + else + { + iSize = CryptChaChaEncrypt(&pSecure->WriteChaCha, pSend, iSize, AeadNonce, sizeof(AeadNonce), AeadData, iAeadDataSize, AeadTag, sizeof(AeadTag)); + } + + // append tag to output + ds_memcpy(pSend+iSize, AeadTag, sizeof(AeadTag)); + iSize += sizeof(AeadTag); + + // add explicit IV to size *after* encrypt+tag + if ((pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) && (pSecure->pCipher->uEnc != SSL3_ENC_CHACHA)) + { + iSize += 8; + } + + // return encrypted size to caller + return(iSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAeadDecrypt + + \Description + Decrypt data with an AEAD cipher + + \Input *pSecure - secure state + \Input *pRecv - data to decrypt + \Input iSize - size of data to decrypt + \Input bServer - TRUE if server, else FALSE + \Input *pBadMac - [out] bad mac flag + \Input *pAlert - [out] alert, on error + + \Output + int32_t - size of decrypted data, or -1 on error + + \Version 01/14/2018 (jbrookes) Refactored from _RecvPacket() +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLAeadDecrypt(SecureStateT *pSecure, uint8_t *pRecv, int32_t iSize, uint8_t bServer, uint8_t *pBadMac, int32_t *pAlert) +{ + uint8_t AeadData[13], AeadNonce[12]; + int32_t iAeadDataSize; + + // generate nonce + if ((pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) && (pSecure->pCipher->uEnc != SSL3_ENC_CHACHA)) + { + // generate nonce (4 bytes implicit salt from TLS IV; 8 bytes explicit from packet) + _ProtoSSLAeadGenerateNonce(pSecure, AeadNonce, 0, pRecv, bServer ? pSecure->pClientInitVec : pSecure->pServerInitVec); + + // skip explicit IV in packet data + pSecure->iRecvBase += 8; + pRecv += 8; + } + else + { + // generate tls1.3/chacha nonce + _ProtoSSLAeadGenerateNonce(pSecure, AeadNonce, pSecure->uRecvSeqn, NULL, bServer ? pSecure->pClientInitVec : pSecure->pServerInitVec); + } + + // remove authentication tag + pSecure->iRecvSize -= 16; + pSecure->iRecvProg = pSecure->iRecvSize; + + // recalculate size + iSize = pSecure->iRecvSize - pSecure->iRecvBase; + + // generate AEAD additional data + if (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + // generate aead data (13 bytes, matches TLS MAC) + _ProtoSSLGenerateMacSource(AeadData, pSecure->uRecvSeqn, pSecure->RecvHead[0], pSecure->uSslVersion, (uint32_t)iSize); + iAeadDataSize = sizeof(AeadData); + } + else + { + // as per https://tools.ietf.org/html/rfc8446#section-5.2, tls1.3 AEAD data is the record header + iAeadDataSize = sizeof(pSecure->RecvHead); + ds_memcpy_s(AeadData, sizeof(AeadData), pSecure->RecvHead, iAeadDataSize); + #if DEBUG_RAW_DATA + NetPrintMem(AeadData, iAeadDataSize, "AeadData-recv"); + #endif + } + + // decrypt data + if (pSecure->pCipher->uEnc == SSL3_ENC_GCM) + { + iSize = CryptGcmDecrypt(&pSecure->ReadGcm, pRecv, iSize, AeadNonce, sizeof(AeadNonce), AeadData, iAeadDataSize, pRecv+iSize, 16); + } + else + { + iSize = CryptChaChaDecrypt(&pSecure->ReadChaCha, pRecv, iSize, AeadNonce, sizeof(AeadNonce), AeadData, iAeadDataSize, pRecv+iSize, 16); + } + + /* as per http://tools.ietf.org/html/rfc5288#section-3: Implementations MUST send TLS Alert bad_record_mac for all types of + failures encountered in processing the AES-GCM algorithm; this gets handled later in the receive flow */ + if (iSize < 0) + { + NetPrintf(("protossl: aead decrypt of received data failed\n")); + *pBadMac = TRUE; + } + + // for tls1.3, handle padding and content-type extraction + if ((pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && !(*pBadMac)) + { + // handle record padding as per https://tools.ietf.org/html/rfc8446#section-5.4 + for ( ; (iSize > 0) && (pRecv[iSize-1] == 0); iSize -= 1) + ; + // read and overwrite content type + if (iSize > 0) + { + pSecure->iRecvSize = pSecure->iRecvProg = pSecure->iRecvBase+iSize-1; + pSecure->RecvHead[0] = pSecure->RecvData[pSecure->iRecvSize]; + } + else + { + /* Implementations MUST limit their scanning to the cleartext returned from the AEAD decryption. If a receiving implementation + does not find a non-zero octet in the cleartext, it MUST terminate the connection with an "unexpected_message" alert. */ + NetPrintf(("protossl: _ProtoSSLAeadDecrypt: no content-type included in message\n")); + *pAlert = SSL3_ALERT_DESC_UNEXPECTED_MESSAGE; + iSize = -1; + } + } + // return size to caller + return(iSize); +} + +/* + secure packet send and receive +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendSecure + + \Description + Send secure queued data + + \Input *pState - module state + \Input *pSecure - secure state + + \Output + int32_t - zero if nothing was sent, else one + + \Version 11/07/2013 (jbrookes) Refactored from _ProtoSSLUpdateSend() +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendSecure(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + int32_t iResult, iXfer = 0; + + // see if there is data to send + if (pSecure->iSendProg < pSecure->iSendSize) + { + // try to send + iResult = SocketSend(pState->pSock, (char *)pSecure->SendData+pSecure->iSendProg, pSecure->iSendSize-pSecure->iSendProg, 0); + if (iResult > 0) + { + pSecure->iSendProg += iResult; + iXfer = 1; + } + if (iResult < 0) + { + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + pState->iClosed = 1; + } + // see if the data can be cleared + if (pSecure->iSendProg == pSecure->iSendSize) + { + pSecure->iSendProg = pSecure->iSendSize = 0; + } + } + + // return if something was sent + return(iXfer); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendPacket + + \Description + Build an outgoing SSL packet. Accepts head/body buffers to allow easy + contruction of header / data in individual buffers (or set one to null + if data already combind). + + \Input *pState - ssl state ptr + \Input uType - record type + \Input *pHeadPtr - pointer to head buffer + \Input iHeadLen - length of head buffer + \Input *pBodyPtr - pointer to data to send + \Input iBodyLen - length of data to send + + \Output int32_t - -1=invalid length, zero=no error + + \Version 11/10/2005 gschaefer +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendPacket(ProtoSSLRefT *pState, uint8_t uType, const void *pHeadPtr, int32_t iHeadLen, const void *pBodyPtr, int32_t iBodyLen) +{ + SecureStateT *pSecure = pState->pSecure; + uint32_t uRecordVersion; + uint8_t *pSend; + int32_t iSize; + + // verify if the input buffer length is good + if ((iHeadLen + iBodyLen + SSL_SNDOVH_PACKET) > (signed)sizeof(pSecure->SendData)) + { + NetPrintf(("protossl: _ProtoSSLSendPacket: buffer overflow (iHeadLen=%d, iBodyLen=%d)\n", iHeadLen, iBodyLen)); + return(-1); + } + + // setup record frame + pSecure->SendData[0] = uType; + // get ssl version to embed in header; as per https://tools.ietf.org/html/rfc8446#section-5.1 the record layer version is frozen at 1.2 for TLS1.3+ + uRecordVersion = (pSecure->uSslVersion < SSL3_TLS1_3) ? pSecure->uSslVersion : SSL3_TLS1_2; + pSecure->SendData[1] = (uint8_t)(uRecordVersion>>8); + pSecure->SendData[2] = (uint8_t)(uRecordVersion>>0); + + // point to data area + pSend = pSecure->SendData+5; + iSize = 0; + + /* reserve space for explicit IV if we're TLS1.1 or greater and are using AES (block cipher). reserving + space here means we don't have to shuffle packet data around later. note that the IV is *not* included + in calculating the handshake data for the finish packet */ + if (pSecure->bSendSecure && (pSecure->pCipher != NULL) && (pSecure->pCipher->uEnc == SSL3_ENC_AES) && (pSecure->uSslVersion >= SSL3_TLS1_1)) + { + pSend += 16; + } + // reserve space for explicit IV for GCM (TLS1.2 and lower) + if (pSecure->bSendSecure && (pSecure->pCipher != NULL) && (pSecure->pCipher->uEnc == SSL3_ENC_GCM) && (pSecure->uSslVersion < SSL3_TLS1_3)) + { + pSend += 8; + } + + // copy over the head + ds_memcpy(pSend+iSize, pHeadPtr, iHeadLen); + iSize += iHeadLen; + + // copy over the body + ds_memcpy(pSend+iSize, pBodyPtr, iBodyLen); + iSize += iBodyLen; + + // hash handshake data for "finish" packet + if (uType == SSL3_REC_HANDSHAKE) + { + _ProtoSSLHandshakeHashUpdate(pSecure, pSend, iSize, "_ProtoSSLSendPacket"); + } + + // handle encryption + if (pSecure->bSendSecure && (pSecure->pCipher != NULL)) + { + // add MAC for non-AEAD ciphers + if (pSecure->pCipher->uMacType != CRYPTHASH_NULL) + { + iSize = _ProtoSSLGenerateMac(pSecure, pSend, iSize, pState->bServer); + } + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: _ProtoSSLSendPacket (secure enc=%d mac=%d): type=%d, size=%d, seq=%qd\n", + pSecure->pCipher->uEnc, pSecure->pCipher->uMac, pSecure->SendData[0], iSize, pSecure->uSendSeqn)); + #if (DEBUG_RAW_DATA > 1) + NetPrintMem(pSecure->SendData, iSize+5, "_ProtoSSLSendPacket"); + #endif + + // encrypt the data + if (pSecure->pCipher->uEnc == SSL3_ENC_AES) + { + iSize = _ProtoSSLAesEncrypt(pSecure, pSend, iSize); + } + if ((pSecure->pCipher->uEnc == SSL3_ENC_GCM) || (pSecure->pCipher->uEnc == SSL3_ENC_CHACHA)) + { + iSize = _ProtoSSLAeadEncrypt(pSecure, pSend, iSize, pState->bServer); + } + } + else + { + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: _ProtoSSLSendPacket (unsecure): type=%d, size=%d, seq=%qd\n", pSecure->SendData[0], iSize, pSecure->uSendSeqn)); + #if (DEBUG_RAW_DATA > 1) + NetPrintMem(pSecure->SendData, iSize+5, "_ProtoSSLSendPacket"); + #endif + } + + // setup total record size + pSecure->SendData[3] = (uint8_t)(iSize>>8); + pSecure->SendData[4] = (uint8_t)(iSize>>0); + + // setup buffer pointers + pSecure->iSendProg = 0; + pSecure->iSendSize = iSize+5; + + // increment the sequence + pSecure->uSendSeqn += 1; + return(0); +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvGetRecordTypeName + + \Description + Get debug ssl record type name + + \Input uRecordType - tls record type + + \Output + const char * - pointer to record type name, or "unknown" if not recognized + + \Version 11/15/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_ProtoSSLRecvGetRecordTypeName(uint32_t uRecordType) +{ + static const char *_ContentTypeNames[5] = { "ChangeCipherSpec", "Alert", "Handshake", "Application", "Heartbeat" }; + const char *pContentType = ((uRecordType >= 20) && (uRecordType <= 24)) ? _ContentTypeNames[uRecordType-20] : "unknown"; + return(pContentType); +} +#endif + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvGetHandshakeTypeName + + \Description + Get debug ssl handshake type name + + \Input uHandshakeType - tls record type + + \Output + const char * - pointer to handshake type name, or "unknown" if not recognized + + \Version 05/03/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_ProtoSSLRecvGetHandshakeTypeName(uint32_t uHandshakeType) +{ + static const char *_HandshakeTypeNames[] = + { + "hello_request", "client_hello", "server_hello", "hello_verify_request", + "new_session_ticket", "end_of_early_data", "hello_retry_request", "unassigned_7", + "encrypted_extensions", "unassigned_9", "unassigned_10", "certificate", + "server_key_exchange", "certificate_request", "server_hello_done", "certificate_verify", + "client_key_exchange", "unassigned_17", "unassigned_18", "unassigned_19", + "finished", "certificate_url", "certificate_status", "supplemental_data", + "key_update", + "message_hash" + }; + const char *pHandshakeType; + if (uHandshakeType <= SSL3_MSG_KEY_UPDATE) + { + pHandshakeType = _HandshakeTypeNames[uHandshakeType]; + } + else if (uHandshakeType == SSL3_MSG_MESSAGE_HASH) + { + pHandshakeType = _HandshakeTypeNames[SSL3_MSG_KEY_UPDATE+1]; + } + else + { + pHandshakeType = "unknown"; + } + return(pHandshakeType); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvReset + + \Description + Reset receive tracking; called when we're done processing a packet or + in an error condition + + \Input *pSecure - secure state + + \Version 11/14/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static inline void _ProtoSSLRecvReset(SecureStateT *pSecure) +{ + pSecure->iRecvHead = pSecure->iRecvProg = pSecure->iRecvSize = pSecure->iRecvBase = pSecure->iRecvHshkProg = 0; + pSecure->bRecvProc = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvPacket + + \Description + Decode ssl3 record header, decrypt data, verify mac. + + \Input *pState - ssl state ptr + \Input *pAlert - [out] alert, on error + + \Output + int32_t - 0 for success, negative for error + + \Version 11/10/2005 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvPacket(ProtoSSLRefT *pState, int32_t *pAlert) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iSize = pSecure->iRecvSize-pSecure->iRecvBase; + uint8_t bBadMac = FALSE; + + /* check the record type - as per http://tools.ietf.org/html/rfc5246#section-6, if a TLS implementation + receives an unexpected record type, it MUST send an unexpected_message alert */ + if ((pSecure->RecvHead[0] < SSL3_REC_CIPHER) || (pSecure->RecvHead[0] > SSL3_REC_APPLICATION)) + { + NetPrintf(("protossl: _RecvPacket: unknown record type (%s) %d\n", _ProtoSSLRecvGetRecordTypeName(pSecure->RecvHead[0]), pSecure->RecvHead[0])); + *pAlert = SSL3_ALERT_DESC_UNEXPECTED_MESSAGE; + return(-1); + } + + /* as per https://tools.ietf.org/html/rfc8446#section-5 check for change_cipher_spec record; if + we find one, we ignore it */ + if ((pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && (pSecure->RecvHead[0] == SSL3_REC_CIPHER)) + { + // validate ccs message + if ((iSize != 1) || (pSecure->RecvData[0] != 1)) + { + NetPrintf(("protossl: invalid change_cipher_spec message in tls1.3 flow\n")); + *pAlert = SSL3_ALERT_DESC_DECODE_ERROR; + return(-1); + } + else + { + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: skipping dummy change_cipher_spec message in tls1.3 flow\n")); + pSecure->iRecvHead = pSecure->iRecvProg = pSecure->iRecvSize = pSecure->iRecvBase = pSecure->iRecvHshkProg = 0; + return(0); + } + } + + // if we are reciving the finished packet, we need to transition to secure receiving + if (pState->iState == ST3_RECV_FINISH) + { + pSecure->bRecvSecure = TRUE; + } + + // handle decryption + if (pSecure->bRecvSecure && (pSecure->pCipher != NULL)) + { + // decrypt the data + if (pSecure->pCipher->uEnc == SSL3_ENC_AES) + { + iSize = _ProtoSSLAesDecrypt(pSecure, pSecure->RecvData+pSecure->iRecvBase, iSize, &bBadMac); + } + if ((pSecure->pCipher->uEnc == SSL3_ENC_GCM) || (pSecure->pCipher->uEnc == SSL3_ENC_CHACHA)) + { + iSize = _ProtoSSLAeadDecrypt(pSecure, pSecure->RecvData+pSecure->iRecvBase, iSize, pState->bServer, &bBadMac, pAlert); + } + + // validate MAC for non-AEAD ciphers + if (pSecure->pCipher->uMacType != CRYPTHASH_NULL) + { + iSize = _ProtoSSLVerifyMac(pSecure, iSize, pState->bServer, &bBadMac); + } + + // handle mac/decrypt errors + if (iSize < 0) + { + if (bBadMac) + { + NetPrintf(("protossl: _RecvPacket: bad MAC!\n")); + *pAlert = SSL3_ALERT_DESC_BAD_RECORD_MAC; + } + return(-1); + } + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: _RecvPacket (secure enc=%d mac=%d): type=%s, size=%d, seq=%qd\n", + pSecure->pCipher->uEnc, pSecure->pCipher->uMac, _ProtoSSLRecvGetRecordTypeName(pSecure->RecvHead[0]), iSize, pSecure->uRecvSeqn)); + #if (DEBUG_RAW_DATA > 1) + NetPrintMem(pSecure->RecvData, pSecure->iRecvSize, "_RecvPacket"); + #endif + } + else + { + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: _RecvPacket (unsecure): type=%s, size=%d, seq=%qd\n", _ProtoSSLRecvGetRecordTypeName(pSecure->RecvHead[0]), iSize, pSecure->uRecvSeqn)); + #if (DEBUG_RAW_DATA > 1) + NetPrintMem(pSecure->RecvData+pSecure->iRecvBase, iSize, "_RecvPacket"); + #endif + } + + // increment the sequence number + pSecure->uRecvSeqn += 1; + + /* check for empty packet; some implementations of SSL emit a zero-length frame followed immediately + by an n-length frame when TLS1.0 is employed with a CBC cipher like AES. this is done as a defense + against the BEAST attack. we need to detect and clear the empty packet here so the following code + doesn't use the old packet header with new data */ + if (pSecure->iRecvSize == 0) + { + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: detected empty packet\n")); + pSecure->iRecvHead = 0; + } + + // return success + return(0); +} + +/* + alerts +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGetAlert + + \Description + Get alert from table based on alert type + + \Input *pState - module state + \Input *pAlertDesc - [out] storage for alert description + \Input uAlertLevel - alert level + \Input uAlertType - alert type + + \Output + int32_t - 0=no alert, 1=alert + + \Version 10/31/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGetAlert(ProtoSSLRefT *pState, ProtoSSLAlertDescT *pAlertDesc, uint8_t uAlertLevel, uint8_t uAlertType) +{ + int32_t iErr, iResult = 0; + + // set up default alertdesc + pAlertDesc->iAlertType = uAlertType; + pAlertDesc->pAlertDesc = "unknown"; + + // find alert description + if (uAlertLevel != 0) + { + for (iErr = 0; _ProtoSSL_AlertList[iErr].iAlertType != -1; iErr += 1) + { + if (_ProtoSSL_AlertList[iErr].iAlertType == uAlertType) + { + pAlertDesc->pAlertDesc = _ProtoSSL_AlertList[iErr].pAlertDesc; + iResult = 1; + break; + } + } + } + return(iResult); +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _ProtoSSLDebugAlert + + \Description + Debug-only display of info following server alert + + \Input *pState - module state + \Input uAlertLevel - alert level + \Input uAlertType - alert type + + \Version 04/04/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLDebugAlert(ProtoSSLRefT *pState, uint8_t uAlertLevel, uint8_t uAlertType) +{ + ProtoSSLAlertDescT Alert; + _ProtoSSLGetAlert(pState, &Alert, uAlertLevel, uAlertType); + NetPrintf(("protossl: ALERT: level=%d, type=%d, name=%s\n", uAlertLevel, uAlertType, Alert.pAlertDesc)); +} +#else +#define _ProtoSSLDebugAlert(_pState, _uAlertLevel, _uAlertType) +#endif + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendAlert + + \Description + Send an alert + + \Input *pState - module state reference + \Input iLevel - alert level (1=warning, 2=error) + \Input iValue - alert value + + \Output + int32_t - current state + + \Version 11/06/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendAlert(ProtoSSLRefT *pState, int32_t iLevel, int32_t iValue) +{ + /* $$TODO: we can currently only send if the send state is empty; this should + be addressed so we can queue an alert for sending at any time */ + if ((pState->pSecure != NULL) && (pState->pSecure->iSendProg == 0) && (pState->pSecure->iSendSize == 0)) + { + uint8_t strHead[2]; + #if DIRTYCODE_LOGGING + ProtoSSLAlertDescT Alert; + _ProtoSSLGetAlert(pState, &Alert, iLevel, iValue); + #endif + pState->uAlertLevel = strHead[0] = (uint8_t)iLevel; + pState->uAlertValue = strHead[1] = (uint8_t)iValue; + pState->bAlertSent = TRUE; + NetPrintf(("protossl: sending alert: level=%d type=%s (%d)\n", iLevel, Alert.pAlertDesc, iValue)); + // stage the alert for sending + _ProtoSSLSendPacket(pState, SSL3_REC_ALERT, strHead, sizeof(strHead), NULL, 0); + // flush alert + _ProtoSSLSendSecure(pState, pState->pSecure); + /* As per http://tools.ietf.org/html/rfc5246#section-7.2.2, any error alert sent + or received must result in current session information being reset (no resume) */ + if (iLevel == SSL3_ALERT_LEVEL_FATAL) + { + _SessionHistoryInvalidate(pState->strHost, SockaddrInGetPort(&pState->PeerAddr)); + } + } + else + { + NetPrintf(("protossl: unable to send alert (pSecure=%p, iSendProg=%d, iSendSize=%d)\n", pState->pSecure, + pState->pSecure->iSendProg, pState->pSecure->iSendSize)); + } + + // return current state + return(pState->iState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvAlert + + \Description + Process an alert message + + \Input *pState - module state reference + \Input *pSecure - secure state reference + + \Version 11/14/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLRecvAlert(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + pState->uAlertLevel = pSecure->RecvData[pSecure->iRecvBase]; + pState->uAlertValue = pSecure->RecvData[pSecure->iRecvBase+1]; + pState->bAlertSent = FALSE; + + // process warnings + if (pState->uAlertLevel == SSL3_ALERT_LEVEL_WARNING) + { + if (pState->uAlertValue == SSL3_ALERT_DESC_CLOSE_NOTIFY) + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: received close notification\n")); + // only an error if we are still in setup + if (pState->iState < ST3_SECURE) + { + pState->iState = ST_FAIL_SETUP; + } + } + if (pState->uAlertValue == SSL3_ALERT_DESC_NO_RENEGOTIATION) + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: received no_renegotiation alert\n")); + pState->iState = (pState->iState == ST3_RECV_HELLO) ? ST3_SECURE : ST_FAIL_SECURE; + } + } + else // if not a warning, it is an error + { + _ProtoSSLDebugAlert(pState, pSecure->RecvData[pSecure->iRecvBase], pSecure->RecvData[pSecure->iRecvBase+1]); + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + } + + // consume the alert message + _ProtoSSLRecvReset(pSecure); + + // process if the alert resulted in a failure state + if (pState->iState >= ST_FAIL) + { + /* As per http://tools.ietf.org/html/rfc5246#section-7.2.2 any error alert sent or + received must result in current session information being reset (no resume) */ + _SessionHistoryInvalidate(pState->strHost, SockaddrInGetPort(&pState->PeerAddr)); + pState->iClosed = 1; + } +} + +/* + handshake utility functions +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateFingerprint + + \Description + Calculate fingerprint for given data + + \Input *pOutBuf - output buffer to store fingerprint + \Input iOutSize - size of output buffer + \Input *pInpData - input data to calculate fingerprint for + \Input iInpSize - size of input data + \Input eHashType - hash operation to use + + \Version 04/29/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLGenerateFingerprint(uint8_t *pOutBuf, int32_t iOutSize, const uint8_t *pInpData, int32_t iInpSize, CryptHashTypeE eHashType) +{ + const CryptHashT *pHash; + + // calculate the fingerprint using designated hash type + if ((pHash = CryptHashGet(eHashType)) != NULL) + { + uint8_t aHashState[CRYPTHASH_MAXSTATE]; + int32_t iHashSize = CryptHashGetSize(eHashType); + if (iHashSize > iOutSize) + { + iHashSize = iOutSize; + } + pHash->Init(aHashState, iHashSize); + pHash->Update(aHashState, pInpData, iInpSize); + pHash->Final(aHashState, pOutBuf, iHashSize); + #if DEBUG_VAL_CERT + NetPrintMem(pOutBuf, iHashSize, "sha1-fingerprint"); + #endif + } + else + { + NetPrintf(("protossl: _AsnParseCertificate: could not calculate fingerprint\n")); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateEccSharedSecret + + \Description + Generate the ECDHE shared secret if necessary + + \Input *pState - module state reference + + \Output + uint8_t - TRUE=operation complete, FALSE=operation ongoing + + \Version 03/07/2017 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _ProtoSSLGenerateEccSharedSecret(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + const CryptCurveDhT *pEcc; + CryptEccPointT PublicKey; + int32_t iSecretSize = 0; + uint32_t uCryptUsecs = 0; + + // only generate the secret if we are using an ECDHE cipher + if (pSecure->pCipher->uKey != SSL3_KEY_ECDHE) + { + return(TRUE); + } + + /* init context with key exchange private key, if we have not already done so. + if we fail to get the dh functions then our master secret will be bad which will + lead to handshake failure */ + if ((pEcc = _ProtoSSLEccInitContext(pSecure, pSecure->pEllipticCurve)) != NULL) + { + /* generate shared secret: elliptic curve generation takes multiple frames + so stay in the same state until the operation is complete */ + pEcc->PointInit(&PublicKey, pSecure->PubKey, pSecure->uPubKeyLength); + if (pEcc->Secret(&pSecure->EccContext, &PublicKey, NULL, &uCryptUsecs) > 0) + { + return(FALSE); + } + iSecretSize = pEcc->PointFinal(pSecure->EccContext, NULL, TRUE, pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey)); + } + + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (generate shared secret) %dms\n", + uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + + // build master secret + _ProtoSSLBuildKey(pState, pSecure->MasterKey, sizeof(pSecure->MasterKey), pSecure->PreMasterKey, iSecretSize, + pSecure->ClientRandom, pSecure->ServerRandom, sizeof(pSecure->ClientRandom), "master secret", + pSecure->uSslVersion); + + // finished with ecc context + pSecure->bEccContextInitialized = FALSE; + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateCertificateVerifyHash + + \Description + Generate a tls1.3 DigitalSignatureHash; see + https://tools.ietf.org/html/rfc8446#section-4.4.3 + + \Input *pBuffer - [out] storage for generated digitalsignaturehash object + \Input eHashType - signature algorithm hash type + \Input *pHashData - pointer to hash data to embed in the object + \Input iHashSize - size of hash data + \Input bServer - TRUE if server, else FALSE + \Input bSending - TRUE if sending, else receiving + + \Output + int32_t - size of generated hash + + \Version 05/18/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateCertificateVerifyHash(uint8_t *pBuffer, CryptHashTypeE eHashType, const uint8_t *pHashData, int32_t iHashSize, uint8_t bServer, uint8_t bSending) +{ + uint8_t aTempBuf[64 + 34 + CRYPTHASH_MAXDIGEST], *pTempBuf = aTempBuf; + uint8_t aHashState[CRYPTHASH_MAXSTATE]; + const CryptHashT *pHash = CryptHashGet(eHashType); + + // 64 bytes of spaces + ds_memset(pTempBuf, 32, 64); + pTempBuf += 64; + // context string + pTempBuf += ds_snzprintf((char *)pTempBuf, sizeof(aTempBuf) - 64, "TLS 1.3, %s CertificateVerify", ((bServer && bSending) || (!bServer && !bSending)) ? "server" : "client"); + // zero separator + *pTempBuf++ = '\0'; + // handshake hash + ds_memcpy(pTempBuf, pHashData, iHashSize); + pTempBuf += iHashSize; + + // hash the envelope with the signature algorithm hash (may not match handshake hash) + iHashSize = CryptHashGetSize(eHashType); + pHash->Init(aHashState, iHashSize); + pHash->Update(aHashState, aTempBuf, pTempBuf - aTempBuf); + pHash->Final(aHashState, pBuffer, iHashSize); + + // return hash size to caller + return(iHashSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGenerateFinishHash + + \Description + Generates finish hash data + + \Input *pBuffer - [out] storage for generated finish hash + \Input *pSecure - secure state + \Input *pLabelTLS - label to use for TLS1 finish hash calculation + \Input bServer - TRUE if we're the server, else client + \Input bSending - TRUE if we're sending the finish hash, else receiving + + \Output + int32_t - finish hash size + + \Version 10/11/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLGenerateFinishHash(uint8_t *pBuffer, SecureStateT *pSecure, const char *pLabelTLS, uint8_t bServer, uint8_t bSending) +{ + uint8_t aMacTemp[128]; + int32_t iHashSize; + + if (pSecure->uSslVersion < SSL3_TLS1_3) + { + iHashSize = 12; + ds_strnzcpy((char *)aMacTemp, pLabelTLS, sizeof(aMacTemp)); + + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + // setup the finish verification hashes as per https://tools.ietf.org/html/rfc4346#section-7.4.9 + _ProtoSSLHandshakeHashGet(pSecure, CRYPTHASH_MD5, aMacTemp+15, sizeof(aMacTemp)-15); + _ProtoSSLHandshakeHashGet(pSecure, CRYPTHASH_SHA1, aMacTemp+31, sizeof(aMacTemp)-31); + _ProtoSSLDoPRF(pBuffer, iHashSize, pSecure->MasterKey, sizeof(pSecure->MasterKey), aMacTemp, 51); + } + else + { + // setup the finish verification hashes as per https://tools.ietf.org/html/rfc5246#section-7.4.9 + int32_t iHashSize2 = _ProtoSSLHandshakeHashGet(pSecure, pSecure->pCipher->uPrfType, aMacTemp+15, sizeof(aMacTemp)-15); + _ProtoSSLDoPHash(pBuffer, iHashSize, pSecure->MasterKey, sizeof(pSecure->MasterKey), aMacTemp, 15+iHashSize2, pSecure->pCipher->uPrfType); + } + } + else + { + // setup the finish verification hashes as per https://tools.ietf.org/html/rfc8446#section-4.4.4 + uint8_t aFinKey[CRYPTHASH_MAXDIGEST], *pHandshakeSecret; + // get handshake hash + iHashSize = _ProtoSSLHandshakeHashGet(pSecure, pSecure->pCipher->uPrfType, aMacTemp, sizeof(aMacTemp)); + // get handshake secret + pHandshakeSecret = ((bServer && bSending) || (!bServer && !bSending)) ? pSecure->pServerSecret : pSecure->pClientSecret; + // finished_key = HKDF-Expand-Label(BaseKey, "finished", "", Hash.length) + _ProtoSSLHkdfExpandLabel(aFinKey, iHashSize, pHandshakeSecret, iHashSize, "finished", aMacTemp, 0, pSecure->pCipher->uPrfType); + // verify_data = HMAC(finished_key, Hash(Handshake Context + Certificate* + CertificateVerify*) + _ProtoSSLHkdfExtract(pBuffer, iHashSize, aFinKey, iHashSize, aMacTemp, iHashSize, pSecure->pCipher->uPrfType); + } + + return(iHashSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGetSignatureScheme + + \Description + Get SignatureScheme from two-byte ident + + \Input uIdent - ident + + \Output + const SignatureSchemeT * - pointer to signature scheme, or NULL + + \Version 05/12/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static const SignatureSchemeT *_ProtoSSLGetSignatureScheme(const uint16_t uIdent) +{ + const SignatureSchemeT *pSigScheme; + int32_t iTable; + + for (iTable = 0, pSigScheme = NULL; iTable < (signed)(sizeof(_SSL3_SignatureSchemes) / sizeof(_SSL3_SignatureSchemes[0])); iTable += 1) + { + if (_SSL3_SignatureSchemes[iTable].uIdent != uIdent) + { + continue; + } + pSigScheme = &_SSL3_SignatureSchemes[iTable]; + } + return(pSigScheme); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLChooseSignatureScheme + + \Description + Choose a SignatureScheme from a list + + \Input *pSecure - secure state + \Input *pCertificate - server/client certificate + \Input *pData - signature scheme list + \Input uSigSchemeLen - length of list in bytes + \Input *pDataEnd - safe parse limit + + \Output + const SignatureSchemeT * - pointer to signature scheme, or NULL + + \Version 05/12/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static const SignatureSchemeT *_ProtoSSLChooseSignatureScheme(SecureStateT *pSecure, CertificateDataT *pCertificate, const uint8_t *pData, uint32_t uSigSchemeLen, const uint8_t *pDataEnd) +{ + const SignatureSchemeT *pSigScheme; + int32_t iSigScheme, iNumSigSchemes; + uint32_t uCertSigAlg; + + // get signature scheme we need for certificate, if available + uCertSigAlg = _CertificateGetSigAlg(pCertificate); + + // pick first supported signature scheme + for (iSigScheme = 0, iNumSigSchemes = (signed)(uSigSchemeLen/sizeof(SignatureAlgorithmT)); iSigScheme < iNumSigSchemes; iSigScheme += 1, pData += 2) + { + // skip unsupported signature schemes + if ((pSigScheme = _ProtoSSLGetSignatureScheme(_SafeRead16(pData, pDataEnd))) == NULL) + { + continue; + } + // don't pick a signature scheme we don't have a certificate for + if ((uCertSigAlg != SSL3_SIGALG_NONE) && (pSigScheme->SigAlg.uSigAlg != uCertSigAlg)) + { + continue; + } + // don't allow pss signature schemes with a pkcs1 certificate + if ((uCertSigAlg == SSL3_SIGALG_RSA) && (pCertificate->iKeyType != pSigScheme->uOidType)) + { + continue; + } + // tls1.3 specific checks + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + // don't allow pkcs1 signature schemes + if (pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PKCS1) + { + continue; + } + // enforce tls1.3 more restrictive signature schemes based on certificate key type + if ((uCertSigAlg == SSL3_SIGALG_ECDSA) && (pCertificate->iCrvType != pSigScheme->uOidType)) + { + continue; + } + } + return(pSigScheme); + } + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLGetCipher + + \Description + Get cipher from ident, validate that we previously sent it + + \Input *pSecure - secure state + \Input uCipherIdent - cipher ident to get cipher for + + \Output + CipherSuiteT * - pointer to cipher, or null + + \Version 01/18/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const CipherSuiteT *_ProtoSSLGetCipher(SecureStateT *pSecure, uint16_t uCipherIdent) +{ + const CipherSuiteT *pCipher; + int32_t iIndex; + + // match the cipher from our list + for (iIndex = 0, pCipher = NULL; (iIndex < (signed)(sizeof(_SSL3_CipherSuite)/sizeof(_SSL3_CipherSuite[0])) && (pCipher == NULL)); iIndex += 1) + { + if (uCipherIdent != _SSL3_CipherSuite[iIndex].uIdent) + { + continue; + } + // validate that we sent it + if ((_SSL3_CipherSuite[iIndex].uId & pSecure->uSentCiphers) == 0) + { + NetPrintf(("protossl: received cipher from server that we did not send\n")); + break; + } + pCipher = &_SSL3_CipherSuite[iIndex]; + } + + // return to caller + return(pCipher); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLChooseCipher + + \Description + Choose cipher from list of idents idents + + \Input *pSecure - secure state + \Input *pCertificate - server/client cert, if we have one + \Input *pCipherList - list of cipher idents + \Input iNumCiphers - number of ciphers + \Input *pDataEnd - end of data for safe parsing + \Input uCipherMask - mask of enabled ciphers + \Input uCipherPref - ident of preferred cipher, or zero for no preference + + \Output + CipherSuiteT * - pointer to cipher, or null + + \Version 02/19/2018 (jbrookes) Refactored from _ProtoSSLUpdateRecvClientHello +*/ +/********************************************************************************F*/ +static const CipherSuiteT *_ProtoSSLChooseCipher(SecureStateT *pSecure, CertificateDataT *pCertificate, const uint8_t *pCipherList, int32_t iNumCiphers, const uint8_t *pDataEnd, uint32_t uCipherMask, uint32_t uCipherPref) +{ + const uint8_t *pCipherStart = pCipherList; + const CipherSuiteT *pCipher; + int32_t iCipher, iIndex; + uint16_t uCipherIdent; + uint32_t uCertSigAlg; + + // get signature algorithm we need for certificate, if available + uCertSigAlg = _CertificateGetSigAlg(pCertificate); + + // pick first supported cipher + for (iCipher = 0, iIndex = 0, pSecure->pCipher = NULL; (iCipher < iNumCiphers) && (pSecure->pCipher == NULL); iCipher += 1, pCipherList += 2) + { + // read cipher + uCipherIdent = _SafeRead16(pCipherList, pDataEnd); + + // check against our list + for (iIndex = 0; iIndex < (signed)(sizeof(_SSL3_CipherSuite)/sizeof(_SSL3_CipherSuite[0])); iIndex += 1) + { + // ref supported cipher list entry + pCipher = &_SSL3_CipherSuite[iIndex]; + + // skip non-matching ciphers + if (uCipherIdent != pCipher->uIdent) + { + continue; + } + // skip non-preferred cipher, if set + if ((uCipherPref != 0) && (uCipherIdent != uCipherPref)) + { + continue; + } + // skip disabled ciphers + if ((pCipher->uId & uCipherMask) == 0) + { + continue; + } + /* skip elliptic curve ciphers if we found no supported elliptic curves in the extensions; for tls1.3 + we let this throogh, and will send a HelloRetryRequest asking for an elliptic curve we support */ + if ((pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) && (pCipher->uKey != SSL3_KEY_RSA) && (pSecure->pEllipticCurve == NULL)) + { + continue; + } + // tls1.2 or prior; skip ecdsa/rsa ciphers if we don't have an ecdsa/rsa cert + if ((pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) && (pCipher->uSig != uCertSigAlg)) + { + continue; + } + // skip ciphers that require a newer version of SSL than has been negotiated + if (pCipher->uMinVers > pSecure->uSslVersion) + { + continue; + } + // tls1.3 requires tls1.3 ciphers + if ((pSecure->uSslVersion >= SSL3_TLS1_3) && (pCipher->uMinVers < SSL3_TLS1_3)) + { + continue; + } + + // found a cipher + pSecure->pCipher = pCipher; + break; + } + } + // if we were looking for a preferred cipher and didn't find it, try again with no preference + if ((pSecure->pCipher == NULL) && (uCipherPref != 0)) + { + pSecure->pCipher = _ProtoSSLChooseCipher(pSecure, pCertificate, pCipherStart, iNumCiphers, pDataEnd, uCipherMask, 0); + } + return(pSecure->pCipher); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLChooseCurve + + \Description + Choose curve based on enabled curves and default curve preferences + + \Input uEnabledCurves - enabled curves + \Input iCurveDflt - preferred default curve + + \Output + EllipticCurveT * - pointer to curve, or null + + \Version 06/07/2019 (eesponda) +*/ +/********************************************************************************F*/ +static const EllipticCurveT *_ProtoSSLChooseCurve(uint32_t uEnabledCurves, int32_t iCurveDflt) +{ + int32_t iEllipticCurve; + const EllipticCurveT *pEllipticCurve = NULL; + + // if the default curve is enabled and supported use that + if ((iCurveDflt != -1) && ((_SSL3_EllipticCurves[iCurveDflt].uId & uEnabledCurves) != 0)) + { + pEllipticCurve = &_SSL3_EllipticCurves[iCurveDflt]; + } + // otherwise choose the first supported curve in our list + for (iEllipticCurve = 0; (iEllipticCurve < SSL3_NUM_CURVES) && (pEllipticCurve == NULL); iEllipticCurve += 1) + { + // skip disabled curves + if ((_SSL3_EllipticCurves[iEllipticCurve].uId & uEnabledCurves) == 0) + { + continue; + } + // found a curve + pEllipticCurve = &_SSL3_EllipticCurves[iEllipticCurve]; + } + return(pEllipticCurve); +} + +/* + hello extension writing +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteAlpn + + \Description + Write Application Level Protocol Negotiation (ALPN) extension to ClientHello + and ServerHello handshake packets; ref http://tools.ietf.org/html/rfc7301 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Version 08/03/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteAlpn(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + uint16_t uAlpnExtensionLength, uExtnLength; + uint8_t *pBufStart = pBuffer; + const SecureStateT *pSecure = pState->pSecure; + + // if no protocols, don't write anything into ClientHello + if ((!pState->bServer) && (pState->uNumAlpnProtocols == 0)) + { + return(0); + } + // if no protocol selected, don't write anything into ServerHello + else if ((pState->bServer) && ((pSecure == NULL) || (*pSecure->strAlpnProtocol == '\0'))) + { + return(0); + } + + // calculate the size of the extension based on if we are client or server + uAlpnExtensionLength = (!pState->bServer) ? pState->uAlpnExtensionLength : ((uint8_t)strlen(pSecure->strAlpnProtocol) + sizeof(uint8_t)); + uExtnLength = 2+uAlpnExtensionLength; // ident+length+protocol list length + + // make sure we have room + if ((2+2+uExtnLength) > iBufLen) + { + NetPrintf(("protossl: could not add extension alpn; insufficient buffer\n")); + return(0); + } + + // extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_ALPN>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_ALPN>>0); + // extension length + *pBuffer++ = (uint8_t)(uExtnLength>>8); + *pBuffer++ = (uint8_t)(uExtnLength>>0); + // alpn length + *pBuffer++ = (uint8_t)(pState->uAlpnExtensionLength>>8); + *pBuffer++ = (uint8_t)(pState->uAlpnExtensionLength>>0); + + // write the protocols + if (!pState->bServer) + { + int16_t iProtocol; + + // alpn protocols (length + byte string) + for (iProtocol = 0; iProtocol < pState->uNumAlpnProtocols; iProtocol += 1) + { + const AlpnProtocolT *pProtocol = &pState->aAlpnProtocols[iProtocol]; + + *pBuffer++ = pProtocol->uLength; + ds_memcpy(pBuffer, pProtocol->strName, pProtocol->uLength); + pBuffer += pProtocol->uLength; + } + } + else + { + const uint8_t uProtocolLen = uAlpnExtensionLength-(uint8_t)sizeof(uint8_t); + + // alpn protocol + *pBuffer++ = uProtocolLen; + ds_memcpy(pBuffer, pSecure->strAlpnProtocol, uProtocolLen); + pBuffer += uProtocolLen; + } + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteCookie + + \Description + Write Cookie extension, if we have one + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Version 11/29/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteCookie(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + uint8_t *pBufStart = pBuffer; + SecureStateT *pSecure = pState->pSecure; + int32_t iCookieLength; + + // only if we have a cookie + if (pSecure->pCookie == NULL) + { + return(0); + } + + // read cookie length + iCookieLength = (pSecure->pCookie[0] << 8) | pSecure->pCookie[1]; + + // make sure we have enough room + if ((2+2+2+iCookieLength) > iBufLen) + { + NetPrintf(("protossl: could not add extension cookie; insufficient buffer\n")); + return(0); + } + + // extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_COOKIE>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_COOKIE>>0); + // extension length + *pBuffer++ = (uint8_t)((iCookieLength+2)>>8); + *pBuffer++ = (uint8_t)((iCookieLength+2)>>0); + // cookie length + *pBuffer++ = (uint8_t)(iCookieLength>>8); + *pBuffer++ = (uint8_t)(iCookieLength>>0); + // add the cookie + ds_memcpy_s(pBuffer, iBufLen-6, pSecure->pCookie+2, iCookieLength); + pBuffer += iCookieLength; + + // as per https://tools.ietf.org/html/rfc8446#section-4.2.2 cookies can only be used once + pSecure->pCookie = NULL; + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteEllipticCurves + + \Description + Write the Elliptic Curves / Supported Groups extension to the ClientHello; + ref http://tools.ietf.org/html/rfc4492 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Notes + TLS1.3 calls this "supported_groups", but the actual extension format is + identical. + + \Version 01/19/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteEllipticCurves(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + const uint16_t uMaxEllipticCurvesLength = sizeof(_SSL3_EllipticCurves[0].uIdent)*(sizeof(_SSL3_EllipticCurves)/sizeof(*_SSL3_EllipticCurves)); + uint16_t uEllipticCurvesLength, uExtnLength; + uint8_t *pBufStart = pBuffer, *pExtnLength, uNumCurves; + int32_t iEllipticCurve; + + // make sure we have enough room + if ((2+2+2+uMaxEllipticCurvesLength) > iBufLen) + { + NetPrintf(("protossl: could not add extension supported_groups; insufficient buffer\n")); + return(0); + } + // if no curves are enabled don't write anything + if (pState->uEnabledCurves == 0) + { + return(0); + } + + // extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_ELLIPTIC_CURVES>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_ELLIPTIC_CURVES>>0); + // save the location and skip past + pExtnLength = pBuffer; + pBuffer += 4; + // supported elliptic curves + for (iEllipticCurve = 0, uNumCurves = 0; iEllipticCurve < (signed)(sizeof(_SSL3_EllipticCurves)/sizeof(*_SSL3_EllipticCurves)); iEllipticCurve += 1) + { + // skip disabled curves + if ((_SSL3_EllipticCurves[iEllipticCurve].uId & pState->uEnabledCurves) == 0) + { + continue; + } + *pBuffer++ = (uint8_t)(_SSL3_EllipticCurves[iEllipticCurve].uIdent>>8); + *pBuffer++ = (uint8_t)(_SSL3_EllipticCurves[iEllipticCurve].uIdent>>0); + uNumCurves += 1; + } + uEllipticCurvesLength = sizeof(_SSL3_EllipticCurves[0].uIdent)*uNumCurves; + uExtnLength = uEllipticCurvesLength+2; + + // extension length + *pExtnLength++ = (uint8_t)(uExtnLength>>8); + *pExtnLength++ = (uint8_t)(uExtnLength>>0); + // elliptic curves length + *pExtnLength++ = (uint8_t)(uEllipticCurvesLength>>8); + *pExtnLength++ = (uint8_t)(uEllipticCurvesLength>>0); + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteKeyShare + + \Description + Write the KeyShare extension to the ClientHello; + ref https://tools.ietf.org/html/rfc8446#section-4.2.8 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Version 01/25/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteKeyShare(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iDataLength = 0; + uint8_t *pBufStart = pBuffer; + uint8_t aPublicKey[128]; + const EllipticCurveT *pEllipticCurve; + + // ref elliptic curve + if (((pEllipticCurve = pSecure->pEllipticCurve) == NULL) && (pState->iCurveDflt != -1) && ((_SSL3_EllipticCurves[pState->iCurveDflt].uId & pState->uEnabledCurves) != 0)) + { + pEllipticCurve = _ProtoSSLChooseCurve(pState->uEnabledCurves, pState->iCurveDflt); + } + + // set up curve data + if (pEllipticCurve != NULL) + { + const CryptCurveDhT *pEcc; + int32_t iPublicKeySize = 0; + + // set public key ident + aPublicKey[0] = (uint8_t)(pEllipticCurve->uIdent>>8); + aPublicKey[1] = (uint8_t)(pEllipticCurve->uIdent>>0); + + // if server in helloretryrequest flow, use KeyShareHelloRetryRequest value which does not include a key or key size + if (pState->bServer && (pState->iState == ST3_SEND_HELLO_RETRY)) + { + iDataLength = 2; + } + else + { + iDataLength = 4; + + // encode public key generated in Hello into buffer + if (pSecure->bEccKeyGenerated && ((pEcc = _ProtoSSLEccInitContext(pSecure, pEllipticCurve)) != NULL)) + { + iPublicKeySize = pEcc->PointFinal(pSecure->EccContext, NULL, FALSE, aPublicKey+4, sizeof(aPublicKey)-4); + iDataLength += iPublicKeySize; + } + + // encode public key size + aPublicKey[2] = (uint8_t)(iPublicKeySize>>8); + aPublicKey[3] = (uint8_t)(iPublicKeySize>>0); + } + } + + // make sure we have enough room + if ((2+2+2+iDataLength) > iBufLen) + { + NetPrintf(("protossl: could not add extension key_share; insufficient buffer\n")); + return(0); + } + + // extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_KEY_SHARE>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_KEY_SHARE>>0); + // extension length (client only) + if (!pState->bServer) + { + *pBuffer++ = (uint8_t)((iDataLength+2)>>8); + *pBuffer++ = (uint8_t)((iDataLength+2)>>0); + } + // key_share length + *pBuffer++ = (uint8_t)(iDataLength>>8); + *pBuffer++ = (uint8_t)(iDataLength>>0); + // key share data + if (iDataLength > 0) + { + ds_memcpy(pBuffer, aPublicKey, iDataLength); + pBuffer += iDataLength; + } + // return extension size to caller + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWritePreSharedKey + + \Description + Write Pre-Shared Key (PSK) extension to the ClientHello handshake packet; + ref https://tools.ietf.org/html/rfc8446#section-4.2.11 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + \Input *pHshkMsgBuf - handshake message buffer pointer + + \Output + int32_t - number of bytes added to buffer + + \Version 12/07/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWritePreSharedKey(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen, const uint8_t *pHshkMsgBuf) +{ + uint8_t *pBufStart = pBuffer, *pHshkMsgEnd; + const SessionHistoryT *pSessHist; + const SessionTicketT *pSessTick; + int32_t iExtnLength, iHashSize; + uint32_t uAgeAdd; + + // only add if we have a ticket to match + if (((pSessHist = _SessionHistoryGet(pState->strHost, SockaddrInGetPort(&pState->PeerAddr), NULL)) == NULL) || !pSessHist->bSessionTicket) + { + return(0); + } + pSessTick = &pSessHist->SessionTicket; + + // get hash length + iHashSize = CryptHashGetSize(pSessTick->eHashType); + + // make sure we have enough room + iExtnLength = 4 + 2 + 2 + pSessTick->uTickLen + 4 + 2 + 1 + iHashSize; + if (iExtnLength > iBufLen) + { + return((iBufLen == 0) ? iExtnLength : 0); + } + + // pre_shared_Key extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_PRE_SHARED_KEY>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_PRE_SHARED_KEY>>0); + + // client adds offered psks (we only support one) + if (!pState->bServer) + { + // set pre_shared_key extension length + *pBuffer++ = (uint8_t)((iExtnLength-4)>>8); + *pBuffer++ = (uint8_t)((iExtnLength-4)>>0); + + // add psks identity length + *pBuffer++ = (uint8_t)((2+pSessTick->uTickLen+sizeof(uint32_t))>>8); + *pBuffer++ = (uint8_t)((2+pSessTick->uTickLen+sizeof(uint32_t))>>0); + // add identity length + *pBuffer++ = (uint8_t)(pSessTick->uTickLen>>8); + *pBuffer++ = (uint8_t)(pSessTick->uTickLen>>0); + // add ticket + ds_memcpy(pBuffer, pSessTick->aTicketData, pSessTick->uTickLen); + pBuffer += pSessTick->uTickLen; + // add obfuscated ticket age + uAgeAdd = (time(NULL) - pSessTick->uRecvTime) * 1000; + uAgeAdd += pSessTick->uAgeAdd; + *pBuffer++ = (uint8_t)(uAgeAdd>>24); + *pBuffer++ = (uint8_t)(uAgeAdd>>16); + *pBuffer++ = (uint8_t)(uAgeAdd>>8); + *pBuffer++ = (uint8_t)(uAgeAdd>>0); + // save handshake message end + pHshkMsgEnd = pBuffer; + // add binders length + *pBuffer++ = (uint8_t)((iHashSize+1)>>8); + *pBuffer++ = (uint8_t)((iHashSize+1)>>0); + // add binder length + *pBuffer++ = (uint8_t)(iHashSize>>0); + // calculate the binder + pBuffer = _ProtoSSLCalcResumeBinder(pState, pBuffer, iBufLen, pSessTick, pHshkMsgBuf, pHshkMsgEnd-pHshkMsgBuf, iHashSize); + } + else // indicated selected psk - we only support one + { + *pBuffer++ = (uint8_t)(2>>8); + *pBuffer++ = (uint8_t)(2>>0); + *pBuffer++ = (uint8_t)(0>>8); + *pBuffer++ = (uint8_t)(0>>0); + } + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWritePreSharedKeyModes + + \Description + Write Pre-Shared Key Exchange Modes extension to the ClientHello handshake + packet; ref https://tools.ietf.org/html/rfc8446#section-4.2.9 + + This message is client-only + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Version 12/12/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWritePreSharedKeyModes(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + const uint16_t uExtnLength = 2; + uint8_t *pBufStart = pBuffer; + + // make sure we have enough room + if ((2+2+uExtnLength) > iBufLen) + { + NetPrintf(("protossl: could not add extension psk_modes; insufficient buffer\n")); + return(0); + } + + // extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_PSK_MODES>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_PSK_MODES>>0); + // extension length + *pBuffer++ = (uint8_t)(uExtnLength>>8); + *pBuffer++ = (uint8_t)(uExtnLength>>0); + // psk_mode array length + *pBuffer++ = 1; + // psk_modes (only offer psk_dhe_ke; most servers explicitly disallow psk_ke) + *pBuffer++ = 1; + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteRenegotiationInfo + + \Description + Write Renegotiation Info extension to the ClientHello handshake packet; + ref https://tools.ietf.org/html/rfc5746 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Notes + This extension is added strictly for compatibility with sites that require + it and will fail the connection if it is not present. ProtoSSL does not + support renegotiation of any kind and an attempt by a server to renegotiate + will result in a no_renegotiation alert. + + \Version 03/27/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteRenegotiationInfo(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + uint32_t uHostLen = (uint32_t)strlen(pState->strHost); + uint8_t *pBufStart = pBuffer; + + // make sure we have enough room + if ((uHostLen+9) > (unsigned)iBufLen) + { + NetPrintf(("protossl: could not add extension server_name; insufficient buffer\n")); + return(0); + } + + // renegotiation_info extension ident + *pBuffer++ = (uint8_t)((SSL_EXTN_RENEGOTIATION_INFO>>8)&0xff); + *pBuffer++ = (uint8_t)((SSL_EXTN_RENEGOTIATION_INFO>>0)&0xff); + // empty renegotiation_info length + *pBuffer++ = (uint8_t)0; + *pBuffer++ = (uint8_t)1; + // empty renegotiation_info + *pBuffer++ = (uint8_t)0; + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteServerName + + \Description + Write Server Name Indication (SNI) extension to the ClientHello handshake + packet; ref http://tools.ietf.org/html/rfc6066#page-6 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Version 10/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteServerName(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + uint32_t uHostLen = (uint32_t)strlen(pState->strHost); + uint8_t *pBufStart = pBuffer; + + // make sure we have enough room + if ((uHostLen+9) > (unsigned)iBufLen) + { + NetPrintf(("protossl: could not add extension server_name; insufficient buffer\n")); + return(0); + } + + // server name extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_SERVER_NAME>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_SERVER_NAME>>0); + // server name extension length + *pBuffer++ = (uint8_t)((uHostLen+5) >> 8); + *pBuffer++ = (uint8_t)(uHostLen+5); + + // server name list length + *pBuffer++ = (uint8_t)((uHostLen+3) >> 8); + *pBuffer++ = (uint8_t)(uHostLen+3); + // type: hostname + *pBuffer++ = 0; + // hostname length + *pBuffer++ = (uint8_t)(uHostLen >> 8); + *pBuffer++ = (uint8_t)(uHostLen); + + // hostname + ds_memcpy(pBuffer, pState->strHost, uHostLen); + pBuffer += (int32_t)uHostLen; + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteSignatureAlgorithms + + \Description + Write Signature Algorithms extension to the ClientHello handshake packet; + ref https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Version 10/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteSignatureAlgorithms(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + const int32_t _iListLength = sizeof(_SSL3_SignatureSchemes)/sizeof(_SSL3_SignatureSchemes[0])*2; // signature hash algorithms list length + const int32_t _iExtnLength = 2+_iListLength; // ident+length+algorithms list length + SecureStateT *pSecure = pState->pSecure; + uint8_t *pBufStart = pBuffer; + int32_t iIndex; + + // as per RFC, this extension is not meaningful for TLS versions prior to 1.2; clients MUST NOT offer it if they are offering prior versions + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + return(0); + } + // make sure we have enough room + if ((2+2+_iExtnLength) > iBufLen) + { + NetPrintf(("protossl: could not add extension signature_algorithms; insufficient buffer\n")); + return(0); + } + + // signature hash algorithms extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_SIGNATURE_ALGORITHMS>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_SIGNATURE_ALGORITHMS>>0); + // signature hash algorithms extension length + *pBuffer++ = (uint8_t)(_iExtnLength>>8); + *pBuffer++ = (uint8_t)(_iExtnLength>>0); + // signature hash algorithms extension list length + *pBuffer++ = (uint8_t)(_iListLength>>8); + *pBuffer++ = (uint8_t)(_iListLength>>0); + // supported signature algorithms + for (iIndex = 0; iIndex < (signed)(sizeof(_SSL3_SignatureSchemes)/sizeof(*_SSL3_SignatureSchemes)); iIndex += 1) + { + *pBuffer++ = (uint8_t)(_SSL3_SignatureSchemes[iIndex].uIdent>>8); + *pBuffer++ = (uint8_t)(_SSL3_SignatureSchemes[iIndex].uIdent>>0); + } + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnWriteSupportedVersions + + \Description + Write the Supported Versions extension to the ClientHello; + ref https://tools.ietf.org/html/rfc8446#section-4.2.1 + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + + \Output + int32_t - number of bytes added to buffer + + \Notes + Unlike other extensions, supported_versions is an extension required to + support TLS1.3. A supported_versions extension overrides the legacy TLS + client version in the ClientHello, which remains fixed at 1.2. + + \Version 01/24/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnWriteSupportedVersions(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen) +{ + int32_t iVersion, iNumVersions; + int32_t iDataLength, iExtnLength; + uint8_t *pBufStart = pBuffer; + SecureStateT *pSecure = pState->pSecure; + + // list all versions if not server + if (!pState->bServer) + { + iNumVersions = pState->uSslVersion - pState->uSslVersionMin + 1; + iDataLength = (iNumVersions * 2) + 1; + } + else + { + iNumVersions = 1; + iDataLength = 2; + } + // calculate extension length + iExtnLength = 2 + 2 + iDataLength; + + // make sure we have enough room + if (iExtnLength > iBufLen) + { + NetPrintf(("protossl: could not add extension supported_versions; insufficient buffer\n")); + return(0); + } + + // extension ident + *pBuffer++ = (uint8_t)(SSL_EXTN_SUPPORTED_VERSIONS>>8); + *pBuffer++ = (uint8_t)(SSL_EXTN_SUPPORTED_VERSIONS>>0); + // extension length + *pBuffer++ = (uint8_t)((iDataLength)>>8); + *pBuffer++ = (uint8_t)((iDataLength)>>0); + + // add extension data + if (!pState->bServer) + { + // size of versions list + *pBuffer++ = (uint8_t)(iNumVersions*2); + // supported_versions data + for (iVersion = pState->uSslVersion; iVersion >= pState->uSslVersionMin; iVersion -= 1) + { + *pBuffer++ = (uint8_t)(iVersion>>8); + *pBuffer++ = (uint8_t)(iVersion>>0); + } + } + else + { + *pBuffer++ = (uint8_t)(pSecure->uSslVersion>>8); + *pBuffer++ = (uint8_t)(pSecure->uSslVersion>>0); + } + + return((int32_t)(pBuffer-pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAddHelloExtensions + + \Description + Add any ClientHello extensions + + \Input *pState - module state reference + \Input *pBuffer - buffer to write extension into + \Input iBufLen - length of buffer + \Input *pHshkMsgBuf - handshake message buffer + \Input uHelloExtn - flag of which hello extensions to include + + \Output + uint8_t * - pointer past end of extension chunk + + \Notes + ClientHello extension registry: + http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml + + \Version 10/01/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t *_ProtoSSLAddHelloExtensions(ProtoSSLRefT *pState, uint8_t *pBuffer, int32_t iBufLen, const uint8_t *pHshkMsgBuf, uint32_t uHelloExtn) +{ + SecureStateT *pSecure = pState->pSecure; + uint32_t uExtnLen; + int32_t iBufOff, iPskOff = 0; + + // skip extensions length field + iBufOff = 2; + + // add enabled extensions (client only) + if (!pState->bServer) + { + if (uHelloExtn & PROTOSSL_HELLOEXTN_SERVERNAME) + { + iBufOff += _ProtoSSLHelloExtnWriteServerName(pState, pBuffer+iBufOff, iBufLen-iBufOff); + } + if (uHelloExtn & PROTOSSL_HELLOEXTN_ELLIPTIC_CURVES) + { + iBufOff += _ProtoSSLHelloExtnWriteEllipticCurves(pState, pBuffer+iBufOff, iBufLen-iBufOff); + } + } + + // add enabled extensions (client & server) + if (uHelloExtn & PROTOSSL_HELLOEXTN_ALPN) + { + iBufOff += _ProtoSSLHelloExtnWriteAlpn(pState, pBuffer+iBufOff, iBufLen-iBufOff); + } + if ((!pState->bServer && (pState->uSslVersionMin < SSL3_TLS1_3)) || (pState->bServer && pSecure->bRenegotiationInfo && (pSecure->uSslVersion < SSL3_TLS1_3))) + { + iBufOff += _ProtoSSLHelloExtnWriteRenegotiationInfo(pState, pBuffer+iBufOff, iBufLen-iBufOff); + } + if ((pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && (pState->iState != ST3_SEND_EXTN) && (pState->iState != ST3_SEND_CERT_REQ)) + { + iBufOff += _ProtoSSLHelloExtnWriteKeyShare(pState, pBuffer+iBufOff, iBufLen-iBufOff); + iBufOff += _ProtoSSLHelloExtnWriteSupportedVersions(pState, pBuffer+iBufOff, iBufLen-iBufOff); + iBufOff += _ProtoSSLHelloExtnWriteCookie(pState, pBuffer+iBufOff, iBufLen-iBufOff); + } + if (((uHelloExtn & PROTOSSL_HELLOEXTN_SIGALGS) && !pState->bServer) || ((pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && (!pState->bServer || pState->iState == ST3_SEND_CERT_REQ))) + { + iBufOff += _ProtoSSLHelloExtnWriteSignatureAlgorithms(pState, pBuffer+iBufOff, iBufLen-iBufOff); + } + + // add psk-modes (client only) + if (!pState->bServer) + { + iBufOff += _ProtoSSLHelloExtnWritePreSharedKeyModes(pState, pBuffer+iBufOff, iBufLen-iBufOff); + } + + /* get pre-shared key length so we can write overall extension length. we need that value + to be correct when we actually write the pre_shared_key extension, so that we can calculate + the binder hash correctly */ + if (!pState->bServer && (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3)) + { + int32_t iPskLen = _ProtoSSLHelloExtnWritePreSharedKey(pState, NULL, 0, NULL); + iPskOff = iBufOff; + if ((iBufOff+iPskLen) > iBufLen) + { + NetPrintf(("protossl: could not add extension psk_modes; insufficient buffer\n")); + iPskLen = 0; + } + iBufOff += iPskLen; + } + + // update extension length + if (iBufOff > 2) + { + uExtnLen = (uint32_t)(iBufOff-2); + pBuffer[0] = (uint8_t)(uExtnLen>>8); + pBuffer[1] = (uint8_t)(uExtnLen>>0); + } + else + { + iBufOff = 0; + } + + // add pre_shared_key extension + if ((iBufOff > 2) && (iPskOff > 0)) + { + _ProtoSSLHelloExtnWritePreSharedKey(pState, pBuffer+iPskOff, iBufLen-iPskOff, pHshkMsgBuf); + } + + // return updated buffer pointer + return(pBuffer+iBufOff); +} + +/* + hello extension parsing +*/ + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnGetName + + \Description + Get debug extension name for logging + + \Input uExtnId + + \Output + const char * - pointer to name, or "unknown" if not recognized + + \Version 04/11/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_ProtoSSLHelloExtnGetName(uint32_t uExtnId) +{ + const char *pName = "unknown"; + if (uExtnId <= SSL_EXTN_MAX) + { + pName = _SSL3_strExtensionNames[uExtnId]; + } + else if (uExtnId == SSL_EXTN_RENEGOTIATION_INFO) + { + pName = "renegotiation_info"; + } + return(pName); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParseAlpn + + \Description + Parse the ALPN extension from the client & server hello + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 08/05/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParseAlpn(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + SecureStateT *pSecure = pState->pSecure; + uint16_t uAlpnExtensionLength = _SafeRead16(pData, pDataEnd); + pData += 2; + + if (uAlpnExtensionLength == 0) + { + /* if the extension has data but the list length is zero + treat this as handshake failure */ + NetPrintf(("protossl: received invalid alpn extension length in client hello\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_SETUP); + } + + if (!pState->bServer) + { + // save the negotiated protocol in the secure state so it can be queried (add 1 for nul terminator) + _SafeReadString(pSecure->strAlpnProtocol, sizeof(pSecure->strAlpnProtocol), (const char *)pData+1, _SafeRead8(pData, pDataEnd), pDataEnd); + NetPrintfVerbose((pState->iVerbose, 0, "protossl: server negotiated protocol %s using alpn extension\n", pSecure->strAlpnProtocol)); + } + else if (pState->uNumAlpnProtocols > 0) // skip parsing if the user has not chosed any protocols + { + uint32_t uProtocol; + + // loop through the protocols and pick the first supported + for (uProtocol = 0; (uProtocol < pState->uNumAlpnProtocols) && (pSecure->strAlpnProtocol[0] == '\0'); uProtocol += 1) + { + const AlpnProtocolT *pProtocol = &pState->aAlpnProtocols[uProtocol]; + uint32_t uOffset, uDataLen; + + for (uOffset = 0; uOffset < uAlpnExtensionLength; uOffset += uDataLen+1) + { + const uint8_t *pExtensionData = pData+uOffset; + uDataLen = _SafeRead8(pExtensionData, pDataEnd); + _SafeReadString(pSecure->strAlpnProtocol, sizeof(pSecure->strAlpnProtocol), (const char *)pExtensionData+1, uDataLen, pDataEnd); + + if (!strcmp(pSecure->strAlpnProtocol, pProtocol->strName)) + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: negotiated protocol %s using alpn extension\n", pSecure->strAlpnProtocol)); + break; + } + else + { + pSecure->strAlpnProtocol[0] = '\0'; + } + } + } + + if (pSecure->strAlpnProtocol[0] == '\0') + { + /* if we did not match a protocol then we do not support any + of the protocols that the client prefers; treat this as handshake failure */ + NetPrintf(("protossl: client requested protocols that are not supported by the server via alpn extension\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_NO_APPLICATION_PROTOCOL); + return(ST_FAIL_SETUP); + } + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParseCookie + + \Description + Parse the Cookie extension from the client & server hello + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 11/29/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParseCookie(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + SecureStateT *pSecure = pState->pSecure; + + // point to cookie in receive buffer so we don't have to waste memory saving it off + pSecure->pCookie = pData; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: parsed cookie of length %d\n", _SafeRead16(pData, pDataEnd))); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParseEllipticCurves + + \Description + Parse the elliptic curves extension from the client hello + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 01/19/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParseEllipticCurves(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + int32_t iEllipticCurve, iIndex; + SecureStateT *pSecure = pState->pSecure; + uint16_t uEllipticCurveExtensionLength = _SafeRead16(pData, pDataEnd); + pData += 2; + + if (uEllipticCurveExtensionLength == 0) + { + /* if the extension has data but the list length is zero + treat this as handshake failure */ + NetPrintf(("protossl: received invalid elliptic curves extension length in client hello\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_SETUP); + } + + // pick first supported elliptic curve + for (iEllipticCurve = 0, pSecure->pEllipticCurve = NULL; (iEllipticCurve < (signed)(uEllipticCurveExtensionLength/2)) && (pSecure->pEllipticCurve == NULL); iEllipticCurve += 1, pData += 2) + { + for (iIndex = 0; iIndex < (signed)(sizeof(_SSL3_EllipticCurves)/sizeof(*_SSL3_EllipticCurves)); iIndex += 1) + { + if ((pData+2) > pDataEnd) + { + continue; + } + // skip non-matching elliptic curve + if ((pData[0] != (uint8_t)(_SSL3_EllipticCurves[iIndex].uIdent >> 8)) || (pData[1] != (uint8_t)(_SSL3_EllipticCurves[iIndex].uIdent))) + { + continue; + } + // skip disabled curves + if ((_SSL3_EllipticCurves[iIndex].uId & pState->uEnabledCurves) == 0) + { + continue; + } + // found an elliptic curve + pSecure->pEllipticCurve = &_SSL3_EllipticCurves[iIndex]; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: using elliptic curve %s (ident=0x%04x)\n", pSecure->pEllipticCurve->strName, pSecure->pEllipticCurve->uIdent)); + break; + } + } + + // ecdhe key exchange is required in TLS1.3+. if we cannot find a curve that we share in common, we need to fail the handshake + if ((pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && (pSecure->pEllipticCurve == NULL)) + { + NetPrintf(("protossl: no elliptic curve found\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_SETUP); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParseKeyShare + + \Description + Parse the key share extension from the hello (tls1.3) + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 01/25/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParseKeyShare(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iKeyShare, iIndex; + uint16_t uKeyShareCurveId; + uint32_t uKeyShareLen; + uint32_t uExtnLen; + + NetPrintfVerbose((pState->iVerbose, 0, "protossl: parsing keyshare extension\n")); + + // process extension length; server only + if (pState->bServer) + { + if ((uExtnLen = _SafeRead16(pData, pDataEnd)) == 0) + { + NetPrintf(("protossl: received empty key_share\n")); + } + pDataEnd = DS_MIN(&pData[2]+uExtnLen, pDataEnd); + pData += 2; + } + + // iterate through array of key share objects + for (iKeyShare = 0, uKeyShareLen = 0, pSecure->pEllipticCurve = NULL; (pData < pDataEnd) && (pSecure->pEllipticCurve == NULL); iKeyShare += 1) + { + // get keyshare object curve id + uKeyShareCurveId = _SafeRead16(pData, pDataEnd); + pData += 2; + // get keyshare size (if key is present) + if (pData < pDataEnd) + { + uKeyShareLen = _SafeRead16(pData, pDataEnd); + // skip to keyshare data + pData += 2; + } + + // see if we have a match + for (iIndex = 0; iIndex < (signed)(sizeof(_SSL3_EllipticCurves)/sizeof(*_SSL3_EllipticCurves)); iIndex += 1) + { + // skip non-matching elliptic curve + if (uKeyShareCurveId != _SSL3_EllipticCurves[iIndex].uIdent) + { + continue; + } + // skip disabled curves + if ((_SSL3_EllipticCurves[iIndex].uId & pState->uEnabledCurves) == 0) + { + continue; + } + // found an elliptic curve + pSecure->pEllipticCurve = &_SSL3_EllipticCurves[iIndex]; + + // save key + if (uKeyShareLen > 0) + { + _SafeReadBytes(pSecure->PubKey, sizeof(pSecure->PubKey), pData, uKeyShareLen, pDataEnd); + pSecure->uPubKeyLength = uKeyShareLen; + } + + NetPrintfVerbose((pState->iVerbose, 0, "protossl: using elliptic curve %s (ident=0x%04x)\n", pSecure->pEllipticCurve->strName, pSecure->pEllipticCurve->uIdent)); + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->PubKey, pSecure->uPubKeyLength, "key_share"); + #endif + break; + } + // move to next keyshare object + pData += uKeyShareLen; + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParsePreSharedKey + + \Description + Parse the pre_shared_key extension from the hello (tls1.3) + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 12/13/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParsePreSharedKey(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + SecureStateT *pSecure = pState->pSecure; + + NetPrintfVerbose((pState->iVerbose, 0, "protossl: parsing pre_shared_key extension\n")); + + if (pState->bServer) + { + //$$TODO add server parsing of psk + } + else + { + uint16_t uIdentity = _SafeRead16(pData, pDataEnd); + pSecure->bSessionResume = (uIdentity == 0) ? TRUE : FALSE; + NetPrintf(("protossl: psk selected identity=%d; resume %s\n", uIdentity, pSecure->bSessionResume ? "true" : "false")); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParseRenegotiationInfo + + \Description + Parse the Renegotiation Info extension from the client & server hello + ref https://tools.ietf.org/html/rfc5746 + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 03/27/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParseRenegotiationInfo(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + SecureStateT *pSecure = pState->pSecure; + pSecure->bRenegotiationInfo = TRUE; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: parsed renegotation_info\n")); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParseSignatureAlgorithms + + \Description + Parse the signature algorithms (tls1.3 calls them signature + schemes) extension. + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Notes + This is needed when sending ServerKeyExchange messages to the client + + \Version 03/03/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParseSignatureAlgorithms(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + SecureStateT *pSecure = pState->pSecure; + uint16_t uSigSchemeLen; + + // get sig scheme length + uSigSchemeLen = _SafeRead16(pData, pDataEnd); + pData += 2; + + // if the extension has data but the list length is zero treat this as handshake failure + if (uSigSchemeLen == 0) + { + NetPrintf(("protossl: received invalid signature algorithms length in client hello\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_SETUP); + } + + // pick first supported signature algorithm + pSecure->pSigScheme = _ProtoSSLChooseSignatureScheme(pSecure, pState->pCertificate, pData, uSigSchemeLen, pDataEnd); + + /* tls1.2 https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1) and tls1.3 (https://tools.ietf.org/html/rfc8446#section-9.2) + define actions around the presence/absence of this extension, but do not define actions in the case no suitable signature algorithms + are included. we treat this as a handshake failure based around observed behavior of other implementations e.g. openssl */ + if (pSecure->pSigScheme == NULL) + { + NetPrintf(("protossl: no acceptable signature algorithms included in signature_algorithms extension\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_SETUP); + } + + // log choice and return success + NetPrintfVerbose((pState->iVerbose, 0, "protossl: using signature scheme 0x%04x\n", pSecure->pSigScheme->uIdent)); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLHelloExtnParseSupportedVersions + + \Description + Parse the Supported Versions extension from the ClientHello + ref https://tools.ietf.org/html/rfc8446#section-4.2.1 + + \Input *pState - module state reference + \Input *pData - the extension data we are parsing + \Input *pDataEnd - end of extension data + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 01/26/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLHelloExtnParseSupportedVersions(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd) +{ + SecureStateT *pSecure = pState->pSecure; + uint32_t uExtnLength = pState->bServer ? _SafeRead8(pData++, pDataEnd) : 2; + uint32_t uSslVersion; + + // pick first valid supported version + for (pDataEnd = pData + uExtnLength; pData < pDataEnd; pData += 2) + { + uSslVersion = _SafeRead16(pData, pDataEnd); + + // range check + if ((uSslVersion < pState->uSslVersionMin) || (uSslVersion > pState->uSslVersion)) + { + continue; + } + + // found a version, roll with it + pSecure->uSslClientVersion = pSecure->uSslVersion = uSslVersion; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: ssl protocol version overriden by supported_versions extension to %s\n", _SSL3_strVersionNames[pSecure->uSslVersion & 0xff])); + return(0); + } + + /* if the "supported_versions" extension in the ServerHello contains a version not offered by the client or contains + a version prior to TLS 1.3, the client MUST abort the handshake with an "illegal_parameter" alert */ + if (!pState->bServer) + { + NetPrintf(("protossl: supported_versions extension did not contain a valid version\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLParseHelloExtensions + + \Description + Parse the hello extensions in the client & server hello handshake packets + + \Input *pState - module state reference + \Input *pData - start of extension data (length word) + \Input *pDataEnd - end of the payload data + \Input uExtensionParse - id of extension to parse, or -1 to parse all + + \Output + int32_t - result of opertion (zero or ST_FAIL_* on error) + + \Version 08/05/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLParseHelloExtensions(ProtoSSLRefT *pState, const uint8_t *pData, const uint8_t *pDataEnd, uint16_t uExtensionParse) +{ + SecureStateT *pSecure = pState->pSecure; + uint16_t uExtensionType, uExtensionLength; + int32_t iResult = 0; + + // read overall length of extensions, and constrain reading to extension end + uExtensionLength = _SafeRead16(pData, pDataEnd); + pDataEnd = DS_MIN(&pData[2]+uExtensionLength, pDataEnd); + pData += 2; + + // parse the complete extension section while we have not encountered an error + for (iResult = 0; (pData < pDataEnd) && (iResult == 0); pData += uExtensionLength) + { + // get extension type and length + uExtensionType = _SafeRead16(pData, pDataEnd); + uExtensionLength = _SafeRead16(pData+2, pDataEnd); + // skip header + pData += 4; + + // validate extension fits in available space + if ((pData+uExtensionLength) > pDataEnd) + { + NetPrintf(("protossl: extension length %d too big\n", uExtensionLength)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_DECODE_ERROR); + return(ST_FAIL_SETUP); + } + + // check for specific extension parsing + if ((uExtensionParse != SSL_EXTN_ALL) && (uExtensionParse != uExtensionType)) + { + continue; + } + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: parsing extension %s (%d) with length %d\n", _ProtoSSLHelloExtnGetName(uExtensionType), uExtensionType, uExtensionLength)); + + // skip extensions without data + if (uExtensionLength == 0) + { + continue; + } + + #if DEBUG_RAW_DATA + NetPrintMem(pData, uExtensionLength, "extension data"); + #endif + + // parse extensions only clients send + if (pState->bServer) + { + if (uExtensionType == SSL_EXTN_ELLIPTIC_CURVES) + { + iResult = _ProtoSSLHelloExtnParseEllipticCurves(pState, pData, pDataEnd); + } + } + + // parse ALPN extension + if (uExtensionType == SSL_EXTN_ALPN) + { + iResult = _ProtoSSLHelloExtnParseAlpn(pState, pData, pDataEnd); + } + + // the following extensions are only parsed if this is a tls1.2 or greater connection request + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + continue; + } + + // parse supported_versions extension only if tls1.3 is enabled, hello version is 1.2, and we are explicitly parsing for it + if ((pState->uSslVersion >= SSL3_TLS1_3) && (pSecure->uSslVersion == SSL3_TLS1_2) && (uExtensionType == SSL_EXTN_SUPPORTED_VERSIONS) && (uExtensionParse == uExtensionType)) + { + iResult = _ProtoSSLHelloExtnParseSupportedVersions(pState, pData, pDataEnd); + } + + // parse SignatureAlgorithms + if (uExtensionType == SSL_EXTN_SIGNATURE_ALGORITHMS) + { + iResult = _ProtoSSLHelloExtnParseSignatureAlgorithms(pState, pData, pDataEnd); + } + if (uExtensionType == SSL_EXTN_RENEGOTIATION_INFO) + { + iResult = _ProtoSSLHelloExtnParseRenegotiationInfo(pState, pData, pDataEnd); + } + + // the following extensions are only parsed if this is a tls1.3 connection request + if (pSecure->uSslVersion < SSL3_TLS1_3) + { + continue; + } + + // parse cookie extension + if (uExtensionType == SSL_EXTN_COOKIE) + { + iResult = _ProtoSSLHelloExtnParseCookie(pState, pData, pDataEnd); + } + // parse KeyShare extnesion + if (uExtensionType == SSL_EXTN_KEY_SHARE) + { + iResult = _ProtoSSLHelloExtnParseKeyShare(pState, pData, pDataEnd); + } + // parse PreSharedKey extnesion + if (uExtensionType == SSL_EXTN_PRE_SHARED_KEY) + { + iResult = _ProtoSSLHelloExtnParsePreSharedKey(pState, pData, pDataEnd); + } + } + + return(iResult); +} + +/* + handshaking +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendClientHello + + \Description + Send ClientHello handshake packet; ref http://tools.ietf.org/html/rfc5246#section-7.4.1.2 + for TLS1.2 and https://tools.ietf.org/html/rfc8446#section-4.1.2 for TLS1.3 + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_HELLO, ST3_RECV_HELLO, ST_FAIL_SETUP) + + \Version 03/15/2013 (jbrookes) Added session resume support +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendClientHello(ProtoSSLRefT *pState) +{ + int32_t iCipher, iNumCiphers, iBodyLen; + uint8_t strHead[4], strBody[2048]; + uint8_t *pData = strBody, *pCiphSize; + SecureStateT *pSecure = pState->pSecure; + SessionHistoryT SessHist; + uint32_t uHelloExtn = pState->uHelloExtn, uEnabledCiphers = pState->uEnabledCiphers; + + // if we haven't picked an elliptic curve, pick our default + if ((pSecure->pEllipticCurve == NULL) && (pState->iCurveDflt != -1)) + { + pSecure->pEllipticCurve = _ProtoSSLChooseCurve(pState->uEnabledCurves, pState->iCurveDflt); + } + + // for tls1.3 start ecdhe key generation here so we can spread it out + if ((pState->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && (pSecure->pEllipticCurve != NULL)) + { + const CryptCurveDhT *pEcc; + uint32_t uCryptUsecs; + + // initialize elliptic curve context if not already initialized + if ((pEcc = _ProtoSSLEccInitContext(pSecure, pSecure->pEllipticCurve)) == NULL) + { + /* if we cannot find the dh functions, there is some configuration mishap or the server is faulty. + let's fail early here so we can debug the issue instead of a null pointer exception */ + return(ST_FAIL_SETUP); + } + // generate the public key + if (pEcc->Public(pSecure->EccContext, NULL, &uCryptUsecs) > 0) + { + return(ST3_SEND_HELLO); + } + pSecure->uPubKeyLength = pEcc->PointFinal(pSecure->EccContext, NULL, FALSE, pSecure->PubKey, sizeof(pSecure->PubKey)); + pSecure->bEccKeyGenerated = TRUE; + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (generate public key for server key message) %dms\n", + uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + } + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send ClientHello\n")); + + // initialize the ssl performance timer + pSecure->uTimer = 0; + + // reset client cert level (server will let us know if we need to send one) + pState->iClientCertLevel = 0; + + // set desired ssl version + pSecure->uSslVersion = pState->uSslVersion; + // remember requested version; this is later used to detect a version rollback attack + pSecure->uSslClientVersion = pState->uSslVersion; + + // set TLS1.3 specific options + if (pSecure->uSslClientVersion >= PROTOSSL_VERSION_TLS1_3) + { + // freeze hello version at 1.2 as per https://tools.ietf.org/html/rfc8446#section-4.1.2 + pSecure->uSslClientVersion = PROTOSSL_VERSION_TLS1_2; + // add required elliptic_curves (which TLS1.3 calls supported_groups) + uHelloExtn |= PROTOSSL_HELLOEXTN_ELLIPTIC_CURVES; + // if no tls1.3 ciphers are enabled, do that now + if ((pState->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && !(uEnabledCiphers & PROTOSSL_CIPHER_ALL_13)) + { + uEnabledCiphers |= PROTOSSL_CIPHER_ALL_13; + } + } + *pData++ = (uint8_t)(pSecure->uSslClientVersion>>8); + *pData++ = (uint8_t)(pSecure->uSslClientVersion>>0); + + // set random data + if (pState->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + // TLS1.2 and prior specify first four bytes of client random are utc time + uint32_t uUtcTime = (uint32_t)ds_timeinsecs(); + pSecure->ClientRandom[0] = (uint8_t)(uUtcTime >> 24); + pSecure->ClientRandom[1] = (uint8_t)(uUtcTime >> 16); + pSecure->ClientRandom[2] = (uint8_t)(uUtcTime >> 8); + pSecure->ClientRandom[3] = (uint8_t)(uUtcTime >> 0); + CryptRandGet(&pSecure->ClientRandom[4], sizeof(pSecure->ClientRandom) - 4); + } + else + { + // TLS1.3 has 32 bytes of random data + CryptRandGet(pSecure->ClientRandom, sizeof(pSecure->ClientRandom)); + } + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->ClientRandom, sizeof(pSecure->ClientRandom), "ClientRandom"); + #endif + // set client random in packet + ds_memcpy(pData, pSecure->ClientRandom, sizeof(pSecure->ClientRandom)); + pData += 32; + + // if we have a previous session for this peer and resume is enabled, set it + if ((_SessionHistoryGetInfo(&SessHist, pState->strHost, SockaddrInGetPort(&pState->PeerAddr), NULL) != NULL) && pState->bSessionResumeEnabled) + { + SessionInfoT *pSessInfo = &SessHist.SessionInfo; + NetPrintfVerbose((DEBUG_RES_SESS, 0, "protossl: setting session id for resume\n")); + #if DEBUG_RES_SESS && DEBUG_RAW_DATA + NetPrintMem(pSessInfo->SessionId, sizeof(pSessInfo->SessionId), "ClientHello session id"); + #endif + /* save session id and master secret; the sessionid will be compared when we + receive the serverhello to determine if we are in the resume flow or not */ + ds_memcpy(pSecure->SessionId, pSessInfo->SessionId, sizeof(pSecure->SessionId)); + ds_memcpy(pSecure->MasterKey, pSessInfo->MasterSecret, sizeof(pSecure->MasterKey)); + pSecure->bSentSessionId = TRUE; + } + else if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.2 a tls 1.3 client MUST always send + a non-empty sessionID in support of middlebox compatibility mode */ + if (!pSecure->bSentSessionId) + { + /* generate a new session id if we haven't sent one already; otherwise, in an HRR flow we + need to resend the same sessionid as we did the first time */ + CryptRandGet(pSecure->SessionId, sizeof(pSecure->SessionId)); + pSecure->bSentSessionId = TRUE; + } + } + else + { + pSecure->bSentSessionId = FALSE; + } + + // add sessionid to ClientHello + if (pSecure->bSentSessionId) + { + *pData++ = (uint8_t)sizeof(pSecure->SessionId); + ds_memcpy(pData, pSecure->SessionId, sizeof(pSecure->SessionId)); + pData += sizeof(pSecure->SessionId); + } + else // add in empty session identifier + { + *pData++ = 0; + } + + // add the cipher suite list + *pData++ = 0; + pCiphSize = pData++; // save and skip cipher size location + for (iCipher = 0, iNumCiphers = 0, pSecure->uSentCiphers = 0; iCipher < (signed)(sizeof(_SSL3_CipherSuite)/sizeof(_SSL3_CipherSuite[0])); iCipher += 1) + { + // skip ciphers that require a newer version of SSL than we are offering + if (_SSL3_CipherSuite[iCipher].uMinVers > pState->uSslVersion) + { + continue; + } + // skip disabled ciphers + if ((_SSL3_CipherSuite[iCipher].uId & uEnabledCiphers) == 0) + { + continue; + } + // add cipher to list + *pData++ = (uint8_t)(_SSL3_CipherSuite[iCipher].uIdent>>8); + *pData++ = (uint8_t)(_SSL3_CipherSuite[iCipher].uIdent>>0); + iNumCiphers += 1; + // remember we sent it + pSecure->uSentCiphers |= _SSL3_CipherSuite[iCipher].uId; + // add the elliptic curve extension if we are using any ecdhe ciphers + if (_SSL3_CipherSuite[iCipher].uKey == SSL3_KEY_ECDHE) + { + uHelloExtn |= PROTOSSL_HELLOEXTN_ELLIPTIC_CURVES; + } + } + // make sure we selected at least one cipher + if (iNumCiphers == 0) + { + NetPrintf(("protossl: no ciphers selected in ClientHello\n")); + return(ST_FAIL_SETUP); + } + // write cipher suite list size + *pCiphSize = iNumCiphers*2; + + // add compression_methods list with only null compression (we don't support compression) + *pData++ = 1; + *pData++ = 0; + + // add extensions + if (uHelloExtn != 0) + { + pData = _ProtoSSLAddHelloExtensions(pState, pData, (uint32_t)sizeof(strBody)-(uint32_t)(pData-strBody), strBody, uHelloExtn); + } + + // setup the header + iBodyLen = pData-strBody; + strHead[0] = SSL3_MSG_CLIENT_HELLO; + strHead[1] = 0; + strHead[2] = (uint8_t)(iBodyLen>>8); + strHead[3] = (uint8_t)(iBodyLen>>0); + + // if in hrr flow, make sure we're not sending the same clienthello we sent last time + if (pSecure->uSslVersion >= SSL3_TLS1_3) + { + uint8_t aClientHelloHash[sizeof(pSecure->aClientHelloHash)]; + // hash clienthello for possible hrr compare + _ProtoSSLGenerateFingerprint(aClientHelloHash, sizeof(aClientHelloHash), strBody, iBodyLen, CRYPTHASH_SHA256); + // if in hrr flow, compare + if ((pSecure->bHelloRetry) && (!memcmp(aClientHelloHash, pSecure->aClientHelloHash, sizeof(aClientHelloHash)))) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.4 sending the same clienthello twice + must generate an illegal_parameter alert */ + NetPrintf(("protossl: sending the same ClientHello twice\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + + } + // copy hello hash to state for possible future compare + ds_memcpy_s(pSecure->aClientHelloHash, sizeof(pSecure->aClientHelloHash), aClientHelloHash, sizeof(aClientHelloHash)); + } + + // set starting handshake message + pSecure->iCurMsg = SSL3_MSG_CLIENT_HELLO; + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, 4, strBody, iBodyLen); + return(ST3_RECV_HELLO); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvClientHello + + \Description + Process ClientHello handshake packet + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_SEND_HELLO, ST3_SEND_HELLO_RETRY, or ST_FAIL_* on error) + + \Version 10/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvClientHello(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iNumCiphers, iParseResult; + const uint8_t *pDataEnd = pData + iDataSize; + const uint8_t *pCipherList; + SessionHistoryT SessHist; + uint32_t uEnabledCiphers = pState->uEnabledCiphers, uPreferredCipher=0, uSessionSize; + uint8_t aSessionId[SSL_SESSID_SIZE], uCompression, uCompressionMethods; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process ClientHello\n")); + + // get protocol version client wants from us; make sure it is at most TLS1.2, as that is the highest allowed version for this field + if ((pSecure->uSslClientVersion = pSecure->uSslVersion = _SafeRead16(pData, pDataEnd)) > PROTOSSL_VERSION_TLS1_2) + { + NetPrintf(("protossl: invalid ssl version %d in client hello\n", pSecure->uSslClientVersion)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_CONN); + } + pData += 2; + + // save client random data + _SafeReadBytes(pSecure->ClientRandom, sizeof(pSecure->ClientRandom), pData, sizeof(pSecure->ClientRandom), pDataEnd); + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->ClientRandom, sizeof(pSecure->ClientRandom), "ClientRandom"); + #endif + pData += 32; + + // check for possible session resume + uSessionSize = _SafeRead8(pData++, pDataEnd); + _SafeReadBytes(aSessionId, sizeof(aSessionId), pData, SSL_SESSID_SIZE, pDataEnd); + pData += uSessionSize; + + // read cipher suite list size and save cipher list pointer (we will parse it after extensions, if present) + iNumCiphers = _SafeRead16(pData, pDataEnd) / 2; + pData += 2; + pCipherList = pData; + // skip cipher list + pData += iNumCiphers*2; + + // save and skip compression_methods + uCompressionMethods = _SafeRead8(pData++, pDataEnd); + // read first method; allows us to later check for one null method if we have a tls1.3 connection + uCompression = _SafeRead8(pData, pDataEnd); + pData += uCompressionMethods; + + // parse hello extensions for supported_versions first; this allows extension parsers to know if the connection will be tls1.3 or not + if ((iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pDataEnd, SSL_EXTN_SUPPORTED_VERSIONS)) != 0) + { + return(iParseResult); + } + + // pick protocol version; this must be done after possible supported_versions extension parsing + if (pSecure->uSslVersion > pState->uSslVersion) + { + NetPrintfVerbose((pState->iVerbose, 1, "protossl: client requested SSL version %d.%d, downgrading to our max supported version\n", pData[0], pData[1])); + pSecure->uSslVersion = pState->uSslVersion; + } + else if (pSecure->uSslVersion < pState->uSslVersionMin) + { + NetPrintf(("protossl: client requested SSL version %d.%d is not supported\n", pData[0], pData[1])); + /* As per http://tools.ietf.org/html/rfc5246#appendix-E.1, if server supports (or is willing to use) + only versions greater than client_version, it MUST send a "protocol_version" alert message and + close the connection. */ + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_PROTOCOL_VERSION); + return(ST_FAIL_CONN_MINVERS); + } + NetPrintfVerbose((pState->iVerbose, 0, "protossl: using %s\n", _SSL3_strVersionNames[pSecure->uSslVersion&0xff])); + + // parse all other extensions + if ((iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pDataEnd, SSL_EXTN_ALL)) != 0) + { + return(iParseResult); + } + + // check for session resume info + if (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + if (pState->bSessionResumeEnabled) + { + // get info for possible resume + if ((uSessionSize == sizeof(pSecure->SessionId)) && _SessionHistoryGetInfo(&SessHist, NULL, 0, aSessionId)) + { + // flag that we got the info + pSecure->bSessionResume = TRUE; + // save cipher preference + uPreferredCipher = SessHist.SessionInfo.uCipherId; + } + } + } + else + { + // save session info for later echo as per https://tools.ietf.org/html/rfc8446#section-4.1.3 + if (uSessionSize == sizeof(pSecure->SessionId)) + { + ds_memcpy(pSecure->SessionId, aSessionId, sizeof(pSecure->SessionId)); + pSecure->bSessionResume = TRUE; + } + else + { + pSecure->bSessionResume = FALSE; + } + // if we are in the HelloRetryRequest flow, set the preferred cipher to the cipher we previously selected + if (pSecure->bHelloRetry) + { + uPreferredCipher = pSecure->uRetryCipher; + } + } + + // choose a cipher from cipher list, with optional cipher preference + if ((pSecure->pCipher = _ProtoSSLChooseCipher(pSecure, pState->pCertificate, pCipherList, iNumCiphers, pDataEnd, uEnabledCiphers, uPreferredCipher)) == NULL) + { + NetPrintf(("protossl: no matching cipher\n")); + /* As per http://tools.ietf.org/html/rfc5246#section-7.4.1.3, a client providing no + ciphers we support results in a handshake failure alert */ + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_CONN_NOCIPHER); + } + else + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: using cipher suite %s (ident=%d,%d)\n", pSecure->pCipher->strName, pCipherList[0], pCipherList[1])); + } + + // make sure legacy_compression field includes only null compression if tls 1.3 + if ((pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && ((uCompressionMethods != 1) || (uCompression != 0))) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.2 compression must be disabled; "if a TLS 1.3 ClientHello is + received with any other value in this field, the server MUST abort the handshake with an "illegal_parameter" alert */ + NetPrintf(("protossl: legacy_compression_methods not null for tls 1.3 connection\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_CONN); + } + + // now that we've selected the cipher, check for possible resume + if ((pSecure->uSslVersion < SSL3_TLS1_3) && (pSecure->bSessionResume)) + { + SessionInfoT *pSessInfo = &SessHist.SessionInfo; + if ((pSessInfo->uSslVersion == pSecure->uSslVersion) && (pSessInfo->uCipherId == pSecure->pCipher->uIdent)) + { + ds_memcpy(pSecure->SessionId, aSessionId, sizeof(pSecure->SessionId)); + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->SessionId, sizeof(pSecure->SessionId), "ClientHello session id"); + #endif + NetPrintfVerbose((pState->iVerbose, 0, "protossl: resuming previous session\n")); + // copy session id and master secret + ds_memcpy(pSecure->MasterKey, pSessInfo->MasterSecret, sizeof(pSecure->MasterKey)); + ds_memcpy(pSecure->SessionId, pSessInfo->SessionId, sizeof(pSecure->SessionId)); + } + else + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: no session resume due to cipher or version mismatch\n")); + pSecure->bSessionResume = FALSE; + } + } + + // if no session resume, generate a new sessionid for possible future reuse + if ((pSecure->uSslVersion < SSL3_TLS1_3) && !pSecure->bSessionResume) + { + CryptRandGet(pSecure->SessionId, sizeof(pSecure->SessionId)); + } + + // make sure we found a key share we support + if ((pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) && ((pSecure->pEllipticCurve == NULL) || (pSecure->uPubKeyLength == 0))) + { + // pick default key share + pSecure->pEllipticCurve = _ProtoSSLChooseCurve(pState->uEnabledCurves, pState->iCurveDflt); + return(ST3_SEND_HELLO_RETRY); + } + + // if not tls1.3 and we got a key_share from a 1.3 client, zero it here so we don't think it's a valid key later in the handshake flow + if ((pSecure->uSslVersion < SSL3_TLS1_3) && (pSecure->uPubKeyLength != 0)) + { + pSecure->uPubKeyLength = 0; + } + + return(ST3_SEND_HELLO); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendHelloRetryRequest + + \Description + Send Hello Retry Request (TLS 1.3+) as defined by + https://tools.ietf.org/html/rfc8446#section-4.1.4 + + \Input *pState - module state reference + + \Output + int32_t - ST3_RECV_HELLO + + \Version 08/08/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendHelloRetryRequest(ProtoSSLRefT *pState) +{ + uint8_t strBody[512], *pData = strBody; + uint8_t strHead[4]; + uint16_t uSslVersion; + SecureStateT *pSecure = pState->pSecure; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send HelloRetryRequest\n")); + + // remember we're in HRR flow + pSecure->bHelloRetry = TRUE; + + // inject synthetic message hash (we only need to do tls1.3 hashes here) + _ProtoSSLHandshakeHashInject(pSecure, CRYPTHASH_SHA256); + _ProtoSSLHandshakeHashInject(pSecure, CRYPTHASH_SHA384); + + // add fixed tls1.2 version to message body + uSslVersion = SSL3_TLS1_2; + *pData++ = (uint8_t)(uSslVersion>>8); + *pData++ = (uint8_t)(uSslVersion>>0); + + // add special random value to indicate this is HelloRetryRequest, not a ServerHello + ds_memcpy(pData, _SSL3_HelloRetryRequestRandom, sizeof(_SSL3_HelloRetryRequestRandom)); + pData += 32; + // echo client's sessionid + if (pSecure->bSessionResume) + { + *pData++ = sizeof(pSecure->SessionId); + ds_memcpy(pData, pSecure->SessionId, sizeof(pSecure->SessionId)); + pData += sizeof(pSecure->SessionId); + } + else + { + *pData++ = 0; + } + + // the cipher we send here MUST be chosen in the subsequent ServerHello + pSecure->uRetryCipher = pSecure->pCipher->uIdent; + *pData++ = (uint8_t)(pSecure->uRetryCipher>>8); + *pData++ = (uint8_t)(pSecure->uRetryCipher>>0); + + // set legacy_compression_method + *pData++ = 0; + + // add keyshare extension + pData = _ProtoSSLAddHelloExtensions(pState, pData, (uint32_t)(sizeof(strBody)-(pData-strBody)), strBody, 0); + + // setup the header + strHead[0] = SSL3_MSG_SERVER_HELLO; + strHead[1] = 0; + strHead[2] = (uint8_t)((pData-strBody)>>8); + strHead[3] = (uint8_t)((pData-strBody)>>0); + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, 4, strBody, pData-strBody); + + // return next state + return(ST3_RECV_HELLO); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvHelloRetryRequest + + \Description + Process Hello Retry Request from server (TLS1.3+) as per + https://tools.ietf.org/html/rfc8446#section-4.1.4 + + \Input *pState - module state reference + \Input *pData - pointer to message data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST_SEND_HELLO, or ST_FAIL_* on error) + + \Notes + The HelloRetryRequest masquerades as a ServerHello, but with a specific + ServerRandom value. This function expects to be given data immediately + following the ServerRandom. + + \Version 08/08/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvHelloRetryRequest(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iParseResult; + const uint8_t *pDataEnd = pData + iDataSize; + const CipherSuiteT *pCipher; + uint8_t aSessionId[SSL_SESSID_SIZE]; + uint32_t uSessionLen; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process HelloRetryRequest\n")); + + // validate legacy_version, which must be TLS1.2 + if (pSecure->uSslVersion != PROTOSSL_VERSION_TLS1_2) + { + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + + // as per https://tools.ietf.org/html/rfc8446#section-4.1.4, a second HRR results in a fatal error + if (pSecure->bHelloRetry) + { + NetPrintf(("protossl: received HRR after already receiving an HRR\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_UNEXPECTED_MESSAGE); + return(ST_FAIL_SETUP); + } + + // inject synthetic message hash (we only need to do tls1.3 hashes here) + _ProtoSSLHandshakeHashInject(pState->pSecure, CRYPTHASH_SHA256); + _ProtoSSLHandshakeHashInject(pState->pSecure, CRYPTHASH_SHA384); + + // read legacy session id + if ((uSessionLen = _SafeRead8(pData, pDataEnd)) == SSL_SESSID_SIZE) + { + _SafeReadBytes(aSessionId, sizeof(aSessionId), pData+1, SSL_SESSID_SIZE, pDataEnd); + } + pData += uSessionLen+1; + + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.3, a client receiving a legacy_session_id field that + does not match what it sent in the ClientHello MUST abort the handshake with an "illegal_parameter" alert */ + if (pSecure->bSentSessionId ? memcmp(pSecure->SessionId, aSessionId, sizeof(pSecure->SessionId)) : uSessionLen != 0) + { + NetPrintf(("protossl: received mismatched sessionid\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + + // as per https://tools.ietf.org/html/rfc8446#section-4.1.4, a client receiving a cipher suite that was not offered MUST abort the handshake + if ((pCipher = _ProtoSSLGetCipher(pSecure, _SafeRead16(pData, pDataEnd))) == NULL) + { + NetPrintf(("protossl: received cipher in HelloRetryRequest that we didn't send (ident=%d,%d)\n", pData[0], pData[1])); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + // save cipher for later verification when we receive the ServerHello + pSecure->uRetryCipher = pCipher->uIdent; + pData += 2; + + // skip legacy_compression_method, make sure it's zero + if (_SafeRead8(pData++, pDataEnd)) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.2 compression must be disabled; "if a TLS 1.3 ClientHello is + received with any other value in this field, the server MUST abort the handshake with an "illegal_parameter" alert */ + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_CONN); + } + + // parse hello extensions for supported_versions first; this allows extension parsers to know if the connection will be tls1.3 or not + if ((iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pDataEnd, SSL_EXTN_SUPPORTED_VERSIONS)) != 0) + { + return(iParseResult); + } + // parse the rest of the extensions + if ((iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pDataEnd, SSL_EXTN_ALL)) != 0) + { + return(iParseResult); + } + // remember we're in the HelloRetryRequest flow + pSecure->bHelloRetry = TRUE; + /* if we hit HRR, we assume that we need to make a change to our curve. let's throw + away the context and generated key */ + pSecure->bEccContextInitialized = FALSE; + pSecure->bEccKeyGenerated = FALSE; + + // retry ClientHello + return(ST3_SEND_HELLO); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendServerHello + + \Description + Send ServerHello handshake packet + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_HELLO, ST3_SEND_EXTN, ST3_SEND_CHANGE, + ST3_SEND_CERT, or ST_FAIL_* on error) + + \Version 10/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendServerHello(ProtoSSLRefT *pState) +{ + uint8_t strHead[4]; + uint8_t strBody[512]; + uint8_t *pData = strBody; + SecureStateT *pSecure = pState->pSecure; + uint32_t uSslVersion; + int32_t iNextState; + + // if tls1.3, generate public key (tls1.2 and prior do this in SendServerKeyExchange) + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + const CryptCurveDhT *pEcc; + uint32_t uCryptUsecs; + + // initialize elliptic curve context if not already initialized + if ((pEcc = _ProtoSSLEccInitContext(pSecure, pSecure->pEllipticCurve)) == NULL) + { + /* if we cannot find the dh functions, there is some configuration mishap or the server is faulty. + let's fail early here so we can debug the issue instead of a null pointer exception */ + return(ST_FAIL_SETUP); + } + // generate the public key; public key is extracted and sent to peer in the key_share extension + if (pEcc->Public(pSecure->EccContext, NULL, &uCryptUsecs) > 0) + { + return(ST3_SEND_HELLO); + } + pSecure->bEccKeyGenerated = TRUE; + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (generate public key for server key message) %dms\n", + uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + } + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send ServerHello\n")); + + // tls1.3 fixes hello version number at 1.2 + uSslVersion = (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) ? pSecure->uSslVersion : SSL3_TLS1_2; + + // set negotiated protocol version + *pData++ = (uint8_t)(uSslVersion>>8); + *pData++ = (uint8_t)(uSslVersion>>0); + + // generate and set server random data + CryptRandGet(pSecure->ServerRandom, sizeof(pSecure->ServerRandom)); + // set random downgrade bytes as appropriate, as per https://tools.ietf.org/html/rfc8446#section-4.1.3 + if ((pSecure->uSslVersion == SSL3_TLS1_2) && (pState->uSslVersion >= SSL3_TLS1_3)) + { + ds_memcpy(&pSecure->ServerRandom[24], _SSL3_ServerRandomDowngrade12, sizeof(_SSL3_ServerRandomDowngrade12)); + } + else if ((pSecure->uSslVersion < SSL3_TLS1_2) && (pState->uSslVersion >= SSL3_TLS1_2)) + { + ds_memcpy(&pSecure->ServerRandom[24], _SSL3_ServerRandomDowngrade11, sizeof(_SSL3_ServerRandomDowngrade11)); + } + ds_memcpy(pData, pSecure->ServerRandom, sizeof(pSecure->ServerRandom)); + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->ServerRandom, sizeof(pSecure->ServerRandom), "ServerRandom"); + #endif + pData += 32; + + // add sessionid to ServerHello (TLS1.2 and prior) / echo client's legacy sessionid (TLS1.3) + if (((pSecure->uSslVersion < SSL3_TLS1_3) && pState->bSessionResumeEnabled) || (pSecure->bSessionResume)) + { + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->SessionId, sizeof(pSecure->SessionId), "SessionId"); + #endif + *pData++ = sizeof(pSecure->SessionId); + ds_memcpy(pData, pSecure->SessionId, sizeof(pSecure->SessionId)); + pData += sizeof(pSecure->SessionId); + } + else + { + *pData++ = 0; + } + + // add the selected cipher suite + *pData++ = (uint8_t)(pSecure->pCipher->uIdent>>8); + *pData++ = (uint8_t)(pSecure->pCipher->uIdent>>0); + + // add null compression_method + *pData++ = 0; + + // add extensions; note that for TLS1.3 we only add extensions required to establish secure comms; the rest are sent in EncryptedExtensions + pData = _ProtoSSLAddHelloExtensions(pState, pData, (uint32_t)sizeof(strBody)-(uint32_t)(pData-strBody), strBody, (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) ? pState->uHelloExtn : 0); + + // setup the header + strHead[0] = SSL3_MSG_SERVER_HELLO; + strHead[1] = 0; + strHead[2] = (uint8_t)((pData-strBody)>>8); + strHead[3] = (uint8_t)((pData-strBody)>>0); + + // send the packet + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, 4, strBody, (int32_t)(pData-strBody)); + + // determine next state + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + // send encrypted extensions + iNextState = ST3_SEND_EXTN; + } + else + { + iNextState = pSecure->bSessionResume ? ST3_SEND_CHANGE : ST3_SEND_CERT; + } + return(iNextState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvServerHello + + \Description + Process ServerHello handshake packet + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_HELLO, ST3_RECV_CHANGE, ST3_PROC_ASYNC, + or ST_FAIL_* on error) + + \Notes + Also handles initial parsing of tls1.3 HelloRetryRequest; if an HRR is + identified, processing is handed off to the HRR-specific handler. + + \Version 03/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvServerHello(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + int32_t iParseResult, iState = ST3_RECV_HELLO; + const uint8_t *pDataEnd, *pSessionId=NULL, *pCipher; + SecureStateT *pSecure = pState->pSecure; + const uint8_t *pDataStart = pData; + uint8_t aSessionId[SSL_SESSID_SIZE], uSessionLen; + + /* get the location of server hello end; some servers will not send any extension length + so we need to make sure we don't parse past the end of the packet server hello */ + pDataEnd = pData + iDataSize; + + // get server-specified version of the protocol + pSecure->uSslVersion = _SafeRead16(pData, pDataEnd); + pData += 2; + + // save server random data + _SafeReadBytes(pSecure->ServerRandom, sizeof(pSecure->ServerRandom), pData, sizeof(pSecure->ServerRandom), pDataEnd); + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->ServerRandom, sizeof(pSecure->ServerRandom), "ServerRandom"); + #endif + pData += 32; + + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.3, check for special random value, + indicating this is a HelloRetryRequest and not a ServerHello */ + if (!memcmp(pSecure->ServerRandom, _SSL3_HelloRetryRequestRandom, sizeof(_SSL3_HelloRetryRequestRandom))) + { + return(_ProtoSSLRecvHelloRetryRequest(pState, pData, iDataSize - (pData-pDataStart))); + } + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process Server Hello\n")); + + // read sessionid length + uSessionLen = _SafeRead8(pData++, pDataEnd); + // save pointer to and skip sessionid + pSessionId = pData; + pData += uSessionLen; + + // save cipher offset + pCipher = pData; + pData += 2; + + // validate and skip compression + if (_SafeRead8(pData++, pDataEnd) != 0) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.2 compression must be disabled; "if a TLS 1.3 ClientHello is received + with any other value in this field, the server MUST abort the handshake with an "illegal_parameter" alert. unlike the server + flow, the client flow enforces this restriction unconditionally, as our client does not support compression */ + NetPrintf(("protossl: compression_methods not zero\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_CONN); + } + + // parse hello extensions for supported_versions first; this allows extension parsers to know if the connection will be tls1.3 or not + if ((iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pDataEnd, SSL_EXTN_SUPPORTED_VERSIONS)) != 0) + { + return(iParseResult); + } + + // make sure we support version; this must be done after possible supported_versions extension parsing + if (pSecure->uSslVersion != pState->uSslVersion) + { + if ((pSecure->uSslVersion < pState->uSslVersionMin) || (pSecure->uSslVersion > pState->uSslVersion)) + { + NetPrintf(("protossl: server specified SSL version 0x%04x is not supported\n", pSecure->uSslVersion)); + /* As per http://tools.ietf.org/html/rfc5246#appendix-E.1 - If the version chosen by + the server is not supported by the client (or not acceptable), the client MUST send a + "protocol_version" alert message and close the connection. */ + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_PROTOCOL_VERSION); + return((pSecure->uSslVersion < pState->uSslVersionMin) ? ST_FAIL_CONN_MINVERS : ST_FAIL_CONN_MAXVERS); + } + else + { + NetPrintfVerbose((pState->iVerbose, 1, "protossl: downgrading SSL version\n")); + } + } + NetPrintfVerbose((pState->iVerbose, 0, "protossl: using %s to connect to %s\n", _SSL3_strVersionNames[pSecure->uSslVersion&0xff], pState->strHost)); + + // parse all other extensions + if ((iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pDataEnd, SSL_EXTN_ALL)) != 0) + { + return(iParseResult); + } + + // protect against possible downgrade attack as per https://tools.ietf.org/html/rfc8446#section-4.1.3 + if ((pSecure->uSslVersion == SSL3_TLS1_2) && (pState->uSslVersion >= SSL3_TLS1_3) && !memcmp(&pSecure->ServerRandom[24], _SSL3_ServerRandomDowngrade12, sizeof(_SSL3_ServerRandomDowngrade12))) + { + NetPrintf(("protossl: invalid tls1.3 downgrade detected\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + else if ((pSecure->uSslVersion < SSL3_TLS1_2) && (pState->uSslVersion >= SSL3_TLS1_2) && !memcmp(&pSecure->ServerRandom[24], _SSL3_ServerRandomDowngrade11, sizeof(_SSL3_ServerRandomDowngrade11))) + { + NetPrintf(("protossl: invalid tls1.2 downgrade detected\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + + // get and validate cipher + if ((pSecure->pCipher = _ProtoSSLGetCipher(pSecure, _SafeRead16(pCipher, pDataEnd))) == NULL) + { + NetPrintf(("protossl: no matching cipher (ident=%d,%d)\n", pCipher[0], pCipher[1])); + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.3: a client receiving a cipher suite that was not offered + MUST abort the handshake with an "illegal_parameter" alert */ + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_CONN_NOCIPHER); + } + else + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: using cipher suite %s (ident=%d,%d)\n", pSecure->pCipher->strName, pCipher[0], pCipher[1])); + } + + // if in retry flow, make sure we got the expected cipher as per https://tools.ietf.org/html/rfc8446#section-4.1.4 + if (pSecure->bHelloRetry && (pSecure->pCipher->uIdent != pSecure->uRetryCipher)) + { + NetPrintf(("protossl: cipher specified in ServerHello does not match the cipher from HelloRetryRequest\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + + // read session info + if (uSessionLen == SSL_SESSID_SIZE) + { + _SafeReadBytes(aSessionId, sizeof(aSessionId), pSessionId, SSL_SESSID_SIZE, pDataEnd); + #if DEBUG_RAW_DATA + NetPrintMem(aSessionId, sizeof(aSessionId), "ServerHello session id"); + #endif + } + + // process session id (tls1.2 and previous only) + if (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + // save server session id + if (uSessionLen == sizeof(pSecure->SessionId)) + { + // check for ClientHello/ServerHello session match; a match indicates we are resuming the session and bypassing key exchange + if (!memcmp(pSecure->SessionId, aSessionId, sizeof(pSecure->SessionId))) + { + // set resume and update state + NetPrintfVerbose((pState->iVerbose, 0, "protossl: resuming previous session\n")); + pSecure->bSessionResume = TRUE; + iState = ST3_RECV_CHANGE; + } + else + { + // save session id for possible future resumption + ds_memcpy(pSecure->SessionId, aSessionId, sizeof(pSecure->SessionId)); + } + } + + // clear premaster secret if not resuming + if (!pSecure->bSessionResume) + { + ds_memclr(pSecure->MasterKey, sizeof(pSecure->MasterKey)); + } + } + else if (pSecure->bSentSessionId ? memcmp(pSecure->SessionId, aSessionId, sizeof(pSecure->SessionId)) : uSessionLen != 0) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.1.3, a client receiving a legacy_session_id field that + does not match what it sent in the ClientHello MUST abort the handshake with an "illegal_parameter" alert */ + NetPrintf(("protossl: received mismatched sessionid\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + + // tls1.3+ data is immediately encrypted following ServerHello and keyshare/psk + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + // make sure the server gave us a keyshare we support + if (pSecure->pEllipticCurve == NULL) + { + NetPrintf(("protossl: no supported elliptic curve given in tls1.3 server hello\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_ILLEGAL_PARAMETER); + return(ST_FAIL_SETUP); + } + // update the handshake hash with ServerHello, which hasn't been processed yet + //$$todo - is this actually required? + _ProtoSSLRecvHandshakeFinish(pState); + // set up async execution to build secrets and key material + iState = _ProtoSSLUpdateSetAsyncState(pState, _ProtoSSLBuildHandshakeKey, iState, ST_FAIL_SETUP, SSL3_ALERT_DESC_INTERNAL_ERROR); + } + + // return new state + return(iState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendEncryptedExtensions + + \Description + Send EncryptedExtensions handshake message (TLS1.3+ only) + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_EXTN, ST3_SEND_CERT_REQ or ST3_SEND_CERT) + + \Version 01/26/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendEncryptedExtensions(ProtoSSLRefT *pState) +{ + uint8_t strBody[512], strHead[4], *pData = strBody+2; + uint32_t uExtnLen; + + // data after ServerHello is encrypted + if (_ProtoSSLBuildHandshakeKey(pState) > 0) + { + NetPrintfVerbose((pState->iVerbose, 1, "protossl: generating handshake key\n")); + return(ST3_SEND_EXTN); + } + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send EncryptedExtensions\n")); + + // add extensions to message body + pData = _ProtoSSLAddHelloExtensions(pState, pData, (uint32_t)sizeof(strBody)-2, strBody, pState->uHelloExtn); + uExtnLen = pData - strBody - 2; + // set extn length + strBody[0] = (uint8_t)(uExtnLen>>8); + strBody[1] = (uint8_t)(uExtnLen>>0); + + // setup the header + strHead[0] = SSL3_MSG_ENCRYPTED_EXTENSIONS; + strHead[1] = 0; + strHead[2] = (uint8_t)((uExtnLen+2)>>8); + strHead[3] = (uint8_t)((uExtnLen+2)>>0); + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, 4, strBody, uExtnLen+2); + + // return next state + return((pState->iClientCertLevel > 0) ? ST3_SEND_CERT_REQ : ST3_SEND_CERT); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvEncryptedExtensions + + \Description + Process EncryptedExtensions handshake packet (TLS1.3+ only) + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_FINISH, ST3_RECV_HELLO, or ST_FAIL_* on error) + + \Version 01/26/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvEncryptedExtensions(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iParseResult, iNextState = (pSecure->bSessionResume) ? ST3_RECV_FINISH : ST3_RECV_HELLO; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process EncryptedExtensions\n")); + + // parse the extensions + iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pData+iDataSize, SSL_EXTN_ALL); + + // move to next state + return((iParseResult == 0) ? iNextState : iParseResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateSendCertificate + + \Description + Send Certificate handshake packet + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_KEY, ST3_SEND_VERIFY, ST3_SEND_CERT_REQ, + or ST3_SEND_DONE) + + \Version 10/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendCertificate(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t strHead[4]; + uint8_t strBody[4096]; + int32_t iCertSize, iNumCerts, iBodySize=0, iExtnSize=0; + int32_t iState = (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) ? ST3_SEND_KEY : ST3_SEND_VERIFY; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send Certificate\n")); + + // set certificate_request_context + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + strBody[iBodySize++] = 0; + iExtnSize = 2; + } + + /* if we have a certificate, add it to body $$todo if client we need to validate certificate matches sigalg/cipher, and send an + empty message if it does not. if server we should have already validated that or aborted the connection if it does not */ + if (pState->pCertificate != NULL) + { + iCertSize = pState->pCertificate->iCertSize; + ds_memcpy_s(strBody+iBodySize+6, sizeof(strBody)-iBodySize-6, pState->pCertificate->aCertData, pState->pCertificate->iCertSize); + } + else if (pState->bServer) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.4.2 a server's certificate list MUST not be empty; this is + a general practice in earlier versions but not specifically enforced prior */ + NetPrintf(("protossl: no valid certificate\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_CERT_INVALID); + } + else + { + /* as per https://tools.ietf.org/html/rfc5246#section-7.4.6, a client MUST send an empty certificate_list if it does not have an + appropriate certificate to send in response to the server's authentication request. */ + iCertSize = 0; + // if we're a tls1.3 client skip sending CertificateVerify and go directly to Finished + if (pSecure->uSslVersion >= SSL3_TLS1_3) + { + iState = ST3_SEND_FINISH; + } + } + iNumCerts = (iCertSize) ? 1 : 0; + + // copy the certificate list into message body + strBody[iBodySize++] = 0; + strBody[iBodySize++] = (uint8_t)(((iCertSize+iExtnSize)+(iNumCerts*3))>>8); + strBody[iBodySize++] = (uint8_t)(((iCertSize+iExtnSize)+(iNumCerts*3))>>0); + if (iNumCerts) + { + strBody[iBodySize++] = 0; + strBody[iBodySize++] = (uint8_t)((iCertSize)>>8); + strBody[iBodySize++] = (uint8_t)((iCertSize)>>0); + } + iBodySize += iCertSize; + + /* set Certificate extensions - note that this field is expected after every certificate, so this + code only works when a single certificate is being sent */ + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + strBody[iBodySize++] = 0; + strBody[iBodySize++] = 0; + } + + // setup the header + strHead[0] = SSL3_MSG_CERTIFICATE; + strHead[1] = 0; + strHead[2] = (uint8_t)((iBodySize)>>8); + strHead[3] = (uint8_t)((iBodySize)>>0); + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, 4, strBody, iBodySize); + + /* remember we sent a certificate (since we will send an empty message if no certificate + is available, only remember if we *actually* sent a certificate). this is used later + to determine if we should send a CertificateVerify handshake message */ + pSecure->bSentCert = iNumCerts ? TRUE : FALSE; + + // if we're a server executing a TLS1.2 or prior handshake, override state transition set above + if ((pSecure->uSslVersion <= SSL3_TLS1_2) && (pState->bServer) && (pSecure->pCipher->uKey == SSL3_KEY_RSA)) + { + iState = (pState->iClientCertLevel > 0) ? ST3_SEND_CERT_REQ : ST3_SEND_DONE; + } + return(iState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvCertificate + + \Description + Process Certificate handshake packet; ref http://tools.ietf.org/html/rfc5246#section-7.4.2 + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_HELLO, ST_WAIT_CA, or ST_FAIL_* on error) + + \Version 08/26/2011 (szhu) Added multi-certificate support +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvCertificate(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iCertSize, iCertHeight, iSize1, iSize2, iResult, iVerify; + uint8_t aFingerprintId[SSL_FINGERPRINT_SIZE], bCertCached = FALSE; + X509CertificateT leafCert, prevCert; + #if DIRTYCODE_LOGGING + char strIdentSubject[512], strIdentIssuer[512]; + #endif + const uint8_t *pDataEnd = pData+iDataSize; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process Certificate\n")); + + ds_memclr(&leafCert, sizeof(leafCert)); + ds_memclr(&prevCert, sizeof(prevCert)); + + // skip certificate_request_context + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + uint32_t uContextLen = _SafeRead8(pData, pDataEnd); + NetPrintfVerbose((DEBUG_RAW_DATA, 0, "protossl: certificate_request_context length=%d\n", uContextLen)); + pData += uContextLen + 1; + iDataSize -= uContextLen + 1; + } + + // get outer size + if ((iCertSize = iSize1 = _SafeRead24(pData, pDataEnd)) <= 3) + { + NetPrintf(("protossl: no certificate included in certificate message\n")); + if (!pState->bServer) + { + /* as per https://tools.ietf.org/html/rfc8446#section-4.4.2.4, If the server supplies an empty Certificate message, the client MUST + abort the handshake with a "decode_error" alert. */ + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_DECODE_ERROR); + iResult = ST_FAIL_CERT_NONE; + } + else if (pState->bServer && (pState->iClientCertLevel == 2)) + { + /* as per http://tools.ietf.org/html/rfc5246#section-7.4.6 (TLS 1.2), a client providing an empty certificate response to a certificate + request can either be ignored and treated as unauthenticated, or responded to with a fatal handshake failure. a server + MUST always provide a certificate if the agreed-upon key exchange method uses certificates for authentication + as per https://tools.ietf.org/html/rfc8446#section-4.4.2.4 (TLS 1.3), If the client does not send any certificates (i.e., it sends an empty Certificate message), + the server MAY at its discretion either continue the handshake without client authentication or abort the handshake with a "certificate_required" alert. + Also, if some aspect of the certificate chain was unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its discretion either + continue the handshake (considering the client unauthenticated) or abort the handshake. */ + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, pSecure->uSslVersion < SSL3_TLS1_3 ? SSL3_ALERT_DESC_HANDSHAKE_FAILURE : SSL3_ALERT_DESC_CERTIFICATE_REQUIRED); + iResult = ST_FAIL_CERT_NONE; + } + else + { + iResult = (pSecure->uSslVersion < SSL3_TLS1_3) ? ST3_RECV_HELLO : ST3_RECV_FINISH; + } + return(iResult); + } + else if (iCertSize != (iDataSize-3)) + { + NetPrintf(("protossl: invalid certificate length\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_BAD_CERTFICIATE); + return(ST_FAIL_CERT_INVALID); + } + else + { + // remember we received a certificate in the certificate message + pSecure->bRecvCert = TRUE; + + // generate a fingerprint of entire certificate envelope + _ProtoSSLGenerateFingerprint(aFingerprintId, sizeof(aFingerprintId), pData, iCertSize, CRYPTHASH_SHA1); + + // see if we've already validated this chain + if (_CertValidHistoryCheck(aFingerprintId, iCertSize)) + { + NetPrintfVerbose((DEBUG_VAL_CERT, 0, "protossl: matched certificate fingerprint in certificate validity history\n")); + bCertCached = TRUE; + } + } + + // skip outer size + pData += 3; + + // process certificates + for (iVerify = -1, iCertHeight = 0; (iSize1 > 0) && (iVerify != 0); pData += iSize2, iSize1 -= iSize2) + { + uint64_t uTime; + int32_t iParse; + + // get certificate size + iSize2 = _SafeRead24(pData, pDataEnd); + + // make sure certificate size isn't too large + if (iSize2 > iSize1) + { + NetPrintf(("protossl: _ServerCert: certificate is larger than envelope (%d/%d)\n", iSize1, iSize2)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_BAD_CERTFICIATE); + return(ST_FAIL_CERT_INVALID); + } + + // skip certificate object length + pData += 3; + iSize1 -= 3; + + // parse the certificate + if ((iParse = _AsnParseCertificate(&pSecure->Cert, pData, iSize2)) < 0) + { + NetPrintf(("protossl: _ServerCert: x509 cert invalid (error=%d)\n", iParse)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_BAD_CERTFICIATE); + return(ST_FAIL_CERT_INVALID); + } + #if DEBUG_VAL_CERT + _CertificateDebugPrint(&pSecure->Cert, "parsed certificate"); + #endif + + // skip TLS1.3 certificate extension field + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + const uint8_t *pExtn = pData + iSize2; + uint16_t uExtnLen = _SafeRead16(pExtn, pDataEnd); + iSize2 += uExtnLen + 2; + } + + // allow verification bypass; note this must come after first certificate is parsed since we need it later for key exchange + if (pState->bAllowAnyCert) + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: bypassing certificate verify (disabled)\n")); + return(ST3_RECV_HELLO); + } + + // validate date range + if ((uTime = ds_timeinsecs()) != 0) + { + if ((uTime < pSecure->Cert.uGoodFrom) || (uTime > pSecure->Cert.uGoodTill)) + { + #if DIRTYCODE_LOGGING + struct tm TmTime; + char strTime[32]; + _CertificateDebugPrint(&pSecure->Cert, "certificate failed date range validation:"); + NetPrintf(("protossl: from: %s\n", ds_timetostr(ds_secstotime(&TmTime, pSecure->Cert.uGoodFrom), TIMETOSTRING_CONVERSION_ISO_8601, FALSE, strTime, sizeof(strTime)))); + NetPrintf(("protossl: till: %s\n", ds_timetostr(ds_secstotime(&TmTime, pSecure->Cert.uGoodTill), TIMETOSTRING_CONVERSION_ISO_8601, FALSE, strTime, sizeof(strTime)))); + #endif + iVerify = SSL_ERR_CERT_INVALIDDATE; + pSecure->bDateVerifyFailed = TRUE; + } + } + + // processing specific to first (leaf) certificate + if (iCertHeight == 0) + { + // ensure certificate was issued to this host (server certificates only) + if (!pState->bServer && (_CertificateMatchHostname(pState->strHost, pSecure->Cert.Subject.strCommon) != 0)) + { + // check against alternative name(s), if present + if (_CertificateMatchSubjectAlternativeName(pState->strHost, pSecure->Cert.pSubjectAlt, pSecure->Cert.iSubjectAltLen) != 0) + { + NetPrintf(("protossl: _ServerCert: certificate not issued to this host (%s != %s)\n", pState->strHost, pSecure->Cert.Subject.strCommon)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_CERTIFICATE_UNKNOWN); + _CertificateDebugPrint(&pSecure->Cert, "cert"); + _CertificateSetFailureInfo(pState, &pSecure->Cert, FALSE); + return(ST_FAIL_CERT_HOST); + } + } + // save leaf cert as we will need it later for handshake + ds_memcpy(&leafCert, &pSecure->Cert, sizeof(leafCert)); + // debug display of leaf certificate signature algorithm and signature length + NetPrintfVerbose((pState->iVerbose, 0, "protossl: sigalg=%s, siglen=%d\n", _SSL3_strSignatureTypes[pSecure->Cert.iSigType-ASN_OBJ_RSA_PKCS_MD5], pSecure->Cert.iSigSize*8)); + + // if we cached this cert chain allow bypass; this must come after we've verified the hostname matches + if (bCertCached) + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: bypassing certificate verify (cached)\n")); + iVerify = 0; + break; + } + } + else + { + // check to see if the cert is the next cert in the trust chain anchored by the leaf certificate + if ((_CertificateCompareIdent(&pSecure->Cert.Subject, &prevCert.Issuer, prevCert.iCertIsCA) != 0) || + ((pSecure->Cert.iKeyModSize != prevCert.iSigSize) && (pSecure->Cert.iKeyType != ASN_OBJ_ECDSA_KEY))) + { + // not part of our chain, so skip it + _CertificateDebugPrint(&pSecure->Cert, "_ServerCert: certificate not part of chain, skipping:"); + continue; + } + + // check basic constraints (CA) + if (pSecure->Cert.iCertIsCA == FALSE) + { + continue; + } + + // check basic constraints (pathLenConstraints) + if ((pSecure->Cert.iMaxHeight != 0) && (iCertHeight > pSecure->Cert.iMaxHeight)) + { + continue; + } + + // verify previous cert's signature with current cert's public key + if ((_ProtoSSLVerifyCertificateSignature(pSecure, &prevCert, pSecure->Cert.KeyModData, pSecure->Cert.iKeyModSize, + pSecure->Cert.KeyExpData, pSecure->Cert.iKeyExpSize, pSecure->Cert.iKeyType, pSecure->Cert.iCrvType)) != 0) + { + continue; + } + + NetPrintfVerbose((pState->iVerbose, 0, "protossl: cert (%s) verified by ca (%s)\n", _CertificateDebugFormatIdent(&prevCert.Subject, strIdentSubject, sizeof(strIdentSubject)), + _CertificateDebugFormatIdent(&prevCert.Issuer, strIdentIssuer, sizeof(strIdentIssuer)))); + } + + // track cert height + iCertHeight += 1; + // force _VerifyCertificate to update pState->CertInfo on cert failure + pState->bCertInfoSet = FALSE; + // see if we can verify against a trust anchor; a success here means we are done parsing the certificate chain, whether there are more certificates or not + iVerify = _ProtoSSLVerifyCertificate(pState, pSecure, &pSecure->Cert, FALSE); + + // save current cert for validation of next cert + ds_memcpy(&prevCert, &pSecure->Cert, sizeof(prevCert)); + } + + // process verification result + if (iVerify != 0) + { + #if DIRTYCODE_LOGGING + if (iVerify == SSL_ERR_GOSCA_INVALIDUSE) + { + NetPrintf(("protossl: gos ca (%s) may not be used for non-ea domain %s\n", _CertificateDebugFormatIdent(&pSecure->Cert.Issuer, strIdentSubject, sizeof(strIdentSubject)), + pSecure->Cert.Subject.strCommon)); + } + else if (iVerify == SSL_ERR_CAPROVIDER_INVALID) + { + NetPrintf(("protossl: ca (%s) may not be used as a ca provider\n", _CertificateDebugFormatIdent(&pSecure->Cert.Issuer, strIdentSubject, sizeof(strIdentSubject)))); + } + else + { + _CertificateDebugPrint(&pSecure->Cert, "_VerifyCertificate() failed -- no CA available for this certificate"); + NetPrintf(("protossl: _ServerCert: x509 cert untrusted (error=%d)\n", iVerify)); + } + #endif + + // check if we need to fetch CA + if ((iVerify != SSL_ERR_GOSCA_INVALIDUSE) && (_ProtoSSLInitiateCARequest(pState) == 0)) + { + iResult = ST_WAIT_CA; + } + else + { + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_UNKNOWN_CA); + iResult = ST_FAIL_CERT_NOTRUST; + } + } + else if (pSecure->bDateVerifyFailed) + { + // an invalid date is not flagged as a verification failure, so we check the flag directly here + NetPrintf(("protossl: _ServerCert: failed date validation\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_CERTIFICATE_EXPIRED); + iResult = ST_FAIL_CERT_BADDATE; + } + else + { + // cert chain is valid + NetPrintfVerbose((pState->iVerbose, 0, "protossl: cert (%s) verified by ca (%s)\n", _CertificateDebugFormatIdent(&pSecure->Cert.Subject, strIdentSubject, sizeof(strIdentSubject)), + _CertificateDebugFormatIdent(&pSecure->Cert.Issuer, strIdentIssuer, sizeof(strIdentIssuer)))); + + // process next handshake packet + iResult = ST3_RECV_HELLO; + // add cert fingerprint to cert validation history if it's not already there + if (!bCertCached) + { + _CertValidHistoryAdd(aFingerprintId, iCertSize); + } + } + // restore the leaf cert for use during handshake + if ((iCertHeight != 0) && (iResult != ST_FAIL_CERT_NOTRUST)) + { + ds_memcpy(&pSecure->Cert, &leafCert, sizeof(pSecure->Cert)); + } + + // still in hello state (if validated) + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendServerKeyExchange + + \Description + Send the ServerKeyExchange message (only for ECDHE key exchange) + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_KEY, ST3_SEND_CERT_REQ, ST3_SEND_DONE + or ST_FAIL_SETUP on error) + + \Version 03/03/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendServerKeyExchange(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t aBody[1024], aHead[4]; + uint8_t *pData = aBody; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send ServerKeyExchange\n")); + + // if using RSA for key exchange we do not send this message, treat as error + if (pSecure->pCipher->uKey == SSL3_KEY_RSA) + { + NetPrintf(("protossl: _SendServerKey: wrong key exchange algorithm RSA")); + return(ST_FAIL_SETUP); + } + if (pSecure->pSigScheme == NULL) + { + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + pSecure->pSigScheme = _ProtoSSLGetSignatureScheme((pSecure->pCipher->uSig == SSL3_SIGALG_RSA) ? SSL3_SIGSCHEME_RSA_PKCS1_SHA1 : SSL3_SIGSCHEME_ECDSA_SHA1); + } + else + { + /* if we failed to find a matching signature algorithm and the key exchange uses ecdhe we fail + as we won't be able to match a signature algorithm to sign with */ + NetPrintf(("protossl: _SendServerKey: no valid signature algorithm needed for key exchange\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_SETUP); + } + } + // make sure we found a supported elliptic curve if we are using ECC cipher suite + if (pSecure->pEllipticCurve == NULL) + { + NetPrintf(("protossl: _SendServerKey: no matching elliptic curve when using ecc cipher suite\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + /* when using ECC cipher suite make sure that we have a suitable elliptic curve. + we are allowed to try a different cipher suite but I do not believe it is worth + the hassle of reparsing. + note: ECDHE-ECDSA is allowed to use a different curve for the ephemeral ECDH key + in the ServerKeyExchange message */ + return(ST_FAIL_SETUP); + } + + /* generate public key + elliptic curve generation takes multiple frames + so stay in the same state until the operation is complete */ + if (!pSecure->bEccKeyGenerated) + { + const CryptCurveDhT *pEcc; + uint32_t uCryptUsecs; + + // initialize elliptic curve context if not already initialized + if ((pEcc = _ProtoSSLEccInitContext(pSecure, pSecure->pEllipticCurve)) == NULL) + { + /* if we cannot find the dh functions, there is some configuration mishap or the server is faulty. + let's fail early here so we can debug the issue instead of a null pointer exception */ + return(ST_FAIL_SETUP); + } + // generate the public key + if (pEcc->Public(pSecure->EccContext, NULL, &uCryptUsecs) > 0) + { + return(ST3_SEND_KEY); + } + pSecure->uPubKeyLength = pEcc->PointFinal(pSecure->EccContext, NULL, FALSE, pSecure->PubKey, sizeof(pSecure->PubKey)); + pSecure->bEccKeyGenerated = TRUE; + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (generate public key for server key message) %dms\n", + uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + } + + // setup the data that needed to be hashed + *pData++ = SSL3_CURVETYPE_NAMED_CURVE; + *pData++ = (uint8_t)(pSecure->pEllipticCurve->uIdent>>8); + *pData++ = (uint8_t)(pSecure->pEllipticCurve->uIdent>>0); + *pData++ = pSecure->uPubKeyLength; + ds_memcpy_s(pData, pSecure->uPubKeyLength, pSecure->PubKey, sizeof(pSecure->PubKey)); + pData += pSecure->uPubKeyLength; + + // setup the encryption for the server key exchange signature + if (!pSecure->bSigGenerated) + { + CryptHashTypeE eHashType = _SSL3_HashIdToCrypt[pSecure->pSigScheme->SigAlg.uHashId]; + const CryptHashT *pHash; + + // calculate signature + if ((pHash = CryptHashGet(eHashType)) != NULL) + { + uint8_t aSigObj[SSL_SIG_MAX], aHashDigest[CRYPTHASH_MAXDIGEST]; + int32_t iPrivateKeySize, iHashSize, iSigSize; + X509PrivateKeyT PrivateKey; + + // decode private key, extract key modulus and private exponent + if ((iPrivateKeySize = _CertificateDecodePrivate(pState->pPrivateKey, pState->iPrivateKeyLen, &PrivateKey)) < 0) + { + NetPrintf(("protossl: unable to decode private key\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_INTERNAL_ERROR); + return(ST_FAIL_SETUP); + } + + // generate signature hash and encode (where appropriate) to create signature hash object + if ((pSecure->uSslVersion < SSL3_TLS1_2) && (pSecure->pCipher->uSig != SSL3_SIGALG_ECDSA)) + { + iSigSize = _ProtoSSLGenerateSignatureHash(pSecure, CryptHashGet(CRYPTHASH_MD5), aSigObj, aBody, (uint32_t)(pData-aBody)); + iSigSize += _ProtoSSLGenerateSignatureHash(pSecure, CryptHashGet(CRYPTHASH_SHA1), aSigObj+iSigSize, aBody, (uint32_t)(pData-aBody)); + } + else + { + iHashSize = _ProtoSSLGenerateSignatureHash(pSecure, pHash, aHashDigest, aBody, (uint32_t)(pData-aBody)); + if (pSecure->pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PKCS1) + { + iSigSize = _AsnWriteDigitalHashObject(aSigObj, sizeof(aSigObj), aHashDigest, iHashSize, eHashType); + } + else if (pSecure->pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PSS) + { + iSigSize = _Pkcs1EncodeEMSA_PSS(aSigObj, PrivateKey.Modulus.iObjSize, aHashDigest, iHashSize, eHashType); + } + else // ecdsa signatures have no additional encoding + { + ds_memcpy_s(aSigObj, sizeof(aSigObj), aHashDigest, iHashSize); + iSigSize = iHashSize; + } + } + + // start async signature generation + return(_ProtoSSLGenerateSignature(pState, pSecure, pSecure->pSigScheme, aSigObj, iSigSize, &PrivateKey, ST3_SEND_KEY)); + } + } + + // signature is ready, format the message + if (pSecure->uSslVersion >= SSL3_TLS1_2) + { + *pData++ = (uint8_t)(pSecure->pSigScheme->uIdent>>8); + *pData++ = (uint8_t)(pSecure->pSigScheme->uIdent>>0); + } + *pData++ = (uint8_t)(pSecure->SigVerify.iSigSize>>8); + *pData++ = (uint8_t)(pSecure->SigVerify.iSigSize>>0); + ds_memcpy(pData, pSecure->SigVerify.aSigData, pSecure->SigVerify.iSigSize); + pData += pSecure->SigVerify.iSigSize; + + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->SigVerify.aSigData, pSecure->SigVerify.iSigSize, "encrypted server key exchange signature"); + #endif + + // format the packet header + aHead[0] = SSL3_MSG_SERVER_KEY; + aHead[1] = 0; + aHead[2] = (uint8_t)((pData-aBody)>>8); + aHead[3] = (uint8_t)((pData-aBody)>>0); + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, aHead, sizeof(aHead), aBody, (int32_t)(pData-aBody)); + return((pState->iClientCertLevel > 0) ? ST3_SEND_CERT_REQ : ST3_SEND_DONE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvServerKeyExchange + + \Description + Process ServerKeyExchange handshake packet; not used with RSA cipher suites + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_HELLO, ST_PROC_ASYNC, or ST_FAIL_SETUP on error) + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvServerKeyExchange(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + int32_t iIndex; + SecureStateT *pSecure = pState->pSecure; + CryptHashTypeE eHashType; + const CryptHashT *pHash; + const uint8_t *pBegin = pData, *pDataEnd = pData+iDataSize, *pSigData; + const SignatureSchemeT *pSigScheme; + int32_t iNextState = ST3_RECV_HELLO; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process ServerKeyExchange\n")); + + // if using RSA for key exchange we do not receive this message, treat as error + if (pSecure->pCipher->uKey == SSL3_KEY_RSA) + { + NetPrintf(("protossl: _ServerKey: wrong key exchange algorithm RSA\n")); + return(ST_FAIL_SETUP); + } + // make sure it is using the curve type we support + if ((_SafeRead8(pData, pDataEnd) == SSL3_CURVETYPE_NAMED_CURVE) && (pData+3 <= pDataEnd)) + { + // validate the elliptic curve sent by the server + for (iIndex = 0; iIndex < (signed)(sizeof(_SSL3_EllipticCurves)/sizeof(*_SSL3_EllipticCurves)); iIndex += 1) + { + // skip non-matching elliptic curve + if ((pData[1] != (uint8_t)(_SSL3_EllipticCurves[iIndex].uIdent >> 8)) || (pData[2] != (uint8_t)(_SSL3_EllipticCurves[iIndex].uIdent))) + { + continue; + } + // found an elliptic curve + pSecure->pEllipticCurve = &_SSL3_EllipticCurves[iIndex]; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: _ServerKey: using named curve %s (ident=0x%04x)\n", pSecure->pEllipticCurve->strName, pSecure->pEllipticCurve->uIdent)); + break; + } + pData += 3; + } + // make sure the server sent us one of our supported elliptic curves + if (pSecure->pEllipticCurve == NULL) + { + NetPrintf(("protossl: _ServerKey: no matching named curve when using ecdhe key exchange\n")); + return(ST_FAIL_SETUP); + } + + // copy the public key + pSecure->uPubKeyLength = _SafeRead8(pData++, pDataEnd); + _SafeReadBytes(pSecure->PubKey, sizeof(pSecure->PubKey), pData, pSecure->uPubKeyLength, pDataEnd); + pData += pSecure->uPubKeyLength; + + // if we attempted a TLS 1.3 connection but were downgraded, reset the ecc parameters + if (pState->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + pSecure->bEccContextInitialized = FALSE; + pSecure->bEccKeyGenerated = FALSE; + } + + // get signature scheme + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + // EC ciphers in TLS versions prior to 1.2 do not include their signature algorithm in the packet data and are assumed to be SHA1 + pSigScheme = _ProtoSSLGetSignatureScheme(pSecure->Cert.iKeyType == ASN_OBJ_ECDSA_KEY ? SSL3_SIGSCHEME_ECDSA_SHA1 : SSL3_SIGSCHEME_RSA_PKCS1_SHA1); + pSigData = pData; + } + else + { + if ((pSigScheme = _ProtoSSLGetSignatureScheme(_SafeRead16(pData, pDataEnd))) == NULL) + { + NetPrintf(("protossl: _ServerKey: unsupported signature scheme or signature algorithm\n")); + return(ST_FAIL_SETUP); + } + pSigData = pData+2; + } + // get signature hash + eHashType = _SSL3_HashIdToCrypt[pSigScheme->SigAlg.uHashId]; + + // calculate and verify the signature + if ((pHash = CryptHashGet(eHashType)) != NULL) + { + uint8_t aHashDigest[CRYPTHASH_MAXDIGEST]; + int32_t iHashSize, iSigSize = _SafeRead16(pSigData, pDataEnd); + pSigData += 2; + + // create signature hash + if ((pSecure->uSslVersion < SSL3_TLS1_2) && (pSigScheme->uIdent != SSL3_SIGSCHEME_ECDSA_SHA1)) + { + // ecdhe-rsa-sha cipher suites + iHashSize = _ProtoSSLGenerateSignatureHash(pSecure, CryptHashGet(CRYPTHASH_MD5), aHashDigest, pBegin, (uint32_t)(pData-pBegin)); + iHashSize += _ProtoSSLGenerateSignatureHash(pSecure, CryptHashGet(CRYPTHASH_SHA1), aHashDigest+iHashSize, pBegin, (uint32_t)(pData-pBegin)); + } + else + { + iHashSize = _ProtoSSLGenerateSignatureHash(pSecure, pHash, aHashDigest, pBegin, (uint32_t)(pData-pBegin)); + } + + // verify the signature; note this is executed iteratively + iNextState = _ProtoSSLVerifySignature(pState, pSecure, pSigScheme, pSigData, iSigSize, aHashDigest, iHashSize, pSecure->uSslVersion >= SSL3_TLS1_2 ? eHashType : CRYPTHASH_NULL, ST3_RECV_HELLO); + } + else + { + NetPrintf(("protossl: _ServerKey: unsupported signature hash\n")); + return(ST_FAIL_SETUP); + } + + return(iNextState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendCertificateRequest + + \Description + Send CertificateRequest handshake packet + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_DONE) + + \Version 10/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendCertificateRequest(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t strHead[4], strBody[128]; + int32_t iBodySize; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send Certificate Request\n")); + + if (pSecure->uSslVersion < SSL3_TLS1_3) + { + // setup the body + iBodySize = 0; + strBody[iBodySize++] = 2; // number of certificate types + strBody[iBodySize++] = SSL3_SIGN_RSA; // rsa signing support + strBody[iBodySize++] = SSL3_SIGN_ECDSA; // ecdsa signing support + + // add TLS1.2 required SignatureAndHashAlgorithm as per http://tools.ietf.org/html/rfc5246#section-7.4.4 + if (pSecure->uSslVersion >= SSL3_TLS1_2) + { + int32_t iScheme, iNumSchemes = sizeof(_SSL3_SignatureSchemes)/sizeof(_SSL3_SignatureSchemes[0]); + strBody[iBodySize++] = 0; + strBody[iBodySize++] = iNumSchemes * 2; + for (iScheme = 0; iScheme < iNumSchemes; iScheme += 1) + { + strBody[iBodySize++] = (uint8_t)(_SSL3_SignatureSchemes[iScheme].uIdent>>8); + strBody[iBodySize++] = (uint8_t)(_SSL3_SignatureSchemes[iScheme].uIdent>>0); + } + } + + // number of certificate authorities (not supported) + strBody[iBodySize++] = 0; + strBody[iBodySize++] = 0; + } + else + { + const uint8_t *pDataEnd; + // write empty certificate_request_context, which we do not require as we don't support post-handshake authentication + strBody[0] = 0; + // add signature_algorithms extension + pDataEnd = _ProtoSSLAddHelloExtensions(pState, strBody+1, sizeof(strBody)-1, strBody, PROTOSSL_HELLOEXTN_SIGALGS); + iBodySize = pDataEnd - strBody; + } + + // setup the header + strHead[0] = SSL3_MSG_CERT_REQ; + strHead[1] = 0; + strHead[2] = 0; + strHead[3] = iBodySize; + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, sizeof(strHead), strBody, iBodySize); + return((pSecure->uSslVersion < SSL3_TLS1_3) ? ST3_SEND_DONE : ST3_SEND_CERT); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvCertificateRequest + + \Description + Process CertificateRequest handshake packet + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_HELLO, or ST_FAIL_* on error) + + \Version 10/18/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvCertificateRequest(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + const uint8_t *pDataEnd = pData + iDataSize; + int32_t iType, iNumTypes; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process CertificateRequest\n")); + + // process tls1.2 and prior certificate request + if (pSecure->uSslVersion <= SSL3_TLS1_2) + { + uint8_t uSign; + // make sure an rsa/ecdsa certificate is permissable + for (iType = 0, iNumTypes = _SafeRead8(pData++, pDataEnd); iType < iNumTypes; iType += 1) + { + uSign = _SafeRead8(pData+iType, pDataEnd); + if ((uSign == SSL3_SIGN_RSA) || (uSign == SSL3_SIGN_ECDSA)) + { + break; + } + } + // makes sure we got a valid signing algorithm + if (iType == iNumTypes) + { + NetPrintf(("protossl: no valid cert signing option found in RecvCertificateRequest\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_DECODE_ERROR); + return(ST_FAIL_SETUP); + } + // skip past cert types + pData += iNumTypes; + + // TLS1.2 includes SignatureAndHashAlgorithm specification: http://tools.ietf.org/html/rfc5246#section-7.4.4 + if (pSecure->uSslVersion == SSL3_TLS1_2) + { + // get and skip list length + uint32_t uSigSchemeLen = _SafeRead16(pData, pDataEnd); + pData += 2; + // pick first supported signature algorithm + if ((pSecure->pSigScheme = _ProtoSSLChooseSignatureScheme(pSecure, pState->pCertificate, pData, uSigSchemeLen, pDataEnd)) == NULL) + { + NetPrintf(("protossl: no valid signature algorithm found RecvCertificateRequest\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_HANDSHAKE_FAILURE); + return(ST_FAIL_SETUP); + } + } + } + else + { + // parse tls1.3 certificate request as per https://tools.ietf.org/html/rfc8446#section-4.3.2 + int32_t iParseResult; + // skip certificate_request_context, which we do not require as we don't support post-handshake authentication + pData += _SafeRead8(pData, pDataEnd)+1; + // parse signature_algorithms + if ((iParseResult = _ProtoSSLParseHelloExtensions(pState, pData, pDataEnd, SSL_EXTN_SIGNATURE_ALGORITHMS)) != 0) + { + return(iParseResult); + } + } + + /* As per http://tools.ietf.org/html/rfc5246#section-7.4.6, if no suitable certificate is available, + the client MUST send a certificate message containing no certificates, so we don't bother to check + whether we have a certificate here */ + pState->iClientCertLevel = 1; + return(ST3_RECV_HELLO); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendServerHelloDone + + \Description + Send ServerHelloDone handshake packet; marks end of server hello sequence + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_RECV_HELLO) + + \Notes + Applies to TLS1.2 and prior only + + \Version 10/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendServerHelloDone(ProtoSSLRefT *pState) +{ + uint8_t strHead[4]; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send ServerHelloDone\n")); + + // setup the header + strHead[0] = SSL3_MSG_SERVER_DONE; + strHead[1] = 0; + strHead[2] = 0; + strHead[3] = 0; + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, 4, NULL, 0); + return(ST3_RECV_HELLO); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvServerHelloDone + + \Description + Process ServerHelloDone handshake packet; marks end of server hello sequence + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_SEND_CERT or ST3_SEND_KEY) + + \Notes + Applies to TLS1.2 and prior only + + \Version 10/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvServerHelloDone(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process ServerHelloDone\n")); + return((pState->iClientCertLevel != 0) ? ST3_SEND_CERT : ST3_SEND_KEY); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateSendClientKeyExchange + + \Description + Send ClientKeyExchange handshake packet to the server + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_VERIFY or ST3_SEND_CHANGE on completion, + ST3_SEND_KEY if computation is ongoing, ST_FAIL_SETUP on error) + + \Version 01/23/2017 (eesponda) Updated to support ECDHE +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendClientKeyExchange(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t aHead[6]; + + // handle rsa key exchange + if (pState->pSecure->pCipher->uKey == SSL3_KEY_RSA) + { + if (!pSecure->bRSAContextInitialized) + { + // build pre-master secret + CryptRandGet(pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey)); + /* From http://tools.ietf.org/html/rfc5246#section-7.4.7.1, client_version: The version requested + by the client in the ClientHello. This is used to detect version roll-back attacks */ + pSecure->PreMasterKey[0] = (uint8_t)(pSecure->uSslClientVersion>>8); + pSecure->PreMasterKey[1] = (uint8_t)(pSecure->uSslClientVersion>>0); + + // init rsa state + if (CryptRSAInit(&pSecure->RSAContext, pSecure->Cert.KeyModData, pSecure->Cert.iKeyModSize, pSecure->Cert.KeyExpData, pSecure->Cert.iKeyExpSize)) + { + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_INTERNAL_ERROR); + return(ST_FAIL_SETUP); + } + CryptRSAInitMaster(&pSecure->RSAContext, pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey)); + pSecure->bRSAContextInitialized = TRUE; + } + + // encrypt the premaster key (iterative) + if (CryptRSAEncrypt(&pSecure->RSAContext, 1)) + { + return(ST3_SEND_KEY); + } + pSecure->bRSAContextInitialized = FALSE; + + // update ssl perf timer + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (encrypt rsa client premaster secret) %dms (%d calls)\n", + pSecure->RSAContext.uCryptMsecs, pSecure->RSAContext.uNumExpCalls)); + pSecure->uTimer += pSecure->RSAContext.uCryptMsecs; + + // build master secret + _ProtoSSLBuildKey(pState, pSecure->MasterKey, sizeof(pSecure->MasterKey), pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey), + pSecure->ClientRandom, pSecure->ServerRandom, sizeof(pSecure->ClientRandom), "master secret", + pSecure->uSslVersion); + + // setup the header + aHead[0] = SSL3_MSG_CLIENT_KEY; + aHead[1] = 0; + aHead[2] = (uint8_t)((pSecure->Cert.iKeyModSize+2)>>8); + aHead[3] = (uint8_t)((pSecure->Cert.iKeyModSize+2)>>0); + aHead[4] = (uint8_t)(pSecure->Cert.iKeyModSize>>8); + aHead[5] = (uint8_t)(pSecure->Cert.iKeyModSize>>0); + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, aHead, 6, pSecure->RSAContext.EncryptBlock, pSecure->RSAContext.iKeyModSize); + } + else + { + uint8_t aPublicKey[128]; + int32_t iPublicKeySize; + uint32_t uCryptUsecs; + const CryptCurveDhT *pEcc; + + /* generate public key; elliptic curve generation takes multiple frames + so stay in the same state until the operation is complete */ + + // initialize elliptic curve context if not already initialized + if ((pEcc = _ProtoSSLEccInitContext(pSecure, pSecure->pEllipticCurve)) == NULL) + { + /* if we cannot find the dh functions, there is some configuration mishap or the server is faulty. + let's fail early here so we can debug the issue instead of a null pointer exception */ + return(ST_FAIL_SETUP); + } + // generate the public key + if (pEcc->Public(&pSecure->EccContext, NULL, &uCryptUsecs) > 0) + { + return(ST3_SEND_KEY); + } + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (generate public key for client key message) %dms\n", + uCryptUsecs/1000)); + pSecure->uTimer += uCryptUsecs/1000; + + // encode public key into buffer + iPublicKeySize = pEcc->PointFinal(pSecure->EccContext, NULL, FALSE, aPublicKey+1, sizeof(aPublicKey)-1); + aPublicKey[0] = iPublicKeySize; + + // format header + aHead[0] = SSL3_MSG_CLIENT_KEY; + aHead[1] = 0; + aHead[2] = 0; + aHead[3] = iPublicKeySize+1; + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, aHead, 4, aPublicKey, iPublicKeySize+1); + } + + // move to next state + return(pSecure->bSentCert ? ST3_SEND_VERIFY : ST3_SEND_CHANGE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvClientKeyExchange + + \Description + Process ClientKeyExchange handshake packet + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_HELLO, ST3_RECV_CHANGE, or ST_FAIL_SETUP on error) + + \Version 10/15/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvClientKeyExchange(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iCryptKeySize, iPrivKeySize; + uint8_t strRandomSecret[48]; + X509PrivateKeyT PrivateKey; + const uint8_t *pCryptKeyData; + uint16_t uSslVersion; + const uint8_t *pDataEnd = pData+iDataSize; + + // handle RSA key exchange + if (pSecure->pCipher->uKey == SSL3_KEY_RSA) + { + // read encrypted key data + iCryptKeySize = _SafeRead16(pData, pDataEnd); + pCryptKeyData = pData + 2; + + // decode private key and extract key modulus and private exponent + if ((iPrivKeySize = _CertificateDecodePrivate(pState->pPrivateKey, pState->iPrivateKeyLen, &PrivateKey)) < 0) + { + NetPrintf(("protossl: unable to decode private key\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_INTERNAL_ERROR); + return(ST_FAIL_SETUP); + } + + // decrypt client premaster secret using private key + NetPrintfVerbose((pState->iVerbose, 1, "protossl: decrypt client key (iKeySize=%d, iKeyModSize=%d, iKeyExpSize=%d)\n", iPrivKeySize, PrivateKey.Modulus.iObjSize, PrivateKey.PrivateExponent.iObjSize)); + if (CryptRSAInit2(&pSecure->RSAContext, PrivateKey.Modulus.iObjSize, &PrivateKey.PrimeP, &PrivateKey.PrimeQ, &PrivateKey.ExponentP, &PrivateKey.ExponentQ, &PrivateKey.Coefficient)) + { + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_INTERNAL_ERROR); + return(ST_FAIL_SETUP); + } + CryptRSAInitSignature(&pSecure->RSAContext, pCryptKeyData, iCryptKeySize); + CryptRSAEncrypt(&pSecure->RSAContext, 0); + + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (rsa decrypt client premaster secret) %dms\n", pSecure->RSAContext.uCryptMsecs)); + pSecure->uTimer += pSecure->RSAContext.uCryptMsecs; + + // copy out and display decrypted client premaster key data + ds_memcpy(pSecure->PreMasterKey, pSecure->RSAContext.EncryptBlock+iCryptKeySize-sizeof(pSecure->PreMasterKey), sizeof(pSecure->PreMasterKey)); + #if DEBUG_RAW_DATA + NetPrintMem(pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey), "client premaster key"); + #endif + + // generate random for use in possible roll-back attack handling + CryptRandGet(strRandomSecret, sizeof(strRandomSecret)); + // validate client TLS version (first two bytes of premaster secret) + if ((uSslVersion = (pSecure->PreMasterKey[0] << 8) | pSecure->PreMasterKey[1]) != pSecure->uSslClientVersion) + { + NetPrintf(("protossl: detected possible roll-back attack; premaster secret tls version %d.%d does not match client-specified version\n", + pSecure->PreMasterKey[0], pSecure->PreMasterKey[1])); + } + + /* As per http://tools.ietf.org/html/rfc5246#section-7.4.7.1, a mismatch in the premaster secret tls version + and the client-specified version should be handled by generating a random premaster secret and continuing + on. The random data should be generated unconditionally to prevent possible timing attacks. This same + response is also utilized for any detected PKCS#1 padding errors. */ + if ((uSslVersion != pSecure->uSslClientVersion) || !_Pkcs1VerifyRSAES(pSecure->RSAContext.EncryptBlock, iCryptKeySize, sizeof(pSecure->PreMasterKey))) + { + ds_memcpy(pSecure->PreMasterKey, strRandomSecret, sizeof(strRandomSecret)); + } + + // build master secret + _ProtoSSLBuildKey(pState, pSecure->MasterKey, sizeof(pSecure->MasterKey), pSecure->PreMasterKey, sizeof(pSecure->PreMasterKey), + pSecure->ClientRandom, pSecure->ServerRandom, sizeof(pSecure->ClientRandom), "master secret", + pSecure->uSslVersion); + } + else + { + // for ecdhe key exchange we just copy the public key + pSecure->uPubKeyLength = _SafeRead8(pData++, pDataEnd); + _SafeReadBytes(pSecure->PubKey, sizeof(pSecure->PubKey), pData, pSecure->uPubKeyLength, pDataEnd); + } + + // move on to next state + return(((pState->iClientCertLevel > 0) && pSecure->bRecvCert) ? ST3_RECV_HELLO : ST3_RECV_CHANGE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendCertificateVerify + + \Description + Send CertificateVerify handshake packet + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_SEND_CHANGE or ST3_SEND_FINISH on completion, + ST3_SEND_VERIFY if RSA computation is ongoing, or ST_FAIL_SETUP + on error) + + \Version 10/30/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendCertificateVerify(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + uint32_t uHeadSize; + uint8_t strHead[16]; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send CertificateVerify\n")); + + // set up for encryption of certificate verify message + if (!pSecure->bSigGenerated) + { + uint8_t aSigObj[SSL_SIG_MAX+128]; // must be big enough to hold max hash size plus ASN.1 object encoding (TLS1.2), or a full signature object (TLS1.3+) + int32_t iPrivKeySize, iHashSize=0; + X509PrivateKeyT PrivateKey; + + // decode private key and extract key modulus and private exponent + if ((iPrivKeySize = _CertificateDecodePrivate(pState->pPrivateKey, pState->iPrivateKeyLen, &PrivateKey)) < 0) + { + NetPrintf(("protossl: unable to decode private key\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_INTERNAL_ERROR); + return(ST_FAIL_SETUP); + } + + // set up buffer with current handshake hash + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + // select signature scheme based on server/client certificate + pSecure->pSigScheme = _ProtoSSLGetSignatureScheme(pState->pCertificate->iKeyType == ASN_OBJ_ECDSA_KEY ? SSL3_SIGSCHEME_ECDSA_SHA1 : SSL3_SIGSCHEME_RSA_PKCS1_SHA1); + // generate digest hash + if (pSecure->pSigScheme->SigAlg.uSigAlg != SSL3_SIGALG_ECDSA) + { + iHashSize = _ProtoSSLHandshakeHashGet(pSecure, CRYPTHASH_MD5, aSigObj, SSL3_MAC_MD5); + } + iHashSize += _ProtoSSLHandshakeHashGet(pSecure, CRYPTHASH_SHA1, aSigObj+iHashSize, SSL3_MAC_SHA); + } + else + { + CryptHashTypeE eHashType = _SSL3_HashIdToCrypt[pSecure->pSigScheme->SigAlg.uHashId]; + uint8_t aMessageHash[CRYPTHASH_MAXDIGEST], aTempHash[CRYPTHASH_MAXDIGEST]; + + // get message digest hash + if (pSecure->uSslVersion < SSL3_TLS1_3) + { + // get handshake hash using signature hash algorithm + iHashSize = _ProtoSSLHandshakeHashGet(pSecure, eHashType, aMessageHash, sizeof(aMessageHash)); + } + else + { + // get handshake hash using cipher prf hash algorithm + iHashSize = _ProtoSSLHandshakeHashGet(pSecure, pSecure->pCipher->uPrfType, aTempHash, sizeof(aTempHash)); + // construct tls1.3 digital signature envelope using signature hash algorithm + iHashSize = _ProtoSSLGenerateCertificateVerifyHash(aMessageHash, eHashType, aTempHash, iHashSize, pState->bServer, TRUE); + } + + // encode message digest hash + if ((pSecure->pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PKCS1) && (pSecure->uSslVersion < SSL3_TLS1_3)) + { + iHashSize = _AsnWriteDigitalHashObject(aSigObj, sizeof(aSigObj), aMessageHash, iHashSize, eHashType); + } + else if (pSecure->pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PSS) + { + iHashSize = _Pkcs1EncodeEMSA_PSS(aSigObj, PrivateKey.Modulus.iObjSize, aMessageHash, iHashSize, eHashType); + } + else // ecdsa signatures have no additional encoding + { + ds_memcpy_s(aSigObj, sizeof(aSigObj), aMessageHash, iHashSize); + } + } + + #if DEBUG_RAW_DATA + NetPrintMem(aSigObj, iHashSize, "certificate verify hash"); + #endif + + // start async signature generation + return(_ProtoSSLGenerateSignature(pState, pSecure, pSecure->pSigScheme, aSigObj, iHashSize, &PrivateKey, ST3_SEND_VERIFY)); + } + + // setup the header + strHead[0] = SSL3_MSG_CERT_VERIFY; + strHead[1] = 0; + + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + strHead[2] = (uint8_t)((pSecure->SigVerify.iSigSize+2)>>8); + strHead[3] = (uint8_t)((pSecure->SigVerify.iSigSize+2)>>0); + strHead[4] = (uint8_t)(pSecure->SigVerify.iSigSize>>8); + strHead[5] = (uint8_t)(pSecure->SigVerify.iSigSize>>0); + uHeadSize = 6; + } + else + { + strHead[2] = (uint8_t)((pSecure->SigVerify.iSigSize+4)>>8); + strHead[3] = (uint8_t)((pSecure->SigVerify.iSigSize+4)>>0); + strHead[4] = (uint8_t)(pSecure->pSigScheme->uIdent>>8); + strHead[5] = (uint8_t)(pSecure->pSigScheme->uIdent>>0); + strHead[6] = (uint8_t)(pSecure->SigVerify.iSigSize>>8); + strHead[7] = (uint8_t)(pSecure->SigVerify.iSigSize>>0); + uHeadSize = 8; + } + + // send the packet + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, uHeadSize, pSecure->SigVerify.aSigData, pSecure->SigVerify.iSigSize); + + // transition to next state + return((pSecure->uSslVersion < SSL3_TLS1_3) ? ST3_SEND_CHANGE : ST3_SEND_FINISH); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvCertificateVerify + + \Description + Process CertificateVerify handshake packet + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_CHANGE, ST3_RECV_FINISH, ST_PROC_ASYNC, + or ST_FAIL_SETUP on error) + + \Version 10/30/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvCertificateVerify(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iCryptKeySize, iHashSize; + const uint8_t *pCryptKeyData; + CryptHashTypeE eHashType = CRYPTHASH_NULL; + const uint8_t *pDataEnd = pData+iDataSize; + uint8_t aHandshakeHash[CRYPTHASH_MAXDIGEST]; + const SignatureSchemeT *pSigScheme = NULL; + int32_t iNextState = (pSecure->uSslVersion < SSL3_TLS1_3) ? ST3_RECV_CHANGE : ST3_RECV_FINISH; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process CertificateVerify\n")); + + // get encrypted key data size and offset + if (pSecure->uSslVersion < SSL3_TLS1_2) + { + iCryptKeySize = _SafeRead16(pData, pDataEnd); + pCryptKeyData = pData + 2; + pSigScheme = _ProtoSSLGetSignatureScheme((pSecure->Cert.iKeyType == ASN_OBJ_ECDSA_KEY) ? SSL3_SIGSCHEME_ECDSA_SHA1 : SSL3_SIGSCHEME_RSA_PKCS1_SHA1); + } + else + { + // get signature info + pSigScheme = _ProtoSSLGetSignatureScheme(_SafeRead16(pData, pDataEnd)); + // validate scheme and key type; as per https://tools.ietf.org/html/rfc8446#section-4.4.3 PKCS1 is not a supported signature scheme in tls1.3+ + if ((pSigScheme == NULL) || ((pSigScheme->uVerifyScheme == SSL3_SIGVERIFY_RSA_PKCS1) && (pSecure->uSslVersion >= SSL3_TLS1_3))) + { + NetPrintf(("protossl: unsupported hashid or signature algorithm in CertificateVerify\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_DECODE_ERROR); + return(ST_FAIL_SETUP); + } + // get crypt key size and data + iCryptKeySize = _SafeRead16(pData+2, pDataEnd); + pCryptKeyData = pData + 4; + } + + // get digest hash from signature scheme + eHashType = _SSL3_HashIdToCrypt[pSigScheme->SigAlg.uHashId]; + + // get handshake hash + if ((pSecure->uSslVersion < SSL3_TLS1_2) && (pSigScheme->SigAlg.uSigAlg != SSL3_SIGALG_ECDSA)) + { + iHashSize = _ProtoSSLHandshakeHashGet(pSecure, CRYPTHASH_MD5, aHandshakeHash, SSL3_MAC_MD5); + iHashSize += _ProtoSSLHandshakeHashGet(pSecure, CRYPTHASH_SHA1, aHandshakeHash + iHashSize, SSL3_MAC_SHA); + } + else if (pSecure->uSslVersion < SSL3_TLS1_3) + { + iHashSize = _ProtoSSLHandshakeHashGet(pSecure, eHashType, aHandshakeHash, sizeof(aHandshakeHash)); + } + else + { + // tls1.3 wraps handshake hash in a digital signature envelope + uint8_t aHandshakeHash2[CRYPTHASH_MAXDIGEST]; + // get handshake hash + iHashSize = _ProtoSSLHandshakeHashGet(pSecure, pSecure->pCipher->uPrfType, aHandshakeHash2, sizeof(aHandshakeHash2)); + // construct tls1.3 digital signature envelope + iHashSize = _ProtoSSLGenerateCertificateVerifyHash(aHandshakeHash, eHashType, aHandshakeHash2, iHashSize, pState->bServer, FALSE); + } + + // verify the signature; note this is executed iteratively + return(_ProtoSSLVerifySignature(pState, pSecure, pSigScheme, pCryptKeyData, iCryptKeySize, aHandshakeHash, iHashSize, pSecure->uSslVersion >= SSL3_TLS1_2 ? eHashType : CRYPTHASH_NULL, iNextState)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendChangeCipherSpec + + \Description + Send ChangeCipherSpec handshake packet + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_CHANGE, ST3_SEND_FINISH) + + \Notes + Applies to TLS1.2 and prior only + + \Version 10/15/2013 (jbrookes) Updated to handle client and server +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendChangeCipherSpec(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t strHead[4]; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send ChangeCipherSpec\n")); + + // generate the shared secret if necessary + if (!pState->bServer && !pSecure->bSessionResume && !_ProtoSSLGenerateEccSharedSecret(pState)) + { + return(ST3_SEND_CHANGE); + } + + // build key material if we haven't already + if (pSecure->pServerKey == NULL) + { + _ProtoSSLBuildKeyMaterial(pState); + } + + // initialize write cipher + if (pSecure->pCipher->uEnc == SSL3_ENC_AES) + { + CryptAesInit(&pSecure->WriteAes, pState->bServer ? pSecure->pServerKey : pSecure->pClientKey, pSecure->pCipher->uLen, CRYPTAES_KEYTYPE_ENCRYPT, + pState->bServer ? pSecure->pServerInitVec : pSecure->pClientInitVec); + } + if (pSecure->pCipher->uEnc == SSL3_ENC_GCM) + { + CryptGcmInit(&pSecure->WriteGcm, pState->bServer ? pSecure->pServerKey : pSecure->pClientKey, pSecure->pCipher->uLen); + } + if (pSecure->pCipher->uEnc == SSL3_ENC_CHACHA) + { + CryptChaChaInit(&pSecure->WriteChaCha, pState->bServer ? pSecure->pServerKey : pSecure->pClientKey, pSecure->pCipher->uLen); + } + + // mark as cipher change + strHead[0] = 1; + _ProtoSSLSendPacket(pState, SSL3_REC_CIPHER, strHead, 1, NULL, 0); + + // reset the sequence number + pSecure->uSendSeqn = 0; + return(ST3_SEND_FINISH); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvChangeCipherSpec + + \Description + Process ChangeCipherSpec handshake packet + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_RECV_CHANGE, ST3_RECV_FINISH) + + \Notes + Applies to TLS1.2 and prior only + + \Version 10/15/2013 (jbrookes) Updated to handle client and server +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvChangeCipherSpec(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + const uint8_t *pDataEnd = pData + iDataSize; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process ChangeCipherSpec\n")); + + // validate ccs message + if ((iDataSize != 1) || (_SafeRead8(pData, pDataEnd) != 1)) + { + NetPrintf(("protossl: invalid ChangeCipherSpec message\n")); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_DECODE_ERROR); + pState->iClosed = 1; + _ProtoSSLRecvReset(pSecure); + pState->iState = ST_FAIL_SETUP; + } + + // generate the shared secret if necessary + if (pState->bServer && !pSecure->bSessionResume && !_ProtoSSLGenerateEccSharedSecret(pState)) + { + return(ST3_RECV_CHANGE); + } + + // build key material if we haven't already + if (pSecure->pServerKey == NULL) + { + _ProtoSSLBuildKeyMaterial(pState); + } + + // initialize read cipher + if (pSecure->pCipher->uEnc == SSL3_ENC_AES) + { + CryptAesInit(&pSecure->ReadAes, pState->bServer ? pSecure->pClientKey : pSecure->pServerKey, pSecure->pCipher->uLen, CRYPTAES_KEYTYPE_DECRYPT, + pState->bServer ? pSecure->pClientInitVec : pSecure->pServerInitVec); + } + if (pSecure->pCipher->uEnc == SSL3_ENC_GCM) + { + CryptGcmInit(&pSecure->ReadGcm, pState->bServer ? pSecure->pClientKey : pSecure->pServerKey, pSecure->pCipher->uLen); + } + if (pSecure->pCipher->uEnc == SSL3_ENC_CHACHA) + { + CryptChaChaInit(&pSecure->ReadChaCha, pState->bServer ? pSecure->pClientKey : pSecure->pServerKey, pSecure->pCipher->uLen); + } + + // reset sequence number + pSecure->uRecvSeqn = 0; + + // just gobble packet -- we assume next state is crypted regardless + return(ST3_RECV_FINISH); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendFinished + + \Description + Send Finished handshake packet + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_RECV_CHANGE, ST3_RECV_HELLO, + ST3_RECV_FINISH, ST3_SECURE) + + \Version 10/23/2013 (jbrookes) Rewritten to handle client and server +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendFinished(ProtoSSLRefT *pState) +{ + uint8_t strHead[4]; + uint8_t strBody[256]; + SecureStateT *pSecure = pState->pSecure; + static const struct SSLFinished _SendFinished[2] = { + { "client finished", {ST3_RECV_CHANGE, ST3_SECURE } }, + { "server finished", {ST3_SECURE, ST3_RECV_CHANGE } }, + }; + const struct SSLFinished *pFinished = &_SendFinished[pState->bServer]; + int32_t iNextState = ST3_SECURE; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: SendFinished\n")); + + // do finish hash, save size in packet header + strHead[3] = (uint8_t)_ProtoSSLGenerateFinishHash(strBody, pSecure, pFinished->strLabel, pState->bServer, TRUE); + + // setup the header + strHead[0] = SSL3_MSG_FINISHED; + strHead[1] = 0; + strHead[2] = 0; + + // all sends from here on out are secure + pSecure->bSendSecure = TRUE; + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, sizeof(strHead), strBody, strHead[3]); + + // tls1.3 handshake processing + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + if (pState->bServer) + { + // save handshake hash for building application secrets + _ProtoSSLHandshakeHashGet(pSecure, pSecure->pCipher->uPrfType, pSecure->aFinishHash, sizeof(pSecure->aFinishHash)); + } + else + { + // switch to application keys; this must come after _ProtoSSLSendPacket as the server expects the Finished packet to be sent using the handshake key + _ProtoSSLBuildApplicationKey(pState, pSecure); + } + } + + // determine next state + if (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + // next state will be secure mode (handshaking complete) or recv cipher change depending on client/server and if resuming or not + iNextState = pFinished->uNextState[pSecure->bSessionResume]; + } + else if (pState->bServer) + { + // server will expect either Finished or Certificate if requesting a client cert + iNextState = (pState->iClientCertLevel != 0) ? ST3_RECV_HELLO : ST3_RECV_FINISH; + } + return(iNextState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvFinished + + \Description + Process Finished handshake packet + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_SEND_CHANGE, ST3_SEND_CERT, ST3_SEND_FINISH, + ST3_SECURE, or ST_FAIL_SETUP on error) + + \Version 10/23/2013 (jbrookes) Rewritten to handle client and server +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLUpdateRecvFinished(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + uint8_t aMacFinal[CRYPTHASH_MAXDIGEST]; + int32_t iMacSize, iNextState = ST3_SECURE; + static const struct SSLFinished _RecvFinished[2] = { + { "server finished", {ST3_SECURE, ST3_SEND_CHANGE } }, + { "client finished", {ST3_SEND_CHANGE, ST3_SECURE } }, + }; + const struct SSLFinished *pFinished = &_RecvFinished[pState->bServer]; + const uint8_t *pDataEnd = pData + iDataSize; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process RecvFinished\n")); + + // calculate the finish verification hash + iMacSize = _ProtoSSLGenerateFinishHash(aMacFinal, pSecure, pFinished->strLabel, pState->bServer, FALSE); + + // verify the hash + if (memcmp(aMacFinal, pData, DS_MIN(iMacSize, pDataEnd-pData))) + { + NetPrintf(("protossl: finish hash mismatch; failed setup\n")); + #if DEBUG_RAW_DATA + NetPrintMem(aMacFinal, iMacSize, "aMacFinal"); + NetPrintMem(pData, iMacSize, "pData"); + #endif + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_DECRYPT_ERROR); + return(ST_FAIL_SETUP); + } + + // save session (tls1.2 and previous only) + if (pSecure->uSslVersion <= PROTOSSL_VERSION_TLS1_2) + { + _SessionHistoryAdd(pSecure, pState->strHost, SockaddrInGetPort(&pState->PeerAddr), NULL, pSecure->SessionId, sizeof(pSecure->SessionId), pSecure->MasterKey); + } + + // tls1.3 handshake processing + if (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) + { + if (pState->bServer) + { + // switch to application keys + _ProtoSSLBuildApplicationKey(pState, pSecure); + } + else + { + // update hash state with received Finished packet from server (which hasn't been processed yet) + _ProtoSSLRecvHandshakeFinish(pState); + // save current handshake hash + _ProtoSSLHandshakeHashGet(pSecure, pSecure->pCipher->uPrfType, pSecure->aFinishHash, sizeof(pSecure->aFinishHash)); + } + } + + // determine next state + if (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + // next state will be secure mode (handshaking complete) or send cipher change depending on client/server and if resuming or not + iNextState = pFinished->uNextState[pSecure->bSessionResume]; + } + else if (!pState->bServer) + { + // client will send either Finished or Certificate if a client cert was requested by the server + iNextState = (pState->iClientCertLevel != 0) ? ST3_SEND_CERT : ST3_SEND_FINISH; + } + return(iNextState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSendHelloRequest + + \Description + Send HelloRequest handshake packet + + \Input *pState - module state reference + + \Output + int32_t - next state (ST3_RECV_HELLO) + + \Version 03/28/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSendHelloRequest(ProtoSSLRefT *pState) +{ + uint8_t strHead[4]; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Send HelloRequest\n")); + + // setup the header + strHead[0] = SSL3_MSG_HELLO_REQUEST; + strHead[1] = 0; + strHead[2] = 0; + strHead[3] = 0; + + // setup packet for send + _ProtoSSLSendPacket(pState, SSL3_REC_HANDSHAKE, strHead, sizeof(strHead), strHead, 0); + + // set up to receive ClientHello + return(ST3_RECV_HELLO); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvHelloRequest + + \Description + Receive HelloRequest handshake packet; we respond with a no_renegotation + warning alert, or unexpected_message if the connection is TLS 1.3. + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (current state) + + \Version 03/28/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvHelloRequest(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + SecureStateT *pSecure = pState->pSecure; + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process HelloRequest\n")); + + if (pSecure->uSslVersion < PROTOSSL_VERSION_TLS1_3) + { + // respond with no_renegotation alert; if we were to implement renegotation, we'd move to ST3_SEND_HELLO + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_WARNING, SSL3_ALERT_DESC_NO_RENEGOTIATION); + } + else + { + // hello request is not a valid tls 1.3 message + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_UNEXPECTED_MESSAGE); + } + + return(pState->iState); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvNewSessionTicket + + \Description + Process NewSessionTicket handshake packet; see + https://tools.ietf.org/html/rfc8446#section-4.6.1 + + \Input *pState - module state reference + \Input *pData - packet data + \Input iDataSize - size of packet data + + \Output + int32_t - next state (ST3_SECURE) + + \Version 03/17/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvNewSessionTicket(ProtoSSLRefT *pState, const uint8_t *pData, int32_t iDataSize) +{ + const uint8_t *pDataEnd = pData + iDataSize; + SecureStateT *pSecure = pState->pSecure; + SessionTicketT SessTick; + CryptHashTypeE eHashType = pSecure->pCipher->uPrfType; + int32_t iHashLen = CryptHashGetSize(eHashType); + + NetPrintfVerbose((DEBUG_MSG_LIST, 0, "protossl: SSL Msg: Process NewSessionTicket\n")); + + // initialize session ticket memory + ds_memset(&SessTick, 0, sizeof(SessTick)); + // save receipt time + SessTick.uRecvTime = time(NULL); + // save resumption key + ds_memcpy_s(SessTick.aResumeKey, sizeof(SessTick.aResumeKey), pSecure->pResumeSecret, iHashLen); + // save hash type + SessTick.eHashType = eHashType; + + // parse required session ticket fields + SessTick.uLifetime = _SafeRead32(pData, pDataEnd); + pData += 4; + SessTick.uAgeAdd = _SafeRead32(pData, pDataEnd); + pData += 4; + + // copy ticket nonce + SessTick.uNonceLen = _SafeRead8(pData, pDataEnd); + pData += 1; + _SafeReadBytes(SessTick.aTicketNonce, sizeof(SessTick.aTicketNonce), pData, SessTick.uNonceLen, pDataEnd); + pData += SessTick.uNonceLen; + + // copy ticket length + SessTick.uTickLen = _SafeRead16(pData, pDataEnd); + pData += 2; + + // make sure we have enough space + if (SessTick.uTickLen > sizeof(SessTick.aTicketData)) + { + NetPrintf(("protossl: session ticket too large for session history buffer\n")); + return(ST3_SECURE); + } + + // copy ticket data + _SafeReadBytes(SessTick.aTicketData, sizeof(SessTick.aTicketData), pData, SessTick.uTickLen, pDataEnd); + pData += SessTick.uTickLen; + + // parse extensions length, if present + if (pData <= (pDataEnd-2)) + { + SessTick.uExtnLen = _SafeRead16(pData, pDataEnd); + } + + NetPrintfVerbose((pState->iVerbose, 0, "protossl: ticket_sni: %s:%d, ticket_lifetime=%d, ticket_age_add=0x%08x, nonce_len=%d, ticket_len=%d, extn_len=%d\n", + pState->strHost, SockaddrInGetPort(&pState->PeerAddr), SessTick.uLifetime, SessTick.uAgeAdd, SessTick.uNonceLen, SessTick.uTickLen, SessTick.uExtnLen)); + + // add ticket to session history + _SessionHistoryAdd(pSecure, pState->strHost, SockaddrInGetPort(&pState->PeerAddr), &SessTick, NULL, 0, NULL); + + return(ST3_SECURE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvHandshakeValidate + + \Description + Validate handshake message transition is permissible; as per + https://tools.ietf.org/html/rfc8446#section-4: "a peer which receives a + handshake message in an unexpected order MUST abort the handshake + with an "unexpected_message" alert." + + \Input *pState - protossl state + \Input *pSecure - secure state + \Input iNxtMsg - next message + + \Output + int32_t - true if validated, else false + + \Notes + This function validates that a given handshake state transition is possible + within the spec, but it does not validate that the transition is correct + given the specific set of parameters (ciphers/signature algorithms/etc) + selected. Such validation is left up to the specific handshake handler. + + \Version 05/02/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLRecvHandshakeValidate(ProtoSSLRefT *pState, SecureStateT *pSecure, int32_t iNxtMsg) +{ + static const SSLHandshakeMapT *_aHandshakeMaps[2][2] = + { + { _SSL3_ClientRecvMsgMap, _SSL3_ClientRecvMsgMap_13 }, + { _SSL3_ServerRecvMsgMap, _SSL3_ServerRecvMsgMap_13 } + }; + uint32_t uMapVersIdx = (pSecure->uSslVersion >= PROTOSSL_VERSION_TLS1_3) ? 1 : 0; + const SSLHandshakeMapT *pMap; + int32_t iCount; + + for (iCount = 0, pMap = _aHandshakeMaps[pState->bServer][uMapVersIdx]; pMap[iCount].iCurMsg != -1; iCount += 1) + { + if (pSecure->iCurMsg != pMap[iCount].iCurMsg) + { + continue; + } + if (iNxtMsg != pMap[iCount].iNxtMsg) + { + continue; + } + break; + } + + NetPrintfVerbose((DEBUG_MSG_LIST || (pMap[iCount].iCurMsg == -1), 0, "protossl: %s handshake message state transition %s(%d)->%s(%d)\n", (pMap[iCount].iCurMsg != -1) ? "validated" : "invalid", + _ProtoSSLRecvGetHandshakeTypeName(pSecure->iCurMsg), pSecure->iCurMsg, + _ProtoSSLRecvGetHandshakeTypeName(iNxtMsg), iNxtMsg)); + + return(pMap[iCount].iCurMsg != -1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvHandshake + + \Description + Decode ssl handshake packet, validate message flow + + \Input *pState - protossl state + \Input *pSecure - secure state + \Input *pMsgType - [out] handshake message type + + \Output + uint8_t * - pointer to handshake packet start, or NULL if error + + \Version 11/10/2005 (gschaefer) +*/ +/********************************************************************************F*/ +static const uint8_t *_ProtoSSLRecvHandshake(ProtoSSLRefT *pState, SecureStateT *pSecure, uint8_t *pMsgType) +{ + uint8_t *pRecv = pSecure->RecvData+pSecure->iRecvHshkProg; + + // get message type + *pMsgType = pRecv[0]; + + // make sure next message is possible in handshake flow + if (!_ProtoSSLRecvHandshakeValidate(pState, pSecure, *pMsgType)) + { + return(NULL); + } + + // update current message + pSecure->iCurMsg = *pMsgType; + + // point to data + return(pRecv+4); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLRecvHandshakeFinish + + \Description + Complete processing of handshake packet, including computation of + handshake hash. + + \Input *pState - module state reference + + \Version 03/16/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLRecvHandshakeFinish(ProtoSSLRefT *pState) +{ + SecureStateT *pSecure = pState->pSecure; + + // if this is a handshake packet and we haven't already processed it + if ((pSecure->RecvHead[0] == SSL3_REC_HANDSHAKE) && (pSecure->iRecvSize > 0) && (pSecure->iRecvSize >= pSecure->iRecvHshkSize)) + { + // keep a running hash of received data for verify/finish hashes + _ProtoSSLHandshakeHashUpdate(pSecure, pSecure->RecvData+pSecure->iRecvHshkProg, pSecure->iRecvHshkSize, "recv"); + // consume the packet + pSecure->iRecvHshkProg += pSecure->iRecvHshkSize; + pSecure->iRecvHshkSize = 0; + } + + // see if all the data was consumed + if (pSecure->iRecvHshkProg == pSecure->iRecvSize) + { + _ProtoSSLRecvReset(pSecure); + } +} + +/* + main update loop +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateAsync + + \Description + Perform iterative operations + + \Input *pState - module state reference + \Input *pSecure - secure state reference + + \Output + int32_t - one if async operation occurred, else zero + + \Version 02/15/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLUpdateAsync(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + #if DEBUG_ENC_PERF + uint64_t uTickUsec = NetTickUsec(); + #endif + int32_t iResult; + // not in async state, return + if (pState->iState != ST3_PROC_ASYNC) + { + return(0); + } + // execute async op and check for completion + iResult = pState->AsyncInfo.pAsyncOp(pState); + #if DEBUG_ENC_PERF + NetPrintf(("protossl: async op took %qd us\n", NetTickDiff(NetTickUsec(), uTickUsec))); + #endif + if (iResult == 0) + { + pState->iState = pState->AsyncInfo.iNextState; + pState->AsyncInfo.iNextState = 0; + return(0); + } + else if (iResult < 0) + { + pState->iState = pState->AsyncInfo.iFailState; + pState->AsyncInfo.iNextState = 0; + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, pState->AsyncInfo.iFailAlert); + return(0); + } + // continuing to execute async op + return(ST3_PROC_ASYNC); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateSetAsyncState + + \Description + Set up for 'async' execution of pAsyncOp. Asynchronous in this + case means iterative execution from the main update loop. + + \Input *pState - module state + \Input *pAsyncOp - function to execute asynchronously + \Input iNextState - state to transition to on success + \Input iFailState - state to transition to on failure + \Input iFailAlert - alert to send on failure + + \Output + int32_t - updated state (ST3_PROC_ASYNC) + + \Version 02/16/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLUpdateSetAsyncState(ProtoSSLRefT *pState, AsyncOpT *pAsyncOp, int32_t iNextState, int32_t iFailState, int32_t iFailAlert) +{ + pState->iState = ST3_PROC_ASYNC; + pState->AsyncInfo.iNextState = iNextState; + pState->AsyncInfo.iFailState = iFailState; + pState->AsyncInfo.iFailAlert = iFailAlert; + pState->AsyncInfo.pAsyncOp = pAsyncOp; + return(_ProtoSSLUpdateAsync(pState, pState->pSecure)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateSend + + \Description + Send data on a secure connection + + \Input *pState - module state reference + \Input *pSecure - secure state reference + + \Output + int32_t - one if data was sent, else zero + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLUpdateSend(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + // handle send states if output buffer is empty + if (pSecure->iSendProg == pSecure->iSendSize) + { + // deal with send states + if (pState->iState == ST3_SEND_HELLO) + { + pState->iState = pState->bServer ? _ProtoSSLSendServerHello(pState) : _ProtoSSLSendClientHello(pState); + } + else if (pState->iState == ST3_SEND_HELLO_RETRY) + { + pState->iState = _ProtoSSLSendHelloRetryRequest(pState); + } + else if (pState->iState == ST3_SEND_EXTN) + { + pState->iState = _ProtoSSLSendEncryptedExtensions(pState); + } + else if (pState->iState == ST3_SEND_CERT) + { + pState->iState = _ProtoSSLSendCertificate(pState); + } + else if (pState->iState == ST3_SEND_CERT_REQ) + { + pState->iState = _ProtoSSLSendCertificateRequest(pState); + } + else if (pState->iState == ST3_SEND_DONE) + { + pState->iState = _ProtoSSLSendServerHelloDone(pState); + } + else if (pState->iState == ST3_SEND_KEY) + { + pState->iState = pState->bServer ? _ProtoSSLSendServerKeyExchange(pState) : _ProtoSSLSendClientKeyExchange(pState); + } + else if (pState->iState == ST3_SEND_VERIFY) + { + pState->iState = _ProtoSSLSendCertificateVerify(pState); + } + else if (pState->iState == ST3_SEND_CHANGE) + { + pState->iState = _ProtoSSLSendChangeCipherSpec(pState); + } + else if (pState->iState == ST3_SEND_FINISH) + { + pState->iState = _ProtoSSLSendFinished(pState); + } + else if (pState->iState == ST3_SEND_HELLO_REQUEST) + { + pState->iState = _ProtoSSLSendHelloRequest(pState); + } + } + + // send any queued data + return(_ProtoSSLSendSecure(pState, pState->pSecure)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateRecvHandshake + + \Description + Process a handshake message + + \Input *pState - module state reference + \Input *pSecure - secure state reference + + \Output + uint32_t - FALSE if there was an error, else TRUE + + \Version 11/14/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ProtoSSLUpdateRecvHandshake(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + uint8_t bUnhandledMsg = FALSE, uMsgType; + const uint8_t *pData; + int32_t iDataSize; + + // make sure it's a handshake message + if (pSecure->RecvHead[0] != SSL3_REC_HANDSHAKE) + { + /* as per https://tools.ietf.org/html/rfc8446#section-5, if a TLS implementation receives an unexpected record type, + it MUST terminate the connection with an "unexpected_message" alert */ + NetPrintf(("protossl: received unexpected record %s (%d) when expecting a handshake record\n", _ProtoSSLRecvGetRecordTypeName(pSecure->RecvHead[0]), pSecure->RecvHead[0])); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_UNEXPECTED_MESSAGE); + return(FALSE); + } + + // calculate handshake packet size + if (pSecure->iRecvHshkSize == 0) + { + pData = pSecure->RecvData + pSecure->iRecvHshkProg; + pSecure->iRecvHshkSize = ((pData[1]<<16)|(pData[2]<<8)|(pData[3]<<0)) + SSL3_MSG_HEADER_SIZE; + // make sure packet fits in our buffer + if ((pSecure->iRecvHshkProg+pSecure->iRecvHshkSize) > (int32_t)sizeof(pSecure->RecvData)) + { + NetPrintf(("protossl: _ProtoSSLUpdateRecvHandshake: packet at base %d is too long (%d vs %d)\n", pSecure->iRecvHshkProg, + pSecure->iRecvHshkSize, sizeof(pSecure->RecvData)-pSecure->iRecvHshkProg)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_DECODE_ERROR); + return(FALSE); + } + } + + // make sure we have the entire packet before proceeding + if (pSecure->iRecvSize < pSecure->iRecvHshkSize) + { + // receive next packet header + pSecure->iRecvHead = 0; + pSecure->bRecvProc = FALSE; + return(TRUE); + } + + // size of handshake data is handshake size minus header size + iDataSize = pSecure->iRecvHshkSize - SSL3_MSG_HEADER_SIZE; + + // get handshake message and ensure it is valid in the handshake state machine + if ((pData = _ProtoSSLRecvHandshake(pState, pSecure, &uMsgType)) == NULL) + { + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_UNEXPECTED_MESSAGE); + return(FALSE); + } + + // process handshake message based on state + if (pState->iState == ST3_RECV_HELLO) + { + if (uMsgType == SSL3_MSG_CLIENT_HELLO) + { + pState->iState = _ProtoSSLRecvClientHello(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_SERVER_HELLO) + { + pState->iState = _ProtoSSLRecvServerHello(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_ENCRYPTED_EXTENSIONS) + { + pState->iState = _ProtoSSLRecvEncryptedExtensions(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_CERTIFICATE) + { + pState->iState = _ProtoSSLRecvCertificate(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_CERT_REQ) + { + pState->iState = _ProtoSSLRecvCertificateRequest(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_CERT_VERIFY) + { + pState->iState = _ProtoSSLRecvCertificateVerify(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_CLIENT_KEY) + { + pState->iState = _ProtoSSLRecvClientKeyExchange(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_SERVER_KEY) + { + pState->iState = _ProtoSSLRecvServerKeyExchange(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_SERVER_DONE) + { + pState->iState = _ProtoSSLRecvServerHelloDone(pState, pData, iDataSize); + } + else + { + bUnhandledMsg = TRUE; + } + } + else if (pState->iState == ST3_RECV_FINISH) + { + if (uMsgType == SSL3_MSG_FINISHED) + { + pState->iState = _ProtoSSLUpdateRecvFinished(pState, pData, iDataSize); + } + else + { + bUnhandledMsg = TRUE; + } + } + else if (pState->iState == ST3_SECURE) + { + // handle post-finish handshake messages + if (uMsgType == SSL3_MSG_HELLO_REQUEST) + { + pState->iState = _ProtoSSLRecvHelloRequest(pState, pData, iDataSize); + } + else if (uMsgType == SSL3_MSG_NEW_SESSION_TICKET) + { + pState->iState = _ProtoSSLRecvNewSessionTicket(pState, pData, iDataSize); + } + // finish here since we won't in _ProtoSSLUpdateRecvPacket() + _ProtoSSLRecvHandshakeFinish(pState); + } + else + { + bUnhandledMsg = TRUE; + } + + // fatal alert if message was unhandled + if (bUnhandledMsg) + { + NetPrintf(("protossl: unhandled handshake message %s(%d) in state %d\n", _ProtoSSLRecvGetHandshakeTypeName(uMsgType), uMsgType, pState->iState)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_UNEXPECTED_MESSAGE); + } + + return(!bUnhandledMsg); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateRecvPacket + + \Description + Process a received packet + + \Input *pState - module state reference + \Input *pSecure - secure state reference + + \Version 11/14/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLUpdateRecvPacket(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + int32_t iState = pState->iState; + + if (pSecure->RecvHead[0] == SSL3_REC_ALERT) + { + _ProtoSSLRecvAlert(pState, pSecure); + } + else if ((pSecure->RecvHead[0] == SSL3_REC_CIPHER) && (pState->iState == ST3_RECV_CHANGE)) + { + if ((pState->iState = _ProtoSSLRecvChangeCipherSpec(pState, pSecure->RecvData+pSecure->iRecvHshkProg, pSecure->iRecvSize)) != ST3_RECV_CHANGE) + { + pSecure->iRecvHshkProg = pSecure->iRecvSize; + } + } + else if (!_ProtoSSLUpdateRecvHandshake(pState, pSecure)) + { + pState->iClosed = 1; + _ProtoSSLRecvReset(pSecure); + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + } + + // output final crypto timing, if we have just reached secure state + if ((iState != pState->iState) && (pState->iState == ST3_SECURE) && (pSecure->uTimer > 0)) + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: SSL Perf (setup) %dms\n", pSecure->uTimer)); + pSecure->uTimer = 0; + } + + /* finish recv handshake processing: due to the fact that the recv hello state handles which state we are in based on the + packet data we want to move forward regardless. when we are not in the recv hello state we want to make sure to to only + move to the next state when the state changes to allow for any additional processing that happens over multiple frames. */ + if ((pState->iState == ST3_RECV_HELLO) || (iState != pState->iState)) + { + _ProtoSSLRecvHandshakeFinish(pState); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateRecvValidate + + \Description + Validate received packet + + \Input *pState - module state reference + \Input *pSecure - secure state reference + + \Version 11/14/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoSSLUpdateRecvValidate(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + /* validate the format version, and make sure it is a 3.x protocol version. we make an assumption here that + the 3.x packet format will not change in future revisions to the TLS protocol (this was confirmed with the + 1.3 version of the protocol). validation of the specific protocol version is handled during handshaking. */ + if (pSecure->RecvHead[1] == (SSL3_VERSION>>8)) + { + uint32_t uRecvSize = (pSecure->RecvHead[3]<<8)|(pSecure->RecvHead[4]<<0); + // check data length to make sure it is valid; as per http://tools.ietf.org/html/rfc5246#section-6.2.1: The length MUST NOT exceed 2^14+2^11 + if (uRecvSize > SSL_RCVMAX_PACKET) + { + NetPrintf(("protossl: received oversized packet (%d/%d); terminating connection\n", uRecvSize, SSL_RCVMAX_PACKET)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_RECORD_OVERFLOW); + pState->iClosed = 1; + _ProtoSSLRecvReset(pSecure); + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + } + // save data offset + pSecure->iRecvBase = pSecure->iRecvSize; + // add in the data length + pSecure->iRecvSize += uRecvSize; + // make sure it doesn't exceed our buffer + if (pSecure->iRecvSize > (int32_t)sizeof(pSecure->RecvData)) + { + NetPrintf(("protossl: packet length of %d is too large\n", pSecure->iRecvSize)); + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, SSL3_ALERT_DESC_RECORD_OVERFLOW); + pState->iClosed = 1; + _ProtoSSLRecvReset(pSecure); + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + } + } + else + { + if ((pSecure->RecvHead[0] == 0x80) && (pSecure->RecvHead[2] == 1)) + { + NetPrintf(("protossl: received %d byte SSLv2 ClientHello offering SSLv%d.%d; disconnecting\n", + pSecure->RecvHead[1], pSecure->RecvHead[3], pSecure->RecvHead[4])); + pState->iState = ST_FAIL_CONN_SSL2; + } + else + { + NetPrintf(("protossl: received what appears to be a non-SSL connection attempt; disconnecting\n")); + pState->iState = ST_FAIL_CONN_NOTSSL; + } + pState->iClosed = 1; + _ProtoSSLRecvReset(pSecure); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateRecv + + \Description + Receive data on a secure connection + + \Input *pState - module state reference + \Input *pSecure - secure state reference + + \Output + int32_t - 1=data was received, else 0 + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLUpdateRecv(ProtoSSLRefT *pState, SecureStateT *pSecure) +{ + int32_t iResult, iXfer = 0; + + // receive ssl record header + if (pSecure->iRecvHead < SSL_MIN_PACKET) + { + iResult = SocketRecv(pState->pSock, (char *)pSecure->RecvHead+pSecure->iRecvHead, SSL_MIN_PACKET-pSecure->iRecvHead, 0); + if (iResult > 0) + { + pSecure->iRecvHead += iResult; + iXfer = 1; + } + if (iResult < 0) + { + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + pState->iClosed = 1; + } + } + // wait for a complete ssl record header before passing this point + if (pSecure->iRecvHead < SSL_MIN_PACKET) + { + return(iXfer); + } + + /* see if we can determine the full packet size + this needs to handle the following situations: + - initial receive of a packet (just the header has been received) + - receive of the second or later packet in a fragmented handshake packet + it needs to NOT be executed in the following scenario: + - when processing a packet that has been received but has not had all of its handshake packets processed */ + if ((pSecure->iRecvProg == pSecure->iRecvSize) && !pSecure->bRecvProc) + { + _ProtoSSLUpdateRecvValidate(pState, pSecure); + } + + // finish receiving ssl record data + if (pSecure->iRecvProg < pSecure->iRecvSize) + { + iResult = SocketRecv(pState->pSock, (char *)pSecure->RecvData+pSecure->iRecvProg, pSecure->iRecvSize-pSecure->iRecvProg, 0); + if (iResult > 0) + { + pSecure->iRecvProg += iResult; + iXfer = 1; + } + if (iResult < 0) + { + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + pState->iClosed = 1; + } + } + + // handle decryption and data validation + if ((pSecure->iRecvProg == pSecure->iRecvSize) && !pSecure->bRecvProc) + { + uint8_t bFirstRecord = (pSecure->iRecvBase == 0) ? TRUE : FALSE; + int32_t iAlert = 0; + // at end of receive, process the packet + if ((iResult = _ProtoSSLRecvPacket(pState, &iAlert)) < 0) + { + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_FATAL, iAlert); + pState->iState = (pState->iState < ST3_SECURE) ? ST_FAIL_SETUP : ST_FAIL_SECURE; + _ProtoSSLRecvReset(pSecure); + pState->iClosed = 1; + } + // remember we've received it + pSecure->bRecvProc = (pSecure->iRecvSize > 0) ? TRUE : FALSE; + // initial handshake offset needs to be updated to skip stuff decryption might have skipped (e.g. AEAD explicit IV in tls1.2) + if (bFirstRecord) + { + pSecure->iRecvHshkProg = pSecure->iRecvBase; + } + } + + // process received packet - note that this might get called multiple times per ssl record, or one or more times per multiple ssl records + if ((pSecure->iRecvSize > 0) && (pSecure->iRecvProg == pSecure->iRecvSize) && ((pSecure->RecvHead[0] != SSL3_REC_APPLICATION) || (pState->iState == ST3_RECV_HELLO)) && (pState->iClosed == 0)) + { + _ProtoSSLUpdateRecvPacket(pState, pSecure); + } + + return(iXfer); +} + +/* + on-demand ca install +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLInitiateCARequest + + \Description + Initiate CA fetch request + + \Input *pState - module state reference + + \Output + int32_t - 0=success, negative=failure + + \Version 02/28/2012 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLInitiateCARequest(ProtoSSLRefT *pState) +{ + // we allow only one fetch request for each ssl negotiation attempt + if (pState->bCertInfoSet && (pState->iCARequestId <= 0) && !pState->bServer) + { + if ((pState->iCARequestId = DirtyCertCARequestCert(&pState->CertInfo, pState->strHost, SockaddrInGetPort(&pState->PeerAddr))) > 0) + { + // save the failure cert, it will be validated again after fetching CA cert + if (pState->pCertToVal == NULL) + { + pState->pCertToVal = DirtyMemAlloc(sizeof(*pState->pCertToVal), PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + // continue to fetch CA cert even if we fail to allocate cert memory + if (pState->pCertToVal != NULL) + { + ds_memcpy(pState->pCertToVal, &pState->pSecure->Cert, sizeof(*pState->pCertToVal)); + } + return(0); + } + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLUpdateCARequest + + \Description + Update CA fetch request status + + \Input *pState - module state reference + + \Output + int32_t - updated module state + + \Version 02/28/2012 (szhu) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLUpdateCARequest(ProtoSSLRefT *pState) +{ + int32_t iComplete; + // see if request completed + if ((iComplete = DirtyCertCARequestDone(pState->iCARequestId)) != 0) + { + DirtyCertCARequestFree(pState->iCARequestId); + pState->iCARequestId = 0; + // if CA fetch request failed + if (iComplete < 0) + { + _CertificateSetFailureInfo(pState, pState->pCertToVal, TRUE); + pState->iState = ST_FAIL_CERT_REQUEST; + } + // if cert not validated + else if ((pState->pCertToVal == NULL) || (_ProtoSSLVerifyCertificate(pState, pState->pSecure, pState->pCertToVal, FALSE) != 0)) + { + _CertificateSetFailureInfo(pState, pState->pCertToVal, TRUE); + pState->iState = ST_FAIL_CERT_NOTRUST; + } + else + { + // cert validated + #if DIRTYCODE_LOGGING + char strIdentSubject[512], strIdentIssuer[512]; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: cert (%s) validated by ca (%s)\n", _CertificateDebugFormatIdent(&pState->pCertToVal->Subject, strIdentSubject, sizeof(strIdentSubject)), + _CertificateDebugFormatIdent(&pState->pCertToVal->Issuer, strIdentIssuer, sizeof(strIdentIssuer)))); + #endif + pState->iState = ST3_RECV_HELLO; + } + + if (pState->pCertToVal != NULL) + { + DirtyMemFree(pState->pCertToVal, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pCertToVal = NULL; + } + } + return(pState->iState); +} + +/* + module state management +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAllocSecureState + + \Description + Allocate secure state + + \Input iMemGroup - memgroup info for alloc + \Input *pMemGroupUserData - memgroup info for alloc + + \Output + SecureStateT * - secure state, or null on alloc failure + + \Version 03/17/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static SecureStateT *_ProtoSSLAllocSecureState(int32_t iMemGroup, void *pMemGroupUserData) +{ + SecureStateT *pSecure = DirtyMemAlloc(sizeof(*pSecure), PROTOSSL_MEMID, iMemGroup, pMemGroupUserData); + if (pSecure != NULL) + { + ds_memclr(pSecure, sizeof(*pSecure)); + } + return(pSecure); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLResetSecureState + + \Description + Reset secure state. Does not affect the TCP connection, if any. + + \Input *pState - Reference pointer + \Input iSecure - secure status (0=disabled, 1=enabled) + + \Output + int32_t - SOCKERR_NONE on success, SOCKERR_NOMEM on failure + + \Version 03/17/2010 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLResetSecureState(ProtoSSLRefT *pState, int32_t iSecure) +{ + SecureStateT *pSecure; + + // acquire access to secure state + NetCritEnter(&pState->SecureCrit); + + // see if we need to get rid of secure state + if (!iSecure && (pState->pSecure != NULL)) + { + DirtyMemFree(pState->pSecure, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pSecure = NULL; + } + + // see if we need to alloc secure state + if (iSecure && (pState->pSecure == NULL)) + { + pState->pSecure = _ProtoSSLAllocSecureState(pState->iMemGroup, pState->pMemGroupUserData); + } + + // reset secure state if present + if ((pSecure = pState->pSecure) != NULL) + { + // clear secure state + ds_memclr(pSecure, sizeof(*pSecure)); + + // reset handshake hashes + _ProtoSSLHandshakeHashInit(pSecure); + } + + // release access to secure state + NetCritLeave(&pState->SecureCrit); + + // return allocate error if secure wanted and failed + return((iSecure && !pSecure) ? SOCKERR_NOMEM : SOCKERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLResetState + + \Description + Reset connection back to unconnected state (will disconnect from server). + + \Input *pState - Reference pointer + \Input iSecure - to be completed + + \Output + int32_t - SOCKERR_NONE on success, SOCKERR_NOMEM on failure + + \Version 03/25/2004 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLResetState(ProtoSSLRefT *pState, int32_t iSecure) +{ + // close socket if needed + if (pState->pSock != NULL) + { + pState->iLastSocketError = SocketInfo(pState->pSock, 'serr', 0, NULL, 0); + SocketClose(pState->pSock); + pState->pSock = NULL; + } + + // done with resolver record + if (pState->pHost != NULL) + { + pState->pHost->Free(pState->pHost); + pState->pHost = NULL; + } + + // free dirtycert certificate + if (pState->pCertToVal != NULL) + { + DirtyMemFree(pState->pCertToVal, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->pCertToVal = NULL; + } + + // done with dirtycert + if (pState->iCARequestId > 0) + { + DirtyCertCARequestFree(pState->iCARequestId); + } + pState->iCARequestId = 0; + + // reset the state + pState->iState = ST_IDLE; + pState->iClosed = 1; + pState->uAlertLevel = 0; + pState->uAlertValue = 0; + pState->bAlertSent = FALSE; + + // reset secure state + return(_ProtoSSLResetSecureState(pState, iSecure)); +} + +/* + trusted store management +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLChkCACert + + \Description + Check to see if the given CA cert already exists in our list of + CA certs. + + \Input *pNewCACert - pointer to new CA cert to check for duplicates of + + \Output + int32_t - zero=not duplicate, non-zero=duplicate + + \Version 05/11/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLChkCACert(const X509CertificateT *pNewCACert) +{ + const ProtoSSLCACertT *pCACert; + + for (pCACert = _ProtoSSL_CACerts; pCACert != NULL; pCACert = pCACert->pNext) + { + if (!_CertificateCompareIdent(&pCACert->Subject, &pNewCACert->Subject, TRUE) && (pCACert->iKeyModSize == pNewCACert->iKeyModSize) && + !memcmp(pCACert->pKeyModData, pNewCACert->KeyModData, pCACert->iKeyModSize)) + { + break; + } + } + + return(pCACert != NULL); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLAddCACert + + \Description + Add a new CA certificate to certificate list + + \Input *pCert - pointer to new cert to add + \Input bVerified - TRUE if verified, else false + \Input iMemGroup - memgroup to use for alloc + \Input *pMemGroupUserData - memgroup userdata for alloc + + \Output + int32_t - zero=error/duplicate, one=added + + \Version 01/13/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLAddCACert(X509CertificateT *pCert, uint8_t bVerified, int32_t iMemGroup, void *pMemGroupUserData) +{ + ProtoSSLCACertT *pCACert; + int32_t iCertSize = sizeof(*pCACert) + pCert->iKeyModSize; + + // see if this certificate already exists + if (_ProtoSSLChkCACert(pCert)) + { + _CertificateDebugPrint(pCert, "ignoring redundant add of CA cert"); + return(0); + } + + // find append point for new CA + for (pCACert = _ProtoSSL_CACerts; pCACert->pNext != NULL; pCACert = pCACert->pNext) + ; + + // allocate new record + if ((pCACert->pNext = (ProtoSSLCACertT *)DirtyMemAlloc(iCertSize, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + _CertificateDebugPrint(pCert, "failed to allocate memory for cert"); + return(0); + } + + // clear allocated memory + pCACert = pCACert->pNext; + ds_memclr(pCACert, iCertSize); + + // if this cert has not already been verified, allocate memory for X509 cert data and copy the X509 cert data for later validation + if (!bVerified) + { + if ((pCACert->pX509Cert = (X509CertificateT *)DirtyMemAlloc(sizeof(*pCert), PROTOSSL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + _CertificateDebugPrint(pCert, "failed to allocate memory for X509 cert"); + DirtyMemFree(pCACert->pNext, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData); + pCACert->pNext = NULL; + return(0); + } + // copy cert data + ds_memcpy(pCACert->pX509Cert, pCert, sizeof(*pCert)); + } + + // copy textual identity of this certificate + // (don't need to save issuer since we already trust this certificate) + ds_memcpy(&pCACert->Subject, &pCert->Subject, sizeof(pCACert->Subject)); + + // copy key info + pCACert->iKeyType = pCert->iKeyType; + pCACert->iCrvType = pCert->iCrvType; + + // copy exponent data + pCACert->iKeyExpSize = pCert->iKeyExpSize; + ds_memcpy(pCACert->KeyExpData, pCert->KeyExpData, pCACert->iKeyExpSize); + + // copy modulus data, immediately following header + pCACert->iKeyModSize = pCert->iKeyModSize; + pCACert->pKeyModData = (uint8_t *)pCACert + sizeof(*pCACert); + ds_memcpy((uint8_t *)pCACert->pKeyModData, pCert->KeyModData, pCACert->iKeyModSize); + + // save memgroup and user info used to allocate + pCACert->iMemGroup = iMemGroup; + + _CertificateDebugPrint(pCert, "added new certificate authority"); + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSetCACert + + \Description + Add one or more X.509 CA certificates to the trusted list. A + certificate added will be available to all ProtoSSL modules for + the lifetime of the application. This functional can add one or more + PEM certificates or a single DER certificate. + + \Input *pCertData - pointer to certificate data (PEM or DER) + \Input iCertSize - size of certificate data + \Input bVerify - if TRUE verify cert chain on add + + \Output + int32_t - negative=error, positive=count of CAs added + + \Notes + The certificate must be in .DER (binary) or .PEM (base64-encoded) + format. + + \Version 01/13/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoSSLSetCACert(const uint8_t *pCertData, int32_t iCertSize, uint8_t bVerify) +{ + int32_t iResult, iCount = -1; + X509CertificateT Cert; + uint8_t *pCertBuffer = NULL; + const int32_t _iMaxCertSize = 4096; + const uint8_t *pCertBeg, *pCertEnd; + int32_t iMemGroup; + uint32_t uCertType; + void *pMemGroupUserData; + SecureStateT *pSecure; + + #if DIRTYCODE_LOGGING + uint32_t uTick = NetTick(); + #endif + + // process PEM signature if present + if (_CertificateFindData(pCertData, iCertSize, &pCertBeg, &pCertEnd, &uCertType) == 0) + { + // no markers -- consume all the data + pCertBeg = pCertData; + pCertEnd = pCertData+iCertSize; + } + + // remember remaining data for possible further parsing + iCertSize -= pCertEnd-pCertData; + pCertData = pCertEnd; + + // get memgroup settings for certificate blob + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // if the cert is base64 encoded we decode it; otherwise we assume it is binary and parse it directly + if ((iResult = Base64Decode2((int32_t)(pCertEnd-pCertBeg), (const char *)pCertBeg, NULL)) > 0) + { + if (iResult > _iMaxCertSize) + { + return(-111); + } + // allocate cert buffer + if ((pCertBuffer = (uint8_t *)DirtyMemAlloc(_iMaxCertSize, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + return(-112); + } + // decode the cert + Base64Decode2((int32_t)(pCertEnd-pCertBeg), (const char *)pCertBeg, (char *)pCertBuffer); + pCertBeg = pCertBuffer; + pCertEnd = pCertBeg+iResult; + } + + // allocate temporary secure state to verify certificate with + if ((pSecure = _ProtoSSLAllocSecureState(iMemGroup, pMemGroupUserData)) == NULL) + { + DirtyMemFree(pCertBuffer, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData); + return(-113); + } + + // parse the x.509 certificate onto stack + if ((iResult = _AsnParseCertificate(&Cert, pCertBeg, (int32_t)(pCertEnd-pCertBeg))) == 0) + { + // verify signature of this certificate (self-signed allowed) + if (!bVerify || ((iResult = _ProtoSSLVerifyCertificate(NULL, pSecure, &Cert, TRUE)) == 0)) + { + // add certificate to CA list + iCount = _ProtoSSLAddCACert(&Cert, bVerify, iMemGroup, pMemGroupUserData); + } + } + + // if CA was PEM encoded and there is extra data, check for more CAs + while ((iResult == 0) && (iCertSize > 0) && (_CertificateFindData(pCertData, iCertSize, &pCertBeg, &pCertEnd, &uCertType) != 0)) + { + // remember remaining data for possible further parsing + iCertSize -= pCertEnd-pCertData; + pCertData = pCertEnd; + + // cert must be base64 encoded + if (((iResult = Base64Decode2((int32_t)(pCertEnd-pCertBeg), (const char *)pCertBeg, NULL)) <= 0) || (iResult > _iMaxCertSize)) + { + break; + } + Base64Decode2((int32_t)(pCertEnd-pCertBeg), (const char *)pCertBeg, (char *)pCertBuffer); + + // parse the x.509 certificate onto stack + if ((iResult = _AsnParseCertificate(&Cert, pCertBuffer, iResult)) < 0) + { + continue; + } + + // verify signature of this certificate (self-signed allowed) + if (bVerify && ((iResult = _ProtoSSLVerifyCertificate(NULL, pSecure, &Cert, TRUE)) < 0)) + { + continue; + } + + // add certificate to CA list + iCount += _ProtoSSLAddCACert(&Cert, bVerify, iMemGroup, pMemGroupUserData); + } + + // cleanup temp secure state + DirtyMemFree(pSecure, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData); + + // cleanup temp allocation + if (pCertBuffer != NULL) + { + DirtyMemFree(pCertBuffer, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData); + } + + NetPrintf(("protossl: SSL Perf (CA load) %dms\n", NetTickDiff(NetTick(), uTick))); + return(iCount); +} + +/* + misc helpers +*/ + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLSetSockOpt + + \Description + Set socket options + + \Input *pState - module state + + \Version 09/12/2012 (jbrookes) Refactored from ProtoSSLBind() and ProtoSSLConnect() +*/ +/********************************************************************************F*/ +static void _ProtoSSLSetSockOpt(ProtoSSLRefT *pState) +{ + // set debug level + SocketControl(pState->pSock, 'spam', pState->iVerbose, NULL, NULL); + + // set recv/send buffer size? + if (pState->iRecvBufSize != 0) + { + SocketControl(pState->pSock, 'rbuf', pState->iRecvBufSize, NULL, NULL); + } + if (pState->iSendBufSize != 0) + { + SocketControl(pState->pSock, 'sbuf', pState->iSendBufSize, NULL, NULL); + } + + // set max send/recv rate? + if (pState->iMaxRecvRate != 0) + { + SocketControl(pState->pSock, 'maxr', pState->iMaxRecvRate, NULL, NULL); + } + if (pState->iMaxSendRate != 0) + { + SocketControl(pState->pSock, 'maxs', pState->iMaxSendRate, NULL, NULL); + } + + // set keep-alive options? + if (pState->bKeepAlive != 0) + { + SocketControl(pState->pSock, 'keep', pState->bKeepAlive, &pState->uKeepAliveTime, &pState->uKeepAliveTime); + } + + // set nodelay? + if (pState->bNoDelay) + { + SocketControl(pState->pSock, 'ndly', TRUE, NULL, NULL); + } + + // set reuseaddr + if (pState->bReuseAddr) + { + SocketControl(pState->pSock, 'radr', TRUE, NULL, NULL); + } + + // if async receive is enabled set it on the socket and adjust the packet queue to fit a large packet (SSL_RCVMAX_PACKET) + if (pState->bAsyncRecv) + { + SocketControl(pState->pSock, 'arcv', TRUE, NULL, NULL); + SocketControl(pState->pSock, 'pque', (SSL_RCVMAX_PACKET / SOCKET_MAXUDPRECV) + 1, NULL, NULL); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoSSLCacheLocalAddress + + \Description + Cache value of our local address being used, some connections are very short + lived making it otherwise difficult to read the local addr info reliably + + \Input *pState - reference pointer + + \Version 09/13/2017 (cvienneau) used in Qos2.0 +*/ +/********************************************************************************F*/ +static void _ProtoSSLCacheLocalAddress(ProtoSSLRefT *pState) +{ + if (pState->pSock != NULL) + { + if (SocketInfo(pState->pSock, 'bind', 0, &pState->LocalAddr, sizeof(pState->LocalAddr)) != 0) + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: _ProtoSSLCacheLocalAddress failed to read local address info from 'bind'.\n")); + } + } + else + { + NetPrintfVerbose((pState->iVerbose, 0, "protossl: _ProtoSSLCacheLocalAddress socket is NULL.\n")); + } +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoSSLStartup + + \Description + Start up ProtoSSL. Used to create global state shared across SSL refs. + + \Output + int32_t - negative=failure, else success + + \Version 09/14/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLStartup(void) +{ + ProtoSSLStateT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // make sure we haven't already been called + if (_ProtoSSL_pState != NULL) + { + NetPrintf(("protossl: global state already allocated\n")); + return(-1); + } + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pState = DirtyMemAlloc(sizeof(*pState), PROTOSSL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protossl: could not allocate global state\n")); + return(-1); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + NetCritInit(&pState->StateCrit, "ProtoSSL Global State"); + + // initalize cyptrand module + if (CryptRandInit() != 0) + { + NetCritKill(&pState->StateCrit); + DirtyMemFree(pState, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData); + return(-1); + } + + // set global defaults + pState->iDfltCiph = PROTOSSL_CIPHER_ALL; + pState->iDfltVers = SSL3_VERSION; + pState->iDfltMinVers = SSL3_TLS1_0; + pState->iDfltCurves = PROTOSSL_CURVE_ALL; + + // save state ref + _ProtoSSL_pState = pState; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLShutdown + + \Description + Shut down ProtoSSL. Cleans up global state. + + \Version 09/14/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoSSLShutdown(void) +{ + ProtoSSLStateT *pState = _ProtoSSL_pState; + + // make sure we haven't already been called + if (pState == NULL) + { + NetPrintf(("protossl: global state not allocated\n")); + return; + } + + // deallocate stored CAs + ProtoSSLClrCACerts(); + + // shutdown cyptrand module + CryptRandShutdown(); + + // shut down, deallocate resources + NetCritKill(&pState->StateCrit); + DirtyMemFree(pState, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + + // clear state pointer + _ProtoSSL_pState = NULL; +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLCreate + + \Description + Allocate an SSL connection and prepare for use + + \Output + ProtoSSLRefT * - module state; NULL=failure + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +ProtoSSLRefT *ProtoSSLCreate(void) +{ + ProtoSSLRefT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pState = DirtyMemAlloc(sizeof(*pState), PROTOSSL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protossl: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pState, sizeof(*pState)); + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserData = pMemGroupUserData; + + // set defaults + pState->iLastSocketError = SOCKERR_NONE; + pState->iVerbose = SSL_VERBOSE_DEFAULT; + pState->bSessionResumeEnabled = TRUE; + pState->iCurveDflt = SSL3_CURVE_DEFAULT; + pState->uHelloExtn = PROTOSSL_HELLOEXTN_DEFAULT; + + // set defaults with global overrides + if (_ProtoSSL_pState != NULL) + { + pState->uSslVersion = _ProtoSSL_pState->iDfltVers; + pState->uSslVersionMin = _ProtoSSL_pState->iDfltMinVers; + pState->uEnabledCiphers = _ProtoSSL_pState->iDfltCiph; + pState->uEnabledCurves = _ProtoSSL_pState->iDfltCurves; + } + else + { + pState->uSslVersion = SSL3_VERSION; + pState->uSslVersionMin = SSL3_TLS1_0; + pState->uEnabledCiphers = PROTOSSL_CIPHER_ALL; + pState->uEnabledCurves = PROTOSSL_CURVE_ALL; + } + + // init secure state critical section + NetCritInit(&pState->SecureCrit, "ProtoSSL Secure State"); + + // return module state + return(pState); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLReset + + \Description + Reset connection back to unconnected state (will disconnect from server). + + \Input *pState - module state reference + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +void ProtoSSLReset(ProtoSSLRefT *pState) +{ + // reset to unsecure mode + _ProtoSSLResetState(pState, 0); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLDestroy + + \Description + Destroy the module and release its state. Will disconnect from the + server if required. + + \Input *pState - module state reference + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +void ProtoSSLDestroy(ProtoSSLRefT *pState) +{ + // reset to unsecure mode (free all secure resources) + _ProtoSSLResetState(pState, 0); + // free certificate, if allocated + if (pState->pCertificate != NULL) + { + DirtyMemFree(pState->pCertificate, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + // free private key, if allocated + if (pState->pPrivateKey != NULL) + { + DirtyMemFree(pState->pPrivateKey, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + // kill critical section + NetCritKill(&pState->SecureCrit); + // free remaining state + DirtyMemFree(pState, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLAccept + + \Description + Accept an incoming connection. + + \Input *pState - module state reference + \Input iSecure - flag indicating use of secure connection: 1=secure, 0=unsecure + \Input *pAddr - where the client's address should be written + \Input *pAddrlen - the length of the client's address space + + \Output + ProtoSSLRefT * - accepted connection or NULL if not available + + \Version 10/27/2013 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoSSLRefT *ProtoSSLAccept(ProtoSSLRefT *pState, int32_t iSecure, struct sockaddr *pAddr, int32_t *pAddrlen) +{ + ProtoSSLRefT *pClient; + SocketT *pSocket; + + // check for connect + pSocket = SocketAccept(pState->pSock, pAddr, pAddrlen); + if (pSocket == NULL) + { + return(NULL); + } + + // we have an incoming connection attempt, so create an ssl object for it + DirtyMemGroupEnter(pState->iMemGroup, pState->pMemGroupUserData); + pClient = ProtoSSLCreate(); + DirtyMemGroupLeave(); + if (pClient == NULL) + { + SocketClose(pSocket); + return(NULL); + } + + // set up new ssl object with the socket we just accepted + if (_ProtoSSLResetState(pClient, iSecure) != SOCKERR_NONE) + { + ProtoSSLDestroy(pClient); + return(NULL); + } + pClient->pSock = pSocket; + ds_memcpy(&pClient->PeerAddr, pAddr, *pAddrlen); + + // update socket status + SocketInfo(pClient->pSock, 'stat', 0, NULL, 0); + + // set client state + pClient->iState = (pClient->pSecure ? ST3_RECV_HELLO : ST_UNSECURE); + pClient->iClosed = 0; + pClient->bServer = TRUE; + return(pClient); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLBind + + \Description + Create a socket bound to the given address. + + \Input *pState - module state reference + \Input *pAddr - the IPv4 address + \Input iAddrlen - the size of the IPv4 address. + + \Output + int32_t - SOCKERR_xxx (zero=success, negative=failure) + + \Version 03/03/2004 (sbevan) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLBind(ProtoSSLRefT *pState, const struct sockaddr *pAddr, int32_t iAddrlen) +{ + // if we had a socket, get last error from it and close it + if (pState->pSock != NULL) + { + pState->iLastSocketError = SocketInfo(pState->pSock, 'serr', 0, NULL, 0); + SocketClose(pState->pSock); + } + + // create the socket + if ((pState->pSock = SocketOpen(AF_INET, SOCK_STREAM, 0)) == NULL) + { + return(SOCKERR_OTHER); + } + + // set socket options + _ProtoSSLSetSockOpt(pState); + + // do the bind, return result + return(SocketBind(pState->pSock, pAddr, iAddrlen)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLConnect + + \Description + Make a secure connection to an SSL server. + + \Input *pState - module state reference + \Input iSecure - flag indicating use of secure connection (1=secure, 0=unsecure) + \Input *pAddr - textual form of address (1.2.3.4 or www.ea.com) + \Input uAddr - the IP address of the server (if not in textual form) + \Input iPort - the TCP port of the server (if not in textual form) + + \Output + int32_t - SOCKERR_xxx (zero=success, negative=failure) + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLConnect(ProtoSSLRefT *pState, int32_t iSecure, const char *pAddr, uint32_t uAddr, int32_t iPort) +{ + int32_t iIndex; + int32_t iError; + + // reset connection state + iError = _ProtoSSLResetState(pState, iSecure); + if (iError != SOCKERR_NONE) + { + return(iError); + } + + // only allow secure connection if dirtycert service name has been set + if ((iSecure != 0) && (DirtyCertStatus('snam', NULL, 0) < 0)) + { + NetPrintf(("protossl: ************************************************************************************\n")); + NetPrintf(("protossl: ProtoSSLConnect() requires a valid DirtyCert service name in the format\n")); + NetPrintf(("protossl: \"game-year-platform\", set when calling NetConnStartup() by using the -servicename\n")); + NetPrintf(("protossl: argument, to be set before SSL use is allowed. If a service name doesn't include\n")); + NetPrintf(("protossl: dashes it is assumed to simply be the 'game' identifier, in which case DirtySDK will\n")); + NetPrintf(("protossl: fill in the year and platform. For titles using BlazeSDK, the service name specified\n")); + NetPrintf(("protossl: must match the BlazeSDK service name, therefore the full name should be specified.\n")); + NetPrintf(("protossl: *** PLEASE SET A UNIQUE AND MEANINGFUL SERVICE NAME, EVEN FOR TOOL OR SAMPLE USE ***\n")); + return(SOCKERR_INVALID); + } + + // allocate the socket + if ((pState->pSock = SocketOpen(AF_INET, SOCK_STREAM, 0)) == NULL) + { + return(SOCKERR_NORSRC); + } + + // set socket options + _ProtoSSLSetSockOpt(pState); + + // init peer structure + SockaddrInit(&pState->PeerAddr, AF_INET); + + // clear previous cert info, if any + pState->bCertInfoSet = FALSE; + ds_memclr(&pState->CertInfo, sizeof(pState->CertInfo)); + + // handle default address case + if (pAddr == NULL) + { + pAddr = ""; + } + + // parse the address string + for (iIndex = 0; (pAddr[iIndex] != 0) && (pAddr[iIndex] != ':') && ((unsigned)iIndex < sizeof(pState->strHost)-1); ++iIndex) + { + // copy over to host + pState->strHost[iIndex] = pAddr[iIndex]; + } + pState->strHost[iIndex] = 0; + + // attempt to set host address + SockaddrInSetAddrText(&pState->PeerAddr, pState->strHost); + if (SockaddrInGetAddr(&pState->PeerAddr) == 0) + { + SockaddrInSetAddr(&pState->PeerAddr, uAddr); + } + + // attempt to set peer address + if (pAddr[iIndex] == ':') + { + SockaddrInSetPort(&pState->PeerAddr, atoi(pAddr+iIndex+1)); + } + else + { + SockaddrInSetPort(&pState->PeerAddr, iPort); + } + + // see if we need to start DNS request + if (SockaddrInGetAddr(&pState->PeerAddr) == 0) + { + // do dns lookup prior to connect + pState->pHost = SocketLookup(pState->strHost, 30*1000); + pState->iState = ST_ADDR; + } + else + { + // set to connect state + pState->iState = ST_CONN; + } + + // return error code + return(SOCKERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLListen + + \Description + Start listening for an incoming connection. + + \Input *pState - module state reference + \Input iBacklog - number of pending connections allowed + + \Output + int32_t - SOCKERR_xxx (zero=success, negative=failure) + + \Version 03/03/2004 (sbevan) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLListen(ProtoSSLRefT *pState, int32_t iBacklog) +{ + return(SocketListen(pState->pSock, iBacklog)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLDisconnect + + \Description + Disconnect from the server + + \Input *pState - module state reference + + \Output + int32_t - SOCKERR_xxx (zero=success, negative=failure) + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLDisconnect(ProtoSSLRefT *pState) +{ + if (pState->pSock) + { + // send a close_notify alert as per http://tools.ietf.org/html/rfc5246#section-7.2.1 + if ((pState->pSecure != NULL) && (pState->iState == ST3_SECURE)) + { + // send the alert + _ProtoSSLSendAlert(pState, SSL3_ALERT_LEVEL_WARNING, SSL3_ALERT_DESC_CLOSE_NOTIFY); + } + + /* if in server mode, just close the write side. on the client we do an immediate hard + close of the socket as the calling application that is polling to drive our operation + may stop updating us once they have received all of the data, which would prevent us + from closing the socket */ + if (pState->bServer) + { + SocketShutdown(pState->pSock, SOCK_NOSEND); + } + else + { + SocketClose(pState->pSock); + pState->pSock = NULL; + } + } + + pState->iState = ST_IDLE; + pState->iClosed = 1; + + // done with dirtycert + if (pState->iCARequestId > 0) + { + DirtyCertCARequestFree(pState->iCARequestId); + } + pState->iCARequestId = 0; + return(SOCKERR_NONE); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLUpdate + + \Description + Give time to module to do its thing (should be called + periodically to allow module to perform work). Calling + once per 100ms or so should be enough. + + This is actually a collection of state functions plus + the overall update function. They are kept together for + ease of reading. + + \Input *pState - module state reference + + \Version 11/10/2005 gschaefer +*/ +/********************************************************************************F*/ +void ProtoSSLUpdate(ProtoSSLRefT *pState) +{ + int32_t iXfer; + int32_t iResult; + SecureStateT *pSecure = pState->pSecure; + + // resolve the address + if (pState->iState == ST_ADDR) + { + // check for completion + if (pState->pHost->Done(pState->pHost)) + { + pState->iState = (pState->pHost->addr != 0) ? ST_CONN : ST_FAIL_DNS; + SockaddrInSetAddr(&pState->PeerAddr, pState->pHost->addr); + // free the record + pState->pHost->Free(pState->pHost); + pState->pHost = NULL; + } + } + + // see if we should start a connection + if (pState->iState == ST_CONN) + { + // start the connection attempt + if ((iResult = SocketConnect(pState->pSock, &pState->PeerAddr, sizeof pState->PeerAddr)) == SOCKERR_NONE) + { + pState->iState = ST_WAIT_CONN; + } + else + { + pState->iState = ST_FAIL_CONN; + pState->iClosed = 1; + } + } + + // wait for connection + if (pState->iState == ST_WAIT_CONN) + { + iResult = SocketInfo(pState->pSock, 'stat', 0, NULL, 0); + if (iResult > 0) + { + pState->iState = pSecure ? ST3_SEND_HELLO : ST_UNSECURE; + pState->iClosed = 0; + _ProtoSSLCacheLocalAddress(pState); + } + if (iResult < 0) + { + pState->iState = ST_FAIL_CONN; + pState->iClosed = 1; + } + } + + // handle secure i/o (non-secure is done immediately in ProtoSSLSend/ProtoSSLRecv) + while ((pState->pSock != NULL) && (pState->iState >= ST3_SEND_HELLO) && (pState->iState <= ST3_SECURE)) + { + // get access to secure state + NetCritEnter(&pState->SecureCrit); + + // update async processing, if any + if (_ProtoSSLUpdateAsync(pState, pSecure)) + { + NetCritLeave(&pState->SecureCrit); + break; + } + + // update send processing + iXfer = _ProtoSSLUpdateSend(pState, pSecure); + + // update recv processing + iXfer += _ProtoSSLUpdateRecv(pState, pSecure); + + // release access to secure state + NetCritLeave(&pState->SecureCrit); + + // break out of loop if no i/o activity + if (iXfer == 0) + { + break; + } + } + + // wait for CA cert (this comes last intentionally) + if (pState->iState == ST_WAIT_CA) + { + // acquire secure state crit section to guard dirtycert access + NetCritEnter(&pState->SecureCrit); + + // update CA request processing + _ProtoSSLUpdateCARequest(pState); + + // release secure state crit section + NetCritLeave(&pState->SecureCrit); + } +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLSend + + \Description + Send data to the server over secure TCP connection (actually, whether the + connection is secure or not is determined by the secure flag passed during + the SSLConnect call). + + \Input *pState - module state reference + \Input *pBuffer - data to send + \Input iLength - length of data (if negative, input data is assumed to be null-terminated string) + + \Output + int32_t - negative=error, otherwise number of bytes sent + + \Version 06/03/2002 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLSend(ProtoSSLRefT *pState, const char *pBuffer, int32_t iLength) +{ + int32_t iResult = SOCKERR_CLOSED; + SecureStateT *pSecure = pState->pSecure; + + // allow easy string sends + if (iLength < 0) + { + iLength = (int32_t)strlen(pBuffer); + } + // guard against zero-length sends, which can result in an invalid send condition with some stream (e.g. RC4) ciphers + if (iLength == 0) + { + return(0); + } + + // make sure connection established + if (pState->iState == ST3_SECURE) + { + iResult = 0; + + // get access to secure state + NetCritEnter(&pState->SecureCrit); + + // make sure buffer is empty + if (pSecure->iSendSize == 0) + { + // limit send length + if (iLength > SSL_SNDLIM_PACKET) + { + iLength = SSL_SNDLIM_PACKET; + } + + // setup packet for send + if (_ProtoSSLSendPacket(pState, SSL3_REC_APPLICATION, NULL, 0, pBuffer, iLength) == 0) + { + iResult = iLength; + // try and send now + ProtoSSLUpdate(pState); + } + } + + // release access to secure state + NetCritLeave(&pState->SecureCrit); + } + + // handle unsecure sends + if (pState->iState == ST_UNSECURE) + { + iResult = SocketSend(pState->pSock, pBuffer, iLength, 0); + } + + // return the result + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLRecv + + \Description + Receive data from the server + + \Input *pState - module state reference + \Input *pBuffer - receiver data + \Input iLength - maximum buffer length + + \Output + int32_t - negative=error, zero=nothing available, positive=bytes received + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLRecv(ProtoSSLRefT *pState, char *pBuffer, int32_t iLength) +{ + SecureStateT *pSecure = pState->pSecure; + int32_t iResult = 0; + + // make sure in right state + if (pState->iState == ST3_SECURE) + { + // get access to secure state + NetCritEnter(&pState->SecureCrit); + + // check for more data if no packet pending + if ((pSecure->iRecvProg == 0) || (pSecure->iRecvProg != pSecure->iRecvSize)) + { + ProtoSSLUpdate(pState); + } + + // check for end of data + if (((pSecure->iRecvSize < SSL_MIN_PACKET) || (pSecure->iRecvProg < pSecure->iRecvSize)) && (pState->iClosed)) + { + iResult = SOCKERR_CLOSED; + } + // see if data pending + else if ((pSecure->iRecvProg == pSecure->iRecvSize) && (pSecure->iRecvBase < pSecure->iRecvSize) && + (pSecure->RecvHead[0] == SSL3_REC_APPLICATION)) + { + iResult = pSecure->iRecvSize-pSecure->iRecvBase; + // only return what user can store + if (iResult > iLength) + { + iResult = iLength; + } + // return the data + ds_memcpy(pBuffer, pSecure->RecvData+pSecure->iRecvBase, iResult); + pSecure->iRecvBase += iResult; + + // see if we can grab a new packet + if (pSecure->iRecvBase >= pSecure->iRecvSize) + { + _ProtoSSLRecvReset(pSecure); + } + } + + // release access to secure state + NetCritLeave(&pState->SecureCrit); + } + + // handle unsecure receive + if (pState->iState == ST_UNSECURE) + { + iResult = SocketRecv(pState->pSock, pBuffer, iLength, 0); + } + + // return error if in failure state + if (pState->iState >= ST_FAIL) + { + iResult = -1; + } + + // terminate buffer if there is room + if ((iResult > 0) && (iResult < iLength)) + { + pBuffer[iResult] = 0; + } + + // return the data size + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLStat + + \Description + Return the current module status (according to selector) + + \Input *pState - module state reference + \Input iSelect - status selector ('conn'=return "am i connected" flag) + \Input pBuffer - buffer pointer + \Input iLength - buffer size + + \Output + int32_t - negative=error, zero=false, positive=true + + \Notes + Selectors are: + + \verbatim + SELECTOR DESCRIPTION + 'addr' Address of peer we are connecting/connected to + 'alpn' Get the negotiated protocol using the ALPN extension from the secure state + 'alrt' Return alert status 0=no alert, 1=recv alert, 2=sent alert; alert desc copied to pBuffer + 'cert' Return SSL cert info (valid after 'fail') + 'cfip' TRUE/FALSE indication if a CA fetch is in progress + 'ciph' Cipher suite negotiated (string name in output buffer) + 'crpt' Returns whether we are performing any crypto operations at this time + 'fail' Return PROTOSSL_ERROR_* or zero if no error + 'hres' Return hResult containing either the socket error or ssl error + 'htim' Return timing of most recent SSL handshake in milliseconds + 'ladd' cached local client address used for the last connection; copy into pBuffer, buffer must be at least sizeof(struct sockaddr) + 'maxr' Return max recv rate (0=uncapped) + 'maxs' Return max send rate (0=uncapped) + 'recv' Return number of bytes in recv buffer (secure only) + 'resu' Returns whether session was resumed or not + 'rsao' [DEPRECATED] - same as 'crpt' + 'send' Return number of bytes in send buffer (secure only) + 'serr' Return socket error + 'salg' Signature algorithm negotiated (string name in output buffer); not available on resume + 'sock' Copy SocketT ref to output buffer + 'stat' Return like SocketInfo('stat') + 'vers' Return current SSL version + \endverbatim + + \Version 03/08/2002 (gschaefer) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLStat(ProtoSSLRefT *pState, int32_t iSelect, void *pBuffer, int32_t iLength) +{ + int32_t iResult = -1; + + // pass-through to SocketInfo(NULL,...) + if (pState == NULL) + { + return(SocketInfo(NULL, iSelect, 0, pBuffer, iLength)); + } + + // return address of peer we are trying to connect to + if (iSelect == 'addr') + { + if ((pBuffer != NULL) && (iLength == sizeof(pState->PeerAddr))) + { + ds_memcpy(pBuffer, &pState->PeerAddr, iLength); + } + return(SockaddrInGetAddr(&pState->PeerAddr)); + } + if ((iSelect == 'alpn') && (pState->pSecure != NULL)) + { + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, pState->pSecure->strAlpnProtocol, iLength); + } + + return(0); + } + // return most recent alert if any + if (iSelect == 'alrt') + { + if ((pBuffer != NULL) && (iLength == sizeof(ProtoSSLAlertDescT))) + { + ProtoSSLAlertDescT Alert; + if ((iResult = _ProtoSSLGetAlert(pState, &Alert, pState->uAlertLevel, pState->uAlertValue)) != 0) + { + ds_memcpy(pBuffer, &Alert, sizeof(Alert)); + iResult = pState->bAlertSent ? 2 : 1; + } + } + return(iResult); + } + // return certificate info (valid after 'fail' response) + if ((iSelect == 'cert') && (pBuffer != NULL) && (iLength == sizeof(pState->CertInfo))) + { + ds_memcpy(pBuffer, &pState->CertInfo, sizeof(pState->CertInfo)); + return(0); + } + // return if a CA fetch request is in progress + if (iSelect == 'cfip') + { + return((pState->iState == ST_WAIT_CA) ? 1 : 0); + } + // return current cipher suite + if ((iSelect == 'ciph') && (pState->pSecure != NULL) && (pState->pSecure->pCipher != NULL)) + { + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, pState->pSecure->pCipher->strName, iLength); + } + return(pState->pSecure->pCipher->uId); + } + // return whether we are performing any crypto operations at this time + if ((iSelect == 'crpt') || (iSelect == 'rsao')) + { + switch (pState->iState) + { + case ST3_SEND_HELLO: + case ST3_SEND_EXTN: + case ST3_SEND_KEY: + case ST3_SEND_VERIFY: + case ST3_SEND_CHANGE: + case ST3_RECV_CHANGE: + case ST3_PROC_ASYNC: + iResult = TRUE; + break; + default: + iResult = FALSE; + break; + } + return(iResult); + } + // return timing of most recent SSL handshake in milliseconds + if ((iSelect == 'htim') && (pState->pSecure != NULL)) + { + return(pState->pSecure->uTimer); + } + // cached local client address used for the last connection; copy into pBuffer, buffer must be at least sizeof(struct sockaddr) + if (iSelect == 'ladd') + { + if ((pBuffer != NULL) && (iLength >= (int32_t)sizeof(pState->LocalAddr))) + { + ds_memcpy(pBuffer, &pState->LocalAddr, sizeof(pState->LocalAddr)); + return(0); + } + return(-1); + } + // return configured max receive rate + if (iSelect == 'maxr') + { + return(pState->iMaxRecvRate); + } + // return configured max send rate + if (iSelect == 'maxs') + { + return(pState->iMaxSendRate); + } + // return number of bytes in recv buffer (useful only when connection type is secure) + if (iSelect == 'recv') + { + return((pState->pSecure != NULL) ? pState->pSecure->iRecvSize-pState->pSecure->iRecvProg : 0); + } + // return whether session was resumed or not + if ((iSelect == 'resu') && (pState->pSecure != NULL)) + { + return(pState->pSecure->bSessionResume); + } + // return current signature algorithm if available + if ((iSelect == 'salg') && (pState->pSecure != NULL)) + { + if ((pBuffer != NULL) && (pState->pSecure->Cert.iSigType >= ASN_OBJ_RSA_PKCS_MD5)) + { + ds_strnzcpy(pBuffer, _SSL3_strSignatureTypes[pState->pSecure->Cert.iSigType-ASN_OBJ_RSA_PKCS_MD5], iLength); + return(1); + } + } + // return number of bytes in send buffer (useful only when connection type is secure) + if (iSelect == 'send') + { + return((pState->pSecure != NULL) ? pState->pSecure->iSendSize-pState->pSecure->iSendProg : 0); + } + // return last socket error + if (iSelect == 'serr') + { + // pass through to socket module if we have a socket, else return cached last error + return((pState->pSock != NULL) ? SocketInfo(pState->pSock, iSelect, 0, pBuffer, iLength) : pState->iLastSocketError); + } + // return socket ref + if (iSelect == 'sock') + { + if ((pBuffer == NULL) || (iLength != sizeof(pState->pSock))) + { + return(-1); + } + ds_memcpy(pBuffer, &pState->pSock, sizeof(pState->pSock)); + return(0); + } + // return current ssl version for the connection + if ((iSelect == 'vers') && (pState->pSecure != NULL)) + { + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, _SSL3_strVersionNames[pState->pSecure->uSslVersion&0xff], iLength); + } + return(pState->pSecure->uSslVersion); + } + // return failure code + if (iSelect == 'fail') + { + if (pState->iState & ST_FAIL) + { + switch (pState->iState) + { + case ST_FAIL_DNS: + iResult = PROTOSSL_ERROR_DNS; + break; + case ST_FAIL_CONN: + iResult = PROTOSSL_ERROR_CONN; + break; + case ST_FAIL_CONN_SSL2: + iResult = PROTOSSL_ERROR_CONN_SSL2; + break; + case ST_FAIL_CONN_NOTSSL: + iResult = PROTOSSL_ERROR_CONN_NOTSSL; + break; + case ST_FAIL_CONN_MINVERS: + iResult = PROTOSSL_ERROR_CONN_MINVERS; + break; + case ST_FAIL_CONN_MAXVERS: + iResult = PROTOSSL_ERROR_CONN_MAXVERS; + break; + case ST_FAIL_CONN_NOCIPHER: + iResult = PROTOSSL_ERROR_CONN_NOCIPHER; + break; + case ST_FAIL_CONN_NOCURVE: + iResult = PROTOSSL_ERROR_CONN_NOCURVE; + break; + case ST_FAIL_CERT_NONE: + iResult = PROTOSSL_ERROR_CERT_MISSING; + break; + case ST_FAIL_CERT_INVALID: + iResult = PROTOSSL_ERROR_CERT_INVALID; + break; + case ST_FAIL_CERT_HOST: + iResult = PROTOSSL_ERROR_CERT_HOST; + break; + case ST_FAIL_CERT_NOTRUST: + iResult = PROTOSSL_ERROR_CERT_NOTRUST; + break; + case ST_FAIL_CERT_BADDATE: + iResult = PROTOSSL_ERROR_CERT_BADDATE; + break; + case ST_FAIL_CERT_REQUEST: + iResult = PROTOSSL_ERROR_CERT_REQUEST; + break; + case ST_FAIL_SETUP: + iResult = PROTOSSL_ERROR_SETUP; + break; + case ST_FAIL_SECURE: + iResult = PROTOSSL_ERROR_SECURE; + break; + default: + iResult = PROTOSSL_ERROR_UNKNOWN; + break; + } + } + else + { + iResult = 0; + } + return(iResult); + } + + if (iSelect == 'hres') + { + uint32_t hResult; + int32_t iSerr = ProtoSSLStat(pState, 'serr', NULL, 0); + int32_t iFail = ProtoSSLStat(pState, 'fail', NULL, 0); + + if (iSerr < SOCKERR_CLOSED) + { + hResult = DirtyErrGetHResult(DIRTYAPI_SOCKET, iSerr, TRUE); + } + else if (iFail != 0) + { + hResult = DirtyErrGetHResult(DIRTYAPI_PROTO_SSL, iFail, TRUE); + } + else + { + hResult = DirtyErrGetHResult(DIRTYAPI_PROTO_SSL, 0, FALSE); //success hResult + } + return(hResult); + } + + // only pass through if socket is valid + if (pState->pSock != NULL) + { + // special processing for 'stat' selector + if (iSelect == 'stat') + { + // if we're in a failure state, return error + if (pState->iState >= ST_FAIL) + { + return(-1); + } + // don't check connected status until we are connected and done with secure negotiation (if secure) + if (pState->iState < ST3_SECURE) + { + return(0); + } + // if we're connected (in ST_UNSECURE or ST3_SECURE state) fall through + } + + // pass through request to the socket module + iResult = SocketInfo(pState->pSock, iSelect, 0, pBuffer, iLength); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLControl + + \Description + ProtoSSL control function. Different selectors control different behaviors. + + \Input *pState - module state reference + \Input iSelect - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR DESCRIPTION + 'alpn' Set the ALPN protocols (using IANA registerd named) as a comma delimited list + 'arcv' Set async receive on the ssl socket + 'ccrt' Set client certificate level (0=disabled, 1=requested, 2=required) + 'ciph' Set enabled/disabled ciphers (PROTOSSL_CIPHER_*) + 'crvd' Set default curve (-1=no default; else [0,SSL3_EC_MAX] + 'curv' Set enabled/disabled curves (PROTOSS_CURVE_*) + 'extn' Set enabled ClientHello extensions (PROTOSSL_HELLOEXTN_*), (default=0=disabled) + 'gcph' Set global cipher default + 'gcrv' Set global curve default + 'gvrs' Set global version default + 'gvmn' Set global version min default + 'host' Set remote host + 'hreq' Send HelloRequest (server only) + 'maxr' Set max recv rate (0=uncapped, default) + 'maxs' Set max send rate (0=uncapped, default) + 'ncrt' Disable client certificate validation + 'rbuf' Set socket recv buffer size (must be called before Connect()) + 'resu' Set whether session resume is enabled or disabled (default=1=enabled) + 'sbuf' Set socket send buffer size (must be called before Connect()) + 'scrt' Set certificate (pValue=cert, iValue=len) + 'snod' Set whether TCP_NODELAY option is enabled or disabled on the socket (must be called before Connect()) + 'secu' Start secure negotiation on an established unsecure connection + 'skey' Set private key (pValue=key, iValue=len) + 'skep' Set socket keep-alive settings (iValue=enable/disable, iValue2=keep-alive time/interval) + 'spam' Set debug logging level (default=1) + 'vers' Set client-requested SSL version (default=0x302, TLS1.1) + 'vmin' Set minimum SSL version application will accept + \endverbatim + + \Version 01/27/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLControl(ProtoSSLRefT *pState, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue) +{ + int32_t iResult = -1; + + if (iSelect == 'alpn') + { + uint16_t uProtocol, uAlpnExtensionLength; + const char *pSrc = (const char *)pValue; + if (pSrc == NULL) + { + NetPrintf(("protossl: invalid ALPN extension protocol list provided\n")); + return(-1); + } + NetPrintfVerbose((pState->iVerbose, 0, "protossl: setting the ALPN extension protocols using %s\n", (const char *)pValue)); + ds_memclr(pState->aAlpnProtocols, sizeof(pState->aAlpnProtocols)); + + for (uProtocol = 0, uAlpnExtensionLength = 0; uProtocol < SSL_ALPN_MAX_PROTOCOLS; uProtocol += 1) + { + AlpnProtocolT *pProtocol = &pState->aAlpnProtocols[uProtocol]; + if ((pProtocol->uLength = (uint8_t)ds_strsplit(pSrc, ',', pProtocol->strName, sizeof(pProtocol->strName), &pSrc)) == 0) + { + break; + } + + uAlpnExtensionLength += pProtocol->uLength; + uAlpnExtensionLength += sizeof(pProtocol->uLength); + } + pState->uNumAlpnProtocols = uProtocol; + pState->uAlpnExtensionLength = uAlpnExtensionLength; + + return(0); + } + if (iSelect == 'arcv') + { + pState->bAsyncRecv = (uint8_t)iValue; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: async recv set to %s\n", pState->bAsyncRecv ? "TRUE" : "FALSE")); + return(0); + } + if (iSelect == 'ccrt') + { + NetPrintf(("protossl: setting client cert level to %d\n", iValue)); + pState->iClientCertLevel = iValue; + return(0); + } + if (iSelect == 'ciph') + { + pState->uEnabledCiphers = (uint32_t)iValue; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: enabled ciphers=%d\n", iValue)); + return(0); + } + if (iSelect == 'crvd') + { + // attempt to validate the curve is enabled + int8_t iCurveDflt = (int8_t)DS_CLAMP(iValue, -1, SSL3_CURVE_MAX); + if ((iCurveDflt == -1) || ((_SSL3_EllipticCurves[iCurveDflt].uId & pState->uEnabledCurves) != 0)) + { + pState->iCurveDflt = iCurveDflt; + NetPrintf(("protossl: choosing curve %d\n", pState->iCurveDflt)); + return(0); + } + else + { + NetPrintf(("protossl: selected curve %d is disabled and cannot be used as default\n", iCurveDflt)); + return(-1); + } + } + if (iSelect == 'curv') + { + pState->uEnabledCurves = (uint32_t)iValue; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: enabled curves=%u\n", pState->uEnabledCurves)); + return(0); + } + if (iSelect == 'extn') + { + pState->uHelloExtn = (uint8_t)iValue; + NetPrintfVerbose((pState->iVerbose, 0, "protossl: clienthello extensions set to 0x%02x\n", pState->uHelloExtn)); + return(0); + } + // handle global settings + if (((iSelect == 'gcph') || (iSelect == 'gcrv') || (iSelect == 'gver') || (iSelect == 'gvmn')) && (_ProtoSSL_pState != NULL)) + { + if (iSelect == 'gcph') + { + NetPrintf(("protossl: setting global default cipher mask to 0x%x\n", iValue)); + _ProtoSSL_pState->iDfltCiph = iValue; + return(0); + } + if (iSelect == 'gcrv') + { + NetPrintf(("protossl: setting global default curve mask to 0x%08x\n", iValue)); + _ProtoSSL_pState->iDfltCurves = iValue; + return(0); + } + if (iSelect == 'gver') + { + NetPrintf(("protossl: setting global default version to 0x%x\n", iValue)); + _ProtoSSL_pState->iDfltVers = iValue; + return(0); + } + if (iSelect == 'gvmn') + { + NetPrintf(("protossl: setting global default min version to 0x%x\n", iValue)); + _ProtoSSL_pState->iDfltMinVers = iValue; + return(0); + } + } + if (iSelect == 'host') + { + NetPrintf(("protossl: setting host to '%s'\n", (const char *)pValue)); + ds_strnzcpy(pState->strHost, (const char *)pValue, sizeof(pState->strHost)); + return(0); + } + if ((iSelect == 'hreq') && (pState->bServer) && (pState->iState == ST3_SECURE)) + { + NetPrintf(("protossl: sending Hello Request\n")); + pState->iState = ST3_SEND_HELLO_REQUEST; + return(0); + } + if (iSelect == 'maxr') + { + pState->iMaxRecvRate = iValue; + if (pState->pSock != NULL) + { + SocketControl(pState->pSock, iSelect, iValue, NULL, NULL); + } + return(0); + } + if (iSelect == 'maxs') + { + pState->iMaxSendRate = iValue; + if (pState->pSock != NULL) + { + SocketControl(pState->pSock, iSelect, iValue, NULL, NULL); + } + return(0); + } + if (iSelect == 'ncrt') + { + pState->bAllowAnyCert = (uint8_t)iValue; + return(0); + } + if (iSelect == 'radr') + { + pState->bReuseAddr = TRUE; + return(0); + } + if (iSelect == 'rbuf') + { + pState->iRecvBufSize = iValue; + return(0); + } + if (iSelect == 'resu') + { + pState->bSessionResumeEnabled = iValue ? TRUE : FALSE; + return(0); + } + if (iSelect == 'sbuf') + { + pState->iSendBufSize = iValue; + return(0); + } + if (iSelect == 'scrt') + { + if (pState->pCertificate != NULL) + { + DirtyMemFree(pState->pCertificate, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + } + pState->pCertificate = _CertificateDecodePublic(pState, (uint8_t *)pValue, iValue); + return(0); + } + if (iSelect == 'snod') + { + pState->bNoDelay = (uint8_t)iValue; + return(0); + } + if (iSelect == 'skey') + { + if (pState->pPrivateKey != NULL) + { + DirtyMemFree(pState->pPrivateKey, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData); + pState->iPrivateKeyLen = 0; + } + if ((pState->pPrivateKey = DirtyMemAlloc(iValue, PROTOSSL_MEMID, pState->iMemGroup, pState->pMemGroupUserData)) != NULL) + { + ds_memcpy(pState->pPrivateKey, pValue, iValue); + pState->iPrivateKeyLen = iValue; + } + else + { + NetPrintf(("protossl: could not allocate memory for private key\n")); + } + return(0); + } + if (iSelect == 'skep') + { + pState->bKeepAlive = (uint8_t)iValue; + pState->uKeepAliveTime = (uint32_t)iValue2; + return(0); + } + if (iSelect == 'secu') + { + if (pState->iState != ST_UNSECURE) + { + NetPrintf(("protossl: cannot promote to a secure connection unless connected in unsecure state\n")); + return(-1); + } + _ProtoSSLResetSecureState(pState, 1); + pState->iState = ST3_SEND_HELLO; + return(0); + } + if (iSelect == 'spam') + { + pState->iVerbose = (int8_t)iValue; + return(0); + } + if (iSelect == 'vers') + { + uint32_t uSslVersion = DS_CLAMP(iValue, pState->uSslVersionMin, SSL3_VERSION_MAX); + if (pState->uSslVersion != uSslVersion) + { + NetPrintf(("protossl: setting sslvers to %s (0x%04x)\n", _SSL3_strVersionNames[uSslVersion&0xff], uSslVersion)); + pState->uSslVersion = uSslVersion; + } + return(0); + } + if (iSelect == 'vmin') + { + uint32_t uSslVersionMin = DS_CLAMP(iValue, SSL3_VERSION_MIN, SSL3_VERSION_MAX); + if (pState->uSslVersionMin != uSslVersionMin) + { + NetPrintf(("protossl: setting min sslvers to %s (0x%04x)\n", _SSL3_strVersionNames[uSslVersionMin&0xff], uSslVersionMin)); + pState->uSslVersionMin = uSslVersionMin; + // make sure requested version is at least minimum version + ProtoSSLControl(pState, 'vers', pState->uSslVersion, 0, NULL); + } + return(0); + } + // if we have a socket ref, pass unhandled selector through + if (pState->pSock != NULL) + { + iResult = SocketControl(pState->pSock, iSelect, iValue, pValue, NULL); + } + else + { + NetPrintf(("protossl: ProtoSSLControl('%C') unhandled\n", iSelect)); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLSetCACert + + \Description + Add one or more X.509 CA certificates to the trusted list. A + certificate added will be available to all ProtoSSL instances for + the lifetime of the application. This function can add one or more + PEM certificates or a single DER certificate. + + \Input *pCertData - pointer to certificate data (PEM or DER) + \Input iCertSize - size of certificate data + + \Output + int32_t - negative=error, positive=count of CAs added + + \Notes + The certificate must be in .DER (binary) or .PEM (base64-encoded) + format. + + \Version 01/13/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLSetCACert(const uint8_t *pCertData, int32_t iCertSize) +{ + return(_ProtoSSLSetCACert(pCertData, iCertSize, TRUE)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLSetCACert2 + + \Description + Add one or more X.509 CA certificates to the trusted list. A + certificate added will be available to all ProtoSSL instances for + the lifetime of the application. This function can add one or more + PEM certificates or a single DER certificate. + + This version of the function does not validate the CA at load time. + The X509 certificate data will be copied and retained until the CA + is validated, either by use of ProtoSSLValidateAllCA() or by the CA + being used to validate a certificate. + + \Input *pCertData - pointer to certificate data (PEM or DER) + \Input iCertSize - size of certificate data + + \Output + int32_t - negative=error, positive=count of CAs added + + \Notes + The certificate must be in .DER (binary) or .PEM (base64-encoded) + format. + + \Version 04/21/2011 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLSetCACert2(const uint8_t *pCertData, int32_t iCertSize) +{ + return(_ProtoSSLSetCACert(pCertData, iCertSize, FALSE)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLValidateAllCA + + \Description + Validate all CA that have been added but not yet been validated. Validation + is a one-time process and disposes of the X509 certificate that is retained + until validation. + + \Output + int32_t - zero on success; else the number of certs that could not be validated + + \Version 04/21/2011 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLValidateAllCA(void) +{ + ProtoSSLCACertT *pCACert; + SecureStateT *pSecure; + void *pMemGroupUserData; + int32_t iInvalid, iMemGroup; + + // get memgroup settings for certificate blob + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate secure state for certificate validation + if ((pSecure = _ProtoSSLAllocSecureState(iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protossl: could not allocate secure state for ca validation\n")); + return(-1); + } + + // validate all installed CA Certs that have not yet been validated + for (pCACert = _ProtoSSL_CACerts, iInvalid = 0; pCACert != NULL; pCACert = pCACert->pNext) + { + // if the CA hasn't been verified already, do that now + if (pCACert->pX509Cert != NULL) + { + if (_ProtoSSLVerifyCertificate(NULL, pSecure, pCACert->pX509Cert, TRUE) == 0) + { + #if DIRTYCODE_LOGGING + char strIdentSubject[512], strIdentIssuer[512]; + NetPrintf(("protossl: ca (%s) validated by ca (%s)\n", _CertificateDebugFormatIdent(&pCACert->pX509Cert->Subject, strIdentSubject, sizeof(strIdentSubject)), + _CertificateDebugFormatIdent(&pCACert->pX509Cert->Issuer, strIdentIssuer, sizeof(strIdentIssuer)))); + #endif + + // cert successfully verified + DirtyMemFree(pCACert->pX509Cert, PROTOSSL_MEMID, pCACert->iMemGroup, pCACert->pMemGroupUserData); + pCACert->pX509Cert = NULL; + } + else + { + _CertificateDebugPrint(pCACert->pX509Cert, "ca could not be validated"); + iInvalid += 1; + } + } + } + + // free secure state used for validation + DirtyMemFree(pSecure, PROTOSSL_MEMID, iMemGroup, pMemGroupUserData); + + // return number of certs we could not validate + return(iInvalid); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLClrCACerts + + \Description + Clears all dynamic CA certs from the list. + + \Version 01/14/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoSSLClrCACerts(void) +{ + ProtoSSLCACertT *pCACert, *pCACert0=NULL; + + /* + * This code makes the following assumptions: + * 1) There is at least one static cert. + * 2) All static certs come first, followed by all dynamic certs. + */ + + // scan for first dynamic certificate + for (pCACert = _ProtoSSL_CACerts; (pCACert != NULL) && (pCACert->iMemGroup == 0); ) + { + pCACert0 = pCACert; + pCACert = pCACert->pNext; + } + // any dynamic certs? + if ((pCACert != NULL) && (pCACert0 != NULL)) + { + // null-terminate static list + pCACert0->pNext = NULL; + + // delete dynamic certs + for ( ; pCACert != NULL; ) + { + pCACert0 = pCACert->pNext; + if (pCACert->pX509Cert != NULL) + { + DirtyMemFree(pCACert->pX509Cert, PROTOSSL_MEMID, pCACert->iMemGroup, pCACert->pMemGroupUserData); + } + DirtyMemFree(pCACert, PROTOSSL_MEMID, pCACert->iMemGroup, pCACert->pMemGroupUserData); + pCACert = pCACert0; + } + } +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLPkcs1GenerateInit + + \Description + Init for generating a PKCSv1.5 RSA signature + + \Input *pPkcs1 - pkcs1 state + \Input *pHashData - hash of the data which was signed + \Input iHashLen - length of the hash + \Input iHashType - the hash which was used + \Input iModSize - size of the modulus + \Input *pPrimeP - prime p from the private key + \Input *pPrimeQ - prime q from the private key + \Input *pExponentP - exponent p from the private key + \Input *pExponentQ - exponent q from the private key + \Input *pCoefficient- coefficient from the private key + + \Notes + This function will block on the RSA operation. + + \Output + int32_t - zero=validation successful, negative=validation failed + + \Version 03/15/2020 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLPkcs1GenerateInit(ProtoSSLPkcs1T *pPkcs1, const uint8_t *pHashData, int32_t iHashLen, int32_t iHashType, int32_t iModSize, const CryptBinaryObjT *pPrimeP, const CryptBinaryObjT *pPrimeQ, const CryptBinaryObjT *pExponentP, const CryptBinaryObjT *pExponentQ, const CryptBinaryObjT *pCoefficient) +{ + static uint8_t aSigData[SSL_SIG_MAX]; + int32_t iSigSize; + + // generate the signature data + if ((iSigSize = _AsnWriteDigitalHashObject(aSigData, sizeof(aSigData), pHashData, iHashLen, (CryptHashTypeE)iHashType)) == 0) + { + return(-1); + } + #if DEBUG_RAW_DATA + NetPrintMem(pHashData, iHashLen, "message digest"); + #endif + + // init the rsa module with the private key and signature information + if (CryptRSAInit2(&pPkcs1->RSAContext, iModSize, pPrimeP, pPrimeQ, pExponentP, pExponentQ, pCoefficient)) + { + return(-1); + } + CryptRSAInitPrivate(&pPkcs1->RSAContext, aSigData, iSigSize); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLPkcs1GenerateUpdate + + \Description + Generate the PKCSv1.5 RSA signature + + \Input *pPkcs1 - pkcs1 state + \Input iNumIterations - number of iterations to perform or zero to block until complete + \Input *pSigData - [out] signature data + \Input iSigSize - size of the signature + + \Output + int32_t - 1=operation pending, 0=operation complete + + \Version 03/15/2020 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLPkcs1GenerateUpdate(ProtoSSLPkcs1T *pPkcs1, int32_t iNumIterations, uint8_t *pSigData, int32_t iSigSize) +{ + // perform the encryption rounds + if (CryptRSAEncrypt(&pPkcs1->RSAContext, iNumIterations) > 0) + { + return(1); + } + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (pkcs1 sig encrypt) %dms\n", pPkcs1->RSAContext.uCryptMsecs)); + + #if DEBUG_RAW_DATA + NetPrintMem(pPkcs1->RSAContext.EncryptBlock, pPkcs1->RSAContext.iKeyModSize, "encrypted signature"); + #endif + + // copy the signature + ds_memcpy_s(pSigData, iSigSize, pPkcs1->RSAContext.EncryptBlock, pPkcs1->RSAContext.iKeyModSize); + return(0); +} + +/*F********************************************************************************/ +/*! + \Function ProtoSSLPkcs1Verify + + \Description + Verify the PKCSv1.5 RSA signature + + \Input *pSigData - signature data + \Input iSigLen - length of the signature + \Input *pHashData - hash of the data which was signed + \Input iHashLen - length of the hash + \Input iHashType - the hash which was used + \Input *pMod - public key modulus + \Input iModSize - size of the modulus + \Input *pExp - public key exponent + \Input iExpSize - size of the exponent + + \Notes + This function will block on the RSA operation. + + \Output + int32_t - zero=validation successful, negative=validation failed + + \Version 02/14/2020 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoSSLPkcs1Verify(const uint8_t *pSigData, int32_t iSigLen, const uint8_t *pHashData, int32_t iHashLen, int32_t iHashType, const uint8_t *pMod, int32_t iModSize, const uint8_t *pExp, int32_t iExpSize) +{ + CryptRSAT RSA; + int32_t iResult = -1; + + // init the rsa module with the public key and signature information + if (CryptRSAInit(&RSA, pMod, iModSize, pExp, iExpSize)) + { + return(-1); + } + CryptRSAInitSignature(&RSA, pSigData, iSigLen); + + // perform the encryption rounds + CryptRSAEncrypt(&RSA, 0); + NetPrintfVerbose((DEBUG_ENC_PERF, 0, "protossl: SSL Perf (pkcs1 sig verify) %dms\n", RSA.uCryptMsecs)); + + #if DEBUG_RAW_DATA + NetPrintMem(RSA.EncryptBlock, RSA.iKeyModSize, "decrypted signature"); + NetPrintMem(pHashData, iHashLen, "message digest"); + #endif + + // extract hash data from signature block + if ((pSigData = _Pkcs1VerifyEMSA(RSA.EncryptBlock, iSigLen, iHashLen, (CryptHashTypeE)iHashType)) != NULL) + { + // compare hash data with precalculated certificate body hash + iResult = !memcmp(pHashData, pSigData, iHashLen) ? 0 : -1; + } + + return(iResult); +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protostream.c b/r5dev/thirdparty/dirtysdk/source/proto/protostream.c new file mode 100644 index 00000000..8212a9b1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protostream.c @@ -0,0 +1,1170 @@ +/*H********************************************************************************/ +/*! + \File protostream.c + + \Description + Manage streaming of an Internet media resource. + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 11/16/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/proto/protohttp.h" + +#include "DirtySDK/proto/protostream.h" + +/*** Defines **********************************************************************/ + +#define PROTOSTREAM_MINBUFFER (32*1024) //!< minimum buffer configuration +#define PROTOSTREAM_MAXURL (256) //!< maximum url length +#define PROTOSTREAM_RESTARTFREQ_MAX (60) //!< maximum restart increase is one minute +#define PROTOSTREAM_SAMPLE_PERIOD (2000) //!< stats sampling period + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +// module state memory +struct ProtoStreamRefT +{ + ProtoHttpRefT *pProtoHttp; //!< protohttp ref + + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + ProtoStreamCallbackT *pCallback; //!< user callback + void *pUserData; //!< user callback data + int32_t iCallbackRate; //!< callback rate in ms + uint32_t uLastCallback; //!< last time callback was triggered + + enum + { + ST_IDLE, //!< idle state + ST_OPEN //!< stream is active + } eState; + + int32_t iRestartFreq; //!< restart frequency (PROTOSTREAM_FREQ_* or restart time in seconds) + int32_t iRestartTime; //!< restart timer + int32_t iRestartIncr; //!< amount to increase restart frequency by in case of an error + int32_t iRestartThreshold; //!< minimum amount of data that must be buffered before playback starts + int32_t iTimeout; //!< current http timeout + + int32_t iLastSize; //!< previous stream size + int32_t iLastModified; //!< previous last mod time + int32_t iLastHttpCode; //!< most recent http result code, saved on stream completion + int32_t iLastRecvTime; //!< time data was last received + + int32_t iBufSize; //!< streaming buffer size + int32_t iBufLen; //!< amount of data in buffer + int32_t iBufInp; //!< buffer input position + int32_t iBufOut; //!< buffer output position + int32_t iBufMin; //!< min amount of data to provide to caller; -1 if no min + + int32_t iBufAvg; //!< measured buffer size average + int32_t iBufDev; //!< measured buffer size deviation + + int32_t iStreamRead; //!< total amount of data read from stream + int32_t iStreamTime; //!< time stream has been open for, in milliseconds + + #if DIRTYCODE_DEBUG + uint32_t uStarveTime; //!< time to starve input until, for testing (debug only) + #endif + + uint8_t *pBufMin; //!< buffer to handle min-sized reads + + uint8_t bPrebuffering; //!< pre-buffering data before giving it to app + uint8_t bReceivedHeader; //!< TRUE=received HTTP header, else FALSE + uint8_t bPaused; //!< TRUE if stream is paused, else FALSE + int8_t iVerbose; //!< verbose debug level (debug only) + int8_t iStreamStatus; //!< stream status: -1=error, 0=in progress, 1=complete + + char strUrl[PROTOSTREAM_MAXURL]; //!< current url + + char strError[256]; //!< buffer to store HTTP result text, if any + uint8_t aBuffer[1]; //!< variable-sized streaming buffer - must come last +}; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamDefaultCallback + + \Description + Default (empty) callback. + + \Input *pProtoStream - pointer to module state + \Input eStatus - callback status (PROTOSTREAM_STATUS_*) + \Input *pBuffer - data pointer, or null + \Input iLength - data length, or zero + \Input *pUserData - user data pointer + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoStreamDefaultCallback(ProtoStreamRefT *pProtoStream, ProtoStreamStatusE eStatus, const uint8_t *pBuffer, int32_t iLength, void *pUserData) +{ + // no data consumed + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamDone + + \Description + Called when stream is complete. + + \Input *pProtoStream - pointer to module state + + \Output + None. + + \Version 11/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoStreamDone(ProtoStreamRefT *pProtoStream) +{ + NetPrintf(("protostream: stream done\n")); + + // notify user of stream completion + pProtoStream->pCallback(pProtoStream, PROTOSTREAM_STATUS_DONE, NULL, 0, pProtoStream->pUserData); + + // set up for restart if appropriate + if (pProtoStream->iRestartFreq != PROTOSTREAM_FREQ_ONCE) + { + int32_t iRestartFreq = pProtoStream->iRestartFreq; + + // if the most recent http response code was an error, increase restart time + if (pProtoStream->iLastHttpCode != PROTOHTTP_RESPONSE_SUCCESSFUL) + { + iRestartFreq += pProtoStream->iRestartIncr; + if (iRestartFreq > PROTOSTREAM_RESTARTFREQ_MAX) + { + iRestartFreq = PROTOSTREAM_RESTARTFREQ_MAX; + } + else + { + pProtoStream->iRestartIncr *= 2; + } + NetPrintf(("protostream: setting restart frequency to %d due to http error %d\n", + iRestartFreq, pProtoStream->iLastHttpCode)); + } + else + { + pProtoStream->iRestartIncr = 1; + } + + // set restart time + pProtoStream->iRestartTime = NetTick() + iRestartFreq * 1000; + } + + // go to idle state + pProtoStream->eState = ST_IDLE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamReset + + \Description + Reset the module state. + + \Input *pProtoStream - pointer to module state + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoStreamReset(ProtoStreamRefT *pProtoStream) +{ + // abort any ongoing stream + ProtoHttpAbort(pProtoStream->pProtoHttp); + + // reset buffering state + pProtoStream->iBufLen = 0; + pProtoStream->iBufInp = 0; + pProtoStream->iBufOut = 0; + + // reset other misc stuff + pProtoStream->uLastCallback = 0; + pProtoStream->eState = ST_IDLE; + pProtoStream->bReceivedHeader = FALSE; + pProtoStream->bPrebuffering = TRUE; + pProtoStream->iStreamStatus = 0; + pProtoStream->strUrl[0] = '\0'; + pProtoStream->iLastHttpCode = 0; + pProtoStream->iStreamTime = 0; + pProtoStream->iStreamRead = 0; + pProtoStream->iRestartThreshold = pProtoStream->iBufSize/2; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamUpdateStats + + \Description + Update stream statistics + + \Input *pProtoStream - pointer to module state + \Input iDataRead - amount of data read + + \Version 11/22/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoStreamUpdateStats(ProtoStreamRefT *pProtoStream, int32_t iDataRead) +{ + int32_t iRecvTime, iRecvElapsed; + + // get current time + iRecvTime = NetTick(); + + // prime lastrecvtime + if (pProtoStream->iLastRecvTime == 0) + { + pProtoStream->iLastRecvTime = iRecvTime; + } + + // calculate elapsed time since last receive and update last received time + iRecvElapsed = iRecvTime - pProtoStream->iLastRecvTime; + pProtoStream->iLastRecvTime = iRecvTime; + + // update overall time and size info + pProtoStream->iStreamTime += iRecvElapsed; + pProtoStream->iStreamRead += iDataRead; + + // perform latency calc using weighted time average + if ((iRecvElapsed >= 0) && (iRecvElapsed < PROTOSTREAM_SAMPLE_PERIOD)) + { + // figure out weight of existing data + int32_t iWeight = PROTOSTREAM_SAMPLE_PERIOD - iRecvElapsed; + // figure deviation first since it uses average + int32_t iChange = pProtoStream->iBufLen - pProtoStream->iBufAvg; + if (iChange < 0) + { + iChange = -iChange; + } + // calc weighted deviation + pProtoStream->iBufDev = ((iWeight*pProtoStream->iBufDev) + (iRecvElapsed*iChange))/PROTOSTREAM_SAMPLE_PERIOD; + // calc weighted average + pProtoStream->iBufAvg = ((iWeight*pProtoStream->iBufAvg) + (iRecvElapsed*pProtoStream->iBufLen))/PROTOSTREAM_SAMPLE_PERIOD; + // calc restart threshold, if we're not prebuffering + if (pProtoStream->bPrebuffering == FALSE) + { + pProtoStream->iRestartThreshold = pProtoStream->iBufSize - pProtoStream->iBufAvg + pProtoStream->iBufDev; + // clamp restart threshold to [0.5 ... 1.0] + if (pProtoStream->iRestartThreshold < pProtoStream->iBufSize/2) + { + pProtoStream->iRestartThreshold = pProtoStream->iBufSize/2; + } + else if (pProtoStream->iRestartThreshold > pProtoStream->iBufSize) + { + pProtoStream->iRestartThreshold = pProtoStream->iBufSize; + } + } + NetPrintfVerbose((pProtoStream->iVerbose, 1, "protostream: dev=%5d avg=%5d len=%5d thr=%d\n", + pProtoStream->iBufDev, pProtoStream->iBufAvg, pProtoStream->iBufLen, pProtoStream->iRestartThreshold)); + } + else + { + // if more than our scale has elapsed, use this data + pProtoStream->iBufDev = 0; + pProtoStream->iBufAvg = 0; + pProtoStream->iRestartThreshold = pProtoStream->iBufSize/2; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamChanged + + \Description + Returns whether stream has changed since previous stream or not + + \Input *pProtoStream - pointer to module state + + \Output + uint32_t - TRUE if stream changed, else FALSE + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ProtoStreamChanged(ProtoStreamRefT *pProtoStream) +{ + int32_t iLast, iSize; + uint32_t bChanged = FALSE; + + // get size + iSize = ProtoHttpStatus(pProtoStream->pProtoHttp, 'body', NULL, 0); + + // if size has changed or is unspecified, stream has changed + if ((iSize <= 0) || (iSize != pProtoStream->iLastSize)) + { + pProtoStream->iLastSize = iSize; + bChanged = TRUE; + } + + // get last modified time + iLast = ProtoHttpStatus(pProtoStream->pProtoHttp, 'date', NULL, 0); + + // if last modified time has changed or is unspecified, stream has changed + if ((iLast == 0) || (iLast != pProtoStream->iLastModified)) + { + pProtoStream->iLastModified = iLast; + bChanged = TRUE; + } + + // return changed status + return(bChanged); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamRecv + + \Description + Try and receive data if there is room + + \Input *pProtoStream - pointer to module state + + \Output + int32_t - bytes received, or negative if error + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoStreamRecv(ProtoStreamRefT *pProtoStream) +{ + int32_t iRecvMax, iRecvResult; + + // if the stream is done, don't try and receive any more + if (pProtoStream->iStreamStatus != 0) + { + return(0); + } + + #if DIRTYCODE_DEBUG + if (pProtoStream->uStarveTime != 0) + { + if (NetTickDiff(NetTick(), pProtoStream->uStarveTime) > 0) + { + NetPrintf(("protostream: stream starvation complete\n")); + pProtoStream->uStarveTime = 0; + } + else + { + return(0); + } + } + #endif + + // no room in buffer? + if (pProtoStream->iBufLen == pProtoStream->iBufSize) + { + return(0); + } + + // get max size we can receive + iRecvMax = pProtoStream->iBufSize - pProtoStream->iBufLen; + if (iRecvMax > (pProtoStream->iBufSize - pProtoStream->iBufInp)) + { + iRecvMax = pProtoStream->iBufSize - pProtoStream->iBufInp; + } + + // have we received the header yet? + if (pProtoStream->bReceivedHeader == FALSE) + { + // wait until we've received the header + if (ProtoHttpStatus(pProtoStream->pProtoHttp, 'head', NULL, 0) > 0) + { + // if the stream hasn't changed since last time, skip it + if (!_ProtoStreamChanged(pProtoStream)) + { + // mark stream as complete + NetPrintf(("protostream: stream unchanged from last play -- skipping\n")); + ProtoHttpAbort(pProtoStream->pProtoHttp); + pProtoStream->iStreamStatus = 1; + return(0); + } + // if the response isn't ok, disable prebuffering so client code isn't stuck waiting + if (ProtoHttpStatus(pProtoStream->pProtoHttp, 'code', NULL, 0) != PROTOHTTP_RESPONSE_OK) + { + pProtoStream->bPrebuffering = FALSE; + } + // mark header as received + pProtoStream->bReceivedHeader = TRUE; + } + } + + // try and get some data + if ((iRecvResult = ProtoHttpRecv(pProtoStream->pProtoHttp, (char *)pProtoStream->aBuffer + pProtoStream->iBufInp, 1, iRecvMax)) > 0) + { + #if DIRTYCODE_LOGGING + if (pProtoStream->iVerbose > 1) + { + int32_t iStreamBps=0; + if (pProtoStream->iStreamTime != 0) + { + iStreamBps = (int32_t)(((int64_t)pProtoStream->iStreamRead*8*1000)/(int64_t)pProtoStream->iStreamTime); + } + NetPrintf(("protostream: recv [0x%04x,0x%04x] len=%d bps=%d clk=%.2f\n", pProtoStream->iBufInp, + pProtoStream->iBufInp+iRecvResult, pProtoStream->iBufLen+iRecvResult, iStreamBps, + (float)pProtoStream->iStreamTime/1000.0f)); + } + #endif + + // update buffer pointers + pProtoStream->iBufLen += iRecvResult; + pProtoStream->iBufInp += iRecvResult; + if (pProtoStream->iBufInp == pProtoStream->iBufSize) + { + pProtoStream->iBufInp = 0; + } + } + else if (iRecvResult == PROTOHTTP_RECVDONE) + { + // mark stream as complete + NetPrintf(("protostream: stream data transfer complete\n")); + pProtoStream->iLastHttpCode = ProtoHttpStatus(pProtoStream->pProtoHttp, 'code', NULL, 0); + pProtoStream->iStreamStatus = (pProtoStream->iLastHttpCode == PROTOHTTP_RESPONSE_OK) ? 1 : -1; + if (pProtoStream->iStreamStatus < 0) + { + ds_strsubzcpy(pProtoStream->strError, sizeof(pProtoStream->strError), (char *)pProtoStream->aBuffer+pProtoStream->iBufOut, pProtoStream->iBufLen-pProtoStream->iBufOut); + } + } + else if ((iRecvResult < 0) && (iRecvResult != PROTOHTTP_RECVWAIT)) + { + NetPrintf(("protostream: error %d receiving http response\n", iRecvResult)); + pProtoStream->iStreamStatus = -1; + pProtoStream->iLastSize = -1; + } + + // return result to caller + return(iRecvResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamDataCallback + + \Description + Determine amount of data that can be read from buffer, and call + user callback with a pointer to the beginning of the data in the queue + and the amount of data that can be read. + + \Input *pProtoStream - pointer to module state + \Input eStatus - current status + + \Output + int32_t - amount of data read + + \Version 01/25/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoStreamDataCallback(ProtoStreamRefT *pProtoStream, ProtoStreamStatusE eStatus) +{ + int32_t iRead, iReadMax; + + // determine max amount of data that can be read + iReadMax = pProtoStream->iBufSize - pProtoStream->iBufOut; + if (iReadMax > pProtoStream->iBufLen) + { + iReadMax = pProtoStream->iBufLen; + } + + // if buffer minimum conditions do not apply, allow the user to grab some data + if ((iReadMax > pProtoStream->iBufMin) || ((pProtoStream->iBufMin >= pProtoStream->iBufLen) && (pProtoStream->iStreamStatus == 1))) + { + if ((iRead = pProtoStream->pCallback(pProtoStream, eStatus, pProtoStream->aBuffer + pProtoStream->iBufOut, iReadMax, pProtoStream->pUserData)) > 0) + { + NetPrintfVerbose((pProtoStream->iVerbose, 1, "protostream: read [0x%04x,0x%04x]\n", pProtoStream->iBufOut, pProtoStream->iBufOut+iRead)); + + // if they consumed too much data, print a diagnostic warning and clamp + if (iRead > iReadMax) + { + NetPrintf(("protostream: warning; tried to read %d bytes when max was %d\n", iRead, iReadMax)); + iRead = iReadMax; + } + + // update buffer status + pProtoStream->iBufOut += iRead; + if (pProtoStream->iBufOut == pProtoStream->iBufSize) + { + pProtoStream->iBufOut = 0; + } + pProtoStream->iBufLen -= iRead; + } + else if (iRead < 0) + { + NetPrintf(("protostream: stream error - aborting\n")); + pProtoStream->eState = ST_IDLE; + } + } + else // enforce minimum data amount, if specified + { + // make sure we have enough data + if (pProtoStream->iBufLen < pProtoStream->iBufMin) + { + return(0); + } + // read minimum amount of data + if ((iReadMax = ProtoStreamRead(pProtoStream, (char *)pProtoStream->pBufMin, pProtoStream->iBufMin, pProtoStream->iBufMin)) != pProtoStream->iBufMin) + { + NetPrintf(("protostream: stream error; minbuf read failed err=%d\n", iReadMax)); + return(0); + } + // pass data on to caller + iRead = pProtoStream->pCallback(pProtoStream, eStatus, pProtoStream->pBufMin, iReadMax, pProtoStream->pUserData); + } + + return(iRead); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamDataCallbackProcess + + \Description + Do data callback processing. + + \Input *pProtoStream - pointer to module state + + \Output + None. + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoStreamDataCallbackProcess(ProtoStreamRefT *pProtoStream) +{ + ProtoStreamStatusE eStatus; + uint32_t uTick = NetTick(); + int32_t iRead; + + // if we're prebuffering, wait until there is enough data or the stream is complete + if (pProtoStream->bPrebuffering == TRUE) + { + if ((pProtoStream->iBufLen < pProtoStream->iRestartThreshold) && (pProtoStream->iStreamStatus != 1)) + { + return; + } + NetPrintf(("protostream: prebuffered %d bytes\n", pProtoStream->iBufLen)); + pProtoStream->bPrebuffering = FALSE; + } + + // determine callback status type + if (pProtoStream->uLastCallback != 0) + { + // recurring data callback + eStatus = PROTOSTREAM_STATUS_DATA; + + // only update if update rate exceeded and not paused + if ((NetTickDiff(uTick, pProtoStream->uLastCallback) < pProtoStream->iCallbackRate) || (pProtoStream->bPaused == TRUE)) + { + return; + } + } + else + { + // start of stream + eStatus = PROTOSTREAM_STATUS_BEGIN; + } + + // update callback timer + pProtoStream->uLastCallback = uTick; + + // do the callback + iRead = _ProtoStreamDataCallback(pProtoStream, eStatus); + if ((iRead > 0) && (pProtoStream->iBufOut == 0) && (pProtoStream->iBufLen > 0)) + { + /* if they consumed all of the data, and we wrapped, + and there is still data to be consumed, give them + another shot at the data */ + _ProtoStreamDataCallback(pProtoStream, eStatus); + } + + // if we have emptied the buffer, and the stream is not done, rebuffer + if ((pProtoStream->iBufLen == 0) && (pProtoStream->iStreamStatus == 0)) + { + NetPrintf(("protostream: exhausted buffer; prebuffering %d bytes\n", pProtoStream->iRestartThreshold)); + pProtoStream->bPrebuffering = TRUE; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoStreamOpen + + \Description + Begin streaming an Internet media source + + \Input *pProtoStream - pointer to module state + \Input *pUrl - resource to stream + \Input *pReq - request body, if POST + \Input iFreq - restart frequency in seconds, or PROTOSTREAM_FREQ_* + + \Output + int32_t - negative=failure, else success + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoStreamOpen(ProtoStreamRefT *pProtoStream, const char *pUrl, const char *pReq, int32_t iFreq) +{ + int32_t iResult; + + // reset state + _ProtoStreamReset(pProtoStream); + + // cache info + pProtoStream->iRestartFreq = iFreq; + ds_strnzcpy(pProtoStream->strUrl, pUrl, sizeof(pProtoStream->strUrl)); + + // open stream + if ((iResult = ProtoHttpRequest(pProtoStream->pProtoHttp, pUrl, pReq, -1, pReq != NULL ? PROTOHTTP_REQUESTTYPE_POST : PROTOHTTP_REQUESTTYPE_GET)) >= 0) + { + pProtoStream->eState = ST_OPEN; + } + else + { + NetPrintf(("protostream: error opening stream '%s'\n", pUrl)); + } + + // return result code to caller + return(iResult); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoStreamCreate + + \Description + Create the stream module + + \Input iBufSize - size of streaming buffer (at least PROTOSTREAM_MINBUFFER) + + \Output + ProtoStreamRefT * - new module state, or NULL + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoStreamRefT *ProtoStreamCreate(int32_t iBufSize) +{ + ProtoStreamRefT *pProtoStream; + int32_t iRefSize; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // enforce minimum buffer size + if (iBufSize < PROTOSTREAM_MINBUFFER) + { + iBufSize = PROTOSTREAM_MINBUFFER; + } + + // calc ref size + iRefSize = sizeof(*pProtoStream) - sizeof(pProtoStream->aBuffer) + iBufSize; + + // allocate and init module state + if ((pProtoStream = DirtyMemAlloc(iRefSize, PROTOSTREAM_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protostream: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pProtoStream, iRefSize); + pProtoStream->iMemGroup = iMemGroup; + pProtoStream->pMemGroupUserData = pMemGroupUserData; + pProtoStream->pCallback = _ProtoStreamDefaultCallback; + + // create http ref + if ((pProtoStream->pProtoHttp = ProtoHttpCreate(4096)) == NULL) + { + NetPrintf(("protostream: could not allocate http module\n")); + DirtyMemFree(pProtoStream, PROTOSTREAM_MEMID, pProtoStream->iMemGroup, pProtoStream->pMemGroupUserData); + return(NULL); + } + + // init other state variables + pProtoStream->iBufSize = iBufSize; + pProtoStream->iBufMin = -1; + pProtoStream->iVerbose = 1; + pProtoStream->iTimeout = 60*1000; + ProtoHttpControl(pProtoStream->pProtoHttp, 'time', pProtoStream->iTimeout, 0, NULL); + + // return ref to caller + return(pProtoStream); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamDestroy + + \Description + Destroy the ProtoStream module + + \Input *pProtoStream - pointer to module state + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoStreamDestroy(ProtoStreamRefT *pProtoStream) +{ + // free minbuf buffer, if allocated + if (pProtoStream->pBufMin != NULL) + { + DirtyMemFree(pProtoStream->pBufMin, PROTOSTREAM_MEMID, pProtoStream->iMemGroup, pProtoStream->pMemGroupUserData); + } + + // dispose of http module + ProtoHttpDestroy(pProtoStream->pProtoHttp); + + // dispose of module memory + DirtyMemFree(pProtoStream, PROTOSTREAM_MEMID, pProtoStream->iMemGroup, pProtoStream->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamSetCallback + + \Description + Set recurring ProtoStream callback. + + \Input *pProtoStream - pointer to module state + \Input iRate - callback rate in ms + \Input *pCallback - data callback + \Input *pUserData - user data for callback + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoStreamSetCallback(ProtoStreamRefT *pProtoStream, int32_t iRate, ProtoStreamCallbackT *pCallback, void *pUserData) +{ + pProtoStream->pCallback = (pCallback != NULL) ? pCallback : _ProtoStreamDefaultCallback; + pProtoStream->pUserData = pUserData; + pProtoStream->iCallbackRate = iRate; +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamSetHttpCallback + + \Description + Set custom callbacks for ProtoHttp ref managed by ProtoStream. + + \Input *pProtoStream - pointer to module state + \Input *pCustomHeaderCb - pointer to custom send header callback (may be NULL) + \Input *pReceiveHeaderCb- pointer to recv header callback (may be NULL) + \Input *pUserData - user-supplied callback ref (may be NULL) + + \Version 10/26/2011 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoStreamSetHttpCallback(ProtoStreamRefT *pProtoStream, ProtoHttpCustomHeaderCbT *pCustomHeaderCb, ProtoHttpReceiveHeaderCbT *pReceiveHeaderCb, void *pUserData) +{ + ProtoHttpCallback(pProtoStream->pProtoHttp, pCustomHeaderCb, pReceiveHeaderCb, pUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamOpen + + \Description + Begin streaming an Internet media source + + \Input *pProtoStream - pointer to module state + \Input *pUrl - resource to stream + \Input iFreq - restart frequency in seconds, or PROTOSTREAM_FREQ_* + + \Output + int32_t - negative=failure, else success + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoStreamOpen(ProtoStreamRefT *pProtoStream, const char *pUrl, int32_t iFreq) +{ + // reset restart values + pProtoStream->iRestartIncr = 1; + pProtoStream->iRestartThreshold = pProtoStream->iBufSize/2; + + // reset previous stream info + pProtoStream->iLastSize = 0; + pProtoStream->iLastModified = 0; + + // open stream + return(_ProtoStreamOpen(pProtoStream, pUrl, NULL, iFreq)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamOpen2 + + \Description + Begin streaming an Internet media source using POST request + + \Input *pProtoStream - pointer to module state + \Input *pUrl - resource to stream + \Input *pReq - request body + \Input iFreq - restart frequency in seconds, or PROTOSTREAM_FREQ_* + + \Output + int32_t - negative=failure, else success + + \Version 10/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoStreamOpen2(ProtoStreamRefT *pProtoStream, const char *pUrl, const char *pReq, int32_t iFreq) +{ + // reset restart values + pProtoStream->iRestartIncr = 1; + pProtoStream->iRestartThreshold = pProtoStream->iBufSize/2; + + // reset previous stream info + pProtoStream->iLastSize = 0; + pProtoStream->iLastModified = 0; + + // open stream + return(_ProtoStreamOpen(pProtoStream, pUrl, pReq, iFreq)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamRead + + \Description + Read at least iMinLen bytes from stream into user buffer. + + \Input *pProtoStream - pointer to module state + \Input *pBuffer - [out] output buffer + \Input iBufLen - size of output buffer + \Input iMinLen - minimum amount of data to copy + + \Output + int32_t - number of bytes copied; negative=stream not open + + \Version 11/21/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoStreamRead(ProtoStreamRefT *pProtoStream, char *pBuffer, int32_t iBufLen, int32_t iMinLen) +{ + char *pBufStart = pBuffer; + int32_t iBufCopy; + + // make sure we're open + if (pProtoStream->eState != ST_OPEN) + { + NetPrintf(("protostream: no stream open\n")); + return(-1); + } + + // make sure we're not prebuffering, have enough data, and are not paused + if ((pProtoStream->bPrebuffering == TRUE) || (pProtoStream->iBufLen < iMinLen) || (pProtoStream->bPaused == TRUE)) + { + return(0); + } + + // determine amount of data to copy + iBufCopy = (pProtoStream->iBufLen < iBufLen) ? pProtoStream->iBufLen : iBufLen; + + // does copy span tail end of buffer? + if ((pProtoStream->iBufOut + iBufCopy) > pProtoStream->iBufSize) + { + // copy tail end of data to buffer + int32_t iBufCopy2 = pProtoStream->iBufSize - pProtoStream->iBufOut; + ds_memcpy(pBuffer, &pProtoStream->aBuffer[pProtoStream->iBufOut], iBufCopy2); + + NetPrintfVerbose((pProtoStream->iVerbose, 1, "protostream: read [0x%04x,0x%04x]\n", pProtoStream->iBufOut, pProtoStream->iBufOut+iBufCopy2)); + + // adjust buffer parameters + pProtoStream->iBufOut = 0; + pProtoStream->iBufLen -= iBufCopy2; + + // adjust current copy parameters + pBuffer += iBufCopy2; + iBufCopy -= iBufCopy2; + } + + // copy data to output buffer + NetPrintfVerbose((pProtoStream->iVerbose, 1, "protostream: read [0x%04x,0x%04x]\n", pProtoStream->iBufOut, pProtoStream->iBufOut+iBufCopy)); + ds_memcpy(pBuffer, &pProtoStream->aBuffer[pProtoStream->iBufOut], iBufCopy); + pBuffer += iBufCopy; + + // adjust buffer parameters + pProtoStream->iBufOut += iBufCopy; + if (pProtoStream->iBufOut == pProtoStream->iBufSize) + { + pProtoStream->iBufOut = 0; + } + pProtoStream->iBufLen -= iBufCopy; + + // return amount copied to caller + return((int32_t)(pBuffer - pBufStart)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamPause + + \Description + Pause/unpause stream. + + \Input *pProtoStream - pointer to module state + \Input bPause - TRUE to pause, else FALSE + + \Version 01/25/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoStreamPause(ProtoStreamRefT *pProtoStream, uint8_t bPause) +{ + NetPrintf(("protostream: pause %s (was %s)\n", bPause ? "enabled" : "disabled", pProtoStream->bPaused ? "enabled" : "disabled")); + if ((pProtoStream->bPaused = bPause) == TRUE) + { + ProtoHttpControl(pProtoStream->pProtoHttp, 'time', 0x7fffffff, 0, NULL); + } + else + { + ProtoHttpControl(pProtoStream->pProtoHttp, 'time', pProtoStream->iTimeout, 0, NULL); + } +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamClose + + \Description + Stop streaming + + \Input *pProtoStream - pointer to module state + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoStreamClose(ProtoStreamRefT *pProtoStream) +{ + _ProtoStreamReset(pProtoStream); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamStatus + + \Description + Get module status. + + \Input *pProtoStream - pointer to module state + \Input iStatus - status selector + \Input *pBuffer - selector specific + \Input iBufSize - selector specific + + \Output + int32_t - selector specific + + \Notes + iStatus can be one of the following: + + \verbatim + 'bufl' - amount of data currently buffered + 'bufs' - size of current stream buffer + 'code' - most recent http result code + 'done' - stream status: -1=error, 0=in progress, 1=complete + 'serr' - copies http error result string to pBuffer + \endverbatim + + Unrecognized codes are passed through to ProtoHttpStatus(). + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoStreamStatus(ProtoStreamRefT *pProtoStream, int32_t iStatus, void *pBuffer, int32_t iBufSize) +{ + // return current amount of data in buffer + if (iStatus == 'bufl') + { + return(pProtoStream->iBufLen); + } + // return current size of stream buffer + if (iStatus == 'bufs') + { + return(pProtoStream->iBufSize); + } + // return most recent http result code + if (iStatus == 'code') + { + return(pProtoStream->iLastHttpCode); + } + // return completion status + if (iStatus == 'done') + { + return(pProtoStream->iStreamStatus); + } + // return error string, if set + if (iStatus == 'serr') + { + ds_strnzcpy(pBuffer, pProtoStream->strError, iBufSize); + return(0); + } + // if not handled, let ProtoHttp take a stab at it + return(ProtoHttpStatus(pProtoStream->pProtoHttp, iStatus, pBuffer, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamControl + + \Description + Set control options + + \Input *pProtoStream - pointer to module state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iStatus can be one of the following: + + \verbatim + 'minb' - set minimum data amount of data for data callback (note; not enforced at end of stream) + 'play' - set whether playing or not (client timeout is disabled if not playing) + 'spam' - set verbose debug level (debug only) + 'strv' - set number of seconds to starve input (debug only) + \endverbatim + + Unhandled codes are passed through to ProtoHttpControl(). + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoStreamControl(ProtoStreamRefT *pProtoStream, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + // set minimum amount of data caller needs in a callback + if (iControl == 'minb') + { + if ((pProtoStream->iBufMin != iValue) && (pProtoStream->pBufMin != NULL)) + { + DirtyMemFree(pProtoStream->pBufMin, PROTOSTREAM_MEMID, pProtoStream->iMemGroup, pProtoStream->pMemGroupUserData); + } + if ((iValue > 0) && ((pProtoStream->pBufMin = DirtyMemAlloc(iValue, PROTOSTREAM_MEMID, pProtoStream->iMemGroup, pProtoStream->pMemGroupUserData)) != NULL)) + { + pProtoStream->iBufMin = iValue; + } + else + { + NetPrintf(("protostream: could not allocate %d bytes for minbuffer\n", iValue)); + pProtoStream->iBufMin = -1; + } + return(0); + } + // set http timeout based on if application is playing back or not + if (iControl == 'play') + { + // set parameters to pass through to ProtoHttp + iControl = 'time'; + iValue = (iValue != 0) ? pProtoStream->iTimeout : 0x40000000; // 'disabled' == very large timeout value + } + #if DIRTYCODE_LOGGING + // set verbosity for us and pass through to protohttp as well + if (iControl == 'spam') + { + pProtoStream->iVerbose = iValue; + } + #endif + #if DIRTYCODE_DEBUG + // set to starve for a period of time (debug only) + if (iControl == 'strv') + { + NetPrintf(("protostream: starving input for %d seconds\n", iValue)); + pProtoStream->uStarveTime = NetTick() + (iValue * 1000); + return(0); + } + #endif + if (iControl == 'time') + { + // remember most recent timeout value, and pass through to protohttp + pProtoStream->iTimeout = iValue; + } + + // if not handled, let ProtoHttp take a stab at it + return(ProtoHttpControl(pProtoStream->pProtoHttp, iControl, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoStreamUpdate + + \Description + Update the ProtoStream module + + \Input *pProtoStream - pointer to module state + + \Version 11/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoStreamUpdate(ProtoStreamRefT *pProtoStream) +{ + // update module in idle state + if (pProtoStream->eState == ST_IDLE) + { + // if we have a url and are in restart mode + if ((pProtoStream->strUrl[0] != '\0') && (pProtoStream->iRestartFreq != PROTOSTREAM_FREQ_ONCE)) + { + // time to restart? + if (NetTickDiff(NetTick(), pProtoStream->iRestartTime) > 0) + { + char strUrl[PROTOSTREAM_MAXURL]; + ds_strnzcpy(strUrl, pProtoStream->strUrl, sizeof(strUrl)); + _ProtoStreamOpen(pProtoStream, strUrl, NULL, pProtoStream->iRestartFreq); + } + } + } + + // update stream in open state + if (pProtoStream->eState == ST_OPEN) + { + int32_t iRecvResult; + + // give time to http module + ProtoHttpUpdate(pProtoStream->pProtoHttp); + + // try and receive data + for ( iRecvResult = 1; iRecvResult > 0; ) + { + if ((iRecvResult = _ProtoStreamRecv(pProtoStream)) >= 0) + { + _ProtoStreamUpdateStats(pProtoStream, iRecvResult); + } + } + + // user data callback processing + _ProtoStreamDataCallbackProcess(pProtoStream); + + // check for stream completion + if ((pProtoStream->iStreamStatus != 0) && (pProtoStream->iBufLen == 0)) + { + _ProtoStreamDone(pProtoStream); + } + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/prototunnel.c b/r5dev/thirdparty/dirtysdk/source/proto/prototunnel.c new file mode 100644 index 00000000..3a3a9a64 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/prototunnel.c @@ -0,0 +1,3314 @@ +/*H********************************************************************************/ +/*! + \File prototunnel.c + + \Description + + ProtoTunnel creates and manages a Virtual DirtySock Tunnel connection. The + tunnel transparently bundles data sent from multiple ports to a specific + remote host into a single send, optionally encrypting packets based on the + tunnel mappings set up by the caller. Only data sent over a UDP socket may + be tunneled. ProtoTunnel does not itself send packets unsolicited, it merely + encapsulates packets being sent by other API's like CommUDP or VoIP. + Therefore, ProtoTunnel packets always include user data. + + \Notes + \verbatim + Encryption Key + + ProtoTunnel assumes that the encryption key used is provided by the + caller creating the tunnel. It does not provide any mechanism for + secure key exchange; this is left to the caller. A good random + encryption key of at least sixty-four characters is strongly recommended + to provide adequate security. + + Packet format + + ProtoTunnel bundles multiple packets bound for different ports on the + same destination address into a single packet. There are two types of + packets, handshake packets and regular packets. A handshake packet is + identical in layout to a regular packet, with the addition of handshake + data. Packets contain the following data elements: + + - Two-byte Ident field + - Two-byte tunnel encryption header + - Handshake info (handshake packets only) + - Encrypted list of one or more two-byte sub-packet headers + - Encrypted HMAC message digest + - Encrypted packet data (if any) + - Unencrypted packet data (if any) + + 0 8 15 + ------------------------------- + | Ident | Ident field + ------------------------------- + | Encryption | Encryption field + ------------------------------- + | | + | Handshake | Handshake field (handshake packets only) + | | + ------------------------------- + | Packet size | PIdx | Packet #1 header + ------------------------------- + | Packet size | PIdx | Packet #2 header + ------------------------------- + . + . + . + ------------------------------- + | Packet size | PIdx | Packet #n header + ------------------------------- + | | + | HMAC | HMAC message digest (length dependent on HMAC type and size, may be zero) + | | + ------------------------------- + | | + | Encrypted Packet Data | Encrypted Packet Data + | | + ------------------------------- + | | + | Unencrypted Packet Data | Unencrypted Packet Data + | | + ------------------------------- + + Ident field + + The Ident field contains one of two values; handshake packets are identified + by the high bit being set (0x8000) and otherwise contain a two byte protocol + version identifier. For example, an Ident field of 0x8101 would indicate a + handshake packet using protocol version 1.1. Non-handshake packets do not + include the high bit, with the lower 15 bits used to indicate the remote + tunnel index on an active connection. The ident field is authenticated but + not encrypted, so it may be processed before the packet is decrypted. During + handshaking, an endpoint receiving packets with a lesser version of the + protocol specified will downgrade to that version. (Note that downgrading + to versions older than 1.1 is not supported). The ident field is the only + field that is required to exist in any given ProtoTunnel protocol version. + + Encryption field + + The Encryption field contains a 16bit stream index used to synchronize + the stream cipher in the event of lost packets. The high bit (0x8000) + is reserved to indicate that the packet is encrypted. The remaining + 15bit value is shifted up by three bits to give a total window size of 256k. + Because of the shift, the stream cipher encrypts in blocks of eight bytes. + This requires the stream cipher to be iterated over the amount + of data that is encrypted, and then advanced by the number of bytes + required to round up to the next multiple of eight bytes. In the event + one or more tunnel packets are lost, the receiving side will advance the + stream cipher by the amount of data lost to remain synchronized with the + sender. A packet received that has a negative stream offset relative to + the current offset is assumed to be a previous packet. ProtoTunnel will + make an attempt to decrypt such a packet with a copy of the previous + state. If that fails, the packet is discarded. All packets contain some + encrypted data, and all packet data is authenticated (see HMAC below). + + Handshake field + + Handshake data is sent until a connection is determined to be active. The + format of the field is as follows: + + 0 8 15 + ------------------------------- + | Client Ident | Client Ident field + | | + ------------------------------- + | Connection Ident | Connection Ident field + ------------------------------- + | Active |XXXXXXXXXXXXXX| Active field + ------------------------------- + + The Client Ident is used on the receiving side to match incoming handshake + packets to the specific tunnel endpoint configured to receive them. This + is necessary when the packets arrive from an unexpected address and/or port, + usually due to NAT. Because the Handshake data is unencrypted, this can + be trivially done, without having to potentially try decryption against + many endpoints. This is an important consideration for servers that many + have hundreds or even thousands of endpoints. + + The Connection Ident field is used to specify the Ident that non-handshake + packets from the remote endpoint will use to identify the connection they + belong to. This ident is simply the index of the tunnel endpoint in the + tunnel list. Because the non-handshake Ident field is 15 bits, this limits + the maximum number of tunnel endpoints to 32,767. The Ident present in + non-handshake packets is useful when a port and/or address change takes + place. There are multiple scenarios where this type of behavior can occur; + for example a connection where data is not sent for a period of time can + have its route expired by a NAT device. When data resumes being sent, + it likely will originate from a different port. Another example is a + corporate firewall, which may aggressively reallocate ports or even + addresses dynamically. Including the Ident in unencrypted form non- + handshake packets allows trivial rematching of incoming packet data without + needing to decrypt the packet data. + + The Active field is how ProtoTunnel determines if a connection is active. + Initially, it is set to zero. When an endpoint successfully receives + data from a remote endpoint, the Active field in the handshake packets + it sends is set to one. When an endpoint receives a handshake packet + from a remote endpoint with the Active field set to one, the connection + is considered to be complete, and handshake information is no longer + sent. + + Packet Headers + + Packet headers consist of a twelve-bit size field and a four-bit port + index. This means the maximum bundled packet size is 4k and the maximum + number of port mappings in a tunnel is 16. ProtoTunnel restricts the + actual maximum number of port mappings to eight. ProtoTunnel also + reserves the last port to use for embedded control data (see ProtoTunnel + Control Info below), so this leaves seven user-allocatable ports. It + also means that tunnels are required to be created with the same port + mappings on each side of the tunnel, otherwise the bundled tunnel + packets will not be forwarded to the correct ports. The tunnel + demultiplexer iterates through the packet headers until the aggregate + size of packet data plus packet headers plus encryption header equals + the size of the received packet. When the packet format is encrypted, + packet headers are decrypted one at a time until all of the headers are + successfully decrypted. If there are any discrepencies (e.g. invalid + port mappings or aggregate sizes don't match) the packet is considered + to be invalid and is discarded. + + Encrypted HMAC message digest + + The HMAC message digest provides message authentication; in the event + that packet data is modified on the wire, the HMAC calculation on the + receiving side will not match the expected result, which will indicate + that the data has been modified, and the packet will be discarded. Any + supported HMAC algorithm maybe be chosen, and any amount of truncation + is supported up to 1/2 the hash size. The maximum size of the HMAC is + 24 bytes. A NULL HMAC may be chosen, in which case no space is consumed + in the packet body. It is assumed that both sides are configured with + the same HMAC type and size, otherwise a connection is not be possible. + + Encrypted Packet Data + + Encrypted packets, if any, immediately follow the final header. All + encrypted packet data is decrypted together in one pass. + + Unencrypted Packet Data + + Unencrypted packets, if any, immediately follow encrypted packets, or + packet headers if there are no encrypted packets. + + Matching packets to an endpoint + + When ProtoTunnel is running in a configuration where multiple endpoints + are using the same socket, an incoming packet must be matched to a specific + tunnel endpoint for it to be properly received. Two examples of this type + of scenario are a dedicated server, or a peer-mesh game with more than + two clients. Due to NAT behavior we may not always know where a packet + will be coming from at any given time. + + ProtoTunnel matches differently depending on whether the tunnel is active + or not. If the tunnel is not yet active, the ClientId field in the handshake + data is used to match the packet to the appropriate tunnel. During this + phase of handshaking, ProtoTunnel will update the address and port of the + remote endpoint based on where the data originates from, assuming the + packet can be decrypted and authenticated. When a tunnel is active, + ProtoTunnel uses the Ident field to directly route the packet to the proper + endpoint. If the source addr and/or port change while a tunnel is active, + the updated port will be detected and used to match future packets. + + \endverbatim + + \Copyright + Copyright (c) 2005-2015 Electronic Arts Inc. + + \Version 12/02/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/crypt/cryptarc4.h" +#include "DirtySDK/crypt/crypthash.h" +#include "DirtySDK/crypt/crypthmac.h" + +#include "DirtySDK/proto/prototunnel.h" + +/*** Defines **********************************************************************/ + +#define PROTOTUNNEL_HMAC_DEBUG (FALSE) +#define PROTOTUNNEL_HMAC_MAXSIZE (16) //!< maximum supported HMAC size +#define PROTOTUNNEL_HMAC_DEFSIZE (12) //!< default supported HMAC size + +#define PROTOTUNNEL_PKTHDRSIZE (2) +#define PROTOTUNNEL_MAXHDROFFS (sizeof(ProtoTunnelHandshakeT)) +#define PROTOTUNNEL_MAXPACKETS (8) +#define PROTOTUNNEL_MAXHDRSIZE (PROTOTUNNEL_PKTHDRSIZE * PROTOTUNNEL_MAXPACKETS) +#define PROTOTUNNEL_PACKETBUFFER (SOCKET_MAXUDPRECV) +#define PROTOTUNNEL_MAXPACKET (PROTOTUNNEL_PACKETBUFFER - PROTOTUNNEL_MAXHDROFFS - PROTOTUNNEL_PKTHDRSIZE - PROTOTUNNEL_HMAC_MAXSIZE) + +#define PROTOTUNNEL_CRYPTBITS (3) // number of bits added to extend window +#define PROTOTUNNEL_CRYPTALGN (1 << PROTOTUNNEL_CRYPTBITS) +#define PROTOTUNNEL_CRYPTMASK (PROTOTUNNEL_CRYPTALGN-1) + +#define PROTOTUNNEL_CRYPTARC4_ITER (12) // improved security by skipping slightly less secure first output data (3k bytes), this data is not transmitted so does not produce overhead on the connection + +#define PROTOTUNNEL_CONTROL_PORTIDX (PROTOTUNNEL_MAXPORTS-1) + +#define PROTOTUNNEL_IDENT_HANDSHAKE (0x8000) + +#define PROTOTUNNEL_IPPROTO (IPPROTO_IP) + +#define PROTOTUNNEL_MAXKEYS (PROTOTUNNEL_MAXPORTS) + +// comma delimited set of support versions +// IMPORTANT: make sure to add/delete when min/current version is changed +#define PROTOTUNNEL_VERSIONS ("1.1") + +typedef enum ProtoTunnelControlE +{ + PROTOTUNNEL_CTRL_INIT +} ProtoTunnelControlE; + +typedef enum ProtoTunnelPortListFuncE +{ + PROTOTUNNEL_PORTLIST_ADD, + PROTOTUNNEL_PORTLIST_DEL, + PROTOTUNNEL_PORTLIST_CHK +} ProtoTunnelPortListFuncE; + +// packet processing return codes +#define PROTOTUNNEL_PACKETRECVFAIL_VALIDATE (-1) //!< decrypt or validate failed +#define PROTOTUNNEL_PACKETRECVFAIL_NOMATCH (-2) //!< could not match packet to a tunnel +#define PROTOTUNNEL_PACKETRECVFAIL_VERSION (-3) //!< fatal prototunnel protocol version mismatch +#define PROTOTUNNEL_PACKETRECVFAIL_OUTOFORDER (-4) //!< out-of-order packet discard; packet is previous to our current window offset +#define PROTOTUNNEL_PACKETRECVFAIL_TRYAGAIN (-5) //!< packet decrypt failed but rematch found new key/offset; try again + +/*** Macros ***********************************************************************/ + +#define PROTOTUNNEL_GetIdentFromPacket(__pPacketData) (((__pPacketData[0])<<8) | (__pPacketData)[1]) +#define PROTOTUNNEL_GetEncryptionFromPacket(__uTunnelVers, __pPacketData) (((__pPacketData[2])<<8) | (__pPacketData)[3]) +#define PROTOTUNNEL_GetStreamOffsetFromPacket(__uTunnelVers, __pPacketData) (PROTOTUNNEL_GetEncryptionFromPacket(__uTunnelVers, __pPacketData) & 0x7fff) + +/*** Type Definitions *************************************************************/ + +//! handshake packet format for version 1.1 of the protocol +typedef struct ProtoTunnelHandshake_1_1_T +{ + uint8_t aCrypt[2]; //!< stream cipher offset + uint8_t aClientIdent[4]; //!< client identifier, used to tag handshake packets for initial matching of packet to tunnel + uint8_t aConnIdent[2]; //!< connection identifier, used to tag regular packets for re-matching + uint8_t uActive; //!< is connection active? +} ProtoTunnelHandshake_1_1_T; + +//! prototunnel handshake packet format +typedef struct ProtoTunnelHandshakeT +{ + uint8_t aIdent[2]; + union + { + ProtoTunnelHandshake_1_1_T V1_1; + } Version; +} ProtoTunnelHandshakeT; + +//! handshake control info +typedef struct ProtoTunnelControlT +{ + uint8_t uPacketType; //!< PROTOTUNNEL_CTRL_* + uint8_t aClientId[4]; //!< source clientId + uint8_t aProtocolVers[2]; //!< protocol version + uint8_t uActive; //!< is connection active? +} ProtoTunnelControlT; + +typedef struct ProtoTunnelT +{ + ProtoTunnelInfoT Info; //!< mapping info + uint32_t uVirtualAddr; //!< virtual address, used for NAT support + uint32_t uLocalClientId; //!< tunnel-specific local clientId (can override ProtoTunnelT if non-zero) + int16_t iBuffSize; //!< current next open position in packet data buffer + int16_t iDataSize; //!< current amount of data queued + int8_t iNumPackets; //!< number of packets queued + uint8_t uHandshakeSize; //!< size of handshake header, determined by protocol version + uint8_t uConnIdent[2]; //!< connection identifier, determined in handshaking (version 1.1+ only) + + NetCritT PacketCrit; //!< packet buffer critical section + + uint16_t uSendOffset; //!< stream send offset + uint16_t uRecvOffset; //!< stream recv offset + uint16_t uRecvOOPOffset; //!< stream recv offset for OOPState + uint16_t _pad2; + + CryptArc4T CryptSendState; //!< crypto state for encrypting data + CryptArc4T CryptRecvState; //!< crypto state for decrypting data + CryptArc4T CryptRecvOOPState; //!< state for receiving out-of-order packets + + #if DIRTYCODE_LOGGING + uint32_t uLastStatUpdate; //!< last time stat update was printed + ProtoTunnelStatT LastSendStat; //!< previous send stat + #endif + + uint32_t uLastTunnelSend; //!< last time data was sent on this tunnel + + ProtoTunnelStatT SendStat; //!< cumulative send statistics + ProtoTunnelStatT RecvStat; //!< cumulative recv statistics + + uint32_t uLastSendNumBytes; //!< tracking variable for send bytes per second calculation + uint32_t uLastSendNumSubpacketBytes; //!< tracking variable for send number of subpacket bytes per second calculation + uint32_t uLastRecvNumBytes; //!< tracking variable for receive bytes per second calculation + uint32_t uLastRecvNumSubpacketBytes; //!< tracking variable for receive number of subpacket bytes per second calculation + + char aKeyList[PROTOTUNNEL_MAXKEYS][128]; //!< crypto key(s) + char aHmacKey[64]; //!< storage for HMAC key (HMAC IV encrypted by tunnel key) + uint8_t uActive; //!< "active" if we've received data from remote peer + uint8_t uRefCount; //!< tunnel ref count + uint8_t uSendKey; //!< index of key we are using to send with + uint8_t uRecvKey; //!< index of key we are using to recv with + uint8_t bSendCtrlInfo; //!< whether to send control info or not + uint8_t aForceCrypt[PROTOTUNNEL_MAXPACKETS]; //!< force crypt override (per sub-packet) + uint8_t aPacketData[PROTOTUNNEL_PACKETBUFFER+PROTOTUNNEL_MAXHDRSIZE+2]; //!< packet aggregator +} ProtoTunnelT; + +struct ProtoTunnelRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + SocketT *pSocket; //!< physical socket tunnel uses + SocketT *pServerSocket; //!< server socket (xboxone only) + + uint16_t aServerRemotePortList[16]; //!< remote ports for server socket + uint16_t uTunnelPort; //!< port socket tunnel is bound to + uint32_t uLocalClientId; //!< local client identifier + + uint16_t uVersion; //!< protocol version + uint8_t uHmacType; //!< HMAC hash type + uint8_t uHmacSize; //!< HMAC size + + int32_t iMaxTunnels; //!< maximum number of tunnels + int32_t iMaxRecv; //!< maximum number of receive calls that may be made in one execution of _ProtoTunnelRecvCallback + int32_t iIdleCbRate; //!< rate that socket callback is called by idle thread in milliseconds + int32_t iVerbosity; //!< verbosity level + + uint32_t uVirtualAddr; //!< next free virtual address + uint32_t uFlushRate; //!< rate at which packets are flushed by update + + #if DIRTYCODE_DEBUG + int32_t iPacketDrop; //!< drop the next n packets + #endif + + uint32_t uNumRecvCalls; //!< number of receive calls + uint32_t uNumPktsRecvd; //!< number of packets received + uint32_t uNumSubPktsRecvd; //!< number of sub-packets received + uint32_t uNumPktsDiscard; //!< number of out-of-order packet discards + + ProtoTunnelCallbackT *pCallback; //!< prototunnel event callback + void *pUserData; //!< callback user data + + RawRecvCallbackT *pRawRecvCallback; //!< raw inbound data filtering function + void *pRawRecvUserData; //!< callback user data + + NetCritT TunnelsCritS; //!< "send thread" critical section + NetCritT TunnelsCritR; //!< "recv thread" critical section + ProtoTunnelT Tunnels[1]; //!< variable-length tunnel array - must be last +}; + +/*** Variables ********************************************************************/ + +// hmac init vector; borrowed from SHA512 initial hash +static const uint8_t _ProtoTunnel_aHmacInitVec[64] = +{ + 0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, + 0xbb, 0x67, 0xae, 0x85, 0x84, 0xca, 0xa7, 0x3b, + 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94, 0xf8, 0x2b, + 0xa5, 0x4f, 0xf5, 0x3a, 0x5f, 0x1d, 0x36, 0xf1, + 0x51, 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, + 0x9b, 0x05, 0x68, 0x8c, 0x2b, 0x3e, 0x6c, 0x1f, + 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, 0x6b, + 0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79 +}; + +static uint8_t _ProtoTunnel_bBaseAddrUsed[256]; + +//! used to control behavior for _ProtoTunnelSocketOpen +static uint8_t _ProtoTunnel_bRetryRandomOnFailure = TRUE; + +/*** Private Functions ************************************************************/ + +int32_t ProtoTunnelValidatePacket(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, uint8_t *pOutputData, const uint8_t *pPacketData, int32_t iPacketSize, const char *pKey); + + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelSetVersion + + \Description + Set protocol version for the specified tunnel + + \Input *pProtoTunnel - module state + \Input *pTunnel - tunnel to set version for + \Input uTunnelVers - version to set (PROTOTUNNEL_VERSION_*) + + \Version 03/26/2015 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoTunnelSetVersion(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, uint32_t uTunnelVers) +{ + uint32_t uHandshakeSize = 2; // all handshake sizes are a minimum of two bytes + + // calculate size of header + switch (uTunnelVers) + { + case PROTOTUNNEL_VERSION_1_1: + uHandshakeSize += sizeof(ProtoTunnelHandshake_1_1_T); + break; + default: + NetPrintf(("prototunnel: [%p] trying to set unknown version %d.%d; assuming 1.1 format\n", pProtoTunnel, uTunnelVers>>8, uTunnelVers&0xff)); + uHandshakeSize += sizeof(ProtoTunnelHandshake_1_1_T); + break; + } + + pTunnel->Info.uTunnelVers = uTunnelVers; + pTunnel->uHandshakeSize = uHandshakeSize; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelGetBaseAddress + + \Description + Assign a free base address to the specified prototunnel instance + + \Input *pProtoTunnel - module state + + \Output + int32_t - 0 for success, -1 if no address available + + \Version 08/22/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelGetBaseAddress(ProtoTunnelRefT *pProtoTunnel) +{ + int32_t iRetCode = -1; // default to error + int32_t iIndex; + + // skip index=0 because we don't want to create base addresses with format 0.x.x.x + for (iIndex = 1; iIndex < 256; iIndex++) + { + if (_ProtoTunnel_bBaseAddrUsed[iIndex] == FALSE) + { + _ProtoTunnel_bBaseAddrUsed[iIndex] = TRUE; + pProtoTunnel->uVirtualAddr = iIndex << 24; + + NetPrintf(("prototunnel: [%p] base virtual address set to %a\n", pProtoTunnel, pProtoTunnel->uVirtualAddr)); + + iRetCode = 0; // signal success + break; + } + } + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelReleaseBaseAddress + + \Description + Return a free base address to be assigned to a new protoutunnel instance + + \Input *pProtoTunnel - module state + + \Version 08/22/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _ProtoTunnelReleaseBaseAddress(ProtoTunnelRefT *pProtoTunnel) +{ + NetPrintf(("prototunnel: [%p] releasing base virtual address %a\n", pProtoTunnel, (pProtoTunnel->uVirtualAddr&0xFF000000))); + + _ProtoTunnel_bBaseAddrUsed[pProtoTunnel->uVirtualAddr >> 24] = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelIndexFromId + + \Description + Return tunnel index of tunnel with specified Id, or -1 if there is no + tunnel that matches. + + \Input *pProtoTunnel - module state + \Input uTunnelId - tunnel ident + + \Output + int32_t - tunnel index, or -1 if not found + + \Version 02/20/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelIndexFromId(ProtoTunnelRefT *pProtoTunnel, uint32_t uTunnelId) +{ + int32_t iTunnel; + + // find tunnel id + for (iTunnel = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel += 1) + { + // found it? + if (pProtoTunnel->Tunnels[iTunnel].uVirtualAddr == uTunnelId) + { + return(iTunnel); + } + } + // not found + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelStreamAdvance + + \Description + Advance stream offset, and advance crypt state if we need to for padding + purposes. + + \Input *pCryptState - pointer to crypt state to advance + \Input *pOffset - pointer to current offset (16bit in crypt data units) + \Input uOffset - amount to advance by (in bytes) + + \Version 12/08/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoTunnelStreamAdvance(CryptArc4T *pCryptState, uint16_t *pOffset, uint32_t uOffset) +{ + uint32_t uCryptAlign; + + // convert from number of bytes to number of crypt data units + *pOffset += (uint16_t)(uOffset>>PROTOTUNNEL_CRYPTBITS); + + // if we have 'left-over' bytes, advance the crypt state and add a data unit + if ((uCryptAlign = (uOffset & PROTOTUNNEL_CRYPTMASK)) != 0) + { + uCryptAlign = PROTOTUNNEL_CRYPTALGN - uCryptAlign; + CryptArc4Advance(pCryptState, uCryptAlign); + *pOffset += 1; + } + // handle 15 bit wrapping + *pOffset &= 0x7fff; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelVirtualToPhysical + + \Description + Convert a virtual address into its corresponding physical address. + + \Input *pProtoTunnel - pointer to module state + \Input uVirtualAddr - virtual address + \Input *pBuf - [out] storage for sockaddr (optional) + \Input iBufSize - size of buffer + + \Output + int32_t - physical address, or zero if no match + + \Version 03/31/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelVirtualToPhysical(ProtoTunnelRefT *pProtoTunnel, uint32_t uVirtualAddr, char *pBuf, int32_t iBufSize) +{ + ProtoTunnelT *pTunnel; + uint32_t uRemoteAddr; + int32_t iTunnel; + + // acquire exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // find tunnel virtual address is bound to + for (iTunnel = 0, uRemoteAddr = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++) + { + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + if (pTunnel->uVirtualAddr == uVirtualAddr) + { + struct sockaddr SockAddr; + if ((pBuf != NULL) && (iBufSize >= (signed)sizeof(SockAddr))) + { + SockaddrInit(&SockAddr, AF_INET); + SockaddrInSetAddr(&SockAddr, pTunnel->Info.uRemoteAddr); + SockaddrInSetPort(&SockAddr, pTunnel->Info.uRemotePort); + ds_memcpy(pBuf, &SockAddr, sizeof(SockAddr)); + } + uRemoteAddr = pTunnel->Info.uRemoteAddr; + break; + } + } + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + // return addr to caller + return(uRemoteAddr); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelServerPortListFunc + + \Description + Perform a function (add, del, check) on server socket port list + + \Input *pProtoTunnel - pointer to module state + \Input uPort - port value + \Input ePortOp - function type (PORTLIST_*) + + \Output + uint32_t - true=success, else false + + \Version 10/04/2017 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ProtoTunnelServerPortListFunc(ProtoTunnelRefT *pProtoTunnel, uint16_t uPort, ProtoTunnelPortListFuncE ePortOp) +{ + const int32_t iNumPorts = sizeof(pProtoTunnel->aServerRemotePortList) / sizeof(pProtoTunnel->aServerRemotePortList[0]); + uint16_t uCheckVal = (ePortOp != PROTOTUNNEL_PORTLIST_ADD) ? uPort : 0; + uint32_t bSuccess = FALSE; + int32_t iPortIdx; + + // find entry + for (iPortIdx = 0; iPortIdx < iNumPorts; iPortIdx += 1) + { + if (pProtoTunnel->aServerRemotePortList[iPortIdx] == uCheckVal) + { + break; + } + } + // operate on entry + if (iPortIdx < iNumPorts) + { + switch (ePortOp) + { + case PROTOTUNNEL_PORTLIST_DEL: + uPort = 0; + case PROTOTUNNEL_PORTLIST_ADD: + pProtoTunnel->aServerRemotePortList[iPortIdx] = uPort; + case PROTOTUNNEL_PORTLIST_CHK: + bSuccess = TRUE; + default: + break; + } + } + // return result to caller + return(bSuccess); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelSocketSendto + + \Description + Convert a virtual address into its corresponding physical address. + + \Input *pProtoTunnel - pointer to module state + \Input *pBuf - data to send + \Input iLen - length of data to send + \Input iFlags - flags + \Input *pTo - address to send to + \Input iToLen - length of address + + \Output + int32_t - SocketSendto() result, or SOCKERR_INVALID if no socket + + \Version 10/01/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelSocketSendto(ProtoTunnelRefT *pProtoTunnel, const char *pBuf, int32_t iLen, int32_t iFlags, const struct sockaddr *pTo, int32_t iToLen) +{ + SocketT *pSocket = _ProtoTunnelServerPortListFunc(pProtoTunnel, SockaddrInGetPort(pTo), PROTOTUNNEL_PORTLIST_CHK) ? pProtoTunnel->pServerSocket : pProtoTunnel->pSocket; + if (pSocket != NULL) + { + return(SocketSendto(pSocket, pBuf, iLen, iFlags, pTo, iToLen)); + } + else + { + return(SOCKERR_INVALID); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelBufferCollect + + \Description + Collect buffered data in final packet form. + + \Input *pProtoTunnel - pointer to module state + \Input *pTunnel - tunnel to send data on + \Input iHeadSize - size of packet headers + \Input *pPacketData - [out] buffer for finalized packet + \Input **ppHeaderDst - pointer to current header output in finalized packet + \Input **ppPacketDst - pointer to current packet data output in finalized packet + \Input uPortFlag - flag indicating whether we are collecting encrypted or + unencrypted packet data + \Input *pLimit - end of buffer, used for debug overflow check + + \Output + int32_t - size of packet data added to output buffer + + \Version 12/08/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelBufferCollect(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, int32_t iHeadSize, uint8_t *pPacketData, uint8_t **ppHeaderDst, uint8_t **ppPacketDst, uint32_t uPortFlag, uint8_t *pLimit) +{ + int32_t iDataSize, iPacket, iPacketSize; + uint8_t *pHeaderSrc, *pPacketSrc; + uint32_t bEnabled; + + // point to packet headers and data + pHeaderSrc = pTunnel->aPacketData + PROTOTUNNEL_MAXHDROFFS; + pPacketSrc = pTunnel->aPacketData + PROTOTUNNEL_MAXHDRSIZE + PROTOTUNNEL_MAXHDROFFS; + iDataSize = 0; + + // collect packets + for (iPacket = 0; iPacket < pTunnel->iNumPackets; iPacket++) + { + // get packet size + iPacketSize = (pHeaderSrc[0] << 4) | (pHeaderSrc[1] >> 4); + + // if packet encryption matches that specified by the caller, copy it and its header + bEnabled = (((pTunnel->Info.aPortFlags[pHeaderSrc[1] & 0xf] & (unsigned)PROTOTUNNEL_PORTFLAG_ENCRYPTED) == uPortFlag)); + if (pTunnel->aForceCrypt[iPacket] != 0) + { + bEnabled = !bEnabled; + } + if (bEnabled) + { + // make sure we can fit the packet; this shouldn't happen, because we only buffer packets we can fit in ProtoTunnelBufferData() + if ((((*ppPacketDst) + iPacketSize) - pLimit) > 0) + { + NetPrintf(("prototunnel: [%p][%04d] collect buffer overflow by %d bytes; packet of size %d will be discarded\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), (((*ppPacketDst) + iPacketSize) - pLimit), iPacketSize)); + break; + } + + // copy packet data and add it to packet buffer + ds_memcpy(*ppPacketDst, pPacketSrc, iPacketSize); + (*ppHeaderDst)[0] = pHeaderSrc[0]; + (*ppHeaderDst)[1] = pHeaderSrc[1]; + iDataSize += iPacketSize; + *ppPacketDst += iPacketSize; + *ppHeaderDst += PROTOTUNNEL_PKTHDRSIZE; + } + + // increment to next packet header and packet data + pHeaderSrc += PROTOTUNNEL_PKTHDRSIZE; + pPacketSrc += iPacketSize; + } + + // return size to caller + return(iDataSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelBufferSend + + \Description + Send buffered data + + \Input *pProtoTunnel - pointer to module state + \Input *pTunnel - tunnel to send data on + \Input uCurTick - current tick count + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoTunnelBufferSend(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, uint32_t uCurTick) +{ + uint8_t aPacketData[PROTOTUNNEL_PACKETBUFFER]; + int32_t iCryptSize, iDataSize, iHeadSize, iHshkSize, iResult; + uint8_t *pHeaderDst, *pPacketDst, *pCrypt; + uint8_t *pHmac; + uint16_t uSendHeader = pTunnel->uSendOffset | 0x8000; // add GENERIC bit so we know the protocol type + struct sockaddr SendAddr; + #if DIRTYCODE_LOGGING + int32_t iNumPackets; + #endif + + // get sole access to packet buffer + NetCritEnter(&pTunnel->PacketCrit); + + // no data to send or socket to send it on? + if ((pProtoTunnel->pSocket == NULL) || (pTunnel->iDataSize <= 0)) + { + NetCritLeave(&pTunnel->PacketCrit); + return; + } + + // create send addr + SockaddrInit(&SendAddr, AF_INET); + SockaddrInSetAddr(&SendAddr, pTunnel->Info.uRemoteAddr); + SockaddrInSetPort(&SendAddr, pTunnel->Info.uRemotePort); + + // format packet in local buffer + ds_memclr(aPacketData, sizeof(aPacketData)); + + // get handshake header size and total header size (including sub-packet headers) + iHshkSize = (pTunnel->bSendCtrlInfo) ? pTunnel->uHandshakeSize : 4; + iHeadSize = (pTunnel->iNumPackets * PROTOTUNNEL_PKTHDRSIZE) + iHshkSize; + + // set up for copy + pHeaderDst = aPacketData + iHshkSize; + pPacketDst = aPacketData + iHeadSize; + + // reserve space for the HMAC; we put it here so it will be encrypted and easy to locate + pHmac = aPacketData + iHeadSize; + iCryptSize = pProtoTunnel->uHmacSize; + ds_memclr(pHmac, iCryptSize); + pPacketDst += iCryptSize; + + // collect encrypted packets + iCryptSize += _ProtoTunnelBufferCollect(pProtoTunnel, pTunnel, iHeadSize, aPacketData, &pHeaderDst, &pPacketDst, PROTOTUNNEL_PORTFLAG_ENCRYPTED, aPacketData + PROTOTUNNEL_PACKETBUFFER); + + // collect unencrypted packets + iDataSize = _ProtoTunnelBufferCollect(pProtoTunnel, pTunnel, iHeadSize, aPacketData, &pHeaderDst, &pPacketDst, 0, aPacketData + PROTOTUNNEL_PACKETBUFFER) + iCryptSize; + + // calculate total encrypted size (encrypted headers + encrypted data, but skip the first two bytes) + iCryptSize += iHeadSize - iHshkSize; + // total size of packet + iDataSize += iHeadSize; + + // write protocol header + if (pTunnel->bSendCtrlInfo) + { + ProtoTunnelHandshakeT Handshake; + uint32_t uLocalClientId = (pTunnel->uLocalClientId != 0) ? pTunnel->uLocalClientId : pProtoTunnel->uLocalClientId; + uint16_t uTunnelIndex = pTunnel - pProtoTunnel->Tunnels; + uint16_t uIdent = pTunnel->Info.uTunnelVers | PROTOTUNNEL_IDENT_HANDSHAKE; + + Handshake.aIdent[0] = (uint8_t)(uIdent >> 8); + Handshake.aIdent[1] = (uint8_t)(uIdent); + Handshake.Version.V1_1.aCrypt[0] = 0; + Handshake.Version.V1_1.aCrypt[1] = 0; + Handshake.Version.V1_1.aClientIdent[0] = (uint8_t)(uLocalClientId >> 24); + Handshake.Version.V1_1.aClientIdent[1] = (uint8_t)(uLocalClientId >> 16); + Handshake.Version.V1_1.aClientIdent[2] = (uint8_t)(uLocalClientId >> 8); + Handshake.Version.V1_1.aClientIdent[3] = (uint8_t)(uLocalClientId); + Handshake.Version.V1_1.aConnIdent[0] = (uint8_t)(uTunnelIndex >> 8); + Handshake.Version.V1_1.aConnIdent[1] = (uint8_t)(uTunnelIndex); + Handshake.Version.V1_1.uActive = pTunnel->uActive; + ds_memcpy(aPacketData, &Handshake, sizeof(Handshake)); + + pCrypt = ((ProtoTunnelHandshakeT *)aPacketData)->Version.V1_1.aCrypt; + } + else + { + aPacketData[0] = pTunnel->uConnIdent[0]; + aPacketData[1] = pTunnel->uConnIdent[1]; + pCrypt = aPacketData+2; + } + + // set tunnel encryption header + pCrypt[0] = (uint8_t)(uSendHeader >> 8); + pCrypt[1] = (uint8_t)(uSendHeader >> 0); + + // calculate the hmac and write it into the space reserved for it + if (pProtoTunnel->uHmacType != CRYPTHASH_NULL) + { + CryptHmacCalc(pHmac, pProtoTunnel->uHmacSize, aPacketData, iDataSize, (uint8_t *)pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey), (CryptHashTypeE)pProtoTunnel->uHmacType); + #if PROTOTUNNEL_HMAC_DEBUG + NetPrintMem(aPacketData, iDataSize, "send-hmac-data"); + NetPrintMem(pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey), "send-hmac-key"); + NetPrintMem(pHmac, pProtoTunnel->uHmacSize, "send-hmac"); + #endif + } + + // verbose-only writing of unencrypted payload + if (pProtoTunnel->iVerbosity > 3) + { + NetPrintMem(aPacketData, (iDataSize < 64) ? iDataSize : 64, "prototunnel-send-nocrypt"); + } + // encrypt payload and advance stream if necessary + CryptArc4Apply(&pTunnel->CryptSendState, aPacketData + iHshkSize, iCryptSize); + _ProtoTunnelStreamAdvance(&pTunnel->CryptSendState, &pTunnel->uSendOffset, iCryptSize); + + // accumulate stats + pTunnel->SendStat.uLastPacketTime = uCurTick; + pTunnel->SendStat.uNumBytes += iDataSize; + pTunnel->SendStat.uNumSubpacketBytes += iDataSize - iHeadSize - pProtoTunnel->uHmacSize; + pTunnel->SendStat.uNumPackets += 1; + pTunnel->SendStat.uNumSubpackets += pTunnel->iNumPackets + pTunnel->bSendCtrlInfo; + + #if DIRTYCODE_LOGGING + iNumPackets = pTunnel->iNumPackets + pTunnel->bSendCtrlInfo; + #endif + + // mark data as sent + pTunnel->iBuffSize = 0; + pTunnel->iDataSize = 0; + pTunnel->iNumPackets = 0; + ds_memclr(pTunnel->aForceCrypt, sizeof(pTunnel->aForceCrypt)); + + // release packet buffer critical section + NetCritLeave(&pTunnel->PacketCrit); + + #if DIRTYCODE_LOGGING + if (NetTickDiff(uCurTick, pTunnel->uLastStatUpdate) > 5000) + { + ProtoTunnelStatT DiffStat; + DiffStat.uNumBytes = pTunnel->SendStat.uNumBytes - pTunnel->LastSendStat.uNumBytes; + DiffStat.uNumSubpacketBytes = pTunnel->SendStat.uNumSubpacketBytes - pTunnel->LastSendStat.uNumSubpacketBytes; + DiffStat.uNumPackets = pTunnel->SendStat.uNumPackets - pTunnel->LastSendStat.uNumPackets; + DiffStat.uNumSubpackets = pTunnel->SendStat.uNumSubpackets - pTunnel->LastSendStat.uNumSubpackets; + ds_memcpy_s(&pTunnel->LastSendStat, sizeof(pTunnel->LastSendStat), &pTunnel->SendStat, sizeof(pTunnel->SendStat)); + NetPrintfVerbose((pProtoTunnel->iVerbosity, 2, "prototunnel: [%p][%04d] pkts: %d eff: %.2f sent: %d eff: %.2f\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), + DiffStat.uNumPackets, (float)DiffStat.uNumSubpackets / (float)DiffStat.uNumPackets, + DiffStat.uNumBytes, (float)DiffStat.uNumSubpacketBytes / (float)DiffStat.uNumBytes)); + pTunnel->uLastStatUpdate = uCurTick; + } + if (pProtoTunnel->iVerbosity > 3) + { + NetPrintMem(aPacketData, (iDataSize < 64) ? iDataSize : 64, "prototunnel-send"); + } + #endif + + // send the data + if ((iResult = _ProtoTunnelSocketSendto(pProtoTunnel, (char *)aPacketData, iDataSize, 0, &SendAddr, sizeof(SendAddr))) < 0) + { + NetPrintf(("prototunnel: [%p][%04d] send - error %d sending buffered packet to %a:%d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), + iResult, SockaddrInGetAddr(&SendAddr), SockaddrInGetPort(&SendAddr))); + } + else + { + NetPrintfVerbose((pProtoTunnel->iVerbosity, 2, "prototunnel: [%p][%04d] send - sent %d bytes [%d packets] to %a:%d\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iDataSize, iNumPackets, SockaddrInGetAddr(&SendAddr), SockaddrInGetPort(&SendAddr))); + } + + pTunnel->uLastTunnelSend = NetTick(); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelBufferData + + \Description + Buffer a send + + \Input *pProtoTunnel - pointer to module state + \Input *pTunnel - tunnel to buffer data on + \Input *pData - data to be sent + \Input uSize - size of data to send + \Input uPortIdx - index of port in tunnel map + \Input bForceCrypt - TRUE to force encryption of packet, else FALSE + + \Output + int32_t - amount of data buffered, or socket error code (SOCKERR_XXX) + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelBufferData(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, const uint8_t *pData, uint32_t uSize, uint32_t uPortIdx, uint8_t bForceCrypt) +{ + uint32_t uCurTick = NetTick(); + uint8_t *pBuffer; + + // make sure data is within size limits + if ((uSize == 0) || (uSize > PROTOTUNNEL_MAXPACKET)) + { + NetPrintf(("prototunnel: [%p][%04d] packet of size %d will not be tunneled (max size = %d)\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uSize, PROTOTUNNEL_MAXPACKET)); + NetPrintMem(pData, 64, "prototunnel-nobuf"); + return(SOCKERR_NORSRC); + } + + // acquire packet buffer critical section + NetCritEnter(&pTunnel->PacketCrit); + + // if buffer is full, or we've already buffered max packets, flush buffer + if ((((unsigned)pTunnel->iDataSize + uSize + PROTOTUNNEL_PKTHDRSIZE) > PROTOTUNNEL_PACKETBUFFER) || + (pTunnel->iNumPackets == PROTOTUNNEL_MAXPACKETS)) + { + // flush current packet + _ProtoTunnelBufferSend(pProtoTunnel, pTunnel, uCurTick); + } + + // if buffer is empty, reserve space for max header data (1.1 handshake + max sub-packet headers) + if (pTunnel->iBuffSize == 0) + { + pTunnel->iBuffSize = PROTOTUNNEL_MAXHDROFFS + PROTOTUNNEL_MAXHDRSIZE; + pTunnel->iDataSize = PROTOTUNNEL_MAXHDROFFS + pProtoTunnel->uHmacSize; + ds_memclr(pTunnel->aPacketData, pTunnel->iBuffSize); + } + + // store packet header + pBuffer = pTunnel->aPacketData + (pTunnel->iNumPackets * PROTOTUNNEL_PKTHDRSIZE) + PROTOTUNNEL_MAXHDROFFS; + pBuffer[0] = (uint8_t)(uSize >> 4); + pBuffer[1] = (uint8_t)((uSize << 4) | uPortIdx); + + // store packet data + pBuffer = pTunnel->aPacketData + pTunnel->iBuffSize; + ds_memcpy(pBuffer, pData, uSize); + pTunnel->iBuffSize += uSize; + pTunnel->iDataSize += uSize + PROTOTUNNEL_PKTHDRSIZE; + pTunnel->aForceCrypt[(uint32_t)pTunnel->iNumPackets] = bForceCrypt; + + // update overall count + pTunnel->iNumPackets += 1; + + if (pTunnel->Info.aPortFlags[uPortIdx] & PROTOTUNNEL_PORTFLAG_AUTOFLUSH) + { + _ProtoTunnelBufferSend(pProtoTunnel, pTunnel, uCurTick); + } + + // release packet buffer critical section + NetCritLeave(&pTunnel->PacketCrit); + + // return buffered size to caller + return(uSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelSendCallback + + \Description + Global send callback handler. + + \Input *pSocket - socket to send on + \Input iType - socket type + \Input *pData - data to be sent + \Input iDataSize - size of data to send + \Input *pTo - destination address + \Input *pCallref - prototunnel ref + + \Output + int32_t - 0 = send not handled; >0 = send successfully handled (bytes sent); <0 = send handled but failed (SOCKERR_XXX) + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelSendCallback(SocketT *pSocket, int32_t iType, const uint8_t *pData, int32_t iDataSize, const struct sockaddr *pTo, void *pCallref) +{ + ProtoTunnelRefT *pProtoTunnel = (ProtoTunnelRefT *)pCallref; + ProtoTunnelT *pTunnel = NULL; + uint32_t uAddr, uPort; + int32_t iPort = 0, iTunnel; + int32_t iResult = 0; // default to "not handled" + uint32_t bFound, bForceCrypt=0; + #if DIRTYCODE_LOGGING + uint32_t bFoundAddr = FALSE; + #endif + struct sockaddr SockAddr; + + // only handle dgram sockets, and don't handle our own socket + if ((iType != SOCK_DGRAM) || (pProtoTunnel->pSocket == pSocket) || (pProtoTunnel->pServerSocket == pSocket)) + { + return(0); // not handled + } + + // if a destination address is not specified, get it from the socket + if (pTo == NULL) + { + SocketInfo(pSocket, 'peer', 0, &SockAddr, sizeof(SockAddr)); + pTo = &SockAddr; + } + + // get destination address and port + uAddr = SockaddrInGetAddr(pTo); + uPort = SockaddrInGetPort(pTo); + + // ignore sends to an invalid destination + if ((uAddr == 0) || (uPort == 0)) + { + return(0); // not handled + } + + // get exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + + // see if we have a match + for (iTunnel = 0, bFound = FALSE; (iTunnel < pProtoTunnel->iMaxTunnels) && (bFound == FALSE); iTunnel++) + { + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + + // does virtual address match? + if (pTunnel->uVirtualAddr != uAddr) + { + continue; + } + #if DIRTYCODE_LOGGING + // remember if we have a tunnel to this address + bFoundAddr = TRUE; + #endif + // see if we have a port match or are at the end of our port list + for (iPort = 0; iPort < PROTOTUNNEL_MAXPORTS; iPort++) + { + uint32_t uPort2 = pTunnel->Info.aRemotePortList[iPort]; + if (uPort2 == uPort) + { + bFound = TRUE; + break; + } + } + } + + // found a match? + if (bFound == TRUE) + { + // if we have a match, queue data + iResult = _ProtoTunnelBufferData(pProtoTunnel, pTunnel, pData, iDataSize, iPort, bForceCrypt); + } + else + { + NetPrintfVerbose((pProtoTunnel->iVerbosity, 1, "prototunnel: [%p] send - no match for data sent to %a:%d (%s mismatch)\n", pProtoTunnel, uAddr, uPort, bFoundAddr ? "port" : "addr")); + } + + // release exclusive access to tunnel list and return result + NetCritLeave(&pProtoTunnel->TunnelsCritS); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelDecryptAndValidatePacket2 + + \Description + Decrypt the tunnel packet + + \Input *pProtoTunnel - pointer to module state + \Input *pTunnel - tunnel + \Input *pHeadOffset - [out] offset of sub-packet header data + \Input *pPacketData - pointer to tunnel packet + \Input iRecvLen - size of tunnel packet + \Input bUpdateState - TRUE to update crypto state, else FALSE + \Input bCurrentState - TRUE when decrypting against current state, FALSE against oop state + + \Output + int32_t - PROTOTUNNEL_PACKETRECVFAIL_* on error, else number of subpackets decoded + + \Version 12/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelDecryptAndValidatePacket2(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, uint32_t *pHeadOffset, uint8_t *pPacketData, int32_t iRecvLen, uint8_t bUpdateState, uint8_t bCurrentState) +{ + int32_t iEncryptedSize, iNumPackets, iPacketOffset, iPacketSize, iHdrCopySize; + uint8_t aPacketHeader[PROTOTUNNEL_MAXHDRSIZE+PROTOTUNNEL_MAXHDROFFS]; + uint8_t *pPacketStart = pPacketData; + uint32_t uRecvOffset, uRecvOffsetState; + CryptArc4T CryptRecvState, *pCryptRecvState; + uint16_t *pRecvOffsetState; + uint16_t uIdent, uVersion; + uint8_t bSynced = FALSE; + uint32_t uHeadOffset; + + // make a copy of packet header before doing anything else, in case we need to restore it after a decrypt failure + iHdrCopySize = DS_MIN(sizeof(aPacketHeader), (unsigned)iRecvLen); + ds_memcpy(aPacketHeader, pPacketData, iHdrCopySize); + + // get reference to crypt state based on whether we are decoding the current state or oop state + pCryptRecvState = (bCurrentState) ? &pTunnel->CryptRecvState : &pTunnel->CryptRecvOOPState; + pRecvOffsetState = (bCurrentState) ? &pTunnel->uRecvOffset : &pTunnel->uRecvOOPOffset; + + // get stream offset from packet header - this tells us where we need to be to be able to decrypt the packet + uRecvOffset = PROTOTUNNEL_GetStreamOffsetFromPacket(pTunnel->Info.uTunnelVers, pPacketData); + + // work on a local copy of the crypt state and stream offset, in case we need to throw this packet out + ds_memcpy_s(&CryptRecvState, sizeof(CryptRecvState), pCryptRecvState, sizeof(*pCryptRecvState)); + uRecvOffsetState = *pRecvOffsetState; + + // lost data? sync the cipher + if (uRecvOffset != uRecvOffsetState) + { + // calc how many data units we've missed + int16_t iRecvOffset = (signed)uRecvOffset; + int16_t iTunlOffset = (signed)uRecvOffsetState; + // calc 15bit offset + int16_t iRecvDiff = (iRecvOffset - iTunlOffset) & 0x7fff; + /* sign extend our 15bit offset - we do it this way because the simpler way of shifting left and + back again right relies on the right shift sign-extending the value, however in the C standard + it is implementation-dependent whether the shift is treated as signed or unsigned */ + iRecvDiff |= (iRecvDiff & 16384) << 1; + + /* check to see if packet is previous to our current window offset; we call this an out-of-order packet and handle it + differently from a skipped packet, which is ahead of our current window offset. this distinction is made because + it is trivial to advance the state, but it cannot be rolled back */ + if (iRecvDiff < 0) + { + NetPrintf(("prototunnel: [%p][%04d] received out of order packet (off=%d)\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iRecvDiff)); + return(PROTOTUNNEL_PACKETRECVFAIL_OUTOFORDER); + } + + // advance the cipher to resync + NetPrintf(("prototunnel: [%p][%04d] stream skip %d bytes\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iRecvDiff<Tunnels), uVersion>>8, uVersion&0xff)); + if (uVersion != pTunnel->Info.uTunnelVers) + { + NetPrintf(("prototunnel: [%p][%04d] version mismatch; received %d.%d but expected %d.%d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uVersion>>8, uVersion&0xff, + pTunnel->Info.uTunnelVers>>8, pTunnel->Info.uTunnelVers&0xff)); + if ((uVersion > pTunnel->Info.uTunnelVers) || (uVersion < PROTOTUNNEL_VERSION_1_1)) + { + NetPrintf(("prototunnel: [%p][%04d] can't connect with protocol version %d.%d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uVersion>>8, uVersion&0xff)); + return(PROTOTUNNEL_PACKETRECVFAIL_VERSION); + } + } + + ds_memcpy(&Handshake, pPacketData, sizeof(Handshake)); + pTunnel->uConnIdent[0] = Handshake.Version.V1_1.aConnIdent[0]; + pTunnel->uConnIdent[1] = Handshake.Version.V1_1.aConnIdent[1]; + NetPrintf(("prototunnel: [%p][%04d] setting connection ident to %d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), (pTunnel->uConnIdent[0] << 8) | pTunnel->uConnIdent[1])); + + uHeadOffset = pTunnel->uHandshakeSize; + } + else + { + uVersion = pTunnel->Info.uTunnelVers; + uHeadOffset = 4; + } + + // skip header + pPacketData += uHeadOffset; + iPacketOffset = uHeadOffset; + // save sub-packet header offset for later use + *pHeadOffset = uHeadOffset; + + /* subtract out hmac size while we decrypt packet headers and check overall + packet size, as hmac is not included in that calculation */ + iRecvLen -= pProtoTunnel->uHmacSize; + + // iterate through packet headers + for (iNumPackets = 0, iEncryptedSize = 0; iPacketOffset < iRecvLen; iNumPackets++) + { + // decrypt the packet header + CryptArc4Apply(&CryptRecvState, pPacketData, PROTOTUNNEL_PKTHDRSIZE); + uRecvOffset += PROTOTUNNEL_PKTHDRSIZE; + + // extract size and port info + iPacketSize = (pPacketData[0] << 4) | (pPacketData[1] >> 4); + iPacketOffset += iPacketSize + PROTOTUNNEL_PKTHDRSIZE; + if (pTunnel->Info.aPortFlags[pPacketData[1] & 0xf] & PROTOTUNNEL_PORTFLAG_ENCRYPTED) + { + iEncryptedSize += iPacketSize; + } + + // increment to next packet header + pPacketData += PROTOTUNNEL_PKTHDRSIZE; + } + + // make sure size matches + if (iPacketOffset != iRecvLen) + { + NetPrintf(("prototunnel: [%p][%04d] recv - mismatched size in received packet (%d received, %d decoded)\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iRecvLen+pProtoTunnel->uHmacSize, iPacketOffset)); + // restore original packet header + ds_memcpy(pPacketStart, aPacketHeader, iHdrCopySize); + return(PROTOTUNNEL_PACKETRECVFAIL_VALIDATE); + } + + // restore hmac to packet size, and add hmac to size of data to be decrypted + iRecvLen += pProtoTunnel->uHmacSize; + iEncryptedSize += pProtoTunnel->uHmacSize; + + // decrypt encrypted packet data + if (iEncryptedSize > 0) + { + CryptArc4Apply(&CryptRecvState, pPacketData, iEncryptedSize); + uRecvOffset += iEncryptedSize; + } + + // calculate HMAC and compare with what we received + if (pProtoTunnel->uHmacType != CRYPTHASH_NULL) + { + uint8_t aPackHmac[PROTOTUNNEL_HMAC_MAXSIZE], aCalcHmac[PROTOTUNNEL_HMAC_MAXSIZE]; + // make a copy of HMAC + ds_memcpy_s(aPackHmac, sizeof(aPackHmac), pPacketData, pProtoTunnel->uHmacSize); + // clear HMAC payload in packet to zero so we can calculate the HMAC ourselves + ds_memclr(pPacketData, pProtoTunnel->uHmacSize); + // calculate HMAC + CryptHmacCalc(aCalcHmac, pProtoTunnel->uHmacSize, pPacketStart, iRecvLen, (uint8_t *)pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey), (CryptHashTypeE)pProtoTunnel->uHmacType); + // validate + if (memcmp(aPackHmac, aCalcHmac, pProtoTunnel->uHmacSize)) + { + NetPrintf(("prototunnel: [%p][%04d] bad HMAC!\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + #if PROTOTUNNEL_HMAC_DEBUG + NetPrintMem(pPacketData, pProtoTunnel->uHmacSize, "recv-hmac"); + NetPrintMem(pPacketStart, iRecvLen, "recv-hmac-data"); + NetPrintMem(pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey), "recv-hmac-key"); + #endif + return(PROTOTUNNEL_PACKETRECVFAIL_VALIDATE); + } + else + { + #if PROTOTUNNEL_HMAC_DEBUG + NetPrintf(("prototunnel: [%p][%04d] recv - hmac validated\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + #endif + } + } + + // update crypt state and stream offset with working temp copies + if (bUpdateState) + { + // if we had to sync the cipher, save current state to oop state before advancing for possible future out-of-order packet decoding + if (bCurrentState && bSynced) + { + NetPrintf(("prototunnel: [%p][%04d] saving out-of-order state\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + ds_memcpy_s(&pTunnel->CryptRecvOOPState, sizeof(pTunnel->CryptRecvOOPState), pCryptRecvState, sizeof(*pCryptRecvState)); + pTunnel->uRecvOOPOffset = *pRecvOffsetState; + } + // now save advanced state + ds_memcpy_s(pCryptRecvState, sizeof(*pCryptRecvState), &CryptRecvState, sizeof(CryptRecvState)); + *pRecvOffsetState = (uint16_t)uRecvOffsetState; + // advance stream by received encrypted packet data size, plus padding if necessary + _ProtoTunnelStreamAdvance(pCryptRecvState, pRecvOffsetState, uRecvOffset); + } + + // if we downgraded protocol version, update here + if (pTunnel->Info.uTunnelVers != uVersion) + { + NetPrintf(("prototunnel: [%p][%04d] downgrading to version %d.%d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uVersion>>8, uVersion&0xff)); + _ProtoTunnelSetVersion(pProtoTunnel, pTunnel, uVersion); + } + + // return number of packets decoded + return(iNumPackets); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelDecryptAndValidatePacket + + \Description + Decrypt the tunnel packet, with limited out-of-order packet recovery + + \Input *pProtoTunnel - pointer to module state + \Input *pTunnel - tunnel + \Input *pHeadOffset - [out] offset of sub-packet header data + \Input *pPacketData - pointer to tunnel packet + \Input iRecvLen - size of tunnel packet + \Input bUpdateState - TRUE to update crypt state, else FALSE + + \Output + int32_t - PROTOTUNNEL_PACKETRECVFAIL_* on error, else number of subpackets decoded + + \Notes + If an out-of-order packet is detected against the current tunnel decrypt + state, a single attempt is made to recover the packet using the out-of-order + decrypt state. + + \Version 02/21/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelDecryptAndValidatePacket(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, uint32_t *pHeadOffset, uint8_t *pPacketData, int32_t iRecvLen, uint8_t bUpdateState) +{ + int32_t iResult; + // try decrypting packet with current state + if ((iResult = _ProtoTunnelDecryptAndValidatePacket2(pProtoTunnel, pTunnel, pHeadOffset, pPacketData, iRecvLen, bUpdateState, TRUE)) == PROTOTUNNEL_PACKETRECVFAIL_OUTOFORDER) + { + // received out-of-order packet prior to our current state; try again with out-of-order packet state + if ((iResult = _ProtoTunnelDecryptAndValidatePacket2(pProtoTunnel, pTunnel, pHeadOffset, pPacketData, iRecvLen, bUpdateState, FALSE)) > 0) + { + NetPrintf(("prototunnel: [%p][%04d] out of order packet recovered by oop state\n", pProtoTunnel, pTunnel - pProtoTunnel->Tunnels)); + } + } + // return result + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelGetControlInfo + + \Description + Get control info from handshake + + \Input *pTunnel - tunnel packet was received on + \Input *pControlInfo - [out] control info from packet data + \Input *pPacketData - source data + + \Output + uint32_t - non-zero if control info was found, else zero + + \Version 03/26/2015 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _ProtoTunnelGetControlInfo(ProtoTunnelT *pTunnel, ProtoTunnelControlT *pControlInfo, const uint8_t *pPacketData) +{ + uint32_t bFound = FALSE; + + // get ident + uint16_t uIdent = PROTOTUNNEL_GetIdentFromPacket(pPacketData); + + // get control info, if present + if (uIdent & PROTOTUNNEL_IDENT_HANDSHAKE) + { + ProtoTunnelHandshakeT *pHandshake = (ProtoTunnelHandshakeT *)pPacketData; + pControlInfo->uPacketType = PROTOTUNNEL_CTRL_INIT; + pControlInfo->aProtocolVers[0] = pHandshake->aIdent[0] & ~0x80; + pControlInfo->aProtocolVers[1] = pHandshake->aIdent[1]; + pControlInfo->aClientId[0] = pHandshake->Version.V1_1.aClientIdent[0]; + pControlInfo->aClientId[1] = pHandshake->Version.V1_1.aClientIdent[1]; + pControlInfo->aClientId[2] = pHandshake->Version.V1_1.aClientIdent[2]; + pControlInfo->aClientId[3] = pHandshake->Version.V1_1.aClientIdent[3]; + pControlInfo->uActive = pHandshake->Version.V1_1.uActive; + bFound = TRUE; + } + else + { + ds_memclr(pControlInfo, sizeof(*pControlInfo)); + } + + return(bFound); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelCryptSetup + + \Description + Setup crypto state for key index iKey + + \Input *pTunnel - tunnel to setup crypto state for + \Input iKey - index of key to use to setup state + \Input bForceSetup - force setup + + \Version 02/24/2016 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoTunnelCryptSetup(ProtoTunnelT *pTunnel, int32_t iKey, uint8_t bForceSetup) +{ + uint8_t *pKey = (uint8_t *)pTunnel->aKeyList[iKey]; + int32_t iKeyLen = (int32_t)strlen((char *)pKey); + + // only setup if we haven't already, or if forced + if ((pTunnel->uSendKey == (uint8_t)iKey) && (bForceSetup != TRUE)) + { + return; + } + + // initialize HMAC key (shared for both send and recv) by running IV through RC4 initialized with tunnel key + CryptArc4Init(&pTunnel->CryptSendState, pKey, iKeyLen, PROTOTUNNEL_CRYPTARC4_ITER); + ds_memcpy_s(pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey), _ProtoTunnel_aHmacInitVec, sizeof(_ProtoTunnel_aHmacInitVec)); + CryptArc4Apply(&pTunnel->CryptSendState, (uint8_t *)pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey)); + + // initialize send/recv crypto state + CryptArc4Init(&pTunnel->CryptSendState, pKey, iKeyLen, PROTOTUNNEL_CRYPTARC4_ITER); + CryptArc4Init(&pTunnel->CryptRecvState, pKey, iKeyLen, PROTOTUNNEL_CRYPTARC4_ITER); + pTunnel->uSendKey = pTunnel->uRecvKey = (uint8_t)iKey; + pTunnel->uSendOffset = pTunnel->uRecvOffset = 0; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelMatchTunnel + + \Description + Attempt to decrypt incoming data against a tunnel, and check for clientId match + + \Input *pProtoTunnel - module state + \Input *pTunnel - tunnel to check data against + \Input *pPacketData - source data + \Input iPacketSize - size of source data + + \Output + int32_t - FALSE=no match, TRUE=match + + \Version 06/26/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelMatchTunnel(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, const uint8_t *pPacketData, int32_t iPacketSize) +{ + uint8_t aPacketData[PROTOTUNNEL_PACKETBUFFER]; + int32_t iNumPackets, iRecvKey; + ProtoTunnelControlT ControlInfo; + uint32_t uClientId; + + // try any non-empty keys in this tunnel + for (iNumPackets = -1, iRecvKey = 0; iRecvKey < PROTOTUNNEL_MAXKEYS; iRecvKey += 1) + { + // skip empty keys + if (pTunnel->aKeyList[iRecvKey][0] == '\0') + { + continue; + } + + // try and validate packet against this key with a zero stream offset + if ((iNumPackets = ProtoTunnelValidatePacket(pProtoTunnel, pTunnel, aPacketData, pPacketData, iPacketSize, pTunnel->aKeyList[iRecvKey])) > 0) + { + break; + } + } + + // if we couldn't successfully decode the packet, we return no match + if (iNumPackets < 0) + { + return(FALSE); + } + + // if we're rematching an active tunnel, we're done + if (pTunnel->uActive != 0) + { + // reset crypt state, matching what ProtoTunnelValidatePacket did to successfully decrypt the packet + _ProtoTunnelCryptSetup(pTunnel, iRecvKey, TRUE); + return(TRUE); + } + + // get packet control info + if (_ProtoTunnelGetControlInfo(pTunnel, &ControlInfo, aPacketData) != 0) + { + uClientId = ControlInfo.aClientId[0] << 24; + uClientId |= ControlInfo.aClientId[1] << 16; + uClientId |= ControlInfo.aClientId[2] << 8; + uClientId |= ControlInfo.aClientId[3]; + } + else + { + NetPrintf(("prototunnel: [%p][%04d] no control data included in packet on inactive tunnel\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + return(FALSE); + } + + // if we found the matching tunnel, we're done + if (uClientId == pTunnel->Info.uRemoteClientId) + { + #if DIRTYCODE_LOGGING + if (iRecvKey != 0) + { + NetPrintf(("prototunnel: [%p][%04d] matched key with index=%d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iRecvKey)); + } + #endif + _ProtoTunnelCryptSetup(pTunnel, iRecvKey, FALSE); + return(TRUE); + } + else + { + NetPrintf(("prototunnel: [%p][%04d] packet clientId 0x%08x does not match tunnel clientId 0x%08x\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uClientId, pTunnel->Info.uRemoteClientId)); + if (pProtoTunnel->iVerbosity > 3) + { + NetPrintMem(aPacketData, (iPacketSize < 64) ? iPacketSize : 64, "prototunnel-recv-decrypt"); + } + } + + // no match + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelFindTunnel + + \Description + Find the tunnel endpoint for incoming packet + + \Input *pProtoTunnel - module state + \Input *pPacketData - packet data + \Input iRecvLen - packet size + \Input uRecvAddr - source addr of packet + \Input uRecvPort - source port of packet + + \Output + int32_t - matching tunnel index; iMaxTunnels means no match + + \Version 03/26/2015 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelFindTunnel(ProtoTunnelRefT *pProtoTunnel, uint8_t *pPacketData, int32_t iRecvLen, uint32_t uRecvAddr, uint16_t uRecvPort) +{ + int32_t iTunnel = pProtoTunnel->iMaxTunnels; + ProtoTunnelT *pTunnel = NULL; + uint32_t uClientId; + uint16_t uIdent = PROTOTUNNEL_GetIdentFromPacket(pPacketData); + + if (uIdent & PROTOTUNNEL_IDENT_HANDSHAKE) + { + ProtoTunnelHandshakeT *pHandshake = (ProtoTunnelHandshakeT *)pPacketData; + uClientId = pHandshake->Version.V1_1.aClientIdent[0] << 24; + uClientId |= pHandshake->Version.V1_1.aClientIdent[1] << 16; + uClientId |= pHandshake->Version.V1_1.aClientIdent[2] << 8; + uClientId |= pHandshake->Version.V1_1.aClientIdent[3]; + + // find tunnel with matching clientId + for (iTunnel = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel += 1) + { + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + if (pTunnel->Info.uRemoteClientId == uClientId) + { + if (pTunnel->uActive == 0) + { + // decrypt and validate packet + if (!_ProtoTunnelMatchTunnel(pProtoTunnel, pTunnel, pPacketData, iRecvLen)) + { + NetPrintf(("prototunnel: [%p][%04d] error; validate failed on match of uClientId=0x%08x to inactive tunnel\n", pProtoTunnel, iTunnel, uClientId)); + iTunnel = pProtoTunnel->iMaxTunnels; + } + } + else + { + NetPrintf(("prototunnel: [%p][%04d] warning; matching uClientId=0x%08x to active tunnel\n", pProtoTunnel, iTunnel, uClientId)); + } + break; + } + } + } + else if (uIdent < (unsigned)pProtoTunnel->iMaxTunnels) + { + // a non-handshake ident field contains the local tunnel index exchanged in handshaking + pTunnel = &pProtoTunnel->Tunnels[uIdent]; + if (pTunnel->uActive == 1) + { + // decrypt and validate packet + if (_ProtoTunnelMatchTunnel(pProtoTunnel, pTunnel, pPacketData, iRecvLen) != 0) + { + // found tunnel to match incoming data with + iTunnel = (signed)uIdent; + } + } + } + + // handle port change + if ((iTunnel < pProtoTunnel->iMaxTunnels) && (pTunnel != NULL) && (pTunnel->Info.uRemotePort != uRecvPort)) + { + NetPrintf(("prototunnel: [%p][%04d] detected port change; updating remote port from %d to %d\n", pProtoTunnel, iTunnel, pTunnel->Info.uRemotePort, uRecvPort)); + pTunnel->Info.uRemotePort = uRecvPort; + } + + // couldn't match? + if (iTunnel == pProtoTunnel->iMaxTunnels) + { + NetPrintf(("prototunnel: [%p] could not match packet from %a:%d (ident=0x%04x)\n", pProtoTunnel, uRecvAddr, uRecvPort, uIdent)); + } + return(iTunnel); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelRecvData + + \Description + Match incoming data with a tunnel, and if source address is mapped, + decode the number of subpackets and decrypt packet data. + + \Input *pProtoTunnel - module state + \Input *pRemotePortList - [out] storage for remote port list of matching tunnel + \Input iPortListSize - size of port list + \Input *pHeadOffset - [out] storage for sub-packet header data offset + \Input *pPacketData - source data + \Input iRecvLen - size of source data + \Input *pRecvAddr - source address + + \Output + int32_t - PROTOTUNNEL_PACKETRECVFAIL_*, else number of decoded packets + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelRecvData(ProtoTunnelRefT *pProtoTunnel, uint16_t *pRemotePortList, int32_t iPortListSize, uint32_t *pHeadOffset, uint8_t *pPacketData, int32_t iRecvLen, struct sockaddr *pRecvAddr) +{ + uint32_t uRecvAddr, uRecvPort; + int32_t iNumPackets, iTunnel; + ProtoTunnelT *pTunnel; + uint32_t uCurTick = NetTick(); + + // get exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // find tunnel with matching remote address, port, and mode + for (iTunnel = 0, uRecvAddr = SockaddrInGetAddr(pRecvAddr), uRecvPort = SockaddrInGetPort(pRecvAddr); iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++) + { + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + if ((pTunnel->Info.uRemoteAddr == uRecvAddr) && (pTunnel->Info.uRemotePort == uRecvPort)) + { + if (pTunnel->uActive == 0) + { + // decrypt packet and verify clientId matches + if (_ProtoTunnelMatchTunnel(pProtoTunnel, pTunnel, pPacketData, iRecvLen)) + { + // found tunnel to match incoming data with + break; + } + } + else + { + break; + } + } + } + + // if no matching tunnel found, use packet data to match to a tunnel + if (iTunnel == pProtoTunnel->iMaxTunnels) + { + iTunnel = _ProtoTunnelFindTunnel(pProtoTunnel, pPacketData, iRecvLen, uRecvAddr, uRecvPort); + } + + // did we find a tunnel? + if (iTunnel < pProtoTunnel->iMaxTunnels) + { + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + + // if the tunnel isn't active yet, bind to it and mark it as active + if (pTunnel->uActive == 0) + { + if ((pTunnel->Info.uRemoteAddr != uRecvAddr) || (pTunnel->Info.uRemotePort != uRecvPort)) + { + NetPrintf(("prototunnel: [%p][%04d] updating remote address from %a:%d to %a:%d\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), pTunnel->Info.uRemoteAddr, pTunnel->Info.uRemotePort, uRecvAddr, uRecvPort)); + } + + // update tunnel to addr/port combo + pTunnel->Info.uRemoteAddr = uRecvAddr; + pTunnel->Info.uRemotePort = uRecvPort; + + // mark that we're now active + pTunnel->uActive = 1; + + NetPrintf(("prototunnel: [%p][%04d] matched incoming data from %a:%d to tunnelId 0x%08x / clientId 0x%08x\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uRecvAddr, uRecvPort, pTunnel->uVirtualAddr, pTunnel->Info.uRemoteClientId)); + } + + // make a copy of port list for later use + ds_memcpy(pRemotePortList, pTunnel->Info.aRemotePortList, iPortListSize); + + // display recv info + NetPrintfVerbose((pProtoTunnel->iVerbosity, 2, "prototunnel: [%p][%04d] recv - received %d bytes on tunnel 0x%08x from %a:%d\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iRecvLen, pTunnel->uVirtualAddr, uRecvAddr, uRecvPort)); + + iNumPackets = _ProtoTunnelDecryptAndValidatePacket(pProtoTunnel, pTunnel, pHeadOffset, pPacketData, iRecvLen, TRUE); + + if (iNumPackets > 0) + { + ProtoTunnelControlT ControlInfo; + + // accumulate receive stats + pTunnel->RecvStat.uLastPacketTime = uCurTick; + pTunnel->RecvStat.uNumBytes += iRecvLen; + pTunnel->RecvStat.uNumSubpacketBytes += iRecvLen - (iNumPackets * PROTOTUNNEL_PKTHDRSIZE) - 2 - pProtoTunnel->uHmacSize; + pTunnel->RecvStat.uNumPackets += 1; + pTunnel->RecvStat.uNumSubpackets += iNumPackets; + pProtoTunnel->uNumSubPktsRecvd += (unsigned)iNumPackets; + + // process any control info + if (_ProtoTunnelGetControlInfo(pTunnel, &ControlInfo, pPacketData) != 0) + { + if (ControlInfo.uActive == 1) + { + NetPrintf(("prototunnel: [%p][%04d] connection is active; disabling control packets\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + pTunnel->bSendCtrlInfo = FALSE; + } + else if (!pTunnel->bSendCtrlInfo) + { + /* this path is typically exercised when one side of the connection ends up refcounting a tunnel and the other side + ends up destroying/recreating the tunnel because both consoles get Blaze messages with different timing. Under such + conditions, the prototunnel handshaking (sending of ctrl messages) needs to occur again. */ + NetPrintf(("prototunnel: [%p][%04d] peer no longer sees connection as active; resume sending control packets\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + pTunnel->bSendCtrlInfo = TRUE; + } + } + else if ((pTunnel->bSendCtrlInfo) && (pTunnel->uActive == 1)) + { + NetPrintf(("prototunnel: [%p][%04d] connection is active; disabling control packets\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + pTunnel->bSendCtrlInfo = FALSE; + } + + // rewrite address to virtual address + SockaddrInSetAddr(pRecvAddr, pTunnel->uVirtualAddr); + } + else if (iNumPackets == PROTOTUNNEL_PACKETRECVFAIL_VALIDATE) + { + /* attempt rematch with a different key and/or stream offset of zero; if rematch is successful we return TRYAGAIN, + as we need the caller to call us again following a successful rematch. if rematch fails here, we do not want to + try again, so we directly return the error code */ + NetPrintf(("prototunnel: [%p][%04d] attempting tunnel rematch after VALIDATE error\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + if (_ProtoTunnelMatchTunnel(pProtoTunnel, pTunnel, pPacketData, iRecvLen)) + { + NetPrintf(("prototunnel: [%p][%04d] tunnel rematch succeeded\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + iNumPackets = PROTOTUNNEL_PACKETRECVFAIL_TRYAGAIN; + } + else + { + NetPrintf(("prototunnel: [%p][%04d] tunnel rematch failed\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + } + } + else if (iNumPackets == PROTOTUNNEL_PACKETRECVFAIL_OUTOFORDER) + { + NetPrintf(("prototunnel: [%p][%04d] discarding out of order packet\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + pTunnel->SendStat.uNumDiscards += 1; + pProtoTunnel->uNumPktsDiscard += 1; + } + } + else + { + // tell calling code there is no matching tunnel + iNumPackets = PROTOTUNNEL_PACKETRECVFAIL_NOMATCH; + } + + // release exclusive access + NetCritLeave(&pProtoTunnel->TunnelsCritR); + + // return number of packets decoded + return(iNumPackets); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelRecv + + \Description + Callback to handle idle and recv callbacks on a tunnel socket. + + \Input *pProtoTunnel - module ref + \Input pPacketData - packet data + \Input iRecvLen - receive length + \Input *pRecvAddr - sender's address + + \Output + int32_t - zero + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelRecv(ProtoTunnelRefT *pProtoTunnel, uint8_t *pPacketData, int32_t iRecvLen, struct sockaddr *pRecvAddr) +{ + int32_t iPacket, iNumPackets; + uint16_t aRemotePortList[PROTOTUNNEL_MAXPORTS]; + uint8_t *pPacketHead; + uint32_t uHeadOffset; + + // find matching tunnel and decode packets + if ((iNumPackets = _ProtoTunnelRecvData(pProtoTunnel, aRemotePortList, sizeof(aRemotePortList), &uHeadOffset, pPacketData, iRecvLen, pRecvAddr)) == PROTOTUNNEL_PACKETRECVFAIL_TRYAGAIN) + { + // we were able to match a new key/stream offset; run it through again + iNumPackets = _ProtoTunnelRecvData(pProtoTunnel, aRemotePortList, sizeof(aRemotePortList), &uHeadOffset, pPacketData, iRecvLen, pRecvAddr); + } + + // if no packets + if (iNumPackets <= 0) + { + NetPrintf(("prototunnel: [%p] recv - received unhandled %d bytes from %a:%d (%d)\n", pProtoTunnel, iRecvLen, + SockaddrInGetAddr(pRecvAddr), SockaddrInGetPort(pRecvAddr), iNumPackets)); + + // if event callback is set, call it + if ((pProtoTunnel->pCallback != NULL) && (iNumPackets == PROTOTUNNEL_PACKETRECVFAIL_NOMATCH)) + { + pProtoTunnel->pCallback(pProtoTunnel, PROTOTUNNEL_EVENT_RECVNOMATCH, (char *)pPacketData, iRecvLen, pRecvAddr, pProtoTunnel->pUserData); + } + + // display recv info + if (pProtoTunnel->iVerbosity > 3) + { + NetPrintMem(pPacketData, (iRecvLen < 64) ? iRecvLen : 64, "prototunnel-recv-nomatch"); + } + return(0); + } + + // display decrypted packet data + #if DIRTYCODE_LOGGING + if (pProtoTunnel->iVerbosity > 3) + { + NetPrintMem(pPacketData, (iRecvLen < 64) ? iRecvLen : 64, "prototunnel-recv"); + } + #endif + + // demultiplex aggregate packet data and push into the appropriate sockets + for (iPacket = 0, pPacketHead = pPacketData+uHeadOffset, pPacketData = pPacketData+uHeadOffset+(iNumPackets*PROTOTUNNEL_PKTHDRSIZE)+pProtoTunnel->uHmacSize; iPacket < iNumPackets; iPacket++) + { + // extract size and port info + uint32_t uPktHead = (pPacketHead[0] << 8) | pPacketHead[1]; + uint32_t uPktSize = uPktHead >> 4; + uint32_t uPortIdx = uPktHead & 0xf; + uint32_t uPort = aRemotePortList[uPortIdx]; + + // if it is a control packet, skip it + if (uPortIdx != PROTOTUNNEL_CONTROL_PORTIDX) + { + SocketT *pSocket; + + // find SOCK_DGRAM socket bound to this port + if (SocketInfo(NULL, 'bndu', uPort, &pSocket, sizeof(pSocket)) == 0) + { + // rewrite port to match what socket will be expecting + SockaddrInSetPort(pRecvAddr, uPort); + + // display tunneled receive + NetPrintfVerbose((pProtoTunnel->iVerbosity, 2, "prototunnel: [%p] recv - %db->%d\n", pProtoTunnel, uPktSize, uPort)); + + // forward data on to caller + SocketControl(pSocket, 'push', uPktSize, pPacketData, pRecvAddr); + } + else + { + NetPrintf(("prototunnel: [%p] recv - warning, got data for port %d with no socket bound to that port\n", pProtoTunnel, uPort)); + } + } + else + { + NetPrintf(("prototunnel: [%p] recv - received control packet\n", pProtoTunnel)); + } + + // skip to next packet + pPacketHead += PROTOTUNNEL_PKTHDRSIZE; + pPacketData += uPktSize; + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelRecvCallback + + \Description + Callback to handle idle and recv callbacks on a tunnel socket. + + \Input *pSocket - socket ref + \Input iFlags - unused + \Input *_pRef - tunnel map ref + + \Output + int32_t - zero + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoTunnelRecvCallback(SocketT *pSocket, int32_t iFlags, void *_pRef) +{ + ProtoTunnelRefT *pProtoTunnel = (ProtoTunnelRefT *)_pRef; + struct sockaddr RecvAddr; + int32_t iRecvAddrLen = sizeof(RecvAddr), iRecv, iRecvLen; + uint8_t aPacketData[PROTOTUNNEL_PACKETBUFFER]; + + SockaddrInit(&RecvAddr, AF_INET); + + // got any input? + for (iRecv = 0; iRecv < pProtoTunnel->iMaxRecv; iRecv += 1) + { + if ((iRecvLen = SocketRecvfrom(pSocket, (char *)aPacketData, sizeof(aPacketData), 0, &RecvAddr, &iRecvAddrLen)) <= 0) + { + break; + } + + // drop packet for easy testing of lost packet recovery + #if DIRTYCODE_DEBUG + if (pProtoTunnel->iPacketDrop > 0) + { + pProtoTunnel->iPacketDrop -= 1; + NetPrintf(("prototunnel: [%p] dropping received packet\n", pProtoTunnel)); + continue; + } + #endif + + // forward to any registered raw inbound data filter + if (pProtoTunnel->pRawRecvCallback) + { + if (pProtoTunnel->pRawRecvCallback(pSocket, aPacketData, iRecvLen, &RecvAddr, sizeof(RecvAddr), pProtoTunnel->pRawRecvUserData) > 0) + { + // inbound raw data was swallowed by the filter, this packet is to be ignored by ProtoTunnel + continue; + } + } + + // process the packet + _ProtoTunnelRecv(pProtoTunnel, aPacketData, iRecvLen, &RecvAddr); + } + + // update stats + pProtoTunnel->uNumRecvCalls += iRecv > 0; + pProtoTunnel->uNumPktsRecvd += (unsigned)iRecv; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelSocketConfig + + \Description + Configure tunnel socket + + \Input *pProtoTunnel - pointer to module state + \Input *pSocket - socket to configure + + \Output + uint32_t - local port socket is bound to + + \Version 09/18/2013 (jbrookes) split from _ProtoTunnelSocketOpen +*/ +/********************************************************************************F*/ +static uint32_t _ProtoTunnelSocketConfig(ProtoTunnelRefT *pProtoTunnel, SocketT *pSocket) +{ + struct sockaddr BindAddr; + uint32_t uPort; + + // retrieve bound port + SocketInfo(pSocket, 'bind', 0, &BindAddr, sizeof(BindAddr)); + + // reference local port + uPort = SockaddrInGetPort(&BindAddr); + NetPrintf(("prototunnel: [%p] bound socket to port %d\n", pProtoTunnel, uPort)); + + // set up for socket callback events + SocketCallback(pSocket, CALLB_RECV, pProtoTunnel->iIdleCbRate, pProtoTunnel, &_ProtoTunnelRecvCallback); + + return(uPort); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelSocketOpen + + \Description + Open a tunnel socket + + \Input *pProtoTunnel - pointer to module state + \Input iPort - port tunnel will go over + + \Output + SocketT * - new socket or NULL on failure + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static SocketT *_ProtoTunnelSocketOpen(ProtoTunnelRefT *pProtoTunnel, int32_t iPort) +{ + struct sockaddr BindAddr; + SocketT *pSocket; + int32_t iResult; + + // open the socket + if ((pSocket = SocketOpen(AF_INET, SOCK_DGRAM, PROTOTUNNEL_IPPROTO)) == NULL) + { + NetPrintf(("prototunnel: [%p] unable to open socket\n", pProtoTunnel)); + return(NULL); + } + + // bind socket to specified port + SockaddrInit(&BindAddr, AF_INET); + SockaddrInSetPort(&BindAddr, iPort); + if ((iResult = SocketBind(pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE) + { + if (_ProtoTunnel_bRetryRandomOnFailure) + { + NetPrintf(("prototunnel: [%p] error %d binding to port %d, trying random\n", pProtoTunnel, iResult, iPort)); + SockaddrInSetPort(&BindAddr, 0); + if ((iResult = SocketBind(pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE) + { + NetPrintf(("prototunnel: [%p] error %d binding to port\n", pProtoTunnel, iResult)); + SocketClose(pSocket); + return(NULL); + } + } + else + { + NetPrintf(("prototunnel: [%p] error %d binding to port %d\n", pProtoTunnel, iResult, iPort)); + SocketClose(pSocket); + return(NULL); + } + } + + // return socket to caller + return(pSocket); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoTunnelStatCalc + + \Description + Calculates ProtoTunnel Stats + + \Input *pTunnel - Tunnel that calculations are to be performed on. + \Input iSelect - 'rcvs' for receive stats and 'snds' for send stats + + \Version 09/17/2014 (tcho) +*/ +/********************************************************************************F*/ +static void _ProtoTunnelStatCalc(ProtoTunnelT *pTunnel, int32_t iSelect) +{ + uint32_t uCurTick = NetTick(); + uint32_t uRange = 0; + + if (iSelect == 'rcvs') + { + if (pTunnel->RecvStat.uUpdateTime == 0) + { + pTunnel->RecvStat.uBytePerSecond = 0; + pTunnel->RecvStat.uRawBytesPerSecond = 0; + pTunnel->RecvStat.uEfficiency = 0; + } + else + { + uRange = NetTickDiff(uCurTick, pTunnel->RecvStat.uUpdateTime); + pTunnel->RecvStat.uRawBytesPerSecond = (pTunnel->RecvStat.uNumBytes - pTunnel->uLastRecvNumBytes) * 1000 / uRange; + pTunnel->RecvStat.uBytePerSecond = (pTunnel->RecvStat.uNumSubpacketBytes - pTunnel->uLastRecvNumSubpacketBytes) * 1000 / uRange; + pTunnel->RecvStat.uEfficiency = (uint32_t)(((float)pTunnel->RecvStat.uBytePerSecond/(float)pTunnel->RecvStat.uRawBytesPerSecond) * 100.0f); + } + + // update tracking variable + pTunnel->RecvStat.uPrevUpdateTime = pTunnel->RecvStat.uUpdateTime; + pTunnel->RecvStat.uUpdateTime = uCurTick; + pTunnel->uLastRecvNumBytes = pTunnel->RecvStat.uNumBytes; + pTunnel->uLastRecvNumSubpacketBytes = pTunnel->RecvStat.uNumSubpacketBytes; + } + else if (iSelect == 'snds') + { + if (pTunnel->SendStat.uUpdateTime == 0) + { + pTunnel->SendStat.uBytePerSecond = 0; + pTunnel->SendStat.uRawBytesPerSecond = 0; + pTunnel->SendStat.uEfficiency = 0; + } + else + { + uRange = NetTickDiff(uCurTick, pTunnel->SendStat.uUpdateTime); + pTunnel->SendStat.uRawBytesPerSecond = (pTunnel->SendStat.uNumBytes - pTunnel->uLastSendNumBytes) * 1000 / uRange; + pTunnel->SendStat.uBytePerSecond = (pTunnel->SendStat.uNumSubpacketBytes - pTunnel->uLastSendNumSubpacketBytes) * 1000 / uRange; + pTunnel->SendStat.uEfficiency = (uint32_t)(((float)pTunnel->SendStat.uBytePerSecond/(float)pTunnel->SendStat.uRawBytesPerSecond) * 100.0f); + } + + // update tracking variable + pTunnel->SendStat.uPrevUpdateTime = pTunnel->SendStat.uUpdateTime; + pTunnel->SendStat.uUpdateTime = uCurTick; + pTunnel->uLastSendNumBytes = pTunnel->SendStat.uNumBytes; + pTunnel->uLastSendNumSubpacketBytes = pTunnel->SendStat.uNumSubpacketBytes; + } + +} +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelCreate + + \Description + Create the ProtoTunnel module. + + \Input iMaxTunnels - maximum number of tunnels module can allocate + \Input iTunnelPort - local port for socket all tunnels will use + + \Output + ProtoTunnelRefT * - pointer to new module, or NULL + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoTunnelRefT *ProtoTunnelCreate(int32_t iMaxTunnels, int32_t iTunnelPort) +{ + ProtoTunnelRefT *pProtoTunnel; + int32_t iRefSize = sizeof(*pProtoTunnel) + ((iMaxTunnels-1) * sizeof(ProtoTunnelT)); + int32_t iMemGroup; + void *pMemGroupUserData; + + // maximum of 32k tunnels due to size of ident field + if (iMaxTunnels > 32767) + { + NetPrintf(("prototunnel: clamping requested %d tunnels to max of 32767\n", iMaxTunnels)); + iMaxTunnels = 32767; + } + + // query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pProtoTunnel = DirtyMemAlloc(iRefSize, PROTOTUNNEL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("prototunnel: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pProtoTunnel, iRefSize); + pProtoTunnel->iMemGroup = iMemGroup; + pProtoTunnel->pMemGroupUserData = pMemGroupUserData; + pProtoTunnel->iMaxTunnels = iMaxTunnels; + pProtoTunnel->iMaxRecv = 64; + pProtoTunnel->iIdleCbRate = 100; + pProtoTunnel->uTunnelPort = iTunnelPort; + pProtoTunnel->uHmacType = CRYPTHASH_MURMUR3; + pProtoTunnel->uHmacSize = PROTOTUNNEL_HMAC_DEFSIZE; + pProtoTunnel->uVersion = PROTOTUNNEL_VERSION; + + if (_ProtoTunnelGetBaseAddress(pProtoTunnel) != 0) + { + DirtyMemFree(pProtoTunnel, PROTOTUNNEL_MEMID, pProtoTunnel->iMemGroup, pProtoTunnel->pMemGroupUserData); + return(NULL); + } + + // create the tunnel socket + if ((pProtoTunnel->pSocket = _ProtoTunnelSocketOpen(pProtoTunnel, iTunnelPort)) == NULL) + { + _ProtoTunnelReleaseBaseAddress(pProtoTunnel); + DirtyMemFree(pProtoTunnel, PROTOTUNNEL_MEMID, pProtoTunnel->iMemGroup, pProtoTunnel->pMemGroupUserData); + return(NULL); + } + pProtoTunnel->uTunnelPort = _ProtoTunnelSocketConfig(pProtoTunnel, pProtoTunnel->pSocket); + + // initialize critical sections + NetCritInit(&pProtoTunnel->TunnelsCritS, "prototunnel-global-send"); + NetCritInit(&pProtoTunnel->TunnelsCritR, "prototunnel-global-recv"); + + // restrict max udp packet size + SocketControl(NULL, 'maxp', PROTOTUNNEL_MAXPACKET, NULL, NULL); + + // hook into global socket send hook + SocketControl(NULL, 'sdcb', TRUE, (void *)_ProtoTunnelSendCallback, pProtoTunnel); + + // configure prototunnel socket to NOT call socket send callbacks + SocketControl(pProtoTunnel->pSocket, 'scbk', FALSE, NULL, NULL); + + // return ref to caller + return(pProtoTunnel); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelDestroy + + \Description + Destroy the ProtoTunnel module. + + \Input *pProtoTunnel - pointer to module state + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoTunnelDestroy(ProtoTunnelRefT *pProtoTunnel) +{ + // clear global socket send hook + SocketControl(NULL, 'sdcb', FALSE, (void *)_ProtoTunnelSendCallback, pProtoTunnel); + + // close tunnel socket + if (pProtoTunnel->pSocket != NULL) + { + SocketClose(pProtoTunnel->pSocket); + } + + // close server socket, if allocated + if (pProtoTunnel->pServerSocket != NULL) + { + SocketClose(pProtoTunnel->pServerSocket); + } + + // dispose of critical sections + NetCritKill(&pProtoTunnel->TunnelsCritR); + NetCritKill(&pProtoTunnel->TunnelsCritS); + + _ProtoTunnelReleaseBaseAddress(pProtoTunnel); + + // dispose of module memory + DirtyMemFree(pProtoTunnel, PROTOTUNNEL_MEMID, pProtoTunnel->iMemGroup, pProtoTunnel->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelCallback + + \Description + Set event callback + + \Input *pProtoTunnel - pointer to module state + \Input *pCallback - callback pointer + \Input *pUserData - callback data + + \Version 03/24/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoTunnelCallback(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelCallbackT *pCallback, void *pUserData) +{ + pProtoTunnel->pCallback = pCallback; + pProtoTunnel->pUserData = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelAlloc + + \Description + Allocate a tunnel. + + \Input *pProtoTunnel - pointer to module state + \Input *pInfo - tunnel info + \Input *pKey - encryption key for tunnel + + \Output + int32_t - negative=error, else allocated tunnel id + + \Version 12/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoTunnelAlloc(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelInfoT *pInfo, const char *pKey) +{ + ProtoTunnelT *pTunnel; + int32_t iTunnel; + + // get exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // see if we already have a tunnel with this clientId + for (iTunnel = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++) + { + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + if (pTunnel->Info.uRemoteClientId == pInfo->uRemoteClientId) + { + int32_t iKey, iKeySlot, iResult = (signed)pTunnel->uVirtualAddr; + + // refcount the tunnel + NetPrintf(("prototunnel: [%p][%04d] refcounting tunnel with id=0x%08x key=%s clientId=0x%08x remote address=%a\n", + pProtoTunnel, iTunnel, pTunnel->uVirtualAddr, pKey, pInfo->uRemoteClientId, pInfo->uRemoteAddr)); + pTunnel->uRefCount += 1; + + // append key to key list + for (iKey = 0, iKeySlot = -1; iKey < PROTOTUNNEL_MAXKEYS; iKey++) + { + #if DIRTYCODE_LOGGING + if (!strcmp(pKey, pTunnel->aKeyList[iKey])) + { + NetPrintf(("prototunnel: [%p][%04d] warning - duplicate key %s on alloc\n", pProtoTunnel, iTunnel, pKey)); + } + #endif + if ((iKeySlot == -1) && (pTunnel->aKeyList[iKey][0] == '\0')) + { + iKeySlot = iKey; + } + } + if (iKeySlot != -1) + { + ds_strnzcpy(pTunnel->aKeyList[iKeySlot], pKey, sizeof(pTunnel->aKeyList[iKeySlot])); + } + else + { + NetPrintf(("prototunnel: [%p][%04d] error - key overflow on alloc\n", pProtoTunnel, iTunnel)); + iResult = -1; + } + + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + return(iResult); + } + } + + // find an unallocated tunnel + for (iTunnel = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++) + { + if (pProtoTunnel->Tunnels[iTunnel].uVirtualAddr == 0) + { + break; + } + } + // make sure we found room + if (iTunnel == pProtoTunnel->iMaxTunnels) + { + NetPrintf(("prototunnel: [%p] could not allocate a new tunnel\n", pProtoTunnel)); + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + return(-1); + } + + // ref and init tunnel + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + ds_memclr(pTunnel, sizeof(*pTunnel)); + ds_memcpy_s(&pTunnel->Info, sizeof(pTunnel->Info), pInfo, sizeof(*pInfo)); + NetCritInit(&pTunnel->PacketCrit, "prototunnel-tunnel"); + ds_strnzcpy(pTunnel->aKeyList[0], pKey, sizeof(pTunnel->aKeyList[0])); + pTunnel->uRefCount = 1; + + pTunnel->uLastTunnelSend = NetTick(); + + // init tunnel crypto state + _ProtoTunnelCryptSetup(pTunnel, 0, TRUE); + + // port index seven is reserved by ProtoTunnel and always encrypted + pTunnel->Info.aPortFlags[PROTOTUNNEL_CONTROL_PORTIDX] = PROTOTUNNEL_PORTFLAG_ENCRYPTED; + + // initialize to send connect info + pTunnel->bSendCtrlInfo = TRUE; + + // assign a virtual address/id + pTunnel->uVirtualAddr = pProtoTunnel->uVirtualAddr++; + + // if unspecified, set remote port + if (pTunnel->Info.uRemotePort == 0) + { + pTunnel->Info.uRemotePort = pProtoTunnel->uTunnelPort; + } + + // set default tunnel protocol version + _ProtoTunnelSetVersion(pProtoTunnel, pTunnel, pProtoTunnel->uVersion); + + #if DIRTYCODE_LOGGING + pTunnel->uLastStatUpdate = NetTick(); + #endif + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + // debug spam + #if DIRTYCODE_LOGGING + { + int32_t iPort; + NetPrintf(("prototunnel: [%p][%04d] creating map to remote client %a:%d (id=0x%08x)\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), pTunnel->Info.uRemoteAddr, pTunnel->Info.uRemotePort, pTunnel->Info.uRemoteClientId)); + for (iPort = 0; (iPort < PROTOTUNNEL_MAXPORTS) && (pTunnel->Info.aRemotePortList[iPort] != 0); iPort++) + { + NetPrintf(("prototunnel: [%p][%04d] [%d] %d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iPort, pTunnel->Info.aRemotePortList[iPort])); + } + } + #endif + + // return addr/id to caller + NetPrintf(("prototunnel: [%p][%04d] allocated tunnel with id 0x%08x key=%s\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), pTunnel->uVirtualAddr, pTunnel->aKeyList[0])); + return(pTunnel->uVirtualAddr); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelFree + + \Description + Free a tunnel. + + \Input *pProtoTunnel - pointer to module state + \Input uTunnelId - id of tunnel to free + \Input *pKey - key tunnel was allocated with + + \Notes + pKey is only required when tunnel refcounting is used. Otherwise, pKey + may be specified as NULL. + + \Version 12/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +uint32_t ProtoTunnelFree(ProtoTunnelRefT *pProtoTunnel, uint32_t uTunnelId, const char *pKey) +{ + return(ProtoTunnelFree2(pProtoTunnel, uTunnelId, pKey, 0)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelFree2 + + \Description + Free a tunnel. Same as ProtoTunnelFree but takes an IP address that is + no longer used. Consider for deprecation in the future. + + \Input *pProtoTunnel - pointer to module state + \Input uTunnelId - id of tunnel to free + \Input *pKey - key tunnel was allocated with + \Input uAddr - address of peer that is being freed + + \Notes + pKey is only required when tunnel refcounting is used. Otherwise, pKey + may be specified as NULL. + + \Version 03/15/2010 (jrainy) +*/ +/********************************************************************************F*/ +uint32_t ProtoTunnelFree2(ProtoTunnelRefT *pProtoTunnel, uint32_t uTunnelId, const char *pKey, uint32_t uAddr) +{ + ProtoTunnelT *pTunnel; + int32_t iTunnel; + uint32_t uRet = (uint32_t)-1; + + #if DIRTYCODE_LOGGING + uint32_t bFound = FALSE; + #endif + + // get exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // find tunnel id + for (iTunnel = 0, pTunnel = &pProtoTunnel->Tunnels[0]; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++, pTunnel++) + { + // found it? + if (pTunnel->uVirtualAddr == uTunnelId) + { + #if DIRTYCODE_LOGGING + bFound = TRUE; + #endif + + // deallocate the tunnel + if (pTunnel->uRefCount == 1) + { + NetPrintf(("prototunnel: [%p][%04d] freeing tunnel 0x%08x\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uTunnelId)); + + // flush buffer before destroy + _ProtoTunnelBufferSend(pProtoTunnel, pTunnel, NetTick()); + + // dispose of critical section + NetCritKill(&pTunnel->PacketCrit); + + ds_memclr(pTunnel, sizeof(*pTunnel)); + + uRet = 0; + } + else + { + int32_t iKey; + + NetPrintf(("prototunnel: [%p][%04d] decrementing refcount of tunnel 0x%08x (clientId=0x%08x)\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uTunnelId, pTunnel->Info.uRemoteClientId)); + + // remove key from key list + for (iKey = 0; iKey < PROTOTUNNEL_MAXKEYS; iKey++) + { + if (!strcmp(pKey, pTunnel->aKeyList[iKey])) + { + ds_memclr(pTunnel->aKeyList[iKey], sizeof(pTunnel->aKeyList[iKey])); + break; + } + } + + // did we blow away our send key? + if (pTunnel->uSendKey == (uint8_t)iKey) + { + NetPrintf(("prototunnel: [%p][%04d] free of current send key; picking another\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels))); + for (iKey = 0; iKey < PROTOTUNNEL_MAXKEYS; iKey++) + { + if (pTunnel->aKeyList[iKey][0] != '\0') + { + NetPrintf(("prototunnel: [%p][%04d] picking key %d (%s) offset=%d\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iKey, pTunnel->aKeyList[iKey], pTunnel->uSendOffset)); + // init tunnel crypto state for new key + _ProtoTunnelCryptSetup(pTunnel, iKey, FALSE); + break; + } + } + } + else if (iKey == PROTOTUNNEL_MAXKEYS) + { + NetPrintf(("prototunnel: [%p][%04d] could not find key %s in key list on free\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), pKey)); + } + + pTunnel->uRefCount -= 1; + uRet = pTunnel->uRefCount; + + NetPrintf(("prototunnel: [%p][%04d] refcounting down tunnel with id=0x%08x key=%s clientId=0x%08x remote address=%a\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), pTunnel->uVirtualAddr, pKey, pTunnel->Info.uRemoteClientId, pTunnel->Info.uRemoteAddr)); + } + + // done + break; + } + } + + #if DIRTYCODE_LOGGING + if (bFound == FALSE) + { + NetPrintf(("prototunnel: [%p][%04d] could not find tunnel with id 0x%08x to free\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), uTunnelId)); + } + #endif + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + return(uRet); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelUpdatePortList + + \Description + Updates the port list for the given tunnel. Port and port flag info is + copied over from the specified info structure if the port value is non- + zero. + + \Input *pProtoTunnel - pointer to module state + \Input uTunnelId - id of tunnel to update port mapping for + \Input *pInfo - structure containing updated port info + + \Output + int32_t - zero=success, else could not find tunnel with given id + + \Version 06/12/2008 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoTunnelUpdatePortList(ProtoTunnelRefT *pProtoTunnel, uint32_t uTunnelId, ProtoTunnelInfoT *pInfo) +{ + ProtoTunnelT *pTunnel; + int32_t iTunnel, iResult = -1; + int32_t iPort; + + // get exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // find tunnel id + for (iTunnel = 0, pTunnel = &pProtoTunnel->Tunnels[0]; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++, pTunnel++) + { + // found it? + if (pTunnel->uVirtualAddr == uTunnelId) + { + // copy portlist items that should be updated + for (iPort = 0; iPort < PROTOTUNNEL_MAXPORTS; iPort += 1) + { + if (pInfo->aRemotePortList[iPort] != 0) + { + NetPrintf(("prototunnel: [%p][%04d] updating port mapping %d for tunnel=0x%08x from (%d,%d) to (%d,%d)\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), iPort, uTunnelId, + pTunnel->Info.aRemotePortList[iPort], pTunnel->Info.aPortFlags[iPort], + pInfo->aRemotePortList[iPort], pInfo->aPortFlags[iPort])); + pTunnel->Info.aRemotePortList[iPort] = pInfo->aRemotePortList[iPort]; + pTunnel->Info.aPortFlags[iPort] = pInfo->aPortFlags[iPort]; + } + } + iResult = 0; + break; + } + } + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + // return result code to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelValidatePacket + + \Description + Validate key against packet, a tunnel has not been allocated yet + + \Input *pProtoTunnel- pointer to module state + \Input *pTunnel - pointer to tunnel + \Input *pOutputData - [out] pointer to buffer to store decrypted data (may be NULL) + \Input *pPacketData - pointer to tunnel packet + \Input iPacketSize - size of tunnel packet + \Input *pKey - encryption key for tunnel + + \Output + int32_t - PROTOTUNNEL_PACKETRECVFAIL_* on error, else number of subpackets decoded + + \Version 06/06/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoTunnelValidatePacket(ProtoTunnelRefT *pProtoTunnel, ProtoTunnelT *pTunnel, uint8_t *pOutputData, const uint8_t *pPacketData, int32_t iPacketSize, const char *pKey) +{ + uint8_t aPacketData[PROTOTUNNEL_PACKETBUFFER]; + int32_t iNumPackets; + uint32_t uHeadOffset; + + CryptArc4T CryptTemp; + uint32_t uRecvOffsetTemp; + uint8_t aHmacKeyTemp[64]; + + // copy packet data to temp buffer + ds_memcpy_s(aPacketData, sizeof(aPacketData), pPacketData, iPacketSize); + + // save current hmac key + ds_memcpy_s(aHmacKeyTemp, sizeof(aHmacKeyTemp), pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey)); + + // init HMAC key by running IV through RC4 initialized with tunnel key + CryptArc4Init(&CryptTemp, (const unsigned char *)pKey, (int32_t)strlen(pKey), PROTOTUNNEL_CRYPTARC4_ITER); + ds_memcpy_s(pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey), _ProtoTunnel_aHmacInitVec, sizeof(_ProtoTunnel_aHmacInitVec)); + CryptArc4Apply(&CryptTemp, (uint8_t *)pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey)); + + // save current recv state + ds_memcpy_s(&CryptTemp, sizeof(CryptTemp), &pTunnel->CryptRecvState, sizeof(pTunnel->CryptRecvState)); + uRecvOffsetTemp = pTunnel->uRecvOffset; + + // init recv state with specified key & reset offset + CryptArc4Init(&pTunnel->CryptRecvState, (unsigned char *)pKey, (int32_t)strlen(pKey), PROTOTUNNEL_CRYPTARC4_ITER); + pTunnel->uRecvOffset = 0; + + // decrypt and validate packet data + iNumPackets = _ProtoTunnelDecryptAndValidatePacket(pProtoTunnel, pTunnel, &uHeadOffset, aPacketData, iPacketSize, FALSE); + + // restore previous recv state + ds_memcpy(&pTunnel->CryptRecvState, &CryptTemp, sizeof(CryptTemp)); + pTunnel->uRecvOffset = uRecvOffsetTemp; + + // restore previous HMAC key + ds_memcpy_s(pTunnel->aHmacKey, sizeof(pTunnel->aHmacKey), aHmacKeyTemp, sizeof(aHmacKeyTemp)); + + // copy decrypted data to output buffer, if available + if ((iNumPackets > 0) && (pOutputData != NULL)) + { + ds_memcpy_s(pOutputData, iPacketSize, aPacketData, sizeof(aPacketData)); + } + + // return number of packets decoded + return(iNumPackets); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelStatus + + \Description + Get module status + + \Input *pProtoTunnel - pointer to module state + \Input iSelect - status selector + \Input iValue - selector specific + \Input *pBuf - [out] - selector specific + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'actv' - return number of active tunnels based on version (via iValue) + 'bnds' - return if the specified port (iValue) is part of the server port list + 'dpkt' - return number of out-of-order packet discards + 'hmac' - return HMAC type/size + 'idle' - return idle callback rate + 'lprt' - return local socket port + 'maxp' - return maximum size of packet that can be tunneled (NOTE: pProtoTunnel can be NULL) + 'rcvs' - copy receive statistics to pBuf + 'rcal' - return number of recv calls + 'rmax' - return maximum number of recv calls allowed in _ProtoTunnelRecvCallback + 'rprt' - remote game port for the specificed tunnel id + 'rsub' - return number of sub-packets received + 'rtot' - return number of packets received + 'snds' - copy send statistics to pBuf + 'sock' - copy socket ref to pBuf + 'vers' - return protocol version used with this tunnel id (specified with iValue) + 'vset' - return set of supported versions as a comma delimited string (via pBuf) + 'vtop' - convert virtual address to physical address (pBuf == sockaddr, optional) + \endverbatim + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoTunnelStatus(ProtoTunnelRefT *pProtoTunnel, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + if (iSelect == 'actv') + { + int32_t iTunnel; + int32_t iNumActive = 0; + + // acquire exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // get active tunnels by version + for (iTunnel = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel += 1) + { + const ProtoTunnelT *pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + if (pTunnel->uActive == 0) + { + continue; + } + if (pTunnel->Info.uTunnelVers == (uint16_t)iValue) + { + iNumActive += 1; + } + } + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + return(iNumActive); + } + if (iSelect == 'bnds') + { + return(_ProtoTunnelServerPortListFunc(pProtoTunnel, (uint16_t)iValue, PROTOTUNNEL_PORTLIST_CHK)); + } + if (iSelect == 'dpkt') + { + return(pProtoTunnel->uNumPktsDiscard); + } + if (iSelect == 'hmac') + { + return((pProtoTunnel->uHmacType << 4) | pProtoTunnel->uHmacSize); + } + if (iSelect == 'idle') + { + return(pProtoTunnel->iIdleCbRate); + } + if (iSelect == 'lprt') + { + return(pProtoTunnel->uTunnelPort); + } + if (iSelect == 'maxp') + { + return(PROTOTUNNEL_MAXPACKET); + } + if (iSelect == 'rcal') + { + return(pProtoTunnel->uNumRecvCalls); + } + if (iSelect == 'rmax') + { + return(pProtoTunnel->iMaxRecv); + } + if (iSelect == 'rprt') + { + if ((pBuf != NULL) && (iBufSize == sizeof(uint16_t))) + { + int32_t iTunnel = _ProtoTunnelIndexFromId(pProtoTunnel, (uint32_t)iValue); + if (iTunnel != -1) + { + *(uint16_t *)pBuf = pProtoTunnel->Tunnels[iTunnel].Info.uRemotePort; + return(0); + } + else + { + NetPrintf(("prototunnel: [%p] 'rprt' status selector called with unknown tunnelid=0x%08x\n", pProtoTunnel, iValue)); + return(-1); + } + } + } + if (iSelect == 'rsub') + { + return(pProtoTunnel->uNumSubPktsRecvd); + } + if (iSelect == 'rtot') + { + return(pProtoTunnel->uNumPktsRecvd); + } + if (iSelect == 'rcvs') + { + if ((pBuf != NULL) && (iBufSize == sizeof(ProtoTunnelStatT)) && (iValue < pProtoTunnel->iMaxTunnels)) + { + _ProtoTunnelStatCalc(&pProtoTunnel->Tunnels[iValue], iSelect); + ds_memcpy(pBuf, &pProtoTunnel->Tunnels[iValue].RecvStat, sizeof(ProtoTunnelStatT)); + return(0); + } + } + if (iSelect == 'snds') + { + if ((pBuf != NULL) && (iBufSize == sizeof(ProtoTunnelStatT)) && (iValue < pProtoTunnel->iMaxTunnels)) + { + _ProtoTunnelStatCalc(&pProtoTunnel->Tunnels[iValue], iSelect); + ds_memcpy(pBuf, &pProtoTunnel->Tunnels[iValue].SendStat, sizeof(ProtoTunnelStatT)); + return(0); + } + } + if (iSelect == 'sock') + { + ds_memcpy(pBuf, &pProtoTunnel->pSocket, iBufSize); + return(0); + } + if (iSelect == 'vers') + { + int32_t iResult = -1; + int32_t iTunnel = _ProtoTunnelIndexFromId(pProtoTunnel, (uint32_t)iValue); + if (iTunnel != -1) + { + iResult = (int32_t)pProtoTunnel->Tunnels[iTunnel].Info.uTunnelVers; + } + else + { + NetPrintf(("prototunnel: [%p] 'vers' status selector called with unknown tunnelid=0x%08x\n", pProtoTunnel, iValue)); + } + return(iResult); + } + if (iSelect == 'vset') + { + if (pBuf != NULL && iBufSize > 0) + { + ds_strnzcpy(pBuf, PROTOTUNNEL_VERSIONS, iBufSize); + return(0); + } + } + if (iSelect == 'vtop') + { + return(_ProtoTunnelVirtualToPhysical(pProtoTunnel, iValue, pBuf, iBufSize)); + } + // selector not supported + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function ProtoTunnelControl + + \Description + Control the module + + \Input *pProtoTunnel - pointer to module state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector result + + \Notes + iControl can be one of the following: + + \verbatim + 'bind' - recreate tunnel socket bound to specified port (iValue is port to bind to) + 'bndr' - remove a server tunnel port mapping from list (iValue is port to remove) + 'bnds' - recreate server tunnel socket bound to ephemeral port (iValue is ignored, iValue2 is remote server port) + 'clid' - set local clientId for all tunnels + 'drop' - drop next iValue packets (debug only; used to test crypt recovery) + 'flsh' - flush the specified tunnelId + 'hmac' - set hmac type (iValue) and size (iValue2) + 'idle' - set idle callback rate in milliseconds (iValue; default is 100) + 'rand' - set behavior to retry random port on bind failure via iValue, call before ProtoTunnelCreate + 'rate' - set flush rate in milliseconds; defaults to 16ms + 'rmax' - set maximum receive calls per call to _ProtoTunnelRecvCallback (iValue; default is 64) + 'rprt' - set specified tunnel's remote port + 'rrcb' - set raw receive callback + 'rrud' - set raw receive user data + 'sock' - set socket ref + 'spam' - set verbosity level (debug only) + 'tcid' - set per-tunnel local clientId override for specific tunnel + \endverbatim + + Unrecognized selectors are passed through to SocketControl() + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoTunnelControl(ProtoTunnelRefT *pProtoTunnel, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue) +{ + if (iControl == 'bind') + { + SocketT *pOldSocket = NULL, *pNewSocket = NULL; + NetPrintf(("prototunnel: [%p] recreating tunnel socket bound to port %d\n", pProtoTunnel, iValue)); + + // early out if we already have socket bound to this port + if (pProtoTunnel->uTunnelPort == (unsigned)iValue) + { + NetPrintf(("prototunnel: [%p] already have socket bound to port %d\n", pProtoTunnel, pProtoTunnel->uTunnelPort)); + return(0); + } + + // recreate tunnel socket bound to new port + if ((pNewSocket = _ProtoTunnelSocketOpen(pProtoTunnel, iValue)) == NULL) + { + NetPrintf(("prototunnel: [%p] could not recreate tunnel socket\n", pProtoTunnel)); + return(-1); + } + + // acquire exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // save old tunnel socket and replace with new socket + if (pProtoTunnel->pSocket != NULL) + { + pOldSocket = pProtoTunnel->pSocket; + } + pProtoTunnel->pSocket = pNewSocket; + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + // reconfigure new tunnel socket + pProtoTunnel->uTunnelPort = _ProtoTunnelSocketConfig(pProtoTunnel, pProtoTunnel->pSocket); + + // close old socket + if (pOldSocket != NULL) + { + SocketClose(pOldSocket); + } + return(0); + } + if (iControl == 'bndr') + { + // del remote port from list + if (!_ProtoTunnelServerPortListFunc(pProtoTunnel, (uint16_t)iValue, PROTOTUNNEL_PORTLIST_DEL)) + { + NetPrintf(("prototunnel: [%p] could not del port %d from server port list\n", pProtoTunnel, iValue)); + } + return(0); + } + if (iControl == 'bnds') + { + if (pProtoTunnel->pServerSocket == NULL) + { + SocketT *pSocket = NULL; + + // create tunnel socket (binding to an ephemeral port) + NetPrintf(("prototunnel: [%p] creating server tunnel socket (binding to system selected ephemeral port) - uServerRemotePort = %d\n", pProtoTunnel, iValue)); + if ((pSocket = _ProtoTunnelSocketOpen(pProtoTunnel, 0)) == NULL) + { + NetPrintf(("prototunnel: [%p] could not create server tunnel socket\n", pProtoTunnel)); + return(-1); + } + + // acquire exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // set new socket + pProtoTunnel->pServerSocket = pSocket; + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + // reconfigure new tunnel socket + _ProtoTunnelSocketConfig(pProtoTunnel, pProtoTunnel->pServerSocket); + } + else + { + NetPrintf(("prototunnel: --- reusing server socket ---\n")); + } + + // add remote port to list + if (!_ProtoTunnelServerPortListFunc(pProtoTunnel, (uint16_t)iValue, PROTOTUNNEL_PORTLIST_ADD)) + { + NetPrintf(("prototunnel: [%p] could not add port to server port list\n", pProtoTunnel)); + } + return(0); + } + if (iControl == 'clid') + { + NetPrintf(("prototunnel: [%p] setting local clientId=0x%08x\n", pProtoTunnel, iValue)); + pProtoTunnel->uLocalClientId = iValue; + return(0); + } + #if DIRTYCODE_DEBUG + if (iControl == 'drop') + { + pProtoTunnel->iPacketDrop = iValue; + return(0); + } + #endif + if ((iControl == 'flsh') || (iControl == 'rprt')) + { + ProtoTunnelT *pTunnel; + int32_t iTunnel; + uint32_t uCurTick = NetTick(); + + // acquire exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // flush specified tunnel + for (iTunnel = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++) + { + pTunnel = &pProtoTunnel->Tunnels[iTunnel]; + if (pTunnel->uVirtualAddr == (unsigned)iValue) + { + if (iControl == 'flsh') + { + NetPrintf(("prototunnel: [%p][%04d] explicitly flushing tunnel 0x%08x\n", pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), pTunnel->uVirtualAddr)); + _ProtoTunnelBufferSend(pProtoTunnel, pTunnel, uCurTick); + } + if (iControl == 'rprt') + { + NetPrintf(("prototunnel: [%p][%04d] updating remote port for tunnel 0x%08x to %d\n", + pProtoTunnel, (pTunnel - pProtoTunnel->Tunnels), pTunnel->uVirtualAddr, iValue2)); + pTunnel->Info.uRemotePort = (unsigned)iValue2; + } + break; + } + } + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + + // if we didn't find the tunnel + if (iTunnel == pProtoTunnel->iMaxTunnels) + { + NetPrintf(("prototunnel: [%p] unable to find tunnel 0x%08x for '%c%c%c%c' operation\n", + pProtoTunnel, iValue, (uint8_t)(iControl>>24), (uint8_t)(iControl>>16), (uint8_t)(iControl>>8), (uint8_t)iControl)); + return(-1); + } + return(0); + } + if (iControl == 'hmac') + { + int32_t iHashSize; + // validate hmac type + if ((iValue <= CRYPTHASH_NULL) || (iValue >= CRYPTHASH_NUMHASHES)) + { + NetPrintf(("prototunnel: [%p] ignoring attempt to set invalid hmactype %d\n", pProtoTunnel, iValue)); + return(-1); + } + // validate hmac size + iHashSize = CryptHashGetSize((CryptHashTypeE)iValue); + if (iValue2 > iHashSize) + { + NetPrintf(("prototunnel: [%p] hmacsize %d is larger than max hmactype size %d; truncating\n", pProtoTunnel, iValue2, iHashSize)); + iValue2 = iHashSize; + } + if (iValue2 > PROTOTUNNEL_HMAC_MAXSIZE) + { + NetPrintf(("prototunnel: [%p] hmacsize %d is too large; truncating\n", pProtoTunnel, iValue2)); + iValue2 = PROTOTUNNEL_HMAC_MAXSIZE; + } + NetPrintf(("prototunnel: [%p] setting hmactype=%d and hmacsize=%d\n", pProtoTunnel, iValue, iValue2)); + pProtoTunnel->uHmacType = (uint8_t)iValue; + pProtoTunnel->uHmacSize = (uint8_t)iValue2; + return(0); + } + if (iControl == 'idle') + { + pProtoTunnel->iIdleCbRate = iValue; + SocketCallback(pProtoTunnel->pSocket, CALLB_RECV, pProtoTunnel->iIdleCbRate, pProtoTunnel, &_ProtoTunnelRecvCallback); + return(0); + } + if (iControl == 'rand') + { + NetPrintf(("prototunnel: setting retry random port on bind failure to %s\n", (uint8_t)iValue ? "TRUE" : "FALSE")); + _ProtoTunnel_bRetryRandomOnFailure = (uint8_t)iValue; + return(0); + } + if (iControl == 'rate') + { + pProtoTunnel->uFlushRate = iValue; + return(0); + } + if (iControl == 'rmax') + { + pProtoTunnel->iMaxRecv = iValue; + return(0); + } + if (iControl == 'rrcb') + { + NetPrintf(("prototunnel: [%p] 'rrcb' selector used to change raw recv callback from %p to %p\n", pProtoTunnel, pProtoTunnel->pRawRecvCallback, pValue)); + pProtoTunnel->pRawRecvCallback = (RawRecvCallbackT *)pValue; + return(0); + } + if (iControl == 'rrud') + { + NetPrintf(("prototunnel: [%p] 'rrud' selector used to change raw recv callback user data from %p to %p\n", pProtoTunnel, pProtoTunnel->pRawRecvUserData, pValue)); + pProtoTunnel->pRawRecvUserData = (void *)pValue; + return(0); + } + if (iControl == 'sock') + { + NetPrintf(("prototunnel: [%p] replacing tunnel socket\n", pProtoTunnel)); + + // acquire exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // close current tunnel socket + if (pProtoTunnel->pSocket != NULL) + { + SocketClose(pProtoTunnel->pSocket); + pProtoTunnel->pSocket = NULL; + } + // write in the new socket and configure it + pProtoTunnel->pSocket = (SocketT *)pValue; + _ProtoTunnelSocketConfig(pProtoTunnel, pProtoTunnel->pSocket); + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); + return(0); + } + if (iControl == 'spam') + { + pProtoTunnel->iVerbosity = iValue; + return(0); + } + if (iControl == 'tcid') + { + int32_t iTunnel = _ProtoTunnelIndexFromId(pProtoTunnel, (uint32_t)iValue); + if (iTunnel != -1) + { + NetPrintf(("prototunnel: [%p][%04d] changing local client id from 0x%08x to 0x%08x\n", pProtoTunnel, iTunnel, pProtoTunnel->Tunnels[iTunnel].uLocalClientId, iValue2)); + pProtoTunnel->Tunnels[iTunnel].uLocalClientId = (uint32_t)iValue2; + return(0); + } + else + { + NetPrintf(("prototunnel: [%p] 'tcid' control selector called with unknown tunnelid=0x%08x\n", pProtoTunnel, iValue)); + return(-1); + } + } + if (iControl == 'vers') + { + if (iValue < PROTOTUNNEL_VERSION_MIN) + { + NetPrintf(("prototunnel [%p] ignoring attempt to set invalid version %d.%d\n", pProtoTunnel, iValue>>8, iValue&0xff)); + return(-1); + } + NetPrintf(("prototunnel: [%p] setting version to %d.%d\n", pProtoTunnel, iValue>>8, iValue&0xff)); + pProtoTunnel->uVersion = (uint16_t)iValue; + return(0); + } + // pass-through to SocketControl() + return(SocketControl(pProtoTunnel->pSocket, iControl, iValue, NULL, NULL)); +} + +/*F********************************************************************************/ +/*! + + \Function ProtoTunnelUpdate + + \Description + Update the module + + \Input *pProtoTunnel - pointer to module state + + \Version 12/02/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoTunnelUpdate(ProtoTunnelRefT *pProtoTunnel) +{ + uint32_t uCurTick = NetTick(); + int32_t iTunnel; + + // time to flush? + + // acquire exclusive access to tunnel list + NetCritEnter(&pProtoTunnel->TunnelsCritS); + NetCritEnter(&pProtoTunnel->TunnelsCritR); + + // flush all tunnels + // check individual flush timers, which are reset each send + for (iTunnel = 0; iTunnel < pProtoTunnel->iMaxTunnels; iTunnel++) + { + ProtoTunnelT *pTunnel = &(pProtoTunnel->Tunnels[iTunnel]); + if (pTunnel->uVirtualAddr + && (NetTickDiff(uCurTick, pTunnel->uLastTunnelSend) > (signed)pProtoTunnel->uFlushRate)) + { + _ProtoTunnelBufferSend(pProtoTunnel, pTunnel, uCurTick); + } + } + + // release exclusive access to tunnel list + NetCritLeave(&pProtoTunnel->TunnelsCritR); + NetCritLeave(&pProtoTunnel->TunnelsCritS); +} + +/*F*************************************************************************************/ +/*! + \Function ProtoTunnelRawSendto + + \Description + Send data to a remote host over the prototunnel socket. The destination address + is supplied along with the data. Important: remote host shall not be expecting + tunneled data because that function bypasses all the tunneling logic on the sending + side. + + \Input *pProtoTunnel - pointer to module state + \Input *pBuf - the data to be sent + \Input iLen - size of data + \Input *pTo - the address to send to + \Input iToLen - length of address + + \Output + int32_t - number of bytes sent or standard network error code (SOCKERR_xxx) + + \Version 07/12/2011 (mclouatre) +*/ +/************************************************************************************F*/ +int32_t ProtoTunnelRawSendto(ProtoTunnelRefT *pProtoTunnel, const char *pBuf, int32_t iLen, const struct sockaddr *pTo, int32_t iToLen) +{ + return(_ProtoTunnelSocketSendto(pProtoTunnel, pBuf, iLen, 0, pTo, iToLen)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protoupnp.c b/r5dev/thirdparty/dirtysdk/source/proto/protoupnp.c new file mode 100644 index 00000000..34ed5bae --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protoupnp.c @@ -0,0 +1,2253 @@ +/*H********************************************************************************/ +/*! + \File protoupnp.c + + \Description + Implements a simple UPnP client, designed specifically to talk to a UPnP + router and open up a firewall port for peer-peer communication with a + remote client. + + \Notes + ProtoUpnp implementation was based on the following documents: + + References: + [1] UPnP Device Architecture: http://www.upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0-20080424.pdf + [2] UPnP Resources: http://upnp.org/sdcps-and-certification/resources/ + [3] Intel Software for UPnP Technology: http://software.intel.com/en-us/articles/intel-software-for-upnp-technology-download-tools/ + [4] Internet Gateway Device v1.0: http://upnp.org/specs/gw/igd1/ + [5] SOAP 1.2: http://www.w3.org/TR/2003/REC-soap12-part0-20030624/ + [6] UPnP compatibility list (old): http://ccgi.mgillespie.plus.com/upnp/test.php + + In addition, many thanks to the Burnout3 team, whose UPnP implementation + was used as reference. + + UPnP Optional Features: + [a] - Finite Lease Duration + [b] - Wildcard in Source IP (Remote Host) + [c] - Wildcard in External Port + [d] - Non-matching Internal and External Ports + [e] - Specific Remote Host + [f] - Specific External Port + + Tested against the following routers: + <1> Linksys WRT54G 3.03.06 [b,c,d,e,f] + <2> Linksys BEFSR41 [b,c?,d,e,f] [c? because GetSpecificPortMapping with wc port fails) + <3> Netgear WGR614v6 1.0.8 [b,d,f] + + Common UPnP SOAP errors we have seen in usage: + + 402: Invalid Args + 403: Undefined + 501: Action Failed + 713: SpecifiedArrayIndexInvalid (GetGenericPortMapping return code, but we don't use that method?) + 714: NoSuchEntryInArray (GetSpecificPortMapping/DeletePortMapping return code, should not be fatal) + 716: WildCardNotPermittedInExtPort (AddPortMapping return code, but we don't ask for a wildcard external port?) + 718: ConflictInMappingEntry (AddPortMapping return code) + + \Copyright + Copyright (c) 2005 Electronic Arts Inc. + + \Version 03/23/2005 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/xml/xmlparse.h" + +#include "DirtySDK/proto/protoupnp.h" + +/*** Defines **********************************************************************/ + +//! define this to allow reuse of a preexisting mapping that matches what we want +#define PROTOUPNP_REUSEMAPPING (FALSE) + +//! define this to use WAN IP address as the source address in the port map if a remote host is unspecified +#define PROTOMANGLE_USEWANIPSRCADDR (FALSE) + +//! define this to use verbose variable descriptions in requests +#define PROTOUPNP_FULLVARDESC (FALSE) + +//! addr to send discovery requests to (239.255.255.250) +#define PROTOUPNP_DISCOVERYADDR (0xEFFFFFFA) + +//! port to send discovery requests to +#define PROTOUPNP_DISCOVERYPORT (1900) + +//! interval at which we send out discovery request broadcasts +#define PROTOUPNP_DISCOVERYINTERVAL (15000) + +//! maximum number of services parsed +#define PROTOUPNP_MAXSERVICES (5) + +//! default port to map +#define PROTOUPNP_DEFAULTPORTMAP (3658) + +/*** Macros ***********************************************************************/ + +//! add a soap item of type string +#define _ProtoUpnpSoapRequestAddStr(_pUpnp, _pName, _pData) _ProtoUpnpSoapRequestAdd(_pUpnp, _pName, "string", _pData) + +//! add a soap item of type ui2 +#define _ProtoUpnpSoapRequestAddUI2(_pUpnp, _pName, _uData) _ProtoUpnpSoapRequestAdd(_pUpnp, _pName, "ui2", _ProtoUpnpIntToStr(_uData)) + +//! add a soap item of type ui4 +#define _ProtoUpnpSoapRequestAddUI4(_pUpnp, _pName, _uData) _ProtoUpnpSoapRequestAdd(_pUpnp, _pName, "ui4", _ProtoUpnpIntToStr(_uData)) + +//! add a soap item of type boolean +#define _ProtoUpnpSoapRequestAddBln(_pUpnp, _pName, _uData) _ProtoUpnpSoapRequestAdd(_pUpnp, _pName, "boolean", _ProtoUpnpIntToStr(_uData)) + +/*** Type Definitions *************************************************************/ + +//! port mapping description +typedef struct ProtoUpnpPortMapT +{ + int32_t iAddr; + int32_t iPort; + int32_t iLeaseDuration; + char strDesc[31]; + uint8_t bEnabled; +} ProtoUpnpPortMapT; + +//! upnp service info +typedef struct ProtoUpnpServiceT +{ + char strServiceType[64]; //!< service type description + char strDescriptionUrl[128]; //!< service description URL + char strControlUrl[256]; //!< service control url +} ProtoUpnpServiceT; + +//! upnp device info +typedef struct ProtoUpnpDeviceT +{ + char strUrl[128]; //!< string holding device description url + char strUrlBase[64]; //!< base url, with addr/port info + char strUdn[64]; //!< device universal device name + char strModel[127]; //!< device manufacturer and model + uint8_t bDiscovered; //!< TRUE if we've discovered a UPnP device + uint32_t uExternalAddress; //!< external address of device + ProtoUpnpPortMapT CurPortMap; //!< current port mapping + int32_t iNumServices; //!< number of services device supports + ProtoUpnpServiceT Services[PROTOUPNP_MAXSERVICES]; //!< list of services device supports +} ProtoUpnpDeviceT; + +//! module state +struct ProtoUpnpRefT +{ + int32_t iRefCount; //!< module ref count + + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + SocketT *pUdp; //!< udp socket, used for discovery process + ProtoHttpRefT *pProtoHttp; //!< http module, used for device communication + + int32_t iClientAddr; //!< local address + + int32_t iDiscoveryTimer; //!< timer used to time discovery sends + struct sockaddr DiscoveryAddr; //!< address to send discovery to + + int32_t iStatus; //!< current status (PROTOUPNP_STATUS_*) + + int32_t iService; //!< current service index + + ProtoUpnpDeviceT Device; + + enum + { + ST_IDLE, //!< idle state + ST_DISCOVERY, //!< UPnP discovery + ST_DESCRIPTION, //!< UPnP description + ST_SERVICEDESC, //!< service description + ST_GETSTATEVAR, //!< QueryStateVariable request + ST_GETEXTADDR, //!< GetExternalAddress request + ST_GETPORTMAPPING, //!< GetSpecificPortMapping request + ST_DELPORTMAPPING, //!< DeletePortMapping request + ST_ADDPORTMAPPING, //!< AddPortMapping request + + ST_LAST, //!< last state + } eState; + + int32_t iSendSize; //!< size of current send buffer + int32_t iSentBytes; //!< amount of data that we have sent + int32_t iHttpError; //!< most recent ProtoHttpRecv() error, if any + int32_t iSoapError; //!< SOAP error + + int32_t iRemoteHost; //!< address of remote host + int32_t iPortToMapExt; //!< external port we are trying to map + int32_t iPortToMapInt; //!< internal port we are trying to map + int32_t iLeaseDuration; //!< lease duration to try and set + + const ProtoUpnpMacroT *pCommandList; //!< pointer to current command list, if any + + uint8_t bRequestInProgress; //!< TRUE if a device request is in progress + uint8_t bEnableMapping; //!< TRUE to enable mapping, else FALSE + uint8_t bPortIsMapped; //!< TRUE if port is already mapped + uint8_t bVerbose; //!< TRUE to set verbose mode, else FALSE + + #if DIRTYCODE_DEBUG + uint8_t bFakeResponse; //!< TRUE if faking a response, else FALSE + #endif + + char strRequestName[64]; //!< name of current request + char strSendBuf[2048]; //!< buffer to construct posts in + char strRecvBuf[16*1024]; //!< buffer to receive into + + NetCritT Crit; //!< critical section +}; + +/*** Variables ********************************************************************/ + +/*! protoupnp description, sent in soap requests. note that this string is deliberately + short because some implementations (e.g. BEFSR41) only store a small number of + characters (e.g. 11) for the mapping description */ +static const char _ProtoUpnp_strDescription[] = "EA Tunnel"; + +//! protoupnp macro to check for and get the upnp information +static const ProtoUpnpMacroT _ProtoUpnp_cmd_dscg[] = +{ + { 'disc', 0, 0, NULL }, // discovery + { 'desc', 0, 0, NULL }, // get router description + { 'gadr', 0, 0, NULL }, // GetExternalIPAddress + { 0, 0, 0, NULL } +}; + +//! protoupnp macro to add a port mapping +static const ProtoUpnpMacroT _ProtoUpnp_cmd_addp[] = +{ + { 'gprt', 0, 0, NULL }, // GetSpecificPortMapping + { 'aprt', 0, 0, NULL }, // AddPortMapping + { 0, 0, 0, NULL } +}; + +//! protoupnp macro to discover, describe, and check port mapping +static const ProtoUpnpMacroT _ProtoUpnp_cmd_dscp[] = +{ + { 'disc', 0, 0, NULL }, // discovery + { 'desc', 0, 0, NULL }, // get router description + { 'gadr', 0, 0, NULL }, // GetExternalIPAddress + { 'gprt', 0, 0, NULL }, // GetSpecificPortMapping + { 0, 0, 0, NULL } +}; + +//! protoupnp macro to discover, describe, and add port map +static const ProtoUpnpMacroT _ProtoUpnp_cmd_upnp[] = +{ + { 'disc', 0, 0, NULL }, // discovery + { 'desc', 0, 0, NULL }, // get router description + { 'gadr', 0, 0, NULL }, // GetExternalIPAddress + { 'gprt', 0, 0, NULL }, // GetSpecificPortMapping + { 'aprt', 0, 0, NULL }, // AddPortMapping + { 0, 0, 0, NULL } +}; + +//! protoupnp macro to fully test router +static const ProtoUpnpMacroT _ProtoUpnp_cmd_test[] = +{ + // initial test is basically the 'addp' macro plus service description + { 'disc', 0, 0, NULL }, + { 'desc', 0, 0, NULL }, + { 'sdsc', 0, 0, NULL }, + { 'gadr', 0, 0, NULL }, + { 'gprt', 0, 0, NULL }, + { 'aprt', 0, 0, NULL }, + { 'dprt', 0, 0, NULL }, + + // test Wildcard in Source IP optional feature + { 'host', 0, 0, NULL }, // set host to use wildcard + { 'aprt', 0, 0, NULL }, + { 'gprt', 0, 0, NULL }, + { 'dprt', 0, 0, NULL }, + { 'host', -1, 0, NULL }, // restore host + + // test Wildcard in External Port optional feature + { 'extp', 0, 0, NULL }, + { 'aprt', 0, 0, NULL }, + { 'gprt', 0, 0, NULL }, + { 'dprt', 0, 0, NULL }, + + // test Non-matching Internal and External Ports optional feature + { 'extp', 3000, 0, NULL }, + { 'aprt', 0, 0, NULL }, + { 'gprt', 0, 0, NULL }, + { 'dprt', 0, 0, NULL }, + + // done + { 0, 0, 0, NULL } +}; + +//! map states to control request idents +static uint32_t _ProtoUpnp_aStateMap[] = +{ + 'idle', // ST_IDLE + 'disc', // ST_DISCOVERY + 'desc', // ST_DESCRIPTION + 'sdsc', // ST_SERVICEDESC + 'gvar', // ST_GETSTATEVAR + 'gadr', // ST_GETEXTADDR + 'gprt', // ST_GETPORTMAPPING + 'dprt', // ST_DELPORTMAPPING + 'aprt', // ST_ADDPORTMAPPING +}; + +//! upnp module ref +static ProtoUpnpRefT *_ProtoUpnp_pRef = NULL; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpIntToStr + + \Description + Convert an integer to a string, and return a pointer to the string. + + \Input iVal - value to convert to string + + \Output + const char *- pointer to string + + \Version 03/30/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_ProtoUpnpIntToStr(int32_t iVal) +{ + static char _strVal[16]; + ds_snzprintf(_strVal, sizeof(_strVal), "%d", iVal); + return(_strVal); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpFindUrl + + \Description + Find the url following the http://host:port/ portion of a url. + + \Input *pUrl - pointer to source url to parse + + \Output + const char * - pointer to url or NULL + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_ProtoUpnpFindUrl(const char *pUrl) +{ + // make sure it's http (or https) + if ((pUrl = ds_stristr(pUrl, "http")) == NULL) + { + return(NULL); + } + + // skip past double slash + if ((pUrl = ds_stristr(pUrl, "//")) == NULL) + { + return(NULL); + } + pUrl += 2; + + // next slash is the start of the url + return(strchr(pUrl, '/')); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpMakeFullUrl + + \Description + Create a full url for the given device. + + \Input *pDevice - pointer to device + \Input *pBuffer - [out] storage for full url + \Input iBufSize - size of output buffer + \Input *pUrl - pointer to input url (which may be relative or absolute) + + \Version 11/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpMakeFullUrl(ProtoUpnpDeviceT *pDevice, char *pBuffer, int iBufSize, const char *pUrl) +{ + // clear output url buffer + ds_memclr(pBuffer, iBufSize); + + // prepend url base if we weren't given an absolute url + if (ds_strnicmp(pUrl, "http", 4)) + { + ds_strnzcpy(pBuffer, pDevice->strUrlBase, iBufSize); + // if relative url does not start with a slash, add one here + if (*pUrl != '/') + { + NetPrintf(("protoupnp: relative url does not start with a forward slash; adding one\n")); + ds_strnzcat(pBuffer, "/", iBufSize); + } + } + ds_strnzcat(pBuffer, pUrl, iBufSize); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpReset + + \Description + Reset module state + + \Input *pProtoUpnp - pointer to module state + + \Version 07/14/2009 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpReset(ProtoUpnpRefT *pProtoUpnp) +{ + // init device structure + ds_memclr(&pProtoUpnp->Device, sizeof(pProtoUpnp->Device)); + + // force immediate discovery broadcast + pProtoUpnp->iDiscoveryTimer = NetTick() - PROTOUPNP_DISCOVERYINTERVAL; + + // clear status + pProtoUpnp->iStatus = 0; + + // reset service index + pProtoUpnp->iService = 0; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpError + + \Description + Handle error condition + + \Input *pProtoUpnp - pointer to module state + \Input *pReason - reason for error + + \Version 10/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpError(ProtoUpnpRefT *pProtoUpnp, const char *pReason) +{ + // abort current command list? + //pProtoUpnp->pCommandList = NULL; + + // reset to idle state + pProtoUpnp->eState = ST_IDLE; + + // output reason to debug output + NetPrintf(("protoupnp: error -- %s\n", pReason)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpSendDiscoveryRequest + + \Description + Send a discovery request + + \Input *pProtoUpnp - pointer to module state + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpSendDiscoveryRequest(ProtoUpnpRefT *pProtoUpnp) +{ + /*! HTTP-formatted packet to send in UDP discovery broadcast + [1] 1.2.2 Discovery: Search: Request with M-SEARCH */ + static const char _strDiscoveryPacket[] = + "M-SEARCH * HTTP/1.1\r\n" \ + "Host:239.255.255.250:1900\r\n" \ + "ST:urn:schemas-upnp-org:device:WANConnectionDevice:1\r\n" \ + "Man:\"ssdp:discover\"\r\n" \ + "MX:3\r\n" \ + "\r\n"; + + // send discovery multicast + #if DIRTYCODE_LOGGING + if (pProtoUpnp->bVerbose) + { + NetPrintf(("protoupnp: multicasting discovery request\n")); + } + #endif + SocketSendto(pProtoUpnp->pUdp, _strDiscoveryPacket, sizeof(_strDiscoveryPacket), 0, + &pProtoUpnp->DiscoveryAddr, sizeof(pProtoUpnp->DiscoveryAddr)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpParseDiscoveryResponse + + \Description + Parse a discovery response + + \Input *pProtoUpnp - pointer to module state + \Input *pResponse - pointer to response + + \Output + int32_t - zero=failure, else success + + \Notes + The discovery response is an HTTP response in a UDP packet. Since + ProtoHttp does not support UDP, we do the parsing inline here. + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpParseDiscoveryResponse(ProtoUpnpRefT *pProtoUpnp, const char *pResponse) +{ + const char strLocation[] = "Location:"; + const char *pLocation, *pUrl; + int32_t iStrLen; + + // echo response to debug output + #if DIRTYCODE_LOGGING + if (pProtoUpnp->bVerbose) + { + NetPrintf(("protoupnp: received discovery response:\n")); + NetPrintWrap(pProtoUpnp->strRecvBuf, 80); + } + #endif + + // make sure we have a valid header + if (strncmp(pResponse, "HTTP", 4)) + { + NetPrintf(("protoupnp: ignoring non-http response\n")); + return(0); + } + + /*! check for substring that must be included in a valid discovery response + [1] 1.2.3 Discovery: Search: Response */ + if (!ds_stristr(pResponse, "urn:schemas-upnp-org:device:wanconnectiondevice")) + { + NetPrintf(("protoupnp: ignoring non-discovery response\n")); + return(0); + } + + // find and skip location header + if ((pLocation = ds_stristr(pResponse, strLocation)) == NULL) + { + NetPrintf(("protoupnp: response did not include a location header\n")); + return(0); + } + pLocation += sizeof(strLocation) - 1; + + // skip to location body + while((*pLocation != '\0') && (*pLocation <= ' ')) + { + pLocation += 1; + } + + // copy url, leaving room for null termination + for (iStrLen = 0; iStrLen < (signed)(sizeof(pProtoUpnp->Device.strUrl) - 1); iStrLen++) + { + if ((*pLocation == '\0') || (*pLocation == '\r') || (*pLocation == '\n')) + { + break; + } + pProtoUpnp->Device.strUrl[iStrLen] = *pLocation++; + } + + // null terminate url + pProtoUpnp->Device.strUrl[iStrLen] = '\0'; + NetPrintf(("protoupnp: found upnp device '%s'\n", pProtoUpnp->Device.strUrl)); + + // extract address/port + if ((pUrl = _ProtoUpnpFindUrl(pProtoUpnp->Device.strUrl)) != NULL) + { + ds_strsubzcpy(pProtoUpnp->Device.strUrlBase, sizeof(pProtoUpnp->Device.strUrlBase), pProtoUpnp->Device.strUrl, (int32_t)(pUrl-pProtoUpnp->Device.strUrl)); + NetPrintf(("protoupnp: parsed base url '%s'\n", pProtoUpnp->Device.strUrlBase)); + } + + // mark device as valid and set status + pProtoUpnp->Device.bDiscovered = TRUE; + + // reset to idle state + pProtoUpnp->eState = ST_IDLE; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpGetRemoteHost + + \Description + Get string-formatted address for remote host used in UPnP service requests. + + \Input *pProtoUpnp - pointer to module state + \Input *pBuffer - buffer + \Input iBufSize - buffer size + + \Version 11/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpGetRemoteHost(ProtoUpnpRefT *pProtoUpnp, char *pBuffer, int32_t iBufSize) +{ + uint32_t uRemoteHost; + + // get remote host address + #if PROTOMANGLE_USEWANIPSRCADDR + uRemoteHost = (pProtoUpnp->iRemoteHost == -1) ? pProtoUpnp->Device.uExternalAddress : (unsigned)pProtoUpnp->iRemoteHost; + #else + uRemoteHost = (pProtoUpnp->iRemoteHost == -1) ? 0 : (unsigned)pProtoUpnp->iRemoteHost; + #endif + + // return formatted host string + if (uRemoteHost != 0) + { + SocketInAddrGetText(uRemoteHost, pBuffer, iBufSize); + } + else + { + *pBuffer = '\0'; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpHttpReset + + \Description + Reset HTTP-tracking variables prior to initiating an HTTP transaction. + + \Input *pProtoUpnp - pointer to module state + + \Version 10/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpHttpReset(ProtoUpnpRefT *pProtoUpnp) +{ + pProtoUpnp->iHttpError = 0; + pProtoUpnp->iSoapError = 0; + pProtoUpnp->bRequestInProgress = TRUE; + ds_memclr(pProtoUpnp->strRecvBuf, sizeof(pProtoUpnp->strRecvBuf)); + if (pProtoUpnp->pProtoHttp != NULL) + { + ProtoHttpControl(pProtoUpnp->pProtoHttp, 'keep', 0, 0, NULL); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpHttpWaitResponse + + \Description + Wait for an HTTP response. + + \Input *pProtoUpnp - pointer to module state + + \Output + int32_t - negative=failure, zero=in progress, positve=success + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpHttpWaitResponse(ProtoUpnpRefT *pProtoUpnp) +{ + int32_t iRecvResult; + + // if faking a response, data is already in buffer waiting for us + #if DIRTYCODE_DEBUG + if (pProtoUpnp->bFakeResponse) + { + pProtoUpnp->bFakeResponse = FALSE; + pProtoUpnp->bRequestInProgress = FALSE; + pProtoUpnp->eState = ST_IDLE; + return(2); + } + #endif + + // give some time to ProtoHttp + ProtoHttpUpdate(pProtoUpnp->pProtoHttp); + + // send any data that is still needed to be sent + if (pProtoUpnp->iSentBytes < pProtoUpnp->iSendSize) + { + int32_t iResult; + if ((iResult = ProtoHttpSend(pProtoUpnp->pProtoHttp, pProtoUpnp->strSendBuf+pProtoUpnp->iSentBytes, pProtoUpnp->iSendSize-pProtoUpnp->iSentBytes)) >= 0) + { + pProtoUpnp->iSentBytes += iResult; + } + else + { + NetPrintf(("protoupnp: %s request send failed for service %s with result %d\n", pProtoUpnp->strRequestName, + pProtoUpnp->Device.Services[pProtoUpnp->iService].strServiceType, iResult)); + pProtoUpnp->iHttpError = iResult; + pProtoUpnp->bRequestInProgress = FALSE; + pProtoUpnp->eState = ST_IDLE; + pProtoUpnp->iSentBytes = 0; + return(-1); + } + } + + // read data + if ((iRecvResult = ProtoHttpRecvAll(pProtoUpnp->pProtoHttp, pProtoUpnp->strRecvBuf, sizeof(pProtoUpnp->strRecvBuf))) >= 0) + { + // echo response to debug output + #if DIRTYCODE_LOGGING + if (pProtoUpnp->bVerbose) + { + NetPrintf(("\n")); + NetPrintWrap(pProtoUpnp->strRecvBuf, 80); + } + #endif + + // reset transaction state + pProtoUpnp->bRequestInProgress = FALSE; + pProtoUpnp->eState = ST_IDLE; + pProtoUpnp->iSentBytes = 0; + + // check for zero-length response (has been shown to happen in some Windows ICS error responses) + if (iRecvResult == 0) + { + return(-1); + } + // success + return(1); + } + else if ((iRecvResult < 0) && (iRecvResult != PROTOHTTP_RECVWAIT)) + { + NetPrintf(("protoupnp: %s request receive failed for service %s with result %d\n", pProtoUpnp->strRequestName, + pProtoUpnp->Device.Services[pProtoUpnp->iService].strServiceType, iRecvResult)); + pProtoUpnp->iHttpError = iRecvResult; + pProtoUpnp->bRequestInProgress = FALSE; + pProtoUpnp->eState = ST_IDLE; + pProtoUpnp->iSentBytes = 0; + return(-1); + } + + // return pending result + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpSoapFormatRequestHeader + + \Description + Format SOAP header for request given by pRequestName, using the currently + active service. + + \Input *pProtoUpnp - pointer to module state + \Input *pRequestName - pointer to request name + + \Version 03/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpSoapFormatRequestHeader(ProtoUpnpRefT *pProtoUpnp, const char *pRequestName) +{ + char strHdrExtra[256]; + + // set up extended header fields + ds_snzprintf(strHdrExtra, sizeof(strHdrExtra), + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "SOAPAction: \"%s#%s\"\r\n" + "Cache-Control: no-cache\r\n", + pProtoUpnp->Device.Services[pProtoUpnp->iService].strServiceType, + pRequestName); + + // tell ProtoHttp to use this for the extended header info + ProtoHttpControl(pProtoUpnp->pProtoHttp, 'apnd', 0, 0, strHdrExtra); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpSoapRequestOpen + + \Description + Format a SOAP request based on pRequestName and the given variable-argument + list that constructs the SOAP request body, for the currently active + service. + + \Input *pProtoUpnp - pointer to module state + \Input *pRequestName - pointer to request name + + \Version 03/30/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpSoapRequestOpen(ProtoUpnpRefT *pProtoUpnp, const char *pRequestName) +{ + char *pBuf = pProtoUpnp->strSendBuf; + int32_t iBufSize = sizeof(pProtoUpnp->strSendBuf); + + // set up the header + _ProtoUpnpSoapFormatRequestHeader(pProtoUpnp, pRequestName); + + // set up soap envelope start + pProtoUpnp->iSendSize = ds_snzprintf(pBuf, iBufSize, + "\r\n" + " \r\n" + " \r\n", + pRequestName, + pProtoUpnp->Device.Services[pProtoUpnp->iService].strServiceType); + + // save request name + ds_strnzcpy(pProtoUpnp->strRequestName, pRequestName, sizeof(pProtoUpnp->strRequestName)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpSoapRequestAdd + + \Description + Add a value to a soap request. This function should not be used directly, + the _ProtoUpnpSoapRequestAddStr, AddUI2, AddUI4, and AddBln macros should be + used instead. + + \Input *pProtoUpnp - pointer to module state + \Input *pName - pointer to item name + \Input *pType - pointer to item type + \Input *pData - pointer to item data + + + \Version 03/30/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpSoapRequestAdd(ProtoUpnpRefT *pProtoUpnp, const char *pName, const char *pType, const char *pData) +{ + char *pBuf = pProtoUpnp->strSendBuf + pProtoUpnp->iSendSize; + int32_t iBufSize = sizeof(pProtoUpnp->strSendBuf) - pProtoUpnp->iSendSize; + + #if PROTOUPNP_FULLVARDESC + pProtoUpnp->iSendSize += ds_snzprintf(pBuf, iBufSize, + " <%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s\r\n", + pName, pType, pData, pName); + #else + pProtoUpnp->iSendSize += ds_snzprintf(pBuf, iBufSize, " <%s>%s\r\n", pName, pData, pName); + #endif +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpSoapRequestClose + + \Description + Close the soap request. + + \Input *pProtoUpnp - pointer to module state + + \Version 03/30/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpSoapRequestClose(ProtoUpnpRefT *pProtoUpnp) +{ + char *pBuf = pProtoUpnp->strSendBuf + pProtoUpnp->iSendSize; + int32_t iBufSize = sizeof(pProtoUpnp->strSendBuf) - pProtoUpnp->iSendSize; + + // append soap envelope end + pProtoUpnp->iSendSize += ds_snzprintf(pBuf, iBufSize, + " \r\n" + " \r\n" + "\r\n\r\n", + pProtoUpnp->strRequestName); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpSoapRequestPost + + \Description + Post a formatted SOAP request to gateway device. + + \Input *pProtoUpnp - pointer to module state + + \Version 03/27/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpSoapRequestPost(ProtoUpnpRefT *pProtoUpnp) +{ + int32_t iResult; + + if (pProtoUpnp->bRequestInProgress == TRUE) + { + _ProtoUpnpError(pProtoUpnp, "a soap request is already in progress"); + return; + } + + // print request to debug output + NetPrintf(("protoupnp: initiating %s request for service %s\n", pProtoUpnp->strRequestName, + pProtoUpnp->Device.Services[pProtoUpnp->iService].strServiceType)); + + // initiate external address fetch operation + _ProtoUpnpHttpReset(pProtoUpnp); + if ((iResult = ProtoHttpPost(pProtoUpnp->pProtoHttp, + pProtoUpnp->Device.Services[pProtoUpnp->iService].strControlUrl, + pProtoUpnp->strSendBuf, pProtoUpnp->iSendSize, + FALSE)) >= 0) + { + pProtoUpnp->iSentBytes = iResult; + } + else + { + NetPrintf(("protoupnp: %s request failed for service %s with result %d\n", pProtoUpnp->strRequestName, + pProtoUpnp->Device.Services[pProtoUpnp->iService].strServiceType, iResult)); + pProtoUpnp->iHttpError = iResult; + pProtoUpnp->bRequestInProgress = FALSE; + pProtoUpnp->eState = ST_IDLE; + return; + } + + // verbose output? + #if DIRTYCODE_LOGGING + if (pProtoUpnp->bVerbose) + { + NetPrintWrap(pProtoUpnp->strSendBuf, 80); + } + #endif +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpSoapWaitResponse + + \Description + Poll for a soap response. If there is an error response, this function + parses it and stores the value internally. + + \Input *pProtoUpnp - pointer to module state + + \Version 03/30/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpSoapWaitResponse(ProtoUpnpRefT *pProtoUpnp) +{ + ProtoHttpResponseE eResponse; + int32_t iResult; + + // wait until http transaction completes + if ((iResult = _ProtoUpnpHttpWaitResponse(pProtoUpnp)) == 0) + { + return(iResult); + } + #if DIRTYCODE_DEBUG + else if (iResult == 2) + { + return(iResult); + } + #endif + + // check for error response + eResponse = ProtoHttpStatus(pProtoUpnp->pProtoHttp, 'code', NULL, 0); + + // if an error + if (eResponse != PROTOHTTP_RESPONSE_SUCCESSFUL) + { + // if it is a soap error, parse the response + if (eResponse == PROTOHTTP_RESPONSE_SERVERERROR) + { + const char *pXml, *pXml2; + + // find UPnPError + if ((pXml = XmlFind(pProtoUpnp->strRecvBuf, "%*:Envelope.%*:Body.%*:Fault.detail.UPnPError")) != NULL) + { + // get error code + if ((pXml2 = XmlFind(pXml, ".errorCode")) != NULL) + { + pProtoUpnp->iSoapError = XmlContentGetInteger(pXml2, 0); + } + + // get error description + #if DIRTYCODE_LOGGING + { + char strErrorText[96] = "unknown"; + if ((pXml2 = XmlFind(pXml, ".errorDescription")) != NULL) + { + XmlContentGetString(pXml2, strErrorText, sizeof(strErrorText), ""); + } + NetPrintf(("protoupnp: soap error %d (%s) in response to %s request\n", + pProtoUpnp->iSoapError, strErrorText, pProtoUpnp->strRequestName)); + } + #endif + } + } + + iResult = -1; + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpXmlGetAddress + + \Description + Get an address from XML field pName. + + \Input *pXml - pointer to current location in xml file + \Input *pName - name of field to get + \Input *pAddress - [out] output buffer to store address + + \Output + int32_t - negative=not found, else found + + \Version 10/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpXmlGetAddress(const char *pXml, const char *pName, int32_t *pAddress) +{ + if ((pXml = XmlFind(pXml, pName)) != NULL) + { + *pAddress = XmlContentGetAddress(pXml, 0); + return(0); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpXmlGetBoolean + + \Description + Get a boolean from XML field pName. + + \Input *pXml - pointer to current location in xml file + \Input *pName - name of field to get + \Input *pBoolean - [out] output buffer to store boolean + + \Output + int32_t - negative=not found, else found + + \Version 10/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpXmlGetBoolean(const char *pXml, const char *pName, uint8_t *pBoolean) +{ + if ((pXml = XmlFind(pXml, pName)) != NULL) + { + *pBoolean = (uint8_t)XmlContentGetInteger(pXml, 0); + return(0); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpXmlGetInteger + + \Description + Get an integer from XML field pName. + + \Input *pXml - pointer to current location in xml file + \Input *pName - name of field to get + \Input *pInteger - [out] output buffer to store integer + + \Output + int32_t - negative=not found, else found + + \Version 10/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpXmlGetInteger(const char *pXml, const char *pName, int32_t *pInteger) +{ + if ((pXml = XmlFind(pXml, pName)) != NULL) + { + *pInteger = XmlContentGetInteger(pXml, 0); + return(0); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpXmlGetString + + \Description + Get a string from XML field pName. + + \Input *pXml - pointer to current location in xml file + \Input *pName - name of field to get + \Input *pBuffer - [out] output buffer to store string + \Input iBufSize - size of output buffer + + \Output + int32_t - negative=not found, else found + + \Version 10/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpXmlGetString(const char *pXml, const char *pName, char *pBuffer, int32_t iBufSize) +{ + ds_memclr(pBuffer, iBufSize); + if ((pXml = XmlFind(pXml, pName)) != NULL) + { + return(XmlContentGetString(pXml, pBuffer, iBufSize, "")); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpXmlParseDescription + + \Description + Parse a device description response + + \Input *pProtoUpnp - pointer to module state + + \Output + int32_t - negative=failure, else success + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpXmlParseDescription(ProtoUpnpRefT *pProtoUpnp) +{ + char strContentA[256], strContentB[256], strContentC[256]; + ProtoUpnpDeviceT *pDevice = &pProtoUpnp->Device; + const char *pXml, *pXml2; + + // parse optional URLBase field if present + if ((pXml = XmlFind(pProtoUpnp->strRecvBuf, "root.URLBase")) != NULL) + { + // get urlbase string + int32_t iUrlBaseLen = XmlContentGetString(pXml, pProtoUpnp->Device.strUrlBase, sizeof(pProtoUpnp->Device.strUrlBase), ""); + if (iUrlBaseLen > 0) + { + // if urlbase ends in a trailing slash, trim it + if (pProtoUpnp->Device.strUrlBase[iUrlBaseLen-1] == '/') + { + NetPrintf(("protoupnp: trimming trailing slash from parsed URLBase string\n")); + pProtoUpnp->Device.strUrlBase[iUrlBaseLen-1] = '\0'; + } + NetPrintf(("protoupnp: parsed URLBase=%s\n", pProtoUpnp->Device.strUrlBase)); + } + else + { + NetPrintf(("protoupnp: unable to parse URLBase field\n")); + } + } + + // find a WANConnectionDevice + NetPrintf(("protoupnp: parsing xml response for a WANConnectionDevice:\n")); + for (pXml = XmlFind(pProtoUpnp->strRecvBuf, "root.device"); pXml != NULL; ) + { + // get deviceType + pXml2 = XmlFind(pXml, ".deviceType"); + + // get deviceType contents + XmlContentGetString(pXml2, strContentA, sizeof(strContentA), ""); + NetPrintf(("protoupnp: deviceType=%s\n", strContentA)); + + // is this the deviceType we are looking for? + if (ds_stristr(strContentA, "WANConnectionDevice")) + { + // get UDN + _ProtoUpnpXmlGetString(pXml2, "UDN", pDevice->strUdn, sizeof(pDevice->strUdn)); + // get manufacturer name + _ProtoUpnpXmlGetString(pXml2, "manufacturer", strContentA, sizeof(strContentA)); + // get model name + _ProtoUpnpXmlGetString(pXml2, "modelName", strContentB, sizeof(strContentB)); + // get model number (firmware rev) + _ProtoUpnpXmlGetString(pXml2, "modelNumber", strContentC, sizeof(strContentC)); + + // format model name + ds_snzprintf(pProtoUpnp->Device.strModel, sizeof(pProtoUpnp->Device.strModel), "%s %s %s", + strContentA, strContentB, strContentC); + NetPrintf(("protoupnp: device=%s UDN=%s\n", pProtoUpnp->Device.strModel, pDevice->strUdn)); + break; + } + + // first try to advance through recursion + if ((pXml2 = XmlFind(pXml, ".deviceList.device")) == NULL) + { + // if no child device lists, skip to next element + pXml2 = XmlSkip(pXml); + } + pXml = pXml2; + } + + // find any services associated with this device + NetPrintf(("protoupnp: parsing xml response for WANConnection Services:\n")); + for (pXml = XmlFind(pXml, ".serviceList.service"); pXml != NULL; pXml = XmlNext(pXml)) + { + // get serviceType header + if (_ProtoUpnpXmlGetString(pXml, ".serviceType", strContentB, sizeof(strContentB)) >= 0) + { + // get serviceType contents + NetPrintf(("protoupnp: serviceType=%s\n", strContentB)); + + // if it's not a serviceType we want, ignore it + if (!ds_stristr(strContentB, "connection")) + { + continue; + } + + // if we are out of space to store it, continue + if (pDevice->iNumServices >= PROTOUPNP_MAXSERVICES) + { + NetPrintf(("protoupnp: service list full, ignoring this entry\n")); + continue; + } + + // if this a serviceType we want, find the controlUrl + if (_ProtoUpnpXmlGetString(pXml, ".controlURL", strContentA, sizeof(strContentA)) >= 0) + { + ProtoUpnpServiceT *pService = &pDevice->Services[pDevice->iNumServices]; + + // create controlURL + _ProtoUpnpMakeFullUrl(&pProtoUpnp->Device, pService->strControlUrl, sizeof(pService->strControlUrl), strContentA); + NetPrintf(("protoupnp: controlURL=%s\n", pService->strControlUrl)); + + // create serviceURL + _ProtoUpnpXmlGetString(pXml, ".SCPDURL", strContentA, sizeof(strContentA)); + _ProtoUpnpMakeFullUrl(&pProtoUpnp->Device, pService->strDescriptionUrl, sizeof(pService->strDescriptionUrl), strContentA); + NetPrintf(("protoupnp: SCPDURL=%s\n", pService->strDescriptionUrl)); + + // save serviceType + ds_strnzcpy(pService->strServiceType, strContentB, sizeof(pService->strServiceType)); + NetPrintf(("protoupnp: serviceType=%s\n", pService->strServiceType)); + + pDevice->iNumServices += 1; + } + } + } + + return((pDevice->iNumServices > 0) ? 0 : -1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpXmlParseGetExtAddr + + \Description + Parse a service GetExternalIpAddress response + + \Input *pProtoUpnp - pointer to module state + + \Output + int32_t - negative=failure, else success + + \Version 03/25/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoUpnpXmlParseGetExtAddr(ProtoUpnpRefT *pProtoUpnp) +{ + const char *pXml; + + // parse response and look for external address + NetPrintf(("protoupnp: parsing xml response to GetExternalIPAddress request:\n")); + if ((pXml = XmlFind(pProtoUpnp->strRecvBuf, "%*:Envelope.%*:Body.%*:GetExternalIPAddressResponse")) != NULL) + { + // first, try to get contents ($$note - try this with a Netgear as it is different from the Linksys) + if ((pProtoUpnp->Device.uExternalAddress = XmlContentGetAddress(pXml, 0)) == 0) + { + // if that didn't work, try NewExternalIPAddress + _ProtoUpnpXmlGetAddress(pXml, ".NewExternalIPAddress", (int32_t *)&pProtoUpnp->Device.uExternalAddress); + } + // display parsed address + NetPrintf(("protoupnp: IPAddress=%a\n", pProtoUpnp->Device.uExternalAddress)); + } + + return((pProtoUpnp->Device.uExternalAddress != 0) ? 0 : -1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpXmlParseGetPortMapping + + \Description + Parse a service GetSpecificPortMappingEntry response + + \Input *pProtoUpnp - pointer to module state + + \Version 10/07/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoUpnpXmlParseGetPortMapping(ProtoUpnpRefT *pProtoUpnp) +{ + const char *pXml; + + // parse response and look for external address + NetPrintf(("protoupnp: parsing xml response to GetSpecificPortMappingEntry request:\n")); + if ((pXml = XmlFind(pProtoUpnp->strRecvBuf, "%*:Envelope.%*:Body.%*:GetSpecificPortMappingEntryResponse")) != NULL) + { + // parse addr + _ProtoUpnpXmlGetAddress(pXml, ".NewInternalClient", &pProtoUpnp->Device.CurPortMap.iAddr); + // parse port + _ProtoUpnpXmlGetInteger(pXml, ".NewInternalPort", &pProtoUpnp->Device.CurPortMap.iPort); + // parse enabled + _ProtoUpnpXmlGetBoolean(pXml, ".NewEnabled", &pProtoUpnp->Device.CurPortMap.bEnabled); + // parse description + _ProtoUpnpXmlGetString(pXml, ".NewPortMappingDescription", pProtoUpnp->Device.CurPortMap.strDesc, + sizeof(pProtoUpnp->Device.CurPortMap.strDesc)); + + // debug output + NetPrintf(("protoupnp: found port mapping ->%a:%d enabled=%s (%s)\n", + pProtoUpnp->Device.CurPortMap.iAddr, pProtoUpnp->Device.CurPortMap.iPort, + pProtoUpnp->Device.CurPortMap.bEnabled ? "true" : "false", + pProtoUpnp->Device.CurPortMap.strDesc)); + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoUpnpIdle + + \Description + NetConn idle function to update the ProtoUpnp module. + + \Input *pData - pointer to module state + \Input uTick - current tick count + + \Notes + This function is installed as a NetConn Idle function. NetConnIdle() + must be regularly polled for this function to be called. + + \Version 1.0 02/01/2008 (cadam) First Version +*/ +/********************************************************************************F*/ +static void _ProtoUpnpIdle(void *pData, uint32_t uTick) +{ + ProtoUpnpRefT *pRef = _ProtoUpnp_pRef; + + ProtoUpnpUpdate(pRef); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoUpnpCreate + + \Description + Create the ProtoUpnp module state. + + \Output + ProtoUpnpRefT * - pointer to module state, or NULL + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoUpnpRefT *ProtoUpnpCreate(void) +{ + ProtoUpnpRefT *pProtoUpnp; + int32_t iMemGroup, iResult; + void *pMemGroupUserData; + struct sockaddr BindAddr; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // if already created, just ref it + if (_ProtoUpnp_pRef != NULL) + { + _ProtoUpnp_pRef->iRefCount += 1; + return(_ProtoUpnp_pRef); + } + + // allocate and initialize module state + if ((pProtoUpnp = DirtyMemAlloc(sizeof(*pProtoUpnp), PROTOUPNP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + return(NULL); + } + ds_memclr(pProtoUpnp, sizeof(*pProtoUpnp)); + pProtoUpnp->iMemGroup = iMemGroup; + pProtoUpnp->pMemGroupUserData = pMemGroupUserData; + + // open udp socket + if ((pProtoUpnp->pUdp = SocketOpen(AF_INET, SOCK_DGRAM, 0)) == NULL) + { + NetPrintf(("protoupnp: unable to open udp socket\n")); + DirtyMemFree(pProtoUpnp, PROTOUPNP_MEMID, pProtoUpnp->iMemGroup, pProtoUpnp->pMemGroupUserData); + return(NULL); + } + // bind udp socket + SockaddrInit(&BindAddr, AF_INET); + if ((iResult = SocketBind(pProtoUpnp->pUdp, &BindAddr, sizeof(BindAddr))) == SOCKERR_NONE) + { + #if DIRTYCODE_LOGGING + SocketInfo(pProtoUpnp->pUdp, 'bind', 0, &BindAddr, sizeof(BindAddr)); + NetPrintf(("protoupnp: bound discovery socket to port %d\n", SockaddrInGetPort(&BindAddr))); + #endif + } + else + { + NetPrintf(("protoupnp: error %d binding discovery socket\n", iResult)); + SocketClose(pProtoUpnp->pUdp); + DirtyMemFree(pProtoUpnp, PROTOUPNP_MEMID, pProtoUpnp->iMemGroup, pProtoUpnp->pMemGroupUserData); + return(NULL); + } + + // allocate protohttp module + if ((pProtoUpnp->pProtoHttp = ProtoHttpCreate(1024)) == NULL) + { + NetPrintf(("protoupnp: unable to create protohttp module\n")); + SocketClose(pProtoUpnp->pUdp); + DirtyMemFree(pProtoUpnp, PROTOUPNP_MEMID, pProtoUpnp->iMemGroup, pProtoUpnp->pMemGroupUserData); + return(NULL); + } + + // setup critical section + NetCritInit(&pProtoUpnp->Crit, "protoupnp"); + + // set to quiet mode + ProtoHttpControl(pProtoUpnp->pProtoHttp, 'spam', FALSE, 0, NULL); + + // set up discovery sendto address + SockaddrInit(&pProtoUpnp->DiscoveryAddr, AF_INET); + SockaddrInSetAddr(&pProtoUpnp->DiscoveryAddr, PROTOUPNP_DISCOVERYADDR); + SockaddrInSetPort(&pProtoUpnp->DiscoveryAddr, PROTOUPNP_DISCOVERYPORT); + + // set initial state + pProtoUpnp->eState = ST_IDLE; + pProtoUpnp->iLeaseDuration = 4*60*60; // four hours + pProtoUpnp->iPortToMapExt = pProtoUpnp->iPortToMapInt = PROTOUPNP_DEFAULTPORTMAP; + pProtoUpnp->bEnableMapping = TRUE; + pProtoUpnp->iRemoteHost = -1; + + // add protoupnp task handle + NetConnIdleAdd(_ProtoUpnpIdle, pProtoUpnp); + + // save ref and return module pointer to caller + pProtoUpnp->iRefCount = 1; + _ProtoUpnp_pRef = pProtoUpnp; + + return(pProtoUpnp); +} + +/*F********************************************************************************/ +/*! + \Function ProtoUpnpGetRef + + \Description + Get ProtoUpnp reference + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoUpnpRefT *ProtoUpnpGetRef(void) +{ + return(_ProtoUpnp_pRef); +} + +/*F********************************************************************************/ +/*! + \Function ProtoUpnpDestroy + + \Description + Destroy the ProtoUpnp module. + + \Input *pProtoUpnp - pointer to module state + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoUpnpDestroy(ProtoUpnpRefT *pProtoUpnp) +{ + // if we aren't the last, just decrement the refcount and return + if (--pProtoUpnp->iRefCount > 0) + { + return; + } + + // destroy ProtoHttp module + ProtoHttpDestroy(pProtoUpnp->pProtoHttp); + + // close udp socket + SocketClose(pProtoUpnp->pUdp); + + // destroy critical section + NetCritKill(&pProtoUpnp->Crit); + + // del protoupnp task handle + NetConnIdleDel(_ProtoUpnpIdle, pProtoUpnp); + + // release module memory + DirtyMemFree(pProtoUpnp, PROTOUPNP_MEMID, pProtoUpnp->iMemGroup, pProtoUpnp->pMemGroupUserData); + + // clear ref + _ProtoUpnp_pRef = NULL; +} + +/*F********************************************************************************/ +/*! + \Function ProtoUpnpStatus + + \Description + Get information from the ProtoUpnp module. + + \Input *pProtoUpnp - pointer to module state + \Input iSelect - info selector + \Input *pBuf - [out] pointer to output buffer + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'body' - get most recent http response body + 'ctrl' - return control ident for current operation + 'disc' - TRUE if a UPnP device has been discovered, else FALSE + 'dnam' - device name ("manufacturer modelName modelNumber") + 'done' - returns TRUE if in IDLE state and no macro is currently being processed + 'durn' - device URN + 'extn' - device external (WAN) IP address + 'extp' - returns external port used in mapping operations + 'idle' - return if module is idle or not + 'intp' - returns internal port used in mapping operations + 'lerr' - most recent error result (http or soap) + 'macr' - return current macro being executed, or zero if none, + name copied to pBuf + 'rbdy' - get most recent htto request body + 'stat' - module status (PROTOUPNP_STATUS_*) + \endverbatim + + \Version 10/10/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoUpnpStatus(ProtoUpnpRefT *pProtoUpnp, int32_t iSelect, void *pBuf, int32_t iBufSize) +{ + // return most recent http body + if (iSelect == 'body') + { + ds_strnzcpy(pBuf, pProtoUpnp->strRecvBuf, iBufSize); + return(0); + } + // return control ident for current operation + if (iSelect == 'ctrl') + { + return(_ProtoUpnp_aStateMap[pProtoUpnp->eState]); + } + // have we discovered a device? + if (iSelect == 'disc') + { + return(pProtoUpnp->Device.bDiscovered); + } + // return device name + if (iSelect == 'dnam') + { + ds_strnzcpy(pBuf, pProtoUpnp->Device.strModel, iBufSize); + return(0); + } + if (iSelect == 'done') + { + return((pProtoUpnp->pCommandList == NULL) && (pProtoUpnp->eState == ST_IDLE)); + } + // return device urn + if (iSelect == 'durn') + { + ds_strnzcpy(pBuf, pProtoUpnp->Device.strUdn, iBufSize); + return(0); + } + // return device external (WAN) IP address + if (iSelect == 'extn') + { + return(pProtoUpnp->Device.uExternalAddress); + } + // return external port used in map operations + if (iSelect == 'extp') + { + return(pProtoUpnp->iPortToMapExt); + } + // return if idle or not + if (iSelect == 'idle') + { + return(pProtoUpnp->eState == ST_IDLE); + } + // set source port + if (iSelect == 'intp') + { + return(pProtoUpnp->iPortToMapInt); + } + // last error + if (iSelect == 'lerr') + { + if (pProtoUpnp->iSoapError != 0) + { + return(pProtoUpnp->iSoapError); + } + else if (pProtoUpnp->iHttpError != -1) + { + return(pProtoUpnp->iHttpError); + } + else + { + return(0); + } + } + // return current macro being executed + if (iSelect == 'macr') + { + int32_t iMacro = (pProtoUpnp->pCommandList != NULL) ? pProtoUpnp->pCommandList->iControl : 0; + if (pBuf != NULL) + { + ds_strnzcpy(pBuf, pProtoUpnp->strRequestName, iBufSize); + } + return(iMacro); + } + // most recent request body + if (iSelect == 'rbdy') + { + ds_strnzcpy(pBuf, pProtoUpnp->strSendBuf, iBufSize); + return(0); + } + // return module status + if (iSelect == 'stat') + { + return(pProtoUpnp->iStatus); + } + // try passing it to ProtoHttp + return(ProtoHttpStatus(pProtoUpnp->pProtoHttp, iSelect, pBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoUpnpControl + + \Description + Control ProtoUpnp behavior + + \Input *pProtoUpnp - pointer to module state + \Input iControl - control selector + \Input iValue - selector specifc + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'abrt' - abort current operation + 'aprt' - start AddPortMapping request + 'desc' - initiate description + 'disc' - initiate discovery + 'dprt' - start DeletePortMapping request + 'extp' - set external port + 'gadr' - start GetExternalIPAddress request + 'gprt' - start GetSpecificPortMappingEntry request + 'host' - host address to allow traffic through firewall from + 'intp' - set internal port + 'ldur' - set lease duration + 'macr' - execute a macro. iValue=macro id, or pValue=pointer to user macro + 'addp' - macro to add a new port mapping + 'port' - set internal and external ports + 'spam' - enable/disable verbose output + \endverbatim + + \Version 05/24/2005 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoUpnpControl(ProtoUpnpRefT *pProtoUpnp, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue) +{ + char strAddrText[20]; + + // abort + if (iControl == 'abrt') + { + NetCritEnter(&pProtoUpnp->Crit); + pProtoUpnp->eState = ST_IDLE; + pProtoUpnp->bRequestInProgress = FALSE; + NetCritLeave(&pProtoUpnp->Crit); + return(0); + } + // set dest port + if (iControl == 'extp') + { + NetPrintf(("protoupnp: set external port to %d\n", iValue)); + pProtoUpnp->iPortToMapExt = iValue; + return(0); + } + // fake a command + #if DIRTYCODE_DEBUG + if (iControl == 'fake') + { + const int32_t _StateMap[] = { 'idle', 'disc', 'desc', 'sdsc', 'gvar', 'gext', 'gprt', 'dprt', 'aprt' }; + int32_t iState; + + NetCritEnter(&pProtoUpnp->Crit); + + // copy input into receive buffer + ds_strnzcpy(pProtoUpnp->strRecvBuf, pValue, sizeof(pProtoUpnp->strRecvBuf)); + + // fake the command + if (iValue == 'disc') + { + // parse the response & update status + NetPrintf(("protoupnp: faking 'disc' command\n")); + _ProtoUpnpParseDiscoveryResponse(pProtoUpnp, pProtoUpnp->strRecvBuf); + pProtoUpnp->iStatus |= PROTOUPNP_STATUS_DISCOVERED; + } + else + { + for (iState = 0; iState < ST_LAST; iState++) + { + if (_StateMap[iState] == iValue) + { + NetPrintf(("protoupnp: faking '%c%c%c%c' command\n", + (iValue>>24)&0xff, (iValue>>16)&0xff, (iValue>>8)&0xff,(iValue>>0)&0xff)); + pProtoUpnp->bFakeResponse = TRUE; + pProtoUpnp->eState = iState; + break; + } + } + } + + NetCritLeave(&pProtoUpnp->Crit); + return(0); + } + #endif + // set remote host + if (iControl == 'host') + { + NetPrintf(("protoupnp: set remote host to %a\n", iValue)); + pProtoUpnp->iRemoteHost = iValue; + return(0); + } + // set source port + if (iControl == 'intp') + { + NetPrintf(("protoupnp: set internal port to %d\n", iValue)); + pProtoUpnp->iPortToMapInt = iValue; + return(0); + } + // set lease duration + if (iControl == 'ldur') + { + NetPrintf(("protoupnp: set lease duration to %d\n", iValue)); + pProtoUpnp->iLeaseDuration = iValue; + return(0); + } + // set both internal and external ports + if (iControl == 'port') + { + NetPrintf(("protoupnp: setting internal and external port to %d\n", iValue)); + pProtoUpnp->iPortToMapInt = pProtoUpnp->iPortToMapExt = iValue; + return(0); + } + // set verbose + if (iControl == 'spam') + { + ProtoHttpControl(pProtoUpnp->pProtoHttp, iControl, iValue, 0, NULL); + pProtoUpnp->bVerbose = iValue; + return(0); + } + + /* + The following control functions can only be initiated in IDLE state + */ + + if ((pProtoUpnp->eState != ST_IDLE) || (pProtoUpnp->bRequestInProgress)) + { + NetPrintf(("protoupnp: '%c%c%c%c' only valid in IDLE state\n", + (iControl>>24)&0xff, (iControl>>16)&0xff, (iControl>>8)&0xff, iControl&0xff)); + return(-1); + } + + // execute a macro + if (iControl == 'macr') + { + const ProtoUpnpMacroT *pMacro; + + // is it a built-in, set up command list to execute + if (iValue == 'dscg') + { + pProtoUpnp->pCommandList = _ProtoUpnp_cmd_dscg; + } + else if (iValue == 'dscp') + { + pProtoUpnp->pCommandList = _ProtoUpnp_cmd_dscp; + } + else if (iValue == 'addp') + { + pProtoUpnp->pCommandList = _ProtoUpnp_cmd_addp; + } + else if (iValue == 'upnp') + { + pProtoUpnp->pCommandList = _ProtoUpnp_cmd_upnp; + } + else if (iValue == 'test') + { + pProtoUpnp->pCommandList = _ProtoUpnp_cmd_test; + } + else // user-specified macro + { + pProtoUpnp->pCommandList = pValue; + } + // start macro + pMacro = pProtoUpnp->pCommandList; + NetPrintf(("protoupnp: command list start -> %c%c%c%c\n", + (pMacro->iControl>>24)&0xff, (pMacro->iControl>>16)&0xff, + (pMacro->iControl>>8)&0xff, pMacro->iControl&0xff)); + ProtoUpnpControl(pProtoUpnp, pMacro->iControl, pMacro->iValue, pMacro->iValue2, pMacro->pValue); + return(0); + } + + // start discovery + if (iControl == 'disc') + { + // reset module + _ProtoUpnpReset(pProtoUpnp); + + NetPrintf(("protoupnp: searching for upnp devices\n")); + + // set to discovery state + pProtoUpnp->eState = ST_DISCOVERY; + return(0); + } + + // only allow subsequent operations if we've discovered a device + if (pProtoUpnp->Device.bDiscovered != TRUE) + { + NetPrintf(("protoupnp: '%c%c%c%c' only valid if a device has been discovered\n", + (iControl>>24)&0xff, (iControl>>16)&0xff, (iControl>>8)&0xff, iControl&0xff)); + return(-1); + } + + // start description + if (iControl == 'desc') + { + // initiate description fetch operation + NetPrintf(("protoupnp: sending description request\n")); + ds_strnzcpy(pProtoUpnp->strRequestName, "Description", sizeof(pProtoUpnp->strRequestName)); + _ProtoUpnpHttpReset(pProtoUpnp); + ProtoHttpGet(pProtoUpnp->pProtoHttp, pProtoUpnp->Device.strUrl, FALSE); + + // set to description state + pProtoUpnp->eState = ST_DESCRIPTION; + return(0); + } + + // only allow subsequent operations if we've described a device + if (pProtoUpnp->Device.iNumServices == 0) + { + NetPrintf(("protoupnp: '%c%c%c%c' only valid if a device has been described\n", + (iControl>>24)&0xff, (iControl>>16)&0xff, (iControl>>8)&0xff, iControl&0xff)); + return(-1); + } + + // start addportmapping request + if (iControl == 'aprt') + { + // if we haven't already, acquire client address + if (pProtoUpnp->iClientAddr == 0) + { + // get local address + pProtoUpnp->iClientAddr = NetConnStatus('addr', 0, NULL, 0); + } + + // if a mapping doesn't exist, go ahead and map it + if (!pProtoUpnp->bPortIsMapped) + { + // get remote host address + _ProtoUpnpGetRemoteHost(pProtoUpnp, strAddrText, sizeof(strAddrText)); + + // post AddPortMapping request + NetPrintf(("protoupnp: addportmap %s:%d->%a:%d duration=%d enabled=%s\n", strAddrText, pProtoUpnp->iPortToMapExt, pProtoUpnp->iClientAddr, + pProtoUpnp->iPortToMapInt, pProtoUpnp->iLeaseDuration, pProtoUpnp->bEnableMapping ? "true" : "false")); + + // format the request + _ProtoUpnpSoapRequestOpen(pProtoUpnp, "AddPortMapping"); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewRemoteHost", strAddrText); + _ProtoUpnpSoapRequestAddUI2(pProtoUpnp, "NewExternalPort", pProtoUpnp->iPortToMapExt); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewProtocol", "UDP"); + _ProtoUpnpSoapRequestAddUI2(pProtoUpnp, "NewInternalPort", pProtoUpnp->iPortToMapInt); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewInternalClient", SocketInAddrGetText(pProtoUpnp->iClientAddr, strAddrText, sizeof(strAddrText))); + _ProtoUpnpSoapRequestAddBln(pProtoUpnp, "NewEnabled", pProtoUpnp->bEnableMapping); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewPortMappingDescription", _ProtoUpnp_strDescription); + _ProtoUpnpSoapRequestAddUI4(pProtoUpnp, "NewLeaseDuration", pProtoUpnp->iLeaseDuration); + _ProtoUpnpSoapRequestClose(pProtoUpnp); + _ProtoUpnpSoapRequestPost(pProtoUpnp); + + // set to poll for response + pProtoUpnp->eState = ST_ADDPORTMAPPING; + } + else + { + #if PROTOUPNP_REUSEMAPPING + // do we already have the mapping we want? + if ((pProtoUpnp->Device.CurPortMap.iAddr == pProtoUpnp->iClientAddr) && + (pProtoUpnp->Device.CurPortMap.iPort == pProtoUpnp->iPortToMapInt) && + (pProtoUpnp->Device.CurPortMap.bEnabled == pProtoUpnp->bEnableMapping) && + (!strcmp(pProtoUpnp->Device.CurPortMap.strDesc, _ProtoUpnp_strDescription))) + { + NetPrintf(("protoupnp: reusing already existing port mapping\n")); + } + else + #endif + { + // we have to delete the current mapping before we can add a new one + ProtoUpnpControl(pProtoUpnp, 'dprt', 0, 0, NULL); + + // if this is a command-list command, don't consume it + if (pProtoUpnp->pCommandList != NULL) + { + pProtoUpnp->pCommandList -= 1; + } + } + } + return(0); + } + // start delportmapping request + if (iControl == 'dprt') + { + // get remote host address + _ProtoUpnpGetRemoteHost(pProtoUpnp, strAddrText, sizeof(strAddrText)); + + // post DeletePortMapping request + _ProtoUpnpSoapRequestOpen(pProtoUpnp, "DeletePortMapping"); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewRemoteHost", strAddrText); + _ProtoUpnpSoapRequestAddUI2(pProtoUpnp, "NewExternalPort", pProtoUpnp->iPortToMapExt); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewProtocol", "UDP"); + _ProtoUpnpSoapRequestClose(pProtoUpnp); + _ProtoUpnpSoapRequestPost(pProtoUpnp); + + // set to poll for response + pProtoUpnp->eState = ST_DELPORTMAPPING; + return(0); + } + // start getexternalipaddress request + if (iControl == 'gadr') + { + // post GetExternalIPAddress request + _ProtoUpnpSoapRequestOpen(pProtoUpnp, "GetExternalIPAddress"); + _ProtoUpnpSoapRequestClose(pProtoUpnp); + _ProtoUpnpSoapRequestPost(pProtoUpnp); + + // set to getextaddr state + pProtoUpnp->eState = ST_GETEXTADDR; + return(0); + } + // start getgenericportmapping request + if (iControl == 'ggpt') + { + // post GetSpecificPortMappingEntry request + _ProtoUpnpSoapRequestOpen(pProtoUpnp, "GetGenericPortMappingEntry"); + _ProtoUpnpSoapRequestAddUI2(pProtoUpnp, "NewPortMappingIndex", iValue); + _ProtoUpnpSoapRequestClose(pProtoUpnp); + _ProtoUpnpSoapRequestPost(pProtoUpnp); + + // set to poll for response + pProtoUpnp->eState = ST_GETPORTMAPPING; + return(0); + } + // start getspecificportmapping request + if (iControl == 'gprt') + { + // get remote host address + _ProtoUpnpGetRemoteHost(pProtoUpnp, strAddrText, sizeof(strAddrText)); + + // post GetSpecificPortMappingEntry request + _ProtoUpnpSoapRequestOpen(pProtoUpnp, "GetSpecificPortMappingEntry"); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewRemoteHost", strAddrText); + _ProtoUpnpSoapRequestAddUI2(pProtoUpnp, "NewExternalPort", pProtoUpnp->iPortToMapExt); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "NewProtocol", "UDP"); + _ProtoUpnpSoapRequestClose(pProtoUpnp); + _ProtoUpnpSoapRequestPost(pProtoUpnp); + + // set to poll for response + pProtoUpnp->eState = ST_GETPORTMAPPING; + return(0); + } + // start QueryStateVariable request + if (iControl == 'gvar') + { + // post QueryStateVariable request + _ProtoUpnpSoapRequestOpen(pProtoUpnp, "QueryStateVariable"); + _ProtoUpnpSoapRequestAddStr(pProtoUpnp, "varName", pValue); + _ProtoUpnpSoapRequestClose(pProtoUpnp); + _ProtoUpnpSoapRequestPost(pProtoUpnp); + + // set to poll for response + pProtoUpnp->eState = ST_GETSTATEVAR; + return(0); + } + // get service description + if (iControl == 'sdsc') + { + // initiate description fetch operation + NetPrintf(("protoupnp: sending service description request\n")); + ds_strnzcpy(pProtoUpnp->strRequestName, "ServiceDesc", sizeof(pProtoUpnp->strRequestName)); + _ProtoUpnpHttpReset(pProtoUpnp); + ProtoHttpGet(pProtoUpnp->pProtoHttp, pProtoUpnp->Device.Services[pProtoUpnp->iService].strDescriptionUrl, FALSE); + + // set to description state + pProtoUpnp->eState = ST_SERVICEDESC; + return(0); + } + // unhandled + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function ProtoUpnpUpdate + + \Description + Update the ProtoUpnp module. + + \Input *pProtoUpnp - pointer to module state + + \Version 03/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoUpnpUpdate(ProtoUpnpRefT *pProtoUpnp) +{ + int32_t iCurTick = NetTick(); + int32_t iResult; + + NetCritEnter(&pProtoUpnp->Crit); + + // update in idle state + if (pProtoUpnp->eState == ST_IDLE) + { + // update command list + if (pProtoUpnp->pCommandList != NULL) + { + // get next command + const ProtoUpnpMacroT *pMacro = ++pProtoUpnp->pCommandList; + + // process command + if (pMacro->iControl != 0) + { + NetPrintf(("protoupnp: command list -> %c%c%c%c\n", + (pMacro->iControl>>24)&0xff, (pMacro->iControl>>16)&0xff, + (pMacro->iControl>>8)&0xff, pMacro->iControl&0xff)); + ProtoUpnpControl(pProtoUpnp, pMacro->iControl, pMacro->iValue, pMacro->iValue2, pMacro->pValue); + } + else + { + NetPrintf(("protoupnp: command list complete\n")); + pProtoUpnp->pCommandList = NULL; + } + } + } + + // update during discovery process + if (pProtoUpnp->eState == ST_DISCOVERY) + { + struct sockaddr RemoteAddr; + int32_t iResponseSize, iAddrLen = sizeof(RemoteAddr); + + // send out a discovery broadcast? + if (NetTickDiff(iCurTick, pProtoUpnp->iDiscoveryTimer) >= PROTOUPNP_DISCOVERYINTERVAL) + { + // send discovery request + _ProtoUpnpSendDiscoveryRequest(pProtoUpnp); + + // set timer for next broadcast + pProtoUpnp->iDiscoveryTimer = iCurTick; + } + + // poll for discovery response + if ((iResponseSize = SocketRecvfrom(pProtoUpnp->pUdp, pProtoUpnp->strRecvBuf, SOCKET_MAXUDPRECV, 0, &RemoteAddr, &iAddrLen)) > 0) + { + // add null-termination + pProtoUpnp->strRecvBuf[iResponseSize] = '\0'; + + // parse the response + _ProtoUpnpParseDiscoveryResponse(pProtoUpnp, pProtoUpnp->strRecvBuf); + + // update status + pProtoUpnp->iStatus |= PROTOUPNP_STATUS_DISCOVERED; + } + } + + // update during description process + if (pProtoUpnp->eState == ST_DESCRIPTION) + { + // check for response + if ((iResult = _ProtoUpnpHttpWaitResponse(pProtoUpnp)) > 0) + { + // parse the response + if (_ProtoUpnpXmlParseDescription(pProtoUpnp) >= 0) + { + pProtoUpnp->iStatus |= PROTOUPNP_STATUS_DESCRIBED; + } + else + { + _ProtoUpnpError(pProtoUpnp, "parsing description"); + } + } + else if (iResult < 0) + { + _ProtoUpnpError(pProtoUpnp, "getting description"); + } + } + + // update during service description process + if (pProtoUpnp->eState == ST_SERVICEDESC) + { + // check for response + if ((iResult = _ProtoUpnpHttpWaitResponse(pProtoUpnp)) > 0) + { + // $$ todo - handle service description response + } + else if (iResult < 0) + { + _ProtoUpnpError(pProtoUpnp, "getting service description"); + } + } + + // update during QueryStateVariable request + if (pProtoUpnp->eState == ST_GETSTATEVAR) + { + // check for response + if ((iResult = _ProtoUpnpHttpWaitResponse(pProtoUpnp)) > 0) + { + // $$ todo - handle querystatevariable response + } + else if (iResult < 0) + { + _ProtoUpnpError(pProtoUpnp, "querying state variable"); + } + } + + // update during GetExternalIPAddress request + if (pProtoUpnp->eState == ST_GETEXTADDR) + { + // check for response + if ((iResult = _ProtoUpnpSoapWaitResponse(pProtoUpnp)) > 0) + { + // parse the response + if (_ProtoUpnpXmlParseGetExtAddr(pProtoUpnp) >= 0) + { + pProtoUpnp->iStatus |= PROTOUPNP_STATUS_GOTEXTADDR; + } + else + { + // didn't find it, so check the next service + if (pProtoUpnp->iService < (pProtoUpnp->Device.iNumServices-1)) + { + pProtoUpnp->iService += 1; + ProtoUpnpControl(pProtoUpnp, 'gadr', 0, 0, NULL); + } + } + } + else if (iResult < 0) + { + _ProtoUpnpError(pProtoUpnp, "getting external address"); + } + } + + // update during GetSpecificPortMappingEntry request + if (pProtoUpnp->eState == ST_GETPORTMAPPING) + { + // check for response + if ((iResult = _ProtoUpnpSoapWaitResponse(pProtoUpnp)) != 0) + { + // if we got a success response, that means a mapping already exists + if (iResult > 0) + { + NetPrintf(("protoupnp: mapping already exists\n")); + _ProtoUpnpXmlParseGetPortMapping(pProtoUpnp); + pProtoUpnp->iStatus |= PROTOUPNP_STATUS_FNDPORTMAP; + pProtoUpnp->bPortIsMapped = TRUE; + } + else if (pProtoUpnp->iSoapError == 714) + { + NetPrintf(("protoupnp: mapping does not exist\n")); + pProtoUpnp->bPortIsMapped = FALSE; + } + else if (pProtoUpnp->iSoapError == 501) + { + // not supported? try continuing on anyway (BEFSR41) + NetPrintf(("protoupnp: unable to query mapping\n")); + pProtoUpnp->bPortIsMapped = FALSE; + } + else // some other error, so bail + { + _ProtoUpnpError(pProtoUpnp, "getting portmapping"); + } + } + } + + // update during DeletePortMapping request + if (pProtoUpnp->eState == ST_DELPORTMAPPING) + { + // check for response + if ((iResult = _ProtoUpnpSoapWaitResponse(pProtoUpnp)) != 0) + { + // if we got a success response, that means we've successfully deleted the mapping + if (iResult > 0) + { + NetPrintf(("protoupnp: deleted port mapping\n")); + pProtoUpnp->iStatus |= PROTOUPNP_STATUS_DELPORTMAP; + pProtoUpnp->bPortIsMapped = FALSE; + } + else if (pProtoUpnp->iSoapError == 714) + { + NetPrintf(("protoupnp: mapping does not exist\n")); + pProtoUpnp->bPortIsMapped = FALSE; + } + else // some other error, so bail + { + _ProtoUpnpError(pProtoUpnp, "deleting port mapping"); + } + } + } + + // update during AddPortMapping request + if (pProtoUpnp->eState == ST_ADDPORTMAPPING) + { + // check for response + if ((iResult = _ProtoUpnpSoapWaitResponse(pProtoUpnp)) != 0) + { + // if we got a success response, that means the mapping succeeded + if (iResult > 0) + { + pProtoUpnp->iStatus |= PROTOUPNP_STATUS_ADDPORTMAP; + NetPrintf(("protoupnp: port mapping added\n")); + } + else + { + uint8_t bRetry = TRUE; + if (pProtoUpnp->iSoapError == 716) + { + NetPrintf(("protoupnp: wildcard not permitted in ext port\n")); + bRetry = FALSE; + } + else if (pProtoUpnp->iSoapError == 725) + { + NetPrintf(("protoupnp: finite lease duration not supported -- trying again with infinite\n")); + pProtoUpnp->iLeaseDuration = 0; + } + else if (pProtoUpnp->iSoapError == 726) + { + NetPrintf(("protoupnp: specific remote host not supported -- trying again with wildcard\n")); + pProtoUpnp->iRemoteHost = 0; + } + else if (pProtoUpnp->iLeaseDuration != 0) + { + NetPrintf(("protoupnp: unknown error adding port mapping -- trying again with infinite lease duration\n")); + pProtoUpnp->iLeaseDuration = 0; + } + else if (pProtoUpnp->iRemoteHost != 0) + { + NetPrintf(("protoupnp: unknown error adding port mapping -- trying again with wildcard source IP\n")); + pProtoUpnp->iRemoteHost = 0; + } + else + { + // nothing else to try, so just fail + bRetry = FALSE; + } + + // retry or fail? + if (bRetry) + { + ProtoUpnpControl(pProtoUpnp, 'aprt', 0, 0, NULL); + } + else + { + _ProtoUpnpError(pProtoUpnp, "adding port mapping"); + } + } + } + } + + NetCritLeave(&pProtoUpnp->Crit); +} diff --git a/r5dev/thirdparty/dirtysdk/source/proto/protowebsocket.c b/r5dev/thirdparty/dirtysdk/source/proto/protowebsocket.c new file mode 100644 index 00000000..b90cc6db --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/proto/protowebsocket.c @@ -0,0 +1,2051 @@ +/*H********************************************************************************/ +/*! + \File protowebsocket.c + + \Description + This module implements a WebSocket client interface as documented by + RFC 6455 (http://tools.ietf.org/html/rfc6455). The current version of the + WebSockets protocol implemented here is version 13. Up to date registry + information regarding new extensions, protocols, result codes, etc. can be + found at http://www.iana.org/assignments/websocket/websocket.xml. + + \Notes + While the standard supports up to a 64-bit frame size, this implementation + only supports up to 32-bit frame sizes. + + \Todo + - Sec-WebSocket-Protocols support + + \Copyright + Copyright (c) 2012 Electronic Arts Inc. + + \Version 11/26/2012 (jbrookes) First Version + \Version 03/31/2017 (jbrookes) Added frame send/recv capability +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtyvers.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/crypt/cryptrand.h" +#include "DirtySDK/crypt/cryptsha1.h" +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/proto/protossl.h" +#include "DirtySDK/util/base64.h" + +#include "DirtySDK/proto/protowebsocket.h" + +/*** Defines **********************************************************************/ + +//! ProtoWebSocket revision number (maj.min) +#define PROTOWEBSOCKET_VERSION (0x0100) // update this for major bug fixes or protocol additions/changes + +//! default ProtoWebSocket timeout +#define PROTOWEBSOCKET_TIMEOUT (5*30*1000) + +//! define WebSocket protocol version +#define PROTOWEBSOCKET_PROTOCOLVERS (13) + +//! default protowebsocket buffer size +#define PROTOWEBSOCKET_BUFSIZE_MIN (4*1024) + +// opcode definitions +#define PROTOWEBSOCKET_OPCODE_BASE (0x0) +#define PROTOWEBSOCKET_OPCODE_CONT (PROTOWEBSOCKET_OPCODE_BASE+0) +#define PROTOWEBSOCKET_OPCODE_TEXT (PROTOWEBSOCKET_OPCODE_BASE+1) +#define PROTOWEBSOCKET_OPCODE_BINA (PROTOWEBSOCKET_OPCODE_BASE+2) +// control frame type definitions +#define PROTOWEBSOCKET_CTRL_BASE (0x8) +#define PROTOWEBSOCKET_CTRL_CLSE (PROTOWEBSOCKET_CTRL_BASE+0) +#define PROTOWEBSOCKET_CTRL_PING (PROTOWEBSOCKET_CTRL_BASE+1) +#define PROTOWEBSOCKET_CTRL_PONG (PROTOWEBSOCKET_CTRL_BASE+2) +// fragment end +#define PROTOWEBSOCKET_OPCODE_FIN (0x80) + +// max WebSocket protocol send header size +#define PROTOWEBSOCKET_MAXSENDHDR (12) + +//! default size for our temporary input buffer +#define PROTOWEBSOCKET_DEFAULT_INPUTBUFSIZE (256) + +/*** Type Definitions *************************************************************/ + +// state for a websocket connection +struct ProtoWebSocketRefT +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + ProtoSSLRefT *pProtoSSL; + + enum + { + ST_DISC, //!< disconnected + ST_CONN, //!< connecting + ST_SEND, //!< sending handshake + ST_RECV, //!< receiving handshake + ST_OPEN, //!< connected + ST_FAIL //!< connection failed + } eState; //!< current state + + int32_t iVersion; //!< protocol version + + char *pOutBuf; //!< output buffer + int32_t iOutMax; //!< max amount of data that can be stored in the buffer + int32_t iOutLen; //!< current amount of data in buffer + int32_t iOutOff; //!< current offset within buffer + int32_t iRecvRslt; + int32_t iRecvSize; //!< used by RecvFrame to track total received size + + char *pInpBuf; //!< temporary input buffer used for connection establishment + int32_t iInpMax; //!< max amount of data that can be stored in the buffer + int32_t iInpLen; //!< current amount of data in buffer + int32_t iInpOff; //!< current offset within buffer + + int32_t iFrameHdrOff; + int32_t iFrameHdrLen; + int32_t iFrameLen; + int32_t iFrameType; + + int32_t iCtrlDataLen; //!< length of control packet data + int32_t iCtrlDataOff; //!< offset while receiving control packet data + + int32_t iVerbose; + uint32_t uTimer; + int32_t iTimeout; + int32_t iKeepAlive; //!< if >0, number of seconds before issuing a keep-alive pong + uint32_t uKeepTimer; + int32_t iSSLFail; + int32_t iCloseReason; + + char *pAppendHdr; //!< append header buffer pointer + int32_t iAppendLen; //!< size of append header buffer + + int32_t iPort; //!< current connection port + + uint8_t bTimeout; + uint8_t bDoPong; //!< execute a 'pong' frame at earliest opportunity + uint8_t bDoClose; //!< execute a 'close' frame at earliest opportunity; disconnect + uint8_t bClosing; //!< have sent a close frame + uint8_t bRecvCtrlData; //!< currently receiving control data + uint8_t bMessageSend; //!< if true, we're sending a message (FIN bit not sent until message is complete) + uint8_t bFrameFrag; //!< if true, we're sending a fragmented frame + uint8_t bFrameFin; //!< if true this packet is a fin packet + + char strHost[256]; //!< server name + char strFrameHdr[PROTOWEBSOCKET_MAXSENDHDR]; //!< cache to receive frame header into + + char strRawKey[32]; //!< generated key sent to server; must hold 16 characters base64 encoded + char strEncKey[32]; //!< encoded key we expect to get back from server; must hold 16 characters base64 encoded + + char strCtrlData[256]; //!< cached control packet data +}; + +/*** Variables ********************************************************************/ + +#if DIRTYCODE_LOGGING +static const char *_ProtoWebSocket_strOpcodeNames[16] = +{ + "cont", + "text", + "bina", + "", "", "", "", "", + "clse", + "ping", + "pong", + "", "", "", "", "" +}; +static const char *_ProtoWebSocket_strCloseReasons[PROTOWEBSOCKET_CLOSEREASON_COUNT] = +{ + "normal", + "going away", + "protocol error", + "unsupported data", + "reserved", + "no status received", + "abnormal closure", + "invalid frame data", + "policy violation", + "message too big", + "mandatory extension", + "internal error", + "service restart", + "try again later", + "reserved", + "tls handshake", +}; +#endif + +/*** Function Prototypes **********************************************************/ + +static int32_t _ProtoWebSocketRecvData(ProtoWebSocketRefT *pWebSocket, char *pStrBuf, int32_t iSize); + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSetAppendHeader + + \Description + Set given string as append header, allocating memory as required. + + \Input *pWebSocket - module state + \Input *pAppendHdr - append header string + + \Output + int32_t - zero=success, else error + + \Version 12/04/2012 (jbrookes) Copied from ProtoHttp +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketSetAppendHeader(ProtoWebSocketRefT *pWebSocket, const char *pAppendHdr) +{ + int32_t iAppendBufLen, iAppendStrLen; + + // check for empty append string, in which case we free the buffer + if ((pAppendHdr == NULL) || (*pAppendHdr == '\0')) + { + if (pWebSocket->pAppendHdr != NULL) + { + DirtyMemFree(pWebSocket->pAppendHdr, PROTOHTTP_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData); + pWebSocket->pAppendHdr = NULL; + } + pWebSocket->iAppendLen = 0; + return(0); + } + + // check to see if append header is already set + if ((pWebSocket->pAppendHdr != NULL) && (!strcmp(pAppendHdr, pWebSocket->pAppendHdr))) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] ignoring set of append header '%s' that is already set\n", pWebSocket, pAppendHdr)); + return(0); + } + + // get append header length + iAppendStrLen = (int32_t)strlen(pAppendHdr); + // append buffer size includes null and space for \r\n if not included by submitter + iAppendBufLen = iAppendStrLen + 3; + + // see if we need to allocate a new buffer + if (iAppendBufLen > pWebSocket->iAppendLen) + { + if (pWebSocket->pAppendHdr != NULL) + { + DirtyMemFree(pWebSocket->pAppendHdr, PROTOHTTP_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData); + } + if ((pWebSocket->pAppendHdr = DirtyMemAlloc(iAppendBufLen, PROTOHTTP_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData)) != NULL) + { + pWebSocket->iAppendLen = iAppendBufLen; + } + else + { + NetPrintf(("protowebsocket: [%p] could not allocate %d byte buffer for append header\n", pWebSocket, iAppendBufLen)); + pWebSocket->iAppendLen = 0; + return(-1); + } + } + + // copy append header + ds_strnzcpy(pWebSocket->pAppendHdr, pAppendHdr, iAppendStrLen+1); + + // if append header is not \r\n terminated, do it here + if (pAppendHdr[iAppendStrLen-2] != '\r' || pAppendHdr[iAppendStrLen-1] != '\n') + { + ds_strnzcat(pWebSocket->pAppendHdr, "\r\n", pWebSocket->iAppendLen); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketGenerateKey + + \Description + Generate random base64-encoded key to be sent to server in initial + handshake, and base64-encoded SHA1 hash of the key that we expect + from the server in response. + + \Input *pWebSocket - module state + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoWebSocketGenerateKey(ProtoWebSocketRefT *pWebSocket) +{ + char strAccept[128]; + uint8_t aSha1Data[CRYPTSHA1_HASHSIZE]; + CryptSha1T Sha1State; + uint8_t aRandom[16]; + + // get 16-digit random number + CryptRandGet(aRandom, sizeof(aRandom)); + + // base64 encode string + Base64Encode2((char *)aRandom, sizeof(aRandom), pWebSocket->strRawKey, sizeof(pWebSocket->strRawKey)); + + // now, generate accept header we must receive to validate response + + // concatentate key with hardcoded GUID as per spec + ds_strnzcpy(strAccept, pWebSocket->strRawKey, sizeof(strAccept)); + ds_strnzcat(strAccept, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", sizeof(strAccept)); + + // hash the key with SHA1 + CryptSha1Init(&Sha1State); + CryptSha1Update(&Sha1State, (uint8_t *)strAccept, (uint32_t)strlen(strAccept)); + CryptSha1Final(&Sha1State, aSha1Data, CRYPTSHA1_HASHSIZE); + + // base64 encode for final accept key + Base64Encode2((char *)aSha1Data, CRYPTSHA1_HASHSIZE, pWebSocket->strEncKey, sizeof(pWebSocket->strEncKey)); + + NetPrintfVerbose((pWebSocket->iVerbose, 2, "protowebsocket: [%p] raw key=%s\n", pWebSocket, pWebSocket->strRawKey)); + NetPrintfVerbose((pWebSocket->iVerbose, 2, "protowebsocket: [%p] enc key=%s\n", pWebSocket, pWebSocket->strEncKey)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketFormatHeader + + \Description + Format websocket handshake header + + \Input *pWebSocket - module state + \Input *pHost - websocket host + \Input *pUrl - websocket url + \Input iPort - protocol port + \Input iSecure - true=secure, else false + + \Output + int32_t - positive=success, negative=failure + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketFormatHeader(ProtoWebSocketRefT *pWebSocket, const char *pHost, const char *pUrl, int32_t iPort, int32_t iSecure) +{ + int32_t iBufSize = pWebSocket->iOutMax, iBufLen = 0; + + // if no url specified use the minimum + if (*pUrl == '\0') + { + pUrl = "/"; + } + + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "GET %s HTTP/1.1\r\n", pUrl); + if ((iSecure && (iPort == 443)) || (iPort == 80)) + { + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "Host: %s\r\n", pHost); + } + else + { + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "Host: %s:%d\r\n", pHost, iPort); + } + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "Upgrade: websocket\r\n"); + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "Connection: upgrade\r\n"); + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "Sec-WebSocket-Key: %s\r\n", pWebSocket->strRawKey); + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "Sec-WebSocket-Version: %d\r\n", pWebSocket->iVersion); + if ((pWebSocket->pAppendHdr == NULL) || !ds_stristr(pWebSocket->pAppendHdr, "User-Agent:")) + { + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "User-Agent: ProtoWebSocket %d.%d/DS %d.%d.%d.%d.%d (" DIRTYCODE_PLATNAME ")\r\n", + (PROTOWEBSOCKET_VERSION>>8)&0xff, PROTOWEBSOCKET_VERSION&0xff, DIRTYSDK_VERSION_YEAR, DIRTYSDK_VERSION_SEASON, + DIRTYSDK_VERSION_MAJOR, DIRTYSDK_VERSION_MINOR, DIRTYSDK_VERSION_PATCH); + } + if ((pWebSocket->pAppendHdr == NULL) || (pWebSocket->pAppendHdr[0] == '\0')) + { + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "Accept: */*\r\n"); + } + else + { + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "%s", pWebSocket->pAppendHdr); + } + iBufLen += ds_snzprintf(pWebSocket->pOutBuf+iBufLen, iBufSize-iBufLen, "\r\n"); + pWebSocket->iOutLen = iBufLen; + pWebSocket->iOutOff = 0; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketDisconnect + + \Description + Disconnect from server + + \Input *pWebSocket - module state + \Input iNewState - new state to set (fail or disc) + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketDisconnect(ProtoWebSocketRefT *pWebSocket, int32_t iNewState) +{ + NetPrintfVerbose((pWebSocket->iVerbose, 0, "protowebsocket: [%p] disconnecting\n", pWebSocket)); + pWebSocket->eState = iNewState; + if (iNewState == ST_FAIL) + { + pWebSocket->iSSLFail = ProtoSSLStat(pWebSocket->pProtoSSL, 'fail', NULL, 0); + } + return(ProtoSSLDisconnect(pWebSocket->pProtoSSL)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketReset + + \Description + Reset module state + + \Input *pWebSocket - module state + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoWebSocketReset(ProtoWebSocketRefT *pWebSocket) +{ + // if we're not already disconnected, disconnect now + if (pWebSocket->eState != ST_DISC && pWebSocket->eState != ST_FAIL) + { + _ProtoWebSocketDisconnect(pWebSocket, ST_DISC); + } + + // reset state + pWebSocket->iOutLen = 0; + pWebSocket->iOutOff = 0; + pWebSocket->iInpLen = 0; + pWebSocket->iInpOff = 0; + pWebSocket->iRecvRslt = 0; + pWebSocket->iRecvSize = 0; + pWebSocket->iFrameHdrOff = 0; + pWebSocket->iFrameHdrLen = 2; // minimal header size + pWebSocket->iFrameLen = 0; + pWebSocket->iFrameType = 0; + pWebSocket->iCtrlDataLen = 0; + pWebSocket->iSSLFail = 0; + pWebSocket->bTimeout = FALSE; + pWebSocket->bDoPong = FALSE; + pWebSocket->bDoClose = FALSE; + pWebSocket->bClosing = FALSE; + pWebSocket->bFrameFrag = FALSE; + pWebSocket->bFrameFin = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketFail + + \Description + Handle a socket failure + + \Input *pWebSocket - module state + \Input iResult - result from a socket operation + \Input *pStrDebug - debug text indicating failure context + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoWebSocketFail(ProtoWebSocketRefT *pWebSocket, int32_t iResult, const char *pStrDebug) +{ + if (pWebSocket->eState == ST_FAIL) + { + return; + } + NetPrintf(("protowebsocket: [%p] failure: %s (err=%d)\n", pWebSocket, pStrDebug, iResult)); + _ProtoWebSocketDisconnect(pWebSocket, ST_FAIL); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketValidateHeader + + \Description + Validate server response header + + \Input *pWebSocket - module state + \Input *pBuffer - buffer holding received header + \Input *pHeader - name of header to validate + \Input *pValue - header value to validate + + \Output + int32_t - zero=success, negative=failure + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketValidateHeader(ProtoWebSocketRefT *pWebSocket, const char *pBuffer, const char *pHeader, const char *pValue) +{ + char strDebug[256]; + char strHdrVal[128]; + + if (ProtoHttpGetHeaderValue(NULL, pBuffer, pHeader, strHdrVal, sizeof(strHdrVal), NULL) < 0) + { + ds_snzprintf(strDebug, sizeof(strDebug), "missing %s header", pHeader); + _ProtoWebSocketFail(pWebSocket, -1, strDebug); + return(-1); + } + else if (ds_stricmp(strHdrVal, pValue)) + { + ds_snzprintf(strDebug, sizeof(strDebug), "invalid %s header value '%s'", pHeader, pValue); + _ProtoWebSocketFail(pWebSocket, -1, strDebug); + return(-1); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketProcessHeader + + \Description + Process websocket handshake server response + + \Input *pWebSocket - module state + \Input *pBuffer - buffer holding received header + + \Output + int32_t - positive=success, negative=failure + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketProcessHeader(ProtoWebSocketRefT *pWebSocket, const char *pBuffer) +{ + int32_t iHdrCode; + + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] handshake response received\n", pWebSocket)); + #if DIRTYCODE_LOGGING + if (pWebSocket->iVerbose > 2) + { + NetPrintWrap(pBuffer, 80); + } + #endif + + /* as per https://www.w3.org/TR/websockets/ 4.8: When the user agent validates the server's response during + the "establish a WebSocket connection" algorithm, if the status code received from the server is not 101 + (e.g. it is a redirect), the user agent must fail the WebSocket connection. */ + + // validate 101 response + iHdrCode = ProtoHttpParseHeaderCode(pBuffer); + if (iHdrCode != 101) + { + _ProtoWebSocketFail(pWebSocket, iHdrCode, "invalid http response code"); + return(-1); + } + // validate "upgrade: websocket" header + if (_ProtoWebSocketValidateHeader(pWebSocket, pBuffer, "upgrade", "websocket") < 0) + { + return(-2); + } + // validate "connection: upgrade" header + if (_ProtoWebSocketValidateHeader(pWebSocket, pBuffer, "connection", "upgrade") < 0) + { + return(-3); + } + // validate accept header + if (_ProtoWebSocketValidateHeader(pWebSocket, pBuffer, "sec-websocket-accept", pWebSocket->strEncKey) < 0) + { + return(-4); + } + + //$$TODO - support Sec-WebSocket-Extensions? + //$$TODO - support Sec-WebSocket-Protocol? + + pWebSocket->eState = ST_OPEN; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSend + + \Description + Try and send some data. If data is sent, the timout value is updated. + + \Input *pWebSocket - module state + \Input *pStrBuf - pointer to buffer to send from + \Input iSize - amount of data to try and send + + \Output + int32_t - negative=error, else number of bytes sent + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketSend(ProtoWebSocketRefT *pWebSocket, const char *pStrBuf, int32_t iSize) +{ + int32_t iResult; + + // try and send some data + if ((iResult = ProtoSSLSend(pWebSocket->pProtoSSL, pStrBuf, iSize)) > 0) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] sent %d bytes\n", pWebSocket, iResult)); + #if DIRTYCODE_LOGGING + if (pWebSocket->iVerbose > 3) + { + NetPrintMem(pStrBuf, iResult, "ws-send"); + } + #endif + + // sent data, so update timer + pWebSocket->uTimer = NetTick(); + } + else if (iResult < 0) + { + NetPrintf(("protowebsocket: [%p] error %d sending %d bytes\n", pWebSocket, iResult, iSize)); + pWebSocket->eState = ST_FAIL; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSendBuff + + \Description + Send buffered data + + \Input *pWebSocket - module state + + \Output + int32_t - zero=in progress, positive=done, negative=failure + + \Version 11/29/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketSendBuff(ProtoWebSocketRefT *pWebSocket) +{ + int32_t iResult; + if ((iResult = _ProtoWebSocketSend(pWebSocket, pWebSocket->pOutBuf+pWebSocket->iOutOff, pWebSocket->iOutLen-pWebSocket->iOutOff)) > 0) + { + pWebSocket->iOutOff += iResult; + if (pWebSocket->iOutOff == pWebSocket->iOutLen) + { + iResult = pWebSocket->iOutLen; + pWebSocket->iOutLen = pWebSocket->iOutOff = 0; + } + else + { + iResult = 0; + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSetLength + + \Description + Endode message length in the format websocket protocol reqires + + \Input *pWebSocket - module state + \Input *pBuffer - buffer to encode to + \Input iMask - true if data is masked, else false + \Input iLength - length to set + + \Output + uint8_t * - pointer past end of encoded length + + \Notes + While the standard supports up to a 64-bit frame size, this implementation + only supports up to 32-bit frame sizes. + + \Version 11/29/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t *_ProtoWebSocketSetLength(ProtoWebSocketRefT *pWebSocket, uint8_t *pBuffer, int32_t iMask, int32_t iLength) +{ + iMask = iMask ? 0x80 : 0; + if (iLength < 126) + { + *pBuffer++ = (uint8_t)iLength | (uint8_t)iMask; + } + else if (iLength < 65536) + { + *pBuffer++ = (uint8_t)126 | (uint8_t)iMask; + *pBuffer++ = (uint8_t)(iLength >> 8); + *pBuffer++ = (uint8_t)(iLength >> 0); + } + else + { + *pBuffer++ = (uint8_t)127 | (uint8_t)iMask; + *pBuffer++ = 0; + *pBuffer++ = 0; + *pBuffer++ = 0; + *pBuffer++ = 0; + *pBuffer++ = (uint8_t)(iLength >> 24); + *pBuffer++ = (uint8_t)(iLength >> 16); + *pBuffer++ = (uint8_t)(iLength >> 8); + *pBuffer++ = (uint8_t)(iLength >> 0); + } + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSetMask + + \Description + Generate and encode mask; required for sending data from client to server + + \Input *pWebSocket - module state + \Input *pBuffer - [out] buffer to write mask to + \Input **ppMask - [out] storage for pointer to mask + + \Output + uint8_t * - pointer past end of encoded mask + + \Version 11/29/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static uint8_t *_ProtoWebSocketSetMask(ProtoWebSocketRefT *pWebSocket, uint8_t *pBuffer, uint8_t **ppMask) +{ + // generate four-byte random mask + CryptRandGet(pBuffer, 4); + // save pointer to mask for caller + *ppMask = pBuffer; + // print mask for extended diagnostics + NetPrintfVerbose((pWebSocket->iVerbose, 2, "protowebsocket: [%p] mask=0x%02x%02x%02x%02x\n", pWebSocket, + pBuffer[0], pBuffer[1], pBuffer[2], pBuffer[3])); + return(pBuffer+4); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSendData + + \Description + Send masked data from client to server. + + \Input *pWebSocket - module state + \Input *pBuffer - data to send + \Input iLength - length of data to send + \Input uOpcode - frame type + + \Output + int32_t - number of data bytes sent + + \Version 11/29/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketSendData(ProtoWebSocketRefT *pWebSocket, const char *pBuffer, int32_t iLength, uint8_t uOpcode) +{ + uint8_t *pPacket = (uint8_t *)pWebSocket->pOutBuf, *pMask; + int32_t iBufIdx, iOutMax, iResult; + uint8_t uFrameFin = PROTOWEBSOCKET_OPCODE_FIN; + + // only allow if there's not a send in progress + if (pWebSocket->iOutLen != 0) + { + return(0); + } + + // opcode is only set for the first packet of a fragmented message + if (pWebSocket->bMessageSend && pWebSocket->bFrameFrag) + { + uOpcode = 0; + } + + // calculate available space, while reserving max header size + iOutMax = pWebSocket->iOutMax - PROTOWEBSOCKET_MAXSENDHDR; + if (iLength > iOutMax) + { + // for message sending, clear FIN bit when it isn't the final frame + if (pWebSocket->bMessageSend) + { + uFrameFin = 0; + pWebSocket->bFrameFrag = TRUE; + } + // constrain send length to max available space + iLength = iOutMax; + } + else if (pWebSocket->bMessageSend) + { + // reset fragmentation flag on final frame of message + pWebSocket->bFrameFrag = FALSE; + } + + // set packet opcode + *pPacket++ = uOpcode|uFrameFin; + // set packet length + pPacket = _ProtoWebSocketSetLength(pWebSocket, pPacket, 1, iLength); + // set packet mask + pPacket = _ProtoWebSocketSetMask(pWebSocket, pPacket, &pMask); + + // mask the data + for (iBufIdx = 0; iBufIdx < iLength; iBufIdx += 1) + { + *pPacket++ = *pBuffer++ ^ pMask[iBufIdx&0x3]; + } + + // set buffer parms + pWebSocket->iOutOff = 0; + pWebSocket->iOutLen = (int32_t)(pPacket-(uint8_t *)pWebSocket->pOutBuf); + + // try to send + if ((iResult = _ProtoWebSocketSendBuff(pWebSocket)) < 0) + { + return(iResult); + } + + #if DIRTYCODE_LOGGING + if (iResult > 0) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] sent frame fin=%d typ=%s len=%d\n", pWebSocket, ((uint8_t *)pWebSocket->pOutBuf)[0]>>7, + _ProtoWebSocket_strOpcodeNames[((uint8_t *)pWebSocket->pOutBuf)[0]&0xf], iResult)); + } + #endif + + + // return amount of data buffered + return(iLength); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSendUserData + + \Description + Send masked data from client to server making, wrapping the state error + checking + + \Input *pWebSocket - module state + \Input *pBuffer - data to send + \Input iLength - length of data to send + \Input uOpcode - frame type + + \Output + int32_t - number of data bytes sent + + \Version 08/30/2017 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketSendUserData(ProtoWebSocketRefT *pWebSocket, const char *pBuffer, int32_t iLength, uint8_t uOpcode) +{ + // handle not connected states + if ((pWebSocket->eState == ST_DISC) || (pWebSocket->eState == ST_FAIL)) + { + return(SOCKERR_NOTCONN); + } + if (pWebSocket->eState != ST_OPEN) + { + return(0); + } + return(_ProtoWebSocketSendData(pWebSocket, pBuffer, iLength, uOpcode)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSendClose + + \Description + Send a close notification to server (or queue it if we are in the middle + of another send). + + \Input *pWebSocket - module state + \Input iReason - (optional) close reason (PROTOWEBSOCKET_CLOSEREASON_*) + \Input *pStrReason - (optional) string close reason + + \Output + int32_t - number of bytes data sent + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketSendClose(ProtoWebSocketRefT *pWebSocket, int32_t iReason, const char *pStrReason) +{ + int32_t iDataLen, iResult; + + // if we've already sent a close frame, we don't want to send another + if (pWebSocket->bClosing) + { + return(0); + } + pWebSocket->bClosing = TRUE; + + // format reason data + if (iReason != 0) + { + iDataLen = 2; + pWebSocket->strCtrlData[0] = (iReason>>8)&0xff; + pWebSocket->strCtrlData[1] = iReason&0xff; + if (pStrReason != NULL) + { + ds_strnzcpy(pWebSocket->strCtrlData+2, pStrReason, sizeof(pWebSocket->strCtrlData)-2); + iDataLen += (int32_t)strlen(pWebSocket->strCtrlData+2); + } + } + else + { + pWebSocket->strCtrlData[0] = '\0'; + iDataLen = 0; + } + + // try immediate send + NetPrintfVerbose((pWebSocket->iVerbose, 0, "protowebsocket: [%p] closing connection (code=%d, text=%s)\n", pWebSocket, iReason, pStrReason)); + if ((iResult = _ProtoWebSocketSendData(pWebSocket, pWebSocket->strCtrlData, iDataLen, PROTOWEBSOCKET_CTRL_CLSE)) == 0) + { + // couldn't buffer for sending, so set up to try again + pWebSocket->bDoClose = TRUE; + pWebSocket->iCtrlDataLen = iDataLen; + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketSendPing + + \Description + Send a ping notification to server -- unlike close, we don't queue for + later sending if we can't send immediately. + + \Input *pWebSocket - module state + \Input *pStrData - (optional) string ping data + \Input iType - PING or PONG + + \Output + int32_t - number of bytes data sent + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketSendPing(ProtoWebSocketRefT *pWebSocket, const char *pStrData, int32_t iType) +{ + int32_t iDataLen = 0; + if (pStrData != NULL) + { + iDataLen = (int32_t)strlen(pStrData); + } + return(_ProtoWebSocketSendData(pWebSocket, pStrData, iDataLen, iType)); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketRecv + + \Description + Try and receive some data. If data is received, the timout value is + updated. + + \Input *pWebSocket - module state + \Input *pStrBuf - [out] pointer to buffer to receive into + \Input iSize - amount of data to try and receive + + \Output + int32_t - negative=error, else success + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketRecv(ProtoWebSocketRefT *pWebSocket, char *pStrBuf, int32_t iSize) +{ + // if we have no buffer space, don't try to receive + if (iSize == 0) + { + return(0); + } + + /* if we are receiving when in the RECV state or if we have no data already waiting + pull from the TCP buffer. when we are in the RECV state we need to pull into the temp + input buffer, hence why we need the state check */ + if ((pWebSocket->eState == ST_RECV) || (pWebSocket->iInpLen == 0)) + { + pWebSocket->iRecvRslt = ProtoSSLRecv(pWebSocket->pProtoSSL, pStrBuf, iSize); + } + // otherwise pull from the temp input buffer + else + { + iSize = DS_MIN(iSize, pWebSocket->iInpLen-pWebSocket->iInpOff); + ds_memcpy(pStrBuf, pWebSocket->pInpBuf+pWebSocket->iInpOff, iSize); + pWebSocket->iInpOff += iSize; + + // if we read the entire buffer, reset the variables + if (pWebSocket->iInpLen == pWebSocket->iInpOff) + { + pWebSocket->iInpLen = pWebSocket->iInpOff = 0; + } + pWebSocket->iRecvRslt = iSize; + } + + // handle the result + if (pWebSocket->iRecvRslt > 0) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] recv %d bytes\n", pWebSocket, pWebSocket->iRecvRslt)); + #if DIRTYCODE_LOGGING + if (pWebSocket->iVerbose > 3) + { + NetPrintMem(pStrBuf, pWebSocket->iRecvRslt, "ws-recv"); + } + #endif + + // received data, so update timer + pWebSocket->uTimer = NetTick(); + } + return(pWebSocket->iRecvRslt); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketCheckCtrl + + \Description + Check a control frame received from the server + + \Input *pWebSocket - module state + \Input *pStrBuf - pointer to buffer containing received data from server + \Input iSize - amount of data to received from the server + + \Version 11/29/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoWebSocketCheckCtrl(ProtoWebSocketRefT *pWebSocket, char *pStrBuf, int32_t iSize) +{ + if (pWebSocket->iFrameType == PROTOWEBSOCKET_CTRL_PING) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] got a ping with %d bytes of data\n", pWebSocket, iSize)); + // set to respond with a pong + pWebSocket->bDoPong = TRUE; + } + else if (pWebSocket->iFrameType == PROTOWEBSOCKET_CTRL_PONG) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] got a pong with %d bytes of data\n", pWebSocket, iSize)); + } + else if (pWebSocket->iFrameType == PROTOWEBSOCKET_CTRL_CLSE) + { + int32_t iCloseReason = ((uint8_t)pStrBuf[0]) << 8 | (uint8_t)pStrBuf[1]; + if ((iCloseReason >= PROTOWEBSOCKET_CLOSEREASON_BASE) && (iCloseReason <= PROTOWEBSOCKET_CLOSEREASON_MAX)) + { + NetPrintfVerbose((pWebSocket->iVerbose, 0, "protowebsocket: [%p] got a close with reason '%s' (len=%d)\n", pWebSocket, + _ProtoWebSocket_strCloseReasons[iCloseReason-PROTOWEBSOCKET_CLOSEREASON_BASE], iSize)); + pWebSocket->iCloseReason = iCloseReason; + } + else + { + NetPrintfVerbose((pWebSocket->iVerbose, 0, "protowebsocket: [%p] got a close with unknown reason %d (len=%d)\n", pWebSocket, + iCloseReason, iSize)); + pWebSocket->iCloseReason = PROTOWEBSOCKET_CLOSEREASON_UNKNOWN; + } + // disconnect + _ProtoWebSocketDisconnect(pWebSocket, ST_DISC); + } + else + { + NetPrintf(("protowebsocket: [%p] ignoring unknown control type %d (len=%d)\n", pWebSocket, pWebSocket->iFrameType, iSize)); + // ignore unknown frame type data? + } + + // done with control data, reset frame header info; this is only necessary for control messages with no associated data + pWebSocket->iFrameHdrOff = 0; + pWebSocket->iFrameHdrLen = 2; // minimum header size + pWebSocket->bFrameFin = FALSE; +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketCheckHeader + + \Description + Check a server->client header for completion + + \Input *pWebSocket - module state + \Input *pStrBuf - pointer to buffer containing received data from server + \Input iSize - amount of data received from the server + + \Output + int32_t - zero=in progress, else complete + + \Notes + While the standard supports up to a 64-bit frame size, this implementation + only supports up to 32-bit frame sizes. + + \Version 01/09/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketCheckHeader(ProtoWebSocketRefT *pWebSocket, char *pStrBuf, int32_t iSize) +{ + // check for minimum frame size + if (iSize < 2) + { + return(0); + } + // decode frame info + pWebSocket->bFrameFin = ((uint8_t *)pStrBuf)[0] >> 7; + pWebSocket->iFrameType = pStrBuf[0]&0x0f; + pWebSocket->iFrameLen = pStrBuf[1]; + // handle extended frame lengths + if (pWebSocket->iFrameLen == 126) + { + pWebSocket->iFrameHdrLen = 4; + } + else if (pWebSocket->iFrameLen == 127) + { + pWebSocket->iFrameHdrLen = 10; + } + // check extended header size + if (iSize < pWebSocket->iFrameHdrLen) + { + return(0); + } + // decode extended frame lengths + if (pWebSocket->iFrameLen == 126) + { + pWebSocket->iFrameLen = (((uint8_t *)pStrBuf)[2] << 8) | ((uint8_t *)pStrBuf)[3]; + } + else if (pWebSocket->iFrameLen == 127) + { + pWebSocket->iFrameLen = (((uint8_t *)pStrBuf)[6] << 24) | (((uint8_t *)pStrBuf)[7] << 16) | (((uint8_t *)pStrBuf)[8] << 8) | ((uint8_t *)pStrBuf)[9]; + } + // if it's a control frame, set up to process it + if (pWebSocket->iFrameType & PROTOWEBSOCKET_CTRL_BASE) + { + pWebSocket->iCtrlDataLen = pWebSocket->iFrameLen; + pWebSocket->iCtrlDataOff = 0; + } + return(1); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketRecvHeader + + \Description + Receive a server->client frame header. As headers are variable length + and we receive data directly into the user-supplied buffer, we process + the header separately from the data. + + \Input *pWebSocket - module state + + \Output + int32_t - negative=error, zero=in progress, positive=done + + \Version 01/09/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketRecvHeader(ProtoWebSocketRefT *pWebSocket) +{ + int32_t iResult; + + // receive first two header bytes + while ((iResult = _ProtoWebSocketRecv(pWebSocket, pWebSocket->strFrameHdr+pWebSocket->iFrameHdrOff, pWebSocket->iFrameHdrLen-pWebSocket->iFrameHdrOff)) > 0) + { + // accumulate result + pWebSocket->iFrameHdrOff += iResult; + // see if we have a valid header + if ((iResult = _ProtoWebSocketCheckHeader(pWebSocket, pWebSocket->strFrameHdr, pWebSocket->iFrameHdrOff)) > 0) + { + break; + } + } + #if DIRTYCODE_LOGGING + if (iResult > 0) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] recv frame fin=%d typ=%s len=%d\n", pWebSocket, ((uint8_t *)pWebSocket->strFrameHdr)[0]>>7, + _ProtoWebSocket_strOpcodeNames[pWebSocket->iFrameType], pWebSocket->iFrameLen)); + } + #endif + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketRecvCtrl + + \Description + Receive a control packet into internal buffer. Any data that doesn't + fit in the internal buffer is discarded. + + \Input *pWebSocket - module state + + \Output + int32_t - zero + + \Version 02/01/2013 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketRecvCtrl(ProtoWebSocketRefT *pWebSocket) +{ + char strTempBuf[256], *pStrBuf; + int32_t iSize; + + // receive into control data cache buffer (if we have enough room) + if (pWebSocket->iCtrlDataOff < (signed)sizeof(pWebSocket->strCtrlData)) + { + pStrBuf = pWebSocket->strCtrlData + pWebSocket->iCtrlDataOff; + iSize = (signed)sizeof(pWebSocket->strCtrlData) - pWebSocket->iCtrlDataOff; + } + else // no room, so just receive into stack buffer and throw out + { + pStrBuf = strTempBuf; + iSize = (signed)sizeof(strTempBuf); + } + pWebSocket->bRecvCtrlData = TRUE; + pWebSocket->iCtrlDataOff += _ProtoWebSocketRecvData(pWebSocket, pStrBuf, iSize); + pWebSocket->bRecvCtrlData = FALSE; + + // process if we've received the whole control frame + if ((pWebSocket->iCtrlDataOff == pWebSocket->iCtrlDataLen) && (pStrBuf != strTempBuf)) + { + _ProtoWebSocketCheckCtrl(pWebSocket, pStrBuf, pWebSocket->iCtrlDataLen); + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketRecvData + + \Description + Try and receive some data. + + \Input *pWebSocket - module state + \Input *pStrBuf - [out] pointer to buffer to receive into + \Input iSize - amount of data to try and receive + + \Output + int32_t - negative=error, else success + + \Version 11/27/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _ProtoWebSocketRecvData(ProtoWebSocketRefT *pWebSocket, char *pStrBuf, int32_t iSize) +{ + int32_t iResult; + + // receive header + if ((pWebSocket->iFrameLen == 0) && ((iResult = _ProtoWebSocketRecvHeader(pWebSocket)) <= 0)) + { + return(iResult); + } + + // receive control frame data + if ((pWebSocket->iFrameType & PROTOWEBSOCKET_CTRL_BASE) && (!pWebSocket->bRecvCtrlData)) + { + return(_ProtoWebSocketRecvCtrl(pWebSocket)); + } + + // limit read maximum to frame len + if (iSize > pWebSocket->iFrameLen) + { + iSize = pWebSocket->iFrameLen; + } + + // receive some data + if ((iResult = _ProtoWebSocketRecv(pWebSocket, pStrBuf, iSize)) > 0) + { + // decrement frame size by amount of user data received + pWebSocket->iFrameLen -= iResult; + // if we've received all of the data, reset header info + if (pWebSocket->iFrameLen == 0) + { + pWebSocket->iFrameHdrOff = 0; + pWebSocket->iFrameHdrLen = 2; // minimum header size + } + } + else if (iResult < 0) + { + _ProtoWebSocketFail(pWebSocket, iResult, "receiving data"); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketProcessSendPong + + \Description + Process a pending pong request + + \Input *pWebSocket - module state + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoWebSocketProcessSendPong(ProtoWebSocketRefT *pWebSocket) +{ + int32_t iResult; + if ((pWebSocket->bDoPong != TRUE) || (pWebSocket->iOutLen > 0)) + { + return; + } + if ((iResult = _ProtoWebSocketSendPing(pWebSocket, pWebSocket->strCtrlData, PROTOWEBSOCKET_CTRL_PONG)) >= 0) + { + pWebSocket->bDoPong = FALSE; + } + else if (iResult < 0) + { + _ProtoWebSocketFail(pWebSocket, iResult, "sending pong"); + pWebSocket->bDoPong = FALSE; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketProcessSendClose + + \Description + Process a pending close request. + + \Input *pWebSocket - module state + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoWebSocketProcessSendClose(ProtoWebSocketRefT *pWebSocket) +{ + int32_t iResult; + if ((pWebSocket->bDoClose != TRUE) || (pWebSocket->iOutLen > 0)) + { + return; + } + if ((iResult = _ProtoWebSocketSendData(pWebSocket, pWebSocket->strCtrlData, pWebSocket->iCtrlDataLen, PROTOWEBSOCKET_CTRL_CLSE)) == 0) + { + pWebSocket->bDoClose = FALSE; + } + else if (iResult < 0) + { + _ProtoWebSocketFail(pWebSocket, iResult, "sending close"); + pWebSocket->bDoClose = FALSE; + } +} + +/*F********************************************************************************/ +/*! + \Function _ProtoWebSocketProcessSend + + \Description + Process an in-progress send from the output buffer. + + \Input *pWebSocket - module state + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +static void _ProtoWebSocketProcessSend(ProtoWebSocketRefT *pWebSocket) +{ + int32_t iResult; + if (pWebSocket->iOutLen == 0) + { + return; + } + if ((iResult = _ProtoWebSocketSendBuff(pWebSocket)) < 0) + { + _ProtoWebSocketFail(pWebSocket, iResult, "sending data"); + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketCreate + + \Description + Allocate a WebSocket connection and prepare for use + + \Input iBufSize - websocket buffer size (determines max message size) + + \Output + ProtoWebSocketRefT * - module state, or NULL on failure + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoWebSocketRefT *ProtoWebSocketCreate(int32_t iBufSize) +{ + ProtoWebSocketRefT *pWebSocket; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // clamp the buffer size + if (iBufSize < PROTOWEBSOCKET_BUFSIZE_MIN) + { + iBufSize = PROTOWEBSOCKET_BUFSIZE_MIN; + } + iBufSize += PROTOWEBSOCKET_MAXSENDHDR; + + // allocate and init module state + if ((pWebSocket = DirtyMemAlloc(sizeof(*pWebSocket), PROTOWEBSOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protowebsocket: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pWebSocket, sizeof(*pWebSocket)); + pWebSocket->iMemGroup = iMemGroup; + pWebSocket->pMemGroupUserData = pMemGroupUserData; + pWebSocket->iTimeout = PROTOWEBSOCKET_TIMEOUT; + pWebSocket->iVersion = PROTOWEBSOCKET_PROTOCOLVERS; + pWebSocket->iVerbose = 1; + + // allocate ssl module + if ((pWebSocket->pProtoSSL = ProtoSSLCreate()) == NULL) + { + NetPrintf(("protowebsocket: [%p] unable to allocate ssl module\n", pWebSocket)); + ProtoWebSocketDestroy(pWebSocket); + return(NULL); + } + // allocate websocket buffer + if ((pWebSocket->pOutBuf = DirtyMemAlloc(iBufSize, PROTOWEBSOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protowebsocket: [%p] unable to allocate websocket buffer\n", pWebSocket)); + ProtoWebSocketDestroy(pWebSocket); + return(NULL); + } + pWebSocket->iOutMax = iBufSize; + + // allocate temp input buffer + pWebSocket->iInpMax = PROTOWEBSOCKET_DEFAULT_INPUTBUFSIZE; + if ((pWebSocket->pInpBuf = (char *)DirtyMemAlloc(pWebSocket->iInpMax, PROTOWEBSOCKET_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("protowebsocket: [%p] unable to allocate temporary input buffer\n", pWebSocket)); + ProtoWebSocketDestroy(pWebSocket); + return(NULL); + } + + // return module state to caller + return(pWebSocket); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketCreate2 + + \Description + Allocate a WebSocket connection and prepare for use: ProtoSSL function + signature compatible version. + + \Output + ProtoWebSocketRefT * - module state, or NULL on failure + + \Version 03/08/2013 (jbrookes) +*/ +/********************************************************************************F*/ +ProtoWebSocketRefT *ProtoWebSocketCreate2(void) +{ + return(ProtoWebSocketCreate(PROTOWEBSOCKET_BUFSIZE_MIN)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketDestroy + + \Description + Destroy a connection and release state + + \Input *pWebSocket - websocket module state + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoWebSocketDestroy(ProtoWebSocketRefT *pWebSocket) +{ + if (pWebSocket->pAppendHdr != NULL) + { + DirtyMemFree(pWebSocket->pAppendHdr, PROTOWEBSOCKET_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData); + } + if (pWebSocket->pInpBuf != NULL) + { + DirtyMemFree(pWebSocket->pInpBuf, PROTOWEBSOCKET_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData); + } + if (pWebSocket->pOutBuf != NULL) + { + DirtyMemFree(pWebSocket->pOutBuf, PROTOWEBSOCKET_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData); + } + if (pWebSocket->pProtoSSL != NULL) + { + ProtoSSLDestroy(pWebSocket->pProtoSSL); + } + DirtyMemFree(pWebSocket, PROTOWEBSOCKET_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketConnect + + \Description + Initiate a connection to a server + + \Input *pWebSocket - websocket module state + \Input *pUrl - url containing host information; ws:// or wss:// + + \Output + int32_t - zero=success, else failure + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketConnect(ProtoWebSocketRefT *pWebSocket, const char *pUrl) +{ + int32_t iSecure, iResult; + char strKind[16]; + + // reset module state + _ProtoWebSocketReset(pWebSocket); + + // parse out the url + pUrl = ProtoHttpUrlParse(pUrl, strKind, sizeof(strKind), pWebSocket->strHost, sizeof(pWebSocket->strHost), &pWebSocket->iPort, &iSecure); + + // do our own secure determination + iSecure = ds_stricmp(strKind, "wss") ? 0 : 1; + if (iSecure && (pWebSocket->iPort == 80)) + { + pWebSocket->iPort = 443; + } + + // create key we send and encded key we must validate in server response + _ProtoWebSocketGenerateKey(pWebSocket); + + // format the request header for sending once connected + _ProtoWebSocketFormatHeader(pWebSocket, pWebSocket->strHost, pUrl, pWebSocket->iPort, iSecure); + + // start the connect + NetPrintfVerbose((pWebSocket->iVerbose, 0, "protowebsocket: [%p] connecting to %s:%d\n", pWebSocket, pWebSocket->strHost, pWebSocket->iPort)); + iResult = ProtoSSLConnect(pWebSocket->pProtoSSL, iSecure, pWebSocket->strHost, 0, pWebSocket->iPort); + pWebSocket->eState = (iResult == SOCKERR_NONE) ? ST_CONN : ST_FAIL; + pWebSocket->uTimer = NetTick(); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketDisconnect + + \Description + Disconnect from the server. Note that this is an asynchronous operation. + Connection status can be polled with the 'conn' status selector + + \Input *pWebSocket - websocket module state + + \Output + int32_t - zero=success, else failure + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketDisconnect(ProtoWebSocketRefT *pWebSocket) +{ + // ignore request in states where we are already disconnected + if ((pWebSocket->eState == ST_DISC) || (pWebSocket->eState == ST_FAIL)) + { + return(0); + } + // if we're in handshaking just do an immediate close + if (pWebSocket->eState != ST_OPEN) + { + return(_ProtoWebSocketDisconnect(pWebSocket, ST_DISC)); + } + // send close notification + return(_ProtoWebSocketSendClose(pWebSocket, PROTOWEBSOCKET_CLOSEREASON_NORMAL, NULL)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketSend + + \Description + Send data to a server over a WebSocket connection. Stream-type send, no + framing is performed by ProtoWebSocket. + + \Input *pWebSocket - websocket module state + \Input *pBuffer - data to send + \Input iLength - amount of data to send + + \Output + int32_t - negative=failure, else number of bytes of data sent + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketSend(ProtoWebSocketRefT *pWebSocket, const char *pBuffer, int32_t iLength) +{ + return(_ProtoWebSocketSendUserData(pWebSocket, pBuffer, iLength, PROTOWEBSOCKET_OPCODE_BINA)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketSendText + + \Description + Send text data to a server over a WebSocket connection. Stream-type send, no + framing is performed by ProtoWebSocket. + + \Input *pWebSocket - websocket module state + \Input *pBuffer - data to send (NULL terminated C-style string) + + \Output + int32_t - negative=failure, else number of bytes of data sent + + \Version 08/30/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketSendText(ProtoWebSocketRefT *pWebSocket, const char *pBuffer) +{ + return(_ProtoWebSocketSendUserData(pWebSocket, pBuffer, (int32_t)strlen(pBuffer), PROTOWEBSOCKET_OPCODE_TEXT)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketSendMessage + + \Description + Send a message to a server over a WebSocket connection. If the message + size is larger than the ProtoWebSocket buffer size, the message will be + sent fragmented. + + \Input *pWebSocket - websocket module state + \Input *pBuffer - data to send + \Input iLength - amount of data to send + + \Output + int32_t - negative=failure, else number of bytes of data sent + + \Version 03/31/2017 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketSendMessage(ProtoWebSocketRefT *pWebSocket, const char *pBuffer, int32_t iLength) +{ + int32_t iResult; + pWebSocket->bMessageSend = TRUE; + iResult = _ProtoWebSocketSendUserData(pWebSocket, pBuffer, iLength, PROTOWEBSOCKET_OPCODE_BINA); + pWebSocket->bMessageSend = FALSE; + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketSendMessageText + + \Description + Send a text message to a server over a WebSocket connection. If the message + size is larger than the ProtoWebSocket buffer size, the message will be + sent fragmented. + + \Input *pWebSocket - websocket module state + \Input *pBuffer - data to send (NULL terminated C-style string) + + \Output + int32_t - negative=failure, else number of bytes of data sent + + \Version 08/30/2017 (eesponda) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketSendMessageText(ProtoWebSocketRefT *pWebSocket, const char *pBuffer) +{ + int32_t iResult; + pWebSocket->bMessageSend = TRUE; + iResult = _ProtoWebSocketSendUserData(pWebSocket, pBuffer, (int32_t)strlen(pBuffer), PROTOWEBSOCKET_OPCODE_TEXT); + pWebSocket->bMessageSend = FALSE; + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketRecv + + \Description + Receive data from a server over a WebSocket connection. Stream-type recv, + framing bits are ignored. + + \Input *pWebSocket - websocket module state + \Input *pBuffer - [out] buffer to receive data + \Input iLength - size of buffer + + \Output + int32_t - negative=failure, else number of bytes of data received + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketRecv(ProtoWebSocketRefT *pWebSocket, char *pBuffer, int32_t iLength) +{ + // handle not connected states + if ((pWebSocket->eState == ST_DISC) || (pWebSocket->eState == ST_FAIL)) + { + return(SOCKERR_NOTCONN); + } + if (pWebSocket->eState != ST_OPEN) + { + return(0); + } + + // connected, do the receive + return(_ProtoWebSocketRecvData(pWebSocket, pBuffer, iLength)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketRecvMessage + + \Description + Receive a message from a server over a WebSocket connection. Note that + the user buffer is expected to persist and be capable of receiving the + entire message. + + \Input *pWebSocket - websocket module state + \Input *pBuffer - [out] buffer to receive data + \Input iLength - size of buffer + + \Output + int32_t - negative=failure, SOCKERR_NOMEM if the user buffer + is too small, zero=pending, else frame size received + + \Version 03/30/2017 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketRecvMessage(ProtoWebSocketRefT *pWebSocket, char *pBuffer, int32_t iLength) +{ + int32_t iResult; + + // see if we need more buffer + if (pWebSocket->iRecvSize == iLength) + { + return(SOCKERR_NOMEM); + } + + // do the receive + if ((iResult = ProtoWebSocketRecv(pWebSocket, pBuffer+pWebSocket->iRecvSize, iLength-pWebSocket->iRecvSize)) > 0) + { + // accumulate receive size + pWebSocket->iRecvSize += iResult; + + // return accumulated size if final frame and completely read, else return zero + if ((pWebSocket->bFrameFin) && (pWebSocket->iFrameLen == 0)) + { + iResult = pWebSocket->iRecvSize; + pWebSocket->iRecvSize = 0; + pWebSocket->bFrameFin = FALSE; + } + else + { + iResult = 0; + } + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketStatus + + \Description + Get module status + + \Input *pWebSocket - websocket module state + \Input iSelect - status selector + \Input *pBuffer - [inp/out] buffer, selector specific + \Input iBufSize - size of buffer + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR RETURN RESULT + 'crsn' returns close reason (PROTOWEBSOCKET_CLOSEREASON_*) + 'essl' returns protossl error state + 'host' current host copied to output buffer + 'port' current port + 'stat' connection status (-1=not connected, 0=connection in progress, 1=connected) + 'time' TRUE if the client timed out the connection, else FALSE + \endverbatim + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketStatus(ProtoWebSocketRefT *pWebSocket, int32_t iSelect, void *pBuffer, int32_t iBufSize) +{ + // return close reason + if (iSelect == 'crsn') + { + if ((pBuffer != NULL) && (pWebSocket->iCtrlDataLen > 0)) + { + int32_t iCopyLen = (iBufSize < pWebSocket->iCtrlDataLen) ? iBufSize : pWebSocket->iCtrlDataLen; + ds_memcpy(pBuffer, pWebSocket->strCtrlData, iCopyLen); + } + return(pWebSocket->iCloseReason); + } + + // return protossl error state (we cache this since the state is reset when we disconnect on error) + if (iSelect == 'essl') + { + return(pWebSocket->iSSLFail); + } + + // return current host + if (iSelect == 'host') + { + ds_strnzcpy(pBuffer, pWebSocket->strHost, iBufSize); + return(0); + } + + // return current port + if (iSelect == 'port') + { + return(pWebSocket->iPort); + } + + // check connection status + if (iSelect == 'stat') + { + if ((pWebSocket->eState == ST_DISC) || (pWebSocket->eState == ST_FAIL)) + { + return(-1); + } + else if (pWebSocket->eState != ST_OPEN) + { + return(0); + } + // if we're connected (ST_OPEN) fall through to protossl connectivity check + } + + // return timeout indicator + if (iSelect == 'time') + { + return(pWebSocket->bTimeout); + } + + // pass through unhandled selectors to protossl + return(ProtoSSLStat(pWebSocket->pProtoSSL, iSelect, pBuffer, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketControl + + \Description + Control module behavior + + \Input *pWebSocket - websocket module state + \Input iSelect - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + Selectors are: + + \verbatim + SELECTOR DESCRIPTION + 'apnd' The given buffer will be appended to future headers sent by + ProtoWebSocket. The Accept header in the default header will + be omitted. If there is a User-Agent header included in the + append header, it will replace the default User-Agent header. + 'clse' Send a close message with optional reason (iValue) and data + (string passed in pValue) + 'keep' Set keep-alive timer, in seconds; client sends a 'pong' at this + rate to keep connection alive (0 disables; disabled by default) + 'ires' Resize the input buffer + 'ping' Send a ping message with optional data (string passed in pValue) + 'pong' Send a pong message with optional data (string passed in pValue) + 'spam' Sets debug output verbosity (0...n) + 'time' Sets ProtoWebSocket client timeout in seconds (default=300s) + \endverbatim + + Unhandled selectors are passed on to ProtoSSL. + + \Version 11/30/2012 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t ProtoWebSocketControl(ProtoWebSocketRefT *pWebSocket, int32_t iSelect, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iSelect == 'apnd') + { + return(_ProtoWebSocketSetAppendHeader(pWebSocket, (const char *)pValue)); + } + if (iSelect == 'clse') + { + return(_ProtoWebSocketSendClose(pWebSocket, iValue, (const char *)pValue)); + } + if (iSelect == 'keep') + { + // enforce a minimum keep-alive interval of 30s + if (iValue < 30) + { + NetPrintf(("protowebsocket: [%p] warning: enforcing a minimum keep-alive interval (%ds->30s)\n", pWebSocket, iValue)); + iValue = 30; + } + pWebSocket->iKeepAlive = iValue*1000; + return(0); + } + if (iSelect == 'ires') + { + char *pInpBuf; + + // make sure that we don't truncate any data already in the buffer, this assumes that you don't resize multiple times + iValue = DS_MAX(iValue, PROTOWEBSOCKET_DEFAULT_INPUTBUFSIZE); + + // allocate buffer and reassign + if ((pInpBuf = (char *)DirtyMemAlloc(iValue, PROTOWEBSOCKET_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData)) == NULL) + { + NetPrintf(("protowebsocket: [%p] unable to allocate buffer for resizing input buffer\n", pWebSocket)); + return(-1); + } + ds_memcpy(pInpBuf, pWebSocket->pInpBuf, pWebSocket->iInpLen); + + DirtyMemFree(pWebSocket->pInpBuf, PROTOWEBSOCKET_MEMID, pWebSocket->iMemGroup, pWebSocket->pMemGroupUserData); + pWebSocket->pInpBuf = pInpBuf; + pWebSocket->iInpMax = iValue; + + return(0); + } + if ((iSelect == 'ping') || (iSelect == 'pong')) + { + return(_ProtoWebSocketSendPing(pWebSocket, (const char *)pValue, (iSelect == 'ping') ? PROTOWEBSOCKET_CTRL_PING : PROTOWEBSOCKET_CTRL_PONG)); + } + if (iSelect == 'spam') + { + pWebSocket->iVerbose = iValue; + return(0); + } + if (iSelect == 'time') + { + pWebSocket->iTimeout = iValue*1000; + return(0); + } + // pass unhandled selectors through to ProtoSSL + return(ProtoSSLControl(pWebSocket->pProtoSSL, iSelect, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function ProtoWebSocketUpdate + + \Description + Give life to module (should be called periodically to allow module to + perform work). + + \Input *pWebSocket - websocket module state + + \Version 11/26/2012 (jbrookes) +*/ +/********************************************************************************F*/ +void ProtoWebSocketUpdate(ProtoWebSocketRefT *pWebSocket) +{ + uint32_t uCurTick = NetTick(); + int32_t iResult; + char *pStart, *pEnd; + + // give time to comm module + ProtoSSLUpdate(pWebSocket->pProtoSSL); + + // check for timeout + if ((pWebSocket->eState != ST_DISC) && (pWebSocket->eState != ST_FAIL)) + { + if (NetTickDiff(uCurTick, pWebSocket->uTimer) > pWebSocket->iTimeout) + { + NetPrintf(("protowebsocket: [%p] inactivity timeout (state=%d)\n", pWebSocket, pWebSocket->eState)); + pWebSocket->eState = ST_FAIL; + pWebSocket->bTimeout = TRUE; + } + } + + // see if network connection is complete + if (pWebSocket->eState == ST_CONN) + { + if ((iResult = ProtoSSLStat(pWebSocket->pProtoSSL, 'stat', NULL, 0)) > 0) + { + pWebSocket->uTimer = uCurTick; + pWebSocket->eState = ST_SEND; + } + else if (iResult < 0) + { + _ProtoWebSocketFail(pWebSocket, iResult, "connecting"); + } + } + + // sending handshake + if (pWebSocket->eState == ST_SEND) + { + if ((iResult = _ProtoWebSocketSendBuff(pWebSocket)) > 0) + { + NetPrintfVerbose((pWebSocket->iVerbose, 1, "protowebsocket: [%p] sent handshake request\n", pWebSocket)); + #if DIRTYCODE_LOGGING + if (pWebSocket->iVerbose > 2) + { + NetPrintWrap(pWebSocket->pOutBuf, 80); + } + #endif + pWebSocket->eState = ST_RECV; + } + else if (iResult < 0) + { + _ProtoWebSocketFail(pWebSocket, iResult, "sending handshake request"); + } + } + + // receive server handshake response: note that due to serialized nature of handshaking + if (pWebSocket->eState == ST_RECV) + { + if ((iResult = _ProtoWebSocketRecv(pWebSocket, pWebSocket->pInpBuf+pWebSocket->iInpLen, pWebSocket->iInpMax-pWebSocket->iInpLen)) > 0) + { + pWebSocket->iInpLen += iResult; + // see if we have the complete header + pStart = pWebSocket->pInpBuf; + pEnd = pWebSocket->pInpBuf+pWebSocket->iInpLen-3; + + // scan for blank line marking body start + while ((pStart != pEnd) && ((pStart[0] != '\r') || (pStart[1] != '\n') || (pStart[2] != '\r') || (pStart[3] != '\n'))) + { + pStart += 1; + } + + if (pStart != pEnd) + { + // process the header + if ((iResult = _ProtoWebSocketProcessHeader(pWebSocket, pWebSocket->pInpBuf)) >= 0) + { + NetPrintf(("protowebsocket: [%p] connection open\n", pWebSocket)); + pWebSocket->eState = ST_OPEN; + } + else + { + _ProtoWebSocketFail(pWebSocket, iResult, "processing handshake response"); + } + // remove header from buffer + pWebSocket->iInpOff = (int32_t)(pStart+4-pWebSocket->pInpBuf); + } + } + else if (iResult < 0) + { + _ProtoWebSocketFail(pWebSocket, iResult, "receiving handshake response"); + } + } + + // process in open state + if (pWebSocket->eState == ST_OPEN) + { + // issue a keep-alive? + if ((pWebSocket->iKeepAlive > 0) && (NetTickDiff(uCurTick, pWebSocket->uTimer) > pWebSocket->iKeepAlive)) + { + NetPrintfVerbose((pWebSocket->iVerbose, 0, "protowebsocket: [%p] sending keep-alive at %u\n", pWebSocket, uCurTick)); + pWebSocket->bDoPong = TRUE; + pWebSocket->strCtrlData[0] = '\0'; + pWebSocket->iCtrlDataLen = 0; + } + + // process pong request + _ProtoWebSocketProcessSendPong(pWebSocket); + + // process close request + _ProtoWebSocketProcessSendClose(pWebSocket); + + // process send request + _ProtoWebSocketProcessSend(pWebSocket); + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/util/aws.c b/r5dev/thirdparty/dirtysdk/source/util/aws.c new file mode 100644 index 00000000..029eb807 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/aws.c @@ -0,0 +1,851 @@ +/*H********************************************************************************/ +/*! + \File aws.c + + \Description + Implements AWS utility functions, including SigV4 signing and signed binary + event reading and writing. + + \Notes + References: + Signature V4: https://docs.aws.amazon.com/en_us/general/latest/gr/signature-version-4.html + SigV4 Signing: https://docs.aws.amazon.com/en_us/general/latest/gr/sigv4_signing.html + SigV4 Examples: https://docs.aws.amazon.com/en_us/general/latest/gr/sigv4-signed-request-examples.html + Event Stream Encoding: https://docs.aws.amazon.com/transcribe/latest/dg/streaming-format.html + + \Copyright + Copyright 2018 Electronic Arts + + \Version 12/26/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/crypt/crypthash.h" +#include "DirtySDK/crypt/crypthmac.h" +#include "DirtySDK/proto/protohttputil.h" + +#include "DirtySDK/util/aws.h" + +/*** Defines **********************************************************************/ + +//! define this for debug output +#define AWS_DEBUG (DIRTYCODE_DEBUG && FALSE) + +//! length of aws binary event prelude +#define AWS_EVENT_PRELUDE_SIZE (12) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! AWS event header types +typedef enum +{ + AWS_BOOLEAN_TRUE = 0, + AWS_BOOLEAN_FALSE, + AWS_BYTE, + AWS_SHORT, + AWS_INTEGER, + AWS_LONG, + AWS_BYTE_ARRAY, + AWS_STRING, + AWS_TIMESTAMP, + AWS_UUID +} AWSHeaderValueE; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _AWSHashSha2 + + \Description + Calculate SHA2 hash of specified data + + \Input *pHashBuf - [out] storage for hash data + \Input iHashLen - length of hash and hash data buffer + \Input *pData - data to hash + \Input iDataLen - length of data to hash, or -1 if data is a string + + \Version 01/15/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSHashSha2(uint8_t *pHashBuf, int32_t iHashLen, const uint8_t *pData, int32_t iDataLen) +{ + CryptSha2T Sha2; + // get data length if it is a string + if (iDataLen < 0) + { + iDataLen = (int32_t)strlen((const char *)pData); + } + // create hash of request payload + CryptSha2Init(&Sha2, iHashLen); + CryptSha2Update(&Sha2, pData, iDataLen); + CryptSha2Final(&Sha2, pHashBuf, iHashLen); +} + +/*F********************************************************************************/ +/*! + \Function _AWSGetDateTime + + \Description + Get current datetime in ISO8601 basic format. + + \Input *pStrDateTime - [out] storage for datetime string + \Input iDateTimeLen - length of datetime buffer + \Input *pStrDate - [out, optional] storage for date string (YYYYMMDD) + \Input iDateLen - length of date buffer + + \Version 12/26/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSGetDateTime(char *pStrDateTime, int32_t iDateTimeLen, char *pStrDate, int32_t iDateLen) +{ + struct tm CurTime; + uint64_t uTime; + // get current time + uTime = ds_timeinsecs(); + // get full date in ISO8601 (basic format) + ds_timetostr(ds_secstotime(&CurTime, uTime), TIMETOSTRING_CONVERSION_ISO_8601_BASIC, FALSE, pStrDateTime, iDateTimeLen); + // get date string (optional) + if (pStrDate != NULL) + { + ds_strsubzcpy(pStrDate, iDateLen, pStrDateTime, 8); + } +} + +/*F********************************************************************************/ +/*! + \Function _AWSGetKeyInfo + + \Description + Extract keyid and key from keyid:key string + + \Input *pStrKeyInfo - pointer to source keyid:key info + \Input *pStrId - [out] storage for keyid + \Input iIdLen - size of keyid buffer + \Input *pStrKey - [out] storage for key + \Input iKeyLen - size of key buffer + + \Version 12/26/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSGetKeyInfo(const char *pStrKeyInfo, char *pStrId, int32_t iIdLen, char *pStrKey, int32_t iKeyLen) +{ + const char *pSep; + *pStrKey = *pStrId = '\0'; + if ((pSep = strchr(pStrKeyInfo, ':')) != NULL) + { + ds_strsubzcpy(pStrId, iIdLen, pStrKeyInfo, (int32_t)(pSep-pStrKeyInfo)); + ds_strnzcpy(pStrKey, pSep+1, iKeyLen); + } +} + +/*F********************************************************************************/ +/*! + \Function _AWSGetHostInfo + + \Description + Get region from host + + \Input *pStrHost - hostname, used to derive service and region + \Input *pStrRegion - [out] AWS region buffer + \Input iRegionLen - length of region buffer + + \Version 12/31/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSGetHostInfo(const char *pStrHost, char *pStrRegion, int32_t iRegionLen) +{ + const char *pDot; + // skip service name in host header + pDot = strchr(pStrHost, '.'); + pStrHost = pDot+1; + // get region from host header + pDot = strchr(pStrHost, '.'); + ds_strsubzcpy(pStrRegion, iRegionLen, pStrHost, (int32_t)(pDot-pStrHost)); +} + +/*F********************************************************************************/ +/*! + \Function _AWSSignGetHeaders + + \Description + Given an HTTP header buffer, creates the Canonical and Signed header lists + required for AWSV4 signing. + + \Input *pHeader - HTTP header to parse + \Input *pCanonHdrBuf - [out] buffer for canonical header list + \Input iCanonHdrLen - lengh of canonical header buffer + \Input *pSignedHdrBuf - [out] buffer for signed header list + \Input iSignedHdrLen - length of signed header buffer + \Input *pStrHost - [out] buffer for hostname from host header + \Input iHostLen - length of host buffer + \Input *pStrMethod - [out] buffer for method from request + \Input iMethodLen - length of method buffer + \Input *pStrUri - [out] buffer for URI from request + \Input iUriLen - length of URI buffer + \Input *pStrQuery - [out] buffer for query from request + \Input iQueryLen - length of query buffer + + \Version 12/28/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSSignGetHeaders(const char *pHeader, char *pCanonHdrBuf, int32_t iCanonHdrLen, char *pSignedHdrBuf, int32_t iSignedHdrLen, char *pStrHost, int32_t iHostLen, char *pStrMethod, int32_t iMethodLen, char *pStrUri, int32_t iUriLen, char *pStrQuery, int32_t iQueryLen) +{ + char strName[128], strValue[256], strLine[384]; + const char *pHdrTmp = pHeader; + + // get HTTPRequestMethod, CanonicalURI, and CanonicalQuery from first line of request if this is *not* HTTP2 + if (ds_strnicmp(pHeader, ":method:", 8)) + { + pHdrTmp = ProtoHttpUtilParseRequest(pHeader, pStrMethod, iMethodLen, pStrUri, iUriLen, pStrQuery, iQueryLen, NULL, NULL); + } + + // get headers, process them, and add to canonical and signed lists in sorted order + while (ProtoHttpGetNextHeader(NULL, pHdrTmp, strName, sizeof(strName), strValue, sizeof(strValue), &pHdrTmp) == 0) + { + // save copy of host header value + if (!ds_stricmp(strName, "host") || !ds_stricmp(strName, ":authority")) + { + ds_strnzcpy(pStrHost, strValue, iHostLen); + } + // save copy of method header value (http2) + if (!ds_stricmp(strName, ":method")) + { + ds_strnzcpy(pStrMethod, strValue, iMethodLen); + } + // save copy of path header value (http2) + if (!ds_stricmp(strName, ":path")) + { + ds_strnzcpy(pStrUri, strValue, iUriLen); + } + // http2 pseudo-headers don't include values in their canonical representation + if (strName[0] == ':') + { + strValue[0] = '\0'; + } + // convert name to lower case + ds_strtolower(strName); + // trim spaces in value + ds_strtrim(strValue); + // format header line for canonical headers list + ds_snzprintf(strLine, sizeof(strLine), "%s:%s", strName, strValue); + // insert header line in canonical header list + ds_strlistinsert(pCanonHdrBuf, iCanonHdrLen, strLine, '\n'); + // insert header name in signed header list + ds_strlistinsert(pSignedHdrBuf, iSignedHdrLen, strName, ';'); + } + // eliminate trailing semicolon in signed header list + pSignedHdrBuf[strlen(pSignedHdrBuf)-1] = '\0'; +} + +/*F********************************************************************************/ +/*! + \Function _AWSSignCreateCanonicalRequest + + \Description + Create a Canonical Request for Signature Version 4 as per + https://docs.aws.amazon.com/en_us/general/latest/gr/sigv4-create-canonical-request.html + + \Input *pSigBuf - [out] storage for signature + \Input iSigLen - length of signature buffer + \Input *pHeader - request header + \Input *pRequest - request body + \Input *pStrDateTime - datetime string + \Input *pCanonHdrs - canonical header list + \Input *pSignedHdrs - signed header list + \Input *pMethod - request method + \Input *pUri - request uri + \Input *pQuery - request query + + \Version 12/21/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSSignCreateCanonicalRequest(char *pSigBuf, int32_t iSigLen, const char *pHeader, const char *pRequest, const char *pStrDateTime, const char *pCanonHdrs, const char *pSignedHdrs, const char *pMethod, const char *pUri, const char *pQuery) +{ + uint8_t aHashBuf[CRYPTSHA256_HASHSIZE]; + char strSigTemp[1024], strSignTemp[(CRYPTSHA256_HASHSIZE*2)+1]; + int32_t iOffset; + + // create hash of request payload + _AWSHashSha2(aHashBuf, sizeof(aHashBuf), (const uint8_t *)pRequest, -1); + + // create CanonicalRequest + iOffset = 0; + iOffset += ds_snzprintf(strSigTemp+iOffset, sizeof(strSigTemp)-iOffset, "%s\n", pMethod); // HTTPRequestMethod + '\n' + + iOffset += ds_snzprintf(strSigTemp+iOffset, sizeof(strSigTemp)-iOffset, "%s\n", pUri); // CanonicalURI + '\n' + + iOffset += ds_snzprintf(strSigTemp+iOffset, sizeof(strSigTemp)-iOffset, "%s\n", pQuery); // CanonicalQueryString + '\n' + + iOffset += ds_snzprintf(strSigTemp+iOffset, sizeof(strSigTemp)-iOffset, "%s\n", pCanonHdrs); // CanonicalHeaders + '\n' + + iOffset += ds_snzprintf(strSigTemp+iOffset, sizeof(strSigTemp)-iOffset, "%s\n", pSignedHdrs); // SignedHeaders + '\n' + + iOffset += ds_snzprintf(strSigTemp+iOffset, sizeof(strSigTemp)-iOffset, "%s", ds_fmtoctstring_lc(strSignTemp, sizeof(strSignTemp), aHashBuf, sizeof(aHashBuf))); // HexEncode(Hash(RequestPayload)) + NetPrintfVerbose((AWS_DEBUG, 0, "aws: CanonicalRequest:\n%s\n", strSigTemp)); + + // hash the CanonicalRequest + _AWSHashSha2(aHashBuf, sizeof(aHashBuf), (const uint8_t *)strSigTemp, iOffset); + + // create the octet string version of the hash in output buffer + ds_fmtoctstring_lc(pSigBuf, iSigLen, aHashBuf, sizeof(aHashBuf)); // HexEncode(Hash(CanonicalRequest)) +} + +/*F********************************************************************************/ +/*! + \Function _AWSSignCreateStringToSign + + \Description + Create a String to Sign for Signature Version 4 as per + https://docs.aws.amazon.com/en_us/general/latest/gr/sigv4-create-string-to-sign.html + + \Input *pSignStr - [out] storage for string to sign + \Input iSignLen - length of sign buffer + \Input *pSignType - signing type string + \Input *pDateTimeStr - datetime in ISO8601 basic format + \Input *pKeyPath - keypath (date/region/service) string + \Input *pSigBuf - signature created in Task1 + + \Output + int32_t - length of string to sign + + \Version 12/26/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AWSSignCreateStringToSign(char *pSignStr, int32_t iSignLen, const char *pSignType, const char *pDateTimeStr, const char *pKeyPath, const char *pSigBuf) +{ + int32_t iOffset = 0; + iOffset += ds_snzprintf(pSignStr+iOffset, iSignLen-iOffset, "%s\n", pSignType); // AWS4-HMAC-SHA256\n + iOffset += ds_snzprintf(pSignStr+iOffset, iSignLen-iOffset, "%s\n", pDateTimeStr); // YYYYMMDD'T'HHMMSS'Z' + iOffset += ds_snzprintf(pSignStr+iOffset, iSignLen-iOffset, "%s/aws4_request\n", pKeyPath); // /YYYYMMDD///aws4_request\n + iOffset += ds_snzprintf(pSignStr+iOffset, iSignLen-iOffset, "%s", pSigBuf); // base16-encoded hashed CanonicalRequest + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function _AWSSignCreateSignature + + \Description + Calculate the Signature for AWS Signature Version 4 as per + https://docs.aws.amazon.com/en_us/general/latest/gr/sigv4-calculate-signature.html + + \Input *pSigBuf - [out] storage for signature + \Input iSigLen - length of signature buffer + \Input *pSign - string to sign + \Input *pSecret - secret key + \Input *pStrDate - date string + \Input *pRegion - AWS region + \Input *pService - AWS service + + \Version 12/26/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSSignCreateSignature(uint8_t *pSigBuf, int32_t iSigLen, const char *pSign, const char *pSecret, const char *pStrDate, const char *pRegion, const char *pService) +{ + uint8_t aHmac[CRYPTSHA256_HASHSIZE]; + const char *pRequest = "aws4_request"; + char strKey[256]; + + // create signing key + ds_snzprintf(strKey, sizeof(strKey), "%s%s", "AWS4", pSecret); + + // kDate = HMAC("AWS4" + kSecret, Date); note our CryptHmac parameters are reversed from their documentation + CryptHmacCalc(aHmac, sizeof(aHmac), (const uint8_t *)pStrDate, (int32_t)strlen(pStrDate), (const uint8_t *)strKey, (int32_t)strlen(strKey), CRYPTHASH_SHA256); + // kRegion = HMAC(kDate, Region) + CryptHmacCalc(aHmac, sizeof(aHmac), (const uint8_t *)pRegion, (int32_t)strlen(pRegion), aHmac, sizeof(aHmac), CRYPTHASH_SHA256); + // kService = HMAC(kRegion, Service) + CryptHmacCalc(aHmac, sizeof(aHmac), (const uint8_t *)pService, (int32_t)strlen(pService), aHmac, sizeof(aHmac), CRYPTHASH_SHA256); + // kSigning = HMAC(kService, "aws4_request") + CryptHmacCalc(aHmac, sizeof(aHmac), (const uint8_t *)pRequest, (int32_t)strlen(pRequest), aHmac, sizeof(aHmac), CRYPTHASH_SHA256); + + // sign the string + CryptHmacCalc(aHmac, sizeof(aHmac), (const uint8_t *)pSign, (int32_t)strlen(pSign), aHmac, sizeof(aHmac), CRYPTHASH_SHA256); + // copy signature to output + ds_memcpy_s(pSigBuf, iSigLen, aHmac, sizeof(aHmac)); +} + +/*F********************************************************************************/ +/*! + \Function _AWSSignCreateAuthorization + + \Description + Create the Authorization header for adding to the HTTP request + https://docs.aws.amazon.com/en_us/general/latest/gr/sigv4-add-signature-to-request.html + + \Input *pAuthBuf - [out] storage for authorization header + \Input iAuthLen - length of auth buffer + \Input *pStrSig - signature + \Input *pKeyId - key id + \Input *pStrDateTime - datetime string + \Input *pKeyPath - keypath (date/region/service) string + \Input *pSignedHdrs - signed headers + + \Output + int32_t - length of authorization header + + \Version 12/26/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AWSSignCreateAuthorization(char *pAuthBuf, int32_t iAuthLen, const char *pStrSig, const char *pKeyId, const char *pStrDateTime, const char *pKeyPath, const char *pSignedHdrs) +{ + int32_t iOffset; + // Authorization: algorithm Credential=access key ID/credential scope, SignedHeaders=SignedHeaders, Signature=signature + iOffset = 0; + iOffset += ds_snzprintf(pAuthBuf+iOffset, iAuthLen-iOffset, "Authorization: AWS4-HMAC-SHA256 "); + iOffset += ds_snzprintf(pAuthBuf+iOffset, iAuthLen-iOffset, "Credential=%s/%s/aws4_request, ", pKeyId, pKeyPath); + iOffset += ds_snzprintf(pAuthBuf+iOffset, iAuthLen-iOffset, "SignedHeaders=%s, ", pSignedHdrs); + iOffset += ds_snzprintf(pAuthBuf+iOffset, iAuthLen-iOffset, "Signature=%s\r\n", pStrSig); + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function _AWSWriteEventPrelude + + \Description + Write binary event prelude to buffer + + \Input *pBuffer - [out] buffer to write prelude to + \Input iHdrLen - length of header + \Input iMsgLen - length of message + + \Version 01/16/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSWriteEventPrelude(uint8_t *pBuffer, int32_t iHdrLen, int32_t iMsgLen) +{ + uint32_t uCrc32; + // write total length (including message_crc) + pBuffer[0] = (uint8_t)(iMsgLen>>24); + pBuffer[1] = (uint8_t)(iMsgLen>>16); + pBuffer[2] = (uint8_t)(iMsgLen>>8); + pBuffer[3] = (uint8_t)(iMsgLen>>0); + // write headers length + pBuffer[4] = (uint8_t)(iHdrLen>>24); + pBuffer[5] = (uint8_t)(iHdrLen>>16); + pBuffer[6] = (uint8_t)(iHdrLen>>8); + pBuffer[7] = (uint8_t)(iHdrLen>>0); + // calculate and write prelude_crc + uCrc32 = NetCrc32(pBuffer, 8, NULL); + pBuffer[8] = (uint8_t)(uCrc32>>24); + pBuffer[9] = (uint8_t)(uCrc32>>16); + pBuffer[10] = (uint8_t)(uCrc32>>8); + pBuffer[11] = (uint8_t)(uCrc32>>0); +} + +/*F********************************************************************************/ +/*! + \Function _AWSWriteEventHeader + + \Description + Write binary event header to buffer + + \Input *pBuffer - [out] buffer to write header to + \Input iBufSize - buffer size + \Input *pHeader - name of header to write + \Input *pValue - header value data + \Input iValueSize - size of header value + \Input eHeaderValue - header value type + + \Output + int32_t - size of header written + + \Version 01/16/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AWSWriteEventHeader(uint8_t *pBuffer, int32_t iBufSize, const char *pHeader, const uint8_t *pValue, int32_t iValueSize, AWSHeaderValueE eHeaderValue) +{ + int32_t iOffset=0, iStrLen = (int32_t)strlen(pHeader); + // write header length + pBuffer[iOffset++] = (uint8_t)iStrLen; + // copy header name + ds_memcpy_s(pBuffer+iOffset, iBufSize-iOffset, pHeader, iStrLen); + iOffset += iStrLen; + // copy header value type + pBuffer[iOffset++] = (uint8_t)eHeaderValue; + // copy header value length + if ((eHeaderValue == AWS_BYTE_ARRAY) || (eHeaderValue == AWS_STRING)) + { + pBuffer[iOffset++] = (uint8_t)(iValueSize>>8); + pBuffer[iOffset++] = (uint8_t)(iValueSize>>0); + } + // copy header value + #if EA_SYSTEM_LITTLE_ENDIAN + if ((eHeaderValue == AWS_SHORT) || (eHeaderValue == AWS_INTEGER) || (eHeaderValue == AWS_LONG) || (eHeaderValue == AWS_TIMESTAMP)) + { + int32_t iByte, iData; + for (iByte = 0, iData = iValueSize-1; iByte < iValueSize; iByte += 1) + { + pBuffer[iOffset++] = pValue[iData--]; + } + } + else + #endif + { + ds_memcpy_s(pBuffer+iOffset, iBufSize-iOffset, pValue, iValueSize); + iOffset += iValueSize; + } + // return offset to caller + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function _AWSWriteSignEvent + + \Description + Sign an AWS binary event + + \Input *pSignature - [out] buffer to write signature to + \Input iSigSize - size of signature to write + \Input *pHead - header of event to sign + \Input iHeadSize - size of header + \Input *pData - data to sign + \Input iDataSize - size of data (may be zero) + \Input *pSignInfo - [in/out] signing info for event + + \Notes + The signature field in the signing info is used to sign the event, and + then updated with the new signature, which AWS calls "signature chaining". + + String stringToSign = + "AWS4-HMAC-SHA256-PAYLOAD" + "\n" + DATE + "\n" + + KEYPATH + "\n" + + Hex(priorSignature) + "\n" + + HexHash(nonSigHeaders) + "\n" + + HexHash(payload) + + \Version 01/16/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static void _AWSWriteSignEvent(uint8_t *pSignature, int32_t iSigSize, const uint8_t *pHead, int32_t iHeadSize, const uint8_t *pData, int32_t iDataSize, AWSSignInfoT *pSignInfo) +{ + char strSign[1024], strDateTime[32], strDate[9], strSignTemp[(CRYPTSHA256_HASHSIZE*2)+1]; + uint8_t aHashBuf[CRYPTSHA256_HASHSIZE]; + int32_t iOffset; + // get datetime + _AWSGetDateTime(strDateTime, sizeof(strDateTime), strDate, sizeof(strDate)); + // create the string to signl includes header, datetime, keypath, and priorSignature + iOffset = _AWSSignCreateStringToSign(strSign, sizeof(strSign), "AWS4-HMAC-SHA256-PAYLOAD", strDateTime, pSignInfo->strKeyPath, pSignInfo->strSignature); + // append hexhash of non-signed headers + _AWSHashSha2(aHashBuf, sizeof(aHashBuf), pHead, iHeadSize); + iOffset += ds_snzprintf(strSign+iOffset, sizeof(strSign)-iOffset, "\n%s\n", ds_fmtoctstring_lc(strSignTemp, sizeof(strSignTemp), aHashBuf, sizeof(aHashBuf))); // HexEncode(Hash(nonSigHeaders)) + \n + // append hexhash of payload + _AWSHashSha2(aHashBuf, sizeof(aHashBuf), pData, iDataSize); + iOffset += ds_snzprintf(strSign+iOffset, sizeof(strSign)-iOffset, "%s", ds_fmtoctstring_lc(strSignTemp, sizeof(strSignTemp), aHashBuf, sizeof(aHashBuf))); // HexEncode(Hash(payload)) + // debug logging of string to sign + NetPrintfVerbose((AWS_DEBUG, 0, "aws: EventStringToSign:\n%s\n", strSign)); + // sign the string; String signature = HMAC(derived_signing_key, stringToSign); + _AWSSignCreateSignature(pSignature, iSigSize, strSign, pSignInfo->strKey, strDate, pSignInfo->strRegion, pSignInfo->strService); + // update signing key (signature chaining) + ds_strnzcpy(pSignInfo->strSignature, ds_fmtoctstring_lc(strSignTemp, sizeof(strSignTemp), pSignature, iSigSize), sizeof(pSignInfo->strSignature)); +} + +/*F********************************************************************************/ +/*! + \Function _AWSReadEventHeader + + \Description + Read an event header from an event + + \Input *pBuffer - buffer pointing to event to read + \Input iBufSize - size of buffer + \Input *pHeaderName - [out] buffer to store header name + \Input iNameSize - size of header name buffer + \Input *pHeaderValue - [out] buffer to store header value + \Input *pValueSize - [in] size of value buffer, [out] size of value + \Input *pValueType - [out] storage for value type + + \Output + int32_t - size of event + + \Version 01/17/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _AWSReadEventHeader(const uint8_t *pBuffer, int32_t iBufSize, char *pHeaderName, int32_t iNameSize, uint8_t *pHeaderValue, int32_t *pValueSize, AWSHeaderValueE *pValueType) +{ + int32_t iHdrLen, iValLen=0; + int32_t iOffset=0; + // get length + iHdrLen = pBuffer[iOffset++]; + // copy string + ds_strsubzcpy(pHeaderName, iNameSize, (const char *)pBuffer+iOffset, iHdrLen); + iOffset += iHdrLen; + // get value type + *pValueType = (AWSHeaderValueE)pBuffer[iOffset++]; + // get value length (assume string for now) $$todo - add support for other types + if ((*pValueType == AWS_BYTE_ARRAY) || (*pValueType == AWS_STRING)) + { + iValLen = pBuffer[iOffset++] << 8; + iValLen |= pBuffer[iOffset++]; + } + // copy value + ds_memcpy_s(pHeaderValue, *pValueSize, pBuffer+iOffset, iValLen); + iOffset += iValLen; + // null-terminate if string + if (*pValueType == AWS_STRING) + { + pHeaderValue[iValLen] = '\0'; + } + *pValueSize = iValLen; + return(iOffset); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function AWSSignSigV4 + + \Description + Sign request as per Amazon Signature Version 4 Signing Process: + https://docs.aws.amazon.com/en_us/general/latest/gr/signature-version-4.html + + \Input *pHeader - http header + \Input iHeaderSize - size of header buffer + \Input *pRequest - request body + \Input *pKeyInfo - signing key info ("keyid:key") + \Input *pService - AWS service + \Input *pSignInfo - [out, optional] storage for signing info for later use + + \Output + int32_t - updated header length + + \Version 12/26/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t AWSSignSigV4(char *pHeader, int32_t iHeaderSize, const char *pRequest, const char *pKeyInfo, const char *pService, AWSSignInfoT *pSignInfo) +{ + char strCanonHdrs[512]="", strSignedHdrs[256]="", strDateTime[32]="", strHost[256], strQuery[128]=""; + char strSig[(CRYPTSHA256_HASHSIZE*2)+1]; + char strRegion[32], strSign[256], strDate[9], strMethod[8], strUri[128]; + char strKey[64], strKeyId[32], strKeyPath[128]; + uint8_t aSigBuf[CRYPTSHA256_HASHSIZE]; + int32_t iOffset; + + // get/assemble info we need to sign request + + // get DateTime for X-Amz-Date header + _AWSGetDateTime(strDateTime, sizeof(strDateTime), strDate, sizeof(strDate)); + // append DateTime to header so it will be included in canonical/signed headers + iOffset = (int32_t)strlen(pHeader); + iOffset += ds_snzprintf(pHeader+iOffset, iHeaderSize-iOffset, "X-Amz-Date: %s\r\n", strDateTime); + // create canonical and signed header lists + _AWSSignGetHeaders(pHeader, strCanonHdrs, sizeof(strCanonHdrs), strSignedHdrs, sizeof(strSignedHdrs), strHost, sizeof(strHost), strMethod, sizeof(strMethod), strUri, sizeof(strUri), strQuery, sizeof(strQuery)); + // extract region and date info + _AWSGetHostInfo(strHost, strRegion, sizeof(strRegion)); + // extract keyid and key from secret + _AWSGetKeyInfo(pKeyInfo, strKeyId, sizeof(strKeyId), strKey, sizeof(strKey)); + // create keypath + ds_snzprintf(strKeyPath, sizeof(strKeyPath), "%s/%s/%s", strDate, strRegion, pService); + + // sign the request as per https://docs.aws.amazon.com/en_us/general/latest/gr/signature-version-4.html + + // Task 1: Create a Canonical Request for Signature Version 4 + _AWSSignCreateCanonicalRequest(strSig, sizeof(strSig), pHeader, pRequest, strDateTime, strCanonHdrs, strSignedHdrs, strMethod, strUri, strQuery); + // Task 2: Create a String to Sign for Signature Version 4 + _AWSSignCreateStringToSign(strSign, sizeof(strSign), "AWS4-HMAC-SHA256", strDateTime, strKeyPath, strSig); + NetPrintfVerbose((AWS_DEBUG, 0, "aws: StringToSign:\n%s\n", strSign)); + // Task 3: Calculate the Signature for AWS Signature Version 4 and convert it to a hexstring + _AWSSignCreateSignature(aSigBuf, sizeof(aSigBuf), strSign, strKey, strDate, strRegion, pService); + ds_fmtoctstring_lc(strSig, sizeof(strSig), aSigBuf, sizeof(aSigBuf)); + // Task 4: Create the Authorization header + iOffset += _AWSSignCreateAuthorization(pHeader+iOffset, iHeaderSize-iOffset, strSig, strKeyId, strDateTime, strKeyPath, strSignedHdrs); + + // save signing info + if (pSignInfo != NULL) + { + ds_strnzcpy(pSignInfo->strService, pService, sizeof(pSignInfo->strService)); + ds_strnzcpy(pSignInfo->strRegion, strRegion, sizeof(pSignInfo->strRegion)); + ds_strnzcpy(pSignInfo->strKeyPath, strKeyPath, sizeof(pSignInfo->strKeyPath)); + ds_strnzcpy(pSignInfo->strSignature, strSig, sizeof(pSignInfo->strSignature)); + ds_strnzcpy(pSignInfo->strKey, strKey, sizeof(pSignInfo->strKey)); + } + + // return updated header length + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function AWSWriteEvent + + \Description + Write an AWS signed binary event + + \Input *pBuffer - [out] buffer to write event + \Input iBufSize - size of buffer + \Input *pData - event data + \Input *pDataSize - [in/out] size of input data available/written + \Input *pEventType - event type name + \Input *pSignInfo - [in/out] signing info for event + + \Output + int32_t - size of event data written + + \Notes + The signature field in the signing info is used to sign the event, and + then updated with the new signature; AWS calls this "signature chaining". + + \Version 01/16/2019 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t AWSWriteEvent(uint8_t *pBuffer, int32_t iBufSize, const uint8_t *pData, int32_t *pDataSize, const char *pEventType, AWSSignInfoT *pSignInfo) +{ + int32_t iOffset=0, iHdrOff, iHdrLen, iAudOff, iAudLen, iSigOff, uCrc32; + uint64_t uTimeMs = ds_timeinsecs() * 1000; + static const uint8_t aZeroBuf[CRYPTSHA256_HASHSIZE] = { 0 }; + static const char *pContentType = "application/octet-stream"; + static const char *pMessageType = "event"; + const uint8_t *pSignData=(const uint8_t *)""; + int32_t iSignDataSize=0; + + // reserve room for outer chunk prelude + iOffset += AWS_EVENT_PRELUDE_SIZE; + + // write outer chunk headers + iOffset += _AWSWriteEventHeader(pBuffer+iOffset, iBufSize-iOffset, ":date", (const uint8_t *)&uTimeMs, sizeof(uTimeMs), AWS_TIMESTAMP); + iHdrOff = iOffset; + iOffset += _AWSWriteEventHeader(pBuffer+iOffset, iBufSize-iOffset, ":chunk-signature", aZeroBuf, sizeof(aZeroBuf), AWS_BYTE_ARRAY); + iSigOff = iOffset-sizeof(aZeroBuf); + iHdrLen = iOffset-AWS_EVENT_PRELUDE_SIZE; + + if (pData != NULL) + { + // reserve room for event chunk prelude + iAudOff = iOffset; + iOffset += AWS_EVENT_PRELUDE_SIZE; + + // write event chunk headers + iOffset += _AWSWriteEventHeader(pBuffer+iOffset, iBufSize-iOffset, ":event-type", (const uint8_t *)pEventType, (int32_t)strlen(pEventType), AWS_STRING); + iOffset += _AWSWriteEventHeader(pBuffer+iOffset, iBufSize-iOffset, ":content-type", (const uint8_t *)pContentType, (int32_t)strlen(pContentType), AWS_STRING); + iOffset += _AWSWriteEventHeader(pBuffer+iOffset, iBufSize-iOffset, ":message-type", (const uint8_t *)pMessageType, (int32_t)strlen(pMessageType), AWS_STRING); + iAudLen = iOffset-iAudOff-AWS_EVENT_PRELUDE_SIZE; + + // calculate how much data we can write, including the event chunk message_crc and outer chunk message_crc + *pDataSize = DS_MIN(*pDataSize, iBufSize-iOffset-4-4); + // copy event data + ds_memcpy(pBuffer+iOffset, pData, *pDataSize); + iOffset += *pDataSize; + + // finish event chunk (add four bytes for crc32) + _AWSWriteEventPrelude(pBuffer+iAudOff, iAudLen, iOffset+4-iAudOff); + + // calculate and write event chunk message_crc + uCrc32 = NetCrc32(pBuffer+iAudOff, iOffset-iAudOff, NULL); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>24); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>16); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>8); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>0); + + // locate data to sign + pSignData = pBuffer+iAudOff; + iSignDataSize = iOffset-iAudOff; + } + + // sign the event + _AWSWriteSignEvent(pBuffer+iSigOff, CRYPTSHA256_HASHSIZE, pBuffer+AWS_EVENT_PRELUDE_SIZE, iHdrOff-AWS_EVENT_PRELUDE_SIZE, pSignData, iSignDataSize, pSignInfo); + + // finish outer chunk (add four bytes for crc32) + _AWSWriteEventPrelude(pBuffer, iHdrLen, iOffset+4); + + // calculate and write outer chunk message_crc + uCrc32 = NetCrc32(pBuffer, iOffset, NULL); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>24); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>16); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>8); + pBuffer[iOffset++] = (uint8_t)(uCrc32>>0); + + // return offset to caller + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function AWSReadEvent + + \Description + Read an AWS signed binary event + + \Input *pBuffer - buffer pointing to event to read + \Input iBufSize - size of buffer + \Input *pEventType - [out] buffer to store header event type + \Input iEventSize - size of event type buffer + \Input *pMessage - [out] buffer to store event message + \Input *pMessageSize - [in] size of message buffer, [out] size of message + + \Output + int32_t - size of event + + \Notes + This function does not attempt to verify the CRCs. + + \Version 01/17/2019 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t AWSReadEvent(const uint8_t *pBuffer, int32_t iBufSize, char *pEventType, int32_t iEventSize, char *pMessage, int32_t *pMessageSize) +{ + char strHeaderName[128], strHeaderValue[1024]; + int32_t iOffset=0, iValueLen; + int32_t iMsgLen, iHdrLen; + AWSHeaderValueE eValueType; + int32_t iResult = -1; + + // make sure we have enough data for prelude + if (iBufSize < AWS_EVENT_PRELUDE_SIZE) + { + return(iResult); + } + + // read overall message length + iMsgLen = pBuffer[iOffset++] << 24; + iMsgLen |= pBuffer[iOffset++] << 16; + iMsgLen |= pBuffer[iOffset++] << 8; + iMsgLen |= pBuffer[iOffset++] << 0; + // read header length + iHdrLen = pBuffer[iOffset++] << 24; + iHdrLen |= pBuffer[iOffset++] << 16; + iHdrLen |= pBuffer[iOffset++] << 8; + iHdrLen |= pBuffer[iOffset++] << 0; + // skip crc $$todo - verify this? + iOffset += 4; + + // calculate actual message length (total-headers-prelude-crc32 + iMsgLen = iMsgLen-iHdrLen-AWS_EVENT_PRELUDE_SIZE-4; + + // get message headers + while (iOffset < (iHdrLen+AWS_EVENT_PRELUDE_SIZE)) + { + iOffset += _AWSReadEventHeader(pBuffer+iOffset, iBufSize-iOffset, strHeaderName, sizeof(strHeaderName), (uint8_t *)strHeaderValue, (iValueLen=(int32_t)sizeof(strHeaderValue), &iValueLen), &eValueType); + if (eValueType != AWS_STRING) + { + NetPrintfVerbose((AWS_DEBUG, 0, "aws: %s: [%d bytes] type=%d\n", strHeaderName, iValueLen, eValueType)); + continue; + } + + NetPrintfVerbose((AWS_DEBUG, 0, "aws: %s: %s\n", strHeaderName, strHeaderValue)); + if (!ds_stricmp(strHeaderName, ":event-type") || !ds_stricmp(strHeaderName, ":exception-type")) + { + ds_strnzcpy(pEventType, strHeaderValue, iEventSize); + } + } + + // copy message body + ds_strsubzcpy(pMessage, *pMessageSize, (const char *)pBuffer+iOffset, iMsgLen); + *pMessageSize = iMsgLen; + // skip past message + iOffset += iMsgLen; + // skip past crc32 + iOffset += 4; + // return offset past event to caller + return(iOffset); +} diff --git a/r5dev/thirdparty/dirtysdk/source/util/base64.c b/r5dev/thirdparty/dirtysdk/source/util/base64.c new file mode 100644 index 00000000..b5fe0275 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/base64.c @@ -0,0 +1,586 @@ +/*H*******************************************************************/ +/*! + \File base64.c + + \Description + This module implements Base-64 encoding/decoding as defined in RFC + 4648: https://tools.ietf.org/html/rfc4648 + + \Notes + No strict compliance check has been made. Instead it has only + been confirmed that Decode(Encode(x)) = x for various inputs + which was all that was required for the original application. + + \Copyright + Copyright (c) Electronic Arts 2003-2017. ALL RIGHTS RESERVED. + + \Version 1.0 12/11/2003 (SJB) First Version +*/ +/*******************************************************************H*/ + +/*** Include files ***************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/util/base64.h" + +/*** Defines *********************************************************/ + +/*** Type Definitions ************************************************/ + +/*** Variables *******************************************************/ + +/* + The table used in converting an unsigned char -> Base64. + This is as per the RFC. +*/ +static const char _Base64_strEncode[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/* The table used in converting an unsigned char -> Base64 (URL) + This is as per the RFC: https://tools.ietf.org/html/rfc4648#section-5 */ +static const char _Base64_strEncodeUrl[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789-_"; + +/* + The table used in coverting Base64 -> unsigned char. + This is not strictly needed since this mapping can be determined + by searching _Base64_Encode, but by having it as a separate table + it means the decode can run in O(n) time where n is the length of + the input rather than O(n*m) where m is 64. + + The table is designed such that after subtracting '+' from the + base 64 value, the corresponding char value can be found by + indexing into this table. Since the characters chosen by the + Base64 designers are not contiguous in ASCII there are some holes + in the table and these are filled with -1 to indicate an invalid + value. +*/ +static const signed char _Base64_strDecode[] = +{ + 62, // + + -1, -1, -1, // ,-. + 63, // / + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9 + -1, -1, -1, -1, -1, -1, -1, // :;<=>?@ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, // A- + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // -Z + -1, -1, -1, -1, -1, -1, // [\]^_` + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // a- + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // -z +}; + +static const signed char _Base64_strDecodeUrl[] = +{ + -1, -1, // +, + 62, // - + -1, -1, // ./ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9 + -1, -1, -1, -1, -1, -1, -1, // :;<=>?@ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, // A- + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // -Z + -1, -1, -1, -1, // [\]^ + 63, // _ + -1, // ` + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // a- + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // -z +}; + +/*** Private functions ***********************************************/ + +/*F*******************************************************************/ +/*! + \Function _Base64Encode + + \Description + Base-64 encode a string. + + \Input iInputLen - the length of the input + \Input *pInput - the input + \Input *pOutput - the output + \Input iOutputLen - the output buffer size + \Input *pEncode - the table to use for encoding + \Input bPadded - do you want padding to be encoded? + + \Output + int32_t - encoded length, or -1 on error + + \Notes + pOutput is NUL terminated. + + It is assumed that pOutput is large enough to hold + Base64EncodedSize(iInputLen)+1 bytes. + + The following chart should help explain the various bit shifts + that are in the code. On the top are the 8-bit bytes of the + input and on the bottom are the 6-bit bytes of the output :- + + \verbatim + | 0 | 1 | 2 | in + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0 | 1 | 2 | 3 | out + \endverbatim + + As the above shows the out0 is the top 6 bits of in0, out1 is + the bottom two bits of in0 along with the top 4 bits of in1, + ... etc. + + To speed things up the encoder tries to work on chunks of 3 + input bytes at a time since that produces 4 output bytes + without having to maintain any inter-byte processing state. + + For any input that is not a multiple of 3 then 1 or 2 padding + bytes are are added as appropriate as described in the RFC. + + \Version 12/11/2002 (sbevan) First Version +*/ +/*******************************************************************F*/ +static int32_t _Base64Encode(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen, const char *pEncode, uint8_t bPadded) +{ + int32_t iInp, iOut; + + // validate output buffer size, including null termination + if ((Base64EncodedSize(iInputLen)+1) > iOutputLen) + { + return(-1); + } + + // encode the data + for (iInp=0, iOut=0; iInputLen >= 3; iOut += 4, iInp += 3, iInputLen -= 3) + { + pOutput[iOut+0] = pEncode[(pInput[iInp]>>2)&0x3f]; + pOutput[iOut+1] = pEncode[((pInput[iInp]&0x3)<<4)|((pInput[iInp+1]>>4)&0x0f)]; + pOutput[iOut+2] = pEncode[((pInput[iInp+1]&0xf)<<2)|((pInput[iInp+2]>>6)&0x03)]; + pOutput[iOut+3] = pEncode[(pInput[iInp+2]&0x3f)]; + } + switch (iInputLen) + { + case 1: + pOutput[iOut+0] = pEncode[(pInput[iInp]>>2)&0x3f]; + pOutput[iOut+1] = pEncode[((pInput[iInp]&0x3)<<4)]; + + if (bPadded) + { + pOutput[iOut+2] = '='; + pOutput[iOut+3] = '='; + iOut += 4; + } + else + { + iOut += 2; + } + break; + case 2: + pOutput[iOut+0] = pEncode[(pInput[iInp]>>2)&0x3f]; + pOutput[iOut+1] = pEncode[((pInput[iInp]&0x3)<<4)|((pInput[iInp+1]>>4)&0x0f)]; + pOutput[iOut+2] = pEncode[((pInput[iInp+1]&0xf)<<2)]; + + if (bPadded) + { + pOutput[iOut+3] = '='; + iOut += 4; + } + else + { + iOut += 3; + } + break; + } + pOutput[iOut] = '\0'; + return(iOut); +} + +/*F*******************************************************************/ +/*! + \Function _Base64Decode + + \Description + Decode a Base-64 encoded string. + + \Input *pInput - the Base-64 encoded string + \Input iInputLen - the length of the encoded input + \Input *pOutput - [out] the decoded string, or NULL to calculate output size + \Input iOutputLen - size of output buffer + \Input *pDecode - the table to use for the decoding + + \Output + int32_t - decoded size on success, zero on error. + + \Notes + The only reasons for failure are if the input base64 sequence does + not amount to a number of characters comprising an even multiple + of four characters, or if the input contains a character not in + the Base64 character set and not a whitespace character. This + behavior is mentioned in the RFC: + + "In base64 data, characters other than those in Table 1, + line breaks, and other white space probably indicate a + transmission error, about which a warning message or even + a message rejection might be appropriate under some + circumstances." + + If pOutput is NULL, the function will return the number of + characters required to buffer the output, or zero on error. + + The following chart should help explain the various bit shifts + that are in the code. On the top are the 6-bit bytes in the + input and and on the bottom are the 8-bit bytes of the output :- + + \verbatim + | 0 | 1 | 2 | 3 | in + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | 0 | 1 | 2 | out + \endverbatim + + As the above shows the out0 consists of all of in0 and the top + 2 bits of in1, out1 is the bottom 4 bits of in1 and the top 4 + bits of in2, ... etc. + + \Version 12/11/2002 (sbevan) First Version + \Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality +*/ +/*******************************************************************F*/ +static int32_t _Base64Decode(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen, const signed char *pDecode) +{ + char ci[4]; + int8_t co0, co1, co2, co3; + int32_t iInputCnt, iInputOff, iOutputOff; + + for (iInputOff = 0, iOutputOff = 0; iInputOff < iInputLen; ) + { + // scan input + for (iInputCnt = 0; (iInputCnt < 4) && (iInputOff < iInputLen) && (pInput[iInputOff] != '\0') && (iOutputOff < iOutputLen); iInputOff += 1) + { + // ignore whitespace + if ((pInput[iInputOff] == ' ') || (pInput[iInputOff] == '\t') || (pInput[iInputOff] == '\r') || (pInput[iInputOff] == '\n')) + { + continue; + } + // basic range validation + else if ((pInput[iInputOff] < '+') || (pInput[iInputOff] > 'z')) + { + return(0); + } + // fetch input character + else + { + ci[iInputCnt++] = pInput[iInputOff]; + } + } + // if we didn't get anything, we're done + if (iInputCnt == 0) + { + break; + } + if (iInputCnt < 4) + { + // error if we did not get at least 2 + if (iInputCnt < 2) + { + return(0); + } + // otherwise for everything under the quad, default to padding + if (iInputCnt < 3) + { + ci[2] = '='; + } + ci[3] = '='; + } + // decode the sequence + co0 = pDecode[(int32_t)ci[0]-'+']; + co1 = pDecode[(int32_t)ci[1]-'+']; + co2 = pDecode[(int32_t)ci[2]-'+']; + co3 = pDecode[(int32_t)ci[3]-'+']; + + if ((co0 >= 0) && (co1 >= 0)) + { + if ((co2 >= 0) && (co3 >= 0)) + { + if ((pOutput != NULL) && ((iOutputLen - iOutputOff) > 2)) + { + pOutput[iOutputOff+0] = (co0<<2)|((co1>>4)&0x3); + pOutput[iOutputOff+1] = (co1&0x3f)<<4|((co2>>2)&0x3F); + pOutput[iOutputOff+2] = ((co2&0x3)<<6)|co3; + } + iOutputOff += 3; + } + else if ((co2 >= 0) && (ci[3] == '=')) + { + if ((pOutput != NULL) && ((iOutputLen - iOutputOff) > 1)) + { + pOutput[iOutputOff+0] = (co0<<2)|((co1>>4)&0x3); + pOutput[iOutputOff+1] = (co1&0x3f)<<4|((co2>>2)&0x3F); + } + iOutputOff += 2; + // consider input complete + iInputOff = iInputLen; + } + else if ((ci[2] == '=') && (ci[3] == '=')) + { + if ((pOutput != NULL) && ((iOutputLen - iOutputOff) > 0)) + { + pOutput[iOutputOff+0] = (co0<<2)|((co1>>4)&0x3); + } + iOutputOff += 1; + // consider input complete + iInputOff = iInputLen; + } + else + { + // illegal input character + return(0); + } + } + else + { + // illegal input character + return(0); + } + } + // return length of decoded data or zero if decoding failed + return((iInputOff == iInputLen) ? iOutputOff : 0); +} + +/*** Public functions ************************************************/ + +/*F*******************************************************************/ +/*! + \Function Base64Encode + + \Deprecated + The calling convention for Base64Encode will be replaced with + that of Base64Encode2 in a future release. + + \Description + Base-64 encode a string. + + \Input iInputLen - the length of the input + \Input *pInput - the input + \Input *pOutput - the output + + \Notes + pOutput is NUL terminated. + + It is assumed that pOutput is large enough to hold + Base64EncodedSize(iInputLen)+1 bytes. + + \Version 1.0 12/11/2002 (SJB) First Version +*/ +/*******************************************************************F*/ +void Base64Encode(int32_t iInputLen, const char *pInput, char *pOutput) +{ + Base64Encode2(pInput, iInputLen, pOutput, 0x7fffffff); +} + +/*F*******************************************************************/ +/*! + \Function Base64Encode2 + + \Description + Base-64 encode a string. + + \Input iInputLen - the length of the input + \Input *pInput - the input + \Input *pOutput - the output + \Input iOutputLen - the output buffer size + + \Output + int32_t - encoded length, or -1 on error + + \Notes + pOutput is NUL terminated. + + \Version 12/11/2002 (sbevan) First Version +*/ +/*******************************************************************F*/ +int32_t Base64Encode2(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen) +{ + return(_Base64Encode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strEncode, TRUE)); +} + +/*F*******************************************************************/ +/*! + \Function Base64Decode + + \Deprecated + The calling convention for Base64Decode will be replaced with + that of Base64Decode3 in a future release. + + \Description + Decode a Base-64 encoded string. + + \Input iInputLen - the length of the encoded input + \Input *pInput - the Base-64 encoded string + \Input *pOutput - [out] the decoded string, or NULL to calculate output size + + \Output + int32_t - true on success, false on error. + + \Notes + It is assumed that pOutput is large enough to hold + Base64DecodedSize(iInputLen) bytes. If pOutput is NULL, + the function will return the number of characters required to + buffer the output or zero on error. + + \Version 12/11/2002 (sbevan) First Version + \Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality +*/ +/*******************************************************************F*/ +int32_t Base64Decode(int32_t iInputLen, const char *pInput, char *pOutput) +{ + int32_t iOutputLen = Base64Decode2(iInputLen, pInput, pOutput); + if (pOutput != NULL) + { + iOutputLen = iOutputLen ? TRUE : FALSE; + } + return(iOutputLen); +} + +/*F*******************************************************************/ +/*! + \Function Base64Decode2 + + \Deprecated + The calling convention for Base64Decode2 will be replaced with + that of Base64Decode3 in a future release. + + \Description + Decode a Base-64 encoded string. + + \Input iInputLen - the length of the encoded input + \Input *pInput - the Base-64 encoded string + \Input *pOutput - [out[ the decoded string, or NULL to calculate output size + + \Output + int32_t - decoded size on success, zero on error. + + \Notes + It is assumed that pOutput is large enough to hold + Base64DecodedSize(iInputLen) bytes. If pOutput is NULL, + the function will return the number of characters required to + buffer the output or zero on error. + + \Version 12/11/2002 (sbevan) First Version + \Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality +*/ +/*******************************************************************F*/ +int32_t Base64Decode2(int32_t iInputLen, const char *pInput, char *pOutput) +{ + return(Base64Decode3(pInput, iInputLen, pOutput, 0x7fffffff)); +} + +/*F*******************************************************************/ +/*! + \Function Base64Decode3 + + \Description + Decode a Base-64 encoded string. + + \Input *pInput - the Base-64 encoded string + \Input iInputLen - the length of the encoded input + \Input *pOutput - [out] the decoded string, or NULL to calculate output size + \Input iOutputLen - size of output buffer + + \Output + int32_t - decoded size on success, zero on error. + + \Notes + If pOutput is NULL, the function will return the number of + characters required to buffer the output, or zero on error. + + \Version 12/11/2002 (sbevan) First Version + \Version 01/29/2009 (jbrookes) Enhanced to skip whitespace, added decoded length functionality +*/ +/*******************************************************************F*/ +int32_t Base64Decode3(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen) +{ + return(_Base64Decode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strDecode)); +} + +/*F*******************************************************************/ +/*! + \Function Base64EncodeUrl + + \Description + Base-64 encode a string url. + + \Input *pInput - the input + \Input iInputLen - the length of the input + \Input *pOutput - the output + \Input iOutputLen - the output buffer size + + \Output + int32_t - encoded length, or -1 on error + + \Notes + pOutput is NUL terminated. + + \Version 11/15/2017 (eesponda) +*/ +/*******************************************************************F*/ +int32_t Base64EncodeUrl(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen) +{ + return(Base64EncodeUrl2(pInput, iInputLen, pOutput, iOutputLen, TRUE)); +} + +/*F*******************************************************************/ +/*! + \Function Base64EncodeUrl2 + + \Description + Base-64 encode a string url with extra options + + \Input *pInput - the input + \Input iInputLen - the length of the input + \Input *pOutput - the output + \Input iOutputLen - the output buffer size + \Input bPadded - is the output padded? + + \Output + int32_t - encoded length, or -1 on error + + \Notes + pOutput is NUL terminated. + + \Version 05/20/2020 (eesponda) +*/ +/*******************************************************************F*/ +int32_t Base64EncodeUrl2(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen, uint8_t bPadded) +{ + return(_Base64Encode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strEncodeUrl, bPadded)); +} + +/*F*******************************************************************/ +/*! + \Function Base64DecodeUrl + + \Description + Decode a Base-64 encoded url string. + + \Input *pInput - the Base-64 encoded string + \Input iInputLen - the length of the encoded input + \Input *pOutput - [out] the decoded string, or NULL to calculate output size + \Input iOutputLen - size of output buffer + + \Output + int32_t - decoded size on success, zero on error. + + \Notes + If pOutput is NULL, the function will return the number of + characters required to buffer the output, or zero on error. + + \Version 11/15/2017 (eesponda) +*/ +/*******************************************************************F*/ +int32_t Base64DecodeUrl(const char *pInput, int32_t iInputLen, char *pOutput, int32_t iOutputLen) +{ + return(_Base64Decode(pInput, iInputLen, pOutput, iOutputLen, _Base64_strDecodeUrl)); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/util/binary7.c b/r5dev/thirdparty/dirtysdk/source/util/binary7.c new file mode 100644 index 00000000..cbafec29 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/binary7.c @@ -0,0 +1,157 @@ +/*H*************************************************************************************/ +/*! + \File binary7.c + + \Description + This module provides routines to encode/decode binary7 data to/from a buffer. + + \Copyright + Copyright (c) Electronic Arts 2009. ALL RIGHTS RESERVED. + + \Version 1.0 11/02/2009 (cadam) First version +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include +#include +#include + +// include platform types and functions +#include "DirtySDK/platform.h" + +// self-include +#include "DirtySDK/util/binary7.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +/*** Private Functions *****************************************************************/ + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function Binary7Encode + + \Description + Encode binary data to fit in 7-bit high-ASCII characters. All of the requested + data will be encoded in a reversible form that is slightly larger than the + original data. + + \Input *pDst - destination buffer (will hold encoded data) + \Input iDstLen - destination buffer max size (in bytes) + \Input *pSrc - source buffer + \Input iSrcLen - source buffer length (in bytes) + \Input bTerminate - TRUE to terminate the buffer, else FALSE. + + \Output + int32_t - < 0 indicates error; >= 0 indicates bytes in converted data + + \Notes + If there is insufficient space to store all the encoded data, an error is + returned immediately; no data is encoded or written. + Storage requirements are (slen*8+6)/7 bytes. + + \Version 01/20/2004 (djones) +*/ +/*************************************************************************************F*/ +char *Binary7Encode(unsigned char *pDst, int32_t iDstLen, unsigned const char *pSrc, int32_t iSrcLen, uint32_t bTerminate) +{ + int32_t iBytesLeft = iSrcLen; + uint32_t uBuffer = 0; // buffer for holding leftover bits + int32_t iBits = 0; // number of bits currently in buffer + + // verify destination buffer is big enough to hold encoded data. + if (iDstLen < ((((iSrcLen * 8) + 6) / 7) + (signed)bTerminate)) + { + return(NULL); + } + + // encode all bytes. + while (iBytesLeft-- > 0) + { + uBuffer |= (*pSrc++) << iBits; // merge new bits up above leftover bits + iBits += 8; + while (iBits >= 7) // have enough bits to write out an encoded byte + { + *pDst++ = 0x80 | (uBuffer & 0x7F); // write encoded byte + uBuffer >>= 7; // discard written bits + iBits -= 7; + } + } + + // a few bits left over? + if (iBits > 0) + { + *pDst++ = 0x80 | uBuffer; // write leftover bits + } + + // terminate? + if (bTerminate) + { + *pDst = '\0'; + } + + // return pointer past end of dest buffer + return((char *)pDst); +} + +/*F*************************************************************************************/ +/*! + \Function Binary7Decode + + \Description + Decode binary data from 7-bit high-ASCII characters to 8-bit data. + + \Input *pDst - destination buffer (will hold decoded data) + \Input iDstLen - destination buffer max size (in bytes) + \Input *pSrc` - source buffer + + \Output + int32_t - < 0 indicates error; >= 0 indicates bytes in converted data + + \Notes + Decoding will stop at the first low-ASCII character, or until dlen bytes have + been written, whichever comes first. Storage requirements are slen*7/8 bytes. + + \Version 01/20/2004 (djones) +*/ +/*************************************************************************************F*/ +unsigned const char *Binary7Decode(unsigned char *pDst, int32_t iDstLen, unsigned const char *pSrc) +{ + uint32_t uBuffer = 0; // buffer for holding leftover bits + int32_t iBits = 0; // number of bits currently in buffer + + // Begin decoding bytes. + while ((*pSrc & 0x80) && (iDstLen > 0)) + { + uBuffer |= (*pSrc++ & 0x7F) << iBits; // merge new bits up above leftover bits + iBits += 7; + if (iBits >= 8) // have enough bits to write out a decoded byte + { + *pDst++ = uBuffer & 0xFF; // write decoded byte + uBuffer >>= 8; // discard written bits + iBits -= 8; + iDstLen -= 1; + } + } + + // Unlike encoding, where all source bits must be converted, + // in decoding, extra bits are discarded, so no special + // handling is required for leftover bits here. + + // return pointer past end of dest buffer + return(pSrc); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/util/hpack.c b/r5dev/thirdparty/dirtysdk/source/util/hpack.c new file mode 100644 index 00000000..7e15d380 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/hpack.c @@ -0,0 +1,1430 @@ +/*H********************************************************************************/ +/*! + \File hpack.c + + \Description + This module implements a decode/encoder based on the HPACK spec + (https://tools.ietf.org/html/rfc7541). Which is used for encoding/decoding + the HEADERS frame in the HTTP/2 protocol. + + \Copyright + Copyright (c) Electronic Arts 2016. ALL RIGHTS RESERVED. +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/proto/protohttputil.h" +#include "DirtySDK/util/hpack.h" + +/*** Defines **********************************************************************/ + +//! size of the static table +#define HPACK_STATIC_TABLE_SIZE (61) + +//! size of huffman code table +#define HPACK_HUFFMAN_TABLE_SIZE (256) + +//! overhead for a table entry +#define HPACK_TABLE_ENTRY_OVERHEAD (32) + +//! controls if we log the dynamic table info +#define HPACK_DYNAMICTABLE_LOGGING (DIRTYCODE_LOGGING && FALSE) + +//! flags for different header field types +#define HPACK_INDEXED_HEADER_FIELD (1 << 7) +#define HPACK_LITERAL_HEADER_FIELD (1 << 6) +#define HPACK_DYNAMIC_TABLE_UPDATE (1 << 5) +#define HPACK_LITERAL_HEADER_FIELD_NEVER (1 << 4) + +/*** Type Definitions *************************************************************/ + +//! defines an entry in the dynamic table +typedef struct TableEntryT +{ + char *pName; //!< header name + char *pValue; //!< header value +} TableEntryT; + +//! defines an entry in the static table +typedef struct StaticTableEntryT +{ + const char *pName; //!< header name + const char *pValue; //!< header value +} StaticTableEntryT; + +//! linked list node we use to define our dynamic table +typedef struct DynamicNodeT +{ + struct DynamicNodeT *pNext; + struct DynamicNodeT *pPrev; + + TableEntryT Entry; +} DynamicNodeT; + +//! entry in the huffman tree +typedef struct HuffmanCodeT +{ + uint32_t uCode; //!< the huffman code + uint32_t uLen; //!< the length of the code in bits +} HuffmanCodeT; + +//! tree used for huffman decoding +typedef struct HuffmanNodeT +{ + struct HuffmanNodeT *pLeft; //!< 1 bit + struct HuffmanNodeT *pRight; //!< 0 bit + + uint8_t uChar; //!< the character represented by the sequence + uint8_t _pad[3]; +} HuffmanNodeT; + +//! data used to keep track of the huffman encoding +typedef struct HuffmanEncodeT +{ + uint32_t aResult[256]; + uint32_t uCount; + uint32_t uNextBoundary; +} HuffmanEncodeT; + +//! module ref for our encoder/decoder +struct HpackRefT +{ + DynamicNodeT *pHead; //!< points to the head of the dynamic table (for insertion/lookup) + DynamicNodeT *pTail; //!< points to the tail of the dynamic table (for removal) + + uint32_t uTableSize; //!< current size of the dynamic table + uint32_t uTableMax; //!< maximum size of the dynamic table + + HuffmanNodeT *pHuffmanTree; //!< huffman tree used for decoding + + //! memgroup data + int32_t iMemGroup; + void *pMemGroupUserData; + + uint8_t bUpdatePending; //!< there is a pending update to the dynamic table + uint8_t _pad[3]; +}; + +/*** Variables ********************************************************************/ + +//! our fixed static table +static const StaticTableEntryT Hpack_aStaticTable[HPACK_STATIC_TABLE_SIZE] = +{ + { ":authority", "" }, + { ":method", "GET" }, + { ":method", "POST" }, + { ":path", "/" }, + { ":path", "/index.html" }, + { ":scheme", "http" }, + { ":scheme", "https" }, + { ":status", "200" }, + { ":status", "204" }, + { ":status", "206" }, + { ":status", "304" }, + { ":status", "400" }, + { ":status", "404" }, + { ":status", "500" }, + { "accept-charset", "" }, + { "accept-encoding", "gzip, deflate" }, + { "accept-language", "" }, + { "accept-ranges", "" }, + { "accept", "" }, + { "access-control-allow-origin", "" }, + { "age", "" }, + { "allow", "" }, + { "authorization", "" }, + { "cache-control", "" }, + { "content-disposition", "" }, + { "content-encoding", "" }, + { "content-language", "" }, + { "content-length", "" }, + { "content-location", "" }, + { "content-range", "" }, + { "content-type", "" }, + { "cookie", "" }, + { "date", "" }, + { "etag", "" }, + { "expect", "" }, + { "expires", "" }, + { "from", "" }, + { "host", "" }, + { "if-match", "" }, + { "if-modified-since", "" }, + { "if-none-match", "" }, + { "if-range", "" }, + { "if-unmodified-since", "" }, + { "last-modified", "" }, + { "link", "" }, + { "location", "" }, + { "max-forwards", "" }, + { "proxy-authenticate", "" }, + { "proxy-authorization", "" }, + { "range", "" }, + { "referer", "" }, + { "refresh", "" }, + { "retry-after", "" }, + { "server", "" }, + { "set-cookie", "" }, + { "strict-transport-security", "" }, + { "transfer-encoding", "" }, + { "user-agent", "" }, + { "vary", "" }, + { "via", "" }, + { "www-authenticate", "" } +}; + +//! table of huffman codes used for encoding / decoding based on +//! https://tools.ietf.org/html/rfc7541#appendix-B +static const HuffmanCodeT Hpack_aHuffmanCodes[256] = +{ + { 0x00001ff8, 13 }, { 0x007fffd8, 23 }, { 0x0fffffe2, 28 }, { 0x0fffffe3, 28 }, { 0x0fffffe4, 28 }, { 0x0fffffe5, 28 }, { 0x0fffffe6, 28 }, { 0x0fffffe7, 28 }, /* 0 - 7 */ + { 0x0fffffe8, 28 }, { 0x00ffffea, 24 }, { 0x3ffffffc, 30 }, { 0x0fffffe9, 28 }, { 0x0fffffea, 28 }, { 0x3ffffffd, 30 }, { 0x0fffffeb, 28 }, { 0x0fffffec, 28 }, /* 8 - 15 */ + { 0x0fffffed, 28 }, { 0x0fffffee, 28 }, { 0x0fffffef, 28 }, { 0x0ffffff0, 28 }, { 0x0ffffff1, 28 }, { 0x0ffffff2, 28 }, { 0x3ffffffe, 30 }, { 0x0ffffff3, 28 }, /* 16 - 23 */ + { 0x0ffffff4, 28 }, { 0x0ffffff5, 28 }, { 0x0ffffff6, 28 }, { 0x0ffffff7, 28 }, { 0x0ffffff8, 28 }, { 0x0ffffff9, 28 }, { 0x0ffffffa, 28 }, { 0x0ffffffb, 28 }, /* 24 - 31 */ + { 0x00000014, 6 }, { 0x000003f8, 10 }, { 0x000003f9, 10 }, { 0x00000ffa, 12 }, { 0x00001ff9, 13 }, { 0x00000015, 6 }, { 0x000000f8, 8 }, { 0x000007fa, 11 }, /* 32 - 39 */ + { 0x000003fa, 10 }, { 0x000003fb, 10 }, { 0x000000f9, 8 }, { 0x000007fb, 11 }, { 0x000000fa, 8 }, { 0x00000016, 6 }, { 0x00000017, 6 }, { 0x00000018, 6 }, /* 40 - 47 */ + { 0x00000000, 5 }, { 0x00000001, 5 }, { 0x00000002, 5 }, { 0x00000019, 6 }, { 0x0000001a, 6 }, { 0x0000001b, 6 }, { 0x0000001c, 6 }, { 0x0000001d, 6 }, /* 48 - 55 */ + { 0x0000001e, 6 }, { 0x0000001f, 6 }, { 0x0000005c, 7 }, { 0x000000fb, 8 }, { 0x00007ffc, 15 }, { 0x00000020, 6 }, { 0x00000ffb, 12 }, { 0x000003fc, 10 }, /* 56 - 63 */ + { 0x00001ffa, 13 }, { 0x00000021, 6 }, { 0x0000005d, 7 }, { 0x0000005e, 7 }, { 0x0000005f, 7 }, { 0x00000060, 7 }, { 0x00000061, 7 }, { 0x00000062, 7 }, /* 64 - 71 */ + { 0x00000063, 7 }, { 0x00000064, 7 }, { 0x00000065, 7 }, { 0x00000066, 7 }, { 0x00000067, 7 }, { 0x00000068, 7 }, { 0x00000069, 7 }, { 0x0000006a, 7 }, /* 72 - 79 */ + { 0x0000006b, 7 }, { 0x0000006c, 7 }, { 0x0000006d, 7 }, { 0x0000006e, 7 }, { 0x0000006f, 7 }, { 0x00000070, 7 }, { 0x00000071, 7 }, { 0x00000072, 7 }, /* 80 - 87 */ + { 0x000000fc, 8 }, { 0x00000073, 7 }, { 0x000000fd, 8 }, { 0x00001ffb, 13 }, { 0x0007fff0, 19 }, { 0x00001ffc, 13 }, { 0x00003ffc, 14 }, { 0x00000022, 6 }, /* 88 - 95 */ + { 0x00007ffd, 15 }, { 0x00000003, 5 }, { 0x00000023, 6 }, { 0x00000004, 5 }, { 0x00000024, 6 }, { 0x00000005, 5 }, { 0x00000025, 6 }, { 0x00000026, 6 }, /* 96 - 103 */ + { 0x00000027, 6 }, { 0x00000006, 5 }, { 0x00000074, 7 }, { 0x00000075, 7 }, { 0x00000028, 6 }, { 0x00000029, 6 }, { 0x0000002a, 6 }, { 0x00000007, 5 }, /* 104 - 111 */ + { 0x0000002b, 6 }, { 0x00000076, 7 }, { 0x0000002c, 6 }, { 0x00000008, 5 }, { 0x00000009, 5 }, { 0x0000002d, 6 }, { 0x00000077, 7 }, { 0x00000078, 7 }, /* 112 - 119 */ + { 0x00000079, 7 }, { 0x0000007a, 7 }, { 0x0000007b, 7 }, { 0x00007ffe, 15 }, { 0x000007fc, 11 }, { 0x00003ffd, 14 }, { 0x00001ffd, 13 }, { 0x0ffffffc, 28 }, /* 120 - 127 */ + { 0x000fffe6, 20 }, { 0x003fffd2, 22 }, { 0x000fffe7, 20 }, { 0x000fffe8, 20 }, { 0x003fffd3, 22 }, { 0x003fffd4, 22 }, { 0x003fffd5, 22 }, { 0x007fffd9, 23 }, /* 128 - 135 */ + { 0x003fffd6, 22 }, { 0x007fffda, 23 }, { 0x007fffdb, 23 }, { 0x007fffdc, 23 }, { 0x007fffdd, 23 }, { 0x007fffde, 23 }, { 0x00ffffeb, 24 }, { 0x007fffdf, 23 }, /* 136 - 143 */ + { 0x00ffffec, 24 }, { 0x00ffffed, 24 }, { 0x003fffd7, 22 }, { 0x007fffe0, 23 }, { 0x00ffffee, 24 }, { 0x007fffe1, 23 }, { 0x007fffe2, 23 }, { 0x007fffe3, 23 }, /* 144 - 151 */ + { 0x007fffe4, 23 }, { 0x001fffdc, 21 }, { 0x003fffd8, 22 }, { 0x007fffe5, 23 }, { 0x003fffd9, 22 }, { 0x007fffe6, 23 }, { 0x007fffe7, 23 }, { 0x00ffffef, 24 }, /* 152 - 159 */ + { 0x003fffda, 22 }, { 0x001fffdd, 21 }, { 0x000fffe9, 20 }, { 0x003fffdb, 22 }, { 0x003fffdc, 22 }, { 0x007fffe8, 23 }, { 0x007fffe9, 23 }, { 0x001fffde, 21 }, /* 160 - 167 */ + { 0x007fffea, 23 }, { 0x003fffdd, 22 }, { 0x003fffde, 22 }, { 0x00fffff0, 24 }, { 0x001fffdf, 21 }, { 0x003fffdf, 22 }, { 0x007fffeb, 23 }, { 0x007fffec, 23 }, /* 168 - 175 */ + { 0x001fffe0, 21 }, { 0x001fffe1, 21 }, { 0x003fffe0, 22 }, { 0x001fffe2, 21 }, { 0x007fffed, 23 }, { 0x003fffe1, 22 }, { 0x007fffee, 23 }, { 0x007fffef, 23 }, /* 176 - 183 */ + { 0x000fffea, 20 }, { 0x003fffe2, 22 }, { 0x003fffe3, 22 }, { 0x003fffe4, 22 }, { 0x007ffff0, 23 }, { 0x003fffe5, 22 }, { 0x003fffe6, 22 }, { 0x007ffff1, 23 }, /* 184 - 191 */ + { 0x03ffffe0, 26 }, { 0x03ffffe1, 26 }, { 0x000fffeb, 20 }, { 0x0007fff1, 19 }, { 0x003fffe7, 22 }, { 0x007ffff2, 23 }, { 0x003fffe8, 22 }, { 0x01ffffec, 25 }, /* 192 - 199 */ + { 0x03ffffe2, 26 }, { 0x03ffffe3, 26 }, { 0x03ffffe4, 26 }, { 0x07ffffde, 27 }, { 0x07ffffdf, 27 }, { 0x03ffffe5, 26 }, { 0x00fffff1, 24 }, { 0x01ffffed, 25 }, /* 200 - 207 */ + { 0x0007fff2, 19 }, { 0x001fffe3, 21 }, { 0x03ffffe6, 26 }, { 0x07ffffe0, 27 }, { 0x07ffffe1, 27 }, { 0x03ffffe7, 26 }, { 0x07ffffe2, 27 }, { 0x00fffff2, 24 }, /* 208 - 215 */ + { 0x001fffe4, 21 }, { 0x001fffe5, 21 }, { 0x03ffffe8, 26 }, { 0x03ffffe9, 26 }, { 0x0ffffffd, 28 }, { 0x07ffffe3, 27 }, { 0x07ffffe4, 27 }, { 0x07ffffe5, 27 }, /* 216 - 223 */ + { 0x000fffec, 20 }, { 0x00fffff3, 24 }, { 0x000fffed, 20 }, { 0x001fffe6, 21 }, { 0x003fffe9, 22 }, { 0x001fffe7, 21 }, { 0x001fffe8, 21 }, { 0x007ffff3, 23 }, /* 224 - 231 */ + { 0x003fffea, 22 }, { 0x003fffeb, 22 }, { 0x01ffffee, 25 }, { 0x01ffffef, 25 }, { 0x00fffff4, 24 }, { 0x00fffff5, 24 }, { 0x03ffffea, 26 }, { 0x007ffff4, 23 }, /* 232 - 239 */ + { 0x03ffffeb, 26 }, { 0x07ffffe6, 27 }, { 0x03ffffec, 26 }, { 0x03ffffed, 26 }, { 0x07ffffe7, 27 }, { 0x07ffffe8, 27 }, { 0x07ffffe9, 27 }, { 0x07ffffea, 27 }, /* 240 - 247 */ + { 0x07ffffeb, 27 }, { 0x0ffffffe, 28 }, { 0x07ffffec, 27 }, { 0x07ffffed, 27 }, { 0x07ffffee, 27 }, { 0x07ffffef, 27 }, { 0x07fffff0, 27 }, { 0x03ffffee, 26 } /* 248 - 255 */ +}; + +// EOS (End of String) specifier +static const HuffmanCodeT Hpack_EOS = { 0x3fffffff, 30 }; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _HpackDynamicTableGet + + \Description + Attempts to find a table entry in the static and dynamic tables + + \Input *pRef - module state + \Input iIndex - the index of the table entry we are looking for + + \Output + const TableEntryT * - the found entry or NULL + + \Version 10/05/2016 (eesponda) +*/ +/********************************************************************************F*/ +static const TableEntryT *_HpackDynamicTableGet(HpackRefT *pRef, int32_t iIndex) +{ + int32_t iCount; + const DynamicNodeT *pNode; + + // loop through the list either finding our index or hitting the end, if index is found return the entry + for (pNode = pRef->pHead, iCount = 0; pNode != NULL; pNode = pNode->pNext, iCount += 1) + { + if (iCount == iIndex) + { + break; + } + } + return(pNode != NULL ? &pNode->Entry : NULL); +} + +/*F********************************************************************************/ +/*! + \Function _HpackHuffmanTreeInsert + + \Description + Inserts a character into the huffman tree recursively + + \Input *pRef - module state + \Input **pNode - current node we are visiting + \Input uCode - huffman code + \Input uLen - number of bits in the code + \Input uChar - character corresponding to huffman code + + \Output + uint8_t - TRUE if successfully, FALSE otherwise + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _HpackHuffmanTreeInsert(HpackRefT *pRef, HuffmanNodeT **pNode, uint32_t uCode, uint32_t uLen, uint8_t uChar) +{ + HuffmanNodeT **pTargetNode = ((uCode >> (uLen-1)) & 1) ? &(*pNode)->pLeft : &(*pNode)->pRight; + if (*pTargetNode == NULL) + { + if ((*pTargetNode = (HuffmanNodeT *)DirtyMemAlloc(sizeof(**pTargetNode), HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) == NULL) + { + NetPrintf(("hpack: [%p] could not allocate node for huffman tree\n", pRef)); + return(FALSE); + } + ds_memclr(*pTargetNode, sizeof(**pTargetNode)); + } + + if ((uLen-1) > 0) + { + return(_HpackHuffmanTreeInsert(pRef, pTargetNode, uCode, uLen-1, uChar)); + } + else + { + (*pTargetNode)->uChar = uChar; + return(TRUE); + } +} + +/*F********************************************************************************/ +/*! + \Function _HpackHuffmanTreeBuild + + \Description + Builds the huffman tree based on the static huffman code table + + \Input *pRef - module state + + \Output + uint8_t - TRUE if successfully, FALSE otherwise + + \Version 10/19/2016 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _HpackHuffmanTreeBuild(HpackRefT *pRef) +{ + uint8_t uChar, bResult = TRUE; + + // allocate the root of the tree + if ((pRef->pHuffmanTree = (HuffmanNodeT *)DirtyMemAlloc(sizeof(*pRef->pHuffmanTree), HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) == NULL) + { + NetPrintf(("hpack: [%p] could not allocate root of huffman tree\n", pRef)); + return(FALSE); + } + ds_memclr(pRef->pHuffmanTree, sizeof(*pRef->pHuffmanTree)); + + /* go through each character one at a time 0-255 + building a path in the huffman tree based on the code for the character + at the end save the character in the leaf + + if any allocation fails this will be all cleaned up in the destroy that + happens if this function returns FALSE + */ + for (uChar = 0; (uChar < HPACK_HUFFMAN_TABLE_SIZE-1) && (bResult == TRUE); uChar += 1) + { + const HuffmanCodeT *pHuffman = &Hpack_aHuffmanCodes[uChar]; + bResult = _HpackHuffmanTreeInsert(pRef, &pRef->pHuffmanTree, pHuffman->uCode, pHuffman->uLen, uChar); + } + + return(bResult); +} + +/*F********************************************************************************/ +/*! + \Function _HpackHuffmanTreeDestroy + + \Description + Recursively clean up the huffman tree + + \Input *pRef - module state + \Input *pNode - currently visited node to cleanup + + \Version 10/19/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _HpackHuffmanTreeDestroy(HpackRefT *pRef, HuffmanNodeT *pNode) +{ + if (pNode == NULL) + { + return; + } + + _HpackHuffmanTreeDestroy(pRef, pNode->pLeft); + pNode->pLeft = NULL; + + _HpackHuffmanTreeDestroy(pRef, pNode->pRight); + pNode->pRight = NULL; + + DirtyMemFree(pNode, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function _HpackHuffmanTreeDecode + + \Description + Use the huffman tree to decode a series of octets + + \Input *pRef - module state + \Input *pData - input buffer with octets to decode + \Input iLen - size of the input buffer to decode + \Input *pBuffer - [out] output string that we write data to + \Input *pSuccess- [out] if the decoding was success + + \Version 10/19/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _HpackHuffmanTreeDecode(HpackRefT *pRef, const uint8_t *pData, int32_t iLen, char **pBuffer, uint8_t *pSuccess) +{ + int32_t iIndex, iWrite; + char strTemp[256]; + const HuffmanNodeT *pNode = pRef->pHuffmanTree; + + /* loop through each octet traversing the tree bit by bit + when a leaf node is found write that to our output */ + for (iIndex = 0, iWrite = 0; iIndex < iLen; iIndex += 1) + { + int32_t iBit; + for (iBit = 7; iBit >= 0; iBit -= 1) + { + pNode = ((pData[iIndex] >> iBit) & 1) ? pNode->pLeft : pNode->pRight; + if (pNode == NULL) + { + NetPrintf(("hpack: [%p] huffman code does not exist in tree\n", pRef)); + *pSuccess = FALSE; + return; + } + if ((pNode->pLeft != NULL) || (pNode->pRight != NULL)) + { + continue; + } + + // copy string but leave enough room for null terminator + if ((uint32_t)iWrite < sizeof(strTemp)-1) + { + strTemp[iWrite++] = (char)pNode->uChar; + } + pNode = pRef->pHuffmanTree; + } + } + + // write null terminator and return number of bytes written + strTemp[iWrite++] = '\0'; + + // allocate space for the output buffer + if ((*pBuffer = (char *)DirtyMemAlloc(iWrite, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL) + { + ds_strnzcpy(*pBuffer, strTemp, iWrite); + } + else + { + NetPrintf(("hpack: [%p] could not allocate space for decoding the string into\n", pRef)); + *pSuccess = FALSE; + } +} + +/*F********************************************************************************/ +/*! + \Function _HpackDecodeInteger + + \Description + Decodes an integer from the sequence of octets + + \Input *pBuf - where we decode the integer from + \Input iBufLen - size of the input buffer + \Input uMask - enable bits we use for decoding the integer + \Input *pValue - [out] where we write the decoded integer + + \Output + int32_t - amount of bytes read from the buffer + + \Version 10/07/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _HpackDecodeInteger(const uint8_t *pBuf, int32_t iBufLen, uint8_t uMask, uint32_t *pValue) +{ + int32_t iRead = 1; //!< we always at least read one byte + + if ((*pValue = *pBuf & uMask) == uMask) + { + uint8_t uMSB = 0, uByte; + + // read until you decode the full integer or reach the end of the buffer + do + { + uByte = pBuf[iRead++]; + *pValue += (uByte & 0x7f) * (1 << uMSB); + uMSB += 7; + } while (((uByte & 0x80) == 0x80) && (iRead < iBufLen)); + + // log in the case of reaching the end + if (iRead == iBufLen) + { + NetPrintf(("hpack: reached the end of the buffer before decoding full integer\n")); + } + } + return(iRead); +} + +/*F********************************************************************************/ +/*! + \Function _HpackDecodeString + + \Description + Decodes an string from the sequence of octets + + \Input *pRef - module state + \Input *pBuf - where we decode the string from + \Input iBufLen - size of the input buffer + \Input *pOutput - [out] where we write the decode string + \Input *pSuccess- [out] if the decode was successful + + \Output + int32_t - amount of bytes read from the buffer + + \Version 10/07/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _HpackDecodeString(HpackRefT *pRef, const uint8_t *pBuf, int32_t iBufLen, char **pOutput, uint8_t *pSuccess) +{ + int32_t iRead = 0; + uint8_t bHuffman; + uint32_t uLen; + + // initialize the output to NULL in cases of error + *pOutput = NULL; + + // decode if huffman encoding was used and the length of the string + bHuffman = (*pBuf & 0x80) == 0x80; + iRead += _HpackDecodeInteger(pBuf, iBufLen, 0x7f, &uLen); + + // make sure we have enough space in input + if ((uint32_t)iBufLen-iRead < uLen) + { + NetPrintf(("hpack: [%p] not enough space in input buffer to decode string\n", pRef)); + *pSuccess = FALSE; + return(iBufLen-iRead); + } + + // decode using the huffman tree or just copy the literal string + if (bHuffman == TRUE) + { + _HpackHuffmanTreeDecode(pRef, pBuf+iRead, uLen, pOutput, pSuccess); + } + else + { + // allocate the buffer for decoding + if ((*pOutput = (char *)DirtyMemAlloc(uLen+1, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL) + { + ds_strnzcpy(*pOutput, (const char *)pBuf+iRead, uLen+1); + } + else + { + NetPrintf(("hpack: [%p] could not allocate space for decoding the string into\n", pRef)); + *pSuccess = FALSE; + } + } + + return(iRead+uLen); +} + +/*F********************************************************************************/ +/*! + \Function _HpackDecodeIndexedField + + \Description + Decodes an indexed header field + + \Input *pRef - module state + \Input uIndex - index of the header field + \Input *pEntry - [out] header field data retrieved from the table + + \Output + uint8_t - TRUE if successfully pulled the indexed field from the table + + \Version 10/17/2016 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _HpackDecodeIndexedField(HpackRefT *pRef, uint32_t uIndex, TableEntryT *pEntry) +{ + const TableEntryT *pFound; + uint8_t bResult = TRUE; + + if (uIndex == 0) + { + NetPrintf(("hpack: [%p] received invalid index of 0 when decoding indexed header field\n", pRef)); + return(FALSE); + } + uIndex -= 1; // make it zero-indexed + + // try to find the entry in the static table index space + if (uIndex < HPACK_STATIC_TABLE_SIZE) + { + ds_memcpy(pEntry, &Hpack_aStaticTable[uIndex], sizeof(*pEntry)); + } + // try to find the entry in the dynamic table index space + else if ((pFound = _HpackDynamicTableGet(pRef, uIndex - HPACK_STATIC_TABLE_SIZE)) != NULL) + { + ds_memcpy(pEntry, pFound, sizeof(*pEntry)); + } + else + { + NetPrintf(("hpack: [%p] indexed header field could not be found at index %u\n", pRef, uIndex)); + bResult = FALSE; + } + return(bResult); +} +/*F********************************************************************************/ +/*! + \Function _HpackFindIndexedField + + \Description + Find an index given a header field + + \Input *pRef - module state + \Input *pEntry - header field we need the index for + \Input *pIndex - [out] index of the header field + + \Output + uint8_t - TRUE if successfully found the index for the header field + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _HpackFindIndexedField(HpackRefT *pRef, const StaticTableEntryT *pEntry, uint32_t *pIndex) +{ + uint32_t uIndex; + const TableEntryT *pFound; + + // try to find a valid index in the static table space + for (uIndex = 0; uIndex < HPACK_STATIC_TABLE_SIZE; uIndex += 1) + { + if (strncmp(pEntry->pName, Hpack_aStaticTable[uIndex].pName, strlen(pEntry->pName)) == 0 && + strncmp(pEntry->pValue, Hpack_aStaticTable[uIndex].pValue, strlen(pEntry->pValue)) == 0) + { + *pIndex = uIndex+1; + return(TRUE); + } + } + + // if not found in static, try the dynamic table space + uIndex = 0; + while ((pFound = _HpackDynamicTableGet(pRef, uIndex)) != NULL) + { + if (strncmp(pEntry->pName, pFound->pName, strlen(pEntry->pName)) == 0 && + strncmp(pEntry->pValue, pFound->pValue, strlen(pEntry->pValue)) == 0) + { + *pIndex = uIndex+HPACK_STATIC_TABLE_SIZE+1; + return(TRUE); + } + uIndex += 1; + } + + // otherwise it was not found + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _HpackFindIndexedFieldName + + \Description + Find an index given a header field name + + \Input *pRef - module state + \Input *pName - the header field name we are looking for + \Input *pIndex - [out] index of the header field + + \Output + uint8_t - TRUE if successfully found the index for the header field name + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _HpackFindIndexedFieldName(HpackRefT *pRef, const char *pName, uint32_t *pIndex) +{ + uint32_t uIndex; + const TableEntryT *pFound; + + // try to find a valid index in the static table space + for (uIndex = 0; uIndex < HPACK_STATIC_TABLE_SIZE; uIndex += 1) + { + if (strncmp(pName, Hpack_aStaticTable[uIndex].pName, strlen(Hpack_aStaticTable[uIndex].pName)) == 0) + { + *pIndex = uIndex+1; + return(TRUE); + } + } + + // if not found in static, try the dynamic table space + uIndex = 0; + while ((pFound = _HpackDynamicTableGet(pRef, uIndex)) != NULL) + { + if (strncmp(pName, pFound->pName, strlen(pFound->pName)) == 0) + { + *pIndex = uIndex+HPACK_STATIC_TABLE_SIZE+1; + return(TRUE); + } + uIndex += 1; + } + + // otherwise it was not found + return(FALSE); +} + + +/*F********************************************************************************/ +/*! + \Function _HpackDecodeLiteralField + + \Description + Decodes a literal header field + + \Input *pRef - module state + \Input *pBuf - bytes we are decoding the header field from + \Input iBufSize - size of the input buffer + \Input uIndex - index of the header field + \Input *pEntry - [out] header field data retrieved from the table + \Input *pSuccess- [out] if the decode was successful + + \Output + int32_t - number of bytes read from the buffer + + \Version 10/17/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _HpackDecodeLiteralField(HpackRefT *pRef, const uint8_t *pBuf, int32_t iBufSize, uint32_t uIndex, TableEntryT *pEntry, uint8_t *pSuccess) +{ + int32_t iRead = 0; + + // if the header name is indexed then pull it from the table + if (uIndex > 0) + { + char *pName; + int32_t iLen; + + // retrieve the entry, the value in pEntry will point to the static table entry + if (_HpackDecodeIndexedField(pRef, uIndex, pEntry) == FALSE) + { + *pSuccess = FALSE; + return(0); + } + + // allocate the space for the string as this will go into the dynamic table + iLen = (int32_t)strlen(pEntry->pName)+1; + if ((pName = (char *)DirtyMemAlloc(iLen, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL) + { + ds_strnzcpy(pName, pEntry->pName, iLen); + } + else + { + NetPrintf(("hpack: [%p] could not allocate space for the entry name\n", pRef)); + *pSuccess = FALSE; + } + pEntry->pName = pName; + } + // otherwise decode it from the buffer + else + { + iRead += _HpackDecodeString(pRef, pBuf+iRead, iBufSize-iRead, &pEntry->pName, pSuccess); + } + + // decode the value from the buffer + iRead += _HpackDecodeString(pRef, pBuf+iRead, iBufSize-iRead, &pEntry->pValue, pSuccess); + + // return how much data was read + return(iRead); +} + +/*F********************************************************************************/ +/*! + \Function _HpackDynamicTableEject + + \Description + Ejects header fields from the dynamic table until size hits a specified + value + + \Input *pRef - module state + \Input uSize - size we need the table to be under + + \Version 10/17/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _HpackDynamicTableEject(HpackRefT *pRef, uint32_t uSize) +{ + // if we are over the size, eject entries + while (pRef->uTableSize > uSize) + { + DynamicNodeT *pNode = pRef->pTail; + + // recalculate new size + pRef->uTableSize -= (uint32_t)strlen(pNode->Entry.pName) + (uint32_t)strlen(pNode->Entry.pValue) + HPACK_TABLE_ENTRY_OVERHEAD; + + // fix up pointers and delete memory + if (pRef->pTail != pRef->pHead) + { + pRef->pTail = pNode->pPrev; + pRef->pTail->pNext = NULL; + } + else + { + pRef->pTail = pRef->pHead = NULL; + } + DirtyMemFree(pNode->Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(pNode->Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(pNode, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + } +} + +/*F********************************************************************************/ +/*! + \Function _HpackDynamicTableResize + + \Description + Updates the maximum size of the dynamic table + + \Input *pRef - module state + \Input uSize - new maximum size of the table + + \Version 10/17/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _HpackDynamicTableResize(HpackRefT *pRef, uint32_t uSize) +{ + // eject entries + _HpackDynamicTableEject(pRef, uSize); + + // update size + pRef->uTableMax = uSize; + +#if HPACK_DYNAMICTABLE_LOGGING + NetPrintf(("hpack: [%p] new table size maximum %u\n", pRef, pRef->uTableMax)); +#endif +} + +/*F********************************************************************************/ +/*! + \Function _HpackDynamicTableInsert + + \Description + Inserts a new entry into the dynamic table, ejecting any necessary entries + + \Input *pRef - module state + \Input *pEntry - new entry we are adding to the table + + \Output + uint8_t - success=TRUE, failure=FALSE + + \Version 10/17/2016 (eesponda) +*/ +/********************************************************************************F*/ +static uint8_t _HpackDynamicTableInsert(HpackRefT *pRef, const TableEntryT *pEntry) +{ + DynamicNodeT *pNode; + uint32_t uEntrySize; + + // make sure entry is valid + if ((pEntry->pName == NULL) || (pEntry->pValue == NULL)) + { + return(FALSE); + } + // allocate memory for the node in the dynamic table + if ((pNode = (DynamicNodeT *)DirtyMemAlloc(sizeof(*pNode), HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) == NULL) + { + return(FALSE); + } + ds_memclr(pNode, sizeof(*pNode)); + ds_memcpy(&pNode->Entry, pEntry, sizeof(pNode->Entry)); + + uEntrySize = (uint32_t)strlen(pEntry->pName) + (uint32_t)strlen(pEntry->pValue) + HPACK_TABLE_ENTRY_OVERHEAD; + pRef->uTableSize += uEntrySize; + + // attach to list + if (pRef->pTail != NULL) + { + pNode->pNext = pRef->pHead; + pRef->pHead->pPrev = pNode; + } + else + { + pRef->pTail = pNode; + } + pRef->pHead = pNode; + + // if we are over the size, eject entries + _HpackDynamicTableEject(pRef, pRef->uTableMax); + + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function _HpackEncodeInteger + + \Description + Encodes an integer into a sequence of octets + + \Input uValue - integer we are encoding + \Input uMask - enable bits we use for encoding the integer + \Input *pBuf - [out] where we encode the integer into + \Input iBufLen - size of the output buffer + + \Output + int32_t - amount of bytes written to the buffer + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _HpackEncodeInteger(uint32_t uValue, uint8_t uMask, uint8_t *pBuf, int32_t iBufLen) +{ + int32_t iWrite = 1; + + if (uValue < uMask) + { + *pBuf |= (uint8_t)uValue; + } + else + { + *pBuf |= uMask; //!< turn on all the bits in the prefix + uValue -= uMask; //!< decrease the value by that amount + + for (iWrite = 1; (iWrite < iBufLen) && (uValue >= 0x80); iWrite += 1) + { + // set the lsb of the value and set the msb as a continuation flag + pBuf[iWrite] = (uValue % 0x80) + 0x80; + uValue /= 0x80; + } + pBuf[iWrite++] = uValue; + } + return(iWrite); +} + +/*F********************************************************************************/ +/*! + \Function _HpackHuffmanEncode + + \Description + Huffman encodes a character + + \Input *pEncode - encoding state + \Input *pHuffman - huffman code information + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _HpackHuffmanEncode(HuffmanEncodeT *pEncode, const HuffmanCodeT *pHuffman) +{ + int32_t iIndex; + const int32_t iBits = sizeof(*pEncode->aResult) * 8; + const int32_t iSize = sizeof(pEncode->aResult) / sizeof(*pEncode->aResult); + + // increase the count of bits and track the next octet boundary + pEncode->uCount += pHuffman->uLen; + if (pEncode->uCount > pEncode->uNextBoundary) + { + /* figure out how much we need to increment the boundary by + if the count is on an octet, just move the boundary there + if the count is over an octet, just move to the next octet past count (might be more than 1 octet) */ + const uint8_t uDifference = pEncode->uCount - pEncode->uNextBoundary; + if ((uDifference % 8) == 0) + { + pEncode->uNextBoundary = pEncode->uCount; + } + else + { + const uint8_t uNumOctets = (uDifference / 8) + 1; + pEncode->uNextBoundary += 8 * uNumOctets; + } + } + + // loop through the bytes backwards shifting by the left + for (iIndex = iSize-1; iIndex > 0; iIndex -= 1) + { + uint32_t uResult; + if ((uResult = (pEncode->aResult[iIndex] << pHuffman->uLen) | (pEncode->aResult[iIndex-1] >> (iBits-pHuffman->uLen))) != 0) + { + pEncode->aResult[iIndex] = uResult; + } + } + // finally encode using the huffman code + *pEncode->aResult = (*pEncode->aResult << pHuffman->uLen) | pHuffman->uCode; +} + +/*F********************************************************************************/ +/*! + \Function _HpackEncodeString + + \Description + Encodes a string into a sequence of octets + + \Input *pRef - module state + \Input *pBuf - where we encode the string from + \Input iBufSize - size of the input buffer + \Input *pOutput - [out] where we write the encoded string + \Input iOutSize - size of the output buffer + \Input bHuffman - use huffman encoding? + + \Output + int32_t - amount of bytes written into the buffer + + \Version 10/21/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _HpackEncodeString(HpackRefT *pRef, const char *pBuf, int32_t iBufSize, uint8_t *pOutput, int32_t iOutSize, uint8_t bHuffman) +{ + int32_t iWrite = 0; + + if (bHuffman == TRUE) + { + int32_t iIndex, iTotalBytes; + uint32_t uLeftover; + HuffmanCodeT Huffman; + HuffmanEncodeT Encode; + + ds_memclr(Encode.aResult, sizeof(Encode.aResult)); + Encode.uCount = 0; + Encode.uNextBoundary = 8; + + // encode each character one at a time + for (iIndex = 0; iIndex < iBufSize; iIndex += 1) + { + uint8_t uChar = pBuf[iIndex]; + _HpackHuffmanEncode(&Encode, &Hpack_aHuffmanCodes[uChar]); + } + + // finalize the huffman encoding using the EOS code + if ((uLeftover = Encode.uNextBoundary - Encode.uCount) > 0) + { + Huffman.uCode = Hpack_EOS.uCode >> (Hpack_EOS.uLen - uLeftover); + Huffman.uLen = uLeftover; + _HpackHuffmanEncode(&Encode, &Huffman); + } + + pOutput[iWrite] = 0x80; + iTotalBytes = Encode.uCount/8; + iWrite += _HpackEncodeInteger(iTotalBytes, 0x7f, pOutput, iOutSize); + + /* write the sequence to the output buffer + to ensure we write only as much as needed we calculate + how many bytes we need to write each iteration */ + while ((iTotalBytes > 0) && (iWrite < iOutSize)) + { + // figure out how many bytes we need to write + int32_t iRemainder = iTotalBytes % sizeof(*Encode.aResult); + // figure out what index into the result we need to read from + iIndex = (iTotalBytes-1) / sizeof(*Encode.aResult); + + // if on the boundary set the bytes to equal to the boundary + if (iRemainder == 0) + { + iRemainder = sizeof(*Encode.aResult); + } + // write the correct number of bytes to the output + for (; iRemainder > 0; iRemainder -= 1, iTotalBytes -= 1) + { + pOutput[iWrite++] = (uint8_t)(Encode.aResult[iIndex] >> ((iRemainder-1) * 8)); + } + } + } + else + { + iWrite += _HpackEncodeInteger(iBufSize, 0x7f, pOutput, iOutSize); + + ds_memcpy_s(pOutput+iWrite, iOutSize-iWrite, pBuf, iBufSize); + iWrite += iBufSize; + } + + return(iWrite); +} + + +#if HPACK_DYNAMICTABLE_LOGGING +/*F********************************************************************************/ +/*! + \Function _HpackPrintTable + + \Description + Prints the entries in the dynamic table + + \Input *pRef - module state + + \Version 11/04/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _HpackPrintTable(HpackRefT *pRef) +{ + const TableEntryT *pEntry; + uint32_t uIndex = 0; + + while ((pEntry = _HpackDynamicTableGet(pRef, uIndex)) != NULL) + { + uIndex += 1; + NetPrintf(("hpack: [%p] [%02u] (s = %u) %s: %s\n", pRef, uIndex, strlen(pEntry->pName) + strlen(pEntry->pValue) + HPACK_TABLE_ENTRY_OVERHEAD, pEntry->pName, pEntry->pValue)); + } + NetPrintf(("hpack: [%p] table size: %u\n", pRef, pRef->uTableSize)); +} +#endif + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function HpackCreate + + \Description + Allocate module state and prepare for use + + \Input uTableMax - maximum size of the dynamic table + \Input bDecoder - is the ref for a decoder? + + \Output + HpackRefT * - pointer to module state, or NULL + + \Notes + uTableMax is based upon SETTINGS_HEADER_TABLE_SIZE in the + HTTP/2 header frame + + \Version 10/05/2016 (eesponda) +*/ +/********************************************************************************F*/ +HpackRefT *HpackCreate(uint32_t uTableMax, uint8_t bDecoder) +{ + HpackRefT *pRef; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query memgroup data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate state + if ((pRef = (HpackRefT *)DirtyMemAlloc(sizeof(*pRef), HPACK_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("hpack: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pRef, sizeof(*pRef)); + pRef->uTableMax = uTableMax; + pRef->iMemGroup = iMemGroup; + pRef->pMemGroupUserData = pMemGroupUserData; + + // build huffman tree + if ((bDecoder == TRUE) && (_HpackHuffmanTreeBuild(pRef) == FALSE)) + { + HpackDestroy(pRef); + return(NULL); + } + + return(pRef); +} + +/*F********************************************************************************/ +/*! + \Function HpackDestroy + + \Description + Destroy the module and release its state + + \Input *pRef - module state + + \Version 10/05/2016 (eesponda) +*/ +/********************************************************************************F*/ +void HpackDestroy(HpackRefT *pRef) +{ + // clean up the tree recursively + if (pRef->pHuffmanTree != NULL) + { + _HpackHuffmanTreeDestroy(pRef, pRef->pHuffmanTree); + pRef->pHuffmanTree = NULL; + } + + // clear the dynamic table + HpackClear(pRef); + + DirtyMemFree(pRef, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function HpackDecode + + \Description + Decode the byte array into formatted header string + + \Input *pRef - module state + \Input *pInput - the bytes we are decoding + \Input iInpSize - number of bytes in the input + \Input *pOutput - [out] formatted header string CRLF delimited + \Input iOutSize - size of the string we are writing to + + \Output + int32_t - success=amount of data written to output, failure=negative + + \Version 10/17/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t HpackDecode(HpackRefT *pRef, const uint8_t *pInput, int32_t iInpSize, char *pOutput, int32_t iOutSize) +{ + int32_t iRead = 0, iWrite = 0; + uint32_t uIndex; + uint8_t bSuccess = TRUE; + + // clear the output + ds_memclr(pOutput, iOutSize); + + while ((iRead < iInpSize) && (bSuccess == TRUE)) + { + TableEntryT Entry; + uint32_t uSize; + uint8_t uByte = pInput[iRead], bWrite = TRUE, bFree = FALSE; + ds_memclr(&Entry, sizeof(Entry)); + + /* mask out the higher bits to figure out what type of header field it is + then pass a mask of the lower bits to the decode */ + + // indexed header field '1' 7-bit prefix + if ((uByte & HPACK_INDEXED_HEADER_FIELD) != 0) + { + iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_INDEXED_HEADER_FIELD-1, &uIndex); + bSuccess = _HpackDecodeIndexedField(pRef, uIndex, &Entry); + } + // literal header field with incremental indexing '01' 6-bit prefix + else if ((uByte & HPACK_LITERAL_HEADER_FIELD) != 0) + { + iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_LITERAL_HEADER_FIELD-1, &uIndex); + iRead += _HpackDecodeLiteralField(pRef, pInput+iRead, iInpSize-iRead, uIndex, &Entry, &bSuccess); + + // add entry to dynamic table + if (_HpackDynamicTableInsert(pRef, &Entry) == FALSE) + { + bFree = TRUE; /* free the memory */ + bSuccess = FALSE; /* set the failure flag */ + } + } + // dynamic table size update '001' 5-bit prefix + else if ((uByte & HPACK_DYNAMIC_TABLE_UPDATE) != 0) + { + bWrite = FALSE; /* disable writing headers */ + + iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_DYNAMIC_TABLE_UPDATE-1, &uSize); + _HpackDynamicTableResize(pRef, uSize); + } + // literal header field never indexed '0001' 4-bit prefix + else if ((uByte & HPACK_LITERAL_HEADER_FIELD_NEVER) != 0) + { + iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_LITERAL_HEADER_FIELD_NEVER-1, &uIndex); + iRead += _HpackDecodeLiteralField(pRef, pInput+iRead, iInpSize-iRead, uIndex, &Entry, &bSuccess); + bFree = TRUE; /* cleanup after it is written */ + } + // literal header field without indexing '0000' 4-bit prefix + else + { + iRead += _HpackDecodeInteger(pInput+iRead, iInpSize-iRead, HPACK_LITERAL_HEADER_FIELD_NEVER-1, &uIndex); + iRead += _HpackDecodeLiteralField(pRef, pInput+iRead, iInpSize-iRead, uIndex, &Entry, &bSuccess); + bFree = TRUE; /* cleanup after it is written */ + } + + // write the header field out if not dynamic table size update + if (bWrite == TRUE) + { + iWrite += ds_snzprintf(pOutput+iWrite, iOutSize-iWrite, "%s: %s\r\n", Entry.pName, Entry.pValue); + } + // if we need to cleanup entry memory do so + if (bFree == TRUE) + { + DirtyMemFree(Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + } + } + +#if HPACK_DYNAMICTABLE_LOGGING + _HpackPrintTable(pRef); +#endif + + return(bSuccess == TRUE ? iWrite : -1); +} + +/*F********************************************************************************/ +/*! + \Function HpackEncode + + \Description + Encode the formatted header string into a byte array + + \Input *pRef - module state + \Input *pInput - formatted header string CRLF delimited + \Input *pOutput - [out] byte array we are writing into + \Input iOutSize - size of the byte array we are writing to + \Input bHuffman - do we need to huffman encode the strings? + + \Output + int32_t - success=amount of data written, failure=negative + + \Version 10/17/2016 (eesponda) +*/ +/********************************************************************************F*/ +int32_t HpackEncode(HpackRefT *pRef, const char *pInput, uint8_t *pOutput, int32_t iOutSize, uint8_t bHuffman) +{ + char strName[256], strValue[4*1024]; + StaticTableEntryT TempEntry; + int32_t iWrite = 0; + + // clear the output + ds_memclr(pOutput, iOutSize); + + // if there is a pending dynamic table update, encode that first + if (pRef->bUpdatePending == TRUE) + { + pOutput[iWrite] = HPACK_DYNAMIC_TABLE_UPDATE; + iWrite += _HpackEncodeInteger(pRef->uTableMax, HPACK_DYNAMIC_TABLE_UPDATE-1, pOutput+iWrite, iOutSize-iWrite); + + pRef->bUpdatePending = FALSE; + } + + // point to the address of the stack variables + TempEntry.pName = strName; + TempEntry.pValue = strValue; + + // for each header entry try to encode + while (ProtoHttpGetNextHeader(NULL, pInput, strName, sizeof(strName), strValue, sizeof(strValue), &pInput) == 0) + { + uint32_t uIndex; + TableEntryT Entry; + + /* force the strName to lowercase */ + for (uIndex = 0; (strName[uIndex] != '\0') && (uIndex < sizeof(strName)); uIndex += 1) + { + if (isupper(strName[uIndex])) + { + strName[uIndex] = tolower(strName[uIndex]); + } + } + + /* attempt to find the header entry in the static/dynamic table + if found, then encode it as an indexed header field */ + if (_HpackFindIndexedField(pRef, &TempEntry, &uIndex) == TRUE) + { + // write indexed header field '1' followed by index using 7-bit prefix + pOutput[iWrite] = HPACK_INDEXED_HEADER_FIELD; + iWrite += _HpackEncodeInteger(uIndex, HPACK_INDEXED_HEADER_FIELD-1, pOutput+iWrite, iOutSize-iWrite); + } + // otherwise encode it as literal header field with indexing + else + { + int32_t iLen; + + /* write literal header field with incremental indexing '01' followed by index (if indexable name) or encoded string (if not) + and encoded string (for value) */ + pOutput[iWrite] = HPACK_LITERAL_HEADER_FIELD; + if (_HpackFindIndexedFieldName(pRef, TempEntry.pName, &uIndex) == TRUE) + { + iWrite += _HpackEncodeInteger(uIndex, HPACK_LITERAL_HEADER_FIELD-1, pOutput+iWrite, iOutSize-iWrite); + } + else + { + iWrite += 1; + iWrite += _HpackEncodeString(pRef, TempEntry.pName, (int32_t)strlen(TempEntry.pName), pOutput+iWrite, iOutSize-iWrite, bHuffman); + } + iWrite += _HpackEncodeString(pRef, TempEntry.pValue, (int32_t)strlen(TempEntry.pValue), pOutput+iWrite, iOutSize-iWrite, bHuffman); + + // allocate space for the entry name in the dynamic table + iLen = (int32_t)strlen(TempEntry.pName)+1; + if ((Entry.pName = (char *)DirtyMemAlloc(iLen, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL) + { + ds_strnzcpy(Entry.pName, TempEntry.pName, iLen); + } + else + { + NetPrintf(("hpack: [%p] could not allocate space for entry name for encoding dynamic table\n", pRef)); + } + // allocate space for the entry value in the dynamic table + iLen = (int32_t)strlen(TempEntry.pValue)+1; + if ((Entry.pValue = (char *)DirtyMemAlloc(iLen, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData)) != NULL) + { + ds_strnzcpy(Entry.pValue, TempEntry.pValue, iLen); + } + else + { + NetPrintf(("hpack: [%p] could not allocate space for entry value for encoding dynamic table\n", pRef)); + } + + // insert the new entry into the dynamic table + if (_HpackDynamicTableInsert(pRef, &Entry) == FALSE) + { + DirtyMemFree(Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + return(-1); + } + } + } + +#if HPACK_DYNAMICTABLE_LOGGING + _HpackPrintTable(pRef); +#endif + + return(iWrite); +} + +/*F********************************************************************************/ +/*! + \Function HpackClear + + \Description + Clears the dynamic table + + \Input *pRef - module state + + \Version 10/31/2016 (eesponda) +*/ +/********************************************************************************F*/ +void HpackClear(HpackRefT *pRef) +{ + DynamicNodeT *pNode; + + //! walk the list and clean up the memory + while ((pNode = pRef->pHead) != NULL) + { + pRef->pHead = pNode->pNext; + + DirtyMemFree(pNode->Entry.pName, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(pNode->Entry.pValue, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + DirtyMemFree(pNode, HPACK_MEMID, pRef->iMemGroup, pRef->pMemGroupUserData); + } + pRef->pTail = NULL; + pRef->uTableSize = 0; +} + +/*F********************************************************************************/ +/*! + \Function HpackResize + + \Description + Resizes the dynamic table + + \Input *pRef - module state + \Input uTableSize - new maximum table size + \Input bSendUpdate - do we need to notify peer of dynamic table size change + + \Version 11/09/2016 (eesponda) +*/ +/********************************************************************************F*/ +void HpackResize(HpackRefT *pRef, uint32_t uTableSize, uint8_t bSendUpdate) +{ + _HpackDynamicTableResize(pRef, uTableSize); + pRef->bUpdatePending = bSendUpdate; +} diff --git a/r5dev/thirdparty/dirtysdk/source/util/jsonformat.c b/r5dev/thirdparty/dirtysdk/source/util/jsonformat.c new file mode 100644 index 00000000..eb595379 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/jsonformat.c @@ -0,0 +1,1377 @@ +/*H*************************************************************************************/ +/*! + \File jsonformat.c + + \Description + This module formats simple JSON, in a linear fashion, using a character buffer + that the client provides. + + \Copyright + Copyright (c) Electronic Arts 2012. + + \Notes + \verbatim + When JsonInit() is called, a 24-bytes header is added at the beginning of the + client-provided buffer. The header is an ascii string that looks like this: + + where: + AAAAAAAA is the offset field + BBBBBBBB is the buffer size field + CC is the indent field + DD is the flag field + Those first 24 bytes are replaced by whitespaces when JsonFinish() is called. + + Required buffer capacity for json = ??? length(JSON) + 1. + That means if length(buffer) <= length(JSON), buffer-overrun occurs. + \endverbatim + + \Version 12/11/2012 (jbrookes) First Version, based on XmlFormat +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/util/jsonformat.h" + +/*** Defines ***************************************************************************/ + +#define JSON_HEADER_LEN (22) +#define JSON_MAX_TAG_LENGTH (128) +#define JSON_MAX_FLOAT_LENGTH (128) +#define JSON_MAX_DATE_LENGTH (32) + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ + +static const uint8_t _Json_Hex2AsciiTable[16] = "0123456789abcdef"; + +// ascii->hex conversion table for 7bit ASCII characters +static const uint8_t _Json_Ascii2HexTable[128] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/*! 7bit ASCII characters that should be encoded ([0..31], ", ?, \) + characters that are represented with a 1 are escaped in long form + (u00xy) whereas characters represented with a 2 are escaped with + a single backslash */ +static const uint8_t _Json_EncodeStringTable[128] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, // 00-0F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10-1F + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-2F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, // 30-3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-6F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, // 70-7F +}; + + +static int32_t _iLastIndent; //$$TODO - clean this up (put it in table?) + + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function _JsonGet8 + + \Description + Get 8bit value from given offset in buffer + + \Input *pJson - pointer to start of buffer + \Input iOffset - offset within buffer to get value from + + \Output + int32_t - value + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonGet8(const char *pJson, int32_t iOffset) +{ + return((_Json_Ascii2HexTable[(int32_t)pJson[iOffset+0]] << 4) | + _Json_Ascii2HexTable[(int32_t)pJson[iOffset+1]]); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonSet8 + + \Description + Save 8bit value in buffer + + \Input *pJson - pointer to start of buffer + \Input iOffset - offset to save at + \Input iValue - value to save + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _JsonSet8(char *pJson, int32_t iOffset, int32_t iValue) +{ + pJson[iOffset+0] = _Json_Hex2AsciiTable[(iValue >> 4) & 15]; + pJson[iOffset+1] = _Json_Hex2AsciiTable[(iValue >> 0) & 15]; +} + +/*F*************************************************************************************/ +/*! + \Function _JsonGet32 + + \Description + Get 32bit value from given offset in buffer + + \Input *pJson - pointer to start of buffer + \Input iOffset - offset within buffer to get value from + + \Output + int32_t - value + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonGet32(char *pJson, int32_t iOffset) +{ + return((_Json_Ascii2HexTable[(int32_t)pJson[iOffset+0]] << 28) | + (_Json_Ascii2HexTable[(int32_t)pJson[iOffset+1]] << 24) | + (_Json_Ascii2HexTable[(int32_t)pJson[iOffset+2]] << 20) | + (_Json_Ascii2HexTable[(int32_t)pJson[iOffset+3]] << 16) | + (_Json_Ascii2HexTable[(int32_t)pJson[iOffset+4]] << 12) | + (_Json_Ascii2HexTable[(int32_t)pJson[iOffset+5]] << 8) | + (_Json_Ascii2HexTable[(int32_t)pJson[iOffset+6]] << 4) | + _Json_Ascii2HexTable[(int32_t)pJson[iOffset+7]]); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonSet32 + + \Description + Save 32-bit value at given offset in buffer + + \Input *pJson - pointer to start of buffer + \Input iOffset - offset into buffer to save + \Input iValue - value to save into buffer + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _JsonSet32(char *pJson, int32_t iOffset, int32_t iValue) +{ + pJson[iOffset+0] = _Json_Hex2AsciiTable[(iValue>>28)&15]; + pJson[iOffset+1] = _Json_Hex2AsciiTable[(iValue>>24)&15]; + pJson[iOffset+2] = _Json_Hex2AsciiTable[(iValue>>20)&15]; + pJson[iOffset+3] = _Json_Hex2AsciiTable[(iValue>>16)&15]; + pJson[iOffset+4] = _Json_Hex2AsciiTable[(iValue>>12)&15]; + pJson[iOffset+5] = _Json_Hex2AsciiTable[(iValue>>8)&15]; + pJson[iOffset+6] = _Json_Hex2AsciiTable[(iValue>>4)&15]; + pJson[iOffset+7] = _Json_Hex2AsciiTable[(iValue>>0)&15]; +} + +/*F*************************************************************************************/ +/*! + \Function _JsonGetOffset + + \Description + Get offset from buffer + + \Input *pJson - pointer to start of buffer + + \Output + int32_t - offset + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonGetOffset(char *pJson) +{ + return(_JsonGet32(pJson, 1)); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonGetLength + + \Description + Get length from buffer + + \Input *pJson - pointer to start of buffer + + \Output + int32_t - length + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonGetLength(char *pJson) +{ + return(_JsonGet32(pJson, 9)); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonGetIndent + + \Description + Get indent level from buffer + + \Input *pJson - pointer to start of buffer + + \Output + int32_t - indent + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonGetIndent(char *pJson) +{ + return(_JsonGet8(pJson, 17)); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonGetFlags + + \Description + Get flags from buffer + + \Input *pJson - pointer to start of buffer + + \Output + int32_t - flags + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonGetFlags(const char *pJson) +{ + return(_JsonGet8(pJson, 19)); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonSetOffset + + \Description + Save offset in buffer + + \Input *pJson - pointer to start of buffer + \Input iOffset - offset to save in buffer + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _JsonSetOffset(char *pJson, int32_t iOffset) +{ + _JsonSet32(pJson, 1, iOffset); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonSetLength + + \Description + Save length in buffer + + \Input *pJson - pointer to start of buffer + \Input iLength - length to save in buffer + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _JsonSetLength(char *pJson, int32_t iLength) +{ + _JsonSet32(pJson, 9, iLength); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonSetIndent + + \Description + Save indent level in buffer + + \Input *pJson - pointer to start of buffer + \Input iIndent - indent level to save + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _JsonSetIndent(char *pJson, int32_t iIndent) +{ + _JsonSet8(pJson, 17, iIndent); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonSetFlags + + \Description + Save flags in buffer + + \Input *pJson - pointer to start of buffer + \Input iFlags - flags + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _JsonSetFlags(char *pJson, int32_t iFlags) +{ + _JsonSet8(pJson, 19, iFlags); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonValidHeader + + \Description + Check whether buffer is initialized with a valid header. + + \Input *pBuffer - buffer to which JSON will be written + + \Output + int32_t - returns 1 if there is a valid header, 0 otherwise + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonValidHeader(const char *pBuffer) +{ + return((pBuffer[0] != '{') ? 0 : 1); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonOpenTag + + \Description + Check whether there is an open tag in the buffer. + + \Input *pBuffer - buffer to which JSON will be written + \Input iOffset - current offset within the buffer + + \Output + int32_t - returns 1 if an open tag exists in the buffer, 0 otherwise + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonOpenTag(const char *pBuffer, int32_t iOffset) +{ + int32_t iBackPos; + int32_t iBeginCount = 0; + int32_t iEndCount = 0; + + for (iBackPos = (iOffset-1); iBackPos >= JSON_HEADER_LEN; iBackPos--) + { + // Count all closing tags (ended tag with no children will be counted as starting and closing tag) + if (pBuffer[iBackPos] == '}') + { + iEndCount++; + iBackPos--; + } + // Count all starting tags + else if (pBuffer[iBackPos] == '{') + { + iBeginCount++; + // If there are more open tags than close tags, return true + if (iBeginCount > iEndCount) + { + return(1); + } + } + } + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonUpdateHeader + + \Description + Update the JSON header to reflect the number of characters written. If the buffer + was full, this function will update the header to indicate the full buffer and will + return the JSON_ERR_FULL error code. + + \Input *pBuffer - buffer to which json will be written + \Input iNumChars - number of characters written, -1 for buffer filled + \Input iLength - length of the buffer + \Input iOffset - current offset within the buffer + \Input iIndent - current indent level + + \Output + int32_t - returns JSON_ERR_FULL if buffer was filled, JSON_ERR_NONE otherwise + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonUpdateHeader(char *pBuffer, int32_t iNumChars, int32_t iLength, int32_t iOffset, int32_t iIndent) +{ + iOffset += iNumChars; + + // leave room for null character + iLength -= 1; + + if ((iNumChars < 0) || (iOffset > iLength)) + { + pBuffer[iLength] = '\0'; + iOffset = iLength; + _JsonSetOffset(pBuffer, iOffset); + return(JSON_ERR_FULL); + } + else + { + _iLastIndent = _JsonGetIndent(pBuffer); + _JsonSetOffset(pBuffer, iOffset); + _JsonSetIndent(pBuffer, iIndent); + return(JSON_ERR_NONE); + } +} + +/*F****************************************************************************/ +/*! + \Function _JsonIndent + + \Description + Add the appropriate level of whitespace to indent the current tag if + the whitespace flag is enabled for the buffer. + + \Input *pBuffer - buffer to which JSON will be written + \Input iLength - length of the buffer + \Input iOffset - current offset within the buffer + \Input iIndent - current indent level + \Input iFlags - flags + + \Output + int32_t - negative=buffer overrun, zero/positive=number of characters written + + \Version 12/11/2012 (jbrookes) +*/ +/****************************************************************************F*/ +static int32_t _JsonIndent(char *pBuffer, int32_t iLength, int32_t iOffset, int32_t iIndent, int32_t iFlags) +{ + int32_t iNumChars = -1; + + #if DIRTYCODE_LOGGING + if (iIndent < 0) + { + NetPrintf(("jsonformat: warning -- invalid indent level (%d) in _JsonIndent\n", iIndent)); + } + #endif + + if ((iFlags & JSON_FL_WHITESPACE) == 0) + { + iNumChars = 0; + } + else if (iIndent <= 0) + { + if ((iLength - iOffset) > 1) + { + iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "\n"); + } + } + else + { + if ((iLength - iOffset) > ((iIndent * 2) + 1)) + { + iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "\n%*c", iIndent * 2, ' '); + } + } + + return(iNumChars); +} + +#if 0 //UNUSED? +/*F*************************************************************************************/ +/*! + \Function _JsonFormatInsert + + \Description + Insert a preformatted item + + \Input *pBuffer - the JSON buffer + \Input *pValue - raw string to insert (must be correctly preformatted) + + \Output + int32_t - return code. + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonFormatInsert(char *pBuffer, const char *pValue) +{ + int32_t iWidth, iCount; + int32_t iOffset, iLength, iIndent, iFlags; + + // make sure there is a header and extract fields + if (!_JsonValidHeader(pBuffer)) + return(JSON_ERR_UNINIT); + + iOffset = _JsonGetOffset(pBuffer); + iLength = _JsonGetLength(pBuffer); + iIndent = _JsonGetIndent(pBuffer); + iFlags = _JsonGetFlags(pBuffer); + + // there must be an open tag in the buffer + if (!_JsonOpenTag(pBuffer, iOffset)) + { + return(JSON_ERR_NOT_OPEN); + } + + // check if there's enough room for the insertion to complete successfully + iWidth = (int32_t)strlen(pValue); + // figure out indent size + iWidth += ((pValue[0] == '{') && (iFlags & JSON_FL_WHITESPACE)) ? (1 + iIndent * 2) : 0; + // we must be able to insert completely + if ((iLength - iOffset) <= iWidth) + { + return(JSON_ERR_FULL); + } + + // buffer is good, now start to determine the insertion type + // 1. value, handle indent if necessary + if (pValue[0] == '{') + { + if (iFlags & JSON_FL_WHITESPACE) + { + pBuffer[iOffset++] = '\n'; + for (iCount = 0; iCount < iIndent; ++iCount) + { + pBuffer[iOffset++] = ' '; + pBuffer[iOffset++] = ' '; + } + } + } + // 2. attr="value"> + else if (strchr(pValue, '"') != NULL) + { + // the tag must be open for accepting attributes (no children and no element text set) + if (!_JsonOpenForAttr(pBuffer, iOffset)) + { + return(JSON_ERR_ATTR_POSITION); + } + // replace the close tag '>' to ' ' for input (pValue[] must look like: attr="value">) + pBuffer[iOffset - 1] = ' '; + } + + // for all insertion types, insert the string + for (iCount = 0; pValue[iCount] != 0; ++iCount) + { + pBuffer[iOffset++] = pValue[iCount]; + } + pBuffer[iOffset] = 0; + + // update offset and return + _JsonSetOffset(pBuffer, iOffset); + return(JSON_ERR_NONE); +} +#endif + +/*F*************************************************************************************/ +/*! + \Function _JsonEncodeString + + \Description + Encode a string as needed for reserved characters + + \Input *pBuffer - the JSON buffer + \Input iLength - number of eight-bit characters available in buffer + \Input *_pSrc - source string to add to buffer + + \Output + int32_t - negative=not enough buffer, zero/positive=encoded length. + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonEncodeString(char *pBuffer, int32_t iLength, const char *_pSrc) +{ + char * const pBackup = pBuffer; + const uint8_t *pSource; + int32_t iRemain, iEncodeType; + + if (_pSrc == NULL) + { + _pSrc = "null"; + } + pSource = (const uint8_t *)_pSrc; + + // encode the string + for (iRemain = iLength; (iRemain > 1) && (*pSource != '\0'); ++pSource) + { + if ((*pSource < 128) && ((iEncodeType = _Json_EncodeStringTable[*pSource]) != 0)) + { + if ((iEncodeType == 2) && (iRemain > 6)) + { + // hex encode all out of range values + *pBuffer++ = '\\'; + *pBuffer++ = 'u'; + *pBuffer++ = '0'; + *pBuffer++ = '0'; + *pBuffer++ = _Json_Hex2AsciiTable[(*pSource)>>4]; + *pBuffer++ = _Json_Hex2AsciiTable[(*pSource)&0xF]; + iRemain -= 6; + } + else if ((iEncodeType == 1) && (iRemain > 1)) + { + *pBuffer++ = '\\'; + *pBuffer++ = *pSource; + } + else // buffer overrun + { + break; + } + } + else + { + // normal encoding + *pBuffer++ = *pSource; + --iRemain; + } + } + + // make sure space for terminator + if (iRemain > 0) + { + *pBuffer = 0; + } + + // if character(s) remains, return negative to indicate buffer-overrun. + if (*pSource != '\0') + { + // if buffer has been changed, reset the change(s) + if (pBackup != pBuffer) + { + *pBackup = '\0'; + } + return(-1); + } + + // return encoded size + return(iLength-iRemain); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonAddStr + + \Description + Adds a string element + + \Input *pBuffer - JSON buffer + \Input *pElemName - name of the element (may be null) + \Input *pValue - value of the element + \Input bQuote - TRUE if value should be quoted + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonAddStr(char *pBuffer, const char *pElemName, const char *pValue, uint8_t bQuote) +{ + int32_t iOffset, iLength, iNumChars, iIndent, iFlags, iTempLength; + + if (!_JsonValidHeader(pBuffer)) + { + return(JSON_ERR_UNINIT); + } + + iOffset = _JsonGetOffset(pBuffer); + iLength = _JsonGetLength(pBuffer); + iIndent = _JsonGetIndent(pBuffer); + iFlags = _JsonGetFlags(pBuffer); + + // there must be an open tag in the buffer + if (_JsonOpenTag(pBuffer, iOffset)) + { + iNumChars = 0; + if (iIndent <= _iLastIndent) + { + if ((iTempLength = ds_snzprintf(pBuffer+iOffset, iLength-iOffset, ",")) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(JSON_ERR_FULL); + } + iNumChars = iTempLength; + } + + if ((iTempLength = _JsonIndent(pBuffer+iNumChars, iLength-iNumChars, iOffset, iIndent, iFlags)) < 0) + { + return(JSON_ERR_FULL); + } + iNumChars += iTempLength; + + // add element name + if (pElemName != NULL) + { + if ((iTempLength = ds_snzprintf(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, "\"%s\":", pElemName)) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(JSON_ERR_FULL); + } + iNumChars += iTempLength; + } + + // add leading quote + if (bQuote) + { + if ((iTempLength = ds_snzprintf(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, "\"")) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(JSON_ERR_FULL); + } + iNumChars += iTempLength; + } + + // add element value + if ((iTempLength = _JsonEncodeString(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, pValue)) < 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(JSON_ERR_FULL); + } + iNumChars += iTempLength; + + // add trailing quote + if (bQuote) + { + if ((iTempLength = ds_snzprintf(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, "\"")) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(JSON_ERR_FULL); + } + iNumChars += iTempLength; + } + + // all done successfully + return(_JsonUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); + } + else + { + return(JSON_ERR_NOT_OPEN); + } +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function JsonInit + + \Description + Initialize the Json Buffer. This MUST be called prior to any other calls. + Client can allocate the buffer on stack, but should not access it directly. + (see JsonData()). + + \Input *pBuffer - buffer to which JSON will be written. + \Input iBufLen size of buffer passed in. + \Input uFlags - encoding flags (JSONFORMAT_FL_xxx) + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +void JsonInit(char *pBuffer, int32_t iBufLen, unsigned char uFlags) +{ + int32_t iOffset; + + ds_memclr(pBuffer, iBufLen); + if (iBufLen > JSON_HEADER_LEN) + { + iOffset = ds_snzprintf(pBuffer, iBufLen - 1, "{00000000000000000000}{"); + _JsonSetOffset(pBuffer, iOffset); + _JsonSetLength(pBuffer, iBufLen); + _JsonSetFlags(pBuffer, uFlags); + _JsonSetIndent(pBuffer, 1); + _iLastIndent = 0; + } +} + +/*F*************************************************************************************/ +/*! + \Function JsonBufSizeIncrease + + \Description + Notify the jsonformat API that the buffer size was increased. + + \Input *pBuffer - buffer to which JSON is being written. + \Input iNewBufLen new size for that buffer + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +void JsonBufSizeIncrease(char *pBuffer, int32_t iNewBufLen) +{ + _JsonSetLength(pBuffer, iNewBufLen); +} + +/*F*************************************************************************************/ +/*! + \Function JsonFinish + + \Description + Signal completion of output to this buffer. Clear the JSON API hidden + data. + + \Input *pBuffer buffer to which JSON was written. + + \Output char* - pointer to real JSON data (skipping past header) + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +char *JsonFinish(char *pBuffer) +{ + if (_JsonValidHeader(pBuffer)) + { + int32_t iOffset = _JsonGetOffset(pBuffer); + int32_t iBufLen = _JsonGetLength(pBuffer); + int32_t iFlags = _JsonGetFlags(pBuffer); + + // add final close tag + if (iFlags == JSON_FL_WHITESPACE) + { + ds_snzprintf(pBuffer + iOffset, iBufLen - iOffset, "\n}"); + } + else + { + ds_snzprintf(pBuffer + iOffset, iBufLen - iOffset, "}"); + } + + // remove header + ds_memset(pBuffer, ' ', JSON_HEADER_LEN); + pBuffer += JSON_HEADER_LEN; + } + return(pBuffer); +} + +/*F*************************************************************************************/ +/*! + \Function JsonObjectStart + + \Description + Start a JSON object, to which you can add other objects or elements. Must be + ended with JsonObjectEnd. + + \Input *pBuffer - JSON buffer + \Input *pName - name of the object (may be null) + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonObjectStart(char *pBuffer, const char *pName) +{ + int32_t iOffset, iLength, iNumChars, iIndent, iFlags; + + if (!_JsonValidHeader(pBuffer)) + { + return(JSON_ERR_UNINIT); + } + + iOffset = _JsonGetOffset(pBuffer); + iLength = _JsonGetLength(pBuffer); + iIndent = _JsonGetIndent(pBuffer); + iFlags = _JsonGetFlags(pBuffer); + + if (iIndent <= _iLastIndent) + { + if ((iNumChars = ds_snzprintf(pBuffer+iOffset, iLength-iOffset, ",")) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + } + + if ((iNumChars = _JsonIndent(pBuffer, iLength, iOffset, iIndent, iFlags)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + + if (pName != NULL) + { + if ((iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "\"%s\":", pName)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + } + + if ((iNumChars = _JsonIndent(pBuffer, iLength, iOffset, iIndent++, iFlags)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + + if ((iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "{")) < 0) + { + return(JSON_ERR_FULL); + } + return(_JsonUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonObjectEnd + + \Description + End the current object -- must have an outstanding open tag. + + \Input *pBuffer - JSON buffer. + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonObjectEnd(char *pBuffer) +{ + int32_t iOffset, iLength, iNumChars; + int32_t iIndent, iFlags; + + if (!_JsonValidHeader(pBuffer)) + { + return(JSON_ERR_UNINIT); + } + + iOffset = _JsonGetOffset(pBuffer); + iLength = _JsonGetLength(pBuffer); + iFlags = _JsonGetFlags(pBuffer); + iIndent = _JsonGetIndent(pBuffer) - 1; + + if ((iNumChars = _JsonIndent(pBuffer, iLength, iOffset, iIndent, iFlags)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + + iNumChars = ds_snzprintf(pBuffer+iOffset, iLength-iOffset, "}"); + return(_JsonUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonArrayStart + + \Description + Start a JSON array, to which you can add other objects or elements. Must be + ended with JsonArrayEnd. + + \Input *pBuffer - JSON buffer + \Input *pName - name of the array (may be null) + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonArrayStart(char *pBuffer, const char *pName) +{ + int32_t iOffset, iLength, iNumChars, iIndent, iFlags; + + if (!_JsonValidHeader(pBuffer)) + { + return(JSON_ERR_UNINIT); + } + + iOffset = _JsonGetOffset(pBuffer); + iLength = _JsonGetLength(pBuffer); + iIndent = _JsonGetIndent(pBuffer); + iFlags = _JsonGetFlags(pBuffer); + + iNumChars = 0; + if (iIndent <= _iLastIndent) + { + int32_t iTempLength; + if ((iTempLength = ds_snzprintf(pBuffer+iOffset, iLength-iOffset, ",")) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(JSON_ERR_FULL); + } + iNumChars = iTempLength; + } + iOffset += iNumChars; + if ((iNumChars = _JsonIndent(pBuffer, iLength, iOffset, iIndent, iFlags)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + + if (pName != NULL) + { + if ((iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "\"%s\":", pName)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + } + + if ((iNumChars = _JsonIndent(pBuffer, iLength, iOffset, iIndent++, iFlags)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + + if ((iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "[")) < 0) + { + return(JSON_ERR_FULL); + } + return(_JsonUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonArrayEnd + + \Description + End the current array -- must have an outstanding open array + + \Input *pBuffer - JSON buffer + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonArrayEnd(char *pBuffer) +{ + int32_t iOffset, iLength, iNumChars; + int32_t iIndent, iFlags; + + if (!_JsonValidHeader(pBuffer)) + { + return(JSON_ERR_UNINIT); + } + + iOffset = _JsonGetOffset(pBuffer); + iLength = _JsonGetLength(pBuffer); + iFlags = _JsonGetFlags(pBuffer); + iIndent = _JsonGetIndent(pBuffer) - 1; + + if ((iNumChars = _JsonIndent(pBuffer, iLength, iOffset, iIndent, iFlags)) < 0) + { + return(JSON_ERR_FULL); + } + iOffset += iNumChars; + + iNumChars = ds_snzprintf(pBuffer+iOffset, iLength-iOffset, "]"); + return(_JsonUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonAddStr + + \Description + Adds a string element + + \Input *pBuffer - JSON buffer + \Input *pElemName - name of the element (may be null) + \Input *pValue - value of the element + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonAddStr(char *pBuffer, const char *pElemName, const char *pValue) +{ + return(_JsonAddStr(pBuffer, pElemName, pValue, TRUE)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonAddInt + + \Description + Add an integer element + + \Input *pBuffer - JSON buffer + \Input *pElemName - name of the element + \Input iValue - integer value of the element + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonAddInt(char *pBuffer, const char *pElemName, int64_t iValue) +{ + char strInt[64]; + ds_snzprintf(strInt, sizeof(strInt), "%lld", iValue); + return(_JsonAddStr(pBuffer, pElemName, strInt, FALSE)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonAddNum + + \Description + Add a complete, contained decimal element. Builds start and end tag. + Nothing can be appended to this tag. Format it using formatSpec. + + \Input *pBuffer - JSON buffer + \Input *pElemName - name of the element + \Input *pFormatSpec - format spec for formatting the float (see printf) + \Input fValue - float value of the element + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonAddNum(char *pBuffer, const char *pElemName, const char *pFormatSpec, float fValue) +{ + char strFloat[JSON_MAX_FLOAT_LENGTH]; + + if (ds_snzprintf(strFloat, sizeof(strFloat), pFormatSpec, fValue) <= 0) + { + return(JSON_ERR_INVALID_PARAM); + } + return(_JsonAddStr(pBuffer, pElemName, strFloat, FALSE)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonAddDate + + \Description + Add a date (will be encoded as a string; use JsonAddInteger for integer encoding) + + \Input *pBuffer - JSON buffer + \Input *pElemName - name of the element + \Input uEpochDate - date/time since epoch + + \Output + int32_t - JSON_ERR_* + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonAddDate(char *pBuffer, const char *pElemName, uint32_t uEpochDate) +{ + struct tm *pTime, Time; + char strDate[JSON_MAX_DATE_LENGTH]; + + if ((pTime = ds_secstotime(&Time, uEpochDate)) == NULL) + { + return(JSON_ERR_INVALID_PARAM); + } + + ds_timetostr(pTime, TIMETOSTRING_CONVERSION_ISO_8601, FALSE, strDate, sizeof(strDate)); + return(_JsonAddStr(pBuffer, pElemName, strDate, FALSE)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonFormatVPrintf + + \Description + Takes a format string and variable parameter list and formats into syntax + correct JSON. The "v" (variable args) version can be called by other + functions that use "..." in the their prototype. + + \Input *pJsonBuff - JSON output buffer + \Input iBufLen - length of Json output buffer (negative=append to existing buffer) + \Input *pFormat - printf style format string + \Input pFmtArgs - variable argument list (use va_start / va_end to get) + + \Output + char * - pointer to formatted buffer + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +char *JsonFormatVPrintf(char *pJsonBuff, int32_t iBufLen, const char *pFormat, va_list pFmtArgs) +{ +#if 0 + int32_t iToken; + int32_t iIndex; + char strName[65]; + const char *pName; + const char *pParse; + unsigned char uFlags = 0; + /* table of allowed name characters. see http://www.w3.org/TR/xml/#charsets + for information on name characters allowed by the XML 1.0 specification */ + static char _ConvName[128] = + " " " " + " -. " "0123456789: " + " ABCDEFGHIJKLMNO" "PQRSTUVWXYZ _" + " abcdefghijklmno" "pqrstuvwxyz "; + + // determine if whitespace indenting is desired + if (*pFormat == ' ') + { + uFlags |= JSON_FL_WHITESPACE; + ++pFormat; + } + + // start the formatting + if (iBufLen >= 0) + JsonInit(pJsonBuff, iBufLen, uFlags); + + // parse the format string + for (pParse = pFormat; *pParse != 0; ++pParse) + { + // look for tag open + if (pParse[0] == '{') + { + for (iIndex = 0; (iIndex < (signed)(sizeof(strName) - 1)) && (pParse[iIndex+1] > 0) && (pParse[iIndex+1] < 127); ++iIndex) + { + if (_ConvName[(int32_t)pParse[iIndex+1]] <= ' ') + break; + strName[iIndex] = pParse[iIndex+1]; + } + strName[iIndex] = 0; + if (iIndex > 0) + { + JsonObjStart(pJsonBuff, strName); + } + } + + // parse name for assignments + if (pParse[0] == '=') + { + // find start of name + for (pName = pParse; (pName != pFormat) && (pName[-1] > 0) && (pName[-1] < 127); --pName) + { + if (_ConvName[pName[-1]&127] <= ' ') + break; + } + // copy and format name in private buffer + for (iIndex = 0; (iIndex < (signed)(sizeof(strName) - 1)) && (pName+iIndex != pParse); ++iIndex) + { + strName[iIndex] = pName[iIndex]; + } + strName[iIndex] = 0; + } + + // grab next 3 characters as a token + iToken = ' '; + iToken = (iToken << 8) | pParse[0]; + iToken = (iToken << 8) | pParse[1]; + iToken = (iToken << 8) | pParse[2]; + + // handle format tokens + if (pParse[0] == '}') + { + JsonObjEnd(pJsonBuff); + } + else if (iToken == ' >%s') + { + const char *pString = va_arg(pFmtArgs, const char *); + JsonElemSetString(pJsonBuff, pString); + } + else if (iToken == ' >%d') + { + int32_t iInteger = va_arg(pFmtArgs, int32_t); + JsonElemSetInt(pJsonBuff, iInteger); + } + else if (iToken == ' >%e') + { + uint32_t uEpoch = va_arg(pFmtArgs, uint32_t); + if (uEpoch == 0) + uEpoch = (uint32_t)ds_timeinsecs(); + JsonElemSetDate(pJsonBuff, uEpoch); + } + else if (iToken == ' >%a') + { + uint32_t uAddr = va_arg(pFmtArgs, uint32_t); + JsonElemSetAddr(pJsonBuff, uAddr); + } + else if (strName[0] == 0) + { + } + else if (iToken == ' =%s') + { + const char *pString = va_arg(pFmtArgs, const char *); + JsonAttrSetString(pJsonBuff, strName, pString); + } + else if (iToken == ' =%d') + { + int32_t iInteger = va_arg(pFmtArgs, int32_t); + JsonAttrSetInt(pJsonBuff, strName, iInteger); + } + else if (iToken == ' =%e') + { + uint32_t uEpoch = va_arg(pFmtArgs, uint32_t); + if (uEpoch == 0) + uEpoch = (uint32_t)ds_timeinsecs(); + JsonAttrSetDate(pJsonBuff, strName, uEpoch); + } + else if (iToken == ' =%a') + { + uint32_t uAddr = va_arg(pFmtArgs, uint32_t); + JsonAttrSetAddr(pJsonBuff, strName, uAddr); + } + } + + // finish up and return data pointer + if (iBufLen >= 0) + pJsonBuff = JsonFinish(pJsonBuff); + return(pJsonBuff); + #else + return(NULL); + #endif +} + +/*F*************************************************************************************/ +/*! + \Function JsonFormatPrintf + + \Description + Takes a format string and variable parameter list and formats into syntax + correct JSON. + + \Input *pJsonBuff - JSON output buffer + \Input iBufLen - length of Json output buffer (negative=append to existing buffer) + \Input *pFormat - printf style format string + + \Output + char * - pointer to formatted buffer + + \Version 12/11/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +char *JsonFormatPrintf(char *pJsonBuff, int32_t iBufLen, const char *pFormat, ...) +{ + char *pResult; + va_list pFmtArgs; + + // setup varargs and call real function + va_start(pFmtArgs, pFormat); + pResult = JsonFormatVPrintf(pJsonBuff, iBufLen, pFormat, pFmtArgs); + va_end(pFmtArgs); + + // return pointer to start of valid JSON data (skips leading spaces) + return(pResult); +} diff --git a/r5dev/thirdparty/dirtysdk/source/util/jsonparse.c b/r5dev/thirdparty/dirtysdk/source/util/jsonparse.c new file mode 100644 index 00000000..baf03d94 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/jsonparse.c @@ -0,0 +1,1229 @@ +/*H*************************************************************************************/ +/*! + \File jsonparse.c + + \Description + Simple JSON parser. + + \Copyright + Copyright (c) Electronic Arts 2012. + + \Notes + Written by Greg Schaefer outside of EA for a personal project, but donated + back for a good cause. + + The parse buffer contains a list of objects, consisting of an offset and size. + The offset is the offset of the object in the JSON buffer. The size is the + size of the object in the JSON buffer, except for arrays and objects, where it + is the size of the object in the parse buffer (used when skipping past an array + or object). Values are written as uint16_t, with progressive encoding employed + when required. + + UNICODE support is limited to extracting ASCII characters that are encoded + as UNICODE. + + \Todo + Implement support for getting real numbers + + \Version 12/11/2012 (jbrookes) Added to DirtySDK, added some new functionality +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/util/jsonparse.h" + +/*** Defines ***************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ + +//! decode table to classify characters as whitespace, number, letter, bracket, brace, or invalid +static const uint8_t _strDecode[256] = +{ + 0,' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', // 00..0f + ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', // 10..1f + ' ', 0,'"', 0, 0, 0, 0, 0, 0, 0, 0, 0,',','1', 0, 0, // 20..2f + '1','1','1','1','1','1','1','1','1','1',':', 0, 0, 0, 0, 0, // 30..3f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..4f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'[','\\',']', 0, 0, // 50..5f + ' ','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a', // 60..6f + 'a','a','a','a','a','a','a','a','a','a','a','{',0 ,'}', 0, 0, // 70..7f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80..8f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90..9f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0..af + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b0..bf + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0..cf + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d0..df + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0..ef + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f0..ff +}; + +//! characters that need to be escaped +static const uint8_t _strEscape[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..0f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10..1f + 0, 0,'"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'/', // 20..2f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30..3f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..4f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50..5f + 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 10, 0, // 60..6f + 0, 0, 0, 0, 0,'u', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70..7f + 0, 0, 13, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80..8f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90..9f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a0..af + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b0..bf + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c0..cf + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d0..df + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e0..ef + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f0..ff +}; + +//! hex->int decode table +static const uint8_t _HexDecode[256] = +{ + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,128,128,128,128,128,128, + 128, 10, 11, 12, 13, 14, 15,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128, 10, 11, 12, 13, 14, 15,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128 +}; + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function _JsonGetQuoted + + \Description + Get text from a quoted string + + \Input *pDst - [out] storage for text + \Input iLim - size of output buffer + \Input *pSrc - pointer to quoted string + \Input iSkip - output buffer increment when writing (zero for length check) + + \Output + int32_t - returns string length + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +static int32_t _JsonGetQuoted(uint8_t *pDst, int32_t iLim, const uint8_t *pSrc, int32_t iSkip) +{ + int32_t iLen = 0; + uint8_t c; + + // make room for terminator + iLim -= 1; + + // make sure its a quoted string + if (*pSrc == '"') + { + for (++pSrc; (iLen < iLim) && (c = *pSrc) != '"'; ++pSrc) + { + // handle escape sequence + if ((c == '\\') && (pSrc[1] > ' ')) + { + c = _strEscape[*++pSrc]; + if ((c == 'u') && (pSrc[1] != '"') && (pSrc[2] != '"') && (pSrc[3] != '"') && (pSrc[4] != '"')) + { + c = (_HexDecode[pSrc[3]]<<4)|(_HexDecode[pSrc[4]]<<0), pSrc += 4; + } + } + // conditional save (to use as length check) + *pDst = c; + pDst += iSkip; + ++iLen; + } + *pDst = '\0'; + } + + return(iLen); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonParseWrite + + \Description + Write a value to the parse buffer. Depending on size, the value will be encoded + in one or two 16 bit values using a progressive encoding scheme. + + \Input *pParse - [out] buffer for parse value + \Input *pMax - limit of parse buffer + \Input uValue - value to write to parse buffer + \Input bWide - if TRUE, write using wide encoding + + \Output + uint16_t * - pointer to next value + + \Notes + Values from zero to 0x7fff are written as-is using a uint16_t. Larger values + are written with the lower 15 bits or'd with 0x8000, followed by bits 15-31 in + the following uint16_t. The largest number that can be encoded this way is + 0x7fffffff, as one bit is sacrificed for the progressive bit. + + \Version 02/02/2016 (jbrookes) +*/ +/*************************************************************************************F*/ +static uint16_t *_JsonParseWrite(uint16_t *pParse, uint16_t *pMax, uint32_t uValue, uint8_t bWide) +{ + if ((uValue > 0x7fff) || (bWide == TRUE)) + { + if (pParse < pMax) + { + *pParse = (uint16_t)uValue | 0x8000; + } + uValue >>= 15; + pParse += 1; + } + if (pParse < pMax) + { + *pParse = (uint16_t)uValue; + } + pParse += 1; + return(pParse); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonParseRead + + \Description + Read a progressively-encoded value from the parse buffer. + + \Input *pParse - parse buffer + \Input *pValue - [out] storage for value read from parse buffer + \Input *pParseMax - end of parse buffer + + \Output + uint16_t * - pointer to next value + + \Notes + See _JsonParseWrite for a description of the progressive encoding scheme. + + \Version 02/02/2016 (jbrookes) +*/ +/*************************************************************************************F*/ +static const uint16_t *_JsonParseRead(const uint16_t *pParse, uint32_t *pValue, const uint16_t *pParseMax) +{ + *pValue = 0; + if (pParse < pParseMax) + { + *pValue = *pParse; + } + pParse += 1; + if (*pValue & 0x8000) + { + *pValue &= ~0x8000; + if (pParse < pParseMax) + { + *pValue |= (uint32_t)*pParse << 15; + } + pParse += 1; + } + return(pParse); +} + +/*F*************************************************************************************/ +/*! + \Function _JsonParseReadOffsetAndSize + + \Description + Read offset and size values. If offset exceeds parse buffer size, an offset and + size of zero are returned. + + \Input *pParse - parse buffer + \Input *pOffset - [out] storage for offset + \Input *pSize - [out] storage for size + \Input *pParseMax - end of parse buffer + + \Output + uint16_t * - pointer to next parse object + + \Notes + See _JsonParseWrite for a description of the progressive encoding scheme. + + \Version 02/02/2016 (jbrookes) +*/ +/*************************************************************************************F*/ +static const uint16_t *_JsonParseReadOffsetAndSize(const uint16_t *pParse, uint32_t *pOffset, uint32_t *pSize, const uint16_t *pParseMax) +{ + pParse = _JsonParseRead(pParse, pOffset, pParseMax); + pParse = _JsonParseRead(pParse, pSize, pParseMax); + if (pParse > pParseMax) + { + *pOffset = 0; + *pSize = 0; + } + return(pParse); +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************/ +/*! + \Function JsonParse + + \Description + Pre-parse a JSON buffer for fast parsing. Must be called on a buffer before + any Find() or Get() functions are used. Pass pDst=NULL and iMax=0 to query + the required table size. + + \Input *pDst - [out] storage for lookup table, two elements per object + \Input iMax - number of uint16_t elements in output table + \Input *_pSrc - pointer to JSON buffer + \Input iLen - length of JSON buffer; if -1 a strlen is performed + + \Output + int32_t - zero=table too small, else size of table in bytes + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonParse(uint16_t *pDst, int32_t iMax, const char *_pSrc, int32_t iLen) +{ + uint8_t c; + int32_t iArray = 0; + int32_t iObject = 0; + uint16_t *pArray[16]; + uint16_t *pObject[16]; + const uint8_t *pSrc = (const uint8_t *)_pSrc; + const uint8_t *pOff = pSrc; + const uint8_t *pEnd; + uint16_t *pBeg = pDst; + uint16_t *pMax = pDst+iMax; + uint32_t uStart; + uint8_t bTruncated = FALSE; + int32_t iBufSize; + + // easy string length + if (iLen < 0) + { + iLen = (int32_t)strlen(_pSrc); + } + pEnd = pSrc+iLen; + + // check size and save json pointer in parse buffer + if (iMax != 0) + { + // make sure we have enough space to store json pointer plus at least one max-sized json parse object + if (iMax < (int32_t)(sizeof(pSrc)/sizeof(uint16_t)) + 4 + 4) + { + return(0); + } + // save json pointer at start of parse buffer + ds_memcpy(pDst, &pSrc, sizeof(pSrc)); + // save max size + ds_memcpy(pDst + (sizeof(pSrc)/sizeof(*pDst)), &iMax, sizeof(iMax)); + } + // add space for pointer + pDst += sizeof(pSrc)/sizeof(*pDst); + // add space for max size + pDst += sizeof(iMax)/sizeof(*pDst); + + // find the tokens + for (; pSrc != pEnd; ++pSrc) + { + c = _strDecode[*pSrc]; + + // handle end of decoding + if (c == '\0') + { + break; + } + // handle white space + if (c == ' ') + { + continue; + } + // save start of token + pDst = _JsonParseWrite(pDst, pMax, uStart = (uint32_t)(pSrc-pOff), FALSE); + + // handle number + if (c == '1') + { + // skip past number + for (++pSrc; (pSrc != pEnd) && (*pSrc >= '0') && (*pSrc <= '9'); ++pSrc) + ; + // handle decimal ending + if ((pSrc != pEnd) && (*pSrc == '.')) + { + for (++pSrc; (pSrc != pEnd) && (*pSrc >= '0') && (*pSrc <= '9'); ++pSrc) + ; + } + // handle exponent + if ((pSrc != pEnd) && ((*pSrc|32) == 'e')) + { + // gobble extension after exponent + ++pSrc; + if ((pSrc != pEnd) && ((*pSrc == '+') || (*pSrc == '-'))) + { + ++pSrc; + } + for (; (pSrc != pEnd) && (*pSrc >= '0') && (*pSrc <= '9'); ++pSrc) + ; + } + --pSrc; + pDst = _JsonParseWrite(pDst, pMax, (uint32_t)((pSrc-pOff)-uStart+1), FALSE); + } + // handle string + else if (c == '"') + { + // walk the string + for (++pSrc; (pSrc != pEnd) && (*pSrc != '"'); ++pSrc) + { + if ((*pSrc == '\\') && (pSrc[1] > ' ')) + { + ++pSrc; + } + } + pDst = _JsonParseWrite(pDst, pMax, (uint32_t)((pSrc-pOff)-uStart+1), FALSE); + } + // handle token + else if (c == 'a') + { + for (++pSrc; (pSrc != pEnd) && (_strDecode[*pSrc] == 'a'); ++pSrc) + ; + --pSrc; + pDst = _JsonParseWrite(pDst, pMax, (uint32_t)((pSrc-pOff)-uStart+1), FALSE); + } + // handle object open + else if ((c == '{') && (iObject < (int32_t)(sizeof(pObject)/sizeof(pObject[0])))) + { + pObject[iObject++] = pDst; + pDst = _JsonParseWrite(pDst, pMax, 0, TRUE); + } + // handle object close + else if ((c == '}') && (iObject > 0)) + { + --iObject; + _JsonParseWrite(pObject[iObject], pMax, (uint32_t)(pDst-pObject[iObject]), TRUE); + pDst = _JsonParseWrite(pDst, pMax, 1, FALSE); + } + // handle array open + else if ((c == '[') && (iArray < (int32_t)(sizeof(pArray)/sizeof(pArray[0])))) + { + pArray[iArray++] = pDst; + pDst = _JsonParseWrite(pDst, pMax, 0, TRUE); + } + // handle array close + else if ((c == ']') && (iArray > 0)) + { + --iArray; + _JsonParseWrite(pArray[iArray], pMax, (uint32_t)(pDst-pArray[iArray]), TRUE); + pDst = _JsonParseWrite(pDst, pMax, 1, FALSE); + } + else + { + pDst = _JsonParseWrite(pDst, pMax, 1, FALSE); + } + } + + // close any remaining objects -- invalid JSON? + while (iObject-- > 0) + { + _JsonParseWrite(pObject[iObject], pMax, (uint32_t)(pDst-pObject[iObject]), TRUE); + } + while (iArray-- > 0) + { + _JsonParseWrite(pArray[iArray], pMax, (uint32_t)(pDst-pArray[iArray]), TRUE); + } + + // make sure we always terminate output buffer + if ((iMax != 0) && (pDst > pMax-2)) + { + pDst = pMax-2; + bTruncated = TRUE; + } + // terminate output buffer + pDst = _JsonParseWrite(pDst, pMax, 0, FALSE); + pDst = _JsonParseWrite(pDst, pMax, 0, FALSE); + + // calculate size of buffer in bytes + iBufSize = (int32_t)(((uint8_t *)pDst) - ((uint8_t *)pBeg)); + + // return size in bytes, or zero if the table was truncated + return(bTruncated ? 0 : iBufSize); +} + +/*F*************************************************************************************/ +/*! +\Function JsonParse2 + + \Description + Pre-parse a JSON buffer for fast parsing. Must be called on a buffer before + any Find() or Get() functions are used. This version allocates the required + amount of memory internally rather than taking the buffer as input. + + \Input *pSrc - pointer to JSON buffer + \Input iLen - length of JSON buffer; if -1 a strlen is performed + \Input iMemModule - memory module passed to DirtyMemAlloc + \Input iMemGroup - memory group passed to DirtyMemAlloc + \Input *pMemGroupUserData - memory group user data passed to DirtyMemAlloc + + \Output + uint16_t * - allocated parse table, or NULL on error + + \Version 12/05/2016 (jbrookes) +*/ +/*************************************************************************************F*/ +uint16_t *JsonParse2(const char *pSrc, int32_t iLen, int32_t iMemModule, int32_t iMemGroup, void *pMemGroupUserData) +{ + uint16_t *pBuff; + int32_t iSize; + + // get buffer size + if ((iSize = JsonParse(NULL, 0, pSrc, iLen)) == 0) + { + return(NULL); + } + // allocate buffer space + if ((pBuff = DirtyMemAlloc(iSize, iMemModule, iMemGroup, pMemGroupUserData)) == NULL) + { + return(NULL); + } + // parse the buffer + if (JsonParse(pBuff, iSize, pSrc, iLen) != iSize) + { + DirtyMemFree(pBuff, iMemModule, iMemGroup, pMemGroupUserData); + pBuff = NULL; + } + return(pBuff); +} + +/*F*************************************************************************************/ +/*! + \Function JsonFind + + \Description + Locate a JSON element + + \Input *pParse - pre-parsed lookup from JsonParse + \Input *pName - name of element to locate + + \Output + const char * - pointer to element, or NULL if not found + + \Notes + A name can include a period character '.' which indicates an object reference, + or an open bracket '[' which indicates an array reference. A bracket must be + the last character in the name. + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +const char *JsonFind(const uint16_t *pParse, const char *pName) +{ + return(JsonFind2(pParse, NULL, pName, 0)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonFind2 + + \Description + Locate a JSON element, starting from an offset, with an optional array index + + \Input *pParse - pre-parsed lookup from JsonParse + \Input *_pJsonOff - offset into JSON buffer to begin search + \Input *pName - name of element to locate + \Input iIndex - [optional] index of array element to return + + \Output + const uint8_t * - pointer to element, or NULL if not found + + \Notes + This version of Find is useful for parsing arrays of scalars or objects, or + for starting a parse at an offset within the JSON document. + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +const char *JsonFind2(const uint16_t *pParse, const char *_pJsonOff, const char *pName, int32_t iIndex) +{ + const uint8_t *pJsonOff = (const uint8_t *)_pJsonOff; + const uint8_t *pJson; + const char *pLast, *pElem; + const uint16_t *pParseMax, *pTemp, *pTemp2; + int32_t iLen, iMax; + uint32_t uOffset, uOffsetNext, uSize, uSizeNext, uTemp; + uint8_t c; + + // save pointer for parse max + pParseMax = pParse; + + // get json data pointer; note cast to avoid invalid vc warning -- thinks &pJson is const whereas it points to something const + ds_memcpy((uint8_t *)&pJson, pParse, sizeof(pJson)); + pParse += sizeof(pJson)/sizeof(*pParse); + // get parse buffer max size + ds_memcpy(&iMax, pParse, sizeof(iMax)); + pParse += sizeof(iMax)/sizeof(*pParse); + + // point to end of parse buffer + pParseMax += iMax; + + // if json has outer object, then skip it + pTemp = _JsonParseRead(pParse, &uOffset, pParseMax); + if (pJson[uOffset] == '{') + { + pParse = _JsonParseRead(pTemp, &uTemp, pParseMax); + } + + // if they gave us an offset, move the parse forward to match + if (pJsonOff != NULL) + { + uint32_t uJsonOff = (uint32_t)(pJsonOff - pJson); + while (1) + { + pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax); + if ((uOffset >= uJsonOff) || (uSize == 0)) + { + break; + } + pParse = pTemp; + } + } + + // parse the find string + for (uSize = 1; (*pName != 0) && (uSize != 0); ) + { + pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax); + + // handle traversing into an object + if (*pName == '.') + { + // if not an object, then its an error + if (pJson[uOffset] != '{') + { + break; + } + // move into object and keep parsing + pParse = pTemp; + ++pName; + continue; + } + // handle traversing into an array + else if (*pName == '[') + { + // if not an array, then its an error + if (pJson[uOffset] != '[') + { + break; + } + // skip to nth element + for (pParse = pTemp, uSize = 1; (iIndex > 0) && (uSize != 0); iIndex -= 1) + { + pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax); + c = pJson[uOffset]; + if (c == ']') + { + break; + } + else if (c == '{') + { + // skip to end of object + pParse += uSize; + pTemp = pParse; + } + // skip object + if (uSize != 0) + { + pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax); + } + // skip trailing comma? + pTemp = _JsonParseReadOffsetAndSize(pTemp, &uOffset, &uSize, pParseMax); + if ((uSize != 0) && (pJson[uOffset] == ',')) + { + pParse = pTemp; + } + } + // if we didn't end on a close bracket we've found our element + if ((uSize != 0) && (pJson[uOffset] != ']')) // must re-check because we could end exactly on the close bracket + { + pName = "\0"; + } + break; + } + // check to see if object name matches + else if (((*pName|32) >= 'a') && ((*pName|32) <= 'z')) + { + // figure out length + for (pLast = pName; (*pLast != 0) && (*pLast != '.') && (*pLast != '['); ++pLast) + ; + iLen = (int32_t)(pLast-pName); + + // search for this name + for (uSize = 1; uSize != 0; pParse = pTemp) + { + pTemp = _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax); + c = pJson[uOffset]; + + // skip arrays & objects + if ((c == '[') || (c == '{')) + { + pTemp = _JsonParseReadOffsetAndSize(pParse+uSize, &uOffset, &uSize, pParseMax); + if (uSize == 0) + { + break; + } + continue; + } + + // end of array/object is terminator, or we hit the end of the parse buffer + if ((c == ']') || (c == '}') || (uSize == 0)) + { + return(NULL); + } + + // look for a label + pTemp2 = _JsonParseReadOffsetAndSize(pTemp, &uOffsetNext, &uSizeNext, pParseMax); + if ((c == '"') && (pJson[uOffsetNext] == ':') && (iLen+2 == (signed)uSize)) + { + if (memcmp(pJson+uOffset+1, pName, iLen) == 0) + { + // skip the label + pParse = pTemp2; + uOffset = uOffsetNext; + uSize = uSizeNext; + pName = pLast; + break; + } + } + } + } + else + { + // invalid pattern + return(NULL); + } + } + + // get current offset and size from parse + _JsonParseReadOffsetAndSize(pParse, &uOffset, &uSize, pParseMax); + + /* point to element; validate the name matches and that we are pointing at a valid element. both checks + are required, as we could have found the name successfully, but then failed to find the element */ + if ((*pName == '\0') && (uSize != 0)) + { + pElem = (const char *)(pJson+uOffset); + } + else + { + pElem = NULL; + } + return(pElem); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetString + + \Description + Extract a JSON string element. Pass pBuffer=NULL for length check. + + \Input *pJson - JSON buffer + \Input *pBuffer - [out] storage for string (may be NULL for length query) + \Input iLength - length of output buffer + \Input *pDefault - default value to use, if string could not be extracted + + \Output + int32_t - length of string, or negative on error + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonGetString(const char *pJson, char *pBuffer, int32_t iLength, const char *pDefault) +{ + return(JsonGetString2(pJson, pBuffer, iLength, pDefault, NULL)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetString2 + + \Description + Extract a JSON string element. Pass pBuffer=NULL for length check. Error + accumulating version. + + \Input *pJson - JSON buffer + \Input *pBuffer - [out] storage for string (may be NULL for length query) + \Input iLength - length of output buffer + \Input *pDefault - default value to use, if string could not be extracted + \Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error + + \Output + int32_t - length of string, or negative on error + + \Version 12/05/2016 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonGetString2(const char *pJson, char *pBuffer, int32_t iLength, const char *pDefault, uint8_t *pError) +{ + uint8_t c; + + // handle default value + if (pJson == NULL) + { + // error if no default string + if (pDefault == NULL) + { + if (pError != NULL) + { + *pError = 1; + } + return(-1); + } + // copy string if it isn't a length-only query + if (pBuffer != NULL) + { + ds_strnzcpy(pBuffer, pDefault, iLength); + pDefault = pBuffer; + } + // return the length + return((int32_t)strlen(pDefault)); + } + + // handle length query + if (pBuffer == NULL) + { + return(_JsonGetQuoted(&c, INT32_MAX, (const uint8_t *)pJson, 0)); + } + + // make sure return buffer has terminator length + if (iLength < 1) + { + if (pError != NULL) + { + *pError = 1; + } + return(-1); + } + + // copy the string + return(_JsonGetQuoted((uint8_t *)pBuffer, iLength, (const uint8_t *)pJson, 1)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetInteger + + \Description + Extract a JSON integer number element. + + \Input *pJson - JSON buffer + \Input iDefault - default value to use, if number could not be extracted + + \Output + int64_t - integer, or default value + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int64_t JsonGetInteger(const char *pJson, int64_t iDefault) +{ + return(JsonGetInteger2(pJson, iDefault, INT64_MIN, INT64_MAX, NULL)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetInteger2 + + \Description + Extract a JSON integer number element with range checking and error accumulation. + Pass zero for both iMin and iMax to disable range checking. + + \Input *pJson - JSON buffer + \Input iDefault - default value to use, if number could not be extracted + \Input iMin - min value for range checking + \Input iMax - max value for range checking + \Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error + + \Output + int64_t - integer, or default value + + \Version 12/05/2016 (jbrookes) +*/ +/*************************************************************************************F*/ +int64_t JsonGetInteger2(const char *pJson, int64_t iDefault, int64_t iMin, int64_t iMax, uint8_t *pError) +{ + int64_t iSign = 1; + int64_t iValue; + + // handle default + if (pJson == NULL) + { + if (pError != NULL) + { + *pError = 1; + } + return(iDefault); + } + + // handle negative + if (*pJson == '-') + { + ++pJson; + iSign = -1; + } + + // parse the value + for (iValue = 0; (*pJson >= '0') && (*pJson <= '9'); ++pJson) + { + iValue = (iValue * 10) + (*pJson & 15); + } + + // range check + if ((iMin != 0) || (iMax != 0)) + { + iValue = DS_CLAMP(iValue, iMin, iMax); + } + + // return with sign + return(iSign*iValue); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetDate + + \Description + Extract a JSON date element + + \Input *pJson - JSON buffer + \Input uDefault - default date value to use + + \Output + uint32_t - extracted date, or default + + \Notes + Date is assumed to be in ISO_8601 format + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +uint32_t JsonGetDate(const char *pJson, uint32_t uDefault) +{ + return(JsonGetDate2(pJson, uDefault, NULL)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetDate2 + + \Description + Extract a JSON date element + + \Input *pJson - JSON buffer + \Input uDefault - default date value to use + \Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error + + \Output + uint32_t - extracted date, or default + + \Notes + Date is assumed to be in ISO_8601 format + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +uint32_t JsonGetDate2(const char *pJson, uint32_t uDefault, uint8_t *pError) +{ + uint32_t uDate = uDefault; + char strValue[128]; + + JsonGetString(pJson, strValue, sizeof(strValue), ""); + if ((uDate = (uint32_t)ds_strtotime2(strValue, TIMETOSTRING_CONVERSION_ISO_8601)) == 0) + { + uDate = uDefault; + if (pError != NULL) + { + *pError = 1; + } + } + return(uDate); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetBoolean + + \Description + Extract a JSON boolean + + \Input *pJson - JSON buffer + \Input bDefault - default boolean value to use + + \Output + uint8_t - extracted bool, or default + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +uint8_t JsonGetBoolean(const char *pJson, uint8_t bDefault) +{ + return(JsonGetBoolean2(pJson, bDefault, NULL)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetBoolean2 + + \Description + Extract a JSON boolean, with accumulating error + + \Input *pJson - JSON buffer + \Input bDefault - default boolean value to use + \Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error + + \Output + uint8_t - extracted bool, or default + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +uint8_t JsonGetBoolean2(const char *pJson, uint8_t bDefault, uint8_t *pError) +{ + uint8_t bValue = bDefault; + + // handle default + if (pJson == NULL) + { + if (pError != NULL) + { + *pError = 1; + } + return(bDefault); + } + // check for true + if (strncmp((const char *)pJson, "true", 4) == 0) + { + bValue = TRUE; + } + else if (strncmp((const char *)pJson, "false", 4) == 0) + { + bValue = FALSE; + } + else if (pError != NULL) + { + *pError = 1; + } + // return result + return(bValue); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetEnum + + \Description + Extract a JSON enumerated value. The source type is assumed to be a string, + and pEnumArray contains a NULL-terminated list of strings that may be converted + to an enum value. + + \Input *pJson - JSON buffer + \Input *pEnumArray - NULL-terminated list of enum strings + \Input iDefault - default enum value to use if no match is found + + \Output + int32_t - enum value; or default + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonGetEnum(const char *pJson, const char *pEnumArray[], int32_t iDefault) +{ + return(JsonGetEnum2(pJson, pEnumArray, iDefault, NULL)); +} + +/*F*************************************************************************************/ +/*! + \Function JsonGetEnum2 + + \Description + Extract a JSON enumerated value, with accumulating error. The source type is + assumed to be a string, and pEnumArray contains a NULL-terminated list of strings + that may be converted to an enum value. + + \Input *pJson - JSON buffer + \Input *pEnumArray - NULL-terminated list of enum strings + \Input iDefault - default enum value to use if no match is found + \Input *pError - [in/out] accumulation flag for parse errors; 1=error, 0=no error + + \Output + int32_t - enum value; or default + + \Version 12/13/2012 (jbrookes) +*/ +/*************************************************************************************F*/ +int32_t JsonGetEnum2(const char *pJson, const char *pEnumArray[], int32_t iDefault, uint8_t *pError) +{ + int32_t iEnum = iDefault; + char strValue[128]; + + JsonGetString(pJson, strValue, sizeof(strValue), ""); + for (iEnum = 0; pEnumArray[iEnum] != NULL; iEnum += 1) + { + if (!strcmp(pEnumArray[iEnum], strValue)) + { + return(iEnum); + } + } + if (pError != NULL) + { + *pError = 1; + } + return(iDefault); +} + +/*F*************************************************************************************************/ +/*! + \Function JsonSeekObjectEnd + + \Description + Seek to the end of an object within the JSON text buffer + + \Input *pList - pointer to the beginning of object + + \Output + const char * - pointer to the end of the object, NULL for error + + \Version 04/17/2013 (amakoukji) +*/ +/*************************************************************************************************F*/ +const char *JsonSeekObjectEnd(const char *pList) +{ + const char *pCurrent = pList; + const char *pEnd = pList + (int32_t)strlen(pList); + uint32_t uDepth = 0; + char c; + + while (pCurrent < pEnd) + { + c = *pCurrent; + + switch (c) + { + case '"': + { + // walk the string + for (++pCurrent; (pCurrent != pEnd) && (*pCurrent != '"'); ++pCurrent) + { + if ((*pCurrent == '\\') && (pCurrent[1] > ' ')) + { + ++pCurrent; + } + } + } + break; + + case '{': + { + uDepth += 1; + } + break; + + case '}': + { + uDepth -= 1; + } + break; + + case ',': + { + if (uDepth == 0) + { + return(pCurrent); + } + } + break; + + case ']': + { + if (uDepth == 0) + { + return(pCurrent); + } + } + break; + } + + pCurrent += 1; + } + + // error + return(NULL); +} + +/*F*************************************************************************************************/ +/*! + \Function JsonSeekValue + + \Description + Given a pointer to the beginning of a simple JSON key value pair, seeks the + start of the value + + \Input *pKey - module ref + + \Output + int32_t - pointer to the start of the value, NULL for error + + \Version 11/28/2013 (amakoukji) +*/ +/*************************************************************************************************F*/ +const char *JsonSeekValue(const char *pKey) +{ + const char *pTemp = pKey; + + if (pKey == NULL) + { + return(NULL); + } + + // seek to the ':' + while (*pTemp != ':') + { + if ((*pTemp == '\0') || (*pTemp == '\n')) + { + return(NULL); + } + pTemp += 1; + } + pTemp += 1; + + // seek to non-whitespace + while ((*pTemp == ' ') || (*pTemp == '\t')) + { + if ((*pTemp == '\0') || (*pTemp == '\n')) + { + return(NULL); + } + pTemp += 1; + } + + return(pTemp); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/util/murmurhash3.c b/r5dev/thirdparty/dirtysdk/source/util/murmurhash3.c new file mode 100644 index 00000000..792e4950 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/murmurhash3.c @@ -0,0 +1,448 @@ +/*H********************************************************************************/ +/*! + \File murmurhash3.c + + \Description + An implementation of MurmurHash3, based heavily on the x64 128bit output + implementation. MurmurHash3 is a public domain hashing algorithm written + by Austin Appleby, ref http://code.google.com/p/smhasher/wiki/MurmurHash3. + + MurmurHash3 is not considered cryptographically secure, however it has + excellent collision resistance and is orders of magnitude faster than the + fastest secure cryptographic hash. + + \Copyright + Copyright (c) 2014 Electronic Arts + + \Version 03/07/2014 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include + +#include "DirtySDK/util/murmurhash3.h" + +/*** Defines **********************************************************************/ + +#if defined(_MSC_VER) + #define MH3_FORCE_INLINE __forceinline +#else + #define MH3_FORCE_INLINE __inline__ __attribute__((always_inline)) +#endif + + +#define MURMURHASH3_C1 (0x87c37b91114253d5) +#define MURMURHASH3_C2 (0x4cf5ad432745937f) + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +#if !defined(_MSC_VER) +/*F********************************************************************************/ +/*! + \Function _rotl64 + + \Description + Implementation of _rotl64 for non-Windows. + + \Input x - 64bit data to rotate + \Input r - number of bits to rotate + + \Output + uint64_t - rotated data + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static MH3_FORCE_INLINE uint64_t _MurmurHash3Rotl64(uint64_t x, int8_t r) +{ + return (x << r) | (x >> (64 - r)); +} +#else + #define _MurmurHash3Rotl64(x, y) _rotl64(x, y) +#endif + +/*F********************************************************************************/ +/*! + \Function _MurmurHash3Fmix + + \Description + 'fmix' operation from murmurhash3 + + \Input k - 64bit data to mix + + \Output + uint64_t - 64 bits of mixed data + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static MH3_FORCE_INLINE uint64_t _MurmurHash3Fmix (uint64_t k) +{ + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + return(k); +} + +/*F********************************************************************************/ +/*! + \Function _MurmurHash3Read64 + + \Description + Read 64 bits of input data. Little-endian versions that can handle an + unaligned 64bit load from memory do a direct read for speed. Other + versions execute a portable 64bit load, which is slower. + + \Input *pData - input data to read + \Input iData - 64bit offset of data to read + + \Output + uint64_t - 64 bits of data ready to be hashed + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +MH3_FORCE_INLINE static uint64_t _MurmurHash3Read64(const uint64_t *pData, int32_t iData) +{ +#if EA_SYSTEM_LITTLE_ENDIAN + return(pData[iData]); +#else + /* read data little endian; we do this because we want to optimize + for running on x64 hardware */ + const uint8_t *_pData = (const uint8_t *)(pData+iData); + uint64_t uData; + uData = ((uint64_t)_pData[0]); + uData |= ((uint64_t)_pData[1]) << 8; + uData |= ((uint64_t)_pData[2]) << 16; + uData |= ((uint64_t)_pData[3]) << 24; + uData |= ((uint64_t)_pData[4]) << 32; + uData |= ((uint64_t)_pData[5]) << 40; + uData |= ((uint64_t)_pData[6]) << 48; + uData |= ((uint64_t)_pData[7]) << 56; + return(uData); +#endif +} + +/*F********************************************************************************/ +/*! + \Function _MurmurHash3Transform + + \Description + Add 16 byte block of state data to the MurmurHash3 context (hash the data) + + \Input *pContext - hash context + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _MurmurHash3Transform(MurmurHash3T *pContext) +{ + uint64_t k1 = _MurmurHash3Read64((uint64_t *)pContext->aData, 0); + uint64_t k2 = _MurmurHash3Read64((uint64_t *)pContext->aData, 1); + uint64_t h1 = pContext->aState[0]; + uint64_t h2 = pContext->aState[1]; + const uint64_t c1 = MURMURHASH3_C1; + const uint64_t c2 = MURMURHASH3_C2; + + k1 *= c1; k1 = _MurmurHash3Rotl64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = _MurmurHash3Rotl64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = _MurmurHash3Rotl64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = _MurmurHash3Rotl64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + + pContext->aState[0] = h1; + pContext->aState[1] = h2; +} + +/*F********************************************************************************/ +/*! + \Function _MurmurHash3Finalize + + \Description + Hash remaining data, output hash result + + \Input *pOutput - [out] buffer to store result hash + \Input iOutLen - length of output + \Input *pInput - remaining input data to hash + \Input iInpLen - total size of input data (not remaining) + \Input h1 - 1st 64bit word of 128bit state + \Input h2 - 2nd 64bit word of 128bit state + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static void _MurmurHash3Finalize(uint8_t *pOutput, int32_t iOutLen, const uint8_t *pInput, int32_t iInpLen, uint64_t h1, uint64_t h2) +{ + const uint64_t c1 = MURMURHASH3_C1; + const uint64_t c2 = MURMURHASH3_C2; + uint64_t k1 = 0; + uint64_t k2 = 0; + + // hash remaining unaligned data + switch(iInpLen&15) + { + case 15: k2 ^= ((uint64_t)pInput[14]) << 48; + case 14: k2 ^= ((uint64_t)pInput[13]) << 40; + case 13: k2 ^= ((uint64_t)pInput[12]) << 32; + case 12: k2 ^= ((uint64_t)pInput[11]) << 24; + case 11: k2 ^= ((uint64_t)pInput[10]) << 16; + case 10: k2 ^= ((uint64_t)pInput[ 9]) << 8; + case 9: k2 ^= ((uint64_t)pInput[ 8]) << 0; + k2 *= c2; k2 = _MurmurHash3Rotl64(k2,33); k2 *= c1; h2 ^= k2; + + case 8: k1 ^= ((uint64_t)pInput[ 7]) << 56; + case 7: k1 ^= ((uint64_t)pInput[ 6]) << 48; + case 6: k1 ^= ((uint64_t)pInput[ 5]) << 40; + case 5: k1 ^= ((uint64_t)pInput[ 4]) << 32; + case 4: k1 ^= ((uint64_t)pInput[ 3]) << 24; + case 3: k1 ^= ((uint64_t)pInput[ 2]) << 16; + case 2: k1 ^= ((uint64_t)pInput[ 1]) << 8; + case 1: k1 ^= ((uint64_t)pInput[ 0]) << 0; + k1 *= c1; k1 = _MurmurHash3Rotl64(k1,31); k1 *= c2; h1 ^= k1; + }; + + // finalization + h1 ^= iInpLen; + h2 ^= iInpLen; + + h1 += h2; + h2 += h1; + + h1 = _MurmurHash3Fmix(h1); + h2 = _MurmurHash3Fmix(h2); + + h1 += h2; + h2 += h1; + + // write hash to output + switch(iOutLen) + { + case 16: pOutput[15] = (uint8_t)(h2 >> 56); + case 15: pOutput[14] = (uint8_t)(h2 >> 48); + case 14: pOutput[13] = (uint8_t)(h2 >> 40); + case 13: pOutput[12] = (uint8_t)(h2 >> 32); + case 12: pOutput[11] = (uint8_t)(h2 >> 24); + case 11: pOutput[10] = (uint8_t)(h2 >> 16); + case 10: pOutput[9] = (uint8_t)(h2 >> 8); + case 9: pOutput[8] = (uint8_t)(h2); + case 8: pOutput[7] = (uint8_t)(h2 >> 56); + case 7: pOutput[6] = (uint8_t)(h1 >> 48); + case 6: pOutput[5] = (uint8_t)(h1 >> 40); + case 5: pOutput[4] = (uint8_t)(h1 >> 32); + case 4: pOutput[3] = (uint8_t)(h1 >> 24); + case 3: pOutput[2] = (uint8_t)(h1 >> 16); + case 2: pOutput[1] = (uint8_t)(h1 >> 8); + case 1: pOutput[0] = (uint8_t)(h1); + }; +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function MurmurHash3Init + + \Description + Init the MurmurHash3 context + + \Input *pContext - hash context + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void MurmurHash3Init(MurmurHash3T *pContext) +{ + ds_memclr(pContext, sizeof(*pContext)); + // borrowed init vector from MD5 + pContext->aState[0] = 0x67452301efcdab89; + pContext->aState[1] = 0x98badcfe10325476; +} + +/*F********************************************************************************/ +/*! + \Function MurmurHash3Init2 + + \Description + Init the MurmurHash3 context, with hash size + + \Input *pContext - hash context + \Input iHashSize - hash size (may be 32 or 128) + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void MurmurHash3Init2(MurmurHash3T *pContext, int32_t iHashSize) +{ + MurmurHash3Init(pContext); +} + +/*F********************************************************************************/ +/*! + \Function MurmurHash3Update + + \Description + Add data to the MurmurHash3 context (hash the data) + + \Input *pContext - hash context + \Input *pBuffer - input data to hash + \Input iLength - length of data to hash + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void MurmurHash3Update(MurmurHash3T *pContext, const void *pBuffer, int32_t iLength) +{ + const uint8_t *pInput = (const uint8_t *)pBuffer; + const uint64_t c1 = MURMURHASH3_C1; + const uint64_t c2 = MURMURHASH3_C2; + int32_t iCount, iAdd; + uint64_t h1, h2, k1, k2; + + // get index into block buffer + iCount = pContext->iCount&15; + pContext->iCount += iLength; + + // see if we need to append to existing data + if (iCount > 0) + { + // figure out number to fill block + iAdd = 16-iCount; + + // if less than a full block + if (iLength < iAdd) + { + ds_memcpy(pContext->aData+iCount, pInput, iLength); + return; + } + + // finish off the block and transform + ds_memcpy(pContext->aData+iCount, pInput, iAdd); + pInput += iAdd; + iLength -= iAdd; + _MurmurHash3Transform(pContext); + } + + // get state & 64-bit data pointer + h1 = pContext->aState[0]; + h2 = pContext->aState[1]; + + // hash 128 bit blocks of data; inline for speed + for ( ; iLength >= 16; iLength -= 16, pInput += 16) + { + k1 = _MurmurHash3Read64((const uint64_t *)pInput, 0); + k2 = _MurmurHash3Read64((const uint64_t *)pInput, 1); + + k1 *= c1; k1 = _MurmurHash3Rotl64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = _MurmurHash3Rotl64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = _MurmurHash3Rotl64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = _MurmurHash3Rotl64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + // update state + pContext->aState[0] = h1; + pContext->aState[1] = h2; + + // copy partial block data, if any, into state + if (iLength > 0) + { + ds_memcpy_s(pContext->aData, sizeof(pContext->aData), pInput, iLength); + } +} + +/*F********************************************************************************/ +/*! + \Function MurmurHash3Final + + \Description + Convert MurmurHash3 state into final output form + + \Input *pContext - hash context + \Input *pBuffer - [out] buffer to hold output + \Input iLength - length of output buffer + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void MurmurHash3Final(MurmurHash3T *pContext, void *pBuffer, int32_t iLength) +{ + _MurmurHash3Finalize((uint8_t *)pBuffer, iLength, pContext->aData, pContext->iCount, pContext->aState[0], pContext->aState[1]); +} + +/*F********************************************************************************/ +/*! + \Function MurmurHash3 + + \Description + Generate 128-bit MurmurHash3 of specified input data + + \Input *_pOutput - [out] buffer to hold output + \Input iOutLen - length of output buffer + \Input *_pInput - input data to be hashed + \Input iInpLen - length of input + \Input *pKey - key to init hash + \Input iKeyLen - length of key (must be 16) + + \Todo + Support key lengths other than 16 + + \Version 03/07/2014 (jbrookes) +*/ +/********************************************************************************F*/ +void MurmurHash3(void *_pOutput, int32_t iOutLen, const void *_pInput, int32_t iInpLen, const void *pKey, int32_t iKeyLen) +{ + const uint8_t *pInput = (const uint8_t *)_pInput; + uint8_t *pOutput = (uint8_t *)_pOutput; + int32_t iBlock, iNumBlocks = iInpLen/16; + const uint64_t c1 = MURMURHASH3_C1; + const uint64_t c2 = MURMURHASH3_C2; + uint64_t h1, h2, k1, k2; + const uint64_t *pInput64; + + // get state (seed) + h1 = _MurmurHash3Read64((const uint64_t *)pKey, 0); + h2 = _MurmurHash3Read64((const uint64_t *)pKey, 1); + + // get 64-bit pointer to input + pInput64 = (const uint64_t *)pInput; + + // hash the 128 bit blocks of data + for (iBlock = 0; iBlock < iNumBlocks; iBlock += 1) + { + k1 = _MurmurHash3Read64(pInput64, iBlock*2+0); + k2 = _MurmurHash3Read64(pInput64, iBlock*2+1); + + k1 *= c1; k1 = _MurmurHash3Rotl64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = _MurmurHash3Rotl64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = _MurmurHash3Rotl64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = _MurmurHash3Rotl64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + // hash remaining data and output hash + _MurmurHash3Finalize(pOutput, iOutLen, pInput + iNumBlocks*16, iInpLen, h1, h2); +} + + + + + + + diff --git a/r5dev/thirdparty/dirtysdk/source/util/protobufcommon.c b/r5dev/thirdparty/dirtysdk/source/util/protobufcommon.c new file mode 100644 index 00000000..b586c406 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/protobufcommon.c @@ -0,0 +1,93 @@ +/*H*************************************************************************************/ +/*! + \File protobufcommon.h + + \Description + Shared definitions and functionality for the protobuf encoder / decoder + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +/*** Include Files *********************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/util/protobufcommon.h" + +/*** Varibles **************************************************************************/ + +//! strings representation for debugging +static const char *_ProtobufCommon_aTypes[] = +{ + "PROTOBUF_TYPE_VARINT", + "PROTOBUF_TYPE_64BIT", + "PROTOBUF_TYPE_LENGTH_DELIMITED", + "PROTOBUF_TYPE_START_GROUP", + "PROTOBUF_TYPE_END_GROUP", + "PROTOBUF_TYPE_32BIT" +}; + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function ProtobufCommonReadSize + + \Description + Reads the encoded size of the message from the buffer + + \Input *pBuffer - buffer we are using to read + \Input iBufLen - size of the buffer + \Input *pResult - [out] size of the message + + \Output + const uint8_t * - location past the encoded size or NULL if iBufLen is less than + 4 bytes or the size of the message + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufCommonReadSize(const uint8_t *pBuffer, int32_t iBufLen, int32_t *pResult) +{ + *pResult = 0; + + /* if the full size of the buffer is less than the size + we cannot read the message size */ + if (iBufLen < 4) + { + return(pBuffer); + } + iBufLen -= 4; + + *pResult |= (*pBuffer++ << 24); + *pResult |= (*pBuffer++ << 16); + *pResult |= (*pBuffer++ << 8); + *pResult |= (*pBuffer++ << 0); + + // make sure that the size of the message isn't larger than the buffer + return((*pResult <= iBufLen) ? pBuffer : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufCommonGetType + + \Description + Converts the type to its string representation + + \Input eType - type we are converting + + \Output + const char * - string representation or empty string if no mapping found + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const char *ProtobufCommonGetType(ProtobufTypeE eType) +{ + return((eType >= PROTOBUF_TYPE_VARINT) && (eType <= PROTOBUF_TYPE_32BIT) ? _ProtobufCommon_aTypes[eType] : ""); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/util/protobufread.c b/r5dev/thirdparty/dirtysdk/source/util/protobufread.c new file mode 100644 index 00000000..22e07111 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/protobufread.c @@ -0,0 +1,927 @@ +/*H*************************************************************************************/ +/*! + \File protobufread.c + + \Description + Implementation of decoder for the Google Protobuf wire format + See: https://developers.google.com/protocol-buffers/docs/encoding + + This only supports protobuf version 3, if any lesser versions are used + the result is undefined. + + \Copyright + Copyright (c) Electronic Arts 2017-2018. ALL RIGHTS RESERVED. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +/*** Includes **************************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/util/protobufcommon.h" +#include "DirtySDK/util/protobufread.h" + +/*** Macros ****************************************************************************/ + +// helper to read the header information and return the location of the first element +#define PROTOBUF_ReadPackedRepeated(pState, pCurrent, pRepeated) ((ProtobufReadMessage((pState), (pCurrent), (pRepeated)) != NULL) ? (pRepeated)->pBuffer : NULL) + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function _ProtobufReadVarint + + \Description + Reads varint data from the buffer + + \Input *pBuffer - buffer we are reading from + \Input iBufLen - length of the buffer + \Input *pValue - [out] data pulled from the buffer + + \Output + int32_t - amount of bytes read from the buffer + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static int32_t _ProtobufReadVarint(const uint8_t *pBuffer, int32_t iBufLen, uint64_t *pValue) +{ + int32_t iRead = 0; + uint8_t uMSB = 0, uByte; + + // initialize the value to zero + if (pValue != NULL) + { + *pValue = 0; + } + + // sanity check + if ((pBuffer == NULL) || (iBufLen <= 0)) + { + return(0); + } + + // read until you decode the full integer or reach the end of the buffer + do + { + uByte = pBuffer[iRead++]; + if (pValue != NULL) + { + *pValue += (uByte & 0x7f) * ((uint64_t)1 << uMSB); + } + uMSB += 7; + } while (((uByte & 0x80) == 0x80) && (iRead < iBufLen)); + + return(iRead); +} + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadInit + + \Description + Initializes the reading structure based on buffer + + \Input *pState - state we are initializing + \Input *pBuffer - buffer we are using to read + \Input iBufLen - size of the buffer + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +void ProtobufReadInit(ProtobufReadT *pState, const uint8_t *pBuffer, int32_t iBufLen) +{ + ds_memclr(pState, sizeof(*pState)); + pState->pBuffer = pBuffer; + pState->pBufEnd = pBuffer+iBufLen; +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadFind + + \Description + Finds a field in the buffer + + \Input *pState - reader state + \Input uField - field we are looking for + + \Output + const uint8_t * - pointer to the location of the field or NULL if not found + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadFind(const ProtobufReadT *pState, uint32_t uField) +{ + const uint8_t *pBuffer = pState->pBuffer; + + for (;;) + { + uint32_t uField2; + uint8_t uType; + + // read the tag information, if we didn't read anything then we are done + if ((pBuffer = ProtobufReadHeader(pState, pBuffer, &uField2, &uType)) == NULL) + { + break; + } + + // check to see if it is the field you are looking for + if (uField == uField2) + { + return(pBuffer); + } + // try to read past + else if ((pBuffer = ProtobufReadSkip(pState, pBuffer, uType)) == NULL) + { + break; + } + } + return(NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadFind2 + + \Description + Finds a field in the buffer allowing to continue from a point + + \Input *pState - reader state + \Input uField - field we are looking for + \Input *pCurrent - where to continue from or NULL if to start from the beginning + + \Output + const uint8_t * - pointer to the location of the field or NULL if not found / end + of buffer + + \Notes + This function is useful when reading repeated length delimited types (not packed). + Since the reading happens procedurally, we need to continue finding repeated elements + past the previous one that was read. + + For this reason, when passing data into pCurrent make sure this is past the element + that you have read otherwise parsing will fail. + + \code{.c} + char strText[256]; + const uint8_t *pCurrent = NULL; + + while ((pCurrent = ProtobufReadString(&Read, ProtobufReadFind2(&Read, 1, pCurrent), strText, sizeof(strText))) != NULL) + { + ... + } + \endcode + + \Version 12/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadFind2(const ProtobufReadT *pState, uint32_t uField, const uint8_t *pCurrent) +{ + // assign the buffer to the beginning if we are not continuing + if (pCurrent == NULL) + { + pCurrent = pState->pBuffer; + } + + for (;;) + { + uint32_t uField2; + uint8_t uType; + + // read the tag information, if we didn't read anything then we are done + if ((pCurrent = ProtobufReadHeader(pState, pCurrent, &uField2, &uType)) == NULL) + { + break; + } + + // check to see if it is the field you are looking for + if (uField == uField2) + { + return(pCurrent); + } + + // try read past + if ((pCurrent = ProtobufReadSkip(pState, pCurrent, uType)) == NULL) + { + break; + } + } + return(NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadHeader + + \Description + Reads the tag header information from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pField - [out] field identifier + \Input *pType - [out] type identifier (equates to ProtobufTypeE) + + \Output + const uint8_t * - pointer to the location past the tag header or NULL if the end + + \Notes + This function is for more advanced use where speed in critical to prevent + scanning the entire buffer when using ProtobufReadFind. When in doubt do + not use this and ProtobufReadSkip directly. + + \code{.c} + uint32_t uField; + uint8_t uType; + const uint8_t *pCurrent = Read.pBuffer; + + while ((pCurrent = ProtobufReadHeader(&Read, pCurrent, &uField, &uType)) != NULL) + { + // check for correct field / type combo you expect, for instance in a simple case + // note: normally you would check all your known type/field combos + if ((uType == PROTOBUF_TYPE_VARINT) && (uField == 1)) + { + uint64_t uResult; + pCurrent = ProtobufReadVarint2(&Read, pCurrent, &uResult); + } + // otherwise skip + else + { + pCurrent = ProtobufReadSkip(&Read, pCurrent, uType); + } + } + \endcode + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadHeader(const ProtobufReadT *pState, const uint8_t *pCurrent, uint32_t *pField, uint8_t *pType) +{ + int32_t iRead; + uint64_t uTag; + + // read the tag information, if we didn't read anything then we have reached the end + if ((iRead = _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), &uTag)) <= 0) + { + return(NULL); + } + pCurrent += iRead; + + // parse the tag information and return current location + *pField = (uint32_t)(uTag >> 3); + *pType = uTag & 7; + return(pCurrent); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadSkip + + \Description + Read over an element in the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input uType - type identifier (equates to ProtobufTypeE) + + \Output + const uint8_t * - location past the element or NULL if unhandled type / end of buffer + + \Notes + See notes in ProtobufReadHeader + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadSkip(const ProtobufReadT *pState, const uint8_t *pCurrent, uint8_t uType) +{ + switch (uType) + { + case PROTOBUF_TYPE_VARINT: + { + pCurrent += _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), NULL); + break; + } + + case PROTOBUF_TYPE_64BIT: + { + pCurrent += DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), 8); + break; + } + + case PROTOBUF_TYPE_LENGTH_DELIMITED: + { + uint64_t uValue; + pCurrent += _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), &uValue); + pCurrent += DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), uValue); + break; + } + + case PROTOBUF_TYPE_32BIT: + { + pCurrent += DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), 4); + break; + } + + case PROTOBUF_TYPE_START_GROUP: + case PROTOBUF_TYPE_END_GROUP: + default: + // not sure how to read these so return an error so we can stop trying to read + NetPrintf(("protobufreader: found '%s' type that we cannot handle\n", ProtobufCommonGetType(uType))); + return(NULL); + } + return(pCurrent); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadVarint + + \Description + Reads a varint from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + + \Output + uint64_t - value read from the buffer, zero if pCurrent is NULL + + \Notes + This function is different than many of the other read functions where the output + is a pointer output parameter, due to the nature of the varint we read it out in + an uint64_t. Since this will cause some annoyances in using it for most cases we + opted to use a special function in this case. This version of the function will + not work when reading repeated fields so please use ProtobufReadVarint2 in that + case. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +uint64_t ProtobufReadVarint(const ProtobufReadT *pState, const uint8_t *pCurrent) +{ + uint64_t uResult; + _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), &uResult); + return(uResult); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadVarint2 + + \Description + Reads a varint from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pResult - [out] result of reading + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadVarint2(const ProtobufReadT *pState, const uint8_t *pCurrent, uint64_t *pResult) +{ + int32_t iRead = _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), pResult); + return((iRead > 0) ? (pCurrent+iRead) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadRepeatedVarint + + \Description + Reads a repeated varint from the buffer into an array + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pResult - [out] output array + \Input iCount - number of elements that can be written + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + If you need to figure out the amount of elements for the repeated field, you + can call the ProtobufReadNumRepeatedElements function. Otherwise, you should + pass in an array large enough to hold the elements you expect. + + \Version 01/02/2018 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadRepeatedVarint(const ProtobufReadT *pState, const uint8_t *pCurrent, uint64_t *pResult, int32_t iCount) +{ + ProtobufReadT Field; + int32_t iIndex = 0; + ds_memclr(pResult, sizeof(*pResult) * iCount); + + // read the header for the packed repeated field + if ((pCurrent = PROTOBUF_ReadPackedRepeated(pState, pCurrent, &Field)) == NULL) + { + return(pCurrent); + } + // write as many elements as possible from the buffer + while ((pCurrent = ProtobufReadVarint2(&Field, pCurrent, (iIndex < iCount) ? &pResult[iIndex++] : NULL)) != NULL) + ; + return((Field.pBufEnd < pState->pBufEnd) ? (Field.pBufEnd+1) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadSint32 + + \Description + Reads a sint32 from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pResult - [out] result of reading + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadSint32(const ProtobufReadT *pState, const uint8_t *pCurrent, int32_t *pResult) +{ + uint64_t uResult; + pCurrent = ProtobufReadVarint2(pState, pCurrent, &uResult); + *pResult = (int32_t)(((uResult >> 1) ^ -(int32_t)(uResult & 1))); + return(pCurrent); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadRepeatedSint32 + + \Description + Reads a repeated varint sint32 from the buffer into an array + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pResult - [out] output array + \Input iCount - number of elements that can be written + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + If you need to figure out the amount of elements for the repeated field, you + can call the ProtobufReadNumRepeatedElements function. Otherwise, you should + pass in an array large enough to hold the elements you expect. + + \Version 01/02/2018 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadRepeatedSint32(const ProtobufReadT *pState, const uint8_t *pCurrent, int32_t *pResult, int32_t iCount) +{ + ProtobufReadT Field; + int32_t iIndex = 0; + ds_memclr(pResult, sizeof(*pResult) * iCount); + + // read the header for the packed repeated field + if ((pCurrent = PROTOBUF_ReadPackedRepeated(pState, pCurrent, &Field)) == NULL) + { + return(pCurrent); + } + // write as many elements as possible from the buffer + while ((pCurrent = ProtobufReadSint32(&Field, pCurrent, (iIndex < iCount) ? &pResult[iIndex++] : NULL)) != NULL) + ; + return((Field.pBufEnd < pState->pBufEnd) ? (Field.pBufEnd+1) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadSint64 + + \Description + Reads a sint64 from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pResult - [out] result of reading + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadSint64(const ProtobufReadT *pState, const uint8_t *pCurrent, int64_t *pResult) +{ + uint64_t uResult; + pCurrent = ProtobufReadVarint2(pState, pCurrent, &uResult); + *pResult = ((uResult >> 1) ^ -(int64_t)(uResult & 1)); + return(pCurrent); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadRepeatedSint64 + + \Description + Reads a repeated varint sint64 from the buffer into an array + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pResult - [out] output array + \Input iCount - number of elements that can be written + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + If you need to figure out the amount of elements for the repeated field, you + can call the ProtobufReadNumRepeatedElements function. Otherwise, you should + pass in an array large enough to hold the elements you expect. + + \Version 01/02/2018 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadRepeatedSint64(const ProtobufReadT *pState, const uint8_t *pCurrent, int64_t *pResult, int32_t iCount) +{ + ProtobufReadT Field; + int32_t iIndex = 0; + ds_memclr(pResult, sizeof(*pResult) * iCount); + + // read the header for the packed repeated field + if ((pCurrent = PROTOBUF_ReadPackedRepeated(pState, pCurrent, &Field)) == NULL) + { + return(pCurrent); + } + // write as many elements as possible from the buffer + while ((pCurrent = ProtobufReadSint64(&Field, pCurrent, (iIndex < iCount) ? &pResult[iIndex++] : NULL)) != NULL) + ; + return((Field.pBufEnd < pState->pBufEnd) ? (Field.pBufEnd+1) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadFixed32 + + \Description + Reads a fixed32, float or sfixed32 from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pOutput - [out] output of reading + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + We use void * for reading to allow you to pass in different 32 bit types, if the + wrong type is used a max of 4 bytes is always read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadFixed32(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput) +{ + uint32_t uLength = (pCurrent != NULL) ? DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), 4) : 0; + if (pOutput != NULL) + { + ds_memcpy_s(pOutput, 4, pCurrent, uLength); + } + return((uLength > 0) ? (pCurrent+uLength) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadRepeatedFixed32 + + \Description + Reads a repeated fixed32, float or sfixed32 from the buffer into an array + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pOutput - [out] output array + \Input iOutLen - size of the output + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + Since fixed types can be read as a block, we can memcpy the entire repeated field + straight into the array. To achieve this you need to make sure to pass in the size + in bytes of the array and not the number of elements. + + If you need to figure out the amount of elements for the repeated field, you + can call the ProtobufReadNumRepeatedElements function. Otherwise, you should + pass in an array large enough to hold the elements you expect. + + \Version 01/02/2018 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadRepeatedFixed32(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput, int32_t iOutLen) +{ + ProtobufReadT Field; + + // sanity check to try to weed out incorrect usage + if ((iOutLen % 4) != 0) + { + NetPrintf(("protobufwrite: [%p] fixed64 repeated output length is not a multiple of 8, please check to make sure you are sending size instead of count\n", pState)); + return(NULL); + } + ds_memclr(pOutput, iOutLen); + + // read the header for the packed repeated field + if ((pCurrent = PROTOBUF_ReadPackedRepeated(pState, pCurrent, &Field)) == NULL) + { + return(pCurrent); + } + + // write as many elements as possible from the buffer + ds_memcpy_s(pOutput, iOutLen, pCurrent, (uint32_t)(Field.pBufEnd-pCurrent)); + return((Field.pBufEnd < pState->pBufEnd) ? (Field.pBufEnd+1) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadFixed64 + + \Description + Reads a fixed64, double or sfixed64 from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pOutput - [out] output of reading + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + We use void * for reading to allow you to pass in different 64 bit types, if the + wrong type is used a max of 8 bytes is always read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadFixed64(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput) +{ + uint32_t uLength = (pCurrent != NULL) ? DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), 8) : 0; + if (pOutput != NULL) + { + ds_memcpy_s(pOutput, 8, pCurrent, uLength); + } + return((uLength > 0) ? (pCurrent+uLength) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadRepeatedFixed64 + + \Description + Reads a repeated fixed64, double or sfixed64 from the buffer into an array + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pOutput - [out] output array + \Input iOutLen - size of the output + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + Since fixed types can be read as a block, we can memcpy the entire repeated field + straight into the array. To achieve this you need to make sure to pass in the size + in bytes of the array and not the number of elements. + + If you need to figure out the amount of elements for the repeated field, you + can call the ProtobufReadNumRepeatedElements function. Otherwise, you should + pass in an array large enough to hold the elements you expect. + + \Version 01/02/2018 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadRepeatedFixed64(const ProtobufReadT *pState, const uint8_t *pCurrent, void *pOutput, int32_t iOutLen) +{ + ProtobufReadT Field; + + // sanity check to try to weed out incorrect usage + if ((iOutLen % 8) != 0) + { + NetPrintf(("protobufwrite: [%p] fixed64 repeated output length is not a multiple of 8, please check to make sure you are sending size instead of count\n", pState)); + return(NULL); + } + ds_memclr(pOutput, iOutLen); + + // read the header for the packed repeated field + if ((pCurrent = PROTOBUF_ReadPackedRepeated(pState, pCurrent, &Field)) == NULL) + { + return(pCurrent); + } + + // write as many elements as possible from the buffer + ds_memcpy_s(pOutput, iOutLen, pCurrent, (uint32_t)(Field.pBufEnd-pCurrent)); + return((Field.pBufEnd < pState->pBufEnd) ? (Field.pBufEnd+1) : NULL); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadBytes + + \Description + Reads the bytes type from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pOutput - [out] output of reading + \Input iOutLen - size of the output + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadBytes(const ProtobufReadT *pState, const uint8_t *pCurrent, uint8_t *pOutput, int32_t iOutLen) +{ + uint64_t uLength; + int32_t iRead; + + // parse the length and update to not pass the end + if ((iRead = _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), &uLength)) <= 0) + { + if (pOutput != NULL) + { + ds_memclr(pOutput, iOutLen); + } + return(NULL); + } + pCurrent += iRead; + uLength = DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), uLength); + + ds_memcpy_s(pOutput, iOutLen, pCurrent, (int32_t)(uLength)); + return(pCurrent+uLength); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadString + + \Description + Reads a string from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pOutput - [out] output of reading + \Input iOutLen - size of the output + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadString(const ProtobufReadT *pState, const uint8_t *pCurrent, char *pOutput, int32_t iOutLen) +{ + uint64_t uLength; + int32_t iRead; + + // parse the length and update to not pass the end + if ((iRead = _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), &uLength)) <= 0) + { + if (pOutput != NULL) + { + *pOutput = '\0'; + } + return(NULL); + } + pCurrent += iRead; + uLength = DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), uLength); + + ds_strsubzcpy(pOutput, iOutLen, (const char *)pCurrent, (int32_t)(uLength)); + return(pCurrent+uLength); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadMessage + + \Description + Reads an embedded message from the buffer + + \Input *pState - reader state + \Input *pCurrent - location in the buffer or NULL + \Input *pMsg - [out] new reader state used for reading embedded message + + \Output + const uint8_t * - location past the element in the buffer or NULL if end is reached + + \Notes + The result of this function is only important when reading repeated fields where + we want to stop reading when there are no more elements in the field to read. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +const uint8_t *ProtobufReadMessage(const ProtobufReadT *pState, const uint8_t *pCurrent, ProtobufReadT *pMsg) +{ + uint64_t uLength; + int32_t iRead; + + if ((iRead = _ProtobufReadVarint(pCurrent, (int32_t)(pState->pBufEnd-pCurrent), &uLength)) <= 0) + { + ProtobufReadInit(pMsg, NULL, 0); + return(NULL); + } + pCurrent += iRead; + + // update length to not read past the end + uLength = DS_MIN((uint32_t)(pState->pBufEnd-pCurrent), uLength); + + ProtobufReadInit(pMsg, pCurrent, (int32_t)(uLength)); + return(pCurrent+uLength); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufReadNumRepeatedElements + + \Description + Gets the number of elements in a repeated field + + \Input *pState - reader state + \Input uField - field identifier of the repeated field + \Input uType - types encoded in the repeated field (they are not sent over the wire) + + \Output + int32_t - number of elements in the repeated field + + \Notes + This function is available as an utility. If you don't need to get the number + of elements don't call it as it saves from relooping over the repeated field. + + \Version 08/14/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufReadNumRepeatedElements(const ProtobufReadT *pState, uint32_t uField, uint8_t uType) +{ + int32_t iNumElements = 0; + + if ((uType == PROTOBUF_TYPE_VARINT) || (uType == PROTOBUF_TYPE_64BIT) || (uType == PROTOBUF_TYPE_32BIT)) + { + ProtobufReadT Repeated; + const uint8_t *pCurrent = PROTOBUF_ReadPackedRepeated(pState, ProtobufReadFind(pState, uField), &Repeated); + + if (uType == PROTOBUF_TYPE_VARINT) + { + while ((pCurrent = ProtobufReadVarint2(&Repeated, pCurrent, NULL)) != NULL) + { + iNumElements += 1; + } + } + else if (uType == PROTOBUF_TYPE_64BIT) + { + iNumElements = (int32_t)(Repeated.pBufEnd-Repeated.pBuffer) / 8; + } + else if (uType == PROTOBUF_TYPE_32BIT) + { + iNumElements = (int32_t)(Repeated.pBufEnd-Repeated.pBuffer) / 4; + } + } + else if (uType == PROTOBUF_TYPE_LENGTH_DELIMITED) + { + const uint8_t *pCurrent = NULL; + while ((pCurrent = ProtobufReadFind2(pState, uField, pCurrent)) != NULL) + { + pCurrent = ProtobufReadSkip(pState, pCurrent, uType); + iNumElements += 1; + } + } + + return(iNumElements); +} diff --git a/r5dev/thirdparty/dirtysdk/source/util/protobufwrite.c b/r5dev/thirdparty/dirtysdk/source/util/protobufwrite.c new file mode 100644 index 00000000..be4d4835 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/protobufwrite.c @@ -0,0 +1,684 @@ +/*H*************************************************************************************/ +/*! + \File protobufwrite.c + + \Description + Implementation of encoder for the Google Protobuf wire format + See: https://developers.google.com/protocol-buffers/docs/encoding + + \Copyright + Copyright (c) Electronic Arts 2017-2018. ALL RIGHTS RESERVED. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************H*/ + +/*** Includes **************************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/util/protobufcommon.h" +#include "DirtySDK/util/protobufwrite.h" + +/*** Defines ***************************************************************************/ + +//! maximum octets used to encode a varint +#define PROTOBUF_VARINT_MAX (10) + +//! max number of levels that we can embed messages +#define PROTOBUF_MAX_EMBEDDED_MSG (100) + +/*** Macros ****************************************************************************/ + +//! encodes the field number and type into a tag +#define PROTOBUF_MakeTag(uField, uType) (((uField) << 3) | (uType)) + +/*** Type Definitions ******************************************************************/ + +//! module state +struct ProtobufWriteRefT +{ + uint8_t *pBuffer; //!< pointer to the start of the buffer + int32_t iBufLen; //!< length of the buffer + int32_t iBufOff; //!< offset into the buffer + + int32_t iMemGroup; //!< memory group identifier + void *pMemGroupUserdata; //!< memory group data + + uint8_t *aMessagesBegin[PROTOBUF_MAX_EMBEDDED_MSG]; //!< stack of pointers to where each encoded messages begin + int32_t iNumMessages; //!< current number of messages we are embedding + + uint8_t bEncodeSize; //!< should we encode the size of the message at the beginning of the buffer? (needed for grpc) + uint8_t _pad[3]; +}; + +/*** Variables *************************************************************************/ + +// variable to test if something is set +static const uint8_t _aEmptyBuffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +/*** Private Functions *****************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function _ProtobufWriteVarint + + \Description + Write a varint type to the buffer + + \Input uValue - varint value to write + \Input *pBuffer - [out] destination we are writing to + \Input iBufLen - size of the output destination + \Input *pError - [out] where we write the result of the operation + + \Output + int32_t - amount of bytes written to the buffer + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static int32_t _ProtobufWriteVarint(uint64_t uValue, uint8_t *pBuffer, int32_t iBufLen, int32_t *pError) +{ + int32_t iWrite; + + /* make we can at least write 1 byte for the cases + where we can fit it in one octet */ + if (iBufLen <= 0) + { + *pError = PROTOBUFWRITE_ERROR_FULL; + return(0); + } + + for (iWrite = 0; uValue >= 0x80; iWrite += 1) + { + // make sure we have space left + if (iWrite >= (iBufLen - 1)) + { + *pError = PROTOBUFWRITE_ERROR_FULL; + return(0); + } + + // set the lsb of the value and set the msb as a continuation flag + pBuffer[iWrite] = (uValue % 0x80) + 0x80; + uValue /= 0x80; + } + pBuffer[iWrite++] = (uint8_t)uValue; + + return(iWrite); +} + +/*F*************************************************************************************/ +/*! + \Function _ProtobufMemcpy + + \Description + Copies data to the buffer + + \Input *pDst - [out] pointer to output + \Input iDstLen - size of output + \Input *pSrc - pointer to input + \Input iSrcLen - size of input + \Input *pError - [out] where we write the result of the operation + + \Output + int32_t - amount of bytes written to the buffer + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +static int32_t _ProtobufMemcpy(void *pDst, int32_t iDstLen, const void *pSrc, int32_t iSrcLen, int32_t *pError) +{ + if ((iDstLen = DS_MIN(iDstLen, iSrcLen)) > 0) + { + ds_memcpy(pDst, pSrc, iDstLen); + } + else + { + *pError = PROTOBUFWRITE_ERROR_FULL; + } + return(iDstLen); +} + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteCreate + + \Description + Create and initialize the writer module ref + + \Input *pBuffer - where we are writing to + \Input iBufLen - the size of the buffer we are writing to + \Input bEncodeSize - encode the size of the message at the beginning of the buffer? + + \Output + ProtobufWriteRefT * - pointer to module ref or NULL if failure + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +ProtobufWriteRefT *ProtobufWriteCreate(uint8_t *pBuffer, int32_t iBufLen, uint8_t bEncodeSize) +{ + ProtobufWriteRefT *pState; + int32_t iMemGroup; + void *pMemGroupUserdata; + + // query memory group information + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserdata); + + // allocate module state + if ((pState = (ProtobufWriteRefT *)DirtyMemAlloc(sizeof(*pState), PROTOBUF_MEMID, iMemGroup, pMemGroupUserdata)) == NULL) + { + NetPrintf(("protobufwriter: cannot allocate writer module state\n")); + return(NULL); + } + ds_memclr(pState, sizeof(*pState)); + pState->pBuffer = pBuffer; + pState->iBufLen = iBufLen; + pState->iMemGroup = iMemGroup; + pState->pMemGroupUserdata = pMemGroupUserdata; + + // assign the encode size setting, if true save space at the beginning for encoding size + if ((pState->bEncodeSize = bEncodeSize) == TRUE) + { + pState->iBufOff += 4; + } + + return(pState); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteVarint + + \Description + Write the non-zigzag encoded (sint32, sint64) versions of the varint types + + \Input *pState - module state + \Input uValue - value we are writing + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Notes + This function supports the following types: int32, uint32, uint64, bool and + enum. + + If you pass in 0 for uField, nothing will be written to the buffer for the tag + which you only want to do when writing packed repeated fields. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteVarint(ProtobufWriteRefT *pState, uint64_t uValue, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + + // don't write the defaults to save space when not in a repeated field + if ((uValue != 0) || (uField == 0)) + { + if (uField > 0) + { + pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_VARINT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + } + pState->iBufOff += _ProtobufWriteVarint(uValue, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + } + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteRepeatedVarint + + \Description + Write the non-zigzag encoded (sint32, sint64) versions of the varint types + repeated + + \Input *pState - module state + \Input *pValues - array of values we are writing + \Input iCount - number of entries in the array + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Notes + This function supports the following types: int32, uint32, uint64, bool and + enum. + + \Version 12/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteRepeatedVarint(ProtobufWriteRefT *pState, uint64_t *pValues, int32_t iCount, uint32_t uField) +{ + int32_t iIndex, iError; + + ProtobufWriteMessageBegin(pState, uField); + for (iIndex = 0, iError = PROTOBUFWRITE_ERROR_OK; (iIndex < iCount) && (iError == PROTOBUFWRITE_ERROR_OK); iIndex += 1) + { + iError = ProtobufWriteVarint(pState, pValues[iIndex], 0); + } + ProtobufWriteMessageEnd(pState); + + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteSint32 + + \Description + Write the zigzag encoded sint32 type + + \Input *pState - module state + \Input iValue - value we are writing + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Notes + If you pass in 0 for uField, nothing will be written for the tag + which you only want to do when writing packed repeated fields. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteSint32(ProtobufWriteRefT *pState, int32_t iValue, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + // zigzag encode the value + const uint64_t uValue = (iValue << 1) ^ (iValue >> 31); + + // don't write the defaults to save space when not in a repeated field + if ((uValue != 0) || (uField == 0)) + { + if (uField > 0) + { + pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_VARINT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + } + pState->iBufOff += _ProtobufWriteVarint(uValue, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + } + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteRepeatedSint32 + + \Description + Write the zigzag encoded sint32 type repeated + + \Input *pState - module state + \Input *pValues - array of values we are writing + \Input iCount - number of entries in the array + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Version 12/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteRepeatedSint32(ProtobufWriteRefT *pState, int32_t *pValues, int32_t iCount, uint32_t uField) +{ + int32_t iIndex, iError; + + ProtobufWriteMessageBegin(pState, uField); + for (iIndex = 0, iError = PROTOBUFWRITE_ERROR_OK; (iIndex < iCount) && (iError == PROTOBUFWRITE_ERROR_OK); iIndex += 1) + { + iError = ProtobufWriteSint32(pState, pValues[iIndex], 0); + } + ProtobufWriteMessageEnd(pState); + + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteSint64 + + \Description + Write the zigzag encoded sint64 type + + \Input *pState - module state + \Input iValue - value we are writing + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Notes + If you pass in 0 for uField, nothing will be written for the tag + which you only want to do when writing packed repeated fields. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteSint64(ProtobufWriteRefT *pState, int64_t iValue, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + // zigzag encode the value + const uint64_t uValue = (iValue << 1) ^ (iValue >> 63); + + // don't write the defaults to save space when not in a repeated field + if ((uValue != 0) || (uField == 0)) + { + if (uField > 0) + { + pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_VARINT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + } + pState->iBufOff += _ProtobufWriteVarint(uValue, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + } + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteRepeatedSint64 + + \Description + Write the zigzag encoded sint64 type repeated + + \Input *pState - module state + \Input *pValues - array of values we are writing + \Input iCount - number of entries in the array + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Version 12/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteRepeatedSint64(ProtobufWriteRefT *pState, int64_t *pValues, int32_t iCount, uint32_t uField) +{ + int32_t iIndex, iError; + + ProtobufWriteMessageBegin(pState, uField); + for (iIndex = 0, iError = PROTOBUFWRITE_ERROR_OK; (iIndex < iCount) && (iError == PROTOBUFWRITE_ERROR_OK); iIndex += 1) + { + iError = ProtobufWriteSint64(pState, pValues[iIndex], 0); + } + ProtobufWriteMessageEnd(pState); + + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteFixed32 + + \Description + Write the fixed32, float or sfixed32 type to the buffer + + \Input *pState - module state + \Input *pValue - value we are writing + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Notes + The fixed types are written in little endian so we use memcpy. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteFixed32(ProtobufWriteRefT *pState, const void *pValue, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + + // don't write the defaults to save space + if (memcmp(pValue, _aEmptyBuffer, 4) != 0) + { + pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_32BIT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pValue, 4, &iError); + } + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteRepeatedFixed32 + + \Description + Write the repeated fixed32, float or sfixed32 type to the buffer + + \Input *pState - module state + \Input *pInput - array of values we are writing + \Input iInpLen - size (in bytes) of the array + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Version 12/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteRepeatedFixed32(ProtobufWriteRefT *pState, const void *pInput, int32_t iInpLen, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + + ProtobufWriteMessageBegin(pState, uField); + pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pInput, iInpLen, &iError); + ProtobufWriteMessageEnd(pState); + + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteFixed64 + + \Description + Write the fixed64, double or sfixed64 type to the buffer + + \Input *pState - module state + \Input *pValue - value we are writing + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Notes + The fixed types are written in little endian so we use memcpy. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteFixed64(ProtobufWriteRefT *pState, const void *pValue, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + + // don't write the defaults to save space + if (memcmp(pValue, _aEmptyBuffer, 8) != 0) + { + pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_64BIT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pValue, 8, &iError); + } + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteRepeatedFixed64 + + \Description + Write the repeated fixed64, double or sfixed64 type to the buffer + + \Input *pState - module state + \Input *pInput - array of values we are writing + \Input iInpLen - size (in bytes) of the array + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Version 12/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteRepeatedFixed64(ProtobufWriteRefT *pState, const void *pInput, int32_t iInpLen, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + + ProtobufWriteMessageBegin(pState, uField); + pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pInput, iInpLen, &iError); + ProtobufWriteMessageEnd(pState); + + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteLengthDelimited + + \Description + Write the length delimited types + + \Input *pState - module state + \Input *pValue - value we are writing + \Input iLength - size of the value + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteLengthDelimited(ProtobufWriteRefT *pState, const void *pValue, int32_t iLength, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + + // don't write empty data to save space when not in a repeated field + if ((pValue != NULL) && (iLength > 0)) + { + pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_LENGTH_DELIMITED), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + pState->iBufOff += _ProtobufWriteVarint(iLength, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pValue, iLength, &iError); + } + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteMessageBegin + + \Description + Begin encoding an embedded message in place + + \Input *pState - module state + \Input uField - field identifier + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Notes + Embedded messages work like a stack (FILO) when nesting more messages. + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteMessageBegin(ProtobufWriteRefT *pState, uint32_t uField) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK; + + // make sure that we have not reached our maximum + if (pState->iNumMessages == PROTOBUF_MAX_EMBEDDED_MSG) + { + return(PROTOBUFWRITE_ERROR_EMBED); + } + + pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_LENGTH_DELIMITED), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError); + pState->aMessagesBegin[pState->iNumMessages++] = pState->pBuffer+pState->iBufOff; + + if ((pState->iBufLen-pState->iBufOff) > PROTOBUF_VARINT_MAX) + { + pState->iBufOff += PROTOBUF_VARINT_MAX; + } + else + { + iError = PROTOBUFWRITE_ERROR_FULL; + } + + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteMessageEnd + + \Description + Finish encoding an embedded message in place + + \Input *pState - module state + + \Output + int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteMessageEnd(ProtobufWriteRefT *pState) +{ + int32_t iError = PROTOBUFWRITE_ERROR_OK, iInpLen, iBufOff; + uint8_t *pMsgBegin; + + // make sure that we still have messages in the stack + if (pState->iNumMessages > 0) + { + pMsgBegin = pState->aMessagesBegin[--pState->iNumMessages]; + } + else + { + return(PROTOBUFWRITE_ERROR_EMBED); + } + + // calculate the size of the message and encode it to the buffer + iInpLen = (int32_t)((pState->pBuffer+pState->iBufOff)-pMsgBegin-PROTOBUF_VARINT_MAX); + iBufOff = _ProtobufWriteVarint(iInpLen, pMsgBegin, PROTOBUF_VARINT_MAX, &iError); + + // fill the gap (if any) + memmove(pMsgBegin+iBufOff, pMsgBegin+PROTOBUF_VARINT_MAX, iInpLen); + pState->iBufOff -= (PROTOBUF_VARINT_MAX-iBufOff); + + return(iError); +} + +/*F*************************************************************************************/ +/*! + \Function ProtobufWriteDestroy + + \Description + Finish writing to the buffer and return the amount of data written + + \Input *pState - module state + + \Output + int32_t - amount of bytes written to the buffer + + \Version 07/05/2017 (eesponda) +*/ +/*************************************************************************************F*/ +int32_t ProtobufWriteDestroy(ProtobufWriteRefT *pState) +{ + const int32_t iResult = pState->iBufOff; + + // encode the size if needed + if (pState->bEncodeSize == TRUE) + { + // calculate the size of the message without accounting for encoded size + const int32_t iMessageSize = SocketNtohl(iResult-4); + ds_memcpy(pState->pBuffer, (const void *)&iMessageSize, sizeof(iMessageSize)); + } + + DirtyMemFree(pState, PROTOBUF_MEMID, pState->iMemGroup, pState->pMemGroupUserdata); + return(iResult); +} diff --git a/r5dev/thirdparty/dirtysdk/source/util/utf8.c b/r5dev/thirdparty/dirtysdk/source/util/utf8.c new file mode 100644 index 00000000..f753da42 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/util/utf8.c @@ -0,0 +1,527 @@ +/*H*************************************************************************************************/ +/*! + + \File utf8.c + + \Description + This module implements routines for converting to and from UTF-8. + + \Notes + This code only decodes the first three octets of UTF-8, thus it only handles UCS-2 codes, + not UCS-4 codes. It also does not handle UTF-16 (and surrogate pairs), and is therefore + limited to encoding to/decoding from the basic reference plane. + + Helpful references: + + http://www.utf-8.com/ - links + http://www.cis.ohio-state.edu/cgi-bin/rfc/rfc2279.html - RFC 2279 + http://www.unicode.org/charts/ - UNICODE character charts + http://www-106.ibm.com/developerworks/library/utfencodingforms/ - UNICODE primer + http://www.columbia.edu/kermit/utf8.html - UTF-8 samples + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2003. ALL RIGHTS RESERVED. + + \Version 1.0 03/25/03 (JLB) First version. + +*/ +/*************************************************************************************************H*/ + + +/*** Include files *********************************************************************/ + +#include "DirtySDK/util/utf8.h" + +/*** Defines ***************************************************************************/ + +/*** Macros ****************************************************************************/ + +/*** Type Definitions ******************************************************************/ + +/*** Function Prototypes ***************************************************************/ + +/*** Variables *************************************************************************/ + +// Private variables + +// Public variables + + +/*** Private Functions *****************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function _Utf8GetNumBytes + + \Description + Decode the number of bytes in a UTF-8 encoded sequence. + + \Input cLead - lead character of UTF-8 sequence + + \Output + int32_t - number of bytes in the sequence + + \Version 03/25/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _Utf8GetNumBytes(unsigned char cLead) +{ + int32_t iCodeSize; + + if ((cLead & 0x80) == 0x00) + { + iCodeSize = 1; + } + else if ((cLead & 0xE0) == 0xC0) + { + iCodeSize = 2; + } + else if ((cLead & 0xF0) == 0xE0) + { + iCodeSize = 3; + } + else + { + iCodeSize = 4; + } + + return(iCodeSize); +} + +/*F*************************************************************************************************/ +/*! + \Function _Utf8DecodeToUCS2 + + \Description + Decode a UTF-8 sequence into a UCS-2 code point. + + \Input *pOutPtr - pointer to output for decoded UCS-2 value + \Input *pStr - pointer to input UTF-8 string + + \Output + int32_t - number of input 8bit characters consumed + + \Notes + UCS-2 range (hex) UTF-8 octet sequence (binary) + 007F 0xxxxxxx + 07FF 110xxxxx 10xxxxxx + FFFF 1110xxxx 10xxxxxx 10xxxxxx + + \Version 03/26/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _Utf8DecodeToUCS2(uint16_t *pOutPtr, const unsigned char *pStr) +{ + int32_t iCodeSize; + + if ((*pStr & 0x80) == 0x00) + { + pOutPtr[0] = (uint16_t)pStr[0]; + iCodeSize = 1; + } + else if ((*pStr & 0xE0) == 0xC0) + { + pOutPtr[0] = ((pStr[0] & ~0xE0) << 6) | (pStr[1] & ~0xC0); + iCodeSize = 2; + } + else if ((*pStr & 0xF0) == 0xE0) + { + pOutPtr[0] = ((pStr[0] & ~0xF0) << 12) | ((pStr[1] & ~0xC0) << 6) | (pStr[2] & ~0xC0); + iCodeSize = 3; + } + else + { + iCodeSize = 4; + } + + return(iCodeSize); +} + +/*F*************************************************************************************************/ +/*! + \Function Utf8EncodeFromUCS2CodePt + + \Description + Encode a single UCS-2 code point ("char") into a UTF-8 sequence. + + \Input uCodePt - input UCS-2 code point + \Input *pOutPtr - pointer to output for encoded UTF-8 sequence. + + \Output + int32_t - number of 8bit characters output + + \Notes + See notes for _Utf8DecodeToUCS2() + + \Version 03/26/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8EncodeFromUCS2CodePt(char *pOutPtr, uint16_t uCodePt) +{ + int32_t iCodeSize; + + if (uCodePt < 0x0080) + { + pOutPtr[0] = (char)uCodePt; + iCodeSize = 1; + } + else if (uCodePt < 0x800) + { + pOutPtr[0] = 0xC0 | (uCodePt >> 6); + pOutPtr[1] = 0x80 | (uCodePt & 0x3F); + iCodeSize = 2; + } + else + { + pOutPtr[0] = 0xE0 | (uCodePt >> 12); + pOutPtr[1] = 0x80 | ((uCodePt >> 6) & 0x3F); + pOutPtr[2] = 0x80 | (uCodePt & 0x3F); + iCodeSize = 3; + } + + return(iCodeSize); +} + +/*F*************************************************************************************************/ +/*! + \Function _Utf8Translate + + \Description + Look through translation subtables and translate uCodePt + + \Input *pOutBuf - output buffer to store 8bit translated output + \Input *pTransTbl - translation table to translate with + \Input uCodePt - UCS-2 code point to translate + \Input cReplace - character to replace untranslatable characters with (or null-termination char to strip) + + \Output + int32_t - number of ascii characters output + + \Version 03/26/03 (JLB) +*/ +/*************************************************************************************************F*/ +static int32_t _Utf8Translate(char *pOutBuf, const Utf8TransTblT *pTransTbl, uint16_t uCodePt, char cReplace) +{ + unsigned char cCode; + char *pOldBuf = pOutBuf; + int32_t bFound; + + // look through subtables + for (bFound = FALSE; pTransTbl->uRangeEnd != 0; pTransTbl++) + { + // are we in range? + if ((uCodePt >= pTransTbl->uRangeBegin) && (uCodePt <= (pTransTbl->uRangeEnd))) + { + // dereference table + uCodePt -= pTransTbl->uRangeBegin; + cCode = (unsigned char)pTransTbl->pCodeTbl[uCodePt]; + + if ((cCode == 0xFF) && (cReplace != '\0')) + { + // untranslatable - replace + *(pOutBuf++) = cReplace; + } + else + { + // translate + *(pOutBuf++) = cCode; + } + + bFound = TRUE; + break; + } + } + + // not found and replacing? + if ((bFound == FALSE) && (cReplace != '\0')) + { + // replace + *(pOutBuf++) = cReplace; + } + + // return number of characters output + return((int32_t)(pOutBuf-pOldBuf)); +} + + +/*** Public Functions ******************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function Utf8Strip + + \Description + Strip non-ASCII UTF-8 encoded data. + + \Input *pOutStr - pointer to output buffer (may be same as input buffer) + \Input iBufSize - size of output buffer in ASCII units (char) + \Input *pInStr - pointer to source string + + \Output + int32_t - number of characters in new string, or zero if no utf8 data was stripped + + \Version 03/25/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8Strip(char *pOutStr, int32_t iBufSize, const char *pInStr) +{ + return(Utf8Replace(pOutStr, iBufSize, pInStr, '\0')); +} + +/*F*************************************************************************************************/ +/*! + \Function Utf8Replace + + \Description + Replace non-ASCII UTF-8 encoded data with the given character. + + \Input *pOutStr - pointer to output buffer (may be same as input buffer) + \Input iBufSize - size of output buffer in ASCII units (char) + \Input *pInStr - pointer to source string + \Input cReplace - character to replace non-ASCII UTF-8 characters with + + \Output + int32_t - number of characters in new string, or zero if no UTF-8 data was replaced + + \Version 03/25/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8Replace(char *pOutStr, int32_t iBufSize, const char *pInStr, char cReplace) +{ + int32_t iSrcIdx, iDstIdx = 0; + + // fast scan to find any utf8 encoded data + for (iSrcIdx = 0; ((pInStr[iSrcIdx] & 0x80) == 0) && (pInStr[iSrcIdx] != '\0'); iSrcIdx++) + { + } + + // did we find any? + if (pInStr[iSrcIdx] != '\0') + { + // yes, so replace/strip it + for (iDstIdx = iSrcIdx; pInStr[iSrcIdx] != '\0' && iDstIdx < iBufSize; ) + { + // do we have utf8 data? + if (pInStr[iSrcIdx] & 0x80) + { + // figure out how many bytes of utf8 data we have + int32_t iNumBytes = _Utf8GetNumBytes(pInStr[iSrcIdx]); + + // skip them + iSrcIdx += iNumBytes; + + // replace with cReplace + if (cReplace != '\0') + { + pOutStr[iDstIdx++] = cReplace; + } + } + else + { + // normal string data - copy it + pOutStr[iDstIdx++] = pInStr[iSrcIdx++]; + } + } + + // terminate + pOutStr[iDstIdx++] = '\0'; + } + + return(iDstIdx); +} + +/*F*************************************************************************************************/ +/*! + \Function Utf8StrLen + + \Description + Returns the number of code points in a UTF-8 encoded string. + + \Input *pStr - pointer to string to get string length of + + \Output + int32_t - number of code points in string + + \Version 03/26/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8StrLen(const char *pStr) +{ + int32_t iSrcIdx, iStrLen; + + for (iSrcIdx = iStrLen = 0; pStr[iSrcIdx] != '\0'; iStrLen++) + { + if (pStr[iSrcIdx] & 0x80) + { + // figure out how many bytes of utf8 data we have + int32_t iNumBytes = _Utf8GetNumBytes(pStr[iSrcIdx]); + + // skip them + iSrcIdx += iNumBytes; + } + else + { + iSrcIdx++; + } + } + + return(iStrLen); +} + +/*F*************************************************************************************************/ +/*! + \Function Utf8EncodeFromUCS2 + + \Description + Convert a UCS-2 code point sequence into a UTF-8 code point sequence. + + \Input *pOutStr - pointer to buffer to encode string to + \Input iBufLen - length of output buffer, in char units + \Input *pInStr - pointer to string to encode + + \Output + int32_t - output string length, in char units + + \Version 03/27/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8EncodeFromUCS2(char *pOutStr, int32_t iBufLen, const uint16_t *pInStr) +{ + int32_t iStrLen; + + // a UCS-2 encoded string can generate up to three chars. + iBufLen -= 2; + + // encode + for (iStrLen = 0; (*pInStr != 0x0000) && (iStrLen < iBufLen); pInStr++) + { + iStrLen += Utf8EncodeFromUCS2CodePt(&pOutStr[iStrLen], *pInStr); + } + + // NULL terminate & return length to caller + pOutStr[iStrLen++] = '\0'; + return(iStrLen); +} + +/*F*************************************************************************************************/ +/*! + \Function Utf8DecodeToUCS2 + + \Description + Convert a UTF-8 code point sequence into a UCS-2 code point sequence. + + \Input *pOutStr - pointer to buffer to decode string to + \Input iBufLen - length of output buffer, in UCS-2 units (int16_t) + \Input *pInStr - pointer to string to decode + + \Output + int32_t - output string length, in code points + + \Version 03/26/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8DecodeToUCS2(uint16_t *pOutStr, int32_t iBufLen, const char *pInStr) +{ + int32_t iSrcIdx, iStrLen, iCodeSize; + + // ensure room for NULL terminator + iBufLen--; + + // decode string + for (iSrcIdx = iStrLen = 0; (pInStr[iSrcIdx] != '\0') && (iStrLen < iBufLen); ) + { + if ((iCodeSize = _Utf8DecodeToUCS2(&pOutStr[iStrLen], (const unsigned char *)&pInStr[iSrcIdx])) <= 3) + { + iStrLen++; + } + + iSrcIdx += iCodeSize; + } + + // NULL terminate & return length to caller + pOutStr[iStrLen++] = 0x0000; + return(iStrLen); +} + +/*F*************************************************************************************************/ +/*! + \Function Utf8EncodeFrom8Bit + + \Description + Encode the given 8bit input string to UTF-8, based on the input translation table + + \Input *pOutStr - pointer to output UTF-8 string buffer + \Input iBufLen - length of buffer + \Input *pInStr - pointer to input 8bit string + \Input *pEncodeTbl - pointer to translation table to map 8bit string to UCS-2 + + \Output + int32_t - length of output UCS-8 string + + \Version 03/28/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8EncodeFrom8Bit(char *pOutStr, int32_t iBufLen, const char *pInStr, const Utf8EncodeTblT *pEncodeTbl) +{ + int32_t iStrLen; + uint16_t uCodePt; + + // a UCS-2 encoded value can generate up to three chars. + iBufLen -= 2; + + // encode + for (iStrLen = 0; (*pInStr != 0x0000) && (iStrLen < iBufLen); pInStr++) + { + uCodePt = pEncodeTbl->uCodeTbl[*(const unsigned char *)pInStr]; + iStrLen += Utf8EncodeFromUCS2CodePt(&pOutStr[iStrLen], uCodePt); + } + + // NULL terminate & return length to caller + pOutStr[iStrLen++] = '\0'; + return(iStrLen); +} + +/*F*************************************************************************************************/ +/*! + \Function Utf8TranslateTo8Bit + + \Description + Translates the given UTF-8 sequence based on the input translation table. + + \Input *pOutStr - pointer to buffer to decode string to + \Input iBufLen - length of output buffer, in ASCII units (char) + \Input *pInStr - pointer to string to decode + \Input cReplace - \verbatim character to replace code point with if untranslateable ('\0' to strip) \endverbatim + \Input *pTransTbl - pointer to NULL-terminated translation table array + + \Output + int32_t - length of output string in ASCII characters (8bit) + + \Notes + 'pTransTbl' is expected to be a NULL-terminated array of Utf8TransTblT structures that + represent a sparse translation table from 16bit UCS-2 space to 8bit game font space. + + \Version 03/26/03 (JLB) +*/ +/*************************************************************************************************F*/ +int32_t Utf8TranslateTo8Bit(char *pOutStr, int32_t iBufLen, const char *pInStr, char cReplace, const Utf8TransTblT *pTransTbl) +{ + int32_t iSrcIdx, iDstIdx; + uint16_t uCodePt = 0; + + // ensure room for NULL terminator + iBufLen--; + + // translate string + for (iSrcIdx = iDstIdx = 0; (pInStr[iSrcIdx] != '\0') && (iDstIdx < iBufLen); ) + { + iSrcIdx += _Utf8DecodeToUCS2(&uCodePt, (const unsigned char *)&pInStr[iSrcIdx]); + iDstIdx += _Utf8Translate(&pOutStr[iDstIdx], pTransTbl, uCodePt, cReplace); + } + + // NULL terminate & return length to caller + pOutStr[iDstIdx++] = '\0'; + return(iDstIdx); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/pc/voipheadsetpc.c b/r5dev/thirdparty/dirtysdk/source/voip/pc/voipheadsetpc.c new file mode 100644 index 00000000..4835b069 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/pc/voipheadsetpc.c @@ -0,0 +1,2306 @@ +/*H********************************************************************************/ +/*! + \File voipheadsetpc.c + + \Description + VoIP headset manager. + + \Copyright + Copyright Electronic Arts 2004-2011. + + \Notes + LPCM is supported only in loopback mode. + + \Version 1.0 03/30/2004 (jbrookes) First Version + \Version 2.0 10/20/2011 (jbrookes) Major rewrite for cleanup and to fix I/O bugs + \Version 3.0 07/09/2012 (akirchner) Added functionality to play buffer +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#pragma warning(push,0) +#include +#pragma warning(pop) + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "DirtySDK/voip/voiptranscribe.h" +#include "DirtySDK/voip/voipnarrate.h" + +#include "voippriv.h" +#include "voipcommon.h" +#include "voipconnection.h" +#include "voippacket.h" +#include "voipmixer.h" +#include "voipconduit.h" +#include "DirtySDK/voip/voipcodec.h" +#include "voipheadset.h" + +#include "voipdvi.h" +#include "voippcm.h" + +/*** Defines **********************************************************************/ + +// Defines taken from mmddk.h - not including mmddk directly as that would +// add a dependancy on the Windows DDK +#define DRVM_MAPPER (0x2000) +#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER + 23) +#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER + 21) +#define DRVM_MAPPER_PREFERRED_FLAGS_PREFERREDONLY (0x00000001) + +#define VOIP_HEADSET_SAMPLERATE (16000) //!< sample rate; 16khz audio +#define VOIP_HEADSET_SAMPLEWIDTH (2) //!< sample size; 16-bit samples +#define VOIP_HEADSET_FRAMEDURATION (20) //!< frame duration in milliseconds; 20ms +#define VOIP_HEADSET_FRAMESAMPLES ((VOIP_HEADSET_SAMPLERATE*VOIP_HEADSET_FRAMEDURATION)/1000) //!< samples per frame (20ms; 8khz=160, 11.025khz=220.5, 16khz=320) +#define VOIP_HEADSET_FRAMESIZE (VOIP_HEADSET_FRAMESAMPLES*VOIP_HEADSET_SAMPLEWIDTH) //!< frame size in bytes; 640 +#define VOIP_HEADSET_NUMWAVEBUFFERS (5) +#define VOIP_HEADSET_MAXDEVICES (16) +#define VOIP_HEADSET_WAVEFORMAT (WAVE_FORMAT_1M16) +#define VOIP_HEADSET_PREBUFFERBLOCKS (4) + +#if defined(_WIN64) + #define VOIP_HEADSET_WAVE_MAPPER ((uint64_t)-1) +#else + #define VOIP_HEADSET_WAVE_MAPPER (WAVE_MAPPER) +#endif + +//! transmission interval in milliseconds +#define VOIP_THREAD_SLEEP_DURATION (20) + +//! headset types (input, output) +typedef enum VoipHeadsetDeviceTypeE +{ + VOIP_HEADSET_INPDEVICE, + VOIP_HEADSET_OUTDEVICE, +} VoipHeadsetDeviceTypeE; + +/*** Macros ************************************************************************/ + +/*** Type Definitions **************************************************************/ +//! playback data +typedef struct VoipHeadsetWaveDataT +{ + WAVEHDR WaveHdr; + uint8_t FrameData[VOIP_HEADSET_FRAMESIZE]; +} VoipHeadsetWaveDataT; + +//! wave caps structure; this is a combination of the in and out caps, which are identical other than dwSupport (output only) +typedef struct VoipHeadsetWaveCapsT +{ + WORD wMid; //!< manufacturer ID + WORD wPid; //!< product ID + MMVERSION vDriverVersion; //!< version of the driver + CHAR szPname[MAXPNAMELEN]; //!< product name (NULL terminated string) + DWORD dwFormats; //!< formats supported + WORD wChannels; //!< number of sources supported + WORD wReserved1; //!< packing + DWORD dwSupport; //!< functionality supported by driver +} VoipHeadsetWaveCapsT; + +//! device info +typedef struct VoipHeadsetDeviceInfoT +{ + VoipHeadsetDeviceTypeE eDevType; //!< device type + + HANDLE hDevice; //!< handle to currently open device, or NULL if no device is open + int32_t iActiveDevice; //!< device index of active device + int32_t iDeviceToOpen; //!< device index of device we want to open, or -1 for any + + int32_t iCurVolume; //!< playback volume (output devices only) + int32_t iNewVolume; //!< new volume to set (if different from iCurVolume) + + VoipHeadsetWaveDataT WaveData[VOIP_HEADSET_NUMWAVEBUFFERS]; //!< audio input/output buffer + int32_t iCurWaveBuffer; //!< current input/output buffer + + DWORD dwCheckFlag[VOIP_HEADSET_NUMWAVEBUFFERS]; //!< flag to check for empty buffer (output only) + + VoipHeadsetWaveCapsT WaveDeviceCaps[VOIP_HEADSET_MAXDEVICES]; + int32_t iNumDevices; //!< number of enumerated devices + + uint8_t bActive; //!< device is active (recording/playing) + uint8_t bCloseDevice; //!< device close requested + uint8_t bChangeDevice; //!< device change requested + uint8_t _pad; +} VoipHeadsetDeviceInfoT; + +//!< local user data space +typedef struct PCLocalVoipUserT +{ + uint32_t uPlaybackFlags; //!< mute flag every remote user is 1 bit +} PCLocalVoipUserT; + +//! remote user data space +typedef struct PCRemoteVoipUserT +{ + VoipUserT User; +} PCRemoteVoipUserT; + +//! VOIP module state data +struct VoipHeadsetRefT +{ + int32_t iMemGroup; //!< mem group + void *pMemGroupUserData; //!< mem group user data + + VoipHeadsetDeviceInfoT MicrInfo; //!< input device info + VoipHeadsetDeviceInfoT SpkrInfo; //!< output device info + + // conduit info + int32_t iMaxConduits; + VoipConduitRefT *pConduitRef; + + // mixer state + VoipMixerRefT *pMixerRef; + + // accessibility support modules + VoipTranscribeRefT *pTranscribeRef; + VoipNarrateRefT *pNarrateRef; + + // codec frame size in bytes + int32_t iCmpFrameSize; + + // module debug level + int32_t iDebugLevel; + + // which user 0-3 "owns" voip, VOIP_INVALID_LOCAL_USER_INDEX if no one + int32_t iParticipatingUserIndex; + + // boolean control options + volatile uint8_t bMicOn; //!< TRUE if the mic is on, else FALSE + uint8_t bMuted; //!< TRUE if muted (e.g. push-to-talk), else FALSE + uint8_t bLoopback; //!< TRUE if loopback is enabled, else FALSE + uint8_t bTextChatAccessibility; //!< TRUE if text chat accessibility features are enabled + uint8_t bCrossplay; //!< TRUE if cross-play is enabled, else FALSE + + uint8_t uSendSeq; //!< send sequence count + uint8_t _pad[2]; + + // user callback data + VoipHeadsetMicDataCbT *pMicDataCb; + VoipHeadsetTextDataCbT *pTextDataCb; + VoipHeadsetStatusCbT *pStatusCb; + void *pCbUserData; + + // speaker callback data + VoipSpkrCallbackT *pSpkrDataCb; + void *pSpkrCbUserData; + + // critical section for guarding access to device close/change flags + NetCritT DevChangeCrit; + + // player + int32_t iPlayerActive; + int16_t *pPlayerBuffer; + uint32_t uPlayerBufferFrameCurrent; + uint32_t uPlayerBufferFrames; + uint32_t uPlayerFirstTime; + int32_t iPlayerFirstUse; + + // STT + uint8_t bVoiceTranscriptionEnabled; + + // TTS + uint8_t bNarrating; + int32_t iNarrateWritePos; + VoipNarrateGenderE eDefaultGender; + int8_t narrateBuffer[VOIP_HEADSET_FRAMESIZE]; + + PCLocalVoipUserT aLocalUsers[VOIP_MAXLOCALUSERS]; + + //! remote user list - must come last in ref as it is variable length + int32_t iMaxRemoteUsers; + PCRemoteVoipUserT aRemoteUsers[1]; +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Public Variables + +// Private Variables + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetWaveGetNumDevs + + \Description + Wraps wave(In|Out)GetNumDevs() + + \Input eDevType - VOIP_HEADSET_INPDEVICE or VOIP_HEADSET_OUTDEVICE + + \Output + UINT - wave(In|Out)GetNumDevs() result + + \Version 10/12/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static UINT _VoipHeadsetWaveGetNumDevs(VoipHeadsetDeviceTypeE eDevType) +{ + return((eDevType == VOIP_HEADSET_INPDEVICE) ? waveInGetNumDevs() : waveOutGetNumDevs()); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetWaveGetDevCaps + + \Description + Wraps wave(In|Out)GetDevCaps() + + \Input uDeviceID - device to get capabilities of + \Input *pWaveCaps - wave capabilities (note this structure does double-duty for input and output) + \Input eDevType - VOIP_HEADSET_INPDEVICE or VOIP_HEADSET_OUTDEVICE + + \Output + MMRESULT - wave(In|Out)GetNumDevs() result + + \Version 10/12/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static MMRESULT _VoipHeadsetWaveGetDevCaps(UINT_PTR uDeviceID, VoipHeadsetWaveCapsT *pWaveCaps, VoipHeadsetDeviceTypeE eDevType) +{ + return((eDevType == VOIP_HEADSET_INPDEVICE) ? waveInGetDevCaps(uDeviceID, (LPWAVEINCAPSA)pWaveCaps, sizeof(WAVEINCAPSA)) : waveOutGetDevCaps(uDeviceID, (LPWAVEOUTCAPSA)pWaveCaps, sizeof(WAVEOUTCAPSA))); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetWaveOpen + + \Description + Wraps wave(In|Out)Open() + + \Input *pHandle - [out] storage for handle to opened device + \Input uDeviceID - device to open + \Input *pWaveFormat - wave format we want + \Input eDevType - VOIP_HEADSET_INPDEVICE or VOIP_HEADSET_OUTDEVICE + + \Output + MMRESULT - wave(In|Out)Open() result + + \Version 10/12/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static MMRESULT _VoipHeadsetWaveOpen(HANDLE *pHandle, UINT uDeviceID, LPCWAVEFORMATEX pWaveFormat, VoipHeadsetDeviceTypeE eDevType) +{ + return((eDevType == VOIP_HEADSET_INPDEVICE) ? waveInOpen((LPHWAVEIN)pHandle, uDeviceID, pWaveFormat, 0, 0, 0) : waveOutOpen((LPHWAVEOUT)pHandle, uDeviceID, pWaveFormat, 0, 0, 0)); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetWaveClose + + \Description + Wraps wave(In|Out)Close() + + \Input hHandle - handle to device to close + \Input eDevType - VOIP_HEADSET_INPDEVICE or VOIP_HEADSET_OUTDEVICE + + \Output + MMRESULT - wave(In|Out)Close() result + + \Version 10/12/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static MMRESULT _VoipHeadsetWaveClose(HANDLE hHandle, VoipHeadsetDeviceTypeE eDevType) +{ + return((eDevType == VOIP_HEADSET_INPDEVICE) ? waveInClose((HWAVEIN)hHandle) : waveOutClose((HWAVEOUT)hHandle)); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetWavePrepareHeader + + \Description + Wraps wave(In|Out)PrepareHeader() + + \Input hHandle - handle to device to prepare wave header for + \Input pWaveHeader - wave header to prepare + \Input eDevType - VOIP_HEADSET_INPDEVICE or VOIP_HEADSET_OUTDEVICE + + \Output + MMRESULT - wave(In|Out)PrepareHeader() result + + \Version 10/12/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static MMRESULT _VoipHeadsetWavePrepareHeader(HANDLE hHandle, LPWAVEHDR pWaveHeader, VoipHeadsetDeviceTypeE eDevType) +{ + return((eDevType == VOIP_HEADSET_INPDEVICE) ? waveInPrepareHeader((HWAVEIN)hHandle, pWaveHeader, sizeof(WAVEHDR)) : waveOutPrepareHeader((HWAVEOUT)hHandle, pWaveHeader, sizeof(WAVEHDR))); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetWaveUnprepareHeader + + \Description + Wraps wave(In|Out)UnprepareHeader() + + \Input hHandle - handle to device to prepare wave header for + \Input pWaveHeader - wave header to unprepare + \Input eDevType - VOIP_HEADSET_INPDEVICE or VOIP_HEADSET_OUTDEVICE + + \Output + MMRESULT - wave(In|Out)UnprepareHeader() result + + \Version 10/12/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static MMRESULT _VoipHeadsetWaveUnprepareHeader(HANDLE hHandle, LPWAVEHDR pWaveHeader, VoipHeadsetDeviceTypeE eDevType) +{ + return((eDevType == VOIP_HEADSET_INPDEVICE) ? waveInUnprepareHeader((HWAVEIN)hHandle, pWaveHeader, sizeof(WAVEHDR)) : waveOutUnprepareHeader((HWAVEOUT)hHandle, pWaveHeader, sizeof(WAVEHDR))); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetWaveReset + + \Description + Wraps wave(In|Out)Reset() + + \Input hHandle - handle to device to prepare wave header for + \Input eDevType - VOIP_HEADSET_INPDEVICE or VOIP_HEADSET_OUTDEVICE + + \Output + MMRESULT - wave(In|Out)Reset() result + + \Version 10/12/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static MMRESULT _VoipHeadsetWaveReset(HANDLE hHandle, VoipHeadsetDeviceTypeE eDevType) +{ + return((eDevType == VOIP_HEADSET_INPDEVICE) ? waveInReset((HWAVEIN)hHandle) : waveOutReset((HWAVEOUT)hHandle)); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetPrepareWaveHeaders + + \Description + Prepare wave headers for use. + + \Input *pDeviceInfo - device info + \Input iNumBuffers - number of buffers in array + \Input hDevice - handle to wave device + + \Output + int32_t - negative=failure, zero=success + + \Version 07/28/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetPrepareWaveHeaders(VoipHeadsetDeviceInfoT *pDeviceInfo, int32_t iNumBuffers, HANDLE hDevice) +{ + HRESULT hResult; + int32_t iPlayBuf; + + for (iPlayBuf = 0; iPlayBuf < iNumBuffers; iPlayBuf++) + { + // set up the wave header + pDeviceInfo->WaveData[iPlayBuf].WaveHdr.lpData = (LPSTR)pDeviceInfo->WaveData[iPlayBuf].FrameData; + pDeviceInfo->WaveData[iPlayBuf].WaveHdr.dwBufferLength = sizeof(pDeviceInfo->WaveData[iPlayBuf].FrameData); + pDeviceInfo->WaveData[iPlayBuf].WaveHdr.dwFlags = 0L; + pDeviceInfo->WaveData[iPlayBuf].WaveHdr.dwLoops = 0L; + + // prepare the header + if ((hResult = _VoipHeadsetWavePrepareHeader(hDevice, &pDeviceInfo->WaveData[iPlayBuf].WaveHdr, pDeviceInfo->eDevType)) != MMSYSERR_NOERROR) + { + NetPrintf(("voipheadsetpc: error %d preparing %s buffer %d\n", hResult, + pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", iPlayBuf)); + return(-1); + } + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetUnprepareWaveHeaders + + \Description + Unprepare wave headers, prior to releasing them. + + \Input *pDeviceInfo - device info + \Input iNumBuffers - number of buffers in array + \Input hDevice - handle to wave device + + \Version 07/28/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetUnprepareWaveHeaders(VoipHeadsetDeviceInfoT *pDeviceInfo, int32_t iNumBuffers, HANDLE hDevice) +{ + HRESULT hResult; + int32_t iPlayBuf; + + for (iPlayBuf = 0; iPlayBuf < iNumBuffers; iPlayBuf++) + { + if ((hResult = _VoipHeadsetWaveUnprepareHeader(hDevice, &pDeviceInfo->WaveData[iPlayBuf].WaveHdr, pDeviceInfo->eDevType)) != MMSYSERR_NOERROR) + { + NetPrintf(("voipheadsetpc: error %d unpreparing %s buffer %d\n", hResult, + pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", iPlayBuf)); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetOpenDevice + + \Description + Open a wave device. + + \Input *pDeviceInfo - device state + \Input iDevice - handle to wave device (may be WAVE_MAPPER) + \Input iNumBuffers - number of input buffers + + \Output + int32_t - negative=error, zero=success + + \Version 07/28/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetOpenDevice(VoipHeadsetDeviceInfoT *pDeviceInfo, int32_t iDevice, int32_t iNumBuffers) +{ + WAVEFORMATEX WaveFormatEx = { WAVE_FORMAT_PCM, 1, VOIP_HEADSET_SAMPLERATE, VOIP_HEADSET_SAMPLERATE * VOIP_HEADSET_SAMPLEWIDTH, 2, 16, 0 }; + HANDLE hDevice = NULL; + HRESULT hResult; + + // open device + if ((hResult = _VoipHeadsetWaveOpen(&hDevice, iDevice, &WaveFormatEx, pDeviceInfo->eDevType)) != S_OK) + { + NetPrintf(("voipheadsetpc: error %d opening input device\n", hResult)); + return(-1); + } + + // set up input wave data + if (_VoipHeadsetPrepareWaveHeaders(pDeviceInfo, iNumBuffers, hDevice) >= 0) + { + // set volume to -1 so it will be reset + pDeviceInfo->iCurVolume = -1; + + // set active device + pDeviceInfo->iActiveDevice = iDevice; + + if (pDeviceInfo->eDevType == VOIP_HEADSET_OUTDEVICE) + { + int32_t iBuffer; + + // mark as playing + pDeviceInfo->bActive = TRUE; + + // set up check flag (we have to check for WHDR_PREPARED until we've written into the buffer) + for (iBuffer = 0; iBuffer < VOIP_HEADSET_NUMWAVEBUFFERS; iBuffer += 1) + { + pDeviceInfo->dwCheckFlag[iBuffer] = WHDR_PREPARED; + } + } + + NetPrintf(("voipheadsetpc: opened %s device\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output")); + } + else + { + _VoipHeadsetWaveClose(hDevice, pDeviceInfo->eDevType); + hDevice = NULL; + } + + pDeviceInfo->hDevice = hDevice; + return((hDevice == NULL) ? -2 : 0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetCloseDevice + + \Description + Close an open device. + + \Input *pHeadset - module state + \Input *pDeviceInfo - device info + + \Version 07/28/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetCloseDevice(VoipHeadsetRefT *pHeadset, VoipHeadsetDeviceInfoT *pDeviceInfo) +{ + MMRESULT hResult; + int32_t iDevice; + + // no open device, nothing to do + if (pDeviceInfo->hDevice == 0) + { + return; + } + + // reset the device + if ((hResult = _VoipHeadsetWaveReset(pDeviceInfo->hDevice, pDeviceInfo->eDevType)) != MMSYSERR_NOERROR) + { + NetPrintf(("voipheadsetpc: failed to reset %s device (err=%d)\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "waveIn" : "waveOut", hResult)); + } + + // unprepare the wave headers + _VoipHeadsetUnprepareWaveHeaders(pDeviceInfo, 2, pDeviceInfo->hDevice); + + // close the device + if ((hResult = _VoipHeadsetWaveClose(pDeviceInfo->hDevice, pDeviceInfo->eDevType)) != MMSYSERR_NOERROR) + { + NetPrintf(("voipheadsetpc: failed to close %s device (err=%d)\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "waveIn" : "waveOut", hResult)); + } + + // reset device state + NetPrintf(("voipheadsetpc: closed %s device\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output")); + + pDeviceInfo->hDevice = NULL; + pDeviceInfo->bActive = FALSE; + iDevice = pDeviceInfo->iActiveDevice; + pDeviceInfo->iActiveDevice = -1; + + // trigger device inactive callback + // user index is always 0 because PC does not support MLU + pHeadset->pStatusCb(0, FALSE, pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? VOIP_HEADSET_STATUS_INPUT : VOIP_HEADSET_STATUS_OUTPUT, pHeadset->pCbUserData); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetEnumerateDevices + + \Description + Enumerate devices attached to system, and add those that are compatible + to device list. + + \Input *pHeadset - headset module state + \Input *pDeviceInfo - device info + + \Version 07/28/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetEnumerateDevices(VoipHeadsetRefT *pHeadset, VoipHeadsetDeviceInfoT *pDeviceInfo) +{ + int32_t iDevice, iNumDevices, iAddedDevices; + VoipHeadsetWaveCapsT *pDeviceCaps; + const char *pActiveDeviceName = NULL; + int32_t iActiveDevice = -1; + + // get total number of eDevType devices + iNumDevices = _VoipHeadsetWaveGetNumDevs(pDeviceInfo->eDevType); + + // get the active device name, if a device is active + if (pDeviceInfo->iActiveDevice != -1) + { + iActiveDevice = pDeviceInfo->iActiveDevice; + pActiveDeviceName = pDeviceInfo->WaveDeviceCaps[iActiveDevice].szPname; + } + + // walk device list + for (iDevice = 0, iAddedDevices = 0; (iDevice < iNumDevices) && (iAddedDevices < VOIP_HEADSET_MAXDEVICES); iDevice++) + { + // get device capabilities + pDeviceCaps = &pDeviceInfo->WaveDeviceCaps[iAddedDevices]; + _VoipHeadsetWaveGetDevCaps(iDevice, pDeviceCaps, pDeviceInfo->eDevType); + + // print device info + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: querying %s device %d\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", iDevice)); + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: wMid=%d\n", pDeviceCaps->wMid)); + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: wPid=%d\n", pDeviceCaps->wPid)); + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: vDriverVersion=%d.%d\n", (pDeviceCaps->vDriverVersion&0xff00) >> 8, pDeviceCaps->vDriverVersion&0xff)); + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: szPname=%s\n", pDeviceCaps->szPname)); + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: dwFormats=0x%08x\n", pDeviceCaps->dwFormats)); + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: wChannels=%d\n", pDeviceCaps->wChannels)); + + // is it compatible? + if (pDeviceCaps->dwFormats & VOIP_HEADSET_WAVEFORMAT) + { + // if this device is our active input device make sure to update our iActiveDevice value; + if ((iActiveDevice != -1) && (strcmp(pActiveDeviceName, pDeviceCaps->szPname) == 0)) + { + // if the active input device index changed log the change + if (iActiveDevice != iAddedDevices) + { + pDeviceInfo->iActiveDevice = iAddedDevices; + NetPrintf(("voipheadsetpc: active %s device, %s, moved from index %d to %d\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", + pActiveDeviceName, iActiveDevice, iAddedDevices)); + } + + // already matched don't compare names again + iActiveDevice = -1; + } + + NetPrintf(("voipheadsetpc: %s device %d is compatible\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", iDevice)); + iAddedDevices++; + } + else + { + NetPrintf(("voipheadsetpc: %s device %d is not compatible\n", pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", iDevice)); + } + + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc:\n")); + } + + // set number of devices + pDeviceInfo->iNumDevices = iAddedDevices; +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetStop + + \Description + Stop recording & playback, and close USB audio device. + + \Notes + This function is safe to call regardless of device state. + + \Version 11/03/2003 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetStop(VoipHeadsetRefT *pHeadset) +{ + _VoipHeadsetCloseDevice(pHeadset, &pHeadset->MicrInfo); + _VoipHeadsetCloseDevice(pHeadset, &pHeadset->SpkrInfo); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetEnumerate + + \Description + Enumerate all connected audio devices. + + \Input *pHeadset - headset module state + + \Notes + Although we only support one headset, we will scan up to eight to try and + locate one that meets our needs for data format and sample rate. + + \Version 07/27/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetEnumerate(VoipHeadsetRefT *pHeadset) +{ + NetPrintf(("voipheadsetpc: enumerating devices\n")); + + // re-enumerating will kill our existing devices, so clean up appropriately. + _VoipHeadsetStop(pHeadset); + + // enumerate input devices + _VoipHeadsetEnumerateDevices(pHeadset, &pHeadset->MicrInfo); + + // enumerate output devices + _VoipHeadsetEnumerateDevices(pHeadset, &pHeadset->SpkrInfo); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetSetCodec + + \Description + Sets the specified codec. + + \Input *pHeadset - pointer to module state + \Input iCodecIdent - codec identifier to set + + \Output + int32_t - zero=success, negative=failure + + \Version 10/26/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetSetCodec(VoipHeadsetRefT *pHeadset, int32_t iCodecIdent) +{ + int32_t iResult; + + // pass through codec creation request + if ((iResult = VoipCodecCreate(iCodecIdent, pHeadset->iMaxConduits)) < 0) + { + return(iResult); + } + + // query codec output size + pHeadset->iCmpFrameSize = VoipCodecStatus(iCodecIdent, 'fsiz', VOIP_HEADSET_FRAMESAMPLES, NULL, 0); + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetSendEncodedAudio + + \Description + Send encoded audio on the wire, or into the conduit for local playback + in loopback mode. + + \Input *pHeadset - module state + \Input *pPacket - compressed audio data + \Input iCompBytes - size of the compressed audio data + + \Version 12/05/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetSendEncodedAudio(VoipHeadsetRefT *pHeadset, VoipMicrPacketT *pPacket, int32_t iCompBytes) +{ + if (!pHeadset->bLoopback) + { + pHeadset->pMicDataCb(&pPacket->aData, iCompBytes, NULL, 0, pHeadset->iParticipatingUserIndex, pHeadset->uSendSeq++, pHeadset->pCbUserData); + } + else + { + // set up a null user for loopback + VoipUserT VoipUser; + ds_memclr(&VoipUser, sizeof(VoipUser)); + + VoipConduitReceiveVoiceData(pHeadset->pConduitRef, &VoipUser, pPacket->aData, iCompBytes); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetNarrateCb + + \Description + VoipNarrate callback handler + + \Input *pNarrateRef - narration module state + \Input iUserIndex - local user index of user who requested narration + \Input *pSamples - sample data, or NULL if no data + \Input iSize - size of sample data in bytes, or zero if no data + \Input *pUserData - callback user data (headset module ref) + + \Version 12/05/2018 (tcho) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetNarrateCb(VoipNarrateRefT *pNarrateRef, int32_t iUserIndex, const int16_t *pSamples, int32_t iSize, void *pUserData) +{ + VoipHeadsetRefT *pHeadset = (VoipHeadsetRefT *)pUserData; + const int8_t *pBuffer = (const int8_t *)pSamples; + VoipMicrPacketT PacketData; + int32_t iCompBytes; + + if (iSize > 0) + { + int32_t iRemain = iSize; + while (iRemain != 0) + { + int32_t iCopySize = 0; + int8_t *pFrameBuffer = pHeadset->narrateBuffer + pHeadset->iNarrateWritePos; + + if (pHeadset->iNarrateWritePos == 0) + { + ds_memclr(pFrameBuffer, VOIP_HEADSET_FRAMESIZE); + } + + if (iRemain >= (VOIP_HEADSET_FRAMESIZE - pHeadset->iNarrateWritePos)) + { + iCopySize = VOIP_HEADSET_FRAMESIZE - pHeadset->iNarrateWritePos; + } + else + { + iCopySize = iRemain; + } + + ds_memcpy(pFrameBuffer, pBuffer + (iSize - iRemain), iCopySize); + iRemain -= iCopySize; + + if ((iCopySize == VOIP_HEADSET_FRAMESIZE) || ((pHeadset->iNarrateWritePos + iCopySize) == VOIP_HEADSET_FRAMESIZE)) + { + pHeadset->iNarrateWritePos = 0; + + // compress the input data, and if there is compressed data send it to the appropriate function + if ((iCompBytes = VoipCodecEncode(PacketData.aData, (const int16_t*)(pHeadset->narrateBuffer), VOIP_HEADSET_FRAMESAMPLES, (!pHeadset->bTextChatAccessibility && pHeadset->bVoiceTranscriptionEnabled) ? pHeadset->pTranscribeRef : NULL)) > 0) + { + _VoipHeadsetSendEncodedAudio(pHeadset, &PacketData, iCompBytes); + } + } + else + { + pHeadset->iNarrateWritePos += iCopySize; + } + } + } + else + { + pHeadset->bNarrating = (iSize == VOIPNARRATE_STREAM_START) ? TRUE : FALSE; + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetProcessPlay + + \Description + Process recording of data from the buffer and sending it to the registered + mic data user callback. + + \Input *pHeadset - pointer to headset state + + \Version 07/09/2012 (akirchner) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetProcessPlay(VoipHeadsetRefT *pHeadset) +{ + VoipMicrPacketT PacketData; + int32_t iCompBytes; + uint32_t uPlayerLastTime; + uint32_t uPackets; + uint32_t uPacket; + + if (!pHeadset->iPlayerFirstUse) + { + pHeadset->iPlayerFirstUse = 1; + pHeadset->uPlayerFirstTime = NetTick(); + } + + uPlayerLastTime = NetTick(); + uPackets = (uPlayerLastTime - pHeadset->uPlayerFirstTime) / VOIP_THREAD_SLEEP_DURATION; + + for (uPacket = 0; uPacket < uPackets; uPacket++) + { + pHeadset->uPlayerBufferFrameCurrent += VOIP_HEADSET_SAMPLEWIDTH; + pHeadset->uPlayerBufferFrameCurrent = (pHeadset->uPlayerBufferFrameCurrent >= pHeadset->uPlayerBufferFrames) ? 0 : pHeadset->uPlayerBufferFrameCurrent; + + // compress the buffer data, and if there is compressed data send it to the appropriate function + if ((iCompBytes = VoipCodecEncode(PacketData.aData, &pHeadset->pPlayerBuffer[VOIP_HEADSET_FRAMESAMPLES * pHeadset->uPlayerBufferFrameCurrent], VOIP_HEADSET_FRAMESAMPLES, pHeadset->pTranscribeRef)) > 0) + { + _VoipHeadsetSendEncodedAudio(pHeadset, &PacketData, iCompBytes); + } + } + + pHeadset->uPlayerFirstTime = uPlayerLastTime; +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetProcessRecord + + \Description + Process recording of data from the mic and sending it to the registered + mic data user callback. + + \Input *pHeadset - pointer to headset state + + \Version 04/01/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetProcessRecord(VoipHeadsetRefT *pHeadset) +{ + VoipHeadsetDeviceInfoT *pMicrInfo = &pHeadset->MicrInfo; + VoipMicrPacketT PacketData; + uint8_t FrameData[VOIP_HEADSET_FRAMESIZE]; //!< current recorded audio frame + int32_t iCompBytes; + HRESULT hResult; + + // only record if we have a recording device open + if ((pMicrInfo->hDevice == NULL) || (pHeadset->iParticipatingUserIndex == VOIP_INVALID_LOCAL_USER_INDEX)) + { + return; + } + + // read data from the mic + if (pMicrInfo->bActive == TRUE) + { + if (pHeadset->bMicOn == FALSE) + { + // tell hardware to stop recording + if ((hResult = waveInStop((HWAVEIN)pMicrInfo->hDevice)) == MMSYSERR_NOERROR) + { + // mark as not recording + NetPrintf(("voipheadsetpc: stop recording\n")); + pMicrInfo->bActive = FALSE; + } + else + { + NetPrintf(("voipheadsetpc: error %d trying to stop recording\n", hResult)); + } + } + + // pull in any waiting data + for (; pMicrInfo->WaveData[pMicrInfo->iCurWaveBuffer].WaveHdr.dwFlags & WHDR_DONE; ) + { + // copy audio data out of buffer + ds_memcpy_s(FrameData, sizeof(FrameData), &pMicrInfo->WaveData[pMicrInfo->iCurWaveBuffer].FrameData, VOIP_HEADSET_FRAMESIZE); + + // compress the input data, and if there is compressed data send it to the appropriate function + if (!pHeadset->bNarrating && (!pHeadset->bMuted || pHeadset->bLoopback)) + { + if ((iCompBytes = VoipCodecEncode(PacketData.aData, (int16_t *)FrameData, VOIP_HEADSET_FRAMESAMPLES, pHeadset->bVoiceTranscriptionEnabled ? pHeadset->pTranscribeRef : NULL)) > 0) + { + if (pHeadset->bTextChatAccessibility == FALSE) + { + _VoipHeadsetSendEncodedAudio(pHeadset, &PacketData, iCompBytes); + } + } + } + + // re-queue buffer to read more data + if ((hResult = waveInAddBuffer((HWAVEIN)pMicrInfo->hDevice, &pMicrInfo->WaveData[pMicrInfo->iCurWaveBuffer].WaveHdr, sizeof(WAVEHDR))) != MMSYSERR_NOERROR) + { + if (hResult == MMSYSERR_NODRIVER) + { + NetPrintf(("voipheadsetpc: waveInAddBuffer returned MMSYSERR_NODRIVER. Headset removed? Closing headset\n")); + _VoipHeadsetStop(pHeadset); + break; + } + else if (hResult != WAVERR_STILLPLAYING) + { + NetPrintf(("voipheadsetpc: could not add buffer %d to record queue (err=%d)\n", pMicrInfo->iCurWaveBuffer, hResult)); + } + } + + // index to next read buffer + pMicrInfo->iCurWaveBuffer = (pMicrInfo->iCurWaveBuffer + 1) % VOIP_HEADSET_NUMWAVEBUFFERS; + } + } + // if not recording and a mic-on request comes in, start recording + else if (pHeadset->bMicOn == TRUE) + { + int32_t iBuffer; + + if ((hResult = waveInStart((HWAVEIN)pMicrInfo->hDevice)) == MMSYSERR_NOERROR) + { + // mark as recording + NetPrintf(("voipheadsetpc: recording...\n")); + pMicrInfo->bActive = TRUE; + } + else + { + NetPrintf(("voipheadsetpc: error %d trying to start recording\n", hResult)); + } + + // add buffers to start recording + for (iBuffer = 0; iBuffer < VOIP_HEADSET_NUMWAVEBUFFERS; iBuffer += 1) + { + if ((hResult = waveInAddBuffer((HWAVEIN)pMicrInfo->hDevice, &pMicrInfo->WaveData[iBuffer].WaveHdr, sizeof(WAVEHDR))) != MMSYSERR_NOERROR) + { + NetPrintf(("voipheadsetpc: waveInAddBuffer for buffer %d failed err=%d\n", iBuffer, hResult)); + } + } + pMicrInfo->iCurWaveBuffer = 0; + + // reset compression state + VoipCodecReset(); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetProcessPlayback + + \Description + Process playback of data received from the network to the headset earpiece. + + \Input *pHeadset - pointer to headset state + + \Version 04/01/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetProcessPlayback(VoipHeadsetRefT *pHeadset) +{ + VoipHeadsetDeviceInfoT *pSpkrInfo = &pHeadset->SpkrInfo; + uint8_t FrameData[VOIP_HEADSET_FRAMESIZE]; //!< current recorded audio frame + int32_t iSampBytes; + HRESULT hResult; + + // only play if we have a playback device open + if ((pSpkrInfo->hDevice == NULL) || (pHeadset->iParticipatingUserIndex == VOIP_INVALID_LOCAL_USER_INDEX)) + { + return; + } + + // write data to the audio output if it's available + if (pSpkrInfo->bActive == TRUE) + { + int32_t iLoop, iNumBufAvail; + + // update play volume, if requested + if ((pSpkrInfo->iCurVolume != pSpkrInfo->iNewVolume) && (pSpkrInfo->iNewVolume != -1)) + { + int32_t iVolume; + + iVolume = (pSpkrInfo->iNewVolume * 65535) / 100; + iVolume |= iVolume << 16; + + if ((hResult = waveOutSetVolume((HWAVEOUT)pSpkrInfo->hDevice, iVolume)) == MMSYSERR_NOERROR) + { + NetPrintf(("voipheadsetpc: changed play volume to %d\n", pSpkrInfo->iNewVolume)); + pSpkrInfo->iCurVolume = pSpkrInfo->iNewVolume; + } + else + { + NetPrintf(("voipheadsetpc: error %d trying to change play volume\n", hResult)); + } + } + + // count number of buffers available to write into + for (iLoop = 0, iNumBufAvail = 0; iLoop < VOIP_HEADSET_NUMWAVEBUFFERS; iLoop += 1) + { + iNumBufAvail += (pSpkrInfo->WaveData[iLoop].WaveHdr.dwFlags & WHDR_DONE) ? 1 : 0; + } + + // if we have space to write + for ( ; pSpkrInfo->WaveData[pSpkrInfo->iCurWaveBuffer].WaveHdr.dwFlags & pSpkrInfo->dwCheckFlag[pSpkrInfo->iCurWaveBuffer]; ) + { + // decode and mix buffered packet data + if ((iSampBytes = VoipMixerProcess(pHeadset->pMixerRef, FrameData)) == VOIP_HEADSET_FRAMESIZE) + { + // if there's nothing playing, we want to prebuffer with silence + if (iNumBufAvail == VOIP_HEADSET_NUMWAVEBUFFERS) + { + uint8_t FrameSilenceData[VOIP_HEADSET_FRAMESIZE]; + int32_t iBlock; + + ds_memclr(FrameSilenceData, sizeof(FrameSilenceData)); + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: prebuffering %d blocks\n", VOIP_HEADSET_PREBUFFERBLOCKS)); + for (iBlock = 0; iBlock < VOIP_HEADSET_PREBUFFERBLOCKS; iBlock += 1) + { + ds_memcpy_s(pSpkrInfo->WaveData[pSpkrInfo->iCurWaveBuffer].FrameData, sizeof(pSpkrInfo->WaveData[pSpkrInfo->iCurWaveBuffer].FrameData), FrameSilenceData, sizeof(FrameSilenceData)); + if ((hResult = waveOutWrite((HWAVEOUT)pSpkrInfo->hDevice, &pSpkrInfo->WaveData[pSpkrInfo->iCurWaveBuffer].WaveHdr, sizeof(WAVEHDR))) == MMSYSERR_NOERROR) + { + pSpkrInfo->dwCheckFlag[pSpkrInfo->iCurWaveBuffer] = WHDR_DONE; + pSpkrInfo->iCurWaveBuffer = (pSpkrInfo->iCurWaveBuffer + 1) % VOIP_HEADSET_NUMWAVEBUFFERS; + } + else + { + NetPrintf(("voipheadsetpc: write failed (error=%d)\n", hResult)); + } + } + iNumBufAvail = 0; + } + + // forward data to speaker callback, if callback is specified + if (pHeadset->pSpkrDataCb != NULL) + { + pHeadset->pSpkrDataCb((int16_t *)FrameData, VOIP_HEADSET_FRAMESAMPLES, pHeadset->pSpkrCbUserData); + } + + // copy data to prepared wave buffer + ds_memcpy_s(pSpkrInfo->WaveData[pSpkrInfo->iCurWaveBuffer].FrameData, sizeof(pSpkrInfo->WaveData[pSpkrInfo->iCurWaveBuffer].FrameData), FrameData, VOIP_HEADSET_FRAMESIZE); + + // write out wave buffer + if ((hResult = waveOutWrite((HWAVEOUT)pSpkrInfo->hDevice, &pSpkrInfo->WaveData[pSpkrInfo->iCurWaveBuffer].WaveHdr, sizeof(WAVEHDR))) == MMSYSERR_NOERROR) + { + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: [%2d] wrote %d samples\n", pSpkrInfo->iCurWaveBuffer, VOIP_HEADSET_FRAMESAMPLES)); + pSpkrInfo->dwCheckFlag[pSpkrInfo->iCurWaveBuffer] = WHDR_DONE; + pSpkrInfo->iCurWaveBuffer = (pSpkrInfo->iCurWaveBuffer + 1) % VOIP_HEADSET_NUMWAVEBUFFERS; + } + else + { + if (hResult == MMSYSERR_NODRIVER) + { + NetPrintf(("voipheadsetpc: returned MMSYSERR_NODRIVER. Headset removed? Closing headset\n")); + _VoipHeadsetStop(pHeadset); + } + else if (hResult == WAVERR_STILLPLAYING) + { + NetPrintf(("voipheadsetpc: WAVERR_STILLPLAYING\n")); + } + else + { + NetPrintf(("voipheadsetpc: write failed (error=%d)\n", hResult)); + } + } + } + else if (iSampBytes > 0) + { + NetPrintf(("voipheadsetpc: error - got %d bytes from mixer when we were expecting %d\n", iSampBytes, VOIP_HEADSET_FRAMESIZE)); + } + else + { + // no data waiting in the mixer, so break out + break; + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetProcessDeviceChange + + \Description + Process a device change request. + + \Input *pHeadset - headset state + \Input *pDeviceInfo - device info + + \Version 10/12/2011 (jbrookes) Split from VoipHeadsetProcess() +*/ +/********************************************************************************F*/ +static void _VoipHeadsetProcessDeviceChange(VoipHeadsetRefT *pHeadset, VoipHeadsetDeviceInfoT *pDeviceInfo) +{ + // early out if no change is requested + if (!pDeviceInfo->bChangeDevice && !pDeviceInfo->bCloseDevice) + { + return; + } + + // device change requires sole access to headset critical section + NetCritEnter(&pHeadset->DevChangeCrit); + + // close the device + _VoipHeadsetCloseDevice(pHeadset, pDeviceInfo); + pDeviceInfo->bCloseDevice = FALSE; + + // process change input device request + if (pDeviceInfo->bChangeDevice) + { + if (_VoipHeadsetOpenDevice(pDeviceInfo, pDeviceInfo->iDeviceToOpen, VOIP_HEADSET_NUMWAVEBUFFERS) == 0) + { + // user index is always 0 because PC does not support MLU + pHeadset->pStatusCb(0, TRUE, pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? VOIP_HEADSET_STATUS_INPUT : VOIP_HEADSET_STATUS_OUTPUT, pHeadset->pCbUserData); + } + + pDeviceInfo->bChangeDevice = FALSE; + } + + NetCritLeave(&pHeadset->DevChangeCrit); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetProcessTranscription + + \Description + Process voice transcription + + \Input pHeadset - headset ref + + \Version 09/07/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipHeadsetProcessTranscription(VoipHeadsetRefT *pHeadset) +{ + char strTranscribeBuf[VOIPTRANSCRIBE_OUTPUT_MAX]; + + if (pHeadset->pTranscribeRef != NULL) + { + // process transcription + VoipTranscribeUpdate(pHeadset->pTranscribeRef); + + // if a transcription is available, send it along + if (VoipTranscribeGet(pHeadset->pTranscribeRef, strTranscribeBuf, sizeof(strTranscribeBuf)) > 0) + { + if (pHeadset->iParticipatingUserIndex != VOIP_INVALID_LOCAL_USER_INDEX) + { + pHeadset->pTextDataCb(strTranscribeBuf, pHeadset->iParticipatingUserIndex, pHeadset->pCbUserData); + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetGetRemoteUserIndex + + \Description + Returns the user index of the remote user + + \Input *pHeadset - pHeadset ref + \Input *pRemoteUser - remote user + + \Output + int32_t - remote user index, negative if user is not found + + \Version 05/28/2019 (tcho) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetGetRemoteUserIndex(VoipHeadsetRefT *pHeadset, VoipUserT *pRemoteUser) +{ + int32_t iRemoteUserIndex; + + for (iRemoteUserIndex = 0; iRemoteUserIndex < pHeadset->iMaxRemoteUsers; ++iRemoteUserIndex) + { + if (VOIP_SameUser(&pHeadset->aRemoteUsers[iRemoteUserIndex].User, pRemoteUser)) + { + break; + } + } + + return((iRemoteUserIndex != pHeadset->iMaxRemoteUsers) ? iRemoteUserIndex : -1); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetPlaybackCallback + + \Description + Determines if we should be submit audio to the given mixer + + \Input *pMixer - mixer ref + \Input *pRemoteUser - remote user who sent the mic packets + \Input *pUserData - callback user data (VoipHeadsetRefT) + + \Output + uint8_t - TRUE for playback, FALSE for not to playback + + \Version 05/28/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static uint8_t _VoipHeadsetPlaybackCallback(VoipMixerRefT *pMixer, VoipUserT *pRemoteUser, void *pUserData) +{ + uint8_t bPlayback = TRUE; + VoipHeadsetRefT *pHeadset = (VoipHeadsetRefT *)pUserData; + + int32_t iRemoteUserIndex; + int32_t iLocalUserIndex; + + // find the remote user index + iRemoteUserIndex = _VoipHeadsetGetRemoteUserIndex(pHeadset, pRemoteUser); + + if (iRemoteUserIndex < 0) + { + NetPrintf(("voipheadsetpc: _VoipHeadsetPlaybackCallback() cannot find the remote user iPersonaId: %lld", pRemoteUser->AccountInfo.iPersonaId)); + return(TRUE); + } + + // loop through all local users determine if ANY of them have this remote user index muted (we have a shared device on PC) + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS; ++iLocalUserIndex) + { + // muting from +-pbk selectors + if ((pHeadset->aLocalUsers[iLocalUserIndex].uPlaybackFlags & (1 << iRemoteUserIndex)) == 0) + { + bPlayback = FALSE; + break; + } + } + return(bPlayback); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetCreate + + \Description + Create the headset manager. + + \Input iMaxConduits - max number of conduits + \Input *pMicDataCb - pointer to user callback to trigger when mic data is ready + \Input *pTextDataCb - pointer to user callback to trigger when transcribed text is ready + \Input *pOpaqueDataCb - pointer to user callback to trigger when opaque data is ready (ignored) + \Input *pStatusCb - pointer to user callback to trigger when headset status changes + \Input *pCbUserData - pointer to user callback data + \Input iData - platform-specific - unused for PC + + \Output + VoipHeadsetRefT * - pointer to module state, or NULL if an error occured + + \Version 03/30/2004 (jbrookes) +*/ +/********************************************************************************F*/ +VoipHeadsetRefT *VoipHeadsetCreate(int32_t iMaxConduits, VoipHeadsetMicDataCbT *pMicDataCb, VoipHeadsetTextDataCbT *pTextDataCb, VoipHeadsetOpaqueDataCbT *pOpaqueDataCb, VoipHeadsetStatusCbT *pStatusCb, void *pCbUserData, int32_t iData) +{ + VoipHeadsetRefT *pHeadset; + int32_t iMemGroup, iSize, iLocalUserIndex; + void *pMemGroupUserData; + + // Query mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // make sure we don't exceed maxconduits + if (iMaxConduits > VOIP_MAXCONNECT) + { + NetPrintf(("voipheadsetpc: request for %d conduits exceeds max\n", iMaxConduits)); + return(NULL); + } + + iSize = sizeof(*pHeadset) + (sizeof(PCRemoteVoipUserT) * (iMaxConduits + VOIP_MAX_LOW_LEVEL_CONNS - 1)); + + // allocate and clear module state + if ((pHeadset = (VoipHeadsetRefT *)DirtyMemAlloc(iSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + return(NULL); + } + ds_memclr(pHeadset, iSize); + + // allocate mixer + if ((pHeadset->pMixerRef = VoipMixerCreate(16, VOIP_HEADSET_FRAMESAMPLES)) == NULL) + { + NetPrintf(("voipheadsetpc: unable to create mixer\n")); + DirtyMemFree(pHeadset, VOIP_MEMID, iMemGroup, pMemGroupUserData); + return(NULL); + } + + // allocate conduit manager + if ((pHeadset->pConduitRef = VoipConduitCreate(iMaxConduits)) == NULL) + { + NetPrintf(("voipheadsetpc: unable to allocate conduit manager\n")); + VoipMixerDestroy(pHeadset->pMixerRef); + DirtyMemFree(pHeadset, VOIP_MEMID, iMemGroup, pMemGroupUserData); + return(NULL); + } + pHeadset->iMaxConduits = iMaxConduits; + pHeadset->iMaxRemoteUsers = iMaxConduits + VOIP_MAX_LOW_LEVEL_CONNS; + VoipConduitRegisterPlaybackCb(pHeadset->pConduitRef, _VoipHeadsetPlaybackCallback, pHeadset); + + // initialize playback flags (default on for everyone) + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS; ++iLocalUserIndex) + { + pHeadset->aLocalUsers[iLocalUserIndex].uPlaybackFlags = 0xFFFFFFFF; + } + + // allocate narration module + if ((pHeadset->pNarrateRef = VoipNarrateCreate(_VoipHeadsetNarrateCb, pHeadset)) == NULL) + { + VoipTranscribeDestroy(pHeadset->pTranscribeRef); + VoipConduitDestroy(pHeadset->pConduitRef); + VoipMixerDestroy(pHeadset->pMixerRef); + DirtyMemFree(pHeadset, VOIP_MEMID, iMemGroup, pMemGroupUserData); + return(NULL); + } + + // set no participating user at the start + pHeadset->iParticipatingUserIndex = VOIP_INVALID_LOCAL_USER_INDEX; + + // set TTS default gender + pHeadset->eDefaultGender = VOIPNARRATE_GENDER_MALE; + + // set mixer + VoipConduitMixerSet(pHeadset->pConduitRef, pHeadset->pMixerRef); + + // register codecs + VoipCodecRegister('dvid', &VoipDVI_CodecDef); + VoipCodecRegister('lpcm', &VoipPCM_CodecDef); + + // set up to use dvi codec by default + VoipHeadsetControl(pHeadset, 'cdec', 'dvid', 0, NULL); + + // enable microphone + VoipHeadsetControl(pHeadset, 'micr', TRUE, 0, NULL); + + // save mem info + pHeadset->iMemGroup = iMemGroup; + pHeadset->pMemGroupUserData = pMemGroupUserData; + + // save info + pHeadset->pMicDataCb = pMicDataCb; + pHeadset->pTextDataCb = pTextDataCb; + pHeadset->pStatusCb = pStatusCb; + pHeadset->pCbUserData = pCbUserData; + + // no currently active device + pHeadset->SpkrInfo.eDevType = VOIP_HEADSET_OUTDEVICE; + pHeadset->SpkrInfo.iActiveDevice = -1; + pHeadset->SpkrInfo.iDeviceToOpen = WAVE_MAPPER; + + pHeadset->MicrInfo.eDevType = VOIP_HEADSET_INPDEVICE; + pHeadset->MicrInfo.iActiveDevice = -1; + pHeadset->MicrInfo.iDeviceToOpen = WAVE_MAPPER; + + // play + pHeadset->iPlayerActive = 0; + + // set initial volume + pHeadset->SpkrInfo.iNewVolume = -1; + + // set default debuglevel + pHeadset->iDebugLevel = 1; + + // enumerate headsets on startup + _VoipHeadsetEnumerate(pHeadset); + + // init the critical section + NetCritInit(&pHeadset->DevChangeCrit, "voipheadsetpc-devchange"); + + // return module ref to caller + return(pHeadset); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetDestroy + + \Description + Destroy the headset manager. + + \Input *pHeadset - pointer to headset state + + \Version 03/31/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipHeadsetDestroy(VoipHeadsetRefT *pHeadset) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + // stop headsets + _VoipHeadsetStop(pHeadset); + + // destroy transcription ref + if (pHeadset->pTranscribeRef != NULL) + { + VoipTranscribeDestroy(pHeadset->pTranscribeRef); + } + + // destroy narrate ref + VoipNarrateDestroy(pHeadset->pNarrateRef); + + // free conduit manager + VoipConduitDestroy(pHeadset->pConduitRef); + + // free mixer + VoipMixerDestroy(pHeadset->pMixerRef); + + // free active codec + VoipCodecDestroy(); + + // kill the critical section + NetCritKill(&pHeadset->DevChangeCrit); + + // dispose of module memory + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + DirtyMemFree(pHeadset, VOIP_MEMID, iMemGroup, pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetReceiveVoiceDataCb + + \Description + Connectionlist callback to handle receiving a voice packet from a remote peer. + + \Input *pRemoteUsers - user we're receiving the voice data from + \Input iRemoteUserSize - pRemoteUsers array size + \Input iConsoleId - generic identifier for the console to which the users belong + \Input *pMicrInfo - micr info from inbound packet + \Input *pPacketData - pointer to beginning of data in packet payload + \Input *pUserData - VoipHeadsetT ref + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipHeadsetReceiveVoiceDataCb(VoipUserT *pRemoteUsers, int32_t iRemoteUserSize, int32_t iConsoleId, VoipMicrInfoT *pMicrInfo, uint8_t *pPacketData, void *pUserData) +{ + VoipHeadsetRefT *pHeadset = (VoipHeadsetRefT *)pUserData; + uint32_t uRemoteUserIndex = 0; + uint32_t uMicrPkt; + const BYTE *pSubPacket = pPacketData; + + // if we're not playing, ignore it + if (pHeadset->SpkrInfo.bActive == FALSE) + { + #if DIRTYCODE_LOGGING + if ((pMicrInfo->uSeqn % 30) == 0) + { + NetPrintfVerbose((pHeadset->iDebugLevel, 2, "voipheadsetpc: playback disabled, discarding voice data (seqn=%d)\n", pMicrInfo->uSeqn)); + } + #endif + return; + } + + // validate subpacket size if we are dealing with a fixed-length scenario + if ((pMicrInfo->uSubPacketSize != 0xFF) && (pMicrInfo->uSubPacketSize != pHeadset->iCmpFrameSize)) + { + NetPrintf(("voipheadsetpc: discarding voice packet with %d voice bundles and mismatched sub-packet size %d (expecting %d)\n", + pMicrInfo->uNumSubPackets, pMicrInfo->uSubPacketSize, pHeadset->iCmpFrameSize)); + return; + } + + // if this is the shared user index + if (pMicrInfo->uUserIndex == VOIP_SHARED_REMOTE_INDEX) + { + int32_t iIndex; + + // find the first valid user to playback the audio + for (iIndex = 0; iIndex < iRemoteUserSize; iIndex += 1) + { + if (!VOIP_NullUser(&pRemoteUsers[iIndex])) + { + uRemoteUserIndex = iIndex; + break; + } + } + + if (iIndex == iRemoteUserSize) + { + // didn't find a remote user to play back the shared audio + NetPrintf(("voipheadsetpc: discarding voice packet from shared user because we cannot find a remote user to play it back as!\n")); + return; + } + } + else + { + uRemoteUserIndex = pMicrInfo->uUserIndex; + } + + // submit voice sub-packets + for (uMicrPkt = 0; uMicrPkt < pMicrInfo->uNumSubPackets; uMicrPkt++) + { + // get the size of the subpacket based on variable or not + uint32_t uSubPacketSize = (pMicrInfo->uSubPacketSize != 0xFF) ? pMicrInfo->uSubPacketSize : *pSubPacket++; + + // send it to conduit manager + VoipConduitReceiveVoiceData(pHeadset->pConduitRef, &pRemoteUsers[uRemoteUserIndex], pSubPacket, uSubPacketSize); + + // move to next packet + pSubPacket += uSubPacketSize; + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetSetRemoteUserVoicePlayback + + \Description + Helper function to enable or disable playback of voice from a remote user + for a given local user. + + \Input *pHeadset - pointer to headset state + \Input *pRemoteUser - remote user + \Input iLocalUserIndex - local user index + \Input bEnablePlayback - TRUE to enable voice playback. FALSE to disable voice playback. + + \Output + int32_t - negative=error, zero=success + + \Version 06/09/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetSetRemoteUserVoicePlayback(VoipHeadsetRefT *pHeadset, VoipUserT *pRemoteUser, int32_t iLocalUserIndex, uint8_t bEnablePlayback) +{ + int32_t iRemoteUserIndex; + + // find the remote user index + iRemoteUserIndex = _VoipHeadsetGetRemoteUserIndex(pHeadset, pRemoteUser); + + if (iRemoteUserIndex < 0) + { + NetPrintf(("voipheadsetpc: _VoipHeadsetSetRemoteUserVoicePlayback() cannot find the remote user iPersonaId: %lld", pRemoteUser->AccountInfo.iPersonaId)); + return(-1); + } + + if (bEnablePlayback) + { + pHeadset->aLocalUsers[iLocalUserIndex].uPlaybackFlags |= (1 << iRemoteUserIndex); + } + else + { + pHeadset->aLocalUsers[iLocalUserIndex].uPlaybackFlags &= ~(1 << iRemoteUserIndex); + } + NetPrintf(("voipheadsetpc: local user[%d] playback flags now: 0x%08x.\n", iLocalUserIndex, pHeadset->aLocalUsers[iLocalUserIndex].uPlaybackFlags)); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetAddRemoteTalker + + \Description + Create the game chat participant for this remote user. + + \Input *pHeadset - headset module + \Input *pRemoteUser - remote user + \Input uConsoleId - unique console identifier (local scope only, does not need to be the same on all hosts) + + \Output + int32_t - negative=error, zero=success + + \Version 06/09/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetAddRemoteTalker(VoipHeadsetRefT *pHeadset, VoipUserT *pRemoteUser, uint64_t uConsoleId) +{ + int32_t iRemoteUserIndex; + PCRemoteVoipUserT *pPcRemoteUser = NULL; + + // find a duplicate entry + for (iRemoteUserIndex = 0; iRemoteUserIndex < pHeadset->iMaxRemoteUsers; ++iRemoteUserIndex) + { + if (VOIP_SameUser(&pHeadset->aRemoteUsers[iRemoteUserIndex].User, pRemoteUser)) + { + pPcRemoteUser = &pHeadset->aRemoteUsers[iRemoteUserIndex]; + break; + } + } + + // early return if we remote user already exists + if (pPcRemoteUser != NULL) + { + NetPrintf(("voipheadsetpc: adding remote talker %lld failed because it already exists\n", pRemoteUser->AccountInfo.iPersonaId)); + return(-1); + } + + // find a empty remote user entry + for (iRemoteUserIndex = 0; iRemoteUserIndex < pHeadset->iMaxRemoteUsers; ++iRemoteUserIndex) + { + if (pHeadset->aRemoteUsers[iRemoteUserIndex].User.AccountInfo.iPersonaId == 0) + { + ds_memclr(&pHeadset->aRemoteUsers[iRemoteUserIndex], sizeof(PCRemoteVoipUserT)); + ds_memcpy(&pHeadset->aRemoteUsers[iRemoteUserIndex].User, pRemoteUser, sizeof(VoipUserT)); + pPcRemoteUser = &pHeadset->aRemoteUsers[iRemoteUserIndex]; + + // register the remote user with all the conduits + VoipConduitRegisterUser(pHeadset->pConduitRef, pRemoteUser, TRUE); + + NetPrintf(("voipheadsetpc: registered remote talker %lld at remote user index %d\n", pRemoteUser->AccountInfo.iPersonaId, iRemoteUserIndex)); + break; + } + } + + if (pPcRemoteUser == NULL) + { + NetPrintf(("voipheadsetpc: adding remote talker %lld failed because aRemoteUsers is full\n", pRemoteUser->AccountInfo.iPersonaId)); + return(-2); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipHeadsetRemoveRemoteTalker + + \Description + Remove the specified remote user from the collection of users known by the chat manager. + + \Input *pHeadset - headset module + \Input *pUser - user to be removed + + \Output + int32_t - negative=error, zero=success + + \Version 11/22/2018 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _VoipHeadsetRemoveRemoteTalker(VoipHeadsetRefT *pHeadset, VoipUserT *pUser) +{ + int32_t iRetCode = 0; + int32_t iRemoteUserIndex; + PCRemoteVoipUserT *pPcRemoteUser = NULL; + + VoipConduitRegisterUser(pHeadset->pConduitRef, pUser, FALSE); + + // find the remote user + for (iRemoteUserIndex = 0; iRemoteUserIndex < pHeadset->iMaxRemoteUsers; ++iRemoteUserIndex) + { + if (VOIP_SameUser(&pHeadset->aRemoteUsers[iRemoteUserIndex].User, pUser)) + { + pPcRemoteUser = &pHeadset->aRemoteUsers[iRemoteUserIndex]; + break; + } + } + + if (pPcRemoteUser != NULL) + { + NetPrintf(("voipheadsetpc: unregistered remote talker %lld at remote user index %d\n", pPcRemoteUser->User.AccountInfo.iPersonaId, iRemoteUserIndex)); + ds_memclr(pPcRemoteUser, sizeof(PCRemoteVoipUserT)); + } + else + { + NetPrintf(("voipheadsetpc: unregistered remote talker %lld failed because it does not exist\n", pUser->AccountInfo.iPersonaId)); + iRetCode = -1; + } + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetRegisterUserCb + + \Description + Connectionlist callback to register/unregister a new user with the + VoipConduit module. + + \Input *pRemoteUser - user to register + \Input iConsoleId - generic identifier for the console to which the user belongs (ignored) + \Input bRegister - true=register, false=unregister + \Input *pUserData - voipheadset module ref + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipHeadsetRegisterUserCb(VoipUserT *pRemoteUser, int32_t iConsoleId, uint32_t bRegister, void *pUserData) +{ + VoipHeadsetRefT *pHeadset = (VoipHeadsetRefT *)pUserData; + + // early exit if invalid remote talker + if (VOIP_NullUser(pRemoteUser)) + { + NetPrintf(("voipheadsetpc: can't %s NULL remote talker\n", (bRegister ? "register" : "unregister"))); + return; + } + + if (bRegister) + { + _VoipHeadsetAddRemoteTalker(pHeadset, pRemoteUser, (uint64_t)iConsoleId); + } + else + { + _VoipHeadsetRemoveRemoteTalker(pHeadset, pRemoteUser); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetProcess + + \Description + Headset process function. + + \Input *pHeadset - pointer to headset state + \Input uFrameCount - process iteration counter + + \Version 03/31/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipHeadsetProcess(VoipHeadsetRefT *pHeadset, uint32_t uFrameCount) +{ + if (pHeadset->iPlayerActive) + { + // process playing + _VoipHeadsetProcessPlay(pHeadset); + } + else + { + // process recording + _VoipHeadsetProcessRecord(pHeadset); + } + + // process narration + VoipNarrateUpdate(pHeadset->pNarrateRef); + + // process transcription + _VoipHeadsetProcessTranscription(pHeadset); + + // process playback + _VoipHeadsetProcessPlayback(pHeadset); + + // process device change requests + _VoipHeadsetProcessDeviceChange(pHeadset, &pHeadset->MicrInfo); + _VoipHeadsetProcessDeviceChange(pHeadset, &pHeadset->SpkrInfo); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetSetVolume + + \Description + Sets play and record volume. + + \Input *pHeadset - pointer to headset state + \Input iPlayVol - play volume to set + \Input iRecVol - record volume to set + + \Notes + To not set a value, specify it as -1. + + \Version 03/31/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipHeadsetSetVolume(VoipHeadsetRefT *pHeadset, int32_t iPlayVol, uint32_t iRecVol) +{ + if (iPlayVol != -1) + { + pHeadset->SpkrInfo.iNewVolume = iPlayVol; + } +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetControl + + \Description + Control function. + + \Input *pHeadset - headset module state + \Input iControl - control selector + \Input iValue - control value + \Input iValue2 - control value + \Input *pValue - control value + + \Output + int32_t - selector specific, or -1 if no such selector + + \Notes + iControl can be one of the following: + + \verbatim + 'aloc' - promote user to 'participating' state, or demote user from 'participating' state + 'cdec' - create new codec + 'cide' - close voip input device + 'code' - close voip output device + 'cstm' - clear speech to text metrics in VoipSpeechToTextMetricsT + 'ctsm' - clear text to speech metrics in VoipTextToSpeechMetricsT + 'edev' - enumerate voip input/output devices + 'idev' - select voip input device + 'loop' - enable/disable loopback + 'micr' - enable/disable recording + 'mute' - enable/disable audio input muting + 'odev' - select voip output device + 'play' - enable/disable playing + 'txta' - enable/disable text chat accessibility, this also controls loopback in this context + 'tran' - enable/disable local generation of transcribed text for speech procuced by local users (speech-to-text component) + 'svol' - changes speaker volume + 'ttos' - send utf8 text in pValue as voice and initialize TTS voice + 'xply' - enable/disable crossplay (no difference on PC) + + \endverbatim + + \Version 07/28/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipHeadsetControl(VoipHeadsetRefT *pHeadset, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'aloc') + { + int32_t iLocalUserIndex; + + if (iValue2 == 0) + { + pHeadset->iParticipatingUserIndex = VOIP_INVALID_LOCAL_USER_INDEX; + NetPrintf(("voipheadsetpc: no user participating\n", iValue)); + } + else + { + pHeadset->iParticipatingUserIndex = iValue; + NetPrintf(("voipheadsetpc: user %d, entering participating state\n", iValue)); + } + + // whenever active user status changes all mute masks will be re-calculated so lets reset our state + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS; ++iLocalUserIndex) + { + pHeadset->aLocalUsers[iLocalUserIndex].uPlaybackFlags = 0xFFFFFFFF; + } + + return(0); + } + if (iControl == 'cdec') + { + return(_VoipHeadsetSetCodec(pHeadset, iValue)); + } + if ((iControl == 'cide') || (iControl == 'code')) + { + VoipHeadsetDeviceInfoT *pDeviceInfo = (iControl == 'cide') ? &pHeadset->MicrInfo : &pHeadset->SpkrInfo; + NetCritEnter(&pHeadset->DevChangeCrit); + pDeviceInfo->bCloseDevice = TRUE; + NetCritLeave(&pHeadset->DevChangeCrit); + } + if (iControl == 'cstm') + { + if (pHeadset->pTranscribeRef != NULL) + { + return(VoipTranscribeControl(pHeadset->pTranscribeRef, iControl, iValue, 0, NULL)); + } + return(-1); + } + if (iControl == 'ctsm') + { + return(VoipNarrateControl(pHeadset->pNarrateRef, iControl, 0, 0, NULL)); + } + if (iControl == 'edev') + { + _VoipHeadsetEnumerateDevices(pHeadset, &pHeadset->MicrInfo); + _VoipHeadsetEnumerateDevices(pHeadset, &pHeadset->SpkrInfo); + return(0); + } + if ((iControl == 'idev') || (iControl == 'odev')) + { + VoipHeadsetDeviceInfoT *pDeviceInfo = (iControl == 'idev') ? &pHeadset->MicrInfo : &pHeadset->SpkrInfo; + if (pDeviceInfo->iActiveDevice != iValue) + { + NetPrintf(("voipheadsetpc: '%C' selector used to change %s device from %d to %d\n", iControl, pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", + pDeviceInfo->iActiveDevice, iValue)); + NetCritEnter(&pHeadset->DevChangeCrit); + pDeviceInfo->iDeviceToOpen = iValue; + pDeviceInfo->bChangeDevice = TRUE; + NetCritLeave(&pHeadset->DevChangeCrit); + } + else + { + NetPrintf(("voipheadsetpc: '%C' selector ignored because %s device %d is already active\n", iControl, pDeviceInfo->eDevType == VOIP_HEADSET_INPDEVICE ? "input" : "output", + pDeviceInfo->iActiveDevice, iValue)); + } + return(0); + } + if (iControl == 'loop') + { + pHeadset->bLoopback = iValue; + NetPrintf(("voipheadsetpc: loopback mode %s\n", (iValue ? "enabled" : "disabled"))); + return(0); + } + if (iControl == 'micr') + { + pHeadset->bMicOn = (uint8_t)iValue; + NetPrintf(("voipheadsetpc: mic %s\n", (iValue ? "enabled" : "disabled"))); + return(0); + } + if (iControl == 'mute') + { + uint8_t bMuted = iValue2 ? TRUE : FALSE; + if (pHeadset->bMuted != bMuted) + { + NetPrintfVerbose((pHeadset->iDebugLevel, 1, "voipheadsetpc: mute %s\n", (pHeadset->bMuted ? "enabled" : "disabled"))); + pHeadset->bMuted = bMuted; + } + return(0); + } + if ((iControl == '-pbk') || (iControl == '+pbk')) + { + uint8_t bVoiceEnable = (iControl == '+pbk') ? TRUE : FALSE; + int32_t iRetCode = 0; // default to success + + VoipUserT *pRemoteUser; + + if ((pValue != NULL) && (iValue < VOIP_MAXLOCALUSERS_EXTENDED)) + { + pRemoteUser = (VoipUserT *)pValue; + + // make sure the local user and the remote user are not a shared user (shared user concept not supported on pc) + if ((iValue != VOIP_SHARED_USER_INDEX) && ((pRemoteUser->AccountInfo.iPersonaId & VOIP_SHARED_USER_MASK) != VOIP_SHARED_USER_VALUE)) + { + _VoipHeadsetSetRemoteUserVoicePlayback(pHeadset, pRemoteUser, iValue, bVoiceEnable); + } + } + else + { + NetPrintf(("voipheadsetpc: VoipHeadsetControl('%C', %d) invalid arguments\n", iControl, iValue)); + iRetCode = -2; + } + return(iRetCode); + } + #if DIRTYCODE_LOGGING + if (iControl == 'spam') + { + pHeadset->iDebugLevel = iValue; + NetPrintf(("voipheadsetpc: debuglevel=%d\n", pHeadset->iDebugLevel)); + VoipConduitControl(pHeadset->pConduitRef, 'spam', iValue, pValue); + return(VoipCodecControl(VOIP_CODEC_ACTIVE, iControl, iValue, 0, NULL)); + } + #endif + if (iControl == 'txta') + { + pHeadset->bTextChatAccessibility = pHeadset->bLoopback = iValue; + NetPrintf(("voipheadsetpc: text chat accessibility mode %s\n", (iValue ? "enabled" : "disabled"))); + return(0); + } + if (iControl == 'play') + { + if (iValue) + { + pHeadset->pPlayerBuffer = (int16_t *) pValue; + pHeadset->uPlayerBufferFrameCurrent = 0; + pHeadset->uPlayerBufferFrames = iValue / (VOIP_HEADSET_SAMPLEWIDTH * VOIP_HEADSET_FRAMESAMPLES); + pHeadset->uPlayerFirstTime = 0; + + pHeadset->iPlayerActive = 1; + } + else + { + pHeadset->iPlayerActive = 0; + } + + NetPrintf(("voipheadsetpc: play %s\n", ((pHeadset->iPlayerActive) ? "enabled" : "disabled"))); + + return(0); + } + if (iControl == 'tran') + { + if (iValue != pHeadset->bVoiceTranscriptionEnabled) + { + pHeadset->bVoiceTranscriptionEnabled = iValue; + NetPrintf(("voipheadsetpc: %s voice transcription locally\n", pHeadset->bVoiceTranscriptionEnabled ? "enabling" : "disabling")); + + // create the transcription module the first time we need it + if (pHeadset->bVoiceTranscriptionEnabled && (pHeadset->pTranscribeRef == NULL)) + { + // allocate transcription module; give it 16k buffer to match ssl max frame size for efficient network transport + if ((pHeadset->pTranscribeRef = VoipTranscribeCreate(16 * 1024)) != NULL) + { + // since this is being created late, set the debug level to the current setting + #if DIRTYCODE_LOGGING + VoipTranscribeControl(pHeadset->pTranscribeRef, 'spam', pHeadset->iDebugLevel, 0, NULL); + #endif + } + else + { + NetPrintf(("voipheadsetpc: unable to allocate transcription manager\n")); + return(-1); + } + } + } + return(0); + } + if (iControl == 'svol') + { + VoipHeadsetSetVolume(pHeadset, iValue, 0); + return(0); + } + if (iControl == 'voic') + { + int iRet = 0; + const VoipSynthesizedSpeechCfgT *pCfg = (const VoipSynthesizedSpeechCfgT *)pValue; + pHeadset->eDefaultGender = (pCfg->iPersonaGender == 1) ? VOIPNARRATE_GENDER_FEMALE : VOIPNARRATE_GENDER_MALE; + + if (pCfg->iLanguagePackCode != 0) + { + iRet = VoipNarrateControl(pHeadset->pNarrateRef, 'lang', pCfg->iLanguagePackCode, 0, NULL); + } + + return(iRet); + } + if (iControl == 'ttos') + { + VoipNarrateGenderE eGender = ((iValue2 > VOIPNARRATE_GENDER_NONE) && (iValue2 < VOIPNARRATE_NUMGENDERS)) ? (VoipNarrateGenderE)iValue2 : pHeadset->eDefaultGender; + return(VoipNarrateInput(pHeadset->pNarrateRef, iValue, eGender, (const char *)pValue)); + } + if (iControl == 'uvoc') + { + VoipNarrateControl(pHeadset->pNarrateRef, 'uvoc', 0, 0, NULL); + return(0); + } + if(iControl == 'xply') + { + uint8_t bCrossplay = iValue ? TRUE : FALSE; + + if (pHeadset->bCrossplay != bCrossplay) + { + NetPrintf(("voipheadsetpc: changing crossplay mode to: %s", bCrossplay ? "crossplay" : "native")); + pHeadset->bCrossplay = bCrossplay; + } + return(0); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetStatus + + \Description + Status function. + + \Input *pHeadset - headset module state + \Input iSelect - control selector + \Input iValue - selector specific + \Input *pBuf - buffer pointer + \Input iBufSize - buffer size + + \Output + int32_t - selector specific, or -1 if no such selector + + \Notes + iSelect can be one of the following: + + \verbatim + 'ndev' - zero + 'idev' - get name of input device at index iValue (-1 returns number of input devices) + 'odev' - get name of output device at index iValue (-1 returns number of input devices) + 'idft' - get the default input device index (as specified in control panel) + 'mute' - get muted status + 'odft' - get the default output device index (as specified in control panel) + 'ruvu' - return TRUE if the given remote user (pBuf) is registered with voipheadset, FALSE if not. + 'sttm' - get the VoipSpeechToTextMetricsT via pBuf + 'ttos' - returns TRUE if TTS is active, else FALSE + 'ttsm' - get the VoipTextToSpeechMetricsT via pBuf + 'xply' - return status on crossplay + + \endverbatim + + \Version 07/28/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipHeadsetStatus(VoipHeadsetRefT *pHeadset, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + if (iSelect == 'ndev') + { + return(0); + } + if ((iSelect == 'idev') || (iSelect == 'odev')) + { + VoipHeadsetDeviceInfoT *pDeviceInfo = (iSelect == 'idev') ? &pHeadset->MicrInfo : &pHeadset->SpkrInfo; + if (iValue == -1) + { + return(pDeviceInfo->iNumDevices); + } + else if ((iValue >= 0) && (iValue < pDeviceInfo->iNumDevices)) + { + ds_memcpy(pBuf, pDeviceInfo->WaveDeviceCaps[iValue].szPname, sizeof(pDeviceInfo->WaveDeviceCaps[iValue].szPname)); + return(iValue); + } + } + if (iSelect == 'idft') + { + DWORD dwPreferredDevice=0; + DWORD dwStatusFlags=0; + DWORD dwRetVal = 0; + + dwRetVal = waveInMessage((HWAVEIN)VOIP_HEADSET_WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, (DWORD_PTR)&dwPreferredDevice, (DWORD_PTR)&dwStatusFlags); + return((int32_t)dwPreferredDevice); + } + if (iSelect == 'mute') + { + return(pHeadset->bMuted); + } + if (iSelect == 'odft') + { + DWORD dwPreferredDevice=0; + DWORD dwStatusFlags; + DWORD dwRetVal; + uint32_t uMessage; + + switch (iValue) + { + case VOIP_DEFAULTDEVICE_VOICECOM: + dwStatusFlags = 0; + uMessage = DRVM_MAPPER_CONSOLEVOICECOM_GET; + break; + + case VOIP_DEFAULTDEVICE_VOICECOM_ONLY: + dwStatusFlags = DRVM_MAPPER_PREFERRED_FLAGS_PREFERREDONLY; + uMessage = DRVM_MAPPER_CONSOLEVOICECOM_GET; + break; + + case VOIP_DEFAULTDEVICE_PREFERRED: + dwStatusFlags = 0; + uMessage = DRVM_MAPPER_PREFERRED_GET; + break; + + case VOIP_DEFAULTDEVICE_PREFERRED_ONLY: + dwStatusFlags = DRVM_MAPPER_PREFERRED_FLAGS_PREFERREDONLY; + uMessage = DRVM_MAPPER_PREFERRED_GET; + break; + + default: + NetPrintf(("voipheadsetpc: iValue=%d, which is not a VoipHeadsetPreferredDeviceTypeE, using default\n", iValue)); + dwStatusFlags = 0; + uMessage = DRVM_MAPPER_CONSOLEVOICECOM_GET; + break; + } + + dwRetVal = waveOutMessage((HWAVEOUT)VOIP_HEADSET_WAVE_MAPPER, uMessage, (DWORD_PTR)&dwPreferredDevice, (DWORD_PTR)&dwStatusFlags); + + return((int32_t)dwPreferredDevice); + } + if (iSelect == 'ruvu') + { + int32_t iRemoteUserSpaceIndex = 0; + + for (iRemoteUserSpaceIndex = 0; iRemoteUserSpaceIndex < pHeadset->iMaxRemoteUsers; ++iRemoteUserSpaceIndex) + { + if (VOIP_SameUser(&pHeadset->aRemoteUsers[iRemoteUserSpaceIndex].User, (VoipUserT *)pBuf)) + { + // remote user found and it is registered with voipheadset + return (TRUE); + } + } + + return(FALSE); + } + if (iSelect == 'sttm') + { + if ((pHeadset->pTranscribeRef != NULL) && (iValue == pHeadset->iParticipatingUserIndex)) + { + return(VoipTranscribeStatus(pHeadset->pTranscribeRef, iSelect, iValue, pBuf, iBufSize)); + } + return(-1); + } + if (iSelect == 'ttos') + { + return(pHeadset->bNarrating); + } + if (iSelect == 'ttsm') + { + if (iValue == pHeadset->iParticipatingUserIndex) + { + return(VoipNarrateStatus(pHeadset->pNarrateRef, iSelect, iValue, pBuf, iBufSize)); + } + return(-1); + } + if (iSelect == 'xply') + { + return(pHeadset->bCrossplay); + } + // unhandled result, fallthrough to active codec + return(VoipCodecStatus(VOIP_CODEC_ACTIVE, iSelect, iValue, pBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetSpkrCallback + + \Description + Set speaker output callback. + + \Input *pHeadset - headset module state + \Input *pCallback - what to call when output data is available + \Input *pUserData - user data for callback + + \Version 12/12/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipHeadsetSpkrCallback(VoipHeadsetRefT *pHeadset, VoipSpkrCallbackT *pCallback, void *pUserData) +{ + pHeadset->pSpkrDataCb = pCallback; + pHeadset->pSpkrCbUserData = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetConfigTranscription + + \Description + Configure the transcribe module + + \Input *pHeadset - headset module state + \Input uProfile - transcribe profile + \Input *pUrl - transcribe provider url + \Input *pKey - transcribe key + + \Version 11/06/2018 (tcho) +*/ +/********************************************************************************F*/ +void VoipHeadsetConfigTranscription(VoipHeadsetRefT *pHeadset, uint32_t uProfile, const char *pUrl, const char *pKey) +{ + VoipTranscribeConfig(uProfile, pUrl, pKey); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetConfigNarration + + \Description + Configure the transcribe module + + \Input *pHeadset - headset module state + \Input uProvider - narrate provider identifier + \Input *pUrl - narrate provider url + \Input *pKey - narrate key + + \Version 1/07/2019 (tcho) +*/ +/********************************************************************************F*/ +void VoipHeadsetConfigNarration(VoipHeadsetRefT *pHeadset, uint32_t uProvider, const char *pUrl, const char *pKey) +{ + VoipNarrateConfig((VoipNarrateProviderE)uProvider, pUrl, pKey); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetSetFirstPartyIdCallback + + \Description + Callback to tell dirtysdk what the first party id is for this user + + \Input *pHeadset - headset module state + \Input *pCallback - what to call when transcribed text is received from remote player + \Input *pUserData - user data for callback + + \Version 04/28/2020 (eesponda) +*/ +/********************************************************************************F*/ +void VoipHeadsetSetFirstPartyIdCallback(VoipHeadsetRefT *pHeadset, VoipFirstPartyIdCallbackCbT *pCallback, void *pUserData) +{ +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/pc/voipnarratepc.cpp b/r5dev/thirdparty/dirtysdk/source/voip/pc/voipnarratepc.cpp new file mode 100644 index 00000000..97aa5585 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/pc/voipnarratepc.cpp @@ -0,0 +1,1146 @@ +/*H********************************************************************************/ +/*! +\File voipnarratepc.cpp + + \Description + Voip narration API wrapping SAPI text to speech APIs for PC + + \Copyright + Copyright 2018 Electronic Arts + + \Version 12/4/2018 (tcho) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ +#pragma warning(push, 0) +#include +#include +#pragma warning(pop) + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtyerr.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "DirtySDK/voip/voipnarrate.h" +/*** Defines **********************************************************************/ +// must be the same as voipheadsetpc +#define VOIPNARRATE_SAMPLEWIDTH (2) //!< sample size; 16-bit samples +#define VOIPNARRATE_FRAMEDURATION (20) //!< frame duration in milliseconds; 20ms +#define VOIPNARRATE_FRAMESAMPLES ((VOIPNARRATE_SAMPLERATE*VOIPNARRATE_FRAMEDURATION)/1000) //!< samples per frame (20ms; 8khz=160, 11.025khz=220.5, 16khz=320) +#define VOIPNARRATE_FRAMESIZE (VOIPNARRATE_FRAMESAMPLES*VOIPNARRATE_SAMPLEWIDTH) //!< frame size in bytes; 640 +#define VOIPNARRATE_READLEN (VOIPNARRATE_FRAMESIZE+30) +#define VOIPNARRATE_BUFFER_LEN (50000) +/*** Macros ***********************************************************************/ +/*** Type Definitions *************************************************************/ + +//! narration request data +typedef struct VoipNarrateRequestT +{ + struct VoipNarrateRequestT *pNext; + VoipNarrateGenderE eGender; + char strText[VOIPNARRATE_INPUT_MAX]; +} VoipNarrateRequestT; + +//! TTS States +typedef enum VoipNarrateVoiceStateE +{ + VOIPNARRATE_VOICE_UNINITIALIZED, + VOIPNARRATE_VOICE_UNINITIALIZING, + VOIPNARRATE_VOICE_READY, + VOIPNARRATE_VOICE_BUSY, + VOIPNARRATE_VOICE_INITIALIZATION_FAILED +} VoipNarrateVoiceStateE; + +//! tts result voice stream +class VoipNarrateVoiceStream : public IStream +{ + public: + VoipNarrateVoiceStream(int32_t iMemGroup, void *pMemGroupUserData); + virtual ~VoipNarrateVoiceStream(); + + // from IStream + HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition); + HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize); + HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags); + HRESULT STDMETHODCALLTYPE Revert(void); + HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType); + HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType); + HRESULT STDMETHODCALLTYPE Clone(IStream **ppStream); + HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag); + HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten); + + // from ISequentialStream + HRESULT STDMETHODCALLTYPE Read(void *pData, ULONG uDataLen, ULONG *pRead); + HRESULT STDMETHODCALLTYPE Write(const void *pData, ULONG uDataLen, ULONG *pWritten); + + // from IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject); + ULONG STDMETHODCALLTYPE AddRef(void); + ULONG STDMETHODCALLTYPE Release(void); + + // useful methods + uint32_t GetDataSize(); + + private: + NetCritT m_BufferCrit; + uint8_t* m_pBuffer; + uint32_t m_uBufferSize; + uint32_t m_uReadPos; + uint32_t m_uWritePos; + int32_t m_iMemGroup; + void *m_pMemGroupUserData; + LONG m_uRefCount; +}; + +struct VoipNarrateRefT +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + VoipNarrateVoiceDataCbT *pVoiceDataCb; //!< user callback used to provide voice data + void *pUserData; //!< user data for user callback + + VoipNarrateRequestT *pRequest; //!< list of queued requests, if any + + VoipTextToSpeechMetricsT Metrics; //!< Usage metrics of the narration module + uint32_t uTtsStartTime; + + uint8_t bFirstSynthOfPhrase; + uint8_t bChangeLang; + int32_t iLangCode; + NetCritT VoiceCrit; + ISpVoice *pVoice; + ISpStream *pVoiceStream; + VoipNarrateVoiceStream *pBaseStream; + VoipNarrateVoiceStateE eVoiceState; + VoipNarrateGenderE eGender; +}; +/*** Private Functions ************************************************************/ +/*F********************************************************************************/ +/*! + \Function _VoipNarrateUninitialize + + \Description + Uninitialize SAPI + + \Input *pVoipNarrate - pointer to module state + + \Version 12/05/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipNarrateUninitialize(VoipNarrateRefT *pVoipNarrate) +{ + // release SpStream interface + if (pVoipNarrate->pVoiceStream != NULL) + { + pVoipNarrate->pVoiceStream->Release(); + pVoipNarrate->pVoiceStream = NULL; + } + + // release SpVoice interface + if (pVoipNarrate->pVoice != NULL) + { + pVoipNarrate->pVoice->Release(); + pVoipNarrate->pVoice = NULL; + CoUninitialize(); + } + + // set state + NetCritEnter(&pVoipNarrate->VoiceCrit); + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_UNINITIALIZED; + NetCritLeave(&pVoipNarrate->VoiceCrit); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateInitialize + + \Description + Initialize SAPI + + \Input *pVoipNarrate - pointer to module state + + \Version 12/05/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipNarrateInitialize(VoipNarrateRefT *pVoipNarrate) +{ + HRESULT hResult; + WAVEFORMATEX WaveFormatEx = { WAVE_FORMAT_PCM, 1, VOIPNARRATE_SAMPLERATE, VOIPNARRATE_SAMPLERATE * VOIPNARRATE_SAMPLEWIDTH, 2, 16, 0 }; + + // initialize COM library + hResult = CoInitialize(NULL); + if (FAILED(hResult) && (hResult != RPC_E_CHANGED_MODE)) + { + NetPrintf(("voipnarratepc: failed to initialize COM library! (err=%s)\n", DirtyErrGetName(hResult))); + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_INITIALIZATION_FAILED; + return; + } + + // create our own IStream + pVoipNarrate->pBaseStream = new (DirtyMemAlloc(sizeof(VoipNarrateVoiceStream), VOIP_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData)) VoipNarrateVoiceStream(pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); + + // create SpVoice interface + if ((hResult = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoipNarrate->pVoice)) != S_OK) + { + NetPrintf(("voipnarratepc: failed to create ISpVoice interface!(err=%s)\n", DirtyErrGetName(hResult))); + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_INITIALIZATION_FAILED; + CoUninitialize(); + return; + } + + // create SpStream interface + if ((hResult = CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, IID_ISpStream, (void **)&pVoipNarrate->pVoiceStream)) != S_OK) + { + NetPrintf(("voipnarratepc: failed to create ISpStream interface!(err=%s)\n", DirtyErrGetName(hResult))); + pVoipNarrate->pVoice->Release(); + pVoipNarrate->pVoice = NULL; + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_INITIALIZATION_FAILED; + CoUninitialize(); + return; + } + + // set the voice stream's base stream and wav format + if (pVoipNarrate->pVoiceStream->SetBaseStream(pVoipNarrate->pBaseStream, SPDFID_WaveFormatEx, &WaveFormatEx) != S_OK) + { + NetPrintf(("voipnarratepc: failed to set base stream!\n")); + } + + // set audio output of the voice interface + if (pVoipNarrate->pVoice->SetOutput(pVoipNarrate->pVoiceStream, TRUE) != S_OK) + { + NetPrintf(("voipnarratepc: failed to set voice stream!\n")); + } + + // set interest + if (pVoipNarrate->pVoice->SetInterest(SPFEI_ALL_TTS_EVENTS, NULL) != S_OK) + { + NetPrintf(("voipnarratepc: failed to set voice interest!\n")); + } + + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_READY; +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateGenderChange + + \Description + Change sythesize voice gender (right only english is supported for now) + + \Input *pVoipNarrate - pointer to module state + \Input eGender - preferred gender for voice narration + + \Output + int32_t - negative=failure, else success + + + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateGenderChange(VoipNarrateRefT *pVoipNarrate, VoipNarrateGenderE eGender) +{ + wchar_t strLang[20]; + wchar_t strGender[20]; + ISpObjectToken *pVoiceToken; + IEnumSpObjectTokens *pVoiceTokenList; + + _snwprintf(strLang, sizeof(strLang), L"Language=%d", pVoipNarrate->iLangCode); + _snwprintf(strGender, sizeof(strGender), L"Gender=%s", eGender == VOIPNARRATE_GENDER_FEMALE ? L"Female" : L"Male"); + + if (SpEnumTokens(SPCAT_VOICES, strLang, strGender, &pVoiceTokenList) != S_OK) + { + NetPrintf(("voipheadsetpc: cannot retrieve voice token list\n")); + return(-1); + } + + pVoiceTokenList->Next(1, &pVoiceToken, NULL); + + if (pVoiceToken != NULL) + { + pVoipNarrate->pVoice->SetVoice(pVoiceToken); + } + else + { + NetPrintf(("voipheadsetpc: Lang:%i Gender:%s voice not found \n", pVoipNarrate->iLangCode, pVoipNarrate->eGender == VOIPNARRATE_GENDER_FEMALE ? "Female" : "Male")); + return(-2); + } + + return(0); +} + + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateRequestAdd + + \Description + Queue request for later sending + + \Input *pVoipNarrate - pointer to module state + \Input iUserIndex - local user index of user who is requesting speech synthesis + \Input eGender - preferred gender for voice narration + \Input *pText - text to be converted + + \Output + int32_t - negative=failure, else success + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateRequestAdd(VoipNarrateRefT *pVoipNarrate, int32_t iUserIndex, VoipNarrateGenderE eGender, const char *pText) +{ + VoipNarrateRequestT *pRequest; + + // allocate and clear the request + if ((pRequest = (VoipNarrateRequestT *)DirtyMemAlloc(sizeof(*pRequest), VOIPNARRATE_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData)) == NULL) + { + NetPrintf(("voipnarratepc: could not allocate request\n")); + pVoipNarrate->Metrics.uErrorCount += 1; + return(-1); + } + ds_memclr(pRequest, sizeof(*pRequest)); + + // copy the request data + ds_strnzcpy(pRequest->strText, pText, sizeof(pRequest->strText)); + pRequest->eGender = eGender; + + // add to queue + pRequest->pNext = pVoipNarrate->pRequest; + pVoipNarrate->pRequest = pRequest; + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateRequestGet + + \Description + Get queued request + + \Input *pVoipNarrate - pointer to module state + \Input *pRequest - [out] storage for request + + \Version 12/04/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipNarrateRequestGet(VoipNarrateRefT *pVoipNarrate, VoipNarrateRequestT *pRequest) +{ + VoipNarrateRequestT **ppRequest; + // get oldest request (we add to head, so get from tail) + for (ppRequest = &pVoipNarrate->pRequest; (*ppRequest)->pNext != NULL; ppRequest = &((*ppRequest)->pNext)) + ; + // copy request + ds_memcpy_s(pRequest, sizeof(*pRequest), *ppRequest, sizeof(**ppRequest)); + // free request + DirtyMemFree(*ppRequest, VOIPNARRATE_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); + // remove from list + *ppRequest = NULL; +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateStart + + \Description + Starts SAPI voice sythesis + + \Input *pVoipNarrate - pointer to module state + \Input eGender - preferred gender for voice for narration + \Input *pText - pointer to text request + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipNarrateStart(VoipNarrateRefT *pVoipNarrate, VoipNarrateGenderE eGender, const char *pText) +{ + HRESULT hResult; + int32_t iStringLength = 0; + int32_t iSizeNeeded = MultiByteToWideChar(CP_UTF8, 0, pText, -1, NULL, 0); + wchar_t *pWideText = (wchar_t *)DirtyMemAlloc((int32_t)(sizeof(wchar_t) * iSizeNeeded), VOIP_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); + + iStringLength = MultiByteToWideChar(CP_UTF8, 0, pText, -1, pWideText, iSizeNeeded); + pVoipNarrate->Metrics.uEventCount++; + pVoipNarrate->Metrics.uCharCountSent += iStringLength; + + // change the gender if needed + if ((pVoipNarrate->eGender != eGender) || (pVoipNarrate->bChangeLang == TRUE)) + { + VoipNarrateGenderE eTempGender = eGender; + + if (eGender == VOIPNARRATE_GENDER_NEUTRAL) + { + eTempGender = pVoipNarrate->eGender; + } + + if (_VoipNarrateGenderChange(pVoipNarrate, eTempGender) == 0) + { + pVoipNarrate->eGender = eGender; + pVoipNarrate->bChangeLang = FALSE; + } + } + + // submit text for sythesis + hResult = pVoipNarrate->pVoice->Speak(pWideText, SPF_ASYNC, 0); + pVoipNarrate->uTtsStartTime = NetTick(); + + DirtyMemFree(pWideText, VOIP_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); + + if (hResult != S_OK) + { + pVoipNarrate->Metrics.uErrorCount++; + return; + } + + pVoipNarrate->bFirstSynthOfPhrase = TRUE; + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_BUSY; +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateProcessResult + + \Description + Receive streamed voice data and submit it to callback + + \Input *pVoipNarrate - pointer to module state + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipNarrateProcessResult(VoipNarrateRefT *pVoipNarrate) +{ + ULONG iReadLen = 0; + SPVOICESTATUS status; + uint8_t buffer[VOIPNARRATE_READLEN]; + VoipNarrateVoiceStream *pStream = (VoipNarrateVoiceStream *)(pVoipNarrate->pBaseStream); + + // get voice synth status + pVoipNarrate->pVoice->GetStatus(&status, NULL); + ds_memclr(&buffer, VOIPNARRATE_READLEN); + + if (status.dwRunningState == SPRS_DONE) + { + if (pStream->GetDataSize() != 0) + { + // time how long it took to get the tts results + if (pVoipNarrate->bFirstSynthOfPhrase) + { + pVoipNarrate->Metrics.uDelay += NetTickDiff(NetTick(), pVoipNarrate->uTtsStartTime); + } + + pStream->Read(&buffer[0], VOIPNARRATE_READLEN, &iReadLen); + pVoipNarrate->Metrics.uDurationMsRecv += ((iReadLen * 1000) / VOIPNARRATE_SAMPLERATE); + + if (iReadLen != 0) + { + // if this is the beginning of a new phrase signal stream start + if (pVoipNarrate->bFirstSynthOfPhrase == TRUE) + { + pVoipNarrate->bFirstSynthOfPhrase = FALSE; + pVoipNarrate->pVoiceDataCb(pVoipNarrate, 0, (const int16_t *)buffer, VOIPNARRATE_STREAM_START, pVoipNarrate->pUserData); + } + pVoipNarrate->pVoiceDataCb(pVoipNarrate, 0, (const int16_t *)buffer, iReadLen, pVoipNarrate->pUserData); + } + } + else + { + if (pVoipNarrate->bFirstSynthOfPhrase) + { + pVoipNarrate->Metrics.uEmptyResultCount++; + } + + // signal stream end + pVoipNarrate->pVoiceDataCb(pVoipNarrate, 0, (const int16_t *)buffer, VOIPNARRATE_STREAM_END, pVoipNarrate->pUserData); + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_READY; + } + } +} + +/*** Public Functions *************************************************************/ +/*F********************************************************************************/ +/*! + \Function VoipNarrateCreate + + \Description + Create the narration module + + \Input *pVoiceDataCb - callback used to provide voice data + \Input *pUserData - callback user data + + \Output + VoipNarrateRefT * - new module state, or NULL + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +VoipNarrateRefT *VoipNarrateCreate(VoipNarrateVoiceDataCbT *pVoiceDataCb, void *pUserData) +{ + VoipNarrateRefT *pVoipNarrate; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // validate callback + if (pVoiceDataCb == NULL) + { + NetPrintf(("voipnarratepc: could not create module with null callback\n")); + return(NULL); + } + + // allocate and init module state + if ((pVoipNarrate = (VoipNarrateRefT *)DirtyMemAlloc(sizeof(*pVoipNarrate), VOIPNARRATE_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipnarratepc: could not allocate module state\n")); + return(NULL); + } + + ds_memclr(pVoipNarrate, sizeof(*pVoipNarrate)); + pVoipNarrate->iMemGroup = iMemGroup; + pVoipNarrate->pMemGroupUserData = pMemGroupUserData; + pVoipNarrate->iLangCode = 409; // 409 is enUS + pVoipNarrate->eGender = VOIPNARRATE_GENDER_MALE; + pVoipNarrate->pVoiceDataCb = pVoiceDataCb; + pVoipNarrate->pUserData = pUserData; + NetCritInit(&pVoipNarrate->VoiceCrit, "voipnarratepc-tts"); + + return(pVoipNarrate); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateConfig + + \Description + Configure the VoipNarrate module + + \Input eProvider - VOIPNARRATE_PROVIDER_* + \Input *pUrl - pointer to url to use for tts requests + \Input *pKey - pointer to authentication key to use for tts requests + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +void VoipNarrateConfig(VoipNarrateProviderE eProvider, const char *pUrl, const char *pKey) +{ + NetPrintf(("voipnarratepc: VoipNarrateConfig() is not implemented on PC\n")); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateDestroy + + \Description + Destroy the VoipNarrate module + + \Input *pVoipNarrate - pointer to module state + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +void VoipNarrateDestroy(VoipNarrateRefT *pVoipNarrate) +{ + VoipNarrateRequestT *pRequest = pVoipNarrate->pRequest; + + // free queued up request + if (pRequest != NULL) + { + for (pRequest = pVoipNarrate->pRequest; pRequest->pNext != NULL; pRequest = pRequest->pNext) + { + DirtyMemFree(pRequest, VOIPNARRATE_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); + } + } + + // dispose of the crit + NetCritKill(&pVoipNarrate->VoiceCrit); + + // dispose of module memory + DirtyMemFree(pVoipNarrate, VOIPNARRATE_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateInput + + \Description + Input text to be convert to speech + + \Input *pVoipNarrate - pointer to module state + \Input iUserIndex - local user index of user who is requesting speech synthesis + \Input eGender - preferred gender for voice narration + \Input *pText - text to be converted + + \Output + int32_t - zero=success, otherwise=failure + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +int32_t VoipNarrateInput(VoipNarrateRefT *pVoipNarrate, int32_t iUserIndex, VoipNarrateGenderE eGender, const char *pText) +{ + return(_VoipNarrateRequestAdd(pVoipNarrate, iUserIndex, eGender, pText)); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateUpdate + + \Description + Update the narration module + + \Input *pVoipNarrate - pointer to module state + + \Version 12/04/2018 (tcho) +*/ +/********************************************************************************F*/ +void VoipNarrateUpdate(VoipNarrateRefT *pVoipNarrate) +{ + // initialize SAPI + if (pVoipNarrate->eVoiceState == VOIPNARRATE_VOICE_UNINITIALIZED) + { + _VoipNarrateInitialize(pVoipNarrate); + } + + // see if we need to start a queued narration request + if ((pVoipNarrate->pRequest != NULL) && (pVoipNarrate->eVoiceState == VOIPNARRATE_VOICE_READY)) + { + VoipNarrateRequestT Request; + _VoipNarrateRequestGet(pVoipNarrate, &Request); + _VoipNarrateStart(pVoipNarrate, Request.eGender, Request.strText); + } + + if (pVoipNarrate->eVoiceState == VOIPNARRATE_VOICE_BUSY) + { + _VoipNarrateProcessResult(pVoipNarrate); + } + + if (pVoipNarrate->eVoiceState == VOIPNARRATE_VOICE_UNINITIALIZING) + { + _VoipNarrateUninitialize(pVoipNarrate); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateStatus + + \Description + Get module status. + + \Input *pVoipNarrate - pointer to module state + \Input iStatus - status selector + \Input iValue - selector specific + \Input *pBuffer - selector specific + \Input iBufSize - selector specific + + \Output + int32_t - selector specific + + \Notes + Other status codes are passed down to the stream transport handler. + + \verbatim + 'ttsm' - get the VoipTextToSpeechMetricsT via pBuffer + \endverbatim + + \Version 12/05/2018 (tcho) +*/ +/********************************************************************************F*/ +int32_t VoipNarrateStatus(VoipNarrateRefT *pVoipNarrate, int32_t iStatus, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + if (iStatus == 'ttsm') + { + if ((pBuffer != NULL) && (iBufSize >= (int32_t)sizeof(VoipTextToSpeechMetricsT))) + { + ds_memcpy_s(pBuffer, iBufSize, &pVoipNarrate->Metrics, sizeof(VoipTextToSpeechMetricsT)); + return(0); + } + return(-1); + } + + return(-1); +} + +/*F********************************************************************************/ +/*! +\Function VoipNarrateControl + + \Description + Set control options + + \Input *pVoipNarrate - pointer to module state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iStatus can be one of the following: + + \verbatim + 'ctsm' - clear text to speech metrics in VoipTextToSpeechMetricsT + 'lang' - set language code + 'uvoc' - uninitialize SAPI + \endverbatim + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipNarrateControl(VoipNarrateRefT *pVoipNarrate, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'ctsm') + { + ds_memclr(&(pVoipNarrate->Metrics), sizeof(pVoipNarrate->Metrics)); + return(0); + } + if (iControl == 'lang') + { + NetCritEnter(&pVoipNarrate->VoiceCrit); + if (pVoipNarrate->iLangCode != iValue) + { + pVoipNarrate->bChangeLang = TRUE; + pVoipNarrate->iLangCode = iValue; + } + NetCritLeave(&pVoipNarrate->VoiceCrit); + return(0); + } + if (iControl == 'uvoc') + { + NetCritEnter(&pVoipNarrate->VoiceCrit); + pVoipNarrate->eVoiceState = VOIPNARRATE_VOICE_UNINITIALIZING; + NetCritLeave(&pVoipNarrate->VoiceCrit); + return(0); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function VoipNarrateVoiceStream + + \Description + Constructor for VoipNarrateVoiceStream + + \Input iMemGroup - iMemGroup + \Input pMemGroupUserData - pMemGroupUserData + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +VoipNarrateVoiceStream::VoipNarrateVoiceStream(int32_t iMemGroup, void *pMemGroupUserData) +{ + m_pBuffer = NULL; + m_uReadPos = 0; + m_uWritePos = 0; + m_uBufferSize = 0; + m_uRefCount = 0; + m_iMemGroup = iMemGroup; + m_pMemGroupUserData = pMemGroupUserData; + NetCritInit(&m_BufferCrit, "voipnarrate-tts-stream"); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \fn ~VoipNarrateVoiceStream + + \Description + Destructor for VoipNarrateVoiceStream + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +VoipNarrateVoiceStream::~VoipNarrateVoiceStream() +{ + if (m_pBuffer != NULL) + { + DirtyMemFree(m_pBuffer, VOIP_MEMID, m_iMemGroup, m_pMemGroupUserData); + } + + NetCritKill(&m_BufferCrit); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function GetDataSize + + \Description + Return current size of the stream + + \Output + uint32_t - size of the stream + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +uint32_t VoipNarrateVoiceStream::GetDataSize() +{ + return m_uWritePos; +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function QueryInterface + + \Description + Returns the interface of the VoipNarrateVoiceStream + + \Input iid - interface id + \Input **ppvObject - pointer to store the pointer to the interface + + \Output + HRESULT - S_OK if successful, E_NOINTERFACE if not successful + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::QueryInterface(REFIID iid, void ** ppvObject) +{ + if (iid == __uuidof(IUnknown) + || iid == __uuidof(IStream) + || iid == __uuidof(ISequentialStream)) + { + *ppvObject = (IStream *)(this); + AddRef(); + return S_OK; + } + else + return E_NOINTERFACE; +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function AddRef + + \Description + Intcrement the ref count for VoipNarrateVoiceStream + + \Output + ULONG - returns the current ref count + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +ULONG STDMETHODCALLTYPE VoipNarrateVoiceStream::AddRef(void) +{ + return (ULONG)InterlockedIncrement(&m_uRefCount); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Release + + \Description + Free the VoipNarrateVoiceStream + + \Output + ULONG - returns the current ref count + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +ULONG STDMETHODCALLTYPE VoipNarrateVoiceStream::Release(void) +{ + ULONG res = (ULONG)InterlockedDecrement(&m_uRefCount); + if (res == 0) + { + this->~VoipNarrateVoiceStream(); + DirtyMemFree(this, VOIP_MEMID, m_iMemGroup, m_pMemGroupUserData); + } + return res; +} + + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Read + + \Description + Read from the stream + + \Input *pData - pointer to buffer where data will be read into + \Input uDataLen - the max size of pData + \Input *pRead - output the actual amount of data that is read + + \Output + HRESULT - S_OK if sucessful, S_FALSE if not + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::Read(void *pData, ULONG uDataLen, ULONG *pRead) +{ + if ((pData == NULL) || (pRead == NULL)) + { + return(STG_E_INVALIDPOINTER); + } + + if (NetCritTry(&m_BufferCrit) == 0) + { + return (S_OK); + } + + // remaining data is bigger than or equal to bytes requested to be read + if ((m_uWritePos - m_uReadPos) >= uDataLen) + { + *pRead = uDataLen; + } + // remaining data is smaller then bytes requested to be read + else + { + *pRead = (m_uWritePos - m_uReadPos); + } + + // check to see if we have a buffer and the read len is not zero + if ((m_pBuffer == NULL) || (*pRead == 0)) + { + *pRead = 0; + NetCritLeave(&m_BufferCrit); + return(S_FALSE); + } + + // copy data + ds_memcpy(pData, m_pBuffer, *pRead); + m_uReadPos += *pRead; + + //compact the buffer + memmove(m_pBuffer, m_pBuffer + m_uReadPos, m_uWritePos - m_uReadPos); + m_uWritePos -= m_uReadPos; + m_uReadPos = 0; + + NetCritLeave(&m_BufferCrit); + return(S_OK); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Write + + \Description + Write to the stream + + \Input *pData - data to be written to stream + \Input uDataLen - the size of pData + \Input *pWritten - output the actual amount of data that is written + + \Output + HRESULT - S_OK if sucessful, S_FALSE if not + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::Write(const void *pData, ULONG uDataLen, ULONG *pWritten) +{ + if (pData == NULL) + { + return(STG_E_INVALIDPOINTER); + } + + NetCritEnter(&m_BufferCrit); + + // check to see if we have enough room to write the data + if ((m_uBufferSize - m_uWritePos) < uDataLen) + { + int32_t iNewBufferSize; + uint8_t *pNewBuffer; + + // set new buffer size + // we are doubling the buffer size everytime + m_uBufferSize ? (iNewBufferSize = 2 * m_uBufferSize) : (iNewBufferSize = VOIPNARRATE_BUFFER_LEN); + + // need to expand buffer + if ((pNewBuffer = (uint8_t *)DirtyMemAlloc(iNewBufferSize, VOIP_MEMID, m_iMemGroup, m_pMemGroupUserData)) == NULL) + { + NetPrintf(("voipheadsetpc: cannot allocate memory for tts stream buffer!\n")); + NetCritLeave(&m_BufferCrit); + return(STG_E_MEDIUMFULL); + } + + ds_memclr(pNewBuffer, iNewBufferSize); + + // cleanup existing pBuffer and copy contents + if (m_pBuffer != NULL) + { + ds_memcpy(pNewBuffer, m_pBuffer, m_uBufferSize); + DirtyMemFree(m_pBuffer, VOIP_MEMID, m_iMemGroup, m_pMemGroupUserData); + } + + m_pBuffer = pNewBuffer; + m_uBufferSize = iNewBufferSize; + } + + // now write the new data into the buffer + ds_memcpy(m_pBuffer + m_uWritePos, pData, uDataLen); + m_uWritePos += uDataLen; + + if (pWritten != NULL) + { + *pWritten = uDataLen; + } + + NetCritLeave(&m_BufferCrit); + return(S_OK); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function CopyTo + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Stat + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function UnlockRegion + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function LockRegion + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Revert + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::Revert(void) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Commit + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::Commit(DWORD grfCommitFlags) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function SetSize + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::SetSize(ULARGE_INTEGER libNewSize) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Clone + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::Clone(IStream **ppStream) +{ + return(E_NOTIMPL); +} + +/*F********************************************************************************/ +/*! + \relates VoipNarrateVoiceStream + \Function Seek + + \Description + Not Implementated + + \Version 05/17/2018 (tcho) +*/ +/********************************************************************************F*/ +HRESULT STDMETHODCALLTYPE VoipNarrateVoiceStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +{ + // need to return S_OK becuase the voice synthesizer expect this to be implementated (but not necessary in our case) + return(S_OK); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/pc/voippc.c b/r5dev/thirdparty/dirtysdk/source/voip/pc/voippc.c new file mode 100644 index 00000000..803b3911 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/pc/voippc.c @@ -0,0 +1,667 @@ +/*H********************************************************************************/ +/*! + \File voippc.c + + \Description + Voip library interface. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 07/27/2004 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#pragma warning(push,0) +#include +#pragma warning(pop) + +// dirtysock includes +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtythread.h" +#include "DirtySDK/dirtysock/netconn.h" + +// voip includes +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "DirtySDK/voip/voipcodec.h" +#include "DirtySDK/voip/voip.h" + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +//! transmission interval in milliseconds +#define VOIP_THREAD_SLEEP_DURATION (20) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! VoIP module state +struct VoipRefT +{ + VoipCommonRefT Common; //!< cross-platform voip data (must come first!) + uint8_t aIsReadyToParticipate[VOIP_MAXLOCALUSERS_EXTENDED]; //!< TRUE if user wanted to participate but was unable (on PC there can only be 1 participant) + DWORD dwThreadId; //!< thread ID + volatile int32_t iThreadState; //!< control variable used during thread creation and exit (0=waiting for start, 1=running, 2=shutdown) + + uint32_t bSpeakerOutput; //!< whether we are outputting through speakers or not +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private Variables + +//! affinity of the voip thread, if unset uses the global DirtySDK thread affinity +static int32_t _Voip_iThreadAffinity = -1; + +// Public Variables + +/*** Private Functions ************************************************************/ + +/*F*************************************************************************************************/ +/*! + \Function _VoipStatusCb + + \Description + Callback to handle change of headset status. + + \Input iLocalUserIndex - headset that has changed (currently ignored) + \Input bStatus - if TRUE the headset was inserted, else if FALSE it was removed + \Input eUpdate - functionality of the headset (input, output or both) + \Input *pUserData - pointer to callback user data + + \Version 1.0 04/01/04 (JLB) First Version +*/ +/*************************************************************************************************F*/ +static void _VoipStatusCb(int32_t iLocalUserIndex, uint32_t bStatus, VoipHeadsetStatusUpdateE eUpdate, void *pUserData) +{ + VoipRefT *pRef = (VoipRefT *)pUserData; + + /* we don't support each user having their own device, we will treat all users + as they have the same device status, since it is likely shared */ + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS; iLocalUserIndex++) + { + if (bStatus > 0) + { + // Set the appropriate flags + if (eUpdate == VOIP_HEADSET_STATUS_INPUT || eUpdate == VOIP_HEADSET_STATUS_INOUT) + { + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] |= VOIP_LOCAL_USER_INPUTDEVICEOK; + } + + if (eUpdate == VOIP_HEADSET_STATUS_OUTPUT || eUpdate == VOIP_HEADSET_STATUS_INOUT) + { + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] |= VOIP_LOCAL_USER_OUTPUTDEVICEOK; + } + } + else + { + // Reset the appropriate flags + if (eUpdate == VOIP_HEADSET_STATUS_INPUT || eUpdate == VOIP_HEADSET_STATUS_INOUT) + { + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] &= ~VOIP_LOCAL_USER_INPUTDEVICEOK; + } + + if (eUpdate == VOIP_HEADSET_STATUS_OUTPUT || eUpdate == VOIP_HEADSET_STATUS_INOUT) + { + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] &= ~VOIP_LOCAL_USER_OUTPUTDEVICEOK; + } + } + + if ((pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] & VOIP_LOCAL_USER_INPUTDEVICEOK) && + (pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] & VOIP_LOCAL_USER_OUTPUTDEVICEOK)) + { + NetPrintf(("voippc: headset active (audio in: on, audio out: on\n)")); + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] |= VOIP_LOCAL_USER_HEADSETOK; + } + else + { + NetPrintf(("voippc: headset inactive (audio in: %s, audio out: %s\n)", + ((pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] & VOIP_LOCAL_USER_INPUTDEVICEOK) ? "on" : "off"), + ((pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] & VOIP_LOCAL_USER_OUTPUTDEVICEOK) ? "on" : "off"))); + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] &= ~VOIP_LOCAL_USER_HEADSETOK; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipActivateLocalTalker + + \Description + Register the participating talker on the given port. + + \Input *pVoipCommon - voip common state + \Input iLocalUserIndex - local user index associated with the user + \Input *pVoipUser - VoipUserT to register + \Input bActivate - TRUE to activate, FALSE to deactivate + + \Output + int32_t - negative if failed, zero if successful + + \Version 04/25/2013 (tcho) +*/ +/********************************************************************************F*/ +static int32_t _VoipActivateLocalTalker(VoipCommonRefT *pVoipCommon, int32_t iLocalUserIndex, VoipUserT *pVoipUser, uint8_t bActivate) +{ + int32_t iResult = 0; + + if (pVoipUser != NULL) + { + VoipHeadsetControl(pVoipCommon->pHeadset, 'aloc', iLocalUserIndex, bActivate, pVoipUser); + + // mark local user as participating, or not + pVoipCommon->Connectionlist.aIsParticipating[iLocalUserIndex] = bActivate; + + // reapply playback muting config based on the channel configuration always (a user who was blocking may be gone now) + pVoipCommon->bApplyChannelConfig = TRUE; + + // announce jip add or remove event on connection that are already active + VoipConnectionReliableBroadcastUser(&pVoipCommon->Connectionlist, iLocalUserIndex, bActivate); + } + else + { + NetPrintf(("voippc: cannot %s a null user %s participating state (local user index %d)\n", + (bActivate ? "promote" : "demote"), (bActivate ? "to" : "from"), iLocalUserIndex)); + + iResult = -1; + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipThread + + \Description + Main Voip thread that updates all active connection and calls the + XHV Engine's DoWork() function. + + \Input *pParam - pointer to voip module state + + \Version 1.0 03/19/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +static void _VoipThread(void *pParam) +{ + VoipRefT *pVoip = (VoipRefT *)pParam; + uint32_t uProcessCt = 0; + uint32_t uTime0, uTime1; + int32_t iSleep; + + // wait until we're started up + while(pVoip->iThreadState == 0) + { + NetConnSleep(VOIP_THREAD_SLEEP_DURATION); + } + + // execute until we're killed + while(pVoip->iThreadState == 1) + { + uTime0 = NetTick(); + + NetCritEnter(&pVoip->Common.ThreadCrit); + NetCritEnter(&pVoip->Common.Connectionlist.NetCrit); + + if (pVoip->Common.bApplyChannelConfig) + { + pVoip->Common.bApplyChannelConfig = FALSE; + VoipCommonApplyChannelConfig((VoipCommonRefT *)pVoip); + } + + // update status of remote users + VoipCommonUpdateRemoteStatus(&pVoip->Common); + NetCritLeave(&pVoip->Common.Connectionlist.NetCrit); + + // update connections + VoipConnectionUpdate(&pVoip->Common.Connectionlist); + + // set headset mute status based on connectionlist send mask + VoipHeadsetControl(pVoip->Common.pHeadset, 'mute', 0, pVoip->Common.Connectionlist.uSendMask == 0, NULL); + + // update headset + VoipHeadsetProcess(pVoip->Common.pHeadset, uProcessCt++); + + // update ttos status + pVoip->Common.uTtsStatus = VoipHeadsetStatus(pVoip->Common.pHeadset, 'ttos', 0, NULL, 0); + + NetCritLeave(&pVoip->Common.ThreadCrit); + + uTime1 = NetTick(); + + iSleep = VOIP_THREAD_SLEEP_DURATION - NetTickDiff(uTime1, uTime0); + + // wait for next tick + if (iSleep >= 0) + { + NetConnSleep(iSleep); + } + else + { + NetPrintf(("voippc: [WARNING] Overall voip thread update took %d ms\n", NetTickDiff(uTime1, uTime0))); + } + } + + // cleanup voice and give voipheadset process time to destroy SAPI related components + VoipHeadsetControl(pVoip->Common.pHeadset, 'uvoc', 0, 0, NULL); + VoipHeadsetProcess(pVoip->Common.pHeadset, uProcessCt++); + + // indicate we're exiting + pVoip->iThreadState = 0; +} + +/*F********************************************************************************/ +/*! + \Function _VoipGetActiveParticipantIndex + + \Description + Gets the user index of the current active user (there can only be one) + + \Input *pVoip - module state + + \Output + int32_t - User index 0-4 or VOIP_INVALID_LOCAL_USER_INDEX if none. + + \Version 03/14/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _VoipGetActiveParticipantIndex(VoipRefT *pVoip) +{ + uint32_t uIndex; + for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; uIndex++) + { + if (pVoip->Common.Connectionlist.aIsParticipating[uIndex] == TRUE) + { + return(uIndex); + } + } + return(VOIP_INVALID_LOCAL_USER_INDEX); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGetFirstReadyParticipantIndex + + \Description + Gets the user index of the first user who would like to be the active user + + \Input *pVoip - module state + + \Output + int32_t - User index 0-4 or VOIP_INVALID_LOCAL_USER_INDEX if none. + + \Version 03/14/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _VoipGetFirstReadyParticipantIndex(VoipRefT *pVoip) +{ + uint32_t uIndex; + for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; uIndex++) + { + if (pVoip->aIsReadyToParticipate[uIndex] == TRUE) + { + return(uIndex); + } + } + return(VOIP_INVALID_LOCAL_USER_INDEX); +} + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipStartup + + \Description + Prepare VoIP for use. + + \Input iMaxPeers - maximum number of peers supported (up to VOIP_MAXCONNECT) + \Input iData - platform-specific - unused for PC + + \Output + VoipRefT - state reference that is passed to all other functions + + \Version 1.0 03/02/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +VoipRefT *VoipStartup(int32_t iMaxPeers, int32_t iData) +{ + VoipRefT *pVoip; + VoipCommonRefT *pVoipCommon; + DirtyThreadConfigT ThreadConfig; + + // common startup + if ((pVoip = VoipCommonStartup(iMaxPeers, sizeof(*pVoip), _VoipStatusCb, iData)) == NULL) + { + return(NULL); + } + pVoipCommon = (VoipCommonRefT *)pVoip; + + // bPrivileged is always TRUE on PC + pVoipCommon->bPrivileged = TRUE; + + // configure thread parameters + ds_memclr(&ThreadConfig, sizeof(ThreadConfig)); + ThreadConfig.pName = "VoIP"; + ThreadConfig.iAffinity = (_Voip_iThreadAffinity == -1) ? NetConnStatus('affn', 0, NULL, 0) : _Voip_iThreadAffinity; + ThreadConfig.iPriority = THREAD_PRIORITY_HIGHEST; + + // create worker thread + if (DirtyThreadCreate(_VoipThread, pVoip, &ThreadConfig) == 0) + { + // tell thread to start executing + pVoip->iThreadState = 1; + } + else + { + pVoip->iThreadState = 0; + VoipShutdown(pVoip, 0); + return(NULL); + } + + // return to caller + return(pVoip); +} + +/*F********************************************************************************/ +/*! + \Function VoipShutdown + + \Description + Release all VoIP resources. + + \Input *pVoip - module state from VoipStartup + \Input uShutdownFlags - NET_SHUTDOWN_* flags + + \Version 03/02/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +void VoipShutdown(VoipRefT *pVoip, uint32_t uShutdownFlags) +{ + // make sure we're really started up + if (VoipGetRef() == NULL) + { + NetPrintf(("voippc: module shutdown called when not in a started state\n")); + return; + } + + // tell thread to shut down and wait for shutdown confirmation (if running) + if (pVoip->iThreadState == 1) + { + for (pVoip->iThreadState = 2; pVoip->iThreadState != 0; ) + { + NetPrintf(("voippc: waiting for shutdown\n")); + NetConnSleep(VOIP_THREAD_SLEEP_DURATION); + } + } + + // unregister local talker + + // common shutdown (must come last as this frees the memory) + VoipCommonShutdown(&pVoip->Common); +} + +/*F********************************************************************************/ +/*! + \Function VoipSetLocalUser + + \Description + Register/unregister specified local user with the voip sub-system. + + \Input *pVoip - module state from VoipStartup + \Input iLocalUserIndex - local user index + \Input bRegister - true to register user, false to unregister user + + \Version 04/15/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipSetLocalUser(VoipRefT *pVoip, int32_t iLocalUserIndex, uint32_t bRegister) +{ + if ((iLocalUserIndex < VOIP_MAXLOCALUSERS) && (iLocalUserIndex >= 0)) + { + NetCritEnter(&pVoip->Common.ThreadCrit); + + if (bRegister) + { + if (VOIP_NullUser(&pVoip->Common.Connectionlist.LocalUsers[iLocalUserIndex])) + { + VoipUserT voipUser; + ds_memclr(&voipUser, sizeof(voipUser)); + + voipUser.ePlatform = VOIP_PLATFORM_PC; + NetConnStatus('acid', iLocalUserIndex, &voipUser.AccountInfo.iAccountId, sizeof(voipUser.AccountInfo.iAccountId)); + NetConnStatus('peid', iLocalUserIndex, &voipUser.AccountInfo.iPersonaId, sizeof(voipUser.AccountInfo.iPersonaId)); + + // register the local talker + NetPrintf(("voippc: registering a local user %lld at index %d\n", voipUser.AccountInfo.iPersonaId, iLocalUserIndex)); + VOIP_CopyUser(&pVoip->Common.Connectionlist.LocalUsers[iLocalUserIndex], &voipUser); + } + else + { + NetPrintf(("voippc: warning -- ignoring attempt to register user index %d because user %lld is already registered at that index\n", + iLocalUserIndex, pVoip->Common.Connectionlist.LocalUsers[iLocalUserIndex].AccountInfo.iPersonaId)); + } + } + else + { + if (!VOIP_NullUser(&pVoip->Common.Connectionlist.LocalUsers[iLocalUserIndex])) + { + // if a participating user demote him first + if (pVoip->Common.Connectionlist.aIsParticipating[iLocalUserIndex] == TRUE) + { + VoipCommonActivateLocalUser(&pVoip->Common, iLocalUserIndex, FALSE); + } + + NetPrintf(("voippc: unregistering local user %lld at index %d\n", pVoip->Common.Connectionlist.LocalUsers[iLocalUserIndex].AccountInfo.iPersonaId, iLocalUserIndex)); + ds_memclr(&pVoip->Common.Connectionlist.LocalUsers[iLocalUserIndex], sizeof(pVoip->Common.Connectionlist.LocalUsers[iLocalUserIndex])); + } + else + { + NetPrintf(("voippc: warning -- ignoring attempt to unregister local user because it is not yet registered\n")); + } + } + + NetCritLeave(&pVoip->Common.ThreadCrit); + } + else + { + NetPrintf(("voippc: VoipSetLocalUser() invoked with an invalid user index %d\n", iLocalUserIndex)); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonActivateLocalUser + + \Description + Promote/demote specified local user to/from "participating" state + + \Input *pVoipCommon - voip common state + \Input iLocalUserIndex - local user index + \Input bActivate - TRUE to activate, FALSE to deactivate + + \Version 04/25/2013 (tcho) +*/ +/********************************************************************************F*/ +void VoipCommonActivateLocalUser(VoipCommonRefT *pVoipCommon, int32_t iLocalUserIndex, uint8_t bActivate) +{ + VoipRefT* pVoip = VoipGetRef(); + if ((iLocalUserIndex < NETCONN_MAXLOCALUSERS) && (iLocalUserIndex >= 0)) + { + NetCritEnter(&pVoipCommon->ThreadCrit); + + if (!VOIP_NullUser(&pVoipCommon->Connectionlist.LocalUsers[iLocalUserIndex])) + { + if (bActivate == TRUE) + { + if (_VoipGetActiveParticipantIndex(pVoip) == VOIP_INVALID_LOCAL_USER_INDEX) + { + NetPrintf(("voippc: promoting local user %lld to participating state.\n", &pVoipCommon->Connectionlist.LocalUsers[iLocalUserIndex].AccountInfo.iPersonaId)); + _VoipActivateLocalTalker(pVoipCommon, iLocalUserIndex, &pVoipCommon->Connectionlist.LocalUsers[iLocalUserIndex], TRUE); + } + else + { + NetPrintf(("voippc: a user is already active, %lld to ready state.\n", &pVoipCommon->Connectionlist.LocalUsers[iLocalUserIndex].AccountInfo.iPersonaId)); + } + pVoip->aIsReadyToParticipate[iLocalUserIndex] = TRUE; + } + else + { + NetPrintf(("voippc: demoting local user %lld from participating/ready state\n", &pVoipCommon->Connectionlist.LocalUsers[iLocalUserIndex].AccountInfo.iPersonaId)); + + // were we the active user, if so deactivate + if (_VoipGetActiveParticipantIndex(pVoip) == iLocalUserIndex) + { + _VoipActivateLocalTalker(pVoipCommon, iLocalUserIndex, &pVoipCommon->Connectionlist.LocalUsers[iLocalUserIndex], FALSE); + } + pVoip->aIsReadyToParticipate[iLocalUserIndex] = FALSE; + + // do we still have an active user? + if (_VoipGetActiveParticipantIndex(pVoip) == VOIP_INVALID_LOCAL_USER_INDEX) + { + // no, but do we have someone who still wants to be the active user? if so make them the active user + int32_t iNewParticpantIndex = _VoipGetFirstReadyParticipantIndex(pVoip); + if (iNewParticpantIndex != VOIP_INVALID_LOCAL_USER_INDEX) + { + NetPrintf(("voippc: promoting local user %lld to participating state\n", &pVoipCommon->Connectionlist.LocalUsers[iNewParticpantIndex].AccountInfo.iPersonaId)); + _VoipActivateLocalTalker(pVoipCommon, iNewParticpantIndex, &pVoipCommon->Connectionlist.LocalUsers[iNewParticpantIndex], TRUE); + } + } + } + } + else + { + NetPrintf(("voippc: VoipActivateLocalUser() failed because no local user registered at index %d\n", iLocalUserIndex)); + } + + NetCritLeave(&pVoipCommon->ThreadCrit); + } + else + { + NetPrintf(("voippc: VoipActivateLocalUser() invoked with an invalid user index %d\n", iLocalUserIndex)); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipStatus + + \Description + Return status. + + \Input *pVoip - module state from VoipStartup + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - status-specific data + + \Notes + iSelect can be one of the following: + + \verbatim + No PC-specific VoipStatus() selectors + \endverbatim + + Unhandled selectors are passed through to VoipCommonStatus(), and + further to VoipHeadsetStatus() if unhandled there. + + \Version 1.0 03/02/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipStatus(VoipRefT *pVoip, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + return(VoipCommonStatus(&pVoip->Common, iSelect, iValue, pBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function VoipControl + + \Description + Set control options. + + \Input *pVoip - module state from VoipStartup + \Input iControl - status selector + \Input iValue - selector-specific + \Input *pValue - register value + + \Output + int32_t - selector-specific data + + \Notes + iControl can be one of the following: + + \verbatim + 'affn' - set the voip thread cpu affinity, please call this before voip startup is called with pVoip being null + '+pbk' - enable voice playback for a given remote user (pValue is the remote user VoipUserT). + '-pbk' - disable voice playback for a given remote user (pValue is the remote user VoipUserT). + 'cdec' - switch to new codec; passes through to VoipHeadsetControl() + 'creg' - register codec + 'plvl' - set the output power level + \endverbatim + + Unhandled selectors are passed through to VoipCommonControl(), and + further to VoipHeadsetControl() if unhandled there. + + \Version 1.0 04/08/2007 (cadam) +*/ +/********************************************************************************F*/ +int32_t VoipControl(VoipRefT *pVoip, int32_t iControl, int32_t iValue, void *pValue) +{ + if ('affn' == iControl) + { + _Voip_iThreadAffinity = iValue; + return(0); + } + if (iControl == 'cdec') + { + int32_t iResult; + // if switching codecs we require sole access to thread critical section + NetCritEnter(&pVoip->Common.ThreadCrit); + iResult = VoipHeadsetControl(pVoip->Common.pHeadset, iControl, iValue, 0, pValue); + NetCritLeave(&pVoip->Common.ThreadCrit); + return(iResult); + } + if (iControl == 'creg') + { + VoipCodecRegister(iValue, pValue); + return(0); + } + if (iControl == '+pbk') + { + return(VoipHeadsetControl(pVoip->Common.pHeadset, '+pbk', iValue, 0, (VoipUserT *)pValue)); + } + if (iControl == '-pbk') + { + return(VoipHeadsetControl(pVoip->Common.pHeadset, '-pbk', iValue, 0, (VoipUserT *)pValue)); + } + if (iControl == 'plvl') + { + int32_t iFractal; + float fValue = *(float *)pValue; + if ((fValue >= 20.0f) || (fValue < 0.0f)) + { + NetPrintf(("voippc: setting power level failed; value must be less than 20 and greater than or equal to 0.")); + return(-1); + } + iFractal = (int32_t)(fValue * (1 << VOIP_CODEC_OUTPUT_FRACTIONAL)); + VoipCodecControl(-1, iControl, iFractal, 0, NULL); + return(0); + } + + // let VoipCommonControl take a shot at it + return(VoipCommonControl(&pVoip->Common, iControl, iValue, pValue)); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/stub/voipcommonstub.c b/r5dev/thirdparty/dirtysdk/source/voip/stub/voipcommonstub.c new file mode 100644 index 00000000..85ab8be8 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/stub/voipcommonstub.c @@ -0,0 +1,498 @@ +/*H********************************************************************************/ +/*! + \File voipcommonstub.c + + \Description + A stub for Voipcommon APIs + + \Copyright + Copyright (c) 2019 Electronic Arts Inc. + + \Version 02/21/2019 (tcho) First Version +*/ +/********************************************************************************H*/ +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/voip/voipdef.h" +#include "DirtySDK/voip/voip.h" + +#include "voippriv.h" +#include "voipcommon.h" + +/*F********************************************************************************/ +/*! + \Function VoipCommonGetRef + + \Description + Return current module reference. + + \Output + VoipRefT * - reference pointer, or NULL if the module is not active + + \Version 02/21/2019 (tcho) +*/ +/********************************************************************************F*/ +VoipRefT *VoipCommonGetRef(void) +{ + return(NULL); +} + +/*F********************************************************************************/ +/*! +\Function VoipCommonStartup + + \Description + Start up common functionality + + \Input iMaxPeers - maximum number of peers supported (up to VOIP_MAXCONNECT) + \Input iVoipRefSize - size of voip ref to allocate + \Input *pStatusCb - headset status callback + \Input iData - platform-specific + + \Output + VoipRefT * - voip ref if successful; else NULL + + \Version 12/02/2009 (jbrookes) +*/ +/********************************************************************************F*/ +VoipRefT *VoipCommonStartup(int32_t iMaxPeers, int32_t iVoipRefSize, VoipHeadsetStatusCbT *pStatusCb, int32_t iData) +{ + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonShutdown + + \Description + Shutdown common functionality + + \Input *pVoipCommon - common module state + + \Version 12/02/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonShutdown(VoipCommonRefT *pVoipCommon) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonUpdateRemoteStatus + + \Description + Process mute list, and set appropriate flags/priority for each remote user. + + \Input *pVoipCommon - pointer to module state + + \Version 08/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonUpdateRemoteStatus(VoipCommonRefT *pVoipCommon) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonStatus + + \Description + Return status. + + \Input *pVoipCommon - voip common state + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector-specific data + + \Version 12/02/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCommonStatus(VoipCommonRefT *pVoipCommon, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonControl + + \Description + Set control options. + + \Input *pVoipCommon - voip common state + \Input iControl - control selector + \Input iValue - selector-specific input + \Input *pValue - selector-specific input + + \Output + int32_t - selector-specific output + + \Version 03/02/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCommonControl(VoipCommonRefT *pVoipCommon, int32_t iControl, int32_t iValue, void *pValue) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonAddMask + + \Description + Add (OR) uAddMask into *pMask + + \Input *pMask - mask to add into + \Input uAddMask - mask to add (OR) + \Input *pMaskName - name of mask (for debug logging) + + \Version 12/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonAddMask(uint32_t *pMask, uint32_t uAddMask, const char *pMaskName) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonDelMask + + \Description + Del (&~) uDelMask from *pMask + + \Input *pMask - mask to del from + \Input uDelMask - mask to del (&~) + \Input *pMaskName - name of mask (for debug logging) + + \Version 12/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonDelMask(uint32_t *pMask, uint32_t uDelMask, const char *pMaskName) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSetMask + + \Description + Set value of mask (with logging). + + \Input *pMask - mask to write to + \Input uNewMask - new mask value + \Input *pMaskName - name of mask (for debug logging) + + \Version 12/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonSetMask(uint32_t *pMask, uint32_t uNewMask, const char *pMaskName) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSelectChannel + + \Description + Select the mode(send/recv) of a given channel. + + \Input *pVoipCommon - common module state + \Input iUserIndex - local user index + \Input iChannel - Channel ID (valid range: [0,63]) + \Input eMode - The mode, combination of VOIP_CHANSEND, VOIP_CHANRECV + + \Output + int32_t - number of channels remaining that this console could join + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipCommonSelectChannel(VoipCommonRefT *pVoipCommon, int32_t iUserIndex, int32_t iChannel, VoipChanModeE eMode) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonApplyChannelConfig + + \Description + Setup user muting flags based on channel config + + \Input *pVoipCommon - voip module state + + \Version 12/02/2009 (jrainy) +*/ +/********************************************************************************F*/ +void VoipCommonApplyChannelConfig(VoipCommonRefT *pVoipCommon) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonResetChannels + + \Description + Resets the channels selection to defaults. Sends and receives to all + + \Input *pVoipCommon - voip common state + \Input iUserIndex - local user index + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +void VoipCommonResetChannels(VoipCommonRefT *pVoipCommon, int32_t iUserIndex) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonMicrophone + + \Description + Select which peers to send voice to + + \Input *pVoipCommon - voip common state + \Input uUserMicrValue - microphone bit values + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +void VoipCommonMicrophone(VoipCommonRefT *pVoipCommon, uint32_t uUserMicrValue) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSpeaker + + \Description + Select which peers to accept voice from + + \Input *pVoipCommon - voip common state + \Input uUserSpkrValue - speaker bit values + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +void VoipCommonSpeaker(VoipCommonRefT *pVoipCommon, uint32_t uUserSpkrValue) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnectionSharingAddSession + + \Description + Add session id to share a specified voip connection + + \Input *pVoipCommon - voip common state + \Input iConnId - connection id + \Input uSessionId - session id we are adding + + \Output + int32_t - zero=success, negative=failure + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnectionSharingAddSession(VoipCommonRefT *pVoipCommon, int32_t iConnId, uint32_t uSessionId) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnectionSharingDelSession + + \Description + Remove session id from sharing a specified voip connection + + \Input *pVoipCommon - voip common state + \Input iConnId - connection id + \Input uSessionId - session id we are removing + + \Output + int32_t - zero=success, negative=failure + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnectionSharingDelSession(VoipCommonRefT* pVoipCommon, int32_t iConnId, uint32_t uSessionId) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonMapVoipServerId + + \Description + For server-based voip, maps a local conn id to a voipserver conn id + + \Input *pVoipCommon - voip common state + \Input iLocalConnId - local connection id + \Input iVoipServerConnId - voipserver connection id + + \Output + int32_t - zero=success, negative=failure + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +int32_t VoipCommonMapVoipServerId(VoipCommonRefT *pVoipCommon, int32_t iLocalConnId, int32_t iVoipServerConnId) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSetLocalClientId + + \Description + Set local client id for connection + + \Input *pVoipCommon - voip common state + \Input iConnId - connection id + \Input uLocalClientId - local client id + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +void VoipCommonSetLocalClientId(VoipCommonRefT *pVoipCommon, int32_t iConnId, uint32_t uLocalClientId) +{ +} + +/*F*************************************************************************************************/ +/*! + \Function VoipCommonSetDisplayTranscribedTextCallback + + \Description + Set callback to be invoked when transcribed text (from local user or remote user) + is ready to be displayed locally. + + \Input *pVoipCommon - voip common state + \Input *pCallback - notification handler + \Input *pUserData - user data for handler + + \Version 05/03/2017 (mclouatre) +*/ +/*************************************************************************************************F*/ +void VoipCommonSetDisplayTranscribedTextCallback(VoipCommonRefT *pVoipCommon, VoipDisplayTranscribedTextCallbackT *pCallback, void *pUserData) +{ +} + +/*F*************************************************************************************************/ +/*! + \Function VoipCommonSetEventCallback + + \Description + Set voip event notification handler. + + \Input *pVoipCommon - voip common state + \Input *pCallback - event notification handler + \Input *pUserData - user data for handler + + \Version 02/10/2006 (jbrookes) +*/ +/*************************************************************************************************F*/ +void VoipCommonSetEventCallback(VoipCommonRefT *pVoipCommon, VoipCallbackT *pCallback, void *pUserData) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnect + + \Description + Connect to a peer. + + \Input *pVoipCommon - voip common state + \Input iConnID - [zero, iMaxPeers-1] for an explicit slot or VOIP_CONNID_NONE to auto-allocate + \Input uAddress - remote peer address + \Input uManglePort - port from demangler + \Input uGamePort - port to connect on + \Input uClientId - remote clientId to connect to (cannot be 0) + \Input uSessionId - session identifier (optional) + + \Output + int32_t - connection identifier (negative=error) + + \Version 1.0 03/02/2004 (jbrookes) first version + \Version 1.1 10/26/2009 (mclouatre) uClientId is no longer optional +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnect(VoipCommonRefT *pVoipCommon, int32_t iConnID, uint32_t uAddress, uint32_t uManglePort, uint32_t uGamePort, uint32_t uClientId, uint32_t uSessionId) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonDisconnect + + \Description + Disconnect from peer. + + \Input *pVoipCommon - voip common state + \Input iConnID - which connection to disconnect (VOIP_CONNID_ALL for all) + \Input bSendDiscMsg - TRUE if a voip disc pkt needs to be sent, FALSE otherwise + + \Todo + Multiple connection support. + + \Version 15/01/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipCommonDisconnect(VoipCommonRefT *pVoipCommon, int32_t iConnID, int32_t bSendDiscMsg) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnStatus + + \Description + Return information about peer connection. + + \Input *pVoipCommon - voip common state + \Input iConnID - which connection to get remote info for, or VOIP_CONNID_ALL + + \Output + int32_t - VOIP_CONN* flags, or VOIP_FLAG_INVALID if iConnID is invalid + + \Version 05/06/2014 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnStatus(VoipCommonRefT *pVoipCommon, int32_t iConnID) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonRemoteUserStatus + + \Description + Return information about remote peer. + + \Input *pVoipCommon - voip common state + \Input iConnID - which connection to get remote info for, or VOIP_CONNID_ALL + \Input iRemoteUserIndex - user index at the connection iConnID + + \Output + int32_t - VOIP_REMOTE* flags, or VOIP_FLAG_INVALID if iConnID is invalid + + \Version 05/06/2014 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t VoipCommonRemoteUserStatus(VoipCommonRefT *pVoipCommon, int32_t iConnID, int32_t iRemoteUserIndex) +{ + return(0); +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/source/voip/stub/voipheadsetstub.c b/r5dev/thirdparty/dirtysdk/source/voip/stub/voipheadsetstub.c new file mode 100644 index 00000000..b0dfe8c5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/stub/voipheadsetstub.c @@ -0,0 +1,328 @@ +/*H********************************************************************************/ +/*! + \File voipheadsetstub.c + + \Description + Stubbed VoIP headset manager. To be used on platform where voip connectivity + is to be exercised but not voice acquisition/playback. Typically needed + for stress testing voip connectivity with dummy voip traffic. + + \Copyright + Copyright Electronic Arts 2017. + + \Version 12/06/2017 (mclouatre) +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "voipconnection.h" +#include "voipheadset.h" + +/*** Defines **********************************************************************/ + +/*** Macros ************************************************************************/ + +/*** Type Definitions **************************************************************/ + +//! VOIP module state data +struct VoipHeadsetRefT +{ + int32_t bParticipating; //!< local user is now in "participating" state + + // speaker callback data + VoipSpkrCallbackT *pSpkrDataCb; + void *pSpkrCbUserData; +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Public Variables + +// Private Variables + +/*** Private Functions ************************************************************/ + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetCreate + + \Description + Create the headset manager. + + \Input iMaxConduits - max number of conduits + \Input *pMicDataCb - pointer to user callback to trigger when mic data is ready + \Input *pTextDataCb - pointer to user callback to trigger when transcribed text is ready + \Input *pOpaqueDataCb - pointer to user callback to trigger when opaque data is ready + \Input *pStatusCb - pointer to user callback to trigger when headset status changes + \Input *pCbUserData - pointer to user callback data + \Input iData - platform-specific - unused for PC + + \Output + VoipHeadsetRefT * - pointer to module state, or NULL if an error occured + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +VoipHeadsetRefT *VoipHeadsetCreate(int32_t iMaxConduits, VoipHeadsetMicDataCbT *pMicDataCb, VoipHeadsetTextDataCbT *pTextDataCb, VoipHeadsetOpaqueDataCbT *pOpaqueDataCb, VoipHeadsetStatusCbT *pStatusCb, void *pCbUserData, int32_t iData) +{ + VoipHeadsetRefT *pHeadset; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // make sure we don't exceed maxconduits + if (iMaxConduits > VOIP_MAXCONNECT) + { + NetPrintf(("voipheadsetstub: request for %d conduits exceeds max\n", iMaxConduits)); + return(NULL); + } + + // allocate and clear module state + if ((pHeadset = DirtyMemAlloc(sizeof(*pHeadset), VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + return(NULL); + } + ds_memclr(pHeadset, sizeof(*pHeadset)); + + // return module ref to caller + return(pHeadset); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetDestroy + + \Description + Destroy the headset manager. + + \Input *pHeadset - pointer to headset state + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipHeadsetDestroy(VoipHeadsetRefT *pHeadset) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + // dispose of module memory + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + DirtyMemFree(pHeadset, VOIP_MEMID, iMemGroup, pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetReceiveVoiceDataCb + + \Description + Connectionlist callback to handle receiving a voice packet from a remote peer. + + \Input *pRemoteUsers - user we're receiving the voice data from + \Input iRemoteUserSize - pRemoteUsers array size + \Input iConsoleId - generic identifier for the console to which the users belong + \Input *pMicrInfo - micr info from inbound packet + \Input *pPacketData - pointer to beginning of data in packet payload + \Input *pUserData - VoipHeadsetT ref + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipHeadsetReceiveVoiceDataCb(VoipUserT *pRemoteUsers, int32_t iRemoteUserSize, int32_t iConsoleId, VoipMicrInfoT *pMicrInfo, uint8_t *pPacketData, void *pUserData) +{ + +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetRegisterUserCb + + \Description + Connectionlist callback to register/unregister a new user with the + VoipConduit module. + + \Input *pRemoteUser - user to register + \Input iConsoleId - generic identifier for the console to which the user belongs (ignored) + \Input bRegister - true=register, false=unregister + \Input *pUserData - voipheadset module ref + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipHeadsetRegisterUserCb(VoipUserT *pRemoteUser, int32_t iConsoleId, uint32_t bRegister, void *pUserData) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetProcess + + \Description + Headset process function. + + \Input *pHeadset - pointer to headset state + \Input uFrameCount - process iteration counter + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipHeadsetProcess(VoipHeadsetRefT *pHeadset, uint32_t uFrameCount) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetSetVolume + + \Description + Sets play and record volume. + + \Input *pHeadset - pointer to headset state + \Input iPlayVol - play volume to set + \Input iRecVol - record volume to set + + \Notes + To not set a value, specify it as -1. + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipHeadsetSetVolume(VoipHeadsetRefT *pHeadset, int32_t iPlayVol, uint32_t iRecVol) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetControl + + \Description + Control function. + + \Input *pHeadset - headset module state + \Input iControl - control selector + \Input iValue - control value + \Input iValue2 - control value + \Input *pValue - control value + + \Output + int32_t - selector specific, or -1 if no such selector + + \Notes + iControl can be one of the following: + + \verbatim + 'aloc' - promote/demote to/from participating state + 'cide' - close voip input device + 'code' - close voip output device + 'edev' - enumerate voip input/output devices + 'idev' - select voip input device + 'loop' - enable/disable loopback + 'micr' - enable/disable recording + 'odev' - select voip output device + 'play' - enable/disable playing + 'svol' - changes speaker volume + \endverbatim + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t VoipHeadsetControl(VoipHeadsetRefT *pHeadset, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'aloc') + { + pHeadset->bParticipating = ((iValue2 == 0) ? FALSE : TRUE); + + NetPrintf(("voipheadsetstub: %s participating state\n", pHeadset->bParticipating ? "entering" : "exiting")); + + return(0); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetStatus + + \Description + Status function. + + \Input *pHeadset - headset module state + \Input iSelect - control selector + \Input iValue - selector specific + \Input *pBuf - buffer pointer + \Input iBufSize - buffer size + + \Output + int32_t - selector specific, or -1 if no such selector + + \Notes + iSelect can be one of the following: + + \verbatim + 'ruvu' - always return TRUE because MLU is not supported on unix + \endverbatim + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t VoipHeadsetStatus(VoipHeadsetRefT *pHeadset, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + if (iSelect == 'ruvu') + { + return (TRUE); + } + // unhandled result + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetSpkrCallback + + \Description + Set speaker output callback. + + \Input *pHeadset - headset module state + \Input *pCallback - what to call when output data is available + \Input *pUserData - user data for callback + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipHeadsetSpkrCallback(VoipHeadsetRefT *pHeadset, VoipSpkrCallbackT *pCallback, void *pUserData) +{ + pHeadset->pSpkrDataCb = pCallback; + pHeadset->pSpkrCbUserData = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipHeadsetSetFirstPartyIdCallback + + \Description + Callback to tell dirtysdk what the first party id is for this user + + \Input *pHeadset - headset module state + \Input *pCallback - what to call when transcribed text is received from remote player + \Input *pUserData - user data for callback + + \Version 04/28/2020 (eesponda) +*/ +/********************************************************************************F*/ +void VoipHeadsetSetFirstPartyIdCallback(VoipHeadsetRefT *pHeadset, VoipFirstPartyIdCallbackCbT *pCallback, void *pUserData) +{ +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/stub/voipstub.c b/r5dev/thirdparty/dirtysdk/source/voip/stub/voipstub.c new file mode 100644 index 00000000..fcd3f5a9 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/stub/voipstub.c @@ -0,0 +1,501 @@ +/*H********************************************************************************/ +/*! + \File voipstub.c + + \Description + Voip library interface. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 0.5 03/02/2004 (jbrookes) Implemented stubbed interface. +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voipcodec.h" +#include "voippriv.h" +#include "voipcommon.h" + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! VoIP module state +struct VoipRefT +{ + //! module memory group + int32_t iMemGroup; + void *pMemGroupUserData; +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Private Variables + +//! pointer to module state +static VoipRefT *_Voip_pRef = NULL; + +// Public Variables + + +/*** Private Functions ************************************************************/ + + +/*** Public Functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipStartup + + \Description + Prepare VoIP for use. + + \Input iMaxPeers - maximum number of peers supported (up to 32) + \Input iData - platform-specific - unused for voipstub + + \Output + VoipRefT - state reference that is passed to all other functions + + \Version 1.0 03/02/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +VoipRefT *VoipStartup(int32_t iMaxPeers, int32_t iData) +{ + VoipRefT *pVoip; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // make sure we're not already started + if (_Voip_pRef != NULL) + { + NetPrintf(("voip: module startup called when not in a shutdown state\n")); + return(NULL); + } + + // create and initialize module state + if ((pVoip = (VoipRefT *)DirtyMemAlloc(sizeof(*pVoip), VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voip: unable to allocate module state\n")); + return(NULL); + } + pVoip->iMemGroup = iMemGroup; + pVoip->pMemGroupUserData = pMemGroupUserData; + + // save ref and return to caller + _Voip_pRef = pVoip; + return(pVoip); +} + +/*F********************************************************************************/ +/*! + \Function VoipGetRef + + \Description + Return current module reference. + + \Output + VoipRefT * - reference pointer, or NULL if the module is not active + + \Version 1.0 03/08/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +VoipRefT *VoipGetRef(void) +{ + // return pointer to module state + return(_Voip_pRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipShutdown + + \Description + Release all VoIP resources. + + \Input *pVoip - module state from VoipStartup + \Input uShutdownFlags - NET_SHUTDOWN_* flags + + \Version 03/02/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +void VoipShutdown(VoipRefT *pVoip, uint32_t uShutdownFlags) +{ + // make sure we're really started up + if (_Voip_pRef == NULL) + { + NetPrintf(("voip: module shutdown called when not in a started state\n")); + return; + } + + // free module memory + DirtyMemFree(pVoip, VOIP_MEMID, pVoip->iMemGroup, pVoip->pMemGroupUserData); + + // clear pointer to module state + _Voip_pRef = NULL; +} + + +/*F********************************************************************************/ +/*! + \Function VoipSetLocalUser + + \Description + Register/unregister specified local user with the voip sub-system. + + \Input *pVoip - module state from VoipStartup + \Input iLocalUserIndex - local user index (ignored because MLU not supported on PC) + \Input bRegister - ignored + + \Version 04/15/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipSetLocalUser(VoipRefT *pVoip, int32_t iLocalUserIndex, uint32_t bRegister) +{ + +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonActivateLocalUser + + \Description + Promote/demote specified local user to/from "participating" state + + \Input *pVoipCommon - voip common state + \Input iLocalUserIndex - local user index (ignored because MLU not supported on PC) + \Input bActivate - TRUE to activate, FALSE to deactivate + + \Version 04/25/2013 (tcho) +*/ +/********************************************************************************F*/ +void VoipCommonActivateLocalUser(VoipCommonRefT *pVoipCommon, int32_t iLocalUserIndex, uint8_t bActivate) +{ + +} + +/*F********************************************************************************/ +/*! + \Function VoipStatus + + \Description + Return status. + + \Input *pVoip - module state from VoipStartup + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector-specific data + + \Version 1.0 03/02/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipStatus(VoipRefT *pVoip, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + if (iSelect == 'avlb') + { + return(TRUE); + } + if (iSelect == 'from') + { + return(0); + } + if (iSelect == 'micr') + { + return(0); + } + if (iSelect == 'sock') + { + return(0); + } + if (iSelect == 'spkr') + { + return(0); + } + // unsupported selector + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function VoipControl + + \Description + Set control options. + + \Input *pVoip - module state from VoipStartup + \Input iControl - control selector + \Input iValue - selector-specific + \Input *pValue - selector-specific + + \Output + int32_t - selector-specific data + + \Version 1.0 03/02/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +int32_t VoipControl(VoipRefT *pVoip, int32_t iControl, int32_t iValue, void *pValue) +{ + return(0); +} + +/*F*************************************************************************************************/ +/*! + \Function VoipSpkrCallback + + \Description + Set speaker data callback. + + \Input *pVoip - voip module state + \Input *pCallback - data callback + \Input *pUserData - user data + + \Version 1.0 12/12/2005 (jbrookes) First Version +*/ +/*************************************************************************************************F*/ +void VoipSpkrCallback(VoipRefT *pVoip, VoipSpkrCallbackT *pCallback, void *pUserData) +{ +} + +/*F*************************************************************************************************/ +/*! + \Function VoipCodecControl + + \Description + Stub for codec control + + \Input iCodecIdent - codec identifier, or VOIP_CODEC_ACTIVE + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Version 1.0 10/11/2011 (jbrookes) First Version +*/ +/*************************************************************************************************F*/ +int32_t VoipCodecControl(int32_t iCodecIdent, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipSelectChannel + + \Description + Stub for VoipSelectChannel + + \Input *pVoip - voip module state + \Input iUserIndex - local user index + \Input iChannel - Channel ID (valid range: [0,63]) + \Input eMode - The mode, combination of VOIPGROUP_CHANSEND, VOIPGROUP_CHANRECV + + \Output + int32_t - number of channels remaining that this console could join + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipSelectChannel(VoipRefT *pVoip, int32_t iUserIndex, int32_t iChannel, VoipChanModeE eMode) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipResetChannels + + \Description + Stub for VoipResetChannels + + \Input *pVoip - module ref + \Input iUserIndex - local user index + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +void VoipResetChannels(VoipRefT *pVoip, int32_t iUserIndex) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipLocalUserStatus + + \Description + Return information about local hardware state + + \Input *pVoip - module state from VoipStartup + \Input iLocalUserIndex - local index of the user + + \Output + int32_t - VOIP_LOCAL* flags + + \Version 1.0 03/02/2004 (jbrookes) First Version + \Version 2.0 04/25/2014 (amakoukji) Refactored for MLU +*/ +/********************************************************************************F*/ +int32_t VoipLocalUserStatus(VoipRefT *pVoip, int32_t iLocalUserIndex) +{ + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListCreate + + \Description + Creates the VoipBlockListT. + + \Output + VoipBlockListT * - The Blocked List state + + \Version 05/02/2019 (cvienneau) +*/ +/********************************************************************************F*/ +VoipBlockListT *VoipBlockListCreate(void) +{ + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListDestroy + + \Description + Free the voip block list + + \Input pVoip - voip state + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +void VoipBlockListDestroy(VoipRefT *pVoip) +{ +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListAdd + + \Description + Add a user to be blocked by the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user + \Input iBlockedAccountId - the account id of the user to be blocked + + \Output + uint8_t - TRUE if the user was successfully blocked + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListAdd(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iBlockedAccountId) +{ + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListRemove + + \Description + Remove a user that was blocked by the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user + \Input iBlockedAccountId - the account id of the user who was blocked + + \Output + uint8_t - TRUE if the user was successfully un-blocked + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListRemove(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iBlockedAccountId) +{ + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListIsBlocked + + \Description + Check if a user is blocked by the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user + \Input iBlockedAccountId - the account id of the user who was blocked + + \Output + uint8_t - TRUE if the user is blocked + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListIsBlocked(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iBlockedAccountId) +{ + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListClear + + \Description + Clear the blocked list for the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user (-1 for all users) + + \Output + uint8_t - TRUE if the list was cleared + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListClear(VoipRefT *pVoip, int32_t iLocalUserIndex) +{ + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function VoipRegisterFirstPartyIdCallback + + \Description + Register first party id callback + + \Input *pVoip - module ref + \Input *pCallback - first party id callback + \Input *pUserData - callback user data + + \Version 05/08/2019 (tcho) +*/ +/********************************************************************************F*/ +void VoipRegisterFirstPartyIdCallback(VoipRefT *pVoip, VoipFirstPartyIdCallbackCbT *pCallback, void *pUserData) +{ +} \ No newline at end of file diff --git a/r5dev/thirdparty/dirtysdk/source/voip/unix/voipunix.c b/r5dev/thirdparty/dirtysdk/source/voip/unix/voipunix.c new file mode 100644 index 00000000..02ae35c7 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/unix/voipunix.c @@ -0,0 +1,413 @@ +/*H********************************************************************************/ +/*! + \File voipunix.c + + \Description + voip interface (not production ready - for DirtyCast voip stress testing client) + + \Copyright + Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED. + + \Version 06/08/2017 (mclouatre) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +// dirtysock includes +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +// voip includes +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "DirtySDK/voip/voip.h" + + +/*** Defines **********************************************************************/ +#define VOIP_THREAD_SLEEP_DURATION (20) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! VoIP module state +struct VoipRefT +{ + VoipCommonRefT Common; //!< cross-platform voip data (must come first!) + + // dummy traffic injection config + uint32_t uSendSeqn; //!< send sequence number + int32_t iDummyVoipSubPacketSize; //!< size of test packets (in bytes) + uint32_t uDummyVoipSendPeriod; //!< rate of artificially injected subpackets 0: disabled; [1,1024]: 0% to 100% of voip bandwidth produced over a 20-sec window + uint32_t uDummyVoipWrappingCounter; //!< 32-bit wrapping counter used to implement the right dummy send period +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _VoipStatusCb + + \Description + Callback to handle change of headset status. + + \Input iLocalUserIndex - headset that has changed + \Input bStatus - if TRUE the headset was inserted, else if FALSE it was removed + \Input eUpdate - functionality of the headset (input, output or both) + \Input *pUserData - pointer to callback user data + + \Notes + It is assumed that eUpdate will always be VOIP_HEADSET_STATUS_INOUT for + this platform. + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipStatusCb(int32_t iLocalUserIndex, uint32_t bStatus, VoipHeadsetStatusUpdateE eUpdate, void *pUserData) +{ + VoipRefT *pRef = (VoipRefT *)pUserData; + + if (eUpdate != VOIP_HEADSET_STATUS_INOUT) + { + NetPrintf(("voipunix: warning, unexpected headset event for this platform!\n")); + } + + if (bStatus) + { + NetPrintf(("voipunix: [%d] headset inserted\n", iLocalUserIndex)); + pRef->Common.uPortHeadsetStatus |= 1 << iLocalUserIndex; + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] |= (VOIP_LOCAL_USER_HEADSETOK|VOIP_LOCAL_USER_INPUTDEVICEOK|VOIP_LOCAL_USER_OUTPUTDEVICEOK); + } + else + { + NetPrintf(("voipunix: [%d] headset removed\n", iLocalUserIndex)); + pRef->Common.uPortHeadsetStatus &= ~(1 << iLocalUserIndex); + pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] &= ~(VOIP_LOCAL_USER_HEADSETOK|VOIP_LOCAL_USER_INPUTDEVICEOK|VOIP_LOCAL_USER_OUTPUTDEVICEOK); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipInjectDummyTraffic + + \Description + Inject dummy voip traffic on all active voip connections + + \Input *pVoip - module state + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipInjectDummyTraffic(VoipRefT *pVoip) +{ + static uint8_t DummyVoipSubPacket[VOIP_MAXMICRPKTSIZE]; + VoipConnectionlistT *pConnectionlist = &pVoip->Common.Connectionlist; + + // only inject dummy sub-packets if the 1024-large counter is in between [0, pVoip->uDummyVoipSendPeriod[ + if ((++pVoip->uDummyVoipWrappingCounter % 1024) < pVoip->uDummyVoipSendPeriod) + { + // send dummy traffic on all active connections + VoipConnectionSend(pConnectionlist, 0xFFFFFFFF, &DummyVoipSubPacket[0], pVoip->iDummyVoipSubPacketSize, NULL, 0, 0, pVoip->uSendSeqn++); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipUpdate + + \Description + Update function to be used in a single-threaded environment + + \Input *pVoip - voip ref + + \Version 01/18/2018 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipUpdate(VoipRefT *pVoip) + { + uint32_t uTime0, uTime1; + int32_t iSleep; + + uTime0 = NetTick(); + + if (pVoip->Common.bApplyChannelConfig) + { + pVoip->Common.bApplyChannelConfig = FALSE; + VoipCommonApplyChannelConfig((VoipCommonRefT *)pVoip); + } + + // update status of remote users + VoipCommonUpdateRemoteStatus(&pVoip->Common); + VoipConnectionUpdate(&pVoip->Common.Connectionlist); + + // inject dummy voip traffic for stress test purposes + _VoipInjectDummyTraffic(pVoip); + + uTime1 = NetTick(); + + iSleep = VOIP_THREAD_SLEEP_DURATION - NetTickDiff(uTime1, uTime0); + + // wait for next tick + if (iSleep >= 0) + { + NetConnSleep(iSleep); + } + else + { + NetPrintf(("voipunix: [WARNING] Overall voip update took %d ms\n", NetTickDiff(uTime1, uTime0))); + } +} + +/*** Public Functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function VoipStartup + + \Description + Prepare VoIP for use. + + \Input iMaxPeers - maximum number of peers supported (up to VOIP_MAXCONNECT) + \Input iData - platform-specific - unused on Unix + + \Output + VoipRefT - state reference that is passed to all other functions + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +VoipRefT *VoipStartup(int32_t iMaxPeers, int32_t iData) +{ + VoipRefT *pVoip; + + NetPrintf(("voipunix: WARNING - VoipUnix is not for production. Usage reserved for DirtyCast voip stress testing only!\n")); + + // common startup + if ((pVoip = VoipCommonStartup(iMaxPeers, sizeof(*pVoip), _VoipStatusCb, iData)) == NULL) + { + return(NULL); + } + + // injection of dummy traffic is disabled by default + pVoip->iDummyVoipSubPacketSize = 0; + pVoip->uDummyVoipSendPeriod = 0; + + // bPrivileged is always TRUE on unix + pVoip->Common.bPrivileged = TRUE; + + // return to caller + return(pVoip); +} + +/*F********************************************************************************/ +/*! + \Function VoipShutdown + + \Description + Release all VoIP resources. + + \Input *pVoip - module state from VoipStartup + \Input uShutdownFlags - NET_SHUTDOWN_* flags + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipShutdown(VoipRefT *pVoip, uint32_t uShutdownFlags) +{ + // make sure we're really started up + if (VoipGetRef() == NULL) + { + NetPrintf(("voipunix: module shutdown called when not in a started state\n")); + return; + } + + // common shutdown (must come last as this frees the memory) + VoipCommonShutdown(&pVoip->Common); +} + +/*F********************************************************************************/ +/*! + \Function VoipSetLocalUser + + \Description + Register/unregister specified local user with the voip sub-system. + + \Input *pVoip - module state from VoipStartup + \Input iLocalUserIndex - local user index (ignored because MLU not supported on PC) + \Input bRegister - ignored + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipSetLocalUser(VoipRefT *pVoip, int32_t iLocalUserIndex, uint32_t bRegister) +{ + if (bRegister) + { + if (VOIP_NullUser(&pVoip->Common.Connectionlist.LocalUsers[0])) + { + VoipUserT voipUser; + ds_memclr(&voipUser, sizeof(voipUser)); + + voipUser.ePlatform = VOIP_PLATFORM_LINUX; + NetConnStatus('acid', iLocalUserIndex, &voipUser.AccountInfo.iAccountId, sizeof(voipUser.AccountInfo.iAccountId)); + NetConnStatus('peid', iLocalUserIndex, &voipUser.AccountInfo.iPersonaId, sizeof(voipUser.AccountInfo.iPersonaId)); + + NetPrintf(("voipunix: registering a local user %llx at index %d\n", voipUser.AccountInfo.iPersonaId, iLocalUserIndex)); + VOIP_CopyUser(&pVoip->Common.Connectionlist.LocalUsers[0], &voipUser); + } + else + { + NetPrintf(("voipunix: warning -- ignoring attempt to register local user because user %llx is already registered\n", pVoip->Common.Connectionlist.LocalUsers[0].AccountInfo.iPersonaId)); + } + } + else + { + if (!VOIP_NullUser(&pVoip->Common.Connectionlist.LocalUsers[0])) + { + // if a participating user demote him first + if (pVoip->Common.Connectionlist.aIsParticipating[0] == TRUE) + { + VoipCommonActivateLocalUser(&pVoip->Common, 0, FALSE); + } + + NetPrintf(("voipunix: unregistering local user %llx\n", pVoip->Common.Connectionlist.LocalUsers[0].AccountInfo.iPersonaId)); + ds_memclr(&pVoip->Common.Connectionlist.LocalUsers[0], sizeof(pVoip->Common.Connectionlist.LocalUsers[0])); + } + else + { + NetPrintf(("voipunix: warning -- ignoring attempt to unregister local user because it is not yet registered\n")); + } + } +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonActivateLocalUser + + \Description + Promote/demote specified local user to/from "participating" state + + \Input *pVoipCommon - voip common state + \Input iLocalUserIndex - local user index (ignored because MLU not supported on PC) + \Input bActivate - TRUE to activate, FALSE to deactivate + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipCommonActivateLocalUser(VoipCommonRefT *pVoipCommon, int32_t iLocalUserIndex, uint8_t bActivate) +{ + if (!VOIP_NullUser(&pVoipCommon->Connectionlist.LocalUsers[0])) + { + if (bActivate == TRUE) + { + NetPrintf(("voipunix: promoting user %llx to participating state\n", &pVoipCommon->Connectionlist.LocalUsers[0].AccountInfo.iPersonaId)); + pVoipCommon->Connectionlist.aIsParticipating[0] = TRUE; + + // reapply playback muting config based on the channel configuration only if this is a participating user + pVoipCommon->bApplyChannelConfig = TRUE; + } + else + { + NetPrintf(("voipunix: demoting user %llx from participating state\n", &pVoipCommon->Connectionlist.LocalUsers[0].AccountInfo.iPersonaId)); + pVoipCommon->Connectionlist.aIsParticipating[0] = FALSE; + } + VoipHeadsetControl(pVoipCommon->pHeadset, 'aloc', 0, bActivate, &pVoipCommon->Connectionlist.LocalUsers[0]); + } + else + { + NetPrintf(("voipunix: VoipCommonActivateLocalUser() failed because no local user registered\n")); + } +} + + +/*F********************************************************************************/ +/*! + \Function VoipStatus + + \Description + Return status. + + \Input *pVoip - module state from VoipStartup + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector-specific data + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t VoipStatus(VoipRefT *pVoip, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + return(VoipCommonStatus(&pVoip->Common, iSelect, iValue, pBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function VoipControl + + \Description + Set control options. + + \Input *pVoip - module state from VoipStartup + \Input iControl - control selector + \Input iValue - selector-specific + \Input *pValue - selector-specific + + \Notes + iControl can be one of the following: + + \verbatim + 'psiz' - set size of artificially injected test voip sub-packets + 'prat' - artificially injected voip sub-packets period --> 0: disabled; [1,1024]: 0% to 100% of voip bandwidth produced over a 20-sec window + 'updt' - to pump the voip sub-system in a single-threaded context + \endverbatim + + Unhandled selectors are passed through to VoipCommonControl(), and + further to VoipHeadsetControl() if unhandled there. + + \Version 06/12/2017 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t VoipControl(VoipRefT *pVoip, int32_t iControl, int32_t iValue, void *pValue) +{ + if (iControl == 'psiz') + { + NetPrintf(("voipunix: size of artificially injected test voip sub-packets changed from %d bytes to %d bytes\n", pVoip->iDummyVoipSubPacketSize, iValue)); + pVoip->iDummyVoipSubPacketSize = iValue; + return(0); + } + if (iControl == 'prat') + { + NetPrintf(("voipunix: artificially injected voip sub-packets period changed from %d/1024 to %d/1024\n", pVoip->uDummyVoipSendPeriod, iValue)); + pVoip->uDummyVoipSendPeriod = iValue; + return(0); + } + if (iControl == 'updt') + { + if (VoipGetRef()) + { + _VoipUpdate(VoipGetRef()); + return(0); + } + else + { + NetPrintf(("voipunix: module update called when not in a started state\n")); + return(-1); + } + } + return(VoipCommonControl(&pVoip->Common, iControl, iValue, pValue)); +} + + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voip.c b/r5dev/thirdparty/dirtysdk/source/voip/voip.c new file mode 100644 index 00000000..e4261f89 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voip.c @@ -0,0 +1,214 @@ +/*H********************************************************************************/ +/*! + \File voip.c + + \Description + Cross-platform public voip functions. + + \Copyright + Copyright (c) 2009 Electronic Arts Inc. + + \Version 12/02/2009 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#ifdef _XBOX +#include +#include +#endif + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/netconn.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "DirtySDK/voip/voip.h" + +/*** Defines **********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipLocalUserStatus + + \Description + Return information about local hardware state + + \Input *pVoip - module state from VoipStartup + \Input iLocalUserIndex - local index of the user + + \Output + int32_t - VOIP_LOCAL* flags + + \Version 1.0 03/02/2004 (jbrookes) First Version + \Version 2.0 04/25/2014 (amakoukji) Refactored for MLU +*/ +/********************************************************************************F*/ +int32_t VoipLocalUserStatus(VoipRefT *pVoip, int32_t iLocalUserIndex) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pVoip; + uint32_t uLocalStatus = pVoipCommon->Connectionlist.uLocalUserStatus[iLocalUserIndex]; + + return(uLocalStatus); +} + +/*F********************************************************************************/ +/*! + \Function VoipGetRef + + \Description + Return current module reference. + + \Output + VoipRefT * - reference pointer, or NULL if the module is not active + + \Version 1.0 03/08/2004 (jbrookes) First Version +*/ +/********************************************************************************F*/ +VoipRefT *VoipGetRef(void) +{ + return(VoipCommonGetRef()); +} + +/*F*************************************************************************************************/ +/*! + \Function VoipSpkrCallback + + \Description + Set speaker data callback (not supported on all platforms). + + \Input *pVoip - voip module state + \Input *pCallback - data callback + \Input *pUserData - user data + + \Version 12/12/2005 (jbrookes) +*/ +/*************************************************************************************************F*/ +void VoipSpkrCallback(VoipRefT *pVoip, VoipSpkrCallbackT *pCallback, void *pUserData) +{ + #if defined(DIRTYCODE_PC) || defined(DIRTYCODE_STADIA) + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pVoip; + VoipHeadsetSpkrCallback(pVoipCommon->pHeadset, pCallback, pUserData); + #endif +} + +/*F********************************************************************************/ +/*! + \Function VoipSelectChannel + + \Description + Select the mode(send/recv) of a given channel. + + \Input *pVoip - voip module state + \Input iUserIndex - local user index + \Input iChannel - Channel ID (valid range: [0,63]) + \Input eMode - The mode, combination of VOIP_CHANSEND, VOIP_CHANRECV + + \Output + int32_t - number of channels remaining that this console could join + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipSelectChannel(VoipRefT *pVoip, int32_t iUserIndex, int32_t iChannel, VoipChanModeE eMode) +{ + return(VoipCommonSelectChannel((VoipCommonRefT*)pVoip, iUserIndex, iChannel, eMode)); +} + +/*F********************************************************************************/ +/*! + \Function VoipResetChannels + + \Description + Resets the channels selection to defaults. Sends and receives to all + + \Input *pVoip - module ref + \Input iUserIndex - local user index + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +void VoipResetChannels(VoipRefT *pVoip, int32_t iUserIndex) +{ + VoipCommonResetChannels((VoipCommonRefT*)pVoip, iUserIndex); +} + +/*F********************************************************************************/ +/*! + \Function VoipConfigTranscription + + \Description + Configure voice transcription + + \Input *pVoip - module ref + \Input uProfile - transcribe profile + \Input *pUrl - transcribe provider url + \Input *pKey - transcribe key + + \Version 11/06/2018 (tcho) +*/ +/********************************************************************************F*/ +void VoipConfigTranscription(VoipRefT *pVoip, uint32_t uProfile, const char *pUrl, const char *pKey) +{ + #if defined(DIRTYCODE_PC) || defined(DIRTYCODE_PS4) || defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) || defined(DIRTYCODE_STADIA) + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pVoip; + VoipHeadsetConfigTranscription(pVoipCommon->pHeadset, uProfile, pUrl, pKey); + #endif +} + +/*F********************************************************************************/ +/*! + \Function VoipConfigNarration + + \Description + Configure voice transcription + + \Input *pVoip - module ref + \Input uProvider - narration profile + \Input *pUrl - narration provider url + \Input *pKey - narration key + + \Version 11/06/2018 (tcho) +*/ +/********************************************************************************F*/ +void VoipConfigNarration(VoipRefT *pVoip, uint32_t uProvider, const char *pUrl, const char *pKey) +{ + #if defined(DIRTYCODE_PS4) || defined(DIRTYCODE_STADIA) + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pVoip; + VoipHeadsetConfigNarration(pVoipCommon->pHeadset, uProvider, pUrl, pKey); + #endif +} + +/*F********************************************************************************/ +/*! + \Function VoipRegisterFirstPartyIdCallback + + \Description + Register first party id callback + + \Input *pVoip - module ref + \Input *pCallback - first party id callback + \Input *pUserData - callback user data + + \Version 05/08/2019 (tcho) +*/ +/********************************************************************************F*/ +void VoipRegisterFirstPartyIdCallback(VoipRefT *pVoip, VoipFirstPartyIdCallbackCbT *pCallback, void *pUserData) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pVoip; + VoipHeadsetSetFirstPartyIdCallback(pVoipCommon->pHeadset, pCallback, pUserData); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipblocklist.c b/r5dev/thirdparty/dirtysdk/source/voip/voipblocklist.c new file mode 100644 index 00000000..2aab83df --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipblocklist.c @@ -0,0 +1,423 @@ + +/*H********************************************************************************/ +/*! + \File voipblocklist.c + + \Description + Impliments blocking voip communications based off users accountid + + \Copyright + Copyright (c) 2019 Electronic Arts Inc. + + \Version 07/02/2019 (cvienneau) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include // for qsort + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" + +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voipblocklist.h" + + +/*** Defines **********************************************************************/ +#define VOIPBLOCKLIST_DEFAULT_LIST_SIZE (64) +#define VOIPBLOCKLIST_MAX_LIST_SIZE (2000) + +/*** Type Definitions *************************************************************/ +typedef struct VoipBlockListUserT +{ + int64_t *pBlockList; + uint32_t uCapacity; + uint32_t uConsumed; + uint8_t bSorted; +} VoipBlockListUserT; + +typedef struct VoipBlockListT +{ + VoipBlockListUserT aUserBlockLists[VOIP_MAXLOCALUSERS]; + int32_t iMemGroup; + void *pMemGroupUserData; +} VoipBlockListT; + +/*** Variables ********************************************************************/ + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _VoipBlockListSort + + \Description + qsort callback used to sort fast lookup array. + + \Input *_pElem0 - pointer to first element to compare + \Input *_pElem1 - pointer to second element to compare + + \Output + int32_t - sort value + + \Version 06/19/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _VoipBlockListSort(const void *_pElem0, const void *_pElem1) +{ + int64_t arg0 = *(const int64_t*)_pElem0; + int64_t arg1 = *(const int64_t*)_pElem1; + + if (arg0 < arg1) return -1; + if (arg0 > arg1) return 1; + return 0; +} + +/*F********************************************************************************/ +/*! + \Function _VoipBlockListFindAccountId + + \Description + Sorts the given users Blocked list, then finds the index of the BlockedAccountId + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user + \Input iBlockedAccountId - the account id of the user to be found + + \Output + int32_t - the index of the blocked account id, or -1 if not found + + \Version 06/20/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static int32_t _VoipBlockListFindAccountId(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iBlockedAccountId) +{ + int32_t iCheck, iLow, iHigh; + int64_t iCheckId; + VoipBlockListT *pBlockedList = ((VoipCommonRefT *)pVoip)->pBlockList; + + if (pBlockedList->aUserBlockLists[iLocalUserIndex].bSorted == FALSE) + { + qsort(pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList, pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed, sizeof(pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList[0]), _VoipBlockListSort); + pBlockedList->aUserBlockLists[iLocalUserIndex].bSorted = TRUE; + } + + // execute binary search on sorted lookup table + for (iLow = 0, iHigh = pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed - 1; iLow <= iHigh; ) + { + iCheck = iLow + ((iHigh - iLow) / 2); + if ((iCheckId = pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList[iCheck]) > iBlockedAccountId) + { + iHigh = iCheck - 1; + } + else if (iCheckId < iBlockedAccountId) + { + iLow = iCheck + 1; + } + else + { + return(iCheck); + } + } + // not found + return(-1); +} + +/*** Public functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function VoipBlockListCreate + + \Description + Creates the VoipBlockListT. + + \Output + VoipBlockListT * - The Blocked List state + + \Version 05/02/2019 (cvienneau) +*/ +/********************************************************************************F*/ +VoipBlockListT *VoipBlockListCreate(void) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + VoipBlockListT *pBlockedList = NULL; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + if ((pBlockedList = DirtyMemAlloc(sizeof(*pBlockedList), VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipblocklist: could not allocate block list state.\n")); + return(NULL); + } + ds_memclr(pBlockedList, sizeof(*pBlockedList)); + pBlockedList->iMemGroup = iMemGroup; + pBlockedList->pMemGroupUserData = pMemGroupUserData; + + return(pBlockedList); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListDestroy + + \Description + Free the voip block list + + \Input pVoip - voip state + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +void VoipBlockListDestroy(VoipRefT *pVoip) +{ + VoipBlockListT *pBlockedList = ((VoipCommonRefT *)pVoip)->pBlockList; + + // to free memory used by each user + VoipBlockListClear(pVoip, -1); + + DirtyMemFree(pBlockedList, VOIP_MEMID, pBlockedList->iMemGroup, pBlockedList->pMemGroupUserData); + ((VoipCommonRefT *)pVoip)->pBlockList = NULL; +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListAdd + + \Description + Add a user to be blocked by the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user + \Input iBlockedAccountId - the account id of the user to be blocked + + \Output + uint8_t - TRUE if the user was successfully blocked + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListAdd(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iBlockedAccountId) +{ + VoipBlockListT *pBlockedList = ((VoipCommonRefT *)pVoip)->pBlockList; + + // early out if parameters are bad + if (iLocalUserIndex < 0 || iLocalUserIndex >= VOIP_MAXLOCALUSERS) + { + NetPrintf(("voipblocklist: error, user index %d passed to VoipBlockListAdd is not valid.\n", iLocalUserIndex)); + return(FALSE); + } + + // create/resize the blocked list as needed + if (pBlockedList->aUserBlockLists[iLocalUserIndex].uCapacity < (pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed + 1)) + { + int64_t *pOldList = pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList; + uint32_t uNewCapacity = VOIPBLOCKLIST_DEFAULT_LIST_SIZE; + + // figgure out the size of the new blocked list + if (pBlockedList->aUserBlockLists[iLocalUserIndex].uCapacity != 0) + { + if (pBlockedList->aUserBlockLists[iLocalUserIndex].uCapacity == VOIPBLOCKLIST_MAX_LIST_SIZE) + { + NetPrintf(("voipblocklist: error, user index %d's has no room in blocked list to add %lld.\n", iBlockedAccountId)); + return(FALSE); + } + uNewCapacity = 2 * pBlockedList->aUserBlockLists[iLocalUserIndex].uCapacity; + + if (uNewCapacity > VOIPBLOCKLIST_MAX_LIST_SIZE) + { + uNewCapacity = VOIPBLOCKLIST_MAX_LIST_SIZE; + } + } + + // allocate the new list + if ((pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList = DirtyMemAlloc(sizeof(int64_t) * uNewCapacity, VOIP_MEMID, pBlockedList->iMemGroup, pBlockedList->pMemGroupUserData)) == NULL) + { + NetPrintf(("voipblocklist: could not allocate block list for user %d.\n", iLocalUserIndex)); + return(FALSE); + } + ds_memclr(pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList, sizeof(int64_t) * uNewCapacity); + pBlockedList->aUserBlockLists[iLocalUserIndex].uCapacity = uNewCapacity; + + // copy any contents of the old list to the new list and free it + if (pOldList != NULL) + { + ds_memcpy(pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList, pOldList, sizeof(int64_t) * pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed); + DirtyMemFree(pOldList, VOIP_MEMID, pBlockedList->iMemGroup, pBlockedList->pMemGroupUserData); + } + } + + // fill the last slot of the blocked list with the new value + pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList[pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed] = iBlockedAccountId; + pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed += 1; + pBlockedList->aUserBlockLists[iLocalUserIndex].bSorted = FALSE; + if (VoipGetRef() != NULL) + { + ((VoipCommonRefT *)pVoip)->bApplyChannelConfig = TRUE; + } + NetPrintf(("voipblocklist: user index %d blocked %lld.\n", iLocalUserIndex, iBlockedAccountId)); + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListRemove + + \Description + Remove a user that was blocked by the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user + \Input iBlockedAccountId - the account id of the user who was blocked + + \Output + uint8_t - TRUE if the user was successfully un-blocked + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListRemove(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iBlockedAccountId) +{ + uint32_t uBlockedIndex; + VoipBlockListT *pBlockedList = ((VoipCommonRefT *)pVoip)->pBlockList; + + // early out if parameters are bad + if (iLocalUserIndex < 0 || iLocalUserIndex >= VOIP_MAXLOCALUSERS) + { + NetPrintf(("voipblocklist: error, user index %d passed to VoipBlockListRemove is not valid.\n", iLocalUserIndex)); + return(FALSE); + } + + // look for the blocked user + // note we don't use _VoipBlockListFindAccountId here since if the calling pattern is to call Remove several times in a row + // we don't want to sort it over and over and removing no longer ensuers its sorted + for (uBlockedIndex = 0; uBlockedIndex < pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed; uBlockedIndex++) + { + if (pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList[uBlockedIndex] == iBlockedAccountId) + { + // copy the last entry over the to be removed entry + pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList[uBlockedIndex] = pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList[pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed - 1]; + + //remove the last entry + pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList[pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed - 1] = 0; + pBlockedList->aUserBlockLists[iLocalUserIndex].uConsumed -= 1; + pBlockedList->aUserBlockLists[iLocalUserIndex].bSorted = FALSE; + + if (VoipGetRef() != NULL) + { + ((VoipCommonRefT *)pVoip)->bApplyChannelConfig = TRUE; + } + NetPrintf(("voipblocklist: BlockList, user index %d un-blocked %lld.\n", iLocalUserIndex, iBlockedAccountId)); + return(TRUE); + } + } + NetPrintf(("voipblocklist: BlockList warning, user index %d could not find %lld in blocked list to remove it.\n", iLocalUserIndex, iBlockedAccountId)); + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListIsBlocked + + \Description + Check if a user is blocked by the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user + \Input iBlockedAccountId - the account id of the user who was blocked + + \Output + uint8_t - TRUE if the user is blocked + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListIsBlocked(VoipRefT *pVoip, int32_t iLocalUserIndex, int64_t iBlockedAccountId) +{ + // if its the shared local user index then we really need to test all the local users + if (iLocalUserIndex == VOIP_SHARED_USER_INDEX) + { + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS; iLocalUserIndex++) + { + if (VoipBlockListIsBlocked(pVoip, iLocalUserIndex, iBlockedAccountId) == TRUE) + { + return(TRUE); + } + } + } + // deal with individual users + else + { + // early out if parameters are bad + if (iLocalUserIndex < 0 || iLocalUserIndex >= VOIP_MAXLOCALUSERS) + { + NetPrintf(("voipblocklist: error, user index %d passed to VoipBlockListIsBlocked is not valid.\n", iLocalUserIndex)); + return(FALSE); + } + + // look for the blocked user + if (_VoipBlockListFindAccountId(pVoip, iLocalUserIndex, iBlockedAccountId) >= 0) + { + return(TRUE); + } + } + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function VoipBlockListClear + + \Description + Clear the blocked list for the local user + + \Input pVoip - voip state + \Input iLocalUserIndex - the index of the local user (-1 for all users) + + \Output + uint8_t - TRUE if the list was cleared + + \Version 05/07/2019 (cvienneau) +*/ +/********************************************************************************F*/ +uint8_t VoipBlockListClear(VoipRefT *pVoip, int32_t iLocalUserIndex) +{ + VoipBlockListT *pBlockedList = ((VoipCommonRefT *)pVoip)->pBlockList; + + if (iLocalUserIndex == -1) + { + int32_t iUserLoopIndex; + for (iUserLoopIndex = 0; iUserLoopIndex < VOIP_MAXLOCALUSERS; iUserLoopIndex++) + { + VoipBlockListClear(pVoip, iUserLoopIndex); + } + } + else + { + // early out if parameters are bad + if ((iLocalUserIndex < 0) || (iLocalUserIndex >= VOIP_MAXLOCALUSERS)) + { + NetPrintf(("voipblocklist: error, user index %d passed to VoipBlockListClear is not valid.\n", iLocalUserIndex)); + return(FALSE); + } + + // deallocate + DirtyMemFree(pBlockedList->aUserBlockLists[iLocalUserIndex].pBlockList, VOIP_MEMID, pBlockedList->iMemGroup, pBlockedList->pMemGroupUserData); + ds_memclr(&pBlockedList->aUserBlockLists[iLocalUserIndex], sizeof(pBlockedList->aUserBlockLists[iLocalUserIndex])); + NetPrintf(("voipblocklist: BlockList, cleared blocked list for user index %d.\n", iLocalUserIndex)); + } + + if (VoipGetRef() != NULL) + { + ((VoipCommonRefT *)pVoip)->bApplyChannelConfig = TRUE; + } + return(TRUE); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipcodec.c b/r5dev/thirdparty/dirtysdk/source/voip/voipcodec.c new file mode 100644 index 00000000..6b66f738 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipcodec.c @@ -0,0 +1,564 @@ +/*H********************************************************************************/ +/*! + \File voipcodec.c + + \Description + VoIP codec manager. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 08/04/2004 (jbrookes) First version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" + +#include "DirtySDK/voip/voipdef.h" +#include "DirtySDK/voip/voiptranscribe.h" +#include "DirtySDK/voip/voipcodec.h" + +/*** Defines **********************************************************************/ + +#define VOIPCODEC_MAXENTRIES (8) +#define VOIPCODEC_TIMER (DIRTYCODE_LOGGING && FALSE) + +//! default VAD thresholds +#define VOIPCODEC_DEFAULT_VAD_AMPLITUDE_THRESHOLD (0.00015f) +#define VOIPCODEC_DEFAULT_VAD_FRAMES_THRESHOLD (32) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct VoipCodecRegT +{ + int32_t iCodecIdent; + const VoipCodecDefT *pCodecDef; + VoipCodecRefT *pCodecRef; +} VoipCodecRegT; + + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +//! codec reg table +static VoipCodecRegT _VoipCodec_RegTable[VOIPCODEC_MAXENTRIES]; + +//! number of registered codecs +static int32_t _VoipCodec_iNumCodecs = 0; + +//! active codec +static int32_t _VoipCodec_iActiveCodec = -1; + + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _VoipCodecGet + + \Description + Get the specified codec. + + \Input iCodecIdent - codec ident or VOIP_CODEC_ACTIVE + \Input *pIndex - [out, optional] storage for codec index + + \Output + VoipCodecRegT * - pointer to specified codec, or NULL + + \Version 10/10/2011 (jbrookes) +*/ +/********************************************************************************F*/ +static VoipCodecRegT *_VoipCodecGet(int32_t iCodecIdent, int32_t *pIndex) +{ + VoipCodecRegT *pRegEntry = NULL; + int32_t iCodec; + + // zero is reserved/invalid + if (iCodecIdent == 0) + { + return(NULL); + } + + // search for codec ident + if (iCodecIdent != VOIP_CODEC_ACTIVE) + { + // look up codec by ident + for (iCodec = 0; iCodec < _VoipCodec_iNumCodecs; iCodec++) + { + if (_VoipCodec_RegTable[iCodec].iCodecIdent == iCodecIdent) + { + pRegEntry = &_VoipCodec_RegTable[iCodec]; + if (pIndex != NULL) + { + *pIndex = iCodec; + } + break; + } + } + } + else if (_VoipCodec_iActiveCodec != -1) + { + pRegEntry = &_VoipCodec_RegTable[_VoipCodec_iActiveCodec]; + if (pIndex != NULL) + { + *pIndex = _VoipCodec_iActiveCodec; + } + } + + return(pRegEntry); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipCodecVadProcess16BitFrame + + \Description + Use this function to pass next frame of 16-bit voice samples to the VAD engine. + + \Input *pVoipCodecRef - codec reference pointer + \Input *pVoiceFrame - pointer to voice buffer + \Input iNumSamples - number of samples in voice frame + + \Output + uint8_t - TRUE if voice is considered silent after processing that frame, FALSE otherwise + + \Version 06/13/2012 (mclouatre) +*/ +/*************************************************************************************************F*/ +static uint8_t _VoipCodecVadProcess16BitFrame(VoipCodecRefT *pVoipCodecRef, const int16_t *pVoiceFrame, int32_t iNumSamples) +{ + int32_t iCount; + float fAmp, fPower, fPowerSum; + + fAmp = fPower = fPowerSum = 0.0f; + + // walk the frame contents and calculate the amplitude of the signal + for (iCount = 0; iCount < iNumSamples; iCount += 1) + { + fAmp = (float)abs(pVoiceFrame[iCount]); + fPowerSum += (fAmp * fAmp); + } + + fPower = (fPowerSum / (32768.0f * 32768.0f * (float)iNumSamples)); + + // save current power level for external visibility + pVoipCodecRef->fPowerLevel = fPower; + + if (fPower < pVoipCodecRef->fVadPowerThreshold) + { + if (pVoipCodecRef->iVadCumulSilenceFrames > pVoipCodecRef->iVadSilenceFramesThreshold) + { + if (!pVoipCodecRef->bVadSilent) + { + NetPrintfVerbose((pVoipCodecRef->iDebugLevel, 1, "voipcodec: silence detected (fPowerThreshold=%f, fPower=%f)\n", + pVoipCodecRef->fVadPowerThreshold, fPower)); + pVoipCodecRef->bVadSilent = TRUE; + } + } + else + { + pVoipCodecRef->iVadCumulSilenceFrames++; + } + } + else + { + if (pVoipCodecRef->bVadSilent) + { + NetPrintfVerbose((pVoipCodecRef->iDebugLevel, 1, "voipcodec: voice detected (fPowerThreshold=%f, fPower=%f)\n", + pVoipCodecRef->fVadPowerThreshold, fPower)); + pVoipCodecRef->bVadSilent = FALSE; + } + pVoipCodecRef->iVadCumulSilenceFrames = 0; + } + + return(pVoipCodecRef->bVadSilent); +} + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipCodecRegister + + \Description + Register a codec in the table. + + \Input iCodecIdent - identifier with which the codec will be tagged + \Input *pCodecDef - pointer to codec definition table + + \Version 08/04/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCodecRegister(int32_t iCodecIdent, const VoipCodecDefT *pCodecDef) +{ + int32_t iCodec; + + for (iCodec = _VoipCodec_iNumCodecs; iCodec < VOIPCODEC_MAXENTRIES; iCodec++) + { + if (_VoipCodec_RegTable[iCodec].iCodecIdent == 0) + { + NetPrintf(("voipcodec: registering codec '%C'\n", iCodecIdent)); + _VoipCodec_RegTable[iCodec].iCodecIdent = iCodecIdent; + _VoipCodec_RegTable[iCodec].pCodecDef = pCodecDef; + _VoipCodec_iNumCodecs++; + return; + } + } + + NetPrintf(("voipcodec: unable to register codec '%C'\n", iCodecIdent)); +} + +/*F********************************************************************************/ +/*! + \Function VoipCodecCreate + + \Description + Create a codec module of the given type. + + \Input iCodecIdent - identifier with which the codec will be tagged + \Input iDecodeChannels - number of decoder channels + + \Output + int32_t - zero=success, negative=failure + + \Version 08/04/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCodecCreate(int32_t iCodecIdent, int32_t iDecodeChannels) +{ + VoipCodecRegT *pRegEntry = NULL; + int32_t iCodec = 0; + + // get codec reg entry + if ((pRegEntry = _VoipCodecGet(iCodecIdent, &iCodec)) == NULL) + { + NetPrintf(("voipcodec: codec '%C' is not registered\n", iCodecIdent)); + return(-1); + } + + // if there is an active codec, destroy it + VoipCodecDestroy(); + + // create it and return to caller + if ((pRegEntry->pCodecRef = pRegEntry->pCodecDef->pCreate(iDecodeChannels)) != NULL) + { + NetPrintf(("voipcodec: created codec '%C'\n", iCodecIdent)); + _VoipCodec_iActiveCodec = iCodec; + } + else + { + NetPrintf(("voipcodec: unable to create codec '%C'\n", iCodecIdent)); + return(-1); + } + + // initialize default values + pRegEntry->pCodecRef->fVadPowerThreshold = VOIPCODEC_DEFAULT_VAD_AMPLITUDE_THRESHOLD; + pRegEntry->pCodecRef->iVadSilenceFramesThreshold = VOIPCODEC_DEFAULT_VAD_FRAMES_THRESHOLD; + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipCodecControl + + \Description + Control codec. + + \Input iCodecIdent - codec identifier, or VOIP_CODEC_ACTIVE + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + '+vad' - enable/disable vad + 'vada' - signal level threshold used to detect silence in a single frame (in millionths) + 'vadf' - number of silence frames required for silence detection to kick in + \endverbatim + + \Version 08/09/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCodecControl(int32_t iCodecIdent, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + VoipCodecRegT *pRegEntry; + + // make sure active codec is valid + if ((pRegEntry = _VoipCodecGet(iCodecIdent, NULL)) == NULL) + { + NetPrintf(("voipcodec: invalid codec, cannot process control message '%C'\n", iControl)); + return(-1); + } + + if (iControl == '+vad') + { + if (pRegEntry->pCodecRef->bVadEnabled != (iValue?TRUE:FALSE)) + { + NetPrintf(("voipcodec: VAD mode changed from %s to %s\n", (pRegEntry->pCodecRef->bVadEnabled?"ON":"OFF"), (iValue?"ON":"OFF"))); + pRegEntry->pCodecRef->bVadEnabled= (iValue?TRUE:FALSE); + } + return(0); + } + + #if DIRTYCODE_LOGGING + if (iControl == 'spam') + { + NetPrintf(("voipcodec: debug verbosity level changed from %d to %d\n", pRegEntry->pCodecRef->iDebugLevel, iValue)); + pRegEntry->pCodecRef->iDebugLevel = iValue; + + // do not return here, we want to pass-through to codec-specific control funct. + } + #endif + + + if (iControl == 'vada') + { + float fNewThreshold; + fNewThreshold = iValue * 0.000001f; + NetPrintf(("voipcodec: VAD signal amplitude threshold (power of) changed from %f to %f\n", pRegEntry->pCodecRef->fVadPowerThreshold, fNewThreshold)); + pRegEntry->pCodecRef->fVadPowerThreshold = fNewThreshold; + return(0); + } + + if (iControl == 'vadf') + { + NetPrintf(("voipcodec: VAD frames threshold changed from %d to %d\n", pRegEntry->pCodecRef->iVadSilenceFramesThreshold, iValue)); + pRegEntry->pCodecRef->iVadSilenceFramesThreshold = iValue; + return(0); + } + + // then call the registered codec control function + return(pRegEntry->pCodecDef->pControl(pRegEntry->pCodecRef, iControl, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function VoipCodecStatus + + \Description + Get codec status + + \Input iCodecIdent - codec identifier, or VOIP_CODEC_ACTIVE + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuffer - [out] selector-specific buffer space + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'coco' - returns the number of register codecs + 'plvl' - returns current voice power level (same calc as VAD threshold) + \endverbatim + + \Version 08/09/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCodecStatus(int32_t iCodecIdent, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + VoipCodecRegT *pRegEntry; + + // returns the number of active codecs + if (iSelect == 'coco') + { + return(_VoipCodec_iNumCodecs); + } + // make sure active codec is valid + if ((pRegEntry = _VoipCodecGet(iCodecIdent, NULL)) == NULL) + { + NetPrintf(("voipcodec: invalid codec, cannot process status message '%C'\n", iSelect)); + return(-1); + } + // returns current voice power level (same calculation as is used for VAD threshold) + if (iSelect == 'plvl') + { + return((int32_t)(pRegEntry->pCodecRef->fPowerLevel / 0.000001f)); + } + // call the registered status function + return(pRegEntry->pCodecDef->pStatus(pRegEntry->pCodecRef, iSelect, iValue, pBuffer, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function VoipCodecDestroy + + \Description + Destroy the active codec + + \Version 08/04/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCodecDestroy(void) +{ + VoipCodecRegT *pRegEntry; + + // get active codec + if ((pRegEntry = _VoipCodecGet(VOIP_CODEC_ACTIVE, NULL)) == NULL) + { + return; + } + + // call the registered destroy function + pRegEntry->pCodecRef->pCodecDef->pDestroy(pRegEntry->pCodecRef); + // clear codec ref state pointer + pRegEntry->pCodecRef = NULL; + // reset active codec index + _VoipCodec_iActiveCodec = -1; +} + +/*F********************************************************************************/ +/*! + \Function VoipCodecReset + + \Description + Resets codec state of active codec. + + \Version 08/04/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCodecReset(void) +{ + VoipCodecRegT *pRegEntry; + + // get active codec + if ((pRegEntry = _VoipCodecGet(VOIP_CODEC_ACTIVE, NULL)) == NULL) + { + return; + } + // call the registered reset function + pRegEntry->pCodecDef->pReset(pRegEntry->pCodecRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipCodecEncode + + \Description + Encode input data with the active codec. + + \Input *pOutput - pointer to output buffer + \Input *pInput - pointer to input sample data + \Input iNumSamples - number of input samples + \Input *pTranscribeRef - transcription ref, if available + + \Output + int32_t - size of output data + + \Version 08/04/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCodecEncode(uint8_t *pOutput, const int16_t *pInput, int32_t iNumSamples, VoipTranscribeRefT *pTranscribeRef) +{ + VoipCodecRegT *pRegEntry; + int32_t iResult; + #if VOIPCODEC_TIMER + uint64_t uTimer; + #endif + + // make sure active codec is valid + if ((pRegEntry = _VoipCodecGet(VOIP_CODEC_ACTIVE, NULL)) == NULL) + { + NetPrintf(("voipcodec: no active codec, cannot encode\n")); + return(0); + } + + // if VAD is enabled and we detect silence, skip the encode + if (pRegEntry->pCodecRef->bVadEnabled && (_VoipCodecVadProcess16BitFrame(pRegEntry->pCodecRef, pInput, iNumSamples))) + { + return(0); + } + + #if VOIPCODEC_TIMER + uTimer = NetTickUsec(); + #endif + + // call the registered encoder + iResult = pRegEntry->pCodecDef->pEncode(pRegEntry->pCodecRef, pOutput, pInput, iNumSamples); + + #if VOIPCODEC_TIMER + NetPrintf(("voipcodecencode: %dus\n", NetTickDiff(NetTickUsec(), uTimer))); + #endif + + // submit voice data for transcription if we were passed a transcription ref + if ((pTranscribeRef != NULL) && (iResult > 0)) + { + if (VoipTranscribeStatus(pTranscribeRef, 'cmpr', 0, NULL, 0)) + { + // transcribing compressed audio + VoipTranscribeSubmit(pTranscribeRef, pOutput, iResult); + } + else + { + // transcribing uncompressed audio + VoipTranscribeSubmit(pTranscribeRef, (const uint8_t *)pInput, iNumSamples*sizeof(*pInput)); + } + } + + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function VoipCodecDecode + + \Description + Decode input data with the active codec. + + \Input *pOutput - pointer to output buffer + \Input *pInput - pointer to input compressed data + \Input iInputBytes - size in bytes of input data + \Input iChannel - the input channel whose data we are decoding + + \Output + int32_t - number of output samples + + \Version 08/04/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCodecDecode(int32_t *pOutput, const uint8_t *pInput, int32_t iInputBytes, int32_t iChannel) +{ + VoipCodecRegT *pRegEntry; + int32_t iResult; + #if VOIPCODEC_TIMER + uint64_t uTimer = NetTickUsec(); + #endif + + // make sure active codec is valid + if ((pRegEntry = _VoipCodecGet(VOIP_CODEC_ACTIVE, NULL)) == NULL) + { + NetPrintf(("voipcodec: no active codec, cannot decode\n")); + return(0); + } + + // call the registered decoder + iResult = pRegEntry->pCodecDef->pDecode(pRegEntry->pCodecRef, pOutput, pInput, iInputBytes, iChannel); + + #if VOIPCODEC_TIMER + NetPrintf(("voipcodecdecode: %dus\n", NetTickDiff(NetTickUsec(), uTimer))); + #endif + + return(iResult); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipcommon.c b/r5dev/thirdparty/dirtysdk/source/voip/voipcommon.c new file mode 100644 index 00000000..f91e3773 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipcommon.c @@ -0,0 +1,2155 @@ +/*H********************************************************************************/ +/*! + \File voipcommon.c + + \Description + Cross-platform voip data types and functions. + + \Copyright + Copyright (c) 2009 Electronic Arts Inc. + + \Version 12/02/2009 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#ifdef _XBOX +#include +#include +#endif + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/netconn.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" + +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voipblocklist.h" + +/*** Defines **********************************************************************/ +#define VOIPCOMMON_CODEDCHAN_ALL (0) //! Special handling for codeded channel {(0,none),(0,none),(0,none),(0,none)} = talk/listen to everyone +#define VOIPCOMMON_CODEDCHAN_NONE (0x01010101) //! Special handling for codeded channel {(1,none),(1,none),(1,none),(1,none)} = can't talk or listen to anyone + +/*** Type Definitions *************************************************************/ + + +/*** Variables ********************************************************************/ + +//! pointer to module state +static VoipRefT *_Voip_pRef = NULL; + +//! voip memgroup +static int32_t _Voip_iMemGroup='dflt'; + +//! voip memgroup userdata +static void *_Voip_pMemGroupUserData=NULL; + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! +\Function _VoipCommonTextDataCb + + \Description + Callback to handle locally generated transcribed text. + + \Input *pStrUtf8 - pointer to text data + \Input uLocalUserIndex - local user index + \Input *pUserData - pointer to callback user data + + \Version 10/29/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipCommonTextDataCb(const char *pStrUtf8, uint32_t uLocalUserIndex, void *pUserData) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pUserData; + + VoipConnectionReliableTranscribedTextMessage(&pVoipCommon->Connectionlist, uLocalUserIndex, pStrUtf8); + /* We also want locally generated transcribed text shown to its originator if that local user has + also requested transcribed text from remote users. Motivation: a hearing-impaired person requesting + transcribed text from other players also wants to see transcribed text for his own speech + (request from EA's accessibility team). + + Note on XBoxOne this data will come through _VoipCommonReceiveTextDataCb as it would for remote generated text. + */ + if (pVoipCommon->Connectionlist.bTranscribedTextRequested[uLocalUserIndex] == TRUE) + { + if (pVoipCommon->pDisplayTranscribedTextCb != NULL) + { + pVoipCommon->pDisplayTranscribedTextCb(-1, uLocalUserIndex, pStrUtf8, pVoipCommon->pDisplayTranscribedTextUserData); + } + } +} + +/*F********************************************************************************/ +/*! +\Function _VoipCommonMicDataCb + + \Description + Callback to handle locally acquired mic data. + + \Input *pVoiceData - pointer to mic data + \Input iDataSize - size of mic data + \Input *pMetaData - pointer to platform-specific metadata (can be NULL) + \Input iMetaDataSize - size of platform-specifici metadata + \Input uLocalUserIndex - local user index + \Input uSendSeqn - send sequence + \Input *pUserData - pointer to callback user data + + \Version 10/29/2018 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipCommonMicDataCb(const void *pVoiceData, int32_t iDataSize, const void *pMetaData, int32_t iMetaDataSize, uint32_t uLocalUserIndex, uint8_t uSendSeqn, void *pUserData) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pUserData; + + if (pVoipCommon != NULL) + { + VoipConnectionSend(&pVoipCommon->Connectionlist, 0xFFFFFFFF, (uint8_t *)pVoiceData, iDataSize, (uint8_t *)pMetaData, iMetaDataSize, uLocalUserIndex, uSendSeqn); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonOpaqueDataCb + + \Description + Callback to handle locally generated opaque data to be sent reliably to other voip peers. + + \Input pOpaqueData - data buffer + \Input iOpaqueDataSize - opaque data size in bytes + \Input uSendMask - mask identifying which connections to send the data to + \Input bReliable - TRUE if reliable transmission needed, FALSE otherwise + \Input uSendSeqn - send sequence + \Input *pUserData - user data + + \Version 11/22/2018 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipCommonOpaqueDataCb(const uint8_t *pOpaqueData, int32_t iOpaqueDataSize, uint32_t uSendMask, uint8_t bReliable, uint8_t uSendSeqn, void *pUserData) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pUserData; + + int32_t iConnID; + + if (bReliable) + { + for (iConnID = 0; iConnID < pVoipCommon->Connectionlist.iMaxConnections; iConnID++) + { + if (uSendMask & (1 << iConnID)) + { + VoipConnectionReliableSendOpaque(&pVoipCommon->Connectionlist, iConnID, pOpaqueData, iOpaqueDataSize); + } + } + } + else + { + /* The xboxone unreliable date frames are opaque to us. Consequently, we do not know if they contain encode voice from a specific + local user or from multiple local users. Therfore, we always invoke VoipConnectionSend() with the local user index being + VOIP_SHARED_USER_INDEX. By doing so, we guarantee that VOIP_REMOTE_USER_RECVVOICE and VOIP_LOCAL_USER_SENDVOICE gets updated + consistently for all participating users on a given console. We loose the per-user granularity for that flag, but it's + the best we can do. */ + VoipConnectionSend(&pVoipCommon->Connectionlist, uSendMask, pOpaqueData, iOpaqueDataSize, NULL, 0, VOIP_SHARED_USER_INDEX, uSendSeqn); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonEncode + + \Description + Packs the channel selection for sending to a remote machine + + \Input iChannels[] - the array of channels to encode + \Input uModes[] - the array of modes to encode + + \Output + uint32_t - the coded channels + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +static uint32_t _VoipCommonEncode(int32_t iChannels[], uint32_t uModes[]) +{ + uint32_t codedChannel = 0; + int32_t iIndex; + + for(iIndex = 0; iIndex < VOIP_MAX_CONCURRENT_CHANNEL; iIndex++) + { + codedChannel |= (iChannels[iIndex] & 0x3f) << (8 * iIndex); + codedChannel |= ((uModes[iIndex] & 0x3) << 6) << (8 * iIndex); + } + + return(codedChannel); +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonDecode + + \Description + Unpacks the channel selection received by a remote machine + + \Input uCodedChannel - the coded channels + \Input iChannels[] - the array of channels to fill up + \Input uModes[] - the array of modes to fill up + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +static void _VoipCommonDecode(uint32_t uCodedChannel, int32_t iChannels[], uint32_t uModes[]) +{ + int32_t iIndex; + for(iIndex = 0; iIndex < VOIP_MAX_CONCURRENT_CHANNEL; iIndex++) + { + iChannels[iIndex] = (uCodedChannel >> (8 * iIndex)) & 0x3f; + uModes[iIndex] = ((uCodedChannel >> (8 * iIndex)) >> 6) & 0x3; + } +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonProcessChannelChange + + \Description + Unpacks the channel selection received by a remote machine + + \Input pVoipCommon - voip common state + \Input iConnId - connection id + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +void VoipCommonProcessChannelChange(VoipCommonRefT *pVoipCommon, int32_t iConnId) +{ + int32_t iUserIndex; + + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + uint32_t uInputOuputVar; + #if DIRTYCODE_LOGGING + uint32_t uNewChannelConfig, uOldChannelConfig; + #endif + + uInputOuputVar = (uint32_t)iUserIndex; + VoipStatus(VoipGetRef(), 'rchn', iConnId, &uInputOuputVar, sizeof(uInputOuputVar)); + + #if DIRTYCODE_LOGGING + uNewChannelConfig = uInputOuputVar; + uOldChannelConfig = pVoipCommon->uRemoteChannelSelection[iConnId][iUserIndex]; + + NetPrintf(("voipcommon: got remote channels 0x%08x (old config: 0x%08x)for user index %d on low-level conn id %d\n", + uNewChannelConfig, uOldChannelConfig, iUserIndex, iConnId)); + #endif + + pVoipCommon->uRemoteChannelSelection[iConnId][iUserIndex] = uInputOuputVar; + } + + VoipCommonApplyChannelConfig(pVoipCommon); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipIdle + + \Description + NetConn idle function to update the Voip module. + + This function is designed to handle issuing user callbacks based on events such as + headset insertion and removal. It is implemented as a NetConn idle function instead + of as a part of the Voip thread so that callbacks will be generated from the same + thread that DirtySock operates in. + + \Input *pData - pointer to module state + \Input uTick - current tick count + + \Notes + This function is installed as a NetConn Idle function. NetConnIdle() + must be regularly polled for this function to be called. + + \Version 02/10/2006 (jbrookes) +*/ +/*************************************************************************************************F*/ +static void _VoipIdle(void *pData, uint32_t uTick) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pData; + int32_t iIndex; + + // check for state changes and trigger callback + if (pVoipCommon->uLastHsetStatus != pVoipCommon->uPortHeadsetStatus) + { + pVoipCommon->pCallback((VoipRefT *)pData, VOIP_CBTYPE_HSETEVENT, pVoipCommon->uPortHeadsetStatus, pVoipCommon->pUserData); + pVoipCommon->uLastHsetStatus = pVoipCommon->uPortHeadsetStatus; + } + + if (pVoipCommon->uLastFrom != pVoipCommon->Connectionlist.uRecvVoice) + { + pVoipCommon->pCallback((VoipRefT *)pData, VOIP_CBTYPE_FROMEVENT, pVoipCommon->Connectionlist.uRecvVoice, pVoipCommon->pUserData); + pVoipCommon->uLastFrom = pVoipCommon->Connectionlist.uRecvVoice; + } + + if (pVoipCommon->uLastTtsStatus != pVoipCommon->uTtsStatus) + { + pVoipCommon->pCallback((VoipRefT *)pData, VOIP_CBTYPE_TTOSEVENT, pVoipCommon->uTtsStatus, pVoipCommon->pUserData); + pVoipCommon->uLastTtsStatus = pVoipCommon->uTtsStatus; + } + + for (iIndex = 0; iIndex < VOIP_MAXLOCALUSERS; ++iIndex) + { + if (pVoipCommon->uLastLocalStatus[iIndex] != pVoipCommon->Connectionlist.uLocalUserStatus[iIndex]) + { + if ((pVoipCommon->uLastLocalStatus[iIndex] ^ pVoipCommon->Connectionlist.uLocalUserStatus[iIndex]) & VOIP_LOCAL_USER_SENDVOICE) + { + pVoipCommon->pCallback((VoipRefT *)pData, VOIP_CBTYPE_SENDEVENT, pVoipCommon->Connectionlist.uLocalUserStatus[iIndex] & VOIP_LOCAL_USER_SENDVOICE, pVoipCommon->pUserData); + } + pVoipCommon->uLastLocalStatus[iIndex] = pVoipCommon->Connectionlist.uLocalUserStatus[iIndex]; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonChannelMatch + + \Description + Returns whether a given channel selection is to be received and/or sent to + + \Input *pVoipCommon - voip common state + \Input iUserIndex - local user index + \Input uCodedChannel - coded channels of remote user + + \Output + uint32_t - the coded channels + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +static VoipChanModeE _VoipCommonChannelMatch(VoipCommonRefT *pVoipCommon, int32_t iUserIndex, uint32_t uCodedChannel) +{ + int32_t iIndexRemote; + int32_t iIndexLocal; + VoipChanModeE eMode = 0; + int32_t iChannels[VOIP_MAX_CONCURRENT_CHANNEL]; + uint32_t uModes[VOIP_MAX_CONCURRENT_CHANNEL]; + + // Voip traffic sent on channels {(0,none),(0,none),(0,none),(0,none)} = talk/listen to everyone = VOIPCOMMON_CODEDCHAN_ALL + // Voip traffic blocked on channels {(1,none),(1,none),(1,none),(1,none)} = can't talk or listen = VOIPCOMMON_CODEDCHAN_NONE + // is assumed to be meant for everybody. + // enables default behaviour for teams not using channels. + if ((uCodedChannel == VOIPCOMMON_CODEDCHAN_ALL) || (pVoipCommon->uLocalChannelSelection[iUserIndex] == VOIPCOMMON_CODEDCHAN_ALL)) + { + return(VOIP_CHANSEND|VOIP_CHANRECV); + } + else if ((uCodedChannel == VOIPCOMMON_CODEDCHAN_NONE) || (pVoipCommon->uLocalChannelSelection[iUserIndex] == VOIPCOMMON_CODEDCHAN_NONE)) + { + return(VOIP_CHANNONE); + } + + _VoipCommonDecode(uCodedChannel, iChannels, uModes); + + // for all our channels, and all the channels of the remote party + for(iIndexRemote = 0; iIndexRemote < VOIP_MAX_CONCURRENT_CHANNEL; iIndexRemote++) + { + for(iIndexLocal = 0; iIndexLocal < VOIP_MAX_CONCURRENT_CHANNEL; iIndexLocal++) + { + // if their channel numbers match + if (pVoipCommon->iLocalChannels[iUserIndex][iIndexLocal] == iChannels[iIndexRemote]) + { + // if we're sending on it and they're receiving on it + if ((pVoipCommon->uLocalModes[iUserIndex][iIndexLocal] & VOIP_CHANSEND) && (uModes[iIndexRemote] & VOIP_CHANRECV)) + { + // let's send + eMode |= VOIP_CHANSEND; + } + // if we're receiving on it and they're send on it + if ((pVoipCommon->uLocalModes[iUserIndex][iIndexLocal] & VOIP_CHANRECV) && (uModes[iIndexRemote] & VOIP_CHANSEND)) + { + // let's receive + eMode |= VOIP_CHANRECV; + } + } + } + } + + return(eMode); +} + + +/*F********************************************************************************/ +/*! + \Function _VoipCommonComputeEffectiveUserSendMask + + \Description + Compute effective user send mask from two other masks: user-selected + send mask and mask obtained from voip channel config + + \Input *pVoipCommon - voip common state + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +static void _VoipCommonComputeEffectiveUserSendMask(VoipCommonRefT *pVoipCommon) +{ + VoipCommonSetMask(&pVoipCommon->Connectionlist.uUserSendMask, pVoipCommon->uUserMicrValue & pVoipCommon->uChanSendMask, "usendmask"); + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + pVoipCommon->Connectionlist.bApplyRelFromMuting = TRUE; // to reflect these changes in the communication relationship + #endif +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonComputeDefaultSharedChannelConfig + + \Description + Computes the default shared channel config. This is the logical "and" of all + local user's channel modes + + \Input *pVoipCommon - voip common state + + \Notes + Loops through all the channels and "and" the channel mode of all the valid users on the + that particular channel together. Only if everyone is subcribed to the channel do we set + the mode for the shared user. + + \Version 12/11/2014 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipCommonComputeDefaultSharedChannelConfig(VoipCommonRefT *pVoipCommon) +{ + // only bother to do any of this if we are in fact using the "default shared channel config", which is more of a behavior than a static thing + if (pVoipCommon->bUseDefaultSharedChannelConfig) + { + int32_t iLocalUserIndex; + int32_t iChannel; + int32_t iChannelSlot; + int32_t iEmptySlot = 0; + + _VoipCommonDecode(VOIPCOMMON_CODEDCHAN_NONE, pVoipCommon->iDefaultSharedChannels, pVoipCommon->uDefaultSharedModes); + + // loop through all the channel from 1 to 63 + for (iChannel = 1; iChannel < VOIP_MAXLOCALCHANNEL; ++iChannel) + { + int32_t iValidUser = 0; + int32_t iChannelSubscriber = 0; + int32_t iChannelAllUser = 0; + uint32_t uSharedChannelMode = VOIP_CHANSENDRECV; + + // only compute the shared channel config if we still have a free slot + if (iEmptySlot != VOIP_MAX_CONCURRENT_CHANNEL) + { + // loop through all the local users excluding the shared user + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS; ++iLocalUserIndex) + { + if (!VOIP_NullUser((VoipUserT *)&pVoipCommon->Connectionlist.LocalUsers[iLocalUserIndex])) + { + ++iValidUser; + + // the user is valid loop through his subscribed channels + for (iChannelSlot = 0; iChannelSlot < VOIP_MAX_CONCURRENT_CHANNEL; ++iChannelSlot) + { + // check to see if we are in an VOIPCOMMON_CODEDCHAN_ALL state, in which case consider treating the shared user as a VOIPCOMMON_CODEDCHAN_ALL + if (_VoipCommonEncode(pVoipCommon->iLocalChannels[iLocalUserIndex], pVoipCommon->uLocalModes[iLocalUserIndex]) == VOIPCOMMON_CODEDCHAN_ALL) + { + ++iChannelAllUser; + break; + } + // check to see if the user is actually a member of the channel, in which case consider using this channel for the shared user too + else if (pVoipCommon->iLocalChannels[iLocalUserIndex][iChannelSlot] == iChannel) + { + ++iChannelSubscriber; + uSharedChannelMode &= pVoipCommon->uLocalModes[iLocalUserIndex][iChannelSlot]; + break; + } + } + } + } + + // if everyone is all open, then the shared channel is all open too + if ((iValidUser == iChannelAllUser) && (iChannelAllUser != 0)) + { + _VoipCommonDecode(VOIPCOMMON_CODEDCHAN_ALL, pVoipCommon->iDefaultSharedChannels, pVoipCommon->uDefaultSharedModes); + } + // if everyone is subscribed to the channel (or all open) then the uSharedChannelMode is valid + else if ((iValidUser == (iChannelSubscriber + iChannelAllUser)) && (iChannelSubscriber != 0)) + { + // if we were set to VOIPCOMMON_CODEDCHAN_NONE but now we are going to make changes, clear out to VOIPCOMMON_CODEDCHAN_ALL first + if (_VoipCommonEncode(pVoipCommon->iDefaultSharedChannels, pVoipCommon->uDefaultSharedModes) == VOIPCOMMON_CODEDCHAN_NONE) + { + _VoipCommonDecode(VOIPCOMMON_CODEDCHAN_ALL, pVoipCommon->iDefaultSharedChannels, pVoipCommon->uDefaultSharedModes); + } + pVoipCommon->iDefaultSharedChannels[iEmptySlot] = iChannel; + pVoipCommon->uDefaultSharedModes[iEmptySlot] = uSharedChannelMode; + ++iEmptySlot; + } + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonComputeEffectiveUserRecvMask + + \Description + Compute effective user recv mask from two other masks: user-selected + recv mask and mask obtained from voip channel config + + \Input *pVoipCommon - voip common state + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +static void _VoipCommonComputeEffectiveUserRecvMask(VoipCommonRefT *pVoipCommon) +{ + VoipCommonSetMask(&pVoipCommon->Connectionlist.uUserRecvMask, pVoipCommon->uUserSpkrValue & pVoipCommon->uChanRecvMask, "urecvmask"); + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + pVoipCommon->Connectionlist.bApplyRelFromMuting = TRUE; // to reflect these changes in the communication relationship + #endif +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonSetChannels + + \Description + Set the active voip channel (coded value of channels and modes) + + \Input *pVoipCommon - voip common state + \Input uChannels - channels value we are setting for the user + \Input iUserIndex - index of user we are performing the operation on + + \Output + int32_t - zero=success, negative=failure + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _VoipCommonSetChannels(VoipCommonRefT *pVoipCommon, uint32_t uChannels, int32_t iUserIndex) +{ + if ((iUserIndex >= 0) && (iUserIndex < VOIP_MAXLOCALUSERS)) + { + NetPrintf(("voipcommon: setting voip channels (0x%08x) for local user index %d\n", uChannels, iUserIndex)); + pVoipCommon->Connectionlist.aChannels[iUserIndex] = uChannels; + return(0); + } + else if (iUserIndex == VOIP_SHARED_USER_INDEX) + { + NetPrintf(("voipcommon: setting voip channels (0x%08x) for local shared user\n", uChannels)); + pVoipCommon->Connectionlist.aChannels[iUserIndex] = uChannels; + return(0); + } + else + { + NetPrintf(("voipcommon: warning - setting voip channels for invalid local user index %d\n", iUserIndex)); + return(-1); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipCommonSetSharedUserChannelConfig + + \Description + Set the shared user channel config to custom or default based on the + bUseDefaultSharedChannelConfig flag. + + \Input *pVoipCommon - voip common state + + \Version 12/16/2014 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipCommonSetSharedUserChannelConfig(VoipCommonRefT *pVoipCommon) +{ + int32_t iMaxConcurrentChannel; + int32_t iSharedUserIndex = VOIP_SHARED_USER_INDEX; + + for (iMaxConcurrentChannel = 0; iMaxConcurrentChannel < VOIP_MAX_CONCURRENT_CHANNEL; ++iMaxConcurrentChannel) + { + if (pVoipCommon->bUseDefaultSharedChannelConfig) + { + pVoipCommon->iLocalChannels[iSharedUserIndex][iMaxConcurrentChannel] = pVoipCommon->iDefaultSharedChannels[iMaxConcurrentChannel]; + pVoipCommon->uLocalModes[iSharedUserIndex][iMaxConcurrentChannel] = pVoipCommon->uDefaultSharedModes[iMaxConcurrentChannel]; + } + else + { + pVoipCommon->iLocalChannels[iSharedUserIndex][iMaxConcurrentChannel] = pVoipCommon->iCustomSharedChannels[iMaxConcurrentChannel]; + pVoipCommon->uLocalModes[iSharedUserIndex][iMaxConcurrentChannel] = pVoipCommon->uCustomSharedModes[iMaxConcurrentChannel]; + } + } + + // encdoe the channel selection and set the active voip channels + pVoipCommon->uLocalChannelSelection[iSharedUserIndex] = _VoipCommonEncode(pVoipCommon->iLocalChannels[iSharedUserIndex], pVoipCommon->uLocalModes[iSharedUserIndex]); + _VoipCommonSetChannels(pVoipCommon, pVoipCommon->uLocalChannelSelection[iSharedUserIndex], iSharedUserIndex); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipCommonCallback + + \Description + Default (empty) user callback function. + + \Input *pVoip - voip module state + \Input eCbType - type of event + \Input iValue - event-specific information + \Input *pUserData - callback user data + + \Output + None. + + \Version 02/10/2006 (jbrookes) +*/ +/*************************************************************************************************F*/ +static void _VoipCommonCallback(VoipRefT *pVoip, VoipCbTypeE eCbType, int32_t iValue, void *pUserData) +{ +} + +#if defined(DIRTYCODE_PS4) +/*F********************************************************************************/ +/*! + \Function _VoipCommonDisplayRemoteTextDataCb + + \Description + Callback to handle receiving a transcribed text packet from a remote peer. + (once permission check has been validated in voipheadset) + + \Input *pRemoteUser - transcribed text's originator + \Input *pStrUtf - transcribed text + \Input *pUserData - user data + + \Version 04/04/2019 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipCommonDisplayRemoteTextDataCb(const VoipUserT *pRemoteUser, const char *pStrUtf8, void *pUserData) +{ + int32_t iConnectionIndex, iRemoteUserIndex = 0; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pUserData; + uint32_t bFound = FALSE; + + for (iConnectionIndex = 0; iConnectionIndex < pVoipCommon->Connectionlist.iMaxConnections; iConnectionIndex++) + { + VoipConnectionT *pConnection = &pVoipCommon->Connectionlist.pConnections[iConnectionIndex]; + + for (iRemoteUserIndex = 0; iRemoteUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iRemoteUserIndex++) + { + if (VOIP_SameUser(pRemoteUser, (VoipUserT *)&pConnection->RemoteUsers[iRemoteUserIndex])) + { + bFound = TRUE; + break; + } + } + + if (bFound == TRUE) + { + break; + } + } + + if (bFound == TRUE) + { + if (pVoipCommon->pDisplayTranscribedTextCb != NULL) + { + pVoipCommon->pDisplayTranscribedTextCb(iConnectionIndex, iRemoteUserIndex, pStrUtf8, pVoipCommon->pDisplayTranscribedTextUserData); + } + } + else + { + NetPrintf(("voipcommon: failed to find matching remote user for originator of transcribed text\n")); + } +} +#endif + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) +/*F********************************************************************************/ +/*! + \Function _VoipCommonReceiveTextDataCb + + \Description + Callback to handle receiving a transcribed text packet from a remote peer. + + \Input *pSourceUser - transcribed text's originator + \Input *pStrUtf - transcribed text + \Input *pUserData - user data + + \Version 05/02/2017 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipCommonReceiveTextDataNativeCb(const VoipUserT *pSourceUser, const char *pStrUtf8, void *pUserData) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pUserData; + + if (pVoipCommon->pDisplayTranscribedTextCb != NULL) + { + int32_t iConnectionIndex, iUserIndex; + + // see if a local user sent this text (seeing what they said) + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; iUserIndex++) + { + int64_t iLocalPersonaId; + if (NetConnStatus('peid', iUserIndex, &iLocalPersonaId, sizeof(iLocalPersonaId)) == 0) + { + if (iLocalPersonaId == pSourceUser->AccountInfo.iPersonaId) + { + pVoipCommon->pDisplayTranscribedTextCb(-1, iUserIndex, pStrUtf8, pVoipCommon->pDisplayTranscribedTextUserData); + return; + } + } + } + + // search for a remote user who sent this text + for (iConnectionIndex = 0; iConnectionIndex < pVoipCommon->Connectionlist.iMaxConnections; iConnectionIndex++) + { + VoipConnectionT *pConnection = &pVoipCommon->Connectionlist.pConnections[iConnectionIndex]; + + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + if (VOIP_SameUser(pSourceUser, &pConnection->RemoteUsers[iUserIndex])) + { + pVoipCommon->pDisplayTranscribedTextCb(iConnectionIndex, iUserIndex, pStrUtf8, pVoipCommon->pDisplayTranscribedTextUserData); + return; + } + } + } + + // we didn't find a local or remote user who sent this text, just drop it but complain + NetPrintf(("voipcommon: failed to find matching originator of transcribed text; user: %lld, text: %s\n", pSourceUser->AccountInfo.iPersonaId, pStrUtf8)); + } +} +#endif + +/*F********************************************************************************/ +/*! + \Function _VoipCommonReceiveTextDataCb + + \Description + Callback to handle receiving a transcribed text packet from a remote peer. + + \Input iConnId - connection index + \Input iRemoteUserIndex - index of remote user + \Input *pStrUtf8 - pointer to beginning of text + \Input *pUserData - user data + + \Version 05/02/2017 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipCommonReceiveTextDataCb(int32_t iConnId, int32_t iRemoteUserIndex, const char *pStrUtf8, void *pUserData) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)pUserData; + + if (pVoipCommon->pDisplayTranscribedTextCb != NULL) + { + #if defined(DIRTYCODE_PS4) + VoipUserT VoipUser; + uint32_t uConnUserPair; + + // initialize variable used with VoipStatus('rcvu'). most-significant 16 bits = remote user index, least-significant 16 bits = conn index + uConnUserPair = iConnId; + uConnUserPair |= (iRemoteUserIndex << 16); + + // find out if there is a valid remote user for that connId/userId pair + if (VoipStatus(VoipGetRef(), 'rcvu', (int32_t)uConnUserPair, &VoipUser, sizeof(VoipUser)) == 0) + { + VoipHeadsetTranscribedTextPermissionCheck(pVoipCommon->pHeadset, &VoipUser, pStrUtf8); + } + else + { + NetPrintf(("voipcommon: 'transcribed text received' event dropped because voipheadset can't find remote user index %d on connection %d\n", iRemoteUserIndex, iConnId)); + } + #else + pVoipCommon->pDisplayTranscribedTextCb(iConnId, iRemoteUserIndex, pStrUtf8, pVoipCommon->pDisplayTranscribedTextUserData); + #endif + + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipCommonGetRef + + \Description + Return current module reference. + + \Output + VoipRefT * - reference pointer, or NULL if the module is not active + + \Version 03/08/2004 (jbrookes) +*/ +/********************************************************************************F*/ +VoipRefT *VoipCommonGetRef(void) +{ + // return pointer to module state + return(_Voip_pRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonStartup + + \Description + Start up common functionality + + \Input iMaxPeers - maximum number of peers supported (up to VOIP_MAXCONNECT) + \Input iVoipRefSize - size of voip ref to allocate + \Input *pStatusCb - headset status callback + \Input iData - platform-specific + + \Output + VoipRefT * - voip ref if successful; else NULL + + \Version 12/02/2009 (jbrookes) +*/ +/********************************************************************************F*/ +VoipRefT *VoipCommonStartup(int32_t iMaxPeers, int32_t iVoipRefSize, VoipHeadsetStatusCbT *pStatusCb, int32_t iData) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + VoipCommonRefT *pVoipCommon; + int32_t i; + + // make sure we're not already started + if (_Voip_pRef != NULL) + { + NetPrintf(("voipcommon: module startup called when not in a shutdown state\n")); + return(NULL); + } + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // create and initialize module state + if ((pVoipCommon = (VoipCommonRefT *)DirtyMemAlloc(iVoipRefSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipcommon: unable to allocate module state\n")); + return(NULL); + } + ds_memclr(pVoipCommon, iVoipRefSize); + + // set voip memgroup + VoipCommonMemGroupSet(iMemGroup, pMemGroupUserData); + + // by default, user-selected micr and spkr flag always default to ON + pVoipCommon->uUserMicrValue = 0xFFFFFFFF; + pVoipCommon->uUserSpkrValue = 0xFFFFFFFF; + + // create thread critical section + NetCritInit(&pVoipCommon->ThreadCrit, "voip"); + + // create block list + if ((pVoipCommon->pBlockList = VoipBlockListCreate()) == NULL) + { + NetPrintf(("voipcommon: unable to allocate block list\n")); + VoipCommonShutdown(pVoipCommon); + return(NULL); + } + + // create connection list + if (VoipConnectionStartup(&pVoipCommon->Connectionlist, iMaxPeers) < 0) + { + NetPrintf(("voipcommon: unable to allocate connectionlist\n")); + VoipCommonShutdown(pVoipCommon); + return(NULL); + } + + // create headset module + if ((pVoipCommon->pHeadset = VoipHeadsetCreate(iMaxPeers, &_VoipCommonMicDataCb, &_VoipCommonTextDataCb, &_VoipCommonOpaqueDataCb, pStatusCb, pVoipCommon, iData)) == NULL) + { + NetPrintf(("voipcommon: unable to create VoipHeadset layer\n")); + VoipCommonShutdown(pVoipCommon); + return(NULL); + } + + // turn on default shared channel config by default + pVoipCommon->bUseDefaultSharedChannelConfig = TRUE; + + // set up connectionlist callback interface +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + VoipConnectionSetCallbacks(&pVoipCommon->Connectionlist, &VoipHeadsetReceiveVoiceDataCb, (void *)pVoipCommon->pHeadset, + NULL, NULL, + &VoipHeadsetRegisterUserCb, (void *)pVoipCommon->pHeadset, + &VoipHeadsetReceiveOpaqueDataCb, (void *)pVoipCommon->pHeadset); + + VoipHeadsetSetTranscribedTextReceivedCallback(pVoipCommon->pHeadset, &_VoipCommonReceiveTextDataNativeCb, (void *)pVoipCommon); +#else + #if defined(DIRTYCODE_PS4) + VoipHeadsetSetTranscribedTextReceivedCallback(pVoipCommon->pHeadset, &_VoipCommonDisplayRemoteTextDataCb, (void *)pVoipCommon); + #endif + + VoipConnectionSetCallbacks(&pVoipCommon->Connectionlist, &VoipHeadsetReceiveVoiceDataCb, (void *)pVoipCommon->pHeadset, + &_VoipCommonReceiveTextDataCb, (void *)pVoipCommon, + &VoipHeadsetRegisterUserCb, (void *)pVoipCommon->pHeadset, + NULL, (void *)pVoipCommon->pHeadset); +#endif + + // add callback idle function + VoipCommonSetEventCallback(pVoipCommon, NULL, NULL); + pVoipCommon->uLastFrom = pVoipCommon->Connectionlist.uRecvVoice; + for (i = 0; i < VOIP_MAXLOCALUSERS; ++i) + { + pVoipCommon->uLastLocalStatus[i] = pVoipCommon->Connectionlist.uLocalUserStatus[i]; + } + NetConnIdleAdd(_VoipIdle, pVoipCommon); + + // save ref + _Voip_pRef = (VoipRefT *)pVoipCommon; + return(_Voip_pRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonShutdown + + \Description + Shutdown common functionality + + \Input *pVoipCommon - common module state + + \Version 12/02/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonShutdown(VoipCommonRefT *pVoipCommon) +{ + void *pMemGroupUserData; + int32_t iMemGroup; + + // del callback idle function + if (_Voip_pRef) + { + NetConnIdleDel(_VoipIdle, pVoipCommon); + } + + // destroy headset module + if (pVoipCommon->pHeadset) + { + VoipHeadsetDestroy(pVoipCommon->pHeadset); + } + + // shut down connectionlist + if (pVoipCommon->Connectionlist.pConnections) + { + VoipConnectionShutdown(&pVoipCommon->Connectionlist); + } + + // destroy the block list + if (pVoipCommon->pBlockList) + { + VoipBlockListDestroy((VoipRefT *)pVoipCommon); + } + + // destroy critical section + NetCritKill(&pVoipCommon->ThreadCrit); + + // free module memory + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + DirtyMemFree(pVoipCommon, VOIP_MEMID, iMemGroup, pMemGroupUserData); + + // clear pointer to module state + _Voip_pRef = NULL; +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonMemGroupQuery + + \Description + Get VoIP mem group data. + + \Input *pMemGroup - [OUT param] pointer to variable to be filled with mem group id + \Input **ppMemGroupUserData - [OUT param] pointer to variable to be filled with pointer to user data + + \Version 1.0 11/11/2005 (jbrookes) First Version + \Version 1.1 11/18/2008 (mclouatre) returned values now passed in [OUT] parameters +*/ +/********************************************************************************F*/ +void VoipCommonMemGroupQuery(int32_t *pMemGroup, void **ppMemGroupUserData) +{ + *pMemGroup = _Voip_iMemGroup; + *ppMemGroupUserData = _Voip_pMemGroupUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonMemGroupSet + + \Description + Set VoIP mem group data. + + \Input iMemGroup - mem group to set + \Input *pMemGroupUserData - user data associated with mem group + + \Version 1.0 11/11/2005 (jbrookes) First Version + \Version 1.1 11/18/2008 (mclouatre) Adding second parameter (user data) +*/ +/********************************************************************************F*/ +void VoipCommonMemGroupSet(int32_t iMemGroup, void *pMemGroupUserData) +{ + _Voip_iMemGroup = iMemGroup; + _Voip_pMemGroupUserData = pMemGroupUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonUpdateRemoteStatus + + \Description + Process mute list, and set appropriate flags/priority for each remote user. + + \Input *pVoipCommon - pointer to module state + + \Version 08/23/2005 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonUpdateRemoteStatus(VoipCommonRefT *pVoipCommon) +{ + uint32_t uSendMask, uRecvMask, uChannelMask, bEnabled; + int32_t iConnection; + int32_t bTextTranscriptionEnabled = FALSE; + int32_t iLocalUserIndex; + + // process all active channels + for (iConnection = 0; iConnection < pVoipCommon->Connectionlist.iMaxConnections; iConnection++) + { + VoipConnectionT *pConnection = &pVoipCommon->Connectionlist.pConnections[iConnection]; + + // if not active, don't process + if (pConnection->eState != ST_ACTV) + { + continue; + } + + // calculate channel mask + uChannelMask = 1 << iConnection; + + // decide whether this channel should be enabled or not + bEnabled = pVoipCommon->bPrivileged; + + // set send/recv masks based on enable + user send override + if (bEnabled && ((pVoipCommon->Connectionlist.uUserSendMask & uChannelMask) != 0)) + { + uSendMask = pVoipCommon->Connectionlist.uSendMask | uChannelMask; + } + else + { + uSendMask = pVoipCommon->Connectionlist.uSendMask & ~uChannelMask; + } + + // set send/recv masks based on enable + user recv override + if (bEnabled && ((pVoipCommon->Connectionlist.uUserRecvMask & uChannelMask) != 0)) + { + uRecvMask = pVoipCommon->Connectionlist.uRecvMask | uChannelMask; + } + else + { + uRecvMask = pVoipCommon->Connectionlist.uRecvMask & ~uChannelMask; + } + + // set send/recv masks and priority + VoipConnectionSetSendMask(&pVoipCommon->Connectionlist, uSendMask); + VoipConnectionSetRecvMask(&pVoipCommon->Connectionlist, uRecvMask); + + if ((pConnection->bTranscribedTextRequested) && (pVoipCommon->Connectionlist.uSendMask & (1 << iConnection))) + { + // enable local text transcription because at least one remote user requested transcribed text + bTextTranscriptionEnabled = TRUE; + } + } + + /* We also want text transcription locally enabled if any local user is requesting text transcription + from remote users. Motivation: a hearing-impaired person requesting transcribed text from other + players also wants to see transcribed text for his own speech (request from EA's accessibility team). */ + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS; iLocalUserIndex++) + { + if (pVoipCommon->Connectionlist.bTranscribedTextRequested[iLocalUserIndex] == TRUE) + { + bTextTranscriptionEnabled = TRUE; + } + } + + if (bTextTranscriptionEnabled != pVoipCommon->bTextTranscriptionEnabled) + { + NetPrintf(("voipcommon: %s text transcription locally\n", bTextTranscriptionEnabled?"enabling":"disabling")); + pVoipCommon->bTextTranscriptionEnabled = bTextTranscriptionEnabled; + VoipHeadsetControl(pVoipCommon->pHeadset, 'tran', bTextTranscriptionEnabled, 0, NULL); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonStatus + + \Description + Return status. + + \Input *pVoipCommon - voip common state + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuf - [out] storage for selector-specific output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector-specific data + + \Notes + Allowed selectors: + + \verbatim + 'avlb' - whether a given ConnID is available + 'chan' - retrieve the active voip channel (coded value of channels and modes) + 'chnc' - return the count of channel slots used by the voip group manager + 'chnl' - return the channel id and channel mode associated with specified channel slot + 'from' - bitfield indicating which peers are talking + 'hset' - bitfield indicating which ports have headsets + 'lprt' - return local socket port + 'luvu' - return voipuser of specified local user + 'maxc' - return max connections + 'mgrp' - voip memgroup user id + 'mgud' - voip memgroup user data + 'micr' - who we're sending voice to + 'rchn' - retrieve the active voip channel of a remote peer (coded value of channels and modes) + 'rcvu' - return voipuser of specified remote user (connection and user index) + 'shch' - return bUseDefaultSharedChannelConfig which indicates if the default shared channel config is used + 'sock' - voip socket ref + 'spkr' - who we're accepting voice from + 'umic' - user-specified microphone send list + 'uspk' - user-specified microphone recv list + \endverbatim + + \Version 12/02/2009 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCommonStatus(VoipCommonRefT *pVoipCommon, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + if (iSelect == 'avlb') + { + return(VoipConnectionCanAllocate(&pVoipCommon->Connectionlist, iValue)); + } + if (iSelect == 'chan') + { + return(pVoipCommon->Connectionlist.aChannels[iValue]); + } + if (iSelect == 'chnc') + { + return(VOIP_MAX_CONCURRENT_CHANNEL); + } + if (iSelect == 'chnl') + { + int32_t iUserIndex = iValue & 0xFFFF; + int32_t iChannelSlot = iValue >> 16; + + if (!pBuf) + { + NetPrintf(("voipcommon: pBuf must be valid with the 'chnl' selector\n")); + return(-1); + } + + if ((unsigned)iBufSize < sizeof(VoipChanModeE)) + { + NetPrintf(("voipcommon: user buffer used with the 'chnl' selector is too small (expected size = %d; current size =%d)\n", + sizeof(VoipChanModeE), iBufSize)); + return(-2); + } + + if ((iChannelSlot < 0) || (iChannelSlot >= VOIP_MAX_CONCURRENT_CHANNEL)) + { + NetPrintf(("voipcommon: invalid slot id (%d) used with 'chnl' selector; valid range is [0,%d]\n", iChannelSlot, VOIP_MAX_CONCURRENT_CHANNEL-1)); + return(-3); + } + + *(VoipChanModeE *)pBuf = pVoipCommon->uLocalModes[iUserIndex][iChannelSlot]; + return(pVoipCommon->iLocalChannels[iUserIndex][iChannelSlot]); + } + if (iSelect == 'from') + { + return(pVoipCommon->Connectionlist.uRecvVoice); + } + if (iSelect == 'hset') + { + uint32_t uPortHeadsetStatus = pVoipCommon->uPortHeadsetStatus; + return(uPortHeadsetStatus); + } + if (iSelect == 'lprt') + { + return(pVoipCommon->Connectionlist.uBindPort); + } + if (iSelect == 'luvu') + { + if (pBuf) + { + if (iBufSize >= (signed)sizeof(VoipUserT)) + { + if ((iValue < VOIP_MAXLOCALUSERS_EXTENDED) && (!VOIP_NullUser((VoipUserT *)(&pVoipCommon->Connectionlist.LocalUsers[iValue])))) + { + // copy user id in caller-provided buffer + ds_memcpy(pBuf, &pVoipCommon->Connectionlist.LocalUsers[iValue], sizeof(VoipUserT)); + return(0); + } + } + else + { + NetPrintf(("voipcommon: VoipCommonStatus('luvu') error --> iBufSize (%d) not big enough (needs to be at least %d bytes)\n", + iBufSize, sizeof(VoipUserT))); + } + } + else + { + NetPrintf(("voipcommon: VoipCommonStatus('luvu') error --> pBuf cannot be NULL\n")); + } + + return(-1); + } + if (iSelect == 'maxc') + { + return(pVoipCommon->Connectionlist.iMaxConnections); + } + if (iSelect == 'mgrp') + { + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // return mem group id + return(iMemGroup); + } + if (iSelect == 'mgud') + { + // make sure user-provided buffer is large enough to receive a pointer + if ((pBuf != NULL) && (iBufSize >= (signed)sizeof(void *))) + { + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // fill [out] parameter with pointer to mem group user data + *((void **)pBuf) = pMemGroupUserData; + return(0); + } + else + { + // unhandled + return(-1); + } + } + if (iSelect == 'micr') + { + return(pVoipCommon->Connectionlist.uSendMask); + } + if (iSelect == 'rchn') + { + if (pBuf) + { + if (iBufSize == sizeof(uint32_t)) + { + // pBut is used as an input and an output parameter + // input -> user index + // output -> returned channels + int32_t iUserIndex = *(uint32_t *)pBuf; + *(uint32_t *)pBuf = pVoipCommon->Connectionlist.pConnections[iValue].aRecvChannels[iUserIndex]; + return(0); + } + else + { + NetPrintf(("voipcommon: VoipCommonStatus('rchn') error for conn id %d --> iBufSize (%d) does not match expected size (%d)\n", + iValue, iBufSize, sizeof(int32_t))); + } + } + else + { + NetPrintf(("voipcommon: VoipCommonStatus('rchn') error for conn id %d --> pBuf cannot be NULL\n", iValue)); + } + + return(-1); + } + if (iSelect == 'rcvu') + { + int32_t iConnIndex = iValue & 0xFFFF; + int32_t iRemoteUserIndex = iValue >> 16; + + if (pBuf) + { + if (iBufSize >= (signed)sizeof(VoipUserT)) + { + if (pVoipCommon->Connectionlist.pConnections[iConnIndex].eState != ST_DISC) + { + if ((iRemoteUserIndex < VOIP_MAXLOCALUSERS_EXTENDED) && (!VOIP_NullUser((VoipUserT *)(&pVoipCommon->Connectionlist.pConnections[iConnIndex].RemoteUsers[iRemoteUserIndex])))) + { + // copy user id in caller-provided buffer + ds_memcpy(pBuf, &pVoipCommon->Connectionlist.pConnections[iConnIndex].RemoteUsers[iRemoteUserIndex], sizeof(VoipUserT)); + return(0); + } + } + else + { + NetPrintf(("voipcommon: VoipCommonStatus('rcvu') error for conn id %d and remote user index %d --> connection is not active\n", + iConnIndex, iRemoteUserIndex)); + } + } + else + { + NetPrintf(("voipcommon: VoipCommonStatus('rcvu') error for conn id %d and remote user index %d --> iBufSize (%d) not big enough (needs to be at least %d bytes)\n", + iConnIndex, iRemoteUserIndex, iBufSize, sizeof(VoipUserT))); + } + } + else + { + NetPrintf(("voipcommon: VoipCommonStatus('rcvu') error for conn id %d and remote user index %d --> pBuf cannot be NULL\n", iConnIndex, iRemoteUserIndex)); + } + + return(-1); + } + if (iSelect == 'shch') + { + return(pVoipCommon->bUseDefaultSharedChannelConfig); + } + if (iSelect == 'sock') + { + if (pBuf != NULL) + { + if (iBufSize >= (signed)sizeof(pVoipCommon->Connectionlist.pSocket)) + { + ds_memcpy(pBuf, &pVoipCommon->Connectionlist.pSocket, sizeof(pVoipCommon->Connectionlist.pSocket)); + } + else + { + NetPrintf(("voipcommon: socket reference cannot be copied because user buffer is not large enough\n")); + return(-1); + } + } + else + { + NetPrintf(("voipcommon: socket reference cannot be copied because user buffer pointer is NULL\n")); + return(-1); + } + return(0); + } + if (iSelect == 'spkr') + { + return(pVoipCommon->Connectionlist.uRecvMask); + } + if (iSelect == 'umic') + { + return(pVoipCommon->uUserMicrValue); + } + if (iSelect == 'uspk') + { + return(pVoipCommon->uUserSpkrValue); + } + // unrecognized selector, so pass through to voipheadset + return(VoipHeadsetStatus(pVoipCommon->pHeadset, iSelect, iValue, pBuf, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonControl + + \Description + Set control options. + + \Input *pVoipCommon - voip common state + \Input iControl - control selector + \Input iValue - selector-specific input + \Input *pValue - selector-specific input + + \Output + int32_t - selector-specific output + + \Notes + iControl can be one of the following: + + \verbatim + 'clid' - set local client id + 'flsh' - send currently queued voice data immediately (iValue=connection id) + 'shch' - turn on or the of the default shared channel config. iValue = TRUE to turn, FALSE to turn off + 'stot' - turn on/off (*pValue) STT for local user specified with iValue + 'time' - set data timeout in milliseconds + 'xply' - toggle cross play + \endverbatim + + Unhandled selectors are passed through to VoipHeadsetControl() + + \Version 03/02/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipCommonControl(VoipCommonRefT *pVoipCommon, int32_t iControl, int32_t iValue, void *pValue) +{ + if (iControl == 'clid') + { + if ((pVoipCommon->Connectionlist.uClientId != 0) && (pVoipCommon->Connectionlist.uClientId != (unsigned)iValue)) + { + NetPrintf(("voipcommon: warning - local client id is being changed from %d to %d\n", pVoipCommon->Connectionlist.uClientId, iValue)); + } + pVoipCommon->Connectionlist.uClientId = (unsigned)iValue; + return(0); + } + if (iControl == 'flsh') + { + return(VoipConnectionFlush(&pVoipCommon->Connectionlist, iValue)); + } + if (iControl == 'shch') + { + pVoipCommon->bUseDefaultSharedChannelConfig = iValue; + pVoipCommon->bApplyChannelConfig = TRUE; + NetPrintf(("voipcommon: VoipCommonControl('shch') default shared channel config is %s\n", pVoipCommon->bUseDefaultSharedChannelConfig ? "on" : "off")); + return(0); + } + if (iControl == 'stot') + { + uint32_t bEnabled = TRUE; // default to 'enabled' + int32_t iUserLocalIndex = iValue; + + if (pValue != NULL) + { + bEnabled = *(uint32_t *)pValue; + } + + if ((iUserLocalIndex >= 0) && (iUserLocalIndex < VOIP_MAXLOCALUSERS)) + { + NetPrintf(("voipcommon: %s STT for local user index %d\n", (bEnabled ?"enabling":"disabling"), iUserLocalIndex)); + pVoipCommon->Connectionlist.bTranscribedTextRequested[iUserLocalIndex] = bEnabled; + return(0); + } + else + { + NetPrintf(("voipcommon: warning invalid local user index %d used with VoipControl('stot')\n", iUserLocalIndex)); + return(-1); + } + } + if (iControl == 'time') + { + pVoipCommon->Connectionlist.iDataTimeout = iValue; + return(0); + } + if (iControl == 'xply') + { + int32_t iRet; + NetCritEnter(&pVoipCommon->ThreadCrit); + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + if (iValue == TRUE) + { + VoipConnectionSetTextCallback(&pVoipCommon->Connectionlist, &_VoipCommonReceiveTextDataCb, (void *)pVoipCommon); + } + else + { + VoipConnectionSetTextCallback(&pVoipCommon->Connectionlist, NULL, NULL); + } + #endif + iRet = VoipHeadsetControl(pVoipCommon->pHeadset, 'xply', iValue, 0, pValue); + NetCritLeave(&pVoipCommon->ThreadCrit); + + return(iRet); + } + + // unhandled selectors are passed through to voipheadset + if (pVoipCommon) + { + return(VoipHeadsetControl(pVoipCommon->pHeadset, iControl, iValue, 0, pValue)); + } + else + { + // some voipheadset control selectors can be used before a call to VoipStartup() + return(VoipHeadsetControl(NULL, iControl, iValue, 0, pValue)); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonAddMask + + \Description + Add (OR) uAddMask into *pMask + + \Input *pMask - mask to add into + \Input uAddMask - mask to add (OR) + \Input *pMaskName - name of mask (for debug logging) + + \Version 12/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonAddMask(uint32_t *pMask, uint32_t uAddMask, const char *pMaskName) +{ + VoipCommonSetMask(pMask, *pMask | uAddMask, pMaskName); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonDelMask + + \Description + Del (&~) uDelMask from *pMask + + \Input *pMask - mask to del from + \Input uDelMask - mask to del (&~) + \Input *pMaskName - name of mask (for debug logging) + + \Version 12/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonDelMask(uint32_t *pMask, uint32_t uDelMask, const char *pMaskName) +{ + VoipCommonSetMask(pMask, *pMask & ~uDelMask, pMaskName); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSetMask + + \Description + Set value of mask (with logging). + + \Input *pMask - mask to write to + \Input uNewMask - new mask value + \Input *pMaskName - name of mask (for debug logging) + + \Version 12/03/2009 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipCommonSetMask(uint32_t *pMask, uint32_t uNewMask, const char *pMaskName) +{ + NetPrintf(("voipcommon: %s update: 0x%08x->0x%08x\n", pMaskName, *pMask, uNewMask)); + *pMask = uNewMask; +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSelectChannel + + \Description + Select the mode(send/recv) of a given channel. + + \Input *pVoipCommon - common module state + \Input iUserIndex - local user index + \Input iChannel - Channel ID (valid range: [0,63]) + \Input eMode - The mode, combination of VOIP_CHANSEND, VOIP_CHANRECV + + \Output + int32_t - number of channels remaining that this console could join + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipCommonSelectChannel(VoipCommonRefT *pVoipCommon, int32_t iUserIndex, int32_t iChannel, VoipChanModeE eMode) +{ + int32_t iIndex; + int32_t iSlot = VOIP_MAX_CONCURRENT_CHANNEL; + int32_t iCount = 0; + + // if the shared user index is 0xff, the current platform does not support shared channel config feature + if (iUserIndex != VOIP_SHARED_USER_INDEX) + { + if ((iUserIndex < 0) || (iUserIndex >= VOIP_MAXLOCALUSERS)) + { + NetPrintf(("voipcommon: [channel] warning - attempt to set invalid local channels index %d\n", iUserIndex)); + return(-3); + } + } + + // enforcing the valid ranges + eMode &= VOIP_CHANSENDRECV; + iChannel &= 0x3f; + + // find the slot to store the specified channel and count the slots remaining. + for(iIndex = 0; iIndex < VOIP_MAX_CONCURRENT_CHANNEL; iIndex++) + { + // remember either the slot with the channel we want to set, or if we have found nothing, an empty slot + if ( ((pVoipCommon->uLocalModes[iUserIndex][iIndex] != VOIP_CHANNONE) && (pVoipCommon->iLocalChannels[iUserIndex][iIndex] == iChannel)) || + ((pVoipCommon->uLocalModes[iUserIndex][iIndex] == VOIP_CHANNONE) && (iSlot == VOIP_MAX_CONCURRENT_CHANNEL)) ) + { + iSlot = iIndex; + } + // and take the opportunity to count the free slots + if (pVoipCommon->uLocalModes[iUserIndex][iIndex] == VOIP_CHANNONE) + { + iCount++; + } + } + + // no more slots to store the channel selection or + // the given channel doesn't exist + if (iSlot == VOIP_MAX_CONCURRENT_CHANNEL) + { + return(-1); + } + + //count if we're taking a spot. + if ((pVoipCommon->uLocalModes[iUserIndex][iSlot] == VOIP_CHANNONE) && (eMode != VOIP_CHANNONE)) + { + iCount--; + } + //count if we're freeing a spot. + if ((pVoipCommon->uLocalModes[iUserIndex][iSlot] != VOIP_CHANNONE) && (eMode == VOIP_CHANNONE)) + { + iCount++; + iChannel = 0; // when freeing a channel reset it back to the 0 to bring us towards the default state + } + + // if we are trying to set the shared channel + if (iUserIndex == VOIP_SHARED_USER_INDEX) + { + pVoipCommon->iCustomSharedChannels[iSlot] = iChannel; + pVoipCommon->uCustomSharedModes[iSlot] = eMode; + } + // set the channel and mode in selected slot + else + { + pVoipCommon->iLocalChannels[iUserIndex][iSlot] = iChannel; + pVoipCommon->uLocalModes[iUserIndex][iSlot] = eMode; + + NetPrintf(("voipcommon: [channel] set local channel at slot %d: channelId=%d, channelMode=%u for local user index %d\n", + iSlot, pVoipCommon->iLocalChannels[iUserIndex][iSlot], pVoipCommon->uLocalModes[iUserIndex][iSlot], iUserIndex)); + pVoipCommon->uLocalChannelSelection[iUserIndex] = _VoipCommonEncode(pVoipCommon->iLocalChannels[iUserIndex], pVoipCommon->uLocalModes[iUserIndex]); + NetPrintf(("voipcommon: [channel] set local channels 0x%08x for local user index %d\n", pVoipCommon->uLocalChannelSelection[iUserIndex], iUserIndex)); + } + + _VoipCommonSetChannels(pVoipCommon, pVoipCommon->uLocalChannelSelection[iUserIndex], iUserIndex); + pVoipCommon->bApplyChannelConfig = TRUE; + + return(iCount); + +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonApplyChannelConfig + + \Description + Setup user muting flags based on channel config + + \Input *pVoipCommon - voip module state + + \Todo + Rename this function to indicate that we are applying the peer to peer + mutes instead of just applying channel configurations. The name we have + now no longer makes sense. + + \Version 12/02/2009 (jrainy) +*/ +/********************************************************************************F*/ +void VoipCommonApplyChannelConfig(VoipCommonRefT *pVoipCommon) +{ + static uint32_t uLastChanSendMask = 0; + static uint32_t uLastChanRecvMask = 0; + int32_t iConnIndex, iLocalUserIndex, iRemoteUserIndex; + uint32_t uConnUserPair; + uint8_t bSocialBlocked = FALSE; + VoipChanModeE eMatch; + VoipUserT LocalUser; + + pVoipCommon->uChanRecvMask = pVoipCommon->uChanSendMask = 0; + pVoipCommon->uUserSendMask = 0; + + // setup the shared channel, to reflect the channel changes that may have occurred of the local users + _VoipCommonComputeDefaultSharedChannelConfig(pVoipCommon); + _VoipCommonSetSharedUserChannelConfig(pVoipCommon); + + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iLocalUserIndex++) + { + // find out if there is a local user for that local user index + if (VoipStatus(VoipGetRef(), 'luvu', iLocalUserIndex, &LocalUser, sizeof(LocalUser)) == 0) + { + for(iConnIndex = 0; iConnIndex < VoipStatus(VoipGetRef(), 'maxc', 0, NULL, 0); iConnIndex++) + { + if (pVoipCommon->Connectionlist.pConnections[iConnIndex].eState != ST_DISC) + { + for (iRemoteUserIndex = 0; iRemoteUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iRemoteUserIndex++) + { + // initialize variable used with VoipStatus('rcvu'). most-significant 16 bits = remote user index, least-significant 16 bits = conn index + VoipUserT RemoteUser; + uConnUserPair = iConnIndex; + uConnUserPair |= (iRemoteUserIndex << 16); + + // find out if there is a valid remote user for that connId/userId pair + if (VoipStatus(VoipGetRef(), 'rcvu', (int32_t)uConnUserPair, &RemoteUser, sizeof(RemoteUser)) == 0) + { + uint8_t bFirstPartyBlocked = FALSE; + eMatch = _VoipCommonChannelMatch(pVoipCommon, iLocalUserIndex, pVoipCommon->uRemoteChannelSelection[iConnIndex][iRemoteUserIndex]); + + // does the local user have that remote user blocked, if so do not alow voip between them? + bSocialBlocked = VoipBlockListIsBlocked((VoipRefT *)pVoipCommon, iLocalUserIndex, RemoteUser.AccountInfo.iAccountId); + + // apply first party mute list block + if (VoipHeadsetStatus(pVoipCommon->pHeadset, 'fpml', iLocalUserIndex, &RemoteUser, sizeof(RemoteUser)) > 0) + { + bFirstPartyBlocked = TRUE; + } + + // update send mask + if (!bFirstPartyBlocked && !bSocialBlocked && (eMatch & VOIP_CHANSEND)) + { + pVoipCommon->uChanSendMask |= (1 << iConnIndex); + pVoipCommon->uUserSendMask |= (1 << iLocalUserIndex); + } + + // update receive mask and user-specific playback config + if (!bFirstPartyBlocked && !bSocialBlocked && (eMatch & VOIP_CHANRECV)) + { + pVoipCommon->uChanRecvMask |= (1 << iConnIndex); + + // remote users exist in the connection list before they are registered with voipheadset later in the voip thread + // therefore we must make sure the remote user is registered with voip headset before applying voice playback + if (VoipHeadsetStatus(pVoipCommon->pHeadset, 'ruvu', 0, &RemoteUser, sizeof(RemoteUser))) + { + // enable playback of the remote user's voice for the specified local user + VoipControl(VoipGetRef(), '+pbk', iLocalUserIndex, &RemoteUser); + } + } + else + { + // remote users exist in the connection list before they are registered with voipheadset later in the voip thread + // therefore we must make sure the remote user is registered with voip headset before applying voice playback + if (VoipHeadsetStatus(pVoipCommon->pHeadset, 'ruvu', 0, &RemoteUser, sizeof(RemoteUser))) + { + // disable playback of the remote user's voice for the specified local user + VoipControl(VoipGetRef(), '-pbk', iLocalUserIndex, &RemoteUser); + } + } + } // if + } // for + } // if + } // for + } // if + } // for + + if (uLastChanSendMask != pVoipCommon->uChanSendMask) + { + NetPrintf(("voipcommon: uChanSendMask is now 0x%08x\n", pVoipCommon->uChanSendMask)); + uLastChanSendMask = pVoipCommon->uChanSendMask; + } + + if (uLastChanRecvMask != pVoipCommon->uChanRecvMask) + { + NetPrintf(("voipcommon: uChanRecvMask is now 0x%08x\n", pVoipCommon->uChanRecvMask)); + uLastChanRecvMask = pVoipCommon->uChanRecvMask; + } + + _VoipCommonComputeEffectiveUserSendMask(pVoipCommon); + _VoipCommonComputeEffectiveUserRecvMask(pVoipCommon); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonResetChannels + + \Description + Resets the channels selection to defaults. Sends and receives to all + + \Input *pVoipCommon - voip common state + \Input iUserIndex - local user index + + \Version 12/07/2009 (jrainy) +*/ +/********************************************************************************F*/ +void VoipCommonResetChannels(VoipCommonRefT *pVoipCommon, int32_t iUserIndex) +{ + // if the default config is on ignore the channel reset on the shared user + if ((pVoipCommon->bUseDefaultSharedChannelConfig) && (iUserIndex == VOIP_SHARED_USER_INDEX)) + { + return; + } + + if ((iUserIndex < 0 || iUserIndex >= VOIP_MAXLOCALUSERS) && iUserIndex != VOIP_SHARED_USER_INDEX) + { + NetPrintf(("voipcommon: [channel] warning - attempt to reset invalid local channels index %d\n", iUserIndex)); + return; + } + + _VoipCommonDecode(VOIPCOMMON_CODEDCHAN_ALL, pVoipCommon->iLocalChannels[iUserIndex], pVoipCommon->uLocalModes[iUserIndex]); + + pVoipCommon->uLocalChannelSelection[iUserIndex] = _VoipCommonEncode(pVoipCommon->iLocalChannels[iUserIndex], pVoipCommon->uLocalModes[iUserIndex]); + NetPrintf(("voipcommon: [channel] set local channels 0x%08x\n", pVoipCommon->uLocalChannelSelection[iUserIndex])); + + if (VoipGetRef() != NULL) + { + _VoipCommonSetChannels(pVoipCommon, pVoipCommon->uLocalChannelSelection[iUserIndex], iUserIndex); + pVoipCommon->bApplyChannelConfig = TRUE; + } +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonMicrophone + + \Description + Select which peers to send voice to + + \Input *pVoipCommon - voip common state + \Input uUserMicrValue - microphone bit values + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +void VoipCommonMicrophone(VoipCommonRefT *pVoipCommon, uint32_t uUserMicrValue) +{ + NetPrintf(("voipcommon: uUserMicrValue changed from 0x%08x to 0x%08x\n", pVoipCommon->uUserMicrValue, uUserMicrValue)); + pVoipCommon->uUserMicrValue = uUserMicrValue; + _VoipCommonComputeEffectiveUserSendMask(pVoipCommon); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSpeaker + + \Description + Select which peers to accept voice from + + \Input *pVoipCommon - voip common state + \Input uUserSpkrValue - speaker bit values + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +void VoipCommonSpeaker(VoipCommonRefT *pVoipCommon, uint32_t uUserSpkrValue) +{ + NetPrintf(("voipcommon: uUserSpkrValue changed from 0x%08x to 0x%08x\n", pVoipCommon->uUserSpkrValue, uUserSpkrValue)); + pVoipCommon->uUserSpkrValue = uUserSpkrValue; + _VoipCommonComputeEffectiveUserRecvMask(pVoipCommon); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnectionSharingAddSession + + \Description + Add session id to share a specified voip connection + + \Input *pVoipCommon - voip common state + \Input iConnId - connection id + \Input uSessionId - session id we are adding + + \Output + int32_t - zero=success, negative=failure + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnectionSharingAddSession(VoipCommonRefT *pVoipCommon, int32_t iConnId, uint32_t uSessionId) +{ + int32_t iRetCode; + + // acquire critical section to modify ConnectionList + NetCritEnter(&pVoipCommon->ThreadCrit); + + iRetCode = VoipConnectionAddSessionId(&pVoipCommon->Connectionlist, iConnId, uSessionId); + + // release critical section to modify ConnectionList + NetCritLeave(&pVoipCommon->ThreadCrit); + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnectionSharingDelSession + + \Description + Remove session id from sharing a specified voip connection + + \Input *pVoipCommon - voip common state + \Input iConnId - connection id + \Input uSessionId - session id we are removing + + \Output + int32_t - zero=success, negative=failure + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnectionSharingDelSession(VoipCommonRefT* pVoipCommon, int32_t iConnId, uint32_t uSessionId) +{ + int32_t iRetCode; + + // acquire critical section to modify ConnectionList + NetCritEnter(&pVoipCommon->ThreadCrit); + + iRetCode = VoipConnectionDeleteSessionId(&pVoipCommon->Connectionlist, iConnId, uSessionId); + + // release critical section to modify ConnectionList + NetCritLeave(&pVoipCommon->ThreadCrit); + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonMapVoipServerId + + \Description + For server-based voip, maps a local conn id to a voipserver conn id + + \Input *pVoipCommon - voip common state + \Input iLocalConnId - local connection id + \Input iVoipServerConnId - voipserver connection id + + \Output + int32_t - zero=success, negative=failure + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +int32_t VoipCommonMapVoipServerId(VoipCommonRefT *pVoipCommon, int32_t iLocalConnId, int32_t iVoipServerConnId) +{ + if (pVoipCommon->Connectionlist.pConnections[iLocalConnId].eState != ST_DISC) + { + NetPrintf(("voipcommon: mapping local conn id %d to voipserver conn id %d\n", iLocalConnId, iVoipServerConnId)); + pVoipCommon->Connectionlist.pConnections[iLocalConnId].iVoipServerConnId = iVoipServerConnId; + return(0); + } + else + { + NetPrintf(("voipcommon: warning - mapping local conn id to voipserver conn id ignored because connection state is ST_DISC\n")); + return(-1); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonSetLocalClientId + + \Description + Set local client id for connection + + \Input *pVoipCommon - voip common state + \Input iConnId - connection id + \Input uLocalClientId - local client id + + \Version 01/30/2019 (eesponda) +*/ +/********************************************************************************F*/ +void VoipCommonSetLocalClientId(VoipCommonRefT *pVoipCommon, int32_t iConnId, uint32_t uLocalClientId) +{ + if (pVoipCommon->Connectionlist.pConnections[iConnId].bIsLocalClientIdValid) + { + NetPrintf(("voipcommon: warning - local client id 0x%08x is being replaced with 0x%08x for conn id %d\n", + pVoipCommon->Connectionlist.pConnections[iConnId].uLocalClientId, uLocalClientId, iConnId)); + } + else + { + NetPrintf(("voipcommon: assigning local client id 0x%08x to conn id %d\n", uLocalClientId, iConnId)); + } + pVoipCommon->Connectionlist.pConnections[iConnId].uLocalClientId = uLocalClientId; + pVoipCommon->Connectionlist.pConnections[iConnId].bIsLocalClientIdValid = TRUE; +} + +/*F*************************************************************************************************/ +/*! + \Function VoipCommonSetDisplayTranscribedTextCallback + + \Description + Set callback to be invoked when transcribed text (from local user or remote user) + is ready to be displayed locally. + + \Input *pVoipCommon - voip common state + \Input *pCallback - notification handler + \Input *pUserData - user data for handler + + \Version 05/03/2017 (mclouatre) +*/ +/*************************************************************************************************F*/ +void VoipCommonSetDisplayTranscribedTextCallback(VoipCommonRefT *pVoipCommon, VoipDisplayTranscribedTextCallbackT *pCallback, void *pUserData) +{ + // acquire critical section + NetCritEnter(&pVoipCommon->ThreadCrit); + + if (pCallback == NULL) + { + pVoipCommon->pDisplayTranscribedTextCb = NULL; + pVoipCommon->pDisplayTranscribedTextUserData = NULL; + } + else + { + pVoipCommon->pDisplayTranscribedTextCb = pCallback; + pVoipCommon->pDisplayTranscribedTextUserData = pUserData; + } + + // release critical section + NetCritLeave(&pVoipCommon->ThreadCrit); +} + +/*F*************************************************************************************************/ +/*! + \Function VoipCommonSetEventCallback + + \Description + Set voip event notification handler. + + \Input *pVoipCommon - voip common state + \Input *pCallback - event notification handler + \Input *pUserData - user data for handler + + \Version 02/10/2006 (jbrookes) +*/ +/*************************************************************************************************F*/ +void VoipCommonSetEventCallback(VoipCommonRefT *pVoipCommon, VoipCallbackT *pCallback, void *pUserData) +{ + // acquire critical section + NetCritEnter(&pVoipCommon->ThreadCrit); + + if (pCallback == NULL) + { + pVoipCommon->pCallback = _VoipCommonCallback; + pVoipCommon->pUserData = NULL; + } + else + { + pVoipCommon->pCallback = pCallback; + pVoipCommon->pUserData = pUserData; + } + + // release critical section + NetCritLeave(&pVoipCommon->ThreadCrit); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnect + + \Description + Connect to a peer. + + \Input *pVoipCommon - voip common state + \Input iConnID - [zero, iMaxPeers-1] for an explicit slot or VOIP_CONNID_NONE to auto-allocate + \Input uAddress - remote peer address + \Input uManglePort - port from demangler + \Input uGamePort - port to connect on + \Input uClientId - remote clientId to connect to (cannot be 0) + \Input uSessionId - session identifier (optional) + + \Output + int32_t - connection identifier (negative=error) + + \Version 1.0 03/02/2004 (jbrookes) first version + \Version 1.1 10/26/2009 (mclouatre) uClientId is no longer optional +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnect(VoipCommonRefT *pVoipCommon, int32_t iConnID, uint32_t uAddress, uint32_t uManglePort, uint32_t uGamePort, uint32_t uClientId, uint32_t uSessionId) +{ + // acquire critical section to modify ConnectionList + NetCritEnter(&pVoipCommon->ThreadCrit); + + // initiate connection + iConnID = VoipConnectionStart(&pVoipCommon->Connectionlist, iConnID, uAddress, uManglePort, uGamePort, uClientId, uSessionId); + + // release critical section + NetCritLeave(&pVoipCommon->ThreadCrit); + + // return connection ID to caller + return(iConnID); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonDisconnect + + \Description + Disconnect from peer. + + \Input *pVoipCommon - voip common state + \Input iConnID - which connection to disconnect (VOIP_CONNID_ALL for all) + \Input bSendDiscMsg - TRUE if a voip disc pkt needs to be sent, FALSE otherwise + + \Todo + Multiple connection support. + + \Version 15/01/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipCommonDisconnect(VoipCommonRefT *pVoipCommon, int32_t iConnID, int32_t bSendDiscMsg) +{ + // acquire critical section to modify ConnectionList + NetCritEnter(&pVoipCommon->ThreadCrit); + + // shut down connection + VoipConnectionStop(&pVoipCommon->Connectionlist, iConnID, bSendDiscMsg); + + // release critical section + NetCritLeave(&pVoipCommon->ThreadCrit); +} +/*F********************************************************************************/ +/*! + \Function VoipCommonRemoteUserStatus + + \Description + Return information about remote peer. + + \Input *pVoipCommon - voip common state + \Input iConnID - which connection to get remote info for, or VOIP_CONNID_ALL + \Input iRemoteUserIndex - user index at the connection iConnID + + \Output + int32_t - VOIP_REMOTE* flags, or VOIP_FLAG_INVALID if iConnID is invalid + + \Version 05/06/2014 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t VoipCommonRemoteUserStatus(VoipCommonRefT *pVoipCommon, int32_t iConnID, int32_t iRemoteUserIndex) +{ + int32_t iRemoteStatus = VOIP_FLAG_INVALID; + + if (pVoipCommon != NULL && pVoipCommon->Connectionlist.pConnections != NULL) + { + if (iConnID == VOIP_CONNID_ALL) + { + for (iConnID = 0, iRemoteStatus = 0; iConnID < pVoipCommon->Connectionlist.iMaxConnections; iConnID++) + { + iRemoteStatus |= (int32_t)pVoipCommon->Connectionlist.pConnections[iConnID].uRemoteUserStatus[iRemoteUserIndex]; + } + } + else if ((iConnID >= 0) && (iConnID < pVoipCommon->Connectionlist.iMaxConnections)) + { + iRemoteStatus = pVoipCommon->Connectionlist.pConnections[iConnID].uRemoteUserStatus[iRemoteUserIndex]; + } + } + + return(iRemoteStatus); +} + +/*F********************************************************************************/ +/*! + \Function VoipCommonConnStatus + + \Description + Return information about peer connection. + + \Input *pVoipCommon - voip common state + \Input iConnID - which connection to get remote info for, or VOIP_CONNID_ALL + + \Output + int32_t - VOIP_CONN* flags, or VOIP_FLAG_INVALID if iConnID is invalid + + \Version 05/06/2014 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t VoipCommonConnStatus(VoipCommonRefT *pVoipCommon, int32_t iConnID) +{ + int32_t iRemoteStatus = VOIP_FLAG_INVALID; + + if (pVoipCommon != NULL && pVoipCommon->Connectionlist.pConnections != NULL) + { + if (iConnID == VOIP_CONNID_ALL) + { + for (iConnID = 0, iRemoteStatus = 0; iConnID < pVoipCommon->Connectionlist.iMaxConnections; iConnID++) + { + iRemoteStatus |= (int32_t)pVoipCommon->Connectionlist.pConnections[iConnID].uRemoteConnStatus; + } + } + else if ((iConnID >= 0) && (iConnID < pVoipCommon->Connectionlist.iMaxConnections)) + { + iRemoteStatus = pVoipCommon->Connectionlist.pConnections[iConnID].uRemoteConnStatus; + } + } + + return(iRemoteStatus); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipcommon.h b/r5dev/thirdparty/dirtysdk/source/voip/voipcommon.h new file mode 100644 index 00000000..e132df9c --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipcommon.h @@ -0,0 +1,171 @@ +/*H********************************************************************************/ +/*! + \File voipcommon.h + + \Description + Cross-platform voip data types and private functions. + + \Copyright + Copyright (c) 2009 Electronic Arts Inc. + + \Version 12/02/2009 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _voipcommon_h +#define _voipcommon_h + +/*** Include files ****************************************************************/ + +#include "voipconnection.h" +#include "voipheadset.h" +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voipblocklist.h" + + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + + +typedef struct VoipCommonRefT +{ + VoipConnectionlistT Connectionlist; //!< virtual connection list + VoipHeadsetRefT *pHeadset; //!< voip headset manager + VoipBlockListT *pBlockList; //!< muting based on persona id + + VoipCallbackT *pCallback; //!< voip event callback (optional) + void *pUserData; //!< user data for voip event callback + + VoipDisplayTranscribedTextCallbackT *pDisplayTranscribedTextCb; //!< callback invoked when some transcribed text (from a remote user or from a local user) is ready to be displayed locally (optional) + void *pDisplayTranscribedTextUserData; //!< user data associated with callback invoked when some transcribed text (from a remote user or from a local user) is ready to be displayed locally (optional) + + NetCritT ThreadCrit; //!< critical section used for thread synchronization + uint8_t bApplyChannelConfig; //!< apply channel configs on the next voip thread pass + + uint32_t uLastFrom; //!< most recent from status, used for callback tracking + uint32_t uLastHsetStatus; //!< most recent headset status, used for callback tracking + uint32_t uLastLocalStatus[VOIP_MAXLOCALUSERS]; //!< most recent local status, used for callback tracking + uint32_t uPortHeadsetStatus; //!< bitfield indicating which ports have headsets + uint32_t uLastTtsStatus; //!< most recent TTS status, used for callback tracking + uint32_t uTtsStatus; //!< current TTS status (bitmask of local user TTS flag) + uint32_t uUserSendMask; //!< current sending status (bitmask of local user send flag) + + uint8_t bUseDefaultSharedChannelConfig; //!< whether we use the default shared config or not (only supported on xbox one) + uint8_t bPrivileged; //!< whether we are communication-privileged or not + uint8_t bTextTranscriptionEnabled; //!< whether text transcription is enabled or not locally + uint8_t _pad[1]; + + uint32_t uRemoteChannelSelection[VOIP_MAX_LOW_LEVEL_CONNS][VOIP_MAXLOCALUSERS_EXTENDED]; //voipconduit; avoid name clash with new API +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "voipconnection.h" +#include "voipmixer.h" +#include "voipdvi.h" + +#include "voipconduit.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +//! name of a voip conduit +#define VOIP_ConduitName(_iConduit) ('a' + (_iConduit)) + +/*** Type Definitions *************************************************************/ +//! headset conduit info +typedef struct VoipConduitT +{ + VoipUserT PacketUser; +} VoipConduitT; + +//! conduit module state +struct VoipConduitRefT +{ + VoipMixerRefT *apMixers[VOIP_MAXLOCALUSERS]; + VoipConduitPlaybackCbT *pConduitPlayback; + void *pUserData; + int32_t iNumConduits; + int32_t iVerbosity; + VoipConduitT Conduits[1]; +}; + + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Public Variables + +// Private Variables + +/*** Private Functions ************************************************************/ + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipConduitCreate + + \Description + Create voice conduit manager. + + \Input iMaxConduits - number of conduits to support + + \Output + VoipConduitRefT * - pointer to module state, or NULL if an error occurred + + \Version 07/29/2004 (jbrookes) Split from voipheadset.c +*/ +/********************************************************************************F*/ +VoipConduitRefT *VoipConduitCreate(int32_t iMaxConduits) +{ + VoipConduitRefT *pConduitRef; + int32_t iRefSize; + int32_t iMemGroup; + void *pMemGroupUserData; + + // calculate size + iRefSize = sizeof(VoipConduitRefT) + (sizeof(VoipConduitT) * (iMaxConduits-1)); + + // allocate and clear module state + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + if ((pConduitRef = DirtyMemAlloc(iRefSize, VOIP_MEMID, iMemGroup, pMemGroupUserData )) == NULL) + { + return(NULL); + } + ds_memclr(pConduitRef, iRefSize); + + // init ref & return to caller + pConduitRef->iNumConduits = iMaxConduits; + return(pConduitRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipConduitMixerSet + + \Description + Assign a mixer to the conduit + + \Input *pConduitRef - pointer to conduit manager + \Input *pMixerRef - pointer to mixer to assign + + \Version 10/26/2011 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConduitMixerSet(VoipConduitRefT *pConduitRef, VoipMixerRefT *pMixerRef) +{ + int32_t iMixerIndex; + + // find an empty mixer in the array + for (iMixerIndex = 0; iMixerIndex < VOIP_MAXLOCALUSERS; ++iMixerIndex) + { + if (pConduitRef->apMixers[iMixerIndex] == NULL) + { + pConduitRef->apMixers[iMixerIndex] = pMixerRef; + break; + } + } + + if (iMixerIndex == VOIP_MAXLOCALUSERS) + { + NetPrintf(("voipconduit: VoipConduitMixerSet() cannot add mixer %p!\n", pMixerRef)); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipConduitMixerUnset + + \Description + Unassign a mixer to the conduit + + \Input *pConduitRef - pointer to conduit manager + \Input *pMixerRef - pointer to mixer to unassign + + \Version 03/12/2019 (tcho) +*/ +/********************************************************************************F*/ +void VoipConduitMixerUnset(VoipConduitRefT *pConduitRef, VoipMixerRefT *pMixerRef) +{ + int32_t iMixerIndex; + + // find an empty mixer in the array + for (iMixerIndex = 0; iMixerIndex < VOIP_MAXLOCALUSERS; ++iMixerIndex) + { + if (pConduitRef->apMixers[iMixerIndex] == pMixerRef) + { + pConduitRef->apMixers[iMixerIndex] = NULL; + break; + } + } + + if (iMixerIndex == VOIP_MAXLOCALUSERS) + { + NetPrintf(("voipconduit: VoipConduitMixerUnset() cannot find mixer %p!\n", pMixerRef)); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipConduitDestroy + + \Description + Destroy voice conduit manager. + + \Input *pConduitRef - pointer to conduit manager + + \Version 07/29/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConduitDestroy(VoipConduitRefT *pConduitRef) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + DirtyMemFree(pConduitRef, VOIP_MEMID, iMemGroup, pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipConduitReceiveVoiceData + + \Description + Receive voice data, and send it to the mixer. + + \Input *pConduitRef - pointer to conduit manager + \Input *pRemoteUser - user data came from + \Input *pData - incoming voice data packet + \Input iDataSize - voice data size + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConduitReceiveVoiceData(VoipConduitRefT *pConduitRef, VoipUserT *pRemoteUser, const uint8_t *pData, int32_t iDataSize) +{ + VoipConduitT *pConduit; + int32_t iConduit; + int32_t iMixerIndex; + + // copy to matching slot in queue + for (iConduit = 0; iConduit < pConduitRef->iNumConduits; iConduit++) + { + pConduit = &pConduitRef->Conduits[iConduit]; + if (VOIP_SameUser(&pConduit->PacketUser, pRemoteUser)) + { + int32_t iConduitMask = 1 << iConduit; + + for (iMixerIndex = 0; iMixerIndex < VOIP_MAXLOCALUSERS; ++iMixerIndex) + { + // decompress the frame and accumulate to the current mixbuffer + if (pConduitRef->apMixers[iMixerIndex] != NULL) + { + uint8_t bPlayback = TRUE; + + if (pConduitRef->pConduitPlayback != NULL) + { + bPlayback = pConduitRef->pConduitPlayback(pConduitRef->apMixers[iMixerIndex], pRemoteUser, pConduitRef->pUserData); + } + + if (bPlayback) + { + if (VoipMixerAccumulate(pConduitRef->apMixers[iMixerIndex], (uint8_t *)pData, iDataSize, iConduitMask, iConduit) < 0) + { + NetPrintfVerbose((pConduitRef->iVerbosity, 3, "voipconduit: [%c] index[%d], discarding packet due to mixbuffer overflow\n", VOIP_ConduitName(iConduit), iMixerIndex)); + } + } + } + } + break; + } + } + + #if DIRTYCODE_LOGGING + if (iConduit == pConduitRef->iNumConduits) + { + NetPrintf(("voipconduit: could not find a conduit for voice packet from user '%lld'\n", pRemoteUser->AccountInfo.iPersonaId)); + } + #endif +} + +/*F********************************************************************************/ +/*! + \Function VoipConduitRegisterUser + + \Description + Register/unregister a remote user with the conduit manager. + + \Input *pConduitRef - pointer to conduit manager + \Input *pRemoteUser - remote user + \Input bRegister - if TRUE, register user, else unregister user. + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConduitRegisterUser(VoipConduitRefT *pConduitRef, VoipUserT *pRemoteUser, uint32_t bRegister) +{ + VoipUserT *pConduitUser; + int32_t iConduit; + + // find an open slot + for (iConduit = 0; iConduit < pConduitRef->iNumConduits; iConduit++) + { + // ref conduit user + pConduitUser = &pConduitRef->Conduits[iConduit].PacketUser; + + if (bRegister) + { + if (VOIP_NullUser(pConduitUser)) + { + NetPrintf(("voipconduit: [%c] registering user '%lld'\n", VOIP_ConduitName(iConduit), pRemoteUser->AccountInfo.iPersonaId)); + VOIP_CopyUser(pConduitUser, pRemoteUser); + break; + } + } + else + { + if (VOIP_SameUser(pConduitUser, pRemoteUser)) + { + NetPrintf(("voipconduit: [%c] unregistering user '%lld'\n", VOIP_ConduitName(iConduit), pRemoteUser->AccountInfo.iPersonaId)); + VOIP_ClearUser(pConduitUser); + break; + } + } + } + + #if DIRTYCODE_LOGGING + if (iConduit >= pConduitRef->iNumConduits) + { + NetPrintf(("voipconduit: could not %s user '%lld'\n", + (bRegister) ? "register" : "unregister", + pRemoteUser->AccountInfo.iPersonaId)); + } + #endif +} + +/*F********************************************************************************/ +/*! + \Function VoipConduitRegisterPlaybackCb + + \Description + Register/unregister a remote user with the conduit manager. + + \Input *pConduitRef - pointer to conduit manager + \Input *pPlaybackCallback - playback callback + \Input *pUserData - callback user data + + \Version 06/24/2019 (tcho) +*/ +/********************************************************************************F*/ +void VoipConduitRegisterPlaybackCb(VoipConduitRefT *pConduitRef, VoipConduitPlaybackCbT *pPlaybackCallback, void *pUserData) +{ + pConduitRef->pConduitPlayback = pPlaybackCallback; + pConduitRef->pUserData = pUserData; +} + + +/*F********************************************************************************/ +/*! + \Function VoipConduitControl + + \Description + Control function. + + \Input *pConduitRef - pointer to conduit manager + \Input iControl - control selector + \Input iValue - control value + \Input *pValue - control value + + \Output + int32_t - selector specific, or -1 if no such selector + + \Notes + iControl can be one of the following: + + \verbatim + 'spam' - set the debug level + 'rmcb' - register playback callback + 'rmcu' - register playback call back user data + \endverbatim + + \Version 06/03/2016 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t VoipConduitControl(VoipConduitRefT *pConduitRef, int32_t iControl, int32_t iValue, void *pValue) +{ + int32_t iReturn = -1; + if (iControl == 'spam') + { + pConduitRef->iVerbosity = iValue; + iReturn = 0; + } + return(iReturn); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipconduit.h b/r5dev/thirdparty/dirtysdk/source/voip/voipconduit.h new file mode 100644 index 00000000..3b6c1bc1 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipconduit.h @@ -0,0 +1,69 @@ +/*H********************************************************************************/ +/*! + \File voipconduit.h + + \Description + VoIP data packet definitions. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 07/29/2004 (jbrookes) Split from voipheadset + \Version 12/01/2009 (jbrookes) voipchannel->voipconduit; avoid name clash with new API +*/ +/********************************************************************************H*/ + +#ifndef _voipconduit_h +#define _voipconduit_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ +//! playback callback +typedef uint8_t(VoipConduitPlaybackCbT)(VoipMixerRefT *pMixer, VoipUserT *pRemoteUser, void *pUserData); + +//! conduit manager module state +typedef struct VoipConduitRefT VoipConduitRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create conduit manager +VoipConduitRefT *VoipConduitCreate(int32_t iMaxConduits); + +// set conduit mixer +void VoipConduitMixerSet(VoipConduitRefT *pConduitRef, VoipMixerRefT *pMixerRef); + +// unset a conduit mixer +void VoipConduitMixerUnset(VoipConduitRefT *pConduitRef, VoipMixerRefT *pMixerRef); + +// destroy conduit manager +void VoipConduitDestroy(VoipConduitRefT *pConduitRef); + +// receive voice data +void VoipConduitReceiveVoiceData(VoipConduitRefT *pConduitRef, VoipUserT *pRemoteUser, const uint8_t *pData, int32_t iDataSize); + +// register a remote user +void VoipConduitRegisterUser(VoipConduitRefT *pConduitRef, VoipUserT *pRemoteUser, uint32_t bRegister); + +// register playback callback +void VoipConduitRegisterPlaybackCb(VoipConduitRefT *pConduitRef, VoipConduitPlaybackCbT *pPlaybackCallback, void *pUserData); + +// control setters +int32_t VoipConduitControl(VoipConduitRefT *pConduitRef, int32_t iControl, int32_t iValue, void *pValue); + +#ifdef __cplusplus +}; +#endif + +#endif // _voipconduit_h + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipconnection.c b/r5dev/thirdparty/dirtysdk/source/voip/voipconnection.c new file mode 100644 index 00000000..d4c36baf --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipconnection.c @@ -0,0 +1,4040 @@ +/*H********************************************************************************/ +/*! + \File voipconnection.c + + \Description + VoIP virtual connection manager. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Notes + \verbatim + + None of the packet types used by DirtySDK's voip implementation is reliable, i.e. delivery to the other end of the connection is not guaranteed. + There exists some sort of reliability during the connection establishment handshaking: voip connect packets (VoipConnPacketT) are being re-sent + until packets are detected as received by the other end of the connection. This mechanism is specific to the connection establishment and is + not generic enough for re-use after connection establishment. + + MLU VoIP join-in-progress (local user added to voip console-to-console connection after connection established) requires some + post-initial-handshaking reliability. For that purpose specifically, a reliability scheme was implemented with protocol components + piggy-backed on voip micr packets and voip ping packets. Those protocol components are: DATA, ACK and ACKCNF. + + DATA (producer->consumer): data to be delivered reliably (tagged with a sequence number) + ACK (consumer->producer): acked sequence number + ACKCNF (producer->consumer): acked sequence number confirmation (can be leveraged by the consumer to stop acking and eliminate the associated acking overhead) + + From the perspective of a game console producing reliable data, an "outbound reliable data flow" on a given connection looks like this: + + Reliable data producer (local) Reliable Data Consumer (remote) + + ------ DATA -----> + <------ ACK ----- + ------ ACKCNF -----> + + From the perspective of a game console consuming reliable data, an "inbound reliable data flow" on a given connection looks like this: + + Reliable data consumer (local) Reliable Data Producer (remote) + + <------ DATA ----- + ------ ACK -----> + <------ ACKCNF ----- + + ACK and ACKCNF protocol components are added, when necessary, as a pair to the beginning of any ping or voip packet. When the packet + is flagged with VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF, then it contains at least one such entry. The exact number of entries is + captured in the first byte of the packet payload. Then each entry consists of a 4-byte destination client id and a 1-byte field where: + bit 7 = ACKCNF (1 means "continue acking", 0 means "you can stop acking") + bits 0-6 = ACK (acked seq number in the 1 to 127 range - 0 means "unused" or "invalid") + + DATA protocol component is also added at the beginning of the voip packet payload (after ACK+ACkNF if any). When the packet + is flagged with VOIP_PACKET_RELIABLE_FLAG_DATA, then it contains at least one such entry. The exact number of entries is + captured in the first byte of the packet payload (or in the first byte following the ACK+ACKCNF portion). When a DATA entry + is created, it is queued with the SAME sequence number in the outbound queue of all connections. The use of the SAME + sequence number is key in not having to specifiy what destination console the entry is for. For P2P connectivity this is not + really a concern as packets are exchanged directly between consoles. But for voipserver-based connectivity, the voip micr + packets are sent once to the voipserver and then rebroadcasted from there to all other peers. Consequently it is important that + the sequence number that the DATA entries are tagged with are meaningful to all remote peers. + For voipserver-based voip: + Have a look at addition comments in _VoipReliableDataOutProcess() describing the logics used to add DATA entries + to voip micr packets sent to the voipserver. + \endverbatim + + \Version 1.0 03/17/2004 (jbrookes) First Version + \Version 1.1 01/06/2008 (mclouatre) Created VoipConnectionAddRef(), VoipConnectionRemoveRef(), VoipConnectionResetRef(), VoipConnectionGetFallbackAddr() + \Version 1.2 10/26/2009 (mclouatre) Renamed from xbox/voipconnection.c to xenon/voipconnectionxenon.c + \Version 1.3 11/13/2009 (jbrookes) Merged Xenon back into common version + \Version 1.4 09/29/2014 (mclouatre) Addes support for voip reliable data +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#ifdef _XBOX +#include +#include +#endif + +#include +#include +#include // for wcslen() + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" + +#include "voipconnection.h" + +/*** Defines **********************************************************************/ + +#define VOIP_PACKET_VERSION ('j') //!< current packet version +#define VOIP_PING_RATE (500) //!< send ping packets twice a second +#define VOIP_TIMEOUT (15*1000) //!< default voip data timeout +#define VOIP_CONNTIMEOUT (10*1000) //!< connection timeout +#define VOIP_MSPERPACKET (100) //!< number of ms per network packet (100ms = 10hz) + +//! enable for verbose debugging +#define VOIP_CONNECTION_DEBUG (DIRTYCODE_DEBUG && FALSE) +#define VOIP_RELIABLE_DEBUG (DIRTYCODE_DEBUG && FALSE) + +#define VOIP_IPPROTO (IPPROTO_IP) + +/*** Macros ***********************************************************************/ + +//! compare packet types +#define VOIP_SamePacketType(_packetHead1, _packetHead2) \ + (!memcmp((_packetHead1)->aType, (_packetHead2)->aType, sizeof((_packetHead1)->aType))) + +//! get connection ID +#define VOIP_ConnID(_pConnectionlist, _pConnection) ((uint32_t)((_pConnection)-(_pConnectionlist)->pConnections)) + +/*** Type Definitions *************************************************************/ + +#if DIRTYCODE_LOGGING +const char *_strReliableType[] = +{ + "USERADD", + "USERREM", + "OPAQUE ", + "TEXT " +}; +#endif + +/*** Function Prototypes **********************************************************/ + +static void _VoipConnectionStop(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnID, int32_t bSendDiscMsg); +static void _VoipEncodeU16(uint8_t *pValue, uint16_t uValue); +static uint16_t _VoipDecodeU16(const uint8_t *pValue); + +/*** Variables ********************************************************************/ + +//! VoIP conn packet header +static VoipPacketHeadT _Voip_ConnPacket = +{ + { 'C', 'O', VOIP_PACKET_VERSION }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0} +}; + +//! VoIP disc packet header +static VoipPacketHeadT _Voip_DiscPacket = +{ + { 'D', 'S', 'C' }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0} +}; + +//! VoIP ping packet header +static VoipPacketHeadT _Voip_PingPacket = +{ + { 'P', 'N', 'G' }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0} +}; + +//! VoIP mic data packet header +static VoipPacketHeadT _Voip_MicrPacket = +{ + { 'M', 'I', 'C' }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0} +}; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _VoipEncodeLocalHeadsetStatus + + \Description + Encode the uLocalUserStatus fields in the packet header + + \Input *pConnectionlist - information read from here to be written to the packet header + \Input *pHead - packet header to write to + + \Version 11/20/2008 (cvienneau) +*/ +/********************************************************************************F*/ +static void _VoipEncodeLocalHeadsetStatus(VoipConnectionlistT *pConnectionlist, VoipPacketHeadT *pHead) +{ + uint16_t uFlags = 0; + int32_t i = 0; + for (i = 0; i < VOIP_MAXLOCALUSERS; ++i) + { + if (pConnectionlist->uLocalUserStatus[i] & VOIP_LOCAL_USER_HEADSETOK) + { + uFlags |= (1 << i); + } + } + + // Encode 16 for byte ordering + _VoipEncodeU16(pHead->aHeadsetStat, uFlags); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDecodeRemoteHeadsetStatus + + \Description + Reads aHeadsetStat field from an incoming packet and sets appropriate state + about the remote connection. + + \Input *pConnection - connection to write state to + \Input *pHead - packet header to read information from + + \Version 11/20/2008 (cvienneau) +*/ +/********************************************************************************F*/ +static void _VoipDecodeRemoteHeadsetStatus(VoipConnectionT *pConnection, VoipPacketHeadT *pHead) +{ + uint32_t iIndex; + uint16_t uFlags = _VoipDecodeU16(pHead->aHeadsetStat); + + for (iIndex = 0; iIndex < VOIP_MAXLOCALUSERS; ++iIndex) + { + uint16_t uMask = 1 << iIndex; + if (uFlags & uMask) + { + pConnection->uRemoteUserStatus[iIndex] |= VOIP_REMOTE_USER_HEADSETOK; //on + } + else + { + pConnection->uRemoteUserStatus[iIndex] &= ~VOIP_REMOTE_USER_HEADSETOK; //off + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipDecodeU64 + + \Description + Decode uint64_t from given packet structure. + + \Input *pValue - network-order data field + + \Output + uint64_t - decoded value + + \Version 07/03/2019 (tcho) +*/ +/********************************************************************************F*/ +static uint64_t _VoipDecodeU64(const uint8_t *pValue) +{ + uint64_t uValue = (uint64_t)pValue[0] << 56; + uValue |= (uint64_t)pValue[1] << 48; + uValue |= (uint64_t)pValue[2] << 40; + uValue |= (uint64_t)pValue[3] << 32; + uValue |= (uint64_t)pValue[4] << 24; + uValue |= (uint64_t)pValue[5] << 16; + uValue |= (uint64_t)pValue[6] << 8; + uValue |= (uint64_t)pValue[7]; + return(uValue); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDecodeU32 + + \Description + Decode uint32_t from given packet structure. + + \Input *pValue - network-order data field + + \Output + uint32_t - decoded value + + \Version 06/02/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _VoipDecodeU32(const uint8_t *pValue) +{ + uint32_t uValue = pValue[0] << 24; + uValue |= pValue[1] << 16; + uValue |= pValue[2] << 8; + uValue |= pValue[3]; + return(uValue); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDecodeU16 + + \Description + Decode uint16_t from given packet structure. + + \Input *pValue - network-order data field + + \Output + uint16_t - decoded value + + \Version 05/02/2014 (jbrookes) +*/ +/********************************************************************************F*/ +static uint16_t _VoipDecodeU16(const uint8_t *pValue) +{ + uint16_t uValue = pValue[0] << 8; + uValue |= pValue[1]; + return(uValue); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDecodeVoipUser + + \Description + Decode a voip user to a voip user packet + + \Input *pUserPacket - input voip user packet to decode + \Input *pUser - voip user to storge the decoded result + + \Version 07/03/2019 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipDecodeVoipUser(VoipUserPacketT *pUserPacket, VoipUserT *pUser) +{ + pUser->AccountInfo.iAccountId = (int64_t)_VoipDecodeU64(pUserPacket->aAccountId); + pUser->AccountInfo.iPersonaId = (int64_t)_VoipDecodeU64(pUserPacket->aPersonaId); + pUser->uFlags = _VoipDecodeU32(pUserPacket->aFlags); + pUser->ePlatform = _VoipDecodeU32(pUserPacket->aPlatform); +} + +/*F********************************************************************************/ +/*! + \Function _VoipEncodeU64 + + \Description + Encode given uint64_t in network order + + \Input *pValue - storage for network-order u64 + \Input uValue - u64 value to encode + + \Version 07/03/2019 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipEncodeU64(uint8_t *pValue, uint64_t uValue) +{ + pValue[0] = (uint8_t)(uValue >> 56); + pValue[1] = (uint8_t)(uValue >> 48); + pValue[2] = (uint8_t)(uValue >> 40); + pValue[3] = (uint8_t)(uValue >> 32); + pValue[4] = (uint8_t)(uValue >> 24); + pValue[5] = (uint8_t)(uValue >> 16); + pValue[6] = (uint8_t)(uValue >> 8); + pValue[7] = (uint8_t)(uValue); +} + +/*F********************************************************************************/ +/*! + \Function _VoipEncodeU32 + + \Description + Encode given uint32_t in network order + + \Input *pValue - storage for network-order u32 + \Input uValue - u32 value to encode + + \Version 06/02/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipEncodeU32(uint8_t *pValue, uint32_t uValue) +{ + pValue[0] = (uint8_t)(uValue >> 24); + pValue[1] = (uint8_t)(uValue >> 16); + pValue[2] = (uint8_t)(uValue >> 8); + pValue[3] = (uint8_t)uValue; +} + +/*F********************************************************************************/ +/*! + \Function _VoipEncodeU16 + + \Description + Encode given uint16_t in network order + + \Input *pValue - storage for network-order u32 + \Input uValue - u16 value to encode + + \Version 05/02/2014 (amakoukji) +*/ +/********************************************************************************F*/ +static void _VoipEncodeU16(uint8_t *pValue, uint16_t uValue) +{ + pValue[0] = (uint8_t)(uValue >> 8); + pValue[1] = (uint8_t)uValue; +} + +/*F********************************************************************************/ +/*! + \Function _VoipEncodeVoipUser + + \Description + Encodes a voip user to a voip user packet + + \Input *pUserPacket - output voip user packet + \Input *pUser - user to encode + + \Version 07/03/2019 (tcho) +*/ +/********************************************************************************F*/ +static void _VoipEncodeVoipUser(VoipUserPacketT *pUserPacket, VoipUserT *pUser) +{ + _VoipEncodeU64(pUserPacket->aAccountId, (uint64_t) pUser->AccountInfo.iAccountId); + _VoipEncodeU64(pUserPacket->aPersonaId, (uint64_t) pUser->AccountInfo.iPersonaId); + _VoipEncodeU32(pUserPacket->aFlags, pUser->uFlags); + _VoipEncodeU32(pUserPacket->aPlatform, pUser->ePlatform); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionIncrementReliableSeqNb + + \Description + Return the next seq nb following the specified seq nb. + Sequence number validity range: [1,127] (0 = invalid) + + \Input uSeqNb - input sequence number + + \Output + uint8_t - output sequence number + + \Version 09/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t _VoipConnectionIncrementReliableSeqNb(uint8_t uSeqNb) +{ + if (uSeqNb == 127) + { + return(1); + } + else + { + return(uSeqNb + 1); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataEnqueue + + \Description + Add a reliable data entry to the tail of the specified linked list. + + \Input **pListHead - pointe to head of list + \Input *pNewEntry - entry to be added + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipReliableDataEnqueue(LinkedReliableDataT **pListHead, LinkedReliableDataT *pNewEntry) +{ + if (*pListHead == NULL) + { + *pListHead = pNewEntry; + } + else + { + // find tail of list and append + LinkedReliableDataT *pCurrent = *pListHead; + while (pCurrent->pNext != NULL) + { + pCurrent = pCurrent->pNext; + } + pCurrent->pNext = pNewEntry; + } + + pNewEntry->pNext = NULL; +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataDequeue + + \Description + Remove a reliable data entry from the tail of the specified linked list. + + \Input **pListHead - pointer to head of list + + \Output + LinkedReliableDataT * - pointer to returned buffer; NULL if error + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static LinkedReliableDataT *_VoipReliableDataDequeue(LinkedReliableDataT **pListHead) +{ + LinkedReliableDataT *pRemovedEntry = NULL; + + // return head of list to caller + if (*pListHead) + { + pRemovedEntry = *pListHead; + *pListHead = (*pListHead)->pNext; + pRemovedEntry->pNext = NULL; + } + + return(pRemovedEntry); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGetReliableDataBufferFromFreePool + + \Description + Obtain a free reliable data buffer from the free pool. + (Extract from head of linked list) + + \Input *pConnectionlist - connection list + \Input bAllocIfEmpty - enable/disable allocating buffer in pool is empty + + \Output + LinkedReliableDataT * - pointer to returned buffer; NULL if error + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static LinkedReliableDataT *_VoipGetReliableDataBufferFromFreePool(VoipConnectionlistT *pConnectionlist, uint8_t bAllocIfEmpty) +{ + LinkedReliableDataT *pReliableDataBuffer; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query current mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // get a buffer from the pool of free buffer + pReliableDataBuffer = _VoipReliableDataDequeue(&pConnectionlist->pFreeReliableDataPool); + + // if pool is empty, allocate a new entry + if ((pReliableDataBuffer == NULL) && (bAllocIfEmpty != FALSE)) + { + if ((pReliableDataBuffer = (LinkedReliableDataT *)DirtyMemAlloc(sizeof(*pReliableDataBuffer), VOIP_MEMID, iMemGroup, pMemGroupUserData)) != NULL) + { + ds_memclr(pReliableDataBuffer, sizeof(*pReliableDataBuffer)); + + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: new free reliable data buffer (%p) added to free pool\n", pReliableDataBuffer)); + #endif + } + else + { + NetPrintf(("voipconnection: error, unable to allocate reliable data buffer\n")); + } + } + + #if VOIP_RELIABLE_DEBUG + if (pReliableDataBuffer) + { + NetPrintf(("voipconnection: reliable data buffer (%p) obtained from free pool\n", pReliableDataBuffer)); + } + else + { + NetPrintf(("voipconnection: free pool of reliable data buffers is now empty\n")); + } + #endif + + return(pReliableDataBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReturnReliableDataBufferToFreePool + + \Description + Return buffer to free pool. (Append to tail of linked list) + + \Input *pConnectionlist - connection list + \Input *pFreeBuffer - buffer to be returned to pool + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipReturnReliableDataBufferToFreePool(VoipConnectionlistT *pConnectionlist, LinkedReliableDataT *pFreeBuffer) +{ + _VoipReliableDataEnqueue(&pConnectionlist->pFreeReliableDataPool, pFreeBuffer); + + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: reliable data buffer (%p) returned to free pool\n", pFreeBuffer)); + #endif +} + +/*F********************************************************************************/ +/*! + \Function _VoipPacketGetWritePtr + + \Description + Calculate next write position in the specified voip packet + + \Input *pMicrPacket - packet pointer + + \Output + uint8_t * - next write position in the packet + + \Version 07/18/2013 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t *_VoipPacketGetWritePtr(VoipMicrPacketT *pMicrPacket) +{ + uint8_t *pWrite = &pMicrPacket->aData[0]; + + // is the packet buffer already filled with ACK+ACKCNF protocol components used for the reliability mechanism? + if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF) + { + int32_t iReliableAckEntriesCount = *pWrite++; + pWrite += (iReliableAckEntriesCount * sizeof(ReliableAckT)); + } + + // is the packet buffer already filled with the DATA protocol component used for the reliability mechanism? + if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA) + { + int32_t iReliableDataEntriesCount = *pWrite++; + int32_t iReliableDataEntryIndex; + + for (iReliableDataEntryIndex = 0; iReliableDataEntryIndex < iReliableDataEntriesCount; iReliableDataEntryIndex++) + { + ReliableDataT *pCurrent = (ReliableDataT *)pWrite; + uint32_t uSize = _VoipDecodeU16(&pCurrent->info.uSize[0]); + + pWrite += sizeof(pCurrent->info) + uSize; + } + } + + // is the packet buffer already filled with some metadata? + if (pMicrPacket->Head.uFlags & VOIP_PACKET_STATUS_FLAG_METADATA) + { + int32_t iMetaDataSize; + iMetaDataSize = *pWrite++; + pWrite += iMetaDataSize; + } + + // is the packet buffer already filled with some sub-pkts? + if (pMicrPacket->MicrInfo.uNumSubPackets != 0) + { + uint8_t bVariableSubPktLen = ((pMicrPacket->MicrInfo.uSubPacketSize == 0xFF) ? TRUE : FALSE); // check if sub-pkts in this packet are fixed length or variable length + + // if necessary, jump over sub-pkts already packed in the packet buffer + if (bVariableSubPktLen) + { + int32_t iSubPktIndex, iSubPacketSize; + + // find where we need to write; each sub-packet is prepended (one byte) with its length + for (iSubPktIndex = 0; iSubPktIndex < pMicrPacket->MicrInfo.uNumSubPackets; iSubPktIndex++) + { + iSubPacketSize = *pWrite; + pWrite += (iSubPacketSize + 1); + } + } + else + { + pWrite += (pMicrPacket->MicrInfo.uNumSubPackets * pMicrPacket->MicrInfo.uSubPacketSize); + } + } + + return(pWrite); +} + + +/*F********************************************************************************/ +/*! + \Function _VoipPacketGetMetaDataPtr + + \Description + Find position of metadata in the specified voip packet + + \Input *pMicrPacket - packet pointer + + \Output + uint8_t * - ptr to metadata + + \Version 09/25/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t *_VoipPacketGetMetaDataPtr(VoipMicrPacketT *pMicrPacket) +{ + uint8_t *pMetaData = &pMicrPacket->aData[0]; + + // is the packet buffer already filled with ACK+ACKCNF protocol components used for the reliability mechanism? + if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF) + { + int32_t iReliableAckEntriesCount = *pMetaData++; + pMetaData += (iReliableAckEntriesCount * sizeof(ReliableAckT)); + } + + // is the packet buffer already filled with the DATA protocol component used for the reliability mechanism? + if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA) + { + int32_t iReliableDataEntriesCount = *pMetaData++; + int32_t iReliableDataEntryIndex; + + for (iReliableDataEntryIndex = 0; iReliableDataEntryIndex < iReliableDataEntriesCount; iReliableDataEntryIndex++) + { + ReliableDataT *pCurrent = (ReliableDataT *)pMetaData; + uint32_t uSize = _VoipDecodeU16(&pCurrent->info.uSize[0]); + + pMetaData += sizeof(pCurrent->info) + uSize; + } + } + + return(pMetaData); +} + +/*F********************************************************************************/ +/*! + \Function _VoipPrepareVoipServerSendMask + + \Description + Convert local send mask to voip-server ready send mask + + \Input *pConnectionlist - connection list + + \Output + uint32_t - send mask to be packed in voip packet sent to voip server + + \Version 10/27/2006 (mclouatre) +*/ +/********************************************************************************F*/ +static uint32_t _VoipPrepareVoipServerSendMask(VoipConnectionlistT *pConnectionlist) +{ + int32_t iLocalConnId, iVoipServerConnId; + uint32_t uLocalSendMask = pConnectionlist->uSendMask; + uint32_t uVoipServerSendMask = 0; + + for (iLocalConnId = 0; iLocalConnId < pConnectionlist->iMaxConnections; iLocalConnId++) + { + iVoipServerConnId = pConnectionlist->pConnections[iLocalConnId].iVoipServerConnId; + + // make sure connection is used by voip server + if (iVoipServerConnId != VOIP_CONNID_NONE) + { + // make sure local connection has send bit set + if (uLocalSendMask & (1 << iLocalConnId)) + { + // set the proper bit in the send mask for the voip server + uVoipServerSendMask |= (1 << iVoipServerConnId); + } + } + } + + return(uVoipServerSendMask); +} + + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionIsTranscribedTextRequested + + \Description + Returns TRUE if at least one local user is requesting text + transcription from remote talkers. + + \Input *pConnectionlist - pointer to connectionlist + + \Output + uint32_t - TRUE or FALSE + + \Version 05/05/2017 (mclouatre) +*/ +/********************************************************************************F*/ +static uint32_t _VoipConnectionIsTranscribedTextRequested(VoipConnectionlistT *pConnectionlist) +{ + + int32_t iUserIndex; + + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; iUserIndex++) + { + if (pConnectionlist->aIsParticipating[iUserIndex] != FALSE) + { + if (pConnectionlist->bTranscribedTextRequested[iUserIndex] != FALSE) + { + return(TRUE); + } + } + } + + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionAllocate + + \Description + Allocate an empty connection for use. + + \Input *pConnectionlist - pointer to connectionlist + \Input iConnID - connection to allocate (or VOIP_CONNID_*) + + \Output + VoipConnectionT * - pointer to connection to use or NULL + + \Version 03/17/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static VoipConnectionT *_VoipConnectionAllocate(VoipConnectionlistT *pConnectionlist, int32_t iConnID) +{ + VoipConnectionT *pConnection = NULL; + + // get a connection ID + pConnection = NULL; + if (iConnID == VOIP_CONNID_NONE) + { + // find a free connection slot + for (iConnID = 0; iConnID < pConnectionlist->iMaxConnections; iConnID++) + { + // if this is an unallocated connection + if (pConnectionlist->pConnections[iConnID].eState == ST_DISC) + { + pConnection = &pConnectionlist->pConnections[iConnID]; + break; + } + } + + // make sure we found a connection + if (pConnection == NULL) + { + NetPrintf(("voipconnection: out of connections\n")); + } + } + else if ((iConnID >= 0) && (iConnID < pConnectionlist->iMaxConnections)) + { + // if we're currently connected, complain + if (pConnectionlist->pConnections[iConnID].eState != ST_DISC) + { + NetPrintf(("voipconnection: [%d] connection not available, currently used for clientId=%d\n", iConnID, pConnectionlist->pConnections[iConnID].uRemoteClientId)); + return(NULL); + } + + // ref connection + pConnection = &pConnectionlist->pConnections[iConnID]; + } + else + { + NetPrintf(("voipconnection: %d is an invalid connection id\n", iConnID)); + } + + // return connection ref to caller + return(pConnection); +} + +/*F********************************************************************************/ +/*! + \Function _VoipSocketSendto + + \Description + Send data with the given socket. + + \Input *pSocket - socket to send on + \Input uClientId - client identifier + \Input *pBuf - data to send + \Input iLen - size of data to send + \Input iFlags - send flags + \Input *pTo - address to send data to + \Input iToLen - length of address structure + + \Output + int32_t - amount of data sent + + \Version 11/30/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipSocketSendto(SocketT *pSocket, uint32_t uClientId, VoipPacketHeadT *pBuf, int32_t iLen, int32_t iFlags, struct sockaddr *pTo, int32_t iToLen) +{ + // make sure socket exists + if (pSocket == NULL) + { + return(0); + } + + // set connection identifier (goes in all packets) + _VoipEncodeU32(pBuf->aClientId, uClientId); + + // send the packet + return(SocketSendto(pSocket, (char *)pBuf, iLen, iFlags, pTo, iToLen)); +} + + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionMatchSessionId + + \Description + Check whether specified session ID matchs the voip connection. + + \Input *pConnectionlist - connectionlist + \Input iConnId - connection ID + \Input uSessionId - session ID + + \Output + uint8_t - TRUE for match, FALSE for no match + + \Version 08/30/2011 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t _VoipConnectionMatchSessionId(VoipConnectionlistT *pConnectionlist, int32_t iConnId, uint32_t uSessionId) +{ + int32_t iSessionIndex; + int32_t bMatch = FALSE; + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId]; + + NetCritEnter(&pConnectionlist->NetCrit); + + for (iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++) + { + if (pConnection->uSessionId[iSessionIndex] == uSessionId) + { + bMatch = TRUE; + } + } + + NetCritLeave(&pConnectionlist->NetCrit); + + return(bMatch); +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _VoipConnectionPrintRemoteUsers + + \Description + Print remote users + + \Input *pConnection - connection to transition + \Input iConnID - index of connection + + \Version 03/11/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipConnectionPrintRemoteUsers(VoipConnectionT *pConnection, int32_t iConnID) +{ + int32_t iIndex; + + for (iIndex = 0; iIndex < pConnection->iMaxRemoteUsers; iIndex++) + { + if (!VOIP_NullUser((VoipUserT *)(&pConnection->RemoteUsers[iIndex]))) + { + NetPrintf(("voipconnection: [%d] remote user #%d --> %lld\n", iConnID, iIndex, ((VoipUserT *)&pConnection->RemoteUsers[iIndex])->AccountInfo.iPersonaId)); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionPrintSessionIds + + \Description + Prints all session IDs sharing this voip connection + + \Input *pConnectionlist - connectionlist + \Input iConnId - connection id + + \Version 08/30/2011 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipConnectionPrintSessionIds(VoipConnectionlistT *pConnectionlist, int32_t iConnId) +{ + int32_t iSessionIndex; + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId]; + + NetCritEnter(&pConnectionlist->NetCrit); + + if (pConnection->uSessionId[0] != 0) + { + NetPrintf(("voipconnection: [%d] IDs of sessions sharing this voip connection are: ", iConnId)); + for (iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++) + { + if (pConnection->uSessionId[iSessionIndex] != 0) + { + NetPrintf(("0x%08x\n", pConnection->uSessionId[iSessionIndex])); + } + else + { + break; + } + } + } + else + { + NetPrintf(("voipconnection: [%d] there is no session ID associated with this connection\n", iConnId)); + } + + NetCritLeave(&pConnectionlist->NetCrit); +} +#endif // DIRTYCODE_LOGGING + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionReliableDataEnqueue + + \Description + Add a reliable data entry to the outbound queue of the + specified connection. (Append to tail of linked list) + + \Input *pConnectionlist - connection list + \Input iConnID - connection identifier (VOIP_CONNID_ALL for all) + \Input *pReliableData - pointer to reliable data to be enqueued + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipConnectionReliableDataEnqueue(VoipConnectionlistT *pConnectionlist, int32_t iConnID, ReliableDataT *pReliableData) +{ + int32_t iConnectionIndex; + uint32_t uCurrentTick = NetTick(); + + if (iConnID == VOIP_CONNID_ALL) + { + iConnectionIndex = 0; + } + else + { + iConnectionIndex = iConnID; + } + + while (iConnectionIndex < pConnectionlist->iMaxConnections) + { + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex]; + + if (pConnection->eState == ST_ACTV || pConnection->eState == ST_CONN) + { + LinkedReliableDataT *pNewEntry = _VoipGetReliableDataBufferFromFreePool(pConnectionlist, TRUE); + + if (pNewEntry) + { + // fill with input data + ds_memcpy_s(&pNewEntry->data, sizeof(pNewEntry->data), pReliableData, sizeof(*pReliableData)); + + // fill sequence number + pNewEntry->data.info.uSeq = pConnection->uOutSeqNb; + pConnection->uOutSeqNb = _VoipConnectionIncrementReliableSeqNb(pConnection->uOutSeqNb); + + // set remote peer id + _VoipEncodeU32(&pNewEntry->data.info.aRemoteClientId[0], pConnection->uRemoteClientId); + + // fill enqueue tick + pNewEntry->uEnqueueTick = uCurrentTick; + + // add to oubtound queue + _VoipReliableDataEnqueue(&pConnection->pOutboundReliableDataQueue, pNewEntry); + + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] reliable data buffer (%p) added to outbound queue (seq# %03d, %s, tick=%u)\n", iConnectionIndex, pNewEntry, + pNewEntry->data.info.uSeq, _strReliableType[pNewEntry->data.info.uType], pNewEntry->uEnqueueTick)); + #endif + + /* + In the case of voipserver-based topology, to improve responsiveness and increase chances of reliable data + making it quick to other end, we alter the last send tick on the connection. This should result in a + ping packets being sent immediately, and help with reliable data reaching other end for voip connections + that see their voip traffic squelched by the voip server. + */ + if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE) + { + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] last send tick altered to speed up sending of reliable data via ping packet\n", iConnectionIndex)); + #endif + pConnection->uLastSend = pConnection->uLastSend - VOIP_PING_RATE; + } + } + else + { + NetPrintf(("voipconnection: [%d] failed to obtain resources for enqueueing new outbound reliable data\n", iConnectionIndex)); + } + } + + // break out of while loop if we are dealing with a single connection only + if (iConnID == VOIP_CONNID_ALL) + { + iConnectionIndex++; + } + else + { + break; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionReliableDataDequeue + + \Description + Remove reliable data entry from the outbound queue of the + specified connection. (Remove entry at head of linked list) + + \Input *pConnectionlist - connection list + \Input iConnID - connection identifier (VOIP_CONNID_ALL for all) + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipConnectionReliableDataDequeue(VoipConnectionlistT *pConnectionlist, int32_t iConnID) +{ + LinkedReliableDataT *pRemovedEntry = _VoipReliableDataDequeue(&pConnectionlist->pConnections[iConnID].pOutboundReliableDataQueue); + + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] reliable data buffer (%p) removed from outbound queue (seq# %03d, %s)\n", iConnID, pRemovedEntry, + pRemovedEntry->data.info.uSeq, _strReliableType[pRemovedEntry->data.info.uType])); + #endif + + _VoipReturnReliableDataBufferToFreePool(pConnectionlist, pRemovedEntry); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataPackACKandACKCNF + + \Description + Outbound processing of reliablity data - deals with ACK+ACKCNF protocol + components specifically. + + \Input *pConnectionlist - connectionlist ref + \Input iConnID - id of connection to be acked; VOIP_CONNID_ALL to ack all connections + \Input *pFlags - pointer to uFlags field of the outbound packet + \Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet + \Input *pWrite - pointer to next byte to be written in packet payload + \Input iSpaceLeftInPkt - amount of space left in packet payload + + \Output + uint8_t * - pointer to first byte after the newly added ACK + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t * _VoipReliableDataPackACKandACKCNF(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint8_t *pFlags, uint32_t bPing, uint8_t *pWrite, int32_t iSpaceLeftInPkt) +{ + uint8_t uEntryCount = 0; + uint8_t *pEntryCount = pWrite; // remember where the entry count needs to be written if any entry added to packet buffer + uint32_t uSpaceLeftInPkt = (uint32_t)iSpaceLeftInPkt; // amount of space still available in packet for reliable data + int32_t iConnectionIndex; + + // skip the byte where the entry count is supposed to be written + pWrite++; + + if (iConnID == VOIP_CONNID_ALL) + { + iConnectionIndex = 0; + } + else + { + iConnectionIndex = iConnID; + } + + while (iConnectionIndex < pConnectionlist->iMaxConnections) + { + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex]; + uint32_t bAckOrAckCnfNeeded = FALSE; + + // if our outbound queue is not empty or if the peer at the remote end of the connection is still acking us, then + // add ACK+ACKCNF protocol component to packet. + if ((pConnectionlist->pConnections[iConnectionIndex].pOutboundReliableDataQueue != NULL) || pConnection->bPeerIsAcking) + { + bAckOrAckCnfNeeded = TRUE; + } + + // make sure there is enough space left in destination packet buffer + if (sizeof(ReliableAckT) < uSpaceLeftInPkt) + { + // do we need to send ACK or ACKCNF to this remote client + if (pConnection->bPeerNeedsAck || bAckOrAckCnfNeeded) + { + ReliableAckT ackEntry; + + // set remote peer id + _VoipEncodeU32(&ackEntry.aRemoteClientId[0], pConnection->uRemoteClientId); + + // Fill in ACK value + if (pConnection->bPeerNeedsAck) + { + /* validty range: [1,127] + In a scenario where we have no yet received inbound reliable traffic from remote peer, pConnection->uInSeqNb + is 0 and we end up initializing ackEntry.uAckedSeq with 0 which means 'ignore this field, no sequence number acked' */ + ackEntry.uAckedSeq = pConnection->uInSeqNb; + } + else + { + // means: 'ignore this field, no sequence number acked' + ackEntry.uAckedSeq = 0; + } + + // set ACKCNF + if (bAckOrAckCnfNeeded) + { + // if our outbound queue is not empty, flag that we are still expecting acks back + if (pConnection->pOutboundReliableDataQueue != NULL) + { + ackEntry.uAckedSeq |= VOIP_RELIABLE_ACKCNF_MASK; + } + } + + // add entry in the packet + ds_memcpy_s(pWrite, uSpaceLeftInPkt, &ackEntry, sizeof(ackEntry)); + pWrite += sizeof(ackEntry); + + // removed space used from space left + uSpaceLeftInPkt -= sizeof(ackEntry); + + // increment entry count + uEntryCount += 1; + + // flag that packet contains at least one ACK+ACKCNF protocol component entry + *pFlags |= VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF; + + #if VOIP_RELIABLE_DEBUG + if (pConnection->bPeerNeedsAck) + { + NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# %03d, %s) added to outbound %s packet for clientId=0x%08x\n", iConnectionIndex, + (ackEntry.uAckedSeq & 0x7F), ((pConnection->pOutboundReliableDataQueue != NULL)?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), pConnection->uRemoteClientId)); + } + else + { + NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# INV, %s) added to outbound %s packet for clientId=0x%08x\n", iConnectionIndex, + ((pConnection->pOutboundReliableDataQueue != NULL) ?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), pConnection->uRemoteClientId)); + } + #endif + } + } + else + { + NetPrintf(("voipconnection: warning - failed to fit all ACK entries in outbound %s packet\n", (bPing?"PNG":"MIC"))); + break; // no space left in packet, force while loop to exit + } + + // break out of while loop if we are dealing with a single connection only + if (iConnID == VOIP_CONNID_ALL) + { + iConnectionIndex++; + } + else + { + break; + } + } + + if (uEntryCount == 0) + { + // reposition pWrite to its initial value + pWrite = pEntryCount; + } + else + { + // write the entry count in the first byte that the input pWrite was pointing to + *pEntryCount = uEntryCount; + } + + return(pWrite); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataPackDATA + + \Description + Outbound processing of reliablity data - deals with DATA protocol component specifically. + + \Input *pConnectionlist - connectionlist ref + \Input iConnID - id of connection on which the outbound packet is being sent; CANNOT be VOIP_CONNID_ALL + \Input *pFlags - pointer to uFlags field of the outbound packet + \Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet + \Input *pWrite - pointer to next byte to be written in packet payload + \Input iSpaceLeftInPkt - amount of space left in packet payload + + \Output + uint8_t * - pointer to first byte after the newly added DATA + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t * _VoipReliableDataPackDATA(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint8_t *pFlags, uint32_t bPing, uint8_t *pWrite, int32_t iSpaceLeftInPkt) +{ + uint8_t uEntryCount = 0; + uint8_t *pEntryCount = pWrite; // remember where the entry count needs to be written if any entry added to packet buffer + uint32_t uSpaceLeftInPkt = (uint32_t)iSpaceLeftInPkt; // amount of space still available in packet for reliable data + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID]; + LinkedReliableDataT *pCurrent = pConnection->pOutboundReliableDataQueue; + + // skip the byte where the entry count is supposed to be written + pWrite++; + + // find how many entries from the outbound reliable data queue fit in the outbound packet + while (pCurrent != NULL) + { + uint16_t uSize = _VoipDecodeU16(&pCurrent->data.info.uSize[0]); + + if (uSize < uSpaceLeftInPkt) + { + // pack reliable data info + ds_memcpy(pWrite, &pCurrent->data.info, sizeof(pCurrent->data.info)); + pWrite += sizeof(pCurrent->data.info); + + // pack reliable data payload + ds_memcpy(pWrite, pCurrent->data.aData, uSize); + pWrite += uSize; + + // removed space used from space left + uSpaceLeftInPkt -= uSize; + + // increment the entry count in the packet + uEntryCount += 1; + + // flag that packet contains at least one DATA protocol component entry + *pFlags |= VOIP_PACKET_RELIABLE_FLAG_DATA; + + #if VOIP_RELIABLE_DEBUG + { + int32_t uClientId = _VoipDecodeU32(&pCurrent->data.info.aRemoteClientId[0]); + NetPrintf(("voipconnection: [%d] reliable DATA (target: 0x%08x, seq# %03d, %s, %d bytes) added to outbound %s packet\n", iConnID, uClientId, pCurrent->data.info.uSeq, + _strReliableType[pCurrent->data.info.uType], uSize, (bPing?"PNG":"MIC"))); + } + #endif + + // move to next pending entry + pCurrent = pCurrent->pNext; + } + else + { + NetPrintf(("voipconnection: [%d] warning - failed to fit all DATA entries in outbound %s packet (last entry size = %d)\n", + iConnID, (bPing?"PNG":"MIC"), uSize)); + pCurrent = NULL; // no space left in packet, force while loop to exit + } + } + + if (uEntryCount == 0) + { + // reposition pWrite to its initial value + pWrite = pEntryCount; + } + else + { + // write the entry count in the first byte that the input pWrite was pointing to + *pEntryCount = uEntryCount; + } + + return(pWrite); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataOutProcess + + \Description + Outbound processing of reliablity data. To be invoked every time a + ping packet or a micr packet is sent. Reliability protocol components + (ACK, ACKCNF, DATA) are going to be added to the packet if necessary. + + \Input *pConnectionlist - connectionlist ref + \Input iConnID - connection id; VOIP_CONNID_ALL if packet is being sent to voipserver + \Input *pPacketHeader - pointer to header of the outbound packet + \Input *pPacketPayload - pointer to first payload byte of outbound packet + \Input iPayloadCapacity - amount of bytes we can fit in the payload + + \Output + uint8_t * - pointer to first byte after reliable data in packet payload + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t * _VoipReliableDataOutProcess(VoipConnectionlistT *pConnectionlist, int32_t iConnID, VoipPacketHeadT *pPacketHeader, uint8_t *pPacketPayload, int32_t iPayloadCapacity) +{ + int32_t iSpaceLeftInPkt = iPayloadCapacity; + uint32_t bPing = ((pPacketHeader->aType[0] == 'P' && pPacketHeader->aType[1] == 'N' && pPacketHeader->aType[2] == 'G') ? TRUE : FALSE); + uint8_t *pWrite; + + // add ACK and ACKCNF to packet as necessary + pWrite = _VoipReliableDataPackACKandACKCNF(pConnectionlist, iConnID, &pPacketHeader->uFlags, bPing, pPacketPayload, iSpaceLeftInPkt); + iSpaceLeftInPkt -= (pWrite - pPacketPayload); + + if (iConnID == VOIP_CONNID_ALL) + { + int32_t iConnectionIndex; + int32_t iLargestTickDiff = 0; + + /* + When voip traffic flows through the voip server, we need to pick a local voip connection + to pack the outbound reliable data entries from. Each connection have its own outbound + reliable queue. They are all populated with same entries tagged with same sequence numbers. + But the acking progress on each queue may progress differently. For instance, queue X may + have entry seq nb 10 acked, but queue Y may still be waiting for entry seq nb 8 to be acked. + + The best we can do is pick the connection that has the oldest pending reliable data. Other + connections will benefit of this too if there are still waiting for ack of that entry, or + they will rely on the next ping packet to go out. + */ + + for (iConnectionIndex = 0; iConnectionIndex < pConnectionlist->iMaxConnections; iConnectionIndex++) + { + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex]; + + if ((pConnection->eState == ST_ACTV) && (pConnection->pOutboundReliableDataQueue != NULL)) + { + int32_t iTickDiff = NetTickDiff(NetTick(), pConnection->pOutboundReliableDataQueue->uEnqueueTick); + + if (iLargestTickDiff < iTickDiff) + { + iLargestTickDiff = iTickDiff; + iConnID = iConnectionIndex; + } + } + } + // if above for loop did not match on any connection, fallback to connection index 0 + if (iConnID == VOIP_CONNID_ALL) + { + iConnID = 0; + } + } + + pWrite = _VoipReliableDataPackDATA(pConnectionlist, iConnID, &pPacketHeader->uFlags, bPing, pWrite, iSpaceLeftInPkt); + + return(pWrite); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataInboundUserAddProcess + + \Description + Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_USERADD. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection on which the packet was received + \Input *pReliableData - pointer to inbound reliable data entry + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipReliableDataInboundUserAddProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData) +{ + uint8_t uUserIndex = pReliableData->aData[0]; // first byte is user index + VoipUserT *pRemoteUser = (VoipUserT *)(&pReliableData->aData[0] + 1); + + if (VOIP_NullUser((VoipUserT *)&pConnection->RemoteUsers[uUserIndex])) + { + int32_t iConnID = pConnection - &pConnectionlist->pConnections[0]; + + // update collection of remote users for this connection + VOIP_CopyUser(&pConnection->RemoteUsers[uUserIndex], pRemoteUser); + + // register the user + pConnectionlist->pRegUserCb((VoipUserT *)&pConnection->RemoteUsers[uUserIndex], (uint32_t)iConnID, TRUE, pConnectionlist->pRegUserUserData); + + // force re-application of channel config since we have a new remote player + ((VoipCommonRefT *)VoipGetRef())->bApplyChannelConfig = TRUE; + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + // reapply player-to-player xone comm relationships + pConnectionlist->bApplyRelFromMuting = TRUE; +#endif + // set mute list to be updated + pConnectionlist->bUpdateMute = TRUE; + + #if DIRTYCODE_LOGGING + _VoipConnectionPrintRemoteUsers(pConnection, (pConnection - &pConnectionlist->pConnections[0])); + #endif + } + else + { + NetPrintf(("voipconnection: [%d] join-in-progress for remote user %lld failed because index %d already used by %lld\n", (pConnection - &pConnectionlist->pConnections[0]), + ((VoipUserT *)pRemoteUser)->AccountInfo.iPersonaId, uUserIndex, ((VoipUserT *)&pConnection->RemoteUsers[uUserIndex])->AccountInfo.iPersonaId)); + } +} + + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataInboundUserRemProcess + + \Description + Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_USERREM. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection on which the packet was received + \Input *pReliableData - pointer to inbound reliable data entry + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipReliableDataInboundUserRemProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData) +{ + uint8_t uUserIndex = pReliableData->aData[0]; // first byte is user index + VoipUserT *pRemoteUser = (VoipUserT *)(&pReliableData->aData[0] + 1); + + if (!VOIP_NullUser((VoipUserT *)&pConnection->RemoteUsers[uUserIndex])) + { + if (memcmp(pRemoteUser, &pConnection->RemoteUsers[uUserIndex], sizeof(*pRemoteUser)) == 0) + { + int32_t iConnID = pConnection - &pConnectionlist->pConnections[0]; + + // unregister the user + pConnectionlist->pRegUserCb((VoipUserT *)&pConnection->RemoteUsers[uUserIndex], (uint32_t)iConnID, FALSE, pConnectionlist->pRegUserUserData); + + // force re-application of channel config since we lost a remote player + ((VoipCommonRefT *)VoipGetRef())->bApplyChannelConfig = TRUE; + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + // reapply player-to-player xone comm relationships + pConnectionlist->bApplyRelFromMuting = TRUE; +#endif + + // set mute list to be updated + pConnectionlist->bUpdateMute = TRUE; + + // clear the user + VOIP_ClearUser((VoipUserT *)&pConnection->RemoteUsers[uUserIndex]); + + #if DIRTYCODE_LOGGING + _VoipConnectionPrintRemoteUsers(pConnection, (pConnection - &pConnectionlist->pConnections[0])); + #endif + } + else + { + NetPrintf(("voipconnection: [%d] leave-in-progress for remote user %lld failed because remote user at index %d rather is %lld\n", (pConnection - &pConnectionlist->pConnections[0]), + ((VoipUserT *)pRemoteUser)->AccountInfo.iPersonaId, uUserIndex, ((VoipUserT *)&pConnection->RemoteUsers[uUserIndex])->AccountInfo.iPersonaId)); + } + } + else + { + NetPrintf(("voipconnection: [%d] leave-in-progress for remote user %lld failed because no remote user at index %d\n", (pConnection - &pConnectionlist->pConnections[0]), + ((VoipUserT *)pRemoteUser)->AccountInfo.iPersonaId, uUserIndex)); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataInboundOpaqueProcess + + \Description + Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_OPAQUE. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection on which the packet was received + \Input *pReliableData - pointer to inbound reliable data entry + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipReliableDataInboundOpaqueProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData) +{ + uint16_t uDataSize = _VoipDecodeU16(&pReliableData->info.uSize[0]); + int32_t iConnID = pConnection - &pConnectionlist->pConnections[0]; + pConnectionlist->pOpaqueCb(iConnID, &pReliableData->aData[0], uDataSize, pConnectionlist->pOpaqueUserData); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataInboundTextProcess + + \Description + Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_TEXT. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection on which the packet was received + \Input *pReliableData - pointer to inbound reliable data entry + + \Version 05/02/2017 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipReliableDataInboundTextProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData) +{ + uint8_t uUserIndex = pReliableData->aData[0]; // first byte is user index + uint16_t uTextSize = _VoipDecodeU16(&pReliableData->info.uSize[0]) - 1; // text size is payload size minus the byte used for the user index + + if (uTextSize > 0) + { + if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist)) + { + int32_t iConnID = pConnection - &pConnectionlist->pConnections[0]; + + if (pConnectionlist->uRecvMask & (1 << iConnID)) + { + pConnectionlist->pTextCb(iConnID, uUserIndex, (char *)&pReliableData->aData[1], (void *)pConnectionlist->pTextUserData); + } + else + { + NetPrintf(("voipconnection: inbound transcribed text dropped because connection (%d) is muted\n", iConnID)); + } + } + else + { + NetPrintf(("voipconnection: inbound transcribed text dropped because none of the local players requested for it (normal when voip is routed through DirytCast voipserver)\n")); + } + } + else + { + NetPrintf(("voipconnection: inbound transcribed text dropped because decoding resulted in an empty string\n")); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataUnpackACKandACKCNF + + \Description + Inbound processing of reliablity data - deals with ACK+ACKCNF protocol + components specifically. + + \Input *pConnectionlist - connectionlist ref + \Input uSrcClientId - client id of originator + \Input uFlags - uFlags from the inbound packet + \Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet + \Input *pRead - pointer to next byte to be read in packet payload + + \Output + uint8_t * - pointer to first byte after ACK + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t * _VoipReliableDataUnpackACKandACKCNF(VoipConnectionlistT *pConnectionlist, uint32_t uSrcClientId, uint8_t uFlags, uint32_t bPing, uint8_t *pRead) +{ + int32_t iConnectionIndex; + VoipConnectionT *pConnection = NULL; + uint32_t bPeerIsAckingUs = FALSE; + + // find connection matching the source client id + for (iConnectionIndex = 0; iConnectionIndex < pConnectionlist->iMaxConnections; iConnectionIndex++) + { + if (pConnectionlist->pConnections[iConnectionIndex].uRemoteClientId == uSrcClientId) + { + pConnection = &pConnectionlist->pConnections[iConnectionIndex]; + break; + } + } + + // is the packet buffer already filled with ACK+ACKCNF protocol components used for the reliability mechanism? + if (uFlags & VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF) + { + int32_t iReliableAckEntriesCount = *pRead++; + int32_t iReliableAckEntryIndex; + + for (iReliableAckEntryIndex = 0; iReliableAckEntryIndex < iReliableAckEntriesCount; iReliableAckEntryIndex++) + { + uint32_t uClientId; + ReliableAckT *pReliableAck = (ReliableAckT *)pRead; + + pRead += sizeof(ReliableAckT); + + if (pConnection != NULL) + { + // determine our local client id from the remote client's point of view + uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId; + + // extract client id from ACK+ACKCNF protocol component entry + uClientId = _VoipDecodeU32(&pReliableAck->aRemoteClientId[0]); + + // ignore the entry if it is not for us + if (uClientId == uLocalClientId) + { + uint8_t uAckedSeq; + + // deal with ACKCNF + pConnection->bPeerNeedsAck = ((pReliableAck->uAckedSeq & VOIP_RELIABLE_ACKCNF_MASK) ? TRUE : FALSE); + + // deal with ACK + uAckedSeq = pReliableAck->uAckedSeq & ~VOIP_RELIABLE_ACKCNF_MASK; + if (uAckedSeq != 0) + { + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# %03d, %s) found in inbound %s packet from clientId=0x%08x\n", + iConnectionIndex, uAckedSeq, (pConnection->bPeerNeedsAck ?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), uSrcClientId)); + #endif + bPeerIsAckingUs = TRUE; + + while ( (pConnection->pOutboundReliableDataQueue != NULL) && + (uAckedSeq >= pConnection->pOutboundReliableDataQueue->data.info.uSeq) ) + { + _VoipConnectionReliableDataDequeue(pConnectionlist, iConnectionIndex); + } + } + else + { + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# INV, %s) found in inbound %s packet from clientId=0x%08x\n", + iConnectionIndex, (pConnection->bPeerNeedsAck ?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), uSrcClientId)); + #endif + } + } + } + else + { + NetPrintf(("voipconnection: [%d] dropped inbound ACK+ACKCNF - failed to find connection matching originating clientId=0x%08x\n", iConnectionIndex, uSrcClientId)); + } + } + } + + if (pConnection != NULL) + { + pConnection->bPeerIsAcking = bPeerIsAckingUs; + } + + return(pRead); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataUnpackDATA + + \Description + Inbound processing of reliablity data - deals with DATA protocol component specifically. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection on which the packet was received + \Input uFlags - uFlags from the inbound packet + \Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet + \Input *pRead - pointer to next byte to be read in packet payload + + \Output + uint8_t * - pointer to first byte after DATA + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t * _VoipReliableDataUnpackDATA(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, uint8_t uFlags, uint32_t bPing, uint8_t *pRead) +{ + // is the packet buffer already filled with the ACK protocol component used for the reliability mechanism? + if (uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA) + { + int32_t iReliableDataEntriesCount = *pRead++; + int32_t iReliableDataEntryIndex; + + for (iReliableDataEntryIndex = 0; iReliableDataEntryIndex < iReliableDataEntriesCount; iReliableDataEntryIndex++) + { + ReliableDataT *pReliableData = (ReliableDataT *)pRead; + uint16_t uSize = _VoipDecodeU16(&pReliableData->info.uSize[0]); + uint32_t uClientId = _VoipDecodeU32(&pReliableData->info.aRemoteClientId[0]); + uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId; + + // move read pointer ahead + pRead += sizeof(pReliableData->info) + uSize; + + // make sure this is for us + if (uClientId == uLocalClientId) + { + if (pReliableData->info.uSeq == _VoipConnectionIncrementReliableSeqNb(pConnection->uInSeqNb)) + { + // update next expected inbound reliable seq nb + pConnection->uInSeqNb = pReliableData->info.uSeq; + + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] reliable DATA (seq# %03d, %s, %d bytes) found in inbound %s packet\n", (pConnection - &pConnectionlist->pConnections[0]), + pReliableData->info.uSeq, _strReliableType[pReliableData->info.uType], uSize, (bPing?"PNG":"MIC"))); + #endif + + if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_USERADD) + { + _VoipReliableDataInboundUserAddProcess(pConnectionlist, pConnection, pReliableData); + } + else if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_USERREM) + { + _VoipReliableDataInboundUserRemProcess(pConnectionlist, pConnection, pReliableData); + } + else if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_OPAQUE) + { + _VoipReliableDataInboundOpaqueProcess(pConnectionlist, pConnection, pReliableData); + } + else if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_TEXT) + { + _VoipReliableDataInboundTextProcess(pConnectionlist, pConnection, pReliableData); + } + else + { + NetPrintf(("voipconnection: [%d] unsupported inbound reliable data type (%d)\n", (pConnection - &pConnectionlist->pConnections[0]), pReliableData->info.uType)); + } + } + else + { + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] dropped inbound reliable data embedded in %s packet - seq nb mismatch (got: %d - expected: %d)\n", + (pConnection - &pConnectionlist->pConnections[0]), (bPing ? "PNG" : "MIC"), pReliableData->info.uSeq, _VoipConnectionIncrementReliableSeqNb(pConnection->uInSeqNb))); + #endif + } + } + else + { + #if VOIP_RELIABLE_DEBUG + NetPrintf(("voipconnection: [%d] dropped inbound reliable data embedded in %s packet - client id mismatch (target: 0x%08x - local: 0x%08x)\n", + (pConnection - &pConnectionlist->pConnections[0]), (bPing ? "PNG" : "MIC"), uClientId, uLocalClientId)); + #endif + } + } + } + + return(pRead); +} + +/*F********************************************************************************/ +/*! + \Function _VoipReliableDataInProcess + + \Description + Inbound processing of reliablity data. To be invoked every time a + ping packet or a micr packet is received. Reliability protocol components + (ACK, ACKCNF, DATA) included in the packet are going to be processed appropriately. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection on which the packet was received + \Input *pPacketHeader - pointer to header of the inbound packet + \Input *pPacketPayload - pointer to first payload byte of inbound packet + + \Output + uint8_t - first byte following reliable data in packet payload + + \Version 09/14/2014 (mclouatre) +*/ +/********************************************************************************F*/ +static uint8_t * _VoipReliableDataInProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, VoipPacketHeadT *pPacketHeader, uint8_t *pPacketPayload) +{ + // note: for voipserver-based topology, uSrcClientId may not be same as pConnection->uClientId + uint32_t uSrcClientId = _VoipDecodeU32(&pPacketHeader->aClientId[0]); + uint32_t bPing = ((pPacketHeader->aType[0] == 'P' && pPacketHeader->aType[1] == 'N' && pPacketHeader->aType[2] == 'G') ? TRUE : FALSE); + + uint8_t *pRead = _VoipReliableDataUnpackACKandACKCNF(pConnectionlist, uSrcClientId, pPacketHeader->uFlags, bPing, pPacketPayload); + + pRead = _VoipReliableDataUnpackDATA(pConnectionlist, pConnection, pPacketHeader->uFlags, bPing, pRead); + + return(pRead); +} + +/*F********************************************************************************/ +/*! + \Function _VoipUpdateUserFlags + + \Description + Update the flags field for each user just before serialization. + + \Input *pConnectionlist - connectionlist ref + + \Version 10/02/2019 (cvienneau) +*/ +/********************************************************************************F*/ +static void _VoipUpdateUserFlags(VoipConnectionlistT *pConnectionlist) +{ + int32_t iUserIndex; + uint8_t bCrossPlay = VoipStatus(VoipGetRef(), 'xply', 0, NULL, 0); + + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + if (pConnectionlist->aIsParticipating[iUserIndex] == TRUE) + { + // is cross play setup for the local users + if (bCrossPlay) + { + pConnectionlist->LocalUsers[iUserIndex].uFlags |= VOIPUSER_FLAG_CROSSPLAY; + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionConn + + \Description + Send a connection packet to peer + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - pointer to connection to connect on + \Input uTick - current tick count + + \Version 03/20/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipConnectionConn(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, uint32_t uTick) +{ + VoipConnPacketT ConnPacket; + int32_t iUserIndex; + uint32_t uLocalClientId = 0; + uint32_t uSize = sizeof(ConnPacket) - sizeof(ConnPacket.LocalUsers) + (sizeof(*ConnPacket.LocalUsers) * VOIP_MAXLOCALUSERS_EXTENDED); + + // output diagnostic info + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) sending connect packet to remote console (%a:%d)\n", + VOIP_ConnID(pConnectionlist, pConnection), pConnection->uSessionId[0], + SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port))); + + // send a connection packet + ds_memclr(&ConnPacket, sizeof(VoipConnPacketT)); + ds_memcpy_s(&ConnPacket.Head, sizeof(ConnPacket.Head), &_Voip_ConnPacket, sizeof(_Voip_ConnPacket)); + _VoipEncodeU32(ConnPacket.aRemoteClientId, pConnection->uRemoteClientId); + _VoipEncodeU32(ConnPacket.Head.aSessionId, pConnection->uSessionId[0]); + _VoipEncodeLocalHeadsetStatus(pConnectionlist, &(ConnPacket.Head)); + if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist)) + { + ConnPacket.Head.uFlags |= VOIP_PACKET_STATUS_FLAG_STT; + } + _VoipUpdateUserFlags(pConnectionlist); + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + if (pConnectionlist->aIsParticipating[iUserIndex] != FALSE) + { + _VoipEncodeVoipUser(&ConnPacket.LocalUsers[iUserIndex], &pConnectionlist->LocalUsers[iUserIndex]); + } + } + + ConnPacket.bConnected = pConnection->bConnPktRecv; + + // extended local users always have an extra slot allocated and are not the same as maxlocalusers + ConnPacket.uNumLocalUsers = VOIP_MAXLOCALUSERS_EXTENDED; + + #if VOIP_CONNECTION_DEBUG + NetPrintMem(&ConnPacket, sizeof(ConnPacket), "connection packet data"); + #endif + uLocalClientId = pConnection->uLocalClientId == 0 ? pConnectionlist->uClientId : pConnection->uLocalClientId; + pConnection->iSendResult = _VoipSocketSendto(pConnectionlist->pSocket, uLocalClientId, &ConnPacket.Head, uSize, 0, (struct sockaddr *)&pConnection->SendAddr, sizeof(pConnection->SendAddr)); + + // update timestamp + pConnection->uLastSend = uTick; +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionDisc + + \Description + Send a disconnection request to peer. + + \Input iConnId - connection id + \Input uClientId - client id + \Input uRemoteClientId - remote client id + \Input uSessionId - session identifier + \Input *pSendAddr - address to send disconnect to + \Input *pSocket - socket to send request with + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipConnectionDisc(int32_t iConnId, uint32_t uClientId, uint32_t uRemoteClientId, uint32_t uSessionId, struct sockaddr_in *pSendAddr, SocketT *pSocket) +{ + VoipDiscPacketT DiscPacket; + + // send a disconnect packet + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) sending disconnect packet to %a:%d\n", + iConnId, uSessionId, SocketNtohl(pSendAddr->sin_addr.s_addr), SocketNtohs(pSendAddr->sin_port))); + + // set up disc packet + ds_memcpy_s(&DiscPacket.Head, sizeof(DiscPacket.Head), &_Voip_DiscPacket, sizeof(_Voip_DiscPacket)); + _VoipEncodeU32(DiscPacket.aRemoteClientId, uRemoteClientId); + _VoipEncodeU32(DiscPacket.Head.aSessionId, uSessionId); + + // send disc packet + _VoipSocketSendto(pSocket, uClientId, &DiscPacket.Head, sizeof(DiscPacket), 0, (struct sockaddr *)pSendAddr, sizeof(*pSendAddr)); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionInit + + \Description + Transition from connecting to connected. + + \Input *pConnectionlist - connectionlist connection is a part of + \Input *pConnection - connection to transition + \Input iConnID - index of connection + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipConnectionInit(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnID) +{ + int32_t iLocalUserIndex; + + NetPrintf(("voipconnection: [%d] connection established; talking to remote user(s)\n", iConnID)); + + #if DIRTYCODE_LOGGING + _VoipConnectionPrintRemoteUsers(pConnection, iConnID); + #endif + + // register the new remote users with voipheadset + VoipConnectionRegisterRemoteTalkers(pConnectionlist, iConnID, TRUE); + + // we want to call this before the connection goes to state ST_ACTV + // to avoid race conditions where the voipthread would start working with this conn before the channel config is updated + VoipCommonApplyChannelConfig((VoipCommonRefT *)VoipGetRef()); + + // update connection flags + pConnection->uRemoteConnStatus = VOIP_CONN_CONNECTED; + + // flag that this connection shall now be considered when updating friend status bitmask + if (pConnectionlist->iFriendConnId == VOIP_CONNID_NONE) + { + pConnectionlist->iFriendConnId = iConnID; + } + else + { + pConnectionlist->iFriendConnId = VOIP_CONNID_ALL; + } + + // initialize timer used for sending at a fixed 10hz rate + for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iLocalUserIndex++) + { + pConnection->aVoiceSendTimer[iLocalUserIndex] = NetTick(); + } + + /* + To avoid multi-threading race conditions, it is important to only move the connection state to ST_ACTV at the end of + the function (after initializing all connection vars and updating channel config) because some code exercised by the + _VoipThread (like _VoipUpdateRemoteStatus()) may start to use these fields concurrently as soon as the connection state + becomes ST_ACTV + */ + pConnection->eState = ST_ACTV; +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionPing + + \Description + Ping a connected remote peer. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - pointer to connection to send on + \Input uTick - current tick count + + \Version 03/20/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipConnectionPing(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, uint32_t uTick) +{ + VoipPingPacketT PingPacket; + int32_t iUserIndex; + uint32_t uChannelUserIndices = 0; + uint8_t *pWrite = PingPacket.aData; + const uint8_t *pEnd = pWrite+sizeof(PingPacket.aData); + uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId; + + #if VOIP_CONNECTION_DEBUG + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) pinging %a:%d\n", + VOIP_ConnID(pConnectionlist, pConnection), pConnection->uSessionId[0], + SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port))); + #endif + + // set up ping packet + ds_memcpy_s(&PingPacket.Head, sizeof(PingPacket.Head), &_Voip_PingPacket, sizeof(_Voip_PingPacket)); + _VoipEncodeLocalHeadsetStatus(pConnectionlist, &(PingPacket.Head)); + _VoipEncodeU32(PingPacket.aRemoteClientId, pConnection->uRemoteClientId); + _VoipEncodeU32(PingPacket.Head.aSessionId, pConnection->uSessionId[0]); + if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist)) + { + PingPacket.Head.uFlags |= VOIP_PACKET_STATUS_FLAG_STT; + } + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + /* write the channel information for the participating user indices and + set the bit so the receiving side knows which index it corresponds to */ + if (pConnectionlist->aIsParticipating[iUserIndex]) + { + uChannelUserIndices |= 1 << iUserIndex; + _VoipEncodeU32(pWrite, pConnectionlist->aChannels[iUserIndex]); + pWrite += 4; + } + } + _VoipEncodeU32(PingPacket.aChannelUserIndices, uChannelUserIndices); + + // if needed, add reliable protocol components to this ping packet + pWrite = _VoipReliableDataOutProcess(pConnectionlist, (pConnection - &pConnectionlist->pConnections[0]), &PingPacket.Head, pWrite, pEnd - pWrite); + + // send a 'ping' packet + pConnection->iSendResult = _VoipSocketSendto(pConnectionlist->pSocket, uLocalClientId, &PingPacket.Head, (pWrite - (uint8_t *)&PingPacket), 0, (struct sockaddr *)&pConnection->SendAddr, sizeof(pConnection->SendAddr)); + + // update send timestamp and local status flag + pConnection->uLastSend = uTick; + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; ++iUserIndex) + { + if (NetTickDiff(uTick, pConnectionlist->uLocalUserLastSend[iUserIndex]) > VOIP_PING_RATE) + { + pConnectionlist->uLocalUserStatus[iUserIndex] &= ~VOIP_LOCAL_USER_SENDVOICE; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionIsSendPktReady + + \Description + Determine if we should send the current packet. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection pointer + \Input iUserIndex - index of local user that generated the voice to be sent + \Input uTick - current tick count + + \Output + uint32_t - TRUE if ready, FALSE otherwise. + + \Version 10/01/2011 (mclouatre) +*/ +/********************************************************************************F*/ +static uint32_t _VoipConnectionIsSendPktReady(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iUserIndex, uint32_t uTick) +{ + VoipMicrPacketT *pVoipPkt = &pConnection->VoipMicrPacket[iUserIndex]; + uint8_t *pWrite = _VoipPacketGetWritePtr(pVoipPkt); + uint8_t bVariableSubPktLen = ((pVoipPkt->MicrInfo.uSubPacketSize == 0xFF) ? TRUE : FALSE); // check if sub-pkts in this packet are fixed length or variable length + int32_t iEstimatedResultingPacketPayloadSize; + + // if an additional sub-packet would cause us to overflow the sub-packet buffer, send packet + if (bVariableSubPktLen) + { + // is next packet going to overflow? 1 byte for prepended size + estimated subpkt size + iEstimatedResultingPacketPayloadSize = (pWrite + 1 + pConnection->iMaxSubPktSize) - pVoipPkt->aData; + } + else + { + iEstimatedResultingPacketPayloadSize = (pWrite + pVoipPkt->MicrInfo.uSubPacketSize) - pVoipPkt->aData; + } + + if (iEstimatedResultingPacketPayloadSize > (signed)sizeof(pVoipPkt->aData)) + { + return(TRUE); + } + + // if 10hz timer has elapsed, send packet + if ((pVoipPkt->MicrInfo.uNumSubPackets > 0) && (NetTickDiff(uTick, pConnection->aVoiceSendTimer[iUserIndex]) >= 0)) + { + return(TRUE); + } + + // not yet ready to send + return(FALSE); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionSendSingle + + \Description + Send a single buffered voice packet, if it is time to send it. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - pointer to connection to send on + \Input iUserIndex - index of the local user that generated the voice data to be sent + \Input uTick - current tick count + \Input bFlush - flush data + + \Version 03/20/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipConnectionSendSingle(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iUserIndex, uint32_t uTick, uint32_t bFlush) +{ + // ref current buffered packet + VoipMicrPacketT *pPacket = &pConnection->VoipMicrPacket[iUserIndex]; + uint32_t bSendPacket = FALSE; + int32_t iParticipatingUserIndex; + uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId; + + // should we send? + if (_VoipConnectionIsSendPktReady(pConnectionlist, pConnection, iUserIndex, uTick) || (bFlush == TRUE)) + { + bSendPacket = TRUE; + } + + // send packet? + if (bSendPacket == TRUE) + { + int32_t iPacketSize, iValidPayloadSize; + uint32_t uVoipServerReadySendMask = 0; + + // set packet sequence + pPacket->MicrInfo.uSeqn = pConnection->uSendSeqn++; + + // set the user index + if (iUserIndex == VOIP_SHARED_USER_INDEX) + { + pPacket->MicrInfo.uUserIndex = VOIP_SHARED_REMOTE_INDEX; + } + else + { + pPacket->MicrInfo.uUserIndex = iUserIndex; + } + + // set the channels + _VoipEncodeU32(pPacket->MicrInfo.channels.aChannelId, pConnectionlist->aChannels[iUserIndex]); + + // set the session id + _VoipEncodeU32(pPacket->Head.aSessionId, pConnection->uSessionId[0]); + + // flag whether one of the local users is requesting transcribed text + if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist)) + { + pPacket->Head.uFlags |= VOIP_PACKET_STATUS_FLAG_STT; + } + + // set send mask for VoipServer use + if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE) + { + uVoipServerReadySendMask = _VoipPrepareVoipServerSendMask(pConnectionlist); + } + _VoipEncodeU32(pPacket->MicrInfo.aSendMask, uVoipServerReadySendMask); + _VoipEncodeLocalHeadsetStatus(pConnectionlist, &pPacket->Head); + + #if VOIP_CONNECTION_DEBUG + // display verbose status (metered to about once every three seconds) + if ((pConnection->uSendSeqn % 30) == 0) + { + char strSubPktSize[32]; + sprintf(strSubPktSize, "size %d (bytes)", pPacket->MicrInfo.uSubPacketSize); + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) local usr %d sending pkt (seq#:%d) with %d sub-pkt(s) of %s to %a:%d | voip channels:0x%08x\n", + VOIP_ConnID(pConnectionlist, pConnection), pConnection->uSessionId[0], iUserIndex, pPacket->MicrInfo.uSeqn, pPacket->MicrInfo.uNumSubPackets, + (pPacket->MicrInfo.uSubPacketSize == 0xFF ? "variable-length" : strSubPktSize), SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port), + pConnectionlist->aChannels[iUserIndex])); + } + #endif + + // send voice packet + iValidPayloadSize = _VoipPacketGetWritePtr(&pConnection->VoipMicrPacket[iUserIndex]) - &pPacket->aData[0]; + iPacketSize = sizeof(pConnection->VoipMicrPacket[iUserIndex]) - sizeof(pPacket->aData) + iValidPayloadSize; + pConnection->iSendResult = _VoipSocketSendto(pConnectionlist->pSocket, uLocalClientId, &pPacket->Head, + iPacketSize, 0, (struct sockaddr *)&pConnection->SendAddr, sizeof(pConnection->SendAddr)); + + // update send info -- don't update uLastSend in server mode, to force pinging even when voice is being sent + if (pConnection->iVoipServerConnId == VOIP_CONNID_NONE) + { + pConnection->uLastSend = uTick; + } + + // if packet contains reliable data, don't update aVoiceSendTimer to make sure we retry sending voip sub-packets soon (in case reliable data took all the place in the packet payload) + if ((pPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA) == 0) + { + pConnection->aVoiceSendTimer[iUserIndex] = uTick + VOIP_MSPERPACKET; + } + + // if the voice packet is from a shared user set all the participating user's status + if (iUserIndex == VOIP_SHARED_USER_INDEX) + { + for (iParticipatingUserIndex = 0; iParticipatingUserIndex < VOIP_MAXLOCALUSERS; ++iParticipatingUserIndex) + { + if (pConnectionlist->aIsParticipating[iParticipatingUserIndex] == TRUE) + { + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + /* For xboxone, opaque traffic is always sent as if it was originated from the + "shared user". We need to query VoipHeadset (via VoipCommon) to find out which + local user is exactly detected as talking by the MS game chat 2's chat manager + and consequently involved in generating the current outbound flow of voip packets. */ + if (VoipCommonStatus((VoipCommonRefT *)VoipGetRef(), 'talk', iParticipatingUserIndex, NULL, 0)) + #endif + { + pConnectionlist->uLocalUserStatus[iParticipatingUserIndex] |= VOIP_LOCAL_USER_SENDVOICE; + pConnectionlist->uLocalUserLastSend[iParticipatingUserIndex] = uTick; + } + } + } + } + else + { + pConnectionlist->uLocalUserStatus[iUserIndex] |= VOIP_LOCAL_USER_SENDVOICE; + pConnectionlist->uLocalUserLastSend[iUserIndex] = uTick; + } + + // reset buffer used to build voip micr packet + ds_memclr(&pPacket->MicrInfo, sizeof(pPacket->MicrInfo)); // clear micr packet info + pPacket->Head.uFlags = 0; // clear flags in packet header + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionRecvSingle + + \Description + Handle data incoming on a connection. + + \Input *pConnectionlist - connectionlist connection belongs to + \Input *pConnection - pointer to connection to receive on + \Input *pSendAddr - remote address data was received from + \Input *pPacket - packet data that was received + \Input iSize - size of data received + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipConnectionRecvSingle(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, struct sockaddr_in *pSendAddr, VoipPacketBufferT *pPacket, int32_t iSize) +{ + int32_t iConnID = VOIP_ConnID(pConnectionlist, pConnection); + uint32_t uSessionId = _VoipDecodeU32(pPacket->VoipPacketHead.aSessionId); + #if DIRTYCODE_LOGGING + uint32_t uClientId = _VoipDecodeU32(pPacket->VoipPacketHead.aClientId); + const char *pStrPktType; + #endif + uint32_t uTick; + uint8_t uPktType; + uint32_t uIndex; + + // note time of arrival + uTick = NetTick(); + + // count the packet + pConnection->iRecvPackets += 1; + pConnection->iRecvData += iSize; + + // did port change? + if (pConnection->SendAddr.sin_port != pSendAddr->sin_port) + { + NetPrintf(("voipconnection: [%d] port remap #%d: %d bytes from %d instead of %d\n", iConnID, + pConnection->iChangePort++, iSize, + SocketNtohs(pSendAddr->sin_port), + SocketNtohs(pConnection->SendAddr.sin_port))); + + // send future to remapped port + pConnection->SendAddr.sin_port = pSendAddr->sin_port; + + // reset counter of packets received + pConnection->iRecvPackets = 0; + } + + if (pPacket->VoipPacketHead.uFlags & VOIP_PACKET_STATUS_FLAG_STT) + { + #if DIRTYCODE_LOGGING + if (pConnection->bTranscribedTextRequested == FALSE) + { + NetPrintf(("voipconnection: [%d] one or several of the remote peers on that connection are requesting transcribed text\n", iConnID)); + } + #endif + pConnection->bTranscribedTextRequested = TRUE; + } + else + { + #if DIRTYCODE_LOGGING + if (pConnection->bTranscribedTextRequested != FALSE) + { + NetPrintf(("voipconnection: [%d] remote peer(s) are no longer requesting transcribed text\n", iConnID)); + } + #endif + pConnection->bTranscribedTextRequested = FALSE; + } + + // identify type of incoming packet (connect, disconnect, ping, mic or unknown) + if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_ConnPacket)) + { + uPktType = 'C'; // connection packet + #if DIRTYCODE_LOGGING + pStrPktType = "connect"; + #endif + } + else if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_PingPacket)) + { + uPktType = 'P'; // ping packet + #if DIRTYCODE_LOGGING + pStrPktType = "ping"; + #endif + } + else if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_MicrPacket)) + { + uPktType = 'M'; // data packet + #if DIRTYCODE_LOGGING + pStrPktType = "data"; + #endif + } + else if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_DiscPacket)) + { + uPktType = 'D'; // disconnect packet + #if DIRTYCODE_LOGGING + pStrPktType = "disconnect"; + #endif + } + else + { + NetPrintf(("voipconnection: [%d] received unknown packet type 0x%02x%02x%02x\n", iConnID, + pPacket->VoipPacketHead.aType[0], pPacket->VoipPacketHead.aType[1], pPacket->VoipPacketHead.aType[2])); + return; + } + + // ignore packet if session id does not match + if (_VoipConnectionMatchSessionId(pConnectionlist, iConnID, uSessionId) == FALSE) + { + #if DIRTYCODE_LOGGING + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) ignoring packet because of session IDs mismatch\n", + iConnID, uSessionId, pStrPktType)); + + // print set of sessions sharing this connection + _VoipConnectionPrintSessionIds(pConnectionlist, iConnID); + + NetPrintMem(pPacket, iSize, "packet data"); + #endif + + return; + } + + // handle incoming packet + switch (uPktType) + { + // connect packet + case 'C': + { + VoipConnPacketT *pConnPacket = &pPacket->VoipConnPacket; + int32_t iRemoteUserIndex; + + // got a connection packet + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) received conn packet bConnected=%d eState=%d uClientId=0x%08x\n", + iConnID, uSessionId, pConnPacket->bConnected, pConnection->eState, uClientId)); + + // copy remote user's info + pConnection->iMaxRemoteUsers = pConnPacket->uNumLocalUsers - 1; + pConnection->iMaxRemoteUsersExt = pConnection->iMaxRemoteUsers; + + for (iRemoteUserIndex = 0; iRemoteUserIndex < pConnection->iMaxRemoteUsersExt; iRemoteUserIndex++) + { + _VoipDecodeVoipUser(&pConnPacket->LocalUsers[iRemoteUserIndex], &pConnection->RemoteUsers[iRemoteUserIndex]); + } + + _VoipDecodeRemoteHeadsetStatus(pConnection, &pConnPacket->Head); + + // flag that we received Conn packet from remote party + pConnection->bConnPktRecv = TRUE; + + // if they've received data from us, consider the connection established + if ((pConnPacket->bConnected) && (pConnection->eState == ST_CONN)) + { + _VoipConnectionInit(pConnectionlist, pConnection, iConnID); + } + + // if peer no more sees us as connected... go back to ST_CONN state and hope for connection to go back up + if ((!pConnPacket->bConnected) && (pConnection->eState == ST_ACTV)) + { + NetPrintf(("voipconnection: [%d] voip connection lost! try to re-establish. connection back to ST_CONN state (from ST_ACTV)... \n", iConnID)); + pConnection->eState = ST_CONN; + } + + // update last received time + pConnection->uLastRecv = uTick; + } + break; + + // ping packet + case 'P': + { + VoipPingPacketT *pPingPacket = &pPacket->VoipPingPacket; + int32_t iUserIndex; + uint32_t uChannelUserIndices; + const uint8_t *pRead = pPingPacket->aData; + + #if VOIP_CONNECTION_DEBUG + char strGlobalVoipChannelConfig[80]; + char strUserVoipChannelConfig[20]; + ds_memclr(strGlobalVoipChannelConfig, sizeof(strGlobalVoipChannelConfig)); + ds_memclr(strUserVoipChannelConfig, sizeof(strUserVoipChannelConfig)); + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) received ping packet from clientId=0x%08x\n", iConnID, uSessionId, uClientId)); + #endif + + if (pConnection->bConnPktRecv) + { + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + // if we receive a ping in connection state, that means we're connected + if (pConnection->eState == ST_CONN) + { + _VoipConnectionInit(pConnectionlist, pConnection, iConnID); + } + + // update connection status flags + _VoipDecodeRemoteHeadsetStatus(pConnection, &pPingPacket->Head); + + // check to see if VOIP_REMOTE_USER_RECVVOICE flag should be deactivated for other users + for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; ++uIndex) + { + if (NetTickDiff(uTick, pConnection->uUserLastRecv[uIndex]) > VOIP_PING_RATE) + { + pConnection->uRemoteUserStatus[uIndex] &= ~VOIP_REMOTE_USER_RECVVOICE; + } + } + + pConnection->uRemoteConnStatus |= VOIP_CONN_BROADCONN; + pConnection->uLastRecv = uTick; + + // read the bitset to figure which channel configurations need to be updated + uChannelUserIndices = _VoipDecodeU32(pPingPacket->aChannelUserIndices); + + // update channel config for each remote user and remote shared user + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + if ((uChannelUserIndices >> iUserIndex) & 1) + { + pConnection->aRecvChannels[iUserIndex] = _VoipDecodeU32(pRead); + pRead += 4; + #if VOIP_CONNECTION_DEBUG + ds_snzprintf(strUserVoipChannelConfig, sizeof(strUserVoipChannelConfig), "user#%d:0x%08x ", iUserIndex, pConnection->aRecvChannels[iUserIndex]); + ds_strnzcat(strGlobalVoipChannelConfig, strUserVoipChannelConfig, sizeof(strGlobalVoipChannelConfig)); + #endif + } + } + #if VOIP_CONNECTION_DEBUG + NetPrintf((" voip channels: %s\n", strGlobalVoipChannelConfig)); + #endif + + // process any reliable protocol components that may be included in this ping packet + _VoipReliableDataInProcess(pConnectionlist, pConnection, &pPingPacket->Head, (uint8_t *)pRead); + + // update the channel + if (memcmp(&pConnection->aRecvChannels, &pConnection->aLastRecvChannels, sizeof(pConnection->aRecvChannels))) + { + VoipCommonProcessChannelChange(pVoipCommon, iConnID); + ds_memcpy_s(&pConnection->aLastRecvChannels, sizeof(pConnection->aLastRecvChannels), &pConnection->aRecvChannels, sizeof(pConnection->aRecvChannels)); + VoipCommonUpdateRemoteStatus(pVoipCommon); + } + } + else + { + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) ping packet from clientId=0x%08x ignored because conn packet not yet received\n", + iConnID, uSessionId, uClientId)); + } + } + break; + + // data packet + case 'M': + { + VoipMicrPacketT *pMicrPacket = &pPacket->VoipMicrPacket; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + #if VOIP_CONNECTION_DEBUG + if ((pConnection->iRecvPackets % 30) == 0) + { + char strSubPktSize[32]; + sprintf(strSubPktSize, "size %d (bytes)", pMicrPacket->MicrInfo.uSubPacketSize); + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) received pkt (seq#:%d) with %d sub-pkt(s) of %s from remote usr idx %d | voip channels:0x%08x\n", + iConnID, uSessionId, pMicrPacket->MicrInfo.uSeqn, pMicrPacket->MicrInfo.uNumSubPackets, (pMicrPacket->MicrInfo.uSubPacketSize == 0xFF ? "variable-length" : strSubPktSize), + pMicrPacket->MicrInfo.uUserIndex, _VoipDecodeU32(pMicrPacket->MicrInfo.channels.aChannelId))); + NetPrintMem(pMicrPacket, iSize, "mic packet data"); + } + #endif + + if (pConnection->bConnPktRecv) + { + uint8_t *pRead; + int32_t iRemoteUserIndex; + uint32_t uLocalHeadsetStatusAggregate = 0; + + // if we receive voice data in connection state, that means we're connected + if (pConnection->eState == ST_CONN) + { + _VoipConnectionInit(pConnectionlist, pConnection, iConnID); + } + + // update connection status flags + _VoipDecodeRemoteHeadsetStatus(pConnection, &pMicrPacket->Head); + pConnection->uRemoteConnStatus |= (VOIP_CONN_ACTIVE|VOIP_CONN_BROADCONN); + if (pMicrPacket->MicrInfo.uUserIndex == VOIP_SHARED_REMOTE_INDEX) + { + pConnection->aRecvChannels[VOIP_SHARED_USER_INDEX] = _VoipDecodeU32(pMicrPacket->MicrInfo.channels.aChannelId); + } + else + { + pConnection->aRecvChannels[pMicrPacket->MicrInfo.uUserIndex] = _VoipDecodeU32(pMicrPacket->MicrInfo.channels.aChannelId); + } + + // update the channel + if (memcmp(&pConnection->aRecvChannels, &pConnection->aLastRecvChannels, sizeof(pConnection->aRecvChannels))) + { + VoipCommonProcessChannelChange(pVoipCommon, iConnID); + ds_memcpy_s(&pConnection->aLastRecvChannels, sizeof(pConnection->aLastRecvChannels), &pConnection->aRecvChannels, sizeof(pConnection->aRecvChannels)); + VoipCommonUpdateRemoteStatus(pVoipCommon); + } + + pConnection->uLastRecv = uTick; + + // validate and update packet sequence number + if (pMicrPacket->MicrInfo.uSeqn != (uint8_t)(pConnection->uRecvSeqn+1)) + { + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) got packet seq # %d while expecting packet seq # %d\n", + iConnID, uSessionId, pMicrPacket->MicrInfo.uSeqn, (uint8_t)pConnection->uRecvSeqn+1)); + } + pConnection->uRecvSeqn = pMicrPacket->MicrInfo.uSeqn; + + // process any reliable protocol components that may be included in this MIC packet + pRead = _VoipReliableDataInProcess(pConnectionlist, pConnection, &pMicrPacket->Head, &pMicrPacket->aData[0]); + + /* is the connection active, and are we sending to it? + xboxone: After rebasing VoipHeadsetXboxOne to MS game chat 2, per-connection muting + is handled by VoipHeadsetXboxOne (See usage of +snd, -snd, +rcv, -rcv). + But for crossplay we want the muting to be applied here just like other plaforms. + Testing has shown that apply te send mask and +snd, -snd, +rcv, -rcv has no negative effect.*/ + if (pConnectionlist->uRecvMask & (1 << iConnID)) + { + // if the voice packet is from a remote shared user update all the remote user's status + if (pMicrPacket->MicrInfo.uUserIndex == VOIP_SHARED_REMOTE_INDEX) + { + for (iRemoteUserIndex = 0; iRemoteUserIndex < pConnection->iMaxRemoteUsers; ++iRemoteUserIndex) + { + VoipUserT *pVoipUser = (VoipUserT *)&pConnection->RemoteUsers[iRemoteUserIndex]; + + if (!VOIP_NullUser(pVoipUser)) + { + #if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + /* For xboxone, opaque traffic is always received as if it was originated from the + "shared user". We need to query VoipHeadset (via VoipCommon) to find out which + remote user is exactly detected as talking by the MS game chat 2's chat manager + and consequently involved in generating the current inbound flow of voip packets. */ + if (VoipCommonStatus((VoipCommonRefT *)VoipGetRef(), 'talk', 0, pVoipUser, sizeof(*pVoipUser))) + #endif + { + pConnection->uRemoteUserStatus[iRemoteUserIndex] |= VOIP_REMOTE_USER_RECVVOICE; + pConnection->uUserLastRecv[iRemoteUserIndex] = uTick; + } + } + } + } + else + { + pConnection->uUserLastRecv[pMicrPacket->MicrInfo.uUserIndex] = uTick; + pConnection->uRemoteUserStatus[pMicrPacket->MicrInfo.uUserIndex] |= VOIP_REMOTE_USER_RECVVOICE; + } + + // if there is at least one headset capable of outputting voice data, forward the packet along, ignore otherwise + // we do this to ignore spamming on PC (ticket GOS-30000) + for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; ++uIndex) + { + uLocalHeadsetStatusAggregate |= pVoipCommon->Connectionlist.uLocalUserStatus[uIndex]; + } + + if (uLocalHeadsetStatusAggregate & VOIP_LOCAL_USER_OUTPUTDEVICEOK) + { + pConnectionlist->pVoiceCb((VoipUserT *)&pConnection->RemoteUsers[0], pConnection->iMaxRemoteUsers, iConnID, &pMicrPacket->MicrInfo, pRead, pConnectionlist->pVoiceUserData); + } + } + + // check to see if VOIP_REMOTE_USER_RECVVOICE flag should be deactivated for other users + for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; ++uIndex) + { + if (NetTickDiff(uTick, pConnection->uUserLastRecv[uIndex]) > VOIP_PING_RATE) + { + pConnection->uRemoteUserStatus[uIndex] &= ~VOIP_REMOTE_USER_RECVVOICE; + } + } + } + else + { + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) voip data packet from clientId=0x%08x ignored because conn packet not yet received\n", + iConnID, uSessionId, uClientId)); + } + } + break; + + case 'D': + { + VoipDiscPacketT *pDiscPacket = &pPacket->VoipDiscPacket; + uint32_t uRemoteClientId = _VoipDecodeU32(pDiscPacket->aRemoteClientId); + uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId; + if (uRemoteClientId == uLocalClientId) + { + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) remote peer 0x%08x has disconnected from 0x%08x\n", + iConnID, uSessionId, uClientId, uRemoteClientId)); + + // In this specific case, we know that remote peer is already in 'disconnected' state. + // No need to send back a DISC msg. Therefore, set bSendDiscMsg parameter to FALSE. + _VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnID], iConnID, FALSE); + } + else + { + // this should only occurs with multiple machines on the same PC and voip being routed to the incorrect client + NetPrintf(("voipconnection: [%d] (sess id 0x%08x) remote peer 0x%08x has disconnected from 0x%08x, but we are 0x%08x\n", + iConnID, uSessionId, uClientId, uRemoteClientId, pConnectionlist->uClientId)); + } + } + break; + + default: + { + NetPrintf(("voipconnection: [%d] critical error! this path can't be taken in _VoipConnectionRecvSing()\n")); + } + break; + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionUpdateSingle + + \Description + Update a virtual connection. + + \Input *pConnectionlist - connectionlist + \Input *pConnection - pointer to connection to update + \Input iConnId - index of connection to update + \Input uTick - current tick count + + \Output + uint32_t - nonzero=receiving voice, zero=not receiving voice + + \Version 03/20/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _VoipConnectionUpdateSingle(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnId, uint32_t uTick) +{ + int32_t iTimeout; + int32_t iUserIndex; + uint8_t bIsReceivingVoice = FALSE; + + // make sure it's a valid connection + if (pConnection->eState == ST_DISC) + { + return(0); + } + + // see if we need to send a conn or ping packet + if ((uTick - pConnection->uLastSend) > VOIP_PING_RATE) + { + // if we're connecting, send connection packet + if (pConnection->eState == ST_CONN) + { + _VoipConnectionConn(pConnectionlist, pConnection, uTick); + } + else + { + _VoipConnectionPing(pConnectionlist, pConnection, uTick); + } + } + + // see if we need to send any buffered voice data + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + _VoipConnectionSendSingle(pConnectionlist, pConnection, iUserIndex, uTick, FALSE); + } + + // see if we need to time out the connection + iTimeout = (pConnection->eState == ST_ACTV) ? pConnectionlist->iDataTimeout : VOIP_CONNTIMEOUT; + if (NetTickDiff(uTick, pConnection->uLastRecv) > iTimeout) + { + NetPrintf(("voipconnection: [%d] timing out connection due to inactivity\n", iConnId)); + _VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnId], iConnId, TRUE); + } + + // return if we are receiving voice or not + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; iUserIndex++) + { + if (pConnection->uRemoteUserStatus[iUserIndex] & VOIP_REMOTE_USER_RECVVOICE) + { + bIsReceivingVoice = TRUE; + break; + } + } + return((bIsReceivingVoice == TRUE) ? (1 << iConnId) : 0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionRecv + + \Description + Receive data and forward it to the appropriate connection. + + \Input *pConnectionlist - connectionlist + + \Output + int32_t - amount of data received + + \Version 03/21/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipConnectionRecv(VoipConnectionlistT *pConnectionlist) +{ + struct sockaddr_in RecvAddr; + int32_t iAddrLen, iRecvSize; + + VoipPacketBufferT RecvPacket; + + // no socket? no way to receive. + if (pConnectionlist->pSocket == NULL) + { + return(0); + } + + // try and receive from a peer + if ((iRecvSize = SocketRecvfrom(pConnectionlist->pSocket, (char *)&RecvPacket, sizeof(RecvPacket), 0, (struct sockaddr *)&RecvAddr, (iAddrLen=sizeof(RecvAddr),&iAddrLen))) > 0) + { + VoipConnectionT *pConnection; + int32_t iConnID; + + // extract client identifier + uint32_t uClientId = _VoipDecodeU32(RecvPacket.VoipPacketHead.aClientId); + + // find the matching connection + for (iConnID = 0, pConnection = NULL; iConnID < pConnectionlist->iMaxConnections; iConnID++) + { + pConnection = &pConnectionlist->pConnections[iConnID]; + + // if the clientID matches, this is the connection + if (uClientId == pConnection->uRemoteClientId) + { + break; + } + } + + // if we found a matching connection + if (iConnID < pConnectionlist->iMaxConnections) + { + // give the data to the connection + _VoipConnectionRecvSingle(pConnectionlist, pConnection, &RecvAddr, &RecvPacket, iRecvSize); + } + else if (!VOIP_SamePacketType(&RecvPacket.VoipPacketHead, &_Voip_ConnPacket) && !VOIP_SamePacketType(&RecvPacket.VoipPacketHead, &_Voip_DiscPacket)) + { + NetPrintf(("voipconnection: ignoring 0x%02x%02x%02x packet from address %a:%d clientId=0x%08x\n", + RecvPacket.VoipPacketHead.aType[0], RecvPacket.VoipPacketHead.aType[1], RecvPacket.VoipPacketHead.aType[2], + SocketNtohl(RecvAddr.sin_addr.s_addr), SocketNtohs(RecvAddr.sin_port), + uClientId)); + NetPrintMem(&RecvPacket, iRecvSize, "packet data"); + } + } + + // return data length + return(iRecvSize); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionRecvCallback + + \Description + Voip socket recv event callback. + + \Input *pSocket - voip socket + \Input iFlags - unused + \Input *pRef - connectionlist ref + + \Output + int32_t - zero + + \Version 12/16/2005 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipConnectionRecvCallback(SocketT *pSocket, int32_t iFlags, void *pRef) +{ + VoipConnectionlistT *pConnectionlist = (VoipConnectionlistT *)pRef; + + // see if we have exclusive access + if (NetCritTry(&pConnectionlist->NetCrit)) + { + // try and receive data + _VoipConnectionRecv(pConnectionlist); + + // free access + NetCritLeave(&pConnectionlist->NetCrit); + } + else + { + NetPrintf(("voipconnection: _VoipConnectionRecvCallback() could not acquire connectionlist critical section\n")); + } + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionTrySocketClose + + \Description + Check if the voip socket can be closed. The close will occur when all the + connections are closed. Moved from the voip loop, to also be done immediately + upon connection stop. + + \Input *pConnectionlist - connectionlist ref + + \Output + int32_t - zero + + \Version 06/18/2009 (jrainy) +*/ +/********************************************************************************F*/ +static void _VoipConnectionTrySocketClose(VoipConnectionlistT *pConnectionlist) +{ + int32_t iConnId, iNumConnections; + + NetCritEnter(&pConnectionlist->NetCrit); + + // count number of active connections + for (iConnId = 0, iNumConnections = 0; iConnId < pConnectionlist->iMaxConnections; iConnId++) + { + if (pConnectionlist->pConnections[iConnId].eState != ST_DISC) + { + iNumConnections++; + } + } + + // if no connections left, kill socket + if ((iNumConnections == 0) && (pConnectionlist->pSocket != NULL)) + { + NetPrintf(("voipconnection: closing socket in _VoipConnectionTrySocketClose()\n")); + SocketClose(pConnectionlist->pSocket); + pConnectionlist->pSocket = NULL; + pConnectionlist->uBindPort = 0; + } + NetCritLeave(&pConnectionlist->NetCrit); +} + +/*F********************************************************************************/ +/*! + \Function _VoipConnectionStop + + \Description + Stop a connection. + + \Input *pConnectionlist - connectionlist ref + \Input *pConnection - connection to stop + \Input iConnID - index of connection to stop, or VOIP_CONNID_ALL to stop all connections + \Input bSendDiscMsg - TRUE - send DISC msg to peer; FALSE - do not send DISC msg to peer + + \Notes + Loss of the single unreliable disconnect packet sent in this function + is not critical, as the connection manager will respond to unexpected + packets that are not connect/disconnect packets (ie pings and voice + packets) with disconnection requests. + + \Version 05/10/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipConnectionStop(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnID, int32_t bSendDiscMsg) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + int32_t i = 0; + + NetCritEnter(&pConnectionlist->NetCrit); + + // make sure we're not already disconnected + if (pConnection->eState == ST_DISC) + { + NetPrintf(("voipconnection: [%d] disconnection attempt canceled because connection already in state ST_DISC!\n", iConnID)); + NetCritLeave(&pConnectionlist->NetCrit); + return; + } + + if (bSendDiscMsg) + { + // send a disconnection packet + uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId; + _VoipConnectionDisc(iConnID, uLocalClientId, pConnection->uRemoteClientId, pConnection->uSessionId[0], &pConnection->SendAddr, pConnectionlist->pSocket); + } + else + { + NetPrintf(("voipconnection: [%d] no need to send disconnect message to %a:%d\n", iConnID, + SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port))); + } + + // set to disconnected before unregister + pConnection->eState = ST_DISC; + pConnection->bConnPktRecv = FALSE; + + // subtract connection identifier from send/recv masks + VoipConnectionSetSendMask(pConnectionlist, pConnectionlist->uSendMask & ~(1 << iConnID)); + VoipConnectionSetRecvMask(pConnectionlist, pConnectionlist->uRecvMask & ~(1 << iConnID)); + + // unregister all the remote users on this connection with voipheadset + VoipConnectionRegisterRemoteTalkers(pConnectionlist, iConnID, FALSE); + + // clear outbound reliable queue + while (pConnection->pOutboundReliableDataQueue != NULL) + { + _VoipConnectionReliableDataDequeue(pConnectionlist, iConnID); + } + + // clear connection and mark as disconnected + ds_memclr(pConnection, sizeof(*pConnection)); + pConnection->uRemoteConnStatus = VOIP_CONN_STOPPED; + pConnection->iVoipServerConnId = VOIP_CONNID_NONE; + for ( i = 0; i < VOIP_MAXLOCALUSERS; ++i) + { + pConnectionlist->uLocalUserStatus[i] &= ~VOIP_LOCAL_USER_SENDVOICE; + pConnectionlist->uLocalUserLastSend[i] = 0; + } + + // clear channel config maintained by VoipCommon + ds_memclr(&pVoipCommon->uRemoteChannelSelection[iConnID], sizeof(pVoipCommon->uRemoteChannelSelection[iConnID])); + + NetCritLeave(&pConnectionlist->NetCrit); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipConnectionStartup + + \Description + Startup a connectionlist. + + \Input *pConnectionlist - connectionlist to init + \Input iMaxPeers - max number of connections to support + + \Output + int32_t - zero=success, negative=error + + \Version 03/20/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipConnectionStartup(VoipConnectionlistT *pConnectionlist, int32_t iMaxPeers) +{ + int32_t iConnId; + int32_t iMemGroup; + void *pMemGroupUserData; + + //Check to see if Max Peer has exceeded VOIP_MAXCONNECT + if (iMaxPeers > VOIP_MAXCONNECT) + { + NetPrintf(("voipconnection: error, max peer exceeded VOIP_MAXCONNECT.\n")); + return(-2); + } + + // Query current mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init connectionlist + if ((pConnectionlist->pConnections = (VoipConnectionT *)DirtyMemAlloc(sizeof(VoipConnectionT) * iMaxPeers, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipconnection: error, unable to allocate connectionlist\n")); + return(-1); + } + ds_memclr(pConnectionlist->pConnections, sizeof(VoipConnectionT) * iMaxPeers); + + pConnectionlist->iFriendConnId = VOIP_CONNID_NONE; + + // reset local-to-server conn id mappings + for (iConnId = 0; iConnId < iMaxPeers; iConnId++) + { + pConnectionlist->pConnections[iConnId].iVoipServerConnId = VOIP_CONNID_NONE; + } + + // init critical section + NetCritInit(&pConnectionlist->NetCrit, "voipconnection"); + + // set default timeout + pConnectionlist->iDataTimeout = VOIP_TIMEOUT; + + // set max peers + pConnectionlist->iMaxConnections = iMaxPeers; + + NetPrintf(("voipconnection: max connections = %d max reliable data size = %d max ping payload size = %d\n", + pConnectionlist->iMaxConnections, VOIP_MAXRELIABLEDATA, VOIP_MAXPINGPKTSIZE)); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionSetCallbacks + + \Description + Set required recv voice and reg user callbacks. + + \Input *pConnectionlist - connectionlist to register callbacks for + \Input *pVoiceCb - voice callback to register + \Input *pVoiceUserData - user data to be passed to the voice callback + \Input *pTextCb - text callback to register + \Input *pTextUserData - user data to be passed to the text callback + \Input *pRegUserCb - user reg callback to register + \Input *pRegUserUserData - user data to be passed to the reguser callback + \Input *pOpaqueCb - opaque data callback to register + \Input *pOpaqueUserData - user data to be passed to the opaquedata callback + + \Version 03/20/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionSetCallbacks(VoipConnectionlistT *pConnectionlist, VoipConnectRecvVoiceCbT *pVoiceCb, void *pVoiceUserData, + VoipConnectRecvTextCbT *pTextCb, void *pTextUserData, + VoipConnectRegUserCbT *pRegUserCb, void *pRegUserUserData, + VoipConnectRecvOpaqueCbT *pOpaqueCb, void *pOpaqueUserData) +{ + // save callbacks and callback user data + pConnectionlist->pRegUserCb = pRegUserCb; + pConnectionlist->pRegUserUserData = pRegUserUserData; + pConnectionlist->pVoiceCb = pVoiceCb; + pConnectionlist->pVoiceUserData = pVoiceUserData; + pConnectionlist->pTextCb = pTextCb; + pConnectionlist->pTextUserData = pTextUserData; + pConnectionlist->pOpaqueCb = pOpaqueCb; + pConnectionlist->pOpaqueUserData = pOpaqueUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionSetTextCallback + + \Description + Set required text callbacks for X1 crossplay asscessibility + Should be set to null for native mode + + \Input *pConnectionlist - connectionlist to register callbacks for + \Input *pTextCb - text callback to register + \Input *pTextUserData - user data to be passed to the text callback + + \Version 04/10/2019 (tcho) +*/ +/********************************************************************************F*/ +void VoipConnectionSetTextCallback(VoipConnectionlistT *pConnectionlist, VoipConnectRecvTextCbT *pTextCb, void *pTextUserData) +{ + // save callbacks and callback user data + pConnectionlist->pTextCb = pTextCb; + pConnectionlist->pTextUserData = pTextUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionShutdown + + \Description + Shutdown a connectionlist + + \Input *pConnectionlist - connectionlist to close + + \Version 03/20/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionShutdown(VoipConnectionlistT *pConnectionlist) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + LinkedReliableDataT *pEntry; + + // query current mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // clear free pool of reliable data entries + while ((pEntry = _VoipGetReliableDataBufferFromFreePool(pConnectionlist, FALSE)) != NULL) + { + DirtyMemFree(pEntry, VOIP_MEMID, iMemGroup, pMemGroupUserData); + } + + // dispose of connectionlist + DirtyMemFree(pConnectionlist->pConnections, VOIP_MEMID, iMemGroup, pMemGroupUserData); + + // dispose of socket + if (pConnectionlist->pSocket) + { + NetPrintf(("voipconnection: closing socket in VoipConnectionShutdown()\n")); + SocketClose(pConnectionlist->pSocket); + } + + // release critical section + NetCritKill(&pConnectionlist->NetCrit); + + // clear connectionlist + ds_memclr(pConnectionlist, sizeof(*pConnectionlist)); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionCanAllocate + + \Description + Check whether a given Connection ID can be allocated in this Connection List + + \Input *pConnectionlist - connection list ref + \Input iConnID - connection index + + \Output + uint8_t - Whether a connection can be allocated with given ConnID + + \Version 02/19/2008 (jrainy) +*/ +/********************************************************************************F*/ +uint8_t VoipConnectionCanAllocate(VoipConnectionlistT *pConnectionlist, int32_t iConnID) +{ + if ((iConnID < 0) || (iConnID >= pConnectionlist->iMaxConnections)) + { + return(FALSE); + } + + return(pConnectionlist->pConnections[iConnID].eState == ST_DISC); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionStart + + \Description + Start a connection to a peer. + + \Input *pConnectionlist - connection list ref + \Input iConnID - connection index + \Input uAddr - address to connect to + \Input uConnPort - connection port + \Input uBindPort - local bind port + \Input uClientId - id of client to connect to + \Input uSessionId - session id (cannot be 0) + + \Output + int32_t - connection identifier on success, negative=failure + + \Version 03/18/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipConnectionStart(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint32_t uAddr, uint32_t uConnPort, uint32_t uBindPort, uint32_t uClientId, uint32_t uSessionId) +{ + VoipConnectionT *pConnection; + struct sockaddr BindAddr; + int32_t iUserIndex; + + // sanity check: make sure local and remote clientIDs are different + if (pConnectionlist->uClientId == uClientId) + { + NetPrintf(("voipconnection: local client id (%d) and remote client id (%d) can't be same\n", pConnectionlist->uClientId, uClientId)); + return(-1); + } + + // if we haven't allocated a socket yet, do so now + if (pConnectionlist->pSocket == NULL) + { + int32_t iResult; + + // open the socket + if ((pConnectionlist->pSocket = SocketOpen(AF_INET, SOCK_DGRAM, VOIP_IPPROTO)) == NULL) + { + NetPrintf(("voipconnection: error creating socket\n")); + return(-2); + } + + // bind the socket + SockaddrInit(&BindAddr, AF_INET); + SockaddrInSetPort(&BindAddr, uBindPort); + if ((iResult = SocketBind(pConnectionlist->pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE) + { + NetPrintf(("voipconnection: error %d binding socket to port %d, trying random\n", iResult, uBindPort)); + SockaddrInSetPort(&BindAddr, 0); + if ((iResult = SocketBind(pConnectionlist->pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE) + { + NetPrintf(("voipconnection: error %d binding socket\n", iResult)); + SocketClose(pConnectionlist->pSocket); + pConnectionlist->pSocket = NULL; + return(-3); + } + } + + // retrieve bound port + SocketInfo(pConnectionlist->pSocket, 'bind', 0, &BindAddr, sizeof(BindAddr)); + uBindPort = SockaddrInGetPort(&BindAddr); + NetPrintf(("voipconnection: bound socket to port %d\n", uBindPort)); + + // save local port + pConnectionlist->uBindPort = uBindPort; + + // setup for socket events + SocketCallback(pConnectionlist->pSocket, CALLB_RECV, 5000, pConnectionlist, &_VoipConnectionRecvCallback); + } + + // make sure bind matches our previous bind + if (uBindPort != pConnectionlist->uBindPort) + { + NetPrintf(("voipconnection: warning, only one global bind port is currently supported, using previously specified port\n")); + } + + // convert address and ports to network form + uAddr = SocketHtonl(uAddr); + uConnPort = SocketHtons(uConnPort); + + /* + Guarding the following code with NetCrit proved to be required to protect against + _VoipConnectionStop() being invoked from the socket async recv thread upon reception + of a DISC packet (typically belonging to a previous session) on that connection. + + We did not include the above portion of this function within this critical + usage function, because atomic access to it is guaranteed by the threadcrit + being lock externally. + */ + NetCritEnter(&pConnectionlist->NetCrit); + + // allocate a connection + if ((pConnection = _VoipConnectionAllocate(pConnectionlist, iConnID)) == NULL) + { + NetCritLeave(&pConnectionlist->NetCrit); + + NetPrintf(("voipconnection: alloc failed\n")); + return(-4); + } + + iConnID = VOIP_ConnID(pConnectionlist, pConnection); + + // init the connection + ds_memclr(pConnection, sizeof(*pConnection)); + pConnection->iVoipServerConnId = VOIP_CONNID_NONE; + pConnection->SendAddr.sin_family = AF_INET; + pConnection->SendAddr.sin_addr.s_addr = uAddr; + pConnection->SendAddr.sin_port = uConnPort; + pConnection->uRemoteClientId = uClientId; + + // sequence number for reliable data ranges from 1 to 127 + pConnection->uOutSeqNb = 1; // use 1 as the first outbound sequence number + pConnection->uInSeqNb = 0; // seq nb of last received inbound message. validity range : [1,127]. 0 means no inbound traffic received yet + + // add specified session ID to the set of sessions sharing this connection + VoipConnectionAddSessionId(pConnectionlist, iConnID, uSessionId); + + pConnection->uRecvSeqn = 0xFFFFFFFF; // intialize last receive sequence number with -1. + pConnection->uRemoteConnStatus = 0; + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; ++iUserIndex) + { + pConnection->uRemoteUserStatus[iUserIndex] = 0; + } + pConnection->eState = ST_CONN; + pConnection->uLastRecv = NetTick(); + + // set up to transmit mic data + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + ds_memcpy_s(&pConnection->VoipMicrPacket[iUserIndex].Head, sizeof(pConnection->VoipMicrPacket[iUserIndex].Head), &_Voip_MicrPacket, sizeof(_Voip_MicrPacket)); + } + + // output connect message + NetPrintf(("voipconnection: [%d] connecting to %a:%d localId=0x%08x remoteId=0x%08x\n", iConnID, + SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port), + pConnection->uLocalClientId, pConnection->uRemoteClientId)); + + NetCritLeave(&pConnectionlist->NetCrit); + + // return connection ID to caller + return(iConnID); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionUpdate + + \Description + Update all connections + + \Input *pConnectionlist - connection list + + \Version 03/18/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionUpdate(VoipConnectionlistT *pConnectionlist) +{ + int32_t iConnId; + uint32_t uTick; + + // get sole access + NetCritEnter(&pConnectionlist->NetCrit); + + // receive any incoming data + while(_VoipConnectionRecv(pConnectionlist) > 0) + ; + + _VoipConnectionTrySocketClose(pConnectionlist); + + // relinquish sole access + NetCritLeave(&pConnectionlist->NetCrit); + + // update connection status for all connections, keeping track of who we are receiving voice from + for (iConnId = 0, uTick = NetTick(), pConnectionlist->uRecvVoice = 0; iConnId < pConnectionlist->iMaxConnections; iConnId++) + { + pConnectionlist->uRecvVoice |= _VoipConnectionUpdateSingle(pConnectionlist, &pConnectionlist->pConnections[iConnId], iConnId, uTick); + } + + #if !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) + /* check if we need to time out talking status + on xbox one this is handled in the voipxbxone _VoipUpdateLocalStatus() + */ + for (int32_t i = 0; i < VOIP_MAXLOCALUSERS; ++i) + { + if ((pConnectionlist->uLocalUserStatus[i] & VOIP_LOCAL_USER_TALKING) && (NetTickDiff(uTick, pConnectionlist->uLastVoiceTime[i]) > VOIP_TALKTIMEOUT)) + { + pConnectionlist->uLocalUserStatus[i] &= ~VOIP_LOCAL_USER_TALKING; + } + } + #endif +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionStop + + \Description + Stop a connection with a peer. + + \Input *pConnectionlist - connection list ref + \Input iConnID - connection to stop, or VOIP_CONNID_ALL to stop all connections + \Input bSendDiscMsg - TRUE - send DISC msg to peer; FALSE - do not send DISC msg to peer + + \Version 03/18/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionStop(VoipConnectionlistT *pConnectionlist, int32_t iConnID, int32_t bSendDiscMsg) +{ + if (iConnID == VOIP_CONNID_ALL) + { + // disconnect from all current connections + for (iConnID = 0; iConnID < pConnectionlist->iMaxConnections; iConnID++) + { + _VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnID], iConnID, bSendDiscMsg); + } + } + else if ((iConnID >= 0) && (iConnID < pConnectionlist->iMaxConnections)) + { + // disconnect from the given connection + _VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnID], iConnID, bSendDiscMsg); + } + else + { + NetPrintf(("voipconnection: disconnect with iConnID=%d is invalid\n", iConnID)); + } + + _VoipConnectionTrySocketClose(pConnectionlist); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionSend + + \Description + Send data to peer. + + \Input *pConnectionlist - connectionlist to send to + \Input uSendMask - mask of connections to send to + \Input *pVoiceData - pointer to data to send + \Input iDataSize - size of data to send + \Input *pMetaData - pointer to metadata to be added to voip packet + \Input iMetaDataSize - size of metadata + \Input uUserIndex - local user index + \Input uSendSeqn - seq nb + + \Version 03/17/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionSend(VoipConnectionlistT *pConnectionlist, uint32_t uSendMask, const uint8_t *pVoiceData, int32_t iDataSize, const uint8_t *pMetaData, int32_t iMetaDataSize, uint32_t uUserIndex, uint8_t uSendSeqn) +{ + uint32_t uCurTick = NetTick(); + int32_t iConnID; + int32_t iParticipatingUserIndex; + uint8_t bSentToVoipServer = FALSE; + + // early exit if metadata is too big + if ((pMetaData != NULL) && (iMetaDataSize > 256)) + { + NetPrintf(("voipconnection: critical error! metadata is too big\n")); + return; + } + + // early exit if the user index is invalid + if (uUserIndex >= VOIP_MAXLOCALUSERS_EXTENDED) + { + return; + } + + // if we are dealing with the remote user update all the user's last voice receive time + if (uUserIndex == VOIP_SHARED_USER_INDEX) + { + for (iParticipatingUserIndex = 0; iParticipatingUserIndex < VOIP_MAXLOCALUSERS; ++iParticipatingUserIndex) + { + if (pConnectionlist->aIsParticipating[iParticipatingUserIndex] == TRUE) + { + pConnectionlist->uLastVoiceTime[iParticipatingUserIndex] = uCurTick; + } + } + } + else + { + pConnectionlist->uLastVoiceTime[uUserIndex] = uCurTick; + } + + // loop through all connections + for (iConnID = 0; iConnID < pConnectionlist->iMaxConnections; iConnID++) + { + // ref the connection + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID]; + + // skip connections that are not set in the sendmask parameter + if ((uSendMask & (1 << iConnID)) == 0) + { + continue; + } + + // for connections via voip server, send only to the first connection + // todo: amakoukji, for CCS phase 2 we will need to remember what voip server we already sent the packet to and skip + if ((pConnection->iVoipServerConnId != VOIP_CONNID_NONE) && (bSentToVoipServer != FALSE)) + { + continue; + } + + /* is the connection active, and are we sending to it? + xboxone: After rebasing VoipHeadsetXboxOne to MS game chat 2, per-connection muting + is handled by VoipHeadsetXboxOne (See usage of +snd, -snd, +rcv, -rcv). + But for crossplay we want the muting to be applied here just like other plaforms. + Testing has shown that apply te send mask and +snd, -snd, +rcv, -rcv has no negative effect.*/ + + if ((pConnection->eState == ST_ACTV) && (pConnectionlist->uSendMask & (1 << iConnID))) + { + uint8_t *pWrite; // write position in voip packet + VoipMicrPacketT *pMicrPacket = &pConnection->VoipMicrPacket[uUserIndex]; + uint8_t bVariableSubPktLen; + int32_t iResult; + + // set talking flag + // if the voice packet is from a shared user set all the participating user status to be talking + if (uUserIndex == VOIP_SHARED_USER_INDEX) + { + for (iParticipatingUserIndex = 0; iParticipatingUserIndex < VOIP_MAXLOCALUSERS; ++iParticipatingUserIndex) + { + if (pConnectionlist->aIsParticipating[iParticipatingUserIndex] == TRUE) + { + pConnectionlist->uLocalUserStatus[iParticipatingUserIndex] |= VOIP_LOCAL_USER_TALKING; + } + } + } + else + { + pConnectionlist->uLocalUserStatus[uUserIndex] |= VOIP_LOCAL_USER_TALKING; + } + + // record maximum outgoing sub-pkt size on this connection + if (iDataSize > pConnection->iMaxSubPktSize) + { + NetPrintf(("voipconnection: [%d] max recorded outbound sub-pkt size increased from %d to %d\n", iConnID, pConnection->iMaxSubPktSize, iDataSize)); + pConnection->iMaxSubPktSize = iDataSize; + } + + // is new metadata different than metadata already in packet + if ((pMicrPacket->MicrInfo.uNumSubPackets != 0) && (pMetaData != NULL)) + { + uint8_t bDifferentMetaData = FALSE; + uint8_t *pPackedMetaData = _VoipPacketGetMetaDataPtr(pMicrPacket); + + #if DIRTYCODE_LOGGING + if (!(pMicrPacket->Head.uFlags & VOIP_PACKET_STATUS_FLAG_METADATA)) + { + NetPrintf(("voipconnection: [%d] critical error - metadata expected in packet but missing\n", iConnID)); + } + #endif + + if (iMetaDataSize == *pPackedMetaData) + { + if (memcmp(pMetaData, (pPackedMetaData+1), iMetaDataSize) != 0) + { + bDifferentMetaData = TRUE; + } + } + else + { + bDifferentMetaData = TRUE; + } + + if (bDifferentMetaData) + { + NetPrintf(("voipconnection: [%d] flushing pending voip packet because new sub-packet has different metadata\n", iConnID)); + _VoipConnectionSendSingle(pConnectionlist, pConnection, uUserIndex, uCurTick, TRUE); + } + } + + // if no voice sub-packets are queued yet + if (pMicrPacket->MicrInfo.uNumSubPackets == 0) + { + // if needed, add reliable protocol components to this voip packet + if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE) + { + // packet routed through voip server + pWrite = _VoipReliableDataOutProcess(pConnectionlist, VOIP_CONNID_ALL, &pMicrPacket->Head, &pMicrPacket->aData[0], sizeof(pMicrPacket->aData)); + } + else + { + // p2p packet + pWrite = _VoipReliableDataOutProcess(pConnectionlist, iConnID, &pMicrPacket->Head, &pMicrPacket->aData[0], sizeof(pMicrPacket->aData)); + } + + // if the voice timer has elapsed, reset it + if (NetTickDiff(uCurTick, pConnection->aVoiceSendTimer[uUserIndex]) >= 0) + { + pConnection->aVoiceSendTimer[uUserIndex] = uCurTick + VOIP_MSPERPACKET; + } + + // if necessary, add metadata before we start appending the first packet + if (pMetaData != NULL) + { + // store metadata length in packet buffer + *pWrite++ = (unsigned)iMetaDataSize; + + // copy metadata into packet buffer + ds_memcpy(pWrite, pMetaData, iMetaDataSize); + pWrite += iMetaDataSize; + + // set flag signaling that payload starts with metadata size/metadata pair + pMicrPacket->Head.uFlags |= VOIP_PACKET_STATUS_FLAG_METADATA; + } + } + else + { + pWrite = _VoipPacketGetWritePtr(pMicrPacket); + } + + // does this platform work with variable length sub-pkts + if ((iResult = VoipStatus(VoipGetRef(), 'vlen', 0, &bVariableSubPktLen, sizeof(bVariableSubPktLen))) < 0) + { + // if selector is not supported, assume fixed length + bVariableSubPktLen = FALSE; + } + + if (bVariableSubPktLen) + { + // MicrInfo.uSubPacketSize == 0xFF means that sub-pkts may have different sizes + pMicrPacket->MicrInfo.uSubPacketSize = 0xFF; + } + else + { + #if DIRTYCODE_LOGGING + if (pMicrPacket->MicrInfo.uSubPacketSize != 0 && pMicrPacket->MicrInfo.uSubPacketSize != iDataSize) + { + NetPrintf(("voipconnection: [%d] critical error - sub-packets have different size -> %d vs %d\n", iConnID, pMicrPacket->MicrInfo.uSubPacketSize, iDataSize)); + } + if (pMicrPacket->MicrInfo.uSubPacketSize == 0xFF) + { + NetPrintf(("voipconnection: [%d] critical error - sub-pkt size conflicting with fixed-length sub-pkt mode\n", iConnID)); + } + #endif + pMicrPacket->MicrInfo.uSubPacketSize = (unsigned)iDataSize; + } + + if (bVariableSubPktLen) + { + // store sub-pkt length in packet buffer + *pWrite = (unsigned)iDataSize; + + // copy data into packet buffer + ds_memcpy(pWrite+1, pVoiceData, iDataSize); + + #if DIRTYCODE_LOGGING + if ((pWrite + 1 + iDataSize) >= ((&pConnection->VoipMicrPacket[uUserIndex].aData[0]) + sizeof(pConnection->VoipMicrPacket[uUserIndex].aData))) + { + NetPrintf(("voipconnection: [%d] critical error - sub-packet packing overflowed!\n", iConnID)); + } + #endif + } + else + { + // copy data into packet buffer + ds_memcpy(pWrite, pVoiceData, iDataSize); + } + + // increment sub-packet count + pMicrPacket->MicrInfo.uNumSubPackets += 1; + + // identify local user index that generated the voice data + pMicrPacket->MicrInfo.uUserIndex = uUserIndex; + + // see if we need to send any buffered voice data + _VoipConnectionSendSingle(pConnectionlist, pConnection, uUserIndex, uCurTick, FALSE); + + if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE) + { + // mark game server as served + bSentToVoipServer = TRUE; + } + } + } +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionFlush + + \Description + Send currently queued voice data, if any. + + \Input *pConnectionlist - connectionlist to send to + \Input iConnID - connection ident of connection to flush + + \Output + int32_t - number of voice packets flushed + + \Notes + Currently only useful on Xenon + + \Version 01/04/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipConnectionFlush(VoipConnectionlistT *pConnectionlist, int32_t iConnID) +{ + int32_t iUserIndex; + int32_t iNumSubPackets = 0; + int32_t iTotalNumSubPackets = 0; + + // ref the connection + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID]; + + for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++) + { + iNumSubPackets = (signed)pConnection->VoipMicrPacket[iUserIndex].MicrInfo.uNumSubPackets; + + /* is the connection active, and are we sending to it? + xboxone: Never "mute" here, i.e. skip applying pConnectionlist->uSendMask. + Per-connection muting handling is rather deferred to VoipHeadsetXboxOne (See usage of +snd, -snd, +rcv, -rcv). + After rebasing VoipHeadsetXboxOne to MS game chat 2, we found out that the integration + with our per-connection muting here was not behaving properly during unmuting: resuming + submission of data frames to MS game chat 2 resulted in a ~ 5-sec delay before speech + would resume. */ + if ((pConnection->eState == ST_ACTV) && (iNumSubPackets > 0) + #if !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK) + && (pConnectionlist->uSendMask & (1 << iConnID)) + #endif + ) + { + // see if we need to send any buffered voice data + _VoipConnectionSendSingle(pConnectionlist, pConnection, iUserIndex, NetTick(), TRUE); + + // increment total number of sub-packets flushed for the connection + iTotalNumSubPackets += iNumSubPackets; + } + } + + // return total number of sub-packets flushed for the connection + return(iTotalNumSubPackets); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionRegisterRemoteTalkers + + \Description + Register/unregister remote users associated with the given connection. + + \Input *pConnectionlist - connectionlist to send to + \Input iConnID - connection ident of connection to flush + \Input bRegister - if TRUE register talkers, else unregister them + + \Version 01/04/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionRegisterRemoteTalkers(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint32_t bRegister) +{ + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID]; + int32_t iUser; + + // register any remote users that are part of the connection + for (iUser = 0; iUser < pConnection->iMaxRemoteUsersExt; iUser++) + { + VoipUserT* pRemoteUser = (VoipUserT *)(&pConnection->RemoteUsers[iUser]); + // if no user, don't register + if (VOIP_NullUser(pRemoteUser)) + { + continue; + } + + #if DIRTYCODE_LOGGING + // if the user shouldn't be joining us, complain + if (pRemoteUser->ePlatform != VOIP_LOCAL_PLATFORM) // a platform that doesn't match the local platform is joining us + { + // is cross play setup properly for the remote user + if ((pRemoteUser->uFlags & VOIPUSER_FLAG_CROSSPLAY) == FALSE) + { + NetPrintf(("voipconnection: [%d] error, platform %d, persona %lld attempted to join, but cross play is not enabled remotely.\n", iConnID, pRemoteUser->ePlatform, pRemoteUser->AccountInfo.iPersonaId)); + } + + // is cross play setup properly for the local user + if (VoipStatus(VoipGetRef(), 'xply', 0, NULL, 0) == FALSE) + { + NetPrintf(("voipconnection: [%d] error, platform %d, persona %lld attempted to join, but cross play is not enabled locally.\n", iConnID, pRemoteUser->ePlatform, pRemoteUser->AccountInfo.iPersonaId)); + } + } + else if (((pRemoteUser->uFlags & VOIPUSER_FLAG_CROSSPLAY) == TRUE) != (VoipStatus(VoipGetRef(), 'xply', 0, NULL, 0) == TRUE)) // the platform matches, but the cross play settings should match too + { + NetPrintf(("voipconnection: [%d] error, local and remote user have mismatched cross play settings, platform %d, persona %lld.\n", iConnID, pRemoteUser->ePlatform, pRemoteUser->AccountInfo.iPersonaId)); + } + #endif + + // register the user + pConnectionlist->pRegUserCb((VoipUserT *)&pConnection->RemoteUsers[iUser], iConnID, bRegister, pConnectionlist->pRegUserUserData); + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) + // reapply player-to-player xone comm relationships + pConnectionlist->bApplyRelFromMuting = TRUE; +#endif + } + + // set mute list to be updated + pConnectionlist->bUpdateMute = TRUE; +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionSetSendMask + + \Description + Set connections to send to. + + \Input *pConnectionlist - connectionlist to send to + \Input uSendMask - connection send mask + + \Version 03/22/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionSetSendMask(VoipConnectionlistT *pConnectionlist, uint32_t uSendMask) +{ + if (pConnectionlist->uSendMask != uSendMask) + { + VoipCommonSetMask(&pConnectionlist->uSendMask, uSendMask, "sendmask"); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionSetRecvMask + + \Description + Set connections to receive from. + + \Input *pConnectionlist - connectionlist to send to + \Input uRecvMask - connection receive mask + + \Version 03/22/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipConnectionSetRecvMask(VoipConnectionlistT *pConnectionlist, uint32_t uRecvMask) +{ + if (pConnectionlist->uRecvMask != uRecvMask) + { + VoipCommonSetMask(&pConnectionlist->uRecvMask, uRecvMask, "recvmask"); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionAddSessionId + + \Description + Add a session ID the set of higher level sessions sharing the specified VoIP connection. + + \Input *pConnectionlist - connectionlist + \Input iConnId - connection ID + \Input uSessionId - session ID to be added + + \Output + int32_t - 0 for success, negative for failure + + \Notes + Maintaning a set of session IDs per voip connection is required to support cases + where two consoles are involved in a P2P game and a pg simultaneously. Both of these + constructs will have a different session ID over the same shared voip connection. + Under some specific race conditions affecting the order at which Blaze messages are + processed by each game client, it is very possible that the pg session id is setup + first on one side and second on the other side thus leading to VOIP connectivity + failures if the voip connection construct is not supporting multiple concurrent + session IDs. + + \Version 08/30/2011 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t VoipConnectionAddSessionId(VoipConnectionlistT *pConnectionlist, int32_t iConnId, uint32_t uSessionId) +{ + int32_t iSessionIndex; + int32_t iRetCode = -1; + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId]; + + NetCritEnter(&pConnectionlist->NetCrit); + + for (iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++) + { + if (pConnection->uSessionId[iSessionIndex] == 0) + { + // free spot, insert session ID here + pConnection->uSessionId[iSessionIndex] = uSessionId; + + NetPrintf(("voipconnection: [%d] added 0x%08x to set of sessions sharing this voip connection\n", iConnId, uSessionId)); + + iRetCode = 0; + break; + } + } + + if (iRetCode != 0) + { + NetPrintf(("voipconnection: [%d] warning - 0x%08x could not be added to set of sessions sharing this voip connection because the list is full.\n", iConnId, uSessionId)); + } + + #if DIRTYCODE_LOGGING + // print set of sessions sharing this connection + _VoipConnectionPrintSessionIds(pConnectionlist, iConnId); + #endif + + NetCritLeave(&pConnectionlist->NetCrit); + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionDeleteSessionId + + \Description + Delete a session ID from the set of sessions sharing this voip connection. + + \Input *pConnectionlist - connectionlist + \Input iConnId - connection id + \Input uSessionId - session ID to be deleted + + \Output + int32_t - 0 for success, negative for failure + + \Version 08/30/2011 (mclouatre) +*/ +/********************************************************************************F*/ +int32_t VoipConnectionDeleteSessionId(VoipConnectionlistT *pConnectionlist, int32_t iConnId, uint32_t uSessionId) +{ + int32_t iSessionIndex; + int32_t iRetCode = -1; + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId]; + + NetCritEnter(&pConnectionlist->NetCrit); + + // now delete this address from the list of fallbacks + for(iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++) + { + if (pConnection->uSessionId[iSessionIndex] == uSessionId) + { + int32_t iSessionIndex2; + + // move all following session IDs one cell backward in the array + for(iSessionIndex2 = iSessionIndex; iSessionIndex2 < VOIP_MAXSESSIONIDS; iSessionIndex2++) + { + if (iSessionIndex2 == VOIP_MAXSESSIONIDS-1) + { + // last entry, reset to 0 + pConnection->uSessionId[iSessionIndex2] = 0; + } + else + { + pConnection->uSessionId[iSessionIndex2] = pConnection->uSessionId[iSessionIndex2+1]; + } + } + + NetPrintf(("voipconnection: [%d] removed 0x%08x from set of sessions sharing this voip connection\n", iConnId, uSessionId)); + + iRetCode = 0; + break; + } + } + + if (iRetCode != 0) + { + NetPrintf(("voipconnection: [%d] warning - 0x%08x not deleted because not found in set of sessions sharing this voip connection\n", iConnId, uSessionId)); + } + + #if DIRTYCODE_LOGGING + // print set of sessions sharing this connection + _VoipConnectionPrintSessionIds(pConnectionlist, iConnId); + #endif + + NetCritLeave(&pConnectionlist->NetCrit); + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionReliableBroadcastUser + + \Description + Use this function to broadcast local user join-in-progress/leave-in-progress + data that needs to be sent reliably on all active connections (or connections in ST_CONN state). + + \Input *pConnectionlist - connectionlist + \Input uLocalUserIndex - local user index + \Input bParticipating - TRUE if user joined-in-progress, FALSE if user left-in-progress + + \Version 09/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipConnectionReliableBroadcastUser(VoipConnectionlistT *pConnectionlist, uint8_t uLocalUserIndex, uint32_t bParticipating) +{ + ReliableDataT ReliableData; + uint8_t *pWrite = &ReliableData.aData[0]; + + // fill type + ReliableData.info.uType = (bParticipating ? VOIP_RELIABLE_TYPE_USERADD : VOIP_RELIABLE_TYPE_USERREM); + + // fill payload with user index first + *pWrite++ = uLocalUserIndex; + + // then append voip user + ds_memcpy_s(pWrite, sizeof(ReliableData.aData), &pConnectionlist->LocalUsers[uLocalUserIndex], sizeof(pConnectionlist->LocalUsers[uLocalUserIndex])); + pWrite = pWrite + sizeof(pConnectionlist->LocalUsers[uLocalUserIndex]); + + // fill size + _VoipEncodeU16(&ReliableData.info.uSize[0], (pWrite - &ReliableData.aData[0])); + + // enqueue for transmission on all connections (will also fill in seq number) + _VoipConnectionReliableDataEnqueue(pConnectionlist, VOIP_CONNID_ALL, &ReliableData); +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionReliableTranscribedTextMessage + + \Description + Use this function to reliably broadcast a transcribed text message + (originated from a local user) on all connections that requested for it. + + \Input *pConnectionlist - connectionlist + \Input uLocalUserIndex - local user index + \Input *pStrUtf8 - pointer to text message to be sent + + \Version 09/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipConnectionReliableTranscribedTextMessage(VoipConnectionlistT *pConnectionlist, uint8_t uLocalUserIndex, const char *pStrUtf8) +{ + int32_t iConnectionIndex; + ReliableDataT ReliableData; + uint8_t *pWrite = &ReliableData.aData[0]; + + // fill type + ReliableData.info.uType = VOIP_RELIABLE_TYPE_TEXT; + + // fill payload with user index first + *pWrite++ = uLocalUserIndex; + ds_strnzcpy((char *)pWrite, pStrUtf8, VOIP_MAXRELIABLEDATA - 1); + pWrite += strlen((const char *)pWrite)+1; + + // fill size of payload + _VoipEncodeU16(&ReliableData.info.uSize[0], (pWrite - &ReliableData.aData[0])); + + // enqueue for transmission only on connections with users that requested for transcribed text + for (iConnectionIndex = 0; iConnectionIndex < pConnectionlist->iMaxConnections; iConnectionIndex++) + { + VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex]; + + if ((pConnection->eState == ST_ACTV) && pConnection->bTranscribedTextRequested && (pConnectionlist->uSendMask & (1 << iConnectionIndex))) + { + _VoipConnectionReliableDataEnqueue(pConnectionlist, iConnectionIndex, &ReliableData); + } + } +} + +/*F********************************************************************************/ +/*! + \Function VoipConnectionReliableSendOpaque + + \Description + Use this function to send opaque data reliably over specified connection. + + \Input *pConnectionlist - connectionlist + \Input iConnID - connection to send the data over + \Input *pData - pointer to buffer filled with reliable data to be broadcasted + \Input uDataSize - reliable data size (in bytes) + + \Version 09/18/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipConnectionReliableSendOpaque(VoipConnectionlistT *pConnectionlist, int32_t iConnID, const uint8_t *pData, uint16_t uDataSize) +{ + ReliableDataT ReliableData; + + // fill type + ReliableData.info.uType = VOIP_RELIABLE_TYPE_OPAQUE; + + // fill size + _VoipEncodeU16(&ReliableData.info.uSize[0], uDataSize); + + // fill payload + ds_memcpy_s(&ReliableData.aData[0], sizeof(ReliableData.aData), pData, uDataSize); + + // enqueue for transmission on all connections (will also fill in seq number) + _VoipConnectionReliableDataEnqueue(pConnectionlist, iConnID, &ReliableData); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipconnection.h b/r5dev/thirdparty/dirtysdk/source/voip/voipconnection.h new file mode 100644 index 00000000..446a36c5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipconnection.h @@ -0,0 +1,246 @@ +/*H********************************************************************************/ +/*! + \File voipconnection.h + + \Description + VoIP virtual connection manager. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 03/17/2004 (jbrookes) First Version + \Version 1.1 12/10/2008 (mclouatre) Added ref count to VOIP connections. + \Version 1.2 01/06/2009 (mclouatre) Added field RemoteAddrFallbacks to VoipConnectionT structure. Create related functions + \Version 1.3 10/26/2009 (mclouatre) Renamed from xbox/voipconnection.h to xenon/voipconnectionxenon.h +*/ +/********************************************************************************H*/ + +#ifndef _voipconnection_h +#define _voipconnection_h + +/*** Include files ****************************************************************/ + +#include "voippacket.h" + +/*** Defines **********************************************************************/ +//! maximum number of session IDs per VOIP connection +#define VOIP_MAXSESSIONIDS (8) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! voice receive callback +typedef void (VoipConnectRecvVoiceCbT)(VoipUserT *pRemoteUsers, int32_t iRemoteUserSize, int32_t iConnId, VoipMicrInfoT *pMicrInfo, uint8_t *pPacketData, void *pUserData); + +//! transcribed text recv callback +typedef void (VoipConnectRecvOpaqueCbT)(int32_t iConnId, const uint8_t *pOpaqueData, int32_t iOpaqueDataSize, void *pUserData); + +//! transcribed text recv callback +typedef void (VoipConnectRecvTextCbT)(int32_t iConnId, int32_t iRemoteUserIndex, const char *pStrUtf8, void *pUserData); + +//! new remote user register callback +typedef void (VoipConnectRegUserCbT)(VoipUserT *pRemoteUser, int32_t iConnId, uint32_t bRegister, void *pUserData); + +typedef struct SendAddrFallbackT +{ + uint32_t SendAddr; //!< send address + uint8_t bReachable; //!< FALSE if voip detected the IP addr as being unreachable +} SendAddrFallbackT; + +//! type used to create linked list of reliable data entries +typedef struct LinkedReliableDataT +{ + struct LinkedReliableDataT *pNext; // +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "voipdvi.h" + +/*** Defines **********************************************************************/ + +#define DIV_RANGE (4) +#define MAX_RANGE (8191) + +//! encode/decode information +#define VOIPDVI_FRAME_SIZE (8) + +/*** Macros ***********************************************************************/ + +#define CLIP(x,lo,hi) \ + if (x < lo) \ + { \ + x = lo; \ + } \ + else if (x > hi) \ + { \ + x = hi; \ + } + + +/*** Type Definitions *************************************************************/ + +//! DVI compression state +typedef struct DVICompStateT +{ + int16_t iEstimate; + int16_t iStepIndex; +} DVICompStateT; + +typedef struct VoipDVIStateT +{ + VoipCodecRefT CodecState; + + // codec-specifc data goes here + DVICompStateT EncodeState; + int32_t iOutputVolume; +} VoipDVIStateT; + + +/*** Function Prototypes **********************************************************/ + +static VoipCodecRefT *_VoipDVICreate(int32_t iDecoderChannels); +static void _VoipDVIDestroy(VoipCodecRefT *pCodecState); +static int32_t _VoipDVIEncodeBlock3(VoipCodecRefT *pCodecState, uint8_t *pOut, const int16_t *pInp, int32_t iNumSamples); +static int32_t _VoipDVIDecodeBlock3(VoipCodecRefT *pCodecState, int32_t *pOut, const uint8_t *pInp, int32_t iInputBytes, int32_t iChannel); +static void _VoipDVIReset(VoipCodecRefT *pCodecState); +static int32_t _VoipDVIControl(VoipCodecRefT *pCodecState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); +static int32_t _VoipDVIStatus(VoipCodecRefT *pCodecState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize); + +/*** Variables ********************************************************************/ + +// Public variables + +//! public DVI codec definition +const VoipCodecDefT VoipDVI_CodecDef = +{ + _VoipDVICreate, + _VoipDVIDestroy, + _VoipDVIEncodeBlock3, + _VoipDVIDecodeBlock3, + _VoipDVIReset, + _VoipDVIControl, + _VoipDVIStatus, +}; + +// Private variables + +static int16_t _StepLimit3 = 0; +static int16_t _StepTable3[64]; +static int16_t _StepIndex3[4] = { -2, -1, 2, 5 }; + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _DeltaEncode3 + + \Description + Encode the prediction error into a 3-bit value + + \Input iStep - current step size + \Input iDelta - error delta to encode + + \Output + int32_t - Three bit encoded value + + \Version 05/13/2003 (gschaefer) +*/ +/********************************************************************************F*/ +static __inline int32_t _DeltaEncode3(int32_t iStep, int32_t iDelta) +{ + int32_t iEncode = 0; + + // check for negative direction + if (iDelta < 0) + { + iEncode |= 4; + iDelta = -iDelta; + } + + // primary delta shift + if (iDelta >= iStep) + { + iEncode |= 2; + iDelta -= iStep; + } + + // secondary delta shift + iStep >>= 1; + if (iDelta >= iStep) + { + iEncode |= 1; + iDelta -= iStep; + } + + // return encoded value + return(iEncode); +} + +/*F********************************************************************************/ +/*! + \Function _DeltaDecode3 + + \Description + Decode 3-bit value into prediction error delta + + \Input iStep - current step size + \Input iEncode - encoded 3-bit value + + \Output + int32_t - Prediction error delta + + \Version 105/13/2003 (gschaefer) +*/ +/********************************************************************************F*/ +static __inline int32_t _DeltaDecode3(int32_t iStep, int32_t iEncode) +{ + int32_t iDelta = 0; + + // primary delta shift + if (iEncode & 2) + { + iDelta += iStep; + } + + // secondary delta shift + iStep >>= 1; + if (iEncode & 1) + { + iDelta += iStep; + } + + // always add final fraction to reduce truncation error + iDelta += (iStep >> 1); + + // handle negative shift + if (iEncode & 4) + { + iDelta = -iDelta; + } + return(iDelta); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDVISetupBlock3 + + \Description + Dynamically build the initial quantization table + + \Version 05/13/2003 (gschaefer) +*/ +/********************************************************************************F*/ +static void _VoipDVISetupBlock3(void) +{ + int32_t iStep; + int32_t iLast = 0; + + // reset number of table entries + _StepLimit3 = 0; + + // use fixed point math to allow step = step * 1.2 + for (iStep = 300; iStep < MAX_RANGE*100; iStep = (iStep * 120) / 100) + { + // skip non integer changes + if ((iStep/100) != iLast) + { + iLast = iStep/100; + _StepTable3[_StepLimit3++] = iLast; + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipDVICreate + + \Description + Create Voip DVI state + + \Input iDecodeChannels - number of channels to support decoding for + + \Output + VoipCodecRefT * - pointer to new codec state, or NULL if failure + + \Version 08/05/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static VoipCodecRefT *_VoipDVICreate(int32_t iDecodeChannels) +{ + VoipDVIStateT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // set up DVI tables + _VoipDVISetupBlock3(); + + // allocate and clear state + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + if ((pState = (VoipDVIStateT *) DirtyMemAlloc (sizeof(*pState), VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipdvi: unable to allocate state\n")); + return(NULL); + } + ds_memclr(pState, sizeof(*pState)); + + // set up codec state + pState->CodecState.pCodecDef = &VoipDVI_CodecDef; + pState->CodecState.iDecodeChannels = iDecodeChannels; + + // set the output level + pState->iOutputVolume = 1 << VOIP_CODEC_OUTPUT_FRACTIONAL; + + // return generic state ref to caller + return(&pState->CodecState); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDVIDestroy + + \Description + Destroy given Voip DVI codec state + + \Input *pCodecState - pointer to state to destroy + + \Version 08/05/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipDVIDestroy(VoipCodecRefT *pCodecState) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + DirtyMemFree(pCodecState, VOIP_MEMID, iMemGroup, pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDVIEncodeBlock3 + + \Description + Encode a 16-bit linear PCM sample into a 3-bit value using ADPCM. + + \Input *pCodecState - module state + \Input *pOut - packed output buffer + \Input *pInp - 16-bit mono sample buffer pointer + \Input iNumSamples - number of samples to compress + + \Output + int32_t - size of compressed data in bytes + + \Version 05/13/2003 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _VoipDVIEncodeBlock3(VoipCodecRefT *pCodecState, uint8_t *pOut, const int16_t *pInp, int32_t iNumSamples) +{ + int32_t iCount; + int32_t iIndex; + int32_t iDelta; + int32_t iSample; + int32_t iEstimate; + int32_t iStepIndex; + unsigned char Stage[8]; + unsigned char *pBeg = pOut; + DVICompStateT *pCompState = &((VoipDVIStateT *)pCodecState)->EncodeState; + + // DVI encoder requires input samples to be a multiple of VOIPDVI_FRAME_SIZE + if ((iNumSamples % VOIPDVI_FRAME_SIZE) != 0) + { + NetPrintf(("voipdvi: error - dvi encoder can only encode multiples of %d samples (%d submitted).\n", VOIPDVI_FRAME_SIZE, iNumSamples)); + return(0); + } + + // save initial state to output buffer + ds_memcpy(pOut, pCompState, sizeof(*pCompState)); + pOut += sizeof(*pCompState); + + // fetch initial state + iEstimate = pCompState->iEstimate; + iStepIndex = pCompState->iStepIndex; + + for (iCount = 0; iCount < iNumSamples; ++iCount) + { + // calc delta from previous estimate + iDelta = (*pInp++ / DIV_RANGE) - iEstimate; + CLIP(iDelta, -MAX_RANGE, MAX_RANGE); + // encode the sample + iSample = _DeltaEncode3(_StepTable3[iStepIndex], iDelta); + // adjust the estimate based on decoded sample (since this is what decoded will use) + iEstimate += _DeltaDecode3(_StepTable3[iStepIndex], iSample); + CLIP(iEstimate, -MAX_RANGE, MAX_RANGE); + + #if 0 //$$ debug diagnostic to help tune/debug algorithm + NetPrintf(("sample #%d: orig=%5d comp=%5d err=%4d delta=%4d idx=%2d step=%5d code=%02x\n", + iCount, pInp[-1], iEstimate, pInp[-1]-iEstimate, iDelta, + iStepIndex, _StepTable3[iStepIndex], iSample)); + #endif + + // adjust the step size for next iteration + iStepIndex += _StepIndex3[iSample & 3]; + CLIP(iStepIndex, 0, _StepLimit3-1); + // save into staging buffer + iIndex = iCount&7; + Stage[iIndex] = iSample; + // see if we need to store the output + if (iIndex == 7) + { + pOut[0] = (Stage[0]<<0) | (Stage[1]<<3) | (Stage[2]<<6); + pOut[1] = (Stage[2]>>2) | (Stage[3]<<1) | (Stage[4]<<4) | (Stage[5]<<7); + pOut[2] = (Stage[5]>>1) | (Stage[6]<<2) | (Stage[7]<<5); + pOut += 3; + } + } + + // update with current state + pCompState->iEstimate = iEstimate; + pCompState->iStepIndex = iStepIndex; + + // return the length + return(pOut - pBeg); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDVIDecodeBlock3 + + \Description + Decode 3-bit ADPCM packed data and accumulate into a 32-bit buffer of 16-bit linear PCM + samples. + + \Input *pCodecState - module state + \Input *pOut - 32-bit linear PCM sample accumulation buffer + \Input *pInp - packed data from encoder + \Input iInputBytes - to be completed + \Input iChannel - ignored + + \Output + int32_t - number of samples decoded + + \Version 05/13/2003 (gschaefer) +*/ +/********************************************************************************F*/ +static int32_t _VoipDVIDecodeBlock3(VoipCodecRefT *pCodecState, int32_t *pOut, const unsigned char *pInp, int32_t iInputBytes, int32_t iChannel) +{ + VoipDVIStateT *pState = (VoipDVIStateT *)pCodecState; + int32_t iCount, iNumSamples; + int32_t iIndex; + int32_t iInput; + int32_t iEstimate; + int32_t iStepIndex; + int32_t *pBeg = pOut; + unsigned char Stage[VOIPDVI_FRAME_SIZE]; + DVICompStateT DecodeState; + + // first four bytes of input are DVI state data + ds_memcpy(&DecodeState, pInp, sizeof(DecodeState)); + pInp += sizeof(DecodeState); + iInputBytes -= sizeof(DecodeState); + + if ((iInputBytes % 3) != 0) + { + NetPrintf(("voipdvi: dvi decoder can only decode multiples of 3 bytes (%d submitted)\n", iInputBytes)); + return(0); + } + + // read state + iEstimate = DecodeState.iEstimate; + iStepIndex = DecodeState.iStepIndex; + + // calculate number of output samples based on the input data size + iNumSamples = (iInputBytes * VOIPDVI_FRAME_SIZE)/3; + + // loop based on decoded data size + for (iCount = 0; iCount < iNumSamples; ++iCount) + { + // see if we need to fetch data + iIndex = iCount & 7; + if (iIndex == 0) + { + Stage[0] = pInp[0]; + Stage[1] = pInp[0]>>3; + Stage[2] = (pInp[0]>>6) | (pInp[1]<<2); + Stage[3] = pInp[1]>>1; + Stage[4] = pInp[1]>>4; + Stage[5] = (pInp[1]>>7) | (pInp[2]<<1); + Stage[6] = pInp[2]>>2; + Stage[7] = pInp[2]>>5; + pInp += 3; + } + + // get input byte + iInput = (Stage[iIndex] & 7); + // decode to delta and add to previous estimate + iEstimate += _DeltaDecode3(_StepTable3[iStepIndex], iInput); + CLIP(iEstimate, -MAX_RANGE, MAX_RANGE); + // update the index based on delta direction + iStepIndex += _StepIndex3[iInput & 3]; + CLIP(iStepIndex, 0, _StepLimit3-1); + // scale the volume using the output level and save to output buffer + *pOut++ += ((iEstimate * pState->iOutputVolume) >> VOIP_CODEC_OUTPUT_FRACTIONAL) * DIV_RANGE; + } + + // return the length + return(pOut - pBeg); +} + +/*F********************************************************************************/ +/*! + \Function _VoipDVIReset + + \Description + Reset DVI Encoder state. + + \Input *pCodecState - pointer to DVI module + + \Version 08/05/2004 +*/ +/********************************************************************************F*/ +static void _VoipDVIReset(VoipCodecRefT *pCodecState) +{ + VoipDVIStateT *pState = (VoipDVIStateT *)pCodecState; + ds_memclr(&pState->EncodeState, sizeof(pState->EncodeState)); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipDVIControl + + \Description + Modifies parameters of the codec + + \Input *pCodecState - pointer to decode state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'plvl' - Set the output power level + \endverbatim + + \Version 03/12/2008 (grouse) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipDVIControl(VoipCodecRefT *pCodecState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + VoipDVIStateT *pState = (VoipDVIStateT *)pCodecState; + + if (iControl == 'plvl') + { + pState->iOutputVolume = iValue; + return(0); + } + + NetPrintf(("voipdvi: unhandled control selector '%C'\n", iControl)); + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipDVIStatus + + \Description + Get codec status + + \Input *pCodecState - pointer to decode state + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuffer - [out] storage for selector output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'fsiz' - size of encoder output / decoder input in bytes (iValue=samples per frame) + \endverbatim + + \Version 10/11/2011 (jbrookes) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipDVIStatus(VoipCodecRefT *pCodecState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + VoipDVIStateT *pState = (VoipDVIStateT *)pCodecState; + + // these options require module state + if (pState != NULL) + { + if (iSelect == 'fsiz') + { + // 3 bits per sample + 4 bytes of overhead per frame + return(((iValue*3)/VOIPDVI_FRAME_SIZE) + sizeof(DVICompStateT)); + } + } + NetPrintfVerbose((pState->CodecState.iDebugLevel, 1, "voipdvi: unhandled status selector '%C'\n", iSelect)); + return(-1); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipdvi.h b/r5dev/thirdparty/dirtysdk/source/voip/voipdvi.h new file mode 100644 index 00000000..56d81d08 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipdvi.h @@ -0,0 +1,43 @@ +/*H********************************************************************************/ +/*! + \File voipdvi.h + + \Description + Table based 16:3 ADPCM compression originally based off EAC SIMEX code, + modified by Greg Schaefer. + + \Copyright + Copyright (c) Electronic Arts 2003-2004. ALL RIGHTS RESERVED. + + \Version 1.0 11/01/2002 (ischmidt) First version (based on SIMEX by Dave Mercier) + \Version 2.0 05/13/2003 (gschaefer) Rewrite to 16:3 (from 16:4) +*/ +/********************************************************************************H*/ + +#ifndef _voipdvi_h +#define _voipdvi_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/voip/voipcodec.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +extern const VoipCodecDefT VoipDVI_CodecDef; + +#if defined(__cplusplus) +}; +#endif + +/*** Functions ********************************************************************/ + +#endif // _voipdvi_h diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipgroup.c b/r5dev/thirdparty/dirtysdk/source/voip/voipgroup.c new file mode 100644 index 00000000..9bbbd3c7 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipgroup.c @@ -0,0 +1,2075 @@ +/*H********************************************************************************/ +/*! + \File voipgroup.c + + \Description + A module that handles logical grouping of voip connections. Each module that wants to deal + with a block of voip connections uses a voipgroup. This reduces the singleton nature + of voip.h + + \Copyright + Copyright (c) 2007 Electronic Arts Inc. + + \Version 12/18/07 (jrainy) +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +// dirtysock includes +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +// voip includes +#include "DirtySDK/voip/voip.h" +#include "DirtySDK/voip/voipgroup.h" +#include "voippriv.h" +#include "voipcommon.h" + +/*** Defines **********************************************************************/ + +#define MAX_CLIENTS_PER_GROUP (32) + +#define MAX_REF_PER_LOW_LEVEL_CONNS (8) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! high-level connection state +typedef enum VoipGroupConnStateE +{ + VOIPGROUP_CONNSTATE_ACTIVE, //iMaxGroups; iGroup++) + { + if ((pManager->aGroups[iGroup].bUsed) && (pManager->aGroups[iGroup].pGroupCallback)) + { + if (eCbType == VOIP_CBTYPE_FROMEVENT) + { + iNewValue = 0; + for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + iMappedConnId = _VoipGroupMatch(&pManager->aGroups[iGroup], iConnId); + if (iMappedConnId != VOIP_CONNID_NONE) + { + // lookup bit indexed by mapped value, and set the bit before mapping. + iNewValue |= (((iValue & (1 << iMappedConnId)) ? 1 : 0) << iConnId); + } + } + } + else + { + iNewValue = iValue; + } + + pManager->aGroups[iGroup].pGroupCallback(pVoip, eCbType, iNewValue, pManager->aGroups[iGroup].pGroupCallbackData); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupManagerDisplayTranscribedTextCallback + + \Description + Callback registered with low-level voip transcribed text callback + + \Input iLowLevelConnId - connection index (-1 if originator is local) + \Input iUserIndex - index of local/remote user from which the transcribed text is originated + \Input *pStrUtf8 - received transcribed text + \Input *pUserData - pointer to the VoipGroupManager + + \Output + int32_t - TRUE + + \Version 05/03/2017 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _VoipGroupManagerDisplayTranscribedTextCallback(int32_t iLowLevelConnId, int32_t iUserIndex, const char *pStrUtf8, void *pUserData) +{ + VoipGroupManagerT *pManager = (VoipGroupManagerT *)pUserData; + int32_t iGroup; + uint8_t bDisplayed; + + for (iGroup = 0, bDisplayed = FALSE; (iGroup < pManager->iMaxGroups) && !bDisplayed; iGroup++) + { + if ((pManager->aGroups[iGroup].bUsed) && (pManager->aGroups[iGroup].pDisplayTranscribedTextCb)) + { + if (iLowLevelConnId != -1) + { + int32_t iHighLevelConnId; + + for (iHighLevelConnId = 0; iHighLevelConnId < MAX_CLIENTS_PER_GROUP; iHighLevelConnId++) + { + // is the entry valid and is it associated with the low level conn id we are interested in + if ((pManager->aGroups[iGroup].aConnections[iHighLevelConnId].uClientId != 0) && + (pManager->aGroups[iGroup].aConnections[iHighLevelConnId].iMappedLowLevelConnId == iLowLevelConnId)) + { + bDisplayed = pManager->aGroups[iGroup].pDisplayTranscribedTextCb(iHighLevelConnId, iUserIndex, pStrUtf8, pManager->aGroups[iGroup].pDisplayTranscribedTextUserData); + break; + } + } + } + else + { + // display transcribed text from local user + bDisplayed = pManager->aGroups[iGroup].pDisplayTranscribedTextCb(-1, iUserIndex, pStrUtf8, pManager->aGroups[iGroup].pDisplayTranscribedTextUserData); + } + } + } + return(TRUE); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupManagerCreate + + \Description + Creates the single VoipGroupManager. + + \Input iMaxGroups - the maximum number of voip groups to allocate + + \Output + VoipGroupManagerT * - The VoipGroup Manager + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +static VoipGroupManagerT *_VoipGroupManagerCreate(int8_t iMaxGroups) +{ + if (_VoipGroupManager_pRef == NULL) + { + int32_t iMemGroup, iMemSize; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // calculate size of state + iMemSize = sizeof(*_VoipGroupManager_pRef) + (sizeof(_VoipGroupManager_pRef->aGroups[0]) * (iMaxGroups - 1)); + + if ((_VoipGroupManager_pRef = DirtyMemAlloc(iMemSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipgroup: could not allocate module state\n")); + return(NULL); + } + ds_memclr(_VoipGroupManager_pRef, iMemSize); + + _VoipGroupManager_pRef->iMaxGroups = iMaxGroups; + _VoipGroupManager_pRef->iMemGroup = iMemGroup; + _VoipGroupManager_pRef->pMemGroupUserData = pMemGroupUserData; + + // register callback with low-level VOIP module if it exists + if (VoipGetRef() != NULL) + { + VoipCommonSetEventCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerEventCallback, _VoipGroupManager_pRef); + VoipCommonSetDisplayTranscribedTextCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerDisplayTranscribedTextCallback, _VoipGroupManager_pRef); + } + } + + return(_VoipGroupManager_pRef); +} + + + +/*F********************************************************************************/ +/*! + \Function _VoipGroupFindFreeHighLevelConn + + \Description + Find a free high level connection spot in the given voipgroup. + + \Input *pVoipGroup - the voipgroup to inspect for a free connection spot + + \Output + int32_t - failure: VOIP_CONNID_NONE, success: conn id of high-level conn found + + \Version 10/04/2010 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _VoipGroupFindFreeHighLevelConn(VoipGroupRefT *pVoipGroup) +{ + int32_t iConnId; + + for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + if (pVoipGroup->aConnections[iConnId].uClientId == 0) + { + break; + } + } + + if (iConnId == MAX_CLIENTS_PER_GROUP) + { + NetPrintf(("voipgroup: [%p] warning - voipgroup is full\n", pVoipGroup)); + return(VOIP_CONNID_NONE); + } + + return(iConnId); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupFindUsedHighLevelConn + + \Description + In the given voipgroiup, find the high level connection associated with + specified client id. + + \Input *pVoipGroup - the voipgroup to inspect for a free connection spot + \Input uClientId - client id to look for + + \Output + int32_t - failure: VOIP_CONNID_NONE, success: conn id of high-level conn found + + \Version 10/04/2010 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _VoipGroupFindUsedHighLevelConn(VoipGroupRefT *pVoipGroup, uint32_t uClientId) +{ + int32_t iConnId; + + for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) + { + break; + } + } + + if (iConnId == MAX_CLIENTS_PER_GROUP) + { + return(VOIP_CONNID_NONE); + } + + return(iConnId); +} + + +/*F********************************************************************************/ +/*! + \Function _VoipGroupLowLevelConnInUse + + \Description + Tests whether a voip-level ConnId is in use. + + \Input iConnId - voip-level ConnId + + \Output + uint8_t - TRUE or FALSE + + \Version 12/02/2009 (jrainy) +*/ +/********************************************************************************F*/ +static uint8_t _VoipGroupLowLevelConnInUse(int32_t iConnId) +{ + return(_VoipGroupManager_pRef->aLowLevelConnReferences[iConnId][0] != NULL); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupManagerGetGroup + + \Description + Allocates a new VoipGroup for the given manager + + \Input *pVoipGroupManager - The manager to allocate the group from + + \Output + VoipGroupRefT* - The allocated VoipGroup + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +static VoipGroupRefT* _VoipGroupManagerGetGroup(VoipGroupManagerT *pVoipGroupManager) +{ + int32_t iIndex; + + if (pVoipGroupManager->iNumGroups < pVoipGroupManager->iMaxGroups) + { + for (iIndex = 0; (iIndex < pVoipGroupManager->iMaxGroups); iIndex++) + { + if (!pVoipGroupManager->aGroups[iIndex].bUsed) + { + return(&pVoipGroupManager->aGroups[iIndex]); + } + } + } + + NetPrintf(("voipgroup: no voipgroup available\n")); + return(NULL); +} + +#if DIRTYCODE_LOGGING +/*F********************************************************************************/ +/*! + \Function _VoipGroupPrintRefsToLowLevelConn + + \Description + Prints all references to a given low-level voip connection + + \Input iLowLevelConnId - id of low-level connection + + \Version 11/12/2009 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipGroupPrintRefsToLowLevelConn(int32_t iLowLevelConnId) +{ + int32_t iRefIndex; + VoipGroupManagerT *pManager = _VoipGroupManager_pRef; + + if (_VoipGroupLowLevelConnInUse(iLowLevelConnId)) + { + NetPrintf(("voipgroup: references to low-level voip conn %d are: ", iLowLevelConnId)); + for (iRefIndex = 0; iRefIndex < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex++) + { + if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] != NULL) + { + NetPrintf(("%p ", pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex])); + } + else + { + break; + } + } + NetPrintf(("\n")); + } + else + { + NetPrintf(("voipgroup: there is no reference to low-level voip conn %d\n", iLowLevelConnId)); + } +} +#endif + +/*F********************************************************************************/ +/*! + \Function _VoipGroupManagerShutdown + + \Description + free the singleton VoipGroupManager. + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +static void _VoipGroupManagerShutdown(void) +{ + if (_VoipGroupManager_pRef != NULL) + { + // remove callback registered with low-level VOIP module + if (VoipGetRef() != NULL) + { + VoipCommonSetEventCallback((VoipCommonRefT *)VoipGetRef(), NULL, NULL); + VoipCommonSetDisplayTranscribedTextCallback((VoipCommonRefT *)VoipGetRef(), NULL, NULL); + } + + DirtyMemFree(_VoipGroupManager_pRef, VOIP_MEMID, _VoipGroupManager_pRef->iMemGroup, _VoipGroupManager_pRef->pMemGroupUserData); + _VoipGroupManager_pRef = NULL; + + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupManagerReleaseGroup + + \Description + Frees a VoipGroup for the given manager + + \Input *pManager - The manager to allocate the group from + \Input *pVoipGroup - The VoipGroup to free + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +static void _VoipGroupManagerReleaseGroup(VoipGroupManagerT *pManager, VoipGroupRefT* pVoipGroup) +{ + int32_t iSlot; + int32_t iUser; + + NetPrintf(("voipgroup: [%p] remove all connections and clear all mappings\n", pVoipGroup)); + + // remove all connections + for(iSlot = 0; iSlot < MAX_CLIENTS_PER_GROUP; iSlot++) + { + if (pVoipGroup->aConnections[iSlot].uClientId != 0) + { + VoipGroupDisconnect(pVoipGroup, iSlot); + } + } + + // deactivate active refs + for(iUser = 0; iUser < VOIP_MAXLOCALUSERS; iUser++) + { + if (pVoipGroup->aParticipatingUser[iUser] == TRUE) + { + VoipGroupActivateLocalUser(pVoipGroup, iUser, FALSE); + } + } + + // clear voipgroup entry + ds_memclr(pVoipGroup, sizeof(VoipGroupRefT)); + + // shutdown the voipgroup manager if there are no groups left + if ((--pManager->iNumGroups) == 0) + { + _VoipGroupManagerShutdown(); + } + + return; +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupMatch + + \Description + Converts between VoipGroup ID and Voip ID. + + \Input *pVoipGroup - The VoipGroup the iConnId indexes in + \Input iConnId - The VoipGroup ID + + \Output + int32_t - The corresponding Voip ID + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +static int32_t _VoipGroupMatch(VoipGroupRefT *pVoipGroup, int32_t iConnId) +{ + int32_t iIndex, iConn, iRetVal = VOIP_CONNID_NONE; + uint32_t uClientId; + VoipGroupManagerT *pManager = _VoipGroupManager_pRef; + + if ((iConnId < 0) || (iConnId >= MAX_CLIENTS_PER_GROUP)) + { + return(VOIP_CONNID_NONE); + } + + uClientId = pVoipGroup->aConnections[iConnId].uClientId; + if (uClientId == 0) + { + return(VOIP_CONNID_NONE); + } + + // if this connection is suspended look for the active group + // for this iConnId to get the proper low level conn id + if (pVoipGroup->aConnections[iConnId].eConnState == VOIPGROUP_CONNSTATE_SUSPENDED) + { + for (iIndex = 0; iIndex < pManager->iMaxGroups; iIndex++) + { + for (iConn = 0; iConn < MAX_CLIENTS_PER_GROUP; iConn++) + { + if ( (pManager->aGroups[iIndex].aConnections[iConn].uClientId == uClientId) && + (pManager->aGroups[iIndex].aConnections[iConn].eConnState == VOIPGROUP_CONNSTATE_ACTIVE) ) + { + iRetVal = pManager->aGroups[iIndex].aConnections[iConn].iMappedLowLevelConnId; + break; + } + } + } + } + else + { + iRetVal = pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId; + } + + return(iRetVal); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupFindCollision + + \Description + Looks for another group with a low-level connection to a given client id + + \Input *pVoipGroup - pointer to voipgroup looking for a colliding voipgroup + \Input uClientId - the client id to match + \Input eConnState - connection state to match + \Input *pCollidingConnId - output parameter to be filled with id of colliding connection if applicable (invalid if return value is NULL) + + \Output + VoipGroupRefT* - group reference if found, NULL if no collision + + \Version 011/11/2009 (mclouatre) +*/ +/********************************************************************************F*/ +static VoipGroupRefT* _VoipGroupFindCollision(VoipGroupRefT *pVoipGroup, uint32_t uClientId, VoipGroupConnStateE eConnState, int32_t *pCollidingConnId) +{ + int32_t iIndex, iConnId; + VoipGroupManagerT *pManager = _VoipGroupManager_pRef; + + for (iIndex = 0; iIndex < pManager->iMaxGroups; iIndex++) + { + for (iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + if ((pManager->aGroups[iIndex].aConnections[iConnId].uClientId == uClientId) && + (pManager->aGroups[iIndex].aConnections[iConnId].eConnState == eConnState)) + { + // do not allow a voipgroup to collide with itself + if (&pManager->aGroups[iIndex] == pVoipGroup) + { + continue; + } + + NetPrintf(("voipgroup: [%p] collision found with voipgroup %p for client id 0x%08x\n", pVoipGroup, &pManager->aGroups[iIndex], uClientId)); + *pCollidingConnId = iConnId; + return(&pManager->aGroups[iIndex]); + } + } + } + + *pCollidingConnId = VOIP_CONNID_NONE; + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupAddRefToLowLevelConn + + \Description + Add a reference to a low level connection + + \Input iLowLevelConnId - low level conn id + \Input *pVoipGroup - voipgroup to be added as a reference to the low-level connection + + \Output + int32_t - 0 for success, -1 for failure + + \Version 11/12/2009 (mclouatre) +*/ +/********************************************************************************F*/ +static int32_t _VoipGroupAddRefToLowLevelConn(int32_t iLowLevelConnId, VoipGroupRefT * pVoipGroup) +{ + int32_t iRefIndex; + int32_t iRetCode = 0; // default to succes + VoipGroupManagerT *pManager = _VoipGroupManager_pRef; + + for (iRefIndex = 0; iRefIndex < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex++) + { + // if this ref already is in the table, fake a failure + if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] == pVoipGroup) + { + iRetCode = -1; + NetPrintf(("voipgroup: [%p] error - current voipgroup already exists as a reference for low-level voip conn %d\n", pVoipGroup, iLowLevelConnId)); + break; + } + + // look for a free spot to find our ref + if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] == NULL) + { + pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] = pVoipGroup; + NetPrintf(("voipgroup: [%p] added current voipgroup as a reference for low-level voip conn %d\n", pVoipGroup, iLowLevelConnId)); + break; + } + } + + if (iRefIndex == MAX_REF_PER_LOW_LEVEL_CONNS) + { + iRetCode = -1; + + NetPrintf(("voipgroup: [%p] critical error - cannot add current voipgroup as a reference for low-level conn %d, max number of references exceeded\n", + pVoipGroup, iLowLevelConnId)); + } + + #if DIRTYCODE_LOGGING + _VoipGroupPrintRefsToLowLevelConn(iLowLevelConnId); + #endif + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function _VoipGroupRemoveRefToLowLevelConn + + \Description + Remove a reference to a low level connection + + \Input iLowLevelConnId - low level conn id + \Input *pVoipGroup - voipgroup to be added as a reference to the low-level connection + + \Version 11/12/2009 (mclouatre) +*/ +/********************************************************************************F*/ +static void _VoipGroupRemoveRefToLowLevelConn(int32_t iLowLevelConnId, VoipGroupRefT * pVoipGroup) +{ + int32_t iRefIndex; + VoipGroupManagerT *pManager = _VoipGroupManager_pRef; + + for(iRefIndex = 0; iRefIndex < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex++) + { + if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] == pVoipGroup) + { + int32_t iRefIndex2; + + // move all following references one cell backward in the array + for(iRefIndex2 = iRefIndex; iRefIndex2 < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex2++) + { + if (iRefIndex2 == MAX_REF_PER_LOW_LEVEL_CONNS-1) + { + // last entry, reset to NULL + pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex2] = NULL; + } + else + { + pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex2] = pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex2+1]; + } + } + + NetPrintf(("voipgroup: [%p] current voipgroup no more referencing low-level voip conn %d\n", pVoipGroup, iLowLevelConnId)); + #if DIRTYCODE_LOGGING + _VoipGroupPrintRefsToLowLevelConn(iLowLevelConnId); + #endif + + break; + } + } + + if (iRefIndex == MAX_REF_PER_LOW_LEVEL_CONNS) + { + NetPrintf(("voipgroup: [%p] warning - current voipgroup not found as a valid reference for low-level conn %d\n", pVoipGroup, iLowLevelConnId)); + } +} + +/*** Public Functions *************************************************************/ + +/*F********************************************************************************/ +/*! + \Function VoipGroupCreate + + \Description + allocates a VoipGroup + + \Input iMaxGroups - the maximum number of groups we can have active at one time + + \Output + VoipGroupRefT* - Allocated VoipGroup + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +VoipGroupRefT* VoipGroupCreate(int8_t iMaxGroups) +{ + VoipGroupManagerT *pManager = _VoipGroupManagerCreate(iMaxGroups); + VoipGroupRefT *pNewlyCreated; + + if ((pNewlyCreated = _VoipGroupManagerGetGroup(pManager)) != NULL) + { + pManager->iNumGroups++; + pNewlyCreated->bUsed = TRUE; + } + + return(pNewlyCreated); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupDestroy + + \Description + Deallocates a VoipGroup + + \Input *pVoipGroup - VoipGroup to Deallocate + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +void VoipGroupDestroy(VoipGroupRefT *pVoipGroup) +{ + _VoipGroupManagerReleaseGroup(_VoipGroupManager_pRef, pVoipGroup); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupSetEventCallback + + \Description + Sets the callback and userdata for the specified group + + \Input *pVoipGroup - VoipGroup to use + \Input *pCallback - pointer to the callback to use + \Input *pUserData - pointer to the user specified data + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +void VoipGroupSetEventCallback(VoipGroupRefT *pVoipGroup, VoipCallbackT *pCallback, void *pUserData) +{ + pVoipGroup->pGroupCallback = pCallback; + pVoipGroup->pGroupCallbackData = pUserData; + + VoipCommonSetEventCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerEventCallback, _VoipGroupManager_pRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupSetDisplayTranscribedTextCallback + + \Description + Sets the callback and userdata for the specified group + + \Input *pVoipGroup - VoipGroup to use + \Input *pCallback - pointer to the callback to use + \Input *pUserData - pointer to the user specified data + + \Version 05/03/2017 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipGroupSetDisplayTranscribedTextCallback(VoipGroupRefT *pVoipGroup, VoipDisplayTranscribedTextCallbackT *pCallback, void *pUserData) +{ + + pVoipGroup->pDisplayTranscribedTextCb = pCallback; + pVoipGroup->pDisplayTranscribedTextUserData = pUserData; + + VoipCommonSetDisplayTranscribedTextCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerDisplayTranscribedTextCallback, _VoipGroupManager_pRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupSetConnSharingEventCallback + + \Description + Sets the connection sharing callback and userdata for the specified group + + \Input *pVoipGroup - voipgroup ref + \Input *pCallback - pointer to the callback to use + \Input *pUserData - pointer to the user specified data + + \Notes + The callback registered with this function must invoke VoipGroupSuspend() + when eCbType=VOIPGROUP_CBTYPE_CONNSUSPEND, and it must call VoipGroupResume() + when eCbType=VOIPGROUP_CBTYPE_CONNRESUME. + + \Version 11/11/2009 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipGroupSetConnSharingEventCallback(VoipGroupRefT *pVoipGroup, ConnSharingCallbackT *pCallback, void *pUserData) +{ + pVoipGroup->pConnSharingCallback = pCallback; + pVoipGroup->pConnSharingCallbackData = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupConnect + + \Description + Set up a connection, using VoipGroup + + \Input *pVoipGroup - The VoipGroup the flags apply to + \Input iConnId - connection index + \Input uAddress - address to connect to + \Input uManglePort - port to connect on + \Input uGamePort - port to connect on + \Input uClientId - client identifier + \Input uSessionId - session identifier (optional) + \Input bIsConnectivityHosted - is the connection through a relay server + \Input uLowLevelConnectivityId - low level remote client id + + \Output + int32_t - the VoipGroup connid the connection was made with + + \Version 11/03/2015 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t VoipGroupConnect(VoipGroupRefT *pVoipGroup, int32_t iConnId, uint32_t uAddress, uint32_t uManglePort, uint32_t uGamePort, uint32_t uClientId, uint32_t uSessionId, uint8_t bIsConnectivityHosted, uint32_t uLowLevelConnectivityId) +{ + int32_t iMappedLowLevelConnId = VOIP_CONNID_NONE; + int32_t iHighLevelConnIdInUse = VOIP_CONNID_NONE; + int32_t iCollidingConnId; + VoipGroupRefT *pCollidingVoipGroup; + uint8_t bMuted = FALSE; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + NetPrintf(("voipgroup: [%p] connecting high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, uSessionId)); + + // is the specified client id already known by the current voipgroup? + // note: typically this occurs when the caller perform successive calls to VoipGroupConnect() while + // trying different port obtained from the demangler. + if ((iHighLevelConnIdInUse = _VoipGroupFindUsedHighLevelConn(pVoipGroup, uClientId)) != VOIP_CONNID_NONE) + { + if (iConnId == VOIP_CONNID_NONE) + { + iConnId = iHighLevelConnIdInUse; + } + + if (iHighLevelConnIdInUse == iConnId) + { + NetPrintf(("voipgroup: [%p] current voipgroup already knows about client id 0x%08x (low level id 0x%08x) with high-level conn id %d\n", + pVoipGroup, uClientId, uLowLevelConnectivityId, iConnId)); + + // if connection is "suspended", early exit faking a success + if (pVoipGroup->aConnections[iConnId].eConnState == VOIPGROUP_CONNSTATE_SUSPENDED) + { + NetPrintf(("voipgroup: [%p] high-level conn %d (sess id 0x%08x) is suspended, assume low-level connectivity is up\n", pVoipGroup, iConnId, pVoipGroup->aConnections[iConnId].uSessionId)); + return(iConnId); + } + } + else + { + NetPrintf(("voipgroup: [%p] critical error - current voipgroup already knows about client id x%08x (low level id 0x%08x) but conn ids do not match (%d != %d)\n", + pVoipGroup, uClientId, uLowLevelConnectivityId, iHighLevelConnIdInUse, iConnId)); + return(-4); + } + } + + // if the user doesn't specify a conn id, use the first unmapped one. + if (iConnId == VOIP_CONNID_NONE) + { + if ((iConnId = _VoipGroupFindFreeHighLevelConn(pVoipGroup)) == VOIP_CONNID_NONE) + { + NetPrintf(("voipgroup: [%p] warning - voipgroup is full\n", pVoipGroup)); + return(-1); + } + } + + // check for collision: verify if another voipgroup deals with the same client + if ( (pCollidingVoipGroup = _VoipGroupFindCollision(pVoipGroup, uClientId, VOIPGROUP_CONNSTATE_ACTIVE, &iCollidingConnId)) != NULL ) + { + // if there is a collision check the mute status of the colliding voipgroup's on the connId so we can reset the status if we suspend + bMuted = VoipGroupIsMutedByConnId(pCollidingVoipGroup, iCollidingConnId); + + /* If we are re-establishing voip from a hosted voip conn to a p2p voip conn, and if we know that the original + hosted voip conn was resulting from a failing p2p voip conn, then we know this new targeted p2p voip conn will fail. + Consequently, to avoid an unnecessary 10-sec p2p connection handshaking delay (which results in novoip between the users during that + time because the original hosted voip connection gets suspended), we just return an error. The assumption being that blaze, upon + detecting the connection failure, will immediately resume connectivity but hosted this time. */ + if ((pCollidingVoipGroup->iCcMode == VOIPGROUP_CCMODE_HOSTEDFALLBACK) && pCollidingVoipGroup->aConnections[iCollidingConnId].bIsConnectivityHosted && !bIsConnectivityHosted) + { + NetPrintf(("voipgroup: [%p] failing VoipGroupConnect() because we anticipate the target p2p conn to fail... we instead prefer waiting for hosted conn to hosted conn transition\n", pVoipGroup)); + return(-2); + } + + // if both voipgroups do not share same bServer / bTunnel attributes + // OR if the previous or current connection is connectivity hosted, + // then connection re-establishment kicks-in + if ( (pCollidingVoipGroup->bServer != pVoipGroup->bServer) || + (pCollidingVoipGroup->bTunnel != pVoipGroup->bTunnel) || + bIsConnectivityHosted || + pCollidingVoipGroup->aConnections[iCollidingConnId].bIsConnectivityHosted) + { + /* + Note: + Voipgroups with bServer attribute set have precedence over voipgroups with bServer attribute not set, + i.e. voip connectivity via voipserver is always preferred over p2p voip connectivity. Therefore, a newly + created voip connection goes straight to "suspended state" if there already exists a voip connection + via the voipserver for the same client. Or, if the newly created voip connection is to be established + through the voipserver, then any existing p2p voip connection to the same client is first moved to + "suspended" state, and only then the voip connection through the voipserver is established. + */ + + // is the colliding voipgroup already using the voipserver? + // note: bServer will never be the true for connectivity hosted connection + if (pCollidingVoipGroup->bServer) + { + // no need to proceed with current connection establishment because the colliding voipgroup + // already has voip connectivity via the voipserver, so it has precedence. just mark current + // connection as "SUSPENDED" such that it can be resumed later if needed. + pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_SUSPENDED; + pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = VOIP_CONNID_NONE; + pVoipGroup->aConnections[iConnId].uClientId = uClientId; + pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId = uLowLevelConnectivityId; + pVoipGroup->aConnections[iConnId].uSessionId = uSessionId; + pVoipGroup->aConnections[iConnId].bIsConnectivityHosted = bIsConnectivityHosted; + + NetPrintf(("voipgroup: [%p] early suspension of high-level conn %d (sess id 0x%08x) - there already exists a low-level voip conn through the voipserver for client id 0x%08x (low level id 0x%08x).\n", + pVoipGroup, iConnId, uSessionId, uClientId, uLowLevelConnectivityId)); + return(iConnId); // fake success + } + else + { + // apply crossplay flag + VoipCommonControl(pVoipCommon, 'xply', pVoipGroup->bCrossplay, NULL); + + // let the colliding voipgroup entity know that it has to suspend its connection right away + // because the current voipgroup will be creating a new one with new connection parameters + pCollidingVoipGroup->pConnSharingCallback(pCollidingVoipGroup, VOIPGROUP_CBTYPE_CONNSUSPEND, + iCollidingConnId, pCollidingVoipGroup->pConnSharingCallbackData, FALSE, FALSE); + } + } + else + { + // both voipgroups can share the underlying low-level voip connection + iMappedLowLevelConnId = pCollidingVoipGroup->aConnections[iCollidingConnId].iMappedLowLevelConnId; + + // add session ID to the set of sessions sharing the voip connection + VoipCommonConnectionSharingAddSession(pVoipCommon, iMappedLowLevelConnId, uSessionId); + } + } + + if (iMappedLowLevelConnId < 0) + { + // apply crossplay flag + VoipCommonControl(pVoipCommon, 'xply', pVoipGroup->bCrossplay, NULL); + + // only call VoipCommonConnect() with a valid conn id if any of the following two condidions is met: + // 1- VoipCommonConnect() was already called for that low-level connection (i.e iHighLevelConnIdInUse is valid) + // 2- The high-level conn id is directly available in voip and there is currently no reference to it + // (there is indeed a small transition time during which a connection may go to DISC state and a voip goup still refers to it) + if ( (iHighLevelConnIdInUse != VOIP_CONNID_NONE) || + (VoipStatus(VoipGetRef(), 'avlb', iConnId, NULL, 0) && !_VoipGroupLowLevelConnInUse(iConnId)) ) + { + // conn id is available, use it! + iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, iConnId, uAddress, uManglePort, uGamePort, uLowLevelConnectivityId, uSessionId); + } + else + { + // else, let the voip module select a low-level voip conn id for us. + iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, VOIP_CONNID_NONE, uAddress, uManglePort, uGamePort, uLowLevelConnectivityId, uSessionId); + } + } + + if (iMappedLowLevelConnId < 0) + { + NetPrintf(("voipgroup: [%p] VoipGroupConnect() failed obtaining a valid low-level conn id\n", pVoipGroup)); + return(-3); + } + + NetPrintf(("voipgroup: [%p] high-level conn id %d (sess id 0x%08x) mapped to low-level conn id %d --> address %a, clientId=0x%08x (low level id 0x%08x).\n", + pVoipGroup, iConnId, uSessionId, iMappedLowLevelConnId, uAddress, uClientId, uLowLevelConnectivityId)); + + // set the local client id for the mapping + VoipCommonSetLocalClientId(pVoipCommon, iMappedLowLevelConnId, pVoipGroup->aLowLevelConnectivityId[iConnId]); + + // save the mapping. + pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = iMappedLowLevelConnId; + if (pVoipGroup->bServer == TRUE || bIsConnectivityHosted == TRUE) + { + // let the voip module know about the local-to-voipserver conn id mapping + VoipCommonMapVoipServerId(pVoipCommon, pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId, iConnId); + } + + // save other connection-specific data + pVoipGroup->aConnections[iConnId].uClientId = uClientId; + pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId = uLowLevelConnectivityId; + pVoipGroup->aConnections[iConnId].uSessionId = uSessionId; + pVoipGroup->aConnections[iConnId].bIsConnectivityHosted = bIsConnectivityHosted; + + // if this is the first attempt to connect, let's just add ourself as an additional reference for this connection + if ((iHighLevelConnIdInUse == VOIP_CONNID_NONE) && (_VoipGroupAddRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup) < 0)) + { + NetPrintf(("voipgroup: [%p] VoipGroupConnect() failed to add a new reference to the low-level conn %d\n", pVoipGroup, iMappedLowLevelConnId)); + VoipGroupDisconnect(pVoipGroup, iConnId); + return(-4); + } + + // don't initialize the user masks if there was a collision since they'll already be set + if (!bMuted) + { + _VoipGroupManager_pRef->uUserRecvMask |= (1 << iMappedLowLevelConnId); + VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserRecvMask); + _VoipGroupManager_pRef->uUserSendMask |= (1 << iMappedLowLevelConnId); + VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserSendMask); + } + + // return the high-level connection id used + return(iConnId); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupResume + + \Description + resume a connection + + \Input *pVoipGroup - voipgroup ref + \Input iConnId - connection index + \Input uAddress - address to connect to + \Input uManglePort - port to connect on + \Input uGamePort - port to connect on + \Input uClientId - client identifier + \Input uSessionId - session identifier (optional) + \Input bIsConnectivityHosted - TRUE is connection is connectivity hosted + + \Notes + Muting logic is intentionally excluded from the resume. It is currently + handled by ConnApi. + + \Version 01/26/2016 (amakoukji) +*/ +/********************************************************************************F*/ +void VoipGroupResume(VoipGroupRefT *pVoipGroup, int32_t iConnId, uint32_t uAddress, uint32_t uManglePort, uint32_t uGamePort, uint32_t uClientId, uint32_t uSessionId, uint8_t bIsConnectivityHosted) +{ + int32_t iMappedLowLevelConnId = -1; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + NetPrintf(("voipgroup: [%p] resuming high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, uSessionId)); + + #if DIRTYCODE_LOGGING + if (pVoipGroup->aConnections[iConnId].uClientId != uClientId) + { + NetPrintf(("voipgroup: [%p] warning connection being resumed has inconsistent client id: 0x%08x should be 0x%08x\n", + pVoipGroup, uClientId, pVoipGroup->aConnections[iConnId].uClientId)); + } + if (pVoipGroup->aConnections[iConnId].uSessionId != uSessionId) + { + NetPrintf(("voipgroup: [%p] warning connection being resumed has inconsistent session id: 0x%08x should be 0x%08x\n", + pVoipGroup, uSessionId, pVoipGroup->aConnections[iConnId].uSessionId)); + } + #endif + + // force connection back to active state + pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_ACTIVE; + + // check if the high-level conn id is directly available in voip and if there is currently no reference to it + // (there is indeed a small transition time during which a connection may go to DISC state and a voip goup still refers to it) + if (VoipStatus(VoipGetRef(), 'avlb', iConnId, NULL, 0) && !_VoipGroupLowLevelConnInUse(iConnId)) + { + // conn id is available, use it! + iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, iConnId, uAddress, uManglePort, uGamePort, pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId, uSessionId); + } + else + { + // else, let Voip allocate one. + iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, VOIP_CONNID_NONE, uAddress, uManglePort, uGamePort, pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId, uSessionId); + } + + if (iMappedLowLevelConnId < 0) + { + NetPrintf(("voipgroup: [%p] VoipGroupResume() failed obtaining a valid low-level conn id\n", pVoipGroup)); + return; + } + + NetPrintf(("voipgroup: [%p] high-level conn id %d (sess id 0x%08x) mapped to low-level conn id %d --> address %a, clientId=0x%08x (low level id 0x%08x).\n", + pVoipGroup, iConnId, uSessionId, iMappedLowLevelConnId, uAddress, uClientId, pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId)); + + // set the local client id for the mapping + VoipCommonSetLocalClientId(pVoipCommon, iMappedLowLevelConnId, pVoipGroup->aLowLevelConnectivityId[iConnId]); + + // save the mapping. + pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = iMappedLowLevelConnId; + if (pVoipGroup->bServer == TRUE || bIsConnectivityHosted == TRUE) + { + // let the voip module know about the local-to-voipserver conn id mapping + VoipCommonMapVoipServerId(pVoipCommon, pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId, iConnId); + } + + // let's just add ourself as an additional reference for this connection + if ((_VoipGroupAddRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup)) < 0) + { + NetPrintf(("voipgroup: [%p] VoipGroupResume() failed adding a new reference\n", pVoipGroup)); + return; + } + + return; +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupDisconnect + + \Description + Teardown a connection, using VoipGroups. + + \Input *pVoipGroup - The VoipGroup the flags apply to + \Input iConnId - connection index + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +void VoipGroupDisconnect(VoipGroupRefT *pVoipGroup, int32_t iConnId) +{ + int32_t iCollidingConnId; + int32_t iMappedLowLevelConnId; + VoipGroupRefT *pCollidingVoipGroup; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + NetPrintf(("voipgroup: [%p] disconnecting high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, pVoipGroup->aConnections[iConnId].uSessionId)); + + // if high-level connection is already suspended, skip disconnection and just clean up corresponding data in the group + if (pVoipGroup->aConnections[iConnId].eConnState != VOIPGROUP_CONNSTATE_SUSPENDED) + { + iMappedLowLevelConnId = _VoipGroupMatch(pVoipGroup, iConnId); + + if (iMappedLowLevelConnId != VOIP_CONNID_NONE) + { + // remove voipgroup from low-level connection's reference set + _VoipGroupRemoveRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup); + + // only proceed if the ref count for this low-level conn is 0 + if (!_VoipGroupLowLevelConnInUse(iMappedLowLevelConnId)) + { + uint8_t bMuted = VoipGroupIsMutedByConnId(pVoipGroup, iConnId); + VoipCommonDisconnect(pVoipCommon, iMappedLowLevelConnId, TRUE); + + _VoipGroupManager_pRef->uUserRecvMask &= ~(1 << iMappedLowLevelConnId); + VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserRecvMask); + _VoipGroupManager_pRef->uUserSendMask &= ~(1 << iMappedLowLevelConnId); + VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserSendMask); + + // scan other voipgroups and resume any suspended connection to the same client id + if ( (pCollidingVoipGroup = _VoipGroupFindCollision(pVoipGroup, pVoipGroup->aConnections[iConnId].uClientId, VOIPGROUP_CONNSTATE_SUSPENDED, &iCollidingConnId)) != NULL ) + { + // apply colliding voip group crossplay flag + VoipCommonControl(pVoipCommon, 'xply', pCollidingVoipGroup->bCrossplay, NULL); + + // let colliding voipgroup entity that it can resume its connection right away as the current + // voipgroup has just disconnected a connection with different connection parameters + pCollidingVoipGroup->pConnSharingCallback(pCollidingVoipGroup, VOIPGROUP_CBTYPE_CONNRESUME, + iCollidingConnId, pCollidingVoipGroup->pConnSharingCallbackData, bMuted, bMuted); + } + } + else + { + // remove session ID from the set of sessions sharing the low-level voip connection + // note: don't call this function earlier in time (before VoipCommonDisconnect) because the session id is needed for building the disconnect message + VoipCommonConnectionSharingDelSession(pVoipCommon, iMappedLowLevelConnId, pVoipGroup->aConnections[iConnId].uSessionId); + + NetPrintf(("voipgroup: [%p] skipped call to VoipCommonDisconnect() because low-level conn ref count has not reached 0\n", pVoipGroup)); + } + + pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = VOIP_CONNID_NONE; + } + else + { + NetPrintf(("voipgroup: [%p] warning - VoipGroupDisconnect() dealing with a corrupted connection entry\n", pVoipGroup)); + } + } + else + { + NetPrintf(("voipgroup: [%p] skipped call to VoipCommonDisconnect() because high-level conn %d is suspended\n", pVoipGroup, iConnId)); + } + + // free the high-level connection entry + pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_ACTIVE; + pVoipGroup->aConnections[iConnId].uClientId = 0; + pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId = 0; + pVoipGroup->aConnections[iConnId].uSessionId = 0; + pVoipGroup->aConnections[iConnId].bIsConnectivityHosted = FALSE; + + return; +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupSuspend + + \Description + suspend a connection + + \Input *pVoipGroup - voipgroup ref + \Input iConnId - connection index + + \Version 11/11/2009 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipGroupSuspend(VoipGroupRefT *pVoipGroup, int32_t iConnId) +{ + int32_t iMappedLowLevelConnId; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + NetPrintf(("voipgroup: [%p] suspending high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, pVoipGroup->aConnections[iConnId].uSessionId)); + + // force connection state back to suspended + iMappedLowLevelConnId = _VoipGroupMatch(pVoipGroup, iConnId); + + if (iMappedLowLevelConnId != VOIP_CONNID_NONE) + { + pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_SUSPENDED; + + // remove voipgroup from low-level connection's reference set + _VoipGroupRemoveRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup); + + if (_VoipGroupLowLevelConnInUse(iMappedLowLevelConnId)) + { + /* + note: + This scenario can occur if the low-level connection has already been disconnected by the other peer. + In such a case, the voipgroup is not aware of the state of the low-level connection, and it still + maintains the high-level connection entry as the corresponding connapi client is maintained in DISC + state by the upper ConnApi layer. Now when asked to go to suspended state because of a conflict with another + newly created voipgroup, it is very possible that the low-level connection has already been re-used and has + other voipgroups referring to it. + */ + NetPrintf(("voipgroup: [%p] VoipGroupSuspend() skips call to VoipCommonDisconnect() because low-level conn ref count has not reached 0\n", pVoipGroup)); + } + else + { + /* + note: + bSendDiscMsg is FALSE here because we don't want the voip disc pkt to result in the + other end of the connection having its connapi voip connection status moved to + CONNAPI_STATUS_DISC state. + */ + VoipCommonDisconnect(pVoipCommon, iMappedLowLevelConnId, FALSE); + + _VoipGroupManager_pRef->uUserRecvMask &= ~(1 << iMappedLowLevelConnId); + VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserRecvMask); + _VoipGroupManager_pRef->uUserSendMask &= ~(1 << iMappedLowLevelConnId); + VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserSendMask); + + pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = VOIP_CONNID_NONE; + } + } + else + { + NetPrintf(("voipgroup: [%p] low level conn id not found\n", pVoipGroup)); + } + + return; +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupMuteByClientId3 + + \Description + mutes a connection by the client id and mute flag + + \Input uClientId - client id + \Input bMute - TRUE to mute FALSE to umute + \Input uMuteFlag - mute flags (VOIPGROUP_INBOUND_MUTE, VOIPGROUP_OUTBOUND_MUTE, VOIPGROUP_TWOWAY_MUTE) + + \Output + int32_t - negative=error, zero=success + + \Version 10/30/2017 (amakoukji) +*/ +/********************************************************************************F*/ +int32_t VoipGroupMuteByClientId3(uint32_t uClientId, uint8_t bMute, uint32_t uMuteFlag) +{ + VoipGroupManagerT* pManager = _VoipGroupManager_pRef; + VoipGroupRefT *pVoipGroup = NULL; + int32_t iConnId = MAX_CLIENTS_PER_GROUP; + int32_t iGroupIndex; + + if (uClientId == (uint32_t)VOIP_CONNID_ALL) + { + iConnId = (int32_t)VOIP_CONNID_ALL; + } + else + { + for (iGroupIndex = 0; iGroupIndex < pManager->iMaxGroups; ++iGroupIndex) + { + pVoipGroup = &pManager->aGroups[iGroupIndex]; + + // look for the client in this voip group + if (pVoipGroup->bUsed) + { + for (iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) + { + break; + } + } + + // found a match in the inner for-loop, stop looking + if (iConnId != MAX_CLIENTS_PER_GROUP) + { + break; + } + } + } + + // if the client is not found in any voip group return an error + if ((iGroupIndex == pManager->iMaxGroups) && (iConnId == MAX_CLIENTS_PER_GROUP)) + { + return(-1); + } + } + + return(VoipGroupMuteByConnId2(pVoipGroup, iConnId, bMute, uMuteFlag)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupMuteByClientId2 + + \Description + mutes a connection by the client id and mute flag + + \Input *pVoipGroup - voipgroup ref + \Input uClientId - client id + \Input bMute - TRUE to mute FALSE to umute + \Input uMuteFlag - mute flags (VOIPGROUP_INBOUND_MUTE, VOIPGROUP_OUTBOUND_MUTE, VOIPGROUP_TWOWAY_MUTE) + + \Output + int32_t - negative=error, zero=success + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +int32_t VoipGroupMuteByClientId2(VoipGroupRefT *pVoipGroup, uint32_t uClientId, uint8_t bMute, uint32_t uMuteFlag) +{ + int32_t iConnId; + + if (uClientId == (uint32_t)VOIP_CONNID_ALL) + { + iConnId = (int32_t)VOIP_CONNID_ALL; + } + else + { + for (iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) + { + break; + } + } + + if (iConnId == MAX_CLIENTS_PER_GROUP) + { + return(-1); + } + } + + return(VoipGroupMuteByConnId2(pVoipGroup, iConnId, bMute, uMuteFlag)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupMuteByClientId + + \Description + 2-way mutes a connection by the client id + + \Input *pVoipGroup - voipgroup ref + \Input iClientId - client id + \Input uMute - TRUE to mute FALSE to umute + + \Output + int32_t - negative=error, zero=success + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +int32_t VoipGroupMuteByClientId(VoipGroupRefT *pVoipGroup, uint32_t iClientId, uint8_t uMute) +{ + return(VoipGroupMuteByClientId2(pVoipGroup, iClientId, uMute, VOIPGROUP_TWOWAY_MUTE)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupMuteByConnId2 + + \Description + mutes a connection by the high level connection id and mute flag + + \Input *pVoipGroup - voipgroup ref + \Input iConnId - connection index + \Input bMute - TRUE to mute FALSE to umute + \Input uMuteFlag - mute flags (VOIPGROUP_INBOUND_MUTE, VOIPGROUP_OUTBOUND_MUTE, VOIPGROUP_TWOWAY_MUTE) + + \Output + int32_t - negative=error, zero=success + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +int32_t VoipGroupMuteByConnId2(VoipGroupRefT *pVoipGroup, uint32_t iConnId, uint8_t bMute, uint32_t uMuteFlag) +{ + VoipGroupManagerT* pManager = _VoipGroupManager_pRef; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + int32_t iMappedLowLevelConnId; + int32_t iIndex; + + iMappedLowLevelConnId = ((iConnId == (uint32_t)VOIP_CONNID_ALL) ? (int32_t)VOIP_CONNID_ALL : _VoipGroupMatch(pVoipGroup, iConnId)); + + if (iMappedLowLevelConnId == (int32_t)VOIP_CONNID_NONE) + { + return(-1); + } + + if (bMute) + { + if (iMappedLowLevelConnId == (int32_t)VOIP_CONNID_ALL) + { + // mute all + if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) + { + pManager->uUserRecvMask = 0x00000000; + } + + if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) + { + pManager->uUserSendMask = 0x00000000; + } + } + else + { + if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) + { + pManager->uUserRecvMask &= ~(1 << iMappedLowLevelConnId); + } + + if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) + { + pManager->uUserSendMask &= ~(1 << iMappedLowLevelConnId); + } + } + } + else + { + if (iMappedLowLevelConnId == VOIP_CONNID_ALL) + { + // unmute all connIds + for (iIndex = 0; iIndex < MAX_CLIENTS_PER_GROUP; ++iIndex) + { + if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) + { + pManager->uUserRecvMask |= (1 << pVoipGroup->aConnections[iIndex].iMappedLowLevelConnId); + } + + if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) + { + pManager->uUserSendMask |= (1 << pVoipGroup->aConnections[iIndex].iMappedLowLevelConnId); + } + } + } + else + { + if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) + { + pManager->uUserRecvMask |= (1 << iMappedLowLevelConnId); + } + + if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) + { + pManager->uUserSendMask |= (1 << iMappedLowLevelConnId); + } + } + } + VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserRecvMask); + VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserSendMask); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupMuteByConnId + + \Description + 2-way mutes a connection by the high level connection id + + \Input *pVoipGroup - voipgroup ref + \Input iConnId - connection index + \Input uMute - TRUE to mute FALSE to umute + + \Output + int32_t - negative=error, zero=success + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +int32_t VoipGroupMuteByConnId(VoipGroupRefT *pVoipGroup, uint32_t iConnId, uint8_t uMute) +{ + return (VoipGroupMuteByConnId2(pVoipGroup, iConnId, uMute, VOIPGROUP_TWOWAY_MUTE)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupIsMutedByClientId2 + + \Description + reutrns the mute flag for the connection specified by the client id + + \Input *pVoipGroup - voipgroup ref + \Input iClientId - connection index + + \Output + uint8_t - mute flag + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +uint32_t VoipGroupIsMutedByClientId2(VoipGroupRefT *pVoipGroup, uint32_t iClientId) +{ + int32_t iConnId; + + for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + if (pVoipGroup->aConnections[iConnId].uClientId == iClientId) + { + break; + } + } + + if (iConnId == MAX_CLIENTS_PER_GROUP) + { + return(FALSE); + } + + return(VoipGroupIsMutedByConnId2(pVoipGroup, iConnId)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupIsMutedByClientId + + \Description + Is the connection specified by the client id muted or not (1-way or 2-way) + + \Input *pVoipGroup - voipgroup ref + \Input uClientId - connection index + + \Output + int32_t - TRUE only if muted 2-ways + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +uint8_t VoipGroupIsMutedByClientId(VoipGroupRefT *pVoipGroup, uint32_t uClientId) +{ + int32_t iConnId; + + for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) + { + if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) + { + break; + } + } + + if (iConnId == MAX_CLIENTS_PER_GROUP) + { + return(FALSE); + } + + return(VoipGroupIsMutedByConnId(pVoipGroup, iConnId)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupIsMutedByConnId2 + + \Description + reutrns the mute flag for the connection specified by the high level connection id + + \Input *pVoipGroup - voipgroup ref + \Input iConnId - connection index + + \Output + uint32_t - mute flag + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +uint32_t VoipGroupIsMutedByConnId2(VoipGroupRefT *pVoipGroup, uint32_t iConnId) +{ + uint8_t uMuteFlag = 0; + int32_t iMappedLowLevelConnId; + VoipGroupManagerT* pManager = _VoipGroupManager_pRef; + + iMappedLowLevelConnId = _VoipGroupMatch(pVoipGroup, iConnId); + + if (iMappedLowLevelConnId == VOIP_CONNID_NONE) + { + return(0); + } + + if ((pManager->uUserRecvMask & (1 << iMappedLowLevelConnId)) == 0) + { + uMuteFlag |= VOIPGROUP_INBOUND_MUTE; + } + + if ((pManager->uUserSendMask & (1 << iMappedLowLevelConnId)) == 0) + { + uMuteFlag |= VOIPGROUP_OUTBOUND_MUTE; + } + + return(uMuteFlag); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupIsMutedByConnId + + \Description + Is the connection specified by the conn id muted or not (1-way or 2-way) + + \Input *pVoipGroup - voipgroup ref + \Input iConnId - connection index + + \Output + int32_t - TRUE only if it is 2-way muted + + \Version 11/02/2015 (tcho) +*/ +/********************************************************************************F*/ +uint8_t VoipGroupIsMutedByConnId(VoipGroupRefT *pVoipGroup, uint32_t iConnId) +{ + uint32_t uMuteFlag = VoipGroupIsMutedByConnId2(pVoipGroup, iConnId); + + if (uMuteFlag == VOIPGROUP_TWOWAY_MUTE) + { + return(TRUE); + } + else + { + return(FALSE); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupMuteAll + + \Description + Mute all users or restore previous mute settings from before a + previous call to VoipGroupMuteAll. This stacks with other VoipGroupMute + functionality. It does not erase the previous muting state, and therefore + is best used for short term muting of all users. + + \Input *pVoipGroup - voipgroup ref + \Input bActive - TRUE to mute all users, FALSE to restore mute settings + + \Version 10/30/2017 (amakoukji) +*/ +/********************************************************************************F*/ +void VoipGroupMuteAll(VoipGroupRefT *pVoipGroup, uint8_t bActive) +{ + VoipGroupManagerT* pManager = _VoipGroupManager_pRef; + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + if (pVoipGroup->bSilent != bActive) + { + pVoipGroup->bSilent = bActive; + VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserRecvMask); + VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserSendMask); + } +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupConnStatus + + \Description + Passes through to VoipConnRemote, afer adjusting connection ID + + \Input *pVoipGroup - The VoipGroup + \Input iConnId - The connection ID + + \Output + int32_t - VOIP_REMOTE_* flags, or VOIP_FLAG_INVALID if iConnId is invalid + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipGroupConnStatus(VoipGroupRefT *pVoipGroup, int32_t iConnId) +{ + int32_t iMappedConnId = _VoipGroupMatch(pVoipGroup, iConnId); + + if (iMappedConnId == VOIP_CONNID_NONE) + { + return(VOIP_FLAG_INVALID); + } + + return(VoipCommonConnStatus((VoipCommonRefT *)VoipGetRef(), iMappedConnId)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupRemoteUserStatus + + \Description + Passes through to VoipUserRemote, afer adjusting connection ID + + \Input *pVoipGroup - The VoipGroup + \Input iConnId - The connection ID + \Input iRemoteUserIndex - User index + + \Output + int32_t - VOIP_REMOTE_* flags, or VOIP_FLAG_INVALID if iConnId is invalid + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipGroupRemoteUserStatus(VoipGroupRefT *pVoipGroup, int32_t iConnId, int32_t iRemoteUserIndex) +{ + int32_t iMappedConnId = _VoipGroupMatch(pVoipGroup, iConnId); + + if (iMappedConnId == VOIP_CONNID_NONE) + { + return(VOIP_FLAG_INVALID); + } + + return(VoipCommonRemoteUserStatus((VoipCommonRefT *)VoipGetRef(), iMappedConnId, iRemoteUserIndex)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupLocalUserStatus + + \Description + return information about local hardware state + + \Input *pVoipGroup - The VoipGroup + \Input iLocalUserIndex - User index + + \Output + int32_t - VOIP_LOCAL_* flags + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipGroupLocalUserStatus(VoipGroupRefT *pVoipGroup, int32_t iLocalUserIndex) +{ + return(VoipLocalUserStatus(VoipGetRef(), iLocalUserIndex)); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupStatus + + \Description + Passes through to VoipStatus, for historical reasons + + \Input *pVoipGroup - The VoipGroup + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuf - selector-specific + \Input iBufSize - size of the pBuf input + + \Output + int32_t - selector-specific data + + \Notes + iSelect can be one of the following: + + \verbatim + rchn: returns the active channel of the remote peer + rcvu: returns the voipuser of the remote peer + slnt: returns if the group is silent + \endverbatim + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipGroupStatus(VoipGroupRefT *pVoipGroup, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + int32_t iRetCode = -1; // default to error + + if (iSelect == 'rchn') + { + // convert high-level id to low-level id + int32_t iMappedConnId = _VoipGroupMatch(pVoipGroup, iValue); + + if (iMappedConnId != VOIP_CONNID_NONE) + { + iRetCode = VoipStatus(VoipGetRef(), iSelect, iMappedConnId, pBuf, iBufSize); + } + } + else if (iSelect == 'rcvu') + { + int32_t iMappedConnId; + int32_t iHighLevelConnId = iValue & 0xFFFF; + int32_t iRemoteUserIndex = iValue >> 16; + + // convert high-level id to low-level id + iMappedConnId = _VoipGroupMatch(pVoipGroup, iHighLevelConnId); + + if (iMappedConnId != VOIP_CONNID_NONE) + { + // rebuild input parameter with converted conn index + iValue = iMappedConnId; + iValue |= (iRemoteUserIndex << 16); + + iRetCode = VoipStatus(VoipGetRef(), iSelect, iValue, pBuf, iBufSize); + } + } + else if (iSelect == 'slnt') + { + iRetCode = pVoipGroup->bSilent; + } + else + { + iRetCode = VoipStatus(VoipGetRef(), iSelect, iValue, pBuf, iBufSize); + } + + return(iRetCode); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupControl + + \Description + Passes through to VoipControl, afer adjusting connection ID and flags. + + \Input *pVoipGroup - The VoipGroup + \Input iControl - status selector + \Input iValue - selector-specific + \Input iValue2 - selector-specific + \Input *pValue - selector-specific + + \Output + int32_t - selector-specific data + + \Notes + iControl can be one of the following: + + \verbatim + ccmd: set the CC mode (VOIPGROUP_CCMODE_*) + lusr: register local voip user + serv: flag voipgroup as using voipserver + tunl: flag voipgroup as using tunnel + vcid: set the local id (pValue) for the voip connection with client (iValue) + \endverbatim + + \Version 01/31/2007 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipGroupControl(VoipGroupRefT *pVoipGroup, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + VoipGroupManagerT *pManager = _VoipGroupManager_pRef; + + if (iControl == 'ccmd') + { + #if DIRTYCODE_LOGGING + static const char *_VoipGroupCcModeNames[] = + { + "VOIPGROUP_CCMODE_PEERONLY", + "VOIPGROUP_CCMODE_HOSTEDONLY", + "VOIPGROUP_CCMODE_HOSTEDFALLBACK" + }; + #endif + + NetPrintf(("voipgroup: [%p] CC mode = %d (%s)\n", pVoipGroup, iValue, _VoipGroupCcModeNames[iValue])); + pVoipGroup->iCcMode = iValue; + return(0); + } + if (iControl == 'lusr') + { + if (VoipGetRef() == NULL) + { + return(-1); + } + + // only proceed with performing the operation for the first group created or + // for the last group being destroyed as groups cannot have different voip + // local users. + if (pManager->iNumGroups != 1) + { + NetPrintf(("voipgroup: [%p] skipping %s of voip local user and %d, because multiple voipgroups exist\n", + pVoipGroup, (iValue2 ? "registration" : "unregistration"), iValue)); + return(-1); + } + + VoipSetLocalUser(VoipGetRef(), iValue, iValue2); + return(0); + } + if (iControl == 'serv') + { + pVoipGroup->bServer = iValue; + return(0); + } + if (iControl == 'tunl') + { + pVoipGroup->bTunnel = iValue; + return(0); + } + if (iControl == 'vcid') + { + NetPrintf(("voipgroup: [%p] set local client id 0x%08x for conn id %d\n", pVoipGroup, *(int32_t*)pValue, iValue)); + pVoipGroup->aLowLevelConnectivityId[iValue] = *(uint32_t*)pValue; + return(0); + } + if (iControl == 'xply') + { + pVoipGroup->bCrossplay = iValue ? TRUE : FALSE; + return(0); + } + if (VoipGetRef() != NULL) + { + return(VoipControl(VoipGetRef(), iControl, iValue, pValue)); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function VoipGroupActivateLocalUser + + \Description + Refcounted access to VoipCommonActivateLocalUser() + + \Input *pVoipGroup - The VoipGroup (if voipgroup is null and bActivate is false we will deactivate the user from all the voipgroup) + \Input iLocalUserIndex - local user index + \Input bActivate - TRUE to activate, FALSE to deactivate + + \Version 05/12/2014 (mclouatre) +*/ +/********************************************************************************F*/ +void VoipGroupActivateLocalUser(VoipGroupRefT *pVoipGroup, int32_t iLocalUserIndex, uint8_t bActivate) +{ + VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); + + if ((iLocalUserIndex >= 0) && (iLocalUserIndex < VOIP_MAXLOCALUSERS)) + { + if (bActivate && (pVoipGroup != NULL)) + { + // sanity check + if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] < 0) + { + NetPrintf(("voipgroup: [%p] warning, VoipGroupActivateLocalUser invalid ref count for user %d, %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); + _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] = 0; + } + + // increment ref count + ++(_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex]); + + pVoipGroup->aParticipatingUser[iLocalUserIndex] = TRUE; + + NetPrintf(("voipgroup: [%p] VoipGroupActivateLocalUser incrementing ref count for user %d to %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); + + // activate underlying voip ref if required + if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] <= 1) + { + VoipCommonActivateLocalUser(pVoipCommon, iLocalUserIndex, bActivate); + } + } + else if (_VoipGroupManager_pRef != NULL) + { + // sanity check + if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] <= 0) + { + NetPrintf(("voipgroup: [%p] warning, deactivating local user %d with ref count %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); + _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] = 0; + } + else + { + if (pVoipGroup == NULL) + { + int32_t iGroupIndex = 0; + VoipGroupManagerT *pVoipGroupManager = _VoipGroupManager_pRef; + + for (iGroupIndex = 0; iGroupIndex < pVoipGroupManager->iMaxGroups; ++iGroupIndex) + { + if (pVoipGroupManager->aGroups[iGroupIndex].aParticipatingUser[iLocalUserIndex] == TRUE) + { + // decrement ref count + --(pVoipGroupManager->aActiveRefCount[iLocalUserIndex]); + pVoipGroupManager->aGroups[iGroupIndex].aParticipatingUser[iLocalUserIndex] = FALSE; + } + } + + NetPrintf(("voipgroup: VoipGroupActivateLocalUser deactivating user %d from all voipgroups\n", iLocalUserIndex)); + } + else + { + if (pVoipGroup->aParticipatingUser[iLocalUserIndex] == TRUE) + { + // decrement ref count + --(_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex]); + pVoipGroup->aParticipatingUser[iLocalUserIndex] = FALSE; + NetPrintf(("voipgroup: [%p] VoipGroupActivateLocalUser decrementing ref count for user %d to %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); + } + } + + // deactivate underlying voip ref if required + if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] <= 0) + { + VoipCommonActivateLocalUser(pVoipCommon, iLocalUserIndex, bActivate); + } + } + } + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipheadset.h b/r5dev/thirdparty/dirtysdk/source/voip/voipheadset.h new file mode 100644 index 00000000..d07996a3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipheadset.h @@ -0,0 +1,121 @@ +/*H********************************************************************************/ +/*! + \File voipheadset.h + + \Description + VoIP headset manager. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 03/31/2004 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _voipheadset_h +#define _voipheadset_h + +/*** Include files ****************************************************************/ + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! voip headset update status +typedef enum VoipHeadsetStatusUpdateE +{ + VOIP_HEADSET_STATUS_INPUT = 1, + VOIP_HEADSET_STATUS_OUTPUT, + VOIP_HEADSET_STATUS_INOUT +} VoipHeadsetStatusUpdateE; + +//! opaque headset module ref +typedef struct VoipHeadsetRefT VoipHeadsetRefT; + +//! mic data ready callback function prototype +typedef void (VoipHeadsetMicDataCbT)(const void *pVoiceData, int32_t iDataSize, const void *pMetaData, int32_t iMetaDataSize, uint32_t uLocalUserIndex, uint8_t uSendSeqn, void *pUserData); + +//! locally generated transcribed text ready callback function prototype +typedef void (VoipHeadsetTextDataCbT)(const char *pTranscribedTextUtf8, uint32_t uLocalUserIndex, void *pUserData); + +#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) +//! remote-generated transcribed text received callback function prototype +typedef void (VoipHeadsetTranscribedTextReceivedCbT)(VoipUserT *pUser, const char *pText, void *pUserData); +#endif + +//! opaque data ready callback function prototype +typedef void (VoipHeadsetOpaqueDataCbT)(const uint8_t *pOpaqueData, int32_t iOpaqueDataSize, uint32_t uSendMask, uint8_t bReliable, uint8_t uSendSeqn, void *pUserData); + +#if defined(DIRTYCODE_PS4) +//! remote-generated transcribed text received callback function prototype +typedef void (VoipHeadsetTranscribedTextReceivedCbT)(const VoipUserT *pUser, const char *pText, void *pUserData); +#endif + +//! headset status change callback +typedef void (VoipHeadsetStatusCbT)(int32_t iLocalUserIndex, uint32_t bStatus, VoipHeadsetStatusUpdateE eUpdate, void *pUserData); + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create headset module +VoipHeadsetRefT *VoipHeadsetCreate(int32_t iMaxChannels, VoipHeadsetMicDataCbT *pMicDataCb, VoipHeadsetTextDataCbT *pTextDataCb, VoipHeadsetOpaqueDataCbT *pOpaqueDataCb, VoipHeadsetStatusCbT *pStatusCb, void *pCbUserData, int32_t iData); + +// destroy headset module +void VoipHeadsetDestroy(VoipHeadsetRefT *pHeadset); + +// callback to handle receiving voice data +void VoipHeadsetReceiveVoiceDataCb(VoipUserT *pRemoteUsers, int32_t iRemoteUserSize, int32_t iConsoleId, VoipMicrInfoT *pMicrInfo, uint8_t *pPacketData, void *pUserData); + +// callback to handle receiving opaque data +void VoipHeadsetReceiveOpaqueDataCb(int32_t iConsoleId, const uint8_t *pOpaqueData, int32_t iOpaqueDataSize, void *pUserData); + +// callback to handle registering of a new user +void VoipHeadsetRegisterUserCb(VoipUserT *pRemoteUser, int32_t iConsoleID, uint32_t bRegister, void *pUserData); + +// process function to update headset(s) +void VoipHeadsetProcess(VoipHeadsetRefT *pHeadset, uint32_t uFrameCount); + +// set play/rec volumes (-1 to keep current setting) +void VoipHeadsetSetVolume(VoipHeadsetRefT *pHeadset, int32_t iPlayVol, uint32_t iRecVol); + +// control function +int32_t VoipHeadsetControl(VoipHeadsetRefT *pHeadset, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +// status function +int32_t VoipHeadsetStatus(VoipHeadsetRefT *pHeadset, int32_t iSelect, int32_t iData, void *pBuf, int32_t iBufSize); + +// set speaker output callback (only available on some platforms) +void VoipHeadsetSpkrCallback(VoipHeadsetRefT *pHeadset, VoipSpkrCallbackT *pCallback, void *pUserData); + +// configure transcribe +void VoipHeadsetConfigTranscription(VoipHeadsetRefT *pHeadset, uint32_t uProfile, const char *pUrl, const char *pKey); + +// configure narrate +void VoipHeadsetConfigNarration(VoipHeadsetRefT *pHeadset, uint32_t uProvider, const char *pUrl, const char *pKey); + +#if defined(DIRTYCODE_PS4) +// validate communication permissions before display text from a remote user +void VoipHeadsetTranscribedTextPermissionCheck(VoipHeadsetRefT *pHeadset, VoipUserT *pOriginator, const char *pStrUtf8); +#endif + +#if defined(DIRTYCODE_XBOXONE)|| defined(DIRTYCODE_PS4) || defined(DIRTYCODE_GDK) +// set callback to be invoked by voipheadsetxboxone when inbound transcribed text has passed communication permission check +void VoipHeadsetSetTranscribedTextReceivedCallback(VoipHeadsetRefT *pHeadset, VoipHeadsetTranscribedTextReceivedCbT *pCallback, void *pUserData); +#endif + +// callback to tell dirtysdk what the first party id is for this user +void VoipHeadsetSetFirstPartyIdCallback(VoipHeadsetRefT *pHeadset, VoipFirstPartyIdCallbackCbT *pPlaybackCallback, void *pUserData); + +#ifdef __cplusplus +}; +#endif + +#endif // _voipheadset_h + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipmixer.c b/r5dev/thirdparty/dirtysdk/source/voip/voipmixer.c new file mode 100644 index 00000000..a6189513 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipmixer.c @@ -0,0 +1,364 @@ +/*H********************************************************************************/ +/*! + \File voipmixer.c + + \Description + VoIP audio mixer + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 07/29/2004 (jbrookes) Split from voipheadset.c +*/ +/********************************************************************************H*/ + + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "voipmixer.h" + +/*** Defines **********************************************************************/ + +//! max number of supported mixbuffers +#define VOIP_MAXMIXBUFFERS (16) + +/*** Macros ***********************************************************************/ + +//! TRUE if value is a power of two, else FALSE +#define VOIP_PowerOf2(_val) ( ((_val) & -(_val)) == (_val) ) + +//! index the next mixbuffer +#define VOIP_NextMixBuffer(_pRef, _iCur) (((_iCur) + 1) % ((_pRef)->iNumMixBuffers)) + +//! absolute value of an 32bit integer +#define VOIP_Abs32(_val) (((_val) ^ ((_val) >> 31)) - ((_val) >> 31)) + +/*** Type Definitions *************************************************************/ + +typedef struct VoipMixBufferT +{ + int32_t *pMixBuffer; + int32_t iMixCount; + int32_t iMixMask; +} VoipMixBufferT; + +//! VOIP module state data +struct VoipMixerRefT +{ + //! number of mixbuffers + int32_t iNumMixBuffers; + + //! current mix buffer to mix into + int32_t iCurMixBuffer; + + //! size of a single mixbuffer + int32_t iMixBufferSize; + + //! mixbuffer list + VoipMixBufferT MixBuffers[VOIP_MAXMIXBUFFERS]; +}; + +/*** Function Prototypes **********************************************************/ + +/*** Variables ********************************************************************/ + +// Public Variables + +// Private Variables + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _VoipMixerPostMixSingle + + \Description + Processes the integer accumulation buffer in the case where only one + data packet was accumulated. + + \Input *pDst - [out] output play buffer + \Input *pSrc - mixbuffer pointer + \Input iNumSamples - number of samples to mix + + \Output + None. + + \Version 04/02/2004 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipMixerPostMixSingle(int16_t *pDst, int32_t *pSrc, int32_t iNumSamples) +{ + int32_t iData; + + // process data + for (iData = 0; iData < iNumSamples; iData++) + { + pDst[iData] = (int16_t)pSrc[iData]; + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipMixerPostMixMulti + + \Description + Processes the integer accumulation buffer in the case where more than one + packet was accumulated. + + \Input *pDst - [out] output play buffer + \Input *pSrc - mixbuffer pointer + \Input uBufMax - largest absolute value in sample buffer + \Input iNumSamples - number of samples to mix + + \Output + None. + + \Version 04/02/2004 (jbrookes) Split from VoipProcessHeadset() +*/ +/********************************************************************************F*/ +static void _VoipMixerPostMixMulti(int16_t *pDst, int32_t *pSrc, uint32_t uBufMax, int32_t iNumSamples) +{ + int32_t iData; + int32_t iMult; + int64_t lVal; + + // calculate scaler + iMult = (0x7fffffff/uBufMax)*32767; + + // scale buffer data + for (iData = 0; iData < iNumSamples; iData++) + { + lVal = (int64_t)(pSrc[iData]<<1) * (int64_t)iMult; + pDst[iData] = (int32_t)(lVal >> 32); + } +} + + +/*** Public functions **************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipMixerCreate + + \Description + Create the mixer module. + + \Input uMixBuffers - number of mixbuffers to implement + \Input uFrameSamples - size of a frame in samples + + \Output + VoipMixerRefT * - pointer to mixer module, or NULL if an error occurred + + \Version 07/29/2004 (jbrookes) +*/ +/********************************************************************************F*/ +VoipMixerRefT *VoipMixerCreate(uint32_t uMixBuffers, uint32_t uFrameSamples) +{ + VoipMixerRefT *pMixerRef; + int32_t iMixBuffer, iMixBufferSize; + int32_t iMemGroup; + void *pMemGroupUserData; + + // make sure mixbuffer count is between 2 and VOIP_MAXMIXBUFFERS + if (uMixBuffers < 2) + { + NetPrintf(("voipmixer: clamping create request of %d mixbuffers to 2\n", uMixBuffers)); + uMixBuffers = 2; + } + if (uMixBuffers > VOIP_MAXMIXBUFFERS) + { + NetPrintf(("voipmixer: clamping create request of %d mixbuffers to %d\n", uMixBuffers, VOIP_MAXMIXBUFFERS)); + uMixBuffers = VOIP_MAXMIXBUFFERS; + } + + // allocate and wipe module state + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + if ((pMixerRef = DirtyMemAlloc(sizeof(*pMixerRef), VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipmixer: unable to allocate %d bytes for mixer state\n", sizeof(*pMixerRef))); + return(NULL); + } + ds_memclr(pMixerRef, sizeof(*pMixerRef)); + + // init ref + pMixerRef->iNumMixBuffers = uMixBuffers; + pMixerRef->iMixBufferSize = uFrameSamples; + + // allocate mix buffers + iMixBufferSize = pMixerRef->iNumMixBuffers * pMixerRef->iMixBufferSize * sizeof(int32_t); + if ((pMixerRef->MixBuffers[0].pMixBuffer = DirtyMemAlloc(iMixBufferSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipmixer: unable to allocate %d bytes for %d mixbuffers\n", iMixBufferSize, pMixerRef->iNumMixBuffers)); + DirtyMemFree(pMixerRef, VOIP_MEMID, iMemGroup, pMemGroupUserData); + return(NULL); + } + ds_memclr(pMixerRef->MixBuffers[0].pMixBuffer, iMixBufferSize); + + // set up mixbuffers 1...N + for (iMixBuffer = 1; iMixBuffer < pMixerRef->iNumMixBuffers; iMixBuffer++) + { + pMixerRef->MixBuffers[iMixBuffer].pMixBuffer = pMixerRef->MixBuffers[iMixBuffer-1].pMixBuffer + pMixerRef->iMixBufferSize; + } + + // return module state to caller + return(pMixerRef); +} + +/*F********************************************************************************/ +/*! + \Function VoipMixerDestroy + + \Description + Destroy the mixer module. + + \Input *pMixerRef - pointer to mixer module + + \Output + None. + + \Version 07/29/2004 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipMixerDestroy(VoipMixerRefT *pMixerRef) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + DirtyMemFree(pMixerRef->MixBuffers[0].pMixBuffer, VOIP_MEMID, iMemGroup, pMemGroupUserData); + DirtyMemFree(pMixerRef, VOIP_MEMID, iMemGroup, pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipMixerAccumulate + + \Description + Decodes and accumulates incoming data into mixbuffer. + + \Input *pMixerRef - pointer to mixer module + \Input *pInputData - pointer to input voice data + \Input iDataSize - size of incoming voice data + \Input iMixMask - bitmask uniquely representing who is doing the writing + \Input iChannel - input channel number + + \Output + int32_t - negative=error, else next mixbuffer to write into + + \Version 07/29/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipMixerAccumulate(VoipMixerRefT *pMixerRef, uint8_t *pInputData, int32_t iDataSize, int32_t iMixMask, int32_t iChannel) +{ + int32_t iNumSamples; + int32_t iIndex, iMixBuffer; + + iMixBuffer = pMixerRef->iCurMixBuffer; + + // find the first spot that we can write into for this conduit + for (iIndex = 0; iIndex != pMixerRef->iNumMixBuffers; iIndex++) + { + // if we've found a spot + if (!(pMixerRef->MixBuffers[iMixBuffer].iMixMask & iMixMask)) + { + break; + } + + iMixBuffer = (iMixBuffer + 1) % pMixerRef->iNumMixBuffers; + } + + // make sure we're not accumulating into a mixbuffer we've already written into + if (iIndex == pMixerRef->iNumMixBuffers) + { + return(-1); + } + + // decode into mixbuffer + iNumSamples = VoipCodecDecode(pMixerRef->MixBuffers[iMixBuffer].pMixBuffer, pInputData, iDataSize, iChannel); + + // validate size + if (iNumSamples != pMixerRef->iMixBufferSize) + { + NetPrintf(("voipmixer: warning, expecting %d samples but got %d samples\n", pMixerRef->iMixBufferSize, iNumSamples)); + } + + // increment the mixcount for the mixbuffer and mark that we've written into it + pMixerRef->MixBuffers[iMixBuffer].iMixCount += 1; + pMixerRef->MixBuffers[iMixBuffer].iMixMask |= iMixMask; + + // swap this channel to the next mixbuffer + iMixBuffer = VOIP_NextMixBuffer(pMixerRef, iMixBuffer); + return(iMixBuffer); +} + +/*F********************************************************************************/ +/*! + \Function VoipMixerProcess + + \Description + Mixes queued data into one output buffer suitable for playback. + + \Input *pMixerRef - pointer to mixer + \Input *pFrameData - pointer to output + + \Output + int32_t - number of bytes written to output buffer + + \Version 04/02/2004 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipMixerProcess(VoipMixerRefT *pMixerRef, uint8_t *pFrameData) +{ + uint32_t uAbsVal, uBufMax; + int32_t *pMixBuffer; + int32_t iSample; + + // make sure we have data in the mixbuffer + if (pMixerRef->MixBuffers[pMixerRef->iCurMixBuffer].iMixCount == 0) + { + return(0); + } + pMixBuffer = pMixerRef->MixBuffers[pMixerRef->iCurMixBuffer].pMixBuffer; + + // calculate the buffer absolute max + for (iSample = 0, uBufMax = 0; iSample < pMixerRef->iMixBufferSize; iSample++) + { + uAbsVal = (uint32_t)VOIP_Abs32(pMixBuffer[iSample]); + if (uBufMax < uAbsVal) + { + uBufMax = uAbsVal; + } + } + + // if the buffer max was less than our max sample value + if (uBufMax < 32767) + { + _VoipMixerPostMixSingle((int16_t *)pFrameData, pMixBuffer, pMixerRef->iMixBufferSize); + } + else + { + _VoipMixerPostMixMulti((int16_t *)pFrameData, pMixBuffer, uBufMax, pMixerRef->iMixBufferSize); + } + + // reset current mixbuffer + ds_memclr(pMixBuffer, pMixerRef->iMixBufferSize*sizeof(int32_t)); + pMixerRef->MixBuffers[pMixerRef->iCurMixBuffer].iMixCount = 0; + pMixerRef->MixBuffers[pMixerRef->iCurMixBuffer].iMixMask = 0; + + // index next mixbuffer + pMixerRef->iCurMixBuffer = VOIP_NextMixBuffer(pMixerRef, pMixerRef->iCurMixBuffer); + return(pMixerRef->iMixBufferSize*2); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipmixer.h b/r5dev/thirdparty/dirtysdk/source/voip/voipmixer.h new file mode 100644 index 00000000..b39fb437 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipmixer.h @@ -0,0 +1,57 @@ +/*H********************************************************************************/ +/*! + \File voipmixer.h + + \Description + VoIP data packet definitions. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 07/29/2004 (jbrookes) Split from voipheadset +*/ +/********************************************************************************H*/ + +#ifndef _voipmixer_h +#define _voipmixer_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/voip/voipcodec.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +//! opaque module state ref +typedef struct VoipMixerRefT VoipMixerRefT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +// create module state +VoipMixerRefT *VoipMixerCreate(uint32_t uMixBuffers, uint32_t uFrameSamples); + +// destroy module state +void VoipMixerDestroy(VoipMixerRefT *pMixerRef); + +// accumulate data into mixbuffer +int32_t VoipMixerAccumulate(VoipMixerRefT *pMixerRef, unsigned char *pInputData, int32_t iDataSize, int32_t iMixMask, int32_t iChannel); + +// process mix data currently in accumulator and return a mixed frame +int32_t VoipMixerProcess(VoipMixerRefT *pMixerRef, unsigned char *pFrameData); + +#ifdef __cplusplus +}; +#endif + +#endif // _voipmixer_h + + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voipnarrate.c b/r5dev/thirdparty/dirtysdk/source/voip/voipnarrate.c new file mode 100644 index 00000000..51507cf4 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voipnarrate.c @@ -0,0 +1,1169 @@ +/*H********************************************************************************/ +/*! + \File voipnarrate.c + + \Description + Voip narration API wrapping Cloud-based text-to-speech services, supporting + IBM Watson, Microsoft Speech Service, Google Speech, and Amazon Polly. + Narration requests may be up to 255 characters in length, and overlapping + requests are queued in order. + + \Notes + References + + IBM Watson: + Text-to Speech-API: https://www.ibm.com/watson/developercloud/text-to-speech/api/v1/curl.html + + Microsoft Speech Service: + Text-to-Speech How-To: https://docs.microsoft.com/en-us/azure/cognitive-services/Speech-Service/how-to-text-to-speech + + Google Text-to-Speech + Text-to-Speech API: https://cloud.google.com/text-to-speech/docs/reference/rest/ + + Amazon Polly + SynthesizeSpeech API: https://docs.aws.amazon.com/polly/latest/dg/API_SynthesizeSpeech.html + Amazon Endpoint Names: https://docs.aws.amazon.com/general/latest/gr/rande.html + VoiceId List: https://docs.aws.amazon.com/polly/latest/dg/API_SynthesizeSpeech.html#polly-SynthesizeSpeech-request-VoiceId + + WAV: + WAVE file format: https://en.wikipedia.org/wiki/WAV#RIFF_WAVE + + \Copyright + Copyright 2018 Electronic Arts + + \Version 10/25/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/proto/protostream.h" +#include "DirtySDK/util/aws.h" +#include "DirtySDK/util/base64.h" +#include "DirtySDK/util/jsonformat.h" +#include "DirtySDK/util/jsonparse.h" +#include "DirtySDK/voip/voipdef.h" + +#include "DirtySDK/voip/voipnarrate.h" + +/*** Defines **********************************************************************/ + +//! protostream minimum data amount (for base64 decoding; four is the minimum amount but that produces one and a half samples, so we choose eight, BUT... +#define VOIPNARRATE_MINBUF (8) + +//! how many ms of audio received should we treat as being empty audio (for metrics) +#define VOIPNARRATE_EMPTY_AUDIO_THRESHOLD_MS (300) + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct VoipNarrateConfigT +{ + VoipNarrateProviderE eProvider; //!< configured provider + char strUrl[256]; //!< URL for text-to-speech request + char strKey[128]; //!< API key required for service authentication +} VoipNarrateConfigT; + +//! narration request data +typedef struct VoipNarrateRequestT +{ + struct VoipNarrateRequestT *pNext; + VoipNarrateGenderE eGender; + char strText[VOIPNARRATE_INPUT_MAX]; + int8_t iUserIndex; +} VoipNarrateRequestT; + +struct VoipNarrateRefT +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + ProtoStreamRefT *pProtoStream; //!< stream transport module to handle buffered download of audio data + + VoipNarrateVoiceDataCbT *pVoiceDataCb; //!< user callback used to provide voice data + void *pUserData; //!< user data for user callback + + VoipNarrateRequestT *pRequest; //!< list of queued requests, if any + VoipNarrateConfigT Config; //!< module configuration (provider and credentials) + + char strHead[256]; //!< http head for narration request + char strBody[512]; //!< http body for narration request + const char *pBody; //!< pointer to start of body (may not match buffer start) + + VoipTextToSpeechMetricsT Metrics; //!< Usage metrics of the narration module + uint32_t uTtsStartTime; //!< time when we sent the request + + uint8_t aVoiceBuf[160*3*2]; //!< base64 decode buffer, sized for one 30ms frame of 16khz 16bit voice audio, also a multiple of three bytes to accomodate base64 4->3 ratio + int32_t iVoiceOff; //!< read offset in buffered voice data + int32_t iVoiceLen; //!< end of buffered voice data + int32_t iSamplesInPhrase; //!< total number of samples received for this phrase + + uint8_t bStart; //!< TRUE if start of stream download, else FALSE + uint8_t bActive; //!< TRUE if stream is active, else FALSE + int8_t iUserIndex; //!< index of local user current request is being made for + int8_t iVerbose; //!< verbose debug level (debug only) +}; + +/*** Variables ********************************************************************/ + +//! global config state +static VoipNarrateConfigT _VoipNarrate_Config = { VOIPNARRATE_PROVIDER_NONE, "", "" }; + +/*** Private Functions ************************************************************/ + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateCustomHeaderCb + + \Description + Custom header callback used to sign AWS requests + + \Input *pState - http module state + \Input *pHeader - pointer to http header buffer + \Input uHeaderSize - size of http header buffer + \Input *pData - pointer to data (unused) + \Input iDataLen - data length (unused) + \Input *pUserRef - voipnarrate ref + + \Output + int32_t - output header length + + \Version 12/28/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateCustomHeaderCb(ProtoHttpRefT *pState, char *pHeader, uint32_t uHeaderSize, const char *pData, int64_t iDataLen, void *pUserRef) +{ + VoipNarrateRefT *pVoipNarrate = (VoipNarrateRefT *)pUserRef; + int32_t iHdrLen = (int32_t)strlen(pHeader); + + // if amazon and we have room, sign the request + if ((pVoipNarrate->Config.eProvider != VOIPNARRATE_PROVIDER_AMAZON) || (uHeaderSize < (unsigned)iHdrLen)) + { + return(iHdrLen); + } + + // sign the request and return the updated size + iHdrLen += AWSSignSigV4(pHeader, uHeaderSize, pVoipNarrate->pBody, pVoipNarrate->Config.strKey, "polly", NULL); + // return size to protohttp + return(iHdrLen); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateSkipWavHeader + + \Description + Return offset past WAV header in input data + + \Input *pData - pointer to wav header + \Input iDataLen - length of data + + \Output + int32_t - offset past WAV header, or zero + + \Version 11/06/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateSkipWavHeader(const uint8_t *pData, int32_t iDataLen) +{ + int32_t iOffset = 0, iChkLen; + uint8_t bFoundData; + + // validate and skip RIFF/WAVE header + if ((iDataLen < 12) || ds_strnicmp((const char *)pData, "RIFF", 4) || ds_strnicmp((const char *)pData+8, "WAVE", 4)) + { + return(0); + } + iOffset += 12; + + // process chunks + for (bFoundData = FALSE; iOffset < (iDataLen+12); iOffset += iChkLen+8) + { + // get chunk length + iChkLen = pData[iOffset+4]; + iChkLen |= pData[iOffset+5]<<8; + iChkLen |= pData[iOffset+6]<<16; + iChkLen |= pData[iOffset+7]<<24; + + // look for data chunk + if (!ds_strnicmp((const char *)pData+iOffset, "data", 4)) + { + bFoundData = TRUE; + iOffset += 8; + break; + } + } + return(bFoundData ? iOffset : 0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateBasicAuth + + \Description + Encode Basic HTTP authorization header as per https://tools.ietf.org/html/rfc7617 + + \Input *pBuffer - [out] output buffer for encoded base64 string + \Input iBufSize - size of output buffer + \Input *pUser - user identifer + \Input *pPass - user password + + \Output + const char * - pointer to output buffer + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipNarrateBasicAuth(char *pBuffer, int32_t iBufSize, const char *pUser, const char *pPass) +{ + char strAuth[128]; + ds_snzprintf(strAuth, sizeof(strAuth), "%s:%s", pUser, pPass); + Base64Encode2(strAuth, (int32_t)strlen(strAuth), pBuffer, iBufSize); + return(pBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateBase64Decode + + \Description + Decode Base64-encoded voice data + + \Input *pVoipNarrate - pointer to module state + \Input *pOutput - [out] buffer to hold decoded voice data + \Input *pOutSize - [in/out] output buffer length, size of output data + \Input *pInput - base64-encoded input data + \Input iInpSize - input buffer length + + \Output + int32_t - negative=failure, else input bytes consumed + + \Version 10/27/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateBase64Decode(VoipNarrateRefT *pVoipNarrate, char *pOutput, int32_t *pOutSize, const char *pInput, int32_t iInpSize) +{ + static const char _strJson[] = "\"audioContent\":"; + const char *pInput2, *pInpEnd = pInput + iInpSize; + int32_t iInpOff = 0; + + // if we have the beginning of json envelope, skip it + if ((pInput2 = strstr(pInput, _strJson)) != NULL) + { + // skip json header + pInput2 += sizeof(_strJson); + // skip to base64 data + for (; (*pInput2 != '"') && (pInput2 < pInpEnd); pInput2 += 1) + ; + if (*pInput2 != '"') + { + return(-1); + } + // skip quote + pInput2 += 1; + // remember to consume this in addition to base64 data + iInpOff = pInput2 - pInput; + pInput = pInput2; + } + // if we have end of json envelope, trim it + if ((pInput2 = strchr(pInput, '"')) != NULL) + { + // handle end of data + if (pInput2 == pInput) + { + iInpOff = iInpSize; + } + iInpSize = pInput2-pInput; + } + + // constrain input size to what will fit in output buffer + if (iInpSize > Base64EncodedSize(*pOutSize)) + { + iInpSize = Base64EncodedSize(*pOutSize); + } + + // make sure input size is a multiple of four to produce an integral number of output bytes + iInpSize &= ~0x03; + + // base64 decode and save output size + *pOutSize = Base64Decode3(pInput, iInpSize, pOutput, *pOutSize); + // return number of bytes of input consumed + return(iInpSize+iInpOff); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateRequestAdd + + \Description + Queue request for later sending + + \Input *pVoipNarrate - pointer to module state + \Input iUserIndex - local user index of user who is requesting speech synthesis + \Input eGender - preferred gender for voice narration + \Input *pText - text to be converted + + \Output + int32_t - negative=failure, else success + + \Version 11/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateRequestAdd(VoipNarrateRefT *pVoipNarrate, int32_t iUserIndex, VoipNarrateGenderE eGender, const char *pText) +{ + VoipNarrateRequestT *pRequest; + + // allocate and clear the request + if ((pRequest = DirtyMemAlloc(sizeof(*pRequest), VOIPNARRATE_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData)) == NULL) + { + NetPrintf(("voipnarrate: could not allocate request\n")); + pVoipNarrate->Metrics.uErrorCount += 1; + return(-1); + } + ds_memclr(pRequest, sizeof(*pRequest)); + + // copy the request data + ds_strnzcpy(pRequest->strText, pText, sizeof(pRequest->strText)); + pRequest->iUserIndex = iUserIndex; + pRequest->eGender = eGender; + + // add to queue + pRequest->pNext = pVoipNarrate->pRequest; + pVoipNarrate->pRequest = pRequest; + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateRequestGet + + \Description + Get queued request + + \Input *pVoipNarrate - pointer to module state + \Input *pRequest - [out] storage for request (may be null) + + \Version 11/09/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipNarrateRequestGet(VoipNarrateRefT *pVoipNarrate, VoipNarrateRequestT *pRequest) +{ + VoipNarrateRequestT **ppRequest; + // get oldest request (we add to head, so get from tail) + for (ppRequest = &pVoipNarrate->pRequest; (*ppRequest)->pNext != NULL; ppRequest = &((*ppRequest)->pNext)) + ; + // copy request + if (pRequest != NULL) + { + ds_memcpy_s(pRequest, sizeof(*pRequest), *ppRequest, sizeof(**ppRequest)); + } + // free request + DirtyMemFree(*ppRequest, VOIPNARRATE_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); + // remove from list + *ppRequest = NULL; +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatHeadWatson + + \Description + Format connection header for IBM Watson Speech service + Ref: https://console.bluemix.net/docs/services/text-to-speech/http.html#usingHTTP + + \Input *pVoipNarrate - pointer to module state + \Input *pUrl - [out] buffer for formatted url + \Input iUrlLen - length of url buffer + \Input *pHead - [out] buffer for formatted request header + \Input iHeadLen - length of header buffer + \Input eGender - preferred gender for voice narration + + \Output + int32_t - negative=failure, else success + + \Version 11/07/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipNarrateFormatHeadWatson(VoipNarrateRefT *pVoipNarrate, char *pUrl, int32_t iUrlLen, char *pHead, int32_t iHeadLen, VoipNarrateGenderE eGender) +{ + char strAuth[128]; + int32_t iOffset=0; + + // encode Basic authorization string with string apikey: + _VoipNarrateBasicAuth(strAuth, sizeof(strAuth), "apikey", pVoipNarrate->Config.strKey); + + // format request header + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "Content-Type: application/json\r\n"); + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "Accept: audio/wav; rate=%d\r\n", VOIPNARRATE_SAMPLERATE); + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "Authorization: Basic %s\r\n", strAuth); + + // format url with voice based on preferred gender + ds_snzprintf(pUrl, iUrlLen, "%s?voice=%s", pVoipNarrate->Config.strUrl, (eGender == VOIPNARRATE_GENDER_FEMALE) ? "en-US_AllisonVoice" : "en-US_MichaelVoice"); + return(pUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatBodyWatson + + \Description + Format request body for IBM Watson Speech service + + \Input *pVoipNarrate - pointer to module state + \Input *pBody - [out] buffer to hold request body + \Input iBodyLen - buffer length + \Input *pText - pointer to text request + + \Output + int32_t - negative=failure, else success + + \Version 11/07/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_VoipNarrateFormatBodyWatson(VoipNarrateRefT *pVoipNarrate, char *pBody, int32_t iBodyLen, const char *pText) +{ + JsonInit(pBody, iBodyLen, JSON_FL_WHITESPACE); + JsonAddStr(pBody, "text", pText); + pBody = JsonFinish(pBody); + return(pBody); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatHeadMicrosoft + + \Description + Format connection header for Microsoft Speech service + + \Input *pVoipNarrate - pointer to module state + \Input *pUrl - [out] buffer for formatted url + \Input iUrlLen - length of url buffer + \Input *pHead - [out] buffer for formatted request header + \Input iHeadLen - length of header buffer + + \Output + int32_t - negative=failure, else success + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipNarrateFormatHeadMicrosoft(VoipNarrateRefT *pVoipNarrate, char *pUrl, int32_t iUrlLen, char *pHead, int32_t iHeadLen) +{ + int32_t iOffset=0; + + // format request header + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "Content-Type: application/ssml+xml\r\n"); + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "X-Microsoft-OutputFormat: raw-%dkhz-16bit-mono-pcm\r\n", VOIPNARRATE_SAMPLERATE/1000); + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "Ocp-Apim-Subscription-Key: %s\r\n", pVoipNarrate->Config.strKey); + + // copy url + ds_strnzcpy(pUrl, pVoipNarrate->Config.strUrl, iUrlLen); + return(pUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatBodyMicrosoft + + \Description + Format request body for Microsoft Speech service + + \Input *pVoipNarrate - pointer to module state + \Input *pBody - [out] buffer to hold request body + \Input iBodyLen - buffer length + \Input eGender - preferred gender for voice narration + \Input *pText - pointer to text request + + \Output + int32_t - negative=failure, else success + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_VoipNarrateFormatBodyMicrosoft(VoipNarrateRefT *pVoipNarrate, char *pBody, int32_t iBodyLen, VoipNarrateGenderE eGender, const char *pText) +{ + int32_t iOffset=0; + // format request body + iOffset += ds_snzprintf(pBody+iOffset, iBodyLen-iOffset, ""); + iOffset += ds_snzprintf(pBody+iOffset, iBodyLen-iOffset, "%s", + (eGender == VOIPNARRATE_GENDER_FEMALE) ? "JessaRUS" : "BenjaminRUS", pText); + iOffset += ds_snzprintf(pBody+iOffset, iBodyLen-iOffset, ""); + // return pointer to body + return(pBody); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatHeadGoogle + + \Description + Format connection header for Google Text to Speech + + \Input *pVoipNarrate - pointer to module state + \Input *pUrl - [out] buffer for formatted url + \Input iUrlLen - length of url buffer + \Input *pHead - [out] buffer for formatted request header + \Input iHeadLen - length of header buffer + + \Output + int32_t - negative=failure, else success + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipNarrateFormatHeadGoogle(VoipNarrateRefT *pVoipNarrate, char *pUrl, int32_t iUrlLen, char *pHead, int32_t iHeadLen) +{ + // format request header + *pHead = '\0'; + // format request url + ds_snzprintf(pUrl, iUrlLen, "%s?key=%s", pVoipNarrate->Config.strUrl, pVoipNarrate->Config.strKey); + // return url + return(pUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatBodyGoogle + + \Description + Format request body for Google text-to-speech request + + \Input *pVoipNarrate - pointer to module state + \Input *pBody - [out] buffer to hold request body + \Input iBodyLen - buffer length + \Input eGender - preferred gender for voice narration + \Input *pText - pointer to text request + + \Output + int32_t - negative=failure, else success + + \Notes + Ref: https://cloud.google.com/text-to-speech/docs/reference/rest/ + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static char * _VoipNarrateFormatBodyGoogle(VoipNarrateRefT *pVoipNarrate, char *pBody, int32_t iBodyLen, VoipNarrateGenderE eGender, const char *pText) +{ + static const char *_strGender[VOIPNARRATE_NUMGENDERS] = { "SSML_VOICE_GENDER_UNSPECIFIED", "FEMALE", "MALE", "NEUTRAL" }; + static const char *_strVoice[VOIPNARRATE_NUMGENDERS] = { "en-US-Standard-D", "en-US-Standard-C", "en-US-Standard-B", "en-US-Standard-D" }; + JsonInit(pBody, iBodyLen, JSON_FL_WHITESPACE); + + JsonObjectStart(pBody, "input"); + JsonAddStr(pBody, "text", pText); + JsonObjectEnd(pBody); + + JsonObjectStart(pBody, "voice"); + JsonAddStr(pBody, "languageCode", "en-US"); + JsonAddStr(pBody, "name", _strVoice[eGender]); + JsonAddStr(pBody, "ssmlGender", _strGender[eGender]); // we specify gender here, but it is unclear if it does anything + JsonObjectEnd(pBody); + + JsonObjectStart(pBody, "audioConfig"); + JsonAddStr(pBody, "audioEncoding", "LINEAR16"); + JsonAddInt(pBody, "sampleRateHertz", VOIPNARRATE_SAMPLERATE); + JsonObjectEnd(pBody); + + pBody = JsonFinish(pBody); + return(pBody); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatHeadAmazon + + \Description + Format connection header for Amazon Polly + + \Input *pVoipNarrate - pointer to module state + \Input *pUrl - [out] buffer for formatted url + \Input iUrlLen - length of url buffer + \Input *pHead - [out] buffer for formatted request header + \Input iHeadLen - length of header buffer + + \Output + int32_t - negative=failure, else success + + \Version 11/21/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipNarrateFormatHeadAmazon(VoipNarrateRefT *pVoipNarrate, char *pUrl, int32_t iUrlLen, char *pHead, int32_t iHeadLen) +{ + int32_t iOffset=0; + // format request header + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "Content-Type: application/json\r\n"); + iOffset += ds_snzprintf(pHead+iOffset, iHeadLen-iOffset, "Accept: audio/wav; rate=%d\r\n", VOIPNARRATE_SAMPLERATE); + // copy url + ds_strnzcpy(pUrl, pVoipNarrate->Config.strUrl, iUrlLen); + return(pUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateFormatBodyAmazon + + \Description + Format request body for Amazon Polly + + \Input *pVoipNarrate - pointer to module state + \Input *pBody - [out] buffer to hold request body + \Input iBodyLen - buffer length + \Input eGender - preferred gender for voice narration + \Input *pText - pointer to text request + + \Output + int32_t - negative=failure, else success + + \Version 12/21/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static char *_VoipNarrateFormatBodyAmazon(VoipNarrateRefT *pVoipNarrate, char *pBody, int32_t iBodyLen, VoipNarrateGenderE eGender, const char *pText) +{ + JsonInit(pBody, iBodyLen, JSON_FL_WHITESPACE); + JsonAddStr(pBody, "OutputFormat", "pcm"); + JsonAddStr(pBody, "Text", pText); + JsonAddStr(pBody, "VoiceId", (eGender == VOIPNARRATE_GENDER_FEMALE) ? "Joanna" : "Joey"); + pBody = JsonFinish(pBody); + return(pBody); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateStreamCallbackGoogle + + \Description + Decode Base64-encoded voice data + + \Input *pVoipNarrate - pointer to module state + \Input eStatus - ProtoStream status + \Input *pData - base64-encoded input data + \Input iDataSize - input buffer length + + \Output + int32_t - negative=failure, else number of input bytes consumed + + \Version 10/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateStreamCallbackGoogle(VoipNarrateRefT *pVoipNarrate, ProtoStreamStatusE eStatus, const uint8_t *pData, int32_t iDataSize) +{ + int32_t iDataRead, iDataDecoded; + + // submit any base64-decoded data we have first + if (pVoipNarrate->iVoiceLen > 0) + { + // if start of stream, see if we need to skip WAV header + if (pVoipNarrate->bStart) + { + pVoipNarrate->iVoiceOff = _VoipNarrateSkipWavHeader(pVoipNarrate->aVoiceBuf, pVoipNarrate->iVoiceLen); + pVoipNarrate->iVoiceLen -= pVoipNarrate->iVoiceOff; + pVoipNarrate->bStart = FALSE; + } + // pass data to user + iDataRead = pVoipNarrate->pVoiceDataCb(pVoipNarrate, pVoipNarrate->iUserIndex, (const int16_t *)(pVoipNarrate->aVoiceBuf+pVoipNarrate->iVoiceOff), pVoipNarrate->iVoiceLen, pVoipNarrate->pUserData); + // mark data as read + pVoipNarrate->iVoiceOff += iDataRead; + pVoipNarrate->iVoiceLen -= iDataRead; + } + // if we don't have data to decode, or we still have decoded voice data that hasn't been consumed yet, don't decode more + if ((pVoipNarrate->iVoiceLen > 0) || (iDataSize <= 0)) + { + return(0); + } + pVoipNarrate->iVoiceOff = 0; + + // base64-decode voice data + if ((iDataRead = _VoipNarrateBase64Decode(pVoipNarrate, (char *)pVoipNarrate->aVoiceBuf, (iDataDecoded = sizeof(pVoipNarrate->aVoiceBuf), &iDataDecoded), (const char *)pData, iDataSize)) >= 0) + { + pVoipNarrate->iVoiceLen = iDataDecoded; + pVoipNarrate->iSamplesInPhrase += iDataDecoded; + pData = pVoipNarrate->aVoiceBuf; + } + else + { + NetPrintf(("voipnarrate: error; could not base64 decode data\n")); + NetPrintMem(pData, iDataSize, "base64 data"); + pVoipNarrate->Metrics.uErrorCount += 1; + } + return(iDataRead); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateStreamCallback + + \Description + Receive streamed voice data and submit it to callback + + \Input *pProtoStream - ProtoStream module state + \Input eStatus - ProtoStream status + \Input *pData - base64-encoded input data + \Input iDataSize - input buffer length + \Input *pUserData - callback user data (VoipNarrate module ref) + + \Output + int32_t - negative=failure, else number of input bytes consumed + + \Version 10/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateStreamCallback(ProtoStreamRefT *pProtoStream, ProtoStreamStatusE eStatus, const uint8_t *pData, int32_t iDataSize, void *pUserData) +{ + VoipNarrateRefT *pVoipNarrate = (VoipNarrateRefT *)pUserData; + int32_t iDataRead, iResult; + char strError[256] = ""; + + // handle start callback notification + if (eStatus == PROTOSTREAM_STATUS_BEGIN) + { + pVoipNarrate->iSamplesInPhrase = 0; + pVoipNarrate->Metrics.uDelay += NetTickDiff(NetTick(), pVoipNarrate->uTtsStartTime); + pVoipNarrate->pVoiceDataCb(pVoipNarrate, pVoipNarrate->iUserIndex, (const int16_t *)pData, VOIPNARRATE_STREAM_START, pVoipNarrate->pUserData); + } + // handle end callback notification + if (eStatus == PROTOSTREAM_STATUS_DONE) + { + // save metrics + int32_t iPhraseDuration = ((pVoipNarrate->iSamplesInPhrase * 1000) / VOIPNARRATE_SAMPLERATE); + pVoipNarrate->Metrics.uDurationMsRecv += iPhraseDuration; + if (iPhraseDuration < VOIPNARRATE_EMPTY_AUDIO_THRESHOLD_MS) + { + pVoipNarrate->Metrics.uEmptyResultCount += 1; + } + + // signal end of stream + pVoipNarrate->pVoiceDataCb(pVoipNarrate, pVoipNarrate->iUserIndex, (const int16_t *)pData, VOIPNARRATE_STREAM_END, pVoipNarrate->pUserData); + pVoipNarrate->bActive = FALSE; + + // check for a completion result that is not successful, and log error response (if any) to debug output + if ((iResult = ProtoStreamStatus(pProtoStream, 'code', NULL, 0)) != PROTOHTTP_RESPONSE_SUCCESSFUL) + { + ProtoStreamStatus(pProtoStream, 'serr', strError, sizeof(strError)); + NetPrintf(("voipnarrate: stream failed with http result %d:\n%s\n", iResult, strError)); + pVoipNarrate->Metrics.uErrorCount += 1; + } + } + + // read data and pass it to callback, processing as necessary + for (iDataRead = 0, iResult = 1; (iResult > 0) && (iDataSize > 0); ) + { + if (pVoipNarrate->Config.eProvider != VOIPNARRATE_PROVIDER_GOOGLE) + { + // if start of stream, see if we need to skip WAV header + if ((pVoipNarrate->bStart) && (iDataSize > 0)) + { + iDataRead = _VoipNarrateSkipWavHeader(pData, iDataSize); + pData += iDataRead; + iDataSize -= iDataRead; + pVoipNarrate->bStart = FALSE; + } + iResult = pVoipNarrate->pVoiceDataCb(pVoipNarrate, pVoipNarrate->iUserIndex, (const int16_t *)pData, iDataSize, pVoipNarrate->pUserData); + pVoipNarrate->iSamplesInPhrase += iResult; + } + else + { + // google-specific processing to deal with base64 encoded audio + iResult = _VoipNarrateStreamCallbackGoogle(pVoipNarrate, eStatus, pData, iDataSize); + } + + iDataRead += iResult; + iDataSize -= iResult; + pData += iResult; + } + return(iDataRead); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateStart + + \Description + Receive streamed voice data and submit it to callback + + \Input *pVoipNarrate - pointer to module state + \Input iUserIndex - local user index of user who is requesting speech synthesis + \Input eGender - preferred gender for voice for narration + \Input *pText - pointer to text request + + \Output + int32_t - ProtoStream request result + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipNarrateStart(VoipNarrateRefT *pVoipNarrate, int32_t iUserIndex, VoipNarrateGenderE eGender, const char *pText) +{ + const char *pUrl, *pReq; + char strUrl[256]; + int32_t iResult; + + // format header/url and request body + if (pVoipNarrate->Config.eProvider == VOIPNARRATE_PROVIDER_MICROSOFT) + { + pUrl = _VoipNarrateFormatHeadMicrosoft(pVoipNarrate, strUrl, sizeof(strUrl), pVoipNarrate->strHead, sizeof(pVoipNarrate->strHead)); + pReq = _VoipNarrateFormatBodyMicrosoft(pVoipNarrate, pVoipNarrate->strBody, sizeof(pVoipNarrate->strBody), eGender, pText); + } + else if (pVoipNarrate->Config.eProvider == VOIPNARRATE_PROVIDER_GOOGLE) + { + pUrl = _VoipNarrateFormatHeadGoogle(pVoipNarrate, strUrl, sizeof(strUrl), pVoipNarrate->strHead, sizeof(pVoipNarrate->strHead)); + pReq = _VoipNarrateFormatBodyGoogle(pVoipNarrate, pVoipNarrate->strBody, sizeof(pVoipNarrate->strBody), eGender, pText); + } + else if (pVoipNarrate->Config.eProvider == VOIPNARRATE_PROVIDER_IBMWATSON) + { + pUrl = _VoipNarrateFormatHeadWatson(pVoipNarrate, strUrl, sizeof(strUrl), pVoipNarrate->strHead, sizeof(pVoipNarrate->strHead), eGender); + pReq = _VoipNarrateFormatBodyWatson(pVoipNarrate, pVoipNarrate->strBody, sizeof(pVoipNarrate->strBody), pText); + } + else if (pVoipNarrate->Config.eProvider == VOIPNARRATE_PROVIDER_AMAZON) + { + pUrl = _VoipNarrateFormatHeadAmazon(pVoipNarrate, strUrl, sizeof(strUrl), pVoipNarrate->strHead, sizeof(pVoipNarrate->strHead)); + pReq = _VoipNarrateFormatBodyAmazon(pVoipNarrate, pVoipNarrate->strBody, sizeof(pVoipNarrate->strBody), eGender, pText); + } + else + { + NetPrintf(("voipnarrate: undefined provider\n")); + return(-1); + } + NetPrintfVerbose((pVoipNarrate->iVerbose, 1, "voipnarrate: request body\n%s\n", pReq)); + pVoipNarrate->pBody = pReq; + + // set request header + ProtoStreamControl(pVoipNarrate->pProtoStream, 'apnd', 0, 0, pVoipNarrate->strHead); + + pVoipNarrate->Metrics.uEventCount += 1; + pVoipNarrate->Metrics.uCharCountSent += (uint32_t)strlen(pText); + pVoipNarrate->uTtsStartTime = NetTick(); + + // make the request + if ((iResult = ProtoStreamOpen2(pVoipNarrate->pProtoStream, pUrl, pReq, PROTOSTREAM_FREQ_ONCE)) >= 0) + { + // mark as stream start and active + pVoipNarrate->bStart = pVoipNarrate->bActive = TRUE; + } + else + { + NetPrintf(("voipnarrate: failed to open stream\n")); + pVoipNarrate->Metrics.uErrorCount += 1; + } + + // return to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipNarrateConfig + + \Description + Configure the VoipNarrate module + + \Input *pVoipNarrate - pointer to module state + \Input *pConfig - module configuration to set + + \Output + uint32_t - TRUE if configured successfully + + \Version 11/07/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _VoipNarrateConfig(VoipNarrateRefT *pVoipNarrate, VoipNarrateConfigT *pConfig) +{ + uint8_t uRet = TRUE; + NetCritEnter(NULL); + if (pConfig->eProvider != VOIPNARRATE_PROVIDER_NONE) + { + ds_memcpy_s(&pVoipNarrate->Config, sizeof(pVoipNarrate->Config), pConfig, sizeof(*pConfig)); + } + else + { + NetPrintfVerbose((pVoipNarrate->iVerbose, 0, "voipnarrate: narration disabled\n")); + ds_memclr(&pVoipNarrate->Config, sizeof(pVoipNarrate->Config)); + uRet = FALSE; + } + NetCritLeave(NULL); + return(uRet); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipNarrateCreate + + \Description + Create the narration module + + \Input *pVoiceDataCb - callback used to provide voice data + \Input *pUserData - callback user data + + \Output + VoipNarrateRefT * - new module state, or NULL + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +VoipNarrateRefT *VoipNarrateCreate(VoipNarrateVoiceDataCbT *pVoiceDataCb, void *pUserData) +{ + VoipNarrateRefT *pVoipNarrate; + int32_t iMemGroup; + void *pMemGroupUserData; + + // query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // validate callback + if (pVoiceDataCb == NULL) + { + NetPrintf(("voipnarrate: could not create module with null callback\n")); + return(NULL); + } + + // allocate and init module state + if ((pVoipNarrate = DirtyMemAlloc(sizeof(*pVoipNarrate), VOIPNARRATE_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voipnarrate: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pVoipNarrate, sizeof(*pVoipNarrate)); + pVoipNarrate->iMemGroup = iMemGroup; + pVoipNarrate->pMemGroupUserData = pMemGroupUserData; + pVoipNarrate->pVoiceDataCb = pVoiceDataCb; + pVoipNarrate->pUserData = pUserData; + pVoipNarrate->iVerbose = 1; + + // allocate streaming module with a buffer to hold up to 1s of 16khz 16bit streaming audio + if ((pVoipNarrate->pProtoStream = ProtoStreamCreate(16*2*1024)) == NULL) + { + VoipNarrateDestroy(pVoipNarrate); + return(NULL); + } + // set protostream callback with a 20ms call rate + ProtoStreamSetCallback(pVoipNarrate->pProtoStream, 20, _VoipNarrateStreamCallback, pVoipNarrate); + // set protostream minimum data amount (for base64 decoding; four is the minimum amount but that produces one and a half samples, so we choose eight) + ProtoStreamControl(pVoipNarrate->pProtoStream, 'minb', 8, 0, NULL); + // set protostream debug level + ProtoStreamControl(pVoipNarrate->pProtoStream, 'spam', 1, 0, NULL); + // set keepalive + ProtoStreamControl(pVoipNarrate->pProtoStream, 'keep', 1, 0, NULL); + // set protostream http custom header callback, used to sign AWS requests + ProtoStreamSetHttpCallback(pVoipNarrate->pProtoStream, _VoipNarrateCustomHeaderCb, NULL, pVoipNarrate); + + // configure for particular provider + if (!_VoipNarrateConfig(pVoipNarrate, &_VoipNarrate_Config)) + { + NetPrintf(("voipnarrate: could not configure for provider\n")); + VoipNarrateDestroy(pVoipNarrate); + return(NULL); + } + + // return ref to caller + return(pVoipNarrate); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateConfig + + \Description + Set global state to configure the VoipNarrate modules + + \Input eProvider - VOIPNARRATE_PROVIDER_* (VOIPNARRATE_PROVIDER_NONE to disable) + \Input *pUrl - pointer to url to use for tts requests + \Input *pKey - pointer to authentication key to use for tts requests + + \Version 11/07/2018 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipNarrateConfig(VoipNarrateProviderE eProvider, const char *pUrl, const char *pKey) +{ + NetCritEnter(NULL); + _VoipNarrate_Config.eProvider = eProvider; + ds_strnzcpy(_VoipNarrate_Config.strUrl, pUrl, sizeof(_VoipNarrate_Config.strUrl)); + ds_strnzcpy(_VoipNarrate_Config.strKey, pKey, sizeof(_VoipNarrate_Config.strKey)); + NetCritLeave(NULL); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateDestroy + + \Description + Destroy the VoipNarrate module + + \Input *pVoipNarrate - pointer to module state + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipNarrateDestroy(VoipNarrateRefT *pVoipNarrate) +{ + // destroy protostream module, if allocated + if (pVoipNarrate->pProtoStream != NULL) + { + ProtoStreamDestroy(pVoipNarrate->pProtoStream); + } + // release any queued requests + while (pVoipNarrate->pRequest != NULL) + { + _VoipNarrateRequestGet(pVoipNarrate, NULL); + } + // dispose of module memory + DirtyMemFree(pVoipNarrate, VOIPNARRATE_MEMID, pVoipNarrate->iMemGroup, pVoipNarrate->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateInput + + \Description + Input text to be convert to speech + + \Input *pVoipNarrate - pointer to module state + \Input iUserIndex - local user index of user who is requesting speech synthesis + \Input eGender - preferred gender for voice narration + \Input *pText - text to be converted + + \Output + int32_t - zero=success, otherwise=failure + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipNarrateInput(VoipNarrateRefT *pVoipNarrate, int32_t iUserIndex, VoipNarrateGenderE eGender, const char *pText) +{ + // make sure a provider is configured + if (pVoipNarrate->Config.eProvider == VOIPNARRATE_PROVIDER_NONE) + { + NetPrintfVerbose((pVoipNarrate->iVerbose, 0, "voipnarrate: no provider configured\n")); + return(-1); + } + // handle if there is already narration ongoing + if (pVoipNarrate->bActive) + { + NetPrintfVerbose((pVoipNarrate->iVerbose, 1, "voipnarrate: queueing request '%s'\n", pText)); + return(_VoipNarrateRequestAdd(pVoipNarrate, iUserIndex, eGender, pText)); + } + // if ready, start the request + return(_VoipNarrateStart(pVoipNarrate, iUserIndex, eGender, pText)); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateStatus + + \Description + Get module status. + + \Input *pVoipNarrate - pointer to module state + \Input iStatus - status selector + \Input iValue - selector specific + \Input *pBuffer - selector specific + \Input iBufSize - selector specific + + \Output + int32_t - selector specific + + \Notes + Other status codes are passed down to the stream transport handler. + + \verbatim + 'ttsm' - get the VoipTextToSpeechMetricsT via pBuffer + \endverbatim + + \Version 11/15/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipNarrateStatus(VoipNarrateRefT *pVoipNarrate, int32_t iStatus, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + if (iStatus == 'ttsm') + { + if ((pBuffer != NULL) && (iBufSize >= (int32_t)sizeof(VoipTextToSpeechMetricsT))) + { + ds_memcpy_s(pBuffer, iBufSize, &pVoipNarrate->Metrics, sizeof(VoipTextToSpeechMetricsT)); + return(0); + } + return(-1); + } + return(ProtoStreamStatus(pVoipNarrate->pProtoStream, iStatus, pBuffer, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateControl + + \Description + Set control options + + \Input *pVoipNarrate - pointer to module state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iStatus can be one of the following: + + \verbatim + 'ctsm' - clear text to speech metrics in VoipTextToSpeechMetricsT + 'spam' - set verbose debug level (debug only) + \endverbatim + + Unhandled codes are passed through to the stream transport handler + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipNarrateControl(VoipNarrateRefT *pVoipNarrate, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + if (iControl == 'ctsm') + { + ds_memclr(&(pVoipNarrate->Metrics), sizeof(pVoipNarrate->Metrics)); + return(0); + } + #if DIRTYCODE_LOGGING + // set verbosity for us and pass through to stream transport handler + if (iControl == 'spam') + { + pVoipNarrate->iVerbose = iValue; + } + #endif + // if not handled, let stream transport handler take a stab at it + return(ProtoStreamControl(pVoipNarrate->pProtoStream, iControl, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function VoipNarrateUpdate + + \Description + Update the narration module + + \Input *pVoipNarrate - pointer to module state + + \Version 10/25/2018 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipNarrateUpdate(VoipNarrateRefT *pVoipNarrate) +{ + // see if we need to start a queued narration request + if ((pVoipNarrate->pRequest != NULL) && !pVoipNarrate->bActive) + { + VoipNarrateRequestT Request; + _VoipNarrateRequestGet(pVoipNarrate, &Request); + _VoipNarrateStart(pVoipNarrate, Request.iUserIndex, Request.eGender, Request.strText); + } + // give life to stream module + ProtoStreamUpdate(pVoipNarrate->pProtoStream); +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voippacket.h b/r5dev/thirdparty/dirtysdk/source/voip/voippacket.h new file mode 100644 index 00000000..b7ba2061 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voippacket.h @@ -0,0 +1,162 @@ +/*H********************************************************************************/ +/*! + \File voippacket.h + + \Description + VoIP data packet definitions. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 03/17/2004 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +#ifndef _voippacket_h +#define _voippacket_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/dirtysock/dirtynet.h" + +/*** Defines **********************************************************************/ + +//! maximum voice payload size +#define VOIP_MAXMICRPKTSIZE (SOCKET_MAXUDPRECV - sizeof(VoipPacketHeadT) - sizeof(VoipMicrInfoT)) + +//! maximum ping payload size +#define VOIP_MAXPINGPKTSIZE (SOCKET_MAXUDPRECV - sizeof(VoipPacketHeadT) - 4 /* aRemoteClientId */ - 4 /* aChannelUserIndices */) + +//! VoIP Flags bit field included in VoIP packets +#define VOIP_PACKET_STATUS_FLAG_METADATA (1) //!< packet contains platform-specific metadata (unreliable) +#define VOIP_PACKET_STATUS_FLAG_STT (2) //!< at least one user on the originating side as requested transcribed text to be sent (Speech-to-text accessibility feature) +#define VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF (32) //!< packet contains ACK+ACKCNF protocol components (reliability mechanism) +#define VOIP_PACKET_RELIABLE_FLAG_DATA (64) //!< packet contains DATA protocol component (reliability mechanism) + +//! range of supported reliable data types +#define VOIP_RELIABLE_TYPE_USERADD (0) //!< reliable data contains newly added local user on the originating console (used for voip join-in-progress) +#define VOIP_RELIABLE_TYPE_USERREM (1) //!< reliable data contains newly removed local user on the originating console (used for voip leave-in-progress) +#define VOIP_RELIABLE_TYPE_OPAQUE (2) //!< reliable data contains platform-specific data blob that needs to be transmitted reliably +#define VOIP_RELIABLE_TYPE_TEXT (3) //!< reliable data contains transcribed text (Speech-to-Text) that needs to be sent reliably + +//! mask used to manipulate ACKCNF bit for uAckedSeq field of ReliableAckT type +#define VOIP_RELIABLE_ACKCNF_MASK (0x80) + + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ +//! VoIP data packet header +typedef struct VoipPacketHeadT +{ + uint8_t aType[3]; //!< type/version info + uint8_t uFlags; //!< flags + uint8_t aClientId[4]; //!< source clientId + uint8_t aSessionId[4]; //!< session ID + uint8_t aHeadsetStat[2]; //!< headset status bitfield (1 bit per local user) +} VoipPacketHeadT; + + +//! VoIP channel config +typedef struct VoipPacketChanT +{ + uint8_t aChannelId[4]; //!< packet channelId +} VoipPacketChanT; + +//! VoIP user packet +typedef struct VoipUserPacketT +{ + uint8_t aAccountId[8]; //!< the nucleus account id (unique per account) + uint8_t aPersonaId[8]; //!< the nucleus persona id (globally unique) + uint8_t aFlags[4]; //!< a bit field contaning VOIPUSER_FLAG_* + uint8_t aPlatform[4]; //!< what platform is this user running on +} VoipUserPacketT; + +//! VoIP connection packet +typedef struct VoipConnPacketT +{ + VoipPacketHeadT Head; + uint8_t aRemoteClientId[4]; + uint8_t bConnected; + uint8_t uNumLocalUsers; // this is the max number of users including the extended user + VoipUserPacketT LocalUsers[32]; +} VoipConnPacketT; + +//! VoIP ping packet +typedef struct VoipPingPacketT +{ + VoipPacketHeadT Head; + uint8_t aRemoteClientId[4]; + uint8_t aChannelUserIndices[4]; //!< a bitset that corresponds to the user indices for the channel data in aData + uint8_t aData[VOIP_MAXPINGPKTSIZE]; //!< ping packet data (this is where piggy-backed channel & reliable data is added when needed) +} VoipPingPacketT; + +//! VoIP disc packet +typedef struct VoipDiscPacketT +{ + VoipPacketHeadT Head; + uint8_t aRemoteClientId[4]; +} VoipDiscPacketT; + +//! VoIP micr packet info +typedef struct VoipMicrInfoT +{ + uint8_t aSendMask[4]; //!< lsb->msb, one bit per user, used for voip server + VoipPacketChanT channels; //!< packet channelId + uint8_t uSeqn; //!< voice packet sequence number + uint8_t uNumSubPackets; //!< current number of sub-packets packed in this packet + uint8_t uSubPacketSize; //!< size of sub-packets (same fixed-size for all sub-packets) + uint8_t uUserIndex; //!< index of user that generated all the sub-packets in this packet +} VoipMicrInfoT; + +//! VoIP data packet +typedef struct VoipMicrPacketT +{ + VoipPacketHeadT Head; //!< packet header + VoipMicrInfoT MicrInfo; //!< micr packet info + uint8_t aData[VOIP_MAXMICRPKTSIZE]; //!< voice packet data (this is where sub-packets are bundled) +} VoipMicrPacketT; + +//! VoipPacketBufferT: a buffer that can(does) contain any type of voip packet +typedef union VoipPacketBufferT +{ + VoipPacketHeadT VoipPacketHead; + VoipConnPacketT VoipConnPacket; + VoipPingPacketT VoipPingPacket; + VoipDiscPacketT VoipDiscPacket; + VoipMicrPacketT VoipMicrPacket; +} VoipPacketBufferT; + +//! reliable data info +typedef struct ReliableDataInfoT +{ + uint8_t aRemoteClientId[4]; //!< remote peer to which this data entry is for (required for 1x send to voipserver) + uint8_t uSeq; //!< sequence number + uint8_t uType; //!< type (VOIP_RELIABLE_TYPE_*) + uint8_t uSize[2]; //!< size of payload +} ReliableDataInfoT; + +//! reliable mechanism - ack entry +typedef struct ReliableAckT +{ + uint8_t aRemoteClientId[4]; //!< remote peer to which this ack entry is for + uint8_t uAckedSeq; //!< bit 7 = ACKCNF flag; bit 0 to 6 = [1,127] seq number (seq nb 0 is invalid - used when only the ACKCNF portion is valid) +} ReliableAckT; + +//! maximum size of a single reliable data entry (in bytes) is what can fit in a PING packet (taking into account that there can be up to 32 ReliableAckT entries before it) +//! assumption: very large reliable data entries may not fit in MIC packets (because of prefixed metadata) but will eventually be sent over a PING packet. +#define VOIP_MAXRELIABLEDATA (VOIP_MAXPINGPKTSIZE - (VOIP_MAXCONNECT * sizeof(ReliableAckT))) + +//! reliable mechanism - data entry +typedef struct ReliableDataT +{ + ReliableDataInfoT info; + uint8_t aData[VOIP_MAXRELIABLEDATA]; +} ReliableDataT; + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#endif // _voippacket_h + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voippcm.c b/r5dev/thirdparty/dirtysdk/source/voip/voippcm.c new file mode 100644 index 00000000..8569d5d9 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voippcm.c @@ -0,0 +1,300 @@ +/*H********************************************************************************/ +/*! + \File voippcm.c + + \Description + PCM codec (ie, pass-through). + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 08/04/2004 (jbrookes) First version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" + +#include "DirtySDK/voip/voipdef.h" +#include "voippriv.h" +#include "voipcommon.h" +#include "voippcm.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +typedef struct VoipPCMStateT +{ + VoipCodecRefT CodecState; + + int32_t iOutputVolume; +} VoipPCMStateT; + +/*** Function Prototypes **********************************************************/ + +static VoipCodecRefT *_VoipPCMCreate(int32_t iNumDecoders); +static void _VoipPCMDestroy(VoipCodecRefT *pState); +static int32_t _VoipPCMEncodeBlock(VoipCodecRefT *pState, uint8_t *pOut, const int16_t *pInp, int32_t iNumSamples); +static int32_t _VoipPCMDecodeBlock(VoipCodecRefT *pState, int32_t *pOut, const uint8_t *pInp, int32_t iInputBytes, int32_t iChannel); +static void _VoipPCMReset(VoipCodecRefT *pState); +static int32_t _VoipPCMControl(VoipCodecRefT *pState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); +static int32_t _VoipPCMStatus(VoipCodecRefT *pCodecState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize); + +/*** Variables ********************************************************************/ + +// Public variables + +const VoipCodecDefT VoipPCM_CodecDef = +{ + _VoipPCMCreate, + _VoipPCMDestroy, + _VoipPCMEncodeBlock, + _VoipPCMDecodeBlock, + _VoipPCMReset, + _VoipPCMControl, + _VoipPCMStatus, +}; + +/*** Private Functions ************************************************************/ + + +/*F*************************************************************************************************/ +/*! + \Function _VoipPCMCreate + + \Description + Create a PCM codec state. + + \Input iDecodeChannels - number of decoder channels + + \Output + VoipCodecStateT * - pointer to pcm codec state + + \Version 08/04/2004 (jbrookes) +*/ +/*************************************************************************************************F*/ +static VoipCodecRefT *_VoipPCMCreate(int32_t iDecodeChannels) +{ + VoipPCMStateT *pState; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + pState = (VoipPCMStateT *) DirtyMemAlloc (sizeof(*pState), VOIP_MEMID, iMemGroup, pMemGroupUserData); + pState->CodecState.pCodecDef = &VoipPCM_CodecDef; + pState->CodecState.iDecodeChannels = iDecodeChannels; + + // set the output level + pState->iOutputVolume = 1 << VOIP_CODEC_OUTPUT_FRACTIONAL; + + return(&pState->CodecState); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipPCMDestroy + + \Description + Destroy a PCM codec state. + + \Input *pState - state to destroy + + \Version 08/04/2004 (jbrookes) +*/ +/*************************************************************************************************F*/ +static void _VoipPCMDestroy(VoipCodecRefT *pState) +{ + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + DirtyMemFree(pState, VOIP_MEMID, iMemGroup, pMemGroupUserData); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipPCMEncodeBlock + + \Description + Encode a 16-bit linear PCM buffer. + + \Input *pState - pointer to encode state + \Input *pOut - pointer to output buffer + \Input *pInp - pointer to input buffer + \Input iNumSamples - number of samples to encode + + \Output int32_t - size of compressed data in bytes + + \Version 08/04/2004 (jbrookes) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipPCMEncodeBlock(VoipCodecRefT *pState, uint8_t *pOut, const int16_t *pInp, int32_t iNumSamples) +{ + int32_t iCount; + uint8_t *pBeg = pOut; + + // copy data to output buffer + for (iCount = 0; iCount < iNumSamples; iCount += 1) + { + *pOut++ = (pInp[iCount] & 0xff00) >> 8; + *pOut++ = (pInp[iCount] & 0xff); + } + + // return the length + return(pOut - pBeg); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipPCMDecodeBlock + + \Description + Decode input to 16-bit linear PCM samples, and accumulate in the given output buffer. + + \Input *pState - pointer to decode state + \Input *pOut - pointer to output buffer + \Input *pInp - pointer to input buffer + \Input iInputBytes - size of input data + \Input iChannel - ignored + + \Output + int32_t - number of samples decoded + + \Version 08/04/2004 (jbrookes) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipPCMDecodeBlock(VoipCodecRefT *pState, int32_t *pOut, const uint8_t *pInp, int32_t iInputBytes, int32_t iChannel) +{ + VoipPCMStateT *pPCMState = (VoipPCMStateT *)pState; + int32_t iCount, iSample; + + // decode input and add it to output + for (iCount = 0; iCount < iInputBytes/2; iCount++) + { + // read sample + iSample = *pInp++ << 8; + iSample |= *pInp++; + + // sign extend + iSample = (iSample << 16) >> 16; + + // write out with output scaling + pOut[iCount] += ((iSample * pPCMState->iOutputVolume) >> VOIP_CODEC_OUTPUT_FRACTIONAL); + } + + // return the number of samples decoded + return(iCount); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipPCMReset + + \Description + Resets codec state. + + \Input *pState - pointer to decode state + + \Version 08/04/2004 (jbrookes) +*/ +/*************************************************************************************************F*/ +static void _VoipPCMReset(VoipCodecRefT *pState) +{ + +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipPCMControl + + \Description + Modifies parameters of the codec + + \Input *pCodecState - pointer to decode state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'plvl' - Set the output power level + \endverbatim + + \Version 03/12/2008 (grouse) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipPCMControl(VoipCodecRefT *pCodecState, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + VoipPCMStateT *pState = (VoipPCMStateT *)pCodecState; + + if (iControl == 'plvl') + { + pState->iOutputVolume = iValue; + return(0); + } + NetPrintf(("voippcm: unhandled control selector '%C'\n", iControl)); + return(-1); +} + +/*F*************************************************************************************************/ +/*! + \Function _VoipPCMStatus + + \Description + Get codec status + + \Input *pCodecState - pointer to decode state + \Input iSelect - status selector + \Input iValue - selector-specific + \Input *pBuffer - [out] storage for selector output + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iSelect can be one of the following: + + \verbatim + 'fsiz' - size of encoder output / decoder input in bytes (iValue=samples per frame) + \endverbatim + + \Version 10/11/2011 (jbrookes) +*/ +/*************************************************************************************************F*/ +static int32_t _VoipPCMStatus(VoipCodecRefT *pCodecState, int32_t iSelect, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + VoipPCMStateT *pModuleState = (VoipPCMStateT *)pCodecState; + + // these options require module state + if (pModuleState != NULL) + { + if (iSelect == 'fsiz') + { + return(iValue*2); + } + } + NetPrintfVerbose((pModuleState->CodecState.iDebugLevel, 1, "voippcm: unhandled status selector '%C'\n", iSelect)); + return(-1); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voippcm.h b/r5dev/thirdparty/dirtysdk/source/voip/voippcm.h new file mode 100644 index 00000000..9e2cddc5 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voippcm.h @@ -0,0 +1,42 @@ +/*H********************************************************************************/ +/*! + \File voippcm.h + + \Description + Table based 16:3 ADPCM compression originally based off EAC SIMEX code, + modified by Greg Schaefer. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 08/04/2004 (jbrookes) First version +*/ +/********************************************************************************H*/ + +#ifndef _voippcm_h +#define _voippcm_h + +/*** Include files ****************************************************************/ + +#include "DirtySDK/voip/voipcodec.h" + +/*** Defines **********************************************************************/ + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/*** Variables ********************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +extern const VoipCodecDefT VoipPCM_CodecDef; + +#ifdef __cplusplus +}; +#endif + +/*** Functions ********************************************************************/ + +#endif // _voippcm_h diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voippriv.h b/r5dev/thirdparty/dirtysdk/source/voip/voippriv.h new file mode 100644 index 00000000..a1323901 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voippriv.h @@ -0,0 +1,75 @@ +/*H********************************************************************************/ +/*! + \File voippriv.h + + \Description + Header for private VoIP functions. + + \Copyright + Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED. + + \Version 1.0 03/17/2004 (jbrookes) First Version + \Version 1.1 11/18/2008 (mclouatre) Adding user data concept to mem group support +*/ +/********************************************************************************H*/ + +#ifndef _voippriv_h +#define _voippriv_h + +/*** Include files ****************************************************************/ +#include "DirtySDK/dirtysock/netconn.h" + +/*** Defines **********************************************************************/ +#define VOIPUSER_FLAG_CROSSPLAY (1<<0) //!< is this voip user running in crossplay mode? + +// enumerate supported platforms +// always add new platforms at the end and never recycle values +typedef enum VoipPlatformTypeE +{ + VOIP_PLATFORM_LINUX = 1, // linux is a stub for the stress client + VOIP_PLATFORM_PC = 2, + VOIP_PLATFORM_PS4 = 3, + VOIP_PLATFORM_XBOXONE = 4 +} VoipPlatformTypeE; + +// used to identify our local platform +#if defined(DIRTYCODE_LINUX) +#define VOIP_LOCAL_PLATFORM VOIP_PLATFORM_LINUX +#elif defined(DIRTYCODE_PC) +#define VOIP_LOCAL_PLATFORM VOIP_PLATFORM_PC +#elif defined(DIRTYCODE_PS4) +#define VOIP_LOCAL_PLATFORM VOIP_PLATFORM_PS4 +#elif defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK) // $$todo$$ should we treat xbsx as a separate platform? +#define VOIP_LOCAL_PLATFORM VOIP_PLATFORM_XBOXONE +#endif + +/*** Macros ***********************************************************************/ + +//! copy VoipUserTs +#define VOIP_CopyUser(_pUser1, _pUser2) (ds_memcpy_s(_pUser1, sizeof(*_pUser1), _pUser2, sizeof(*_pUser2))) + +//! clear VoipUserT +#define VOIP_ClearUser(_pUser1) (ds_memclr(_pUser1, sizeof(*_pUser1))) + +//! return if VoipUserT is NULL or not +#define VOIP_NullUser(_pUser1) ((_pUser1)->AccountInfo.iPersonaId == 0) + +//! compare VoipUserTs +#define VOIP_SameUser(_pUser1, _pUser2) ((_pUser1)->AccountInfo.iPersonaId == (_pUser2)->AccountInfo.iPersonaId) + +/*** Type Definitions *************************************************************/ + +typedef struct VoipUserT +{ + NetConnAccountInfoT AccountInfo; //!< account info + uint32_t uFlags; //!< a bit field contaning VOIPUSER_FLAG_* + VoipPlatformTypeE ePlatform; //!< what platform is this user running on +} VoipUserT; + + +/*** Variables ********************************************************************/ + +/*** Functions ********************************************************************/ + +#endif // _voippriv_h + diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voiptranscribe.c b/r5dev/thirdparty/dirtysdk/source/voip/voiptranscribe.c new file mode 100644 index 00000000..67685ba9 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voiptranscribe.c @@ -0,0 +1,3414 @@ +/*H********************************************************************************/ +/*! + \File voiptranscribe.c + + \Description + VoIP transcription API wrapping Cloud-based speech-to-text services, supporting + IBM Watson, Microsoft Speech Service, Google Speech, and Amazon Transcribe. + + \Notes + References + + Google Speech-to-Text: + Main page: https://cloud.google.com/speech-to-text/docs/ + REST API: https://cloud.google.com/speech-to-text/docs/reference/rest/ + gRPC API: https://cloud.google.com/speech-to-text/docs/reference/rpc/ + Protobuf definitions: https://github.com/googleapis/googleapis/blob/master/google/cloud/speech/v1/cloud_speech.proto + Audio Formats: https://cloud.google.com/speech-to-text/docs/reference/rest/v1/RecognitionConfig#AudioEncoding + + IBM Watson: + Speech to Text API: https://www.ibm.com/watson/developercloud/speech-to-text/api/v1/curl.html + HTTP interface: https://console.bluemix.net/docs/services/speech-to-text/http.html + WebSockets interface: https://console.bluemix.net/docs/services/speech-to-text/websockets.html + Audio Formats: https://console.bluemix.net/docs/services/speech-to-text/audio-formats.html + + Microsoft Speech Service: + Main page: https://docs.microsoft.com/en-us/azure/cognitive-services/Speech-Service/ + REST API: https://docs.microsoft.com/en-us/azure/cognitive-services/Speech-Service/rest-apis + Speech Service WebSocket protocol: https://docs.microsoft.com/en-us/azure/cognitive-services/speech/api-reference-rest/websocketprotocol + + Amazon Transcribe: + Main Page: https://docs.aws.amazon.com/transcribe/latest/dg/what-is-transcribe.html + Streaming Transcription: https://docs.aws.amazon.com/transcribe/latest/dg/streaming.html + StartStreamTranscription: https://docs.aws.amazon.com/transcribe/latest/dg/API_streaming_StartStreamTranscription.html + NOTE: Amazon public API documentation is as of this writing not fully correct; the streaming + format is completely different than what is described and there are other minor changes. + Streaming Format: https://docs.aws.amazon.com/transcribe/latest/dg/streaming-format.html + + Ogg/Opus: + Ogg file format: https://tools.ietf.org/html/rfc3533 + Ogg encapsulation for the Opus Audio Codec: https://tools.ietf.org/html/rfc7845.html + Definition of the Opus Audio Codec: https://tools.ietf.org/html/rfc6716 + + WAV: + WAVE file format: https://en.wikipedia.org/wiki/WAV#RIFF_WAVE + + \Copyright + Copyright 2018 Electronic Arts + + \Version 08/30/2018 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/util/aws.h" +#include "DirtySDK/util/base64.h" +#include "DirtySDK/util/jsonparse.h" + +#include "DirtySDK/proto/protohttp.h" +#include "DirtySDK/proto/protohttp2.h" +#include "DirtySDK/proto/protowebsocket.h" +#include "DirtySDK/proto/protossl.h" + +#include "DirtySDK/util/protobufcommon.h" +#include "DirtySDK/util/protobufread.h" +#include "DirtySDK/util/protobufwrite.h" + +#include "DirtySDK/crypt/cryptrand.h" //$$temp + +#include "DirtySDK/voip/voipdef.h" +#include "DirtySDK/voip/voiptranscribe.h" + +/*** Defines **********************************************************************/ + +#define VOIPTRANSCRIBE_MAXURL (1024) +#define VOIPTRANSCRIBE_MINBUFFER (8*32*1024) //!< buffering for up to eight seconds of uncompressed audio +#define VOIPTRANSCRIBE_SENDTIMEOUT (100) //!< milliseconds of silence audio before we consider an active recording sequence to be complete +#define VOIPTRANSCRIBE_WAIT (-100) //!< replaces PROTHTTP(2)_WAIT + +#define VOIPTRANSCRIBE_CONSECEMPTY (3) //!< default number of consecutive empty results before we backoff +#define VOIPTRANSCRIBE_CONSECERROR (3) //!< default number of consecutive request failures before we backoff + +#define VOIPTRANSCRIBE_AUDIORATE (16000) //!< audio rate in samples per second + +/*! maximum number of samples (eight seconds worth) we allow in a single request; we limit this in case a user's VAD + is not effective as well as to limit the size of the audio buffers required. if we don't break the requests up, + the user will wait indefinitely for a very long transcription result. if the user's microphone is too sensitive + and picking up music, background noise etc continuously, breaking up the requests will cause multiple transactions + with non-voice data to be sent, and will trigger backoff due to empty transcription results being received */ +#define VOIPTRANSCRIBE_MAXREQSAMPLES (8*VOIPTRANSCRIBE_AUDIORATE) + +#define OGG_HEAD_LENGTH (26) //!< header length +#define OGG_HEAD_TYPE_OFFSET (5) //!< offset of type within header +#define OGG_HEAD_GPOS_OFFSET (6) //!< offset of granule position within header + +#define OGG_PAGE_SEG_MAX (255) //!< maximum number of pages in an ogg segment table +#define OGG_PAGE_SEG_DEF (50) //!< 50 pages with each page being 20ms of audio equals one second of audio per page + +#define OGG_TYPE_DAT (0x00) //!< data page +#define OGG_TYPE_CNT (0x01) //!< continuation +#define OGG_TYPE_BOS (0x02) //!< beginning of stream +#define OGG_TYPE_EOS (0x04) //!< end of stream + +/*** Macros ***********************************************************************/ + +/*** Type Definitions *************************************************************/ + +/* + Transport stream API used to provide a single interface to HTTP, HTTP2, and WebSocket stream transport +*/ + +// forward declaration for transport type +typedef struct TransportT TransportT; + +// transport API +typedef void *(TransportCreate)(int32_t iBufSize); +typedef void (TransportDestroy)(void *pState); +typedef int32_t (TransportConnect)(void *pState, const char *pUrl); +typedef void (TransportDisconnect)(void *pState); +typedef void (TransportUpdate)(void *pState); +typedef int32_t (TransportRequest)(void *pState, const char *pUrl, const char *pBuffer, int32_t iLength, int32_t *pRequestId); +typedef int32_t (TransportSend)(void *pState, int32_t iRequestId, const char *pBuffer, int32_t iLength); +typedef int32_t (TransportRecv)(void *pState, int32_t iRequestId, char *pBuffer, int32_t iLength); +typedef int32_t (TransportStatus)(void *pState, int32_t iRequestId, int32_t iStatus, void *pBuffer, int32_t iBufSize); +typedef int32_t (TransportControl)(void *pState, int32_t iRequestId, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue); + +//! supported transport types +typedef enum TransportE +{ + TRANSPORT_HTTP = 0, + TRANSPORT_HTTP2, + TRANSPORT_WEBSOCKETS, + TRANSPORT_NUMPROTOCOLS +} TransportE; + +//! transport class +struct TransportT +{ + TransportE eTransport; + void *pState; + int32_t iStreamId; + TransportCreate *Create; + TransportDestroy *Destroy; + TransportConnect *Connect; + TransportDisconnect *Disconnect; + TransportUpdate *Update; + TransportRequest *Request; + TransportSend *Send; + TransportRecv *Recv; + TransportStatus *Status; + TransportControl *Control; +}; + +//! ogg file writer +typedef struct OggWriterT +{ + uint8_t *pBuffer; //!< ogg write buffer + uint8_t *pHeader; //!< pointer to current header + uint8_t *pChecksum; //!< pointer to current header checksum + uint8_t *pSegmentTable; //!< pointer to current segment table + uint64_t uGranulePos; //!< ogg granule position in units of 48khz audio samples + uint32_t uPageSeqn; //!< monotonically increasing page number + uint32_t uSerial; //!< stream serial number + int32_t iBufLen; //!< ogg buffer length + int32_t iBufOff; //!< ogg write offset + int32_t iBufAudioStart; //!< start of buffered audio + int32_t iNumSegments; //!< number of segments written to current Ogg page +} OggWriterT; + +/* + Voip transcription types +*/ + +//! buffer to hold audio data while it is being submitted +typedef struct VoipBufferT +{ + uint8_t *pBuffer; //!< buffer memory + int32_t iBufLen; //!< buffer length + int32_t iBufOff; //!< writing offset within buffer (buffering audio) + int32_t iBufInp; //!< reading offset within buffer (sending buffered audio) + int32_t iNumSamples; //!< number of samples in buffer + int8_t iBuffer; //!< buffer index + uint8_t bRecStarting; //!< TRUE if recording is starting + uint8_t bRecFinished; //!< TRUE if recording is finished + uint8_t bRecFull; //!< TRUE if recording buffer is full + uint8_t bMinDiscard; //!< minimum discard status for this buffer + uint8_t _pad[3]; + OggWriterT OggWriter; //!< ogg writer type, used for writing compressed audio +} VoipBufferT; + +typedef struct VoipTranscribeConfigT +{ + uint32_t uProfile; //!> transcription profile + char strUrl[VOIPTRANSCRIBE_MAXURL]; //!< url to access transcription service + char strKey[128]; //!< api key for access to transcription service +} VoipTranscribeConfigT; + +//! module state memory +struct VoipTranscribeRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + // module states + enum + { + ST_FAIL=-1, //!< fail + ST_IDLE, //!< idle + ST_CONN, //!< connecting + ST_SEND, //!< sending voice data + ST_RECV //!< receiving transcription result + } eState; + + int32_t iTimeout; //!< current http timeout + uint32_t uVoipTick; //!< timestamp when last voice sample was submitted + + uint32_t uProfile; //!> transcription profile + VoipTranscribeProviderE eProvider; //!< transcription provider + VoipTranscribeFormatE eFormat; //!< audio format + VoipTranscribeTransportE eTransport; //!< transport protocol + int32_t iAudioRate; //!< sampling rate of audio in hz + + char strUrl[VOIPTRANSCRIBE_MAXURL]; //!< url to access transcription service + char strKey[128]; //!< api key for access to transcription service + char strAudioFormat[64]; //!< current audio format e.g. audio/li16 + + int32_t iConsecErrorCt; //!< number of consecutive request failures + int32_t iConsecErrorMax; //!< maximum number of consecutive request failures before we backoff + int32_t iConsecEmptyCt; //!< number of consecutive empty request results + int32_t iConsecEmptyMax; //!< maximum number of consecutive empty request results before we backoff + uint32_t uBackoffTimer; //!< millisecond counter tracking when current backoff time expires + + uint8_t bConnected; //!< connected to transcription service + uint8_t bCompressed; //!< TRUE if transcribing compressed audio, else FALSE + uint8_t bMinDiscard; //!< true if minimum voice sample discard enabled + int8_t iRecBuffer; //!< current recording buffer + int8_t iSndBuffer; //!< current sending buffer + int8_t iVerbose; //!< verbose debug level (debug only) + uint8_t _pad[2]; + + AWSSignInfoT AWSSignInfo; //!< AWS Signing info for current request + + TransportT Transport; //!< transport object + VoipSpeechToTextMetricsT Metrics; //!< metrics object + uint32_t uSttStartTime; //!< time we finished sending the request + + uint16_t aJsonParseBuf[2*1024]; //!< buffer to parse JSON results + char strResponse[16*1024*4]; //!< transcription server response $$temp - increased 4x for large Amazon partial results + char strTranscription[VOIPTRANSCRIBE_OUTPUT_MAX]; //!< transcription result + char strSessionId[128]; //!< AWS session id + + VoipBufferT VoipBuffer[2]; //!< voip audio double buffer + VoipBufferT VoipBufferSnd; //!< voip send buffer (amazon & google need audio encoded) +}; + +/*** Variables ********************************************************************/ + +//! global config state +static VoipTranscribeConfigT _VoipTranscribe_Config = { VOIPTRANSCRIBE_PROFILE_DISABLED, "", "" }; + +//! table for calculating Ogg CRC checksum +static const uint32_t _Ogg_CRCTable[256] = +{ + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +//! Ogg Opus Identity Header +static const uint8_t _aOggOpusIdentHeader[] = +{ + // magic numbers + 'O', 'p', 'u', 's', + 'H', 'e', 'a', 'd', + 1, // version + 1, // output channel count + 0, 0, // pre-skip + 0x80, 0x3e, 0x00, 0x00, // input sample rate; 16khz + 0, 0, // output gain + 0, // output channel mapping +}; + +//! Ogg Opus Comment Header +static const uint8_t _aOggOpusCommentHeader[] = +{ + // magic numbers + 'O', 'p', 'u', 's', + 'T', 'a', 'g', 's', + // vendor string length + 2, 0, 0, 0, + // vendor string + 'E', 'A', + // user comment list length + 0, 0, 0, 0 +}; + +//! Baltimore Cybertrust Root CA, needed for Microsoft Speech +static const char _strCyberTrustRootCA[] = +{ + "-----BEGIN CERTIFICATE-----" + "MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ" + "RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD" + "VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX" + "DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y" + "ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy" + "VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr" + "mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr" + "IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK" + "mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu" + "XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy" + "dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye" + "jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1" + "BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3" + "DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92" + "9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx" + "jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0" + "Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz" + "ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS" + "R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp" + "-----END CERTIFICATE-----" +}; + +//! GlobalSign Root CA R2, needed for Google +static const char _strGlobalSignRootCAR2[] = +{ + "-----BEGIN CERTIFICATE-----" + "MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G" + "A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp" + "Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1" + "MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG" + "A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI" + "hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL" + "v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8" + "eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq" + "tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd" + "C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa" + "zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB" + "mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH" + "V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n" + "bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG" + "3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs" + "J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO" + "291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS" + "ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd" + "AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7" + "TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==" + "-----END CERTIFICATE-----" +}; + +//! Amazon Root CA R1, needed for Amazon Transcribe +static const char _strAmazonRootCAR1[] = +{ + "-----BEGIN CERTIFICATE-----" + "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF" + "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6" + "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL" + "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv" + "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj" + "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM" + "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw" + "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6" + "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L" + "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm" + "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC" + "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA" + "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI" + "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs" + "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv" + "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU" + "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy" + "rqXRfboQnoZsG4q5WTP468SQvvG5" + "-----END CERTIFICATE-----" +}; + + +/*** Private Functions ************************************************************/ + +/* + Transport wrapper class providing protocol-agnostic API for using HTTP, HTTP2, or WebSockets for stream transport +*/ + +/* + TransportRequest +*/ +static int32_t _TransportHttpRequest(void *pProtoHttp, const char *pUrl, const char *pBuffer, int32_t iLength, int32_t *pRequestId) +{ + return(ProtoHttpPost(pProtoHttp, pUrl, (iLength != PROTOHTTP_STREAM_BEGIN) ? pBuffer : NULL, iLength, FALSE)); +} + +static int32_t _TransportHttp2Request(void *pProtoHttp2, const char *pUrl, const char *pBuffer, int32_t iLength, int32_t *pRequestId) +{ + return(ProtoHttp2Request(pProtoHttp2, pUrl, NULL, PROTOHTTP2_STREAM_BEGIN, PROTOHTTP_REQUESTTYPE_POST, pRequestId)); +} + +static int32_t _TransportWebSocketRequest(void *pState, const char *pUrl, const char *pBuffer, int32_t iLength, int32_t *pRequestId) +{ + return(ProtoWebSocketSendText(pState, pBuffer)); +} + +/* + TransportSend +*/ +static int32_t _TransportHttpSend(void *pState, int32_t iRequestId, const char *pBuffer, int32_t iLength) +{ + return(ProtoHttpSend(pState, pBuffer, iLength)); +} + +static int32_t _TransportHttp2Send(void *pState, int32_t iRequestId, const char *pBuffer, int32_t iLength) +{ + int32_t iResult = ProtoHttp2Send(pState, iRequestId, (const uint8_t *)pBuffer, iLength); + return(iResult); +} + +static int32_t _TransportWebSocketSend(void *pState, int32_t iRequestId, const char *pBuffer, int32_t iLength) +{ + return(ProtoWebSocketSend(pState, pBuffer, iLength)); +} + +/* + TransportRecv - note, a zero result returned by one of these functions indicates completion with no data +*/ +static int32_t _TransportHttpRecv(void *pState, int32_t iRequestId, char *pBuffer, int32_t iLength) +{ + int32_t iResult = ProtoHttpRecvAll(pState, pBuffer, iLength); + return((iResult != PROTOHTTP_RECVWAIT) ? iResult : VOIPTRANSCRIBE_WAIT); +} + +static int32_t _TransportHttp2Recv(void *pState, int32_t iRequestId, char *pBuffer, int32_t iLength) +{ + int32_t iResult = ProtoHttp2RecvAll(pState, iRequestId, (uint8_t *)pBuffer, iLength); + return((iResult != PROTOHTTP2_RECVWAIT) ? iResult : VOIPTRANSCRIBE_WAIT); +} + +static int32_t _TransportWebSocketRecv(void *pState, int32_t iRequestId, char *pBuffer, int32_t iLength) +{ + int32_t iResult = ProtoWebSocketRecv(pState, pBuffer, iLength); + return((iResult != 0) ? iResult : VOIPTRANSCRIBE_WAIT); +} + +/* + TransportStatus +*/ +static int32_t _TransportHttpStatus(void *pState, int32_t iRequestId, int32_t iStatus, void *pBuffer, int32_t iBufSize) +{ + return(ProtoHttpStatus(pState, iStatus, pBuffer, iBufSize)); +} + +static int32_t _TransportWebSocketStatus(void *pState, int32_t iRequestId, int32_t iStatus, void *pBuffer, int32_t iBufSize) +{ + return(ProtoWebSocketStatus(pState, iStatus, pBuffer, iBufSize)); +} + +/* + TransportControl +*/ +static int32_t _TransportHttpControl(void *pState, int32_t iRequestId, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + return(ProtoHttpControl(pState, iControl, iValue, iValue2, pValue)); +} + +static int32_t _TransportWebSocketControl(void *pState, int32_t iRequestId, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + return(ProtoWebSocketControl(pState, iControl, iValue, iValue2, pValue)); +} + + +/*F********************************************************************************/ +/*! + \Function _TransportInit + + \Description + Init Transport handler for specified transport method + + \Input *pTransport - transport handler structure + \Input eTransport - transport handler type + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _TransportInit(TransportT *pTransport, TransportE eTransport) +{ + pTransport->eTransport = eTransport; + switch (eTransport) + { + case TRANSPORT_HTTP: + pTransport->Create = (TransportCreate *)ProtoHttpCreate; + pTransport->Destroy = (TransportDestroy *)ProtoHttpDestroy; + pTransport->Connect = NULL; + pTransport->Disconnect = NULL; + pTransport->Update = (TransportUpdate *)ProtoHttpUpdate; + pTransport->Request = _TransportHttpRequest; + pTransport->Send = _TransportHttpSend; + pTransport->Recv = _TransportHttpRecv; + pTransport->Status = _TransportHttpStatus; + pTransport->Control = _TransportHttpControl; + break; + case TRANSPORT_HTTP2: + pTransport->Create = (TransportCreate *)ProtoHttp2Create; + pTransport->Destroy = (TransportDestroy *)ProtoHttp2Destroy; + pTransport->Connect = NULL; + pTransport->Disconnect = NULL; + pTransport->Update = (TransportUpdate *)ProtoHttp2Update; + pTransport->Request = _TransportHttp2Request; + pTransport->Send = _TransportHttp2Send; + pTransport->Recv = _TransportHttp2Recv; + pTransport->Status = (TransportStatus *)ProtoHttp2Status; + pTransport->Control = (TransportControl *)ProtoHttp2Control; + break; + case TRANSPORT_WEBSOCKETS: + pTransport->Create = (TransportCreate *)ProtoWebSocketCreate; + pTransport->Destroy = (TransportDestroy *)ProtoWebSocketDestroy; + pTransport->Connect = (TransportConnect *)ProtoWebSocketConnect; + pTransport->Disconnect = (TransportDisconnect *)ProtoWebSocketDisconnect; + pTransport->Update = (TransportUpdate *)ProtoWebSocketUpdate; + pTransport->Request = _TransportWebSocketRequest; + pTransport->Send = _TransportWebSocketSend; + pTransport->Recv = _TransportWebSocketRecv; + pTransport->Status = _TransportWebSocketStatus; + pTransport->Control = _TransportWebSocketControl; + break; + default: + NetPrintf(("transport: init error\n")); + break; + } +} + +/* + Misc functions we may need for Microsoft when using WebSockets +*/ + +/*F********************************************************************************/ +/*! + \Function _GenerateUUID + + \Description + Generate a type Version 4 UUID as per + https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random) + + \Input *pBuffer - [out] storage for UUID + \Input iBufLen - buffer length + \Input bDashes - include dashes if true + + \Version 09/15/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _GenerateUUID(char *pBuffer, int32_t iBufLen, uint8_t bDashes) +{ + uint32_t uRand[4]; + +#if 0 + // generate 128 bits of vaguely random data + int32_t iRand; + for (iRand = 0; iRand < 4; iRand += 1) + { + uRand[iRand] = NetRand(0xffffffff); + } +#else + CryptRandGet((uint8_t *)uRand, sizeof(uRand)); +#endif + + /* fixup: set the four most significant bits of the 7th byte to 0100'B, so the high nibble is "4" + set the two most significant bits of the 9th byte to 10'B, so the high nibble will be one of "8", "9", "A", or "B". */ + uRand[1] &= ~0xf000; + uRand[1] |= 0x4000; + uRand[2] &= ~0xc0000000; + uRand[2] |= 0x80000000; + + // format it out + if (bDashes) + { + ds_snzprintf(pBuffer, iBufLen, "%08x-%04x-%04x-%04x-%04x%0x", uRand[0], uRand[1]>>16, uRand[1]&0xff, uRand[2]>>16, uRand[2]&0xff, uRand[3]); + } + else + { + ds_snzprintf(pBuffer, iBufLen, "%08x%08x%08x%08x", uRand[0], uRand[1], uRand[2], uRand[3]); + } +} + +/*F********************************************************************************/ +/*! + \Function _GenerateTimestamp + + \Description + Generate a timestamp following 8601 format plus milliseconds + + \Input *pBuffer - [out] storage for timestamp + \Input iBufLen - buffer length + + \Version 09/15/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _GenerateTimestamp(char *pBuffer, int32_t iBufLen) +{ + struct tm CurTime; + char strMillis[8]; + int32_t iMillis; + // get current time... this is equivalent to time(0) + //$$todo - make sure this is UTC + NetPlattimeToTimeMs(&CurTime, &iMillis); + // convert to ISO_8601 + ds_timetostr(&CurTime, TIMETOSTRING_CONVERSION_ISO_8601, 1, pBuffer, iBufLen); + // append milliseconds + ds_snzprintf(strMillis, sizeof(strMillis), "%d", iMillis); + ds_strnzcat(pBuffer, strMillis, iBufLen); +} + +/* + Wave functions to encapsulate our PCM16 audio in a WAV header +*/ + +/*F********************************************************************************/ +/*! + \Function _WaveWriteHeader + + \Description + Write WAV header into output buffer + + \Input *pBuffer - [out] buffer to write header to + \Input iBufLen - length of buffer + \Input iAudioRate - audio rate in hz + \Input iDataSize - size of all data (audio+headers) + + \Output + int32_t - size of header + + \Version 09/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _WaveWriteHeader(uint8_t *pBuffer, int32_t iBufLen, int32_t iAudioRate, int32_t iDataSize) +{ + int32_t iOffset=0, iSize; + static const int32_t _Wav_iFmtLen = 4+4+2+2+4+4+2+4; //! wav format chunk length + + // write group id + pBuffer[iOffset++] = 'R'; + pBuffer[iOffset++] = 'I'; + pBuffer[iOffset++] = 'F'; + pBuffer[iOffset++] = 'F'; + // write total length as counted after size field + iSize = iDataSize-iOffset-4; + pBuffer[iOffset++] = (uint8_t)(iSize); + pBuffer[iOffset++] = (uint8_t)(iSize>>8); + pBuffer[iOffset++] = (uint8_t)(iSize>>16); + pBuffer[iOffset++] = (uint8_t)(iSize>>24); + // write RIFF type + pBuffer[iOffset++] = 'W'; + pBuffer[iOffset++] = 'A'; + pBuffer[iOffset++] = 'V'; + pBuffer[iOffset++] = 'E'; + + // write format chunk + + // format group id + pBuffer[iOffset++] = 'f'; + pBuffer[iOffset++] = 'm'; + pBuffer[iOffset++] = 't'; + pBuffer[iOffset++] = ' '; + // write chunk size as counted after size field + iSize = _Wav_iFmtLen-8; + pBuffer[iOffset++] = (uint8_t)(iSize); + pBuffer[iOffset++] = (uint8_t)(iSize>>8); + pBuffer[iOffset++] = (uint8_t)(iSize>>16); + pBuffer[iOffset++] = (uint8_t)(iSize>>24); + // format tag (16 bit, always 1) + pBuffer[iOffset++] = 1; + pBuffer[iOffset++] = 0; + // channels (16 bit) + pBuffer[iOffset++] = 1; + pBuffer[iOffset++] = 0; + // sampling rate + pBuffer[iOffset++] = (uint8_t)(iAudioRate); + pBuffer[iOffset++] = (uint8_t)(iAudioRate>>8); + pBuffer[iOffset++] = (uint8_t)(iAudioRate>>16); + pBuffer[iOffset++] = (uint8_t)(iAudioRate>>24); + // average bytes per second - rate x 2 + pBuffer[iOffset++] = (uint8_t)(iAudioRate*2); + pBuffer[iOffset++] = (uint8_t)((iAudioRate*2)>>8); + pBuffer[iOffset++] = (uint8_t)((iAudioRate*2)>>16); + pBuffer[iOffset++] = (uint8_t)((iAudioRate*2)>>24); + // block alignment (bytes per sample) + pBuffer[iOffset++] = 2; + pBuffer[iOffset++] = 0; + // bits per sample + pBuffer[iOffset++] = 0x10; + pBuffer[iOffset++] = 0; + pBuffer[iOffset++] = 0; + pBuffer[iOffset++] = 0; + + // write data chunk + pBuffer[iOffset++] = 'd'; + pBuffer[iOffset++] = 'a'; + pBuffer[iOffset++] = 't'; + pBuffer[iOffset++] = 'a'; + // write data size + iSize = iDataSize-iOffset-4; + pBuffer[iOffset++] = (uint8_t)(iSize); + pBuffer[iOffset++] = (uint8_t)(iSize>>8); + pBuffer[iOffset++] = (uint8_t)(iSize>>16); + pBuffer[iOffset++] = (uint8_t)(iSize>>24); + + // return header offset + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function _WaveWriteOpen + + \Description + Writes required WAV header and returns offset to the start of data. + + \Input *pVoipBuffer - voip buffer to write header to + \Input iAudioRate - audio rate in hz + + \Output + int32_t - offset to end of data in buffer + + \Version 09/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _WaveWriteOpen(VoipBufferT *pVoipBuffer, int32_t iAudioRate) +{ + // we write an arbitrarily large size here as we don't know the data size in advance; both microsoft and watson support this + return(_WaveWriteHeader(pVoipBuffer->pBuffer, pVoipBuffer->iBufLen, iAudioRate, 1024*1024)); +} + +/* + Ogg/Opus functions to encapsulate our Opus codec data in the proper format for upload +*/ + +/*F********************************************************************************/ +/*! + \Function _OggWriteChecksum + + \Description + Calculate Ogg CRC32 on specified data, and write to output buffer in + little-endian + + \Input *pChecksum - [out] output buffer for crc32 checksum + \Input *pBuffer - data to checksum + \Input iBufSize - amount of data to checksum + + \Version 09/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _OggWriteChecksum(uint8_t *pChecksum, const uint8_t *pBuffer, int32_t iBufSize) +{ + uint32_t uChecksum; + int32_t iByte; + + // calculate crc32 + for (iByte = 0, uChecksum = 0; iByte < iBufSize; iByte += 1) + { + uChecksum = (uChecksum<<8)^_Ogg_CRCTable[((uChecksum>>24)&0xff)^pBuffer[iByte]]; + } + + // write crc32 + pChecksum[0] = (uint8_t)(uChecksum); + pChecksum[1] = (uint8_t)(uChecksum>>8); + pChecksum[2] = (uint8_t)(uChecksum>>16); + pChecksum[3] = (uint8_t)(uChecksum>>24); +} + +/*F********************************************************************************/ +/*! + \Function _OggWriteGranulePosition + + \Description + Write granule position to buffer + + \Input *pBuffer - [out] pointer to write location + \Input uGranulePos - granule position to write + + \Version 09/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _OggWriteGranulePosition(uint8_t *pBuffer, uint64_t uGranulePos) +{ + pBuffer[0] = (uint8_t)(uGranulePos); + pBuffer[1] = (uint8_t)(uGranulePos>>8); + pBuffer[2] = (uint8_t)(uGranulePos>>16); + pBuffer[3] = (uint8_t)(uGranulePos>>24); + pBuffer[4] = (uint8_t)(uGranulePos>>32); + pBuffer[5] = (uint8_t)(uGranulePos>>40); + pBuffer[6] = (uint8_t)(uGranulePos>>48); + pBuffer[7] = (uint8_t)(uGranulePos>>56); +} + +/*F********************************************************************************/ +/*! + \Function _OggWriteHeader + + \Description + Write Ogg header into output buffer + + \Input *pWriter - ogg writer + \Input *pBuffer - [out] buffer to write header to + \Input iHeaderOffset - offset to write header within buffer + \Input uType - page type (OGG_TYPE_*) + \Input iNumSegments - number of segments in segment table + \Input iDataSize - size of page data, or zero if it is not yet known + + \Output + int32_t - offset past end of header + + \Version 09/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _OggWriteHeader(OggWriterT *pWriter, uint8_t *pBuffer, int32_t iHeaderOffset, uint8_t uType, int32_t iNumSegments, int32_t iDataSize) +{ + int32_t iOffset = iHeaderOffset; + + // save pointer to current header + pWriter->pHeader = pBuffer+iHeaderOffset; + + // write capture pattern + pBuffer[iOffset++] = 'O'; + pBuffer[iOffset++] = 'g'; + pBuffer[iOffset++] = 'g'; + pBuffer[iOffset++] = 'S'; + // write version (always zero) + pBuffer[iOffset++] = 0; + // write header type - 1=continuation, 2=beginning of stream, 4=end of stream + pBuffer[iOffset++] = uType; + // reserve space for granule position + ds_memclr(pBuffer+iOffset, 8); + iOffset += 8; + // write bitstream serial number (32 bit) + pBuffer[iOffset++] = (uint8_t)(pWriter->uSerial); + pBuffer[iOffset++] = (uint8_t)(pWriter->uSerial>>8); + pBuffer[iOffset++] = (uint8_t)(pWriter->uSerial>>16); + pBuffer[iOffset++] = (uint8_t)(pWriter->uSerial>>24); + // write page sequence number + pBuffer[iOffset++] = (uint8_t)(pWriter->uPageSeqn); + pBuffer[iOffset++] = (uint8_t)(pWriter->uPageSeqn>>8); + pBuffer[iOffset++] = (uint8_t)(pWriter->uPageSeqn>>16); + pBuffer[iOffset++] = (uint8_t)(pWriter->uPageSeqn>>24); + pWriter->uPageSeqn += 1; + // write blank 32 bit checksum and save pointer to it + pWriter->pChecksum = pBuffer+iOffset; + pBuffer[iOffset++] = 0; + pBuffer[iOffset++] = 0; + pBuffer[iOffset++] = 0; + pBuffer[iOffset++] = 0; + // write page segments count + pBuffer[iOffset++] = iNumSegments; + // save segment table pointer + pWriter->pSegmentTable = pBuffer+iOffset; + // copy in page segments or reserve space if no segment table included + if (iNumSegments == 1) + { + pBuffer[iOffset] = (uint8_t)iDataSize; + } + else + { + ds_memclr(pBuffer+iOffset, iNumSegments); //$$temp - for debugging, doesn't really need to be cleared + } + // move offset past segment table + iOffset += iNumSegments; + + // return offset past header + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function _OggWriteOpen + + \Description + Set up a buffer for writing Ogg-encapsulated data + + \Input *pWriter - ogg writer + \Input *pBuffer - buffer data will be written to + \Input *iBufLen - length of buffer + + \Output + int32_t - offset in buffer where audio data writing starts + + \Version 09/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _OggWriteOpen(OggWriterT *pWriter, uint8_t *pBuffer, int32_t iBufLen) +{ + // reset ogg segment counter + pWriter->iNumSegments = 0; + // set ogg buffer info + pWriter->pBuffer = pBuffer; + pWriter->iBufOff = 0; + pWriter->iBufLen = iBufLen; + // set bitstream serial number + pWriter->uSerial = NetRand(0xffffffff); + // reset page sequence + pWriter->uPageSeqn = 0; + // return offset to caller + return(pWriter->iBufOff); +} + +/*F********************************************************************************/ +/*! + \Function _OggWriteSegment + + \Description + Write an Opus audio segment to the current page + + \Input *pWriter - ogg writer + \Input *pData - opus data + \Input *iDataLen - length of opus data + \Input iVerbose - debug verbosity level + + \Output + int32_t - negative=buffer full, positive=updated offset in bytes if page is complete, else zero + + \Version 09/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _OggWriteSegment(OggWriterT *pWriter, const uint8_t *pData, int32_t iDataLen, int32_t iVerbose) +{ + // if we're at the beginning of a page, write the header + if (pWriter->iNumSegments == 0) + { + pWriter->iBufOff = _OggWriteHeader(pWriter, pWriter->pBuffer, pWriter->iBufOff, 0, OGG_PAGE_SEG_DEF, 0); + } + // bail if we don't have room for the segment + if ((pWriter->iBufOff+iDataLen) > pWriter->iBufLen) + { + NetPrintfVerbose((iVerbose, 1, "voiptranscribe: ogg/opus writer full\n")); + return(-1); + } + // copy data to buffer + ds_memcpy_s(pWriter->pBuffer+pWriter->iBufOff, pWriter->iBufLen-pWriter->iBufOff, pData, iDataLen); + pWriter->iBufOff += iDataLen; + // add to segment table + pWriter->pSegmentTable[pWriter->iNumSegments++] = (uint8_t)iDataLen; + // add to granule position + pWriter->uGranulePos += 960; //$$temp - assume 20ms audio == 960 samples @48khz + // if we've filled up the page, calculate the CRC and reset for the new page + if (pWriter->iNumSegments == OGG_PAGE_SEG_DEF) + { + NetPrintfVerbose((iVerbose, 1, "voiptranscribe: wrote ogg/opus page with %d segments and length %d\n", pWriter->iNumSegments, pWriter->pBuffer+pWriter->iBufOff-pWriter->pHeader)); + // write updated granule position + _OggWriteGranulePosition(pWriter->pHeader+OGG_HEAD_GPOS_OFFSET, pWriter->uGranulePos); + // calculate the crc32 checksum + _OggWriteChecksum(pWriter->pChecksum, pWriter->pHeader, pWriter->pBuffer+pWriter->iBufOff-pWriter->pHeader); + // reset segment count + pWriter->iNumSegments = 0; + // return updated offset to caller, only once we've finalized the page + return(pWriter->iBufOff); + } + // return zero for unfinalized page + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _OggOpusWriteHeader + + \Description + Write an ogg/opus header + + \Input *pWriter - ogg writer + \Input *pBuffer - buffer data will be written to + \Input iOffset - offset to write header + \Input iBufLen - length of buffer + \Input uType - header type (OGG_TYPE_*) + \Input *pOpusHeader - pointer to body of header we are writing + \Input iOpusHeaderLen - size of body we're writing + + \Output + int32_t - offset in buffer following header + + \Version 09/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _OggOpusWriteHeader(OggWriterT *pWriter, uint8_t *pBuffer, int32_t iOffset, int32_t iBufLen, uint8_t uType, const uint8_t *pOpusHeader, int32_t iOpusHeaderLen) +{ + int32_t iHeaderOffset = iOffset; + // write the header + iOffset = _OggWriteHeader(pWriter, pWriter->pBuffer, iOffset, uType, 1, iOpusHeaderLen); + // copy the data + ds_memcpy_s(pWriter->pBuffer+iOffset, pWriter->iBufLen-iOffset, pOpusHeader, iOpusHeaderLen); + iOffset += iOpusHeaderLen; + // calculate the crc32 checksum + _OggWriteChecksum(pWriter->pChecksum, pBuffer+iHeaderOffset, iOffset-iHeaderOffset); + // return offset to caller + return(iOffset); +} + +/*F********************************************************************************/ +/*! + \Function _OggOpusWriteOpen + + \Description + Open an Ogg/Opus header for writing as per + https://tools.ietf.org/html/rfc7845.html#section-5.1 + + \Input *pWriter - ogg writer + \Input *pBuffer - buffer data will be written to + \Input *iBufLen - length of buffer + + \Output + int32_t - offset in buffer where audio data writing starts + + \Version 09/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _OggOpusWriteOpen(OggWriterT *pWriter, uint8_t *pBuffer, int32_t iBufLen) +{ + // initialize for writing + pWriter->iBufOff = _OggWriteOpen(pWriter, pBuffer, iBufLen); + // write Ogg Opus Ident Header + pWriter->iBufOff = _OggOpusWriteHeader(pWriter, pWriter->pBuffer, pWriter->iBufOff, pWriter->iBufLen, OGG_TYPE_BOS, _aOggOpusIdentHeader, sizeof(_aOggOpusIdentHeader)); + // write Ogg Opus Comment Header + pWriter->iBufOff = _OggOpusWriteHeader(pWriter, pWriter->pBuffer, pWriter->iBufOff, pWriter->iBufLen, OGG_TYPE_DAT, _aOggOpusCommentHeader, sizeof(_aOggOpusCommentHeader)); + // return offset to caller + return(pWriter->iBufOff); +} + +/*F********************************************************************************/ +/*! + \Function _OggOpusWriteFinish + + \Description + Fixes up final page and marks it as end of stream. + + \Input *pWriter - ogg writer + \Input iVerbose - debug verbosity level + + \Output + int32_t - offset to end of data + + \Version 09/12/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _OggOpusWriteFinish(OggWriterT *pWriter, int32_t iVerbose) +{ + // if we have a partially-filled page, finish it here + if (pWriter->iNumSegments > 0) + { + int32_t iEmptySegments = OGG_PAGE_SEG_DEF-pWriter->iNumSegments; + int32_t iMoveSize = (pWriter->pBuffer+pWriter->iBufOff) - (pWriter->pSegmentTable+OGG_PAGE_SEG_DEF); + // contract to remove unwritten segment table entries + memmove(pWriter->pSegmentTable+pWriter->iNumSegments, pWriter->pSegmentTable+OGG_PAGE_SEG_DEF, iMoveSize); + pWriter->iBufOff -= iEmptySegments; + NetPrintfVerbose((iVerbose, 1, "voiptranscribe: wrote ogg/opus page with %d segments and length %d\n", pWriter->iNumSegments, pWriter->pBuffer+pWriter->iBufOff-pWriter->pHeader)); + // update segment count + pWriter->pHeader[OGG_HEAD_LENGTH] = (uint8_t)pWriter->iNumSegments; + // update granule position + _OggWriteGranulePosition(pWriter->pHeader+OGG_HEAD_GPOS_OFFSET, pWriter->uGranulePos); + // write CRC for last page + _OggWriteChecksum(pWriter->pChecksum, pWriter->pHeader, pWriter->pBuffer+pWriter->iBufOff-pWriter->pHeader); + // reset segment count + pWriter->iNumSegments = 0; + } + // mark final page as end of stream + pWriter->pHeader[OGG_HEAD_TYPE_OFFSET] = OGG_TYPE_EOS; + + // return offset to start of data + return(pWriter->iBufOff); +} + +/* + Voip Transcription +*/ + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeBufferReset + + \Description + Reset voip buffer state + + \Input *pVoipTranscribe - module state + \Input *pVoipBuffer - buffer to initialize + + \Version 10/22/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTranscribeBufferReset(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer) +{ + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: [%d] resetting buffer\n", pVoipBuffer->iBuffer)); + pVoipBuffer->iBufOff = 0; + pVoipBuffer->iBufInp = 0; + pVoipBuffer->iNumSamples = 0; + pVoipBuffer->bRecStarting = TRUE; + pVoipBuffer->bRecFinished = FALSE; + pVoipBuffer->bRecFull = FALSE; + pVoipBuffer->bMinDiscard = TRUE; +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeBufferInit + + \Description + Allocate and initialize voip buffer + + \Input *pVoipTranscribe - module state + \Input *pVoipBuffer - buffer to initialize + \Input iBufSize - size of streaming buffer + \Input iBuffer - buffer index to set up + + \Output + int32_t - zero=failure, else success + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeBufferInit(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer, int32_t iBufSize, int32_t iBuffer) +{ + pVoipBuffer->iBuffer = iBuffer; + pVoipBuffer->iBufLen = iBufSize; + _VoipTranscribeBufferReset(pVoipTranscribe, pVoipBuffer); + return((pVoipBuffer->pBuffer = DirtyMemAlloc(iBufSize, VOIPTRANSCRIBE_MEMID, pVoipTranscribe->iMemGroup, pVoipTranscribe->pMemGroupUserData)) != NULL); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeCustomHeaderCb + + \Description + Custom header callback used to sign AWS requests + + \Input *pState - http module state + \Input *pHeader - pointer to http header buffer + \Input uHeaderSize - size of http header buffer + \Input *pData - pointer to data (unused) + \Input iDataLen - data length (unused) + \Input *pUserRef - voiptranscribe ref + + \Output + int32_t - output header length + + \Version 12/28/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeCustomHeaderCb(ProtoHttp2RefT *pState, char *pHeader, uint32_t uHeaderSize, const uint8_t *pData, int64_t iDataLen, void *pUserRef) +{ + VoipTranscribeRefT *pVoipTranscribe = (VoipTranscribeRefT *)pUserRef; + int32_t iHdrLen = (int32_t)strlen(pHeader); + + // if we have room, sign the request + if (uHeaderSize < (unsigned)iHdrLen) + { + return(iHdrLen); + } + + // sign the request and return the updated size + iHdrLen += AWSSignSigV4(pHeader, uHeaderSize, "", pVoipTranscribe->strKey, "transcribe", &pVoipTranscribe->AWSSignInfo); + // return size to protohttp + return(iHdrLen); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeTransportInit + + \Description + Init transport module + + \Input *pVoipTranscribe - pointer to module state + + \Output + int32_t - negative=failure, else success + + \Version 09/17/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeTransportInit(VoipTranscribeRefT *pVoipTranscribe) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + + // init transport class + pVoipTranscribe->eTransport = VOIPTRANSCRIBE_PROFILE_TRANSPORT(pVoipTranscribe->uProfile); + if (pVoipTranscribe->eTransport == VOIPTRANSCRIBE_TRANSPORT_HTTP) + { + _TransportInit(pTransport, TRANSPORT_HTTP); + } + else if (pVoipTranscribe->eTransport == VOIPTRANSCRIBE_TRANSPORT_HTTP2) + { + _TransportInit(pTransport, TRANSPORT_HTTP2); + } + else if (pVoipTranscribe->eTransport == VOIPTRANSCRIBE_TRANSPORT_WEBSOCKETS) + { + _TransportInit(pTransport, TRANSPORT_WEBSOCKETS); + } + + // allocate transport ref; give it a big enough buffer to max out SSL frame size + if ((pTransport->pState = pTransport->Create(16*1024)) == NULL) + { + NetPrintf(("voiptranscribe: could not allocate transport module\n")); + VoipTranscribeDestroy(pVoipTranscribe); + return(-1); + } + + // perform transport-specific initialization + if (pTransport->eTransport == TRANSPORT_HTTP) + { + // don't request connection close + pTransport->Control(pTransport->pState, pTransport->iStreamId, 'keep', 1, 0, NULL); + // enable reuse on put/post + pTransport->Control(pTransport->pState, pTransport->iStreamId, 'rput', 1, 0, NULL); + } + else if (pTransport->eTransport == TRANSPORT_WEBSOCKETS) + { + // increase temporary input buffer used for connection establishment to allow for header info + pTransport->Control(pTransport->pState, pTransport->iStreamId, 'ires', 2*1024, 0, NULL); + } + + // perform provider-specific initialization + if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_AMAZON) + { + // set request header callback for AWS signing + ProtoHttp2Callback(pTransport->pState, _VoipTranscribeCustomHeaderCb, NULL, pVoipTranscribe); + } + + // set common transport parameters + pVoipTranscribe->iTimeout = 60*1000; + pTransport->Control(pTransport->pState, pTransport->iStreamId, 'time', pVoipTranscribe->iTimeout, 0, NULL); + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeTransportCleanup + + \Description + Cleanup transport module + + \Input *pVoipTranscribe - pointer to module state + + \Version 12/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTranscribeTransportCleanup(VoipTranscribeRefT *pVoipTranscribe) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + + // destroy previous transport ref, if allocated + if (pTransport->pState != NULL) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: cleaning up previous transport state\n")); + pTransport->Destroy(pTransport->pState); + } + + // reset transport state + ds_memclr(&pVoipTranscribe->Transport, sizeof(pVoipTranscribe->Transport)); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeBasicAuth + + \Description + Encode Basic HTTP authorization header as per https://tools.ietf.org/html/rfc7617 + + \Input *pBuffer - [out] output buffer for encoded base64 string + \Input iBufSize - size of output buffer + \Input *pUser - user identifer + \Input *pPass - user password + + \Output + const char * - pointer to output buffer + + \Version 02/27/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipTranscribeBasicAuth(char *pBuffer, int32_t iBufSize, const char *pUser, const char *pPass) +{ + char strAuth[128]; + ds_snzprintf(strAuth, sizeof(strAuth), "%s:%s", pUser, pPass); + Base64Encode2(strAuth, (int32_t)strlen(strAuth), pBuffer, iBufSize); + return(pBuffer); +} + +/*F********************************************************************************/ +/*! +\Function _VoipTranscribeParseResponseWatson + + \Description + Parse response from IBM Watson transcription service + + \Input *pVoipTranscribe - pointer to module state + \Input *pResponse - server response + \Input *pResult - parse result buffer + \Input iResultSize - length of result buffer + + \Output + int32_t - negative=failure, zero=listening, else success + + \Notes + A zero result indicates an intermediate response ("listening") that should + be consumed while remaining in the receiving state. + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeParseResponseWatson(VoipTranscribeRefT *pVoipTranscribe, const char *pResponse, char *pResult, int32_t iResultSize) +{ + const char *pCurrent, *pAlt; + uint16_t *pJsonParseBuf; + int32_t iResult = -1; + char strText[128], *pText; + + // parse the response + if (JsonParse(pVoipTranscribe->aJsonParseBuf, sizeof(pVoipTranscribe->aJsonParseBuf)/sizeof(pVoipTranscribe->aJsonParseBuf[0]), pResponse, -1) == 0) + { + NetPrintf(("voiptranscribe: warning: parse results truncated\n")); + } + pJsonParseBuf = pVoipTranscribe->aJsonParseBuf; + + if ((pCurrent = JsonFind2(pJsonParseBuf, NULL, "results[", 0)) != NULL) + { + if ((pAlt = JsonFind2(pJsonParseBuf, pCurrent, ".alternatives[", 0)) != NULL) + { + JsonGetString(JsonFind2(pJsonParseBuf, pAlt, ".transcript", 0), pResult, iResultSize, ""); + + /* as per https://cloud.ibm.com/docs/services/speech-to-text?topic=speech-to-text-basic-response#hesitation, results + can include %HESITATION in some circumstances. we don't want that, so we remove it from the output if detected. + note it seems that smart_formatting also removes it, but we leave this here in case that changes at some point */ + for (pText = pVoipTranscribe->strTranscription; (pText = ds_stristr(pText, "%HESITATION")) != NULL; ) + { + iResult = (int32_t)strlen(pText)+1; + memmove(pText, pText+12, iResult-12); + } + } + iResult = 1; + } + else if ((pCurrent = JsonFind(pJsonParseBuf, "state")) != NULL) + { + JsonGetString(pCurrent, strText, sizeof(strText), ""); + if (!strcmp(strText, "listening")) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: state: listening\n")); + iResult = 0; + } + } + else + { + *pResult = '\0'; + if ((pCurrent = JsonFind(pJsonParseBuf, "error")) != NULL) + { + JsonGetString(pCurrent, pResult, iResultSize, ""); + // if a timeout, don't consider it an error + if (!ds_stricmp(pVoipTranscribe->strTranscription, "Session timed out.")) + { + pVoipTranscribe->strTranscription[0] = '\0'; + iResult = 0; + } + } + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeParseResponseMicrosoft + + \Description + Parse response from Microsoft Speech transcription service + + \Input *pVoipTranscribe - pointer to module state + \Input *pResponse - server response + \Input *pResult - parse result buffer + \Input iResultSize - length of result buffer + + \Output + int32_t - negative=failure, else success + + \Notes + RecognitionStatus: Success, NoMatch, InitialSilenceTimeout, BabbleTimeout, Error + + DisplayText represents the recognized phrase after capitalization, punctuation, and + inverse-text-normalization have been applied and profanity has been masked with + asterisks. The DisplayText field is present only if the RecognitionStatus field has + the value Success. + + Ref: https://docs.microsoft.com/en-us/azure/cognitive-services/speech/concepts#transcription-responses + + \Version 09/15/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeParseResponseMicrosoft(VoipTranscribeRefT *pVoipTranscribe, const char *pResponse, char *pResult, int32_t iResultSize) +{ + int32_t iResult = -1; + const char *pCurrent; + char strText[128]; + uint16_t *pJsonParseBuf; + + // parse the response + if (JsonParse(pVoipTranscribe->aJsonParseBuf, sizeof(pVoipTranscribe->aJsonParseBuf)/sizeof(pVoipTranscribe->aJsonParseBuf[0]), pResponse, -1) == 0) + { + NetPrintf(("voiptranscribe: warning: parse results truncated\n")); + } + pJsonParseBuf = pVoipTranscribe->aJsonParseBuf; + + // get status + if ((pCurrent = JsonFind2(pJsonParseBuf, NULL, "RecognitionStatus", 0)) != NULL) + { + JsonGetString(pCurrent, strText, sizeof(strText), ""); + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: RecognitionStatus=%s\n", strText)); + if (strcmp(strText, "Error")) + { + iResult = 1; + } + } + // get display text + if ((pCurrent = JsonFind2(pJsonParseBuf, NULL, "DisplayText", 0)) != NULL) + { + JsonGetString(pCurrent, pResult, iResultSize, ""); + iResult = 1; + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeParseResponseGoogleJson + + \Description + Parse response from Google Cloud transcription service + + \Input *pVoipTranscribe - pointer to module state + \Input *pResponse - server response + \Input *pResult - parse result buffer + \Input iResultSize - length of result buffer + + \Output + int32_t - negative=failure, else success + + \Notes + Ref: https://cloud.google.com/speech-to-text/docs/reference/rpc/google.cloud.speech.v1#google.cloud.speech.v1.StreamingRecognizeResponse + + \Version 09/27/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeParseResponseGoogleJson(VoipTranscribeRefT *pVoipTranscribe, const char *pResponse, char *pResult, int32_t iResultSize) +{ + const char *pCurrent, *pAlt; + uint16_t *pJsonParseBuf; + int32_t iResult = -1; + + // parse the response + if (JsonParse(pVoipTranscribe->aJsonParseBuf, sizeof(pVoipTranscribe->aJsonParseBuf)/sizeof(pVoipTranscribe->aJsonParseBuf[0]), pResponse, -1) == 0) + { + NetPrintf(("voiptranscribe: warning: parse results truncated\n")); + } + pJsonParseBuf = pVoipTranscribe->aJsonParseBuf; + + // check for transcript result + if ((pCurrent = JsonFind2(pJsonParseBuf, NULL, "results[", 0)) != NULL) + { + if ((pAlt = JsonFind2(pJsonParseBuf, pCurrent, ".alternatives[", 0)) != NULL) + { + JsonGetString(JsonFind2(pJsonParseBuf, pAlt, ".transcript", 0), pResult, iResultSize, ""); + } + iResult = 1; + } + // process error, if there is one + else if ((pCurrent = JsonFind2(pJsonParseBuf, NULL, "error", 0)) != NULL) + { + char strText[128]; + int32_t iCode = JsonGetInteger(JsonFind2(pJsonParseBuf, pCurrent, ".code", 0), 0); + JsonGetString(JsonFind2(pJsonParseBuf, pCurrent, ".message", 0), strText, sizeof(strText), ""); + ds_snzprintf(pResult, iResultSize, "error %d (%s)", iCode, strText); + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeParseResponseGoogleProtobuf + + \Description + Parse response from Google Cloud transcription service + + \Input *pVoipTranscribe - pointer to module state + \Input *pResponse - server response + \Input iResponseSize - server response length + \Input *pResult - parse result buffer + \Input iResultSize - length of result buffer + + \Output + int32_t - negative=failure, else success + + \Notes + See file header for response format and protobuf definition reference. + + \Version 10/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeParseResponseGoogleProtobuf(VoipTranscribeRefT *pVoipTranscribe, const char *pResponse, int32_t iResponseSize, char *pResult, int32_t iResultSize) +{ + ProtobufReadT Reader, Msg, Msg2; + const uint8_t *pCurrent = NULL, *pCurrent2 = NULL; + const uint8_t *pBuffer = (const uint8_t *)pResponse; + int32_t iMsgSize, iResult=-1; + + // an empty response means the audio produced no transcription; this indicates the request is complete, so we return completion + if (iResponseSize == 0) + { + return(1); + } + + // get message size (skipping compression) + if ((pBuffer = ProtobufCommonReadSize(pBuffer+1, iResponseSize-1, &iMsgSize)) == NULL) + { + return(iResult); + } + ProtobufReadInit(&Reader, pBuffer, iMsgSize); + + // pull out the error info if included + if (ProtobufReadMessage(&Reader, ProtobufReadFind(&Reader, 1 /* error */), &Msg) != NULL) + { + char strText[128]; + int32_t iCode = ProtobufReadVarint(&Msg, ProtobufReadFind(&Msg, 1 /* code */)); + ProtobufReadString(&Msg, ProtobufReadFind(&Msg, 2 /* message */), strText, sizeof(strText)); + ds_snzprintf(pResult, iResultSize, "error %d (%s)", iCode, strText); + } + + // read repeated results + if ((pCurrent = ProtobufReadMessage(&Reader, ProtobufReadFind2(&Reader, 2 /* results */, pCurrent), &Msg)) != NULL) + { + // read repeated alternatives + if ((pCurrent2 = ProtobufReadMessage(&Msg, ProtobufReadFind2(&Msg, 1 /* alternatives */, pCurrent2), &Msg2)) != NULL) + { + ProtobufReadString(&Msg2, ProtobufReadFind(&Msg2, 1 /* transcript */), pResult, iResultSize); + iResult = 1; + } + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeParseResponseAmazonJson + + \Description + Parse JSON response from Amazon Transcribe service, after being extracted from + binary event. + + \Input *pVoipTranscribe - pointer to module state + \Input *pResponse - server response + \Input *pResult - parse result buffer + \Input iResultSize - length of result buffer + + \Output + int32_t - negative=failure, zero=listening, else success + + \Version 01/17/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeParseResponseAmazonJson(VoipTranscribeRefT *pVoipTranscribe, const char *pResponse, char *pResult, int32_t iResultSize) +{ + const char *pCurrent, *pAlt; + uint16_t *pJsonParseBuf; + int32_t iResult = -1; + + // parse the response + if (JsonParse(pVoipTranscribe->aJsonParseBuf, sizeof(pVoipTranscribe->aJsonParseBuf)/sizeof(pVoipTranscribe->aJsonParseBuf[0]), pResponse, -1) == 0) + { + NetPrintf(("voiptranscribe: warning: parse results truncated\n")); + } + pJsonParseBuf = pVoipTranscribe->aJsonParseBuf; + + // check for transcript response + if ((pCurrent = JsonFind2(pJsonParseBuf, NULL, "Transcript.Results[", 0)) != NULL) + { + // swallow intermediate/empty results + iResult = 0; + // check for completion + if (((pAlt = JsonFind2(pJsonParseBuf, pCurrent, ".IsPartial", 0)) != NULL) && !JsonGetBoolean(pAlt, FALSE)) + { + if ((pAlt = JsonFind2(pJsonParseBuf, pCurrent, ".Alternatives[", 0)) != NULL) + { + JsonGetString(JsonFind2(pJsonParseBuf, pAlt, ".Transcript", 0), pResult, iResultSize, ""); + } + iResult = 1; + } + } + else if ((pCurrent = JsonFind2(pJsonParseBuf, NULL, "Message", 0)) != NULL) + { + // get error message result + JsonGetString(pCurrent, pResult, iResultSize, ""); + } + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeParseResponseAmazon + + \Description + Parse binary event response from Amazon + + \Input *pVoipTranscribe - pointer to module state + \Input *pResponse - server response + \Input iResponseSize - server response length + \Input *pResult - parse result buffer + \Input iResultSize - length of result buffer + + \Output + int32_t - negative=failure, else success + + \Version 01/17/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeParseResponseAmazon(VoipTranscribeRefT *pVoipTranscribe, const char *pResponse, int32_t iResponseSize, char *pResult, int32_t iResultSize) +{ + char strEventType[32], strHeader[512], strMessage[4096]; + int32_t iMessageLen, iOffset, iReadResult, iResult; + TransportT *pTransport = &pVoipTranscribe->Transport; + + /* get session id from response header, to use in future requests; amazon recommends this as it can improve + transcription accuracy across requests */ + if (pTransport->Status(pTransport->pState, pTransport->iStreamId, 'htxt', strHeader, sizeof(strHeader)) != -1) + { + ProtoHttpGetHeaderValue(NULL, strHeader, "x-amzn-transcribe-session-id", pVoipTranscribe->strSessionId, sizeof(pVoipTranscribe->strSessionId), NULL); + } + // parse error response + if ((iResult = pTransport->Status(pTransport->pState, pTransport->iStreamId, 'code', strHeader, sizeof(strHeader))) != PROTOHTTP_RESPONSE_OK) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 0, "voiptranscribe: received %d result\n", iResult)); + } + // read binary events from response data + for (iOffset = 0, iResult = 0; (iOffset < iResponseSize) && (iResult == 0); iOffset += iReadResult) + { + if ((iReadResult = AWSReadEvent((const uint8_t *)pResponse+iOffset, iResponseSize-iOffset, strEventType, sizeof(strEventType), strMessage, (iMessageLen=(int32_t)sizeof(strMessage), &iMessageLen))) > 0) + { + if (!ds_stricmp(strEventType, "TranscriptEvent")) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: %s\n", strMessage)); + iResult = _VoipTranscribeParseResponseAmazonJson(pVoipTranscribe, strMessage, pResult, iResultSize); + } + if (!ds_stricmp(strEventType, "BadRequestException")) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 0, "voiptranscribe: BadRequestException:\n%s\n", strMessage)); + iResult = _VoipTranscribeParseResponseAmazonJson(pVoipTranscribe, strMessage, pResult, iResultSize); + } + } + else + { + break; + } + } + + return((iResult >= 0) ? 1 : -1); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeParseResponse + + \Description + Parse response from transcription service + + \Input *pVoipTranscribe - pointer to module state + \Input *pResponse - server response + \Input iResponseSize - server response length + \Input *pResult - parse result buffer + \Input iResultSize - length of result buffer + + \Output + int32_t - negative=failure, zero=continue receiving, else success + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeParseResponse(VoipTranscribeRefT *pVoipTranscribe, const char *pResponse, int32_t iResponseSize, char *pResult, int32_t iResultSize) +{ + int32_t iResult = -1; + if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_IBMWATSON) + { + iResult = _VoipTranscribeParseResponseWatson(pVoipTranscribe, pResponse, pResult, iResultSize); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_MICROSOFT) + { + iResult = _VoipTranscribeParseResponseMicrosoft(pVoipTranscribe, pResponse, pResult, iResultSize); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_GOOGLE) + { + iResult = (pVoipTranscribe->eTransport == VOIPTRANSCRIBE_TRANSPORT_HTTP) ? _VoipTranscribeParseResponseGoogleJson(pVoipTranscribe, pResponse, pResult, iResultSize) : _VoipTranscribeParseResponseGoogleProtobuf(pVoipTranscribe, pResponse, iResponseSize, pResult, iResultSize); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_AMAZON) + { + iResult = _VoipTranscribeParseResponseAmazon(pVoipTranscribe, pResponse, iResponseSize, pResult, iResultSize); + } + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeFormatHeaderWatson + + \Description + Format connection header for Watson service + + \Input *pVoipTranscribe - pointer to module state + \Input *pBuffer - [out] buffer to hold formatted header + \Input iBufLen - buffer length + + \Output + int32_t - negative=failure, else success + + \Version 09/17/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipTranscribeFormatHeaderWatson(VoipTranscribeRefT *pVoipTranscribe, char *pBuffer, int32_t iBufLen) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + char strAuth[128]; + int32_t iOffset; + + /* note: pre-encoded auth strings are 68 characters in length, the auth keys are 44 chars. we use this to decide whether + to do the encode or not. this code should be removed in the future once pre-encoded auth keys are no longer in use */ + + // encode Basic authorization string with string apikey: + if (strlen(pVoipTranscribe->strKey) < 68) + { + _VoipTranscribeBasicAuth(strAuth, sizeof(strAuth), "apikey", pVoipTranscribe->strKey); + } + else // just copy it + { + ds_strnzcpy(strAuth, pVoipTranscribe->strKey, sizeof(strAuth)); + } + + // format request header + iOffset = ds_snzprintf(pBuffer, iBufLen, "Authorization: Basic %s\r\n", strAuth); + // set http-specific options + if (pTransport->eTransport == TRANSPORT_HTTP) + { + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "Content-Type: %s\r\n", pVoipTranscribe->strAudioFormat); + } + // return transport-specific url + return(pVoipTranscribe->strUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeFormatHeaderMicrosoft + + \Description + Format connection header for Microsoft Speech service + + \Input *pVoipTranscribe - pointer to module state + \Input *pBuffer - [out] buffer to hold formatted header + \Input iBufLen - buffer length + + \Output + int32_t - negative=failure, else success + + \Version 09/17/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipTranscribeFormatHeaderMicrosoft(VoipTranscribeRefT *pVoipTranscribe, char *pBuffer, int32_t iBufLen) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + int32_t iOffset=0; + + // set http-specific options + if (pTransport->eTransport == TRANSPORT_HTTP) + { + // format request header + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "Accept: application/json;text/xml\r\n"); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "Content-Type: %s\r\n", pVoipTranscribe->strAudioFormat); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "Ocp-Apim-Subscription-Key: %s\r\n", pVoipTranscribe->strKey); + } + // set websockets-specific options + else if (pTransport->eTransport == TRANSPORT_WEBSOCKETS) + { + char strUUID[36], strTimestamp[36]; + // get a UUID + _GenerateUUID(strUUID, sizeof(strUUID), FALSE); + _GenerateTimestamp(strTimestamp, sizeof(strTimestamp)); + // format request header + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "X-ConnectionId: %s\r\n", strUUID); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "X-Timestamp: %s\r\n", strTimestamp); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "Content-Type: %s\r\n", pVoipTranscribe->strAudioFormat); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "Ocp-Apim-Subscription-Key: %s\r\n", pVoipTranscribe->strKey); + } + // return transport-specific url + return(pVoipTranscribe->strUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeFormatHeaderGoogle + + \Description + Format connection header for Google service + + \Input *pVoipTranscribe - pointer to module state + \Input *pBuffer - [out] buffer to hold formatted header + \Input iBufLen - buffer length + + \Output + int32_t - negative=failure, else success + + \Version 09/18/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipTranscribeFormatHeaderGoogle(VoipTranscribeRefT *pVoipTranscribe, char *pBuffer, int32_t iBufLen) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + static char strUrl[256] = ""; + const char *pUrl; + + // format url with api key + if (pTransport->eTransport == TRANSPORT_HTTP) + { + ds_snzprintf(strUrl, sizeof(strUrl), "%s?key=%s", pVoipTranscribe->strUrl, pVoipTranscribe->strKey); + pUrl = strUrl; + } + else + { + pUrl = pVoipTranscribe->strUrl; + ds_snzprintf(pBuffer, iBufLen, "te: trailers\r\ncontent-type: application/grpc\r\nX-Goog-Api-Key: %s\r\n", pVoipTranscribe->strKey); + } + // return url + return(pUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeFormatHeaderAmazon + + \Description + Format connection header for Amazon Transcribe service + + \Input *pVoipTranscribe - pointer to module state + \Input *pBuffer - [out] buffer to hold formatted header + \Input iBufLen - buffer length + + \Output + int32_t - negative=failure, else success + + \Version 09/17/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipTranscribeFormatHeaderAmazon(VoipTranscribeRefT *pVoipTranscribe, char *pBuffer, int32_t iBufLen) +{ + int32_t iOffset=0; + + // format request header + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "content-type: application/x-amz-json-1.1\r\n"); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "x-amzn-content-sha256: STREAMING-AWS4-HMAC-SHA256-EVENTS\r\n"); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "x-amzn-target: com.amazonaws.transcribe.Transcribe.StartStreamTranscription\r\n"); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "x-amzn-transcribe-language-code: en-US\r\n"); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "x-amzn-transcribe-media-encoding: pcm\r\n"); + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "x-amzn-transcribe-sample-rate: %d\r\n", pVoipTranscribe->iAudioRate); + if (pVoipTranscribe->strSessionId[0] != '\0') + { + iOffset += ds_snzprintf(pBuffer+iOffset, iBufLen-iOffset, "x-amzn-transcribe-session-id: %s\r\n", pVoipTranscribe->strSessionId); + } + + // return transport-specific url + return(pVoipTranscribe->strUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSetHeader + + \Description + Set connection header; note that this might be used in the Connect() or + Request() call depending on whether we are using a connection-oriented + protocol or not. + + \Input *pVoipTranscribe - pointer to module state + \Input *pTransport - transport handler + + \Output + int32_t - negative=failure, else success + + \Version 09/17/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static const char *_VoipTranscribeSetHeader(VoipTranscribeRefT *pVoipTranscribe, TransportT *pTransport) +{ + char strHeader[512] = ""; + const char *pUrl = NULL; + // format header with provider-specific fields + if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_IBMWATSON) + { + pUrl = _VoipTranscribeFormatHeaderWatson(pVoipTranscribe, strHeader, sizeof(strHeader)); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_MICROSOFT) + { + pUrl = _VoipTranscribeFormatHeaderMicrosoft(pVoipTranscribe, strHeader, sizeof(strHeader)); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_GOOGLE) + { + pUrl = _VoipTranscribeFormatHeaderGoogle(pVoipTranscribe, strHeader, sizeof(strHeader)); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_AMAZON) + { + pUrl = _VoipTranscribeFormatHeaderAmazon(pVoipTranscribe, strHeader, sizeof(strHeader)); + } + // set the header + pTransport->Control(pTransport->pState, pTransport->iStreamId, 'apnd', 0, 0, strHeader); + // return request header + return(pUrl); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeConnectCheck + + \Description + Check for connection completion, for protocols that require an explicit connection + + \Input *pVoipTranscribe - pointer to module state + \Input *pTransport - transport handler + + \Output + int32_t - negative=failure, zero=connecting, else success + + \Version 09/06/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeConnectCheck(VoipTranscribeRefT *pVoipTranscribe, TransportT *pTransport) +{ + int32_t iResult = (pTransport->eTransport == TRANSPORT_WEBSOCKETS) ? pTransport->Status(pTransport->pState, pTransport->iStreamId, 'stat', NULL, 0) : 1; + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeConnect + + \Description + Open a connection to a transcription service, if we're not already connected + + \Input *pVoipTranscribe - pointer to module state + + \Output + int32_t - negative=failure, else success + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeConnect(VoipTranscribeRefT *pVoipTranscribe) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + const char *pUrl; + int32_t iResult; + + // up the logging level + pTransport->Control(pTransport->pState, pTransport->iStreamId, 'spam', 1, 0, NULL); + + // early out if we're already connected or don't need to connect + if ((iResult = _VoipTranscribeConnectCheck(pVoipTranscribe, pTransport)) > 0) + { + return(1); + } + // set connect headers + if ((pUrl = _VoipTranscribeSetHeader(pVoipTranscribe, pTransport)) == NULL) + { + return(-1); + } + + // make the connection request + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: connecting to %s\n", pUrl)); + if ((iResult = pTransport->Connect(pTransport->pState, pUrl)) < 0) + { + NetPrintf(("voiptranscribe: error connecting to '%s'\n", pUrl)); + return(iResult); + } + + // return result code to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeRequest + + \Description + Make a request against transcription service + + \Input *pVoipTranscribe - pointer to module state + + \Output + int32_t - negative=failure, else success + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeRequest(VoipTranscribeRefT *pVoipTranscribe) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + char strRequest[128] = "", *pRequest = strRequest; + int32_t iRequestLen=0, iResult; + const char *pUrl; + + // set request headers + if ((pUrl = _VoipTranscribeSetHeader(pVoipTranscribe, pTransport)) == NULL) + { + return(-1); + } + + // set content-type for watson+websockets + if ((pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_IBMWATSON) && (pTransport->eTransport == TRANSPORT_WEBSOCKETS)) + { + // format websocket request body + iRequestLen = ds_snzprintf(strRequest, sizeof(strRequest), "{ \"action\": \"start\", \"content-type\": \"%s\", \"smart_formatting\": true }", pVoipTranscribe->strAudioFormat); + } + + // http transfers are streaming (use chunked encoding) + if (pTransport->eTransport == TRANSPORT_HTTP) + { + iRequestLen = PROTOHTTP_STREAM_BEGIN; + } + + // start the request + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: sending request\n")); + if ((iResult = pTransport->Request(pTransport->pState, pUrl, pRequest, iRequestLen, &pTransport->iStreamId)) < 0) + { + NetPrintf(("voiptranscribe: error %d issuing request'%s'\n", iResult, pUrl)); + return(iResult); + } + + // return result code to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSubmitRaw + + \Description + Submit uncompressed voice data to be transcribed + + \Input *pVoipTranscribe - pointer to module state + \Input *pVoipBuffer - buffer to write to + \Input *pBuffer - voice data to be transcribed + \Input iBufLen - size of voice data in bytes + + \Output + int32_t - number of bytes copied + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t _VoipTranscribeSubmitRaw(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer, const uint8_t *pBuffer, int32_t iBufLen) +{ + // start of buffer processing + if (pVoipBuffer->bRecStarting) + { + // reserve a WAV header to encapsulate the data + if (pVoipTranscribe->eFormat == VOIPTRANSCRIBE_FORMAT_WAV16) + { + pVoipBuffer->iBufOff = _WaveWriteOpen(pVoipBuffer, pVoipTranscribe->iAudioRate); + } + pVoipBuffer->bRecStarting = FALSE; + } + + // copy data to output buffer + ds_memcpy(pVoipBuffer->pBuffer+pVoipBuffer->iBufOff, pBuffer, iBufLen); + + // adjust buffer parameters + pVoipBuffer->iBufOff += iBufLen; + + // note if we're full + if (pVoipBuffer->iBufOff == pVoipBuffer->iBufLen) + { + pVoipBuffer->bRecFull = TRUE; + } + + // return amount copied to caller + return(iBufLen); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSubmitOpus + + \Description + Submit Opus voice data to be transcribed + + \Input *pVoipTranscribe - pointer to module state + \Input *pVoipBuffer - buffer to write to + \Input *pBuffer - voice data to be transcribed + \Input iBufLen - size of voice data in bytes + + \Output + int32_t - number of bytes copied + + \Version 09/10/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t _VoipTranscribeSubmitOpus(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer, const uint8_t *pBuffer, int32_t iBufLen) +{ + OggWriterT *pOggWriter = &pVoipBuffer->OggWriter; + int32_t iResult; + + // if we're at the start of the buffer, reserve an ogg header to encapsulate the opus data + if (pVoipBuffer->bRecStarting) + { + pVoipBuffer->iBufOff = _OggOpusWriteOpen(pOggWriter, pVoipBuffer->pBuffer, pVoipBuffer->iBufLen); + pVoipBuffer->bRecStarting = FALSE; + } + + // write voice bundle as an ogg segment + if ((iResult = _OggWriteSegment(pOggWriter, pBuffer, iBufLen, pVoipTranscribe->iVerbose)) > 0) + { + pVoipBuffer->iBufOff = iResult; + } + else if (iResult < 0) + { + pVoipBuffer->bRecFull = TRUE; + } + + // return amount copied to caller + return(iBufLen); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSubmit + + \Description + Submit voice data to be transcribed + + \Input *pVoipTranscribe - pointer to module state + \Input *pBuffer - voice data to be transcribed + \Input iBufLen - size of voice data in bytes + + \Output + int32_t - number of bytes copied + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeSubmit(VoipTranscribeRefT *pVoipTranscribe, const uint8_t *pBuffer, int32_t iBufLen) +{ + VoipBufferT *pVoipBuffer = &pVoipTranscribe->VoipBuffer[pVoipTranscribe->iRecBuffer]; + int32_t iBufAvail = pVoipBuffer->iBufLen - pVoipBuffer->iBufOff; + int32_t iResult; + + // determine amount of data to copy + if (iBufLen > iBufAvail) + { + NetPrintf(("voiptranscribe: [%d] warning; truncating input from %d to %d bytes\n", pVoipBuffer->iBuffer, iBufLen, iBufAvail)); + iBufLen = iBufAvail; + } + // make sure we have something to submit + if (iBufLen == 0) + { + return(0); + } + + NetPrintfVerbose((pVoipTranscribe->iVerbose, 3, "voiptranscribe: [%d] copy [0x%04x,0x%04x]\n", pVoipBuffer->iBuffer, pVoipBuffer->iBufOff, pVoipBuffer->iBufOff+iBufLen)); + + // submit data to buffer + if (!pVoipTranscribe->bCompressed) + { + iResult = _VoipTranscribeSubmitRaw(pVoipTranscribe, pVoipBuffer, pBuffer, iBufLen); + } + else + { + iResult = _VoipTranscribeSubmitOpus(pVoipTranscribe, pVoipBuffer, pBuffer, iBufLen); + } + + // keep track of samples submitted; if compressed assume 20ms of samples at 16khz + if (iResult > 0) + { + pVoipBuffer->iNumSamples += !pVoipTranscribe->bCompressed ? iBufLen/2 : 320; + } + + // update voip timestamp + pVoipTranscribe->uVoipTick = NetTick(); + + // return result to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSubmitFinish + + \Description + Finish processing of data submission + + \Input *pVoipTranscribe - pointer to module state + \Input *pVoipBuffer - buffer to write to + + \Output + int32_t - negative=skip, else process + + \Version 09/13/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeSubmitFinish(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer) +{ + // if we have less than a second of audio don't send it + if ((pVoipBuffer->iNumSamples < pVoipTranscribe->iAudioRate) && pVoipBuffer->bMinDiscard) + { + NetPrintf(("voiptranscribe: [%d] discarding short audio segment with only %d samples\n", pVoipBuffer->iBuffer, pVoipBuffer->iNumSamples)); + _VoipTranscribeBufferReset(pVoipTranscribe, pVoipBuffer); + return(-1); + } + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: [%d] submit finish\n", pVoipBuffer->iBuffer)); + + // record metrics + pVoipTranscribe->Metrics.uEventCount += 1; + pVoipTranscribe->Metrics.uDurationMsSent += ((pVoipBuffer->iNumSamples * 1000) / pVoipTranscribe->iAudioRate); + pVoipTranscribe->uSttStartTime = NetTick(); + + // handle specific audio format requirements + if (pVoipTranscribe->eFormat == VOIPTRANSCRIBE_FORMAT_OPUS) + { + pVoipBuffer->iBufOff = _OggOpusWriteFinish(&pVoipBuffer->OggWriter, pVoipTranscribe->iVerbose); + } + + // finalize current buffer + pVoipBuffer->bRecFinished = TRUE; + // set current buffer as send buffer + pVoipTranscribe->iSndBuffer = pVoipTranscribe->iRecBuffer; + // move to next recording buffer + pVoipTranscribe->iRecBuffer = (pVoipTranscribe->iRecBuffer+1)%2; + // reset the buffer + _VoipTranscribeBufferReset(pVoipTranscribe, &pVoipTranscribe->VoipBuffer[pVoipTranscribe->iRecBuffer]); + + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeBackoffSet + + \Description + Set backoff timer on failure or empty result, if appropriate + + \Input *pVoipTranscribe - pointer to module state + + \Version 12/05/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTranscribeBackoffSet(VoipTranscribeRefT *pVoipTranscribe) +{ + int32_t iCount, iEmptyCt, iErrorCt; + // see if we need to set the backoff timer + iEmptyCt = pVoipTranscribe->iConsecEmptyCt - pVoipTranscribe->iConsecEmptyMax; + iErrorCt = pVoipTranscribe->iConsecErrorCt - pVoipTranscribe->iConsecErrorMax; + // pick the biggest of the two + iCount = DS_MAX(iEmptyCt, iErrorCt); + // if positive, calculate backoff timer + if (iCount > 0) + { + // 2^n backoff on failures above the max + iCount = (1 << iCount) * 1000; + // clamp to maximum of sixty seconds + iCount = DS_MIN(iCount, 60*1000); + // set the backoff timer and make sure it doesn't equal zero (reserved for disabled status) + if ((pVoipTranscribe->uBackoffTimer = NetTick()+iCount) == 0) + { + pVoipTranscribe->uBackoffTimer = 1; + } + + NetPrintfVerbose((pVoipTranscribe->iVerbose, 0, "voiptranscribe: setting backoff timer to +%dms\n", iCount)); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeBackoffCheck + + \Description + Get if backoff is enabled + + \Input *pVoipTranscribe - pointer to module state + \Input *pVoipBuffer - voip buffer + + \Output + int32_t - zero if backoff is enabled, else one + + \Version 12/05/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeBackoffCheck(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer) +{ + if (pVoipTranscribe->uBackoffTimer != 0) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 0, "voiptranscribe: [%d] discarding audio segment with %d samples due to backoff\n", pVoipBuffer->iBuffer, pVoipBuffer->iNumSamples)); + _VoipTranscribeBufferReset(pVoipTranscribe, pVoipBuffer); + } + return(pVoipTranscribe->uBackoffTimer != 0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSendFinish + + \Description + Complete the send request + + \Input *pVoipTranscribe - pointer to module state + + \Output + int32_t - negative=failure, zero=retry, else success + + \Version 09/08/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeSendFinish(VoipTranscribeRefT *pVoipTranscribe) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + char strRequest[128]; + int32_t iRequestLen, iResult = 0; + + if ((pTransport->eTransport == TRANSPORT_HTTP) || (pTransport->eTransport == TRANSPORT_HTTP2)) + { + if ((iResult = pTransport->Send(pTransport->pState, pTransport->iStreamId, NULL, PROTOHTTP_STREAM_END)) == 0) + { + // a successful STREAM_END returns zero, we want to return nonzero so the caller knows the operation completed successfully + iResult = 1; + } + } + if (pTransport->eTransport == TRANSPORT_WEBSOCKETS) + { + iRequestLen = (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_IBMWATSON) ? ds_snzprintf(strRequest, sizeof(strRequest), "{ \"action\": \"stop\" }") : 0; + iResult = _TransportWebSocketRequest(pTransport->pState, NULL, strRequest, iRequestLen, NULL); + } + + // return result code to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeBase64Start + + \Description + Format start of Base64 JSON envelope + + \Input *pVoipTranscribe - pointer to module state + \Input *pVoipBuffer - buffer to write to + + \Output + int32_t - size of output data + + \Version 12/16/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeBase64Start(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer) +{ + int32_t iResult = ds_snzprintf((char *)pVoipBuffer->pBuffer, pVoipBuffer->iBufLen, "{ \"config\": { \"encoding\": \"%s\", \"sampleRateHertz\": %d, \"languageCode\": \"en-US\", \"profanity_filter\": \"true\" }, \"audio\": { \"content\": \"", + pVoipTranscribe->strAudioFormat, pVoipTranscribe->iAudioRate); + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeBase64Encode + + \Description + Base64 encode audio data + + \Input *pVoipBufferOut - buffer to hold encoded output + \Input *pVoipBufferInp - buffer holding binary source data + + \Output + int32_t - length of encoded data + + \Version 12/16/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeBase64Encode(VoipBufferT *pVoipBufferOut, VoipBufferT *pVoipBufferInp) +{ + int32_t iInpLen = pVoipBufferInp->iBufOff-pVoipBufferInp->iBufInp; + int32_t iOutLen = pVoipBufferOut->iBufLen-pVoipBufferOut->iBufOff-4; // save room for terminating "}} at end of base64 encoded input + // pick smallest of input and output length + iInpLen = DS_MIN(iInpLen, Base64DecodedSize(iOutLen-1)); + // make sure input length is a multiple of three; this ensures we have an integral output length with no padding + if (iInpLen > 3) + { + iInpLen = (iInpLen/3)*3; + } + // encode into output buffer + iOutLen = Base64Encode2((const char *)pVoipBufferInp->pBuffer+pVoipBufferInp->iBufInp, iInpLen, (char *)pVoipBufferOut->pBuffer+pVoipBufferOut->iBufOff, iOutLen); + // update input buffer offset + pVoipBufferInp->iBufInp += iInpLen; + // return output buffer offset; + return(iOutLen); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeProtobufStart + + \Description + Format start of Protobuf envelope + + \Input *pVoipTranscribe - pointer to module state + \Input *pVoipBuffer - buffer to encode + + \Output + int32_t - number of bytes in encoded output + + \Notes + See file header for request format and protobuf definition reference. + + \Version 10/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeProtobufStart(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer) +{ + static const uint8_t _aAudioFormatTypes[VOIPTRANSCRIBE_NUMFORMATS] = { 0xff, 1, 0xff, 6 }; + ProtobufWriteRefT *pEncoder; + int32_t iSize=0; + + // write audio settings + pVoipBuffer->pBuffer[0] = 0; + if ((pEncoder = ProtobufWriteCreate(pVoipBuffer->pBuffer+1, pVoipBuffer->iBufLen-1, TRUE)) != NULL) + { + ProtobufWriteMessageBegin(pEncoder, 1 /* streaming_config */); + ProtobufWriteMessageBegin(pEncoder, 1 /* config */); + ProtobufWriteVarint(pEncoder, _aAudioFormatTypes[pVoipTranscribe->eFormat], 1 /* encoding */); + ProtobufWriteVarint(pEncoder, VOIPTRANSCRIBE_AUDIORATE, 2 /* sample_rate_hertz */); + ProtobufWriteString(pEncoder, "en-US", (signed)strlen("en-US"), 3 /* language_code */); + ProtobufWriteVarint(pEncoder, TRUE, 5 /* profanity_filter */); + ProtobufWriteMessageEnd(pEncoder); + ProtobufWriteMessageEnd(pEncoder); + iSize = ProtobufWriteDestroy(pEncoder) + 1; + } + + // return size to caller + return(iSize); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeProtobufEncode + + \Description + Protobuf encode audio data + + \Input *pVoipBufferOut - buffer to hold encoded output + \Input *pVoipBufferInp - buffer holding binary source data + + \Output + int32_t - number of bytes in encoded output + + \Notes + See file header for request format and protobuf definition reference. + + \Version 10/02/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeProtobufEncode(VoipBufferT *pVoipBufferOut, VoipBufferT *pVoipBufferInp) +{ + ProtobufWriteRefT *pEncoder; + int32_t iBufAvail, iBufWrite, iSize=0; + const int32_t _iMaxProtobufOverhead = 1+4+1+2; // compression byte+protobuf length+audio field tag+audio data size (progressive encoded max 2048) + // early out if no data available or buffer full + if ((pVoipBufferInp->iBufInp == pVoipBufferInp->iBufOff) || (pVoipBufferOut->iBufOff == pVoipBufferOut->iBufLen)) + { + return(0); + } + // calculate output buffer space available + iBufAvail = pVoipBufferOut->iBufLen-pVoipBufferOut->iBufOff; + // calculate how much we're going to write (min of available output buffer minus overhead and available input data) + iBufWrite = DS_MIN(iBufAvail-_iMaxProtobufOverhead, pVoipBufferInp->iBufOff-pVoipBufferInp->iBufInp); + // write audio data + pVoipBufferOut->pBuffer[pVoipBufferOut->iBufOff] = 0; + if ((pEncoder = ProtobufWriteCreate(pVoipBufferOut->pBuffer+pVoipBufferOut->iBufOff+1, iBufAvail, TRUE)) != NULL) + { + ProtobufWriteBytes(pEncoder, pVoipBufferInp->pBuffer+pVoipBufferInp->iBufInp, iBufWrite, 2 /* audio_content */); + iSize = ProtobufWriteDestroy(pEncoder) + 1; + // update input buffer offset + pVoipBufferInp->iBufInp += iBufWrite; + } + // return size to caller + return(iSize); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeAwsEncode + + \Description + AWS encode audio data in signed binary event format + + \Input *pVoipTranscribe - module state + \Input *pVoipBufferOut - buffer to hold encoded output + \Input *pVoipBufferInp - buffer holding binary source data (NULL to write empty chunk) + + \Output + int32_t - number of bytes in encoded output + + \Version 01/16/2019 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeAwsEncode(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBufferOut, VoipBufferT *pVoipBufferInp) +{ + int32_t iBufOut, iInpWrite=0; + const uint8_t *pInpData = NULL; + // point to input data and calculate size to encode; if no input we write an empty chunk + if (pVoipBufferInp != NULL) + { + // require send buffer to be empty to ensure our sends are full size + if (pVoipBufferOut->iBufOff != 0) + { + return(0); + } + // locate data to read + pInpData = pVoipBufferInp->pBuffer+pVoipBufferInp->iBufInp; + // calculate how much input we have to write + iInpWrite = pVoipBufferInp->iBufOff-pVoipBufferInp->iBufInp; + } + // write signed audioevent chunk + iBufOut = AWSWriteEvent(pVoipBufferOut->pBuffer+pVoipBufferOut->iBufOff, pVoipBufferOut->iBufLen-pVoipBufferOut->iBufOff, pInpData, &iInpWrite, "AudioEvent", &pVoipTranscribe->AWSSignInfo); + // consume input + if (pVoipBufferInp != NULL) + { + pVoipBufferInp->iBufInp += iInpWrite; + } + // return size of output written + return(iBufOut); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSendEncode + + \Description + Special encoding for providers that need it in either base64/protobuf + (Google) or binary event (Amazon) format. + + \Input *pVoipTranscribe - module state + \Input *pTransport - transport ref + \Input *pVoipBufferSrc - buffer of data to encode for sending + + \Output + VoipBufferT * - pointer to VoipBuffer to send from + + \Version 12/16/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static VoipBufferT *_VoipTranscribeSendEncode(VoipTranscribeRefT *pVoipTranscribe, TransportT *pTransport, VoipBufferT *pVoipBufferSrc) +{ + VoipBufferT *pVoipBufferSnd = &pVoipTranscribe->VoipBufferSnd; + + // if send buffer has been emptied, reset + if (pVoipBufferSnd->iBufInp == pVoipBufferSnd->iBufOff) + { + pVoipBufferSnd->iBufInp = pVoipBufferSnd->iBufOff = 0; + } + + // encode the audio - this consumes data from the source buffer and writes encoded audio into the send buffer + if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_GOOGLE) + { + // if we're at the start of a send, we need to prefix the audio with the encoding + if (pVoipBufferSrc->iBufInp == 0) + { + pVoipBufferSnd->iBufOff = (pTransport->eTransport == TRANSPORT_HTTP) ? _VoipTranscribeBase64Start(pVoipTranscribe, pVoipBufferSnd) : _VoipTranscribeProtobufStart(pVoipTranscribe, pVoipBufferSnd); + } + // encode the audio based on transport type + pVoipBufferSnd->iBufOff += (pTransport->eTransport == TRANSPORT_HTTP) ? _VoipTranscribeBase64Encode(pVoipBufferSnd, pVoipBufferSrc) : _VoipTranscribeProtobufEncode(pVoipBufferSnd, pVoipBufferSrc); + } + else // Amazon + { + pVoipBufferSnd->iBufOff += _VoipTranscribeAwsEncode(pVoipTranscribe, pVoipBufferSnd, pVoipBufferSrc); + } + + // if recording is finished and we've sent all the data, finish the request + if (pVoipBufferSrc->bRecFinished && (pVoipBufferSrc->iBufInp == pVoipBufferSrc->iBufOff)) + { + if ((pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_GOOGLE) && (pTransport->eTransport == TRANSPORT_HTTP)) + { + pVoipBufferSnd->iBufOff += ds_snzprintf((char *)pVoipBufferSnd->pBuffer+pVoipBufferSnd->iBufOff, pVoipBufferSnd->iBufLen-pVoipBufferSnd->iBufOff, "\"}}"); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_AMAZON) + { + pVoipBufferSnd->iBufOff += _VoipTranscribeAwsEncode(pVoipTranscribe, pVoipBufferSnd, NULL); + } + } + + // return send voipbuffer + return(pVoipBufferSnd); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeSend + + \Description + Send a transcription request + + \Input *pVoipTranscribe - module state + \Input *pVoipBuffer - buffer to send + + \Output + int32_t - negative=failure, else success + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeSend(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + int32_t iResult=0; + + // amazon and google need audio encoded for transport; do that here + if ((pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_AMAZON) || (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_GOOGLE)) + { + pVoipBuffer = _VoipTranscribeSendEncode(pVoipTranscribe, pTransport, pVoipBuffer); + } + + // if we have data to send, send it + if (pVoipBuffer->iBufInp < pVoipBuffer->iBufOff) + { + iResult = pTransport->Send(pTransport->pState, pTransport->iStreamId, (const char *)pVoipBuffer->pBuffer+pVoipBuffer->iBufInp, pVoipBuffer->iBufOff-pVoipBuffer->iBufInp); + if (iResult > 0) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: [%d] sent [0x%04x,0x%04x]\n", pVoipBuffer->iBuffer, pVoipBuffer->iBufInp, pVoipBuffer->iBufInp+iResult)); + pVoipBuffer->iBufInp += iResult; + } + else if (iResult < 0) + { + NetPrintf(("voiptranscribe: Send() returned %d\n", iResult)); + } + } + + // return result code to caller + return(iResult); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeUpdateBackoff + + \Description + Do backoff processing + + \Input *pVoipTranscribe - pointer to module state + + \Version 12/05/2018 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTranscribeUpdateBackoff(VoipTranscribeRefT *pVoipTranscribe) +{ + // do not process if backoff timer is not set + if (pVoipTranscribe->uBackoffTimer == 0) + { + return; + } + // reset/clear backoff timer on expiration + if (NetTickDiff(pVoipTranscribe->uBackoffTimer, NetTick()) <= 0) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 0, "voiptranscribe: clearing backoff timer\n")); + pVoipTranscribe->uBackoffTimer = 0; + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeUpdateRecord + + \Description + Update recording of audio data; this function tracks if the recording + should be considered done for this buffer due to the silence timeout + being exceeded, and finalizes the audio buffer. + + \Input *pVoipTranscribe - module state + \Input uCurTick - current tick count + + \Version 12/14/2018 (jbrookes) Split from VoipTranscribeUpdate() +*/ +/********************************************************************************F*/ +static void _VoipTranscribeUpdateRecord(VoipTranscribeRefT *pVoipTranscribe, uint32_t uCurTick) +{ + VoipBufferT *pVoipBuffer = &pVoipTranscribe->VoipBuffer[pVoipTranscribe->iRecBuffer]; + #if DIRTYCODE_LOGGING + static const char *_strStates[] = { "ST_FAIL", "ST_IDLE", "ST_CONN", "ST_SEND", "ST_RECV" }; + #endif + + // see if we have any audio to process + if (pVoipBuffer->iNumSamples == 0) + { + return; + } + // see if we're done submitting data on active recording buffer + if ((pVoipBuffer->bRecStarting || (NetTickDiff(uCurTick, pVoipTranscribe->uVoipTick) < VOIPTRANSCRIBE_SENDTIMEOUT)) && !pVoipBuffer->bRecFull && (pVoipBuffer->iNumSamples < VOIPTRANSCRIBE_MAXREQSAMPLES)) + { + return; + } + /* if this buffer is ready to submit, but our other buffer is in a non-idle state, we gate + submitting the buffer until the other buffer is idle (not connecting/sending/receiving) */ + if ((pVoipTranscribe->eState != ST_IDLE) && (pVoipTranscribe->iSndBuffer != pVoipTranscribe->iRecBuffer)) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 0, "voiptranscribe: [%d] waiting to finish submitting due to being in state %s(%d)\n", pVoipBuffer->iBuffer, _strStates[pVoipTranscribe->eState+1], pVoipTranscribe->eState)); + return; + } + // check to see if we should squelch this + if (_VoipTranscribeBackoffCheck(pVoipTranscribe, pVoipBuffer)) + { + return; + } + + // finish transcribing audio + _VoipTranscribeSubmitFinish(pVoipTranscribe, pVoipBuffer); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeUpdateSend + + \Description + Update sending of audio data. This function meters send size to a minimum + amount for network efficiency, and handles complention of sending when + all of the recorded data has been sent. + + \Input *pVoipTranscribe - module state + \Input *pVoipBuffer - pointer to voipbuffer being sent + + \Output + int32_t - updated state + + \Version 12/14/2018 (jbrookes) Split from VoipTranscribeUpdate() +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeUpdateSend(VoipTranscribeRefT *pVoipTranscribe, VoipBufferT *pVoipBuffer) +{ + int32_t iResult, iState=ST_SEND; + // wait until we have enough data to send (or if we are done recording) + if (((pVoipBuffer->iBufOff-pVoipBuffer->iBufInp) < 1280) && !pVoipBuffer->bRecFinished) + { + return(iState); + } + // send the data + if ((iResult = _VoipTranscribeSend(pVoipTranscribe, pVoipBuffer)) < 0) + { + NetPrintf(("voiptranscribe: [%d] send failed result=%d\n", iResult)); + return(ST_FAIL); + } + // see if we're done + if ((pVoipBuffer->iBufInp == pVoipBuffer->iBufOff) && pVoipBuffer->bRecFinished) + { + // finish sending process and transition to receive state + if ((iResult = _VoipTranscribeSendFinish(pVoipTranscribe)) > 0) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: [%d] send complete result=%d\n", pVoipBuffer->iBuffer, iResult)); + iState = ST_RECV; + } + else if (iResult < 0) + { + iState = ST_FAIL; + } + else + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: [%d] could not send finish; will try again\n", pVoipBuffer->iBuffer)); + } + } + // if transitioning to recv, reset buffer + if (iState == ST_RECV) + { + _VoipTranscribeBufferReset(pVoipTranscribe, pVoipBuffer); + pVoipTranscribe->iSndBuffer = -1; + } + return(iState); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeUpdateRecv + + \Description + Update receiving of transcription response. + + \Input *pVoipTranscribe - module state + + \Output + int32_t - updated state + + \Version 12/14/2018 (jbrookes) Split from VoipTranscribeUpdate() +*/ +/********************************************************************************F*/ +static int32_t _VoipTranscribeUpdateRecv(VoipTranscribeRefT *pVoipTranscribe) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + int32_t iResult, iState=pVoipTranscribe->eState; + + // see if there's anything to receive + if ((iResult = pTransport->Recv(pTransport->pState, pTransport->iStreamId, pVoipTranscribe->strResponse, sizeof(pVoipTranscribe->strResponse))) >= 0) + { + // null terminate and log response if we're expecting text + if (pTransport->eTransport != TRANSPORT_HTTP2) + { + pVoipTranscribe->strResponse[iResult] = '\0'; + NetPrintfVerbose((pVoipTranscribe->iVerbose, 2, "voiptranscribe: response (%d bytes)\n%s\n", iResult, pVoipTranscribe->strResponse)); + } + + // parse the result + if ((iResult = _VoipTranscribeParseResponse(pVoipTranscribe, pVoipTranscribe->strResponse, iResult, pVoipTranscribe->strTranscription, sizeof(pVoipTranscribe->strTranscription))) > 0) + { + // update transcription length metric + uint32_t uTranscriptionLength = (uint32_t)strnlen(pVoipTranscribe->strTranscription, sizeof(pVoipTranscribe->strTranscription)); + pVoipTranscribe->Metrics.uCharCountRecv += uTranscriptionLength; + pVoipTranscribe->Metrics.uDelay += NetTickDiff(NetTick(), pVoipTranscribe->uSttStartTime); + if (uTranscriptionLength == 0) + { + // keep track of number of consecutive empty results + pVoipTranscribe->iConsecEmptyCt += 1; + // update overall empty result count + pVoipTranscribe->Metrics.uEmptyResultCount += 1; + // set backoff if appropriate + _VoipTranscribeBackoffSet(pVoipTranscribe); + } + else + { + // reset consecutive empty result tracker + pVoipTranscribe->iConsecEmptyCt = 0; + } + // reset consecutive error count metric + pVoipTranscribe->iConsecErrorCt = 0; + // log transcription and transition back to idle state + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: transcript=%s\n", pVoipTranscribe->strTranscription)); + iState = ST_IDLE; + } + else if (iResult < 0) + { + NetPrintf(("voiptranscribe: service error: %s\n", pVoipTranscribe->strTranscription)); + pVoipTranscribe->strTranscription[0] = '\0'; + iState = ST_FAIL; + } + + // clean up transaction if http2 + if (pTransport->eTransport == TRANSPORT_HTTP2) + { + ProtoHttp2StreamFree(pTransport->pState, pTransport->iStreamId); + pTransport->iStreamId = PROTOHTTP2_INVALID_STREAMID; + } + } + else if ((iResult < 0) && (iResult != VOIPTRANSCRIBE_WAIT)) + { + NetPrintf(("voiptranscribe: recv() returned %d\n", iResult)); + iState = ST_FAIL; + } + // return updated state + return(iState); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTranscribeConfig + + \Description + Configure the VoipTranscribe module for use. This call is required to + specify the provider, url, and credentials that will be used to access + the transcription service. + + \Input *pVoipTranscribe - pointer to module state + \Input uProfile - transcribe profile (VOIPTRANSCRIBE_PROFILE_DISABLED to disable) + \Input *pUrl - transcribe provider url + \Input *pCred - transcribe credentials + + \Output + uint32_t - TRUE if configured successfully + + \Version 11/08/2018 (tcho) +*/ +/********************************************************************************F*/ +static uint32_t _VoipTranscribeConfig(VoipTranscribeRefT *pVoipTranscribe, uint32_t uProfile, const char *pUrl, const char *pCred) +{ + NetCritEnter(NULL); + + // clean up previous transport state + _VoipTranscribeTransportCleanup(pVoipTranscribe); + + // save configuration parameters + if (VOIPTRANSCRIBE_PROFILE_PROVIDER(uProfile) != VOIPTRANSCRIBE_PROVIDER_NONE) + { + pVoipTranscribe->uProfile = uProfile; + ds_strnzcpy(pVoipTranscribe->strKey, pCred, sizeof(pVoipTranscribe->strKey)); + ds_strnzcpy(pVoipTranscribe->strUrl, pUrl, sizeof(pVoipTranscribe->strUrl)); + } + else + { + NetPrintf(("voiptranscribe: disabled\n")); + pVoipTranscribe->uProfile = uProfile; + ds_memclr(pVoipTranscribe->strKey, sizeof(pVoipTranscribe->strKey)); + ds_memclr(pVoipTranscribe->strUrl, sizeof(pVoipTranscribe->strUrl)); + NetCritLeave(NULL); + return(FALSE); + } + + // set provider info + pVoipTranscribe->eProvider = VOIPTRANSCRIBE_PROFILE_PROVIDER(pVoipTranscribe->uProfile); + if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_MICROSOFT) + { + // install CA certificate required to access microsoft servers + ProtoSSLSetCACert((const uint8_t *)_strCyberTrustRootCA, sizeof(_strCyberTrustRootCA)); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_GOOGLE) + { + // install CA certificate required to access Google Speech-to-text server + ProtoSSLSetCACert((const uint8_t *)_strGlobalSignRootCAR2, sizeof(_strGlobalSignRootCAR2)); + } + else if (pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_AMAZON) + { + // install CA certificate required to access Amazon Transcribe + ProtoSSLSetCACert((const uint8_t *)_strAmazonRootCAR1, sizeof(_strAmazonRootCAR1)); + } + + // init transport class + if (_VoipTranscribeTransportInit(pVoipTranscribe) < 0) + { + NetPrintf(("voiptranscribe: could not initialize transport module\n")); + VoipTranscribeDestroy(pVoipTranscribe); + NetCritLeave(NULL); + return(FALSE); + } + + // set audio parameters + pVoipTranscribe->iAudioRate = VOIPTRANSCRIBE_AUDIORATE; + pVoipTranscribe->eFormat = VOIPTRANSCRIBE_PROFILE_FORMAT(pVoipTranscribe->uProfile); + if (pVoipTranscribe->eProvider != VOIPTRANSCRIBE_PROVIDER_GOOGLE) + { + if (pVoipTranscribe->eFormat == VOIPTRANSCRIBE_FORMAT_LI16) + { + ds_snzprintf(pVoipTranscribe->strAudioFormat, sizeof(pVoipTranscribe->strAudioFormat), "audio/l16; rate=%d; endianness=little-endian", pVoipTranscribe->iAudioRate); + pVoipTranscribe->bCompressed = FALSE; + } + else if (pVoipTranscribe->eFormat == VOIPTRANSCRIBE_FORMAT_WAV16) + { + ds_snzprintf(pVoipTranscribe->strAudioFormat, sizeof(pVoipTranscribe->strAudioFormat), "audio/wav; codec=audio/pcm; samplerate=%d", pVoipTranscribe->iAudioRate); + pVoipTranscribe->bCompressed = FALSE; + } + else if (pVoipTranscribe->eFormat == VOIPTRANSCRIBE_FORMAT_OPUS) + { + ds_strnzcpy(pVoipTranscribe->strAudioFormat, "audio/ogg; codecs=opus", sizeof(pVoipTranscribe->strAudioFormat)); + pVoipTranscribe->bCompressed = TRUE; + } + } + else + { + if (pVoipTranscribe->eFormat == VOIPTRANSCRIBE_FORMAT_LI16) + { + ds_strnzcpy(pVoipTranscribe->strAudioFormat, "LINEAR16", sizeof(pVoipTranscribe->strAudioFormat)); + pVoipTranscribe->bCompressed = FALSE; + } + else if (pVoipTranscribe->eFormat == VOIPTRANSCRIBE_FORMAT_OPUS) + { + ds_strnzcpy(pVoipTranscribe->strAudioFormat, "OGG_OPUS", sizeof(pVoipTranscribe->strAudioFormat)); + pVoipTranscribe->bCompressed = TRUE; + } + } + NetCritLeave(NULL); + + return(TRUE); +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeCreate + + \Description + Create the stream module + + \Input iBufSize - size of streaming buffer (at least VOIPTRANSCRIBE_MINBUFFER) + + \Output + VoipTranscribeRefT * - new module state, or NULL + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +VoipTranscribeRefT *VoipTranscribeCreate(int32_t iBufSize) +{ + VoipTranscribeRefT *pVoipTranscribe; + void *pMemGroupUserData; + int32_t iMemGroup; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // enforce minimum buffer size + if (iBufSize < VOIPTRANSCRIBE_MINBUFFER) + { + iBufSize = VOIPTRANSCRIBE_MINBUFFER; + } + + // allocate and init module state + if ((pVoipTranscribe = DirtyMemAlloc(sizeof(*pVoipTranscribe), VOIPTRANSCRIBE_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voiptranscribe: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pVoipTranscribe, sizeof(*pVoipTranscribe)); + pVoipTranscribe->iMemGroup = iMemGroup; + pVoipTranscribe->pMemGroupUserData = pMemGroupUserData; + + // allocate and initialize buffers + if (!_VoipTranscribeBufferInit(pVoipTranscribe, &pVoipTranscribe->VoipBuffer[0], iBufSize, 0) || !_VoipTranscribeBufferInit(pVoipTranscribe, &pVoipTranscribe->VoipBuffer[1], iBufSize, 1)) + { + NetPrintf(("voiptranscribe: could not allocate voip buffers\n")); + VoipTranscribeDestroy(pVoipTranscribe); + return(NULL); + } + // allocate and initialize voip send buffer; this is used for google, which needs data to be encoded + if (!_VoipTranscribeBufferInit(pVoipTranscribe, &pVoipTranscribe->VoipBufferSnd, 2*1024, -1)) + { + NetPrintf(("voiptranscribe: could not allocate voip send buffer\n")); + VoipTranscribeDestroy(pVoipTranscribe); + return(NULL); + } + + // init other state variables + pVoipTranscribe->bMinDiscard = TRUE; + pVoipTranscribe->iConsecEmptyMax = VOIPTRANSCRIBE_CONSECEMPTY; + pVoipTranscribe->iConsecErrorMax = VOIPTRANSCRIBE_CONSECERROR; + pVoipTranscribe->iSndBuffer = -1; + pVoipTranscribe->iVerbose = 1; + + // configure for particular provider + if (!_VoipTranscribeConfig(pVoipTranscribe, _VoipTranscribe_Config.uProfile, _VoipTranscribe_Config.strUrl, _VoipTranscribe_Config.strKey)) + { + NetPrintf(("voiptranscribe: could not configure for provider\n")); + VoipTranscribeDestroy(pVoipTranscribe); + return(NULL); + } + + // return ref to caller + return(pVoipTranscribe); +} + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeConfig + + \Description + Set global configuration of transcription service. This call is required to + specify the provider, url, and credentials that will be used to access + the transcription service. + + \Input uProfile - transcribe profile (VOIPTRANSCRIBE_PROFILE_DISABLED to disable) + \Input *pUrl - transcribe provider url + \Input *pKey - transcribe access key + + \Version 11/08/2018 (tcho) +*/ +/********************************************************************************F*/ +void VoipTranscribeConfig(uint32_t uProfile, const char *pUrl, const char *pKey) +{ + NetCritEnter(NULL); + _VoipTranscribe_Config.uProfile = uProfile; + ds_strnzcpy(_VoipTranscribe_Config.strKey, pKey, sizeof(_VoipTranscribe_Config.strKey)); + ds_strnzcpy(_VoipTranscribe_Config.strUrl, pUrl, sizeof(_VoipTranscribe_Config.strUrl)); + NetCritLeave(NULL); +} + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeDestroy + + \Description + Destroy the VoipTranscribe module + + \Input *pVoipTranscribe - pointer to module state + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipTranscribeDestroy(VoipTranscribeRefT *pVoipTranscribe) +{ + // dispose of audio buffers + if (pVoipTranscribe->VoipBuffer[0].pBuffer != NULL) + { + DirtyMemFree(pVoipTranscribe->VoipBuffer[0].pBuffer, VOIPTRANSCRIBE_MEMID, pVoipTranscribe->iMemGroup, pVoipTranscribe->pMemGroupUserData); + } + if (pVoipTranscribe->VoipBuffer[1].pBuffer != NULL) + { + DirtyMemFree(pVoipTranscribe->VoipBuffer[1].pBuffer, VOIPTRANSCRIBE_MEMID, pVoipTranscribe->iMemGroup, pVoipTranscribe->pMemGroupUserData); + } + if (pVoipTranscribe->VoipBufferSnd.pBuffer != NULL) + { + DirtyMemFree(pVoipTranscribe->VoipBufferSnd.pBuffer, VOIPTRANSCRIBE_MEMID, pVoipTranscribe->iMemGroup, pVoipTranscribe->pMemGroupUserData); + } + + // cleanup transport state + _VoipTranscribeTransportCleanup(pVoipTranscribe); + + // dispose of module memory + DirtyMemFree(pVoipTranscribe, VOIPTRANSCRIBE_MEMID, pVoipTranscribe->iMemGroup, pVoipTranscribe->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeSubmit + + \Description + Submit voice data to be transcribed + + \Input *pVoipTranscribe - pointer to module state + \Input *pBuffer - voice data to be transcribed + \Input iBufLen - size of voice data in bytes + + \Output + int32_t - number of bytes copied + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTranscribeSubmit(VoipTranscribeRefT *pVoipTranscribe, const uint8_t *pBuffer, int32_t iBufLen) +{ + // submit and return amount copied to caller + return(_VoipTranscribeSubmit(pVoipTranscribe, pBuffer, iBufLen)); +} + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeGet + + \Description + Get transcription if available; if a transcription is available, this + call copies it and clears it. + + \Input *pVoipTranscribe - pointer to module state + \Input *pBuffer - [out] output buffer + \Input iBufLen - size of output buffer + + \Output + int32_t - zero=no transcription, else transcription copied + + \Version 09/07/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTranscribeGet(VoipTranscribeRefT *pVoipTranscribe, char *pBuffer, int32_t iBufLen) +{ + if (pVoipTranscribe->strTranscription[0] == '\0') + { + return(0); + } + ds_strnzcpy((char *)pBuffer, pVoipTranscribe->strTranscription, iBufLen); + pVoipTranscribe->strTranscription[0] = '\0'; + return(1); +} + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeStatus + + \Description + Get module status. + + \Input *pVoipTranscribe - pointer to module state + \Input iStatus - status selector + \Input iValue - selector specific + \Input *pBuffer - selector specific + \Input iBufSize - selector specific + + \Output + int32_t - selector specific + + \Notes + iStatus can be one of the following: + + \verbatim + 'cmpr' - most recent http result code + 'sttm' - get the VoipSpeechToTextMetricsT via pBuf + \endverbatim + + Unrecognized codes are passed down to the transport handler + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTranscribeStatus(VoipTranscribeRefT *pVoipTranscribe, int32_t iStatus, int32_t iValue, void *pBuffer, int32_t iBufSize) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + // return whether audio format is compressed or not + if (iStatus == 'cmpr') + { + return(pVoipTranscribe->bCompressed); + } + if (iStatus == 'sttm') + { + if ((pBuffer != NULL) && (iBufSize >= (int32_t)sizeof(VoipSpeechToTextMetricsT))) + { + ds_memcpy_s(pBuffer, iBufSize, &pVoipTranscribe->Metrics, sizeof(VoipSpeechToTextMetricsT)); + return(0); + } + return(-1); + } + return(pTransport->Status(pTransport->pState, pTransport->iStreamId, iStatus, pBuffer, iBufSize)); +} + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeControl + + \Description + Set control options + + \Input *pVoipTranscribe - pointer to module state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Output + int32_t - selector specific + + \Notes + iStatus can be one of the following: + + \verbatim + 'cstm' - clear speech to text metrics in VoipSpeechToTextMetricsT + 'spam' - set verbose debug level (debug only) + 'time' - set timeout value + 'vdis' - set voice discard on minimum threshold (default=TRUE) + \endverbatim + + Unhandled codes are passed through to the transport handler + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTranscribeControl(VoipTranscribeRefT *pVoipTranscribe, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + + if (iControl == 'cstm') + { + ds_memclr(&(pVoipTranscribe->Metrics), sizeof(pVoipTranscribe->Metrics)); + return(0); + } + #if DIRTYCODE_LOGGING + // set verbosity for us and pass through to transport handler + if (iControl == 'spam') + { + pVoipTranscribe->iVerbose = iValue; + } + #endif + if (iControl == 'time') + { + // remember most recent timeout value, and pass through to transport handler + pVoipTranscribe->iTimeout = iValue; + } + if (iControl == 'vdis') + { + uint8_t bDiscard = iValue ? TRUE : FALSE; + if (pVoipTranscribe->bMinDiscard != bDiscard) + { + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: min discard %s\n", bDiscard ? "enabled" : "disabled")); + pVoipTranscribe->bMinDiscard = bDiscard; + } + return(0); + } + // if not handled, let transport handler take a stab at it + return(pTransport->Control(pTransport->pState, pTransport->iStreamId, iControl, iValue, iValue2, pValue)); +} + +/*F********************************************************************************/ +/*! + \Function VoipTranscribeUpdate + + \Description + Update the VoipTranscribe module + + \Input *pVoipTranscribe - pointer to module state + + \Version 08/30/2018 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipTranscribeUpdate(VoipTranscribeRefT *pVoipTranscribe) +{ + TransportT *pTransport = &pVoipTranscribe->Transport; + uint32_t uCurTick = NetTick(); + int32_t iResult; + + // give time to transport module + pTransport->Update(pTransport->pState); + + // update backoff processing + _VoipTranscribeUpdateBackoff(pVoipTranscribe); + + // update recording processing + _VoipTranscribeUpdateRecord(pVoipTranscribe, uCurTick); + + /* if we have a websockets connection to watson and are in the idle state, we receive to consume "listening" responses that come after transcription + responses. if we do not read these responses, the unread data prevents us from detecting if the server has timed out the connection on us, and + results in the next transcription request failing */ + if ((pVoipTranscribe->eProvider == VOIPTRANSCRIBE_PROVIDER_IBMWATSON) && (pVoipTranscribe->eTransport == VOIPTRANSCRIBE_TRANSPORT_WEBSOCKETS) && + (pVoipTranscribe->eState == ST_IDLE) && (_VoipTranscribeConnectCheck(pVoipTranscribe, &pVoipTranscribe->Transport) > 0)) + { + pVoipTranscribe->eState = _VoipTranscribeUpdateRecv(pVoipTranscribe); + } + + // check for enough data in the current record buffer to start request + if ((pVoipTranscribe->eState == ST_IDLE) && (pVoipTranscribe->iSndBuffer == -1)) + { + VoipBufferT *pVoipBuffer = &pVoipTranscribe->VoipBuffer[pVoipTranscribe->iRecBuffer]; + // see if we have enough data in our current record buffer to start sending (minimum one second) + if ((pVoipBuffer->iNumSamples < pVoipTranscribe->iAudioRate) && pVoipTranscribe->bMinDiscard) + { + return; + } + // if backoff timer is set, defer sending as we might end up squelching it + if (pVoipTranscribe->uBackoffTimer != 0) + { + return; + } + // set send buffer + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: [%d] starting transcription request on recording buffer\n", pVoipBuffer->iBuffer)); + pVoipTranscribe->iSndBuffer = pVoipTranscribe->iRecBuffer; + } + + // if we're in idle state and have an assigned send buffer, start the request + if ((pVoipTranscribe->eState == ST_IDLE) && (pVoipTranscribe->iSndBuffer != -1)) + { + VoipBufferT *pVoipBuffer = &pVoipTranscribe->VoipBuffer[pVoipTranscribe->iSndBuffer]; + NetPrintfVerbose((pVoipTranscribe->iVerbose, 1, "voiptranscribe: [%d] starting transcription request\n", pVoipBuffer->iBuffer)); + // copy mindiscard flag for this buffer + pVoipBuffer->bMinDiscard = pVoipTranscribe->bMinDiscard; + // perform explicit connection for transport handlers that require it + pVoipTranscribe->eState = (_VoipTranscribeConnect(pVoipTranscribe) >= 0) ? ST_CONN : ST_FAIL; + } + + // update module in connecting state + if (pVoipTranscribe->eState == ST_CONN) + { + // check for connection completion for transport handlers that require it + if ((iResult = _VoipTranscribeConnectCheck(pVoipTranscribe, &pVoipTranscribe->Transport)) < 0) + { + pVoipTranscribe->eState = ST_FAIL; + return; + } + else if (iResult == 0) + { + return; + } + + // make transcription request and transition to sending voice data for transcription if successful + pVoipTranscribe->eState = (_VoipTranscribeRequest(pVoipTranscribe) >= 0) ? ST_SEND : ST_FAIL; + } + + // update while sending the transcription request + if (pVoipTranscribe->eState == ST_SEND) + { + pVoipTranscribe->eState = _VoipTranscribeUpdateSend(pVoipTranscribe, &pVoipTranscribe->VoipBuffer[pVoipTranscribe->iSndBuffer]); + } + + // update while receiving the transcription response + if (pVoipTranscribe->eState == ST_RECV) + { + pVoipTranscribe->eState = _VoipTranscribeUpdateRecv(pVoipTranscribe); + } + + // update when in failed state + if (pVoipTranscribe->eState == ST_FAIL) + { + // keep track of number of consecutive failures + pVoipTranscribe->iConsecErrorCt += 1; + // update overall failure count + pVoipTranscribe->Metrics.uErrorCount += 1; + // set backoff if appropriate + _VoipTranscribeBackoffSet(pVoipTranscribe); + // reset current record buffer + _VoipTranscribeBufferReset(pVoipTranscribe, &pVoipTranscribe->VoipBuffer[pVoipTranscribe->iRecBuffer]); + // go back to idle state + pVoipTranscribe->eState = ST_IDLE; + } +} diff --git a/r5dev/thirdparty/dirtysdk/source/voip/voiptunnel.c b/r5dev/thirdparty/dirtysdk/source/voip/voiptunnel.c new file mode 100644 index 00000000..7bc5d590 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/voip/voiptunnel.c @@ -0,0 +1,2239 @@ +/*H********************************************************************************/ +/*! + \File voiptunnel.c + + \Description + This module implements the main logic for the VoipTunnel server. + + Description forthcoming. + + \Copyright + Copyright (c) 2006 Electronic Arts Inc. + + \Version 03/24/2006 (jbrookes) First Version +*/ +/********************************************************************************H*/ + +/*** Include files ****************************************************************/ + +#include +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/dirtysock/dirtymem.h" +#include "DirtySDK/dirtysock/dirtynet.h" +#include "DirtySDK/dirtysock/netconn.h" + +#include "DirtySDK/voip/voipdef.h" +#include "DirtySDK/voip/voiptunnel.h" +#include "voippriv.h" +#include "voippacket.h" + +/*** Defines **********************************************************************/ + +//! define this as TRUE to enable lookup table test code +#define VOIPTUNNEL_LOOKUP_TEST (FALSE) + +//! voiptunnel broadcast flags +#define VOIPTUNNEL_GAMEFLAG_BROADCAST_ALL 0 +#define VOIPTUNNEL_GAMEFLAG_BROADCAST_ALLOTHERS 1 +#define VOIPTUNNEL_GAMEFLAG_SEND_SINGLE 2 +#define VOIPTUNNEL_GAMEFLAG_SEND_MULTI 4 +#define VOIPTUNNEL_GAMEFLAG_MASK 0xf + +#define VOIPTUNNEL_GAMEFLAG_VDP 16 + +/*** Type Definitions *************************************************************/ + +//! VoIP packet data that this module cares about (header + first 4 bytes) +typedef struct VoipTunnelPacketT +{ + VoipPacketHeadT Head; //!< packet header + uint8_t aRemoteClientId[4]; //!< present in all packets except for mic packets, which have the send mask here instead +} VoipTunnelPacketT; + +//! client lookup table element +typedef struct VoipTunnelLookupElemT +{ + uint32_t uClientId; + uint32_t uClientIdx; +} VoipTunnelLookupElemT; + +//! module state +struct VoipTunnelRefT +{ + // module memory group + int32_t iMemGroup; //!< module mem group id + void *pMemGroupUserData; //!< user data associated with mem group + + SocketT *pVoipSocket; //!< virtual socket for receiving tunneled voip data + VoipTunnelCallbackT *pCallback; //!< optional voiptunnel event callback + void *pUserData; //!< user data for voiptunnel callback + uint32_t uLocalClientId; //!< local client id (used for tunnel connectivity) + uint16_t uVoipPort; //!< virtual voip port + uint16_t uVoiceRecvTimeout; //!< number of milliseconds before clearing RECVVOICE flag + uint8_t uDebugLevel; //!< debug level + uint8_t bPortSniff; //!< TRUE if port sniffing enabled, else FALSE (default FALSE) + uint8_t _pad[2]; + int32_t iNumClients; //!< number of tunnel clients + int32_t iNumTalkingClients; //!< number of "talking" clients (i.e. client with VOIPTUNNEL_CLIENTFLAG_RECVVOICE flag set) + int32_t iMaxClients; //!< maximum number tunnel clients + int32_t iMaxVoiceBroadcasters; //!< maximum number of players can be talking at once in a single game + VoipTunnelLookupElemT *pLookupTable; //!< fast-lookup table + int32_t iNumGames; //!< current number of games + int32_t iMaxGames; //!< maximum number of supported gameservers + uint32_t uVoiceDataDropMetric; //!< the number of voice packets that were not rebroadcast globally due to max broadcasters constraint + uint32_t uVoiceMaxTalkersMetric;//!< the number of time the max broadcasters event was reached + VoipTunnelGameT *pGameList; //!< list of games + VoipTunnelClientT ClientList[1]; //!< variable-length tunnel client list -- MUST COME LAST IN THE STRUCTURE +}; + +/*** Variables ********************************************************************/ + + +/*** Private Functions ************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelEventCallback + + \Description + Call user event callback, if it is available. + + \Input *pVoipTunnel - voiptunnel ref + \Input eEvent - event triggering callback + \Input *pClient - pointer to client associated with event + \Input iDataSize - size of packet data associated with event + + \Version 03/01/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTunnelEventCallback(VoipTunnelRefT *pVoipTunnel, VoipTunnelEventE eEvent, VoipTunnelClientT *pClient, int32_t iDataSize) +{ + if (pVoipTunnel->pCallback != NULL) + { + VoipTunnelEventDataT EventData; + EventData.eEvent = eEvent; + EventData.pClient = pClient; + EventData.iDataSize = iDataSize; + pVoipTunnel->pCallback(pVoipTunnel, &EventData, pVoipTunnel->pUserData); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelClientListSend + + \Description + Broadcast input data to all other clients in our session. + + \Input *pVoipTunnel - voiptunnel ref + \Input *pSrcClient - pointer to client that sent the data + \Input uDstClientId - clientId to send to, if SEND_SINGLE + \Input *pPacketData - pointer to packet data + \Input iPacketSize - size of packet data + \Input *pAddr - address data came from + \Input uSendFlag - send flags + + \Version 03/31/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTunnelClientListSend(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pSrcClient, uint32_t uDstClientId, const char *pPacketData, int32_t iPacketSize, struct sockaddr *pAddr, uint32_t uSendFlag) +{ + VoipTunnelClientT *pClient; + int32_t iClient, iResult; + + // if vdp, restore the header + if (uSendFlag & VOIPTUNNEL_GAMEFLAG_VDP) + { + pPacketData -= 2; + iPacketSize += 2; + } + + // unicast or broadcast? + if (!(uSendFlag & VOIPTUNNEL_GAMEFLAG_SEND_SINGLE)) + { + VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pSrcClient->iGameIdx]; + + // forward data data to other clients in session + for (iClient = 0; iClient < VOIPTUNNEL_MAXGROUPSIZE; iClient++) + { + // make sure the client is active + if (pGame->bClientActive[iClient] == FALSE) + { + continue; + } + + // ref client + if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iClient])) == NULL) + { + continue; + } + + // don't send data to source client + if ((uSendFlag & VOIPTUNNEL_GAMEFLAG_BROADCAST_ALLOTHERS) && (pClient == pSrcClient)) + { + continue; + } + + // if send mask not set for this client, skip them + if ((pSrcClient->uSendMask & (1 << (unsigned)iClient)) == 0) + { + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: client send mask mute of voice from clientId=0x%08x to clientId=0x%08x\n", pSrcClient->uClientId, pClient->uClientId)); + continue; + } + + // if game send mask not set for this client, skip them + if ((pSrcClient->uGameSendMask & (1 << (unsigned)iClient)) == 0) + { + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: game send mask mute of voice from clientId=0x%08x to clientId=0x%08x\n", pSrcClient->uClientId, pClient->uClientId)); + continue; + } + + // rewrite address to send to this client + SockaddrInSetAddr(pAddr, pClient->uRemoteAddr); + // if port sniffing is enabled, rewrite port as well + if (pVoipTunnel->bPortSniff) + { + // don't send if we don't have a port to send to + if (pClient->uRemoteVoipPort == 0) + { + continue; + } + SockaddrInSetPort(pAddr, pClient->uRemoteVoipPort); + } + + if ((iResult = SocketSendto(pVoipTunnel->pVoipSocket, pPacketData, iPacketSize, 0, pAddr, sizeof(*pAddr))) == iPacketSize) + { + // call user callback + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_SENDVOICE, pClient, iPacketSize); + } + else + { + NetPrintf(("voiptunnel: send of %d byte voice packet from clientId=0x%08x to clientId=0x%08x failed (err=%d)\n", iPacketSize, pSrcClient->uClientId, pClient->uClientId, iResult)); + } + } + } + else + { + if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, uDstClientId)) != NULL) + { + // rewrite address to send to this client + SockaddrInSetAddr(pAddr, pClient->uRemoteAddr); + // if port sniffing is enabled, rewrite port as well + if (pVoipTunnel->bPortSniff) + { + // don't send if we don't have a port to send to + if (pClient->uRemoteVoipPort == 0) + { + return; + } + SockaddrInSetPort(pAddr, pClient->uRemoteVoipPort); + } + + if ((iResult = SocketSendto(pVoipTunnel->pVoipSocket, pPacketData, iPacketSize, 0, pAddr, sizeof(*pAddr))) == iPacketSize) + { + // call user callback + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_SENDVOICE, pClient, iPacketSize); + } + else + { + NetPrintf(("voiptunnel: send of %d byte voice packet from clientId=0x%08x to clientId=0x%08x failed (err=%d)\n", iPacketSize, pSrcClient->uClientId, pClient->uClientId, iResult)); + } + } + else + { + NetPrintf(("voiptunnel: unable to ref client with id=0x%08x for unicast send\n", uDstClientId)); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelValidatePacketType + + \Description + Returns whether the data points to a valid packet type or not. + + \Input *pPacketData - pointer to type to evaluate + + \Version 08/24/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTunnelValidatePacketType(const char *pPacketData) +{ + return(!memcmp(pPacketData, "CO", 2) || !memcmp(pPacketData, "DSC", 3) || !memcmp(pPacketData, "PNG", 3) || !memcmp(pPacketData, "MIC", 3)); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelValidatePacket + + \Description + Locate packet data and validate packet + + \Input *pVoipTunnel - voip tunnel + \Input *pPacketData - pointer to packet head + \Input iPacketSize - size of received data + \Input pVdpHeader - vdp header + + \Output + const VoipTunnelPacketT *pPacket - pointer to packet + + \Version 08/24/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static VoipTunnelPacketT *_VoipTunnelValidatePacket(VoipTunnelRefT *pVoipTunnel, char *pPacketData, int32_t iPacketSize, uint32_t *pVdpHeader) +{ + // try and determine if there is a VDP header or not + if (_VoipTunnelValidatePacketType(pPacketData+2)) + { + // VDP header... so adjust the pointer to account for it + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 2, "voiptunnel: vdp header detected\n")); + pPacketData += 2; + iPacketSize -= 2; + *pVdpHeader = TRUE; + } + else if (_VoipTunnelValidatePacketType(pPacketData)) + { + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 2, "voiptunnel: no vdp header\n")); + *pVdpHeader = FALSE; + } + else + { + NetPrintf(("voiptunnel: unknown VoIP packet type; discarding\n")); + return(NULL); + } + + // validate size + if (iPacketSize < (signed)sizeof(VoipTunnelPacketT)) + { + NetPrintf(("voiptunnel: voip packet size is too small; discarding\n")); + return(NULL); + } + + return((VoipTunnelPacketT *)pPacketData); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelGetClientId + + \Description + Read client ID from voice packet + + \Input *pPacket - pointer to packet + \Input iPacketSize - size of received data + + \Output + int32_t - clientId, or zero + + \Version 05/16/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static uint32_t _VoipTunnelGetClientId(const VoipTunnelPacketT *pPacket, int32_t iPacketSize) +{ + uint32_t uClientId; + + // extract the client identifier + uClientId = pPacket->Head.aClientId[0] << 24; + uClientId |= pPacket->Head.aClientId[1] << 16; + uClientId |= pPacket->Head.aClientId[2] << 8; + uClientId |= pPacket->Head.aClientId[3]; + + // return to caller + return(uClientId); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelLookupSort + + \Description + qsort callback used to sort fast lookup array. + + \Input *_pElem0 - pointer to first element to compare + \Input *_pElem1 - pointer to second element to compare + + \Output + int32_t - sort value (one or minus one) + + \Version 04/03/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTunnelLookupSort(const void *_pElem0, const void *_pElem1) +{ + const VoipTunnelLookupElemT *pElem0 = (const VoipTunnelLookupElemT *)_pElem0; + const VoipTunnelLookupElemT *pElem1 = (const VoipTunnelLookupElemT *)_pElem1; + + return((pElem0->uClientId > pElem1->uClientId) ? 1 : -1); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelLookupClient + + \Description + Lookup a client given the clientId using binary search on sorted array. + + \Input *pVoipTunnel - module state + \Input uClientId - client identifier + + \Output + VoipTunnelClientT * - pointer to found client, or NULL if not found + + \Version 04/03/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static VoipTunnelClientT *_VoipTunnelLookupClient(VoipTunnelRefT *pVoipTunnel, uint32_t uClientId) +{ + int32_t iCheck, iLow, iHigh; + uint32_t uCheckId; + + // execute binary search on sorted lookup table + for (iLow = 0, iHigh = pVoipTunnel->iNumClients-1; iLow <= iHigh; ) + { + iCheck = iLow + ((iHigh - iLow) / 2); + if ((uCheckId = pVoipTunnel->pLookupTable[iCheck].uClientId) > uClientId) + { + iHigh = iCheck - 1; + } + else if (uCheckId < uClientId) + { + iLow = iCheck + 1; + } + else + { + #if VOIPTUNNEL_LOOKUP_TEST + NetPrintf(("voiptunnel: lookup found client id=0x%08x\n", uClientId)); + #endif + return(&pVoipTunnel->ClientList[pVoipTunnel->pLookupTable[iCheck].uClientIdx]); + } + } + + // not found + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: lookup could not find client id=0x%08x\n", uClientId)); + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelLookupBuild + + \Description + Build a lookup table sorted by clientId for fast lookups. + + \Input *pVoipTunnel - module state + + \Version 04/03/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTunnelLookupBuild(VoipTunnelRefT *pVoipTunnel) +{ + int32_t iClient; + + // only build it if the table has been allocated + if (pVoipTunnel->pLookupTable == NULL) + { + return; + } + + // build unsorted table + for (iClient = 0; iClient < pVoipTunnel->iNumClients; iClient++) + { + pVoipTunnel->pLookupTable[iClient].uClientId = pVoipTunnel->ClientList[iClient].uClientId; + pVoipTunnel->pLookupTable[iClient].uClientIdx = (unsigned)iClient; + } + + // sort it by clientId + qsort(pVoipTunnel->pLookupTable, iClient, sizeof(pVoipTunnel->pLookupTable[0]), _VoipTunnelLookupSort); +} + +#if VOIPTUNNEL_LOOKUP_TEST +/*F********************************************************************************/ +/*! + \Function _VoipTunnelLookupTest + + \Description + Test code to run lookup code through a few simple test cases. + + \Input *pVoipTunnel - module state + + \Version 04/03/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTunnelLookupTest(VoipTunnelRefT *pVoipTunnel) +{ + VoipTunnelLookupElemT _LookupTable[32]; + + // set up temp table for testing + pVoipTunnel->pLookupTable = _LookupTable; + + // test no elements + pVoipTunnel->iNumClients = 0; + _VoipTunnelLookupBuild(pVoipTunnel); + _VoipTunnelLookupClient(pVoipTunnel, 0); + + // test one element + pVoipTunnel->iNumClients = 1; + pVoipTunnel->ClientList[0].uClientId = 0xdeadbeef; + _VoipTunnelLookupBuild(pVoipTunnel); + _VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef); + + // test two elements + pVoipTunnel->iNumClients = 2; + pVoipTunnel->ClientList[1].uClientId = 0xcacabeef; + _VoipTunnelLookupBuild(pVoipTunnel); + _VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef); + _VoipTunnelLookupClient(pVoipTunnel, 0xcacabeef); + + // test three elements + pVoipTunnel->iNumClients = 3; + pVoipTunnel->ClientList[2].uClientId = 0xf000beef; + _VoipTunnelLookupBuild(pVoipTunnel); + _VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef); + _VoipTunnelLookupClient(pVoipTunnel, 0xcacabeef); + _VoipTunnelLookupClient(pVoipTunnel, 0xf000beef); + + // test many elements + pVoipTunnel->iNumClients = 8; + pVoipTunnel->ClientList[3].uClientId = 0xf000b00f; + pVoipTunnel->ClientList[4].uClientId = 0xf000baaf; + pVoipTunnel->ClientList[5].uClientId = 0xeaa0beef; + pVoipTunnel->ClientList[6].uClientId = 0x0000beef; + pVoipTunnel->ClientList[7].uClientId = 0x1000beef; + _VoipTunnelLookupBuild(pVoipTunnel); + _VoipTunnelLookupClient(pVoipTunnel, 0xdeadbeef); + _VoipTunnelLookupClient(pVoipTunnel, 0xcacabeef); + _VoipTunnelLookupClient(pVoipTunnel, 0xf000beef); + _VoipTunnelLookupClient(pVoipTunnel, 0xf000b00f); + _VoipTunnelLookupClient(pVoipTunnel, 0xf000baaf); + _VoipTunnelLookupClient(pVoipTunnel, 0xeaa0beef); + _VoipTunnelLookupClient(pVoipTunnel, 0x0000beef); + _VoipTunnelLookupClient(pVoipTunnel, 0x1000beef); + + // reset + pVoipTunnel->iNumClients = 0; + pVoipTunnel->pLookupTable = NULL; + ds_memclr(pVoipTunnel->ClientList, sizeof(pVoipTunnel->ClientList)); +} +#endif + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelReleaseVoipBroadcastingSlotForTargetClient + + \Description + Releases the broadcasting slot that the client possesses as an originator + when sending voip to the target client (consumer). + + \Input *pVoipTunnel - module state + \Input *pSourceClient - source client + \Input iTargetClientIndex - client index to remove + + \Output + int32_t - updated sendmask for the source client. + + \Notes + voice squelching: There are only 4 broadcasting slots per consumer client. + Only 4 originator clients at a time can send voip to a specific + consumer client. + + \Version 02/01/2019 (amakoukji) +*/ +/********************************************************************************F*/ +static uint32_t _VoipTunnelReleaseVoipBroadcastingSlotForTargetClient(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pSourceClient, int32_t iTargetClientIndex) +{ + // if this is a client we're talking to + if ((pSourceClient->uSendMask) & (1 << iTargetClientIndex)) + { + VoipTunnelClientT *pTargetClient = VoipTunnelClientListMatchId(pVoipTunnel, pSourceClient->aClientIds[iTargetClientIndex]); + + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is not talking to 0x%08x anymore.\n", pSourceClient->uClientId, pTargetClient ? pTargetClient->uClientId : 0xFFFFFFFF)); + + pSourceClient->uSendMask &= ~(1 << iTargetClientIndex); + if (pTargetClient) + { + pTargetClient->iNumTalker--; + if (pTargetClient->uFlags & VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED) + { + pTargetClient->uFlags &= ~VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED; + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_AVLBVOICE, pTargetClient, pSourceClient->iGameIdx); + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is accepting new talkers again.\n", pTargetClient->uClientId)); + } + } + } + + return(pSourceClient->uSendMask); +} + + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelAcquireVoipBroadcastingSlot + + \Description + Allocates sendmask slots to the target clients if some are available. + + \Input *pVoipTunnel - module state + \Input *pClient - client ref + \Input uTargetSendMask - the clients we want to send to + + \Output + int32_t - acquired sendmask for the required client. + + \Notes + voice squelching: There are only 4 broadcasting slots per consumer client. + Only 4 originator clients at a time can send voip to a specific + consumer client. + + \Version 01/15/2010 (cvienneau) +*/ +/********************************************************************************F*/ +static uint32_t _VoipTunnelAcquireVoipBroadcastingSlot(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, uint32_t uTargetSendMask) +{ + int32_t iIndex; + uint32_t uAcquiredSendMask = pClient->uSendMask; + VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pClient->iGameIdx]; + uint32_t uToReleaseMask = 0; + uint32_t uToAddMask = 0; + + if (uAcquiredSendMask == uTargetSendMask) + { + return(uAcquiredSendMask); + } + + // if there's one or more targets we no longer need, release + uToReleaseMask = (uAcquiredSendMask & (~uTargetSendMask)); + if (uToReleaseMask != 0) + { + for (iIndex = 0; iIndex < VOIPTUNNEL_MAXGROUPSIZE; iIndex++) + { + // if this is a client we'd like to release but haven't yet + if (uToReleaseMask & (1 << iIndex)) + { + uAcquiredSendMask = _VoipTunnelReleaseVoipBroadcastingSlotForTargetClient(pVoipTunnel, pClient, iIndex); + } + } + } + + // if there's one or more target we have not yet acquired, try to acquire + uToAddMask = ((~uAcquiredSendMask) & uTargetSendMask); + if (uToAddMask != 0) + { + for (iIndex = 0; iIndex < VOIPTUNNEL_MAXGROUPSIZE; iIndex++) + { + // if this is a client we'd like to talk to, but don't already + if (uToAddMask & (1 << iIndex)) + { + VoipTunnelClientT *pTargetClient = VoipTunnelClientListMatchId(pVoipTunnel, pClient->aClientIds[iIndex]); + + if (pTargetClient) + { + if (pTargetClient->iNumTalker < pVoipTunnel->iMaxVoiceBroadcasters) + { + pTargetClient->iNumTalker++; + + uAcquiredSendMask |= (1 << iIndex); + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is now talking to 0x%08x\n", pClient->uClientId, pTargetClient->uClientId)); + } + else + { + // we've just reached the maximum number of clients allowed to send, send an event and set the flag + if (!(pTargetClient->uFlags & VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED)) + { + pTargetClient->uFlags |= VOIPTUNNEL_CLIENTFLAG_MAX_VOICES_REACHED; + pVoipTunnel->uVoiceMaxTalkersMetric++; + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_MAXDVOICE, pTargetClient, pClient->iGameIdx); + + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: 0x%08x is not taking new talkers (will not get voice from 0x%08x)\n", pTargetClient->uClientId, pClient->uClientId)); + } + + pVoipTunnel->uVoiceDataDropMetric++; + pGame->uVoiceDataDropMetric++; + } + } + } + } + } + + return(uAcquiredSendMask); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelReleaseAllVoipBroadcastingSlots + + \Description + Releases all broadcasting slots that the client possesses as an originator. + + \Input *pVoipTunnel - module state + \Input *pClient - client ref + + \Notes + voice squelching: There are only 4 broadcasting slot per consumer clients. + Only 4 originator clients at a time can send voip to a specific + consumer client. + + \Version 01/15/2010 (cvienneau) +*/ +/********************************************************************************F*/ +static void _VoipTunnelReleaseAllVoipBroadcastingSlots(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient) +{ + int32_t iIndex; + + for(iIndex = 0; iIndex < VOIPTUNNEL_MAXGROUPSIZE; iIndex++) + { + // if this is a client we're talking to + if ((pClient->uSendMask) & (1 << iIndex)) + { + _VoipTunnelReleaseVoipBroadcastingSlotForTargetClient(pVoipTunnel, pClient, iIndex); + } + } +} + \ +/*F********************************************************************************/ +/*! + \Function _VoipTunnelRouteVoipPacket + + \Description + Routes incoming VoIP packet to appropriate destination(s). + + \Input *pVoipTunnel - voip tunnel ref + \Input *pClient - source client + \Input *pPacket - voip packet + \Input iPacketSize - size of packet + \Input *pRecvAddr - source address + \Input uCurTick - current tick + \Input bVdpHeader - TRUE if there was a VDP header, else FALSE + + \Version 03/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTunnelRouteVoipPacket(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, const VoipTunnelPacketT *pPacket, int32_t iPacketSize, struct sockaddr *pRecvAddr, uint32_t uCurTick, uint32_t bVdpHeader) +{ + uint32_t uSendFlag = (bVdpHeader) ? VOIPTUNNEL_GAMEFLAG_VDP : 0; + + // see if we have a packet type that is unicast + if (!memcmp(pPacket->Head.aType, "CO", 2) || !memcmp(pPacket->Head.aType, "PNG", 3) || !memcmp(pPacket->Head.aType, "DSC", 3)) + { + uint32_t uRemoteClientId; + + // extract voip packet version from connection packet if we haven't done so already + if ((pPacket->Head.aType[0] == 'C') && (pClient->uPacketVersion == 0)) + { + pClient->uPacketVersion = pPacket->Head.aType[2]; + NetPrintf(("voiptunnel: detected voip packet format '%c' from clientId=0x%08x\n", (char)pClient->uPacketVersion, pClient->uClientId)); + } + + // extract the remote client identifier + uRemoteClientId = pPacket->aRemoteClientId[0] << 24; + uRemoteClientId |= pPacket->aRemoteClientId[1] << 16; + uRemoteClientId |= pPacket->aRemoteClientId[2] << 8; + uRemoteClientId |= pPacket->aRemoteClientId[3]; + + NetPrintfVerbose((pVoipTunnel->uDebugLevel, pPacket->Head.aType[0] == 'P' ? 2 : 1, "voiptunnel: forwarding %d byte unicast packet of type %c%c%c from clientId=0x%08x to clientId=0x%08x\n", + iPacketSize, pPacket->Head.aType[0], pPacket->Head.aType[1], pPacket->Head.aType[2], + pClient->uClientId, uRemoteClientId)); + + // forward to other clients in our group + _VoipTunnelClientListSend(pVoipTunnel, pClient, uRemoteClientId, (const char *)pPacket, iPacketSize, pRecvAddr, uSendFlag|VOIPTUNNEL_GAMEFLAG_SEND_SINGLE); + } + else if (!memcmp(pPacket->Head.aType, "MIC", 3)) + { + uint32_t uSendMask = (uint32_t)-1; + + // update recv voice timestamp and mark that we are receiving voip mic data from this client + pClient->uLastRecvVoice = uCurTick; + if ((pClient->uFlags & VOIPTUNNEL_CLIENTFLAG_RECVVOICE) == 0) + { + pClient->uFlags |= VOIPTUNNEL_CLIENTFLAG_RECVVOICE; + pVoipTunnel->iNumTalkingClients++; + pVoipTunnel->pGameList[pClient->iGameIdx].iNumTalkingClients++; + } + + + // extract send mask if packet version supports it + if (pClient->uPacketVersion >= (unsigned)'c') + { + uSendMask = pPacket->aRemoteClientId[0] << 24; + uSendMask |= pPacket->aRemoteClientId[1] << 16; + uSendMask |= pPacket->aRemoteClientId[2] << 8; + uSendMask |= pPacket->aRemoteClientId[3]; + } + + // update the client we have permission to send to + pClient->uSendMask = _VoipTunnelAcquireVoipBroadcastingSlot(pVoipTunnel, pClient, uSendMask); + + if (pClient->uSendMask != 0) + { + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: broadcasting voip packet %d from clientId=0x%08x\n", pPacket->aRemoteClientId[0], pClient->uClientId)); + + // broadcast to all other clients in our group + _VoipTunnelClientListSend(pVoipTunnel, pClient, 0, (const char *)pPacket, iPacketSize, pRecvAddr, uSendFlag|VOIPTUNNEL_GAMEFLAG_BROADCAST_ALLOTHERS); + } + else + { + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: failed to acquire voip broadcasting slot for voip packet %d from clientId=0x%08x\n", pPacket->aRemoteClientId[0], pClient->uClientId)); + } + } + else + { + NetPrintf(("voiptunnel: unknown VoIP packet type; ignoring\n")); + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelVoipRecvCallback + + \Description + Callback handler for data received on the virtual Voip socket. + + \Input *pSocket - pointer to socket + \Input iFlags - unused + \Input *_pRef - voiptunnel ref + + \Output + int32_t - zero + + \Version 03/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTunnelVoipRecvCallback(SocketT *pSocket, int32_t iFlags, void *_pRef) +{ + VoipTunnelRefT *pVoipTunnel = (VoipTunnelRefT *)_pRef; + int32_t iAddrLen = sizeof(struct sockaddr), iRecvLen; + char aPacketData[SOCKET_MAXUDPRECV]; + const VoipTunnelPacketT *pPacket; + VoipTunnelClientT *pClient; + struct sockaddr RecvAddr; + uint32_t uClientId, bVdpHeader=0; + uint32_t uCurTick = NetTick(); + + // got any input? + if ((iRecvLen = SocketRecvfrom(pSocket, aPacketData, sizeof(aPacketData), 0, &RecvAddr, &iAddrLen)) <= 0) + { + return(0); + } + + // validate the packet + if ((pPacket = _VoipTunnelValidatePacket(pVoipTunnel, aPacketData, iRecvLen, &bVdpHeader)) == NULL) + { + return(0); + } + + // extract client identifier from voip packet header + uClientId = _VoipTunnelGetClientId(pPacket, iRecvLen); + + // if we don't have a client with this id yet, bail + if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, uClientId)) == NULL) + { + NetPrintf(("voiptunnel: ignoring '%c%c%c' voip packet from %a:%d with unregistered id=0x%08x\n", + pPacket->Head.aType[0], pPacket->Head.aType[1], pPacket->Head.aType[2], + SockaddrInGetAddr(&RecvAddr), SockaddrInGetPort(&RecvAddr), uClientId)); + return(0); + } + + // if the address isn't set yet, set it now + if (pClient->uRemoteAddr != (unsigned)SockaddrInGetAddr(&RecvAddr)) + { + // get source client address + pClient->uRemoteAddr = SockaddrInGetAddr(&RecvAddr); + NetPrintf(("voiptunnel: matching clientId=0x%08x to addr %a\n", pClient->uClientId, pClient->uRemoteAddr)); + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_MATCHADDR, pClient, 0); + } + // if the voip port isn't set yet, set it now + if (pClient->uRemoteVoipPort != SockaddrInGetPort(&RecvAddr)) + { + // get source client port + pClient->uRemoteVoipPort = SockaddrInGetPort(&RecvAddr); + NetPrintf(("voiptunnel: matching clientId=0x%08x to port %d\n", pClient->uClientId, pClient->uRemoteVoipPort)); + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_MATCHPORT, pClient, 0); + } + + // call user callback + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_RECVVOICE, pClient, iRecvLen); + + // forward voip data to others in our group according to packet type + _VoipTunnelRouteVoipPacket(pVoipTunnel, pClient, pPacket, iRecvLen, &RecvAddr, uCurTick, bVdpHeader); + + // update source client timestamp + pClient->uLastUpdate = uCurTick; + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelPrintGameClientList + + \Description + Prints the clients in the game + + \Input *pVoipTunnel - voiptunnel ref + \Input iGameIdx - index of game print clients from + + \Version 01/28/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _VoipTunnelPrintGameClientList(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx) +{ + int32_t iClient; + const VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[iGameIdx]; + + NetPrintf(("voiptunnel: game %d clients\n", iGameIdx)); + for (iClient = 0; iClient < VOIPTUNNEL_MAXGROUPSIZE; iClient++) + { + if (pGame->aClientList[iClient] != 0) + { + NetPrintf(("voiptunnel: [%d] 0x%08x %s\n", iClient, pGame->aClientList[iClient], pGame->bClientActive[iClient] ? "ACTIVE" : "INACTIVE")); + } + } +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelClientGameDel + + \Description + Remove client from the game list + + \Input *pVoipTunnel - voiptunnel ref + \Input *pClient - client to remove + \Input iGameIdx - index of game to remove client from + + \Output + int32_t - negative=error, else success + + \Version 06/10/2007 (jbrookes) +*/ +/********************************************************************************F*/ +static int32_t _VoipTunnelClientGameDel(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, int32_t iGameIdx) +{ + // remove entry from game list + VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[iGameIdx]; + int32_t iGameClient; + + // scan array to find client id + for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++) + { + // found client? + if (pGame->aClientList[iGameClient] == pClient->uClientId) + { + NetPrintf(("voiptunnel: removing clientId=0x%08x from game %d\n", pClient->uClientId, iGameIdx)); + + // if the user was talking as they left, free up that broadcasting slot + _VoipTunnelReleaseAllVoipBroadcastingSlots(pVoipTunnel, pClient); + + // expire recvvoice flag? + if (pClient->uFlags & VOIPTUNNEL_CLIENTFLAG_RECVVOICE) + { + pClient->uFlags &= ~VOIPTUNNEL_CLIENTFLAG_RECVVOICE; + pVoipTunnel->iNumTalkingClients--; + pVoipTunnel->pGameList[iGameIdx].iNumTalkingClients--; + + // catch a potential error if this number goes below 0. + // this occured in GOS-31044 so we place this safeguard here + // if this occurs the metric can no longer be relied upon to be + // accurate but should still be close to the appropriate value, + // rather than negative, which will be problematic for the dashboard. + if (pVoipTunnel->iNumTalkingClients < 0) + { + NetPrintf(("voiptunnel: [%p] error tracking number of talking clients, game %d\n", pVoipTunnel, iGameIdx)); + pVoipTunnel->iNumTalkingClients = 0; + } + } + + // clear entry from game list + pGame->aClientList[iGameClient] = 0; + pGame->bClientActive[iGameClient] = FALSE; + + // decrement client count + pGame->iNumClients -= 1; + // debug echo of game client list after delete + if (pVoipTunnel->uDebugLevel > 0) + { + _VoipTunnelPrintGameClientList(pVoipTunnel, iGameIdx); + } + // update send masks for other clients in this game + for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++) + { + // make sure the client is active + if (pGame->bClientActive[iGameClient] == FALSE) + { + continue; + } + + // ref the client + if ((pClient = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iGameClient])) != NULL) + { + // refresh send mask + VoipTunnelClientRefreshSendMask(pVoipTunnel, pClient); + } + } + return(0); + } + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelSuspendGame + + \Description + Suspends the current active game + + \Input *pVoipTunnel - voiptunnel ref + \Input *pClient - the client we are updating + + \Output + int32_t - zero=success, else=failure + + \Version 01/19/2016 (eesponda) +*/ +/********************************************************************************F*/ +static int32_t _VoipTunnelSuspendGame(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient) +{ + int32_t iGameClient; + VoipTunnelSuspendInfoT *pSuspendInfo = &pClient->aSuspendedData[pClient->iNumSuspended]; + VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pClient->iGameIdx]; + + if (pSuspendInfo->iGameIdx >= 0) + { + NetPrintf(("voiptunnel: invalid data in suspend information for client id=0x%08x game already suspended at this slot\n", pClient->uClientId)); + return(-1); + } + + // refcount the client + NetPrintf(("voiptunnel: refcounting client id=0x%08x suspending game %d\n", pClient->uClientId, pClient->iGameIdx)); + + // update suspended information + pSuspendInfo->iGameIdx = pClient->iGameIdx; + ds_strnzcpy(pSuspendInfo->strTunnelKey, pClient->strTunnelKey, sizeof(pSuspendInfo->strTunnelKey)); + ds_memcpy_s(pSuspendInfo->aClientIds, sizeof(pSuspendInfo->aClientIds), pClient->aClientIds, sizeof(pClient->aClientIds)); + pSuspendInfo->iNumClients = pClient->iNumClients; + pClient->iNumSuspended += 1; + + // deactivate client in game for gamesend mask calculation + for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1) + { + if (pGame->aClientList[iGameClient] == pClient->uClientId) + { + pGame->bClientActive[iGameClient] = FALSE; + break; + } + } + // update send masks for other clients in this game (old) + for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1) + { + VoipTunnelClientT *pClientInfo; + + // make sure the client is active + if (pGame->bClientActive[iGameClient] == FALSE) + { + continue; + } + + // ref the client + if ((pClientInfo = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iGameClient])) != NULL) + { + // refresh send mask + VoipTunnelClientRefreshSendMask(pVoipTunnel, pClientInfo); + } + } + return(0); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelUnsuspendGame + + \Description + Takes the last suspended game for the client and makes it active + + \Input *pVoipTunnel - voiptunnel ref + \Input *pClient - the client we are updating + + \Version 01/19/2016 (eesponda) +*/ +/********************************************************************************F*/ +static void _VoipTunnelUnsuspendGame(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient) +{ + int32_t iGameClient; + VoipTunnelSuspendInfoT *pSuspendInfo = &pClient->aSuspendedData[pClient->iNumSuspended - 1]; + VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pSuspendInfo->iGameIdx]; + + NetPrintf(("voiptunnel: decrementing refcount client id=0x%08x unsuspending game %d\n", pClient->uClientId, pSuspendInfo->iGameIdx)); + + // make suspend information the active information + pClient->iGameIdx = pSuspendInfo->iGameIdx; + ds_strnzcpy(pClient->strTunnelKey, pSuspendInfo->strTunnelKey, sizeof(pClient->strTunnelKey)); + ds_memcpy(pClient->aClientIds, pSuspendInfo->aClientIds, sizeof(pClient->aClientIds)); + pClient->iNumClients = pSuspendInfo->iNumClients; + + // active our client in new active game + for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1) + { + // found client? + if (pGame->aClientList[iGameClient] == pClient->uClientId) + { + pGame->bClientActive[iGameClient] = TRUE; + break; + } + } + + // update send masks for active clients + for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient += 1) + { + VoipTunnelClientT *pClientInfo; + + if (pGame->bClientActive[iGameClient] == FALSE) + { + continue; + } + + if ((pClientInfo = VoipTunnelClientListMatchId(pVoipTunnel, pGame->aClientList[iGameClient])) != NULL) + { + VoipTunnelClientRefreshSendMask(pVoipTunnel, pClientInfo); + } + } + + // clear out the data + ds_memset(pSuspendInfo, -1, sizeof(*pSuspendInfo)); + + // update number of suspended games + pClient->iNumSuspended -= 1; +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelSocketOpen + + \Description + Open a DirtySock socket. + + \Input *pVoipTunnel - module state + \Input uPort - port to bind + \Input *pCallback - socket callback function + + \Output + SocketT * - socket ref, or NULL + + \Version 03/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static SocketT *_VoipTunnelSocketOpen(VoipTunnelRefT *pVoipTunnel, uint16_t uPort, int32_t (*pCallback)(SocketT *pSocket, int32_t iFlags, void *pRef)) +{ + struct sockaddr BindAddr; + SocketT *pSocket; + int32_t iResult; + + // open the socket + if ((pSocket = SocketOpen(AF_INET, SOCK_DGRAM, IPPROTO_IP)) == NULL) + { + NetPrintf(("voiptunnel: unable to open socket\n")); + return(NULL); + } + + // bind socket to specified port + SockaddrInit(&BindAddr, AF_INET); + SockaddrInSetPort(&BindAddr, uPort); + if ((iResult = SocketBind(pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE) + { + NetPrintf(("voiptunnel: error %d binding to port\n", iResult)); + SocketClose(pSocket); + return(NULL); + } + + // set up for socket callback events + SocketCallback(pSocket, CALLB_RECV, 100, pVoipTunnel, pCallback); + + // return ref to caller + return(pSocket); +} + +/*F********************************************************************************/ +/*! + \Function _VoipTunnelClientListUpdate + + \Description + Perform update processing on client list + + \Input *pVoipTunnel - module state + + \Version 09/18/2006 (jbrookes) +*/ +/********************************************************************************F*/ +static void _VoipTunnelClientListUpdate(VoipTunnelRefT *pVoipTunnel) +{ + VoipTunnelClientT *pClient; + uint32_t uCurTick; + int32_t iClient; + + // loop through clientlist + for (iClient = 0, uCurTick = NetTick(); iClient < pVoipTunnel->iNumClients; iClient++) + { + // ref client + pClient = &pVoipTunnel->ClientList[iClient]; + + // expire recvvoice flag? + if (pClient->uFlags & VOIPTUNNEL_CLIENTFLAG_RECVVOICE) + { + if (NetTickDiff(uCurTick, pClient->uLastRecvVoice) > (int32_t)pVoipTunnel->uVoiceRecvTimeout) + { + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: stopped receiving voice from clientId=0x%08x\n", pClient->uClientId)); + pClient->uFlags &= ~VOIPTUNNEL_CLIENTFLAG_RECVVOICE; + pVoipTunnel->iNumTalkingClients--; + pVoipTunnel->pGameList[pClient->iGameIdx].iNumTalkingClients--; + _VoipTunnelReleaseAllVoipBroadcastingSlots(pVoipTunnel, pClient); + + // catch a potential error if this number goes below 0. + // this occured in GOS-31044 so we place this safeguard here + // if this occurs the metric can no longer be relied upon to be + // accurate but should still be close to the appropriate value, + // rather than negative, which will be problematic for the dashboard. + if (pVoipTunnel->iNumTalkingClients < 0) + { + NetPrintf(("voiptunnel: [%p] error tracking number of talking clients\n", pVoipTunnel)); + pVoipTunnel->iNumTalkingClients = 0; + } + } + } + + /* check for connection death (have previously received data, but have not received + data for at least the timeout period. timeout is client timeout plus five seconds + so that clients that are being removed from the game don't produce this warning */ + if ((pClient->uRemoteVoipPort != 0) && ((signed)(uCurTick - pClient->uLastUpdate) > 15*1000)) + { + NetPrintf(("voiptunnel: clientId=0x%08x voice connection has gone dead\n", pClient->uClientId)); + /* clear voip point so 1) we don't send to them 2) we don't warn again and 3) in + the unlikely event data starts flowing again somehow, we get notified by a new + match event */ + pClient->uRemoteVoipPort = 0; + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_DEADVOICE, pClient, 0); + } + } +} + + +/*** Public functions *************************************************************/ + + +/*F********************************************************************************/ +/*! + \Function VoipTunnelCreate + + \Description + Create the VoipTunnel module. + + \Input uVoipPort - local virtual voip port + \Input iMaxClients - max number of clients allowed + \Input iMaxGames - max number of games allowed + + \Output + VoipTunnelRefT * - new module state pointer, or null + + \Version 04/06/2006 (jbrookes) +*/ +/********************************************************************************F*/ +VoipTunnelRefT *VoipTunnelCreate(uint32_t uVoipPort, int32_t iMaxClients, int32_t iMaxGames) +{ + VoipTunnelRefT *pVoipTunnel; + int32_t iMemSize = sizeof(*pVoipTunnel) + (sizeof(pVoipTunnel->ClientList[0]) * (iMaxClients - 1)); + int32_t iGameListSize; + int32_t iMemGroup; + void *pMemGroupUserData; + + // Query current mem group data + DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); + + // allocate and init module state + if ((pVoipTunnel = DirtyMemAlloc(iMemSize, VOIPTUNNEL_MEMID, iMemGroup, pMemGroupUserData)) == NULL) + { + NetPrintf(("voiptunnel: could not allocate module state\n")); + return(NULL); + } + ds_memclr(pVoipTunnel, iMemSize); + pVoipTunnel->iMemGroup = iMemGroup; + pVoipTunnel->pMemGroupUserData = pMemGroupUserData; + pVoipTunnel->uVoipPort = uVoipPort; + pVoipTunnel->iMaxClients = iMaxClients; + pVoipTunnel->iMaxGames = iMaxGames; + pVoipTunnel->iMaxVoiceBroadcasters = VOIPTUNNEL_MAX_BROADCASTING_VOICES_DEFAULT; + pVoipTunnel->uVoiceRecvTimeout = VOIPTUNNEL_RECVVOICE_TIMEOUT_DEFAULT; + + // test lookup table + #if VOIPTUNNEL_LOOKUP_TEST + _VoipTunnelLookupTest(pVoipTunnel); + #endif + + // allocate game list + iGameListSize = sizeof(*pVoipTunnel->pGameList) * iMaxGames; + if ((pVoipTunnel->pGameList = DirtyMemAlloc(iGameListSize, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData)) == NULL) + { + NetPrintf(("voiptunnel: unable to allocate game list\n")); + // destroy anything that was started + VoipTunnelDestroy(pVoipTunnel); + return(NULL); + } + ds_memset(pVoipTunnel->pGameList, -1, iGameListSize); + + // allocate fast lookup table if warranted + if (iMaxClients >= 32) + { + if ((pVoipTunnel->pLookupTable = DirtyMemAlloc(sizeof(*pVoipTunnel->pLookupTable) * iMaxClients, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData)) == NULL) + { + NetPrintf(("voiptunnel: unable to allocate fast lookup table\n")); + } + } + + // create voip socket + if ((pVoipTunnel->pVoipSocket = _VoipTunnelSocketOpen(pVoipTunnel, uVoipPort, _VoipTunnelVoipRecvCallback)) == NULL) + { + // destroy anything that was started + VoipTunnelDestroy(pVoipTunnel); + return(NULL); + } + + // return ref to caller + return(pVoipTunnel); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelCallback + + \Description + Set optional voiptunnel event callback + + \Input *pVoipTunnel - voiptunnel ref + \Input *pCallback - callback pointer + \Input *pUserData - callback user data + + \Version 03/24/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipTunnelCallback(VoipTunnelRefT *pVoipTunnel, VoipTunnelCallbackT *pCallback, void *pUserData) +{ + pVoipTunnel->pCallback = pCallback; + pVoipTunnel->pUserData = pUserData; +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelDestroy + + \Description + Shutdown this VoipTunnel. + + \Input *pVoipTunnel - voiptunnel ref + + \Version 03/24/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipTunnelDestroy(VoipTunnelRefT *pVoipTunnel) +{ + // destroy lookup table + if (pVoipTunnel->pLookupTable != NULL) + { + DirtyMemFree(pVoipTunnel->pLookupTable, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData); + } + // destroy game list + if (pVoipTunnel->pGameList != NULL) + { + DirtyMemFree(pVoipTunnel->pGameList, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData); + } + // destroy virtual voip socket + if (pVoipTunnel->pVoipSocket != NULL) + { + SocketClose(pVoipTunnel->pVoipSocket); + } + // dispose of module memory + DirtyMemFree(pVoipTunnel, VOIPTUNNEL_MEMID, pVoipTunnel->iMemGroup, pVoipTunnel->pMemGroupUserData); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListAdd + + \Description + Add a new client to the client list + + \Input *pVoipTunnel - voiptunnel ref + \Input *pClientInfo - new client info + \Input **ppNewClient - [out] new client pointer (optional; can be NULL) + + \Output + int32_t - negative=error, else success + + \Version 03/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTunnelClientListAdd(VoipTunnelRefT *pVoipTunnel, const VoipTunnelClientT *pClientInfo, VoipTunnelClientT **ppNewClient) +{ + return(VoipTunnelClientListAdd2(pVoipTunnel, pClientInfo, ppNewClient, 0)); +} + + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListAdd2 + + \Description + Add a new client to the client list + + \Input *pVoipTunnel - voiptunnel ref, at the first empty spot past a given index + \Input *pClientInfo - new client info + \Input **ppNewClient - [out] new client pointer (optional; can be NULL) + \Input iStartIndex - index to add at + + \Output + int32_t - negative=error, else success + + \Version 10/18/2011 (jrainy) +*/ +/********************************************************************************F*/ +int32_t VoipTunnelClientListAdd2(VoipTunnelRefT *pVoipTunnel, const VoipTunnelClientT *pClientInfo, VoipTunnelClientT **ppNewClient, int32_t iStartIndex) +{ + VoipTunnelClientT *pNewClient; + VoipTunnelGameT *pGame; + int32_t iGameClient; + + // make sure there's room in the list + if (pVoipTunnel->iNumClients >= pVoipTunnel->iMaxClients) + { + NetPrintf(("voiptunnel: max client limit exceeded\n")); + return(-1); + } + + // make sure game index is valid + if (pClientInfo->iGameIdx >= pVoipTunnel->iMaxGames) + { + NetPrintf(("voiptunnel: invalid game index %d\n", pClientInfo->iGameIdx)); + return(-2); + } + + // make sure game has been initialized and there's room in the game + pGame = &pVoipTunnel->pGameList[pClientInfo->iGameIdx]; + if (pGame->iNumClients < 0) + { + NetPrintf(("voiptunnel: could not add clientId=0x%08x to game %d when game has not been initialized\n", pClientInfo->uClientId, pClientInfo->iGameIdx)); + return(-3); + } + if (pGame->iNumClients >= VOIPTUNNEL_MAXGROUPSIZE) + { + NetPrintf(("voiptunnel: max game size exceeded trying to add clientId=0x%08x to game %d\n", pClientInfo->uClientId, pClientInfo->iGameIdx)); + return(-4); + } + + // make sure client does not already appear in the game list + for (iGameClient = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++) + { + if (pClientInfo->uClientId == pGame->aClientList[iGameClient]) + { + NetPrintf(("voiptunnel: could not add clientId=0x%08x to game %d when client is already in the game at index %d\n", pClientInfo->uClientId, pClientInfo->iGameIdx, iGameClient)); + return(-5); + } + } + + if (iStartIndex < 0) + { + NetPrintf(("voiptunnel: could not accept negative start index %d\n", iStartIndex)); + return(-7); + } + + // check for clients that are already tracked by the voiptunnel + if ((pNewClient = VoipTunnelClientListMatchId(pVoipTunnel, pClientInfo->uClientId)) == NULL) + { + // allocate new entry + NetPrintf(("voiptunnel: adding new client id=0x%08x\n", pClientInfo->uClientId)); + pNewClient = &pVoipTunnel->ClientList[pVoipTunnel->iNumClients]; + + // initialize new entry + ds_memcpy_s(pNewClient, sizeof(*pNewClient), pClientInfo, sizeof(*pClientInfo)); + + // default send mask to all disabled + // at every packet received, this will be recomputed + pNewClient->uSendMask = 0; + pNewClient->iNumTalker = 0; + + // increment client counts + pVoipTunnel->iNumClients += 1; + + // initialize suspended data + ds_memset(pNewClient->aSuspendedData, -1, sizeof(pNewClient->aSuspendedData)); + } + else if (pNewClient->iNumSuspended >= VOIPTUNNEL_MAXSUSPENDED) + { + NetPrintf(("voiptunnel: no more space to suspend game %d for clientId=0x%08x\n", pNewClient->uClientId, pClientInfo->iGameIdx)); + return(-8); + } + else + { + if (_VoipTunnelSuspendGame(pVoipTunnel, pNewClient) == 0) + { + // update current information + pNewClient->iGameIdx = pClientInfo->iGameIdx; + ds_strnzcpy(pNewClient->strTunnelKey, pClientInfo->strTunnelKey, sizeof(pNewClient->strTunnelKey)); + ds_memclr(pNewClient->aClientIds, sizeof(pNewClient->aClientIds)); // reset the ids so it can be updated later + pNewClient->iNumClients = 0; + } + else + { + // we couldn't suspend, treat as failure + return(-9); + } + } + + // add client to game list in first available slot + for (iGameClient = iStartIndex; (iGameClient < VOIPTUNNEL_MAXGROUPSIZE) && (pGame->aClientList[iGameClient] != 0); iGameClient++) + ; + if (iGameClient < VOIPTUNNEL_MAXGROUPSIZE) + { + pGame->aClientList[iGameClient] = pNewClient->uClientId; + pGame->bClientActive[iGameClient] = TRUE; + } + else + { + NetPrintf(("voiptunnel: could not add clientId=0x%08x to game list for game %d\n", pNewClient->uClientId, pClientInfo->iGameIdx)); + return(-6); + } + + // increment game counts + pGame->iNumClients += 1; + + // debug echo of game client list after add + if (pVoipTunnel->uDebugLevel > 0) + { + _VoipTunnelPrintGameClientList(pVoipTunnel, pNewClient->iGameIdx); + } + + // rebuild lookup table + _VoipTunnelLookupBuild(pVoipTunnel); + + // notify application of add + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_ADDCLIENT, pNewClient, 0); + + // write back new client pointer + if (ppNewClient != NULL) + { + *ppNewClient = pNewClient; + } + // return success + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListDel + + \Description + Remove client from the client list given the context of a game + + \Input *pVoipTunnel - voiptunnel ref + \Input *pClient - client to remove + \Input iGameIdx - index of the game we are removing client from + + \Version 04/10/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipTunnelClientListDel(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient, int32_t iGameIdx) +{ + int32_t iGame = -1; + // get client index from client pointer + int32_t iClient = (int32_t)(pClient - pVoipTunnel->ClientList); + + // make sure index is valid + if ((iClient < 0) || (iClient >= pVoipTunnel->iMaxClients)) + { + NetPrintf(("voiptunnel: invalid client index %d specified for deletion\n", iClient)); + return; + } + + // if invalid then take the active game index from the client + if (iGameIdx < 0) + { + iGameIdx = pClient->iGameIdx; + } + + // if we are not removing from the active game then we have to + // make sure to find the game in the suspended data + if (pClient->iGameIdx != iGameIdx) + { + for (iGame = 0; iGame < pClient->iNumSuspended; iGame += 1) + { + if (pClient->aSuspendedData[iGame].iGameIdx == iGameIdx) + { + break; + } + } + if (iGame == pClient->iNumSuspended) + { + NetPrintfVerbose((pVoipTunnel->uDebugLevel, 1, "voiptunnel: could not find game %d for client 0x%08x suspended data\n", iGameIdx, pClient->uClientId)); + return; + } + } + + // notify application of upcoming deletion to allow for deletion of any associate resources + // pass in the slot (non-zero based) to signal if we are removing active/inactive game + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_DELCLIENT, pClient, iGame+1); + + // remove entry from game + if (_VoipTunnelClientGameDel(pVoipTunnel, pClient, iGameIdx) < 0) + { + NetPrintf(("voiptunnel: could not find clientId=0x%08x in game %d\n", pClient->uClientId, pClient->iGameIdx)); + } + + // if deleting from the client's active game use the normal delete logic + if (pClient->iGameIdx == iGameIdx) + { + // unsuspend a game if any are available + if (pClient->iNumSuspended != 0) + { + _VoipTunnelUnsuspendGame(pVoipTunnel, pClient); + return; + } + + // remove entry from client list + if (iClient != (pVoipTunnel->iNumClients - 1)) + { + int32_t iNumMoveClients = (pVoipTunnel->iNumClients - 1) - iClient; + + // move the clients to remove the gap + memmove(pClient, pClient + 1, iNumMoveClients * sizeof(VoipTunnelClientT)); + } + // decrement count + pVoipTunnel->iNumClients -= 1; + + // rebuild lookup table + _VoipTunnelLookupBuild(pVoipTunnel); + } + // otherwise remove the suspended game from being tracked + else + { + // only remove the gap if not at the end + if (iGame != (pClient->iNumSuspended - 1)) + { + int32_t iNumMove = (pClient->iNumSuspended - 1) - iGame; + + // move the information to remove the gap + memmove(&pClient->aSuspendedData[iGame], &pClient->aSuspendedData[iGame+1], iNumMove * sizeof(pClient->aSuspendedData[0])); + } + else + { + /* since we are removing from the end (the slot is not being overwritten), we need to clear out the data so + further logic does not believe we are suspended at this slot */ + ds_memset(&pClient->aSuspendedData[pClient->iNumSuspended - 1], -1, sizeof(*pClient->aSuspendedData)); + } + // decrement count + pClient->iNumSuspended -= 1; + } +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelGameListAdd + + \Description + Add a new game. + + \Input *pVoipTunnel - voiptunnel ref + \Input iGameIdx - index of game whose clients are to be removed + + \Output + int32_t - negative=error, else success + + \Version 04/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTunnelGameListAdd(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx) +{ + return(VoipTunnelGameListAdd2(pVoipTunnel, iGameIdx, 0)); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelGameListAdd2 + + \Description + Add a new game. + + \Input *pVoipTunnel - voiptunnel ref + \Input iGameIdx - index of game whose clients are to be removed + \Input uGameId - unique game identifier + + \Output + int32_t - negative=error, else success + + \Version 12/01/2015 (eesponda) +*/ +/********************************************************************************F*/ +int32_t VoipTunnelGameListAdd2(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx, uint32_t uGameId) +{ + if (pVoipTunnel->iNumGames >= pVoipTunnel->iMaxGames) + { + NetPrintf(("voiptunnel: game list overflow -- could not add game\n")); + return(-1); + } + if (pVoipTunnel->pGameList[iGameIdx].iNumClients != -1) + { + NetPrintf(("voiptunnel: refusing game %d add request; game is already active\n", iGameIdx)); + return(-2); + } + pVoipTunnel->iNumGames++; + ds_memclr(&pVoipTunnel->pGameList[iGameIdx], sizeof(pVoipTunnel->pGameList[0])); + pVoipTunnel->pGameList[iGameIdx].uGameId = uGameId; + + // notify application of add + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_ADDGAME, NULL, iGameIdx); + + return(0); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelGameListDel + + \Description + Remove all clients from the client list who have the given GameServer index + + \Input *pVoipTunnel - voiptunnel ref + \Input iGameIdx - index of game whose clients are to be removed + + \Output + int32_t - number of clients deleted + + \Version 04/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTunnelGameListDel(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx) +{ + int32_t iClient, iClientsDeleted; + if( iGameIdx < pVoipTunnel->iMaxGames) + { + // iterate through client list, and delete all clients associated with iGameServer + for (iClient = 0, iClientsDeleted = 0; iClient < pVoipTunnel->iNumClients; ) + { + VoipTunnelClientT *pClient = &pVoipTunnel->ClientList[iClient]; + // not a client we're looking for? + if (pClient->iGameIdx == iGameIdx) + { + // delete client from list + VoipTunnelClientListDel(pVoipTunnel, pClient, iGameIdx); + + // increment deleted count + iClientsDeleted += 1; + } + else + { + // delete client from list (suspended) + VoipTunnelClientListDel(pVoipTunnel, pClient, iGameIdx); + + // move to next client as when in an inactive game a client is not removed + iClient++; + } + } + + // notify application of upcoming deletion to allow for deletion of any associated resources + _VoipTunnelEventCallback(pVoipTunnel, VOIPTUNNEL_EVENT_DELGAME, NULL, iGameIdx); + + // wipe game and mark as unallocated + ds_memclr(&pVoipTunnel->pGameList[iGameIdx], sizeof(pVoipTunnel->pGameList[0])); + pVoipTunnel->pGameList[iGameIdx].iNumClients = -1; + pVoipTunnel->iNumGames--; + } + else + { + iClientsDeleted = 0; + NetPrintf(("voiptunnel: skipping delete of game %d, out of bounds.\n", iGameIdx)); + } + + // return number of clients deleted + return(iClientsDeleted); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListMatchAddr + + \Description + Return client matching given address. + + \Input *pVoipTunnel - voiptunnel ref + \Input uRemoteAddr - address incoming packet originated from + + \Output + VoipTunnelClientT * - pointer to new client, or NULL + + \Version 03/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +VoipTunnelClientT *VoipTunnelClientListMatchAddr(VoipTunnelRefT *pVoipTunnel, uint32_t uRemoteAddr) +{ + VoipTunnelClientT *pClient; + int32_t iClientId; + + // iterate through client list and look for a match + for (iClientId = 0, pClient = &pVoipTunnel->ClientList[iClientId]; iClientId < pVoipTunnel->iNumClients; iClientId++, pClient++) + { + // is there a match? + if (pClient->uRemoteAddr == uRemoteAddr) + { + return(pClient); + } + } + + // no match + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListMatchId + + \Description + Return client matching given address. + + \Input *pVoipTunnel - voiptunnel ref + \Input uClientId - unique client identifier + + \Output + VoipTunnelClientT * - pointer to matching client, or NULL + + \Version 03/27/2006 (jbrookes) +*/ +/********************************************************************************F*/ +VoipTunnelClientT *VoipTunnelClientListMatchId(VoipTunnelRefT *pVoipTunnel, uint32_t uClientId) +{ + VoipTunnelClientT *pClient; + int32_t iClient; + + // ignore invalid clientId + if (uClientId == 0) + { + return(NULL); + } + + // use lookup? + if (pVoipTunnel->pLookupTable != NULL) + { + pClient = _VoipTunnelLookupClient(pVoipTunnel, uClientId); + } + else + { + // iterate through client list and look for a match + for (iClient = 0, pClient = NULL; iClient < pVoipTunnel->iNumClients; iClient++) + { + // is there a match? + if (pVoipTunnel->ClientList[iClient].uClientId == uClientId) + { + pClient = &pVoipTunnel->ClientList[iClient]; + break; + } + } + } + + // return client to caller, or NULL if no match + return(pClient); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListMatchIndex + + \Description + Return client at the given index. + + \Input *pVoipTunnel - voiptunnel ref + \Input uClientIndex - index of client in client list + + \Output + VoipTunnelClientT * - pointer to client, or NULL + + \Version 03/13/2007 (jbrookes) +*/ +/********************************************************************************F*/ +VoipTunnelClientT *VoipTunnelClientListMatchIndex(VoipTunnelRefT *pVoipTunnel, uint32_t uClientIndex) +{ + VoipTunnelClientT *pClient = NULL; + if (uClientIndex < (unsigned)pVoipTunnel->iNumClients) + { + pClient = &pVoipTunnel->ClientList[uClientIndex]; + } + return(pClient); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListMatchSockaddr + + \Description + Return client matching the address and port in the specified sockaddr + + \Input *pVoipTunnel - voiptunnel ref + \Input *pSockaddr - sockaddr containing addr and port to match + + \Output + VoipTunnelClientT * - pointer to matching client, or NULL + + \Version 03/13/2007 (jbrookes) +*/ +/********************************************************************************F*/ +VoipTunnelClientT *VoipTunnelClientListMatchSockaddr(VoipTunnelRefT *pVoipTunnel, struct sockaddr *pSockaddr) +{ + VoipTunnelClientT *pClient; + int32_t iClientId; + uint32_t uAddr = SockaddrInGetAddr(pSockaddr); + uint16_t uPort = SockaddrInGetPort(pSockaddr); + + // iterate through client list and look for a match + for (iClientId = 0, pClient = &pVoipTunnel->ClientList[iClientId]; iClientId < pVoipTunnel->iNumClients; iClientId++, pClient++) + { + // is there a match? + if ((pClient->uRemoteAddr == uAddr) && (pClient->uRemoteGamePort == uPort)) + { + return(pClient); + } + } + + // no match + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientListMatchFunc + + \Description + Return client matching criteria of matching function + + \Input *pVoipTunnel - voiptunnel ref + \Input *pMatchFunc - function to call to indicate if we have a match or not + \Input *pUserData - user data to pass to match function + + \Output + VoipTunnelClientT * - pointer to found client, or NULL + + \Version 04/10/2007 (jbrookes) +*/ +/********************************************************************************F*/ +VoipTunnelClientT *VoipTunnelClientListMatchFunc(VoipTunnelRefT *pVoipTunnel, VoipTunnelMatchFuncT *pMatchFunc, void *pUserData) +{ + VoipTunnelClientT *pClient; + int32_t iClientId; + + // iterate through client list and look for a match + for (iClientId = 0, pClient = &pVoipTunnel->ClientList[iClientId]; iClientId < pVoipTunnel->iNumClients; iClientId++, pClient++) + { + // is there a match? + if (pMatchFunc(pClient, pUserData) == 0) + { + return(pClient); + } + } + + // no match + return(NULL); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelGameListMatchIndex + + \Description + Return game at the given index. + + \Input *pVoipTunnel - voiptunnel ref + \Input iGameIdx - index of game in game list + + \Output + VoipTunnelGameT * - pointer to game, or NULL + + \Version 12/01/2015 (eesponda) +*/ +/********************************************************************************F*/ +VoipTunnelGameT *VoipTunnelGameListMatchIndex(VoipTunnelRefT *pVoipTunnel, int32_t iGameIdx) +{ + VoipTunnelGameT *pGame = NULL; + if (iGameIdx < pVoipTunnel->iMaxGames) + { + pGame = &pVoipTunnel->pGameList[iGameIdx]; + } + return(pGame); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelGameListMatchId + + \Description + Return game matching given game identifier. + + \Input *pVoipTunnel - voiptunnel ref + \Input uGameId - unique game identifier + \Input *pGameIdx - [out] index the game was at + + \Output + VoipTunnelGameT * - pointer to matching game, or NULL + + \Version 12/01/2015 (eesponda) +*/ +/********************************************************************************F*/ +VoipTunnelGameT *VoipTunnelGameListMatchId(VoipTunnelRefT *pVoipTunnel, uint32_t uGameId, int32_t *pGameIdx) +{ + VoipTunnelGameT *pGame; + int32_t iGameIdx; + + // ignore invalid game id + if (uGameId == 0) + { + return(NULL); + } + // initialize if valid + if (pGameIdx != NULL) + { + *pGameIdx = -1; + } + + // iterate through game list and look for a match + for (iGameIdx = 0, pGame = NULL; iGameIdx < pVoipTunnel->iMaxGames; iGameIdx += 1) + { + if (pVoipTunnel->pGameList[iGameIdx].uGameId == uGameId) + { + pGame = &pVoipTunnel->pGameList[iGameIdx]; + if (pGameIdx != NULL) + { + *pGameIdx = iGameIdx; + } + + break; + } + } + + return(pGame); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelClientRefreshSendMask + + \Description + Let api know client's send list has been updated + + \Input *pVoipTunnel - voiptunnel ref + \Input *pClient - client whose send list has been updated + + \Version 06/10/2007 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipTunnelClientRefreshSendMask(VoipTunnelRefT *pVoipTunnel, VoipTunnelClientT *pClient) +{ + VoipTunnelGameT *pGame = &pVoipTunnel->pGameList[pClient->iGameIdx]; + uint32_t bInList, uGameClientId, uGameSendMask; + int32_t iGameClient, iSendClient; + + // iterate through game list and rebuild client's send mask + for (iGameClient = 0, uGameSendMask = 0; iGameClient < VOIPTUNNEL_MAXGROUPSIZE; iGameClient++) + { + // skip empty or inactive entries + if (pGame->aClientList[iGameClient] == 0 || pGame->bClientActive[iGameClient] == FALSE) + { + continue; + } + // iterate through client's send list + for (bInList = FALSE, iSendClient = 0, uGameClientId = pGame->aClientList[iGameClient]; iSendClient < VOIPTUNNEL_MAXGROUPSIZE; iSendClient++) + { + if (uGameClientId == (unsigned)pClient->aClientIds[iSendClient]) + { + bInList = TRUE; + break; + } + } + // update game send mask + uGameSendMask |= bInList << iGameClient; + } + // write back updated mask + NetPrintf(("voiptunnel: setting clientId=0x%08x gamesend mask to 0x%08x\n", pClient->uClientId, uGameSendMask)); + pClient->uGameSendMask = uGameSendMask; +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelStatus + + \Description + Get module status + + \Input *pVoipTunnel - pointer to module state + \Input iSelect - status selector + \Input iValue - selector specific + \Input *pBuf - [out] - selector specific + \Input iBufSize - size of output buffer + + \Output + int32_t - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'game' - copies game info for game index=iValue into buffer; returns zero on success, negative on failure + 'vddm' - retuns the number of voip packets that were not rebroadcast due to limits on max number of senders per game + 'vmtm' - returns the voip max talker metric (number of times the limit on talkers was reached) + 'mgam' - returns the max number of games the voiptunnel supports + 'musr' - returns the max number of clients the voiptunnel supports + 'ngam' - returns the total game count + 'nusr' - if iValue==-1, returns total user count, else returns count of users w/ gameserver==iValue + 'sock' - returns voip socket ref (copied into buffer), if available + 'talk' - returns the current number of active clients that are tagged as "talking" + \endverbatim + + \Version 04/10/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTunnelStatus(VoipTunnelRefT *pVoipTunnel, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) +{ + if ((iSelect == 'game') && (pBuf != NULL) && (iBufSize == sizeof(VoipTunnelGameT)) && (iValue < pVoipTunnel->iMaxGames)) + { + ds_memcpy(pBuf, &pVoipTunnel->pGameList[iValue], iBufSize); + return(0); + } + if (iSelect == 'mgam') + { + return(pVoipTunnel->iMaxGames); + } + if (iSelect == 'musr') + { + return(pVoipTunnel->iMaxClients); + } + if (iSelect == 'ngam') + { + return(pVoipTunnel->iNumGames); + } + if (iSelect == 'nusr') + { + int32_t iNumClients; + if (iValue == -1) + { + iNumClients = pVoipTunnel->iNumClients; + } + else if (iValue < pVoipTunnel->iMaxGames) + { + iNumClients = pVoipTunnel->pGameList[iValue].iNumClients; + } + else + { + NetPrintf(("voiptunnel: invalid game index (%d) passed to VoipTunnelStatus('nusr') selector\n", iValue)); + iNumClients = -1; + } + return(iNumClients); + } + if (iSelect == 'vddm') + { + return(pVoipTunnel->uVoiceDataDropMetric); + } + if (iSelect == 'vmtm') + { + return(pVoipTunnel->uVoiceMaxTalkersMetric); + } + if ((iSelect == 'sock') && (pBuf != NULL) && (iBufSize >= (signed)sizeof(pVoipTunnel->pVoipSocket))) + { + ds_memcpy(pBuf, &pVoipTunnel->pVoipSocket, sizeof(pVoipTunnel->pVoipSocket)); + return(0); + } + if (iSelect == 'talk') + { + int32_t iNumTalkingClients = -1; // default to error + if (iValue == -1) + { + iNumTalkingClients = pVoipTunnel->iNumTalkingClients; + } + else if (iValue < pVoipTunnel->iMaxGames) + { + iNumTalkingClients = pVoipTunnel->pGameList[iValue].iNumTalkingClients; + } + else + { + NetPrintf(("voiptunnel: invalid game index (%d) passed to VoipTunnelStatus('talk')\n", iValue)); + } + return(iNumTalkingClients); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelControl + + \Description + Control the module + + \Input *pVoipTunnel - pointer to module state + \Input iControl - control selector + \Input iValue - selector specific + \Input iValue2 - selector specific + \Input *pValue - selector specific + + \Notes + iControl can be one of the following: + + \verbatim + 'bvma' - set the maximum number of simultaneous voice broadcasters per game + 'rtim' - set voice receive timeout value (iValue = timeout in ms) + 'spam' - set debug level (iValue = level) + 'xnat' - set port sniffing enable/disable (iValue = TRUE/FALSE, default FALSE) + \endverbatim + + \Version 04/09/2006 (jbrookes) +*/ +/********************************************************************************F*/ +int32_t VoipTunnelControl(VoipTunnelRefT *pVoipTunnel, int32_t iControl, int32_t iValue, int32_t iValue2, const void *pValue) +{ + if (iControl == 'bvma') + { + NetPrintf(("voiptunnel: maximum number of simultaneous voice broadcasters changed from %d to %d\n", pVoipTunnel->iMaxVoiceBroadcasters, iValue)); + pVoipTunnel->iMaxVoiceBroadcasters = iValue; + return(0); + } + if (iControl == 'rtim') + { + pVoipTunnel->uVoiceRecvTimeout = iValue; + return(0); + } + if (iControl == 'spam') + { + pVoipTunnel->uDebugLevel = iValue; + return(0); + } + if (iControl == 'xnat') + { + NetPrintf(("voiptunnel: port sniffing %s\n", iValue ? "enabled" : "disabled")); + pVoipTunnel->bPortSniff = iValue; + return(0); + } + return(-1); +} + +/*F********************************************************************************/ +/*! + \Function VoipTunnelUpdate + + \Description + Update the module + + \Input *pVoipTunnel - voiptunnel ref + + \Version 03/24/2006 (jbrookes) +*/ +/********************************************************************************F*/ +void VoipTunnelUpdate(VoipTunnelRefT *pVoipTunnel) +{ + // update clientlist + _VoipTunnelClientListUpdate(pVoipTunnel); +} + diff --git a/r5dev/thirdparty/dirtysdk/source/xml/xmlformat.c b/r5dev/thirdparty/dirtysdk/source/xml/xmlformat.c new file mode 100644 index 00000000..584d24ff --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/xml/xmlformat.c @@ -0,0 +1,1411 @@ +/*H*************************************************************************************/ +/*! + \File xmlformat.c + + \Description + This module formats simple Xml, in a linear fashion, using a character buffer + that the client provides. See header for more information. + + \Copyright + Copyright (c) Electronic Arts 2004-2008. ALL RIGHTS RESERVED. + + \Notes + \verbatim + When XmlInit() is called, a 24-bytes header is added at the beginning of the + client-provided buffer. The header is an ascii string that looks like this: + + where: + AAAAAAAA is the offset field + BBBBBBBB is the buffer size field + CC is the indent field + DD is the flag field + Those first 24 bytes are replaced by whitespaces when XmlFinish() is called. + + Upon buffer overrun: + 1. _XmlIndent: negative is returned and buffer remain untouched; + 2. _XmlEncodeString: negative is returned and buffer[0] has been reset to '\0' (if length(buffer) > 0); + 3. snzprintf: zero/negative is returned and buffer[0] has been reset to '\0' (if length(buffer) > 0). + + Required buffer capacity for xml = length(xml) + 1. + That means if length(buffer) <= length(xml), buffer-overrun occurs. + \endverbatim + + \Version 01/30/2004 (jbertrand) First Version +*/ +/*************************************************************************************H*/ + +/*** Include files *********************************************************************/ + +#include +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/xml/xmlformat.h" + +/*** Defines ***************************************************************************/ +#define XML_HEADER_LEN 24 +#define XML_MAX_TAG_LENGTH 128 +#define XML_MAX_FLOAT_LENGTH 128 +#define XML_MAX_DATE_LENGTH 32 + +/*** Type Definitions ******************************************************************/ + +/*** Variables *************************************************************************/ +static const unsigned char _Xml_Hex2AsciiTable[16] = "0123456789abcdef"; + +// ascii->hex conversion table for 7bit ASCII characters +static const unsigned char _Xml_Ascii2HexTable[128] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// 7bit ASCII characters that should be encoded ([1..31 minus tab, cr, lf], <, =, >, &, ", and DEL) +static const unsigned char _Xml_EncodeStringTable[128] = +{ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, +}; + +/*** Private Functions *****************************************************************/ +static int32_t _XmlGetOffset(char *pXml) +{ + return((_Xml_Ascii2HexTable[(int32_t)pXml[1]] << 28) | + (_Xml_Ascii2HexTable[(int32_t)pXml[2]] << 24) | + (_Xml_Ascii2HexTable[(int32_t)pXml[3]] << 20) | + (_Xml_Ascii2HexTable[(int32_t)pXml[4]] << 16) | + (_Xml_Ascii2HexTable[(int32_t)pXml[5]] << 12) | + (_Xml_Ascii2HexTable[(int32_t)pXml[6]] << 8) | + (_Xml_Ascii2HexTable[(int32_t)pXml[7]] << 4) | + _Xml_Ascii2HexTable[(int32_t)pXml[8]]); +} + +static int32_t _XmlGetLength(char *pXml) +{ + return((_Xml_Ascii2HexTable[(int32_t)pXml[9]] << 28) | + (_Xml_Ascii2HexTable[(int32_t)pXml[10]] << 24) | + (_Xml_Ascii2HexTable[(int32_t)pXml[11]] << 20) | + (_Xml_Ascii2HexTable[(int32_t)pXml[12]] << 16) | + (_Xml_Ascii2HexTable[(int32_t)pXml[13]] << 12) | + (_Xml_Ascii2HexTable[(int32_t)pXml[14]] << 8) | + (_Xml_Ascii2HexTable[(int32_t)pXml[15]] << 4) | + _Xml_Ascii2HexTable[(int32_t)pXml[16]]); +} + +static int32_t _XmlGetIndent(char *pXml) +{ + return((_Xml_Ascii2HexTable[(int32_t)pXml[17]] << 4) | + _Xml_Ascii2HexTable[(int32_t)pXml[18]]); +} + +static int32_t _XmlGetFlags(char *pXml) +{ + return((_Xml_Ascii2HexTable[(int32_t)pXml[19]] << 4) | + _Xml_Ascii2HexTable[(int32_t)pXml[20]]); +} + +static void _XmlSetOffset(char *pXml, int32_t iOff) +{ + pXml[1] = _Xml_Hex2AsciiTable[(iOff>>28)&15]; + pXml[2] = _Xml_Hex2AsciiTable[(iOff>>24)&15]; + pXml[3] = _Xml_Hex2AsciiTable[(iOff>>20)&15]; + pXml[4] = _Xml_Hex2AsciiTable[(iOff>>16)&15]; + pXml[5] = _Xml_Hex2AsciiTable[(iOff>>12)&15]; + pXml[6] = _Xml_Hex2AsciiTable[(iOff>>8)&15]; + pXml[7] = _Xml_Hex2AsciiTable[(iOff>>4)&15]; + pXml[8] = _Xml_Hex2AsciiTable[(iOff>>0)&15]; +} + +static void _XmlSetLength(char *pXml, int32_t iLen) +{ + pXml[9] = _Xml_Hex2AsciiTable[(iLen>>28)&15]; + pXml[10] = _Xml_Hex2AsciiTable[(iLen>>24)&15]; + pXml[11] = _Xml_Hex2AsciiTable[(iLen>>20)&15]; + pXml[12] = _Xml_Hex2AsciiTable[(iLen>>16)&15]; + pXml[13] = _Xml_Hex2AsciiTable[(iLen>>12)&15]; + pXml[14] = _Xml_Hex2AsciiTable[(iLen>>8)&15]; + pXml[15] = _Xml_Hex2AsciiTable[(iLen>>4)&15]; + pXml[16] = _Xml_Hex2AsciiTable[(iLen>>0)&15]; +} + +static void _XmlSetIndent(char *pXml, int32_t iIndent) +{ + pXml[17] = _Xml_Hex2AsciiTable[(iIndent >> 4) & 15]; + pXml[18] = _Xml_Hex2AsciiTable[(iIndent >> 0) & 15]; +} + +static void _XmlSetFlags(char *pXml, int32_t iFlags) +{ + pXml[19] = _Xml_Hex2AsciiTable[(iFlags >> 4) & 15]; + pXml[20] = _Xml_Hex2AsciiTable[(iFlags >> 0) & 15]; +} + +/*F*************************************************************************************/ +/*! + \Function _XmlValidHeader + + \Description + Check whether buffer is initialized with a valid header. + + \Input *pBuffer - buffer to which xml will be written + + \Output + int32_t - returns 1 if there is a valid header, 0 otherwise +*/ +/*************************************************************************************F*/ +static int32_t _XmlValidHeader(const char *pBuffer) +{ + // Make sure all the header components exist + if ((pBuffer[0] != '<') || (pBuffer[XML_HEADER_LEN-3] != ' ') || (pBuffer[XML_HEADER_LEN-2] != '/') || (pBuffer[XML_HEADER_LEN-1] != '>')) + return(0); + else + return(1); +} + +/*F*************************************************************************************/ +/*! + \Function _XmlOpenTag + + \Description + Check whether there is an open tag in the buffer. + + \Input *pBuffer - buffer to which xml will be written + \Input iOffset - current offset within the buffer + + \Output + int32_t - returns 1 if an open tag exists in the buffer, 0 otherwise +*/ +/*************************************************************************************F*/ +static int32_t _XmlOpenTag(const char *pBuffer, int32_t iOffset) +{ + int32_t iBackPos; + int32_t iBeginCount = 0; + int32_t iEndCount = 0; + + for (iBackPos = (iOffset-1); iBackPos >= XML_HEADER_LEN; iBackPos--) + { + // Count all closing tags (ended tag with no children will be counted as starting and closing tag) + if ( ((pBuffer[iBackPos] == '>') && (pBuffer[iBackPos-1] == '/')) + || ((pBuffer[iBackPos] == '/') && (pBuffer[iBackPos-1] == '<'))) + { + iEndCount++; + iBackPos--; + } + // Count all starting tags + else if (pBuffer[iBackPos] == '<') + { + iBeginCount++; + // If there are more open tags than close tags, return true + if (iBeginCount > iEndCount) + { + return(1); + } + } + } + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function _XmlOpenForAttr + + \Description + Check whether the previous tag can still have attributes written. + + \Input *pBuffer - buffer to which xml will be written + \Input iOffset - current offset within the buffer + + \Output + int32_t - returns 1 if the previous tag can still have attributes written, 0 otherwise +*/ +/*************************************************************************************F*/ +static int32_t _XmlOpenForAttr(const char *pBuffer, int32_t iOffset) +{ + int32_t iBackPos = iOffset-1; + + if ( iOffset < 1 ) + return(0); + + // Buffer does not end with a tag, child text exists + if (pBuffer[iBackPos] != '>') + return(0); + + while (iBackPos >= XML_HEADER_LEN) + { + // Last tag in buffer is already closed + if ((pBuffer[iBackPos] == '>') && (pBuffer[iBackPos-1] == '/')) + return(0); + // Buffer ends with an open tag and no children + else if (pBuffer[iBackPos] == '<') + return(1); + else + iBackPos--; + } + // No tags found in buffer + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function _XmlUpdateHeader + + \Description + Update the XML header to reflect the number of characters written. If the buffer + was full, this function will update the header to indicate the full buffer and will + return the XML_ERR_FULL error code. + + \Input *pBuffer - buffer to which xml will be written + \Input iNumChars - number of characters written, -1 for buffer filled + \Input iLength - length of the buffer + \Input iOffset - current offset within the buffer + \Input iIndent - current indent level + + \Output + int32_t - returns XML_ERR_FULL if buffer was filled, XML_ERR_NONE otherwise +*/ +/*************************************************************************************F*/ +static int32_t _XmlUpdateHeader(char *pBuffer, int32_t iNumChars, int32_t iLength, int32_t iOffset, int32_t iIndent) +{ + if (iNumChars < 0) + { + pBuffer[iLength-1] = 0; + iOffset = iLength; + _XmlSetOffset(pBuffer, iOffset); + return XML_ERR_FULL; + } + else + { + iOffset += iNumChars; + _XmlSetOffset(pBuffer, iOffset); + _XmlSetIndent(pBuffer, iIndent); + return XML_ERR_NONE; + } +} + +/*F****************************************************************************/ +/*! + \Function _XmlIndent + + \Description + Add the appropriate level of whitespace to indent the current tag if + the whitespace flag is enabled for the buffer. + + \Input *pBuffer - buffer to which xml will be written + \Input iLength - length of the buffer + \Input iOffset - current offset within the buffer + \Input iIndent - current indent level + \Input iFlags - flags + + \Output + int32_t - negative=buffer overrun, zero/positive=number of characters written +*/ +/****************************************************************************F*/ +static int32_t _XmlIndent(char *pBuffer, int32_t iLength, int32_t iOffset, int32_t iIndent, int32_t iFlags) +{ + int32_t iNumChars = 0; + +#if DIRTYCODE_LOGGING + if (iIndent < 0) + { + NetPrintf(("xmlformat: warning -- invalid indent level (%d) in _XmlIndent\n", iIndent)); + } +#endif + + if ((iFlags & XML_FL_WHITESPACE) == 0) + iNumChars = 0; + else if (iIndent <= 0) + { + if ((iLength - iOffset) > 1) + { + iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "\n"); + } + else + { + iNumChars = -1; + } + } + else + { + if ((iLength - iOffset) > ((iIndent * 2) + 1)) + { + iNumChars = ds_snzprintf(pBuffer + iOffset, iLength - iOffset, "\n%*c", iIndent * 2, ' '); + } + else + { + iNumChars = -1; + } + } + + return(iNumChars); +} + +/*F*************************************************************************************/ +/*! + \Function _XmlFormatInsert + + \Description + Insert a preformatted item + + \Input *pBuffer - the xml buffer + \Input *pValue - raw string to insert (must be correctly preformatted) + + \Output int32_t - return code. +*/ +/*************************************************************************************F*/ +static int32_t _XmlFormatInsert(char *pBuffer, const char *pValue) +{ + int32_t iWidth, iCount; + int32_t iOffset, iLength, iIndent, iFlags; + + // make sure there is a header and extract fields + if (!_XmlValidHeader(pBuffer)) + return(XML_ERR_UNINIT); + + iOffset = _XmlGetOffset(pBuffer); + iLength = _XmlGetLength(pBuffer); + iIndent = _XmlGetIndent(pBuffer); + iFlags = _XmlGetFlags(pBuffer); + + // there must be an open tag in the buffer + if (!_XmlOpenTag(pBuffer, iOffset)) + { + return(XML_ERR_NOT_OPEN); + } + + // check if there's enough room for the insertion to complete successfully + iWidth = (int32_t)strlen(pValue); + // figure out indent size + iWidth += ((pValue[0] == '<') && (iFlags & XML_FL_WHITESPACE)) ? (1 + iIndent * 2) : 0; + // we must be able to insert completely + if ((iLength - iOffset) <= iWidth) + { + return(XML_ERR_FULL); + } + + // buffer is good, now start to determine the insertion type + // 1. value, handle indent if necessary + if (pValue[0] == '<') + { + if (iFlags & XML_FL_WHITESPACE) + { + pBuffer[iOffset++] = '\n'; + for (iCount = 0; iCount < iIndent; ++iCount) + { + pBuffer[iOffset++] = ' '; + pBuffer[iOffset++] = ' '; + } + } + } + // 2. attr="value"> + else if (strchr(pValue, '"') != NULL) + { + // the tag must be open for accepting attributes (no children and no element text set) + if (!_XmlOpenForAttr(pBuffer, iOffset)) + { + return(XML_ERR_ATTR_POSITION); + } + // replace the close tag '>' to ' ' for input (pValue[] must look like: attr="value">) + pBuffer[iOffset - 1] = ' '; + } + + // for all insertion types, insert the string + for (iCount = 0; pValue[iCount] != 0; ++iCount) + { + pBuffer[iOffset++] = pValue[iCount]; + } + pBuffer[iOffset] = 0; + + // update offset and return + _XmlSetOffset(pBuffer, iOffset); + return(XML_ERR_NONE); +} + +/*F*************************************************************************************/ +/*! + \Function _XmlEncodeString + + \Description + Encode a string with %xx as needed for reserved characters + + \Input *pBuffer - the xml buffer + \Input iLength - number of eight-bit characters available in buffer + \Input *_pSrc - source string to add to buffer + + \Output int32_t - negative=not enough buffer, zero/positive=encoded length. +*/ +/*************************************************************************************F*/ +static int32_t _XmlEncodeString(char *pBuffer, int32_t iLength, const char *_pSrc) +{ + char * const pBackup = pBuffer; + const uint8_t *pSource = (const uint8_t *)_pSrc; + int32_t iRemain; + + // encode the string + for (iRemain = iLength; (iRemain > 1) && (*pSource != '\0'); ++pSource) + { + if ((*pSource < 128) && (_Xml_EncodeStringTable[*pSource])) + { + // hex encode all out of range values + if (iRemain > 6) + { + *pBuffer++ = '&'; + *pBuffer++ = '#'; + *pBuffer++ = 'x'; + *pBuffer++ = _Xml_Hex2AsciiTable[(*pSource)>>4]; + *pBuffer++ = _Xml_Hex2AsciiTable[(*pSource)&0xF]; + *pBuffer++ = ';'; + iRemain -= 6; + } + else // buffer overrun + { + break; + } + } + else + { + // normal encoding + *pBuffer++ = *pSource; + --iRemain; + } + } + + // make sure space for terminator + if (iRemain > 0) + { + *pBuffer = 0; + } + + // if character(s) remains, return negative to indicate buffer-overrun. + if (*pSource != '\0') + { + // if buffer has been changed, reset the change(s) + if (pBackup != pBuffer) + { + *pBackup = '\0'; + } + return(-1); + } + + // return encoded size + return(iLength-iRemain); +} + +/*** Public Functions ******************************************************************/ + +/*F*************************************************************************************/ +/*! + \Function XmlInit + + \Description + Initialize the Xml Buffer. This MUST be called prior to any other calls. + Client can allocate the buffer on stack, but should not access it directly. ( see XmlData() ). + + \Input *pBuffer - buffer to which xml will be written. + \Input iBufLen size of buffer passed in. + \Input uFlags - encoding flags (XMLFORMAT_FL_xxx) +*/ +/*************************************************************************************F*/ +void XmlInit(char *pBuffer, int32_t iBufLen, unsigned char uFlags) +{ + int32_t iOffset; + + ds_memclr(pBuffer, iBufLen); + if (iBufLen > XML_HEADER_LEN) { + iOffset = ds_snzprintf(pBuffer, iBufLen - 1, "<00000000000000000000 />"); + _XmlSetOffset(pBuffer, iOffset); + _XmlSetLength(pBuffer, iBufLen); + _XmlSetFlags(pBuffer, uFlags); + } +} + +/*F*************************************************************************************/ +/*! + \Function XmlBufSizeIncrease + + \Description + Notify the xmlformat API that the buffer size was increased. + + \Input *pBuffer - buffer to which xml is being written. + \Input iNewBufLen new size for that buffer + + \Version 03/30/2010 (mclouatre) +*/ +/*************************************************************************************F*/ +void XmlBufSizeIncrease(char *pBuffer, int32_t iNewBufLen) +{ + _XmlSetLength(pBuffer, iNewBufLen); +} + +/*F*************************************************************************************/ +/*! + \Function XmlFinish + + \Description + Signal completion of output to this buffer. Clear the XML API hidden + data. + + \Input *pBuffer buffer to which xml was written. + + \Output char* - pointer to real XML data (skipping past header) +*/ +/*************************************************************************************F*/ +char *XmlFinish(char *pBuffer) +{ + if (_XmlValidHeader(pBuffer)) + { + ds_memset(pBuffer, ' ', XML_HEADER_LEN); + pBuffer += XML_HEADER_LEN; + } + return(pBuffer); +} + +/*F*************************************************************************************/ +/*! + \Function XmlTagStart + + \Description + Start an xml element (node), to which you can add other elements or attributes. + Must be ended with XmlTagEnd. + + \Input *pBuffer - xml buffer. + \Input *pName - Name of the tag. + + \Output + int32_t - return code. + + \Todo + Consider using var-args for attributes? +*/ +/*************************************************************************************F*/ +int32_t XmlTagStart(char *pBuffer, const char *pName) +{ + int32_t iOffset, iLength, iNumChars, iIndent, iFlags, iTempLength; + + if (!_XmlValidHeader(pBuffer)) + return(XML_ERR_UNINIT); + + iOffset = _XmlGetOffset(pBuffer); + iLength = _XmlGetLength(pBuffer); + iIndent = _XmlGetIndent(pBuffer); + iFlags = _XmlGetFlags(pBuffer); + + if ((iNumChars = _XmlIndent(pBuffer, iLength, iOffset, iIndent++, iFlags)) < 0) + { + return(XML_ERR_FULL); + } + + if ((iTempLength = ds_snzprintf(pBuffer + iOffset + iNumChars, iLength - iOffset - iNumChars, "<%s>", pName)) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iNumChars += iTempLength; + + return _XmlUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent); +} + +/*F*************************************************************************************/ +/*! + \Function XmlTagEnd + + \Description + End the current element or tag -- must have an outstanding open tag. + + \Input *pBuffer - xml buffer. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlTagEnd(char *pBuffer) +{ + int32_t iOffset, iLength, iNumChars; + int32_t iBackPos; + int32_t iEndCount = 0; + int32_t iBeginCount = 0; + int32_t iIndent; + int32_t iFlags; + + if (!_XmlValidHeader(pBuffer)) + return(XML_ERR_UNINIT); + + iOffset = _XmlGetOffset(pBuffer); + iLength = _XmlGetLength(pBuffer); + iIndent = _XmlGetIndent(pBuffer); + iFlags = _XmlGetFlags(pBuffer); + iIndent--; + + for (iBackPos = (iOffset-1); iBackPos >= XML_HEADER_LEN; iBackPos--) + { + // Count all closing tags (ended tag with no children will be counted as starting and closing tag) + if ( ((pBuffer[iBackPos] == '>') && (pBuffer[iBackPos-1] == '/')) + || ((pBuffer[iBackPos] == '/') && (pBuffer[iBackPos-1] == '<'))) + { + iEndCount++; + iBackPos--; + } + // Count all starting tags, if there are more starting than closing tags, we have found the tag to close + else if (pBuffer[iBackPos] == '<') + { + iBeginCount++; + if (iBeginCount > iEndCount) + { + if ((iLength - iOffset) < 3) // "/>" + { + // nothing changed, return(XML_ERR_FULL) directly + return(XML_ERR_FULL); + } + + // If the last tag in the buffer is open and has no children, close it + if ((iEndCount == 0) && (pBuffer[iOffset-1] == '>')) + { + pBuffer[iOffset-1] = ' '; + iNumChars = ds_snzprintf(pBuffer+iOffset, iLength-iOffset, "/>"); + return(_XmlUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); + } + // The open tag has children, so create a new closing tag + else + { + char tagBuffer[XML_MAX_TAG_LENGTH + 4]; + int32_t iTagLen = 0; + tagBuffer[iTagLen++] = '<'; + tagBuffer[iTagLen++] = '/'; + tagBuffer[iTagLen] = '0'; + // Scan forward to obtain the name for the closing tag + do + { + iBackPos++; + tagBuffer[iTagLen] = pBuffer[iBackPos]; + iTagLen++; + } while ((iBackPos < iLength) && (pBuffer[iBackPos+1] != ' ') && (pBuffer[iBackPos+1] != '>') && (iTagLen < XML_MAX_TAG_LENGTH-1)); + tagBuffer[iTagLen++] = '>'; + tagBuffer[iTagLen] = 0; + if (((iNumChars = _XmlIndent(pBuffer, iLength, iOffset, iIndent, iFlags)) < 0) + || ((iLength - iOffset) <= (iTagLen + iNumChars))) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iNumChars += ds_snzprintf(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, "%s", tagBuffer); + return(_XmlUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); + } + } + } + } + return(0); +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemAddString + + \Description + Add a complete, contained text element. Builds start and end tag. + Nothing can be appended to this tag. + + \Input *pBuffer - xml buffer. + \Input *pElemName - name of the element (tag). + \Input *pValue - value of the element. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemAddString(char *pBuffer, const char *pElemName, const char *pValue) +{ + int32_t iOffset, iLength, iNumChars, iIndent, iFlags, iTempLength; + + if (!_XmlValidHeader(pBuffer)) + return(XML_ERR_UNINIT); + + iOffset = _XmlGetOffset(pBuffer); + iLength = _XmlGetLength(pBuffer); + iIndent = _XmlGetIndent(pBuffer); + iFlags = _XmlGetFlags(pBuffer); + + // there must be an open tag in the buffer + if (_XmlOpenTag(pBuffer, iOffset)) + { + if ((iNumChars = _XmlIndent(pBuffer, iLength, iOffset, iIndent, iFlags)) < 0) + { + return(XML_ERR_FULL); + } + + // + if ((iTempLength = ds_snzprintf(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, "<%s>", pElemName)) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iNumChars += iTempLength; + + // Value + if ((iTempLength = _XmlEncodeString(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, pValue)) < 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iNumChars += iTempLength; + + // + if ((iTempLength = ds_snzprintf(pBuffer+iOffset+iNumChars, iLength-iOffset-iNumChars, "", pElemName)) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iNumChars += iTempLength; + + // all done successfully + return(_XmlUpdateHeader(pBuffer, iNumChars, iLength, iOffset, iIndent)); + } + else + { + return(XML_ERR_NOT_OPEN); + } +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemAddInt + + \Description + Add a complete, contained integer element. Builds start and end tag. + Nothing can be appended to this tag. + + \Input *pBuffer - xml buffer. + \Input *pElemName - name of the element (tag). + \Input iValue - integer value of the element. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemAddInt(char *pBuffer, const char *pElemName, int32_t iValue) +{ + char strInt[32]; // [32] is large enough for int32_t string + + ds_snzprintf(strInt, sizeof(strInt), "%d", iValue); + return(XmlElemAddString(pBuffer, pElemName, strInt)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemAddFloat + + \Description + Add a complete, contained decimal element. Builds start and end tag. + Nothing can be appended to this tag. Format it using formatSpec. + + \Input *pBuffer - xml buffer. + \Input *pElemName - name of the element (tag). + \Input *pFormatSpec - Spec for formatting the float (see sprintf). + \Input fValue - float value of the element. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemAddFloat(char *pBuffer, const char *pElemName, const char *pFormatSpec, float fValue) +{ + char strFloat[XML_MAX_FLOAT_LENGTH]; + + if (ds_snzprintf(strFloat, sizeof(strFloat), pFormatSpec, fValue) <= 0) + { + return(XML_ERR_INVALID_PARAM); + } + return(XmlElemAddString(pBuffer, pElemName, strFloat)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemAddDate + + \Description + Add a complete, contained Date element, using epoch date. Builds start and end tag. + Nothing can be appended to this tag. + + \Input *pBuffer - xml buffer. + \Input *pElemName - name of the element (tag). + \Input uEpochDate - date/time since epoch. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemAddDate(char *pBuffer, const char *pElemName, uint32_t uEpochDate) +{ + struct tm *time1, time2; + char strDate[XML_MAX_DATE_LENGTH]; + + if ((time1 = ds_secstotime(&time2, uEpochDate)) == NULL) + { + return(XML_ERR_INVALID_PARAM); + } + + ds_snzprintf(strDate, + sizeof(strDate), + "%04d-%02d-%02dT%02d:%02d:%02dZ", + time1->tm_year + 1900, + time1->tm_mon + 1, + time1->tm_mday, + time1->tm_hour, + time1->tm_min, + time1->tm_sec); + return(XmlElemAddString(pBuffer, pElemName, strDate)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlAttrSetString + + \Description + Set a text attribute for an open element (started tag). + Must be called before Element text is set. + + \Input *pBuffer - xml buffer. + \Input *pAttrName - name of the attribute to be added. + \Input *pValue - string value of the element. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlAttrSetString(char *pBuffer, const char *pAttrName, const char *pValue) +{ + int32_t iOffset, iLength, iNumChars, iIndent; + int32_t iInsert; + + if (!_XmlValidHeader(pBuffer)) + return(XML_ERR_UNINIT); + + iOffset = iInsert = _XmlGetOffset(pBuffer); + iLength = _XmlGetLength(pBuffer); + iIndent = _XmlGetIndent(pBuffer); + + // there must be an open tag in the buffer + if (_XmlOpenTag(pBuffer, iOffset)) + { + // the tag must be open for accepting attributes (no children and no element text set) + if (_XmlOpenForAttr(pBuffer, iOffset)) + { + // insert the tag + if ((iNumChars = ds_snzprintf(pBuffer+iInsert, iLength-iInsert, "%s=\"", pAttrName)) <= 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iInsert += iNumChars; + + // insert the value + if ((iNumChars = _XmlEncodeString(pBuffer+iInsert, iLength-iInsert, pValue)) < 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iInsert += iNumChars; + + // see if there is room for final terminator -- if so, include entire field + if ((iInsert + 3) > iLength) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + // --> attr="value --> + pBuffer[iOffset-1] = ' '; + pBuffer[iInsert+0] = '"'; + pBuffer[iInsert+1] = '>'; + pBuffer[iInsert+2] = '\0'; + + // update the header + return(_XmlUpdateHeader(pBuffer, iInsert-iOffset+2, iLength, iOffset, iIndent)); + } + else + return(XML_ERR_ATTR_POSITION); + } + else + { + return(XML_ERR_NOT_OPEN); + } +} + +/*F*************************************************************************************/ +/*! + \Function XmlAttrSetStringRaw + + \Description + Set a text attribute for an open element (started tag) with no text encoding + being performed on the input string. Must be called before Element text is set. + + \Input *pBuffer - xml buffer. + \Input *pAttr - attribute to be added (expected format: attrName="attrValue"). + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlAttrSetStringRaw(char *pBuffer, const char *pAttr) +{ + char strStringRaw[256]; + + if (ds_snzprintf(strStringRaw, sizeof(strStringRaw), "%s>", pAttr) <= 0) + { + return(XML_ERR_INVALID_PARAM); + } + return(_XmlFormatInsert(pBuffer, strStringRaw)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlAttrSetInt + + \Description + Set an integer attribute for an open element (started tag). + Must be called before Element text is set. + + \Input *pBuffer - xml buffer. + \Input *pAttrName - name of the attribute to be added. + \Input iValue - integer value to be set. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlAttrSetInt(char *pBuffer, const char *pAttrName, int32_t iValue) +{ + char strInt[32]; // [32] is large enough for int32_t string + + ds_snzprintf(strInt, sizeof(strInt), "%d", iValue); + return(XmlAttrSetString(pBuffer, pAttrName, strInt)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlAttrSetFloat + + \Description + Set a decimal attribute for an open element (started tag). + Must be called before Element text is set. Format using formatSpec. + + \Input *pBuffer - xml buffer. + \Input *pAttrName - name of the attribute (tag). + \Input *pFormatSpec - Spec for formatting the float (see sprintf). + \Input fValue - float value of the attribute. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlAttrSetFloat(char *pBuffer, const char *pAttrName, const char *pFormatSpec, float fValue) +{ + char strFloat[XML_MAX_FLOAT_LENGTH]; + + if (ds_snzprintf(strFloat, sizeof(strFloat), pFormatSpec, fValue) <= 0) + { + return(XML_ERR_INVALID_PARAM); + } + return(XmlAttrSetString(pBuffer, pAttrName, strFloat)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlAttrSetDate + + \Description + Set a date attribute for an open element (started tag). + Must be called before Element text is set. + + \Input *pBuffer - xml buffer. + \Input *pAttrName - name of the attribute to be added. + \Input uEpochDate - date value to be set. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlAttrSetDate(char *pBuffer, const char *pAttrName, uint32_t uEpochDate) +{ + struct tm *time1, time2; + char strDate[XML_MAX_DATE_LENGTH]; + + if ((time1 = ds_secstotime(&time2, uEpochDate)) == NULL) + { + return(XML_ERR_INVALID_PARAM); + } + + ds_snzprintf(strDate, + sizeof(strDate), + "%04d-%02d-%02dT%02d:%02d:%02dZ", + time1->tm_year + 1900, + time1->tm_mon + 1, + time1->tm_mday, + time1->tm_hour, + time1->tm_min, + time1->tm_sec); + return(XmlAttrSetString(pBuffer, pAttrName, strDate)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlAttrSetAddr + + \Description + Set an IP address attribute (in dot notation) + + \Input *pBuffer - xml buffer. + \Input *pAttrName - name of the attribute to be added. + \Input uAddr - address value to be set. + + \Output int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlAttrSetAddr(char *pBuffer, const char *pAttrName, uint32_t uAddr) +{ + char strAddr[32]; //[32] is large enough for xxx.xxx.xxx.xxx + + ds_snzprintf(strAddr, sizeof(strAddr), "%a", uAddr); + return(XmlAttrSetString(pBuffer, pAttrName, strAddr)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemSetString + + \Description + Set text value for an open element (started tag). + Must have an open or started tag, uses element from previous start tag call. + To be used if element is simple text node with some attributes. + + \Input *pBuffer - xml buffer. + \Input *pValue - value of the element. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemSetString(char *pBuffer, const char *pValue) +{ + int32_t iOffset, iLength, iNumChars, iIndent, iInsert; + + if (!_XmlValidHeader(pBuffer)) + return(XML_ERR_UNINIT); + + iOffset = iInsert = _XmlGetOffset(pBuffer); + iLength = _XmlGetLength(pBuffer); + iIndent = _XmlGetIndent(pBuffer); + + // there must be an open tag in the buffer + if (_XmlOpenTag(pBuffer, iOffset)) + { + // insert the value + if ((iNumChars = _XmlEncodeString(pBuffer + iInsert, iLength - iInsert, pValue)) < 0) + { + pBuffer[iOffset] = '\0'; // reset all changes + return(XML_ERR_FULL); + } + iInsert += iNumChars; + + // update the header + return(_XmlUpdateHeader(pBuffer, iInsert-iOffset, iLength, iOffset, iIndent)); + } + else + return(XML_ERR_NOT_OPEN); +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemSetStringRaw + + \Description + Set text value for an open element (started tag), with no encoding being + performed on the input string. Must have an open or started tag, uses element + from previous start tag call. To be used if element is simple text node with some + attributes. + + \Input *pBuffer - xml buffer. + \Input *pValue - value of the element. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemSetStringRaw(char *pBuffer, const char *pValue) +{ + return(_XmlFormatInsert(pBuffer, pValue)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemSetInt + + \Description + Set integer value for an open element (started tag). + Must have an open or started tag, uses element from previous start tag call. + To be used if element is a simple node with some attributes. + + \Input *pBuffer - xml buffer. + \Input iValue - integer value of the element. + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemSetInt(char *pBuffer, int32_t iValue) +{ + char strInt[32]; + + ds_snzprintf(strInt, sizeof(strInt), "%d", iValue); + return(XmlElemSetString(pBuffer, strInt)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlElemSetAddr + + \Description + Set an IP address value (in dot notation) for an open element. + + \Input *pBuffer - xml buffer. + \Input uAddr - address to set + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemSetAddr(char *pBuffer, uint32_t uAddr) +{ + char strAddr[32]; + + ds_snzprintf(strAddr, sizeof(strAddr), "%a", uAddr); + return(XmlElemSetString(pBuffer, strAddr)); +} + + +/*F*************************************************************************************/ +/*! + \Function XmlElemSetDate + + \Description + Set a date for an open element. Must have an open element (started tag). + + \Input *pBuffer - xml buffer. + \Input uEpoch - date to set + + \Output + int32_t - return code. +*/ +/*************************************************************************************F*/ +int32_t XmlElemSetDate(char *pBuffer, uint32_t uEpoch) +{ + struct tm Date; + char strDate[XML_MAX_DATE_LENGTH]; + + if (ds_secstotime(&Date, uEpoch) == NULL) + { + return(XML_ERR_INVALID_PARAM); + } + ds_snzprintf(strDate, + sizeof(strDate), + "%04d-%02d-%02dT%02d:%02d:%02dZ", + Date.tm_year + 1900, + Date.tm_mon + 1, + Date.tm_mday, + Date.tm_hour, + Date.tm_min, + Date.tm_sec); + return(XmlElemSetString(pBuffer, strDate)); +} + +/*F*************************************************************************************/ +/*! + \Function XmlFormatVPrintf + + \Description + \verbatim + Takes a format string and variable parameter list and formats into syntax + correct XML doing optimizations like removing empty tags (i.e., + becomes ). The "v" (variable args) version can be called by other + functions that use "..." in the their prototype. + \endverbatim + + \Input *pXmlBuff - xml output buffer + \Input iBufLen - length of Xml output buffer (negative=append to existing buffer) + \Input *pFormat - printf style format string + \Input pFmtArgs - variable argument list (use va_start / va_end to get) + + \Output char* - pointer to formatted buffer +*/ +/*************************************************************************************F*/ +char *XmlFormatVPrintf(char *pXmlBuff, int32_t iBufLen, const char *pFormat, va_list pFmtArgs) +{ + int32_t iToken; + int32_t iIndex; + char strName[65] = ""; + const char *pName; + const char *pParse; + unsigned char uFlags = 0; + /* table of allowed name characters. see http://www.w3.org/TR/xml/#charsets + for information on name characters allowed by the XML 1.0 specification */ + static char _ConvName[128] = + " " " " + " -. " "0123456789: " + " ABCDEFGHIJKLMNO" "PQRSTUVWXYZ _" + " abcdefghijklmno" "pqrstuvwxyz "; + + // determine if whitespace indenting is desired + if (*pFormat == ' ') + { + uFlags |= XML_FL_WHITESPACE; + ++pFormat; + } + + // start the formatting + if (iBufLen >= 0) + XmlInit(pXmlBuff, iBufLen, uFlags); + + // parse the format string + for (pParse = pFormat; *pParse != 0; ++pParse) + { + // look for tag open + if (pParse[0] == '<') + { + for (iIndex = 0; (iIndex < (signed)(sizeof(strName) - 1)) && (pParse[iIndex+1] > 0) && (pParse[iIndex+1] < 127); ++iIndex) + { + if (_ConvName[(int32_t)pParse[iIndex+1]] <= ' ') + break; + strName[iIndex] = pParse[iIndex+1]; + } + strName[iIndex] = '\0'; + if (iIndex > 0) + { + XmlTagStart(pXmlBuff, strName); + } + } + + // parse name for assignments + if (pParse[0] == '=') + { + // find start of name + for (pName = pParse; (pName != pFormat) && (pName[-1] > 0) && (pName[-1] < 127); --pName) + { + if (_ConvName[pName[-1]&127] <= ' ') + break; + } + // copy and format name in private buffer + for (iIndex = 0; (iIndex < (signed)(sizeof(strName) - 1)) && (pName+iIndex != pParse); ++iIndex) + { + strName[iIndex] = pName[iIndex]; + } + strName[iIndex] = '\0'; + } + + // grab next 3 characters as a token + iToken = ' '; + iToken = (iToken << 8) | pParse[0]; + iToken = (iToken << 8) | pParse[1]; + iToken = (iToken << 8) | pParse[2]; + + // handle format tokens + if ((pParse[0] == '/') && (pParse[1] == '>')) + { + XmlTagEnd(pXmlBuff); + } + else if (iToken == ' >%s') + { + const char *pString = va_arg(pFmtArgs, const char *); + XmlElemSetString(pXmlBuff, pString); + } + else if (iToken == ' >%d') + { + int32_t iInteger = va_arg(pFmtArgs, int32_t); + XmlElemSetInt(pXmlBuff, iInteger); + } + else if (iToken == ' >%e') + { + uint32_t uEpoch = va_arg(pFmtArgs, uint32_t); + if (uEpoch == 0) + uEpoch = (uint32_t)ds_timeinsecs(); + XmlElemSetDate(pXmlBuff, uEpoch); + } + else if (iToken == ' >%a') + { + uint32_t uAddr = va_arg(pFmtArgs, uint32_t); + XmlElemSetAddr(pXmlBuff, uAddr); + } + else if (strName[0] == '\0') + { + } + else if (iToken == ' =%s') + { + const char *pString = va_arg(pFmtArgs, const char *); + XmlAttrSetString(pXmlBuff, strName, pString); + } + else if (iToken == ' =%d') + { + int32_t iInteger = va_arg(pFmtArgs, int32_t); + XmlAttrSetInt(pXmlBuff, strName, iInteger); + } + else if (iToken == ' =%e') + { + uint32_t uEpoch = va_arg(pFmtArgs, uint32_t); + if (uEpoch == 0) + uEpoch = (uint32_t)ds_timeinsecs(); + XmlAttrSetDate(pXmlBuff, strName, uEpoch); + } + else if (iToken == ' =%a') + { + uint32_t uAddr = va_arg(pFmtArgs, uint32_t); + XmlAttrSetAddr(pXmlBuff, strName, uAddr); + } + } + + // finish up and return data pointer + if (iBufLen >= 0) + pXmlBuff = XmlFinish(pXmlBuff); + return(pXmlBuff); +} + +/*F*************************************************************************************/ +/*! + \Function XmlFormatPrintf + + \Description + \verbatim + Takes a format string and variable parameter list and formats into syntax + correct XML doing optimizations like removing empty tags (i.e., + becomes ). + \endverbatim + + \Input *pXmlBuff - xml output buffer + \Input iBufLen - length of Xml output buffer (negative=append to existing buffer) + \Input *pFormat - printf style format string + + \Output char* - pointer to formatted buffer +*/ +/*************************************************************************************F*/ +char *XmlFormatPrintf(char *pXmlBuff, int32_t iBufLen, const char *pFormat, ...) +{ + char *pResult; + va_list pFmtArgs; + + // setup varargs and call real function + va_start(pFmtArgs, pFormat); + pResult = XmlFormatVPrintf(pXmlBuff, iBufLen, pFormat, pFmtArgs); + va_end(pFmtArgs); + + // return pointer to start of valid XML data (skips leading spaces) + return(pResult); +} diff --git a/r5dev/thirdparty/dirtysdk/source/xml/xmlparse.c b/r5dev/thirdparty/dirtysdk/source/xml/xmlparse.c new file mode 100644 index 00000000..aceac9b3 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/source/xml/xmlparse.c @@ -0,0 +1,1813 @@ +/*H*************************************************************************************************/ +/*! + \File xmlparse.c + + \Description + \verbatim + This is a simple XML parser for use in controlled situations. It should not + be used with arbitrary XML from uncontrolled hosts (i.e., test its parsing against + a particular host before using it). This only implement a small subset of full + XML and while suitable for many applications, it is easily confused by badly + formed XML or by some of the legitimate but convoluted XML conventions. + + In particular, this parser cannot handle degenerate empty elements (i.e.,

+ will confuse it). Empty elements must be of proper XML form

. Only the + predefined entity types (< > & ' ") are supported. This module + does not support unicode values. + \endverbatim + + \Copyright + Copyright (c) Tiburon Entertainment / Electronic Arts 2002. ALL RIGHTS RESERVED. + + \Version 1.0 01/30/2002 (gschaefer) First Version +*/ +/*************************************************************************************************H*/ + + +/*** Include files ********************************************************************************/ + +#include +#include + +#include "DirtySDK/platform.h" +#include "DirtySDK/dirtysock/dirtylib.h" +#include "DirtySDK/xml/xmlparse.h" + +/*** Type Definitions *****************************************************************************/ + +/*** Variables ************************************************************************************/ + +static const unsigned char _Xml_TopDecode[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 16, 32, 48, 64, 80, 96,112,128,144, 0, 0, 0, 0, 0, 0, + 0,160,176,192,208,224,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,160,176,192,208,224,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const unsigned char _Xml_BtmDecode[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// +static const uint8_t _Xml_CDataTrailer[3] = +{ ']', ']', '>' }; + +/*** Private Functions ****************************************************************************/ + +/*F************************************************************************************************/ +/*! + \Function _ParseNumber + + \Description + Convert a number from string to integer form + + \Input pData - pointer to source string + \Input pValue - pointer to output number + + \Output - Pointer to next string element + + \Version 04/19/2002 (GWS) +*/ +/************************************************************************************************F*/ +static const unsigned char *_ParseNumber(const unsigned char *pData, int32_t *pValue) +{ + for (*pValue = 0; (*pData >= '0') && (*pData <= '9'); ++pData) + { + *pValue = (*pValue * 10) + (*pData & 15); + } + return(pData); +} + +/*F************************************************************************************************/ +/*! + \Function _XmlContentChar + + \Description + Translate an encoded content character to ASCII. + + \Input pXml - pointer to raw xml source + \Input pData - pointer to translated output character + + \Output - Pointer to next xml source item + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +static const unsigned char *_XmlContentChar(const unsigned char *pXml, unsigned char *pData) +{ + uint32_t uNumber; + + // default to bogus + *pData = '~'; + + // coded values + if (pXml[0] == '#') + { + // hex + if (pXml[1] == 'x') + { + // convert hex to integer + for (uNumber = 0, pXml += 2; (*pXml != '\0') && (_Xml_BtmDecode[*pXml] > 0); ++pXml) + { + uNumber = (uNumber << 4) | _Xml_BtmDecode[*pXml]; + } + // truncate to 8 bits + *pData = (unsigned char)uNumber; + } + // decimal + else + { + // convert decimal to integer + for (uNumber = 0, pXml += 1; (*pXml >= '0') && (*pXml <= '9'); ++pXml) + { + uNumber = (uNumber * 10) + (*pXml & 15); + } + // truncate to 8 bits + *pData = (unsigned char)uNumber; + } + } + // & + else if ((pXml[0] == 'a') && (pXml[1] == 'm') && (pXml[2] == 'p')) + { + pXml += 3; + *pData = '&'; + } + // ' + else if ((pXml[0] == 'a') && (pXml[1] == 'p') && (pXml[2] == 'o') && (pXml[3] == 's')) + { + pXml += 4; + *pData = '\''; + } + // " + else if ((pXml[0] == 'q') && (pXml[1] == 'u') && (pXml[2] == 'o') && (pXml[3] == 't')) + { + pXml += 4; + *pData = '"'; + } + // < + else if ((pXml[0] == 'l') && (pXml[1] == 't')) + { + pXml += 2; + *pData = '<'; + } + // > + else if ((pXml[0] == 'g') && (pXml[1] == 't')) + { + pXml += 2; + *pData = '>'; + } + + // skip the terminator if present + if (*pXml == ';') + { + pXml += 1; + } + + // return updated pointer + return(pXml); +} + + +/*F************************************************************************************************/ +/*! + \Function _XmlContentFind + + \Description + Locate the content area for the current element + + \Input _pXml - pointer to xml element (start tag) + + \Output - Pointer to content area (NULL=no content) + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +static const unsigned char *_XmlContentFind(const char *_pXml) +{ + const unsigned char *pXml = (const unsigned char *)_pXml; + + // we should be pointing at an element start + if ((pXml == NULL) || (*pXml != '<')) + { + pXml = NULL; + } + // find end of element + else + { + while ((*pXml != 0) && (*pXml != '>')) + { + ++pXml; + } + if (*pXml != 0) + { + pXml = (pXml[-1] == '/' ? NULL : pXml+1); + } + } + // return content pointer + return(pXml); +} + + +/*F************************************************************************************************/ +/*! + \Function _XmlAttribFind + + \Description + Locate a named attribute within an element + + \Input _pXml - pointer to xml element (start tag) + \Input _pAttrib - pointer to attribute to find + + \Output + unsigned char * - pointer to attribute value (NULL=no content) + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +static const unsigned char *_XmlAttribFind(const char *_pXml, const char *_pAttrib) +{ + int32_t iIndex; + unsigned char uTerm; + const unsigned char *pMatch = NULL; + const unsigned char *pXml = (const unsigned char *)_pXml; + const unsigned char *pAttrib = (const unsigned char *)_pAttrib; + + // we should be pointing at an element start + if ((pXml != NULL) && (*pXml == '<')) + { + // skip past element name + for (; (*pXml != 0) && (*pXml > ' '); ++pXml) + ; + + // parse all the attributes + for (;;) + { + // skip past the white space + for (; (*pXml != 0) && (*pXml <= ' '); ++pXml) + ; + + // see if this is the attribute we want + for (iIndex = 0; (pXml[iIndex] != 0) && (pXml[iIndex] == pAttrib[iIndex]); ++iIndex) + ; + + // see if we reached end of attribute name + if (pAttrib[iIndex] == 0) + { + // skip past white space + for (pXml += iIndex; (*pXml != 0) && (*pXml <= ' '); ++pXml) + ; + // see if we found the matching attribute + if (*pXml == '=') + { + // find the start of the data + for (pMatch = pXml+1; (*pMatch != 0) && (*pMatch <= ' '); ++pMatch) + ; + break; + } + } + + // find end of attribute name + for (; (*pXml != 0) && (*pXml != '>') && (*pXml != '='); ++pXml) + ; + + // make sure this is an attribute assignment + if (*pXml != '=') + { + break; + } + + // skip equals and white space + for (++pXml; (*pXml != 0) && (*pXml <= ' '); ++pXml) + ; + + // see if the data is quoted + if ((*pXml == '"') || (*pXml == '\'')) + { + // find ending quote + for (uTerm = *pXml++; (*pXml != 0) && (*pXml != uTerm); ++pXml) + ; + // if we found a quote, skip it + if (*pXml == uTerm) + { + ++pXml; + } + } + // just skip till next space + else + { + for (; (*pXml != 0) && (*pXml > ' '); ++pXml) + ; + } + } + } + + // return the match point + return(pMatch); +} + +/*F************************************************************************************************/ +/*! + \Function _XmlSkipCDataHeader + + \Description + Determine if the string points to a CDATA header + + \Input pXml - pointer to an XML content string + \Input ppContent - pointer to pointer to the start of the content string + + \Output - TRUE if the string demarks a CDATA header + + \Version 12/14/2006 (kcarpenter) +*/ +/************************************************************************************************F*/ +static int32_t _XmlSkipCDataHeader(const unsigned char *pXml, const unsigned char **ppContent) +{ + int32_t iIndex; + + // Compare the CDATA header char-by-char + for (iIndex = 0; iIndex < (signed)sizeof(_Xml_CDataHeader); iIndex++) + { + if (pXml[iIndex] != _Xml_CDataHeader[iIndex]) + { + return(FALSE); + } + } + + // Pass out content start pointer, if possible + if (ppContent != NULL) + { + *ppContent = pXml + sizeof(_Xml_CDataHeader); + } + return(TRUE); +} + +/*F************************************************************************************************/ +/*! + \Function _XmlSkipCDataTrailer + + \Description + Determine if the string points to a CDATA trailer + + \Input pXml - pointer to the possible end of an XML content string + \Input ppAfterContent - pointer to pointer to the data immediately after the trailer + + \Output - TRUE if the string demarks a CDATA trailer + + \Version 12/14/2006 (kcarpenter) +*/ +/************************************************************************************************F*/ +static int32_t _XmlSkipCDataTrailer(const unsigned char *pXml, const unsigned char **ppAfterContent) +{ + int32_t iIndex; + + // Compare the CDATA trailer char-by-char + for (iIndex = 0; iIndex < (signed)sizeof(_Xml_CDataTrailer); iIndex++) + { + if (pXml[iIndex] != _Xml_CDataTrailer[iIndex]) + { + return(FALSE); + } + } + + // Pass out pointer to after CDATA trailer, if possible + if (ppAfterContent != NULL) + { + *ppAfterContent = pXml + sizeof(_Xml_CDataTrailer); + } + return(TRUE); +} + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************/ +/*! + \Function _XmlPrintFmtLine + + \Description + Print current line buffer to debug output + + \Input *pLine - line buffer to print + \Input iLevel - current xml depth + \Input *pStrPrefix - string to prefix each line with + + \Version 09/16/2010 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _XmlPrintFmtLine(const char *pLine, int32_t iLevel, const char *pStrPrefix) +{ + // declare const string of 64 spaces which gives us 16 levels of indentiation with four spaces per indent + const char strIndent[] = + " " + " " + " " + " "; + int32_t iOffset; + + // figure out offset into tabs string based on level + if ((iOffset = ((sizeof(strIndent)-1)/4 - iLevel)) < 0) + { + iOffset = 0; + } + iOffset *= 4; + + // output the string + NetPrintf(("%s%s%s\r\n", pStrPrefix, strIndent + iOffset, pLine)); +} +#endif + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************/ +/*! + \Function _XmlPrintFmtInsertChar + + \Description + Insert a character into the print buffer. + + \Input **ppLine - current insertion point + \Input **ppXml - current read point + \Input *pLineStart - start of line buffer + \Input iBufSize - size of line buffer + \Input iLevel - current xml depth + \Input *pStrPrefix - string to prefix each line with + + \Version 09/16/2010 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _XmlPrintFmtInsertChar(char **ppLine, const unsigned char **ppXml, char *pLineStart, int32_t iBufSize, int32_t iLevel, const char *pStrPrefix) +{ + // add to line + **ppLine = **ppXml; + *ppLine += 1; + *ppXml += 1; + + // check for full buffer + if ((*ppLine - pLineStart) == (iBufSize - 1)) + { + // flush current line and continue + **ppLine = '\0'; + _XmlPrintFmtLine(pLineStart, iLevel, pStrPrefix); + pLineStart[0] = ' '; + *ppLine = pLineStart + 1; + } +} +#endif + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************/ +/*! + \Function _XmlPrintFmt + + \Description + Takes a as input an XML buffer, and prints out a "nicely" formatted version of + the XML data to debug output. Each line of output is appended to text generated + by the format string and arguments. + + \Input *pXml - xml to print + \Input *pStrPrefix - string to prefix each line with + + \Version 09/16/2010 (jbrookes) +*/ +/*************************************************************************************F*/ +static void _XmlPrintFmt(const unsigned char *pXml, const char *pStrPrefix) +{ + const char *pTagStart, *pTagEnd, *pTmpBuf; + int32_t iLevel, iNewLevel; + char strLine[132], *pLine; + + // skip any leading whitespace + while ((*pXml == ' ') || (*pXml == '\t')) + { + pXml += 1; + } + + // add xml to buffer + for (iLevel = 0; *pXml != '\0'; ) + { + // copy to line buffer until tag end, crlf, or eos + pLine = strLine; + pTagStart = (*pXml == '<') ? (const char *)pXml+1 : NULL; + pTagEnd = NULL; + + // check for close tag + iNewLevel = ((*pXml == '<') && (*(pXml+1) == '/')) ? iLevel-2 : iLevel; + for (; (*pXml != '>') && (*pXml != '\r') && (*pXml != '\0'); ) + { + if ((*pXml == ' ') && (pTagEnd == NULL)) + { + pTagEnd = (const char *)pXml; + } + _XmlPrintFmtInsertChar(&pLine, &pXml, strLine, sizeof(strLine), iLevel, pStrPrefix); + } + if (*pXml == '>') + { + if ((*(pXml-1) != '/') && (*(pXml-1) != '?')) + { + iNewLevel += 1; + } + if (pTagEnd == NULL) + { + pTagEnd = (const char *)pXml; + } + _XmlPrintFmtInsertChar(&pLine, &pXml, strLine, sizeof(strLine), iLevel, pStrPrefix); + } + // scan ahead and see if we have a matching end tag + if (pTagStart != NULL) + { + pTmpBuf = (const char *)pXml; + while ((*pTmpBuf != '>') && (*pTmpBuf != '\r') && (*pTmpBuf != '\0')) + { + if ((*pTmpBuf == '<') && (*(pTmpBuf+1) == '/') && (!strncmp((const char *)pTagStart, (const char *)pTmpBuf+2, (size_t)(pTagEnd-pTagStart)))) + { + while ((*pXml != '>') && (*pXml != '\r') && (*pXml != '\0')) + { + _XmlPrintFmtInsertChar(&pLine, &pXml, strLine, sizeof(strLine), iLevel, pStrPrefix); + } + if (*pXml == '>') + { + _XmlPrintFmtInsertChar(&pLine, &pXml, strLine, sizeof(strLine), iLevel, pStrPrefix); + } + iNewLevel--; + break; + } + pTmpBuf++; + } + } + // skip whitespace + while ((*pXml == '\r') || (*pXml == '\n') || (*pXml == '\t') || (*pXml == ' ')) + { + pXml++; + } + // null terminate + *pLine = '\0'; + + // indent based on level + if (iNewLevel < iLevel) + { + iLevel = iNewLevel; + } + + // output line + _XmlPrintFmtLine(strLine, iLevel, pStrPrefix); + + // index to new level + iLevel = iNewLevel; + } +} +#endif + +/*F************************************************************************************************/ +/*! + \Function _XmlSkip + + \Description + Skip to the next xml element (used to enumerate lists) + + \Input *pXml - pointer to xml element (start tag) + \Input *pValid - [optional, out] TRUE if element being skipped is complete, else FALSE + + \Output - Pointer to next element at same level + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +static const unsigned char *_XmlSkip(const unsigned char *pXml, uint32_t *pValid) +{ + int32_t iIndex; + uint32_t uValid; + + // allow NULL pValid + if (pValid == NULL) + { + pValid = &uValid; + } + + // assume invalid + *pValid = FALSE; + + // make sure reference is valid + if ((pXml == NULL) || (*pXml != '<')) + { + return(NULL); + } + + // handle xml decl + if (pXml[1] == '?') + { + // find the end sequence + for (pXml += 2; (pXml[0] != 0) && (pXml[0] != '?') && (pXml[1] != '>'); ++pXml) + ; + // skip the terminator + if (pXml[0] != 0) + { + pXml += 2; + } + } + + // handle doctype/comment + else if ((pXml[0] == '<') && (pXml[1] == '!')) + { + // count past the < and > + for (iIndex = 1, ++pXml; (pXml[0] != 0) && (iIndex > 0); ++pXml) + { + if (pXml[0] == '>') + { + iIndex -= 1; + } + if (pXml[0] == '<') + { + iIndex += 1; + } + } + } + + // handle regular element + else + { + // count past < and > + for (iIndex = 1, ++pXml; (iIndex > 0) && (pXml[0] != 0);) + { + // if we found an element start + if (pXml[0] == '<') + { + // see if this is CDATA, and if so, skip it + if (_XmlSkipCDataHeader(pXml, &pXml)) + { + // skip over the CDATA section + while (TRUE) + { + if (*pXml == 0) + { + break; + } + if ((*pXml == ']') && _XmlSkipCDataTrailer(pXml, &pXml)) + { + break; + } + ++pXml; + } + continue; + } + // if we hit a doctype/comment, then skip it + if ((pXml[0] == '<') && (pXml[1] == '!')) + { + if ((pXml = _XmlSkip(pXml, NULL)) == NULL) + { + return(pXml); + } + else + { + continue; + } + } + + // adjust the indent level + iIndex += (pXml[1] != '/' ? 1 : -1); + // find the end of the element + for (++pXml; (pXml[0] != 0) && (pXml[0] != '>'); ++pXml) + ; + // dont gobble /> but do skip > + if (pXml[-1] == '/') + { + --pXml; + } + else if (pXml[0] == '>') + { + ++pXml; + } + } + // this is the end of an empty element + else if ((pXml[0] == '/') && (pXml[1] == '>')) + { + iIndex -= 1; + pXml += 2; + } + else + { + ++pXml; + } + } + + // valid? + if (*pXml != '\0') + { + *pValid = 1; + } + else if ((iIndex <= 1) && (pXml[-1] == '>')) + { + *pValid = 1; + } + } + + // skip white space + for (; (*pXml != 0) && (*pXml <= ' '); ++pXml) + ; + + // see if we ran out of data + if (*pXml == 0) + { + pXml = NULL; + } + + // return new position + return(pXml); +} + + +/*** Public functions *****************************************************************************/ + + +/*F************************************************************************************************/ +/*! + \Function XmlSkip + + \Description + Skip to the next xml element (used to enumerate lists) + + \Input *pXml - pointer to xml element (start tag) + + \Output - Pointer to next element at same level + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +const char *XmlSkip(const char *pXml) +{ + return((const char *)_XmlSkip((const unsigned char *)pXml, NULL)); +} + +/*F************************************************************************************************/ +/*! + \Function XmlComplete + + \Description + Determines if the xml element pointed to is complete + + \Input *pXml - pointer to xml element (start tag) + + \Output + uint32_t - TRUE if the element is complete, else FALSE + + \Version 09/24/2010 (jbrookes) +*/ +/************************************************************************************************F*/ +uint32_t XmlComplete(const char *pXml) +{ + uint32_t uValid; + _XmlSkip((const unsigned char *)pXml, &uValid); + return(uValid); +} + +/*F************************************************************************************************/ +/*! + \Function XmlFind + + \Description + Find an element within an xml document + + \Input _pXml - pointer to xml document + \Input _pName - element name (x.y.z notation) + + \Output - Pointer to named element start + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +const char *XmlFind(const char *_pXml, const char *_pName) +{ + int32_t iXmlIndex, iMatchIndex; + const unsigned char *pMatch = NULL; + const unsigned char *pXml = (const unsigned char *)_pXml; + const unsigned char *pName = (const unsigned char *)_pName; + + // make sure record is valid + if ((pXml == NULL) || (pXml[0] == 0)) + { + return(NULL); + } + + // make sure name is valid + if ((pName == NULL) || (pName[0] == 0)) + { + return(NULL); + } + + // allow "." to specify current tag + if ((*pName == '.') && (*pXml == '<')) + { + pName += 1; + pXml += 1; + } + + // scan the data + while (pXml != NULL) + { + // locate the first marker + for (; (pXml[0] != '<') && (pXml[0] != 0); ++pXml) + ; + + // if we hit an xml decl then skip it + if ((pXml[0] == '<') && (pXml[1] == '?')) + { + pXml = _XmlSkip(pXml, NULL); + continue; + } + + // if we hit a doctype/comment, then skip it + if ((pXml[0] == '<') && (pXml[1] == '!')) + { + pXml = _XmlSkip(pXml, NULL); + continue; + } + + // if we hit an end tag, then search must be over + if ((pXml[0] == '<') && (pXml[1] == '/')) + { + break; + } + + // skip past leading < + if (pXml[0] != 0) + { + ++pXml; + } + + // see if we found the matching element + for (iXmlIndex = iMatchIndex = 0; (pXml[iXmlIndex] != '\0') && (pName[iMatchIndex] != '\0'); ++iXmlIndex, ++iMatchIndex) + { + // wildcard match? + if ((pName[iMatchIndex] == '%') && (pName[iMatchIndex+1] == '*')) + { + // consume all input until the subsequent character + for (iMatchIndex += 2; (pXml[iXmlIndex] != '\0') && (pXml[iXmlIndex] != pName[iMatchIndex]); ++iXmlIndex) + ; + } + else if (pXml[iXmlIndex] != pName[iMatchIndex]) + { + break; + } + } + + // handle end of data case + if (pXml[iXmlIndex] == '\0') + { + break; + } + + // see if we matched + if ((pXml[iXmlIndex] <= ' ') || (pXml[iXmlIndex] == '>') || (pXml[iXmlIndex] == '/')) + { + // handle leaf match + if (pName[iMatchIndex] == '\0') + { + pMatch = pXml-1; + break; + } + + // handle node match + if ((pName[iMatchIndex] == '.') && (pXml[iXmlIndex] != '/')) + { + // locate the end of the start tag + for (; (pXml[0] != '\0') && (pXml[0] != '>'); ++pXml) + ; + // see if this is the stop point + if (pName[iMatchIndex+1] == '\0') + { + pMatch = (*pXml ? pXml+1 : NULL); + break; + } + + // search within this area recursively + if (pXml[0] != '\0') + { + pMatch = (const unsigned char *)XmlFind((const char *)pXml+1, (const char *)pName+iMatchIndex+1); + } + break; + } + + } + + // skip past mismatch element + pXml = _XmlSkip(pXml-1, NULL); + } + + // point to matching data + return((const char *)pMatch); +} + + +/*F************************************************************************************************/ +/*! + \Function XmlNext + + \Description + Skip to next element with same name as current + + \Input _pXml - pointer to element start + + \Output - Pointer to next element start + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +const char *XmlNext(const char *_pXml) +{ + int32_t iIndex; + const unsigned char *pMatch; + const unsigned char *pXml = (const unsigned char *)_pXml; + + // make sure we are at element start + for (; (*pXml != 0) && (*pXml != '<'); ++pXml) + ; + + // locate next tag that matches this one + for (pMatch = _XmlSkip(pXml, NULL); pMatch != NULL; pMatch = _XmlSkip(pMatch, NULL)) + { + // skip to the element start + for (; (*pMatch != 0) && (*pMatch != '<'); ++pMatch) + ; + + // see if we found next element of same name + for (iIndex = 0; (pMatch[iIndex] > ' ') && (pMatch[iIndex] != '>') && (pXml[iIndex] > ' ') && (pXml[iIndex] != '>') && (pMatch[iIndex] == pXml[iIndex]); ++iIndex) + ; + + // check for complete match + if (((pMatch[iIndex] <= ' ') || (pMatch[iIndex] == '>')) && ((pXml[iIndex] <= ' ') || (pXml[iIndex] == '>'))) + { + break; + } + } + + // return the match + return((const char *)pMatch); +} + + +/*F************************************************************************************************/ +/*! + \Function XmlStep + + \Description + Step over tag (regardless of the tab being a start tag or a end tag). + + \Input *_pXml - pointer to tag opening character + + \Output + const char * - pointer to byte following tag closing character or NULL if at end of document + + \Version 09/16/2010 (jbrookes) +*/ +/************************************************************************************************F*/ +const char *XmlStep(const char *_pXml) +{ + const unsigned char *pXml = (const unsigned char *)_pXml; + + // make sure we are at tag opening character + for (; (*pXml != 0) && (*pXml != '<'); ++pXml) + ; + + // step to tag closing character + for (; (*pXml != '\0') && (*pXml != '>'); ++pXml) + ; + + // skip past end, return NULL if at end of document + return(((*pXml != '\0') && (*(pXml+1) != '\0')) ? (const char *)pXml + 1 : NULL); +} + +/*F************************************************************************************************/ +/*! + \Function XmlContentGetString + + \Description + Return element contents as a string + + \Input pXml - pointer to xml document + \Input _pBuffer - string output buffer + \Input iLength - length of output buffer + \Input pDefault - default value if (pXml == NULL) + + \Output + int32_t - length of string (-1=nothing copied) + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlContentGetString(const char *pXml, char *_pBuffer, int32_t iLength, const char *pDefault) +{ + int32_t iLen; + const unsigned char *pData; + unsigned char *pBuffer = (unsigned char *)_pBuffer; + int32_t bInCDataSection; + + // validate buffer + if ((pBuffer == NULL) || (iLength < 1)) + { + return(-1); + } + + // locate the content area + pData = _XmlContentFind(pXml); + if (pData != NULL) + { + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // Skip CDATA header, if any + bInCDataSection = _XmlSkipCDataHeader(pData, &pData); + + // convert the string + for (iLen = 1; (iLen < iLength) && (*pData != 0); ++iLen) + { + if (bInCDataSection) + { + // Just copy the data until we reach the end trailer marker + if (_XmlSkipCDataTrailer(pData, NULL)) + { + break; + } + + *pBuffer++ = *pData++; + } + else + { + if (*pData == '<') + { + break; + } + + if (*pData == '&') + { + pData = _XmlContentChar(pData+1, pBuffer++); + } + else + { + *pBuffer++ = *pData++; + } + } + } + // remove trailing white space + while ((iLen > 1) && (pBuffer[-1] <= ' ')) + { + --iLen; + --pBuffer; + } + // terminate buffer + *pBuffer = 0; + } + else if (pDefault != NULL) + { + // copy over the string + for (iLen = 1; (iLen < iLength) && (*pDefault != 0); ++iLen) + { + *pBuffer++ = *pDefault++; + } + // terminate buffer + *pBuffer = 0; + } + else + { + // leave the old string + iLen = 0; + } + + // return length (without terminator) + return(iLen-1); +} + + +/*F************************************************************************************************/ +/*! + \Function XmlContentGetInteger + + \Description + Return element contents as an integer + + \Input pXml - pointer to xml document + \Input iDefault - default value if (pXml == NULL) + + \Output + int32_t - element contents as integer + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlContentGetInteger(const char *pXml, int32_t iDefault) +{ + return((int32_t)XmlContentGetInteger64(pXml, iDefault)); +} + +/*F************************************************************************************************/ +/*! + \Function XmlContentGetInteger64 + + \Description + Return element contents as an integer (64-bit) + + \Input pXml - pointer to xml document + \Input iDefault - default value if (pXml == NULL) + + \Output + int64_t - element contents as integer +*/ +/************************************************************************************************F*/ +int64_t XmlContentGetInteger64(const char *pXml, int64_t iDefault) +{ + int32_t iSign = 1; + uint64_t uNumber; + + const uint8_t *pData; + + // locate the content area + if ((pData = _XmlContentFind(pXml)) == NULL) + { + return(iDefault); + } + + // skip leading whitespace + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // check for a sign value + if (*pData == '+') + { + iSign = 1; + ++pData; + } + if (*pData == '-') + { + iSign = -1; + ++pData; + } + + // parse the number + for (uNumber = 0; (*pData >= '0') && (*pData <= '9'); ++pData) + { + uNumber = (uNumber * 10) + (*pData & 15); + } + + // return final number + return (iSign*uNumber); +} + +/*F************************************************************************************************/ +/*! + \Function XmlContentGetToken + + \Description + Return element contents as a token (a packed sequence of characters) + + \Input pXml - pointer to xml document + \Input iDefault - default value if (pXml == NULL) + + \Output + int32_t - element contents as token + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlContentGetToken(const char *pXml, int32_t iDefault) +{ + int32_t iToken = ' '; + const unsigned char *pData; + + // locate the content area + if ((pData = _XmlContentFind(pXml)) == NULL) + { + return(iDefault); + } + + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // parse the number + while ((*pData > ' ') && (*pData != '<')) + { + iToken = (iToken << 8) | *pData++; + } + + // return the token + return(iToken); +} + + +/*F************************************************************************************************/ +/*! + \Function XmlContentGetDate + + \Description + Return epoch seconds for a date + + \Input pXml - pointer to xml document + \Input uDefault - default value if (pXml == NULL) + + \Output + uint32_t - element contents as epoch seconds + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +uint32_t XmlContentGetDate(const char *pXml, uint32_t uDefault) +{ + struct tm tm; + const unsigned char *pData; + + // locate the content area + if ((pData = _XmlContentFind(pXml)) == NULL) + { + return(uDefault); + } + + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // set the unused fields + tm.tm_isdst = -1; + tm.tm_wday = 0; + tm.tm_yday = 0; + + // extract the date + pData = _ParseNumber(pData, &tm.tm_year); + if ((*pData == '.') || (*pData == '-')) + ++pData; + pData = _ParseNumber(pData, &tm.tm_mon); + if ((*pData == '.') || (*pData == '-')) + ++pData; + pData = _ParseNumber(pData, &tm.tm_mday); + if ((*pData == ' ') || (*pData == 'T')) + ++pData; + pData = _ParseNumber(pData, &tm.tm_hour); + if (*pData == ':') + ++pData; + pData = _ParseNumber(pData, &tm.tm_min); + if (*pData == ':') + ++pData; + _ParseNumber(pData, &tm.tm_sec); + + // validate the fields + if ((tm.tm_year < 1970) || (tm.tm_year > 2099) || (tm.tm_mon < 1) || (tm.tm_mon > 12) || (tm.tm_mday < 1) || (tm.tm_mday > 31)) + { + return(uDefault); + } + if ((tm.tm_hour < 0) || (tm.tm_hour > 23) || (tm.tm_min < 0) || (tm.tm_min > 59) || (tm.tm_sec < 0) || (tm.tm_sec > 61)) + { + return(uDefault); + } + + // return epoch time + tm.tm_mon -= 1; + tm.tm_year -= 1900; + return((uint32_t)ds_timetosecs(&tm)); +} + +/*F************************************************************************************************/ +/*! + \Function XmlContentGetAddress + + \Description + Parse element contents as an internet dot-notation address and return as an integer. + + \Input pXml - pointer to xml document + \Input iDefault - default value if (pXml == NULL) + + \Output + int32_t - element contents as integer + + \Version 10/07/2005 (JLB) +*/ +/************************************************************************************************F*/ +int32_t XmlContentGetAddress(const char *pXml, int32_t iDefault) +{ + const unsigned char *pData; + uint32_t iAddr, iQuad, iValue; + + // locate the content area + if ((pData = _XmlContentFind(pXml)) == NULL) + { + return(iDefault); + } + + // parse address + for (iAddr = iQuad = 0; iQuad < 4; iQuad++, pData++) + { + // parse current digit + for (iValue = 0; (*pData >= '0') && (*pData <= '9'); pData++) + { + iValue = (iValue*10) + (*pData & 15); + } + // verify digit + if ((iQuad < 3) && (*pData != '.')) + { + iAddr = iDefault; + break; + } + // accumulate digit in address + iAddr <<= 8; + iAddr |= iValue; + } + + // return to caller + return(iAddr); +} + +/*F************************************************************************************************/ +/*! + \Function XmlContentGetBinary + + \Description + Return binary encoded data + + \Input pXml - pointer to xml document + \Input pBuffer - pointer to buffer to copy binary data to + \Input iLength - length of buffer pointed to by pBuffer + + \Output + int32_t - negative if error, length otherwise + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlContentGetBinary(const char *pXml, char *pBuffer, int32_t iLength) +{ + int32_t iCount; + const unsigned char *pData; + + // locate the content area + if ((pData = _XmlContentFind(pXml)) == NULL) + { + return(0); + } + + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // see if they just want the length + if (pBuffer == NULL) + { + for (iCount = 0; (pData[0] >= '0') && (pData[1] >= '0'); pData += 2) + { + ++iCount; + } + return(iCount); + } + + // make sure buffer has a valid length + if (iLength < 0) + { + return(-1); + } + + // attempt to copy over data + for (iCount = 0; (iCount < iLength) && (pData[0] >= '0') && (pData[1] >= '0'); ++iCount) + { + *pBuffer++ = _Xml_TopDecode[pData[0]] | _Xml_BtmDecode[pData[1]]; + pData += 2; + } + + // return the length + return(iCount); +} + +/*F************************************************************************************************/ +/*! + \Function XmlAttribGetString + + \Description + Return element attribute as a string + + \Input pXml - pointer to xml element + \Input pAttrib - name of attribute + \Input pBuffer - output string buffer + \Input iLength - length of string buffer + \Input pDefault - default string if (pXml == NULL || pAttrib not found) + + \Output + int32_t - length of result string (-1=nothing copied) + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlAttribGetString(const char *pXml, const char *pAttrib, char *pBuffer, int32_t iLength, const char *pDefault) +{ + int32_t iLen; + unsigned char uTerm; + const unsigned char *pData; + + // validate buffer + if ((pBuffer == NULL) || (iLength < 1)) + { + return(-1); + } + + // locate the content area + pData = _XmlAttribFind(pXml, pAttrib); + if (pData != NULL) + { + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // see if there is a terminator + if ((*pData == '"') || (*pData == '\'')) + { + uTerm = *pData++; + } + else + { + uTerm = 0; + } + + // convert the string + for (iLen = 1; (iLen < iLength) && (*pData != uTerm) && (*pData != 0) && (*pData != '>'); ++iLen) + { + if (*pData == '&') + { + pData = _XmlContentChar(pData+1, (unsigned char *)pBuffer++); + } + else + { + *pBuffer++ = *pData++; + } + } + // terminate buffer + *pBuffer = 0; + } + else if (pDefault != NULL) + { + // copy over the string + for (iLen = 1; (iLen < iLength) && (*pDefault != 0); ++iLen) + { + *pBuffer++ = *pDefault++; + } + // terminate buffer + *pBuffer = 0; + } + else + { + // leave the old value + iLen = 0; + } + + // return length (without terminator) + return(iLen-1); +} + + +/*F************************************************************************************************/ +/*! + \Function XmlAttribGetInteger + + \Description + Return element attribute as an integer + + \Input pXml - pointer to xml element + \Input pAttrib - name of attribute + \Input iDefault - default value if (pXml == NULL || pAttrib not found) + + \Output + int32_t - attibute value as integer + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlAttribGetInteger(const char *pXml, const char *pAttrib, int32_t iDefault) +{ + return((int32_t)XmlAttribGetInteger64(pXml, pAttrib, iDefault)); +} + +/*F************************************************************************************************/ +/*! + \Function XmlAttribGetInteger64 + + \Description + Return element attribute as an integer (64-bit) + + \Input pXml - pointer to xml element + \Input pAttrib - name of attribute + \Input iDefault - default value if (pXml == NULL || pAttrib not found) + + \Output + int64_t - attibute value as integer +*/ +/************************************************************************************************F*/ +int64_t XmlAttribGetInteger64(const char *pXml, const char *pAttrib, int64_t iDefault) +{ + int32_t iSign = 1; + uint64_t uNumber; + const unsigned char *pData; + + // locate the content area + pData = _XmlAttribFind(pXml, pAttrib); + if (pData == NULL) + { + return(iDefault); + } + + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // skip terminator if present + if ((*pData == '"') || (*pData == '\'')) + { + ++pData; + } + + // check for a sign value + if (*pData == '+') + { + iSign = 1; + ++pData; + } + if (*pData == '-') + { + iSign = -1; + ++pData; + } + + // parse the number + for (uNumber = 0; (*pData >= '0') && (*pData <= '9'); ++pData) + { + uNumber = (uNumber * 10) + (*pData & 15); + } + + // check for symbol true + if (((pData[0]|32) == 't') && ((pData[1]|32) == 'r') && ((pData[2]|32) == 'u') && ((pData[3]|32) == 'e')) + { + iSign = 1; + uNumber = 1; + } + + // check for symbol false + if (((pData[0]|32) == 'f') && ((pData[1]|32) == 'a') && ((pData[2]|32) == 'l') && ((pData[3]|32) == 's') && ((pData[4]|32) == 'e')) + { + iSign = 1; + uNumber = 0; + } + + // return final value + return(iSign*uNumber); +} + + +/*F************************************************************************************************/ +/*! + \Function XmlAttribGetToken + + \Description + Return element attribute as a token + + \Input pXml - pointer to xml element + \Input pAttrib - name of attribute + \Input iDefault - default value if (pXml == NULL || pAttrib not found) + + \Output + int32_t - attribute value as a token + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlAttribGetToken(const char *pXml, const char *pAttrib, int32_t iDefault) +{ + int32_t iToken = ' '; + unsigned char uTerm = 0; + const unsigned char *pData; + + // locate the content area + pData = _XmlAttribFind(pXml, pAttrib); + if (pData == NULL) + { + return(iDefault); + } + + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // skip terminator if present + if ((*pData == '"') || (*pData == '\'')) + { + uTerm = *pData++; + } + + // parse the number + while ((*pData > ' ') && (*pData != uTerm) && (*pData != '>') && (*pData != 0)) + { + iToken = (iToken << 8) | *pData++; + } + + // return final value + return(iToken); +} + + +/*F************************************************************************************************/ +/*! + \Function XmlAttribGetDate + + \Description + Return epoch seconds for a date + + \Input pXml - pointer to xml element + \Input pAttrib - name of attribute + \Input uDefault - default value if (pXml == NULL || pAttrib not found) + + \Output + uint32_t - attribute value as epoch seconds + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +uint32_t XmlAttribGetDate(const char *pXml, const char *pAttrib, uint32_t uDefault) +{ + struct tm tm; + const unsigned char *pData; + + // locate the content area + pData = _XmlAttribFind(pXml, pAttrib); + if (pData == NULL) + { + return(uDefault); + } + + // skip leading white space + while ((*pData != 0) && (*pData <= ' ')) + { + ++pData; + } + + // skip terminator if present + if ((*pData == '"') || (*pData == '\'')) + { + ++pData; + } + + // set the unused fields + tm.tm_isdst = -1; + tm.tm_wday = 0; + tm.tm_yday = 0; + + // extract the date + pData = _ParseNumber(pData, &tm.tm_year); + if ((*pData == '.') || (*pData == '-')) + ++pData; + pData = _ParseNumber(pData, &tm.tm_mon); + if ((*pData == '.') || (*pData == '-')) + ++pData; + pData = _ParseNumber(pData, &tm.tm_mday); + if ((*pData == ' ') || (*pData == 'T')) + ++pData; + pData = _ParseNumber(pData, &tm.tm_hour); + if (*pData == ':') + ++pData; + pData = _ParseNumber(pData, &tm.tm_min); + if (*pData == ':') + ++pData; + _ParseNumber(pData, &tm.tm_sec); + + // validate the fields + if ((tm.tm_year < 1970) || (tm.tm_year > 2099) || (tm.tm_mon < 1) || (tm.tm_mon > 12) || (tm.tm_mday < 1) || (tm.tm_mday > 31)) + { + return(uDefault); + } + if ((tm.tm_hour < 0) || (tm.tm_hour > 23) || (tm.tm_min < 0) || (tm.tm_min > 59) || (tm.tm_sec < 0) || (tm.tm_sec > 61)) + { + return(uDefault); + } + + // return epoch time + tm.tm_mon -= 1; + tm.tm_year -= 1900; + return((uint32_t)ds_timetosecs(&tm)); +} + + +////////////////////////////////////////////////////////////////////////////////////////// + + +/*F************************************************************************************************/ +/*! + \Function XmlConvEpoch2Date + + \Description + Convert epoch to date components + + \Input uEpoch - epoch to convert to date components + \Input pYear - pointer to storage for year + \Input pMonth - pointer to storage for month + \Input pDay - pointer to storage for day + \Input pHour - pointer to storage for hour + \Input pMinute - pointer to storage for minute + \Input pSecond - pointer to storage for second + + \Output + int32_t - negative=error, zero=success + + \Version 01/30/2002 (GWS) +*/ +/************************************************************************************************F*/ +int32_t XmlConvEpoch2Date(uint32_t uEpoch, int32_t *pYear, int32_t *pMonth, int32_t *pDay, int32_t *pHour, int32_t *pMinute, int32_t *pSecond) +{ + int32_t iResult = -1; + struct tm tm; + + // convert to calendar time + if (ds_secstotime(&tm, uEpoch) != NULL) + { + // return the fields + if (pYear != NULL) + *pYear = tm.tm_year; + if (pMonth != NULL) + *pMonth = tm.tm_mon; + if (pDay != NULL) + *pDay = tm.tm_mday; + if (pHour != NULL) + *pHour = tm.tm_hour; + if (pMinute != NULL) + *pMinute = tm.tm_min; + if (pSecond != NULL) + *pSecond = tm.tm_sec; + // return success + iResult = 0; + } + + return(iResult); +} + +#if DIRTYCODE_LOGGING +/*F*************************************************************************************/ +/*! + \Function XmlPrintFmtCode + + \Description + Takes a as input an XML buffer, and prints out a "nicely" formatted version of + the XML data to debug output. Each line of output is appended to text generated + by the format string and arguments. + + \Input *pXml - xml to print + \Input *pFormat - printf style format string + \Input ... - variable-argument list + + \Version 09/15/2010 (jbrookes) +*/ +/*************************************************************************************F*/ +void XmlPrintFmtCode(const char *pXml, const char *pFormat, ...) +{ + char strPrefix[128]; + va_list pFmtArgs; + + // format prefix string + va_start(pFmtArgs, pFormat); + ds_vsnzprintf(strPrefix, sizeof(strPrefix), pFormat, pFmtArgs); + va_end(pFmtArgs); + + _XmlPrintFmt((const unsigned char *)pXml, strPrefix); +} +#endif diff --git a/r5dev/thirdparty/dirtysdk/support.txt b/r5dev/thirdparty/dirtysdk/support.txt new file mode 100644 index 00000000..f04de934 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/support.txt @@ -0,0 +1,5 @@ +DirtySock SDK Support + +Please contact GS-DirtySock Support for all support inquiries. +The DirtySock Customer Portal is located at https://developer.ea.com/display/dirtysock/DirtySock+Customer+Portal +The GS Support webpage is located at https://developer.ea.com/display/TEAMS/GS+Support which describes the GS support process. diff --git a/r5dev/thirdparty/dirtysdk/version.txt b/r5dev/thirdparty/dirtysdk/version.txt new file mode 100644 index 00000000..379ebdc6 --- /dev/null +++ b/r5dev/thirdparty/dirtysdk/version.txt @@ -0,0 +1,2 @@ +EADP Game Services DirtySDK 15.1.6.0.5 +August 19, 2020 diff --git a/r5dev/thirdparty/ea/EABase/LICENSE b/r5dev/thirdparty/ea/EABase/LICENSE new file mode 100644 index 00000000..738e3686 --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/LICENSE @@ -0,0 +1,25 @@ +Copyright (C) 2002-2013 Electronic Arts Inc + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/r5dev/thirdparty/ea/EABase/config/eacompiler.h b/r5dev/thirdparty/ea/EABase/config/eacompiler.h new file mode 100644 index 00000000..f10b7453 --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/config/eacompiler.h @@ -0,0 +1,1778 @@ +/*----------------------------------------------------------------------------- + * config/eacompiler.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *----------------------------------------------------------------------------- + * Currently supported defines include: + * EA_COMPILER_GNUC + * EA_COMPILER_ARM + * EA_COMPILER_EDG + * EA_COMPILER_SN + * EA_COMPILER_MSVC + * EA_COMPILER_METROWERKS + * EA_COMPILER_INTEL + * EA_COMPILER_BORLANDC + * EA_COMPILER_IBM + * EA_COMPILER_QNX + * EA_COMPILER_GREEN_HILLS + * EA_COMPILER_CLANG + * EA_COMPILER_CLANG_CL + * + * EA_COMPILER_VERSION = + * EA_COMPILER_NAME = + * EA_COMPILER_STRING = + * + * EA_COMPILER_VA_COPY_REQUIRED + * + * C++98/03 functionality + * EA_COMPILER_NO_STATIC_CONSTANTS + * EA_COMPILER_NO_TEMPLATE_SPECIALIZATION + * EA_COMPILER_NO_TEMPLATE_PARTIAL_SPECIALIZATION + * EA_COMPILER_NO_MEMBER_TEMPLATES + * EA_COMPILER_NO_MEMBER_TEMPLATE_SPECIALIZATION + * EA_COMPILER_NO_TEMPLATE_TEMPLATES + * EA_COMPILER_NO_MEMBER_TEMPLATE_FRIENDS + * EA_COMPILER_NO_VOID_RETURNS + * EA_COMPILER_NO_COVARIANT_RETURN_TYPE + * EA_COMPILER_NO_DEDUCED_TYPENAME + * EA_COMPILER_NO_ARGUMENT_DEPENDENT_LOOKUP + * EA_COMPILER_NO_EXCEPTION_STD_NAMESPACE + * EA_COMPILER_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS + * EA_COMPILER_NO_RTTI + * EA_COMPILER_NO_EXCEPTIONS + * EA_COMPILER_NO_NEW_THROW_SPEC + * EA_THROW_SPEC_NEW / EA_THROW_SPEC_DELETE + * EA_COMPILER_NO_UNWIND + * EA_COMPILER_NO_STANDARD_CPP_LIBRARY + * EA_COMPILER_NO_STATIC_VARIABLE_INIT + * EA_COMPILER_NO_STATIC_FUNCTION_INIT + * EA_COMPILER_NO_VARIADIC_MACROS + * + * C++11 functionality + * EA_COMPILER_NO_RVALUE_REFERENCES + * EA_COMPILER_NO_EXTERN_TEMPLATE + * EA_COMPILER_NO_RANGE_BASED_FOR_LOOP + * EA_COMPILER_NO_CONSTEXPR + * EA_COMPILER_NO_OVERRIDE + * EA_COMPILER_NO_INHERITANCE_FINAL + * EA_COMPILER_NO_NULLPTR + * EA_COMPILER_NO_AUTO + * EA_COMPILER_NO_DECLTYPE + * EA_COMPILER_NO_DEFAULTED_FUNCTIONS + * EA_COMPILER_NO_DELETED_FUNCTIONS + * EA_COMPILER_NO_LAMBDA_EXPRESSIONS + * EA_COMPILER_NO_TRAILING_RETURN_TYPES + * EA_COMPILER_NO_STRONGLY_TYPED_ENUMS + * EA_COMPILER_NO_FORWARD_DECLARED_ENUMS + * EA_COMPILER_NO_VARIADIC_TEMPLATES + * EA_COMPILER_NO_TEMPLATE_ALIASES + * EA_COMPILER_NO_INITIALIZER_LISTS + * EA_COMPILER_NO_NORETURN + * EA_COMPILER_NO_CARRIES_DEPENDENCY + * EA_COMPILER_NO_FALLTHROUGH + * EA_COMPILER_NO_NODISCARD + * EA_COMPILER_NO_MAYBE_UNUSED + * EA_COMPILER_NO_NONSTATIC_MEMBER_INITIALIZERS + * EA_COMPILER_NO_RIGHT_ANGLE_BRACKETS + * EA_COMPILER_NO_ALIGNOF + * EA_COMPILER_NO_ALIGNAS + * EA_COMPILER_NO_DELEGATING_CONSTRUCTORS + * EA_COMPILER_NO_INHERITING_CONSTRUCTORS + * EA_COMPILER_NO_USER_DEFINED_LITERALS + * EA_COMPILER_NO_STANDARD_LAYOUT_TYPES + * EA_COMPILER_NO_EXTENDED_SIZEOF + * EA_COMPILER_NO_INLINE_NAMESPACES + * EA_COMPILER_NO_UNRESTRICTED_UNIONS + * EA_COMPILER_NO_EXPLICIT_CONVERSION_OPERATORS + * EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS + * EA_COMPILER_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS + * EA_COMPILER_NO_NOEXCEPT + * EA_COMPILER_NO_RAW_LITERALS + * EA_COMPILER_NO_UNICODE_STRING_LITERALS + * EA_COMPILER_NO_NEW_CHARACTER_TYPES + * EA_COMPILER_NO_UNICODE_CHAR_NAME_LITERALS + * EA_COMPILER_NO_UNIFIED_INITIALIZATION_SYNTAX + * EA_COMPILER_NO_EXTENDED_FRIEND_DECLARATIONS + * + * C++14 functionality + * EA_COMPILER_NO_VARIABLE_TEMPLATES + * + * C++17 functionality + * EA_COMPILER_NO_INLINE_VARIABLES + * EA_COMPILER_NO_ALIGNED_NEW + * + * C++20 functionality + * EA_COMPILER_NO_DESIGNATED_INITIALIZERS + * + *----------------------------------------------------------------------------- + * + * Supplemental documentation + * EA_COMPILER_NO_STATIC_CONSTANTS + * Code such as this is legal, but some compilers fail to compile it: + * struct A{ static const a = 1; }; + * + * EA_COMPILER_NO_TEMPLATE_SPECIALIZATION + * Some compilers fail to allow template specialization, such as with this: + * template void DoSomething(U u); + * void DoSomething(int x); + * + * EA_COMPILER_NO_TEMPLATE_PARTIAL_SPECIALIZATION + * Some compilers fail to allow partial template specialization, such as with this: + * template class vector{ }; // Primary templated class. + * template class vector{ }; // Partially specialized version. + * + * EA_COMPILER_NO_MEMBER_TEMPLATES + * Some compilers fail to allow member template functions such as this: + * struct A{ template void DoSomething(U u); }; + * + * EA_COMPILER_NO_MEMBER_TEMPLATE_SPECIALIZATION + * Some compilers fail to allow member template specialization, such as with this: + * struct A{ + * template void DoSomething(U u); + * void DoSomething(int x); + * }; + * + * EA_COMPILER_NO_TEMPLATE_TEMPLATES + * Code such as this is legal: + * template class U> + * U SomeFunction(const U x) { return x.DoSomething(); } + * + * EA_COMPILER_NO_MEMBER_TEMPLATE_FRIENDS + * Some compilers fail to compile templated friends, as with this: + * struct A{ template friend class SomeFriend; }; + * This is described in the C++ Standard at 14.5.3. + * + * EA_COMPILER_NO_VOID_RETURNS + * This is legal C++: + * void DoNothing1(){ }; + * void DoNothing2(){ return DoNothing1(); } + * + * EA_COMPILER_NO_COVARIANT_RETURN_TYPE + * See the C++ standard sec 10.3,p5. + * + * EA_COMPILER_NO_DEDUCED_TYPENAME + * Some compilers don't support the use of 'typename' for + * dependent types in deduced contexts, as with this: + * template void Function(T, typename T::type); + * + * EA_COMPILER_NO_ARGUMENT_DEPENDENT_LOOKUP + * Also known as Koenig lookup. Basically, if you have a function + * that is a namespace and you call that function without prefixing + * it with the namespace the compiler should look at any arguments + * you pass to that function call and search their namespace *first* + * to see if the given function exists there. + * + * EA_COMPILER_NO_EXCEPTION_STD_NAMESPACE + * is in namespace std. Some std libraries fail to + * put the contents of in namespace std. The following + * code should normally be legal: + * void Function(){ std::terminate(); } + * + * EA_COMPILER_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS + * Some compilers fail to execute DoSomething() properly, though they + * succeed in compiling it, as with this: + * template + * bool DoSomething(int j){ return i == j; }; + * DoSomething<1>(2); + * + * EA_COMPILER_NO_EXCEPTIONS + * The compiler is configured to disallow the use of try/throw/catch + * syntax (often to improve performance). Use of such syntax in this + * case will cause a compilation error. + * + * EA_COMPILER_NO_UNWIND + * The compiler is configured to allow the use of try/throw/catch + * syntax and behaviour but disables the generation of stack unwinding + * code for responding to exceptions (often to improve performance). + * + *---------------------------------------------------------------------------*/ + +#ifndef INCLUDED_eacompiler_H +#define INCLUDED_eacompiler_H + + #include + + // Note: This is used to generate the EA_COMPILER_STRING macros + #ifndef INTERNAL_STRINGIZE + #define INTERNAL_STRINGIZE(x) INTERNAL_PRIMITIVE_STRINGIZE(x) + #endif + #ifndef INTERNAL_PRIMITIVE_STRINGIZE + #define INTERNAL_PRIMITIVE_STRINGIZE(x) #x + #endif + + // EA_COMPILER_HAS_FEATURE + #ifndef EA_COMPILER_HAS_FEATURE + #if defined(__clang__) + #define EA_COMPILER_HAS_FEATURE(x) __has_feature(x) + #else + #define EA_COMPILER_HAS_FEATURE(x) 0 + #endif + #endif + + + // EA_COMPILER_HAS_BUILTIN + #ifndef EA_COMPILER_HAS_BUILTIN + #if defined(__clang__) + #define EA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) + #else + #define EA_COMPILER_HAS_BUILTIN(x) 0 + #endif + #endif + + + // EDG (EDG compiler front-end, used by other compilers such as SN) + #if defined(__EDG_VERSION__) + #define EA_COMPILER_EDG 1 + + #if defined(_MSC_VER) + #define EA_COMPILER_EDG_VC_MODE 1 + #endif + #if defined(__GNUC__) + #define EA_COMPILER_EDG_GCC_MODE 1 + #endif + #endif + + // EA_COMPILER_WINRTCX_ENABLED + // + // Defined as 1 if the compiler has its available C++/CX support enabled, else undefined. + // This specifically means the corresponding compilation unit has been built with Windows Runtime + // Components enabled, usually via the '-ZW' compiler flags being used. This option allows for using + // ref counted hat-type '^' objects and other C++/CX specific keywords like "ref new" + #if !defined(EA_COMPILER_WINRTCX_ENABLED) && defined(__cplusplus_winrt) + #define EA_COMPILER_WINRTCX_ENABLED 1 + #endif + + + // EA_COMPILER_CPP11_ENABLED + // + // Defined as 1 if the compiler has its available C++11 support enabled, else undefined. + // This does not mean that all of C++11 or any particular feature of C++11 is supported + // by the compiler. It means that whatever C++11 support the compiler has is enabled. + // This also includes existing and older compilers that still identify C++11 as C++0x. + // + // We cannot use (__cplusplus >= 201103L) alone because some compiler vendors have + // decided to not define __cplusplus like thus until they have fully completed their + // C++11 support. + // + #if !defined(EA_COMPILER_CPP11_ENABLED) && defined(__cplusplus) + #if (__cplusplus >= 201103L) // Clang and GCC defines this like so in C++11 mode. + #define EA_COMPILER_CPP11_ENABLED 1 + #elif defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__) + #define EA_COMPILER_CPP11_ENABLED 1 + #elif defined(_MSC_VER) && _MSC_VER >= 1600 // Microsoft unilaterally enables its C++11 support; there is no way to disable it. + #define EA_COMPILER_CPP11_ENABLED 1 + #elif defined(__EDG_VERSION__) // && ??? + // To do: Is there a generic way to determine this? + #endif + #endif + + + // EA_COMPILER_CPP14_ENABLED + // + // Defined as 1 if the compiler has its available C++14 support enabled, else undefined. + // This does not mean that all of C++14 or any particular feature of C++14 is supported + // by the compiler. It means that whatever C++14 support the compiler has is enabled. + // + // We cannot use (__cplusplus >= 201402L) alone because some compiler vendors have + // decided to not define __cplusplus like thus until they have fully completed their + // C++14 support. + #if !defined(EA_COMPILER_CPP14_ENABLED) && defined(__cplusplus) + #if (__cplusplus >= 201402L) // Clang and GCC defines this like so in C++14 mode. + #define EA_COMPILER_CPP14_ENABLED 1 + #elif defined(_MSC_VER) && (_MSC_VER >= 1900) // VS2015+ + #define EA_COMPILER_CPP14_ENABLED 1 + #endif + #endif + + + // EA_COMPILER_CPP17_ENABLED + // + // Defined as 1 if the compiler has its available C++17 support enabled, else undefined. + // This does not mean that all of C++17 or any particular feature of C++17 is supported + // by the compiler. It means that whatever C++17 support the compiler has is enabled. + // + // We cannot use (__cplusplus >= 201703L) alone because some compiler vendors have + // decided to not define __cplusplus like thus until they have fully completed their + // C++17 support. + #if !defined(EA_COMPILER_CPP17_ENABLED) && defined(__cplusplus) + #if (__cplusplus >= 201703L) + #define EA_COMPILER_CPP17_ENABLED 1 + #elif defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L) // C++17+ + #define EA_COMPILER_CPP17_ENABLED 1 + #endif + #endif + + + // EA_COMPILER_CPP20_ENABLED + // + // Defined as 1 if the compiler has its available C++20 support enabled, else undefined. + // This does not mean that all of C++20 or any particular feature of C++20 is supported + // by the compiler. It means that whatever C++20 support the compiler has is enabled. + // + // We cannot use (__cplusplus >= 202003L) alone because some compiler vendors have + // decided to not define __cplusplus like thus until they have fully completed their + // C++20 support. + #if !defined(EA_COMPILER_CPP20_ENABLED) && defined(__cplusplus) + // TODO(rparoin): enable once a C++20 value for the __cplusplus macro has been published + // #if (__cplusplus >= 202003L) + // #define EA_COMPILER_CPP20_ENABLED 1 + // #elif defined(_MSVC_LANG) && (_MSVC_LANG >= 202003L) // C++20+ + // #define EA_COMPILER_CPP20_ENABLED 1 + // #endif + #endif + + + + #if defined(__ARMCC_VERSION) + // Note that this refers to the ARM RVCT compiler (armcc or armcpp), but there + // are other compilers that target ARM processors, such as GCC and Microsoft VC++. + // If you want to detect compiling for the ARM processor, check for EA_PROCESSOR_ARM + // being defined. + // This compiler is also identified by defined(__CC_ARM) || defined(__ARMCC__). + #define EA_COMPILER_RVCT 1 + #define EA_COMPILER_ARM 1 + #define EA_COMPILER_VERSION __ARMCC_VERSION + #define EA_COMPILER_NAME "RVCT" + //#define EA_COMPILER_STRING (defined below) + + // Clang's GCC-compatible driver. + #elif defined(__clang__) && !defined(_MSC_VER) + #define EA_COMPILER_CLANG 1 + #define EA_COMPILER_VERSION (__clang_major__ * 100 + __clang_minor__) + #define EA_COMPILER_NAME "clang" + #define EA_COMPILER_STRING EA_COMPILER_NAME __clang_version__ + + // GCC (a.k.a. GNUC) + #elif defined(__GNUC__) // GCC compilers exist for many platforms. + #define EA_COMPILER_GNUC 1 + #define EA_COMPILER_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) + #define EA_COMPILER_NAME "GCC" + #define EA_COMPILER_STRING EA_COMPILER_NAME " compiler, version " INTERNAL_STRINGIZE( __GNUC__ ) "." INTERNAL_STRINGIZE( __GNUC_MINOR__ ) + + #if (__GNUC__ == 2) && (__GNUC_MINOR__ < 95) // If GCC < 2.95... + #define EA_COMPILER_NO_MEMBER_TEMPLATES 1 + #endif + #if (__GNUC__ == 2) && (__GNUC_MINOR__ <= 97) // If GCC <= 2.97... + #define EA_COMPILER_NO_MEMBER_TEMPLATE_FRIENDS 1 + #endif + #if (__GNUC__ == 3) && ((__GNUC_MINOR__ == 1) || (__GNUC_MINOR__ == 2)) // If GCC 3.1 or 3.2 (but not pre 3.1 or post 3.2)... + #define EA_COMPILER_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS 1 + #endif + + // Borland C++ + #elif defined(__BORLANDC__) + #define EA_COMPILER_BORLANDC 1 + #define EA_COMPILER_VERSION __BORLANDC__ + #define EA_COMPILER_NAME "Borland C" + //#define EA_COMPILER_STRING (defined below) + + #if (__BORLANDC__ <= 0x0550) // If Borland C++ Builder 4 and 5... + #define EA_COMPILER_NO_MEMBER_TEMPLATE_FRIENDS 1 + #endif + #if (__BORLANDC__ >= 0x561) && (__BORLANDC__ < 0x600) + #define EA_COMPILER_NO_MEMBER_FUNCTION_SPECIALIZATION 1 + #endif + + + // Intel C++ + // The Intel Windows compiler masquerades as VC++ and defines _MSC_VER. + // The Intel compiler is based on the EDG compiler front-end. + #elif defined(__ICL) || defined(__ICC) + #define EA_COMPILER_INTEL 1 + + // Should we enable the following? We probably should do so since enabling it does a lot more good than harm + // for users. The Intel Windows compiler does a pretty good job of emulating VC++ and so the user would likely + // have to handle few special cases where the Intel compiler doesn't emulate VC++ correctly. + #if defined(_MSC_VER) + #define EA_COMPILER_MSVC 1 + #define EA_COMPILER_MICROSOFT 1 + #endif + + // Should we enable the following? This isn't as clear because as of this writing we don't know if the Intel + // compiler truly emulates GCC well enough that enabling this does more good than harm. + #if defined(__GNUC__) + #define EA_COMPILER_GNUC 1 + #endif + + #if defined(__ICL) + #define EA_COMPILER_VERSION __ICL + #elif defined(__ICC) + #define EA_COMPILER_VERSION __ICC + #endif + #define EA_COMPILER_NAME "Intel C++" + #if defined(_MSC_VER) + #define EA_COMPILER_STRING EA_COMPILER_NAME " compiler, version " INTERNAL_STRINGIZE( EA_COMPILER_VERSION ) ", EDG version " INTERNAL_STRINGIZE( __EDG_VERSION__ ) ", VC++ version " INTERNAL_STRINGIZE( _MSC_VER ) + #elif defined(__GNUC__) + #define EA_COMPILER_STRING EA_COMPILER_NAME " compiler, version " INTERNAL_STRINGIZE( EA_COMPILER_VERSION ) ", EDG version " INTERNAL_STRINGIZE( __EDG_VERSION__ ) ", GCC version " INTERNAL_STRINGIZE( __GNUC__ ) + #else + #define EA_COMPILER_STRING EA_COMPILER_NAME " compiler, version " INTERNAL_STRINGIZE( EA_COMPILER_VERSION ) ", EDG version " INTERNAL_STRINGIZE( __EDG_VERSION__ ) + #endif + + + #elif defined(_MSC_VER) + #define EA_COMPILER_MSVC 1 + #define EA_COMPILER_MICROSOFT 1 + #define EA_COMPILER_VERSION _MSC_VER + #define EA_COMPILER_NAME "Microsoft Visual C++" + //#define EA_COMPILER_STRING (defined below) + + #if defined(__clang__) + // Clang's MSVC-compatible driver. + #define EA_COMPILER_CLANG_CL 1 + #endif + + #define EA_STANDARD_LIBRARY_MSVC 1 + #define EA_STANDARD_LIBRARY_MICROSOFT 1 + + #if (_MSC_VER <= 1200) // If VC6.x and earlier... + #if (_MSC_VER < 1200) + #define EA_COMPILER_MSVCOLD 1 + #else + #define EA_COMPILER_MSVC6 1 + #endif + + #if (_MSC_VER < 1200) // If VC5.x or earlier... + #define EA_COMPILER_NO_TEMPLATE_SPECIALIZATION 1 + #endif + #define EA_COMPILER_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS 1 // The compiler compiles this OK, but executes it wrong. Fixed in VC7.0 + #define EA_COMPILER_NO_VOID_RETURNS 1 // The compiler fails to compile such cases. Fixed in VC7.0 + #define EA_COMPILER_NO_EXCEPTION_STD_NAMESPACE 1 // The compiler fails to compile such cases. Fixed in VC7.0 + #define EA_COMPILER_NO_DEDUCED_TYPENAME 1 // The compiler fails to compile such cases. Fixed in VC7.0 + #define EA_COMPILER_NO_STATIC_CONSTANTS 1 // The compiler fails to compile such cases. Fixed in VC7.0 + #define EA_COMPILER_NO_COVARIANT_RETURN_TYPE 1 // The compiler fails to compile such cases. Fixed in VC7.1 + #define EA_COMPILER_NO_ARGUMENT_DEPENDENT_LOOKUP 1 // The compiler compiles this OK, but executes it wrong. Fixed in VC7.1 + #define EA_COMPILER_NO_TEMPLATE_TEMPLATES 1 // The compiler fails to compile such cases. Fixed in VC7.1 + #define EA_COMPILER_NO_TEMPLATE_PARTIAL_SPECIALIZATION 1 // The compiler fails to compile such cases. Fixed in VC7.1 + #define EA_COMPILER_NO_MEMBER_TEMPLATE_FRIENDS 1 // The compiler fails to compile such cases. Fixed in VC7.1 + //#define EA_COMPILER_NO_MEMBER_TEMPLATES 1 // VC6.x supports member templates properly 95% of the time. So do we flag the remaining 5%? + //#define EA_COMPILER_NO_MEMBER_TEMPLATE_SPECIALIZATION 1 // VC6.x supports member templates properly 95% of the time. So do we flag the remaining 5%? + + #elif (_MSC_VER <= 1300) // If VC7.0 and earlier... + #define EA_COMPILER_MSVC7 1 + + #define EA_COMPILER_NO_COVARIANT_RETURN_TYPE 1 // The compiler fails to compile such cases. Fixed in VC7.1 + #define EA_COMPILER_NO_ARGUMENT_DEPENDENT_LOOKUP 1 // The compiler compiles this OK, but executes it wrong. Fixed in VC7.1 + #define EA_COMPILER_NO_TEMPLATE_TEMPLATES 1 // The compiler fails to compile such cases. Fixed in VC7.1 + #define EA_COMPILER_NO_TEMPLATE_PARTIAL_SPECIALIZATION 1 // The compiler fails to compile such cases. Fixed in VC7.1 + #define EA_COMPILER_NO_MEMBER_TEMPLATE_FRIENDS 1 // The compiler fails to compile such cases. Fixed in VC7.1 + #define EA_COMPILER_NO_MEMBER_FUNCTION_SPECIALIZATION 1 // This is the case only for VC7.0 and not VC6 or VC7.1+. Fixed in VC7.1 + //#define EA_COMPILER_NO_MEMBER_TEMPLATES 1 // VC7.0 supports member templates properly 95% of the time. So do we flag the remaining 5%? + + #elif (_MSC_VER < 1400) // VS2003 _MSC_VER of 1300 means VC7 (VS2003) + // The VC7.1 and later compiler is fairly close to the C++ standard + // and thus has no compiler limitations that we are concerned about. + #define EA_COMPILER_MSVC7_2003 1 + #define EA_COMPILER_MSVC7_1 1 + + #elif (_MSC_VER < 1500) // VS2005 _MSC_VER of 1400 means VC8 (VS2005) + #define EA_COMPILER_MSVC8_2005 1 + #define EA_COMPILER_MSVC8_0 1 + + #elif (_MSC_VER < 1600) // VS2008. _MSC_VER of 1500 means VC9 (VS2008) + #define EA_COMPILER_MSVC9_2008 1 + #define EA_COMPILER_MSVC9_0 1 + + #elif (_MSC_VER < 1700) // VS2010 _MSC_VER of 1600 means VC10 (VS2010) + #define EA_COMPILER_MSVC_2010 1 + #define EA_COMPILER_MSVC10_0 1 + + #elif (_MSC_VER < 1800) // VS2012 _MSC_VER of 1700 means VS2011/VS2012 + #define EA_COMPILER_MSVC_2011 1 // Microsoft changed the name to VS2012 before shipping, despite referring to it as VS2011 up to just a few weeks before shipping. + #define EA_COMPILER_MSVC11_0 1 + #define EA_COMPILER_MSVC_2012 1 + #define EA_COMPILER_MSVC12_0 1 + + #elif (_MSC_VER < 1900) // VS2013 _MSC_VER of 1800 means VS2013 + #define EA_COMPILER_MSVC_2013 1 + #define EA_COMPILER_MSVC13_0 1 + + #elif (_MSC_VER < 1910) // VS2015 _MSC_VER of 1900 means VS2015 + #define EA_COMPILER_MSVC_2015 1 + #define EA_COMPILER_MSVC14_0 1 + + #elif (_MSC_VER < 1911) // VS2017 _MSC_VER of 1910 means VS2017 + #define EA_COMPILER_MSVC_2017 1 + #define EA_COMPILER_MSVC15_0 1 + + #endif + + + // IBM + #elif defined(__xlC__) + #define EA_COMPILER_IBM 1 + #define EA_COMPILER_NAME "IBM XL C" + #define EA_COMPILER_VERSION __xlC__ + #define EA_COMPILER_STRING "IBM XL C compiler, version " INTERNAL_STRINGIZE( __xlC__ ) + + // Unknown + #else // Else the compiler is unknown + + #define EA_COMPILER_VERSION 0 + #define EA_COMPILER_NAME "Unknown" + + #endif + + #ifndef EA_COMPILER_STRING + #define EA_COMPILER_STRING EA_COMPILER_NAME " compiler, version " INTERNAL_STRINGIZE(EA_COMPILER_VERSION) + #endif + + + // Deprecated definitions + // For backwards compatibility, should be supported for at least the life of EABase v2.0.x. + #ifndef EA_COMPILER_NO_TEMPLATE_PARTIAL_SPECIALIZATION + #define EA_COMPILER_PARTIAL_TEMPLATE_SPECIALIZATION 1 + #endif + #ifndef EA_COMPILER_NO_TEMPLATE_SPECIALIZATION + #define EA_COMPILER_TEMPLATE_SPECIALIZATION 1 + #endif + #ifndef EA_COMPILER_NO_MEMBER_TEMPLATES + #define EA_COMPILER_MEMBER_TEMPLATES 1 + #endif + #ifndef EA_COMPILER_NO_MEMBER_TEMPLATE_SPECIALIZATION + #define EA_COMPILER_MEMBER_TEMPLATE_SPECIALIZATION 1 + #endif + + + + /////////////////////////////////////////////////////////////////////////////// + // EA_COMPILER_VA_COPY_REQUIRED + // + // Defines whether va_copy must be used to copy or save va_list objects between uses. + // Some compilers on some platforms implement va_list whereby its contents + // are destroyed upon usage, even if passed by value to another function. + // With these compilers you can use va_copy to save and restore a va_list. + // Known compiler/platforms that destroy va_list contents upon usage include: + // CodeWarrior on PowerPC + // GCC on x86-64 + // However, va_copy is part of the C99 standard and not part of earlier C and + // C++ standards. So not all compilers support it. VC++ doesn't support va_copy, + // but it turns out that VC++ doesn't usually need it on the platforms it supports, + // and va_copy can usually be implemented via memcpy(va_list, va_list) with VC++. + /////////////////////////////////////////////////////////////////////////////// + + #ifndef EA_COMPILER_VA_COPY_REQUIRED + #if ((defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__)) && (!defined(__i386__) || defined(__x86_64__)) && !defined(__ppc__) && !defined(__PPC__) && !defined(__PPC64__) + #define EA_COMPILER_VA_COPY_REQUIRED 1 + #endif + #endif + + + // EA_COMPILER_NO_RTTI + // + // If EA_COMPILER_NO_RTTI is defined, then RTTI (run-time type information) + // is not available (possibly due to being disabled by the user). + // + #if defined(__EDG_VERSION__) && !defined(__RTTI) + #define EA_COMPILER_NO_RTTI 1 + #elif defined(__clang__) && !EA_COMPILER_HAS_FEATURE(cxx_rtti) + #define EA_COMPILER_NO_RTTI 1 + #elif defined(__IBMCPP__) && !defined(__RTTI_ALL__) + #define EA_COMPILER_NO_RTTI 1 + #elif defined(__GXX_ABI_VERSION) && !defined(__GXX_RTTI) + #define EA_COMPILER_NO_RTTI 1 + #elif defined(_MSC_VER) && !defined(_CPPRTTI) + #define EA_COMPILER_NO_RTTI 1 + #elif defined(__ARMCC_VERSION) && defined(__TARGET_CPU_MPCORE) && !defined(__RTTI) + #define EA_COMPILER_NO_RTTI 1 + #endif + + + + // EA_COMPILER_NO_EXCEPTIONS / EA_COMPILER_NO_UNWIND + // + // If EA_COMPILER_NO_EXCEPTIONS is defined, then the compiler is + // configured to not recognize C++ exception-handling statements + // such as try/catch/throw. Thus, when EA_COMPILER_NO_EXCEPTIONS is + // defined, code that attempts to use exception handling statements + // will usually cause a compilation error. If is often desirable + // for projects to disable exception handling because exception + // handling causes extra code and/or data generation which might + // not be needed, especially if it is known that exceptions won't + // be happening. When writing code that is to be portable between + // systems of which some enable exception handling while others + // don't, check for EA_COMPILER_NO_EXCEPTIONS being defined. + // + #if !defined(EA_COMPILER_NO_EXCEPTIONS) && !defined(EA_COMPILER_NO_UNWIND) + #if defined(EA_COMPILER_GNUC) && defined(_NO_EX) // GCC on some platforms defines _NO_EX when exceptions are disabled. + #define EA_COMPILER_NO_EXCEPTIONS 1 + + #elif (defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_INTEL) || defined(EA_COMPILER_RVCT)) && !defined(__EXCEPTIONS) // GCC and most EDG-based compilers define __EXCEPTIONS when exception handling is enabled. + #define EA_COMPILER_NO_EXCEPTIONS 1 + + #elif (defined(EA_COMPILER_MSVC)) && !defined(_CPPUNWIND) + #define EA_COMPILER_NO_UNWIND 1 + + #endif // EA_COMPILER_NO_EXCEPTIONS / EA_COMPILER_NO_UNWIND + #endif // !defined(EA_COMPILER_NO_EXCEPTIONS) && !defined(EA_COMPILER_NO_UNWIND) + + + // ------------------------------------------------------------------------ + // EA_DISABLE_ALL_VC_WARNINGS / EA_RESTORE_ALL_VC_WARNINGS + // + // Disable and re-enable all warning(s) within code. + // + // Example usage: + // EA_DISABLE_ALL_VC_WARNINGS() + // + // EA_RESTORE_ALL_VC_WARNINGS() + // + //This is duplicated from EABase's eacompilertraits.h + #ifndef EA_DISABLE_ALL_VC_WARNINGS + #if defined(_MSC_VER) + #define EA_DISABLE_ALL_VC_WARNINGS() \ + __pragma(warning(push, 0)) \ + __pragma(warning(disable: 4244 4265 4267 4350 4472 4509 4548 4623 4710 4985 6320 4755 4625 4626 4702)) // Some warnings need to be explicitly called out. + #else + #define EA_DISABLE_ALL_VC_WARNINGS() + #endif + #endif + + //This is duplicated from EABase's eacompilertraits.h + #ifndef EA_RESTORE_ALL_VC_WARNINGS + #if defined(_MSC_VER) + #define EA_RESTORE_ALL_VC_WARNINGS() \ + __pragma(warning(pop)) + #else + #define EA_RESTORE_ALL_VC_WARNINGS() + #endif + #endif + + // Dinkumware + //This is duplicated from EABase's eahave.h + #if !defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && !defined(EA_NO_HAVE_DINKUMWARE_CPP_LIBRARY) + #if defined(__cplusplus) + EA_DISABLE_ALL_VC_WARNINGS() + #include // Need to trigger the compilation of yvals.h without directly using because it might not exist. + EA_RESTORE_ALL_VC_WARNINGS() + #endif + + #if defined(__cplusplus) && defined(_CPPLIB_VER) /* If using the Dinkumware Standard library... */ + #define EA_HAVE_DINKUMWARE_CPP_LIBRARY 1 + #else + #define EA_NO_HAVE_DINKUMWARE_CPP_LIBRARY 1 + #endif + #endif + + + // EA_COMPILER_NO_ALIGNED_NEW + // + // + #if !defined(EA_COMPILER_NO_ALIGNED_NEW) + #if defined(_HAS_ALIGNED_NEW) && _HAS_ALIGNED_NEW // VS2017 15.5 Preview + // supported. + #elif defined(EA_COMPILER_CPP17_ENABLED) + // supported. + #else + #define EA_COMPILER_NO_ALIGNED_NEW 1 + #endif + #endif + + // EA_COMPILER_NO_NEW_THROW_SPEC / EA_THROW_SPEC_NEW / EA_THROW_SPEC_DELETE + // + // If defined then the compiler's version of operator new is not decorated + // with a throw specification. This is useful for us to know because we + // often want to write our own overloaded operator new implementations. + // We need such operator new overrides to be declared identically to the + // way the compiler is defining operator new itself. + // + // Example usage: + // void* operator new(std::size_t) EA_THROW_SPEC_NEW(std::bad_alloc); + // void* operator new[](std::size_t) EA_THROW_SPEC_NEW(std::bad_alloc); + // void* operator new(std::size_t, const std::nothrow_t&) EA_THROW_SPEC_NEW_NONE(); + // void* operator new[](std::size_t, const std::nothrow_t&) EA_THROW_SPEC_NEW_NONE(); + // void operator delete(void*) EA_THROW_SPEC_DELETE_NONE(); + // void operator delete[](void*) EA_THROW_SPEC_DELETE_NONE(); + // void operator delete(void*, const std::nothrow_t&) EA_THROW_SPEC_DELETE_NONE(); + // void operator delete[](void*, const std::nothrow_t&) EA_THROW_SPEC_DELETE_NONE(); + // + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) + #if defined(_MSC_VER) && (_MSC_VER >= 1912) // VS2017 15.3+ + #define EA_THROW_SPEC_NEW(x) noexcept(false) + #define EA_THROW_SPEC_NEW_NONE() noexcept + #define EA_THROW_SPEC_DELETE_NONE() noexcept + + #elif defined(_MSC_VER) && (_MSC_VER >= 1910) // VS2017+ + #define EA_THROW_SPEC_NEW(x) throw(x) + #define EA_THROW_SPEC_NEW_NONE() throw() + #define EA_THROW_SPEC_DELETE_NONE() throw() + + #else + #if defined(EA_PLATFORM_SONY) + #define EA_THROW_SPEC_NEW(X) _THROWS(X) + #elif defined(_MSC_VER) + // Disabled warning "nonstandard extension used: 'throw (...)'" as this warning is a W4 warning which is usually off by default + // and doesn't convey any important information but will still complain when building with /Wall (which most teams do) + #define EA_THROW_SPEC_NEW(X) __pragma(warning(push)) __pragma(warning(disable: 4987)) _THROWS(X) __pragma(warning(pop)) + #else + #define EA_THROW_SPEC_NEW(X) _THROW1(X) + #endif + #define EA_THROW_SPEC_NEW_NONE() _THROW0() + #define EA_THROW_SPEC_DELETE_NONE() _THROW0() + + #endif + #elif defined(EA_COMPILER_NO_EXCEPTIONS) && !defined(EA_COMPILER_RVCT) && !defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_APPLE) && !defined(EA_PLATFORM_NX) + #define EA_COMPILER_NO_NEW_THROW_SPEC 1 + + #define EA_THROW_SPEC_NEW(x) + #define EA_THROW_SPEC_NEW_NONE() + #define EA_THROW_SPEC_DELETE_NONE() + #else + #define EA_THROW_SPEC_NEW(x) throw(x) + #define EA_THROW_SPEC_NEW_NONE() throw() + #define EA_THROW_SPEC_DELETE_NONE() throw() + #endif + + + // EA_COMPILER_NO_STANDARD_CPP_LIBRARY + // + // If defined, then the compiler doesn't provide a Standard C++ library. + // + #if defined(EA_PLATFORM_ANDROID) + // Disabled because EA's eaconfig/android_config/android_sdk packages currently + // don't support linking STL libraries. Perhaps we can figure out what linker arguments + // are needed for an app so we can manually specify them and then re-enable this code. + //#include + // + //#if (__ANDROID_API__ < 9) // Earlier versions of Android provide no std C++ STL implementation. + #define EA_COMPILER_NO_STANDARD_CPP_LIBRARY 1 + //#endif + #endif + + + // EA_COMPILER_NO_STATIC_VARIABLE_INIT + // + // If defined, it means that global or static C++ variables will be + // constructed. Not all compiler/platorm combinations support this. + // User code that needs to be portable must avoid having C++ variables + // that construct before main. + // + //#if defined(EA_PLATFORM_MOBILE) + // #define EA_COMPILER_NO_STATIC_VARIABLE_INIT 1 + //#endif + + + // EA_COMPILER_NO_STATIC_FUNCTION_INIT + // + // If defined, it means that functions marked as startup functions + // (e.g. __attribute__((constructor)) in GCC) are supported. It may + // be that some compiler/platform combinations don't support this. + // + //#if defined(XXX) // So far, all compiler/platforms we use support this. + // #define EA_COMPILER_NO_STATIC_VARIABLE_INIT 1 + //#endif + + // EA_COMPILER_NO_VARIADIC_MACROS + // + // If defined, the compiler doesn't support C99/C++11 variadic macros. + // With a variadic macro, you can do this: + // #define MY_PRINTF(format, ...) printf(format, __VA_ARGS__) + // + #if !defined(EA_COMPILER_NO_VARIADIC_MACROS) + #if defined(_MSC_VER) && (_MSC_VER < 1500) // If earlier than VS2008.. + #define EA_COMPILER_NO_VARIADIC_MACROS 1 + #elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__)) < 401 // If earlier than GCC 4.1.. + #define EA_COMPILER_NO_VARIADIC_MACROS 1 + #elif defined(EA_COMPILER_EDG) // Includes other compilers + // variadic macros are supported + #endif + #endif + + + // EA_COMPILER_NO_RVALUE_REFERENCES + // + // If defined, the compiler doesn't fully support C++11 rvalue reference semantics. + // This applies to the compiler only and not the Standard Library in use with the compiler, + // which is required by the Standard to have some support itself. + // + #if !defined(EA_COMPILER_NO_RVALUE_REFERENCES) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (_MSC_VER >= 1600) // VS2010+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403) // EDG 4.3+. + // supported. Earlier EDG supported a subset of rvalue references. Implicit move constructors and assignment operators aren't supported until EDG 4.5. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && EA_COMPILER_HAS_FEATURE(cxx_rvalue_references) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_RVALUE_REFERENCES 1 + #endif + #endif + + + // EA_COMPILER_NO_EXTERN_TEMPLATE + // + // If defined, the compiler doesn't support C++11 extern template. + // With extern templates, you can do this: + // extern template void DoSomething(KnownType u); + // + #if !defined(EA_COMPILER_NO_EXTERN_TEMPLATE) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (_MSC_VER >= 1700) // VS2012+... + // Extern template is supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && defined(__apple_build_version__) && (EA_COMPILER_VERSION >= 401) + // Extern template is supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && !defined(__apple_build_version__) // Clang other than Apple's Clang + // Extern template is supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4006) // GCC 4.6+ + // Extern template is supported. + #else + #define EA_COMPILER_NO_EXTERN_TEMPLATE 1 + #endif + #endif + + + // EA_COMPILER_NO_RANGE_BASED_FOR_LOOP + // + // If defined, the compiler doesn't support C++11 range-based for loops. + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2930.html + // You must #include for range-based for loops to work. + // Example usage: + // #include + // #include + // std::vector floatVector; + // for(float& f : floatVector) + // f += 1.0; + // + #if !defined(EA_COMPILER_NO_RANGE_BASED_FOR_LOOP) + #if defined(EA_COMPILER_CPP11_ENABLED) && (defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1700)) // VS2012+... + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 405) // EDG 4.5+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && (defined(__clang__) && (EA_COMPILER_VERSION >= 300)) // Clang 3.x+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && (defined(__GNUC__) && (EA_COMPILER_VERSION >= 4006)) // GCC 4.6+ + // supported. + #else + #define EA_COMPILER_NO_RANGE_BASED_FOR_LOOP 1 + #endif + #endif + + + // EA_COMPILER_NO_CONSTEXPR + // + // Refers to C++11 = constexpr (const expression) declarations. + // + #if !defined(EA_COMPILER_NO_CONSTEXPR) + #if defined(EA_COMPILER_CPP11_ENABLED) && (defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1900)) // VS2015+... Not present in VC++ up to and including VS2013. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 406) // EDG 4.6+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && EA_COMPILER_HAS_FEATURE(cxx_constexpr) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4006) // GCC 4.6+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1900) // VS 2015+ + // supported. + #else + #define EA_COMPILER_NO_CONSTEXPR 1 + #endif + #endif + + + // EA_COMPILER_NO_CONSTEXPR_IF + // + // Refers to C++17 = constexpr if(const expression) conditionals. + // + #if !defined(EA_COMPILER_NO_CONSTEXPR_IF) + #if defined(EA_COMPILER_CPP17_ENABLED) && (defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1911)) // VS2017 15.3+ + // supported. + #elif defined(EA_COMPILER_CPP17_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 309) // Clang 3.9+ + // supported. + #elif defined(EA_COMPILER_CPP17_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 7000) // GCC 7+ + // supported. + #else + #define EA_COMPILER_NO_CONSTEXPR_IF 1 + #endif + #endif + + + // EA_COMPILER_NO_OVERRIDE + // + // Refers to the C++11 override specifier. + // + #ifndef EA_COMPILER_NO_OVERRIDE + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION > 1600) // VC++ > VS2010, even without C++11 support. VS2010 does support override, however will generate warnings due to the keyword being 'non-standard' + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4007) // GCC 4.7+ + // supported. + #else + #define EA_COMPILER_NO_OVERRIDE 1 + #endif + #endif + + + // EA_COMPILER_NO_INHERITANCE_FINAL + // + // Refers to the C++11 final specifier. + // + #ifndef EA_COMPILER_NO_INHERITANCE_FINAL + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1500) // VS2008+, even without C++11 support. + // supported, though you need to use EA_INHERITANCE_FINAL for it to work with VS versions prior to 2012. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+ + // supported + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4007) // GCC 4.7+ + // supported + #else + #define EA_COMPILER_NO_INHERITANCE_FINAL 1 + #endif + #endif + + + // EA_COMPILER_NO_AUTO + // + // Refers to C++11 auto. + // + #if !defined(EA_COMPILER_NO_AUTO) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1600) // VS2010+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported with the exception of the usage of braced initializer lists as of EDG 4.3. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + #define EA_COMPILER_NO_AUTO 1 + #endif + #endif + + + // EA_COMPILER_NO_NULLPTR + // + // Refers to C++11 nullptr (which is a built in type). std::nullptr_t is defined in C++11 . + // Note that implements a portable nullptr implementation. + // + #if !defined(EA_COMPILER_NO_NULLPTR) + #if (defined(_MSC_VER) && (_MSC_VER >= 1600)) && defined(EA_COMPILER_CPP11_ENABLED) + // supported + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) && defined(EA_COMPILER_CPP11_ENABLED) + // supported + #elif defined(__clang__) && defined(EA_COMPILER_CPP11_ENABLED) + // supported + #elif defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403) && defined(EA_COMPILER_CPP11_ENABLED) + // supported + #else + #define EA_COMPILER_NO_NULLPTR 1 + #endif + #endif + + + // EA_COMPILER_NO_DECLTYPE + // + // Refers to C++11 decltype. + // + #if !defined(EA_COMPILER_NO_DECLTYPE) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1600) // VS2010+ + // supported, though VS2010 doesn't support the spec completely as specified in the final standard. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4003) // GCC 4.3+ + // supported. + #else + #define EA_COMPILER_NO_DECLTYPE 1 + #endif + #endif + + + + // EA_COMPILER_NO_DEFAULTED_FUNCTIONS + // EA_COMPILER_NO_DELETED_FUNCTIONS + // + // Refers to C++11 = default and = delete function declarations. + // + #if !defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+ + // supported, but as of VS2013 it isn't supported for defaulted move constructors and move assignment operators. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported, but as of EDG 4.3 it isn't supported for defaulted move constructors and move assignment operators until EDG 4.5. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) // Clang 3.0+, including Apple's Clang + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + // VC++ doesn't support it as of VS2012. + #define EA_COMPILER_NO_DEFAULTED_FUNCTIONS 1 + #endif + #endif + + #if !defined(EA_COMPILER_NO_DELETED_FUNCTIONS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+ + // supported, but as of VS2013 it isn't supported for defaulted move constructors and move assignment operators. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + // VC++ doesn't support it as of VS2012. + #define EA_COMPILER_NO_DELETED_FUNCTIONS 1 + #endif + #endif + + + // EA_COMPILER_NO_LAMBDA_EXPRESSIONS + // + // Refers to C++11 lambda expressions. + // + #if !defined(EA_COMPILER_NO_LAMBDA_EXPRESSIONS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1600) // VS2010+ + // supported, though VS2010 doesn't support the spec completely as specified in the final standard. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported. However, converting lambdas to function pointers is not supported until EDG 4.5. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + #define EA_COMPILER_NO_LAMBDA_EXPRESSIONS 1 + #endif + #endif + + + // EA_COMPILER_NO_TRAILING_RETURN_TYPES + // + // Refers to C++11 trailing-return-type. Also sometimes referred to as "incomplete return type". + // + #if !defined(EA_COMPILER_NO_TRAILING_RETURN_TYPES) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1600) // VS2010+ + // supported, though VS2010 doesn't support the spec completely as specified in the final standard. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 402) // EDG 4.2+. + // supported. However, use of "this" in trailing return types is not supported untiil EDG 4.4 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + #define EA_COMPILER_NO_TRAILING_RETURN_TYPES 1 + #endif + #endif + + + // EA_COMPILER_NO_STRONGLY_TYPED_ENUMS + // + // Refers to C++11 strongly typed enums, which includes enum classes and sized enums. Doesn't include forward-declared enums. + // + #if !defined(EA_COMPILER_NO_STRONGLY_TYPED_ENUMS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1700) // VS2012+ + // supported. A subset of this is actually supported by VS2010. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 400) // EDG 4.0+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + #define EA_COMPILER_NO_STRONGLY_TYPED_ENUMS 1 + #endif + #endif + + + // EA_COMPILER_NO_FORWARD_DECLARED_ENUMS + // + // Refers to C++11 forward declared enums. + // + #if !defined(EA_COMPILER_NO_FORWARD_DECLARED_ENUMS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1700) // VS2012+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 405) // EDG 4.5+. + // supported. EDG 4.3 supports basic forward-declared enums, but not forward-declared strongly typed enums. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4006) // GCC 4.6+ + // supported. + #else + #define EA_COMPILER_NO_FORWARD_DECLARED_ENUMS 1 + #endif + #endif + + + // EA_COMPILER_NO_VARIADIC_TEMPLATES + // + // Refers to C++11 variadic templates. + // + #if !defined(EA_COMPILER_NO_VARIADIC_TEMPLATES) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (_MSC_FULL_VER == 170051025) // VS2012 November Preview for Windows only. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403) // EDG 4.3+. + // supported, though 4.1 has partial support for variadic templates. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported, though GCC 4.3 has partial support for variadic templates. + #else + #define EA_COMPILER_NO_VARIADIC_TEMPLATES 1 + #endif + #endif + + + // EA_COMPILER_NO_TEMPLATE_ALIASES + // + // Refers to C++11 alias templates. + // Example alias template usage: + // template + // using Dictionary = eastl::map; + // + // Dictionary StringIntDictionary; + // + #if !defined(EA_COMPILER_NO_TEMPLATE_ALIASES) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 402) // EDG 4.2+. + // supported, though 4.1 has partial support for variadic templates. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4007) // GCC 4.7+ + // supported, though GCC 4.3 has partial support for variadic templates. + #else + #define EA_COMPILER_NO_TEMPLATE_ALIASES 1 + #endif + #endif + + + // EA_COMPILER_NO_VARIABLE_TEMPLATES + // + // Refers to C++14 variable templates. + // Example variable template usage: + // template + // constexpr T pi = T(3.1415926535897932385); + // + #if !defined(EA_COMPILER_NO_VARIABLE_TEMPLATES) + #if defined(_MSC_VER) && (_MSC_FULL_VER >= 190023918) // VS2015 Update 2 and above. + // supported. + #elif defined(EA_COMPILER_CPP14_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 304) && !defined(__apple_build_version__) // Clang 3.4+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP14_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 5000) // GCC 5+ + // supported. + #elif !defined(EA_COMPILER_CPP14_ENABLED) + #define EA_COMPILER_NO_VARIABLE_TEMPLATES 1 + #endif + #endif + + + // EA_COMPILER_NO_INLINE_VARIABLES + // + // Refers to C++17 inline variables that allows the definition of variables in header files + // + // Example usage: + // struct Foo + // { + // static inline constexpr int kConstant = 42; // no out of class definition + // }; + // + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4424.pdf + // http://en.cppreference.com/w/cpp/language/inline + // + #if !defined(EA_COMPILER_NO_INLINE_VARIABLES) + #define EA_COMPILER_NO_INLINE_VARIABLES 1 + #endif + + + // EA_COMPILER_NO_INITIALIZER_LISTS + // + // Refers to C++11 initializer lists. + // This refers to the compiler support for this and not the Standard Library support (std::initializer_list). + // + #if !defined(EA_COMPILER_NO_INITIALIZER_LISTS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (_MSC_FULL_VER == 170051025) // VS2012 November Preview for Windows only. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 405) // EDG 4.5+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported, though GCC 4.3 has partial support for it. + #else + #define EA_COMPILER_NO_INITIALIZER_LISTS 1 + #endif + #endif + + + // EA_COMPILER_NO_NORETURN + // + // Refers to C++11 declaration attribute: noreturn. + // http://en.cppreference.com/w/cpp/language/attributes + // http://blog.aaronballman.com/2011/09/understanding-attributes/ + // + #if !defined(EA_COMPILER_NO_NORETURN) + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1300) // VS2003+ + // supported via __declspec(noreturn). You need to use that or EA_NORETURN. VC++ up to VS2013 doesn't support any C++11 attribute types. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 402) // EDG 4.2+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4008) // GCC 4.8+ + // supported. + #else + #define EA_COMPILER_NO_NORETURN 1 + #endif + #endif + + + // EA_COMPILER_NO_CARRIES_DEPENDENCY + // + // Refers to C++11 declaration attribute: carries_dependency. + // http://en.cppreference.com/w/cpp/language/attributes + // http://blog.aaronballman.com/2011/09/understanding-attributes/ + // + #if !defined(EA_COMPILER_NO_CARRIES_DEPENDENCY) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 402) // EDG 4.2+. + // supported; stricter than other compilers in its usage. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + // Currently GNUC doesn't appear to support this attribute. + //#elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4008) // GCC 4.8+ + // // supported. + #else + #define EA_COMPILER_NO_CARRIES_DEPENDENCY 1 + #endif + #endif + + + // EA_COMPILER_NO_FALLTHROUGH + // + // Refers to C++17 declaration attribute: fallthrough. + // http://en.cppreference.com/w/cpp/language/attributes + // + #if !defined(EA_COMPILER_NO_FALLTHROUGH) + #if defined(EA_COMPILER_CPP17_ENABLED) + // supported. + #else + #define EA_COMPILER_NO_FALLTHROUGH 1 + #endif + #endif + + + // EA_COMPILER_NO_NODISCARD + // + // Refers to C++17 declaration attribute: nodiscard. + // http://en.cppreference.com/w/cpp/language/attributes + // + #if !defined(EA_COMPILER_NO_NODISCARD) + #if defined(EA_COMPILER_CPP17_ENABLED) + // supported. + #else + #define EA_COMPILER_NO_NODISCARD 1 + #endif + #endif + + + // EA_COMPILER_NO_MAYBE_UNUSED + // + // Refers to C++17 declaration attribute: maybe_unused. + // http://en.cppreference.com/w/cpp/language/attributes + // + #if !defined(EA_COMPILER_NO_MAYBE_UNUSED) + #if defined(EA_COMPILER_CPP17_ENABLED) + // supported. + #elif defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1912) // VS2017 15.3+ + // supported. + #else + #define EA_COMPILER_NO_MAYBE_UNUSED 1 + #endif + #endif + + + // EA_COMPILER_NO_STRUCTURED_BINDING + // + // Indicates if target compiler supports the C++17 "structured binding" language feature. + // https://en.cppreference.com/w/cpp/language/structured_binding + // + // + #if !defined(EA_COMPILER_NO_STRUCTURED_BINDING) + #if defined(EA_COMPILER_CPP17_ENABLED) + // supported. + #elif defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1912) // VS2017 15.3+ + // supported. + #else + #define EA_COMPILER_NO_STRUCTURED_BINDING 1 + #endif + #endif + + + // EA_COMPILER_NO_DESIGNATED_INITIALIZERS + // + // Indicates the target compiler supports the C++20 "designated initializer" language feature. + // https://en.cppreference.com/w/cpp/language/aggregate_initialization + // + // Example: + // struct A { int x; int y; }; + // A a = { .y = 42, .x = 1 }; + // + #if !defined(EA_COMPILER_NO_DESIGNATED_INITIALIZERS) + #if defined(EA_COMPILER_CPP20_ENABLED) + // supported. + #else + #define EA_COMPILER_NO_DESIGNATED_INITIALIZERS 1 + #endif + #endif + + + // EA_COMPILER_NO_NONSTATIC_MEMBER_INITIALIZERS + // + // Refers to C++11 declaration attribute: carries_dependency. + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2756.htm + // + #if !defined(EA_COMPILER_NO_NONSTATIC_MEMBER_INITIALIZERS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4007) // GCC 4.7+ + // supported. + #else + #define EA_COMPILER_NO_NONSTATIC_MEMBER_INITIALIZERS 1 + #endif + #endif + + + // EA_COMPILER_NO_RIGHT_ANGLE_BRACKETS + // + // Defines if the compiler supports >> (as opposed to > >) in template + // declarations such as typedef eastl::list> ListList; + // + #if !defined(EA_COMPILER_NO_RIGHT_ANGLE_BRACKETS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1600) // VS2010+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4003) // GCC 4.3+ + // supported. + #else + #define EA_COMPILER_NO_RIGHT_ANGLE_BRACKETS 1 + #endif + #endif + + + // EA_COMPILER_NO_ALIGNOF + // + // Refers specifically to C++11 alignof and not old compiler extensions such as __alignof__(). + // However, EABase provides a portable EA_ALIGN_OF which works for all compilers. + // + #if !defined(EA_COMPILER_NO_ALIGNOF) + // Not supported by VC++ as of VS2013, though EA_ALIGN_OF is supported on all coompilers as an alternative. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_ALIGNOF 1 + #endif + #endif + + + // EA_COMPILER_NO_ALIGNAS + // + // Refers to C++11 alignas. + // + #if !defined(EA_COMPILER_NO_ALIGNAS) + // Not supported by VC++ as of VS2013. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4008) // GCC 4.8+ + // supported. + #else + #define EA_COMPILER_NO_ALIGNAS 1 + #endif + #endif + + + // EA_COMPILER_NO_DELEGATING_CONSTRUCTORS + // + // Refers to C++11 constructor delegation. + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf + // https://www.ibm.com/developerworks/mydeveloperworks/blogs/5894415f-be62-4bc0-81c5-3956e82276f3/entry/c_0x_delegating_constructors + // + #if !defined(EA_COMPILER_NO_DELEGATING_CONSTRUCTORS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 407) // EDG 4.7+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4007) // GCC 4.7+ + // supported. + #else + #define EA_COMPILER_NO_DELEGATING_CONSTRUCTORS 1 + #endif + #endif + + + // EA_COMPILER_NO_INHERITING_CONSTRUCTORS + // + // Refers to C++11 constructor inheritance via 'using'. + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2540.htm + // + #if !defined(EA_COMPILER_NO_INHERITING_CONSTRUCTORS) + // Not supported by VC++ as of VS2013. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && EA_COMPILER_HAS_FEATURE(cxx_inheriting_constructors) // Clang + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4008) // GCC 4.8+ + // supported. + #else + #define EA_COMPILER_NO_INHERITING_CONSTRUCTORS 1 + #endif + #endif + + + // EA_COMPILER_NO_USER_DEFINED_LITERALS + // + // http://en.cppreference.com/w/cpp/language/user_literal + // http://stackoverflow.com/questions/237804/what-new-capabilities-do-user-defined-literals-add-to-c + // + #if !defined(EA_COMPILER_NO_USER_DEFINED_LITERALS) + // Not supported by VC++ as of VS2013. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4007) // GCC 4.7+ + // supported. + #else + #define EA_COMPILER_NO_USER_DEFINED_LITERALS 1 + #endif + #endif + + + // EA_COMPILER_NO_STANDARD_LAYOUT_TYPES + // a.k.a. POD relaxation + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm + // + #if !defined(EA_COMPILER_NO_STANDARD_LAYOUT_TYPES) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1700) // VS2012+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_STANDARD_LAYOUT_TYPES 1 + #endif + #endif + + + // EA_COMPILER_NO_EXTENDED_SIZEOF + // + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2253.html + // Allows you to do this: sizeof(SomeClass::mSomeMember) + // + #if !defined(EA_COMPILER_NO_EXTENDED_SIZEOF) + // Not supported by VC++ as of VS2013. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + // Versions of EDG prior to 4.5 only support extended sizeof in non-member functions. Full support was added in 4.5 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 405) // EDG 4.5+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_EXTENDED_SIZEOF 1 + #endif + #endif + + + // EA_COMPILER_NO_INLINE_NAMESPACES + // + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2535.htm + // http://blog.aaronballman.com/2011/07/inline-namespaces/ + // + #if !defined(EA_COMPILER_NO_INLINE_NAMESPACES) + // Not supported by VC++ as of VS2013. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 405) // EDG 4.5+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + #define EA_COMPILER_NO_INLINE_NAMESPACES 1 + #endif + #endif + + + // EA_COMPILER_NO_UNRESTRICTED_UNIONS + // + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + // + #if !defined(EA_COMPILER_NO_UNRESTRICTED_UNIONS) + // Not supported by VC++ as of VS2013. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 406) // EDG 4.6+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4006) // GCC 4.6+ + // supported. + #else + #define EA_COMPILER_NO_UNRESTRICTED_UNIONS 1 + #endif + #endif + + + // EA_COMPILER_NO_EXPLICIT_CONVERSION_OPERATORS + // + // http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_conversion_operators + // + #if !defined(EA_COMPILER_NO_EXPLICIT_CONVERSION_OPERATORS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (_MSC_FULL_VER == 170051025) // VS2012 November Preview for Windows only. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 404) // EDG 4.4+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_EXPLICIT_CONVERSION_OPERATORS 1 + #endif + #endif + + + // EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS + // + // The compiler does not support default template arguments for function templates. + // http://stackoverflow.com/questions/2447458/default-template-arguments-for-function-templates + // + #if !defined(EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403) // EDG 4.4+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4003) // GCC 4.3+ + // supported. + #else + #define EA_COMPILER_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS 1 + #endif + #endif + + + // EA_COMPILER_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS + // + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm + // http://stackoverflow.com/questions/5751977/local-type-as-template-arguments-in-c + // + #if !defined(EA_COMPILER_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1600) // VS2010+ + // supported. + #if (EA_COMPILER_VERSION < 1700) // VS2010 generates a warning, but the C++ language now allows it. + #pragma warning(disable: 4836) // nonstandard extension used: local types or unnamed types cannot be used as template arguments. + #endif + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 402) // EDG 4.2+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_LOCAL_CLASS_TEMPLATE_PARAMETERS 1 + #endif + #endif + + + // EA_COMPILER_NO_NOEXCEPT + // + // C++11 noexcept + // http://en.cppreference.com/w/cpp/language/attributes + // http://en.cppreference.com/w/cpp/language/noexcept + // + #if !defined(EA_COMPILER_NO_NOEXCEPT) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1900) // VS2014+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 405) // EDG 4.5+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4006) // GCC 4.6+ + // supported. + #else + #define EA_COMPILER_NO_NOEXCEPT 1 + #endif + #endif + + + // EA_COMPILER_NO_RAW_LITERALS + // + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm + // http://en.wikipedia.org/wiki/C%2B%2B11#New_string_literals + // + #if !defined(EA_COMPILER_NO_RAW_LITERALS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 407) // EDG 4.7+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_RAW_LITERALS 1 + #endif + #endif + + + // EA_COMPILER_NO_UNICODE_STRING_LITERALS + // + // http://en.wikipedia.org/wiki/C%2B%2B11#New_string_literals + // + #if !defined(EA_COMPILER_NO_UNICODE_STRING_LITERALS) + // Not supported by VC++ as of VS2013. + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 407) // EDG 4.7+. + // supported. It's not clear if it's v4.4 or v4.7 that adds this support. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 300) && !defined(__apple_build_version__) // Clang 3.0+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 407) // EDG 4.7+. + // supported. It's not clear if it's v4.4 or v4.7 that adds this support. + #else + #define EA_COMPILER_NO_UNICODE_STRING_LITERALS 1 + #endif + #endif + + + // EA_COMPILER_NO_NEW_CHARACTER_TYPES + // + // Refers to char16_t and char32_t as true native types (and not something simply typedef'd from uint16_t and uint32_t). + // http://en.cppreference.com/w/cpp/language/types + // + #if !defined(EA_COMPILER_NO_NEW_CHARACTER_TYPES) + #if defined(EA_COMPILER_NO_UNICODE_STRING_LITERALS) // Some compilers have had support for char16_t prior to support for u"", but it's not useful to have the former without the latter. + #define EA_COMPILER_NO_NEW_CHARACTER_TYPES 1 + #endif + #endif + + + // EA_COMPILER_NO_UNICODE_CHAR_NAME_LITERALS + // + // C++ 11 relaxed \u\U sequences in strings. + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2170.html + // + #if !defined(EA_COMPILER_NO_UNICODE_CHAR_NAME_LITERALS) + // VC++ up till at least VS2013 supports \u and \U but supports them wrong with respect to the C++11 Standard. + + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4005) // GCC 4.5+ + // supported. + #else + #define EA_COMPILER_NO_UNICODE_CHAR_NAME_LITERALS 1 + #endif + #endif + + + // EA_COMPILER_NO_UNIFIED_INITIALIZATION_SYNTAX + // + // http://en.wikipedia.org/wiki/C%2B%2B11#Uniform_initialization + // + #if !defined(EA_COMPILER_NO_UNIFIED_INITIALIZATION_SYNTAX) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1800) // VS2013+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 401) && defined(__apple_build_version__) // Apple clang 4.1+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 301) && !defined(__apple_build_version__) // Clang 3.1+, not including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4004) // GCC 4.4+ + // supported. + #else + #define EA_COMPILER_NO_UNIFIED_INITIALIZATION_SYNTAX 1 + #endif + #endif + + + // EA_COMPILER_NO_EXTENDED_FRIEND_DECLARATIONS + // + // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1791.pdf + // + #if !defined(EA_COMPILER_NO_EXTENDED_FRIEND_DECLARATIONS) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1600) // VS2010+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) // EDG 4.1+. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && (EA_COMPILER_VERSION >= 209) // Clang 2.9+, including Apple's Clang. + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4007) // GCC 4.7+ + // supported. + #else + #define EA_COMPILER_NO_EXTENDED_FRIEND_DECLARATIONS 1 + #endif + #endif + + + // EA_COMPILER_NO_THREAD_LOCAL + // + // Refers specifically to C++ thread_local, which is like compiler __thread implementations except + // that it also supports non-trivial classes (e.g. with ctors). EA_COMPILER_NO_THREAD_LOCAL refers + // specifically to full C++11 thread_local support. The EAThread package provides a wrapper for + // __thread via EA_THREAD_LOCAL (which unfortunately sounds like C++ thread_local). + // + // https://en.cppreference.com/w/cpp/keyword/thread_local + // + #if !defined(EA_COMPILER_NO_THREAD_LOCAL) + #if defined(EA_COMPILER_CPP11_ENABLED) && defined(__clang__) && EA_COMPILER_HAS_FEATURE(cxx_thread_local) + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(_MSC_VER) && (EA_COMPILER_VERSION >= 1900) // VS2015+ + // supported. + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(__GNUC__) && (EA_COMPILER_VERSION >= 4008) // GCC 4.8+ + // supported. + #else + #define EA_COMPILER_NO_THREAD_LOCAL 1 + #endif + #endif + + +#endif // INCLUDED_eacompiler_H + + + + + diff --git a/r5dev/thirdparty/ea/EABase/config/eacompilertraits.h b/r5dev/thirdparty/ea/EABase/config/eacompilertraits.h new file mode 100644 index 00000000..2a196835 --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/config/eacompilertraits.h @@ -0,0 +1,2603 @@ +/*----------------------------------------------------------------------------- + * config/eacompilertraits.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *----------------------------------------------------------------------------- + * Currently supported defines include: + * EA_PREPROCESSOR_JOIN + * + * EA_COMPILER_IS_ANSIC + * EA_COMPILER_IS_C99 + * EA_COMPILER_IS_C11 + * EA_COMPILER_HAS_C99_TYPES + * EA_COMPILER_IS_CPLUSPLUS + * EA_COMPILER_MANAGED_CPP + * EA_COMPILER_INTMAX_SIZE + * EA_OFFSETOF + * EA_SIZEOF_MEMBER + * + * EA_ALIGN_OF() + * EA_ALIGN_MAX_STATIC / EA_ALIGN_MAX_AUTOMATIC + * EA_ALIGN() / EA_PREFIX_ALIGN() / EA_POSTFIX_ALIGN() + * EA_ALIGNED() + * EA_PACKED() + * + * EA_LIKELY() + * EA_UNLIKELY() + * EA_INIT_PRIORITY() + * EA_MAY_ALIAS() + * EA_ASSUME() + * EA_ANALYSIS_ASSUME() + * EA_PURE + * EA_WEAK + * EA_UNUSED() + * EA_EMPTY() + * + * EA_WCHAR_T_NON_NATIVE + * EA_WCHAR_SIZE = + * + * EA_RESTRICT + * EA_DEPRECATED / EA_PREFIX_DEPRECATED / EA_POSTFIX_DEPRECATED + * EA_FORCE_INLINE / EA_PREFIX_FORCE_INLINE / EA_POSTFIX_FORCE_INLINE + * EA_NO_INLINE / EA_PREFIX_NO_INLINE / EA_POSTFIX_NO_INLINE + * EA_NO_VTABLE / EA_CLASS_NO_VTABLE / EA_STRUCT_NO_VTABLE + * EA_PASCAL + * EA_PASCAL_FUNC() + * EA_SSE = [0 | 1] + * EA_IMPORT + * EA_EXPORT + * EA_PRAGMA_ONCE_SUPPORTED + * EA_ONCE + * EA_OVERRIDE + * EA_INHERITANCE_FINAL + * EA_SEALED + * EA_ABSTRACT + * EA_CONSTEXPR / EA_CONSTEXPR_OR_CONST + * EA_CONSTEXPR_IF + * EA_EXTERN_TEMPLATE + * EA_NOEXCEPT + * EA_NORETURN + * EA_CARRIES_DEPENDENCY + * EA_NON_COPYABLE / struct EANonCopyable + * EA_OPTIMIZE_OFF / EA_OPTIMIZE_ON + * EA_SIGNED_RIGHT_SHIFT_IS_UNSIGNED + * + * EA_DISABLE_VC_WARNING / EA_RESTORE_VC_WARNING / EA_DISABLE_ALL_VC_WARNINGS / EA_RESTORE_ALL_VC_WARNINGS + * EA_DISABLE_GCC_WARNING / EA_RESTORE_GCC_WARNING + * EA_DISABLE_CLANG_WARNING / EA_RESTORE_CLANG_WARNING + * EA_DISABLE_SN_WARNING / EA_RESTORE_SN_WARNING / EA_DISABLE_ALL_SN_WARNINGS / EA_RESTORE_ALL_SN_WARNINGS + * EA_DISABLE_GHS_WARNING / EA_RESTORE_GHS_WARNING + * EA_DISABLE_EDG_WARNING / EA_RESTORE_EDG_WARNING + * EA_DISABLE_CW_WARNING / EA_RESTORE_CW_WARNING + * + * EA_DISABLE_DEFAULT_CTOR + * EA_DISABLE_COPY_CTOR + * EA_DISABLE_MOVE_CTOR + * EA_DISABLE_ASSIGNMENT_OPERATOR + * EA_DISABLE_MOVE_OPERATOR + * + * Todo: + * Find a way to reliably detect wchar_t size at preprocessor time and + * implement it below for EA_WCHAR_SIZE. + * + * Todo: + * Find out how to support EA_PASCAL and EA_PASCAL_FUNC for systems in + * which it hasn't yet been found out for. + *---------------------------------------------------------------------------*/ + + +#ifndef INCLUDED_eacompilertraits_H +#define INCLUDED_eacompilertraits_H + + #include + #include + + + // Metrowerks uses #defines in its core C header files to define + // the kind of information we need below (e.g. C99 compatibility) + + + + // Determine if this compiler is ANSI C compliant and if it is C99 compliant. + #if defined(__STDC__) + #define EA_COMPILER_IS_ANSIC 1 // The compiler claims to be ANSI C + + // Is the compiler a C99 compiler or equivalent? + // From ISO/IEC 9899:1999: + // 6.10.8 Predefined macro names + // __STDC_VERSION__ The integer constant 199901L. (150) + // + // 150) This macro was not specified in ISO/IEC 9899:1990 and was + // specified as 199409L in ISO/IEC 9899/AMD1:1995. The intention + // is that this will remain an integer constant of type long int + // that is increased with each revision of this International Standard. + // + #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #define EA_COMPILER_IS_C99 1 + #endif + + // Is the compiler a C11 compiler? + // From ISO/IEC 9899:2011: + // Page 176, 6.10.8.1 (Predefined macro names) : + // __STDC_VERSION__ The integer constant 201112L. (178) + // + #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) + #define EA_COMPILER_IS_C11 1 + #endif + #endif + + // Some compilers (e.g. GCC) define __USE_ISOC99 if they are not + // strictly C99 compilers (or are simply C++ compilers) but are set + // to use C99 functionality. Metrowerks defines _MSL_C99 as 1 in + // this case, but 0 otherwise. + #if (defined(__USE_ISOC99) || (defined(_MSL_C99) && (_MSL_C99 == 1))) && !defined(EA_COMPILER_IS_C99) + #define EA_COMPILER_IS_C99 1 + #endif + + // Metrowerks defines C99 types (e.g. intptr_t) instrinsically when in C99 mode (-lang C99 on the command line). + #if (defined(_MSL_C99) && (_MSL_C99 == 1)) + #define EA_COMPILER_HAS_C99_TYPES 1 + #endif + + #if defined(__GNUC__) + #if (((__GNUC__ * 100) + __GNUC_MINOR__) >= 302) // Also, GCC defines _HAS_C9X. + #define EA_COMPILER_HAS_C99_TYPES 1 // The compiler is not necessarily a C99 compiler, but it defines C99 types. + + #ifndef __STDC_LIMIT_MACROS + #define __STDC_LIMIT_MACROS 1 + #endif + + #ifndef __STDC_CONSTANT_MACROS + #define __STDC_CONSTANT_MACROS 1 // This tells the GCC compiler that we want it to use its native C99 types. + #endif + #endif + #endif + + #if defined(_MSC_VER) && (_MSC_VER >= 1600) + #define EA_COMPILER_HAS_C99_TYPES 1 + #endif + + #ifdef __cplusplus + #define EA_COMPILER_IS_CPLUSPLUS 1 + #endif + + + // ------------------------------------------------------------------------ + // EA_PREPROCESSOR_JOIN + // + // This macro joins the two arguments together, even when one of + // the arguments is itself a macro (see 16.3.1 in C++98 standard). + // This is often used to create a unique name with __LINE__. + // + // For example, this declaration: + // char EA_PREPROCESSOR_JOIN(unique_, __LINE__); + // expands to this: + // char unique_73; + // + // Note that all versions of MSVC++ up to at least version 7.1 + // fail to properly compile macros that use __LINE__ in them + // when the "program database for edit and continue" option + // is enabled. The result is that __LINE__ gets converted to + // something like __LINE__(Var+37). + // + #ifndef EA_PREPROCESSOR_JOIN + #define EA_PREPROCESSOR_JOIN(a, b) EA_PREPROCESSOR_JOIN1(a, b) + #define EA_PREPROCESSOR_JOIN1(a, b) EA_PREPROCESSOR_JOIN2(a, b) + #define EA_PREPROCESSOR_JOIN2(a, b) a##b + #endif + + + // ------------------------------------------------------------------------ + // EA_STRINGIFY + // + // Example usage: + // printf("Line: %s", EA_STRINGIFY(__LINE__)); + // + #ifndef EA_STRINGIFY + #define EA_STRINGIFY(x) EA_STRINGIFYIMPL(x) + #define EA_STRINGIFYIMPL(x) #x + #endif + + + // ------------------------------------------------------------------------ + // EA_IDENTITY + // + #ifndef EA_IDENTITY + #define EA_IDENTITY(x) x + #endif + + + // ------------------------------------------------------------------------ + // EA_COMPILER_MANAGED_CPP + // Defined if this is being compiled with Managed C++ extensions + #ifdef EA_COMPILER_MSVC + #if EA_COMPILER_VERSION >= 1300 + #ifdef _MANAGED + #define EA_COMPILER_MANAGED_CPP 1 + #endif + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_COMPILER_INTMAX_SIZE + // + // This is related to the concept of intmax_t uintmax_t, but is available + // in preprocessor form as opposed to compile-time form. At compile-time + // you can use intmax_t and uintmax_t to use the actual types. + // + #if defined(__GNUC__) && defined(__x86_64__) + #define EA_COMPILER_INTMAX_SIZE 16 // intmax_t is __int128_t (GCC extension) and is 16 bytes. + #else + #define EA_COMPILER_INTMAX_SIZE 8 // intmax_t is int64_t and is 8 bytes. + #endif + + + + // ------------------------------------------------------------------------ + // EA_LPAREN / EA_RPAREN / EA_COMMA / EA_SEMI + // + // These are used for using special characters in macro-using expressions. + // Note that this macro intentionally uses (), as in some cases it can't + // work unless it does. + // + // Example usage: + // int x = SOME_MACRO(SomeTemplate); + // + #ifndef EA_LPAREN + #define EA_LPAREN() ( + #endif + #ifndef EA_RPAREN + #define EA_RPAREN() ) + #endif + #ifndef EA_COMMA + #define EA_COMMA() , + #endif + #ifndef EA_SEMI + #define EA_SEMI() ; + #endif + + + + + // ------------------------------------------------------------------------ + // EA_OFFSETOF + // Implements a portable version of the non-standard offsetof macro. + // + // The offsetof macro is guaranteed to only work with POD types. However, we wish to use + // it for non-POD types but where we know that offsetof will still work for the cases + // in which we use it. GCC unilaterally gives a warning when using offsetof with a non-POD, + // even if the given usage happens to work. So we make a workaround version of offsetof + // here for GCC which has the same effect but tricks the compiler into not issuing the warning. + // The 65536 does the compiler fooling; the reinterpret_cast prevents the possibility of + // an overloaded operator& for the class getting in the way. + // + // Example usage: + // struct A{ int x; int y; }; + // size_t n = EA_OFFSETOF(A, y); + // + #if defined(__GNUC__) // We can't use GCC 4's __builtin_offsetof because it mistakenly complains about non-PODs that are really PODs. + #define EA_OFFSETOF(struct_, member_) ((size_t)(((uintptr_t)&reinterpret_cast((((struct_*)65536)->member_))) - 65536)) + #else + #define EA_OFFSETOF(struct_, member_) offsetof(struct_, member_) + #endif + + // ------------------------------------------------------------------------ + // EA_SIZEOF_MEMBER + // Implements a portable way to determine the size of a member. + // + // The EA_SIZEOF_MEMBER simply returns the size of a member within a class or struct; member + // access rules still apply. We offer two approaches depending on the compiler's support for non-static member + // initializers although most C++11 compilers support this. + // + // Example usage: + // struct A{ int x; int y; }; + // size_t n = EA_SIZEOF_MEMBER(A, y); + // + #ifndef EA_COMPILER_NO_EXTENDED_SIZEOF + #define EA_SIZEOF_MEMBER(struct_, member_) (sizeof(struct_::member_)) + #else + #define EA_SIZEOF_MEMBER(struct_, member_) (sizeof(((struct_*)0)->member_)) + #endif + + // ------------------------------------------------------------------------ + // alignment expressions + // + // Here we define + // EA_ALIGN_OF(type) // Returns size_t. + // EA_ALIGN_MAX_STATIC // The max align value that the compiler will respect for EA_ALIGN for static data (global and static variables). Some compilers allow high values, some allow no more than 8. EA_ALIGN_MIN is assumed to be 1. + // EA_ALIGN_MAX_AUTOMATIC // The max align value for automatic variables (variables declared as local to a function). + // EA_ALIGN(n) // Used as a prefix. n is byte alignment, with being a power of two. Most of the time you can use this and avoid using EA_PREFIX_ALIGN/EA_POSTFIX_ALIGN. + // EA_ALIGNED(t, v, n) // Type, variable, alignment. Used to align an instance. You should need this only for unusual compilers. + // EA_PACKED // Specifies that the given structure be packed (and not have its members aligned). + // + // Also we define the following for rare cases that it's needed. + // EA_PREFIX_ALIGN(n) // n is byte alignment, with being a power of two. You should need this only for unusual compilers. + // EA_POSTFIX_ALIGN(n) // Valid values for n are 1, 2, 4, 8, etc. You should need this only for unusual compilers. + // + // Example usage: + // size_t x = EA_ALIGN_OF(int); Non-aligned equivalents. Meaning + // EA_PREFIX_ALIGN(8) int x = 5; int x = 5; Align x on 8 for compilers that require prefix attributes. Can just use EA_ALIGN instead. + // EA_ALIGN(8) int x; int x; Align x on 8 for compilers that allow prefix attributes. + // int x EA_POSTFIX_ALIGN(8); int x; Align x on 8 for compilers that require postfix attributes. + // int x EA_POSTFIX_ALIGN(8) = 5; int x = 5; Align x on 8 for compilers that require postfix attributes. + // int x EA_POSTFIX_ALIGN(8)(5); int x(5); Align x on 8 for compilers that require postfix attributes. + // struct EA_PREFIX_ALIGN(8) X { int x; } EA_POSTFIX_ALIGN(8); struct X { int x; }; Define X as a struct which is aligned on 8 when used. + // EA_ALIGNED(int, x, 8) = 5; int x = 5; Align x on 8. + // EA_ALIGNED(int, x, 16)(5); int x(5); Align x on 16. + // EA_ALIGNED(int, x[3], 16); int x[3]; Align x array on 16. + // EA_ALIGNED(int, x[3], 16) = { 1, 2, 3 }; int x[3] = { 1, 2, 3 }; Align x array on 16. + // int x[3] EA_PACKED; int x[3]; Pack the 3 ints of the x array. GCC doesn't seem to support packing of int arrays. + // struct EA_ALIGN(32) X { int x; int y; }; struct X { int x; }; Define A as a struct which is aligned on 32 when used. + // EA_ALIGN(32) struct X { int x; int y; } Z; struct X { int x; } Z; Define A as a struct, and align the instance Z on 32. + // struct X { int x EA_PACKED; int y EA_PACKED; }; struct X { int x; int y; }; Pack the x and y members of struct X. + // struct X { int x; int y; } EA_PACKED; struct X { int x; int y; }; Pack the members of struct X. + // typedef EA_ALIGNED(int, int16, 16); int16 n16; typedef int int16; int16 n16; Define int16 as an int which is aligned on 16. + // typedef EA_ALIGNED(X, X16, 16); X16 x16; typedef X X16; X16 x16; Define X16 as an X which is aligned on 16. + + #if !defined(EA_ALIGN_MAX) // If the user hasn't globally set an alternative value... + #if defined(EA_PROCESSOR_ARM) // ARM compilers in general tend to limit automatic variables to 8 or less. + #define EA_ALIGN_MAX_STATIC 1048576 + #define EA_ALIGN_MAX_AUTOMATIC 1 // Typically they support only built-in natural aligment types (both arm-eabi and apple-abi). + #elif defined(EA_PLATFORM_APPLE) + #define EA_ALIGN_MAX_STATIC 1048576 + #define EA_ALIGN_MAX_AUTOMATIC 16 + #else + #define EA_ALIGN_MAX_STATIC 1048576 // Arbitrarily high value. What is the actual max? + #define EA_ALIGN_MAX_AUTOMATIC 1048576 + #endif + #endif + + // EDG intends to be compatible with GCC but has a bug whereby it + // fails to support calling a constructor in an aligned declaration when + // using postfix alignment attributes. Prefix works for alignment, but does not align + // the size like postfix does. Prefix also fails on templates. So gcc style post fix + // is still used, but the user will need to use EA_POSTFIX_ALIGN before the constructor parameters. + #if defined(__GNUC__) && (__GNUC__ < 3) + #define EA_ALIGN_OF(type) ((size_t)__alignof__(type)) + #define EA_ALIGN(n) + #define EA_PREFIX_ALIGN(n) + #define EA_POSTFIX_ALIGN(n) __attribute__((aligned(n))) + #define EA_ALIGNED(variable_type, variable, n) variable_type variable __attribute__((aligned(n))) + #define EA_PACKED __attribute__((packed)) + + // GCC 3.x+, IBM, and clang support prefix attributes. + #elif (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__xlC__) || defined(__clang__) + #define EA_ALIGN_OF(type) ((size_t)__alignof__(type)) + #define EA_ALIGN(n) __attribute__((aligned(n))) + #define EA_PREFIX_ALIGN(n) + #define EA_POSTFIX_ALIGN(n) __attribute__((aligned(n))) + #define EA_ALIGNED(variable_type, variable, n) variable_type variable __attribute__((aligned(n))) + #define EA_PACKED __attribute__((packed)) + + // Metrowerks supports prefix attributes. + // Metrowerks does not support packed alignment attributes. + #elif defined(EA_COMPILER_INTEL) || defined(EA_PLATFORM_XBOX) || (defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1300)) + #define EA_ALIGN_OF(type) ((size_t)__alignof(type)) + #define EA_ALIGN(n) __declspec(align(n)) + #define EA_PREFIX_ALIGN(n) EA_ALIGN(n) + #define EA_POSTFIX_ALIGN(n) + #define EA_ALIGNED(variable_type, variable, n) EA_ALIGN(n) variable_type variable + #define EA_PACKED // See EA_PRAGMA_PACK_VC for an alternative. + + // Arm brand compiler + #elif defined(EA_COMPILER_ARM) + #define EA_ALIGN_OF(type) ((size_t)__ALIGNOF__(type)) + #define EA_ALIGN(n) __align(n) + #define EA_PREFIX_ALIGN(n) __align(n) + #define EA_POSTFIX_ALIGN(n) + #define EA_ALIGNED(variable_type, variable, n) __align(n) variable_type variable + #define EA_PACKED __packed + + #else // Unusual compilers + // There is nothing we can do about some of these. This is not as bad a problem as it seems. + // If the given platform/compiler doesn't support alignment specifications, then it's somewhat + // likely that alignment doesn't matter for that platform. Otherwise they would have defined + // functionality to manipulate alignment. + #define EA_ALIGN(n) + #define EA_PREFIX_ALIGN(n) + #define EA_POSTFIX_ALIGN(n) + #define EA_ALIGNED(variable_type, variable, n) variable_type variable + #define EA_PACKED + + #ifdef __cplusplus + template struct EAAlignOf1 { enum { s = sizeof (T), value = s ^ (s & (s - 1)) }; }; + template struct EAAlignOf2; + template struct helper { template struct Val { enum { value = size_diff }; }; }; + template <> struct helper<0> { template struct Val { enum { value = EAAlignOf2::value }; }; }; + template struct EAAlignOf2 { struct Big { T x; char c; }; + enum { diff = sizeof (Big) - sizeof (T), value = helper::template Val::value }; }; + template struct EAAlignof3 { enum { x = EAAlignOf2::value, y = EAAlignOf1::value, value = x < y ? x : y }; }; + #define EA_ALIGN_OF(type) ((size_t)EAAlignof3::value) + + #else + // C implementation of EA_ALIGN_OF + // This implementation works for most cases, but doesn't directly work + // for types such as function pointer declarations. To work with those + // types you need to typedef the type and then use the typedef in EA_ALIGN_OF. + #define EA_ALIGN_OF(type) ((size_t)offsetof(struct { char c; type m; }, m)) + #endif + #endif + + // EA_PRAGMA_PACK_VC + // + // Wraps #pragma pack in a way that allows for cleaner code. + // + // Example usage: + // EA_PRAGMA_PACK_VC(push, 1) + // struct X{ char c; int i; }; + // EA_PRAGMA_PACK_VC(pop) + // + #if !defined(EA_PRAGMA_PACK_VC) + #if defined(EA_COMPILER_MSVC) + #define EA_PRAGMA_PACK_VC(...) __pragma(pack(__VA_ARGS__)) + #elif !defined(EA_COMPILER_NO_VARIADIC_MACROS) + #define EA_PRAGMA_PACK_VC(...) + #else + // No support. However, all compilers of significance to us support variadic macros. + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_LIKELY / EA_UNLIKELY + // + // Defined as a macro which gives a hint to the compiler for branch + // prediction. GCC gives you the ability to manually give a hint to + // the compiler about the result of a comparison, though it's often + // best to compile shipping code with profiling feedback under both + // GCC (-fprofile-arcs) and VC++ (/LTCG:PGO, etc.). However, there + // are times when you feel very sure that a boolean expression will + // usually evaluate to either true or false and can help the compiler + // by using an explicity directive... + // + // Example usage: + // if(EA_LIKELY(a == 0)) // Tell the compiler that a will usually equal 0. + // { ... } + // + // Example usage: + // if(EA_UNLIKELY(a == 0)) // Tell the compiler that a will usually not equal 0. + // { ... } + // + #ifndef EA_LIKELY + #if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) + #if defined(__cplusplus) + #define EA_LIKELY(x) __builtin_expect(!!(x), true) + #define EA_UNLIKELY(x) __builtin_expect(!!(x), false) + #else + #define EA_LIKELY(x) __builtin_expect(!!(x), 1) + #define EA_UNLIKELY(x) __builtin_expect(!!(x), 0) + #endif + #else + #define EA_LIKELY(x) (x) + #define EA_UNLIKELY(x) (x) + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_HAS_INCLUDE_AVAILABLE + // + // Used to guard against the EA_HAS_INCLUDE() macro on compilers that do not + // support said feature. + // + // Example usage: + // + // #if EA_HAS_INCLUDE_AVAILABLE + // #if EA_HAS_INCLUDE("myinclude.h") + // #include "myinclude.h" + // #endif + // #endif + #if !defined(EA_HAS_INCLUDE_AVAILABLE) + #if defined(EA_COMPILER_CPP17_ENABLED) || defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC) + #define EA_HAS_INCLUDE_AVAILABLE 1 + #else + #define EA_HAS_INCLUDE_AVAILABLE 0 + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_HAS_INCLUDE + // + // May be used in #if and #elif expressions to test for the existence + // of the header referenced in the operand. If possible it evaluates to a + // non-zero value and zero otherwise. The operand is the same form as the file + // in a #include directive. + // + // Example usage: + // + // #if EA_HAS_INCLUDE("myinclude.h") + // #include "myinclude.h" + // #endif + // + // #if EA_HAS_INCLUDE() + // #include + // #endif + + #if !defined(EA_HAS_INCLUDE) + #if defined(EA_COMPILER_CPP17_ENABLED) + #define EA_HAS_INCLUDE(x) __has_include(x) + #elif defined(EA_COMPILER_CLANG) + #define EA_HAS_INCLUDE(x) __has_include(x) + #elif defined(EA_COMPILER_GNUC) + #define EA_HAS_INCLUDE(x) __has_include(x) + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_INIT_PRIORITY_AVAILABLE + // + // This value is either not defined, or defined to 1. + // Defines if the GCC attribute init_priority is supported by the compiler. + // + #if !defined(EA_INIT_PRIORITY_AVAILABLE) + #if defined(__GNUC__) && !defined(__EDG__) // EDG typically #defines __GNUC__ but doesn't implement init_priority. + #define EA_INIT_PRIORITY_AVAILABLE 1 + #elif defined(__clang__) + #define EA_INIT_PRIORITY_AVAILABLE 1 // Clang implements init_priority + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_INIT_PRIORITY + // + // This is simply a wrapper for the GCC init_priority attribute that allows + // multiplatform code to be easier to read. This attribute doesn't apply + // to VC++ because VC++ uses file-level pragmas to control init ordering. + // + // Example usage: + // SomeClass gSomeClass EA_INIT_PRIORITY(2000); + // + #if !defined(EA_INIT_PRIORITY) + #if defined(EA_INIT_PRIORITY_AVAILABLE) + #define EA_INIT_PRIORITY(x) __attribute__ ((init_priority (x))) + #else + #define EA_INIT_PRIORITY(x) + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_INIT_SEG_AVAILABLE + // + // + #if !defined(EA_INIT_SEG_AVAILABLE) + #if defined(_MSC_VER) + #define EA_INIT_SEG_AVAILABLE 1 + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_INIT_SEG + // + // Specifies a keyword or code section that affects the order in which startup code is executed. + // + // https://docs.microsoft.com/en-us/cpp/preprocessor/init-seg?view=vs-2019 + // + // Example: + // EA_INIT_SEG(compiler) MyType gMyTypeGlobal; + // EA_INIT_SEG("my_section") MyOtherType gMyOtherTypeGlobal; + // + #if !defined(EA_INIT_SEG) + #if defined(EA_INIT_SEG_AVAILABLE) + #define EA_INIT_SEG(x) \ + __pragma(warning(push)) __pragma(warning(disable : 4074)) __pragma(warning(disable : 4075)) __pragma(init_seg(x)) \ + __pragma(warning(pop)) + #else + #define EA_INIT_SEG(x) + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_MAY_ALIAS_AVAILABLE + // + // Defined as 0, 1, or 2. + // Defines if the GCC attribute may_alias is supported by the compiler. + // Consists of a value 0 (unsupported, shouldn't be used), 1 (some support), + // or 2 (full proper support). + // + #ifndef EA_MAY_ALIAS_AVAILABLE + #if defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 303) + #if !defined(__EDG__) // define it as 1 while defining GCC's support as 2. + #define EA_MAY_ALIAS_AVAILABLE 2 + #else + #define EA_MAY_ALIAS_AVAILABLE 0 + #endif + #else + #define EA_MAY_ALIAS_AVAILABLE 0 + #endif + #endif + + + // EA_MAY_ALIAS + // + // Defined as a macro that wraps the GCC may_alias attribute. This attribute + // has no significance for VC++ because VC++ doesn't support the concept of + // strict aliasing. Users should avoid writing code that breaks strict + // aliasing rules; EA_MAY_ALIAS is for cases with no alternative. + // + // Example usage: + // void* EA_MAY_ALIAS gPtr = NULL; + // + // Example usage: + // typedef void* EA_MAY_ALIAS pvoid_may_alias; + // pvoid_may_alias gPtr = NULL; + // + #if EA_MAY_ALIAS_AVAILABLE + #define EA_MAY_ALIAS __attribute__((__may_alias__)) + #else + #define EA_MAY_ALIAS + #endif + + + // ------------------------------------------------------------------------ + // EA_ASSUME + // + // This acts the same as the VC++ __assume directive and is implemented + // simply as a wrapper around it to allow portable usage of it and to take + // advantage of it if and when it appears in other compilers. + // + // Example usage: + // void Function(int a) { + // switch(a) { + // case 1: + // DoSomething(1); + // break; + // case 2: + // DoSomething(-1); + // break; + // default: + // EA_ASSUME(0); // This tells the optimizer that the default cannot be reached. + // } + // } + // + #ifndef EA_ASSUME + #if defined(_MSC_VER) && (_MSC_VER >= 1300) // If VC7.0 and later + #define EA_ASSUME(x) __assume(x) + #else + #define EA_ASSUME(x) + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_ANALYSIS_ASSUME + // + // This acts the same as the VC++ __analysis_assume directive and is implemented + // simply as a wrapper around it to allow portable usage of it and to take + // advantage of it if and when it appears in other compilers. + // + // Example usage: + // char Function(char* p) { + // EA_ANALYSIS_ASSUME(p != NULL); + // return *p; + // } + // + #ifndef EA_ANALYSIS_ASSUME + #if defined(_MSC_VER) && (_MSC_VER >= 1300) // If VC7.0 and later + #define EA_ANALYSIS_ASSUME(x) __analysis_assume(!!(x)) // !! because that allows for convertible-to-bool in addition to bool. + #else + #define EA_ANALYSIS_ASSUME(x) + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_DISABLE_VC_WARNING / EA_RESTORE_VC_WARNING + // + // Disable and re-enable warning(s) within code. + // This is simply a wrapper for VC++ #pragma warning(disable: nnnn) for the + // purpose of making code easier to read due to avoiding nested compiler ifdefs + // directly in code. + // + // Example usage: + // EA_DISABLE_VC_WARNING(4127 3244) + // + // EA_RESTORE_VC_WARNING() + // + #ifndef EA_DISABLE_VC_WARNING + #if defined(_MSC_VER) + #define EA_DISABLE_VC_WARNING(w) \ + __pragma(warning(push)) \ + __pragma(warning(disable:w)) + #else + #define EA_DISABLE_VC_WARNING(w) + #endif + #endif + + #ifndef EA_RESTORE_VC_WARNING + #if defined(_MSC_VER) + #define EA_RESTORE_VC_WARNING() \ + __pragma(warning(pop)) + #else + #define EA_RESTORE_VC_WARNING() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_ENABLE_VC_WARNING_AS_ERROR / EA_DISABLE_VC_WARNING_AS_ERROR + // + // Disable and re-enable treating a warning as error within code. + // This is simply a wrapper for VC++ #pragma warning(error: nnnn) for the + // purpose of making code easier to read due to avoiding nested compiler ifdefs + // directly in code. + // + // Example usage: + // EA_ENABLE_VC_WARNING_AS_ERROR(4996) + // + // EA_DISABLE_VC_WARNING_AS_ERROR() + // + #ifndef EA_ENABLE_VC_WARNING_AS_ERROR + #if defined(_MSC_VER) + #define EA_ENABLE_VC_WARNING_AS_ERROR(w) \ + __pragma(warning(push)) \ + __pragma(warning(error:w)) + #else + #define EA_ENABLE_VC_WARNING_AS_ERROR(w) + #endif + #endif + + #ifndef EA_DISABLE_VC_WARNING_AS_ERROR + #if defined(_MSC_VER) + #define EA_DISABLE_VC_WARNING_AS_ERROR() \ + __pragma(warning(pop)) + #else + #define EA_DISABLE_VC_WARNING_AS_ERROR() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_GCC_WARNING / EA_RESTORE_GCC_WARNING + // + // Example usage: + // // Only one warning can be ignored per statement, due to how GCC works. + // EA_DISABLE_GCC_WARNING(-Wuninitialized) + // EA_DISABLE_GCC_WARNING(-Wunused) + // + // EA_RESTORE_GCC_WARNING() + // EA_RESTORE_GCC_WARNING() + // + #ifndef EA_DISABLE_GCC_WARNING + #if defined(EA_COMPILER_GNUC) + #define EAGCCWHELP0(x) #x + #define EAGCCWHELP1(x) EAGCCWHELP0(GCC diagnostic ignored x) + #define EAGCCWHELP2(x) EAGCCWHELP1(#x) + #endif + + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) // Can't test directly for __GNUC__ because some compilers lie. + #define EA_DISABLE_GCC_WARNING(w) \ + _Pragma("GCC diagnostic push") \ + _Pragma(EAGCCWHELP2(w)) + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) + #define EA_DISABLE_GCC_WARNING(w) \ + _Pragma(EAGCCWHELP2(w)) + #else + #define EA_DISABLE_GCC_WARNING(w) + #endif + #endif + + #ifndef EA_RESTORE_GCC_WARNING + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) + #define EA_RESTORE_GCC_WARNING() \ + _Pragma("GCC diagnostic pop") + #else + #define EA_RESTORE_GCC_WARNING() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_ALL_GCC_WARNINGS / EA_RESTORE_ALL_GCC_WARNINGS + // + // This isn't possible except via using _Pragma("GCC system_header"), though + // that has some limitations in how it works. Another means is to manually + // disable individual warnings within a GCC diagnostic push statement. + // GCC doesn't have as many warnings as VC++ and EDG and so this may be feasible. + // ------------------------------------------------------------------------ + + + // ------------------------------------------------------------------------ + // EA_ENABLE_GCC_WARNING_AS_ERROR / EA_DISABLE_GCC_WARNING_AS_ERROR + // + // Example usage: + // // Only one warning can be treated as an error per statement, due to how GCC works. + // EA_ENABLE_GCC_WARNING_AS_ERROR(-Wuninitialized) + // EA_ENABLE_GCC_WARNING_AS_ERROR(-Wunused) + // + // EA_DISABLE_GCC_WARNING_AS_ERROR() + // EA_DISABLE_GCC_WARNING_AS_ERROR() + // + #ifndef EA_ENABLE_GCC_WARNING_AS_ERROR + #if defined(EA_COMPILER_GNUC) + #define EAGCCWERRORHELP0(x) #x + #define EAGCCWERRORHELP1(x) EAGCCWERRORHELP0(GCC diagnostic error x) + #define EAGCCWERRORHELP2(x) EAGCCWERRORHELP1(#x) + #endif + + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) // Can't test directly for __GNUC__ because some compilers lie. + #define EA_ENABLE_GCC_WARNING_AS_ERROR(w) \ + _Pragma("GCC diagnostic push") \ + _Pragma(EAGCCWERRORHELP2(w)) + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) + #define EA_DISABLE_GCC_WARNING(w) \ + _Pragma(EAGCCWERRORHELP2(w)) + #else + #define EA_DISABLE_GCC_WARNING(w) + #endif + #endif + + #ifndef EA_DISABLE_GCC_WARNING_AS_ERROR + #if defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) + #define EA_DISABLE_GCC_WARNING_AS_ERROR() \ + _Pragma("GCC diagnostic pop") + #else + #define EA_DISABLE_GCC_WARNING_AS_ERROR() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_CLANG_WARNING / EA_RESTORE_CLANG_WARNING + // + // Example usage: + // // Only one warning can be ignored per statement, due to how clang works. + // EA_DISABLE_CLANG_WARNING(-Wuninitialized) + // EA_DISABLE_CLANG_WARNING(-Wunused) + // + // EA_RESTORE_CLANG_WARNING() + // EA_RESTORE_CLANG_WARNING() + // + #ifndef EA_DISABLE_CLANG_WARNING + #if defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_CLANG_CL) + #define EACLANGWHELP0(x) #x + #define EACLANGWHELP1(x) EACLANGWHELP0(clang diagnostic ignored x) + #define EACLANGWHELP2(x) EACLANGWHELP1(#x) + + #define EA_DISABLE_CLANG_WARNING(w) \ + _Pragma("clang diagnostic push") \ + _Pragma(EACLANGWHELP2(-Wunknown-warning-option))\ + _Pragma(EACLANGWHELP2(w)) + #else + #define EA_DISABLE_CLANG_WARNING(w) + #endif + #endif + + #ifndef EA_RESTORE_CLANG_WARNING + #if defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_CLANG_CL) + #define EA_RESTORE_CLANG_WARNING() \ + _Pragma("clang diagnostic pop") + #else + #define EA_RESTORE_CLANG_WARNING() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_ALL_CLANG_WARNINGS / EA_RESTORE_ALL_CLANG_WARNINGS + // + // The situation for clang is the same as for GCC. See above. + // ------------------------------------------------------------------------ + + + // ------------------------------------------------------------------------ + // EA_ENABLE_CLANG_WARNING_AS_ERROR / EA_DISABLE_CLANG_WARNING_AS_ERROR + // + // Example usage: + // // Only one warning can be treated as an error per statement, due to how clang works. + // EA_ENABLE_CLANG_WARNING_AS_ERROR(-Wuninitialized) + // EA_ENABLE_CLANG_WARNING_AS_ERROR(-Wunused) + // + // EA_DISABLE_CLANG_WARNING_AS_ERROR() + // EA_DISABLE_CLANG_WARNING_AS_ERROR() + // + #ifndef EA_ENABLE_CLANG_WARNING_AS_ERROR + #if defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_CLANG_CL) + #define EACLANGWERRORHELP0(x) #x + #define EACLANGWERRORHELP1(x) EACLANGWERRORHELP0(clang diagnostic error x) + #define EACLANGWERRORHELP2(x) EACLANGWERRORHELP1(#x) + + #define EA_ENABLE_CLANG_WARNING_AS_ERROR(w) \ + _Pragma("clang diagnostic push") \ + _Pragma(EACLANGWERRORHELP2(w)) + #else + #define EA_DISABLE_CLANG_WARNING(w) + #endif + #endif + + #ifndef EA_DISABLE_CLANG_WARNING_AS_ERROR + #if defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_CLANG_CL) + #define EA_DISABLE_CLANG_WARNING_AS_ERROR() \ + _Pragma("clang diagnostic pop") + #else + #define EA_DISABLE_CLANG_WARNING_AS_ERROR() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_SN_WARNING / EA_RESTORE_SN_WARNING + // + // Note that we define this macro specifically for the SN compiler instead of + // having a generic one for EDG-based compilers. The reason for this is that + // while SN is indeed based on EDG, SN has different warning value mappings + // and thus warning 1234 for SN is not the same as 1234 for all other EDG compilers. + // + // Example usage: + // // Currently we are limited to one warning per line. + // EA_DISABLE_SN_WARNING(1787) + // EA_DISABLE_SN_WARNING(552) + // + // EA_RESTORE_SN_WARNING() + // EA_RESTORE_SN_WARNING() + // + #ifndef EA_DISABLE_SN_WARNING + #if defined(EA_COMPILER_SN) + #define EASNWHELP0(x) #x + #define EASNWHELP1(x) EASNWHELP0(diag_suppress x) + + #define EA_DISABLE_SN_WARNING(w) \ + _Pragma("control %push diag") \ + _Pragma(EASNWHELP1(w)) + #else + #define EA_DISABLE_SN_WARNING(w) + #endif + #endif + + #ifndef EA_RESTORE_SN_WARNING + #if defined(EA_COMPILER_SN) + #define EA_RESTORE_SN_WARNING() \ + _Pragma("control %pop diag") + #else + #define EA_RESTORE_SN_WARNING() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_ALL_SN_WARNINGS / EA_RESTORE_ALL_SN_WARNINGS + // + // Example usage: + // EA_DISABLE_ALL_SN_WARNINGS() + // + // EA_RESTORE_ALL_SN_WARNINGS() + // + #ifndef EA_DISABLE_ALL_SN_WARNINGS + #if defined(EA_COMPILER_SN) + #define EA_DISABLE_ALL_SN_WARNINGS() \ + _Pragma("control %push diag") \ + _Pragma("control diag=0") + #else + #define EA_DISABLE_ALL_SN_WARNINGS() + #endif + #endif + + #ifndef EA_RESTORE_ALL_SN_WARNINGS + #if defined(EA_COMPILER_SN) + #define EA_RESTORE_ALL_SN_WARNINGS() \ + _Pragma("control %pop diag") + #else + #define EA_RESTORE_ALL_SN_WARNINGS() + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_DISABLE_GHS_WARNING / EA_RESTORE_GHS_WARNING + // + // Disable warnings from the Green Hills compiler. + // + // Example usage: + // EA_DISABLE_GHS_WARNING(193) + // EA_DISABLE_GHS_WARNING(236, 5323) + // + // EA_RESTORE_GHS_WARNING() + // EA_RESTORE_GHS_WARNING() + // + #ifndef EA_DISABLE_GHS_WARNING + #if defined(EA_COMPILER_GREEN_HILLS) + #define EAGHSHELP0(x) #x + #define EAGHSHELP1(x) EAGHSHELP0(ghs nowarning x) + + #define EA_DISABLE_GHS_WARNING(w) \ + _Pragma(EAGHSHELP1(w)) + #else + #define EA_DISABLE_GHS_WARNING(w) + #endif + #endif + + #ifndef EA_RESTORE_GHS_WARNING + #if defined(EA_COMPILER_GREEN_HILLS) + #define EA_RESTORE_GHS_WARNING() \ + _Pragma("ghs endnowarning") + #else + #define EA_RESTORE_GHS_WARNING() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_ALL_GHS_WARNINGS / EA_RESTORE_ALL_GHS_WARNINGS + // + // #ifndef EA_DISABLE_ALL_GHS_WARNINGS + // #if defined(EA_COMPILER_GREEN_HILLS) + // #define EA_DISABLE_ALL_GHS_WARNINGS(w) \_ + // _Pragma("_________") + // #else + // #define EA_DISABLE_ALL_GHS_WARNINGS(w) + // #endif + // #endif + // + // #ifndef EA_RESTORE_ALL_GHS_WARNINGS + // #if defined(EA_COMPILER_GREEN_HILLS) + // #define EA_RESTORE_ALL_GHS_WARNINGS() \_ + // _Pragma("_________") + // #else + // #define EA_RESTORE_ALL_GHS_WARNINGS() + // #endif + // #endif + + + + // ------------------------------------------------------------------------ + // EA_DISABLE_EDG_WARNING / EA_RESTORE_EDG_WARNING + // + // Example usage: + // // Currently we are limited to one warning per line. + // EA_DISABLE_EDG_WARNING(193) + // EA_DISABLE_EDG_WARNING(236) + // + // EA_RESTORE_EDG_WARNING() + // EA_RESTORE_EDG_WARNING() + // + #ifndef EA_DISABLE_EDG_WARNING + // EDG-based compilers are inconsistent in how the implement warning pragmas. + #if defined(EA_COMPILER_EDG) && !defined(EA_COMPILER_INTEL) && !defined(EA_COMPILER_RVCT) + #define EAEDGWHELP0(x) #x + #define EAEDGWHELP1(x) EAEDGWHELP0(diag_suppress x) + + #define EA_DISABLE_EDG_WARNING(w) \ + _Pragma("control %push diag") \ + _Pragma(EAEDGWHELP1(w)) + #else + #define EA_DISABLE_EDG_WARNING(w) + #endif + #endif + + #ifndef EA_RESTORE_EDG_WARNING + #if defined(EA_COMPILER_EDG) && !defined(EA_COMPILER_INTEL) && !defined(EA_COMPILER_RVCT) + #define EA_RESTORE_EDG_WARNING() \ + _Pragma("control %pop diag") + #else + #define EA_RESTORE_EDG_WARNING() + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_ALL_EDG_WARNINGS / EA_RESTORE_ALL_EDG_WARNINGS + // + //#ifndef EA_DISABLE_ALL_EDG_WARNINGS + // #if defined(EA_COMPILER_EDG) && !defined(EA_COMPILER_SN) + // #define EA_DISABLE_ALL_EDG_WARNINGS(w) \_ + // _Pragma("_________") + // #else + // #define EA_DISABLE_ALL_EDG_WARNINGS(w) + // #endif + //#endif + // + //#ifndef EA_RESTORE_ALL_EDG_WARNINGS + // #if defined(EA_COMPILER_EDG) && !defined(EA_COMPILER_SN) + // #define EA_RESTORE_ALL_EDG_WARNINGS() \_ + // _Pragma("_________") + // #else + // #define EA_RESTORE_ALL_EDG_WARNINGS() + // #endif + //#endif + + + + // ------------------------------------------------------------------------ + // EA_DISABLE_CW_WARNING / EA_RESTORE_CW_WARNING + // + // Note that this macro can only control warnings via numbers and not by + // names. The reason for this is that the compiler's syntax for such + // warnings is not the same as for numbers. + // + // Example usage: + // // Currently we are limited to one warning per line and must also specify the warning in the restore macro. + // EA_DISABLE_CW_WARNING(10317) + // EA_DISABLE_CW_WARNING(10324) + // + // EA_RESTORE_CW_WARNING(10317) + // EA_RESTORE_CW_WARNING(10324) + // + #ifndef EA_DISABLE_CW_WARNING + #define EA_DISABLE_CW_WARNING(w) + #endif + + #ifndef EA_RESTORE_CW_WARNING + + #define EA_RESTORE_CW_WARNING(w) + + #endif + + + // ------------------------------------------------------------------------ + // EA_DISABLE_ALL_CW_WARNINGS / EA_RESTORE_ALL_CW_WARNINGS + // + #ifndef EA_DISABLE_ALL_CW_WARNINGS + #define EA_DISABLE_ALL_CW_WARNINGS() + + #endif + + #ifndef EA_RESTORE_ALL_CW_WARNINGS + #define EA_RESTORE_ALL_CW_WARNINGS() + #endif + + + + // ------------------------------------------------------------------------ + // EA_PURE + // + // This acts the same as the GCC __attribute__ ((pure)) directive and is + // implemented simply as a wrapper around it to allow portable usage of + // it and to take advantage of it if and when it appears in other compilers. + // + // A "pure" function is one that has no effects except its return value and + // its return value is a function of only the function's parameters or + // non-volatile global variables. Any parameter or global variable access + // must be read-only. Loop optimization and subexpression elimination can be + // applied to such functions. A common example is strlen(): Given identical + // inputs, the function's return value (its only effect) is invariant across + // multiple invocations and thus can be pulled out of a loop and called but once. + // + // Example usage: + // EA_PURE void Function(); + // + #ifndef EA_PURE + #if defined(EA_COMPILER_GNUC) + #define EA_PURE __attribute__((pure)) + #elif defined(EA_COMPILER_ARM) // Arm brand compiler for ARM CPU + #define EA_PURE __pure + #else + #define EA_PURE + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_WEAK + // EA_WEAK_SUPPORTED -- defined as 0 or 1. + // + // GCC + // The weak attribute causes the declaration to be emitted as a weak + // symbol rather than a global. This is primarily useful in defining + // library functions which can be overridden in user code, though it + // can also be used with non-function declarations. + // + // VC++ + // At link time, if multiple definitions of a COMDAT are seen, the linker + // picks one and discards the rest. If the linker option /OPT:REF + // is selected, then COMDAT elimination will occur to remove all the + // unreferenced data items in the linker output. + // + // Example usage: + // EA_WEAK void Function(); + // + #ifndef EA_WEAK + #if defined(_MSC_VER) && (_MSC_VER >= 1300) // If VC7.0 and later + #define EA_WEAK __declspec(selectany) + #define EA_WEAK_SUPPORTED 1 + #elif defined(_MSC_VER) || (defined(__GNUC__) && defined(__CYGWIN__)) + #define EA_WEAK + #define EA_WEAK_SUPPORTED 0 + #elif defined(EA_COMPILER_ARM) // Arm brand compiler for ARM CPU + #define EA_WEAK __weak + #define EA_WEAK_SUPPORTED 1 + #else // GCC and IBM compilers, others. + #define EA_WEAK __attribute__((weak)) + #define EA_WEAK_SUPPORTED 1 + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_UNUSED + // + // Makes compiler warnings about unused variables go away. + // + // Example usage: + // void Function(int x) + // { + // int y; + // EA_UNUSED(x); + // EA_UNUSED(y); + // } + // + #ifndef EA_UNUSED + // The EDG solution below is pretty weak and needs to be augmented or replaced. + // It can't handle the C language, is limited to places where template declarations + // can be used, and requires the type x to be usable as a functions reference argument. + #if defined(__cplusplus) && defined(__EDG__) + template + inline void EABaseUnused(T const volatile & x) { (void)x; } + #define EA_UNUSED(x) EABaseUnused(x) + #else + #define EA_UNUSED(x) (void)x + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_EMPTY + // + // Allows for a null statement, usually for the purpose of avoiding compiler warnings. + // + // Example usage: + // #ifdef EA_DEBUG + // #define MyDebugPrintf(x, y) printf(x, y) + // #else + // #define MyDebugPrintf(x, y) EA_EMPTY + // #endif + // + #ifndef EA_EMPTY + #define EA_EMPTY (void)0 + #endif + + + // ------------------------------------------------------------------------ + // EA_CURRENT_FUNCTION + // + // Provides a consistent way to get the current function name as a macro + // like the __FILE__ and __LINE__ macros work. The C99 standard specifies + // that __func__ be provided by the compiler, but most compilers don't yet + // follow that convention. However, many compilers have an alternative. + // + // We also define EA_CURRENT_FUNCTION_SUPPORTED for when it is not possible + // to have EA_CURRENT_FUNCTION work as expected. + // + // Defined inside a function because otherwise the macro might not be + // defined and code below might not compile. This happens with some + // compilers. + // + #ifndef EA_CURRENT_FUNCTION + #if defined __GNUC__ || (defined __ICC && __ICC >= 600) + #define EA_CURRENT_FUNCTION __PRETTY_FUNCTION__ + #elif defined(__FUNCSIG__) + #define EA_CURRENT_FUNCTION __FUNCSIG__ + #elif (defined __INTEL_COMPILER && __INTEL_COMPILER >= 600) || (defined __IBMCPP__ && __IBMCPP__ >= 500) || (defined __CWCC__ && __CWCC__ >= 0x4200) + #define EA_CURRENT_FUNCTION __FUNCTION__ + #elif defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901 + #define EA_CURRENT_FUNCTION __func__ + #else + #define EA_CURRENT_FUNCTION "(unknown function)" + #endif + #endif + + + // ------------------------------------------------------------------------ + // wchar_t + // Here we define: + // EA_WCHAR_T_NON_NATIVE + // EA_WCHAR_SIZE = + // + #ifndef EA_WCHAR_T_NON_NATIVE + // Compilers that always implement wchar_t as native include: + // COMEAU, new SN, and other EDG-based compilers. + // GCC + // Borland + // SunPro + // IBM Visual Age + #if defined(EA_COMPILER_INTEL) + #if (EA_COMPILER_VERSION < 700) + #define EA_WCHAR_T_NON_NATIVE 1 + #else + #if (!defined(_WCHAR_T_DEFINED) && !defined(_WCHAR_T)) + #define EA_WCHAR_T_NON_NATIVE 1 + #endif + #endif + #elif defined(EA_COMPILER_MSVC) || (defined(EA_COMPILER_CLANG) && defined(EA_PLATFORM_WINDOWS)) + #ifndef _NATIVE_WCHAR_T_DEFINED + #define EA_WCHAR_T_NON_NATIVE 1 + #endif + #elif defined(__EDG_VERSION__) && (!defined(_WCHAR_T) && (__EDG_VERSION__ < 400)) // EDG prior to v4 uses _WCHAR_T to indicate if wchar_t is native. v4+ may define something else, but we're not currently aware of it. + #define EA_WCHAR_T_NON_NATIVE 1 + #endif + #endif + + #ifndef EA_WCHAR_SIZE // If the user hasn't specified that it is a given size... + #if defined(__WCHAR_MAX__) // GCC defines this for most platforms. + #if (__WCHAR_MAX__ == 2147483647) || (__WCHAR_MAX__ == 4294967295) + #define EA_WCHAR_SIZE 4 + #elif (__WCHAR_MAX__ == 32767) || (__WCHAR_MAX__ == 65535) + #define EA_WCHAR_SIZE 2 + #elif (__WCHAR_MAX__ == 127) || (__WCHAR_MAX__ == 255) + #define EA_WCHAR_SIZE 1 + #else + #define EA_WCHAR_SIZE 4 + #endif + #elif defined(WCHAR_MAX) // The SN and Arm compilers define this. + #if (WCHAR_MAX == 2147483647) || (WCHAR_MAX == 4294967295) + #define EA_WCHAR_SIZE 4 + #elif (WCHAR_MAX == 32767) || (WCHAR_MAX == 65535) + #define EA_WCHAR_SIZE 2 + #elif (WCHAR_MAX == 127) || (WCHAR_MAX == 255) + #define EA_WCHAR_SIZE 1 + #else + #define EA_WCHAR_SIZE 4 + #endif + #elif defined(__WCHAR_BIT) // Green Hills (and other versions of EDG?) uses this. + #if (__WCHAR_BIT == 16) + #define EA_WCHAR_SIZE 2 + #elif (__WCHAR_BIT == 32) + #define EA_WCHAR_SIZE 4 + #elif (__WCHAR_BIT == 8) + #define EA_WCHAR_SIZE 1 + #else + #define EA_WCHAR_SIZE 4 + #endif + #elif defined(_WCMAX) // The SN and Arm compilers define this. + #if (_WCMAX == 2147483647) || (_WCMAX == 4294967295) + #define EA_WCHAR_SIZE 4 + #elif (_WCMAX == 32767) || (_WCMAX == 65535) + #define EA_WCHAR_SIZE 2 + #elif (_WCMAX == 127) || (_WCMAX == 255) + #define EA_WCHAR_SIZE 1 + #else + #define EA_WCHAR_SIZE 4 + #endif + #elif defined(EA_PLATFORM_UNIX) + // It is standard on Unix to have wchar_t be int32_t or uint32_t. + // All versions of GNUC default to a 32 bit wchar_t, but EA has used + // the -fshort-wchar GCC command line option to force it to 16 bit. + // If you know that the compiler is set to use a wchar_t of other than + // the default, you need to manually define EA_WCHAR_SIZE for the build. + #define EA_WCHAR_SIZE 4 + #else + // It is standard on Windows to have wchar_t be uint16_t. GCC + // defines wchar_t as int by default. Electronic Arts has + // standardized on wchar_t being an unsigned 16 bit value on all + // console platforms. Given that there is currently no known way to + // tell at preprocessor time what the size of wchar_t is, we declare + // it to be 2, as this is the Electronic Arts standard. If you have + // EA_WCHAR_SIZE != sizeof(wchar_t), then your code might not be + // broken, but it also won't work with wchar libraries and data from + // other parts of EA. Under GCC, you can force wchar_t to two bytes + // with the -fshort-wchar compiler argument. + #define EA_WCHAR_SIZE 2 + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_RESTRICT + // + // The C99 standard defines a new keyword, restrict, which allows for the + // improvement of code generation regarding memory usage. Compilers can + // generate significantly faster code when you are able to use restrict. + // + // Example usage: + // void DoSomething(char* EA_RESTRICT p1, char* EA_RESTRICT p2); + // + #ifndef EA_RESTRICT + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) // If VC8 (VS2005) or later... + #define EA_RESTRICT __restrict + #elif defined(EA_COMPILER_CLANG) + #define EA_RESTRICT __restrict + #elif defined(EA_COMPILER_GNUC) // Includes GCC and other compilers emulating GCC. + #define EA_RESTRICT __restrict // GCC defines 'restrict' (as opposed to __restrict) in C99 mode only. + #elif defined(EA_COMPILER_ARM) + #define EA_RESTRICT __restrict + #elif defined(EA_COMPILER_IS_C99) + #define EA_RESTRICT restrict + #else + // If the compiler didn't support restricted pointers, defining EA_RESTRICT + // away would result in compiling and running fine but you just wouldn't + // the same level of optimization. On the other hand, all the major compilers + // support restricted pointers. + #define EA_RESTRICT + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_DEPRECATED // Used as a prefix. + // EA_PREFIX_DEPRECATED // You should need this only for unusual compilers. + // EA_POSTFIX_DEPRECATED // You should need this only for unusual compilers. + // EA_DEPRECATED_MESSAGE // Used as a prefix and provides a deprecation message. + // + // Example usage: + // EA_DEPRECATED void Function(); + // EA_DEPRECATED_MESSAGE("Use 1.0v API instead") void Function(); + // + // or for maximum portability: + // EA_PREFIX_DEPRECATED void Function() EA_POSTFIX_DEPRECATED; + // + + #ifndef EA_DEPRECATED + #if defined(EA_COMPILER_CPP14_ENABLED) + #define EA_DEPRECATED [[deprecated]] + #elif defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION > 1300) // If VC7 (VS2003) or later... + #define EA_DEPRECATED __declspec(deprecated) + #elif defined(EA_COMPILER_MSVC) + #define EA_DEPRECATED + #else + #define EA_DEPRECATED __attribute__((deprecated)) + #endif + #endif + + #ifndef EA_PREFIX_DEPRECATED + #if defined(EA_COMPILER_CPP14_ENABLED) + #define EA_PREFIX_DEPRECATED [[deprecated]] + #define EA_POSTFIX_DEPRECATED + #elif defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION > 1300) // If VC7 (VS2003) or later... + #define EA_PREFIX_DEPRECATED __declspec(deprecated) + #define EA_POSTFIX_DEPRECATED + #elif defined(EA_COMPILER_MSVC) + #define EA_PREFIX_DEPRECATED + #define EA_POSTFIX_DEPRECATED + #else + #define EA_PREFIX_DEPRECATED + #define EA_POSTFIX_DEPRECATED __attribute__((deprecated)) + #endif + #endif + + #ifndef EA_DEPRECATED_MESSAGE + #if defined(EA_COMPILER_CPP14_ENABLED) + #define EA_DEPRECATED_MESSAGE(msg) [[deprecated(#msg)]] + #else + // Compiler does not support depreaction messages, explicitly drop the msg but still mark the function as deprecated + #define EA_DEPRECATED_MESSAGE(msg) EA_DEPRECATED + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_FORCE_INLINE // Used as a prefix. + // EA_PREFIX_FORCE_INLINE // You should need this only for unusual compilers. + // EA_POSTFIX_FORCE_INLINE // You should need this only for unusual compilers. + // + // Example usage: + // EA_FORCE_INLINE void Foo(); // Implementation elsewhere. + // EA_PREFIX_FORCE_INLINE void Foo() EA_POSTFIX_FORCE_INLINE; // Implementation elsewhere. + // + // Note that when the prefix version of this function is used, it replaces + // the regular C++ 'inline' statement. Thus you should not use both the + // C++ inline statement and this macro with the same function declaration. + // + // To force inline usage under GCC 3.1+, you use this: + // inline void Foo() __attribute__((always_inline)); + // or + // inline __attribute__((always_inline)) void Foo(); + // + // The CodeWarrior compiler doesn't have the concept of forcing inlining per function. + // + #ifndef EA_FORCE_INLINE + #if defined(EA_COMPILER_MSVC) + #define EA_FORCE_INLINE __forceinline + #elif defined(EA_COMPILER_GNUC) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 301) || defined(EA_COMPILER_CLANG) + #if defined(__cplusplus) + #define EA_FORCE_INLINE inline __attribute__((always_inline)) + #else + #define EA_FORCE_INLINE __inline__ __attribute__((always_inline)) + #endif + #else + #if defined(__cplusplus) + #define EA_FORCE_INLINE inline + #else + #define EA_FORCE_INLINE __inline + #endif + #endif + #endif + + #if defined(EA_COMPILER_GNUC) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 301) || defined(EA_COMPILER_CLANG) + #define EA_PREFIX_FORCE_INLINE inline + #define EA_POSTFIX_FORCE_INLINE __attribute__((always_inline)) + #else + #define EA_PREFIX_FORCE_INLINE inline + #define EA_POSTFIX_FORCE_INLINE + #endif + + + // ------------------------------------------------------------------------ + // EA_FORCE_INLINE_LAMBDA + // + // EA_FORCE_INLINE_LAMBDA is used to force inline a call to a lambda when possible. + // Force inlining a lambda can be useful to reduce overhead in situations where a lambda may + // may only be called once, or inlining allows the compiler to apply other optimizations that wouldn't + // otherwise be possible. + // + // The ability to force inline a lambda is currently only available on a subset of compilers. + // + // Example usage: + // + // auto lambdaFunction = []() EA_FORCE_INLINE_LAMBDA + // { + // }; + // + #ifndef EA_FORCE_INLINE_LAMBDA + #if defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + #define EA_FORCE_INLINE_LAMBDA __attribute__((always_inline)) + #else + #define EA_FORCE_INLINE_LAMBDA + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_NO_INLINE // Used as a prefix. + // EA_PREFIX_NO_INLINE // You should need this only for unusual compilers. + // EA_POSTFIX_NO_INLINE // You should need this only for unusual compilers. + // + // Example usage: + // EA_NO_INLINE void Foo(); // Implementation elsewhere. + // EA_PREFIX_NO_INLINE void Foo() EA_POSTFIX_NO_INLINE; // Implementation elsewhere. + // + // That this declaration is incompatbile with C++ 'inline' and any + // variant of EA_FORCE_INLINE. + // + // To disable inline usage under VC++ priof to VS2005, you need to use this: + // #pragma inline_depth(0) // Disable inlining. + // void Foo() { ... } + // #pragma inline_depth() // Restore to default. + // + // Since there is no easy way to disable inlining on a function-by-function + // basis in VC++ prior to VS2005, the best strategy is to write platform-specific + // #ifdefs in the code or to disable inlining for a given module and enable + // functions individually with EA_FORCE_INLINE. + // + #ifndef EA_NO_INLINE + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) // If VC8 (VS2005) or later... + #define EA_NO_INLINE __declspec(noinline) + #elif defined(EA_COMPILER_MSVC) + #define EA_NO_INLINE + #else + #define EA_NO_INLINE __attribute__((noinline)) + #endif + #endif + + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) // If VC8 (VS2005) or later... + #define EA_PREFIX_NO_INLINE __declspec(noinline) + #define EA_POSTFIX_NO_INLINE + #elif defined(EA_COMPILER_MSVC) + #define EA_PREFIX_NO_INLINE + #define EA_POSTFIX_NO_INLINE + #else + #define EA_PREFIX_NO_INLINE + #define EA_POSTFIX_NO_INLINE __attribute__((noinline)) + #endif + + + // ------------------------------------------------------------------------ + // EA_NO_VTABLE + // + // Example usage: + // class EA_NO_VTABLE X { + // virtual void InterfaceFunction(); + // }; + // + // EA_CLASS_NO_VTABLE(X) { + // virtual void InterfaceFunction(); + // }; + // + #ifdef EA_COMPILER_MSVC + #define EA_NO_VTABLE __declspec(novtable) + #define EA_CLASS_NO_VTABLE(x) class __declspec(novtable) x + #define EA_STRUCT_NO_VTABLE(x) struct __declspec(novtable) x + #else + #define EA_NO_VTABLE + #define EA_CLASS_NO_VTABLE(x) class x + #define EA_STRUCT_NO_VTABLE(x) struct x + #endif + + + // ------------------------------------------------------------------------ + // EA_PASCAL + // + // Also known on PC platforms as stdcall. + // This convention causes the compiler to assume that the called function + // will pop off the stack space used to pass arguments, unless it takes a + // variable number of arguments. + // + // Example usage: + // this: + // void DoNothing(int x); + // void DoNothing(int x){} + // would be written as this: + // void EA_PASCAL_FUNC(DoNothing(int x)); + // void EA_PASCAL_FUNC(DoNothing(int x)){} + // + #ifndef EA_PASCAL + #if defined(EA_COMPILER_MSVC) + #define EA_PASCAL __stdcall + #elif defined(EA_COMPILER_GNUC) && defined(EA_PROCESSOR_X86) + #define EA_PASCAL __attribute__((stdcall)) + #else + // Some compilers simply don't support pascal calling convention. + // As a result, there isn't an issue here, since the specification of + // pascal calling convention is for the purpose of disambiguating the + // calling convention that is applied. + #define EA_PASCAL + #endif + #endif + + #ifndef EA_PASCAL_FUNC + #if defined(EA_COMPILER_MSVC) + #define EA_PASCAL_FUNC(funcname_and_paramlist) __stdcall funcname_and_paramlist + #elif defined(EA_COMPILER_GNUC) && defined(EA_PROCESSOR_X86) + #define EA_PASCAL_FUNC(funcname_and_paramlist) __attribute__((stdcall)) funcname_and_paramlist + #else + #define EA_PASCAL_FUNC(funcname_and_paramlist) funcname_and_paramlist + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_SSE + // Visual C Processor Packs define _MSC_FULL_VER and are needed for SSE + // Intel C also has SSE support. + // EA_SSE is used to select FPU or SSE versions in hw_select.inl + // + // EA_SSE defines the level of SSE support: + // 0 indicates no SSE support + // 1 indicates SSE1 is supported + // 2 indicates SSE2 is supported + // 3 indicates SSE3 (or greater) is supported + // + // Note: SSE support beyond SSE3 can't be properly represented as a single + // version number. Instead users should use specific SSE defines (e.g. + // EA_SSE4_2) to detect what specific support is available. EA_SSE being + // equal to 3 really only indicates that SSE3 or greater is supported. + #ifndef EA_SSE + #if defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + #if defined(__SSE3__) + #define EA_SSE 3 + #elif defined(__SSE2__) + #define EA_SSE 2 + #elif defined(__SSE__) && __SSE__ + #define EA_SSE 1 + #else + #define EA_SSE 0 + #endif + #elif (defined(EA_SSE3) && EA_SSE3) || defined EA_PLATFORM_XBOXONE || defined EA_PLATFORM_XBSX + #define EA_SSE 3 + #elif defined(EA_SSE2) && EA_SSE2 + #define EA_SSE 2 + #elif defined(EA_PROCESSOR_X86) && defined(_MSC_FULL_VER) && !defined(__NOSSE__) && defined(_M_IX86_FP) + #define EA_SSE _M_IX86_FP + #elif defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_INTEL) && !defined(__NOSSE__) + #define EA_SSE 1 + #elif defined(EA_PROCESSOR_X86_64) + // All x64 processors support SSE2 or higher + #define EA_SSE 2 + #else + #define EA_SSE 0 + #endif + #endif + + // ------------------------------------------------------------------------ + // We define separate defines for SSE support beyond SSE1. These defines + // are particularly useful for detecting SSE4.x features since there isn't + // a single concept of SSE4. + // + // The following SSE defines are always defined. 0 indicates the + // feature/level of SSE is not supported, and 1 indicates support is + // available. + #ifndef EA_SSE2 + #if EA_SSE >= 2 + #define EA_SSE2 1 + #else + #define EA_SSE2 0 + #endif + #endif + #ifndef EA_SSE3 + #if EA_SSE >= 3 + #define EA_SSE3 1 + #else + #define EA_SSE3 0 + #endif + #endif + #ifndef EA_SSSE3 + #if defined __SSSE3__ || defined EA_PLATFORM_XBOXONE || defined EA_PLATFORM_XBSX + #define EA_SSSE3 1 + #else + #define EA_SSSE3 0 + #endif + #endif + #ifndef EA_SSE4_1 + #if defined __SSE4_1__ || defined EA_PLATFORM_XBOXONE || defined EA_PLATFORM_XBSX + #define EA_SSE4_1 1 + #else + #define EA_SSE4_1 0 + #endif + #endif + #ifndef EA_SSE4_2 + #if defined __SSE4_2__ || defined EA_PLATFORM_XBOXONE || defined EA_PLATFORM_XBSX + #define EA_SSE4_2 1 + #else + #define EA_SSE4_2 0 + #endif + #endif + #ifndef EA_SSE4A + #if defined __SSE4A__ || defined EA_PLATFORM_XBOXONE || defined EA_PLATFORM_XBSX + #define EA_SSE4A 1 + #else + #define EA_SSE4A 0 + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_AVX + // EA_AVX may be used to determine if Advanced Vector Extensions are available for the target architecture + // + // EA_AVX defines the level of AVX support: + // 0 indicates no AVX support + // 1 indicates AVX1 is supported + // 2 indicates AVX2 is supported + #ifndef EA_AVX + #if defined __AVX2__ + #define EA_AVX 2 + #elif defined __AVX__ || defined EA_PLATFORM_XBOXONE || defined EA_PLATFORM_XBSX + #define EA_AVX 1 + #else + #define EA_AVX 0 + #endif + #endif + #ifndef EA_AVX2 + #if EA_AVX >= 2 + #define EA_AVX2 1 + #else + #define EA_AVX2 0 + #endif + #endif + + // EA_FP16C may be used to determine the existence of float <-> half conversion operations on an x86 CPU. + // (For example to determine if _mm_cvtph_ps or _mm_cvtps_ph could be used.) + #ifndef EA_FP16C + #if defined __F16C__ || defined EA_PLATFORM_XBOXONE || defined EA_PLATFORM_XBSX + #define EA_FP16C 1 + #else + #define EA_FP16C 0 + #endif + #endif + + // EA_FP128 may be used to determine if __float128 is a supported type for use. This type is enabled by a GCC extension (_GLIBCXX_USE_FLOAT128) + // but has support by some implementations of clang (__FLOAT128__) + // PS4 does not support __float128 as of SDK 5.500 https://ps4.siedev.net/resources/documents/SDK/5.500/CPU_Compiler_ABI-Overview/0003.html + #ifndef EA_FP128 + #if (defined __FLOAT128__ || defined _GLIBCXX_USE_FLOAT128) && !defined(EA_PLATFORM_SONY) + #define EA_FP128 1 + #else + #define EA_FP128 0 + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_ABM + // EA_ABM may be used to determine if Advanced Bit Manipulation sets are available for the target architecture (POPCNT, LZCNT) + // + #ifndef EA_ABM + #if defined(__ABM__) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_SONY) || defined(EA_PLATFORM_XBSX) + #define EA_ABM 1 + #else + #define EA_ABM 0 + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_NEON + // EA_NEON may be used to determine if NEON is supported. + #ifndef EA_NEON + #if defined(__ARM_NEON__) || defined(__ARM_NEON) + #define EA_NEON 1 + #else + #define EA_NEON 0 + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_BMI + // EA_BMI may be used to determine if Bit Manipulation Instruction sets are available for the target architecture + // + // EA_BMI defines the level of BMI support: + // 0 indicates no BMI support + // 1 indicates BMI1 is supported + // 2 indicates BMI2 is supported + #ifndef EA_BMI + #if defined(__BMI2__) + #define EA_BMI 2 + #elif defined(__BMI__) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + #define EA_BMI 1 + #else + #define EA_BMI 0 + #endif + #endif + #ifndef EA_BMI2 + #if EA_BMI >= 2 + #define EA_BMI2 1 + #else + #define EA_BMI2 0 + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_FMA3 + // EA_FMA3 may be used to determine if Fused Multiply Add operations are available for the target architecture + // __FMA__ is defined only by GCC, Clang, and ICC; MSVC only defines __AVX__ and __AVX2__ + // FMA3 was introduced alongside AVX2 on Intel Haswell + // All AMD processors support FMA3 if AVX2 is also supported + // + // EA_FMA3 defines the level of FMA3 support: + // 0 indicates no FMA3 support + // 1 indicates FMA3 is supported + #ifndef EA_FMA3 + #if defined(__FMA__) || EA_AVX2 >= 1 + #define EA_FMA3 1 + #else + #define EA_FMA3 0 + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_TBM + // EA_TBM may be used to determine if Trailing Bit Manipulation instructions are available for the target architecture + #ifndef EA_TBM + #if defined(__TBM__) + #define EA_TBM 1 + #else + #define EA_TBM 0 + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_IMPORT + // import declaration specification + // specifies that the declared symbol is imported from another dynamic library. + #ifndef EA_IMPORT + #if defined(EA_COMPILER_MSVC) + #define EA_IMPORT __declspec(dllimport) + #else + #define EA_IMPORT + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_EXPORT + // export declaration specification + // specifies that the declared symbol is exported from the current dynamic library. + // this is not the same as the C++ export keyword. The C++ export keyword has been + // removed from the language as of C++11. + #ifndef EA_EXPORT + #if defined(EA_COMPILER_MSVC) + #define EA_EXPORT __declspec(dllexport) + #else + #define EA_EXPORT + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_PRAGMA_ONCE_SUPPORTED + // + // This is a wrapper for the #pragma once preprocessor directive. + // It allows for some compilers (in particular VC++) to implement signifcantly + // faster include file preprocessing. #pragma once can be used to replace + // header include guards or to augment them. However, #pragma once isn't + // necessarily supported by all compilers and isn't guaranteed to be so in + // the future, so using #pragma once to replace traditional include guards + // is not strictly portable. Note that a direct #define for #pragma once is + // impossible with VC++, due to limitations, but can be done with other + // compilers/preprocessors via _Pragma("once"). + // + // Example usage (which includes traditional header guards for portability): + // #ifndef SOMEPACKAGE_SOMEHEADER_H + // #define SOMEPACKAGE_SOMEHEADER_H + // + // #if defined(EA_PRAGMA_ONCE_SUPPORTED) + // #pragma once + // #endif + // + // + // + // #endif + // + #if defined(_MSC_VER) || defined(__GNUC__) || defined(__EDG__) || defined(__APPLE__) + #define EA_PRAGMA_ONCE_SUPPORTED 1 + #endif + + + + // ------------------------------------------------------------------------ + // EA_ONCE + // + // Example usage (which includes traditional header guards for portability): + // #ifndef SOMEPACKAGE_SOMEHEADER_H + // #define SOMEPACKAGE_SOMEHEADER_H + // + // EA_ONCE() + // + // + // + // #endif + // + #if defined(EA_PRAGMA_ONCE_SUPPORTED) + #if defined(_MSC_VER) + #define EA_ONCE() __pragma(once) + #else + #define EA_ONCE() // _Pragma("once") It turns out that _Pragma("once") isn't supported by many compilers. + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_OVERRIDE + // + // C++11 override + // See http://msdn.microsoft.com/en-us/library/jj678987.aspx for more information. + // You can use EA_FINAL_OVERRIDE to combine usage of EA_OVERRIDE and EA_INHERITANCE_FINAL in a single statement. + // + // Example usage: + // struct B { virtual void f(int); }; + // struct D : B { void f(int) EA_OVERRIDE; }; + // + #ifndef EA_OVERRIDE + #if defined(EA_COMPILER_NO_OVERRIDE) + #define EA_OVERRIDE + #else + #define EA_OVERRIDE override + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_INHERITANCE_FINAL + // + // Portably wraps the C++11 final specifier. + // See http://msdn.microsoft.com/en-us/library/jj678985.aspx for more information. + // You can use EA_FINAL_OVERRIDE to combine usage of EA_OVERRIDE and EA_INHERITANCE_FINAL in a single statement. + // This is not called EA_FINAL because that term is used within EA to denote debug/release/final builds. + // + // Example usage: + // struct B { virtual void f() EA_INHERITANCE_FINAL; }; + // + #ifndef EA_INHERITANCE_FINAL + #if defined(EA_COMPILER_NO_INHERITANCE_FINAL) + #define EA_INHERITANCE_FINAL + #elif (defined(_MSC_VER) && (EA_COMPILER_VERSION < 1700)) // Pre-VS2012 + #define EA_INHERITANCE_FINAL sealed + #else + #define EA_INHERITANCE_FINAL final + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_FINAL_OVERRIDE + // + // Portably wraps the C++11 override final specifiers combined. + // + // Example usage: + // struct A { virtual void f(); }; + // struct B : public A { virtual void f() EA_FINAL_OVERRIDE; }; + // + #ifndef EA_FINAL_OVERRIDE + #define EA_FINAL_OVERRIDE EA_OVERRIDE EA_INHERITANCE_FINAL + #endif + + + // ------------------------------------------------------------------------ + // EA_SEALED + // + // This is deprecated, as the C++11 Standard has final (EA_INHERITANCE_FINAL) instead. + // See http://msdn.microsoft.com/en-us/library/0w2w91tf.aspx for more information. + // Example usage: + // struct B { virtual void f() EA_SEALED; }; + // + #ifndef EA_SEALED + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) // VS2005 (VC8) and later + #define EA_SEALED sealed + #else + #define EA_SEALED + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_ABSTRACT + // + // This is a Microsoft language extension. + // See http://msdn.microsoft.com/en-us/library/b0z6b513.aspx for more information. + // Example usage: + // struct X EA_ABSTRACT { virtual void f(){} }; + // + #ifndef EA_ABSTRACT + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) // VS2005 (VC8) and later + #define EA_ABSTRACT abstract + #else + #define EA_ABSTRACT + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_CONSTEXPR + // EA_CONSTEXPR_OR_CONST + // + // Portable wrapper for C++11's 'constexpr' support. + // + // See http://www.cprogramming.com/c++11/c++11-compile-time-processing-with-constexpr.html for more information. + // Example usage: + // EA_CONSTEXPR int GetValue() { return 37; } + // EA_CONSTEXPR_OR_CONST double gValue = std::sin(kTwoPi); + // + #if !defined(EA_CONSTEXPR) + #if defined(EA_COMPILER_NO_CONSTEXPR) + #define EA_CONSTEXPR + #else + #define EA_CONSTEXPR constexpr + #endif + #endif + + #if !defined(EA_CONSTEXPR_OR_CONST) + #if defined(EA_COMPILER_NO_CONSTEXPR) + #define EA_CONSTEXPR_OR_CONST const + #else + #define EA_CONSTEXPR_OR_CONST constexpr + #endif + #endif + + // ------------------------------------------------------------------------ + // EA_CONSTEXPR_IF + // + // Portable wrapper for C++17's 'constexpr if' support. + // + // https://en.cppreference.com/w/cpp/language/if + // + // Example usage: + // + // EA_CONSTEXPR_IF(eastl::is_copy_constructible_v) + // { ... } + // + #if !defined(EA_CONSTEXPR_IF) + #if defined(EA_COMPILER_NO_CONSTEXPR_IF) + #define EA_CONSTEXPR_IF(predicate) if ((predicate)) + #else + #define EA_CONSTEXPR_IF(predicate) if constexpr ((predicate)) + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_EXTERN_TEMPLATE + // + // Portable wrapper for C++11's 'extern template' support. + // + // Example usage: + // EA_EXTERN_TEMPLATE(class basic_string); + // + #if !defined(EA_EXTERN_TEMPLATE) + #if defined(EA_COMPILER_NO_EXTERN_TEMPLATE) + #define EA_EXTERN_TEMPLATE(declaration) + #else + #define EA_EXTERN_TEMPLATE(declaration) extern template declaration + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_NOEXCEPT + // EA_NOEXCEPT_IF(predicate) + // EA_NOEXCEPT_EXPR(expression) + // + // Portable wrapper for C++11 noexcept + // http://en.cppreference.com/w/cpp/language/noexcept + // http://en.cppreference.com/w/cpp/language/noexcept_spec + // + // Example usage: + // EA_NOEXCEPT + // EA_NOEXCEPT_IF(predicate) + // EA_NOEXCEPT_EXPR(expression) + // + // This function never throws an exception. + // void DoNothing() EA_NOEXCEPT + // { } + // + // This function throws an exception of T::T() throws an exception. + // template + // void DoNothing() EA_NOEXCEPT_IF(EA_NOEXCEPT_EXPR(T())) + // { T t; } + // + #if !defined(EA_NOEXCEPT) + #if defined(EA_COMPILER_NO_NOEXCEPT) + #define EA_NOEXCEPT + #define EA_NOEXCEPT_IF(predicate) + #define EA_NOEXCEPT_EXPR(expression) false + #else + #define EA_NOEXCEPT noexcept + #define EA_NOEXCEPT_IF(predicate) noexcept((predicate)) + #define EA_NOEXCEPT_EXPR(expression) noexcept((expression)) + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_NORETURN + // + // Wraps the C++11 noreturn attribute. See EA_COMPILER_NO_NORETURN + // http://en.cppreference.com/w/cpp/language/attributes + // http://msdn.microsoft.com/en-us/library/k6ktzx3s%28v=vs.80%29.aspx + // http://blog.aaronballman.com/2011/09/understanding-attributes/ + // + // Example usage: + // EA_NORETURN void SomeFunction() + // { throw "error"; } + // + #if !defined(EA_NORETURN) + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1300) // VS2003 (VC7) and later + #define EA_NORETURN __declspec(noreturn) + #elif defined(EA_COMPILER_NO_NORETURN) + #define EA_NORETURN + #else + #define EA_NORETURN [[noreturn]] + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_CARRIES_DEPENDENCY + // + // Wraps the C++11 carries_dependency attribute + // http://en.cppreference.com/w/cpp/language/attributes + // http://blog.aaronballman.com/2011/09/understanding-attributes/ + // + // Example usage: + // EA_CARRIES_DEPENDENCY int* SomeFunction() + // { return &mX; } + // + // + #if !defined(EA_CARRIES_DEPENDENCY) + #if defined(EA_COMPILER_NO_CARRIES_DEPENDENCY) + #define EA_CARRIES_DEPENDENCY + #else + #define EA_CARRIES_DEPENDENCY [[carries_dependency]] + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_FALLTHROUGH + // + // [[fallthrough] is a C++17 standard attribute that appears in switch + // statements to indicate that the fallthrough from the previous case in the + // switch statement is intentially and not a bug. + // + // http://en.cppreference.com/w/cpp/language/attributes + // + // Example usage: + // void f(int n) + // { + // switch(n) + // { + // case 1: + // DoCase1(); + // // Compiler may generate a warning for fallthrough behaviour + // + // case 2: + // DoCase2(); + // + // EA_FALLTHROUGH; + // case 3: + // DoCase3(); + // } + // } + // + #if !defined(EA_FALLTHROUGH) + #if defined(EA_COMPILER_NO_FALLTHROUGH) + #define EA_FALLTHROUGH + #else + #define EA_FALLTHROUGH [[fallthrough]] + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_NODISCARD + // + // [[nodiscard]] is a C++17 standard attribute that can be applied to a + // function declaration, enum, or class declaration. If a any of the list + // previously are returned from a function (without the user explicitly + // casting to void) the addition of the [[nodiscard]] attribute encourages + // the compiler to generate a warning about the user discarding the return + // value. This is a useful practice to encourage client code to check API + // error codes. + // + // http://en.cppreference.com/w/cpp/language/attributes + // + // Example usage: + // + // EA_NODISCARD int baz() { return 42; } + // + // void foo() + // { + // baz(); // warning: ignoring return value of function declared with 'nodiscard' attribute + // } + // + #if !defined(EA_NODISCARD) + #if defined(EA_COMPILER_NO_NODISCARD) + #define EA_NODISCARD + #else + #define EA_NODISCARD [[nodiscard]] + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_MAYBE_UNUSED + // + // [[maybe_unused]] is a C++17 standard attribute that suppresses warnings + // on unused entities that are declared as maybe_unused. + // + // http://en.cppreference.com/w/cpp/language/attributes + // + // Example usage: + // void foo(EA_MAYBE_UNUSED int i) + // { + // assert(i == 42); // warning suppressed when asserts disabled. + // } + // + #if !defined(EA_MAYBE_UNUSED) + #if defined(EA_COMPILER_NO_MAYBE_UNUSED) + #define EA_MAYBE_UNUSED + #else + #define EA_MAYBE_UNUSED [[maybe_unused]] + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_NO_UBSAN + // + // The LLVM/Clang undefined behaviour sanitizer will not analyse a function tagged with the following attribute. + // + // https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#disabling-instrumentation-with-attribute-no-sanitize-undefined + // + // Example usage: + // EA_NO_UBSAN int SomeFunction() { ... } + // + #ifndef EA_NO_UBSAN + #if defined(EA_COMPILER_CLANG) + #define EA_NO_UBSAN __attribute__((no_sanitize("undefined"))) + #else + #define EA_NO_UBSAN + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_NO_ASAN + // + // The LLVM/Clang address sanitizer will not analyse a function tagged with the following attribute. + // + // https://clang.llvm.org/docs/AddressSanitizer.html#disabling-instrumentation-with-attribute-no-sanitize-address + // + // Example usage: + // EA_NO_ASAN int SomeFunction() { ... } + // + #ifndef EA_NO_ASAN + #if defined(EA_COMPILER_CLANG) + #define EA_NO_ASAN __attribute__((no_sanitize("address"))) + #else + #define EA_NO_ASAN + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_ASAN_ENABLED + // + // Defined as 0 or 1. It's value depends on the compile environment. + // Specifies whether the code is being built with Clang's Address Sanitizer. + // + #if defined(__has_feature) + #if __has_feature(address_sanitizer) + #define EA_ASAN_ENABLED 1 + #else + #define EA_ASAN_ENABLED 0 + #endif + #else + #define EA_ASAN_ENABLED 0 + #endif + + + // ------------------------------------------------------------------------ + // EA_NON_COPYABLE + // + // This macro defines as a class as not being copy-constructable + // or assignable. This is useful for preventing class instances + // from being passed to functions by value, is useful for preventing + // compiler warnings by some compilers about the inability to + // auto-generate a copy constructor and assignment, and is useful + // for simply declaring in the interface that copy semantics are + // not supported by the class. Your class needs to have at least a + // default constructor when using this macro. + // + // Beware that this class works by declaring a private: section of + // the class in the case of compilers that don't support C++11 deleted + // functions. + // + // Note: With some pre-C++11 compilers (e.g. Green Hills), you may need + // to manually define an instances of the hidden functions, even + // though they are not used. + // + // Example usage: + // class Widget { + // Widget(); + // . . . + // EA_NON_COPYABLE(Widget) + // }; + // + #if !defined(EA_NON_COPYABLE) + #if defined(EA_COMPILER_NO_DELETED_FUNCTIONS) + #define EA_NON_COPYABLE(EAClass_) \ + private: \ + EA_DISABLE_VC_WARNING(4822); /* local class member function does not have a body */ \ + EAClass_(const EAClass_&); \ + void operator=(const EAClass_&); \ + EA_RESTORE_VC_WARNING(); + #else + #define EA_NON_COPYABLE(EAClass_) \ + EA_DISABLE_VC_WARNING(4822); /* local class member function does not have a body */ \ + EAClass_(const EAClass_&) = delete; \ + void operator=(const EAClass_&) = delete; \ + EA_RESTORE_VC_WARNING(); + #endif + #endif + + + // ------------------------------------------------------------------------ + // EA_FUNCTION_DELETE + // + // Semi-portable way of specifying a deleted function which allows for + // cleaner code in class declarations. + // + // Example usage: + // + // class Example + // { + // private: // For portability with pre-C++11 compilers, make the function private. + // void foo() EA_FUNCTION_DELETE; + // }; + // + // Note: EA_FUNCTION_DELETE'd functions should be private to prevent the + // functions from being called even when the compiler does not support + // deleted functions. Some compilers (e.g. Green Hills) that don't support + // C++11 deleted functions can require that you define the function, + // which you can do in the associated source file for the class. + // + #if defined(EA_COMPILER_NO_DELETED_FUNCTIONS) + #define EA_FUNCTION_DELETE + #else + #define EA_FUNCTION_DELETE = delete + #endif + + // ------------------------------------------------------------------------ + // EA_DISABLE_DEFAULT_CTOR + // + // Disables the compiler generated default constructor. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_DEFAULT_CTOR(Example); + // }; + // + #define EA_DISABLE_DEFAULT_CTOR(ClassName) ClassName() EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_COPY_CTOR + // + // Disables the compiler generated copy constructor. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_COPY_CTOR(Example); + // }; + // + #define EA_DISABLE_COPY_CTOR(ClassName) ClassName(const ClassName &) EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_MOVE_CTOR + // + // Disables the compiler generated move constructor. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_MOVE_CTOR(Example); + // }; + // + #define EA_DISABLE_MOVE_CTOR(ClassName) ClassName(ClassName&&) EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_ASSIGNMENT_OPERATOR + // + // Disables the compiler generated assignment operator. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_ASSIGNMENT_OPERATOR(Example); + // }; + // + #define EA_DISABLE_ASSIGNMENT_OPERATOR(ClassName) ClassName & operator=(const ClassName &) EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EA_DISABLE_MOVE_OPERATOR + // + // Disables the compiler generated move operator. This macro is + // provided to improve portability and clarify intent of code. + // + // Example usage: + // + // class Example + // { + // private: + // EA_DISABLE_MOVE_OPERATOR(Example); + // }; + // + #define EA_DISABLE_MOVE_OPERATOR(ClassName) ClassName & operator=(ClassName&&) EA_FUNCTION_DELETE + + // ------------------------------------------------------------------------ + // EANonCopyable + // + // Declares a class as not supporting copy construction or assignment. + // May be more reliable with some situations that EA_NON_COPYABLE alone, + // though it may result in more code generation. + // + // Note that VC++ will generate warning C4625 and C4626 if you use EANonCopyable + // and you are compiling with /W4 and /Wall. There is no resolution but + // to redelare EA_NON_COPYABLE in your subclass or disable the warnings with + // code like this: + // EA_DISABLE_VC_WARNING(4625 4626) + // ... + // EA_RESTORE_VC_WARNING() + // + // Example usage: + // struct Widget : EANonCopyable { + // . . . + // }; + // + #ifdef __cplusplus + struct EANonCopyable + { + #if defined(EA_COMPILER_NO_DEFAULTED_FUNCTIONS) || defined(__EDG__) + // EDG doesn't appear to behave properly for the case of defaulted constructors; + // it generates a mistaken warning about missing default constructors. + EANonCopyable() {} // Putting {} here has the downside that it allows a class to create itself, + ~EANonCopyable() {} // but avoids linker errors that can occur with some compilers (e.g. Green Hills). + #else + EANonCopyable() = default; + ~EANonCopyable() = default; + #endif + + EA_NON_COPYABLE(EANonCopyable) + }; + #endif + + + // ------------------------------------------------------------------------ + // EA_OPTIMIZE_OFF / EA_OPTIMIZE_ON + // + // Implements portable inline optimization enabling/disabling. + // Usage of these macros must be in order OFF then ON. This is + // because the OFF macro pushes a set of settings and the ON + // macro pops them. The nesting of OFF/ON sets (e.g. OFF, OFF, ON, ON) + // is not guaranteed to work on all platforms. + // + // This is often used to allow debugging of some code that's + // otherwise compiled with undebuggable optimizations. It's also + // useful for working around compiler code generation problems + // that occur in optimized builds. + // + // Some compilers (e.g. VC++) don't allow doing this within a function and + // so the usage must be outside a function, as with the example below. + // GCC on x86 appears to have some problem with argument passing when + // using EA_OPTIMIZE_OFF in optimized builds. + // + // Example usage: + // // Disable optimizations for SomeFunction. + // EA_OPTIMIZE_OFF() + // void SomeFunction() + // { + // ... + // } + // EA_OPTIMIZE_ON() + // + #if !defined(EA_OPTIMIZE_OFF) + #if defined(EA_COMPILER_MSVC) + #define EA_OPTIMIZE_OFF() __pragma(optimize("", off)) + #elif defined(__ghs) + #define EA_OPTIMIZE_OFF() _Pragma("ghs ZO") + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION > 4004) && (defined(__i386__) || defined(__x86_64__)) // GCC 4.4+ - Seems to work only on x86/Linux so far. However, GCC 4.4 itself appears broken and screws up parameter passing conventions. + #define EA_OPTIMIZE_OFF() \ + _Pragma("GCC push_options") \ + _Pragma("GCC optimize 0") + #elif defined(EA_COMPILER_CLANG) && (!defined(EA_PLATFORM_ANDROID) || (EA_COMPILER_VERSION >= 380)) + #define EA_OPTIMIZE_OFF() \ + EA_DISABLE_CLANG_WARNING(-Wunknown-pragmas) \ + _Pragma("clang optimize off") \ + EA_RESTORE_CLANG_WARNING() + #else + #define EA_OPTIMIZE_OFF() + #endif + #endif + + #if !defined(EA_OPTIMIZE_ON) + #if defined(EA_COMPILER_MSVC) + #define EA_OPTIMIZE_ON() __pragma(optimize("", on)) + #elif defined(__ghs) + #define EA_OPTIMIZE_ON() _Pragma("ghs revertoptions") + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION > 4004) && (defined(__i386__) || defined(__x86_64__)) // GCC 4.4+ - Seems to work only on x86/Linux so far. However, GCC 4.4 itself appears broken and screws up parameter passing conventions. + #define EA_OPTIMIZE_ON() _Pragma("GCC pop_options") + #elif defined(EA_COMPILER_CLANG) && (!defined(EA_PLATFORM_ANDROID) || (EA_COMPILER_VERSION >= 380)) + #define EA_OPTIMIZE_ON() \ + EA_DISABLE_CLANG_WARNING(-Wunknown-pragmas) \ + _Pragma("clang optimize on") \ + EA_RESTORE_CLANG_WARNING() + #else + #define EA_OPTIMIZE_ON() + #endif + #endif + + + + // ------------------------------------------------------------------------ + // EA_SIGNED_RIGHT_SHIFT_IS_UNSIGNED + // + // Defined if right shifts of signed integers (i.e. arithmetic shifts) fail + // to propogate the high bit downward, and thus preserve sign. Most hardware + // and their corresponding compilers do this. + // + // + +#endif // Header include guard + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EABase/config/eaplatform.h b/r5dev/thirdparty/ea/EABase/config/eaplatform.h new file mode 100644 index 00000000..aef33aad --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/config/eaplatform.h @@ -0,0 +1,851 @@ +/*----------------------------------------------------------------------------- + * config/eaplatform.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *----------------------------------------------------------------------------- + * Currently supported platform indentification defines include: + */ +#ifdef EA_PLATFORM_PS4 // ifdef for code stripping purposes +// EA_PLATFORM_PS4 (EA_PLATFORM_KETTLE) +#endif +#ifdef EA_PLATFORM_XBOXONE // ifdef for code stripping purposes + // EA_PLATFORM_XBOXONE (EA_PLATFORM_CAPILANO) + // EA_PLATFORM_XBOXONE_XDK (EA_PLATFORM_CAPILANO_XDK), set by capilano_config package + // EA_PLATFORM_XBOXONE_ADK (EA_PLATFORM_CAPILANO_ADK), set by capilano_config package +#endif +#ifdef EA_PLATFORM_NX // ifdef for code stripping purposes + // EA_PLATFORM_NX +#endif +#ifdef EA_PLATFORM_XBSX // ifdef for code stripping purposes + // EA_PLATFORM_XBSX +#endif +// EA_PLATFORM_ANDROID +// EA_PLATFORM_APPLE +// EA_PLATFORM_IPHONE +// EA_PLATFORM_IPHONE_SIMULATOR +// EA_PLATFORM_OSX +// EA_PLATFORM_LINUX +// EA_PLATFORM_SAMSUNG_TV +// EA_PLATFORM_WINDOWS +// EA_PLATFORM_WIN32 +// EA_PLATFORM_WIN64 +// EA_PLATFORM_WINDOWS_PHONE +// EA_PLATFORM_WINRT +// EA_PLATFORM_SUN +// EA_PLATFORM_LRB (Larrabee) +// EA_PLATFORM_POSIX (pseudo-platform; may be defined along with another platform like EA_PLATFORM_LINUX, EA_PLATFORM_UNIX, EA_PLATFORM_QNX) +// EA_PLATFORM_UNIX (pseudo-platform; may be defined along with another platform like EA_PLATFORM_LINUX) +// EA_PLATFORM_CYGWIN (pseudo-platform; may be defined along with another platform like EA_PLATFORM_LINUX) +// EA_PLATFORM_MINGW (pseudo-platform; may be defined along with another platform like EA_PLATFORM_WINDOWS) +// EA_PLATFORM_MICROSOFT (pseudo-platform; may be defined along with another platform like EA_PLATFORM_WINDOWS) +// +// EA_ABI_ARM_LINUX (a.k.a. "eabi". for all platforms that use the CodeSourcery GNU/Linux toolchain, like Android) +// EA_ABI_ARM_APPLE (similar to eabi but not identical) +// EA_ABI_ARM64_APPLE (similar to eabi but not identical) https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html +// EA_ABI_ARM_WINCE (similar to eabi but not identical) +// +// Other definitions emanated from this file inclue: +// EA_PLATFORM_NAME = +// EA_PLATFORM_DESCRIPTION = +// EA_PROCESSOR_XXX +// EA_MISALIGNED_SUPPORT_LEVEL=0|1|2 +// EA_SYSTEM_LITTLE_ENDIAN | EA_SYSTEM_BIG_ENDIAN +// EA_ASM_STYLE_ATT | EA_ASM_STYLE_INTEL | EA_ASM_STYLE_MOTOROLA +// EA_PLATFORM_PTR_SIZE = +// EA_PLATFORM_WORD_SIZE = +// EA_CACHE_LINE_SIZE = +//--------------------------------------------------------------------------- + +/* + EA_PLATFORM_MOBILE + EA_PLATFORM_MOBILE is a peer to EA_PLATORM_DESKTOP and EA_PLATFORM_CONSOLE. Their definition is qualitative rather + than quantitative, and refers to the general (usually weaker) capabilities of the machine. Mobile devices have a + similar set of weaknesses that are useful to generally categorize. The primary motivation is to avoid code that + tests for multiple mobile platforms on a line and needs to be updated every time we get a new one. + For example, mobile platforms tend to have weaker ARM processors, don't have full multiple processor support, + are hand-held, don't have mice (though may have touch screens or basic cursor controls), have writable solid + state permanent storage. Production user code shouldn't have too many expectations about the meaning of this define. + + EA_PLATFORM_DESKTOP + This is similar to EA_PLATFORM_MOBILE in its qualitative nature and refers to platforms that are powerful. + For example, they nearly always have virtual memory, mapped memory, hundreds of GB of writable disk storage, + TCP/IP network connections, mice, keyboards, 512+ MB of RAM, multiprocessing, multiple display support. + Production user code shouldn't have too many expectations about the meaning of this define. + + EA_PLATFORM_CONSOLE + This is similar to EA_PLATFORM_MOBILE in its qualitative nature and refers to platforms that are consoles. + This means platforms that are connected to TVs, are fairly powerful (especially graphics-wise), are tightly + controlled by vendors, tend not to have mapped memory, tend to have TCP/IP, don't have multiple process support + though they might have multiple CPUs, support TV output only. Production user code shouldn't have too many + expectations about the meaning of this define. + +*/ + + +#ifndef INCLUDED_eaplatform_H +#define INCLUDED_eaplatform_H + + +// Cygwin +// This is a pseudo-platform which will be defined along with EA_PLATFORM_LINUX when +// using the Cygwin build environment. +#if defined(__CYGWIN__) + #define EA_PLATFORM_CYGWIN 1 + #define EA_PLATFORM_DESKTOP 1 +#endif + +// MinGW +// This is a pseudo-platform which will be defined along with EA_PLATFORM_WINDOWS when +// using the MinGW Windows build environment. +#if defined(__MINGW32__) || defined(__MINGW64__) + #define EA_PLATFORM_MINGW 1 + #define EA_PLATFORM_DESKTOP 1 +#endif + +#if defined(EA_PLATFORM_PS4) || defined(__ORBIS__) || defined(EA_PLATFORM_KETTLE) + // PlayStation 4 + // Orbis was Sony's code-name for the platform, which is now obsolete. + // Kettle was an EA-specific code-name for the platform, which is now obsolete. + #if defined(EA_PLATFORM_PS4) + #undef EA_PLATFORM_PS4 + #endif + #define EA_PLATFORM_PS4 1 + + // Backward compatibility: + #if defined(EA_PLATFORM_KETTLE) + #undef EA_PLATFORM_KETTLE + #endif + // End backward compatbility + + #define EA_PLATFORM_KETTLE 1 + #define EA_PLATFORM_NAME "PS4" + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "PS4 on x64" + #define EA_PLATFORM_CONSOLE 1 + #define EA_PLATFORM_SONY 1 + #define EA_PLATFORM_POSIX 1 + // #define EA_POSIX_THREADS_AVAILABLE 1 // POSIX threading API is available but discouraged. Sony indicated use of the scePthreads* API is preferred. + #define EA_PROCESSOR_X86_64 1 + #if defined(__GNUC__) || defined(__clang__) + #define EA_ASM_STYLE_ATT 1 + #endif + +#elif defined(EA_PLATFORM_PS5) + + #define EA_PLATFORM_PS5 1 + #define EA_PLATFORM_NAME "PS5" + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "PS5 on x64" + #define EA_PLATFORM_CONSOLE 1 + #define EA_PLATFORM_SONY 1 + #define EA_PLATFORM_POSIX 1 + // #define EA_POSIX_THREADS_AVAILABLE 1 // POSIX threading API is available but discouraged. Sony indicated use of the scePthreads* API is preferred. + #define EA_PROCESSOR_X86_64 1 + #if defined(__GNUC__) || defined(__clang__) + #define EA_ASM_STYLE_ATT 1 + #endif + +#elif defined(EA_PLATFORM_XBOXONE) || defined(_DURANGO) || defined(_XBOX_ONE) || defined(EA_PLATFORM_CAPILANO) || defined(_GAMING_XBOX_XBOXONE) + // XBox One + // Durango was Microsoft's code-name for the platform, which is now obsolete. + // Microsoft uses _DURANGO instead of some variation of _XBOX, though it's not natively defined by the compiler. + // Capilano was an EA-specific code-name for the platform, which is now obsolete. + #if defined(EA_PLATFORM_XBOXONE) + #undef EA_PLATFORM_XBOXONE + #endif + #define EA_PLATFORM_XBOXONE 1 + + // Backward compatibility: + #if defined(EA_PLATFORM_CAPILANO) + #undef EA_PLATFORM_CAPILANO + #endif + #define EA_PLATFORM_CAPILANO 1 + #if defined(EA_PLATFORM_CAPILANO_XDK) && !defined(EA_PLATFORM_XBOXONE_XDK) + #define EA_PLATFORM_XBOXONE_XDK 1 + #endif + #if defined(EA_PLATFORM_CAPILANO_ADK) && !defined(EA_PLATFORM_XBOXONE_ADK) + #define EA_PLATFORM_XBOXONE_ADK 1 + #endif + // End backward compatibility + + #if !defined(_DURANGO) + #define _DURANGO + #endif + #define EA_PLATFORM_NAME "XBox One" + //#define EA_PROCESSOR_X86 Currently our policy is that we don't define this, even though x64 is something of a superset of x86. + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "XBox One on x64" + #define EA_ASM_STYLE_INTEL 1 + #define EA_PLATFORM_CONSOLE 1 + #define EA_PLATFORM_MICROSOFT 1 + + // WINAPI_FAMILY defines - mirrored from winapifamily.h + #define EA_WINAPI_FAMILY_APP 1000 + #define EA_WINAPI_FAMILY_DESKTOP_APP 1001 + #define EA_WINAPI_FAMILY_PHONE_APP 1002 + #define EA_WINAPI_FAMILY_TV_APP 1003 + #define EA_WINAPI_FAMILY_TV_TITLE 1004 + #define EA_WINAPI_FAMILY_GAMES 1006 + + #if defined(WINAPI_FAMILY) + #include + #if defined(WINAPI_FAMILY_TV_TITLE) && WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_TV_TITLE + #elif defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_DESKTOP_APP + #elif defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_GAMES + #else + #error Unsupported WINAPI_FAMILY + #endif + #else + #error WINAPI_FAMILY should always be defined on Capilano. + #endif + + // Macro to determine if a partition is enabled. + #define EA_WINAPI_FAMILY_PARTITION(Partition) (Partition) + + #if EA_WINAPI_FAMILY == EA_WINAPI_FAMILY_DESKTOP_APP + #define EA_WINAPI_PARTITION_CORE 1 + #define EA_WINAPI_PARTITION_DESKTOP 1 + #define EA_WINAPI_PARTITION_APP 1 + #define EA_WINAPI_PARTITION_PC_APP 0 + #define EA_WIANPI_PARTITION_PHONE 0 + #define EA_WINAPI_PARTITION_TV_APP 0 + #define EA_WINAPI_PARTITION_TV_TITLE 0 + #define EA_WINAPI_PARTITION_GAMES 0 + #elif EA_WINAPI_FAMILY == EA_WINAPI_FAMILY_TV_TITLE + #define EA_WINAPI_PARTITION_CORE 1 + #define EA_WINAPI_PARTITION_DESKTOP 0 + #define EA_WINAPI_PARTITION_APP 0 + #define EA_WINAPI_PARTITION_PC_APP 0 + #define EA_WIANPI_PARTITION_PHONE 0 + #define EA_WINAPI_PARTITION_TV_APP 0 + #define EA_WINAPI_PARTITION_TV_TITLE 1 + #define EA_WINAPI_PARTITION_GAMES 0 + #elif EA_WINAPI_FAMILY == EA_WINAPI_FAMILY_GAMES + #define EA_WINAPI_PARTITION_CORE 1 + #define EA_WINAPI_PARTITION_DESKTOP 0 + #define EA_WINAPI_PARTITION_APP 0 + #define EA_WINAPI_PARTITION_PC_APP 0 + #define EA_WIANPI_PARTITION_PHONE 0 + #define EA_WINAPI_PARTITION_TV_APP 0 + #define EA_WINAPI_PARTITION_TV_TITLE 0 + #define EA_WINAPI_PARTITION_GAMES 1 + #else + #error Unsupported WINAPI_FAMILY + #endif + + #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_GAMES) + #define EA_PLATFORM_GDK 1 + #define EA_PLATFORM_XBOX_GDK 1 + #endif + + #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_TV_TITLE) + #define EA_PLATFORM_XBOXONE_XDK 1 + #endif +#elif defined(EA_PLATFORM_XBSX) || defined(_GAMING_XBOX_SCARLETT) + + #if !defined(_GAMING_XBOX_SCARLETT) + #define _GAMING_XBOX_SCARLETT + #endif + + #define EA_PLATFORM_GDK 1 + #define EA_PLATFORM_XBOX_GDK 1 + + #define EA_PLATFORM_NAME "XBox Series X" + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "XBox Series X on x64" + #define EA_ASM_STYLE_INTEL 1 + #define EA_PLATFORM_CONSOLE 1 + #define EA_PLATFORM_MICROSOFT 1 + + // WINAPI_FAMILY defines - mirrored from winapifamily.h + #define EA_WINAPI_FAMILY_APP 1000 + #define EA_WINAPI_FAMILY_DESKTOP_APP 1001 + #define EA_WINAPI_FAMILY_PHONE_APP 1002 + #define EA_WINAPI_FAMILY_TV_APP 1003 + #define EA_WINAPI_FAMILY_TV_TITLE 1004 + #define EA_WINAPI_FAMILY_GAMES 1006 + + #if defined(WINAPI_FAMILY) + #include + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_GAMES + #else + #error WINAPI_FAMILY should always be defined on Capilano. + #endif + + // Macro to determine if a partition is enabled. + #define EA_WINAPI_FAMILY_PARTITION(Partition) (Partition) + + #define EA_WINAPI_PARTITION_CORE 1 + #define EA_WINAPI_PARTITION_DESKTOP 0 + #define EA_WINAPI_PARTITION_APP 0 + #define EA_WINAPI_PARTITION_PC_APP 0 + #define EA_WIANPI_PARTITION_PHONE 0 + #define EA_WINAPI_PARTITION_TV_APP 0 + #define EA_WINAPI_PARTITION_TV_TITLE 0 + #define EA_WINAPI_PARTITION_GAMES 1 + +// Larrabee // This part to be removed once __LRB__ is supported by the Larrabee compiler in 2009. +#elif defined(EA_PLATFORM_LRB) || defined(__LRB__) || (defined(__EDG__) && defined(__ICC) && defined(__x86_64__)) + #undef EA_PLATFORM_LRB + #define EA_PLATFORM_LRB 1 + #define EA_PLATFORM_NAME "Larrabee" + #define EA_PLATFORM_DESCRIPTION "Larrabee on LRB1" + #define EA_PROCESSOR_X86_64 1 + #if defined(BYTE_ORDER) && (BYTE_ORDER == 4321) + #define EA_SYSTEM_BIG_ENDIAN 1 + #else + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #endif + #define EA_PROCESSOR_LRB 1 + #define EA_PROCESSOR_LRB1 1 // Larrabee version 1 + #define EA_ASM_STYLE_ATT 1 // Both types of asm style + #define EA_ASM_STYLE_INTEL 1 // are supported. + #define EA_PLATFORM_DESKTOP 1 + +// Android (Google phone OS) +#elif defined(EA_PLATFORM_ANDROID) || defined(__ANDROID__) + #undef EA_PLATFORM_ANDROID + #define EA_PLATFORM_ANDROID 1 + #define EA_PLATFORM_LINUX 1 + #define EA_PLATFORM_UNIX 1 + #define EA_PLATFORM_POSIX 1 + #define EA_PLATFORM_NAME "Android" + #define EA_ASM_STYLE_ATT 1 + #if defined(__arm__) + #define EA_ABI_ARM_LINUX 1 // a.k.a. "ARM eabi" + #define EA_PROCESSOR_ARM32 1 + #define EA_PLATFORM_DESCRIPTION "Android on ARM" + #elif defined(__aarch64__) + #define EA_PROCESSOR_ARM64 1 + #define EA_PLATFORM_DESCRIPTION "Android on ARM64" + #elif defined(__i386__) + #define EA_PROCESSOR_X86 1 + #define EA_PLATFORM_DESCRIPTION "Android on x86" + #elif defined(__x86_64) + #define EA_PROCESSOR_X86_64 1 + #define EA_PLATFORM_DESCRIPTION "Android on x64" + #else + #error Unknown processor + #endif + #if !defined(EA_SYSTEM_BIG_ENDIAN) && !defined(EA_SYSTEM_LITTLE_ENDIAN) + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #endif + #define EA_PLATFORM_MOBILE 1 + +// Samsung SMART TV - a Linux-based smart TV +#elif defined(EA_PLATFORM_SAMSUNG_TV) + #undef EA_PLATFORM_SAMSUNG_TV + #define EA_PLATFORM_SAMSUNG_TV 1 + #define EA_PLATFORM_LINUX 1 + #define EA_PLATFORM_UNIX 1 + #define EA_PLATFORM_POSIX 1 + #define EA_PLATFORM_NAME "SamsungTV" + #define EA_PLATFORM_DESCRIPTION "Samsung SMART TV on ARM" + #define EA_ASM_STYLE_ATT 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PROCESSOR_ARM32 1 + #define EA_ABI_ARM_LINUX 1 // a.k.a. "ARM eabi" + #define EA_PROCESSOR_ARM7 1 + +#elif defined(__APPLE__) && __APPLE__ + #include + + // Apple family of operating systems. + #define EA_PLATFORM_APPLE + #define EA_PLATFORM_POSIX 1 + + // iPhone + // TARGET_OS_IPHONE will be undefined on an unknown compiler, and will be defined on gcc. + #if defined(EA_PLATFORM_IPHONE) || defined(__IPHONE__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR) + #undef EA_PLATFORM_IPHONE + #define EA_PLATFORM_IPHONE 1 + #define EA_PLATFORM_NAME "iPhone" + #define EA_ASM_STYLE_ATT 1 + #define EA_POSIX_THREADS_AVAILABLE 1 + #if defined(__arm__) + #define EA_ABI_ARM_APPLE 1 + #define EA_PROCESSOR_ARM32 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "iPhone on ARM" + #elif defined(__aarch64__) || defined(__AARCH64) + #define EA_ABI_ARM64_APPLE 1 + #define EA_PROCESSOR_ARM64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "iPhone on ARM64" + #elif defined(__i386__) + #define EA_PLATFORM_IPHONE_SIMULATOR 1 + #define EA_PROCESSOR_X86 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "iPhone simulator on x86" + #elif defined(__x86_64) || defined(__amd64) + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "iPhone simulator on x64" + #else + #error Unknown processor + #endif + #define EA_PLATFORM_MOBILE 1 + + // Macintosh OSX + // TARGET_OS_MAC is defined by the Metrowerks and older AppleC compilers. + // Howerver, TARGET_OS_MAC is defined to be 1 in all cases. + // __i386__ and __intel__ are defined by the GCC compiler. + // __dest_os is defined by the Metrowerks compiler. + // __MACH__ is defined by the Metrowerks and GCC compilers. + // powerc and __powerc are defined by the Metrowerks and GCC compilers. + #elif defined(EA_PLATFORM_OSX) || defined(__MACH__) || (defined(__MSL__) && (__dest_os == __mac_os_x)) + #undef EA_PLATFORM_OSX + #define EA_PLATFORM_OSX 1 + #define EA_PLATFORM_UNIX 1 + #define EA_PLATFORM_POSIX 1 + //#define EA_PLATFORM_BSD 1 We don't currently define this. OSX has some BSD history but a lot of the API is different. + #define EA_PLATFORM_NAME "OSX" + #if defined(__i386__) || defined(__intel__) + #define EA_PROCESSOR_X86 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "OSX on x86" + #elif defined(__x86_64) || defined(__amd64) + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "OSX on x64" + #elif defined(__arm__) + #define EA_ABI_ARM_APPLE 1 + #define EA_PROCESSOR_ARM32 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "OSX on ARM" + #elif defined(__aarch64__) || defined(__AARCH64) + #define EA_ABI_ARM64_APPLE 1 + #define EA_PROCESSOR_ARM64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "OSX on ARM64" + #elif defined(__POWERPC64__) || defined(__powerpc64__) + #define EA_PROCESSOR_POWERPC 1 + #define EA_PROCESSOR_POWERPC_64 1 + #define EA_SYSTEM_BIG_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "OSX on PowerPC 64" + #elif defined(__POWERPC__) || defined(__powerpc__) + #define EA_PROCESSOR_POWERPC 1 + #define EA_PROCESSOR_POWERPC_32 1 + #define EA_SYSTEM_BIG_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "OSX on PowerPC" + #else + #error Unknown processor + #endif + #if defined(__GNUC__) + #define EA_ASM_STYLE_ATT 1 + #else + #define EA_ASM_STYLE_MOTOROLA 1 + #endif + #define EA_PLATFORM_DESKTOP 1 + #else + #error Unknown Apple Platform + #endif + +// Linux +// __linux and __linux__ are defined by the GCC and Borland compiler. +// __i386__ and __intel__ are defined by the GCC compiler. +// __i386__ is defined by the Metrowerks compiler. +// _M_IX86 is defined by the Borland compiler. +// __sparc__ is defined by the GCC compiler. +// __powerpc__ is defined by the GCC compiler. +// __ARM_EABI__ is defined by GCC on an ARM v6l (Raspberry Pi 1) +// __ARM_ARCH_7A__ is defined by GCC on an ARM v7l (Raspberry Pi 2) +#elif defined(EA_PLATFORM_LINUX) || (defined(__linux) || defined(__linux__)) + #undef EA_PLATFORM_LINUX + #define EA_PLATFORM_LINUX 1 + #define EA_PLATFORM_UNIX 1 + #define EA_PLATFORM_POSIX 1 + #define EA_PLATFORM_NAME "Linux" + #if defined(__i386__) || defined(__intel__) || defined(_M_IX86) + #define EA_PROCESSOR_X86 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Linux on x86" + #elif defined(__ARM_ARCH_7A__) || defined(__ARM_EABI__) + #define EA_ABI_ARM_LINUX 1 + #define EA_PROCESSOR_ARM32 1 + #define EA_PLATFORM_DESCRIPTION "Linux on ARM 6/7 32-bits" + #elif defined(__aarch64__) || defined(__AARCH64) + #define EA_PROCESSOR_ARM64 1 + #define EA_PLATFORM_DESCRIPTION "Linux on ARM64" + #elif defined(__x86_64__) + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Linux on x64" + #elif defined(__powerpc64__) + #define EA_PROCESSOR_POWERPC 1 + #define EA_PROCESSOR_POWERPC_64 1 + #define EA_SYSTEM_BIG_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Linux on PowerPC 64" + #elif defined(__powerpc__) + #define EA_PROCESSOR_POWERPC 1 + #define EA_PROCESSOR_POWERPC_32 1 + #define EA_SYSTEM_BIG_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Linux on PowerPC" + #else + #error Unknown processor + #error Unknown endianness + #endif + #if defined(__GNUC__) + #define EA_ASM_STYLE_ATT 1 + #endif + #define EA_PLATFORM_DESKTOP 1 + + +#elif defined(EA_PLATFORM_BSD) || (defined(__BSD__) || defined(__FreeBSD__)) + #undef EA_PLATFORM_BSD + #define EA_PLATFORM_BSD 1 + #define EA_PLATFORM_UNIX 1 + #define EA_PLATFORM_POSIX 1 // BSD's posix complaince is not identical to Linux's + #define EA_PLATFORM_NAME "BSD Unix" + #if defined(__i386__) || defined(__intel__) + #define EA_PROCESSOR_X86 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "BSD on x86" + #elif defined(__x86_64__) + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "BSD on x64" + #elif defined(__powerpc64__) + #define EA_PROCESSOR_POWERPC 1 + #define EA_PROCESSOR_POWERPC_64 1 + #define EA_SYSTEM_BIG_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "BSD on PowerPC 64" + #elif defined(__powerpc__) + #define EA_PROCESSOR_POWERPC 1 + #define EA_PROCESSOR_POWERPC_32 1 + #define EA_SYSTEM_BIG_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "BSD on PowerPC" + #else + #error Unknown processor + #error Unknown endianness + #endif + #if !defined(EA_PLATFORM_FREEBSD) && defined(__FreeBSD__) + #define EA_PLATFORM_FREEBSD 1 // This is a variation of BSD. + #endif + #if defined(__GNUC__) + #define EA_ASM_STYLE_ATT 1 + #endif + #define EA_PLATFORM_DESKTOP 1 + + +#elif defined(EA_PLATFORM_WINDOWS_PHONE) + #undef EA_PLATFORM_WINDOWS_PHONE + #define EA_PLATFORM_WINDOWS_PHONE 1 + #define EA_PLATFORM_NAME "Windows Phone" + #if defined(_M_AMD64) || defined(_AMD64_) || defined(__x86_64__) + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows Phone on x64" + #elif defined(_M_IX86) || defined(_X86_) + #define EA_PROCESSOR_X86 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows Phone on X86" + #elif defined(_M_ARM) + #define EA_ABI_ARM_WINCE 1 + #define EA_PROCESSOR_ARM32 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows Phone on ARM" + #else //Possibly other Windows Phone variants + #error Unknown processor + #error Unknown endianness + #endif + #define EA_PLATFORM_MICROSOFT 1 + + // WINAPI_FAMILY defines - mirrored from winapifamily.h + #define EA_WINAPI_FAMILY_APP 1 + #define EA_WINAPI_FAMILY_DESKTOP_APP 2 + #define EA_WINAPI_FAMILY_PHONE_APP 3 + + #if defined(WINAPI_FAMILY) + #include + #if WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_PHONE_APP + #else + #error Unsupported WINAPI_FAMILY for Windows Phone + #endif + #else + #error WINAPI_FAMILY should always be defined on Windows Phone. + #endif + + // Macro to determine if a partition is enabled. + #define EA_WINAPI_FAMILY_PARTITION(Partition) (Partition) + + // Enable the appropriate partitions for the current family + #if EA_WINAPI_FAMILY == EA_WINAPI_FAMILY_PHONE_APP + # define EA_WINAPI_PARTITION_CORE 1 + # define EA_WINAPI_PARTITION_PHONE 1 + # define EA_WINAPI_PARTITION_APP 1 + #else + # error Unsupported WINAPI_FAMILY for Windows Phone + #endif + + +// Windows +// _WIN32 is defined by the VC++, Intel and GCC compilers. +// _WIN64 is defined by the VC++, Intel and GCC compilers. +// __WIN32__ is defined by the Borland compiler. +// __INTEL__ is defined by the Metrowerks compiler. +// _M_IX86, _M_AMD64 and _M_IA64 are defined by the VC++, Intel, and Borland compilers. +// _X86_, _AMD64_, and _IA64_ are defined by the Metrowerks compiler. +// _M_ARM is defined by the VC++ compiler. +#elif (defined(EA_PLATFORM_WINDOWS) || (defined(_WIN32) || defined(__WIN32__) || defined(_WIN64))) && !defined(_XBOX) + #undef EA_PLATFORM_WINDOWS + #define EA_PLATFORM_WINDOWS 1 + #define EA_PLATFORM_NAME "Windows" + #ifdef _WIN64 // VC++ defines both _WIN32 and _WIN64 when compiling for Win64. + #define EA_PLATFORM_WIN64 1 + #else + #define EA_PLATFORM_WIN32 1 + #endif + #if defined(_M_AMD64) || defined(_AMD64_) || defined(__x86_64__) + #define EA_PROCESSOR_X86_64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows on x64" + #elif defined(_M_IX86) || defined(_X86_) + #define EA_PROCESSOR_X86 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows on X86" + #elif defined(_M_IA64) || defined(_IA64_) + #define EA_PROCESSOR_IA64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows on IA-64" + #elif defined(_M_ARM) + #define EA_ABI_ARM_WINCE 1 + #define EA_PROCESSOR_ARM32 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows on ARM" + #elif defined(_M_ARM64) + #define EA_PROCESSOR_ARM64 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "Windows on ARM64" + #else //Possibly other Windows CE variants + #error Unknown processor + #error Unknown endianness + #endif + #if defined(__GNUC__) + #define EA_ASM_STYLE_ATT 1 + #elif defined(_MSC_VER) || defined(__BORLANDC__) || defined(__ICL) + #define EA_ASM_STYLE_INTEL 1 + #endif + #define EA_PLATFORM_DESKTOP 1 + #define EA_PLATFORM_MICROSOFT 1 + + // WINAPI_FAMILY defines to support Windows 8 Metro Apps - mirroring winapifamily.h in the Windows 8 SDK + #define EA_WINAPI_FAMILY_APP 1000 + #define EA_WINAPI_FAMILY_DESKTOP_APP 1001 + #define EA_WINAPI_FAMILY_GAMES 1006 + + #if defined(WINAPI_FAMILY) + #if defined(_MSC_VER) + #pragma warning(push, 0) + #endif + #include + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + #if defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_DESKTOP_APP + #elif defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_APP + #elif defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_GAMES + #else + #error Unsupported WINAPI_FAMILY + #endif + #else + #define EA_WINAPI_FAMILY EA_WINAPI_FAMILY_DESKTOP_APP + #endif + + #define EA_WINAPI_PARTITION_DESKTOP 1 + #define EA_WINAPI_PARTITION_APP 1 + #define EA_WINAPI_PARTITION_GAMES (EA_WINAPI_FAMILY == EA_WINAPI_FAMILY_GAMES) + + #define EA_WINAPI_FAMILY_PARTITION(Partition) (Partition) + + // EA_PLATFORM_WINRT + // This is a subset of Windows which is used for tablets and the "Metro" (restricted) Windows user interface. + // WinRT doesn't doesn't have access to the Windows "desktop" API, but WinRT can nevertheless run on + // desktop computers in addition to tablets. The Windows Phone API is a subset of WinRT and is not included + // in it due to it being only a part of the API. + #if defined(__cplusplus_winrt) + #define EA_PLATFORM_WINRT 1 + #endif + +// Sun (Solaris) +// __SUNPRO_CC is defined by the Sun compiler. +// __sun is defined by the GCC compiler. +// __i386 is defined by the Sun and GCC compilers. +// __sparc is defined by the Sun and GCC compilers. +#elif defined(EA_PLATFORM_SUN) || (defined(__SUNPRO_CC) || defined(__sun)) + #undef EA_PLATFORM_SUN + #define EA_PLATFORM_SUN 1 + #define EA_PLATFORM_UNIX 1 + #define EA_PLATFORM_POSIX 1 + #define EA_PLATFORM_NAME "SUN" + #if defined(__i386) + #define EA_PROCESSOR_X86 1 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "SUN on x86" + #elif defined(__sparc) + #define EA_PROCESSOR_SPARC 1 + #define EA_SYSTEM_BIG_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "SUN on Sparc" + #else + #error Unknown processor + #error Unknown endianness + #endif + #define EA_PLATFORM_DESKTOP 1 + +#elif defined(EA_PLATFORM_NX) || defined(__NX) + #if defined(EA_PLATFORM_NX) + #undef EA_PLATFORM_NX + #endif + #define EA_PLATFORM_NX 1 + #define EA_PLATFORM_NAME "NX" + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_CONSOLE 1 + #define EA_PLATFORM_NINTENDO 1 + #define EA_PLATFORM_POSIX 1 + #define EA_POSIX_THREADS_AVAILABLE 1 + + #if defined(__aarch64__) + #define EA_PROCESSOR_ARM64 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "NX on ARM64" + #else + #define EA_PROCESSOR_ARM32 + #define EA_PROCESSOR_ARM7 + #define EA_SYSTEM_LITTLE_ENDIAN 1 + #define EA_PLATFORM_DESCRIPTION "NX on ARM" + #endif + + #if defined(__GNUC__) || defined(__clang__) + #define EA_ASM_STYLE_ATT 1 + #endif + +#else + #error Unknown platform + #error Unknown processor + #error Unknown endianness +#endif + +#ifndef EA_PROCESSOR_ARM + #if defined(EA_PROCESSOR_ARM32) || defined(EA_PROCESSOR_ARM64) || defined(EA_PROCESSOR_ARM7) + #define EA_PROCESSOR_ARM + #endif +#endif + +// EA_PLATFORM_PTR_SIZE +// Platform pointer size; same as sizeof(void*). +// This is not the same as sizeof(int), as int is usually 32 bits on +// even 64 bit platforms. +// +// _WIN64 is defined by Win64 compilers, such as VC++. +// _M_IA64 is defined by VC++ and Intel compilers for IA64 processors. +// __LP64__ is defined by HP compilers for the LP64 standard. +// _LP64 is defined by the GCC and Sun compilers for the LP64 standard. +// __ia64__ is defined by the GCC compiler for IA64 processors. +// __arch64__ is defined by the Sparc compiler for 64 bit processors. +// __mips64__ is defined by the GCC compiler for MIPS processors. +// __powerpc64__ is defined by the GCC compiler for PowerPC processors. +// __64BIT__ is defined by the AIX compiler for 64 bit processors. +// __sizeof_ptr is defined by the ARM compiler (armcc, armcpp). +// +#ifndef EA_PLATFORM_PTR_SIZE + #if defined(__WORDSIZE) // Defined by some variations of GCC. + #define EA_PLATFORM_PTR_SIZE ((__WORDSIZE) / 8) + #elif defined(_WIN64) || defined(__LP64__) || defined(_LP64) || defined(_M_IA64) || defined(__ia64__) || defined(__arch64__) || defined(__aarch64__) || defined(__mips64__) || defined(__64BIT__) || defined(__Ptr_Is_64) + #define EA_PLATFORM_PTR_SIZE 8 + #elif defined(__CC_ARM) && (__sizeof_ptr == 8) + #define EA_PLATFORM_PTR_SIZE 8 + #else + #define EA_PLATFORM_PTR_SIZE 4 + #endif +#endif + + + +// EA_PLATFORM_WORD_SIZE +// This defines the size of a machine word. This will be the same as +// the size of registers on the machine but not necessarily the same +// as the size of pointers on the machine. A number of 64 bit platforms +// have 64 bit registers but 32 bit pointers. +// +#ifndef EA_PLATFORM_WORD_SIZE + #define EA_PLATFORM_WORD_SIZE EA_PLATFORM_PTR_SIZE +#endif + +// EA_PLATFORM_MIN_MALLOC_ALIGNMENT +// This defines the minimal alignment that the platform's malloc +// implementation will return. This should be used when writing custom +// allocators to ensure that the alignment matches that of malloc +#ifndef EA_PLATFORM_MIN_MALLOC_ALIGNMENT + #if defined(EA_PLATFORM_APPLE) + #define EA_PLATFORM_MIN_MALLOC_ALIGNMENT 16 + #elif defined(EA_PLATFORM_ANDROID) && defined(EA_PROCESSOR_ARM) + #define EA_PLATFORM_MIN_MALLOC_ALIGNMENT 8 + #elif defined(EA_PLATFORM_ANDROID) && defined(EA_PROCESSOR_X86_64) + #define EA_PLATFORM_MIN_MALLOC_ALIGNMENT 8 + #else + #define EA_PLATFORM_MIN_MALLOC_ALIGNMENT (EA_PLATFORM_PTR_SIZE * 2) + #endif +#endif + + +// EA_MISALIGNED_SUPPORT_LEVEL +// Specifies if the processor can read and write built-in types that aren't +// naturally aligned. +// 0 - not supported. Likely causes an exception. +// 1 - supported but slow. +// 2 - supported and fast. +// +#ifndef EA_MISALIGNED_SUPPORT_LEVEL + #if defined(EA_PROCESSOR_X86_64) + #define EA_MISALIGNED_SUPPORT_LEVEL 2 + #else + #define EA_MISALIGNED_SUPPORT_LEVEL 0 + #endif +#endif + +// Macro to determine if a Windows API partition is enabled. Always false on non Microsoft platforms. +#if !defined(EA_WINAPI_FAMILY_PARTITION) + #define EA_WINAPI_FAMILY_PARTITION(Partition) (0) +#endif + + +// EA_CACHE_LINE_SIZE +// Specifies the cache line size broken down by compile target. +// This the expected best guess values for the targets that we can make at compilation time. + +#ifndef EA_CACHE_LINE_SIZE + #if defined(EA_PROCESSOR_X86) + #define EA_CACHE_LINE_SIZE 32 // This is the minimum possible value. + #elif defined(EA_PROCESSOR_X86_64) + #define EA_CACHE_LINE_SIZE 64 // This is the minimum possible value + #elif defined(EA_PROCESSOR_ARM32) + #define EA_CACHE_LINE_SIZE 32 // This varies between implementations and is usually 32 or 64. + #elif defined(EA_PROCESSOR_ARM64) + #define EA_CACHE_LINE_SIZE 64 // Cache line Cortex-A8 (64 bytes) http://shervinemami.info/armAssembly.html however this remains to be mostly an assumption at this stage + #elif (EA_PLATFORM_WORD_SIZE == 4) + #define EA_CACHE_LINE_SIZE 32 // This is the minimum possible value + #else + #define EA_CACHE_LINE_SIZE 64 // This is the minimum possible value + #endif +#endif + + +#endif // INCLUDED_eaplatform_H + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EABase/eabase.h b/r5dev/thirdparty/ea/EABase/eabase.h new file mode 100644 index 00000000..7febdddf --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/eabase.h @@ -0,0 +1,1024 @@ +/*----------------------------------------------------------------------------- + * eabase.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + + +#ifndef INCLUDED_eabase_H +#define INCLUDED_eabase_H + + +// Identify the compiler and declare the EA_COMPILER_xxxx defines +#include + +// Identify traits which this compiler supports, or does not support +#include + +// Identify the platform and declare the EA_xxxx defines +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +// Always include version.h for backwards compatibility. +#include + +// Define common SI unit macros +#include + + +// ------------------------------------------------------------------------ +// The C++ standard defines size_t as a built-in type. Some compilers are +// not standards-compliant in this respect, so we need an additional include. +// The case is similar with wchar_t under C++. + +#if defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_MSVC) || defined(EA_WCHAR_T_NON_NATIVE) || defined(EA_PLATFORM_SONY) + #if defined(EA_COMPILER_MSVC) + #pragma warning(push, 0) + #pragma warning(disable: 4265 4365 4836 4574) + #endif + #include + #if defined(EA_COMPILER_MSVC) + #pragma warning(pop) + #endif +#endif + +// ------------------------------------------------------------------------ +// Include stddef.h on Apple's clang compiler to ensure the ptrdiff_t type +// is defined. +#if defined(EA_COMPILER_CLANG) && defined(EA_PLATFORM_APPLE) + #include +#endif + +// ------------------------------------------------------------------------ +// Include assert.h on C11 supported compilers so we may allow static_assert usage +// http://en.cppreference.com/w/c/error/static_assert +// C11 standard(ISO / IEC 9899:2011) : +// 7.2/3 Diagnostics (p : 186) +#if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201100L + #include +#endif + + +// ------------------------------------------------------------------------ +// By default, GCC defines NULL as ((void*)0), which is the +// C definition. This causes all sort of problems for C++ code, so it is +// worked around by undefining NULL. + +#if defined(NULL) + #undef NULL +#endif + + +// ------------------------------------------------------------------------ +// Define the NULL pointer. This is normally defined in , but we +// don't want to force a global dependency on that header, so the definition +// is duplicated here. + +#if defined(__cplusplus) + #define NULL 0 +#else + #define NULL ((void*)0) +#endif + + +// ------------------------------------------------------------------------ +// C98/99 Standard typedefs. From the ANSI ISO/IEC 9899 standards document +// Most recent versions of the gcc-compiler come with these defined in +// inttypes.h or stddef.h. Determining if they are predefined can be +// tricky, so we expect some problems on non-standard compilers + +//#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) +// #error " was #included before eabase.h, but without __STDC_FORMAT_MACROS #defined. You must #include eabase.h or an equivalent before #including C99 headers, or you must define __STDC_FORMAT_MACRO before #including system headrs." +//#endif + +// ------------------------------------------------------------------------ +// We need to test this after we potentially include stddef.h, otherwise we +// would have put this into the compilertraits header. +#if !defined(EA_COMPILER_HAS_INTTYPES) && (!defined(_MSC_VER) || (_MSC_VER > 1500)) && (defined(EA_COMPILER_IS_C99) || defined(INT8_MIN) || defined(EA_COMPILER_HAS_C99_TYPES) || defined(_SN_STDINT_H)) + #define EA_COMPILER_HAS_INTTYPES +#endif + +#ifdef EA_COMPILER_HAS_INTTYPES // If the compiler supports inttypes... + // ------------------------------------------------------------------------ + // Include the stdint header to define and derive the required types. + // Additionally include inttypes.h as many compilers, including variations + // of GCC define things in inttypes.h that the C99 standard says goes + // in stdint.h. + // + // The C99 standard specifies that inttypes.h only define printf/scanf + // format macros if __STDC_FORMAT_MACROS is defined before #including + // inttypes.h. For consistency, we do that here. + #ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS + #endif + // The GCC PSP compiler defines standard int types (e.g. uint32_t) but not PRId8, etc. + // MSVC added support for inttypes.h header in VS2013. + #if !defined(EA_COMPILER_MSVC) || (defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1800) + #include // PRId8, SCNd8, etc. + #endif + #if defined(_MSC_VER) + #pragma warning(push, 0) + #endif + #include // int32_t, INT64_C, UINT8_MAX, etc. + #include // float_t, double_t, etc. + #include // FLT_EVAL_METHOD. + #if defined(_MSC_VER) + #pragma warning(pop) + #endif + + #if !defined(FLT_EVAL_METHOD) && (defined(__FLT_EVAL_METHOD__) || defined(_FEVAL)) // GCC 3.x defines __FLT_EVAL_METHOD__ instead of the C99 standard FLT_EVAL_METHOD. + #ifdef __FLT_EVAL_METHOD__ + #define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ + #else + #define FLT_EVAL_METHOD _FEVAL + #endif + #endif + + // MinGW GCC (up to at least v4.3.0-20080502) mistakenly neglects to define float_t and double_t. + // This appears to be an acknowledged bug as of March 2008 and is scheduled to be fixed. + // Similarly, Android uses a mix of custom standard library headers which prior to SDK API level 21 + // don't define float_t and double_t. + #if defined(__MINGW32__) || (defined(EA_PLATFORM_ANDROID) && !(defined(EA_ANDROID_SDK_LEVEL) && EA_ANDROID_SDK_LEVEL >= 21)) + #if defined(__FLT_EVAL_METHOD__) + #if(__FLT_EVAL_METHOD__== 0) + typedef float float_t; + typedef double double_t; + #elif(__FLT_EVAL_METHOD__ == 1) + typedef double float_t; + typedef double double_t; + #elif(__FLT_EVAL_METHOD__ == 2) + typedef long double float_t; + typedef long double double_t; + #endif + #else + typedef float float_t; + typedef double double_t; + #endif + #endif + + // The CodeSourcery definitions of PRIxPTR and SCNxPTR are broken for 32 bit systems. + #if defined(__SIZEOF_SIZE_T__) && (__SIZEOF_SIZE_T__ == 4) && (defined(__have_long64) || defined(__have_longlong64)) + #undef PRIdPTR + #define PRIdPTR "d" + #undef PRIiPTR + #define PRIiPTR "i" + #undef PRIoPTR + #define PRIoPTR "o" + #undef PRIuPTR + #define PRIuPTR "u" + #undef PRIxPTR + #define PRIxPTR "x" + #undef PRIXPTR + #define PRIXPTR "X" + + #undef SCNdPTR + #define SCNdPTR "d" + #undef SCNiPTR + #define SCNiPTR "i" + #undef SCNoPTR + #define SCNoPTR "o" + #undef SCNuPTR + #define SCNuPTR "u" + #undef SCNxPTR + #define SCNxPTR "x" + #endif +#else // else we must implement types ourselves. + + #if !defined(__BIT_TYPES_DEFINED__) && !defined(__int8_t_defined) + typedef signed char int8_t; //< 8 bit signed integer + #endif + #if !defined( __int8_t_defined ) + typedef signed short int16_t; //< 16 bit signed integer + typedef signed int int32_t; //< 32 bit signed integer. This works for both 32 bit and 64 bit platforms, as we assume the LP64 is followed. + #define __int8_t_defined + #endif + typedef unsigned char uint8_t; //< 8 bit unsigned integer + typedef unsigned short uint16_t; //< 16 bit unsigned integer + #if !defined( __uint32_t_defined ) + typedef unsigned int uint32_t; //< 32 bit unsigned integer. This works for both 32 bit and 64 bit platforms, as we assume the LP64 is followed. + #define __uint32_t_defined + #endif + + // According to the C98/99 standard, FLT_EVAL_METHOD defines control the + // width used for floating point _t types. + #if defined(_MSC_VER) && _MSC_VER >= 1800 + // MSVC's math.h provides float_t, double_t under this condition. + #elif defined(FLT_EVAL_METHOD) + #if (FLT_EVAL_METHOD == 0) + typedef float float_t; + typedef double double_t; + #elif (FLT_EVAL_METHOD == 1) + typedef double float_t; + typedef double double_t; + #elif (FLT_EVAL_METHOD == 2) + typedef long double float_t; + typedef long double double_t; + #endif + #endif + + #if defined(EA_PLATFORM_SUN) || defined(EA_PLATFORM_SGI) + #if (EA_PLATFORM_PTR_SIZE == 4) + typedef signed long long int64_t; + typedef unsigned long long uint64_t; + #else + typedef signed long int64_t; + typedef unsigned long uint64_t; + #endif + + #elif defined(EA_COMPILER_MSVC) + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + + #else + typedef signed long long int64_t; + typedef unsigned long long uint64_t; + #endif +#endif + + +// ------------------------------------------------------------------------ +// macros for declaring constants in a portable way. +// +// e.g. int64_t x = INT64_C(1234567812345678); +// e.g. int64_t x = INT64_C(0x1111111122222222); +// e.g. uint64_t x = UINT64_C(0x1111111122222222); +// +// Microsoft VC++'s definitions of INT8_C/UINT8_C/INT16_C/UINT16_C are like so: +// #define INT8_C(x) (x) +// #define INT16_C(x) (x) +// #define UINT8_C(x) (x) +// #define UINT16_C(x) (x) +// To consider: undefine Microsoft's and use the casting versions below. +// ------------------------------------------------------------------------ + +#ifndef INT8_C_DEFINED // If the user hasn't already defined these... + #define INT8_C_DEFINED + + #ifndef INT8_C + #define INT8_C(x) int8_t(x) // For the majority of compilers and platforms, long is 32 bits and long long is 64 bits. + #endif + #ifndef UINT8_C + #define UINT8_C(x) uint8_t(x) + #endif + #ifndef INT16_C + #define INT16_C(x) int16_t(x) + #endif + #ifndef UINT16_C + #define UINT16_C(x) uint16_t(x) // Possibly we should make this be uint16_t(x##u). Let's see how compilers react before changing this. + #endif + #ifndef INT32_C + #define INT32_C(x) x##L + #endif + #ifndef UINT32_C + #define UINT32_C(x) x##UL + #endif + #ifndef INT64_C + #define INT64_C(x) x##LL // The way to deal with this is to compare ULONG_MAX to 0xffffffff and if not equal, then remove the L. + #endif + #ifndef UINT64_C + #define UINT64_C(x) x##ULL // We need to follow a similar approach for LL. + #endif + #ifndef UINTMAX_C + #define UINTMAX_C(x) UINT64_C(x) + #endif +#endif + +// ------------------------------------------------------------------------ +// type sizes +#ifndef INT8_MAX_DEFINED // If the user hasn't already defined these... + #define INT8_MAX_DEFINED + + // The value must be 2^(n-1)-1 + #ifndef INT8_MAX + #define INT8_MAX 127 + #endif + #ifndef INT16_MAX + #define INT16_MAX 32767 + #endif + #ifndef INT32_MAX + #define INT32_MAX 2147483647 + #endif + #ifndef INT64_MAX + #define INT64_MAX INT64_C(9223372036854775807) + #endif + #ifndef INTMAX_MAX + #define INTMAX_MAX INT64_MAX + #endif + #ifndef INTPTR_MAX + #if EA_PLATFORM_PTR_SIZE == 4 + #define INTPTR_MAX INT32_MAX + #else + #define INTPTR_MAX INT64_MAX + #endif + #endif + + // The value must be either -2^(n-1) or 1-2(n-1). + #ifndef INT8_MIN + #define INT8_MIN -128 + #endif + #ifndef INT16_MIN + #define INT16_MIN -32768 + #endif + #ifndef INT32_MIN + #define INT32_MIN (-INT32_MAX - 1) // -2147483648 + #endif + #ifndef INT64_MIN + #define INT64_MIN (-INT64_MAX - 1) // -9223372036854775808 + #endif + #ifndef INTMAX_MIN + #define INTMAX_MIN INT64_MIN + #endif + #ifndef INTPTR_MIN + #if EA_PLATFORM_PTR_SIZE == 4 + #define INTPTR_MIN INT32_MIN + #else + #define INTPTR_MIN INT64_MIN + #endif + #endif + + // The value must be 2^n-1 + #ifndef UINT8_MAX + #define UINT8_MAX 0xffU // 255 + #endif + #ifndef UINT16_MAX + #define UINT16_MAX 0xffffU // 65535 + #endif + #ifndef UINT32_MAX + #define UINT32_MAX UINT32_C(0xffffffff) // 4294967295 + #endif + #ifndef UINT64_MAX + #define UINT64_MAX UINT64_C(0xffffffffffffffff) // 18446744073709551615 + #endif + #ifndef UINTMAX_MAX + #define UINTMAX_MAX UINT64_MAX + #endif + #ifndef UINTPTR_MAX + #if EA_PLATFORM_PTR_SIZE == 4 + #define UINTPTR_MAX UINT32_MAX + #else + #define UINTPTR_MAX UINT64_MAX + #endif + #endif +#endif + +#ifndef FLT_EVAL_METHOD + #define FLT_EVAL_METHOD 0 + typedef float float_t; + typedef double double_t; +#endif + +#if defined(EA_COMPILER_HAS_INTTYPES) && (!defined(EA_COMPILER_MSVC) || (defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1800)) + #define EA_COMPILER_HAS_C99_FORMAT_MACROS +#endif + +#ifndef EA_COMPILER_HAS_C99_FORMAT_MACROS + // ------------------------------------------------------------------------ + // sized printf and scanf format specifiers + // See the C99 standard, section 7.8.1 -- Macros for format specifiers. + // + // The C99 standard specifies that inttypes.h only define printf/scanf + // format macros if __STDC_FORMAT_MACROS is defined before #including + // inttypes.h. For consistency, we define both __STDC_FORMAT_MACROS and + // the printf format specifiers here. We also skip the "least/most" + // variations of these specifiers, as we've decided to do so with + // basic types. + // + // For 64 bit systems, we assume the LP64 standard is followed + // (as opposed to ILP64, etc.) For 32 bit systems, we assume the + // ILP32 standard is followed. See: + // http://www.opengroup.org/public/tech/aspen/lp64_wp.htm + // for information about this. Thus, on both 32 and 64 bit platforms, + // %l refers to 32 bit data while %ll refers to 64 bit data. + + #ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS + #endif + + #if defined(EA_COMPILER_MSVC) // VC++ 7.1+ understands long long as a data type but doesn't accept %ll as a printf specifier. + #define EA_PRI_64_LENGTH_SPECIFIER "I64" + #define EA_SCN_64_LENGTH_SPECIFIER "I64" + #else + #define EA_PRI_64_LENGTH_SPECIFIER "ll" + #define EA_SCN_64_LENGTH_SPECIFIER "ll" + #endif // It turns out that some platforms use %q to represent a 64 bit value, but these are not relevant to us at this time. + + // Printf format specifiers + #if defined(EA_COMPILER_IS_C99) || defined(EA_COMPILER_GNUC) + #define PRId8 "hhd" + #define PRIi8 "hhi" + #define PRIo8 "hho" + #define PRIu8 "hhu" + #define PRIx8 "hhx" + #define PRIX8 "hhX" + #else // VC++, Borland, etc. which have no way to specify 8 bit values other than %c. + #define PRId8 "c" // This may not work properly but it at least will not crash. Try using 16 bit versions instead. + #define PRIi8 "c" // " + #define PRIo8 "o" // " + #define PRIu8 "u" // " + #define PRIx8 "x" // " + #define PRIX8 "X" // " + #endif + + #define PRId16 "hd" + #define PRIi16 "hi" + #define PRIo16 "ho" + #define PRIu16 "hu" + #define PRIx16 "hx" + #define PRIX16 "hX" + + #define PRId32 "d" // This works for both 32 bit and 64 bit systems, as we assume LP64 conventions. + #define PRIi32 "i" + #define PRIo32 "o" + #define PRIu32 "u" + #define PRIx32 "x" + #define PRIX32 "X" + + #define PRId64 EA_PRI_64_LENGTH_SPECIFIER "d" + #define PRIi64 EA_PRI_64_LENGTH_SPECIFIER "i" + #define PRIo64 EA_PRI_64_LENGTH_SPECIFIER "o" + #define PRIu64 EA_PRI_64_LENGTH_SPECIFIER "u" + #define PRIx64 EA_PRI_64_LENGTH_SPECIFIER "x" + #define PRIX64 EA_PRI_64_LENGTH_SPECIFIER "X" + + #if (EA_PLATFORM_PTR_SIZE == 4) + #define PRIdPTR PRId32 // Usage of pointer values will generate warnings with + #define PRIiPTR PRIi32 // some compilers because they are defined in terms of + #define PRIoPTR PRIo32 // integers. However, you can't simply use "p" because + #define PRIuPTR PRIu32 // 'p' is interpreted in a specific and often different + #define PRIxPTR PRIx32 // way by the library. + #define PRIXPTR PRIX32 + #elif (EA_PLATFORM_PTR_SIZE == 8) + #define PRIdPTR PRId64 + #define PRIiPTR PRIi64 + #define PRIoPTR PRIo64 + #define PRIuPTR PRIu64 + #define PRIxPTR PRIx64 + #define PRIXPTR PRIX64 + #endif + + // Scanf format specifiers + #if defined(EA_COMPILER_IS_C99) || defined(EA_COMPILER_GNUC) + #define SCNd8 "hhd" + #define SCNi8 "hhi" + #define SCNo8 "hho" + #define SCNu8 "hhu" + #define SCNx8 "hhx" + #else // VC++, Borland, etc. which have no way to specify 8 bit values other than %c. + #define SCNd8 "c" // This will not work properly but it at least will not crash. Try using 16 bit versions instead. + #define SCNi8 "c" // " + #define SCNo8 "c" // " + #define SCNu8 "c" // " + #define SCNx8 "c" // " + #endif + + #define SCNd16 "hd" + #define SCNi16 "hi" + #define SCNo16 "ho" + #define SCNu16 "hu" + #define SCNx16 "hx" + + #define SCNd32 "d" // This works for both 32 bit and 64 bit systems, as we assume LP64 conventions. + #define SCNi32 "i" + #define SCNo32 "o" + #define SCNu32 "u" + #define SCNx32 "x" + + #define SCNd64 EA_SCN_64_LENGTH_SPECIFIER "d" + #define SCNi64 EA_SCN_64_LENGTH_SPECIFIER "i" + #define SCNo64 EA_SCN_64_LENGTH_SPECIFIER "o" + #define SCNu64 EA_SCN_64_LENGTH_SPECIFIER "u" + #define SCNx64 EA_SCN_64_LENGTH_SPECIFIER "x" + + #if defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1900) + #define SCNdPTR PRIdPTR + #define SCNiPTR PRIiPTR + #define SCNoPTR PRIoPTR + #define SCNuPTR PRIuPTR + #define SCNxPTR PRIxPTR + #elif (EA_PLATFORM_PTR_SIZE == 4) + #define SCNdPTR SCNd32 // Usage of pointer values will generate warnings with + #define SCNiPTR SCNi32 // some compilers because they are defined in terms of + #define SCNoPTR SCNo32 // integers. However, you can't simply use "p" because + #define SCNuPTR SCNu32 // 'p' is interpreted in a specific and often different + #define SCNxPTR SCNx32 // way by the library. + #elif (EA_PLATFORM_PTR_SIZE == 8) + #define SCNdPTR SCNd64 + #define SCNiPTR SCNi64 + #define SCNoPTR SCNo64 + #define SCNuPTR SCNu64 + #define SCNxPTR SCNx64 + #endif +#endif + + +// ------------------------------------------------------------------------ +// bool8_t +// The definition of a bool8_t is controversial with some, as it doesn't +// act just like built-in bool. For example, you can assign -100 to it. +// +#ifndef BOOL8_T_DEFINED // If the user hasn't already defined this... + #define BOOL8_T_DEFINED + #if defined(EA_COMPILER_MSVC) || (defined(EA_COMPILER_INTEL) && defined(EA_PLATFORM_WINDOWS)) + #if defined(__cplusplus) + typedef bool bool8_t; + #else + typedef int8_t bool8_t; + #endif + #else // EA_COMPILER_GNUC generally uses 4 bytes per bool. + typedef int8_t bool8_t; + #endif +#endif + + +// ------------------------------------------------------------------------ +// intptr_t / uintptr_t +// Integer type guaranteed to be big enough to hold +// a native pointer ( intptr_t is defined in STDDEF.H ) +// +#if !defined(_INTPTR_T_DEFINED) && !defined(_intptr_t_defined) && !defined(EA_COMPILER_HAS_C99_TYPES) + #if (EA_PLATFORM_PTR_SIZE == 4) + typedef int32_t intptr_t; + #elif (EA_PLATFORM_PTR_SIZE == 8) + typedef int64_t intptr_t; + #endif + + #define _intptr_t_defined + #define _INTPTR_T_DEFINED +#endif + +#if !defined(_UINTPTR_T_DEFINED) && !defined(_uintptr_t_defined) && !defined(EA_COMPILER_HAS_C99_TYPES) + #if (EA_PLATFORM_PTR_SIZE == 4) + typedef uint32_t uintptr_t; + #elif (EA_PLATFORM_PTR_SIZE == 8) + typedef uint64_t uintptr_t; + #endif + + #define _uintptr_t_defined + #define _UINTPTR_T_DEFINED +#endif + +#if !defined(EA_COMPILER_HAS_INTTYPES) + #ifndef INTMAX_T_DEFINED + #define INTMAX_T_DEFINED + + // At this time, all supported compilers have int64_t as the max + // integer type. Some compilers support a 128 bit integer type, + // but in some cases it is not a true int128_t but rather a + // crippled data type. Also, it turns out that Unix 64 bit ABIs + // require that intmax_t be int64_t and nothing larger. So we + // play it safe here and set intmax_t to int64_t, even though + // an int128_t type may exist. + + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; + #endif +#endif + + +// ------------------------------------------------------------------------ +// ssize_t +// signed equivalent to size_t. +// This is defined by GCC (except the QNX implementation of GCC) but not by other compilers. +// +#if !defined(__GNUC__) + // As of this writing, all non-GCC compilers significant to us implement + // uintptr_t the same as size_t. However, this isn't guaranteed to be + // so for all compilers, as size_t may be based on int, long, or long long. + #if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) + #define _SSIZE_T_ + #define _SSIZE_T_DEFINED + + #if defined(_MSC_VER) && (EA_PLATFORM_PTR_SIZE == 8) + typedef __int64 ssize_t; + #else + typedef long ssize_t; + #endif + #endif +#else + #include +#endif + + +// ------------------------------------------------------------------------ +// Character types +// +#if defined(EA_COMPILER_MSVC) + #if defined(EA_WCHAR_T_NON_NATIVE) + // In this case, wchar_t is not defined unless we include + // wchar.h or if the compiler makes it built-in. + #ifdef EA_COMPILER_MSVC + #pragma warning(push, 3) + #endif + #include + #ifdef EA_COMPILER_MSVC + #pragma warning(pop) + #endif + #endif +#endif + + +// ------------------------------------------------------------------------ +// char8_t -- Guaranteed to be equal to the compiler's char data type. +// Some compilers implement char8_t as unsigned, though char +// is usually set to be signed. +// +// char16_t -- This is set to be an unsigned 16 bit value. If the compiler +// has wchar_t as an unsigned 16 bit value, then char16_t is +// set to be the same thing as wchar_t in order to allow the +// user to use char16_t with standard wchar_t functions. +// +// char32_t -- This is set to be an unsigned 32 bit value. If the compiler +// has wchar_t as an unsigned 32 bit value, then char32_t is +// set to be the same thing as wchar_t in order to allow the +// user to use char32_t with standard wchar_t functions. +// +// EA_CHAR8_UNIQUE +// EA_CHAR16_NATIVE +// EA_CHAR32_NATIVE +// EA_WCHAR_UNIQUE +// +// VS2010 unilaterally defines char16_t and char32_t in its yvals.h header +// unless _HAS_CHAR16_T_LANGUAGE_SUPPORT or _CHAR16T are defined. +// However, VS2010 does not support the C++0x u"" and U"" string literals, +// which makes its definition of char16_t and char32_t somewhat useless. +// Until VC++ supports string literals, the build system should define +// _CHAR16T and let EABase define char16_t and EA_CHAR16. +// +// GCC defines char16_t and char32_t in the C compiler in -std=gnu99 mode, +// as __CHAR16_TYPE__ and __CHAR32_TYPE__, and for the C++ compiler +// in -std=c++0x and -std=gnu++0x modes, as char16_t and char32_t too. +// +// The EA_WCHAR_UNIQUE symbol is defined to 1 if wchar_t is distinct from +// char8_t, char16_t, and char32_t, and defined to 0 if not. In some cases, +// if the compiler does not support char16_t/char32_t, one of these two types +// is typically a typedef or define of wchar_t. For compilers that support +// the C++11 unicode character types often overloads must be provided to +// support existing code that passes a wide char string to a function that +// takes a unicode string. +// +// The EA_CHAR8_UNIQUE symbol is defined to 1 if char8_t is distinct type +// from char in the type system, and defined to 0 if otherwise. + +#if !defined(EA_CHAR16_NATIVE) + // To do: Change this to be based on EA_COMPILER_NO_NEW_CHARACTER_TYPES. + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && defined(_HAS_CHAR16_T_LANGUAGE_SUPPORT) && _HAS_CHAR16_T_LANGUAGE_SUPPORT // VS2010+ + #define EA_CHAR16_NATIVE 1 + #elif defined(EA_COMPILER_CLANG) && defined(EA_COMPILER_CPP11_ENABLED) + #if __has_feature(cxx_unicode_literals) + #define EA_CHAR16_NATIVE 1 + #elif (EA_COMPILER_VERSION >= 300) && !(defined(EA_PLATFORM_IPHONE) || defined(EA_PLATFORM_OSX)) + #define EA_CHAR16_NATIVE 1 + #elif defined(EA_PLATFORM_APPLE) + #define EA_CHAR16_NATIVE 1 + #else + #define EA_CHAR16_NATIVE 0 + #endif + #elif defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 404) && defined(__CHAR16_TYPE__) && defined(EA_COMPILER_CPP11_ENABLED)// EDG 4.4+. + #define EA_CHAR16_NATIVE 1 + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) && !defined(EA_COMPILER_EDG) && (defined(EA_COMPILER_CPP11_ENABLED) || defined(__STDC_VERSION__)) // g++ (C++ compiler) 4.4+ with -std=c++0x or gcc (C compiler) 4.4+ with -std=gnu99 + #define EA_CHAR16_NATIVE 1 + #elif defined(EA_COMPILER_SN) && (__EDG_VERSION__ >= 408) && defined(EA_COMPILER_CPP11_ENABLED) + #define EA_CHAR16_NATIVE 1 + #else + #define EA_CHAR16_NATIVE 0 + #endif +#endif + +#if !defined(EA_CHAR32_NATIVE) // Microsoft currently ties char32_t language support to char16_t language support. So we use CHAR16_T here. + // To do: Change this to be based on EA_COMPILER_NO_NEW_CHARACTER_TYPES. + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && defined(_HAS_CHAR16_T_LANGUAGE_SUPPORT) && _HAS_CHAR16_T_LANGUAGE_SUPPORT // VS2010+ + #define EA_CHAR32_NATIVE 1 + #elif defined(EA_COMPILER_CLANG) && defined(EA_COMPILER_CPP11_ENABLED) + #if __has_feature(cxx_unicode_literals) + #define EA_CHAR32_NATIVE 1 + #elif (EA_COMPILER_VERSION >= 300) && !(defined(EA_PLATFORM_IPHONE) || defined(EA_PLATFORM_OSX)) + #define EA_CHAR32_NATIVE 1 + #elif defined(EA_PLATFORM_APPLE) + #define EA_CHAR32_NATIVE 1 + #else + #define EA_CHAR32_NATIVE 0 + #endif + #elif defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 404) && defined(__CHAR32_TYPE__) && defined(EA_COMPILER_CPP11_ENABLED)// EDG 4.4+. + #define EA_CHAR32_NATIVE 1 + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) && !defined(EA_COMPILER_EDG) && (defined(EA_COMPILER_CPP11_ENABLED) || defined(__STDC_VERSION__)) // g++ (C++ compiler) 4.4+ with -std=c++0x or gcc (C compiler) 4.4+ with -std=gnu99 + #define EA_CHAR32_NATIVE 1 +#elif defined(EA_COMPILER_SN) && (__EDG_VERSION__ >= 408) && defined(EA_COMPILER_CPP11_ENABLED) + #define EA_CHAR32_NATIVE 1 + #else + #define EA_CHAR32_NATIVE 0 + #endif +#endif + + +#if EA_CHAR16_NATIVE || EA_CHAR32_NATIVE + #define EA_WCHAR_UNIQUE 1 +#else + #define EA_WCHAR_UNIQUE 0 +#endif + + +// EA_CHAR8_UNIQUE +// +// Check for char8_t support in the cpp type system. Moving forward from c++20, +// the char8_t type allows users to overload function for character encoding. +// +// EA_CHAR8_UNIQUE is 1 when the type is a unique in the type system and +// can there be used as a valid overload. EA_CHAR8_UNIQUE is 0 otherwise. +// +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0482r6.html +// +#ifdef __cpp_char8_t + #define CHAR8_T_DEFINED + #define EA_CHAR8_UNIQUE 1 +#else + #define EA_CHAR8_UNIQUE 0 +#endif + + +#ifndef CHAR8_T_DEFINED // If the user hasn't already defined these... + #define CHAR8_T_DEFINED + #if defined(EA_PLATFORM_APPLE) + #define char8_t char // The Apple debugger is too stupid to realize char8_t is typedef'd to char, so we #define it. + #else + typedef char char8_t; + #endif + + #if EA_CHAR16_NATIVE + // In C++, char16_t and char32_t are already defined by the compiler. + // In MS C, char16_t and char32_t are already defined by the compiler/standard library. + // In GCC C, __CHAR16_TYPE__ and __CHAR32_TYPE__ are defined instead, and we must define char16_t and char32_t from these. + #if defined(__GNUC__) && !defined(__GXX_EXPERIMENTAL_CXX0X__) && defined(__CHAR16_TYPE__) // If using GCC and compiling in C... + typedef __CHAR16_TYPE__ char16_t; + typedef __CHAR32_TYPE__ char32_t; + #endif + #elif (EA_WCHAR_SIZE == 2) + #if (defined(_MSC_VER) && (_MSC_VER >= 1600)) // if VS2010+ or using platforms that use Dinkumware under a compiler that doesn't natively support C++11 char16_t. + #if !defined(_CHAR16T) + #define _CHAR16T + #endif + #if !defined(_HAS_CHAR16_T_LANGUAGE_SUPPORT) || !_HAS_CHAR16_T_LANGUAGE_SUPPORT + typedef wchar_t char16_t; + typedef uint32_t char32_t; + #endif + #else + typedef wchar_t char16_t; + typedef uint32_t char32_t; + #endif + #else + typedef uint16_t char16_t; + #if defined(__cplusplus) + typedef wchar_t char32_t; + #else + typedef uint32_t char32_t; + #endif + #endif +#endif + + +// CHAR8_MIN, CHAR8_MAX, etc. +// +#define EA_LIMITS_DIGITS_S(T) ((sizeof(T) * 8) - 1) +#define EA_LIMITS_DIGITS_U(T) ((sizeof(T) * 8)) +#define EA_LIMITS_DIGITS(T) ((EA_LIMITS_IS_SIGNED(T) ? EA_LIMITS_DIGITS_S(T) : EA_LIMITS_DIGITS_U(T))) +#define EA_LIMITS_IS_SIGNED(T) ((T)(-1) < 0) +#define EA_LIMITS_MIN_S(T) ((T)((T)1 << EA_LIMITS_DIGITS_S(T))) +#define EA_LIMITS_MIN_U(T) ((T)0) +#define EA_LIMITS_MIN(T) ((EA_LIMITS_IS_SIGNED(T) ? EA_LIMITS_MIN_S(T) : EA_LIMITS_MIN_U(T))) +#define EA_LIMITS_MAX_S(T) ((T)(((((T)1 << (EA_LIMITS_DIGITS(T) - 1)) - 1) << 1) + 1)) +#define EA_LIMITS_MAX_U(T) ((T)~(T)0) +#define EA_LIMITS_MAX(T) ((EA_LIMITS_IS_SIGNED(T) ? EA_LIMITS_MAX_S(T) : EA_LIMITS_MAX_U(T))) + +#if !defined(CHAR8_MIN) + #define CHAR8_MIN EA_LIMITS_MIN(char8_t) +#endif + +#if !defined(CHAR8_MAX) + #define CHAR8_MAX EA_LIMITS_MAX(char8_t) +#endif + +#if !defined(CHAR16_MIN) + #define CHAR16_MIN EA_LIMITS_MIN(char16_t) +#endif + +#if !defined(CHAR16_MAX) + #define CHAR16_MAX EA_LIMITS_MAX(char16_t) +#endif + +#if !defined(CHAR32_MIN) + #define CHAR32_MIN EA_LIMITS_MIN(char32_t) +#endif + +#if !defined(CHAR32_MAX) + #define CHAR32_MAX EA_LIMITS_MAX(char32_t) +#endif + + + +// EA_CHAR8 / EA_CHAR16 / EA_CHAR32 / EA_WCHAR +// +// Supports usage of portable string constants. +// +// Example usage: +// const char16_t* str = EA_CHAR16("Hello world"); +// const char32_t* str = EA_CHAR32("Hello world"); +// const char16_t c = EA_CHAR16('\x3001'); +// const char32_t c = EA_CHAR32('\x3001'); +// +#ifndef EA_CHAR8 + #if EA_CHAR8_UNIQUE + #define EA_CHAR8(s) u8 ## s + #else + #define EA_CHAR8(s) s + #endif +#endif + +#ifndef EA_WCHAR + #define EA_WCHAR_(s) L ## s + #define EA_WCHAR(s) EA_WCHAR_(s) +#endif + +#ifndef EA_CHAR16 + #if EA_CHAR16_NATIVE && !defined(_MSC_VER) // Microsoft doesn't support char16_t string literals. + #define EA_CHAR16_(s) u ## s + #define EA_CHAR16(s) EA_CHAR16_(s) + #elif (EA_WCHAR_SIZE == 2) + #if defined(_MSC_VER) && (_MSC_VER >= 1900) && defined(__cplusplus) // VS2015 supports u"" string literals. + #define EA_CHAR16_(s) u ## s + #define EA_CHAR16(s) EA_CHAR16_(s) + #else + #define EA_CHAR16_(s) L ## s + #define EA_CHAR16(s) EA_CHAR16_(s) + #endif + #else + //#define EA_CHAR16(s) // Impossible to implement efficiently. + #endif +#endif + +#ifndef EA_CHAR32 + #if EA_CHAR32_NATIVE && !defined(_MSC_VER) // Microsoft doesn't support char32_t string literals. + #define EA_CHAR32_(s) U ## s + #define EA_CHAR32(s) EA_CHAR32_(s) + #elif (EA_WCHAR_SIZE == 2) + #if defined(_MSC_VER) && (_MSC_VER >= 1900) && defined(__cplusplus) // VS2015 supports u"" string literals. + #define EA_CHAR32_(s) U ## s + #define EA_CHAR32(s) EA_CHAR32_(s) + #else + //#define EA_CHAR32(s) // Impossible to implement. + #endif + #elif (EA_WCHAR_SIZE == 4) + #define EA_CHAR32_(s) L ## s + #define EA_CHAR32(s) EA_CHAR32_(s) + #else + #error Unexpected size of wchar_t + #endif +#endif + +// EAText8 / EAText16 +// +// Provided for backwards compatibility with older code. +// +#if defined(EABASE_ENABLE_EATEXT_MACROS) + #define EAText8(x) x + #define EAChar8(x) x + + #define EAText16(x) EA_CHAR16(x) + #define EAChar16(x) EA_CHAR16(x) +#endif + + + + +// ------------------------------------------------------------------------ +// EAArrayCount +// +// Returns the count of items in a built-in C array. This is a common technique +// which is often used to help properly calculate the number of items in an +// array at runtime in order to prevent overruns, etc. +// +// Example usage: +// int array[75]; +// size_t arrayCount = EAArrayCount(array); // arrayCount is 75. +// +#if defined(EA_COMPILER_NO_CONSTEXPR) + #ifndef EAArrayCount + #define EAArrayCount(x) (sizeof(x) / sizeof(x[0])) + #endif +#else + // This C++11 version is a little smarter than the macro version above; + // it can tell the difference between arrays and pointers. Other simpler + // templated versions have failed in various subtle ways. + + template + char (&EAArraySizeHelper(T (&x)[N]))[N]; + + template + char (&EAArraySizeHelper(T (&&x)[N]))[N]; + + #define EAArrayCount(x) (sizeof(EAArraySizeHelper(x))) +#endif + + +// ------------------------------------------------------------------------ +// static_assert +// +// C++11 static_assert (a.k.a. compile-time assert). +// +// Specification: +// void static_assert(bool const_expression, const char* description); +// +// Example usage: +// static_assert(sizeof(int) == 4, "int must be 32 bits"); +// +#if defined(_MSC_VER) && (_MSC_VER >= 1600) && defined(__cplusplus) + // static_assert is defined by the compiler for both C and C++. +#elif !defined(__cplusplus) && defined(EA_PLATFORM_ANDROID) && ((defined(__STDC_VERSION__) && __STDC_VERSION__ < 201100L) || !defined(__STDC_VERSION__)) + // AndroidNDK does not support static_assert despite claiming it's a C11 compiler + #define NEED_CUSTOM_STATIC_ASSERT +#elif defined(__clang__) && defined(__cplusplus) + // We need to separate these checks on a new line, as the pre-processor on other compilers will fail on the _has_feature macros + #if !(__has_feature(cxx_static_assert) || __has_extension(cxx_static_assert)) + #define NEED_CUSTOM_STATIC_ASSERT + #endif +#elif defined(__GNUC__) && (defined(__GXX_EXPERIMENTAL_CXX0X__) || (defined(__cplusplus) && (__cplusplus >= 201103L))) + // static_assert is defined by the compiler. +#elif defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 401) && defined(EA_COMPILER_CPP11_ENABLED) + // static_assert is defined by the compiler. +#elif !defined(__cplusplus) && defined(__GLIBC__) && defined(__USE_ISOC11) + // static_assert is defined by the compiler. +#elif !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201100L + // static_assert is defined by the compiler. +#else + #define NEED_CUSTOM_STATIC_ASSERT +#endif + +#ifdef NEED_CUSTOM_STATIC_ASSERT + #ifdef __GNUC__ + // On GCC the 'unused' attribute can be used to indicate a typedef is not actually used + // (such as in the static_assert implementation below). New versions of GCC generate + // warnings for unused typedefs in function/method scopes. + #define EA_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) + #else + #define EA_STATIC_ASSERT_UNUSED_ATTRIBUTE + #endif + #define EA_STATIC_ASSERT_TOKEN_PASTE(a,b) a ## b + #define EA_STATIC_ASSERT_CONCATENATE_HELPER(a,b) EA_STATIC_ASSERT_TOKEN_PASTE(a,b) + + #if defined(__COUNTER__) // If this extension is available, which allows multiple statements per line... + #define static_assert(expression, description) typedef char EA_STATIC_ASSERT_CONCATENATE_HELPER(compileTimeAssert,__COUNTER__) [((expression) != 0) ? 1 : -1] EA_STATIC_ASSERT_UNUSED_ATTRIBUTE + #else + #define static_assert(expression, description) typedef char EA_STATIC_ASSERT_CONCATENATE_HELPER(compileTimeAssert,__LINE__) [((expression) != 0) ? 1 : -1] EA_STATIC_ASSERT_UNUSED_ATTRIBUTE + #endif + + #undef NEED_CUSTOM_STATIC_ASSERT +#endif + +// ------------------------------------------------------------------------ +// EA_IS_ENABLED +// +// EA_IS_ENABLED is intended to be used for detecting if compile time features are enabled or disabled. +// +// It has some advantages over using a standard #if or #ifdef tests: +// 1) Fails to compile when passes numeric macro values. Valid options are strictly enabled or disabled. +// 2) Fails to compile when passed undefined macro values rather than disabling by default +// 3) Fails to compile when the passed macro is defined to but empty +// +// To use the macro, the calling code should create a define for the feature to enable or disable. This feature define +// must be set to either EA_ENABLED or EA_DISABLED. (Do not try to set the feature define directly to some other +// value.) +// +// Note: These macros are analogous to the Frostbite macro FB_USING used in combination with FB_OFF / FB_ON and are +// designed to be compatible to support gradual migration. +// +// Example usage: +// +// // The USER_PROVIDED_FEATURE_DEFINE should be defined as either +// // EA_ENABLED or EA_DISABLED. +// #define USER_PROVIDED_FEATURE_DEFINE EA_ENABLED +// +// #if EA_IS_ENABLED(USER_PROVIDED_FEATURE_DEFINE) +// // USER_PROVIDED_FEATURE_DEFINE is enabled +// #else +// // USER_PROVIDED_FEATURE_DEFINE is disabled +// #endif +// +#define EA_ENABLED 111- +#define EA_DISABLED 333- +// NOTE: Numeric values for x will produce a parse error while empty values produce a divide by zero, and the test is a bool for proper negation behavior +#define EA_IS_ENABLED(x) (333 == 333 * 111 / ((x 0) * (((x 0) == 333 ? 1 : 0) + ((x 0) == 111 ? 1 : 0)))) + + + +// Define int128_t / uint128_t types. +// NOTE(rparolin): include file at the end because we want all the signed integral types defined. +#ifdef __cplusplus + #include +#endif + +#endif // Header include guard + + + + diff --git a/r5dev/thirdparty/ea/EABase/eadeprecated.h b/r5dev/thirdparty/ea/EABase/eadeprecated.h new file mode 100644 index 00000000..aff3682d --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/eadeprecated.h @@ -0,0 +1,181 @@ +/*----------------------------------------------------------------------------- + * eadeprecatedbase.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + +#pragma once +#ifndef INCLUDED_eadeprecated_H +#define INCLUDED_eadeprecated_H + +#include + + +// ------------------------------------------------------------------------ +// Documentation on deprecated attribute: https://en.cppreference.com/w/cpp/language/attributes/deprecated +// Documentation on SimVer version numbers: http://simver.org/ +// +// These macros provide a structured formatting to C++ deprecated annotation messages. This ensures +// that the required information is presented in a standard format for developers and tools. +// +// Example usage: +// +// Current package version : current_ver +// Future version for code removal : major_ver, minor_ver, change_ver +// Deprecation comment : "" +// +// EA_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated function") +// void TestFunc() {} +// +// EA_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated typedef") +// typedef int TestTypedef; +// +// EA_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated variable") +// int TestVariable; +// +// EA_DEPRECATED_STRUCT(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated struct") +// TestStruct {}; +// +// EA_DEPRECATED_CLASS(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated class") +// TestClass {}; +// +// union TestUnion +// { +// EA_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated data member") int n; +// }; +// +// EA_DEPRECATED_ENUM(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated enumeration") +// TestEnumeration { TestEnumeration_Value1, TestEnumeration_Value2 }; +// +// enum TestEnumerator +// { +// TestEnumerator_Value1 EA_DEPRECATED_ENUMVALUE(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated enum value") = 5, +// TestEnumerator_Value2 = 4 +// }; +// +// EA_DISABLE_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Suppress the deprecated warning until the given Release") +// void TestFunc() {} +// EA_RESTORE_DEPRECATED() +// + +// ------------------------------------------------------------------------ +// Create an integer version number which can be compared with numerical operators +#define EA_CREATE_VERSION(MAJOR, MINOR, PATCH) \ + (((MAJOR) * 1000000) + (((MINOR) + 1) * 10000) + (((PATCH) + 1) * 100)) + +// ------------------------------------------------------------------------ +// INTERNAL MACROS - DO NOT USE DIRECTLY +// +// When EA_DEPRECATED_API_EXPIRED_IS_ERROR this macro produce a static asset on code that is past the expiry date. + +#if defined(EA_DEPRECATED_API_EXPIRED_IS_ERROR) && EA_DEPRECATED_API_EXPIRED_IS_ERROR + #define EA_DEPRECATED_BEFORETYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation) \ + static_assert(_moduleVersion < EA_CREATE_VERSION(_major_version,_minor_version,_patch_version), "This API has been deprecated and needs to be removed"); +#else + #define EA_DEPRECATED_BEFORETYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation) +#endif + + +// ------------------------------------------------------------------------ +// INTERNAL MACROS - DO NOT USE DIRECTLY +// +// When EA_IGNORE_DEPRECATION is set deprecation annotation will not be produced + +#if defined(EA_IGNORE_DEPRECATION) && EA_IGNORE_DEPRECATION + #define EA_DEPRECATED_AFTERTYPE(_major_version, _minor_version, _patch_version, _annotation, _msg) +#else + #define EA_DEPRECATED_AFTERTYPE(_major_version, _minor_version, _patch_version, _annotation, _msg) \ + EA_DEPRECATED_MESSAGE(_msg. This API will be removed in _major_version._minor_version._patch_version _annotation) +#endif + +// ------------------------------------------------------------------------ +// INTERNAL MACROS - DO NOT USE DIRECTLY +// +// Simple case + +#define EA_DEPRECATED_SIMPLE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg) \ + EA_DEPRECATED_BEFORETYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation) \ + EA_DEPRECATED_AFTERTYPE(_major_version, _minor_version, _patch_version, _annotation, _msg) + + +// ------------------------------------------------------------------------ +// INTERNAL MACROS - DO NOT USE DIRECTLY +// +// Macro which inserts the keyword to correctly format the deprecation annotation +#define EA_DEPRECATED_TYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg, _keyword) \ + EA_DEPRECATED_BEFORETYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation) \ + _keyword \ + EA_DEPRECATED_AFTERTYPE(_major_version, _minor_version, _patch_version, _annotation, _msg) + + + +// ------------------------------------------------------------------------ +// PUBLIC MACROS +// See file header comment for example usage. + +// ------------------------------------------------------------------------ +// EA_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated function") +// void TestFunc() {} +// +// EA_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated typedef") +// typedef int TestTypedef; +// +// EA_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated variable") +// int TestVariable; + +#define EA_DEPRECATED_API(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg) \ + EA_STRINGIFY(EA_DEPRECATED_SIMPLE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg)) + + +// ------------------------------------------------------------------------ +// EA_DEPRECATED_STRUCT(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated struct") +// TestStruct {}; + +#define EA_DEPRECATED_STRUCT(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg) \ + EA_STRINGIFY(EA_DEPRECATED_TYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg, struct)) + + +// ------------------------------------------------------------------------ +// EA_DEPRECATED_CLASS(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated class") +// TestClass {}; + +#define EA_DEPRECATED_CLASS(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg) \ + EA_STRINGIFY(EA_DEPRECATED_TYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg, class)) + + +// ------------------------------------------------------------------------ +// EA_DEPRECATED_ENUM(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated enumeration") +// TestEnumeration { TestEnumeration_Value1, TestEnumeration_Value2 }; + +#define EA_DEPRECATED_ENUM(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg) \ + EA_STRINGIFY(EA_DEPRECATED_TYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg, enum)) + + +// ------------------------------------------------------------------------ +// enum TestEnumerator +// { +// TestEnumerator_Value1 EA_DEPRECATED_ENUMVALUE(current_ver, major_ver, minor_ver, change_ver, tag, "Do not use deprecated enum value") = 5, +// TestEnumerator_Value2 = 4 +// }; +#define EA_DEPRECATED_ENUMVALUE(_value, _moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg) \ + _value EA_STRINGIFY(EA_DEPRECATED_AFTERTYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation)) + + +// ------------------------------------------------------------------------ +// Suppress deprecated warnings around a block of code, see file comment for full usage. +// EA_DISABLE_DEPRECATED(current_ver, major_ver, minor_ver, change_ver, tag, "Suppress the deprecated warning until the given Release") + +#define EA_DISABLE_DEPRECATED(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation, _msg) \ + EA_STRINGIFY(EA_DEPRECATED_BEFORETYPE(_moduleVersion, _major_version, _minor_version, _patch_version, _annotation)) \ + EA_DISABLE_VC_WARNING(4996); \ + EA_DISABLE_CLANG_WARNING(-Wdeprecated-declarations); + +// ------------------------------------------------------------------------ +// Restore the compiler warnings +// EA_RESTORE_DEPRECATED() + +#define EA_RESTORE_DEPRECATED() \ + EA_RESTORE_CLANG_WARNING(); \ + EA_RESTORE_VC_WARNING(); + +#endif // Header include guard \ No newline at end of file diff --git a/r5dev/thirdparty/ea/EABase/eahave.h b/r5dev/thirdparty/ea/EABase/eahave.h new file mode 100644 index 00000000..b25e515b --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/eahave.h @@ -0,0 +1,881 @@ +/*----------------------------------------------------------------------------- + * eahave.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------- + This file's functionality is preliminary and won't be considered stable until + a future EABase version. + *---------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------- + This header identifies if the given facilities are available in the + standard build environment the current compiler/linker/standard library/ + operating system combination. This file may in some cases #include standard + headers in order to make availability determinations, such as to check + compiler or SDK version numbers. However, it cannot be perfect. + This header does not identify compiler features, as those are defined in + eacompiler.h and eacompilertraits.h. Rather this header is about library support. + This header does not identify platform or library conventions either, such + as whether the file paths use \ or / for directory separators. + + We provide three types of HAVE features here: + + - EA_HAVE_XXX_FEATURE - Have compiler feature. + Identifies if the compiler has or lacks some feature in the + current build. Sometimes you need to check to see if the + compiler is running in some mode in able to write portable code + against it. For example, some compilers (e.g. VC++) have a + mode in which all language extensions are disabled. If you want + to write code that works with that but still uses the extensions + when available then you can check #if defined(EA_HAVE_EXTENSIONS_FEATURE). + Features can be forcibly cancelled via EA_NO_HAVE_XXX_FEATURE. + EA_NO_HAVE is useful for a build system or user to override the + defaults because it happens to know better. + + - EA_HAVE_XXX_H - Have header file information. + Identifies if a given header file is available to the current + compile configuration. For example, some compilers provide a + malloc.h header, while others don't. For the former we define + EA_HAVE_MALLOC_H, while for the latter it remains undefined. + If a header is missing then it may still be that the functions + the header usually declares are declared in some other header. + EA_HAVE_XXX does not include the possibility that our own code + provides versions of these headers, and in fact a purpose of + EA_HAVE_XXX is to decide if we should be using our own because + the system doesn't provide one. + Header availability can be forcibly cancelled via EA_NO_HAVE_XXX_H. + EA_NO_HAVE is useful for a build system or user to override the + defaults because it happens to know better. + + - EA_HAVE_XXX_DECL - Have function declaration information. + Identifies if a given function declaration is provided by + the current compile configuration. For example, some compiler + standard libraries declare a wcslen function, while others + don't. For the former we define EA_HAVE_WCSLEN_DECL, while for + the latter it remains undefined. If a declaration of a function + is missing then we assume the implementation is missing as well. + EA_HAVE_XXX_DECL does not include the possibility that our + own code provides versions of these declarations, and in fact a + purpose of EA_HAVE_XXX_DECL is to decide if we should be using + our own because the system doesn't provide one. + Declaration availability can be forcibly cancelled via EA_NO_HAVE_XXX_DECL. + EA_NO_HAVE is useful for a build system or user to override the + defaults because it happens to know better. + + - EA_HAVE_XXX_IMPL - Have function implementation information. + Identifies if a given function implementation is provided by + the current compile and link configuration. For example, it's + commonly the case that console platforms declare a getenv function + but don't provide a linkable implementation. + In this case the user needs to provide such a function manually + as part of the link. If the implementation is available then + we define EA_HAVE_GETENV_IMPL, otherwise it remains undefined. + Beware that sometimes a function may not seem to be present in + the Standard Library but in reality you need to link some auxiliary + provided library for it. An example of this is the Unix real-time + functions such as clock_gettime. + EA_HAVE_XXX_IMPL does not include the possibility that our + own code provides versions of these implementations, and in fact a + purpose of EA_HAVE_XXX_IMPL is to decide if we should be using + our own because the system doesn't provide one. + Implementation availability can be forcibly cancelled via EA_NO_HAVE_XXX_IMPL. + EA_NO_HAVE is useful for a build system or user to override the + defaults because it happens to know better. + + It's not practical to define EA_HAVE macros for every possible header, + declaration, and implementation, and so the user must simply know that + some headers, declarations, and implementations tend to require EA_HAVE + checking. Nearly every C Standard Library we've seen has a + header, a strlen declaration, and a linkable strlen implementation, + so there's no need to provide EA_HAVE support for this. On the other hand + it's commonly the case that the C Standard Library doesn't have a malloc.h + header or an inet_ntop declaration. + +---------------------------------------------------------------------------*/ + + +#ifndef INCLUDED_eahave_H +#define INCLUDED_eahave_H + + +#include + + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +/* EA_HAVE_XXX_FEATURE */ + +#if !defined(EA_HAVE_EXTENSIONS_FEATURE) && !defined(EA_NO_HAVE_EXTENSIONS_FEATURE) + #define EA_HAVE_EXTENSIONS_FEATURE 1 +#endif + + +/* EA_HAVE_XXX_LIBRARY */ + +// Dinkumware +#if !defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && !defined(EA_NO_HAVE_DINKUMWARE_CPP_LIBRARY) + #if defined(__cplusplus) + EA_DISABLE_ALL_VC_WARNINGS() + #include // Need to trigger the compilation of yvals.h without directly using because it might not exist. + EA_RESTORE_ALL_VC_WARNINGS() + #endif + + #if defined(__cplusplus) && defined(_CPPLIB_VER) /* If using the Dinkumware Standard library... */ + #define EA_HAVE_DINKUMWARE_CPP_LIBRARY 1 + #else + #define EA_NO_HAVE_DINKUMWARE_CPP_LIBRARY 1 + #endif +#endif + +// GCC libstdc++ +#if !defined(EA_HAVE_LIBSTDCPP_LIBRARY) && !defined(EA_NO_HAVE_LIBSTDCPP_LIBRARY) + #if defined(__GLIBCXX__) /* If using libstdc++ ... */ + #define EA_HAVE_LIBSTDCPP_LIBRARY 1 + #else + #define EA_NO_HAVE_LIBSTDCPP_LIBRARY 1 + #endif +#endif + +// Clang libc++ +#if !defined(EA_HAVE_LIBCPP_LIBRARY) && !defined(EA_NO_HAVE_LIBCPP_LIBRARY) + #if EA_HAS_INCLUDE_AVAILABLE + #if EA_HAS_INCLUDE(<__config>) + #define EA_HAVE_LIBCPP_LIBRARY 1 // We could also #include and check if defined(_LIBCPP_VERSION). + #endif + #endif + + #if !defined(EA_HAVE_LIBCPP_LIBRARY) + #define EA_NO_HAVE_LIBCPP_LIBRARY 1 + #endif +#endif + + +/* EA_HAVE_XXX_H */ + +// #include +#if !defined(EA_HAVE_SYS_TYPES_H) && !defined(EA_NO_HAVE_SYS_TYPES_H) + #define EA_HAVE_SYS_TYPES_H 1 +#endif + +// #include (and not sys/io.h or asm/io.h) +#if !defined(EA_HAVE_IO_H) && !defined(EA_NO_HAVE_IO_H) + // Unix doesn't have Microsoft's but has the same functionality in and . + #if defined(EA_PLATFORM_MICROSOFT) + #define EA_HAVE_IO_H 1 + #else + #define EA_NO_HAVE_IO_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_INTTYPES_H) && !defined(EA_NO_HAVE_INTTYPES_H) + #if !defined(EA_PLATFORM_MICROSOFT) + #define EA_HAVE_INTTYPES_H 1 + #else + #define EA_NO_HAVE_INTTYPES_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_UNISTD_H) && !defined(EA_NO_HAVE_UNISTD_H) + #if defined(EA_PLATFORM_UNIX) + #define EA_HAVE_UNISTD_H 1 + #else + #define EA_NO_HAVE_UNISTD_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_SYS_TIME_H) && !defined(EA_NO_HAVE_SYS_TIME_H) + #if !defined(EA_PLATFORM_MICROSOFT) && !defined(_CPPLIB_VER) /* _CPPLIB_VER indicates Dinkumware. */ + #define EA_HAVE_SYS_TIME_H 1 /* defines struct timeval */ + #else + #define EA_NO_HAVE_SYS_TIME_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_SYS_PTRACE_H) && !defined(EA_NO_HAVE_SYS_PTRACE_H) + #if defined(EA_PLATFORM_UNIX) && !defined(__CYGWIN__) && (defined(EA_PLATFORM_DESKTOP) || defined(EA_PLATFORM_SERVER)) + #define EA_HAVE_SYS_PTRACE_H 1 /* declares the ptrace function */ + #else + #define EA_NO_HAVE_SYS_PTRACE_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_SYS_STAT_H) && !defined(EA_NO_HAVE_SYS_STAT_H) + #if (defined(EA_PLATFORM_UNIX) && !(defined(EA_PLATFORM_SONY) && defined(EA_PLATFORM_CONSOLE))) || defined(__APPLE__) || defined(EA_PLATFORM_ANDROID) + #define EA_HAVE_SYS_STAT_H 1 /* declares the stat struct and function */ + #else + #define EA_NO_HAVE_SYS_STAT_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_LOCALE_H) && !defined(EA_NO_HAVE_LOCALE_H) + #define EA_HAVE_LOCALE_H 1 +#endif + +// #include +#if !defined(EA_HAVE_SIGNAL_H) && !defined(EA_NO_HAVE_SIGNAL_H) + #if !defined(EA_PLATFORM_BSD) && !defined(EA_PLATFORM_SONY) && !defined(EA_PLATFORM_NX) + #define EA_HAVE_SIGNAL_H 1 + #else + #define EA_NO_HAVE_SIGNAL_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_SYS_SIGNAL_H) && !defined(EA_NO_HAVE_SYS_SIGNAL_H) + #if defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_SONY) + #define EA_HAVE_SYS_SIGNAL_H 1 + #else + #define EA_NO_HAVE_SYS_SIGNAL_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_PTHREAD_H) && !defined(EA_NO_HAVE_PTHREAD_H) + #if defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_POSIX) + #define EA_HAVE_PTHREAD_H 1 /* It can be had under Microsoft/Windows with the http://sourceware.org/pthreads-win32/ library */ + #else + #define EA_NO_HAVE_PTHREAD_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_WCHAR_H) && !defined(EA_NO_HAVE_WCHAR_H) + #if defined(EA_PLATFORM_DESKTOP) && defined(EA_PLATFORM_UNIX) && defined(EA_PLATFORM_SONY) && defined(EA_PLATFORM_APPLE) + #define EA_HAVE_WCHAR_H 1 + #else + #define EA_NO_HAVE_WCHAR_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_MALLOC_H) && !defined(EA_NO_HAVE_MALLOC_H) + #if defined(_MSC_VER) || defined(__MINGW32__) + #define EA_HAVE_MALLOC_H 1 + #else + #define EA_NO_HAVE_MALLOC_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_ALLOCA_H) && !defined(EA_NO_HAVE_ALLOCA_H) + #if !defined(EA_HAVE_MALLOC_H) && !defined(EA_PLATFORM_SONY) + #define EA_HAVE_ALLOCA_H 1 + #else + #define EA_NO_HAVE_ALLOCA_H 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_EXECINFO_H) && !defined(EA_NO_HAVE_EXECINFO_H) + #if (defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_OSX)) && !defined(EA_PLATFORM_ANDROID) + #define EA_HAVE_EXECINFO_H 1 + #else + #define EA_NO_HAVE_EXECINFO_H 1 + #endif +#endif + +// #include (Unix semaphore support) +#if !defined(EA_HAVE_SEMAPHORE_H) && !defined(EA_NO_HAVE_SEMAPHORE_H) + #if defined(EA_PLATFORM_UNIX) + #define EA_HAVE_SEMAPHORE_H 1 + #else + #define EA_NO_HAVE_SEMAPHORE_H 1 + #endif +#endif + +// #include (Unix semaphore support) +#if !defined(EA_HAVE_DIRENT_H) && !defined(EA_NO_HAVE_DIRENT_H) + #if defined(EA_PLATFORM_UNIX) && !defined(EA_PLATFORM_CONSOLE) + #define EA_HAVE_DIRENT_H 1 + #else + #define EA_NO_HAVE_DIRENT_H 1 + #endif +#endif + +// #include , , , +#if !defined(EA_HAVE_CPP11_CONTAINERS) && !defined(EA_NO_HAVE_CPP11_CONTAINERS) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_CONTAINERS 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) // Actually GCC 4.3 supports array and unordered_ + #define EA_HAVE_CPP11_CONTAINERS 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_CONTAINERS 1 + #else + #define EA_NO_HAVE_CPP11_CONTAINERS 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_ATOMIC) && !defined(EA_NO_HAVE_CPP11_ATOMIC) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_ATOMIC 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) + #define EA_HAVE_CPP11_ATOMIC 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_ATOMIC 1 + #else + #define EA_NO_HAVE_CPP11_ATOMIC 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_CONDITION_VARIABLE) && !defined(EA_NO_HAVE_CPP11_CONDITION_VARIABLE) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_CONDITION_VARIABLE 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) + #define EA_HAVE_CPP11_CONDITION_VARIABLE 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_CONDITION_VARIABLE 1 + #else + #define EA_NO_HAVE_CPP11_CONDITION_VARIABLE 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_MUTEX) && !defined(EA_NO_HAVE_CPP11_MUTEX) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_MUTEX 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) + #define EA_HAVE_CPP11_MUTEX 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_MUTEX 1 + #else + #define EA_NO_HAVE_CPP11_MUTEX 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_THREAD) && !defined(EA_NO_HAVE_CPP11_THREAD) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_THREAD 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) + #define EA_HAVE_CPP11_THREAD 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_THREAD 1 + #else + #define EA_NO_HAVE_CPP11_THREAD 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_FUTURE) && !defined(EA_NO_HAVE_CPP11_FUTURE) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_FUTURE 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4005) + #define EA_HAVE_CPP11_FUTURE 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_FUTURE 1 + #else + #define EA_NO_HAVE_CPP11_FUTURE 1 + #endif +#endif + + +// #include +#if !defined(EA_HAVE_CPP11_TYPE_TRAITS) && !defined(EA_NO_HAVE_CPP11_TYPE_TRAITS) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_TYPE_TRAITS 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) // Prior versions of libstdc++ have incomplete support for C++11 type traits. + #define EA_HAVE_CPP11_TYPE_TRAITS 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_TYPE_TRAITS 1 + #else + #define EA_NO_HAVE_CPP11_TYPE_TRAITS 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_TUPLES) && !defined(EA_NO_HAVE_CPP11_TUPLES) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_TUPLES 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003) + #define EA_HAVE_CPP11_TUPLES 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_TUPLES 1 + #else + #define EA_NO_HAVE_CPP11_TUPLES 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_REGEX) && !defined(EA_NO_HAVE_CPP11_REGEX) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) && (defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_REGEX 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4003) + #define EA_HAVE_CPP11_REGEX 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_REGEX 1 + #else + #define EA_NO_HAVE_CPP11_REGEX 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_RANDOM) && !defined(EA_NO_HAVE_CPP11_RANDOM) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_RANDOM 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4005) + #define EA_HAVE_CPP11_RANDOM 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_RANDOM 1 + #else + #define EA_NO_HAVE_CPP11_RANDOM 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_CHRONO) && !defined(EA_NO_HAVE_CPP11_CHRONO) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_CHRONO 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) // chrono was broken in glibc prior to 4.7. + #define EA_HAVE_CPP11_CHRONO 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_CHRONO 1 + #else + #define EA_NO_HAVE_CPP11_CHRONO 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_SCOPED_ALLOCATOR) && !defined(EA_NO_HAVE_CPP11_SCOPED_ALLOCATOR) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 540) // Dinkumware. VS2012+ + #define EA_HAVE_CPP11_SCOPED_ALLOCATOR 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4007) + #define EA_HAVE_CPP11_SCOPED_ALLOCATOR 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_SCOPED_ALLOCATOR 1 + #else + #define EA_NO_HAVE_CPP11_SCOPED_ALLOCATOR 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_INITIALIZER_LIST) && !defined(EA_NO_HAVE_CPP11_INITIALIZER_LIST) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) && !defined(EA_COMPILER_NO_INITIALIZER_LISTS) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_INITIALIZER_LIST 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_CLANG) && (EA_COMPILER_VERSION >= 301) && !defined(EA_COMPILER_NO_INITIALIZER_LISTS) && !defined(EA_PLATFORM_APPLE) + #define EA_HAVE_CPP11_INITIALIZER_LIST 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBCPP_LIBRARY) && defined(EA_COMPILER_CLANG) && (EA_COMPILER_VERSION >= 301) && !defined(EA_COMPILER_NO_INITIALIZER_LISTS) && !defined(EA_PLATFORM_APPLE) + #define EA_HAVE_CPP11_INITIALIZER_LIST 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) && !defined(EA_COMPILER_NO_INITIALIZER_LISTS) && !defined(EA_PLATFORM_APPLE) + #define EA_HAVE_CPP11_INITIALIZER_LIST 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) && !defined(EA_COMPILER_NO_INITIALIZER_LISTS) + #define EA_HAVE_CPP11_INITIALIZER_LIST 1 + #else + #define EA_NO_HAVE_CPP11_INITIALIZER_LIST 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_SYSTEM_ERROR) && !defined(EA_NO_HAVE_CPP11_SYSTEM_ERROR) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) && !(defined(_HAS_CPP0X) && _HAS_CPP0X) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_SYSTEM_ERROR 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_CLANG) && (EA_COMPILER_VERSION >= 301) && !defined(EA_PLATFORM_APPLE) + #define EA_HAVE_CPP11_SYSTEM_ERROR 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) && !defined(EA_PLATFORM_APPLE) + #define EA_HAVE_CPP11_SYSTEM_ERROR 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_SYSTEM_ERROR 1 + #else + #define EA_NO_HAVE_CPP11_SYSTEM_ERROR 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_CODECVT) && !defined(EA_NO_HAVE_CPP11_CODECVT) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_CODECVT 1 + // Future versions of libc++ may support this header. However, at the moment there isn't + // a reliable way of detecting if this header is available. + //#elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4008) + // #define EA_HAVE_CPP11_CODECVT 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_CODECVT 1 + #else + #define EA_NO_HAVE_CPP11_CODECVT 1 + #endif +#endif + +// #include +#if !defined(EA_HAVE_CPP11_TYPEINDEX) && !defined(EA_NO_HAVE_CPP11_TYPEINDEX) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_TYPEINDEX 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) + #define EA_HAVE_CPP11_TYPEINDEX 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_TYPEINDEX 1 + #else + #define EA_NO_HAVE_CPP11_TYPEINDEX 1 + #endif +#endif + + + + +/* EA_HAVE_XXX_DECL */ + +#if !defined(EA_HAVE_mkstemps_DECL) && !defined(EA_NO_HAVE_mkstemps_DECL) + #if defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_SUN) + #define EA_HAVE_mkstemps_DECL 1 + #else + #define EA_NO_HAVE_mkstemps_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_gettimeofday_DECL) && !defined(EA_NO_HAVE_gettimeofday_DECL) + #if defined(EA_PLATFORM_POSIX) /* Posix means Linux, Unix, and Macintosh OSX, among others (including Linux-based mobile platforms). */ + #define EA_HAVE_gettimeofday_DECL 1 + #else + #define EA_NO_HAVE_gettimeofday_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_strcasecmp_DECL) && !defined(EA_NO_HAVE_strcasecmp_DECL) + #if !defined(EA_PLATFORM_MICROSOFT) + #define EA_HAVE_strcasecmp_DECL 1 /* This is found as stricmp when not found as strcasecmp */ + #define EA_HAVE_strncasecmp_DECL 1 + #else + #define EA_HAVE_stricmp_DECL 1 + #define EA_HAVE_strnicmp_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_mmap_DECL) && !defined(EA_NO_HAVE_mmap_DECL) + #if defined(EA_PLATFORM_POSIX) + #define EA_HAVE_mmap_DECL 1 /* mmap functionality varies significantly between systems. */ + #else + #define EA_NO_HAVE_mmap_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_fopen_DECL) && !defined(EA_NO_HAVE_fopen_DECL) + #define EA_HAVE_fopen_DECL 1 /* C FILE functionality such as fopen */ +#endif + +#if !defined(EA_HAVE_ISNAN) && !defined(EA_NO_HAVE_ISNAN) + #if defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_MINGW) + #define EA_HAVE_ISNAN(x) _isnan(x) /* declared in */ + #define EA_HAVE_ISINF(x) !_finite(x) + #elif defined(EA_PLATFORM_APPLE) + #define EA_HAVE_ISNAN(x) std::isnan(x) /* declared in */ + #define EA_HAVE_ISINF(x) std::isinf(x) + #elif defined(EA_PLATFORM_ANDROID) + #define EA_HAVE_ISNAN(x) __builtin_isnan(x) /* There are a number of standard libraries for Android and it's hard to tell them apart, so just go with builtins */ + #define EA_HAVE_ISINF(x) __builtin_isinf(x) + #elif defined(__GNUC__) && defined(__CYGWIN__) + #define EA_HAVE_ISNAN(x) __isnand(x) /* declared nowhere, it seems. */ + #define EA_HAVE_ISINF(x) __isinfd(x) + #else + #define EA_HAVE_ISNAN(x) std::isnan(x) /* declared in */ + #define EA_HAVE_ISINF(x) std::isinf(x) + #endif +#endif + +#if !defined(EA_HAVE_itoa_DECL) && !defined(EA_NO_HAVE_itoa_DECL) + #if defined(EA_COMPILER_MSVC) + #define EA_HAVE_itoa_DECL 1 + #else + #define EA_NO_HAVE_itoa_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_nanosleep_DECL) && !defined(EA_NO_HAVE_nanosleep_DECL) + #if (defined(EA_PLATFORM_UNIX) && !defined(EA_PLATFORM_SONY)) || defined(EA_PLATFORM_IPHONE) || defined(EA_PLATFORM_OSX) || defined(EA_PLATFORM_SONY) || defined(EA_PLATFORM_NX) + #define EA_HAVE_nanosleep_DECL 1 + #else + #define EA_NO_HAVE_nanosleep_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_utime_DECL) && !defined(EA_NO_HAVE_utime_DECL) + #if defined(EA_PLATFORM_MICROSOFT) + #define EA_HAVE_utime_DECL _utime + #elif EA_PLATFORM_UNIX + #define EA_HAVE_utime_DECL utime + #else + #define EA_NO_HAVE_utime_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_ftruncate_DECL) && !defined(EA_NO_HAVE_ftruncate_DECL) + #if !defined(__MINGW32__) + #define EA_HAVE_ftruncate_DECL 1 + #else + #define EA_NO_HAVE_ftruncate_DECL 1 + #endif +#endif + +#if !defined(EA_HAVE_localtime_DECL) && !defined(EA_NO_HAVE_localtime_DECL) + #define EA_HAVE_localtime_DECL 1 +#endif + +#if !defined(EA_HAVE_pthread_getattr_np_DECL) && !defined(EA_NO_HAVE_pthread_getattr_np_DECL) + #if defined(EA_PLATFORM_LINUX) + #define EA_HAVE_pthread_getattr_np_DECL 1 + #else + #define EA_NO_HAVE_pthread_getattr_np_DECL 1 + #endif +#endif + + + +/* EA_HAVE_XXX_IMPL*/ + +#if !defined(EA_HAVE_WCHAR_IMPL) && !defined(EA_NO_HAVE_WCHAR_IMPL) + #if defined(EA_PLATFORM_DESKTOP) + #define EA_HAVE_WCHAR_IMPL 1 /* Specifies if wchar_t string functions are provided, such as wcslen, wprintf, etc. Implies EA_HAVE_WCHAR_H */ + #else + #define EA_NO_HAVE_WCHAR_IMPL 1 + #endif +#endif + +#if !defined(EA_HAVE_getenv_IMPL) && !defined(EA_NO_HAVE_getenv_IMPL) + #if (defined(EA_PLATFORM_DESKTOP) || defined(EA_PLATFORM_UNIX)) && !defined(EA_PLATFORM_WINRT) + #define EA_HAVE_getenv_IMPL 1 + #else + #define EA_NO_HAVE_getenv_IMPL 1 + #endif +#endif + +#if !defined(EA_HAVE_setenv_IMPL) && !defined(EA_NO_HAVE_setenv_IMPL) + #if defined(EA_PLATFORM_UNIX) && defined(EA_PLATFORM_POSIX) + #define EA_HAVE_setenv_IMPL 1 + #else + #define EA_NO_HAVE_setenv_IMPL 1 + #endif +#endif + +#if !defined(EA_HAVE_unsetenv_IMPL) && !defined(EA_NO_HAVE_unsetenv_IMPL) + #if defined(EA_PLATFORM_UNIX) && defined(EA_PLATFORM_POSIX) + #define EA_HAVE_unsetenv_IMPL 1 + #else + #define EA_NO_HAVE_unsetenv_IMPL 1 + #endif +#endif + +#if !defined(EA_HAVE_putenv_IMPL) && !defined(EA_NO_HAVE_putenv_IMPL) + #if (defined(EA_PLATFORM_DESKTOP) || defined(EA_PLATFORM_UNIX)) && !defined(EA_PLATFORM_WINRT) + #define EA_HAVE_putenv_IMPL 1 /* With Microsoft compilers you may need to use _putenv, as they have deprecated putenv. */ + #else + #define EA_NO_HAVE_putenv_IMPL 1 + #endif +#endif + +#if !defined(EA_HAVE_time_IMPL) && !defined(EA_NO_HAVE_time_IMPL) + #if defined(EA_PLATFORM_NX) + #define EA_NO_HAVE_time_IMPL 1 + #else + #define EA_HAVE_time_IMPL 1 + #define EA_HAVE_clock_IMPL 1 + #endif +#endif + +// fopen() +#if !defined(EA_HAVE_fopen_IMPL) && !defined(EA_NO_HAVE_fopen_IMPL) + #define EA_HAVE_fopen_IMPL 1 /* C FILE functionality such as fopen */ +#endif + +// inet_ntop() +#if !defined(EA_HAVE_inet_ntop_IMPL) && !defined(EA_NO_HAVE_inet_ntop_IMPL) + #if (defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_POSIX)) && !defined(EA_PLATFORM_SONY) && !defined(EA_PLATFORM_NX) + #define EA_HAVE_inet_ntop_IMPL 1 /* This doesn't identify if the platform SDK has some alternative function that does the same thing; */ + #define EA_HAVE_inet_pton_IMPL 1 /* it identifies strictly the inet_ntop and inet_pton functions. For example, Microsoft has InetNtop in */ + #else + #define EA_NO_HAVE_inet_ntop_IMPL 1 + #define EA_NO_HAVE_inet_pton_IMPL 1 + #endif +#endif + +// clock_gettime() +#if !defined(EA_HAVE_clock_gettime_IMPL) && !defined(EA_NO_HAVE_clock_gettime_IMPL) + #if defined(EA_PLATFORM_LINUX) || defined(__CYGWIN__) || (defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)) || (defined(EA_PLATFORM_POSIX) && defined(_CPPLIB_VER) /*Dinkumware*/) + #define EA_HAVE_clock_gettime_IMPL 1 /* You need to link the 'rt' library to get this */ + #else + #define EA_NO_HAVE_clock_gettime_IMPL 1 + #endif +#endif + +#if !defined(EA_HAVE_getcwd_IMPL) && !defined(EA_NO_HAVE_getcwd_IMPL) + #if (defined(EA_PLATFORM_DESKTOP) || defined(EA_PLATFORM_UNIX)) && !defined(EA_PLATFORM_ANDROID) && !defined(EA_PLATFORM_WINRT) + #define EA_HAVE_getcwd_IMPL 1 /* With Microsoft compilers you may need to use _getcwd, as they have deprecated getcwd. And in any case it's present at */ + #else + #define EA_NO_HAVE_getcwd_IMPL 1 + #endif +#endif + +#if !defined(EA_HAVE_tmpnam_IMPL) && !defined(EA_NO_HAVE_tmpnam_IMPL) + #if (defined(EA_PLATFORM_DESKTOP) || defined(EA_PLATFORM_UNIX)) && !defined(EA_PLATFORM_ANDROID) + #define EA_HAVE_tmpnam_IMPL 1 + #else + #define EA_NO_HAVE_tmpnam_IMPL 1 + #endif +#endif + +// nullptr, the built-in C++11 type. +// This EA_HAVE is deprecated, as EA_COMPILER_NO_NULLPTR is more appropriate, given that nullptr is a compiler-level feature and not a library feature. +#if !defined(EA_HAVE_nullptr_IMPL) && !defined(EA_NO_HAVE_nullptr_IMPL) + #if defined(EA_COMPILER_NO_NULLPTR) + #define EA_NO_HAVE_nullptr_IMPL 1 + #else + #define EA_HAVE_nullptr_IMPL 1 + #endif +#endif + +// std::nullptr_t +// Note that implements a portable nullptr implementation, but this +// EA_HAVE specifically refers to std::nullptr_t from the standard libraries. +#if !defined(EA_HAVE_nullptr_t_IMPL) && !defined(EA_NO_HAVE_nullptr_t_IMPL) + #if defined(EA_COMPILER_CPP11_ENABLED) + // VS2010+ with its default Dinkumware standard library. + #if defined(_MSC_VER) && (_MSC_VER >= 1600) && defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) + #define EA_HAVE_nullptr_t_IMPL 1 + + #elif defined(EA_HAVE_LIBCPP_LIBRARY) // clang/llvm libc++ + #define EA_HAVE_nullptr_t_IMPL 1 + + #elif defined(EA_HAVE_LIBSTDCPP_LIBRARY) // GNU libstdc++ + // Unfortunately __GLIBCXX__ date values don't go strictly in version ordering. + #if (__GLIBCXX__ >= 20110325) && (__GLIBCXX__ != 20120702) && (__GLIBCXX__ != 20110428) + #define EA_HAVE_nullptr_t_IMPL 1 + #else + #define EA_NO_HAVE_nullptr_t_IMPL 1 + #endif + + // We simply assume that the standard library (e.g. Dinkumware) provides std::nullptr_t. + #elif defined(__clang__) + #define EA_HAVE_nullptr_t_IMPL 1 + + // With GCC compiler >= 4.6, std::nullptr_t is always defined in , in practice. + #elif defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) + #define EA_HAVE_nullptr_t_IMPL 1 + + // The EDG compiler provides nullptr, but uses an older standard library that doesn't support std::nullptr_t. + #elif defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403) + #define EA_HAVE_nullptr_t_IMPL 1 + + #else + #define EA_NO_HAVE_nullptr_t_IMPL 1 + #endif + #else + #define EA_NO_HAVE_nullptr_t_IMPL 1 + #endif +#endif + +// std::terminate +#if !defined(EA_HAVE_std_terminate_IMPL) && !defined(EA_NO_HAVE_std_terminate_IMPL) + #if !defined(EA_PLATFORM_IPHONE) && !defined(EA_PLATFORM_ANDROID) + #define EA_HAVE_std_terminate_IMPL 1 /* iOS doesn't appear to provide an implementation for std::terminate under the armv6 target. */ + #else + #define EA_NO_HAVE_std_terminate_IMPL 1 + #endif +#endif + +// : std::begin, std::end, std::prev, std::next, std::move_iterator. +#if !defined(EA_HAVE_CPP11_ITERATOR_IMPL) && !defined(EA_NO_HAVE_CPP11_ITERATOR_IMPL) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) && !(defined(_HAS_CPP0X) && _HAS_CPP0X) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_ITERATOR_IMPL 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4006) + #define EA_HAVE_CPP11_ITERATOR_IMPL 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_ITERATOR_IMPL 1 + #else + #define EA_NO_HAVE_CPP11_ITERATOR_IMPL 1 + #endif +#endif + +// : std::weak_ptr, std::shared_ptr, std::unique_ptr, std::bad_weak_ptr, std::owner_less +#if !defined(EA_HAVE_CPP11_SMART_POINTER_IMPL) && !defined(EA_NO_HAVE_CPP11_SMART_POINTER_IMPL) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) && !(defined(_HAS_CPP0X) && _HAS_CPP0X) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_SMART_POINTER_IMPL 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) + #define EA_HAVE_CPP11_SMART_POINTER_IMPL 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_SMART_POINTER_IMPL 1 + #else + #define EA_NO_HAVE_CPP11_SMART_POINTER_IMPL 1 + #endif +#endif + +// : std::function, std::mem_fn, std::bad_function_call, std::is_bind_expression, std::is_placeholder, std::reference_wrapper, std::hash, std::bind, std::ref, std::cref. +#if !defined(EA_HAVE_CPP11_FUNCTIONAL_IMPL) && !defined(EA_NO_HAVE_CPP11_FUNCTIONAL_IMPL) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) && !(defined(_HAS_CPP0X) && _HAS_CPP0X) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_FUNCTIONAL_IMPL 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) + #define EA_HAVE_CPP11_FUNCTIONAL_IMPL 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_FUNCTIONAL_IMPL 1 + #else + #define EA_NO_HAVE_CPP11_FUNCTIONAL_IMPL 1 + #endif +#endif + +// std::current_exception, std::rethrow_exception, std::exception_ptr, std::make_exception_ptr +#if !defined(EA_HAVE_CPP11_EXCEPTION_IMPL) && !defined(EA_NO_HAVE_CPP11_EXCEPTION_IMPL) + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) && (_CPPLIB_VER >= 520) && !(defined(_HAS_CPP0X) && _HAS_CPP0X) // Dinkumware. VS2010+ + #define EA_HAVE_CPP11_EXCEPTION_IMPL 1 + #elif defined(EA_COMPILER_CPP11_ENABLED) && defined(EA_HAVE_LIBSTDCPP_LIBRARY) && defined(EA_COMPILER_GNUC) && (EA_COMPILER_VERSION >= 4004) + #define EA_HAVE_CPP11_EXCEPTION_IMPL 1 + #elif defined(EA_HAVE_LIBCPP_LIBRARY) && (_LIBCPP_VERSION >= 1) + #define EA_HAVE_CPP11_EXCEPTION_IMPL 1 + #else + #define EA_NO_HAVE_CPP11_EXCEPTION_IMPL 1 + #endif +#endif + + + + +/* Implementations that all platforms seem to have: */ +/* + alloca + malloc + calloc + strtoll + strtoull + vsprintf + vsnprintf +*/ + +/* Implementations that we don't care about: */ +/* + bcopy -- Just use memmove or some customized equivalent. bcopy offers no practical benefit. + strlcpy -- So few platforms have this built-in that we get no benefit from using it. Use EA::StdC::Strlcpy instead. + strlcat -- " +*/ + + + +/*----------------------------------------------------------------------------- + EABASE_USER_HAVE_HEADER + + This allows the user to define a header file to be #included after the + eahave.h's contents are compiled. A primary use of this is to override + the contents of this header file. You can define the overhead header + file name in-code or define it globally as part of your build file. + + Example usage: + #define EABASE_USER_HAVE_HEADER "MyHaveOverrides.h" + #include +---------------------------------------------------------------------------*/ + +#ifdef EABASE_USER_HAVE_HEADER + #include EABASE_USER_HAVE_HEADER +#endif + + +#endif /* Header include guard */ + + + diff --git a/r5dev/thirdparty/ea/EABase/earesult.h b/r5dev/thirdparty/ea/EABase/earesult.h new file mode 100644 index 00000000..d08b3460 --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/earesult.h @@ -0,0 +1,62 @@ +/*----------------------------------------------------------------------------- + * earesult.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + + +#ifndef INCLUDED_earesult_H +#define INCLUDED_earesult_H + + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once /* Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. */ +#endif + + + +/* This result type is width-compatible with most systems. */ +typedef int32_t ea_result_type; + + +namespace EA +{ + typedef int32_t result_type; + + enum + { +#ifndef SUCCESS + // Deprecated + // Note: a public MS header has created a define of this name which causes a build error. Fortunately they + // define it to 0 which is compatible. + // see: WindowsSDK\8.1.51641-fb\installed\Include\um\RasError.h + SUCCESS = 0, +#endif + // Deprecated + FAILURE = -1, + + // These values are now the preferred constants + EA_SUCCESS = 0, + EA_FAILURE = -1, + }; +} + + +/* Macro to simplify testing for success. */ +#ifndef EA_SUCCEEDED + #define EA_SUCCEEDED(result) ((result) >= 0) +#endif + +/* Macro to simplfify testing for general failure. */ +#ifndef EA_FAILED + #define EA_FAILED(result) ((result) < 0) +#endif + + +#endif + + + + diff --git a/r5dev/thirdparty/ea/EABase/eastdarg.h b/r5dev/thirdparty/ea/EABase/eastdarg.h new file mode 100644 index 00000000..c175093e --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/eastdarg.h @@ -0,0 +1,99 @@ +/*----------------------------------------------------------------------------- + * eastdarg.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + + +#ifndef INCLUDED_eastdarg_H +#define INCLUDED_eastdarg_H + + +#include +#include + + +// VA_ARG_COUNT +// +// Returns the number of arguments passed to a macro's ... argument. +// This applies to macros only and not functions. +// +// Example usage: +// assert(VA_ARG_COUNT() == 0); +// assert(VA_ARG_COUNT(a) == 1); +// assert(VA_ARG_COUNT(a, b) == 2); +// assert(VA_ARG_COUNT(a, b, c) == 3); +// +#if !defined(VA_ARG_COUNT) + #define VA_ARG_COUNT(...) VA_ARG_COUNT_II((VA_ARG_COUNT_PREFIX_ ## __VA_ARGS__ ## _VA_ARG_COUNT_POSTFIX,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)) + #define VA_ARG_COUNT_II(__args) VA_ARG_COUNT_I __args + #define VA_ARG_COUNT_PREFIX__VA_ARG_COUNT_POSTFIX ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0 + #define VA_ARG_COUNT_I(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,N,...) N +#endif + + +// va_copy +// +// va_copy is required by C++11 +// C++11 and C99 require va_copy to be #defined and implemented. +// http://en.cppreference.com/w/cpp/utility/variadic/va_copy +// +// Example usage: +// void Func(char* p, ...){ +// va_list args, argsCopy; +// va_start(args, p); +// va_copy(argsCopy, args); +// (use args) +// (use argsCopy, which acts the same as args) +// va_end(args); +// va_end(argsCopy); +// } +// +#ifndef va_copy + #if defined(__va_copy) // GCC and others define this for non-C99 compatibility. + #define va_copy(dest, src) __va_copy((dest), (src)) + #else + // This may not work for some platforms, depending on their ABI. + // It works for Microsoft x86,x64, and PowerPC-based platforms. + #define va_copy(dest, src) memcpy(&(dest), &(src), sizeof(va_list)) + #endif +#endif + + + +// va_list_reference +// +// va_list_reference is not part of the C or C++ standards. +// It allows you to pass a va_list by reference to another +// function instead of by value. You cannot simply use va_list& +// as that won't work with many va_list implementations because +// they are implemented as arrays (which can't be passed by +// reference to a function without decaying to a pointer). +// +// Example usage: +// void Test(va_list_reference args){ +// printf("%d", va_arg(args, int)); +// } +// void Func(char* p, ...){ +// va_list args; +// va_start(args, p); +// Test(args); // Upon return args will be modified. +// va_end(args); +// } +#ifndef va_list_reference + #if defined(EA_PLATFORM_MICROSOFT) || (EA_PLATFORM_PTR_SIZE == 4) || (defined(EA_PLATFORM_APPLE) && defined(EA_PROCESSOR_ARM64)) || defined(EA_PLATFORM_NX) || (defined(EA_PLATFORM_ANDROID) && defined(EA_PROCESSOR_ARM64)) + // This is required for platform ABIs in which va_list is a struct or pointer. + #define va_list_reference va_list& + #else + // This is required for platform ABIs in which va_list is defined to be an array. + #define va_list_reference va_list + #endif +#endif + + + + +#endif /* Header include guard */ + + + diff --git a/r5dev/thirdparty/ea/EABase/eaunits.h b/r5dev/thirdparty/ea/EABase/eaunits.h new file mode 100644 index 00000000..22357234 --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/eaunits.h @@ -0,0 +1,54 @@ +/*----------------------------------------------------------------------------- + * eaunits.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + + +#ifndef INCLUDED_eaunits_h +#define INCLUDED_eaunits_h + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +// Defining common SI unit macros. +// +// The mebibyte is a multiple of the unit byte for digital information. Technically a +// megabyte (MB) is a power of ten, while a mebibyte (MiB) is a power of two, +// appropriate for binary machines. Many Linux distributions use the unit, but it is +// not widely acknowledged within the industry or media. +// Reference: https://en.wikipedia.org/wiki/Mebibyte +// +// Examples: +// auto size1 = EA_KILOBYTE(16); +// auto size2 = EA_MEGABYTE(128); +// auto size3 = EA_MEBIBYTE(8); +// auto size4 = EA_GIBIBYTE(8); + +// define byte for completeness +#define EA_BYTE(x) (x) + +// Decimal SI units +#define EA_KILOBYTE(x) (size_t(x) * 1000) +#define EA_MEGABYTE(x) (size_t(x) * 1000 * 1000) +#define EA_GIGABYTE(x) (size_t(x) * 1000 * 1000 * 1000) +#define EA_TERABYTE(x) (size_t(x) * 1000 * 1000 * 1000 * 1000) +#define EA_PETABYTE(x) (size_t(x) * 1000 * 1000 * 1000 * 1000 * 1000) +#define EA_EXABYTE(x) (size_t(x) * 1000 * 1000 * 1000 * 1000 * 1000 * 1000) + +// Binary SI units +#define EA_KIBIBYTE(x) (size_t(x) * 1024) +#define EA_MEBIBYTE(x) (size_t(x) * 1024 * 1024) +#define EA_GIBIBYTE(x) (size_t(x) * 1024 * 1024 * 1024) +#define EA_TEBIBYTE(x) (size_t(x) * 1024 * 1024 * 1024 * 1024) +#define EA_PEBIBYTE(x) (size_t(x) * 1024 * 1024 * 1024 * 1024 * 1024) +#define EA_EXBIBYTE(x) (size_t(x) * 1024 * 1024 * 1024 * 1024 * 1024 * 1024) + +#endif // INCLUDED_earesult_H + + + + diff --git a/r5dev/thirdparty/ea/EABase/int128.h b/r5dev/thirdparty/ea/EABase/int128.h new file mode 100644 index 00000000..334c7202 --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/int128.h @@ -0,0 +1,1268 @@ +/*----------------------------------------------------------------------------- + * eaint128_t.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + + +#ifndef INCLUDED_int128_h +#define INCLUDED_int128_h + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// EA_INT128_INTRINSIC_AVAILABLE +// +#if (EA_COMPILER_INTMAX_SIZE >= 16) && (defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)) + // __int128_t/__uint128_t is supported + #define EA_INT128_INTRINSIC_AVAILABLE 1 +#else + #define EA_INT128_INTRINSIC_AVAILABLE 0 +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// EA_INT128_ALIGNAS +// +#if EA_INT128_INTRINSIC_AVAILABLE && !defined(EA_COMPILER_NO_ALIGNAS) + #define EA_INT128_ALIGNAS alignas(unsigned __int128) +#else + #define EA_INT128_ALIGNAS +#endif + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// EA_HAVE_INT128 +// +// Indicates that EABase implements 128-bit integer types +// +#define EA_HAVE_INT128 1 + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// uint128_t_base +// +struct EA_INT128_ALIGNAS int128_t_base +{ + // Constructors / destructors + int128_t_base() = default; + int128_t_base(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3); + int128_t_base(uint64_t nPart0, uint64_t nPart1); + int128_t_base(uint8_t value); + int128_t_base(uint16_t value); + int128_t_base(uint32_t value); + int128_t_base(uint64_t value); + int128_t_base(const int128_t_base& value) = default; + + // Assignment operator + int128_t_base& operator=(const int128_t_base& value) = default; + + // Explicit operators to convert back to basic types + EA_CONSTEXPR explicit operator bool() const; + EA_CONSTEXPR explicit operator char() const; + EA_CONSTEXPR explicit operator int() const; + EA_CONSTEXPR explicit operator long() const; + EA_CONSTEXPR explicit operator long long() const; + EA_CONSTEXPR explicit operator short() const; + EA_CONSTEXPR explicit operator signed char() const; + EA_CONSTEXPR explicit operator unsigned char() const; + EA_CONSTEXPR explicit operator unsigned int() const; + EA_CONSTEXPR explicit operator unsigned long long() const; + EA_CONSTEXPR explicit operator unsigned long() const; + EA_CONSTEXPR explicit operator unsigned short() const; +#if EA_WCHAR_UNIQUE + // EA_CONSTEXPR explicit operator char16_t() const; + // EA_CONSTEXPR explicit operator char32_t() const; + // EA_CONSTEXPR explicit operator wchar_t() const; +#endif + EA_CONSTEXPR explicit operator float() const; + EA_CONSTEXPR explicit operator double() const; + EA_CONSTEXPR explicit operator long double() const; +#if EA_INT128_INTRINSIC_AVAILABLE + EA_CONSTEXPR explicit operator __int128() const; + EA_CONSTEXPR explicit operator unsigned __int128() const; +#endif + + // Math operators + static void OperatorPlus (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result); + static void OperatorMinus(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result); + static void OperatorMul (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result); + + // Shift operators + static void OperatorShiftRight(const int128_t_base& value, int nShift, int128_t_base& result); + static void OperatorShiftLeft (const int128_t_base& value, int nShift, int128_t_base& result); + + // Unary arithmetic/logic operators + bool operator!() const; + + // Logical operators + static void OperatorXOR(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result); + static void OperatorOR (const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result); + static void OperatorAND(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result); + + bool IsZero() const; + void SetZero(); + void TwosComplement(); + void InverseTwosComplement(); + + int GetBit(int nIndex) const; + void SetBit(int nIndex, int value); + +protected: + void DoubleToUint128(double value); + + EA_CONSTEXPR uint64_t Low() const + { + return mPart0; + } + + EA_CONSTEXPR uint64_t High() const + { + return mPart1; + } + +protected: + #ifdef EA_SYSTEM_BIG_ENDIAN + uint64_t mPart1; // Most significant byte. + uint64_t mPart0; // Least significant byte. + #else + uint64_t mPart0; // Least significant byte. + uint64_t mPart1; // Most significant byte. + #endif +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// int128_t +// +// Implements signed 128 bit integer. +// +struct int128_t : public int128_t_base +{ + // Constructors / destructors + using int128_t_base::int128_t_base; + + // Assignment operator + using int128_t_base::operator=; + + // Unary arithmetic/logic operators + int128_t operator-() const; + int128_t& operator++(); + int128_t& operator--(); + int128_t operator++(int); + int128_t operator--(int); + int128_t operator~() const; + int128_t operator+() const; + + // Math operators + int128_t operator+ (const int128_t& other); + int128_t operator- (const int128_t& other); + int128_t operator* (const int128_t& other); + int128_t operator/ (const int128_t& other); + int128_t operator% (const int128_t& other); + int128_t& operator+=(const int128_t& other); + int128_t& operator-=(const int128_t& other); + int128_t& operator*=(const int128_t& other); + int128_t& operator/=(const int128_t& other); + int128_t& operator%=(const int128_t& other); + + // Shift operators + int128_t operator>> (int nShift) const; + int128_t operator<< (int nShift) const; + int128_t& operator>>=(int nShift); + int128_t& operator<<=(int nShift); + + // Logical operators + int128_t operator^ (const int128_t& other) const; + int128_t operator| (const int128_t& other) const; + int128_t operator& (const int128_t& other) const; + int128_t& operator^=(const int128_t& other); + int128_t& operator|=(const int128_t& other); + int128_t& operator&=(const int128_t& other); + + // Equality operators + bool operator==(const int128_t& other) const; + bool operator!=(const int128_t& other) const; + bool operator> (const int128_t& other) const; + bool operator>=(const int128_t& other) const; + bool operator< (const int128_t& other) const; + bool operator<=(const int128_t& other) const; + +protected: + int compare(const int128_t& other) const; + void Negate(); + void Modulus(const int128_t& divisor, int128_t& quotient, int128_t& remainder) const; + bool IsNegative() const; // Returns true for value < 0 + bool IsPositive() const; // Returns true for value >= 0 +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// uint128_t +// +// Implements unsigned 128 bit integer. +// +struct uint128_t : public int128_t_base +{ + // Constructors / destructors + using int128_t_base::int128_t_base; + + // Assignment operator + using int128_t_base::operator=; + + // Unary arithmetic/logic operators + uint128_t operator-() const; + uint128_t& operator++(); + uint128_t& operator--(); + uint128_t operator++(int); + uint128_t operator--(int); + uint128_t operator~() const; + uint128_t operator+() const; + + // Math operators + uint128_t operator+ (const uint128_t& other); + uint128_t operator- (const uint128_t& other); + uint128_t operator* (const uint128_t& other); + uint128_t operator/ (const uint128_t& other); + uint128_t operator% (const uint128_t& other); + uint128_t& operator+=(const uint128_t& other); + uint128_t& operator-=(const uint128_t& other); + uint128_t& operator*=(const uint128_t& other); + uint128_t& operator/=(const uint128_t& other); + uint128_t& operator%=(const uint128_t& other); + + // Shift operators + uint128_t operator>> (int nShift) const; + uint128_t operator<< (int nShift) const; + uint128_t& operator>>=(int nShift); + uint128_t& operator<<=(int nShift); + + // Logical operators + uint128_t operator^ (const uint128_t& other) const; + uint128_t operator| (const uint128_t& other) const; + uint128_t operator& (const uint128_t& other) const; + uint128_t& operator^=(const uint128_t& other); + uint128_t& operator|=(const uint128_t& other); + uint128_t& operator&=(const uint128_t& other); + + // Equality operators + bool operator==(const uint128_t& other) const; + bool operator!=(const uint128_t& other) const; + bool operator> (const uint128_t& other) const; + bool operator>=(const uint128_t& other) const; + bool operator< (const uint128_t& other) const; + bool operator<=(const uint128_t& other) const; + +protected: + int compare(const uint128_t& other) const; + void Negate(); + void Modulus(const uint128_t& divisor, uint128_t& quotient, uint128_t& remainder) const; + bool IsNegative() const; // Returns true for value < 0 + bool IsPositive() const; // Returns true for value >= 0 +}; + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// uint128_t_base implementation +/////////////////////////////////////////////////////////////////////////////////////////////////////// +EA_CONSTEXPR inline int128_t_base::operator bool() const { return mPart0 || mPart1; } +EA_CONSTEXPR inline int128_t_base::operator char() const { return static_cast(Low()); } +#if EA_WCHAR_UNIQUE +// EA_CONSTEXPR inline int128_t_base::operator char16_t() const { return static_cast(Low()); } +// EA_CONSTEXPR inline int128_t_base::operator char32_t() const { return static_cast(Low()); } +// EA_CONSTEXPR inline int128_t_base::operator wchar_t() const { return static_cast(Low()); } +#endif +EA_CONSTEXPR inline int128_t_base::operator int() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator long() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator long long() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator short() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator signed char() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator unsigned char() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator unsigned int() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator unsigned long long() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator unsigned long() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator unsigned short() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator float() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator double() const { return static_cast(Low()); } +EA_CONSTEXPR inline int128_t_base::operator long double() const { return static_cast(Low()); } +#if EA_INT128_INTRINSIC_AVAILABLE +EA_CONSTEXPR inline int128_t_base::operator __int128() const { return static_cast<__int128>(Low()); } +EA_CONSTEXPR inline int128_t_base::operator unsigned __int128() const { return static_cast(Low()); } +#endif + +inline void int128_t_base::SetBit(int nIndex, int value) +{ + // EA_ASSERT((nIndex >= 0) && (nIndex < 128)); + + const uint64_t nBitMask = ((uint64_t)1 << (nIndex % 64)); + + if(nIndex < 64) + { + if(value) + mPart0 = mPart0 | nBitMask; + else + mPart0 = mPart0 & ~nBitMask; + } + else if(nIndex < 128) + { + if(value) + mPart1 = mPart1 | nBitMask; + else + mPart1 = mPart1 & ~nBitMask; + } +} + +inline int int128_t_base::GetBit(int nIndex) const +{ + // EA_ASSERT((nIndex >= 0) && (nIndex < 128)); + + const uint64_t nBitMask = ((uint64_t)1 << (nIndex % 64)); + + if(nIndex < 64) + return ((mPart0 & nBitMask) ? 1 : 0); + else if(nIndex < 128) + return ((mPart1 & nBitMask) ? 1 : 0); + return 0; +} + +inline int128_t_base::int128_t_base(uint32_t nPart0, uint32_t nPart1, uint32_t nPart2, uint32_t nPart3) +{ + mPart1 = ((uint64_t)nPart3 << 32) + nPart2; + mPart0 = ((uint64_t)nPart1 << 32) + nPart0; +} + +inline int128_t_base::int128_t_base(uint64_t nPart0, uint64_t nPart1) +{ + mPart1 = nPart1; + mPart0 = nPart0; +} + +inline int128_t_base::int128_t_base(uint8_t value) +{ + mPart1 = 0; + mPart0 = value; +} + +inline int128_t_base::int128_t_base(uint16_t value) +{ + mPart1 = 0; + mPart0 = value; +} + +inline int128_t_base::int128_t_base(uint32_t value) +{ + mPart1 = 0; + mPart0 = value; +} + +inline int128_t_base::int128_t_base(uint64_t value) +{ + mPart1 = 0; + mPart0 = value; +} + +/////////////////////////////////////////////////////////////////////////////// +// OperatorPlus +// +// Returns: (value1 + value2) into result. +// The output 'result' *is* allowed to point to the same memory as one of the inputs. +// To consider: Fix 'defect' of this function whereby it doesn't implement overflow wraparound. +// +inline void int128_t_base::OperatorPlus(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result) +{ + uint64_t t = value1.mPart0 + value2.mPart0; + uint64_t nCarry = (t < value1.mPart0) && (t < value2.mPart0); + result.mPart0 = t; + result.mPart1 = value1.mPart1 + value2.mPart1 + nCarry; +} + +/////////////////////////////////////////////////////////////////////////////// +// OperatorMinus +// +// Returns: (value1 - value2) into result. +// The output 'result' *is* allowed to point to the same memory as one of the inputs. +// To consider: Fix 'defect' of this function whereby it doesn't implement overflow wraparound. +// +inline void int128_t_base::OperatorMinus(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result) +{ + uint64_t t = (value1.mPart0 - value2.mPart0); + uint64_t nCarry = (value1.mPart0 < value2.mPart0) ? 1u : 0u; + result.mPart0 = t; + result.mPart1 = (value1.mPart1 - value2.mPart1) - nCarry; +} + +/////////////////////////////////////////////////////////////////////////////// +// OperatorMul +// +// 64 bit systems: +// This is how it would be able to work if we could get a 128 bit result from +// two 64 bit values. None of the 64 bit systems that we are currently working +// with have C language support for multiplying two 64 bit numbers and retrieving +// the 128 bit result. However, many 64 bit platforms have support at the asm +// level for doing such a thing. +// Part 1 Part 0 +// 0000000000000002 0000000000000001 +// x 0000000000000002 0000000000000001 +// ------------------------------------------- +// | 0000000000000002 0000000000000001 +// + 0000000000000004 | 0000000000000002 (0000000000000000) +// ------------------------------------------------------------------------- +// +inline void int128_t_base::OperatorMul(const int128_t_base& a, const int128_t_base& b, int128_t_base& result) +{ + // To consider: Use compiler or OS-provided custom functionality here, such as + // Windows UnsignedMultiply128 and GCC's built-in int128_t. + + #if defined(DISABLED_PLATFORM_WIN64) + // To do: Implement x86-64 asm here. + + #else + // Else we are stuck doing something less efficient. In this case we + // fall back to doing 32 bit multiplies as with 32 bit platforms. + result = (a.mPart0 & 0xffffffff) * (b.mPart0 & 0xffffffff); + int128_t v01 = (a.mPart0 & 0xffffffff) * ((b.mPart0 >> 32) & 0xffffffff); + int128_t v02 = (a.mPart0 & 0xffffffff) * (b.mPart1 & 0xffffffff); + int128_t v03 = (a.mPart0 & 0xffffffff) * ((b.mPart1 >> 32) & 0xffffffff); + + int128_t v10 = ((a.mPart0 >> 32) & 0xffffffff) * (b.mPart0 & 0xffffffff); + int128_t v11 = ((a.mPart0 >> 32) & 0xffffffff) * ((b.mPart0 >> 32) & 0xffffffff); + int128_t v12 = ((a.mPart0 >> 32) & 0xffffffff) * (b.mPart1 & 0xffffffff); + + int128_t v20 = (a.mPart1 & 0xffffffff) * (b.mPart0 & 0xffffffff); + int128_t v21 = (a.mPart1 & 0xffffffff) * ((b.mPart0 >> 32) & 0xffffffff); + + int128_t v30 = ((a.mPart1 >> 32) & 0xffffffff) * (b.mPart0 & 0xffffffff); + + // Do row addition, shifting as needed. + OperatorPlus(result, v01 << 32, result); + OperatorPlus(result, v02 << 64, result); + OperatorPlus(result, v03 << 96, result); + + OperatorPlus(result, v10 << 32, result); + OperatorPlus(result, v11 << 64, result); + OperatorPlus(result, v12 << 96, result); + + OperatorPlus(result, v20 << 64, result); + OperatorPlus(result, v21 << 96, result); + + OperatorPlus(result, v30 << 96, result); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// +// OperatorShiftRight +// +// Returns: value >> nShift into result +// The output 'result' may *not* be the same as one the input. +// With rightward shifts of negative numbers, shift in zero from the left side. +// +inline void int128_t_base::OperatorShiftRight(const int128_t_base& value, int nShift, int128_t_base& result) +{ + if(nShift >= 0) + { + if(nShift < 64) + { // 0 - 63 + result.mPart1 = (value.mPart1 >> nShift); + + if(nShift == 0) + result.mPart0 = (value.mPart0 >> nShift); + else + result.mPart0 = (value.mPart0 >> nShift) | (value.mPart1 << (64 - nShift)); + } + else + { // 64+ + result.mPart1 = 0; + result.mPart0 = (value.mPart1 >> (nShift - 64)); + } + } + else // (nShift < 0) + OperatorShiftLeft(value, -nShift, result); +} + + +/////////////////////////////////////////////////////////////////////////////// +// OperatorShiftRight +// +// Returns: value << nShift into result +// The output 'result' may *not* be the same as one the input. +// With rightward shifts of negative numbers, shift in zero from the left side. +// +inline void int128_t_base::OperatorShiftLeft(const int128_t_base& value, int nShift, int128_t_base& result) +{ + if(nShift >= 0) + { + if(nShift < 64) + { + if(nShift) // We need to have a special case because CPUs convert a shift by 64 to a no-op. + { + // 1 - 63 + result.mPart0 = (value.mPart0 << nShift); + result.mPart1 = (value.mPart1 << nShift) | (value.mPart0 >> (64 - nShift)); + } + else + { + result.mPart0 = value.mPart0; + result.mPart1 = value.mPart1; + } + } + else + { // 64+ + result.mPart0 = 0; + result.mPart1 = (value.mPart0 << (nShift - 64)); + } + } + else // (nShift < 0) + OperatorShiftRight(value, -nShift, result); +} + + +inline bool int128_t_base::operator!() const +{ + return (mPart0 == 0) && (mPart1 == 0); +} + + +/////////////////////////////////////////////////////////////////////////////// +// OperatorXOR +// +// Returns: value1 ^ value2 into result +// The output 'result' may be the same as one the input. +// +inline void int128_t_base::OperatorXOR(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result) +{ + result.mPart0 = (value1.mPart0 ^ value2.mPart0); + result.mPart1 = (value1.mPart1 ^ value2.mPart1); +} + + +/////////////////////////////////////////////////////////////////////////////// +// OperatorOR +// +// Returns: value1 | value2 into result +// The output 'result' may be the same as one the input. +// +inline void int128_t_base::OperatorOR(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result) +{ + result.mPart0 = (value1.mPart0 | value2.mPart0); + result.mPart1 = (value1.mPart1 | value2.mPart1); +} + + +/////////////////////////////////////////////////////////////////////////////// +// OperatorAND +// +// Returns: value1 & value2 into result +// The output 'result' may be the same as one the input. +// +inline void int128_t_base::OperatorAND(const int128_t_base& value1, const int128_t_base& value2, int128_t_base& result) +{ + result.mPart0 = (value1.mPart0 & value2.mPart0); + result.mPart1 = (value1.mPart1 & value2.mPart1); +} + + +inline bool int128_t_base::IsZero() const +{ + return (mPart0 == 0) && // Check mPart0 first as this will likely yield faster execution. + (mPart1 == 0); +} + + +inline void int128_t_base::SetZero() +{ + mPart1 = 0; + mPart0 = 0; +} + + +inline void int128_t_base::TwosComplement() +{ + mPart1 = ~mPart1; + mPart0 = ~mPart0; + + // What we want to do, but isn't available at this level: + // operator++(); + // Alternative: + int128_t_base one((uint32_t)1); + OperatorPlus(*this, one, *this); +} + + +inline void int128_t_base::InverseTwosComplement() +{ + // What we want to do, but isn't available at this level: + // operator--(); + // Alternative: + int128_t_base one((uint32_t)1); + OperatorMinus(*this, one, *this); + + mPart1 = ~mPart1; + mPart0 = ~mPart0; +} + + +inline void int128_t_base::DoubleToUint128(double value) +{ + // Currently this function is limited to 64 bits of integer input. + // We need to make a better version of this function. Perhaps we should implement + // it via dissecting the IEEE floating point format (sign, exponent, matissa). + // EA_ASSERT(fabs(value) < 18446744073709551616.0); // Assert that the input is <= 64 bits of integer. + + mPart1 = 0; + mPart0 = (value >= 0 ? (uint64_t)value : (uint64_t)-value); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// uint128_t implementation +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +inline uint128_t uint128_t::operator^(const uint128_t& other) const +{ + uint128_t temp; + uint128_t::OperatorXOR(*this, other, temp); + return temp; +} + +inline uint128_t uint128_t::operator|(const uint128_t& other) const +{ + uint128_t temp; + uint128_t::OperatorOR(*this, other, temp); + return temp; +} + +inline uint128_t uint128_t::operator&(const uint128_t& other) const +{ + uint128_t temp; + uint128_t::OperatorAND(*this, other, temp); + return temp; +} + +inline uint128_t& uint128_t::operator^=(const uint128_t& value) +{ + OperatorXOR(*this, value, *this); + return *this; +} + +inline uint128_t& uint128_t::operator|=(const uint128_t& value) +{ + OperatorOR(*this, value, *this); + return *this; +} + +inline uint128_t& uint128_t::operator&=(const uint128_t& value) +{ + OperatorAND(*this, value, *this); + return *this; +} + +// With rightward shifts of negative numbers, shift in zero from the left side. +inline uint128_t uint128_t::operator>>(int nShift) const +{ + uint128_t temp; + OperatorShiftRight(*this, nShift, temp); + return temp; +} + +// With rightward shifts of negative numbers, shift in zero from the left side. +inline uint128_t uint128_t::operator<<(int nShift) const +{ + uint128_t temp; + OperatorShiftLeft(*this, nShift, temp); + return temp; +} + +inline uint128_t& uint128_t::operator>>=(int nShift) +{ + uint128_t temp; + OperatorShiftRight(*this, nShift, temp); + *this = temp; + return *this; +} + +inline uint128_t& uint128_t::operator<<=(int nShift) +{ + uint128_t temp; + OperatorShiftLeft(*this, nShift, temp); + *this = temp; + return *this; +} + +inline uint128_t& uint128_t::operator+=(const uint128_t& value) +{ + OperatorPlus(*this, value, *this); + return *this; +} + +inline uint128_t& uint128_t::operator-=(const uint128_t& value) +{ + OperatorMinus(*this, value, *this); + return *this; +} + +inline uint128_t& uint128_t::operator*=(const uint128_t& value) +{ + *this = *this * value; + return *this; +} + +inline uint128_t& uint128_t::operator/=(const uint128_t& value) +{ + *this = *this / value; + return *this; +} + +inline uint128_t& uint128_t::operator%=(const uint128_t& value) +{ + *this = *this % value; + return *this; +} + +inline uint128_t uint128_t::operator+(const uint128_t& other) +{ + uint128_t temp; + uint128_t::OperatorPlus(*this, other, temp); + return temp; +} + +inline uint128_t uint128_t::operator-(const uint128_t& other) +{ + uint128_t temp; + uint128_t::OperatorMinus(*this, other, temp); + return temp; +} + +inline uint128_t uint128_t::operator*(const uint128_t& other) +{ + uint128_t returnValue; + int128_t_base::OperatorMul(*this, other, returnValue); + return returnValue; +} + +inline uint128_t uint128_t::operator/(const uint128_t& other) +{ + uint128_t remainder; + uint128_t quotient; + this->Modulus(other, quotient, remainder); + return quotient; +} + +inline uint128_t uint128_t::operator%(const uint128_t& other) +{ + uint128_t remainder; + uint128_t quotient; + this->Modulus(other, quotient, remainder); + return remainder; +} + +inline uint128_t uint128_t::operator+() const +{ + return *this; +} + +inline uint128_t uint128_t::operator~() const +{ + return uint128_t(~mPart0, ~mPart1); +} + +inline uint128_t& uint128_t::operator--() +{ + int128_t_base one((uint32_t)1); + OperatorMinus(*this, one, *this); + return *this; +} + +inline uint128_t uint128_t::operator--(int) +{ + uint128_t temp((uint32_t)1); + OperatorMinus(*this, temp, temp); + return temp; +} + +inline uint128_t uint128_t::operator++(int) +{ + uint128_t prev = *this; + uint128_t temp((uint32_t)1); + OperatorPlus(*this, temp, *this); + return prev; +} + +inline uint128_t& uint128_t::operator++() +{ + int128_t_base one((uint32_t)1); + OperatorPlus(*this, one, *this); + return *this; +} + +inline void uint128_t::Negate() +{ + TwosComplement(); +} + +inline uint128_t uint128_t::operator-() const +{ + uint128_t returnValue(*this); + returnValue.Negate(); + return returnValue; +} + +// This function forms the basis of all logical comparison functions. +// If value1 < value2, the return value is -1. +// If value1 == value2, the return value is 0. +// If value1 > value2, the return value is 1. +inline int uint128_t::compare(const uint128_t& other) const +{ + // Compare individual parts. At this point, the two numbers have the same sign. + if(mPart1 == other.mPart1) + { + if(mPart0 == other.mPart0) + return 0; + else if(mPart0 > other.mPart0) + return 1; + // return -1; //Just fall through to the end. + } + else if(mPart1 > other.mPart1) + return 1; + return -1; +} + +EA_DISABLE_VC_WARNING(4723) // warning C4723: potential divide by 0 +inline void uint128_t::Modulus(const uint128_t& divisor, uint128_t& quotient, uint128_t& remainder) const +{ + uint128_t tempDividend(*this); + uint128_t tempDivisor(divisor); + + if(tempDivisor.IsZero()) + { + // Force a divide by zero exception. + // We know that tempDivisor.mPart0 is zero. + quotient.mPart0 /= tempDivisor.mPart0; + } + else if(tempDividend.IsZero()) + { + quotient = uint128_t((uint32_t)0); + remainder = uint128_t((uint32_t)0); + } + else + { + remainder.SetZero(); + + for(int i(0); i < 128; i++) + { + remainder += (uint32_t)tempDividend.GetBit(127 - i); + const bool bBit(remainder >= tempDivisor); + quotient.SetBit(127 - i, bBit); + + if(bBit) + remainder -= tempDivisor; + + if((i != 127) && !remainder.IsZero()) + remainder <<= 1; + } + } +} +EA_RESTORE_VC_WARNING() + +inline bool uint128_t::operator==(const uint128_t& other) const +{ + return (mPart0 == other.mPart0) && // Check mPart0 first as this will likely yield faster execution. + (mPart1 == other.mPart1); +} + +inline bool uint128_t::operator< (const uint128_t& other) const { return (compare(other) < 0); } +inline bool uint128_t::operator!=(const uint128_t& other) const { return !(*this == other); } +inline bool uint128_t::operator> (const uint128_t& other) const { return other < *this; } +inline bool uint128_t::operator>=(const uint128_t& other) const { return !(*this < other); } +inline bool uint128_t::operator<=(const uint128_t& other) const { return !(other < *this); } + +inline bool uint128_t::IsNegative() const +{ // True if value < 0 + return false; +} + +inline bool uint128_t::IsPositive() const +{ + // True of value >= 0 + return true; +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// int128_t implementation +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +inline void int128_t::Negate() +{ + if (IsPositive()) + TwosComplement(); + else + InverseTwosComplement(); +} + +inline int128_t int128_t::operator-() const +{ + int128_t returnValue(*this); + returnValue.Negate(); + return returnValue; +} + +inline int128_t& int128_t::operator++() +{ + int128_t_base one((uint32_t)1); + OperatorPlus(*this, one, *this); + return *this; +} + +inline int128_t& int128_t::operator--() +{ + int128_t_base one((uint32_t)1); + OperatorMinus(*this, one, *this); + return *this; +} + +inline int128_t int128_t::operator++(int) +{ + int128_t prev = *this; + int128_t temp((uint32_t)1); + OperatorPlus(*this, temp, *this); + return prev; +} + +inline int128_t int128_t::operator--(int) +{ + int128_t temp((uint32_t)1); + OperatorMinus(*this, temp, temp); + return temp; +} + +inline int128_t int128_t::operator+() const +{ + return *this; +} + +inline int128_t int128_t::operator~() const +{ + return int128_t(~mPart0, ~mPart1); +} + +inline int128_t int128_t::operator+(const int128_t& other) +{ + int128_t temp; + int128_t::OperatorPlus(*this, other, temp); + return temp; +} + +inline int128_t int128_t::operator-(const int128_t& other) +{ + int128_t temp; + int128_t::OperatorMinus(*this, other, temp); + return temp; +} + +// This function forms the basis of all logical comparison functions. +// If value1 < value2, the return value is -1. +// If value1 == value2, the return value is 0. +// If value1 > value2, the return value is 1. +inline int int128_t::compare(const int128_t& other) const +{ + // Cache some values. Positive means >= 0. Negative means < 0 and thus means '!positive'. + const bool bValue1IsPositive( IsPositive()); + const bool bValue2IsPositive(other.IsPositive()); + + // Do positive/negative tests. + if(bValue1IsPositive != bValue2IsPositive) + return bValue1IsPositive ? 1 : -1; + + // Compare individual parts. At this point, the two numbers have the same sign. + if(mPart1 == other.mPart1) + { + if(mPart0 == other.mPart0) + return 0; + else if(mPart0 > other.mPart0) + return 1; + // return -1; //Just fall through to the end. + } + else if(mPart1 > other.mPart1) + return 1; + return -1; +} + +inline bool int128_t::operator==(const int128_t& other) const +{ + return (mPart0 == other.mPart0) && // Check mPart0 first as this will likely yield faster execution. + (mPart1 == other.mPart1); +} + +inline bool int128_t::operator!=(const int128_t& other) const +{ + return (mPart0 != other.mPart0) || // Check mPart0 first as this will likely yield faster execution. + (mPart1 != other.mPart1); +} + +inline bool int128_t::operator>(const int128_t& other) const +{ + return (compare(other) > 0); +} + +inline bool int128_t::operator>=(const int128_t& other) const +{ + return (compare(other) >= 0); +} + +inline bool int128_t::operator<(const int128_t& other) const +{ + return (compare(other) < 0); +} + +inline bool int128_t::operator<=(const int128_t& other) const +{ + return (compare(other) <= 0); +} + +inline bool int128_t::IsNegative() const +{ // True if value < 0 + return ((mPart1 & UINT64_C(0x8000000000000000)) != 0); +} + +inline bool int128_t::IsPositive() const +{ // True of value >= 0 + return ((mPart1 & UINT64_C(0x8000000000000000)) == 0); +} + +inline int128_t int128_t::operator*(const int128_t& other) +{ + int128_t a(*this); + int128_t b(other); + int128_t returnValue; + + // Correctly handle negative values + bool bANegative(false); + bool bBNegative(false); + + if(a.IsNegative()) + { + bANegative = true; + a.Negate(); + } + + if(b.IsNegative()) + { + bBNegative = true; + b.Negate(); + } + + int128_t_base::OperatorMul(a, b, returnValue); + + // Do negation as needed. + if(bANegative != bBNegative) + returnValue.Negate(); + + return returnValue; +} + +inline int128_t int128_t::operator/(const int128_t& other) +{ + int128_t remainder; + int128_t quotient; + this->Modulus(other, quotient, remainder); + return quotient; +} + +inline int128_t int128_t::operator<<(int nShift) const +{ + int128_t temp; + OperatorShiftLeft(*this, nShift, temp); + return temp; +} + +inline int128_t& int128_t::operator+=(const int128_t& value) +{ + OperatorPlus(*this, value, *this); + return *this; +} + +inline int128_t& int128_t::operator-=(const int128_t& value) +{ + OperatorMinus(*this, value, *this); + return *this; +} + +inline int128_t& int128_t::operator<<=(int nShift) +{ + int128_t temp; + OperatorShiftLeft(*this, nShift, temp); + *this = temp; + return *this; +} + +inline int128_t& int128_t::operator*=(const int128_t& value) +{ + *this = *this * value; + return *this; +} + +inline int128_t& int128_t::operator%=(const int128_t& value) +{ + *this = *this % value; + return *this; +} + +inline int128_t int128_t::operator%(const int128_t& other) +{ + int128_t remainder; + int128_t quotient; + this->Modulus(other, quotient, remainder); + return remainder; +} + +inline int128_t& int128_t::operator/=(const int128_t& value) +{ + *this = *this / value; + return *this; +} + +// With rightward shifts of negative numbers, shift in zero from the left side. +inline int128_t int128_t::operator>>(int nShift) const +{ + int128_t temp; + OperatorShiftRight(*this, nShift, temp); + return temp; +} + +inline int128_t& int128_t::operator>>=(int nShift) +{ + int128_t temp; + OperatorShiftRight(*this, nShift, temp); + *this = temp; + return *this; +} + +inline int128_t int128_t::operator^(const int128_t& other) const +{ + int128_t temp; + int128_t::OperatorXOR(*this, other, temp); + return temp; +} + +inline int128_t int128_t::operator|(const int128_t& other) const +{ + int128_t temp; + int128_t::OperatorOR(*this, other, temp); + return temp; +} + + +inline int128_t int128_t::operator&(const int128_t& other) const +{ + int128_t temp; + int128_t::OperatorAND(*this, other, temp); + return temp; +} + +inline int128_t& int128_t::operator^=(const int128_t& value) +{ + OperatorXOR(*this, value, *this); + return *this; +} + +inline int128_t& int128_t::operator|=(const int128_t& value) +{ + OperatorOR(*this, value, *this); + return *this; +} + +inline int128_t& int128_t::operator&=(const int128_t& value) +{ + OperatorAND(*this, value, *this); + return *this; +} + +EA_DISABLE_VC_WARNING(4723) // warning C4723: potential divide by 0 +inline void int128_t::Modulus(const int128_t& divisor, int128_t& quotient, int128_t& remainder) const +{ + int128_t tempDividend(*this); + int128_t tempDivisor(divisor); + + bool bDividendNegative = false; + bool bDivisorNegative = false; + + if(tempDividend.IsNegative()) + { + bDividendNegative = true; + tempDividend.Negate(); + } + if(tempDivisor.IsNegative()) + { + bDivisorNegative = true; + tempDivisor.Negate(); + } + + // Handle the special cases + if(tempDivisor.IsZero()) + { + // Force a divide by zero exception. + // We know that tempDivisor.mPart0 is zero. + quotient.mPart0 /= tempDivisor.mPart0; + } + else if(tempDividend.IsZero()) + { + quotient = int128_t((uint32_t)0); + remainder = int128_t((uint32_t)0); + } + else + { + remainder.SetZero(); + + for(int i(0); i < 128; i++) + { + remainder += (uint32_t)tempDividend.GetBit(127 - i); + const bool bBit(remainder >= tempDivisor); + quotient.SetBit(127 - i, bBit); + + if(bBit) + remainder -= tempDivisor; + + if((i != 127) && !remainder.IsZero()) + remainder <<= 1; + } + } + + if((bDividendNegative && !bDivisorNegative) || (!bDividendNegative && bDivisorNegative)) + { + // Ensure the following formula applies for negative dividends + // dividend = divisor * quotient + remainder + quotient.Negate(); + } +} +EA_RESTORE_VC_WARNING() + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// INT128_C / UINT128_C +// +// The C99 language defines macros for portably defining constants of +// sized numeric types. For example, there might be: +// #define UINT64_C(x) x##ULL +// Since our int128 data type is not a built-in type, we can't define a +// UINT128_C macro as something that pastes ULLL at the end of the digits. +// Instead we define it to create a temporary that is constructed from a +// string of the digits. This will work in most cases that suffix pasting +// would work. +// +/* EA_CONSTEXPR */ inline uint128_t UINT128_C(uint64_t nPart1, uint64_t nPart0) { return uint128_t(nPart0, nPart1); } +/* EA_CONSTEXPR */ inline int128_t INT128_C(int64_t nPart1, int64_t nPart0) { return int128_t(static_cast(nPart0), static_cast(nPart1)); } + + + + +#endif // INCLUDED_int128_h + diff --git a/r5dev/thirdparty/ea/EABase/nullptr.h b/r5dev/thirdparty/ea/EABase/nullptr.h new file mode 100644 index 00000000..d6629d50 --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/nullptr.h @@ -0,0 +1,102 @@ +/*----------------------------------------------------------------------------- + * nullptr.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + + +#include +#include + + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once /* Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. */ +#endif + + +#if defined(EA_COMPILER_CPP11_ENABLED) && !defined(EA_COMPILER_NO_NULLPTR) && !defined(EA_HAVE_nullptr_t_IMPL) + // The compiler supports nullptr, but the standard library doesn't implement a declaration for std::nullptr_t. So we provide one. + namespace std { typedef decltype(nullptr) nullptr_t; } +#endif + + + +#if defined(EA_COMPILER_NO_NULLPTR) // If the compiler lacks a native version... + + namespace std + { + class nullptr_t + { + public: + template // When tested a pointer, acts as 0. + operator T*() const + { return 0; } + + template // When tested as a member pointer, acts as 0. + operator T C::*() const + { return 0; } + + typedef void* (nullptr_t::*bool_)() const; + operator bool_() const // An rvalue of type std::nullptr_t can be converted to an rvalue of type bool; the resulting value is false. + { return false; } // We can't use operator bool(){ return false; } because bool is convertable to int which breaks other required functionality. + + // We can't enable this without generating warnings about nullptr being uninitialized after being used when created without "= {}". + //void* mSizeofVoidPtr; // sizeof(nullptr_t) == sizeof(void*). Needs to be public if nullptr_t is to be a POD. + + private: + void operator&() const; // Address cannot be taken. + }; + + inline nullptr_t nullptr_get() + { + nullptr_t n = { }; // std::nullptr exists. + return n; + } + + #if !defined(nullptr) // If somebody hasn't already defined nullptr in a custom way... + #define nullptr nullptr_get() + #endif + + } // namespace std + + + template + inline bool operator==(T* p, const std::nullptr_t) + { return p == 0; } + + template + inline bool operator==(const std::nullptr_t, T* p) + { return p == 0; } + + template + inline bool operator==(T U::* p, const std::nullptr_t) + { return p == 0; } + + template + inline bool operator==(const std::nullptr_t, T U::* p) + { return p == 0; } + + inline bool operator==(const std::nullptr_t, const std::nullptr_t) + { return true; } + + inline bool operator!=(const std::nullptr_t, const std::nullptr_t) + { return false; } + + inline bool operator<(const std::nullptr_t, const std::nullptr_t) + { return false; } + + inline bool operator>(const std::nullptr_t, const std::nullptr_t) + { return false; } + + inline bool operator<=(const std::nullptr_t, const std::nullptr_t) + { return true; } + + inline bool operator>=(const std::nullptr_t, const std::nullptr_t) + { return true; } + + + using std::nullptr_t; // exported to global namespace. + using std::nullptr_get; // exported to global namespace. + +#endif // EA_COMPILER_NO_NULLPTR + diff --git a/r5dev/thirdparty/ea/EABase/version.h b/r5dev/thirdparty/ea/EABase/version.h new file mode 100644 index 00000000..4c8111be --- /dev/null +++ b/r5dev/thirdparty/ea/EABase/version.h @@ -0,0 +1,36 @@ +/*----------------------------------------------------------------------------- + * version.h + * + * Copyright (c) Electronic Arts Inc. All rights reserved. + *---------------------------------------------------------------------------*/ + +#ifndef INCLUDED_EABASE_VERSION_H +#define INCLUDED_EABASE_VERSION_H + +/////////////////////////////////////////////////////////////////////////////// +// EABASE_VERSION +// +// We more or less follow the conventional EA packaging approach to versioning +// here. A primary distinction here is that minor versions are defined as two +// digit entities (e.g. .03") instead of minimal digit entities ".3"). The logic +// here is that the value is a counter and not a floating point fraction. +// Note that the major version doesn't have leading zeros. +// +// Example version strings: +// "0.91.00" // Major version 0, minor version 91, patch version 0. +// "1.00.00" // Major version 1, minor and patch version 0. +// "3.10.02" // Major version 3, minor version 10, patch version 02. +// "12.03.01" // Major version 12, minor version 03, patch version +// +// Example usage: +// printf("EABASE version: %s", EABASE_VERSION); +// printf("EABASE version: %d.%d.%d", EABASE_VERSION_N / 10000 % 100, EABASE_VERSION_N / 100 % 100, EABASE_VERSION_N % 100); +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EABASE_VERSION + #define EABASE_VERSION "2.10.01" + #define EABASE_VERSION_N 21001 +#endif + +#endif diff --git a/r5dev/thirdparty/ea/EAThread/.gitignore b/r5dev/thirdparty/ea/EAThread/.gitignore new file mode 100644 index 00000000..8d148cdc --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/.gitignore @@ -0,0 +1,49 @@ +tags +cscope.out +**/*.swp +**/*.swo +.swp +*.swp +.swo +.TMP +-.d +eastl_build_out +build_bench +bench.bat +build.bat +.p4config + +## CMake generated files +CMakeCache.txt +cmake_install.cmake + +## Patch files +*.patch + +## For Visual Studio Generated projects +*.sln +**/*.vcxproj +**/*.vcxproj.filters +*.VC.opendb +*.sdf +**/*.suo +**/*.user +.vs/* +**/Debug/* +CMakeFiles/* +EASTL.dir/** +RelWithDebInfo/* +Release/* +Win32/* +x64/* +MinSizeRel/* +build*/* +Testing/* +%ALLUSERSPROFILE%/* + +# Buck +/buck-out/ +/.buckd/ +/buckaroo/ +.buckconfig.local +BUCKAROO_DEPS diff --git a/r5dev/thirdparty/ea/EAThread/.p4ignore b/r5dev/thirdparty/ea/EAThread/.p4ignore new file mode 100644 index 00000000..55cf7354 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/.p4ignore @@ -0,0 +1 @@ +tags \ No newline at end of file diff --git a/r5dev/thirdparty/ea/EAThread/3RDPARTYLICENSES.TXT b/r5dev/thirdparty/ea/EAThread/3RDPARTYLICENSES.TXT new file mode 100644 index 00000000..5512dc85 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/3RDPARTYLICENSES.TXT @@ -0,0 +1,21 @@ +Additional licenses also apply to this software package as detailed below. + +-------------------------------------------------------------------------- +Copyright (c) 2015 Jeff Preshing + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgement in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +-------------------------------------------------------------------------- diff --git a/r5dev/thirdparty/ea/EAThread/CMakeLists.txt b/r5dev/thirdparty/ea/EAThread/CMakeLists.txt new file mode 100644 index 00000000..bb56026e --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/CMakeLists.txt @@ -0,0 +1,88 @@ +cmake_minimum_required( VERSION 3.16 ) +add_module( "lib" "EAThread" "" ${FOLDER_CONTEXT} TRUE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "Base" + "source/eathread.cpp" + "source/eathread_barrier.cpp" + "source/eathread_callstack.cpp" + "source/eathread_condition.cpp" + "source/eathread_futex.cpp" + "source/eathread_mutex.cpp" + "source/eathread_pool.cpp" + "source/eathread_rwmutex.cpp" + "source/eathread_rwmutex_ip.cpp" + "source/eathread_semaphore.cpp" + "source/eathread_storage.cpp" + "source/eathread_thread.cpp" + "source/version.cpp" +) + +add_sources( SOURCE_GROUP "Base/Include" + "include/eathread/eathread.h" + "include/eathread/eathread_atomic.h" + "include/eathread/eathread_barrier.h" + "include/eathread/eathread_callstack.h" + "include/eathread/eathread_callstack_context.h" + "include/eathread/eathread_condition.h" + "include/eathread/eathread_futex.h" + "include/eathread/eathread_list.h" + "include/eathread/eathread_mutex.h" + "include/eathread/eathread_pool.h" + "include/eathread/eathread_rwmutex.h" + "include/eathread/eathread_rwmutex_ip.h" + "include/eathread/eathread_rwsemalock.h" + "include/eathread/eathread_rwspinlock.h" + "include/eathread/eathread_rwspinlockw.h" + "include/eathread/eathread_semaphore.h" + "include/eathread/eathread_spinlock.h" + "include/eathread/eathread_storage.h" + "include/eathread/eathread_sync.h" + "include/eathread/eathread_thread.h" + "include/eathread/shared_array_mt.h" + "include/eathread/shared_ptr_mt.h" + "include/eathread/version.h" +) + +add_sources( SOURCE_GROUP "Base/Include/Internal" + "include/eathread/internal/config.h" + "include/eathread/internal/dllinfo.h" + "include/eathread/internal/eathread_atomic_standalone.h" + "include/eathread/internal/eathread_atomic_standalone_gcc.h" + "include/eathread/internal/eathread_atomic_standalone_msvc.h" + "include/eathread/internal/eathread_global.h" + "include/eathread/internal/timings.h" +) + +#add_sources( SOURCE_GROUP "PC64" +# "source/pc/eathread_callstack_win64.cpp" +# "source/pc/eathread_mutex_pc.cpp" +# "source/pc/eathread_pc.cpp" +# "source/pc/eathread_semaphore_pc.cpp" +# "source/pc/eathread_thread_pc.cpp" +#) + +#add_sources( SOURCE_GROUP "PC64/Include" +# "include/eathread/x86-64/eathread_atomic_x86-64.h" +# "include/eathread/x86-64/eathread_sync_x86-64.h" +#) + +# add_sources( SOURCE_GROUP "CPP11" +# "source/cpp11/eathread_cpp11.cpp" +# "source/cpp11/eathread_mutex_cpp11.cpp" +# "source/cpp11/eathread_semaphore_cpp11.cpp" +# "source/cpp11/eathread_thread_cpp11.cpp" +# ) +# +# add_sources( SOURCE_GROUP "CPP11/Include" +# "include/eathread/cpp11/eathread_atomic_cpp11.h" +# ) + +end_sources() +thirdparty_suppress_warnings() + +target_include_directories( ${PROJECT_NAME} PRIVATE + "${THIRDPARTY_SOURCE_DIR}/ea/" + "${THIRDPARTY_SOURCE_DIR}/ea/EAThread/include/" +) diff --git a/r5dev/thirdparty/ea/EAThread/LICENSE b/r5dev/thirdparty/ea/EAThread/LICENSE new file mode 100644 index 00000000..93ab2288 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/LICENSE @@ -0,0 +1,27 @@ +/* +Copyright (C) 2017 Electronic Arts Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of Electronic Arts, Inc. ("EA") nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ELECTRONIC ARTS AND ITS CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ELECTRONIC ARTS OR ITS CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/cpp11/eathread_atomic_cpp11.h b/r5dev/thirdparty/ea/EAThread/include/eathread/cpp11/eathread_atomic_cpp11.h new file mode 100644 index 00000000..8c9b2ae8 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/cpp11/eathread_atomic_cpp11.h @@ -0,0 +1,131 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// eathread_atomic.h +// +// Defines functionality for thread-safe primitive operations. +// +// EAThread atomics do NOT imply the use of read/write barriers. This is +// partly due to historical reasons and partly due to EAThread's internal +// code being optimized for not using barriers. +// +// In future, we are considering migrating the atomics interface which +// defaults atomics to use full read/write barriers while allowing users +// to opt-out of full barrier usage. The new C++11 interface already provides +// similar interfaces. +// +// http://en.cppreference.com/w/cpp/atomic/memory_order +// +// Created by Rob Parolin +///////////////////////////////////////////////////////////////////////////// + +#ifndef EATHREAD_INTERNAL_EATHREAD_ATOMIC_H +#define EATHREAD_INTERNAL_EATHREAD_ATOMIC_H + +#include +#include +#include +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +#define EA_THREAD_ATOMIC_IMPLEMENTED + +namespace EA +{ + namespace Thread + { + /// class AtomicInt + /// + /// Implements thread-safe access to an integer and primary operations on that integer. + /// AtomicIntegers are commonly used as lightweight flags and signals between threads + /// or as the synchronization object for spinlocks. Those familiar with the Win32 API + /// will find that AtomicInt32 is essentially a platform independent interface to + /// the Win32 InterlockedXXX family of functions. Those familiar with Linux may + /// find that AtomicInt32 is essentially a platform independent interface to atomic_t + /// functionality. + /// + /// Note that the reference implementation defined here is itself not thread-safe. + /// A thread-safe version requires platform-specific code. + /// + /// Example usage + /// AtomicInt32 i = 0; + /// + /// ++i; + /// i--; + /// i += 7; + /// i -= 3; + /// i = 2; + /// + /// int x = i.GetValue(); + /// i.Increment(); + /// bool oldValueWas6 = i.SetValueConditional(3, 6); + /// i.Add(4); + /// + template + class AtomicInt + { + public: + typedef AtomicInt ThisType; + typedef T ValueType; + + /// AtomicInt + /// Empty constructor. Intentionally leaves mValue in an unspecified state. + /// This is done so that an AtomicInt acts like a standard built-in integer. + AtomicInt() + {} + + AtomicInt(ValueType n) + { SetValue(n); } + + AtomicInt(const ThisType& x) + { SetValue(x.GetValue()); } + + AtomicInt& operator=(const ThisType& x) + { SetValue(x.GetValue()); return *this; } + + ValueType GetValue() const + { return mValue.load(); } + + ValueType GetValueRaw() const + { return mValue; } + + ValueType SetValue(ValueType n) + { return mValue.exchange(n); } + + bool SetValueConditional(ValueType n, ValueType condition) + { return mValue.compare_exchange_strong(condition, n); } + + ValueType Increment() + { return mValue.operator++(); } + + ValueType Decrement() + { return mValue.operator--(); } + + ValueType Add(ValueType n) + { return mValue.fetch_add(n) + n; } + + // operators + inline operator const ValueType() const { return GetValue(); } + inline ValueType operator =(ValueType n) { return mValue.operator=(n); } + inline ValueType operator+=(ValueType n) { return mValue.operator+=(n); } + inline ValueType operator-=(ValueType n) { return mValue.operator-=(n); } + inline ValueType operator++() { return mValue.operator++(); } + inline ValueType operator++(int) { return mValue.operator++(0); } + inline ValueType operator--() { return mValue.operator--(); } + inline ValueType operator--(int) { return mValue.operator--(0); } + + protected: + std::atomic mValue; + }; + + } // namespace Thread +} // namespace EA + + +#endif // EATHREAD_INTERNAL_EATHREAD_ATOMIC_H + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread.h new file mode 100644 index 00000000..a0853f61 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread.h @@ -0,0 +1,832 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// eathread.h +// +// Created by Paul Pedriana, Maxis +// +// Provides some base global definitions for the EA::Thread library. +// +// Design +// Many of the design criteria for EA::Thread is based on the design of the +// Posix threading standard. The Posix threading standard is designed to +// work portably on a wide range of operating systems and hardware, including +// embedded systems and realtime environments. As such, Posix threads generally +// represent a competent model to follow where possible. Windows and various +// other platforms have independent multi-threading systems which are taken +// into account here as well. If something exists in Windows but doesn't +// exist here (e.g. Thread suspend/resume), there is a decent chance that it +// is by design and for some good reason. +// +// C++ +// There are a number of C++ libraries devoted to multithreading. Usually the +// goal of these libraries is provide a platform independent interface which +// simplifies the most common usage patterns and helps prevent common errors. +// Some of these libraries are basic wrappers around existing C APIs while +// others provide a new and different paradigm. We take the former approach +// here, as it is provides more or less the same functionality but provides +// it in a straightforward way that is easily approached by those familiar +// with platform-specific APIs. This approach has been referred to as the +// "Wrapper Facade Pattern". +// +// Condition Variables +// Posix condition variables are implemented via the Condition class. Condition +// is essentially the Java and C# name for Posix' condition variables. For some +// people, a condition variable may seem similar to a Win32 Signal. In actuality +// they are similar but there is one critical difference: a Signal does not +// atomically unlock a mutex as part of the signaling process. This results in +// problematic race conditions that make reliable producer/consumer systems +// impossible to implement. +// +// Signals +// As of this writing, there isn't a Win32-like Signal class. The reason for this +// is that Semaphore does most or all the duty that Signal does and is a little +// more portable, given that Signals exist only on Win32 and not elsewhere. +// +// Timeouts +// Timeouts are specified as absolute times and not relative times. This may +// not be how Win32 threading works but it is what's proper and is how Posix +// threading works. From the OpenGroup online pthread documentation on this: +// An absolute time measure was chosen for specifying the +// timeout parameter for two reasons. First, a relative time +// measure can be easily implemented on top of a function +// that specifies absolute time, but there is a race +// condition associated with specifying an absolute timeout +// on top of a function that specifies relative timeouts. +// For example, assume that clock_gettime() returns the +// current time and cond_relative_timed_wait() uses relative +// timeouts: +// clock_gettime(CLOCK_REALTIME, &now); +// reltime = sleep_til_this_absolute_time - now; +// cond_relative_timed_wait(c, m, &reltime); +// If the thread is preempted between the first statement and +// the last statement, the thread blocks for too long. Blocking, +// however, is irrelevant if an absolute timeout is used. +// An absolute timeout also need not be recomputed if it is used +// multiple times in a loop, such as that enclosing a condition wait. +// For cases when the system clock is advanced discontinuously by +// an operator, it is expected that implementations process any +// timed wait expiring at an intervening time as if that time had +// actually occurred. +// +// General Threads +// For detailed information about threads, it is recommended that you read +// various competent sources of information about multithreading and +// multiprocessing. +// Programming with POSIX(R) Threads, by David R. Butenhof +// http://www.opengroup.org/onlinepubs/007904975/basedefs/pthread.h.html +// usenet: comp.programming.threads +// http://www.openmp.org/index.cgi?faq +// http://www.lambdacs.com/cpt/MFAQ.html +// http://www.lambdacs.com/cpt/FAQ.html +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/processes_and_threads.asp +// +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_H +#define EATHREAD_EATHREAD_H + +#include + +#if !EA_THREADS_AVAILABLE + // Do nothing +#elif EA_USE_CPP11_CONCURRENCY + EA_DISABLE_VC_WARNING(4265 4365 4836 4571 4625 4626 4628 4193 4127 4548 4574 4946 4350) + #include + #include + EA_RESTORE_VC_WARNING() +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) // Dinkumware doesn't usually provide gettimeofday or + #include // clock_gettime + #elif defined(EA_PLATFORM_UNIX) + #include // gettimeofday + #endif +#endif +#if defined(EA_PLATFORM_APPLE) + #include +#endif +#if defined(EA_PLATFORM_SONY) + #include "sdk_version.h" + #include +#endif +#include +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EA_THREAD_PREEMPTIVE / EA_THREAD_COOPERATIVE +// +// Defined or not defined. +// +// EA_THREAD_COOPERATIVE means that threads are not time-sliced by the +// operating system. If there exist multiple threads of the same priority +// then they will need to wait, sleep, or yield in order for the others +// to get time. See enum Scheduling and EATHREAD_SCHED for more info. +// +// EA_THREAD_PREEMPTIVE means that threads are time-sliced by the operating +// system at runtime. If there exist multiple threads of the same priority +// then the operating system will split execution time between them. +// See enum Scheduling and EATHREAD_SCHED for more info. +// +#if !EA_THREADS_AVAILABLE + #define EA_THREAD_COOPERATIVE +#else + #define EA_THREAD_PREEMPTIVE +#endif + + +/// namespace EA +/// +/// This is the standard Electronic Arts C++ namespace. +/// +namespace EA +{ + namespace Allocator + { + class ICoreAllocator; + } + + /// namespace Thread + /// + /// This is the standard Electronic Arts Thread C++ namespace. + /// + namespace Thread + { + /// Scheduling + /// Defines scheduling types supported by the given platform. + /// These are defined in detail by the Posix standard, with the + /// exception of Coop, which is added here. FIFO scheduling + /// is the most classic for game development, as it allows for + /// thread priorities and well-behaved synchronization primitives, + /// but it doesn't do time-slicing. The problem with time slicing + /// is that threads are pre-empted in the middle of work and this + /// hurts execution performance and cache performance. + /// + enum Scheduling + { + kSchedulingFIFO = 1, /// There is no automatic time-slicing; thread priorities control execution and context switches. + kSchedulingRR = 2, /// Same as FIFO but is periodic time-slicing. + kSchedulingSporadic = 4, /// Complex scheduling control. See the Posix standard. + kSchedulingTS = 8, /// a.k.a. SCHED_OTHER. Usually same as FIFO or RR except that thread priorities and execution can be temporarily modified. + kSchedulingCoop = 16 /// The user must control thread scheduling beyond the use of synchronization primitives. + }; + + #if defined(EA_PLATFORM_UNIX) + #define EATHREAD_SCHED kSchedulingFIFO + + #elif defined(EA_PLATFORM_MICROSOFT) + #define EATHREAD_SCHED kSchedulingRR + + #else + #define EATHREAD_SCHED kSchedulingFIFO + + #endif + + + // EATHREAD_MULTIPROCESSING_OS + // + // Defined as 0 or 1. + // Indicates whether the OS supports multiple concurrent processes, which may be in + // addition to supporting multiple threads within a process. + // Some platforms support multiple concurrently loaded processes but don't support + // running these processes concurrently. We don't currently count this as a + // multiprocessing OS. + #ifndef EATHREAD_MULTIPROCESSING_OS + #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_UNIX) + #define EATHREAD_MULTIPROCESSING_OS 1 + #else + #define EATHREAD_MULTIPROCESSING_OS 0 + #endif + #endif + + // EATHREAD_OTHER_THREAD_NAMING_SUPPORTED + // + // Defined as 0 or 1. + // Indicates whether the OS supports setting the thread name from a different + // thread (set to 1) or if the name can be set only from the curren thread (set to 0) + #ifndef EATHREAD_OTHER_THREAD_NAMING_SUPPORTED + #if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_APPLE) + #define EATHREAD_OTHER_THREAD_NAMING_SUPPORTED 0 + #else + #define EATHREAD_OTHER_THREAD_NAMING_SUPPORTED 1 + #endif + #endif + + // Uint / Int + // Defines a machine-word sized integer, useful for operations that are as efficient + // as possible on the given machine. Note that the C99 intfastNN_t types aren't sufficient, + // as they are defined by compilers in an undesirable way for the processors we work with. + #if !defined(EA_PLATFORM_WORD_SIZE) || (EA_PLATFORM_WORD_SIZE == 4) + typedef uint32_t Uint; + typedef int32_t Int; + #else + typedef uint64_t Uint; + typedef int64_t Int; + #endif + + + /// ThreadId + /// Uniquely identifies a thread throughout the system and is used by the EAThread API + /// to identify threads in a way equal to system provided thread ids. A ThreadId is the + /// same as a system thread id and can be used in direct system threading API calls. + #if !EA_THREADS_AVAILABLE + typedef int ThreadId; + #elif EA_USE_CPP11_CONCURRENCY + typedef std::thread::id ThreadId; + #elif defined(EA_PLATFORM_SONY) + typedef uint64_t ThreadId; + #elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + typedef pthread_t ThreadId; + #elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + typedef void* ThreadId; // This is really HANDLE, but HANDLE is the same as void* and we can avoid an expensive #include here. + #else + typedef int ThreadId; + #endif + + + // ThreadId constants + #if EA_USE_CPP11_CONCURRENCY + const ThreadId kThreadIdInvalid = ThreadId(); /// Special ThreadId indicating an invalid thread identifier. + #else + const ThreadId kThreadIdInvalid = ThreadId(0); /// Special ThreadId indicating an invalid thread identifier. + const ThreadId kThreadIdCurrent = ThreadId(INT_MAX); /// Special ThreadId indicating the current thread. + const ThreadId kThreadIdAny = ThreadId(INT_MAX - 1); /// Special ThreadId indicating no thread in particular. + #endif + + /// SysThreadId + /// It turns out that Microsoft operating systems (Windows, XBox, XBox 360) + /// have two different ways to identify a thread: HANDLE and DWORD. Some API + /// functions take thread HANDLES, while others take what Microsoft calls + /// thread ids (DWORDs). EAThread ThreadId is a HANDLE, as that is used for + /// more of the core threading APIs. However, some OS-level APIs accept instead + /// the DWORD thread id. + #if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE && !EA_USE_CPP11_CONCURRENCY + typedef uint32_t SysThreadId; + const SysThreadId kSysThreadIdInvalid = SysThreadId(0); /// Special SysThreadId indicating an invalid thread identifier. + #elif defined(EA_PLATFORM_SONY) + typedef ScePthread SysThreadId; + const SysThreadId kSysThreadIdInvalid = { 0 }; /// Special SysThreadId indicating an invalid thread identifier. + #elif defined(EA_PLATFORM_APPLE) + typedef thread_act_t SysThreadId; // thread_act_t is useful for calling mach APIs such as thread_policy_set() with. + const SysThreadId kSysThreadIdInvalid = SysThreadId(0); /// Special SysThreadId indicating an invalid thread identifier. + #elif EA_USE_CPP11_CONCURRENCY + typedef std::thread::native_handle_type SysThreadId; + const SysThreadId kSysThreadIdInvalid = { 0 }; /// Special SysThreadId indicating an invalid thread identifier. + + // For MSVC, native_handle_type is not a primitive type so we define operator== and operator!= for convenience. + // We use an auto converting proxy type for comparisons to avoid errors when native_handle_type is a built in type. + bool Equals(const SysThreadId& a, const SysThreadId& b); + struct SysThreadIdProxy + { + SysThreadIdProxy(const SysThreadId& id_) : id(id_) {} + SysThreadId id; + }; + inline bool operator==(const SysThreadId& lhs, const SysThreadIdProxy& rhs) { return Equals(lhs, rhs.id); } + inline bool operator!=(const SysThreadId& lhs, const SysThreadIdProxy& rhs) { return !Equals(lhs, rhs.id); } + #else + typedef ThreadId SysThreadId; + const SysThreadId kSysThreadIdInvalid = SysThreadId(0); /// Special SysThreadId indicating an invalid thread identifier. + #endif + + /// ThreadUniqueId + /// Uniquely identifies a thread throughout the system, but in a way that is not + /// necessarily compatible with system thread id identification. Sometimes it is + /// costly to work with system thread ids whereas all you want is some integer + /// that is unique between threads and you don't need to use it for system calls. + /// See the EAThreadGetUniqueId macro/function for usage. + typedef Uint ThreadUniqueId; + + // ThreadUniqueId constants + const ThreadUniqueId kThreadUniqueIdInvalid = 0; /// Special ThreadUniqueId indicating an invalid thread identifier. + + + // Time constants + // Milliseconds are the units of time in EAThread. While every generation of computers + // results in faster computers and thus milliseconds become an increasingly large number + // compared to the computer speed, computer multithreading is still largely done at the + // millisecond level, due to it still being a small value relative to human perception. + // We may reconsider this some time in the future and provide an option to have ThreadTime + // be specified in finer units, such as microseconds. + #if EA_USE_CPP11_CONCURRENCY + typedef std::chrono::milliseconds::rep ThreadTime; /// Current storage mechanism for time used by thread timeout functions. Units are milliseconds. + const ThreadTime kTimeoutImmediate = std::chrono::milliseconds::zero().count();/// Used to specify to functions to return immediately if the operation could not be done. + const ThreadTime kTimeoutNone = std::chrono::milliseconds::max().count(); /// Used to specify to functions to block without a timeout (i.e. block forever). + const ThreadTime kTimeoutYield = std::chrono::milliseconds::zero().count(); /// This is used with ThreadSleep to minimally yield to threads of equivalent priority. + + #define EA_THREADTIME_AS_INT64(t) ((int64_t)(t)) + #define EA_THREADTIME_AS_DOUBLE(t) ((double)(t)) + + #elif defined(EA_PLATFORM_SONY) && EA_THREADS_AVAILABLE + typedef double ThreadTime; // SceKernelUseconds maps to unsigned int + static_assert(sizeof(ThreadTime) >= sizeof(unsigned int), "ThreadTime not large enough for uint32_t representation of milliseconds for platform portablity"); + + const ThreadTime kTimeoutImmediate = 0; + const ThreadTime kTimeoutNone = DBL_MAX; + const ThreadTime kTimeoutYield = 0.000001; // 1 nanosecond in terms of a millisecond + + #define EA_THREADTIME_AS_UINT_MICROSECONDS(t) ((unsigned int)((t) * 1000.0)) /// Returns the milliseconds time as uint in microseconds. + #define EA_THREADTIME_AS_INT64(t) ((int64_t)(t)) /// Returns the unconverted milliseconds time as a int64_t. + #define EA_THREADTIME_AS_DOUBLE(t) (t) /// Returns the time as double milliseconds. May include a fraction component. + #define EA_TIMESPEC_AS_UINT(t) ((unsigned int)(((t).tv_sec * 1000) + ((t).tv_nsec / 1000000))) /// Returns the time as uint in milliseconds. + #define EA_TIMESPEC_AS_DOUBLE_IN_MS(t) ( (((t).tv_sec * 1000000000ull) + ((t).tv_nsec))/1000000.0) /// Returns the time as uint in milliseconds. + + #elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE + struct ThreadTime : public timespec + { + typedef int seconds_t; // To consider: change to uint64_t or maybe long. + typedef int nseconds_t; + + ThreadTime() { tv_sec = 0; tv_nsec = 0; } + ThreadTime(const timespec& ts) { tv_sec = ts.tv_sec; tv_nsec = ts.tv_nsec; } + ThreadTime(seconds_t nSeconds, nseconds_t nNanoseconds) { tv_sec = (long)nSeconds; tv_nsec = (long)nNanoseconds; } + ThreadTime(const int64_t& nMilliseconds) { tv_sec = (long)(nMilliseconds / 1000); tv_nsec = (long)((nMilliseconds - (tv_sec * 1000)) * 1000000); } + ThreadTime& operator+=(const int64_t& nMilliseconds) { long lTemp((long)nMilliseconds / 1000); tv_sec += lTemp; tv_nsec += (long)((nMilliseconds - (lTemp * 1000)) * 1000000); if(tv_nsec >= 1000000000){ tv_sec++; tv_nsec -= 1000000000; } return *this; } + ThreadTime& operator-=(const int64_t& nMilliseconds) { long lTemp((long)nMilliseconds / 1000); tv_sec -= lTemp; tv_nsec -= (long)((nMilliseconds - (lTemp * 1000)) * 1000000); if(tv_nsec < 0) { tv_sec--; tv_nsec += 1000000000; } return *this; } + ThreadTime& operator+=(const ThreadTime& tt) { tv_sec += tt.tv_sec; tv_nsec += tt.tv_nsec; if(tv_nsec >= 1000000000){ tv_sec++; tv_nsec -= 1000000000; } return *this; } + ThreadTime& operator-=(const ThreadTime& tt) { tv_sec -= tt.tv_sec; tv_nsec -= tt.tv_nsec; if(tv_nsec < 0) { tv_sec--; tv_nsec += 1000000000; } return *this; } + }; + inline ThreadTime operator+ (const ThreadTime& tt1, const ThreadTime& tt2) { ThreadTime ttR(tt1); ttR += tt2; return ttR; } + inline ThreadTime operator+ (const ThreadTime& tt, const int64_t& nMilliseconds){ ThreadTime ttR(tt); ttR += nMilliseconds; return ttR; } + inline ThreadTime operator- (const ThreadTime& tt1, const ThreadTime& tt2) { ThreadTime ttR(tt1); ttR -= tt2; return ttR; } + inline ThreadTime operator- (const ThreadTime& tt, const int64_t& nMilliseconds){ ThreadTime ttR(tt); ttR -= nMilliseconds; return ttR; } + inline bool operator==(const ThreadTime& tt1, const ThreadTime& tt2) { return (tt1.tv_nsec == tt2.tv_nsec) && (tt1.tv_sec == tt2.tv_sec); } // These comparisons assume that the nsec value is normalized (always between 0 && 1000000000). + inline bool operator!=(const ThreadTime& tt1, const ThreadTime& tt2) { return (tt1.tv_nsec != tt2.tv_nsec) || (tt1.tv_sec != tt2.tv_sec); } + inline bool operator< (const ThreadTime& tt1, const ThreadTime& tt2) { return (tt1.tv_sec == tt2.tv_sec) ? (tt1.tv_nsec < tt2.tv_nsec) : (tt1.tv_sec < tt2.tv_sec); } + inline bool operator> (const ThreadTime& tt1, const ThreadTime& tt2) { return (tt1.tv_sec == tt2.tv_sec) ? (tt1.tv_nsec > tt2.tv_nsec) : (tt1.tv_sec > tt2.tv_sec); } + inline bool operator<=(const ThreadTime& tt1, const ThreadTime& tt2) { return (tt1.tv_sec == tt2.tv_sec) ? (tt1.tv_nsec <= tt2.tv_nsec) : (tt1.tv_sec <= tt2.tv_sec); } + inline bool operator>=(const ThreadTime& tt1, const ThreadTime& tt2) { return (tt1.tv_sec == tt2.tv_sec) ? (tt1.tv_nsec >= tt2.tv_nsec) : (tt1.tv_sec >= tt2.tv_sec); } + + const ThreadTime kTimeoutImmediate(0, 0); /// Used to specify to functions to return immediately if the operation could not be done. + const ThreadTime kTimeoutNone(INT_MAX, INT_MAX); /// Used to specify to functions to block without a timeout (i.e. block forever). + const ThreadTime kTimeoutYield(0, 0); /// Used to specify to ThreadSleep to yield to threads of equivalent priority. + + #define EA_THREADTIME_AS_INT64(t) ((int64_t)(((t).tv_sec * 1000) + ((t).tv_nsec / 1000000))) /// Returns the time as int64_t milliseconds. + #define EA_THREADTIME_AS_INT64_MICROSECONDS(t) ((int64_t)(((t).tv_sec * 1000000) + (((t).tv_nsec / 1000)))) /// Returns the time as int64_t microseconds. + #define EA_THREADTIME_AS_DOUBLE(t) (((t).tv_sec * 1000.0) + ((t).tv_nsec / 1000000.0)) /// Returns the time as double milliseconds. + + #elif defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_X86_64) + typedef uint64_t ThreadTime; /// Current storage mechanism for time used by thread timeout functions. Units are milliseconds. + const ThreadTime kTimeoutImmediate = 0; /// Used to specify to functions to return immediately if the operation could not be done. + const ThreadTime kTimeoutNone = UINT64_MAX; /// Used to specify to functions to block without a timeout (i.e. block forever). + const ThreadTime kTimeoutYield = 0; /// This is used with ThreadSleep to minimally yield to threads of equivalent priority. + + #define EA_THREADTIME_AS_INT64(t) ((int64_t)(t)) + #define EA_THREADTIME_AS_DOUBLE(t) ((double)(t)) + + #else + typedef unsigned ThreadTime; /// Current storage mechanism for time used by thread timeout functions. Units are milliseconds. + const ThreadTime kTimeoutImmediate = 0; /// Used to specify to functions to return immediately if the operation could not be done. + const ThreadTime kTimeoutNone = UINT_MAX; /// Used to specify to functions to block without a timeout (i.e. block forever). + const ThreadTime kTimeoutYield = 0; /// This is used with ThreadSleep to minimally yield to threads of equivalent priority. + + #define EA_THREADTIME_AS_INT64(t) ((int64_t)(t)) + #define EA_THREADTIME_AS_DOUBLE(t) ((double)(t)) + + #endif + + #if defined(EA_PLATFORM_MICROSOFT) /// Can be removed from C++11 Concurrency builds once full C++11 implementation is completed + uint32_t RelativeTimeoutFromAbsoluteTimeout(ThreadTime absoluteTimeout); + #endif + + // Thread priority constants + // There is a standardized mechanism to convert system-specific thread + // priorities to these platform-independent priorities and back without + // loss of precision or behaviour. The convention is that kThreadPriorityDefault + // equates to the system-specific normal thread priority. Thus for Microsoft + // APIs a thread with priority kThreadPriorityDefault will be of Microsoft + // priority THREAD_PRIORITY_NORMAL. A thread with an EAThread priority + // of kThreadPriorityDefault + 1 will have a Microsoft priority of THREAD_PRIORITY_NORMAL + 1. + // The only difference is that with EAThread all platforms are standardized on + // kThreadPriorityDefault as the normal value and that higher EAThread priority + // integral values mean higher thread priorities for running threads. This last + // item is of significance because Sony platforms natively define lower integers + // to mean higher thread priorities. With EAThread you get consistent behaviour + // across platforms and thus kThreadPriorityDefault + 1 always results in a + // thread that runs at priority of one level higher. On Sony platforms, this + 1 + // gets translated to a - 1 when calling the Sony native thread priority API. + // EAThread priorities have no mandated integral bounds, though + // kThreadPriorityMin and kThreadPriorityMax are defined as convenient practical + // endpoints for users. Users should not generally use hard-coded constants to + // refer to EAThread priorities much like it's best not to use hard-coded + // constants to refer to platform-specific native thread priorities. Also, users + // generally want to avoid manipulating thread priorities to the extent possible + // and use conventional synchronization primitives to control execution. + // Similarly, wildly varying thread priorities such as +100 are not likely to + // achieve much and are not likely to be very portable. + // + const int kThreadPriorityUnknown = INT_MIN; /// Invalid or unknown priority. + const int kThreadPriorityMin = -128; /// Minimum thread priority enumerated by EAThread. In practice, a valid thread priority can be anything other than kThreadPriorityUnknown. + const int kThreadPriorityDefault = 0; /// Default (a.k.a. normal) thread priority. + const int kThreadPriorityMax = 127; /// Maximum thread priority enumerated by EAThread. In practice, a valid thread priority can be anything other than kThreadPriorityUnknown. + + + + /// kSysThreadPriorityDefault + /// Defines the platform-specific default thread priority. + #if defined(EA_PLATFORM_SONY) + const int kSysThreadPriorityDefault = 700; + #elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + const int kSysThreadPriorityDefault = 0; // Some Unix variants use values other than zero, but these are not relevant. + #elif defined(EA_PLATFORM_MICROSOFT) + const int kSysThreadPriorityDefault = 0; // Same as THREAD_PRIORITY_NORMAL + #else + const int kSysThreadPriorityDefault = 0; + #endif + + + // The following functions are standalone and not static members of the thread class + // because they are potentially used by multiple threading primitives and we don't + // want to create a dependency of threading primitives on class Thread. + + /// GetThreadId + /// Gets the thread ID for the current thread. This thread ID should + /// be unique throughout the system. + EATHREADLIB_API ThreadId GetThreadId(); + + + /// GetSysThreadId + /// Gets the operating system thread id associated with the given ThreadId. + /// It turns out that Microsoft operating systems (Windows, XBox, XBox 360) + /// have two different ways to identify a thread: HANDLE and DWORD. Some API + /// functions take thread HANDLES, while others take what Microsoft calls + /// thread ids (DWORDs). EAThread ThreadId is a HANDLE, as that is used for + /// more of the core threading APIs. However, some OS-level APIs accept instead + /// the DWORD thread id. This function returns the OS thread id for a given + /// EAThread ThreadId. In the case of Microsoft OSs, this returns a DWORD from + /// a HANDLE and with other OSs this function simply returns the ThreadId. + /// Returns a valid SysThreadId or kSysThreadIdInvalid if the input id is invalid. + EATHREADLIB_API SysThreadId GetSysThreadId(ThreadId id); + + + /// GetThreadId + /// + /// This is a portable function to convert between ThreadId's and SysThreadId's. + /// For platforms that do not differentiate between these two types no conversion is attempted. + EATHREADLIB_API ThreadId GetThreadId(SysThreadId id); + + + /// GetSysThreadId + /// Gets the SysThreadId for the current thread. This thread ID should + /// be unique throughout the system. + EATHREADLIB_API SysThreadId GetSysThreadId(); + + + /// GetThreadPriority + /// Gets the priority of the current thread. + /// This function can return any int except for kThreadPriorityUnknown, as the + /// current thread's priority will always be knowable. A return value of kThreadPriorityDefault + /// means that this thread is of normal (a.k.a. default) priority. + /// See the documentation for thread priority constants (e.g. kThreadPriorityDefault) + /// for more information about thread priority values and behaviour. + EATHREADLIB_API int GetThreadPriority(); + + + /// SetThreadPriority + /// Sets the priority of the current thread. + /// Accepts any integer priority value except kThreadPriorityUnknown. + /// On some platforms, this function will automatically convert any invalid + /// priority for that particular platform to a valid one. A normal (a.k.a. default) thread + /// priority is identified by kThreadPriorityDefault. + /// See the documentation for thread priority constants (e.g. kThreadPriorityDefault) + /// for more information about thread priority values and behaviour. + EATHREADLIB_API bool SetThreadPriority(int nPriority); + + + /// GetThreadStackBase + /// Returns the base address of the current thread's stack. + /// Recall that on all supported platforms that the stack grows downward + /// and thus that the stack base address is of a higher value than the + /// stack's contents. + EATHREADLIB_API void* GetThreadStackBase(); + + + // Thread processor constants + const int kProcessorDefault = -1; /// Use the default processor for the platform. On many platforms, the default is to not be tied to any specific processor, but other threads can only ever be bound to a single processor. + const int kProcessorAny = -2; /// Run the thread on any processor. Many platforms will switch threads between processors dynamically. + + + /// SetThreadProcessor + /// Sets the processor the current thread should run on. Valid values + /// are kThreadProcessorDefault, kThreadProcessorAny, or a processor + /// index in the range of [0, processor count). If the input value + /// is >= the processor count, it will be reduced to be a modulo of + /// the processor count. Any other invalid value will cause the processor + /// to be set to zero. + /// This function isn't guaranteed to restrict the thread from executing + /// on the given processor for all platforms. Some platforms don't support + /// assigning thread processor affinity, while with others (e.g. Windows using + /// SetThreadIdealProcessor) the OS tries to comply but will use a different + /// processor when the assigned one is unavailable. + EATHREADLIB_API void SetThreadProcessor(int nProcessor); + + + /// GetThreadProcessor + /// Returns the (possibly virtual) CPU index that the thread is currently + /// running on. Different systems may have differing definitions of what + /// a unique processor is. Some CPUs have multiple sub-CPUs (e.g. "cores") + /// which are treated as unique processors by the system. + /// Many systems switch threads between processors dynamically; thus it's + /// possible that the thread may be on a different CPU by the time this + /// function returns. + /// Lastly, some systems don't provide the ability to detect what processor + /// the current thread is running on; in these cases this function returns 0. + EATHREADLIB_API int GetThreadProcessor(); + + + /// GetProcessorCount + /// Returns the (possibly virtual) CPU count that the current system has. + /// Some systems (e.g. Posix, Unix) don't expose an ability to tell how + /// many processors there are; in these cases this function returns 1. + /// This function returns the number of currently active processors. + /// Some systems can modify the number of active processors dynamically. + EATHREADLIB_API int GetProcessorCount(); + + + /// kThreadAffinityMaskAny + /// Defines the thread affinity mask that enables the thread + /// to float on all available processors. + typedef uint64_t ThreadAffinityMask; + const ThreadAffinityMask kThreadAffinityMaskAny = ~0ULL; + + /// GetAvailableCpuAffinityMask + /// Returns the thread affinity mask with only the available + /// processors. + /// If a theoretical platform reserves core 7 (counting from 0) in an + /// 8 core system, the affinity mask would be 0x7f. + EATHREADLIB_API ThreadAffinityMask GetAvailableCpuAffinityMask(); + + /// SetThreadAffinityMask + /// + /// The nAffinityMask is a bit field where each bit designates a processor. + /// + /// This function isn't guaranteed to restrict the thread from executing + /// on the given processor for all platforms. Some platforms don't support + /// assigning thread processor affinity, while with others (e.g. Windows using + /// SetThreadIdealProcessor) the OS tries to comply but will use a different + /// processor when the assigned one is unavailable. + EATHREADLIB_API void SetThreadAffinityMask(ThreadAffinityMask nAffinityMask); + EATHREADLIB_API void SetThreadAffinityMask(const EA::Thread::ThreadId& id, ThreadAffinityMask nAffinityMask); + + + /// GetThreadAffinityMask + /// + /// Returns the current thread affinity mask specified by the user. + EATHREADLIB_API ThreadAffinityMask GetThreadAffinityMask(); + EATHREADLIB_API ThreadAffinityMask GetThreadAffinityMask(const EA::Thread::ThreadId& id); + + + /// GetName + /// Returns the name of the thread assigned by the SetName function. + /// If the thread was not named by the SetName function, then the name is empty (""). + EATHREADLIB_API const char* GetThreadName(); + EATHREADLIB_API const char* GetThreadName(const EA::Thread::ThreadId& id); + + + /// SetThreadName + /// + /// Sets a descriptive name or the thread. On some platforms this name is passed on + /// to the debugging tools so they can see this name. The name length, including a + /// terminating 0 char, is limited to EATHREAD_NAME_SIZE characters. Any characters + /// beyond that are ignored. + /// + /// You can set the name of a Thread object only if it has already begun. You can + /// also set the name with the Begin function via the ThreadParameters argument to + /// Begin. This design is in order to simplify the implementation, but being able + /// to set ThreadParameters before Begin is something that can be considered in the + /// future. + /// + /// Some platforms (e.g. Linux) have the restriction that this function works + /// properly only when called by the same thread that you want to name. Given this + /// situation, the most portable way to use this SetName function is to either + /// always call it from the thread to be named or to use the ThreadParameters to + /// give the thread a name before it is started and let the started thread name + /// itself. + // + // + // + EATHREADLIB_API void SetThreadName(const char* pName); + EATHREADLIB_API void SetThreadName(const EA::Thread::ThreadId& id, const char* pName); + + + /// ThreadSleep + /// Puts the current thread to sleep for an amount of time hinted at + /// by the time argument. The timeout is merely a hint and the system + /// thread scheduler might return well before the sleep time has elapsed. + /// The input 'timeRelative' refers to a relative time and not an + /// absolute time such as used by EAThread mutexes, semaphores, etc. + /// This is for consistency with other threading systems such as Posix and Win32. + /// A sleep time of zero has the same effect as simply yielding to other + /// available threads. + /// + EATHREADLIB_API void ThreadSleep(const ThreadTime& timeRelative = kTimeoutImmediate); + + + /// ThreadCooperativeYield + /// On platforms that use cooperative multithreading instead of + /// pre-emptive multithreading, this function maps to ThreadSleep(0). + /// On pre-emptive platforms, this function is a no-op. The intention + /// is to allow cooperative multithreaded systems to yield manually + /// in order for other threads to run, but also not to penalize + /// pre-emptive systems that don't need such manual yielding. If you + /// want to forcefully yield on a pre-emptive system, call ThreadSleep(0). + #ifdef EA_THREAD_COOPERATIVE + #define ThreadCooperativeYield() EA::Thread::ThreadSleep(EA::Thread::kTimeoutYield) + #else + #define ThreadCooperativeYield() + #endif + + + /// End + /// This function provides a way for a thread to end itself. + EATHREADLIB_API void ThreadEnd(intptr_t threadReturnValue); + + + /// GetThreadTime + /// Gets the current absolute time in milliseconds. + /// This is required for working with absolute timeouts, for example. + /// To specify a timeout that is relative to the current time, simply + /// add time (in milliseconds) to the return value of GetThreadTime. + /// Alternatively, you can use ConvertRelativeTime to calculate an absolute time. + EATHREADLIB_API ThreadTime GetThreadTime(); + + + /// ConvertRelativeTime + /// Given a relative time (in milliseconds), this function returns an + /// absolute time (in milliseconds). + /// Example usage: + /// mutex.Lock(ConvertRelativeTime(1000)); + EATHREADLIB_API inline ThreadTime ConvertRelativeTime(const ThreadTime& timeRelative) + { + return GetThreadTime() + timeRelative; + } + + /// SetAssertionFailureFunction + /// Allows the user to specify a callback function to trap assertion failures. + /// You can use this to glue your own assertion system into this system. + typedef void (*AssertionFailureFunction)(const char* pExpression, void* pContext); + EATHREADLIB_API void SetAssertionFailureFunction(AssertionFailureFunction pAssertionFailureFunction, void* pContext); + + + /// AssertionFailure + /// Triggers an assertion failure. This function is generally intended for internal + /// use but is available so that related code can use the same system. + EATHREADLIB_API void AssertionFailure(const char* pExpression); + EATHREADLIB_API void AssertionFailureV(const char* pFormat, ...); + + + + + /// Allocator + /// This is the same as (the first four functions of) ICoreAllocator. + /// If the allocator is set via SetAllocator, then it must be done before + /// any other thread operations which might allocate memory are done. + /// Typically this includes creating objects via factory functions and + /// creating threads whereby you specify that thread resources be allocated for you.. + class Allocator + { + public: + virtual ~Allocator() {} + virtual void* Alloc(size_t size, const char* name = 0, unsigned int flags = 0) = 0; + virtual void* Alloc(size_t size, const char* name, unsigned int flags, + unsigned int align, unsigned int alignOffset = 0) = 0; + virtual void Free(void* block, size_t size=0) = 0; + }; + + EATHREADLIB_API void SetAllocator(Allocator* pAllocator); + EATHREADLIB_API Allocator* GetAllocator(); + + EATHREADLIB_API void SetAllocator(EA::Allocator::ICoreAllocator* pAllocator); + + } // namespace Thread + +} // namespace EA + + + +/// EAThreadGetUniqueId +/// +/// Gets a value that is unique per thread but isn't necessarily the system-recognized +/// thread id. This function is at least as fast as GetThreadId, and on some platforms +/// is potentially significantly faster due to being implemented in inline asm which +/// avoids a system function call which may cause an instruction cache miss penalty. +/// This function is useful for creating very fast implementations of some kinds of +/// threading constructs. It's implemented as a macro instead of a function in order +/// to optimizing inlining success across all platforms and compilers. +/// +/// This function is guaranteed to yield a valid value; there are no error conditions. +/// +/// This macro acts as if it were declared as a function like this: +/// void EAThreadGetUniqueId(ThreadUniqueId& result); +/// +/// Example usage: +/// ThreadUniqueId x; +/// EAThreadGetUniqueId(x); +/// +#if EA_USE_CPP11_CONCURRENCY + #define EAThreadGetUniqueId(dest) (dest = static_cast(std::hash()(std::this_thread::get_id()))) + +#elif defined(EA_PLATFORM_WINDOWS) && defined(EA_COMPILER_MSVC) && !defined(EA_PLATFORM_WIN64) + + // Reference implementation: + //extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(); + //#define EAThreadGetUniqueId(dest) dest = (ThreadUniqueId)(uintptr_t)GetCurrentThreadId() + + // Fast implementation: + extern "C" unsigned long __readfsdword(unsigned long offset); + #pragma intrinsic(__readfsdword) + #define EAThreadGetUniqueId(dest) dest = (EA::Thread::ThreadUniqueId)(uintptr_t)__readfsdword(0x18) + +#elif defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86_64) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + #define EAThreadGetUniqueId(dest) dest = (EA::Thread::ThreadUniqueId)(uintptr_t)__readgsqword(0x30) + // Could also use dest = (EA::Thread::ThreadUniqueId)NtCurrentTeb(), but that would require #including , which is very heavy. + +#else + + // Reference implementation: + #define EAThreadGetUniqueId(dest) dest = (EA::Thread::ThreadUniqueId)(uintptr_t)EA::Thread::GetThreadId() + +#endif + + +// EAThreadIdToString +// Convert a thread id to a string suitable for use with printf like functions, e.g.: +// printf("%s", EAThreadIdToString(myThreadId)); +// This macro is intended for debugging purposes and makes no guarantees about performance +// or how a thread id is mapped to a string. +namespace EA +{ + namespace Thread + { + namespace detail + { + struct EATHREADLIB_API ThreadIdToStringBuffer + { + public: + enum { BufSize = 32 }; + explicit ThreadIdToStringBuffer(EA::Thread::ThreadId threadId); + const char* c_str() const { return mBuf; } + private: + char mBuf[BufSize]; + }; + + struct EATHREADLIB_API SysThreadIdToStringBuffer + { + public: + enum { BufSize = 32 }; + explicit SysThreadIdToStringBuffer(EA::Thread::SysThreadId sysThreadId); + const char* c_str() const { return mBuf; } + private: + char mBuf[BufSize]; + }; + } + } +} + +#if !defined(EAThreadThreadIdToString) + #define EAThreadThreadIdToString(threadId) (EA::Thread::detail::ThreadIdToStringBuffer(threadId).c_str()) +#endif +#if !defined(EAThreadSysThreadIdToString) + #define EAThreadSysThreadIdToString(sysThreadId) (EA::Thread::detail::SysThreadIdToStringBuffer(sysThreadId).c_str()) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Inline functions +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + // We implement GetSysThreadId in our associated .cpp file. +#elif defined(EA_PLATFORM_SONY) + // We implement GetSysThreadId in our associated .cpp file. +#elif defined(EA_PLATFORM_APPLE) + // We implement GetSysThreadId in our associated .cpp file. +#elif EA_USE_CPP11_CONCURRENCY + // We implement GetSysThreadId in our associated .cpp file. +#else + inline EA::Thread::SysThreadId EA::Thread::GetSysThreadId(ThreadId id) + { + return id; + } + + inline EA::Thread::SysThreadId EA::Thread::GetSysThreadId() + { + return GetThreadId(); // ThreadId == SysThreadId in this case + } +#endif + +#endif // EATHREAD_EATHREAD_H + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_atomic.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_atomic.h new file mode 100644 index 00000000..cef132f9 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_atomic.h @@ -0,0 +1,467 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Defines functionality for thread-safe primitive operations. +// +// EAThread atomics do NOT imply the use of read/write barriers. This is +// partly due to historical reasons and partly due to EAThread's internal +// code being optimized for not using barriers. +// +// In future, we are considering migrating the atomics interface which +// defaults atomics to use full read/write barriers while allowing users +// to opt-out of full barrier usage. The new C++11 interface already provides +// similar interfaces. +// +// http://en.cppreference.com/w/cpp/atomic/memory_order +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_ATOMIC_H +#define EATHREAD_EATHREAD_ATOMIC_H + + +#include +EA_DISABLE_ALL_VC_WARNINGS() +#include +EA_RESTORE_ALL_VC_WARNINGS() +#include +#include +#include + + +#if !EA_THREADS_AVAILABLE + // Do nothing. Let the default implementation below be used. +#elif defined(EA_USE_COMMON_ATOMICINT_IMPLEMENTATION) && EA_USE_COMMON_ATOMICINT_IMPLEMENTATION + #include +#elif defined(EA_PROCESSOR_X86) || (defined(EA_PLATFORM_WINRT) && defined(EA_PROCESSOR_ARM)) + #include +#elif defined(EA_PROCESSOR_X86_64) + #include +#elif defined(EA_PLATFORM_ANDROID) && EATHREAD_C11_ATOMICS_AVAILABLE + #include // Android API 21+ only support C11 atomics +#else + #error Platform not supported yet. +#endif + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + + +// EATHREAD_ATOMIC_128_SUPPORTED +// +// Defined as 0 or 1. Defined as 1 whenever possible for the given compiler/platform combination. +// Defines if 128 bit atomic operations are supported. +// Such operations are only ever supported on 64 bit platforms. +// +#ifndef EATHREAD_ATOMIC_128_SUPPORTED // If not defined by one of the above headers... + #define EATHREAD_ATOMIC_128_SUPPORTED 0 +#endif + +namespace EA +{ + namespace Thread + { + enum Atomic64Implementation + { + kAtomic64Emulated, + kAtomic64Native + }; + + /// SetDoubleWordAtomicsImplementation + /// Some platforms have multiple implementations, some of which support + /// double word atomics and some that don't. For example, certain ARM + /// processors will support the ldrexd/strexd atomic instructions but + /// others will not. + EATHREADLIB_API void SetAtomic64Implementation(Atomic64Implementation implementation); + } +} + + +#if !defined(EA_THREAD_ATOMIC_IMPLEMENTED) // If there wasn't a processor-specific version already defined... + + // Fail the build if atomics aren't being defined for the given platform/compiler. + // If we need to add an exception here, we can add an appropriate ifdef. + static_assert(false, "atomic operations must be defined for this platform."); + + namespace EA + { + namespace Thread + { + /// Standalone atomic functions + /// These act the same as the class functions below. + /// The T return values are the previous value, except for the + /// AtomicFetchSwap function which returns the swapped out value. + /// + /// T AtomicGetValue(volatile T*); + /// T AtomicGetValue(const volatile T*); + /// void AtomicSetValue(volatile T*, T value); + /// T AtomicFetchIncrement(volatile T*); + /// T AtomicFetchDecrement(volatile T*); + /// T AtomicFetchAdd(volatile T*, T value); + /// T AtomicFetchSub(volatile T*, T value); + /// T AtomicFetchOr(volatile T*, T value); + /// T AtomicFetchAnd(volatile T*, T value); + /// T AtomicFetchXor(volatile T*, T value); + /// T AtomicFetchSwap(volatile T*, T value); + /// T AtomicFetchSwapConditional(volatile T*, T value, T condition); + /// bool AtomicSetValueConditional(volatile T*, T value, T condition); + + + + /// class AtomicInt + /// + /// Implements thread-safe access to an integer and primary operations on that integer. + /// AtomicIntegers are commonly used as lightweight flags and signals between threads + /// or as the synchronization object for spinlocks. Those familiar with the Win32 API + /// will find that AtomicInt32 is essentially a platform independent interface to + /// the Win32 InterlockedXXX family of functions. Those familiar with Linux may + /// find that AtomicInt32 is essentially a platform independent interface to atomic_t + /// functionality. + /// + /// Note that the reference implementation defined here is itself not thread-safe. + /// A thread-safe version requires platform-specific code. + /// + /// Example usage + /// AtomicInt32 i = 0; + /// + /// ++i; + /// i--; + /// i += 7; + /// i -= 3; + /// i = 2; + /// + /// int x = i.GetValue(); + /// i.Increment(); + /// bool oldValueWas6 = i.SetValueConditional(3, 6); + /// i.Add(4); + /// + template + class EATHREADLIB_API AtomicInt + { + public: + /// ThisType + /// A typedef for this class type itself, for usage clarity. + typedef AtomicInt ThisType; + + + /// ValueType + /// A typedef for the basic object we work with. + typedef T ValueType; + + + /// AtomicInt + /// Empty constructor. Intentionally leaves mValue in an unspecified state. + /// This is done so that an AtomicInt acts like a standard built-in integer. + AtomicInt() + {} + + + /// AtomicInt + /// Constructs with an intial value. + AtomicInt(ValueType n) + : mValue(n) {} + + + /// AtomicInt + /// Copy ctor. Uses GetValue to read the value, and thus is synchronized. + AtomicInt(const ThisType& x) + : mValue(x.GetValue()) {} + + + /// AtomicInt + /// Assignment operator. Uses GetValue to read the value, and thus is synchronized. + AtomicInt& operator=(const ThisType& x) + { mValue = x.GetValue(); return *this; } + + + /// GetValue + /// Safely gets the current value. A platform-specific version of + /// this might need to do something more than just read the value. + ValueType GetValue() const + { return mValue; } + + + /// GetValueRaw + /// "Unsafely" gets the current value. This is useful for algorithms + /// that want to poll the value in a high performance way before + /// reading or setting the value in a more costly thread-safe way. + /// You should not use this function when attempting to do thread-safe + /// atomic operations. + ValueType GetValueRaw() const + { return mValue; } + + + /// SetValue + /// Safely sets a new value. Returns the old value. Note that due to + /// expected multithreaded accesses, a call to GetValue after SetValue + /// might return a different value then what was set with SetValue. + /// This of course depends on your situation. + ValueType SetValue(ValueType n) + { + const ValueType nOldValue(mValue); + mValue = n; + return nOldValue; + } + + + /// SetValueConditional + /// Safely set the value to a new value if the original value is equal to + /// a condition value. Returns true if the condition was met and the + /// assignment occurred. The comparison and value setting are done as + /// an atomic operation and thus another thread cannot intervene between + /// the two as would be the case with simple C code. + bool SetValueConditional(ValueType n, ValueType condition) + { + if(mValue == condition) + { + mValue = n; + return true; + } + return false; + } + + + /// Increment + /// Safely increments the value. Returns the new value. + /// This function acts the same as the C++ pre-increment operator. + ValueType Increment() + { return ++mValue; } + + + /// Decrement + /// Safely decrements the value. Returns the new value. + /// This function acts the same as the C++ pre-decrement operator. + ValueType Decrement() + { return --mValue; } + + + /// Add + /// Safely adds a value, which can be negative. Returns the new value. + /// You can implement subtraction with this function by using a negative argument. + ValueType Add(ValueType n) + { return (mValue += n); } + + + /// operators + /// These allow an AtomicInt object to safely act like a built-in type. + /// + /// Note: The operators for AtomicInt behaves differently than standard + /// C++ operators in that it will always return a ValueType instead + /// of a reference. + /// + /// cast operator + /// Returns the AtomicInt value as an integral type. This allows the + /// AtomicInt to behave like a standard built-in integer type. + operator const ValueType() const + { return mValue; } + + /// operator = + /// Assigns a new value and returns the value after the operation. + /// + ValueType operator=(ValueType n) + { + mValue = n; + return n; + } + + /// pre-increment operator+= + /// Adds a value to the AtomicInt and returns the value after the operation. + /// + /// This function doesn't obey the C++ standard in that it does not return + /// a reference, but rather the value of the AtomicInt after the + /// operation is complete. It must be noted that this design is motivated by + /// the fact that it is unsafe to rely on the returned value being equal to + /// the previous value + n, as another thread might have modified the AtomicInt + /// immediately after the subtraction operation. So rather than returning the + /// reference of AtomicInt, the function returns a copy of the AtomicInt value + /// used in the function. + ValueType operator+=(ValueType n) + { + mValue += n; + return mValue; + } + + /// pre-increment operator-= + /// Subtracts a value to the AtomicInt and returns the value after the operation. + /// + /// This function doesn't obey the C++ standard in that it does not return + // a reference, but rather the value of the AtomicInt after the + /// operation is complete. It must be noted that this design is motivated by + /// the fact that it is unsafe to rely on the returned value being equal to + /// the previous value - n, as another thread might have modified the AtomicInt + /// immediately after the subtraction operation. So rather than returning the + /// reference of AtomicInt, the function returns a copy of the AtomicInt value + /// used in the function. + ValueType operator-=(ValueType n) + { + mValue -= n; + return mValue; + } + + /// pre-increment operator++ + /// Increments the AtomicInt. + /// + /// This function doesn't obey the C++ standard in that it does not return + // a reference, but rather the value of the AtomicInt after the + /// operation is complete. It must be noted that this design is motivated by + /// the fact that it is unsafe to rely on the returned value being equal to + /// the previous value + 1, as another thread might have modified the AtomicInt + /// immediately after the subtraction operation. So rather than returning the + /// reference of AtomicInt, the function returns a copy of the AtomicInt value + /// used in the function. + ValueType operator++() + { return ++mValue; } + + /// post-increment operator++ + /// Increments the AtomicInt and returns the value of the AtomicInt before + /// the increment operation. + /// + /// This function doesn't obey the C++ standard in that it does not return + // a reference, but rather the value of the AtomicInt after the + /// operation is complete. It must be noted that this design is motivated by + /// the fact that it is unsafe to rely on the returned value being equal to + /// the previous value, as another thread might have modified the AtomicInt + /// immediately after the subtraction operation. So rather than returning the + /// reference of AtomicInt, the function returns a copy of the AtomicInt value + /// used in the function. + ValueType operator++(int) + { return mValue++; } + + /// pre-increment operator-- + /// Decrements the AtomicInt. + /// + /// This function doesn't obey the C++ standard in that it does not return + // a reference, but rather the value of the AtomicInt after the + /// operation is complete. It must be noted that this design is motivated by + /// the fact that it is unsafe to rely on the returned value being equal to + /// the previous value - 1, as another thread might have modified the AtomicInt + /// immediately after the subtraction operation. So rather than returning the + /// reference of AtomicInt, the function returns a copy of the AtomicInt value + /// used in the function. + ValueType operator--() + { return --mValue; } + + /// post-increment operator-- + /// Increments the AtomicInt and returns the value of the AtomicInt before + /// the increment operation. + /// + /// This function doesn't obey the C++ standard in that it does not return + // a reference, but rather the value of the AtomicInt after the + /// operation is complete. It must be noted that this design is motivated by + /// the fact that it is unsafe to rely on the returned value being equal to + /// the previous value, as another thread might have modified the AtomicInt + /// immediately after the subtraction operation. So rather than returning the + /// reference of AtomicInt, the function returns a copy of the AtomicInt value + /// used in the function. + ValueType operator--(int) + { return mValue--;} + + protected: + volatile ValueType mValue; /// May not be the same on all platforms. + }; + + + } // namespace Thread + + } // namespace EA + +#endif // #if EA_THREAD_ATOMIC_IMPLEMENTED + + + + + +namespace EA +{ + namespace Thread + { + + // Typedefs + typedef AtomicInt AtomicInt32; /// int32_t atomic integer. + typedef AtomicInt AtomicUint32; /// uint32_t atomic integer. + typedef AtomicInt AtomicInt64; /// int64_t atomic integer. + typedef AtomicInt AtomicUint64; /// uint64_t atomic integer. + + #if !defined(EA_PLATFORM_WORD_SIZE) || (EA_PLATFORM_WORD_SIZE == 4) + typedef AtomicInt32 AtomicIWord; + typedef AtomicUint32 AtomicUWord; + #else + typedef AtomicInt64 AtomicIWord; + typedef AtomicUint64 AtomicUWord; + #endif + + #if !defined(EA_PLATFORM_PTR_SIZE) || (EA_PLATFORM_PTR_SIZE == 4) + typedef AtomicInt32 AtomicIntPtr; + typedef AtomicUint32 AtomicUintPtr; + #else + typedef AtomicInt64 AtomicIntPtr; + typedef AtomicUint64 AtomicUintPtr; + #endif + + + // VC++ yields spurious warnings about void* being cast to an integer type and vice-versa. + // These warnings are baseless because we check for platform pointer size above. + EA_DISABLE_VC_WARNING(4311 4312 4251) + + + /// class AtomicPointer + /// + /// For simplicity of the current implementation, we simply have AtomicPointer map + /// to AtomicInt32. This is reasonably safe because AtomicInt32 uses intptr_t + /// as its ValueType and there are no foreseeble supported platforms in which + /// intptr_t will not exist or be possible as a data type. + /// + class EATHREADLIB_API AtomicPointer : public AtomicIntPtr + { + public: + typedef void* PointerValueType; + + AtomicPointer(void* p = NULL) + : AtomicIntPtr(static_cast(reinterpret_cast(p))) {} + + AtomicPointer& operator=(void* p) + { AtomicIntPtr::operator=(static_cast(reinterpret_cast(p))); return *this; } + + operator const void*() const // It's debateable whether this should be supported. + { return (void*)AtomicIntPtr::GetValue(); } + + void* GetValue() const + { return (void*)AtomicIntPtr::GetValue(); } + + void* GetValueRaw() const + { return (void*)AtomicIntPtr::GetValueRaw(); } + + void* SetValue(void* p) + { return (void*)AtomicIntPtr::SetValue(static_cast(reinterpret_cast(p))); } + + bool SetValueConditional(void* p, void* pCondition) + { return AtomicIntPtr::SetValueConditional(static_cast(reinterpret_cast(p)), static_cast(reinterpret_cast(pCondition))); } + }; + + + EA_RESTORE_VC_WARNING() + + } // namespace Thread + +} // namespace EA + + +#endif // EATHREAD_EATHREAD_ATOMIC_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_barrier.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_barrier.h new file mode 100644 index 00000000..dd716d1f --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_barrier.h @@ -0,0 +1,248 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements Posix-style barriers. +// Note that thread synchronization barriers are different from +// memory synchronization barriers (a.k.a. fences). +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_BARRIER_H +#define EATHREAD_EATHREAD_BARRIER_H + + +#include +#include + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // Suppress warning about class 'AtomicInt32' needs to have a + // dll-interface to be used by clients of class which have a templated member. + // + // These templates cannot be instantiated outside of the DLL. If you try, a + // link error will result. This compiler warning is intended to notify users + // of this. + EA_DISABLE_VC_WARNING(4251) +#endif + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +///////////////////////////////////////////////////////////////////////// +/// EABarrierData +/// +/// This is used internally by class Barrier. +/// Todo: Consider moving this declaration into a platform-specific +/// header file. +/// + +#if defined(EA_PLATFORM_SONY) + #include + #include + + // We implement the barrier manually, as not all Posix thread implementations + // have barriers and even those that have it lack a timeout wait version. + struct EABarrierData{ + ScePthreadCond mCV; // Wait for barrier. + ScePthreadMutex mMutex; // Control access to barrier. + int mnHeight; // Number of threads required. + int mnCurrent; // Current number of threads. As threads wait, this value decreases towards zero. + unsigned long mnCycle; // Cycle count. + bool mbValid; // True if valid. + + EABarrierData(); + }; + +#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE + #include + + // We implement the barrier manually, as not all Posix threads implemetnation + // have barrers and even those that have it lack a timeout wait version. + struct EABarrierData{ + pthread_cond_t mCV; // Wait for barrier. + pthread_mutex_t mMutex; // Control access to barrier. + int mnHeight; // Number of threads required. + int mnCurrent; // Current number of threads. As threads wait, this value decreases towards zero. + unsigned long mnCycle; // Cycle count. + bool mbValid; // True if valid. + + EABarrierData(); + }; + +#else // All other platforms + #include + #include + + struct EATHREADLIB_API EABarrierData{ + EA::Thread::AtomicInt32 mnCurrent; // Current number of threads. As threads wait, this value decreases towards zero. + int mnHeight; // Number of threads required. + EA::Thread::AtomicInt32 mnIndex; // Which semaphore we are using. + EA::Thread::Semaphore mSemaphore0; // First semaphore. We can't use an array of Semaphores, because that would + EA::Thread::Semaphore mSemaphore1; // Second semaphore. intefere with our ability to initialize them our way. + EABarrierData(); + + private: + // Prevent default generation of these functions by declaring but not defining them. + EABarrierData(const EABarrierData& rhs); // copy constructor + EABarrierData& operator=(const EABarrierData& rhs); // assignment operator + }; + +#endif + + + + +namespace EA +{ + namespace Thread + { + /// BarrierParameters + /// Specifies barrier settings. + struct EATHREADLIB_API BarrierParameters + { + int mHeight; /// Barrier 'height'. Refers to number of threads which must wait before being released. + bool mbIntraProcess; /// True if the semaphore is intra-process, else inter-process. + char mName[16]; /// Barrier name, applicable only to platforms that recognize named synchronization objects. + + BarrierParameters(int height = 0, bool bIntraProcess = true, const char* pName = NULL); + }; + + + /// Barrier + /// A Barrier is a synchronization point for a set of threads. A barrier has + /// a count associated with it and threads call the wait function until the + /// given count of threads have reached the wait point. Then all threads + /// are released. The first thread released is given a special return value + /// that identifies it uniquely so that one-time work can be done. + /// + /// A primary use of barriers is to spread out work between a number of threads + /// and wait until the work is complete. For example, if you want to find and + /// count all objects of a given kind in a large grid, you might have four + /// threads each work on a quadrant and wait on the barrier until all are + /// finished. This particular example is more practical on SMP systems than + /// uniprocessor systems, but there are also uniprocessor uses. It should be + /// noted, however, that a Barrier synchronizes the completion of -threads-, + /// and not necessarily the completion of -tasks-. There may or may not be + /// a direct correspondence between the two. + /// + class EATHREADLIB_API Barrier + { + public: + enum Result{ + kResultPrimary = 0, /// The barrier wait suceeded and this thread is the designated solitary primary thread. Similar to Posix "serial" thread. + kResultSecondary = 1, /// The barrier wait suceeded and this thread is one of the secondary threads. + kResultError = -1, /// The wait resulted in error, due to various possible reasons. + kResultTimeout = -2 /// The barrier wait timed out. + }; + + /// Barrier + /// For immediate default initialization, use no args. + /// For custom immediate initialization, supply a first argument. + /// For deferred initialization, use Barrier(NULL, false) then later call Init. + /// For deferred initialization of an array of objects, create an empty + /// subclass whose default constructor chains back to Barrier(NULL, false). + Barrier(const BarrierParameters* pBarrierParameters = NULL, bool bDefaultParameters = true); + + /// Barrier + /// This is a constructor which initializes the Barrier to a specific height + /// and intializes the other Barrier parameters to default values. See the + /// BarrierParameters struct for info on these default values. + Barrier(int height); + + /// ~Barrier + /// Destroys an existing Barrier. The Barrier must not be waited on + /// by any thread, otherwise the resulting behaviour is undefined. + ~Barrier(); + + /// Init + /// Initializes the Barrier; used in cases where it cannot be initialized + /// via the constructor (as in the case with default construction or + /// array initialization. + bool Init(const BarrierParameters* pBarrierParameters); + + /// Wait + /// Causes the current thread to wait until the designated number of threads have called Wait. + /// + /// Returns one of enum Result. + /// + /// A timeout means that the thread gives up its contribution to the height while + /// waiting for the full height to be achieved. A timeout of zero means that a thread + /// only succeeds if it is the final thread (the one which puts the height to full); + /// otherwise the call returns with a timeout. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + Result Wait(const ThreadTime& timeoutAbsolute = kTimeoutNone); + + /// GetPlatformData + /// Returns the platform-specific data handle for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + void* GetPlatformData() + { return &mBarrierData; } + + protected: + EABarrierData mBarrierData; + + private: + // Objects of this class are not copyable. + Barrier(const Barrier&){} + Barrier& operator=(const Barrier&){ return *this; } + }; + + + /// BarrierFactory + /// + /// Implements a factory-based creation and destruction mechanism for class Barrier. + /// A primary use of this would be to allow the Barrier implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API BarrierFactory + { + public: + static Barrier* CreateBarrier(); // Internally implemented as: return new Barrier; + static void DestroyBarrier(Barrier* pBarrier); // Internally implemented as: delete pBarrier; + + static size_t GetBarrierSize(); // Internally implemented as: return sizeof(Barrier); + static Barrier* ConstructBarrier(void* pMemory); // Internally implemented as: return new(pMemory) Barrier; + static void DestructBarrier(Barrier* pBarrier); // Internally implemented as: pBarrier->~Barrier(); + }; + + + } // namespace Thread + +} // namespace EA + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // re-enable warning(s) disabled above. + EA_RESTORE_VC_WARNING() +#endif + + +#endif // EATHREAD_EATHREAD_BARRIER_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_callstack.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_callstack.h new file mode 100644 index 00000000..037da7c3 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_callstack.h @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +#ifndef EATHREAD_EATHREAD_CALLSTACK_H +#define EATHREAD_EATHREAD_CALLSTACK_H + +#include +#include +#include + +namespace EA +{ + namespace Thread + { + #if defined(EA_PLATFORM_MICROSOFT) + /// This function is the same as EA::Thread::GetSysThreadId(ThreadId id). + /// This function converts from one type of Microsoft thread identifier to another. + /// threadId is the same as EA::Thread::ThreadId and is a Microsoft thread HANDLE. + /// The return value is a Microsoft DWORD thread id which is the same as EA::Thread::SysThreadId. + /// Upon failure, the return value will be zero. + EATHREADLIB_API uint32_t GetThreadIdFromThreadHandle(intptr_t threadId); + #endif + + /// EAGetInstructionPointer / GetInstructionPointer + /// + /// Returns the current instruction pointer (a.k.a. program counter). + /// This function is implemented as a macro, it acts as if its declaration + /// were like so: + /// void EAGetInstructionPointer(void*& p); + /// + /// For portability, this function should only be used as a standalone + /// statement on its own line. + /// + /// These functions return NULL/0 on error or if getting the value is not possible + /// + /// Example usage: + /// void* pInstruction; + /// EAGetInstructionPointer(pInstruction); + /// + #if defined(EA_COMPILER_MSVC) + + EATHREADLIB_API EA_NO_INLINE void GetInstructionPointer(void*& pInstruction); + + #define EAGetInstructionPointer(p) EA::Thread::GetInstructionPointer(p) + + #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + + EATHREADLIB_API EA_NO_INLINE void GetInstructionPointer(void*& pInstruction); + + #define EAGetInstructionPointer(p) EA::Thread::GetInstructionPointer(p) + + #elif defined(EA_COMPILER_ARM) || defined(EA_COMPILER_RVCT) + + #define EAGetInstructionPointer(p) \ + { \ + p = static_cast(__current_pc()); \ + } + + EA_FORCE_INLINE void GetInstructionPointer(void*& p) + { + EAGetInstructionPointer(p); + } + + #else + + #error "EAGetInstructionPointer() Not Supported On The Given Platform/Compiler!!!" + + #endif + + + /// EASetStackBase / SetStackBase / GetStackBase / GetStackLimit + /// + /// EASetStackBase as a macro and acts as if its declaration were like so: + /// void EASetStackBase(); + /// + /// EASetStackBase sets the current stack pointer as the bottom (beginning) + /// of the stack. Depending on the platform, the "bottom" may be up or down + /// depending on whether the stack grows upward or downward (usually it grows + /// downward and so "bottom" actually refers to an address that is above child + /// stack frames in memory. + /// This function is intended to be called on application startup as early as + /// possible, and in each created thread, as early as possible. Its purpose + /// is to record the beginning stack pointer because the platform doesn't provide + /// APIs to tell what it is, and we need to know it (e.g. so we don't overrun + /// it during stack unwinds). + /// + /// For portability, EASetStackBase should be used only as a standalone + /// statement on its own line, as it may include statements that can't work otherwise. + /// + /// Example usage: + /// int main(int argc, char** argv) { + /// EASetStackBase(); + /// . . . + /// } + /// + /// SetStackBase is a function which lets you explicitly set a stack bottom instead + /// of doing it automatically with EASetStackBase. If you pass NULL for pStackBase + /// then the function uses its stack location during its execution, which will be + /// a little less optimal than calling EASetStackBase. + /// + /// GetStackBase returns the stack bottom set by EASetStackBase or SetStackBase. + /// It returns NULL if no stack bottom was set or could be set. + /// + /// GetStackLimit returns the current stack "top", which will be lower than the stack + /// bottom in memory if the platform grows its stack downward. + + EATHREADLIB_API void SetStackBase(void* pStackBase); + inline void SetStackBase(uintptr_t pStackBase){ SetStackBase((void*)pStackBase); } + EATHREADLIB_API void* GetStackBase(); + EATHREADLIB_API void* GetStackLimit(); + + + #if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86) + #define EASetStackBase() \ + { \ + void* esp; \ + __asm { mov esp, ESP } \ + ::EA::Thread::SetStackBase(esp); \ + } + + #elif defined(EA_COMPILER_MSVC) && (defined(EA_PROCESSOR_X86_64) || defined(EA_PROCESSOR_ARM)) + // This implementation uses SetStackBase(NULL), which internally retrieves the stack pointer. + #define EASetStackBase() \ + { \ + ::EA::Thread::SetStackBase((void*)NULL); \ + } \ + + #elif defined(EA_COMPILER_ARM) // ARM compiler + + #define EASetStackBase() \ + ::EA::Thread::SetStackBase((void*)__current_sp()) + + #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) // This covers EA_PLATFORM_UNIX, EA_PLATFORM_OSX + + #define EASetStackBase() \ + ::EA::Thread::SetStackBase((void*)__builtin_frame_address(0)); + + #else + // This implementation uses SetStackBase(NULL), which internally retrieves the stack pointer. + #define EASetStackBase() \ + { \ + ::EA::Thread::SetStackBase((void*)NULL); \ + } \ + + #endif + + #if defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_SONY) + // GetPthreadStackInfo + // + // With some implementations of pthread, the stack base is returned by pthread as NULL if it's the main thread, + // or possibly if it's a thread you created but didn't call pthread_attr_setstack manually to provide your + // own stack. It's impossible for us to tell here whether will be such a NULL return value, so we just do what + // we can and the user nees to beware that a NULL return value means that the system doesn't provide the + // given information for the current thread. This function returns false and sets pBase and pLimit to NULL in + // the case that the thread base and limit weren't returned by the system or were returned as NULL. + + bool GetPthreadStackInfo(void** pBase, void** pLimit); + #endif + + } // namespace Thread + +} // namespace EA + + +#endif // Header include guard. diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_callstack_context.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_callstack_context.h new file mode 100644 index 00000000..117c8751 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_callstack_context.h @@ -0,0 +1,21 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + +#ifndef EATHREAD_EATHREAD_CALLSTACK_CONTEXT_H +#define EATHREAD_EATHREAD_CALLSTACK_CONTEXT_H + + +#include +#include +#include + +#endif // Header include guard. + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_condition.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_condition.h new file mode 100644 index 00000000..a076f5fa --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_condition.h @@ -0,0 +1,243 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements a condition variable in the style of Posix condition variables +// and Java and C# thread Monitors (Java objects and C# monitors have built-in +// locks and pthreads condition variables and EAThread::Conditions and Posix +// condition variables do not. A Condition is usually the appropriate thread +// synchronization mechanism for producer/consumer situations whereby one +// or more threads create data for one or more other threads to work on, +// such as is the case with a message queue. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_CONDITION_H +#define EATHREAD_EATHREAD_CONDITION_H + + +#include +#include +#include + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // Suppress warning about class 'EA::Thread::simple_list' needs to have + // dll-interface to be used by clients of class which have a templated member. + // + // These templates cannot be instantiated outside of the DLL. If you try, a + // link error will result. This compiler warning is intended to notify users + // of this. + EA_DISABLE_VC_WARNING(4251) +#endif + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +///////////////////////////////////////////////////////////////////////// +/// EAConditionData +/// +/// This is used internally by class Condition. +/// Todo: Consider moving this declaration into a platform-specific +/// header file. +/// +#if defined(EA_PLATFORM_SONY) + // Condition variables are built into Posix/Unix. + #include + #include + + struct EAConditionData + { + ScePthreadCond mCV; + EAConditionData(); + }; + +#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE + // Condition variables are built into Posix/Unix. + #include + + struct EAConditionData + { + pthread_cond_t mCV; + EAConditionData(); + }; + +#else // All other platforms + #include + #include + + struct EATHREADLIB_API EAConditionData + { + EA::Thread::AtomicInt32 mnWaitersBlocked; + int mnWaitersToUnblock; + int mnWaitersDone; + EA::Thread::Semaphore mSemaphoreBlockQueue; + EA::Thread::Semaphore mSemaphoreBlockLock; + EA::Thread::Mutex mUnblockLock; + + EAConditionData(); + + private: + // Prevent default generation of these functions by declaring but not defining them. + EAConditionData(const EAConditionData& rhs); // copy constructor + EAConditionData& operator=(const EAConditionData& rhs); // assignment operator + }; + +#endif + + + +namespace EA +{ + namespace Thread + { +#if defined(EA_PLATFORM_SONY) + static const int CONDITION_VARIABLE_NAME_LENGTH_MAX = 31; +#else + static const int CONDITION_VARIABLE_NAME_LENGTH_MAX = 15; +#endif + /// ConditionParameters + /// Specifies condition variable settings. + struct EATHREADLIB_API ConditionParameters + { + bool mbIntraProcess; /// True if the Condition is intra-process, else inter-process. + char mName[CONDITION_VARIABLE_NAME_LENGTH_MAX + 1]; /// Condition name, applicable only to platforms that recognize named synchronization objects. + + ConditionParameters(bool bIntraProcess = true, const char* pName = NULL); + }; + + + /// Condition + /// Implements a condition variable thread synchronization primitive. A condition variable is usually the + /// appropriate thread synchronization mechanism for producer/consumer situations whereby one or more + /// threads create data for one or more other threads to work on, such as is the case with a message queue. + /// + /// To use a condition variable to wait for resource, you Lock the Mutex for that resource, then (in a loop) + /// check and Wait on a condition variable that you associate with the mutex. Upon calling Wait, + /// the Lock will be released so that other threads can adjust the resource. Upon return from Wait, + /// the Mutex is re-locked for the caller. To use a Condition to signal a change in something, you simply + /// call the Signal function. In the case of Signal(false), one blocking waiter will be released, + /// whereas with Signal(true), all blocking waiters will be released. Upon release of single or multiple + /// waiting threads, the Lock is contested for by all of them, so in the case or more than one waiter, + /// only one will immediately come away with ownership of the lock. + class EATHREADLIB_API Condition + { + public: + enum Result + { + kResultOK = 0, + kResultError = -1, + kResultTimeout = -2 + }; + + /// Condition + /// For immediate default initialization, use no args. + /// For custom immediate initialization, supply a first argument. + /// For deferred initialization, use Condition(NULL, false) then later call Init. + /// For deferred initialization of an array of objects, create an empty + /// subclass whose default constructor chains back to Condition(NULL, false). + Condition(const ConditionParameters* pConditionParameters = NULL, bool bDefaultParameters = true); + + /// ~Condition + /// Destroys the Condition object. If any threads that are blocking while waiting on + /// while the Condition is destroyed, the resulting behaviour is undefined. + ~Condition(); + + /// Init + /// Initializes the Condition. + bool Init(const ConditionParameters* pConditionParameters); + + /// Wait + /// Waits for the Condition with timeout. You must have a Mutex + /// (that you conceptually associate with the resource) locked before + /// calling this function or else the resulting behaviour is undefined. + /// Within a while loop, check the resource state and call Wait if the + /// necessary condition is not met. + /// + /// The call to Wait associates the Condition with your mutex, so it can + /// then unlock the mutex/resource (allows another thread to fill the resource). + /// + /// Upon non-error return of Wait, the mutex will be re-locked by the calling + /// thread, even if the result is a timeout. Upon returning from wait, before + /// doing any processing as a result of a Signal, your loop should always re-check + /// the resource state. The Posix Wait specification explicitly notes + /// that uncommon 'spurious wakeups' are possible and so should be tested + /// for. It impossible to test for a spurious wakeup from within this Wait + /// function, as this function can't know the resource state that caused the + /// Signal to occur. + /// + /// It should be noted that upon a kResultOK return from Wait, the user should + /// not assume that what the user was waiting on is still available. The signaling + /// of a Condition should be considered merely a hint to the waiter that the user + /// can probably proceed. Also, the user should usually call Wait only if the + /// user has nothing to wait for; the user should check for this before calling Wait. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + Result Wait(Mutex* pMutex, const ThreadTime& timeoutAbsolute = kTimeoutNone); + + /// Signal + /// Releases one or all waiters, depending on the input 'bBroadcast' argument. + /// The waiters will then contest for the Lock. + bool Signal(bool bBroadcast = false); + + /// GetPlatformData + /// Returns the platform-specific data handle for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + void* GetPlatformData() + { return &mConditionData; } + + protected: + EAConditionData mConditionData; + + private: + // Objects of this class are not copyable. + Condition(const Condition&){} + Condition& operator=(const Condition&){ return *this; } + }; + + + /// ConditionFactory + /// + /// Implements a factory-based creation and destruction mechanism for class Condition. + /// A primary use of this would be to allow the Condition implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API ConditionFactory + { + public: + static Condition* CreateCondition(); // Internally implemented as: return new Condition; + static void DestroyCondition(Condition* pCondition); // Internally implemented as: delete pCondition; + + static size_t GetConditionSize(); // Internally implemented as: return sizeof(Condition); + static Condition* ConstructCondition(void* pMemory); // Internally implemented as: return new(pMemory) Condition; + static void DestructCondition(Condition* pCondition); // Internally implemented as: pCondition->~Condition(); + }; + + + } // namespace Thread + +} // namespace EA + + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // re-enable warning 4251 (it's a level-1 warning and should not be suppressed globally) + EA_RESTORE_VC_WARNING() +#endif + + + +#endif // EATHREAD_EATHREAD_CONDITION_H diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_futex.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_futex.h new file mode 100644 index 00000000..7ef90d05 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_futex.h @@ -0,0 +1,797 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements a fast, user-space mutex. Also known as a lightweight mutex. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_FUTEX_H +#define EATHREAD_EATHREAD_FUTEX_H + +#include +#include +#include +#include +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_MANUAL_FUTEX_ENABLED +// +// Defined as 0 or 1. +// If enabled then Futex is implemented with atomics and semaphores instead of +// via a direct system-supported lightweight mutex implementation. +// +#ifndef EATHREAD_MANUAL_FUTEX_ENABLED + #if defined(EA_PLATFORM_MICROSOFT) // VC++ has CriticalSection, which is a futex. + #define EATHREAD_MANUAL_FUTEX_ENABLED 0 // Currently 0 because that's best. Can be set to 1. + #elif defined(EA_PLATFORM_SONY) + #define EATHREAD_MANUAL_FUTEX_ENABLED 0 // Allows us to have a spin count. + #else + #define EATHREAD_MANUAL_FUTEX_ENABLED 1 // Set to 1 until we can resolve any dependencies such as PPMalloc. + #endif +#endif +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_FUTEX_SPIN_COUNT +// +#ifndef EATHREAD_FUTEX_SPIN_COUNT + #define EATHREAD_FUTEX_SPIN_COUNT 256 +#endif +/////////////////////////////////////////////////////////////////////////////// + + + +///////////////////////////////////////////////////////////////////////// +/// Futex data +/// +/// This is used internally by class Futex. +/// Note that we don't use an EAThread semaphore, as the direct semaphore +/// we use here is more direct and avoid inefficiencies that result from +/// the possibility of EAThread semaphores being optimized for being +/// standalone. +/// +#if !EA_THREADS_AVAILABLE + #define EA_THREAD_NONTHREADED_FUTEX 1 + + #if EATHREAD_MANUAL_FUTEX_ENABLED + struct EAFutexSemaphore + { + int mnCount; + }; + #endif + +#elif EA_USE_CPP11_CONCURRENCY + EA_DISABLE_VC_WARNING(4265 4365 4836 4571 4625 4626 4628 4193 4127 4548) + #include + EA_RESTORE_VC_WARNING() + +#elif defined(EA_PLATFORM_APPLE) + #if EATHREAD_MANUAL_FUTEX_ENABLED + #include + typedef EA::Thread::Semaphore EAFutexSemaphore; + #endif + +#elif defined(EA_PLATFORM_SONY) + #if EATHREAD_MANUAL_FUTEX_ENABLED + #include + #include + + typedef SceKernelSema EAFutexSemaphore; + #endif + +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #if EATHREAD_MANUAL_FUTEX_ENABLED + #include + typedef sem_t EAFutexSemaphore; + #endif + +#elif defined(EA_PLATFORM_MICROSOFT) + + // We avoid #including heavy system headers, as this file is a common header itself. + + extern "C" + { + #if defined(EA_COMPILER_GNUC) + // Mingw declares these slightly differently. + struct _CRITICAL_SECTION; + __declspec(dllimport) int __stdcall InitializeCriticalSectionAndSpinCount(_CRITICAL_SECTION* pCriticalSection, unsigned long dwSpinCount); + __declspec(dllimport) void __stdcall InitializeCriticalSection(_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) void __stdcall DeleteCriticalSection(_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) void __stdcall EnterCriticalSection(_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) void __stdcall LeaveCriticalSection(_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) int __stdcall TryEnterCriticalSection(_CRITICAL_SECTION* pCriticalSection); + #else + #if !defined _Must_inspect_result_ + #define _Must_inspect_result_ + #endif + + struct _RTL_CRITICAL_SECTION; + __declspec(dllimport) _Must_inspect_result_ int __stdcall InitializeCriticalSectionAndSpinCount(_Out_ _RTL_CRITICAL_SECTION* pCriticalSection, _In_ unsigned long dwSpinCount); + __declspec(dllimport) void __stdcall InitializeCriticalSection(_Out_ _RTL_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) void __stdcall DeleteCriticalSection(_Inout_ _RTL_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) void __stdcall EnterCriticalSection(_Inout_ _RTL_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) void __stdcall LeaveCriticalSection(_Inout_ _RTL_CRITICAL_SECTION* pCriticalSection); + __declspec(dllimport) int __stdcall TryEnterCriticalSection(_Inout_ _RTL_CRITICAL_SECTION* pCriticalSection); + #endif + + __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(); + } + + #if EATHREAD_MANUAL_FUTEX_ENABLED + typedef void* EAFutexSemaphore; // void* instead of HANDLE to avoid #including windows headers. + #endif + +#else + #define EA_THREAD_NONTHREADED_FUTEX 1 + + #if EATHREAD_MANUAL_FUTEX_ENABLED + struct EAFutexSemaphore + { + int mnCount; + }; + #endif +#endif +///////////////////////////////////////////////////////////////////////// + + + + +namespace EA +{ + namespace Thread + { + #if defined(EA_COMPILER_MSVC) && EA_PLATFORM_PTR_SIZE == 8 + static const int FUTEX_PLATFORM_DATA_SIZE = 40; // CRITICAL_SECTION is 40 bytes on Win64. + #elif defined(EA_COMPILER_MSVC) && EA_PLATFORM_PTR_SIZE == 4 + static const int FUTEX_PLATFORM_DATA_SIZE = 32; // CRITICAL_SECTION is 24 bytes on Win32 and 28 bytes on XBox 360. + #endif + + + /// class Futex + /// + /// A Futex is a fast user-space mutex. It works by attempting to use + /// atomic integer updates for the common case whereby the mutex is + /// not already locked. If the mutex is already locked then the futex + /// drops down to waiting on a system-level semaphore. The result is + /// that uncontested locking operations can be significantly faster + /// than contested locks. Contested locks are slightly slower than in + /// the case of a formal mutex, but usually not by much. + /// + /// The Futex acts the same as a conventional mutex with respect to + /// memory synchronization. Specifically: + /// - A Lock or successful TryLock implies a read barrier (i.e. acquire). + /// - A second lock by the same thread implies no barrier. + /// - A failed TryLock implies no barrier. + /// - A final unlock by a thread implies a write barrier (i.e. release). + /// - A non-final unlock by a thread implies no barrier. + /// + /// Futex limitations relative to Mutexes: + /// - Futexes cannot be inter-process. + /// - Futexes cannot be named. + /// - Futexes cannot participate in condition variables. A special + /// condition variable could be made that works with them, though. + /// - Futex locks don't have timeouts. This could probably be + /// added with some work, though users generally shouldn't need timeouts. + /// + class EATHREADLIB_API Futex + { + public: + enum Result + { + kResultTimeout = -2 + }; + + /// Futex + /// + /// Creates a Futex. There are no creation options. + /// + Futex(); + + /// ~Futex + /// + /// Destroys an existing futex. The futex must not be locked by any thread + /// upon this call, otherwise the resulting behaviour is undefined. + /// + ~Futex(); + + /// TryLock + /// + /// Tries to lock the futex; returns true if possible. + /// This function always returns immediately. It will return false if + /// the futex is locked by another thread, and it will return true + /// if the futex is not locked or is already locked by the current thread. + /// + bool TryLock(); + + /// Lock + /// + /// Locks the futex; returns the new lock count. + /// If the futex is locked by another thread, this call will block until + /// the futex is unlocked. If the futex is not locked or is locked by the + /// current thread, this call will return immediately. + /// + void Lock(); + + /// Lock + /// + /// Tries to lock the futex until the given time. + /// If the futex is locked by another thread, this call will block until + /// the futex is unlocked or the given time has passed. If the futex is not locked + /// or is locked by the current thread, this call will return immediately. + /// + /// Return value: + /// kResultTimeout Timeout + /// > 0 The new lock count. + int Lock(const ThreadTime& timeoutAbsolute); + + /// Unlock + /// + /// Unlocks the futex. The futex must already be locked at least once by + /// the calling thread. Otherwise the behaviour is not defined. + /// + void Unlock(); + + /// GetLockCount + /// + /// Returns the number of locks on the futex. The return value from this + /// function is only reliable if the calling thread already has one lock on + /// the futex. Otherwise the returned value may not represent actual value + /// at any point in time, as other threads lock or unlock the futex soon after the call. + /// + int GetLockCount() const; + + /// HasLock + /// Returns true if the current thread has the futex locked. + bool HasLock() const; + + /// SetSpinCount + /// Specifies how many times we spin while waiting to acquire the lock. + void SetSpinCount(Uint spinCount); + + protected: + #if EATHREAD_MANUAL_FUTEX_ENABLED + void CreateFSemaphore(); + void DestroyFSemaphore(); + void SignalFSemaphore(); + void WaitFSemaphore(); + bool WaitFSemaphore(const ThreadTime& timeoutAbsolute); + void OnLockAcquired(ThreadUniqueId threadUniqueId); + #endif + + private: + // Objects of this class are not copyable. + Futex(const Futex&){} + Futex& operator=(const Futex&){ return *this; } + + protected: + #if EATHREAD_MANUAL_FUTEX_ENABLED + AtomicUWord mUseCount; /// Not the same thing as lock count, as waiters increment this value. + uint16_t mRecursionCount; /// The number of times the lock-owning thread has the mutex. This is currently uint16_t for backward compatibility with PPMalloc. + uint16_t mSpinCount; /// The number of times we spin while waiting for the lock. To do: Change these to be uint32_t once PPMalloc is no longer dependent on this. + ThreadUniqueId mThreadUniqueId; /// Unique id for owning thread; not necessarily same as type ThreadId. + EAFutexSemaphore mSemaphore; /// OS-level semaphore that waiters wait on when lock attempts failed. + #else + + #if EA_USE_CPP11_CONCURRENCY + std::recursive_timed_mutex mMutex; + int mnLockCount; + std::thread::id mLockingThread; + #elif defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_MICROSOFT) // In the case of Microsoft platforms, we just use CRITICAL_SECTION, as it is essentially a futex. + // We use raw structure math because otherwise we'd expose the user to system headers, + // which breaks code and bloats builds. We validate our math in eathread_futex.cpp. + #if EA_PLATFORM_PTR_SIZE == 8 + uint64_t mCRITICAL_SECTION[FUTEX_PLATFORM_DATA_SIZE / sizeof(uint64_t)]; + #else + uint64_t mCRITICAL_SECTION[FUTEX_PLATFORM_DATA_SIZE / sizeof(uint64_t)]; + #endif + #elif defined(EA_PLATFORM_SONY) + EA::Thread::Mutex mMutex; + Uint mSpinCount; + #else + #define EAT_FUTEX_USE_MUTEX 1 + EA::Thread::Mutex mMutex; + #endif + #endif + }; + + + + /// FutexFactory + /// + /// Implements a factory-based creation and destruction mechanism for class Futex. + /// A primary use of this would be to allow the Futex implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + /// + class EATHREADLIB_API FutexFactory + { + public: + static Futex* CreateFutex(); // Internally implemented as: return new Futex; + static void DestroyFutex(Futex* pFutex); // Internally implemented as: delete pFutex; + + static size_t GetFutexSize(); // Internally implemented as: return sizeof(Futex); + static Futex* ConstructFutex(void* pMemory); // Internally implemented as: return new(pMemory) Futex; + static void DestructFutex(Futex* pFutex); // Internally implemented as: pFutex->~Futex(); + }; + + + + /// class AutoFutex + /// An AutoFutex locks the Futex in its constructor and + /// unlocks the Futex in its destructor (when it goes out of scope). + class EATHREADLIB_API AutoFutex + { + public: + AutoFutex(Futex& futex); + ~AutoFutex(); + + protected: + Futex& mFutex; + + // Prevent copying by default, as copying is dangerous. + AutoFutex(const AutoFutex&); + const AutoFutex& operator=(const AutoFutex&); + }; + + + } // namespace Thread + +} // namespace EA + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// Inlines +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// EAFutexReadBarrier +// +// For futexes, which are intended to be used only in user-space and without +// talking to IO devices, DMA memory, or uncached memory, we directly use +// memory barriers. + #define EAFutexReadBarrier EAReadBarrier + #define EAFutexWriteBarrier EAWriteBarrier + #define EAFutexReadWriteBarrier EAReadWriteBarrier +/////////////////////////////////////////////////////////////////////////////// + + + +namespace EA +{ + namespace Thread + { + #if EATHREAD_MANUAL_FUTEX_ENABLED + + inline Futex::Futex() + : mUseCount(0), + mRecursionCount(0), + mSpinCount(EATHREAD_FUTEX_SPIN_COUNT), + mThreadUniqueId(kThreadUniqueIdInvalid), + mSemaphore() + { + CreateFSemaphore(); + } + + + inline Futex::~Futex() + { + EAT_ASSERT(mUseCount == 0); + + DestroyFSemaphore(); + } + + + inline void Futex::OnLockAcquired(ThreadUniqueId threadUniqueId) + { + EAFutexReadBarrier(); + mThreadUniqueId = threadUniqueId; + mRecursionCount = 1; + } + + + inline bool Futex::TryLock() + { + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + + if(mUseCount.SetValueConditional(1, 0)) // If we could acquire the lock... (set it to 1 if it's 0) + { + OnLockAcquired(threadUniqueId); + return true; + } + + // This only happens in the case of recursion on the same thread + // This is threadsafe because the only case where this equality passes + // is when this value was set on this thread anyway. + if(EATHREAD_LIKELY(mThreadUniqueId == threadUniqueId)) // If it turns out that we already have the lock... + { + ++mUseCount; + ++mRecursionCount; + return true; + } + + return false; + } + + + inline void Futex::Lock() + { + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + + if(mSpinCount) // If we have spinning enabled (usually true)... + { + if(mUseCount.SetValueConditional(1, 0)) // If we could acquire the lock... (set it to 1 if it's 0) + { + OnLockAcquired(threadUniqueId); + return; + } + + if(mThreadUniqueId != threadUniqueId) // Don't spin if we already have the lock. + { + for(Uint count = mSpinCount; count > 0; count--) // Implement a spin lock for a number of tries. + { + // We use GetValueRaw calls below instead of atomics because we don't want atomic behavior. + if(mUseCount.GetValueRaw() > 1) // If there are multiple waiters, don't bother spinning any more, as they are already spinning themselves. + break; + + if(mUseCount.GetValueRaw() == 0) // If it looks like the lock is now free, try to acquire it. + { + if(mUseCount.SetValueConditional(1, 0)) // If we could acquire the lock... (set it to 1 if it's 0) + { + OnLockAcquired(threadUniqueId); + return; + } + } + + EAProcessorPause(); + } + } + } + + if(++mUseCount > 1) // If we could not get the lock (previous value of mUseCount was >= 1 and not 0) or we already had the lock... + { + if(mThreadUniqueId == threadUniqueId) // If we already have the lock... + { + mRecursionCount++; + return; + } + WaitFSemaphore(); + } + // Else the increment was from 0 to 1, and we own the lock. + OnLockAcquired(threadUniqueId); + } + + + + + inline int Futex::Lock(const ThreadTime& timeoutAbsolute) + { + if(timeoutAbsolute == kTimeoutNone) + { + Lock(); + return (int)mRecursionCount; + } + else if(timeoutAbsolute == kTimeoutImmediate) + { + if(TryLock()) + return (int)mRecursionCount; + else + return kResultTimeout; + } + else + { + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + + if(++mUseCount > 1) // If we could not get the lock (previous value of mUseCount was >= 1 and not 0) or we already had the lock... + { + if(mThreadUniqueId == threadUniqueId) // If we already have the lock... + return (int)++mRecursionCount; + + if(!WaitFSemaphore(timeoutAbsolute)) + { + --mUseCount; + return kResultTimeout; + } + } + // Else the increment was from 0 to 1, and we own the lock. + OnLockAcquired(threadUniqueId); + return 1; // Return mRecursionCount. + } + } + + + inline void Futex::Unlock() + { + #if EAT_ASSERT_ENABLED + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + EAT_ASSERT(mThreadUniqueId == threadUniqueId); + EAT_ASSERT((mRecursionCount > 0) && (mUseCount > 0)); + #endif + + if(EATHREAD_LIKELY(--mRecursionCount == 0)) + { + mThreadUniqueId = kThreadUniqueIdInvalid; + + // after the decrement below we will no longer own the lock + EAFutexWriteBarrier(); + if(EATHREAD_UNLIKELY(--mUseCount > 0)) + SignalFSemaphore(); + } + else + { + // this thread still owns the lock, was recursive + --mUseCount; + } + } + + + inline int Futex::GetLockCount() const + { + // No atomic operation or memory barrier required, as this function only + // has validity if it is being called from the lock-owning thread. However, + // we don't at this time choose to assert that mThreadUniqueId == GetThreadId(). + return (int)mRecursionCount; + } + + + inline bool Futex::HasLock() const + { + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + + return (mThreadUniqueId == threadUniqueId); + } + + + inline void Futex::SetSpinCount(Uint spinCount) + { + mSpinCount = spinCount; + } + + #else // #if EATHREAD_MANUAL_FUTEX_ENABLED + + #if EA_USE_CPP11_CONCURRENCY + + inline Futex::Futex() : mnLockCount(0) {} + + inline Futex::~Futex() { EAT_ASSERT(!GetLockCount()); } + + inline bool Futex::TryLock() + { + if (mMutex.try_lock()) + { + EAT_ASSERT(mnLockCount >= 0); + EAT_ASSERT(mnLockCount == 0 || mLockingThread == std::this_thread::get_id()); + ++mnLockCount; + mLockingThread = std::this_thread::get_id(); + return true; + } + + return false; + } + + inline void Futex::Lock() { mMutex.lock(); mLockingThread = std::this_thread::get_id(); ++mnLockCount; } + + inline int Futex::Lock(const ThreadTime& timeoutAbsolute) + { + if (timeoutAbsolute == kTimeoutNone) + { + if (!mMutex.try_lock()) + { + return kResultTimeout; + } + } + else + { + std::chrono::milliseconds timeoutAbsoluteMs(timeoutAbsolute); + std::chrono::time_point timeout_time(timeoutAbsoluteMs); + if (!mMutex.try_lock_until(timeout_time)) + { + return kResultTimeout; + } + } + + EAT_ASSERT(mnLockCount >= 0); + EAT_ASSERT(mnLockCount == 0 || mLockingThread == std::this_thread::get_id()); + mLockingThread = std::this_thread::get_id(); + return ++mnLockCount; // This is safe to do because we have the lock. + } + + inline void Futex::Unlock() + { + EAT_ASSERT(HasLock()); + --mnLockCount; + if (mnLockCount == 0) + mLockingThread = std::thread::id(); + mMutex.unlock(); + } + + inline int Futex::GetLockCount() const { return mnLockCount; } + + inline bool Futex::HasLock() const + { + if ((mnLockCount > 0) && (std::this_thread::get_id() == mLockingThread)) + return true; + return false; + } + + inline void Futex::SetSpinCount(Uint) + { + // Not supported + } + + #elif defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_MICROSOFT) // Win32, Win64, etc. + + inline Futex::Futex() + { + // We use InitializeCriticalSectionAndSpinCount, as that has resulted in improved performance in practice on multiprocessors systems. + int rv = InitializeCriticalSectionAndSpinCount((_RTL_CRITICAL_SECTION*)mCRITICAL_SECTION, EATHREAD_FUTEX_SPIN_COUNT); + EAT_ASSERT(rv != 0); + EA_UNUSED(rv); + } + + inline Futex::~Futex() + { + EAT_ASSERT(!GetLockCount()); + DeleteCriticalSection((_RTL_CRITICAL_SECTION*)mCRITICAL_SECTION); + } + + inline bool Futex::TryLock() + { + return TryEnterCriticalSection((_RTL_CRITICAL_SECTION*)mCRITICAL_SECTION) != 0; + } + + inline void Futex::Lock() + { + EnterCriticalSection((_RTL_CRITICAL_SECTION*)mCRITICAL_SECTION); + } + + inline int Futex::Lock(const ThreadTime& timeoutAbsolute) + { + if(timeoutAbsolute == kTimeoutNone) + { + Lock(); + return GetLockCount(); + } + else if(timeoutAbsolute == kTimeoutImmediate) + { + if(TryLock()) + return GetLockCount(); + else + return kResultTimeout; + } + else + { + while(!TryLock()) + { + if(GetThreadTime() >= timeoutAbsolute) + return kResultTimeout; + ThreadSleep(1); + } + return GetLockCount(); + } + } + + inline void Futex::Unlock() + { + EAT_ASSERT(HasLock()); + LeaveCriticalSection((_RTL_CRITICAL_SECTION*)mCRITICAL_SECTION); + } + + inline int Futex::GetLockCount() const + { + // Return the RecursionCount member of RTL_CRITICAL_SECTION. + + // We use raw structure math because otherwise we'd expose the user to system headers, + // which breaks code and bloats builds. We validate our math in eathread_futex.cpp. + #if EA_PLATFORM_PTR_SIZE == 8 + return *((int*)mCRITICAL_SECTION + 3); + #else + return *((int*)mCRITICAL_SECTION + 2); + #endif + } + + inline bool Futex::HasLock() const + { + // Check the OwningThread member of RTL_CRITICAL_SECTION. + + // We use raw structure math because otherwise we'd expose the user to system headers, + // which breaks code and bloats builds. We validate our math in eathread_futex.cpp. + #if EA_PLATFORM_PTR_SIZE == 8 + return (*((uint32_t*)mCRITICAL_SECTION + 4) == (uintptr_t)GetCurrentThreadId()); + #else + return (*((uint32_t*)mCRITICAL_SECTION + 3) == (uintptr_t)GetCurrentThreadId()); + #endif + } + + inline void Futex::SetSpinCount(Uint) + { + // Not supported + } + + #elif defined(EAT_FUTEX_USE_MUTEX) + + inline Futex::Futex() + { } + + inline Futex::~Futex() + { } + + inline bool Futex::TryLock() + { return mMutex.Lock(EA::Thread::kTimeoutImmediate) > 0; } + + inline void Futex::Lock() + { mMutex.Lock(); } + + inline int Futex::Lock(const ThreadTime& timeoutAbsolute) + { return mMutex.Lock(timeoutAbsolute); } + + inline void Futex::Unlock() + { mMutex.Unlock(); } + + inline int Futex::GetLockCount() const + { return mMutex.GetLockCount(); } + + inline bool Futex::HasLock() const + { return mMutex.HasLock(); } + + inline void Futex::SetSpinCount(Uint) + { } + + #endif // EA_COMPILER_MSVC + + #endif // EATHREAD_MANUAL_FUTEX_ENABLED + + + + inline AutoFutex::AutoFutex(Futex& futex) + : mFutex(futex) + { + mFutex.Lock(); + } + + inline AutoFutex::~AutoFutex() + { + mFutex.Unlock(); + } + + } // namespace Thread + +} // namespace EA + + + +#endif // EATHREAD_EATHREAD_FUTEX_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_list.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_list.h new file mode 100644 index 00000000..03dab79a --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_list.h @@ -0,0 +1,323 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// This is a small templated list implementation which suffices for our +// purposes but is not optimal. It is present in order to avoid dependencies +// on external libraries. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_LIST_H +#define EATHREAD_EATHREAD_LIST_H + + +#include +#include +#include // size_t, etc. +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +namespace EA +{ + namespace Thread + { + + namespace details + { + /// Default allocator implementation used by the simple_list class + template + struct ListDefaultAllocatorImpl + { + template + struct rebind { typedef ListDefaultAllocatorImpl other; }; + + T* construct() + { + Allocator* pAllocator = GetAllocator(); + + if(pAllocator) + return new(pAllocator->Alloc(sizeof(T))) T; + else + return new T; + } + + void destroy(T* obj) + { + Allocator* pAllocator = GetAllocator(); + + if(pAllocator) + { + obj->~T(); + pAllocator->Free(obj); + } + else + delete obj; + } + }; + } + + + /// Simple version of an STL bidirectional list. + /// Implemented to avoid dependency on container implementations. + /// + /// This implementation has some non-stl standard methods like find. + /// + template > + class simple_list + { + simple_list(const simple_list&); + simple_list& operator=(const simple_list&); + + protected: + struct list_node + { + T mValue; + list_node* mpPrev; + list_node* mpNext; + }; + + typedef list_node node_t; + typedef typename Allocator::template rebind::other allocator_t; + + allocator_t mAllocator; + node_t* mpNodeHead; + node_t* mpNodeTail; + size_t mnSize; + + public: + typedef T value_type; //< list value type + typedef const T const_value_type; //< constant list value type + typedef const T& const_value_ref_type; //< constant reference list value type + + struct const_iterator; + struct iterator; + friend struct const_iterator; + friend struct iterator; + + + struct const_iterator + { + friend class simple_list; + + const_iterator() + : mpNode(NULL) + { } + + const_iterator(const const_iterator& rhs) + : mpNode(rhs.mpNode) + { } + + const_iterator& operator=(const const_iterator& rhs) + { + mpNode = rhs.mpNode; + return *this; + } + + const T& operator*() const + { return mpNode->mValue; } + + const T* operator->() const + { return &**this; } + + bool operator==(const const_iterator& rhs) const + { return rhs.mpNode == mpNode; } + + bool operator!=(const const_iterator& rhs) const + { return rhs.mpNode != mpNode; } + + const_iterator& operator++() + { + mpNode = mpNode->mpNext; + return *this; + } + + protected: + const node_t* mpNode; + + protected: + const_iterator(node_t* pNode) + : mpNode(pNode) + { } + + const_iterator& operator=(const node_t* pNode) + { + mpNode = pNode; + return *this; + } + }; // const_iterator + + + + struct iterator : public const_iterator + { + friend class simple_list; + + iterator() + : const_iterator(){ } + + iterator(const const_iterator& rhs) + : const_iterator(rhs) + { } + + iterator& operator=(const const_iterator& rhs) + { + *static_cast(this)= rhs; + return *this; + } + + T& operator*() const + { return const_cast(**static_cast(this)); } + + T& operator->() const + { return const_cast(&**static_cast(this)); } + + iterator& operator++() + { + ++(*static_cast(this)); + return *this; + } + + protected: + iterator(node_t* pNode) + : const_iterator(pNode) + { } + + iterator& operator=(node_t* pNode) + { + const_cast(*this) = pNode; + return *this; + } + }; // iterator + + + + simple_list() + : mnSize(0) + { + mpNodeHead = mAllocator.construct(); + mpNodeTail = mAllocator.construct(); + mpNodeHead->mpNext = mpNodeTail; + mpNodeHead->mpPrev = mpNodeTail; + mpNodeTail->mpNext = mpNodeHead; + mpNodeTail->mpPrev = mpNodeHead; + } + + ~simple_list() + { + clear(); + mAllocator.destroy(mpNodeHead); + mAllocator.destroy(mpNodeTail); + } + + bool empty() const + { return mpNodeHead->mpNext == mpNodeTail; } + + void push_back(const T& value) + { + node_t* const pNode = mAllocator.construct(); + pNode->mValue = value; + pNode->mpPrev = mpNodeTail->mpPrev; + pNode->mpNext = mpNodeTail; + pNode->mpPrev->mpNext = pNode; + mpNodeTail->mpPrev = pNode; + ++mnSize; + } + + void push_front(const T& value) + { + node_t* const pNode = mAllocator.construct(); + pNode->mValue = value; + pNode->mpPrev = mpNodeHead; + pNode->mpNext = mpNodeHead->mpNext; + mpNodeHead->mpNext = pNode; + ++mnSize; + } + + void pop_front() + { + if(!empty()) + { + node_t* const pNode = mpNodeHead->mpNext; + mpNodeHead->mpNext = pNode->mpNext; + pNode->mpNext->mpPrev = mpNodeHead; + mAllocator.destroy(pNode); + --mnSize; + } + } + + size_t size() const + { return mnSize; } + + iterator erase(iterator& iter) + { + if(!empty()) + { + node_t* const pNext = iter.mpNode->mpNext; + iter.mpNode->mpNext->mpPrev = iter.mpNode->mpPrev; + iter.mpNode->mpPrev->mpNext = iter.mpNode->mpNext; + --mnSize; + mAllocator.destroy(const_cast(iter.mpNode)); + return pNext; + } + return end(); + } + + void clear() + { + if(!empty()) + { + node_t* pNode = mpNodeHead->mpNext; + + while(pNode != mpNodeTail) + { + node_t* const pNext = pNode->mpNext; + pNode->mpNext->mpPrev = pNode->mpPrev; + pNode->mpPrev->mpNext = pNext; + mAllocator.destroy(pNode); + pNode = pNext; + } + mnSize = 0; + } + } + + T& front() const + { return mpNodeHead->mpNext->mValue; } + + const const_iterator begin() const + { return mpNodeHead->mpNext; } + + const const_iterator end() const + { return mpNodeTail; } + + /// returns end()if not found + iterator find(const T& element) + { + iterator iter = begin(); + while((iter != end()) && !(element == *iter)) + ++iter; + return iter; + } + + }; // simple_list + + } // namespace Thread + +} // namespace EA + + +#endif // EATHREAD_EATHREAD_LIST_H + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_mutex.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_mutex.h new file mode 100644 index 00000000..32f6526d --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_mutex.h @@ -0,0 +1,341 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements a lightweight mutex. +///////////////////////////////////////////////////////////////////////////// + +// TODO(rparolin): Consider adding support for static thread safety analysis. +// https://clang.llvm.org/docs/ThreadSafetyAnalysis.html + + +#ifndef EATHREAD_EATHREAD_MUTEX_H +#define EATHREAD_EATHREAD_MUTEX_H + +#if defined(EA_COMPILER_MSVC) +#include // #include math.h because VC++ has a header file but that requires math.h to be #included before some other headers, lest you get a warning. +#endif +#include +#include +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +///////////////////////////////////////////////////////////////////////// +/// EAMutexData +/// +/// This is used internally by class Mutex. +/// Todo: Consider moving this declaration into a platform-specific +/// header file. +/// +#if !EA_THREADS_AVAILABLE + #define EA_THREAD_NONTHREADED_MUTEX 1 + + struct EAMutexData + { + int mnLockCount; + + EAMutexData(); + }; + +#elif EA_USE_CPP11_CONCURRENCY + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + + #if defined EA_PLATFORM_MICROSOFT + #ifdef CreateMutex + #undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW. + #endif + #endif + + struct EAMutexData + { + std::recursive_timed_mutex mMutex; + int mnLockCount; + #if EAT_ASSERT_ENABLED + EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds. + #endif + + EAMutexData(); + + private: + EAMutexData(const EAMutexData&); + EAMutexData& operator=(const EAMutexData&); + }; + +#elif defined(EA_PLATFORM_SONY) + #include + #include + + struct EAMutexData + { + ScePthreadMutex mMutex; + int mnLockCount; + #if EAT_ASSERT_ENABLED + EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds. + #endif + + EAMutexData(); + void SimulateLock(bool bLock); + }; + +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + + #if defined(EA_PLATFORM_WINDOWS) + #ifdef CreateMutex + #undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW. + #endif + #endif + + struct EAMutexData + { + pthread_mutex_t mMutex; + int mnLockCount; + #if EAT_ASSERT_ENABLED + EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds. + #endif + + EAMutexData(); + void SimulateLock(bool bLock); + }; + +#elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + + #if defined(EA_PROCESSOR_X86_64) || defined(EA_PROCESSOR_ARM64) + static const int MUTEX_PLATFORM_DATA_SIZE = 40; // CRITICAL_SECTION is 40 bytes on Win64 & ARM64. + #else + static const int MUTEX_PLATFORM_DATA_SIZE = 32; // CRITICAL_SECTION is 24 bytes on Win32, 28 bytes on XBox 360. + #endif + + #ifdef CreateMutex + #undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW. + #endif + + struct EATHREADLIB_API EAMutexData + { + uint64_t mData[MUTEX_PLATFORM_DATA_SIZE / sizeof(uint64_t)]; // Holds either CRITICAL_SECTION or HANDLE if mbIntraProcess is true or false, respectively. + int mnLockCount; + bool mbIntraProcess; + #if EAT_ASSERT_ENABLED + EA::Thread::ThreadId mThreadId; // This value is only valid in debug builds. + EA::Thread::SysThreadId mSysThreadId; // This value is only valid in debug builds. + #endif + + EAMutexData(); + }; + +#else + #define EA_THREAD_NONTHREADED_MUTEX 1 + + struct EAMutexData + { + int mnLockCount; + + EAMutexData(); + }; + + + +#endif +///////////////////////////////////////////////////////////////////////// + + + + +namespace EA +{ + namespace Thread + { + /// MutexParameters + /// Specifies mutex settings. + struct EATHREADLIB_API MutexParameters + { + bool mbIntraProcess; /// True if the mutex is intra-process, else inter-process. + char mName[128]; /// Mutex name, applicable only to platforms that recognize named synchronization objects. + + MutexParameters(bool bIntraProcess = true, const char* pName = NULL); + }; + + + /// class Mutex + /// + /// Mutex are assumed to always be 'recursive', meaning that a given thread + /// can lock the mutex more than once. If you want a specifically non-recursive + /// mutex, you can use a semaphore with a lock count of 1. + class EATHREADLIB_API Mutex + { + public: + enum Result + { + kResultError = -1, + kResultTimeout = -2 + }; + + /// Mutex + /// For immediate default initialization, use no args. + /// For custom immediate initialization, supply a first argument. + /// For deferred initialization, use Mutex(NULL, false) then later call Init. + /// For deferred initialization of an array of objects, create an empty + /// subclass whose default constructor chains back to Mutex(NULL, false). + Mutex(const MutexParameters* pMutexParameters = NULL, bool bDefaultParameters = true); + + /// ~Mutex + /// Destroys an existing mutex. The mutex must not be locked by any thread, + /// otherwise the resulting behaviour is undefined. + ~Mutex(); + + /// Init + /// Initializes the mutex if not done so in the constructor. + /// This should only be called in the case that this class was constructed + /// with RWMutex(NULL, false). + bool Init(const MutexParameters* pMutexParameters); + + /// Lock + /// Locks the mutex, with a timeout specified. This function will + /// return immediately if the mutex is not locked or if the calling + /// thread already has it locked at least once. If the mutex is + /// locked by another thread, this function will block until the mutex + /// is unlocked by the owning thread or until the timeout time has + /// passed. This function may return before the specified timeout has passed + /// and so should not be implicitly used as a timer. Some platforms may + /// return immediately if the timeout is specified as anything but kTimeoutNone. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + /// Return value: + /// kResultError Error + /// kResultTimeout Timeout + /// > 0 The new lock count. + int Lock(const ThreadTime& timeoutAbsolute = EA::Thread::kTimeoutNone); + + /// Unlock + /// Unlocks the mutex. The mutex must already be locked at least once by + /// the calling thread. Otherwise the behaviour is not defined. + /// Return value is the lock count value immediately upon unlock. + int Unlock(); + + /// GetLockCount + /// Returns the number of locks on the mutex. The return value from this + /// function is only reliable if the calling thread already has one lock on + /// the critical section. Otherwise the value could be changing as other + /// threads lock or unlock the mutex soon after the call. + /// This function is useful in debugging and asserting and useful for backing + /// out of recursive locks under the case of exceptions and other abortive + /// situations. This function will not necessarily call memory synchronization + /// primitives (e.g. ReadBarrier) itself on systems that require SMP synchronization. + int GetLockCount() const; + + + /// HasLock + /// Returns true if the current thread has the mutex locked. + /// This function is reliable only in a debug build whereby + /// EAT_ASSERT_ENABLED is defined to 1. This function can thus + /// only be used in debugging situations whereby you want to + /// assert that you have a mutex locked or not. To make this + /// function work in a non-debug environment would necessitate + /// adding an undesirable amount of code and data. + bool HasLock() const; + + /// GetPlatformData + /// Returns the platform-specific data handle for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + void* GetPlatformData() + { return &mMutexData; } + + protected: + EAMutexData mMutexData; + + private: + // Objects of this class are not copyable. + Mutex(const Mutex&){} + Mutex& operator=(const Mutex&){ return *this; } + }; + + + + /// MutexFactory + /// + /// Implements a factory-based creation and destruction mechanism for class Mutex. + /// A primary use of this would be to allow the Mutex implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API MutexFactory + { + public: + static Mutex* CreateMutex(); // Internally implemented as: return new Mutex; + static void DestroyMutex(Mutex* pMutex); // Internally implemented as: delete pMutex; + + static size_t GetMutexSize(); // Internally implemented as: return sizeof(Mutex); + static Mutex* ConstructMutex(void* pMemory); // Internally implemented as: return new(pMemory) Mutex; + static void DestructMutex(Mutex* pMutex); // Internally implemented as: pMutex->~Mutex(); + }; + + + } // namespace Thread + +} // namespace EA + + + + + +namespace EA +{ + namespace Thread + { + /// class AutoMutex + /// An AutoMutex locks the Mutex in its constructor and + /// unlocks the Mutex in its destructor (when it goes out of scope). + class EATHREADLIB_API AutoMutex + { + public: + inline AutoMutex(Mutex& mutex) + : mMutex(mutex) + { mMutex.Lock(); } + + inline ~AutoMutex() + { mMutex.Unlock(); } + + protected: + Mutex& mMutex; + + // Prevent copying by default, as copying is dangerous. + AutoMutex(const AutoMutex&); + const AutoMutex& operator=(const AutoMutex&); + }; + + } // namespace Thread + +} // namespace EA + + + +#endif // EATHREAD_EATHREAD_MUTEX_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_pool.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_pool.h new file mode 100644 index 00000000..9cceadf0 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_pool.h @@ -0,0 +1,301 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements a classic thread pool. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_POOL_H +#define EATHREAD_EATHREAD_POOL_H + + +#ifndef EATHREAD_EATHREAD_THREAD_H + #include +#endif +#ifndef EATHREAD_EATHREAD_CONDITION_H + #include +#endif +#ifndef EATHREAD_EATHREAD_ATOMIC_H + #include +#endif +#ifndef EATHREAD_EATHREAD_LIST_H + #include +#endif +#include + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // Suppress warning about class 'EA::Thread::simple_list' needs to have + // dll-interface to be used by clients of class which have a templated member. + // + // These templates cannot be instantiated outside of the DLL. If you try, a + // link error will result. This compiler warning is intended to notify users + // of this. + EA_DISABLE_VC_WARNING(4251) +#endif + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +///////////////////////////////////////////////////////////////////////////// +// EA_THREAD_POOL_MAX_SIZE +// +// Defines the maximum number of threads the pool can have. +// Currently we have a limit of at most N threads in a pool, in order to +// simplify memory management issues. +// +#ifndef EA_THREAD_POOL_MAX_SIZE + #define EA_THREAD_POOL_MAX_SIZE 16 +#endif + + + +namespace EA +{ + namespace Thread + { + /// ThreadPoolParameters + /// Specifies how a thread pool is initialized + struct EATHREADLIB_API ThreadPoolParameters + { + unsigned mnMinCount; /// Default is kDefaultMinCount. + unsigned mnMaxCount; /// Default is kDefaultMaxCount. + unsigned mnInitialCount; /// Default is kDefaultInitialCount + ThreadTime mnIdleTimeoutMilliseconds; /// Default is kDefaultIdleTimeout. This is a relative time, not an absolute time. Can be a millisecond value or Thread::kTimeoutNone or Thread::kTimeoutImmediate. + unsigned mnProcessorMask; /// Default is 0xffffffff. Controls which processors we are allowed to create threads on. Default is all processors. + ThreadParameters mDefaultThreadParameters; /// Currently only the mnStackSize, mnPriority, and mpName fields from ThreadParameters are used. + + ThreadPoolParameters(); + + private: + // Prevent default generation of these functions by not defining them + ThreadPoolParameters(const ThreadPoolParameters& rhs); // copy constructor + ThreadPoolParameters& operator=(const ThreadPoolParameters& rhs); // assignment operator + }; + + + /// class ThreadPool + /// + /// Implements a conventional thread pool. Thread pools are useful for situations where + /// thread creation and destruction is common and the application speed would improve + /// by using pre-made threads that are ready to execute. + class EATHREADLIB_API ThreadPool + { + public: + enum Default + { + kDefaultMinCount = 0, + kDefaultMaxCount = 4, + kDefaultInitialCount = 0, + kDefaultIdleTimeout = 60000, // Milliseconds + kDefaultProcessorMask = 0xffffffff + }; + + enum Result + { + kResultOK = 0, + kResultError = -1, + kResultTimeout = -2, + kResultDeferred = -3 + }; + + enum JobWait + { + kJobWaitNone, /// Wait for no jobs to complete, including those currently running. + kJobWaitCurrent, /// Wait for currently proceeding jobs to complete but not those that haven't started. + kJobWaitAll /// Wait for all jobs to complete, including those that haven't yet begun. + }; + + /// ThreadPool + /// For immediate default initialization, use no args. + /// For custom immediate initialization, supply a first argument. + /// For deferred initialization, use ThreadPool(NULL, false) then later call Init. + /// For deferred initialization of an array of objects, create an empty + /// subclass whose default constructor chains back to ThreadPool(NULL, false). + ThreadPool(const ThreadPoolParameters* pThreadPoolParameters = NULL, bool bDefaultParameters = true); + + /// ~ThreadPool + /// Destroys the thread pool. Waits for any busy threads to complete. + ~ThreadPool(); + + /// Init + /// Initializes the thread pool with given characteristics. If the thread pool is + /// already initialized, this updates the settings. + bool Init(const ThreadPoolParameters* pThreadPoolParameters); + + /// Shutdown + /// Disables the thread pool, waits for busy threads to complete, destroys all threads. + /// + /// If bWaitForAllJobs is true, then Shutdown will wait until all jobs, including + /// jobs that haven't been started yet, to complete. Otherwise, only currently + /// proceeding jobs will be completed. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + bool Shutdown(JobWait jobWait = kJobWaitAll, const ThreadTime& timeoutAbsolute = kTimeoutNone); + + /// Begin + /// Starts a thread from the pool with the given parameters. + /// Returns kResultError or a job id of >= kResultOK. A return of kResultDeferred is + /// possible if the number of active threads is greater or equal to the max count. + /// If input ppThread is non-NULL and return value is >= kResultOK, the returned thread + /// will be the thread used for the job. Else the returned thread pointer will be NULL. + /// If input bEnabledDeferred is false but the max count of active theads has been + /// reached, a new thread is nevertheless created. + int Begin(IRunnable* pRunnable, void* pContext = NULL, Thread** ppThread = NULL, bool bEnableDeferred = false); + int Begin(RunnableFunction pFunction, void* pContext = NULL, Thread** ppThread = NULL, bool bEnableDeferred = false); + + /// WaitForJobCompletion + /// Waits for an individual job or for all jobs (job id of -1) to complete. + /// If a job id is given which doesn't correspond to any existing job, + /// the job is assumed to have been completed and the wait completes immediately. + /// If new jobs are added while the wait is occurring, this function will wait + /// for those jobs to complete as well. jobWait is valid only if nJob is -1. + /// Note that the timeout is specified in absolute time and not relative time. + /// Returns one of enum Result. + int WaitForJobCompletion(int nJob = -1, JobWait jobWait = kJobWaitAll, const ThreadTime& timeoutAbsolute = kTimeoutNone); + + /// Pause + /// Enables or disables the activation of threads from the pool. + /// When paused, calls to Begin will return kResultDeferred instead of kResultOK. + void Pause(bool bPause); + + /// Locks the thread pool thread list. + void Lock(); + void Unlock(); + + struct Job + { + int mnJobID; /// Unique job id. + IRunnable* mpRunnable; /// User-supplied IRunnable. This is an alternative to mpFunction. + RunnableFunction mpFunction; /// User-supplied function. This is an alternative to mpRunnable. + void* mpContext; /// User-supplied context. + + Job(); + }; + + struct ThreadInfo + { + volatile bool mbActive; /// True if the thread is currently busy working on a job. + volatile bool mbQuit; /// If set to true then this thread should quit at the next opportunity. + //bool mbPersistent; /// If true then this thread is never quit at runtime. False by default. + Thread* mpThread; /// The Thread itself. + ThreadPool* mpThreadPool; /// The ThreadPool that owns this thread. + Job mCurrentJob; /// The most recent job a thread is or was working on. + + ThreadInfo(); + }; + + /// AddThread + /// Adds a new thread with the given ThreadParameters. + /// The return value is not safe to use unless this function is called + /// and the result used within a Lock/Unlock pair. + /// It's the user's responsibility to supply ThreadParameters that are sane. + /// If bBeginThread is true, then the Thread is started via a call to + /// pThreadInfo->mpThread->Begin(ThreadFunction, pThreadInfo, &tp); + /// Otherwise the user is expected to manually start the thread. + ThreadInfo* AddThread(const ThreadParameters& tp, bool bBeginThread); + + // Gets the ThreadInfo for the nth Thread identified by index. + // You must call this function and use the info within a Lock/Unlock pair + // on the thread pool. + ThreadInfo* GetThreadInfo(int index); + + // Unless you call this function while the Pool is locked (via Lock), the return + // value may be out of date by the time you read it. + int GetThreadCount(); + + protected: + typedef EA::Thread::simple_list JobList; + typedef EA::Thread::simple_list ThreadInfoList; + + // Member functions + static intptr_t ThreadFunction(void* pContext); + ThreadInfo* CreateThreadInfo(); + void SetupThreadParameters(ThreadParameters& tp); + void AdjustThreadCount(unsigned nCount); + Result QueueJob(const Job& job, Thread** ppThread, bool bEnableDeferred); + void AddThread(ThreadInfo* pThreadInfo); + void RemoveThread(ThreadInfo* pThreadInfo); + void FixThreads(); + + // Member data + bool mbInitialized; // + uint32_t mnMinCount; // Min number of threads to have available. + uint32_t mnMaxCount; // Max number of threads to have available. + AtomicInt32 mnCurrentCount; // Current number of threads available. + AtomicInt32 mnActiveCount; // Current number of threads busy with jobs. + ThreadTime mnIdleTimeoutMilliseconds; // Timeout before quitting threads that have had no jobs. + uint32_t mnProcessorMask; // If mask is not 0xffffffff then we manually round-robin assign processors. + uint32_t mnProcessorCount; // The number of processors currently present. + uint32_t mnNextProcessor; // Used if we are manually round-robin assigning processors. + AtomicInt32 mnPauseCount; // A positive value means we pause working on jobs. + AtomicInt32 mnLastJobID; // + ThreadParameters mDefaultThreadParameters; // + Condition mThreadCondition; // Manages signalling mJobList. + Mutex mThreadMutex; // Guards manipulation of mThreadInfoList and mJobList. + ThreadInfoList mThreadInfoList; // List of threads in our pool. + JobList mJobList; // List of waiting jobs. + + private: + // Prevent default generation of these functions by not defining them + ThreadPool(const ThreadPool& rhs); // copy constructor + ThreadPool& operator=(const ThreadPool& rhs); // assignment operator + }; + + + + /// ThreadPoolFactory + /// + /// Implements a factory-based creation and destruction mechanism for class ThreadPool. + /// A primary use of this would be to allow the ThreadPool implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API ThreadPoolFactory + { + public: + static ThreadPool* CreateThreadPool(); // Internally implemented as: return new ThreadPool; + static void DestroyThreadPool(ThreadPool* pThreadPool); // Internally implemented as: delete pThreadPool; + + static size_t GetThreadPoolSize(); // Internally implemented as: return sizeof(ThreadPool); + static ThreadPool* ConstructThreadPool(void* pMemory); // Internally implemented as: return new(pMemory) ThreadPool; + static void DestructThreadPool(ThreadPool* pThreadPool); // Internally implemented as: pThreadPool->~ThreadPool(); + }; + + } // namespace Thread + +} // namespace EA + + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // re-enable warning 4251 (it's a level-1 warning and should not be suppressed globally) + EA_RESTORE_VC_WARNING() +#endif + + +#endif // EATHREAD_EATHREAD_POOL_H + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwmutex.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwmutex.h new file mode 100644 index 00000000..2e5bdf2a --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwmutex.h @@ -0,0 +1,221 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements a lightweight mutex with multiple reads but single writer. +// This allows for high performance systems whereby the consumers of data +// are more common than the producers of data. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_RWMUTEX_H +#define EATHREAD_EATHREAD_RWMUTEX_H + +#include +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +///////////////////////////////////////////////////////////////////////// +/// EARWMutexData +/// +/// This is used internally by class RWMutex. +/// Todo: Consider moving this declaration into a platform-specific +/// header file. +/// + #include + #include + + struct EATHREADLIB_API EARWMutexData + { + int mnReadWaiters; + int mnWriteWaiters; + int mnReaders; + EA::Thread::ThreadId mThreadIdWriter; + EA::Thread::Mutex mMutex; + EA::Thread::Condition mReadCondition; + EA::Thread::Condition mWriteCondition; + + EARWMutexData(); + + private: + // Prevent default generation of these functions by declaring but not defining them. + EARWMutexData(const EARWMutexData& rhs); // copy constructor + EARWMutexData& operator=(const EARWMutexData& rhs); // assignment operator + }; +///////////////////////////////////////////////////////////////////////// + + + +namespace EA +{ + namespace Thread + { + /// RWMutexParameters + /// Specifies rwlock settings. + struct EATHREADLIB_API RWMutexParameters + { + bool mbIntraProcess; /// True if the mutex is intra-process, else inter-process. + char mName[16]; /// Mutex name, applicable only to platforms that recognize named synchronization objects. + + RWMutexParameters(bool bIntraProcess = true, const char* pName = NULL); + }; + + + /// class RWMutex + /// Implements a multiple reader / single writer mutex. + /// This allows for significantly higher performance when data to be protected + /// is read much more frequently than written. In this case, a waiting writer + /// gets top priority and all new readers block after a waiter starts waiting. + class EATHREADLIB_API RWMutex + { + public: + enum Result + { + kResultError = -1, + kResultTimeout = -2 + }; + + enum LockType + { + kLockTypeNone = 0, + kLockTypeRead = 1, + kLockTypeWrite = 2 + }; + + /// RWMutex + /// For immediate default initialization, use no args. + /// For custom immediate initialization, supply a first argument. + /// For deferred initialization, use RWMutex(NULL, false) then later call Init. + /// For deferred initialization of an array of objects, create an empty + /// subclass whose default constructor chains back to RWMutex(NULL, false). + RWMutex(const RWMutexParameters* pRWMutexParameters = NULL, bool bDefaultParameters = true); + + /// ~RWMutex + /// Destroys an existing mutex. The mutex must not be locked by any thread, + /// otherwise the resulting behaviour is undefined. + ~RWMutex(); + + /// Init + /// Initializes the mutex if not done so in the constructor. + /// This should only be called in the case that this class was constructed + /// with RWMutex(NULL, false). + bool Init(const RWMutexParameters* pRWMutexParameters); + + /// Lock + /// Returns the new lock count for the given lock type. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + int Lock(LockType lockType, const ThreadTime& timeoutAbsolute = EA::Thread::kTimeoutNone); + + /// Unlock + /// Unlocks the mutex. The mutex must already be locked by the + /// calling thread. Otherwise the behaviour is not defined. + /// Return value is the lock count value immediately upon unlock + /// or is one of enum Result. + int Unlock(); + + /// GetLockCount + int GetLockCount(LockType lockType); + + /// GetPlatformData + /// Returns the platform-specific data handle for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + void* GetPlatformData() + { return &mRWMutexData; } + + protected: + EARWMutexData mRWMutexData; + + private: + // Objects of this class are not copyable. + RWMutex(const RWMutex&){} + RWMutex& operator=(const RWMutex&){ return *this; } + }; + + + /// RWMutexFactory + /// + /// Implements a factory-based creation and destruction mechanism for class RWMutex. + /// A primary use of this would be to allow the RWMutex implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API RWMutexFactory + { + public: + static RWMutex* CreateRWMutex(); // Internally implemented as: return new RWMutex; + static void DestroyRWMutex(RWMutex* pRWMutex); // Internally implemented as: delete pRWMutex; + + static size_t GetRWMutexSize(); // Internally implemented as: return sizeof(RWMutex); + static RWMutex* ConstructRWMutex(void* pMemory); // Internally implemented as: return new(pMemory) RWMutex; + static void DestructRWMutex(RWMutex* pRWMutex); // Internally implemented as: pRWMutex->~RWMutex(); + }; + + + } // namespace Thread + +} // namespace EA + + + + +namespace EA +{ + namespace Thread + { + /// class AutoRWMutex + /// An AutoRWMutex locks the RWMutex in its constructor and + /// unlocks the AutoRWMutex in its destructor (when it goes out of scope). + class AutoRWMutex + { + public: + AutoRWMutex(RWMutex& mutex, RWMutex::LockType lockType) + : mMutex(mutex) + { mMutex.Lock(lockType); } + + ~AutoRWMutex() + { mMutex.Unlock(); } + + protected: + RWMutex& mMutex; + + // Prevent copying by default, as copying is dangerous. + AutoRWMutex(const AutoRWMutex&); + const AutoRWMutex& operator=(const AutoRWMutex&); + }; + + } // namespace Thread + +} // namespace EA + + + + +#endif // EATHREAD_EATHREAD_RWMUTEX_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwmutex_ip.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwmutex_ip.h new file mode 100644 index 00000000..5f2639a5 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwmutex_ip.h @@ -0,0 +1,415 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements an interprocess mutex with multiple reads but single writer. +// This allows for high performance systems whereby the consumers of mpData +// are more common than the producers of mpData. +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef EATHREAD_EATHREAD_RWMUTEX_IP_H +#define EATHREAD_EATHREAD_RWMUTEX_IP_H + + +#include +#include +#include +#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +// We have to be careful about disabling this warning. Sometimes the warning is meaningful; sometimes it isn't. +// 4251: class (some template) needs to have dll-interface to be used by clients. +// 6054: String 'argument 2' might not be zero-terminated +EA_DISABLE_VC_WARNING(4251 6054) + +namespace EA +{ + namespace Thread + { + #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + + template + class Shared + { + public: + Shared(); + Shared(const char* pName); + ~Shared(); + + bool Init(const char* pName); + void Shutdown(); + bool IsNew() const { return mbCreated; } + T* operator->() { return static_cast(mpData); } + + protected: + uint32_t& GetRefCount(); + + Shared(const Shared&); + Shared& operator=(const Shared&); + + protected: + HANDLE mMapping; + void* mpData; + bool mbCreated; + char mName[32]; + T* mpT; // For debug purposes only. + }; + + + template + inline Shared::Shared() + : mMapping(NULL) + , mpData(NULL) + , mbCreated(false) + , mpT(NULL) + { + } + + + template + inline Shared::Shared(const char* pName) + : mMapping(NULL) + , mpData(NULL) + , mbCreated(false) + , mpT(NULL) + { + Init(pName); + } + + + template + inline Shared::~Shared() + { + Shutdown(); + } + + + template + inline bool Shared::Init(const char* pName) + { + bool bReturnValue = false; + + if(pName) + strncpy(mName, pName, sizeof(mName)); + else + mName[0] = 0; + mName[sizeof(mName) - 1] = 0; + + char mutexName[sizeof(mName) + 16]; + strcpy(mutexName, mName); + strcat(mutexName, ".SharedMutex"); + HANDLE hMutex = CreateMutexA(NULL, FALSE, mutexName); + EAT_ASSERT(hMutex != NULL); + if(hMutex != NULL) + { + WaitForSingleObject(hMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily. + + const size_t kDataSize = sizeof(T) + 8; // Add bytes so that we can store a ref-count of our own after the mpData. + mMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, kDataSize, mName); + + if(mMapping) + { + mbCreated = (GetLastError() != ERROR_ALREADY_EXISTS); + mpData = MapViewOfFile(mMapping, FILE_MAP_ALL_ACCESS, 0, 0, kDataSize); + + uint32_t& refCount = GetRefCount(); // The ref count is stored at the end of the mapped data. + + if(mbCreated) // If we were the first one to create this, then construct it. + { + new(mpData) T; + refCount = 1; + } + else + refCount++; + + mpT = static_cast(mpData); // For debug purposes only. + + bReturnValue = true; + } + + ReleaseMutex(hMutex); + CloseHandle(hMutex); + } + + return bReturnValue; + } + + + template + inline void Shared::Shutdown() + { + char mutexName[sizeof(mName) + 16]; + strcpy(mutexName, mName); + strcat(mutexName, ".SharedMutex"); + HANDLE hMutex = CreateMutexA(NULL, FALSE, mutexName); + EAT_ASSERT(hMutex != NULL); + if(hMutex != NULL) + { + WaitForSingleObject(hMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily. + + if(mMapping) + { + if(mpData) + { + uint32_t& refCount = GetRefCount(); // The ref count is stored at the end of the mapped data. + + if(refCount == 1) // If we are the last to use it, + static_cast(mpData)->~T(); + else + refCount--; + + UnmapViewOfFile(mpData); + mpData = NULL; + } + + CloseHandle(mMapping); + mMapping = 0; + } + + ReleaseMutex(hMutex); + CloseHandle(hMutex); + } + } + + template + inline uint32_t& Shared::GetRefCount() + { + // There will be space after T because we allocated it in Init. + uint32_t* pData32 = (uint32_t*)(((uintptr_t)mpData + sizeof(T) + 3) & ~3); // Round up to next 32 bit boundary. + return *pData32; + } + + #else + + template + class Shared + { + public: + Shared() { } + Shared(const char*) { } + + bool Init(const char*) { return true; } + void Shutdown() { } + bool IsNew() const { return true; } + T* operator->() { return &mT; } + + T mT; + }; + + #endif // #if defined(EA_PLATFORM_WINDOWS) + + } // namespace Thread + +} // namespace EA + + + +namespace EA +{ + namespace Thread + { + ///////////////////////////////////////////////////////////////////////// + /// EARWMutexIPData + /// + #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + + struct EATHREADLIB_API SharedData + { + int mnReadWaiters; + int mnWriteWaiters; + int mnReaders; + DWORD mThreadIdWriter; // Need to use a thread id instead of a thread handle. + + SharedData() : mnReadWaiters(0), mnWriteWaiters(0), mnReaders(0), mThreadIdWriter(EA::Thread::kSysThreadIdInvalid) { } + }; + + struct EATHREADLIB_API EARWMutexIPData + { + Shared mSharedData; + HANDLE mMutex; + HANDLE mReadSemaphore; + HANDLE mWriteSemaphore; + + EARWMutexIPData(); + ~EARWMutexIPData(); + + bool Init(const char* pName); + void Shutdown(); + + private: + EARWMutexIPData(const EARWMutexIPData&); + EARWMutexIPData& operator=(const EARWMutexIPData&); + }; + + #else + + struct EATHREADLIB_API EARWMutexIPData + { + EARWMutexIPData(){} + + private: + EARWMutexIPData(const EARWMutexIPData&); + EARWMutexIPData& operator=(const EARWMutexIPData&); + }; + + #endif + + + /// RWMutexParameters + struct EATHREADLIB_API RWMutexIPParameters + { + bool mbIntraProcess; /// True if the mutex is intra-process, else inter-process. + char mName[16]; /// Mutex name, applicable only to platforms that recognize named synchronization objects. + + RWMutexIPParameters(bool bIntraProcess = true, const char* pName = NULL); + }; + + + /// class RWMutexIP + /// Implements an interprocess multiple reader / single writer mutex. + /// This allows for significantly higher performance when mpData to be protected + /// is read much more frequently than written. In this case, a waiting writer + /// gets top priority and all new readers block after a waiter starts waiting. + class EATHREADLIB_API RWMutexIP + { + public: + enum Result + { + kResultError = -1, + kResultTimeout = -2 + }; + + enum LockType + { + kLockTypeNone = 0, + kLockTypeRead = 1, + kLockTypeWrite = 2 + }; + + /// RWMutexIP + /// For immediate default initialization, use no args. + /// For custom immediate initialization, supply a first argument. + /// For deferred initialization, use RWMutexIP(NULL, false) then later call Init. + /// For deferred initialization of an array of objects, create an empty + /// subclass whose default constructor chains back to RWMutexIP(NULL, false). + RWMutexIP(const RWMutexIPParameters* pRWMutexIPParameters = NULL, bool bDefaultParameters = true); + + /// ~RWMutexIP + /// Destroys an existing mutex. The mutex must not be locked by any thread, + /// otherwise the resulting behaviour is undefined. + ~RWMutexIP(); + + /// Init + /// Initializes the mutex if not done so in the constructor. + /// This should only be called in the case that this class was constructed + /// with RWMutexIP(NULL, false). + bool Init(const RWMutexIPParameters* pRWMutexIPParameters); + + /// Lock + /// Returns the new lock count for the given lock type. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + int Lock(LockType lockType, const ThreadTime& timeoutAbsolute = EA::Thread::kTimeoutNone); + + /// Unlock + /// Unlocks the mutex. The mutex must already be locked by the + /// calling thread. Otherwise the behaviour is not defined. + /// Return value is the lock count value immediately upon unlock + /// or is one of enum Result. + int Unlock(); + + /// GetLockCount + int GetLockCount(LockType lockType); + + /// GetPlatformData + /// Returns the platform-specific mpData handle for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + void* GetPlatformData() + { return &mRWMutexIPData; } + + protected: + EARWMutexIPData mRWMutexIPData; + + private: + // Objects of this class are not copyable. + RWMutexIP(const RWMutexIP&){} + RWMutexIP& operator=(const RWMutexIP&){ return *this; } + }; + + + /// RWMutexIPFactory + /// + /// Implements a factory-based creation and destruction mechanism for class RWMutexIP. + /// A primary use of this would be to allow the RWMutexIP implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API RWMutexIPFactory + { + public: + static RWMutexIP* CreateRWMutexIP(); // Internally implemented as: return new RWMutexIP; + static void DestroyRWMutexIP(RWMutexIP* pRWMutex); // Internally implemented as: delete pRWMutex; + + static size_t GetRWMutexIPSize(); // Internally implemented as: return sizeof(RWMutexIP); + static RWMutexIP* ConstructRWMutexIP(void* pMemory); // Internally implemented as: return new(pMemory) RWMutexIP; + static void DestructRWMutexIP(RWMutexIP* pRWMutex); // Internally implemented as: pRWMutex->~RWMutexIP(); + }; + + + } // namespace Thread + +} // namespace EA + + + + +namespace EA +{ + namespace Thread + { + /// class AutoRWMutexIP + /// An AutoRWMutex locks the RWMutexIP in its constructor and + /// unlocks the AutoRWMutex in its destructor (when it goes out of scope). + class AutoRWMutexIP + { + public: + AutoRWMutexIP(RWMutexIP& mutex, RWMutexIP::LockType lockType) + : mMutex(mutex) + { mMutex.Lock(lockType); } + + ~AutoRWMutexIP() + { mMutex.Unlock(); } + + protected: + RWMutexIP& mMutex; + + // Prevent copying by default, as copying is dangerous. + AutoRWMutexIP(const AutoRWMutexIP&); + const AutoRWMutexIP& operator=(const AutoRWMutexIP&); + }; + + } // namespace Thread + +} // namespace EA + +EA_RESTORE_VC_WARNING() + +#endif // EATHREAD_EATHREAD_RWMUTEX_IP_H diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwsemalock.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwsemalock.h new file mode 100644 index 00000000..b53ae6a1 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwsemalock.h @@ -0,0 +1,253 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +//--------------------------------------------------------- +// For conditions of distribution and use, see +// https://github.com/preshing/cpp11-on-multicore/blob/master/LICENSE +//--------------------------------------------------------- + +#ifndef EATHREAD_EATHREAD_RWSEMALOCK_H +#define EATHREAD_EATHREAD_RWSEMALOCK_H + +#include "eathread_atomic.h" +#include "eathread_semaphore.h" + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +namespace EA +{ + namespace Thread + { + //--------------------------------------------------------- + // RWSemaLock + //--------------------------------------------------------- + class RWSemaLock + { + public: + RWSemaLock() : mStatus(0) {} + RWSemaLock(const RWSemaLock&) = delete; + RWSemaLock(RWSemaLock&&) = delete; + RWSemaLock& operator=(const RWSemaLock&) = delete; + RWSemaLock& operator=(RWSemaLock&&) = delete; + + void ReadLock() + { + Status oldStatus, newStatus; + do + { + oldStatus.data = mStatus.GetValue(); + newStatus.data = oldStatus.data; + + if (oldStatus.writers > 0) + { + newStatus.waitToRead++; + } + else + { + newStatus.readers++; + } + // CAS until successful. On failure, oldStatus will be updated with the latest value. + } + while (!mStatus.SetValueConditional(newStatus.data, oldStatus.data)); + + if (oldStatus.writers > 0) + { + mReadSema.Wait(); + } + } + + bool ReadTryLock() + { + Status oldStatus, newStatus; + do + { + oldStatus.data = mStatus.GetValue(); + newStatus.data = oldStatus.data; + + if (oldStatus.writers > 0) + { + return false; + } + else + { + newStatus.readers++; + } + // CAS until successful. On failure, oldStatus will be updated with the latest value. + } + while (!mStatus.SetValueConditional(newStatus.data, oldStatus.data)); + + return true; + } + + void ReadUnlock() + { + Status oldStatus; + oldStatus.data = mStatus.Add(-Status::kIncrementRead) + Status::kIncrementRead; + + EAT_ASSERT(oldStatus.readers > 0); + if (oldStatus.readers == 1 && oldStatus.writers > 0) + { + mWriteSema.Post(); + } + } + + void WriteLock() + { + Status oldStatus; + oldStatus.data = mStatus.Add(Status::kIncrementWrite) - Status::kIncrementWrite; + EAT_ASSERT(oldStatus.writers + 1 <= Status::kMaximum); + if (oldStatus.readers > 0 || oldStatus.writers > 0) + { + mWriteSema.Wait(); + } + } + + bool WriteTryLock() + { + Status oldStatus, newStatus; + do + { + oldStatus.data = mStatus.GetValue(); + newStatus.data = oldStatus.data; + + if (oldStatus.writers > 0 || oldStatus.readers > 0) + { + return false; + } + else + { + newStatus.writers++; + } + // CAS until successful. On failure, oldStatus will be updated with the latest value. + } + while (!mStatus.SetValueConditional(newStatus.data, oldStatus.data)); + + return true; + } + + void WriteUnlock() + { + uint32_t waitToRead = 0; + Status oldStatus, newStatus; + do + { + oldStatus.data = mStatus.GetValue(); + EAT_ASSERT(oldStatus.readers == 0); + newStatus.data = oldStatus.data; + newStatus.writers--; + waitToRead = oldStatus.waitToRead; + if (waitToRead > 0) + { + newStatus.waitToRead = 0; + newStatus.readers = waitToRead; + } + // CAS until successful. On failure, oldStatus will be updated with the latest value. + } + while (!mStatus.SetValueConditional(newStatus.data, oldStatus.data)); + + if (waitToRead > 0) + { + mReadSema.Post(waitToRead); + } + else if (oldStatus.writers > 1) + { + mWriteSema.Post(); + } + } + + // NOTE(rparolin): + // Since the RWSemaLock uses atomics to update its status flags before blocking on a semaphore, you cannot + // rely on the answer the IsReadLocked/IsWriteLocked gives you. It's at a best a guess and you can't rely + // on it for any kind of validation checks which limits its usefulness. In addition, the original + // implementation from Preshing does not include such functionality. + // + // bool IsReadLocked() {...} + // bool IsWriteLocked() {...} + + protected: + EA_DISABLE_VC_WARNING(4201) // warning C4201: nonstandard extension used: nameless struct/union + union Status + { + enum + { + kIncrementRead = 1, + kIncrementWaitToRead = 1 << 10, + kIncrementWrite = 1 << 20, + kMaximum = (1 << 10) - 1, + }; + + struct + { + int readers : 10; // 10-bits = 1024 + int waitToRead : 10; + int writers : 10; + int pad : 2; + }; + + int data; + }; + EA_RESTORE_VC_WARNING() + + AtomicInt32 mStatus; + Semaphore mReadSema; // semaphores are non-copyable + Semaphore mWriteSema; // semaphores are non-copyable + }; + + + //--------------------------------------------------------- + // ReadLockGuard + //--------------------------------------------------------- + class AutoSemaReadLock + { + private: + RWSemaLock& m_lock; + + public: + AutoSemaReadLock(const AutoSemaReadLock&) = delete; + AutoSemaReadLock(AutoSemaReadLock&&) = delete; + AutoSemaReadLock& operator=(const AutoSemaReadLock&) = delete; + AutoSemaReadLock& operator=(AutoSemaReadLock&&) = delete; + + AutoSemaReadLock(RWSemaLock& lock) : m_lock(lock) + { + m_lock.ReadLock(); + } + + ~AutoSemaReadLock() + { + m_lock.ReadUnlock(); + } + }; + + + //--------------------------------------------------------- + // WriteLockGuard + //--------------------------------------------------------- + class AutoSemaWriteLock + { + private: + RWSemaLock& m_lock; + + public: + AutoSemaWriteLock(const AutoSemaWriteLock&) = delete; + AutoSemaWriteLock(AutoSemaWriteLock&&) = delete; + AutoSemaWriteLock& operator=(const AutoSemaWriteLock&) = delete; + AutoSemaWriteLock& operator=(AutoSemaWriteLock&&) = delete; + + AutoSemaWriteLock(RWSemaLock& lock) : m_lock(lock) + { + m_lock.WriteLock(); + } + + ~AutoSemaWriteLock() + { + m_lock.WriteUnlock(); + } + }; + } +} + +#endif // EATHREAD_EATHREAD_RWSEMALOCK_H diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwspinlock.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwspinlock.h new file mode 100644 index 00000000..e1c0b05e --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwspinlock.h @@ -0,0 +1,393 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements an efficient proper multithread-safe spinlock which supports +// multiple readers but a single writer. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_RWSPINLOCK_H +#define EATHREAD_EATHREAD_RWSPINLOCK_H + +#include +#include +#include +#include +#include + +EA_DISABLE_VC_WARNING(4100) // (Compiler claims pRWSpinLock is unreferenced) + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +namespace EA +{ + namespace Thread + { + /// class RWSpinLock + /// + /// A RWSpinLock is like a SpinLock except you can have multiple + /// readers but a single exclusive writer. This is very beneficial for + /// situations whereby there are many consumers of some data but only + /// one producer of the data. Unlock many thread-level read/write lock + /// implementations, this spin lock, like many others, follows the most + /// lean approach and does not do arbitration or fairness. The result is + /// that if you have many readers who are constantly locking the read + /// lock, write lock attempts may not be able to succeed. So you need to + /// be careful in how you use this. + /// + /// We take a page from the Linux kernel here and implement read/write + /// locks via a mechanism that uses a 'bias' value and limits the number + /// of total readers to 2^24-1, or 16,777,214. This shouldn't be a problem. + /// When the spinlock is unlocked, the value is 0x01000000. + /// Readers decrement the lock by one each, so when the spinlock is + /// read-locked, the value is between 1 and 0x00ffffff. Writers decrement + /// the lock by 0x01000000, so when a spinlock is write-locked, the value + /// must be zero. It must be zero because there can only be one writer + /// and because there can be no readers when there is a writer. When a + /// reader attempts to get a read-lock, it decrements the lock count and + /// examines the new value. If the new value is < 0, then there was a + /// write-lock present and so the reader immediately increments the lock + /// count and tries again later. There are two results that come about due + /// to this: + /// 1) In the case of 32 bit integers, if by some wild chance of nature + /// there are 256 or more reader threads and there is a writer thread + /// with a write lock and every one of the reader threads executes + /// the same decrement and compare to < 0 at the same time, then the + /// 257th thread will mistakenly think that there isn't a write lock. + /// 2) The logic to test if a write-lock is taken is not to compare + /// against zero but to compare against (> -255 and <= 0). This is + /// because readers will occasionally be 'mistakenly' decrementing + /// the lock while trying to obtain read access. + /// + /// We thus have the following possible values: + /// 0 < value < 0x01000000 ----> read-locked + /// value == 0x01000000 ----> unlocked + /// 0x01000000 < value <= 0 ----> write-locked + /// + class RWSpinLock + { + public: + RWSpinLock(); + + // This function cannot be called while the current thread + // already has a write lock, else this function will hang. + // This function can be called if the current thread already + // has a read lock, though all read locks must be matched by unlocks. + void ReadLock(); + + // This function cannot be called while the current thread + // already has a write lock, else this function will hang. + // This function can be called if the current thread already + // has a read lock (in which case it will always succeed), + // though all read locks must be matched by unlocks. + bool ReadTryLock(); + + // Returns true if any thread currently has a read lock. + // The return value is subject to be out of date by the + // time it is read by the current thread, unless the current + // thread has a read lock. If IsReadLocked is true, then + // at that moment IsWriteLocked is necessarily false. + // If IsReadLocked is false, IsWriteLock may be either true or false. + bool IsReadLocked() const; + + // Unlocks for reading, as a match to ReadLock or a successful + // ReadTryLock. All read locks must be matched by ReadUnlock with + // the same thread that has the read lock. + void ReadUnlock(); + + // This function cannot be called while the current thread + // already has a read or write lock, else this function will hang. + void WriteLock(); + + // If this function is called while the current thread already + // has a read or write lock, it will always return false. + bool WriteTryLock(); + + // If this function returns true, then IsReadLocked must at that moment + // be false. + bool IsWriteLocked() const; + + // Matches WriteLock or a successful WriteTryLock. + void WriteUnlock(); + + // Returns the address of mValue. This value should be read for + // diagnostic purposes only and should not be written. + void* GetPlatformData(); + + public: + enum Value + { + kValueUnlocked = 0x01000000 + }; + + AtomicInt32 mValue; + }; + + + + /// RWSpinLockFactory + /// + /// Implements a factory-based creation and destruction mechanism for class RWSpinlock. + /// A primary use of this would be to allow the RWSpinlock implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + /// + class EATHREADLIB_API RWSpinLockFactory + { + public: + static RWSpinLock* CreateRWSpinLock(); + static void DestroyRWSpinLock(RWSpinLock* pRWSpinLock); + static size_t GetRWSpinLockSize(); + static RWSpinLock* ConstructRWSpinLock(void* pMemory); + static void DestructRWSpinLock(RWSpinLock* pRWSpinLock); + }; + + + + /// class AutoRWSpinLock + /// + /// Example usage: + /// void Function() { + /// AutoRWSpinLock autoLock(AutoRWSpinLock::kLockTypeRead); + /// // Do something + /// } + /// + class AutoRWSpinLock + { + public: + enum LockType + { + kLockTypeRead, + kLockTypeWrite + }; + + AutoRWSpinLock(RWSpinLock& spinLock, LockType lockType) ; + ~AutoRWSpinLock(); + + protected: + RWSpinLock& mSpinLock; + LockType mLockType; + + // Prevent copying by default, as copying is dangerous. + AutoRWSpinLock(const AutoRWSpinLock&); + const AutoRWSpinLock& operator=(const AutoRWSpinLock&); + }; + + } // namespace Thread + +} // namespace EA + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// inlines +/////////////////////////////////////////////////////////////////////////////// + +namespace EA +{ + namespace Thread + { + + /////////////////////////////////////////////////////////////////////// + // RWSpinLock + /////////////////////////////////////////////////////////////////////// + + inline + RWSpinLock::RWSpinLock() + : mValue(kValueUnlocked) + { + } + + + inline + void RWSpinLock::ReadLock() + { + Top: // Due to modern processor branch prediction, the compiler will optimize better for true branches and so we do a manual goto loop here. + if((unsigned)mValue.Decrement() < kValueUnlocked) + return; + mValue.Increment(); + while(mValue.GetValueRaw() <= 0){ // It is better to do this polling loop as a first check than to + #ifdef EA_THREAD_COOPERATIVE // do an atomic decrement repeatedly, as the atomic lock is + ThreadSleep(); // potentially not a cheap thing due to potential bus locks on some platforms.. + #else + EAProcessorPause(); // We don't check for EA_TARGET_SMP here and instead sleep if not defined because you probably shouldn't be using a spinlock on a pre-emptive system unless it is a multi-processing system. + #endif + } + goto Top; + } + + + inline + bool RWSpinLock::ReadTryLock() + { + const unsigned nNewValue = (unsigned)mValue.Decrement(); + if(nNewValue < kValueUnlocked) // Given that nNewValue is unsigned, we don't need to test for < 0. + return true; + mValue.Increment(); + return false; + } + + + inline + bool RWSpinLock::IsReadLocked() const + { + const unsigned nValue = (unsigned)mValue.GetValue(); + return ((nValue - 1) < (kValueUnlocked - 1)); // Given that nNewValue is unsigned, this is faster than comparing ((n > 0) && (n < kValueUnlocked)), due to the presence of only one comparison instead of two. + } + + + inline + void RWSpinLock::ReadUnlock() + { + mValue.Increment(); + } + + + inline + void RWSpinLock::WriteLock() + { + Top: + if(mValue.Add(-kValueUnlocked) == 0) + return; + mValue.Add(kValueUnlocked); + while(mValue.GetValueRaw() != kValueUnlocked){ // It is better to do this polling loop as a first check than to + #ifdef EA_THREAD_COOPERATIVE // do an atomic decrement repeatedly, as the atomic lock is + ThreadSleep(); // potentially not a cheap thing due to potential bus locks on some platforms.. + #else + EAProcessorPause(); // We don't check for EA_TARGET_SMP here and instead sleep if not defined because you probably shouldn't be using a spinlock on a pre-emptive system unless it is a multi-processing system. + #endif + } + goto Top; + } + + + inline + bool RWSpinLock::WriteTryLock() + { + if(mValue.Add(-kValueUnlocked) == 0) + return true; + mValue.Add(kValueUnlocked); + return false; + } + + + inline + bool RWSpinLock::IsWriteLocked() const + { + return (mValue.GetValue() <= 0); // This fails to work if 127 threads at once are in the middle of a failed write lock attempt. + } + + + inline + void RWSpinLock::WriteUnlock() + { + mValue.Add(kValueUnlocked); + } + + + inline + void* RWSpinLock::GetPlatformData() + { + return &mValue; + } + + + + /////////////////////////////////////////////////////////////////////// + // RWSpinLockFactory + /////////////////////////////////////////////////////////////////////// + + inline + RWSpinLock* RWSpinLockFactory::CreateRWSpinLock() + { + Allocator* pAllocator = GetAllocator(); + + if(pAllocator) + return new(pAllocator->Alloc(sizeof(RWSpinLock))) RWSpinLock; + else + return new RWSpinLock; + } + + + inline + void RWSpinLockFactory::DestroyRWSpinLock(RWSpinLock* pRWSpinLock) + { + Allocator* pAllocator = GetAllocator(); + + if(pAllocator) + { + pRWSpinLock->~RWSpinLock(); + pAllocator->Free(pRWSpinLock); + } + else + delete pRWSpinLock; + } + + + inline + size_t RWSpinLockFactory::GetRWSpinLockSize() + { + return sizeof(RWSpinLock); + } + + + inline + RWSpinLock* RWSpinLockFactory::ConstructRWSpinLock(void* pMemory) + { + return new(pMemory) RWSpinLock; + } + + + inline + void RWSpinLockFactory::DestructRWSpinLock(RWSpinLock* pRWSpinLock) + { + pRWSpinLock->~RWSpinLock(); + } + + + + + /////////////////////////////////////////////////////////////////////// + // AutoRWSpinLock + /////////////////////////////////////////////////////////////////////// + + inline + AutoRWSpinLock::AutoRWSpinLock(RWSpinLock& spinLock, LockType lockType) + : mSpinLock(spinLock), mLockType(lockType) + { + if(mLockType == kLockTypeRead) + mSpinLock.ReadLock(); + else + mSpinLock.WriteLock(); + } + + + inline + AutoRWSpinLock::~AutoRWSpinLock() + { + if(mLockType == kLockTypeRead) + mSpinLock.ReadUnlock(); + else + mSpinLock.WriteUnlock(); + } + + + } // namespace Thread + +} // namespace EA + +EA_RESTORE_VC_WARNING() + +#endif // EATHREAD_EATHREAD_RWSPINLOCK_H diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwspinlockw.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwspinlockw.h new file mode 100644 index 00000000..00deb831 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_rwspinlockw.h @@ -0,0 +1,430 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements an efficient proper multithread-safe spinlock which supports +// multiple simultaneous readers but a single writer, where writers get +// priority over readers. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_RWSPINLOCKW_H +#define EATHREAD_EATHREAD_RWSPINLOCKW_H + +#include +#include +#include +#include +#include + +EA_DISABLE_VC_WARNING(4100) // (Compiler claims pRWSpinLockW is unreferenced) + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +namespace EA +{ + namespace Thread + { + /// class RWSpinLockW + /// + /// This class differs from RWSpinLock in that it gives writers priority. + /// In exchange for that feature, this version doesn't allow recursive + /// read locks and it becomes inefficient due to excessive spinning if + /// there are very many simultaneous readers. + /// + /// A RWSpinLockW is like a SpinLock except you can have multiple + /// readers but a single exclusive writer. This is very beneficial for + /// situations whereby there are many consumers of some data but only + /// one producer of the data. Unlock many thread-level read/write lock + /// implementations, this spin lock, like many others, follows the most + /// lean approach and does not do arbitration or fairness. The result is + /// that if you have many readers who are constantly locking the read + /// lock, write lock attempts may not be able to succeed. So you need to + /// be careful in how you use this. + /// + /// Note the usage of GetValueRaw in the source code for this class. + /// Use of GetValueRaw instead of GetValue is due to a tradeoff that + /// has been chosen. GetValueRaw does not come with memory read barrier + /// and thus the read value may be out of date. This is OK because it's + /// only used as a rule of thumb to help decide what synchronization + /// primitive to use next. This results in significantly faster execution + /// because only one memory synchronization primitive is typically + /// executed instead of two. The problem with GetValueRaw, however, + /// is that in cases where there is very high locking activity from + /// many threads simultaneously GetValueRaw usage could result in + /// a "bad guess" as to what to do next and can also result in a lot + /// of spinning, even infinite spinning in the most pathological case. + /// However, in practice on the platforms that target this situation + /// is unlikely to the point of being virtually impossible in practice. + /// And if it was possible then we recommend the user use a different + /// mechanism, such as the regular EAThread RWSpinLockW. + /// + class RWSpinLockW + { + public: + RWSpinLockW(); + + // This function cannot be called while the current thread + // already has a write lock, else this function will hang. + // Nor can this function can be called if the current thread + // already has a read lock, as it can result in a hang. + void ReadLock(); + + // This function cannot be called while the current thread + // already has a write lock, else this function will hang. + // Nor can this function can be called if the current thread + // already has a read lock, as it can result in a hang. + bool ReadTryLock(); + + // If this function returns true, then IsReadLocked must at that moment + // be false. + bool IsReadLocked() const; + + void ReadUnlock(); + + // This function cannot be called while the current thread + // already has a read or write lock, else this function will hang. + void WriteLock(); + + // If this function is called while the current thread already + // has a read or write lock, it will always return false. + bool WriteTryLock(); + + // If this function returns true, then IsReadLocked must at that moment + // be false. + bool IsWriteLocked() const; + + void WriteUnlock(); + + /// Returns the platform-specific data handle for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + void* GetPlatformData(); + + protected: + enum Value + { + kWriteLockBit = 0x80000000, + kWriteWaitingInc = 0x00010000, + kReadLockInc = 0x00000001, + kWriteWaitingMask = 0x7FFF0000, + kReadLockMask = 0x0000FFFF, + kLockAllMask = kWriteLockBit | kReadLockMask, + kWriteAllMask = kWriteLockBit | kWriteWaitingMask, + }; + + AtomicInt32 mValue; + }; + + + + /// RWSpinLockWFactory + /// + /// Implements a factory-based creation and destruction mechanism for class RWSpinLockW. + /// A primary use of this would be to allow the RWSpinLockW implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + /// + class EATHREADLIB_API RWSpinLockWFactory + { + public: + static RWSpinLockW* CreateRWSpinLockW(); + static void DestroyRWSpinLockW(RWSpinLockW* pRWSpinLockW); + static size_t GetRWSpinLockWSize(); + static RWSpinLockW* ConstructRWSpinLockW(void* pMemory); + static void DestructRWSpinLockW(RWSpinLockW* pRWSpinLockW); + }; + + + + /// class AutoRWSpinLockW + /// + /// Example usage: + /// void Function() { + /// AutoRWSpinLockW autoLock(AutoRWSpinLockW::kLockTypeRead); + /// // Do something + /// } + /// + class AutoRWSpinLockW + { + public: + enum LockType + { + kLockTypeRead, + kLockTypeWrite + }; + + AutoRWSpinLockW(RWSpinLockW& SpinLockW, LockType lockType); + ~AutoRWSpinLockW(); + + protected: + RWSpinLockW& mSpinLockW; + LockType mLockType; + + // Prevent copying by default, as copying is dangerous. + AutoRWSpinLockW(const AutoRWSpinLockW&); + const AutoRWSpinLockW& operator=(const AutoRWSpinLockW&); + }; + + } // namespace Thread + +} // namespace EA + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// inlines +/////////////////////////////////////////////////////////////////////////////// + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + + + /////////////////////////////////////////////////////////////////////// + // RWSpinLockW + /////////////////////////////////////////////////////////////////////// + + inline + RWSpinLockW::RWSpinLockW() + : mValue(0) + { + } + + + inline + void RWSpinLockW::ReadLock() + { + int32_t currVal = mValue.GetValueRaw(); // See not above about GetValueRaw usage. + + // If there is no writer nor waiting writers, attempt a read lock. + if( (currVal & kWriteAllMask) == 0 ) + { + if( mValue.SetValueConditional( currVal + kReadLockInc, currVal ) ) + return; + } + + // Spin until there is no writer and no waiting writers. + // By waiting till there no read or write lockers, we tend to avoid the case + // whereby readers starve out writers. The downside is that a lot of read + // activity can cause read parallelism to be reduced and read threads waiting + // for each other. + do + { + EA_THREAD_DO_SPIN(); + currVal = mValue.GetValue(); // or EAReadBarrier(); mValue.GetValueRaw(); + }while (currVal & kWriteAllMask); + + // At this point, we ignore waiting writers and take the lock if we + // can. Any waiting writers that have shown up right as we execute this + // code aren't given any priority over us, unlike above where they are. + for( ;; ) + { + // This code has a small problem in that a large number of simultaneous + // frequently locking/unlocking readers can cause this code to spin + // a lot (in theory, indefinitely). However, in practice our use cases + // and target hardware shouldn't cause this to happen. + if( (currVal & kWriteLockBit) == 0 ) + { + if( mValue.SetValueConditional( currVal + kReadLockInc, currVal ) ) + return; + } + + EA_THREAD_DO_SPIN(); + currVal = mValue.GetValue(); // or EAReadBarrier(); mValue.GetValueRaw(); + } + } + + + inline + bool RWSpinLockW::ReadTryLock() + { + int32_t currVal = mValue.GetValueRaw(); + + // If there is no writer nor waiting writers, attempt a read lock. + if( (currVal & kWriteAllMask) == 0 ) + { + if( mValue.SetValueConditional( currVal + kReadLockInc, currVal ) ) + return true; + } + + return false; + } + + + inline + bool RWSpinLockW::IsReadLocked() const + { + // This return value has only diagnostic meaning. It cannot be used for thread synchronization purposes. + return ((mValue.GetValueRaw() & kReadLockMask) != 0); + } + + + inline + void RWSpinLockW::ReadUnlock() + { + EAT_ASSERT(IsReadLocked()); // This can't tell us if the current thread was one of the lockers. But it's better than nothing as a debug test. + mValue.Add( -kReadLockInc ); + } + + + inline + void RWSpinLockW::WriteLock() + { + int32_t currVal = mValue.GetValueRaw(); + + // If there is no writer, waiting writers, nor readers, attempt a write lock. + if( (currVal & kLockAllMask) == 0 ) + { + if( mValue.SetValueConditional( currVal | kWriteLockBit, currVal ) ) + return; + } + + // Post a waiting write. This will make new readers spin until all existing + // readers have released their lock, so that we get an even chance. + mValue.Add( kWriteWaitingInc ); + + // Spin until we get the lock. + for( ;; ) + { + if( (currVal & kLockAllMask) == 0 ) + { + if( mValue.SetValueConditional( (currVal | kWriteLockBit) - kWriteWaitingInc, currVal ) ) + return; + } + + EA_THREAD_DO_SPIN(); + currVal = mValue.GetValue(); // or EAReadBarrier(); mValue.GetValueRaw(); + } + } + + + inline + bool RWSpinLockW::WriteTryLock() + { + int32_t currVal = mValue.GetValueRaw(); + + // If there is no writer, waiting writers, nor readers, attempt a write lock. + if( (currVal & kLockAllMask) == 0 ) + { + if( mValue.SetValueConditional( currVal | kWriteLockBit, currVal ) ) + return true; + } + + return false; + } + + + inline + bool RWSpinLockW::IsWriteLocked() const + { + // This return value has only diagnostic meaning. It cannot be used for thread synchronization purposes. + return ( (mValue.GetValueRaw() & kWriteLockBit) != 0 ); + } + + + inline + void RWSpinLockW::WriteUnlock() + { + EAT_ASSERT(IsWriteLocked()); + mValue.Add( -kWriteLockBit ); + } + + + inline + void* RWSpinLockW::GetPlatformData() + { + return &mValue; + } + + + + /////////////////////////////////////////////////////////////////////// + // RWSpinLockFactory + /////////////////////////////////////////////////////////////////////// + + inline + RWSpinLockW* RWSpinLockWFactory::CreateRWSpinLockW() + { + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(RWSpinLockW))) RWSpinLockW; + else + return new RWSpinLockW; + } + + inline + void RWSpinLockWFactory::DestroyRWSpinLockW(RWSpinLockW* pRWSpinLock) + { + if(gpAllocator) + { + pRWSpinLock->~RWSpinLockW(); + gpAllocator->Free(pRWSpinLock); + } + else + delete pRWSpinLock; + } + + inline + size_t RWSpinLockWFactory::GetRWSpinLockWSize() + { + return sizeof(RWSpinLockW); + } + + inline + RWSpinLockW* RWSpinLockWFactory::ConstructRWSpinLockW(void* pMemory) + { + return new(pMemory) RWSpinLockW; + } + + inline + void RWSpinLockWFactory::DestructRWSpinLockW(RWSpinLockW* pRWSpinLock) + { + pRWSpinLock->~RWSpinLockW(); + } + + + + /////////////////////////////////////////////////////////////////////// + // AutoRWSpinLock + /////////////////////////////////////////////////////////////////////// + + inline + AutoRWSpinLockW::AutoRWSpinLockW(RWSpinLockW& spinLock, LockType lockType) + : mSpinLockW(spinLock), mLockType(lockType) + { + if(mLockType == kLockTypeRead) + mSpinLockW.ReadLock(); + else + mSpinLockW.WriteLock(); + } + + + inline + AutoRWSpinLockW::~AutoRWSpinLockW() + { + if(mLockType == kLockTypeRead) + mSpinLockW.ReadUnlock(); + else + mSpinLockW.WriteUnlock(); + } + + + } // namespace Thread + +} // namespace EA + +EA_RESTORE_VC_WARNING() + +#endif // Header include guard diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_semaphore.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_semaphore.h new file mode 100644 index 00000000..02eeeb2e --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_semaphore.h @@ -0,0 +1,338 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements a semaphore thread synchronization class. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_SEMAPHORE_H +#define EATHREAD_EATHREAD_SEMAPHORE_H + + +#include +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_USE_SYNTHESIZED_SEMAPHORE +// +// Defined as 0 or 1. Defined as 1 if the OS provides no native semaphore support. +// +#ifndef EATHREAD_USE_SYNTHESIZED_SEMAPHORE + #define EATHREAD_USE_SYNTHESIZED_SEMAPHORE 0 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_FAST_MS_SEMAPHORE_ENABLED +// +// Defined as 0 or 1. +// Enables the usage of a faster intra-process semaphore on Microsoft platforms. +// By faster we mean that it is typically 10x or more faster. +// Has the downside that it is not interchangeable with the SEMAPHORE built-in +// type and it's behaviour won't be strictly identical. +// Even if this option is enabled, you can still get the built-in behaviour +// of Microsoft semaphores by specifying the semaphore as inter-process. +// +#ifndef EATHREAD_FAST_MS_SEMAPHORE_ENABLED + #define EATHREAD_FAST_MS_SEMAPHORE_ENABLED 1 +#endif + + +///////////////////////////////////////////////////////////////////////// +/// EASemaphoreData +/// +/// This is used internally by class Semaphore. +/// Todo: Consider moving this declaration into a platform-specific +/// header file. +/// +#if !EA_THREADS_AVAILABLE + struct EASemaphoreData + { + volatile int mnCount; + int mnMaxCount; + + EASemaphoreData(); + }; + +#elif EATHREAD_USE_SYNTHESIZED_SEMAPHORE + #include + #include + #include + + struct EASemaphoreData + { + EA::Thread::Condition mCV; + EA::Thread::Mutex mMutex; + EA::Thread::AtomicInt32 mnCount; + int mnMaxCount; + bool mbValid; + + EASemaphoreData(); + }; + +#elif defined(EA_PLATFORM_APPLE) + + #include + #include + + struct EASemaphoreData + { + semaphore_t mSemaphore; + EA::Thread::AtomicInt32 mnCount; + int mnMaxCount; + bool mbIntraProcess; + + EASemaphoreData(); + }; + +#elif defined(EA_PLATFORM_SONY) + #include + #include + #include + struct EASemaphoreData + { + SceKernelSema mSemaphore; + + int mnMaxCount; + EA::Thread::AtomicInt32 mnCount; + + EASemaphoreData(); + }; + +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + + #if defined(EA_PLATFORM_WINDOWS) + #ifdef CreateSemaphore + #undef CreateSemaphore // Windows #defines CreateSemaphore to CreateSemaphoreA or CreateSemaphoreW. + #endif + #endif + + struct EASemaphoreData + { + sem_t mSemaphore; + EA::Thread::AtomicInt32 mnCount; + int mnMaxCount; + bool mbIntraProcess; + + EASemaphoreData(); + }; + +#elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + #ifdef CreateSemaphore + #undef CreateSemaphore // Windows #defines CreateSemaphore to CreateSemaphoreA or CreateSemaphoreW. + #endif + + struct EATHREADLIB_API EASemaphoreData + { + void* mhSemaphore; // We use void* instead of HANDLE in order to avoid #including windows.h. HANDLE is typedef'd to (void*) on all Windows-like platforms. + int32_t mnCount; // Number of available posts. Under the fast semaphore pathway, a negative value means there are waiters. + int32_t mnCancelCount; // Used by fast semaphore logic. Is the deferred cancel count. + int32_t mnMaxCount; // + bool mbIntraProcess; // Used under Windows, which can have multiple processes. Always true for XBox. + + EASemaphoreData(); + void UpdateCancelCount(int32_t n); + }; + +#endif +///////////////////////////////////////////////////////////////////////// + + + + + +namespace EA +{ + namespace Thread + { + /// SemaphoreParameters + /// Specifies semaphore settings. + struct EATHREADLIB_API SemaphoreParameters + { + int mInitialCount; /// Initial available count + int mMaxCount; /// Max possible count. Defaults to INT_MAX. + bool mbIntraProcess; /// True if the semaphore is intra-process, else inter-process. + char mName[16]; /// Semaphore name, applicable only to platforms that recognize named synchronization objects. + + SemaphoreParameters(int initialCount = 0, bool bIntraProcess = true, const char* pName = NULL); + }; + + + /// class Semaphore + /// A semaphore is an object which has an associated count which is >= 0 and + /// a value > 0 means that a thread can 'grab' the semaphore and decrement its + /// value by one. A value of 0 means that threads must wait until another thread + /// 'un-grabs' the semaphore. Thus a semaphore is like a car rental agency which + /// has a limited number of cars for rent and if they are out of cars, you have + /// to wait until one of the renters returns their car. + class EATHREADLIB_API Semaphore + { + public: + enum Result{ + kResultError = -1, + kResultTimeout = -2 + }; + + /// Semaphore + /// For immediate default initialization, use no args. + /// For custom immediate initialization, supply a first argument. + /// For deferred initialization, use Semaphore(NULL, false) then later call Init. + /// For deferred initialization of an array of objects, create an empty + /// subclass whose default constructor chains back to Semaphore(NULL, false). + Semaphore(const SemaphoreParameters* pSemaphoreParameters = NULL, bool bDefaultParameters = true); + + /// Semaphore + /// This is a constructor which initializes the Semaphore to a specific count + /// and intializes the other Semaphore parameters to default values. See the + /// SemaphoreParameters struct for info on these default values. + Semaphore(int initialCount); + + /// ~Semaphore + /// Destroys an existing semaphore. The semaphore must not be locked + /// by any thread, otherwise the resulting behaviour is undefined. + ~Semaphore(); + + /// Init + /// Initializes the semaphore with given parameters. + bool Init(const SemaphoreParameters* pSemaphoreParameters); + + /// Wait + /// Locks the semaphore (reducing its count by one) or gives up trying to + /// lock it after a given timeout has expired. If the semaphore count is > 0 + /// then the count will be reduced by one. If the semaphore count is 0, the + /// call will block until another thread unlocks it or the timeout expires. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + /// Return value: + /// kResultError The semaphore could not be obtained due to error. + /// kResultTimeout The semaphore could not be obtained due to timeout. + /// >= 0 The new count for the semaphore. + /// + /// It's possible that two threads waiting on the same semaphore will return + /// with a result of zero. Thus you cannot rely on the semaphore's return value + /// to ascertain which was the last thread to return from the Wait. + int Wait(const ThreadTime& timeoutAbsolute = kTimeoutNone); + + /// Post + /// Increments the signalled value of the semaphore by the count. + /// Returns the available count after the operation has completed. + /// Returns kResultError upon error. A Wait is often eventually + /// followed by a corresponding Post. + /// For the case of count being greater than 1, not all platforms + /// act the same. If count results in exceeding the max count then + /// kResultError is returned. Some platforms return kResultError + /// before any of account is applied, while others return + /// kResultError after some of count has been applied. + int Post(int count = 1); + + /// GetCount + /// Returns current number of available locks associated with the semaphore. + /// This is useful for debugging and for quick polling checks of the + /// status of the semaphore. This value changes over time as multiple + /// threads wait and post to the semaphore. This value cannot be trusted + /// to exactly represent the count upon its return if multiple threads are + /// using this Semaphore at the time. + int GetCount() const; + + /// GetPlatformData + /// Returns the platform-specific data handle for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + void* GetPlatformData() + { return &mSemaphoreData; } + + // Objects of this class are not copyable. + Semaphore(const Semaphore&) = delete; + Semaphore& operator=(const Semaphore&) = delete; + + protected: + EASemaphoreData mSemaphoreData; + }; + + + /// SemaphoreFactory + /// + /// Implements a factory-based creation and destruction mechanism for class Semaphore. + /// A primary use of this would be to allow the Semaphore implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API SemaphoreFactory + { + public: + static Semaphore* CreateSemaphore(); // Internally implemented as: return new Semaphore; + static void DestroySemaphore(Semaphore* pSemaphore); // Internally implemented as: delete pSemaphore; + + static size_t GetSemaphoreSize(); // Internally implemented as: return sizeof(Semaphore); + static Semaphore* ConstructSemaphore(void* pMemory); // Internally implemented as: return new(pMemory) Semaphore; + static void DestructSemaphore(Semaphore* pSemaphore); // Internally implemented as: pSemaphore->~Semaphore(); + }; + + + } // namespace Thread + +} // namespace EA + + + + +namespace EA +{ + namespace Thread + { + /// class AutoSemaphore + /// An AutoSemaphore grabs the Semaphore in its constructor and posts + /// the Semaphore once in its destructor (when it goes out of scope). + class EATHREADLIB_API AutoSemaphore + { + public: + AutoSemaphore(Semaphore& semaphore) + : mSemaphore(semaphore) + { mSemaphore.Wait(); } + + ~AutoSemaphore() + { mSemaphore.Post(1); } + + protected: + Semaphore& mSemaphore; + + // Prevent copying by default, as copying is dangerous. + AutoSemaphore(const AutoSemaphore&); + const AutoSemaphore& operator=(const AutoSemaphore&); + }; + + } // namespace Thread + +} // namespace EA + +#endif // EATHREAD_EATHREAD_SEMAPHORE_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_spinlock.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_spinlock.h new file mode 100644 index 00000000..9ec55fd4 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_spinlock.h @@ -0,0 +1,319 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Implements an efficient proper multithread-safe spinlock. +// +// A spin lock is the lightest form of mutex available. The Lock operation is +// simply a loop that waits to set a shared variable. SpinLocks are not +// recursive (i.e. they can only be locked once by a thread) and are +// intra-process only. You have to be careful using spin locks because if you +// have a high priority thread that calls Lock while a lower priority thread +// has the same lock, then on many systems the higher priority thread will +// use up all the CPU time waiting for the lock and the lower priority thread +// will not get the CPU time needed to free the lock. +// +// From Usenet: +// A spinlock is a machine-specific "optimized" form of mutex +// ("MUTual EXclusion" device). However, you should never use +// a spinlock unless you know that you have multiple threads +// and that you're running on a multiprocessor. Otherwise, at +// best you're wasting a lot of time. A spinlock is great for +// "highly parallel" algorithms like matrix decompositions, +// where the application (or runtime) "knows" (or at least goes +// to lengths to ensure) that the threads participating are all +// running at the same time. Unless you know that, (and, if your +// code doesn't create threads, you CAN'T know that), don't even +// think of using a spinlock." +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_SPINLOCK_H +#define EATHREAD_EATHREAD_SPINLOCK_H + + +#include +#include +#include // include new for placement new operator + +#if defined(EA_PROCESSOR_X86) + // The reference x86 code works fine, as there is little that assembly + // code can do to improve it by much, assuming that the code is compiled + // in an optimized way. With VC7 on the PC platform, compiling with + // optimization set to 'minimize size' and most other optimizations + // enabled yielded code that was similar to Intel reference asm code. + // However, when the compiler was set to minimize size and enable inlining, + // it created an implementation of the Lock function that was less optimal. + // #include +#elif defined(EA_PROCESSOR_IA64) + // The reference code below is probably fine. + // #include +#endif + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +// The above header files would define EA_THREAD_SPINLOCK_IMPLEMENTED. +#if !defined(EA_THREAD_SPINLOCK_IMPLEMENTED) + + // We provide an implementation that works for all systems but is less optimal. + #include + #include + + namespace EA + { + namespace Thread + { + /// class SpinLock + /// + /// Spinlocks are high-performance locks designed for special circumstances. + /// As such, they are not 'recursive' -- you cannot lock a spinlock twice. + /// Spinlocks have no explicit awareness of threading, but they are explicitly + /// thread-safe. + /// + /// You do not want to use spin locks as a *general* replacement for mutexes or + /// critical sections, even if you know your mutex use won't be recursive. + /// The reason for this is due to thread scheduling and thread priority issues. + /// A spinlock is not a kernel- or threading-kernel-level object and thus while + /// this gives it a certain amount of speed, it also means that if you have a + /// low priority thread thread with a spinlock locked and a high priority thread + /// waiting for the spinlock, the program will hang, possibly indefinitely, + /// because the thread scheduler is giving all its time to the high priority + /// thread which happens to be stuck. + /// + /// On the other hand, when judiciously used, a spin lock can yield significantly + /// higher performance than general mutexes, especially on platforms where mutex + /// locking is particularly expensive or on multiprocessing systems. + /// + class SpinLock + { + protected: // Declared at the top because otherwise some compilers fail to compile inline functions below. + AtomicInt32 mAI; /// A value of 0 means unlocked, while 1 means locked. + + public: + SpinLock(); + + void Lock(); + bool TryLock(); + bool IsLocked(); + void Unlock(); + + void* GetPlatformData(); + }; + + + /// SpinLockFactory + /// + /// Implements a factory-based creation and destruction mechanism for class Spinlock. + /// A primary use of this would be to allow the Spinlock implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API SpinLockFactory + { + public: + static SpinLock* CreateSpinLock(); + static void DestroySpinLock(SpinLock* pSpinLock); + + static size_t GetSpinLockSize(); + static SpinLock* ConstructSpinLock(void* pMemory); + + static void DestructSpinLock(SpinLock* pSpinLock); + }; + + } // namespace Thread + + } // namespace EA + + +#endif // EA_THREAD_SPINLOCK_IMPLEMENTED + + + +namespace EA +{ + namespace Thread + { + /// class AutoSpinLock + /// An AutoSpinLock locks the SpinLock in its constructor and + /// unlocks the SpinLock in its destructor (when it goes out of scope). + class AutoSpinLock + { + public: + AutoSpinLock(SpinLock& spinLock); + ~AutoSpinLock(); + + protected: + SpinLock& mSpinLock; + + protected: + // Prevent copying by default, as copying is dangerous. + AutoSpinLock(const AutoSpinLock&); + const AutoSpinLock& operator=(const AutoSpinLock&); + }; + + } // namespace Thread + +} // namespace EA + + + + + + +/////////////////////////////////////////////////////////////////////////////// +// inlines +/////////////////////////////////////////////////////////////////////////////// + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + + + /////////////////////////////////////////////////////////////////////// + // SpinLock + /////////////////////////////////////////////////////////////////////// + + inline + SpinLock::SpinLock() + : mAI(0) + { + } + + inline + void SpinLock::Lock() + { + Top: // Due to modern processor branch prediction, the compiler will optimize better for true branches and so we do a manual goto loop here. + if(mAI.SetValueConditional(1, 0)) + return; + + // The loop below is present because the SetValueConditional + // call above is likely to be significantly more expensive and + // thus we benefit by polling before attempting the real thing. + // This is a common practice and is recommended by Intel, etc. + while (mAI.GetValue() != 0) + { + #ifdef EA_THREAD_COOPERATIVE + ThreadSleep(); + #else + EAProcessorPause(); + #endif + } + goto Top; + } + + inline + bool SpinLock::TryLock() + { + return mAI.SetValueConditional(1, 0); + } + + inline + bool SpinLock::IsLocked() + { + return mAI.GetValueRaw() != 0; + } + + inline + void SpinLock::Unlock() + { + EAT_ASSERT(IsLocked()); + mAI.SetValue(0); + } + + inline + void* SpinLock::GetPlatformData() + { + return &mAI; + } + + + /////////////////////////////////////////////////////////////////////// + // SpinLockFactory + /////////////////////////////////////////////////////////////////////// + + inline + SpinLock* SpinLockFactory::CreateSpinLock() + { + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(SpinLock))) SpinLock; + else + return new SpinLock; + } + + inline + void SpinLockFactory::DestroySpinLock(SpinLock* pSpinLock) + { + if(gpAllocator) + { + pSpinLock->~SpinLock(); + gpAllocator->Free(pSpinLock); + } + else + delete pSpinLock; + } + + inline + size_t SpinLockFactory::GetSpinLockSize() + { + return sizeof(SpinLock); + } + + inline + SpinLock* SpinLockFactory::ConstructSpinLock(void* pMemory) + { + return new(pMemory) SpinLock; + } + + EA_DISABLE_VC_WARNING(4100) // Compiler mistakenly claims pSpinLock is unreferenced + inline + void SpinLockFactory::DestructSpinLock(SpinLock* pSpinLock) + { + pSpinLock->~SpinLock(); + } + EA_RESTORE_VC_WARNING() + + + /////////////////////////////////////////////////////////////////////// + // AutoSpinLock + /////////////////////////////////////////////////////////////////////// + + inline + AutoSpinLock::AutoSpinLock(SpinLock& spinLock) + : mSpinLock(spinLock) + { + mSpinLock.Lock(); + } + + inline + AutoSpinLock::~AutoSpinLock() + { + mSpinLock.Unlock(); + } + + } // namespace Thread + +} // namespace EA + +#endif // EATHREAD_EATHREAD_SPINLOCK_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_storage.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_storage.h new file mode 100644 index 00000000..914604b3 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_storage.h @@ -0,0 +1,334 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Defines thread-local storage and related concepts in a platform-independent +// and thread-safe manner. +// +// As of this writing (10/2003), documentation concerning thread-local +// storage implementations under GCC, pthreads, and MSVC/Windows can be found at: +// http://gcc.gnu.org/onlinedocs/gcc-3.3.2/gcc/Thread-Local.html#Thread-Local +// http://java.icmc.sc.usp.br/library/books/ibm_pthreads/users-33.htm#324811 +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_Thread_Local_Storage_.28.TLS.29.asp +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_STORAGE_H +#define EATHREAD_EATHREAD_STORAGE_H + + +#include + +EA_DISABLE_VC_WARNING(4574) +#include +EA_RESTORE_VC_WARNING() + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +namespace EA +{ + namespace Thread + { + ///////////////////////////////////////////////////////////////////////// + /// EA_THREAD_LOCAL + /// + /// Documentation (partially culled from online information): + /// Thread Local Storage (a.k.a. TLS and Thread Specific Storage) is a + /// mechanism by which each thread in a multithreaded process allocates + /// storage for thread-specific data. In standard multithreaded programs, + /// data is shared among all threads of a given process, whereas thread + /// local storage is the mechanism for allocating per-thread data. + /// + /// The EA_THREAD_LOCAL specifier may be used alone, with the extern or + /// static specifiers, but with no other storage class specifier. + /// When used with extern or static, EA_THREAD_LOCAL must appear + /// immediately after the other storage class specifier. + /// + /// The EA_THREAD_LOCAL specifier may be applied to any global, file-scoped + /// static, function-scoped static, or static data member of a class. + /// It may not be applied to block-scoped automatic or non-static data member. + /// + /// When the address-of operator is applied to a thread-local variable, + /// it is evaluated at run-time and returns the address of the current + /// thread's instance of that variable. An address so obtained may be used + /// by any thread. When a thread terminates, any pointers to thread-local + /// variables in that thread become invalid. + /// + /// No static initialization may refer to the address of a thread-local variable. + /// In C++, if an initializer is present for a thread-local variable, + /// it must be a constant-expression, as defined in 5.19.2 of the ANSI/ISO C++ standard. + /// + /// Windows has special considerations for using thread local storage in a DLL. + /// + /// Example usage: + /// #if defined(EA_THREAD_LOCAL) + /// EA_THREAD_LOCAL int n = 0; // OK + /// extern EA_THREAD_LOCAL struct Data s; // OK + /// static EA_THREAD_LOCAL char* p; // OK + /// EA_THREAD_LOCAL int i = sizeof(i); // OK. + /// EA_THREAD_LOCAL std::string s("hello"); // Bad -- Can't be used for initialized objects. + /// EA_THREAD_LOCAL int Function(); // Bad -- Can't be used as return value. + /// void Function(){ EA_THREAD_LOCAL int i = 0; } // Bad -- Can't be used in function. + /// void Function(EA_THREAD_LOCAL int i){ } // Bad -- can't be used as argument. + /// extern int i; EA_THREAD_LOCAL int i; // Bad -- Declarations differ. + /// int EA_THREAD_LOCAL i; // Bad -- Can't be used as a type modifier. + /// EA_THREAD_LOCAL int i = i; // Bad -- Can't reference self before initialization. + /// #else + /// Need to use EA::Thread::ThreadLocalStorage. + /// #endif + + #if !EA_THREADS_AVAILABLE + #define EA_THREAD_LOCAL + + #elif !defined(EA_COMPILER_NO_THREAD_LOCAL) || EA_USE_CPP11_CONCURRENCY + #define EA_THREAD_LOCAL thread_local + + #elif defined(EA_COMPILER_MSVC) + // This appears to be supported by VC++, Borland C++. + // And it is supported by all compilers for the Windows platform. + #define EA_THREAD_LOCAL __declspec(thread) + + #else + // Else don't define it as anything. This will result in a compilation + // error reporting the problem. We cannot simply #define away the + // EA_THEAD_LOCAL term, as doing so would defeat the purpose of the + // specifier. Dynamic thread local storage is a more flexible and + // portable solution to the problem. + // #define EA_THREAD_LOCAL + #endif + ///////////////////////////////////////////////////////////////////////// + + } // namespace Thread + +} // namespace EA + + + + + +///////////////////////////////////////////////////////////////////////// +/// EAThreadLocalStorageData +/// +/// This is used internally by class ThreadLocalStorage. +/// Todo: Consider moving this declaration into a platform-specific +/// header file. +/// +#if defined(EA_PLATFORM_SONY) + #include + + struct EAThreadLocalStorageData{ + ScePthreadKey mKey; // This is usually a pointer. + int mResult; // Result of call to scePthreadKeyCreate, so we can know if mKey is valid. + }; +#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && !defined(EA_PLATFORM_NX) + // In this case we will be using pthread_key_create, pthread_key_delete, pthread_getspecific, pthread_setspecific. + #include + + struct EAThreadLocalStorageData{ + pthread_key_t mKey; // This is usually a pointer. + int mResult; // Result of call to pthread_key_create, so we can know if mKey is valid. + }; + +#elif defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_WINDOWS_PHONE) && !(defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) + // In this case we will be using TlsAlloc, TlsFree, TlsGetValue, TlsSetValue. + typedef uint32_t EAThreadLocalStorageData; + +#elif (!EA_THREADS_AVAILABLE || defined(EA_PLATFORM_CONSOLE)) && !defined(EA_PLATFORM_NX) + #include + + struct EAThreadLocalStorageData + { + struct ThreadToDataPair + { + EA::Thread::ThreadUniqueId mThreadID; + const void* mpData; + }; + #ifndef EA_TLS_MAX_COUNT + #define EA_TLS_MAX_COUNT 16 // This is the max number of threads that might want to use the given thread-local-storage item. + #endif + ThreadToDataPair* GetTLSEntry(bool bCreateIfNotFound); + ThreadToDataPair mDataArray[EA_TLS_MAX_COUNT]; + int mDataArrayCount; + }; + +#else // STL version which uses less memory but uses heap memory. + + // If you use this version, then you want to make sure your STL is using new/delete + // by default and then make sure you are globally mapping new/delete to your + // custom allocation system. STLPort, for example, tends to want to use its + // own internal allocator which is non-optimal for serious uses. + + EA_DISABLE_VC_WARNING(4574 4350) + #include // Note that this dependency on STL map is only present if you use this pathway, which is disabled by default. + EA_RESTORE_VC_WARNING() + + #include + #include + + struct EAThreadLocalStorageData + { + EAThreadLocalStorageData() : mThreadToDataMap(NULL) {} + ~EAThreadLocalStorageData() { delete mThreadToDataMap; mThreadToDataMap = NULL; } + void** GetTLSEntry(bool bCreateIfNotFound); + // We allocate this map only when needed + // This prevents too early allocations before our allocator initialization + std::map *mThreadToDataMap; + EA::Thread::Futex mFutex; + private: + // Disable copy and assignment + EAThreadLocalStorageData(const EAThreadLocalStorageData&); + EAThreadLocalStorageData operator=(const EAThreadLocalStorageData&); + }; +#endif +///////////////////////////////////////////////////////////////////////// + + + +namespace EA +{ + namespace Thread + { + ///////////////////////////////////////////////////////////////////////// + /// class ThreadLocalStorage + /// + /// This is a class that lets you store a pointer to data uniquely for + /// each thread. It thus allows access to a pointer as if it were local + /// but each thread gets its own copy. + /// + /// The implementation behind this class maps to the PThreads API under + /// Unix-like systems, maps to the Windows TLS SPI under Windows, and + /// maps to a custom implementation otherwise. The PThreads API has a + /// mechanism whereby you can set a callback to execute when a thread + /// exits; the callback will call the callback once for each pointer + /// that was stored in all thread local storage objects. Due to the + /// general weaknesses of the PThread mechanism and due to our interest + /// in being as lean as possible, we don't support automatic callbacks + /// such as with PThreads. The same effect can be achieved manually + /// when needed. + /// + /// Example usage: + /// ThreadLocalStorage tls; + /// void* pValue; + /// bool bResult; + /// + /// pValue = tls.GetValue(); // Return value will be NULL. + /// bResult = tls.SetValue(NULL); // This is fine and bResult should be true. + /// pValue = tls.GetValue(); // Return value will be NULL. + /// bResult = tls.SetValue(pSomeObject); // Set thread-specific value to pSomeObject. + /// bResult = tls.SetValue(pOtherObject); // Set thread-specific value to pOtherObject. + /// pValue = tls.GetValue(); // Return value will be pOtherObject. + /// bResult = tls.SetValue(NULL); // This is fine and bResult should be true. + /// + class EATHREADLIB_API ThreadLocalStorage + { + public: + ThreadLocalStorage(); + ~ThreadLocalStorage(); + + /// GetValue + /// Returns the pointer previous stored via GetValue or returns NULL if there + /// is not stored value or if the user stored NULL. + void* GetValue(); + + /// SetValue + /// Stores a pointer, returns true if the storage was possible. In general, + /// the only reason that false would ever be returned is if there wasn't + /// sufficient memory remaining for the operation. When a thread exits, + /// it should call SetValue(NULL), as there is currently no mechanism to + /// automatically detect thread exits on some platforms and thus there is + /// no way to automatically clear these values. + bool SetValue(const void* pData); + + /// GetPlatformData + /// Returns the platform-specific thread local storage handle for debugging + /// uses or other cases whereby special (and non-portable) uses are required. + void* GetPlatformData() + { return &mTLSData; } + + protected: + EAThreadLocalStorageData mTLSData; + + private: + // Disable copy and assignment + ThreadLocalStorage(const ThreadLocalStorage&); + ThreadLocalStorage operator=(const ThreadLocalStorage&); + }; + ///////////////////////////////////////////////////////////////////////// + + + + /// ThreadLocalStorageFactory + /// + /// Implements a factory-based creation and destruction mechanism for class ThreadLocalStorage. + /// A primary use of this would be to allow the ThreadLocalStorage implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API ThreadLocalStorageFactory + { + public: + static ThreadLocalStorage* CreateThreadLocalStorage(); // Internally implemented as: return new ThreadLocalStorage; + static void DestroyThreadLocalStorage(ThreadLocalStorage* pTLS); // Internally implemented as: delete pTLS; + + static size_t GetThreadLocalStorageSize(); // Internally implemented as: return sizeof(ThreadLocalStorage); + static ThreadLocalStorage* ConstructThreadLocalStorage(void* pMemory); // Internally implemented as: return new(pMemory) ThreadLocalStorage; + static void DestructThreadLocalStorage(ThreadLocalStorage* pTLS); // Internally implemented as: pTLS->~ThreadLocalStorage(); + }; + + + + // ThreadLocalPointer + // This is a class that adds pointer type awareness to ThreadLocalStorage. + // The interface is designed to look like the standard auto_ptr class. + // + // The following is disabled until we provide a way to enumerate and delete + // the pointers when the object goes out of scope or delete the thread-specific + // pointer when the thread ends. Both are require before this class fully acts + // as one would expect. + // + //template + //class ThreadLocalPointer + //{ + //public: + // T* get() const { return static_cast(mTLS.GetValue()); } + // T* operator->() const { return static_cast(mTLS.GetValue()); } + // T& operator*() const { return *static_cast(mTLS.GetValue()); } + // void reset(T* pNew = 0){ + // T* const pTemp = get(); + // if(pNew != pTemp){ + // delete pTemp; + // mTLS.SetValue(pTemp); + // } + // } + // + //protected: + // ThreadLocalStorage mTLS; + // + //private: + // ThreadLocalPointer(const ThreadLocalPointer&); + // const ThreadLocalPointer& operator=(const ThreadLocalPointer&); + //}; + ///////////////////////////////////////////////////////////////////////// + + + } // namespace Thread + +} // namespace EA + + +#endif // #ifdef EATHREAD_EATHREAD_STORAGE_H + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_sync.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_sync.h new file mode 100644 index 00000000..84b0ac1f --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_sync.h @@ -0,0 +1,270 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// Functionality related to memory and code generation synchronization. +// +// Overview (partially taken from Usenet) +// On all modern hardware, a store instruction does not necessarily result +// in an immediate write to main memory, or even to the (processor specific) +// cache. A store instruction simply places a write request in a request +// queue, and continues. (Future reads in the same processor will check if +// there is a write to the same address in this queue, and fetch it, rather +// than reading from memory. Reads from another processor, however, can't +// see this queue.) Generally, the ordering of requests in this queue is +// not guaranteed, although some hardware offers stricter guarantees. +// Thus, you must do something to ensure that the writes actually occur. +// This is called a write barrier, and generally takes the form of a special +// instruction. +// +// And of course, just because you have written the data to main memory +// doesn't mean that some other processor, executing a different thread, +// doesn't have a stale copy in its cache, and use that for a read. Before +// reading the variables, you need to ensure that the processor has the +// most recent copy in its cache. This is called a read barrier, and +// again, takes the form of a special hardware instruction. A number of +// architectures (e.g. Intel x86-32) still guarantee read consistency -- +// all of the processors "listen" on the main memory bus, and if there is +// a write, automatically purge the corresponding data from their cache. +// But not all. +// +// Note that if you are writing data within a operating system-level +// locked mutex, the lock and unlock of the mutex will synchronize memory +// for you, thus eliminating the need for you to execute read and/or write +// barriers. However, mutex locking and its associated thread stalling is +// a potentially inefficient operation when in some cases you could simply +// write the memory from one thread and read it from another without +// using mutexes around the data access. Some systems let you write memory +// from one thread and read it from another (without you using mutexes) +// without using memory barriers, but others (notably SMP) will not let you +// get away with this, even if you put a mutex around the write. In these +// cases you need read/write barriers. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_EATHREAD_SYNC_H +#define EATHREAD_EATHREAD_SYNC_H + + +// Note +// These functions are not placed in a C++ namespace but instead are standalone. +// The reason for this is that these are usually implemented as #defines of +// C or asm code or implemented as compiler intrinsics. We however document +// these functions here as if they are simply functions. The actual platform- +// specific declarations are in the appropriate platform-specific directory. + +#include +#include + +#if !EA_THREADS_AVAILABLE + // Do nothing. +#elif defined(EA_PROCESSOR_X86) + #include +#elif defined(EA_PROCESSOR_X86_64) + #include +#elif defined(EA_PROCESSOR_IA64) + #include +#elif defined(EA_PLATFORM_APPLE) + #include +#elif defined(EA_PROCESSOR_ARM) + #include +#endif + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +// EA_THREAD_DO_SPIN +// +// Provides a macro which maps to whatever processor idle functionality the given platform requires. +// +// Example usage: +// EA_THREAD_DO_SPIN(); +// +#ifndef EA_THREAD_DO_SPIN + #ifdef EA_THREAD_COOPERATIVE + #define EA_THREAD_DO_SPIN() ThreadSleep() + #else + #define EA_THREAD_DO_SPIN() EAProcessorPause() // We don't check for EA_TARGET_SMP here and instead sleep if not defined because you probably shouldn't be using a spinlock on a pre-emptive system unless it is a multi-processing system. + #endif +#endif + + + +// The above header files would define EA_THREAD_SYNC_IMPLEMENTED. +#if !defined(EA_THREAD_SYNC_IMPLEMENTED) + // Perhaps it should be considered too serious of an error to allow compilation + // to continue. If so, then we should enable the #error below. + // #error EA_THREAD_SYNC_IMPLEMENTED not defined. + + + /// EAProcessorPause + /// + /// \Declaration + /// void EAProcessorPause(); + /// + /// \Description + /// This statement causes the processor to efficiently (as much as possible) + /// execute a no-op (a.k.a nop or noop). These are particularly useful in + /// spin-wait loops. Without a proper pause, some processors suffer severe + /// performance penalties while executing spin-wait loops such as those in + /// simple spin locks. Many processors have specialized pause instructions + /// (e.g. Intel x86 P4 'pause' or 'asm rep nop') that can be taken advantage + /// of here. + /// + /// \Example + /// while (!flag) { + /// EAProcessorPause(); + /// } + #define EAProcessorPause() + + + + /// EAReadBarrier + /// + /// \Declaration + /// void EAReadBarrier(); + /// + /// \Description + /// A read barrier ensures that neither software nor hardware perform a memory + /// read prior to the read barrier and that recent writes to main memory are + /// immediately seen (and not using stale cached data) by the processor executing + /// the read barrier. This generally does not mean a (performance draining) + /// invalidation of the entire cache but does possibly mean invalidating any cache + /// that refers to main memory which has changed. Thus, there is a performance + /// cost but considering the use of this operation, this is the most efficient + /// way of achieving the effect. + /// + /// \Example + /// The following function will operate fine on some multiprocessing systems but + /// hang (possibly indefinitely) on other multiprocessing systems unless the + /// EAReadBarrier call is present. + /// + /// void ThreadFunction() { + /// extern volatile int gFlag; + /// while(gFlag == 0){ // Wait for separate thread to write to gSomeFlag. + /// EAProcessorPause(); + /// EAReadBarrier(); + /// // Do memory sharing operations with other threads here. + /// } + /// } + #define EAReadBarrier() + + + + + + /// EAWriteBarrier + /// + /// \Declaration + /// void EAWriteBarrier(); + /// + /// \Description + /// A write barrier ensures that neither software nor hardware delay a memory + /// write operation past the barrier. If you want your memory write committed + /// to main memory immediately, this statement will have that effect. As such, + /// this is something like a flush of the current processor's write cache. + /// Note that flushing memory from a processor's cache to main memory like this + /// doesn't cause a second processor to immediately see the changed values in + /// main memory, as the second processor has a read cache between it and main + /// memory. Thus, a second processor would need to execute a read barrier if it + /// wants to see the updates immediately. + #define EAWriteBarrier() + + + + + + /// EAReadWriteBarrier + /// + /// Declaration + /// void EAReadWriteBarrier(); + /// + /// Description + /// A read/write barrier has the same effect as both a read barrier and a write + /// barrier at once. A read barrier ensures that neither software nor hardware + /// perform a memory read prior to the read barrier, while a write barrier + /// ensures that neither software nor hardware delay a memory write operation + /// past the barrier. A ReadWriteBarrier specifically acts like a WriteBarrier + /// followed by a ReadBarrier, despite the name ReadWriteBarrier being the + /// other way around. + /// + /// EAReadWriteBarrier synchronizes both reads and writes to system memory + /// between processors and their caches on multiprocessor systems, particulary + /// SMP systems. This can be useful to ensure the state of global variables at + /// a particular point in your code for multithreaded applications. Higher level + /// thread synchronization level primitives such as mutexes achieve the same + /// effect (while providing the additional functionality of synchronizing code + /// execution) but at a significantly higher cost. + /// + /// A two-processor SMP system has two processors, each with its own instruction + /// and data caches. If the first processor writes to a memory location and the + /// second processor needs to read from that location, the first procesor's + /// write may still be in its cache and not committed to main memory and the + /// second processor may thus would not see the newly written value. The value + /// will eventually get written from the first cache to main memory, but if you + /// need to ensure that it is written at a particular time, you would use a + /// ReadWrite barrier. + /// + /// This function is similar to the Linux kernel rwb() function and to the + /// Windows kernel KeMemoryBarrier function. + #define EAReadWriteBarrier() + + + + + + /// EACompilerMemoryBarrier + /// + /// \Declaration + /// void EACompilerMemoryBarrier(); + /// + /// \Description + /// Provides a barrier for compiler optimization. The compiler will not make + /// assumptions about locations across an EACompilerMemoryBarrier statement. + /// For example, if a compiler has memory values temporarily cached in + /// registers but you need them to be written to memory, you can execute the + /// EACompilerMemoryBarrier statement. This is somewhat similar in concept to + /// the C volatile keyword except that it applies to all memory the compiler + /// is currently working with and applies its effect only where you specify + /// and not for every usage as with the volatile keyword. + /// + /// Under GCC, this statement is equivalent to the GCC `asm volatile("":::"memory")` + /// statement. Under VC++, this is equivalent to a _ReadWriteBarrier statement + /// (not to be confused with EAReadWriteBarrier above) and equivalent to the Windows + /// kernel function KeMemoryBarrierWithoutFence. This is also known as barrier() + /// undef Linux. + /// + /// EACompilerMemoryBarrier is a compiler-level statement and not a + /// processor-level statement. For processor-level memory barriers, + /// use EAReadBarrier, etc. + /// + /// \Example + /// Without the compiler memory barrier below, an optimizing compiler might + /// never assign 0 to gValue because gValue is reassigned to 1 later and + /// because gValue is not declared volatile. + /// + /// void ThreadFunction() { + /// extern int gValue; // Note that gValue is intentionally not declared volatile, + /// gValue = 0; + /// EACompilerMemoryBarrier(); + /// gValue = 1; + /// } + #define EACompilerMemoryBarrier() + + +#endif // EA_THREAD_SYNC_IMPLEMENTED + + +#endif // #ifdef EATHREAD_EATHREAD_SYNC_H + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_thread.h b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_thread.h new file mode 100644 index 00000000..ee07286d --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/eathread_thread.h @@ -0,0 +1,795 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EATHREAD_EATHREAD_THREAD_H +#define EATHREAD_EATHREAD_THREAD_H + +#include +#include +#include +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // Suppress warning about class 'AtomicInt32' needs to have a + // dll-interface to be used by clients of class which have a templated member. + // + // These templates cannot be instantiated outside of the DLL. If you try, a + // link error will result. This compiler warning is intended to notify users + // of this. + EA_DISABLE_VC_WARNING(4251) +#endif + + +///////////////////////////////////////////////////////////////////////// +/// ThreadData +/// +/// This is used internally by class Thread. +/// To consider: Move this declaration into a platform-specific +/// header file. +///////////////////////////////////////////////////////////////////////// + +#if !EA_THREADS_AVAILABLE + + struct EAThreadDynamicData + { + }; + + struct EAThreadData + { + EAThreadDynamicData* mpData; + }; + +#elif EA_USE_CPP11_CONCURRENCY + #include + #include + + EA_DISABLE_VC_WARNING(4062 4265 4365 4836 4571 4625 4626 4628 4193 4127 4548 4350) + #if EA_PLATFORM_WINDOWS + #include // workaround for compile errors in winrt. see http://connect.microsoft.com/VisualStudio/feedback/details/730564/ppl-in-winrt-projects-fail-to-compile + #endif + #include + #include + + struct EAThreadDynamicData + { + typedef void (*ThreadFunc)(EAThreadDynamicData* tdd, void* userFunc, void* userContext, void* userWrapperFunc); + EAThreadDynamicData(EA::Thread::ThreadUniqueId uniqueThreadId, const char* pThreadName); + EAThreadDynamicData(void* userFunc, void* userContext, void* userWrapperFunc, ThreadFunc threadFunc); + ~EAThreadDynamicData(); + + void AddRef(); + void Release(); + + EA::Thread::AtomicInt32 mnRefCount; + EA::Thread::AtomicInt32 mStatus; + intptr_t mReturnValue; + char mName[EATHREAD_NAME_SIZE]; + void* mpStackBase; + EA::Thread::ThreadAffinityMask mnThreadAffinityMask; + + EA::Thread::ThreadUniqueId mUniqueThreadId; + struct EAThreadComposite + { + EAThreadComposite() + : mReturnPromise() + , mReturnFuture(mReturnPromise.get_future()) + , mGetStatusFuture(mReturnFuture) + { + } + + std::promise mReturnPromise; + std::shared_future mReturnFuture; + std::shared_future mGetStatusFuture; + std::thread mThread; + } *mpComp; + + private: + // Disable copy and assignment + EAThreadDynamicData(const EAThreadDynamicData&); + EAThreadDynamicData operator=(const EAThreadDynamicData&); + }; + + struct EAThreadData + { + EAThreadDynamicData* mpData; + }; + + EA_RESTORE_VC_WARNING() + +// TODO: collapse the defines. +#elif defined(EA_PLATFORM_SONY) + #include + #include + #include + #include + + // Internal queue wrapper which is used to allow for a higher resolution sleep than what is provided by Sony's sleep functions + // as despite the names, sceKernelSleep, sceKernelUSleep and sceKernelNanosleep are all 1 ms resolution whereas this timer is 100 microseconds + struct EAThreadTimerQueue + { + EAThreadTimerQueue() + { + int result = sceKernelCreateEqueue(&mTimerEventQueue, "EAThread Timer Queue"); + mbEnabled = result == SCE_OK; + + // A timer queue will fail to be created when there are too many kernel objects open. It is a valid + // use-case for the Event Queue to fail being created as the ThreadSleep function implements a fallback. + // + // EAT_ASSERT_FORMATTED(mbEnabled, "Failed to initialize the EAThread Timer Queue (0x%x)", result); + } + + ~EAThreadTimerQueue() + { + if(mbEnabled) // only destroy the queue if it was created. + sceKernelDeleteEqueue(mTimerEventQueue); + + mbEnabled = false; + } + + SceKernelEqueue mTimerEventQueue; + EA::Thread::AtomicUint32 mCurrentId = 0; + bool mbEnabled = false; + }; + + struct EAThreadDynamicData + { + EAThreadDynamicData(); + ~EAThreadDynamicData(); + + void AddRef(); + void Release(); + + EA::Thread::ThreadId mThreadId; + EA::Thread::SysThreadId mSysThreadId; + pid_t mThreadPid; // For Linux this is the thread ID from gettid(). Otherwise it's the getpid() value. + volatile int mnStatus; + intptr_t mnReturnValue; + void* mpStartContext[2]; + void* mpBeginThreadUserWrapper; // User-specified BeginThread function wrapper or class wrapper + void* mpStackBase; + EA::Thread::AtomicInt32 mnRefCount; + char mName[EATHREAD_NAME_SIZE]; + int mStartupProcessor; // The thread affinity for the thread to set itself to after it starts. We need to do this because we currently have no way to set the affinity of another thread until after it has started. + EA::Thread::Mutex mRunMutex; // Locked while the thread is running. The reason for this mutex is that it allows timeouts to be specified in the WaitForEnd function. + EA::Thread::Semaphore mStartedSemaphore; // Signaled when the thread starts. This allows us to know in a thread-safe way when the thread has actually started executing. + EA::Thread::ThreadAffinityMask mnThreadAffinityMask; + EAThreadTimerQueue mThreadTimerQueue; // This queue allows for high resolution timer events to be submitted per thread allowing for better sleep resolution than Sony's provided sleep functions + }; + + + struct EAThreadData{ + EAThreadDynamicData* mpData; + }; + +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #include + + struct EAThreadDynamicData + { + EAThreadDynamicData(); + ~EAThreadDynamicData(); + + void AddRef(); + void Release(); + + EA::Thread::ThreadId mThreadId; + EA::Thread::SysThreadId mSysThreadId; + pid_t mThreadPid; // For Linux this is the thread ID from gettid(). Otherwise it's the getpid() value. + volatile int mnStatus; + intptr_t mnReturnValue; + void* mpStartContext[2]; + void* mpBeginThreadUserWrapper; // User-specified BeginThread function wrapper or class wrapper + void* mpStackBase; + EA::Thread::AtomicInt32 mnRefCount; + char mName[EATHREAD_NAME_SIZE]; + int mStartupProcessor; // DEPRECATED: The thread affinity for the thread to set itself to after it starts. We need to do this because we currently have no way to set the affinity of another thread until after it has started. + EA::Thread::ThreadAffinityMask mnThreadAffinityMask; // mStartupProcessor is deprecated in favor of using the the mnThreadAffinityMask and doesn't suffer from the limitations of only specifying the value at thread startup time. + EA::Thread::Mutex mRunMutex; // Locked while the thread is running. The reason for this mutex is that it allows timeouts to be specified in the WaitForEnd function. + EA::Thread::Semaphore mStartedSemaphore; // Signaled when the thread starts. This allows us to know in a thread-safe way when the thread has actually started executing. + }; + + + struct EAThreadData + { + EAThreadDynamicData* mpData; + }; + +#elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + + struct EAThreadDynamicData + { + EAThreadDynamicData(); + ~EAThreadDynamicData(); + void AddRef(); + void Release(); + + EA::Thread::ThreadId mhThread; + unsigned int mnThreadId; // EA::Thread::SysThreadId + int mnStatus; + EA::Thread::ThreadAffinityMask mnThreadAffinityMask; + intptr_t mnReturnValue; + void* mpStartContext[3]; + void* mpBeginThreadUserWrapper; // User-specified BeginThread function wrapper or class wrapper + void* mpStackBase; + EA::Thread::AtomicInt32 mnRefCount; + char mName[EATHREAD_NAME_SIZE]; + }; + + + struct EAThreadData + { + EAThreadDynamicData* mpData; + }; + +#endif + +namespace EA +{ +namespace Thread +{ + +struct EATHREADLIB_API ThreadEnumData +{ + ThreadEnumData(); + ~ThreadEnumData(); + + EAThreadDynamicData* mpThreadDynamicData; + void Release(); +}; + +} +} +///////////////////////////////////////////////////////////////////////// + + + + +namespace EA +{ + namespace Thread + { + /// FindThreadDynamicData + /// Utility functionality, not needed for most uses. + EATHREADLIB_API EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId); + EATHREADLIB_API EAThreadDynamicData* FindThreadDynamicData(SysThreadId threadId); + + /// EnumerateThreads + /// Enumerates known threads. For some platforms the returned thread list is limited + /// to the main thread and threads created by EAThread. + /// Returns the required count to enumerate all threads. + /// Fills in thread data up till the supplied capacity. + /// + /// Example usage: + /// ThreadEnumData enumData[32]; + /// size_t count = EA::Thread::EnumerateThreads(enumData, EAArrayCount(enumData)); + /// + /// for(size_t i = 0; i < count; i++) + /// { + /// printf("Thread id: %s\n", EAThreadIdToString(enumData[i].mpThreadDynamicData->mThreadId)); + /// enumData[i].Release(); + /// } + size_t EATHREADLIB_API EnumerateThreads(ThreadEnumData* pDataArray, size_t dataArrayCapacity); + + /// RunnableFunction + /// Defines the prototype of a standalone thread function. + /// The return value is of type intptr_t, which is a standard integral + /// data type that is large enough to hold an int or void*. + typedef intptr_t (*RunnableFunction)(void* pContext); + + /// IRunnable + /// Defines a class whose Run function executes in a separate thread. + /// An implementation of this interface can be run using a Thread class instance. + struct EATHREADLIB_API IRunnable + { + virtual ~IRunnable() { } + + /// \brief Task run entry point + /// The thread terminates when this method returns. + /// The return value is of type intptr_t, which is a standard integral + /// data type that is large enough to hold an int or void*. + virtual intptr_t Run(void* pContext = NULL) = 0; + }; + + /// RunnableFunctionUserWrapper + /// Defines the prototype of a user callback function when thread function is started. + /// \param pContext: thread start context void* passed in from thread Thread::Begin() + /// \param defaultRunnableFunction: default function Thread::Begin() normally would + /// call, user must call this function with passed in pContext. + /// + /// Here's an example: + /// \code + /// int ThreadFunction(void*) + /// { + /// printf("Throw NULL pointer Exception.\n"); + /// char* pTest = NULL; + /// *pTest = 1; + /// return 0; + /// } + /// + /// intptr_t MyThreadBeginWrapper(RunnableFunction defaultRunnableFunction, void* pContext) + /// { + /// // Do pre-start thread function stuff + /// try { + /// // must call defaultRunnableFunction to execute thread function, if don't then + /// // thread function will never gets executed. + /// intptr_t retValue = defaultRunnableFunction(pContext); + /// } + /// catch(...) { + /// printf("Exception detected.\n"); + /// } + /// + /// // do post-start thread function stuff + /// return retValue; + /// } + /// \endcode + /// + /// In your thread begin() function: + /// \code + /// ... + /// threadIds = threads.Begin(ThreadFunction, NULL, NULL, MyThreadBeginWrapper); + /// ... + /// \endcode + typedef intptr_t (*RunnableFunctionUserWrapper)(RunnableFunction defaultRunnableFunction, void* pContext); + + + /// RunnableClassUserWrapper + /// Defines the prototype of a user callback function when thread function is started. + /// \param pContext: thread start context void* passed in from thread Thread::Begin() + /// \param defaultRunnableFunction: default function Thread::Begin() normally would + /// call, user must call this function with passed in pContext. + /// + /// Here's an example: + /// \code + /// class MyThreadClass + /// { + /// virtual intptr_t Run(void* pContext = NULL) + /// { + /// printf("Throw NULL pointer Exception.\n"); + /// char* pTest = NULL; + /// *pTest = 1; + /// return 0; + /// } + /// } + /// + /// intptr_t MyThreadBeginWrapper(IRunnable defaultRunnableFunction, void* pContext) + /// { + /// // do pre-start thread function stuff + /// + /// // a good example is try catch block + /// try + /// { + /// // must call defaultRunnableFunction to execute thread function, if don't then + /// // thread function will never gets executed. + /// intptr_t retValue = defaultRunnableFunction->Run(pContext); + /// } + /// catch(...) + /// { + /// printf("Exception detected.\n"); + /// } + /// + /// // do post-start thread function stuff + /// return retValue; + /// } + /// \endcode + /// + /// In your thread begin() function: + /// + /// \code + /// ... + /// MyThreadClass myThreadClass = new MyThreadClass(); + /// threadIds = threads.Begin(&myThreadClass, NULL, NULL, MyThreadBeginWrapper); + /// ... + /// \endcode + typedef intptr_t (*RunnableClassUserWrapper)(IRunnable* defaultRunnableClass, void* pContext); + + + /// ThreadParameters + /// Used for specifying thread starting parameters. Note that we do not + /// include a 'start paused' parameter. The reason for this is that such + /// a thing is not portable and other mechanisms can achieve the same + /// effect. Thread pause/resume in general is considered bad practice. + struct EATHREADLIB_API ThreadParameters + { + void* mpStack; /// Pointer to stack memory. This would be the low address of the memory. A NULL value means to create a default stack. Default is NULL. Note that some platforms (such as Windows) don't support a user-supplied stack. + size_t mnStackSize; /// Size of the stack memory. Default is variable, depending on the platform. + int mnPriority; /// Value in the range of [kThreadPriorityMin, kThreadPriorityMax]. Default is kThreadPriorityDefault. + int mnProcessor; /// 0-based index of which processor to run the thread on. A value of -1 means to use default. Default is -1. See SetThreadProcessor for caveats regarding this value. + const char* mpName; /// A name to give to the thread. Useful for identifying threads in a descriptive way. + EA::Thread::ThreadAffinityMask mnAffinityMask; /// A bitmask representing the cores that the thread is allowed to run on. NOTE: This affinity mask is only applied when mnProcessor is set to kProcessorAny. + bool mbDisablePriorityBoost; /// Whether the system should override the default behavior of boosting the thread priority as they come out of a wait state (currently only supported on Windows). + + ThreadParameters(); + }; + + + + /// Thread + /// + /// Note that we do not provide thread suspend and resume functions. + /// The reason for this is that such things are inherently unsafe as + /// you usually cannot know where the thread is executing when the + /// suspension occurs. The safe alternative is to use signal or + /// semaphore primitives to achieve the same thing in a safe way. + /// + /// For performance reasons, the thread creation functions of this + /// class are themselves not thread-safe. Thus if you want to call + /// the Begin functions for an instance of this class from multiple + /// threads, you will need to synchronize access to the begin + /// functions yourself. + class EATHREADLIB_API Thread + { + public: + enum Status + { + kStatusNone, /// The thread has neither started nor ended. + kStatusRunning, /// The thread has started but not ended. + kStatusEnded /// The thread has both started and ended. + }; + + /// Thread + /// \brief Thread constructor. + Thread(); + + /// Thread + /// \brief Thread copy constructor. + Thread(const Thread& t); + + /// Thread + /// \brief Thread destructor. The destructor does not take any + /// action on the thread associated with it. Any threads created + /// by this class will continue to run and exit normally after + /// this destructor has executed. + ~Thread(); + + /// operator= + /// \brief Thread assignment operator. + Thread& operator=(const Thread& t); + + /// \brief Return global RunnableFunctionUserWrapper set by user. + /// \return function pointer to RunnableFunctionUserWrapper function user + /// set, if NULL, nothing is set. + /// \sa RunnableFunctionUserWrapper + static RunnableFunctionUserWrapper GetGlobalRunnableFunctionUserWrapper(); + + /// \brief Set global RunnableFunctionUserWrapper. This can only be + /// set once in the application life time. + /// \param pUserWrapper user specified wrapper function pointer. + /// \sa RunnableFunctionUserWrapper + static void SetGlobalRunnableFunctionUserWrapper(RunnableFunctionUserWrapper pUserWrapper); + + /// \brief Return global RunnableClassUserWrapper set by user. + /// \return function pointer to RunnableClassUserWrapper function user + /// set, if NULL, nothing is set. + /// \sa RunnableClassUserWrapper + static RunnableClassUserWrapper GetGlobalRunnableClassUserWrapper(); + + /// \brief Set global RunnableClassUserWrapper. This can only be + /// set once in the application life time. + /// \sa RunnableClassUserWrapper + static void SetGlobalRunnableClassUserWrapper(RunnableClassUserWrapper pUserWrapper); + + /// Begin + /// \brief Starts a thread via a RunnableFunction. + /// Returns the thread id of the newly running thread. + /// The pContext argument is passed to the RunnableFunction and serves + /// to allow the caller to pass information to the thread. + /// The pThreadParameters argument allows the caller to specify additional + /// information about how to start the thread. If this parameter is NULL, + /// then default settings will be chosen. + /// The Begin function itself is not thread-safe. While this Thread class + /// can be used to Begin multiple threads, the Begin function itself cannot + /// safely be executed by multiple threads at a time. This is by design and + /// allows for a simpler more efficient library. + /// User can have their own RunnableFunction wrapper by specifying one in + /// pUserWrapper. When pUserWrapper is used, pUserWrapper will get called + /// first, then pUserWrapper function can do whatever is desired before the + /// just-created thread's entry point is called. + /// \sa RunnableFunctionUserWrapper + ThreadId Begin(RunnableFunction pFunction, void* pContext = NULL, const ThreadParameters* pThreadParameters = NULL, RunnableFunctionUserWrapper pUserWrapper = GetGlobalRunnableFunctionUserWrapper()); + + /// Begin + /// Starts a thread via an object of the IRunnable interface. + /// Returns the thread id of the newly running thread. + /// The pContext argument is passed to the RunnableFunction and serves + /// to allow the caller to pass information to the thread. + /// The pThreadParameters argument allows the caller to specify additional + /// information about how to start the thread. If this parameter is NULL, + /// then default settings will be chosen. + /// The Begin function itself is not thread-safe. While this Thread class + /// can be used to Begin multiple threads, the Begin function itself cannot + /// safely be executed by multiple threads at a time. This is by design and + /// allows for a simpler more efficient library. + /// User can have their own RunnableClass wrapper by specifying one pUserWrapper. + /// When pUserWrapper is used, pUserWrapper will get called first, then + /// pUserWrapper function can do whatever is desired before the just-created + /// thread's entry point is called. + /// \sa RunnableClassUserWrapper + ThreadId Begin(IRunnable* pRunnable, void* pContext = NULL, const ThreadParameters* pThreadParameters = NULL, RunnableClassUserWrapper pUserWrapper = GetGlobalRunnableClassUserWrapper()); + + /// WaitForEnd + /// Waits for the thread associated with an object of this class + /// to end. Returns one of enum Status to indicate the status upon + /// return of this call. + /// This function is similar to the Posix pthread_join function and + /// the Windows WaitForSingleObject function. + /// If input pThreadReturnValue is non-NULL, it will be filled in with + /// the return value of the thread. + /// This function must be called only by a single thread at a time. + /// The resulting behaviour is undefined if multiple threads call this function. + /// + /// Note that the timeout is specified in absolute time and not relative time. + /// + /// Note also that due to the way thread scheduling works -- particularly in a + /// time-sliced threading environment -- that the timeout value is a hint and + /// the actual amount of time passed before the timeout occurs may be significantly + /// more or less than the specified timeout time. + /// + Status WaitForEnd(const ThreadTime& timeoutAbsolute = kTimeoutNone, intptr_t* pThreadReturnValue = NULL); + + /// GetStatus + /// Returns one of enum GetStatus. Note that in the most general sense + /// the running status may change if the thread quit right after + /// this call was made. But this function is useful if you know that + /// a function was running and you want to poll for its status while + /// waiting for it to exit. + /// If input pThreadReturnValue is non-NULL, it will be filled in with + /// the return value of the thread if the Status is kStatusEnded. + /// If the Status is not kStatusEnded, pThreadReturnValue will be ignored. + Status GetStatus(intptr_t* pThreadReturnValue = NULL) const; + + /// GetId + /// Gets the Id of the thread associated with an object of this class. + /// This Id is unique throughout the system. This function returns a + /// value that under Posix threads would be synonymous with pthread_t + /// and under Windows would be synonymous with a thread HANDLE (and not + /// a Windows thread id). + ThreadId GetId() const; + + /// GetPriority + /// Gets the priority of the thread. Return kThreadPriorityUnknown if + /// the thread associated with this class isn't running. If a thread + /// wants to get its own priority, it can use this class member or it + /// can simply use the global SetThreadPriority function and not need + /// an instance of this class. If you want to manipulate the thread + /// priority via the native platform interface, you can use GetId to + /// get the platform-specific identifier and use that value with native APIs. + /// + /// This function can return any int except for kThreadPriorityUnknown, as the + /// current thread's priority will always be knowable. A return value of kThreadPriorityDefault + /// means that this thread is of normal (a.k.a. default) priority. + /// See the documentation for thread priority constants (e.g. kThreadPriorityDefault) + /// for more information about thread priority values and behaviour. + int GetPriority() const; + + /// SetPriority + /// Sets the priority of the thread. Returns false if the thread associated + /// with this class isn't running. If a thread wants to set its own priority, + /// it can use this class member or it can simply use the global SetThreadPriority + /// function and not need an instance of this class. If you want to manipulate + /// the thread priority via the native platform interface, you can use GetId to + /// get the platform-specific identifier and use that value with native APIs. + /// + /// Accepts any integer priority value except kThreadPriorityUnknown. + /// On some platforms, this function will automatically convert any invalid + /// priority for that particular platform to a valid one. A normal (a.k.a. default) thread + /// priority is identified by kThreadPriorityDefault. + /// + /// You can set the priority of a Thread object only if it has already begun. + /// You can also set the priority with the Begin function via the ThreadParameters + /// argument to Begin. This design is so in order to simply the implementation, + /// but being able to set ThreadParameters before Begin is something that can + /// be considered in the future. + bool SetPriority(int priority); + + /// SetProcessor + /// Sets the processor the given thread should run on. Valid values + /// are kThreadProcessorDefault, kThreadProcessorAny, or a processor + /// index in the range of [0, processor count). If the input value + /// is >= the processor count, it will be reduced to be a modulo of + /// the processor count. Any other invalid value will cause the processor + /// to be set to zero. + /// + /// For some platforms you can set the processor of a Thread object only if it + /// has already begun. + /// + /// You can also set the processor with the Begin function via the ThreadParameters + /// argument to Begin. This design is so in order to simply the implementation, + /// but being able to set ThreadParameters before Begin is something that can + /// be considered in the future. This is the most reliable way to set the thread + /// processor, as it works on all platforms. + void SetProcessor(int nProcessor); + + /// Wake + /// Wakes up a sleeping thread if it is sleeping. This necessarily can only + /// be called from a thread other than the sleeping thread. You must be careful + /// to not rely on this function as a synchronization primitive. For example, + /// in the general case you cannot be sure that after calling Wake that the + /// thread will be awake, as it is possible that right after you called Wake + /// the thread immediately went back to sleep before you could do anything. + /// Nevertheless, this function is useful in waking up a thread from a + /// (potentially long) sleep so that it can examine data, lock a synchronization + /// primitive, or simply exit. + /// + /// Note that this class has no member Sleep function. The reason is that a + /// thread can only put itself to sleep and cannot put other threads to sleep. + /// The thread should use the static Sleep function to put itself to sleep. + void Wake(); + + /// GetName + /// Returns the name of the thread assigned by the SetName function. + /// If the thread was not named by the SetName function, then the name is empty (""). + const char* GetName() const; + + /// SetName + /// Sets a descriptive name or the thread. On some platforms this name is passed + /// on to the debugging tools so they can see this name. The name length, including + /// a terminating 0 char, is limited to EATHREAD_NAME_SIZE characters. Any characters + /// beyond that are ignored. + /// + /// You can set the name of a Thread object only if it has already begun. + /// You can also set the name with the Begin function via the ThreadParameters + /// argument to Begin. This design is so in order to simply the implementation, + /// but being able to set ThreadParameters before Begin is something that can + /// be considered in the future. + /// + /// Some platforms (e.g. Linux) have the restriction this function works propertly only + /// when called by the same thread that you want to name. Given this situation, + /// the most portable way to use this SetName function is to either always call + /// it from the thread to be named or to use the ThreadParameters to give the + /// thread a name before it is started and let the started thread name itself. + void SetName(const char* pName); + + /// SetAffinityMask + /// Sets an affinity mask for the thread. On some platforms, this OS feature is + /// not supported. In this situation, you are at the mercy of the OS thread scheduler. + /// + /// Example(s): + /// "00000100" -> thread is pinned to processor 2 + /// "01010100" -> thread is pinned to processor 2, 4, and 6. + void SetAffinityMask(ThreadAffinityMask mnAffinityMask); + + /// GetAffinityMask + /// Returns the affinity mask for this specific thread. + ThreadAffinityMask GetAffinityMask(); + + /// SetDefaultProcessor + /// Sets the default processor to create threads with. To specify the processor + /// for a running thread, use SetProcessor() or specify the processor in the + /// thread creation ThreadParameters. + /// + /// If nProcessor is set to kProcessorAny, EAThread will automatically determine + /// which processor to launch threads to. + /// + /// Please refer to SetProcessor for valid values for the nProcessor argument. + static void SetDefaultProcessor(int nProcessor) + { sDefaultProcessor = nProcessor; } + + + /// GetDefaultProcessor + /// Gets the default processor to create threads with. + static int GetDefaultProcessor() + { return sDefaultProcessor; } + + + /// SetDefaultProcessorMask + /// Sets which processors created threads should be explicitly run on. + /// The default value is 0xffffffffffffffff. + /// Each bit refers to the associated processor. A mask of 0xffffffffffffffff + /// means to allow running on any processor, and on desktop platforms such + /// as Windows it means that the OS decides what processor to use on its own. + /// Not all platforms support this functionality, even if multiple processors are present. + static void SetDefaultProcessorMask(uint64_t mask) + { sDefaultProcessorMask.SetValue(mask); } + + + /// GetDefaultProcessorMask + /// Returns the mask set by SetDefaultProcessorMask. + static uint64_t GetDefaultProcessorMask() + { return sDefaultProcessorMask.GetValue(); } + + + /// GetPlatformData + /// Returns platform-specific data for this thread for debugging uses or + /// other cases whereby special (and non-portable) uses are required. + /// The value returned is a struct of type EAThreadData. + void* GetPlatformData() + { return &mThreadData; } + + protected: + static RunnableFunctionUserWrapper sGlobalRunnableFunctionUserWrapper; + static RunnableClassUserWrapper sGlobalRunnableClassUserWrapper; + static EA::Thread::AtomicInt32 sDefaultProcessor; + static EA::Thread::AtomicUint64 sDefaultProcessorMask; + EAThreadData mThreadData; + }; + + + /// ThreadFactory + /// + /// Implements a factory-based creation and destruction mechanism for class Thread. + /// A primary use of this would be to allow the Thread implementation to reside in + /// a private library while users of the class interact only with the interface + /// header and the factory. The factory provides conventional create/destroy + /// semantics which use global operator new, but also provides manual construction/ + /// destruction semantics so that the user can provide for memory allocation + /// and deallocation. + class EATHREADLIB_API ThreadFactory + { + public: + static Thread* CreateThread(); // Internally implemented as: return new Thread; + static void DestroyThread(Thread* pThread); // Internally implemented as: delete pThread; + + static size_t GetThreadSize(); // Internally implemented as: return sizeof(Thread); + static Thread* ConstructThread(void* pMemory); // Internally implemented as: return new(pMemory) Thread; + static void DestructThread(Thread* pThread); // Internally implemented as: pThread->~Thread(); + }; + + + /// MakeThread + /// + /// Simplify creating threads with lambdas + /// + template + auto MakeThread(F&& f, const EA::Thread::ThreadParameters& params = EA::Thread::ThreadParameters()) + { + typedef std::decay_t decayed_f_t; + + auto get_memory = [] + { + const auto sz = sizeof(decayed_f_t); + auto* pAllocator = EA::Thread::GetAllocator(); + + if(pAllocator) + return pAllocator->Alloc(sz); + else + return malloc(sz); + }; + + auto thread_enty = [](void* pMemory) -> intptr_t + { + auto free_memory = [](void* p) + { + auto* pAllocator = EA::Thread::GetAllocator(); + if(pAllocator) + return pAllocator->Free(p); + else + return free(p); + }; + + auto* pF = reinterpret_cast(pMemory); + (*pF)(); + pF->~decayed_f_t(); + free_memory(pF); + return 0; + }; + + EA::Thread::Thread thread; + thread.Begin(thread_enty, new(get_memory()) decayed_f_t(std::forward(f)), ¶ms); // deleted in the thread entry function + return thread; + } + + } // namespace Thread + +} // namespace EA + + +#if defined(EA_DLL) && defined(EA_COMPILER_MSVC) + // re-enable warning 4251 (it's a level-1 warning and should not be suppressed globally) + EA_RESTORE_VC_WARNING() +#endif + + +#endif // EATHREAD_EATHREAD_THREAD_H diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/internal/config.h b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/config.h new file mode 100644 index 00000000..d4701fd7 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/config.h @@ -0,0 +1,631 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_INTERNAL_CONFIG_H +#define EATHREAD_INTERNAL_CONFIG_H + + +#include + +EA_DISABLE_VC_WARNING(4574) +#include +EA_RESTORE_VC_WARNING() + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_VERSION +// +// We more or less follow the conventional EA packaging approach to versioning +// here. A primary distinction here is that minor versions are defined as two +// digit entities (e.g. .03") instead of minimal digit entities ".3"). The logic +// here is that the value is a counter and not a floating point fraction. +// Note that the major version doesn't have leading zeros. +// +// Example version strings: +// "0.91.00" // Major version 0, minor version 91, patch version 0. +// "1.00.00" // Major version 1, minor and patch version 0. +// "3.10.02" // Major version 3, minor version 10, patch version 02. +// "12.03.01" // Major version 12, minor version 03, patch version +// +// Example usage: +// printf("EATHREAD_VERSION version: %s", EATHREAD_VERSION); +// printf("EATHREAD_VERSION version: %d.%d.%d", EATHREAD_VERSION_N / 10000 % 100, EATHREAD_VERSION_N / 100 % 100, EATHREAD_VERSION_N % 100); +// +#ifndef EATHREAD_VERSION + #define EATHREAD_VERSION "1.33.03" + #define EATHREAD_VERSION_N 13303 + + // Older style version info + #define EATHREAD_VERSION_MAJOR (EATHREAD_VERSION_N / 100 / 100 % 100) + #define EATHREAD_VERSION_MINOR (EATHREAD_VERSION_N / 100 % 100) + #define EATHREAD_VERSION_PATCH (EATHREAD_VERSION_N % 100) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// _GNU_SOURCE +// +// Defined or not defined. +// If this is defined then GlibC extension functionality is enabled during +// calls to glibc header files. +// +#if !defined(_GNU_SOURCE) + #define _GNU_SOURCE +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_TLS_COUNT +// +// Defined as compile-time constant integer > 0. +// +#if !defined(EATHREAD_TLS_COUNT) + #define EATHREAD_TLS_COUNT 16 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EA_THREADS_AVAILABLE +// +// Defined as 0 or 1 +// Defines if threading is supported on the given platform. +// If 0 then the EAThread implementation is not capable of creating threads, +// but other facilities (e.g. mutex) work in a non-thread-aware way. +// +#ifndef EA_THREADS_AVAILABLE + #define EA_THREADS_AVAILABLE 1 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EA_USE_CPP11_CONCURRENCY +// +// Defined as 0 or 1 +// +#ifndef EA_USE_CPP11_CONCURRENCY + #if defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + #define EA_USE_CPP11_CONCURRENCY 1 + #else + #define EA_USE_CPP11_CONCURRENCY 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EA_USE_COMMON_ATOMICINT_IMPLEMENTATION +// +// Use the common EAThread AtomicInt implementation on all platforms. +// +// Defined as 0 or 1 +// +#ifndef EA_USE_COMMON_ATOMICINT_IMPLEMENTATION + #define EA_USE_COMMON_ATOMICINT_IMPLEMENTATION 1 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EA_POSIX_THREADS_AVAILABLE +// +// Defined as 0 or 1 +// +#ifndef EA_POSIX_THREADS_AVAILABLE + #if defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_APPLE) + #define EA_POSIX_THREADS_AVAILABLE 1 + #elif defined(EA_PLATFORM_SONY) + #define EA_POSIX_THREADS_AVAILABLE 0 // POSIX threading API is present but use is discouraged by Sony. They want shipping code to use their scePthreads* API. + #else + #define EA_POSIX_THREADS_AVAILABLE 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EAT_ASSERT_ENABLED +// +// Defined as 0 or 1, default is 1 if EA_DEBUG or _DEBUG is defined. +// If defined as 1, then assertion failures are reported via EA::Thread::AssertionFailure(). +// +#ifndef EAT_ASSERT_ENABLED + #if defined(EA_DEBUG) || defined(_DEBUG) + #define EAT_ASSERT_ENABLED 1 + #else + #define EAT_ASSERT_ENABLED 0 + #endif +#endif + + + +#if EAT_ASSERT_ENABLED + #define EAT_ASSERT(expression) \ + EA_DISABLE_VC_WARNING(4127) \ + do { \ + EA_ANALYSIS_ASSUME(expression); \ + if (!(expression) ) \ + EA::Thread::AssertionFailure(__FILE__ "(" EA_STRINGIFY(__LINE__) "): " #expression); \ + } while(0) \ + EA_RESTORE_VC_WARNING() +#else + #define EAT_ASSERT(expression) +#endif + +#if EAT_ASSERT_ENABLED + #define EAT_ASSERT_MSG(expression, msg) \ + EA_DISABLE_VC_WARNING(4127) \ + do { \ + EA_ANALYSIS_ASSUME(expression); \ + if (!(expression) ) \ + EA::Thread::AssertionFailure(msg); \ + } while(0) \ + EA_RESTORE_VC_WARNING() +#else + #define EAT_ASSERT_MSG(expression, msg) +#endif + +#if EAT_ASSERT_ENABLED + #define EAT_ASSERT_FORMATTED(expression, pFormat, ...) \ + EA_DISABLE_VC_WARNING(4127) \ + do { \ + EA_ANALYSIS_ASSUME(expression); \ + if (!(expression) ) \ + EA::Thread::AssertionFailureV(pFormat, __VA_ARGS__); \ + } while(0) \ + EA_RESTORE_VC_WARNING() +#else + #define EAT_ASSERT_FORMATTED(expression, pFormat, ...) +#endif + +#if EAT_ASSERT_ENABLED + #define EAT_FAIL_MSG(msg) (EA::Thread::AssertionFailure(msg)) +#else + #define EAT_FAIL_MSG(msg) +#endif + +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// EAT_COMPILETIME_ASSERT +// +// Compile-time assertion for this module. +// C-like declaration: +// void EAT_COMPILETIME_ASSERT(bool bExpression); +// +#if !defined(EAT_COMPILETIME_ASSERT) + #define EAT_COMPILETIME_ASSERT(expression) static_assert(expression, EA_STRINGIFY(expression)) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_TLSALLOC_DTOR_ENABLED +// +// Defined as 0 or 1. Default is 1. +// Defines if the TLSAlloc class destructor frees the TLS thread handle. +// This won't make a difference unless you were using EAThread in a DLL and +// you were repeatedly loading and unloading DLLs. +// See eathread_pc.cpp for usage of this and more info about the situation. +// +#ifndef EATHREAD_TLSALLOC_DTOR_ENABLED + #define EATHREAD_TLSALLOC_DTOR_ENABLED 1 +#endif +/////////////////////////////////////////////////////////////////////////////// + + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_LIKELY / EATHREAD_UNLIKELY +// +// Defined as a macro which gives a hint to the compiler for branch +// prediction. GCC gives you the ability to manually give a hint to +// the compiler about the result of a comparison, though it's often +// best to compile shipping code with profiling feedback under both +// GCC (-fprofile-arcs) and VC++ (/LTCG:PGO, etc.). However, there +// are times when you feel very sure that a boolean expression will +// usually evaluate to either true or false and can help the compiler +// by using an explicity directive... +// +// Example usage: +// if(EATHREAD_LIKELY(a == 0)) // Tell the compiler that a will usually equal 0. +// { ... } +// +// Example usage: +// if(EATHREAD_UNLIKELY(a == 0)) // Tell the compiler that a will usually not equal 0. +// { ... } +// +#ifndef EATHREAD_LIKELY + #define EATHREAD_LIKELY(x) EA_LIKELY(x) + #define EATHREAD_UNLIKELY(x) EA_UNLIKELY(x) +#endif +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_NAMING +// +// Defined as 0, 1 (enabled), or 2 (enabled only when debugger is present). +// +#define EATHREAD_NAMING_DISABLED 0 +#define EATHREAD_NAMING_ENABLED 1 +#define EATHREAD_NAMING_OPTIONAL 2 + +#ifndef EATHREAD_NAMING + #if defined(EA_SHIP) || defined(EA_FINAL) // These are two de-facto standard EA defines for identifying a shipping build. + #define EATHREAD_NAMING 0 + #else + #define EATHREAD_NAMING EATHREAD_NAMING_ENABLED // or EATHREAD_NAMING_OPTIONAL? + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_NAME_SIZE +// +// Specifies the max size to support for naming threads. +// This value can be changed as desired. +// +#ifndef EATHREAD_NAME_SIZE + #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_UNIX) + #define EATHREAD_NAME_SIZE 64 + #else + #define EATHREAD_NAME_SIZE 32 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EA_XBDM_ENABLED +// +// Defined as 0 or 1, with 1 being the default for debug builds. +// This controls whether xbdm library usage is enabled on XBox 360. This library +// allows for runtime debug functionality. But shipping applications are not +// allowed to use xbdm. +// +#if !defined(EA_XBDM_ENABLED) + #if defined(EA_DEBUG) + #define EA_XBDM_ENABLED 1 + #else + #define EA_XBDM_ENABLED 0 + #endif +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_DLL +// +// Defined as 0 or 1. The default is dependent on the definition of EA_DLL. +// If EA_DLL is defined, then EATHREAD_DLL is 1, else EATHREAD_DLL is 0. +// EA_DLL is a define that controls DLL builds within the EAConfig build system. +// EATHREAD_DLL controls whether EATHREAD_VERSION is built and used as a DLL. +// Normally you wouldn't do such a thing, but there are use cases for such +// a thing, particularly in the case of embedding C++ into C# applications. +// +#ifndef EATHREAD_DLL + #if defined(EA_DLL) + #define EATHREAD_DLL 1 + #else + #define EATHREAD_DLL 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREADLIB_API +// +// This is used to label functions as DLL exports under Microsoft platforms. +// If EA_DLL is defined, then the user is building EAThread as a DLL and EAThread's +// non-templated functions will be exported. EAThread template functions are not +// labelled as EATHREADLIB_API (and are thus not exported in a DLL build). This is +// because it's not possible (or at least unsafe) to implement inline templated +// functions in a DLL. +// +// Example usage of EATHREADLIB_API: +// EATHREADLIB_API int someVariable = 10; // Export someVariable in a DLL build. +// +// struct EATHREADLIB_API SomeClass{ // Export SomeClass and its member functions in a DLL build. +// EATHREADLIB_LOCAL void PrivateMethod(); // Not exported. +// }; +// +// EATHREADLIB_API void SomeFunction(); // Export SomeFunction in a DLL build. +// +// For GCC, see http://gcc.gnu.org/wiki/Visibility +// +#ifndef EATHREADLIB_API // If the build file hasn't already defined this to be dllexport... + #if EATHREAD_DLL + #if defined(EA_COMPILER_MSVC) || defined(EA_PLATFORM_MINGW) + #define EATHREADLIB_API __declspec(dllimport) + #define EATHREADLIB_LOCAL + #elif defined(EA_PLATFORM_CYGWIN) + #define EATHREADLIB_API __attribute__((dllimport)) + #define EATHREADLIB_LOCAL + #elif (defined(__GNUC__) && (__GNUC__ >= 4)) // GCC AND Clang + #define EATHREADLIB_API __attribute__ ((visibility("default"))) + #define EATHREADLIB_LOCAL __attribute__ ((visibility("hidden"))) + #else + #define EATHREADLIB_API + #define EATHREADLIB_LOCAL + #endif + #else + #define EATHREADLIB_API + #define EATHREADLIB_LOCAL + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_ALLOC_PREFIX +// +// Defined as a string literal. Defaults to this package's name. +// Can be overridden by the user by predefining it or by editing this file. +// This define is used as the default name used by this package for naming +// memory allocations and memory allocators. +// +// All allocations names follow the same naming pattern: +// /[/] +// +// Example usage: +// void* p = pCoreAllocator->Alloc(37, EATHREAD_ALLOC_PREFIX, 0); +// +// Example usage: +// gMessageServer.GetMessageQueue().get_allocator().set_name(EATHREAD_ALLOC_PREFIX "MessageSystem/Queue"); +// +#ifndef EATHREAD_ALLOC_PREFIX + #define EATHREAD_ALLOC_PREFIX "EAThread/" +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_USE_STANDARD_NEW +// +// Defines whether we use the basic standard operator new or the named +// extended version of operator new, as per the EASTL package. +// +#ifndef EATHREAD_USE_STANDARD_NEW + #if EATHREAD_DLL // A DLL must provide its own implementation of new, so we just use built-in new. + #define EATHREAD_USE_STANDARD_NEW 1 + #else + #define EATHREAD_USE_STANDARD_NEW 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_NEW +// +// This is merely a wrapper for operator new which can be overridden and +// which has debug/release forms. +// +// Example usage: +// SomeClass* pObject = EATHREAD_NEW("SomeClass") SomeClass(1, 2, 3); +// +#ifndef EATHREAD_NEW + #if EATHREAD_USE_STANDARD_NEW + #define EATHREAD_NEW(name) new + #define EATHREAD_NEW_ALIGNED(alignment, offset, name) new + #define EATHREAD_DELETE delete + #else + #if defined(EA_DEBUG) + #define EATHREAD_NEW(name) new(name, 0, 0, __FILE__, __LINE__) + #define EATHREAD_NEW_ALIGNED(alignment, offset, name) new(alignment, offset, name, 0, 0, __FILE__, __LINE__) + #define EATHREAD_DELETE delete + #else + #define EATHREAD_NEW(name) new(name, 0, 0, 0, 0) + #define EATHREAD_NEW_ALIGNED(alignment, offset, name) new(alignment, offset, name, 0, 0, 0, 0) + #define EATHREAD_DELETE delete + #endif + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_HAS_EMULATED_AND_NATIVE_ATOMICS +// +// This symbol is defined if a platform has both native and emulated atomics. +// Currently the only platform that requires this is iOS as earlier versions +// of the operating system (ie: iOS 3) do not provide OS support for 64-bit +// atomics while later versions (ie: iOS 4/5) do. +#ifndef EATHREAD_HAS_EMULATED_AND_NATIVE_ATOMICS + #if defined(EA_PLATFORM_APPLE) + #define EATHREAD_HAS_EMULATED_AND_NATIVE_ATOMICS 1 + #else + #define EATHREAD_HAS_EMULATED_AND_NATIVE_ATOMICS 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_GLIBC_BACKTRACE_AVAILABLE +// +// You generally need to be using GCC, GLIBC, and Linux for backtrace to be available. +// And even then it's available only some of the time. +// +#if !defined(EATHREAD_GLIBC_BACKTRACE_AVAILABLE) + #if (defined(EA_COMPILER_CLANG) || defined(EA_COMPILER_GNUC)) && (defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_APPLE)) && !defined(EA_PLATFORM_CYGWIN) && !defined(EA_PLATFORM_ANDROID) + #define EATHREAD_GLIBC_BACKTRACE_AVAILABLE 1 + #else + #define EATHREAD_GLIBC_BACKTRACE_AVAILABLE 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_GLIBC_VERSION +// +// We provide our own GLIBC numeric version to determine when system library +// calls are available. +// +#if defined(__GLIBC__) + #define EATHREAD_GLIBC_VERSION ((__GLIBC__ * 1000) + __GLIBC_MINOR__) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_GETCALLSTACK_SUPPORTED +// +// Defined as 0 or 1. +// Identifies whether runtime callstack unwinding (i.e. GetCallstack()) is +// supported for the given platform. In some cases it may be that unwinding +// support code is present but it hasn't been tested for reliability and may +// have bugs preventing it from working properly. In some cases (e.g. x86) +// it may be that optimized builds make it difficult to read the callstack +// reliably, despite that we flag the platform as supported. +// +#if !defined(EATHREAD_GETCALLSTACK_SUPPORTED) + #if EATHREAD_GLIBC_BACKTRACE_AVAILABLE // Typically this means Linux on x86. + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_IPHONE) + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_ANDROID) + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_IPHONE_SIMULATOR) + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_WINDOWS_PHONE) && defined(EA_PROCESSOR_ARM) + #define EATHREAD_GETCALLSTACK_SUPPORTED 0 + #elif defined(EA_PLATFORM_MICROSOFT) + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_LINUX) + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_OSX) + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_SONY) + #define EATHREAD_GETCALLSTACK_SUPPORTED 1 + #elif defined(EA_PLATFORM_CYGWIN) // Support hasn't been verified. + #define EATHREAD_GETCALLSTACK_SUPPORTED 0 + #elif defined(EA_PLATFORM_MINGW) // Support hasn't been verified. + #define EATHREAD_GETCALLSTACK_SUPPORTED 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_DEBUG_DETAIL_ENABLED +// +// Defined as 0 or 1. +// If true then detailed debug info is displayed. Can be enabled in opt builds. +// +#ifndef EATHREAD_DEBUG_DETAIL_ENABLED + #define EATHREAD_DEBUG_DETAIL_ENABLED 0 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_MIN_ABSOLUTE_TIME +// +// Defined as a time in milliseconds. +// Locks and waits allow the user to specify an absolute timeout time. In order +// to detect that the user accidentally specified a relative time, we define a +// minimum allowed absolute time which we assert on. This minimum time is one +// that in practice is impossible to be a future absolute time. +// +#ifndef EATHREAD_MIN_ABSOLUTE_TIME + #define EATHREAD_MIN_ABSOLUTE_TIME 10000 +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED +// +// Defined as 0 or 1. +// If true then the platform supports a user specified thread affinity mask. +// +#ifndef EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + #define EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED 1 + #elif defined(EA_PLATFORM_SONY) + #define EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED 1 + #elif defined(EA_PLATFORM_IPHONE) + // iPhone does not respect thread affinity + // https://forums.developer.apple.com/thread/44002 + // Consider: http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/processor_assign.html + #define EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED 0 + #elif defined(EA_PLATFORM_ANDROID) || defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_UNIX) + #define EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED 1 + #elif defined(EA_PLATFORM_NX) + #define EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED 1 + #elif defined(EA_USE_CPP11_CONCURRENCY) && EA_USE_CPP11_CONCURRENCY + // CPP11 doesn't not provided a mechanism to set thread affinities. + #define EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED 0 + #else + #define EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED 1 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_GLOBAL_VARIABLE_DLL_SAFETY +// +// Defined as 0 or 1. +// +// +#ifndef EATHREAD_GLOBAL_VARIABLE_DLL_SAFETY + #define EATHREAD_GLOBAL_VARIABLE_DLL_SAFETY 0 +#endif + + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_SCEDBG_ENABLED +// +// Defined as 0 or 1. +// Informs EAThread if Sony Debug libraries are available for us. +// +#ifndef EATHREAD_SCEDBG_ENABLED + #ifndef EA_SCEDBG_ENABLED + #define EATHREAD_SCEDBG_ENABLED 0 + #else + #define EATHREAD_SCEDBG_ENABLED EA_SCEDBG_ENABLED + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_DEBUG_BREAK +// +#ifndef EATHREAD_DEBUG_BREAK + #ifdef EA_COMPILER_MSVC + #define EATHREAD_DEBUG_BREAK() __debugbreak() + #else + #define EATHREAD_DEBUG_BREAK() *(volatile int*)(0) = 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_C11_ATOMICS_AVAILABLE +// +#ifndef EATHREAD_C11_ATOMICS_AVAILABLE + #if (defined(EA_ANDROID_SDK_LEVEL) && (EA_ANDROID_SDK_LEVEL >= 21)) + #define EATHREAD_C11_ATOMICS_AVAILABLE 1 + #else + #define EATHREAD_C11_ATOMICS_AVAILABLE 0 + #endif +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_ALIGNMENT_CHECK +// + +namespace EA { +namespace Thread { +namespace detail { + // Used to assert that memory accesses on x86-64 are atomic when "naturally" aligned to the size of registers. + template + inline bool IsNaturallyAligned(T* p) + { + return ((uintptr_t)p & (sizeof(EA_PLATFORM_WORD_SIZE) - 1)) == 0; + } +}}} + +#ifndef EATHREAD_ALIGNMENT_CHECK + #define EATHREAD_ALIGNMENT_CHECK(address) EAT_ASSERT_MSG(EA::Thread::detail::IsNaturallyAligned(address), "address is not naturally aligned.") +#endif + + +#endif // Header include guard + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/internal/dllinfo.h b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/dllinfo.h new file mode 100644 index 00000000..41eda781 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/dllinfo.h @@ -0,0 +1,15 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EATHREAD_DLLINFO_H +#define EATHREAD_DLLINFO_H + + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +#endif diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone.h b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone.h new file mode 100644 index 00000000..50ffb2aa --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone.h @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +/// Standalone atomic functions +/// These act the same as the class functions below. +/// The T return values are the previous value, except for the +/// AtomicFetchSwap function which returns the swapped out value. +/// +/// T AtomicGetValue(volatile T*); +/// T AtomicGetValue(const volatile T*); +/// void AtomicSetValue(volatile T*, T value); +/// T AtomicFetchIncrement(volatile T*); +/// T AtomicFetchDecrement(volatile T*); +/// T AtomicFetchAdd(volatile T*, T value); +/// T AtomicFetchSub(volatile T*, T value); +/// T AtomicFetchOr(volatile T*, T value); +/// T AtomicFetchAnd(volatile T*, T value); +/// T AtomicFetchXor(volatile T*, T value); +/// T AtomicFetchSwap(volatile T*, T value); +/// T AtomicFetchSwapConditional(volatile T*, T value, T condition); +/// bool AtomicSetValueConditional(volatile T*, T value, T condition); + +#if defined(EA_COMPILER_MSVC) + #include +#elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + #include +#else + #error unsupported platform +#endif + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone_gcc.h b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone_gcc.h new file mode 100644 index 00000000..e00fb8e0 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone_gcc.h @@ -0,0 +1,199 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +namespace EA +{ +namespace Thread +{ + +// TODO(rparolin): Consider use of clang builtin __sync_swap. +// https://clang.llvm.org/docs/LanguageExtensions.html#sync-swap + +// TODO(rparolin): Consider use of C11 atomics +// https://clang.llvm.org/docs/LanguageExtensions.html#c11-atomic-builtins + +namespace detail +{ + template + inline T AtomicGetValue(volatile T* ptr); +} // namespace detail + +// int +inline int AtomicGetValue(volatile int* ptr) { return detail::AtomicGetValue(ptr); } +inline int AtomicGetValue(const volatile int* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline int AtomicSetValue(volatile int* dest, int value) { return __sync_lock_test_and_set(dest, value); } +inline int AtomicFetchIncrement(volatile int* dest) { return __sync_fetch_and_add(dest, int(1)); } +inline int AtomicFetchDecrement(volatile int* dest) { return __sync_fetch_and_add(dest, int(-1)); } +inline int AtomicFetchAdd(volatile int* dest, int value) { return __sync_fetch_and_add(dest, value); } +inline int AtomicFetchSub(volatile int* dest, int value) { return __sync_fetch_and_sub(dest, value); } +inline int AtomicFetchOr(volatile int* dest, int value) { return __sync_fetch_and_or(dest, value); } +inline int AtomicFetchAnd(volatile int* dest, int value) { return __sync_fetch_and_and(dest, value); } +inline int AtomicFetchXor(volatile int* dest, int value) { return __sync_fetch_and_xor(dest, value); } +inline int AtomicFetchSwap(volatile int* dest, int value) { return __sync_lock_test_and_set(dest, value); } +inline int AtomicFetchSwapConditional(volatile int* dest, int value, int condition) { return __sync_val_compare_and_swap(dest, condition, value); } +inline bool AtomicSetValueConditional(volatile int* dest, int value, int condition) { return __sync_bool_compare_and_swap(dest, condition, value); } + +// unsigned int +inline unsigned int AtomicGetValue(volatile unsigned int* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned int AtomicGetValue(const volatile unsigned int* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned int AtomicSetValue(volatile unsigned int* dest, unsigned int value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned int AtomicFetchIncrement(volatile unsigned int* dest) { return __sync_fetch_and_add(dest, (unsigned int)(1)); } +inline unsigned int AtomicFetchDecrement(volatile unsigned int* dest) { return __sync_fetch_and_add(dest, (unsigned int)(-1)); } +inline unsigned int AtomicFetchAdd(volatile unsigned int* dest, unsigned int value) { return __sync_fetch_and_add(dest, value); } +inline unsigned int AtomicFetchSub(volatile unsigned int* dest, unsigned int value) { return __sync_fetch_and_sub(dest, value); } +inline unsigned int AtomicFetchOr(volatile unsigned int* dest, unsigned int value) { return __sync_fetch_and_or(dest, value); } +inline unsigned int AtomicFetchAnd(volatile unsigned int* dest, unsigned int value) { return __sync_fetch_and_and(dest, value); } +inline unsigned int AtomicFetchXor(volatile unsigned int* dest, unsigned int value) { return __sync_fetch_and_xor(dest, value); } +inline unsigned int AtomicFetchSwap(volatile unsigned int* dest, unsigned int value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned int AtomicFetchSwapConditional(volatile unsigned int* dest, unsigned int value, unsigned int condition) { return __sync_val_compare_and_swap(dest, condition, value); } +inline bool AtomicSetValueConditional(volatile unsigned int* dest, unsigned int value, unsigned int condition) { return __sync_bool_compare_and_swap(dest, condition, value); } + +// short +inline short AtomicGetValue(volatile short* ptr) { return detail::AtomicGetValue(ptr); } +inline short AtomicGetValue(const volatile short* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline short AtomicSetValue(volatile short* dest, short value) { return __sync_lock_test_and_set(dest, value); } +inline short AtomicFetchIncrement(volatile short* dest) { return __sync_fetch_and_add(dest, short(1)); } +inline short AtomicFetchDecrement(volatile short* dest) { return __sync_fetch_and_add(dest, short(-1)); } +inline short AtomicFetchAdd(volatile short* dest, short value) { return __sync_fetch_and_add(dest, value); } +inline short AtomicFetchSub(volatile short* dest, short value) { return __sync_fetch_and_sub(dest, value); } +inline short AtomicFetchOr(volatile short* dest, short value) { return __sync_fetch_and_or(dest, value); } +inline short AtomicFetchAnd(volatile short* dest, short value) { return __sync_fetch_and_and(dest, value); } +inline short AtomicFetchXor(volatile short* dest, short value) { return __sync_fetch_and_xor(dest, value); } +inline short AtomicFetchSwap(volatile short* dest, short value) { return __sync_lock_test_and_set(dest, value); } +inline short AtomicFetchSwapConditional(volatile short* dest, short value, short condition) { return __sync_val_compare_and_swap(reinterpret_cast(dest), static_cast(condition), static_cast(value)); } +inline bool AtomicSetValueConditional(volatile short* dest, short value, short condition) { return __sync_bool_compare_and_swap(reinterpret_cast(dest), static_cast(condition), static_cast(value)); } + +// unsigned short +inline unsigned short AtomicGetValue(volatile unsigned short* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned short AtomicGetValue(const volatile unsigned short* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned short AtomicSetValue(volatile unsigned short* dest, unsigned short value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned short AtomicFetchIncrement(volatile unsigned short* dest) { return __sync_fetch_and_add(dest, (unsigned short)(1)); } +inline unsigned short AtomicFetchDecrement(volatile unsigned short* dest) { return __sync_fetch_and_add(dest, (unsigned short)(-1)); } +inline unsigned short AtomicFetchAdd(volatile unsigned short* dest, unsigned short value) { return __sync_fetch_and_add(dest, value); } +inline unsigned short AtomicFetchSub(volatile unsigned short* dest, unsigned short value) { return __sync_fetch_and_sub(dest, value); } +inline unsigned short AtomicFetchOr(volatile unsigned short* dest, unsigned short value) { return __sync_fetch_and_or(dest, value); } +inline unsigned short AtomicFetchAnd(volatile unsigned short* dest, unsigned short value) { return __sync_fetch_and_and(dest, value); } +inline unsigned short AtomicFetchXor(volatile unsigned short* dest, unsigned short value) { return __sync_fetch_and_xor(dest, value); } +inline unsigned short AtomicFetchSwap(volatile unsigned short* dest, unsigned short value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned short AtomicFetchSwapConditional(volatile unsigned short* dest, unsigned short value, unsigned short condition) { return __sync_val_compare_and_swap(dest, condition, value); } +inline bool AtomicSetValueConditional(volatile unsigned short* dest, unsigned short value, unsigned short condition) { return __sync_bool_compare_and_swap(dest, condition, value); } + +// long +inline long AtomicGetValue(volatile long* ptr) { return detail::AtomicGetValue(ptr); } +inline long AtomicGetValue(const volatile long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline long AtomicSetValue(volatile long* dest, long value) { return __sync_lock_test_and_set(dest, value); } +inline long AtomicFetchIncrement(volatile long* dest) { return __sync_fetch_and_add(dest, long(1)); } +inline long AtomicFetchDecrement(volatile long* dest) { return __sync_fetch_and_add(dest, long(-1)); } +inline long AtomicFetchAdd(volatile long* dest, long value) { return __sync_fetch_and_add(dest, value); } +inline long AtomicFetchSub(volatile long* dest, long value) { return __sync_fetch_and_sub(dest, value); } +inline long AtomicFetchOr(volatile long* dest, long value) { return __sync_fetch_and_or(dest, value); } +inline long AtomicFetchAnd(volatile long* dest, long value) { return __sync_fetch_and_and(dest, value); } +inline long AtomicFetchXor(volatile long* dest, long value) { return __sync_fetch_and_xor(dest, value); } +inline long AtomicFetchSwap(volatile long* dest, long value) { return __sync_lock_test_and_set(dest, value); } +inline long AtomicFetchSwapConditional(volatile long* dest, long value, long condition) { return __sync_val_compare_and_swap(dest, condition, value); } +inline bool AtomicSetValueConditional(volatile long* dest, long value, long condition) { return __sync_bool_compare_and_swap(dest, condition, value); } + +// unsigned long +inline unsigned long AtomicGetValue(volatile unsigned long* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned long AtomicGetValue(const volatile unsigned long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned long AtomicSetValue(volatile unsigned long* dest, unsigned long value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned long AtomicFetchIncrement(volatile unsigned long* dest) { return __sync_fetch_and_add(dest, (unsigned long)(1)); } +inline unsigned long AtomicFetchDecrement(volatile unsigned long* dest) { return __sync_fetch_and_add(dest, (unsigned long)(-1)); } +inline unsigned long AtomicFetchAdd(volatile unsigned long* dest, unsigned long value) { return __sync_fetch_and_add(dest, value); } +inline unsigned long AtomicFetchSub(volatile unsigned long* dest, unsigned long value) { return __sync_fetch_and_sub(dest, value); } +inline unsigned long AtomicFetchOr(volatile unsigned long* dest, unsigned long value) { return __sync_fetch_and_or(dest, value); } +inline unsigned long AtomicFetchAnd(volatile unsigned long* dest, unsigned long value) { return __sync_fetch_and_and(dest, value); } +inline unsigned long AtomicFetchXor(volatile unsigned long* dest, unsigned long value) { return __sync_fetch_and_xor(dest, value); } +inline unsigned long AtomicFetchSwap(volatile unsigned long* dest, unsigned long value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned long AtomicFetchSwapConditional(volatile unsigned long* dest, unsigned long value, unsigned long condition) { return __sync_val_compare_and_swap(dest, condition, value); } +inline bool AtomicSetValueConditional(volatile unsigned long* dest, unsigned long value, unsigned long condition) { return __sync_bool_compare_and_swap(dest, condition, value); } + +// char32_t +#if EA_CHAR32_NATIVE + inline char32_t AtomicGetValue(volatile char32_t* ptr) { return detail::AtomicGetValue(ptr); } + inline char32_t AtomicGetValue(const volatile char32_t* ptr) { return AtomicGetValue(const_cast(ptr)); } + inline char32_t AtomicSetValue(volatile char32_t* dest, char32_t value) { return __sync_lock_test_and_set(dest, value); } + inline char32_t AtomicFetchIncrement(volatile char32_t* dest) { return __sync_fetch_and_add(dest, char32_t(1)); } + inline char32_t AtomicFetchDecrement(volatile char32_t* dest) { return __sync_fetch_and_add(dest, char32_t(-1)); } + inline char32_t AtomicFetchAdd(volatile char32_t* dest, char32_t value) { return __sync_fetch_and_add(dest, value); } + inline char32_t AtomicFetchSub(volatile char32_t* dest, char32_t value) { return __sync_fetch_and_sub(dest, value); } + inline char32_t AtomicFetchOr(volatile char32_t* dest, char32_t value) { return __sync_fetch_and_or(dest, value); } + inline char32_t AtomicFetchAnd(volatile char32_t* dest, char32_t value) { return __sync_fetch_and_and(dest, value); } + inline char32_t AtomicFetchXor(volatile char32_t* dest, char32_t value) { return __sync_fetch_and_xor(dest, value); } + inline char32_t AtomicFetchSwap(volatile char32_t* dest, char32_t value) { return __sync_lock_test_and_set(dest, value); } + inline char32_t AtomicFetchSwapConditional(volatile char32_t* dest, char32_t value, char32_t condition) { return __sync_val_compare_and_swap(dest, condition, value); } + inline bool AtomicSetValueConditional(volatile char32_t* dest, char32_t value, char32_t condition) { return __sync_bool_compare_and_swap(dest, condition, value); } +#endif + +// long long +inline long long AtomicGetValue(volatile long long* ptr) { return detail::AtomicGetValue(ptr); } +inline long long AtomicGetValue(const volatile long long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline long long AtomicSetValue(volatile long long* dest, long long value) { return __sync_lock_test_and_set(dest, value); } +inline long long AtomicFetchIncrement(volatile long long* dest) { return __sync_fetch_and_add(dest, (long long)(1)); } +inline long long AtomicFetchDecrement(volatile long long* dest) { return __sync_fetch_and_add(dest, (long long)(-1)); } +inline long long AtomicFetchAdd(volatile long long* dest, long long value) { return __sync_fetch_and_add(dest, value); } +inline long long AtomicFetchSub(volatile long long* dest, long long value) { return __sync_fetch_and_sub(dest, value); } +inline long long AtomicFetchOr(volatile long long* dest, long long value) { return __sync_fetch_and_or(dest, value); } +inline long long AtomicFetchAnd(volatile long long* dest, long long value) { return __sync_fetch_and_and(dest, value); } +inline long long AtomicFetchXor(volatile long long* dest, long long value) { return __sync_fetch_and_xor(dest, value); } +inline long long AtomicFetchSwap(volatile long long* dest, long long value) { return __sync_lock_test_and_set(dest, value); } +inline long long AtomicFetchSwapConditional(volatile long long* dest, long long value, long long condition) { return __sync_val_compare_and_swap(dest, condition, value); } +inline bool AtomicSetValueConditional(volatile long long* dest, long long value, long long condition) { return __sync_bool_compare_and_swap(dest, condition, value); } + +// unsigned long long +inline unsigned long long AtomicGetValue(volatile unsigned long long* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned long long AtomicGetValue(const volatile unsigned long long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned long long AtomicSetValue(volatile unsigned long long* dest, unsigned long long value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned long long AtomicFetchIncrement(volatile unsigned long long* dest) { return __sync_fetch_and_add(dest, (unsigned long long)(1)); } +inline unsigned long long AtomicFetchDecrement(volatile unsigned long long* dest) { return __sync_fetch_and_add(dest, (unsigned long long)(-1)); } +inline unsigned long long AtomicFetchAdd(volatile unsigned long long* dest, unsigned long long value) { return __sync_fetch_and_add(dest, value); } +inline unsigned long long AtomicFetchSub(volatile unsigned long long* dest, unsigned long long value) { return __sync_fetch_and_sub(dest, value); } +inline unsigned long long AtomicFetchOr(volatile unsigned long long* dest, unsigned long long value) { return __sync_fetch_and_or(dest, value); } +inline unsigned long long AtomicFetchAnd(volatile unsigned long long* dest, unsigned long long value) { return __sync_fetch_and_and(dest, value); } +inline unsigned long long AtomicFetchXor(volatile unsigned long long* dest, unsigned long long value) { return __sync_fetch_and_xor(dest, value); } +inline unsigned long long AtomicFetchSwap(volatile unsigned long long* dest, unsigned long long value) { return __sync_lock_test_and_set(dest, value); } +inline unsigned long long AtomicFetchSwapConditional(volatile unsigned long long* dest, unsigned long long value, unsigned long long condition) { return __sync_val_compare_and_swap(dest, condition, value); } +inline bool AtomicSetValueConditional(volatile unsigned long long* dest, unsigned long long value, unsigned long long condition) { return __sync_bool_compare_and_swap(dest, condition, value); } + +// +// You can not simply define a template for the above atomics due to the explicit 128bit overloads +// below. The compiler will prefer those overloads during overload resolution and attempt to convert +// temporaries as they are more specialized than a template. +// +// template inline T AtomicGetValue(volatile T* source) { return __sync_fetch_and_add(source, (T)(0)); } +// template inline void AtomicSetValue(volatile T* dest, T value) { __sync_lock_test_and_set(dest, value); } +// template inline T AtomicFetchIncrement(volatile T* dest) { return __sync_fetch_and_add(dest, (T)(1)); } +// template inline T AtomicFetchDecrement(volatile T* dest) { return __sync_fetch_and_add(dest, (T)(-1)); } +// template inline T AtomicFetchAdd(volatile T* dest, T value) { return __sync_fetch_and_add(dest, value); } +// template inline T AtomicFetchOr(volatile T* dest, T value) { return __sync_fetch_and_or(dest, value); } +// template inline T AtomicFetchAnd(volatile T* dest, T value) { return __sync_fetch_and_and(dest, value); } +// template inline T AtomicFetchXor(volatile T* dest, T value) { return __sync_fetch_and_xor(dest, value); } +// template inline T AtomicFetchSwap(volatile T* dest, T value) { return __sync_lock_test_and_set(dest, value); } +// template inline bool AtomicSetValueConditional(volatile T* dest, T value, T condition) { return __sync_bool_compare_and_swap(dest, condition, value); } +// + +namespace detail +{ + template + inline T AtomicGetValue(volatile T* ptr) + { + #if EA_PLATFORM_WORD_SIZE >= 8 && defined(EA_PROCESSOR_X86_64) + EATHREAD_ALIGNMENT_CHECK(ptr); + EACompilerMemoryBarrier(); + T value = *ptr; + EACompilerMemoryBarrier(); + return value; + #else + return AtomicFetchAdd(ptr, T(0)); + #endif + } +} // namespace detail + +} // namespace Thread +} // namespace EA + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone_msvc.h b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone_msvc.h new file mode 100644 index 00000000..fc96453b --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_atomic_standalone_msvc.h @@ -0,0 +1,260 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + +///////////////////////////////////////////////////////////////////////////// +// InterlockedXXX intrinsics +// +#if defined(EA_PLATFORM_MICROSOFT) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + + #if defined(EA_PROCESSOR_X86) + #if defined(_InterlockedExchange64_INLINE) + #define _InterlockedExchange64 _InterlockedExchange64_INLINE + #define _InterlockedExchangeAdd64 _InterlockedExchangeAdd64_INLINE + #define _InterlockedAnd64 _InterlockedAnd64_INLINE + #define _InterlockedOr64 _InterlockedOr64_INLINE + #define _InterlockedXor64 _InterlockedXor64_INLINE + #else + namespace EA { + namespace Thread { + namespace Internal { + template + T CAS64Helper(PtrT* ptr, T newVal, ValueTransformOp op) + { + T oldVal; + + do + { + oldVal = static_cast(*ptr); + } while (oldVal != _InterlockedCompareExchange64(ptr, op(oldVal, newVal), oldVal)); + + return oldVal; + } + }}} + + template + auto _InterlockedExchange64(T1* ptr, T2 value) + { + return EA::Thread::Internal::CAS64Helper(ptr, value, [](auto o, auto n) { return n; }); + } + + template + auto _InterlockedExchangeAdd64(T1* ptr, T2 value) + { + return EA::Thread::Internal::CAS64Helper(ptr, value, [](auto o, auto n) { return o + n; }); + } + + template + auto _InterlockedAnd64(T1* ptr, T2 value) + { + return EA::Thread::Internal::CAS64Helper(ptr, value, [](auto o, auto n) { return o & n; }); + } + + template + auto _InterlockedOr64(T1* ptr, T2 value) + { + return EA::Thread::Internal::CAS64Helper(ptr, value, [](auto o, auto n) { return o | n; }); + } + + template + auto _InterlockedXor64(T1* ptr, T2 value) + { + return EA::Thread::Internal::CAS64Helper(ptr, value, [](auto o, auto n) { return o ^ n; }); + } + #endif + #endif + + inline bool InterlockedSetIfEqual(volatile int64_t* dest, int64_t newValue, int64_t condition) + { + return (_InterlockedCompareExchange64(dest, newValue, condition) == condition); + } + + inline bool InterlockedSetIfEqual(volatile uint64_t* dest, uint64_t newValue, uint64_t condition) + { + return (_InterlockedCompareExchange64((int64_t volatile*)dest, (int64_t)newValue, (int64_t)condition) == (int64_t)condition); + } + +#endif // EA_PLATFORM_MICROSOFT + + + + +namespace EA +{ +namespace Thread +{ + +namespace detail +{ + template + inline T AtomicGetValue(volatile T* ptr); +} // namespace detail + +// int +inline int AtomicGetValue(volatile int* ptr) { return detail::AtomicGetValue(ptr); } +inline int AtomicGetValue(const volatile int* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline int AtomicSetValue(volatile int* ptr, int value) { return _InterlockedExchange((long*)ptr, (long)value); } +inline int AtomicFetchIncrement(volatile int* ptr) { return static_cast(_InterlockedIncrement((long*)ptr)) - 1; } +inline int AtomicFetchDecrement(volatile int* ptr) { return static_cast(_InterlockedDecrement((long*)ptr)) + 1; } +inline int AtomicFetchAdd(volatile int* ptr, int value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)value)); } +inline int AtomicFetchSub(volatile int* ptr, int value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, -(long)value)); } +inline int AtomicFetchOr(volatile int* ptr, int value) { return static_cast(_InterlockedOr((long*)ptr, (long)value)); } +inline int AtomicFetchAnd(volatile int* ptr, int value) { return static_cast(_InterlockedAnd((long*)ptr, (long)value)); } +inline int AtomicFetchXor(volatile int* ptr, int value) { return static_cast(_InterlockedXor((long*)ptr, (long)value)); } +inline int AtomicFetchSwap(volatile int* ptr, int value) { return static_cast(_InterlockedExchange((long*)ptr, (long)value)); } +inline int AtomicFetchSwapConditional(volatile int* ptr, int value, int condition) { return _InterlockedCompareExchange((long*)ptr, (long)value, (long)condition); } +inline bool AtomicSetValueConditional(volatile int* ptr, int value, int condition) { return _InterlockedCompareExchange((long*)ptr, (long)value, (long)condition) == (long)condition; } + +// unsigned int +inline unsigned int AtomicGetValue(volatile unsigned int* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned int AtomicGetValue(const volatile unsigned int* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned int AtomicSetValue(volatile unsigned int* ptr, unsigned int value) { return static_cast(_InterlockedExchange((long*)ptr, (long)value)); } +inline unsigned int AtomicFetchIncrement(volatile unsigned int* ptr) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)1)); } +inline unsigned int AtomicFetchDecrement(volatile unsigned int* ptr) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)-1)); } +inline unsigned int AtomicFetchAdd(volatile unsigned int* ptr, unsigned int value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)value)); } +inline unsigned int AtomicFetchSub(volatile unsigned int* ptr, unsigned int value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, -(long)value)); } +inline unsigned int AtomicFetchOr(volatile unsigned int* ptr, unsigned int value) { return static_cast(_InterlockedOr((long*)ptr, (long)value)); } +inline unsigned int AtomicFetchAnd(volatile unsigned int* ptr, unsigned int value) { return static_cast(_InterlockedAnd((long*)ptr, (long)value)); } +inline unsigned int AtomicFetchXor(volatile unsigned int* ptr, unsigned int value) { return static_cast(_InterlockedXor((long*)ptr, (long)value)); } +inline unsigned int AtomicFetchSwap(volatile unsigned int* ptr, unsigned int value) { return static_cast(_InterlockedExchange((long*)ptr, (long)value)); } +inline unsigned int AtomicFetchSwapConditional(volatile unsigned int* ptr, unsigned int value, unsigned int condition) { return (unsigned int)_InterlockedCompareExchange((long*)ptr, (long)value, (long)condition); } +inline bool AtomicSetValueConditional(volatile unsigned int* ptr, unsigned int value, unsigned int condition) { return _InterlockedCompareExchange((long*)ptr, (long)value, (long)condition) == (long)condition; } + +// short +inline short AtomicGetValue(volatile short* ptr) { return detail::AtomicGetValue(ptr); } +inline short AtomicGetValue(const volatile short* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline short AtomicSetValue(volatile short* ptr, short value) { return static_cast(_InterlockedExchange16((short*)ptr, (short)value)); } +inline short AtomicFetchIncrement(volatile short* ptr) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, (short)1)); } +inline short AtomicFetchDecrement(volatile short* ptr) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, (short)-1)); } +inline short AtomicFetchAdd(volatile short* ptr, short value) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, (short)value)); } +inline short AtomicFetchSub(volatile short* ptr, short value) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, -value)); } +inline short AtomicFetchOr(volatile short* ptr, short value) { return static_cast(_InterlockedOr16((short*)ptr, (short)value)); } +inline short AtomicFetchAnd(volatile short* ptr, short value) { return static_cast(_InterlockedAnd16((short*)ptr, (short)value)); } +inline short AtomicFetchXor(volatile short* ptr, short value) { return static_cast(_InterlockedXor16((short*)ptr, (short)value)); } +inline short AtomicFetchSwap(volatile short* ptr, short value) { return static_cast(_InterlockedExchange16((short*)ptr, (short)value)); } +inline short AtomicFetchSwapConditional(volatile short* ptr, short value, short condition) { return _InterlockedCompareExchange16(ptr, value, condition); } +inline bool AtomicSetValueConditional(volatile short* ptr, short value, short condition) { return _InterlockedCompareExchange16(ptr, value, condition) == condition; } + +// unsigned short +inline unsigned short AtomicGetValue(volatile unsigned short* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned short AtomicGetValue(const volatile unsigned short* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned short AtomicSetValue(volatile unsigned short* ptr, unsigned short value) { return static_cast(_InterlockedExchange16((short*)ptr, (short)value)); } +inline unsigned short AtomicFetchIncrement(volatile unsigned short* ptr) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, (short)1)); } +inline unsigned short AtomicFetchDecrement(volatile unsigned short* ptr) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, (short)-1)); } +inline unsigned short AtomicFetchAdd(volatile unsigned short* ptr, unsigned short value) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, (short)value)); } +inline unsigned short AtomicFetchSub(volatile unsigned short* ptr, unsigned short value) { return static_cast(_InterlockedExchangeAdd16((short*)ptr, -(short)value)); } +inline unsigned short AtomicFetchOr(volatile unsigned short* ptr, unsigned short value) { return static_cast(_InterlockedOr16((short*)ptr, (short)value)); } +inline unsigned short AtomicFetchAnd(volatile unsigned short* ptr, unsigned short value) { return static_cast(_InterlockedAnd16((short*)ptr, (short)value)); } +inline unsigned short AtomicFetchXor(volatile unsigned short* ptr, unsigned short value) { return static_cast(_InterlockedXor16((short*)ptr, (short)value)); } +inline unsigned short AtomicFetchSwap(volatile unsigned short* ptr, unsigned short value) { return static_cast(_InterlockedExchange16((short*)ptr, (short)value)); } +inline unsigned short AtomicFetchSwapConditional(volatile unsigned short* ptr, unsigned short value, unsigned short condition) { return (unsigned short)_InterlockedCompareExchange16((short*)ptr, (short)value, (short)condition); } +inline bool AtomicSetValueConditional(volatile unsigned short* ptr, unsigned short value, unsigned short condition) { return _InterlockedCompareExchange16((short*)ptr, (short)value, (short)condition) == (short)condition; } + +// long +inline long AtomicGetValue(volatile long* ptr) { return detail::AtomicGetValue(ptr); } +inline long AtomicGetValue(const volatile long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline long AtomicSetValue(volatile long* ptr, long value) { return _InterlockedExchange(ptr, value); } +inline long AtomicFetchIncrement(volatile long* ptr) { return _InterlockedIncrement(ptr) - 1; } +inline long AtomicFetchDecrement(volatile long* ptr) { return _InterlockedDecrement(ptr) + 1; } +inline long AtomicFetchAdd(volatile long* ptr, long value) { return _InterlockedExchangeAdd(ptr, value); } +inline long AtomicFetchSub(volatile long* ptr, long value) { return _InterlockedExchangeAdd(ptr, -value); } +inline long AtomicFetchOr(volatile long* ptr, long value) { return _InterlockedOr(ptr, value); } +inline long AtomicFetchAnd(volatile long* ptr, long value) { return _InterlockedAnd(ptr, value); } +inline long AtomicFetchXor(volatile long* ptr, long value) { return _InterlockedXor(ptr, value); } +inline long AtomicFetchSwap(volatile long* ptr, long value) { return _InterlockedExchange(ptr, value); } +inline long AtomicFetchSwapConditional(volatile long* ptr, long value, long condition) { return _InterlockedCompareExchange(ptr, value, condition); } +inline bool AtomicSetValueConditional(volatile long* ptr, long value, long condition) { return _InterlockedCompareExchange(ptr, value, condition) == condition; } + +// unsigned long +inline unsigned long AtomicGetValue(volatile unsigned long* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned long AtomicGetValue(const volatile unsigned long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned long AtomicSetValue(volatile unsigned long* ptr, unsigned long value) { return static_cast(_InterlockedExchange((long*)ptr, (long)value)); } +inline unsigned long AtomicFetchIncrement(volatile unsigned long* ptr) { return static_cast(_InterlockedIncrement((long*)ptr)) - 1; } +inline unsigned long AtomicFetchDecrement(volatile unsigned long* ptr) { return static_cast(_InterlockedDecrement((long*)ptr)) + 1; } +inline unsigned long AtomicFetchAdd(volatile unsigned long* ptr, unsigned long value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)value)); } +inline unsigned long AtomicFetchSub(volatile unsigned long* ptr, unsigned long value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, -(long)value)); } +inline unsigned long AtomicFetchOr(volatile unsigned long* ptr, unsigned long value) { return static_cast(_InterlockedOr((long*)ptr, (long)value)); } +inline unsigned long AtomicFetchAnd(volatile unsigned long* ptr, unsigned long value) { return static_cast(_InterlockedAnd((long*)ptr, (long)value)); } +inline unsigned long AtomicFetchXor(volatile unsigned long* ptr, unsigned long value) { return static_cast(_InterlockedXor((long*)ptr, (long)value)); } +inline unsigned long AtomicFetchSwap(volatile unsigned long* ptr, unsigned long value) { return static_cast(_InterlockedExchange((long*)ptr, (long)value)); } +inline unsigned long AtomicFetchSwapConditional(volatile unsigned long* ptr, unsigned long value, unsigned long condition) { return static_cast(_InterlockedCompareExchange((long*)ptr, (long)value, (long)condition)); } +inline bool AtomicSetValueConditional(volatile unsigned long* ptr, unsigned long value, unsigned long condition) { return static_cast(_InterlockedCompareExchange((long*)ptr, (long)value, (long)condition)) == condition; } + +// char32_t +#if EA_CHAR32_NATIVE + inline char32_t AtomicGetValue(volatile char32_t* ptr) { return detail::AtomicGetValue(ptr); } + inline char32_t AtomicGetValue(const volatile char32_t* ptr) { return AtomicGetValue(const_cast(ptr)); } + inline char32_t AtomicSetValue(volatile char32_t* ptr, char32_t value) { return static_cast(_InterlockedExchange((long*)ptr, (long)value)); } + inline char32_t AtomicFetchIncrement(volatile char32_t* ptr) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)1)); } + inline char32_t AtomicFetchDecrement(volatile char32_t* ptr) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)-1)); } + inline char32_t AtomicFetchAdd(volatile char32_t* ptr, char32_t value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, (long)value)); } + inline char32_t AtomicFetchSub(volatile char32_t* ptr, char32_t value) { return static_cast(_InterlockedExchangeAdd((long*)ptr, -(long)value)); } + inline char32_t AtomicFetchOr(volatile char32_t* ptr, char32_t value) { return static_cast(_InterlockedOr((long*)ptr, (long)value)); } + inline char32_t AtomicFetchAnd(volatile char32_t* ptr, char32_t value) { return static_cast(_InterlockedAnd((long*)ptr, (long)value)); } + inline char32_t AtomicFetchXor(volatile char32_t* ptr, char32_t value) { return static_cast(_InterlockedXor((long*)ptr, (long)value)); } + inline char32_t AtomicFetchSwap(volatile char32_t* ptr, char32_t value) { return static_cast(_InterlockedExchange((long*)ptr, (long)value)); } + inline char32_t AtomicFetchSwapConditional(volatile char32_t* ptr, char32_t value, unsigned int condition) { return static_cast(_InterlockedCompareExchange((long*)ptr, (long)value, (long)condition)); } + inline bool AtomicSetValueConditional(volatile char32_t* ptr, char32_t value, unsigned int condition) { return _InterlockedCompareExchange((long*)ptr, (long)value, (long)condition) == (long)condition; } +#endif + + +// long long +inline long long AtomicGetValue(volatile long long* ptr) { return detail::AtomicGetValue(ptr); } +inline long long AtomicGetValue(const volatile long long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline long long AtomicSetValue(volatile long long* ptr, long long value) { return static_cast(_InterlockedExchange64(ptr, value)); } +inline long long AtomicFetchIncrement(volatile long long* ptr) { return static_cast(_InterlockedExchangeAdd64(ptr, (long long)1)); } +inline long long AtomicFetchDecrement(volatile long long* ptr) { return static_cast(_InterlockedExchangeAdd64(ptr, (long long)-1)); } +inline long long AtomicFetchAdd(volatile long long* ptr, long long value) { return static_cast(_InterlockedExchangeAdd64(ptr, value)); } +inline long long AtomicFetchSub(volatile long long* ptr, long long value) { return static_cast(_InterlockedExchangeAdd64(ptr, -(long long)value)); } +inline long long AtomicFetchOr(volatile long long* ptr, long long value) { return static_cast(_InterlockedOr64(ptr, value)); } +inline long long AtomicFetchAnd(volatile long long* ptr, long long value) { return static_cast(_InterlockedAnd64(ptr, value)); } +inline long long AtomicFetchXor(volatile long long* ptr, long long value) { return static_cast(_InterlockedXor64(ptr, value)); } +inline long long AtomicFetchSwap(volatile long long* ptr, long long value) { return static_cast(_InterlockedExchange64(ptr, value)); } +inline long long AtomicFetchSwapConditional(volatile long long* ptr, long long value, long long condition) { return _InterlockedCompareExchange64(ptr, value, condition); } +inline bool AtomicSetValueConditional(volatile long long* ptr, long long value, long long condition) { return _InterlockedCompareExchange64(ptr, value, condition) == condition; } + +// unsigned long long +inline unsigned long long AtomicGetValue(volatile unsigned long long* ptr) { return detail::AtomicGetValue(ptr); } +inline unsigned long long AtomicGetValue(const volatile unsigned long long* ptr) { return AtomicGetValue(const_cast(ptr)); } +inline unsigned long long AtomicSetValue(volatile unsigned long long* ptr, unsigned long long value) { return static_cast(_InterlockedExchange64(reinterpret_cast(ptr), (long long)value)); } +inline unsigned long long AtomicFetchIncrement(volatile unsigned long long* ptr) { return static_cast(_InterlockedExchangeAdd64(reinterpret_cast(ptr), (long long)1)); } +inline unsigned long long AtomicFetchDecrement(volatile unsigned long long* ptr) { return static_cast(_InterlockedExchangeAdd64(reinterpret_cast(ptr), (long long)-1)); } +inline unsigned long long AtomicFetchAdd(volatile unsigned long long* ptr, unsigned long long value) { return static_cast(_InterlockedExchangeAdd64(reinterpret_cast(ptr), (long long)value)); } +inline unsigned long long AtomicFetchSub(volatile unsigned long long* ptr, unsigned long long value) { return static_cast(_InterlockedExchangeAdd64(reinterpret_cast(ptr), -(long long)value)); } +inline unsigned long long AtomicFetchOr(volatile unsigned long long* ptr, unsigned long long value) { return static_cast(_InterlockedOr64(reinterpret_cast(ptr), (long long)value)); } +inline unsigned long long AtomicFetchAnd(volatile unsigned long long* ptr, unsigned long long value) { return static_cast(_InterlockedAnd64(reinterpret_cast(ptr),(long long) value)); } +inline unsigned long long AtomicFetchXor(volatile unsigned long long* ptr, unsigned long long value) { return static_cast(_InterlockedXor64(reinterpret_cast(ptr),(long long) value)); } +inline unsigned long long AtomicFetchSwap(volatile unsigned long long* ptr, unsigned long long value) { return static_cast(_InterlockedExchange64(reinterpret_cast(ptr),(long long) value)); } +inline unsigned long long AtomicFetchSwapConditional(volatile unsigned long long* ptr, unsigned long long value, unsigned long long condition) { return static_cast(_InterlockedCompareExchange64(reinterpret_cast(ptr), (long long)value, (long long)condition)); } +inline bool AtomicSetValueConditional(volatile unsigned long long* ptr, unsigned long long value, unsigned long long condition) { return static_cast(_InterlockedCompareExchange64(reinterpret_cast(ptr), (long long)value, (long long)condition)) == condition; } + + +namespace detail +{ + template + inline T AtomicGetValue(volatile T* ptr) + { + #if EA_PLATFORM_WORD_SIZE >= 8 && defined(EA_PROCESSOR_X86_64) + EATHREAD_ALIGNMENT_CHECK(ptr); + EACompilerMemoryBarrier(); + T value = *ptr; + EACompilerMemoryBarrier(); + return value; + #else + return AtomicFetchAdd(ptr, T(0)); + #endif + } +} // namespace detail + + +} // namespace Thread +} // namespace EA + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_global.h b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_global.h new file mode 100644 index 00000000..9e930135 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/eathread_global.h @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +///////////////////////////////////////////////////////////////////////////// +// NOTE(rparolin): Provides a unified method of access to EAThread global +// variables that (when specified by the user) can become DLL safe by adding a +// dependency on EAStdC EAGlobal implementation. +///////////////////////////////////////////////////////////////////////////// + +#ifndef EATHREAD_INTERNAL_GLOBAL_H +#define EATHREAD_INTERNAL_GLOBAL_H + +#if EATHREAD_GLOBAL_VARIABLE_DLL_SAFETY + #include + + #define EATHREAD_GLOBALVARS (*EA::StdC::AutoStaticOSGlobalPtr().get()) + #define EATHREAD_GLOBALVARS_CREATE_INSTANCE EA::StdC::AutoStaticOSGlobalPtr gGlobalVarsInstance; + #define EATHREAD_GLOBALVARS_EXTERN_INSTANCE + +#else + #define EATHREAD_GLOBALVARS gEAThreadGlobalVars + #define EATHREAD_GLOBALVARS_CREATE_INSTANCE EA::Thread::EAThreadGlobalVars gEAThreadGlobalVars + #define EATHREAD_GLOBALVARS_EXTERN_INSTANCE extern EA::Thread::EAThreadGlobalVars gEAThreadGlobalVars + +#endif + +#endif diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/internal/timings.h b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/timings.h new file mode 100644 index 00000000..599bfd81 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/internal/timings.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +#ifndef EATHREAD_INTERNAL_TIMINGS_H +#define EATHREAD_INTERNAL_TIMINGS_H + +namespace EA +{ + namespace Thread + { + +#if defined(EA_PLATFORM_SONY) + // RelativeTimeoutFromAbsoluteTimeout returns a relative timeout in microseconds. + inline uint32_t RelativeTimeoutFromAbsoluteTimeout(EA::Thread::ThreadTime timeoutAbsolute) + { + using namespace EA::Thread; + + EAT_ASSERT((timeoutAbsolute == kTimeoutImmediate) || (timeoutAbsolute > EATHREAD_MIN_ABSOLUTE_TIME)); // Assert that the user didn't make the mistake of treating time as relative instead of absolute. + + uint32_t timeoutRelative = 0; + + if (timeoutAbsolute == kTimeoutNone) + { + timeoutRelative = 0xffffffff; + } + else if (timeoutAbsolute == kTimeoutImmediate) + { + timeoutRelative = 0; + } + else + { + ThreadTime timeCurrent(GetThreadTime()); + timeoutRelative = (timeoutAbsolute > timeCurrent) ? EA_THREADTIME_AS_UINT_MICROSECONDS(timeoutAbsolute - timeCurrent) : 0; + } + + EAT_ASSERT((timeoutRelative == 0xffffffff) || (timeoutRelative < 100000000)); // Assert that the timeout is a sane value and didn't wrap around. + + return timeoutRelative; + } +#endif + + } +} + +#endif diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/shared_array_mt.h b/r5dev/thirdparty/ea/EAThread/include/eathread/shared_array_mt.h new file mode 100644 index 00000000..b9a4d0f9 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/shared_array_mt.h @@ -0,0 +1,431 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This is a multithread-safe version of shared_array_mt. +// For basic documentation, see shared_array_mt. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_SHARED_ARRAY_MT_H +#define EATHREAD_SHARED_ARRAY_MT_H + +#ifndef INCLUDED_eabase_H + #include +#endif +#ifndef EATHREAD_EATHREAD_FUTEX_H + #include +#endif +#include // More properly: #include // Definition of std::ptrdiff_t + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + + +/// namespace EA +/// The standard Electronic Arts namespace +namespace EA +{ + namespace Thread + { + /// class shared_array_mt + /// A shared_array_mt is the same as shared_ptr but for arrays. + template + class shared_array_mt + { + private: + /// this_type + /// This is an alias for shared_array_mt, this class. + typedef shared_array_mt this_type; + + /// reference_count_type + /// An internal reference count type. Must be convertable to int + /// so that the public use_count function can work. + typedef EA::Thread::AtomicInt32 reference_count_type; + + T* mpArray; /// The owned pointer. Points to an array of T. + reference_count_type* mpRefCount; /// Reference count for owned pointer. + mutable Futex mMutex; /// Mutex guarding access to this class. + + public: + typedef T element_type; + typedef T value_type; + + /// shared_array_mt + /// Takes ownership of the pointer and sets the reference count + /// to the pointer to 1. It is OK if the input pointer is null. + /// The shared reference count is allocated on the heap via operator new. + /// If an exception occurs during the allocation of the shared + /// reference count, the owned pointer is deleted and the exception + /// is rethrown. A null pointer is given a reference count of 1. + explicit shared_array_mt(T* pArray = 0) + : mpArray(pArray), mMutex() + { + // We don't lock our mutex in this function, as this is the constructor + // and we assume that construction is already done in a thread-safe way + // by the owner of this object. + #if defined(EA_COMPILER_NO_EXCEPTIONS) || defined(EA_COMPILER_NO_UNWIND) + mpRefCount = new reference_count_type(1); + #else + EA_DISABLE_VC_WARNING(4571) + try + { + mpRefCount = new reference_count_type(1); + } + catch(...) + { + delete[] mpArray; + //mpRefCount = 0; shouldn't be necessary. + throw; + } + EA_RESTORE_VC_WARNING() + #endif + } + + /// shared_array_mt + /// Shares ownership of a pointer with another instance of shared_array_mt. + /// This function increments the shared reference count on the pointer. + shared_array_mt(shared_array_mt const& sharedArray) + : mMutex() + { + sharedArray.lock(); + mpArray = sharedArray.mpArray; + mpRefCount = sharedArray.mpRefCount; + mpRefCount->Increment(); // Atomic operation + sharedArray.unlock(); + } + + /// ~shared_array_mt + /// Decrements the reference count for the owned pointer. If the + /// reference count goes to zero, the owned pointer is deleted and + /// the shared reference count is deleted. + ~shared_array_mt() + { + lock(); + const reference_count_type newRefCount(mpRefCount->Decrement()); // Atomic operation + // EAT_ASSERT(newRefCount >= 0); + if(newRefCount == 0) + { + delete[] mpArray; + delete mpRefCount; + } + unlock(); + } + + /// operator= + /// Copies another shared_array_mt to this object. Note that this object + /// may already own a shared pointer with another different pointer + /// (but still of the same type) before this call. In that case, + /// this function releases the old pointer, decrementing its reference + /// count and deleting it if zero, takes shared ownership of the new + /// pointer and increments its reference count. + shared_array_mt& operator=(shared_array_mt const& sharedArray) + { + // We don't lock mutexes here because we let the swap function + // below do the locking and assignment. The if statement below + // isn't protected within a lock operation because it wouldn't + // help by being so because if mpValue is changing during the + // the execution of this function then the user has an external + // race condition that needs to be managed at that level. + if(mpArray != sharedArray.mpArray) + { + // The easiest thing to do is to create a temporary and + // copy ourselves ourselves into it. This is a standard + // method for switching pointer ownership in systems like this. + shared_array_mt(sharedArray).swap(*this); + } + return *this; + } + + // operator= + // We do not defined this function in order to maintain compatibility + // with the currently proposed (2003) C++ standard addition. Use reset instead. + // shared_array_mt& operator=(T* pValue) + // { + // reset(pValue); + // return *this; + // } + + /// lock + /// @brief Locks our mutex for thread-safe access. + /// It is a const function because const-ness refers to the underlying pointer being + /// held and not this class. + void lock() const + { + mMutex.Lock(); + } + + /// unlock + /// @brief Unlocks our mutex which was previous locked. + /// It is a const function because const-ness refers to the underlying pointer being + /// held and not this class. + void unlock() const + { + mMutex.Unlock(); + } + + /// reset + /// Releases the owned pointer and takes ownership of the + /// passed in pointer. If the passed in pointer is the same + /// as the owned pointer, nothing is done. The passed in pointer + /// can be null, in which case the use count is set to 1. + void reset(T* pArray = 0) + { + // We don't lock any mutexes here because we let the swap function do that. + // We don't lock for the 'if' statement below because that wouldn't really buy anything. + if(pArray != mpArray) + { + // The easiest thing to do is to create a temporary and + // copy ourselves ourselves into it. This is a standard + // method for switching pointer ownership in systems like this. + shared_array_mt(pArray).swap(*this); + } + } + + /// swap + /// Exchanges the owned pointer beween two shared_array_mt objects. + void swap(shared_array_mt& sharedArray) + { + lock(); + sharedArray.lock(); + + // std::swap(mpArray, sharedArray.mpArray); // Not used so that we can reduce a dependency. + T* const pArray = sharedArray.mpArray; + sharedArray.mpArray = mpArray; + mpArray = pArray; + + // std::swap(mpRefCount, sharedArray.mpRefCount); // Not used so that we can reduce a dependency. + reference_count_type* const pRefCount = sharedArray.mpRefCount; + sharedArray.mpRefCount = mpRefCount; + mpRefCount = pRefCount; + + sharedArray.unlock(); + unlock(); + } + + /// operator[] + /// Returns a reference to the specified item in the owned pointer + /// array. + /// Example usage: + /// shared_array_mt ptr = new int[6]; + /// int x = ptr[2]; + T& operator[](ptrdiff_t i) const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpArray && (i >= 0)); + return mpArray[i]; + } + + /// operator* + /// Returns the owner pointer dereferenced. + /// Example usage: + /// shared_array_mt ptr = new int(3); + /// int x = *ptr; + T& operator*() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpArray); + return *mpArray; + } + + /// operator-> + /// Allows access to the owned pointer via operator->() + /// Example usage: + /// struct X{ void DoSomething(); }; + /// shared_array_mt ptr = new X; + /// ptr->DoSomething(); + T* operator->() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpArray); + return mpArray; + } + + /// get + /// Returns the owned pointer. Note that this class does + /// not provide an operator T() function. This is because such + /// a thing (automatic conversion) is deemed unsafe. + /// Example usage: + /// struct X{ void DoSomething(); }; + /// shared_array_mt ptr = new X; + /// X* pX = ptr.get(); + /// pX->DoSomething(); + T* get() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + return mpArray; + } + + /// use_count + /// Returns the reference count on the owned pointer. + /// The return value is one if the owned pointer is null. + int use_count() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpRefCount); + return (int)*mpRefCount; + } + + /// unique + /// Returns true if the reference count on the owned pointer is one. + /// The return value is true if the owned pointer is null. + bool unique() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpRefCount); + return (*mpRefCount == 1); + } + + /// add_ref + /// Manually increments the reference count on the owned pointer. + /// This is currently disabled because it isn't in part of the + /// proposed C++ language addition. + /// int add_ref() + /// { + /// lock(); + /// // EAT_ASSERT(mpRefCount); + /// ++*mpRefCount; // Atomic operation + /// unlock(); + /// } + + /// release_ref + /// Manually increments the reference count on the owned pointer. + /// If the reference count becomes zero, then the owned pointer + /// is deleted and reset(0) is called. For any given instance of + /// shared_ptr, release_ref can only be called as many times as -- + /// but no more than -- the number of times add_ref was called + /// for that same shared_ptr. Otherwise, separate instances of + /// shared_ptr would be left with dangling owned pointer instances. + /// This is currently disabled because it isn't in part of the + /// proposed C++ language addition. + /// int release_ref() + /// { + /// lock(); + /// // EAT_ASSERT(mpRefCount); + /// if(*mpRefCount > 1){ + /// const int nReturnValue = --*mpRefCount; // Atomic operation + /// unlock(); + /// return nReturnValue; + /// } + /// reset(0); + /// unlock(); + /// return 0; + /// } + + /// Implicit operator bool + /// Allows for using a scoped_ptr as a boolean. + /// Example usage: + /// shared_array_mt ptr = new int(3); + /// if(ptr) + /// ++*ptr; + /// + /// Note that below we do not use operator bool(). The reason for this + /// is that booleans automatically convert up to short, int, float, etc. + /// The result is that this: if(scopedPtr == 1) would yield true (bad). + typedef T* (this_type::*bool_)() const; + operator bool_() const + { + // We don't lock here because this is essentially a read operation. + if(mpArray) + return &this_type::get; + return 0; + } + + /// operator! + /// This returns the opposite of operator bool; it returns true if + /// the owned pointer is null. Some compilers require this and some don't. + /// shared_array_mt ptr = new int(3); + /// if(!ptr) + /// EAT_ASSERT(false); + bool operator!() const + { + // We don't lock here because this is essentially a read operation. + return (mpArray == 0); + } + + }; // class shared_array_mt + + + /// get_pointer + /// returns shared_array_mt::get() via the input shared_array_mt. + template + inline T* get_pointer(const shared_array_mt& sharedArray) + { + return sharedArray.get(); + } + + /// swap + /// Exchanges the owned pointer beween two shared_array_mt objects. + /// This non-member version is useful for compatibility of shared_array_mt + /// objects with the C++ Standard Library and other libraries. + template + inline void swap(shared_array_mt& sharedArray1, shared_array_mt& sharedArray2) + { + sharedArray1.swap(sharedArray2); + } + + + /// operator!= + /// Compares two shared_array_mt objects for equality. Equality is defined as + /// being true when the pointer shared between two shared_array_mt objects is equal. + /// It is debatable what the appropriate definition of equality is between two + /// shared_array_mt objects, but we follow the current 2nd generation C++ standard proposal. + template + inline bool operator==(const shared_array_mt& sharedArray1, const shared_array_mt& sharedArray2) + { + // EAT_ASSERT((sharedArray1.get() != sharedArray2.get()) || (sharedArray1.use_count() == sharedArray2.use_count())); + return (sharedArray1.get() == sharedArray2.get()); + } + + + /// operator!= + /// Compares two shared_array_mt objects for inequality. Equality is defined as + /// being true when the pointer shared between two shared_array_mt objects is equal. + /// It is debatable what the appropriate definition of equality is between two + /// shared_array_mt objects, but we follow the current 2nd generation C++ standard proposal. + template + inline bool operator!=(const shared_array_mt& sharedArray1, const shared_array_mt& sharedArray2) + { + // EAT_ASSERT((sharedArray1.get() != sharedArray2.get()) || (sharedArray1.use_count() == sharedArray2.use_count())); + return (sharedArray1.get() != sharedArray2.get()); + } + + + /// operator< + /// Returns which shared_array_mt is 'less' than the other. Useful when storing + /// sorted containers of scoped_ptr objects. + template + inline bool operator<(const shared_array_mt& sharedArray1, const shared_array_mt& sharedArray2) + { + return (sharedArray1.get() < sharedArray2.get()); // Alternatively use: std::less(a.get(), b.get()); + } + + } // namespace Thread + +} // namespace EA + + + + +#endif // EATHREAD_SHARED_ARRAY_MT_H + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/shared_ptr_mt.h b/r5dev/thirdparty/ea/EAThread/include/eathread/shared_ptr_mt.h new file mode 100644 index 00000000..fe446e90 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/shared_ptr_mt.h @@ -0,0 +1,472 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// This is a multithread-safe version of shared_ptr_mt. +// For basic documentation, see shared_ptr_mt. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_SHARED_PTR_MT_H +#define EATHREAD_SHARED_PTR_MT_H + +#ifndef INCLUDED_eabase_H + #include +#endif +#ifndef EATHREAD_EATHREAD_FUTEX_H + #include +#endif +#ifndef EATHREAD_EATHREAD_ATOMIC_H + #include +#endif +// #include Temporarily disabled while we wait for compilers to modernize. // Declaration of std::auto_ptr. + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + + +/// namespace EA +/// The standard Electronic Arts namespace +namespace EA +{ + namespace Thread + { + /// class shared_ptr_mt + /// @brief Implements a thread-safe version of shared_ptr. + template + class shared_ptr_mt + { + private: + /// this_type + /// This is an alias for shared_ptr_mt, this class. + typedef shared_ptr_mt this_type; + + /// reference_count_type + /// An internal reference count type. Must be convertable to int + /// so that the public use_count function can work. + typedef EA::Thread::AtomicInt32 reference_count_type; + + T* mpValue; /// The owned pointer. + reference_count_type* mpRefCount; /// Reference count for owned pointer. + mutable Futex mMutex; /// Mutex guarding access to this class. + + public: + typedef T element_type; + typedef T value_type; + + /// shared_ptr_mt + /// Takes ownership of the pointer and sets the reference count + /// to the pointer to 1. It is OK if the input pointer is null. + /// The shared reference count is allocated on the heap via operator new. + /// If an exception occurs during the allocation of the shared + /// reference count, the owned pointer is deleted and the exception + /// is rethrown. A null pointer is given a reference count of 1. + explicit shared_ptr_mt(T* pValue = 0) + : mpValue(pValue), mMutex() + { + // We don't lock our mutex in this function, as this is the constructor + // and we assume that construction is already done in a thread-safe way + // by the owner of this object. + #if defined(EA_COMPILER_NO_EXCEPTIONS) || defined(EA_COMPILER_NO_UNWIND) + mpRefCount = new reference_count_type(1); + #else + EA_DISABLE_VC_WARNING(4571) + try + { + mpRefCount = new reference_count_type(1); + } + catch(...) + { + delete pValue; + //mpRefCount = 0; shouldn't be necessary. + throw; + } + EA_RESTORE_VC_WARNING() + #endif + } + + /// shared_ptr_mt + /// Shares ownership of a pointer with another instance of shared_ptr_mt. + /// This function increments the shared reference count on the pointer. + shared_ptr_mt(shared_ptr_mt const& sharedPtr) + : mMutex() + { + // We don't lock our mutex in this function, as this is the constructor + // and we assume that construction is already done in a thread-safe way + // by the owner of this object. + sharedPtr.lock(); + mpValue = sharedPtr.mpValue; + mpRefCount = sharedPtr.mpRefCount; + mpRefCount->Increment(); // Atomic operation + sharedPtr.unlock(); + } + + // Temporarily disabled while we wait for compilers to modernize. + // + // shared_ptr_mt + // Constructs a shared_ptr_mt from a std::auto_ptr. This class + // transfers ownership of the pointer from the auto_ptr by + // calling its release function. + // If an exception occurs during the allocation of the shared + // reference count, the owned pointer is deleted and the exception + // is rethrown. + //explicit shared_ptr_mt(std::auto_ptr& autoPtr) + // : mMutex() + //{ + // // We don't lock our mutex in this function, as this is the constructor + // // and we assume that construction is already done in a thread-safe way + // // by the owner of this object. + // mpValue = autoPtr.release(); + // + // #if defined(EA_COMPILER_NO_EXCEPTIONS) || defined(EA_COMPILER_NO_UNWIND) + // mpRefCount = new reference_count_type(1); + // #else + // try + // { + // mpRefCount = new reference_count_type(1); + // } + // catch(...) + // { + // delete mpValue; + // mpValue = 0; + // //mpRefCount = 0; shouldn't be necessary. + // throw; + // } + // #endif + //} + + /// ~shared_ptr_mt + /// Decrements the reference count for the owned pointer. If the + /// reference count goes to zero, the owned pointer is deleted and + /// the shared reference count is deleted. + ~shared_ptr_mt() + { + lock(); + const reference_count_type newRefCount(mpRefCount->Decrement()); // Atomic operation + // EAT_ASSERT(newRefCount >= 0); + if(newRefCount == 0) + { + // we should only be deleting the pointer if it is not null. It is possible that the + // user has created a shared ptr without passing in a value. + if (mpValue) + delete mpValue; + delete mpRefCount; + } + unlock(); + } + + /// operator= + /// Copies another shared_ptr_mt to this object. Note that this object + /// may already own a shared pointer with another different pointer + /// (but still of the same type) before this call. In that case, + /// this function releases the old pointer, decrementing its reference + /// count and deleting it if zero, takes shared ownership of the new + /// pointer and increments its reference count. + shared_ptr_mt& operator=(shared_ptr_mt const& sharedPtr) + { + // We don't lock mutexes here because we let the swap function + // below do the locking and assignment. The if statement below + // isn't protected within a lock operation because it wouldn't + // help by being so because if mpValue is changing during the + // the execution of this function then the user has an external + // race condition that needs to be managed at that level. + if(mpValue != sharedPtr.mpValue) + { + // The easiest thing to do is to create a temporary and + // copy ourselves ourselves into it. This is a standard + // method for switching pointer ownership in systems like this. + shared_ptr_mt(sharedPtr).swap(*this); + } + return *this; + } + + // Temporarily disabled while we wait for compilers to modernize. + // + // operator= + // Transfers ownership of a std::auto_ptr to this class. + //shared_ptr_mt& operator=(std::auto_ptr& autoPtr) + //{ + // // We don't lock any mutexes here because we let the swap function do that. + // // EAT_ASSERT(mpValue != autoPtr.get()); + // shared_ptr_mt(autoPtr).swap(*this); + // return *this; + //} + + // operator= + // We do not defined this function in order to maintain compatibility + // with the currently proposed (2003) C++ standard addition. Use reset instead. + // shared_ptr_mt& operator=(T* pValue); + // { + // reset(pValue); + // return *this; + // } + + /// lock + /// @brief Locks our mutex for thread-safe access. + /// It is a const function because const-ness refers to the underlying pointer being + /// held and not this class. + void lock() const + { + mMutex.Lock(); + } + + /// unlock + /// @brief Unlocks our mutex which was previous locked. + /// It is a const function because const-ness refers to the underlying pointer being + /// held and not this class. + void unlock() const + { + mMutex.Unlock(); + } + + /// reset + /// Releases the owned pointer and takes ownership of the + /// passed in pointer. If the passed in pointer is the same + /// as the owned pointer, nothing is done. The passed in pointer + /// can be null, in which case the use count is set to 1. + void reset(T* pValue = 0) + { + // We don't lock any mutexes here because we let the swap function do that. + // We don't lock for the 'if' statement below because that wouldn't really buy anything. + if(pValue != mpValue) + { + // The easiest thing to do is to create a temporary and + // copy ourselves ourselves into it. This is a standard + // method for switching pointer ownership in systems like this. + shared_ptr_mt(pValue).swap(*this); + } + } + + /// swap + /// Exchanges the owned pointer beween two shared_ptr_mt objects. + void swap(shared_ptr_mt& sharedPtr) + { + lock(); + sharedPtr.lock(); + + // std::swap(mpValue, sharedPtr.mpValue); // Not used so that we can reduce a dependency. + T* const pValue = sharedPtr.mpValue; + sharedPtr.mpValue = mpValue; + mpValue = pValue; + + // std::swap(mpRefCount, sharedPtr.mpRefCount); // Not used so that we can reduce a dependency. + reference_count_type* const pRefCount = sharedPtr.mpRefCount; + sharedPtr.mpRefCount = mpRefCount; + mpRefCount = pRefCount; + + sharedPtr.unlock(); + unlock(); + } + + /// operator* + /// Returns the owner pointer dereferenced. + /// Example usage: + /// shared_ptr_mt ptr = new int(3); + /// int x = *ptr; + T& operator*() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpValue); + return *mpValue; + } + + /// operator-> + /// Allows access to the owned pointer via operator->() + /// Example usage: + /// struct X{ void DoSomething(); }; + /// shared_ptr_mt ptr = new X; + /// ptr->DoSomething(); + T* operator->() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpValue); + return mpValue; + } + + /// get + /// Returns the owned pointer. Note that this class does + /// not provide an operator T() function. This is because such + /// a thing (automatic conversion) is deemed unsafe. + /// Example usage: + /// struct X{ void DoSomething(); }; + /// shared_ptr_mt ptr = new X; + /// X* pX = ptr.get(); + /// pX->DoSomething(); + T* get() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + return mpValue; + } + + /// use_count + /// Returns the reference count on the owned pointer. + /// The return value is one if the owned pointer is null. + int use_count() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpRefCount); + return (int)*mpRefCount; + } + + /// unique + /// Returns true if the reference count on the owned pointer is one. + /// The return value is true if the owned pointer is null. + bool unique() const + { + // We don't lock here because this is essentially a read operation. + // We don't put a SMP read barrier here because we assume the caller does such things. + // EAT_ASSERT(mpRefCount); + return (*mpRefCount == 1); + } + + /// add_ref + /// Manually increments the reference count on the owned pointer. + /// This is currently disabled because it isn't in part of the + /// proposed C++ language addition. + /// int add_ref() + /// { + /// lock(); + /// // EAT_ASSERT(mpRefCount); + /// ++*mpRefCount; // Atomic operation + /// unlock(); + /// } + + /// release_ref + /// Manually increments the reference count on the owned pointer. + /// If the reference count becomes zero, then the owned pointer + /// is deleted and reset(0) is called. For any given instance of + /// shared_ptr_mt, release_ref can only be called as many times as -- + /// but no more than -- the number of times add_ref was called + /// for that same shared_ptr_mt. Otherwise, separate instances of + /// shared_ptr_mt would be left with dangling owned pointer instances. + /// This is currently disabled because it isn't in part of the + /// proposed C++ language addition. + /// int release_ref() + /// { + /// lock(); + /// // EAT_ASSERT(mpRefCount); + /// if(*mpRefCount > 1){ + /// const int nReturnValue = --*mpRefCount; // Atomic operation + /// unlock(); + /// return nReturnValue; + /// } + /// reset(0); + /// unlock(); + /// return 0; + /// } + + /// Implicit operator bool + /// Allows for using a scoped_ptr as a boolean. + /// Example usage: + /// shared_ptr_mt ptr = new int(3); + /// if(ptr) + /// ++*ptr; + /// + /// Note that below we do not use operator bool(). The reason for this + /// is that booleans automatically convert up to short, int, float, etc. + /// The result is that this: if(scopedPtr == 1) would yield true (bad). + typedef T* (this_type::*bool_)() const; + operator bool_() const + { + // We don't lock here because this is essentially a read operation. + if(mpValue) + return &this_type::get; + return 0; + } + + /// operator! + /// This returns the opposite of operator bool; it returns true if + /// the owned pointer is null. Some compilers require this and some don't. + /// shared_ptr_mt ptr = new int(3); + /// if(!ptr) + /// EAT_ASSERT(false); + bool operator!() const + { + // We don't lock here because this is essentially a read operation. + return (mpValue == 0); + } + + }; // class shared_ptr_mt + + + /// get_pointer + /// returns shared_ptr_mt::get() via the input shared_ptr_mt. + template + inline T* get_pointer(const shared_ptr_mt& sharedPtr) + { + return sharedPtr.get(); + } + + /// swap + /// Exchanges the owned pointer beween two shared_ptr_mt objects. + /// This non-member version is useful for compatibility of shared_ptr_mt + /// objects with the C++ Standard Library and other libraries. + template + inline void swap(shared_ptr_mt& sharedPtr1, shared_ptr_mt& sharedPtr2) + { + sharedPtr1.swap(sharedPtr2); + } + + + /// operator!= + /// Compares two shared_ptr_mt objects for equality. Equality is defined as + /// being true when the pointer shared between two shared_ptr_mt objects is equal. + /// It is debatable what the appropriate definition of equality is between two + /// shared_ptr_mt objects, but we follow the current 2nd generation C++ standard proposal. + template + inline bool operator==(const shared_ptr_mt& sharedPtr1, const shared_ptr_mt& sharedPtr2) + { + // EAT_ASSERT((sharedPtr1.get() != sharedPtr2.get()) || (sharedPtr1.use_count() == sharedPtr2.use_count())); + return (sharedPtr1.get() == sharedPtr2.get()); + } + + + /// operator!= + /// Compares two shared_ptr_mt objects for inequality. Equality is defined as + /// being true when the pointer shared between two shared_ptr_mt objects is equal. + /// It is debatable what the appropriate definition of equality is between two + /// shared_ptr_mt objects, but we follow the current 2nd generation C++ standard proposal. + template + inline bool operator!=(const shared_ptr_mt& sharedPtr1, const shared_ptr_mt& sharedPtr2) + { + // EAT_ASSERT((sharedPtr1.get() != sharedPtr2.get()) || (sharedPtr1.use_count() == sharedPtr2.use_count())); + return (sharedPtr1.get() != sharedPtr2.get()); + } + + + /// operator< + /// Returns which shared_ptr_mt is 'less' than the other. Useful when storing + /// sorted containers of shared_ptr_mt objects. + template + inline bool operator<(const shared_ptr_mt& sharedPtr1, const shared_ptr_mt& sharedPtr2) + { + return (sharedPtr1.get() < sharedPtr2.get()); // Alternatively use: std::less(a.get(), b.get()); + } + + } // namespace Thread + +} // namespace EA + + + + +#endif // EATHREAD_SHARED_PTR_MT_H + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/version.h b/r5dev/thirdparty/ea/EAThread/include/eathread/version.h new file mode 100644 index 00000000..3568b708 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/version.h @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_VERSION_H +#define EATHREAD_VERSION_H + + +#include + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + + + +namespace EA +{ + namespace Thread + { + /// Version contains the version of the library when it was built. + /// This can be used to verify the correct version has been linked + /// into the executable or loaded by the O/S (in the case of a DLL). + struct Version + { + int mMajor; + int mMinor; + int mPatch; + }; + + /// Get the library version information. + EATHREADLIB_API const Version *GetVersion(); + + /// Check that the linked/loaded library is the same as the headers + /// are expecting. + /// + /// If the version numbers passed to CheckVersion match those + /// built into the library when it was compiled, true is returned. + /// If not, false is returned. + EATHREADLIB_API bool CheckVersion(int majorVersion, int minorVersion, int patchVersion); + + } + +} + +#endif diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/x86-64/eathread_atomic_x86-64.h b/r5dev/thirdparty/ea/EAThread/include/eathread/x86-64/eathread_atomic_x86-64.h new file mode 100644 index 00000000..5e443fc1 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/x86-64/eathread_atomic_x86-64.h @@ -0,0 +1,456 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +///////////////////////////////////////////////////////////////////////////// +// Defines functionality for threadsafe primitive operations. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_X86_64_EATHREAD_ATOMIC_X86_64_H +#define EATHREAD_X86_64_EATHREAD_ATOMIC_X86_64_H + +#include "EABase/eabase.h" +#include +#include + + +#ifdef EA_COMPILER_MSVC + EA_DISABLE_ALL_VC_WARNINGS() + #include // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h. + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif + +EA_DISABLE_VC_WARNING(4146) // unary minus operator applied to unsigned type, result still unsigned + +#if defined(EA_PROCESSOR_X86_64) + + #define EA_THREAD_ATOMIC_IMPLEMENTED + + namespace EA + { + namespace Thread + { + /// + /// Non-member 128-bit Atomics implementation + /// + #if (EA_COMPILER_MSVC >= 1500) // VS2008+ + + #define EATHREAD_ATOMIC_128_SUPPORTED 1 + + // Algorithm for implementing an arbitrary atomic modification via AtomicCompareAndSwap: + // int128_t oldValue; + // + // do { + // oldValue = AtomicGetValue(dest); + // newValue = + // } while(!AtomicCompareAndSwap(dest, oldValue, newValue)); + + // The following function is a wrapper for the Microsoft _InterlockedCompareExchange128 function. + // Early versions of AMD 64-bit hardware do not support 128 bit atomics. To check for hardware support + // for the cmpxchg16b instruction, call the __cpuid intrinsic with InfoType=0x00000001 (standard function 1). + // Bit 13 of CPUInfo[2] (ECX) is 1 if the instruction is supported. + + inline bool AtomicSetValueConditionall28(volatile int64_t* dest128, const int64_t* value128, const int64_t* condition128) + { + __int64 conditionCopy[2] = { condition128[0], condition128[1] }; // We make a copy because Microsoft modifies the output, which is inconsistent with the rest of our atomic API. + return _InterlockedCompareExchange128(dest128, value128[1], value128[0], conditionCopy) == 1; // Question: Do we need to reverse the order of value128 if running on big-endian? Microsoft's documentation currently doesn't address this. + } + + inline bool AtomicSetValueConditionall28(volatile uint64_t* dest128, const uint64_t* value128, const uint64_t* condition128) + { + __int64 conditionCopy[2] = { (int64_t) condition128[0], (int64_t)condition128[1] }; // We make a copy because Microsoft modifies the output, which is inconsistent with the rest of our atomic API. + return _InterlockedCompareExchange128((volatile int64_t*)dest128, (int64_t)value128[1], (int64_t)value128[0], conditionCopy) == 1; // Question: Do we need to reverse the order of value128 if running on big-endian? Microsoft's documentation currently doesn't address this. + } + + #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + + #if defined(EA_COMPILER_CLANG) || (defined(EA_COMPILER_GNUC) && EA_COMPILER_VERSION >= 4003) // GCC 4.3 or later for 128 bit atomics + + #define EATHREAD_ATOMIC_128_SUPPORTED 1 + + // GCC on x64 implements all of its __sync functions below via the cmpxchg16b instruction, + // usually in the form of a loop. + // Use of 128 bit atomics on GCC requires compiling with the -mcx16 compiler argument. + // See http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html. + + inline __int128_t AtomicGetValue(volatile __int128_t* source) + { + return __sync_add_and_fetch(source, __int128_t(0)); // Is there a better way to do an atomic read? + } + + inline void AtomicSetValue(volatile __int128_t* dest, __int128_t value) + { + __sync_lock_test_and_set(dest, value); + } + + inline __int128_t AtomicIncrement(volatile __int128_t* dest) + { + return __sync_add_and_fetch(dest, __int128_t(1)); + } + + inline __int128_t AtomicDecrement(volatile __int128_t* dest) + { + return __sync_add_and_fetch(dest, __int128_t(-1)); + } + + inline __int128_t AtomicAdd(volatile __int128_t* dest, __int128_t value) + { + return __sync_add_and_fetch(dest, value); + } + + inline __int128_t AtomicOr(volatile __int128_t* dest, __int128_t value) + { + return __sync_or_and_fetch(dest, value); + } + + inline __int128_t AtomicAnd(volatile __int128_t* dest, __int128_t value) + { + return __sync_and_and_fetch(dest, value); + } + + inline __int128_t AtomicXor(volatile __int128_t* dest, __int128_t value) + { + return __sync_xor_and_fetch(dest, value); + } + + inline __int128_t AtomicSwap(volatile __int128_t* dest, __int128_t value) + { + return __sync_lock_test_and_set(dest, value); + } + + inline bool AtomicSetValueConditional(volatile __int128_t* dest, __int128_t value, __int128_t condition) + { + return __sync_bool_compare_and_swap(dest, condition, value); + } + + inline bool AtomicSetValueConditional(volatile __uint128_t* dest, __uint128_t value, __uint128_t condition) + { + return __sync_bool_compare_and_swap(dest, condition, value); + } + + // The following 64-bit-based 128 bit atomic is provided for compatibility with the Microsoft version. + // GCC supports the native __int128_t data type and thus can support a 128-bit-based 128 bit atomic. + + inline bool AtomicSetValueConditionall28(volatile int64_t* dest128, const int64_t* value128, const int64_t* condition128) + { + // Use of this requires compiling with the -mcx16 compiler argument. See http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html. + return __sync_bool_compare_and_swap((volatile __int128_t*)dest128, *(volatile __int128_t*)condition128, *(volatile __int128_t*)value128); + } + + inline bool AtomicSetValueConditionall28(volatile uint64_t* dest128, const uint64_t* value128, const uint64_t* condition128) + { + // Use of this requires compiling with the -mcx16 compiler argument. See http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html. + return __sync_bool_compare_and_swap((volatile __uint128_t*)dest128, *(volatile __uint128_t*)condition128, *(volatile __uint128_t*)value128); + } + + #endif + + #endif + + + + /// class AtomicInt + /// Actual implementation may vary per platform. May require certain alignments, sizes, + /// and declaration specifications per platform. + + template + class AtomicInt + { + public: + typedef AtomicInt ThisType; + typedef T ValueType; + + /// AtomicInt + /// Empty constructor. Intentionally leaves mValue in an unspecified state. + /// This is done so that an AtomicInt acts like a standard built-in integer. + AtomicInt() + {} + + AtomicInt(ValueType n) + { SetValue(n); } + + AtomicInt(const ThisType& x) + : mValue(x.GetValue()) {} + + AtomicInt& operator=(const ThisType& x) + { mValue = x.GetValue(); return *this; } + + ValueType GetValueRaw() const + { return mValue; } + + ValueType GetValue() const; + ValueType SetValue(ValueType n); + bool SetValueConditional(ValueType n, ValueType condition); + ValueType Increment(); + ValueType Decrement(); + ValueType Add(ValueType n); + + // operators + inline operator const ValueType() const { return GetValue(); } // Should this be provided? Is it safe enough? Return value of 'const' attempts to make this safe from misuse. + inline ValueType operator =(ValueType n) { SetValue(n); return n; } + inline ValueType operator+=(ValueType n) { return Add(n);} + inline ValueType operator-=(ValueType n) { return Add(-n);} + inline ValueType operator++() { return Increment();} + inline ValueType operator++(int) { return Increment() - 1;} + inline ValueType operator--() { return Decrement(); } + inline ValueType operator--(int) { return Decrement() + 1;} + + protected: + volatile ValueType mValue; + }; + + + #if defined(EA_COMPILER_MSVC) + #pragma intrinsic(_InterlockedExchange) + #pragma intrinsic(_InterlockedExchangeAdd) + #pragma intrinsic(_InterlockedCompareExchange) + #pragma intrinsic(_InterlockedIncrement) + #pragma intrinsic(_InterlockedDecrement) + #pragma intrinsic(_InterlockedExchange64) + #pragma intrinsic(_InterlockedExchangeAdd64) + #pragma intrinsic(_InterlockedCompareExchange64) + #pragma intrinsic(_InterlockedIncrement64) + #pragma intrinsic(_InterlockedDecrement64) + + // The following should work under any compiler, including such compilers as GCC under + // WINE or some other Win32 emulation. Win32 InterlockedXXX functions must exist on + // any system that supports the Windows API, be it 32 or 64 bit Windows. + + // 32 bit versions + template<> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return (ValueType)_InterlockedExchangeAdd((long*)&mValue, 0); } // We shouldn't need to do this, as far as I know, given the x86 architecture. + + template<> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return (ValueType)_InterlockedExchangeAdd((long*)&mValue, 0); } // We shouldn't need to do this, as far as I know, given the x86 architecture. + + template<> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { return (ValueType)_InterlockedExchange((long*)&mValue, (long)n); } // Even though we shouldn't need to use _InterlockedExchange on x86, the intrinsic x86 _InterlockedExchange is at least as fast as C code we would otherwise put here. + + template<> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { return (ValueType)_InterlockedExchange((long*)&mValue, (long)n); } // Even though we shouldn't need to use _InterlockedExchange on x86, the intrinsic x86 _InterlockedExchange is at least as fast as C code we would otherwise put here. + + template<> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return ((ValueType)_InterlockedCompareExchange((long*)&mValue, (long)n, (long)condition) == condition); } + + template<> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return ((ValueType)_InterlockedCompareExchange((long*)&mValue, (long)n, (long)condition) == condition); } + + template<> inline + AtomicInt::ValueType AtomicInt::Increment() + { return (ValueType)_InterlockedIncrement((long*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Increment() + { return (ValueType)_InterlockedIncrement((long*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return (ValueType)_InterlockedDecrement((long*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return (ValueType)_InterlockedDecrement((long*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return ((ValueType)_InterlockedExchangeAdd((long*)&mValue, (long)n) + n); } + + template<> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return ((ValueType)_InterlockedExchangeAdd((long*)&mValue, (long)n) + n); } + + + + // 64 bit versions + template<> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return (ValueType)_InterlockedExchangeAdd64((__int64*)&mValue, 0); } // We shouldn't need to do this, as far as I know, given the x86 architecture. + + template<> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return (ValueType)_InterlockedExchangeAdd64((__int64*)&mValue, 0); } // We shouldn't need to do this, as far as I know, given the x86 architecture. + + template<> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { return (ValueType)_InterlockedExchange64((__int64*)&mValue, (__int64)n); } // Even though we shouldn't need to use _InterlockedExchange on x86, the intrinsic x86 _InterlockedExchange is at least as fast as C code we would otherwise put here. + + template<> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { return (ValueType)_InterlockedExchange64((__int64*)&mValue, (__int64)n); } // Even though we shouldn't need to use _InterlockedExchange on x86, the intrinsic x86 _InterlockedExchange is at least as fast as C code we would otherwise put here. + + template<> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return ((ValueType)_InterlockedCompareExchange64((__int64*)&mValue, (__int64)n, (__int64)condition) == condition); } + + template<> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return ((ValueType)_InterlockedCompareExchange64((__int64*)&mValue, (__int64)n, (__int64)condition) == condition); } + + template<> inline + AtomicInt::ValueType AtomicInt::Increment() + { return (ValueType)_InterlockedIncrement64((__int64*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Increment() + { return (ValueType)_InterlockedIncrement64((__int64*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return (ValueType)_InterlockedDecrement64((__int64*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return (ValueType)_InterlockedDecrement64((__int64*)&mValue); } + + template<> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return ((ValueType)_InterlockedExchangeAdd64((__int64*)&mValue, (__int64)n) + n); } + + template<> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return ((ValueType)_InterlockedExchangeAdd64((__int64*)&mValue, (__int64)n) + n); } + + + #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + + // Recent versions of GCC have atomic primitives built into the compiler and standard library. + #if defined(EA_COMPILER_CLANG) || (defined(EA_COMPILER_GNUC) && EA_COMPILER_VERSION >= 4001) // GCC 4.1 or later + + template <> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return __sync_add_and_fetch(const_cast(&mValue), 0); } + + template <> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return __sync_add_and_fetch(const_cast(&mValue), 0); } + + template <> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { __sync_synchronize(); return __sync_lock_test_and_set(&mValue, n); } + + template <> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { __sync_synchronize(); return __sync_lock_test_and_set(&mValue, n); } + + template <> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return (__sync_val_compare_and_swap(&mValue, condition, n) == condition); } + + template <> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return (__sync_val_compare_and_swap(&mValue, condition, n) == condition); } + + template <> inline + AtomicInt::ValueType AtomicInt::Increment() + { return __sync_add_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Increment() + { return __sync_add_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return __sync_sub_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return __sync_sub_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return __sync_add_and_fetch(&mValue, n); } + + template <> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return __sync_add_and_fetch(&mValue, n); } + + + + template <> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return __sync_add_and_fetch(const_cast(&mValue), 0); } + + template <> inline + AtomicInt::ValueType AtomicInt::GetValue() const + { return __sync_add_and_fetch(const_cast(&mValue), 0); } + + template <> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { __sync_synchronize(); return __sync_lock_test_and_set(&mValue, n); } + + template <> inline + AtomicInt::ValueType AtomicInt::SetValue(ValueType n) + { __sync_synchronize(); return __sync_lock_test_and_set(&mValue, n); } + + template <> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return (__sync_val_compare_and_swap(&mValue, condition, n) == condition); } + + template <> inline + bool AtomicInt::SetValueConditional(ValueType n, ValueType condition) + { return (__sync_val_compare_and_swap(&mValue, condition, n) == condition); } + + template <> inline + AtomicInt::ValueType AtomicInt::Increment() + { return __sync_add_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Increment() + { return __sync_add_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return __sync_sub_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Decrement() + { return __sync_sub_and_fetch(&mValue, 1); } + + template <> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return __sync_add_and_fetch(&mValue, n); } + + template <> inline + AtomicInt::ValueType AtomicInt::Add(ValueType n) + { return __sync_add_and_fetch(&mValue, n); } + + #endif // GCC 4.1 or later + + #endif // GCC + + } // namespace Thread + + + } // namespace EA + + +#endif // EA_PROCESSOR_X86_64 + +EA_RESTORE_VC_WARNING() + +#endif // EATHREAD_X86_64_EATHREAD_ATOMIC_X86_64_H + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/include/eathread/x86-64/eathread_sync_x86-64.h b/r5dev/thirdparty/ea/EAThread/include/eathread/x86-64/eathread_sync_x86-64.h new file mode 100644 index 00000000..45d546f5 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/include/eathread/x86-64/eathread_sync_x86-64.h @@ -0,0 +1,108 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PRAGMA_ONCE_SUPPORTED) + #pragma once // Some compilers (e.g. VC++) benefit significantly from using this. We've measured 3-4% build speed improvements in apps as a result. +#endif + +///////////////////////////////////////////////////////////////////////////// +// Functionality related to memory and code generation synchronization. +///////////////////////////////////////////////////////////////////////////// + + +#ifndef EATHREAD_X86_64_EATHREAD_SYNC_X86_64_H +#define EATHREAD_X86_64_EATHREAD_SYNC_X86_64_H + + +#ifndef INCLUDED_eabase_H + #include "EABase/eabase.h" +#endif + + +#if defined(EA_PROCESSOR_X86_64) + #define EA_THREAD_SYNC_IMPLEMENTED + + #ifdef EA_COMPILER_MSVC + EA_DISABLE_ALL_VC_WARNINGS() + #include // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h. + #include + EA_RESTORE_ALL_VC_WARNINGS() + #endif + + // By default, we define EA_TARGET_SMP to be true. The reason for this is that most + // applications that users of this code are likely to write are going to be executables + // which run properly on any system, be it multiprocessing or not. + #ifndef EA_TARGET_SMP + #define EA_TARGET_SMP 1 + #endif + + // EAProcessorPause + // Intel has defined a 'pause' instruction for x86 processors starting with the P4, though this simply + // maps to the otherwise undocumented 'rep nop' instruction. This pause instruction is important for + // high performance spinning, as otherwise a high performance penalty incurs. + + #if defined(EA_COMPILER_MSVC) || defined(EA_COMPILER_INTEL) || defined(EA_COMPILER_BORLAND) + // Year 2003+ versions of the Microsoft SDK define 'rep nop' as YieldProcessor and/or __yield or _mm_pause. + #pragma intrinsic(_mm_pause) + #define EAProcessorPause() _mm_pause() // The __yield() intrinsic currently doesn't work on x86-64. + #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + #define EAProcessorPause() __asm__ __volatile__ ("rep ; nop") + #else + // In this case we use an Intel-style asm statement. If this doesn't work for your compiler then + // there most likely is some way to make the `rep nop` inline asm statement. + #define EAProcessorPause() __asm { rep nop } // Alternatively: { __asm { _emit 0xf3 }; __asm { _emit 0x90 } } + #endif + + + // EAReadBarrier / EAWriteBarrier / EAReadWriteBarrier + // The x86 processor memory architecture ensures read and write consistency on both single and + // multi processing systems. This makes programming simpler but limits maximimum system performance. + // We define EAReadBarrier here to be the same as EACompilerMemory barrier in order to limit the + // compiler from making any assumptions at its level about memory usage. Year 2003+ versions of the + // Microsoft SDK define a 'MemoryBarrier' statement which has the same effect as EAReadWriteBarrier. + #if defined(EA_COMPILER_MSVC) + #pragma intrinsic(_ReadBarrier) + #pragma intrinsic(_WriteBarrier) + #pragma intrinsic(_ReadWriteBarrier) + + #define EAReadBarrier() _ReadBarrier() + #define EAWriteBarrier() _WriteBarrier() + #define EAReadWriteBarrier() _ReadWriteBarrier() + #elif defined(EA_PLATFORM_PS4) + #define EAReadBarrier() __asm__ __volatile__ ("lfence" ::: "memory"); + #define EAWriteBarrier() __asm__ __volatile__ ("sfence" ::: "memory"); + #define EAReadWriteBarrier() __asm__ __volatile__ ("mfence" ::: "memory"); + #elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 401) // GCC 4.1 or later, includes clang + #define EAReadBarrier __sync_synchronize + #define EAWriteBarrier __sync_synchronize + #define EAReadWriteBarrier __sync_synchronize + #else + #define EAReadBarrier EACompilerMemoryBarrier // Need to implement this for non-VC++ + #define EAWriteBarrier EACompilerMemoryBarrier // Need to implement this for non-VC++ + #define EAReadWriteBarrier EACompilerMemoryBarrier // Need to implement this for non-VC++ + #endif + + + // EACompilerMemoryBarrier + #if defined(EA_COMPILER_MSVC) + #define EACompilerMemoryBarrier() _ReadWriteBarrier() + #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + #define EACompilerMemoryBarrier() __asm__ __volatile__ ("":::"memory") + #else + #define EACompilerMemoryBarrier() // Possibly `EAT_ASSERT(false)` here? + #endif + + +#endif // EA_PROCESSOR_X86 + + +#endif // EATHREAD_X86_64_EATHREAD_SYNC_X86_64_H + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_cpp11.cpp b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_cpp11.cpp new file mode 100644 index 00000000..b781961a --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_cpp11.cpp @@ -0,0 +1,217 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include + +#include "eathread/eathread.h" +#include "eathread/eathread_thread.h" + +#include +#include +#include + +namespace EA +{ + namespace Thread + { + EA::Thread::AssertionFailureFunction gpAssertionFailureFunction = NULL; + void* gpAssertionFailureContext = NULL; + + EATHREADLIB_API ThreadId EA::Thread::GetThreadId() + { + return std::this_thread::get_id(); + } + + EATHREADLIB_API ThreadId EA::Thread::GetThreadId(EA::Thread::SysThreadId id) + { + EAThreadDynamicData* const pTDD = EA::Thread::FindThreadDynamicData(id); + if(pTDD) + { + return pTDD->mpComp->mThread.get_id(); + } + + return EA::Thread::kThreadIdInvalid; + } + + EATHREADLIB_API SysThreadId EA::Thread::GetSysThreadId(ThreadId threadId) + { + EAThreadDynamicData* tdd = EA::Thread::FindThreadDynamicData(threadId); + if (tdd && tdd->mpComp) + return tdd->mpComp->mThread.native_handle(); + + ThreadId threadIdCurrent = GetThreadId(); + if(threadId == threadIdCurrent) + { + #if defined(EA_PLATFORM_MICROSOFT) + std::thread::id stdId = std::this_thread::get_id(); + EAT_COMPILETIME_ASSERT(sizeof(_Thrd_t) == sizeof(std::thread::id)); + return ((_Thrd_t&)stdId)._Hnd; + #elif EA_POSIX_THREADS_AVAILABLE && defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) + std::thread::id stdId = std::this_thread::get_id(); + EAT_COMPILETIME_ASSERT(sizeof(_Thrd_t) == sizeof(std::thread::id)); + return reinterpret_cast<_Thrd_t>(stdId); + #else + #error Platform not supported yet. + #endif + } + + EAT_ASSERT_MSG(false, "Failed to find associated EAThreadDynamicData for this thread.\n"); + return SysThreadId(); + } + + EATHREADLIB_API SysThreadId EA::Thread::GetSysThreadId() + { + // There currently isn't a means to directly get the current SysThreadId, so we do it indirectly: + return GetSysThreadId(std::this_thread::get_id()); + } + + EATHREADLIB_API ThreadTime EA::Thread::GetThreadTime() + { + using namespace std::chrono; + auto nowMs = duration_cast(system_clock::now().time_since_epoch()); + return nowMs.count(); + } + + EATHREADLIB_API int GetThreadPriority() + { + // No way to query or set thread priority through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs + return kThreadPriorityDefault; + } + + EATHREADLIB_API bool SetThreadPriority(int nPriority) + { + // No way to query or set thread priority through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs + return false; + } + + EATHREADLIB_API void SetThreadProcessor(int nProcessor) + { + // No way to query or set thread processor through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs + } + + EATHREADLIB_API int GetThreadProcessor() + { + // No way to query or set thread processor through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs + return 0; + } + + EATHREADLIB_API int GetProcessorCount() + { + return static_cast(std::thread::hardware_concurrency()); + } + + EATHREADLIB_API void ThreadSleep(const ThreadTime& timeRelative) + { + std::this_thread::sleep_for(std::chrono::milliseconds(timeRelative)); + } + + void ThreadEnd(intptr_t threadReturnValue) + { + // No way to end a thread through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs + EAT_ASSERT_MSG(false, "ThreadEnd is not implemented for C++11 threads.\n"); + } + + EATHREADLIB_API void EA::Thread::SetThreadAffinityMask(const EA::Thread::ThreadId& id, ThreadAffinityMask nAffinityMask) + { + // Update the affinity mask in the thread dynamic data cache. + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + pTDD->mnThreadAffinityMask = nAffinityMask; + } + + #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED + // Call the Windows library function. + #endif + } + + EATHREADLIB_API EA::Thread::ThreadAffinityMask EA::Thread::GetThreadAffinityMask(const EA::Thread::ThreadId& id) + { + // Update the affinity mask in the thread dynamic data cache. + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + return pTDD->mnThreadAffinityMask; + } + + return kThreadAffinityMaskAny; + } + + EATHREADLIB_API void SetAssertionFailureFunction(AssertionFailureFunction pAssertionFailureFunction, void* pContext) + { + gpAssertionFailureFunction = pAssertionFailureFunction; + gpAssertionFailureContext = pContext; + } + + EATHREADLIB_API void AssertionFailure(const char* pExpression) + { + if(gpAssertionFailureFunction) + gpAssertionFailureFunction(pExpression, gpAssertionFailureContext); + } + + void* GetThreadStackBase() + { + return nullptr; + } + + // This can be removed once all remaining synchronization primitives are implemented in terms of C++11 APIs + uint32_t EA::Thread::RelativeTimeoutFromAbsoluteTimeout(ThreadTime timeoutAbsolute) + { + EAT_ASSERT((timeoutAbsolute == kTimeoutImmediate) || (timeoutAbsolute > EATHREAD_MIN_ABSOLUTE_TIME)); // Assert that the user didn't make the mistake of treating time as relative instead of absolute. + + DWORD timeoutRelative = 0; + + if (timeoutAbsolute == kTimeoutNone) + { + timeoutRelative = 0xffffffff; + } + else if (timeoutAbsolute == kTimeoutImmediate) + { + timeoutRelative = 0; + } + else + { + ThreadTime timeCurrent(GetThreadTime()); + timeoutRelative = (timeoutAbsolute > timeCurrent) ? static_cast(timeoutAbsolute - timeCurrent) : 0; + } + + EAT_ASSERT((timeoutRelative == 0xffffffff) || (timeoutRelative < 100000000)); // Assert that the timeout is a sane value and didn't wrap around. + + return timeoutRelative; + } + + // Implement native_handle_type comparison as a memcmp() - may need platform specific implementations on some future platforms. + bool Equals(const SysThreadId& a, const SysThreadId& b) + { + static_assert((std::is_fundamental::value || std::is_pointer::value || std::is_pod::value), + "SysThreadId should be comparable using memcmp()"); + return memcmp(&a, &b, sizeof(SysThreadId)) == 0; + } + + namespace detail + { + // Override the default EAThreadToString implementation + #define EAThreadIdToString_CUSTOM_IMPLEMENTATION + ThreadIdToStringBuffer::ThreadIdToStringBuffer(EA::Thread::ThreadId threadId) + { + std::stringstream formatStream; + formatStream << threadId; + strncpy(mBuf, formatStream.str().c_str(), BufSize - 1); + mBuf[BufSize - 1] = '\0'; + } + + SysThreadIdToStringBuffer::SysThreadIdToStringBuffer(EA::Thread::SysThreadId sysThreadId) + { + strncpy(mBuf, "Unknown", BufSize - 1); + mBuf[BufSize - 1] = '\0'; + } + } + } +} + diff --git a/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_mutex_cpp11.cpp b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_mutex_cpp11.cpp new file mode 100644 index 00000000..f3182e52 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_mutex_cpp11.cpp @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "eathread/eathread_mutex.h" + +EAMutexData::EAMutexData() : mnLockCount(0) {} + +EA::Thread::MutexParameters::MutexParameters(bool /*bIntraProcess*/, const char* pName) +{ + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + { + mName[0] = 0; + } +} + +EA::Thread::Mutex::Mutex(const MutexParameters* pMutexParameters, bool bDefaultParameters) +{ + if(!pMutexParameters && bDefaultParameters) + { + MutexParameters parameters; + Init(¶meters); + } + else + { + Init(pMutexParameters); + } +} + +EA::Thread::Mutex::~Mutex() +{ + EAT_ASSERT(mMutexData.mnLockCount == 0); +} + +bool EA::Thread::Mutex::Init(const MutexParameters* pMutexParameters) +{ + if (pMutexParameters) + { + mMutexData.mnLockCount = 0; + return true; + } + return false; +} + +int EA::Thread::Mutex::Lock(const ThreadTime& timeoutAbsolute) +{ + if (timeoutAbsolute == kTimeoutNone) + { + mMutexData.mMutex.lock(); + } + else + { + std::chrono::milliseconds timeoutAbsoluteMs(timeoutAbsolute); + std::chrono::time_point timeout_time(timeoutAbsoluteMs); + if (!mMutexData.mMutex.try_lock_until(timeout_time)) + { + return kResultTimeout; + } + } + + EAT_ASSERT((mMutexData.mThreadId = EA::Thread::GetThreadId()) != kThreadIdInvalid); + EAT_ASSERT(mMutexData.mnLockCount >= 0); + + return ++mMutexData.mnLockCount; // This is safe to do because we have the lock. +} + +int EA::Thread::Mutex::Unlock() +{ + EAT_ASSERT(mMutexData.mThreadId == EA::Thread::GetThreadId()); + EAT_ASSERT(mMutexData.mnLockCount > 0); + + const int nReturnValue(--mMutexData.mnLockCount); // This is safe to do because we have the lock. + mMutexData.mMutex.unlock(); + return nReturnValue; +} + +int EA::Thread::Mutex::GetLockCount() const +{ + return mMutexData.mnLockCount; +} + +bool EA::Thread::Mutex::HasLock() const +{ +#if EAT_ASSERT_ENABLED + return (mMutexData.mnLockCount > 0) && (mMutexData.mThreadId == EA::Thread::GetThreadId()); +#else + return (mMutexData.mnLockCount > 0); // This is the best we can do, though it is of limited use, since it doesn't tell you if you are the thread with the lock. +#endif +} + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_semaphore_cpp11.cpp b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_semaphore_cpp11.cpp new file mode 100644 index 00000000..79b7d554 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_semaphore_cpp11.cpp @@ -0,0 +1,5 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "eathread/eathread_semaphore.h" diff --git a/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_thread_cpp11.cpp b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_thread_cpp11.cpp new file mode 100644 index 00000000..53211070 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/cpp11/eathread_thread_cpp11.cpp @@ -0,0 +1,488 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "eathread/eathread_thread.h" +#include "eathread/eathread.h" +#include "eathread/eathread_sync.h" +#include "eathread/eathread_callstack.h" +#include "eathread/internal/eathread_global.h" + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + + static AtomicInt32 nLastProcessor = 0; + const size_t kMaxThreadDynamicDataCount = 128; + + struct EAThreadGlobalVars + { + char gThreadDynamicData[kMaxThreadDynamicDataCount][sizeof(EAThreadDynamicData)]; + AtomicInt32 gThreadDynamicDataAllocated[kMaxThreadDynamicDataCount]; + Mutex gThreadDynamicMutex; + }; + EATHREAD_GLOBALVARS_CREATE_INSTANCE; + + EAThreadDynamicData* AllocateThreadDynamicData() + { + for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i) + { + if (EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[i].SetValueConditional(1, 0)) + return (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + } + + // This is a safety fallback mechanism. In practice it won't be used in almost all situations. + if (gpAllocator) + return (EAThreadDynamicData*)gpAllocator->Alloc(sizeof(EAThreadDynamicData)); + + return nullptr; + } + + void FreeThreadDynamicData(EAThreadDynamicData* pEAThreadDynamicData) + { + pEAThreadDynamicData->~EAThreadDynamicData(); + if ((pEAThreadDynamicData >= (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData) && (pEAThreadDynamicData < ((EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData + kMaxThreadDynamicDataCount))) + { + EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[pEAThreadDynamicData - (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData].SetValue(0); + } + else + { + // Assume the data was allocated via the fallback mechanism. + if (gpAllocator) + { + gpAllocator->Free(pEAThreadDynamicData); + } + } + } + + EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId) + { + for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i) + { + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + if (pTDD->mpComp && pTDD->mpComp->mThread.get_id() == threadId) + return pTDD; + } + return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved. + } + + EAThreadDynamicData* FindThreadDynamicData(EA::Thread::ThreadUniqueId threadId) + { + for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i) + { + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + if (pTDD->mUniqueThreadId == threadId) + return pTDD; + } + return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved. + } + + EAThreadDynamicData* FindThreadDynamicData(EA::Thread::SysThreadId sysThreadId) + { + for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i) + { + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + if (pTDD->mpComp && pTDD->mpComp->mThread.native_handle() == sysThreadId) + return pTDD; + } + + // NOTE: This function does not support finding externally created threads due to limitations in the CPP11 std::thread API. + // At the time of writing, it is not possible to retrieve the thread object of a thread not created by the CPP11 API. + return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved. + } + } +} + +EA_DISABLE_VC_WARNING(4355) // this used in base member initializer list - should be safe in this context +EAThreadDynamicData::EAThreadDynamicData(void* userFunc, void* userContext, void* userWrapperFunc, ThreadFunc threadFunc) : + mnRefCount(2), // Init ref count to 2, one corresponding release happens on threadFunc exit and the other when Thread class is destroyed or Begin is called again + mStatus(EA::Thread::Thread::kStatusNone), + mpComp(nullptr) +{ + mpComp = new EAThreadComposite(); + + if(mpComp) + mpComp->mThread = std::thread(threadFunc, this, userFunc, userContext, userWrapperFunc); // This doesn't spawn CPP11 threads when created within the EAThreadComposite constructor. +} + + +EAThreadDynamicData::EAThreadDynamicData(EA::Thread::ThreadUniqueId uniqueThreadId, const char* pThreadName) : + mnRefCount(2), // Init ref count to 2, one corresponding release happens on threadFunc exit and the other when Thread class is destroyed or Begin is called again + mStatus(EA::Thread::Thread::kStatusNone), + mpComp(nullptr), + mUniqueThreadId(uniqueThreadId) +{ + strncpy(mName, pThreadName, EATHREAD_NAME_SIZE); + mName[EATHREAD_NAME_SIZE - 1] = 0; +} + +EA_RESTORE_VC_WARNING() + + +EAThreadDynamicData::~EAThreadDynamicData() +{ + if (mpComp->mThread.joinable()) + mpComp->mThread.detach(); + + if(mpComp) + delete mpComp; + + mpComp = nullptr; + + // the threads, promises, and futures in this class will + // allocate memory with the Concurrency runtime new/delete operators. + // If you're crashing in here with access violations on process exit, + // then you likely have a static instance of EA::Thread::Thread somewhere + // that's being destructed after your memory system is uninitialized + // leaving dangling pointers to bad memory. Attempt to change + // these static instances to be constructed/destructed with the scope + // of normal app operation. +} + + +void EAThreadDynamicData::AddRef() +{ + mnRefCount.Increment(); +} + + +void EAThreadDynamicData::Release() +{ + if(mnRefCount.Decrement() == 0) + EA::Thread::FreeThreadDynamicData(this); +} + +namespace EA +{ + namespace Thread + { + ThreadParameters::ThreadParameters() : + mpStack(NULL), + mnStackSize(0), + mnPriority(kThreadPriorityDefault), + mnProcessor(kProcessorDefault), + mpName(""), + mbDisablePriorityBoost(false) + { + } + + RunnableFunctionUserWrapper Thread::sGlobalRunnableFunctionUserWrapper = NULL; + RunnableClassUserWrapper Thread::sGlobalRunnableClassUserWrapper = NULL; + AtomicInt32 Thread::sDefaultProcessor = kProcessorAny; + + RunnableFunctionUserWrapper Thread::GetGlobalRunnableFunctionUserWrapper() + { + return sGlobalRunnableFunctionUserWrapper; + } + + void Thread::SetGlobalRunnableFunctionUserWrapper(RunnableFunctionUserWrapper pUserWrapper) + { + if (sGlobalRunnableFunctionUserWrapper != NULL) + { + // Can only be set once in entire game. + EAT_ASSERT(false); + } + else + { + sGlobalRunnableFunctionUserWrapper = pUserWrapper; + } + } + + RunnableClassUserWrapper Thread::GetGlobalRunnableClassUserWrapper() + { + return sGlobalRunnableClassUserWrapper; + } + + void Thread::SetGlobalRunnableClassUserWrapper(RunnableClassUserWrapper pUserWrapper) + { + if (sGlobalRunnableClassUserWrapper != NULL) + { + // Can only be set once in entire game. + EAT_ASSERT(false); + } + else + { + sGlobalRunnableClassUserWrapper = pUserWrapper; + } + } + + Thread::Thread() + { + mThreadData.mpData = NULL; + } + + + Thread::Thread(const Thread& t) : + mThreadData(t.mThreadData) + { + if (mThreadData.mpData) + mThreadData.mpData->AddRef(); + } + + + Thread& Thread::operator=(const Thread& t) + { + // We don't synchronize access to mpData; we assume that the user + // synchronizes it or this Thread instances is used from a single thread. + if (t.mThreadData.mpData) + t.mThreadData.mpData->AddRef(); + + if (mThreadData.mpData) + mThreadData.mpData->Release(); + + mThreadData = t.mThreadData; + + return *this; + } + + + Thread::~Thread() + { + // We don't synchronize access to mpData; we assume that the user + // synchronizes it or this Thread instances is used from a single thread. + if (mThreadData.mpData) + mThreadData.mpData->Release(); + } + + static void RunnableFunctionInternal(EAThreadDynamicData* tdd, void* userFunc, void* userContext, void* userWrapperFunc) + { + tdd->mStatus = Thread::kStatusRunning; + tdd->mpStackBase = EA::Thread::GetStackBase(); + RunnableFunction pFunction = (RunnableFunction)userFunc; + + if (userWrapperFunc) + { + RunnableFunctionUserWrapper pWrapperFunction = (RunnableFunctionUserWrapper)userWrapperFunc; + // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext + tdd->mpComp->mReturnPromise.set_value(pWrapperFunction(pFunction, userContext)); + } + else + { + tdd->mpComp->mReturnPromise.set_value(pFunction(userContext)); + } + + tdd->mStatus = Thread::kStatusEnded; + tdd->Release(); // Matches an implicit AddRef in EAThreadDynamicData constructor + } + + + ThreadId Thread::Begin(RunnableFunction pFunction, void* pContext, const ThreadParameters* pTP, RunnableFunctionUserWrapper pUserWrapper) + { + // Check there is an entry for the current thread context in our ThreadDynamicData array. + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + if(!FindThreadDynamicData(threadUniqueId)) + { + EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData(threadUniqueId, "external"); + if(pData) + { + pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread. + // Do no AddRef for thread execution because this is not an EAThread managed thread. + } + } + + if (mThreadData.mpData) + mThreadData.mpData->Release(); // Matches an implicit AddRef in EAThreadDynamicData constructor + + // C++11 Threads don't support user-supplied stacks. A user-supplied stack pointer + // here would be a waste of user memory, and so we assert that mpStack == NULL. + EAT_ASSERT(!pTP || (pTP->mpStack == NULL)); + + // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be + // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed + // during execution. + EAThreadDynamicData* pDataAddr = AllocateThreadDynamicData(); + EAT_ASSERT(pDataAddr != nullptr); + EAThreadDynamicData* pData = new(pDataAddr) EAThreadDynamicData(pFunction, pContext, pUserWrapper, RunnableFunctionInternal); // Note that we use a special new here which doesn't use the heap. + EAT_ASSERT(pData != nullptr); + mThreadData.mpData = pData; + if (pTP) + SetName(pTP->mpName); + + return pData->mpComp->mThread.get_id(); + } + + static void RunnableObjectInternal(EAThreadDynamicData* tdd, void* userFunc, void* userContext, void* userWrapperFunc) + { + tdd->mStatus = Thread::kStatusRunning; + IRunnable* pRunnable = (IRunnable*)userFunc; + + if (userWrapperFunc) + { + RunnableClassUserWrapper pWrapperFunction = (RunnableClassUserWrapper)userWrapperFunc; + // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext + tdd->mpComp->mReturnPromise.set_value(pWrapperFunction(pRunnable, userContext)); + } + else + { + tdd->mpComp->mReturnPromise.set_value(pRunnable->Run(userContext)); + } + + tdd->mStatus = Thread::kStatusEnded; + tdd->Release(); // Matches implicit AddRef in EAThreadDynamicData constructor + } + + + ThreadId Thread::Begin(IRunnable* pRunnable, void* pContext, const ThreadParameters* pTP, RunnableClassUserWrapper pUserWrapper) + { + if (mThreadData.mpData) + mThreadData.mpData->Release(); // Matches an implicit AddRef in EAThreadDynamicData constructor + + // C++11 Threads don't support user-supplied stacks. A user-supplied stack pointer + // here would be a waste of user memory, and so we assert that mpStack == NULL. + EAT_ASSERT(!pTP || (pTP->mpStack == NULL)); + + // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be + // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed + // during execution. + EAThreadDynamicData* pDataAddr = AllocateThreadDynamicData(); + EAT_ASSERT(pDataAddr != nullptr); + EAThreadDynamicData* pData = new(pDataAddr) EAThreadDynamicData(pRunnable, pContext, pUserWrapper, RunnableObjectInternal); // Note that we use a special new here which doesn't use the heap. + EAT_ASSERT(pData != nullptr); + mThreadData.mpData = pData; + if (pTP) + SetName(pTP->mpName); + + EAT_ASSERT(pData && pData->mpComp); + return pData->mpComp->mThread.get_id(); + } + + Thread::Status Thread::WaitForEnd(const ThreadTime& timeoutAbsolute, intptr_t* pThreadReturnValue) + { + // The mThreadData memory is shared between threads and when + // reading it we must be synchronized. + EAReadWriteBarrier(); + + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + // Todo: Consider that there may be a subtle race condition here if + // the user immediately calls WaitForEnd right after calling Begin. + if (mThreadData.mpData && mThreadData.mpData->mpComp) + { + // We must not call WaitForEnd from the thread we are waiting to end. That would result in a deadlock. + EAT_ASSERT(mThreadData.mpData->mpComp->mThread.get_id() != GetThreadId()); + + std::chrono::milliseconds timeoutAbsoluteMs(timeoutAbsolute); + std::chrono::time_point timeoutTime(timeoutAbsoluteMs); + if (mThreadData.mpData->mpComp->mReturnFuture.wait_until(timeoutTime) == std::future_status::timeout) + { + return kStatusRunning; + } + + if (pThreadReturnValue) + { + mThreadData.mpData->mReturnValue = mThreadData.mpData->mpComp->mReturnFuture.get(); + *pThreadReturnValue = mThreadData.mpData->mReturnValue; + } + + mThreadData.mpData->mpComp->mThread.join(); + + return kStatusEnded; // A thread was created, so it must have ended. + } + else + { + // Else the user hasn't started the thread yet, so we wait until the user starts it. + // Ideally, what we really want to do here is wait for some kind of signal. + // Instead for the time being we do a polling loop. + while ((!mThreadData.mpData) && (GetThreadTime() < timeoutAbsolute)) + { + ThreadSleep(1); + } + if (mThreadData.mpData) + return WaitForEnd(timeoutAbsolute); + } + return kStatusNone; // No thread has been started. + } + + Thread::Status Thread::GetStatus(intptr_t* pThreadReturnValue) const + { + if (mThreadData.mpData && mThreadData.mpData->mpComp) + { + auto status = static_cast(mThreadData.mpData->mStatus.GetValue()); + if (pThreadReturnValue && status == kStatusEnded) + { + if (mThreadData.mpData->mpComp->mGetStatusFuture.valid()) + mThreadData.mpData->mReturnValue = mThreadData.mpData->mpComp->mGetStatusFuture.get(); + *pThreadReturnValue = mThreadData.mpData->mReturnValue; + } + return status; + } + return kStatusNone; + } + + int Thread::GetPriority() const + { + // No way to query or set thread priority through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs using native_handle() + return kThreadPriorityDefault; + } + + + bool Thread::SetPriority(int nPriority) + { + // No way to query or set thread priority through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs using native_handle() + return false; + } + + + void Thread::SetProcessor(int nProcessor) + { + // No way to query or set thread priority through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs using native_handle() + } + + void EA::Thread::Thread::SetAffinityMask(EA::Thread::ThreadAffinityMask nAffinityMask) + { + if(mThreadData.mpData) + { + EA::Thread::SetThreadAffinityMask(nAffinityMask); + } + } + + EA::Thread::ThreadAffinityMask EA::Thread::Thread::GetAffinityMask() + { + if(mThreadData.mpData) + { + return mThreadData.mpData->mnThreadAffinityMask; + } + + return kThreadAffinityMaskAny; + } + + void Thread::Wake() + { + // No way to wake a thread through standard C++11 thread library. + // On some platforms this could be implemented through platform specific APIs using native_handle() + } + + + const char* Thread::GetName() const + { + if (mThreadData.mpData) + return mThreadData.mpData->mName; + return ""; + } + + + void Thread::SetName(const char* pName) + { + if (mThreadData.mpData && pName) + { + strncpy(mThreadData.mpData->mName, pName, EATHREAD_NAME_SIZE); + mThreadData.mpData->mName[EATHREAD_NAME_SIZE - 1] = 0; + } + } + + ThreadId Thread::GetId() const + { + if (mThreadData.mpData && mThreadData.mpData->mpComp) + return mThreadData.mpData->mpComp->mThread.get_id(); + return kThreadIdInvalid; + } + + } +} + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread.cpp new file mode 100644 index 00000000..c963effa --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread.cpp @@ -0,0 +1,270 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + + +namespace EA +{ + namespace Thread + { + EA::Thread::Allocator* gpAllocator = NULL; + + EATHREADLIB_API void SetAllocator(Allocator* pEAThreadAllocator) + { + gpAllocator = pEAThreadAllocator; + } + + EATHREADLIB_API Allocator* GetAllocator() + { + return gpAllocator; + } + + + + // Currently we take advantage of the fact that ICoreAllocator + // is a binary mapping to EA::Thread::Allocator. + // To do: We need to come up with a better solution that this, + // as it is not future-safe and not even guaranteed to + // be portable. The problem is that we can't make this + // package dependent on the CoreAllocator package without + // breaking users who aren't using it. + + EATHREADLIB_API void SetAllocator(EA::Allocator::ICoreAllocator* pCoreAllocator) + { + gpAllocator = (EA::Thread::Allocator*)(uintptr_t)pCoreAllocator; + } + + EATHREADLIB_API void SetThreadAffinityMask(ThreadAffinityMask nAffinityMask) + { + EA::Thread::SetThreadAffinityMask(GetThreadId(), nAffinityMask); + } + + EATHREADLIB_API ThreadAffinityMask GetThreadAffinityMask() + { + return GetThreadAffinityMask(GetThreadId()); + } + } +} + +#if !EA_THREADS_AVAILABLE + // Do nothing +#elif EA_USE_CPP11_CONCURRENCY + #include "cpp11/eathread_cpp11.cpp" +#elif defined(EA_PLATFORM_SONY) + #include "sony/eathread_sony.cpp" +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include "unix/eathread_unix.cpp" + #if defined(EA_PLATFORM_STADIA) + #include "unix/eathread_stadia.cpp" + #endif +#elif defined(EA_PLATFORM_MICROSOFT) + #include "pc/eathread_pc.cpp" +#endif + +EA::Thread::ThreadAffinityMask EA::Thread::GetAvailableCpuAffinityMask() +{ +#if defined(EA_PLATFORM_STADIA) + return getValidCpuAffinityMask(); +#else + // We assume that the available cores are 0...ProcessorCount-1. + int processorCount = GetProcessorCount(); + EA::Thread::ThreadAffinityMask ret = 0; + EA::Thread::ThreadAffinityMask mask = 1; + while (processorCount > 0) + { + ret |= mask; + mask <<= 1; + --processorCount; + } + return ret; +#endif +} + +namespace EA +{ + namespace Thread + { + namespace detail + { + #if !defined(EAThreadIdToString_CUSTOM_IMPLEMENTATION) + ThreadIdToStringBuffer::ThreadIdToStringBuffer(EA::Thread::ThreadId threadId) + { + sprintf(mBuf, "%d", (int)(intptr_t)threadId); + } + + SysThreadIdToStringBuffer::SysThreadIdToStringBuffer(EA::Thread::SysThreadId sysThreadId) + { + sprintf(mBuf, "%d", (int)(intptr_t)sysThreadId); + } + #endif + } + } +} + +#if !defined(EAT_ASSERT_SNPRINTF) + #if defined(EA_PLATFORM_MICROSOFT) + #define EAT_ASSERT_SNPRINTF _vsnprintf + #else + #define EAT_ASSERT_SNPRINTF snprintf + #endif +#endif + + void EA::Thread::AssertionFailureV(const char* pFormat, ...) + { + const size_t kBufferSize = 512; + char buffer[kBufferSize]; + + va_list arguments; + va_start(arguments, pFormat); + const int nReturnValue = EAT_ASSERT_SNPRINTF(buffer, kBufferSize, pFormat, arguments); + va_end(arguments); + + if(nReturnValue > 0) + { + buffer[kBufferSize - 1] = 0; + AssertionFailure(buffer); + } + } + +/////////////////////////////////////////////////////////////////////////////// +// non-threaded implementation +/////////////////////////////////////////////////////////////////////////////// + +#if !EA_THREADS_AVAILABLE + + #include + #if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #elif defined(EA_PLATFORM_WINDOWS) + extern "C" __declspec(dllimport) void __stdcall Sleep(unsigned long dwMilliseconds); + #endif + + + namespace EA + { + namespace Thread + { + // Assertion variables. + EA::Thread::AssertionFailureFunction gpAssertionFailureFunction = NULL; + void* gpAssertionFailureContext = NULL; + } + } + + EA::Thread::ThreadId EA::Thread::GetThreadId() + { + return 1; + } + + + int EA::Thread::GetThreadPriority() + { + return kThreadPriorityDefault; + } + + + bool EA::Thread::SetThreadPriority(int nPriority) + { + return true; + } + + + void* EA::Thread::GetThreadStackBase() + { + return NULL; + } + + + void EA::Thread::SetThreadProcessor(int /*nProcessor*/) + { + } + + + int EA::Thread::GetThreadProcessor() + { + return 0; + } + + + int EA::Thread::GetProcessorCount() + { + return 1; + } + + + void EA::Thread::ThreadSleep(const ThreadTime& timeRelative) + { + #if defined(EA_PLATFORM_WINDOWS) + + // There is no nanosleep on Windows, but there is Sleep. + if(timeRelative == kTimeoutImmediate) + Sleep(0); + else + Sleep((unsigned)((timeRelative.tv_sec * 1000) + (((timeRelative.tv_nsec % 1000) * 1000000)))); + + #elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + + if(timeRelative == kTimeoutImmediate) + sched_yield(); + else + nanosleep(&timeRelative, 0); + + #endif + } + + + void EA::Thread::ThreadEnd(intptr_t /*threadReturnValue*/) + { + // We could possibly call exit here. + } + + + EA::Thread::ThreadTime EA::Thread::GetThreadTime() + { + #if defined(EA_PLATFORM_WINDOWS) + + return (ThreadTime)GetTickCount(); + + #elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + + #if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_CYGWIN) || (_POSIX_TIMERS > 0) + ThreadTime threadTime; + clock_gettime(CLOCK_REALTIME, &threadTime); // If you get a linker error about clock_getttime, you need to link librt.a (specify -lrt to the linker). + return threadTime; + #else + timeval temp; + gettimeofday(&temp, NULL); + return ThreadTime(temp.tv_sec, temp.tv_usec * 1000); + #endif + + #endif + } + + + void EA::Thread::SetAssertionFailureFunction(EA::Thread::AssertionFailureFunction pAssertionFailureFunction, void* pContext) + { + gpAssertionFailureFunction = pAssertionFailureFunction; + gpAssertionFailureContext = pContext; + } + + + void EA::Thread::AssertionFailure(const char* pExpression) + { + if(gpAssertionFailureFunction) + gpAssertionFailureFunction(pExpression, gpAssertionFailureContext); + else + { + #if EAT_ASSERT_ENABLED + printf("EA::Thread::AssertionFailure: %s\n", pExpression); + #endif + } + } + + +#endif // EA_THREADS_AVAILABLE + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_barrier.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_barrier.cpp new file mode 100644 index 00000000..c47ef6e1 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_barrier.cpp @@ -0,0 +1,194 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +EA_DISABLE_VC_WARNING(4574) +#include +EA_RESTORE_VC_WARNING() + +#if defined(EA_PLATFORM_SONY) + #include "sony/eathread_barrier_sony.cpp" + +#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE + // Posix already defines a barrier (via condition variables or directly with pthread_barrier). + #include "unix/eathread_barrier_unix.cpp" + +#else // All other platforms + + #include + #include + + + EABarrierData::EABarrierData() + : mnCurrent(0), mnHeight(0), mnIndex(0), mSemaphore0(NULL, false), mSemaphore1(NULL, false) + { + // Leave mSemaphores alone for now. We leave them constructed but not initialized. + } + + + EA::Thread::BarrierParameters::BarrierParameters(int height, bool bIntraProcess, const char* pName) + : mHeight(height), mbIntraProcess(bIntraProcess) + { + if(pName) + { + EA_DISABLE_VC_WARNING(4996); // This function or variable may be unsafe / deprecated. + strncpy(mName, pName, sizeof(mName)-1); + EA_RESTORE_VC_WARNING(); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + } + + + EA::Thread::Barrier::Barrier(const BarrierParameters* pBarrierParameters, bool bDefaultParameters) + { + if(!pBarrierParameters && bDefaultParameters) + { + BarrierParameters parameters; + Init(¶meters); + } + else + Init(pBarrierParameters); + } + + + EA::Thread::Barrier::Barrier(int height) + { + BarrierParameters parameters(height); + Init(¶meters); + } + + + EA::Thread::Barrier::~Barrier() + { + // Nothing to do. + } + + + bool EA::Thread::Barrier::Init(const BarrierParameters* pBarrierParameters) + { + // You cannot set the height after it's already been set. + EAT_ASSERT((mBarrierData.mnHeight == 0) && (mBarrierData.mnCurrent == 0)); + + if(pBarrierParameters && (mBarrierData.mnHeight == 0)) + { + mBarrierData.mnHeight = pBarrierParameters->mHeight; // We don't put mutex lock around this as it is only to be ever set once, before use. + mBarrierData.mnCurrent = pBarrierParameters->mHeight; + + SemaphoreParameters sp(0, pBarrierParameters->mbIntraProcess); + mBarrierData.mSemaphore0.Init(&sp); + mBarrierData.mSemaphore1.Init(&sp); + + return true; + } + + return false; + } + + + EA::Thread::Barrier::Result EA::Thread::Barrier::Wait(const ThreadTime& timeoutAbsolute) + { + int result; + const int nCurrentIndex = (int)mBarrierData.mnIndex; + + // Question: What do we do if a fifth thread calls Wait on a barrier with height + // of four after the fourth thread has decremented the current count below? + + EAT_ASSERT(mBarrierData.mnCurrent > 0); // If this assert fails then it means that more threads are waiting on the barrier than the barrier height. + + const int32_t nCurrent = mBarrierData.mnCurrent.Decrement(); // atomic integer operation. + + if(nCurrent == 0) // If the barrier has been breached... + { + mBarrierData.mnCurrent = mBarrierData.mnHeight; + + if(mBarrierData.mnHeight > 1) // If there are threads other than us... + { + // We don't have a potential race condition here because we use alternating + // semaphores and since we are here, all other threads are waiting on the + // current semaphore below. And if they haven't started waiting on the + // semaphore yet, they'll succeed anyway because we Post all directly below. + Semaphore* const pSemaphore = (nCurrentIndex == 0 ? &mBarrierData.mSemaphore0 : &mBarrierData.mSemaphore1); + + result = pSemaphore->Post(mBarrierData.mnHeight - 1); // Upon success, the return value will in practice be >= 1, but semaphore defines success as >= 0. + } + else // Else we are the only thead. + result = 0; + } + else + { + Semaphore* const pSemaphore = (nCurrentIndex == 0 ? &mBarrierData.mSemaphore0 : &mBarrierData.mSemaphore1); + + result = pSemaphore->Wait(timeoutAbsolute); + + if(result == Semaphore::kResultTimeout) + return kResultTimeout; + } + + if(result >= 0) // If the result wasn't an error such as Semaphore::kResultError or Semaphore::kResultTimeout. + { + // Use an atomic operation to change the index, which conveniently gives us a thread to designate as primary. + EAT_ASSERT((unsigned)nCurrentIndex <= 1); + + if(mBarrierData.mnIndex.SetValueConditional(1 - nCurrentIndex, nCurrentIndex)) // Toggle value between 0 and 1. + return kResultPrimary; + + return kResultSecondary; + } + + return kResultError; + } + + + EA::Thread::Barrier* EA::Thread::BarrierFactory::CreateBarrier() + { + EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator(); + + if(pAllocator) + return new(pAllocator->Alloc(sizeof(EA::Thread::Barrier))) EA::Thread::Barrier; + else + return new EA::Thread::Barrier; + } + + void EA::Thread::BarrierFactory::DestroyBarrier(EA::Thread::Barrier* pBarrier) + { + EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator(); + + if(pAllocator) + { + pBarrier->~Barrier(); + pAllocator->Free(pBarrier); + } + else + delete pBarrier; + } + + size_t EA::Thread::BarrierFactory::GetBarrierSize() + { + return sizeof(EA::Thread::Barrier); + } + + EA::Thread::Barrier* EA::Thread::BarrierFactory::ConstructBarrier(void* pMemory) + { + return new(pMemory) EA::Thread::Barrier; + } + + void EA::Thread::BarrierFactory::DestructBarrier(EA::Thread::Barrier* pBarrier) + { + pBarrier->~Barrier(); + } + + +#endif // EA_PLATFORM_XXX + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_callstack.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_callstack.cpp new file mode 100644 index 00000000..2f97afb8 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_callstack.cpp @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#if defined(EA_PLATFORM_WIN32) && EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + #include "pc/eathread_callstack_win32.cpp" +#elif defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_X86_64) + #include "pc/eathread_callstack_win64.cpp" +#elif defined(EA_PLATFORM_SONY) + #include "sony/eathread_callstack_sony.cpp" + #include "sony/eathread_pthread_stack_info.cpp" +#elif defined(EA_PLATFORM_ANDROID) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #include "x86/eathread_callstack_x86.cpp" + #include "unix/eathread_pthread_stack_info.cpp" +#elif defined(EA_PLATFORM_ANDROID) + #include "libunwind/eathread_callstack_libunwind.cpp" + #include "unix/eathread_pthread_stack_info.cpp" +#elif defined(EA_PLATFORM_APPLE) // OSX, iPhone, iPhone Simulator + #include "apple/eathread_callstack_apple.cpp" + #include "unix/eathread_pthread_stack_info.cpp" +#elif defined(EA_PROCESSOR_ARM) + #include "arm/eathread_callstack_arm.cpp" + #if !defined(EA_PLATFORM_MICROSOFT) + #include "unix/eathread_pthread_stack_info.cpp" + #endif +#elif (defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_CYGWIN)) && (defined(EA_PROCESSOR_X86) || defined(EA_PROCESSOR_X86_64)) + #include "x86/eathread_callstack_x86.cpp" + #include "unix/eathread_pthread_stack_info.cpp" +#elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + #include "unix/eathread_callstack_glibc.cpp" + #include "unix/eathread_pthread_stack_info.cpp" +#else + #include "null/eathread_callstack_null.cpp" +#endif + +namespace EA +{ +namespace Thread +{ + +#if defined(EA_COMPILER_MSVC) + +#include + +#pragma intrinsic(_ReturnAddress) + + +/////////////////////////////////////////////////////////////////////////////// +// GetInstructionPointer +// +EATHREADLIB_API EA_NO_INLINE void GetInstructionPointer(void*& pInstruction) +{ + pInstruction = _ReturnAddress(); +} + +#elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + +/////////////////////////////////////////////////////////////////////////////// +// GetInstructionPointer +// +EATHREADLIB_API EA_NO_INLINE void GetInstructionPointer(void*& pInstruction) +{ + pInstruction = __builtin_return_address(0); +} + +#endif + +} // namespace Thread +} // namespace EA diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_condition.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_condition.cpp new file mode 100644 index 00000000..db86e3e6 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_condition.cpp @@ -0,0 +1,265 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +EA_DISABLE_VC_WARNING(4574) +#include +EA_RESTORE_VC_WARNING() + +#if defined(EA_PLATFORM_SONY) + // Posix already defines a Condition (via condition variables). + #include "sony/eathread_condition_sony.cpp" +#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EA_THREADS_AVAILABLE + // Posix already defines a Condition (via condition variables). + #include "unix/eathread_condition_unix.cpp" + +#else // All other platforms + + #include + #include + + EAConditionData::EAConditionData() + : mnWaitersBlocked(0), mnWaitersToUnblock(0), mnWaitersDone(0), + mSemaphoreBlockQueue(NULL, false), // We will be initializing these ourselves specifically below. + mSemaphoreBlockLock(NULL, false), + mUnblockLock(NULL, false) + { + // Empty + } + + + EA::Thread::ConditionParameters::ConditionParameters(bool bIntraProcess, const char* pName) + : mbIntraProcess(bIntraProcess) + { + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + } + + + EA::Thread::Condition::Condition(const ConditionParameters* pConditionParameters, bool bDefaultParameters) + { + if(!pConditionParameters && bDefaultParameters) + { + ConditionParameters parameters; + Init(¶meters); + } + else + Init(pConditionParameters); + } + + + EA::Thread::Condition::~Condition() + { + // Empty + } + + + bool EA::Thread::Condition::Init(const ConditionParameters* pConditionParameters) + { + if(pConditionParameters) + { + // We have a problem with naming here. We implement our Condition variable with two semaphores and a mutex. + // It's not possible to have them all have the same name, since the OS will think you want them to be + // shared instances. What we really need is an explicit debug name that is separate from the OS name. + // And the ConditionParameters::mName should be that debug name only and not be applied to the child primitives. + + const SemaphoreParameters sp1(0, pConditionParameters->mbIntraProcess, NULL); // Set the name to NULL, regardless of what pConditionParameters->mName is. + const SemaphoreParameters sp2(1, pConditionParameters->mbIntraProcess, NULL); + const MutexParameters mp(pConditionParameters->mbIntraProcess, NULL); + + if(mConditionData.mSemaphoreBlockQueue.Init(&sp1) && + mConditionData.mSemaphoreBlockLock .Init(&sp2) && + mConditionData.mUnblockLock.Init(&mp)) + { + return true; + } + } + + return false; + } + + + EA::Thread::Condition::Result EA::Thread::Condition::Wait(Mutex* pMutex, const ThreadTime& timeoutAbsolute) + { + int lockResult, result; + + EAT_ASSERT(pMutex); // The user is required to pass a valid Mutex pointer. + + ++mConditionData.mnWaitersBlocked; // Note that this is an atomic operation. + + EAT_ASSERT(pMutex->GetLockCount() == 1); + lockResult = pMutex->Unlock(); + if(lockResult < 0) + return (Result)lockResult; + + result = mConditionData.mSemaphoreBlockQueue.Wait(timeoutAbsolute); + EAT_ASSERT(result != EA::Thread::Semaphore::kResultError); + // Regardless of the result of the above error, we must press on with the code below. + + mConditionData.mUnblockLock.Lock(); + + const int nWaitersToUnblock = mConditionData.mnWaitersToUnblock; + + if(nWaitersToUnblock != 0) + --mConditionData.mnWaitersToUnblock; + else if(++mConditionData.mnWaitersDone == (INT_MAX / 2)) // This is not an atomic operation. We are within a mutex lock. + { + // Normally this doesn't happen, but can happen under very + // unusual circumstances, such as spurious semaphore signals + // or cases whereby many many threads are timing out. + EAT_ASSERT(false); + mConditionData.mSemaphoreBlockLock.Wait(); + mConditionData.mnWaitersBlocked -= mConditionData.mnWaitersDone; + mConditionData.mSemaphoreBlockLock.Post(); + mConditionData.mnWaitersDone = 0; + } + + mConditionData.mUnblockLock.Unlock(); + + if(nWaitersToUnblock == 1) // If we were the last... + mConditionData.mSemaphoreBlockLock.Post(); + + // We cannot apply a timeout here. The caller always expects to have the + // lock upon return, even in the case of a wait timeout. Similarly, we + // may or may not want the result of the lock attempt to be propogated + // back to the caller. In this case, we do if it is an error. + lockResult = pMutex->Lock(); + + if(lockResult == Mutex::kResultError) + return kResultError; + else if(result >= 0) + return kResultOK; + + return (Result)result; // This is the result of the wait call above. + } + + + bool EA::Thread::Condition::Signal(bool bBroadcast) + { + int result; + int nSignalsToIssue; + + result = mConditionData.mUnblockLock.Lock(); + + if(result < 0) + return false; + + if(mConditionData.mnWaitersToUnblock) + { + if(mConditionData.mnWaitersBlocked == 0) + { + mConditionData.mUnblockLock.Unlock(); + return true; + } + + if(bBroadcast) + { + nSignalsToIssue = (int)mConditionData.mnWaitersBlocked.SetValue(0); + mConditionData.mnWaitersToUnblock += nSignalsToIssue; + } + else + { + nSignalsToIssue = 1; + mConditionData.mnWaitersToUnblock++; + mConditionData.mnWaitersBlocked--; + } + } + else if(mConditionData.mnWaitersBlocked > mConditionData.mnWaitersDone) + { + if(mConditionData.mSemaphoreBlockLock.Wait() == EA::Thread::Semaphore::kResultError) + { + mConditionData.mUnblockLock.Unlock(); + return false; + } + + if(mConditionData.mnWaitersDone != 0) + { + mConditionData.mnWaitersBlocked -= mConditionData.mnWaitersDone; + mConditionData.mnWaitersDone = 0; + } + + if(bBroadcast) + { + nSignalsToIssue = mConditionData.mnWaitersToUnblock = (int)mConditionData.mnWaitersBlocked.SetValue(0); + } + else + { + nSignalsToIssue = mConditionData.mnWaitersToUnblock = 1; + mConditionData.mnWaitersBlocked--; + } + } + else + { + mConditionData.mUnblockLock.Unlock(); + return true; + } + + mConditionData.mUnblockLock.Unlock(); + mConditionData.mSemaphoreBlockQueue.Post(nSignalsToIssue); + + return true; + } + +#endif // EA_PLATFORM_XXX + + + + +EA::Thread::Condition* EA::Thread::ConditionFactory::CreateCondition() +{ + Allocator* pAllocator = GetAllocator(); + + if(pAllocator) + return new(pAllocator->Alloc(sizeof(EA::Thread::Condition))) EA::Thread::Condition; + else + return new EA::Thread::Condition; +} + +void EA::Thread::ConditionFactory::DestroyCondition(EA::Thread::Condition* pCondition) +{ + Allocator* pAllocator = GetAllocator(); + + if(pAllocator) + { + pCondition->~Condition(); + pAllocator->Free(pCondition); + } + else + delete pCondition; +} + +size_t EA::Thread::ConditionFactory::GetConditionSize() +{ + return sizeof(EA::Thread::Condition); +} + +EA::Thread::Condition* EA::Thread::ConditionFactory::ConstructCondition(void* pMemory) +{ + return new(pMemory) EA::Thread::Condition; +} + +void EA::Thread::ConditionFactory::DestructCondition(EA::Thread::Condition* pCondition) +{ + pCondition->~Condition(); +} + + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_futex.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_futex.cpp new file mode 100644 index 00000000..88e121af --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_futex.cpp @@ -0,0 +1,335 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#if defined(EA_THREAD_NONTHREADED_FUTEX) && EA_THREAD_NONTHREADED_FUTEX + + void EA::Thread::Futex::CreateFSemaphore() + { + mSemaphore.mnCount = 0; + } + + void EA::Thread::Futex::DestroyFSemaphore() + { + // Do nothing; + } + + void EA::Thread::Futex::SignalFSemaphore() + { + mSemaphore.mnCount++; + } + + void EA::Thread::Futex::WaitFSemaphore() + { + while(mSemaphore.mnCount <= 0) + EA_THREAD_DO_SPIN(); + mSemaphore.mnCount--; + } + + bool EA::Thread::Futex::WaitFSemaphore(const ThreadTime&) + { + WaitFSemaphore(); + return true; + } + +#elif defined(EA_PLATFORM_APPLE) && EATHREAD_MANUAL_FUTEX_ENABLED + #include + #include + #include + #include + #include + + void EA::Thread::Futex::CreateFSemaphore() + { + mSemaphore.Init(0); + } + + void EA::Thread::Futex::DestroyFSemaphore() + { + // Do nothing; + } + + void EA::Thread::Futex::SignalFSemaphore() + { + mSemaphore.Post(); + } + + void EA::Thread::Futex::WaitFSemaphore() + { + mSemaphore.Wait(); + } + + bool EA::Thread::Futex::WaitFSemaphore(const ThreadTime& timeoutAbsolute) + { + return (mSemaphore.Wait(timeoutAbsolute) >= 0); + } + +#elif defined(EA_PLATFORM_SONY) && !EATHREAD_MANUAL_FUTEX_ENABLED + #include + #include + + EA::Thread::Futex::Futex() + : mSpinCount(EATHREAD_FUTEX_SPIN_COUNT) + { + } + + EA::Thread::Futex::~Futex() + { + } + + void EA::Thread::Futex::Lock() + { + Uint spinCount(mSpinCount); + while(--spinCount) + { + if(TryLock()) + return; + } + + mMutex.Lock(); + } + + void EA::Thread::Futex::Unlock() + { + mMutex.Unlock(); + } + + bool EA::Thread::Futex::TryLock() + { + if(mMutex.Lock(EA::Thread::kTimeoutImmediate) > 0) // This calls scePthreadMutexTrylock + return true; + + return false; + } + + int EA::Thread::Futex::Lock(const ThreadTime& timeoutAbsolute) + { + return mMutex.Lock(timeoutAbsolute); + } + + int EA::Thread::Futex::GetLockCount() const + { + return mMutex.GetLockCount(); + } + + bool EA::Thread::Futex::HasLock() const + { + return mMutex.HasLock(); + } + + void EA::Thread::Futex::SetSpinCount(Uint spinCount) + { + mSpinCount = spinCount; + } + +#elif defined(EA_PLATFORM_SONY) && EATHREAD_MANUAL_FUTEX_ENABLED + #include + #include + + void EA::Thread::Futex::CreateFSemaphore() + { + // To consider: Copy the Futex name into this semaphore name. + int result = sceKernelCreateSema(&mSemaphore, "Futex", SCE_KERNEL_SEMA_ATTR_TH_FIFO, 0, 100000, NULL); + EA_UNUSED(result); + EAT_ASSERT(result == SCE_OK); + } + + void EA::Thread::Futex::DestroyFSemaphore() + { + int result = sceKernelDeleteSema(mSemaphore); + EA_UNUSED(result); + EAT_ASSERT(result == SCE_OK); + } + + void EA::Thread::Futex::SignalFSemaphore() + { + int result = sceKernelSignalSema(mSemaphore, 1); + EA_UNUSED(result); + EAT_ASSERT(result == SCE_OK); + } + + void EA::Thread::Futex::WaitFSemaphore() + { + int result = sceKernelWaitSema(mSemaphore, 1, NULL); + EA_UNUSED(result); + EAT_ASSERT(result == SCE_OK); + } + + bool EA::Thread::Futex::WaitFSemaphore(const ThreadTime& timeoutAbsolute) + { + SceKernelUseconds timeoutRelativeUs = static_cast(RelativeTimeoutFromAbsoluteTimeout(timeoutAbsolute)); + if(timeoutRelativeUs < 1) + timeoutRelativeUs = 1; + + return (sceKernelWaitSema(mSemaphore, 1, &timeoutRelativeUs) == SCE_OK); + } + +#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && EATHREAD_MANUAL_FUTEX_ENABLED + #include + #include + + void EA::Thread::Futex::CreateFSemaphore() + { + const int result = sem_init(&mSemaphore, 0, 0); + (void)result; + EAT_ASSERT(result != -1); + } + + void EA::Thread::Futex::DestroyFSemaphore() + { + #if defined (EA_PLATFORM_APPLE) + sem_close(&mSemaphore); + #elif defined(EA_PLATFORM_ANDROID) + sem_destroy(&mSemaphore); // Android's sem_destroy is broken. http://code.google.com/p/android/issues/detail?id=3106 + #else + int result = -1; + + for(;;) + { + result = sem_destroy(&mSemaphore); + + if((result == -1) && (errno == EBUSY)) // If another thread or process is blocked on this semaphore... + ThreadSleep(kTimeoutYield); // Yield. If we don't yield, it's possible we could block other other threads or processes from running, on some systems. + else + break; + } + + EAT_ASSERT(result != -1); + #endif + } + + void EA::Thread::Futex::SignalFSemaphore() + { + sem_post(&mSemaphore); + } + + void EA::Thread::Futex::WaitFSemaphore() + { + // We don't have much choice but to retry interrupted waits, + // as there is no lock failure return value. + while((sem_wait(&mSemaphore) == -1) && (errno == EINTR)) + continue; + } + + bool EA::Thread::Futex::WaitFSemaphore(const ThreadTime&) + { + WaitFSemaphore(); + return true; + } + +#elif defined(EA_PLATFORM_MICROSOFT) && !EA_USE_CPP11_CONCURRENCY && !EATHREAD_MANUAL_FUTEX_ENABLED + + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + + // Validate what we assume to be invariants. + EAT_COMPILETIME_ASSERT(sizeof(CRITICAL_SECTION) <= (EA::Thread::FUTEX_PLATFORM_DATA_SIZE / sizeof(uint64_t) * sizeof(uint64_t))); + + #if defined(EA_PLATFORM_MICROSOFT) && (defined(EA_PROCESSOR_X86_64) || defined(EA_PROCESSOR_ARM64)) + EAT_COMPILETIME_ASSERT(offsetof(CRITICAL_SECTION, RecursionCount) == (3 * sizeof(int))); + EAT_COMPILETIME_ASSERT(offsetof(CRITICAL_SECTION, OwningThread) == (4 * sizeof(int))); + #elif defined(EA_PLATFORM_WIN32) + EAT_COMPILETIME_ASSERT(offsetof(CRITICAL_SECTION, RecursionCount) == (2 * sizeof(int))); + EAT_COMPILETIME_ASSERT(offsetof(CRITICAL_SECTION, OwningThread) == (3 * sizeof(int))); + #else + EAT_FAIL_MSG("Need to verify offsetof."); + #endif + + +#elif defined(EA_PLATFORM_MICROSOFT) && EATHREAD_MANUAL_FUTEX_ENABLED + + #if defined(EA_PLATFORM_WINDOWS) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + #endif + + void EA::Thread::Futex::CreateFSemaphore() + { + mSemaphore = CreateSemaphoreA(NULL, 0, INT_MAX / 2, NULL); + EAT_ASSERT(mSemaphore != 0); + } + + void EA::Thread::Futex::DestroyFSemaphore() + { + if(mSemaphore) + CloseHandle(mSemaphore); + } + + void EA::Thread::Futex::SignalFSemaphore() + { + ReleaseSemaphore(mSemaphore, 1, NULL); + } + + void EA::Thread::Futex::WaitFSemaphore() + { + WaitForSingleObject(mSemaphore, INFINITE); + } + + bool EA::Thread::Futex::WaitFSemaphore(const ThreadTime& timeoutAbsolute) + { + int64_t timeoutRelativeMS = (int64_t)(timeoutAbsolute - GetThreadTime()); + if(timeoutRelativeMS < 1) + timeoutRelativeMS = 1; + return WaitForSingleObject(mSemaphore, (DWORD)timeoutRelativeMS) == WAIT_OBJECT_0; + } + +#endif + + + + + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::Futex* EA::Thread::FutexFactory::CreateFutex() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::Futex))) EA::Thread::Futex; + else + return new EA::Thread::Futex; +} + +void EA::Thread::FutexFactory::DestroyFutex(EA::Thread::Futex* pFutex) +{ + if(gpAllocator) + { + pFutex->~Futex(); + gpAllocator->Free(pFutex); + } + else + delete pFutex; +} + +size_t EA::Thread::FutexFactory::GetFutexSize() +{ + return sizeof(EA::Thread::Futex); +} + +EA::Thread::Futex* EA::Thread::FutexFactory::ConstructFutex(void* pMemory) +{ + return new(pMemory) EA::Thread::Futex; +} + +void EA::Thread::FutexFactory::DestructFutex(EA::Thread::Futex* pFutex) +{ + pFutex->~Futex(); +} + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_mutex.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_mutex.cpp new file mode 100644 index 00000000..22cabfe1 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_mutex.cpp @@ -0,0 +1,144 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include + +EA_DISABLE_VC_WARNING(4574) +#include +#include +EA_RESTORE_VC_WARNING() + +#if !EA_THREADS_AVAILABLE + #include +#elif EA_USE_CPP11_CONCURRENCY + #include "cpp11/eathread_mutex_cpp11.cpp" + #if defined(CreateMutex) + #undef CreateMutex + #endif +#elif defined(EA_PLATFORM_SONY) + #include "sony/eathread_mutex_sony.cpp" +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include "unix/eathread_mutex_unix.cpp" +#elif defined(EA_PLATFORM_MICROSOFT) + #include "pc/eathread_mutex_pc.cpp" +#endif + + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::Mutex* EA::Thread::MutexFactory::CreateMutex() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::Mutex))) EA::Thread::Mutex; + else + return new EA::Thread::Mutex; +} + +void EA::Thread::MutexFactory::DestroyMutex(EA::Thread::Mutex* pMutex) +{ + if(gpAllocator) + { + pMutex->~Mutex(); + gpAllocator->Free(pMutex); + } + else + delete pMutex; +} + +size_t EA::Thread::MutexFactory::GetMutexSize() +{ + return sizeof(EA::Thread::Mutex); +} + +EA::Thread::Mutex* EA::Thread::MutexFactory::ConstructMutex(void* pMemory) +{ + return new(pMemory) EA::Thread::Mutex; +} + +void EA::Thread::MutexFactory::DestructMutex(EA::Thread::Mutex* pMutex) +{ + pMutex->~Mutex(); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// non-threaded implementation +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_THREAD_NONTHREADED_MUTEX) && EA_THREAD_NONTHREADED_MUTEX + + EAMutexData::EAMutexData() + : mnLockCount(0) + { + // Empty + } + + + EA::Thread::MutexParameters::MutexParameters(bool /*bIntraProcess*/, const char* /*pName*/) + : mbIntraProcess(true) + { + } + + + EA::Thread::Mutex::Mutex(const MutexParameters* pMutexParameters, bool bDefaultParameters) + { + if(!pMutexParameters && bDefaultParameters) + { + MutexParameters parameters; + Init(¶meters); + } + else + Init(pMutexParameters); + } + + + EA::Thread::Mutex::~Mutex() + { + EAT_ASSERT(mMutexData.mnLockCount == 0); + } + + + bool EA::Thread::Mutex::Init(const MutexParameters* /*pMutexParameters*/) + { + // Possibly copy pMutexParameters->mName to mMutexData.mName + return true; + } + + + int EA::Thread::Mutex::Lock(const ThreadTime& /*timeoutAbsolute*/) + { + EAT_ASSERT(mMutexData.mnLockCount < 100000); + + return ++mMutexData.mnLockCount; + } + + + int EA::Thread::Mutex::Unlock() + { + EAT_ASSERT(mMutexData.mnLockCount > 0); + + return --mMutexData.mnLockCount; + } + + + int EA::Thread::Mutex::GetLockCount() const + { + return mMutexData.mnLockCount; + } + + + bool EA::Thread::Mutex::HasLock() const + { + return (mMutexData.mnLockCount > 0); + } + +#endif // EA_THREAD_NONTHREADED_MUTEX diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_pool.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_pool.cpp new file mode 100644 index 00000000..e825dc69 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_pool.cpp @@ -0,0 +1,698 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +// 6011: Dereferencing NULL pointer 'gpAllocator' +// 6211: Leaking memory 'pThreadInfo' due to an exception. +// 6326: Potential comparison of a constant with another constant +EA_DISABLE_VC_WARNING(6011 6211 6326) + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::ThreadPoolParameters::ThreadPoolParameters() + : mnMinCount(EA::Thread::ThreadPool::kDefaultMinCount), + mnMaxCount(EA::Thread::ThreadPool::kDefaultMaxCount), + mnInitialCount(EA::Thread::ThreadPool::kDefaultInitialCount), + mnIdleTimeoutMilliseconds(EA::Thread::ThreadPool::kDefaultIdleTimeout), // This is a relative time, not an absolute time. Can be a millisecond value or Thread::kTimeoutNone or Thread::kTimeoutImmediate. + mnProcessorMask(0xffffffff), + mDefaultThreadParameters() +{ + // Empty +} + + +EA::Thread::ThreadPool::Job::Job() + : mnJobID(-1), mpRunnable(NULL), mpFunction(NULL), mpContext(NULL) +{ + // Empty +} + + +EA::Thread::ThreadPool::ThreadInfo::ThreadInfo() + : mbActive(false), + mbQuit(false), + //mbPersistent(false), + mpThread(NULL), + mpThreadPool(NULL), + mCurrentJob() +{ + // Empty +} + + +EA::Thread::ThreadPool::ThreadPool(const ThreadPoolParameters* pThreadPoolParameters, bool bDefaultParameters) + : mbInitialized(false), + mnMinCount(kDefaultMinCount), + mnMaxCount(kDefaultMaxCount), + mnCurrentCount(0), + mnActiveCount(0), + mnIdleTimeoutMilliseconds(kDefaultIdleTimeout), + mnProcessorMask((unsigned)kDefaultProcessorMask), + mnProcessorCount(0), + mnNextProcessor(0), + mnPauseCount(0), + mnLastJobID(0), + mDefaultThreadParameters(), + mThreadCondition(NULL, false), // Explicitly don't initialize. + mThreadMutex(NULL, false), // Explicitly don't initialize. + mThreadInfoList(), + mJobList() +{ + if(!pThreadPoolParameters && bDefaultParameters) + { + ThreadPoolParameters parameters; + Init(¶meters); + } + else + Init(pThreadPoolParameters); +} + + +EA::Thread::ThreadPool::~ThreadPool() +{ + Shutdown(kJobWaitAll, kTimeoutNone); + EAT_ASSERT(mJobList.empty() && mThreadInfoList.empty() && (mnCurrentCount == 0) && (mnActiveCount == 0) && (mThreadMutex.GetLockCount() == 0)); +} + + +EA_DISABLE_VC_WARNING(4296 4706) // '>=' : expression is always true and assignment within conditional expression (in the assert) + + +#if EAT_ASSERT_ENABLED + template + inline bool EATIsUnsigned(T) + { return (((T)(-1) >> 1) != (T)(-1)); } +#endif + + +// If mDefaultThreadParameters.mnProcessor is set to kThreadPoolParametersProcessorDefault, +// then the ThreadPool controls what processors the thread executes on. Otherwise ThreadPool +// doesn't set the thread affinity itself. +static const int kThreadPoolParametersProcessorDefault = -1; + + +bool EA::Thread::ThreadPool::Init(const ThreadPoolParameters* pThreadPoolParameters) +{ + if(!mbInitialized) + { + if(pThreadPoolParameters && (mnCurrentCount == 0)) + { + mbInitialized = true; + + mnMinCount = pThreadPoolParameters->mnMinCount; + mnMaxCount = pThreadPoolParameters->mnMaxCount; + mnCurrentCount = (int)pThreadPoolParameters->mnInitialCount; + mnIdleTimeoutMilliseconds = pThreadPoolParameters->mnIdleTimeoutMilliseconds; + mnProcessorMask = pThreadPoolParameters->mnProcessorMask; + mDefaultThreadParameters = pThreadPoolParameters->mDefaultThreadParameters; + mnProcessorCount = (uint32_t)EA::Thread::GetProcessorCount(); // We currently assume this value is constant at runtime. + + // Do bounds checking. + //if(mnMinCount < 0) // This check is unnecessary because mnMinCount is of an + // mnMinCount = 0; // unsigned data type. We assert for this unsigned-ness below. + EAT_ASSERT(EATIsUnsigned(mnMinCount)); + + if(mnMaxCount > EA_THREAD_POOL_MAX_SIZE) + mnMaxCount = EA_THREAD_POOL_MAX_SIZE; + + if(mnCurrentCount < (int)mnMinCount) + mnCurrentCount = (int)mnMinCount; + + if(mnCurrentCount > (int)mnMaxCount) + mnCurrentCount = (int)mnMaxCount; + + // Make sure the processor mask refers to existing processors. + const int processorMask = (1 << mnProcessorCount) - 1; // So for a processor count of 8 we have a mask of 11111111 (255) + + if((mnProcessorMask & processorMask) == 0) + mnProcessorMask = 0xffffffff; + + mDefaultThreadParameters.mpStack = NULL; // You can't specify a default stack location, as every thread needs a unique one. + if(mDefaultThreadParameters.mnProcessor != EA::Thread::kProcessorAny) // If the user hasn't set threads to execute on any processor chosen by the OS... + mDefaultThreadParameters.mnProcessor = kThreadPoolParametersProcessorDefault; // then use our default processing, which is for us to currently round-robin the processor used. + + ConditionParameters mnp; + mThreadCondition.Init(&mnp); + + MutexParameters mtp; + mThreadMutex.Init(&mtp); + + mThreadMutex.Lock(); + const int nDesiredCount((int)mnCurrentCount); + mnCurrentCount = 0; + AdjustThreadCount((unsigned int)nDesiredCount); + mThreadMutex.Unlock(); + + return true; + } + } + return false; +} + + +EA_RESTORE_VC_WARNING() // 4296 4706 + + +bool EA::Thread::ThreadPool::Shutdown(JobWait jobWait, const ThreadTime& timeoutAbsolute) +{ + int nResult; + + if(mbInitialized) + { + mbInitialized = false; + + nResult = WaitForJobCompletion(-1, jobWait, timeoutAbsolute); + + mThreadMutex.Lock(); + + // If jobWait is kJobWaitNone, then we nuke all existing jobs. + if(jobWait == kJobWaitNone) + mJobList.clear(); + + // Leave a message to tell the thread to quit. + for(ThreadInfoList::iterator it(mThreadInfoList.begin()), itEnd(mThreadInfoList.end()); it != itEnd; ) + { + ThreadInfo* const pThreadInfo = *it; + + pThreadInfo->mbQuit = true; + //pThreadInfo->mbPersistent = false; + + // If somehow the thread isn't running (possibly because it never started), manually remove it. + if(pThreadInfo->mpThread->GetStatus() != EA::Thread::Thread::kStatusRunning) + it = mThreadInfoList.erase(it); + else + ++it; + } + + // Wake up any threads that may be blocked on a condition variable wait. + mThreadCondition.Signal(true); + + // Make sure we unlock after we signal, lest there be a certain kind of race condition. + mThreadMutex.Unlock(); + + // Wait for any existing threads to quit. + // Todo: Replace this poor polling loop with Thread::Wait calls. + // Doing so requires a little finessing with the thread + // objects in the list. Possibly make ThreadInfo ref-counted. + while(!mThreadInfoList.empty()) + { + ThreadSleep(1); + EAReadBarrier(); + } + + mThreadMutex.Lock(); + mnPauseCount = 0; + mThreadMutex.Unlock(); + } + else + nResult = kResultOK; + + return (nResult == kResultOK); +} + + +intptr_t EA::Thread::ThreadPool::ThreadFunction(void* pContext) +{ + ThreadInfo* const pThreadInfo = reinterpret_cast(pContext); + ThreadPool* const pThreadPool = pThreadInfo->mpThreadPool; + Condition* const pCondition = &pThreadPool->mThreadCondition; + Mutex* const pMutex = &pThreadPool->mThreadMutex; + + pMutex->Lock(); + + while(!pThreadInfo->mbQuit) + { + if(!pThreadPool->mJobList.empty()) + { + pThreadInfo->mCurrentJob = pThreadPool->mJobList.front(); + pThreadPool->mJobList.pop_front(); + pThreadInfo->mbActive = true; + ++pThreadPool->mnActiveCount; // Atomic integer operation. + pMutex->Unlock(); + + // Do the job here. It's important that we keep the mutex unlocked while doing the job. + if(pThreadInfo->mCurrentJob.mpRunnable) + pThreadInfo->mCurrentJob.mpRunnable->Run(pThreadInfo->mCurrentJob.mpContext); + else if(pThreadInfo->mCurrentJob.mpFunction) + pThreadInfo->mCurrentJob.mpFunction(pThreadInfo->mCurrentJob.mpContext); + else + pThreadInfo->mbQuit = true; // Tell ourself to quit. + + // Problem: We are not paying attention to the pThreadInfo->mbPersistent variable. + // We don't have an easy way of dealing with it because we don't have a means for + // the ThreadPool to direct quit commands to individual threads. For now we don't + // pay attention to mbPersistent and require that persistence be controlled by + // the min/max thread count settings. + + pMutex->Lock(); + + --pThreadPool->mnActiveCount; // Atomic integer operation. + pThreadInfo->mbActive = false; + } + else + { + // The wait call here will unlock the condition variable and will re-lock it upon return. + EA::Thread::ThreadTime timeoutAbsolute = (GetThreadTime() + pThreadPool->mnIdleTimeoutMilliseconds); + + if (pThreadPool->mnIdleTimeoutMilliseconds == kTimeoutNone) + timeoutAbsolute = kTimeoutNone; + else if(pThreadPool->mnIdleTimeoutMilliseconds == kTimeoutImmediate) + timeoutAbsolute = kTimeoutImmediate; + else if(timeoutAbsolute == kTimeoutNone) // If it coincidentally is the magic kTimeoutNone value... + timeoutAbsolute -= 1; + + const Condition::Result result = pCondition->Wait(pMutex, timeoutAbsolute); + + if(result != Condition::kResultOK) // If result is an error then what do we do? Is there a + pThreadInfo->mbQuit = true; // specific reason to quit? There's no good solution here, + } // but on the other hand this should never happen in practice. + } + + pThreadPool->RemoveThread(pThreadInfo); + + pMutex->Unlock(); + + return 0; +} + + +EA::Thread::ThreadPool::Result EA::Thread::ThreadPool::QueueJob(const Job& job, Thread** ppThread, bool /*bEnableDeferred*/) +{ + if(mbInitialized){ + mThreadMutex.Lock(); + + // If there are other threads busy with jobs or other threads soon to be busy with jobs and if the thread count is less than the maximum allowable, bump up the thread count by one. + EAT_ASSERT(mnActiveCount <= mnCurrentCount); + if((((int)mnActiveCount >= mnCurrentCount) || !mJobList.empty()) && (mnCurrentCount < (int)mnMaxCount)) + AdjustThreadCount((unsigned)(mnCurrentCount + 1)); + + mJobList.push_back(job); + FixThreads(); + + if(mnPauseCount == 0) + mThreadCondition.Signal(false); // Wake up one thread to work on this. + + mThreadMutex.Unlock(); + + if(ppThread){ + // In this case the caller wants to know what thread got the job. + // So we wait until we know what caller got the job. + // Todo: Complete this. + *ppThread = NULL; + } + + return kResultDeferred; + } + + return kResultError; +} + + +int EA::Thread::ThreadPool::Begin(IRunnable* pRunnable, void* pContext, Thread** ppThread, bool bEnableDeferred) +{ + Job job; + job.mnJobID = mnLastJobID.Increment(); + job.mpRunnable = pRunnable; + job.mpFunction = NULL; + job.mpContext = pContext; + + if(QueueJob(job, ppThread, bEnableDeferred) != kResultError) + return job.mnJobID; + return kResultError; +} + + +int EA::Thread::ThreadPool::Begin(RunnableFunction pFunction, void* pContext, Thread** ppThread, bool bEnableDeferred) +{ + Job job; + job.mnJobID = mnLastJobID.Increment(); + job.mpRunnable = NULL; + job.mpFunction = pFunction; + job.mpContext = pContext; + + if(QueueJob(job, ppThread, bEnableDeferred) != kResultError) + return job.mnJobID; + return kResultError; +} + + +int EA::Thread::ThreadPool::WaitForJobCompletion(int nJob, JobWait jobWait, const ThreadTime& timeoutAbsolute) +{ + int nResult = kResultError; + + if(nJob == -1){ + // We have a problem here in that we need to wait for all threads to finish + // but the only way to wait for them to finish is to use the Thread::WaitForEnd + // function. But when the thread exits, it destroys the Thread object rendering + // it unsafe for us to use that object in any safe way here. We can rearrange + // things to allow this to work more cleanly, but in the meantime we spin and + // sleep, which is not a good solution if the worker threads are of a lower + // priority than this sleeping thread, as this thread will steal their time. + + if(jobWait == kJobWaitNone){ + // Do nothing. + nResult = kResultOK; + } + else if(jobWait == kJobWaitCurrent){ + // Wait for currently running jobs to complete. + while((mnActiveCount != 0) && (GetThreadTime() < timeoutAbsolute)) + ThreadSleep(10); + if(mnActiveCount == 0) + nResult = kResultOK; + else + nResult = kResultTimeout; + } + else{ // jobWait == kJobWaitAll + // Wait for all current and queued jobs to complete. + bool shouldContinue = true; + + while(shouldContinue) + { + mThreadMutex.Lock(); + shouldContinue = (((mnActiveCount != 0) || !mJobList.empty()) && (GetThreadTime() < timeoutAbsolute)); + mThreadMutex.Unlock(); + if(shouldContinue) + ThreadSleep(10); + } + + mThreadMutex.Lock(); + + if((mnActiveCount == 0) && mJobList.empty()) + nResult = kResultOK; + else + nResult = kResultTimeout; + + mThreadMutex.Unlock(); + } + } + else{ + // Like above we do the wait via polling. Ideally we want to set up a + // mechanism whereby we sleep until an alarm wakes us. This can perhaps + // be done by setting a flag in the job which causes the job to signal + // the alarm when complete. In the meantime we will follow the simpler + // behaviour we have here. + + bool bJobExists; + + for(;;){ + bJobExists = false; + mThreadMutex.Lock(); + + // Search the list of jobs yet to become active to see if the job exists in there. + for(JobList::iterator it(mJobList.begin()); it != mJobList.end(); ++it){ + const Job& job = *it; + + if(job.mnJobID == nJob){ // If the user's job was found... + bJobExists = true; + nResult = kResultTimeout; + } + } + + // Search the list of jobs actively executing as well. + for(ThreadInfoList::iterator it(mThreadInfoList.begin()); it != mThreadInfoList.end(); ++it){ + const ThreadInfo* const pThreadInfo = *it; + const Job& job = pThreadInfo->mCurrentJob; + + // Note the thread must be active for the Job assigned to it be valid. + if(pThreadInfo->mbActive && job.mnJobID == nJob){ // If the user's job was found... + bJobExists = true; + nResult = kResultTimeout; + } + } + + mThreadMutex.Unlock(); + if(!bJobExists || (GetThreadTime() >= timeoutAbsolute)) + break; + ThreadSleep(10); + } + + if(!bJobExists) + nResult = kResultOK; + } + + return nResult; +} + + +void EA::Thread::ThreadPool::Pause(bool bPause) +{ + if(bPause) + ++mnPauseCount; + else{ + if(mnPauseCount.Decrement() == 0){ + mThreadMutex.Lock(); + if(!mJobList.empty()) + mThreadCondition.Signal(true); + mThreadMutex.Unlock(); + } + } +} + + +void EA::Thread::ThreadPool::Lock() +{ + mThreadMutex.Lock(); +} + + +void EA::Thread::ThreadPool::Unlock() +{ + mThreadMutex.Unlock(); +} + + +void EA::Thread::ThreadPool::SetupThreadParameters(EA::Thread::ThreadParameters& tp) +{ + if(tp.mnProcessor == kThreadPoolParametersProcessorDefault) // If we are to manipulate tp.mnProcessor... + { + if(mnProcessorMask != 0xffffffff) // If we are not using the default... + { + // We round-robin mnNextProcessor within our mnProcessorMask. + while(((1 << mnNextProcessor) & mnProcessorMask) == 0) + ++mnNextProcessor; + + mnNextProcessor %= mnProcessorCount; + tp.mnProcessor = (int)mnNextProcessor++; + } + } +} + + +EA::Thread::ThreadPool::ThreadInfo* EA::Thread::ThreadPool::AddThread(const EA::Thread::ThreadParameters& tp, bool bBeginThread) +{ + ThreadInfo* const pThreadInfo = CreateThreadInfo(); + EAT_ASSERT(pThreadInfo != NULL); + + if(pThreadInfo) + { + AddThread(pThreadInfo); + + if(bBeginThread) + { + ThreadParameters tpUsed(tp); + SetupThreadParameters(tpUsed); // This function sets tpUsed.mnProcessor + + pThreadInfo->mpThread->Begin(ThreadFunction, pThreadInfo, &tpUsed); + } + } + + return pThreadInfo; +} + + +// Gets the ThreadInfo for the nth Thread identified by index. +// You must call this function within a Lock/Unlock pair on the thread pool. +EA::Thread::ThreadPool::ThreadInfo* EA::Thread::ThreadPool::GetThreadInfo(int index) +{ + EA::Thread::AutoMutex autoMutex(mThreadMutex); + + int i = 0; + + for(ThreadInfoList::iterator it = mThreadInfoList.begin(); it != mThreadInfoList.end(); ++it) + { + if(i == index) + { + ThreadInfo* pThreadInfo = *it; + return pThreadInfo; + } + + ++i; + } + + return NULL; +} + + +// Unless you call this function while the Pool is locked (via Lock), the return +// value may be out of date by the time you read it. +int EA::Thread::ThreadPool::GetThreadCount() +{ + EA::Thread::AutoMutex autoMutex(mThreadMutex); + + return (int)mThreadInfoList.size(); +} + + +EA::Thread::ThreadPool::ThreadInfo* EA::Thread::ThreadPool::CreateThreadInfo() +{ + // Currently we assume that allocation never fails. + ThreadInfo* const pThreadInfo = gpAllocator ? new(gpAllocator->Alloc(sizeof(ThreadInfo))) ThreadInfo : new ThreadInfo; + + if(pThreadInfo) + { + pThreadInfo->mbActive = false; + pThreadInfo->mbQuit = false; + pThreadInfo->mpThreadPool = this; + pThreadInfo->mpThread = gpAllocator ? new(gpAllocator->Alloc(sizeof(Thread))) Thread : new Thread; + } + + return pThreadInfo; +} + + +void EA::Thread::ThreadPool::AdjustThreadCount(unsigned nDesiredCount) +{ + // This function doesn't read mnMinCount/mnMaxCount, as it expects the caller to do so. + // Assumes that condition variable is locked. + int nAdjustment = (int)(nDesiredCount - mnCurrentCount); + + while(nAdjustment > 0) // If we are to create threads... + { + ThreadInfo* const pThreadInfo = CreateThreadInfo(); + EAT_ASSERT(pThreadInfo != NULL); + + AddThread(pThreadInfo); + + ThreadParameters tpUsed(mDefaultThreadParameters); + SetupThreadParameters(tpUsed); // This function sets tpUsed.mnProcessor + + pThreadInfo->mpThread->Begin(ThreadFunction, pThreadInfo, &tpUsed); + nAdjustment--; + } + + while(nAdjustment < 0) // If we are to quit threads... + { + // An empty job is a signal for a thread to quit. + QueueJob(Job(), NULL, true); + nAdjustment++; + } + + FixThreads(); // Makes sure that mnCurrentCount really does match the number of threads waiting for work. +} + + + +void EA::Thread::ThreadPool::AddThread(ThreadInfo* pThreadInfo) +{ + // Assumes that condition variable is locked. + mThreadInfoList.push_back(pThreadInfo); + ++mnCurrentCount; +} + + +void EA::Thread::ThreadPool::RemoveThread(ThreadInfo* pThreadInfo) +{ + // Assumes that condition variable is locked. + ThreadInfoList::iterator it = mThreadInfoList.find(pThreadInfo); + EAT_ASSERT(it != mThreadInfoList.end()); + + if(it != mThreadInfoList.end()) + { + if(gpAllocator) + { + pThreadInfo->mpThread->~Thread(); + gpAllocator->Free(pThreadInfo->mpThread); + } + else + delete pThreadInfo->mpThread; + + pThreadInfo->mpThread = NULL; + mThreadInfoList.erase(it); + + if(gpAllocator) + { + pThreadInfo->~ThreadInfo(); + gpAllocator->Free(pThreadInfo); + } + else + delete pThreadInfo; + + --mnCurrentCount; + } +} + + +// FixThreads +// We have a small in problem in that the system allows threads to explicitly exit at any time without +// returning to the caller. Many operating systems with thread support don't have a mechanism to enable +// you to tell you via a callback when a thread has exited. Due to this latter problem, it is possible +// that threads could exit without us ever finding out about it. So we poll the threads to catch up +// to their state in such cases here. +void EA::Thread::ThreadPool::FixThreads() +{ + // Assumes that condition variable is locked. + for(ThreadInfoList::iterator it(mThreadInfoList.begin()), itEnd(mThreadInfoList.end()); it != itEnd; ++it) + { + ThreadInfo* const pThreadInfo = *it; + + // Fix any threads which have exited via a thread exit and not by simply returning to the caller. + const EA::Thread::Thread::Status status = pThreadInfo->mpThread->GetStatus(); + + if(status == EA::Thread::Thread::kStatusEnded) + pThreadInfo->mpThread->Begin(ThreadFunction, pThreadInfo, &mDefaultThreadParameters); + } +} + + +EA::Thread::ThreadPool* EA::Thread::ThreadPoolFactory::CreateThreadPool() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::ThreadPool))) EA::Thread::ThreadPool; + else + return new EA::Thread::ThreadPool; +} + + +void EA::Thread::ThreadPoolFactory::DestroyThreadPool(EA::Thread::ThreadPool* pThreadPool) +{ + if(gpAllocator) + { + pThreadPool->~ThreadPool(); + gpAllocator->Free(pThreadPool); + } + else + delete pThreadPool; +} + + +size_t EA::Thread::ThreadPoolFactory::GetThreadPoolSize() +{ + return sizeof(EA::Thread::ThreadPool); +} + + +EA::Thread::ThreadPool* EA::Thread::ThreadPoolFactory::ConstructThreadPool(void* pMemory) +{ + return new(pMemory) EA::Thread::ThreadPool; +} + + +void EA::Thread::ThreadPoolFactory::DestructThreadPool(EA::Thread::ThreadPool* pThreadPool) +{ + pThreadPool->~ThreadPool(); +} + +EA_RESTORE_VC_WARNING() diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_rwmutex.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_rwmutex.cpp new file mode 100644 index 00000000..e4bec53b --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_rwmutex.cpp @@ -0,0 +1,259 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include + +EA_DISABLE_VC_WARNING(4985) // 'ceil': attributes not present on previous declaration.1> C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\INCLUDE\intrin.h(142) : see declaration of 'ceil' + +#include +#include +#include +#include // include new for placement new operator +#include + +EA_DISABLE_VC_WARNING(4996) // This function or variable may be unsafe / deprecated. + + EARWMutexData::EARWMutexData() + : mnReadWaiters(0), + mnWriteWaiters(0), + mnReaders(0), + mThreadIdWriter(EA::Thread::kThreadIdInvalid), + mMutex(NULL, false), + mReadCondition(NULL, false), + mWriteCondition(NULL, false) + { + // Empty + } + + + EA::Thread::RWMutexParameters::RWMutexParameters(bool bIntraProcess, const char* pName) + : mbIntraProcess(bIntraProcess) + { + (void)pName; // Suppress possible warnings. + + #ifdef EA_PLATFORM_WINDOWS + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + #endif + } + + + EA::Thread::RWMutex::RWMutex(const RWMutexParameters* pRWMutexParameters, bool bDefaultParameters) + { + if(!pRWMutexParameters && bDefaultParameters) + { + RWMutexParameters parameters; + Init(¶meters); + } + else + Init(pRWMutexParameters); + } + + + EA::Thread::RWMutex::~RWMutex() + { + // Possibly do asserts here. + } + + + bool EA::Thread::RWMutex::Init(const RWMutexParameters* pRWMutexParameters) + { + if(pRWMutexParameters) + { + #if EATHREAD_MULTIPROCESSING_OS + EAT_ASSERT(pRWMutexParameters->mbIntraProcess); // We don't currently have support for intra-process RWMutex on these platforms (and any multi-process platform). + #endif + + MutexParameters mup(pRWMutexParameters->mbIntraProcess); + mRWMutexData.mMutex.Init(&mup); + + ConditionParameters mop(pRWMutexParameters->mbIntraProcess); + mRWMutexData.mReadCondition.Init(&mop); + mRWMutexData.mWriteCondition.Init(&mop); + return true; + } + + return false; + } + + + int EA::Thread::RWMutex::Lock(LockType lockType, const ThreadTime& timeoutAbsolute) + { + int result = 0; + + mRWMutexData.mMutex.Lock(); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily. + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + + // We cannot obtain a write lock recursively, else we will deadlock. + // Alternatively, we can build a bunch of extra logic to deal with this. + EAT_ASSERT(mRWMutexData.mThreadIdWriter != GetThreadId()); + + // Assert that there aren't both readers and writers at the same time. + EAT_ASSERT(!((mRWMutexData.mThreadIdWriter != kThreadIdInvalid) && mRWMutexData.mnReaders)); + + if(lockType == kLockTypeRead) + { + while(mRWMutexData.mThreadIdWriter != kThreadIdInvalid) + { + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + + mRWMutexData.mnReadWaiters++; + const Condition::Result mresult = mRWMutexData.mReadCondition.Wait(&mRWMutexData.mMutex, timeoutAbsolute); + mRWMutexData.mnReadWaiters--; + + EAT_ASSERT(mresult != EA::Thread::Condition::kResultError); + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + + if(mresult == Condition::kResultTimeout) + { + mRWMutexData.mMutex.Unlock(); + return kResultTimeout; + } + } + + result = ++mRWMutexData.mnReaders; // This is not an atomic operation. We are within a mutex lock. + } + else if(lockType == kLockTypeWrite) + { + while((mRWMutexData.mnReaders > 0) || (mRWMutexData.mThreadIdWriter != kThreadIdInvalid)) + { + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + + mRWMutexData.mnWriteWaiters++; + const Condition::Result mresult = mRWMutexData.mWriteCondition.Wait(&mRWMutexData.mMutex, timeoutAbsolute); + mRWMutexData.mnWriteWaiters--; + + EAT_ASSERT(mresult != EA::Thread::Condition::kResultError); + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + + if(mresult == Condition::kResultTimeout) + { + mRWMutexData.mMutex.Unlock(); + return kResultTimeout; + } + } + + result = 1; + mRWMutexData.mThreadIdWriter = GetThreadId(); + } + + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + mRWMutexData.mMutex.Unlock(); + + return result; + } + + + int EA::Thread::RWMutex::Unlock() + { + mRWMutexData.mMutex.Lock(); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily. + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + + if(mRWMutexData.mThreadIdWriter != kThreadIdInvalid) + { + EAT_ASSERT(mRWMutexData.mThreadIdWriter == GetThreadId()); + + //Possibly enable this if we want some runtime error checking at some cost. + //if(mRWMutexData.mThreadIdWriter == GetThreadId()){ + // mRWMutexData.mMutex.Unlock(); + // return kResultError; + //} + + mRWMutexData.mThreadIdWriter = kThreadIdInvalid; + } + else + { + EAT_ASSERT(mRWMutexData.mnReaders >= 1); + + //Possibly enable this if we want some runtime error checking at some cost. + //if(mRWMutexData.mnReaders < 1){ + // mRWMutexData.mMutex.Unlock(); + // return kResultError; + //} + + const int nNewReaders = --mRWMutexData.mnReaders; // This is not an atomic operation. We are within a mutex lock. + if(nNewReaders > 0) + { + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + mRWMutexData.mMutex.Unlock(); + return nNewReaders; + } + } + + if(mRWMutexData.mnWriteWaiters > 0) + mRWMutexData.mWriteCondition.Signal(false); + else if(mRWMutexData.mnReadWaiters > 0) + mRWMutexData.mReadCondition.Signal(true); + + EAT_ASSERT(mRWMutexData.mMutex.GetLockCount() == 1); + mRWMutexData.mMutex.Unlock(); + + return 0; + } + + + int EA::Thread::RWMutex::GetLockCount(LockType lockType) + { + if(lockType == kLockTypeRead) + return mRWMutexData.mnReaders; + else if((lockType == kLockTypeWrite) && (mRWMutexData.mThreadIdWriter != kThreadIdInvalid)) + return 1; + return 0; + } + + + + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::RWMutex* EA::Thread::RWMutexFactory::CreateRWMutex() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::RWMutex))) EA::Thread::RWMutex; + else + return new EA::Thread::RWMutex; +} + +void EA::Thread::RWMutexFactory::DestroyRWMutex(EA::Thread::RWMutex* pRWMutex) +{ + if(gpAllocator) + { + pRWMutex->~RWMutex(); + gpAllocator->Free(pRWMutex); + } + else + delete pRWMutex; +} + +size_t EA::Thread::RWMutexFactory::GetRWMutexSize() +{ + return sizeof(EA::Thread::RWMutex); +} + +EA::Thread::RWMutex* EA::Thread::RWMutexFactory::ConstructRWMutex(void* pMemory) +{ + return new(pMemory) EA::Thread::RWMutex; +} + +void EA::Thread::RWMutexFactory::DestructRWMutex(EA::Thread::RWMutex* pRWMutex) +{ + pRWMutex->~RWMutex(); +} + +EA_RESTORE_VC_WARNING() // 4996 +EA_RESTORE_VC_WARNING() // 4985 + + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_rwmutex_ip.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_rwmutex_ip.cpp new file mode 100644 index 00000000..00fc2ed5 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_rwmutex_ip.cpp @@ -0,0 +1,361 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include // include new for placement new operator +#include + + +#if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + + /////////////////////////////////////////////////////////////////////////// + // EARWMutexIPData + /////////////////////////////////////////////////////////////////////////// + + EA::Thread::EARWMutexIPData::EARWMutexIPData() + : mSharedData(), // This still needs to be Init-ed. + mMutex(NULL), + mReadSemaphore(NULL), + mWriteSemaphore(NULL) + { + } + + EA::Thread::EARWMutexIPData::~EARWMutexIPData() + { + // mSharedData.Shutdown(); // This shouldn't be necessary, as the SharedData dtor will do this itself. + } + + bool EA::Thread::EARWMutexIPData::Init(const char* pName) + { + char mutexName[256]; + mutexName[0] = '\0'; + if(pName) + strcpy(mutexName, pName); + strcat(mutexName, ".Mutex"); + mMutex = CreateMutexA(NULL, FALSE, mutexName); + + char readSemaphoreName[256]; + readSemaphoreName[0] = '\0'; + if(pName) + strcpy(readSemaphoreName, pName); + strcat(readSemaphoreName, ".SemR"); + mReadSemaphore = CreateSemaphoreA(NULL, 0, 9999, readSemaphoreName); + + char writeSemaphoreName[256]; + writeSemaphoreName[0] = '\0'; + if(pName) + strcpy(writeSemaphoreName, pName); + strcat(writeSemaphoreName, ".SemW"); + mWriteSemaphore = CreateSemaphoreA(NULL, 0, 9999, writeSemaphoreName); + + return mSharedData.Init(pName); + } + + void EA::Thread::EARWMutexIPData::Shutdown() + { + if(mMutex) + { + CloseHandle(mMutex); + mMutex = NULL; + } + + if(mReadSemaphore) + { + CloseHandle(mReadSemaphore); + mReadSemaphore = NULL; + } + + if(mWriteSemaphore) + { + CloseHandle(mWriteSemaphore); + mWriteSemaphore = NULL; + } + + mSharedData.Shutdown(); + } + + + + /////////////////////////////////////////////////////////////////////////// + // RWMutexIPParameters + /////////////////////////////////////////////////////////////////////////// + + EA::Thread::RWMutexIPParameters::RWMutexIPParameters(bool bIntraProcess, const char* pName) + : mbIntraProcess(bIntraProcess) + { + #ifdef EA_PLATFORM_WINDOWS + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + #else + (void)pName; // Suppress possible warnings. + #endif + } + + + + /////////////////////////////////////////////////////////////////////////// + // RWMutexIP + /////////////////////////////////////////////////////////////////////////// + + EA::Thread::RWMutexIP::RWMutexIP(const RWMutexIPParameters* pRWMutexIPParameters, bool bDefaultParameters) + { + if(!pRWMutexIPParameters && bDefaultParameters) + { + RWMutexIPParameters parameters; + Init(¶meters); + } + else + Init(pRWMutexIPParameters); + } + + + EA::Thread::RWMutexIP::~RWMutexIP() + { + } + + + bool EA::Thread::RWMutexIP::Init(const RWMutexIPParameters* pRWMutexIPParameters) + { + if(pRWMutexIPParameters) + { + // Must provide a valid name for inter-process RWMutex. + EAT_ASSERT(pRWMutexIPParameters->mbIntraProcess || pRWMutexIPParameters->mName[0]); + + return mRWMutexIPData.Init(pRWMutexIPParameters->mName); + } + + return false; + } + + + int EA::Thread::RWMutexIP::Lock(LockType lockType, const ThreadTime& /*timeoutAbsolute*/) + { + int result = 0; + + WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily. + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + + // We cannot obtain a write lock recursively, else we will deadlock. + // Alternatively, we can build a bunch of extra logic to deal with this. + EAT_ASSERT(mRWMutexIPData.mSharedData->mThreadIdWriter != ::GetCurrentThreadId()); + + // Assert that there aren't both readers and writers at the same time. + EAT_ASSERT(!((mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid) && mRWMutexIPData.mSharedData->mnReaders)); + + if(lockType == kLockTypeRead) + { + while(mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid) + { + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + + mRWMutexIPData.mSharedData->mnReadWaiters++; + ReleaseMutex(mRWMutexIPData.mMutex); + DWORD dwResult = WaitForSingleObject(mRWMutexIPData.mReadSemaphore, INFINITE); // To do: support timeoutAbsolute + WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE); + mRWMutexIPData.mSharedData->mnReadWaiters--; + + EAT_ASSERT(dwResult != WAIT_FAILED); + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + + if(dwResult == WAIT_TIMEOUT) + { + ReleaseMutex(mRWMutexIPData.mMutex); + return kResultTimeout; + } + } + + result = ++mRWMutexIPData.mSharedData->mnReaders; // This is not an atomic operation. We are within a mutex lock. + } + else if(lockType == kLockTypeWrite) + { + while((mRWMutexIPData.mSharedData->mnReaders > 0) || // While somebody has the read lock or + (mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid)) // somebody has the write lock... go back to waiting. + { + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + + mRWMutexIPData.mSharedData->mnWriteWaiters++; + ReleaseMutex(mRWMutexIPData.mMutex); + DWORD dwResult = WaitForSingleObject(mRWMutexIPData.mWriteSemaphore, INFINITE); // To do: support timeoutAbsolute + WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE); + mRWMutexIPData.mSharedData->mnWriteWaiters--; + + EAT_ASSERT(dwResult != WAIT_FAILED); + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + + if(dwResult == WAIT_TIMEOUT) + { + ReleaseMutex(mRWMutexIPData.mMutex); + return kResultTimeout; + } + } + + result = 1; + mRWMutexIPData.mSharedData->mThreadIdWriter = ::GetCurrentThreadId(); + } + + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + ReleaseMutex(mRWMutexIPData.mMutex); + + return result; + } + + + int EA::Thread::RWMutexIP::Unlock() + { + WaitForSingleObject(mRWMutexIPData.mMutex, INFINITE); // This lock should always be fast, as it belongs to us and we only hold onto it very temporarily. + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + + if(mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid) // If we have a write lock... + { + EAT_ASSERT(mRWMutexIPData.mSharedData->mThreadIdWriter == ::GetCurrentThreadId()); + mRWMutexIPData.mSharedData->mThreadIdWriter = kSysThreadIdInvalid; + } + else // Else we have a read lock... + { + EAT_ASSERT(mRWMutexIPData.mSharedData->mnReaders >= 1); + + const int nNewReaders = --mRWMutexIPData.mSharedData->mnReaders; // This is not an atomic operation. We are within a mutex lock. + if(nNewReaders > 0) + { + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + ReleaseMutex(mRWMutexIPData.mMutex); + return nNewReaders; + } + } + + if(mRWMutexIPData.mSharedData->mnWriteWaiters > 0) // We ignore the possibility that + { + ReleaseSemaphore(mRWMutexIPData.mWriteSemaphore, 1, NULL); + // We rely on the released write waiter to decrement mnWriteWaiters. + // If the released write waiter doesn't wake up for a while, it's possible that the ReleaseMutex below + // will be called and another read unlocker will execute this code and release the semaphore again and + // we will have two writers that are released. But this isn't a problem because the released writers + // must still lock our mMutex and contend for the write lock, and one of the two will fail and go back + // to waiting on the semaphore. + } + else if(mRWMutexIPData.mSharedData->mnReadWaiters > 0) + { + // I'm a little concerned about this signal here. We release mnReadWaiters, though it's possible + // that a reader could timeout before this function completes and not all the semaphore count + // will be claimed by waiters. However, the read wait code in the Lock function above does + // seem to be able to handle this case, as it does do a check to make sure it can hold the read + // lock before it claims it. + ReleaseSemaphore(mRWMutexIPData.mReadSemaphore, mRWMutexIPData.mSharedData->mnReadWaiters, NULL); + } + + //EAT_ASSERT(mRWMutexIPData.mMutex.GetLockCount() == 1); + ReleaseMutex(mRWMutexIPData.mMutex); + + return 0; + } + + + int EA::Thread::RWMutexIP::GetLockCount(LockType lockType) + { + if(lockType == kLockTypeRead) + return mRWMutexIPData.mSharedData->mnReaders; + else if((lockType == kLockTypeWrite) && (mRWMutexIPData.mSharedData->mThreadIdWriter != kSysThreadIdInvalid)) + return 1; + return 0; + } + + +#else + + EA::Thread::RWMutexIPParameters::RWMutexIPParameters(bool /*bIntraProcess*/, const char* /*pName*/) + { + } + + + EA::Thread::RWMutexIP::RWMutexIP(const RWMutexIPParameters* /*pRWMutexIPParameters*/, bool /*bDefaultParameters*/) + { + } + + + EA::Thread::RWMutexIP::~RWMutexIP() + { + } + + + bool EA::Thread::RWMutexIP::Init(const RWMutexIPParameters* /*pRWMutexIPParameters*/) + { + return false; + } + + + int EA::Thread::RWMutexIP::Lock(LockType /*lockType*/, const ThreadTime& /*timeoutAbsolute*/) + { + return 0; + } + + + int EA::Thread::RWMutexIP::Unlock() + { + return 0; + } + + + int EA::Thread::RWMutexIP::GetLockCount(LockType /*lockType*/) + { + return 0; + } + +#endif // EA_PLATFORM_XXX + + + + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::RWMutexIP* EA::Thread::RWMutexIPFactory::CreateRWMutexIP() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::RWMutexIP))) EA::Thread::RWMutexIP; + else + return new EA::Thread::RWMutexIP; +} + +void EA::Thread::RWMutexIPFactory::DestroyRWMutexIP(EA::Thread::RWMutexIP* pRWMutexIP) +{ + if(gpAllocator) + { + pRWMutexIP->~RWMutexIP(); + gpAllocator->Free(pRWMutexIP); + } + else + delete pRWMutexIP; +} + +size_t EA::Thread::RWMutexIPFactory::GetRWMutexIPSize() +{ + return sizeof(EA::Thread::RWMutexIP); +} + +EA::Thread::RWMutexIP* EA::Thread::RWMutexIPFactory::ConstructRWMutexIP(void* pMemory) +{ + return new(pMemory) EA::Thread::RWMutexIP; +} + +void EA::Thread::RWMutexIPFactory::DestructRWMutexIP(EA::Thread::RWMutexIP* pRWMutexIP) +{ + pRWMutexIP->~RWMutexIP(); +} + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_semaphore.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_semaphore.cpp new file mode 100644 index 00000000..a7c9a6fc --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_semaphore.cpp @@ -0,0 +1,351 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +EA_DISABLE_VC_WARNING(4574) +#include +#include +EA_RESTORE_VC_WARNING() + +#if !EA_THREADS_AVAILABLE + #include +#elif EATHREAD_USE_SYNTHESIZED_SEMAPHORE + // Fall through. +#elif 0 //EA_USE_CPP11_CONCURRENCY + #include "cpp11/eathread_semaphore_cpp11.cpp" +#elif defined(EA_PLATFORM_APPLE) + #include "apple/eathread_semaphore_apple.cpp" +#elif defined(EA_PLATFORM_ANDROID) + #include "android/eathread_semaphore_android.cpp" +#elif defined(EA_PLATFORM_SONY) + #include "sony/eathread_semaphore_sony.cpp" +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include "unix/eathread_semaphore_unix.cpp" +#elif defined(EA_PLATFORM_MICROSOFT) + #include "pc/eathread_semaphore_pc.cpp" +#endif + + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::Semaphore* EA::Thread::SemaphoreFactory::CreateSemaphore() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::Semaphore))) EA::Thread::Semaphore; + else + return new EA::Thread::Semaphore; +} + +void EA::Thread::SemaphoreFactory::DestroySemaphore(EA::Thread::Semaphore* pSemaphore) +{ + if(gpAllocator) + { + pSemaphore->~Semaphore(); + gpAllocator->Free(pSemaphore); + } + else + delete pSemaphore; +} + +size_t EA::Thread::SemaphoreFactory::GetSemaphoreSize() +{ + return sizeof(EA::Thread::Semaphore); +} + +EA::Thread::Semaphore* EA::Thread::SemaphoreFactory::ConstructSemaphore(void* pMemory) +{ + return new(pMemory) EA::Thread::Semaphore; +} + +void EA::Thread::SemaphoreFactory::DestructSemaphore(EA::Thread::Semaphore* pSemaphore) +{ + pSemaphore->~Semaphore(); +} + + + +#if EATHREAD_USE_SYNTHESIZED_SEMAPHORE + + EASemaphoreData::EASemaphoreData() + : mCV(), + mMutex(), + mnCount(0), + mnMaxCount(INT_MAX), + mbValid(false) + { + // Empty + } + + + EA::Thread::SemaphoreParameters::SemaphoreParameters(int initialCount, bool bIntraProcess, const char* pName) + : mInitialCount(initialCount), + mMaxCount(INT_MAX), + mbIntraProcess(bIntraProcess) + { + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + } + + + EA::Thread::Semaphore::Semaphore(const SemaphoreParameters* pSemaphoreParameters, bool bDefaultParameters) + { + if(!pSemaphoreParameters && bDefaultParameters) + { + SemaphoreParameters parameters; + Init(¶meters); + } + else + Init(pSemaphoreParameters); + } + + + EA::Thread::Semaphore::Semaphore(int initialCount) + { + SemaphoreParameters parameters(initialCount); + Init(¶meters); + } + + + EA::Thread::Semaphore::~Semaphore() + { + EAT_ASSERT(!mSemaphoreData.mMutex.HasLock()); // The mMutex destructor will also assert this, but here it makes it more obvious this mutex is ours. + } + + + bool EA::Thread::Semaphore::Init(const SemaphoreParameters* pSemaphoreParameters) + { + if(pSemaphoreParameters && (!mSemaphoreData.mbValid)) + { + mSemaphoreData.mbValid = true; // It's not really true unless our member mCV and mMutex init OK. To do: Added functions to our classes that verify they are OK. + mSemaphoreData.mnCount = pSemaphoreParameters->mInitialCount; + mSemaphoreData.mnMaxCount = pSemaphoreParameters->mMaxCount; + + if(mSemaphoreData.mnCount < 0) + mSemaphoreData.mnCount = 0; + + return mSemaphoreData.mbValid; + } + + return false; + } + + + int EA::Thread::Semaphore::Wait(const ThreadTime& timeoutAbsolute) + { + int nReturnValue = kResultError; + int result = mSemaphoreData.mMutex.Lock(); // This mutex is owned by us and will be unlocked immediately in the mCV.Wait call, so we don't apply timeoutAbsolute. To consider: Maybe we should do so, though it's less efficient. + + if(result > 0) // If success... + { + if(timeoutAbsolute == kTimeoutImmediate) + { + if(mSemaphoreData.mnCount.GetValue() >= 1) + nReturnValue = mSemaphoreData.mnCount.Decrement(); + else + nReturnValue = kResultTimeout; + } + else + { + if(mSemaphoreData.mnCount.GetValue() >= 1) // If we can decrement it immediately... + nReturnValue = mSemaphoreData.mnCount.Decrement(); + else // Else we need to wait. + { + Condition::Result cResult; + + do{ + cResult = mSemaphoreData.mCV.Wait(&mSemaphoreData.mMutex, timeoutAbsolute); + } while((cResult == Condition::kResultOK) && (mSemaphoreData.mnCount.GetValue() < 1)); // Always need to check the condition and retry if not matched. In rare cases two threads could return from Wait. + + if(cResult == Condition::kResultOK) // If apparent success... + nReturnValue = mSemaphoreData.mnCount.Decrement(); + else if(cResult == Condition::kResultTimeout) + nReturnValue = kResultTimeout; + else + { + // We return immediately here because mCV.Wait has not locked the mutex for + // us and so we don't want to fall through and unlock it below. Also, it would + // be inefficient for us to lock here and fall through only to unlock it below. + return nReturnValue; + } + } + } + + result = mSemaphoreData.mMutex.Unlock(); + EAT_ASSERT(result >= 0); + if(result < 0) + nReturnValue = kResultError; // This Semaphore is now considered dead and unusable. + } + + return nReturnValue; + } + + + int EA::Thread::Semaphore::Post(int count) + { + EAT_ASSERT(mSemaphoreData.mnCount >= 0); + + int newValue = kResultError; + int result = mSemaphoreData.mMutex.Lock(); + + if(result > 0) + { + // Set the new value to be whatever the current value is. + newValue = mSemaphoreData.mnCount.GetValue(); + + if((mSemaphoreData.mnMaxCount - count) < newValue) // If count would cause an overflow... + return kResultError; // We do what most OS implementations of max-count do. count = (mSemaphoreData.mnMaxCount - newValue); + + newValue = mSemaphoreData.mnCount.Add(count); + + bool bResult = mSemaphoreData.mCV.Signal(true); // Signal broadcast (the true arg) because semaphores could have multiple counts and multiple threads waiting for them. There's a potential "thundering herd" problem here. + EAT_ASSERT(bResult); + EA_UNUSED(bResult); + + result = mSemaphoreData.mMutex.Unlock(); // Important that we lock after the mCV.Signal. + EAT_ASSERT(result >= 0); + if(result < 0) + newValue = kResultError; // This Semaphore is now considered dead and unusable. + } + + return newValue; + } + + + int EA::Thread::Semaphore::GetCount() const + { + return mSemaphoreData.mnCount.GetValue(); + } + +#elif !EA_THREADS_AVAILABLE + + /////////////////////////////////////////////////////////////////////////////// + // non-threaded implementation + /////////////////////////////////////////////////////////////////////////////// + + EASemaphoreData::EASemaphoreData() + : mnCount(0), + mnMaxCount(INT_MAX) + { + // Empty + } + + + EA::Thread::SemaphoreParameters::SemaphoreParameters(int initialCount, bool bIntraProcess, const char* pName) + : mInitialCount(initialCount), + mMaxCount(INT_MAX), + mbIntraProcess(bIntraProcess) + { + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + } + + + EA::Thread::Semaphore::Semaphore(const SemaphoreParameters* pSemaphoreParameters, bool bDefaultParameters) + { + if(!pSemaphoreParameters && bDefaultParameters) + { + SemaphoreParameters parameters; + Init(¶meters); + } + else + Init(pSemaphoreParameters); + } + + + EA::Thread::Semaphore::Semaphore(int initialCount) + { + SemaphoreParameters parameters(initialCount); + Init(¶meters); + } + + + EA::Thread::Semaphore::~Semaphore() + { + } + + + bool EA::Thread::Semaphore::Init(const SemaphoreParameters* pSemaphoreParameters) + { + if(pSemaphoreParameters) + { + mSemaphoreData.mnCount = pSemaphoreParameters->mInitialCount; + mSemaphoreData.mnMaxCount = pSemaphoreParameters->mMaxCount; + return true; + } + + return false; + } + + + int EA::Thread::Semaphore::Wait(const ThreadTime& timeoutAbsolute) + { + if(timeoutAbsolute == kTimeoutNone) + { + while(mSemaphoreData.mnCount <= 0) + ThreadSleep(1); + + --mSemaphoreData.mnCount; + } + else if(timeoutAbsolute == 0) + { + if(mSemaphoreData.mnCount) + --mSemaphoreData.mnCount; + else + return kResultTimeout; + } + else + { + while((mSemaphoreData.mnCount <= 0) && (GetThreadTime() < timeoutAbsolute)) + ThreadSleep(1); + + if(mSemaphoreData.mnCount <= 0) + return kResultTimeout; + } + + return mSemaphoreData.mnCount; + } + + + int EA::Thread::Semaphore::Post(int count) + { + EAT_ASSERT(mSemaphoreData.mnCount >= 0); + + // Ideally, what we would do is account for the number of waiters in + // this overflow calculation. If max-count = 4, count = 6, waiters = 8, + // we would release 6 waiters and leave the semaphore at 2. + // The problem is that some of those 6 waiters might time out while we + // are doing this and leave ourselves with count greater than max-count. + if((mSemaphoreData.mnMaxCount - count) < mSemaphoreData.mnCount) // If count would cause an overflow... + return kResultError; // We do what most OS implementations of max-count do. // count = (mSemaphoreData.mnMaxCount - nLastCount); + + return (mSemaphoreData.mnCount += count); + } + + + int EA::Thread::Semaphore::GetCount() const + { + return mSemaphoreData.mnCount; + } + +#endif // !EA_THREADS_AVAILABLE + diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_storage.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_storage.cpp new file mode 100644 index 00000000..eb4d8940 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_storage.cpp @@ -0,0 +1,350 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +EA_DISABLE_VC_WARNING(4574) +#include +EA_RESTORE_VC_WARNING() + +#if defined(EA_PLATFORM_SONY) + #include + + EA::Thread::ThreadLocalStorage::ThreadLocalStorage() + : mTLSData() + { + // To consider: Support the specification of a destructor instead of just passing NULL. + mTLSData.mResult = scePthreadKeyCreate(&mTLSData.mKey, NULL); + EAT_ASSERT(mTLSData.mResult == 0); + } + + + EA::Thread::ThreadLocalStorage::~ThreadLocalStorage() + { + if(mTLSData.mResult == 0) + scePthreadKeyDelete(mTLSData.mKey); + } + + + void* EA::Thread::ThreadLocalStorage::GetValue() + { + return scePthreadGetspecific(mTLSData.mKey); + } + + + bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData) + { + if(scePthreadSetspecific(mTLSData.mKey, pData) == 0) + return true; + return false; + } + + + +#elif (defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE) && !defined(EA_PLATFORM_NX) + #if defined(EA_PLATFORM_UNIX) + #include + #elif defined(EA_PLATFORM_WINDOWS) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + #endif + + EA::Thread::ThreadLocalStorage::ThreadLocalStorage() + : mTLSData() + { + // To consider: Support the specification of a destructor instead of just passing NULL. + mTLSData.mResult = pthread_key_create(&mTLSData.mKey, NULL); + EAT_ASSERT(mTLSData.mResult == 0); + } + + + EA::Thread::ThreadLocalStorage::~ThreadLocalStorage() + { + if(mTLSData.mResult == 0) + pthread_key_delete(mTLSData.mKey); + } + + + void* EA::Thread::ThreadLocalStorage::GetValue() + { + return pthread_getspecific(mTLSData.mKey); + } + + + bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData) + { + if(pthread_setspecific(mTLSData.mKey, pData) == 0) + return true; + return false; + } + + + +#elif defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_WINDOWS_PHONE) && !(defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + + EA::Thread::ThreadLocalStorage::ThreadLocalStorage() + : mTLSData(TlsAlloc()) + { + EAT_ASSERT(mTLSData != TLS_OUT_OF_INDEXES); + } + + + EA::Thread::ThreadLocalStorage::~ThreadLocalStorage() + { + if(mTLSData != TLS_OUT_OF_INDEXES) + TlsFree(mTLSData); + } + + + void* EA::Thread::ThreadLocalStorage::GetValue() + { + return TlsGetValue(mTLSData); + } + + + bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData) + { + if(TlsSetValue(mTLSData, (void*)pData)) + return true; + return false; + } + +#elif (!EA_THREADS_AVAILABLE || defined(EA_PLATFORM_CONSOLE)) && !defined(EA_PLATFORM_NX) + + #include + + #if !EA_THREADS_AVAILABLE + #define OSEnableInterrupts() + #define OSDisableInterrupts() + #else + #error Need to define EnableInterrupts/DisableInterrupts for the given platform. + #endif + + + EAThreadLocalStorageData::ThreadToDataPair* EAThreadLocalStorageData::GetTLSEntry(bool bCreateIfNotFound) + { + const int kArraySize = (sizeof(mDataArray) / sizeof(mDataArray[0])); + ThreadToDataPair* pCurrent, *pEnd; + + EA::Thread::ThreadUniqueId nCurrentThreadID; + EAThreadGetUniqueId(nCurrentThreadID); + + // The code below is likely to execute very quickly and never transfers + // execution outside the function, so we can very briefly disable interrupts + // for the period needed to do the logic below. + OSDisableInterrupts(); + + // We make the assumption that there are likely to be less than 10 threads most of + // the time. Thus, instead of maintaining a sorted array and do a binary search + // within that array, we do a linear search. An improvement would be to make the + // array be sorted if it goes above some preset size, such as 20. + for(pCurrent = mDataArray, pEnd = mDataArray + mDataArrayCount; pCurrent < pEnd; ++pCurrent) + { + if(pCurrent->mThreadID == nCurrentThreadID) + { + OSEnableInterrupts(); + return pCurrent; + } + } + + if((pCurrent >= pEnd) && ((mDataArrayCount + 1) < kArraySize) && bCreateIfNotFound) // If we didn't find it above and there is more room and we should create if not found... + { + pCurrent = mDataArray + mDataArrayCount++; + pCurrent->mThreadID = nCurrentThreadID; + } + else + pCurrent = NULL; + + OSEnableInterrupts(); + + return pCurrent; + } + + + EA::Thread::ThreadLocalStorage::ThreadLocalStorage() + { + memset(mTLSData.mDataArray, 0, sizeof(mTLSData.mDataArray)); + mTLSData.mDataArrayCount = 0; + } + + + EA::Thread::ThreadLocalStorage::~ThreadLocalStorage() + { + // Nothing to do. + } + + + void* EA::Thread::ThreadLocalStorage::GetValue() + { + EAThreadLocalStorageData::ThreadToDataPair* const pTDP = mTLSData.GetTLSEntry(false); + if(pTDP) + return (void*)pTDP->mpData; + return NULL; + } + + + bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData) + { + if(pData == NULL) + { // We remove it from the container so that the container can have room for others. + EAThreadLocalStorageData::ThreadToDataPair* pTDP = mTLSData.GetTLSEntry(false); + + if(pTDP) + { + OSDisableInterrupts(); // Briefly disable interrupts for the duration of the logic below. + const EAThreadLocalStorageData::ThreadToDataPair* const pTDPEnd = mTLSData.mDataArray + mTLSData.mDataArrayCount; + while(++pTDP <= pTDPEnd) // What we do here is move all the other values downward. This is an O(n) operation, + pTDP[-1] = pTDP[0]; // but the number of unique threads usinug us is likely to be pretty small. + mTLSData.mDataArrayCount = (int)(pTDPEnd - mTLSData.mDataArray - 1); + OSEnableInterrupts(); + } + return true; + } + + EAThreadLocalStorageData::ThreadToDataPair* const pTDP = mTLSData.GetTLSEntry(true); + if(pTDP) + pTDP->mpData = pData; + return (pTDP != NULL); + } + +#else + + // Use reference std::map implementation. + EA_DISABLE_VC_WARNING(4574) + #include + EA_RESTORE_VC_WARNING() + + #include + + void** EAThreadLocalStorageData::GetTLSEntry(bool bCreateIfNotFound) + { + EA::Thread::ThreadUniqueId nThreadID; + EAThreadGetUniqueId(nThreadID); + + EA::Thread::AutoFutex autoFutex(mFutex); + + if(bCreateIfNotFound) // We expect this to be true most of the time. + { + // Create as needed + if (mThreadToDataMap == NULL) + { + mThreadToDataMap = new std::map; + } + + return (void**)(char*)&((*mThreadToDataMap)[nThreadID]); // Dereferencing a std::map value by index inserts the value if it is not present. + } + + if (mThreadToDataMap == NULL) + { + return NULL; + } + + std::map::iterator it(mThreadToDataMap->find(nThreadID)); + if(it != mThreadToDataMap->end()) + { + std::map::value_type& value = *it; + return (void**)(char*)&value.second; + } + return NULL; + } + + + EA::Thread::ThreadLocalStorage::ThreadLocalStorage() + { + } + + + EA::Thread::ThreadLocalStorage::~ThreadLocalStorage() + { + // Nothing to do. + } + + + void* EA::Thread::ThreadLocalStorage::GetValue() + { + void** const ppData = mTLSData.GetTLSEntry(false); + if(ppData) + return *ppData; + return NULL; + } + + + bool EA::Thread::ThreadLocalStorage::SetValue(const void* pData) + { + if(pData == NULL) + { + ThreadUniqueId nThreadID; + EAThreadGetUniqueId(nThreadID); + + EA::Thread::AutoFutex autoFutex(mTLSData.mFutex); + + if (mTLSData.mThreadToDataMap) + { + std::map::iterator it(mTLSData.mThreadToDataMap->find(nThreadID)); + if(it != mTLSData.mThreadToDataMap->end()) + mTLSData.mThreadToDataMap->erase(it); + } + return true; + } + + void** const ppData = mTLSData.GetTLSEntry(true); + if(ppData) + *ppData = (void*)pData; + return (*ppData != NULL); + } + +#endif + + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::ThreadLocalStorage* EA::Thread::ThreadLocalStorageFactory::CreateThreadLocalStorage() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::ThreadLocalStorage))) EA::Thread::ThreadLocalStorage; + else + return new EA::Thread::ThreadLocalStorage; +} + +void EA::Thread::ThreadLocalStorageFactory::DestroyThreadLocalStorage(EA::Thread::ThreadLocalStorage* pThreadLocalStorage) +{ + if(gpAllocator) + { + pThreadLocalStorage->~ThreadLocalStorage(); + gpAllocator->Free(pThreadLocalStorage); + } + else + delete pThreadLocalStorage; +} + +size_t EA::Thread::ThreadLocalStorageFactory::GetThreadLocalStorageSize() +{ + return sizeof(EA::Thread::ThreadLocalStorage); +} + +EA::Thread::ThreadLocalStorage* EA::Thread::ThreadLocalStorageFactory::ConstructThreadLocalStorage(void* pMemory) +{ + return new(pMemory) EA::Thread::ThreadLocalStorage; +} + +void EA::Thread::ThreadLocalStorageFactory::DestructThreadLocalStorage(EA::Thread::ThreadLocalStorage* pThreadLocalStorage) +{ + pThreadLocalStorage->~ThreadLocalStorage(); +} + + +#undef OSEnableInterrupts +#undef OSDisableInterrupts diff --git a/r5dev/thirdparty/ea/EAThread/source/eathread_thread.cpp b/r5dev/thirdparty/ea/EAThread/source/eathread_thread.cpp new file mode 100644 index 00000000..b0dc2454 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/eathread_thread.cpp @@ -0,0 +1,262 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "eathread/internal/eathread_global.h" +#include +#include +#include // include new for placement new operator + +#if !EA_THREADS_AVAILABLE + // Do nothing +#elif EA_USE_CPP11_CONCURRENCY + #include "cpp11/eathread_thread_cpp11.cpp" +#elif defined(EA_PLATFORM_SONY) + #include "sony/eathread_thread_sony.cpp" +#elif defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include "unix/eathread_thread_unix.cpp" +#elif defined(EA_PLATFORM_MICROSOFT) + #include "pc/eathread_thread_pc.cpp" +#endif + + + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + } +} + + +EA::Thread::Thread* EA::Thread::ThreadFactory::CreateThread() +{ + if(gpAllocator) + return new(gpAllocator->Alloc(sizeof(EA::Thread::Thread))) EA::Thread::Thread; + else + return new EA::Thread::Thread; +} + +void EA::Thread::ThreadFactory::DestroyThread(EA::Thread::Thread* pThread) +{ + if(gpAllocator) + { + pThread->~Thread(); + gpAllocator->Free(pThread); + } + else + delete pThread; +} + +size_t EA::Thread::ThreadFactory::GetThreadSize() +{ + return sizeof(EA::Thread::Thread); +} + +EA::Thread::Thread* EA::Thread::ThreadFactory::ConstructThread(void* pMemory) +{ + return new(pMemory) EA::Thread::Thread; +} + +void EA::Thread::ThreadFactory::DestructThread(EA::Thread::Thread* pThread) +{ + pThread->~Thread(); +} + +EA::Thread::ThreadEnumData::ThreadEnumData() +: mpThreadDynamicData(NULL) +{ +} + +EA::Thread::ThreadEnumData::~ThreadEnumData() +{ + Release(); +} + +void EA::Thread::ThreadEnumData::Release() +{ + if(mpThreadDynamicData) + { + mpThreadDynamicData->Release(); + mpThreadDynamicData = NULL; + } +} + +extern const size_t kMaxThreadDynamicDataCount; +EATHREAD_GLOBALVARS_EXTERN_INSTANCE; +/////////////////////////////////////////////////////////////////////////////// +// +size_t EA::Thread::EnumerateThreads(ThreadEnumData* pDataArray, size_t dataArrayCapacity) +{ + size_t requiredCount = 0; + + if(dataArrayCapacity > EA::Thread::kMaxThreadDynamicDataCount) + dataArrayCapacity = EA::Thread::kMaxThreadDynamicDataCount; + + EATHREAD_GLOBALVARS.gThreadDynamicMutex.Lock(); + for(size_t i(0); i < EA::Thread::kMaxThreadDynamicDataCount; i++) + { + if(EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[i].GetValue() != 0) + { + if(requiredCount < dataArrayCapacity) + { + pDataArray[requiredCount].mpThreadDynamicData = (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + pDataArray[requiredCount].mpThreadDynamicData->AddRef(); + } + requiredCount++; + } + } + EATHREAD_GLOBALVARS.gThreadDynamicMutex.Unlock(); + + return requiredCount; +} + +/////////////////////////////////////////////////////////////////////////////// +// non-threaded implementation +/////////////////////////////////////////////////////////////////////////////// + +#if !EA_THREADS_AVAILABLE + + // If mulitithreading support is not available, we can't implement anything + // here that works. All we do is define a null implementation that links + // but fails all operations. + + + EA::Thread::ThreadParameters::ThreadParameters() + : mpStack(NULL), + mnStackSize(0), + mnPriority(kThreadPriorityDefault), + mnProcessor(kProcessorDefault), + mpName(""), + mbDisablePriorityBoost(false) + { + } + + + EA::Thread::Thread::Thread() + { + mThreadData.mpData = NULL; + } + + + EA::Thread::Thread::Thread(const Thread& /*t*/) + { + } + + + EA::Thread::Thread& EA::Thread::Thread::operator=(const Thread& /*t*/) + { + return *this; + } + + + EA::Thread::Thread::~Thread() + { + } + + + EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::sGlobalRunnableFunctionUserWrapper = NULL; + EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::sGlobalRunnableClassUserWrapper = NULL; + EA::Thread::AtomicInt32 EA::Thread::Thread::sDefaultProcessor = kProcessorAny; + EA::Thread::AtomicUint64 EA::Thread::Thread::sDefaultProcessorMask = UINT64_C(0xffffffffffffffff); + + + EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::GetGlobalRunnableFunctionUserWrapper() + { + return sGlobalRunnableFunctionUserWrapper; + } + + void EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(EA::Thread::RunnableFunctionUserWrapper pUserWrapper) + { + if (sGlobalRunnableFunctionUserWrapper != NULL) + { + // Can only be set once in entire game. + EAT_ASSERT(false); + } + else + sGlobalRunnableFunctionUserWrapper = pUserWrapper; + } + + EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::GetGlobalRunnableClassUserWrapper() + { + return sGlobalRunnableClassUserWrapper; + } + + void EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(EA::Thread::RunnableClassUserWrapper pUserWrapper) + { + if (sGlobalRunnableClassUserWrapper != NULL) + { + // Can only be set once in entire game. + EAT_ASSERT(false); + } + else + sGlobalRunnableClassUserWrapper = pUserWrapper; + } + + + EA::Thread::ThreadId EA::Thread::Thread::Begin(RunnableFunction /*pFunction*/, void* /*pContext*/, const ThreadParameters* /*pTP*/, RunnableFunctionUserWrapper /*pUserWrapper*/) + { + return kThreadIdInvalid; + } + + + EA::Thread::ThreadId EA::Thread::Thread::Begin(IRunnable* /*pRunnable*/, void* /*pContext*/, const ThreadParameters* /*pTP*/, RunnableClassUserWrapper /*pUserWrapper*/) + { + return kThreadIdInvalid; + } + + + EA::Thread::Thread::Status EA::Thread::Thread::WaitForEnd(const ThreadTime& /*timeoutAbsolute*/, intptr_t* /*pThreadReturnValue*/) + { + return kStatusNone; + } + + + EA::Thread::Thread::Status EA::Thread::Thread::GetStatus(intptr_t* /*pThreadReturnValue*/) const + { + return kStatusNone; + } + + + EA::Thread::ThreadId EA::Thread::Thread::GetId() const + { + return (ThreadId)kThreadIdInvalid; + } + + + int EA::Thread::Thread::GetPriority() const + { + return kThreadPriorityUnknown; + } + + + bool EA::Thread::Thread::SetPriority(int /*nPriority*/) + { + return false; + } + + + void EA::Thread::Thread::SetProcessor(int /*nProcessor*/) + { + } + + + void EA::Thread::Thread::Wake() + { + } + + + const char* EA::Thread::Thread::GetName() const + { + return ""; + } + + + void EA::Thread::Thread::SetName(const char* /*pName*/) + { + } + +#endif // !EA_THREADS_AVAILABLE + diff --git a/r5dev/thirdparty/ea/EAThread/source/libunwind/eathread_callstack_libunwind.cpp b/r5dev/thirdparty/ea/EAThread/source/libunwind/eathread_callstack_libunwind.cpp new file mode 100644 index 00000000..0ec763d5 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/libunwind/eathread_callstack_libunwind.cpp @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace EA +{ +namespace Thread +{ + +EA::Thread::ThreadLocalStorage sStackBase; + +/////////////////////////////////////////////////////////////////////////////// +// SetStackBase +// +EATHREADLIB_API void SetStackBase(void* pStackBase) +{ + if(pStackBase) + sStackBase.SetValue(pStackBase); + else + { + pStackBase = __builtin_frame_address(0); + + if(pStackBase) + SetStackBase(pStackBase); + // Else failure; do nothing. + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackBase +// +EATHREADLIB_API void* GetStackBase() +{ + void* pBase; + + if(GetPthreadStackInfo(&pBase, NULL)) + return pBase; + + // Else we require the user to have set this previously, usually via a call + // to SetStackBase() in the start function of this currently executing + // thread (or main for the main thread). + pBase = sStackBase.GetValue(); + + if(pBase == NULL) + pBase = (void*)(((uintptr_t)&pBase + 4095) & ~4095); // Make a guess, round up to next 4096. + + return pBase; +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackLimit +// +EATHREADLIB_API void* GetStackLimit() +{ + void* pLimit; + + if(GetPthreadStackInfo(NULL, &pLimit)) + return pLimit; + + pLimit = __builtin_frame_address(0); + + return (void*)((uintptr_t)pLimit & ~4095); // Round down to nearest page, as the stack grows downward. +} + + + +} // namespace Thread +} // namespace EA + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/null/eathread_callstack_null.cpp b/r5dev/thirdparty/ea/EAThread/source/null/eathread_callstack_null.cpp new file mode 100644 index 00000000..dcae4496 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/null/eathread_callstack_null.cpp @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + + +EA_DISABLE_VC_WARNING(4172) // returning address of local variable or temporary + + +namespace EA +{ +namespace Thread +{ + + +#if EA_THREADS_AVAILABLE + static EA::Thread::ThreadLocalStorage sStackBase; +#else + static void* sStackBase; +#endif + +/////////////////////////////////////////////////////////////////////////////// +// SetStackBase +// +EATHREADLIB_API void SetStackBase(void* pStackBase) +{ + if(pStackBase) + { + #if EA_THREADS_AVAILABLE + sStackBase.SetValue(pStackBase); + #else + sStackBase = pStackBase; + #endif + } + else + { + pStackBase = GetStackBase(); + SetStackBase(pStackBase); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackBase +// +EATHREADLIB_API void* GetStackBase() +{ + #if EA_THREADS_AVAILABLE + void* pStackBase = sStackBase.GetValue(); + #else + void* pStackBase = sStackBase; + #endif + + if(!pStackBase) + pStackBase = (void*)(((uintptr_t)GetStackLimit() + 4095) & ~4095); // Align up to nearest page, as the stack grows downward. + + return pStackBase; +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackLimit +// +EATHREADLIB_API void* GetStackLimit() +{ + void* pStack = NULL; + + pStack = &pStack; + + return (void*)((uintptr_t)pStack & ~4095); // Round down to nearest page, as the stack grows downward. +} + +} // namespace Thread +} // namespace EA + +EA_RESTORE_VC_WARNING() + diff --git a/r5dev/thirdparty/ea/EAThread/source/pc/eathread_callstack_win32.cpp b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_callstack_win32.cpp new file mode 100644 index 00000000..62122598 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_callstack_win32.cpp @@ -0,0 +1,143 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#pragma warning(push, 0) +#include +#pragma warning(pop) + +namespace EA +{ +namespace Thread +{ + +/////////////////////////////////////////////////////////////////////////////// +// GetThreadIdFromThreadHandle +// +// +EATHREADLIB_API uint32_t GetThreadIdFromThreadHandle(intptr_t threadId) +{ + struct THREAD_BASIC_INFORMATION_WIN32 + { + BOOL ExitStatus; + PVOID TebBaseAddress; + DWORD UniqueProcessId; + DWORD UniqueThreadId; + DWORD AffinityMask; + DWORD Priority; + DWORD BasePriority; + }; + + static HMODULE hKernel32 = NULL; + if (!hKernel32) + hKernel32 = LoadLibraryA("kernel32.dll"); + + if (hKernel32) + { + typedef DWORD (WINAPI *GetThreadIdFunc)(HANDLE); + + static GetThreadIdFunc pGetThreadIdFunc = NULL; + if (!pGetThreadIdFunc) + pGetThreadIdFunc = (GetThreadIdFunc)(uintptr_t)GetProcAddress(hKernel32, "GetThreadId"); + + if (pGetThreadIdFunc) + return pGetThreadIdFunc((HANDLE)threadId); + } + + + static HMODULE hNTDLL = NULL; + if (!hNTDLL) + hNTDLL = LoadLibraryA("ntdll.dll"); + + if (hNTDLL) + { + typedef LONG (WINAPI *NtQueryInformationThreadFunc)(HANDLE, int, PVOID, ULONG, PULONG); + + static NtQueryInformationThreadFunc pNtQueryInformationThread = NULL; + if (!pNtQueryInformationThread) + pNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread"); + + if (pNtQueryInformationThread) + { + THREAD_BASIC_INFORMATION_WIN32 tbi; + + if(pNtQueryInformationThread((HANDLE)threadId, 0, &tbi, sizeof(tbi), NULL) == 0) + return tbi.UniqueThreadId; + } + } + + return 0; +} + +namespace Internal +{ + +struct TIBStackInfo +{ + uintptr_t StackBase; + uintptr_t StackLimit; +}; + +static TIBStackInfo GetStackInfo() +{ + NT_TIB* pTib; + + /** + * Offset 0x18 from the FS segment register gives a pointer to + * the thread information block for the current thread + * https://en.wikipedia.org/wiki/Win32_Thread_Information_Block + */ + __asm { + mov eax, fs:[18h] + mov pTib, eax + } + + return { ((uintptr_t)pTib->StackBase), ((uintptr_t)pTib->StackLimit) }; +} + +} // namespace Internal + +/////////////////////////////////////////////////////////////////////////////// +// SetStackBase +// +EATHREADLIB_API void SetStackBase(void* /*pStackBase*/) +{ + // Nothing to do, as GetStackBase always works on its own. +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackBase +// +EATHREADLIB_API void* GetStackBase() +{ + Internal::TIBStackInfo info = Internal::GetStackInfo(); + + return ((void*)info.StackBase); +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackLimit +// +EATHREADLIB_API void* GetStackLimit() +{ + Internal::TIBStackInfo info = Internal::GetStackInfo(); + + return ((void*)info.StackLimit); + + // Alternative which returns a slightly different answer: + // We return our stack pointer, which is a good approximation of the stack limit of the caller. + // void* pStack = NULL; + // __asm { mov pStack, ESP}; + // return pStack; +} + + +} // namespace Thread +} // namespace EA diff --git a/r5dev/thirdparty/ea/EAThread/source/pc/eathread_callstack_win64.cpp b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_callstack_win64.cpp new file mode 100644 index 00000000..327af371 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_callstack_win64.cpp @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#pragma warning(push, 0) +#include +#pragma warning(pop) + +namespace EA +{ +namespace Thread +{ + +/////////////////////////////////////////////////////////////////////////////// +// GetThreadIdFromThreadHandle +// +// This implementation is the same as the one in EAThread. +// Converts a thread HANDLE (threadId) to a thread id DWORD (sysThreadId). +// Recall that Windows has two independent thread identifier types. +// +EATHREADLIB_API uint32_t GetThreadIdFromThreadHandle(intptr_t threadId) +{ + // Win64 has this function natively, unlike earlier versions of 32 bit Windows. + return (uint32_t)::GetThreadId((HANDLE)threadId); +} + + +/////////////////////////////////////////////////////////////////////////////// +// SetStackBase +// +EATHREADLIB_API void SetStackBase(void* /*pStackBase*/) +{ + // Nothing to do, as GetStackBase always works on its own. +} + +/////////////////////////////////////////////////////////////////////////////// +// GetStackBase +// +EATHREADLIB_API void* GetStackBase() +{ + NT_TIB64* pTIB = (NT_TIB64*)NtCurrentTeb(); // NtCurrentTeb is defined in as an inline call to __readgsqword + return (void*)pTIB->StackBase; +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackLimit +// +EATHREADLIB_API void* GetStackLimit() +{ + NT_TIB64* pTIB = (NT_TIB64*)NtCurrentTeb(); // NtCurrentTeb is defined in as an inline call to __readgsqword + return (void*)pTIB->StackLimit; + + // The following is an alternative implementation that returns the extent + // of the current stack usage as opposed to the stack limit as seen by the OS. + // This value will be a higher address than Tib.StackLimit (recall that the + // stack grows downward). It's debatable which of these two approaches is + // better, as one returns the thread's -usable- stack space while the + // other returns how much the thread is -currently- using. The determination + // of the usable stack space is complicated by the fact that Microsoft + // platforms auto-extend the stack if the process pushes beyond the current limit. + // In the end the Tib.StackLimit solution is actually the most portable across + // Microsoft OSs and compilers for those OSs (Microsoft or not). + + // Alternative implementation: + // We return our stack pointer, which is a good approximation of the stack limit of the caller. + // void* rsp = GetRSP(); + // return rsp; +} + + +} // namespace Thread +} // namespace EA diff --git a/r5dev/thirdparty/ea/EAThread/source/pc/eathread_mutex_pc.cpp b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_mutex_pc.cpp new file mode 100644 index 00000000..23221a00 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_mutex_pc.cpp @@ -0,0 +1,214 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "EABase/eabase.h" +#include "eathread/eathread_mutex.h" +#include "eathread/eathread.h" + +#if defined(EA_PLATFORM_MICROSOFT) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif +#ifdef CreateMutex + #undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW. +#endif + + +#if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + #if defined(EA_PLATFORM_WINDOWS) + extern "C" WINBASEAPI BOOL WINAPI TryEnterCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection); + #endif + + EAMutexData::EAMutexData() + : mnLockCount(0), mbIntraProcess(true) + { + #if EAT_ASSERT_ENABLED + mThreadId = EA::Thread::kThreadIdInvalid; + mSysThreadId = EA::Thread::kSysThreadIdInvalid; + #endif + + ::memset(&mData, 0, sizeof(mData)); + } + + + EA::Thread::MutexParameters::MutexParameters(bool bIntraProcess, const char* pName) + : mbIntraProcess(bIntraProcess) + { + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + } + + + EA::Thread::Mutex::Mutex(const MutexParameters* pMutexParameters, bool bDefaultParameters) + { + if(!pMutexParameters && bDefaultParameters) + { + MutexParameters parameters; + Init(¶meters); + } + else + Init(pMutexParameters); + } + + + EA::Thread::Mutex::~Mutex() + { + EAT_ASSERT(mMutexData.mnLockCount == 0); + + // Consider doing something to verify the mutex object has been initialized. + #if defined(EA_PLATFORM_WINDOWS) + if(mMutexData.mbIntraProcess) + DeleteCriticalSection((CRITICAL_SECTION*)mMutexData.mData); + else + CloseHandle(*(HANDLE*)mMutexData.mData); + #else + DeleteCriticalSection((CRITICAL_SECTION*)mMutexData.mData); + #endif + } + + + bool EA::Thread::Mutex::Init(const MutexParameters* pMutexParameters) + { + // Make sure that internal structure is big enough to hold critical section data. + // If this assert fires, please adjust MUTEX_PLATFORM_DATA_SIZE in eathread_mutex.h accordingly. + EAT_COMPILETIME_ASSERT(sizeof(CRITICAL_SECTION) <= (MUTEX_PLATFORM_DATA_SIZE / sizeof(uint64_t) * sizeof(uint64_t))); + EAT_COMPILETIME_ASSERT(sizeof(HANDLE) <= MUTEX_PLATFORM_DATA_SIZE); + + if(pMutexParameters) + { + mMutexData.mnLockCount = 0; + + #if defined(EA_PLATFORM_WINDOWS) + mMutexData.mbIntraProcess = pMutexParameters->mbIntraProcess; + + if(mMutexData.mbIntraProcess) + { + // We use InitializeCriticalSectionAndSpinCount, as that has resulted in improved performance in practice on multiprocessors systems. + int rv = InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION*)mMutexData.mData, 256); + EAT_ASSERT(rv != 0); + EA_UNUSED(rv); + + return true; + } + else + { + EAT_COMPILETIME_ASSERT(sizeof(pMutexParameters->mName) <= MAX_PATH); + *(HANDLE*)mMutexData.mData = ::CreateMutexA(NULL, false, pMutexParameters->mName[0] ? pMutexParameters->mName : NULL); + EAT_ASSERT(*(HANDLE*)mMutexData.mData != 0); + return *(HANDLE*)mMutexData.mData != 0; + } + #else + // We use InitializeCriticalSectionAndSpinCount, as that has resulted in improved performance in practice on multiprocessors systems. + InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION*)mMutexData.mData, 256); + return true; + #endif + } + return false; + } + + + + EA_DISABLE_VC_WARNING(4706) // disable warning about assignment within a conditional expression + + int EA::Thread::Mutex::Lock(const ThreadTime& timeoutAbsolute) + { + EAT_ASSERT(mMutexData.mnLockCount < 100000); + + #if defined(EA_PLATFORM_WINDOWS) // Non-Windows is always assumed to be intra-process. + if(mMutexData.mbIntraProcess) + { + #endif + if(timeoutAbsolute == kTimeoutNone) + EnterCriticalSection((CRITICAL_SECTION*)mMutexData.mData); + else + { + // To consider: Have a pathway for kTimeoutImmediate which doesn't check the current time. + while(!TryEnterCriticalSection((CRITICAL_SECTION*)mMutexData.mData)) + { + if(GetThreadTime() >= timeoutAbsolute) + return kResultTimeout; + Sleep(1); + } + } + #if defined(EA_PLATFORM_WINDOWS) + } + else + { + EAT_ASSERT(*(HANDLE*)mMutexData.mData != 0); + + const DWORD dw = ::WaitForSingleObject(*(HANDLE*)mMutexData.mData, RelativeTimeoutFromAbsoluteTimeout(timeoutAbsolute)); + + if(dw == WAIT_TIMEOUT) + return kResultTimeout; + + if(dw != WAIT_OBJECT_0) + { + EAT_ASSERT(false); + return kResultError; + } + } + #endif + + EAT_ASSERT((mMutexData.mSysThreadId = EA::Thread::GetSysThreadId()) != kSysThreadIdInvalid); + EAT_ASSERT(mMutexData.mnLockCount >= 0); + return ++mMutexData.mnLockCount; // This is safe to do because we have the lock. + } + + EA_RESTORE_VC_WARNING() + + + + int EA::Thread::Mutex::Unlock() + { + EAT_ASSERT(mMutexData.mSysThreadId == EA::Thread::GetSysThreadId()); + EAT_ASSERT(mMutexData.mnLockCount > 0); + + const int nReturnValue(--mMutexData.mnLockCount); // This is safe to do because we have the lock. + + #if defined(EA_PLATFORM_WINDOWS) + if(mMutexData.mbIntraProcess) + LeaveCriticalSection((CRITICAL_SECTION*)mMutexData.mData); + else + { + EAT_ASSERT(*(HANDLE*)mMutexData.mData != 0); + ReleaseMutex(*(HANDLE*)mMutexData.mData); + } + #else + LeaveCriticalSection((CRITICAL_SECTION*)mMutexData.mData); + #endif + + return nReturnValue; + } + + + int EA::Thread::Mutex::GetLockCount() const + { + return mMutexData.mnLockCount; + } + + + bool EA::Thread::Mutex::HasLock() const + { + #if EAT_ASSERT_ENABLED + return (mMutexData.mnLockCount > 0) && (mMutexData.mSysThreadId == EA::Thread::GetSysThreadId()); + #else + return (mMutexData.mnLockCount > 0); // This is the best we can do, though it is of limited use, since it doesn't tell you if you are the thread with the lock. + #endif + } + + +#endif // EA_PLATFORM_XXX + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/pc/eathread_pc.cpp b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_pc.cpp new file mode 100644 index 00000000..220a1a3d --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_pc.cpp @@ -0,0 +1,919 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "eathread/eathread.h" +#include "eathread/eathread_thread.h" +#include "eathread/eathread_storage.h" + +#if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + #include + + EA_DISABLE_ALL_VC_WARNINGS() + #include + #include // for mbstowcs + #include + EA_RESTORE_ALL_VC_WARNINGS() + + #include "eathread/eathread_futex.h" + + extern "C" WINBASEAPI DWORD WINAPI SetThreadIdealProcessor(_In_ HANDLE hThread, _In_ DWORD dwIdealProcessor); + #if defined(EA_PLATFORM_WIN64) + extern "C" WINBASEAPI DWORD WINAPI GetThreadId(_In_ HANDLE hThread); + extern "C" WINBASEAPI ULONGLONG GetTickCount64(VOID); // Will not run on pre-Vista OS so 64 bit XP not supported + #endif + + // We set this module to initialize early. We want to do this because it + // allows statically initialized objects to call these functions safely. + EA_DISABLE_VC_WARNING(4074) // warning C4074: initializers put in compiler reserved initialization area + #pragma init_seg(compiler) + EA_RESTORE_VC_WARNING() + #ifndef EATHREAD_INIT_SEG_DEFINED + #define EATHREAD_INIT_SEG_DEFINED + #endif + + namespace EA + { + namespace Thread + { + // Note by Paul Pedriana: + // There is a bit of code here which implements "dynamic thread array maintenance". + // The reason for this is that we are trying to present to the user a consistently + // behaving GetThreadId function. The Windows threading API has a number of design + // characteristics that make it less than ideal for applications. One of these + // designs is that an application cannot ask the system what its thread id is and + // get a consistent answer; in fact you always get a different answer. + + // To consider: Use the VC++ undocumented __tlregdtor function to detect thread exits. + // __tlregdtor is a VC++ CRT function which detects the exiting of any threads created + // with the CRT beginthread family of functions. It cannot detect the exit of any threads + // that are begun via direct OS thread creation functions, nor can it detect the exit of + // threads that are exited by direct OS thread exit functions. This is may not be a major + // problem, because C/C++ programs should virtually always be calling the CRT thread begin + // and end functions so that the CRT can be maintained properly for the thread. + // + // typedef void (*_PVFV)(); + // void __tlregdtor(_PVFV func); + // void ThreadExit(){ Do something. May need to be careful about what APIs are called. } + + // Assertion variables. + EA::Thread::AssertionFailureFunction gpAssertionFailureFunction = NULL; + void* gpAssertionFailureContext = NULL; + + // Dynamic thread array maintenance. + // If the user calls GetThreadId from a thread that was created by some third + // party, then we don't have a thread handle for it. The only current way to get + // such a thread handle is to call OpenThread(GetCurrentThreadId()) or + // DuplicateHandle(GetCurrentThread()). In either case the return value is a + // handle which must be disposed of via CloseHandle. Additionally, since the + // thread was created by a thrid party, it's entirely possible that the thread + // will be exited without us ever finding about it. But we still need to call + // CloseHandle on the handle. So we maintain an array of handles and check their + // status periodically and upon process exit. + + const size_t kMaxThreadDynamicArrayCount = 128; + + struct DynamicThreadArray + { + static HANDLE mhDynamicThreadArray[kMaxThreadDynamicArrayCount]; + static CRITICAL_SECTION mCriticalSection; + static bool mbDynamicThreadArrayInitialized; + + static void Initialize(); + static void CheckDynamicThreadArray(bool bCloseAll); + static void AddDynamicThreadHandle(HANDLE hThread, bool bAdd); + }; + + HANDLE DynamicThreadArray::mhDynamicThreadArray[kMaxThreadDynamicArrayCount]; + CRITICAL_SECTION DynamicThreadArray::mCriticalSection; + bool DynamicThreadArray::mbDynamicThreadArrayInitialized; + + // DynamicThreadArray ctor/dtor were removed to because memory tracking systems that are required to run + // pre-main and post-main. In order to support memory tracking of allocations that occur post-main we + // intentially "leak" a operating system critical section and leave it to be cleaned up by the operating + // system at process shutdown. + // + // DynamicThreadArray::DynamicThreadArray() + // { + // Initialize(); + // } + + // DynamicThreadArray::~DynamicThreadArray() + // { + // CheckDynamicThreadArray(true); + // DeleteCriticalSection(&mCriticalSection); + // } + + void DynamicThreadArray::Initialize() + { + static EA::Thread::Futex m; + + const bool done = mbDynamicThreadArrayInitialized; + + // ensure that if we've seen previous writes to mbDynamicThreadArrayInitialized, we also + // see the writes to mCriticalSection, to avoid the case where another thread sees the flag + // before it sees the initialization + EAReadBarrier(); + + if(!done) + { + EA::Thread::AutoFutex _(m); + + if (!mbDynamicThreadArrayInitialized) + { + memset(mhDynamicThreadArray, 0, sizeof(mhDynamicThreadArray)); + InitializeCriticalSection(&mCriticalSection); + + // ensure writes to mCriticalSection and mhDynamicThreadArray are visible before writes + // to mbDynamicThreadArrayInitialized, to avoid the case where another thread sees the + // flag before it sees the initialization + EAWriteBarrier(); + + mbDynamicThreadArrayInitialized = true; + } + } + } + + // This function looks at the existing set of thread ids and see if any of them + // were quit. If so then this function removes their entry from our array of + // thread handles, and most importantly, calls CloseHandle on the thread handle. + void DynamicThreadArray::CheckDynamicThreadArray(bool bCloseAll) + { + Initialize(); + + EnterCriticalSection(&mCriticalSection); + + for(size_t i(0); i < sizeof(mhDynamicThreadArray)/sizeof(mhDynamicThreadArray[0]); i++) + { + if(mhDynamicThreadArray[i]) + { + DWORD dwExitCode(0); + + // Note that GetExitCodeThread is a hazard if the user of a thread exits + // with a return value that is equal to the value of STILL_ACTIVE (i.e. 259). + // We can document that users shouldn't do this, or we can change the code + // here to use WaitForSingleObject(hThread, 0) and assume the thread is + // still active if the return value is WAIT_TIMEOUT. + if(bCloseAll || !GetExitCodeThread(mhDynamicThreadArray[i], &dwExitCode) || (dwExitCode != STILL_ACTIVE)) // If the thread id is invalid or it has exited... + { + CloseHandle(mhDynamicThreadArray[i]); // This matches the DuplicateHandle call we made below. + mhDynamicThreadArray[i] = 0; + } + } + } + + LeaveCriticalSection(&mCriticalSection); + } + + void DynamicThreadArray::AddDynamicThreadHandle(HANDLE hThread, bool bAdd) + { + Initialize(); + + if(hThread) + { + EnterCriticalSection(&mCriticalSection); + + if(bAdd) + { + for(size_t i(0); i < sizeof(mhDynamicThreadArray)/sizeof(mhDynamicThreadArray[0]); i++) + { + if(mhDynamicThreadArray[i] == kThreadIdInvalid) + { + mhDynamicThreadArray[i] = hThread; + hThread = kThreadIdInvalid; // This tells us that we succeeded, and we'll use this result below. + break; + } + } + + EAT_ASSERT(hThread == kThreadIdInvalid); // Assert that there was enough room (that the above loop found a spot). + if(hThread != kThreadIdInvalid) // If not, then we need to free the handle. + CloseHandle(hThread); // This matches the DuplicateHandle call we made below. + } + else + { + for(size_t i(0); i < sizeof(mhDynamicThreadArray)/sizeof(mhDynamicThreadArray[0]); i++) + { + if(mhDynamicThreadArray[i] == hThread) + { + CloseHandle(hThread); // This matches the DuplicateHandle call we made below. + mhDynamicThreadArray[i] = kThreadIdInvalid; + break; + } + } + // By design, we don't consider a non-found handle an error. It may simply be the + // case that the given handle was not a dynamnic thread handle. Due to the way + // Windows works, there's just no way for us to tell. + } + + LeaveCriticalSection(&mCriticalSection); + } + } + + // Thread handle local storage. + // We have this code here in order to cache the thread handles for + // threads, so that the user gets a consistent return value from the + // GetThreadId function for each unique thread. + + static DWORD dwThreadHandleTLS = TLS_OUT_OF_INDEXES; // We intentionally make this an independent variable so that it is initialized unilaterally on segment load. + + struct TLSAlloc + { + TLSAlloc() + { + if(dwThreadHandleTLS == TLS_OUT_OF_INDEXES) // It turns out that the user might have set this to a + dwThreadHandleTLS = TlsAlloc(); // value before this constructor has run. So we check. + } + + #if EATHREAD_TLSALLOC_DTOR_ENABLED + // Since this class is used only as a static variable, this destructor would + // only get called during module destruction: app quit or DLL unload. + // In the case of DLL unload, we may have a problem if the DLL was unloaded + // before threads created by it were destroyed. Whether the problem is significant + // depends on the application. In most cases it won't be significant. + // + // We want to call TlsFree because not doing so results in a memory leak and eventual + // exhaustion of TLS ids by the system. + ~TLSAlloc() + { + if(dwThreadHandleTLS != TLS_OUT_OF_INDEXES) + { + // We don't read the hThread stored at dwThreadHandleTLS and call CloseHandle + // on it, as the DynamicThreadArray destructor will deal with closing any + // thread handles this module knows about. + + TlsFree(dwThreadHandleTLS); + dwThreadHandleTLS = TLS_OUT_OF_INDEXES; + } + } + #endif + }; + static TLSAlloc sTLSAlloc; + + void SetCurrentThreadHandle(HANDLE hThread, bool bDynamic) + { + // EAT_ASSERT(hThread != kThreadIdInvalid); We can't do this, as we can be intentionally called with an hThread of kThreadIdInvalid. + if(dwThreadHandleTLS == TLS_OUT_OF_INDEXES) // This should generally always evaluate to true because we init dwThreadHandleTLS on startup. + dwThreadHandleTLS = TlsAlloc(); + EAT_ASSERT(dwThreadHandleTLS != TLS_OUT_OF_INDEXES); + if(dwThreadHandleTLS != TLS_OUT_OF_INDEXES) + { + DynamicThreadArray::CheckDynamicThreadArray(false); + if(bDynamic) + { + if(hThread != kThreadIdInvalid) // If adding the hThread... + DynamicThreadArray::AddDynamicThreadHandle(hThread, true); + else // Else removing the existing current thread handle... + { + HANDLE hThreadOld = TlsGetValue(dwThreadHandleTLS); + if(hThreadOld != kThreadIdInvalid) // This should always evaluate to true in practice. + DynamicThreadArray::AddDynamicThreadHandle(hThreadOld, false); // Will Close the dynamic thread handle if it is one. + } + } + TlsSetValue(dwThreadHandleTLS, hThread); + } + } + } // namespace Thread + } // namespace EA + + +EATHREADLIB_API EA::Thread::ThreadId EA::Thread::GetThreadId() +{ + // We have some non-trivial code here because Windows doesn't provide a means for a + // thread to read its own thread id (thread handle) in a consistent way. + + // If we have allocated thread-local storage for this module... + if(dwThreadHandleTLS != TLS_OUT_OF_INDEXES) + { + void* const pValue = TlsGetValue(dwThreadHandleTLS); + + if(pValue) // If the current thread's ThreadId has been previously saved... + return pValue; // Under Win32, type ThreadId should be the same as HANDLE which should be the same as void*. + + // Else fall through and get the current thread handle and cache it so that next time the above code will succeed. + } + + // In this case the thread was not created by EAThread. So we give + // the thread a new Id, based on GetCurrentThread and DuplicateHandle. + // GetCurrentThread returns a "pseudo handle" which isn't actually the + // thread handle but is a hard-coded constant which means "current thread". + // If you want to get a real thread handle then you need to call DuplicateHandle + // on the pseudo handle. Every time you call DuplicateHandle you get a different + // result, yet we want this GetThreadId function to return a consistent value + // to the user, as that's what a rational user would expect. So after calling + // DuplicateHandle we save the value for the next time the user calls this + // function. We save the value in thread-local storage, so each unique thread + // sees a unique view of GetThreadId. + HANDLE hThread, hThreadPseudo = GetCurrentThread(); + BOOL bResult = DuplicateHandle(GetCurrentProcess(), hThreadPseudo, GetCurrentProcess(), &hThread, 0, true, DUPLICATE_SAME_ACCESS); + EAT_ASSERT(bResult && (hThread != kThreadIdInvalid)); + if(bResult) + EA::Thread::SetCurrentThreadHandle(hThread, true); // Need to eventually call CloseHandle on hThread, so we store it. + + return hThread; +} + +EATHREADLIB_API EA::Thread::ThreadId EA::Thread::GetThreadId(EA::Thread::SysThreadId id) +{ + EAThreadDynamicData* const pTDD = EA::Thread::FindThreadDynamicData(id); + if(pTDD) + { + return pTDD->mhThread; + } + + return EA::Thread::kThreadIdInvalid; +} + +EATHREADLIB_API EA::Thread::SysThreadId EA::Thread::GetSysThreadId(ThreadId id) +{ + #if defined(EA_PLATFORM_MICROSOFT) && (defined(EA_PROCESSOR_X86_64) || defined(EA_PROCESSOR_ARM64)) + // Win64 has this function natively. + return ::GetThreadId(id); + + // Fast implementation of this, which has been verified: + // uintptr_t pTIB = __readgsqword(0x30); + // uint32_t threadId = *((uint32_t*)(((uint8_t*)pTIB) + 0x48)); + // return (EA::Thread::SysThreadId)threadId; + + #elif defined(EA_PLATFORM_WIN32) + + // What we do here is first try to use the GetThreadId function, which is + // available on some later versions of WinXP and later OSs. If that doesn't + // work then we are using an earlier OS and we use the NtQueryInformationThread + // kernel function to read thread info. + + typedef DWORD (WINAPI *GetThreadIdFunc)(HANDLE); + typedef BOOL (WINAPI *NtQueryInformationThreadFunc)(HANDLE, int, PVOID, ULONG, PULONG); + + // We implement our own manual version of static variables here. We do this because + // the static variable mechanism the compiler provides wouldn't provide thread + // safety for us. + static volatile bool sInitialized = false; + static GetThreadIdFunc spGetThreadIdFunc = NULL; + static NtQueryInformationThreadFunc spNtQueryInformationThread = NULL; + + if(!sInitialized) + { + HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); + if(hKernel32) + spGetThreadIdFunc = (GetThreadIdFunc)(uintptr_t)GetProcAddress(hKernel32, "GetThreadId"); + + if(!spGetThreadIdFunc) + { + HMODULE hNTDLL = GetModuleHandleA("ntdll.dll"); + + if(hNTDLL) + spNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread"); + } + + sInitialized = true; + } + + if(spGetThreadIdFunc) + return (SysThreadId)spGetThreadIdFunc(id); + + if(spNtQueryInformationThread) + { + struct THREAD_BASIC_INFORMATION_WIN32 + { + BOOL ExitStatus; + PVOID TebBaseAddress; + DWORD UniqueProcessId; + DWORD UniqueThreadId; + DWORD AffinityMask; + DWORD Priority; + DWORD BasePriority; + }; + + THREAD_BASIC_INFORMATION_WIN32 tbi; + + if(spNtQueryInformationThread(id, 0, &tbi, sizeof(tbi), NULL) == 0) + return (SysThreadId)tbi.UniqueThreadId; + } + + return kSysThreadIdInvalid; + + #endif +} + + +EATHREADLIB_API EA::Thread::SysThreadId EA::Thread::GetSysThreadId() +{ + return ::GetCurrentThreadId(); +} + + +EATHREADLIB_API int EA::Thread::GetThreadPriority() +{ + const int nPriority = ::GetThreadPriority(GetCurrentThread()); + return kThreadPriorityDefault + (nPriority - THREAD_PRIORITY_NORMAL); +} + + +EATHREADLIB_API bool EA::Thread::SetThreadPriority(int nPriority) +{ + EAT_ASSERT(nPriority != kThreadPriorityUnknown); + int nNewPriority = THREAD_PRIORITY_NORMAL + (nPriority - kThreadPriorityDefault); + bool result = ::SetThreadPriority(GetCurrentThread(), nNewPriority) != 0; + + // Windows process running in NORMAL_PRIORITY_CLASS is picky about the priority passed in. + // So we need to set the priority to the next priority supported + #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + HANDLE thread = GetCurrentThread(); + + while(!result) + { + if (nNewPriority >= THREAD_PRIORITY_TIME_CRITICAL) + return ::SetThreadPriority(thread, THREAD_PRIORITY_TIME_CRITICAL) != 0; + + if (nNewPriority <= THREAD_PRIORITY_IDLE) + return ::SetThreadPriority(thread, THREAD_PRIORITY_IDLE) != 0; + + result = ::SetThreadPriority(thread, nNewPriority) != 0; + nNewPriority++; + } + #endif + + return result; +} + + +EATHREADLIB_API void EA::Thread::SetThreadProcessor(int nProcessor) +{ + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + + DWORD mask = 0xFF; //Default to all + if (nProcessor >= 0) + mask = (DWORD)(1 << nProcessor); + + SetThreadAffinityMask(GetCurrentThread(), mask); + + #else + static const int nProcessorCount = GetProcessorCount(); + + if(nProcessor < 0) + nProcessor = MAXIMUM_PROCESSORS; // This cases the SetThreadIdealProcessor to reset to 'no ideal processor'. + else + { + if(nProcessor >= nProcessorCount) + nProcessor %= nProcessorCount; + } + + // SetThreadIdealProcessor differs from SetThreadAffinityMask in that SetThreadIdealProcessor is not + // a strict assignment, and it allows the OS to move the thread if the ideal processor is busy. + // SetThreadAffinityMask is a more rigid assignment, but it can result in slower performance and + // possibly hangs due to processor contention between threads. For Windows we use SetIdealThreadProcessor + // in the name of safety and likely better overall performance. + SetThreadIdealProcessor(GetCurrentThread(), (DWORD)nProcessor); + + #endif +} + + +void* EA::Thread::GetThreadStackBase() +{ + #if defined(EA_PLATFORM_WIN32) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC) + // Offset 0x18 from the FS segment register gives a pointer to + // the thread information block for the current thread + // VC++ also offers the __readfsdword() intrinsic, which would be better to use here. + NT_TIB* pTib; + + __asm { + mov eax, fs:[18h] + mov pTib, eax + } + + return (void*)pTib->StackBase; + + #elif defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_X86_64) && defined(EA_COMPILER_MSVC) + // VC++ also offers the __readgsdword() intrinsic, which is an alternative which could + // retrieve the current thread TEB if the following proves unreliable. + PNT_TIB64 pTib = reinterpret_cast(NtCurrentTeb()); + + return (void*)pTib->StackBase; + + #elif defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_ARM64) && defined(EA_COMPILER_MSVC) + // TODO(rparolin): Requires an ARM64 implementation. + return nullptr; + + #elif defined(EA_PLATFORM_WIN32) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_GCC) + NT_TIB* pTib; + + asm ( "movl %%fs:0x18, %0\n" + : "=r" (pTib) + ); + + return (void*)pTib->StackBase; + #endif +} + + +#if defined(EA_PLATFORM_WIN32) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) + // People report on the Internet that this function can get you what CPU the current thread + // is running on. But that's false, as this function has been seen to return values greater than + // the number of physical or real CPUs present. For example, this function returns 6 for my + // Single CPU that's dual-hyperthreaded. + static int GetCurrentProcessorNumberCPUID() + { + _asm { mov eax, 1 } + _asm { cpuid } + _asm { shr ebx, 24 } + _asm { mov eax, ebx } + } + + int GetCurrentProcessorNumberXP() + { + int cpuNumber = GetCurrentProcessorNumberCPUID(); + int cpuCount = EA::Thread::GetProcessorCount(); + + return (cpuNumber % cpuCount); // I don't know if this is the right thing to do, but it's better than returning an impossible number and Windows XP is a fading OS as it is. + } + +#endif + + +EATHREADLIB_API int EA::Thread::GetThreadProcessor() +{ + #if defined(EA_PLATFORM_WIN32) + // Only Windows Vista and later provides GetCurrentProcessorNumber. + // So we must dynamically link to this function. + static EA_THREAD_LOCAL bool bInitialized = false; + static EA_THREAD_LOCAL DWORD (WINAPI *pfnGetCurrentProcessorNumber)() = NULL; + + if(!bInitialized) + { + HMODULE hKernel32 = GetModuleHandleA("KERNEL32.DLL"); + if(hKernel32) + pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)())(uintptr_t)GetProcAddress(hKernel32, "GetCurrentProcessorNumber"); + bInitialized = true; + } + + if(pfnGetCurrentProcessorNumber) + return (int)(unsigned)pfnGetCurrentProcessorNumber(); + + #if defined(EA_PLATFORM_WINDOWS) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC) && (EA_COMPILER_MSVC >= 1400) + return GetCurrentProcessorNumberXP(); + #else + return 0; + #endif + + #elif defined(EA_PLATFORM_WIN64) + static EA_THREAD_LOCAL bool bInitialized = false; + static EA_THREAD_LOCAL DWORD (WINAPI *pfnGetCurrentProcessorNumber)() = NULL; + + if(!bInitialized) + { + HMODULE hKernel32 = GetModuleHandleA("KERNEL32.DLL"); // Yes, we want to use Kernel32.dll. There is no Kernel64.dll on Win64. + if(hKernel32) + pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)())(uintptr_t)GetProcAddress(hKernel32, "GetCurrentProcessorNumber"); + bInitialized = true; + } + + if(pfnGetCurrentProcessorNumber) + return (int)(unsigned)pfnGetCurrentProcessorNumber(); + + return 0; + + #else + return (int)(unsigned)GetCurrentProcessorNumber(); + + #endif +} + + +EATHREADLIB_API void EA::Thread::SetThreadAffinityMask(const EA::Thread::ThreadId& id, ThreadAffinityMask nAffinityMask) +{ + // Update the affinity mask in the thread dynamic data cache. + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + pTDD->mnThreadAffinityMask = nAffinityMask; + } + +#if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + DWORD_PTR nProcessorCountMask = 0x7F; // default to all 7 available cores. + #else + DWORD_PTR nProcessorCountMask = (DWORD_PTR)1 << GetProcessorCount(); + #endif + + // Call the Windows library function. + DWORD_PTR nProcessAffinityMask, nSystemAffinityMask; + if(EA_LIKELY(GetProcessAffinityMask(GetCurrentProcess(), &nProcessAffinityMask, &nSystemAffinityMask))) + nProcessorCountMask = nProcessAffinityMask; + + nAffinityMask &= nProcessorCountMask; + + auto opResult = ::SetThreadAffinityMask(id, static_cast(nAffinityMask)); + EA_UNUSED(opResult); + EAT_ASSERT_FORMATTED(opResult != 0, "The Windows platform SetThreadAffinityMask failed. GetLastError %x", GetLastError()); +#endif +} + +EATHREADLIB_API EA::Thread::ThreadAffinityMask EA::Thread::GetThreadAffinityMask(const EA::Thread::ThreadId& id) +{ + // Update the affinity mask in the thread dynamic data cache. + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + return pTDD->mnThreadAffinityMask; + } + + return kThreadAffinityMaskAny; +} + + +// Internal SetThreadName API's so we don't repeat the implementations +namespace EA { +namespace Thread { +namespace Internal { + bool PixSetThreadName(EA::Thread::ThreadId threadId, const char* pName) + { + EA_UNUSED(threadId); EA_UNUSED(pName); + + bool result = true; + + #if (defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX)) && EA_XBOX_DBG_ENABLED == 1 + wchar_t wName[EATHREAD_NAME_SIZE]; + mbstowcs(wName, pName, EATHREAD_NAME_SIZE); + result = (::SetThreadName(threadId, wName) == TRUE); // requires toolhelpx.lib + EAT_ASSERT(result); + #endif + + return result; + } + + bool WinSetThreadName(EA::Thread::ThreadId threadId, const char* pName) + { + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + static auto pSetThreadDescription = SetThreadDescription; + #else + typedef HRESULT(WINAPI *SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription); + + // Check if Windows Operating System has the 'SetThreadDescription" API. + static auto pSetThreadDescription = (SetThreadDescription)GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetThreadDescription"); + #endif + + bool result = true; + + if (pSetThreadDescription) + { + wchar_t wName[EATHREAD_NAME_SIZE]; + mbstowcs(wName, pName, EATHREAD_NAME_SIZE); + + result = SUCCEEDED(pSetThreadDescription(threadId, wName)); + EAT_ASSERT(result); + } + + return result; + } + + void WinSetThreadNameByException(EA::Thread::SysThreadId threadId, const char* pName) + { + struct ThreadNameInfo + { + DWORD dwType; + LPCSTR lpName; + DWORD dwThreadId; + DWORD dwFlags; + }; + + // This setjmp/longjmp weirdness is here to work around an apparent bug in the VS2013 debugger, + // whereby EBX will be trashed on return from RaiseException, causing bad things to happen in code + // which runs later. This only seems to happen when a debugger is attached and there's some managed + // code in the process. + + jmp_buf jmpbuf; + + EA_DISABLE_VC_WARNING(4611) + if (!setjmp(jmpbuf)) + { + ThreadNameInfo threadNameInfo = {0x1000, pName, threadId, 0}; + __try { RaiseException(0x406D1388, 0, sizeof(threadNameInfo) / sizeof(ULONG_PTR), (CONST ULONG_PTR*)(uintptr_t)&threadNameInfo); } + __except (GetExceptionCode() == 0x406D1388 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { } + longjmp(jmpbuf, 1); + } + EA_RESTORE_VC_WARNING() + } + + void SetThreadName(EAThreadDynamicData* pTDD, const char* pName) + { + strncpy(pTDD->mName, pName, EATHREAD_NAME_SIZE); + pTDD->mName[EATHREAD_NAME_SIZE - 1] = 0; + + #if defined(EA_PLATFORM_WINDOWS) && defined(EA_COMPILER_MSVC) || (defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX)) + if(pTDD->mName[0] && (pTDD->mhThread != EA::Thread::kThreadIdInvalid)) + { + #if EATHREAD_NAMING == EATHREAD_NAMING_DISABLED + bool namingEnabled = false; + #elif EATHREAD_NAMING == EATHREAD_NAMING_ENABLED + bool namingEnabled = true; + #else + bool namingEnabled = IsDebuggerPresent(); + #endif + + if(namingEnabled) + { + PixSetThreadName(pTDD->mhThread, pTDD->mName); + WinSetThreadName(pTDD->mhThread, pTDD->mName); + WinSetThreadNameByException(pTDD->mnThreadId, pTDD->mName); + } + } + #endif + } +} // namespace Internal +} // namespace Thread +} // namespace EA + +EATHREADLIB_API void EA::Thread::SetThreadName(const char* pName) { SetThreadName(GetThreadId(), pName); } +EATHREADLIB_API const char* EA::Thread::GetThreadName() { return GetThreadName(GetThreadId()); } + +EATHREADLIB_API void EA::Thread::SetThreadName(const EA::Thread::ThreadId& id, const char* pName) +{ + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + Internal::SetThreadName(pTDD, pName); + } +} + +EATHREADLIB_API const char* EA::Thread::GetThreadName(const EA::Thread::ThreadId& id) +{ + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + return pTDD ? pTDD->mName : ""; +} + +EATHREADLIB_API int EA::Thread::GetProcessorCount() +{ + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + // Capilano has 7-ish physical CPUs available to titles. We can access 50 - 90% of the 7th Core. + // Check platform documentation for details. + DWORD_PTR ProcessAffinityMask; + DWORD_PTR SystemAffinityMask; + unsigned long nCoreCount = 6; + + if(EA_LIKELY(GetProcessAffinityMask(GetCurrentProcess(), &ProcessAffinityMask, &SystemAffinityMask))) + { + _BitScanForward(&nCoreCount, (unsigned long)~ProcessAffinityMask); + } + + return (int) nCoreCount; + + #elif defined(EA_PLATFORM_WINDOWS) + static int nProcessorCount = 0; // This doesn't really need to be an atomic integer. + + if(nProcessorCount == 0) + { + // A better function to use would possibly be KeQueryActiveProcessorCount + // (NTKERNELAPI ULONG KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors)) + + SYSTEM_INFO systemInfo; + memset(&systemInfo, 0, sizeof(systemInfo)); + GetSystemInfo(&systemInfo); + nProcessorCount = (int)systemInfo.dwNumberOfProcessors; + } + + return nProcessorCount; + + #else + static int nProcessorCount = 0; // This doesn't really need to be an atomic integer. + + if(nProcessorCount == 0) + { + // A better function to use would possibly be KeQueryActiveProcessorCount + // (NTKERNELAPI ULONG KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors)) + + SYSTEM_INFO systemInfo; + memset(&systemInfo, 0, sizeof(systemInfo)); + GetNativeSystemInfo(&systemInfo); + nProcessorCount = (int)systemInfo.dwNumberOfProcessors; + } + + return nProcessorCount; + + #endif +} + + +EATHREADLIB_API void EA::Thread::ThreadSleep(const ThreadTime& timeRelative) +{ + // Sleep(0) sleeps the current thread if any other thread of equal priority is ready to run. + // Sleep(n) sleeps the current thread for up to n milliseconds if there is any other thread of any priority ready to run. + // SwitchToThread() sleeps the current thread for one time slice if there is any other thread of any priority ready to run. + + if(timeRelative == 0) + SwitchToThread(); // It's debateable whether we should do a SwitchToThread or a Sleep(0) here. + else // The only difference is that the former allows threads of lower priority to execute. + SleepEx((unsigned)timeRelative, TRUE); +} + + +namespace EA { + namespace Thread { + extern EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId); + extern EAThreadDynamicData* FindThreadDynamicData(SysThreadId sysThreadId); + } +} + +void EA::Thread::ThreadEnd(intptr_t threadReturnValue) +{ + EAThreadDynamicData* const pTDD = FindThreadDynamicData(GetThreadId()); + if(pTDD) + { + pTDD->mnStatus = Thread::kStatusEnded; + pTDD->mnReturnValue = threadReturnValue; + pTDD->Release(); + } + + EA::Thread::SetCurrentThreadHandle(kThreadIdInvalid, true); // We use 'true' here just to be safe, as we don't know who is calling this function. + + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + // _endthreadex is not supported on Capilano because it's not compatible with C++/CX and /ZW. Use of ExitThread could result in memory leaks + // as ExitThread does not clean up memory allocated by the C runtime library. + // https://forums.xboxlive.com/AnswerPage.aspx?qid=47c1607c-bb18-4bc4-a79a-a40c59444ff3&tgt=1 + ExitThread(static_cast(threadReturnValue)); + #elif defined(EA_PLATFORM_MICROSOFT) && defined(EA_PLATFORM_CONSOLE) && !defined(EA_PLATFORM_XBOXONE) && !defined(EA_PLATFORM_XBSX) + EAT_FAIL_MSG("EA::Thread::ThreadEnd: Not supported by this platform."); + #else + _endthreadex((unsigned int)threadReturnValue); + #endif +} + + +EATHREADLIB_API EA::Thread::ThreadTime EA::Thread::GetThreadTime() +{ + // We choose to use GetTickCount because it low overhead and + // still yields values that have a precision in the same range + // as the Win32 thread time slice time. In particular: + // rdtsc takes ~5 cycles and has a nanosecond resolution. But it is unreliable + // GetTickCount() takes ~80 cycles and has ~15ms resolution. + // timeGetTime() takes ~350 cpu cycles and has 1ms resolution. + // QueryPerformanceCounter() takes ~3000 cpu cycles on most machines and has ~1us resolution. + // We add EATHREAD_MIN_ABSOLUTE_TIME to this absolute time to ensure this absolute time is never less than our min + // (This fix was required because GetTickCount64 starts at 0x0 for titles on capilano) + #if defined(EA_PLATFORM_MICROSOFT) && defined(EA_PROCESSOR_X86_64) + return (ThreadTime)(GetTickCount64() + EATHREAD_MIN_ABSOLUTE_TIME); + #else // Note that this value matches the value used by some runtime assertion code within EA::Thread. It would be best to define this as a shared constant between modules. + return (ThreadTime)(GetTickCount() + EATHREAD_MIN_ABSOLUTE_TIME); + #endif +} + + +EATHREADLIB_API void EA::Thread::SetAssertionFailureFunction(EA::Thread::AssertionFailureFunction pAssertionFailureFunction, void* pContext) +{ + gpAssertionFailureFunction = pAssertionFailureFunction; + gpAssertionFailureContext = pContext; +} + + +EATHREADLIB_API void EA::Thread::AssertionFailure(const char* pExpression) +{ + if(gpAssertionFailureFunction) + gpAssertionFailureFunction(pExpression, gpAssertionFailureContext); + else + { + #if EAT_ASSERT_ENABLED + OutputDebugStringA("EA::Thread::AssertionFailure: "); + OutputDebugStringA(pExpression); + OutputDebugStringA("\n"); + #ifdef EA_COMPILER_MSVC + __debugbreak(); + #endif + #endif + } +} + +uint32_t EA::Thread::RelativeTimeoutFromAbsoluteTimeout(ThreadTime timeoutAbsolute) +{ + EAT_ASSERT((timeoutAbsolute == kTimeoutImmediate) || (timeoutAbsolute > EATHREAD_MIN_ABSOLUTE_TIME)); // Assert that the user didn't make the mistake of treating time as relative instead of absolute. + + DWORD timeoutRelative = 0; + + if (timeoutAbsolute == kTimeoutNone) + { + timeoutRelative = INFINITE; + } + else if (timeoutAbsolute == kTimeoutImmediate) + { + timeoutRelative = 0; + } + else + { + ThreadTime timeCurrent(GetThreadTime()); + timeoutRelative = (timeoutAbsolute > timeCurrent) ? static_cast(timeoutAbsolute - timeCurrent) : 0; + } + + EAT_ASSERT((timeoutRelative == INFINITE) || (timeoutRelative < 100000000)); // Assert that the timeout is a sane value and didn't wrap around. + + return timeoutRelative; +} + +#endif // EA_PLATFORM_XXX + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/pc/eathread_semaphore_pc.cpp b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_semaphore_pc.cpp new file mode 100644 index 00000000..4d45e4ab --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_semaphore_pc.cpp @@ -0,0 +1,304 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "EABase/eabase.h" +#include "eathread/eathread_semaphore.h" +#include "eathread/eathread_sync.h" +#include + +#if defined(EA_PLATFORM_MICROSOFT) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + #if defined(EA_PLATFORM_WIN64) + #if !defined _Ret_maybenull_ + #define _Ret_maybenull_ + #endif + + #if !defined _Reserved_ + #define _Reserved_ + #endif + extern "C" WINBASEAPI _Ret_maybenull_ HANDLE WINAPI CreateSemaphoreExW(_In_opt_ LPSECURITY_ATTRIBUTES, _In_ LONG, _In_ LONG, _In_opt_ LPCWSTR, _Reserved_ DWORD, _In_ DWORD); + #endif +#endif +#ifdef CreateSemaphore + #undef CreateSemaphore +#endif + + + +#if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + + // Helper function to abstract away differences between APIs for different versions of Windows + static DWORD EASemaphoreWaitForSingleObject(HANDLE handle, DWORD milliseconds) + { + #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + return WaitForSingleObject(handle, milliseconds); + #else + return WaitForSingleObjectEx(handle, milliseconds, TRUE); + #endif + } + + EASemaphoreData::EASemaphoreData() + : mhSemaphore(0), mnCount(0), mnCancelCount(0), mnMaxCount(INT_MAX), mbIntraProcess(true) + { + EAWriteBarrier(); + static_assert(sizeof(int32_t) == sizeof(LONG), "We use int32_t and LONG interchangably. Windows (including Win64) uses 32 bit longs."); + } + + + void EASemaphoreData::UpdateCancelCount(int32_t cancelCount) + { + // This is used by the fast semaphore path. This function actually isn't called very often -- only under uncommon circumstances. + // This is based on an algorithm discussed on usenet in 2004. + // We safely increment count by min(cancelCount, -count) if count < 0. + int32_t oldCount, newCount, cmpCount; + + if(cancelCount > 0) + { + oldCount = mnCount; + + while(oldCount < 0) + { + // Increment old count by the number of cancels + if((newCount = oldCount + cancelCount) > 0) + newCount = 0; // ...but not greater then zero. + + cmpCount = oldCount; + oldCount = InterlockedCompareExchange((LONG*)&mnCount, newCount, cmpCount); + + if(oldCount == cmpCount) + { + cancelCount -= (newCount - oldCount); + break; + } + } + + if(cancelCount > 0) + InterlockedExchangeAdd((LONG*)&mnCancelCount, cancelCount); + } + } + + + EA::Thread::SemaphoreParameters::SemaphoreParameters(int initialCount, bool bIntraProcess, const char* pName) + : mInitialCount(initialCount), mMaxCount(INT_MAX), mbIntraProcess(bIntraProcess) + { + if(pName) + { + strncpy(mName, pName, sizeof(mName)-1); + mName[sizeof(mName)-1] = 0; + } + else + mName[0] = 0; + } + + + EA::Thread::Semaphore::Semaphore(const SemaphoreParameters* pSemaphoreParameters, bool bDefaultParameters) + { + if(!pSemaphoreParameters && bDefaultParameters) + { + SemaphoreParameters parameters; + Init(¶meters); + } + else + Init(pSemaphoreParameters); + } + + + EA::Thread::Semaphore::Semaphore(int initialCount) + { + SemaphoreParameters parameters(initialCount); + Init(¶meters); + } + + + EA::Thread::Semaphore::~Semaphore() + { + if(mSemaphoreData.mhSemaphore) + CloseHandle(mSemaphoreData.mhSemaphore); + } + + + bool EA::Thread::Semaphore::Init(const SemaphoreParameters* pSemaphoreParameters) + { + if(pSemaphoreParameters && !mSemaphoreData.mhSemaphore) + { + mSemaphoreData.mnCount = pSemaphoreParameters->mInitialCount; + mSemaphoreData.mnMaxCount = pSemaphoreParameters->mMaxCount; + + if(mSemaphoreData.mnCount < 0) + mSemaphoreData.mnCount = 0; + + mSemaphoreData.mbIntraProcess = pSemaphoreParameters->mbIntraProcess; + + // If the fast semaphore is disabled, then we always act like inter-process as opposed to intra-process. + #if EATHREAD_FAST_MS_SEMAPHORE_ENABLED + const bool bIntraProcess = mSemaphoreData.mbIntraProcess; + #else + const bool bIntraProcess = false; + #endif + + if(bIntraProcess) + { + // Note that we do things rather differently for intra-process, as we are + // implementing a fast semaphore. This semaphore will be at least 10 times + // faster than the OS semaphore for all Microsoft platforms for the case of + // successful immediate acquire of a semaphore. Semaphore posts (or releases + // will also be much faster than the OS version. + #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + mSemaphoreData.mhSemaphore = CreateSemaphoreA(NULL, 0, INT_MAX/2, NULL); // Intentionally ignore mnCount and mnMaxCount here. + #else + mSemaphoreData.mhSemaphore = CreateSemaphoreExW(NULL, 0, INT_MAX/2, NULL, 0, SYNCHRONIZE | SEMAPHORE_MODIFY_STATE); // Intentionally ignore mnCount and mnMaxCount here. + #endif + } + else // Else we create a conventional Win32-style semaphore. + { + #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP) + mSemaphoreData.mhSemaphore = CreateSemaphoreA(NULL, (LONG)mSemaphoreData.mnCount, (LONG)mSemaphoreData.mnMaxCount, pSemaphoreParameters->mName[0] ? pSemaphoreParameters->mName : NULL); + #else + wchar_t wName[EAArrayCount(pSemaphoreParameters->mName)]; // We do an ASCII conversion. + for(size_t c = 0; c < EAArrayCount(wName); c++) + wName[c] = (wchar_t)(uint8_t)pSemaphoreParameters->mName[c]; + mSemaphoreData.mhSemaphore = CreateSemaphoreExW(NULL, (LONG)mSemaphoreData.mnCount, (LONG)mSemaphoreData.mnMaxCount, wName[0] ? wName : NULL, 0, SYNCHRONIZE | SEMAPHORE_MODIFY_STATE); + #endif + } + + EAWriteBarrier(); + EAT_ASSERT(mSemaphoreData.mhSemaphore != 0); + return (mSemaphoreData.mhSemaphore != 0); + } + return false; + } + + + int EA::Thread::Semaphore::Wait(const ThreadTime& timeoutAbsolute) + { + EAT_ASSERT(mSemaphoreData.mhSemaphore != 0); + + // If the fast semaphore is disabled, then we always act like inter-process as opposed to intra-process. + #if EATHREAD_FAST_MS_SEMAPHORE_ENABLED + const bool bIntraProcess = mSemaphoreData.mbIntraProcess; + #else + const bool bIntraProcess = false; + #endif + + if(bIntraProcess) // If this is true, we are using the fast semaphore pathway. + { + if(InterlockedDecrement((LONG*)&mSemaphoreData.mnCount) < 0) // InterlockedDecrement returns the new value. If the mnCount was > 0 before this decrement, then this Wait function will return very quickly. + { + const DWORD dw = EASemaphoreWaitForSingleObject(mSemaphoreData.mhSemaphore, RelativeTimeoutFromAbsoluteTimeout(timeoutAbsolute)); + + if(dw != WAIT_OBJECT_0) // If there was a timeout... + { + mSemaphoreData.UpdateCancelCount(1); // or InterlockedIncrement(&mSemaphoreData.mnCancelCount); // The latter has a bug whereby mnCancelCount can increment indefinitely. + + EAT_ASSERT(dw == WAIT_TIMEOUT); // Otherwise it was probably a timeout. + if(dw == WAIT_TIMEOUT) + return kResultTimeout; + return kResultError; // WAIT_FAILED + } + } + + // It is by design that a semaphore post does a full memory barrier. + // We don't need such a barrier for this pathway to work, but rather + // it is expected by the user that such a barrier is executed. Investigation + // into the choice of a full vs. just read or write barrier has concluded + // (based on the Posix standard) that a full read-write barrier is expected. + EAReadWriteBarrier(); + + const int count = (int)mSemaphoreData.mnCount; // Make temporary to avoid race condition in ternary operator below. + return (count > 0 ? count : 0); + } + else + { + const DWORD dw = EASemaphoreWaitForSingleObject(mSemaphoreData.mhSemaphore, RelativeTimeoutFromAbsoluteTimeout(timeoutAbsolute)); + + if(dw == WAIT_OBJECT_0) + return (int)InterlockedDecrement((LONG*)&mSemaphoreData.mnCount); + else if(dw == WAIT_TIMEOUT) + return kResultTimeout; + return kResultError; + } + } + + + int EA::Thread::Semaphore::Post(int count) + { + EAT_ASSERT((mSemaphoreData.mhSemaphore != 0) && (count >= 0)); + + if(count > 0) + { + // If the fast semaphore is disabled, then we always act like inter-process as opposed to intra-process. + #if EATHREAD_FAST_MS_SEMAPHORE_ENABLED + const bool bIntraProcess = mSemaphoreData.mbIntraProcess; + #else + const bool bIntraProcess = false; + #endif + + if(bIntraProcess) // If this is true, we are using the fast semaphore pathway. + { + // It is by design that a semaphore post does a full memory barrier. + // We don't need such a barrier for this pathway to work, but rather + // it is expected by the user that such a barrier is executed. Investigation + // into the choice of a full vs. just read or write barrier has concluded + // (based on the Posix standard) that a full read-write barrier is expected. + EAReadWriteBarrier(); + + if((mSemaphoreData.mnCancelCount > 0) && (mSemaphoreData.mnCount < 0)) // Much of the time this will evaluate to false due to the first condition. + mSemaphoreData.UpdateCancelCount(InterlockedExchange((LONG*)&mSemaphoreData.mnCancelCount, 0)); // It's possible that mnCancelCount may have decremented down to zero between the previous line of code and this line of code. + + const int currentCount = mSemaphoreData.mnCount; + if((mSemaphoreData.mnMaxCount - count) < currentCount) // If count would cause an overflow... + return kResultError; // We do what most OS implementations of max-count do. count = (mSemaphoreData.mnMaxCount - currentCount); + + const int32_t nWaiterCount = -InterlockedExchangeAdd((LONG*)&mSemaphoreData.mnCount, count); // InterlockedExchangeAdd returns the initial value of mnCount. If it's below zero, then it's a count of waiters. + const int nNewCount = count - nWaiterCount; + + if(nWaiterCount > 0) // If there were waiters blocking... + { + const int32_t nReleaseCount = (count < nWaiterCount) ? count : nWaiterCount; // Call ReleaseSemaphore for as many waiters as possible. + ReleaseSemaphore(mSemaphoreData.mhSemaphore, nReleaseCount, NULL); // Note that by the time this executes, nReleaseCount might be > than the actual number of waiting threads, due to timeouts. + } + + return (nNewCount > 0 ? nNewCount : 0); + } + else + { + const int32_t nPreviousCount = InterlockedExchangeAdd((LONG*)&mSemaphoreData.mnCount, count); + const int nNewCount = nPreviousCount + count; + + const BOOL result = ReleaseSemaphore(mSemaphoreData.mhSemaphore, count, NULL); + + if(!result) + { + InterlockedExchangeAdd((LONG*)&mSemaphoreData.mnCount, -count); + EAT_ASSERT(result); + return kResultError; + } + + return nNewCount; + } + } + + return (int)mSemaphoreData.mnCount; // We don't worry if this value is changing. There is nothing that you can rely upon about this value anyway. + } + + + int EA::Thread::Semaphore::GetCount() const + { + // Under our fast pathway, mnCount can go below zero. + // Under the fast pathway, we need to add mnCancelCount to mnCount because mnCount is negative by the number of waiters and mnCancelCount is the number of waiters that have abandoned waiting but the value hasn't been rolled back into mnCount yet. + EAReadBarrier(); + const int count = (int)mSemaphoreData.mnCount + (int)mSemaphoreData.mnCancelCount; // Make temporary to avoid race condition in ternary operator below. + return (count > 0 ? count : 0); + } + +#endif // EA_PLATFORM_XXX + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/pc/eathread_thread_pc.cpp b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_thread_pc.cpp new file mode 100644 index 00000000..84fc76d7 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/pc/eathread_thread_pc.cpp @@ -0,0 +1,831 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "EABase/eabase.h" +#include "eathread/eathread.h" +#include "eathread/eathread_callstack.h" +#include "eathread/eathread_mutex.h" +#include "eathread/eathread_sync.h" +#include "eathread/eathread_thread.h" +#include "eathread/internal/eathread_global.h" + +#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900 // VS2015+ +// required for windows.h that has mismatch that is included in this file + EA_DISABLE_VC_WARNING(5031 5032)// #pragma warning(pop): likely mismatch, popping warning state pushed in different file / detected #pragma warning(push) with no corresponding +#endif + + +// Warning 6312 and 6322 are spurious, as we are not execution a case that could possibly loop. +// 6312: Possible infinite loop: use of the constant EXCEPTION_CONTINUE_EXECUTION in the exception-filter expression of a try-except. Execution restarts in the protected block +// 6322: Empty _except block +EA_DISABLE_VC_WARNING(6312 6322) + + +#if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + #include + #include + + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() + + #if defined(EA_COMPILER_MSVC) + struct ThreadNameInfo{ + DWORD dwType; + LPCSTR lpName; + DWORD dwThreadId; + DWORD dwFlags; + }; + extern "C" WINBASEAPI DWORD WINAPI SetThreadIdealProcessor(_In_ HANDLE hThread, _In_ DWORD dwIdealProcessor); + extern "C" WINBASEAPI BOOL WINAPI IsDebuggerPresent(); + #endif + + + #ifdef EA_COMPILER_MSVC + #ifndef EATHREAD_INIT_SEG_DEFINED + #define EATHREAD_INIT_SEG_DEFINED + #endif + + // We are changing the initalization ordering here because in bulkbuild tool builds the initialization + // order of globals changes and causes a crash when we attempt to lock the Mutex guarding access + // of the EAThreadDynamicData objects. The code attempts to lock a mutex that has been destructed + // and causes a crash within the WindowsSDK. This ensures that global mutex object is not destructed + // until all user code has destructed. + // + #ifndef EATHREAD_INIT_SEG_DEFINED + #define EATHREAD_INIT_SEG_DEFINED + // warning C4075: initializers put in unrecognized initialization area + //warning C4073: initializers put in library initialization area + EA_DISABLE_VC_WARNING(4075 4073) + #pragma init_seg(lib) + #endif + #endif + + + namespace EA { + namespace Thread { + extern void SetCurrentThreadHandle(HANDLE hThread, bool bDynamic); + namespace Internal { extern void SetThreadName(EAThreadDynamicData* pTDD, const char* pName); }; + } + } + + + namespace EA + { + namespace Thread + { + extern Allocator* gpAllocator; + static AtomicInt32 nLastProcessor = 0; + const size_t kMaxThreadDynamicDataCount = 128; + + struct EAThreadGlobalVars + { + EA_PREFIX_ALIGN(8) + char gThreadDynamicData[kMaxThreadDynamicDataCount][sizeof(EAThreadDynamicData)] EA_POSTFIX_ALIGN(8); + AtomicInt32 gThreadDynamicDataAllocated[kMaxThreadDynamicDataCount]; + Mutex gThreadDynamicMutex; + + EAThreadGlobalVars() {} + EAThreadGlobalVars(const EAThreadGlobalVars&) {} + EAThreadGlobalVars& operator=(const EAThreadGlobalVars&) {} + }; + EATHREAD_GLOBALVARS_CREATE_INSTANCE; + + EAThreadDynamicData* AllocateThreadDynamicData() + { + AutoMutex am(EATHREAD_GLOBALVARS.gThreadDynamicMutex); + for(size_t i(0); i < kMaxThreadDynamicDataCount; i++) + { + if(EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[i].SetValueConditional(1, 0)) + return (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + } + + // This is a safety fallback mechanism. In practice it won't be used in almost all situations. + if(gpAllocator) + return (EAThreadDynamicData*)gpAllocator->Alloc(sizeof(EAThreadDynamicData)); + else + return new EAThreadDynamicData; // This is a small problem, as this doesn't just allocate it but also constructs it. + } + + void FreeThreadDynamicData(EAThreadDynamicData* pEAThreadDynamicData) + { + AutoMutex am(EATHREAD_GLOBALVARS.gThreadDynamicMutex); + if((pEAThreadDynamicData >= (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData) && (pEAThreadDynamicData < ((EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData + kMaxThreadDynamicDataCount))) + { + pEAThreadDynamicData->~EAThreadDynamicData(); + EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[pEAThreadDynamicData - (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData].SetValue(0); + } + else + { + // Assume the data was allocated via the fallback mechanism. + if(gpAllocator) + { + pEAThreadDynamicData->~EAThreadDynamicData(); + gpAllocator->Free(pEAThreadDynamicData); + } + else + delete pEAThreadDynamicData; + } + } + + EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId) + { + for(size_t i(0); i < kMaxThreadDynamicDataCount; i++) + { + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + if(pTDD->mhThread == threadId) + return pTDD; + } + return NULL; // This is no practical way we can find the data unless thread-specific storage was involved. + } + + EAThreadDynamicData* FindThreadDynamicData(EA::Thread::SysThreadId sysThreadId) + { + for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i) + { + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + if (pTDD->mnThreadId == sysThreadId) + return pTDD; + } + return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved. + } + + bool IsDebuggerPresent() + { + #if defined(EA_PLATFORM_MICROSOFT) + return ::IsDebuggerPresent() != 0; + #else + return false; + #endif + } + } + + } + + + EAThreadDynamicData::EAThreadDynamicData() + : mhThread(EA::Thread::kThreadIdInvalid), + mnThreadId(0), // Note that this is a Windows "thread id", wheras for us thread ids are what Windows calls a thread handle. + mnStatus(EA::Thread::Thread::kStatusNone), + mnReturnValue(0), + mpBeginThreadUserWrapper(NULL), + mnRefCount(0) + { + // Empty + } + + + EAThreadDynamicData::~EAThreadDynamicData() + { + if(mhThread) + ::CloseHandle(mhThread); + + mhThread = EA::Thread::kThreadIdInvalid; + mnThreadId = 0; + } + + + void EAThreadDynamicData::AddRef() + { + mnRefCount.Increment(); + } + + + void EAThreadDynamicData::Release() + { + if(mnRefCount.Decrement() == 0) + EA::Thread::FreeThreadDynamicData(this); + } + + EA::Thread::ThreadParameters::ThreadParameters() + : mpStack(NULL) + , mnStackSize(0) + , mnPriority(kThreadPriorityDefault) + , mnProcessor(kProcessorDefault) + , mnAffinityMask(kThreadAffinityMaskAny) + , mpName("") + , mbDisablePriorityBoost(false) + { + } + + EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::sGlobalRunnableFunctionUserWrapper = NULL; + EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::sGlobalRunnableClassUserWrapper = NULL; + EA::Thread::AtomicInt32 EA::Thread::Thread::sDefaultProcessor = kProcessorAny; + EA::Thread::AtomicUint64 EA::Thread::Thread::sDefaultProcessorMask = UINT64_C(0xffffffffffffffff); + + EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::GetGlobalRunnableFunctionUserWrapper() + { + return sGlobalRunnableFunctionUserWrapper; + } + + void EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(EA::Thread::RunnableFunctionUserWrapper pUserWrapper) + { + if(sGlobalRunnableFunctionUserWrapper != NULL) + { + // Can only be set once in entire game. + EAT_ASSERT(false); + } + else + { + sGlobalRunnableFunctionUserWrapper = pUserWrapper; + } + } + + EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::GetGlobalRunnableClassUserWrapper() + { + return sGlobalRunnableClassUserWrapper; + } + + void EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(EA::Thread::RunnableClassUserWrapper pUserWrapper) + { + if(sGlobalRunnableClassUserWrapper != NULL) + { + // Can only be set once. + EAT_ASSERT(false); + } + else + sGlobalRunnableClassUserWrapper = pUserWrapper; + } + + // Helper that selects a target processor based on the provided ThreadParameters structure and the various + // pieces of shared state that EAThread maintains to implement a 'round-robin' style processor selection. + int SelectProcessor(const EA::Thread::ThreadParameters* pTP, EA::Thread::AtomicInt32& sDefaultProcessor, EA::Thread::AtomicUint64& sDefaultProcessorMask) + { + int nProcessor; + + if (pTP && (pTP->mnProcessor >= 0)) + { + nProcessor = pTP->mnProcessor; + + // This is a small attempt to try to spread out threads between processors. We don't + // care much if another thread happens to be created here and races with this. + if (nProcessor == EA::Thread::nLastProcessor) + ++EA::Thread::nLastProcessor; + } + else + { + #if defined(EA_PLATFORM_MICROSOFT) + if (!pTP || pTP->mnProcessor == EA::Thread::kProcessorAny) + { + // If the processor is not specified, then allow the scheduler to + // run the thread on any available processor + nProcessor = EA::Thread::kProcessorDefault; + } + else + #endif + + if (sDefaultProcessor >= 0) // If the user has identified a specific processor... + nProcessor = sDefaultProcessor; + else if(sDefaultProcessor == EA::Thread::kProcessorDefault) // If the user explicitly asked for the default processor + nProcessor = sDefaultProcessor; + else + { + // NOTE(rparolin): The reason we have this round-robin code is that the + // originally we used it on Xenon OS which required us to pick a CPU to run on. + // After the Xenon was deprecated this code remained and is now a functional + // requirement. We should probably deprecate and remove in the future but + // currently teams are dependent on it. + const uint64_t processorMask = sDefaultProcessorMask.GetValue(); + + do + { + nProcessor = EA::Thread::nLastProcessor.Increment(); + + if (nProcessor == MAXIMUM_PROCESSORS) + { + EA::Thread::nLastProcessor.SetValueConditional(0, MAXIMUM_PROCESSORS); + nProcessor = 0; + } + } while ((((uint64_t)1 << nProcessor) & processorMask) == 0); + } + } + + return nProcessor; + } + + EA::Thread::Thread::Thread() + { + mThreadData.mpData = NULL; + } + + EA::Thread::Thread::Thread(const Thread& t) + : mThreadData(t.mThreadData) + { + if(mThreadData.mpData) + mThreadData.mpData->AddRef(); + } + + + EA::Thread::Thread& EA::Thread::Thread::operator=(const Thread& t) + { + // We don't synchronize access to mpData; we assume that the user + // synchronizes it or this Thread instances is used from a single thread. + if(t.mThreadData.mpData) + t.mThreadData.mpData->AddRef(); + + if(mThreadData.mpData) + mThreadData.mpData->Release(); + + mThreadData = t.mThreadData; + + return *this; + } + + + EA::Thread::Thread::~Thread() + { + // We don't synchronize access to mpData; we assume that the user + // synchronizes it or this Thread instances is used from a single thread. + if(mThreadData.mpData) + mThreadData.mpData->Release(); + } + + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + static DWORD WINAPI RunnableFunctionInternal(void* pContext) + #else + static unsigned int __stdcall RunnableFunctionInternal(void* pContext) + #endif + { + // The parent thread is sharing memory with us and we need to + // make sure our view of it is synchronized with the parent. + EAReadWriteBarrier(); + + + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext; + EA::Thread::RunnableFunction pFunction = (EA::Thread::RunnableFunction)pTDD->mpStartContext[0]; + void* pCallContext = pTDD->mpStartContext[1]; + + EA::Thread::SetCurrentThreadHandle(pTDD->mpStartContext[2], false); + pTDD->mpStackBase = EA::Thread::GetStackBase(); + pTDD->mnStatus = EA::Thread::Thread::kStatusRunning; + + EA::Thread::SetThreadName(pTDD->mhThread, pTDD->mName); + + if(pTDD->mpBeginThreadUserWrapper != NULL) + { + EA::Thread::RunnableFunctionUserWrapper pWrapperFunction = (EA::Thread::RunnableFunctionUserWrapper)pTDD->mpBeginThreadUserWrapper; + // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext + pTDD->mnReturnValue = pWrapperFunction(pFunction, pCallContext); + } + else + { + pTDD->mnReturnValue = pFunction(pCallContext); + } + + const unsigned int nReturnValue = (unsigned int)pTDD->mnReturnValue; + EA::Thread::SetCurrentThreadHandle(0, false); + pTDD->mnStatus = EA::Thread::Thread::kStatusEnded; + pTDD->Release(); + return nReturnValue; + } + + void EA::Thread::Thread::SetAffinityMask(EA::Thread::ThreadAffinityMask nAffinityMask) + { + if(mThreadData.mpData->mhThread) + { + EA::Thread::SetThreadAffinityMask(mThreadData.mpData->mhThread, nAffinityMask); + } + } + + EA::Thread::ThreadAffinityMask EA::Thread::Thread::GetAffinityMask() + { + if(mThreadData.mpData->mhThread) + { + return mThreadData.mpData->mnThreadAffinityMask; + } + + return kThreadAffinityMaskAny; + } + + EA::Thread::ThreadId EA::Thread::Thread::Begin(RunnableFunction pFunction, void* pContext, const ThreadParameters* pTP, RunnableFunctionUserWrapper pUserWrapper) + { + // Check there is an entry for the current thread context in our ThreadDynamicData array. + ThreadId thisThreadId = GetThreadId(); + if(!FindThreadDynamicData(thisThreadId)) + { + EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; + if(pData) + { + pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread. + // Do no AddRef for thread execution because this is not an EAThread managed thread. + pData->AddRef(); // AddRef for this function, to be released upon this function's exit. + pData->mhThread = thisThreadId; + pData->mnThreadId = GetCurrentThreadId(); + strncpy(pData->mName, "external", EATHREAD_NAME_SIZE); + pData->mName[EATHREAD_NAME_SIZE - 1] = 0; + pData->mpStackBase = EA::Thread::GetStackBase(); + } + } + + if(mThreadData.mpData) + mThreadData.mpData->Release(); // Matches the "AddRef for ourselves" below. + + // Win32-like platforms don't support user-supplied stacks. A user-supplied stack pointer + // here would be a waste of user memory, and so we assert that mpStack == NULL. + EAT_ASSERT(!pTP || (pTP->mpStack == NULL)); + + // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be + // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed + // during execution. + EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; // Note that we use a special new here which doesn't use the heap. + mThreadData.mpData = pData; + + pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread. + pData->AddRef(); // AddRef for the thread, to be released upon the thread exiting. + pData->AddRef(); // AddRef for this function, to be released upon this function's exit. + pData->mhThread = kThreadIdInvalid; + pData->mpStartContext[0] = pFunction; + pData->mpStartContext[1] = pContext; + pData->mpBeginThreadUserWrapper = pUserWrapper; + pData->mnThreadAffinityMask = pTP ? pTP->mnAffinityMask : kThreadAffinityMaskAny; + + const unsigned nStackSize = pTP ? (unsigned)pTP->mnStackSize : 0; + + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + // Capilano no longer supports _beginthreadex. Using CreateThread instead may cause issues when using the MS CRT + // according to MSDN (memory leaks or possibly crashes) as it does not initialize the CRT. This a reasonable + // workaround while we wait for clarification from MS on what the recommended threading APIs are for Capilano. + HANDLE hThread = CreateThread(NULL, nStackSize, RunnableFunctionInternal, pData, CREATE_SUSPENDED, reinterpret_cast(&pData->mnThreadId)); + #else + HANDLE hThread = (HANDLE)_beginthreadex(NULL, nStackSize, RunnableFunctionInternal, pData, CREATE_SUSPENDED, &pData->mnThreadId); + #endif + + if(hThread) + { + pData->mhThread = hThread; + + if(pTP) + SetName(pTP->mpName); + pData->mpStartContext[2] = hThread; + + if(pTP && (pTP->mnPriority != kThreadPriorityDefault)) + SetPriority(pTP->mnPriority); + + #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + if (pTP) + { + auto result = SetThreadPriorityBoost(pData->mhThread, pTP->mbDisablePriorityBoost); + EAT_ASSERT(result != 0); + EA_UNUSED(result); + } + #endif + + #if defined(EA_PLATFORM_MICROSOFT) + int nProcessor = SelectProcessor(pTP, sDefaultProcessor, sDefaultProcessorMask); + if(pTP && pTP->mnProcessor == EA::Thread::kProcessorAny) + SetAffinityMask(pTP->mnAffinityMask); + else + SetProcessor(nProcessor); + #endif + + ResumeThread(hThread); + pData->Release(); // Matches AddRef for this function. + return hThread; + } + + pData->Release(); // Matches AddRef for this function. + pData->Release(); // Matches AddRef for this Thread class above. + pData->Release(); // Matches AddRef for the thread above. + mThreadData.mpData = NULL; // mThreadData.mpData == pData + + return (ThreadId)kThreadIdInvalid; + } + + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + static DWORD WINAPI RunnableObjectInternal(void* pContext) + #else + static unsigned int __stdcall RunnableObjectInternal(void* pContext) + #endif + { + // The parent thread is sharing memory with us and we need to + // make sure our view of it is synchronized with the parent. + EAReadWriteBarrier(); + + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext; + EA::Thread::IRunnable* pRunnable = (EA::Thread::IRunnable*)pTDD->mpStartContext[0]; + void* pCallContext = pTDD->mpStartContext[1]; + + EA::Thread::SetCurrentThreadHandle(pTDD->mpStartContext[2], false); + pTDD->mnStatus = EA::Thread::Thread::kStatusRunning; + + EA::Thread::SetThreadName(pTDD->mhThread, pTDD->mName); + + if(pTDD->mpBeginThreadUserWrapper) + { + EA::Thread::RunnableClassUserWrapper pWrapperClass = (EA::Thread::RunnableClassUserWrapper)pTDD->mpBeginThreadUserWrapper; + // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext + pTDD->mnReturnValue = pWrapperClass(pRunnable, pCallContext); + } + else + pTDD->mnReturnValue = pRunnable->Run(pCallContext); + + const unsigned int nReturnValue = (unsigned int)pTDD->mnReturnValue; + EA::Thread::SetCurrentThreadHandle(0, false); + pTDD->mnStatus = EA::Thread::Thread::kStatusEnded; + pTDD->Release(); + return nReturnValue; + } + + + EA::Thread::ThreadId EA::Thread::Thread::Begin(IRunnable* pRunnable, void* pContext, const ThreadParameters* pTP, RunnableClassUserWrapper pUserWrapper) + { + if(mThreadData.mpData) + mThreadData.mpData->Release(); // Matches the "AddRef for ourselves" below. + + // Win32-like platforms don't support user-supplied stacks. A user-supplied stack pointer + // here would be a waste of user memory, and so we assert that mpStack == NULL. + EAT_ASSERT(!pTP || (pTP->mpStack == NULL)); + + // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be + // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed + // during execution. + EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; // Note that we use a special new here which doesn't use the heap. + mThreadData.mpData = pData; + + pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread. + pData->AddRef(); // AddRef for the thread, to be released upon the thread exiting. + pData->AddRef(); // AddRef for this function, to be released upon this function's exit. + pData->mhThread = kThreadIdInvalid; + pData->mpStartContext[0] = pRunnable; + pData->mpStartContext[1] = pContext; + pData->mpBeginThreadUserWrapper = pUserWrapper; + pData->mnThreadAffinityMask = pTP ? pTP->mnAffinityMask : kThreadAffinityMaskAny; + const unsigned nStackSize = pTP ? (unsigned)pTP->mnStackSize : 0; + + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + // Capilano no longer supports _beginthreadex. Using CreateThread instead may cause issues when using the MS CRT + // according to MSDN (memory leaks or possibly crashes) as it does not initialize the CRT. This a reasonable + // workaround while we wait for clarification from MS on what the recommended threading APIs are for Capilano. + HANDLE hThread = CreateThread(NULL, nStackSize, RunnableObjectInternal, pData, CREATE_SUSPENDED, reinterpret_cast(&pData->mnThreadId)); + #else + HANDLE hThread = (HANDLE)_beginthreadex(NULL, nStackSize, RunnableObjectInternal, pData, CREATE_SUSPENDED, &pData->mnThreadId); + #endif + + if(hThread) + { + pData->mhThread = hThread; + + if(pTP) + SetName(pTP->mpName); + + pData->mpStartContext[2] = hThread; + + if(pTP && (pTP->mnPriority != kThreadPriorityDefault)) + SetPriority(pTP->mnPriority); + + #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + if (pTP) + { + auto result = SetThreadPriorityBoost(pData->mhThread, pTP->mbDisablePriorityBoost); + EAT_ASSERT(result != 0); + EA_UNUSED(result); + } + #endif + + #if defined(EA_PLATFORM_MICROSOFT) + int nProcessor = SelectProcessor(pTP, sDefaultProcessor, sDefaultProcessorMask); + if(pTP && pTP->mnProcessor == EA::Thread::kProcessorAny) + SetAffinityMask(pTP->mnAffinityMask); + else + SetProcessor(nProcessor); + #endif + + ResumeThread(hThread); // This will unsuspend the thread. + pData->Release(); // Matches AddRef for this function. + return hThread; + } + + pData->Release(); // Matches AddRef for this function. + pData->Release(); // Matches AddRef for this Thread class above. + pData->Release(); // Matches AddRef for the thread above. + mThreadData.mpData = NULL; + + return (ThreadId)kThreadIdInvalid; + } + + + EA::Thread::Thread::Status EA::Thread::Thread::WaitForEnd(const ThreadTime& timeoutAbsolute, intptr_t* pThreadReturnValue) + { + // The mThreadData memory is shared between threads and when + // reading it we must be synchronized. + EAReadWriteBarrier(); + + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + // Todo: Consider that there may be a subtle race condition here if + // the user immediately calls WaitForEnd right after calling Begin. + if(mThreadData.mpData) + { + if(mThreadData.mpData->mhThread) // If it was started... + { + // We must not call WaitForEnd from the thread we are waiting to end. That would result in a deadlock. + EAT_ASSERT(mThreadData.mpData->mhThread != EA::Thread::GetThreadId()); + // dwResult normally should be 'WAIT_OBJECT_0', but can also be WAIT_ABANDONED or WAIT_FAILED. + const DWORD dwResult = ::WaitForSingleObject(mThreadData.mpData->mhThread, RelativeTimeoutFromAbsoluteTimeout(timeoutAbsolute)); + if(dwResult == WAIT_TIMEOUT) + return kStatusRunning; + + // Close the handle now so as to minimize handle proliferation. + ::CloseHandle(mThreadData.mpData->mhThread); + mThreadData.mpData->mhThread = 0; + mThreadData.mpData->mnStatus = kStatusEnded; + } + + if(pThreadReturnValue) + { + EAReadWriteBarrier(); + *pThreadReturnValue = mThreadData.mpData->mnReturnValue; + } + return kStatusEnded; // A thread was created, so it must have ended. + } + else + { + // Else the user hasn't started the thread yet, so we wait until the user starts it. + // Ideally, what we really want to do here is wait for some kind of signal. + // Instead for the time being we do a polling loop. + while((!mThreadData.mpData || !mThreadData.mpData->mhThread) && (GetThreadTime() < timeoutAbsolute)) + { + ThreadSleep(1); + EAReadWriteBarrier(); + EACompilerMemoryBarrier(); + } + if(mThreadData.mpData) + return WaitForEnd(timeoutAbsolute); + } + return kStatusNone; // No thread has been started. + } + + + EA::Thread::Thread::Status EA::Thread::Thread::GetStatus(intptr_t* pThreadReturnValue) const + { + // The mThreadData memory is shared between threads and when + // reading it we must be synchronized. + EAReadWriteBarrier(); + + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + if(mThreadData.mpData) + { + if(mThreadData.mpData->mhThread) // If the thread has been started... + { + DWORD dwExitStatus; + + // Note that GetExitCodeThread is a hazard if the user of a thread exits + // with a return value that is equal to the value of STILL_ACTIVE (i.e. 259). + // We can document that users shouldn't do this, or we can change the code + // here to use WaitForSingleObject(hThread, 0) and assume the thread is + // still active if the return value is WAIT_TIMEOUT. + if(::GetExitCodeThread(mThreadData.mpData->mhThread, &dwExitStatus)) + { + if(dwExitStatus == STILL_ACTIVE) + return kStatusRunning; // Nothing has changed. + ::CloseHandle(mThreadData.mpData->mhThread); // Do this now so as to minimize handle proliferation. + mThreadData.mpData->mhThread = 0; + } // else fall through. + } // else fall through. + + if(pThreadReturnValue) + *pThreadReturnValue = mThreadData.mpData->mnReturnValue; + mThreadData.mpData->mnStatus = kStatusEnded; + return kStatusEnded; + } + return kStatusNone; + } + + + EA::Thread::ThreadId EA::Thread::Thread::GetId() const + { + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + if(mThreadData.mpData) + return (ThreadId)mThreadData.mpData->mhThread; + return kThreadIdInvalid; + } + + + int EA::Thread::Thread::GetPriority() const + { + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + if(mThreadData.mpData) + { + const int nPriority = ::GetThreadPriority(mThreadData.mpData->mhThread); + return kThreadPriorityDefault + (nPriority - THREAD_PRIORITY_NORMAL); + } + return kThreadPriorityUnknown; + } + + + bool EA::Thread::Thread::SetPriority(int nPriority) + { + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + + // For more information on how Windows handle thread priority based on process priority, see + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/scheduling_priorities.asp + + EAT_ASSERT(nPriority != kThreadPriorityUnknown); + if(mThreadData.mpData) + { + int nNewPriority = THREAD_PRIORITY_NORMAL + (nPriority - kThreadPriorityDefault); + bool result = ::SetThreadPriority(mThreadData.mpData->mhThread, nNewPriority) != 0; + + // Windows process running in NORMAL_PRIORITY_CLASS is picky about the priority passed in. + // So we need to set the priority to the next priority supported + #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + while(!result) + { + if(nNewPriority >= THREAD_PRIORITY_TIME_CRITICAL) + return ::SetThreadPriority(mThreadData.mpData->mhThread, THREAD_PRIORITY_TIME_CRITICAL) != 0; + + if(nNewPriority <= THREAD_PRIORITY_IDLE) + return ::SetThreadPriority(mThreadData.mpData->mhThread, THREAD_PRIORITY_IDLE) != 0; + + result = ::SetThreadPriority(mThreadData.mpData->mhThread, nNewPriority) != 0; + nNewPriority++; + } + + #endif + + return result; + } + return false; + } + + + void EA::Thread::Thread::SetProcessor(int nProcessor) + { + if(mThreadData.mpData) + { + #if defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + + static int nProcessorCount = GetProcessorCount(); + if(nProcessor >= nProcessorCount) + nProcessor %= nProcessorCount; + + ThreadAffinityMask mask = 0x7F; // default to all 7 available cores. + if (nProcessor >= 0) + mask = ((ThreadAffinityMask)1) << nProcessor; + + SetThreadAffinityMask(mThreadData.mpData->mhThread, mask); + + #else + static int nProcessorCount = GetProcessorCount(); + + if(nProcessor < 0) + nProcessor = MAXIMUM_PROCESSORS; // This causes the SetThreadIdealProcessor to reset to 'no ideal processor'. + else + { + if(nProcessor >= nProcessorCount) + nProcessor %= nProcessorCount; + } + + // SetThreadIdealProcessor differs from SetThreadAffinityMask in that SetThreadIdealProcessor is not + // a strict assignment, and it allows the OS to move the thread if the ideal processor is busy. + // SetThreadAffinityMask is a more rigid assignment, but it can result in slower performance and + // possibly hangs due to processor contention between threads. For Windows we use SetIdealThreadProcessor + // in the name of safety and likely better overall performance. + SetThreadIdealProcessor(mThreadData.mpData->mhThread, (DWORD)nProcessor); + + #endif + } + } + + + typedef VOID (APIENTRY *PAPCFUNC)(_In_ ULONG_PTR dwParam); + extern "C" WINBASEAPI DWORD WINAPI QueueUserAPC(_In_ PAPCFUNC pfnAPC, _In_ HANDLE hThread, _In_ ULONG_PTR dwData); + + void EA::Thread::Thread::Wake() + { + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + struct ThreadWake{ static void WINAPI Empty(ULONG_PTR){} }; + if(mThreadData.mpData && mThreadData.mpData->mhThread) + ::QueueUserAPC((PAPCFUNC)ThreadWake::Empty, mThreadData.mpData->mhThread, 0); + } + + + const char* EA::Thread::Thread::GetName() const + { + if(mThreadData.mpData) + return mThreadData.mpData->mName; + + return ""; + } + + + void EA::Thread::Thread::SetName(const char* pName) + { + if (mThreadData.mpData && pName) + EA::Thread::Internal::SetThreadName(mThreadData.mpData, pName); + } + + +#endif // EA_PLATFORM_MICROSOFT + +EA_RESTORE_VC_WARNING() // 6312 6322 + +#if defined(EA_COMPILER_MSVC) && EA_COMPILER_VERSION >= 1900 // VS2015+ + EA_RESTORE_VC_WARNING() // 5031 5032 +#endif diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_barrier_unix.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_barrier_unix.cpp new file mode 100644 index 00000000..41d3a2d7 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_barrier_unix.cpp @@ -0,0 +1,189 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include + + +#if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #include + #include + #include + #ifdef EA_PLATFORM_WINDOWS + EA_DISABLE_ALL_VC_WARNINGS() + #include // Presumably we are using pthreads-win32. + EA_RESTORE_ALL_VC_WARNINGS() + #endif + + + EABarrierData::EABarrierData() + : mCV(), mMutex(), mnHeight(0), mnCurrent(0), mnCycle(0), mbValid(false) + {} + + + EA::Thread::BarrierParameters::BarrierParameters(int height, bool bIntraProcess, const char* pName) + : mHeight(height), mbIntraProcess(bIntraProcess) + { + if(pName) + strncpy(mName, pName, sizeof(mName)-1); + else + mName[0] = 0; + } + + + EA::Thread::Barrier::Barrier(const BarrierParameters* pBarrierParameters, bool bDefaultParameters) + { + if(!pBarrierParameters && bDefaultParameters) + { + BarrierParameters parameters; + Init(¶meters); + } + else + Init(pBarrierParameters); + } + + + EA::Thread::Barrier::Barrier(int height) + { + BarrierParameters parameters(height); + Init(¶meters); + } + + + EA::Thread::Barrier::~Barrier() + { + if(mBarrierData.mbValid){ + EAT_ASSERT(mBarrierData.mnCurrent == mBarrierData.mnHeight); + int result = pthread_mutex_destroy(&mBarrierData.mMutex); + EA_UNUSED(result); + EAT_ASSERT(result == 0); + result = pthread_cond_destroy(&mBarrierData.mCV); + EAT_ASSERT(result == 0); + EA_UNUSED( result ); //if compiling without asserts + } + } + + + bool EA::Thread::Barrier::Init(const BarrierParameters* pBarrierParameters) + { + if(pBarrierParameters && !mBarrierData.mbValid){ + mBarrierData.mbValid = false; + mBarrierData.mnHeight = pBarrierParameters->mHeight; + mBarrierData.mnCurrent = pBarrierParameters->mHeight; + mBarrierData.mnCycle = 0; + + int result = pthread_mutex_init(&mBarrierData.mMutex, NULL); + if(result == 0){ + result = pthread_cond_init(&mBarrierData.mCV, NULL); + if(result == 0) + mBarrierData.mbValid = true; + else + pthread_mutex_destroy(&mBarrierData.mMutex); + } + return mBarrierData.mbValid; + } + return false; + } + + + EA::Thread::Barrier::Result EA::Thread::Barrier::Wait(const ThreadTime& timeoutAbsolute) + { + if(!mBarrierData.mbValid){ + EAT_ASSERT(false); + return kResultError; + } + + int result = pthread_mutex_lock(&mBarrierData.mMutex); + if(result != 0){ + EAT_ASSERT(false); + return kResultError; + } + + const unsigned long nCurrentCycle = (unsigned)mBarrierData.mnCycle; + bool bPrimary = false; + + if(--mBarrierData.mnCurrent == 0){ // This is not an atomic operation. We are within a mutex lock. + // The last barrier can never time out, as its action is always immediate. + mBarrierData.mnCycle++; + mBarrierData.mnCurrent = mBarrierData.mnHeight; + result = pthread_cond_broadcast(&mBarrierData.mCV); + + // The last thread into the barrier will return a result of + // kResultPrimary rather than kResultSecondary. + if(result == 0) + bPrimary = true; + //else leave result as an error value. + } + else{ + // timeoutMilliseconds + // Wait with cancellation disabled, because pthreads barrier_wait + // should not be a cancellation point. + #if defined(PTHREAD_CANCEL_DISABLE) + int cancel; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel); + #endif + + // Wait until the barrier's cycle changes, which means that + // it has been broadcast, and we don't want to wait anymore. + while(nCurrentCycle == mBarrierData.mnCycle){ + do{ + // Under SMP systems, pthread_cond_wait can return the success value 'spuriously'. + // This is by design and we must retest the predicate condition and if it has + // not true, we must go back to waiting. + result = pthread_cond_timedwait(&mBarrierData.mCV, &mBarrierData.mMutex, &timeoutAbsolute); + } while((result == 0) && (nCurrentCycle == mBarrierData.mnCycle)); + if(result != 0) + break; + } + + #if defined(PTHREAD_CANCEL_DISABLE) + int cancelTemp; + pthread_setcancelstate(cancel, &cancelTemp); + #endif + } + + // We declare a new result2 value because the old one + // might have a special value from above in it. + const int result2 = pthread_mutex_unlock(&mBarrierData.mMutex); (void)result2; + EAT_ASSERT(result2 == 0); + + if(result == 0) + return bPrimary ? kResultPrimary : kResultSecondary; + else if(result == ETIMEDOUT) + return kResultTimeout; + return kResultError; + } + + + EA::Thread::Barrier* EA::Thread::BarrierFactory::CreateBarrier() + { + EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator(); + + if(pAllocator) + return new(pAllocator->Alloc(sizeof(EA::Thread::Barrier))) EA::Thread::Barrier; + else + return new EA::Thread::Barrier; + } + + void EA::Thread::BarrierFactory::DestroyBarrier(EA::Thread::Barrier* pBarrier) + { + EA::Thread::Allocator* pAllocator = EA::Thread::GetAllocator(); + + if(pAllocator) + { + pBarrier->~Barrier(); + pAllocator->Free(pBarrier); + } + else + delete pBarrier; + } + + +#endif // EA_PLATFORM_XXX + + diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_callstack_glibc.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_callstack_glibc.cpp new file mode 100644 index 00000000..5e050256 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_callstack_glibc.cpp @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include + +#include + +namespace EA +{ +namespace Thread +{ + + +// To do: Remove the usage of sStackBase for the platforms that it's not needed, +// as can be seen from the logic below. For example Mac OSX probably doesn't need it. +static EA::Thread::ThreadLocalStorage sStackBase; + +/////////////////////////////////////////////////////////////////////////////// +// SetStackBase +// +EATHREADLIB_API void SetStackBase(void* pStackBase) +{ + if(pStackBase) + sStackBase.SetValue(pStackBase); + else + { + pStackBase = __builtin_frame_address(0); + + if(pStackBase) + SetStackBase(pStackBase); + // Else failure; do nothing. + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackBase +// +EATHREADLIB_API void* GetStackBase() +{ + #if defined(EA_PLATFORM_UNIX) + void* pBase; + if(GetPthreadStackInfo(&pBase, NULL)) + return pBase; + #endif + + // Else we require the user to have set this previously, usually via a call + // to SetStackBase() in the start function of this currently executing + // thread (or main for the main thread). + return sStackBase.GetValue(); +} + + +/////////////////////////////////////////////////////////////////////////////// +// GetStackLimit +// +EATHREADLIB_API void* GetStackLimit() +{ + #if defined(EA_PLATFORM_UNIX) + void* pLimit; + if(GetPthreadStackInfo(NULL, &pLimit)) + return pLimit; + #endif + + // If this fails then we might have an issue where you are using GCC but not + // using the GCC standard library glibc. Or maybe glibc doesn't support + // __builtin_frame_address on this platform. Or maybe you aren't using GCC but + // rather a compiler that masquerades as GCC (common situation). + void* pStack = __builtin_frame_address(0); + return (void*)((uintptr_t)pStack & ~4095); // Round down to nearest page, as the stack grows downward. + +} + + +} // namespace Thread +} // namespace EA + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_condition_unix.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_condition_unix.cpp new file mode 100644 index 00000000..a5e3503a --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_condition_unix.cpp @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include + + +#if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #include + #include + #ifdef EA_PLATFORM_WINDOWS + EA_DISABLE_ALL_VC_WARNINGS() + #include // Presumably we are using pthreads-win32. + EA_RESTORE_ALL_VC_WARNINGS() + #endif + + + EAConditionData::EAConditionData() + { + memset(&mCV, 0, sizeof(mCV)); + } + + + EA::Thread::ConditionParameters::ConditionParameters(bool bIntraProcess, const char* /*pName*/) + : mbIntraProcess(bIntraProcess) + { + // Empty + } + + + EA::Thread::Condition::Condition(const ConditionParameters* pConditionParameters, bool bDefaultParameters) + { + if(!pConditionParameters && bDefaultParameters) + { + ConditionParameters parameters; + Init(¶meters); + } + else + Init(pConditionParameters); + } + + + EA::Thread::Condition::~Condition() + { + pthread_cond_destroy(&mConditionData.mCV); + } + + + bool EA::Thread::Condition::Init(const ConditionParameters* pConditionParameters) + { + if(pConditionParameters) + { + #if defined(EA_PLATFORM_ANDROID) // Some platforms don't provide pthread_condattr_init, and pthread_condattr_t is just an int. + pthread_condattr_t cattr; + memset(&cattr, 0, sizeof(cattr)); + + const int result = pthread_cond_init(&mConditionData.mCV, &cattr); + + return (result == 0); + #else + pthread_condattr_t cattr; + pthread_condattr_init(&cattr); + + #if defined(PTHREAD_PROCESS_PRIVATE) // Some pthread implementations don't recognize this. PTHREAD_PROCESS_SHARED bugged on iphone + #if defined(EA_PLATFORM_IPHONE) || defined(EA_PLATFORM_OSX) + EAT_ASSERT( pConditionParameters->mbIntraProcess == true ); // shared conditions bugged on apple hardware + #elif defined(EA_PLATFORM_NX) + // NX platform headers don't support 'PTHREAD_PROCESS_SHARED'. We don't provide the else clause in the general code below. + if (pConditionParameters->mbIntraProcess) + pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE); + #else + if(pConditionParameters->mbIntraProcess) + pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE); + else + pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); + #endif + #endif + + const int result = pthread_cond_init(&mConditionData.mCV, &cattr); + + pthread_condattr_destroy(&cattr); + + return (result == 0); + #endif + } + + return false; + } + + + EA::Thread::Condition::Result EA::Thread::Condition::Wait(Mutex* pMutex, const ThreadTime& timeoutAbsolute) + { + int result; + pthread_mutex_t* pMutex_t; + EAMutexData* pMutexData; + + EAT_ASSERT(pMutex); + + // We have a small problem here in that if we are using the pMutex argument, + // the pthread_cond_wait call will unlock the mutex via the internal mutex data and + // not without calling the Mutex::Lock function. The result is that the Mutex doesn't + // have its lock count value reduced by one and so other threads will see the lock + // count as being 1 when in fact it should be zero. So we account for that here + // by manually maintaining the lock count, which we can do because we have the lock. + EAT_ASSERT(pMutex->GetLockCount() == 1); + pMutexData = (EAMutexData*)pMutex->GetPlatformData(); + pMutexData->SimulateLock(false); + pMutex_t = &pMutexData->mMutex; + + if(timeoutAbsolute == kTimeoutNone) + result = pthread_cond_wait(&mConditionData.mCV, pMutex_t); + else + result = pthread_cond_timedwait(&mConditionData.mCV, pMutex_t, &timeoutAbsolute); + + pMutexData->SimulateLock(true); + EAT_ASSERT(!pMutex || (pMutex->GetLockCount() == 1)); + + if(result != 0) + { + if(result == ETIMEDOUT) + return kResultTimeout; + EAT_ASSERT(false); + return kResultError; + } + return kResultOK; + } + + + bool EA::Thread::Condition::Signal(bool bBroadcast) + { + if(bBroadcast) + return (pthread_cond_broadcast(&mConditionData.mCV) == 0); + return (pthread_cond_signal(&mConditionData.mCV) == 0); + } + + +#endif // EA_PLATFORM_XXX + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_mutex_unix.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_mutex_unix.cpp new file mode 100644 index 00000000..41978356 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_mutex_unix.cpp @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include + + +#if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #ifdef EA_PLATFORM_WINDOWS + EA_DISABLE_ALL_VC_WARNINGS() + #include // Presumably we are using pthreads-win32. + EA_RESTORE_ALL_VC_WARNINGS() + #ifdef CreateMutex + #undef CreateMutex // Windows #defines CreateMutex to CreateMutexA or CreateMutexW. + #endif + #endif + + + EAMutexData::EAMutexData() + : mMutex(), mnLockCount(0) + { + #if EAT_ASSERT_ENABLED + mThreadId = EA::Thread::kThreadIdInvalid; + #endif + + ::memset(&mMutex, 0, sizeof(mMutex)); + } + + void EAMutexData::SimulateLock(bool bLock) + { + if(bLock) + { + ++mnLockCount; + +#if EAT_ASSERT_ENABLED + mThreadId = EA::Thread::GetThreadId(); +#endif + } + else + { + --mnLockCount; + +#if EAT_ASSERT_ENABLED + mThreadId = EA::Thread::kThreadIdInvalid; +#endif + } + } + + + EA::Thread::MutexParameters::MutexParameters(bool bIntraProcess, const char* /*pName*/) + : mbIntraProcess(bIntraProcess) + { + // Empty + } + + + EA::Thread::Mutex::Mutex(const MutexParameters* pMutexParameters, bool bDefaultParameters) + { + if(!pMutexParameters && bDefaultParameters) + { + MutexParameters parameters; + Init(¶meters); + } + else + Init(pMutexParameters); + } + + + EA::Thread::Mutex::~Mutex() + { + EAT_ASSERT(mMutexData.mnLockCount == 0); + pthread_mutex_destroy(&mMutexData.mMutex); + } + + + bool EA::Thread::Mutex::Init(const MutexParameters* pMutexParameters) + { + if(pMutexParameters) + { + mMutexData.mnLockCount = 0; + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + + #if !defined( EA_PLATFORM_NX ) // NX defines PTHREAD_PROCESS_PRIVATE but doesn't implement pthread_mutexattr_setpshared because they are all private. + #if defined(PTHREAD_PROCESS_PRIVATE) // Some pthread implementations don't recognize this. + #if defined(PTHREAD_PROCESS_SHARED) + if (pMutexParameters->mbIntraProcess) + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); + else + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + #else + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE); + #endif + #endif + #endif + + const int result = pthread_mutex_init(&mMutexData.mMutex, &attr); + pthread_mutexattr_destroy(&attr); + + EAT_ASSERT(result != -1); + return (result != -1); + } + + return false; + } + + + int EA::Thread::Mutex::Lock(const ThreadTime& timeoutAbsolute) + { + int result; + + EAT_ASSERT(mMutexData.mnLockCount < 100000); + + if(timeoutAbsolute == kTimeoutNone) + { + result = pthread_mutex_lock(&mMutexData.mMutex); + + if(result != 0) + { + EAT_ASSERT(false); + return kResultError; + } + } + else if(timeoutAbsolute == kTimeoutImmediate) + { + result = pthread_mutex_trylock(&mMutexData.mMutex); + + if(result != 0) + { + if(result == EBUSY) + return kResultTimeout; + + EAT_ASSERT(false); + return kResultError; + } + } + else + { + #if (defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_WINDOWS)) && !defined(EA_PLATFORM_CYGWIN) && !defined(EA_PLATFORM_ANDROID) + const timespec* pTimeSpec = &timeoutAbsolute; + result = pthread_mutex_timedlock(&mMutexData.mMutex, const_cast(pTimeSpec)); // Some pthread implementations use non-const timespec, so cast for them. + + if(result != 0) + { + if(result == ETIMEDOUT) + return kResultTimeout; + EAT_ASSERT(false); + return kResultError; + } + #else // OSX, BSD + // Some Posix systems don't have pthread_mutex_timedlock. In these + // cases we fall back to a polling mechanism. However, polling really + // isn't proper because the polling thread might be at a greater + // priority level than the lock-owning thread and thus this code + // might not work as well as desired. + while(((result = pthread_mutex_trylock(&mMutexData.mMutex)) != 0) && (GetThreadTime() < timeoutAbsolute)) + ThreadSleep(1); + + if(result != 0) + { + if(result == EBUSY) + return kResultTimeout; + EAT_ASSERT(false); + return kResultError; + } + #endif + } + + EAT_ASSERT(mMutexData.mThreadId = EA::Thread::GetThreadId()); // Intentionally '=' here and not '=='. + EAT_ASSERT(mMutexData.mnLockCount >= 0); + return ++mMutexData.mnLockCount; // This is safe to do because we have the lock. + } + + + int EA::Thread::Mutex::Unlock() + { + EAT_ASSERT(mMutexData.mThreadId == EA::Thread::GetThreadId()); + EAT_ASSERT(mMutexData.mnLockCount > 0); + + const int nReturnValue(--mMutexData.mnLockCount); // This is safe to do because we have the lock. + + if(pthread_mutex_unlock(&mMutexData.mMutex) != 0) + { + EAT_ASSERT(false); + return nReturnValue + 1; + } + + return nReturnValue; + } + + + int EA::Thread::Mutex::GetLockCount() const + { + return mMutexData.mnLockCount; + } + + + bool EA::Thread::Mutex::HasLock() const + { + #if EAT_ASSERT_ENABLED + return (mMutexData.mnLockCount > 0) && (mMutexData.mThreadId == GetThreadId()); + #else + return (mMutexData.mnLockCount > 0); // This is the best we can do. + #endif + } + + +#endif // EA_PLATFORM_XXX + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_pthread_stack_info.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_pthread_stack_info.cpp new file mode 100644 index 00000000..248f7f9f --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_pthread_stack_info.cpp @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include + +#if defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_APPLE) + #include +#endif + +namespace EA +{ +namespace Thread +{ + +#if defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_APPLE) || defined(EA_PLATFORM_SONY) + // With some implementations of pthread, the stack base is returned by pthread as NULL if it's the main thread, + // or possibly if it's a thread you created but didn't call pthread_attr_setstack manually to provide your + // own stack. It's impossible for us to tell here whether will be such a NULL return value, so we just do what + // we can and the user nees to beware that a NULL return value means that the system doesn't provide the + // given information for the current thread. This function returns false and sets pBase and pLimit to NULL in + // the case that the thread base and limit weren't returned by the system or were returned as NULL. + + #if defined(EA_PLATFORM_APPLE) + bool GetPthreadStackInfo(void** pBase, void** pLimit) + { + pthread_t thread = pthread_self(); + void* pBaseTemp = pthread_get_stackaddr_np(thread); + size_t stackSize = pthread_get_stacksize_np(thread); + + if(pBase) + *pBase = pBaseTemp; + if(pLimit) + { + if(pBaseTemp) + *pLimit = (void*)((size_t)pBaseTemp - stackSize); + else + *pLimit = NULL; + } + + return (pBaseTemp != NULL); + } + + #elif defined(EA_PLATFORM_SONY) + bool GetPthreadStackInfo(void** pBase, void** pLimit) + { + bool returnValue = false; + size_t stackSize; + void* pBaseTemp = NULL; + void* pLimitTemp = NULL; + + ScePthreadAttr attr; + + scePthreadAttrInit(&attr); + + int result = scePthreadAttrGet(scePthreadSelf(), &attr); + if(result == 0) // SCE_OK (=0) + { + result = scePthreadAttrGetstack(&attr, &pLimitTemp, &stackSize); + if((result == 0) && (pLimitTemp != NULL)) // If success... + { + pBaseTemp = (void*)((uintptr_t)pLimitTemp + stackSize); // p is returned by pthread_attr_getstack as the lowest address in the stack, and not the stack base. + returnValue = true; + } + else + { + pBaseTemp = NULL; + pLimitTemp = NULL; + } + } + + scePthreadAttrDestroy(&attr); + + if(pBase) + *pBase = pBaseTemp; + if(pLimit) + *pLimit = pLimitTemp; + + return returnValue; + } + #else + bool GetPthreadStackInfo(void** pBase, void** pLimit) + { + bool returnValue = false; + void* pBaseTemp = NULL; + void* pLimitTemp = NULL; + + pthread_attr_t attr; + pthread_attr_init(&attr); + + #if defined(EA_PLATFORM_LINUX) + int result = pthread_getattr_np(pthread_self(), &attr); + #else + int result = pthread_attr_get_np(pthread_self(), &attr); // __BSD__ or __FreeBSD__ + #endif + + if(result == 0) + { + // The pthread_attr_getstack() function returns the stack address and stack size + // attributes of the thread attributes object referred to by attr in the buffers + // pointed to by stackaddr and stacksize, respectively. According to the documentation, + // the stack address reported is the lowest memory address and not the stack 'base'. + // http://pubs.opengroup.org/onlinepubs/007904975/functions/pthread_attr_setstack.html + size_t stackSize; + result = pthread_attr_getstack(&attr, &pLimitTemp, &stackSize); + + if((result == 0) && (pLimitTemp != NULL)) // If success... + { + pBaseTemp = (void*)((uintptr_t)pLimitTemp + stackSize); // p is returned by pthread_attr_getstack as the lowest address in the stack, and not the stack base. + returnValue = true; + } + else + { + pBaseTemp = NULL; + pLimitTemp = NULL; + } + } + + pthread_attr_destroy(&attr); + + if(pBase) + *pBase = pBaseTemp; + if(pLimit) + *pLimit = pLimitTemp; + + return returnValue; + } + #endif +#endif + + +} // namespace Callstack +} // namespace EA + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_semaphore_unix.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_semaphore_unix.cpp new file mode 100644 index 00000000..10f171b3 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_semaphore_unix.cpp @@ -0,0 +1,261 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include + + +#if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #include + #include + #include + #ifdef EA_PLATFORM_WINDOWS + EA_DISABLE_ALL_VC_WARNINGS() + #include + #include // Presumably we are using pthreads-win32. + EA_RESTORE_ALL_VC_WARNINGS() + #ifdef CreateSemaphore + #undef CreateSemaphore // Windows #defines CreateSemaphore to CreateSemaphoreA or CreateSemaphoreW. + #endif + #endif + + + EASemaphoreData::EASemaphoreData() + : mnCount(0), mnMaxCount(INT_MAX) + { + memset(&mSemaphore, 0, sizeof(mSemaphore)); + } + + + EA::Thread::SemaphoreParameters::SemaphoreParameters(int initialCount, bool bIntraProcess, const char* /*pName*/) + : mInitialCount(initialCount), mMaxCount(INT_MAX), mbIntraProcess(bIntraProcess) + { + } + + + EA::Thread::Semaphore::Semaphore(const SemaphoreParameters* pSemaphoreParameters, bool bDefaultParameters) + { + if(!pSemaphoreParameters && bDefaultParameters) + { + SemaphoreParameters parameters; + Init(¶meters); + } + else + Init(pSemaphoreParameters); + } + + + EA::Thread::Semaphore::Semaphore(int initialCount) + { + SemaphoreParameters parameters(initialCount); + Init(¶meters); + } + + + EA::Thread::Semaphore::~Semaphore() + { + #if defined(EA_PLATFORM_ANDROID) + sem_destroy(&mSemaphoreData.mSemaphore); // Android's sem_destroy is broken. http://code.google.com/p/android/issues/detail?id=3106 + #else + int result = -1; + + for(;;) + { + result = sem_destroy(&mSemaphoreData.mSemaphore); + + if((result == -1) && (errno == EBUSY)) // If another thread or process is blocked on this semaphore... + ThreadSleep(kTimeoutYield); // Yield. If we don't yield, it's possible we could block other other threads or processes from running, on some systems. + else + break; + } + + EAT_ASSERT(result != -1); + #endif + } + + + bool EA::Thread::Semaphore::Init(const SemaphoreParameters* pSemaphoreParameters) + { + if(pSemaphoreParameters) + { + mSemaphoreData.mnCount = pSemaphoreParameters->mInitialCount; + mSemaphoreData.mnMaxCount = pSemaphoreParameters->mMaxCount; + + if(mSemaphoreData.mnCount < 0) + mSemaphoreData.mnCount = 0; + + mSemaphoreData.mbIntraProcess = pSemaphoreParameters->mbIntraProcess; + + int result = sem_init(&mSemaphoreData.mSemaphore, mSemaphoreData.mbIntraProcess ? 1 : 0, (unsigned)mSemaphoreData.mnCount); + + // To consider: Remove this fallback and simply return false if the first attempt failed. + if((result == -1) && mSemaphoreData.mbIntraProcess) + { + result = sem_init(&mSemaphoreData.mSemaphore, 0, (unsigned)mSemaphoreData.mnCount); + + if(result == -1) + { + EAT_ASSERT(false); + memset(&mSemaphoreData.mSemaphore, 0, sizeof(mSemaphoreData.mSemaphore)); + } + else + mSemaphoreData.mbIntraProcess = false; + } + + return (result != -1); + } + + return false; + } + + + int EA::Thread::Semaphore::Wait(const ThreadTime& timeoutAbsolute) + { + int result; + + if(timeoutAbsolute == kTimeoutNone) + { + // We retry waits that were interrupted by signals. Should we instead require + // the user to deal with this and return an error value? Or should we require + // the user to disable the appropriate signal interruptions? + while(((result = sem_wait(&mSemaphoreData.mSemaphore)) == -1) && (errno == EINTR)) + continue; + + if(result == -1) + { + EAT_ASSERT(false); // This is an error condition. + return kResultError; + } + } + else if(timeoutAbsolute == kTimeoutImmediate) + { + // The sem_trywait() and sem_wait() functions shall return zero if the calling process successfully + // performed the semaphore lock operation on the semaphore designated by sem. If the call was + // unsuccessful, the state of the semaphore shall be unchanged, and the function shall return a + // value of -1 and set errno to indicate the error. + int trywaitResult = sem_trywait(&mSemaphoreData.mSemaphore); + + if(trywaitResult == -1) + { + if(errno == EAGAIN) // The sem_* family of functions are different from pthreads because they set errno instead of returning an error value. + return kResultTimeout; + + #ifdef EA_PLATFORM_WINDOWS // On Windows, the errno mechanism doesn't work unless you + if(mSemaphoreData.mnCount == 0) // are using the C runtime library as a shared dll between + return kResultTimeout; // the app and pthreads.dll. We try to account for the existence + #endif // of this problem here in a somewhat conservative way. + + return kResultError; + } + + // Android sem_trywait is broken and in earlier versions returns EAGAIN instead of setting + // errno to EAGAIN. http://source-android.frandroid.com/bionic/libc/docs/CHANGES.TXT + #if defined(EA_PLATFORM_ANDROID) + if(trywaitResult == EAGAIN) + return kResultTimeout; + #endif + } + else + { + // Some systems don't have a sem_timedwait. In these cases we + // fall back to a polling mechanism. However, polling really + // isn't proper because the polling thread might be at a greater + // priority level than the lock-owning thread and thus this code + // might not work as well as desired. + + #if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_WINDOWS) + // We retry waits that were interrupted by signals. Should we instead require + // the user to deal with this and return an error value? Or should we require + // the user to disable the appropriate signal interruptions? + while(((result = sem_timedwait(&mSemaphoreData.mSemaphore, &timeoutAbsolute)) == -1) && (errno == EINTR)) + continue; + + if(result == -1) + { + if(errno == ETIMEDOUT) // The sem_* family of functions are different from pthreads because they set errno instead of returning an error value. + return kResultTimeout; + + #ifdef EA_PLATFORM_WINDOWS // On Windows, the errno mechanism doesn't work unless you + if(mSemaphoreData.mnCount == 0) // are using the C runtime library as a shared dll between + return kResultTimeout; // the app and pthreads.dll. We try to account for the existence + #endif // of this problem here in a somewhat conservative way. + + return kResultError; + } + #else + // BSD family of Unixes usually lack sem_trywait as of this writing. + // This is a major problem, as a polling solution doesn't work under some circumstances. + + while(((result = sem_trywait(&mSemaphoreData.mSemaphore)) == -1) && (errno == EAGAIN || errno == EINTR) && (GetThreadTime() < timeoutAbsolute)) + ThreadSleep(1); + + if(result == -1) + { + if(errno == EAGAIN) + return kResultTimeout; + + return kResultError; + } + #endif + } + + EAT_ASSERT(mSemaphoreData.mnCount > 0); + return (int)mSemaphoreData.mnCount.Decrement(); // AtomicInt32 operation. Note that the value of the semaphore count could change from the returned value by the time the caller reads it. This is fine but the user should understand this. + } + + + int EA::Thread::Semaphore::Post(int count) + { + // Some systems have a sem_post_multiple which we could take advantage + // of here to atomically post multiple times. + EAT_ASSERT(mSemaphoreData.mnCount >= 0); + + // It's hard to correctly implement mnMaxCount here, given that it + // may be modified by multiple threads during this execution. So if you want + // to use max-count with an IntraProcess semaphore safely then you need to + // post only from a single thread, or at least a single thread at a time. + + int currentCount = mSemaphoreData.mnCount; + + // If count would cause an overflow exit early + if ((mSemaphoreData.mnMaxCount - count) < currentCount) + return kResultError; + + currentCount += count; + + while(count-- > 0) + { + ++mSemaphoreData.mnCount; // AtomicInt32 operation. + + if(sem_post(&mSemaphoreData.mSemaphore) == -1) + { + --mSemaphoreData.mnCount; // AtomicInt32 operation. + EAT_ASSERT(false); + return kResultError; + } + } + + // If all count posts occurred... + return currentCount; // It's possible that another thread may have modified this value since we changed it, but that's not important. + } + + + int EA::Thread::Semaphore::GetCount() const + { + return (int)mSemaphoreData.mnCount; + } + + +#endif // EA_PLATFORM_XXX + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_stadia.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_stadia.cpp new file mode 100644 index 00000000..41f863e8 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_stadia.cpp @@ -0,0 +1,156 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(EA_PLATFORM_STADIA) +#include +#include +#include + +EATHREADLIB_API bool isUint(const char* buffer) +{ + if (buffer == nullptr || *buffer == '\0') + return false; + + while (isspace(*buffer) && *buffer != '\0') + ++buffer; + + return *buffer != '\0' && isdigit(*buffer); +} + +EATHREADLIB_API EA::Thread::ThreadAffinityMask parseSet(char const* buffer, size_t len) +{ + EA::Thread::ThreadAffinityMask mask = 0; + if (buffer == nullptr || len == 0) + return mask; + + char const* ptr = buffer; + char const* end_ptr = buffer + len; + if (!isUint(ptr)) + return mask; + + uint32_t prevVal = strtoul(ptr, (char**)&ptr, 10); + uint32_t val = 0; + + mask |= 1ULL << prevVal; + + char op; + while (ptr < end_ptr) + { + op = *ptr; + ++ptr; + switch (op) + { + case '-': + + if (!isUint(ptr)) + return mask; + val = strtoul(ptr, (char**)&ptr, 10); + for (int i = prevVal + 1; i <= val; ++i) + { + mask |= 1ULL << i; + } + break; + case ',': + if (!isUint(ptr)) + return mask; + prevVal = strtoul(ptr, (char**)&ptr, 10); + mask |= 1ULL << prevVal; + break; + default: + EAT_ASSERT_MSG(isspace(op), "Invalid op token."); + break; + } + } + return mask; +} + +EATHREADLIB_API bool readLine(const char* fileName, char* buffer, size_t len) +{ + if (fileName == nullptr || buffer == nullptr || len == 0) + return false; + + memset(buffer, 0, len); + + FILE* fp = fopen(fileName, "r"); + if (fp == nullptr) + return false; + + bool ret = fgets(buffer, len, fp) != nullptr; + fclose(fp); + if (ret) + { + size_t slen = strlen(buffer); + if (buffer[slen - 1] == '\n') + buffer[slen - 1] = '\0'; + } + + return ret; +} + +EATHREADLIB_API bool getProcessCpuSetCGroup(char* cgroup, size_t len) +{ + return readLine("/proc/self/cpuset", cgroup, len); +} + +EATHREADLIB_API EA::Thread::ThreadAffinityMask getInstanceCpuSet() +{ + static const char* instanceFileName = "/sys/devices/system/cpu/present"; + + EA::Thread::ThreadAffinityMask mask = 0; + char buf[256]; + if (!readLine(instanceFileName, buf, sizeof(buf))) + return mask; + + return parseSet(buf, strlen(buf)); +} + +EATHREADLIB_API EA::Thread::ThreadAffinityMask initValidCpuAffinityMask() +{ + char cgroup[32]; + if (!getProcessCpuSetCGroup(cgroup, sizeof(cgroup))) + { + return getInstanceCpuSet(); + } + char cpuSetFileName[256]; + sprintf(cpuSetFileName, "/sys/fs/cgroup/cpuset%s/cpuset.cpus", cgroup); + + char buf[256]; + if (!readLine(cpuSetFileName, buf, sizeof(buf))) + return getInstanceCpuSet(); + + return parseSet(buf, strlen(buf)); +} + +EATHREADLIB_API EA::Thread::ThreadAffinityMask getValidCpuAffinityMask() +{ + static EA::Thread::ThreadAffinityMask mask = initValidCpuAffinityMask(); + return mask; +} + +EATHREADLIB_API int initAvailableCpuCount() +{ + EA::Thread::ThreadAffinityMask mask = getValidCpuAffinityMask(); + + int count = 1 & mask; + EAT_ASSERT_MSG(count, "First available cpu is not 0"); + bool prevVal = count; + + for (int i = 1; i < sizeof(mask); ++i) + { + bool val = (1 << i) & mask; + EAT_ASSERT_MSG(!val || prevVal, "Non-contiguous range of available cpus"); + count += val; + prevVal = val; + } + + return count; +} + +EATHREADLIB_API int getAvailableCpuCount() +{ + static int count = initAvailableCpuCount(); + return count; +} + +#endif diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_thread_unix.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_thread_unix.cpp new file mode 100644 index 00000000..de4c162d --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_thread_unix.cpp @@ -0,0 +1,1157 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include +#include +#include "eathread/internal/eathread_global.h" + +#if defined(EA_PLATFORM_NX) + #include // used by gDeadThreadsFutex +#endif + +#if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #include + #include + #include + #include + #if defined(EA_PLATFORM_WINDOWS) + EA_DISABLE_ALL_VC_WARNINGS() + #include // Presumably we are using pthreads-win32. + EA_RESTORE_ALL_VC_WARNINGS() + #elif defined(EA_PLATFORM_LINUX) + #include + #include + #include + #include + #elif defined(EA_PLATFORM_APPLE) + #include + #include + #elif defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_CONSOLE_BSD) || defined(EA_PLATFORM_FREEBSD) + #include + #endif + + #if defined(EA_PLATFORM_LINUX) + #define EA_ALLOW_POSIX_THREADS_PRIORITIES 0 + #else + #define EA_ALLOW_POSIX_THREADS_PRIORITIES 1 + #endif + + #ifdef EA_PLATFORM_ANDROID + #include + #include "../android/com_ea_EAThread_EAThread.h" + #endif + +namespace +{ +#ifdef EA_PLATFORM_ANDROID + void SetCurrentThreadNameJava(JNIEnv* env, const char* name); +#endif + + // We convert a an EAThread priority (higher value implies higher priority) to a native priority + // value, as some implementations of pthreads use lower values to indicate higher priority. + void ConvertToNativePriority(int eathreadPriority, sched_param& param, int& policy) + { + using namespace EA::Thread; + + #if defined(EA_PLATFORM_WINDOWS) + param.sched_priority = THREAD_PRIORITY_NORMAL + (nPriority - kThreadPriorityDefault); + + #elif defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_CYGWIN) + // We are assuming Kernel 2.6 and later behaviour, but perhaps we should dynamically detect. + // Linux supports three scheduling policies SCHED_OTHER, SCHED_RR, and SCHED_FIFO. + // The process needs to be run with superuser privileges to use SCHED_RR or SCHED_FIFO. + // Thread priorities for SCHED_OTHER do not exist; there is only one allowed thread priority: 0. + // Thread priorities for SCHED_RR and SCHED_FIFO are limited to the range of [1, 99] (verified with Linux 2.6.17), + // despite documentation on the Internet that refers to ranges of 0-99, 1-100, 1-140, etc. + // Higher values in this range mean higher priority. + // All of the SCHED_RR and SCHED_FIFO privileges are higher than anything running at SCHED_OTHER, + // as they are considered to be real-time scheduling. A result of this is that there is no + // such thing as having a thread of lower priority than normal; there are only higher real-time priorities. + policy = 0; + + #if EA_ALLOW_POSIX_THREADS_PRIORITIES + if(eathreadPriority <= kThreadPriorityDefault) + #endif + { + #if defined(SCHED_OTHER) + policy = SCHED_OTHER; + #endif + param.sched_priority = 0; + } + #if EA_ALLOW_POSIX_THREADS_PRIORITIES + else + { + #if defined(SCHED_RR) + policy = SCHED_RR; + #endif + param.sched_priority = (eathreadPriority - kThreadPriorityDefault); + } + #else + EA_UNUSED(eathreadPriority); + #endif + + #else + #if defined(EA_PLATFORM_CYGWIN) + policy = SCHED_OTHER; + #else + policy = SCHED_FIFO; + #endif + + int nMin = sched_get_priority_min(policy); + int nMax = sched_get_priority_max(policy); + int adjustDir = 1; + + // Some implementations of Pthreads associate higher priorities with smaller + // integer values. We hide this. To the user, a higher value must always + // indicate higher priority. + if(nMin > nMax) + { + adjustDir = nMax; + nMax = nMin; + nMin = adjustDir; + adjustDir = -1; // Translate user's desire for higher priority to lower value + } + + // native_priority = EAThread_native_priority_default +/- EAThread_user_priority + // This calculation sets the default to be in the middle of low and high, which might not be so for all platforms in practice. + param.sched_priority = ((nMin + nMax) / 2) + (adjustDir * eathreadPriority); + + if(param.sched_priority < nMin) + param.sched_priority = nMin; + else if(param.sched_priority > nMax) + param.sched_priority = nMax; + #endif + } + + + // We convert a native priority value to an EAThread priority (higher value implies higher + // priority), as some implementations of pthreads use lower values to indicate higher priority. + int ConvertFromNativePriority(const sched_param& param, int policy) + { + using namespace EA::Thread; + + #if defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_CYGWIN) + EA_UNUSED(policy); + return kThreadPriorityDefault + param.sched_priority; // This works for both SCHED_OTHER, SCHED_RR, and SCHED_FIFO. + #else + #if defined(EA_PLATFORM_WINDOWS) // In the case of windows, we know for sure that normal priority is defined by 'THREAD_PRIORITY_NORMAL'. + if(param.sched_priority == THREAD_PRIORITY_NORMAL) + return kThreadPriorityDefault; + #elif !(defined(EA_PLATFORM_CYGWIN) || defined(EA_PLATFORM_NX)) + if(policy == SCHED_OTHER) + return 0; // 0 is the only priority permitted with the SCHED_OTHER scheduling scheme. + #endif + + // Some implementations of Pthreads associate higher priorities with smaller + // integer values. We hide this. To the user, a higher value must always + // indicate higher priority. + + // EAThread_user_priority = +/-(native_priority - EAThread_native_priority_default) + const int nMin = sched_get_priority_min(policy); + const int nMax = sched_get_priority_max(policy); + const int nativeBasePriority = (nMin + nMax) / 2; + const int adjustDir = (nMin < nMax) ? 1 : -1; + + return adjustDir * (param.sched_priority - nativeBasePriority); + #endif + } + + + // Setup stack and/or priority of a new thread + void SetupThreadAttributes(pthread_attr_t& creationAttribs, const EA::Thread::ThreadParameters* pTP) + { + int result = 0; + EA_UNUSED( result ); //only used for assertions + + // We create the thread as attached, and we'll call either pthread_join or pthread_detach, + // depending on whether WaitForEnd (pthread_join) is called or not (pthread_detach). + + if(pTP) + { + // Set thread stack address and/or size + if(pTP->mpStack) + { + EAT_ASSERT(pTP->mnStackSize != 0); + + #if !defined(EA_PLATFORM_CYGWIN) // Some implementations of pthreads does not support pthread_attr_setstack. + #if defined(EA_PLATFORM_NX) + EAT_ASSERT((pTP->mnStackSize & 4095) == 0); // stack size has to be 4k aligned on NX. + EAT_ASSERT(((uintptr_t)pTP->mpStack & 4095) == 0); // stack has to be 4k aligned on NX. + #endif + #if defined(PTHREAD_STACK_MIN) + EAT_ASSERT((pTP->mnStackSize >= PTHREAD_STACK_MIN)); + #endif + result = pthread_attr_setstack(&creationAttribs, (void*)pTP->mpStack, pTP->mnStackSize); + EAT_ASSERT(result == 0); + #endif + } + else if(pTP->mnStackSize) + { + #if defined(EA_PLATFORM_NX) + EAT_ASSERT((pTP->mnStackSize & 4095) == 0); // stack size has to be 4k aligned. + #endif + #if defined(PTHREAD_STACK_MIN) + EAT_ASSERT((pTP->mnStackSize >= PTHREAD_STACK_MIN)); + #endif + result = pthread_attr_setstacksize(&creationAttribs, pTP->mnStackSize); + EAT_ASSERT(result == 0); + } + + // Set initial non-zero priority + // Even if pTP->mnPriority == kThreadPriorityDefault, we need to run this on some platforms, as the thread priority for new threads on them isn't the same as the thread priority for the main thread. + int policy = SCHED_OTHER; + sched_param param; + + // initialize all fields of param before we start tinkering with them + result = pthread_attr_getschedparam(&creationAttribs, ¶m); + EAT_ASSERT(result == 0); + ConvertToNativePriority(pTP->mnPriority, param, policy); + result = pthread_attr_setschedpolicy(&creationAttribs, policy); + EAT_ASSERT(result == 0); + result = pthread_attr_setschedparam(&creationAttribs, ¶m); + EAT_ASSERT(result == 0); + + // Unix doesn't let you specify thread CPU affinity via pthread attributes. + // Instead you need to call sched_setaffinity or pthread_setaffinity_np. + } + } + + static void SetPlatformThreadAffinity(EAThreadDynamicData* pTDD) + { + if(pTDD->mThreadId != EA::Thread::kThreadIdInvalid) // If the thread has been created... + { + #if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_NX) + EAT_ASSERT(pTDD && (pTDD->mThreadId != EA::Thread::kThreadIdInvalid)); + + #if defined(EA_PLATFORM_ANDROID) // Android doesn't provide pthread_setaffinity_np. + if(pTDD->mThreadPid != 0) // If the running thread has assigned its pid yet (it does so right after starting)... + { + int processor = (1 << pTDD->mStartupProcessor); + syscall(__NR_sched_setaffinity, pTDD->mThreadPid, sizeof(processor), &processor); + } + // Else wait till the thread has started and let it set its own affinity. + #else + cpu_set_t cpus; + CPU_ZERO(&cpus); + + EAT_ASSERT_MSG(((1 << pTDD->mStartupProcessor) & EA::Thread::GetAvailableCpuAffinityMask()) != 0, "Processor not available!"); + + CPU_SET(pTDD->mStartupProcessor, &cpus); + + int result = pthread_setaffinity_np(pTDD->mThreadId, sizeof(cpus), &cpus); + EAT_ASSERT_MSG(result == 0, "pthread_setaffinity_np: failure"); + EA_UNUSED(result); + #endif + #endif + } + // Else the thread hasn't started yet, or has already exited. Let the thread set its own + // affinity when it starts. + } + +#ifdef EA_PLATFORM_ANDROID + static JavaVM* gJavaVM = NULL; + static jclass gEAThreadClass = NULL; + static jmethodID gSetNameMethodId = NULL; + + // This function needs to be called by Java code on app startup, before + // the C main entrypoint is called, or very shortly thereafter. It's called + // via Java with EAThread.Init(); If your Java code doesn't call EAThread.Init + // then any C++ calls to Java code will fail (though those C++ calls could + // manually set the JNIEnv per call). + extern "C" __attribute__ ((visibility("default"))) JNIEXPORT void JNICALL Java_com_ea_EAThread_EAThread_Init(JNIEnv* env, jclass eaThreadClass) + { + gEAThreadClass = (jclass)env->NewGlobalRef(eaThreadClass); + int getJavaVMResult = env->GetJavaVM(&gJavaVM); + EA_UNUSED(getJavaVMResult); + EAT_ASSERT_MSG(getJavaVMResult == 0, "Unable to get the Java VM from the JNI environment."); + gSetNameMethodId = env->GetStaticMethodID(gEAThreadClass, "setCurrentThreadName", "(Ljava/lang/String;)V"); + EAT_ASSERT(gSetNameMethodId); + } + + // This is called just after creation of a C/C++ thread. + // The Java JNI requires you to do this, else bad things will happen. + static JNIEnv* AttachJavaThread() + { + if(gJavaVM) + { + JNIEnv* env; + jint resultCode = gJavaVM->AttachCurrentThread(&env, NULL); + (void)resultCode; + EAT_ASSERT_MSG(resultCode == 0, "Unable to attach thread"); + + return env; + } + + return NULL; + } + + // This is called just before destruction of a C/C++ thread. + // It is the complement of AttachJavaThread. + static void DetachJavaThread() + { + if(gJavaVM) + gJavaVM->DetachCurrentThread(); + } + + void SetCurrentThreadNameJava(JNIEnv* env, const char* name) + { + if(gJavaVM) + { + jstring threadName = env->NewStringUTF(name); + env->CallStaticVoidMethod(gEAThreadClass, gSetNameMethodId, threadName); + env->DeleteLocalRef(threadName); + + EAT_ASSERT(env->ExceptionOccurred() == NULL); + if (env->ExceptionOccurred() != NULL) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } + } +#endif + +} // namespace + +namespace EA +{ + namespace Thread + { + extern Allocator* gpAllocator; + + const size_t kMaxThreadDynamicDataCount = 128; + + struct EAThreadGlobalVars + { + EA_PREFIX_ALIGN(8) + char gThreadDynamicData[kMaxThreadDynamicDataCount][sizeof(EAThreadDynamicData)] EA_POSTFIX_ALIGN(8); + AtomicInt32 gThreadDynamicDataAllocated[kMaxThreadDynamicDataCount]; + Mutex gThreadDynamicMutex; + }; + EATHREAD_GLOBALVARS_CREATE_INSTANCE; + + EAThreadDynamicData* AllocateThreadDynamicData() + { + for(size_t i(0); i < kMaxThreadDynamicDataCount; i++) + { + if(EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[i].SetValueConditional(1, 0)) + return (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + } + + // This is a safety fallback mechanism. In practice it won't be used in almost all situations. + if(gpAllocator) + return (EAThreadDynamicData*)gpAllocator->Alloc(sizeof(EAThreadDynamicData)); + else + return (EAThreadDynamicData*)new char[sizeof(EAThreadDynamicData)]; // We assume the returned alignment is sufficient. + } + + void FreeThreadDynamicData(EAThreadDynamicData* pEAThreadDynamicData) + { + if((pEAThreadDynamicData >= (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData) && (pEAThreadDynamicData < ((EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData + kMaxThreadDynamicDataCount))) + { + pEAThreadDynamicData->~EAThreadDynamicData(); + EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[pEAThreadDynamicData - (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData].SetValue(0); + } + else + { + // Assume the data was allocated via the fallback mechanism. + pEAThreadDynamicData->~EAThreadDynamicData(); + if(gpAllocator) + gpAllocator->Free(pEAThreadDynamicData); + else + delete[] (char*)pEAThreadDynamicData; + } + } + + // This is a public function. + EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId) + { + for(size_t i(0); i < kMaxThreadDynamicDataCount; i++) + { + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + + if(pTDD->mThreadId == threadId) + return pTDD; + } + + return NULL; // This is no practical way we can find the data unless thread-specific storage was involved. + } + + #if defined(EA_PLATFORM_APPLE) + EAThreadDynamicData* FindThreadDynamicData(EA::Thread::SysThreadId sysThreadId) + { + for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i) + { + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i]; + if (pTDD->mSysThreadId == sysThreadId) + return pTDD; + } + return NULL; // This is no practical way we can find the data unless thread-specific storage was involved. + } + #endif + +#if defined(EA_PLATFORM_NX) + // Nintendo doesn't properly support pthread_detach, so we need to use pthread_join instead. + // Since a thread is not allowed to join itself (and joining a not-yet-dead thread is not ideal) + // we queue up a couple of threadIds and have later threads to die, join the earlier ones. + + static ThreadId sDeadThreadIds[] = + { + EA::Thread::kThreadIdInvalid, // 0 + EA::Thread::kThreadIdInvalid, // 1 + EA::Thread::kThreadIdInvalid, // 2 + EA::Thread::kThreadIdInvalid, // 3 + }; + Futex gDeadThreadsFutex; + + static void NX_PushDeadThreadId(ThreadId threadId) + { + ThreadId tempId; + + { + AutoFutex _(gDeadThreadsFutex); + + // grab oldest entry, from end of array + tempId = sDeadThreadIds[EAArrayCount(sDeadThreadIds) - 1]; + + // shift array contents forward + memmove(sDeadThreadIds + 1, sDeadThreadIds, sizeof(sDeadThreadIds) - sizeof(sDeadThreadIds[0])); + + sDeadThreadIds[0] = threadId; + } + + if (tempId != EA::Thread::kThreadIdInvalid) + pthread_join(tempId, NULL); + } + + void NX_CleanupDeadThreads() + { + ThreadId tempIds[EAArrayCount(sDeadThreadIds)]; + + { + AutoFutex _(gDeadThreadsFutex); + for (int i = 0; i < EAArrayCount(sDeadThreadIds); ++i) + { + tempIds[i] = sDeadThreadIds[i]; + sDeadThreadIds[i] = EA::Thread::kThreadIdInvalid; + } + } + + for (int i = 0; i < EAArrayCount(tempIds); ++i) + { + if (tempIds[i] != EA::Thread::kThreadIdInvalid) + pthread_join(tempIds[i], NULL); + } + } +#endif // defined(EA_PLATFORM_NX) + } +} + + +EAThreadDynamicData::EAThreadDynamicData() + : mThreadId(EA::Thread::kThreadIdInvalid), + mSysThreadId(0), + mThreadPid(0), + mnStatus(EA::Thread::Thread::kStatusNone), + mnReturnValue(0), + //mpStartContext[], + mpBeginThreadUserWrapper(NULL), + mnRefCount(0), + //mName[], + mStartupProcessor(EA::Thread::kProcessorDefault), + mnThreadAffinityMask(EA::Thread::kThreadAffinityMaskAny), + mRunMutex(), + mStartedSemaphore() +{ + memset(mpStartContext, 0, sizeof(mpStartContext)); + memset(mName, 0, sizeof(mName)); +} + + +EAThreadDynamicData::~EAThreadDynamicData() +{ + if (mThreadId != EA::Thread::kThreadIdInvalid) + { + #if defined(EA_PLATFORM_NX) + EA::Thread::NX_PushDeadThreadId(mThreadId); + #else + pthread_detach(mThreadId); + #endif + } + + mThreadId = EA::Thread::kThreadIdInvalid; + mThreadPid = 0; + mSysThreadId = 0; +} + + +void EAThreadDynamicData::AddRef() +{ + mnRefCount.Increment(); // Note that mnRefCount is an AtomicInt32. +} + + +void EAThreadDynamicData::Release() +{ + if(mnRefCount.Decrement() == 0) // Note that mnRefCount is an AtomicInt32. + EA::Thread::FreeThreadDynamicData(this); +} + + +EA::Thread::ThreadParameters::ThreadParameters() + : mpStack(NULL), + mnStackSize(0), + #ifdef EA_PLATFORM_NX + mnPriority(kThreadPriorityMin), + #else + mnPriority(kThreadPriorityDefault), + #endif + mnProcessor(kProcessorDefault), + mpName(""), + mnAffinityMask(kThreadAffinityMaskAny), + mbDisablePriorityBoost(false) +{ + // Empty +} + + +EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::sGlobalRunnableFunctionUserWrapper = NULL; +EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::sGlobalRunnableClassUserWrapper = NULL; +EA::Thread::AtomicInt32 EA::Thread::Thread::sDefaultProcessor = kProcessorAny; +EA::Thread::AtomicUint64 EA::Thread::Thread::sDefaultProcessorMask = UINT64_C(0xffffffffffffffff); + + +EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::GetGlobalRunnableFunctionUserWrapper() +{ + return sGlobalRunnableFunctionUserWrapper; +} + + +void EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(EA::Thread::RunnableFunctionUserWrapper pUserWrapper) +{ + if(sGlobalRunnableFunctionUserWrapper) + EAT_FAIL_MSG("Thread::SetGlobalRunnableFunctionUserWrapper already set."); // Can only be set once for the application. + else + sGlobalRunnableFunctionUserWrapper = pUserWrapper; +} + + +EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::GetGlobalRunnableClassUserWrapper() +{ + return sGlobalRunnableClassUserWrapper; +} + + +void EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(EA::Thread::RunnableClassUserWrapper pUserWrapper) +{ + if(sGlobalRunnableClassUserWrapper) + EAT_FAIL_MSG("EAThread::SetGlobalRunnableClassUserWrapper already set."); // Can only be set once for the application. + else + sGlobalRunnableClassUserWrapper = pUserWrapper; +} + + +EA::Thread::Thread::Thread() +{ + mThreadData.mpData = NULL; +} + + +EA::Thread::Thread::Thread(const Thread& t) + : mThreadData(t.mThreadData) +{ + if(mThreadData.mpData) + mThreadData.mpData->AddRef(); +} + + +EA::Thread::Thread& EA::Thread::Thread::operator=(const Thread& t) +{ + // We don't synchronize access to mpData; we assume that the user + // synchronizes it or this Thread instances is used from a single thread. + if(t.mThreadData.mpData) + t.mThreadData.mpData->AddRef(); + + if(mThreadData.mpData) + mThreadData.mpData->Release(); + + mThreadData = t.mThreadData; + + return *this; +} + + +EA::Thread::Thread::~Thread() +{ + // We don't synchronize access to mpData; we assume that the user + // synchronizes it or this Thread instances is used from a single thread. + if(mThreadData.mpData) + mThreadData.mpData->Release(); +} + + +static void* RunnableFunctionInternal(void* pContext) +{ + // The parent thread is sharing memory with us and we need to + // make sure our view of it is synchronized with the parent. + EAReadWriteBarrier(); + + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext; + EA::Thread::RunnableFunction pFunction = (EA::Thread::RunnableFunction)pTDD->mpStartContext[0]; + void* pCallContext = pTDD->mpStartContext[1]; + + #if defined(EA_PLATFORM_LINUX) && defined(__NR_gettid) + // Unfortunately there's no reliable way to translate a pthread_t to a + // thread pid value. Thus we can know the thread's pid only via the + // thread itself calling gettid(). + + //pTDD->mThreadPid = gettid(); // Some Linux compiler distributions declare gettid(), some don't. + pTDD->mThreadPid = (pid_t)syscall(__NR_gettid); // It's safest to just make the syscall directly. + + if(pTDD->mStartupProcessor != EA::Thread::kProcessorDefault && pTDD->mStartupProcessor != EA::Thread::kProcessorAny) + SetPlatformThreadAffinity(pTDD); + else if(pTDD->mStartupProcessor == EA::Thread::kProcessorAny) + EA::Thread::SetThreadAffinityMask(pTDD->mnThreadAffinityMask); + #elif defined(EA_PLATFORM_NX) + // We do this to emulate the behavior of other consoles that default create thread + // with an affinity capable of floating across all available cores. + if(pTDD->mStartupProcessor == EA::Thread::kProcessorDefault) + EA::Thread::SetThreadAffinityMask(EA::Thread::kThreadAffinityMaskAny); + + #elif !defined(EA_PLATFORM_CONSOLE) && !defined(EA_PLATFORM_MOBILE) + pTDD->mThreadPid = getpid(); // We can't set a thread affinity with a process id. + #else + pTDD->mThreadPid = 0; + #endif + + // Lock the runtime mutex which is used to allow other threads to wait on this thread with a timeout. + pTDD->mRunMutex.Lock(); // Important that this be before the semaphore post. + pTDD->mStartedSemaphore.Post(); // Announce that the thread has started. + pTDD->mnStatus = EA::Thread::Thread::kStatusRunning; + pTDD->mpStackBase = EA::Thread::GetStackBase(); + +#ifdef EA_PLATFORM_ANDROID + + JNIEnv* jni = AttachJavaThread(); + if(pTDD->mName[0]) + SetCurrentThreadNameJava(jni, pTDD->mName); +#elif !EATHREAD_OTHER_THREAD_NAMING_SUPPORTED + // Under Unix we need to set the thread name from the thread that is being named and not from an outside thread. + if(pTDD->mName[0]) + EA::Thread::SetThreadName(pTDD->mThreadId, pTDD->mName); +#endif + + if(pTDD->mpBeginThreadUserWrapper) + { + // If user wrapper is specified, call user wrapper and pass the pFunction and pContext. + EA::Thread::RunnableFunctionUserWrapper pWrapperFunction = (EA::Thread::RunnableFunctionUserWrapper)pTDD->mpBeginThreadUserWrapper; + pTDD->mnReturnValue = pWrapperFunction(pFunction, pCallContext); + } + else + pTDD->mnReturnValue = pFunction(pCallContext); + + #ifdef EA_PLATFORM_ANDROID + DetachJavaThread(); + #endif + + void* pReturnValue = (void*)pTDD->mnReturnValue; + pTDD->mnStatus = EA::Thread::Thread::kStatusEnded; + pTDD->mRunMutex.Unlock(); + pTDD->Release(); + + return pReturnValue; +} + + +static void* RunnableObjectInternal(void* pContext) +{ + EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext; + EA::Thread::IRunnable* pRunnable = (EA::Thread::IRunnable*)pTDD->mpStartContext[0]; + void* pCallContext = pTDD->mpStartContext[1]; + + #if defined(EA_PLATFORM_LINUX) && defined(__NR_gettid) + // Unfortunately there's no reliable way to translate a pthread_t to a + // thread pid value. Thus we can know the thread's pid only via the + // thread itself calling gettid(). + + //pTDD->mThreadPid = gettid(); // Some Linux compiler distributions declare gettid(), some don't. + pTDD->mThreadPid = (pid_t)syscall(__NR_gettid); // It's safest to just make the syscall directly. + + if(pTDD->mStartupProcessor != EA::Thread::kProcessorDefault && pTDD->mStartupProcessor != EA::Thread::kProcessorAny) + SetPlatformThreadAffinity(pTDD); + else if(pTDD->mStartupProcessor == EA::Thread::kProcessorAny) + EA::Thread::SetThreadAffinityMask(pTDD->mnThreadAffinityMask); + + #elif !defined(EA_PLATFORM_CONSOLE) && !defined(EA_PLATFORM_MOBILE) + pTDD->mThreadPid = getpid(); // We can't set a thread affinity with a process id. + #else + pTDD->mThreadPid = 0; + #endif + + pTDD->mRunMutex.Lock(); // Important that this be before the semaphore post. + pTDD->mStartedSemaphore.Post(); + + pTDD->mnStatus = EA::Thread::Thread::kStatusRunning; + +#ifdef EA_PLATFORM_ANDROID + JNIEnv* jni = AttachJavaThread(); + if(pTDD->mName[0]) + SetCurrentThreadNameJava(jni, pTDD->mName); +#elif !EATHREAD_OTHER_THREAD_NAMING_SUPPORTED + // Under Unix we need to set the thread name from the thread that is being named and not from an outside thread. + if(pTDD->mName[0]) + EA::Thread::SetThreadName(pTDD->mThreadId, pTDD->mName); +#endif + + if(pTDD->mpBeginThreadUserWrapper) + { + // If user wrapper is specified, call user wrapper and pass the pFunction and pContext. + EA::Thread::RunnableClassUserWrapper pWrapperClass = (EA::Thread::RunnableClassUserWrapper)pTDD->mpBeginThreadUserWrapper; + pTDD->mnReturnValue = pWrapperClass(pRunnable, pCallContext); + } + else + pTDD->mnReturnValue = pRunnable->Run(pCallContext); + + #ifdef EA_PLATFORM_ANDROID + DetachJavaThread(); + #endif + + void* const pReturnValue = (void*)pTDD->mnReturnValue; + pTDD->mnStatus = EA::Thread::Thread::kStatusEnded; + pTDD->mRunMutex.Unlock(); + pTDD->Release(); + + return pReturnValue; +} + + +/// BeginThreadInternal +/// Extraction of both RunnableFunction and RunnableObject EA::Thread::Begin in order to have thread initialization +/// in one place +static EA::Thread::ThreadId BeginThreadInternal(EAThreadData& mThreadData, void* pRunnableOrFunction, void* pContext, const EA::Thread::ThreadParameters* pTP, + void* pUserWrapper, void* (*InternalThreadFunction)(void*)) +{ + using namespace EA::Thread; + + // The parent thread is sharing memory with us and we need to + // make sure our view of it is synchronized with the parent. + EAReadWriteBarrier(); + + // Check there is an entry for the current thread context in our ThreadDynamicData array. + EA::Thread::ThreadId thisThreadId = EA::Thread::GetThreadId(); + if(!FindThreadDynamicData(thisThreadId)) + { + EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; + if(pData) + { + pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread. + // Do no AddRef for thread execution because this is not an EAThread managed thread. + pData->AddRef(); // AddRef for this function, to be released upon this function's exit. + pData->mThreadId = thisThreadId; + pData->mSysThreadId = GetSysThreadId(); + pData->mThreadPid = 0; + strncpy(pData->mName, "external", EATHREAD_NAME_SIZE); + pData->mName[EATHREAD_NAME_SIZE - 1] = 0; + pData->mpStackBase = EA::Thread::GetStackBase(); + } + } + + if(mThreadData.mpData) + mThreadData.mpData->Release(); // Matches the "AddRef for ourselves" below. + + // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be + // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed + // during execution. + EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; // Note that we use a special new here which doesn't use the heap. + EAT_ASSERT(pData); + + if(pData) + { + mThreadData.mpData = pData; + + pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread. + pData->AddRef(); // AddRef for the thread, to be released upon the thread exiting. + pData->AddRef(); // AddRef for this function, to be released upon this function's exit. + pData->mThreadId = kThreadIdInvalid; + pData->mSysThreadId = 0; + pData->mThreadPid = 0; + pData->mnStatus = Thread::kStatusNone; + pData->mpStartContext[0] = pRunnableOrFunction; + pData->mpStartContext[1] = pContext; + pData->mpBeginThreadUserWrapper = pUserWrapper; + pData->mStartupProcessor = pTP ? pTP->mnProcessor % EA::Thread::GetProcessorCount() : kProcessorDefault; + pData->mnThreadAffinityMask = pTP ? pTP->mnAffinityMask : kThreadAffinityMaskAny; + if(pTP && pTP->mpName) + strncpy(pData->mName, pTP->mpName, EATHREAD_NAME_SIZE); + pData->mName[EATHREAD_NAME_SIZE - 1] = 0; + + // Pass NULL attribute pointer if there are no special setup steps + pthread_attr_t* pCreationAttribs = NULL; + int result(0); + + pthread_attr_t creationAttribs; + + // SetupThreadAttributes is always called in order to create threads as detached + pthread_attr_init(&creationAttribs); + + #ifndef EA_PLATFORM_ANDROID + // Posix has stated that we should call pthread_attr_setinheritsched, otherwise the + // thread priority set up in pthread_attr_t gets ignored by the newly created thread. + pthread_attr_setinheritsched(&creationAttribs, PTHREAD_EXPLICIT_SCHED); + #endif + + SetupThreadAttributes(creationAttribs, pTP); + pCreationAttribs = &creationAttribs; + + result = pthread_create(&pData->mThreadId, pCreationAttribs, InternalThreadFunction, pData); + + if(result == 0) // If success... + { + ThreadId threadIdTemp = pData->mThreadId; // Temp value because Release below might delete pData. + + // If additional attributes were used, free initialization data. + if(pCreationAttribs) + { + result = pthread_attr_destroy(pCreationAttribs); + EAT_ASSERT(result == 0); + } + + if(pData->mStartupProcessor != kProcessorDefault && pData->mStartupProcessor != EA::Thread::kProcessorAny) + SetPlatformThreadAffinity(pData); + else if(pData->mStartupProcessor == EA::Thread::kProcessorAny) + EA::Thread::SetThreadAffinityMask(pData->mThreadId, pData->mnThreadAffinityMask); + + + pData->Release(); // Matches AddRef for this function. + return threadIdTemp; + } + + // If additional attributes were used, free initialization data + if(pCreationAttribs) + { + result = pthread_attr_destroy(pCreationAttribs); + EAT_ASSERT(result == 0); + } + + pData->Release(); // Matches AddRef for "cleanup" above. + pData->Release(); // Matches AddRef for this Thread class above. + pData->Release(); // Matches AddRef for thread above. + mThreadData.mpData = NULL; // mThreadData.mpData == pData + } + + return (ThreadId)kThreadIdInvalid; +} + + +EA::Thread::ThreadId EA::Thread::Thread::Begin(RunnableFunction pFunction, void* pContext, const ThreadParameters* pTP, RunnableFunctionUserWrapper pUserWrapper) +{ + ThreadId threadId = BeginThreadInternal(mThreadData, reinterpret_cast((uintptr_t)pFunction), pContext, pTP, reinterpret_cast((uintptr_t)pUserWrapper), RunnableFunctionInternal); + + if(pTP && pTP->mnProcessor == EA::Thread::kProcessorAny) + EA::Thread::Thread::SetAffinityMask(pTP->mnAffinityMask); + + if(pTP && pTP->mpName) + SetName(pTP->mpName); + + return threadId; +} + + +EA::Thread::ThreadId EA::Thread::Thread::Begin(IRunnable* pRunnable, void* pContext, const ThreadParameters* pTP, RunnableClassUserWrapper pUserWrapper) +{ + ThreadId threadId = BeginThreadInternal(mThreadData, reinterpret_cast((uintptr_t)pRunnable), pContext, pTP, reinterpret_cast((uintptr_t)pUserWrapper), RunnableObjectInternal); + + if(pTP && pTP->mnProcessor == EA::Thread::kProcessorAny) + EA::Thread::Thread::SetAffinityMask(pTP->mnAffinityMask); + + if(pTP && pTP->mpName) + SetName(pTP->mpName); + + return threadId; +} + + +EA::Thread::Thread::Status EA::Thread::Thread::WaitForEnd(const ThreadTime& timeoutAbsolute, intptr_t* pThreadReturnValue) +{ + // In order to support timeoutAbsolute, we don't just call pthread_join, as that's an infinitely blocking call. + // Instead we wait on a Mutex (with a timeout) which the running thread locked, and will unlock as it is exiting. + // Only after the successful Mutex lock do we call pthread_join, as we know that it won't block for an indeterminate + // amount of time (barring a thread priority inversion problem). If the user never calls WaitForEnd, then we + // will eventually call pthread_detach in the EAThreadDynamicData destructor. + + // The mThreadData memory is shared between threads and when + // reading it we must be synchronized. + EAReadWriteBarrier(); + + // A mutex lock around mpData is not needed below because mpData is never allowed to go from non-NULL to NULL. + // However, there is an argument that can be made for placing a memory read barrier before reading it. + + if(mThreadData.mpData) // If this is non-zero then we must have created the thread. + { + EAT_ASSERT(mThreadData.mpData->mThreadId != kThreadIdInvalid); // WaitForEnd can't be called on a thread that hasn't been started + + // We must not call WaitForEnd from the thread we are waiting to end. + // That would result in a deadlock, at least if the timeout was infinite. + EAT_ASSERT(mThreadData.mpData->mThreadId != EA::Thread::GetThreadId()); + + Status currentStatus = GetStatus(); + + if(currentStatus == kStatusNone) // If the thread hasn't started yet... + { + // The thread has not been started yet. Wait on the semaphore (which is posted when the thread actually starts executing). + Semaphore::Result result = (Semaphore::Result)mThreadData.mpData->mStartedSemaphore.Wait(timeoutAbsolute); + EAT_ASSERT(result != Semaphore::kResultError); + + if(result >= 0) // If the Wait succeeded, as opposed to timing out... + { + // We know for sure that the thread status is running now. + currentStatus = kStatusRunning; + mThreadData.mpData->mStartedSemaphore.Post(); // Re-post the semaphore so that any other callers of WaitForEnd don't block on the Wait above. + } + } // fall through. + + if(currentStatus == kStatusRunning) // If the thread has started but not yet exited... + { + // Lock on the mutex (which is available when the thread is exiting) + Mutex::Result result = (Mutex::Result)mThreadData.mpData->mRunMutex.Lock(timeoutAbsolute); + EAT_ASSERT(result != Mutex::kResultError); + + if(result > 0) // If the Lock succeeded, as opposed to timing out... then the thread has exited or is in the process of exiting. + { + // Do a pthread join. This is a blocking call, but we know that it will end very soon, + // as the mutex unlock the thread did is done right before the thread returns to the OS. + // The return value of pthread_join has information that isn't currently useful to us. + pthread_join(mThreadData.mpData->mThreadId, NULL); + mThreadData.mpData->mThreadId = kThreadIdInvalid; + + // We know for sure that the thread status is ended now. + currentStatus = kStatusEnded; + mThreadData.mpData->mRunMutex.Unlock(); + } + // Else the Lock timed out, which means that the thread didn't exit before we ran out of time. + // In this case we need to return to the user that the status is kStatusRunning. + } + else + { + // Else currentStatus == kStatusEnded. + pthread_join(mThreadData.mpData->mThreadId, NULL); + mThreadData.mpData->mThreadId = kThreadIdInvalid; + } + + if(currentStatus == kStatusEnded) + { + // Call GetStatus again to get the thread return value. + currentStatus = GetStatus(pThreadReturnValue); + } + + return currentStatus; + } + else + { + // Else the user hasn't started the thread yet, so we wait until the user starts it. + // Ideally, what we really want to do here is wait for some kind of signal. + // Instead for the time being we do a polling loop. + while((!mThreadData.mpData || (mThreadData.mpData->mThreadId == kThreadIdInvalid)) && (GetThreadTime() < timeoutAbsolute)) + { + ThreadSleep(1); + EAReadWriteBarrier(); + EACompilerMemoryBarrier(); + } + + if(mThreadData.mpData) + return WaitForEnd(timeoutAbsolute); + } + + return kStatusNone; +} + + +EA::Thread::Thread::Status EA::Thread::Thread::GetStatus(intptr_t* pThreadReturnValue) const +{ + if(mThreadData.mpData) + { + EAReadBarrier(); + Status status = (Status)mThreadData.mpData->mnStatus; + + if(pThreadReturnValue && (status == kStatusEnded)) + *pThreadReturnValue = mThreadData.mpData->mnReturnValue; + + return status; + } + + return kStatusNone; +} + + +EA::Thread::ThreadId EA::Thread::Thread::GetId() const +{ + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + if(mThreadData.mpData) + return mThreadData.mpData->mThreadId; + + return kThreadIdInvalid; +} + + +int EA::Thread::Thread::GetPriority() const +{ + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + if(mThreadData.mpData) + { + int policy; + sched_param param; + + int result = pthread_getschedparam(mThreadData.mpData->mThreadId, &policy, ¶m); + + if(result == 0) + return ConvertFromNativePriority(param, policy); + + return kThreadPriorityDefault; + } + + return kThreadPriorityUnknown; +} + + +bool EA::Thread::Thread::SetPriority(int nPriority) +{ + // A mutex lock around mpData is not needed below because + // mpData is never allowed to go from non-NULL to NULL. + EAT_ASSERT(nPriority != kThreadPriorityUnknown); + + if(mThreadData.mpData) + { + int policy; + sched_param param; + + int result = pthread_getschedparam(mThreadData.mpData->mThreadId, &policy, ¶m); + + if(result == 0) // If success... + { + ConvertToNativePriority(nPriority, param, policy); + + result = pthread_setschedparam(mThreadData.mpData->mThreadId, policy, ¶m); + } + + return (result == 0); + } + + return false; +} + + +// To consider: Make it so we return a value. +void EA::Thread::Thread::SetProcessor(int nProcessor) +{ + #if defined(EA_PLATFORM_WINDOWS) + if(mThreadData.mpData) + { + static AtomicInt32 nProcessorCount = 0; + + if(nProcessorCount == 0) + { + SYSTEM_INFO systemInfo; + memset(&systemInfo, 0, sizeof(systemInfo)); + GetSystemInfo(&systemInfo); + nProcessorCount = (int)systemInfo.dwNumberOfProcessors; + } + + DWORD dwThreadAffinityMask; + + if(nProcessor < 0) + dwThreadAffinityMask = 0xffffffff; + else + { + if(nProcessor >= nProcessorCount) + nProcessor %= nProcessorCount; + + dwThreadAffinityMask = 1 << nProcessor; + } + + SetThreadAffinityMask(mThreadData.mpData->mThreadId, dwThreadAffinityMask); + } + #elif defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_NX) + if(mThreadData.mpData) + { + mThreadData.mpData->mStartupProcessor = nProcessor; // Assign this in case the thread hasn't started yet and thus we are leaving it a message to set it when it has started. + SetPlatformThreadAffinity(mThreadData.mpData); + } + #else + EA_UNUSED(nProcessor); + #endif +} + +void EA::Thread::Thread::SetAffinityMask(EA::Thread::ThreadAffinityMask nAffinityMask) +{ + if(mThreadData.mpData && mThreadData.mpData->mThreadId) + { + SetThreadAffinityMask(mThreadData.mpData->mThreadId, nAffinityMask); + } +} + +EA::Thread::ThreadAffinityMask EA::Thread::Thread::GetAffinityMask() +{ + if(mThreadData.mpData && mThreadData.mpData->mThreadId) + { + return mThreadData.mpData->mnThreadAffinityMask; + } + + return kThreadAffinityMaskAny; +} + +void EA::Thread::Thread::Wake() +{ + // Todo: implement this. The solution is to use a signal to wake the sleeping thread via an EINTR. + // Possibly use the SIGCONT signal. Have to look into this to tell what the best approach is. +} + +const char* EA::Thread::Thread::GetName() const +{ + if(mThreadData.mpData) + return mThreadData.mpData->mName; + + return ""; +} + +void EA::Thread::Thread::SetName(const char* pName) +{ + if(mThreadData.mpData && pName) + EA::Thread::SetThreadName(mThreadData.mpData->mThreadId, pName); +} + +#if defined(EA_PLATFORM_ANDROID) +namespace EA +{ + namespace Thread + { + void SetCurrentThreadName(JNIEnv* env, const char* pName) + { + SetCurrentThreadNameJava(env, pName); + } + } +} +#endif + + +#endif // EA_PLATFORM_XXX + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/unix/eathread_unix.cpp b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_unix.cpp new file mode 100644 index 00000000..ff1a9bec --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/unix/eathread_unix.cpp @@ -0,0 +1,813 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include + + +#if defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + #include + #include + #include + #include + #ifdef EA_PLATFORM_WINDOWS + EA_DISABLE_ALL_VC_WARNINGS() + #include // Presumably we are using pthreads-win32. + EA_RESTORE_ALL_VC_WARNINGS() + #include + #else + #include + #if defined(EA_HAVE_DINKUMWARE_CPP_LIBRARY) + #include + #else + #include + #endif + + #if defined(EA_PLATFORM_OSX) || defined(EA_PLATFORM_BSD) + #include + #include + #endif + + #if defined(EA_PLATFORM_LINUX) + #include + #endif + + #if defined(EA_PLATFORM_APPLE) + #include + #endif + + #if defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_CONSOLE_BSD) || defined(__FreeBSD__) + #include + #include + #endif + + #if defined(EA_PLATFORM_ANDROID) + #include + #include + #endif + + #if defined(EA_PLATFORM_NX) + #include + #endif + + #if defined(EA_PLATFORM_STADIA) + EATHREADLIB_API int getAvailableCpuCount(); + #endif + #endif + + namespace EA + { + namespace Thread + { + // Assertion variables. + EA::Thread::AssertionFailureFunction gpAssertionFailureFunction = NULL; + void* gpAssertionFailureContext = NULL; + } + } + + EA::Thread::ThreadId EA::Thread::GetThreadId() + { + return pthread_self(); + } + + EA::Thread::ThreadId EA::Thread::GetThreadId(EA::Thread::SysThreadId id) + { + EAThreadDynamicData* const pTDD = EA::Thread::FindThreadDynamicData(id); + if(pTDD) + { + return pTDD->mThreadId; + } + + return EA::Thread::kThreadIdInvalid; + } + + int EA::Thread::GetThreadPriority() + { + int policy; + sched_param param; + ThreadId currentThreadId = pthread_self(); + + int result = pthread_getschedparam(currentThreadId, &policy, ¶m); + + if(result == 0) + { + #if defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_CYGWIN) + return kThreadPriorityDefault + param.sched_priority; // This works for both SCHED_OTHER, SCHED_RR, and SCHED_FIFO. + #else + #if defined(EA_PLATFORM_WINDOWS) + if(param.sched_priority == THREAD_PRIORITY_NORMAL) + return kThreadPriorityDefault; + #elif !(defined(EA_PLATFORM_CYGWIN) || defined(EA_PLATFORM_NX)) + if(policy == SCHED_OTHER) + return 0; // 0 is the only native priority permitted with the SCHED_OTHER scheduling scheme. + #endif + + // The following needs to be tested on a Unix-by-Unix case. + const int nMin = sched_get_priority_min(policy); + const int nMax = sched_get_priority_max(policy); + + // Some implementations of Pthreads associate higher priorities with smaller + // integer values. We hide this. To the user, a higher value must always + // indicate higher priority. + const int adjustDir = (nMin < nMax) ? 1 : -1; + const int nativeBasePriority = (nMin + nMax) / 2; + + // EAThread_user_priority = +/-(native_priority - EAThread_native_priority_default) + return adjustDir * (param.sched_priority - nativeBasePriority); + #endif + } + + return kThreadPriorityDefault; + } + + + bool EA::Thread::SetThreadPriority(int nPriority) + { + ThreadId currentThreadId = pthread_self(); + int policy; + sched_param param; + int result = -1; + + EAT_ASSERT(nPriority != kThreadPriorityUnknown); + + #if defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_CYGWIN) + // We are assuming Kernel 2.6 and later behavior, but perhaps we should dynamically detect. + // Linux supports three scheduling policies SCHED_OTHER, SCHED_RR, and SCHED_FIFO. + // The process needs to be run with superuser privileges to use SCHED_RR or SCHED_FIFO. + // Thread priorities for SCHED_OTHER do not exist; there is only one allowed thread priority: 0. + // Thread priorities for SCHED_RR and SCHED_FIFO are limited to the range of [1, 99] (verified with Linux 2.6.17), + // despite documentation on the Internet that refers to ranges of 0-99, 1-100, 1-140, etc. + // Higher values in this range mean higher priority. + // All of the SCHED_RR and SCHED_FIFO privileges are higher than anything running at SCHED_OTHER, + // as they are considered to be real-time scheduling. A result of this is that there is no + // such thing as having a thread of lower priority than normal; there are only higher real-time priorities. + if(nPriority <= kThreadPriorityDefault) + { + policy = SCHED_OTHER; + param.sched_priority = 0; + } + else + { + policy = SCHED_RR; + param.sched_priority = (nPriority - kThreadPriorityDefault); + } + + result = pthread_setschedparam(currentThreadId, policy, ¶m); + #else + // The following needs to be tested on a Unix-by-Unix case. + result = pthread_getschedparam(currentThreadId, &policy, ¶m); + + if(result == 0) + { + // Cygwin does not support any scheduling policy other than SCHED_OTHER. + #if !defined(EA_PLATFORM_CYGWIN) + if(policy == SCHED_OTHER) + policy = SCHED_FIFO; + #endif + + int nMin = sched_get_priority_min(policy); + int nMax = sched_get_priority_max(policy); + int adjustDir = 1; + + // Some implementations of pthreads associate higher priorities with smaller integer values. + // To the EAThread user, a higher value indicates a higher priority. + if (nMin > nMax) + { + adjustDir = nMax; + nMax = nMin; + nMin = adjustDir; + adjustDir = -1; // Translate user's desire for higher priority into a native lower value. + } + + // native_priority = EAThread_native_priority_default +/- EAThread_user_priority. + // This calculation sets the default to be in the middle of low and high, which might not be so for all platforms in practice. + param.sched_priority = ((nMin + nMax) / 2) + (adjustDir * nPriority); + + // Clamp to min/max as appropriate for current scheduling policy + if(param.sched_priority < nMin) + param.sched_priority = nMin; + else if(param.sched_priority > nMax) + param.sched_priority = nMax; + + result = pthread_setschedparam(currentThreadId, policy, ¶m); + } + #endif + + return (result == 0); + } + + + void* EA::Thread::GetThreadStackBase() + { + #if defined(EA_PLATFORM_APPLE) + pthread_t threadId = pthread_self(); + return pthread_get_stackaddr_np(threadId); + + #elif (EA_PLATFORM_SOLARIS) + stack_t s; + thr_stksegment(&s); + return s.ss_sp; // Note that this is not the sp pointer (which would refer to the a location low in the stack address space). When returned by thr_stksegment(), ss_sp refers to the top (base) of the stack. + + #elif defined(EA_PLATFORM_CYGWIN) + // Cygwin reserves pthread_attr_getstackaddr and pthread_attr_getstacksize for future use. + // The solution here is probably to use the Windows implementation of this here. + return 0; + + #else // Other Unix + void* stackLow = NULL; + size_t stackSize = 0; + pthread_t threadId = pthread_self(); + + pthread_attr_t sattr; + pthread_attr_init(&sattr); + + #if defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_CONSOLE_BSD) || defined(EA_PLATFORM_FREEBSD) + pthread_attr_get_np(threadId, &sattr); + #elif defined(EA_HAVE_pthread_getattr_np_DECL) + // Note: this function is non-portable; various Unix systems may have different np alternatives + pthread_getattr_np(threadId, &sattr); + #else + EA_UNUSED(threadId); + // What to do? + #endif + + // See http://www.opengroup.org/onlinepubs/009695399/functions/pthread_attr_getstack.html + // stackLow is a constant. It is not the current low location but rather is the lowest allowed location. + + pthread_attr_getstack(&sattr, &stackLow, &stackSize); + pthread_attr_destroy(&sattr); + + return (char*)stackLow + stackSize; + #endif + } + + + void EA::Thread::SetThreadProcessor(int nProcessor) + { + // Posix threading doesn't have the ability to set the processor. + #if defined(EA_PLATFORM_WINDOWS) + DWORD dwThreadAffinityMask; + + if((nProcessor < 0) || (nProcessor >= EA::Thread::GetProcessorCount())) + dwThreadAffinityMask = 0xffffffff; + else + dwThreadAffinityMask = 1 << nProcessor; + + SetThreadAffinityMask(GetCurrentThread(), dwThreadAffinityMask); + + #elif (defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_ANDROID)) || defined(EA_PLATFORM_NX) + cpu_set_t cpus; + CPU_ZERO(&cpus); + + if (nProcessor >= 0) + { + // Ignore for processors we can't run on. + if ((EA::Thread::GetAvailableCpuAffinityMask() & (1 << nProcessor)) == 0) + { + EAT_FAIL_MSG("Requested processor is not available!"); + return; + } + + CPU_SET(nProcessor, &cpus); + } + else + { + for (int c = 0; c < EA::Thread::GetProcessorCount(); c++) + { + // Skip over processors that are not available. + if (((1 << c) & EA::Thread::GetAvailableCpuAffinityMask()) == 0) + continue; + + CPU_SET(c, &cpus); + } + } + + int result = pthread_setaffinity_np(pthread_self(), sizeof(cpus), &cpus); + EAT_ASSERT_FORMATTED(result == 0, "pthread_setaffinity_np: error %x %x", result, errno); + EA_UNUSED(result); + + #else + // Other Unix platforms don't provide a means to specify what processor a thread runs on. + // You have no choice but to let the OS schedule threads for you. + EA_UNUSED(nProcessor); + #endif + } + + + #if defined(EA_PLATFORM_WINDOWS) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) + int GetCurrentProcessorNumberXP() + { + _asm { mov eax, 1 } + _asm { cpuid } + _asm { shr ebx, 24 } + _asm { mov eax, ebx } + } + #endif + + + int EA::Thread::GetThreadProcessor() + { + #if defined(EA_PLATFORM_WINDOWS) + // We are using Posix threading on Windows. It happens to be mapped to Windows threading and + // so we can use Windows facilities to tell what processor the thread is running on. + // Only Windows Vista and later provides GetCurrentProcessorNumber. + // So we must dynamically link to this function. + static EA_THREAD_LOCAL bool bInitialized = false; + static EA_THREAD_LOCAL DWORD (WINAPI *pfnGetCurrentProcessorNumber)() = NULL; + + if(!bInitialized) + { + HMODULE hKernel32 = GetModuleHandle("KERNEL32.DLL"); + if(hKernel32) + pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)())GetProcAddress(hKernel32, "GetCurrentProcessorNumber"); + bInitialized = true; + } + + if(pfnGetCurrentProcessorNumber) + return (int)(unsigned)pfnGetCurrentProcessorNumber(); + + #if defined(EA_PLATFORM_WINDOWS) && defined(EA_PROCESSOR_X86) && defined(EA_COMPILER_MSVC) && (EA_COMPILER_VERSION >= 1400) + return GetCurrentProcessorNumberXP(); + #else + return 0; + #endif + + #elif defined(EA_PLATFORM_ANDROID) + // return zero until Google provides a alternative to smp_processor_id() + return 0; + + #elif EA_VALGRIND_ENABLED + // Valgrind does not support the sched_getcpu() vsyscall. It causes it to detect a segfault in the program and stop it. + // https://bugs.kde.org/show_bug.cgi?id=187043 + // http://git.dorsal.polymtl.ca/?p=ust.git;a=commitdiff_plain;h=8f09cb9340387a52b483752c5d2d6c36035b26bc + return 0; + + #elif (defined(EA_PLATFORM_LINUX) && (defined(EATHREAD_GLIBC_VERSION) && (EATHREAD_GLIBC_VERSION > 2005))) + // http://www.kernel.org/doc/man-pages/online/pages/man3/sched_getcpu.3.html + // http://www.kernel.org/doc/man-pages/online/pages/man2/getcpu.2.html + // Another solution is to use the cpuid instruction like we do for Windows. + int cpu = sched_getcpu(); + if(cpu < 0) + cpu = 0; + + if(cpu >= 0) + return cpu; + + // Ideally we would never need to execute the following code: + cpu_set_t cpus; + CPU_ZERO(&cpus); + pthread_getaffinity_np(pthread_self(), sizeof(cpus), &cpus); + + for(int i = 0; i < CPU_SETSIZE; i++) + { + if(CPU_ISSET(i, &cpus)) + return i; + } + + return 0; + #elif EA_PLATFORM_NX + cpu_set_t cpus; + CPU_ZERO(&cpus); + pthread_getaffinity_np(pthread_self(), sizeof(cpus), &cpus); + for(int i = 0; i < CPU_SETSIZE; i++) + { + if(CPU_ISSET(i, &cpus)) + return i; + } + return 0; + #else + return 0; + #endif + } + +#if defined(EA_PLATFORM_APPLE) + #include + #include + #define SYSCTL_CORE_COUNT "machdep.cpu.core_count" + + typedef struct cpu_set + { + uint32_t count; + } cpu_set_t; + + static inline void CPU_ZERO(cpu_set_t* cs) { cs->count = 0; } + static inline void CPU_SET(int num, cpu_set_t* cs) { cs->count |= (1 << num); } + static inline int CPU_ISSET(int num, cpu_set_t* cs) { return (cs->count & (1 << num)); } + + int pthread_setaffinity_np(pthread_t thread, size_t cpu_size, cpu_set_t* cpu_set) + { + thread_port_t mach_thread; + int core = 0; + + for (core = 0; core < 8 * cpu_size; core++) + { + if (CPU_ISSET(core, cpu_set)) + break; + } + + thread_affinity_policy_data_t policy = {core}; + mach_thread = pthread_mach_thread_np(thread); + thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, 1); + + return 0; + } +#endif + +#if defined(EA_PLATFORM_ANDROID) && __ANDROID_API__ <= 19 + typedef struct cpu_set + { + uint32_t count; + } cpu_set_t; + + static inline void CPU_ZERO(cpu_set_t* cs) { cs->count = 0; } + static inline void CPU_SET(int num, cpu_set_t* cs) { cs->count |= (1 << num); } + static inline int CPU_ISSET(int num, cpu_set_t* cs) { return (cs->count & (1 << num)); } +#endif + + EATHREADLIB_API void EA::Thread::SetThreadAffinityMask(const EA::Thread::ThreadId& id, ThreadAffinityMask nAffinityMask) + { + // Replace kThreadAffinityMaskAny, with AvailableCpuAffinityMask. + if (nAffinityMask == kThreadAffinityMaskAny) + nAffinityMask = EA::Thread::GetAvailableCpuAffinityMask(); + + ThreadAffinityMask sanitizedMask = nAffinityMask & EA::Thread::GetAvailableCpuAffinityMask(); + EAT_ASSERT_MSG(sanitizedMask != 0, "None of the requested processors are available!"); + + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + pTDD->mnThreadAffinityMask = sanitizedMask; + + #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED + cpu_set_t cpuSetMask; + memset(&cpuSetMask, 0, sizeof(cpu_set_t)); + + for (int c = 0; c < EA::Thread::GetProcessorCount(); c++, sanitizedMask >>= 1) + { + if (sanitizedMask & 1) + { + CPU_SET(c, &cpuSetMask); + } + } + + #if defined(EA_PLATFORM_NX) || defined(EA_PLATFORM_APPLE) + int result = pthread_setaffinity_np(pTDD->mThreadId, sizeof(cpu_set_t), &cpuSetMask); + EAT_ASSERT_FORMATTED(result == 0, "pthread_setaffinity_np: error %x %x", result, errno); + EA_UNUSED(result); + #elif defined(EA_PLATFORM_ANDROID) && __ANDROID_API__ <= 19 + if(pTDD->mThreadPid != 0) + { + syscall(__NR_sched_setaffinity, pTDD->mThreadPid, sizeof(nAffinityMask), &nAffinityMask); + } + #else + sched_setaffinity(pTDD->mThreadPid, sizeof(cpu_set_t), &cpuSetMask); + #endif + #endif + } + } + + + EATHREADLIB_API EA::Thread::ThreadAffinityMask EA::Thread::GetThreadAffinityMask(const EA::Thread::ThreadId& id) + { + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + return pTDD->mnThreadAffinityMask; + } + + return kThreadAffinityMaskAny; + } + + // Internal SetThreadName API's so we don't repeat the implementations + namespace Internal + { + // This function is not currently used if the thread name can be set from any other thread + #if !EATHREAD_OTHER_THREAD_NAMING_SUPPORTED + + void SetCurrentThreadName(const char8_t* pName) + { + #if defined(EA_PLATFORM_LINUX) + // http://manpages.courier-mta.org/htmlman2/prctl.2.html + // The Linux documentation says PR_SET_NAME sets the process name, but that + // documentation is wrong and instead it sets the current thread name. + + // Also: http://0pointer.de/blog/projects/name-your-threads.html + // Stefan Kost recently pointed me to the fact that the Linux system call prctl(PR_SET_NAME) + // does not in fact change the process name, but the task name (comm field) -- in contrast + // to what the man page suggests. That makes it very useful for naming threads, since you + // can read back the name you set with PR_SET_NAME earlier from the /proc file system + // (/proc/$PID/task/$TID/comm on newer kernels, /proc/$PID/task/$TID/stat's second field + // on older kernels), and hence distinguish which thread might be responsible for the high + // CPU load or similar problems. + char8_t nameBuf[16]; // Limited to 16 bytes, null terminated if < 16 bytes + strncpy(nameBuf, pName, sizeof(nameBuf)); + nameBuf[15] = 0; + prctl(PR_SET_NAME, (unsigned long)nameBuf, 0, 0, 0); + + #elif defined(EA_PLATFORM_APPLE) + // http://src.chromium.org/viewvc/chrome/trunk/src/base/platform_thread_mac.mm?revision=49465&view=markup&pathrev=49465 + // "There's a non-portable function for doing this: pthread_setname_np. + // It's supported by OS X >= 10.6 and the Xcode debugger will show the thread + // names if they're provided." + // On OSX the return value is always -1 on error; use errno to tell the error value. + typedef int (*pthread_setname_np_type)(const char*); + pthread_setname_np_type pthread_setname_np_ptr = (pthread_setname_np_type)(uintptr_t)dlsym(RTLD_DEFAULT, "pthread_setname_np"); + + if(pthread_setname_np_ptr) + { + // Mac OS X does not expose the length limit of the name, so hardcode it. + char8_t nameBuf[63]; // It is not clear what the size limit actually is, though 63 is known to work because it was seen on the Internet. + strncpy(nameBuf, pName, sizeof(nameBuf)); + nameBuf[62] = 0; + pthread_setname_np_ptr(nameBuf); + } + + #elif defined(EA_PLATFORM_BSD) || defined(EA_PLATFORM_CONSOLE_BSD) || defined(EA_PLATFORM_FREEBSD) + // http://www.unix.com/man-page/freebsd/3/PTHREAD_SET_NAME_NP/ + pthread_set_name_np(pthread_self(), pName); + #elif defined(EA_PLATFORM_NX) + // http://www.unix.com/man-page/freebsd/3/PTHREAD_SET_NAME_NP/ + pthread_setname_np(pthread_self(), pName); + #endif + } + + #endif + + EA::Thread::ThreadId GetId(EAThreadDynamicData* pTDD) + { + if(pTDD) + return pTDD->mThreadId; + + return EA::Thread::kThreadIdInvalid; + } + + void SetThreadName(EAThreadDynamicData* pTDD) + { + #if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_APPLE) + EAT_COMPILETIME_ASSERT(EATHREAD_OTHER_THREAD_NAMING_SUPPORTED == 0); + // http://stackoverflow.com/questions/2369738/can-i-set-the-name-of-a-thread-in-pthreads-linux + // Under some Unixes you can name only the current thread, so we apply the naming + // only if the currently executing thread is the one that is associated with + // this class object. + if(GetId(pTDD) == EA::Thread::GetThreadId()) + SetCurrentThreadName(pTDD->mName); + + #elif defined(EA_PLATFORM_BSD) + EAT_COMPILETIME_ASSERT(EATHREAD_OTHER_THREAD_NAMING_SUPPORTED == 1); + // http://www.unix.com/man-page/freebsd/3/PTHREAD_SET_NAME_NP/ + if(GetId(pTDD) != EA::Thread::kThreadIdInvalid) + pthread_set_name_np(GetId(pTDD), pTDD->mName); + + #elif defined(EA_PLATFORM_NX) + if (GetId(pTDD) != EA::Thread::kThreadIdInvalid) + pthread_setname_np(GetId(pTDD), pTDD->mName); + + #endif + } + } // namespace Internal + + + + EATHREADLIB_API void EA::Thread::SetThreadName(const char* pName) { SetThreadName(GetThreadId(), pName); } + EATHREADLIB_API const char* EA::Thread::GetThreadName() { return GetThreadName(GetThreadId()); } + + EATHREADLIB_API void EA::Thread::SetThreadName(const EA::Thread::ThreadId& id, const char* pName) + { + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + if(pTDD) + { + if(pTDD->mName != pName) // self-assignment check + { + strncpy(pTDD->mName, pName, EATHREAD_NAME_SIZE); + pTDD->mName[EATHREAD_NAME_SIZE - 1] = 0; + } + + Internal::SetThreadName(pTDD); + } + } + + EATHREADLIB_API const char* EA::Thread::GetThreadName(const EA::Thread::ThreadId& id) + { + EAThreadDynamicData* const pTDD = FindThreadDynamicData(id); + return pTDD ? pTDD->mName : ""; + } + + + int EA::Thread::GetProcessorCount() + { + #if defined(EA_PLATFORM_WINDOWS) + static int nProcessorCount = 0; // This doesn't really need to be an atomic integer. + + if(nProcessorCount == 0) + { + // A better function to use would possibly be KeQueryActiveProcessorCount + // (NTKERNELAPI ULONG KeQueryActiveProcessorCount(PKAFFINITY ActiveProcessors)) + + SYSTEM_INFO systemInfo; + memset(&systemInfo, 0, sizeof(systemInfo)); + GetSystemInfo(&systemInfo); + nProcessorCount = (int)systemInfo.dwNumberOfProcessors; + } + + return nProcessorCount; + + #elif defined(EA_PLATFORM_OSX) || defined(EA_PLATFORM_BSD) + // http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/sysctlbyname.3.html + // We can use: + // int sysctl(int* name, u_int namelen, void* oldp, size_t* oldlenp, void* newp, size_t newlen); + // int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen); + + #ifdef EA_PLATFORM_BSD + int mib[4] = { CTL_HW, HW_NCPU, 0, 0 }; + #else + int mib[4] = { CTL_HW, HW_AVAILCPU, 0, 0 }; + #endif + int cpuCount = 0; // Unfortunately, Apple's documentation fails to clarify if this needs to be 'int' or 'long'. + size_t len = sizeof(cpuCount); + + sysctl(mib, 2, &cpuCount, &len, NULL, 0); + + if(cpuCount < 1) + { + mib[1] = HW_NCPU; + sysctl(mib, 2, &cpuCount, &len, NULL, 0); + + if(cpuCount < 1) + cpuCount = 1; + } + + return cpuCount; + + // Maybe simpler, should try it out to make sure it works: + // + // int cpuCount = 0; + // size_t len = sizeof(cpuCount); + // if(sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0) != 0) + // cpuCount = 1; + // return cpuCount; + #elif defined(EA_PLATFORM_NX) + return 3; + #elif defined(EA_PLATFORM_ANDROID) + return android_getCpuCount(); + #elif defined(EA_PLATFORM_STADIA) + return getAvailableCpuCount(); + #else + // Posix doesn't provide a means to get this information. + // Some Unixes provide sysconf() with the _SC_NPROCESSORS_ONLN or _SC_NPROCESSORS_CONF option. + // Another option is to count the number of entries in /proc/cpuinfo + #ifdef _SC_NPROCESSORS_ONLN + return (int)sysconf(_SC_NPROCESSORS_ONLN); + #else + return 1; + #endif + #endif + } + + #if defined(EA_PLATFORM_WINDOWS) + extern "C" __declspec(dllimport) void __stdcall Sleep(unsigned long dwMilliseconds); + #endif + + void EA::Thread::ThreadSleep(const ThreadTime& timeRelative) + { + #if defined(EA_PLATFORM_WINDOWS) + // There is no nanosleep on Windows, but there is Sleep. + if(timeRelative == kTimeoutImmediate) + Sleep(0); + else + Sleep((unsigned)((timeRelative.tv_sec * 1000) + (((timeRelative.tv_nsec % 1000) * 1000000)))); + #else + if(timeRelative == kTimeoutImmediate) + { + sched_yield(); + } + else + { + #if defined(EA_HAVE_nanosleep_DECL) + nanosleep(&timeRelative, 0); + #else + // What to do? + #endif + } + #endif + } + + + namespace EA + { + namespace Thread + { + EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId); + } + } + + void EA::Thread::ThreadEnd(intptr_t threadReturnValue) + { + EAThreadDynamicData* const pTDD = FindThreadDynamicData(GetThreadId()); + + if(pTDD) + { + pTDD->mnStatus = Thread::kStatusEnded; + pTDD->mnReturnValue = threadReturnValue; + pTDD->mRunMutex.Unlock(); + pTDD->Release(); + } + + pthread_exit((void*)threadReturnValue); + } + + + #if defined(EA_PLATFORM_APPLE) + EA::Thread::SysThreadId EA::Thread::GetSysThreadId(ThreadId id) + { + return pthread_mach_thread_np(id); + } + + EA::Thread::SysThreadId EA::Thread::GetSysThreadId() + { + return pthread_mach_thread_np(pthread_self()); // There isn't a self-specific version of pthread_mach_thread_np. + } + #endif + + + EA::Thread::ThreadTime EA::Thread::GetThreadTime() + { + #if defined(EA_PLATFORM_WINDOWS) && !defined(EA_PLATFORM_CYGWIN) + // We use this code instead of GetTickCount or similar because pthreads under + // Win32 uses the 'system file time' definition (e.g. GetSystemTimeAsFileTime()) + // for current time. The implementation here is just like that in the + // pthreads-Win32 ptw32_timespec.c file. + int64_t ft; + ThreadTime threadTime; + GetSystemTimeAsFileTime((FILETIME*)&ft); // nTime64 is in intervals of 100ns. + #define PTW32_TIMESPEC_TO_FILETIME_OFFSET (((int64_t)27111902 << 32) + (int64_t)3577643008) + threadTime.tv_sec = (int)((ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET) / 10000000); + threadTime.tv_nsec = (int)((ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET - ((int64_t)threadTime.tv_sec * (int64_t)10000000)) * 100); + return threadTime; + + // Alternative which will likely be slower: + //#include + //ThreadTime threadTime; + //_timeb fTime; _ftime(&fTime); + //threadTime.tv_sec = (long)fTime.time; + //threadTime.tv_nsec = fTime.millitm * 1000000; + //return threadTime; + #else + // For some systems we may need to use gettimeofday() instead of clock_gettime(). + #if defined(EA_PLATFORM_LINUX) || defined(EA_PLATFORM_CYGWIN) || (_POSIX_TIMERS > 0) + ThreadTime threadTime; + clock_gettime(CLOCK_REALTIME, &threadTime); // If you get a linker error about clock_getttime, you need to link librt.a (specify -lrt to the linker). + return threadTime; + #else + timeval temp; + gettimeofday(&temp, NULL); + return ThreadTime((ThreadTime::seconds_t)temp.tv_sec, (ThreadTime::nseconds_t)temp.tv_usec * 1000); + #endif + #endif + } + + + void EA::Thread::SetAssertionFailureFunction(EA::Thread::AssertionFailureFunction pAssertionFailureFunction, void* pContext) + { + gpAssertionFailureFunction = pAssertionFailureFunction; + gpAssertionFailureContext = pContext; + } + + + void EA::Thread::AssertionFailure(const char* pExpression) + { + if(gpAssertionFailureFunction) + gpAssertionFailureFunction(pExpression, gpAssertionFailureContext); + else + { + #if EAT_ASSERT_ENABLED + #ifdef EA_PLATFORM_WINDOWS + OutputDebugStringA("EA::Thread::AssertionFailure: "); + OutputDebugStringA(pExpression); + OutputDebugStringA("\n"); + #else + printf("EA::Thread::AssertionFailure: "); + printf("%s", pExpression); + printf("\n"); + fflush(stdout); + fflush(stderr); + #endif + + EATHREAD_DEBUG_BREAK(); + #endif + } + } + + +#endif // defined(EA_PLATFORM_UNIX) || EA_POSIX_THREADS_AVAILABLE + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/source/version.cpp b/r5dev/thirdparty/ea/EAThread/source/version.cpp new file mode 100644 index 00000000..eb61cce6 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/source/version.cpp @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "eathread/version.h" + +namespace EA +{ + namespace Thread + { + const Version gVersion = + { + EATHREAD_VERSION_MAJOR, + EATHREAD_VERSION_MINOR, + EATHREAD_VERSION_PATCH + }; + + const Version* GetVersion() + { + return &gVersion; + } + + bool CheckVersion(int majorVersion, int minorVersion, int patchVersion) + { + return (majorVersion == EATHREAD_VERSION_MAJOR) && + (minorVersion == EATHREAD_VERSION_MINOR) && + (patchVersion == EATHREAD_VERSION_PATCH); + } + } +} + + diff --git a/r5dev/thirdparty/ea/EAThread/test/dllsafety/source/TestDllSafety.cpp b/r5dev/thirdparty/ea/EAThread/test/dllsafety/source/TestDllSafety.cpp new file mode 100644 index 00000000..b1ef3dda --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/dllsafety/source/TestDllSafety.cpp @@ -0,0 +1,143 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/////////////////////////////////////////////////////////////////////////////// +// operator new +// EASTL requires the following new operators to be defined. +// +void* operator new[](size_t size, const char*, int, unsigned, const char*, int) +{ + return new char[size]; +} + +void* operator new[](size_t size, size_t, size_t, const char*, int, unsigned, const char*, int) +{ + return new char[size]; +} + +const int NUM_THREADS = 5; + +/////////////////////////////////////////////////////////////////////////////// +// Structure where a single instance is passed to every active thread. +// +struct GlobalData +{ + EA::Thread::AtomicInt32 i; + GlobalData() + : i(0) + {} +}; + +typedef void (*DLL_ENTRY)(GlobalData*); + +/////////////////////////////////////////////////////////////////////////////// +// Thread Entry +// +intptr_t ThreadFunction(void* pData) +{ + GlobalData* const pGlobalData = static_cast(pData); + while(!pGlobalData->i) + { + EA::Thread::ThreadSleep(50); + } + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// The function export below MUST be here as it forces lib files to be +// generated for the DLL's being built. +// +extern "C" +EA_EXPORT void ForceLibFilesToBeGenerated(GlobalData* pGlobal) +{ + using namespace EA::Thread; + using namespace EA::UnitTest; + + // Start two threads in this DLL instance + Thread thread; + for(size_t i = 0; i < NUM_THREADS; i++) + thread.Begin(ThreadFunction, pGlobal); + + // Get the number of threads in the system + ThreadEnumData enumData[32]; + size_t count = EnumerateThreads(enumData, EAArrayCount(enumData)); + //Report("Number of DLL threads detected: %d\n", count); + + for(size_t i = 0; i < count; i++) + enumData[i].Release(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Main +// +int EAMain(int argc, char** argv) +{ + using namespace EA::Thread; + using namespace EA::UnitTest; + + int nErrorCount = 0; + GlobalData data; + +// TODO: DLL usage must be made portable through support at various levels of our technology stack. +#if defined(EA_PLATFORM_MICROSOFT) && defined(EA_PLATFORM_WIN32) + HINSTANCE__* mod1 = LoadLibrary("EAThreadTestDllSafetyMod1.dll"); + HINSTANCE__* mod2 = LoadLibrary("EAThreadTestDllSafetyMod2.dll"); + DLL_ENTRY dllmain1 = (DLL_ENTRY)GetProcAddress(mod1, "ForceLibFilesToBeGenerated"); + DLL_ENTRY dllmain2 = (DLL_ENTRY)GetProcAddress(mod2, "ForceLibFilesToBeGenerated"); + + if(dllmain1 != NULL) + dllmain1(&data); + + if(dllmain2 != NULL) + dllmain2(&data); +#endif + + EA::EAMain::PlatformStartup(); + { + // Start n threads in this DLL instance + Thread thread; + for(size_t i = 0; i < NUM_THREADS; i++) + thread.Begin(ThreadFunction, &data); + + // Get the number of threads in the system and validate. + ThreadEnumData enumData[32]; + size_t count = EnumerateThreads(enumData, EAArrayCount(enumData)); + Report("Number of threads detected: %d/%d. \n", count, NUM_THREADS*3); + EATEST_VERIFY_MSG(count >= NUM_THREADS, "Thread tracking data isn't DLL safe. We are missing data generated in other DLL's."); + + for(size_t i = 0; i < count; i++) + enumData[i].Release(); + + // Release the threads + data.i++; + } + EA::EAMain::PlatformShutdown(nErrorCount); + +#if defined(EA_PLATFORM_MICROSOFT) && defined(EA_PLATFORM_WIN32) + FreeLibrary(mod1); + FreeLibrary(mod2); +#endif + + return nErrorCount; +} + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocess.cpp b/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocess.cpp new file mode 100644 index 00000000..037c3495 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocess.cpp @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThreadInterprocess.h" +#include +#include +#include +#include +#include +#include +#include + +#include + + +/////////////////////////////////////////////////////////////////////////////// +// Globals +// +unsigned int gTestThreadCount = 4; +unsigned int gTestLengthSeconds = 10; + + +/////////////////////////////////////////////////////////////////////////////// +// EAThreadFailure +// +// This is called by EAThread's assert failure function. +// +static void EAThreadFailure(const char* pMessage, void* /*pContext*/) +{ + EA::UnitTest::Report("Thread test failure (EAThread assert): %s\n", pMessage); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// operator new +// EASTL requires the following new operators to be defined. +// +void* operator new[](size_t size, const char*, int, unsigned, const char*, int) +{ + return new char[size]; +} + +void* operator new[](size_t size, size_t, size_t, const char*, int, unsigned, const char*, int) +{ + return new char[size]; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// main +// +int EAMain(int argc, char** argv) +{ + using namespace EA::Thread; + using namespace EA::UnitTest; + using namespace EA::StdC; + + int nErrorCount = 0; + bool bDebugMode = false; + + EA::EAMain::PlatformStartup(); + + // Process possible command line parameters + for(int i(0); i < argc; i++) + { + // Look for -? + if(Stricmp(argv[i], "-?") == 0) + { + printf("Command line parameters:\n"); + printf(" -? Get Help.\n"); + printf(" -t Run the tests for seconds each.\n"); + printf(" -c Specifies test thread count.\n"); + printf(" -d Debug mode. Causes app to wait for a debugger to connect.\n"); + continue; + } + + // Run the tests for seconds each. + if((Stricmp(argv[i], "-t") == 0) && (i < (argc - 1))) + { + gTestLengthSeconds = (unsigned int) atoi(argv[i+1]); + if(gTestLengthSeconds < 3) + gTestLengthSeconds = 3; + continue; + } + + // Specifies test thread count. e.g. -c 10 + if((Stricmp(argv[i], "-c") == 0) && (i < (argc - 1))) + { + gTestThreadCount = (unsigned int) atoi(argv[i+1]); + if(gTestThreadCount < 1) + gTestThreadCount = 1; + if(gTestThreadCount > 100) + gTestThreadCount = 100; + continue; + } + + // Debug mode. Causes app to wait for a debugger to connect. + if(Stricmp(argv[i], "-d") == 0) + { + bDebugMode = true; + continue; + } + } + + + // Set EAThread to route its errors to our own error reporting function. + EA::Thread::SetAssertionFailureFunction(EAThreadFailure, NULL); + + ReportVerbosity(1, "Test time seconds: %u\n", gTestLengthSeconds); + ReportVerbosity(1, "Thread count: %u\n", gTestThreadCount); + + // Print ThreadId for this primary thread. + const ThreadId threadId = GetThreadId(); + ReportVerbosity(1, "Primary thread ThreadId: %08x\n", (int)(intptr_t)threadId); + + // Print SysThreadId for this primary thread. + const SysThreadId sysThreadId = GetSysThreadId(threadId); + ReportVerbosity(1, "Primary thread SysThreadId: %08d\n", (int)(intptr_t)sysThreadId); + + // Print thread priority for this primary thread. + const int nPriority = EA::Thread::GetThreadPriority(); + ReportVerbosity(1, "Primary thread priority: %d\n", nPriority); + + const int nProcessorCount = EA::Thread::GetProcessorCount(); + ReportVerbosity(1, "Currently active virtual processor count: %d\n", nProcessorCount); + + if(bDebugMode) + { + Report("Debug mode activated. Waiting for debugger to attach.\n"); + while(bDebugMode) + ThreadSleepRandom(500, 500, true); + Report("Continuing.\n"); + } + + // Add the tests + TestApplication testSuite("EAThread Interprocess Unit Tests", argc, argv); + + testSuite.AddTest("RWMutex", TestThreadRWMutex); + + nErrorCount += testSuite.Run(); + + EA::EAMain::PlatformShutdown(nErrorCount); + + return nErrorCount; +} + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocess.h b/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocess.h new file mode 100644 index 00000000..9a4a01c6 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocess.h @@ -0,0 +1,17 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TESTTHREADINTERPROCESS_H +#define TESTTHREADINTERPROCESS_H + + +extern unsigned int gTestThreadCount; +extern unsigned int gTestLengthSeconds; + + +// Individual test functions +int TestThreadRWMutex(); + + +#endif // Header include guard diff --git a/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocessRWMutex.cpp b/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocessRWMutex.cpp new file mode 100644 index 00000000..62e01b2a --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/interprocess_test/source/TestThreadInterprocessRWMutex.cpp @@ -0,0 +1,259 @@ +/////////////////////////////////////////////////////////////////////////////// +// TestThreadInterprocessRWMutex.cpp +// +// Copyright (c) 2009, Electronic Arts Inc. All rights reserved. +// Created by Paul Pedriana +/////////////////////////////////////////////////////////////////////////////// + + +#include +#include +#include +#include +#include +#include "TestThreadInterprocess.h" +#include + + +struct RWMWorkDataInterProcess +{ + volatile int mnExpectedValue; // Intentionally not an atomic variable. + volatile int mnCalculatedValue; // Intentionally not an atomic variable. + volatile int mnWriteLockCount; // How many times the write lock was owned, across all processes. + + RWMWorkDataInterProcess() + : mnExpectedValue(0), + mnCalculatedValue(0), + mnWriteLockCount(0) + { + printf("RWMWorkDataInterProcess\n"); + } + + ~RWMWorkDataInterProcess() + { + printf("~RWMWorkDataInterProcess\n"); + } +}; + + +struct RWMWorkDataInterThread +{ + volatile bool mbShouldQuit; // + EA::Thread::RWMutexIP mRWMutexIP; // + EA::Thread::AtomicInt32 mnThreadIndex; // + EA::Thread::AtomicInt32 mnErrorCount; // + EA::Thread::AtomicInt32 mnReadLockCount; // How many times the read lock was owned, within this process. + EA::Thread::AtomicInt32 mnWriteLockCount; // How many times the write lock was owned, within this process. + + RWMWorkDataInterThread() + : mbShouldQuit(false), + mRWMutexIP(NULL, false), + mnThreadIndex(0), + mnErrorCount(0), + mnReadLockCount(0), + mnWriteLockCount(0) + { + } + +protected: + RWMWorkDataInterThread(const RWMWorkDataInterThread& rhs); + RWMWorkDataInterThread& operator=(const RWMWorkDataInterThread& rhs); +}; + + +static intptr_t RWThreadFunction(void* pvWorkData) +{ + using namespace EA::Thread; + + int nErrorCount = 0; + RWMWorkDataInterThread* pWorkData = (RWMWorkDataInterThread*)pvWorkData; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "RWMutexIP test function created: %08x\n", (int)(intptr_t)threadId); + + // We use the interprocess mutex to control access to an interprocess data struct. + Shared gSharedData("RWMWorkDataIP"); + + // We track the amount of time we spend waiting for Locks. + //ThreadTime nInitialTime, nFinalTime; + const ThreadTime kMaxExpectedTime = 1000; + + while(!pWorkData->mbShouldQuit) + { + const bool bWriteLock((rand() % 10) == 0); // 10% of the time, do a write lock. + + if(bWriteLock) + { + //nInitialTime = EA::Thread::GetThreadTime(); + + int nLockResult = pWorkData->mRWMutexIP.Lock(RWMutexIP::kLockTypeWrite, GetThreadTime() + kMaxExpectedTime); + EATEST_VERIFY_MSG(nLockResult != RWMutexIP::kResultError, "RWMutexIP failure: write lock."); + + //nFinalTime = EA::Thread::GetThreadTime(); + //EATEST_VERIFY_MSG((nFinalTime - nInitialTime) < kMaxExpectedTime, "RWMutexIP failure: write lock slow."); + + if(nLockResult > 0) + { + gSharedData->mnWriteLockCount++; + pWorkData->mnWriteLockCount++; + + // Verify exactly one write lock is set. + nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeWrite); + EATEST_VERIFY_MSG(nLockResult == 1, "RWMutexIP failure: write lock verify 1."); + + // What we do here is spend some time manipulating mnExpectedValue and mnCalculatedValue + // while we have the write lock. We change their values in a predicable way but before + // we are done mnCalculatedValue has been incremented by one and both values are equal. + const uintptr_t x = (uintptr_t)pWorkData; + + gSharedData->mnExpectedValue = -1; + EA::UnitTest::ThreadSleepRandom(10, 20); + gSharedData->mnCalculatedValue *= 50; + EA::UnitTest::ThreadSleepRandom(10, 20); + gSharedData->mnCalculatedValue /= (int)(((x + 1) / x) * 50); // This will always be the same as simply '/= 50'. + EA::UnitTest::ThreadSleepRandom(10, 20); + gSharedData->mnCalculatedValue += 1; + EA::UnitTest::ThreadSleepRandom(10, 20); + gSharedData->mnExpectedValue = gSharedData->mnCalculatedValue; + + // Verify no read locks are set. + nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeRead); + EATEST_VERIFY_MSG(nLockResult == 0, "RWMutexIP failure: write lock verify 2."); + + // Verify exactly one write lock is set. + nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeWrite); + EATEST_VERIFY_MSG(nLockResult == 1, "RWMutexIP failure: write lock verify 3."); + + // Verify there are now zero write locks set. + nLockResult = pWorkData->mRWMutexIP.Unlock(); + EATEST_VERIFY_MSG(nLockResult == 0, "RWMutexIP failure: write unlock."); + + EA::UnitTest::ThreadSleepRandom(40, 80); + } + } + else + { + const int nRecursiveLockCount(rand() % 2); + int i, nLockResult, nLocks = 0; + + for(i = 0; i < nRecursiveLockCount; i++) + { + //nInitialTime = EA::Thread::GetThreadTime(); + + nLockResult = pWorkData->mRWMutexIP.Lock(RWMutexIP::kLockTypeRead, GetThreadTime() + kMaxExpectedTime); + + //nFinalTime = EA::Thread::GetThreadTime(); + //EATEST_VERIFY_MSG(nLockResult != RWMutexIP::kResultError, "RWMutexIP failure: read lock."); + + if(nLockResult > 0) + { + nLocks++; + pWorkData->mnReadLockCount++; + + EA::UnitTest::ReportVerbosity(2, "CValue = %d; EValue = %d\n", gSharedData->mnCalculatedValue, gSharedData->mnExpectedValue); + EATEST_VERIFY_MSG(gSharedData->mnCalculatedValue == gSharedData->mnExpectedValue, "RWMutexIP failure: read lock 2"); + } + } + + while(nLocks > 0) + { + // Verify no write locks are set. + nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeWrite); + EATEST_VERIFY_MSG(nLockResult == 0, "RWMutexIP failure: read lock verify 1."); + + // Verify at least N read locks are set. + nLockResult = pWorkData->mRWMutexIP.GetLockCount(RWMutexIP::kLockTypeRead); + EATEST_VERIFY_MSG(nLockResult >= nLocks, "RWMutexIP failure: read lock verify 2."); + + // Verify there is one less read lock set. + nLockResult = pWorkData->mRWMutexIP.Unlock(); + EATEST_VERIFY_MSG(nLockResult >= nLocks-1, "RWMutexIP failure: read unlock."); + + nLocks--; + } + + EA::UnitTest::ThreadSleepRandom(10, 20); + } + } + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + + +int TestThreadRWMutex() +{ + using namespace EA::Thread; + + int nErrorCount(0); + + EA::UnitTest::Report("Thread Pool Test\n"); + + /* + { // ctor tests + // We test various combinations of RWMutexIP ctor and RWMutexIPParameters. + // RWMutexIPParameters(bool bIntraProcess = true, const char* pName = NULL); + // RWMutexIP(const RWMutexIPParameters* pRWMutexIPParameters = NULL, bool bDefaultParameters = true); + + //RWMutexIPParameters mp1(true, NULL); + //RWMutexIPParameters mp2(true, "mp2"); + //RWMutexIPParameters mp3(false, "mp3"); + RWMutexIPParameters mp4(false, "mp4"); + RWMutexIPParameters mp6(false, "mp6"); + + //RWMutexIP mutex1(&mp1, false); + //RWMutexIP mutex2(&mp2, false); + //RWMutexIP mutex3(&mp3, false); + RWMutexIP mutex4(&mp4, false); + //RWMutexIP mutex5(NULL, true); + RWMutexIP mutex6(NULL, false); + mutex6.Init(&mp6); + + //AutoRWMutexIP am1(mutex1, RWMutexIP::kLockTypeRead); + //AutoRWMutexIP am2(mutex2, RWMutexIP::kLockTypeRead); + //AutoRWMutexIP am3(mutex3, RWMutexIP::kLockTypeRead); + AutoRWMutexIP am4(mutex4, RWMutexIP::kLockTypeRead); + AutoRWMutexIP am6(mutex6, RWMutexIP::kLockTypeRead); + } + */ + + { + RWMWorkDataInterThread workData; + RWMutexIPParameters rwMutexIPParameters(false, "RWMTest"); + + // Set up the RWMWorkData + workData.mRWMutexIP.Init(&rwMutexIPParameters); + + // Create the threads + Thread* pThreadArray = new Thread[gTestThreadCount]; + ThreadId* pThreadIdArray = new ThreadId[gTestThreadCount]; + Thread::Status status; + + for(unsigned i(0); i < gTestThreadCount; i++) + pThreadIdArray[i] = pThreadArray[i].Begin(RWThreadFunction, &workData); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000); + + workData.mbShouldQuit = true; + + for(unsigned i(0); i < gTestThreadCount; i++) + { + if(pThreadIdArray[i] != kThreadIdInvalid) + { + status = pThreadArray[i].WaitForEnd(GetThreadTime() + 30000); + + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWMutexIP/Thread failure: status == kStatusRunning."); + } + } + + delete[] pThreadIdArray; + delete[] pThreadArray; + + nErrorCount += (int)workData.mnErrorCount; + } + + return nErrorCount; +} + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestCpuAvailableAffinityMask.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestCpuAvailableAffinityMask.cpp new file mode 100644 index 00000000..d3865bc5 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestCpuAvailableAffinityMask.cpp @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "TestThread.h" +#include +#include +#include +#include +#include + +#if defined(EA_PLATFORM_STADIA) + EATHREADLIB_API bool isUint(const char* buffer); + EATHREADLIB_API EA::Thread::ThreadAffinityMask parseSet(char const* buffer, size_t len); + EATHREADLIB_API bool readLine(const char* fileName, char* buffer, size_t len); + EATHREADLIB_API bool getProcessCpuSetCGroup(char* cgroup, size_t len); + EATHREADLIB_API EA::Thread::ThreadAffinityMask getInstanceCpuSet(); + EATHREADLIB_API EA::Thread::ThreadAffinityMask initValidCpuAffinityMask(); +#endif + + +int TestCpuAvailableAffinityMaskBasic() +{ + int nErrorCount = 0; + +#if defined(EA_PLATFORM_STADIA) + { // Test isUint() + static const char* isUintTest1 = "1"; + static const char* isUintTest2 = "wrong"; + static const char* isUintTest3 = " 9"; + static const char* isUintTest4 = " "; + static const char* isUintTest5 = ""; + + EATEST_VERIFY(isUint(isUintTest1)); + EATEST_VERIFY(!isUint(isUintTest2)); + EATEST_VERIFY(isUint(isUintTest3)); + EATEST_VERIFY(!isUint(isUintTest4)); + EATEST_VERIFY(!isUint(isUintTest5)); + } + + { // Test parseSet() + char const* parseSetTest1 = "0-6"; + char const* parseSetTest2 = "0, 2-6, 8"; + char const* parseSetTest3 = "wrong"; + + EA::Thread::ThreadAffinityMask parseSetVerify1 = 0x7f; + EA::Thread::ThreadAffinityMask parseSetVerify2 = 0x17d; + EA::Thread::ThreadAffinityMask parseSetVerify3 = 0; + + EA::Thread::ThreadAffinityMask parseSetResult1 = parseSet(parseSetTest1, EA::StdC::Strlen(parseSetTest1)); + EA::Thread::ThreadAffinityMask parseSetResult2 = parseSet(parseSetTest2, EA::StdC::Strlen(parseSetTest2)); + EA::Thread::ThreadAffinityMask parseSetResult3 = parseSet(parseSetTest3, EA::StdC::Strlen(parseSetTest3)); + EATEST_VERIFY(parseSetVerify1 == parseSetResult1); + EATEST_VERIFY(parseSetVerify2 == parseSetResult2); + EATEST_VERIFY(parseSetVerify3 == parseSetResult3); + } + + { // Test getProcessCpuSetCGroup() + EATEST_VERIFY(!getProcessCpuSetCGroup(nullptr, 0)); + + char cgroup[40]; + EATEST_VERIFY(getProcessCpuSetCGroup(cgroup, sizeof(cgroup))); + EATEST_VERIFY(EA::StdC::Strlen(cgroup) != 0); + } + + { // Test getInstanceCpuSet() + EA::Thread::ThreadAffinityMask instanceCpuSet = getInstanceCpuSet(); + EATEST_VERIFY(instanceCpuSet != 0); + } + + { // Test initValidCpuAffinityMask() + EA::Thread::ThreadAffinityMask cpuAffinityMask = initValidCpuAffinityMask(); + EATEST_VERIFY(cpuAffinityMask != 0); + } +#endif + + return nErrorCount; +} + + +int TestCpuAvailableAffinityMask() +{ + int nErrorCount = 0; + + nErrorCount += TestCpuAvailableAffinityMaskBasic(); + + { // Test GetAvailableCpuAffinityMask() + EA::Thread::ThreadAffinityMask availableCpuAffinityMask = EA::Thread::GetAvailableCpuAffinityMask(); + + EATEST_VERIFY(availableCpuAffinityMask != 0); + EATEST_VERIFY(availableCpuAffinityMask != EA::Thread::kThreadAffinityMaskAny); + } + + return nErrorCount; +} diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestEnumerateThreads.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestEnumerateThreads.cpp new file mode 100644 index 00000000..011cfd0e --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestEnumerateThreads.cpp @@ -0,0 +1,244 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include +#include "TestThread.h" +#include +#include +#include +#include + +EA_DISABLE_ALL_VC_WARNINGS() +#include +EA_RESTORE_ALL_VC_WARNINGS() + +using namespace EA::Thread; +using namespace EA::Thread::detail; +using namespace EA::UnitTest; + +//------------------------------------------------------------------------------- +// Globals +static Semaphore gSemaphore; + +//------------------------------------------------------------------------------- +// +static intptr_t TestFunction1(void*) +{ + // Wait until we are signaled by the unit test to complete. + gSemaphore.Wait(); + return 0; +} + +//------------------------------------------------------------------------------- +// +int TestSimpleEnumerateThreads() +{ + int nErrorCount = 0; + static EA::Thread::AtomicInt snThreadStartCount; + snThreadStartCount = 0; + + auto threadEntry = [](void*) -> intptr_t + { + snThreadStartCount++; + + // Wait until we are signaled by the unit test to complete. + gSemaphore.Wait(); + return 0; + }; + + const size_t kMaxTestThreadEnumCount = 16; + ThreadEnumData enumData[kMaxTestThreadEnumCount]; + + // Prevents all threads from returning. + gSemaphore.Init(0); + + // Startup all the threads we want to monitor. + Thread threads[kMaxTestThreadEnumCount]; + for(size_t i = 0; i < kMaxTestThreadEnumCount; i++) + { + threads[i].Begin(threadEntry); + } + + // Give all the threads a chance to start up. + while(snThreadStartCount != kMaxTestThreadEnumCount) + EA::Thread::ThreadSleep(0); + + // Enumerate the active threads + size_t threadCount = EA::Thread::EnumerateThreads(enumData, EAArrayCount(enumData)); + EATEST_VERIFY_MSG(threadCount >= kMaxTestThreadEnumCount, "Incorrect number of threads reported."); + // Report("Enumerated (at least) %d threads. Found (%d).\n", kMaxTestThreadEnumCount, threadCount); + + for(size_t j = 0; j < kMaxTestThreadEnumCount; j++) + { + //Report("\tThread id: %s\n", ThreadIdToStringBuffer(enumData[j].mpThreadDynamicData->mhThread).c_str()); + if(enumData[j].mpThreadDynamicData == NULL) + continue; + + #if !defined(EA_PLATFORM_NX) // mpStackBase is null because the necessary POSIX API (pthread_getattr_np) isn't implemented by Nintendo + if(strcmp(enumData[j].mpThreadDynamicData->mName, "external") != 0) // Disabled because we can't guarantee across all platforms that a stack base is available. This will be fixed in a future release. + { + EATEST_VERIFY_MSG(enumData[j].mpThreadDynamicData->mpStackBase != NULL, "All thread meta data is expected to have the stack base address."); + } + #endif + enumData[j].Release(); + } + + // Signal the threads to complete. + gSemaphore.Post(kMaxTestThreadEnumCount); + + // Wait for all threads to complete. + for(size_t i = 0; i < kMaxTestThreadEnumCount; i++) + { + if(threads[i].GetStatus() != Thread::kStatusEnded) + threads[i].WaitForEnd(); + } + + return nErrorCount; +} + +//------------------------------------------------------------------------------- +// +int TestSimpleEnumerateThreads_KillThreadsEarly() +{ + int nErrorCount = 0; + + const size_t kMaxTestThreadEnumCount = 16; + ThreadEnumData enumData[kMaxTestThreadEnumCount]; + + // Prevents all threads from returning. + gSemaphore.Init(0); + + // Startup all the threads we want to monitor. + Thread threads[kMaxTestThreadEnumCount]; + for(size_t i = 0; i < kMaxTestThreadEnumCount; i++) + { + threads[i].Begin(TestFunction1); + } + EA::Thread::ThreadSleep(300); // Give all the threads a chance to start up. + + // Enumerate the active threads + size_t threadCount = EA::Thread::EnumerateThreads(enumData, EAArrayCount(enumData)); + EATEST_VERIFY_MSG(threadCount >= kMaxTestThreadEnumCount, "Incorrect number of threads reported."); + // Report("Enumerated (at least) %d threads. Found (%d).\n", kMaxTestThreadEnumCount, threadCount); + + // Signal the threads to complete. + gSemaphore.Post(kMaxTestThreadEnumCount); + EA::Thread::ThreadSleep(500); + + // Terminate the threads before the user explicitly releases them. + for(size_t i = 0; i < kMaxTestThreadEnumCount; i++) + { + if(threads[i].GetStatus() != Thread::kStatusEnded) + threads[i].WaitForEnd(); + } + + for(size_t j = 0; j < kMaxTestThreadEnumCount; j++) + { + //Report("\tThread id: %s\n", ThreadIdToStringBuffer(enumData[j].mpThreadDynamicData->mhThread).c_str()); + enumData[j].Release(); + } + + return nErrorCount; +} + +//------------------------------------------------------------------------------- +// +int TestEnumerateThreads_EnumerateMain() +{ + int nErrorCount = 0; + + const size_t kMaxTestThreadEnumCount = 16; + ThreadEnumData enumData[kMaxTestThreadEnumCount]; + + size_t threadCount = EA::Thread::EnumerateThreads(enumData, EAArrayCount(enumData)); + EATEST_VERIFY_MSG(threadCount >= 1, "No threads found. We are expecting at least the main thread to be reported."); + + int compare_result = strcmp(enumData[0].mpThreadDynamicData->mName, "external"); + EATEST_VERIFY_MSG(compare_result == 0, "Not an externally created thread."); + +#if EA_USE_CPP11_CONCURRENCY + ThreadUniqueId thisThreadId; + EAThreadGetUniqueId(thisThreadId); + + ThreadUniqueId uniqueThreadId = enumData[0].mpThreadDynamicData->mUniqueThreadId; + EATEST_VERIFY_MSG(uniqueThreadId == thisThreadId, "Did not return the threadId of this call context."); +#elif defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE + ThreadId enumThreadId = enumData[0].mpThreadDynamicData->mhThread; // No portable thread id available in the ThreadDynamicDataStructure. + EATEST_VERIFY_MSG(enumThreadId == EA::Thread::GetThreadId(), "Did not return the threadId of this call context."); +#else + ThreadId enumThreadId = enumData[0].mpThreadDynamicData->mThreadId; + EATEST_VERIFY_MSG(enumThreadId == EA::Thread::GetThreadId(), "Did not return the threadId of this call context."); +#endif + + return nErrorCount; +} + +//------------------------------------------------------------------------------- +// +EA_DISABLE_ALL_VC_WARNINGS() +#include +#include +#include +#include +EA_RESTORE_ALL_VC_WARNINGS() +#include + + +// TODO(rparolin): This forces the build-farm to timeout. Re-enable in the future. +// +// int TestHeavyLoadThreadRegisteration() +// { +// int nErrorCount = 0; + +// #ifdef EA_PLATFORM_MICROSOFT +// // Only tested on Windows because its a reported regression in tools/pipeline related +// // technologies leveraging a large number of non-eathread threads which would exhaust internal +// // tracking system. +// std::atomic isDone = false; +// EA::Thread::Mutex s_mutex; + +// { +// int loopCount = 170; // must exceed the value of EA::Thread::kMaxThreadDynamicDataCount. +// std::vector threads; +// while(loopCount--) +// { +// threads.emplace_back(std::thread([&] +// { +// while (!isDone) +// { +// // We lock an EA::Thread::Mutex because it used to force a +// // non-EAThread thread to be registered due to debug functionality +// // requesting a thread id. Verify that locking a mutex no longer requires +// // external thread registration by locking more threads that we can track. +// EA::Thread::AutoMutex _(s_mutex); +// } +// })); +// } + +// std::this_thread::sleep_for(std::chrono::milliseconds(100)); +// isDone = true; + +// for (auto& th : threads) +// th.join(); +// } +// #endif + +// return nErrorCount; +// } + + +//------------------------------------------------------------------------------- +// +int TestEnumerateThreads() +{ + int nErrorCount = 0; + + nErrorCount += TestSimpleEnumerateThreads(); + nErrorCount += TestSimpleEnumerateThreads_KillThreadsEarly(); + nErrorCount += TestEnumerateThreads_EnumerateMain(); + // nErrorCount += TestHeavyLoadThreadRegisteration(); + + return nErrorCount; +} + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThread.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThread.cpp new file mode 100644 index 00000000..661450e6 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThread.cpp @@ -0,0 +1,252 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include "TestThread.h" +#include +#include +#include +#include +#include +#include +#include + +//Prevents false positive memory leaks on GCC/Clang platforms +#if defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG) + #define EA_MEMORY_GCC_USE_FINALIZE +#endif + +#ifndef EA_OPENSOURCE + #include + #include + #include +#endif + + +#ifdef EA_OPENSOURCE +void* operator new[](size_t size, const char* /*pName*/, int /*flags*/, + unsigned /*debugFlags*/, const char* /*file*/, int /*line*/) +{ + return operator new[](size); +} + +void* operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char* /*pName*/, + int /*flags*/, unsigned /*debugFlags*/, const char* /*file*/, int /*line*/) +{ + return operator new[](size); +} +#endif + + +#ifdef EA_DLL + #include +#endif + +#if defined(EA_PLATFORM_MICROSOFT) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif + +#if defined(EA_COMPILER_MSVC) && defined(EA_PLATFORM_MICROSOFT) + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif + +#include +#include +#include + + +/////////////////////////////////////////////////////////////////////////////// +// gTestLengthSeconds +// +unsigned int gTestLengthSeconds = 2; + + + +/////////////////////////////////////////////////////////////////////////////// +// EAThreadFailure +// +// This is called by EAThread's assert failure function. +// +static void EAThreadFailure(const char* pMessage, void* /*pContext*/) +{ + EA::UnitTest::IncrementGlobalErrorCount(1); + EA::UnitTest::Report("Thread test failure (EAThread assert): %s\n", pMessage); +} + + + +/////////////////////////////////////////////////////////////////////////////// +// TestThreadMisc +// +// To do: Move this to its own file. +// +#if defined(EA_PLATFORM_APPLE) + #include +#endif + +#if defined(EA_PLATFORM_POSIX) +#include +#endif + +bool IsSuperUser() +{ +#if defined(EA_PLATFORM_POSIX) && !defined(EA_PLATFORM_SONY) && !defined(EA_PLATFORM_NX) // PS4 is a POSIX machine but doesn't implement 'getuid'. + // http://pubs.opengroup.org/onlinepubs/009695399/functions/geteuid.html + // http://pubs.opengroup.org/onlinepubs/009695399/functions/getuid.html + uid_t uid = getuid(), euid = geteuid(); + return (uid == 0 || euid == 0); +#else + return true; +#endif +} + + + +int TestThreadGetThreadTimeMin() +{ + int nErrorCount(0); +#if defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_PS4) + EATEST_VERIFY_MSG(EA::Thread::GetThreadTime() >= EATHREAD_MIN_ABSOLUTE_TIME, "Reported GetThreadTime absolute time is less than EATHREAD_MIN_ABSOLUTE_TIME. You are going to have a bad time."); +#endif + return nErrorCount; +} + +int TestThreadMisc() +{ + int nErrorCount = 0; + // this is here because its intended to be the first test run. Since it depends on tick count since title start. + nErrorCount += TestThreadGetThreadTimeMin(); + + #if defined(EA_PLATFORM_APPLE) && EATHREAD_APPLE_GETMODULEINFO_ENABLED + if(!IsSuperUser()) + { + EA::EAMain::Report("Skipping GetModuleInfoApple test because we don't have sufficient system privileges.\n"); + return nErrorCount; + } + + EA::Thread::ModuleInfoApple moduleInfoApple[15]; + size_t n = EA::Thread::GetModuleInfoApple(moduleInfoApple, EAArrayCount(moduleInfoApple), NULL, true); + + #if defined(EA_PLATFORM_OSX) + EATEST_VERIFY(n > 0); + for(size_t i = 0; i < eastl::min(n, EAArrayCount(moduleInfoApple)); i++) + EA::UnitTest::Report("%s\n", moduleInfoApple[i].mPath); + #else + EA_UNUSED(n); + #endif + #endif + + return nErrorCount; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// EAMain +// +int EAMain(int argc, char** argv) +{ + using namespace EA::Thread; + using namespace EA::UnitTest; + + int nErrorCount(0); + + EA::EAMain::PlatformStartup(); + + gTestLengthSeconds = (unsigned int)(3 * EA::UnitTest::GetSystemSpeed()); + if(gTestLengthSeconds == 0) + gTestLengthSeconds = 1; + + // Set EAThread to route its errors to our own error reporting function. + EA::Thread::SetAssertionFailureFunction(EAThreadFailure, NULL); + + #if defined(EA_DLL) && defined(EA_MEMORY_ENABLED) && EA_MEMORY_ENABLED + EA::Allocator::InitSharedDllAllocator(); + #endif + + //Set EAThread to use the Default Allocator to keep track of our memory usage +#ifndef EA_OPENSOURCE + EA::Thread::SetAllocator(EA::Allocator::ICoreAllocator::GetDefaultAllocator()); +#endif + + // Print ThreadId for this primary thread. + const ThreadId threadId = GetThreadId(); + ReportVerbosity(1, "Primary thread ThreadId: %s\n", EAThreadThreadIdToString(threadId)); + + // Print SysThreadId for this primary thread. + const SysThreadId sysThreadId = GetSysThreadId(threadId); + ReportVerbosity(1, "Primary thread SysThreadId: %s\n", EAThreadSysThreadIdToString(sysThreadId)); + + // Print thread priority for this primary thread. + const int nPriority = EA::Thread::GetThreadPriority(); + ReportVerbosity(1, "Primary thread priority: %d\n", nPriority); + + const int nProcessorCount = EA::Thread::GetProcessorCount(); + ReportVerbosity(1, "Currently active virtual processor count: %d\n", nProcessorCount); + + #if defined(EA_PLATFORM_MICROSOFT) && !EA_POSIX_THREADS_AVAILABLE && !EA_USE_CPP11_CONCURRENCY + const DWORD dwCurrentThreadId = GetCurrentThreadId(); // This is a system OS call. + EATEST_VERIFY_F(sysThreadId == dwCurrentThreadId, "GetSysThreadId failed. SysThreadId = %u, sys thread id = %u.\n", (unsigned)sysThreadId, (unsigned)dwCurrentThreadId); + #endif + + // Add the tests + TestApplication testSuite("EAThread Unit Tests", argc, argv); + + #if defined(EA_PLATFORM_NX) + // We are enabling round-robin scheduling on the Nintendo NX by setting the main thread to the lowest thread + // priority. The pthread interfaces inherits the main threads priority when creating new threads so this + // ensures all worker threads spawned in tests will be assigned equal time on cores. This is a documented + // feature of the NX thread scheduler. The default behaviour of the scheduler is to not give up their core to + // threads of equal priority which causes live locks in our tests. + // + // Reference: + // https://developer.nintendo.com/html/online-docs/nx-en/g1kr9vj6-en/document.html?doc=Packages/SDK/NintendoSDK/Documents/Package/contents/Pages/Page_83955697.html + EA::Thread::SetThreadPriority(EA::Thread::kThreadPriorityMin); + #endif + + testSuite.AddTest("Atomic", TestThreadAtomic); + testSuite.AddTest("Barrier", TestThreadBarrier); + testSuite.AddTest("Callstack", TestThreadCallstack); + testSuite.AddTest("Condition", TestThreadCondition); + testSuite.AddTest("EnumerateThreads", TestEnumerateThreads); + testSuite.AddTest("Futex", TestThreadFutex); + testSuite.AddTest("Misc", TestThreadMisc); + testSuite.AddTest("Mutex", TestThreadMutex); + testSuite.AddTest("RWMutex", TestThreadRWMutex); + testSuite.AddTest("RWSemaphore", TestThreadRWSemaLock); + testSuite.AddTest("RWSpinLock", TestThreadRWSpinLock); + testSuite.AddTest("Semaphore", TestThreadSemaphore); + testSuite.AddTest("SmartPtr", TestThreadSmartPtr); + testSuite.AddTest("SpinLock", TestThreadSpinLock); + testSuite.AddTest("Storage", TestThreadStorage); + testSuite.AddTest("Sync", TestThreadSync); + testSuite.AddTest("TestCpuAvailableAffinityMask", TestCpuAvailableAffinityMask); + testSuite.AddTest("Thread", TestThreadThread); + testSuite.AddTest("ThreadPool", TestThreadThreadPool); + + nErrorCount += testSuite.Run(); + +#ifndef EA_USE_CPP11_CONCURRENCY + // Verify the converted a EA::Thread::ThreadId to a EA::Thread::SysThreadId id matches this thread context id. + const ThreadId convertedThreadId = GetThreadId(sysThreadId); + EATEST_VERIFY_F(threadId == convertedThreadId , "GetThreadId failed to convert SysThreadId. ThreadId = %s, converted thread id = %s.\n", EAThreadThreadIdToString(threadId), EAThreadThreadIdToString(convertedThreadId)); +#endif + + EA::EAMain::PlatformShutdown(nErrorCount); + + return nErrorCount; +} + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThread.h b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThread.h new file mode 100644 index 00000000..f395aa0f --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThread.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#ifndef TESTTHREAD_H +#define TESTTHREAD_H + + +extern unsigned int gTestLengthSeconds; +extern bool IsSuperUser(); + +// The maximum number of threads spawned during EAThread unit tests. +#ifndef EATHREAD_MAX_CONCURRENT_THREAD_COUNT + #if defined(EA_PLATFORM_DESKTOP) + #define EATHREAD_MAX_CONCURRENT_THREAD_COUNT 16 + #elif defined(EA_PLATFORM_MOBILE) + #define EATHREAD_MAX_CONCURRENT_THREAD_COUNT 4 + #elif defined(EA_PLATFORM_NX) + #define EATHREAD_MAX_CONCURRENT_THREAD_COUNT 2 // -1 core so we don't kick off the main thread + #else + #define EATHREAD_MAX_CONCURRENT_THREAD_COUNT 8 + #endif +#endif + + +int TestThreadSync(); +int TestThreadAtomic(); +int TestThreadCallstack(); +int TestThreadStorage(); +int TestThreadSpinLock(); +int TestThreadRWSpinLock(); +int TestThreadFutex(); +int TestThreadMutex(); +int TestThreadRWMutex(); +int TestThreadSemaphore(); +int TestThreadRWSemaLock(); +int TestThreadCondition(); +int TestThreadBarrier(); +int TestThreadThread(); +int TestThreadThreadPool(); +int TestThreadSmartPtr(); +int TestThreadMisc(); +int TestEnumerateThreads(); +int TestCpuAvailableAffinityMask(); + +#endif // Header include guard + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadAtomic.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadAtomic.cpp new file mode 100644 index 00000000..abe7469a --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadAtomic.cpp @@ -0,0 +1,1007 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include + +EA_DISABLE_VC_WARNING(4265 4365 4836 4571 4625 4626 4628 4193 4127 4548) +#include +EA_RESTORE_VC_WARNING() + +#if defined(_MSC_VER) + #pragma warning(disable: 4996) // This function or variable may be unsafe / deprecated. +#endif + +#include + + +using namespace EA::Thread; + + +#if EA_THREADS_AVAILABLE + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + +struct AWorkData32 +{ + volatile bool mbShouldQuit; + AtomicInt32 mnAtomicInteger1; + AtomicInt32 mnAtomicInteger2; + AtomicInt32 mnErrorCount; + + AWorkData32() : mbShouldQuit(false), + mnAtomicInteger1(0), mnAtomicInteger2(0), + mnErrorCount(0) {} +}; + + + +static intptr_t Atomic32TestThreadFunction1(void* pvWorkData) +{ + int nErrorCount = 0; + AWorkData32* pWorkData = (AWorkData32*)pvWorkData; + const ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "Atomic test function 1 created, thread id %s\n", EAThreadThreadIdToString(threadId)); + + // Do a series of operations, the final result of which is zero. + while(!pWorkData->mbShouldQuit) + { + ++pWorkData->mnAtomicInteger1; + ++pWorkData->mnAtomicInteger2; + --pWorkData->mnAtomicInteger1; + --pWorkData->mnAtomicInteger2; + pWorkData->mnAtomicInteger1 += 5; + pWorkData->mnAtomicInteger2 += 5; + pWorkData->mnAtomicInteger1 -= 5; + pWorkData->mnAtomicInteger2 -= 5; + pWorkData->mnAtomicInteger1++; + pWorkData->mnAtomicInteger2++; + pWorkData->mnAtomicInteger1--; + pWorkData->mnAtomicInteger2--; + ThreadCooperativeYield(); + } + + pWorkData->mnErrorCount += nErrorCount; + + EA::UnitTest::ReportVerbosity(1, "Atomic test function 1 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); + return 0; +} + + +static intptr_t Atomic32TestThreadFunction2(void* pvWorkData) +{ + int nErrorCount = 0; + AWorkData32* pWorkData = (AWorkData32*)pvWorkData; + const ThreadId threadId = GetThreadId(); + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + int32_t threadUniqueId32 = (int32_t)threadUniqueId; + + EA::UnitTest::ReportVerbosity(1, "Atomic test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId)); + + // Test the SetValueConditional function. We basically create a spinlock here. + while(!pWorkData->mbShouldQuit) + { + if(pWorkData->mnAtomicInteger1.SetValueConditional(threadUniqueId32, 0x11223344)) + { + EATEST_VERIFY_MSG(pWorkData->mnAtomicInteger1 == threadUniqueId32, "AtomicInt SetValueConditional failure."); + pWorkData->mnAtomicInteger1 = 0x11223344; + } + + ThreadCooperativeYield(); + } + + pWorkData->mnErrorCount += nErrorCount; + + EA::UnitTest::ReportVerbosity(1, "Atomic test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); + return 0; +} + + +struct AWorkData64 +{ + volatile bool mbShouldQuit; + AtomicInt64 mnAtomicInteger1; + AtomicInt64 mnAtomicInteger2; + AtomicInt64 mnErrorCount; + + AWorkData64() : mbShouldQuit(false), + mnAtomicInteger1(0), mnAtomicInteger2(0), + mnErrorCount(0) {} +}; + + +static intptr_t Atomic64TestThreadFunction1(void* pvWorkData) +{ + int nErrorCount = 0; + AWorkData64* pWorkData = (AWorkData64*)pvWorkData; + const ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 1 created, thread id %s\n", EAThreadThreadIdToString(threadId)); + + // Do a series of operations, the final result of which is zero. + while(!pWorkData->mbShouldQuit) + { + ++pWorkData->mnAtomicInteger1; + ++pWorkData->mnAtomicInteger2; + --pWorkData->mnAtomicInteger1; + --pWorkData->mnAtomicInteger2; + pWorkData->mnAtomicInteger1 += UINT64_C(0x0000000fffffffff); + pWorkData->mnAtomicInteger2 += UINT64_C(0x0000000ffffffffe); + pWorkData->mnAtomicInteger1 -= UINT64_C(0x0000000fffffffff); + pWorkData->mnAtomicInteger2 -= UINT64_C(0x0000000ffffffffe); + pWorkData->mnAtomicInteger1++; + pWorkData->mnAtomicInteger2++; + pWorkData->mnAtomicInteger1--; + pWorkData->mnAtomicInteger2--; + ThreadCooperativeYield(); + } + + pWorkData->mnErrorCount += nErrorCount; + + EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 1 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); + return 0; +} + + +static intptr_t Atomic64TestThreadFunction2(void* pvWorkData) +{ + int nErrorCount = 0; + AWorkData64* pWorkData = (AWorkData64*)pvWorkData; + const ThreadId threadId = GetThreadId(); + ThreadUniqueId threadUniqueId; + EAThreadGetUniqueId(threadUniqueId); + uint64_t threadUnqueId64 = (uint64_t)threadUniqueId | UINT64_C(0xeeeeddddffffffff); + + EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId)); + + // Test the SetValueConditional function. We basically create a spinlock here. + while(!pWorkData->mbShouldQuit) + { + if(pWorkData->mnAtomicInteger1.SetValueConditional(threadUnqueId64, 0x1122334455667788)) + { + EATEST_VERIFY_MSG(pWorkData->mnAtomicInteger1 == static_cast(threadUnqueId64), "AtomicInt64 SetValueConditional failure."); + pWorkData->mnAtomicInteger1.SetValue(0x1122334455667788); + } + + ThreadCooperativeYield(); + } + + pWorkData->mnErrorCount += nErrorCount; + + EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); + return 0; +} + + +static intptr_t Atomic64TestThreadFunction3(void* pvWorkData) +{ + int nErrorCount = 0; + AWorkData64* pWorkData = (AWorkData64*)pvWorkData; + const ThreadId threadId = GetThreadId(); + const uint64_t value0 = UINT64_C(0x0000000000000000); + const uint64_t value1 = UINT64_C(0xffffffffffffffff); + + EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 created, thread id %s\n", EAThreadThreadIdToString(threadId)); + + // Test the SetValueConditional function. + while(!pWorkData->mbShouldQuit) + { + pWorkData->mnAtomicInteger1.SetValueConditional(value0, value1); + uint64_t currentValue = pWorkData->mnAtomicInteger1.GetValue(); + EATEST_VERIFY_MSG((currentValue == value0) || (currentValue == value1), "AtomicInt64 SetValueConditional failure."); + + pWorkData->mnAtomicInteger1.SetValueConditional(value1, value0); + currentValue = pWorkData->mnAtomicInteger1.GetValue(); + EATEST_VERIFY_MSG((currentValue == value0) || (currentValue == value1), "AtomicInt64 SetValueConditional failure."); + + ThreadCooperativeYield(); + } + + pWorkData->mnErrorCount += nErrorCount; + + EA::UnitTest::ReportVerbosity(1, "Atomic64 test function 2 exiting, thread id %s\n", EAThreadThreadIdToString(threadId)); + return 0; +} + +template +int TestSimpleAtomicOps() +{ + int nErrorCount = 0; + bool result = false; + + alignas(16) T value = 0; + alignas(16) T dest = 0; + alignas(16) T conditionFail = 4; + alignas(16) T conditionSucceed = 0; + + // AtomicGetValue + dest = 3; + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 3, "AtomicGetValue failure\n"); + + // AtomicSetValue + value = AtomicSetValue(&dest, 4); + EATEST_VERIFY_MSG(value == 3, "AtomicSetValue failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 4, "AtomicSetValue failure\n"); + + // AtomicFetchIncrement + value = AtomicFetchIncrement(&dest); + EATEST_VERIFY_MSG(value == 4, "AtomicFetchIncrement failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 5, "AtomicFetchIncrement failure\n"); + + // AtomicFetchDecrement + value = AtomicFetchDecrement(&dest); + EATEST_VERIFY_MSG(value == 5, "AtomicFetchDecrement failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 4, "AtomicFetchDecrement failure\n"); + + // AtomicFetchAdd + value = AtomicFetchAdd(&dest, 3); + EATEST_VERIFY_MSG(value == 4, "AtomicFetchAdd failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 7, "AtomicFetchAdd failure\n"); + + // AtomicFetchSub + value = AtomicFetchSub(&dest, 3); + EATEST_VERIFY_MSG(value == 7, "AtomicFetchSub failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 4, "AtomicFetchSub failure\n"); + value = AtomicFetchSub(&dest, T(-3)); + EATEST_VERIFY_MSG(value == 4, "AtomicFetchSub failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 7, "AtomicFetchSub failure\n"); + + // AtomicFetchOr + value = AtomicFetchOr(&dest, 8); + EATEST_VERIFY_MSG(value == 7, "AtomicFetchOr failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 15, "AtomicFetchOr failure\n"); + + // AtomicFetchAnd + value = AtomicFetchAnd(&dest, 3); + EATEST_VERIFY_MSG(value == 15, "AtomicFetchAnd failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 3, "AtomicFetchAnd failure\n"); + + // AtomicFetchXor + value = AtomicFetchXor(&dest, dest); + EATEST_VERIFY_MSG(value == 3, "AtomicFetchXor failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 0, "AtomicFetchXor failure\n"); + + // AtomicFetchSwap + value = AtomicFetchSwap(&dest, 5); + EATEST_VERIFY_MSG(value == 0, "AtomicFetchSwap failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 5, "AtomicFetchSwap failure\n"); + + // AtomicSetValueConditional + dest = 0; + value = 1; + conditionFail = 4; + conditionSucceed = 0; + + // Try to do conditional fetch swap which should fail + value = EA::Thread::AtomicFetchSwapConditional(&dest, 1, conditionFail); + EATEST_VERIFY_MSG(value != conditionFail, "AtomicFetchSwapConditional failure 0\n"); + EATEST_VERIFY_MSG(dest == 0, "AtomicFetchSwapConditional failure 1\n"); + + // Try to do conditional fetch swap which should succeed + value = EA::Thread::AtomicFetchSwapConditional(&dest, 1, conditionSucceed); + EATEST_VERIFY_MSG(value == conditionSucceed, "AtomicFetchSwapConditional failure 2\n"); + EATEST_VERIFY_MSG(dest == 1, "AtomicFetchSwapConditional failure 3\n"); + + // reset before the next test + dest = 0; + value = 1; + + // Try to do an update which should fail. + result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionFail); + EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure 0\n"); + EATEST_VERIFY_MSG(dest == 0, "AtomicSetValueConditional failure 1\n"); + + // Try to do an update which should succeed. + result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionSucceed); + EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure 2\n"); + EATEST_VERIFY_MSG(dest == 1, "AtomicSetValueConditional failure 3\n"); + + return nErrorCount; +} + +template +inline int TestAtomicsSizeBoundaries() +{ + static_assert(eastl::is_floating_point::value == false, "atomic floats not supported"); + + int nErrorCount = 0; + bool result = false; + alignas(16) T value = 0, dest = 0; + + T max = eastl::numeric_limits::max(); + T lowest = eastl::numeric_limits::lowest(); + + + /// Test the max boundary + /// + value = AtomicSetValue(&dest, max); + EATEST_VERIFY_MSG(value == 0, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == max, "max failure\n"); + + value = AtomicFetchIncrement(&dest); + EATEST_VERIFY_MSG(value == max, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == lowest, "max failure\n"); + + value = AtomicFetchDecrement(&dest); + EATEST_VERIFY_MSG(value == lowest, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == max, "max failure\n"); + + value = AtomicFetchAdd(&dest, 1); + EATEST_VERIFY_MSG(value == max, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == lowest, "max failure\n"); + + value = AtomicFetchAnd(&dest, lowest); + EATEST_VERIFY_MSG(value == lowest, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == lowest, "max failure\n"); + + value = AtomicFetchXor(&dest, lowest); + EATEST_VERIFY_MSG(value == lowest, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 0, "max failure\n"); + + value = AtomicFetchSwap(&dest, lowest); + EATEST_VERIFY_MSG(value == 0, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == lowest, "max failure\n"); + + // reset to zero + result = AtomicSetValueConditional(&dest, 0, lowest); + EATEST_VERIFY_MSG(result, "max failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 0, "max failure\n"); + + + + /// Test the lowest boundary + /// + value = AtomicSetValue(&dest, lowest); + EATEST_VERIFY_MSG(value == 0, "lowest failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == lowest, "lowest failure\n"); + + // decrement the lowest to ensure we rollover to the highest value + value = AtomicFetchDecrement(&dest); + EATEST_VERIFY_MSG(value == lowest, "lowest failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == max, "lowest failure\n"); + + + return nErrorCount; +} + +template +inline int TestAtomicsConstFetch() +{ + int nErrorCount = 0; + + { + alignas(16) const T value = 13; + auto r = AtomicGetValue(&value); + EATEST_VERIFY_MSG(r == value, "failure\n"); + } + + { + struct Foo + { + Foo(uint32_t n) : baz(n) {} + uint32_t getBaz() const { return AtomicGetValue(&this->baz); } + uint32_t baz; + }; + + Foo foo(42); + auto r = foo.getBaz(); + EATEST_VERIFY_MSG(r == 42, "failure\n"); + } + + return nErrorCount; +} + +template +int TestAtomicIntT() +{ + int nErrorCount = 0; + + AtomicInt atomicInt = 0; + + ++atomicInt; + --atomicInt; + atomicInt += 5; + atomicInt -= 5; + atomicInt++; + atomicInt--; + + EATEST_VERIFY(atomicInt == 0); + + return nErrorCount; +} + +template +int TestNonMemberAtomics() +{ + int nErrorCount = 0; + nErrorCount += TestSimpleAtomicOps(); + nErrorCount += TestAtomicsSizeBoundaries(); + nErrorCount += TestAtomicsConstFetch(); + return nErrorCount; +} + +#endif // #if EA_THREADS_AVAILABLE + + +int TestThreadAtomic() +{ + int nErrorCount(0); + + { // Initial tests of 128 bit atomics + #if EATHREAD_ATOMIC_128_SUPPORTED // This will be true only for 64+ bit platforms. + + // To consider: Use __int128_t on GCC for GCC >= 4.1 + + EA_ALIGN(128) int64_t dest128[2] = { 0, 0 }; + EA_ALIGN(128) int64_t value128[2] = { 1, 2 }; + EA_ALIGN(128) int64_t condition128Fail[2] = { 4, 5 }; + EA_ALIGN(128) int64_t condition128Succeed[2] = { 0, 0 }; + bool result; + + // Try to do an update which should fail. + result = EA::Thread::AtomicSetValueConditionall28(dest128, value128, condition128Fail); + + EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure: result should have been false.\n"); + EATEST_VERIFY_F((dest128[0] == 0) && (dest128[1] == 0), "AtomicSetValueConditional failure: dest128[0]:%I64d dest128[1]:%I64d\n", dest128[0], dest128[1]); + EATEST_VERIFY_F((value128[0] == 1) && (value128[1] == 2), "AtomicSetValueConditional failure: value128[0]:%I64d value128[1]:%I64d\n", value128[0], value128[1]); + EATEST_VERIFY_F((condition128Fail[0] == 4) && (condition128Fail[1] == 5), "AtomicSetValueConditional failure: condition128Fail[0]:%I64d condition128Fail[1]:%I64d\n", condition128Fail[0], condition128Fail[1]); + EATEST_VERIFY_F((condition128Succeed[0] == 0) && (condition128Succeed[1] == 0), "AtomicSetValueConditional failure: condition128Succeed[0]:%I64d condition128Succeed[1]:%I64d\n", condition128Succeed[0], condition128Succeed[1]); + + // Try to do an update which should succeed. + // VC++ for VS2010 misgenerates the atomic code below in optimized builds, by passing what appears to be the wrong value for dest128 to AtomicSetValueConditional128. + // We added some diagnostic code and now the compiler does the right thing. I (Paul Pedriana) wonder if the problem is related to + // the alignment specification for dest, which must be 128 byte aligned for AtomicSetValueConditional128 (cmpxchg16b) to work. + // The dest address is indeed being aligned to 16 bytes, so that's not the problem. + EA::UnitTest::ReportVerbosity(1, "%p %p %p\n", dest128, value128, condition128Succeed); + result = EA::Thread::AtomicSetValueConditionall28(dest128, value128, condition128Succeed); + + EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure: result should have been true.\n"); + EATEST_VERIFY_F((dest128[0] == 1) && (dest128[1] == 2), "AtomicSetValueConditional failure: dest128:%p dest128[0]:%I64d dest128[1]:%I64d\n", dest128, dest128[0], dest128[1]); + EATEST_VERIFY_F((value128[0] == 1) && (value128[1] == 2), "AtomicSetValueConditional failure: value128:%p value128[0]:%I64d value128[1]:%I64d\n", value128, value128[0], value128[1]); + EATEST_VERIFY_F((condition128Fail[0] == 4) && (condition128Fail[1] == 5), "AtomicSetValueConditional failure: condition128Fail:%p condition128Fail[0]:%I64d condition128Fail[1]:%I64d\n", condition128Fail, condition128Fail[0], condition128Fail[1]); + EATEST_VERIFY_F((condition128Succeed[0] == 0) && (condition128Succeed[1] == 0), "AtomicSetValueConditional failure: condition128Succeed:%p condition128Succeed[0]:%I64d condition128Succeed[1]:%I64d\n", condition128Succeed, condition128Succeed[0], condition128Succeed[1]); + + + #if defined(EA_COMPILER_GNUC) // GCC defines __int128_t as a built-in type. + + __int128_t dest; + __int128_t value; + __int128_t conditionFail; + __int128_t conditionSucceed; + + // AtomicGetValue + dest = 3; + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 3, "AtomicGetValue[128] failure\n"); + + // AtomicSetValue + AtomicSetValue(&dest, 4); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 4, "AtomicSetValue[128] failure\n"); + + // AtomicIncrement + value = AtomicIncrement(&dest); + EATEST_VERIFY_MSG(value == 5, "AtomicIncrement[128] failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 5, "AtomicIncrement[128] failure\n"); + + // AtomicDecrement + value = AtomicDecrement(&dest); + EATEST_VERIFY_MSG(value == 4, "AtomicDecrement[128] failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 4, "AtomicDecrement[128] failure\n"); + + // AtomicAdd + value = AtomicAdd(&dest, 3); + EATEST_VERIFY_MSG(value == 7, "AtomicAdd[128] failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 7, "AtomicAdd[128] failure\n"); + + // AtomicOr + value = AtomicOr(&dest, 8); + EATEST_VERIFY_MSG(value == 15, "AtomicOr[128] failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 15, "AtomicOr[128] failure\n"); + + // AtomicAnd + value = AtomicAnd(&dest, 3); + EATEST_VERIFY_MSG(value == 3, "AtomicAnd[128] failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 3, "AtomicAnd[128] failure\n"); + + // AtomicXor + value = AtomicXor(&dest, dest); + EATEST_VERIFY_MSG(value == 0, "AtomicXor[128] failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 0, "AtomicXor[128] failure\n"); + + // AtomicSwap + value = AtomicSwap(&dest, 5); + EATEST_VERIFY_MSG(value == 0, "AtomicSwap[128] failure\n"); + value = AtomicGetValue(&dest); + EATEST_VERIFY_MSG(value == 5, "AtomicSwap[128] failure\n"); + + // AtomicSetValueConditional + dest = 0; + value = 1; + conditionFail = 4; + conditionSucceed = 0; + + // Try to do an update which should fail. + result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionFail); + + EATEST_VERIFY_MSG(!result, "AtomicSetValueConditional failure 0\n"); + EATEST_VERIFY_MSG(dest == 0, "AtomicSetValueConditional failure 1\n"); + + // Try to do an update which should succeed. + result = EA::Thread::AtomicSetValueConditional(&dest, value, conditionSucceed); + + EATEST_VERIFY_MSG(result, "AtomicSetValueConditional failure 2\n"); + EATEST_VERIFY_MSG(dest == 1, "AtomicSetValueConditional failure 3\n"); + + if(nErrorCount != 0) + return nErrorCount; + + #endif + + #endif + } + + { // Basic single-threaded Atomic test. + AtomicInt32 i32(1); + AtomicUint32 u32(1); + + EATEST_VERIFY_MSG(i32.GetValue() == 1, "AtomicInt32 failure."); + EATEST_VERIFY_MSG(u32.GetValue() == 1, "AtomicUint32 failure."); + + char buffer[64]; + sprintf(buffer, "%d %u", (signed int)i32.GetValue(), (unsigned int)u32.GetValue()); + EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt32 failure."); + + // Copy ctor/operator=. + AtomicInt32 i32CopyA(i32); + AtomicInt32 i32CopyB(i32CopyA); + i32CopyA = i32CopyB; + + sprintf(buffer, "%d %d", (signed int)i32CopyA.GetValue(), (signed int)i32CopyB.GetValue()); + EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt32 failure."); + + // Test platforms that support 64 bits.. + AtomicInt64 i64(1); + AtomicUint64 u64(1); + + sprintf(buffer, "%.0f %.0f", (double)i64.GetValue(), (double)u64.GetValue()); + EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt64 failure."); + + // Copy ctor/operator=. + AtomicInt64 i64CopyA(i64); + AtomicInt64 i64CopyB(i64CopyA); + i64CopyA = i64CopyB; + + sprintf(buffer, "%d %d", (signed int)i64CopyA.GetValue(), (signed int)i64CopyB.GetValue()); + EATEST_VERIFY_MSG(strcmp(buffer, "1 1") == 0, "AtomicInt64 failure."); + + bool result = i64.SetValueConditional(2, 99999); // This should not set the value to 2. + EATEST_VERIFY_MSG(!result && (i64.GetValue() == 1), "AtomicInt64 failure."); + + i64.SetValueConditional(2, 1); // This should set the value to 2. + EATEST_VERIFY_MSG(!result && (i64.GetValue() == 2), "AtomicInt64 failure."); + } + + { // Basic single-threaded AtomicInt32 test. + AtomicInt32 i(0); // Note that this assignment goes through AtomicInt32 operator=(). + AtomicInt32::ValueType x; + + EATEST_VERIFY_MSG(i == 0, "AtomicInt32 failure."); + + ++i; + i++; + --i; + i--; + i += 7; + i -= 3; + EATEST_VERIFY_MSG(i == 4, "AtomicInt32 failure."); + + i = 2; + x = i.GetValue(); + EATEST_VERIFY_MSG(x == 2, "AtomicInt32 failure."); + + i.Increment(); + i.Decrement(); + i.Add(5); + i.Add(-2); + EATEST_VERIFY_MSG(i == 5, "AtomicInt32 failure."); + + i.SetValue(6); + EATEST_VERIFY_MSG(i == 6, "AtomicInt32 failure."); + + bool bWasEqualTo10000 = i.SetValueConditional(3, 10000); + EATEST_VERIFY_MSG(!bWasEqualTo10000, "AtomicInt32 failure."); + + bool bWasEqualTo6 = i.SetValueConditional(3, 6); + EATEST_VERIFY_MSG(bWasEqualTo6, "AtomicInt32 failure."); + } + + { // Verify pre-increment/post-increment works as intended. + AtomicInt32 i32(0); + AtomicInt32::ValueType x32; + + // ValueType SetValue(ValueType n) + // Safely sets a new value. Returns the old value. + x32 = i32.SetValue(1); + EATEST_VERIFY_MSG(x32 == 0, "AtomicInt return value failure."); + + // ValueType Increment() + // Safely increments the value. Returns the new value. + x32 = i32.Increment(); + EATEST_VERIFY_MSG(x32 == 2, "AtomicInt return value failure."); + + // ValueType Decrement() + // Safely decrements the value. Returns the new value. + x32 = i32.Decrement(); + EATEST_VERIFY_MSG(x32 == 1, "AtomicInt return value failure."); + + // ValueType Add(ValueType n) + // Safely adds a value, which can be negative. Returns the new value. + x32 = i32.Add(35); + EATEST_VERIFY_MSG(x32 == 36, "AtomicInt return value failure."); + + // ValueType operator=(ValueType n) + // Safely assigns the value. Returns the new value. + x32 = (i32 = 17); + EATEST_VERIFY_MSG(x32 == 17, "AtomicInt return value failure."); + + // ValueType operator+=(ValueType n) + // Safely adds a value, which can be negative. Returns the new value. + x32 = (i32 += 3); + EATEST_VERIFY_MSG(x32 == 20, "AtomicInt return value failure."); + + // ValueType operator-=(ValueType n) + // Safely subtracts a value, which can be negative. Returns the new value. + x32 = (i32 -= 6); + EATEST_VERIFY_MSG(x32 == 14, "AtomicInt return value failure."); + + // ValueType operator++() + // pre-increment operator++ + x32 = ++i32; + EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i32 == 15, "AtomicInt return value failure."); + + // ValueType operator++(int) + // post-increment operator++ + x32 = i32++; + EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i32 == 16, "AtomicInt return value failure."); + + // ValueType operator--() + // pre-increment operator-- + x32 = --i32; + EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i32 == 15, "AtomicInt return value failure."); + + // ValueType operator--(int) + // post-increment operator-- + x32 = i32--; + EATEST_VERIFY_MSG(x32 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i32 == 14, "AtomicInt return value failure."); + } + + + { // Verify pre-increment/post-increment works as intended. + AtomicInt64 i64(0); + AtomicInt64::ValueType x64; + + // ValueType SetValue(ValueType n) + // Safely sets a new value. Returns the old value. + x64 = i64.SetValue(1); + EATEST_VERIFY_MSG(x64 == 0, "AtomicInt return value failure."); + + // ValueType Increment() + // Safely increments the value. Returns the new value. + x64 = i64.Increment(); + EATEST_VERIFY_MSG(x64 == 2, "AtomicInt return value failure."); + + // ValueType Decrement() + // Safely decrements the value. Returns the new value. + x64 = i64.Decrement(); + EATEST_VERIFY_MSG(x64 == 1, "AtomicInt return value failure."); + + // ValueType Add(ValueType n) + // Safely adds a value, which can be negative. Returns the new value. + x64 = i64.Add(35); + EATEST_VERIFY_MSG(x64 == 36, "AtomicInt return value failure."); + + // ValueType operator=(ValueType n) + // Safely assigns the value. Returns the new value. + x64 = (i64 = 17); + EATEST_VERIFY_MSG(x64 == 17, "AtomicInt return value failure."); + + // ValueType operator+=(ValueType n) + // Safely adds a value, which can be negative. Returns the new value. + x64 = (i64 += 3); + EATEST_VERIFY_MSG(x64 == 20, "AtomicInt return value failure."); + + // ValueType operator-=(ValueType n) + // Safely subtracts a value, which can be negative. Returns the new value. + x64 = (i64 -= 6); + EATEST_VERIFY_MSG(x64 == 14, "AtomicInt return value failure."); + + // ValueType operator++() + // pre-increment operator++ + x64 = ++i64; + EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i64 == 15, "AtomicInt return value failure."); + + // ValueType operator++(int) + // post-increment operator++ + x64 = i64++; + EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i64 == 16, "AtomicInt return value failure."); + + // ValueType operator--() + // pre-increment operator-- + x64 = --i64; + EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i64 == 15, "AtomicInt return value failure."); + + // ValueType operator--(int) + // post-increment operator-- + x64 = i64--; + EATEST_VERIFY_MSG(x64 == 15, "AtomicInt return value failure."); + EATEST_VERIFY_MSG(i64 == 14, "AtomicInt return value failure."); + } + + { // Basic single-threaded AtomicPointer test. + AtomicPointer p(NULL); + AtomicPointer::PointerValueType pTemp; + + EATEST_VERIFY_MSG(p.GetValue() == NULL, "AtomicPointer failure."); + + ++p; + p++; + --p; + p--; + p += 7; + p -= 3; + EATEST_VERIFY_MSG(p == (void*)4, "AtomicPointer failure."); + + p = (void*)2; + pTemp = p.GetValue(); + EATEST_VERIFY_MSG((uintptr_t)pTemp == 2, "AtomicPointer failure."); + + p.Increment(); + p.Decrement(); + p.Add(5); + p.Add(-2); + EATEST_VERIFY_MSG(p == (void*)5, "AtomicPointer failure."); + + p.SetValue((void*)6); + EATEST_VERIFY_MSG(p == (void*)6, "AtomicPointer failure."); + + bool bWasEqualTo10000 = p.SetValueConditional((void*)3, (void*)10000); + EATEST_VERIFY_MSG(!bWasEqualTo10000, "AtomicPointer failure."); + + bool bWasEqualTo6 = p.SetValueConditional((void*)3, (void*)6); + EATEST_VERIFY_MSG(bWasEqualTo6, "AtomicPointer failure."); + } + + { + AtomicInt32 gA, gB; + + gA = gB = 0; + ++gA; + ++gB; + gA = gB = 0; + + EATEST_VERIFY_MSG((gA == 0) && (gB == 0), "AtomicInt32 operator= failure."); + } + + #if EA_THREADS_AVAILABLE + + { // Multithreaded test 1 + AWorkData32 workData32; + + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + thread[i].Begin(Atomic32TestThreadFunction1, &workData32); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); + + workData32.mbShouldQuit = true; + + for(int i(0); i < kThreadCount; i++) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); + } + + // In the end, the sum must be zero. + EATEST_VERIFY_MSG(workData32.mnAtomicInteger1 == 0, "Atomic/Thread failure."); + EATEST_VERIFY_MSG(workData32.mnAtomicInteger2 == 0, "Atomic/Thread failure."); + + nErrorCount += (int)workData32.mnErrorCount; + } + + { // Multithreaded test 2 + AWorkData32 workData32; + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + thread[i].Begin(Atomic32TestThreadFunction2, &workData32); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); + + workData32.mbShouldQuit = true; + + for(int i(0); i < kThreadCount; i++) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); + } + + nErrorCount += (int)workData32.mnErrorCount; + } + + + { // Multithreaded test 1 + AWorkData64 workData64; + + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + thread[i].Begin(Atomic64TestThreadFunction1, &workData64); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); + + workData64.mbShouldQuit = true; + + for(int i(0); i < kThreadCount; i++) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); + } + + // In the end, the sum must be zero. + EATEST_VERIFY_MSG(workData64.mnAtomicInteger1 == 0, "Atomic/Thread failure."); + EATEST_VERIFY_MSG(workData64.mnAtomicInteger2 == 0, "Atomic/Thread failure."); + + nErrorCount += (int)workData64.mnErrorCount; + } + + { // Multithreaded test 2 + AWorkData64 workData64; + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + thread[i].Begin(Atomic64TestThreadFunction2, &workData64); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); + + workData64.mbShouldQuit = true; + + for(int i(0); i < kThreadCount; i++) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); + } + + nErrorCount += (int)workData64.mnErrorCount; + } + + { // Multithreaded test 3 + AWorkData64 workData64; + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + thread[i].Begin(Atomic64TestThreadFunction3, &workData64); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); + + workData64.mbShouldQuit = true; + + for(int i(0); i < kThreadCount; i++) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != EA::Thread::Thread::kStatusRunning, "Atomic/Thread failure."); + } + + nErrorCount += (int)workData64.mnErrorCount; + } + + #endif + + { + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + nErrorCount += TestAtomicIntT(); + } + + // Non-Member Atomics Tests + { + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + nErrorCount += TestNonMemberAtomics(); + } + + + return nErrorCount; +} + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadBarrier.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadBarrier.cpp new file mode 100644 index 00000000..26879f79 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadBarrier.cpp @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include + + +using namespace EA::Thread; + + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +struct BT_WorkData +{ + Barrier* mpBarrier; + int mnSleepTime; + AtomicInt32 mnErrorCount; + + BT_WorkData(Barrier* pBarrier = NULL, int nSleepTime = 0) + : mpBarrier(pBarrier), mnSleepTime(nSleepTime), mnErrorCount(0) {} +}; + + + +static intptr_t BT_ConsumerFunction(void* pvWorkData) +{ + int nErrorCount = 0; + BT_WorkData* pWorkData = (BT_WorkData*)pvWorkData; + const ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "Barrier consumer test function created: %s\n", EAThreadThreadIdToString(threadId)); + + // Here we would actually do the job, but printing 'job done' is enough in itself. + ThreadSleep((ThreadTime)pWorkData->mnSleepTime); + EA::UnitTest::ReportVerbosity(1, "Job done by thread %s.\n", EAThreadThreadIdToString(threadId)); + EA::UnitTest::ReportVerbosity(1, "Start synchronizing by thread %s.\n", EAThreadThreadIdToString(threadId)); + const Barrier::Result result = pWorkData->mpBarrier->Wait(GetThreadTime() + 10000); + + if(result == Barrier::kResultPrimary) + { + // This is the first thread to be released: call producer function + EA::UnitTest::ReportVerbosity(1, "Serial execution at Barrier by thread %s\n", EAThreadThreadIdToString(threadId)); + } + else if(result == Barrier::kResultTimeout) + { + EA::UnitTest::Report("Barrier time-out by thread %s\n", EAThreadThreadIdToString(threadId)); + nErrorCount++; + } + else if(result == Barrier::kResultError) + { + EA::UnitTest::Report("Barrier error in thread %s\n", EAThreadThreadIdToString(threadId)); + nErrorCount++; + } + + pWorkData->mnErrorCount += nErrorCount; + + ThreadSleep((ThreadTime)pWorkData->mnSleepTime); + + EA::UnitTest::ReportVerbosity(1, "Job synchronized by thread %s.\n", EAThreadThreadIdToString(threadId)); + return 0; +} + + +static intptr_t BT_ConsumerFunction2(void * pvWorkData) +{ + ((Barrier*)pvWorkData)->Wait(); + ((Barrier*)pvWorkData)->Wait(); + return 0; +} + + +int TestThreadBarrier() +{ + int nErrorCount(0); + + #if EA_THREADS_AVAILABLE + + { + Thread::Status status; + const int kThreadCount(kMaxConcurrentThreadCount - 1); + BT_WorkData workData[kThreadCount]; + Thread threads[kThreadCount]; + ThreadId threadId[kThreadCount]; + Barrier barrier(kThreadCount); + int i; + + for(i = 0; i < kThreadCount; i++) + { + workData[i].mpBarrier = &barrier; + workData[i].mnSleepTime = (i + 1) * 500; + } + + for(i = 0; i < kThreadCount; i++) + { + threadId[i] = threads[i].Begin(BT_ConsumerFunction, &workData[i]); + EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Barrier/Thread failure: Couldn't create thread."); + } + + EA::UnitTest::ThreadSleepRandom(2000, 2000); + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + { + status = threads[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Barrier/Thread failure."); + } + + nErrorCount += workData[i].mnErrorCount; + } + } + + { + Thread threads[2]; + ThreadId threadId[2]; + Barrier barrier(2); + Thread::Status status; + + threadId[0] = threads[0].Begin(BT_ConsumerFunction2, &barrier); + EATEST_VERIFY_MSG(threadId[0] != kThreadIdInvalid, "Barrier/Thread failure: Couldn't create thread."); + + threadId[1] = threads[1].Begin(BT_ConsumerFunction2, &barrier); + EATEST_VERIFY_MSG(threadId[1] != kThreadIdInvalid, "Barrier/Thread failure: Couldn't create thread."); + + if(threadId[0] != kThreadIdInvalid) + { + status = threads[0].WaitForEnd(); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Barrier/Thread failure."); + } + + if(threadId[1] != kThreadIdInvalid) + { + status = threads[1].WaitForEnd(); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Barrier/Thread failure."); + } + } + + #endif + + return nErrorCount; +} + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadCallstack.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadCallstack.cpp new file mode 100644 index 00000000..479fac88 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadCallstack.cpp @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef _MSC_VER +#pragma warning(push, 0) +#include +#endif + + +int TestThreadCallstack() +{ + int nErrorCount(0); + + + #if defined(EA_PLATFORM_MICROSOFT) + // bool ThreadHandlesAreEqual(intptr_t threadId1, intptr_t threadId2); + // uint32_t GetThreadIdFromThreadHandle(intptr_t threadId); + #endif + + // To do: Implement tests for the following for supporting platforms. + // bool GetCallstackContext(CallstackContext& context, intptr_t threadId = 0); + // bool GetCallstackContextSysThreadId(CallstackContext& context, intptr_t sysThreadId = 0); + // void GetCallstackContext(CallstackContext& context, const Context* pContext = NULL); + // size_t GetModuleFromAddress(const void* pAddress, char* pModuleFileName, size_t moduleNameCapacity); + // ModuleHandle GetModuleHandleFromAddress(const void* pAddress); + + // EA::Thread::CallstackContext context; + // EA::Thread::GetCallstackContext(context, EA::Thread::GetThreadId()); + // EATEST_VERIFY(context.mRIP != 0); + // EATEST_VERIFY(context.mRSP != 0); + + // To consider: Test SetStackBase. It's not simple because SetStackBase is a backup for + // when GetStackBase's default functionality doesn't happen to work. + // void SetStackBase(void* pStackBase); + // void SetStackBase(uintptr_t pStackBase){ SetStackBase((void*)pStackBase); } + + void* pStackBase = EA::Thread::GetStackBase(); + void* pStackLimit = EA::Thread::GetStackLimit(); + + if(pStackBase && pStackLimit) + { + EATEST_VERIFY((uintptr_t)&nErrorCount < (uintptr_t)pStackBase); + EATEST_VERIFY((uintptr_t)&nErrorCount > (uintptr_t)pStackLimit); + } + + return nErrorCount; +} diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadCondition.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadCondition.cpp new file mode 100644 index 00000000..69f85b02 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadCondition.cpp @@ -0,0 +1,335 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace EA::Thread; + + +/////////////////////////////////////////////////////////////////////////////// +// EATHREAD_INTERPROCESS_CONDITION_SUPPORTED +// +#ifndef EATHREAD_INTERPROCESS_CONDITION_SUPPORTED + #if defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_LINUX) + #define EATHREAD_INTERPROCESS_CONDITION_SUPPORTED 1 + #else + #define EATHREAD_INTERPROCESS_CONDITION_SUPPORTED 0 + #endif +#endif + + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +struct TMWorkData +{ + volatile bool mbProducersShouldQuit; + volatile bool mbConsumersShouldQuit; + EA::Thread::simple_list mJobList; + Condition mCondition; + Mutex mMutex; + int mnLastJobID; + int mnConditionTimeout; + AtomicInt32 mnTotalJobsCreated; + AtomicInt32 mnTotalJobsCompleted; + + TMWorkData( const ConditionParameters* pCondParams ) : mbProducersShouldQuit(false), mbConsumersShouldQuit(false), mCondition( pCondParams ), + mMutex(NULL, true), mnLastJobID(0), mnConditionTimeout(60000), mnTotalJobsCreated(0), mnTotalJobsCompleted(0) + { + // Empty + } + + // define copy ctor and assignment operator + // so the compiler does define them intrisically + TMWorkData(const TMWorkData& rhs); // copy constructor + TMWorkData& operator=(const TMWorkData& rhs); // assignment operator +}; + + +static intptr_t ProducerFunction(void* pvWorkData) +{ + int nErrorCount = 0; + TMWorkData* pWorkData = (TMWorkData*)pvWorkData; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "Condition producer test function created: %s\n", EAThreadThreadIdToString(threadId)); + + EAReadWriteBarrier(); + + while(!pWorkData->mbProducersShouldQuit) + { + EA::UnitTest::ThreadSleepRandom(100, 200); + pWorkData->mMutex.Lock(); + + for(int i(0), iEnd(rand() % 3); i < iEnd; i++) + { + const int nJob(++pWorkData->mnLastJobID); + pWorkData->mJobList.push_back(nJob); + ++pWorkData->mnTotalJobsCreated; + EA::UnitTest::ReportVerbosity(1, "Job %d created by %s.\n", nJob, EAThreadThreadIdToString(threadId)); + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + pWorkData->mMutex.Unlock(); + pWorkData->mCondition.Signal(false); + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + EA::UnitTest::ReportVerbosity(1, "Producer exiting: %s.\n", EAThreadThreadIdToString(threadId)); + + return nErrorCount; +} + +static intptr_t ProducerFunction_DoesNotSignal(void* pvWorkData) +{ + int nErrorCount = 0; + TMWorkData* pWorkData = (TMWorkData*)pvWorkData; + ThreadId threadId = GetThreadId(); + EA_UNUSED(pWorkData); + + EA::UnitTest::ReportVerbosity(1, "Condition producer (does not signal) test function created: %s\n", EAThreadThreadIdToString(threadId)); + + // Intentionally do nothing here. We are testing the conditional variable time out code path by + // ensuring we do not signal the Consumer that any work has been added into the queue for them + // to consume therefor explicitly causing a condition variable timeout. + + EA::UnitTest::ReportVerbosity(1, "Producer (does not signal) exiting: %s.\n", EAThreadThreadIdToString(threadId)); + + return nErrorCount; +} + +static intptr_t ConsumerFunction(void* pvWorkData) +{ + int nErrorCount = 0; + TMWorkData* pWorkData = (TMWorkData*)pvWorkData; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "Condition producer test function created: %s\n", EAThreadThreadIdToString(threadId)); + + pWorkData->mMutex.Lock(); + + do{ + if(!pWorkData->mJobList.empty()) + { + const int nJob = pWorkData->mJobList.front(); + pWorkData->mJobList.pop_front(); + pWorkData->mMutex.Unlock(); + + ThreadCooperativeYield(); // Used by cooperative threading platforms. + + // Here we would actually do the job, but printing 'job done' is enough in itself. + ++pWorkData->mnTotalJobsCompleted; + EA::UnitTest::ReportVerbosity(1, "Job %d done by %s.\n", nJob, EAThreadThreadIdToString(threadId)); + + pWorkData->mMutex.Lock(); + } + else + { + const ThreadTime timeoutAbsolute = GetThreadTime() + pWorkData->mnConditionTimeout; + const Condition::Result result = pWorkData->mCondition.Wait(&pWorkData->mMutex, timeoutAbsolute); + if((result != Condition::kResultOK) && pWorkData->mJobList.empty()) + break; + } + }while(!pWorkData->mbConsumersShouldQuit || !pWorkData->mJobList.empty()); + + pWorkData->mMutex.Unlock(); + + EA::UnitTest::ReportVerbosity(1, "Consumer exiting: %s.\n", EAThreadThreadIdToString(threadId)); + + return nErrorCount; +} + + +int TestThreadCondition() +{ + int nErrorCount(0); + + { // ctor tests + // We test various combinations of Mutex ctor and ConditionParameters. + // ConditionParameters(bool bIntraProcess = true, const char* pName = NULL); + // Condition(const ConditionParameters* pConditionParameters = NULL, bool bDefaultParameters = true); + + ConditionParameters cp1(true, NULL); + ConditionParameters cp2(true, "EATcp2"); + + #if EATHREAD_INTERPROCESS_CONDITION_SUPPORTED + ConditionParameters cp3(false, NULL); + ConditionParameters cp4(false, "EATcp4"); + #else + ConditionParameters cp3(true, NULL); + ConditionParameters cp4(true, "EATcp4"); + #endif + + // Create separate scopes below because some platforms are so + // limited that they can't create all of them at once. + { + Condition cond1(&cp1, false); + Condition cond2(&cp2, false); + Condition cond3(&cp3, false); + + cond1.Signal(); + cond2.Signal(); + cond3.Signal(); + } + { + Condition cond4(&cp4, false); + Condition cond5(NULL, true); + Condition cond6(NULL, false); + cond6.Init(&cp1); + + cond4.Signal(); + cond5.Signal(); + cond6.Signal(); + } + } + + + #if EA_THREADS_AVAILABLE + { + // test producer/consumer wait condition with intra-process condition + { + ConditionParameters exlusiveConditionParams( true, NULL ); + TMWorkData workData( &exlusiveConditionParams ); + Thread::Status status; + int i; + + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread threadProducer[kThreadCount]; + ThreadId threadIdProducer[kThreadCount]; + Thread threadConsumer[kThreadCount]; + ThreadId threadIdConsumer[kThreadCount]; + + // Create producers and consumers. + for(i = 0; i < kThreadCount; i++) + { + threadIdProducer[i] = threadProducer[i].Begin(ProducerFunction, &workData); + EATEST_VERIFY_MSG(threadIdProducer[i] != kThreadIdInvalid, "Condition/Thread failure: Thread creation failed."); + + threadIdConsumer[i] = threadConsumer[i].Begin(ConsumerFunction, &workData); + EATEST_VERIFY_MSG(threadIdConsumer[i] != kThreadIdInvalid, "Condition/Thread failure: Thread creation failed."); + } + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000); + + // Wait for producers to quit. + workData.mbProducersShouldQuit = true; + for(i = 0; i < kThreadCount; i++) + { + if(threadIdProducer[i] != kThreadIdInvalid) + { + status = threadProducer[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Condition/Thread failure: Wait for producer end failed."); + } + } + + EA::UnitTest::ThreadSleepRandom(2000, 2000); + + // Wait for consumers to quit. + workData.mbConsumersShouldQuit = true; + workData.mCondition.Signal(true); + for(i = 0; i < kThreadCount; i++) + { + if(threadIdConsumer[i] != kThreadIdInvalid) + { + status = threadConsumer[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Condition/Thread failure: Wait for consumer end failed."); + } + } + + EATEST_VERIFY_MSG(workData.mnTotalJobsCreated == workData.mnTotalJobsCompleted, "Condition failure: Not all consumer work was processed."); + } + + // test single producer/ single consumer wait condition with inter-process condition + #if /*EATHREAD_INTERPROCESS_CONDITION_SUPPORTED*/ 0 // Disabled because this code fails on most platforms. + { + ConditionParameters sharedConditionParams( false, NULL ); + TMWorkData workData( &sharedConditionParams ); // Inter-process. + Thread::Status status; + Thread threadProducer; + Thread threadConsumer; + + ThreadId threadIdProducer = threadProducer.Begin(ProducerFunction, &workData); + EATEST_VERIFY_MSG(threadIdProducer != kThreadIdInvalid, "Condition/Thread failure: Thread creation failed."); + + ThreadId threadIdConsumer = threadConsumer.Begin(ConsumerFunction, &workData); + EATEST_VERIFY_MSG(threadIdConsumer != kThreadIdInvalid, "Condition/Thread failure: Thread creation failed."); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000); + + // Wait for producer to quit. + workData.mbProducersShouldQuit = true; + + if(threadIdProducer != kThreadIdInvalid) + { + status = threadProducer.WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Condition/Thread failure: Wait for producer end failed."); + } + + EA::UnitTest::ThreadSleepRandom(2000, 2000); + + // Wait for consumers to quit. + workData.mbConsumersShouldQuit = true; + workData.mCondition.Signal(true); + if(threadIdConsumer != kThreadIdInvalid) + { + status = threadConsumer.WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Condition/Thread failure: Wait for consumer end failed."); + } + + EATEST_VERIFY_MSG(workData.mnTotalJobsCreated == workData.mnTotalJobsCompleted, "Condition failure: Not all consumer work was processed."); + } + #endif + + // Test conditional variable timeout explicitly by not sending a signal. + { + //::EA::EAMain::SetVerbosity(5); + ConditionParameters sharedConditionParams( true, NULL ); + TMWorkData workData( &sharedConditionParams ); // Inter-process. + workData.mnConditionTimeout = 3000; // timeout value has to be less than thread timeout value below. + Thread::Status status; + Thread threadProducer; + Thread threadConsumer; + + ThreadId threadIdProducer = threadProducer.Begin(ProducerFunction_DoesNotSignal, &workData); + EATEST_VERIFY_MSG(threadIdProducer != kThreadIdInvalid, "Condition/Thread failure: Thread creation failed."); + + ThreadId threadIdConsumer = threadConsumer.Begin(ConsumerFunction, &workData); + EATEST_VERIFY_MSG(threadIdConsumer != kThreadIdInvalid, "Condition/Thread failure: Thread creation failed."); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000); + + // Wait for producer to quit. + if(threadIdProducer != kThreadIdInvalid) + { + status = threadProducer.WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Condition/Thread failure: Wait for producer end failed."); + } + + EA::UnitTest::ThreadSleepRandom(2000, 2000); + + // Wait for consumers to quit. + workData.mbConsumersShouldQuit = true; + if(threadIdConsumer != kThreadIdInvalid) + { + status = threadConsumer.WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "Condition/Thread failure: Wait for consumer end failed."); + } + } + } + #endif + + return nErrorCount; +} + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadFutex.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadFutex.cpp new file mode 100644 index 00000000..2a8c7662 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadFutex.cpp @@ -0,0 +1,607 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(EA_PLATFORM_MICROSOFT) + #pragma warning(push, 0) + #include + #pragma warning(pop) +#endif + + + +#if defined(_MSC_VER) + #pragma warning(disable: 4996) // This function or variable may be unsafe / deprecated. +#endif + + +using namespace EA::Thread; + + +typedef intptr_t (*FutexTestThreadFunction)(void*); + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +/////////////////////////////////////////////////////////////////////////////// +// FWorkData +// +struct FWorkData +{ + volatile bool mbShouldQuit; + Futex mFutex; + char mUnused[7000]; // We intentionally put a big buffer between these. + int mThreadWithLock[kMaxConcurrentThreadCount]; + AtomicInt32 mThreadCount; + AtomicInt32 mnErrorCount; + + FWorkData() : mbShouldQuit(false), mFutex(), mThreadCount(0), mnErrorCount(0) { memset(mThreadWithLock, 0, sizeof(mThreadWithLock)); } + +private: + // Prevent default generation of these functions by not defining them + FWorkData(const FWorkData& rhs); // copy constructor + FWorkData& operator=(const FWorkData& rhs); // assignment operator +}; + + + + +/////////////////////////////////////////////////////////////////////////////// +// TestThreadFutexSingle +// +static int TestThreadFutexSingle() +{ + + // Formerly declared as a global because VC++ for XBox 360 was found to be possibly throwing + // away the atomic operations when it found that the futex was local to the function. + + // Now that Xbox 360 support has been removed, the futex has been tentatively moved back to + // local scope. Of course, if this problem manifests again, the change will be reversed. + Futex futexSingle; + + int nErrorCount(0); + + EA::UnitTest::ReportVerbosity(1, "\nSimple test...\n"); + + // Single-threaded tests + int nLockCount; + + EATEST_VERIFY_MSG(!futexSingle.HasLock(), "Futex failure."); + + nLockCount = futexSingle.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 0, "Futex failure."); + + futexSingle.Lock(); + nLockCount = futexSingle.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 1, "Futex failure."); + + EATEST_VERIFY_MSG(futexSingle.HasLock(), "Futex failure."); + + futexSingle.Lock(); + nLockCount = futexSingle.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 2, "Futex failure."); + + futexSingle.Unlock(); + nLockCount = futexSingle.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 1, "Futex failure."); + + futexSingle.Unlock(); + nLockCount = futexSingle.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 0, "Futex failure."); + + futexSingle.Lock(); + nLockCount = futexSingle.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 1, "Futex failure."); + + futexSingle.Unlock(); + nLockCount = futexSingle.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 0, "Futex failure."); + + EATEST_VERIFY_MSG(!futexSingle.HasLock(), "Futex failure."); + + return nErrorCount; +} + + + + +/////////////////////////////////////////////////////////////////////////////// +// FutexTestThreadFunction1 +// +static intptr_t FutexTestThreadFunction1(void* pvWorkData) +{ + int nErrorCount = 0; + FWorkData* pWorkData = (FWorkData*)pvWorkData; + const int32_t nThreadIndex = pWorkData->mThreadCount++; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "FutexTestThreadFunction1 created: %s, thread index %d\n", EAThreadThreadIdToString(threadId), nThreadIndex); + + while(!pWorkData->mbShouldQuit) + { + int nRecursiveLockCount(rand() % 3); + int i; + + for(i = 0; i < nRecursiveLockCount; ++i) + { + pWorkData->mFutex.Lock(); + pWorkData->mThreadWithLock[nThreadIndex]++; + + for(int j = 0; j < kMaxConcurrentThreadCount; ++j) + { + // Make sure this thread has the lock. + EATEST_VERIFY_F((j == nThreadIndex) || !pWorkData->mThreadWithLock[j], "Futex failure (thread %s)\n", EAThreadThreadIdToString(threadId)); + } + + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + for(; nRecursiveLockCount > 0; --nRecursiveLockCount) + { + pWorkData->mThreadWithLock[nThreadIndex]--; + + // Verify that N locks are set. + int nLockCount = pWorkData->mFutex.GetLockCount(); + + EATEST_VERIFY_MSG(nLockCount == nRecursiveLockCount, "Futex failure."); + + if(nLockCount != nRecursiveLockCount) + pWorkData->mbShouldQuit = true; + + // Verify the unlock result. + pWorkData->mFutex.Unlock(); + nLockCount = pWorkData->mFutex.GetLockCount(); + + EATEST_VERIFY_MSG((nRecursiveLockCount == 1) || (nLockCount != nRecursiveLockCount), "Futex failure."); + + if(nRecursiveLockCount > 1) // If we have remaining locks... + { + for(int j = 0; j < kMaxConcurrentThreadCount; ++j) + { + // Make sure this thread has the lock. + EATEST_VERIFY_F((j == nThreadIndex) || !pWorkData->mThreadWithLock[j], "Futex failure (thread %s)\n", EAThreadThreadIdToString(threadId)); + } + } + + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + if(nErrorCount) + pWorkData->mbShouldQuit = true; + else + EA::UnitTest::ThreadSleepRandom(100, 200); + } + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// FutexTestThreadFunction2 +// +// In this function we test Lock, attempting to detect memory synchronization +// problems that could occur with an incorrect Futex implementation. +// +static intptr_t FutexTestThreadFunction2(void* pvWorkData) +{ + int nErrorCount = 0; + FWorkData* pWorkData = (FWorkData*)pvWorkData; + const int32_t nThreadIndex = pWorkData->mThreadCount++; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "FutexTestThreadFunction2 created: %s, thread index %d\n", EAThreadThreadIdToString(threadId), nThreadIndex); + + while(!pWorkData->mbShouldQuit) + { + pWorkData->mFutex.Lock(); + pWorkData->mThreadWithLock[nThreadIndex] = 1; + + for(int j = 0; j < kMaxConcurrentThreadCount; ++j) + { + // Make sure this thread has the lock. + EATEST_VERIFY_F((j == nThreadIndex) || !pWorkData->mThreadWithLock[j], "Futex failure (thread %s)\n", EAThreadThreadIdToString(threadId)); + } + + pWorkData->mThreadWithLock[nThreadIndex] = 0; + pWorkData->mFutex.Unlock(); + ThreadCooperativeYield(); // Used by cooperative threading platforms. + + if(nErrorCount) + pWorkData->mbShouldQuit = true; + } + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// FutexTestThreadFunction3 +// +// In this function we test TryLock. +// +static intptr_t FutexTestThreadFunction3(void* pvWorkData) +{ + int nErrorCount = 0; + FWorkData* pWorkData = (FWorkData*)pvWorkData; + const int32_t nThreadIndex = pWorkData->mThreadCount++; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "FutexTestThreadFunction3 created: %s, thread index %d\n", EAThreadThreadIdToString(threadId), nThreadIndex); + + for(int i = 0; !pWorkData->mbShouldQuit; ++i) + { + // We make sure to mix TryLock usage with Lock usage. + bool bResult; + + if(((i % 4) != 0)) // 3/4 of the time, use TryLock, 1/4 of the time, use Lock. + bResult = pWorkData->mFutex.TryLock(); + else + { + pWorkData->mFutex.Lock(); + bResult = true; + } + + if(bResult) + { + pWorkData->mThreadWithLock[nThreadIndex] = 1; + + for(int j = 0; j < kMaxConcurrentThreadCount; ++j) + { + // Make sure this thread has the lock. + EATEST_VERIFY_F((j == nThreadIndex) || !pWorkData->mThreadWithLock[j], "Futex failure (thread %s)\n", EAThreadThreadIdToString(threadId)); + } + + pWorkData->mThreadWithLock[nThreadIndex] = 0; + pWorkData->mFutex.Unlock(); + + if(nErrorCount) + pWorkData->mbShouldQuit = true; + } + + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + +/////////////////////////////////////////////////////////////////////////////// +// TestThreadFutexMulti +// +static int TestThreadFutexMulti(FutexTestThreadFunction pFutexTestThreadFunction, int nThreadCount) +{ + int nErrorCount(0); + FWorkData* const pWorkData = new FWorkData; + Thread thread[kMaxConcurrentThreadCount]; + Thread::Status status; + int i; + + EA::UnitTest::ReportVerbosity(1, "Multithreaded test...\n"); + + if(nThreadCount > kMaxConcurrentThreadCount) + nThreadCount = kMaxConcurrentThreadCount; + + for(i = 0; i < nThreadCount; i++) + thread[i].Begin(pFutexTestThreadFunction, pWorkData); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000); + + pWorkData->mbShouldQuit = true; + + for(i = 0; i < nThreadCount; i++) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status == EA::Thread::Thread::kStatusEnded, "Futex/Thread failure: Thread(s) didn't end."); + } + + nErrorCount += (int)pWorkData->mnErrorCount; + + delete pWorkData; + + return nErrorCount; +} + + +/////////////////////////////////////////////////////////////////////////////// +// TestThreadFutexSpeed +// +static int TestThreadFutexSpeed() +{ + int nErrorCount = 0; + size_t i; + uint64_t t0, t1, tDelta; + const size_t kLoopCount = 1000000; + + EA::UnitTest::ReportVerbosity(1, "\nSpeed test...\n"); + + ////////////////////////////////////////////////// + // Futex + { + Futex f; + + t0 = EA::StdC::Stopwatch::GetCPUCycle(); + f.Lock(); + f.Unlock(); + t1 = EA::StdC::Stopwatch::GetCPUCycle(); + + t0 = EA::StdC::Stopwatch::GetCPUCycle(); + + for(i = 0; i < kLoopCount; i++) + { + f.Lock(); + f.Unlock(); + } + + t1 = EA::StdC::Stopwatch::GetCPUCycle(); + tDelta = t1 - t0; + + EA::UnitTest::ReportVerbosity(1, "Futex time (ticks): %" PRIu64 "\n", tDelta); + } + ////////////////////////////////////////////////// + + + ////////////////////////////////////////////////// + // Mutex + { + EA::Thread::Mutex m; + + t0 = EA::StdC::Stopwatch::GetCPUCycle(); + m.Lock(); + m.Unlock(); + t1 = EA::StdC::Stopwatch::GetCPUCycle(); + + t0 = EA::StdC::Stopwatch::GetCPUCycle(); + + for(i = 0; i < kLoopCount; i++) + { + m.Lock(); + m.Unlock(); + } + + t1 = EA::StdC::Stopwatch::GetCPUCycle(); + tDelta = t1 - t0; + + EA::UnitTest::ReportVerbosity(1, "Mutex time (ticks): %" PRIu64 "\n", tDelta); + } + ////////////////////////////////////////////////// + + + #if defined(EA_PLATFORM_MICROSOFT) && !defined(EA_PLATFORM_WINDOWS_PHONE) && !(defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)) + + ////////////////////////////////////////////////// + // HMUTEX + { + HANDLE hMutex = CreateMutexA(NULL, false, NULL); + + if (hMutex != NULL) + { + WaitForSingleObject(hMutex, INFINITE); + ReleaseMutex(hMutex); + + t0 = EA::StdC::Stopwatch::GetCPUCycle(); + + for(i = 0; i < kLoopCount; i++) + { + WaitForSingleObject(hMutex, INFINITE); + ReleaseMutex(hMutex); + } + + t1 = EA::StdC::Stopwatch::GetCPUCycle(); + tDelta = t1 - t0; + + CloseHandle(hMutex); + + EA::UnitTest::ReportVerbosity(1, "Windows HMUTEX time (ticks): %" PRIu64 "\n", tDelta); + } + } + ////////////////////////////////////////////////// + + ////////////////////////////////////////////////// + // Critical section + { + CRITICAL_SECTION cs; + InitializeCriticalSection(&cs); + + EnterCriticalSection(&cs); + LeaveCriticalSection(&cs); + + t0 = EA::StdC::Stopwatch::GetCPUCycle(); + + for(i = 0; i < kLoopCount; i++) + { + EnterCriticalSection(&cs); + LeaveCriticalSection(&cs); + } + + t1 = EA::StdC::Stopwatch::GetCPUCycle(); + tDelta = t1 - t0; + + DeleteCriticalSection(&cs); + + EA::UnitTest::ReportVerbosity(1, "Windows CriticalSection time (ticks): %" PRIu64 "\n", tDelta); + } + ////////////////////////////////////////////////// + + + #endif + + return nErrorCount; +} + + +static int TestThreadFutexRegressions() +{ + int nErrorCount(0); + + #if EA_THREADS_AVAILABLE && EATHREAD_DEBUG_FUTEX_HANG_ENABLED + { + // Test the ability of Futex to report the callstack of another thread holding a futex. + struct FutexCallstackTestThread : public EA::Thread::IRunnable + { + ThreadParameters mThreadParams; // + EA::Thread::Thread mThread; // The Thread object. + EA::Thread::Futex* mpFutex; // A Futex. + + FutexCallstackTestThread() : mThreadParams(), mThread(), mpFutex(NULL) {} + FutexCallstackTestThread(const FutexCallstackTestThread&){} // Avoid compiler warnings. + void operator=(const FutexCallstackTestThread&){} // Avoid compiler warnings. + + intptr_t Run(void*) + { + if(EA::StdC::Strstr(mThreadParams.mpName, "0")) // If we are thread 0... + { + mpFutex->Lock(); + mpFutex->Lock(); + EA::Thread::ThreadSleep(4000); + mpFutex->Unlock(); + mpFutex->Unlock(); + } + else + { + mpFutex->Lock(); // This should result in a printf tracing two thread 0 lock callstacks. + mpFutex->Unlock(); + } + return 0; + } + }; + + FutexCallstackTestThread thread[2]; + EA::Thread::Futex futex; + + for(int i = 0; i < 3; i++) + { + thread[0].mThreadParams.mpName = "FutexTest0"; + thread[0].mpFutex = &futex; + thread[0].mThread.Begin(&thread[0], NULL, &thread[0].mThreadParams); + + EA::UnitTest::ThreadSleep(300); + + thread[1].mThreadParams.mpName = "FutexTest1"; + thread[1].mpFutex = &futex; + thread[1].mThread.Begin(&thread[1], NULL, &thread[1].mThreadParams); + + EA::UnitTest::ThreadSleep(5000); + + for(int i = 0; i < 2; i++) + thread[i].mThread.WaitForEnd(); + } + } + #endif + + return nErrorCount; +} + + + +#define EATHREAD_DEBUG_FUTEX_HAMMER_ENABLED 1 +// Check multithreaded correctness of Futex. +// Here we hammer on the futex via multiple threads while incrementing non atomic counter +// if the lock ever fails the global count will be incorrect. + +volatile uint32_t gCommonCount = 0; + +static int TestThreadFutexHammer() +{ + int nErrorCount(0); + + #if EA_THREADS_AVAILABLE && EATHREAD_DEBUG_FUTEX_HAMMER_ENABLED + { + static const int NUM_SPINNING_THREADS = 4; + static const uint32_t MAX_NUM_LOOPS = 1 << 5; + + // Test the ability of Futex to report the callstack of another thread holding a futex. + struct FutexCallstackTestThread : public EA::Thread::IRunnable + { + ThreadParameters mThreadParams; // + EA::Thread::Thread mThread; // The Thread object. + EA::Thread::Futex* mpFutex; // A Futex. + uint32_t mThreadLocalId; + + FutexCallstackTestThread() : mThreadParams(), mThread(), mpFutex(NULL) {} + FutexCallstackTestThread(const FutexCallstackTestThread&) {} // Avoid compiler warnings. + void operator=(const FutexCallstackTestThread&) {} // Avoid compiler warnings. + + intptr_t Run(void*) + { + for (uint32_t i = 0; i < MAX_NUM_LOOPS; i++) + { + mpFutex->Lock(); + gCommonCount++; + mpFutex->Unlock(); + } + return 0; + } + }; + + + FutexCallstackTestThread thread[NUM_SPINNING_THREADS]; + EA::Thread::Futex futex; + gCommonCount = 0; // for multiple runs + + for(int i = 0; i < NUM_SPINNING_THREADS; i++) + { + thread[i].mpFutex = &futex; + thread[i].mThread.Begin(&thread[i], NULL, &thread[i].mThreadParams); + } + + EA::UnitTest::ThreadSleep(13); + for(int i = 0; i < NUM_SPINNING_THREADS; i++) + thread[i].mThread.WaitForEnd(); + + EATEST_VERIFY_MSG(gCommonCount == NUM_SPINNING_THREADS * MAX_NUM_LOOPS, "Multithreaded futex test failed, non atomic counter is incorrect"); + } + #endif + + return nErrorCount; +} + + + + +/////////////////////////////////////////////////////////////////////////////// +// TestThreadFutex +// +int TestThreadFutex() +{ + int nErrorCount(0); + + nErrorCount += TestThreadFutexSingle(); + nErrorCount += TestThreadFutexSpeed(); + nErrorCount += TestThreadFutexRegressions(); + + // hammer on the futex a few times + for(int j=0; j <1;j++) + { + nErrorCount += TestThreadFutexHammer(); + } + + #if EA_THREADS_AVAILABLE + nErrorCount += TestThreadFutexMulti(FutexTestThreadFunction1, 4); + nErrorCount += TestThreadFutexMulti(FutexTestThreadFunction2, 4); + nErrorCount += TestThreadFutexMulti(FutexTestThreadFunction3, 4); + #endif + + return nErrorCount; +} + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadMutex.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadMutex.cpp new file mode 100644 index 00000000..0b120da2 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadMutex.cpp @@ -0,0 +1,233 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include + + +using namespace EA::Thread; + + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +struct MWorkData +{ + volatile bool mbShouldQuit; + Mutex mMutex; + volatile int mnExpectedValue; + volatile int mnCalculatedValue; + AtomicInt32 mnErrorCount; + + MWorkData() : mbShouldQuit(false), mMutex(NULL, true), + mnExpectedValue(0), mnCalculatedValue(0), + mnErrorCount(0) {} + + // Prevent default generation of these functions by not defining them +private: + MWorkData(const MWorkData& rhs); // copy constructor + MWorkData& operator=(const MWorkData& rhs); // assignment operator +}; + + +static intptr_t MutexTestThreadFunction(void* pvWorkData) +{ + int nErrorCount = 0; + MWorkData* pWorkData = (MWorkData*)pvWorkData; + const ThreadId threadId = GetThreadId(); + const int currentProcessor = GetThreadProcessor(); + + EA::UnitTest::ReportVerbosity(1, "Mutex test function created: %s (currently on processor %d).\n", EAThreadThreadIdToString(threadId), currentProcessor); + + while(!pWorkData->mbShouldQuit) + { + const int nRecursiveLockCount(rand() % 3); + int i, nLockResult, nLocks = 0; + + for(i = 0; i < nRecursiveLockCount; i++) + { + // Do a lock but allow for the possibility of occasional timeout. + ThreadTime expectedTime(GetThreadTime() + 1000); + nLockResult = pWorkData->mMutex.Lock(expectedTime); + + // Verify the lock succeeded or timed out. + EATEST_VERIFY_MSG(nLockResult != Mutex::kResultError, "Mutex failure."); + + // Verify the timeout of the lock + if (nLockResult == Mutex::kResultTimeout) + { + ThreadTime currentTime(GetThreadTime()); + EATEST_VERIFY_MSG(currentTime >= expectedTime, "Mutex timeout failure."); + } + + if(nLockResult > 0) // If there was no timeout... + { + nLocks++; + + // What we do here is spend some time manipulating mnExpectedValue and mnCalculatedValue + // while we have the lock. We change their values in a predicable way but before we + // are done mnCalculatedValue has been incremented by one and both values are equal. + const uintptr_t x = (uintptr_t)pWorkData; + + pWorkData->mnExpectedValue = -1; + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnCalculatedValue *= 50; + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnCalculatedValue /= (int)(((x + 1) / x) * 50); // This will always be the same as simply '/= 50'. + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnCalculatedValue += 1; + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnExpectedValue = pWorkData->mnCalculatedValue; + + EATEST_VERIFY_MSG(pWorkData->mnCalculatedValue == pWorkData->mnExpectedValue, "Mutex failure."); + } + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + while(nLocks > 0) + { + // Verify that HasLock returns the expected value. + EATEST_VERIFY_MSG(pWorkData->mMutex.HasLock(), "Mutex failure."); + + // Verify that N locks are set. + nLockResult = pWorkData->mMutex.GetLockCount(); + EATEST_VERIFY_MSG(nLockResult == nLocks, "Mutex failure."); + + // Verify the unlock result. + nLockResult = pWorkData->mMutex.Unlock(); + EATEST_VERIFY_MSG(nLockResult >= nLocks-1, "Mutex failure."); + + nLocks--; + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + // If EAT_ASSERT_ENABLED is 1, Mutex::HasLock() tests to see that that not only is the mutex + // locked, but that the lock is owned by the calling thread. + #if EAT_ASSERT_ENABLED + // Verify that HasLock returns the expected value. + EATEST_VERIFY_MSG(!pWorkData->mMutex.HasLock(), "Mutex failure."); + #endif + + EA::UnitTest::ThreadSleepRandom(100, 200); + } + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + +int TestThreadMutex() +{ + int nErrorCount(0); + + { // ctor tests + // We test various combinations of Mutex ctor and MutexParameters. + // MutexParameters(bool bIntraProcess = true, const char* pName = NULL); + // Mutex(const MutexParameters* pMutexParameters = NULL, bool bDefaultParameters = true); + + MutexParameters mp1(true, NULL); + MutexParameters mp2(true, "mp2"); + MutexParameters mp3(false, NULL); + MutexParameters mp4(false, "mp4WithNameThatIsMoreThan32Characters"); // testing kettle mutex name limitations + + Mutex mutex1(&mp1, false); + Mutex mutex2(&mp2, false); + Mutex mutex3(&mp3, false); + Mutex mutex4(&mp4, false); + Mutex mutex5(NULL, true); + Mutex mutex6(NULL, false); + mutex6.Init(&mp1); + + AutoMutex am1(mutex1); + AutoMutex am2(mutex2); + AutoMutex am3(mutex3); + AutoMutex am4(mutex4); + AutoMutex am5(mutex5); + AutoMutex am6(mutex6); + } + + { // Single-threaded tests + Mutex mutex(NULL, true); + + int nLockCount; + + nLockCount = mutex.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 0, "Mutex failure."); + + nLockCount = mutex.Lock(); + EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure."); + + nLockCount = mutex.Lock(); + EATEST_VERIFY_MSG(nLockCount == 2, "Mutex failure."); + + nLockCount = mutex.Unlock(); + EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure."); + + nLockCount = mutex.GetLockCount(); + EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure."); + + nLockCount = mutex.Unlock(); + EATEST_VERIFY_MSG(nLockCount == 0, "Mutex failure."); + + nLockCount = mutex.Lock(); + EATEST_VERIFY_MSG(nLockCount == 1, "Mutex failure."); + + nLockCount = mutex.Unlock(); + EATEST_VERIFY_MSG(nLockCount == 0, "Mutex failure."); + } + + #ifdef EA_PLATFORM_PS4 + { + // Validate the amount of system resources being consumed by a Sony Mutex without a mutex name. It appears the + // Sony OS allocates 32 bytes per mutex regardless if the mutex name is empty or not. EAThread currently checks + // if the mutex name can be omited in an effort to save memory. + MutexParameters mp(false, nullptr); + + Mutex mutexes[4000]; + for(auto& m : mutexes) + m.Init(&mp); + } + #endif + + #if EA_THREADS_AVAILABLE + + { // Multithreaded test + MWorkData workData; + + const int kThreadCount(kMaxConcurrentThreadCount); + Thread thread[kThreadCount]; + Thread::Status status; + int i; + + for(i = 0; i < kThreadCount; i++) + thread[i].Begin(MutexTestThreadFunction, &workData); + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*1000, gTestLengthSeconds*1000); + + workData.mbShouldQuit = true; + + for(i = 0; i < kThreadCount; i++) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + + EATEST_VERIFY_MSG(status == EA::Thread::Thread::kStatusEnded, "Mutex/Thread failure: Thread(s) didn't end."); + } + + nErrorCount += (int)workData.mnErrorCount; + } + + #endif + + return nErrorCount; +} + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWMutex.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWMutex.cpp new file mode 100644 index 00000000..b40ac7cc --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWMutex.cpp @@ -0,0 +1,217 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include + + +using namespace EA::Thread; + + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +struct RWMWorkData +{ + volatile bool mbShouldQuit; + RWMutex mRWMutex; + volatile int mnExpectedValue; + volatile int mnCalculatedValue; + AtomicInt32 mnErrorCount; + + RWMWorkData() : mbShouldQuit(false), mRWMutex(NULL, true), mnExpectedValue(0), + mnCalculatedValue(0), mnErrorCount(0) {} + + // define copy ctor and assignment operator + // so the compiler does define them intrisically + RWMWorkData(const RWMWorkData& rhs); // copy constructor + RWMWorkData& operator=(const RWMWorkData& rhs); // assignment operator +}; + + +static intptr_t ReaderFunction(void* pvWorkData) +{ + int nErrorCount = 0; + RWMWorkData* pWorkData = (RWMWorkData*)pvWorkData; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "RWMutex reader test function created: %s\n", EAThreadThreadIdToString(threadId)); + + while(!pWorkData->mbShouldQuit) + { + const int nRecursiveLockCount(rand() % 3); + int i, nLockResult, nLocks = 0; + + for(i = 0; i < nRecursiveLockCount; i++) + { + // Do a lock but allow for the possibility of occasional timeout. + nLockResult = pWorkData->mRWMutex.Lock(RWMutex::kLockTypeRead, GetThreadTime() + 20); + + EATEST_VERIFY_MSG(nLockResult != RWMutex::kResultError, "RWMutex failure"); + + if(nLockResult > 0) + { + nLocks++; + + EA::UnitTest::ReportVerbosity(2, "CValue = %d; EValue = %d\n", pWorkData->mnCalculatedValue, pWorkData->mnExpectedValue); + EATEST_VERIFY_MSG(pWorkData->mnCalculatedValue == pWorkData->mnExpectedValue, "RWMutex failure"); + } + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + while(nLocks > 0) + { + // Verify no write locks are set. + nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeWrite); + EATEST_VERIFY_MSG(nLockResult == 0, "RWMutex failure"); + + // Verify at least N read locks are set. + nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeRead); + EATEST_VERIFY_MSG(nLockResult >= nLocks, "RWMutex failure"); + + // Verify there is one less read lock set. + nLockResult = pWorkData->mRWMutex.Unlock(); + EATEST_VERIFY_MSG(nLockResult >= nLocks-1, "RWMutex failure"); + + nLocks--; + ThreadCooperativeYield(); // Used by cooperative threading platforms. + } + + EA::UnitTest::ThreadSleepRandom(100, 200); + } + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + +static intptr_t WriterFunction(void* pvWorkData) +{ + int nErrorCount = 0; + RWMWorkData* pWorkData = (RWMWorkData*)pvWorkData; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "RWMutex writer test function created: %s\n", EAThreadThreadIdToString(threadId)); + + while(!pWorkData->mbShouldQuit) + { + // Do a lock but allow for the possibility of occasional timeout. + int nLockResult = pWorkData->mRWMutex.Lock(RWMutex::kLockTypeWrite, GetThreadTime() + 30); + EATEST_VERIFY_MSG(nLockResult != RWMutex::kResultError, "RWMutex failure"); + + ThreadCooperativeYield(); // Used by cooperative threading platforms. + + if(nLockResult > 0) + { + // Verify exactly one write lock is set. + nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeWrite); + EATEST_VERIFY_MSG(nLockResult == 1, "RWMutex failure"); + + // What we do here is spend some time manipulating mnExpectedValue and mnCalculatedValue + // while we have the write lock. We change their values in a predicable way but before + // we are done mnCalculatedValue has been incremented by one and both values are equal. + const uintptr_t x = (uintptr_t)pWorkData; + + pWorkData->mnExpectedValue = -1; + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnCalculatedValue *= 50; + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnCalculatedValue /= (int)(((x + 1) / x) * 50); // This will always be the same as simply '/= 50'. + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnCalculatedValue += 1; + EA::UnitTest::ThreadSleepRandom(10, 20); + pWorkData->mnExpectedValue = pWorkData->mnCalculatedValue; + + // Verify no read locks are set. + nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeRead); + EATEST_VERIFY_MSG(nLockResult == 0, "RWMutex failure"); + + // Verify exactly one write lock is set. + nLockResult = pWorkData->mRWMutex.GetLockCount(RWMutex::kLockTypeWrite); + EATEST_VERIFY_MSG(nLockResult == 1, "RWMutex failure"); + + // Verify there are now zero write locks set. + nLockResult = pWorkData->mRWMutex.Unlock(); + EATEST_VERIFY_MSG(nLockResult == 0, "RWMutex failure"); + + EA::UnitTest::ThreadSleepRandom(400, 800); + } + } + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + +int TestThreadRWMutex() +{ + int nErrorCount(0); + + // Be careful adding when adding more mutexes to this test as the the CTR runs out of + // resources pretty early. + { + RWMutexParameters mp1(true, NULL); + RWMutexParameters mp2(true, "mp2"); + + { + RWMutex mutex1(&mp1, false); + RWMutex mutex2(&mp2, false); + } + + { + RWMutex mutex5(NULL, true); + RWMutex mutex6(NULL, false); + mutex6.Init(&mp1); + } + + { + RWMutex mutex1(&mp1, false); + AutoRWMutex am1(mutex1, RWMutex::kLockTypeRead); + } + } + + #if EA_THREADS_AVAILABLE + + { + RWMWorkData workData; + + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + { + if(i < (kThreadCount * 3 / 4)) + threadId[i] = thread[i].Begin(ReaderFunction, &workData); + else + threadId[i] = thread[i].Begin(WriterFunction, &workData); + } + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 1000, gTestLengthSeconds * 1000); + + workData.mbShouldQuit = true; + + for(int i(0); i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWMutex/Thread failure: status == kStatusRunning."); + } + } + + nErrorCount += (int)workData.mnErrorCount; + } + + #endif + + return nErrorCount; +} + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWSemaLock.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWSemaLock.cpp new file mode 100644 index 00000000..60bff93a --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWSemaLock.cpp @@ -0,0 +1,259 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + + +#include "TestThread.h" +#include +#include +#include +#include + + + +const int kThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + + +/////////////////////////////////////////////////////////////////////////////// +// RWSTestType +// +enum RWSTestType +{ + kRWSTestTypeStandard, + kRWSTestTypeAllWriters, + kRWSTestTypeAllReaders, + kRWSTestTypeMostlyWriters, + kRWSTestTypeMostlyReaders, + kRWSTestTypeCount +}; + + +/////////////////////////////////////////////////////////////////////////////// +// RWSemaWorkData +// +struct RWSemaWorkData +{ + volatile bool mbShouldQuit; + EA::Thread::RWSemaLock mRWSemaLock; + volatile int mnWriterCount; + EA::Thread::AtomicInt32 mnErrorCount; + EA::Thread::AtomicInt32 mnCurrentTestType; + + RWSemaWorkData() + : mbShouldQuit(false) + , mRWSemaLock() + , mnWriterCount(0) + , mnErrorCount(0) + , mnCurrentTestType(kRWSTestTypeStandard) {} + +private: + RWSemaWorkData(const RWSemaWorkData& rhs); + RWSemaWorkData& operator=(const RWSemaWorkData& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// RWSThreadFunction +// +static intptr_t RWSThreadFunction(void* pvWorkData) +{ + using namespace EA::Thread; + + RWSemaWorkData* const pWorkData = (RWSemaWorkData*)pvWorkData; + + ThreadId threadId = GetThreadId(); + EA::UnitTest::ReportVerbosity(1, "RWSemaLock test function created: %s\n", EAThreadThreadIdToString(threadId)); + + int nErrorCount = 0; + + while(!pWorkData->mbShouldQuit) + { + int nWriteLockChance = 0; + const RWSTestType testType = (RWSTestType)pWorkData->mnCurrentTestType.GetValue(); + + switch (testType) + { + default: + case kRWSTestTypeStandard: + nWriteLockChance = 20; + break; + + case kRWSTestTypeAllWriters: + nWriteLockChance = 1000; + break; + + case kRWSTestTypeAllReaders: + nWriteLockChance = 0; + break; + + case kRWSTestTypeMostlyWriters: + nWriteLockChance = 700; + break; + + case kRWSTestTypeMostlyReaders: + nWriteLockChance = 5; + break; + } + + const bool bShouldWrite = ((rand() % 1000) < nWriteLockChance); + + if(bShouldWrite) + { + AutoSemaWriteLock _(pWorkData->mRWSemaLock); + pWorkData->mnWriterCount++; + EA::UnitTest::ThreadSleepRandom(2, 10); + pWorkData->mnWriterCount--; + } + else + { + AutoSemaReadLock _(pWorkData->mRWSemaLock); + EATEST_VERIFY_MSG(pWorkData->mnWriterCount == 0, "ReadLock is held, there should be no active WriteLocks."); + } + } + + pWorkData->mnErrorCount.SetValue(nErrorCount); + + return nErrorCount; +} + + +// NOTE(rparolin): +// This exists to introduce test-only functionality for the RWSemaLock. We can add these functions here because we +// guarantee they will not be called in a concurrent context and they simplify validation of assumption of the lock. +struct TestRWSemaLock : public EA::Thread::RWSemaLock +{ + TestRWSemaLock() = default; + TestRWSemaLock(const TestRWSemaLock&) = delete; + TestRWSemaLock(TestRWSemaLock&&) = delete; + TestRWSemaLock& operator=(const TestRWSemaLock&) = delete; + TestRWSemaLock& operator=(TestRWSemaLock&&) = delete; + + bool IsReadLocked() + { + Status status; + status.data = mStatus.GetValue(); + return status.readers > 0; + } + + bool IsWriteLocked() + { + Status status; + status.data = mStatus.GetValue(); + return status.writers > 0; + } +}; + + +int TestThreadRWSemaLock() +{ + using namespace EA::Thread; + + int nErrorCount = 0; + + { // RWSemaLock -- Basic single-threaded test. + + TestRWSemaLock rwSemaLock; // There are no construction parameters. + + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + + rwSemaLock.ReadTryLock(); + EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.WriteTryLock(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + + rwSemaLock.ReadLock(); + EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + + rwSemaLock.ReadUnlock(); + EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + + rwSemaLock.ReadUnlock(); + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + + rwSemaLock.WriteTryLock(); + EATEST_VERIFY_MSG(rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.ReadTryLock(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.WriteTryLock(), "RWSemaLock failure"); + } + + + { // AutoRWSemaLock -- Basic single-threaded test. + TestRWSemaLock rwSemaLock; // There are no construction parameters. + + { //Special scope just for the AutoRWSemaLock + AutoSemaReadLock autoRWSemaLock1(rwSemaLock); + AutoSemaReadLock autoRWSemaLock2(rwSemaLock); + + EATEST_VERIFY_MSG(rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.WriteTryLock(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + } + + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + + { //Special scope just for the AutoRWSemaLock + AutoSemaWriteLock autoRWSemaLock(rwSemaLock); + + EATEST_VERIFY_MSG(rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.ReadTryLock(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + } + + EATEST_VERIFY_MSG(!rwSemaLock.IsReadLocked(), "RWSemaLock failure"); + EATEST_VERIFY_MSG(!rwSemaLock.IsWriteLocked(), "RWSemaLock failure"); + } + + + #if EA_THREADS_AVAILABLE + + { // Multithreaded test + + RWSemaWorkData workData; + + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + threadId[i] = thread[i].Begin(RWSThreadFunction, &workData); + + for(int e = 0; e < kRWSTestTypeCount; e++) + { + workData.mnCurrentTestType.SetValue(e); + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500); + } + + workData.mbShouldQuit = true; + for(int t(0); t < kThreadCount; t++) + { + if(threadId[t] != kThreadIdInvalid) + { + status = thread[t].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSemalock/Thread failure: status == kStatusRunning.\n"); + } + } + + nErrorCount += (int)workData.mnErrorCount; + } + + #endif + + return nErrorCount; +} + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWSpinLock.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWSpinLock.cpp new file mode 100644 index 00000000..8bd75bb2 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadRWSpinLock.cpp @@ -0,0 +1,477 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include +#include + + +const int kThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +/////////////////////////////////////////////////////////////////////////////// +// RWSTestType +// +enum RWSTestType +{ + kRWSTestTypeStandard, + kRWSTestTypeAllWriters, + kRWSTestTypeAllReaders, + kRWSTestTypeMostlyWriters, + kRWSTestTypeMostlyReaders, + kRWSTestTypeCount +}; + + +/////////////////////////////////////////////////////////////////////////////// +// RWSWorkData +// +struct RWSWorkData +{ + std::atomic mbShouldQuit; + EA::Thread::RWSpinLock mRWSpinLock; + EA::Thread::RWSpinLockW mRWSpinLockW; + volatile int mnExpectedValue; + volatile int mnCalculatedValue; + EA::Thread::AtomicInt32 mnErrorCount; + EA::Thread::AtomicInt32 mnCurrentTestType; + + RWSWorkData() : mbShouldQuit(false), mRWSpinLock(), mRWSpinLockW(), mnExpectedValue(0), mnCalculatedValue(0), + mnErrorCount(0), mnCurrentTestType(kRWSTestTypeStandard) {} + +private: + RWSWorkData(const RWSWorkData& rhs); + RWSWorkData& operator=(const RWSWorkData& rhs); +}; + + +/////////////////////////////////////////////////////////////////////////////// +// RWSWThreadFunction +// +static intptr_t RWSWThreadFunction(void* pvWorkData) +{ + using namespace EA::Thread; + + RWSWorkData* const pWorkData = (RWSWorkData*)pvWorkData; + + ThreadId threadId = GetThreadId(); + EA::UnitTest::ReportVerbosity(1, "RWSpinLockW test function created: %s\n", EAThreadThreadIdToString(threadId)); + + int nErrorCount = 0; + + while(!pWorkData->mbShouldQuit) + { + int nWriteLockChance = 0; + const RWSTestType testType = (RWSTestType)pWorkData->mnCurrentTestType.GetValue(); + + switch (testType) + { + default: + case kRWSTestTypeStandard: + nWriteLockChance = 20; + break; + + case kRWSTestTypeAllWriters: + nWriteLockChance = 1000; + break; + + case kRWSTestTypeAllReaders: + nWriteLockChance = 0; + break; + + case kRWSTestTypeMostlyWriters: + nWriteLockChance = 700; + break; + + case kRWSTestTypeMostlyReaders: + nWriteLockChance = 5; + break; + } + + const bool bShouldWrite = ((rand() % 1000) < nWriteLockChance); + + if(bShouldWrite) + { + pWorkData->mRWSpinLockW.WriteLock(); + + EATEST_VERIFY_MSG(!pWorkData->mRWSpinLockW.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n"); + EATEST_VERIFY_MSG(pWorkData->mRWSpinLockW.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n"); + + pWorkData->mRWSpinLockW.WriteUnlock(); + + ThreadCooperativeYield(); + } + else + { + const int nRecursiveLockCount = 1; // Disabled because we are not recursive: (rand() % 10) ? 1 : 2; + + int nLocks = 0; + + for(int i = 0; i < nRecursiveLockCount; i++) + { + pWorkData->mRWSpinLockW.ReadLock(); + nLocks++; + + ThreadCooperativeYield(); + } + + // Disabled because we are not recursive: + // if((rand() % 10) == 0) + // { + // if(pWorkData->mRWSpinLockW.ReadTryLock()) + // nLocks++; + // } + + while(nLocks > 0) + { + EATEST_VERIFY_MSG(pWorkData->mRWSpinLockW.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n"); + EATEST_VERIFY_MSG(!pWorkData->mRWSpinLockW.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n"); + + pWorkData->mRWSpinLockW.ReadUnlock(); + nLocks--; + + ThreadCooperativeYield(); + } + } + + //if((rand() % 1000) < 3) + // EA::UnitTest::ThreadSleepRandom(50, 100); + } + + pWorkData->mnErrorCount.SetValue(nErrorCount); + + return nErrorCount; +} + + + +static int TestThreadRWSpinLockW() +{ + using namespace EA::Thread; + + int nErrorCount = 0; + + { // RWSpinLockW -- Basic single-threaded test. + + RWSpinLockW rwSpinLock; // There are no construction parameters. + + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure"); + + rwSpinLock.ReadTryLock(); + EATEST_VERIFY_MSG( rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure"); + + // Disabled because we don't support read lock recursion. + // rwSpinLock.ReadLock(); + // EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + + // rwSpinLock.ReadUnlock(); + // EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + + rwSpinLock.ReadUnlock(); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + + rwSpinLock.WriteTryLock(); + EATEST_VERIFY_MSG( rwSpinLock.IsWriteLocked(),"RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure"); + } + + + { // AutoRWSpinLockW -- Basic single-threaded test. + RWSpinLockW rwSpinLock; // There are no construction parameters. + + { //Special scope just for the AutoRWSpinLockW + AutoRWSpinLockW autoRWSpinLockW1(rwSpinLock, AutoRWSpinLockW::kLockTypeRead); + AutoRWSpinLockW autoRWSpinLockW2(rwSpinLock, AutoRWSpinLockW::kLockTypeRead); + + EATEST_VERIFY_MSG( rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure"); + } + + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure"); + + { //Special scope just for the AutoRWSpinLockW + AutoRWSpinLockW autoRWSpinLockW(rwSpinLock, AutoRWSpinLockW::kLockTypeWrite); + + EATEST_VERIFY_MSG( rwSpinLock.IsWriteLocked(),"RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + } + + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLockW failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLockW failure"); + } + + + #if EA_THREADS_AVAILABLE + + { // Multithreaded test + + RWSWorkData workData; + + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + threadId[i] = thread[i].Begin(RWSWThreadFunction, &workData); + + for(int e = 0; e < kRWSTestTypeCount; e++) + { + workData.mnCurrentTestType.SetValue(e); + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500); + } + + workData.mbShouldQuit = true; + + for(int t(0); t < kThreadCount; t++) + { + if(threadId[t] != kThreadIdInvalid) + { + status = thread[t].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSpinlock/Thread failure: status == kStatusRunning.\n"); + } + } + + nErrorCount += (int)workData.mnErrorCount; + } + + #endif + + return nErrorCount; +} + + + +/////////////////////////////////////////////////////////////////////////////// +// RWSThreadFunction +// +static intptr_t RWSThreadFunction(void* pvWorkData) +{ + using namespace EA::Thread; + + RWSWorkData* const pWorkData = (RWSWorkData*)pvWorkData; + + ThreadId threadId = GetThreadId(); + EA::UnitTest::ReportVerbosity(1, "RWSpinLock test function created: %s\n", EAThreadThreadIdToString(threadId)); + + int nErrorCount = 0; + + while(!pWorkData->mbShouldQuit) + { + int nWriteLockChance = 0; + const RWSTestType testType = (RWSTestType)pWorkData->mnCurrentTestType.GetValue(); + + switch (testType) + { + default: + case kRWSTestTypeStandard: + nWriteLockChance = 20; + break; + + case kRWSTestTypeAllWriters: + nWriteLockChance = 1000; + break; + + case kRWSTestTypeAllReaders: + nWriteLockChance = 0; + break; + + case kRWSTestTypeMostlyWriters: + nWriteLockChance = 700; + break; + + case kRWSTestTypeMostlyReaders: + nWriteLockChance = 5; + break; + } + + const bool bShouldWrite = ((rand() % 1000) < nWriteLockChance); + + if(bShouldWrite) + { + pWorkData->mRWSpinLock.WriteLock(); + + EATEST_VERIFY_MSG(!pWorkData->mRWSpinLock.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n"); + EATEST_VERIFY_MSG(pWorkData->mRWSpinLock.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n"); + + pWorkData->mRWSpinLock.WriteUnlock(); + + ThreadCooperativeYield(); + } + else + { + const int nRecursiveLockCount = (rand() % 10) ? 1 : 2; + + int nLocks = 0; + + for(int i = 0; i < nRecursiveLockCount; i++) + { + pWorkData->mRWSpinLock.ReadLock(); + nLocks++; + + ThreadCooperativeYield(); + } + + if((rand() % 10) == 0) + { + if(pWorkData->mRWSpinLock.ReadTryLock()) + nLocks++; + } + + while(nLocks > 0) + { + const int32_t n = pWorkData->mRWSpinLock.mValue; (void)n; + + // It turns out IsReadLocked has a bug and can return a false negative. + // EATEST_VERIFY_MSG(pWorkData->mRWSpinLock.IsReadLocked(), "RWSpinlock failure: IsReadLocked\n"); + // EATEST_VERIFY_MSG(!pWorkData->mRWSpinLock.IsWriteLocked(), "RWSpinlock failure: IsWriteLocked\n"); + + pWorkData->mRWSpinLock.ReadUnlock(); + nLocks--; + + ThreadCooperativeYield(); + } + } + } + + pWorkData->mnErrorCount.SetValue(nErrorCount); + + return nErrorCount; +} + + + +int TestThreadRWSpinLock() +{ + using namespace EA::Thread; + + int nErrorCount = 0; + + { // RWSpinLock -- Basic single-threaded test. + + RWSpinLock rwSpinLock; // There are no construction parameters. + + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + + rwSpinLock.ReadTryLock(); + EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + + rwSpinLock.ReadLock(); + EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + + rwSpinLock.ReadUnlock(); + EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + + rwSpinLock.ReadUnlock(); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + + rwSpinLock.WriteTryLock(); + EATEST_VERIFY_MSG(rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure"); + } + + + { // AutoRWSpinLock -- Basic single-threaded test. + RWSpinLock rwSpinLock; // There are no construction parameters. + + { //Special scope just for the AutoRWSpinLock + AutoRWSpinLock autoRWSpinLock1(rwSpinLock, AutoRWSpinLock::kLockTypeRead); + AutoRWSpinLock autoRWSpinLock2(rwSpinLock, AutoRWSpinLock::kLockTypeRead); + + EATEST_VERIFY_MSG(rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.WriteTryLock(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + } + + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + + { //Special scope just for the AutoRWSpinLock + AutoRWSpinLock autoRWSpinLock(rwSpinLock, AutoRWSpinLock::kLockTypeWrite); + + EATEST_VERIFY_MSG(rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.ReadTryLock(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + } + + EATEST_VERIFY_MSG(!rwSpinLock.IsReadLocked(), "RWSpinLock failure"); + EATEST_VERIFY_MSG(!rwSpinLock.IsWriteLocked(), "RWSpinLock failure"); + } + + + #if EA_THREADS_AVAILABLE + + { // Multithreaded test + + RWSWorkData workData; + + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + Thread::Status status; + + for(int i(0); i < kThreadCount; i++) + threadId[i] = thread[i].Begin(RWSThreadFunction, &workData); + + for(int e = 0; e < kRWSTestTypeCount; e++) + { + workData.mnCurrentTestType.SetValue(e); + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds * 500, gTestLengthSeconds * 500); + } + + workData.mbShouldQuit = true; + + for(int t(0); t < kThreadCount; t++) + { + if(threadId[t] != kThreadIdInvalid) + { + status = thread[t].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status != Thread::kStatusRunning, "RWSpinlock/Thread failure: status == kStatusRunning.\n"); + } + } + + nErrorCount += (int)workData.mnErrorCount; + } + + #endif + + // TestThreadRWSpinLockW + nErrorCount += TestThreadRWSpinLockW(); + + + return nErrorCount; +} + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSemaphore.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSemaphore.cpp new file mode 100644 index 00000000..bdd87af1 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSemaphore.cpp @@ -0,0 +1,353 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include +#include + + +using namespace EA::Thread; + + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +struct SWorkData +{ + volatile bool mbShouldQuit; + Semaphore mSemaphore; + int mnPostCount; // The count to use for Semaphore::Post() + AtomicInt32 mnExpectedCount; + AtomicInt32 mnThreadCount; + AtomicInt32 mnErrorCount; + + SWorkData(const SemaphoreParameters& sp) + : mbShouldQuit(false), mSemaphore(&sp, false), + mnPostCount(1), mnExpectedCount(0), mnThreadCount(0), + mnErrorCount(0) {} + + // define copy ctor and assignment operator + // so the compiler does define them intrisically + SWorkData(const SWorkData& rhs); // copy constructor + SWorkData& operator=(const SWorkData& rhs); // assignment operator +}; + + + +static intptr_t SemaphoreTestThreadFunction(void* pvWorkData) +{ + SWorkData* pWorkData = (SWorkData*)pvWorkData; + int nErrorCount = 0; + ThreadId threadId = GetThreadId(); + + EA::UnitTest::ReportVerbosity(1, "Semaphore test function created: %s\n", EAThreadThreadIdToString(threadId)); + + const AtomicInt32::ValueType nThreadCount = pWorkData->mnThreadCount++; // AtomicInt32 operation. + const AtomicInt32::ValueType nPostCount = pWorkData->mnPostCount; + + #if defined(EA_PLATFORM_DESKTOP) // If the platform tends to run on fast hardware... + const unsigned kShortSleepMin = 50; + const unsigned kShortSleepMax = 100; + #else + const unsigned kShortSleepMin = 100; + const unsigned kShortSleepMax = 200; + #endif + + while(!pWorkData->mbShouldQuit) + { + if((nThreadCount % 2) == 0) // If the first thread or the third thread... + { + // Create 'work'. + pWorkData->mnExpectedCount += nPostCount; // Atomic operation. + pWorkData->mSemaphore.Post(nPostCount); + EA::UnitTest::ThreadSleepRandom(kShortSleepMin, kShortSleepMax); + + // Get the amount of 'work' that is expected. + int nWaitCount = nPostCount; + + // Wait for the 'work' to be queued and signalled. + while(nWaitCount-- > 0) + { + const int result = pWorkData->mSemaphore.Wait(GetThreadTime() + 2000); + + EATEST_VERIFY_MSG((result >= 0) || (result == Semaphore::kResultTimeout), "Semaphore failure 4a: semaphore.Wait()\n"); + + if(result >= 0) // If we we able to acquire the semaphore... + --pWorkData->mnExpectedCount; // Atomic operation. + // else timeout occurred. Every time we have this timeout we miss decrementing the semaphore by one, and thus the expected semaphore count rises by one. It doesn't mean there's a bug, it just means the expected count migrates higher as we get timeouts. + + EA::UnitTest::ThreadSleepRandom(kShortSleepMin, kShortSleepMax); + } + } + else + { + // Get the amount of 'work' that is expected. + int nWaitCount = nPostCount; + + // Wait for the 'work' to be queued and signalled. + while(nWaitCount-- > 0) + { + const int result = pWorkData->mSemaphore.Wait(GetThreadTime() + 2000); + + EATEST_VERIFY_MSG((result >= 0) || (result == Semaphore::kResultTimeout), "Semaphore failure 4b: semaphore.Wait()\n"); + + if(result >= 0) // If we we able to acquire the semaphore... + --pWorkData->mnExpectedCount; // Atomic operation. + // else timeout occurred. Every time we have this timeout we miss decrementing the semaphore by one, and thus the expected semaphore count rises by one. It doesn't mean there's a bug, it just means the expected count migrates higher as we get timeouts. + + EA::UnitTest::ThreadSleepRandom(kShortSleepMin, kShortSleepMax); + } + + // Create 'work'. + pWorkData->mnExpectedCount += nPostCount; // Atomic operation. + pWorkData->mSemaphore.Post(nPostCount); + EA::UnitTest::ThreadSleepRandom(kShortSleepMin, kShortSleepMax); + } + + EATEST_VERIFY_MSG(pWorkData->mSemaphore.GetCount() >= 0, "Semaphore failure 4c: The count should always be >= 0.\n"); + + // We restrict this assert to desktop platforms because the expected value migrates upward on every Wait timeout, and on slower platforms there could be a lot of such timeouts. + #if defined(EA_PLATFORM_DESKTOP) + EATEST_VERIFY_MSG(pWorkData->mSemaphore.GetCount() < 200, "Semaphore failure 4d: The count should always be a small value.\n"); + #endif + } + + pWorkData->mnErrorCount = nErrorCount; + + return 0; +} + + + +struct BadSignalTestData +{ + EA::Thread::Semaphore mSemaphore; // The Semaphore that we are using. + AtomicInt32 mnOKWaitCount; // The number of Wait timeouts that occurred. + AtomicInt32 mnTimeoutWaitCount; // The number of Wait OK returns that occurred. + AtomicInt32 mnErrorWaitCount; // The number of Wait error returns that occurred. + int mnWaitTimeout; // How long a waiter waits before timing out. + int mnWaitThreadCount; // How many waiter threads there are. Caller sets up this value. + int mnPostThreadCount; // How many poster threads there are. Caller sets up this value. + int mnPostCount; // How many signals the poster should Post. Needs to be less than mnWaitThreadCount for this test. Caller sets up this value. + int mnPostDelay; // How long the poster waits before Posting. Needs to be significantly less than mnWaitTimeout. + + BadSignalTestData() + : mSemaphore(), mnOKWaitCount(0), mnTimeoutWaitCount(0), mnErrorWaitCount(0), + mnWaitTimeout(0), mnWaitThreadCount(0), mnPostThreadCount(0), + mnPostCount(0), mnPostDelay(0) { } + + // Define copy ctor and assignment operator + // so the compiler does define them intrisically + BadSignalTestData(const BadSignalTestData& rhs); // copy constructor + BadSignalTestData& operator=(const BadSignalTestData& rhs); // assignment operator +}; + + +static intptr_t BadSignalTestWaitFunction(void *data) +{ + BadSignalTestData* const pBSTD = (BadSignalTestData*)data; + + const int waitResult = pBSTD->mSemaphore.Wait(EA::Thread::GetThreadTime() + pBSTD->mnWaitTimeout); + + if(waitResult == Semaphore::kResultTimeout) + pBSTD->mnTimeoutWaitCount.Increment(); + else if(waitResult >= 0) + pBSTD->mnOKWaitCount.Increment(); + else + pBSTD->mnErrorWaitCount.Increment(); + + return 0; +} + + +static intptr_t BadSignalTestPostFunction(void *data) +{ + BadSignalTestData* const pBSTD = (BadSignalTestData*)data; + + ThreadSleep((ThreadTime) pBSTD->mnPostDelay); + pBSTD->mSemaphore.Post(pBSTD->mnPostCount); // Intentionally post less than the number of waiting threads. + + return 0; +} + + + + +int TestThreadSemaphore() +{ + int nErrorCount(0); + + { // Single threaded test. + const int kInitialCount(4); + int nResult; + + SemaphoreParameters sp(kInitialCount, false, "SingleThreadTest"); + Semaphore semaphore(&sp); + + nResult = semaphore.GetCount(); + EATEST_VERIFY_F(nResult == kInitialCount, "Semaphore failure 2a: semaphore.GetCount(). result = %d\n", nResult); + + // This triggers an assert and so cannot be tested here: + // bResult = semaphore.SetCount(10); + // EATEST_VERIFY_MSG(!bResult, "Semaphore failure in semaphore.SetCount(10)\n"); + + // Grab the full count, leaving none. + for(int i(0); i < kInitialCount; i++) + { + nResult = semaphore.Wait(kTimeoutNone); + EATEST_VERIFY_F(nResult == kInitialCount - i - 1, "Semaphore failure 2b: semaphore.Wait(kTimeoutNone). result = %d\n", nResult); + + nResult = semaphore.GetCount(); + EATEST_VERIFY_F(nResult == kInitialCount - i - 1, "Semaphore failure 2c: semaphore.GetCount(). result = %d\n", nResult); + } + + nResult = semaphore.Wait(kTimeoutImmediate); + EATEST_VERIFY_F(nResult == Semaphore::kResultTimeout, "Semaphore failure 2d: semaphore.Wait(kTimeoutImmediate). result = %d\n", nResult); + + nResult = semaphore.GetCount(); + EATEST_VERIFY_F(nResult == 0, "Semaphore failure 2e: semaphore.GetCount(). result = %d\n", nResult); + + nResult = semaphore.Post(2); + EATEST_VERIFY_F(nResult == 2, "Semaphore failure 2f: semaphore.Post(2). result = %d\n", nResult); + + nResult = semaphore.Post(2); + EATEST_VERIFY_F(nResult == 4, "Semaphore failure 2g: semaphore.Post(2). result = %d\n", nResult); + } + + + #if EA_THREADS_AVAILABLE + + { // Bad signal test. + BadSignalTestData bstd; + Thread thread[4]; + + // These values need to be set up right or else this test won't work. + // What we are trying to do here is make certain that N number of Semaphore + // waiters time out, while M waiters succeed. + bstd.mnWaitTimeout = 5000; + bstd.mnPostDelay = 2000; + bstd.mnWaitThreadCount = 3; // mnWaitThreadCount + mnPostThreadCount should be [4]. + bstd.mnPostThreadCount = 1; + bstd.mnPostCount = 1; + + thread[0].Begin(BadSignalTestWaitFunction, &bstd); + thread[1].Begin(BadSignalTestWaitFunction, &bstd); + thread[2].Begin(BadSignalTestWaitFunction, &bstd); + thread[3].Begin(BadSignalTestPostFunction, &bstd); + + // Wait for the threads to be completed + thread[0].WaitForEnd(); + thread[1].WaitForEnd(); + thread[2].WaitForEnd(); + thread[3].WaitForEnd(); + + EATEST_VERIFY_MSG(bstd.mnTimeoutWaitCount == (bstd.mnWaitThreadCount - (bstd.mnPostThreadCount * bstd.mnPostCount)), "Semaphore failure 1a: bad signal test.\n"); + EATEST_VERIFY_MSG(bstd.mnErrorWaitCount == 0, "Semaphore failure 1b: bad signal test.\n"); + EATEST_VERIFY_MSG(bstd.mnOKWaitCount == (bstd.mnPostThreadCount * bstd.mnPostCount), "Semaphore failure 1c: bad signal test.\n"); + } + + { // Multithreaded test + + // Problem: In the case of the inter-process test below, since we are using a named semaphore it's possible that + // if two instances of this unit test are run on the same machine simultaneously, they will collide because + // they are using the same semaphore. This applies only to true multi-process-capable machines. + uint64_t cycle64 = EA::StdC::Stopwatch::GetCPUCycle(); + uint16_t cycle16 = (uint16_t)((uint16_t)(cycle64 >> 48) ^ (uint16_t)(cycle64 >> 32) ^ (uint16_t)(cycle64 >> 16) ^ (uint16_t)(cycle64 >> 0)); // Munge all the bits together because some platforms might have zeroed low bits; others high bits. This will work for all. + char name[16]; sprintf(name, "SMT%u", (unsigned)cycle16); + SemaphoreParameters semaphoreParameters(0, false, name); + #if defined(EA_PLATFORM_DESKTOP) + const int kLoopCount = 16; + #else + const int kLoopCount = 4; + #endif + + for(int j = 1; j <= kLoopCount; j++) // Intentionally start with 1, as we use j below. + { + if(semaphoreParameters.mbIntraProcess) + semaphoreParameters.mbIntraProcess = false; + else + semaphoreParameters.mbIntraProcess = true; + + SWorkData workData(semaphoreParameters); + workData.mnPostCount = (j % 4); + + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + Thread::Status status; + int i; + + for(i = 0; i < kThreadCount; i++) + { + threadId[i] = thread[i].Begin(SemaphoreTestThreadFunction, &workData); + + EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Semaphore failure 3a: Couldn't create thread.\n"); + } + + EA::UnitTest::ThreadSleepRandom(gTestLengthSeconds*2000, gTestLengthSeconds*2000); + + workData.mbShouldQuit = true; + + #if defined(EA_PLATFORM_DESKTOP) + EA::UnitTest::ThreadSleepRandom(500, 500); + #else + EA::UnitTest::ThreadSleepRandom(1000, 1000); + #endif + + // We seed the semaphore with more posts because timing issues could cause the + // worker threads to get hung waiting for posts but it really isn't because + // of a problem with the semaphore. By the time all threads have quit, the + // expected count should be equal to the increment. + const int kIncrement = 20; + workData.mnExpectedCount += kIncrement; // AtomicInt32 operation + workData.mSemaphore.Post(kIncrement); + + #if defined(EA_PLATFORM_DESKTOP) + EA::UnitTest::ThreadSleepRandom(1000, 1000); + #else + EA::UnitTest::ThreadSleepRandom(2000, 2000); + #endif + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Semaphore failure 3b: Thread(s) didn't end.\n"); + } + } + + // Normally the Semaphore::GetCount function returns a value that is volatile, but we know that + // there aren't any threads using mSemaphore any more, so GetCount returns a value we can rely on. + EATEST_VERIFY_MSG(workData.mnExpectedCount == workData.mSemaphore.GetCount(), "Semaphore failure 3c: Unexpected value.\n"); + if(workData.mnExpectedCount != workData.mSemaphore.GetCount()) + { + EA::UnitTest::ReportVerbosity(1, " Thread count: %d, Intraprocess: %s, Post count: %d, Expected count: %d, Semaphore GetCount: %d\n", + kThreadCount, semaphoreParameters.mbIntraProcess ? "yes" : "no", workData.mnPostCount, (int)workData.mnExpectedCount.GetValue(), workData.mSemaphore.GetCount()); + } + + nErrorCount += (int)workData.mnErrorCount; + } + + } + + #endif // EA_THREADS_AVAILABLE + + return nErrorCount; +} + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSmartPtr.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSmartPtr.cpp new file mode 100644 index 00000000..969d5959 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSmartPtr.cpp @@ -0,0 +1,217 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include // required to test the c11 atomics macros + +using namespace EA::Thread; + + +struct TestClass +{ + int x; +}; + + +static bool IsTrue(bool b) +{ + return b; +} + +static bool IsFalse(bool b) +{ + return !b; +} + + +namespace not_eastl +{ + // Stub of std::shared_ptr implementation used to exercise the C11 atomic + // macro expansion. This was causing issues because the function names of + // the free standing eastl::shared_ptr functions and the C11 atomics + // functions collided. Since the C11 atomics were implemented as macros we + // can not utilize any scoping so we are forced to prevent the problem + // polluting the global namespace. + template class shared_ptr { }; + template inline bool atomic_is_lock_free(const shared_ptr*) { return false; } + template inline shared_ptr atomic_load(const shared_ptr* pSharedPtr) { return *pSharedPtr; } + template inline shared_ptr atomic_load_explicit(const shared_ptr* pSharedPtr, ...) { return *pSharedPtr; } + template inline void atomic_store(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB) {} + template inline void atomic_store_explicit(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB, ...) {} + template shared_ptr atomic_exchange(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB) { return sharedPtrB; } + template inline shared_ptr atomic_exchange_explicit(shared_ptr* pSharedPtrA, shared_ptr sharedPtrB, ...){ return *pSharedPtrA; } + template bool atomic_compare_exchange_strong(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew) { return false; } + template inline bool atomic_compare_exchange_weak(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew) { return false; } + template inline bool atomic_compare_exchange_strong_explicit(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew, ...) { return false; } + template inline bool atomic_compare_exchange_weak_explicit(shared_ptr* pSharedPtr, shared_ptr* pSharedPtrCondition, shared_ptr sharedPtrNew, ...) { return false; } +} + + +int TestThreadSmartPtr() +{ + int nErrorCount(0); + + // C11 atomics & eastl::shared_ptr name collision tests. + { + // If you are seeing compiler errors regarding C11 atomic macro + // expansions here look at header file eathread_atomic_android_c11.h. + // Ensure C11 atomic macros are being undefined properly. We hit this + // problem on android-gcc config. + using namespace not_eastl; + shared_ptr ptr1, ptr2, ptr3; + + atomic_is_lock_free(&ptr1); + atomic_load(&ptr1); + atomic_load_explicit(&ptr1); + atomic_store(&ptr1, ptr2); + atomic_store_explicit(&ptr1, ptr2); + atomic_exchange(&ptr1, ptr2); + atomic_exchange_explicit(&ptr1, ptr2); + atomic_compare_exchange_strong(&ptr1, &ptr2, ptr3); + atomic_compare_exchange_weak(&ptr1, &ptr2, ptr3); + atomic_compare_exchange_strong_explicit(&ptr1, &ptr2, ptr3); + atomic_compare_exchange_strong_explicit(&ptr1, &ptr2, ptr3); + } + + { // Basic single-threaded test. + typedef shared_ptr_mt TestClassSharedPtr; + + // typedef T element_type; + EATEST_VERIFY_MSG(sizeof(TestClassSharedPtr::element_type) > 0, "shared_ptr_mt failure."); + + // typedef T value_type; + EATEST_VERIFY_MSG(sizeof(TestClassSharedPtr::value_type) > 0, "shared_ptr_mt failure"); + + // explicit shared_ptr_mt(T* pValue = 0) + TestClassSharedPtr pSharedPtr0; + TestClassSharedPtr pSharedPtr1(new TestClass); + TestClassSharedPtr pSharedPtr2(new TestClass); + + // shared_ptr_mt(shared_ptr_mt const& sharedPtr) + TestClassSharedPtr pSharedPtr3(pSharedPtr1); + + // void lock() const + // void unlock() const + pSharedPtr0.lock(); + pSharedPtr0.unlock(); + + // operator bool_() const + EATEST_VERIFY_MSG(IsFalse(pSharedPtr0), "shared_ptr_mt failure"); + EATEST_VERIFY_MSG( IsTrue(pSharedPtr1), "shared_ptr_mt failure"); + + // bool operator!() const + EATEST_VERIFY_MSG( IsTrue(!pSharedPtr0), "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(IsFalse(!pSharedPtr1), "shared_ptr_mt failure"); + + // int use_count() const + EATEST_VERIFY_MSG(pSharedPtr0.use_count() == 1, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr1.use_count() == 2, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr2.use_count() == 1, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr3.use_count() == 2, "shared_ptr_mt failure"); + + // bool unique() const + EATEST_VERIFY_MSG( pSharedPtr0.unique(), "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(!pSharedPtr1.unique(), "shared_ptr_mt failure"); + EATEST_VERIFY_MSG( pSharedPtr2.unique(), "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(!pSharedPtr3.unique(), "shared_ptr_mt failure"); + + // shared_ptr_mt& operator=(shared_ptr_mt const& sharedPtr) + pSharedPtr3 = pSharedPtr2; + + EATEST_VERIFY_MSG(pSharedPtr1.use_count() == 1, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr2.use_count() == 2, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr3.use_count() == 2, "shared_ptr_mt failure"); + + // void reset(T* pValue = 0) + pSharedPtr2.reset(); + + EATEST_VERIFY_MSG(IsFalse(pSharedPtr2), "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr1.use_count() == 1, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr2.use_count() == 1, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr3.use_count() == 1, "shared_ptr_mt failure"); + + pSharedPtr3.reset(new TestClass); + + EATEST_VERIFY_MSG(IsTrue(pSharedPtr3), "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr3.use_count() == 1, "shared_ptr_mt failure"); + + // T* get() const + TestClass* pA = pSharedPtr1.get(); + TestClass* pB = pSharedPtr3.get(); + + // template + // inline T* get_pointer(const shared_ptr_mt& sharedPtr) + EATEST_VERIFY_MSG(get_pointer(pSharedPtr1) == pA, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(get_pointer(pSharedPtr3) == pB, "shared_ptr_mt failure"); + + // void swap(shared_ptr_mt& sharedPtr) + pSharedPtr1.swap(pSharedPtr3); + + EATEST_VERIFY_MSG(pSharedPtr1.get() == pB, "shared_ptr_mt failure"); + EATEST_VERIFY_MSG(pSharedPtr3.get() == pA, "shared_ptr_mt failure"); + + // T& operator*() const + (*pSharedPtr1).x = 37; + + EATEST_VERIFY_MSG(pSharedPtr1.get()->x == 37, "shared_ptr_mt failure"); + + // T* operator->() const + pSharedPtr1->x = 73; + + EATEST_VERIFY_MSG(pSharedPtr1.get()->x == 73, "shared_ptr_mt failure"); + + // template + // inline void swap(shared_ptr_mt& sharedPtr1, shared_ptr_mt& sharedPtr2) + swap(pSharedPtr1, pSharedPtr2); + + EATEST_VERIFY_MSG(pSharedPtr2.get()->x == 73, "shared_ptr_mt failure"); + + // template + // inline bool operator==(const shared_ptr_mt& sharedPtr1, const shared_ptr_mt& sharedPtr2) + bool bEqual = pSharedPtr1 == pSharedPtr2; + + EATEST_VERIFY_MSG(!bEqual, "shared_ptr_mt failure"); + + // template + // inline bool operator!=(const shared_ptr_mt& sharedPtr1, const shared_ptr_mt& sharedPtr2) + bool bNotEqual = pSharedPtr1 != pSharedPtr2; + + EATEST_VERIFY_MSG(bNotEqual, "shared_ptr_mt failure"); + + // template + // inline bool operator<(const shared_ptr_mt& sharedPtr1, const shared_ptr_mt& sharedPtr2) + bool bLessA = pSharedPtr1 < pSharedPtr2; + bool bLessB = pSharedPtr2 < pSharedPtr1; + + EATEST_VERIFY_MSG(bLessA || bLessB, "shared_ptr_mt failure"); + } + + + { + typedef shared_array_mt TestClassSharedArray; + + TestClassSharedArray pSharedArray(new TestClass[5]); + TestClassSharedArray pSharedArray2(pSharedArray); + + // To do: Implement the tests above. + } + + return nErrorCount; +} + + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSpinLock.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSpinLock.cpp new file mode 100644 index 00000000..327d43de --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSpinLock.cpp @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include + + +using namespace EA::Thread; + + +int TestThreadSpinLock() +{ + int nErrorCount(0); + + { // SpinLock -- Basic single-threaded test. + + SpinLock spinLock; + + EATEST_VERIFY_MSG(!spinLock.IsLocked(), "SpinLock failure"); + + spinLock.Lock(); + EATEST_VERIFY_MSG(spinLock.IsLocked(), "SpinLock failure"); + + EATEST_VERIFY_MSG(!spinLock.TryLock(), "SpinLock failure"); + + spinLock.Unlock(); + EATEST_VERIFY_MSG(!spinLock.IsLocked(), "SpinLock failure"); + + EATEST_VERIFY_MSG(spinLock.TryLock(), "SpinLock failure"); + EATEST_VERIFY_MSG(spinLock.IsLocked(), "SpinLock failure"); + + spinLock.Unlock(); + EATEST_VERIFY_MSG(!spinLock.IsLocked(), "SpinLock failure"); + } + + + { // AutoSpinLock -- Basic single-threaded test. + + SpinLock spinLock; + + EATEST_VERIFY_MSG(!spinLock.IsLocked(), "AutoSpinLock failure"); + + { //Special scope just for the AutoSpinLock + AutoSpinLock autoSpinLock(spinLock); + + EATEST_VERIFY_MSG(spinLock.IsLocked(), "AutoSpinLock failure"); + } + + EATEST_VERIFY_MSG(!spinLock.IsLocked(), "AutoSpinLock failure"); + } + + + #if EA_THREADS_AVAILABLE + { // Multithreaded test + + // Implement this when we get the thread class working. + // gTestLengthSeconds; + } + #endif + + return nErrorCount; +} + + + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadStorage.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadStorage.cpp new file mode 100644 index 00000000..76665cb1 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadStorage.cpp @@ -0,0 +1,236 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include + +using namespace EA::Thread; + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +/////////////////////////////////////////////////////////////////////////////// +// EA_THREAD_LOCAL tests +// +#ifdef EA_THREAD_LOCAL + + // Test declaration of basic int. + EA_THREAD_LOCAL int gTLI = 0; + + // Test declaration of static int. + static EA_THREAD_LOCAL int sTLI = 0; + + // Test declaration of struct. + struct ThreadLocalData + { + int x; + }; + + EA_THREAD_LOCAL ThreadLocalData gTDL; + +#endif + + + + +struct TLSWorkData +{ + AtomicInt32 mShouldBegin; + AtomicInt32 mShouldEnd; + AtomicInt32 mnErrorCount; + ThreadLocalStorage* mpTLS; + + TLSWorkData() + : mShouldBegin(0), mShouldEnd(0), mnErrorCount(0), mpTLS(NULL) {} + + // define copy ctor and assignment operator + // so the compiler does define them intrisically + TLSWorkData(const TLSWorkData& rhs); // copy constructor + TLSWorkData& operator=(const TLSWorkData& rhs); // assignment operator +}; + + + +static intptr_t TLSTestThreadFunction(void* pvWorkData) +{ + TLSWorkData* pWorkData = (TLSWorkData*)pvWorkData; + void* pValue; + bool bResult; + size_t i = 1; + int nErrorCount = 0; + + while(pWorkData->mShouldBegin.GetValue() == 0) // Spin until we get the should-begin signal. We could also use a Semaphore for this, but our use here is very simple and not performance-oriented. + EA::Thread::ThreadSleep(100); + + pValue = pWorkData->mpTLS->GetValue(); + EATEST_VERIFY_MSG(pValue == NULL, "ThreadLocalStorage failure."); + + do{ + bResult = pWorkData->mpTLS->SetValue((void*)i); + EATEST_VERIFY_MSG(bResult, "ThreadLocalStorage failure."); + + pValue = pWorkData->mpTLS->GetValue(); + EATEST_VERIFY_MSG(pValue == (void*)i, "ThreadLocalStorage failure."); + + i++; + + ThreadCooperativeYield(); // Used by cooperative threading platforms. + }while(pWorkData->mShouldEnd.GetValue() == 0); + + pValue = pWorkData->mpTLS->GetValue(); + EATEST_VERIFY_MSG(pValue != NULL, "ThreadLocalStorage failure."); + + pWorkData->mnErrorCount += nErrorCount; + + return 0; +} + + +static int TestThreadStorageSingle() +{ + int nErrorCount(0); + + { // Re-use test + void* pValue; + ThreadLocalStorage tlsUnused; + + { // Give this its own scope. + ThreadLocalStorage tls0; + + pValue = tls0.GetValue(); + EATEST_VERIFY_MSG(pValue == NULL, "ThreadLocalStorage failure."); + + tls0.SetValue((void*)1); + } + + ThreadLocalStorage tls1; + + pValue = tls1.GetValue(); + EATEST_VERIFY_MSG(pValue == NULL, "ThreadLocalStorage failure."); + } + + + { // Single-threaded test + #ifdef EA_THREAD_LOCAL + // EA_THREAD_LOCAL tests + gTLI = 1; + sTLI = 1; + gTDL.x = 1; + + if((gTLI + sTLI + gTDL.x) == 100000) // Prevent compiler warnings due to non-usage. + EA::UnitTest::ReportVerbosity(1, "EA_THREAD_LOCAL: %d %d %d\n", gTLI, sTLI, gTDL.x); + #endif + + // Test ThreadLocalStorage + ThreadLocalStorage tlsA; + ThreadLocalStorage tlsB; + ThreadLocalStorage tlsC; + void* pValue; + void* pNULL = (void*)0; + void* p1 = (void*)1; + void* p2 = (void*)2; + bool bResult; + + pValue = tlsA.GetValue(); + EATEST_VERIFY_MSG(pValue == pNULL, "ThreadLocalStorage failure."); + + pValue = tlsB.GetValue(); + EATEST_VERIFY_MSG(pValue == pNULL, "ThreadLocalStorage failure."); + + pValue = tlsC.GetValue(); + EATEST_VERIFY_MSG(pValue == pNULL, "ThreadLocalStorage failure."); + + bResult = tlsA.SetValue(pNULL); + EATEST_VERIFY_MSG(bResult, "ThreadLocalStorage failure."); + + pValue = tlsA.GetValue(); + EATEST_VERIFY_MSG(pValue == pNULL, "ThreadLocalStorage failure."); + + bResult = tlsA.SetValue(p1); + EATEST_VERIFY_MSG(bResult, "ThreadLocalStorage failure."); + + pValue = tlsA.GetValue(); + EATEST_VERIFY_MSG(pValue == p1, "ThreadLocalStorage failure."); + + bResult = tlsA.SetValue(pNULL); + EATEST_VERIFY_MSG(bResult, "ThreadLocalStorage failure."); + + pValue = tlsA.GetValue(); + EATEST_VERIFY_MSG(pValue == pNULL, "ThreadLocalStorage failure."); + + bResult = tlsA.SetValue(p2); + EATEST_VERIFY_MSG(bResult, "ThreadLocalStorage failure."); + + pValue = tlsA.GetValue(); + EATEST_VERIFY_MSG(pValue == p2, "ThreadLocalStorage failure."); + } + + return nErrorCount; +} + + +static int TestThreadStorageMultiple() +{ + int nErrorCount(0); + + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + Thread::Status status; + int i; + TLSWorkData workData; + + for(i = 0; i < kThreadCount; i++) + { + threadId[i] = thread[i].Begin(TLSTestThreadFunction, &workData); + EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: Couldn't create thread."); + } + + workData.mpTLS = new ThreadLocalStorage; + workData.mShouldBegin.SetValue(1); + + EA::UnitTest::ThreadSleepRandom(2000, 2000); + + workData.mShouldEnd.SetValue(1); + + EA::UnitTest::ThreadSleepRandom(1000, 1000); + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + { + status = thread[i].WaitForEnd(GetThreadTime() + 30000); + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread(s) didn't end."); + } + } + + delete workData.mpTLS; + workData.mpTLS = NULL; + + nErrorCount += (int)workData.mnErrorCount; + + return nErrorCount; +} + + +int TestThreadStorage() +{ + int nErrorCount(0); + + nErrorCount += TestThreadStorageSingle(); + + #if EA_THREADS_AVAILABLE + // Call this twice, to make sure recyling of TLS works properly. + nErrorCount += TestThreadStorageMultiple(); + nErrorCount += TestThreadStorageMultiple(); + #endif + + return nErrorCount; +} + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSync.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSync.cpp new file mode 100644 index 00000000..964fc6f9 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadSync.cpp @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include + + +int TestThreadSync() +{ + using namespace EA::Thread; + + int nErrorCount(0); + + EAReadBarrier(); + EAWriteBarrier(); + EAReadWriteBarrier(); + EACompilerMemoryBarrier(); + + return nErrorCount; +} + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadThread.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadThread.cpp new file mode 100644 index 00000000..d5f3e1c6 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadThread.cpp @@ -0,0 +1,1508 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef EA_PLATFORM_LINUX + #include +#endif + +#ifdef EA_PLATFORM_MICROSOFT + EA_DISABLE_ALL_VC_WARNINGS() + #include + EA_RESTORE_ALL_VC_WARNINGS() +#endif + +#include + +using namespace EA::Thread; + + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; +static unsigned int sThreadTestTimeMS = 2000; // We potentially change this value below. +static AtomicInt32 sThreadCount = 0; +static AtomicInt32 sShouldGo = 0; + + +static intptr_t TestFunction1(void*) +{ + ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS; + + while(EA::Thread::GetThreadTime() < nTimeEnd) + ThreadSleep(); + return 0; +} + + +static intptr_t TestFunction3(void*) +{ + return 0; +} + + +static intptr_t TestFunction4(void* arg) +{ + const intptr_t returnValue = (intptr_t)arg; + + EA::UnitTest::ThreadSleepRandom(0, 5); + + return returnValue; +} + + +#if !defined(EA_PLATFORM_MOBILE) + static intptr_t TestFunction6(void* arg) + { + const intptr_t returnValue = (intptr_t)arg; + + sThreadCount++; + + while(sShouldGo == 0) + ThreadSleep(10); + + EA::UnitTest::ThreadSleepRandom(3, 8); + + sThreadCount--; + + return returnValue; + } +#endif + + +static intptr_t TestFunction7(void*) +{ + while(sShouldGo == 0) + ThreadSleep(10); + + return 0; +} + + +static intptr_t TestFunction_GetThreadProcessor(void*) +{ + return (intptr_t)GetThreadProcessor(); +} + +static int findSetBitIndex(int requestedIndex, uint64_t availableAffinityMask) +{ + int correctedIndex = -1; + int64_t mask = 1; + do + { + if ((availableAffinityMask & mask) != 0) + --requestedIndex; + + ++correctedIndex; + mask <<= 1; + } while (requestedIndex >= 0); + + return correctedIndex; +} + +static int calculateAvailableProcessorIndex(int index) +{ + using namespace EA::Thread; + const auto affinityMask = GetAvailableCpuAffinityMask(); + const int availableCpuCount = EA::StdC::CountBits64(affinityMask); + const int properIndex = findSetBitIndex(index % availableCpuCount, affinityMask); + return properIndex; +} + +static intptr_t TestFunction_SetAffinityAndWait(void* arg) +{ + const int requestedCore = *(static_cast(arg)); + const int coreIndex = calculateAvailableProcessorIndex(requestedCore); + const ThreadAffinityMask affinityMask = UINT64_C(1) << coreIndex; + + SetThreadAffinityMask(affinityMask); + + while (GetThreadProcessor() != coreIndex) + { + SetThreadAffinityMask(affinityMask); + ThreadSleep(0); + } + + return (intptr_t)coreIndex; +} + +static intptr_t TestFunction_SetThreadProcessorAndWait(void* arg) +{ + const int requestedCore = *(static_cast(arg)); + const int coreIndex = calculateAvailableProcessorIndex(requestedCore); + + SetThreadProcessor(coreIndex); + + while (GetThreadProcessor() != coreIndex) + { + SetThreadProcessor(coreIndex); + ThreadSleep(0); + } + + return (intptr_t)coreIndex; +} + +static intptr_t TestFunction_WaitForThreadMigration(void* arg) +{ + sThreadCount++; + + const int requestedCore = *(static_cast(arg)); + + while (GetThreadProcessor() != requestedCore) + ThreadSleep(0); + + sThreadCount--; + + return (intptr_t)requestedCore; +} + +static intptr_t TestFunction13(void* arg) +{ + ThreadSleep(10); + ThreadEnd(42); // 42 is a magic number we will verify gets passed through the user. + return 0; +} + +static intptr_t TestFunction3ExceptionWrapper(RunnableFunction defaultRunnableFunction, void* pContext) +{ + return defaultRunnableFunction(pContext); +} + + +class TestRunnable1 : public IRunnable +{ + intptr_t Run(void*) + { + const ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS; + while (EA::Thread::GetThreadTime() < nTimeEnd) + ThreadSleep(); + return 0; + } +} gTestRunnable1; + + +class TestRunnable2 : public IRunnable +{ + intptr_t Run(void*) + { + const ThreadTime nTimeEnd = EA::Thread::GetThreadTime() + sThreadTestTimeMS; + + while(EA::Thread::GetThreadTime() < nTimeEnd) + { + ThreadSleep(); + } + return 0; + } +} gTestRunnable2; + +static intptr_t TestRunnable3ExceptionWrapper(IRunnable* defaultRunnableFunction, void* pContext) +{ + return defaultRunnableFunction->Run(pContext); +} + + +class TestRunnable3 : public IRunnable +{ + intptr_t Run(void*) + { + ThreadSleep(500); + return 0; + } +} gTestRunnable3; + +class TestRunnable4 : public IRunnable +{ + intptr_t Run(void* args) + { + // IRunnable object that returns the thread id that executed on. + return TestFunction_WaitForThreadMigration(args); + } +} gTestRunnable4; + + +int TestThreadAffinityMask() +{ + int nErrorCount = 0; + const int TIMEOUT = 30000; + const int MAX_ITERATIONS = 16; + + auto VERIFY_AFFINITY_RESULT = [&](const char* name, intptr_t in_result, int count) + { + #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED + const int nAvailableProcessors = EA::StdC::CountBits64(GetAvailableCpuAffinityMask()); + auto result = static_cast(in_result); + EATEST_VERIFY_F( + result == calculateAvailableProcessorIndex(count), + "Thread '%s' failure: SetAffinityMask not working properly. Thread ran on: %d/%d <=> Expected: %d\n", name, + result, nAvailableProcessors, calculateAvailableProcessorIndex(count)); + #endif + }; + + int count = MAX_ITERATIONS; + while(--count) + { + int properIndex = calculateAvailableProcessorIndex(count); + // Test Thread Affinity Masks (thread parameters) + Thread thread; + ThreadParameters params; + + params.mnAffinityMask = INT64_C(1) << properIndex; + params.mnProcessor = kProcessorAny; + thread.Begin(TestFunction_GetThreadProcessor, NULL, ¶ms); + + intptr_t result = 0; + thread.WaitForEnd(GetThreadTime() + TIMEOUT, &result); + + VERIFY_AFFINITY_RESULT("Parameters", result, properIndex); + } + + count = MAX_ITERATIONS; + while(--count) + { + int startIndex = calculateAvailableProcessorIndex(count + 1); // We explicitly want it to start on another core. + int properIndex = calculateAvailableProcessorIndex(count); + + sThreadCount = 0; + + // Test Thread Affinity Masks (thread object) + ThreadParameters params; + params.mnProcessor = startIndex; + + Thread thread; + thread.Begin(TestFunction_WaitForThreadMigration, &properIndex, ¶ms); // sleeps then grabs the current thread id. + + #if defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_NX) + // spin while we wait for the thread to startup + while (sThreadCount == 0) + ThreadSleep(1); + #endif + + // after thread has started, set the requested affinity + thread.SetAffinityMask(INT64_C(1) << properIndex); + + // wait for the thread to end + intptr_t result = 0; + thread.WaitForEnd(GetThreadTime() + TIMEOUT, &result); + + VERIFY_AFFINITY_RESULT("Object", result, properIndex); + } + + count = MAX_ITERATIONS; + while(--count) + { + int properIndex = calculateAvailableProcessorIndex(count); + + // Test Thread Affinity Masks (global functions) + ThreadParameters params; + params.mnProcessor = kProcessorAny; + + Thread thread; + thread.Begin(TestFunction_SetAffinityAndWait, &properIndex, ¶ms); + + intptr_t result = 0; + thread.WaitForEnd(GetThreadTime() + TIMEOUT, &result); + + VERIFY_AFFINITY_RESULT("Global Function", result, properIndex); + } + + count = MAX_ITERATIONS; + while(--count) + { + int properIndex = calculateAvailableProcessorIndex(count); + + // Test Thread Affinity Masks (thread parameters) - For IRunnable variant of the Thread::Begin function + ThreadParameters params; + params.mnProcessor = kProcessorAny; + params.mnAffinityMask = INT64_C(1) << properIndex; + params.mnProcessor = kProcessorAny; + + Thread thread; + thread.Begin(&gTestRunnable4, &properIndex, ¶ms); // sleeps then grabs the current thread id. + + intptr_t result = 0; + thread.WaitForEnd(GetThreadTime() + TIMEOUT, &result); + + VERIFY_AFFINITY_RESULT("IRunnable Thread Parameters", result, properIndex); + } + + // user regression of passing a -1 as an affinity mask. + { + // NOTE(rparolin): common user mistake. Ensure that we mask out invalid cpu flags and threads start up at least. + int userRegressionMask = ~0u; + + ThreadParameters params; + params.mnProcessor = kProcessorAny; + params.mnAffinityMask = userRegressionMask; + params.mnProcessor = kProcessorAny; + + Thread thread; + thread.Begin(TestFunction3, nullptr, ¶ms); + thread.WaitForEnd(GetThreadTime() + TIMEOUT); + } + + return nErrorCount; +} + + +int TestThreadPriorities() +{ + int nErrorCount = 0; + + if(!IsSuperUser()) + { + EA::EAMain::Report("Skipping Thread Priority test because we don't have sufficient system priviliages.\n"); + return nErrorCount; + } + + // Verify that thread priorities act as expected. + // Threads with higher priorities should execute instead of or before threads of lower priorities. + // On some platforms (e.g. Windows), lower priority threads do get some execution time, so we have to recognize that. + + // Create 20 threads of very high priority, 20 threads of high priority, and 20 threads of regular priority. + // Start the 20 very high priority threads first. + // Wait a bit then start the other 40 threads. + // Quit all the very high priority threads. + // Wait a bit, while having the 40 threads measure how much time they execute. + // Quit the remaining 40 threads. + // Verify that the 20 high priority threads executed much more than the regular threads. + + struct PriorityTestThread : public EA::Thread::IRunnable + { + EA::Thread::Thread mThread; + EA::Thread::ThreadParameters mParameters; + EA::Thread::Semaphore mSemaphore; + char mThreadName[16]; + volatile uint64_t mCounter = 0; + volatile bool mbShouldRun = true; + char mPadd[EA_CACHE_LINE_SIZE];// make sure that these structures end up on different cachelines + + intptr_t Run(void*) + { + mSemaphore.Wait(); + + while(mbShouldRun) + { + mCounter++; + EAReadBarrier(); + } + + return 0; + } + }; + + if((EA::Thread::GetProcessorCount() >= 4)) + { + const int kThreadCount = 4; + PriorityTestThread threadHighestPriority[kThreadCount]; + PriorityTestThread threadRegularPriority[kThreadCount]; + PriorityTestThread threadHighPriority[kThreadCount]; + EA::StdC::LimitStopwatch limitStopwatch(EA::StdC::Stopwatch::kUnitsSeconds); + const EA::Thread::ThreadAffinityMask kCommonAffinityMask = 0xf; // first 4 cores only + + EA::Thread::ThreadParameters commonParams; + commonParams.mbDisablePriorityBoost = true; // we can disable boosting if we want a better simulation of console-like behavior + commonParams.mnAffinityMask = kCommonAffinityMask; + commonParams.mnProcessor = kProcessorAny; + +#if defined(EA_PLATFORM_MICROSOFT) + // Due to how windows thread priorities work we need to further increase the thread priority of the high threads + // If this is not done the test will randomly have the regular priority have a higher count than the high threads. + const int kHigherPriorityDelta = kThreadPriorityMax - 2; +#else + const int kHigherPriorityDelta = 0; +#endif + + const auto highestPriority = EA::Thread::kThreadPriorityDefault + 2 + kHigherPriorityDelta; + const auto highPriority = EA::Thread::kThreadPriorityDefault + 1 + kHigherPriorityDelta; + const auto regularPriority = EA::Thread::kThreadPriorityDefault + 0; + + EA::Thread::SetThreadPriority(EA::Thread::kThreadPriorityDefault + 3); + for (int i = 0; i < kThreadCount; i++) + { + { + PriorityTestThread& highestThread = threadHighestPriority[i]; + highestThread.mParameters = commonParams; + highestThread.mParameters.mnPriority = highestPriority; + EA::StdC::Sprintf(highestThread.mThreadName, "Highest%d", i); + highestThread.mParameters.mpName = highestThread.mThreadName; + highestThread.mThread.Begin(&highestThread, NULL, &highestThread.mParameters); + } + { + PriorityTestThread& regularThread = threadRegularPriority[i]; + regularThread.mParameters = commonParams; + regularThread.mParameters.mnPriority = regularPriority; + EA::StdC::Sprintf(regularThread.mThreadName, "Reg%d", i); + regularThread.mParameters.mpName = regularThread.mThreadName; + regularThread.mThread.Begin(®ularThread, NULL, ®ularThread.mParameters); + } + { + PriorityTestThread& highThread = threadHighPriority[i]; + highThread.mParameters = commonParams; + highThread.mParameters.mnPriority = highPriority; + EA::StdC::Sprintf(highThread.mThreadName, "High%d", i); + highThread.mParameters.mpName = highThread.mThreadName; + highThread.mThread.Begin(&highThread, NULL, &highThread.mParameters); + } + } + + limitStopwatch.SetTimeLimit(1, true); + while(!limitStopwatch.IsTimeUp()) + { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ } + + for(int i = 0; i < kThreadCount; i++) + { + threadHighestPriority[i].mSemaphore.Post(1); + threadHighPriority[i].mSemaphore.Post(1); + threadRegularPriority[i].mSemaphore.Post(1); + } + + limitStopwatch.SetTimeLimit(3, true); + while(!limitStopwatch.IsTimeUp()) + { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ } + + for(int i = 0; i < kThreadCount; i++) + threadHighestPriority[i].mbShouldRun = false; + EAWriteBarrier(); + + limitStopwatch.SetTimeLimit(3, true); + while(!limitStopwatch.IsTimeUp()) + { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ } + + for(int i = 0; i < kThreadCount; i++) + { + threadHighPriority[i].mbShouldRun = false; + threadRegularPriority[i].mbShouldRun = false; + } + EAWriteBarrier(); + + limitStopwatch.SetTimeLimit(3, true); + while(!limitStopwatch.IsTimeUp()) + { /* Do nothing. Don't even sleep, as some platform might not give us the CPU back. */ } + + + // Wait for the threads to end before continuing. + for (int i = 0; i < kThreadCount; i++) + { + threadHighestPriority[i].mThread.WaitForEnd(); + threadHighPriority[i].mThread.WaitForEnd(); + threadRegularPriority[i].mThread.WaitForEnd(); + } + + EA::Thread::SetThreadPriority(EA::Thread::kThreadPriorityDefault); + + EAReadWriteBarrier(); + + // Tally the thread priority counters + uint64_t highPriorityCount = 0; + uint64_t regularPriorityCount = 0; + for(int i = 0; i < kThreadCount; i++) + highPriorityCount += threadHighPriority[i].mCounter; + for(int i = 0; i < kThreadCount; i++) + regularPriorityCount += threadRegularPriority[i].mCounter; + + #ifndef EA_PLATFORM_LINUX + // TODO(rparolin): This unreliable on various linux distros. Requires investigation. + EATEST_VERIFY_F(highPriorityCount > regularPriorityCount, + "Priority execution failure: highPriorityCount: %I64u, regularPriorityCount: %I64u", + highPriorityCount, regularPriorityCount); + #endif + } + + return nErrorCount; +} + +int TestSetThreadProcessConstants() +{ + int nErrorCount = 0; + + // testing a user reported regression of negative value shifts + for(auto k : { kProcessorDefault, kProcessorAny }) + { + Thread t; + + t.Begin([](void* param) -> intptr_t + { + int kConstant = *(int*)param; + SetThreadProcessor(kConstant); + return 0; + }, &k); + t.WaitForEnd(); + } + + return nErrorCount; +} + +int TestNullThreadNames() +{ + int nErrorCount = 0; + + { + ThreadParameters threadParams; + threadParams.mpName = nullptr; + + Thread t; + t.Begin([](void*) -> intptr_t { return 0; }, NULL, &threadParams); + t.WaitForEnd(); + } + + return nErrorCount; +} + +int TestLambdaThreads() +{ + int nErrorCount = 0; + + { // test rvalue + int foo = 0; + MakeThread([&] + { + EATEST_VERIFY(foo == 0); + foo = 42; + }) + .WaitForEnd(); + EATEST_VERIFY(foo == 42); + } + + { // test lvalue + int foo = 0; + + auto callme = [&] + { + EATEST_VERIFY(foo == 0); + foo = 42; + }; + + MakeThread(callme).WaitForEnd(); + EATEST_VERIFY(foo == 42); + } + + { // test thread parameters + const char* MY_THREAD_NAME = "my thread name"; + ThreadParameters params; + params.mpName = MY_THREAD_NAME; + + MakeThread( + [&] { EATEST_VERIFY(strncmp(MY_THREAD_NAME, GetThreadName(), EATHREAD_NAME_SIZE) == 0); }, params) + .WaitForEnd(); + } + + return nErrorCount; +} + +int TestThreadDynamicData() +{ + int nErrorCount = 0; + + const int kOverflowDynamicDataCount = 256; // Must be greater than EA::Thread::kMaxThreadDynamicDataCount. + + for(int i = 0; i < kOverflowDynamicDataCount; i++) + { + EA::Thread::ThreadId id = 0; + EA::Thread::SysThreadId sysId = 0; + + MakeThread([&] + { + id = EA::Thread::GetThreadId(); + sysId = EA::Thread::GetSysThreadId(); + }) + .WaitForEnd(); + + EATEST_VERIFY(FindThreadDynamicData(id) == nullptr); + EATEST_VERIFY(FindThreadDynamicData(sysId) == nullptr); + } + + return nErrorCount; +} + +int TestSetThreadProcessor() +{ + int nErrorCount = 0; + + const int TIMEOUT = 30000; + + auto VERIFY_THREAD_PROCESSOR_RESULT = [&](const char* name, intptr_t in_result, int count) + { + #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED + const int nAvailableProcessors = EA::StdC::CountBits64(EA::Thread::GetAvailableCpuAffinityMask()); + auto result = static_cast(in_result); + EATEST_VERIFY_F( + result == calculateAvailableProcessorIndex(count), + "Thread '%s' failure: SetAffinityMask not working properly. Thread ran on: %d/%d <=> Expected: %d\n", name, + result, nAvailableProcessors, calculateAvailableProcessorIndex(count)); + #endif + }; + + int count = EA::StdC::CountBits64(EA::Thread::GetAvailableCpuAffinityMask()); + while(--count) + { + int correctIndex = calculateAvailableProcessorIndex(count); + Thread thread; + thread.Begin(TestFunction_SetThreadProcessorAndWait, &correctIndex, nullptr); // sleeps then grabs the current thread id. + + // wait for the thread to end + intptr_t result = 0; + thread.WaitForEnd(GetThreadTime() + TIMEOUT, &result); + + VERIFY_THREAD_PROCESSOR_RESULT("SetThreadProcessor", result, correctIndex); + } + + return nErrorCount; +} + +int TestSetProcessor() +{ + int nErrorCount = 0; + + const int TIMEOUT = 30000; + const int MAX_ITERATIONS = 16; + + auto VERIFY_SET_PROCESSOR_RESULT = [&](const char* name, intptr_t in_result, int count) + { + #if EATHREAD_THREAD_AFFINITY_MASK_SUPPORTED + const int nAvailableProcessors = EA::StdC::CountBits64(EA::Thread::GetAvailableCpuAffinityMask()); + auto result = static_cast(in_result); + EATEST_VERIFY_F( + result == calculateAvailableProcessorIndex(count), + "Thread '%s' failure: SetProcessor not working properly. Thread ran on: %d/%d <=> Expected: %d\n", name, + result, nAvailableProcessors, calculateAvailableProcessorIndex(count)); + #endif + }; + + + int count = MAX_ITERATIONS; + while(--count) + { + int startIndex = calculateAvailableProcessorIndex(count + 1); // We explicitly want it to start on another core. + int properIndex = calculateAvailableProcessorIndex(count); + + sThreadCount = 0; + + // Test Thread Affinity Masks (thread object) + ThreadParameters params; + params.mnProcessor = startIndex; + + Thread thread; + thread.Begin(TestFunction_WaitForThreadMigration, &properIndex, ¶ms); // sleeps then grabs the current thread id. + + #if defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_NX) + // spin while we wait for the thread to startup + while (sThreadCount == 0) + ThreadSleep(0); + #endif + + // after thread has started, set the requested affinity + thread.SetProcessor(properIndex); + + // wait for the thread to end + intptr_t result = 0; + thread.WaitForEnd(GetThreadTime() + TIMEOUT, &result); + + VERIFY_SET_PROCESSOR_RESULT("SetProcessor", result, properIndex); + } + + return nErrorCount; +} + +int TestSetThreadProcessorByThreadParams() +{ + // Exercise EA::Thread::GetThreadId, EA::Thread::GetSysThreadId, EAThreadGetUniqueId, SetThreadProcessor, GetThreadProcessor. + + // Create and start N threads paused. + // Release all threads to run at once. + // Have each of the threads record its EAThreadGetUniqueId value and exit. + // Verify that there were no collisions in the recorded id values. + int nErrorCount = 0; + + struct IdTestThread : public EA::Thread::IRunnable + { + EA::Thread::Thread mThread; // The Thread object. + EA::Thread::Semaphore mSemaphore; // Used to pause the thread after it starts. + EA::Thread::ThreadUniqueId mUniqueId; // The EAThreadUniqueId that this thread gets assigned by the OS. + EA::Thread::ThreadId mThreadId; // The EAThreadUniqueId that this thread gets assigned by the OS. + EA::Thread::SysThreadId mSysThreadId; // The EAThreadUniqueId that this thread gets assigned by the OS. + int mAssignedProcessorId; // The processor id that we ask the OS to run this thread on. + int mProcessorId; // The processor id that this thread gets assigned by the OS. Should equal mAssignedProcessorId. + + IdTestThread() : mThread(), mSemaphore(0), mUniqueId(), mThreadId(), mSysThreadId(), mAssignedProcessorId(), mProcessorId() {} + IdTestThread(const IdTestThread&){} // Avoid compiler warnings. + void operator=(const IdTestThread&){} // Avoid compiler warnings. + + intptr_t Run(void*) + { + mSemaphore.Wait(); + EAThreadGetUniqueId(mUniqueId); + mThreadId = EA::Thread::GetThreadId(); + mSysThreadId = EA::Thread::GetSysThreadId(); + mProcessorId = EA::Thread::GetThreadProcessor(); + EAWriteBarrier(); + return 0; + } + }; + + #if defined(EA_PLATFORM_DESKTOP) + const int kThreadCount = 100; + #elif (EA_PLATFORM_WORD_SIZE == 8) + const int kThreadCount = 50; + #else + const int kThreadCount = 16; + #endif + + IdTestThread thread[kThreadCount]; + ThreadParameters threadParams; + EA::UnitTest::RandGenT random(EA::UnitTest::GetRandSeed()); + const int processorCount = EA::StdC::CountBits64(EA::Thread::GetAvailableCpuAffinityMask()); + + for(int i = 0; i < kThreadCount; i++) + { + threadParams.mnProcessor = calculateAvailableProcessorIndex(random(processorCount)); + threadParams.mpName = "IdTest"; + thread[i].mAssignedProcessorId = threadParams.mnProcessor; + thread[i].mThread.Begin(&thread[i], NULL, &threadParams); + } + + EA::UnitTest::ThreadSleep(1000); + + for(int i = 0; i < kThreadCount; i++) + thread[i].mSemaphore.Post(1); + + EA::UnitTest::ThreadSleep(1000); + + for(int i = 0; i < kThreadCount; i++) + thread[i].mThread.WaitForEnd(); + + EAReadBarrier(); + + EA::Thread::ThreadUniqueId uniqueIdArray[kThreadCount]; + EA::Thread::ThreadId idArray[kThreadCount]; + EA::Thread::SysThreadId sysIdArray[kThreadCount]; + + // Problem: We don't have an EAThreadEqual(const ThreadId&, const ThreadId&) function, but could use one. + // If we had such a thing, then we wouldn't need the odd code below and could probably use an eastl::set. + for(int i = 0; i < kThreadCount; i++) + { + memset(&uniqueIdArray[i], 0, sizeof(EA::Thread::ThreadUniqueId)); + memset(&idArray[i], 0, sizeof(EA::Thread::ThreadId)); + memset(&sysIdArray[i], 0, sizeof(EA::Thread::SysThreadId)); + } + + for(int i = 0; i < kThreadCount; i++) + { + for(int j = 0; j < i; j++) + EATEST_VERIFY(memcmp(&uniqueIdArray[j], &thread[i].mUniqueId, sizeof(EA::Thread::ThreadUniqueId)) != 0); + uniqueIdArray[i] = thread[i].mUniqueId; + + for(int j = 0; j < i; j++) + EATEST_VERIFY(memcmp(&idArray[j], &thread[i].mThreadId, sizeof(EA::Thread::ThreadId)) != 0); + idArray[i] = thread[i].mThreadId; + + for(int j = 0; j < i; j++) + EATEST_VERIFY(memcmp(&sysIdArray[j], &thread[i].mSysThreadId, sizeof(EA::Thread::SysThreadId)) != 0); + sysIdArray[i] = thread[i].mSysThreadId; + + #if defined(EA_PLATFORM_CONSOLE) && defined(EA_PLATFORM_MICROSOFT) + EATEST_VERIFY_F(thread[i].mProcessorId == thread[i].mAssignedProcessorId, + " Error: Thread assigned to run on processor %d, found to be running on processor %d.", + thread[i].mAssignedProcessorId, thread[i].mProcessorId); + #endif + } + + return nErrorCount; +} + + +int TestThreadDisablePriorityBoost() +{ + int nErrorCount = 0; + +#if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_XBOXONE) || defined(EA_PLATFORM_XBSX) + { + Thread thread; + ThreadParameters params; + + auto priorityBoostTester = [&](BOOL expectedDisablePriorityBoost) + { + thread.Begin( + [](void* pInExpectedDisablePriorityBoost) -> intptr_t + { + BOOL* pExpectedDisablePriorityBoost = (BOOL*)pInExpectedDisablePriorityBoost; + int nErrorCount = 0; + PBOOL pDisablePriorityBoost = nullptr; + + auto result = GetThreadPriorityBoost(GetCurrentThread(), pDisablePriorityBoost); + EATEST_VERIFY_MSG(result != 0, "GetThreadPriorityBoost failed\n"); + EATEST_VERIFY_MSG((result != 0) && *pDisablePriorityBoost == *pExpectedDisablePriorityBoost, + "Thread Priority Boost was not disabled\n"); + + return nErrorCount; + }, + &expectedDisablePriorityBoost, ¶ms); + + intptr_t threadErrorCount = 0; + thread.WaitForEnd(GetThreadTime() + 30000, &threadErrorCount); + nErrorCount += (int)threadErrorCount; + }; + + + params.mbDisablePriorityBoost = true; + priorityBoostTester(TRUE); + + params.mbDisablePriorityBoost = false; + priorityBoostTester(FALSE); + } +#endif + + return nErrorCount; +} + + +int TestThreadParameters() +{ // Test ThreadParameters + int nErrorCount = 0; + Thread::Status status; + const int kThreadCount(kMaxConcurrentThreadCount - 1); + ThreadId threadId[kThreadCount]; + Thread thread[kThreadCount]; + int i; + ThreadParameters threadParameters; + + const int nOriginalPriority = GetThreadPriority(); + + // Set our thread priority to match that of the threads we will be creating below. + SetThreadPriority(kThreadPriorityDefault + 1); + + for(i = 0; i < kThreadCount; i++) + { + status = thread[i].GetStatus(); + EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (2).\n"); + + // ThreadParameters + threadParameters.mnStackSize = 32768; + threadParameters.mnPriority = kThreadPriorityDefault + 1; + threadParameters.mpName = "abcdefghijklmnopqrstuvwxyz"; // Make an overly large name. + + threadId[i] = thread[i].Begin(TestFunction1, NULL, &threadParameters); + + EATEST_VERIFY_MSG(threadId[i] != (ThreadId)kThreadIdInvalid, "Thread failure: ThreadBegin failed.\n"); + + // It turns out that you can't really do such a thing as set lower priority in native Linux, not with SCHED_OTHER, at least. + #if ((!defined(EA_PLATFORM_UNIX) && !defined(__APPLE__)) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY + EATEST_VERIFY_MSG(thread[i].GetPriority() == threadParameters.mnPriority, "Thread failure: Thread Priority not set correctly (2).\n"); + + #endif + + if(i > 0) + EATEST_VERIFY_MSG(threadId[i] != threadId[i-1], "Thread failure: Thread id collision (2).\n"); + } + + ThreadSleep(200); + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + thread[i].SetName("0123456789012345678901234567890"); // Make an overly large name. + } + + ThreadSleep(200); + + // It turns out that you can't really do such a thing as set lower priority in native Linux. + #if ((!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !defined(__APPLE__) && !EA_USE_CPP11_CONCURRENCY) + int nPriority; + + if(threadId[0] != kThreadIdInvalid) + { + nPriority = thread[0].GetPriority(); + + thread[0].SetPriority(nPriority - 1); + EATEST_VERIFY_MSG(thread[0].GetPriority() == nPriority - 1, "Thread failure: Thread Priority not set correctly (3.1).\n"); + + thread[0].SetPriority(nPriority); + EATEST_VERIFY_MSG(thread[0].GetPriority() == nPriority, "Thread failure: Thread Priority not set correctly (3.2).\n"); + + } + #endif + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + thread[i].WaitForEnd(GetThreadTime() + 30000); + } + + SetThreadPriority(kThreadPriorityDefault); + + for(i = 0; i < kThreadCount; i++) + { + status = thread[i].GetStatus(); + + if(threadId[i] != kThreadIdInvalid) + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (2).\n"); + else + EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (2).\n"); + } + + SetThreadPriority(nOriginalPriority); + return nErrorCount; +} + +int TestThreadEnd() +{ + int nErrorCount(0); + EA::Thread::Thread thread; + Thread::Status status; + + thread.Begin(TestFunction13); + ThreadSleep(20); + + intptr_t returncode; + thread.WaitForEnd(); + status = thread.GetStatus(&returncode); + + EATEST_VERIFY_MSG(returncode == 42, "Thread return code failure: Expected return code 42."); + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded.\n"); + + return nErrorCount; +} + +EA_OPTIMIZE_OFF() +int TestThreadThread() +{ + int nErrorCount(0); + + sThreadTestTimeMS = (gTestLengthSeconds * 1000) / 2; // '/2' because this test doesn't need so much time. + + nErrorCount += TestSetThreadProcessor(); + nErrorCount += TestSetProcessor(); + +#if !defined(EA_PLATFORM_UNIX) && !defined(EA_PLATFORM_MOBILE) + nErrorCount += TestThreadAffinityMask(); +#endif + + + nErrorCount += TestThreadEnd(); + + { + ThreadId threadId = GetThreadId(); + + EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "GetThreadId failure.\n"); + + SysThreadId sysThreadId = GetSysThreadId(threadId); + + EATEST_VERIFY_MSG(sysThreadId != kSysThreadIdInvalid, "GetSysThreadId failure.\n"); + + #if (defined(EA_PLATFORM_MICROSOFT) || defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_PS4)) && !EA_USE_CPP11_CONCURRENCY + const void* pStackBase = GetThreadStackBase(); + const void* pStackTop = &pStackBase; + const intptr_t stackSize = ((char*)pStackBase - (char*)pStackTop); + + // Verify that pStackBase is non-NULL and that the stack size is less than N MB. + EATEST_VERIFY_MSG(pStackBase && (stackSize < 10485760), "GetThreadStackBase failure.\n"); + + #endif + + // We disable this test for now on Kettle because although we have 7 cores available + // there is no guaranty the their ID are 0..6 and the system takes core ID 7 + #if !defined(EA_PLATFORM_PS4) + + int processorCount = GetProcessorCount(); + int processor = GetThreadProcessor(); + + // This isn't much of a test, but it at least exercizes the function. + EATEST_VERIFY_F(processor < processorCount, " Error: GetThreadProcessor [%d] >= GetProcessorCount [%d].\n", processor, processorCount); + #endif + + // To do: Test this: + // void SetThreadProcessor(int nProcessor); + } + + { // Test Current thread functionality + const int nOriginalPriority = GetThreadPriority(); + int nPriority = kThreadPriorityDefault; + + EATEST_VERIFY_MSG(nPriority >= kThreadPriorityMin, "Thread priority failure (1).\n"); + EATEST_VERIFY_MSG(nPriority <= kThreadPriorityMax, "Thread priority failure (2).\n"); + + // It turns out that you can't really do such a thing as set lower priority with most Unix threading subsystems. + // You can do so with Cygwin because it is just a pthreads API running on Windows OS/threading. + // C++11 thread libraries also provide no means to set or query thread priority. + #if (!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY + int nPriority1; + bool bResult; + + bResult = SetThreadPriority(nPriority); + EATEST_VERIFY_MSG(bResult, "Thread priority failure (3).\n"); + + bResult = SetThreadPriority(nPriority - 1); + EATEST_VERIFY_MSG(bResult, "Thread priority failure (4).\n"); + + nPriority1 = GetThreadPriority(); + EATEST_VERIFY_MSG(nPriority1 == nPriority - 1, "Thread priority failure (5).\n"); + + bResult = SetThreadPriority(nPriority + 1); + EATEST_VERIFY_MSG(bResult, "Thread priority failure (6).\n"); + + nPriority1 = GetThreadPriority(); + EATEST_VERIFY_MSG(nPriority1 == nPriority + 1, "Thread priority failure (7).\n"); + + bResult = SetThreadPriority(kThreadPriorityDefault); + EATEST_VERIFY_MSG(bResult, "Thread priority failure (8).\n"); + + nPriority1 = GetThreadPriority(); + EATEST_VERIFY_MSG(nPriority1 == kThreadPriorityDefault, "Thread priority failure (9).\n"); + + #endif + + SetThreadPriority(nOriginalPriority); + ThreadSleep(kTimeoutImmediate); + ThreadSleep(500); + } + + #if defined(EA_PLATFORM_WINDOWS) && !EA_USE_CPP11_CONCURRENCY + { // Try to reproduce Windows problem with Thread::GetStatus returning kStatusEnded when it should return kStatusRunning. + // On my current work machine (WinXP32, Single P4 CPU) this problem doesn't occur. But it might occur with others. + Thread::Status status; + Thread threadBackground[8]; + Thread thread; + + for(int i = 0; i < 8; i++) + threadBackground[i].Begin(TestFunction1); + + EA::Thread::SetThreadPriority(kThreadPriorityDefault + 2); + thread.Begin(TestFunction1); + status = thread.GetStatus(); + EA::Thread::SetThreadPriority(kThreadPriorityDefault); + + EATEST_VERIFY_MSG(status == Thread::kStatusRunning, "Thread failure: Thread should have kStatusRunning.\n"); + + thread.WaitForEnd(); + } + #endif + + + EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(TestFunction3ExceptionWrapper); + EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(TestRunnable3ExceptionWrapper); + + { // Test thread creation functionality. + Thread::Status status; + const int kThreadCount(kMaxConcurrentThreadCount - 1); + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + int i; + + for(i = 0; i < kThreadCount; i++) + { + status = thread[i].GetStatus(); + EATEST_VERIFY_MSG(status == Thread::kStatusNone, "Thread failure: Thread should have kStatusNone (1).\n"); + + threadId[i] = thread[i].Begin(TestFunction1); + EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n"); + + threadId[i] = thread[i].Begin(TestFunction3); + EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction3) failed.\n"); + + threadId[i] = thread[i].Begin(&gTestRunnable3); + EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(&gTestRunnable3) failed.\n"); + + if(i > 0) + EATEST_VERIFY_MSG(threadId[i] != threadId[i-1], "Thread failure: Thread id collision (1).\n"); + } + + // It turns out that you can't really do such a thing as set lower priority in native Linux. + // C++11 threads also have no support for priorities + #if (!defined(EA_PLATFORM_UNIX) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY + int nPriority; + + nPriority = thread[0].GetPriority(); + thread[0].SetPriority(nPriority - 1); + + nPriority = thread[0].GetPriority(); + thread[0].SetPriority(nPriority + 1); + #endif + + ThreadSleep(200); + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + thread[i].WaitForEnd(GetThreadTime() + 30000); + } + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + { + status = thread[i].GetStatus(); + + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (1).\n"); + } + } + } + + + nErrorCount += TestThreadParameters(); + + { + // Test if we can set and retrieve a custom thread name + Thread::Status status; + Thread thread; + ThreadId threadId; + const char* threadName = "Test_Thread"; + const char* defaultName = "DEFAULT"; + ThreadParameters threadParameters; + + threadParameters.mpName = defaultName; + + sThreadTestTimeMS = 10000; + threadId = thread.Begin(TestFunction1, NULL, &threadParameters); + + EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n"); + EATEST_VERIFY_MSG(strncmp(defaultName, thread.GetName(), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name used when initializing the thread."); + + thread.SetName(threadName); + + EATEST_VERIFY_MSG(strncmp(threadName, thread.GetName(), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name set in SetName."); + + ThreadSleep(sThreadTestTimeMS + 1000); + + if(threadId != kThreadIdInvalid) + { + thread.WaitForEnd(GetThreadTime() + 30000); + status = thread.GetStatus(); + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n"); + } + } + + // Test the free standing function variant of the GetName/SetName API + { + // Test if we can set and retrieve a custom thread name + Thread::Status status; + Thread thread; + ThreadId threadId; + const char* threadName = "Test_Thread"; + const char* defaultName = "DEFAULT"; + + ThreadParameters threadParameters; + threadParameters.mpName = defaultName; + threadParameters.mnProcessor = EA::Thread::kProcessorAny; + threadParameters.mnAffinityMask = EA::Thread::GetAvailableCpuAffinityMask(); + + static volatile std::atomic sbThreadStarted; + static volatile std::atomic sbThreadTestDone; + sbThreadStarted = false; + sbThreadTestDone = false; + + threadId = thread.Begin( [](void*) -> intptr_t + { + sbThreadStarted = true; + while (!sbThreadTestDone) + ThreadSleep(); + return 0; + }, + NULL, &threadParameters); + + while(!sbThreadStarted) // Wait for thread to start up + ThreadSleep(); + + EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n"); + EATEST_VERIFY_MSG(strncmp(defaultName, GetThreadName(threadId), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name used when initializing the thread."); + + SetThreadName(threadId, threadName); + EATEST_VERIFY_MSG(strncmp(threadName, GetThreadName(threadId), EATHREAD_NAME_SIZE) == 0, "Thread failure: GetName should return the name set in SetName."); + + sbThreadTestDone = true; // signal that test is completed and the thread can shutdown + if(threadId != kThreadIdInvalid) + { + thread.WaitForEnd(GetThreadTime() + 30000); + status = thread.GetStatus(); + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n"); + } + } + + + { + // Test the creation+destruction of many threads to make sure resources are recycled properly + + #if defined(EA_PLATFORM_DESKTOP) + const int kThreadCount(200); + #else + const int kThreadCount(20); + #endif + Thread thread; + ThreadId threadId; + intptr_t returnValue; + + EA::UnitTest::ReportVerbosity(1, "Creating many threads and then WaitForEnd()\n"); + + for(int i = 0; i < kThreadCount; i++) + { + threadId = thread.Begin(TestFunction4, reinterpret_cast((uintptr_t)i)); + + Thread::Status status = thread.GetStatus(&returnValue); + if(status != Thread::kStatusEnded) + thread.WaitForEnd(GetThreadTime() + 30000, &returnValue); + EA::UnitTest::ReportVerbosity(1, "Thread ended.\n"); + + EATEST_VERIFY_F(returnValue == i, "Thread failure: Thread return code is wrong (1). threadId: %s, status: %d, returnValue: %d\n", EAThreadThreadIdToString(threadId), (int)status, (int)returnValue); + if(returnValue != i) + EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue); + + // Get the status again to make sure it returns the correct status. + thread.GetStatus(&returnValue); + EATEST_VERIFY_F(returnValue == i, "Thread failure: Thread return code is wrong (2). threadId: %s, status: %d, returnValue: %d\n", EAThreadThreadIdToString(threadId), (int)status, (int)returnValue); + if(returnValue != i) + EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue); + } + + + EA::UnitTest::ReportVerbosity(1, "Creating many threads and then repeat GetStatus() until they finish\n"); + for(int i = 0; i < kThreadCount + 1; i++) + { + // Windows will get stuck in infinite loop if return value is 259 (STILL_ACTIVE) + if(i == 259) + ++i; + + threadId = thread.Begin(TestFunction4, reinterpret_cast((uintptr_t)i)); + + const ThreadTime nGiveUpTime = EA::Thread::GetThreadTime() + 20000; // Give up after N milliseconds. + + // Test to see if GetStatus() will recycle system resource properly + while(thread.GetStatus(&returnValue) != Thread::kStatusEnded) + { + ThreadSleep(100); + + if(EA::Thread::GetThreadTime() > nGiveUpTime) + { + EA::UnitTest::ReportVerbosity(1, "Thread failure: GetStatus failed.\n"); + break; + } + } + + EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (3).\n"); + if(returnValue != i) + EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue); + + // Get the status again to make sure it returns the correct status. + thread.GetStatus(&returnValue); + EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (4).\n"); + if(returnValue != i) + EA::UnitTest::ReportVerbosity(1, " Expected: %u, actual: %" PRId64 ".\n", (unsigned)i, (int64_t)returnValue); + + // See if calling WaitForEnd() now will result in a crash + thread.WaitForEnd(GetThreadTime() + 30000, &returnValue); + EATEST_VERIFY_MSG(returnValue == i, "Thread failure: Thread return code is wrong (5).\n"); + } + } + + { + // Test the creation of many threads + + #if defined(EA_PLATFORM_DESKTOP) + Thread::Status status; + const int kThreadCount(96); + Thread thread[kThreadCount]; + ThreadId threadId[kThreadCount]; + int i; + + sThreadTestTimeMS = 10000; + + for(i = 0; i < kThreadCount; i++) + { + threadId[i] = thread[i].Begin(TestFunction1); + EATEST_VERIFY_MSG(threadId[i] != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction1) failed.\n"); + } + + ThreadSleep(sThreadTestTimeMS + 1000); + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + thread[i].WaitForEnd(GetThreadTime() + 30000); + } + + for(i = 0; i < kThreadCount; i++) + { + if(threadId[i] != kThreadIdInvalid) + { + status = thread[i].GetStatus(); + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded (3).\n"); + } + } + #endif + } + + { + // Regression of Thread dtor behaviour - the dtor should not wait for created threads + // We reuse the atomic shouldgo to figure out what is really happening setting it to 0 + // initially and then incrementing it when the thread callback completes, allowing + // us to detect whether the dtor completed before the thread callback. + sShouldGo = 0; + { + // Create a scope for our thread object + Thread thread; + + // Get our thread going. It will sleep immediately for 2 seconds and then increment sShouldGo + ThreadId threadId = thread.Begin(TestFunction7); + EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: thread.Begin(TestFunction7) failed.\n"); + + // Now we exit our scope while our thread in theory still has a second before it completes + } // NOTE(rparolin): If your test hangs here, its most likely due to a semantic change in the thread object that is waiting for threads to complete. + + // Signal to the thread it is allowed to complete. + sShouldGo = 1; + } + + + { + int nOriginalPriority = EA::Thread::GetThreadPriority(); + EA::Thread::SetThreadPriority(kThreadPriorityDefault); + + // Tests setting and getting thread priority while the thread is active. + Thread::Status status; + Thread thread; + ThreadId threadId; + sShouldGo = 0; + + threadId = thread.Begin(TestFunction7); + EATEST_VERIFY_MSG(threadId != kThreadIdInvalid, "Thread failure: ThreadBegin(TestFunction7) failed.\n"); + + // It turns out that you can't really do such a thing as set lower priority in native Linux. + #if ((!defined(EA_PLATFORM_LINUX) && !defined(__APPLE__) && !defined(EA_PLATFORM_BSD)) || defined(__CYGWIN__)) && !EA_USE_CPP11_CONCURRENCY + int nPriority = thread.GetPriority(); + + thread.SetPriority(nPriority - 1); + if(EATEST_VERIFY_MSG(thread.GetPriority() == nPriority - 1, "Thread failure: Thread Priority not set correctly (2).\n")) + nErrorCount++; + + thread.SetPriority(nPriority); + EATEST_VERIFY_MSG(thread.GetPriority() == nPriority, "Thread failure: Thread Priority not set correctly (3).\n"); + #endif + + sShouldGo = 1; + thread.WaitForEnd(GetThreadTime() + 30000); + + status = thread.GetStatus(); + EATEST_VERIFY_MSG(status == Thread::kStatusEnded, "Thread failure: Thread should have kStatusEnded.\n"); + EA::Thread::SetThreadPriority(nOriginalPriority); + } + + nErrorCount += TestThreadDynamicData(); + nErrorCount += TestThreadPriorities(); + nErrorCount += TestSetThreadProcessorByThreadParams(); + nErrorCount += TestSetThreadProcessConstants(); + nErrorCount += TestNullThreadNames(); + nErrorCount += TestLambdaThreads(); + + { + // Test SetDefaultProcessor + Thread::SetDefaultProcessor(kProcessorAny); + } + + + #if !defined(EA_PLATFORM_MOBILE) + // This test does not execute correctly on Android. The 'while(sThreadCount + // < kThreadCount)' loop sometimes hangs indefinitely because the spawned + // threads exit often before the loop continues, so the spawned + // thread count never gets up to kThreadCount. I guess the inner loop + // should be testing addedThreadCount too. (?) But, just disable for now, + // since I guess it's working on other platforms. + { + // Test very many threads starting and quitting, recycling + // The PS3 code had problems with leaking threads in this case. + // Create a bunch of threads. + // Every N ms quit one of them and create a new one. + + // This test tends to be quite taxing on system resources, so cut it back for + // certain platforms. To do: use EATest's platform speed metrics. + ThreadParameters params; + const int kThreadCount(48); // This needs to be greater than the eathread_thread.cpp kMaxThreadDynamicDataCount value. + #ifdef EA_PLATFORM_NX + const int kTotalThreadsToRun(64); + + const auto nAllCoresAffinityMask = ((INT64_C(1) << EA::Thread::GetProcessorCount()) - 1); + const auto nAllCoresButMainAffinityMask = nAllCoresAffinityMask & INT64_C(0XFFFFFFFFFFFFFE); + + params.mnAffinityMask = nAllCoresButMainAffinityMask; + params.mnProcessor = kProcessorAny; + #else + const int kTotalThreadsToRun(500); + #endif + + Thread thread; + ThreadId threadId; + int addedThreadCount = 0; + int i = 0; + + sThreadCount = 0; + sShouldGo = 0; + + // Create threads. + for(i = 0; i < kThreadCount; i++) + { + threadId = thread.Begin(TestFunction6, reinterpret_cast((uintptr_t)i), ¶ms); + EATEST_VERIFY(threadId != kThreadIdInvalid); + } + + // Wait until they are all created and started. + while(sThreadCount < kThreadCount) + ThreadSleep(500); + + // Let them run and exit as they go. + sShouldGo = 1; + + // Add new threads as existing ones leave. + while(addedThreadCount < kTotalThreadsToRun) + { + if(sThreadCount < kThreadCount) + { + threadId = thread.Begin(TestFunction6, reinterpret_cast((uintptr_t)i++), ¶ms); + + EATEST_VERIFY(threadId != kThreadIdInvalid); + + addedThreadCount++; + + #if defined(EA_PLATFORM_DESKTOP) + // The created threads will not get any time slices on weaker platforms. + // thus making the test create threads until the system is out of resources + ThreadSleep(kTimeoutYield); + + // Sometimes it will never exit this loop.. because it will leave faster then it is made + if(addedThreadCount >= kTotalThreadsToRun) + break; + #endif + } + else + ThreadSleep(10); + } + + // Wait until they have all exited. + while(sThreadCount != 0) // While there are threads... + ThreadSleep(500); + + // On some platforms it seems that thread entry/exit is so slow that the thread count might not necessarily + // reflect the actual number of threads running. This was causing an issue where the function was + // exiting before all threads completed, so I added this wait. It may be worth considering + // keeping track of all created threads in an array, and then using a thread-join operation here + // instead + ThreadSleep(1000); + } + + #endif + + return nErrorCount; +} + + + + + + + + + diff --git a/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadThreadPool.cpp b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadThreadPool.cpp new file mode 100644 index 00000000..198181b0 --- /dev/null +++ b/r5dev/thirdparty/ea/EAThread/test/thread/source/TestThreadThreadPool.cpp @@ -0,0 +1,100 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Electronic Arts Inc. All rights reserved. +/////////////////////////////////////////////////////////////////////////////// + +#include "TestThread.h" +#include +#include +#include +#include + + +using namespace EA::Thread; + + +const int kMaxConcurrentThreadCount = EATHREAD_MAX_CONCURRENT_THREAD_COUNT; + + +struct TPWorkData +{ + int mnWorkItem; + TPWorkData(int nWorkItem) : mnWorkItem(nWorkItem) {} +}; + + +static AtomicInt32 gWorkItemsCreated = 0; +static AtomicInt32 gWorkItemsProcessed = 0; + + +static intptr_t WorkerFunction(void* pvWorkData) +{ + TPWorkData* pWorkData = (TPWorkData*)pvWorkData; + + ThreadId threadId = GetThreadId(); + EA::UnitTest::ReportVerbosity(1, "Work %4d starting for thread %s.\n", pWorkData->mnWorkItem, EAThreadThreadIdToString(threadId)); + + EA::UnitTest::ThreadSleepRandom(200, 600); + ++gWorkItemsProcessed; + + EA::UnitTest::ReportVerbosity(1, "Work %4d ending for thread %s.\n", pWorkData->mnWorkItem, EAThreadThreadIdToString(threadId)); + + delete pWorkData; + + return 0; +} + + +int TestThreadThreadPool() +{ + int nErrorCount(0); + + #if EA_THREADS_AVAILABLE + { + ThreadPoolParameters tpp; + tpp.mnMinCount = kMaxConcurrentThreadCount - 1; + tpp.mnMaxCount = kMaxConcurrentThreadCount - 1; + tpp.mnInitialCount = 0; + + tpp.mnIdleTimeoutMilliseconds = EA::Thread::kTimeoutNone; // left in to test the usage of this kTimeout* defines. + tpp.mnIdleTimeoutMilliseconds = 20000; + + + ThreadPool threadPool(&tpp); + int nResult; + + for(unsigned int i = 0; i < gTestLengthSeconds * 3; i++) + { + const int nWorkItem = (int)gWorkItemsCreated++; + TPWorkData* const pWorkData = new TPWorkData(nWorkItem); + + EA::UnitTest::ReportVerbosity(1, "Work %4d created.\n", nWorkItem); + nResult = threadPool.Begin(WorkerFunction, pWorkData, NULL, true); + + EATEST_VERIFY_MSG(nResult != ThreadPool::kResultError, "Thread pool failure in Begin."); + + EA::UnitTest::ThreadSleepRandom(300, 700); + //Todo: If the pool task length gets too long, wait some more. + } + + EA::UnitTest::ReportVerbosity(1, "Shutting down thread pool.\n"); + bool bShutdownResult = threadPool.Shutdown(ThreadPool::kJobWaitAll, GetThreadTime() + 60000); + + EATEST_VERIFY_MSG(bShutdownResult, "Thread pool failure in Shutdown (waiting for jobs to complete)."); + } + + EATEST_VERIFY_MSG(gWorkItemsCreated == gWorkItemsProcessed, "Thread pool failure: gWorkItemsCreated != gWorkItemsProcessed."); + #endif + + return nErrorCount; +} + + + + + + + + + + + diff --git a/r5dev/thirdparty/imgui/CMakeLists.txt b/r5dev/thirdparty/imgui/CMakeLists.txt index 1bec7d7b..d03c6cae 100644 --- a/r5dev/thirdparty/imgui/CMakeLists.txt +++ b/r5dev/thirdparty/imgui/CMakeLists.txt @@ -29,6 +29,8 @@ add_sources( SOURCE_GROUP "Misc" "misc/imgui_editor.h" "misc/imgui_logger.cpp" "misc/imgui_logger.h" + "misc/imgui_snapshot.cpp" + "misc/imgui_snapshot.h" "misc/imgui_utility.cpp" "misc/imgui_utility.h" "misc/cpp/imgui_stdlib.cpp" diff --git a/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.cpp b/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.cpp index b9690c3c..bfaa030a 100644 --- a/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.cpp +++ b/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.cpp @@ -7,8 +7,11 @@ // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp // CHANGELOG // (minor and older changes stripped away, please see git history for details) @@ -31,6 +34,7 @@ // 2016-05-07: DirectX11: Disabling depth-write. #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_dx11.h" // DirectX @@ -422,9 +426,9 @@ bool ImGui_ImplDX11_CreateDeviceObjects() // Create the input layout D3D11_INPUT_ELEMENT_DESC local_layout[] = { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK) { @@ -582,6 +586,7 @@ void ImGui_ImplDX11_Shutdown() if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); } io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; IM_DELETE(bd); } @@ -593,3 +598,7 @@ void ImGui_ImplDX11_NewFrame() if (!bd->pFontSampler) ImGui_ImplDX11_CreateDeviceObjects(); } + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.h b/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.h index f12d7186..20887f37 100644 --- a/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.h +++ b/r5dev/thirdparty/imgui/backends/imgui_impl_dx11.h @@ -5,13 +5,17 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE struct ID3D11Device; struct ID3D11DeviceContext; @@ -24,3 +28,5 @@ IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); + +#endif // #ifndef IMGUI_DISABLE diff --git a/r5dev/thirdparty/imgui/backends/imgui_impl_win32.cpp b/r5dev/thirdparty/imgui/backends/imgui_impl_win32.cpp index bdf27f97..671f73e2 100644 --- a/r5dev/thirdparty/imgui/backends/imgui_impl_win32.cpp +++ b/r5dev/thirdparty/imgui/backends/imgui_impl_win32.cpp @@ -3,16 +3,21 @@ // Implemented features: // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp #include "imgui.h" +#ifndef IMGUI_DISABLE #include "imgui_impl_win32.h" #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -22,7 +27,6 @@ #include #include -// Includes of the game have to be here since this header is precompiled. #include "d3d11.h" #include "windows/id3dx.h" // ImGui_ImplWin32_UpdateMouseCursor @@ -38,6 +42,11 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. +// 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). +// 2023-09-07: Inputs: Added support for keyboard codepage conversion for when application is compiled in MBCS mode and using a non-Unicode window. +// 2023-04-19: Added ImGui_ImplWin32_InitForOpenGL() to facilitate combining raw Win32/Winapi with OpenGL. (#3218) +// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen. (#2702) // 2023-02-15: Inputs: Use WM_NCMOUSEMOVE / WM_NCMOUSELEAVE to track mouse position over non-client area (e.g. OS decorations) when app is not focused. (#6045, #6162) // 2023-02-02: Inputs: Flipping WM_MOUSEHWHEEL (horizontal mouse-wheel) value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -94,6 +103,7 @@ struct ImGui_ImplWin32_Data INT64 Time; INT64 TicksPerSecond; ImGuiMouseCursor LastMouseCursor; + UINT32 KeyboardCodePage; #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD bool HasGamepad; @@ -116,7 +126,17 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() } // Functions -bool ImGui_ImplWin32_Init(void* hwnd) +static void ImGui_ImplWin32_UpdateKeyboardCodePage() +{ + // Retrieve keyboard code page, required for handling of non-Unicode Windows. + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + HKL keyboard_layout = ::GetKeyboardLayout(0); + LCID keyboard_lcid = MAKELCID(HIWORD(keyboard_layout), SORT_DEFAULT); + if (::GetLocaleInfoA(keyboard_lcid, (LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE), (LPSTR)&bd->KeyboardCodePage, sizeof(bd->KeyboardCodePage)) == 0) + bd->KeyboardCodePage = CP_ACP; // Fallback to default ANSI code page when fails. +} + +static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); @@ -138,9 +158,11 @@ bool ImGui_ImplWin32_Init(void* hwnd) bd->TicksPerSecond = perf_frequency; bd->Time = perf_counter; bd->LastMouseCursor = ImGuiMouseCursor_COUNT; + ImGui_ImplWin32_UpdateKeyboardCodePage(); // Set platform dependent data in viewport ImGui::GetMainViewport()->PlatformHandleRaw = (void*)hwnd; + IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch // Dynamically load XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD @@ -166,6 +188,17 @@ bool ImGui_ImplWin32_Init(void* hwnd) return true; } +IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd) +{ + return ImGui_ImplWin32_InitEx(hwnd, false); +} + +IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd) +{ + // OpenGL needs CS_OWNDC + return ImGui_ImplWin32_InitEx(hwnd, true); +} + void ImGui_ImplWin32_Shutdown() { ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); @@ -180,6 +213,7 @@ void ImGui_ImplWin32_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); IM_DELETE(bd); } @@ -490,6 +524,20 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) case VK_F10: return ImGuiKey_F10; case VK_F11: return ImGuiKey_F11; case VK_F12: return ImGuiKey_F12; + case VK_F13: return ImGuiKey_F13; + case VK_F14: return ImGuiKey_F14; + case VK_F15: return ImGuiKey_F15; + case VK_F16: return ImGuiKey_F16; + case VK_F17: return ImGuiKey_F17; + case VK_F18: return ImGuiKey_F18; + case VK_F19: return ImGuiKey_F19; + case VK_F20: return ImGuiKey_F20; + case VK_F21: return ImGuiKey_F21; + case VK_F22: return ImGuiKey_F22; + case VK_F23: return ImGuiKey_F23; + case VK_F24: return ImGuiKey_F24; + case VK_BROWSER_BACK: return ImGuiKey_AppBack; + case VK_BROWSER_FORWARD: return ImGuiKey_AppForward; default: return ImGuiKey_None; } } @@ -514,6 +562,19 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) // Copy this line into your .cpp file to forward declare the function. extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); #endif + +// See https://learn.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages +// Prefer to call this at the top of the message handler to avoid the possibility of other Win32 calls interfering with this. +static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() +{ + LPARAM extra_info = ::GetMessageExtraInfo(); + if ((extra_info & 0xFFFFFF80) == 0xFF515700) + return ImGuiMouseSource_Pen; + if ((extra_info & 0xFFFFFF80) == 0xFF515780) + return ImGuiMouseSource_TouchScreen; + return ImGuiMouseSource_Mouse; +} + IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui::GetCurrentContext() == nullptr) @@ -528,6 +589,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_NCMOUSEMOVE: { // We need to call TrackMouseEvent in order to receive WM_MOUSELEAVE events + ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); const int area = (msg == WM_MOUSEMOVE) ? 1 : 2; bd->MouseHwnd = hwnd; if (bd->MouseTrackedArea != area) @@ -542,6 +604,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) }; if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates. break; + io.AddMouseSourceEvent(mouse_source); io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); break; } @@ -563,6 +626,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: { + ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); int button = 0; if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } @@ -571,6 +635,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA if (bd->MouseButtonsDown == 0 && ::GetCapture() == nullptr) ::SetCapture(hwnd); bd->MouseButtonsDown |= 1 << button; + io.AddMouseSourceEvent(mouse_source); io.AddMouseButtonEvent(button, true); return 0; } @@ -579,6 +644,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_MBUTTONUP: case WM_XBUTTONUP: { + ImGuiMouseSource mouse_source = GetMouseSourceFromMessageExtraInfo(); int button = 0; if (msg == WM_LBUTTONUP) { button = 0; } if (msg == WM_RBUTTONUP) { button = 1; } @@ -587,6 +653,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA bd->MouseButtonsDown &= ~(1 << button); if (bd->MouseButtonsDown == 0 && ::GetCapture() == hwnd) ::ReleaseCapture(); + io.AddMouseSourceEvent(mouse_source); io.AddMouseButtonEvent(button, false); return 0; } @@ -612,10 +679,14 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA int vk = (int)wParam; if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) vk = IM_VK_KEYPAD_ENTER; - - // Submit key event const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); const int scancode = (int)LOBYTE(HIWORD(lParam)); + + // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event. + if (key == ImGuiKey_PrintScreen && !is_key_down) + ImGui_ImplWin32_AddKeyEvent(key, true, vk, scancode); + + // Submit key event if (key != ImGuiKey_None) ImGui_ImplWin32_AddKeyEvent(key, is_key_down, vk, scancode); @@ -643,6 +714,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_KILLFOCUS: io.AddFocusEvent(msg == WM_SETFOCUS); return 0; + case WM_INPUTLANGCHANGE: + ImGui_ImplWin32_UpdateKeyboardCodePage(); + return 0; case WM_CHAR: if (::IsWindowUnicode(hwnd)) { @@ -653,7 +727,7 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA else { wchar_t wch = 0; - ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1); + ::MultiByteToWideChar(bd->KeyboardCodePage, MB_PRECOMPOSED, (char*)&wParam, 1, &wch, 1); io.AddInputCharacter(wch); } return 0; @@ -832,3 +906,5 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) } //--------------------------------------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/r5dev/thirdparty/imgui/backends/imgui_impl_win32.h b/r5dev/thirdparty/imgui/backends/imgui_impl_win32.h index b74b4387..be562714 100644 --- a/r5dev/thirdparty/imgui/backends/imgui_impl_win32.h +++ b/r5dev/thirdparty/imgui/backends/imgui_impl_win32.h @@ -3,19 +3,25 @@ // Implemented features: // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) +// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); +IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); @@ -42,3 +48,5 @@ IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // // - Use to enable alpha compositing transparency with the desktop. // - Use together with e.g. clearing your framebuffer with zero-alpha. IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd + +#endif // #ifndef IMGUI_DISABLE diff --git a/r5dev/thirdparty/imgui/imgui.cpp b/r5dev/thirdparty/imgui/imgui.cpp index abc4e45b..75946bef 100644 --- a/r5dev/thirdparty/imgui/imgui.cpp +++ b/r5dev/thirdparty/imgui/imgui.cpp @@ -1,30 +1,33 @@ -// dear imgui, v1.89.4 +// dear imgui, v1.90.4 // (main code and documentation) // Help: -// - Read FAQ at http://dearimgui.org/faq -// - Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. +// - See links below. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. -// Read imgui.cpp for details, links and comments. +// - Read top of imgui.cpp for more details, links and comments. // Resources: -// - FAQ http://dearimgui.org/faq -// - Homepage & latest https://github.com/ocornut/imgui +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Homepage https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/5886 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/6897 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues +// - Tests & Automation https://github.com/ocornut/imgui_test_engine -// Getting Started? -// - For first-time users having issues compiling/linking/running or issues loading fonts: -// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. +// For first-time users having issues compiling/linking/running/loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. +// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there. +// Copyright (c) 2014-2024 Omar Cornut // Developed by Omar Cornut and every direct or indirect contributors to the GitHub. // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. -// Businesses: you can support continued development via invoiced technical support, maintenance and sponsoring contracts. Please reach out to "contact AT dearimgui.com". -// Individuals: you can support continued development via donations. See docs/README or web page. +// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts. +// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Sponsors +// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. // Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without @@ -48,7 +51,7 @@ DOCUMENTATION - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE - API BREAKING CHANGES (read me when you update!) - FREQUENTLY ASKED QUESTIONS (FAQ) - - Read all answers online: https://www.dearimgui.org/faq, or in docs/FAQ.md (with a Markdown viewer) + - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer) CODE (search for "[SECTION]" in the code to find them) @@ -72,6 +75,7 @@ CODE // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) // [SECTION] INPUTS // [SECTION] ERROR CHECKING +// [SECTION] ITEM SUBMISSION // [SECTION] LAYOUT // [SECTION] SCROLLING // [SECTION] TOOLTIPS @@ -85,7 +89,7 @@ CODE // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW // [SECTION] DEBUG LOG WINDOW -// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL) */ @@ -107,9 +111,10 @@ CODE - Portable, minimize dependencies, run on target (consoles, phones, etc.). - Efficient runtime and memory consumption. - Designed for developers and content-creators, not the typical end-user! Some of the current weaknesses includes: + Designed primarily for developers and content-creators, not the typical end-user! + Some of the current weaknesses (which we aim to address in the future) includes: - - Doesn't look fancy, doesn't animate. + - Doesn't look fancy. - Limited layout features, intricate layouts are typically crafted in code. @@ -159,7 +164,7 @@ CODE - GAMEPAD CONTROLS - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse! - - Download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets + - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets - Backend support: backend needs to: - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys. - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly. @@ -188,9 +193,11 @@ CODE READ FIRST ---------- - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki) - - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction or - destruction steps, less superfluous data retention on your side, less state duplication, less state synchronization, fewer bugs. + - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone! + The UI can be highly dynamic, there are no construction or destruction steps, less superfluous + data retention on your side, less state duplication, less state synchronization, fewer bugs. - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. + Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version. - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build. - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori). You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki. @@ -198,18 +205,38 @@ CODE For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI, where the UI code is called multiple times ("multiple passes") from a single entry point. There are pros and cons to both approaches. - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right. - - This codebase is also optimized to yield decent performances with typical "Debug" builds settings. - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected). If you get an assert, read the messages and comments around the assert. - - C++: this is a very C-ish codebase: we don't rely on C++11, we don't include any C++ headers, and ImGui:: is a namespace. - - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. - See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. - However, imgui_internal.h can optionally export math operators for ImVec2/ImVec4, which we use in this codebase. - - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction (avoid using it in your code!). + - This codebase aims to be highly optimized: + - A typical idle frame should never call malloc/free. + - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible. + - We put particular energy in making sure performances are decent with typical "Debug" build settings as well. + Which mean we tend to avoid over-relying on "zero-cost abstraction" as they aren't zero-cost at all. + - This codebase aims to be both highly opinionated and highly flexible: + - This code works because of the things it choose to solve or not solve. + - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers, + and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now). + This is to increase compatibility, increase maintainability and facilitate use from other languages. + - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types. + See FAQ "How can I use my own math types instead of ImVec2/ImVec4?" for details about setting up imconfig.h for that. + We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally. + - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction + (so don't use ImVector in your code or at our own risk!). + - Building: We don't use nor mandate a build system for the main library. + This is in an effort to ensure that it works in the real world aka with any esoteric build setup. + This is also because providing a build system for the main library would be of little-value. + The build problems are almost never coming from the main library but from specific backends. HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI ---------------------------------------------- + - Update submodule or copy/overwrite every file. + - About imconfig.h: + - You may modify your copy of imconfig.h, in this case don't overwrite it. + - or you may locally branch to modify imconfig.h and merge/rebase latest. + - or you may '#define IMGUI_USER_CONFIG "my_config_file.h"' globally from your build system to + specify a custom path for your imconfig.h file and instead not have to modify the default one. + - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h) - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over "master". - You can also use '#define IMGUI_USER_CONFIG "my_config_file.h" to redirect configuration to your own file. @@ -218,11 +245,12 @@ CODE from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will likely be a comment about it. Please report any issue to the GitHub page! - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file. - - Try to keep your copy of Dear ImGui reasonably up to date. + - Try to keep your copy of Dear ImGui reasonably up to date! GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE --------------------------------------------------------------- + - See https://github.com/ocornut/imgui/wiki/Getting-Started. - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library. - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder. - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system. @@ -286,7 +314,7 @@ CODE // Build and load the texture atlas into a texture // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer) int width, height; - unsigned char* pixels = NULL; + unsigned char* pixels = nullptr; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // At this point you've got the texture data and you need to upload that to your graphic system: @@ -330,7 +358,7 @@ CODE To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application, you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! - Please read the FAQ and example applications for details about this! + Please read the FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" about this. HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE @@ -397,7 +425,47 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago: + - 2024/01/15 (1.90.2) - commented out obsolete ImGuiIO::ImeWindowHandle marked obsolete in 1.87, favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'. + - 2023/12/19 (1.90.1) - commented out obsolete ImGuiKey_KeyPadEnter redirection to ImGuiKey_KeypadEnter. + - 2023/11/06 (1.90.1) - removed CalcListClipping() marked obsolete in 1.86. Prefer using ImGuiListClipper which can return non-contiguous ranges. + - 2023/11/05 (1.90.1) - imgui_freetype: commented out ImGuiFreeType::BuildFontAtlas() obsoleted in 1.81. prefer using #define IMGUI_ENABLE_FREETYPE or see commented code for manual calls. + - 2023/11/05 (1.90.1) - internals,columns: commented out legacy ImGuiColumnsFlags_XXX symbols redirecting to ImGuiOldColumnsFlags_XXX, obsoleted from imgui_internal.h in 1.80. + - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete). + - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete). + those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features. + - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior "bool-to-flags" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work. + - old: BeginChild("Name", size, true) + - new: BeginChild("Name", size, ImGuiChildFlags_Border) + - old: BeginChild("Name", size, false) + - new: BeginChild("Name", size) or BeginChild("Name", 0) or BeginChild("Name", size, ImGuiChildFlags_None) + - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow. + - old: BeginChild("Name", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding); + - new: BeginChild("Name", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0); + - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user). + - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() ("Stack Tool") to ShowIDStackToolWindow() ("ID Stack Tool"), as earlier name was misleading. Kept inline redirection function. (#4631) + - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of "name getter" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete). + - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...) + - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...); + - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...); + - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...); + - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions: + - GetWindowContentRegionWidth() -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful. + - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources. + - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry! + - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878) + - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15). + - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). + - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior. + - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610) + - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage. + - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3. + - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago: + - ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference) + - ListBoxFooter() -> use EndListBox() + - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin(). + - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices(). + - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago: - ImGuiSliderFlags_ClampOnInput -> use ImGuiSliderFlags_AlwaysClamp - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite - ImDrawList::AddBezierCurve() -> use ImDrawList::AddBezierCubic() @@ -783,7 +851,7 @@ CODE ================================ Read all answers online: - https://www.dearimgui.org/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url) + https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url) Read all answers locally (with a text editor or ideally a Markdown viewer): docs/FAQ.md Some answers are copied down here to facilitate searching in code. @@ -793,11 +861,12 @@ CODE Q: Where is the documentation? A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++. - - Run the examples/ and explore them. + - Run the examples/ applications and explore them. + - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide. - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. - The demo covers most features of Dear ImGui, so you can read the code and see its output. - See documentation and comments at the top of imgui.cpp + effectively imgui.h. - - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the + - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ folder to explain how to integrate Dear ImGui with your own engine/application. - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links. - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful. @@ -807,24 +876,24 @@ CODE Q: What is this library called? Q: Which version should I get? >> This library is called "Dear ImGui", please don't call it "ImGui" :) - >> See https://www.dearimgui.org/faq for details. + >> See https://www.dearimgui.com/faq for details. Q&A: Integration ================ Q: How to get started? - A: Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. + A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt. Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application? A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags! - >> See https://www.dearimgui.org/faq for a fully detailed answer. You really want to read this. + >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this. - Q. How can I enable keyboard controls? - Q: How can I use this without a mouse, without a keyboard or without a screen? (gamepad, input share, remote display) + Q. How can I enable keyboard or gamepad controls? + Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display) Q: I integrated Dear ImGui in my engine and little squares are showing instead of text... Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around... Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries... - >> See https://www.dearimgui.org/faq + >> See https://www.dearimgui.com/faq Q&A: Usage ---------- @@ -835,10 +904,10 @@ CODE - How can I have multiple widgets with the same label? - How can I have multiple windows with the same label? Q: How can I display an image? What is ImTextureID, how does it work? - Q: How can I use my own math types instead of ImVec2/ImVec4? + Q: How can I use my own math types instead of ImVec2? Q: How can I interact with standard C++ types (such as std::string and std::vector)? Q: How can I display custom shapes? (using low-level ImDrawList API) - >> See https://www.dearimgui.org/faq + >> See https://www.dearimgui.com/faq Q&A: Fonts, Text ================ @@ -848,7 +917,7 @@ CODE Q: How can I easily use icons in my application? Q: How can I load multiple fonts? Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? - >> See https://www.dearimgui.org/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md + >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/blob/master/docs/FONTS.md Q&A: Concerns ============= @@ -857,18 +926,18 @@ CODE Q: Can you create elaborate/serious tools with Dear ImGui? Q: Can you reskin the look of Dear ImGui? Q: Why using C++ (as opposed to C)? - >> See https://www.dearimgui.org/faq + >> See https://www.dearimgui.com/faq Q&A: Community ============== Q: How can I help? - A: - Businesses: please reach out to "contact AT dearimgui.com" if you work in a place using Dear ImGui! + A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. - This is among the most useful thing you can do for Dear ImGui. With increased funding, we can hire more people working on this project. - - Individuals: you can support continued development via PayPal donations. See README. - - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, read docs/TODO.txt - and see how you want to help and can help! + This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project. + Also see https://github.com/ocornut/imgui/wiki/Sponsors + - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. + - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help! - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers. But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions. @@ -894,11 +963,7 @@ CODE // System includes #include // vsnprintf, sscanf, printf -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif // [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled #if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) @@ -977,17 +1042,21 @@ CODE // Debug options #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window -#define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower) // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time before the window list starts to appear +static const float NAV_ACTIVATE_HIGHLIGHT_TIMER = 0.10f; // Time to highlight an item activated by a shortcut. + // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend) static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow(). static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. +// Tooltip offset +static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale + //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- @@ -997,7 +1066,6 @@ static void FindHoveredWindow(); static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags); static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window); -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); // Settings @@ -1033,7 +1101,6 @@ static ImVec2 NavCalcPreferredRefPos(); static void NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window); static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window); static void NavRestoreLayer(ImGuiNavLayer layer); -static void NavRestoreHighlightAfterMove(); static int FindWindowFocusIndex(ImGuiWindow* window); // Error Checking and Debug Tools @@ -1041,6 +1108,7 @@ static void ErrorCheckNewFrameSanityChecks(); static void ErrorCheckEndFrameSanityChecks(); static void UpdateDebugToolItemPicker(); static void UpdateDebugToolStackQueries(); +static void UpdateDebugToolFlashStyleColor(); // Inputs static void UpdateKeyboardInputs(); @@ -1050,15 +1118,15 @@ static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt); // Misc static void UpdateSettings(); -static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); +static int UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); -static ImGuiWindow* FindBlockingModal(ImGuiWindow* window); // Viewports +const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. static void UpdateViewportsNewFrame(); } @@ -1128,7 +1196,7 @@ ImGuiStyle::ImGuiStyle() FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) - CellPadding = ImVec2(4,2); // Padding within a table cell + CellPadding = ImVec2(4,2); // Padding within a table cell. CellPadding.y may be altered between different rows. TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). @@ -1140,6 +1208,8 @@ ImGuiStyle::ImGuiStyle() TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. + TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. + TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees). ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. @@ -1155,6 +1225,13 @@ ImGuiStyle::ImGuiStyle() CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. + // Behaviors + HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. + HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. + HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. + HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. + // Default theme ImGui::StyleColorsDark(this); } @@ -1163,30 +1240,30 @@ ImGuiStyle::ImGuiStyle() // Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times. void ImGuiStyle::ScaleAllSizes(float scale_factor) { - WindowPadding = ImFloor(WindowPadding * scale_factor); - WindowRounding = ImFloor(WindowRounding * scale_factor); - WindowMinSize = ImFloor(WindowMinSize * scale_factor); - ChildRounding = ImFloor(ChildRounding * scale_factor); - PopupRounding = ImFloor(PopupRounding * scale_factor); - FramePadding = ImFloor(FramePadding * scale_factor); - FrameRounding = ImFloor(FrameRounding * scale_factor); - ItemSpacing = ImFloor(ItemSpacing * scale_factor); - ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); - CellPadding = ImFloor(CellPadding * scale_factor); - TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); - IndentSpacing = ImFloor(IndentSpacing * scale_factor); - ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor); - ScrollbarSize = ImFloor(ScrollbarSize * scale_factor); - ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); - GrabMinSize = ImFloor(GrabMinSize * scale_factor); - GrabRounding = ImFloor(GrabRounding * scale_factor); - LogSliderDeadzone = ImFloor(LogSliderDeadzone * scale_factor); - TabRounding = ImFloor(TabRounding * scale_factor); - TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImFloor(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; - SeparatorTextPadding = ImFloor(SeparatorTextPadding * scale_factor); - DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); - DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); - MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); + WindowPadding = ImTrunc(WindowPadding * scale_factor); + WindowRounding = ImTrunc(WindowRounding * scale_factor); + WindowMinSize = ImTrunc(WindowMinSize * scale_factor); + ChildRounding = ImTrunc(ChildRounding * scale_factor); + PopupRounding = ImTrunc(PopupRounding * scale_factor); + FramePadding = ImTrunc(FramePadding * scale_factor); + FrameRounding = ImTrunc(FrameRounding * scale_factor); + ItemSpacing = ImTrunc(ItemSpacing * scale_factor); + ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor); + CellPadding = ImTrunc(CellPadding * scale_factor); + TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor); + IndentSpacing = ImTrunc(IndentSpacing * scale_factor); + ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor); + ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor); + ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor); + GrabMinSize = ImTrunc(GrabMinSize * scale_factor); + GrabRounding = ImTrunc(GrabRounding * scale_factor); + LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor); + TabRounding = ImTrunc(TabRounding * scale_factor); + TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; + SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); + DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor); + DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor); + MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor); } ImGuiIO::ImGuiIO() @@ -1201,18 +1278,12 @@ ImGuiIO::ImGuiIO() DisplaySize = ImVec2(-1.0f, -1.0f); DeltaTime = 1.0f / 60.0f; IniSavingRate = 5.0f; - IniFilename = "platform\\cfg\\system\\layout.ini"; // Important: "imgui_layout.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). + IniFilename = "platform\\cfg\\system\\layout.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). LogFilename = "platform\\logs\\imgui_log.txt"; - MouseDoubleClickTime = 0.30f; - MouseDoubleClickMaxDist = 6.0f; #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO for (int i = 0; i < ImGuiKey_COUNT; i++) KeyMap[i] = -1; #endif - KeyRepeatDelay = 0.275f; - KeyRepeatRate = 0.050f; - HoverDelayNormal = 0.30f; - HoverDelayShort = 0.10f; UserData = NULL; Fonts = NULL; @@ -1221,6 +1292,12 @@ ImGuiIO::ImGuiIO() FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); + MouseDoubleClickTime = 0.30f; + MouseDoubleClickMaxDist = 6.0f; + MouseDragThreshold = 6.0f; + KeyRepeatDelay = 0.275f; + KeyRepeatRate = 0.050f; + // Miscellaneous options MouseDrawCursor = false; #ifdef __APPLE__ @@ -1242,11 +1319,12 @@ ImGuiIO::ImGuiIO() // Note: Initialize() will setup default clipboard/ime handlers. BackendPlatformName = BackendRendererName = NULL; BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL; + PlatformLocaleDecimalPoint = '.'; // Input (NB: we already have memset zero the entire structure!) MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); - MouseDragThreshold = 6.0f; + MouseSource = ImGuiMouseSource_Mouse; for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } AppAcceptingEvents = true; @@ -1268,6 +1346,7 @@ void ImGuiIO::AddInputCharacter(unsigned int c) ImGuiInputEvent e; e.Type = ImGuiInputEventType_Text; e.Source = ImGuiInputSource_Keyboard; + e.EventId = g.InputEventsNextEventId++; e.Text.Char = c; g.InputEventsQueue.push_back(e); } @@ -1320,13 +1399,15 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) } } -// FIXME: Perhaps we could clear queued events as well? -void ImGuiIO::ClearInputCharacters() +// Clear all incoming events. +void ImGuiIO::ClearEventsQueue() { - InputQueueCharacters.resize(0); + IM_ASSERT(Ctx != NULL); + ImGuiContext& g = *Ctx; + g.InputEventsQueue.clear(); } -// FIXME: Perhaps we could clear queued events as well? +// Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. void ImGuiIO::ClearInputKeys() { #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -1347,11 +1428,21 @@ void ImGuiIO::ClearInputKeys() MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f; } MouseWheel = MouseWheelH = 0.0f; + InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters(). } -static ImGuiInputEvent* FindLatestInputEvent(ImGuiInputEventType type, int arg = -1) +// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue. +// Current frame character buffer is now also cleared by ClearInputKeys(). +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +void ImGuiIO::ClearInputCharacters() { - ImGuiContext& g = *GImGui; + InputQueueCharacters.resize(0); +} +#endif + +static ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1) +{ + ImGuiContext& g = *ctx; for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--) { ImGuiInputEvent* e = &g.InputEventsQueue[n]; @@ -1370,6 +1461,8 @@ static ImGuiInputEvent* FindLatestInputEvent(ImGuiInputEventType type, int arg = // - ImGuiKey key: Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character) // - bool down: Is the key down? use false to signify a key release. // - float analog_value: 0.0f..1.0f +// IMPORTANT: THIS FUNCTION AND OTHER "ADD" GRABS THE CONTEXT FROM OUR INSTANCE. +// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULLFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT. void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) { //if (e->Down) { IMGUI_DEBUG_LOG_IO("AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\n", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); } @@ -1378,7 +1471,7 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) return; ImGuiContext& g = *Ctx; IM_ASSERT(ImGui::IsNamedKeyOrModKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. - IM_ASSERT(!ImGui::IsAliasKey(key)); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events. + IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events. IM_ASSERT(key != ImGuiMod_Shortcut); // We could easily support the translation here but it seems saner to not accept it (TestEngine perform a translation itself) // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. @@ -1393,8 +1486,8 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) BackendUsingLegacyNavInputArray = false; // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed) - const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_Key, (int)key); - const ImGuiKeyData* key_data = ImGui::GetKeyData(key); + const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key); + const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key); const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down; const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue; if (latest_key_down == down && latest_key_analog == analog_value) @@ -1404,6 +1497,7 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) ImGuiInputEvent e; e.Type = ImGuiInputEventType_Key; e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard; + e.EventId = g.InputEventsNextEventId++; e.Key.Key = key; e.Key.Down = down; e.Key.AnalogValue = analog_value; @@ -1457,10 +1551,10 @@ void ImGuiIO::AddMousePosEvent(float x, float y) return; // Apply same flooring as UpdateMouseInputs() - ImVec2 pos((x > -FLT_MAX) ? ImFloorSigned(x) : x, (y > -FLT_MAX) ? ImFloorSigned(y) : y); + ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y); // Filter duplicate - const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_MousePos); + const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos); const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos; if (latest_pos.x == pos.x && latest_pos.y == pos.y) return; @@ -1468,8 +1562,10 @@ void ImGuiIO::AddMousePosEvent(float x, float y) ImGuiInputEvent e; e.Type = ImGuiInputEventType_MousePos; e.Source = ImGuiInputSource_Mouse; + e.EventId = g.InputEventsNextEventId++; e.MousePos.PosX = pos.x; e.MousePos.PosY = pos.y; + e.MousePos.MouseSource = g.InputEventsNextMouseSource; g.InputEventsQueue.push_back(e); } @@ -1482,7 +1578,7 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) return; // Filter duplicate - const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_MouseButton, (int)mouse_button); + const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button); const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button]; if (latest_button_down == down) return; @@ -1490,8 +1586,10 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseButton; e.Source = ImGuiInputSource_Mouse; + e.EventId = g.InputEventsNextEventId++; e.MouseButton.Button = mouse_button; e.MouseButton.Down = down; + e.MouseButton.MouseSource = g.InputEventsNextMouseSource; g.InputEventsQueue.push_back(e); } @@ -1508,24 +1606,36 @@ void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseWheel; e.Source = ImGuiInputSource_Mouse; + e.EventId = g.InputEventsNextEventId++; e.MouseWheel.WheelX = wheel_x; e.MouseWheel.WheelY = wheel_y; + e.MouseWheel.MouseSource = g.InputEventsNextMouseSource; g.InputEventsQueue.push_back(e); } +// This is not a real event, the data is latched in order to be stored in actual Mouse events. +// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes. +void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source) +{ + IM_ASSERT(Ctx != NULL); + ImGuiContext& g = *Ctx; + g.InputEventsNextMouseSource = source; +} + void ImGuiIO::AddFocusEvent(bool focused) { IM_ASSERT(Ctx != NULL); ImGuiContext& g = *Ctx; // Filter duplicate - const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_Focus); + const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus); const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost; - if (latest_focused == focused) + if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused)) return; ImGuiInputEvent e; e.Type = ImGuiInputEventType_Focus; + e.EventId = g.InputEventsNextEventId++; e.AppFocused.Focused = focused; g.InputEventsQueue.push_back(e); } @@ -1782,13 +1892,15 @@ const char* ImStrSkipBlank(const char* str) // and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are // designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.) #ifdef IMGUI_USE_STB_SPRINTF +#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION #define STB_SPRINTF_IMPLEMENTATION +#endif #ifdef IMGUI_STB_SPRINTF_FILENAME #include IMGUI_STB_SPRINTF_FILENAME #else #include "stb_sprintf.h" #endif -#endif +#endif // #ifdef IMGUI_USE_STB_SPRINTF #if defined(_MSC_VER) && !defined(vsnprintf) #define vsnprintf _vsnprintf @@ -1830,21 +1942,9 @@ int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) void ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) { - ImGuiContext& g = *GImGui; va_list args; va_start(args, fmt); - if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0) - { - const char* buf = va_arg(args, const char*); // Skip formatting when using "%s" - *out_buf = buf; - if (out_buf_end) { *out_buf_end = buf + strlen(buf); } - } - else - { - int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args); - *out_buf = g.TempBuffer.Data; - if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; } - } + ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args); va_end(args); } @@ -1854,9 +1954,23 @@ void ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0) { const char* buf = va_arg(args, const char*); // Skip formatting when using "%s" + if (buf == NULL) + buf = "(null)"; *out_buf = buf; if (out_buf_end) { *out_buf_end = buf + strlen(buf); } } + else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0) + { + int buf_len = va_arg(args, int); // Skip formatting when using "%.*s" + const char* buf = va_arg(args, const char*); + if (buf == NULL) + { + buf = "(null)"; + buf_len = ImMin(buf_len, 6); + } + *out_buf = buf; + *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it. + } else { int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args); @@ -1949,11 +2063,18 @@ ImFileHandle ImFileOpen(const char* filename, const char* mode) // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32! const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0); - ImVector buf; - buf.resize(filename_wsize + mode_wsize); - ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize); - ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize); - return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]); + + // Use stack buffer if possible, otherwise heap buffer. Sizes include zero terminator. + // We don't rely on current ImGuiContext as this is implied to be a helper function which doesn't depend on it (see #7314). + wchar_t local_temp_stack[FILENAME_MAX]; + ImVector local_temp_heap; + if (filename_wsize + mode_wsize > IM_ARRAYSIZE(local_temp_stack)) + local_temp_heap.resize(filename_wsize + mode_wsize); + wchar_t* filename_wbuf = local_temp_heap.Data ? local_temp_heap.Data : local_temp_stack; + wchar_t* mode_wbuf = filename_wbuf + filename_wsize; + ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_wbuf, filename_wsize); + ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_wbuf, mode_wsize); + return ::_wfopen(filename_wbuf, mode_wbuf); #else return fopen(filename, mode); #endif @@ -2185,6 +2306,18 @@ int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_e } return bytes_count; } + +const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr) +{ + while (in_text_curr > in_text_start) + { + in_text_curr--; + if ((*in_text_curr & 0xC0) != 0x80) + return in_text_curr; + } + return in_text_start; +} + IM_MSVC_RUNTIME_CHECKS_RESTORE //----------------------------------------------------------------------------- @@ -2380,11 +2513,9 @@ void ImGuiStorage::SetInt(ImGuiID key, int val) { ImGuiStoragePair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) - { Data.insert(it, ImGuiStoragePair(key, val)); - return; - } - it->val_i = val; + else + it->val_i = val; } void ImGuiStorage::SetBool(ImGuiID key, bool val) @@ -2396,22 +2527,18 @@ void ImGuiStorage::SetFloat(ImGuiID key, float val) { ImGuiStoragePair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) - { Data.insert(it, ImGuiStoragePair(key, val)); - return; - } - it->val_f = val; + else + it->val_f = val; } void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) { ImGuiStoragePair* it = LowerBound(Data, key); if (it == Data.end() || it->key != key) - { Data.insert(it, ImGuiStoragePair(key, val)); - return; - } - it->val_p = val; + else + it->val_p = val; } void ImGuiStorage::SetAllInt(int v) @@ -2471,16 +2598,15 @@ void ImGuiTextFilter::Build() input_range.split(',', &Filters); CountGrep = 0; - for (int i = 0; i != Filters.Size; i++) + for (ImGuiTextRange& f : Filters) { - ImGuiTextRange& f = Filters[i]; while (f.b < f.e && ImCharIsBlankA(f.b[0])) f.b++; while (f.e > f.b && ImCharIsBlankA(f.e[-1])) f.e--; if (f.empty()) continue; - if (Filters[i].b[0] != '-') + if (f.b[0] != '-') CountGrep += 1; } } @@ -2493,9 +2619,8 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const if (text == NULL) text = ""; - for (int i = 0; i != Filters.Size; i++) + for (const ImGuiTextRange& f : Filters) { - const ImGuiTextRange& f = Filters[i]; if (f.empty()) continue; if (f.b[0] == '-') @@ -2604,8 +2729,6 @@ void ImGuiTextIndex::append(const char* base, int old_size, int new_size) //----------------------------------------------------------------------------- // [SECTION] ImGuiListClipper -// This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed -// the API mid-way through development and support two ways to using the clipper, needs some rework (see TODO) //----------------------------------------------------------------------------- // FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell. @@ -2616,54 +2739,6 @@ static bool GetSkipItemForListClipping() return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -// Legacy helper to calculate coarse clipping of large list of evenly sized items. -// This legacy API is not ideal because it assumes we will return a single contiguous rectangle. -// Prefer using ImGuiListClipper which can returns non-contiguous ranges. -void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.LogEnabled) - { - // If logging is active, do not perform any clipping - *out_items_display_start = 0; - *out_items_display_end = items_count; - return; - } - if (GetSkipItemForListClipping()) - { - *out_items_display_start = *out_items_display_end = 0; - return; - } - - // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect - // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly. - ImRect rect = window->ClipRect; - if (g.NavMoveScoringItems) - rect.Add(g.NavScoringNoClipRect); - if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) - rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel - - const ImVec2 pos = window->DC.CursorPos; - int start = (int)((rect.Min.y - pos.y) / items_height); - int end = (int)((rect.Max.y - pos.y) / items_height); - - // When performing a navigation request, ensure we have one item extra in the direction we are moving to - // FIXME: Verify this works with tabbing - const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); - if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) - start--; - if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) - end++; - - start = ImClamp(start, 0, items_count); - end = ImClamp(end + 1, start, items_count); - *out_items_display_start = start; - *out_items_display_end = end; -} -#endif - static void ImGuiListClipper_SortAndFuseRanges(ImVector& ranges, int offset = 0) { if (ranges.Size - offset <= 1) @@ -2725,9 +2800,6 @@ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int it ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); - Ctx = ImGui::GetCurrentContext(); - IM_ASSERT(Ctx != NULL); - ItemsCount = -1; } ImGuiListClipper::~ImGuiListClipper() @@ -2737,6 +2809,9 @@ ImGuiListClipper::~ImGuiListClipper() void ImGuiListClipper::Begin(int items_count, float items_height) { + if (Ctx == NULL) + Ctx = ImGui::GetCurrentContext(); + ImGuiContext& g = *Ctx; ImGuiWindow* window = g.CurrentWindow; IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name); @@ -2762,10 +2837,10 @@ void ImGuiListClipper::Begin(int items_count, float items_height) void ImGuiListClipper::End() { - ImGuiContext& g = *Ctx; if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) { // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. + ImGuiContext& g = *Ctx; IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name); if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) ImGuiListClipper_SeekCursorForItem(this, ItemsCount); @@ -2783,13 +2858,13 @@ void ImGuiListClipper::End() ItemsCount = -1; } -void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) +void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end) { ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet. - IM_ASSERT(item_min <= item_max); - if (item_min < item_max) - data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); + IM_ASSERT(item_begin <= item_end); + if (item_begin < item_end) + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end)); } static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) @@ -2866,7 +2941,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); if (is_nav_request) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); - if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) + if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); // Add focused/active item @@ -2884,26 +2959,28 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping. // - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list, // which with the flooring/ceiling tend to lead to 2 items instead of one being submitted. - for (int i = 0; i < data->Ranges.Size; i++) - if (data->Ranges[i].PosToIndexConvert) + for (ImGuiListClipperRange& range : data->Ranges) + if (range.PosToIndexConvert) { - int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight); - int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f); - data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1); - data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, clipper->ItemsCount); - data->Ranges[i].PosToIndexConvert = false; + int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight); + int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f); + range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1); + range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount); + range.PosToIndexConvert = false; } ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); } // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. - if (data->StepNo < data->Ranges.Size) + while (data->StepNo < data->Ranges.Size) { clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount); if (clipper->DisplayStart > already_submitted) //-V1051 ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart); data->StepNo++; + if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size) + continue; return true; } @@ -2970,13 +3047,14 @@ const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) return style.Colors[idx]; } -ImU32 ImGui::GetColorU32(ImU32 col) +ImU32 ImGui::GetColorU32(ImU32 col, float alpha_mul) { ImGuiStyle& style = GImGui->Style; - if (style.Alpha >= 1.0f) + alpha_mul *= style.Alpha; + if (alpha_mul >= 1.0f) return col; ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; - a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. + a = (ImU32)(a * alpha_mul); // We don't need to clamp 0..255 because alpha is in 0..1 range. return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); } @@ -2988,7 +3066,8 @@ void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; g.ColorStack.push_back(backup); - g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); + if (g.DebugFlashStyleColorIdx != idx) + g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); } void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) @@ -2998,7 +3077,8 @@ void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) backup.Col = idx; backup.BackupValue = g.Style.Colors[idx]; g.ColorStack.push_back(backup); - g.Style.Colors[idx] = col; + if (g.DebugFlashStyleColorIdx != idx) + g.Style.Colors[idx] = col; } void ImGui::PopStyleColor(int count) @@ -3006,7 +3086,7 @@ void ImGui::PopStyleColor(int count) ImGuiContext& g = *GImGui; if (g.ColorStack.Size < count) { - IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times: stack underflow."); + IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!"); count = g.ColorStack.Size; } while (count > 0) @@ -3020,34 +3100,35 @@ void ImGui::PopStyleColor(int count) static const ImGuiDataVarInfo GStyleVarInfo[] = { - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextBorderSize) },// ImGuiStyleVar_SeparatorTextBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) }, // ImGuiStyleVar_DisabledAlpha + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) }, // ImGuiStyleVar_CellPadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) }, // ImGuiStyleVar_TabRounding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) }, // ImGuiStyleVar_TabBarBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) }, // ImGuiStyleVar_SelectableTextAlign + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},// ImGuiStyleVar_SeparatorTextBorderSize + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign + { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding }; const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx) @@ -3068,7 +3149,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) *pvar = val; return; } - IM_ASSERT_USER_ERROR(0, "Called PushStyleVar() variant with wrong type!"); + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); } void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) @@ -3082,7 +3163,7 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) *pvar = val; return; } - IM_ASSERT_USER_ERROR(0, "Called PushStyleVar() variant with wrong type!"); + IM_ASSERT_USER_ERROR(0, "Calling PushStyleVar() variant with wrong type!"); } void ImGui::PopStyleVar(int count) @@ -3090,7 +3171,7 @@ void ImGui::PopStyleVar(int count) ImGuiContext& g = *GImGui; if (g.StyleVarStack.Size < count) { - IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times: stack underflow."); + IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!"); count = g.StyleVarStack.Size; } while (count > 0) @@ -3234,6 +3315,9 @@ void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end // Default clip_rect uses (pos_min,pos_max) // Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) +// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList. +// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take +// better advantage of the render function taking size into account for coarse clipping. void ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) { // Perform CPU side clipping for single clipped element to avoid using scissor state @@ -3322,7 +3406,7 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con // Render text, render ellipsis RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); - ImVec2 ellipsis_pos = ImFloor(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y)); + ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y)); if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x) for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale) font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar); @@ -3376,22 +3460,22 @@ void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFl float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; ImRect display_rect = bb; display_rect.ClipWith(window->ClipRect); - if (flags & ImGuiNavHighlightFlags_TypeDefault) + const float thickness = 2.0f; + if (flags & ImGuiNavHighlightFlags_Compact) { - const float THICKNESS = 2.0f; - const float DISTANCE = 3.0f + THICKNESS * 0.5f; - display_rect.Expand(ImVec2(DISTANCE, DISTANCE)); + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness); + } + else + { + const float distance = 3.0f + thickness * 0.5f; + display_rect.Expand(ImVec2(distance, distance)); bool fully_visible = window->ClipRect.Contains(display_rect); if (!fully_visible) window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, 0, THICKNESS); + window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, thickness); if (!fully_visible) window->DrawList->PopClipRect(); } - if (flags & ImGuiNavHighlightFlags_TypeThin) - { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f); - } } void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow) @@ -3399,13 +3483,12 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso ImGuiContext& g = *GImGui; IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT); ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas; - for (int n = 0; n < g.Viewports.Size; n++) + for (ImGuiViewportP* viewport : g.Viewports) { // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor. ImVec2 offset, size, uv[4]; if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) continue; - ImGuiViewportP* viewport = g.Viewports[n]; const ImVec2 pos = base_pos - offset; const float scale = base_scale; if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale))) @@ -3481,6 +3564,7 @@ void ImGui::DestroyContext(ImGuiContext* ctx) // IMPORTANT: ###xxx suffixes must be same in ALL languages static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { + { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" }, { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, @@ -3520,9 +3604,18 @@ void ImGui::Initialize() // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); + viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; g.Viewports.push_back(viewport); g.TempBuffer.resize(1024 * 3 + 1, 0); + // Build KeysMayBeCharInput[] lookup table (1 bool per named key) + for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) + if ((key >= ImGuiKey_0 && key <= ImGuiKey_9) || (key >= ImGuiKey_A && key <= ImGuiKey_Z) || (key >= ImGuiKey_Keypad0 && key <= ImGuiKey_Keypad9) + || key == ImGuiKey_Tab || key == ImGuiKey_Space || key == ImGuiKey_Apostrophe || key == ImGuiKey_Comma || key == ImGuiKey_Minus || key == ImGuiKey_Period + || key == ImGuiKey_Slash || key == ImGuiKey_Semicolon || key == ImGuiKey_Equal || key == ImGuiKey_LeftBracket || key == ImGuiKey_RightBracket || key == ImGuiKey_GraveAccent + || key == ImGuiKey_KeypadDecimal || key == ImGuiKey_KeypadDivide || key == ImGuiKey_KeypadMultiply || key == ImGuiKey_KeypadSubtract || key == ImGuiKey_KeypadAdd || key == ImGuiKey_KeypadEqual) + g.KeysMayBeCharInput.SetBit(key); + #ifdef IMGUI_HAS_DOCK #endif @@ -3532,8 +3625,11 @@ void ImGui::Initialize() // This function is merely here to free heap allocations. void ImGui::Shutdown() { - // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) ImGuiContext& g = *GImGui; + IM_ASSERT_USER_ERROR(g.IO.BackendPlatformUserData == NULL, "Forgot to shutdown Platform backend?"); + IM_ASSERT_USER_ERROR(g.IO.BackendRendererUserData == NULL, "Forgot to shutdown Renderer backend?"); + + // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) if (g.IO.Fonts && g.FontAtlasOwnedByContext) { g.IO.Fonts->Locked = false; @@ -3571,6 +3667,7 @@ void ImGui::Shutdown() g.FontStack.clear(); g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); + g.NavTreeNodeStack.clear(); g.Viewports.clear_delete(); @@ -3587,6 +3684,7 @@ void ImGui::Shutdown() g.ClipboardHandlerData.clear(); g.MenusIdSubmittedThisFrame.clear(); g.InputTextState.ClearFreeMemory(); + g.InputTextDeactivatedState.ClearFreeMemory(); g.SettingsWindows.clear(); g.SettingsHandlers.clear(); @@ -3621,9 +3719,9 @@ void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) { ImGuiContext& g = *ctx; IM_ASSERT(hook_id != 0); - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].HookId == hook_id) - g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; + for (ImGuiContextHook& hook : g.Hooks) + if (hook.HookId == hook_id) + hook.Type = ImGuiContextHookType_PendingRemoval_; } // Call context hooks (used by e.g. test engine) @@ -3631,9 +3729,9 @@ void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) { ImGuiContext& g = *ctx; - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].Type == hook_type) - g.Hooks[n].Callback(&g, &g.Hooks[n]); + for (ImGuiContextHook& hook : g.Hooks) + if (hook.Type == hook_type) + hook.Callback(&g, &hook); } @@ -3664,6 +3762,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NUL DrawList = &DrawListInst; DrawList->_Data = &Ctx->DrawListSharedData; DrawList->_OwnerName = Name; + NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX); } ImGuiWindow::~ImGuiWindow() @@ -3718,7 +3817,10 @@ static void SetCurrentWindow(ImGuiWindow* window) g.CurrentWindow = window; g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL; if (window) + { g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); + ImGui::NavUpdateCurrentWindowIsScrollPushableX(); + } } void ImGui::GcCompactTransientMiscBuffers() @@ -3759,13 +3861,23 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) { ImGuiContext& g = *GImGui; - // While most behaved code would make an effort to not steal active id during window move/drag operations, - // we at least need to be resilient to it. Cancelling the move is rather aggressive and users of 'master' branch - // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. - if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) + // Clear previous active id + if (g.ActiveId != 0) { - IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); - g.MovingWindow = NULL; + // While most behaved code would make an effort to not steal active id during window move/drag operations, + // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch + // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that. + if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId) + { + IMGUI_DEBUG_LOG_ACTIVEID("SetActiveID() cancel MovingWindow\n"); + g.MovingWindow = NULL; + } + + // This could be written in a more general way (e.g associate a hook to ActiveId), + // but since this is currently quite an exception we'll leave it as is. + // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId() + if (g.InputTextState.ID == g.ActiveId) + InputTextDeactivateHook(g.ActiveId); } // Set active id @@ -3788,10 +3900,12 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) g.ActiveIdNoClearOnFocusLoss = false; g.ActiveIdWindow = window; g.ActiveIdHasBeenEditedThisFrame = false; + g.ActiveIdFromShortcut = false; if (id) { g.ActiveIdIsAlive = id; - g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? (ImGuiInputSource)ImGuiInputSource_Nav : ImGuiInputSource_Mouse; + g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse; + IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None); } // Clear declaration of inputs claimed by the widget @@ -3823,31 +3937,28 @@ ImGuiID ImGui::GetHoveredID() return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; } -// This is called by ItemAdd(). -// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID(). -void ImGui::KeepAliveID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId == id) - g.ActiveIdIsAlive = id; - if (g.ActiveIdPreviousFrame == id) - g.ActiveIdPreviousFrameIsAlive = true; -} - void ImGui::MarkItemEdited(ImGuiID id) { // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data. ImGuiContext& g = *GImGui; - IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); - IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. + if (g.LockMarkEdited > 0) + return; + if (g.ActiveId == id || g.ActiveId == 0) + { + g.ActiveIdHasBeenEditedThisFrame = true; + g.ActiveIdHasBeenEditedBefore = true; + } + + // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343) + // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714) + IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id); + //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id); - g.ActiveIdHasBeenEditedThisFrame = true; - g.ActiveIdHasBeenEditedBefore = true; g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited; } -static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) +bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) { // An active popup disable hovering on other windows (apart from its own children) // FIXME-OPT: This could be cached/stored within the window. @@ -3866,12 +3977,30 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla // Inhibit hover unless the window is within the stack of our modal/popup if (want_inhibit) - if (!ImGui::IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window)) + if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window)) return false; } return true; } +static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags) +{ + ImGuiContext& g = *GImGui; + if (flags & ImGuiHoveredFlags_DelayNormal) + return g.Style.HoverDelayNormal; + if (flags & ImGuiHoveredFlags_DelayShort) + return g.Style.HoverDelayShort; + return 0.0f; +} + +static ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags) +{ + // Allow instance flags to override shared flags + if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal)) + shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal); + return user_flags | shared_flags; +} + // This is roughly matching the behavior of internal-facing ItemHoverable() // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered() // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId @@ -3879,12 +4008,17 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!"); + if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride)) { if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; if (!IsItemFocused()) return false; + + if (flags & ImGuiHoveredFlags_ForTooltip) + flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav); } else { @@ -3892,6 +4026,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; + + if (flags & ImGuiHoveredFlags_ForTooltip) + flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse); + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function // Done with rectangle culling so we can perform heavier checks now @@ -3901,12 +4039,13 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was // the test that has been running for a long while. if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0) - if ((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0) return false; // Test if another item is active (e.g. being dragged) + const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) - if (g.ActiveId != 0 && g.ActiveId != g.LastItemData.ID && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) + if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) return false; // Test if interactions on this window are blocked by an active popup or modal. @@ -3920,48 +4059,61 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Special handling for calling after Begin() which represent the title bar or tab. // When the window is skipped/collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. - if (g.LastItemData.ID == window->MoveId && window->WriteAccessed) + if (id == window->MoveId && window->WriteAccessed) return false; + + // Test if using AllowOverlap and overlapped + if ((g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap) && id != 0) + if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0) + if (g.HoveredIdPreviousFrame != g.LastItemData.ID) + return false; } // Handle hover delay // (some ideas: https://www.nngroup.com/articles/timing-exposing-content) - float delay; - if (flags & ImGuiHoveredFlags_DelayNormal) - delay = g.IO.HoverDelayNormal; - else if (flags & ImGuiHoveredFlags_DelayShort) - delay = g.IO.HoverDelayShort; - else - delay = 0.0f; - if (delay > 0.0f) + const float delay = CalcDelayFromHoveredFlags(flags); + if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary)) { ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect); - if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverDelayIdPreviousFrame != hover_delay_id)) - g.HoverDelayTimer = 0.0f; - g.HoverDelayId = hover_delay_id; - return g.HoverDelayTimer >= delay; + if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id)) + g.HoverItemDelayTimer = 0.0f; + g.HoverItemDelayId = hover_delay_id; + + // When changing hovered item we requires a bit of stationary delay before activating hover timer, + // but once unlocked on a given item we also moving. + //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); } + if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id) + return false; + + if (g.HoverItemDelayTimer < delay) + return false; } return true; } // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). -bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) +// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call) +// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28. +// If you used this in your legacy/custom widgets code: +// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.InFlags'. +// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable. +bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags) { ImGuiContext& g = *GImGui; - if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) - return false; - ImGuiWindow* window = g.CurrentWindow; if (g.HoveredWindow != window) return false; - if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) - return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) return false; + if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) + return false; + if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) + if (!g.ActiveIdFromShortcut) + return false; + // Done with rectangle culling so we can perform heavier checks now. - ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdDisabled = true; @@ -3971,29 +4123,46 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level // hover test in widgets code. We could also decide to split this function is two. if (id != 0) + { + // Drag source doesn't report as hovered + if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) + return false; + SetHoveredID(id); + // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. + // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test. + if (item_flags & ImGuiItemFlags_AllowOverlap) + { + g.HoveredIdAllowOverlap = true; + if (g.HoveredIdPreviousFrame != id) + return false; + } + } + // When disabled we'll return false but still set HoveredId if (item_flags & ImGuiItemFlags_Disabled) { // Release active id if turning disabled - if (g.ActiveId == id) + if (g.ActiveId == id && id != 0) ClearActiveID(); g.HoveredIdDisabled = true; return false; } +#ifndef IMGUI_DISABLE_DEBUG_TOOLS if (id != 0) { // [DEBUG] Item Picker tool! - // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making - // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered - // items if we performed the test in ItemAdd(), but that would incur a small runtime cost. + // We perform the check here because reaching is path is rare (1~ time a frame), + // making the cost of this tool near-zero! We could get better call-stack and support picking non-hovered + // items if we performed the test in ItemAdd(), but that would incur a bigger runtime cost. if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); if (g.DebugItemPickerBreakId == id) IM_DEBUG_BREAK(); } +#endif if (g.NavDisableMouseHover) return false; @@ -4002,12 +4171,13 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) } // FIXME: This is inlined/duplicated in ItemAdd() +// FIXME: The id != 0 path is not used by our codebase, may get rid of it? bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (!bb.Overlaps(window->ClipRect)) - if (id == 0 || (id != g.ActiveId && id != g.NavId)) + if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId)) if (!g.LogEnabled) return true; return false; @@ -4051,20 +4221,51 @@ float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) // IM_ALLOC() == ImGui::MemAlloc() void* ImGui::MemAlloc(size_t size) { + void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS if (ImGuiContext* ctx = GImGui) - ctx->IO.MetricsActiveAllocations++; - return (*GImAllocatorAllocFunc)(size, GImAllocatorUserData); + DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size); +#endif + return ptr; } // IM_FREE() == ImGui::MemFree() void ImGui::MemFree(void* ptr) { - if (ptr) +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (ptr != NULL) if (ImGuiContext* ctx = GImGui) - ctx->IO.MetricsActiveAllocations--; + DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1); +#endif return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData); } +// We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of "no allocations on idle/repeating frames" +void ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size) +{ + ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx]; + IM_UNUSED(ptr); + if (entry->FrameCount != frame_count) + { + info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf); + entry = &info->LastEntriesBuf[info->LastEntriesIdx]; + entry->FrameCount = frame_count; + entry->AllocCount = entry->FreeCount = 0; + } + if (size != (size_t)-1) + { + entry->AllocCount++; + info->TotalAllocCount++; + //printf("[%05d] MemAlloc(%d) -> 0x%p\n", frame_count, size, ptr); + } + else + { + entry->FreeCount++; + info->TotalFreeCount++; + //printf("[%05d] MemFree(0x%p)\n", frame_count, ptr); + } +} + const char* ImGui::GetClipboardText() { ImGuiContext& g = *GImGui; @@ -4107,33 +4308,33 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name) +static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name) { // Create the draw list on demand, because they are not frequently used for all viewports ImGuiContext& g = *GImGui; - IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists)); - ImDrawList* draw_list = viewport->DrawLists[drawlist_no]; + IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists)); + ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no]; if (draw_list == NULL) { draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData); draw_list->_OwnerName = drawlist_name; - viewport->DrawLists[drawlist_no] = draw_list; + viewport->BgFgDrawLists[drawlist_no] = draw_list; } // Our ImDrawList system requires that there is always a command - if (viewport->DrawListsLastFrame[drawlist_no] != g.FrameCount) + if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount) { draw_list->_ResetForNewFrame(); draw_list->PushTextureID(g.IO.Fonts->TexID); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - viewport->DrawListsLastFrame[drawlist_no] = g.FrameCount; + viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount; } return draw_list; } ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) { - return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background"); + return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background"); } ImDrawList* ImGui::GetBackgroundDrawList() @@ -4144,7 +4345,7 @@ ImDrawList* ImGui::GetBackgroundDrawList() ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) { - return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); + return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); } ImDrawList* ImGui::GetForegroundDrawList() @@ -4251,10 +4452,10 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (g.HoveredIdDisabled) g.MovingWindow = NULL; } - else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL) + else if (root_window == NULL && g.NavWindow != NULL) { // Clicking on void disable focus - FocusWindow(NULL); + FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal); } } @@ -4342,12 +4543,11 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() } // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app) - if (g.WantCaptureKeyboardNextFrame != -1) - io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); - else - io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); + io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) io.WantCaptureKeyboard = true; + if (g.WantCaptureKeyboardNextFrame != -1) // Manual override + io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; @@ -4398,8 +4598,8 @@ void ImGui::NewFrame() SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int n = 0; n < g.Viewports.Size; n++) - virtual_space.Add(g.Viewports[n]->GetMainRect()); + for (ImGuiViewportP* viewport : g.Viewports) + virtual_space.Add(viewport->GetMainRect()); g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); @@ -4414,11 +4614,8 @@ void ImGui::NewFrame() g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; // Mark rendering data as invalid to prevent user who may have a handle on it to use it. - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataP.Clear(); - } + for (ImGuiViewportP* viewport : g.Viewports) + viewport->DrawDataP.Valid = false; // Drag and drop keep the source ID alive so even if the source disappear our state is consistent if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) @@ -4483,21 +4680,33 @@ void ImGui::NewFrame() } #endif + // Record when we have been stationary as this state is preserved while over same item. + // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values. + // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function. + if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) + g.HoverItemUnlockedStationaryId = g.HoverItemDelayId; + else if (g.HoverItemDelayId == 0) + g.HoverItemUnlockedStationaryId = 0; + if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) + g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID; + else if (g.HoveredWindow == NULL) + g.HoverWindowUnlockedStationaryId = 0; + // Update hover delay for IsItemHovered() with delays and tooltips - g.HoverDelayIdPreviousFrame = g.HoverDelayId; - if (g.HoverDelayId != 0) + g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId; + if (g.HoverItemDelayId != 0) { - //if (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f) // Need design/flags - g.HoverDelayTimer += g.IO.DeltaTime; - g.HoverDelayClearTimer = 0.0f; - g.HoverDelayId = 0; + g.HoverItemDelayTimer += g.IO.DeltaTime; + g.HoverItemDelayClearTimer = 0.0f; + g.HoverItemDelayId = 0; } - else if (g.HoverDelayTimer > 0.0f) + else if (g.HoverItemDelayTimer > 0.0f) { // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps - g.HoverDelayClearTimer += g.IO.DeltaTime; - if (g.HoverDelayClearTimer >= ImMax(0.20f, g.IO.DeltaTime * 2.0f)) // ~6 frames at 30 Hz + allow for low framerate - g.HoverDelayTimer = g.HoverDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. + // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle. + g.HoverItemDelayClearTimer += g.IO.DeltaTime; + if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate + g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. } // Drag and drop @@ -4552,9 +4761,8 @@ void ImGui::NewFrame() // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; - for (int i = 0; i != g.Windows.Size; i++) + for (ImGuiWindow* window : g.Windows) { - ImGuiWindow* window = g.Windows[i]; window->WasActive = window->Active; window->Active = false; window->WriteAccessed = false; @@ -4570,16 +4778,16 @@ void ImGui::NewFrame() for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) TableGcCompactTransientBuffers(g.Tables.GetByIndex(i)); - for (int i = 0; i < g.TablesTempData.Size; i++) - if (g.TablesTempData[i].LastTimeActive >= 0.0f && g.TablesTempData[i].LastTimeActive < memory_compact_start_time) - TableGcCompactTransientBuffers(&g.TablesTempData[i]); + for (ImGuiTableTempData& table_temp_data : g.TablesTempData) + if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time) + TableGcCompactTransientBuffers(&table_temp_data); if (g.GcCompactAll) GcCompactTransientMiscBuffers(); g.GcCompactAll = false; // Closing the focused window restore focus to the first active root window in descending z-order if (g.NavWindow && !g.NavWindow->WasActive) - FocusTopMostWindowUnderOne(NULL, NULL); + FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // No window should be open at the beginning of the frame. // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. @@ -4590,15 +4798,22 @@ void ImGui::NewFrame() g.GroupStack.resize(0); // [DEBUG] Update debug features +#ifndef IMGUI_DISABLE_DEBUG_TOOLS UpdateDebugToolItemPicker(); UpdateDebugToolStackQueries(); + UpdateDebugToolFlashStyleColor(); if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0) - g.DebugLocateId = 0; - if (g.DebugLogClipperAutoDisableFrames > 0 && --g.DebugLogClipperAutoDisableFrames == 0) { - DebugLog("(Auto-disabled ImGuiDebugLogFlags_EventClipper to avoid spamming)\n"); - g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper; + g.DebugLocateId = 0; + g.DebugBreakInLocateId = false; } + if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0) + { + DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n"); + g.DebugLogFlags &= ~g.DebugLogAutoDisableFlags; + g.DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; + } +#endif // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. @@ -4610,10 +4825,12 @@ void ImGui::NewFrame() // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack, // allowing to validate correct Begin/End behavior in user code. +#ifndef IMGUI_DISABLE_DEBUG_TOOLS if (g.IO.ConfigDebugBeginReturnValueLoop) g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10); else g.DebugBeginReturnValueCullDepth = -1; +#endif CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } @@ -4646,53 +4863,17 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im } } -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) -{ - if (draw_list->CmdBuffer.Size == 0) - return; - if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) - return; - - // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. - // May trigger for you if you are using PrimXXX functions incorrectly. - IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); - IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); - if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) - IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); - - // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) - // If this assert triggers because you are drawing lots of stuff manually: - // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. - // Be mindful that the ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. - // - If you want large meshes with more than 64K vertices, you can either: - // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. - // Most example backends already support this from 1.71. Pre-1.71 backends won't. - // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. - // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. - // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: - // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - // Your own engine or render API may use different parameters or function calls to specify index sizes. - // 2 and 4 bytes indices are generally supported by most graphics API. - // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching - // the 64K limit to split your draw commands in multiple draw lists. - if (sizeof(ImDrawIdx) == 2) - IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); - - out_list->push_back(draw_list); -} - static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = g.Viewports[0]; g.IO.MetricsRenderWindows++; - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[layer], window->DrawList); - for (int i = 0; i < window->DC.ChildWindows.Size; i++) - { - ImGuiWindow* child = window->DC.ChildWindows[i]; + if (window->DrawList->_Splitter._Count > 1) + window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows. + ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList); + for (ImGuiWindow* child : window->DC.ChildWindows) if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active AddWindowToDrawData(child, layer); - } } static inline int GetWindowDisplayLayer(ImGuiWindow* window) @@ -4706,42 +4887,41 @@ static inline void AddRootWindowToDrawData(ImGuiWindow* window) AddWindowToDrawData(window, GetWindowDisplayLayer(window)); } -void ImDrawDataBuilder::FlattenIntoSingleLayer() +static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) { - int n = Layers[0].Size; - int size = n; - for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) - size += Layers[i].Size; - Layers[0].resize(size); - for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) + int n = builder->Layers[0]->Size; + int full_size = n; + for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++) + full_size += builder->Layers[i]->Size; + builder->Layers[0]->resize(full_size); + for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++) { - ImVector& layer = Layers[layer_n]; - if (layer.empty()) + ImVector* layer = builder->Layers[layer_n]; + if (layer->empty()) continue; - memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); - n += layer.Size; - layer.resize(0); + memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*)); + n += layer->Size; + layer->resize(0); } } -static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) +static void InitViewportDrawData(ImGuiViewportP* viewport) { ImGuiIO& io = ImGui::GetIO(); ImDrawData* draw_data = &viewport->DrawDataP; + + viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists; + viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1; + viewport->DrawDataBuilder.Layers[0]->resize(0); + viewport->DrawDataBuilder.Layers[1]->resize(0); + draw_data->Valid = true; - draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - draw_data->CmdListsCount = draw_lists->Size; + draw_data->CmdListsCount = 0; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = viewport->Size; draw_data->FramebufferScale = io.DisplayFramebufferScale; - for (int n = 0; n < draw_lists->Size; n++) - { - ImDrawList* draw_list = draw_lists->Data[n]; - draw_list->_PopUnusedDrawCmd(); - draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; - draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; - } + draw_data->OwnerViewport = viewport; } // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. @@ -4780,14 +4960,14 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 ImDrawList* draw_list = window->RootWindow->DrawList; if (draw_list->CmdBuffer.Size == 0) draw_list->AddDrawCmd(); - draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // Ensure ImDrawCmd are not merged + draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that) draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col); ImDrawCmd cmd = draw_list->CmdBuffer.back(); IM_ASSERT(cmd.ElemCount == 6); draw_list->CmdBuffer.pop_back(); draw_list->CmdBuffer.push_front(cmd); - draw_list->PopClipRect(); draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command. + draw_list->PopClipRect(); } } @@ -4823,7 +5003,7 @@ static void ImGui::RenderDimmedBackgrounds() { // Draw dimming behind modal or a begin stack child, whichever comes first in draw order. ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window); - RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio)); + RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio)); } else if (dim_bg_for_window_list) { @@ -4865,20 +5045,9 @@ void ImGui::EndFrame() ImGuiPlatformImeData* ime_data = &g.PlatformImeData; if (g.IO.SetPlatformImeDataFn && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) { - IMGUI_DEBUG_LOG_IO("Calling io.SetPlatformImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); + IMGUI_DEBUG_LOG_IO("[io] Calling io.SetPlatformImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); ImGuiViewport* viewport = GetMainViewport(); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (viewport->PlatformHandleRaw == NULL && g.IO.ImeWindowHandle != NULL) - { - viewport->PlatformHandleRaw = g.IO.ImeWindowHandle; - g.IO.SetPlatformImeDataFn(viewport, ime_data); - viewport->PlatformHandleRaw = NULL; - } - else -#endif - { - g.IO.SetPlatformImeDataFn(viewport, ime_data); - } + g.IO.SetPlatformImeDataFn(viewport, ime_data); } // Hide implicit/fallback "Debug" window if it hasn't been used @@ -4918,9 +5087,8 @@ void ImGui::EndFrame() // We cannot do that on FocusWindow() because children may not exist yet g.WindowsTempSortBuffer.resize(0); g.WindowsTempSortBuffer.reserve(g.Windows.Size); - for (int i = 0; i != g.Windows.Size; i++) + for (ImGuiWindow* window : g.Windows) { - ImGuiWindow* window = g.Windows[i]; if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it continue; AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window); @@ -4935,6 +5103,7 @@ void ImGui::EndFrame() g.IO.Fonts->Locked = false; // Clear Input data for next frame + g.IO.MousePosPrev = g.IO.MousePos; g.IO.AppFocusLost = false; g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; g.IO.InputQueueCharacters.resize(0); @@ -4952,32 +5121,30 @@ void ImGui::Render() if (g.FrameCountEnded != g.FrameCount) EndFrame(); - const bool first_render_of_frame = (g.FrameCountRendered != g.FrameCount); + if (g.FrameCountRendered == g.FrameCount) + return; g.FrameCountRendered = g.FrameCount; - g.IO.MetricsRenderWindows = 0; + g.IO.MetricsRenderWindows = 0; CallContextHooks(&g, ImGuiContextHookType_RenderPre); - // Add background ImDrawList (for each active viewport) - for (int n = 0; n != g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.Clear(); - if (viewport->DrawLists[0] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); - } - // Draw modal/window whitening backgrounds - if (first_render_of_frame) - RenderDimmedBackgrounds(); + RenderDimmedBackgrounds(); + + // Add background ImDrawList (for each active viewport) + for (ImGuiViewportP* viewport : g.Viewports) + { + InitViewportDrawData(viewport); + if (viewport->BgFgDrawLists[0] != NULL) + AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); + } // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL); - for (int n = 0; n != g.Windows.Size; n++) + for (ImGuiWindow* window : g.Windows) { - ImGuiWindow* window = g.Windows[n]; IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive "warning C6011: Dereferencing NULL pointer 'window'" if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1]) AddRootWindowToDrawData(window); @@ -4987,22 +5154,25 @@ void ImGui::Render() AddRootWindowToDrawData(windows_to_render_top_most[n]); // Draw software mouse cursor if requested by io.MouseDrawCursor flag - if (g.IO.MouseDrawCursor && first_render_of_frame && g.MouseCursor != ImGuiMouseCursor_None) + if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); // Setup ImDrawData structures for end-user g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; - for (int n = 0; n < g.Viewports.Size; n++) + for (ImGuiViewportP* viewport : g.Viewports) { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.FlattenIntoSingleLayer(); + FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder); // Add foreground ImDrawList (for each active viewport) - if (viewport->DrawLists[1] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); + if (viewport->BgFgDrawLists[1] != NULL) + AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); - SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); + // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). ImDrawData* draw_data = &viewport->DrawDataP; + IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount); + for (ImDrawList* draw_list : draw_data->CmdLists) + draw_list->_PopUnusedDrawCmd(); + g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; } @@ -5033,7 +5203,7 @@ ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_tex // FIXME: Investigate using ceilf or e.g. // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html - text_size.x = IM_FLOOR(text_size.x + 0.99999f); + text_size.x = IM_TRUNC(text_size.x + 0.99999f); return text_size; } @@ -5063,12 +5233,8 @@ static void FindHoveredWindow() continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) - ImRect bb(window->OuterRectClipped); - if (window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) - bb.Expand(padding_regular); - else - bb.Expand(padding_for_resize); - if (!bb.Contains(g.IO.MousePos)) + ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize; + if (!window->OuterRectClipped.ContainsWithPad(g.IO.MousePos, hit_padding)) continue; // Support for one rectangular hole in any given window @@ -5153,6 +5319,9 @@ bool ImGui::IsItemToggledSelection() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false; } +// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, +// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! +// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. bool ImGui::IsAnyItemHovered() { ImGuiContext& g = *GImGui; @@ -5183,17 +5352,28 @@ bool ImGui::IsItemEdited() return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0; } +// Allow next item to be overlapped by subsequent items. +// This works by requiring HoveredId to match for two subsequent frames, +// so if a following items overwrite it our interactions will naturally be disabled. +void ImGui::SetNextItemAllowOverlap() +{ + ImGuiContext& g = *GImGui; + g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap; +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. -// FIXME: Although this is exposed, its interaction and ideal idiom with using ImGuiButtonFlags_AllowItemOverlap flag are extremely confusing, need rework. +// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead. void ImGui::SetItemAllowOverlap() { ImGuiContext& g = *GImGui; ImGuiID id = g.LastItemData.ID; if (g.HoveredId == id) g.HoveredIdAllowOverlap = true; - if (g.ActiveId == id) + if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id. g.ActiveIdAllowOverlap = true; } +#endif // FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function. void ImGui::SetActiveIdUsingAllKeyboardKeys() @@ -5229,40 +5409,105 @@ ImVec2 ImGui::GetItemRectSize() return g.LastItemData.Rect.GetSize(); } -bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) +// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'. +// ImGuiChildFlags_Border is defined as always == 1 in order to allow old code passing 'true'. Read comments in imgui.h for details! +bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) +{ + ImGuiID id = GetCurrentWindow()->GetID(str_id); + return BeginChildEx(str_id, id, size_arg, child_flags, window_flags); +} + +bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) +{ + return BeginChildEx(NULL, id, size_arg, child_flags, window_flags); +} + +bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; + IM_ASSERT(id != 0); - flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow; - flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag + // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument. + const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle; + IM_UNUSED(ImGuiChildFlags_SupportedMask_); + IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && "Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?"); + IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && "Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!"); + if (child_flags & ImGuiChildFlags_AlwaysAutoResize) + { + IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && "Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!"); + IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && "Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!"); + } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding) + child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding; +#endif + if (child_flags & ImGuiChildFlags_AutoResizeX) + child_flags &= ~ImGuiChildFlags_ResizeX; + if (child_flags & ImGuiChildFlags_AutoResizeY) + child_flags &= ~ImGuiChildFlags_ResizeY; - // Size - const ImVec2 content_avail = GetContentRegionAvail(); - ImVec2 size = ImFloor(size_arg); - const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); - if (size.x <= 0.0f) - size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too many issues) - if (size.y <= 0.0f) - size.y = ImMax(content_avail.y + size.y, 4.0f); + // Set window flags + window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar; + window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag + if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize)) + window_flags |= ImGuiWindowFlags_AlwaysAutoResize; + if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0) + window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; + + // Special framed style + if (child_flags & ImGuiChildFlags_FrameStyle) + { + PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]); + PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding); + PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize); + PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding); + child_flags |= ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding; + window_flags |= ImGuiWindowFlags_NoMove; + } + + // Forward child flags + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags; + g.NextWindowData.ChildFlags = child_flags; + + // Forward size + // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set. + // (the alternative would to store conditional flags per axis, which is possible but more code) + const ImVec2 size_avail = GetContentRegionAvail(); + const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y); + const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y); SetNextWindowSize(size); // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. + // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround. + // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it. const char* temp_window_name; + /*if (name && parent_window->IDStack.back() == parent_window->ID) + ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s", parent_window->Name, name); // May omit ID if in root of ID stack + else*/ if (name) ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%s_%08X", parent_window->Name, name, id); else ImFormatStringToTempBuffer(&temp_window_name, NULL, "%s/%08X", parent_window->Name, id); + // Set style const float backup_border_size = g.Style.ChildBorderSize; - if (!border) + if ((child_flags & ImGuiChildFlags_Border) == 0) g.Style.ChildBorderSize = 0.0f; - bool ret = Begin(temp_window_name, NULL, flags); + + // Begin into window + const bool ret = Begin(temp_window_name, NULL, window_flags); + + // Restore style g.Style.ChildBorderSize = backup_border_size; + if (child_flags & ImGuiChildFlags_FrameStyle) + { + PopStyleVar(3); + PopStyleColor(); + } ImGuiWindow* child_window = g.CurrentWindow; child_window->ChildId = id; - child_window->AutoFitChildAxises = (ImS8)auto_fit_axises; // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually. // While this is not really documented/defined, it seems that the expected thing to do. @@ -5270,94 +5515,61 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b parent_window->DC.CursorPos = child_window->Pos; // Process navigation-in immediately so NavInit can run on first frame - if (g.NavActivateId == id && !(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavHasScroll)) + // Can enter a child if (A) it has navigable items or (B) it can be scrolled. + const ImGuiID temp_id_for_activation = ImHashStr("##Child", 0, id); + if (g.ActiveId == temp_id_for_activation) + ClearActiveID(); + if (g.NavActivateId == id && !(window_flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY)) { FocusWindow(child_window); NavInitWindow(child_window, false); - SetActiveID(id + 1, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item - g.ActiveIdSource = ImGuiInputSource_Nav; + SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item + g.ActiveIdSource = g.NavInputSource; } return ret; } -bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags); -} - -bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - IM_ASSERT(id != 0); - return BeginChildEx(NULL, id, size_arg, border, extra_flags); -} - void ImGui::EndChild() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; + ImGuiWindow* child_window = g.CurrentWindow; IM_ASSERT(g.WithinEndChild == false); - IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls + IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() calls g.WithinEndChild = true; - if (window->BeginCount > 1) + ImVec2 child_size = child_window->Size; + End(); + if (child_window->BeginCount == 1) { - End(); - } - else - { - ImVec2 sz = window->Size; - if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f - sz.x = ImMax(4.0f, sz.x); - if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) - sz.y = ImMax(4.0f, sz.y); - End(); - ImGuiWindow* parent_window = g.CurrentWindow; - ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); - ItemSize(sz); - if ((window->DC.NavLayersActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) + ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size); + ItemSize(child_size); + if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !(child_window->Flags & ImGuiWindowFlags_NavFlattened)) { - ItemAdd(bb, window->ChildId); - RenderNavHighlight(bb, window->ChildId); + ItemAdd(bb, child_window->ChildId); + RenderNavHighlight(bb, child_window->ChildId); // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying) - if (window->DC.NavLayersActiveMask == 0 && window == g.NavWindow) - RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); + if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow) + RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_Compact); } else { // Not navigable into ItemAdd(bb, 0); + + // But when flattened we directly reach items, adjust active layer mask accordingly + if (child_window->Flags & ImGuiWindowFlags_NavFlattened) + parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext; } - if (g.HoveredWindow == window) + if (g.HoveredWindow == child_window) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; } g.WithinEndChild = false; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } -// Helper to create a child window / scrolling region that looks like a normal widget frame. -bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); - PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); - PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); - PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); - bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); - PopStyleVar(3); - PopStyleColor(); - return ret; -} - -void ImGui::EndChildFrame() -{ - EndChild(); -} - static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) { window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); @@ -5379,9 +5591,9 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { - window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y)); + window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y)); if (settings->Size.x > 0 && settings->Size.y > 0) - window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y)); + window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y)); window->Collapsed = settings->Collapsed; } @@ -5414,6 +5626,7 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); window->Pos = main_viewport->Pos + ImVec2(60, 60); + window->Size = window->SizeFull = ImVec2(0, 0); window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; if (settings != NULL) @@ -5462,13 +5675,37 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) return window; } +static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window) +{ + // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) + // FIXME: Essentially we want to restrict manual resizing to WindowMinSize+Decoration, and allow api resizing to be smaller. + // Perhaps should tend further a neater test for this. + ImGuiContext& g = *GImGui; + ImVec2 size_min; + if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) + { + size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f; + size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f; + } + else + { + size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f; + size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.y : 4.0f; + } + + // Reduce artifacts with very small windows + ImGuiWindow* window_for_height = window; + size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); + return size_min; +} + static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired) { ImGuiContext& g = *GImGui; ImVec2 new_size = size_desired; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint) { - // Using -1,-1 on either X/Y axis to preserve the current size. + // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max. ImRect cr = g.NextWindowData.SizeConstraintRect; new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; @@ -5482,19 +5719,13 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& s g.NextWindowData.SizeCallback(&data); new_size = data.DesiredSize; } - new_size.x = IM_FLOOR(new_size.x); - new_size.y = IM_FLOOR(new_size.y); + new_size.x = IM_TRUNC(new_size.x); + new_size.y = IM_TRUNC(new_size.y); } // Minimum size - if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) - { - ImGuiWindow* window_for_height = window; - new_size = ImMax(new_size, g.Style.WindowMinSize); - const float minimum_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f); - new_size.y = ImMax(new_size.y, minimum_height); // Reduce artifacts with very small windows - } - return new_size; + ImVec2 size_min = CalcWindowMinSize(window); + return ImMax(new_size, size_min); } static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal) @@ -5511,10 +5742,10 @@ static void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_cur return; } - content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); - content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); - content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_FLOOR(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x); - content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_FLOOR(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); + content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x); + content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y); + content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x); + content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y); } static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents) @@ -5533,14 +5764,9 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont else { // Maximum window size is determined by the viewport size or monitor size - const bool is_popup = (window->Flags & ImGuiWindowFlags_Popup) != 0; - const bool is_menu = (window->Flags & ImGuiWindowFlags_ChildMenu) != 0; - ImVec2 size_min = style.WindowMinSize; - if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) - size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); - - ImVec2 avail_size = ImGui::GetMainViewport()->WorkSize; - ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f)); + ImVec2 size_min = CalcWindowMinSize(window); + ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f; + ImVec2 size_auto_fit = ImClamp(size_desired, size_min, size_max); // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. @@ -5588,7 +5814,7 @@ static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& co *out_size = size_constrained; } -// Data for resizing from corner +// Data for resizing from resize grip / corner struct ImGuiResizeGripDef { ImVec2 CornerPosN; @@ -5606,9 +5832,9 @@ static const ImGuiResizeGripDef resize_grip_def[4] = // Data for resizing from borders struct ImGuiResizeBorderDef { - ImVec2 InnerDir; - ImVec2 SegmentN1, SegmentN2; - float OuterAngle; + ImVec2 InnerDir; // Normal toward inside + ImVec2 SegmentN1, SegmentN2; // End positions, normalized (0,0: upper left) + float OuterAngle; // Angle toward outside }; static const ImGuiResizeBorderDef resize_border_def[4] = { @@ -5654,7 +5880,7 @@ ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) // Handle resize for: Resize Grips, Borders, Gamepad // Return true when using auto-fit (double-click on resize grip) -static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) +static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) { ImGuiContext& g = *GImGui; ImGuiWindowFlags flags = window->Flags; @@ -5664,12 +5890,16 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window. return false; - bool ret_auto_fit = false; - const int resize_border_count = g.IO.ConfigWindowsResizeFromEdges ? 4 : 0; - const float grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); - const float grip_hover_inner_size = IM_FLOOR(grip_draw_size * 0.75f); + int ret_auto_fit_mask = 0x00; + const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); + const float grip_hover_inner_size = IM_TRUNC(grip_draw_size * 0.75f); const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f; + ImRect clamp_rect = visibility_rect; + const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar); + if (window_move_from_title_bar) + clamp_rect.Min.y -= window->TitleBarHeight(); + ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); @@ -5695,19 +5925,19 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (hovered || held) g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - if (held && g.IO.MouseClickedCount[0] == 2 && resize_grip_n == 0) + if (held && g.IO.MouseDoubleClicked[0]) { - // Manual auto-fit when double-clicking + // Auto-fit when double-clicking size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit); - ret_auto_fit = true; + ret_auto_fit_mask = 0x03; // Both axises ClearActiveID(); } else if (held) { // Resize from any of the four corners // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? visibility_rect.Min.x : -FLT_MAX, def.CornerPosN.y == 1.0f ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? visibility_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? visibility_rect.Max.y : +FLT_MAX); + ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX); + ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX); ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip corner_target = ImClamp(corner_target, clamp_min, clamp_max); CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target); @@ -5717,8 +5947,16 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (resize_grip_n == 0 || held || hovered) resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); } - for (int border_n = 0; border_n < resize_border_count; border_n++) + + int resize_border_mask = 0x00; + if (window->Flags & ImGuiWindowFlags_ChildWindow) + resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0); + else + resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00; + for (int border_n = 0; border_n < 4; border_n++) { + if ((resize_border_mask & (1 << border_n)) == 0) + continue; const ImGuiResizeBorderDef& def = resize_border_def[border_n]; const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; @@ -5727,22 +5965,73 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); - //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); - if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) - { + //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); + if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) + hovered = false; + if (hovered || held) g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; - if (held) - *border_held = border_n; - } - if (held) + if (held && g.IO.MouseDoubleClicked[0]) { - ImVec2 clamp_min(border_n == ImGuiDir_Right ? visibility_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down ? visibility_rect.Min.y : -FLT_MAX); - ImVec2 clamp_max(border_n == ImGuiDir_Left ? visibility_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? visibility_rect.Max.y : +FLT_MAX); - ImVec2 border_target = window->Pos; - border_target[axis] = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; - border_target = ImClamp(border_target, clamp_min, clamp_max); - CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); + // Double-clicking bottom or right border auto-fit on this axis + // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases. + if (border_n == 1 || border_n == 3) // Right and bottom border + { + size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis]; + ret_auto_fit_mask |= (1 << axis); + hovered = held = false; // So border doesn't show highlighted at new position + } + ClearActiveID(); } + else if (held) + { + // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop. + // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually. + // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it! + const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false)); + if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing) + { + g.WindowResizeBorderExpectedRect = border_rect; + g.WindowResizeRelativeMode = false; + } + if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0) + g.WindowResizeRelativeMode = true; + + const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size); + const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis]; + const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above. + + // Use absolute mode position + ImVec2 border_target = window->Pos; + border_target[axis] = border_target_abs_mode_for_axis; + + // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position. + bool ignore_resize = false; + if (g.WindowResizeRelativeMode) + { + //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, "Relative Mode"); + border_target[axis] = border_target_rel_mode_for_axis; + if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis)) + ignore_resize = true; + } + + // Clamp, apply + ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX); + ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX); + border_target = ImClamp(border_target, clamp_min, clamp_max); + if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent + { + if ((flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (flags & ImGuiWindowFlags_NoScrollbar)) + border_target.x = ImClamp(border_target.x, window->ParentWindow->InnerClipRect.Min.x, window->ParentWindow->InnerClipRect.Max.x); + if (flags & ImGuiWindowFlags_NoScrollbar) + border_target.y = ImClamp(border_target.y, window->ParentWindow->InnerClipRect.Min.y, window->ParentWindow->InnerClipRect.Max.y); + } + if (!ignore_resize) + CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target); + } + if (hovered) + *border_hovered = border_n; + if (held) + *border_held = border_n; } PopID(); @@ -5764,11 +6053,11 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s const float NAV_RESIZE_SPEED = 600.0f; const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y); g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step; - g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, visibility_rect.Min - window->Pos - window->Size); // We need Pos+Size >= visibility_rect.Min, so Size >= visibility_rect.Min - Pos, so size_delta >= visibility_rect.Min - window->Pos - window->Size + g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size g.NavWindowingToggleLayer = false; g.NavDisableMouseHover = true; resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); - ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaSize); + ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize); if (accum_floored.x != 0.0f || accum_floored.y != 0.0f) { // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. @@ -5779,19 +6068,24 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s } // Apply back modified position/size to window - if (size_target.x != FLT_MAX) - { - window->SizeFull = size_target; + const ImVec2 curr_pos = window->Pos; + const ImVec2 curr_size = window->SizeFull; + if (size_target.x != FLT_MAX && (window->Size.x != size_target.x || window->SizeFull.x != size_target.x)) + window->Size.x = window->SizeFull.x = size_target.x; + if (size_target.y != FLT_MAX && (window->Size.y != size_target.y || window->SizeFull.y != size_target.y)) + window->Size.y = window->SizeFull.y = size_target.y; + if (pos_target.x != FLT_MAX && window->Pos.x != ImTrunc(pos_target.x)) + window->Pos.x = ImTrunc(pos_target.x); + if (pos_target.y != FLT_MAX && window->Pos.y != ImTrunc(pos_target.y)) + window->Pos.y = ImTrunc(pos_target.y); + if (curr_pos.x != window->Pos.x || curr_pos.y != window->Pos.y || curr_size.x != window->SizeFull.x || curr_size.y != window->SizeFull.y) MarkIniSettingsDirty(window); - } - if (pos_target.x != FLT_MAX) - { - window->Pos = ImFloor(pos_target); - MarkIniSettingsDirty(window); - } - window->Size = window->SizeFull; - return ret_auto_fit; + // Recalculate next expected border expected coordinates + if (*border_held != -1) + g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, WINDOWS_HOVER_PADDING); + + return ret_auto_fit_mask; } static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect) @@ -5803,27 +6097,40 @@ static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_ window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); } +static void RenderWindowOuterSingleBorder(ImGuiWindow* window, int border_n, ImU32 border_col, float border_size) +{ + const ImGuiResizeBorderDef& def = resize_border_def[border_n]; + const float rounding = window->WindowRounding; + const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); + window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); + window->DrawList->PathStroke(border_col, ImDrawFlags_None, border_size); +} + static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - float rounding = window->WindowRounding; - float border_size = window->WindowBorderSize; - if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground)) - window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size); - - int border_held = window->ResizeBorderHeld; - if (border_held != -1) + const float border_size = window->WindowBorderSize; + const ImU32 border_col = GetColorU32(ImGuiCol_Border); + if (border_size > 0.0f && (window->Flags & ImGuiWindowFlags_NoBackground) == 0) + window->DrawList->AddRect(window->Pos, window->Pos + window->Size, border_col, window->WindowRounding, 0, window->WindowBorderSize); + else if (border_size > 0.0f) { - const ImGuiResizeBorderDef& def = resize_border_def[border_held]; - ImRect border_r = GetResizeBorderRect(window, border_held, rounding, 0.0f); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle); - window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); - window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), 0, ImMax(2.0f, border_size)); // Thicker than usual + if (window->ChildFlags & ImGuiChildFlags_ResizeX) // Similar code as 'resize_border_mask' computation in UpdateWindowManualResize() but we specifically only always draw explicit child resize border. + RenderWindowOuterSingleBorder(window, 1, border_col, border_size); + if (window->ChildFlags & ImGuiChildFlags_ResizeY) + RenderWindowOuterSingleBorder(window, 3, border_col, border_size); + } + if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1) + { + const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered; + const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered); + RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual } if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) { float y = window->Pos.y + window->TitleBarHeight() - 1; - window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); + window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize); } } @@ -5941,18 +6248,18 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl ImVec2 collapse_button_pos; if (has_close_button) { - pad_r += button_sz; - close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); + close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); + pad_r += button_sz + style.ItemInnerSpacing.x; } if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right) { - pad_r += button_sz; - collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - style.FramePadding.x, title_bar_rect.Min.y); + collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y); + pad_r += button_sz + style.ItemInnerSpacing.x; } if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left) { - collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l - style.FramePadding.x, title_bar_rect.Min.y); - pad_l += button_sz; + collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y); + pad_l += button_sz + style.ItemInnerSpacing.x; } // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) @@ -5962,14 +6269,12 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl // Close button if (has_close_button) - { if (CloseButton(window->GetID("#CLOSE"), close_button_pos)) { *p_open = false; if (window->CloseCallback) ((void(*)(void))window->CloseCallback)(); } - } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; g.CurrentItemFlags = item_flags_backup; @@ -6031,31 +6336,35 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) // should be positioned behind that modal window, unless the window was created inside the modal begin-stack. // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. -// - Window // FindBlockingModal() returns Modal1 -// - Window // .. returns Modal1 +// - WindowA // FindBlockingModal() returns Modal1 +// - WindowB // .. returns Modal1 // - Modal1 // .. returns Modal2 -// - Window // .. returns Modal2 -// - Window // .. returns Modal2 +// - WindowC // .. returns Modal2 +// - WindowD // .. returns Modal2 // - Modal2 // .. returns Modal2 -static ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) +// - WindowE // .. returns NULL +// Notes: +// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL. +// Only difference is here we check for ->Active/WasActive but it may be unecessary. +ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) { ImGuiContext& g = *GImGui; if (g.OpenPopupStack.Size <= 0) return NULL; // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. - for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + for (ImGuiPopupData& popup_data : g.OpenPopupStack) { - ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; + ImGuiWindow* popup_window = popup_data.Window; if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal)) continue; if (!popup_window->Active && !popup_window->WasActive) // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows. continue; - if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. - break; - for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow) - if (IsWindowWithinBeginStackOf(window, parent)) - return popup_window; // Place window above its begin stack parent. + if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click. + return popup_window; + if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal + continue; + return popup_window; // Place window right below first block modal } return NULL; } @@ -6081,6 +6390,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* if (window_just_created) window = CreateNewWindow(name, flags); + // [DEBUG] Debug break requested by user + if (g.DebugBreakInWindow == window->ID) + IM_DEBUG_BREAK(); + // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; @@ -6109,6 +6422,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* { UpdateWindowInFocusOrderList(window, window_just_created, flags); window->Flags = (ImGuiWindowFlags)flags; + window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0; window->LastFrameActive = current_frame; window->LastTimeActive = (float)g.Time; window->BeginOrderWithinParent = 0; @@ -6129,7 +6443,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* window->IDStack.push_back(window->ID); // Add to stack - // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() g.CurrentWindow = window; ImGuiWindowStackData window_stack_data; window_stack_data.Window = window; @@ -6137,19 +6450,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* window_stack_data.StackSizesOnBegin.SetToContextState(&g); g.CurrentWindowStack.push_back(window_stack_data); if (flags & ImGuiWindowFlags_ChildMenu) - g.BeginMenuCount++; + g.BeginMenuDepth++; // Update ->RootWindow and others pointers (before any possible call to FocusWindow) if (first_begin_of_the_frame) { UpdateWindowParentAndRootLinks(window, flags, parent_window); window->ParentWindowInBeginStack = parent_window_in_stack; + + // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack, + // e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798) + window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL; } // Add to focus scope stack - PushFocusScope(window->ID); + PushFocusScope((flags & ImGuiWindowFlags_NavFlattened) ? g.CurrentFocusScopeId : window->ID); window->NavRootFocusScopeId = g.CurrentFocusScopeId; - g.CurrentWindow = NULL; // Add to popup stack if (flags & ImGuiWindowFlags_Popup) @@ -6185,6 +6501,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* { window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); + if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild() + g.NextWindowData.SizeVal.x = window->SizeFull.x; + if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) + g.NextWindowData.SizeVal.y = window->SizeFull.y; SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); } if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll) @@ -6211,6 +6531,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* if (window->Appearing) SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); + // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow() + g.CurrentWindow = NULL; + // When reusing window again multiple times a frame, just append content (don't need to setup again) if (first_begin_of_the_frame) { @@ -6285,7 +6608,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* else window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f) window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. @@ -6408,7 +6731,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow)) if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f) ClampWindowPos(window, visibility_rect); - window->Pos = ImFloor(window->Pos); + window->Pos = ImTrunc(window->Pos); // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) // Large values tend to lead to variety of artifacts and are not recommended. @@ -6426,22 +6749,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* want_focus = true; else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; - - ImGuiWindow* modal = GetTopMostPopupModal(); - if (modal != NULL && !IsWindowWithinBeginStackOf(window, modal)) - { - // Avoid focusing a window that is created outside of active modal. This will prevent active modal from being closed. - // Since window is not focused it would reappear at the same display position like the last time it was visible. - // In case of completely new windows it would go to the top (over current modal), but input to such window would still be blocked by modal. - // Position window behind a modal that is not a begin-parent of this window. - want_focus = false; - if (window == window->RootWindow) - { - ImGuiWindow* blocking_modal = FindBlockingModal(window); - IM_ASSERT(blocking_modal != NULL); - BringWindowToDisplayBehind(window, blocking_modal); - } - } } // [Test Engine] Register whole window in the item system (before submitting further decorations) @@ -6457,13 +6764,19 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* #endif // Handle manual resize: Resize Grips, Borders, Gamepad - int border_held = -1; + int border_hovered = -1, border_held = -1; ImU32 resize_grip_col[4] = {}; - const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. - const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); + const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. + const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); if (!window->Collapsed) - if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) - use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; + if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) + { + if (auto_fit_mask & (1 << ImGuiAxis_X)) + use_current_size_for_scrollbar_x = true; + if (auto_fit_mask & (1 << ImGuiAxis_Y)) + use_current_size_for_scrollbar_y = true; + } + window->ResizeBorderHovered = (signed char)border_hovered; window->ResizeBorderHeld = (signed char)border_held; // SCROLLBAR VISIBILITY @@ -6481,14 +6794,16 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); - window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); + window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - ((window->ScrollbarY && !(flags & ImGuiWindowFlags_OverlayHorizontalScrollbar)) ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); if (window->ScrollbarX && !window->ScrollbarY) window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); // Amend the partially filled window->DecorationXXX values. - window->DecoOuterSizeX2 += window->ScrollbarSizes.x; - window->DecoOuterSizeY2 += window->ScrollbarSizes.y; + if (!(flags & ImGuiWindowFlags_OverlayVerticalScrollbar)) + window->DecoOuterSizeX2 += window->ScrollbarSizes.x; + if (!(flags & ImGuiWindowFlags_OverlayHorizontalScrollbar)) + window->DecoOuterSizeY2 += window->ScrollbarSizes.y; } // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) @@ -6525,17 +6840,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* // Affected by window/frame border size. Used by: // - Begin() initial clip rect float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerRect.Min.x + ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); - window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerRect.Min.y + top_border_size); - window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerRect.Max.x - ImMax(ImFloor(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); - window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); + window->InnerClipRect.Min.x = ImTrunc(0.5f + window->InnerRect.Min.x + ImMax(ImTrunc(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); + window->InnerClipRect.Min.y = ImTrunc(0.5f + window->InnerRect.Min.y + top_border_size); + window->InnerClipRect.Max.x = ImTrunc(0.5f + window->InnerRect.Max.x - ImMax(ImTrunc(window->WindowPadding.x * 0.5f), window->WindowBorderSize)); + window->InnerClipRect.Max.y = ImTrunc(0.5f + window->InnerRect.Max.y - window->WindowBorderSize); window->InnerClipRect.ClipWithFull(host_rect); // Default item width. Make it proportional to window size if window manually resizes if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) - window->ItemWidthDefault = ImFloor(window->Size.x * 0.65f); + window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f); else - window->ItemWidthDefault = ImFloor(g.FontSize * 16.0f); + window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f); // SCROLLING @@ -6568,8 +6883,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL; bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false; - bool parent_is_empty = parent_window->DrawList->VtxBuffer.Size > 0; - if (window->DrawList->CmdBuffer.back().ElemCount == 0 && parent_is_empty && !previous_child_overlapping) + bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0); + if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping) render_decorations_in_parent = true; } if (render_decorations_in_parent) @@ -6596,14 +6911,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2))); const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2))); - window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); - window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); + window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); + window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y; window->ParentWorkRect = window->WorkRect; // [LEGACY] Content Region // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. + // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling. // Used by: // - Mouse wheel scrolling + many other things window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1; @@ -6634,8 +6950,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; window->DC.NavLayersActiveMaskNext = 0x00; + window->DC.NavIsScrollPushableX = true; window->DC.NavHideHighlightOneFrame = false; - window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); + window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f); window->DC.MenuBarAppending = false; window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user); @@ -6651,6 +6968,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* window->DC.TextWrapPos = -1.0f; // disabled window->DC.ItemWidthStack.resize(0); window->DC.TextWrapPosStack.resize(0); + if (flags & ImGuiWindowFlags_Modal) + window->DC.ModalDimBgColor = ColorConvertFloat4ToU32(GetStyleColorVec4(ImGuiCol_ModalWindowDimBg)); if (window->AutoFitFramesX > 0) window->AutoFitFramesX--; @@ -6658,11 +6977,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* window->AutoFitFramesY--; // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) + // We ImGuiFocusRequestFlags_UnlessBelowModal to: + // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed. + // - Position window behind the modal that is not a begin-parent of this window. if (want_focus) - { - FocusWindow(window); + FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal); + if (want_focus && window == g.NavWindow) NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls - } // Title bar if (!(flags & ImGuiWindowFlags_NoTitleBar)) @@ -6713,18 +7034,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* // Update visibility if (first_begin_of_the_frame) { - if (flags & ImGuiWindowFlags_ChildWindow) + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) { // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); - if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) // FIXME: Doesn't make sense for ChildWindow?? - { - const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); - if (!g.LogEnabled && !nav_request) - if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) + const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); + if (!g.LogEnabled && !nav_request) + if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) + { + if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) + window->HiddenFramesCannotSkipItems = 1; + else window->HiddenFramesCanSkipItems = 1; - } + } // Hide along with parent or if parent is collapsed if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0)) @@ -6758,12 +7081,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags, void* // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors. // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing) - if (!window->IsFallbackWindow && ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size))) - { - if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; } - if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; } - return false; - } +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (!window->IsFallbackWindow) + if ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size)) + { + if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; } + if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; } + return false; + } +#endif return !window->SkipItems; } @@ -6801,7 +7127,7 @@ void ImGui::End() // Pop from window stack g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; if (window->Flags & ImGuiWindowFlags_ChildMenu) - g.BeginMenuCount--; + g.BeginMenuDepth--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithContextState(&g); @@ -6888,10 +7214,25 @@ int ImGui::FindWindowDisplayIndex(ImGuiWindow* window) } // Moving window to front of display and set focus (which happens to be back of our sorted list) -void ImGui::FocusWindow(ImGuiWindow* window) +void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags) { ImGuiContext& g = *GImGui; + // Modal check? + if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case. + if (ImGuiWindow* blocking_modal = FindBlockingModal(window)) + { + IMGUI_DEBUG_LOG_FOCUS("[focus] FocusWindow(\"%s\", UnlessBelowModal): prevented by \"%s\".\n", window ? window->Name : "", blocking_modal->Name); + if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) + BringWindowToDisplayBehind(window, blocking_modal); // Still bring to right below modal. + return; + } + + // Find last focused child (if any) and focus it instead. + if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL) + window = NavRestoreLastChildNavWindow(window); + + // Apply focus if (g.NavWindow != window) { SetNavWindow(window); @@ -6899,8 +7240,9 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavMousePosDirty = true; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavLayer = ImGuiNavLayer_Main; - g.NavFocusScopeId = window ? window->NavRootFocusScopeId : 0; + SetNavFocusScope(window ? window->NavRootFocusScopeId : 0); g.NavIdIsAlive = false; + g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; // Close popups if any ClosePopupsOverWindow(window, false); @@ -6928,9 +7270,10 @@ void ImGui::FocusWindow(ImGuiWindow* window) BringWindowToDisplayFront(display_front_window); } -void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window) +void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags) { ImGuiContext& g = *GImGui; + IM_UNUSED(filter_viewport); // Unused in master branch. int start_idx = g.WindowsFocusOrder.Size - 1; if (under_this_window != NULL) { @@ -6947,16 +7290,15 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; - IM_ASSERT(window == window->RootWindow); - if (window != ignore_window && window->WasActive) - if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) - { - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); - FocusWindow(focus_window); - return; - } + if (window == ignore_window || !window->WasActive) + continue; + if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) + { + FocusWindow(window, flags); + return; + } } - FocusWindow(NULL); + FocusWindow(NULL, flags); } // Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. @@ -7145,9 +7487,14 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b return false; } +// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. +// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, +// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! +// Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function + IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!"); + ImGuiContext& g = *GImGui; ImGuiWindow* ref_window = g.HoveredWindow; ImGuiWindow* cur_window = g.CurrentWindow; @@ -7175,6 +7522,17 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; + + // When changing hovered window we requires a bit of stationary delay before activating hover timer. + // FIXME: We don't support delay other than stationary one for now, other delay would need a way + // to fullfill the possibility that multiple IsWindowHovered() with varying flag could return true + // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache. + // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow. + if (flags & ImGuiHoveredFlags_ForTooltip) + flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse); + if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID) + return false; + return true; } @@ -7239,7 +7597,7 @@ void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) // Set const ImVec2 old_pos = window->Pos; - window->Pos = ImFloor(pos); + window->Pos = ImTrunc(pos); ImVec2 offset = window->Pos - old_pos; if (offset.x == 0.0f && offset.y == 0.0f) return; @@ -7277,18 +7635,22 @@ void ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond con IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); + // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize) + if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0) + window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0; + if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0) + window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0; + // Set ImVec2 old_size = window->SizeFull; - window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0; - window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0; if (size.x <= 0.0f) window->AutoFitOnlyGrows = false; else - window->SizeFull.x = IM_FLOOR(size.x); + window->SizeFull.x = IM_TRUNC(size.x); if (size.y <= 0.0f) window->AutoFitOnlyGrows = false; else - window->SizeFull.y = IM_FLOOR(size.y); + window->SizeFull.y = IM_TRUNC(size.y); if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y) MarkIniSettingsDirty(window); } @@ -7342,7 +7704,7 @@ void ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const I window->HitTestHoleOffset = ImVec2ih(pos - window->Pos); } -void ImGui::SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window) +void ImGui::SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window) { window->Hidden = window->SkipItems = true; window->HiddenFramesCanSkipItems = 1; @@ -7408,6 +7770,10 @@ void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; } +// For each axis: +// - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width. +// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint. +// - See "Demo->Examples->Constrained-resizing window" for examples. void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) { ImGuiContext& g = *GImGui; @@ -7423,7 +7789,7 @@ void ImGui::SetNextWindowContentSize(const ImVec2& size) { ImGuiContext& g = *GImGui; g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize; - g.NextWindowData.ContentSizeVal = ImFloor(size); + g.NextWindowData.ContentSizeVal = ImTrunc(size); } void ImGui::SetNextWindowScroll(const ImVec2& scroll) @@ -7485,35 +7851,89 @@ void ImGui::SetWindowFontScale(float scale) g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); } -void ImGui::ActivateItem(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - g.NavNextActivateId = id; - g.NavNextActivateFlags = ImGuiActivateFlags_None; -} - void ImGui::PushFocusScope(ImGuiID id) { ImGuiContext& g = *GImGui; - g.FocusScopeStack.push_back(id); + ImGuiFocusScopeData data; + data.ID = id; + data.WindowID = g.CurrentWindow->ID; + g.FocusScopeStack.push_back(data); g.CurrentFocusScopeId = id; } void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ? + if (g.FocusScopeStack.Size == 0) + { + IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!"); + return; + } g.FocusScopeStack.pop_back(); - g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back() : 0; + g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back().ID : 0; +} + +void ImGui::SetNavFocusScope(ImGuiID focus_scope_id) +{ + ImGuiContext& g = *GImGui; + g.NavFocusScopeId = focus_scope_id; + g.NavFocusRoute.resize(0); // Invalidate + if (focus_scope_id == 0) + return; + IM_ASSERT(g.NavWindow != NULL); + + // Store current path (in reverse order) + if (focus_scope_id == g.CurrentFocusScopeId) + { + // Top of focus stack contains local focus scopes inside current window + for (int n = g.FocusScopeStack.Size - 1; n >= 0 && g.FocusScopeStack.Data[n].WindowID == g.CurrentWindow->ID; n--) + g.NavFocusRoute.push_back(g.FocusScopeStack.Data[n]); + } + else if (focus_scope_id == g.NavWindow->NavRootFocusScopeId) + g.NavFocusRoute.push_back({ focus_scope_id, g.NavWindow->ID }); + else + return; + + // Then follow on manually set ParentWindowForFocusRoute field (#6798) + for (ImGuiWindow* window = g.NavWindow->ParentWindowForFocusRoute; window != NULL; window = window->ParentWindowForFocusRoute) + g.NavFocusRoute.push_back({ window->NavRootFocusScopeId, window->ID }); + IM_ASSERT(g.NavFocusRoute.Size < 100); // Maximum depth is technically 251 as per CalcRoutingScore(): 254 - 3 +} + +// Focus = move navigation cursor, set scrolling, set focus window. +void ImGui::FocusItem() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IMGUI_DEBUG_LOG_FOCUS("FocusItem(0x%08x) in window \"%s\"\n", g.LastItemData.ID, window->Name); + if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this? + { + IMGUI_DEBUG_LOG_FOCUS("FocusItem() ignored while DragDropActive!\n"); + return; + } + + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight | ImGuiNavMoveFlags_NoSelect; + ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; + SetNavWindow(window); + NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags); + NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); +} + +void ImGui::ActivateItemByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.NavNextActivateId = id; + g.NavNextActivateFlags = ImGuiActivateFlags_None; } // Note: this will likely be called ActivateItem() once we rework our Focus/Activation system! +// But ActivateItem() should function without altering scroll/focus? void ImGui::SetKeyboardFocusHere(int offset) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(offset >= -1); // -1 is allowed but not below - IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); + IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere(%d) in window \"%s\"\n", offset, window->Name); // It makes sense in the vast majority of cases to never interrupt a drag and drop. // When we refactor this function into ActivateItem() we may want to make this an option. @@ -7521,14 +7941,15 @@ void ImGui::SetKeyboardFocusHere(int offset) // is also automatically dropped in the event g.ActiveId is stolen. if (g.DragDropActive || g.MovingWindow != NULL) { - IMGUI_DEBUG_LOG_ACTIVEID("SetKeyboardFocusHere() ignored while DragDropActive!\n"); + IMGUI_DEBUG_LOG_FOCUS("SetKeyboardFocusHere() ignored while DragDropActive!\n"); return; } SetNavWindow(window); + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; - NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, ImGuiNavMoveFlags_Tabbing | ImGuiNavMoveFlags_FocusApi, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. if (offset == -1) { NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal); @@ -7546,12 +7967,11 @@ void ImGui::SetItemDefaultFocus() ImGuiWindow* window = g.CurrentWindow; if (!window->Appearing) return; - if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResultId == 0) || g.NavLayer != window->DC.NavLayerCurrent) + if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent) return; g.NavInitRequest = false; - g.NavInitResultId = g.LastItemData.ID; - g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); + NavApplyItemToResult(&g.NavInitResult); NavUpdateAnyRequestFlag(); // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll) @@ -7614,7 +8034,7 @@ void ImGui::PushOverrideID(ImGuiID id) } // Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call -// (note that when using this pattern, TestEngine's "Stack Tool" will tend to not display the intermediate stack level. +// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level. // for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more) ImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed) { @@ -7704,6 +8124,7 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // - IsMouseDragPastThreshold() [Internal] // - IsMouseDragging() // - GetMousePos() +// - SetMousePos() [Internal] // - GetMousePosOnOpeningCurrentPopup() // - IsMousePosValid() // - IsAnyMouseDown() @@ -7735,13 +8156,33 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // - Shortcut() [Internal] //----------------------------------------------------------------------------- -ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) +ImGuiKeyChord ImGui::FixupKeyChord(ImGuiContext* ctx, ImGuiKeyChord key_chord) { - ImGuiContext& g = *GImGui; + // Convert ImGuiMod_Shortcut and add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. + ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); + if (IsModKey(key)) + { + if (key == ImGuiKey_LeftCtrl || key == ImGuiKey_RightCtrl) + key_chord |= ImGuiMod_Ctrl; + if (key == ImGuiKey_LeftShift || key == ImGuiKey_RightShift) + key_chord |= ImGuiMod_Shift; + if (key == ImGuiKey_LeftAlt || key == ImGuiKey_RightAlt) + key_chord |= ImGuiMod_Alt; + if (key == ImGuiKey_LeftSuper || key == ImGuiKey_RightSuper) + key_chord |= ImGuiMod_Super; + } + if (key_chord & ImGuiMod_Shortcut) + return (key_chord & ~ImGuiMod_Shortcut) | (ctx->IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); + return key_chord; +} + +ImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key) +{ + ImGuiContext& g = *ctx; // Special storage location for mods if (key & ImGuiMod_Mask_) - key = ConvertSingleModFlagToKey(key); + key = ConvertSingleModFlagToKey(ctx, key); #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END); @@ -7772,11 +8213,13 @@ static const char* const GKeyNames[] = "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", "F24", "Apostrophe", "Comma", "Minus", "Period", "Slash", "Semicolon", "Equal", "LeftBracket", "Backslash", "RightBracket", "GraveAccent", "CapsLock", "ScrollLock", "NumLock", "PrintScreen", "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply", "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", + "AppBack", "AppForward", "GamepadStart", "GamepadBack", "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown", "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown", @@ -7790,22 +8233,22 @@ IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); const char* ImGui::GetKeyName(ImGuiKey key) { + ImGuiContext& g = *GImGui; #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO - IM_ASSERT((IsNamedKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); + IM_ASSERT((IsNamedKeyOrModKey(key) || key == ImGuiKey_None) && "Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code."); #else if (IsLegacyKey(key)) { - ImGuiIO& io = GetIO(); - if (io.KeyMap[key] == -1) + if (g.IO.KeyMap[key] == -1) return "N/A"; - IM_ASSERT(IsNamedKey((ImGuiKey)io.KeyMap[key])); - key = (ImGuiKey)io.KeyMap[key]; + IM_ASSERT(IsNamedKey((ImGuiKey)g.IO.KeyMap[key])); + key = (ImGuiKey)g.IO.KeyMap[key]; } #endif if (key == ImGuiKey_None) return "None"; if (key & ImGuiMod_Mask_) - key = ConvertSingleModFlagToKey(key); + key = ConvertSingleModFlagToKey(&g, key); if (!IsNamedKey(key)) return "Unknown"; @@ -7813,17 +8256,17 @@ const char* ImGui::GetKeyName(ImGuiKey key) } // ImGuiMod_Shortcut is translated to either Ctrl or Super. -void ImGui::GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size) +const char* ImGui::GetKeyChordName(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; - if (key_chord & ImGuiMod_Shortcut) - key_chord = ConvertShortcutMod(key_chord); - ImFormatString(out_buf, (size_t)out_buf_size, "%s%s%s%s%s", + key_chord = FixupKeyChord(&g, key_chord); + ImFormatString(g.TempKeychordName, IM_ARRAYSIZE(g.TempKeychordName), "%s%s%s%s%s", (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", (key_chord & ImGuiMod_Shift) ? "Shift+" : "", (key_chord & ImGuiMod_Alt) ? "Alt+" : "", (key_chord & ImGuiMod_Super) ? (g.IO.ConfigMacOSXBehaviors ? "Cmd+" : "Super+") : "", GetKeyName((ImGuiKey)(key_chord & ~ImGuiMod_Mask_))); + return g.TempKeychordName; } // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) @@ -7890,6 +8333,7 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex) { routing_entry = &rt->Entries[old_routing_idx]; + routing_entry->RoutingCurrScore = routing_entry->RoutingNextScore; routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry routing_entry->RoutingNext = ImGuiKeyOwner_None; routing_entry->RoutingNextScore = 255; @@ -7898,11 +8342,15 @@ static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer // Apply routing to owner if there's no owner already (RoutingCurr == None at this point) + // This is the result of previous frame's SetShortcutRouting() call. if (routing_entry->Mods == g.IO.KeyMods) { - ImGuiKeyOwnerData* owner_data = ImGui::GetKeyOwnerData(key); + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_data->OwnerCurr == ImGuiKeyOwner_None) + { owner_data->OwnerCurr = routing_entry->RoutingCurr; + //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X) via Routing\n", GetKeyName(key), routing_entry->RoutingCurr); + } } } @@ -7932,13 +8380,11 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) ImGuiContext& g = *GImGui; ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; ImGuiKeyRoutingData* routing_data; - if (key_chord & ImGuiMod_Shortcut) - key_chord = ConvertShortcutMod(key_chord); ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); if (key == ImGuiKey_None) - key = ConvertSingleModFlagToKey(mods); - IM_ASSERT(IsNamedKey(key)); + key = ConvertSingleModFlagToKey(&g, mods); + IM_ASSERT(IsNamedKey(key) && (key_chord & ImGuiMod_Shortcut) == 0); // Please call ConvertShortcutMod() in calling function. // Get (in the majority of case, the linked list will have one element so this should be 2 reads. // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame). @@ -7967,12 +8413,11 @@ ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) // - 254: ImGuiInputFlags_RouteGlobalLow // - 255: never route // 'flags' should include an explicit routing policy -static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputFlags flags) +static int CalcRoutingScore(ImGuiID focus_scope_id, ImGuiID owner_id, ImGuiInputFlags flags) { if (flags & ImGuiInputFlags_RouteFocused) { ImGuiContext& g = *GImGui; - ImGuiWindow* focused = g.NavWindow; // ActiveID gets top priority // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) @@ -7985,16 +8430,13 @@ static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputF // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best) // Assuming only WindowA is submitting a routing request, // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. - if (focused != NULL && focused->RootWindow == location->RootWindow) - for (int next_score = 3; focused != NULL; next_score++) - { - if (focused == location) - { - IM_ASSERT(next_score < 255); - return next_score; - } - focused = (focused->RootWindow != focused) ? focused->ParentWindow : NULL; // FIXME: This could be later abstracted as a focus path - } + // This essentially follow the window->ParentWindowForFocusRoute chain. + if (focus_scope_id == 0) + return 255; + for (int index_in_focus_path = 0; index_in_focus_path < g.NavFocusRoute.Size; index_in_focus_path++) + if (g.NavFocusRoute.Data[index_in_focus_path].ID == focus_scope_id) + return 3 + index_in_focus_path; + return 255; } @@ -8006,13 +8448,29 @@ static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputF return 0; } +// We need this to filter some Shortcut() routes when an item e.g. an InputText() is active +// e.g. ImGuiKey_G won't be considered a shortcut when item is active, but ImGuiMod|ImGuiKey_G can be. +static bool IsKeyChordPotentiallyCharInput(ImGuiKeyChord key_chord) +{ + // Mimic 'ignore_char_inputs' logic in InputText() + ImGuiContext& g = *GImGui; + + // When the right mods are pressed it cannot be a char input so we won't filter the shortcut out. + ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); + const bool ignore_char_inputs = ((mods & ImGuiMod_Ctrl) && !(mods & ImGuiMod_Alt)) || (g.IO.ConfigMacOSXBehaviors && (mods & ImGuiMod_Super)); + if (ignore_char_inputs) + return false; + + // Return true for A-Z, 0-9 and other keys associated to char inputs. Other keys such as F1-F12 won't be filtered. + ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); + return g.KeysMayBeCharInput.TestBit(key); +} + // Request a desired route for an input chord (key + mods). // Return true if the route is available this frame. // - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state. // (Conceptually this does a "Submit for next frame" + "Test for current frame". // As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.) -// - Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default) -// - Using 'owner_id == ImGuiKeyOwner_None': allows disabling/locking a shortcut. bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; @@ -8020,37 +8478,80 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI flags |= ImGuiInputFlags_RouteGlobalHigh; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut() else IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used + IM_ASSERT(owner_id != ImGuiKeyOwner_Any && owner_id != ImGuiKeyOwner_None); + + // Convert ImGuiMod_Shortcut and add ImGuiMod_XXXX when a corresponding ImGuiKey_LeftXXX/ImGuiKey_RightXXX is specified. + key_chord = FixupKeyChord(&g, key_chord); + + // [DEBUG] Debug break requested by user + if (g.DebugBreakInShortcutRouting == key_chord) + IM_DEBUG_BREAK(); if (flags & ImGuiInputFlags_RouteUnlessBgFocused) if (g.NavWindow == NULL) return false; - if (flags & ImGuiInputFlags_RouteAlways) - return true; - const int score = CalcRoutingScore(g.CurrentWindow, owner_id, flags); + // Note how ImGuiInputFlags_RouteAlways won't set routing and thus won't set owner. May want to rework this? + if (flags & ImGuiInputFlags_RouteAlways) + { + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> always\n", GetKeyChordName(key_chord), owner_id, flags); + return true; + } + + // Specific culling when there's an active. + if (g.ActiveId != 0 && g.ActiveId != owner_id) + { + // Cull shortcuts with no modifiers when it could generate a character. + // e.g. Shortcut(ImGuiKey_G) also generates 'g' character, should not trigger when InputText() is active. + // but Shortcut(Ctrl+G) should generally trigger when InputText() is active. + // TL;DR: lettered shortcut with no mods or with only Alt mod will not trigger while an item reading text input is active. + // (We cannot filter based on io.InputQueueCharacters[] contents because of trickling and key<>chars submission order are undefined) + if ((flags & ImGuiInputFlags_RouteFocused) && g.IO.WantTextInput && IsKeyChordPotentiallyCharInput(key_chord)) + { + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> filtered as potential char input\n", GetKeyChordName(key_chord), owner_id, flags); + return false; + } + + // ActiveIdUsingAllKeyboardKeys trumps all for ActiveId + if ((flags & ImGuiInputFlags_RouteGlobalHigh) == 0 && g.ActiveIdUsingAllKeyboardKeys) + { + ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); + if (key == ImGuiKey_None) + key = ConvertSingleModFlagToKey(&g, (ImGuiKey)(key_chord & ImGuiMod_Mask_)); + if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) + return false; + } + } + + // FIXME-SHORTCUT: A way to configure the location/focus-scope to test would render this more flexible. + const int score = CalcRoutingScore(g.CurrentFocusScopeId, owner_id, flags); + IMGUI_DEBUG_LOG_INPUTROUTING("SetShortcutRouting(%s, owner_id=0x%08X, flags=%04X) -> score %d\n", GetKeyChordName(key_chord), owner_id, flags, score); if (score == 255) return false; // Submit routing for NEXT frame (assuming score is sufficient) // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <). ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); - const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore); if (score < routing_data->RoutingNextScore) { - routing_data->RoutingNext = routing_id; + routing_data->RoutingNext = owner_id; routing_data->RoutingNextScore = (ImU8)score; } // Return routing state for CURRENT frame - return routing_data->RoutingCurr == routing_id; + if (routing_data->RoutingCurr == owner_id) + IMGUI_DEBUG_LOG_INPUTROUTING("--> granting current route\n"); + return routing_data->RoutingCurr == owner_id; } // Currently unused by core (but used by tests) // Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading. bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id) { + ImGuiContext& g = *GImGui; const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); + key_chord = FixupKeyChord(&g, key_chord); ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry. return routing_data->RoutingCurr == routing_id; } @@ -8087,13 +8588,28 @@ bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) if (t < 0.0f) return false; IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function! + if (flags & (ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_)) // Setting any _RepeatXXX option enables _Repeat + flags |= ImGuiInputFlags_Repeat; bool pressed = (t == 0.0f); - if (!pressed && ((flags & ImGuiInputFlags_Repeat) != 0)) + if (!pressed && (flags & ImGuiInputFlags_Repeat) != 0) { float repeat_delay, repeat_rate; GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate); pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0; + if (pressed && (flags & ImGuiInputFlags_RepeatUntilMask_)) + { + // Slightly bias 'key_pressed_time' as DownDuration is an accumulation of DeltaTime which we compare to an absolute time value. + // Ideally we'd replace DownDuration with KeyPressedTime but it would break user's code. + ImGuiContext& g = *GImGui; + double key_pressed_time = g.Time - t + 0.00001f; + if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChange) && (g.LastKeyModsChangeTime > key_pressed_time)) + pressed = false; + if ((flags & ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone) && (g.LastKeyModsChangeFromNoneTime > key_pressed_time)) + pressed = false; + if ((flags & ImGuiInputFlags_RepeatUntilOtherKeyPress) && (g.LastKeyboardKeyPressTime > key_pressed_time)) + pressed = false; + } } if (!pressed) return false; @@ -8145,7 +8661,7 @@ bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInput const float t = g.IO.MouseDownDuration[button]; if (t < 0.0f) return false; - IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function! + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsMouseClicked) == 0); // Passing flags not supported by this function! // FIXME: Could support RepeatRate and RepeatUntil flags here. const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0; const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0); @@ -8179,6 +8695,13 @@ bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); } +bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id); +} + int ImGui::GetMouseClickedCount(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; @@ -8198,9 +8721,8 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c if (clip) rect_clipped.ClipWith(g.CurrentWindow->ClipRect); - // Expand for touch input - const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - if (!rect_for_touch.Contains(g.IO.MousePos)) + // Hit testing, expanded for touch input + if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding)) return false; return true; } @@ -8231,6 +8753,17 @@ ImVec2 ImGui::GetMousePos() return g.IO.MousePos; } +// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well. +// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend. +void ImGui::TeleportMousePos(const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + g.IO.MousePos = g.IO.MousePosPrev = pos; + g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + g.IO.WantSetMousePos = true; + //IMGUI_DEBUG_LOG_IO("TeleportMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); +} + // NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() { @@ -8366,7 +8899,9 @@ static void ImGui::UpdateKeyboardInputs() GetKeyData(ImGuiMod_Super)->Down = io.KeySuper; } } +#endif + // Import legacy ImGuiNavInput_ io inputs and convert to gamepad keys #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active) @@ -8389,7 +8924,6 @@ static void ImGui::UpdateKeyboardInputs() MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown); #undef NAV_MAP_KEY } -#endif #endif // Update aliases @@ -8398,15 +8932,20 @@ static void ImGui::UpdateKeyboardInputs() UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH); UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel); - // Synchronize io.KeyMods and io.KeyXXX values. + // Synchronize io.KeyMods and io.KeyCtrl/io.KeyShift/etc. values. // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array. // - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array. // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing. + const ImGuiKeyChord prev_key_mods = io.KeyMods; io.KeyMods = GetMergedModsFromKeys(); io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0; io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0; io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0; io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0; + if (prev_key_mods != io.KeyMods) + g.LastKeyModsChangeTime = g.Time; + if (prev_key_mods != io.KeyMods && prev_key_mods == 0) + g.LastKeyModsChangeFromNoneTime = g.Time; // Clear gamepad data if disabled if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) @@ -8422,6 +8961,14 @@ static void ImGui::UpdateKeyboardInputs() ImGuiKeyData* key_data = &io.KeysData[i]; key_data->DownDurationPrev = key_data->DownDuration; key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f; + if (key_data->DownDuration == 0.0f) + { + ImGuiKey key = (ImGuiKey)(ImGuiKey_KeysData_OFFSET + i); + if (IsKeyboardKey(key)) + g.LastKeyboardKeyPressTime = g.Time; + else if (key == ImGuiKey_ReservedForModCtrl || key == ImGuiKey_ReservedForModShift || key == ImGuiKey_ReservedForModAlt || key == ImGuiKey_ReservedForModSuper) + g.LastKeyboardKeyPressTime = g.Time; + } } // Update keys/input owner (named keys only): one entry per key @@ -8435,6 +8982,7 @@ static void ImGui::UpdateKeyboardInputs() owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore } + // Update key routing (for e.g. shortcuts) UpdateKeyRoutingTable(&g.KeysRoutingTable); } @@ -8443,9 +8991,16 @@ static void ImGui::UpdateMouseInputs() ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; + // Mouse Wheel swapping flag + // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead + // - We avoid doing it on OSX as it the OS input layer handles this already. + // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature. + // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source. + io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors; + // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) if (IsMousePosValid(&io.MousePos)) - io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); + io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos); // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) @@ -8453,11 +9008,17 @@ static void ImGui::UpdateMouseInputs() else io.MouseDelta = ImVec2(0.0f, 0.0f); + // Update stationary timer. + // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates. + const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework. + const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold); + g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f; + //IMGUI_DEBUG_LOG("%.4f\n", g.MouseStationaryTimer); + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; - io.MousePosPrev = io.MousePos; for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; @@ -8508,7 +9069,7 @@ static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount) g.WheelingWindowReleaseTimer = 0.0f; if (g.WheelingWindow == window) return; - IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL"); + IMGUI_DEBUG_LOG_IO("[io] LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL"); g.WheelingWindow = window; g.WheelingWindowRefMousePos = g.IO.MousePos; if (window == NULL) @@ -8594,8 +9155,8 @@ void ImGui::UpdateMouseWheel() { const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; SetWindowPos(window, window->Pos + offset, 0); - window->Size = ImFloor(window->Size * scale); - window->SizeFull = ImFloor(window->SizeFull * scale); + window->Size = ImTrunc(window->Size * scale); + window->SizeFull = ImTrunc(window->SizeFull * scale); } return; } @@ -8603,15 +9164,9 @@ void ImGui::UpdateMouseWheel() return; // Mouse wheel scrolling - // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead - // - We avoid doing it on OSX as it the OS input layer handles this already. - // - However this means when running on OSX over Emcripten, Shift+WheelY will incur two swappings (1 in OS, 1 here), cancelling the feature. - const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; - if (swap_axis) - { - wheel.x = wheel.y; - wheel.y = 0.0f; - } + // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs() + if (g.IO.MouseWheelRequestAxisSwap) + wheel = ImVec2(wheel.y, 0.0f); // Maintain a rough average of moving magnitude on both axises // FIXME: should by based on wall clock time rather than frame-counter @@ -8637,15 +9192,17 @@ void ImGui::UpdateMouseWheel() { LockWheelingWindow(window, wheel.x); float max_step = window->InnerRect.GetWidth() * 0.67f; - float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); + float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step)); SetScrollX(window, window->Scroll.x - wheel.x * scroll_step); + g.WheelingWindowScrolledFrame = g.FrameCount; } if (do_scroll[ImGuiAxis_Y]) { LockWheelingWindow(window, wheel.y); float max_step = window->InnerRect.GetHeight() * 0.67f; - float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); + float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step)); SetScrollY(window, window->Scroll.y - wheel.y * scroll_step); + g.WheelingWindowScrolledFrame = g.FrameCount; } } } @@ -8665,19 +9222,25 @@ void ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse) #ifndef IMGUI_DISABLE_DEBUG_TOOLS static const char* GetInputSourceName(ImGuiInputSource source) { - const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Nav", "Clipboard" }; + const char* input_source_names[] = { "None", "Mouse", "Keyboard", "Gamepad", "Clipboard" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); return input_source_names[source]; } +static const char* GetMouseSourceName(ImGuiMouseSource source) +{ + const char* mouse_source_names[] = { "Mouse", "TouchScreen", "Pen" }; + IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT); + return mouse_source_names[source]; +} static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) { ImGuiContext& g = *GImGui; - if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("%s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f, %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } - if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.3f, %.3f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } - if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } - if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } + if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; } + if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; } + if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; } + if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } + if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } + if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } } #endif @@ -8705,11 +9268,14 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; if (e->Type == ImGuiInputEventType_MousePos) { + if (g.IO.WantSetMousePos) + continue; // Trickling Rule: Stop processing queued events if we already handled a mouse button change ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY); if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted)) break; io.MousePos = event_pos; + io.MouseSource = e->MousePos.MouseSource; mouse_moved = true; } else if (e->Type == ImGuiInputEventType_MouseButton) @@ -8719,7 +9285,10 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) break; + if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover. + break; io.MouseDown[button] = e->MouseButton.Down; + io.MouseSource = e->MouseButton.MouseSource; mouse_button_changed |= (1 << button); } else if (e->Type == ImGuiInputEventType_MouseWheel) @@ -8729,6 +9298,7 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) break; io.MouseWheelH += e->MouseWheel.WheelX; io.MouseWheel += e->MouseWheel.WheelY; + io.MouseSource = e->MouseWheel.MouseSource; mouse_wheeled = true; } else if (e->Type == ImGuiInputEventType_Key) @@ -8807,7 +9377,7 @@ ImGuiID ImGui::GetKeyOwner(ImGuiKey key) return ImGuiKeyOwner_None; ImGuiContext& g = *GImGui; - ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); ImGuiID owner_id = owner_data->OwnerCurr; if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any) @@ -8831,7 +9401,7 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) return false; - ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_id == ImGuiKeyOwner_Any) return (owner_data->LockThisFrame == false); @@ -8856,10 +9426,12 @@ bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) // - SetKeyOwner(..., Any or None, Lock) : set lock void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) { + ImGuiContext& g = *GImGui; IM_ASSERT(IsNamedKeyOrModKey(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it) IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function! + //IMGUI_DEBUG_LOG("SetKeyOwner(%s, owner_id=0x%08X, flags=%08X)\n", GetKeyName(key), owner_id, flags); - ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); owner_data->OwnerCurr = owner_data->OwnerNext = owner_id; // We cannot lock by default as it would likely break lots of legacy code. @@ -8868,6 +9440,17 @@ void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease); } +// Rarely used helper +void ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +{ + if (key_chord & ImGuiMod_Ctrl) { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); } + if (key_chord & ImGuiMod_Shift) { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); } + if (key_chord & ImGuiMod_Alt) { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); } + if (key_chord & ImGuiMod_Super) { SetKeyOwner(ImGuiMod_Super, owner_id, flags); } + if (key_chord & ImGuiMod_Shortcut) { SetKeyOwner(ImGuiMod_Shortcut, owner_id, flags); } + if (key_chord & ~ImGuiMod_Mask_) { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); } +} + // This is more or less equivalent to: // if (IsItemHovered() || IsItemActive()) // SetKeyOwner(key, GetItemID()); @@ -8889,18 +9472,17 @@ void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) } } -bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +// This is the only public API until we expose owner_id versions of the API as replacements. +bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord) +{ + return IsKeyChordPressed(key_chord, 0, ImGuiInputFlags_None); +} + +// This is equivalent to comparing KeyMods + doing a IsKeyPressed() +bool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; - - // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any. - if ((flags & ImGuiInputFlags_RouteMask_) == 0) - flags |= ImGuiInputFlags_RouteFocused; - if (!SetShortcutRouting(key_chord, owner_id, flags)) - return false; - - if (key_chord & ImGuiMod_Shortcut) - key_chord = ConvertShortcutMod(key_chord); + key_chord = FixupKeyChord(&g, key_chord); ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); if (g.IO.KeyMods != mods) return false; @@ -8908,12 +9490,45 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags // Special storage location for mods ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); if (key == ImGuiKey_None) - key = ConvertSingleModFlagToKey(mods); + key = ConvertSingleModFlagToKey(&g, mods); + if (!IsKeyPressed(key, owner_id, (flags & ImGuiInputFlags_RepeatMask_))) + return false; + return true; +} - if (!IsKeyPressed(key, owner_id, (flags & (ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateMask_)))) +void ImGui::SetNextItemShortcut(ImGuiKeyChord key_chord) +{ + ImGuiContext& g = *GImGui; + g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasShortcut; + g.NextItemData.Shortcut = key_chord; +} + +bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +{ + //ImGuiContext& g = *GImGui; + //IMGUI_DEBUG_LOG("Shortcut(%s, owner_id=0x%08X, flags=%X)\n", GetKeyChordName(key_chord, g.TempBuffer.Data, g.TempBuffer.Size), owner_id, flags); + + // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any. + if ((flags & ImGuiInputFlags_RouteMask_) == 0) + flags |= ImGuiInputFlags_RouteFocused; + + // Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default) + // Effectively makes Shortcut() always input-owner aware. + if (owner_id == ImGuiKeyOwner_Any || owner_id == ImGuiKeyOwner_None) + owner_id = GetRoutingIdFromOwnerId(owner_id); + + // Submit route + if (!SetShortcutRouting(key_chord, owner_id, flags)) + return false; + + // Default repeat behavior for Shortcut() + // So e.g. pressing Ctrl+W and releasing Ctrl while holding W will not trigger the W shortcut. + if ((flags & ImGuiInputFlags_Repeat) != 0 && (flags & ImGuiInputFlags_RepeatUntilMask_) == 0) + flags |= ImGuiInputFlags_RepeatUntilKeyModsChange; + + if (!IsKeyChordPressed(key_chord, owner_id, flags)) return false; IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function! - return true; } @@ -9143,6 +9758,11 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); PopStyleVar(); } + while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044 + { + if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'", window->Name); + PopFont(); + } while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); @@ -9189,12 +9809,135 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx) IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!"); } +//----------------------------------------------------------------------------- +// [SECTION] ITEM SUBMISSION +//----------------------------------------------------------------------------- +// - KeepAliveID() +// - ItemHandleShortcut() [Internal] +// - ItemAdd() +//----------------------------------------------------------------------------- + +// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID(). +void ImGui::KeepAliveID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) + g.ActiveIdIsAlive = id; + if (g.ActiveIdPreviousFrame == id) + g.ActiveIdPreviousFrameIsAlive = true; +} + +static void ItemHandleShortcut(ImGuiID id) +{ + // FIXME: Generalize Activation queue? + ImGuiContext& g = *GImGui; + if (ImGui::Shortcut(g.NextItemData.Shortcut, id, ImGuiInputFlags_None) && g.NavActivateId == 0) + { + g.NavActivateId = id; // Will effectively disable clipping. + g.NavActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_FromShortcut; + if (g.ActiveId == 0 || g.ActiveId == id) + g.NavActivateDownId = g.NavActivatePressedId = id; + ImGui::NavHighlightActivated(id); + } +} + +// Declare item bounding box for clipping and interaction. +// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface +// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. +// THIS IS IN THE PERFORMANCE CRITICAL PATH (UNTIL THE CLIPPING TEST AND EARLY-RETURN) +bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + // Set item data + // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set) + g.LastItemData.ID = id; + g.LastItemData.Rect = bb; + g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; + g.LastItemData.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags; + g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; + // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared. + + if (id != 0) + { + KeepAliveID(id); + + // Directional navigation processing + // Runs prior to clipping early-out + // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget + // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests + // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of + // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. + // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able + // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). + // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. + // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. + if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav)) + { + // FIMXE-NAV: investigate changing the window tests into a simple 'if (g.NavFocusScopeId == g.CurrentFocusScopeId)' test. + window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + if (g.NavId == id || g.NavAnyRequest) + if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) + if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) + NavProcessItem(); + } + + if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasShortcut) + ItemHandleShortcut(id); + } + + // Lightweight clear of SetNextItemXXX data. + g.NextItemData.Flags = ImGuiNextItemDataFlags_None; + g.NextItemData.ItemFlags = ImGuiItemFlags_None; + +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (id != 0) + IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData); +#endif + + // Clipping test + // (this is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value) + //const bool is_clipped = IsClippedEx(bb, id); + //if (is_clipped) + // return false; + // g.NavActivateId is not necessarily == g.NavId, in the case of remote activation (e.g. shortcuts) + const bool is_rect_visible = bb.Overlaps(window->ClipRect); + if (!is_rect_visible) + if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId && id != g.NavActivateId)) + if (!g.LogEnabled) + return false; + + // [DEBUG] +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (id != 0) + { + if (id == g.DebugLocateId) + DebugLocateItemResolveWithLastItem(); + + // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". + // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". + // READ THE FAQ: https://dearimgui.com/faq + IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); + } + //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] + //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0) + // window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG] +#endif + + // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) + if (is_rect_visible) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible; + if (IsMouseHoveringRect(bb.Min, bb.Max)) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; + return true; +} + //----------------------------------------------------------------------------- // [SECTION] LAYOUT //----------------------------------------------------------------------------- // - ItemSize() -// - ItemAdd() // - SameLine() // - GetCursorScreenPos() // - SetCursorScreenPos() @@ -9225,6 +9968,7 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx) // Advance cursor given item size for layout. // Register minimum needed size so it can extend the bounding box used for auto-fit calculation. // See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different. +// THIS IS IN THE PERFORMANCE CRITICAL PATH. void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) { ImGuiContext& g = *GImGui; @@ -9244,8 +9988,8 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG] window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; window->DC.CursorPosPrevLine.y = line_y1; - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line - window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line + window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] @@ -9261,87 +10005,10 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) SameLine(); } -// Declare item bounding box for clipping and interaction. -// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface -// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction. -bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Set item data - // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set) - g.LastItemData.ID = id; - g.LastItemData.Rect = bb; - g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb; - g.LastItemData.InFlags = g.CurrentItemFlags | extra_flags; - g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None; - - // Directional navigation processing - if (id != 0) - { - KeepAliveID(id); - - // Runs prior to clipping early-out - // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget - // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests - // unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of - // thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. - // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able - // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). - // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. - // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. - if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav)) - { - window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); - if (g.NavId == id || g.NavAnyRequest) - if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) - if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(); - } - - // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". - // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". - // READ THE FAQ: https://dearimgui.org/faq - IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); - } - g.NextItemData.Flags = ImGuiNextItemDataFlags_None; - -#ifdef IMGUI_ENABLE_TEST_ENGINE - if (id != 0) - IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData); -#endif - - // Clipping test - // (FIXME: This is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value) - //const bool is_clipped = IsClippedEx(bb, id); - //if (is_clipped) - // return false; - const bool is_rect_visible = bb.Overlaps(window->ClipRect); - if (!is_rect_visible) - if (id == 0 || (id != g.ActiveId && id != g.NavId)) - if (!g.LogEnabled) - return false; - - // [DEBUG] -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - if (id != 0 && id == g.DebugLocateId) - DebugLocateItemResolveWithLastItem(); -#endif - //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] - - // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) - if (is_rect_visible) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible; - if (IsMouseHoveringRect(bb.Min, bb.Max)) - g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; - return true; -} - // Gets back to previous line and continue with horizontal layout // offset_from_start_x == 0 : follow right after previous item // offset_from_start_x != 0 : align to specified x position (relative to window/group left) -// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 +// spacing_w < 0 : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0 // spacing_w >= 0 : enforce spacing amount void ImGui::SameLine(float offset_from_start_x, float spacing_w) { @@ -9375,9 +10042,6 @@ ImVec2 ImGui::GetCursorScreenPos() return window->DC.CursorPos; } -// 2022/08/05: Setting cursor position also extend boundaries (via modifying CursorMaxPos) used to compute window size, group size etc. -// I believe this was is a judicious choice but it's probably being relied upon (it has been the case since 1.31 and 1.50) -// It would be sane if we requested user to use SetCursorPos() + Dummy(ImVec2(0,0)) to extend CursorMaxPos... void ImGui::SetCursorScreenPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); @@ -9474,14 +10138,18 @@ void ImGui::PushMultiItemsWidths(int components, float w_full) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(components > 0); const ImGuiStyle& style = g.Style; - const float w_item_one = ImMax(1.0f, IM_FLOOR((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); - const float w_item_last = ImMax(1.0f, IM_FLOOR(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width - window->DC.ItemWidthStack.push_back(w_item_last); - for (int i = 0; i < components - 2; i++) - window->DC.ItemWidthStack.push_back(w_item_one); - window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one; + float w_items = w_full - style.ItemInnerSpacing.x * (components - 1); + float prev_split = w_items; + for (int i = components - 1; i > 0; i--) + { + float next_split = IM_TRUNC(w_items * i / components); + window->DC.ItemWidthStack.push_back(ImMax(prev_split - next_split, 1.0f)); + prev_split = next_split; + } + window->DC.ItemWidth = ImMax(prev_split, 1.0f); g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth; } @@ -9508,7 +10176,7 @@ float ImGui::CalcItemWidth() float region_max_x = GetContentRegionMaxAbs().x; w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w); } - w = IM_FLOOR(w); + w = IM_TRUNC(w); return w; } @@ -9569,10 +10237,8 @@ ImVec2 ImGui::GetContentRegionMax() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = window->ContentRegionRect.Max - window->Pos; - if (window->DC.CurrentColumns || g.CurrentTable) - mx.x = window->WorkRect.Max.x - window->Pos.x; - return mx; + ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; + return mx - window->Pos; } // [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features. @@ -9580,9 +10246,7 @@ ImVec2 ImGui::GetContentRegionMaxAbs() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImVec2 mx = window->ContentRegionRect.Max; - if (window->DC.CurrentColumns || g.CurrentTable) - mx.x = window->WorkRect.Max.x; + ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max; return mx; } @@ -9617,6 +10281,7 @@ void ImGui::BeginGroup() ImGuiGroupData& group_data = g.GroupStack.back(); group_data.WindowID = window->ID; group_data.BackupCursorPos = window->DC.CursorPos; + group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine; group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; group_data.BackupIndent = window->DC.Indent; group_data.BackupGroupOffset = window->DC.GroupOffset; @@ -9624,6 +10289,7 @@ void ImGui::BeginGroup() group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset; group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; group_data.BackupHoveredIdIsAlive = g.HoveredId != 0; + group_data.BackupIsSameLine = window->DC.IsSameLine; group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; group_data.EmitItem = true; @@ -9650,11 +10316,13 @@ void ImGui::EndGroup() ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); window->DC.CursorPos = group_data.BackupCursorPos; + window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine; window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); window->DC.Indent = group_data.BackupIndent; window->DC.GroupOffset = group_data.BackupGroupOffset; window->DC.CurrLineSize = group_data.BackupCurrLineSize; window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset; + window->DC.IsSameLine = group_data.BackupIsSameLine; if (g.LogEnabled) g.LogLinePosY = -FLT_MAX; // To enforce a carriage return @@ -9695,7 +10363,8 @@ void ImGui::EndGroup() g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated; g.GroupStack.pop_back(); - //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] + if (g.DebugShowGroupRects) + window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] } @@ -9734,7 +10403,7 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) } scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]); } - scroll[axis] = IM_FLOOR(ImMax(scroll[axis], 0.0f)); + scroll[axis] = IM_ROUND(ImMax(scroll[axis], 0.0f)); if (!window->Collapsed && !window->SkipItems) scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]); } @@ -9789,7 +10458,7 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) { if (can_be_fully_visible_x) - SetScrollFromPosX(window, ImFloor((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f); + SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f); else SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f); } @@ -9804,7 +10473,7 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) { if (can_be_fully_visible_y) - SetScrollFromPosY(window, ImFloor((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f); + SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f); else SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f); } @@ -9889,7 +10558,7 @@ void ImGui::SetScrollY(float scroll_y) void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) { IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); - window->ScrollTarget.x = IM_FLOOR(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset + window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset window->ScrollTargetCenterRatio.x = center_x_ratio; window->ScrollTargetEdgeSnapDist.x = 0.0f; } @@ -9897,7 +10566,7 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - window->ScrollTarget.y = IM_FLOOR(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset + window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; window->ScrollTargetEdgeSnapDist.y = 0.0f; } @@ -9949,31 +10618,40 @@ bool ImGui::BeginTooltip() return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } +bool ImGui::BeginItemTooltip() +{ + if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + return false; + return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); +} + bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; if (g.DragDropWithinSource || g.DragDropWithinTarget) { - // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) - // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. - // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. + // Drag and Drop tooltips are positioning differently than other tooltips: + // - offset visibility to increase visibility around mouse. + // - never clamp within outer viewport boundary. + // We call SetNextWindowPos() to enforce position and disable clamping. + // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones). //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; - ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); + ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale; SetNextWindowPos(tooltip_pos); SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( - tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip; + tooltip_flags |= ImGuiTooltipFlags_OverridePrevious; } char window_name[16]; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); - if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip) + if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious) if (ImGuiWindow* window = FindWindowByName(window_name)) if (window->Active) { // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. - SetWindowHiddendAndSkipItemsForCurrentFrame(window); + SetWindowHiddenAndSkipItemsForCurrentFrame(window); ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); } ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; @@ -9992,14 +10670,6 @@ void ImGui::EndTooltip() End(); } -void ImGui::SetTooltipV(const char* fmt, va_list args) -{ - if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None)) - return; - TextV(fmt, args); - EndTooltip(); -} - void ImGui::SetTooltip(const char* fmt, ...) { va_list args; @@ -10008,6 +10678,32 @@ void ImGui::SetTooltip(const char* fmt, ...) va_end(args); } +void ImGui::SetTooltipV(const char* fmt, va_list args) +{ + if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) + return; + TextV(fmt, args); + EndTooltip(); +} + +// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'. +// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse. +void ImGui::SetItemTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + SetTooltipV(fmt, args); + va_end(args); +} + +void ImGui::SetItemTooltipV(const char* fmt, va_list args) +{ + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + SetTooltipV(fmt, args); +} + + //----------------------------------------------------------------------------- // [SECTION] POPUPS //----------------------------------------------------------------------------- @@ -10053,6 +10749,7 @@ bool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags) return IsPopupOpen(id, popup_flags); } +// Also see FindBlockingModal(NULL) ImGuiWindow* ImGui::GetTopMostPopupModal() { ImGuiContext& g = *GImGui; @@ -10063,6 +10760,7 @@ ImGuiWindow* ImGui::GetTopMostPopupModal() return NULL; } +// See Demo->Stacked Modal to confirm what this is for. ImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal() { ImGuiContext& g = *GImGui; @@ -10116,17 +10814,23 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) } else { - // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui - // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing - // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. - if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) + // Gently handle the user mistakenly calling OpenPopup() every frames: it is likely a programming mistake! + // However, if we were to run the regular code path, the ui would become completely unusable because the popup will always be + // in hidden-while-calculating-size state _while_ claiming focus. Which is extremely confusing situation for the programmer. + // Instead, for successive frames calls to OpenPopup(), we silently avoid reopening even if ImGuiPopupFlags_NoReopen is not specified. + bool keep_existing = false; + if (g.OpenPopupStack[current_stack_size].PopupId == id) + if ((g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) || (popup_flags & ImGuiPopupFlags_NoReopen)) + keep_existing = true; + if (keep_existing) { + // No reopen g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; } else { - // Close child popups if any, then flag popup for open/reopen - ClosePopupToLevel(current_stack_size, false); + // Reopen: close child popups if any, then flag popup for open/reopen (set position, focus, init navigation) + ClosePopupToLevel(current_stack_size, true); g.OpenPopupStack.push_back(popup_ref); } @@ -10156,14 +10860,15 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to if (!popup.Window) continue; IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); - if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) - continue; // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow) - // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3: - // Window -> Popup1 -> Popup2 -> Popup3 + // - Clicking/Focusing Window2 won't close Popup1: + // Window -> Popup1 -> Window2(Ref) + // - Clicking/focusing Popup1 will close Popup2 and Popup3: + // Window -> Popup1(Ref) -> Popup2 -> Popup3 // - Each popups may contain child windows, which is why we compare ->RootWindow! // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child + // We step through every popup from bottom to top to validate their position relative to reference window. bool ref_window_is_descendent_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) @@ -10191,7 +10896,7 @@ void ImGui::ClosePopupsExceptModals() for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--) { ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window; - if (!window || window->Flags & ImGuiWindowFlags_Modal) + if (!window || (window->Flags & ImGuiWindowFlags_Modal)) break; } if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below @@ -10213,16 +10918,9 @@ void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_ { ImGuiWindow* focus_window = (popup_window && popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : popup_backup_nav_window; if (focus_window && !focus_window->WasActive && popup_window) - { - // Fallback - FocusTopMostWindowUnderOne(popup_window, NULL); - } + FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback else - { - if (g.NavLayer == ImGuiNavLayer_Main && focus_window) - focus_window = NavRestoreLastChildNavWindow(focus_window); - FocusWindow(focus_window); - } + FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None); } } @@ -10269,7 +10967,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) char name[20]; if (flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuCount); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.BeginMenuDepth); // Recycle windows based on depth else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame @@ -10278,6 +10976,8 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); + //g.CurrentWindow->FocusRouteParentWindow = g.CurrentWindow->ParentWindowInBeginStack; + return is_open; } @@ -10295,7 +10995,9 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) } // If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup. -// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup) so the actual value of *p_open is meaningless here. +// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup). +// - *p_open set back to false in BeginPopupModal() when popup is not open. +// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup. bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; @@ -10304,6 +11006,8 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla if (!IsPopupOpen(id, ImGuiPopupFlags_None)) { g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values + if (p_open && *p_open) + *p_open = false; return false; } @@ -10535,15 +11239,20 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } if (window->Flags & ImGuiWindowFlags_Tooltip) { - // Position tooltip (always follows mouse) - float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = NavCalcPreferredRefPos(); + // Position tooltip (always follows mouse + clamp within outer boundaries) + // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position. + // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin() + IM_ASSERT(g.CurrentWindow == window); + const float scale = g.Style.MouseCursorScale; + const ImVec2 ref_pos = NavCalcPreferredRefPos(); + const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale; ImRect r_avoid; if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); + r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. + //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255)); + return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); } IM_ASSERT(0); return window->Pos; @@ -10564,11 +11273,25 @@ void ImGui::SetNavWindow(ImGuiWindow* window) { IMGUI_DEBUG_LOG_FOCUS("[focus] SetNavWindow(\"%s\")\n", window ? window->Name : ""); g.NavWindow = window; + g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; } g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false; NavUpdateAnyRequestFlag(); } +void ImGui::NavHighlightActivated(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.NavHighlightActivatedId = id; + g.NavHighlightActivatedTimer = NAV_ACTIVATE_HIGHLIGHT_TIMER; +} + +void ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis) +{ + ImGuiContext& g = *GImGui; + g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX; +} + void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel) { ImGuiContext& g = *GImGui; @@ -10576,9 +11299,13 @@ void ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu); g.NavId = id; g.NavLayer = nav_layer; - g.NavFocusScopeId = focus_scope_id; + SetNavFocusScope(focus_scope_id); g.NavWindow->NavLastIds[nav_layer] = id; g.NavWindow->NavRectRel[nav_layer] = rect_rel; + + // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it) + NavClearPreferredPosForAxis(ImGuiAxis_X); + NavClearPreferredPosForAxis(ImGuiAxis_Y); } void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) @@ -10594,47 +11321,37 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; g.NavId = id; g.NavLayer = nav_layer; - g.NavFocusScopeId = g.CurrentFocusScopeId; + SetNavFocusScope(g.CurrentFocusScopeId); window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); - if (g.ActiveIdSource == ImGuiInputSource_Nav) + if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) g.NavDisableMouseHover = true; else g.NavDisableHighlight = true; + + // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it) + NavClearPreferredPosForAxis(ImGuiAxis_X); + NavClearPreferredPosForAxis(ImGuiAxis_Y); } -ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) +static ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy) { if (ImFabs(dx) > ImFabs(dy)) return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; } -static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) +static float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max) { - if (a1 < b0) - return a1 - b0; - if (b1 < a0) - return a0 - b1; + if (cand_max < curr_min) + return cand_max - curr_min; + if (curr_max < cand_min) + return cand_min - curr_max; return 0.0f; } -static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) -{ - if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) - { - r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); - r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); - } - else // FIXME: PageUp/PageDown are leaving move_dir == None - { - r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); - r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); - } -} - // Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057 static bool ImGui::NavScoreItem(ImGuiNavItemData* result) { @@ -10657,10 +11374,6 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window } - // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) - // For example, this ensures that items in one column are not reached when moving vertically from items in another column. - NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); - // Compute distance between boxes // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); @@ -10699,32 +11412,41 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; } + const ImGuiDir move_dir = g.NavMoveDir; #if IMGUI_DEBUG_NAV_SCORING - char buf[128]; - if (IsMouseHoveringRect(cand.Min, cand.Max)) + char buf[200]; + if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate. { - ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - ImDrawList* draw_list = GetForegroundDrawList(window); - draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); - draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); - draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40,0,0,150)); - draw_list->AddText(cand.Max, ~0U, buf); - } - else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. - { - if (quadrant == g.NavMoveDir) + if (quadrant == move_dir) { ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); ImDrawList* draw_list = GetForegroundDrawList(window); - draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); + draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80)); + draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200)); draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf); } } + const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max); + const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space)); + if (debug_hovering || debug_tty) + { + ImFormatString(buf, IM_ARRAYSIZE(buf), + "d-box (%7.3f,%7.3f) -> %7.3f\nd-center (%7.3f,%7.3f) -> %7.3f\nd-axial (%7.3f,%7.3f) -> %7.3f\nnav %c, quadrant %c", + dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "-WENS"[move_dir+1], "-WENS"[quadrant+1]); + if (debug_hovering) + { + ImDrawList* draw_list = GetForegroundDrawList(window); + draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100)); + draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200)); + draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200)); + draw_list->AddText(cand.Max, ~0U, buf); + } + if (debug_tty) { IMGUI_DEBUG_LOG_NAV("id 0x%08X\n%s\n", g.LastItemData.ID, buf); } + } #endif // Is it in the quadrant we're interested in moving to? bool new_best = false; - const ImGuiDir move_dir = g.NavMoveDir; if (quadrant == move_dir) { // Does it beat the current best candidate? @@ -10778,27 +11500,47 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) result->FocusScopeId = g.CurrentFocusScopeId; result->InFlags = g.LastItemData.InFlags; result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); + if (result->InFlags & ImGuiItemFlags_HasSelectionUserData) + { + IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid); + result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData. + } +} + +// True when current work location may be scrolled horizontally when moving left / right. +// This is generally always true UNLESS within a column. We don't have a vertical equivalent. +void ImGui::NavUpdateCurrentWindowIsScrollPushableX() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL); } // We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -// This is called after LastItemData is set. +// This is called after LastItemData is set, but NextItemData is also still valid. static void ImGui::NavProcessItem() { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; const ImGuiID id = g.LastItemData.ID; - const ImRect nav_bb = g.LastItemData.NavRect; const ImGuiItemFlags item_flags = g.LastItemData.InFlags; + // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221) + if (window->DC.NavIsScrollPushableX == false) + { + g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x); + g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x); + } + const ImRect nav_bb = g.LastItemData.NavRect; + // Process Init Request if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0) { // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0; - if (candidate_for_nav_default_focus || g.NavInitResultId == 0) + if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0) { - g.NavInitResultId = id; - g.NavInitResultRectRel = WindowRectAbsToRel(window, nav_bb); + NavApplyItemToResult(&g.NavInitResult); } if (candidate_for_nav_default_focus) { @@ -10811,35 +11553,44 @@ static void ImGui::NavProcessItem() // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy) if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0) { - const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) != 0; - if (is_tabbing) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi) || (window->Flags & ImGuiWindowFlags_NoNavInputs) == 0) { - NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags); - } - else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) - { - ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - if (NavScoreItem(result)) - NavApplyItemToResult(result); + const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0; + if (is_tabbing) + { + NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags); + } + else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) + { + ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; + if (NavScoreItem(result)) + NavApplyItemToResult(result); - // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisible)) - NavApplyItemToResult(&g.NavMoveResultLocalVisible); + // Features like PageUp/PageDown need to maintain a separate score for the visible set of items. + const float VISIBLE_RATIO = 0.70f; + if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) + if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) + if (NavScoreItem(&g.NavMoveResultLocalVisible)) + NavApplyItemToResult(&g.NavMoveResultLocalVisible); + } } } - // Update window-relative bounding box of navigated item + // Update information for currently focused/navigated item if (g.NavId == id) { if (g.NavWindow != window) SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window. g.NavLayer = window->DC.NavLayerCurrent; + SetNavFocusScope(g.CurrentFocusScopeId); // Will set g.NavFocusScopeId AND store g.NavFocusScopePath g.NavFocusScopeId = g.CurrentFocusScopeId; g.NavIdIsAlive = true; - window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) + if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData) + { + IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid); + g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData. + } + window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) } } @@ -10855,8 +11606,12 @@ void ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flag ImGuiContext& g = *GImGui; if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0) + { if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent) return; + if (g.NavFocusScopeId != g.CurrentFocusScopeId) + return; + } // - Can always land on an item when using API call. // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item. @@ -10917,7 +11672,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM ImGuiContext& g = *GImGui; IM_ASSERT(g.NavWindow != NULL); - if (move_flags & ImGuiNavMoveFlags_Tabbing) + if (move_flags & ImGuiNavMoveFlags_IsTabbing) move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId; g.NavMoveSubmitted = g.NavMoveScoringItems = true; @@ -10927,7 +11682,7 @@ void ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavM g.NavMoveFlags = move_flags; g.NavMoveScrollFlags = scroll_flags; g.NavMoveForwardToNextFrame = false; - g.NavMoveKeyMods = g.IO.KeyMods; + g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods; g.NavMoveResultLocal.Clear(); g.NavMoveResultLocalVisible.Clear(); g.NavMoveResultOther.Clear(); @@ -10944,6 +11699,19 @@ void ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result) NavUpdateAnyRequestFlag(); } +// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere +void ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data) +{ + ImGuiContext& g = *GImGui; + g.NavMoveScoringItems = false; + g.LastItemData.ID = tree_node_data->ID; + g.LastItemData.InFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper). + g.LastItemData.NavRect = tree_node_data->NavRect; + NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult() + NavClearPreferredPosForAxis(ImGuiAxis_Y); + NavUpdateAnyRequestFlag(); +} + void ImGui::NavMoveRequestCancel() { ImGuiContext& g = *GImGui; @@ -10969,10 +11737,12 @@ void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNav void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags) { ImGuiContext& g = *GImGui; - IM_ASSERT(wrap_flags != 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY - // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it, NavEndFrame() will do the same test + IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY + + // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it: + // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest(). if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main) - g.NavMoveFlags |= wrap_flags; + g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags; } // FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0). @@ -11002,6 +11772,7 @@ void ImGui::NavRestoreLayer(ImGuiNavLayer layer) { ImGuiWindow* prev_nav_window = g.NavWindow; g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); // FIXME-NAV: Should clear ongoing nav requests? + g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; if (prev_nav_window) IMGUI_DEBUG_LOG_FOCUS("[focus] NavRestoreLayer: from \"%s\" to SetNavWindow(\"%s\")\n", prev_nav_window->Name, g.NavWindow->Name); } @@ -11041,7 +11812,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) if (window->Flags & ImGuiWindowFlags_NoNavInputs) { g.NavId = 0; - g.NavFocusScopeId = window->NavRootFocusScopeId; + SetNavFocusScope(window->NavRootFocusScopeId); return; } @@ -11054,14 +11825,13 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect()); g.NavInitRequest = true; g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavInitResultRectRel = ImRect(); + g.NavInitResult.ID = 0; NavUpdateAnyRequestFlag(); } else { g.NavId = window->NavLastIds[0]; - g.NavFocusScopeId = window->NavRootFocusScopeId; + SetNavFocusScope(window->NavRootFocusScopeId); } } @@ -11089,7 +11859,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() } ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); ImGuiViewport* viewport = GetMainViewport(); - return ImFloor(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImFloor() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. + return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } } @@ -11140,12 +11910,12 @@ static void ImGui::NavUpdate() g.NavInputSource = ImGuiInputSource_Keyboard; // Process navigation init request (select first/default focus) - if (g.NavInitResultId != 0) + g.NavJustMovedToId = 0; + if (g.NavInitResult.ID != 0) NavInitRequestApplyResult(); g.NavInitRequest = false; g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavJustMovedToId = 0; + g.NavInitResult.ID = 0; // Process navigation move request if (g.NavMoveSubmitted) @@ -11182,10 +11952,10 @@ static void ImGui::NavUpdate() g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate)); - const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false))); - const bool input_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Enter)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput)); - const bool input_pressed = input_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Enter, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, false))); + const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space, ImGuiKeyOwner_None)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_None)); + const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, ImGuiKeyOwner_None)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, ImGuiKeyOwner_None))); + const bool input_down = (nav_keyboard_active && (IsKeyDown(ImGuiKey_Enter, ImGuiKeyOwner_None) || IsKeyDown(ImGuiKey_KeypadEnter, ImGuiKeyOwner_None))) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_None)); + const bool input_pressed = input_down && ((nav_keyboard_active && (IsKeyPressed(ImGuiKey_Enter, ImGuiKeyOwner_None) || IsKeyPressed(ImGuiKey_KeypadEnter, ImGuiKeyOwner_None))) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, ImGuiKeyOwner_None))); if (g.ActiveId == 0 && activate_pressed) { g.NavActivateId = g.NavId; @@ -11199,13 +11969,22 @@ static void ImGui::NavUpdate() if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down)) g.NavActivateDownId = g.NavId; if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed)) + { g.NavActivatePressedId = g.NavId; + NavHighlightActivated(g.NavId); + } } if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) g.NavDisableHighlight = true; if (g.NavActivateId != 0) IM_ASSERT(g.NavActivateDownId == g.NavActivateId); + // Highlight + if (g.NavHighlightActivatedTimer > 0.0f) + g.NavHighlightActivatedTimer = ImMax(0.0f, g.NavHighlightActivatedTimer - io.DeltaTime); + if (g.NavHighlightActivatedTimer == 0.0f) + g.NavHighlightActivatedId = 0; + // Process programmatic activation request // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others) if (g.NavNextActivateId != 0) @@ -11229,12 +12008,12 @@ static void ImGui::NavUpdate() ImGuiWindow* window = g.NavWindow; const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. const ImGuiDir move_dir = g.NavMoveDir; - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll && move_dir != ImGuiDir_None) + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None) { if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) - SetScrollX(window, ImFloor(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); + SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) - SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); + SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } // *Normal* Manual scroll with LStick @@ -11244,9 +12023,9 @@ static void ImGui::NavUpdate() const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f; if (scroll_dir.x != 0.0f && window->ScrollbarX) - SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor)); + SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor)); if (scroll_dir.y != 0.0f) - SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor)); + SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor)); } } @@ -11260,20 +12039,16 @@ static void ImGui::NavUpdate() // Update mouse position if requested // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied) if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - { - io.MousePos = io.MousePosPrev = NavCalcPreferredRefPos(); - io.WantSetMousePos = true; - //IMGUI_DEBUG_LOG_IO("SetMousePos: (%.1f,%.1f)\n", io.MousePos.x, io.MousePos.y); - } + TeleportMousePos(NavCalcPreferredRefPos()); // [DEBUG] g.NavScoringDebugCount = 0; #if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) + if (ImGuiWindow* debug_window = g.NavWindow) { - ImDrawList* draw_list = GetForegroundDrawList(g.NavWindow); - if (1) { for (int layer = 0; layer < 2; layer++) { ImRect r = WindowRectRelToAbs(g.NavWindow, g.NavWindow->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255,200,0,255)); } } // [DEBUG] - if (1) { ImU32 col = (!g.NavWindow->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } + ImDrawList* draw_list = GetForegroundDrawList(debug_window); + int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); } + //if (1) { ImU32 col = (!debug_window->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } } #endif } @@ -11285,15 +12060,50 @@ void ImGui::NavInitRequestApplyResult() if (!g.NavWindow) return; + ImGuiNavItemData* result = &g.NavInitResult; + if (g.NavId != result->ID) + { + g.NavJustMovedToId = result->ID; + g.NavJustMovedToFocusScopeId = result->FocusScopeId; + g.NavJustMovedToKeyMods = 0; + } + // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently. - IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", g.NavInitResultId, g.NavLayer, g.NavWindow->Name); - SetNavID(g.NavInitResultId, g.NavLayer, 0, g.NavInitResultRectRel); + IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); + SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result + if (result->SelectionUserData != ImGuiSelectionUserData_Invalid) + g.NavLastValidSelectionUserData = result->SelectionUserData; if (g.NavInitRequestFromMove) NavRestoreHighlightAfterMove(); } +// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position +static void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags) +{ + // Bias initial rect + ImGuiContext& g = *GImGui; + const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos; + + // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias. + // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column. + // - But each successful move sets new bias on one axis, only cleared when using mouse. + if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0) + { + if (preferred_pos_rel.x == FLT_MAX) + preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x; + if (preferred_pos_rel.y == FLT_MAX) + preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y; + } + + // Apply general bias on the other axis + if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX) + r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x; + else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX) + r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y; +} + void ImGui::NavUpdateCreateMoveRequest() { ImGuiContext& g = *GImGui; @@ -11318,7 +12128,7 @@ void ImGui::NavUpdateCreateMoveRequest() g.NavMoveScrollFlags = ImGuiScrollFlags_None; if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { - const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove; + const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateNavMove; if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; } if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; } if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; } @@ -11339,13 +12149,15 @@ void ImGui::NavUpdateCreateMoveRequest() g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y); } - // [DEBUG] Always send a request + // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction. #if IMGUI_DEBUG_NAV_SCORING - if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C)) - g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); - if (io.KeyCtrl && g.NavMoveDir == ImGuiDir_None) + //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C)) + // g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3); + if (io.KeyCtrl) { - g.NavMoveDir = g.NavMoveDirForDebug; + if (g.NavMoveDir == ImGuiDir_None) + g.NavMoveDir = g.NavMoveDirForDebug; + g.NavMoveClipDir = g.NavMoveDir; g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult; } #endif @@ -11360,7 +12172,7 @@ void ImGui::NavUpdateCreateMoveRequest() { IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer); g.NavInitRequest = g.NavInitRequestFromMove = true; - g.NavInitResultId = 0; + g.NavInitResult.ID = 0; g.NavDisableHighlight = false; } @@ -11398,9 +12210,9 @@ void ImGui::NavUpdateCreateMoveRequest() ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); scoring_rect = WindowRectRelToAbs(window, nav_rect_rel); scoring_rect.TranslateY(scoring_rect_offset_y); - scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); - scoring_rect.Max.x = scoring_rect.Min.x; - IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem(). + if (g.NavMoveSubmitted) + NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags); + IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem(). //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] } @@ -11422,16 +12234,16 @@ void ImGui::NavUpdateCreateTabbingRequest() // Initiate tabbing request // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!) - // Initially this was designed to use counters and modulo arithmetic, but that could not work with unsubmitted items (list clipper). Instead we use a strategy close to other move requests. // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping. const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (nav_keyboard_active) g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1; else g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1; + ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate; ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY; ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down; - NavMoveRequestSubmit(ImGuiDir_None, clip_dir, ImGuiNavMoveFlags_Tabbing, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. + NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable. g.NavTabbingCounter = -1; } @@ -11448,17 +12260,20 @@ void ImGui::NavMoveRequestApplyResult() ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL; // Tabbing forward wrap - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && result == NULL) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL) if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) result = &g.NavTabbingResultFirst; // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) + const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; if (result == NULL) { - if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) - g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; - if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) + g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight; + if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0) NavRestoreHighlightAfterMove(); + NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis. + IMGUI_DEBUG_LOG_NAV("[nav] NavMoveSubmitted but not led to a result!\n"); return; } @@ -11476,55 +12291,67 @@ void ImGui::NavMoveRequestApplyResult() // Scroll to keep newly navigated item fully into view. if (g.NavLayer == ImGuiNavLayer_Main) { + ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); + ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); + if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY) { - // FIXME: Should remove this + // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge? float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f; SetScrollY(result->Window, scroll_target); } - else - { - ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel); - ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags); - } } if (g.NavWindow != result->Window) { IMGUI_DEBUG_LOG_FOCUS("[focus] NavMoveRequest: SetNavWindow(\"%s\")\n", result->Window->Name); g.NavWindow = result->Window; + g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; } + + // FIXME: Could become optional e.g. ImGuiNavMoveFlags_NoClearActiveId if we later want to apply navigation requests without altering active input. if (g.ActiveId != result->ID) ClearActiveID(); - if (g.NavId != result->ID) + + // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) + // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior. + if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0) { - // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId) g.NavJustMovedToId = result->ID; g.NavJustMovedToFocusScopeId = result->FocusScopeId; g.NavJustMovedToKeyMods = g.NavMoveKeyMods; } - // Focus + // Apply new NavID/Focus IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \"%s\"\n", result->ID, g.NavLayer, g.NavWindow->Name); + ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer]; SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel); + if (result->SelectionUserData != ImGuiSelectionUserData_Invalid) + g.NavLastValidSelectionUserData = result->SelectionUserData; - // Tabbing: Activates Inputable or Focus non-Inputable - if ((g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && (result->InFlags & ImGuiItemFlags_Inputable)) + // Restore last preferred position for current axis + // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0) { - g.NavNextActivateId = result->ID; - g.NavNextActivateFlags = ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState; - g.NavMoveFlags |= ImGuiNavMoveFlags_DontSetNavHighlight; + preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis]; + g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel; } + // Tabbing: Activates Inputable, otherwise only Focus + if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0) + g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate; + // Activate if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate) { g.NavNextActivateId = result->ID; g.NavNextActivateFlags = ImGuiActivateFlags_None; + if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) + g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState | ImGuiActivateFlags_FromTabbing; } // Enable nav highlight - if ((g.NavMoveFlags & ImGuiNavMoveFlags_DontSetNavHighlight) == 0) + if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0) NavRestoreHighlightAfterMove(); } @@ -11551,15 +12378,14 @@ static void ImGui::NavUpdateCancelRequest() NavRestoreLayer(ImGuiNavLayer_Main); NavRestoreHighlightAfterMove(); } - else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) + else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow) { // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; + ImGuiWindow* child_window = g.NavWindow->RootWindowForNav; + ImGuiWindow* parent_window = child_window->ParentWindow; IM_ASSERT(child_window->ChildId != 0); - ImRect child_rect = child_window->Rect(); FocusWindow(parent_window); - SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); + SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_window->Rect())); NavRestoreHighlightAfterMove(); } else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) @@ -11597,7 +12423,7 @@ static float ImGui::NavUpdatePageUpPageDown() if (g.NavLayer != ImGuiNavLayer_Main) NavRestoreLayer(ImGuiNavLayer_Main); - if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) + if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY) { // Fallback manual-scroll when window has no navigable item if (IsKeyPressed(ImGuiKey_PageUp, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat)) @@ -11619,14 +12445,14 @@ static float ImGui::NavUpdatePageUpPageDown() nav_scoring_rect_offset_y = -page_offset_y; g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; } else if (IsKeyPressed(ImGuiKey_PageDown, true)) { nav_scoring_rect_offset_y = +page_offset_y; g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item) g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; + g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove; } else if (home_pressed) { @@ -11665,8 +12491,7 @@ static void ImGui::NavEndFrame() // Perform wrap-around in menus // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly. // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame. - const ImGuiNavMoveFlags wanted_flags = ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY; - if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & wanted_flags) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) + if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0) NavUpdateCreateWrappingRequest(); } @@ -11678,7 +12503,9 @@ static void ImGui::NavUpdateCreateWrappingRequest() bool do_forward = false; ImRect bb_rel = window->NavRectRel[g.NavLayer]; ImGuiDir clip_dir = g.NavMoveDir; + const ImGuiNavMoveFlags move_flags = g.NavMoveFlags; + //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) { bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x; @@ -11722,6 +12549,8 @@ static void ImGui::NavUpdateCreateWrappingRequest() if (!do_forward) return; window->NavRectRel[g.NavLayer] = bb_rel; + NavClearPreferredPosForAxis(ImGuiAxis_X); + NavClearPreferredPosForAxis(ImGuiAxis_Y); NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags); } @@ -11775,7 +12604,7 @@ static void ImGui::NavUpdateWindowing() bool apply_toggle_layer = false; ImGuiWindow* modal_window = GetTopMostPopupModal(); - bool allow_windowing = (modal_window == NULL); + bool allow_windowing = (modal_window == NULL); // FIXME: This prevent CTRL+TAB from being usable with windows that are inside the Begin-stack of that modal. if (!allow_windowing) g.NavWindowingTarget = NULL; @@ -11788,10 +12617,12 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL+Tab or Square+L/R window selection + // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab) + const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing"); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); - const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); + const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); + const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, 0, ImGuiInputFlags_None); const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard! if (start_windowing_with_gamepad || start_windowing_with_keyboard) @@ -11802,6 +12633,10 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f); g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; + + // Register ownership of our mods. Using ImGuiInputFlags_RouteGlobalHigh in the Shortcut() calls instead would probably be correct but may have more side-effects. + if (keyboard_next_window || keyboard_prev_window) + SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id); } // Gamepad update @@ -11845,28 +12680,33 @@ static void ImGui::NavUpdateWindowing() } // Keyboard: Press and Release ALT to toggle menu layer - // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. - // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. - if (nav_keyboard_active && IsKeyPressed(ImGuiMod_Alt, ImGuiKeyOwner_None)) - { - g.NavWindowingToggleLayer = true; - g.NavInputSource = ImGuiInputSource_Keyboard; - } + const ImGuiKey windowing_toggle_keys[] = { ImGuiKey_LeftAlt, ImGuiKey_RightAlt }; + for (ImGuiKey windowing_toggle_key : windowing_toggle_keys) + if (nav_keyboard_active && IsKeyPressed(windowing_toggle_key, ImGuiKeyOwner_None)) + { + g.NavWindowingToggleLayer = true; + g.NavWindowingToggleKey = windowing_toggle_key; + g.NavInputSource = ImGuiInputSource_Keyboard; + break; + } if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard) { // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370) // We cancel toggling nav layer when other modifiers are pressed. (See #4439) + // - AltGR is Alt+Ctrl on some layout but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). // We cancel toggling nav layer if an owner has claimed the key. - if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_None) == false) + if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) + g.NavWindowingToggleLayer = false; + if (TestKeyOwner(g.NavWindowingToggleKey, ImGuiKeyOwner_None) == false || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_None) == false) g.NavWindowingToggleLayer = false; - // Apply layer toggle on release + // Apply layer toggle on Alt release // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss. - if (IsKeyReleased(ImGuiMod_Alt) && g.NavWindowingToggleLayer) + if (IsKeyReleased(g.NavWindowingToggleKey) && g.NavWindowingToggleLayer) if (g.ActiveId == 0 || g.ActiveIdAllowOverlap) if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev)) apply_toggle_layer = true; - if (!IsKeyDown(ImGuiMod_Alt)) + if (!IsKeyDown(g.NavWindowingToggleKey)) g.NavWindowingToggleLayer = false; } @@ -11884,7 +12724,7 @@ static void ImGui::NavUpdateWindowing() const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); g.NavWindowingAccumDeltaPos += nav_move_dir * move_step; g.NavDisableMouseHover = true; - ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaPos); + ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos); if (accum_floored.x != 0.0f || accum_floored.y != 0.0f) { ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; @@ -11899,9 +12739,9 @@ static void ImGui::NavUpdateWindowing() { ClearActiveID(); NavRestoreHighlightAfterMove(); - apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); ClosePopupsOverWindow(apply_focus_window, false); - FocusWindow(apply_focus_window); + FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild); + apply_focus_window = g.NavWindow; if (apply_focus_window->NavLastIds[0] == 0) NavInitWindow(apply_focus_window, false); @@ -12016,6 +12856,14 @@ void ImGui::ClearDragDrop() memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); } +bool ImGui::BeginTooltipHidden() +{ + ImGuiContext& g = *GImGui; + bool ret = Begin("##Tooltip_Hidden", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); + SetWindowHiddenAndSkipItemsForCurrentFrame(g.CurrentWindow); + return ret; +} + // When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() // If the item has an identifier: // - This assume/require the item to be activated (typically via ButtonBehavior). @@ -12072,7 +12920,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) // Rely on keeping other window->LastItemXXX fields intact. source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect); KeepAliveID(source_id); - bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id); + bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags); if (is_hovered && g.IO.MouseClicked[mouse_button]) { SetActiveID(source_id, window); @@ -12118,12 +12966,13 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) { // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. - bool ret = BeginTooltip(); + bool ret; + if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) + ret = BeginTooltipHidden(); + else + ret = BeginTooltip(); IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin("##Hidden", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame(). IM_UNUSED(ret); - - if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) - SetWindowHiddendAndSkipItemsForCurrentFrame(g.CurrentWindow); } if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) @@ -12212,13 +13061,14 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) IM_ASSERT(g.DragDropWithinTarget == false); g.DragDropTargetRect = bb; + g.DragDropTargetClipRect = window->ClipRect; // May want to be overriden by user depending on use case? g.DragDropTargetId = id; g.DragDropWithinTarget = true; return true; } // We don't use BeginDragDropTargetCustom() and duplicate its code because: -// 1) we use LastItemRectHoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. +// 1) we use LastItemData's ImGuiItemStatusFlags_HoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can. // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case) bool ImGui::BeginDragDropTarget() @@ -12246,6 +13096,7 @@ bool ImGui::BeginDragDropTarget() IM_ASSERT(g.DragDropWithinTarget == false); g.DragDropTargetRect = display_rect; + g.DragDropTargetClipRect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasClipRect) ? g.LastItemData.ClipRect : window->ClipRect; g.DragDropTargetId = id; g.DragDropWithinTarget = true; return true; @@ -12260,7 +13111,6 @@ bool ImGui::IsDragDropPayloadBeingAccepted() const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; ImGuiPayload& payload = g.DragDropPayload; IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? @@ -12284,7 +13134,7 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop payload.Preview = was_accepted_previously; flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame) if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) - window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); + RenderDragDropTargetRect(r, g.DragDropTargetClipRect); g.DragDropAcceptFrameCount = g.FrameCount; payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased() @@ -12295,10 +13145,20 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop return &payload; } -// FIXME-DRAGDROP: Settle on a proper default visuals for drop target. -void ImGui::RenderDragDropTargetRect(const ImRect& bb) +// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target. +void ImGui::RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect) { - GetWindowDrawList()->AddRect(bb.Min - ImVec2(3.5f, 3.5f), bb.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImRect bb_display = bb; + bb_display.ClipWith(item_clip_rect); // Clip THEN expand so we have a way to visualize that target is not entirely visible. + bb_display.Expand(3.5f); + bool push_clip_rect = !window->ClipRect.Contains(bb_display); + if (push_clip_rect) + window->DrawList->PushClipRectFullScreen(); + window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); + if (push_clip_rect) + window->DrawList->PopClipRect(); } const ImGuiPayload* ImGui::GetDragDropPayload() @@ -12646,9 +13506,9 @@ ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) { ImGuiContext& g = *GImGui; const ImGuiID type_hash = ImHashStr(type_name); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].TypeHash == type_hash) - return &g.SettingsHandlers[handler_n]; + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + if (handler.TypeHash == type_hash) + return &handler; return NULL; } @@ -12657,9 +13517,9 @@ void ImGui::ClearIniSettings() { ImGuiContext& g = *GImGui; g.SettingsIniData.clear(); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].ClearAllFn) - g.SettingsHandlers[handler_n].ClearAllFn(&g, &g.SettingsHandlers[handler_n]); + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + if (handler.ClearAllFn != NULL) + handler.ClearAllFn(&g, &handler); } void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) @@ -12674,6 +13534,7 @@ void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) } // Zero-tolerance, no error reporting, cheap .ini parsing +// Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty! void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) { ImGuiContext& g = *GImGui; @@ -12693,9 +13554,9 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) // Call pre-read handlers // Some types will clear their data (e.g. dock information) some types will allow merge/override (window) - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].ReadInitFn) - g.SettingsHandlers[handler_n].ReadInitFn(&g, &g.SettingsHandlers[handler_n]); + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + if (handler.ReadInitFn != NULL) + handler.ReadInitFn(&g, &handler); void* entry_data = NULL; ImGuiSettingsHandler* entry_handler = NULL; @@ -12739,9 +13600,9 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) memcpy(buf, ini_data, ini_size); // Call post-read handlers - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].ApplyAllFn) - g.SettingsHandlers[handler_n].ApplyAllFn(&g, &g.SettingsHandlers[handler_n]); + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + if (handler.ApplyAllFn != NULL) + handler.ApplyAllFn(&g, &handler); } void ImGui::SaveIniSettingsToDisk(const char* ini_filename) @@ -12767,11 +13628,8 @@ const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) g.SettingsDirtyTimer = 0.0f; g.SettingsIniData.Buf.resize(0); g.SettingsIniData.Buf.push_back(0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - { - ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; - handler->WriteAllFn(&g, handler, &g.SettingsIniData); - } + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + handler.WriteAllFn(&g, &handler, &g.SettingsIniData); if (out_size) *out_size = (size_t)g.SettingsIniData.size(); return g.SettingsIniData.c_str(); @@ -12781,12 +13639,13 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) { ImGuiContext& g = *GImGui; -#if !IMGUI_DEBUG_INI_SETTINGS - // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier. - if (const char* p = strstr(name, "###")) - name = p; -#endif + if (g.IO.ConfigDebugIniSettings == false) + { + // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier. + if (const char* p = strstr(name, "###")) + name = p; + } const size_t name_len = strlen(name); // Allocate chunk @@ -12805,7 +13664,7 @@ ImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id) { ImGuiContext& g = *GImGui; for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->ID == id) + if (settings->ID == id && !settings->WantDelete) return settings; return NULL; } @@ -12836,8 +13695,8 @@ void ImGui::ClearWindowSettings(const char* name) static void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) { ImGuiContext& g = *ctx; - for (int i = 0; i != g.Windows.Size; i++) - g.Windows[i]->SettingsOffset = -1; + for (ImGuiWindow* window : g.Windows) + window->SettingsOffset = -1; g.SettingsWindows.clear(); } @@ -12862,6 +13721,7 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } + else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); } } // Apply to existing windows (if any) @@ -12882,9 +13742,8 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl // Gather data from windows that were active during this session // (if a window wasn't opened in this session we preserve its settings) ImGuiContext& g = *ctx; - for (int i = 0; i != g.Windows.Size; i++) + for (ImGuiWindow* window : g.Windows) { - ImGuiWindow* window = g.Windows[i]; if (window->Flags & ImGuiWindowFlags_NoSavedSettings) continue; @@ -12897,7 +13756,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl IM_ASSERT(settings->ID == window->ID); settings->Pos = ImVec2ih(window->Pos); settings->Size = ImVec2ih(window->SizeFull); - + settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0; settings->Collapsed = window->Collapsed; settings->WantDelete = false; } @@ -12910,9 +13769,18 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl continue; const char* settings_name = settings->GetName(); buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); - buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); - buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); - buf->appendf("Collapsed=%d\n", settings->Collapsed); + if (settings->IsChild) + { + buf->appendf("IsChild=1\n"); + buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); + } + else + { + buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); + buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); + if (settings->Collapsed) + buf->appendf("Collapsed=1\n"); + } buf->append("\n"); } } @@ -12963,10 +13831,8 @@ static void ImGui::UpdateViewportsNewFrame() main_viewport->Pos = ImVec2(0.0f, 0.0f); main_viewport->Size = g.IO.DisplaySize; - for (int n = 0; n < g.Viewports.Size; n++) + for (ImGuiViewportP* viewport : g.Viewports) { - ImGuiViewportP* viewport = g.Viewports[n]; - // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin; viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax; @@ -13153,7 +14019,7 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeDat //----------------------------------------------------------------------------- // [SECTION] METRICS/DEBUGGER WINDOW //----------------------------------------------------------------------------- -// - RenderViewportThumbnail() [Internal] +// - DebugRenderViewportThumbnail() [Internal] // - RenderViewportsThumbnails() [Internal] // - DebugTextEncoding() // - MetricsHelpMarker() [Internal] @@ -13184,16 +14050,15 @@ void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* ImVec2 off = bb.Min - viewport->Pos * scale; float alpha_mul = 1.0f; window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); - for (int i = 0; i != g.Windows.Size; i++) + for (ImGuiWindow* thumb_window : g.Windows) { - ImGuiWindow* thumb_window = g.Windows[i]; if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) continue; ImRect thumb_r = thumb_window->Rect(); ImRect title_r = thumb_window->TitleBarRect(); - thumb_r = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale)); - title_r = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height + thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off + thumb_r.Max * scale)); + title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off + ImVec2(title_r.Max.x, title_r.Min.y + title_r.GetHeight() * 3.0f) * scale)); // Exaggerate title bar height thumb_r.ClipWithFull(bb); title_r.ClipWithFull(bb); const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); @@ -13203,6 +14068,8 @@ void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name)); } draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul)); + if (viewport->ID == g.DebugMetricsConfig.HighlightViewportID) + window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); } static void RenderViewportsThumbnails() @@ -13210,16 +14077,14 @@ static void RenderViewportsThumbnails() ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. float SCALE = 1.0f / 8.0f; - ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int n = 0; n < g.Viewports.Size; n++) - bb_full.Add(g.Viewports[n]->GetMainRect()); + ImRect bb_full(g.Viewports[0]->Pos, g.Viewports[0]->Pos + g.Viewports[0]->Size); ImVec2 p = window->DC.CursorPos; ImVec2 off = p - bb_full.Min * SCALE; - for (int n = 0; n < g.Viewports.Size; n++) + + // Draw viewports + for (ImGuiViewportP* viewport : g.Viewports) { - ImGuiViewportP* viewport = g.Viewports[n]; ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE); ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb); } @@ -13229,14 +14094,15 @@ static void RenderViewportsThumbnails() // Draw an arbitrary US keyboard layout to visualize translated keys void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) { - const ImVec2 key_size = ImVec2(35.0f, 35.0f); - const float key_rounding = 3.0f; - const ImVec2 key_face_size = ImVec2(25.0f, 25.0f); - const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f); - const float key_face_rounding = 2.0f; - const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f); + const float scale = ImGui::GetFontSize() / 13.0f; + const ImVec2 key_size = ImVec2(35.0f, 35.0f) * scale; + const float key_rounding = 3.0f * scale; + const ImVec2 key_face_size = ImVec2(25.0f, 25.0f) * scale; + const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f) * scale; + const float key_face_rounding = 2.0f * scale; + const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f) * scale; const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f); - const float key_row_offset = 9.0f; + const float key_row_offset = 9.0f * scale; ImVec2 board_min = GetCursorScreenPos(); ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f); @@ -13269,7 +14135,7 @@ void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding); ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y); draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label); - if (ImGui::IsKeyDown(key_data->Key)) + if (IsKeyDown(key_data->Key)) draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding); } draw_list->PopClipRect(); @@ -13279,7 +14145,7 @@ void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) void ImGui::DebugTextEncoding(const char* str) { Text("Text: \"%s\"", str); - if (!BeginTable("list", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) + if (!BeginTable("##DebugTextEncoding", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable)) return; TableSetupColumn("Offset"); TableSetupColumn("UTF-8"); @@ -13311,11 +14177,40 @@ void ImGui::DebugTextEncoding(const char* str) EndTable(); } +static void DebugFlashStyleColorStop() +{ + ImGuiContext& g = *GImGui; + if (g.DebugFlashStyleColorIdx != ImGuiCol_COUNT) + g.Style.Colors[g.DebugFlashStyleColorIdx] = g.DebugFlashStyleColorBackup; + g.DebugFlashStyleColorIdx = ImGuiCol_COUNT; +} + +// Flash a given style color for some + inhibit modifications of this color via PushStyleColor() calls. +void ImGui::DebugFlashStyleColor(ImGuiCol idx) +{ + ImGuiContext& g = *GImGui; + DebugFlashStyleColorStop(); + g.DebugFlashStyleColorTime = 0.5f; + g.DebugFlashStyleColorIdx = idx; + g.DebugFlashStyleColorBackup = g.Style.Colors[idx]; +} + +void ImGui::UpdateDebugToolFlashStyleColor() +{ + ImGuiContext& g = *GImGui; + if (g.DebugFlashStyleColorTime <= 0.0f) + return; + ColorConvertHSVtoRGB(cosf(g.DebugFlashStyleColorTime * 6.0f) * 0.5f + 0.5f, 0.5f, 0.5f, g.Style.Colors[g.DebugFlashStyleColorIdx].x, g.Style.Colors[g.DebugFlashStyleColorIdx].y, g.Style.Colors[g.DebugFlashStyleColorIdx].z); + g.Style.Colors[g.DebugFlashStyleColorIdx].w = 1.0f; + if ((g.DebugFlashStyleColorTime -= g.IO.DeltaTime) <= 0.0f) + DebugFlashStyleColorStop(); +} + // Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. static void MetricsHelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); @@ -13327,9 +14222,8 @@ static void MetricsHelpMarker(const char* desc) // [DEBUG] List fonts in a font atlas and display its texture void ImGui::ShowFontAtlas(ImFontAtlas* atlas) { - for (int i = 0; i < atlas->Fonts.Size; i++) + for (ImFont* font : atlas->Fonts) { - ImFont* font = atlas->Fonts[i]; PushID(font); DebugNodeFont(font); PopID(); @@ -13353,8 +14247,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; if (cfg->ShowDebugLog) ShowDebugLogWindow(&cfg->ShowDebugLog); - if (cfg->ShowStackTool) - ShowStackToolWindow(&cfg->ShowStackTool); + if (cfg->ShowIDStackTool) + ShowIDStackToolWindow(&cfg->ShowIDStackTool); if (!Begin("Dear ImGui Metrics/Debugger", p_open) || GetCurrentWindow()->BeginCount > 1) { @@ -13362,11 +14256,14 @@ void ImGui::ShowMetricsWindow(bool* p_open) return; } + // [DEBUG] Clear debug breaks hooks after exactly one cycle. + DebugBreakClearData(); + // Basic info Text("Dear ImGui %s", GetVersion()); Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); Text("%d vertices, %d indices (%d triangles)", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3); - Text("%d visible windows, %d active allocations", io.MetricsRenderWindows, io.MetricsActiveAllocations); + Text("%d visible windows, %d current allocations", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount); //SameLine(); if (SmallButton("GC")) { g.GcCompactAll = true; } Separator(); @@ -13395,8 +14292,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == TRT_ColumnsRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); } else if (rect_type == TRT_ColumnsWorkRect) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); } else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; } - else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } // Note: y1/y2 not always accurate - else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } + else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate + else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); } else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } IM_ASSERT(0); @@ -13410,7 +14307,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == WRT_InnerRect) { return window->InnerRect; } else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } else if (rect_type == WRT_WorkRect) { return window->WorkRect; } - else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } + else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); } else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } IM_ASSERT(0); @@ -13421,34 +14318,24 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Tools if (TreeNode("Tools")) { - bool show_encoding_viewer = TreeNode("UTF-8 Encoding viewer"); - SameLine(); - MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct."); - if (show_encoding_viewer) - { - static char buf[100] = ""; - SetNextItemWidth(-FLT_MIN); - InputText("##Text", buf, IM_ARRAYSIZE(buf)); - if (buf[0] != 0) - DebugTextEncoding(buf); - TreePop(); - } - + // Debug Break features // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted. - if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive) - DebugStartItemPicker(); + SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x); SameLine(); MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash."); + if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive) + DebugStartItemPicker(); + Checkbox("Show \"Debug Break\" buttons in other sections (io.ConfigDebugIsDebuggerPresent)", &g.IO.ConfigDebugIsDebuggerPresent); + + SeparatorText("Visualize"); - // Stack Tool is your best friend! Checkbox("Show Debug Log", &cfg->ShowDebugLog); SameLine(); MetricsHelpMarker("You can also call ImGui::ShowDebugLogWindow() from your code."); - // Stack Tool is your best friend! - Checkbox("Show Stack Tool", &cfg->ShowStackTool); + Checkbox("Show ID Stack Tool", &cfg->ShowIDStackTool); SameLine(); - MetricsHelpMarker("You can also call ImGui::ShowStackToolWindow() from your code."); + MetricsHelpMarker("You can also call ImGui::ShowIDStackToolWindow() from your code."); Checkbox("Show windows begin order", &cfg->ShowWindowsBeginOrder); Checkbox("Show windows rectangles", &cfg->ShowWindowsRects); @@ -13511,11 +14398,26 @@ void ImGui::ShowMetricsWindow(bool* p_open) Unindent(); } } + Checkbox("Show groups rectangles", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data + + SeparatorText("Validate"); Checkbox("Debug Begin/BeginChild return value", &io.ConfigDebugBeginReturnValueLoop); SameLine(); MetricsHelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running."); + Checkbox("UTF-8 Encoding viewer", &cfg->ShowTextEncodingViewer); + SameLine(); + MetricsHelpMarker("You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct."); + if (cfg->ShowTextEncodingViewer) + { + static char buf[64] = ""; + SetNextItemWidth(-FLT_MIN); + InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf)); + if (buf[0] != 0) + DebugTextEncoding(buf); + } + TreePop(); } @@ -13530,9 +14432,9 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship! ImVector& temp_buffer = g.WindowsTempSortBuffer; temp_buffer.resize(0); - for (int i = 0; i < g.Windows.Size; i++) - if (g.Windows[i]->LastFrameActive + 1 >= g.FrameCount) - temp_buffer.push_back(g.Windows[i]); + for (ImGuiWindow* window : g.Windows) + if (window->LastFrameActive + 1 >= g.FrameCount) + temp_buffer.push_back(window); struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } }; ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder); DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL); @@ -13544,44 +14446,44 @@ void ImGui::ShowMetricsWindow(bool* p_open) // DrawLists int drawlist_count = 0; - for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) - drawlist_count += g.Viewports[viewport_i]->DrawDataBuilder.GetDrawListCount(); + for (ImGuiViewportP* viewport : g.Viewports) + drawlist_count += viewport->DrawDataP.CmdLists.Size; if (TreeNode("DrawLists", "DrawLists (%d)", drawlist_count)) { Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); - for (int viewport_i = 0; viewport_i < g.Viewports.Size; viewport_i++) - { - ImGuiViewportP* viewport = g.Viewports[viewport_i]; - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); - } + for (ImGuiViewportP* viewport : g.Viewports) + for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists) + DebugNodeDrawList(NULL, viewport, draw_list, "DrawList"); TreePop(); } // Viewports if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size)) { - Indent(GetTreeNodeToLabelSpacing()); - RenderViewportsThumbnails(); - Unindent(GetTreeNodeToLabelSpacing()); - for (int i = 0; i < g.Viewports.Size; i++) - DebugNodeViewport(g.Viewports[i]); + SetNextItemOpen(true, ImGuiCond_Once); + if (TreeNode("Windows Minimap")) + { + RenderViewportsThumbnails(); + TreePop(); + } + cfg->HighlightViewportID = 0; + + for (ImGuiViewportP* viewport : g.Viewports) + DebugNodeViewport(viewport); TreePop(); } // Details for Popups if (TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size)) { - for (int i = 0; i < g.OpenPopupStack.Size; i++) + for (const ImGuiPopupData& popup_data : g.OpenPopupStack) { // As it's difficult to interact with tree nodes while popups are open, we display everything inline. - const ImGuiPopupData* popup_data = &g.OpenPopupStack[i]; - ImGuiWindow* window = popup_data->Window; + ImGuiWindow* window = popup_data.Window; BulletText("PopupID: %08x, Window: '%s' (%s%s), BackupNavWindow '%s', ParentWindow '%s'", - popup_data->PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "", - popup_data->BackupNavWindow ? popup_data->BackupNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL"); + popup_data.PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "", + popup_data.BackupNavWindow ? popup_data.BackupNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL"); } TreePop(); } @@ -13623,6 +14525,13 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } + // Details for TypingSelect + if (TreeNode("TypingSelect", "TypingSelect (%d)", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0)) + { + DebugNodeTypingSelectState(&g.TypingSelectState); + TreePop(); + } + // Details for Docking #ifdef IMGUI_HAS_DOCK if (TreeNode("Docking")) @@ -13647,11 +14556,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("\"%s\"", g.IO.IniFilename); else TextUnformatted(""); + Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { - for (int n = 0; n < g.SettingsHandlers.Size; n++) - BulletText("%s", g.SettingsHandlers[n].TypeName); + for (ImGuiSettingsHandler& handler : g.SettingsHandlers) + BulletText("\"%s\"", handler.TypeName); TreePop(); } if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) @@ -13679,6 +14589,22 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } + // Settings + if (TreeNode("Memory allocations")) + { + ImGuiDebugAllocInfo* info = &g.DebugAllocInfo; + Text("%d current allocations", info->TotalAllocCount - info->TotalFreeCount); + if (SmallButton("GC now")) { g.GcCompactAll = true; } + Text("Recent frames with allocations:"); + int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf); + for (int n = buf_size - 1; n >= 0; n--) + { + ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size]; + BulletText("Frame %06d: %+3d ( %2d malloc, %2d free )%s", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount, (n == 0) ? " (most recent)" : ""); + } + TreePop(); + } + if (TreeNode("Inputs")) { Text("KEYBOARD/GAMEPAD/MOUSE KEYS"); @@ -13689,7 +14615,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; #else - struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array //Text("Legacy raw:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } } #endif Text("Keys down:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); } @@ -13714,6 +14640,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); } Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); } Text("Mouse wheel: %.1f", io.MouseWheel); + Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer); + Text("Mouse source: %s", GetMouseSourceName(io.MouseSource)); Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused Unindent(); } @@ -13730,41 +14658,44 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("KEY OWNERS"); { Indent(); - if (BeginListBox("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6))) - { + if (BeginChild("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings)) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { - ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key); if (owner_data->OwnerCurr == ImGuiKeyOwner_None) continue; Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr, owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : ""); DebugLocateItemOnHover(owner_data->OwnerCurr); } - EndListBox(); - } + EndChild(); Unindent(); } Text("SHORTCUT ROUTING"); + SameLine(); + MetricsHelpMarker("Declared shortcut routes automatically set key owner when mods matches."); { Indent(); - if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6))) - { + if (BeginChild("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings)) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; ) { - char key_chord_name[64]; ImGuiKeyRoutingData* routing_data = &rt->Entries[idx]; - GetKeyChordName(key | routing_data->Mods, key_chord_name, IM_ARRAYSIZE(key_chord_name)); - Text("%s: 0x%08X", key_chord_name, routing_data->RoutingCurr); + ImGuiKeyChord key_chord = key | routing_data->Mods; + Text("%s: 0x%08X (scored %d)", GetKeyChordName(key_chord), routing_data->RoutingCurr, routing_data->RoutingCurrScore); DebugLocateItemOnHover(routing_data->RoutingCurr); + if (g.IO.ConfigDebugIsDebuggerPresent) + { + SameLine(); + if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord")) + g.DebugBreakInShortcutRouting = key_chord; + } idx = routing_data->NextEntryIndex; } } - EndListBox(); - } + EndChild(); Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); Unindent(); } @@ -13788,7 +14719,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame - Text("HoverDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverDelayId, g.HoverDelayTimer, g.HoverDelayClearTimer); + Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer); Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); DebugLocateItemOnHover(g.DragDropPayload.SourceId); Unindent(); @@ -13799,11 +14730,20 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); DebugLocateItemOnHover(g.NavId); Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource)); + Text("NavLastValidSelectionUserData = %" IM_PRId64 " (0x%" IM_PRIX64 ")", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); Text("NavActivateId/DownId/PressedId: %08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId); Text("NavActivateFlags: %04X", g.NavActivateFlags); Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); Text("NavFocusScopeId = 0x%08X", g.NavFocusScopeId); + Text("NavFocusRoute[] = "); + for (int path_n = g.NavFocusRoute.Size - 1; path_n >= 0; path_n--) + { + const ImGuiFocusScopeData& focus_scope = g.NavFocusRoute[path_n]; + SameLine(0.0f, 0.0f); + Text("0x%08X/", focus_scope.ID); + SetItemTooltip("In window \"%s\"", FindWindowByID(focus_scope.WindowID)->Name); + } Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); Unindent(); @@ -13813,9 +14753,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Overlay: Display windows Rectangles and Begin Order if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder) { - for (int n = 0; n < g.Windows.Size; n++) + for (ImGuiWindow* window : g.Windows) { - ImGuiWindow* window = g.Windows[n]; if (!window->WasActive) continue; ImDrawList* draw_list = GetForegroundDrawList(window); @@ -13872,21 +14811,90 @@ void ImGui::ShowMetricsWindow(bool* p_open) End(); } +void ImGui::DebugBreakClearData() +{ + // Those fields are scattered in their respective subsystem to stay in hot-data locations + ImGuiContext& g = *GImGui; + g.DebugBreakInWindow = 0; + g.DebugBreakInTable = 0; + g.DebugBreakInShortcutRouting = ImGuiKey_None; +} + +void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location) +{ + if (!BeginItemTooltip()) + return; + Text("To call IM_DEBUG_BREAK() %s:", description_of_location); + Separator(); + TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space."); + Separator(); + TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!"); + EndTooltip(); +} + +// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc. +// In order to reduce interferences with the contents we are trying to debug into. +bool ImGui::DebugBreakButton(const char* label, const char* description_of_location) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset); + ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y); + + const ImRect bb(pos, pos + size); + ItemSize(size, 0.0f); + if (!ItemAdd(bb, id)) + return false; + + // WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects. + bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags); + bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id); + DebugBreakButtonTooltip(false, description_of_location); + + ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + ImVec4 hsv; + ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z); + ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z); + + RenderNavHighlight(bb, id); + RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding); + RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb); + + IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); + return pressed; +} + // [DEBUG] Display contents of Columns void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) { if (!TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) return; BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX); - for (int column_n = 0; column_n < columns->Columns.Size; column_n++) - BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, GetColumnOffsetFromNorm(columns, columns->Columns[column_n].OffsetNorm)); + for (ImGuiOldColumnData& column : columns->Columns) + BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm)); TreePop(); } +static void FormatTextureIDForDebugDisplay(char* buf, int buf_size, ImTextureID tex_id) +{ + union { void* ptr; int integer; } tex_id_opaque; + memcpy(&tex_id_opaque, &tex_id, ImMin(sizeof(void*), sizeof(tex_id))); + if (sizeof(tex_id) >= sizeof(void*)) + ImFormatString(buf, buf_size, "0x%p", tex_id_opaque.ptr); + else + ImFormatString(buf, buf_size, "0x%04X", tex_id_opaque.integer); +} + // [DEBUG] Display contents of ImDrawList -void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label) +void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label) { ImGuiContext& g = *GImGui; + IM_UNUSED(viewport); // Used in docking branch ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; int cmd_count = draw_list->CmdBuffer.Size; if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL) @@ -13918,10 +14926,11 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, continue; } + char texid_desc[20]; + FormatTextureIDForDebugDisplay(texid_desc, IM_ARRAYSIZE(texid_desc), pcmd->TextureId); char buf[300]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", - pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId, - pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); + ImFormatString(buf, IM_ARRAYSIZE(buf), "DrawCmd:%5d tris, Tex %s, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)", + pcmd->ElemCount / 3, texid_desc, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list) DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes); @@ -14001,8 +15010,8 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co // Draw bounding boxes if (show_aabb) { - out_draw_list->AddRect(ImFloor(clip_rect.Min), ImFloor(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU - out_draw_list->AddRect(ImFloor(vtxs_rect.Min), ImFloor(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles + out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU + out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles } out_draw_list->Flags = backup_flags; } @@ -14027,7 +15036,7 @@ void ImGui::DebugNodeFont(ImFont* font) SetNextItemWidth(GetFontSize() * 8); DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); SameLine(); MetricsHelpMarker( - "Note than the default embedded font is NOT meant to be scaled.\n\n" + "Note that the default embedded font is NOT meant to be scaled.\n\n" "Font are currently rendered into bitmaps at a given size at the time of building the atlas. " "You may oversample them to get some flexibility with scaling. " "You can also render at multiple sizes and select which one to use at runtime.\n\n" @@ -14113,11 +15122,8 @@ void ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label) { if (!TreeNode(label, "%s: %d entries, %d bytes", label, storage->Data.Size, storage->Data.size_in_bytes())) return; - for (int n = 0; n < storage->Data.Size; n++) - { - const ImGuiStorage::ImGuiStoragePair& p = storage->Data[n]; + for (const ImGuiStorage::ImGuiStoragePair& p : storage->Data) BulletText("Key 0x%08X Value { i: %d }", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer. - } TreePop(); } @@ -14129,13 +15135,11 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) char* p = buf; const char* buf_end = buf + IM_ARRAYSIZE(buf); const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2); - p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); - p += ImFormatString(p, buf_end - p, " { "); + p += ImFormatString(p, buf_end - p, "%s 0x%08X (%d tabs)%s {", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? "" : " *Inactive*"); for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - p += ImFormatString(p, buf_end - p, "%s'%s'", - tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab)); + p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", TabBarGetTabName(tab_bar, tab)); } p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } @@ -14166,8 +15170,12 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) { + ImGuiContext& g = *GImGui; SetNextItemOpen(true, ImGuiCond_Once); - if (TreeNode("viewport0", "Viewport #%d", 0)) + bool open = TreeNode("viewport0", "Viewport #%d", 0); + if (IsItemHovered()) + g.DebugMetricsConfig.HighlightViewportID = viewport->ID; + if (open) { ImGuiWindowFlags flags = viewport->Flags; BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f", @@ -14177,9 +15185,8 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : ""); - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - DebugNodeDrawList(NULL, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); + for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists) + DebugNodeDrawList(NULL, viewport, draw_list, "DrawList"); TreePop(); } } @@ -14206,8 +15213,11 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) if (window->MemoryCompacted) TextDisabled("Note: some memory buffers have been compacted/freed."); + if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()")) + g.DebugBreakInWindow = window->ID; + ImGuiWindowFlags flags = window->Flags; - DebugNodeDrawList(window, window->DrawList, "DrawList"); + DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList"); BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y); BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", @@ -14225,14 +15235,18 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); DebugLocateItemOnHover(window->NavLastIds[layer]); } + const ImVec2* pr = window->NavPreferredScoringPosRel; + for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) + BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater. BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } - if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } - if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } + if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } + if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } + if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); } + if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } if (window->ColumnsStorage.Size > 0 && TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) { - for (int n = 0; n < window->ColumnsStorage.Size; n++) - DebugNodeColumns(&window->ColumnsStorage[n]); + for (ImGuiOldColumns& columns : window->ColumnsStorage) + DebugNodeColumns(&columns); TreePop(); } DebugNodeStorage(&window->StateStorage, "Storage"); @@ -14298,9 +15312,36 @@ void ImGui::DebugLogV(const char* fmt, va_list args) const int old_size = g.DebugLogBuf.size(); g.DebugLogBuf.appendf("[%05d] ", g.FrameCount); g.DebugLogBuf.appendfv(fmt, args); + g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size()); if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size); - g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size()); +#ifdef IMGUI_ENABLE_TEST_ENGINE + if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine) + IMGUI_TEST_ENGINE_LOG("%s", g.DebugLogBuf.begin() + old_size); +#endif +} + +// FIXME-LAYOUT: To be done automatically via layout mode once we rework ItemSize/ItemAdd into ItemLayout. +static void SameLineOrWrap(const ImVec2& size) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImVec2 pos(window->DC.CursorPosPrevLine.x + g.Style.ItemSpacing.x, window->DC.CursorPosPrevLine.y); + if (window->ClipRect.Contains(ImRect(pos, pos + size))) + ImGui::SameLine(); +} + +static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags) +{ + ImGuiContext& g = *GImGui; + ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight()); + SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout. + if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0) + { + g.DebugLogAutoDisableFrames = 2; + g.DebugLogAutoDisableFlags |= flags; + } + ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)"); } void ImGui::ShowDebugLogWindow(bool* p_open) @@ -14314,15 +15355,18 @@ void ImGui::ShowDebugLogWindow(bool* p_open) return; } - AlignTextToFramePadding(); - Text("Log events:"); - SameLine(); CheckboxFlags("All", &g.DebugLogFlags, ImGuiDebugLogFlags_EventMask_); - SameLine(); CheckboxFlags("ActiveId", &g.DebugLogFlags, ImGuiDebugLogFlags_EventActiveId); - SameLine(); CheckboxFlags("Focus", &g.DebugLogFlags, ImGuiDebugLogFlags_EventFocus); - SameLine(); CheckboxFlags("Popup", &g.DebugLogFlags, ImGuiDebugLogFlags_EventPopup); - SameLine(); CheckboxFlags("Nav", &g.DebugLogFlags, ImGuiDebugLogFlags_EventNav); - SameLine(); if (CheckboxFlags("Clipper", &g.DebugLogFlags, ImGuiDebugLogFlags_EventClipper)) { g.DebugLogClipperAutoDisableFrames = 2; } if (IsItemHovered()) SetTooltip("Clipper log auto-disabled after 2 frames"); - SameLine(); CheckboxFlags("IO", &g.DebugLogFlags, ImGuiDebugLogFlags_EventIO); + ImGuiDebugLogFlags all_enable_flags = ImGuiDebugLogFlags_EventMask_ & ~ImGuiDebugLogFlags_EventInputRouting; + CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags); + SetItemTooltip("(except InputRouting which is spammy)"); + + ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId); + ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper); + ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus); + ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO); + ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav); + ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup); + //ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); + ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting); if (SmallButton("Clear")) { @@ -14332,7 +15376,10 @@ void ImGui::ShowDebugLogWindow(bool* p_open) SameLine(); if (SmallButton("Copy")) SetClipboardText(g.DebugLogBuf.c_str()); - BeginChild("##log", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + BeginChild("##log", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + + const ImGuiDebugLogFlags backup_log_flags = g.DebugLogFlags; + g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper; ImGuiListClipper clipper; clipper.Begin(g.DebugLogIndex.size()); @@ -14341,10 +15388,10 @@ void ImGui::ShowDebugLogWindow(bool* p_open) { const char* line_begin = g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no); const char* line_end = g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no); - TextUnformatted(line_begin, line_end); + TextUnformatted(line_begin, line_end); // Display line ImRect text_rect = g.LastItemData.Rect; if (IsItemHovered()) - for (const char* p = line_begin; p < line_end - 10; p++) + for (const char* p = line_begin; p <= line_end - 10; p++) // Search for 0x???????? identifiers { ImGuiID id = 0; if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1) @@ -14357,6 +15404,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) p += 10; } } + g.DebugLogFlags = backup_log_flags; if (GetScrollY() >= GetScrollMaxY()) SetScrollHereY(1.0f); EndChild(); @@ -14365,9 +15413,41 @@ void ImGui::ShowDebugLogWindow(bool* p_open) } //----------------------------------------------------------------------------- -// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) +// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL) //----------------------------------------------------------------------------- +// Draw a small cross at current CursorPos in current window's DrawList +void ImGui::DebugDrawCursorPos(ImU32 col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImVec2 pos = window->DC.CursorPos; + window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f); + window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f); +} + +// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList +void ImGui::DebugDrawLineExtents(ImU32 col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + float curr_x = window->DC.CursorPos.x; + float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y); + float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y); + window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f); + window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f); + window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f); +} + +// Draw last item rect in ForegroundDrawList (so it is always visible) +void ImGui::DebugDrawItemRect(ImU32 col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); +} + +// [DEBUG] Locate item position/rectangle given an ID. static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green void ImGui::DebugLocateItem(ImGuiID target_id) @@ -14375,8 +15455,10 @@ void ImGui::DebugLocateItem(ImGuiID target_id) ImGuiContext& g = *GImGui; g.DebugLocateId = target_id; g.DebugLocateFrames = 2; + g.DebugBreakInLocateId = false; } +// FIXME: Doesn't work over through a modal window, because they clear HoveredWindow. void ImGui::DebugLocateItemOnHover(ImGuiID target_id) { if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup)) @@ -14384,11 +15466,24 @@ void ImGui::DebugLocateItemOnHover(ImGuiID target_id) ImGuiContext& g = *GImGui; DebugLocateItem(target_id); GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR); + + // Can't easily use a context menu here because it will mess with focus, active id etc. + if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f) + { + DebugBreakButtonTooltip(false, "in ItemAdd()"); + if (IsKeyChordPressed(g.DebugBreakKeyChord)) + g.DebugBreakInLocateId = true; + } } void ImGui::DebugLocateItemResolveWithLastItem() { ImGuiContext& g = *GImGui; + + // [DEBUG] Debug break requested by user + if (g.DebugBreakInLocateId) + IM_DEBUG_BREAK(); + ImGuiLastItemData item_data = g.LastItemData; g.DebugLocateId = 0; ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow); @@ -14400,6 +15495,12 @@ void ImGui::DebugLocateItemResolveWithLastItem() draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR); } +void ImGui::DebugStartItemPicker() +{ + ImGuiContext& g = *GImGui; + g.DebugItemPickerActive = true; +} + // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. void ImGui::UpdateDebugToolItemPicker() { @@ -14434,13 +15535,13 @@ void ImGui::UpdateDebugToolItemPicker() EndTooltip(); } -// [DEBUG] Stack Tool: update queries. Called by NewFrame() +// [DEBUG] ID Stack Tool: update queries. Called by NewFrame() void ImGui::UpdateDebugToolStackQueries() { ImGuiContext& g = *GImGui; - ImGuiStackTool* tool = &g.DebugStackTool; + ImGuiIDStackTool* tool = &g.DebugIDStackTool; - // Clear hook when stack tool is not visible + // Clear hook when id stack tool is not visible g.DebugHookIdInfo = 0; if (g.FrameCount != tool->LastActiveFrame + 1) return; @@ -14474,12 +15575,12 @@ void ImGui::UpdateDebugToolStackQueries() } } -// [DEBUG] Stack tool: hooks called by GetID() family functions +// [DEBUG] ID Stack tool: hooks called by GetID() family functions void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiStackTool* tool = &g.DebugStackTool; + ImGuiIDStackTool* tool = &g.DebugIDStackTool; // Step 0: stack query // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget. @@ -14522,7 +15623,7 @@ void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* dat info->DataType = data_type; } -static int StackToolFormatLevelInfo(ImGuiStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size) +static int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size) { ImGuiStackLevelInfo* info = &tool->Results[n]; ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL; @@ -14539,20 +15640,20 @@ static int StackToolFormatLevelInfo(ImGuiStackTool* tool, int n, bool format_for return ImFormatString(buf, buf_size, "???"); } -// Stack Tool: Display UI -void ImGui::ShowStackToolWindow(bool* p_open) +// ID Stack Tool: Display UI +void ImGui::ShowIDStackToolWindow(bool* p_open) { ImGuiContext& g = *GImGui; if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)) SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver); - if (!Begin("Dear ImGui Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) + if (!Begin("Dear ImGui ID Stack Tool", p_open) || GetCurrentWindow()->BeginCount > 1) { End(); return; } // Display hovered/active status - ImGuiStackTool* tool = &g.DebugStackTool; + ImGuiIDStackTool* tool = &g.DebugIDStackTool; const ImGuiID hovered_id = g.HoveredIdPreviousFrame; const ImGuiID active_id = g.ActiveId; #ifdef IMGUI_ENABLE_TEST_ENGINE @@ -14568,7 +15669,7 @@ void ImGui::ShowStackToolWindow(bool* p_open) Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC); SameLine(); TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*"); - if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiMod_Ctrl) && IsKeyPressed(ImGuiKey_C)) + if (tool->CopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, 0, ImGuiInputFlags_RouteGlobal)) { tool->CopyToClipboardLastTime = (float)g.Time; char* p = g.TempBuffer.Data; @@ -14621,7 +15722,7 @@ void ImGui::ShowStackToolWindow(bool* p_open) void ImGui::ShowMetricsWindow(bool*) {} void ImGui::ShowFontAtlas(ImFontAtlas*) {} void ImGui::DebugNodeColumns(ImGuiOldColumns*) {} -void ImGui::DebugNodeDrawList(ImGuiWindow*, const ImDrawList*, const char*) {} +void ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {} void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {} void ImGui::DebugNodeFont(ImFont*) {} void ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {} @@ -14634,10 +15735,9 @@ void ImGui::DebugNodeViewport(ImGuiViewportP*) {} void ImGui::DebugLog(const char*, ...) {} void ImGui::DebugLogV(const char*, va_list) {} void ImGui::ShowDebugLogWindow(bool*) {} -void ImGui::ShowStackToolWindow(bool*) {} +void ImGui::ShowIDStackToolWindow(bool*) {} +void ImGui::DebugStartItemPicker() {} void ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {} -void ImGui::UpdateDebugToolItemPicker() {} -void ImGui::UpdateDebugToolStackQueries() {} #endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS diff --git a/r5dev/thirdparty/imgui/imgui.h b/r5dev/thirdparty/imgui/imgui.h index aa57da9c..8658edd3 100644 --- a/r5dev/thirdparty/imgui/imgui.h +++ b/r5dev/thirdparty/imgui/imgui.h @@ -1,29 +1,30 @@ -// dear imgui, v1.89.4 +// dear imgui, v1.90.4 // (headers) // Help: -// - Read FAQ at http://dearimgui.org/faq -// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. +// - See links below. // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. -// Read imgui.cpp for details, links and comments. +// - Read top of imgui.cpp for more details, links and comments. // Resources: -// - FAQ http://dearimgui.org/faq -// - Homepage & latest https://github.com/ocornut/imgui +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Homepage https://github.com/ocornut/imgui // - Releases & changelog https://github.com/ocornut/imgui/releases -// - Gallery https://github.com/ocornut/imgui/issues/5886 (please post your screenshots/video there!) +// - Gallery https://github.com/ocornut/imgui/issues/6897 (please post your screenshots/video there!) // - Wiki https://github.com/ocornut/imgui/wiki (lots of good stuff there) // - Glossary https://github.com/ocornut/imgui/wiki/Glossary // - Issues & support https://github.com/ocornut/imgui/issues +// - Tests & Automation https://github.com/ocornut/imgui_test_engine -// Getting Started? -// - For first-time users having issues compiling/linking/running or issues loading fonts: -// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. +// For first-time users having issues compiling/linking/running/loading fonts: +// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. +// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there. // Library Version -// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345') -#define IMGUI_VERSION "1.89.4" -#define IMGUI_VERSION_NUM 18940 +// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') +#define IMGUI_VERSION "1.90.4" +#define IMGUI_VERSION_NUM 19040 #define IMGUI_HAS_TABLE /* @@ -33,10 +34,11 @@ Index of this file: // [SECTION] Forward declarations and basic types // [SECTION] Dear ImGui end-user API functions // [SECTION] Flags & Enumerations +// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) // [SECTION] Helpers: Memory allocations macros, ImVector<> // [SECTION] ImGuiStyle // [SECTION] ImGuiIO -// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) +// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) @@ -49,7 +51,7 @@ Index of this file: #pragma once // Configuration file with compile-time options -// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system') +// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system) #ifdef IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG #endif @@ -69,7 +71,7 @@ Index of this file: // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h) -// Using dear imgui via a shared library is not recommended, because we don't guarantee backward nor forward ABI compatibility (also function call overhead, as dear imgui is a call-heavy API) +// Using dear imgui via a shared library is not recommended: we don't guarantee backward nor forward ABI compatibility + this is a call-heavy library and function call overhead adds up. #ifndef IMGUI_API #define IMGUI_API #endif @@ -84,10 +86,11 @@ Index of this file: #endif #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. -#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11 #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. +// (MSVC provides an equivalent mechanism via SAL Annotations but it would require the macros in a different +// location. e.g. #include + void myprintf(_Printf_format_string_ const char* format, ...)) #if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) @@ -115,10 +118,14 @@ Index of this file: #endif #if defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#if __has_warning("-Wunknown-warning-option") +#pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind @@ -167,6 +174,7 @@ struct ImGuiViewport; // A Platform Window (always only one in 'ma // In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value) +enum ImGuiMouseSource : int; // -> enum ImGuiMouseSource // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen) typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type @@ -177,7 +185,7 @@ typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor() -// Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file) +// Flags (declared as int to allow using as flags without overhead, and to not pollute the top of this file) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! // In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. @@ -186,6 +194,7 @@ typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: f typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build typedef int ImGuiBackendFlags; // -> enum ImGuiBackendFlags_ // Flags: for io.BackendFlags typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for InvisibleButton() +typedef int ImGuiChildFlags; // -> enum ImGuiChildFlags_ // Flags: for BeginChild() typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc. typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() @@ -193,7 +202,7 @@ typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: f typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() -typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for storage only for now: an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. +typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for IsKeyChordPressed(), Shortcut() etc. an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. @@ -233,8 +242,8 @@ typedef unsigned long long ImU64; // 64-bit unsigned integer // Character types // (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display) -typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. typedef unsigned int ImWchar32; // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings. +typedef unsigned short ImWchar16; // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings. #ifdef IMGUI_USE_WCHAR32 // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16] typedef ImWchar32 ImWchar; #else @@ -249,14 +258,15 @@ typedef void (*ImGuiMemFreeFunc)(void* ptr, void* user_data); // ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type] // This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type. +// Add '#define IMGUI_DEFINE_MATH_OPERATORS' in your imconfig.h file to benefit from courtesy maths operators for those types. IM_MSVC_RUNTIME_CHECKS_OFF struct ImVec2 { float x, y; constexpr ImVec2() : x(0.0f), y(0.0f) { } constexpr ImVec2(float _x, float _y) : x(_x), y(_y) { } - float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. - float& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return ((float*)(void*)(char*)this)[idx]; } // We very rarely use this [] operator, so the assert overhead is fine. + float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return ((const float*)(const void*)(const char*)this)[idx]; } #ifdef IM_VEC2_CLASS_EXTRA IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif @@ -292,7 +302,7 @@ namespace ImGui // Main IMGUI_API ImGuiIO& GetIO(); // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) - IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleCol(), PushStyleVar() to modify style mid-frame! + IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleColor(), PushStyleVar() to modify style mid-frame! IMGUI_API void NewFrame(); // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame(). IMGUI_API void EndFrame(); // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all! IMGUI_API void Render(); // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData(). @@ -302,7 +312,7 @@ namespace ImGui IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc. IMGUI_API void ShowDebugLogWindow(bool* p_open = NULL); // create Debug Log window. display a simplified log of important dear imgui events. - IMGUI_API void ShowStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. + IMGUI_API void ShowIDStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID. IMGUI_API void ShowAboutWindow(bool* p_open = NULL); // create About window. display Dear ImGui version, credits and build/system information. IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. @@ -323,23 +333,33 @@ namespace ImGui // Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin(). // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting // anything to the window. Always call a matching End() for each Begin() call, regardless of its return value! - // [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu, - // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function - // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] + // [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions + // such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding + // BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] // - Note that the bottom of window stack always contains a window called "Debug". IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0, void* close_callback = nullptr); IMGUI_API void End(); // Child Windows // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child. - // - For each independent axis of 'size': ==0.0f: use remaining host window size / >0.0f: fixed size / <0.0f: use remaining window size minus abs(size) / Each axis can use a different mode, e.g. ImVec2(0,400). - // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting anything to the window. - // Always call a matching EndChild() for each BeginChild() call, regardless of its return value. - // [Important: due to legacy reason, this is inconsistent with most other functions such as BeginMenu/EndMenu, - // BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding BeginXXX function - // returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] - IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0); - IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), bool border = false, ImGuiWindowFlags flags = 0); + // - Before 1.90 (November 2023), the "ImGuiChildFlags child_flags = 0" parameter was "bool border = false". + // This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Border == true. + // Consider updating your old code: + // BeginChild("Name", size, false) -> Begin("Name", size, 0); or Begin("Name", size, ImGuiChildFlags_None); + // BeginChild("Name", size, true) -> Begin("Name", size, ImGuiChildFlags_Border); + // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)): + // == 0.0f: use remaining parent window size for this axis. + // > 0.0f: use specified size for this axis. + // < 0.0f: right/bottom-align to specified distance from available content boundaries. + // - Specifying ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY makes the sizing automatic based on child contents. + // Combining both ImGuiChildFlags_AutoResizeX _and_ ImGuiChildFlags_AutoResizeY defeats purpose of a scrolling region and is NOT recommended. + // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting + // anything to the window. Always call a matching EndChild() for each BeginChild() call, regardless of its return value. + // [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions + // such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding + // BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.] + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0); + IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0); IMGUI_API void EndChild(); // Windows Utilities @@ -347,10 +367,10 @@ namespace ImGui IMGUI_API bool IsWindowAppearing(); IMGUI_API bool IsWindowCollapsed(); IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. - IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! + IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives - IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) - IMGUI_API ImVec2 GetWindowSize(); // get current window size + IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (note: it is unlikely you need to use this. Consider using current layout pos instead, GetCursorScreenPos()) + IMGUI_API ImVec2 GetWindowSize(); // get current window size (note: it is unlikely you need to use this. Consider using GetCursorScreenPos() and e.g. GetContentRegionAvail() instead) IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) @@ -358,7 +378,7 @@ namespace ImGui // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() - IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Sizes will be rounded down. Use callback to apply non-trivial programmatic constraints. + IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use 0.0f or FLT_MAX if you don't want limits. Use -1 for both min and max of same axis to preserve current size (which itself is a constraint). Use callback to apply non-trivial programmatic constraints. IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin() IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin() @@ -427,16 +447,28 @@ namespace ImGui IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList - IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList + IMGUI_API ImU32 GetColorU32(ImU32 col, float alpha_mul = 1.0f); // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. - // Cursor / Layout + // Layout cursor positioning // - By "cursor" we mean the current output position. // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down. // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget. // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API: - // Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos() - // Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. + // - Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward. + // - Window-local coordinates: SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos() + // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates. + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (prefer using this, also more useful to work with ImDrawList API). + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates + IMGUI_API ImVec2 GetCursorPos(); // [window-local] cursor position in window coordinates (relative to window position) + IMGUI_API float GetCursorPosX(); // [window-local] " + IMGUI_API float GetCursorPosY(); // [window-local] " + IMGUI_API void SetCursorPos(const ImVec2& local_pos); // [window-local] " + IMGUI_API void SetCursorPosX(float local_x); // [window-local] " + IMGUI_API void SetCursorPosY(float local_y); // [window-local] " + IMGUI_API ImVec2 GetCursorStartPos(); // [window-local] initial cursor position, in window coordinates + + // Other layout functions IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. IMGUI_API void SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f); // call between widgets or groups to layout them horizontally. X position given in window coordinates. IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in a horizontal-layout context. @@ -446,15 +478,6 @@ namespace ImGui IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0 IMGUI_API void BeginGroup(); // lock horizontal starting position IMGUI_API void EndGroup(); // unlock horizontal starting position + capture the whole group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) - IMGUI_API ImVec2 GetCursorPos(); // cursor position in window coordinates (relative to window position) - IMGUI_API float GetCursorPosX(); // (some functions are using window-relative coordinates, such as: GetCursorPos, GetCursorStartPos, GetContentRegionMax, GetWindowContentRegion* etc. - IMGUI_API float GetCursorPosY(); // other functions such as GetCursorScreenPos or everything in ImDrawList:: - IMGUI_API void SetCursorPos(const ImVec2& local_pos); // are using the main, absolute coordinate system. - IMGUI_API void SetCursorPosX(float local_x); // GetWindowPos() + GetCursorPos() == GetCursorScreenPos() etc.) - IMGUI_API void SetCursorPosY(float local_y); // - IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position in window coordinates - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute coordinates (useful to work with ImDrawList API). generally top-left == GetMainViewport()->Pos == (0,0) in single viewport mode, and bottom-right == GetMainViewport()->Pos+Size == io.DisplaySize in single-viewport mode. - IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute coordinates IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) IMGUI_API float GetTextLineHeight(); // ~ FontSize IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) @@ -462,7 +485,7 @@ namespace ImGui IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) // ID stack/scopes - // Read the FAQ (docs/FAQ.md or http://dearimgui.org/faq) for more details about how ID are handled in dear imgui. + // Read the FAQ (docs/FAQ.md or http://dearimgui.com/faq) for more details about how ID are handled in dear imgui. // - Those questions are answered and impacted by understanding of the ID stack system: // - "Q: Why is my widget not reacting when I click on it?" // - "Q: How can I have widgets with an empty label?" @@ -501,7 +524,7 @@ namespace ImGui // - Most widgets return true when the value has been changed or when pressed/selected // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state. IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0, 0)); // button - IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text + IMGUI_API bool SmallButton(const char* label); // button with (FramePadding.y == 0) to easily embed within text IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape IMGUI_API bool Checkbox(const char* label, bool* v); @@ -514,8 +537,10 @@ namespace ImGui // Widgets: Images // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples - IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)); - IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); + // - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. + // - Note that Image() may add +2.0f to provided size if a border is visible, ImageButton() adds style.FramePadding*2.0f to provided size. + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)); + IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Widgets: Combo Box (Dropdown) // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. @@ -524,7 +549,7 @@ namespace ImGui IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" - IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); + IMGUI_API bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1); // Widgets: Drag Sliders // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. @@ -613,9 +638,9 @@ namespace ImGui IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); - IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired. + IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushID(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired. IMGUI_API void TreePush(const void* ptr_id); // " - IMGUI_API void TreePop(); // ~ Unindent()+PopId() + IMGUI_API void TreePop(); // ~ Unindent()+PopID() IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). IMGUI_API bool CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header. @@ -628,22 +653,22 @@ namespace ImGui IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. // Widgets: List Boxes - // - This is essentially a thin wrapper to using BeginChild/EndChild with some stylistic changes. - // - The BeginListBox()/EndListBox() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() or any items. + // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. + // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items. // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analoguous to how Combos are created. // - Choose frame width: size.x > 0.0f: custom / size.x < 0.0f or -FLT_MIN: right-align / size.x = 0.0f (default): use current ItemWidth // - Choose frame height: size.y > 0.0f: custom / size.y < 0.0f or -FLT_MIN: bottom-align / size.y = 0.0f (default): arbitrary default height which can fit ~7 items IMGUI_API bool BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region IMGUI_API void EndListBox(); // only call EndListBox() if BeginListBox() returned true! IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); - IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1); // Widgets: Data Plotting // - Consider using ImPlot (https://github.com/epezent/implot) which is much better! IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); IMGUI_API void PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float)); - IMGUI_API void PlotHistogram(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); + IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0)); // Widgets: Value() Helpers. // - Those are merely shortcut to calling Text() with a format string. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) @@ -667,12 +692,21 @@ namespace ImGui IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL // Tooltips - // - Tooltip are windows following the mouse. They do not take focus away. - IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). - IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip() returns true! - IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). + // - Tooltips are windows following the mouse. They do not take focus away. + // - A tooltip window can contain items of any types. SetTooltip() is a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom. + IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. + IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true! + IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip(). IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Tooltips: helpers for showing a tooltip when hovering an item + // - BeginItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip) && BeginTooltip())' idiom. + // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) { SetTooltip(...); }' idiom. + // - Where 'ImGuiHoveredFlags_ForTooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + IMGUI_API bool BeginItemTooltip(); // begin/append a tooltip window if preceding item was hovered. + IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceeding item was hovered. override any previous call to SetTooltip(). + IMGUI_API void SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Popups, Modals // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. @@ -681,9 +715,7 @@ namespace ImGui // - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered(). // - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack. // This is sometimes leading to confusing mistakes. May rework this in the future. - - // Popups: begin/end functions - // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window. + // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards if returned true. ImGuiWindowFlags are forwarded to the window. // - BeginPopupModal(): block every interaction behind the window, cannot be closed by user, add a dimming background, has a title bar. IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. @@ -733,12 +765,10 @@ namespace ImGui // TableNextColumn() will automatically wrap-around into the next row if needed. // - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column! // - Summary of possible call flow: - // -------------------------------------------------------------------------------------------------------- - // TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1") // OK - // TableNextRow() -> TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK - // TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK: TableNextColumn() automatically gets to next row! - // TableNextRow() -> Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear! - // -------------------------------------------------------------------------------------------------------- + // - TableNextRow() -> TableSetColumnIndex(0) -> Text("Hello 0") -> TableSetColumnIndex(1) -> Text("Hello 1") // OK + // - TableNextRow() -> TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK + // - TableNextColumn() -> Text("Hello 0") -> TableNextColumn() -> Text("Hello 1") // OK: TableNextColumn() automatically gets to next row! + // - TableNextRow() -> Text("Hello 0") // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear! // - 5. Call EndTable() IMGUI_API bool BeginTable(const char* str_id, int column, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f); IMGUI_API void EndTable(); // only call EndTable() if BeginTable() returns true! @@ -756,8 +786,9 @@ namespace ImGui // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled. IMGUI_API void TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0); IMGUI_API void TableSetupScrollFreeze(int cols, int rows); // lock columns/rows so they stay visible when scrolled. - IMGUI_API void TableHeadersRow(); // submit all headers cells based on data provided to TableSetupColumn() + submit context menu IMGUI_API void TableHeader(const char* label); // submit one header cell manually (rarely used) + IMGUI_API void TableHeadersRow(); // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu + IMGUI_API void TableAngledHeadersRow(); // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW. // Tables: Sorting & Miscellaneous functions // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting. @@ -815,7 +846,7 @@ namespace ImGui IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! - IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. may return NULL. use ImGuiPayload::IsDataType() to test for the payload type. + IMGUI_API const ImGuiPayload* GetDragDropPayload(); // peek directly into the current payload from anywhere. returns NULL when drag and drop is finished or inactive. use ImGuiPayload::IsDataType() to test for the payload type. // Disabling [BETA API] // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) @@ -834,6 +865,9 @@ namespace ImGui IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. + // Overlapping mode + IMGUI_API void SetNextItemAllowOverlap(); // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this. + // Item/Widgets Utilities and Query Functions // - Most of the functions are referring to the previous Item that has been submitted. // - See Demo Window under "Widgets->Querying Status" for an interactive visualization of most of those functions. @@ -854,7 +888,6 @@ namespace ImGui IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item - IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. // Viewports // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -875,8 +908,6 @@ namespace ImGui IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API ImGuiStorage* GetStateStorage(); - IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame - IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) // Text Utilities IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); @@ -895,6 +926,7 @@ namespace ImGui IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)? + IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord); // was key chord (mods + key) pressed, e.g. you can pass 'ImGuiMod_Ctrl | ImGuiKey_S' as a key-chord. This doesn't do any routing or focus check, please consider using Shortcut() function instead. IMGUI_API int GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared. IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call. @@ -935,7 +967,10 @@ namespace ImGui IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. // Debug Utilities + // - Your main debugging friend is the ShowMetricsWindow() function, which is also accessible from Demo->Tools->Metrics Debugger IMGUI_API void DebugTextEncoding(const char* text); + IMGUI_API void DebugFlashStyleColor(ImGuiCol idx); + IMGUI_API void DebugStartItemPicker(); IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro. // Memory Allocators @@ -974,21 +1009,50 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus) ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) - ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) - ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window - ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) - ImGuiWindowFlags_UnsavedDocument = 1 << 20, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + ImGuiWindowFlags_NoNavInputs = 1 << 16, // No gamepad/keyboard navigation within the window + ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) + ImGuiWindowFlags_UnsavedDocument = 1 << 18, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, + ImGuiWindowFlags_OverlayVerticalScrollbar = 1 << 19, // Draw the vertical scrollbar as an overlay (scrollbar space will not take up content region space) + ImGuiWindowFlags_OverlayHorizontalScrollbar = 1 << 20, // Draw the horizontal scrollbar as an overlay (scrollbar space will not take up content region space) + // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. + ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] On child window: share focus scope, allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows. ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu() + + // Obsolete names +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30, // Obsoleted in 1.90: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call. +#endif +}; + +// Flags for ImGui::BeginChild() +// (Legacy: bit 0 must always correspond to ImGuiChildFlags_Border to be backward compatible with old API using 'bool border = false'. +// About using AutoResizeX/AutoResizeY flags: +// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see "Demo->Child->Auto-resize with Constraints"). +// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing. +// - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped. +// While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional "resizing after becoming visible again" glitch. +// - You may also use ImGuiChildFlags_AlwaysAutoResize to force an update even when child window is not in view. +// HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping. +enum ImGuiChildFlags_ +{ + ImGuiChildFlags_None = 0, + ImGuiChildFlags_Border = 1 << 0, // Show an outer border and enable WindowPadding. (IMPORTANT: this is always == 1 == true for legacy reason) + ImGuiChildFlags_AlwaysUseWindowPadding = 1 << 1, // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense) + ImGuiChildFlags_ResizeX = 1 << 2, // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags) + ImGuiChildFlags_ResizeY = 1 << 3, // Allow resize from bottom border (layout direction). " + ImGuiChildFlags_AutoResizeX = 1 << 4, // Enable auto-resizing width. Read "IMPORTANT: Size measurement" details above. + ImGuiChildFlags_AutoResizeY = 1 << 5, // Enable auto-resizing height. Read "IMPORTANT: Size measurement" details above. + ImGuiChildFlags_AlwaysAutoResize = 1 << 6, // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED. + ImGuiChildFlags_FrameStyle = 1 << 7, // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding. }; // Flags for ImGui::InputText() @@ -1017,8 +1081,8 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) ImGuiInputTextFlags_EscapeClearsAll = 1 << 20, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) - ImGuiInputTextFlags_AutoCaretEnd = 1 << 21 // Move the input text cursor automatically to the end of the buffer on initial focus. - + ImGuiInputTextFlags_AutoCaretEnd = 1 << 21 // Move the input text cursor automatically to the end of the buffer on initial focus. + // Obsolete names //ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior }; @@ -1029,25 +1093,30 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_None = 0, ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected ImGuiTreeNodeFlags_Framed = 1 << 1, // Draw frame with background (e.g. for CollapsingHeader) - ImGuiTreeNodeFlags_AllowItemOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one + ImGuiTreeNodeFlags_AllowOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). - ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow + ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag! ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line. In the future we may refactor the hit system to be front-to-back, allowing natural overlaps and then this can become the default. ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (bypass the indented area). - ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) - //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 14, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible + ImGuiTreeNodeFlags_SpanAllColumns = 1 << 13, // Frame will span all columns of its container table (text will still fit in current column) + ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 14, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) + //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 15, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog, + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiTreeNodeFlags_AllowItemOverlap = ImGuiTreeNodeFlags_AllowOverlap, // Renamed in 1.89.7 +#endif }; // Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions. -// - To be backward compatible with older API which took an 'int mouse_button = 1' argument, we need to treat -// small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags. +// - To be backward compatible with older API which took an 'int mouse_button = 1' argument instead of 'ImGuiPopupFlags flags', +// we need to treat small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags. // It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags. // - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0. // IMPORTANT: because the default parameter is 1 (==ImGuiPopupFlags_MouseButtonRight), if you rely on the default parameter @@ -1061,10 +1130,12 @@ enum ImGuiPopupFlags_ ImGuiPopupFlags_MouseButtonMiddle = 2, // For BeginPopupContext*(): open on Middle Mouse release. Guaranteed to always be == 2 (same as ImGuiMouseButton_Middle) ImGuiPopupFlags_MouseButtonMask_ = 0x1F, ImGuiPopupFlags_MouseButtonDefault_ = 1, - ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack - ImGuiPopupFlags_NoOpenOverItems = 1 << 6, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space - ImGuiPopupFlags_AnyPopupId = 1 << 7, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. - ImGuiPopupFlags_AnyPopupLevel = 1 << 8, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) + ImGuiPopupFlags_NoReopen = 1 << 5, // For OpenPopup*(), BeginPopupContext*(): don't reopen same popup if already open (won't reposition, won't reinitialize navigation) + //ImGuiPopupFlags_NoReopenAlwaysNavInit = 1 << 6, // For OpenPopup*(), BeginPopupContext*(): focus and initialize navigation even when not reopening. + ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 7, // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack + ImGuiPopupFlags_NoOpenOverItems = 1 << 8, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space + ImGuiPopupFlags_AnyPopupId = 1 << 10, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. + ImGuiPopupFlags_AnyPopupLevel = 1 << 11, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel, }; @@ -1073,10 +1144,14 @@ enum ImGuiSelectableFlags_ { ImGuiSelectableFlags_None = 0, ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this doesn't close parent popup window - ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) + ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Frame will span all columns of its container table (text will still fit in current column) ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text - ImGuiSelectableFlags_AllowItemOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one + ImGuiSelectableFlags_AllowOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiSelectableFlags_AllowItemOverlap = ImGuiSelectableFlags_AllowOverlap, // Renamed in 1.89.7 +#endif }; // Flags for ImGui::BeginCombo() @@ -1090,6 +1165,7 @@ enum ImGuiComboFlags_ ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button + ImGuiComboFlags_WidthFitPreview = 1 << 7, // Width dynamically calculated from preview contents ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest, }; @@ -1100,7 +1176,7 @@ enum ImGuiTabBarFlags_ ImGuiTabBarFlags_Reorderable = 1 << 0, // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list ImGuiTabBarFlags_AutoSelectNewTabs = 1 << 1, // Automatically select new tabs when they appear ImGuiTabBarFlags_TabListPopupButton = 1 << 2, // Disable buttons to open the tab list popup - ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabBarFlags_NoCloseWithMiddleMouseButton = 1 << 3, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. ImGuiTabBarFlags_NoTabListScrollingButtons = 1 << 4, // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll) ImGuiTabBarFlags_NoTooltip = 1 << 5, // Disable tooltips when hovering a tab ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 6, // Resize tabs when they don't fit @@ -1113,147 +1189,15 @@ enum ImGuiTabBarFlags_ enum ImGuiTabItemFlags_ { ImGuiTabItemFlags_None = 0, - ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + ImGuiTabItemFlags_UnsavedDocument = 1 << 0, // Display a dot next to the title + set ImGuiTabItemFlags_NoAssumedClosure. ImGuiTabItemFlags_SetSelected = 1 << 1, // Trigger flag to programmatically make the tab selected when calling BeginTabItem() - ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. - ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem() + ImGuiTabItemFlags_NoCloseWithMiddleMouseButton = 1 << 2, // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You may handle this behavior manually on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false. + ImGuiTabItemFlags_NoPushId = 1 << 3, // Don't call PushID()/PopID() on BeginTabItem()/EndTabItem() ImGuiTabItemFlags_NoTooltip = 1 << 4, // Disable tooltip for the given tab ImGuiTabItemFlags_NoReorder = 1 << 5, // Disable reordering this tab or having another tab cross over this tab ImGuiTabItemFlags_Leading = 1 << 6, // Enforce the tab position to the left of the tab bar (after the tab list popup button) ImGuiTabItemFlags_Trailing = 1 << 7, // Enforce the tab position to the right of the tab bar (before the scrolling buttons) -}; - -// Flags for ImGui::BeginTable() -// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect. -// Read comments/demos carefully + experiment with live demos to get acquainted with them. -// - The DEFAULT sizing policies are: -// - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize. -// - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off. -// - When ScrollX is off: -// - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight. -// - Columns sizing policy allowed: Stretch (default), Fixed/Auto. -// - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all). -// - Stretch Columns will share the remaining width according to their respective weight. -// - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors. -// The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. -// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing). -// - When ScrollX is on: -// - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed -// - Columns sizing policy allowed: Fixed/Auto mostly. -// - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed. -// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop. -// - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable(). -// If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again. -// - Read on documentation at the top of imgui_tables.cpp for details. -enum ImGuiTableFlags_ -{ - // Features - ImGuiTableFlags_None = 0, - ImGuiTableFlags_Resizable = 1 << 0, // Enable resizing columns. - ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) - ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu. - ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. - ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file. - ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). - // Decorations - ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) - ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows. - ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom. - ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns. - ImGuiTableFlags_BordersOuterV = 1 << 10, // Draw vertical borders on the left and right sides. - ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders. - ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders. - ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders. - ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders. - ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders. - ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style - ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style - // Sizing Policy (read above for defaults) - ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. - ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. - ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths. - ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn(). - // Sizing Extra Options - ImGuiTableFlags_NoHostExtendX = 1 << 16, // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. - ImGuiTableFlags_NoHostExtendY = 1 << 17, // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. - ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. - ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. - // Clipping - ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). - // Padding - ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers. - ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outermost padding. - ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). - // Scrolling - ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX. - ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. - // Sorting - ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). - ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). - - // [Internal] Combinations and masks - ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, -}; - -// Flags for ImGui::TableSetupColumn() -enum ImGuiTableColumnFlags_ -{ - // Input configuration flags - ImGuiTableColumnFlags_None = 0, - ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) - ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column. - ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column. - ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). - ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). - ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. - ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. - ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. - ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). - ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). - ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. - ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. - ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit label for this column. Convenient for some small columns. Name will still appear in context menu. - ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. - ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). - ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. - ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). - ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. - - // Output status flags, read-only via TableGetColumnFlags() - ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. - ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling. - ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs - ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse - - // [Internal] Combinations and masks - ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, - ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable, - ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered, - ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge) -}; - -// Flags for ImGui::TableNextRow() -enum ImGuiTableRowFlags_ -{ - ImGuiTableRowFlags_None = 0, - ImGuiTableRowFlags_Headers = 1 << 0, // Identify header row (set default background color + width of its contents accounted differently for auto column width) -}; - -// Enum for ImGui::TableSetBgColor() -// Background colors are rendering in 3 layers: -// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set. -// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set. -// - Layer 2: draw with CellBg color if set. -// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color. -// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows. -// If you set the color of RowBg0 target, your color will override the existing RowBg0 color. -// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color. -enum ImGuiTableBgTarget_ -{ - ImGuiTableBgTarget_None = 0, - ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) - ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking) - ImGuiTableBgTarget_CellBg = 3, // Set cell background color (top-most color) + ImGuiTabItemFlags_NoAssumedClosure = 1 << 8, // Tab is selected when trying to close + closure is not immediately assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. }; // Flags for ImGui::IsWindowFocused() @@ -1282,16 +1226,30 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window - ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled - ImGuiHoveredFlags_NoNavOverride = 1 << 10, // Disable using gamepad/keyboard navigation state when active, always query mouse. + ImGuiHoveredFlags_AllowWhenOverlappedByItem = 1 << 8, // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item. + ImGuiHoveredFlags_AllowWhenOverlappedByWindow = 1 << 9, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window. + ImGuiHoveredFlags_AllowWhenDisabled = 1 << 10, // IsItemHovered() only: Return true even if the item is disabled + ImGuiHoveredFlags_NoNavOverride = 1 << 11, // IsItemHovered() only: Disable using gamepad/keyboard navigation state when active, always query mouse + ImGuiHoveredFlags_AllowWhenOverlapped = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow, ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, - // Hovering delays (for tooltips) - ImGuiHoveredFlags_DelayNormal = 1 << 11, // Return true after io.HoverDelayNormal elapsed (~0.30 sec) - ImGuiHoveredFlags_DelayShort = 1 << 12, // Return true after io.HoverDelayShort elapsed (~0.10 sec) - ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) + // Tooltips mode + // - typically used in IsItemHovered() + SetTooltip() sequence. + // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior. + // e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often. + // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay. + ImGuiHoveredFlags_ForTooltip = 1 << 12, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. + + // (Advanced) Mouse Hovering delays. + // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags. + // - use those if you need specific overrides. + ImGuiHoveredFlags_Stationary = 1 << 13, // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay. + ImGuiHoveredFlags_DelayNone = 1 << 14, // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this. + ImGuiHoveredFlags_DelayShort = 1 << 15, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_DelayNormal = 1 << 16, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_NoSharedDelay = 1 << 17, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() @@ -1351,11 +1309,17 @@ enum ImGuiSortDirection_ ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. }; +// Since 1.90, defining IMGUI_DISABLE_OBSOLETE_FUNCTIONS automatically defines IMGUI_DISABLE_OBSOLETE_KEYIO as well. +#if defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(IMGUI_DISABLE_OBSOLETE_KEYIO) +#define IMGUI_DISABLE_OBSOLETE_KEYIO +#endif + // A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values. // All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87). // Since >= 1.89 we increased typing (went from int to enum), some legacy code may need a cast to ImGuiKey. // Read details about the 1.87 and 1.89 transition : https://github.com/ocornut/imgui/issues/4921 // Note that "Keys" related to physical keys and are not the same concept as input "Characters", the later are submitted via io.AddInputCharacter(). +// The keyboard key enum values are named after the keys on a standard US keyboard, and on other keyboard types the keys reported may not match the keycaps. enum ImGuiKey : int { // Keyboard @@ -1384,6 +1348,8 @@ enum ImGuiKey : int ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z, ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6, ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12, + ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18, + ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24, ImGuiKey_Apostrophe, // ' ImGuiKey_Comma, // , ImGuiKey_Minus, // - @@ -1409,9 +1375,11 @@ enum ImGuiKey : int ImGuiKey_KeypadAdd, ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, + ImGuiKey_AppBack, // Available on some keyboard/mouses. Often referred as "Browser Back" + ImGuiKey_AppForward, // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION ACTION - // (download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets) + // (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets) ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows) @@ -1477,7 +1445,7 @@ enum ImGuiKey : int #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89 - ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 + //ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 #endif }; @@ -1532,15 +1500,15 @@ enum ImGuiCol_ ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive, - ImGuiCol_TitleBg, - ImGuiCol_TitleBgActive, - ImGuiCol_TitleBgCollapsed, + ImGuiCol_TitleBg, // Title bar + ImGuiCol_TitleBgActive, // Title bar when focused + ImGuiCol_TitleBgCollapsed, // Title bar when collapsed ImGuiCol_MenuBarBg, ImGuiCol_ScrollbarBg, ImGuiCol_ScrollbarGrab, ImGuiCol_ScrollbarGrabHovered, ImGuiCol_ScrollbarGrabActive, - ImGuiCol_CheckMark, + ImGuiCol_CheckMark, // Checkbox tick and RadioButton circle ImGuiCol_SliderGrab, ImGuiCol_SliderGrabActive, ImGuiCol_Button, @@ -1611,6 +1579,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_GrabMinSize, // float GrabMinSize ImGuiStyleVar_GrabRounding, // float GrabRounding ImGuiStyleVar_TabRounding, // float TabRounding + ImGuiStyleVar_TabBarBorderSize, // float TabBarBorderSize ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign ImGuiStyleVar_SelectableTextAlign, // ImVec2 SelectableTextAlign ImGuiStyleVar_SeparatorTextBorderSize,// float SeparatorTextBorderSize @@ -1719,7 +1688,19 @@ enum ImGuiMouseCursor_ ImGuiMouseCursor_COUNT }; -// Enumeration for ImGui::SetWindow***(), SetNextWindow***(), SetNextItem***() functions +// Enumeration for AddMouseSourceEvent() actual source of Mouse Input data. +// Historically we use "Mouse" terminology everywhere to indicate pointer data, e.g. MousePos, IsMousePressed(), io.AddMousePosEvent() +// But that "Mouse" data can come from different source which occasionally may be useful for application to know about. +// You can submit a change of pointer type using io.AddMouseSourceEvent(). +enum ImGuiMouseSource : int +{ + ImGuiMouseSource_Mouse = 0, // Input is coming from an actual mouse. + ImGuiMouseSource_TouchScreen, // Input is coming from a touch screen (no hovering prior to initial press, less precise initial press aiming, dual-axis wheeling possible). + ImGuiMouseSource_Pen, // Input is coming from a pressure/magnetic pen (often used in conjunction with high-sampling rates). + ImGuiMouseSource_COUNT +}; + +// Enumeration for ImGui::SetNextWindow***(), SetWindow***(), SetNextItem***() functions // Represent a condition. // Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always. enum ImGuiCond_ @@ -1731,6 +1712,170 @@ enum ImGuiCond_ ImGuiCond_Appearing = 1 << 3, // Set the variable if the object/window is appearing after being hidden/inactive (or the first time) }; +//----------------------------------------------------------------------------- +// [SECTION] Tables API flags and structures (ImGuiTableFlags, ImGuiTableColumnFlags, ImGuiTableRowFlags, ImGuiTableBgTarget, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs) +//----------------------------------------------------------------------------- + +// Flags for ImGui::BeginTable() +// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect. +// Read comments/demos carefully + experiment with live demos to get acquainted with them. +// - The DEFAULT sizing policies are: +// - Default to ImGuiTableFlags_SizingFixedFit if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize. +// - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off. +// - When ScrollX is off: +// - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight. +// - Columns sizing policy allowed: Stretch (default), Fixed/Auto. +// - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all). +// - Stretch Columns will share the remaining width according to their respective weight. +// - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors. +// The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns. +// (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing). +// - When ScrollX is on: +// - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed +// - Columns sizing policy allowed: Fixed/Auto mostly. +// - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed. +// - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop. +// - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable(). +// If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again. +// - Read on documentation at the top of imgui_tables.cpp for details. +enum ImGuiTableFlags_ +{ + // Features + ImGuiTableFlags_None = 0, + ImGuiTableFlags_Resizable = 1 << 0, // Enable resizing columns. + ImGuiTableFlags_Reorderable = 1 << 1, // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers) + ImGuiTableFlags_Hideable = 1 << 2, // Enable hiding/disabling columns in context menu. + ImGuiTableFlags_Sortable = 1 << 3, // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate. + ImGuiTableFlags_NoSavedSettings = 1 << 4, // Disable persisting columns order, width and sort settings in the .ini file. + ImGuiTableFlags_ContextMenuInBody = 1 << 5, // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow(). + // Decorations + ImGuiTableFlags_RowBg = 1 << 6, // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually) + ImGuiTableFlags_BordersInnerH = 1 << 7, // Draw horizontal borders between rows. + ImGuiTableFlags_BordersOuterH = 1 << 8, // Draw horizontal borders at the top and bottom. + ImGuiTableFlags_BordersInnerV = 1 << 9, // Draw vertical borders between columns. + ImGuiTableFlags_BordersOuterV = 1 << 10, // Draw vertical borders on the left and right sides. + ImGuiTableFlags_BordersH = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders. + ImGuiTableFlags_BordersV = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders. + ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders. + ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders. + ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders. + ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style + ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style + // Sizing Policy (read above for defaults) + ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. + ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. + ImGuiTableFlags_SizingStretchProp = 3 << 13, // Columns default to _WidthStretch with default weights proportional to each columns contents widths. + ImGuiTableFlags_SizingStretchSame = 4 << 13, // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn(). + // Sizing Extra Options + ImGuiTableFlags_NoHostExtendX = 1 << 16, // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. + ImGuiTableFlags_NoHostExtendY = 1 << 17, // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible. + ImGuiTableFlags_NoKeepColumnsVisible = 1 << 18, // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable. + ImGuiTableFlags_PreciseWidths = 1 << 19, // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth. + // Clipping + ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). + // Padding + ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers. + ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outermost padding. + ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). + // Scrolling + ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX. + ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. + // Sorting + ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). + ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). + // Miscellaneous + ImGuiTableFlags_HighlightHoveredColumn = 1 << 28, // Highlight column headers when hovered (may evolve into a fuller highlight) + + // [Internal] Combinations and masks + ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, +}; + +// Flags for ImGui::TableSetupColumn() +enum ImGuiTableColumnFlags_ +{ + // Input configuration flags + ImGuiTableColumnFlags_None = 0, + ImGuiTableColumnFlags_Disabled = 1 << 0, // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state) + ImGuiTableColumnFlags_DefaultHide = 1 << 1, // Default as a hidden/disabled column. + ImGuiTableColumnFlags_DefaultSort = 1 << 2, // Default as a sorting column. + ImGuiTableColumnFlags_WidthStretch = 1 << 3, // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp). + ImGuiTableColumnFlags_WidthFixed = 1 << 4, // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable). + ImGuiTableColumnFlags_NoResize = 1 << 5, // Disable manual resizing. + ImGuiTableColumnFlags_NoReorder = 1 << 6, // Disable manual reordering this column, this will also prevent other columns from crossing over this column. + ImGuiTableColumnFlags_NoHide = 1 << 7, // Disable ability to hide/disable this column. + ImGuiTableColumnFlags_NoClip = 1 << 8, // Disable clipping for this column (all NoClip columns will render in a same draw command). + ImGuiTableColumnFlags_NoSort = 1 << 9, // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table). + ImGuiTableColumnFlags_NoSortAscending = 1 << 10, // Disable ability to sort in the ascending direction. + ImGuiTableColumnFlags_NoSortDescending = 1 << 11, // Disable ability to sort in the descending direction. + ImGuiTableColumnFlags_NoHeaderLabel = 1 << 12, // TableHeadersRow() will not submit horizontal label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers. + ImGuiTableColumnFlags_NoHeaderWidth = 1 << 13, // Disable header text width contribution to automatic column width. + ImGuiTableColumnFlags_PreferSortAscending = 1 << 14, // Make the initial sort direction Ascending when first sorting on this column (default). + ImGuiTableColumnFlags_PreferSortDescending = 1 << 15, // Make the initial sort direction Descending when first sorting on this column. + ImGuiTableColumnFlags_IndentEnable = 1 << 16, // Use current Indent value when entering cell (default for column 0). + ImGuiTableColumnFlags_IndentDisable = 1 << 17, // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored. + ImGuiTableColumnFlags_AngledHeader = 1 << 18, // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row. + + // Output status flags, read-only via TableGetColumnFlags() + ImGuiTableColumnFlags_IsEnabled = 1 << 24, // Status: is enabled == not hidden by user/api (referred to as "Hide" in _DefaultHide and _NoHide) flags. + ImGuiTableColumnFlags_IsVisible = 1 << 25, // Status: is visible == is enabled AND not clipped by scrolling. + ImGuiTableColumnFlags_IsSorted = 1 << 26, // Status: is currently part of the sort specs + ImGuiTableColumnFlags_IsHovered = 1 << 27, // Status: is hovered by mouse + + // [Internal] Combinations and masks + ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, + ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable, + ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered, + ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge) +}; + +// Flags for ImGui::TableNextRow() +enum ImGuiTableRowFlags_ +{ + ImGuiTableRowFlags_None = 0, + ImGuiTableRowFlags_Headers = 1 << 0, // Identify header row (set default background color + width of its contents accounted differently for auto column width) +}; + +// Enum for ImGui::TableSetBgColor() +// Background colors are rendering in 3 layers: +// - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set. +// - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set. +// - Layer 2: draw with CellBg color if set. +// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color. +// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows. +// If you set the color of RowBg0 target, your color will override the existing RowBg0 color. +// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color. +enum ImGuiTableBgTarget_ +{ + ImGuiTableBgTarget_None = 0, + ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) + ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking) + ImGuiTableBgTarget_CellBg = 3, // Set cell background color (top-most color) +}; + +// Sorting specifications for a table (often handling sort specs for a single column, occasionally more) +// Obtained by calling TableGetSortSpecs(). +// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time. +// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame! +struct ImGuiTableSortSpecs +{ + const ImGuiTableColumnSortSpecs* Specs; // Pointer to sort spec array. + int SpecsCount; // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled. + bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag. + + ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); } +}; + +// Sorting specification for one column of a table (sizeof == 12 bytes) +struct ImGuiTableColumnSortSpecs +{ + ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call) + ImS16 ColumnIndex; // Index of the column + ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) + ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending + + ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); } +}; + //----------------------------------------------------------------------------- // [SECTION] Helpers: Memory allocations macros, ImVector<> //----------------------------------------------------------------------------- @@ -1820,6 +1965,7 @@ struct ImVector inline bool contains(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } inline T* find(const T& v) { T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; } inline const T* find(const T& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; } + inline int find_index(const T& v) const { const T* data_end = Data + Size; const T* it = find(v); if (it == data_end) return -1; const ptrdiff_t off = it - Data; return (int)off; } inline bool find_erase(const T& v) { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; } inline bool find_erase_unsorted(const T& v) { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; } inline int index_from_ptr(const T* it) const { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; } @@ -1853,7 +1999,7 @@ struct ImGuiStyle float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines. ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). - ImVec2 CellPadding; // Padding within a table cell + ImVec2 CellPadding; // Padding within a table cell. CellPadding.y may be altered between different rows. ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1). @@ -1865,6 +2011,8 @@ struct ImGuiStyle float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. float TabBorderSize; // Thickness of border around tabs. float TabMinWidthForCloseButton; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. + float TabBarBorderSize; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. + float TableAngledHeadersAngle; // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees). ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. @@ -1881,6 +2029,14 @@ struct ImGuiStyle float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. ImVec4 Colors[ImGuiCol_COUNT]; + // Behaviors + // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO) + float HoverStationaryDelay; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. + float HoverDelayShort; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. + float HoverDelayNormal; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. + ImGuiHoveredFlags HoverFlagsForTooltipNav; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. + IMGUI_API ImGuiStyle(); IMGUI_API void ScaleAllSizes(float scale_factor); }; @@ -1915,13 +2071,6 @@ struct ImGuiIO float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - float HoverDelayNormal; // = 0.30 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. - float HoverDelayShort; // = 0.10 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. void* UserData; // = NULL // Store your own data. ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. @@ -1941,13 +2090,38 @@ struct ImGuiIO bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. + // Inputs Behaviors + // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle) + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. + float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. + + //------------------------------------------------------------------ // Debug options - // - tools to test correct Begin/End and BeginChild/EndChild behaviors. - // - presently Begn()/End() and BeginChild()EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() - // this is inconsistent with other BeginXXX functions and create confusion for many users. - // - we expect to update the API eventually. In the meanwhile we provided tools to facilitate checking user-code behavior. - bool ConfigDebugBeginReturnValueOnce; // = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows. - bool ConfigDebugBeginReturnValueLoop; // = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running. + //------------------------------------------------------------------ + + // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro. + // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability. + // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application. + // e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). + bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK(). + + // Tools to test correct Begin/End and BeginChild/EndChild behaviors. + // - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() + // - This is inconsistent with other BeginXXX functions and create confusion for many users. + // - We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior. + bool ConfigDebugBeginReturnValueOnce;// = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows. + bool ConfigDebugBeginReturnValueLoop;// = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running. + + // Option to deactivate io.AddFocusEvent(false) handling. + // - May facilitate interactions with a debugger when focus loss leads to clearing inputs data. + // - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. + bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing. + + // Options to audit .ini data + bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) //------------------------------------------------------------------ // Platform Functions @@ -1970,11 +2144,9 @@ struct ImGuiIO // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows) // (default to use native imm32 api on Windows) void (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - void* ImeWindowHandle; // = NULL // [Obsolete] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. -#else - void* _UnusedPadding; // Unused field to keep data structure the same size. -#endif + + // Optional: Platform locale + ImWchar PlatformLocaleDecimalPoint; // '.' // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point //------------------------------------------------------------------ // Input - Call before calling NewFrame() @@ -1986,6 +2158,7 @@ struct ImGuiIO IMGUI_API void AddMousePosEvent(float x, float y); // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered) IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change IMGUI_API void AddMouseWheelEvent(float wheel_x, float wheel_y); // Queue a mouse wheel update. wheel_y<0: scroll down, wheel_y>0: scroll up, wheel_x<0: scroll right, wheel_x>0: scroll left. + IMGUI_API void AddMouseSourceEvent(ImGuiMouseSource source); // Queue a mouse source change (Mouse/TouchScreen/Pen) IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from a UTF-16 character, it can be a surrogate @@ -1993,8 +2166,11 @@ struct ImGuiIO IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode. IMGUI_API void SetAppAcceptingEvents(bool accepting_events); // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen. - IMGUI_API void ClearInputCharacters(); // [Internal] Clear the text input buffer manually - IMGUI_API void ClearInputKeys(); // [Internal] Release all keys + IMGUI_API void ClearEventsQueue(); // Clear all incoming events. + IMGUI_API void ClearInputKeys(); // Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + IMGUI_API void ClearInputCharacters(); // [Obsoleted in 1.89.8] Clear the current frame text input buffer. Now included within ClearInputKeys(). +#endif //------------------------------------------------------------------ // Output - Updated by NewFrame() or EndFrame()/Render() @@ -2014,7 +2190,6 @@ struct ImGuiIO int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 int MetricsRenderWindows; // Number of visible windows int MetricsActiveWindows; // Number of active windows - int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts. ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. @@ -2024,6 +2199,7 @@ struct ImGuiIO int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow. float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums. + //void* ImeWindowHandle; // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning. #endif //------------------------------------------------------------------ @@ -2039,6 +2215,7 @@ struct ImGuiIO bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold SHIFT to turn vertical scroll into horizontal scroll. float MouseWheelH; // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends. + ImGuiMouseSource MouseSource; // Mouse actual input peripheral (Mouse/TouchScreen/Pen). bool KeyCtrl; // Keyboard modifier down: Control bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt @@ -2058,6 +2235,7 @@ struct ImGuiIO bool MouseReleased[5]; // Mouse button went from Down to !Down bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. + bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) @@ -2073,7 +2251,7 @@ struct ImGuiIO }; //----------------------------------------------------------------------------- -// [SECTION] Misc data structures +// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) //----------------------------------------------------------------------------- // Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used. @@ -2147,30 +2325,6 @@ struct ImGuiPayload bool IsDelivery() const { return Delivery; } }; -// Sorting specification for one column of a table (sizeof == 12 bytes) -struct ImGuiTableColumnSortSpecs -{ - ImGuiID ColumnUserID; // User id of the column (if specified by a TableSetupColumn() call) - ImS16 ColumnIndex; // Index of the column - ImS16 SortOrder; // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here) - ImGuiSortDirection SortDirection : 8; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending (you can use this or SortSign, whichever is more convenient for your sort function) - - ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); } -}; - -// Sorting specifications for a table (often handling sort specs for a single column, occasionally more) -// Obtained by calling TableGetSortSpecs(). -// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time. -// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame! -struct ImGuiTableSortSpecs -{ - const ImGuiTableColumnSortSpecs* Specs; // Pointer to sort spec array. - int SpecsCount; // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled. - bool SpecsDirty; // Set to true when specs have changed since last time! Use this to sort again, then clear the flag. - - ImGuiTableSortSpecs() { memset(this, 0, sizeof(*this)); } -}; - //----------------------------------------------------------------------------- // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) //----------------------------------------------------------------------------- @@ -2254,9 +2408,9 @@ struct ImGuiStorage { ImGuiID key; union { int val_i; float val_f; void* val_p; }; - ImGuiStoragePair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } - ImGuiStoragePair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } - ImGuiStoragePair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } + ImGuiStoragePair(ImGuiID _key, int _val) { key = _key; val_i = _val; } + ImGuiStoragePair(ImGuiID _key, float _val) { key = _key; val_f = _val; } + ImGuiStoragePair(ImGuiID _key, void* _val) { key = _key; val_p = _val; } }; ImVector Data; @@ -2283,11 +2437,10 @@ struct ImGuiStorage IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0.0f); IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL); - // Use on your own storage if you know only integer are being stored (open/close all tree nodes) - IMGUI_API void SetAllInt(int val); - - // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. + // Advanced: for quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. IMGUI_API void BuildSortByKey(); + // Obsolete: use on your own storage if you know only integer are being stored (open/close all tree nodes) + IMGUI_API void SetAllInt(int val); }; // Helper: Manually clip large list of items. @@ -2328,11 +2481,15 @@ struct ImGuiListClipper IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. - // Call ForceDisplayRangeByIndices() before first call to Step() if you need a range of items to be displayed regardless of visibility. - IMGUI_API void ForceDisplayRangeByIndices(int item_min, int item_max); // item_max is exclusive e.g. use (42, 42+1) to make item 42 always visible BUT due to alignment/padding of certain items it is likely that an extra item may be included on either end of the display range. + // Call IncludeItemByIndex() or IncludeItemsByIndex() *BEFORE* first call to Step() if you need a range of items to not be clipped, regardless of their visibility. + // (Due to alignment / padding of certain items it is possible that an extra item may be included on either end of the display range). + inline void IncludeItemByIndex(int item_index) { IncludeItemsByIndex(item_index, item_index + 1); } + IMGUI_API void IncludeItemsByIndex(int item_begin, int item_end); // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] + inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9] + inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6] + //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] #endif }; @@ -2340,7 +2497,6 @@ struct ImGuiListClipper // - It is important that we are keeping those disabled by default so they don't leak in user space. // - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h) // - You can use '#define IMGUI_DEFINE_MATH_OPERATORS' to import our operators, provided as a courtesy. -// - We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. #ifdef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED IM_MSVC_RUNTIME_CHECKS_OFF @@ -2350,15 +2506,20 @@ static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); } static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); } +static inline ImVec2 operator-(const ImVec2& lhs) { return ImVec2(-lhs.x, -lhs.y); } static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } static inline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs) { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; } static inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs) { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; } +static inline bool operator==(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } +static inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y; } static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); } static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); } static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); } +static inline bool operator==(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w; } +static inline bool operator!=(const ImVec4& lhs, const ImVec4& rhs) { return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z || lhs.w != rhs.w; } IM_MSVC_RUNTIME_CHECKS_RESTORE #endif @@ -2395,8 +2556,8 @@ struct ImColor constexpr ImColor() { } constexpr ImColor(float r, float g, float b, float a = 1.0f) : Value(r, g, b, a) { } constexpr ImColor(const ImVec4& col) : Value(col) {} - ImColor(int r, int g, int b, int a = 255) { float sc = 1.0f / 255.0f; Value.x = (float)r * sc; Value.y = (float)g * sc; Value.z = (float)b * sc; Value.w = (float)a * sc; } - ImColor(ImU32 rgba) { float sc = 1.0f / 255.0f; Value.x = (float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * sc; Value.y = (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * sc; Value.z = (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * sc; Value.w = (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * sc; } + constexpr ImColor(int r, int g, int b, int a = 255) : Value((float)r * (1.0f / 255.0f), (float)g * (1.0f / 255.0f), (float)b * (1.0f / 255.0f), (float)a* (1.0f / 255.0f)) {} + constexpr ImColor(ImU32 rgba) : Value((float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * (1.0f / 255.0f)) {} inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } inline operator ImVec4() const { return Value; } @@ -2428,9 +2589,9 @@ typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* c // Special Draw callback value to request renderer backend to reset the graphics/render state. // The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address. -// This is useful for example if you submitted callbacks which you know have altered the render state and you want it to be restored. -// It is not done by default because they are many perfectly useful way of altering render state for imgui contents (e.g. changing shader/blending settings before an Image call). -#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-1) +// This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored. +// Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call). +#define ImDrawCallback_ResetRenderState (ImDrawCallback)(-8) // Typically, 1 command = 1 GPU draw call (unless command is a callback) // - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled, @@ -2594,6 +2755,8 @@ struct ImDrawList IMGUI_API void AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0); IMGUI_API void AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f); IMGUI_API void AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments); + IMGUI_API void AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f); + IMGUI_API void AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0); IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); IMGUI_API void AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness); @@ -2610,7 +2773,8 @@ struct ImDrawList IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0); // Stateful path API, add points then finish with PathFillConvex() or PathStroke() - // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. + // - Important: filled shapes must always use clockwise winding order! The anti-aliasing fringe depends on it. Counter-clockwise shapes will have "inward" anti-aliasing. + // so e.g. 'PathArcTo(center, radius, PI * -0.5f, PI)' is ok, whereas 'PathArcTo(center, radius, PI, PI * -0.5f)' won't have correct anti-aliasing when followed by PathFillConvex(). inline void PathClear() { _Path.Size = 0; } inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); } @@ -2618,6 +2782,7 @@ struct ImDrawList inline void PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; } IMGUI_API void PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0); IMGUI_API void PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle + IMGUI_API void PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse IMGUI_API void PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points) IMGUI_API void PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0); // Quadratic Bezier (3 control points) IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0); @@ -2630,7 +2795,7 @@ struct ImDrawList // Advanced: Channels // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives) // - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end) - // - FIXME-OBSOLETE: This API shouldn't have been in ImDrawList in the first place! + // - This API shouldn't have been in ImDrawList in the first place! // Prefer using your own persistent instance of ImDrawListSplitter as you can stack them. // Using the ImDrawList::ChannelsXXXX you cannot stack a split over another. inline void ChannelsSplit(int count) { _Splitter.Split(this, count); } @@ -2671,18 +2836,20 @@ struct ImDrawList // as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList) struct ImDrawData { - bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // Number of ImDrawList* to render - int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size - int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size - ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. - ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) - ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) - ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. + bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. + int CmdListsCount; // Number of ImDrawList* to render (should always be == CmdLists.size) + int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size + int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size + ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. + ImVec2 DisplayPos; // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications) + ImVec2 DisplaySize; // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications) + ImVec2 FramebufferScale; // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display. + ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). // Functions ImDrawData() { Clear(); } - void Clear() { memset(this, 0, sizeof(*this)); } // The ImDrawList are owned by ImGuiContext! + IMGUI_API void Clear(); + IMGUI_API void AddDrawList(ImDrawList* draw_list); // Helper to add an external draw list into an existing ImDrawData. IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! IMGUI_API void ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. }; @@ -2698,7 +2865,7 @@ struct ImFontConfig bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). int FontNo; // 0 // Index of font within TTF/OTF file float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal so you can reduce this to 2 to save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. + int OversampleH; // 2 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis. bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. @@ -2708,7 +2875,8 @@ struct ImFontConfig float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. - float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. + float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future. + float RasterizerDensity; // 1.0f // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display. IMPORTANT: If you increase this it is expected that you increase font scale accordingly, otherwise quality may look lowered. ImWchar EllipsisChar; // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. // [Internal] @@ -2792,13 +2960,13 @@ struct ImFontAtlas IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); - IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. - IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. + IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed. + IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. IMGUI_API void ClearTexData(); // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory. IMGUI_API void ClearFonts(); // Clear output font data (glyphs storage, UV coordinates). - IMGUI_API void Clear(); // Clear all input and output. + IMGUI_API void Clear(); // Clear allPlatformHandleRaw input and output. // Build atlas, retrieve pixel data. // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). @@ -2816,7 +2984,8 @@ struct ImFontAtlas //------------------------------------------- // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) - // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. + // NB: Make sure that your string are UTF-8 and NOT in your local code page. + // Read https://github.com/ocornut/imgui/blob/master/docs/FONTS.md/#about-utf-8-encoding for details. // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data. IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin IMGUI_API const ImWchar* GetGlyphRangesGreek(); // Default + Greek and Coptic @@ -2963,6 +3132,7 @@ enum ImGuiViewportFlags_ // - Windows are generally trying to stay within the Work Area of their host viewport. struct ImGuiViewport { + ImGuiID ID; // Unique identifier for the viewport ImGuiViewportFlags Flags; // See ImGuiViewportFlags_ ImVec2 Pos; // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates) ImVec2 Size; // Main Area: Size of the viewport. @@ -3011,6 +3181,16 @@ namespace ImGui #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.90.0 (from September 2023) + static inline bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0) { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); } + static inline void EndChildFrame() { EndChild(); } + //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border + //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags) { return BeginChild(id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border + static inline void ShowStackToolWindow(bool* p_open = NULL) { ShowIDStackToolWindow(p_open); } + IMGUI_API bool ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1); + IMGUI_API bool Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1); + // OBSOLETED in 1.89.7 (from June 2023) + IMGUI_API void SetItemAllowOverlap(); // Use SetNextItemAllowOverlap() before item. // OBSOLETED in 1.89.4 (from March 2023) static inline void PushAllowKeyboardFocus(bool tab_stop) { PushTabStop(tab_stop); } static inline void PopAllowKeyboardFocus() { PopTabStop(); } @@ -3019,16 +3199,16 @@ namespace ImGui // OBSOLETED in 1.88 (from May 2022) static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. - // OBSOLETED in 1.86 (from November 2021) - IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper. - // OBSOLETED in 1.85 (from August 2021) - static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } - // OBSOLETED in 1.81 (from February 2021) - IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items - static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } - static inline void ListBoxFooter() { EndListBox(); } // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + //-- OBSOLETED in 1.86 (from November 2021) + //IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Code removed, see 1.90 for last version of the code. Calculate range of visible items for large list of evenly sized items. Prefer using ImGuiListClipper. + //-- OBSOLETED in 1.85 (from August 2021) + //static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } + //-- OBSOLETED in 1.81 (from February 2021) + //static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } + //static inline bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1) { float height = GetTextLineHeightWithSpacing() * ((height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f) + GetStyle().FramePadding.y * 2.0f; return BeginListBox(label, ImVec2(0.0f, height)); } // Helper to calculate size from items_count and height_in_items + //static inline void ListBoxFooter() { EndListBox(); } //-- OBSOLETED in 1.79 (from August 2020) //static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry! //-- OBSOLETED in 1.78 (from June 2020): Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details. @@ -3075,21 +3255,21 @@ namespace ImGui //static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42 } -// OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() -typedef ImDrawFlags ImDrawCornerFlags; -enum ImDrawCornerFlags_ -{ - ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit - ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally). - ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82. - ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82. - ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82. - ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82 - ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, - ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, - ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, - ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight, -}; +//-- OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() +//typedef ImDrawFlags ImDrawCornerFlags; +//enum ImDrawCornerFlags_ +//{ +// ImDrawCornerFlags_None = ImDrawFlags_RoundCornersNone, // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit +// ImDrawCornerFlags_TopLeft = ImDrawFlags_RoundCornersTopLeft, // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally). +// ImDrawCornerFlags_TopRight = ImDrawFlags_RoundCornersTopRight, // Was == 0x02 (1 << 1) prior to 1.82. +// ImDrawCornerFlags_BotLeft = ImDrawFlags_RoundCornersBottomLeft, // Was == 0x04 (1 << 2) prior to 1.82. +// ImDrawCornerFlags_BotRight = ImDrawFlags_RoundCornersBottomRight, // Was == 0x08 (1 << 3) prior to 1.82. +// ImDrawCornerFlags_All = ImDrawFlags_RoundCornersAll, // Was == 0x0F prior to 1.82 +// ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, +// ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, +// ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, +// ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight, +//}; // RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022) // RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place. @@ -3098,6 +3278,8 @@ enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl //typedef ImGuiKeyChord ImGuiKeyModFlags; // == int //enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super }; +#define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // OBSOLETED IN 1.90 (now using C++11 standard version) + #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022) @@ -3120,9 +3302,14 @@ enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl #pragma warning (pop) #endif -// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) +// Include imgui_user.h at the end of imgui.h +// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. #ifdef IMGUI_INCLUDE_IMGUI_USER_H +#ifdef IMGUI_USER_H_FILENAME +#include IMGUI_USER_H_FILENAME +#else #include "imgui_user.h" #endif +#endif #endif // #ifndef IMGUI_DISABLE diff --git a/r5dev/thirdparty/imgui/imgui_demo.cpp b/r5dev/thirdparty/imgui/imgui_demo.cpp index 6f6e73d4..707c14d3 100644 --- a/r5dev/thirdparty/imgui/imgui_demo.cpp +++ b/r5dev/thirdparty/imgui/imgui_demo.cpp @@ -1,16 +1,18 @@ -// dear imgui, v1.89.4 +// dear imgui, v1.90.4 // (demo code) // Help: -// - Read FAQ at http://dearimgui.org/faq -// - Newcomers, read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. +// - Read FAQ at http://dearimgui.com/faq // - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that. +// - Need help integrating Dear ImGui in your codebase? +// - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started +// - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase. // Read imgui.cpp for more details, documentation and comments. // Get the latest version at https://github.com/ocornut/imgui -// ------------------------------------------------- +//--------------------------------------------------- // PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT! -// ------------------------------------------------- +//--------------------------------------------------- // Message to the person tempted to delete this file when integrating Dear ImGui into their codebase: // Think again! It is the most useful reference code that you and other coders will want to refer to and call. // Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of your game/app! @@ -24,14 +26,23 @@ // Thank you, // -Your beloved friend, imgui_demo.cpp (which you won't delete) -// Message to beginner C/C++ programmers about the meaning of the 'static' keyword: -// In this demo code, we frequently use 'static' variables inside functions. A static variable persists across calls, -// so it is essentially like a global variable but declared inside the scope of the function. We do this as a way to -// gather code and data in the same place, to make the demo source code faster to read, faster to write, and smaller -// in size. It also happens to be a convenient way of storing simple UI related information as long as your function -// doesn't need to be reentrant or used in multiple threads. This might be a pattern you will want to use in your code, -// but most of the real data you would be editing is likely going to be stored outside your functions. +//-------------------------------------------- +// ABOUT THE MEANING OF THE 'static' KEYWORD: +//-------------------------------------------- +// In this demo code, we frequently use 'static' variables inside functions. +// A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function. +// Think of "static int n = 0;" as "global int n = 0;" ! +// We do this IN THE DEMO because we want: +// - to gather code and data in the same place. +// - to make the demo source code faster to read, faster to change, smaller in size. +// - it is also a convenient way of storing simple UI related information as long as your function +// doesn't need to be reentrant or used in multiple threads. +// This might be a pattern you will want to use in your code, but most of the data you would be working +// with in a complex codebase is likely going to be stored outside your functions. +//----------------------------------------- +// ABOUT THE CODING STYLE OF OUR DEMO CODE +//----------------------------------------- // The Demo code in this file is designed to be easy to copy-and-paste into your application! // Because of this: // - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace. @@ -43,7 +54,7 @@ // Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp. // Navigating this file: -// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. /* @@ -91,10 +102,9 @@ Index of this file: #include // sqrtf, powf, cosf, sinf, floorf, ceilf #include // vsnprintf, sscanf, printf #include // NULL, malloc, free, atoi -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t +#if !defined(_MSC_VER) || _MSC_VER >= 1800 +#include // PRId64/PRIu64, not avail in some MinGW headers. #endif // Visual Studio warnings @@ -144,14 +154,13 @@ Index of this file: #define vsnprintf _vsnprintf #endif -// Format specifiers, printing 64-bit hasn't been decently standardized... -// In a real application you should be using PRId64 and PRIu64 from (non-windows) and on Windows define them yourself. -#ifdef _MSC_VER -#define IM_PRId64 "I64d" -#define IM_PRIu64 "I64u" -#else -#define IM_PRId64 "lld" -#define IM_PRIu64 "llu" +// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013) +#if !defined(PRId64) && defined(_MSC_VER) +#define PRId64 "I64d" +#define PRIu64 "I64u" +#elif !defined(PRId64) +#define PRId64 "lld" +#define PRIu64 "llu" #endif // Helpers macros @@ -162,7 +171,8 @@ Index of this file: #define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B)) #define IM_CLAMP(V, MN, MX) ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V)) -// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall +// Enforce cdecl calling convention for functions called by the standard library, +// in case compilation settings changed the default to e.g. __vectorcall #ifndef IMGUI_CDECL #ifdef _MSC_VER #define IMGUI_CDECL __cdecl @@ -178,19 +188,19 @@ Index of this file: #if !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Forward Declarations -static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppMainMenuBar(); static void ShowExampleAppConsole(bool* p_open); +static void ShowExampleAppCustomRendering(bool* p_open); +static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppLog(bool* p_open); static void ShowExampleAppLayout(bool* p_open); static void ShowExampleAppPropertyEditor(bool* p_open); -static void ShowExampleAppLongText(bool* p_open); +static void ShowExampleAppSimpleOverlay(bool* p_open); static void ShowExampleAppAutoResize(bool* p_open); static void ShowExampleAppConstrainedResize(bool* p_open); -static void ShowExampleAppSimpleOverlay(bool* p_open); static void ShowExampleAppFullscreen(bool* p_open); +static void ShowExampleAppLongText(bool* p_open); static void ShowExampleAppWindowTitles(bool* p_open); -static void ShowExampleAppCustomRendering(bool* p_open); static void ShowExampleMenuFile(); // We split the contents of the big ShowDemoWindow() function into smaller functions @@ -211,7 +221,7 @@ static void ShowDemoWindowInputs(); static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); @@ -246,59 +256,59 @@ void* GImGuiDemoMarkerCallbackUserData = NULL; void ImGui::ShowDemoWindow(bool* p_open) { // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup - // Most functions would normally just crash if the context is missing. - IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing dear imgui context. Refer to examples app!"); + // Most functions would normally just assert/crash if the context is missing. + IM_ASSERT(ImGui::GetCurrentContext() != NULL && "Missing Dear ImGui context. Refer to examples app!"); // Examples Apps (accessible from the "Examples" menu) static bool show_app_main_menu_bar = false; - static bool show_app_documents = false; static bool show_app_console = false; + static bool show_app_custom_rendering = false; + static bool show_app_documents = false; static bool show_app_log = false; static bool show_app_layout = false; static bool show_app_property_editor = false; - static bool show_app_long_text = false; + static bool show_app_simple_overlay = false; static bool show_app_auto_resize = false; static bool show_app_constrained_resize = false; - static bool show_app_simple_overlay = false; static bool show_app_fullscreen = false; + static bool show_app_long_text = false; static bool show_app_window_titles = false; - static bool show_app_custom_rendering = false; if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); if (show_app_documents) ShowExampleAppDocuments(&show_app_documents); if (show_app_console) ShowExampleAppConsole(&show_app_console); + if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); if (show_app_log) ShowExampleAppLog(&show_app_log); if (show_app_layout) ShowExampleAppLayout(&show_app_layout); if (show_app_property_editor) ShowExampleAppPropertyEditor(&show_app_property_editor); - if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); + if (show_app_simple_overlay) ShowExampleAppSimpleOverlay(&show_app_simple_overlay); if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize); if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize); - if (show_app_simple_overlay) ShowExampleAppSimpleOverlay(&show_app_simple_overlay); if (show_app_fullscreen) ShowExampleAppFullscreen(&show_app_fullscreen); + if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); if (show_app_window_titles) ShowExampleAppWindowTitles(&show_app_window_titles); - if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); - // Dear ImGui Tools/Apps (accessible from the "Tools" menu) - static bool show_app_metrics = false; - static bool show_app_debug_log = false; - static bool show_app_stack_tool = false; - static bool show_app_about = false; - static bool show_app_style_editor = false; + // Dear ImGui Tools (accessible from the "Tools" menu) + static bool show_tool_metrics = false; + static bool show_tool_debug_log = false; + static bool show_tool_id_stack_tool = false; + static bool show_tool_style_editor = false; + static bool show_tool_about = false; - if (show_app_metrics) - ImGui::ShowMetricsWindow(&show_app_metrics); - if (show_app_debug_log) - ImGui::ShowDebugLogWindow(&show_app_debug_log); - if (show_app_stack_tool) - ImGui::ShowStackToolWindow(&show_app_stack_tool); - if (show_app_about) - ImGui::ShowAboutWindow(&show_app_about); - if (show_app_style_editor) + if (show_tool_metrics) + ImGui::ShowMetricsWindow(&show_tool_metrics); + if (show_tool_debug_log) + ImGui::ShowDebugLogWindow(&show_tool_debug_log); + if (show_tool_id_stack_tool) + ImGui::ShowIDStackToolWindow(&show_tool_id_stack_tool); + if (show_tool_style_editor) { - ImGui::Begin("Dear ImGui Style Editor", &show_app_style_editor); + ImGui::Begin("Dear ImGui Style Editor", &show_tool_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } + if (show_tool_about) + ImGui::ShowAboutWindow(&show_tool_about); // Demonstrate the various window flags. Typically you would just use the default! static bool no_titlebar = false; @@ -359,18 +369,23 @@ void ImGui::ShowDemoWindow(bool* p_open) { IMGUI_DEMO_MARKER("Menu/Examples"); ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); + + ImGui::SeparatorText("Mini apps"); ImGui::MenuItem("Console", NULL, &show_app_console); - ImGui::MenuItem("Log", NULL, &show_app_log); - ImGui::MenuItem("Simple layout", NULL, &show_app_layout); - ImGui::MenuItem("Property editor", NULL, &show_app_property_editor); - ImGui::MenuItem("Long text display", NULL, &show_app_long_text); - ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); - ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); - ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); - ImGui::MenuItem("Fullscreen window", NULL, &show_app_fullscreen); - ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); ImGui::MenuItem("Documents", NULL, &show_app_documents); + ImGui::MenuItem("Log", NULL, &show_app_log); + ImGui::MenuItem("Property editor", NULL, &show_app_property_editor); + ImGui::MenuItem("Simple layout", NULL, &show_app_layout); + ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); + + ImGui::SeparatorText("Concepts"); + ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); + ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); + ImGui::MenuItem("Fullscreen window", NULL, &show_app_fullscreen); + ImGui::MenuItem("Long text display", NULL, &show_app_long_text); + ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); + ImGui::EndMenu(); } //if (ImGui::MenuItem("MenuItem")) {} // You can also use MenuItem() inside a menu bar! @@ -382,11 +397,17 @@ void ImGui::ShowDemoWindow(bool* p_open) #else const bool has_debug_tools = false; #endif - ImGui::MenuItem("Metrics/Debugger", NULL, &show_app_metrics, has_debug_tools); - ImGui::MenuItem("Debug Log", NULL, &show_app_debug_log, has_debug_tools); - ImGui::MenuItem("Stack Tool", NULL, &show_app_stack_tool, has_debug_tools); - ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); - ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); + ImGui::MenuItem("Metrics/Debugger", NULL, &show_tool_metrics, has_debug_tools); + ImGui::MenuItem("Debug Log", NULL, &show_tool_debug_log, has_debug_tools); + ImGui::MenuItem("ID Stack Tool", NULL, &show_tool_id_stack_tool, has_debug_tools); + ImGui::MenuItem("Style Editor", NULL, &show_tool_style_editor); + bool is_debugger_present = ImGui::GetIO().ConfigDebugIsDebuggerPresent; + if (ImGui::MenuItem("Item Picker", NULL, false, has_debug_tools && is_debugger_present)) + ImGui::DebugStartItemPicker(); + if (!is_debugger_present) + ImGui::SetItemTooltip("Requires io.ConfigDebugIsDebuggerPresent=true to be set.\n\nWe otherwise disable the menu option to avoid casual users crashing the application.\n\nYou can however always access the Item Picker in Metrics->Tools."); + ImGui::Separator(); + ImGui::MenuItem("About Dear ImGui", NULL, &show_tool_about); ImGui::EndMenu(); } ImGui::EndMenuBar(); @@ -398,23 +419,21 @@ void ImGui::ShowDemoWindow(bool* p_open) IMGUI_DEMO_MARKER("Help"); if (ImGui::CollapsingHeader("Help")) { - ImGui::Text("ABOUT THIS DEMO:"); + ImGui::SeparatorText("ABOUT THIS DEMO:"); ImGui::BulletText("Sections below are demonstrating many aspects of the library."); ImGui::BulletText("The \"Examples\" menu above leads to more demo contents."); ImGui::BulletText("The \"Tools\" menu above gives access to: About Box, Style Editor,\n" "and Metrics/Debugger (general purpose Dear ImGui debugging tool)."); - ImGui::Separator(); - ImGui::Text("PROGRAMMER GUIDE:"); + ImGui::SeparatorText("PROGRAMMER GUIDE:"); ImGui::BulletText("See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!"); ImGui::BulletText("See comments in imgui.cpp."); ImGui::BulletText("See example applications in the examples/ folder."); - ImGui::BulletText("Read the FAQ at http://www.dearimgui.org/faq/"); + ImGui::BulletText("Read the FAQ at https://www.dearimgui.com/faq/"); ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls."); ImGui::BulletText("Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls."); - ImGui::Separator(); - ImGui::Text("USER GUIDE:"); + ImGui::SeparatorText("USER GUIDE:"); ImGui::ShowUserGuide(); } @@ -465,12 +484,18 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Text("Also see Style->Rendering for rendering options."); ImGui::SeparatorText("Debug"); + ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent); + ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application."); ImGui::BeginDisabled(); ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); // . ImGui::EndDisabled(); - ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover"); + ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover."); ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop); ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running."); + ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss); + ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data."); + ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); + ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)."); ImGui::TreePop(); ImGui::Spacing(); @@ -483,13 +508,13 @@ void ImGui::ShowDemoWindow(bool* p_open) "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" "Here we expose them as read-only fields to avoid breaking interactions with your backend."); - // Make a local copy to avoid modifying actual backend flags. - // FIXME: We don't use BeginDisabled() to keep label bright, maybe we need a BeginReadonly() equivalent.. - ImGuiBackendFlags backend_flags = io.BackendFlags; - ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &backend_flags, ImGuiBackendFlags_HasGamepad); - ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &backend_flags, ImGuiBackendFlags_HasMouseCursors); - ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &backend_flags, ImGuiBackendFlags_HasSetMousePos); - ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &backend_flags, ImGuiBackendFlags_RendererHasVtxOffset); + // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright? + ImGui::BeginDisabled(); + ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &io.BackendFlags, ImGuiBackendFlags_HasGamepad); + ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); + ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); + ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); + ImGui::EndDisabled(); ImGui::TreePop(); ImGui::Spacing(); } @@ -624,37 +649,8 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); ImGui::Text("%d", counter); - { - // Tooltips - IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); - //ImGui::AlignTextToFramePadding(); - ImGui::Text("Tooltips:"); - - ImGui::SameLine(); - ImGui::SmallButton("Button"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); - - ImGui::SameLine(); - ImGui::SmallButton("Fancy"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) - { - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); - ImGui::EndTooltip(); - } - - ImGui::SameLine(); - ImGui::SmallButton("Delayed"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) // With a delay - ImGui::SetTooltip("I am a tooltip with a delay."); - - ImGui::SameLine(); - HelpMarker( - "Tooltip are created by using the IsItemHovered() function over any kind of item."); - } + ImGui::Button("Tooltip"); + ImGui::SetItemTooltip("I am a tooltip"); ImGui::LabelText("label", "Value"); @@ -744,7 +740,7 @@ static void ShowDemoWindowWidgets() static int elem = Element_Fire; const char* elems_names[Element_COUNT] = { "Fire", "Earth", "Air", "Water" }; const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : "Unknown"; - ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); + ImGui::SliderInt("slider enum", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here. ImGui::SameLine(); HelpMarker("Using the format string parameter to display a name instead of the underlying integer."); } @@ -772,7 +768,8 @@ static void ShowDemoWindowWidgets() static int item_current = 0; ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); HelpMarker( - "Using the simplified one-liner Combo API here.\nRefer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API."); + "Using the simplified one-liner Combo API here.\n" + "Refer to the \"Combo\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API."); } { @@ -783,22 +780,116 @@ static void ShowDemoWindowWidgets() static int item_current = 1; ImGui::ListBox("listbox", &item_current, items, IM_ARRAYSIZE(items), 4); ImGui::SameLine(); HelpMarker( - "Using the simplified one-liner ListBox API here.\nRefer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API."); + "Using the simplified one-liner ListBox API here.\n" + "Refer to the \"List boxes\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API."); } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tooltips"); + if (ImGui::TreeNode("Tooltips")) + { + // Tooltips are windows following the mouse. They do not take focus away. + ImGui::SeparatorText("General"); + + // Typical use cases: + // - Short-form (text only): SetItemTooltip("Hello"); + // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); } + + // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); } + // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); } + + HelpMarker( + "Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\n\n" + "We provide a helper SetItemTooltip() function to perform the two with standards flags."); + + ImVec2 sz = ImVec2(-FLT_MIN, 0.0f); + + ImGui::Button("Basic", sz); + ImGui::SetItemTooltip("I am a tooltip"); + + ImGui::Button("Fancy", sz); + if (ImGui::BeginItemTooltip()) + { + ImGui::Text("I am a fancy tooltip"); + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); + ImGui::EndTooltip(); + } + + ImGui::SeparatorText("Always On"); + + // Showcase NOT relying on a IsItemHovered() to emit a tooltip. + // Here the tooltip is always emitted when 'always_on == true'. + static int always_on = 0; + ImGui::RadioButton("Off", &always_on, 0); + ImGui::SameLine(); + ImGui::RadioButton("Always On (Simple)", &always_on, 1); + ImGui::SameLine(); + ImGui::RadioButton("Always On (Advanced)", &always_on, 2); + if (always_on == 1) + ImGui::SetTooltip("I am following you around."); + else if (always_on == 2 && ImGui::BeginTooltip()) + { + ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f)); + ImGui::EndTooltip(); + } + + ImGui::SeparatorText("Custom"); + + HelpMarker( + "Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize" + "tooltip activation details across your application. You may however decide to use custom" + "flags for a specific tooltip instance."); + + // The following examples are passed for documentation purpose but may not be useful to most users. + // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from + // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or gamepad/keyboard is being used. + // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary. + ImGui::Button("Manual", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + ImGui::SetTooltip("I am a manually emitted tooltip."); + + ImGui::Button("DelayNone", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone)) + ImGui::SetTooltip("I am a tooltip with no delay."); + + ImGui::Button("DelayShort", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort); + + ImGui::Button("DelayLong", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec).", ImGui::GetStyle().HoverDelayNormal); + + ImGui::Button("Stationary", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary)) + ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating."); + + // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav', + // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag. + // As a result, Set + ImGui::BeginDisabled(); + ImGui::Button("Disabled item", sz); + ImGui::EndDisabled(); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + ImGui::SetTooltip("I am a a tooltip for a disabled item."); + + ImGui::TreePop(); + } + // Testing ImGuiOnceUponAFrame helper. //static ImGuiOnceUponAFrame once; //for (int i = 0; i < 5; i++) // if (once) // ImGui::Text("This will be displayed only once."); - IMGUI_DEMO_MARKER("Widgets/Trees"); - if (ImGui::TreeNode("Trees")) + IMGUI_DEMO_MARKER("Widgets/Tree Nodes"); + if (ImGui::TreeNode("Tree Nodes")) { - IMGUI_DEMO_MARKER("Widgets/Trees/Basic trees"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Basic trees"); if (ImGui::TreeNode("Basic trees")) { for (int i = 0; i < 5; i++) @@ -819,7 +910,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Trees/Advanced, with Selectable nodes"); + IMGUI_DEMO_MARKER("Widgets/Tree Nodes/Advanced, with Selectable nodes"); if (ImGui::TreeNode("Advanced, with Selectable nodes")) { HelpMarker( @@ -832,6 +923,7 @@ static void ShowDemoWindowWidgets() ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker("Extend hit area to all available width instead of allowing more items to be laid out after the node."); ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &base_flags, ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker("For use in Tables only."); ImGui::Checkbox("Align label with current X position", &align_label_with_current_x_position); ImGui::Checkbox("Test tree node as drag source", &test_drag_and_drop); ImGui::Text("Hello!"); @@ -1007,7 +1099,7 @@ static void ShowDemoWindowWidgets() "CJK text will only appear if the font was loaded with the appropriate CJK character ranges. " "Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. " "Read docs/FONTS.md for details."); - ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we would use u8"blah blah" with the proper characters directly in the string. + ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis @@ -1051,10 +1143,10 @@ static void ShowDemoWindowWidgets() ImVec2 pos = ImGui::GetCursorScreenPos(); ImVec2 uv_min = ImVec2(0.0f, 0.0f); // Top-left ImVec2 uv_max = ImVec2(1.0f, 1.0f); // Lower-right - ImVec4 tint_col = use_text_color_for_tint ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint + ImVec4 tint_col = use_text_color_for_tint ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint ImVec4 border_col = ImGui::GetStyleColorVec4(ImGuiCol_Border); ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { float region_sz = 32.0f; float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; @@ -1110,16 +1202,29 @@ static void ShowDemoWindowWidgets() ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", &flags, ImGuiComboFlags_PopupAlignLeft); ImGui::SameLine(); HelpMarker("Only makes a difference if the popup is larger than the combo"); if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", &flags, ImGuiComboFlags_NoArrowButton)) - flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both + flags &= ~ImGuiComboFlags_NoPreview; // Clear incompatible flags if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", &flags, ImGuiComboFlags_NoPreview)) - flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both + flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear incompatible flags + if (ImGui::CheckboxFlags("ImGuiComboFlags_WidthFitPreview", &flags, ImGuiComboFlags_WidthFitPreview)) + flags &= ~ImGuiComboFlags_NoPreview; + + // Override default popup height + if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightSmall", &flags, ImGuiComboFlags_HeightSmall)) + flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall); + if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightRegular", &flags, ImGuiComboFlags_HeightRegular)) + flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular); + if (ImGui::CheckboxFlags("ImGuiComboFlags_HeightLargest", &flags, ImGuiComboFlags_HeightLargest)) + flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest); // Using the generic BeginCombo() API, you have full control over how to display the combo contents. // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; static int item_current_idx = 0; // Here we store our selection data as an index. - const char* combo_preview_value = items[item_current_idx]; // Pass in the preview value visible before opening the combo (it could be anything) + + // Pass in the preview value visible before opening the combo (it could technically be different contents or not pulled from items[]) + const char* combo_preview_value = items[item_current_idx]; + if (ImGui::BeginCombo("combo 1", combo_preview_value, flags)) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) @@ -1135,6 +1240,10 @@ static void ShowDemoWindowWidgets() ImGui::EndCombo(); } + ImGui::Spacing(); + ImGui::SeparatorText("One-liner variants"); + HelpMarker("Flags above don't apply to this section."); + // Simplified one-liner Combo() API, using values packed in a single constant string // This is a convenience for when the selection set is small and known at compile-time. static int item_current_2 = 0; @@ -1146,9 +1255,8 @@ static void ShowDemoWindowWidgets() ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); // Simplified one-liner Combo() using an accessor function - struct Funcs { static bool ItemGetter(void* data, int n, const char** out_str) { *out_str = ((const char**)data)[n]; return true; } }; static int item_current_4 = 0; - ImGui::Combo("combo 4 (function)", &item_current_4, &Funcs::ItemGetter, items, IM_ARRAYSIZE(items)); + ImGui::Combo("combo 4 (function)", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items)); ImGui::TreePop(); } @@ -1156,6 +1264,11 @@ static void ShowDemoWindowWidgets() IMGUI_DEMO_MARKER("Widgets/List Boxes"); if (ImGui::TreeNode("List boxes")) { + // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild() + // using the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. + // You may be tempted to simply use BeginChild() directly. However note that BeginChild() requires EndChild() + // to always be called (inconsistent with BeginListBox()/EndListBox()). + // Using the generic BeginListBox() API, you have full control over how to display the combo contents. // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively // stored in the object itself, etc.) @@ -1208,16 +1321,16 @@ static void ShowDemoWindowWidgets() IMGUI_DEMO_MARKER("Widgets/Selectables/Basic"); if (ImGui::TreeNode("Basic")) { - static bool selection[5] = { false, true, false, false, false }; + static bool selection[5] = { false, true, false, false }; ImGui::Selectable("1. I am selectable", &selection[0]); ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("(I am not selectable)"); - ImGui::Selectable("4. I am selectable", &selection[3]); - if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) + ImGui::Selectable("3. I am selectable", &selection[2]); + if (ImGui::Selectable("4. I am double clickable", selection[3], ImGuiSelectableFlags_AllowDoubleClick)) if (ImGui::IsMouseDoubleClicked(0)) - selection[4] = !selection[4]; + selection[3] = !selection[3]; ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Single Selection"); if (ImGui::TreeNode("Selection State: Single Selection")) { @@ -1249,17 +1362,18 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } - IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more text into the same line"); - if (ImGui::TreeNode("Rendering more text into the same line")) + IMGUI_DEMO_MARKER("Widgets/Selectables/Rendering more items on the same line"); + if (ImGui::TreeNode("Rendering more items on the same line")) { - // Using the Selectable() override that takes "bool* p_selected" parameter, - // this function toggle your bool value automatically. + // (1) Using SetNextItemAllowOverlap() + // (2) Using the Selectable() override that takes "bool* p_selected" parameter, the bool value is toggled automatically. static bool selected[3] = { false, false, false }; - ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); - ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(); ImGui::SmallButton("Link 1"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(); ImGui::SmallButton("Link 2"); + ImGui::SetNextItemAllowOverlap(); ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(); ImGui::SmallButton("Link 3"); ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/In columns"); if (ImGui::TreeNode("In columns")) { @@ -1295,6 +1409,7 @@ static void ShowDemoWindowWidgets() } ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Selectables/Grid"); if (ImGui::TreeNode("Grid")) { @@ -1380,6 +1495,7 @@ static void ShowDemoWindowWidgets() HelpMarker("You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include in here)"); ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); ImGui::CheckboxFlags("ImGuiInputTextFlags_AllowTabInput", &flags, ImGuiInputTextFlags_AllowTabInput); + ImGui::SameLine(); HelpMarker("When _AllowTabInput is set, passing through the widget with Tabbing doesn't automatically activate it, in order to also cycling through subsequent widgets."); ImGui::CheckboxFlags("ImGuiInputTextFlags_CtrlEnterForNewLine", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine); ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags); ImGui::TreePop(); @@ -1390,7 +1506,15 @@ static void ShowDemoWindowWidgets() { struct TextFilters { - // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i' + // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback) + static int FilterCasingSwap(ImGuiInputTextCallbackData* data) + { + if (data->EventChar >= 'a' && data->EventChar <= 'z') { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase + else if (data->EventChar >= 'A' && data->EventChar <= 'Z') { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase + return 0; + } + + // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out) static int FilterImGuiLetters(ImGuiInputTextCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) @@ -1399,12 +1523,13 @@ static void ShowDemoWindowWidgets() } }; - static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); - static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); - static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); - static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + static char buf1[32] = ""; ImGui::InputText("default", buf1, 32); + static char buf2[32] = ""; ImGui::InputText("decimal", buf2, 32, ImGuiInputTextFlags_CharsDecimal); + static char buf3[32] = ""; ImGui::InputText("hexadecimal", buf3, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[32] = ""; ImGui::InputText("uppercase", buf4, 32, ImGuiInputTextFlags_CharsUppercase); + static char buf5[32] = ""; ImGui::InputText("no blank", buf5, 32, ImGuiInputTextFlags_CharsNoBlank); + static char buf6[32] = ""; ImGui::InputText("casing swap", buf6, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters. + static char buf7[32] = ""; ImGui::InputText("\"imgui\"", buf7, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters. ImGui::TreePop(); } @@ -1419,6 +1544,7 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Completion, History, Edit Callbacks"); if (ImGui::TreeNode("Completion, History, Edit Callbacks")) { struct Funcs @@ -1460,16 +1586,21 @@ static void ShowDemoWindowWidgets() }; static char buf1[64]; ImGui::InputText("Completion", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback); - ImGui::SameLine(); HelpMarker("Here we append \"..\" each time Tab is pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback."); + ImGui::SameLine(); HelpMarker( + "Here we append \"..\" each time Tab is pressed. " + "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf2[64]; ImGui::InputText("History", buf2, 64, ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback); - ImGui::SameLine(); HelpMarker("Here we replace and select text each time Up/Down are pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback."); + ImGui::SameLine(); HelpMarker( + "Here we replace and select text each time Up/Down are pressed. " + "See 'Examples>Console' for a more meaningful demonstration of using this callback."); static char buf3[64]; static int edit_count = 0; ImGui::InputText("Edit", buf3, 64, ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count); - ImGui::SameLine(); HelpMarker("Here we toggle the casing of the first character on every edit + count edits."); + ImGui::SameLine(); HelpMarker( + "Here we toggle the casing of the first character on every edit + count edits."); ImGui::SameLine(); ImGui::Text("(%d)", edit_count); ImGui::TreePop(); @@ -1518,6 +1649,18 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Text Input/Miscellaneous"); + if (ImGui::TreeNode("Miscellaneous")) + { + static char buf1[16]; + static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll; + ImGui::CheckboxFlags("ImGuiInputTextFlags_EscapeClearsAll", &flags, ImGuiInputTextFlags_EscapeClearsAll); + ImGui::CheckboxFlags("ImGuiInputTextFlags_ReadOnly", &flags, ImGuiInputTextFlags_ReadOnly); + ImGui::CheckboxFlags("ImGuiInputTextFlags_NoUndoRedo", &flags, ImGuiInputTextFlags_NoUndoRedo); + ImGui::InputText("Hello", buf1, IM_ARRAYSIZE(buf1), flags); + ImGui::TreePop(); + } + ImGui::TreePop(); } @@ -1632,8 +1775,9 @@ static void ShowDemoWindowWidgets() ImGui::EndPopup(); } - // Demo Trailing Tabs: click the "+" button to add a new tab (in your app you may want to use a font icon instead of the "+") - // Note that we submit it before the regular tabs, but because of the ImGuiTabItemFlags_Trailing flag it will always appear at the end. + // Demo Trailing Tabs: click the "+" button to add a new tab. + // (In your app you may want to use a font icon instead of the "+") + // We submit it before the regular tabs, but thanks to the ImGuiTabItemFlags_Trailing flag it will always appear at the end. if (show_trailing_button) if (ImGui::TabItemButton("+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) active_tabs.push_back(next_tab_id++); // Add new tab @@ -1917,7 +2061,8 @@ static void ShowDemoWindowWidgets() if (ImGui::Button("Default: Float + HDR + Hue Wheel")) ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); - // Always both a small version of both types of pickers (to make it more visible in the demo to people who are skimming quickly through it) + // Always display a small version of both types of pickers + // (that's in order to make it more visible in the demo to people who are skimming quickly through it) ImGui::Text("Both types:"); float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f; ImGui::SetNextItemWidth(w); @@ -2070,12 +2215,12 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); - ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%" IM_PRId64); - ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" IM_PRId64); - ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" IM_PRId64); - ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" IM_PRIu64 " ms"); - ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" IM_PRIu64 " ms"); - ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" IM_PRIu64 " ms"); + ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%" PRId64); + ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%" PRId64); + ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%" PRId64); + ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%" PRIu64 " ms"); + ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%" PRIu64 " ms"); + ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%" PRIu64 " ms"); ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); ImGui::SliderScalar("slider float low log", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", ImGuiSliderFlags_Logarithmic); ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); @@ -2088,8 +2233,8 @@ static void ShowDemoWindowWidgets() ImGui::SliderScalar("slider u8 reverse", ImGuiDataType_U8, &u8_v, &u8_max, &u8_min, "%u"); ImGui::SliderScalar("slider s32 reverse", ImGuiDataType_S32, &s32_v, &s32_fifty, &s32_zero, "%d"); ImGui::SliderScalar("slider u32 reverse", ImGuiDataType_U32, &u32_v, &u32_fifty, &u32_zero, "%u"); - ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" IM_PRId64); - ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" IM_PRIu64 " ms"); + ImGui::SliderScalar("slider s64 reverse", ImGuiDataType_S64, &s64_v, &s64_fifty, &s64_zero, "%" PRId64); + ImGui::SliderScalar("slider u64 reverse", ImGuiDataType_U64, &u64_v, &u64_fifty, &u64_zero, "%" PRIu64 " ms"); IMGUI_DEMO_MARKER("Widgets/Data Types/Inputs"); static bool inputs_step = true; @@ -2322,6 +2467,36 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Drag and Drop/Tooltip at target location"); + if (ImGui::TreeNode("Tooltip at target location")) + { + for (int n = 0; n < 2; n++) + { + // Drop targets + ImGui::Button(n ? "drop here##1" : "drop here##0"); + if (ImGui::BeginDragDropTarget()) + { + ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip; + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags)) + { + IM_UNUSED(payload); + ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed); + ImGui::BeginTooltip(); + ImGui::Text("Cannot drop here!"); + ImGui::EndTooltip(); + } + ImGui::EndDragDropTarget(); + } + + // Drop source + static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f }; + if (n == 0) + ImGui::ColorButton("drag me", col4); + + } + ImGui::TreePop(); + } + ImGui::TreePop(); } @@ -2366,8 +2541,10 @@ static void ShowDemoWindowWidgets() if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } bool hovered_delay_none = ImGui::IsItemHovered(); + bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary); bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort); bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal); + bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary // Display the values of IsItemHovered() and other common item state functions. // Note that the ImGuiHoveredFlags_XXX flags can be combined. @@ -2379,7 +2556,8 @@ static void ShowDemoWindowWidgets() "IsItemHovered() = %d\n" "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsItemHovered(_AllowWhenOverlapped) = %d\n" + "IsItemHovered(_AllowWhenOverlappedByItem) = %d\n" + "IsItemHovered(_AllowWhenOverlappedByWindow) = %d\n" "IsItemHovered(_AllowWhenDisabled) = %d\n" "IsItemHovered(_RectOnly) = %d\n" "IsItemActive() = %d\n" @@ -2398,7 +2576,8 @@ static void ShowDemoWindowWidgets() ImGui::IsItemHovered(), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem), + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow), ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled), ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), ImGui::IsItemActive(), @@ -2414,7 +2593,13 @@ static void ShowDemoWindowWidgets() ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y ); ImGui::BulletText( - "w/ Hovering Delay: None = %d, Fast %d, Normal = %d", hovered_delay_none, hovered_delay_short, hovered_delay_normal); + "with Hovering Delay or Stationary test:\n" + "IsItemHovered() = = %d\n" + "IsItemHovered(_Stationary) = %d\n" + "IsItemHovered(_DelayShort) = %d\n" + "IsItemHovered(_DelayNormal) = %d\n" + "IsItemHovered(_Tooltip) = %d", + hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip); if (item_disabled) ImGui::EndDisabled(); @@ -2433,7 +2618,7 @@ static void ShowDemoWindowWidgets() static bool embed_all_inside_a_child_window = false; ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window); if (embed_all_inside_a_child_window) - ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true); + ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Border); // Testing IsWindowFocused() function with its various flags. ImGui::BulletText( @@ -2466,7 +2651,8 @@ static void ShowDemoWindowWidgets() "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AnyWindow) = %d\n", + "IsWindowHovered(_AnyWindow) = %d\n" + "IsWindowHovered(_Stationary) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), @@ -2477,9 +2663,10 @@ static void ShowDemoWindowWidgets() ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); + ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); - ImGui::BeginChild("child", ImVec2(0, 50), true); + ImGui::BeginChild("child", ImVec2(0, 50), ImGuiChildFlags_Border); ImGui::Text("This is another child window for testing the _ChildWindows flag."); ImGui::EndChild(); if (embed_all_inside_a_child_window) @@ -2563,7 +2750,7 @@ static void ShowDemoWindowLayout() ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar; if (disable_mouse_wheel) window_flags |= ImGuiWindowFlags_NoScrollWithMouse; - ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), false, window_flags); + ImGui::BeginChild("ChildL", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags); for (int i = 0; i < 100; i++) ImGui::Text("%04d: scrollable region", i); ImGui::EndChild(); @@ -2579,7 +2766,7 @@ static void ShowDemoWindowLayout() if (!disable_menu) window_flags |= ImGuiWindowFlags_MenuBar; ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("ChildR", ImVec2(0, 260), true, window_flags); + ImGui::BeginChild("ChildR", ImVec2(0, 260), ImGuiChildFlags_Border, window_flags); if (!disable_menu && ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Menu")) @@ -2604,6 +2791,35 @@ static void ShowDemoWindowLayout() ImGui::PopStyleVar(); } + // Child 3: manual-resize + ImGui::SeparatorText("Manual-resize"); + { + HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents."); + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); + if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) + for (int n = 0; n < 10; n++) + ImGui::Text("Line %04d", n); + ImGui::PopStyleColor(); + ImGui::EndChild(); + } + + // Child 4: auto-resizing height with a limit + ImGui::SeparatorText("Auto-resize with constraints"); + { + static int draw_lines = 3; + static int max_height_in_lines = 10; + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + ImGui::DragInt("Lines Count", &draw_lines, 0.2f); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + ImGui::DragInt("Max Height (in Lines)", &max_height_in_lines, 0.2f); + + ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines)); + if (ImGui::BeginChild("ConstrainedChild", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) + for (int n = 0; n < draw_lines; n++) + ImGui::Text("Line %04d", n); + ImGui::EndChild(); + } + ImGui::SeparatorText("Misc/Advanced"); // Demonstrate a few extra things @@ -2615,19 +2831,33 @@ static void ShowDemoWindowLayout() // the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details. { static int offset_x = 0; + static bool override_bg_color = true; + static ImGuiChildFlags child_flags = ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY; ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); ImGui::DragInt("Offset X", &offset_x, 1.0f, -1000, 1000); + ImGui::Checkbox("Override ChildBg color", &override_bg_color); + ImGui::CheckboxFlags("ImGuiChildFlags_Border", &child_flags, ImGuiChildFlags_Border); + ImGui::CheckboxFlags("ImGuiChildFlags_AlwaysUseWindowPadding", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding); + ImGui::CheckboxFlags("ImGuiChildFlags_ResizeX", &child_flags, ImGuiChildFlags_ResizeX); + ImGui::CheckboxFlags("ImGuiChildFlags_ResizeY", &child_flags, ImGuiChildFlags_ResizeY); + ImGui::CheckboxFlags("ImGuiChildFlags_FrameStyle", &child_flags, ImGuiChildFlags_FrameStyle); + ImGui::SameLine(); HelpMarker("Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding."); + if (child_flags & ImGuiChildFlags_FrameStyle) + override_bg_color = false; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x); - ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); - ImGui::BeginChild("Red", ImVec2(200, 100), true, ImGuiWindowFlags_None); + if (override_bg_color) + ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100)); + ImGui::BeginChild("Red", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None); + if (override_bg_color) + ImGui::PopStyleColor(); + for (int n = 0; n < 50; n++) ImGui::Text("Some test %d", n); ImGui::EndChild(); bool child_is_hovered = ImGui::IsItemHovered(); ImVec2 child_rect_min = ImGui::GetItemRectMin(); ImVec2 child_rect_max = ImGui::GetItemRectMax(); - ImGui::PopStyleColor(); ImGui::Text("Hovered: %d", child_is_hovered); ImGui::Text("Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y); } @@ -2720,11 +2950,11 @@ static void ShowDemoWindowLayout() // Text IMGUI_DEMO_MARKER("Layout/Basic Horizontal Layout/SameLine"); ImGui::Text("Two items: Hello"); ImGui::SameLine(); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor"); // Adjust spacing ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + ImGui::TextColored(ImVec4(1, 1, 0, 1), "Sailor"); // Button ImGui::AlignTextToFramePadding(); @@ -2775,7 +3005,7 @@ static void ShowDemoWindowLayout() ImGui::PushID(i); ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + //ImGui::SetItemTooltip("ListBox %d hovered", i); } ImGui::PopItemWidth(); @@ -2828,8 +3058,7 @@ static void ShowDemoWindowLayout() ImGui::SameLine(); ImGui::Button("EEE"); ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); + ImGui::SetItemTooltip("First group hovered"); } // Capture the group size and create widgets using the same size ImVec2 size = ImGui::GetItemRectSize(); @@ -3017,7 +3246,7 @@ static void ShowDemoWindowLayout() const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0; const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); - const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), true, child_flags); + const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Border, child_flags); if (ImGui::BeginMenuBar()) { ImGui::TextUnformatted("abc"); @@ -3064,7 +3293,7 @@ static void ShowDemoWindowLayout() float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f; ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0); ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i); - bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), true, child_flags); + bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Border, child_flags); if (scroll_to_off) ImGui::SetScrollX(scroll_to_off_px); if (scroll_to_pos) @@ -3106,7 +3335,7 @@ static void ShowDemoWindowLayout() ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30); - ImGui::BeginChild("scrolling", scrolling_child_size, true, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::BeginChild("scrolling", scrolling_child_size, ImGuiChildFlags_Border, ImGuiWindowFlags_HorizontalScrollbar); for (int line = 0; line < lines; line++) { // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine() @@ -3175,7 +3404,9 @@ static void ShowDemoWindowLayout() IMGUI_DEMO_MARKER("Layout/Scrolling/Horizontal contents size demo window"); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0)); - HelpMarker("Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); + HelpMarker( + "Test how different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\n\n" + "Use 'Metrics->Tools->Show windows rectangles' to visualize rectangles."); ImGui::Checkbox("H-scrollbar", &show_h_scrollbar); ImGui::Checkbox("Button", &show_button); // Will grow contents size (unless explicitly overwritten) ImGui::Checkbox("Tree nodes", &show_tree_nodes); // Will grow contents size and display highlight over full width @@ -3250,7 +3481,7 @@ static void ShowDemoWindowLayout() } if (show_child) { - ImGui::BeginChild("child", ImVec2(0, 0), true); + ImGui::BeginChild("child", ImVec2(0, 0), ImGuiChildFlags_Border); ImGui::EndChild(); } ImGui::End(); @@ -3323,6 +3554,37 @@ static void ShowDemoWindowLayout() ImGui::TreePop(); } + + IMGUI_DEMO_MARKER("Layout/Overlap Mode"); + if (ImGui::TreeNode("Overlap Mode")) + { + static bool enable_allow_overlap = true; + + HelpMarker( + "Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\n\n" + "By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. " + "Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state."); + ImGui::Checkbox("Enable AllowOverlap", &enable_allow_overlap); + + ImVec2 button1_pos = ImGui::GetCursorScreenPos(); + ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f); + if (enable_allow_overlap) + ImGui::SetNextItemAllowOverlap(); + ImGui::Button("Button 1", ImVec2(80, 80)); + ImGui::SetCursorScreenPos(button2_pos); + ImGui::Button("Button 2", ImVec2(80, 80)); + + // This is typically used with width-spanning items. + // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut + // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.) + if (enable_allow_overlap) + ImGui::SetNextItemAllowOverlap(); + ImGui::Selectable("Some Selectable", false); + ImGui::SameLine(); + ImGui::SmallButton("++"); + + ImGui::TreePop(); + } } static void ShowDemoWindowPopups() @@ -3390,8 +3652,7 @@ static void ShowDemoWindowPopups() ImGui::Separator(); ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); + ImGui::SetItemTooltip("I am a tooltip over a popup"); if (ImGui::Button("Stacked Popup")) ImGui::OpenPopup("another popup"); @@ -3475,8 +3736,7 @@ static void ShowDemoWindowPopups() ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Right-click to open popup"); + ImGui::SetItemTooltip("Right-click to open popup"); } } @@ -3656,6 +3916,14 @@ struct MyItem // very often by the sorting algorithm it would be a little wasteful. static const ImGuiTableSortSpecs* s_current_sort_specs; + static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count) + { + s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function. + if (items_count > 1) + qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs); + s_current_sort_specs = NULL; + } + // Compare function to be used by qsort() static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs) { @@ -3682,7 +3950,8 @@ struct MyItem } // qsort() is instable so always return a way to differenciate items. - // Your own compare function may want to avoid fallback on implicit sort specs e.g. a Name compare if it wasn't already part of the sort specs. + // Your own compare function may want to avoid fallback on implicit sort specs. + // e.g. a Name compare if it wasn't already part of the sort specs. return (a->ID - b->ID); } }; @@ -3728,7 +3997,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) } ImGui::SameLine(); ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); for (int m = 0; m < IM_ARRAYSIZE(policies); m++) @@ -3766,6 +4035,7 @@ static void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags) ImGui::CheckboxFlags("_PreferSortDescending", p_flags, ImGuiTableColumnFlags_PreferSortDescending); ImGui::CheckboxFlags("_IndentEnable", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker("Default for column 0"); ImGui::CheckboxFlags("_IndentDisable", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker("Default for column >0"); + ImGui::CheckboxFlags("_AngledHeader", p_flags, ImGuiTableColumnFlags_AngledHeader); } static void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags) @@ -3790,10 +4060,10 @@ static void ShowDemoWindowTables() ImGui::PushID("Tables"); int open_action = -1; - if (ImGui::Button("Open all")) + if (ImGui::Button("Expand all")) open_action = 1; ImGui::SameLine(); - if (ImGui::Button("Close all")) + if (ImGui::Button("Collapse all")) open_action = 0; ImGui::SameLine(); @@ -3864,8 +4134,9 @@ static void ShowDemoWindowTables() // as TableNextColumn() will automatically wrap around and create new rows as needed. // This is generally more convenient when your cells all contains the same type of data. HelpMarker( - "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains the same type of contents.\n" - "This is also more similar to the old NextColumn() function of the Columns API, and provided to facilitate the Columns->Tables API transition."); + "Only using TableNextColumn(), which tends to be convenient for tables where every cell contains " + "the same type of contents.\n This is also more similar to the old NextColumn() function of the " + "Columns API, and provided to facilitate the Columns->Tables API transition."); if (ImGui::BeginTable("table3", 3)) { for (int item = 0; item < 14; item++) @@ -3921,8 +4192,8 @@ static void ShowDemoWindowTables() if (ImGui::BeginTable("table1", 3, flags)) { - // Display headers so we can inspect their interaction with borders. - // (Headers are not the main purpose of this section of the demo, so we are not elaborating on them too much. See other sections for details) + // Display headers so we can inspect their interaction with borders + // (Headers are not the main purpose of this section of the demo, so we are not elaborating on them now. See other sections for details) if (display_headers) { ImGui::TableSetupColumn("One"); @@ -3961,7 +4232,9 @@ static void ShowDemoWindowTables() PushStyleCompact(); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); ImGui::CheckboxFlags("ImGuiTableFlags_BordersV", &flags, ImGuiTableFlags_BordersV); - ImGui::SameLine(); HelpMarker("Using the _Resizable flag automatically enables the _BordersInnerV flag as well, this is why the resize borders are still showing when unchecking this."); + ImGui::SameLine(); HelpMarker( + "Using the _Resizable flag automatically enables the _BordersInnerV flag as well, " + "this is why the resize borders are still showing when unchecking this."); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) @@ -4079,6 +4352,7 @@ static void ShowDemoWindowTables() ImGui::CheckboxFlags("ImGuiTableFlags_Hideable", &flags, ImGuiTableFlags_Hideable); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBody", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::CheckboxFlags("ImGuiTableFlags_NoBordersInBodyUntilResize", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker("Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)"); + ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); PopStyleCompact(); if (ImGui::BeginTable("table1", 3, flags)) @@ -4101,7 +4375,8 @@ static void ShowDemoWindowTables() ImGui::EndTable(); } - // Use outer_size.x == 0.0f instead of default to make the table as tight as possible (only valid when no scrolling and no stretch column) + // Use outer_size.x == 0.0f instead of default to make the table as tight as possible + // (only valid when no scrolling and no stretch column) if (ImGui::BeginTable("table2", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f))) { ImGui::TableSetupColumn("One"); @@ -4134,7 +4409,8 @@ static void ShowDemoWindowTables() "e.g.:\n" "- BorderOuterV\n" "- any form of row selection\n" - "Because of this, activating BorderOuterV sets the default to PadOuterX. Using PadOuterX or NoPadOuterX you can override the default.\n\n" + "Because of this, activating BorderOuterV sets the default to PadOuterX. " + "Using PadOuterX or NoPadOuterX you can override the default.\n\n" "Actual padding values are using style.CellPadding.\n\n" "In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding."); @@ -4250,7 +4526,8 @@ static void ShowDemoWindowTables() EditTableSizingFlags(&sizing_policy_flags[table_n]); // To make it easier to understand the different sizing policy, - // For each policy: we display one table where the columns have equal contents width, and one where the columns have different contents width. + // For each policy: we display one table where the columns have equal contents width, + // and one where the columns have different contents width. if (ImGui::BeginTable("table1", 3, sizing_policy_flags[table_n] | flags1)) { for (int row = 0; row < 3; row++) @@ -4279,7 +4556,9 @@ static void ShowDemoWindowTables() ImGui::Spacing(); ImGui::TextUnformatted("Advanced"); ImGui::SameLine(); - HelpMarker("This section allows you to interact and see the effect of various sizing policies depending on whether Scroll is enabled and the contents of your columns."); + HelpMarker( + "This section allows you to interact and see the effect of various sizing policies " + "depending on whether Scroll is enabled and the contents of your columns."); enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText }; static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable; @@ -4294,7 +4573,9 @@ static void ShowDemoWindowTables() if (contents_type == CT_FillButton) { ImGui::SameLine(); - HelpMarker("Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop where contents width can feed into auto-column width can feed into contents width."); + HelpMarker( + "Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop " + "where contents width can feed into auto-column width can feed into contents width."); } ImGui::DragInt("Columns", &column_count, 0.1f, 1, 64, "%d", ImGuiSliderFlags_AlwaysClamp); ImGui::CheckboxFlags("ImGuiTableFlags_Resizable", &flags, ImGuiTableFlags_Resizable); @@ -4340,7 +4621,9 @@ static void ShowDemoWindowTables() IMGUI_DEMO_MARKER("Tables/Vertical scrolling, with clipping"); if (ImGui::TreeNode("Vertical scrolling, with clipping")) { - HelpMarker("Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\nWe also demonstrate using ImGuiListClipper to virtualize the submission of many items."); + HelpMarker( + "Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\n\n" + "We also demonstrate using ImGuiListClipper to virtualize the submission of many items."); static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; PushStyleCompact(); @@ -4386,8 +4669,9 @@ static void ShowDemoWindowTables() HelpMarker( "When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, " "as automatically stretching columns doesn't make much sense with horizontal scrolling.\n\n" - "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX," - "because the container window won't automatically extend vertically to fix contents (this may be improved in future versions)."); + "Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX, " + "because the container window won't automatically extend vertically to fix contents " + "(this may be improved in future versions)."); static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; static int freeze_cols = 1; static int freeze_rows = 1; @@ -4444,7 +4728,8 @@ static void ShowDemoWindowTables() HelpMarker( "Showcase using Stretch columns + ScrollX together: " "this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\n" - "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns + ScrollX together doesn't make sense."); + "Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns " + "along with ScrollX doesn't make sense."); static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody; static float inner_width = 1000.0f; PushStyleCompact(); @@ -4502,8 +4787,9 @@ static void ShowDemoWindowTables() } // Create the real table we care about for the example! - // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above, otherwise in - // a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible + resizing the parent window down) + // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above, + // otherwise in a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible + // + resizing the parent window down). const ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV @@ -4511,15 +4797,22 @@ static void ShowDemoWindowTables() ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9); if (ImGui::BeginTable("table_columns_flags", column_count, flags, outer_size)) { + bool has_angled_header = false; for (int column = 0; column < column_count; column++) + { + has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0; ImGui::TableSetupColumn(column_names[column], column_flags[column]); + } + if (has_angled_header) + ImGui::TableAngledHeadersRow(); ImGui::TableHeadersRow(); for (int column = 0; column < column_count; column++) column_flags_out[column] = ImGui::TableGetColumnFlags(column); float indent_step = (float)((int)TEXT_BASE_WIDTH / 2); for (int row = 0; row < 8; row++) { - ImGui::Indent(indent_step); // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags. + // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags. + ImGui::Indent(indent_step); ImGui::TableNextRow(); for (int column = 0; column < column_count; column++) { @@ -4568,7 +4861,9 @@ static void ShowDemoWindowTables() ImGui::EndTable(); } - HelpMarker("Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, fixed columns with set width may still be shrunk down if there's not enough space in the host."); + HelpMarker( + "Using TableSetupColumn() to setup explicit width.\n\nUnless _NoKeepColumnsVisible is set, " + "fixed columns with set width may still be shrunk down if there's not enough space in the host."); static ImGuiTableFlags flags2 = ImGuiTableFlags_None; PushStyleCompact(); @@ -4578,7 +4873,8 @@ static void ShowDemoWindowTables() PopStyleCompact(); if (ImGui::BeginTable("table2", 4, flags2)) { - // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed. + // We could also set ImGuiTableFlags_SizingFixedFit on the table and then all columns + // will default to ImGuiTableColumnFlags_WidthFixed. ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f); @@ -4650,10 +4946,13 @@ static void ShowDemoWindowTables() IMGUI_DEMO_MARKER("Tables/Row height"); if (ImGui::TreeNode("Row height")) { - HelpMarker("You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\nWe cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row."); - if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerV)) + HelpMarker( + "You can pass a 'min_row_height' to TableNextRow().\n\nRows are padded with 'style.CellPadding.y' on top and bottom, " + "so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\n\n" + "We cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row."); + if (ImGui::BeginTable("table_row_height", 1, ImGuiTableFlags_Borders)) { - for (int row = 0; row < 10; row++) + for (int row = 0; row < 8; row++) { float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row); ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height); @@ -4662,6 +4961,48 @@ static void ShowDemoWindowTables() } ImGui::EndTable(); } + + HelpMarker( + "Showcase using SameLine(0,0) to share Current Line Height between cells.\n\n" + "Please note that Tables Row Height is not the same thing as Current Line Height, " + "as a table cell may contains multiple lines."); + if (ImGui::BeginTable("table_share_lineheight", 2, ImGuiTableFlags_Borders)) + { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::ColorButton("##1", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40)); + ImGui::TableNextColumn(); + ImGui::Text("Line 1"); + ImGui::Text("Line 2"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::ColorButton("##2", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40)); + ImGui::TableNextColumn(); + ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column + ImGui::Text("Line 1, with SameLine(0,0)"); + ImGui::Text("Line 2"); + + ImGui::EndTable(); + } + + HelpMarker("Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table."); + if (ImGui::BeginTable("table_changing_cellpadding_y", 1, ImGuiTableFlags_Borders)) + { + ImGuiStyle& style = ImGui::GetStyle(); + for (int row = 0; row < 8; row++) + { + if ((row % 3) == 2) + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(style.CellPadding.x, 20.0f)); + ImGui::TableNextRow(ImGuiTableRowFlags_None); + ImGui::TableNextColumn(); + ImGui::Text("CellPadding.y = %.2f", style.CellPadding.y); + if ((row % 3) == 2) + ImGui::PopStyleVar(); + } + ImGui::EndTable(); + } + ImGui::TreePop(); } @@ -4797,6 +5138,11 @@ static void ShowDemoWindowTables() { static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody; + static ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAllColumns; + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", &tree_node_flags, ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAllColumns", &tree_node_flags, ImGuiTreeNodeFlags_SpanAllColumns); + + HelpMarker("See \"Columns flags\" section to configure how indentation is applied to individual columns."); if (ImGui::BeginTable("3ways", 3, flags)) { // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On @@ -4820,7 +5166,7 @@ static void ShowDemoWindowTables() const bool is_folder = (node->ChildCount > 0); if (is_folder) { - bool open = ImGui::TreeNodeEx(node->Name, ImGuiTreeNodeFlags_SpanFullWidth); + bool open = ImGui::TreeNodeEx(node->Name, tree_node_flags); ImGui::TableNextColumn(); ImGui::TextDisabled("--"); ImGui::TableNextColumn(); @@ -4834,7 +5180,7 @@ static void ShowDemoWindowTables() } else { - ImGui::TreeNodeEx(node->Name, ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth); + ImGui::TreeNodeEx(node->Name, tree_node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen); ImGui::TableNextColumn(); ImGui::Text("%d", node->Size); ImGui::TableNextColumn(); @@ -4869,7 +5215,8 @@ static void ShowDemoWindowTables() { HelpMarker( "Showcase using PushItemWidth() and how it is preserved on a per-column basis.\n\n" - "Note that on auto-resizing non-resizable fixed columns, querying the content width for e.g. right-alignment doesn't make sense."); + "Note that on auto-resizing non-resizable fixed columns, querying the content width for " + "e.g. right-alignment doesn't make sense."); if (ImGui::BeginTable("table_item_width", 3, ImGuiTableFlags_Borders)) { ImGui::TableSetupColumn("small"); @@ -4955,13 +5302,72 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } - // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader() + // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers + if (open_action != -1) + ImGui::SetNextItemOpen(open_action != 0); + IMGUI_DEMO_MARKER("Tables/Angled headers"); + if (ImGui::TreeNode("Angled headers")) + { + const char* column_names[] = { "Track", "cabasa", "ride", "smash", "tom-hi", "tom-mid", "tom-low", "hihat-o", "hihat-c", "snare-s", "snare-c", "clap", "rim", "kick" }; + const int columns_count = IM_ARRAYSIZE(column_names); + const int rows_count = 12; + + static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn; + static ImGuiTableColumnFlags column_flags = ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed; + static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage + static int frozen_cols = 1; + static int frozen_rows = 2; + ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX); + ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY); + ImGui::CheckboxFlags("_Resizable", &table_flags, ImGuiTableFlags_Resizable); + ImGui::CheckboxFlags("_NoBordersInBody", &table_flags, ImGuiTableFlags_NoBordersInBody); + ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags, ImGuiTableFlags_HighlightHoveredColumn); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2); + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8); + ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2); + ImGui::CheckboxFlags("Disable header contributing to column width", &column_flags, ImGuiTableColumnFlags_NoHeaderWidth); + + if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12))) + { + ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder); + for (int n = 1; n < columns_count; n++) + ImGui::TableSetupColumn(column_names[n], column_flags); + ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows); + + ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag. + ImGui::TableHeadersRow(); // Draw remaining headers and allow access to context-menu and other functions. + for (int row = 0; row < rows_count; row++) + { + ImGui::PushID(row); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::Text("Track %d", row); + for (int column = 1; column < columns_count; column++) + if (ImGui::TableSetColumnIndex(column)) + { + ImGui::PushID(column); + ImGui::Checkbox("", &bools[row * columns_count + column]); + ImGui::PopID(); + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + ImGui::TreePop(); + } + + // Demonstrate creating custom context menus inside columns, + // while playing it nice with context menus provided by TableHeadersRow()/TableHeader() if (open_action != -1) ImGui::SetNextItemOpen(open_action != 0); IMGUI_DEMO_MARKER("Tables/Context menus"); if (ImGui::TreeNode("Context menus")) { - HelpMarker("By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); + HelpMarker( + "By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\n" + "Using ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body."); static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody; PushStyleCompact(); @@ -4998,7 +5404,9 @@ static void ShowDemoWindowTables() // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu. // [2.2] Right-click on the ".." to open a custom popup // [2.3] Right-click in columns to open another custom popup - HelpMarker("Demonstrate mixing table context menu (over header), item context button (over button) and custom per-colum context menu (over column body)."); + HelpMarker( + "Demonstrate mixing table context menu (over header), item context button (over button) " + "and custom per-colunm context menu (over column body)."); ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders; if (ImGui::BeginTable("table_context_menu_2", COLUMNS_COUNT, flags2)) { @@ -5073,6 +5481,7 @@ static void ShowDemoWindowTables() static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings; ImGui::CheckboxFlags("ImGuiTableFlags_ScrollY", &flags, ImGuiTableFlags_ScrollY); ImGui::CheckboxFlags("ImGuiTableFlags_SizingFixedFit", &flags, ImGuiTableFlags_SizingFixedFit); + ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); for (int n = 0; n < 3; n++) { char buf[32]; @@ -5153,14 +5562,11 @@ static void ShowDemoWindowTables() ImGui::TableHeadersRow(); // Sort our data if sort specs have been changed! - if (ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs()) - if (sorts_specs->SpecsDirty) + if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) + if (sort_specs->SpecsDirty) { - MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function. - if (items.Size > 1) - qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs); - MyItem::s_current_sort_specs = NULL; - sorts_specs->SpecsDirty = false; + MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size); + sort_specs->SpecsDirty = false; } // Demonstrate using clipper for large vertical lists @@ -5203,6 +5609,7 @@ static void ShowDemoWindowTables() | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingFixedFit; + static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None; enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow }; static int contents_type = CT_SelectableSpanRow; @@ -5296,9 +5703,17 @@ static void ShowDemoWindowTables() ImGui::TreePop(); } - if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) + if (ImGui::TreeNodeEx("Headers:", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Checkbox("show_headers", &show_headers); + ImGui::CheckboxFlags("ImGuiTableFlags_HighlightHoveredColumn", &flags, ImGuiTableFlags_HighlightHoveredColumn); + ImGui::CheckboxFlags("ImGuiTableColumnFlags_AngledHeader", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader); + ImGui::SameLine(); HelpMarker("Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \"Angled headers\" section of the demo)."); + ImGui::TreePop(); + } + + if (ImGui::TreeNodeEx("Other:", ImGuiTreeNodeFlags_DefaultOpen)) + { ImGui::Checkbox("show_wrapped_text", &show_wrapped_text); ImGui::DragFloat2("##OuterSize", &outer_size_value.x); @@ -5359,24 +5774,22 @@ static void ShowDemoWindowTables() // Declare columns // We use the "user_id" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications. // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index! - ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); - ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); - ImGui::TableSetupColumn("Quantity", ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); - ImGui::TableSetupColumn("Description", (flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Description); - ImGui::TableSetupColumn("Hidden", ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); + ImGui::TableSetupColumn("ID", columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID); + ImGui::TableSetupColumn("Name", columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name); + ImGui::TableSetupColumn("Action", columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action); + ImGui::TableSetupColumn("Quantity", columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity); + ImGui::TableSetupColumn("Description", columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description); + ImGui::TableSetupColumn("Hidden", columns_base_flags | ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort); ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows); // Sort our data if sort specs have been changed! - ImGuiTableSortSpecs* sorts_specs = ImGui::TableGetSortSpecs(); - if (sorts_specs && sorts_specs->SpecsDirty) + ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs(); + if (sort_specs && sort_specs->SpecsDirty) items_need_sort = true; - if (sorts_specs && items_need_sort && items.Size > 1) + if (sort_specs && items_need_sort && items.Size > 1) { - MyItem::s_current_sort_specs = sorts_specs; // Store in variable accessible by the sort function. - qsort(&items[0], (size_t)items.Size, sizeof(items[0]), MyItem::CompareWithSortSpecs); - MyItem::s_current_sort_specs = NULL; - sorts_specs->SpecsDirty = false; + MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size); + sort_specs->SpecsDirty = false; } items_need_sort = false; @@ -5385,6 +5798,8 @@ static void ShowDemoWindowTables() const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0; // Show headers + if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0) + ImGui::TableAngledHeadersRow(); if (show_headers) ImGui::TableHeadersRow(); @@ -5426,7 +5841,7 @@ static void ShowDemoWindowTables() ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow) { - ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap : ImGuiSelectableFlags_None; + ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None; if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height))) { if (ImGui::GetIO().KeyCtrl) @@ -5450,7 +5865,7 @@ static void ShowDemoWindowTables() // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity, // and we are currently sorting on the column showing the Quantity. // To avoid triggering a sort while holding the button, we only trigger it when the button has been released. - // You will probably need a more advanced system in your code if you want to automatically sort when a specific entry changes. + // You will probably need some extra logic if you want to automatically sort when a specific entry changes. if (ImGui::TableSetColumnIndex(2)) { if (ImGui::SmallButton("Chop")) { item->Quantity += 1; } @@ -5652,7 +6067,7 @@ static void ShowDemoWindowColumns() { ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f); - ImGui::BeginChild("##ScrollingRegion", child_size, false, ImGuiWindowFlags_HorizontalScrollbar); + ImGui::BeginChild("##ScrollingRegion", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar); ImGui::Columns(10); // Also demonstrate using clipper for large vertical lists @@ -5713,8 +6128,6 @@ static void ShowDemoWindowColumns() ImGui::TreePop(); } -namespace ImGui { extern ImGuiKeyData* GetKeyData(ImGuiKey key); } - static void ShowDemoWindowInputs() { IMGUI_DEMO_MARKER("Inputs & Focus"); @@ -5740,16 +6153,18 @@ static void ShowDemoWindowInputs() for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); - // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends. - // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END. + // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows + // displaying the data for old/new backends. + // User code should never have to go through such hoops! + // You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END. #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN; #else - struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key >= 0 && key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array ImGuiKey start_key = (ImGuiKey)0; #endif - ImGui::Text("Keys down:"); for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); ImGui::SameLine(); ImGui::Text("(%.02f)", ImGui::GetKeyData(key)->DownDuration); } + ImGui::Text("Keys down:"); for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? "\"%s\"" : "\"%s\" %d", ImGui::GetKeyName(key), key); } ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); ImGui::Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine(); ImGui::Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. @@ -5780,7 +6195,8 @@ static void ShowDemoWindowInputs() { HelpMarker( "Hovering the colored canvas will override io.WantCaptureXXX fields.\n" - "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering and true when clicking."); + "Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering " + "and true when clicking."); static int capture_override_mouse = -1; static int capture_override_keyboard = -1; const char* capture_override_desc[] = { "None", "Set to false", "Set to true" }; @@ -5931,10 +6347,11 @@ void ImGui::ShowAboutWindow(bool* p_open) return; } IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); - ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); + ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); + ImGui::Text("If your company uses this, please consider sponsoring the project!"); static bool show_config_info = false; ImGui::Checkbox("Config/Build Information", &show_config_info); @@ -5945,7 +6362,7 @@ void ImGui::ShowAboutWindow(bool* p_open) bool copy_to_clipboard = ImGui::Button("Copy to clipboard"); ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18); - ImGui::BeginChildFrame(ImGui::GetID("cfg_infos"), child_size, ImGuiWindowFlags_NoMove); + ImGui::BeginChild(ImGui::GetID("cfg_infos"), child_size, ImGuiChildFlags_FrameStyle); if (copy_to_clipboard) { ImGui::LogToClipboard(); @@ -6061,7 +6478,7 @@ void ImGui::ShowAboutWindow(bool* p_open) ImGui::LogText("\n```\n"); ImGui::LogFinish(); } - ImGui::EndChildFrame(); + ImGui::EndChild(); } ImGui::End(); } @@ -6085,9 +6502,8 @@ void ImGui::ShowFontSelector(const char* label) ImFont* font_current = ImGui::GetFont(); if (ImGui::BeginCombo(label, font_current->GetDebugName())) { - for (int n = 0; n < io.Fonts->Fonts.Size; n++) + for (ImFont* font : io.Fonts->Fonts) { - ImFont* font = io.Fonts->Fonts[n]; ImGui::PushID((void*)font); if (ImGui::Selectable(font->GetDebugName(), font == font_current)) io.FontDefault = font; @@ -6173,7 +6589,6 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SeparatorText("Main"); ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); @@ -6187,6 +6602,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); + ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f"); ImGui::SeparatorText("Rounding"); ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 12.0f, "%.0f"); @@ -6197,6 +6613,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); ImGui::SliderFloat("TabRounding", &style.TabRounding, 0.0f, 12.0f, "%.0f"); + ImGui::SeparatorText("Tables"); + ImGui::SliderFloat2("CellPadding", (float*)&style.CellPadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderAngle("TableAngledHeadersAngle", &style.TableAngledHeadersAngle, -50.0f, +50.0f); + ImGui::SeparatorText("Widgets"); ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); int window_menu_button_position = style.WindowMenuButtonPosition + 1; @@ -6209,11 +6629,25 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SameLine(); HelpMarker("Alignment applies when a selectable is larger than its text content."); ImGui::SliderFloat("SeparatorTextBorderSize", &style.SeparatorTextBorderSize, 0.0f, 10.0f, "%.0f"); ImGui::SliderFloat2("SeparatorTextAlign", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%0.f"); + ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); + ImGui::SeparatorText("Tooltips"); + for (int n = 0; n < 2; n++) + if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav")) + { + ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav; + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone); + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort); + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal); + ImGui::CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary); + ImGui::CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay); + ImGui::TreePop(); + } + ImGui::SeparatorText("Misc"); ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); + ImGui::EndTabItem(); } @@ -6253,14 +6687,21 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) "Left-click on color square to open color picker,\n" "Right-click to open edit options menu."); - ImGui::BeginChild("##colors", ImVec2(0, 0), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); - ImGui::PushItemWidth(-160); + ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX)); + ImGui::BeginChild("##colors", ImVec2(0, 0), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); + ImGui::PushItemWidth(ImGui::GetFontSize() * -12); for (int i = 0; i < ImGuiCol_COUNT; i++) { const char* name = ImGui::GetStyleColorName(i); if (!filter.PassFilter(name)) continue; ImGui::PushID(i); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (ImGui::Button("?")) + ImGui::DebugFlashStyleColor((ImGuiCol)i); + ImGui::SetItemTooltip("Flash given color to identify places where it is used."); + ImGui::SameLine(); +#endif ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) { @@ -6480,7 +6921,7 @@ static void ShowExampleMenuFile() { static bool enabled = true; ImGui::MenuItem("Enabled", "", &enabled); - ImGui::BeginChild("child", ImVec2(0, 60), true); + ImGui::BeginChild("child", ImVec2(0, 60), ImGuiChildFlags_Border); for (int i = 0; i < 10; i++) ImGui::Text("Scrolling Text %d", i); ImGui::EndChild(); @@ -6565,19 +7006,19 @@ struct ExampleAppConsole { ClearLog(); for (int i = 0; i < History.Size; i++) - free(History[i]); + ImGui::MemFree(History[i]); } // Portable helpers static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; } static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; } - static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); } + static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); } static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } void ClearLog() { for (int i = 0; i < Items.Size; i++) - free(Items[i]); + ImGui::MemFree(Items[i]); Items.clear(); } @@ -6646,7 +7087,7 @@ struct ExampleAppConsole // Reserve enough left-over height for 1 separator + 1 input text const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); - if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar)) + if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) { if (ImGui::BeginPopupContextWindow()) { @@ -6681,9 +7122,8 @@ struct ExampleAppConsole ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing if (copy_to_clipboard) ImGui::LogToClipboard(); - for (int i = 0; i < Items.Size; i++) + for (const char* item : Items) { - const char* item = Items[i]; if (!Filter.PassFilter(item)) continue; @@ -6744,7 +7184,7 @@ struct ExampleAppConsole for (int i = History.Size - 1; i >= 0; i--) if (Stricmp(History[i], command_line) == 0) { - free(History[i]); + ImGui::MemFree(History[i]); History.erase(History.begin() + i); break; } @@ -6958,7 +7398,7 @@ struct ExampleAppLog ImGui::Separator(); - if (ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) + if (ImGui::BeginChild("scrolling", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar)) { if (clear) Clear(); @@ -7077,7 +7517,7 @@ static void ShowExampleAppLayout(bool* p_open) // Left static int selected = 0; { - ImGui::BeginChild("left pane", ImVec2(150, 0), true); + ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX); for (int i = 0; i < 100; i++) { // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav @@ -7172,6 +7612,7 @@ static void ShowPlaceholderObject(const char* prefix, int uid) } // Demonstrate create a simple property editor. +// This demo is a bit lackluster nowadays, would be nice to improve. static void ShowExampleAppPropertyEditor(bool* p_open) { ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver); @@ -7180,23 +7621,24 @@ static void ShowExampleAppPropertyEditor(bool* p_open) ImGui::End(); return; } - IMGUI_DEMO_MARKER("Examples/Property Editor"); + IMGUI_DEMO_MARKER("Examples/Property Editor"); HelpMarker( "This example shows how you may implement a property editor using two columns.\n" - "All objects/fields data are dummies here.\n" - "Remember that in many simple cases, you can use ImGui::SameLine(xxx) to position\n" - "your cursor horizontally instead of using the Columns() API."); + "All objects/fields data are dummies here.\n"); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2)); - if (ImGui::BeginTable("split", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable)) + if (ImGui::BeginTable("##split", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Object"); + ImGui::TableSetupColumn("Contents"); + ImGui::TableHeadersRow(); + // Iterate placeholder objects (all the same data) for (int obj_i = 0; obj_i < 4; obj_i++) - { ShowPlaceholderObject("Object", obj_i); - //ImGui::Separator(); - } + ImGui::EndTable(); } ImGui::PopStyleVar(); @@ -7303,18 +7745,31 @@ static void ShowExampleAppConstrainedResize(bool* p_open) { // Helper functions to demonstrate programmatic constraints // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier. - static void AspectRatio(ImGuiSizeCallbackData* data) { float aspect_ratio = *(float*)data->UserData; data->DesiredSize.x = IM_MAX(data->CurrentSize.x, data->CurrentSize.y); data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio); } - static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->CurrentSize.x, data->CurrentSize.y); } - static void Step(ImGuiSizeCallbackData* data) { float step = *(float*)data->UserData; data->DesiredSize = ImVec2((int)(data->CurrentSize.x / step + 0.5f) * step, (int)(data->CurrentSize.y / step + 0.5f) * step); } + // FIXME: None of the three demos works consistently when resizing from borders. + static void AspectRatio(ImGuiSizeCallbackData* data) + { + float aspect_ratio = *(float*)data->UserData; + data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio); + } + static void Square(ImGuiSizeCallbackData* data) + { + data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y); + } + static void Step(ImGuiSizeCallbackData* data) + { + float step = *(float*)data->UserData; + data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); + } }; const char* test_desc[] = { "Between 100x100 and 500x500", "At least 100x100", - "Resize vertical only", - "Resize horizontal only", + "Resize vertical + lock current width", + "Resize horizontal + lock current height", "Width Between 400 and 500", + "Height at least 400", "Custom: Aspect Ratio 16:9", "Custom: Always Square", "Custom: Fixed Steps (100)", @@ -7323,7 +7778,7 @@ static void ShowExampleAppConstrainedResize(bool* p_open) // Options static bool auto_resize = false; static bool window_padding = true; - static int type = 5; // Aspect Ratio + static int type = 6; // Aspect Ratio static int display_lines = 10; // Submit constraint @@ -7331,12 +7786,13 @@ static void ShowExampleAppConstrainedResize(bool* p_open) float fixed_step = 100.0f; if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500)); // Between 100x100 and 500x500 if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 - if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only - if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only + if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Resize vertical + lock current width + if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Resize horizontal + lock current height if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width Between and 400 and 500 - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio); // Aspect ratio - if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square - if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step); // Fixed Step + if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 500), ImVec2(-1, FLT_MAX)); // Height at least 400 + if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio); // Aspect ratio + if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square + if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step); // Fixed Step // Submit window if (!window_padding) @@ -7443,7 +7899,7 @@ static void ShowExampleAppFullscreen(bool* p_open) static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings; // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.) - // Based on your use case you may want one of the other. + // Based on your use case you may want one or the other. const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos); ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size); @@ -7580,6 +8036,9 @@ static void ShowExampleAppCustomRendering(bool* p_open) const float rounding = sz / 5.0f; const int circle_segments = circle_segments_override ? circle_segments_override_v : 0; const int curve_segments = curve_segments_override ? curve_segments_override_v : 0; + const ImVec2 cp3[3] = { ImVec2(0.0f, sz * 0.6f), ImVec2(sz * 0.5f, -sz * 0.4f), ImVec2(sz, sz) }; // Control points for curves + const ImVec2 cp4[4] = { ImVec2(0.0f, 0.0f), ImVec2(sz * 1.3f, sz * 0.3f), ImVec2(sz - sz * 1.3f, sz - sz * 0.3f), ImVec2(sz, sz) }; + float x = p.x + 4.0f; float y = p.y + 4.0f; for (int n = 0; n < 2; n++) @@ -7588,6 +8047,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) float th = (n == 0) ? 1.0f : thickness; draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th); x += sz + spacing; // N-gon draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th); x += sz + spacing; // Circle + draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, sz*0.3f, col, -0.3f, circle_segments, th); x += sz + spacing; // Ellipse draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th); x += sz + spacing; // Square draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th); x += sz + spacing; // Square with all rounded corners draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th); x += sz + spacing; // Square with two rounded corners @@ -7597,19 +8057,26 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th); x += sz + spacing; // Diagonal line + // Path + draw_list->PathArcTo(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, 3.141592f, 3.141592f * -0.5f); + draw_list->PathStroke(col, ImDrawFlags_None, th); + x += sz + spacing; + // Quadratic Bezier Curve (3 control points) - ImVec2 cp3[3] = { ImVec2(x, y + sz * 0.6f), ImVec2(x + sz * 0.5f, y - sz * 0.4f), ImVec2(x + sz, y + sz) }; - draw_list->AddBezierQuadratic(cp3[0], cp3[1], cp3[2], col, th, curve_segments); x += sz + spacing; + draw_list->AddBezierQuadratic(ImVec2(x + cp3[0].x, y + cp3[0].y), ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), col, th, curve_segments); + x += sz + spacing; // Cubic Bezier Curve (4 control points) - ImVec2 cp4[4] = { ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f), ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz) }; - draw_list->AddBezierCubic(cp4[0], cp4[1], cp4[2], cp4[3], col, th, curve_segments); + draw_list->AddBezierCubic(ImVec2(x + cp4[0].x, y + cp4[0].y), ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), col, th, curve_segments); x = p.x + 4; y += sz + spacing; } - draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz*0.5f, col, ngon_sides); x += sz + spacing; // N-gon - draw_list->AddCircleFilled(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments); x += sz + spacing; // Circle + + // Filled shapes + draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides); x += sz + spacing; // N-gon + draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments); x += sz + spacing; // Circle + draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, sz * 0.3f, col, -0.3f, circle_segments); x += sz + spacing;// Ellipse draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col); x += sz + spacing; // Square draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f); x += sz + spacing; // Square with all rounded corners draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br); x += sz + spacing; // Square with two rounded corners @@ -7618,9 +8085,27 @@ static void ShowExampleAppCustomRendering(bool* p_open) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col); x += sz + spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col); x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness) draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col); x += sz; // Pixel (faster than AddLine) + + // Path + draw_list->PathArcTo(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, 3.141592f * -0.5f, 3.141592f); + draw_list->PathFillConvex(col); + x += sz + spacing; + + // Quadratic Bezier Curve (3 control points) + draw_list->PathLineTo(ImVec2(x + cp3[0].x, y + cp3[0].y)); + draw_list->PathBezierQuadraticCurveTo(ImVec2(x + cp3[1].x, y + cp3[1].y), ImVec2(x + cp3[2].x, y + cp3[2].y), curve_segments); + draw_list->PathFillConvex(col); + x += sz + spacing; + + // Cubic Bezier Curve (4 control points): this is concave so not drawing it yet + //draw_list->PathLineTo(ImVec2(x + cp4[0].x, y + cp4[0].y)); + //draw_list->PathBezierCubicCurveTo(ImVec2(x + cp4[1].x, y + cp4[1].y), ImVec2(x + cp4[2].x, y + cp4[2].y), ImVec2(x + cp4[3].x, y + cp4[3].y), curve_segments); + //draw_list->PathFillConvex(col); + //x += sz + spacing; + draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255)); - ImGui::Dummy(ImVec2((sz + spacing) * 10.2f, (sz + spacing) * 3.0f)); + ImGui::Dummy(ImVec2((sz + spacing) * 12.2f, (sz + spacing) * 3.0f)); ImGui::PopItemWidth(); ImGui::EndTabItem(); } @@ -7642,7 +8127,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // To use a child window instead we could use, e.g: // ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Disable padding // ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255)); // Set a background color - // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_NoMove); + // ImGui::BeginChild("canvas", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove); // ImGui::PopStyleColor(); // ImGui::PopStyleVar(); // [...] @@ -7740,6 +8225,43 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::EndTabItem(); } + // Demonstrate out-of-order rendering via channels splitting + // We use functions in ImDrawList as each draw list contains a convenience splitter, + // but you can also instantiate your own ImDrawListSplitter if you need to nest them. + if (ImGui::BeginTabItem("Draw Channels")) + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + { + ImGui::Text("Blue shape is drawn first: appears in back"); + ImGui::Text("Red shape is drawn after: appears in front"); + ImVec2 p0 = ImGui::GetCursorScreenPos(); + draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue + draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red + ImGui::Dummy(ImVec2(75, 75)); + } + ImGui::Separator(); + { + ImGui::Text("Blue shape is drawn first, into channel 1: appears in front"); + ImGui::Text("Red shape is drawn after, into channel 0: appears in back"); + ImVec2 p1 = ImGui::GetCursorScreenPos(); + + // Create 2 channels and draw a Blue shape THEN a Red shape. + // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls. + draw_list->ChannelsSplit(2); + draw_list->ChannelsSetCurrent(1); + draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue + draw_list->ChannelsSetCurrent(0); + draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red + + // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1. + // This works by copying draw indices only (vertices are not copied). + draw_list->ChannelsMerge(); + ImGui::Dummy(ImVec2(75, 75)); + ImGui::Text("After reordering, contents of channel 0 appears below channel 1."); + } + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); } @@ -7831,12 +8353,11 @@ struct ExampleAppDocuments // Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag. static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app) { - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + for (MyDocument& doc : app.Documents) { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open && doc->OpenPrev) - ImGui::SetTabItemClosed(doc->Name); - doc->OpenPrev = doc->Open; + if (!doc.Open && doc.OpenPrev) + ImGui::SetTabItemClosed(doc.Name); + doc.OpenPrev = doc.Open; } } @@ -7861,23 +8382,19 @@ void ShowExampleAppDocuments(bool* p_open) if (ImGui::BeginMenu("File")) { int open_count = 0; - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - open_count += app.Documents[doc_n].Open ? 1 : 0; + for (MyDocument& doc : app.Documents) + open_count += doc.Open ? 1 : 0; if (ImGui::BeginMenu("Open", open_count < app.Documents.Size)) { - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open) - if (ImGui::MenuItem(doc->Name)) - doc->DoOpen(); - } + for (MyDocument& doc : app.Documents) + if (!doc.Open && ImGui::MenuItem(doc.Name)) + doc.DoOpen(); ImGui::EndMenu(); } if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - app.Documents[doc_n].DoQueueClose(); + for (MyDocument& doc : app.Documents) + doc.DoQueueClose(); if (ImGui::MenuItem("Exit", "Ctrl+F4") && p_open) *p_open = false; ImGui::EndMenu(); @@ -7888,13 +8405,13 @@ void ShowExampleAppDocuments(bool* p_open) // [Debug] List documents with one checkbox for each for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) { - MyDocument* doc = &app.Documents[doc_n]; + MyDocument& doc = app.Documents[doc_n]; if (doc_n > 0) ImGui::SameLine(); - ImGui::PushID(doc); - if (ImGui::Checkbox(doc->Name, &doc->Open)) - if (!doc->Open) - doc->DoForceClose(); + ImGui::PushID(&doc); + if (ImGui::Checkbox(doc.Name, &doc.Open)) + if (!doc.Open) + doc.DoForceClose(); ImGui::PopID(); } @@ -7923,26 +8440,25 @@ void ShowExampleAppDocuments(bool* p_open) //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name); // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway.. // Submit Tabs - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + for (MyDocument& doc : app.Documents) { - MyDocument* doc = &app.Documents[doc_n]; - if (!doc->Open) + if (!doc.Open) continue; - ImGuiTabItemFlags tab_flags = (doc->Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); - bool visible = ImGui::BeginTabItem(doc->Name, &doc->Open, tab_flags); + ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); + bool visible = ImGui::BeginTabItem(doc.Name, &doc.Open, tab_flags); // Cancel attempt to close when unsaved add to save queue so we can display a popup. - if (!doc->Open && doc->Dirty) + if (!doc.Open && doc.Dirty) { - doc->Open = true; - doc->DoQueueClose(); + doc.Open = true; + doc.DoQueueClose(); } - MyDocument::DisplayContextMenu(doc); + MyDocument::DisplayContextMenu(&doc); if (visible) { - MyDocument::DisplayContents(doc); + MyDocument::DisplayContents(&doc); ImGui::EndTabItem(); } } @@ -7956,15 +8472,12 @@ void ShowExampleAppDocuments(bool* p_open) if (close_queue.empty()) { // Close queue is locked once we started a popup - for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) - { - MyDocument* doc = &app.Documents[doc_n]; - if (doc->WantClose) + for (MyDocument& doc : app.Documents) + if (doc.WantClose) { - doc->WantClose = false; - close_queue.push_back(doc); + doc.WantClose = false; + close_queue.push_back(&doc); } - } } // Display closing confirmation UI @@ -7990,13 +8503,13 @@ void ShowExampleAppDocuments(bool* p_open) { ImGui::Text("Save change to the following items?"); float item_height = ImGui::GetTextLineHeightWithSpacing(); - if (ImGui::BeginChildFrame(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height))) + if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle)) { for (int n = 0; n < close_queue.Size; n++) if (close_queue[n]->Dirty) ImGui::Text("%s", close_queue[n]->Name); - ImGui::EndChildFrame(); } + ImGui::EndChild(); ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f); if (ImGui::Button("Yes", button_size)) diff --git a/r5dev/thirdparty/imgui/imgui_draw.cpp b/r5dev/thirdparty/imgui/imgui_draw.cpp index 031c3e7d..1319a6e1 100644 --- a/r5dev/thirdparty/imgui/imgui_draw.cpp +++ b/r5dev/thirdparty/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.4 +// dear imgui, v1.90.4 // (drawing and font code) /* @@ -63,6 +63,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning: macro name is a reserved identifier #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision +#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter #elif defined(__GNUC__) #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used @@ -134,7 +135,7 @@ namespace IMGUI_STB_NAMESPACE #define STBTT_sqrt(x) ImSqrt(x) #define STBTT_pow(x,y) ImPow(x,y) #define STBTT_fabs(x) ImFabs(x) -#define STBTT_ifloor(x) ((int)ImFloorSigned(x)) +#define STBTT_ifloor(x) ((int)ImFloor(x)) #define STBTT_iceil(x) ((int)ImCeil(x)) #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION @@ -385,9 +386,9 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error) void ImDrawList::_ResetForNewFrame() { // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory. - IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, ClipRect) == 0); - IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, TextureId) == sizeof(ImVec4)); - IM_STATIC_ASSERT(IM_OFFSETOF(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4)); + IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID)); if (_Splitter._Count > 1) _Splitter.Merge(this); @@ -474,7 +475,7 @@ void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) } // Compare ClipRect, TextureId and VtxOffset with a single memcmp() -#define ImDrawCmd_HeaderSize (IM_OFFSETOF(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) +#define ImDrawCmd_HeaderSize (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int)) #define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS) (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize)) // Compare ClipRect, TextureId, VtxOffset #define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC) (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize)) // Copy ClipRect, TextureId, VtxOffset #define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1) (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset) @@ -560,7 +561,7 @@ int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy - if (radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) + if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts)) return _Data->CircleSegmentCounts[radius_idx]; // Use cached value else return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError); @@ -640,7 +641,7 @@ void ImDrawList::PrimReserve(int idx_count, int vtx_count) _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size; } -// Release the a number of reserved vertices/indices from the end of the last reservation made with PrimReserve(). +// Release the number of reserved vertices/indices from the end of the last reservation made with PrimReserve(). void ImDrawList::PrimUnreserve(int idx_count, int vtx_count) { IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0); @@ -1190,8 +1191,8 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f); const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f); - const int a_min_sample = a_is_reverse ? (int)ImFloorSigned(a_min_sample_f) : (int)ImCeil(a_min_sample_f); - const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloorSigned(a_max_sample_f); + const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f); + const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f); const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0); const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX; @@ -1216,6 +1217,27 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa } } +void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments) +{ + if (num_segments <= 0) + num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. + + _Path.reserve(_Path.Size + (num_segments + 1)); + + const float cos_rot = ImCos(rot); + const float sin_rot = ImSin(rot); + for (int i = 0; i <= num_segments; i++) + { + const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); + ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y); + const float rel_x = (point.x * cos_rot) - (point.y * sin_rot); + const float rel_y = (point.x * sin_rot) + (point.y * cos_rot); + point.x = rel_x + center.x; + point.y = rel_y + center.y; + _Path.push_back(point); + } +} + ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t) { float u = 1.0f - t; @@ -1311,33 +1333,22 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, } } -IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4)); static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) { + /* + IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4)); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - // Obsoleted in 1.82 (from February 2021) - // Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) - // ~0 --> ImDrawFlags_RoundCornersAll or 0 - if (flags == ~0) - return ImDrawFlags_RoundCornersAll; - - // Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations) - // 0x01 --> ImDrawFlags_RoundCornersTopLeft (VALUE 0x01 OVERLAPS ImDrawFlags_Closed but ImDrawFlags_Closed is never valid in this path!) - // 0x02 --> ImDrawFlags_RoundCornersTopRight - // 0x03 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight - // 0x04 --> ImDrawFlags_RoundCornersBotLeft - // 0x05 --> ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBotLeft - // ... - // 0x0F --> ImDrawFlags_RoundCornersAll or 0 - // (See all values in ImDrawCornerFlags_) - if (flags >= 0x01 && flags <= 0x0F) - return (flags << 4); - + // Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023) + // - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) + if (flags == ~0) { return ImDrawFlags_RoundCornersAll; } + // - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code. + if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); } // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f' #endif - - // If this triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values. - // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc... + */ + // If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values. + // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway. + // See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in "API BREAKING CHANGES" section. IM_ASSERT((flags & 0x0F) == 0 && "Misuse of legacy hardcoded ImDrawCornerFlags values!"); if ((flags & ImDrawFlags_RoundCornersMask_) == 0) @@ -1348,10 +1359,12 @@ static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags) { - flags = FixRectCornerFlags(flags); - rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f ) - 1.0f); - rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f ) - 1.0f); - + if (rounding >= 0.5f) + { + flags = FixRectCornerFlags(flags); + rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f); + rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f); + } if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone) { PathLineTo(a); @@ -1544,6 +1557,35 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in PathFillConvex(col); } +// Ellipse +void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + if (num_segments <= 0) + num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. + + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + if (num_segments <= 0) + num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here. + + // Because we are filling a closed shape we remove 1 from the count of segments/points + const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1); + PathFillConvex(col); +} + // Cubic Bezier takes 4 controls points void ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments) { @@ -1808,6 +1850,63 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) // [SECTION] ImDrawData //----------------------------------------------------------------------------- +void ImDrawData::Clear() +{ + Valid = false; + CmdListsCount = TotalIdxCount = TotalVtxCount = 0; + CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them. + DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f); + OwnerViewport = NULL; +} + +// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list +// as long at it is expected that the result will be later merged into draw_data->CmdLists[]. +void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list) +{ + if (draw_list->CmdBuffer.Size == 0) + return; + if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL) + return; + + // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. + // May trigger for you if you are using PrimXXX functions incorrectly. + IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); + IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); + if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset)) + IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); + + // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) + // If this assert triggers because you are drawing lots of stuff manually: + // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds. + // Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents. + // - If you want large meshes with more than 64K vertices, you can either: + // (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'. + // Most example backends already support this from 1.71. Pre-1.71 backends won't. + // Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them. + // (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h. + // Most example backends already support this. For example, the OpenGL example code detect index size at compile-time: + // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + // Your own engine or render API may use different parameters or function calls to specify index sizes. + // 2 and 4 bytes indices are generally supported by most graphics API. + // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching + // the 64K limit to split your draw commands in multiple draw lists. + if (sizeof(ImDrawIdx) == 2) + IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); + + // Add to output list + records state in ImDrawData + out_list->push_back(draw_list); + draw_data->CmdListsCount++; + draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; + draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; +} + +void ImDrawData::AddDrawList(ImDrawList* draw_list) +{ + IM_ASSERT(CmdLists.Size == CmdListsCount); + draw_list->_PopUnusedDrawCmd(); + ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list); +} + // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! void ImDrawData::DeIndexAllBuffers() { @@ -1832,15 +1931,9 @@ void ImDrawData::DeIndexAllBuffers() // or if there is a difference between your window resolution and framebuffer resolution. void ImDrawData::ScaleClipRects(const ImVec2& fb_scale) { - for (int i = 0; i < CmdListsCount; i++) - { - ImDrawList* cmd_list = CmdLists[i]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i]; - cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y); - } - } + for (ImDrawList* draw_list : CmdLists) + for (ImDrawCmd& cmd : draw_list->CmdBuffer) + cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y); } //----------------------------------------------------------------------------- @@ -1896,6 +1989,14 @@ void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int ve } } +void ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out) +{ + ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx; + ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx; + for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) + vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out; +} + //----------------------------------------------------------------------------- // [SECTION] ImFontConfig //----------------------------------------------------------------------------- @@ -1904,10 +2005,11 @@ ImFontConfig::ImFontConfig() { memset(this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; - OversampleH = 3; // FIXME: 2 may be a better default? + OversampleH = 2; OversampleV = 1; GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; + RasterizerDensity = 1.0f; EllipsisChar = (ImWchar)-1; } @@ -1981,19 +2083,19 @@ ImFontAtlas::~ImFontAtlas() void ImFontAtlas::ClearInputData() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); - for (int i = 0; i < ConfigData.Size; i++) - if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) + for (ImFontConfig& font_cfg : ConfigData) + if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas) { - IM_FREE(ConfigData[i].FontData); - ConfigData[i].FontData = NULL; + IM_FREE(font_cfg.FontData); + font_cfg.FontData = NULL; } // When clearing this we lose access to the font name and other information used to build the font. - for (int i = 0; i < Fonts.Size; i++) - if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) + for (ImFont* font : Fonts) + if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size) { - Fonts[i]->ConfigData = NULL; - Fonts[i]->ConfigDataCount = 0; + font->ConfigData = NULL; + font->ConfigDataCount = 0; } ConfigData.clear(); CustomRects.clear(); @@ -2090,6 +2192,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1) new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar; + ImFontAtlasUpdateConfigDataPointers(this); + // Invalidate texture TexReady = false; ClearTexData(); @@ -2126,7 +2230,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) if (font_cfg.Name[0] == '\0') ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels); font_cfg.EllipsisChar = (ImWchar)0x0085; - font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units + font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault(); @@ -2156,13 +2260,14 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, } // NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). -ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) +ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); IM_ASSERT(font_cfg.FontData == NULL); - font_cfg.FontData = ttf_data; - font_cfg.FontDataSize = ttf_size; + IM_ASSERT(font_data_size > 100 && "Incorrect value for font_data_size!"); // Heuristic to prevent accidentally passing a wrong value to font_data_size. + font_cfg.FontData = font_data; + font_cfg.FontDataSize = font_data_size; font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels; if (glyph_ranges) font_cfg.GlyphRanges = glyph_ranges; @@ -2377,7 +2482,10 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) + { + IM_ASSERT(0 && "stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize."); return false; + } // Measure highest codepoints ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex]; @@ -2459,7 +2567,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) // Convert our ranges in the format stb_truetype wants ImFontConfig& cfg = atlas->ConfigData[src_i]; - src_tmp.PackRange.font_size = cfg.SizePixels; + src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity; src_tmp.PackRange.first_unicode_codepoint_in_range = 0; src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data; src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size; @@ -2468,7 +2576,7 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV; // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects) - const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels); + const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity); const int padding = atlas->TexGlyphPadding; for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++) { @@ -2553,13 +2661,10 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) // 9. Setup ImFont and glyphs for runtime for (int src_i = 0; src_i < src_tmp_array.Size; src_i++) { - ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; - if (src_tmp.GlyphsCount == 0) - continue; - // When merging fonts with MergeMode=true: // - We can have multiple input fonts writing into a same destination font. // - dst_font->ConfigData is != from cfg which is our source configuration. + ImFontBuildSrcData& src_tmp = src_tmp_array[src_i]; ImFontConfig& cfg = atlas->ConfigData[src_i]; ImFont* dst_font = cfg.DstFont; @@ -2567,12 +2672,14 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) int unscaled_ascent, unscaled_descent, unscaled_line_gap; stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); - const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); - const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); + const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); + const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); const float font_off_x = cfg.GlyphOffset.x; const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent); + const float inv_rasterization_scale = 1.0f / cfg.RasterizerDensity; + for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++) { // Register glyph @@ -2581,7 +2688,11 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) stbtt_aligned_quad q; float unused_x = 0.0f, unused_y = 0.0f; stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0); - dst_font->AddGlyph(&cfg, (ImWchar)codepoint, q.x0 + font_off_x, q.y0 + font_off_y, q.x1 + font_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, pc.xadvance); + float x0 = q.x0 * inv_rasterization_scale + font_off_x; + float y0 = q.y0 * inv_rasterization_scale + font_off_y; + float x1 = q.x1 * inv_rasterization_scale + font_off_x; + float y1 = q.y1 * inv_rasterization_scale + font_off_y; + dst_font->AddGlyph(&cfg, (ImWchar)codepoint, x0, y0, x1, y1, q.s0, q.t0, q.s1, q.t1, pc.xadvance * inv_rasterization_scale); } } @@ -2601,19 +2712,31 @@ const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype() #endif // IMGUI_ENABLE_STB_TRUETYPE +void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas) +{ + for (ImFontConfig& font_cfg : atlas->ConfigData) + { + ImFont* font = font_cfg.DstFont; + if (!font_cfg.MergeMode) + { + font->ConfigData = &font_cfg; + font->ConfigDataCount = 0; + } + font->ConfigDataCount++; + } +} + void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) { if (!font_config->MergeMode) { font->ClearOutputData(); font->FontSize = font_config->SizePixels; - font->ConfigData = font_config; - font->ConfigDataCount = 0; + IM_ASSERT(font->ConfigData == font_config); font->ContainerAtlas = atlas; font->Ascent = ascent; font->Descent = descent; } - font->ConfigDataCount++; } void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque) @@ -2623,6 +2746,9 @@ void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opa ImVector& user_rects = atlas->CustomRects; IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong. +#ifdef __GNUC__ + if (user_rects.Size < 1) { __builtin_unreachable(); } // Workaround for GCC bug if IM_ASSERT() is defined to conditionally throw (see #5343) +#endif ImVector pack_rects; pack_rects.resize(user_rects.Size); @@ -2757,6 +2883,13 @@ static void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas) // Note: this is called / shared by both the stb_truetype and the FreeType builder void ImFontAtlasBuildInit(ImFontAtlas* atlas) { + // Round font size + // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet. + // - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes. + // - We may support it better later and remove this rounding. + for (ImFontConfig& cfg : atlas->ConfigData) + cfg.SizePixels = ImTrunc(cfg.SizePixels); + // Register texture region for mouse cursors or standard white pixels if (atlas->PackIdMouseCursors < 0) { @@ -2798,9 +2931,9 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas) } // Build all fonts lookup tables - for (int i = 0; i < atlas->Fonts.Size; i++) - if (atlas->Fonts[i]->DirtyLookupTables) - atlas->Fonts[i]->BuildLookupTable(); + for (ImFont* font : atlas->Fonts) + if (font->DirtyLookupTables) + font->BuildLookupTable(); atlas->TexReady = true; } @@ -3165,6 +3298,7 @@ void ImFont::BuildLookupTable() max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); // Build lookup table + IM_ASSERT(Glyphs.Size > 0 && "Font has not loaded glyph!"); IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved IndexAdvanceX.clear(); IndexLookup.clear(); @@ -3200,7 +3334,25 @@ void ImFont::BuildLookupTable() SetGlyphVisible((ImWchar)' ', false); SetGlyphVisible((ImWchar)'\t', false); - // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). + // Setup Fallback character + const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; + FallbackGlyph = FindGlyphNoFallback(FallbackChar); + if (FallbackGlyph == NULL) + { + FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars)); + FallbackGlyph = FindGlyphNoFallback(FallbackChar); + if (FallbackGlyph == NULL) + { + FallbackGlyph = &Glyphs.back(); + FallbackChar = (ImWchar)FallbackGlyph->Codepoint; + } + } + FallbackAdvanceX = FallbackGlyph->AdvanceX; + for (int i = 0; i < max_codepoint + 1; i++) + if (IndexAdvanceX[i] < 0.0f) + IndexAdvanceX[i] = FallbackAdvanceX; + + // Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis). // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character. // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots. const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 }; @@ -3221,25 +3373,6 @@ void ImFont::BuildLookupTable() EllipsisCharStep = (glyph->X1 - glyph->X0) + 1.0f; EllipsisWidth = EllipsisCharStep * 3.0f - 1.0f; } - - // Setup fallback character - const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' }; - FallbackGlyph = FindGlyphNoFallback(FallbackChar); - if (FallbackGlyph == NULL) - { - FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars)); - FallbackGlyph = FindGlyphNoFallback(FallbackChar); - if (FallbackGlyph == NULL) - { - FallbackGlyph = &Glyphs.back(); - FallbackChar = (ImWchar)FallbackGlyph->Codepoint; - } - } - - FallbackAdvanceX = FallbackGlyph->AdvanceX; - for (int i = 0; i < max_codepoint + 1; i++) - if (IndexAdvanceX[i] < 0.0f) - IndexAdvanceX[i] = FallbackAdvanceX; } // API is designed this way to avoid exposing the 4K page size @@ -3282,7 +3415,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX); if (advance_x != advance_x_original) { - float char_off_x = cfg->PixelSnapH ? ImFloor((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f; + float char_off_x = cfg->PixelSnapH ? ImTrunc((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f; x0 += char_off_x; x1 += char_off_x; } @@ -3550,8 +3683,8 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im if (glyph->Colored) col |= ~IM_COL32_A_MASK; float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; - float x = IM_FLOOR(pos.x); - float y = IM_FLOOR(pos.y); + float x = IM_TRUNC(pos.x); + float y = IM_TRUNC(pos.y); draw_list->PrimReserve(6, 4); draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); } @@ -3563,8 +3696,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. // Align to be pixel perfect - float x = IM_FLOOR(pos.x); - float y = IM_FLOOR(pos.y); + float x = IM_TRUNC(pos.x); + float y = IM_TRUNC(pos.y); if (y > clip_rect.w) return; @@ -3790,6 +3923,7 @@ void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir d void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) { + // FIXME-OPT: This should be baked in font. draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8); } @@ -3863,8 +3997,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im } else { - draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL - draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR + draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b); // BL + draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e); // TR } if (p1.x > rect.Min.x + rounding) { @@ -3883,8 +4017,8 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im } else { - draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR - draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR + draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b); // TR + draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e); // BR } } draw_list->PathFillConvex(col); @@ -4071,8 +4205,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i //----------------------------------------------------------------------------- // ProggyClean.ttf // Copyright (c) 2004, 2005 Tristan Grimmer -// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) -// Download and more information at http://upperbounds.net +// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download) +// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php //----------------------------------------------------------------------------- // File: 'ProggyClean.ttf' (41208 bytes) // Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding). diff --git a/r5dev/thirdparty/imgui/imgui_internal.h b/r5dev/thirdparty/imgui/imgui_internal.h index a3138541..ca1747ab 100644 --- a/r5dev/thirdparty/imgui/imgui_internal.h +++ b/r5dev/thirdparty/imgui/imgui_internal.h @@ -1,12 +1,7 @@ -// dear imgui, v1.89.4 +// dear imgui, v1.90.4 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. -// To implement maths operators for ImVec2 (disabled by default to not conflict with using IM_VEC2_CLASS_EXTRA with your own math types+operators), use: -/* -#define IMGUI_DEFINE_MATH_OPERATORS -#include "imgui_internal.h" -*/ /* @@ -20,9 +15,12 @@ Index of this file: // [SECTION] Generic helpers // [SECTION] ImDrawList support // [SECTION] Widgets support: flags, enums, data structures +// [SECTION] Data types support +// [SECTION] Popup support // [SECTION] Inputs support // [SECTION] Clipper support // [SECTION] Navigation support +// [SECTION] Typing-select support // [SECTION] Columns support // [SECTION] Multi-select support // [SECTION] Docking support @@ -81,7 +79,7 @@ Index of this file: #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' #endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' -#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloorSigned() +#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor() #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h #pragma clang diagnostic ignored "-Wold-style-cast" @@ -130,10 +128,12 @@ struct ImGuiDataVarInfo; // Variable information (e.g. to avoid style struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box +struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id struct ImGuiLastItemData; // Status storage for last submitted items struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result +struct ImGuiNavTreeNodeData; // Temporary storage for last TreeNode() being a Left arrow landing candidate. struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions struct ImGuiNextWindowData; // Storage for SetNextWindow** functions struct ImGuiNextItemData; // Storage for SetNextItem** functions @@ -151,6 +151,8 @@ struct ImGuiTableInstanceData; // Storage for one instance of a same table struct ImGuiTableTempData; // Temporary storage for one table (one per table in the stack), shared between tables. struct ImGuiTableSettings; // Storage for a table .ini settings struct ImGuiTableColumnsSettings; // Storage for a column .ini settings +struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest() +struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public) struct ImGuiWindow; // Storage for one window struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) @@ -163,6 +165,7 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E // Flags typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags +typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow(); typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressed(), IsMouseClicked(), SetKeyOwner(), SetItemKeyOwner() etc. typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags @@ -175,6 +178,7 @@ typedef int ImGuiScrollFlags; // -> enum ImGuiScrollFlags_ // F typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // Flags: for SeparatorEx() typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx() typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx() +typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest() typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...); @@ -194,13 +198,13 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer namespace ImStb { -#undef STB_TEXTEDIT_STRING -#undef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_STRING ImGuiInputTextState -#define STB_TEXTEDIT_CHARTYPE ImWchar -#define STB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f) -#define STB_TEXTEDIT_UNDOSTATECOUNT 99 -#define STB_TEXTEDIT_UNDOCHARCOUNT 999 +#undef IMSTB_TEXTEDIT_STRING +#undef IMSTB_TEXTEDIT_CHARTYPE +#define IMSTB_TEXTEDIT_STRING ImGuiInputTextState +#define IMSTB_TEXTEDIT_CHARTYPE ImWchar +#define IMSTB_TEXTEDIT_GETWIDTH_NEWLINE (-1.0f) +#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 +#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 #include "imstb_textedit.h" } // namespace ImStb @@ -225,12 +229,14 @@ namespace ImStb #else #define IMGUI_DEBUG_LOG(...) ((void)0) #endif -#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) -#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_SELECTION(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Static Asserts #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") @@ -263,8 +269,13 @@ namespace ImStb #define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 -#define IM_FLOOR(_VAL) ((float)(int)(_VAL)) // ImFloor() is not inlined in MSVC debug builds +#define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // ImTrunc() is not inlined in MSVC debug builds #define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // +#define IM_STRINGIFY_HELPER(_X) #_X +#define IM_STRINGIFY(_X) IM_STRINGIFY_HELPER(_X) // Preprocessor idiom to stringify e.g. an integer. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +#define IM_FLOOR IM_TRUNC +#endif // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall #ifdef _MSC_VER @@ -289,16 +300,28 @@ namespace ImStb #elif defined(__clang__) #define IM_DEBUG_BREAK() __builtin_debugtrap() #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) -#define IM_DEBUG_BREAK() __asm__ volatile("int $0x03") +#define IM_DEBUG_BREAK() __asm__ volatile("int3;nop") #elif defined(__GNUC__) && defined(__thumb__) #define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xde01") #elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__) -#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0"); +#define IM_DEBUG_BREAK() __asm__ volatile(".inst 0xe7f001f0") #else #define IM_DEBUG_BREAK() IM_ASSERT(0) // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger! #endif #endif // #ifndef IM_DEBUG_BREAK +// Format specifiers, printing 64-bit hasn't been decently standardized... +// In a real application you should be using PRId64 and PRIu64 from (non-windows) and on Windows define them yourself. +#if defined(_MSC_VER) && !defined(__clang__) +#define IM_PRId64 "I64d" +#define IM_PRIu64 "I64u" +#define IM_PRIX64 "I64X" +#else +#define IM_PRId64 "lld" +#define IM_PRIu64 "llu" +#define IM_PRIX64 "llX" +#endif + //----------------------------------------------------------------------------- // [SECTION] Generic helpers // Note that the ImXXX helpers functions are lower-level than ImGui functions. @@ -342,18 +365,18 @@ static inline bool ImIsPowerOfTwo(ImU64 v) { return v != 0 && (v & static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } // Helpers: String -IMGUI_API int ImStricmp(const char* str1, const char* str2); -IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); -IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); -IMGUI_API char* ImStrdup(const char* str); -IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); -IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); -IMGUI_API int ImStrlenW(const ImWchar* str); +IMGUI_API int ImStricmp(const char* str1, const char* str2); // Case insensitive compare. +IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); // Case insensitive compare to a certain count. +IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); // Copy to a certain count and always zero terminate (strncpy doesn't). +IMGUI_API char* ImStrdup(const char* str); // Duplicate a string. +IMGUI_API char* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str); // Copy in provided buffer, recreate buffer if needed. +IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); // Find first occurrence of 'c' in string range. IMGUI_API const char* ImStreolRange(const char* str, const char* str_end); // End end-of-line -IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line -IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); -IMGUI_API void ImStrTrimBlanks(char* str); -IMGUI_API const char* ImStrSkipBlank(const char* str); +IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); // Find a substring in a string range. +IMGUI_API void ImStrTrimBlanks(char* str); // Remove leading and trailing blanks from a buffer. +IMGUI_API const char* ImStrSkipBlank(const char* str); // Find first non-blank character. +IMGUI_API int ImStrlenW(const ImWchar* str); // Computer string length (ImWchar string) +IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line (ImWchar string) IM_MSVC_RUNTIME_CHECKS_OFF static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } @@ -380,6 +403,7 @@ IMGUI_API int ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, co IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end); // return number of bytes to express one char in UTF-8 IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 +IMGUI_API const char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr); // return previous UTF-8 code-point. // Helpers: File System #ifdef IMGUI_DISABLE_FILE_FUNCTIONS @@ -415,7 +439,6 @@ IM_MSVC_RUNTIME_CHECKS_OFF #define ImAcos(X) acosf(X) #define ImAtan2(Y, X) atan2f((Y), (X)) #define ImAtof(STR) atof(STR) -//#define ImFloorStd(X) floorf(X) // We use our own, see ImFloor() and ImFloorSigned() #define ImCeil(X) ceilf(X) static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision static inline double ImPow(double x, double y) { return pow(x, y); } @@ -453,10 +476,10 @@ static inline float ImSaturate(float f) static inline float ImLengthSqr(const ImVec2& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y); } static inline float ImLengthSqr(const ImVec4& lhs) { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); } static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; } -static inline float ImFloor(float f) { return (float)(int)(f); } -static inline float ImFloorSigned(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() -static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } -static inline ImVec2 ImFloorSigned(const ImVec2& v) { return ImVec2(ImFloorSigned(v.x), ImFloorSigned(v.y)); } +static inline float ImTrunc(float f) { return (float)(int)(f); } +static inline ImVec2 ImTrunc(const ImVec2& v) { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); } +static inline float ImFloor(float f) { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf() +static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2(ImFloor(v.x), ImFloor(v.y)); } static inline int ImModPositive(int a, int b) { return (a + b) % b; } static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } @@ -476,7 +499,6 @@ IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, c IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); inline float ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; } -IMGUI_API ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy); // Helper: ImVec1 (1D vector) // (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches) @@ -520,6 +542,7 @@ struct IMGUI_API ImRect ImVec2 GetBR() const { return Max; } // Bottom-right bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; } + bool ContainsWithPad(const ImVec2& p, const ImVec2& pad) const { return p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y; } bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; } void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } @@ -530,7 +553,7 @@ struct IMGUI_API ImRect void TranslateY(float dy) { Min.y += dy; Max.y += dy; } void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. - void Floor() { Min.x = IM_FLOOR(Min.x); Min.y = IM_FLOOR(Min.y); Max.x = IM_FLOOR(Max.x); Max.y = IM_FLOOR(Max.y); } + void Floor() { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); } bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } ImVec4 ToVec4() const { return ImVec4(Min.x, Min.y, Max.x, Max.y); } }; @@ -669,9 +692,6 @@ struct ImPool int GetBufSize() const { return Buf.Size; } int GetMapSize() const { return Map.Data.Size; } // It is the map we need iterate to find valid items, since we don't have "alive" storage anywhere T* TryGetMapData(ImPoolIdx n) { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - int GetSize() { return GetMapSize(); } // For ImPlot: should use GetMapSize() from (IMGUI_VERSION_NUM >= 18304) -#endif }; // Helper: ImChunkStream<> @@ -695,7 +715,6 @@ struct ImChunkStream int offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; } T* ptr_from_offset(int off) { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); } void swap(ImChunkStream& rhs) { rhs.Buf.swap(Buf); } - }; // Helper: ImGuiTextIndex<> @@ -768,12 +787,10 @@ struct IMGUI_API ImDrawListSharedData struct ImDrawDataBuilder { - ImVector Layers[2]; // Global layers for: regular, tooltip + ImVector* Layers[2]; // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData. + ImVector LayerData1; - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } - int GetDrawListCount() const { int count = 0; for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) count += Layers[n].Size; return count; } - IMGUI_API void FlattenIntoSingleLayer(); + ImDrawDataBuilder() { memset(this, 0, sizeof(*this)); } }; //----------------------------------------------------------------------------- @@ -798,9 +815,11 @@ enum ImGuiItemFlags_ ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable() + ImGuiItemFlags_AllowOverlap = 1 << 9, // false // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame. // Controlled by widget code ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_HasSelectionUserData = 1 << 11, // false // Set by SetNextItemSelectionUserData() }; // Status flags for an already submitted item @@ -816,8 +835,8 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. - ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) - ImGuiItemStatusFlags_Visible = 1 << 9, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()). + ImGuiItemStatusFlags_Visible = 1 << 8, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()). + ImGuiItemStatusFlags_HasClipRect = 1 << 9, // g.LastItemData.ClipRect is valid // Additional status + semantic for ImGuiTestEngine #ifdef IMGUI_ENABLE_TEST_ENGINE @@ -829,6 +848,14 @@ enum ImGuiItemStatusFlags_ #endif }; +// Extend ImGuiHoveredFlags_ +enum ImGuiHoveredFlagsPrivate_ +{ + ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay, + ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, + ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_, +}; + // Extend ImGuiInputTextFlags_ enum ImGuiInputTextFlagsPrivate_ { @@ -849,7 +876,7 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_PressedOnDragDropHold = 1 << 9, // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) ImGuiButtonFlags_Repeat = 1 << 10, // hold to repeat ImGuiButtonFlags_FlattenChildren = 1 << 11, // allow interactions even if a child window is overlapping - ImGuiButtonFlags_AllowItemOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() + ImGuiButtonFlags_AllowOverlap = 1 << 12, // require previous frame HoveredId to either match id or be null before being usable. ImGuiButtonFlags_DontClosePopups = 1 << 13, // disable automatically closing parent popup on press // [UNUSED] //ImGuiButtonFlags_Disabled = 1 << 14, // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine @@ -873,7 +900,7 @@ enum ImGuiComboFlagsPrivate_ enum ImGuiSliderFlagsPrivate_ { ImGuiSliderFlags_Vertical = 1 << 20, // Should this slider be orientated vertically? - ImGuiSliderFlags_ReadOnly = 1 << 21, + ImGuiSliderFlags_ReadOnly = 1 << 21, // Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags_ReadOnly instead. }; // Extend ImGuiSelectableFlags_ @@ -894,6 +921,7 @@ enum ImGuiSelectableFlagsPrivate_ enum ImGuiTreeNodeFlagsPrivate_ { ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20, + ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 21,// (FIXME-WIP) Turn Down arrow into an Up arrow, but reversed trees (#6517) }; enum ImGuiSeparatorFlags_ @@ -901,7 +929,17 @@ enum ImGuiSeparatorFlags_ ImGuiSeparatorFlags_None = 0, ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar ImGuiSeparatorFlags_Vertical = 1 << 1, - ImGuiSeparatorFlags_SpanAllColumns = 1 << 2, + ImGuiSeparatorFlags_SpanAllColumns = 1 << 2, // Make separator cover all columns of a legacy Columns() set. +}; + +// Flags for FocusWindow(). This is not called ImGuiFocusFlags to avoid confusion with public-facing ImGuiFocusedFlags. +// FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf() +// and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in. +enum ImGuiFocusRequestFlags_ +{ + ImGuiFocusRequestFlags_None = 0, + ImGuiFocusRequestFlags_RestoreFocusedChild = 1 << 0, // Find last focused child (if any) and focus it instead. + ImGuiFocusRequestFlags_UnlessBelowModal = 1 << 1, // Do not set focus if the window is below a modal. }; enum ImGuiTextFlags_ @@ -913,7 +951,7 @@ enum ImGuiTextFlags_ enum ImGuiTooltipFlags_ { ImGuiTooltipFlags_None = 0, - ImGuiTooltipFlags_OverridePreviousTooltip = 1 << 0, // Override will clear/ignore previously submitted tooltip (defaults to append) + ImGuiTooltipFlags_OverridePrevious = 1 << 1, // Clear/ignore previously submitted tooltip (defaults to append) }; // FIXME: this is in development, not exposed/functional as a generic feature yet. @@ -947,43 +985,6 @@ enum ImGuiPlotType ImGuiPlotType_Histogram, }; -enum ImGuiPopupPositionPolicy -{ - ImGuiPopupPositionPolicy_Default, - ImGuiPopupPositionPolicy_ComboBox, - ImGuiPopupPositionPolicy_Tooltip, -}; - -struct ImGuiDataVarInfo -{ - ImGuiDataType Type; - ImU32 Count; // 1+ - ImU32 Offset; // Offset in parent structure - void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); } -}; - -struct ImGuiDataTypeTempStorage -{ - ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT -}; - -// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). -struct ImGuiDataTypeInfo -{ - size_t Size; // Size in bytes - const char* Name; // Short descriptive name for the type, for debugging - const char* PrintFmt; // Default printf format for the type - const char* ScanFmt; // Default scanf format for the type -}; - -// Extend ImGuiDataType_ -enum ImGuiDataTypePrivate_ -{ - ImGuiDataType_String = ImGuiDataType_COUNT + 1, - ImGuiDataType_Pointer, - ImGuiDataType_ID, -}; - // Stacked color modifier, backup of modified data so we can restore it struct ImGuiColorMod { @@ -1020,6 +1021,7 @@ struct IMGUI_API ImGuiGroupData ImGuiID WindowID; ImVec2 BackupCursorPos; ImVec2 BackupCursorMaxPos; + ImVec2 BackupCursorPosPrevLine; ImVec1 BackupIndent; ImVec1 BackupGroupOffset; ImVec2 BackupCurrLineSize; @@ -1027,6 +1029,7 @@ struct IMGUI_API ImGuiGroupData ImGuiID BackupActiveIdIsAlive; bool BackupActiveIdPreviousFrameIsAlive; bool BackupHoveredIdIsAlive; + bool BackupIsSameLine; bool EmitItem; }; @@ -1048,6 +1051,15 @@ struct IMGUI_API ImGuiMenuColumns void CalcNextTotalWidth(bool update_offsets); }; +// Internal temporary state for deactivating InputText() instances. +struct IMGUI_API ImGuiInputTextDeactivatedState +{ + ImGuiID ID; // widget id owning the text state (which just got deactivated) + ImVector TextA; // text buffer + + ImGuiInputTextDeactivatedState() { memset(this, 0, sizeof(*this)); } + void ClearFreeMemory() { ID = 0; TextA.clear(); } +}; // Internal state of the currently focused/edited text input box // For a given item ID, access with ImGui::GetInputTextState() struct IMGUI_API ImGuiInputTextState @@ -1057,7 +1069,7 @@ struct IMGUI_API ImGuiInputTextState int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not. ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. ImVector TextA; // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity. - ImVector InitialTextA; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + ImVector InitialTextA; // value to revert to when pressing Escape = backup of end-user buffer at the time of focus (in UTF-8, unaltered) bool TextAIsValid; // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument) int BufCapacityA; // end-user buffer capacity float ScrollX; // horizontal scrolling/offset @@ -1067,12 +1079,15 @@ struct IMGUI_API ImGuiInputTextState bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. + bool ReloadUserBuf; // force a reload of user buf so it may be modified externally. may be automatic in future version. + int ReloadSelectionStart; // POSITIONS ARE IN IMWCHAR units *NOT* UTF-8 this is why this is not exposed yet. + int ReloadSelectionEnd; ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } int GetUndoAvailCount() const { return Stb.undostate.undo_point; } - int GetRedoAvailCount() const { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } + int GetRedoAvailCount() const { return IMSTB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; } void OnKeyPressed(int key); // Cannot be inline because we call in code in stb_textedit.h implementation // Cursor & Selection @@ -1084,21 +1099,16 @@ struct IMGUI_API ImGuiInputTextState int GetSelectionStart() const { return Stb.select_start; } int GetSelectionEnd() const { return Stb.select_end; } void SelectAll() { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; } -}; -// Storage for current popup stack -struct ImGuiPopupData -{ - ImGuiID PopupId; // Set on OpenPopup() - ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() - ImGuiWindow* BackupNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close - int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value - int OpenFrameCount; // Set on OpenPopup() - ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) - ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) - ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup + // Reload user buf (WIP #2890) + // If you modify underlying user-passed const char* while active you need to call this (InputText V2 may lift this) + // strcpy(my_buf, "hello"); + // if (ImGuiInputTextState* state = ImGui::GetInputTextState(id)) // id may be ImGui::GetItemID() is last item + // state->ReloadUserBufAndSelectAll(); + void ReloadUserBufAndSelectAll() { ReloadUserBuf = true; ReloadSelectionStart = 0; ReloadSelectionEnd = INT_MAX; } + void ReloadUserBufAndKeepSelection() { ReloadUserBuf = true; ReloadSelectionStart = Stb.select_start; ReloadSelectionEnd = Stb.select_end; } + void ReloadUserBufAndMoveToEnd() { ReloadUserBuf = true; ReloadSelectionStart = ReloadSelectionEnd = INT_MAX; } - ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; } }; enum ImGuiNextWindowDataFlags_ @@ -1112,6 +1122,7 @@ enum ImGuiNextWindowDataFlags_ ImGuiNextWindowDataFlags_HasFocus = 1 << 5, ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, ImGuiNextWindowDataFlags_HasScroll = 1 << 7, + ImGuiNextWindowDataFlags_HasChildFlags = 1 << 8, }; // Storage for SetNexWindow** functions @@ -1126,6 +1137,7 @@ struct ImGuiNextWindowData ImVec2 SizeVal; ImVec2 ContentSizeVal; ImVec2 ScrollVal; + ImGuiChildFlags ChildFlags; bool CollapsedVal; ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; @@ -1137,23 +1149,31 @@ struct ImGuiNextWindowData inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } }; +// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect() +// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.) +typedef ImS64 ImGuiSelectionUserData; + enum ImGuiNextItemDataFlags_ { - ImGuiNextItemDataFlags_None = 0, - ImGuiNextItemDataFlags_HasWidth = 1 << 0, - ImGuiNextItemDataFlags_HasOpen = 1 << 1, + ImGuiNextItemDataFlags_None = 0, + ImGuiNextItemDataFlags_HasWidth = 1 << 0, + ImGuiNextItemDataFlags_HasOpen = 1 << 1, + ImGuiNextItemDataFlags_HasShortcut = 1 << 2, }; struct ImGuiNextItemData { ImGuiNextItemDataFlags Flags; - float Width; // Set by SetNextItemWidth() - ImGuiID FocusScopeId; // Set by SetNextItemMultiSelectData() (!= 0 signify value has been set, so it's an alternate version of HasSelectionData, we don't use Flags for this because they are cleared too early. This is mostly used for debugging) - ImGuiCond OpenCond; - bool OpenVal; // Set by SetNextItemOpen() + ImGuiItemFlags ItemFlags; // Currently only tested/used for ImGuiItemFlags_AllowOverlap. + // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem() + ImGuiSelectionUserData SelectionUserData; // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values) + float Width; // Set by SetNextItemWidth() + ImGuiKeyChord Shortcut; // Set by SetNextItemShortcut() + bool OpenVal; // Set by SetNextItemOpen() + ImGuiCond OpenCond : 8; - ImGuiNextItemData() { memset(this, 0, sizeof(*this)); } - inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; } // Also cleared manually by ItemAdd()! + ImGuiNextItemData() { memset(this, 0, sizeof(*this)); SelectionUserData = -1; } + inline void ClearFlags() { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()! }; // Status storage for the last submitted item @@ -1164,11 +1184,23 @@ struct ImGuiLastItemData ImGuiItemStatusFlags StatusFlags; // See ImGuiItemStatusFlags_ ImRect Rect; // Full rectangle ImRect NavRect; // Navigation scoring rectangle (not displayed) - ImRect DisplayRect; // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set) + // Rarely used fields are not explicitly cleared, only valid when the corresponding ImGuiItemStatusFlags is set. + ImRect DisplayRect; // Display rectangle (ONLY VALID IF ImGuiItemStatusFlags_HasDisplayRect is set) + ImRect ClipRect; // Clip rectangle at the time of submitting item (ONLY VALID IF ImGuiItemStatusFlags_HasClipRect is set) ImGuiLastItemData() { memset(this, 0, sizeof(*this)); } }; +// Store data emitted by TreeNode() for usage by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere. +// This is the minimum amount of data that we need to perform the equivalent of NavApplyItemToResult() and which we can't infer in TreePop() +// Only stored when the node is a potential candidate for landing on a Left arrow jump. +struct ImGuiNavTreeNodeData +{ + ImGuiID ID; + ImGuiItemFlags InFlags; + ImRect NavRect; +}; + struct IMGUI_API ImGuiStackSizes { short SizeOfIDStack; @@ -1189,9 +1221,9 @@ struct IMGUI_API ImGuiStackSizes // Data saved for each window pushed into the stack struct ImGuiWindowStackData { - ImGuiWindow* Window; - ImGuiLastItemData ParentLastItemDataBackup; - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting + ImGuiWindow* Window; + ImGuiLastItemData ParentLastItemDataBackup; + ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting }; struct ImGuiShrinkWidthItem @@ -1210,6 +1242,66 @@ struct ImGuiPtrOrIndex ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } }; +//----------------------------------------------------------------------------- +// [SECTION] Data types support +//----------------------------------------------------------------------------- + +struct ImGuiDataVarInfo +{ + ImGuiDataType Type; + ImU32 Count; // 1+ + ImU32 Offset; // Offset in parent structure + void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); } +}; + +struct ImGuiDataTypeTempStorage +{ + ImU8 Data[8]; // Can fit any data up to ImGuiDataType_COUNT +}; + +// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo(). +struct ImGuiDataTypeInfo +{ + size_t Size; // Size in bytes + const char* Name; // Short descriptive name for the type, for debugging + const char* PrintFmt; // Default printf format for the type + const char* ScanFmt; // Default scanf format for the type +}; + +// Extend ImGuiDataType_ +enum ImGuiDataTypePrivate_ +{ + ImGuiDataType_String = ImGuiDataType_COUNT + 1, + ImGuiDataType_Pointer, + ImGuiDataType_ID, +}; + +//----------------------------------------------------------------------------- +// [SECTION] Popup support +//----------------------------------------------------------------------------- + +enum ImGuiPopupPositionPolicy +{ + ImGuiPopupPositionPolicy_Default, + ImGuiPopupPositionPolicy_ComboBox, + ImGuiPopupPositionPolicy_Tooltip, +}; + +// Storage for popup stacks (g.OpenPopupStack and g.BeginPopupStack) +struct ImGuiPopupData +{ + ImGuiID PopupId; // Set on OpenPopup() + ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() + ImGuiWindow* BackupNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close + int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value + int OpenFrameCount; // Set on OpenPopup() + ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) + ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) + ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup + + ImGuiPopupData() { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; } +}; + //----------------------------------------------------------------------------- // [SECTION] Inputs support //----------------------------------------------------------------------------- @@ -1254,19 +1346,18 @@ enum ImGuiInputEventType enum ImGuiInputSource { ImGuiInputSource_None = 0, - ImGuiInputSource_Mouse, + ImGuiInputSource_Mouse, // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them. ImGuiInputSource_Keyboard, ImGuiInputSource_Gamepad, ImGuiInputSource_Clipboard, // Currently only used by InputText() - ImGuiInputSource_Nav, // Stored in g.ActiveIdSource only ImGuiInputSource_COUNT }; // FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension? // Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor' -struct ImGuiInputEventMousePos { float PosX, PosY; }; -struct ImGuiInputEventMouseWheel { float WheelX, WheelY; }; -struct ImGuiInputEventMouseButton { int Button; bool Down; }; +struct ImGuiInputEventMousePos { float PosX, PosY; ImGuiMouseSource MouseSource; }; +struct ImGuiInputEventMouseWheel { float WheelX, WheelY; ImGuiMouseSource MouseSource; }; +struct ImGuiInputEventMouseButton { int Button; bool Down; ImGuiMouseSource MouseSource; }; struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; struct ImGuiInputEventText { unsigned int Char; }; struct ImGuiInputEventAppFocused { bool Focused; }; @@ -1275,6 +1366,7 @@ struct ImGuiInputEvent { ImGuiInputEventType Type; ImGuiInputSource Source; + ImU32 EventId; // Unique, sequential increasing integer to identify an event (if you need to correlate them to other data). union { ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos @@ -1300,11 +1392,12 @@ struct ImGuiKeyRoutingData { ImGuiKeyRoutingIndex NextEntryIndex; ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImGuiMod_Shortcut is already translated to Ctrl/Super. + ImU8 RoutingCurrScore; // [DEBUG] For debug display ImU8 RoutingNextScore; // Lower is better (0: perfect score) ImGuiID RoutingCurr; ImGuiID RoutingNext; - ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; } + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingCurrScore = RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; } }; // Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. @@ -1332,49 +1425,69 @@ struct ImGuiKeyOwnerData }; // Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() -// Don't mistake with ImGuiInputTextFlags! (for ImGui::InputText() function) +// Don't mistake with ImGuiInputTextFlags! (which is for ImGui::InputText() function) enum ImGuiInputFlags_ { - // Flags for IsKeyPressed(), IsMouseClicked(), Shortcut() + // Flags for IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked(), Shortcut() ImGuiInputFlags_None = 0, - ImGuiInputFlags_Repeat = 1 << 0, // Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1. + + // Repeat mode + ImGuiInputFlags_Repeat = 1 << 0, // Enable repeat. Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1. ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default) ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster - ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak, + + // Repeat mode: Specify when repeating key pressed can be interrupted. + // In theory ImGuiInputFlags_RepeatUntilOtherKeyPress may be a desirable default, but it would break too many behavior so everything is opt-in. + ImGuiInputFlags_RepeatUntilRelease = 1 << 4, // Stop repeating when released (default for all functions except Shortcut). This only exists to allow overriding Shortcut() default behavior. + ImGuiInputFlags_RepeatUntilKeyModsChange = 1 << 5, // Stop repeating when released OR if keyboard mods are changed (default for Shortcut) + ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone = 1 << 6, // Stop repeating when released OR if keyboard mods are leaving the None state. Allows going from Mod+Key to Key by releasing Mod. + ImGuiInputFlags_RepeatUntilOtherKeyPress = 1 << 7, // Stop repeating when released OR if any other keyboard key is pressed during the repeat // Flags for SetItemKeyOwner() - ImGuiInputFlags_CondHovered = 1 << 4, // Only set if item is hovered (default to both) - ImGuiInputFlags_CondActive = 1 << 5, // Only set if item is active (default to both) + ImGuiInputFlags_CondHovered = 1 << 8, // Only set if item is hovered (default to both) + ImGuiInputFlags_CondActive = 1 << 9, // Only set if item is active (default to both) ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, - ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, // Flags for SetKeyOwner(), SetItemKeyOwner() - ImGuiInputFlags_LockThisFrame = 1 << 6, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code. - ImGuiInputFlags_LockUntilRelease = 1 << 7, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code. + // Locking is useful to make input-owner-aware code steal keys from non-input-owner-aware code. If all code is input-owner-aware locking would never be necessary. + ImGuiInputFlags_LockThisFrame = 1 << 10, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. + ImGuiInputFlags_LockUntilRelease = 1 << 11, // Further accesses to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. // Routing policies for Shortcut() + low-level SetShortcutRouting() // - The general idea is that several callers register interest in a shortcut, and only one owner gets it. - // - When a policy (other than _RouteAlways) is set, Shortcut() will register itself with SetShortcutRouting(), + // Parent -> call Shortcut(Ctrl+S) // When Parent is focused, Parent gets the shortcut. + // Child1 -> call Shortcut(Ctrl+S) // When Child1 is focused, Child1 gets the shortcut (Child1 overrides Parent shortcuts) + // Child2 -> no call // When Child2 is focused, Parent gets the shortcut. + // The whole system is order independent, so if Child1 does it calls before Parent results will be identical. + // This is an important property as it facilitate working with foreign code or larger codebase. + // - Visualize registered routes in 'Metrics->Inputs' and submitted routes in 'Debug Log->InputRouting'. + // - When a policy (except for _RouteAlways *) is set, Shortcut() will register itself with SetShortcutRouting(), // allowing the system to decide where to route the input among other route-aware calls. - // - Shortcut() uses ImGuiInputFlags_RouteFocused by default: meaning that a simple Shortcut() poll - // will register a route and only succeed when parent window is in the focus stack and if no-one - // with a higher priority is claiming the shortcut. - // - Using ImGuiInputFlags_RouteAlways is roughly equivalent to doing e.g. IsKeyPressed(key) + testing mods. + // (* Using ImGuiInputFlags_RouteAlways is roughly equivalent to calling IsKeyChordPressed(key)). + // - Shortcut() uses ImGuiInputFlags_RouteFocused by default. Meaning that a Shortcut() call will register + // a route and only succeed when parent window is in the focus-stack and if no-one with a higher priority + // is claiming the same shortcut. + // - You can chain two unrelated windows in the focus stack using SetWindowParentWindowForFocusRoute(). // - Priorities: GlobalHigh > Focused (when owner is active item) > Global > Focused (when focused window) > GlobalLow. // - Can select only 1 policy among all available. - ImGuiInputFlags_RouteFocused = 1 << 8, // (Default) Register focused route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window. - ImGuiInputFlags_RouteGlobalLow = 1 << 9, // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority. - ImGuiInputFlags_RouteGlobal = 1 << 10, // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText). - ImGuiInputFlags_RouteGlobalHigh = 1 << 11, // Register route globally (highest priority: unlikely you need to use that: will interfere with every active items) - ImGuiInputFlags_RouteMask_ = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh, // _Always not part of this! - ImGuiInputFlags_RouteAlways = 1 << 12, // Do not register route, poll keys directly. - ImGuiInputFlags_RouteUnlessBgFocused= 1 << 13, // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications. - ImGuiInputFlags_RouteExtraMask_ = ImGuiInputFlags_RouteAlways | ImGuiInputFlags_RouteUnlessBgFocused, + ImGuiInputFlags_RouteFocused = 1 << 12, // (Default) Honor focus route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window. + ImGuiInputFlags_RouteGlobalLow = 1 << 13, // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority IF you need a Global priority. + ImGuiInputFlags_RouteGlobal = 1 << 14, // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText will take priority over this). + ImGuiInputFlags_RouteGlobalHigh = 1 << 15, // Register route globally (higher priority: unlikely you need to use that: will interfere with every active items, e.g. CTRL+A registered by InputText will be overriden by this) + ImGuiInputFlags_RouteAlways = 1 << 16, // Do not register route, poll keys directly. + // Routing polices: extra options + ImGuiInputFlags_RouteUnlessBgFocused= 1 << 17, // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications. // [Internal] Mask of which function support which flags - ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_, - ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteExtraMask_, + ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak, + ImGuiInputFlags_RepeatUntilMask_ = ImGuiInputFlags_RepeatUntilRelease | ImGuiInputFlags_RepeatUntilKeyModsChange | ImGuiInputFlags_RepeatUntilKeyModsChangeFromNone | ImGuiInputFlags_RepeatUntilOtherKeyPress, + ImGuiInputFlags_RepeatMask_ = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RepeatUntilMask_, + ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, + ImGuiInputFlags_RouteMask_ = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh, // _Always not part of this! + ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_RepeatMask_, + ImGuiInputFlags_SupportedByIsMouseClicked = ImGuiInputFlags_Repeat, + ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_RepeatMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteAlways | ImGuiInputFlags_RouteUnlessBgFocused, ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease, ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_, }; @@ -1383,6 +1496,7 @@ enum ImGuiInputFlags_ // [SECTION] Clipper support //----------------------------------------------------------------------------- +// Note that Max is exclusive, so perhaps should be using a Begin/End convention. struct ImGuiListClipperRange { int Min; @@ -1418,6 +1532,8 @@ enum ImGuiActivateFlags_ ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key. ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used. ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) + ImGuiActivateFlags_FromTabbing = 1 << 3, // Activation requested by a tabbing request + ImGuiActivateFlags_FromShortcut = 1 << 4, // Activation requested by an item shortcut via SetNextItemShortcut() function. }; // Early work-in-progress API for ScrollToItem() @@ -1438,8 +1554,7 @@ enum ImGuiScrollFlags_ enum ImGuiNavHighlightFlags_ { ImGuiNavHighlightFlags_None = 0, - ImGuiNavHighlightFlags_TypeDefault = 1 << 0, - ImGuiNavHighlightFlags_TypeThin = 1 << 1, + ImGuiNavHighlightFlags_Compact = 1 << 1, // Compact highlight, no padding ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. ImGuiNavHighlightFlags_NoRounding = 1 << 3, }; @@ -1451,15 +1566,18 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_LoopY = 1 << 1, ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful but provided for completeness + ImGuiNavMoveFlags_WrapMask_ = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY, ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5, // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown) ImGuiNavMoveFlags_ScrollToEdgeY = 1 << 6, // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary ImGuiNavMoveFlags_Forwarded = 1 << 7, ImGuiNavMoveFlags_DebugNoResult = 1 << 8, // Dummy scoring for debug purpose, don't apply result - ImGuiNavMoveFlags_FocusApi = 1 << 9, - ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight - ImGuiNavMoveFlags_Activate = 1 << 11, - ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12, // Do not alter the visible state of keyboard vs mouse nav highlight + ImGuiNavMoveFlags_FocusApi = 1 << 9, // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details) + ImGuiNavMoveFlags_IsTabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight + ImGuiNavMoveFlags_IsPageMove = 1 << 11, // Identify a PageDown/PageUp request. + ImGuiNavMoveFlags_Activate = 1 << 12, // Activate/select target item. + ImGuiNavMoveFlags_NoSelect = 1 << 13, // Don't trigger selection by not setting g.NavJustMovedTo + ImGuiNavMoveFlags_NoSetNavHighlight = 1 << 14, // Do not alter the visible state of keyboard vs mouse nav highlight }; enum ImGuiNavLayer @@ -1476,19 +1594,63 @@ struct ImGuiNavItemData ImGuiID FocusScopeId; // Init,Move // Best candidate focus scope ID ImRect RectRel; // Init,Move // Best candidate bounding box in window relative space ImGuiItemFlags InFlags; // ????,Move // Best candidate item flags + ImGuiSelectionUserData SelectionUserData;//I+Mov // Best candidate SetNextItemSelectionData() value. float DistBox; // Move // Best candidate box distance to current NavId float DistCenter; // Move // Best candidate center distance to current NavId float DistAxial; // Move // Best candidate axial distance to current NavId ImGuiNavItemData() { Clear(); } - void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; DistBox = DistCenter = DistAxial = FLT_MAX; } + void Clear() { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; } +}; + +struct ImGuiFocusScopeData +{ + ImGuiID ID; + ImGuiID WindowID; +}; + +//----------------------------------------------------------------------------- +// [SECTION] Typing-select support +//----------------------------------------------------------------------------- + +// Flags for GetTypingSelectRequest() +enum ImGuiTypingSelectFlags_ +{ + ImGuiTypingSelectFlags_None = 0, + ImGuiTypingSelectFlags_AllowBackspace = 1 << 0, // Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state) + ImGuiTypingSelectFlags_AllowSingleCharMode = 1 << 1, // Allow "single char" search mode which is activated when pressing the same character multiple times. +}; + +// Returned by GetTypingSelectRequest(), designed to eventually be public. +struct IMGUI_API ImGuiTypingSelectRequest +{ + ImGuiTypingSelectFlags Flags; // Flags passed to GetTypingSelectRequest() + int SearchBufferLen; + const char* SearchBuffer; // Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize). + bool SelectRequest; // Set when buffer was modified this frame, requesting a selection. + bool SingleCharMode; // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication. + ImS8 SingleCharSize; // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input. +}; + +// Storage for GetTypingSelectRequest() +struct IMGUI_API ImGuiTypingSelectState +{ + ImGuiTypingSelectRequest Request; // User-facing data + char SearchBuffer[64]; // Search buffer: no need to make dynamic as this search is very transient. + ImGuiID FocusScope; + int LastRequestFrame = 0; + float LastRequestTime = 0.0f; + bool SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing. + + ImGuiTypingSelectState() { memset(this, 0, sizeof(*this)); } + void Clear() { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging }; //----------------------------------------------------------------------------- // [SECTION] Columns support //----------------------------------------------------------------------------- -// Flags for internal's BeginColumns(). Prefix using BeginTable() nowadays! +// Flags for internal's BeginColumns(). This is an obsolete API. Prefer using BeginTable() nowadays! enum ImGuiOldColumnFlags_ { ImGuiOldColumnFlags_None = 0, @@ -1496,16 +1658,16 @@ enum ImGuiOldColumnFlags_ ImGuiOldColumnFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window - ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. + ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, // Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None, - ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder, - ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize, - ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths, - ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow, - ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize, + //ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None, + //ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder, + //ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize, + //ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths, + //ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow, + //ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize, #endif }; @@ -1544,6 +1706,9 @@ struct ImGuiOldColumns // [SECTION] Multi-select support //----------------------------------------------------------------------------- +// We always assume that -1 is an invalid value (which works for indices and pointers) +#define ImGuiSelectionUserData_Invalid ((ImGuiSelectionUserData)-1) + #ifdef IMGUI_HAS_MULTI_SELECT // #endif // #ifdef IMGUI_HAS_MULTI_SELECT @@ -1564,18 +1729,17 @@ struct ImGuiOldColumns // Every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { - int DrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used - ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. + int BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used + ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; - ImDrawDataBuilder DrawDataBuilder; - + ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData ImVec2 WorkOffsetMin; // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!) ImVec2 WorkOffsetMax; // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height). ImVec2 BuildWorkOffsetMin; // Work Area: Offset being built during current frame. Generally >= 0.0f. ImVec2 BuildWorkOffsetMax; // Work Area: Offset being built during current frame. Generally <= 0.0f. - ImGuiViewportP() { DrawListsLastFrame[0] = DrawListsLastFrame[1] = -1; DrawLists[0] = DrawLists[1] = NULL; } - ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } + ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } + ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) ImVec2 CalcWorkRectPos(const ImVec2& off_min) const { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); } @@ -1601,6 +1765,7 @@ struct ImGuiWindowSettings ImVec2ih Pos; ImVec2ih Size; bool Collapsed; + bool IsChild; bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) bool WantDelete; // Set to invalidate/delete the settings entry @@ -1630,6 +1795,7 @@ struct ImGuiSettingsHandler // This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack. enum ImGuiLocKey : int { + ImGuiLocKey_VersionStr, ImGuiLocKey_TableSizeOne, ImGuiLocKey_TableSizeAllFit, ImGuiLocKey_TableSizeAllDefault, @@ -1654,29 +1820,53 @@ struct ImGuiLocEntry enum ImGuiDebugLogFlags_ { // Event types - ImGuiDebugLogFlags_None = 0, - ImGuiDebugLogFlags_EventActiveId = 1 << 0, - ImGuiDebugLogFlags_EventFocus = 1 << 1, - ImGuiDebugLogFlags_EventPopup = 1 << 2, - ImGuiDebugLogFlags_EventNav = 1 << 3, - ImGuiDebugLogFlags_EventClipper = 1 << 4, - ImGuiDebugLogFlags_EventIO = 1 << 5, - ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventIO, - ImGuiDebugLogFlags_OutputToTTY = 1 << 10, // Also send output to TTY + ImGuiDebugLogFlags_None = 0, + ImGuiDebugLogFlags_EventActiveId = 1 << 0, + ImGuiDebugLogFlags_EventFocus = 1 << 1, + ImGuiDebugLogFlags_EventPopup = 1 << 2, + ImGuiDebugLogFlags_EventNav = 1 << 3, + ImGuiDebugLogFlags_EventClipper = 1 << 4, + ImGuiDebugLogFlags_EventSelection = 1 << 5, + ImGuiDebugLogFlags_EventIO = 1 << 6, + ImGuiDebugLogFlags_EventInputRouting = 1 << 7, + + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting, + ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY + ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine +}; + +struct ImGuiDebugAllocEntry +{ + int FrameCount; + ImS16 AllocCount; + ImS16 FreeCount; +}; + +struct ImGuiDebugAllocInfo +{ + int TotalAllocCount; // Number of call to MemAlloc(). + int TotalFreeCount; + ImS16 LastEntriesIdx; // Current index in buffer + ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations + + ImGuiDebugAllocInfo() { memset(this, 0, sizeof(*this)); } }; struct ImGuiMetricsConfig { bool ShowDebugLog = false; - bool ShowStackTool = false; + bool ShowIDStackTool = false; bool ShowWindowsRects = false; bool ShowWindowsBeginOrder = false; bool ShowTablesRects = false; bool ShowDrawCmdMesh = true; bool ShowDrawCmdBoundingBoxes = true; + bool ShowTextEncodingViewer = false; bool ShowAtlasTintedWithTextColor = false; int ShowWindowsRectsType = -1; int ShowTablesRectsType = -1; + int HighlightMonitorIdx = -1; + ImGuiID HighlightViewportID = 0; }; struct ImGuiStackLevelInfo @@ -1690,8 +1880,8 @@ struct ImGuiStackLevelInfo ImGuiStackLevelInfo() { memset(this, 0, sizeof(*this)); } }; -// State for Stack tool queries -struct ImGuiStackTool +// State for ID Stack tool queries +struct ImGuiIDStackTool { int LastActiveFrame; int StackLevel; // -1: query stack and resize Results, >= 0: individual stack level @@ -1700,7 +1890,7 @@ struct ImGuiStackTool bool CopyToClipboardOnCtrlC; float CopyToClipboardLastTime; - ImGuiStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; } + ImGuiIDStackTool() { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; } }; //----------------------------------------------------------------------------- @@ -1730,8 +1920,6 @@ struct ImGuiContext bool Initialized; bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; - ImVector InputEventsQueue; // Input events which will be tricked/written into IO structure. - ImVector InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail. ImGuiStyle Style; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. @@ -1748,6 +1936,12 @@ struct ImGuiContext bool TestEngineHookItems; // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log() void* TestEngine; // Test engine user data + // Inputs + ImVector InputEventsQueue; // Input events which will be trickled/written into IO structure. + ImVector InputEventsTrail; // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail. + ImGuiMouseSource InputEventsNextMouseSource; + ImU32 InputEventsNextEventId; + // Windows state ImVector Windows; // Windows, sorted in display order, back to front ImVector WindowsFocusOrder; // Root windows, sorted in focus order, back to front. @@ -1756,6 +1950,7 @@ struct ImGuiContext ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow* int WindowsActiveCount; // Number of unique windows submitted by frame ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING) + ImGuiID DebugBreakInWindow; // Set to break in Begin() call. ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. @@ -1763,12 +1958,13 @@ struct ImGuiContext ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL + int WheelingWindowScrolledFrame; float WheelingWindowReleaseTimer; ImVec2 WheelingWindowWheelRemainder; ImVec2 WheelingAxisAvg; // Item/widgets state and tracking information - ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] + ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; @@ -1784,10 +1980,11 @@ struct ImGuiContext bool ActiveIdHasBeenPressedBefore; // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch. bool ActiveIdHasBeenEditedBefore; // Was the value associated to the widget Edited over the course of the Active state. bool ActiveIdHasBeenEditedThisFrame; + bool ActiveIdFromShortcut; + int ActiveIdMouseButton : 8; ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) ImGuiWindow* ActiveIdWindow; - ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) - int ActiveIdMouseButton; + ImGuiInputSource ActiveIdSource; // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad ImGuiID ActiveIdPreviousFrame; bool ActiveIdPreviousFrameIsAlive; bool ActiveIdPreviousFrameHasBeenEditedBefore; @@ -1799,32 +1996,39 @@ struct ImGuiContext // - The idea is that instead of "eating" a given key, we can link to an owner. // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID. // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame(). + double LastKeyModsChangeTime; // Record the last time key mods changed (affect repeat delay when using shortcut logic) + double LastKeyModsChangeFromNoneTime; // Record the last time key mods changed away from being 0 (affect repeat delay when using shortcut logic) + double LastKeyboardKeyPressTime; // Record the last time a keyboard key (ignore mouse/gamepad ones) was pressed. + ImBitArrayForNamedKeys KeysMayBeCharInput; // Lookup to tell if a key can emit char input, see IsKeyChordPotentiallyCharInput(). sizeof() = 20 bytes ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT]; ImGuiKeyRoutingTable KeysRoutingTable; ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency) + ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);' #endif // Next window/item data - ImGuiID CurrentFocusScopeId; // == g.FocusScopeStack.back() - ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() + ImGuiID CurrentFocusScopeId; // Value for currently appending items == g.FocusScopeStack.back(). Not to be mistaken with g.NavFocusScopeId. + ImGuiItemFlags CurrentItemFlags; // Value for currently appending items == g.ItemFlagsStack.back() ImGuiID DebugLocateId; // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions + bool DebugShowGroupRects; // Shared stacks - ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() - ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() - ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() - ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() - ImVectorItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() - ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() - ImVectorOpenPopupStack; // Which popups are open (persistent) - ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) - int BeginMenuCount; + ImGuiCol DebugFlashStyleColorIdx; // (Keep close to ColorStack to share cache line) + ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() + ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() + ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() + ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() + ImVector ItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() + ImVector GroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() + ImVector OpenPopupStack; // Which popups are open (persistent) + ImVector BeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) + ImVector NavTreeNodeStack; // Stack for TreeNode() when a NavLeft requested is emitted. // Viewports ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. @@ -1832,18 +2036,22 @@ struct ImGuiContext // Gamepad/keyboard Navigation ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavId; // Focused item for navigation - ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set) + ImGuiID NavFocusScopeId; // Focused focus scope (e.g. selection code often wants to "clear other items" when landing on an item of the same scope) + ImVector NavFocusRoute; // Reversed copy focus scope stack for NavId (should contains NavFocusScopeId). This essentially follow the window->ParentWindowForFocusRoute chain. ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem() ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) ImGuiActivateFlags NavActivateFlags; + ImGuiID NavHighlightActivatedId; + float NavHighlightActivatedTimer; ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). ImGuiKeyChord NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; - ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. + ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse ImGuiNavLayer NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. + ImGuiSelectionUserData NavLastValidSelectionUserData; // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data. bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRectRel is valid bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) @@ -1853,8 +2061,7 @@ struct ImGuiContext bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd() bool NavInitRequest; // Init request for appearing window to select first item bool NavInitRequestFromMove; - ImGuiID NavInitResultId; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) - ImRect NavInitResultRectRel; // Init request result rectangle (relative to parent window) + ImGuiNavItemData NavInitResult; // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called) bool NavMoveSubmitted; // Move request submitted, will process result on next NewFrame() bool NavMoveScoringItems; // Move request submitted, still scoring incoming items bool NavMoveForwardToNextFrame; @@ -1883,12 +2090,12 @@ struct ImGuiContext float NavWindowingTimer; float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; + ImGuiKey NavWindowingToggleKey; ImVec2 NavWindowingAccumDeltaPos; ImVec2 NavWindowingAccumDeltaSize; // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImGuiMouseCursor MouseCursor; // Drag and Drop bool DragDropActive; @@ -1899,6 +2106,7 @@ struct ImGuiContext int DragDropMouseButton; ImGuiPayload DragDropPayload; ImRect DragDropTargetRect; // Store rectangle of current target candidate (we favor small targets when overlapping) + ImRect DragDropTargetClipRect; // Store ClipRect at the time of item's drawing ImGuiID DragDropTargetId; ImGuiDragDropFlags DragDropAcceptFlags; float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) @@ -1915,6 +2123,7 @@ struct ImGuiContext // Tables ImGuiTable* CurrentTable; + ImGuiID DebugBreakInTable; // Set to break in BeginTable() call. int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) ImVector TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting) ImPool Tables; // Persistent table data @@ -1928,16 +2137,25 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Hover Delay system - ImGuiID HoverDelayId; - ImGuiID HoverDelayIdPreviousFrame; - float HoverDelayTimer; // Currently used IsItemHovered(), generally inferred from g.HoveredIdTimer but kept uncleared until clear timer elapse. - float HoverDelayClearTimer; // Currently used IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + ImGuiID HoverItemDelayId; + ImGuiID HoverItemDelayIdPreviousFrame; + float HoverItemDelayTimer; // Currently used by IsItemHovered() + float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + ImGuiID HoverItemUnlockedStationaryId; // Mouse has once been stationary on this item. Only reset after departing the item. + ImGuiID HoverWindowUnlockedStationaryId; // Mouse has once been stationary on this window. Only reset after departing the window. + + // Mouse state + ImGuiMouseCursor MouseCursor; + float MouseStationaryTimer; // Time the mouse has been stationary (with some loose heuristic) + ImVec2 MouseLastValidPos; // Widget state - ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; + ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFont InputTextPasswordFont; ImGuiID TempInputId; // Temporary text input when CTRL+clicking on a slider, etc. + int BeginMenuDepth; + int BeginComboDepth; ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets ImGuiID ColorEditCurrentID; // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others). ImGuiID ColorEditSavedID; // ID we are saving/restoring HS for @@ -1946,6 +2164,8 @@ struct ImGuiContext ImU32 ColorEditSavedColor; // RGB value with alpha set to 0. ImVec4 ColorPickerRef; // Initial/reference color at the time of opening the color picker. ImGuiComboPreviewData ComboPreviewData; + ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving + bool WindowResizeRelativeMode; float SliderGrabClickOffset; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? @@ -1955,14 +2175,15 @@ struct ImGuiContext float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() short DisabledStackSize; + short LockMarkEdited; short TooltipOverrideCount; ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once + ImGuiTypingSelectState TypingSelectState; // State for GetTypingSelectRequest() // Platform support ImGuiPlatformImeData PlatformImeData; // Data updated by current frame ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data (when changing we will call io.SetPlatformImeDataFn - char PlatformLocaleDecimalPoint; // '.' or *localeconv()->decimal_point // Settings bool SettingsLoaded; @@ -1991,17 +2212,24 @@ struct ImGuiContext int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. // Debug Tools + // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.) ImGuiDebugLogFlags DebugLogFlags; ImGuiTextBuffer DebugLogBuf; ImGuiTextIndex DebugLogIndex; - ImU8 DebugLogClipperAutoDisableFrames; + ImGuiDebugLogFlags DebugLogAutoDisableFlags; + ImU8 DebugLogAutoDisableFrames; ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above. + bool DebugBreakInLocateId; // Debug break in ItemAdd() call for g.DebugLocateId. + ImGuiKeyChord DebugBreakKeyChord; // = ImGuiKey_Pause ImS8 DebugBeginReturnValueCullDepth; // Cycle between 0..9 then wrap around. bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) ImU8 DebugItemPickerMouseButton; ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID + float DebugFlashStyleColorTime; + ImVec4 DebugFlashStyleColorBackup; ImGuiMetricsConfig DebugMetricsConfig; - ImGuiStackTool DebugStackTool; + ImGuiIDStackTool DebugIDStackTool; + ImGuiDebugAllocInfo DebugAllocInfo; // Misc float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames.. @@ -2012,6 +2240,7 @@ struct ImGuiContext int WantCaptureKeyboardNextFrame; // " int WantTextInputNextFrame; ImVector TempBuffer; // Temporary text buffer + char TempKeychordName[64]; ImGuiContext(ImFontAtlas* shared_font_atlas) { @@ -2031,13 +2260,16 @@ struct ImGuiContext TestEngineHookItems = false; TestEngine = NULL; + InputEventsNextMouseSource = ImGuiMouseSource_Mouse; + InputEventsNextEventId = 1; + WindowsActiveCount = 0; CurrentWindow = NULL; HoveredWindow = NULL; HoveredWindowUnderMovingWindow = NULL; MovingWindow = NULL; WheelingWindow = NULL; - WheelingWindowStartFrame = -1; + WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1; WheelingWindowReleaseTimer = 0.0f; DebugHookIdInfo = 0; @@ -2054,6 +2286,7 @@ struct ImGuiContext ActiveIdHasBeenPressedBefore = false; ActiveIdHasBeenEditedBefore = false; ActiveIdHasBeenEditedThisFrame = false; + ActiveIdFromShortcut = false; ActiveIdClickOffset = ImVec2(-1, -1); ActiveIdWindow = NULL; ActiveIdSource = ImGuiInputSource_None; @@ -2065,6 +2298,8 @@ struct ImGuiContext LastActiveId = 0; LastActiveIdTimer = 0.0f; + LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0; + ActiveIdUsingNavDirMask = 0x00; ActiveIdUsingAllKeyboardKeys = false; #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -2073,15 +2308,18 @@ struct ImGuiContext CurrentFocusScopeId = 0; CurrentItemFlags = ImGuiItemFlags_None; - BeginMenuCount = 0; + DebugShowGroupRects = false; NavWindow = NULL; NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0; NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; + NavHighlightActivatedId = 0; + NavHighlightActivatedTimer = 0.0f; NavJustMovedToKeyMods = ImGuiMod_None; - NavInputSource = ImGuiInputSource_None; + NavInputSource = ImGuiInputSource_Keyboard; NavLayer = ImGuiNavLayer_Main; + NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; NavIdIsAlive = false; NavMousePosDirty = false; NavDisableHighlight = true; @@ -2089,7 +2327,6 @@ struct ImGuiContext NavAnyRequest = false; NavInitRequest = false; NavInitRequestFromMove = false; - NavInitResultId = 0; NavMoveSubmitted = false; NavMoveScoringItems = false; NavMoveForwardToNextFrame = false; @@ -2106,9 +2343,9 @@ struct ImGuiContext NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; + NavWindowingToggleKey = ImGuiKey_None; DimBgRatio = 0.0f; - MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; DragDropSourceFlags = ImGuiDragDropFlags_None; @@ -2128,14 +2365,19 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; - HoverDelayId = HoverDelayIdPreviousFrame = 0; - HoverDelayTimer = HoverDelayClearTimer = 0.0f; + HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; + HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; + + MouseCursor = ImGuiMouseCursor_Arrow; + MouseStationaryTimer = 0.0f; TempInputId = 0; + BeginMenuDepth = BeginComboDepth = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditCurrentID = ColorEditSavedID = 0; ColorEditSavedHue = ColorEditSavedSat = 0.0f; ColorEditSavedColor = 0; + WindowResizeRelativeMode = false; SliderGrabClickOffset = 0.0f; SliderCurrentAccum = 0.0f; SliderCurrentAccumDirty = false; @@ -2145,11 +2387,11 @@ struct ImGuiContext ScrollbarClickDeltaToGrabCenter = 0.0f; DisabledAlphaBackup = 0.0f; DisabledStackSize = 0; + LockMarkEdited = 0; TooltipOverrideCount = 0; PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission - PlatformLocaleDecimalPoint = '.'; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; @@ -2168,17 +2410,28 @@ struct ImGuiContext DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; DebugLocateId = 0; - DebugLogClipperAutoDisableFrames = 0; + DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; + DebugLogAutoDisableFrames = 0; DebugLocateFrames = 0; DebugBeginReturnValueCullDepth = -1; DebugItemPickerActive = false; DebugItemPickerMouseButton = ImGuiMouseButton_Left; DebugItemPickerBreakId = 0; + DebugFlashStyleColorTime = 0.0f; + DebugFlashStyleColorIdx = ImGuiCol_COUNT; + + // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations + DebugBreakInWindow = 0; + DebugBreakInTable = 0; + DebugBreakInLocateId = false; + DebugBreakKeyChord = ImGuiKey_Pause; + DebugBreakInShortcutRouting = ImGuiKey_None; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; FramerateSecPerFrameAccum = 0.0f; WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; + memset(TempKeychordName, 0, sizeof(TempKeychordName)); } }; @@ -2206,14 +2459,15 @@ struct IMGUI_API ImGuiWindowTempData ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; - ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensentate and fix the most common use case of large scroll area. + ImVec2 CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensate and fix the most common use case of large scroll area. // Keyboard/Gamepad navigation ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) short NavLayersActiveMask; // Which layers have been written to (result from previous frame) short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) + bool NavIsScrollPushableX; // Set when current work location may be scrolled horizontally when moving left / right. This is generally always true UNLESS within a column. bool NavHideHighlightOneFrame; - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) + bool NavWindowHasScrollY; // Set per window when scrolling can be used (== ScrollMax.y > 0.0f) // Miscellaneous bool MenuBarAppending; // FIXME: Remove this @@ -2227,6 +2481,7 @@ struct IMGUI_API ImGuiWindowTempData int CurrentTableIdx; // Current table index (into g.Tables) ImGuiLayoutType LayoutType; ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() + ImU32 ModalDimBgColor; // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. @@ -2243,6 +2498,7 @@ struct IMGUI_API ImGuiWindow char* Name; // Window name, owned by the window. ImGuiID ID; // == ImHashStr(Name) ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + ImGuiChildFlags ChildFlags; // Set when window is a child window. See enum ImGuiChildFlags_ ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded. ImVec2 Pos; // Position (always rounded-up to nearest pixel) ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) @@ -2277,6 +2533,7 @@ struct IMGUI_API ImGuiWindow bool IsFallbackWindow; // Set on the "Debug##Default" window. bool IsExplicitChild; // Set when passed _ChildWindow, left to false by BeginDocked() bool HasCloseButton; // Set when the window has a close button (p_open != NULL) + signed char ResizeBorderHovered; // Current border being hovered for resize (-1: none, otherwise 0-3) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) short BeginCountPreviousFrame; // Number of Begin() during the previous frame @@ -2285,7 +2542,6 @@ struct IMGUI_API ImGuiWindow short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImS8 AutoFitFramesX, AutoFitFramesY; - ImS8 AutoFitChildAxises; bool AutoFitOnlyGrows; ImGuiDir AutoPosLastDirection; ImS8 HiddenFramesCanSkipItems; // Hide the window for N frames @@ -2329,10 +2585,12 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. + ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document) ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space + ImVec2 NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX. ImGuiID NavRootFocusScopeId; // Focus Scope ID at the time of Begin() int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy @@ -2419,6 +2677,8 @@ struct IMGUI_API ImGuiTabBar float ScrollingSpeed; float ScrollingRectMinX; float ScrollingRectMaxX; + float SeparatorMinX; + float SeparatorMaxX; ImGuiID ReorderRequestTabId; ImS16 ReorderRequestOffset; ImS8 BeginCount; @@ -2446,7 +2706,7 @@ struct IMGUI_API ImGuiTabBar typedef ImS16 ImGuiTableColumnIdx; typedef ImU16 ImGuiTableDrawChannelIdx; -// [Internal] sizeof() ~ 104 +// [Internal] sizeof() ~ 112 // We use the terminology "Enabled" to refer to a column that is not Hidden by user/api. // We use the terminology "Clipped" to refer to a column that is out of sight because of scrolling/clipping. // This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use "Visible" to mean "not clipped". @@ -2492,7 +2752,7 @@ struct ImGuiTableColumn ImU8 SortDirection : 2; // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending ImU8 SortDirectionsAvailCount : 2; // Number of available sort directions (0 to 3) ImU8 SortDirectionsAvailMask : 4; // Mask of available sort directions (1-bit each) - ImU8 SortDirectionsAvailList; // Ordered of available sort directions (2-bits each) + ImU8 SortDirectionsAvailList; // Ordered list of available sort directions (2-bits each, total 8-bits) ImGuiTableColumn() { @@ -2516,17 +2776,21 @@ struct ImGuiTableCellData }; // Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?) +// sizeof() ~ 24 bytes struct ImGuiTableInstanceData { ImGuiID TableInstanceID; float LastOuterHeight; // Outer height from last frame - float LastFirstRowHeight; // Height of first row from last frame (FIXME: this is used as "header height" and may be reworked) + float LastTopHeadersRowHeight; // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set) float LastFrozenHeight; // Height of frozen section from last frame + int HoveredRowLast; // Index of row which was hovered last frame. + int HoveredRowNext; // Index of row hovered this frame, set after encountering it. - ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastFirstRowHeight = LastFrozenHeight = 0.0f; } + ImGuiTableInstanceData() { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; } }; // FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs, incoming RowData +// sizeof() ~ 580 bytes + heap allocs described in TableBeginInitMemory() struct IMGUI_API ImGuiTable { ImGuiID ID; @@ -2550,6 +2814,7 @@ struct IMGUI_API ImGuiTable float RowPosY1; float RowPosY2; float RowMinHeight; // Height submitted to TableNextRow() + float RowCellPaddingY; // Top and bottom padding. Reloaded during row change. float RowTextBaseline; float RowIndentOffsetX; ImGuiTableRowFlags RowFlags : 16; // Current row flags, see ImGuiTableRowFlags_ @@ -2563,9 +2828,8 @@ struct IMGUI_API ImGuiTable float HostIndentX; float MinColumnWidth; float OuterPaddingX; - float CellPaddingX; // Padding from each borders - float CellPaddingY; - float CellSpacingX1; // Spacing between non-bordered cells + float CellPaddingX; // Padding from each borders. Locked in BeginTable()/Layout. + float CellSpacingX1; // Spacing between non-bordered cells. Locked in BeginTable()/Layout. float CellSpacingX2; float InnerWidth; // User value passed to BeginTable(), see comments at the top of BeginTable() for details. float ColumnsGivenWidth; // Sum of current column width @@ -2574,6 +2838,8 @@ struct IMGUI_API ImGuiTable float ResizedColumnNextWidth; float ResizeLockMinContentsX2; // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table. float RefScale; // Reference scale to be able to rescale columns on font/dpi changes. + float AngledHeadersHeight; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() + float AngledHeadersSlope; // Set by TableAngledHeadersRow(), used in TableUpdateLayout() ImRect OuterRect; // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable(). ImRect InnerRect; // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is ImRect WorkRect; @@ -2596,8 +2862,10 @@ struct IMGUI_API ImGuiTable ImGuiTableColumnIdx ColumnsEnabledCount; // Number of enabled columns (<= ColumnsCount) ImGuiTableColumnIdx ColumnsEnabledFixedCount; // Number of enabled columns (<= ColumnsCount) ImGuiTableColumnIdx DeclColumnsCount; // Count calls to TableSetupColumn() + ImGuiTableColumnIdx AngledHeadersCount; // Count columns with angled headers ImGuiTableColumnIdx HoveredColumnBody; // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column! ImGuiTableColumnIdx HoveredColumnBorder; // Index of column whose right-border is being hovered (for resizing). + ImGuiTableColumnIdx HighlightColumnHeader; // Index of column which should be highlighted. ImGuiTableColumnIdx AutoFitSingleColumn; // Index of single column requesting auto-fit. ImGuiTableColumnIdx ResizedColumn; // Index of column being resized. Reset when InstanceCurrent==0. ImGuiTableColumnIdx LastResizedColumn; // Index of column being resized from previous frame. @@ -2623,6 +2891,7 @@ struct IMGUI_API ImGuiTable bool IsSortSpecsDirty; bool IsUsingHeaders; // Set when the first row had the ImGuiTableRowFlags_Headers flag. bool IsContextPopupOpen; // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted). + bool DisableDefaultContextMenu; // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup() bool IsSettingsRequestLoad; bool IsSettingsDirty; // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data. bool IsDefaultDisplayOrder; // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1) @@ -2630,6 +2899,8 @@ struct IMGUI_API ImGuiTable bool IsResetDisplayOrderRequest; bool IsUnfrozenRows; // Set when we got past the frozen row. bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() + bool IsActiveIdAliveBeforeTable; + bool IsActiveIdInTable; bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame. bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous. bool MemoryCompacted; @@ -2642,10 +2913,12 @@ struct IMGUI_API ImGuiTable // Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table). // - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure. // - We also leave out of this structure data that tend to be particularly useful for debugging/metrics. +// sizeof() ~ 120 bytes. struct IMGUI_API ImGuiTableTempData { int TableIndex; // Index in g.Tables.Buf[] pool float LastTimeActive; // Last timestamp this structure was used + float AngledHeadersExtraWidth; // Used in EndTable() ImVec2 UserOuterSize; // outer_size.x passed to BeginTable() ImDrawListSplitter DrawSplitter; @@ -2726,13 +2999,15 @@ namespace ImGui IMGUI_API void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0); IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); - IMGUI_API void SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window); + IMGUI_API void SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window); + inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } + inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } // Windows: Display Order and Focus Order - IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window); + IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); + IMGUI_API void FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags); IMGUI_API void BringWindowToFocusFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayFront(ImGuiWindow* window); IMGUI_API void BringWindowToDisplayBack(ImGuiWindow* window); @@ -2746,6 +3021,7 @@ namespace ImGui inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list); // Init IMGUI_API void Initialize(); @@ -2818,7 +3094,8 @@ namespace ImGui IMGUI_API void ItemSize(const ImVec2& size, float text_baseline_y = -1.0f); inline void ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min. IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0); - IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); + IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags); + IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); @@ -2840,7 +3117,7 @@ namespace ImGui IMGUI_API void LogSetNextTextDecoration(const char* prefix, const char* suffix); // Popups, Modals, Tooltips - IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags); + IMGUI_API bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags); IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); @@ -2848,9 +3125,11 @@ namespace ImGui IMGUI_API bool IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags); IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); IMGUI_API bool BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags); + IMGUI_API bool BeginTooltipHidden(); IMGUI_API ImRect GetPopupAllowedExtentRect(ImGuiWindow* window); IMGUI_API ImGuiWindow* GetTopMostPopupModal(); IMGUI_API ImGuiWindow* GetTopMostAndVisiblePopupModal(); + IMGUI_API ImGuiWindow* FindBlockingModal(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window); IMGUI_API ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy); @@ -2871,12 +3150,23 @@ namespace ImGui IMGUI_API void NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags); IMGUI_API void NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result); + IMGUI_API void NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data); IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); - IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. + IMGUI_API void NavHighlightActivated(ImGuiID id); + IMGUI_API void NavClearPreferredPosForAxis(ImGuiAxis axis); + IMGUI_API void NavRestoreHighlightAfterMove(); + IMGUI_API void NavUpdateCurrentWindowIsScrollPushableX(); IMGUI_API void SetNavWindow(ImGuiWindow* window); IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); + IMGUI_API void SetNavFocusScope(ImGuiID focus_scope_id); + + // Focus/Activation + // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are + // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones. + IMGUI_API void FocusItem(); // Focus last item (no selection/activation). + IMGUI_API void ActivateItemByID(ImGuiID id); // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again. // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. @@ -2887,10 +3177,11 @@ namespace ImGui inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } - inline ImGuiKeyChord ConvertShortcutMod(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; IM_ASSERT_PARANOID(key_chord & ImGuiMod_Shortcut); return (key_chord & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); } - inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) + inline bool IsModKey(ImGuiKey key) { return key >= ImGuiKey_LeftCtrl && key <= ImGuiKey_RightSuper; } + ImGuiKeyChord FixupKeyChord(ImGuiContext* ctx, ImGuiKeyChord key_chord); + inline ImGuiKey ConvertSingleModFlagToKey(ImGuiContext* ctx, ImGuiKey key) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl; if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift; if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt; @@ -2899,14 +3190,16 @@ namespace ImGui return key; } - IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key); - IMGUI_API void GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size); + IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key); + inline ImGuiKeyData* GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); } + IMGUI_API const char* GetKeyChordName(ImGuiKeyChord key_chord); inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); IMGUI_API ImVec2 GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down); IMGUI_API float GetNavTweakPressedAmount(ImGuiAxis axis); IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate); + IMGUI_API void TeleportMousePos(const ImVec2& pos); IMGUI_API void SetActiveIdUsingAllKeyboardKeys(); inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } @@ -2923,9 +3216,10 @@ namespace ImGui // Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved. IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' - inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &GImGui->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } + inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(ctx, key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag. @@ -2939,6 +3233,7 @@ namespace ImGui IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id); IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0); IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id); + IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id); // [EXPERIMENTAL] Shortcut Routing // - ImGuiKeyChord = a ImGuiKey optionally OR-red with ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. @@ -2950,8 +3245,13 @@ namespace ImGui // - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route. // - Multiple read sites may use the same owner id and will all get the granted route. // - For routing: when owner_id is 0 we use the current Focus Scope ID as a default owner in order to identify our location. + // - TL;DR; + // - IsKeyChordPressed() compares mods + call IsKeyPressed() -> function has no side-effect. + // - Shortcut() submits a route then if currently can be routed calls IsKeyChordPressed() -> function has (desirable) side-effects. + IMGUI_API bool IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetNextItemShortcut(ImGuiKeyChord key_chord); IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0); - IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0); + IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags = 0); // owner_id needs to be explicit and cannot be 0 IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id); IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord); @@ -2972,7 +3272,13 @@ namespace ImGui IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); - IMGUI_API void RenderDragDropTargetRect(const ImRect& bb); + IMGUI_API void RenderDragDropTargetRect(const ImRect& bb, const ImRect& item_clip_rect); + + // Typing-Select API + IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None); + IMGUI_API int TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); + IMGUI_API int TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx); + IMGUI_API int TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data); // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); @@ -2990,10 +3296,13 @@ namespace ImGui IMGUI_API void TableOpenContextMenu(int column_n = -1); IMGUI_API void TableSetColumnWidth(int column_n, float width); IMGUI_API void TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs); - IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. + IMGUI_API int TableGetHoveredColumn(); // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. + IMGUI_API int TableGetHoveredRow(); // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet. IMGUI_API float TableGetHeaderRowHeight(); + IMGUI_API float TableGetHeaderAngledMaxLabelWidth(); IMGUI_API void TablePushBackgroundChannel(); IMGUI_API void TablePopBackgroundChannel(); + IMGUI_API void TableAngledHeadersRowEx(float angle, float max_label_width = 0.0f); // Tables: Internals inline ImGuiTable* GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; } @@ -3006,7 +3315,7 @@ namespace ImGui IMGUI_API void TableUpdateBorders(ImGuiTable* table); IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); IMGUI_API void TableDrawBorders(ImGuiTable* table); - IMGUI_API void TableDrawContextMenu(ImGuiTable* table); + IMGUI_API void TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display); IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table); IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } @@ -3071,7 +3380,7 @@ namespace ImGui IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); - IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight + IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_None); // Navigation highlight IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. IMGUI_API void RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); @@ -3087,8 +3396,8 @@ namespace ImGui IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); - IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); - IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); + IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0); + IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f); IMGUI_API void SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width); IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); @@ -3112,6 +3421,7 @@ namespace ImGui IMGUI_API void TreePushOverrideID(ImGuiID id); IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open); IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. + IMGUI_API void SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data); // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). @@ -3133,6 +3443,7 @@ namespace ImGui // InputText IMGUI_API bool InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API void InputTextDeactivateHook(ImGuiID id); IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } @@ -3149,6 +3460,7 @@ namespace ImGui // Shade functions (write over already created vertices) IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); IMGUI_API void ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); + IMGUI_API void ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out); // Garbage collection IMGUI_API void GcCompactTransientMiscBuffers(); @@ -3158,20 +3470,25 @@ namespace ImGui // Debug Log IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1); IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1); + IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255)); + IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255)); + IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time! IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time! IMGUI_API void DebugLocateItemResolveWithLastItem(); - inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } - inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } + IMGUI_API void DebugBreakClearData(); + IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location); + IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location); IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); - IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, const ImDrawList* draw_list, const char* label); + IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label); IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); IMGUI_API void DebugNodeFont(ImFont* font); IMGUI_API void DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph); @@ -3180,6 +3497,7 @@ namespace ImGui IMGUI_API void DebugNodeTable(ImGuiTable* table); IMGUI_API void DebugNodeTableSettings(ImGuiTableSettings* settings); IMGUI_API void DebugNodeInputTextState(ImGuiInputTextState* state); + IMGUI_API void DebugNodeTypingSelectState(ImGuiTypingSelectState* state); IMGUI_API void DebugNodeWindow(ImGuiWindow* window, const char* label); IMGUI_API void DebugNodeWindowSettings(ImGuiWindowSettings* settings); IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); @@ -3193,13 +3511,12 @@ namespace ImGui inline void SetItemUsingMouseWheel() { SetItemKeyOwner(ImGuiKey_MouseWheelY); } // Changed in 1.89 inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89 - // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): + // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets which used FocusableItemRegister(): // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' - // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' - // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))' (WIP) - // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() - inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() - inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))' + //inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() + //inline void FocusableItemUnregister(ImGuiWindow* window) // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem #endif #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity! @@ -3222,6 +3539,7 @@ struct ImFontBuilderIO #ifdef IMGUI_ENABLE_STB_TRUETYPE IMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype(); #endif +IMGUI_API void ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildInit(ImFontAtlas* atlas); IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque); diff --git a/r5dev/thirdparty/imgui/imgui_tables.cpp b/r5dev/thirdparty/imgui/imgui_tables.cpp index 3bacc34a..260df1a9 100644 --- a/r5dev/thirdparty/imgui/imgui_tables.cpp +++ b/r5dev/thirdparty/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.4 +// dear imgui, v1.90.4 // (tables and columns code) /* @@ -48,7 +48,8 @@ Index of this file: // - TableUpdateLayout() [Internal] followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow(). // | TableSetupDrawChannels() - setup ImDrawList channels // | TableUpdateBorders() - detect hovering columns for resize, ahead of contents submission -// | TableDrawContextMenu() - draw right-click context menu +// | TableBeginContextMenuPopup() +// | - TableDrawDefaultContextMenu() - draw right-click context menu contents //----------------------------------------------------------------------------- // - TableHeadersRow() or TableHeader() user submit a headers row (optional) // | TableSortSpecsClickColumn() - when left-clicked: alter sort order and sort direction @@ -80,20 +81,20 @@ Index of this file: // - outer_size.x <= 0.0f -> Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge. // - outer_size.x > 0.0f -> Set Fixed width. // Y with ScrollX/ScrollY disabled: we output table directly in current window -// - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful is parent window can vertically scroll. +// - outer_size.y < 0.0f -> Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll. // - outer_size.y = 0.0f -> No minimum height (but will auto extend, unless _NoHostExtendY is set) -// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtenY is set) +// - outer_size.y > 0.0f -> Set Minimum height (but will auto extend, unless _NoHostExtendY is set) // Y with ScrollX/ScrollY enabled: using a child window for scrolling -// - outer_size.y < 0.0f -> Bottom-align. Not meaningful is parent window can vertically scroll. +// - outer_size.y < 0.0f -> Bottom-align. Not meaningful if parent window can vertically scroll. // - outer_size.y = 0.0f -> Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window. // - outer_size.y > 0.0f -> Set Exact height. Recommended when using Scrolling on any axis. //----------------------------------------------------------------------------- // Outer size is also affected by the NoHostExtendX/NoHostExtendY flags. -// Important to that note how the two flags have slightly different behaviors! +// Important to note how the two flags have slightly different behaviors! // - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used. // - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible. // In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height. -// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not easily noticeable) +// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not useful and not easily noticeable). //----------------------------------------------------------------------------- // About 'inner_width': // With ScrollX disabled: @@ -112,15 +113,16 @@ Index of this file: //----------------------------------------------------------------------------- // COLUMNS SIZING POLICIES +// (Reference: ImGuiTableFlags_SizingXXX flags and ImGuiTableColumnFlags_WidthXXX flags) //----------------------------------------------------------------------------- // About overriding column sizing policy and width/weight with TableSetupColumn(): -// We use a default parameter of 'init_width_or_weight == -1'. +// We use a default parameter of -1 for 'init_width'/'init_weight'. // - with ImGuiTableColumnFlags_WidthFixed, init_width <= 0 (default) --> width is automatic // - with ImGuiTableColumnFlags_WidthFixed, init_width > 0 (explicit) --> width is custom // - with ImGuiTableColumnFlags_WidthStretch, init_weight <= 0 (default) --> weight is 1.0f // - with ImGuiTableColumnFlags_WidthStretch, init_weight > 0 (explicit) --> weight is custom // Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f) -// and you can fit a 100.0f wide item in it without clipping and with full padding. +// and you can fit a 100.0f wide item in it without clipping and with padding honored. //----------------------------------------------------------------------------- // About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag) // - with Table policy ImGuiTableFlags_SizingFixedFit --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width @@ -134,10 +136,10 @@ Index of this file: // - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place! // that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in. // - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents. -// - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weight/widths. +// - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weights/widths. //----------------------------------------------------------------------------- // About using column width: -// If a column is manual resizable or has a width specified with TableSetupColumn(): +// If a column is manually resizable or has a width specified with TableSetupColumn(): // - you may use GetContentRegionAvail().x to query the width available in a given column. // - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width. // If the column is not resizable and has no width specified with TableSetupColumn(): @@ -151,7 +153,7 @@ Index of this file: // TABLES CLIPPING/CULLING //----------------------------------------------------------------------------- // About clipping/culling of Rows in Tables: -// - For large numbers of rows, it is recommended you use ImGuiListClipper to only submit visible rows. +// - For large numbers of rows, it is recommended you use ImGuiListClipper to submit only visible rows. // ImGuiListClipper is reliant on the fact that rows are of equal height. // See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper. // - Note that auto-resizing columns don't play well with using the clipper. @@ -168,7 +170,7 @@ Index of this file: // - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.). // // [A] [B] [C] -// TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() return false, user can skip submitting items but only if the column doesn't contribute to row height. +// TableNextColumn(): true false false -> [userland] when TableNextColumn() / TableSetColumnIndex() returns false, user can skip submitting items but only if the column doesn't contribute to row height. // SkipItems: false false true -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output. // ClipRect: normal zero-width zero-width -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way. // ImDrawList output: normal dummy dummy -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway). @@ -197,11 +199,7 @@ Index of this file: #include "imgui_internal.h" // System includes -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif // Visual Studio warnings #ifdef _MSC_VER @@ -319,17 +317,22 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if (flags & ImGuiTableFlags_ScrollX) IM_ASSERT(inner_width >= 0.0f); - // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping rules may evolve. + // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve. const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0; const ImVec2 avail_size = GetContentRegionAvail(); - ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); - ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); - if (use_child_window && IsClippedEx(outer_rect, 0)) + const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f); + const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size); + const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows! + if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size) { ItemSize(outer_rect); return false; } + // [DEBUG] Debug break requested by user + if (g.DebugBreakInTable == id) + IM_DEBUG_BREAK(); + // Acquire storage for the table ImGuiTable* table = g.Tables.GetOrAddByKey(id); const ImGuiTableFlags table_last_flags = table->Flags; @@ -348,7 +351,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG flags = TableFixFlags(flags, outer_window); // Initialize - const int instance_no = (table->LastFrameActive != g.FrameCount) ? 0 : table->InstanceCurrent + 1; + const int previous_frame_active = table->LastFrameActive; + const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1; table->ID = id; table->Flags = flags; table->LastFrameActive = g.FrameCount; @@ -366,7 +370,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG IM_ASSERT(table->ColumnsCount == columns_count && "BeginTable(): Cannot change columns count mid-frame while preserving same ID"); if (table->InstanceDataExtra.Size < instance_no) table->InstanceDataExtra.push_back(ImGuiTableInstanceData()); - instance_id = GetIDWithSeed(instance_no, GetIDWithSeed("##Instances", NULL, id)); // Push "##Instance" followed by (int)instance_no in ID stack. + instance_id = GetIDWithSeed(instance_no, GetIDWithSeed("##Instances", NULL, id)); // Push "##Instances" followed by (int)instance_no in ID stack. } else { @@ -407,13 +411,17 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->InnerRect = table->InnerWindow->InnerRect; IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f); + // Allow submitting when host is measuring + if (table->InnerWindow->SkipItems && outer_window_is_measuring_size) + table->InnerWindow->SkipItems = false; + // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned) if (instance_no == 0) { table->HasScrollbarYPrev = table->HasScrollbarYCurr; table->HasScrollbarYCurr = false; } - table->HasScrollbarYCurr |= (table->InnerWindow->ScrollMax.y > 0.0f); + table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY; } else { @@ -442,6 +450,18 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size; inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + // Make left and top borders not overlap our contents by offsetting HostClipRect (#6765) + // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the + // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap + // problem only affect scrolling tables in this case we can get away with doing it without extra cost). + if (inner_window != outer_window) + { + if (flags & ImGuiTableFlags_BordersOuterV) + table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); + if (flags & ImGuiTableFlags_BordersOuterH) + table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); + } + // Padding and Spacing // - None ........Content..... Pad .....Content........ // - PadOuter | Pad ..Content..... Pad .....Content.. Pad | @@ -455,7 +475,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border; table->CellSpacingX2 = inner_spacing_explicit; table->CellPaddingX = inner_padding_explicit; - table->CellPaddingY = g.Style.CellPadding.y; const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f; @@ -472,17 +491,22 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow() + table->RowCellPaddingY = 0.0f; table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any table->FreezeColumnsRequest = table->FreezeColumnsCount = 0; table->IsUnfrozenRows = true; - table->DeclColumnsCount = 0; + table->DeclColumnsCount = table->AngledHeadersCount = 0; + if (previous_frame_active + 1 < g.FrameCount) + table->IsActiveIdInTable = false; + temp_data->AngledHeadersExtraWidth = 0.0f; - // Using opaque colors facilitate overlapping elements of the grid + // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders() table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong); table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight); // Make table current g.CurrentTable = table; + outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); outer_window->DC.CurrentTableIdx = table_idx; if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly. inner_window->DC.CurrentTableIdx = table_idx; @@ -490,7 +514,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0) table->IsResetDisplayOrderRequest = true; - // Mark as used + // Mark as used to avoid GC if (table_idx >= g.TablesLastTimeActive.Size) g.TablesLastTimeActive.resize(table_idx + 1, -1.0f); g.TablesLastTimeActive[table_idx] = (float)g.Time; @@ -583,13 +607,13 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG } // For reference, the average total _allocation count_ for a table is: -// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables) +// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables[]) // + 1 (for table->RawData allocated below) // + 1 (for table->ColumnsNames, if names are used) -// Shared allocations per number of nested tables +// Shared allocations for the maximum number of simultaneously nested tables (generally a very small number) // + 1 (for table->Splitter._Channels) // + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels) -// Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details. +// Where active_channels_count is variable but often == columns_count or == columns_count + 1, see TableSetupDrawChannels() for details. // Unused channels don't perform their +2 allocations. void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) { @@ -616,7 +640,7 @@ void ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count) void ImGui::TableBeginApplyRequests(ImGuiTable* table) { // Handle resizing request - // (We process this at the first TableBegin of the frame) + // (We process this in the TableBegin() of the first instance of each table) // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling? if (table->InstanceCurrent == 0) { @@ -661,8 +685,7 @@ void ImGui::TableBeginApplyRequests(ImGuiTable* table) table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir; IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir); - // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[], - // rebuild the later from the former. + // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former. for (int column_n = 0; column_n < table->ColumnsCount; column_n++) table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n; table->ReorderColumnDir = 0; @@ -736,8 +759,8 @@ static void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, I } } -// Layout columns for the frame. This is in essence the followup to BeginTable(). -// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() to be called first. +// Layout columns for the frame. This is in essence the followup to BeginTable() and this is our largest function. +// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions to be called first. // FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns. // Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns? void ImGui::TableUpdateLayout(ImGuiTable* table) @@ -849,8 +872,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx; IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0); - // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible - // to avoid the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). + // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid + // the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510. // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time. if (has_auto_fit_request && table->OuterWindow != table->InnerWindow) table->InnerWindow->SkipItems = false; @@ -858,8 +881,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->IsSettingsDirty = true; // [Part 3] Fix column flags and record a few extra information. - float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding. - float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns. + float sum_width_requests = 0.0f; // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding. + float stretch_sum_weights = 0.0f; // Sum of all weights for stretch columns. table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { @@ -935,7 +958,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (column->Flags & ImGuiTableColumnFlags_WidthStretch) { float weight_ratio = column->StretchWeight / stretch_sum_weights; - column->WidthRequest = IM_FLOOR(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f); + column->WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f); width_remaining_for_stretched_columns -= column->WidthRequest; } @@ -945,7 +968,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->Flags |= ImGuiTableColumnFlags_NoDirectResize_; // Assign final width, record width in case we will need to shrink - column->WidthGiven = ImFloor(ImMax(column->WidthRequest, table->MinColumnWidth)); + column->WidthGiven = ImTrunc(ImMax(column->WidthRequest, table->MinColumnWidth)); table->ColumnsGivenWidth += column->WidthGiven; } @@ -970,17 +993,25 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem). // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop. ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); - table->HoveredColumnBody = -1; - table->HoveredColumnBorder = -1; + table_instance->HoveredRowLast = table_instance->HoveredRowNext; + table_instance->HoveredRowNext = -1; + table->HoveredColumnBody = table->HoveredColumnBorder = -1; const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); const ImGuiID backup_active_id = g.ActiveId; g.ActiveId = 0; - const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0); + const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None); g.ActiveId = backup_active_id; + // Determine skewed MousePos.x to support angled headers. + float mouse_skewed_x = g.IO.MousePos.x; + if (table->AngledHeadersHeight > 0.0f) + if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight) + mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope); + // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping. int visible_n = 0; + bool has_at_least_one_column_requesting_output = false; bool offset_x_frozen = (table->FreezeColumnsCount > 0); float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1; ImRect host_clip_rect = table->InnerClipRect; @@ -1018,7 +1049,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } // Detect hovered column - if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x) + if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x) table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n; // Lock start position @@ -1037,7 +1068,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter. column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1; column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max - column->ItemWidth = ImFloor(column->WidthGiven * 0.65f); + column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f); column->ClipRect.Min.x = column->MinX; column->ClipRect.Min.y = work_rect.Min.y; column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX; @@ -1061,9 +1092,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0; // Mark column as SkipItems (ignoring all items/layout) + // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2) column->IsSkipItems = !column->IsEnabled || table->HostSkipItems; if (column->IsSkipItems) IM_ASSERT(!is_visible); + if (column->IsRequestOutput && !column->IsSkipItems) + has_at_least_one_column_requesting_output = true; // Update status flags column->Flags |= ImGuiTableColumnFlags_IsEnabled; @@ -1101,18 +1135,26 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) visible_n++; } + // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible. + // Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar. + if (has_at_least_one_column_requesting_output == false) + { + table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true; + table->Columns[table->LeftMostEnabledColumn].IsSkipItems = false; + } + // [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it) // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu. const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x); if (is_hovering_table && table->HoveredColumnBody == -1) - { - if (g.IO.MousePos.x >= unused_x1) + if (mouse_skewed_x >= unused_x1) table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount; - } if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable)) table->Flags &= ~ImGuiTableFlags_Resizable; + table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0); + // [Part 8] Lock actual OuterRect/WorkRect right-most position. // This is done late to handle the case of fixed-columns tables not claiming more widths that they need. // Because of this we are careful with uses of WorkRect and InnerClipRect before this point. @@ -1124,8 +1166,16 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1); } table->InnerWindow->ParentWorkRect = table->WorkRect; - table->BorderX1 = table->InnerClipRect.Min.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : -1.0f); - table->BorderX2 = table->InnerClipRect.Max.x;// +((table->Flags & ImGuiTableFlags_BordersOuter) ? 0.0f : +1.0f); + table->BorderX1 = table->InnerClipRect.Min.x; + table->BorderX2 = table->InnerClipRect.Max.x; + + // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call. + float window_content_max_y; + if (table->Flags & ImGuiTableFlags_NoHostExtendY) + window_content_max_y = table->OuterRect.Max.y; + else + window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y); + table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y); // [Part 9] Allocate draw channels and setup background cliprect TableSetupDrawChannels(table); @@ -1133,18 +1183,30 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 10] Hit testing on borders if (table->Flags & ImGuiTableFlags_Resizable) TableUpdateBorders(table); - table_instance->LastFirstRowHeight = 0.0f; + table_instance->LastTopHeadersRowHeight = 0.0f; table->IsLayoutLocked = true; table->IsUsingHeaders = false; - // [Part 11] Context menu - if (TableBeginContextMenuPopup(table)) + // Highlight header + table->HighlightColumnHeader = -1; + if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent) + table->HighlightColumnHeader = table->ContextPopupColumn; + else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1) + if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive)) + table->HighlightColumnHeader = table->HoveredColumnBody; + + // [Part 11] Default context menu + // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup(). + // - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup(). + // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu, + // e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options. + if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table)) { - TableDrawContextMenu(table); + TableDrawDefaultContextMenu(table, table->Flags); EndPopup(); } - // [Part 12] Sanitize and build sort specs before we have a change to use them for display. + // [Part 12] Sanitize and build sort specs before we have a chance to use them for display. // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change) if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable)) TableSortSpecsBuild(table); @@ -1165,9 +1227,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) } // Process hit-testing on resizing borders. Actual size change will be applied in EndTable() -// - Set table->HoveredColumnBorder with a short delay/timer to reduce feedback noise -// - Submit ahead of table contents and header, use ImGuiButtonFlags_AllowItemOverlap to prioritize widgets -// overlapping the same area. +// - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise. void ImGui::TableUpdateBorders(ImGuiTable* table) { ImGuiContext& g = *GImGui; @@ -1179,9 +1239,9 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) // Actual columns highlight/render will be performed in EndTable() and not be affected. ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS; - const float hit_y1 = table->OuterRect.Min.y; + const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight; const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight); - const float hit_y2_head = hit_y1 + table_instance->LastFirstRowHeight; + const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight; for (int order_n = 0; order_n < table->ColumnsCount; order_n++) { @@ -1207,12 +1267,12 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100)); bool hovered = false, held = false; - bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); + bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); if (pressed && IsMouseDoubleClicked(0)) { TableSetColumnWidthAutoSingle(table, column_n); ClearActiveID(); - held = hovered = false; + held = false; } if (held) { @@ -1284,7 +1344,7 @@ void ImGui::EndTable() max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); if (table->ResizedColumn != -1) max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); - table->InnerWindow->DC.CursorMaxPos.x = max_pos_x; + table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth; } // Pop clipping rect @@ -1356,10 +1416,12 @@ void ImGui::EndTable() { ImGuiTableColumn* column = &table->Columns[table->ResizedColumn]; const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS); - const float new_width = ImFloor(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f); + const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f); table->ResizedColumnNextWidth = new_width; } + table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false); + // Pop from id stack IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, "Mismatching PushID/PopID!"); IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, "Too many PopItemWidth!"); @@ -1400,7 +1462,7 @@ void ImGui::EndTable() } else if (temp_data->UserOuterSize.x <= 0.0f) { - const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f; + const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f); outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth)); } @@ -1436,9 +1498,10 @@ void ImGui::EndTable() g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter; } outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1; + NavUpdateCurrentWindowIsScrollPushableX(); } -// See "COLUMN SIZING POLICIES" comments at the top of this file +// See "COLUMNS SIZING POLICIES" comments at the top of this file // If (init_width_or_weight <= 0.0f) it is ignored void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id) { @@ -1466,6 +1529,11 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f) if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame) flags |= ImGuiTableColumnFlags_WidthFixed; + if (flags & ImGuiTableColumnFlags_AngledHeader) + { + flags |= ImGuiTableColumnFlags_NoHeaderLabel; + table->AngledHeadersCount++; + } TableSetupColumnFlags(table, column, flags); column->UserID = user_id; @@ -1499,6 +1567,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo } // Store name (append with zero-terminator in contiguous buffer) + // FIXME: If we recorded the number of \n in names we could compute header row height column->NameOffset = -1; if (label != NULL && label[0] != 0) { @@ -1547,6 +1616,7 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows) // - TableGetCellBgRect() [Internal] // - TableGetColumnResizeID() [Internal] // - TableGetHoveredColumn() [Internal] +// - TableGetHoveredRow() [Internal] // - TableSetBgColor() //----------------------------------------------------------------------------- @@ -1641,7 +1711,7 @@ ImGuiID ImGui::TableGetColumnResizeID(ImGuiTable* table, int column_n, int insta return instance_id + 1 + column_n; // FIXME: #6140: still not ideal } -// Return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered. +// Return -1 when table is not hovered. return columns_count if hovering the unused space at the right of the right-most visible column. int ImGui::TableGetHoveredColumn() { ImGuiContext& g = *GImGui; @@ -1651,6 +1721,19 @@ int ImGui::TableGetHoveredColumn() return (int)table->HoveredColumnBody; } +// Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row. +// *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value. +// This difference with is the reason why this is not public yet. +int ImGui::TableGetHoveredRow() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + if (!table) + return -1; + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + return (int)table_instance->HoveredRowLast; +} + void ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n) { ImGuiContext& g = *GImGui; @@ -1725,19 +1808,20 @@ void ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height) table->LastRowFlags = table->RowFlags; table->RowFlags = row_flags; + table->RowCellPaddingY = g.Style.CellPadding.y; table->RowMinHeight = row_min_height; TableBeginRow(table); // We honor min_row_height requested by user, but cannot guarantee per-row maximum height, // because that would essentially require a unique clipping rectangle per-cell. - table->RowPosY2 += table->CellPaddingY * 2.0f; + table->RowPosY2 += table->RowCellPaddingY * 2.0f; table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height); // Disable output until user calls TableNextColumn() table->InnerWindow->SkipItems = true; } -// [Internal] Called by TableNextRow() +// [Internal] Only called by TableNextRow() void ImGui::TableBeginRow(ImGuiTable* table) { ImGuiWindow* window = table->InnerWindow; @@ -1758,8 +1842,10 @@ void ImGui::TableBeginRow(ImGuiTable* table) table->RowPosY1 = table->RowPosY2 = next_y1; table->RowTextBaseline = 0.0f; table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent + window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + table->RowCellPaddingY); // This allows users to call SameLine() to share LineSize between columns. + window->DC.PrevLineSize = window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // This allows users to call SameLine() to share LineSize between columns, and to call it from first column too. window->DC.IsSameLine = window->DC.IsSetPos = false; window->DC.CursorMaxPos.y = next_y1; @@ -1796,12 +1882,17 @@ void ImGui::TableEndRow(ImGuiTable* table) const float bg_y2 = table->RowPosY2; const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount); const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest); - if (table->CurrentRow == 0) - TableGetInstanceData(table, table->InstanceCurrent)->LastFirstRowHeight = bg_y2 - bg_y1; + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers))) + table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1; const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y); if (is_visible) { + // Update data for TableGetHoveredRow() + if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2) + table_instance->HoveredRowNext = table->CurrentRow; + // Decide of background color for the row ImU32 bg_col0 = 0; ImU32 bg_col1 = 0; @@ -1813,15 +1904,14 @@ void ImGui::TableEndRow(ImGuiTable* table) bg_col1 = table->RowBgColor[1]; // Decide of top border color - ImU32 border_col = 0; + ImU32 top_border_col = 0; const float border_size = TABLE_BORDER_SIZE; - if (table->CurrentRow > 0 || table->InnerWindow == table->OuterWindow) - if (table->Flags & ImGuiTableFlags_BordersInnerH) - border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; + if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH)) + top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight; const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0; const bool draw_strong_bottom_border = unfreeze_rows_actual; - if ((bg_col0 | bg_col1 | border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color) + if ((bg_col0 | bg_col1 | top_border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color) { // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is // always followed by a change of clipping rectangle we perform the smallest overwrite possible here. @@ -1860,8 +1950,8 @@ void ImGui::TableEndRow(ImGuiTable* table) } // Draw top border - if (border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) - window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), border_col, border_size); + if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y) + window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size); // Draw bottom border at the row unfreezing mark (always strong) if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y) @@ -1879,7 +1969,7 @@ void ImGui::TableEndRow(ImGuiTable* table) IM_ASSERT(table->IsUnfrozenRows == false); const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); table->IsUnfrozenRows = true; - TableGetInstanceData(table, table->InstanceCurrent)->LastFrozenHeight = y0 - table->OuterRect.Min.y; + table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); @@ -1978,6 +2068,7 @@ bool ImGui::TableNextColumn() // FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns. void ImGui::TableBeginCell(ImGuiTable* table, int column_n) { + ImGuiContext& g = *GImGui; ImGuiTableColumn* column = &table->Columns[column_n]; ImGuiWindow* window = table->InnerWindow; table->CurrentColumn = column_n; @@ -1988,12 +2079,14 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row. window->DC.CursorPos.x = start_x; - window->DC.CursorPos.y = table->RowPosY1 + table->CellPaddingY; + window->DC.CursorPos.y = table->RowPosY1 + table->RowCellPaddingY; window->DC.CursorMaxPos.x = window->DC.CursorPos.x; window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT + window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x; // PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns. window->DC.CurrLineTextBaseOffset = table->RowTextBaseline; window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent; + // Note how WorkRect.Max.y is only set once during layout window->WorkRect.Min.y = window->DC.CursorPos.y; window->WorkRect.Min.x = column->WorkMinX; window->WorkRect.Max.x = column->WorkMaxX; @@ -2002,7 +2095,6 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) window->SkipItems = column->IsSkipItems; if (column->IsSkipItems) { - ImGuiContext& g = *GImGui; g.LastItemData.ID = 0; g.LastItemData.StatusFlags = 0; } @@ -2021,7 +2113,6 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n) } // Logging - ImGuiContext& g = *GImGui; if (g.LogEnabled && !column->IsSkipItems) { LogRenderedText(&window->DC.CursorPos, "|"); @@ -2046,7 +2137,7 @@ void ImGui::TableEndCell(ImGuiTable* table) p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen; *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x); if (column->IsEnabled) - table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->CellPaddingY); + table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->RowCellPaddingY); column->ItemWidth = window->DC.ItemWidth; // Propagate text baseline for the entire row @@ -2064,6 +2155,8 @@ void ImGui::TableEndCell(ImGuiTable* table) // - TableSetColumnWidthAutoAll() [Internal] // - TableUpdateColumnsWeightFromWidth() [Internal] //------------------------------------------------------------------------- +// Note that actual columns widths are computed in TableUpdateLayout(). +//------------------------------------------------------------------------- // Maximum column content width given current layout. Use column->MinX so this value on a per-column basis. float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n) @@ -2141,7 +2234,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width) // - All stretch: easy. // - One or more fixed + one stretch: easy. // - One or more fixed + more than one stretch: tricky. - // Qt when manual resize is enabled only support a single _trailing_ stretch column. + // Qt when manual resize is enabled only supports a single _trailing_ stretch column, we support more cases here. // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1. // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user. @@ -2224,7 +2317,7 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) { IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1); - // Measure existing quantity + // Measure existing quantities float visible_weight = 0.0f; float visible_width = 0.0f; for (int column_n = 0; column_n < table->ColumnsCount; column_n++) @@ -2256,6 +2349,7 @@ void ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table) // - TablePopBackgroundChannel() [Internal] // - TableSetupDrawChannels() [Internal] // - TableMergeDrawChannels() [Internal] +// - TableGetColumnBorderCol() [Internal] // - TableDrawBorders() [Internal] //------------------------------------------------------------------------- @@ -2381,11 +2475,11 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) struct MergeGroup { ImRect ClipRect; - int ChannelsCount; - ImBitArrayPtr ChannelsMask; + int ChannelsCount = 0; + ImBitArrayPtr ChannelsMask = NULL; }; int merge_group_mask = 0x00; - MergeGroup merge_groups[4] = {}; + MergeGroup merge_groups[4]; // Use a reusable temp buffer for the merge masks as they are dynamically sized. const int max_draw_channels = (4 + table->ColumnsCount * 2); @@ -2499,11 +2593,9 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x); if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0) merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y); -#if 0 - GetOverlayDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); - GetOverlayDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200)); - GetOverlayDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200)); -#endif + //GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); // [DEBUG] + //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200)); + //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200)); remaining_count -= merge_group->ChannelsCount; for (int n = 0; n < (size_for_masks_bitarrays_one >> 2); n++) remaining_mask[n] &= ~merge_group->ChannelsMask[n]; @@ -2541,6 +2633,18 @@ void ImGui::TableMergeDrawChannels(ImGuiTable* table) } } +static ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n) +{ + const bool is_hovered = (table->HoveredColumnBorder == column_n); + const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent); + const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1); + if (is_resized || is_hovered) + return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered); + if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize))) + return table->BorderColorStrong; + return table->BorderColorLight; +} + // FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow) void ImGui::TableDrawBorders(ImGuiTable* table) { @@ -2555,9 +2659,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw inner border and resizing feedback ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); const float border_size = TABLE_BORDER_SIZE; - const float draw_y1 = table->InnerRect.Min.y; + const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f); const float draw_y2_body = table->InnerRect.Max.y; - const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastFirstRowHeight) : draw_y1; + const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1; if (table->Flags & ImGuiTableFlags_BordersInnerV) { for (int order_n = 0; order_n < table->ColumnsCount; order_n++) @@ -2583,21 +2687,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table) // Draw in outer window so right-most column won't be clipped // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling. - ImU32 col; - float draw_y2; - if (is_hovered || is_resized || is_frozen_separator) - { - draw_y2 = draw_y2_body; - col = is_resized ? GetColorU32(ImGuiCol_SeparatorActive) : is_hovered ? GetColorU32(ImGuiCol_SeparatorHovered) : table->BorderColorStrong; - } - else - { - draw_y2 = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? draw_y2_head : draw_y2_body; - col = (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) ? table->BorderColorStrong : table->BorderColorLight; - } - + float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head; if (draw_y2 > draw_y1) - inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), col, border_size); + inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size); } } @@ -2614,7 +2706,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) const ImU32 outer_col = table->BorderColorStrong; if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter) { - inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size); + inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size); } else if (table->Flags & ImGuiTableFlags_BordersOuterV) { @@ -2629,7 +2721,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) { - // Draw bottom-most row border + // Draw bottom-most row border between it is above outer border. const float border_y = table->RowPosY2; if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y) inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size); @@ -2650,8 +2742,9 @@ void ImGui::TableDrawBorders(ImGuiTable* table) //------------------------------------------------------------------------- // Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set) -// You can sort your data again when 'SpecsChanged == true'. It will be true with sorting specs have changed since -// last call, or the first time. +// When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have +// changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting, +// else you may wastefully sort your data every frame! // Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()! ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() { @@ -2667,7 +2760,6 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs() TableUpdateLayout(table); TableSortSpecsBuild(table); - return &table->SortSpecs; } @@ -2786,7 +2878,7 @@ void ImGui::TableSortSpecsSanitize(ImGuiTable* table) } } - // Fallback default sort order (if no column had the ImGuiTableColumnFlags_DefaultSort flag) + // Fallback default sort order (if no column with the ImGuiTableColumnFlags_DefaultSort flag) if (sort_order_count == 0 && !(table->Flags & ImGuiTableFlags_SortTristate)) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) { @@ -2838,8 +2930,11 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table) // [SECTION] Tables: Headers //------------------------------------------------------------------------- // - TableGetHeaderRowHeight() [Internal] +// - TableGetHeaderAngledMaxLabelWidth() [Internal] // - TableHeadersRow() // - TableHeader() +// - TableAngledHeadersRow() +// - TableAngledHeadersRowEx() [Internal] //------------------------------------------------------------------------- float ImGui::TableGetHeaderRowHeight() @@ -2848,16 +2943,26 @@ float ImGui::TableGetHeaderRowHeight() // Calculate row height, for the unlikely case that some labels may be taller than others. // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height. // In your custom header row you may omit this all together and just call TableNextRow() without a height... - float row_height = GetTextLineHeight(); - int columns_count = TableGetColumnCount(); - for (int column_n = 0; column_n < columns_count; column_n++) - { - ImGuiTableColumnFlags flags = TableGetColumnFlags(column_n); - if ((flags & ImGuiTableColumnFlags_IsEnabled) && !(flags & ImGuiTableColumnFlags_NoHeaderLabel)) - row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n)).y); - } - row_height += GetStyle().CellPadding.y * 2.0f; - return row_height; + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + float row_height = g.FontSize; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0) + row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y); + return row_height + g.Style.CellPadding.y * 2.0f; +} + +float ImGui::TableGetHeaderAngledMaxLabelWidth() +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + float width = 0.0f; + for (int column_n = 0; column_n < table->ColumnsCount; column_n++) + if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n)) + if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader) + width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x); + return width + g.Style.CellPadding.y * 2.0f; // Swap padding } // [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn(). @@ -2877,9 +2982,9 @@ void ImGui::TableHeadersRow() TableUpdateLayout(table); // Open row - const float row_y1 = GetCursorScreenPos().y; const float row_height = TableGetHeaderRowHeight(); TableNextRow(ImGuiTableRowFlags_Headers, row_height); + const float row_y1 = GetCursorScreenPos().y; if (table->HostSkipItems) // Merely an optimization, you may skip in your own code. return; @@ -2901,7 +3006,7 @@ void ImGui::TableHeadersRow() ImVec2 mouse_pos = ImGui::GetMousePos(); if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count) if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height) - TableOpenContextMenu(-1); // Will open a non-column-specific popup. + TableOpenContextMenu(columns_count); // Will open a non-column-specific popup. } // Emit a column header (text + optional sort order) @@ -2930,16 +3035,19 @@ void ImGui::TableHeader(const char* label) // If we already got a row height, there's use that. // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect? ImRect cell_r = TableGetCellBgRect(table, column_n); - float label_height = ImMax(label_size.y, table->RowMinHeight - table->CellPaddingY * 2.0f); + float label_height = ImMax(label_size.y, table->RowMinHeight - table->RowCellPaddingY * 2.0f); // Calculate ideal size for sort order arrow float w_arrow = 0.0f; float w_sort_text = 0.0f; + bool sort_arrow = false; char sort_order_suf[4] = ""; const float ARROW_SCALE = 0.65f; if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) { - w_arrow = ImFloor(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x); + w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x); + if (column->SortOrder != -1) + sort_arrow = true; if (column->SortOrder > 0) { ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), "%d", column->SortOrder + 1); @@ -2947,13 +3055,12 @@ void ImGui::TableHeader(const char* label) } } - // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considering for merging. + // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging. float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow; - column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, column->WorkMaxX); + column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, sort_arrow ? cell_r.Max.x : ImMin(max_pos_x, cell_r.Max.x)); column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x); // Keep header highlighted when context menu is open. - const bool selected = (table->IsContextPopupOpen && table->ContextPopupColumn == column_n && table->InstanceInteracted == table->InstanceCurrent); ImGuiID id = window->GetID(label); ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f)); ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal @@ -2963,12 +3070,11 @@ void ImGui::TableHeader(const char* label) //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG] - // Using AllowItemOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. + // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items. + const bool highlight = (table->HighlightColumnHeader == column_n); bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowItemOverlap); - if (g.ActiveId != id) - SetItemAllowOverlap(); - if (held || hovered || selected) + bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap); + if (held || hovered || highlight) { const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); //RenderFrame(bb.Min, bb.Max, col, false, 0.0f); @@ -2980,7 +3086,7 @@ void ImGui::TableHeader(const char* label) if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0) TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn); } - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding); if (held) table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n; window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f; @@ -3007,7 +3113,7 @@ void ImGui::TableHeader(const char* label) } // Sort order arrow - const float ellipsis_max = cell_r.Max.x - w_arrow - w_sort_text; + const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x); if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort)) { if (column->SortOrder != -1) @@ -3038,19 +3144,156 @@ void ImGui::TableHeader(const char* label) RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); - if (text_clipped && hovered && g.ActiveId == 0 && IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(label_end - label), label); + if (text_clipped && hovered && g.ActiveId == 0) + SetItemTooltip("%.*s", (int)(label_end - label), label); // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden if (IsMouseReleased(1) && IsItemHovered()) TableOpenContextMenu(column_n); } +// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets. +// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn +// FIXME: No hit-testing/button on the angled header. +void ImGui::TableAngledHeadersRow() +{ + ImGuiContext& g = *GImGui; + TableAngledHeadersRowEx(g.Style.TableAngledHeadersAngle, 0.0f); +} + +void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width) +{ + ImGuiContext& g = *GImGui; + ImGuiTable* table = g.CurrentTable; + ImGuiWindow* window = g.CurrentWindow; + ImDrawList* draw_list = window->DrawList; + IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!"); + IM_ASSERT(table->CurrentRow == -1 && "Must be first row"); + + if (max_label_width == 0.0f) + max_label_width = TableGetHeaderAngledMaxLabelWidth(); + + // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user. + const bool flip_label = (angle < 0.0f); + angle -= IM_PI * 0.5f; + const float cos_a = ImCos(angle); + const float sin_a = ImSin(angle); + const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a; + const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a; + const ImVec2 unit_right = ImVec2(cos_a, sin_a); + + // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow() + // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other. + const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f; + const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y); + table->AngledHeadersHeight = row_height; + table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f; + const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right + + // Declare row, override and draw our own background + TableNextRow(ImGuiTableRowFlags_Headers, row_height); + TableNextColumn(); + const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, table->RowPosY2); + table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0); + float clip_rect_min_x = table->BgClipRect.Min.x; + if (table->FreezeColumnsCount > 0) + clip_rect_min_x = ImMax(clip_rect_min_x, table->Columns[table->FreezeColumnsCount - 1].MaxX); + TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel + PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns + draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color. + PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns + + const ImGuiID row_id = GetID("##AngledHeaders"); + ButtonBehavior(row_r, row_id, NULL, NULL); + KeepAliveID(row_id); + + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); + int highlight_column_n = table->HighlightColumnHeader; + if (highlight_column_n == -1 && table->HoveredColumnBody != -1) + if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive))) + highlight_column_n = table->HoveredColumnBody; + + // Draw background and labels in first pass, then all borders. + float max_x = 0.0f; + ImVec2 padding = g.Style.CellPadding; // We will always use swapped component + for (int pass = 0; pass < 2; pass++) + for (int order_n = 0; order_n < table->ColumnsCount; order_n++) + { + if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n)) + continue; + const int column_n = table->DisplayOrderToIndex[order_n]; + ImGuiTableColumn* column = &table->Columns[column_n]; + if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here. + continue; + + ImVec2 bg_shape[4]; + bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y); + bg_shape[1] = ImVec2(column->MinX, row_r.Max.y); + bg_shape[2] = bg_shape[1] + header_angled_vector; + bg_shape[3] = bg_shape[0] + header_angled_vector; + if (pass == 0) + { + // Draw shape + draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg)); + if (column_n == highlight_column_n) + draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover + max_x = ImMax(max_x, bg_shape[3].x); + + // Draw label + // - First draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset. + // - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated. + const char* label_name = TableGetColumnName(table, column_n); + const char* label_name_end = FindRenderedTextEnd(label_name); + const float line_off_step_x = g.FontSize / -sin_a; + float line_off_curr_x = 0.0f; + while (label_name < label_name_end) + { + const char* label_name_eol = strchr(label_name, '\n'); + if (label_name_eol == NULL) + label_name_eol = label_name_end; + + // FIXME: Individual line clipping for right-most column is broken for negative angles. + ImVec2 label_size = CalcTextSize(label_name, label_name_eol); + float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symetrical but hide more text. + float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x); + ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height)); + int vtx_idx_begin = draw_list->_VtxCurrentIdx; + RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size); + int vtx_idx_end = draw_list->_VtxCurrentIdx; + + // Rotate and offset label + ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x, window->ClipRect.Min.y + label_size.y); + ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y); + line_off_curr_x += line_off_step_x; + pivot_out += unit_right * padding.y; + if (flip_label) + pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x)); + pivot_out.x += flip_label ? line_off_curr_x - line_off_step_x : line_off_curr_x; + ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset + //if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); } + + // Register header width + column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(line_off_curr_x); + label_name = label_name_eol + 1; + } + } + if (pass == 1) + { + // Draw border + draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n)); + } + } + PopClipRect(); + PopClipRect(); + table->TempData->AngledHeadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX); +} + //------------------------------------------------------------------------- // [SECTION] Tables: Context Menu //------------------------------------------------------------------------- // - TableOpenContextMenu() [Internal] -// - TableDrawContextMenu() [Internal] +// - TableBeginContextMenuPopup() [Internal] +// - TableDrawDefaultContextMenu() [Internal] //------------------------------------------------------------------------- // Use -1 to open menu not specific to a given column. @@ -3086,7 +3329,13 @@ bool ImGui::TableBeginContextMenuPopup(ImGuiTable* table) // Output context menu into current window (generally a popup) // FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data? -void ImGui::TableDrawContextMenu(ImGuiTable* table) +// Sections to display are pulled from 'flags_for_section_to_display', which is typically == table->Flags. +// - ImGuiTableFlags_Resizable -> display Sizing menu items +// - ImGuiTableFlags_Reorderable -> display "Reset Order" +////- ImGuiTableFlags_Sortable -> display sorting options (disabled) +// - ImGuiTableFlags_Hideable -> display columns visibility menu items +// It means if you have a custom context menus you can call this section and omit some sections, and add your own. +void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -3098,7 +3347,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL; // Sizing - if (table->Flags & ImGuiTableFlags_Resizable) + if (flags_for_section_to_display & ImGuiTableFlags_Resizable) { if (column != NULL) { @@ -3118,7 +3367,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) } // Ordering - if (table->Flags & ImGuiTableFlags_Reorderable) + if (flags_for_section_to_display & ImGuiTableFlags_Reorderable) { if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder)) table->IsResetDisplayOrderRequest = true; @@ -3132,7 +3381,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) // Sorting // (modify TableOpenContextMenu() to add _Sortable flag if enabling this) #if 0 - if ((table->Flags & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0) + if ((flags_for_section_to_display & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0) { if (want_separator) Separator(); @@ -3147,7 +3396,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) #endif // Hiding / Visibility - if (table->Flags & ImGuiTableFlags_Hideable) + if (flags_for_section_to_display & ImGuiTableFlags_Hideable) { if (want_separator) Separator(); @@ -3477,12 +3726,12 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle if (!save_column) continue; buf->appendf("Column %-2d", column_n); - if (column->UserID != 0) buf->appendf(" UserID=%08X", column->UserID); - if (save_size && column->IsStretch) buf->appendf(" Weight=%.4f", column->WidthOrWeight); - if (save_size && !column->IsStretch) buf->appendf(" Width=%d", (int)column->WidthOrWeight); - if (save_visible) buf->appendf(" Visible=%d", column->IsEnabled); - if (save_order) buf->appendf(" Order=%d", column->DisplayOrder); - if (save_sort && column->SortOrder != -1) buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); + if (column->UserID != 0) { buf->appendf(" UserID=%08X", column->UserID); } + if (save_size && column->IsStretch) { buf->appendf(" Weight=%.4f", column->WidthOrWeight); } + if (save_size && !column->IsStretch) { buf->appendf(" Width=%d", (int)column->WidthOrWeight); } + if (save_visible) { buf->appendf(" Visible=%d", column->IsEnabled); } + if (save_order) { buf->appendf(" Order=%d", column->DisplayOrder); } + if (save_sort && column->SortOrder != -1) { buf->appendf(" Sort=%d%c", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); } buf->append("\n"); } buf->append("\n"); @@ -3530,7 +3779,7 @@ void ImGui::TableGcCompactTransientBuffers(ImGuiTable* table) IM_ASSERT(table->MemoryCompacted == false); table->SortSpecs.Specs = NULL; table->SortSpecsMulti.clear(); - table->IsSortSpecsDirty = true; // FIXME: shouldn't have to leak into user performing a sort + table->IsSortSpecsDirty = true; // FIXME: In theory shouldn't have to leak into user performing a sort on resume. table->ColumnsNames.clear(); table->MemoryCompacted = true; for (int n = 0; n < table->ColumnsCount; n++) @@ -3583,13 +3832,10 @@ static const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_poli void ImGui::DebugNodeTable(ImGuiTable* table) { - char buf[512]; - char* p = buf; - const char* buf_end = buf + IM_ARRAYSIZE(buf); - const bool is_active = (table->LastFrameActive >= ImGui::GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. - ImFormatString(p, buf_end - p, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*"); + ImGuiContext& g = *GImGui; + const bool is_active = (table->LastFrameActive >= g.FrameCount - 2); // Note that fully clipped early out scrolling tables will appear as inactive here. if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } - bool open = TreeNode(table, "%s", buf); + bool open = TreeNode(table, "Table 0x%08X (%d columns, in '%s')%s", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? "" : " *Inactive*"); if (!is_active) { PopStyleColor(); } if (IsItemHovered()) GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255)); @@ -3598,13 +3844,25 @@ void ImGui::DebugNodeTable(ImGuiTable* table) if (!open) return; if (table->InstanceCurrent > 0) - ImGui::Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1); + Text("** %d instances of same table! Some data below will refer to last instance.", table->InstanceCurrent + 1); + if (g.IO.ConfigDebugIsDebuggerPresent) + { + if (DebugBreakButton("**DebugBreak**", "in BeginTable()")) + g.DebugBreakInTable = table->ID; + SameLine(); + } + bool clear_settings = SmallButton("Clear settings"); BulletText("OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags)); BulletText("ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? " (auto)" : ""); BulletText("CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX); BulletText("HoveredColumnBody: %d, HoveredColumnBorder: %d", table->HoveredColumnBody, table->HoveredColumnBorder); BulletText("ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn); + for (int n = 0; n < table->InstanceCurrent + 1; n++) + { + ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n); + BulletText("Instance %d: HoveredRow: %d, LastOuterHeight: %.2f", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight); + } //BulletText("BgDrawChannels: %d/%d", 0, table->BgDrawChannelUnfrozen); float sum_weights = 0.0f; for (int n = 0; n < table->ColumnsCount; n++) @@ -3614,6 +3872,7 @@ void ImGui::DebugNodeTable(ImGuiTable* table) { ImGuiTableColumn* column = &table->Columns[n]; const char* name = TableGetColumnName(table, n); + char buf[512]; ImFormatString(buf, IM_ARRAYSIZE(buf), "Column %d order %d '%s': offset %+.2f to %+.2f%s\n" "Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\n" @@ -3902,6 +4161,7 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFl columns->Count = columns_count; columns->Flags = flags; window->DC.CurrentColumns = columns; + window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX(); columns->HostCursorPosY = window->DC.CursorPos.y; columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x; @@ -3912,7 +4172,7 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFl // Set state for first column // We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect const float column_padding = g.Style.ItemSpacing.x; - const float half_clip_extend_x = ImFloor(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize)); + const float half_clip_extend_x = ImTrunc(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize)); const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f); const float max_2 = window->WorkRect.Max.x + half_clip_extend_x; columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f); @@ -3959,8 +4219,9 @@ void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFl float width = offset_1 - offset_0; PushItemWidth(width * 0.65f); window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding; + window->WorkRect.Max.y = window->ContentRegionRect.Max.y; } void ImGui::NextColumn() @@ -3974,7 +4235,7 @@ void ImGui::NextColumn() if (columns->Count == 1) { - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); IM_ASSERT(columns->Current == 0); return; } @@ -4006,7 +4267,7 @@ void ImGui::NextColumn() window->DC.IsSameLine = false; columns->LineMinY = columns->LineMaxY; } - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); window->DC.CursorPos.y = columns->LineMinY; window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); window->DC.CurrLineTextBaseOffset = 0.0f; @@ -4070,7 +4331,7 @@ void ImGui::EndColumns() // Draw column const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); - const float xi = IM_FLOOR(x); + const float xi = IM_TRUNC(x); window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col); } @@ -4091,7 +4352,8 @@ void ImGui::EndColumns() window->ParentWorkRect = columns->HostBackupParentWorkRect; window->DC.CurrentColumns = NULL; window->DC.ColumnsOffset.x = 0.0f; - window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); + NavUpdateCurrentWindowIsScrollPushableX(); } void ImGui::Columns(int columns_count, const char* id, bool border) diff --git a/r5dev/thirdparty/imgui/imgui_widgets.cpp b/r5dev/thirdparty/imgui/imgui_widgets.cpp index 2771c311..7e9a1ffe 100644 --- a/r5dev/thirdparty/imgui/imgui_widgets.cpp +++ b/r5dev/thirdparty/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.89.4 +// dear imgui, v1.90.4 // (widgets code) /* @@ -18,6 +18,8 @@ Index of this file: // [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc. // [SECTION] Widgets: TreeNode, CollapsingHeader, etc. // [SECTION] Widgets: Selectable +// [SECTION] Widgets: Typing-Select support +// [SECTION] Widgets: Multi-Select support // [SECTION] Widgets: ListBox // [SECTION] Widgets: PlotLines, PlotHistogram // [SECTION] Widgets: Value helpers @@ -41,11 +43,7 @@ Index of this file: #include "imgui_internal.h" // System includes -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else #include // intptr_t -#endif //------------------------------------------------------------------------- // Warnings @@ -124,7 +122,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); //------------------------------------------------------------------------- // For InputTextEx() -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); +static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); @@ -479,6 +477,9 @@ void ImGui::BulletTextV(const char* fmt, va_list args) // Frame N + RepeatDelay + RepeatRate*N true true - true //------------------------------------------------------------------------------------------------------------------------------------------------- +// FIXME: For refactor we could output flags, incl mouse hovered vs nav keyboard vs nav triggered etc. +// And better standardize how widgets use 'GetColor32((held && hovered) ? ... : hovered ? ...)' vs 'GetColor32(held ? ... : hovered ? ...);' +// For mouse feedback we typically prefer the 'held && hovered' test, but for nav feedback not always. Outputting hovered=true on Activation may be misleading. bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; @@ -492,6 +493,14 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0) flags |= ImGuiButtonFlags_PressedOnDefault_; + // Default behavior inherited from item flags + // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that. + ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); + if (flags & ImGuiButtonFlags_AllowOverlap) + item_flags |= ImGuiItemFlags_AllowOverlap; + if (flags & ImGuiButtonFlags_Repeat) + item_flags |= ImGuiItemFlags_ButtonRepeat; + ImGuiWindow* backup_hovered_window = g.HoveredWindow; const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window; if (flatten_hovered_children) @@ -504,11 +513,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool #endif bool pressed = false; - bool hovered = ItemHoverable(bb, id); - - // Drag source doesn't report as hovered - if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover)) - hovered = false; + bool hovered = ItemHoverable(bb, id, item_flags); // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) @@ -527,10 +532,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (flatten_hovered_children) g.HoveredWindow = backup_hovered_window; - // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. - if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) - hovered = false; - // Mouse handling const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; if (hovered) @@ -579,7 +580,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { if (mouse_button_released != -1) { - const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior + const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior if (!has_repeated_at_least_once) pressed = true; if (!(flags & ImGuiButtonFlags_NoNavFocus)) @@ -590,7 +591,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. - if (g.ActiveId == id && (flags & ImGuiButtonFlags_Repeat)) + if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat)) if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat)) pressed = true; } @@ -599,16 +600,16 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool g.NavDisableHighlight = true; } - // Gamepad/Keyboard navigation - // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. - if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) + // Gamepad/Keyboard handling + // We report navigated and navigation-activated items as hovered but we don't set g.HoveredId to not interfere with mouse. + if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover) if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus)) hovered = true; if (g.NavActivateDownId == id) { bool nav_activated_by_code = (g.NavActivateId == id); bool nav_activated_by_inputs = (g.NavActivatePressedId == id); - if (!nav_activated_by_inputs && (flags & ImGuiButtonFlags_Repeat)) + if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat)) { // Avoid pressing multiple keys from triggering excessive amount of repeat events const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); @@ -622,9 +623,11 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. pressed = true; SetActiveID(id, window); - g.ActiveIdSource = ImGuiInputSource_Nav; - if (!(flags & ImGuiButtonFlags_NoNavFocus)) + g.ActiveIdSource = g.NavInputSource; + if (!(flags & ImGuiButtonFlags_NoNavFocus) && !(g.NavActivateFlags & ImGuiActivateFlags_FromShortcut)) SetFocusID(id, window); + if (g.NavActivateFlags & ImGuiActivateFlags_FromShortcut) + g.ActiveIdFromShortcut = true; } } @@ -638,7 +641,12 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; const int mouse_button = g.ActiveIdMouseButton; - if (IsMouseDown(mouse_button, test_owner_id)) + if (mouse_button == -1) + { + // Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304). + ClearActiveID(); + } + else if (IsMouseDown(mouse_button, test_owner_id)) { held = true; } @@ -650,7 +658,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool { // Report as pressed when releasing the mouse (this is the most common path) bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; - bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps + bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id); if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned) pressed = true; @@ -660,16 +668,22 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (!(flags & ImGuiButtonFlags_NoNavFocus)) g.NavDisableHighlight = true; } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) + else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) { // When activated using Nav, we hold on the ActiveID until activation button is released - if (g.NavActivateDownId != id) + if (g.NavActivateDownId == id) + held = true; // hovered == true not true as we are already likely hovered on direct activation. + else ClearActiveID(); } if (pressed) g.ActiveIdHasBeenPressedBefore = true; } + // Activation highlight (this may be a remote activation) + if (g.NavHighlightActivatedId == id) + hovered = true; + if (out_hovered) *out_hovered = hovered; if (out_held) *out_held = held; @@ -697,9 +711,6 @@ bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags if (!ItemAdd(bb, id)) return false; - if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -776,9 +787,6 @@ bool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiBu if (!ItemAdd(bb, id)) return false; - if (g.LastItemData.InFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); @@ -807,14 +815,14 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825) // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible? - const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); + const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); ImRect bb_interact = bb; const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea(); if (area_to_visible_ratio < 1.5f) - bb_interact.Expand(ImFloor(bb_interact.GetSize() * -0.25f)); + bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f)); // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window. - // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). + // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer). bool is_clipped = !ItemAdd(bb_interact, id); bool hovered, held; @@ -827,7 +835,7 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered); ImVec2 center = bb.GetCenter(); if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col, 12); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col); float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f; ImU32 cross_col = GetColorU32(ImGuiCol_Text); @@ -843,17 +851,19 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize) + g.Style.FramePadding * 2.0f); - ItemAdd(bb, id); + ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize)); + bool is_clipped = !ItemAdd(bb, id); bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None); + if (is_clipped) + return pressed; // Render ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); if (hovered || held) - window->DrawList->AddCircleFilled(bb.GetCenter()/*+ ImVec2(0.0f, -0.5f)*/, g.FontSize * 0.5f + 1.0f, bg_col, 12); - RenderArrow(window->DrawList, bb.Min + g.Style.FramePadding, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); + window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, bg_col); + RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging(0)) @@ -876,9 +886,9 @@ ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) IM_ASSERT(scrollbar_size > 0.0f); if (axis == ImGuiAxis_X) - return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x, outer_rect.Max.y); + return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); else - return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x, inner_rect.Max.y); + return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); } void ImGui::Scrollbar(ImGuiAxis axis) @@ -939,7 +949,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 const bool allow_interaction = (alpha >= 1.0f); ImRect bb = bb_frame; - bb.Expand(ImVec2(-ImClamp(IM_FLOOR((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_FLOOR((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f))); + bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f))); // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight(); @@ -967,7 +977,6 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 // Click position in scrollbar normalized space (0.0f->1.0f) const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); - SetHoveredID(id); bool seek_absolute = false; if (g.ActiveIdIsJustActivated) @@ -1008,33 +1017,30 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 return held; } -void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +// - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples +// - 'uv0' and 'uv1' are texture coordinates. Read about them from the same link above. +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return; - ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - if (border_col.w > 0.0f) - bb.Max += ImVec2(2, 2); + const float border_size = (border_col.w > 0.0f) ? 1.0f : 0.0f; + const ImVec2 padding(border_size, border_size); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); ItemSize(bb); if (!ItemAdd(bb, 0)) return; - if (border_col.w > 0.0f) - { - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); - window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col)); - } - else - { - window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); - } + // Render + if (border_size > 0.0f) + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f, ImDrawFlags_None, border_size); + window->DrawList->AddImage(user_texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col)); } // ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390) // We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API. -bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) +bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); @@ -1042,7 +1048,7 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size return false; const ImVec2 padding = g.Style.FramePadding; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2.0f); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f); ItemSize(bb); if (!ItemAdd(bb, id)) return false; @@ -1061,14 +1067,15 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size return pressed; } -bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) +// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button. +bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; if (window->SkipItems) return false; - return ImageButtonEx(window->GetID(str_id), user_texture_id, size, uv0, uv1, bg_col, tint_col); + return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col); } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS @@ -1135,12 +1142,12 @@ bool ImGui::Checkbox(const char* label, bool* v) { // Undocumented tristate/mixed/indeterminate checkbox (#2644) // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox) - ImVec2 pad(ImMax(1.0f, IM_FLOOR(square_sz / 3.6f)), ImMax(1.0f, IM_FLOOR(square_sz / 3.6f))); + ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f))); window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding); } else if (*v) { - const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); + const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f); } @@ -1163,10 +1170,8 @@ bool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value) if (!all_on && any_on) { ImGuiContext& g = *GImGui; - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_MixedValue; + g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue; pressed = Checkbox(label, &all_on); - g.CurrentItemFlags = backup_item_flags; } else { @@ -1233,17 +1238,18 @@ bool ImGui::RadioButton(const char* label, bool active) MarkItemEdited(id); RenderNavHighlight(total_bb, id); - window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); + const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius); + window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment); if (active) { - const float pad = ImMax(1.0f, IM_FLOOR(square_sz / 6.0f)); - window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark), 16); + const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark)); } if (style.FrameBorderSize > 0.0f) { - window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); - window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); + window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize); + window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize); } ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); @@ -1386,8 +1392,9 @@ void ImGui::AlignTextToFramePadding() } // Horizontal/vertical separating line -// FIXME: Surprisingly, this seemingly simple widget is adjacent to MANY different legacy/tricky layout issues. -void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) +// FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues. +// Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are. +void ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -1395,8 +1402,8 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) ImGuiContext& g = *GImGui; IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical))); // Check that only 1 option is selected + IM_ASSERT(thickness > 0.0f); - const float thickness = 1.0f; // Cannot use g.Style.SeparatorTextSize yet for various reasons. if (flags & ImGuiSeparatorFlags_Vertical) { // Vertical separator, for menu bars (use current line height). @@ -1415,32 +1422,27 @@ void ImGui::SeparatorEx(ImGuiSeparatorFlags flags) else if (flags & ImGuiSeparatorFlags_Horizontal) { // Horizontal Separator - float x1 = window->Pos.x; - float x2 = window->Pos.x + window->Size.x; - - // FIXME-WORKRECT: old hack (#205) until we decide of consistent behavior with WorkRect/Indent and Separator - if (g.GroupStack.Size > 0 && g.GroupStack.back().WindowID == window->ID) - x1 += window->DC.Indent.x; - - // FIXME-WORKRECT: In theory we should simply be using WorkRect.Min.x/Max.x everywhere but it isn't aesthetically what we want, - // need to introduce a variant of WorkRect for that purpose. (#4787) - if (ImGuiTable* table = g.CurrentTable) - { - x1 = table->Columns[table->CurrentColumn].MinX; - x2 = table->Columns[table->CurrentColumn].MaxX; - } + float x1 = window->DC.CursorPos.x; + float x2 = window->WorkRect.Max.x; + // Preserve legacy behavior inside Columns() + // Before Tables API happened, we relied on Separator() to span all columns of a Columns() set. + // We currently don't need to provide the same feature for tables because tables naturally have border features. ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL; if (columns) + { + x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03 + x2 = window->Pos.x + window->Size.x; PushColumnsBackground(); + } // We don't provide our width to the layout so that it doesn't get feed back into AutoFit // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell) const float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change. const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness)); ItemSize(ImVec2(0.0f, thickness_for_layout)); - const bool item_visible = ItemAdd(bb, 0); - if (item_visible) + + if (ItemAdd(bb, 0)) { // Draw window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator)); @@ -1463,10 +1465,15 @@ void ImGui::Separator() if (window->SkipItems) return; - // Those flags should eventually be overridable by the user + // Those flags should eventually be configurable by the user + // FIXME: We cannot g.Style.SeparatorTextBorderSize for thickness as it relates to SeparatorText() which is a decorated separator, not defaulting to 1.0f. ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - flags |= ImGuiSeparatorFlags_SpanAllColumns; // NB: this only applies to legacy Columns() api as they relied on Separator() a lot. - SeparatorEx(flags); + + // Only applies to legacy Columns() api as they relied on Separator() a lot. + if (window->DC.CurrentColumns) + flags |= ImGuiSeparatorFlags_SpanAllColumns; + + SeparatorEx(flags, 1.0f); } void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w) @@ -1482,14 +1489,14 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end const float separator_thickness = style.SeparatorTextBorderSize; const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); - const float text_baseline_y = ImFloor((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f)); + const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f)); ItemSize(min_size, text_baseline_y); if (!ItemAdd(bb, id)) return; const float sep1_x1 = pos.x; const float sep2_x2 = bb.Max.x; - const float seps_y = ImFloor((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f); + const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f); const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f); const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN @@ -1526,8 +1533,8 @@ void ImGui::SeparatorText(const char* label) return; // The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want: - // - allow headers to be draggable items (would require a stable ID + a noticeable highlight) - // - this high-level entry point to allow formatting? (may require ID separate from formatted string) + // - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight) + // - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string) // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...' // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item, // and then we can turn this into a format function. @@ -1543,14 +1550,20 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav)) return false; + // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is + // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item. + // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item. + ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + button_flags |= ImGuiButtonFlags_AllowOverlap; +#endif + bool hovered, held; ImRect bb_interact = bb; bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); - ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); + ButtonBehavior(bb_interact, id, &hovered, &held, button_flags); if (hovered) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb - if (g.ActiveId != id) - SetItemAllowOverlap(); if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay)) SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); @@ -1558,8 +1571,7 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float ImRect bb_render = bb; if (held) { - ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; - float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; + float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis]; // Minimum pane size float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1); @@ -1572,12 +1584,8 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float // Apply resize if (mouse_delta != 0.0f) { - if (mouse_delta < 0.0f) - IM_ASSERT(*size1 + mouse_delta >= min_size1); - if (mouse_delta > 0.0f) - IM_ASSERT(*size2 - mouse_delta >= min_size2); - *size1 += mouse_delta; - *size2 -= mouse_delta; + *size1 = ImMax(*size1 + mouse_delta, min_size1); + *size2 = ImMax(*size2 - mouse_delta, min_size2); bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); MarkItemEdited(id); } @@ -1631,7 +1639,7 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc width_excess = 0.0f; for (int n = 0; n < count; n++) { - float width_rounded = ImFloor(items[n].Width); + float width_rounded = ImTrunc(items[n].Width); width_excess += items[n].Width - width_rounded; items[n].Width = width_rounded; } @@ -1677,10 +1685,13 @@ bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboF const ImGuiStyle& style = g.Style; const ImGuiID id = window->GetID(label); IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together + if (flags & ImGuiComboFlags_WidthFitPreview) + IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | (ImGuiComboFlags)ImGuiComboFlags_CustomPreview)) == 0); const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); + const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f; + const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth()); const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); ItemSize(total_bb, style.FramePadding.y); @@ -1773,7 +1784,7 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags // This is essentially a specialized version of BeginPopupEx() char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginPopupStack.Size); // Recycle windows based on depth + ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.BeginComboDepth); // Recycle windows based on depth // Set position given a custom constraint (peak into expected window size so we can position it) // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function? @@ -1800,12 +1811,15 @@ bool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above return false; } + g.BeginComboDepth++; return true; } void ImGui::EndCombo() { + ImGuiContext& g = *GImGui; EndPopup(); + g.BeginComboDepth--; } // Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements @@ -1819,7 +1833,7 @@ bool ImGui::BeginComboPreview() if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)) return false; IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? - if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) + if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional) return false; // FIXME: This could be contained in a PushWorkRect() api @@ -1862,18 +1876,15 @@ void ImGui::EndComboPreview() } // Getter for the old Combo() API: const char*[] -static bool Items_ArrayGetter(void* data, int idx, const char** out_text) +static const char* Items_ArrayGetter(void* data, int idx) { const char* const* items = (const char* const*)data; - if (out_text) - *out_text = items[idx]; - return true; + return items[idx]; } // Getter for the old Combo() API: "item1\0item2\0item3\0" -static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) +static const char* Items_SingleStringGetter(void* data, int idx) { - // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. const char* items_separated_by_zeros = (const char*)data; int items_count = 0; const char* p = items_separated_by_zeros; @@ -1884,22 +1895,18 @@ static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) p += strlen(p) + 1; items_count++; } - if (!*p) - return false; - if (out_text) - *out_text = p; - return true; + return *p ? p : NULL; } // Old API, prefer using BeginCombo() nowadays if you can. -bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) +bool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items) { ImGuiContext& g = *GImGui; // Call the getter to obtain the preview string which is a parameter to BeginCombo() const char* preview_value = NULL; if (*current_item >= 0 && *current_item < items_count) - items_getter(data, *current_item, &preview_value); + preview_value = getter(user_data, *current_item); // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't have/need it, but we emulate it here. if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)) @@ -1913,12 +1920,13 @@ bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(voi bool value_changed = false; for (int i = 0; i < items_count; i++) { + const char* item_text = getter(user_data, i); + if (item_text == NULL) + item_text = "*Unknown item*"; + PushID(i); const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - if (Selectable(item_text, item_selected)) + if (Selectable(item_text, item_selected) && *current_item != i) { value_changed = true; *current_item = i; @@ -1957,13 +1965,37 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa return value_changed; } +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +struct ImGuiGetNameFromIndexOldToNewCallbackData { void* UserData; bool (*OldCallback)(void*, int, const char**); }; +static const char* ImGuiGetNameFromIndexOldToNewCallback(void* user_data, int idx) +{ + ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data; + const char* s = NULL; + data->OldCallback(data->UserData, idx, &s); + return s; +} + +bool ImGui::ListBox(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int height_in_items) +{ + ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter }; + return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items); +} +bool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int popup_max_height_in_items) +{ + ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter }; + return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items); +} + +#endif + //------------------------------------------------------------------------- // [SECTION] Data Type and Data Formatting Helpers [Internal] //------------------------------------------------------------------------- // - DataTypeGetInfo() // - DataTypeFormatString() // - DataTypeApplyOp() -// - DataTypeApplyOpFromText() +// - DataTypeApplyFromText() // - DataTypeCompare() // - DataTypeClamp() // - GetMinimumStepAtDecimalPrecision @@ -2084,7 +2116,8 @@ bool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void memcpy(&data_backup, p_data, type_info->Size); // Sanitize format - // For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf + // - For float/double we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in, so force them into %f and %lf + // - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %. char format_sanitized[32]; if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) format = type_info->ScanFmt; @@ -2245,7 +2278,7 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const if (g.IO.KeyShift) adjust_delta *= 10.0f; } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) + else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) { const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); @@ -2352,7 +2385,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation. if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ClearActiveID(); - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) + else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) ClearActiveID(); } if (g.ActiveId != id) @@ -2404,19 +2437,18 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { // Tabbing or CTRL-clicking on Drag turns it into an InputText - const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = hovered && IsMouseClicked(0, id); const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id)); - const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id); + const bool make_active = (clicked || double_clicked || g.NavActivateId == id); if (make_active && (clicked || double_clicked)) SetKeyOwner(ImGuiKey_MouseLeft, id); if (make_active && temp_input_allowed) - if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) + if ((clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) temp_input_is_active = true; // (Optional) simple click (without moving) turns Drag into an InputText @@ -2767,14 +2799,14 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X; const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0; const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); - const SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); + const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it. // Calculate bounds const float grab_padding = 2.0f; // FIXME: Should be part of style. const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; float grab_sz = style.GrabMinSize; - if (!is_floating_point && v_range >= 0) // v_range < 0 may happen on integer overflows - grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit + if (!is_floating_point && v_range_f >= 0.0f) // v_range_f < 0 may happen on integer overflows + grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit grab_sz = ImMin(grab_sz, slider_sz); const float slider_usable_sz = slider_sz - grab_sz; const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f; @@ -2821,7 +2853,7 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ set_new_value = true; } } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) + else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) { if (g.ActiveIdIsJustActivated) { @@ -2843,8 +2875,8 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ } else { - if ((v_range >= -100.0f && v_range <= 100.0f) || tweak_slow) - input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps + if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow) + input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Gamepad/keyboard tweak speeds in integer steps else input_delta /= 100.0f; } @@ -2891,6 +2923,10 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ } } + if (set_new_value) + if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) + set_new_value = false; + if (set_new_value) { TYPE v_new = ScaleValueFromRatioT(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); @@ -2936,11 +2972,6 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); - // Those are the things we can do easily outside the SliderBehaviorT<> template, saves code generation. - ImGuiContext& g = *GImGui; - if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) - return false; - switch (data_type) { case ImGuiDataType_S8: { ImS32 v32 = (ImS32)*(ImS8*)p_v; bool r = SliderBehaviorT(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min, *(const ImS8*)p_max, format, flags, out_grab_bb); if (r) *(ImS8*)p_v = (ImS8)v32; return r; } @@ -2997,18 +3028,17 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); if (!temp_input_is_active) { // Tabbing or CTRL-clicking on Slider turns it into an input box - const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; const bool clicked = hovered && IsMouseClicked(0, id); - const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id); + const bool make_active = (clicked || g.NavActivateId == id); if (make_active && clicked) SetKeyOwner(ImGuiKey_MouseLeft, id); if (make_active && temp_input_allowed) - if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) + if ((clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))) temp_input_is_active = true; if (make_active && !temp_input_is_active) @@ -3164,7 +3194,7 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); const bool clicked = hovered && IsMouseClicked(0, id); if (clicked || g.NavActivateId == id) { @@ -3267,7 +3297,7 @@ const char* ImParseFormatFindEnd(const char* fmt) } // Extract the format out of a format string with leading or trailing decorations -// fmt = "blah blah" -> return fmt +// fmt = "blah blah" -> return "" // fmt = "%.3f" -> return fmt // fmt = "hello %.3f" -> return fmt + 6 // fmt = "%.3f hello" -> return buf written with "%.3f" @@ -3275,7 +3305,7 @@ const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_ { const char* fmt_start = ImParseFormatFindStart(fmt); if (fmt_start[0] != '%') - return fmt; + return ""; const char* fmt_end = ImParseFormatFindEnd(fmt_start); if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. return fmt_start; @@ -3300,7 +3330,7 @@ void ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t *fmt_out = 0; // Zero-terminate } -// - For scanning we need to remove all width and precision fields "%3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" +// - For scanning we need to remove all width and precision fields and flags "%+3.7f" -> "%f". BUT don't strip types like "%I64d" which includes digits. ! "%07I64d" -> "%I64d" const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size) { const char* fmt_end = ImParseFormatFindEnd(fmt_in); @@ -3311,7 +3341,7 @@ const char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, while (fmt_in < fmt_end) { char c = *fmt_in++; - if (!has_type && ((c >= '0' && c <= '9') || c == '.')) + if (!has_type && ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '#')) continue; has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits if (c != '\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '. @@ -3380,33 +3410,29 @@ bool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* return value_changed; } -static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(ImGuiDataType data_type, const char* format) -{ - if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) - return ImGuiInputTextFlags_CharsScientific; - const char format_last_char = format[0] ? format[strlen(format) - 1] : 0; - return (format_last_char == 'x' || format_last_char == 'X') ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal; -} - // Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set! // This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility. // However this may not be ideal for all uses, as some user code may break on out of bound values. bool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max) { + // FIXME: May need to clarify display behavior if format doesn't contain %. + // "%d" -> "%d" / "There are %d items" -> "%d" / "items" -> "%d" (fallback). Also see #6405 + const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type); char fmt_buf[32]; char data_buf[32]; format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); + if (format[0] == 0) + format = type_info->PrintFmt; DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format); ImStrTrimBlanks(data_buf); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; - flags |= InputScalar_DefaultCharsFilter(data_type, format); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited; bool value_changed = false; if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags)) { // Backup old value - size_t data_type_size = DataTypeGetInfo(data_type)->Size; + size_t data_type_size = type_info->Size; ImGuiDataTypeTempStorage data_backup; memcpy(&data_backup, p_data, data_type_size); @@ -3444,13 +3470,15 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data char buf[64]; DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format); - // Testing ActiveId as a minor optimization as filtering is not needed until active - if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) - flags |= InputScalar_DefaultCharsFilter(data_type, format); - flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. + flags |= ImGuiInputTextFlags_AutoSelectAll | (ImGuiInputTextFlags)ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string. bool value_changed = false; - if (p_step != NULL) + if (p_step == NULL) + { + if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) + value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); + } + else { const float button_size = GetFrameHeight(); @@ -3493,11 +3521,6 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data PopID(); EndGroup(); } - else - { - if (InputText(label, buf, IM_ARRAYSIZE(buf), flags)) - value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); - } if (value_changed) MarkItemEdited(g.LastItemData.ID); @@ -3541,7 +3564,6 @@ bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_dat bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags) { - flags |= ImGuiInputTextFlags_CharsScientific; return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags); } @@ -3584,7 +3606,6 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags) bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags) { - flags |= ImGuiInputTextFlags_CharsScientific; return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags); } @@ -3682,8 +3703,8 @@ namespace ImStb { static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { IM_ASSERT(idx <= obj->CurLenW); return obj->TextW[idx]; } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return IMSTB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) @@ -3699,10 +3720,34 @@ static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* ob r->num_chars = (int)(text_remaining - (text + line_start_idx)); } -// When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r'; } -static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (is_separator(obj->TextW[idx - 1]) && !is_separator(obj->TextW[idx]) ) : 1; } -static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } +static bool is_separator(unsigned int c) +{ + return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\n' || c=='\r' || c=='.' || c=='!'; +} + +static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) +{ + // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators. + if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) + return 0; + + bool prev_white = ImCharIsBlankW(obj->TextW[idx - 1]); + bool prev_separ = is_separator(obj->TextW[idx - 1]); + bool curr_white = ImCharIsBlankW(obj->TextW[idx]); + bool curr_separ = is_separator(obj->TextW[idx]); + return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); +} +static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) +{ + if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0) + return 0; + + bool prev_white = ImCharIsBlankW(obj->TextW[idx]); + bool prev_separ = is_separator(obj->TextW[idx]); + bool curr_white = ImCharIsBlankW(obj->TextW[idx - 1]); + bool curr_separ = is_separator(obj->TextW[idx - 1]); + return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ); +} static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } @@ -3777,12 +3822,13 @@ static bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const Im #define STB_TEXTEDIT_K_PGDOWN 0x20000F // keyboard input to move cursor down a page #define STB_TEXTEDIT_K_SHIFT 0x400000 -#define STB_TEXTEDIT_IMPLEMENTATION +#define IMSTB_TEXTEDIT_IMPLEMENTATION +#define IMSTB_TEXTEDIT_memmove memmove #include "imstb_textedit.h" // stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling // the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?) -static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len) +static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len) { stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); @@ -3835,6 +3881,10 @@ void ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count) void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) { + // Accept null ranges + if (new_text == new_text_end) + return; + const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0; const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); if (new_text_len + BufTextLen >= BufSize) @@ -3866,7 +3916,7 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons } // Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source) +static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source) { IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard); unsigned int c = *p_char; @@ -3876,8 +3926,8 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f if (c < 0x20) { bool pass = false; - pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) - pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); + pass |= (c == '\n') && (flags & ImGuiInputTextFlags_Multiline) != 0; // Note that an Enter KEY will emit \r and be ignored (we poll for KEY in InputText() code) + pass |= (c == '\t') && (flags & ImGuiInputTextFlags_AllowTabInput) != 0; if (!pass) return false; apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted. @@ -3905,10 +3955,13 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // The standard mandate that programs starts in the "C" locale where the decimal point is '.'. // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point. // Change the default decimal_point with: - // ImGui::GetCurrentContext()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; + // ImGui::GetIO()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point; // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions. - ImGuiContext& g = *GImGui; - const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; + ImGuiContext& g = *ctx; + const unsigned c_decimal_point = (unsigned int)g.IO.PlatformLocaleDecimalPoint; + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific)) + if (c == '.' || c == ',') + c = c_decimal_point; // Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may @@ -3994,11 +4047,34 @@ static void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* st const int insert_len = new_last_diff - first_diff + 1; const int delete_len = old_last_diff - first_diff + 1; if (insert_len > 0 || delete_len > 0) - if (STB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len)) + if (IMSTB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len)) for (int i = 0; i < delete_len; i++) p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i); } +// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables) +// we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714) +// It would be more desirable that we discourage users from taking advantage of the "user not retaining data" trick, +// but that more likely be attractive when we do have _NoLiveEdit flag available. +void ImGui::InputTextDeactivateHook(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + ImGuiInputTextState* state = &g.InputTextState; + if (id == 0 || state->ID != id) + return; + g.InputTextDeactivatedState.ID = state->ID; + if (state->Flags & ImGuiInputTextFlags_ReadOnly) + { + g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat. + } + else + { + IM_ASSERT(state->TextA.Data != 0); + g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1); + memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->CurLenA + 1); + } +} + // Edit a string of text // - buf_size account for the zero-terminator, so a buf_size of 6 can hold "Hello" but not "Hello!". // This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match @@ -4023,12 +4099,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const bool RENDER_SELECTION_WHEN_INACTIVE = false; const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; - const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; - const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; - const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; - const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; - if (is_resizable) - IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar) BeginGroup(); @@ -4042,7 +4112,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImGuiWindow* draw_window = window; ImVec2 inner_size = frame_size; - ImGuiItemStatusFlags item_status_flags = 0; ImGuiLastItemData item_data_backup; if (is_multiline) { @@ -4053,17 +4122,25 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ EndGroup(); return false; } - item_status_flags = g.LastItemData.StatusFlags; item_data_backup = g.LastItemData; window->DC.CursorPos = backup_pos; + // Prevent NavActivation from Tabbing when our widget accepts Tab inputs: this allows cycling through widgets without stopping. + if (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_FromTabbing) && (flags & ImGuiInputTextFlags_AllowTabInput)) + g.NavActivateId = 0; + + // Prevent NavActivate reactivating in BeginChild() when we are already active. + const ImGuiID backup_activate_id = g.NavActivateId; + if (g.ActiveId == id) // Prevent reactivation + g.NavActivateId = 0; + // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug. - // FIXME-NAV: Pressing NavActivate will trigger general child activation right before triggering our own below. Harmless but bizarre. PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove); + g.NavActivateId = backup_activate_id; PopStyleVar(3); PopStyleColor(); if (!child_visible) @@ -4084,16 +4161,23 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (!(flags & ImGuiInputTextFlags_MergedItem)) if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) return false; - item_status_flags = g.LastItemData.StatusFlags; } - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); if (hovered) g.MouseCursor = ImGuiMouseCursor_TextInput; // We are only allowed to access the state if we are already the active widget. ImGuiInputTextState* state = GetInputTextState(id); - const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; + if (g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) + flags |= ImGuiInputTextFlags_ReadOnly; + const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0; + const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; + const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; + const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0; + if (is_resizable) + IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag! + const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard))); const bool user_clicked = hovered && io.MouseClicked[0]; @@ -4105,24 +4189,32 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; - const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); - const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing); + const bool init_reload_from_user_buf = (state != NULL && state->ReloadUserBuf); + const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); // state != NULL means its our state. + const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav); const bool init_state = (init_make_active || user_scroll_active); - if ((init_state && g.ActiveId != id) || init_changed_specs) + if ((init_state && g.ActiveId != id) || init_changed_specs || init_reload_from_user_buf) { // Access state even if we don't own it yet. state = &g.InputTextState; state->CursorAnimReset(); + state->ReloadUserBuf = false; - // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) - // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) + // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714) + InputTextDeactivateHook(state->ID); + + // From the moment we focused we are normally ignoring the content of 'buf' (unless we are in read-only mode) const int buf_len = (int)strlen(buf); - state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. - memcpy(state->InitialTextA.Data, buf, buf_len + 1); + if (!init_reload_from_user_buf) + { + // Take a copy of the initial buffer value. + state->InitialTextA.resize(buf_len + 1); // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string. + memcpy(state->InitialTextA.Data, buf, buf_len + 1); + } // Preserve cursor position and undo/redo stack if we come back to same widget - // FIXME: Since we reworked this on 2022/06, may want to differenciate recycle_cursor vs recycle_undostate? - bool recycle_state = (state->ID == id && !init_changed_specs); + // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate? + bool recycle_state = (state->ID == id && !init_changed_specs && !init_reload_from_user_buf); if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0))) recycle_state = false; @@ -4153,13 +4245,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ auto_caret = true; } - if (!is_multiline) + if (init_reload_from_user_buf) + { + state->Stb.select_start = state->ReloadSelectionStart; + state->Stb.cursor = state->Stb.select_end = state->ReloadSelectionEnd; + state->CursorClamp(); + } + else if (!is_multiline) { if (flags & ImGuiInputTextFlags_AutoSelectAll) select_all = true; if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState))) select_all = true; - if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl)) + if (user_clicked && io.KeyCtrl) select_all = true; } @@ -4183,6 +4281,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); + SetKeyOwner(ImGuiKey_Enter, id); + SetKeyOwner(ImGuiKey_KeypadEnter, id); SetKeyOwner(ImGuiKey_Home, id); SetKeyOwner(ImGuiKey_End, id); if (is_multiline) @@ -4192,8 +4292,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } if (is_osx) SetKeyOwner(ImGuiMod_Alt, id); - if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. - SetShortcutRouting(ImGuiKey_Tab, id); } // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) @@ -4255,7 +4353,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. // Down the line we should have a cleaner library-wide concept of Selected vs Active. g.ActiveIdAllowOverlap = !io.MouseDown[0]; - g.WantTextInputNextFrame = 1; // Edit in progress const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX; @@ -4323,11 +4420,20 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) - if ((flags & ImGuiInputTextFlags_AllowTabInput) && Shortcut(ImGuiKey_Tab, id) && !is_readonly) + if ((flags & ImGuiInputTextFlags_AllowTabInput) && !is_readonly) { - unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) - state->OnKeyPressed((int)c); + if (Shortcut(ImGuiKey_Tab, id, ImGuiInputFlags_Repeat)) + { + unsigned int c = '\t'; // Insert TAB + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) + state->OnKeyPressed((int)c); + } + // FIXME: Implement Shift+Tab + /* + if (Shortcut(ImGuiKey_Tab | ImGuiMod_Shift, id, ImGuiInputFlags_Repeat)) + { + } + */ } // Process regular text input (before we check for Return because using some IME will effectively send a Return?) @@ -4342,7 +4448,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ unsigned int c = (unsigned int)io.InputQueueCharacters[n]; if (c == '\t') // Skip Tab, see above. continue; - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } @@ -4425,7 +4531,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else if (!is_readonly) { unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) + if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard)) state->OnKeyPressed((int)c); } } @@ -4433,7 +4539,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { if (flags & ImGuiInputTextFlags_EscapeClearsAll) { - if (state->CurLenA > 0) + if (buf[0] != 0) { revert_edit = true; } @@ -4492,7 +4598,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { unsigned int c; s += ImTextCharFromUtf8(&c, s, NULL); - if (!InputTextFilterCharacter(&c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard)) + if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard)) continue; clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; } @@ -4521,9 +4627,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (flags & ImGuiInputTextFlags_EscapeClearsAll) { // Clear input + IM_ASSERT(buf[0] != 0); apply_new_text = ""; apply_new_text_length = 0; - STB_TEXTEDIT_CHARTYPE empty_string; + value_changed = true; + IMSTB_TEXTEDIT_CHARTYPE empty_string; stb_textedit_replace(state, &state->Stb, &empty_string, 0); } else if (strcmp(buf, state->InitialTextA.Data) != 0) @@ -4532,6 +4640,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Push records into the undo stack so we can CTRL+Z the revert operation itself apply_new_text = state->InitialTextA.Data; apply_new_text_length = state->InitialTextA.Size - 1; + value_changed = true; ImVector w_text; if (apply_new_text_length > 0) { @@ -4550,9 +4659,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL); } - // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. + // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer + // before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. - // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). + // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage + // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object + // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); if (apply_edit_back_to_user_buffer) { @@ -4628,7 +4740,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } if (buf_dirty) { - IM_ASSERT((flags & ImGuiInputTextFlags_ReadOnly) == 0); + IM_ASSERT(!is_readonly); IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ? if (callback_data.BufTextLen > backup_current_text_length && is_resizable) @@ -4645,10 +4757,24 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { apply_new_text = state->TextA.Data; apply_new_text_length = state->CurLenA; + value_changed = true; } } } + // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details) + if (g.InputTextDeactivatedState.ID == id) + { + if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0) + { + apply_new_text = g.InputTextDeactivatedState.TextA.Data; + apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1; + value_changed = true; + //IMGUI_DEBUG_LOG("InputText(): apply Deactivated data for 0x%08X: \"%.*s\".\n", id, apply_new_text_length, apply_new_text); + } + g.InputTextDeactivatedState.ID = 0; + } + // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) if (apply_new_text != NULL) { @@ -4676,12 +4802,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size. ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size)); - value_changed = true; } // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) - if (clear_active_id && g.ActiveId == id) + // Otherwise request text input ahead for next frame. + if (g.ActiveId == id && clear_active_id) ClearActiveID(); + else if (g.ActiveId == id) + g.WantTextInputNextFrame = 1; // Render frame if (!is_multiline) @@ -4783,9 +4911,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const float scroll_increment_x = inner_size.x * 0.25f; const float visible_width = inner_size.x - style.FramePadding.x; if (cursor_offset.x < state->ScrollX) - state->ScrollX = IM_FLOOR(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); + state->ScrollX = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x)); else if (cursor_offset.x - visible_width >= state->ScrollX) - state->ScrollX = IM_FLOOR(cursor_offset.x - visible_width + scroll_increment_x); + state->ScrollX = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x); } else { @@ -4835,7 +4963,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ else { ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true); - if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines + if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); rect.ClipWith(clip_rect); if (rect.Overlaps(clip_rect)) @@ -4861,7 +4989,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { state->CursorAnim += io.DeltaTime; bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = ImFloor(draw_pos + cursor_offset - draw_scroll); + ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll); ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f); if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); @@ -4899,11 +5027,9 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)... Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y)); - ImGuiItemFlags backup_item_flags = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; + g.NextItemData.ItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop; EndChild(); item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow); - g.CurrentItemFlags = backup_item_flags; // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active... // FIXME: This quite messy/tricky, should attempt to get rid of the child window. @@ -4947,10 +5073,10 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenW, state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end); Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); - if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), true)) // Visualize undo state + if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 10), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY)) // Visualize undo state { PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - for (int n = 0; n < STB_TEXTEDIT_UNDOSTATECOUNT; n++) + for (int n = 0; n < IMSTB_TEXTEDIT_UNDOSTATECOUNT; n++) { ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n]; const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' '; @@ -5032,10 +5158,8 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag ImGuiContext& g = *GImGui; const ImGuiStyle& style = g.Style; const float square_sz = GetFrameHeight(); - const float w_full = CalcItemWidth(); - const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); - const float w_inputs = w_full - w_button; const char* label_display_end = FindRenderedTextEnd(label); + float w_full = CalcItemWidth(); g.NextItemData.ClearFlags(); BeginGroup(); @@ -5069,6 +5193,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; const int components = alpha ? 4 : 3; + const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); + const float w_inputs = ImMax(w_full - w_button, 1.0f); + w_full = w_inputs + w_button; // Convert to the formats we need float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; @@ -5092,10 +5219,9 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) { // RGB/HSV 0..255 Sliders - const float w_item_one = ImMax(1.0f, IM_FLOOR((w_inputs - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components)); - const float w_item_last = ImMax(1.0f, IM_FLOOR(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components - 1))); + const float w_items = w_inputs - style.ItemInnerSpacing.x * (components - 1); - const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); + const bool hide_prefix = (IM_TRUNC(w_items / components) <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); static const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; static const char* fmt_table_int[3][4] = { @@ -5111,11 +5237,14 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag }; const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1; + float prev_split = 0.0f; for (int n = 0; n < components; n++) { if (n > 0) SameLine(0, style.ItemInnerSpacing.x); - SetNextItemWidth((n + 1 < components) ? w_item_one : w_item_last); + float next_split = IM_TRUNC(w_items * (n + 1) / components); + SetNextItemWidth(ImMax(next_split - prev_split, 1.0f)); + prev_split = next_split; // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0. if (flags & ImGuiColorEditFlags_Float) @@ -5140,7 +5269,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag else ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255)); SetNextItemWidth(w_inputs); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsUppercase)) { value_changed = true; char* p = buf; @@ -5238,7 +5367,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag // Drag and Drop Target // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) { bool accepted_drag_drop = false; if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) @@ -5303,6 +5432,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImGuiIO& io = g.IO; const float width = CalcItemWidth(); + const bool is_readonly = ((g.NextItemData.ItemFlags | g.CurrentItemFlags) & ImGuiItemFlags_ReadOnly) != 0; g.NextItemData.ClearFlags(); PushID(label); @@ -5337,7 +5467,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; - float bars_triangles_half_sz = IM_FLOOR(bars_width * 0.20f); + float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f); float backup_initial_col[4]; memcpy(backup_initial_col, col, components * sizeof(float)); @@ -5373,7 +5503,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { // Hue wheel + SV triangle logic InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); - if (IsItemActive()) + if (IsItemActive() && !is_readonly) { ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; ImVec2 current_off = g.IO.MousePos - wheel_center; @@ -5408,7 +5538,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl { // SV rectangle logic InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); - if (IsItemActive()) + if (IsItemActive() && !is_readonly) { S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1)); V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); @@ -5421,7 +5551,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl // Hue bar logic SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) + if (IsItemActive() && !is_readonly) { H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1)); value_changed = value_changed_h = true; @@ -5505,7 +5635,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) @@ -5592,7 +5722,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl float sin_hue_angle = ImSin(H * 2.0f * IM_PI); ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f); float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; - int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); + int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others. draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments); draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments); @@ -5602,13 +5732,10 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); ImVec2 uv_white = GetFontTexUvWhitePixel(); - draw_list->PrimReserve(6, 6); + draw_list->PrimReserve(3, 3); draw_list->PrimVtx(tra, uv_white, hue_color32); - draw_list->PrimVtx(trb, uv_white, hue_color32); - draw_list->PrimVtx(trc, uv_white, col_white); - draw_list->PrimVtx(tra, uv_white, 0); draw_list->PrimVtx(trb, uv_white, col_black); - draw_list->PrimVtx(trc, uv_white, 0); + draw_list->PrimVtx(trc, uv_white, col_white); draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f); sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); } @@ -5630,10 +5757,11 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl } // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) - float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; - draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, 12); + float sv_cursor_rad = value_changed_sv ? wheel_thickness * 0.55f : wheel_thickness * 0.40f; + int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others. + draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments); + draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments); // Render alpha bar if (alpha_bar) @@ -5739,7 +5867,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl } // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; @@ -5769,7 +5897,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None)) + if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) return; const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) @@ -5807,6 +5935,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; + g.LockMarkEdited++; ImGuiColorEditFlags opts = g.ColorEditOptions; if (allow_opt_inputs) { @@ -5849,6 +5978,7 @@ void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) g.ColorEditOptions = opts; EndPopup(); + g.LockMarkEdited--; } void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags) @@ -5858,6 +5988,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup("context")) return; ImGuiContext& g = *GImGui; + g.LockMarkEdited++; if (allow_opt_picker) { ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function @@ -5887,6 +6018,7 @@ void ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags fl CheckboxFlags("Alpha Bar", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); } EndPopup(); + g.LockMarkEdited--; } //------------------------------------------------------------------------- @@ -6059,42 +6191,69 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // We vertically grow up to current line height up the typical widget height. const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); + const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL); ImRect frame_bb; - frame_bb.Min.x = (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; + frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; frame_bb.Min.y = window->DC.CursorPos.y; - frame_bb.Max.x = window->WorkRect.Max.x; + frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x; frame_bb.Max.y = window->DC.CursorPos.y + frame_height; if (display_frame) { // Framed header expand a little outside the default padding, to the edge of InnerClipRect // (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f) - frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); - frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); + frame_bb.Min.x -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f); + frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f); } - const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapser arrow width + Spacing + const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapsing arrow width + Spacing const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapser + const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapsing ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); ItemSize(ImVec2(text_width, frame_height), padding.y); // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing ImRect interact_bb = frame_bb; - if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth)) == 0) + if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0) interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f; - // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. - // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). - // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. - const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; - bool is_open = TreeNodeUpdateNextOpen(id, flags); - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. + const float backup_clip_rect_min_x = window->ClipRect.Min.x; + const float backup_clip_rect_max_x = window->ClipRect.Max.x; + if (span_all_columns) + { + window->ClipRect.Min.x = window->ParentWorkRect.Min.x; + window->ClipRect.Max.x = window->ParentWorkRect.Max.x; + } + // Compute open and multi-select states before ItemAdd() as it clear NextItem data. + bool is_open = TreeNodeUpdateNextOpen(id, flags); bool item_add = ItemAdd(interact_bb, id); g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; g.LastItemData.DisplayRect = frame_bb; + if (span_all_columns) + { + window->ClipRect.Min.x = backup_clip_rect_min_x; + window->ClipRect.Max.x = backup_clip_rect_max_x; + } + + // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled: + // Store data for the current depth to allow returning to this node from any child item. + // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). + // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle. + // Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase. + if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + { + g.NavTreeNodeStack.resize(g.NavTreeNodeStack.Size + 1); + ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back(); + nav_tree_node_data->ID = id; + nav_tree_node_data->InFlags = g.LastItemData.InFlags; + nav_tree_node_data->NavRect = g.LastItemData.NavRect; + window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); + } + + const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; if (!item_add) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) @@ -6103,9 +6262,16 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l return is_open; } + if (span_all_columns) + { + TablePushBackgroundChannel(); + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; + g.LastItemData.ClipRect = window->ClipRect; + } + ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None; - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - button_flags |= ImGuiButtonFlags_AllowItemOverlap; + if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) + button_flags |= ImGuiButtonFlags_AllowOverlap; if (!is_leaf) button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; @@ -6161,11 +6327,13 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open) { toggled = true; + NavClearPreferredPosForAxis(ImGuiAxis_X); NavMoveRequestCancel(); } if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? { toggled = true; + NavClearPreferredPosForAxis(ImGuiAxis_X); NavMoveRequestCancel(); } @@ -6176,8 +6344,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen; } } - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - SetItemAllowOverlap(); // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 @@ -6185,7 +6351,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // Render const ImU32 text_col = GetColorU32(ImGuiCol_Text); - ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin; + ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact; if (display_frame) { // Framed type @@ -6195,15 +6361,14 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); else // Leaf without bullet, left-adjusted text - text_pos.x -= text_offset_x; + text_pos.x -= text_offset_x -padding.x; if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) frame_bb.Max.x -= g.FontSize + style.FramePadding.x; if (g.LogEnabled) LogSetNextTextDecoration("###", "###"); - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); } else { @@ -6217,12 +6382,20 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f); if (g.LogEnabled) LogSetNextTextDecoration(">", NULL); - RenderText(text_pos, label, label_end, false); } + if (span_all_columns) + TablePopBackgroundChannel(); + + // Label + if (display_frame) + RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); + else + RenderText(text_pos, label, label_end, false); + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) TreePushOverrideID(id); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); @@ -6264,12 +6437,14 @@ void ImGui::TreePop() ImU32 tree_depth_mask = (1 << window->DC.TreeDepth); // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled) - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) - if (g.NavIdIsAlive && (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask)) - { - SetNavID(window->IDStack.back(), g.NavLayer, 0, ImRect()); - NavMoveRequestCancel(); - } + if (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask) // Only set during request + { + ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back(); + IM_ASSERT(nav_tree_node_data->ID == window->IDStack.back()); + if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, nav_tree_node_data); + g.NavTreeNodeStack.pop_back(); + } window->DC.TreeJumpToParentOnPopMask &= tree_depth_mask - 1; IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. @@ -6321,7 +6496,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl ImGuiID id = window->GetID(label); flags |= ImGuiTreeNodeFlags_CollapsingHeader; if (p_visible) - flags |= ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton; + flags |= ImGuiTreeNodeFlags_AllowOverlap | (ImGuiTreeNodeFlags)ImGuiTreeNodeFlags_ClipLabelForTrailingButton; bool is_open = TreeNodeBehavior(id, flags, label); if (p_visible != NULL) { @@ -6331,8 +6506,8 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl ImGuiContext& g = *GImGui; ImGuiLastItemData last_item_backup = g.LastItemData; float button_size = g.FontSize; - float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x * 2.0f - button_size); - float button_y = g.LastItemData.Rect.Min.y; + float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size); + float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y; ImGuiID close_button_id = GetIDWithSeed("#CLOSE", NULL, id); if (CloseButton(close_button_id, ImVec2(button_x, button_y))) *p_visible = false; @@ -6350,7 +6525,7 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFl // Tip: pass a non-visible label (e.g. "##hello") then you can use the space to draw other text or image. // But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id. -// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowItemOverlap are also frequently used flags. +// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags. // FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported. bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) { @@ -6387,8 +6562,8 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; const float spacing_y = style.ItemSpacing.y; - const float spacing_L = IM_FLOOR(spacing_x * 0.50f); - const float spacing_U = IM_FLOOR(spacing_y * 0.50f); + const float spacing_L = IM_TRUNC(spacing_x * 0.50f); + const float spacing_U = IM_TRUNC(spacing_y * 0.50f); bb.Min.x -= spacing_L; bb.Min.y -= spacing_U; bb.Max.x += (spacing_x - spacing_L); @@ -6396,7 +6571,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl } //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); } - // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackground for every Selectable.. + // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable.. const float backup_clip_rect_min_x = window->ClipRect.Min.x; const float backup_clip_rect_max_x = window->ClipRect.Max.x; if (span_all_columns) @@ -6422,10 +6597,15 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only, // which would be advantageous since most selectable are not selected. - if (span_all_columns && window->DC.CurrentColumns) - PushColumnsBackground(); - else if (span_all_columns && g.CurrentTable) - TablePushBackgroundChannel(); + if (span_all_columns) + { + if (g.CurrentTable) + TablePushBackgroundChannel(); + else if (window->DC.CurrentColumns) + PushColumnsBackground(); + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasClipRect; + g.LastItemData.ClipRect = window->ClipRect; + } // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries ImGuiButtonFlags button_flags = 0; @@ -6434,7 +6614,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } - if (flags & ImGuiSelectableFlags_AllowItemOverlap) { button_flags |= ImGuiButtonFlags_AllowItemOverlap; } + if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; } const bool was_selected = selected; bool hovered, held; @@ -6463,9 +6643,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (pressed) MarkItemEdited(id); - if (flags & ImGuiSelectableFlags_AllowItemOverlap) - SetItemAllowOverlap(); - // In this branch, Selectable() cannot toggle the selection so this will never trigger. if (selected != was_selected) //-V547 g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; @@ -6476,12 +6653,16 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); RenderFrame(bb.Min, bb.Max, col, false, 0.0f); } - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); + if (g.NavId == id) + RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding); - if (span_all_columns && window->DC.CurrentColumns) - PopColumnsBackground(); - else if (span_all_columns && g.CurrentTable) - TablePopBackgroundChannel(); + if (span_all_columns) + { + if (g.CurrentTable) + TablePopBackgroundChannel(); + else if (window->DC.CurrentColumns) + PopColumnsBackground(); + } RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); @@ -6506,6 +6687,212 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags return false; } + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Typing-Select support +//------------------------------------------------------------------------- + +// [Experimental] Currently not exposed in public API. +// Consume character inputs and return search request, if any. +// This would typically only be called on the focused window or location you want to grab inputs for, e.g. +// if (ImGui::IsWindowFocused(...)) +// if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest()) +// focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1); +// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer). +ImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiTypingSelectState* data = &g.TypingSelectState; + ImGuiTypingSelectRequest* out_request = &data->Request; + + // Clear buffer + const float TYPING_SELECT_RESET_TIMER = 1.80f; // FIXME: Potentially move to IO config. + const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times + if (data->SearchBuffer[0] != 0) + { + bool clear_buffer = false; + clear_buffer |= (g.NavFocusScopeId != data->FocusScope); + clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time); + clear_buffer |= g.NavAnyRequest; + clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere + clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter); + clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0; + //if (clear_buffer) { IMGUI_DEBUG_LOG("GetTypingSelectRequest(): Clear SearchBuffer.\n"); } + if (clear_buffer) + data->Clear(); + } + + // Append to buffer + const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1; + int buffer_len = (int)strlen(data->SearchBuffer); + bool select_request = false; + for (ImWchar w : g.IO.InputQueueCharacters) + { + const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1); + if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks + continue; + char w_buf[5]; + ImTextCharToUtf8(w_buf, (unsigned int)w); + if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0) + { + select_request = true; // Same character: don't need to append to buffer. + continue; + } + if (data->SingleCharModeLock) + { + data->Clear(); // Different character: clear + buffer_len = 0; + } + memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append + buffer_len += w_len; + select_request = true; + } + g.IO.InputQueueCharacters.resize(0); + + // Handle backspace + if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, 0, ImGuiInputFlags_Repeat)) + { + char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len); + *p = 0; + buffer_len = (int)(p - data->SearchBuffer); + } + + // Return request if any + if (buffer_len == 0) + return NULL; + if (select_request) + { + data->FocusScope = g.NavFocusScopeId; + data->LastRequestFrame = g.FrameCount; + data->LastRequestTime = (float)g.Time; + } + out_request->Flags = flags; + out_request->SearchBufferLen = buffer_len; + out_request->SearchBuffer = data->SearchBuffer; + out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount); + out_request->SingleCharMode = false; + out_request->SingleCharSize = 0; + + // Calculate if buffer contains the same character repeated. + // - This can be used to implement a special search mode on first character. + // - Performed on UTF-8 codepoint for correctness. + // - SingleCharMode is always set for first input character, because it usually leads to a "next". + if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode) + { + const char* buf_begin = out_request->SearchBuffer; + const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen; + const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end); + const char* p = buf_begin + c0_len; + for (; p < buf_end; p += c0_len) + if (memcmp(buf_begin, p, (size_t)c0_len) != 0) + break; + const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0; + out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock); + out_request->SingleCharSize = (ImS8)c0_len; + data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode. + } + + return out_request; +} + +static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2) +{ + int match_len = 0; + while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++)) + match_len++; + return match_len; +} + +// Default handler for finding a result for typing-select. You may implement your own. +// You might want to display a tooltip to visualize the current request SearchBuffer +// When SingleCharMode is set: +// - it is better to NOT display a tooltip of other on-screen display indicator. +// - the index of the currently focused item is required. +// if your SetNextItemSelectionData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData. +int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) +{ + if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot. + return -1; + int idx = -1; + if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode)) + idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx); + else + idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data); + if (idx != -1) + NavRestoreHighlightAfterMove(); + return idx; +} + +// Special handling when a single character is repeated: perform search on a single letter and goes to next. +int ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx) +{ + // FIXME: Assume selection user data is index. Would be extremely practical. + //if (nav_item_idx == -1) + // nav_item_idx = (int)g.NavLastValidSelectionUserData; + + int first_match_idx = -1; + bool return_next_match = false; + for (int idx = 0; idx < items_count; idx++) + { + const char* item_name = get_item_name_func(user_data, idx); + if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize) + continue; + if (return_next_match) // Return next matching item after current item. + return idx; + if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value. + return idx; + if (first_match_idx == -1) // Record first match for wrapping. + first_match_idx = idx; + if (nav_item_idx == idx) // Record that we encountering nav_item so we can return next match. + return_next_match = true; + } + return first_match_idx; // First result +} + +int ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data) +{ + int longest_match_idx = -1; + int longest_match_len = 0; + for (int idx = 0; idx < items_count; idx++) + { + const char* item_name = get_item_name_func(user_data, idx); + const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name); + if (match_len <= longest_match_len) + continue; + longest_match_idx = idx; + longest_match_len = match_len; + if (match_len == req->SearchBufferLen) + break; + } + return longest_match_idx; +} + +void ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data) +{ +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + Text("SearchBuffer = \"%s\"", data->SearchBuffer); + Text("SingleCharMode = %d, Size = %d, Lock = %d", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock); + Text("LastRequest = time: %.2f, frame: %d", data->LastRequestTime, data->LastRequestFrame); +#else + IM_UNUSED(data); +#endif +} + + +//------------------------------------------------------------------------- +// [SECTION] Widgets: Multi-Select support +//------------------------------------------------------------------------- + +void ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data) +{ + // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code! + // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api. + ImGuiContext& g = *GImGui; + g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData; + g.NextItemData.SelectionUserData = selection_user_data; +} + + //------------------------------------------------------------------------- // [SECTION] Widgets: ListBox //------------------------------------------------------------------------- @@ -6514,6 +6901,7 @@ bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags // - ListBox() //------------------------------------------------------------------------- +// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. // Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty" // Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height). bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) @@ -6529,7 +6917,7 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) // Size default to hold ~7.25 items. // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - ImVec2 size = ImFloor(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f)); + ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f)); ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); @@ -6539,10 +6927,11 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) { ItemSize(bb.GetSize(), style.FramePadding.y); ItemAdd(bb, 0, &frame_bb); + g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values return false; } - // FIXME-OPT: We could omit the BeginGroup() if label_size.x but would need to omit the EndGroup() as well. + // FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well. BeginGroup(); if (label_size.x > 0.0f) { @@ -6551,24 +6940,10 @@ bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size); } - BeginChildFrame(id, frame_bb.GetSize()); + BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle); return true; } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -// OBSOLETED in 1.81 (from February 2021) -bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) -{ - // If height_in_items == -1, default height is maximum 7. - ImGuiContext& g = *GImGui; - float height_in_items_f = (height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f; - ImVec2 size; - size.x = 0.0f; - size.y = GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f; - return BeginListBox(label, size); -} -#endif - void ImGui::EndListBox() { ImGuiContext& g = *GImGui; @@ -6576,7 +6951,7 @@ void ImGui::EndListBox() IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && "Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?"); IM_UNUSED(window); - EndChildFrame(); + EndChild(); EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label } @@ -6588,7 +6963,7 @@ bool ImGui::ListBox(const char* label, int* current_item, const char* const item // This is merely a helper around BeginListBox(), EndListBox(). // Considering using those directly to submit custom data or store selection differently. -bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +bool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items) { ImGuiContext& g = *GImGui; @@ -6596,7 +6971,7 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v if (height_in_items < 0) height_in_items = ImMin(items_count, 7); float height_in_items_f = height_in_items + 0.25f; - ImVec2 size(0.0f, ImFloor(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f)); + ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f)); if (!BeginListBox(label, size)) return false; @@ -6609,8 +6984,8 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v while (clipper.Step()) for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) { - const char* item_text; - if (!items_getter(data, i, &item_text)) + const char* item_text = getter(user_data, i); + if (item_text == NULL) item_text = "*Unknown item*"; PushID(i); @@ -6664,7 +7039,7 @@ int ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_get ItemSize(total_bb, style.FramePadding.y); if (!ItemAdd(total_bb, 0, &frame_bb)) return -1; - const bool hovered = ItemHoverable(frame_bb, id); + const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags); // Determine scale from values if not specified if (scale_min == FLT_MAX || scale_max == FLT_MAX) @@ -6957,12 +7332,18 @@ void ImGui::EndMenuBar() PopClipRect(); PopID(); window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. - g.GroupStack.back().EmitItem = false; - EndGroup(); // Restore position on layer 0 + + // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here. + ImGuiGroupData& group_data = g.GroupStack.back(); + group_data.EmitItem = false; + ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos; + window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent. + EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore window->DC.LayoutType = ImGuiLayoutType_Vertical; window->DC.IsSameLine = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.MenuBarAppending = false; + window->DC.CursorMaxPos = restore_cursor_max_pos; } // Important: calling order matters! @@ -7032,7 +7413,7 @@ void ImGui::EndMainMenuBar() // FIXME: With this strategy we won't be able to restore a NULL focus. ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) - FocusTopMostWindowUnderOne(g.NavWindow, NULL); + FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); End(); } @@ -7046,9 +7427,9 @@ static bool IsRootOfOpenMenuSet() // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others // (e.g. inside menu bar vs loose menu items) based on parent ID. - // This would however prevent the use of e.g. PuhsID() user code submitting menus. + // This would however prevent the use of e.g. PushID() user code submitting menus. // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag, - // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. + // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects. // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu. // In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer. @@ -7056,7 +7437,9 @@ static bool IsRootOfOpenMenuSet() // open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart // it likely won't be a problem anyone runs into. const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; - return (window->DC.NavLayerCurrent == upper_popup->ParentNavLayer && upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu)); + if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer) + return false; + return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true); } bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) @@ -7116,15 +7499,15 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Menu inside an horizontal menu bar // Selectable extend their highlight by half ItemSpacing in each direction. // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - 1.0f - IM_FLOOR(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight()); + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); float w = label_size.x; ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); pressed = Selectable("", menu_is_open, selectable_flags, ImVec2(w, label_size.y)); RenderText(text_pos, label); PopStyleVar(); - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else { @@ -7133,7 +7516,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; - float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); @@ -7151,6 +7534,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) PopItemFlag(); bool want_open = false; + bool want_open_nav_init = false; bool want_close = false; if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) { @@ -7161,18 +7545,18 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL; if (g.HoveredWindow == window && child_menu_window != NULL) { - float ref_unit = g.FontSize; // FIXME-DPI - float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f; - ImRect next_window_rect = child_menu_window->Rect(); + const float ref_unit = g.FontSize; // FIXME-DPI + const float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f; + const ImRect next_window_rect = child_menu_window->Rect(); ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR(); ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack. + const float pad_farmost_h = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // Add a bit of extra slack. ta.x += child_dir * -0.5f; tb.x += child_dir * ref_unit; tc.x += child_dir * ref_unit; - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle has maximum height to limit the slope and the bias toward large sub-menus - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); + tb.y = ta.y + ImMax((tb.y - pad_farmost_h) - ta.y, -ref_unit * 8.0f); // Triangle has maximum height to limit the slope and the bias toward large sub-menus + tc.y = ta.y + ImMin((tc.y + pad_farmost_h) - ta.y, +ref_unit * 8.0f); moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } @@ -7180,18 +7564,22 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not) // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon. // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.) - if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover) + if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover && g.ActiveId == 0) want_close = true; // Open + // (note: at this point 'hovered' actually includes the NavDisableMouseHover == false test) if (!menu_is_open && pressed) // Click/activate to open want_open = true; else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open want_open = true; + else if (!menu_is_open && hovered && g.HoveredIdTimer >= 0.30f && g.MouseStationaryTimer >= 0.30f) // Hover to open (timer fallback) + want_open = true; if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open { - want_open = true; + want_open = want_open_nav_init = true; NavMoveRequestCancel(); + NavRestoreHighlightAfterMove(); } } else @@ -7223,13 +7611,13 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { - // Don't reopen/recycle same menu level in the same frame, first close the other menu and yield for a frame. + // Don't reopen/recycle same menu level in the same frame if it is a different menu ID, first close the other menu and yield for a frame. OpenPopup(label); } else if (want_open) { menu_is_open = true; - OpenPopup(label); + OpenPopup(label, ImGuiPopupFlags_NoReopen);// | (want_open_nav_init ? ImGuiPopupFlags_NoReopenAlwaysNavInit : 0)); } if (menu_is_open) @@ -7241,6 +7629,14 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) PopStyleVar(); if (menu_is_open) { + // Implement what ImGuiPopupFlags_NoReopenAlwaysNavInit would do: + // Perform an init request in the case the popup was already open (via a previous mouse hover) + if (want_open && want_open_nav_init && !g.NavInitRequest) + { + FocusWindow(g.CurrentWindow, ImGuiFocusRequestFlags_UnlessBelowModal); + NavInitWindow(g.CurrentWindow, false); + } + // Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu() // (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck) g.LastItemData = last_item_in_parent; @@ -7310,14 +7706,14 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark. float w = label_size.x; - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * 0.5f); + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f); ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) RenderText(text_pos, label); - window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). + window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else { @@ -7326,7 +7722,7 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system. float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f; float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f; - float checkmark_w = IM_FLOOR(g.FontSize * 1.20f); + float checkmark_w = IM_TRUNC(g.FontSize * 1.20f); float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y)); @@ -7471,6 +7867,8 @@ bool ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags) ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id); ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2); tab_bar->ID = id; + tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f); + tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f); return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused); } @@ -7481,6 +7879,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG if (window->SkipItems) return false; + IM_ASSERT(tab_bar->ID != 0); if ((flags & ImGuiTabBarFlags_DockNode) == 0) PushOverrideID(tab_bar->ID); @@ -7523,12 +7922,12 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY); // Draw separator + // (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable) const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive); - const float y = tab_bar->BarRect.Max.y - 1.0f; + if (g.Style.TabBarBorderSize > 0.0f) { - const float separator_min_x = tab_bar->BarRect.Min.x - IM_FLOOR(window->WindowPadding.x * 0.5f); - const float separator_max_x = tab_bar->BarRect.Max.x + IM_FLOOR(window->WindowPadding.x * 0.5f); - window->DrawList->AddLine(ImVec2(separator_min_x, y), ImVec2(separator_max_x, y), col, 1.0f); + const float y = tab_bar->BarRect.Max.y; + window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col); } return true; } @@ -7573,6 +7972,12 @@ void ImGui::EndTabBar() g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back()); } +// Scrolling happens only in the central section (leading/trailing sections are not scrolling) +static float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections) +{ + return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; +} + // This is called only once a frame before by the first call to ItemTab() // The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions. static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) @@ -7729,7 +8134,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++) { ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index]; - float shrinked_width = IM_FLOOR(g.ShrinkWidthBuffer[tab_n].Width); + float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width); if (shrinked_width < 0.0f) continue; @@ -7775,9 +8180,23 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->VisibleTabId = tab_bar->SelectedTabId; tab_bar->VisibleTabWasSubmitted = false; - // Update scrolling + // Apply request requests if (scroll_to_tab_id != 0) TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); + else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow)) + { + const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH; + const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX; + if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f) + { + const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f; + tab_bar->ScrollingTargetDistToVisibility = 0.0f; + tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step); + } + SetKeyOwner(wheel_key, tab_bar->ID); + } + + // Update scrolling tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim); tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget); if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget) @@ -7875,7 +8294,7 @@ void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) if (tab->Flags & ImGuiTabItemFlags_Button) return; // A button appended with TabItemButton(). - if (!(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) + if ((tab->Flags & (ImGuiTabItemFlags_UnsavedDocument | ImGuiTabItemFlags_NoAssumedClosure)) == 0) { // This will remove a frame of lag for selecting another tab on closure. // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure @@ -7914,8 +8333,7 @@ static void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGui int order = TabBarGetTabOrder(tab_bar, tab); // Scrolling happens only in the central section (leading/trailing sections are not scrolling) - // FIXME: This is all confusing. - float scrollable_width = tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing; + float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections); // We make all tabs positions all relative Sections[0].Width to make code simpler float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f); @@ -8303,7 +8721,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0; size.x = tab->Width; if (is_central_section) - window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_FLOOR(tab->Offset - tab_bar->ScrollingAnim), 0.0f); + window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f); else window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f); ImVec2 pos = window->DC.CursorPos; @@ -8327,7 +8745,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } // Click to Select a tab - ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowItemOverlap); + // Allow the close button to overlap + ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); if (g.DragDropActive) button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; bool hovered, held; @@ -8335,10 +8754,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (pressed && !is_tab_button) TabBarQueueFocus(tab_bar, tab); - // Allow the close button to overlap unless we are dragging (in which case we don't want any overlapping tabs to be hovered) - if (g.ActiveId != id) - SetItemAllowOverlap(); - // Drag and drop: re-order tabs if (held && !tab_appearing && IsMouseDragging(0)) { @@ -8360,7 +8775,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth) { // Enlarge tab display when hovering - bb.Max.x = bb.Min.x + IM_FLOOR(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f))); + bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f))); display_draw_list = GetForegroundDrawList(window); TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive)); } @@ -8403,8 +8818,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) @@ -8455,7 +8869,7 @@ void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabI IM_ASSERT(width > 0.0f); const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f)); const float y1 = bb.Min.y + 1.0f; - const float y2 = bb.Max.y - 1.0f; + const float y2 = bb.Max.y - g.Style.TabBarBorderSize; draw_list->PathLineTo(ImVec2(bb.Min.x, y2)); draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9); draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12); @@ -8506,7 +8920,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, } const float button_sz = g.FontSize; - const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x * 2.0f - button_sz), bb.Min.y); + const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y); // Close Button & Unsaved Marker // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap() @@ -8524,10 +8938,8 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, if (close_button_visible) { ImGuiLastItemData last_item_backup = g.LastItemData; - PushStyleVar(ImGuiStyleVar_FramePadding, frame_padding); if (CloseButton(close_button_id, button_pos)) close_button_pressed = true; - PopStyleVar(); g.LastItemData = last_item_backup; // Close with middle mouse button @@ -8536,7 +8948,7 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, } else if (unsaved_marker_visible) { - const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz) + g.Style.FramePadding * 2.0f); + const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz)); RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text)); } @@ -8848,7 +9260,7 @@ bool ImGui::Hotkey(const char* label, int* key, const ImVec2& ssize) return false; } - const bool isHovered = ImGui::ItemHoverable(frameBB, id); + const bool isHovered = ImGui::ItemHoverable(frameBB, id, g.LastItemData.InFlags); if (isHovered) { ImGui::SetHoveredID(id); @@ -8923,5 +9335,4 @@ bool ImGui::Hotkey(const char* label, int* key, const ImVec2& ssize) return didValueChange; } - #endif // #ifndef IMGUI_DISABLE diff --git a/r5dev/thirdparty/imgui/imstb_textedit.h b/r5dev/thirdparty/imgui/imstb_textedit.h index a8a82311..c30f1a12 100644 --- a/r5dev/thirdparty/imgui/imstb_textedit.h +++ b/r5dev/thirdparty/imgui/imstb_textedit.h @@ -2,8 +2,9 @@ // This is a slightly modified version of stb_textedit.h 1.14. // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) -// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000) +// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783) // Grep for [DEAR IMGUI] to find the changes. +// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_* // stb_textedit.h - v1.14 - public domain - Sean Barrett // Development of this library was sponsored by RAD Game Tools @@ -30,7 +31,7 @@ // DEPENDENCIES // // Uses the C runtime function 'memmove', which you can override -// by defining STB_TEXTEDIT_memmove before the implementation. +// by defining IMSTB_TEXTEDIT_memmove before the implementation. // Uses no other functions. Performs no runtime allocations. // // @@ -274,8 +275,8 @@ //// //// -#ifndef INCLUDE_STB_TEXTEDIT_H -#define INCLUDE_STB_TEXTEDIT_H +#ifndef INCLUDE_IMSTB_TEXTEDIT_H +#define INCLUDE_IMSTB_TEXTEDIT_H //////////////////////////////////////////////////////////////////////// // @@ -286,33 +287,33 @@ // and undo state. // -#ifndef STB_TEXTEDIT_UNDOSTATECOUNT -#define STB_TEXTEDIT_UNDOSTATECOUNT 99 +#ifndef IMSTB_TEXTEDIT_UNDOSTATECOUNT +#define IMSTB_TEXTEDIT_UNDOSTATECOUNT 99 #endif -#ifndef STB_TEXTEDIT_UNDOCHARCOUNT -#define STB_TEXTEDIT_UNDOCHARCOUNT 999 +#ifndef IMSTB_TEXTEDIT_UNDOCHARCOUNT +#define IMSTB_TEXTEDIT_UNDOCHARCOUNT 999 #endif -#ifndef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_CHARTYPE int +#ifndef IMSTB_TEXTEDIT_CHARTYPE +#define IMSTB_TEXTEDIT_CHARTYPE int #endif -#ifndef STB_TEXTEDIT_POSITIONTYPE -#define STB_TEXTEDIT_POSITIONTYPE int +#ifndef IMSTB_TEXTEDIT_POSITIONTYPE +#define IMSTB_TEXTEDIT_POSITIONTYPE int #endif typedef struct { // private data - STB_TEXTEDIT_POSITIONTYPE where; - STB_TEXTEDIT_POSITIONTYPE insert_length; - STB_TEXTEDIT_POSITIONTYPE delete_length; + IMSTB_TEXTEDIT_POSITIONTYPE where; + IMSTB_TEXTEDIT_POSITIONTYPE insert_length; + IMSTB_TEXTEDIT_POSITIONTYPE delete_length; int char_storage; } StbUndoRecord; typedef struct { // private data - StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; - STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; + StbUndoRecord undo_rec [IMSTB_TEXTEDIT_UNDOSTATECOUNT]; + IMSTB_TEXTEDIT_CHARTYPE undo_char[IMSTB_TEXTEDIT_UNDOCHARCOUNT]; short undo_point, redo_point; int undo_char_point, redo_char_point; } StbUndoState; @@ -371,7 +372,7 @@ typedef struct float ymin,ymax; // height of row above and below baseline int num_chars; } StbTexteditRow; -#endif //INCLUDE_STB_TEXTEDIT_H +#endif //INCLUDE_IMSTB_TEXTEDIT_H //////////////////////////////////////////////////////////////////////////// @@ -384,11 +385,11 @@ typedef struct // implementation isn't include-guarded, since it might have indirectly // included just the "header" portion -#ifdef STB_TEXTEDIT_IMPLEMENTATION +#ifdef IMSTB_TEXTEDIT_IMPLEMENTATION -#ifndef STB_TEXTEDIT_memmove +#ifndef IMSTB_TEXTEDIT_memmove #include -#define STB_TEXTEDIT_memmove memmove +#define IMSTB_TEXTEDIT_memmove memmove #endif @@ -398,7 +399,7 @@ typedef struct // // traverse the layout to locate the nearest character to a display position -static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) +static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y) { StbTexteditRow r; int n = STB_TEXTEDIT_STRINGLEN(str); @@ -458,7 +459,7 @@ static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) } // API click: on mouse down, move the cursor to the clicked location, and reset the selection -static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +static void stb_textedit_click(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) { // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse // goes off the top or bottom of the text @@ -476,7 +477,7 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat } // API drag: on mouse drag, move the cursor and selection endpoint to the clicked location -static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +static void stb_textedit_drag(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) { int p = 0; @@ -502,11 +503,11 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state // // forward declarations -static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); -static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); -static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); +static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); -static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); +static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); typedef struct { @@ -518,7 +519,7 @@ typedef struct // find the x/y location of a character, and remember info about the previous row in // case we get a move-up event (for page up, we'll have to rescan) -static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) +static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING *str, int n, int single_line) { StbTexteditRow r; int prev_start = 0; @@ -549,7 +550,10 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s i += r.num_chars; find->y += r.baseline_y_delta; if (i == z) // [DEAR IMGUI] + { + r.num_chars = 0; // [DEAR IMGUI] break; // [DEAR IMGUI] + } } find->first_char = first = i; @@ -566,7 +570,7 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s #define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) // make the selection/cursor state valid if client altered the string -static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +static void stb_textedit_clamp(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { int n = STB_TEXTEDIT_STRINGLEN(str); if (STB_TEXT_HAS_SELECTION(state)) { @@ -580,7 +584,7 @@ static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat } // delete characters while updating undo -static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) +static void stb_textedit_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) { stb_text_makeundo_delete(str, state, where, len); STB_TEXTEDIT_DELETECHARS(str, where, len); @@ -588,7 +592,7 @@ static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *sta } // delete the section -static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +static void stb_textedit_delete_selection(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { stb_textedit_clamp(str, state); if (STB_TEXT_HAS_SELECTION(state)) { @@ -625,7 +629,7 @@ static void stb_textedit_move_to_first(STB_TexteditState *state) } // move cursor to last character of selection -static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +static void stb_textedit_move_to_last(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { stb_textedit_sortselection(state); @@ -637,13 +641,13 @@ static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditStat } #ifdef STB_TEXTEDIT_IS_SPACE -static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx ) +static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx ) { return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; } #ifndef STB_TEXTEDIT_MOVEWORDLEFT -static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) +static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c ) { --c; // always move at least one character while( c >= 0 && !is_word_boundary( str, c ) ) @@ -658,7 +662,7 @@ static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) #endif #ifndef STB_TEXTEDIT_MOVEWORDRIGHT -static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c ) +static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c ) { const int len = STB_TEXTEDIT_STRINGLEN(str); ++c; // always move at least one character @@ -685,7 +689,7 @@ static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) } // API cut: delete selection -static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +static int stb_textedit_cut(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { if (STB_TEXT_HAS_SELECTION(state)) { stb_textedit_delete_selection(str,state); // implicitly clamps @@ -696,7 +700,7 @@ static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) } // API paste: replace existing selection with passed-in text -static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) +static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE *text, int len) { // if there's a selection, the paste should delete it stb_textedit_clamp(str, state); @@ -717,14 +721,14 @@ static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditSta #endif // API key: process a keyboard input -static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) +static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) { retry: switch (key) { default: { int c = STB_TEXTEDIT_KEYTOTEXT(key); if (c > 0) { - STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; + IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c; // can't add newline in single-line mode if (c == '\n' && state->single_line) @@ -889,8 +893,8 @@ retry: x = row.x0; for (i=0; i < row.num_chars; ++i) { float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); - #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE - if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) break; #endif x += dx; @@ -951,8 +955,8 @@ retry: x = row.x0; for (i=0; i < row.num_chars; ++i) { float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); - #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE - if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + #ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE) break; #endif x += dx; @@ -1109,8 +1113,8 @@ retry: static void stb_textedit_flush_redo(StbUndoState *state) { - state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; - state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; + state->redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; } // discard the oldest entry in the undo list @@ -1122,13 +1126,13 @@ static void stb_textedit_discard_undo(StbUndoState *state) int n = state->undo_rec[0].insert_length, i; // delete n characters from all other records state->undo_char_point -= n; - STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); + IMSTB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); for (i=0; i < state->undo_point; ++i) if (state->undo_rec[i].char_storage >= 0) state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it } --state->undo_point; - STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); + IMSTB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); } } @@ -1138,7 +1142,7 @@ static void stb_textedit_discard_undo(StbUndoState *state) // fill up even though the undo buffer didn't static void stb_textedit_discard_redo(StbUndoState *state) { - int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; + int k = IMSTB_TEXTEDIT_UNDOSTATECOUNT-1; if (state->redo_point <= k) { // if the k'th undo state has characters, clean those up @@ -1146,7 +1150,7 @@ static void stb_textedit_discard_redo(StbUndoState *state) int n = state->undo_rec[k].insert_length, i; // move the remaining redo character data to the end of the buffer state->redo_char_point += n; - STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + IMSTB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((IMSTB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(IMSTB_TEXTEDIT_CHARTYPE))); // adjust the position of all the other records to account for above memmove for (i=state->redo_point; i < k; ++i) if (state->undo_rec[i].char_storage >= 0) @@ -1154,12 +1158,12 @@ static void stb_textedit_discard_redo(StbUndoState *state) } // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' // [DEAR IMGUI] - size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); + size_t move_size = (size_t)((IMSTB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0])); const char* buf_begin = (char*)state->undo_rec; (void)buf_begin; const char* buf_end = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end; IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin); IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end); - STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); + IMSTB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size); // now move redo_point to point to the new one ++state->redo_point; @@ -1173,32 +1177,32 @@ static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numch // if we have no free records, we have to make room, by sliding the // existing records down - if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + if (state->undo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) stb_textedit_discard_undo(state); // if the characters to store won't possibly fit in the buffer, we can't undo - if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { + if (numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) { state->undo_point = 0; state->undo_char_point = 0; return NULL; } // if we don't have enough free characters in the buffer, we have to make room - while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) + while (state->undo_char_point + numchars > IMSTB_TEXTEDIT_UNDOCHARCOUNT) stb_textedit_discard_undo(state); return &state->undo_rec[state->undo_point++]; } -static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) +static IMSTB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) { StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); if (r == NULL) return NULL; r->where = pos; - r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len; - r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len; + r->insert_length = (IMSTB_TEXTEDIT_POSITIONTYPE) insert_len; + r->delete_length = (IMSTB_TEXTEDIT_POSITIONTYPE) delete_len; if (insert_len == 0) { r->char_storage = -1; @@ -1210,7 +1214,7 @@ static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, } } -static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +static void stb_text_undo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { StbUndoState *s = &state->undostate; StbUndoRecord u, *r; @@ -1237,7 +1241,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // characters stored for *undoing* don't leave room for redo // if the last is true, we have to bail - if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { + if (s->undo_char_point + u.delete_length >= IMSTB_TEXTEDIT_UNDOCHARCOUNT) { // the undo records take up too much character space; there's no space to store the redo characters r->insert_length = 0; } else { @@ -1246,7 +1250,7 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) // there's definitely room to store the characters eventually while (s->undo_char_point + u.delete_length > s->redo_char_point) { // should never happen: - if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) return; // there's currently not enough room, so discard a redo record stb_textedit_discard_redo(s); @@ -1278,11 +1282,11 @@ static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) s->redo_point--; } -static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +static void stb_text_redo(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state) { StbUndoState *s = &state->undostate; StbUndoRecord *u, r; - if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + if (s->redo_point == IMSTB_TEXTEDIT_UNDOSTATECOUNT) return; // we need to do two things: apply the redo record, and create an undo record @@ -1334,20 +1338,20 @@ static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int le stb_text_createundo(&state->undostate, where, 0, length); } -static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) +static void stb_text_makeundo_delete(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) { int i; - STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); + IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); if (p) { for (i=0; i < length; ++i) p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); } } -static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) +static void stb_text_makeundo_replace(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) { int i; - STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); + IMSTB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); if (p) { for (i=0; i < old_length; ++i) p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); @@ -1359,8 +1363,8 @@ static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_lin { state->undostate.undo_point = 0; state->undostate.undo_char_point = 0; - state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; - state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; + state->undostate.redo_point = IMSTB_TEXTEDIT_UNDOSTATECOUNT; + state->undostate.redo_char_point = IMSTB_TEXTEDIT_UNDOCHARCOUNT; state->select_end = state->select_start = 0; state->cursor = 0; state->has_preferred_x = 0; @@ -1383,16 +1387,16 @@ static void stb_textedit_initialize_state(STB_TexteditState *state, int is_singl #pragma GCC diagnostic ignored "-Wcast-qual" #endif -static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) +static int stb_textedit_paste(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, IMSTB_TEXTEDIT_CHARTYPE const *ctext, int len) { - return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len); + return stb_textedit_paste_internal(str, state, (IMSTB_TEXTEDIT_CHARTYPE *) ctext, len); } #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif -#endif//STB_TEXTEDIT_IMPLEMENTATION +#endif//IMSTB_TEXTEDIT_IMPLEMENTATION /* ------------------------------------------------------------------------------ diff --git a/r5dev/thirdparty/imgui/misc/cpp/README.txt b/r5dev/thirdparty/imgui/misc/cpp/README.txt index 42915902..17f0a3cd 100644 --- a/r5dev/thirdparty/imgui/misc/cpp/README.txt +++ b/r5dev/thirdparty/imgui/misc/cpp/README.txt @@ -9,5 +9,5 @@ imgui_scoped.h Try by merging: https://github.com/ocornut/imgui/pull/2197 Discuss at: https://github.com/ocornut/imgui/issues/2096 -See more C++ related extension on Wiki +See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness diff --git a/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.cpp b/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.cpp index dd6bd8a5..cf69aa89 100644 --- a/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.cpp +++ b/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.cpp @@ -4,9 +4,18 @@ // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string +// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: +// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness + #include "imgui.h" #include "imgui_stdlib.h" +// Clang warnings with -Weverything +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness +#endif + struct InputTextCallback_UserData { std::string* Str; @@ -70,3 +79,7 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string* cb_user_data.ChainCallbackUserData = user_data; return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data); } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.h b/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.h index 61afc098..835a808f 100644 --- a/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.h +++ b/r5dev/thirdparty/imgui/misc/cpp/imgui_stdlib.h @@ -4,6 +4,9 @@ // Changelog: // - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string +// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki: +// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness + #pragma once #include @@ -12,7 +15,7 @@ namespace ImGui { // ImGui::InputText() with std::string // Because text input needs dynamic resizing, we need to setup a callback to grow the capacity - IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr); + IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr); + IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr); } diff --git a/r5dev/thirdparty/imgui/misc/imgui_editor.h b/r5dev/thirdparty/imgui/misc/imgui_editor.h index ccc5d189..4f8a627e 100644 --- a/r5dev/thirdparty/imgui/misc/imgui_editor.h +++ b/r5dev/thirdparty/imgui/misc/imgui_editor.h @@ -67,7 +67,6 @@ public: // because it is rendered as " ABC" on the screen. struct Coordinates { - int m_nLine, m_nColumn; Coordinates() : m_nLine(0), m_nColumn(0) {} Coordinates(int aLine, int aColumn) : m_nLine(aLine), m_nColumn(aColumn) { @@ -117,6 +116,8 @@ public: return m_nLine > o.m_nLine; return m_nColumn >= o.m_nColumn; } + + int m_nLine, m_nColumn; }; struct Identifier @@ -219,7 +220,7 @@ public: void SetCursorPosition(const Coordinates& aPosition); inline void SetHandleMouseInputs (bool aValue){ m_bHandleMouseInputs = aValue;} - inline bool IsHandleMouseInputsEnabled() const { return m_bHandleKeyboardInputs; } + inline bool IsHandleMouseInputsEnabled() const { return m_bHandleMouseInputs; } inline void SetHandleKeyboardInputs (bool aValue){ m_bHandleKeyboardInputs = aValue;} inline bool IsHandleKeyboardInputsEnabled() const { return m_bHandleKeyboardInputs; } diff --git a/r5dev/thirdparty/imgui/misc/imgui_logger.cpp b/r5dev/thirdparty/imgui/misc/imgui_logger.cpp index dd4393d6..4e3721ed 100644 --- a/r5dev/thirdparty/imgui/misc/imgui_logger.cpp +++ b/r5dev/thirdparty/imgui/misc/imgui_logger.cpp @@ -9,7 +9,7 @@ #include "imgui_internal.h" template -bool equals(InputIt1 first1, InputIt1 last1, +static bool equals(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, BinaryPredicate p) { for (; first1 != last1 && first2 != last2; ++first1, ++first2) @@ -23,20 +23,18 @@ bool equals(InputIt1 first1, InputIt1 last1, CTextLogger::CTextLogger() : m_bAutoScroll(true) , m_bScrollToCursor(false) + , m_bScrollToStart(false) + , m_bScrolledToStart(false) , m_bScrollToBottom(true) , m_bScrolledToBottom(false) , m_bHandleUserInputs(true) , m_bWithinLoggingRect(false) - , m_bShowWhiteSpaces(false) - , m_bLinesOffsetForward(false) - , m_nLinesOffsetAmount(0) , m_nTabSize(4) , m_nLeftMargin(0) - , m_flTextStart(0.0f) , m_flLineSpacing(1.0f) , m_SelectionMode(SelectionMode::Normal) , m_flLastClick(-1.0) - , m_nStartTime(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + , m_flCursorBlinkerStartTime(-1.0f) { m_Lines.push_back(Line()); } @@ -45,7 +43,7 @@ CTextLogger::~CTextLogger() { } -std::string CTextLogger::GetText(const Coordinates & aStart, const Coordinates & aEnd) const +std::string CTextLogger::GetText(const Coordinates& aStart, const Coordinates& aEnd) const { std::string result; @@ -56,7 +54,7 @@ std::string CTextLogger::GetText(const Coordinates & aStart, const Coordinates & size_t s = 0; for (int i = lstart; i < lend; i++) - s += m_Lines[i].size(); + s += m_Lines[i].Length(); result.reserve(s + s / 8); @@ -66,9 +64,9 @@ std::string CTextLogger::GetText(const Coordinates & aStart, const Coordinates & break; const Line& line = m_Lines[lstart]; - if (istart < static_cast(line.size())) + if (istart < line.Length()) { - result += line[istart].m_Char; + result += line.buffer[istart]; istart++; } else @@ -111,7 +109,7 @@ CTextLogger::Coordinates CTextLogger::SanitizeCoordinates(const Coordinates & aV } else { - column = m_Lines.empty() ? 0 : std::min(column, GetLineMaxColumn(line)); + column = m_Lines.empty() ? 0 : ImMin(column, GetLineMaxColumn(line)); return Coordinates(line, column); } } @@ -160,17 +158,17 @@ bool UTF8StringValid(const char* pszString) return byteCount == 0; } -void CTextLogger::Advance(Coordinates & aCoordinates) const +void CTextLogger::Advance(Coordinates& aCoordinates) const { if (aCoordinates.m_nLine < static_cast(m_Lines.size())) { const Line& line = m_Lines[aCoordinates.m_nLine]; int cindex = GetCharacterIndex(aCoordinates); - if (cindex + 1 < static_cast(line.size())) + if (cindex + 1 < line.Length()) { - int delta = UTF8CharLength(line[cindex].m_Char); - cindex = std::min(cindex + delta, static_cast(line.size() - 1)); + int delta = UTF8CharLength(line.buffer[cindex]); + cindex = ImMin(cindex + delta, static_cast(line.Length() - 1)); } else { @@ -181,61 +179,61 @@ void CTextLogger::Advance(Coordinates & aCoordinates) const } } -void CTextLogger::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd) -{ - assert(aEnd >= aStart); +//void CTextLogger::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd) +//{ +// assert(aEnd >= aStart); +// +// //printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn); +// +// if (aEnd == aStart) +// return; +// +// int start = GetCharacterIndex(aStart); +// int end = GetCharacterIndex(aEnd); +// +// if (aStart.m_nLine == aEnd.m_nLine) +// { +// Line& line = m_Lines[aStart.m_nLine]; +// int n = GetLineMaxColumn(aStart.m_nLine); +// if (aEnd.m_nColumn >= n) +// line.erase(line.begin() + start, line.end()); +// else +// line.erase(line.begin() + start, line.begin() + end); +// } +// else +// { +// Line& firstLine = m_Lines[aStart.m_nLine]; +// Line& lastLine = m_Lines[aEnd.m_nLine]; +// +// firstLine.erase(firstLine.begin() + start, firstLine.end()); +// lastLine.erase(lastLine.begin(), lastLine.begin() + end); +// +// if (aStart.m_nLine < aEnd.m_nLine) +// firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); +// +// if (aStart.m_nLine < aEnd.m_nLine) +// RemoveLine(aStart.m_nLine + 1, aEnd.m_nLine + 1); +// } +//} - //printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn); - - if (aEnd == aStart) - return; - - int start = GetCharacterIndex(aStart); - int end = GetCharacterIndex(aEnd); - - if (aStart.m_nLine == aEnd.m_nLine) - { - Line& line = m_Lines[aStart.m_nLine]; - int n = GetLineMaxColumn(aStart.m_nLine); - if (aEnd.m_nColumn >= n) - line.erase(line.begin() + start, line.end()); - else - line.erase(line.begin() + start, line.begin() + end); - } - else - { - Line& firstLine = m_Lines[aStart.m_nLine]; - Line& lastLine = m_Lines[aEnd.m_nLine]; - - firstLine.erase(firstLine.begin() + start, firstLine.end()); - lastLine.erase(lastLine.begin(), lastLine.begin() + end); - - if (aStart.m_nLine < aEnd.m_nLine) - firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); - - if (aStart.m_nLine < aEnd.m_nLine) - RemoveLine(aStart.m_nLine + 1, aEnd.m_nLine + 1); - } -} - -void CTextLogger::MarkNewline(Coordinates& /* inout */ aWhere, const ImVec4& aColor, int aIndex) +void CTextLogger::MarkNewline(Coordinates& aWhere, int aIndex) { Line& newLine = InsertLine(aWhere.m_nLine + 1); Line& line = m_Lines[aWhere.m_nLine]; - if (aIndex < static_cast(m_Lines[aWhere.m_nLine].size())) + if (aIndex < static_cast(m_Lines[aWhere.m_nLine].buffer.size())) { - newLine.insert(newLine.begin(), line.begin() + aIndex, line.end()); - line.erase(line.begin() + aIndex, line.end()); + newLine.buffer.insert(newLine.buffer.begin(), line.buffer.begin() + aIndex, line.buffer.end()); + line.buffer.erase(line.buffer.begin() + aIndex, line.buffer.end()); } else - line.push_back(Glyph('\n', aColor)); + line.buffer.push_back('\n'); ++aWhere.m_nLine; aWhere.m_nColumn = 0; } -int CTextLogger::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValue, const ImVec4& aColor) +int CTextLogger::InsertTextAt(Coordinates& aWhere, const char* aValue, const ImU32 aColor) { int cindex = GetCharacterIndex(aWhere); int totalLines = 0; @@ -257,7 +255,7 @@ int CTextLogger::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValu } else if (*aValue == '\n') { - MarkNewline(aWhere, aColor, cindex); + MarkNewline(aWhere, cindex); cindex = 0; ++totalLines; ++aValue; @@ -265,9 +263,11 @@ int CTextLogger::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValu else { Line& line = m_Lines[aWhere.m_nLine]; - if (!line.empty() && ImGui::ColorConvertFloat4ToU32(aColor) != ImGui::ColorConvertFloat4ToU32(line[0].m_Color)) + + // if the buffer isn't empty, and the next log isn't from the same context, log it as a new line + if (!line.buffer.empty() && aColor != line.color) { - MarkNewline(aWhere, line[0].m_Color, cindex); + MarkNewline(aWhere, cindex); cindex = 0; ++totalLines; continue; @@ -276,19 +276,22 @@ int CTextLogger::InsertTextAt(Coordinates& /* inout */ aWhere, const char* aValu size_t d = UTF8CharLength(*aValue); while (d-- > 0 && *aValue != '\0') { - if (cindex >= 0 && cindex <= static_cast(line.size())) - line.insert(line.begin() + cindex++, Glyph(*aValue++, aColor)); + if (cindex >= 0 && cindex <= line.Length()) + line.buffer.insert(line.buffer.begin() + cindex++, *aValue++); else ++aValue; // Possibly an invalid character } + + line.color = aColor; ++aWhere.m_nColumn; } } + if (!*aValue) { Line& line = m_Lines[aWhere.m_nLine]; - if (!line.empty() && cindex >= 0 && cindex <= static_cast(line.size())) - line.insert(line.begin() + cindex, Glyph(' ', aColor)); + if (!line.buffer.empty() && cindex >= 0 && cindex <= line.Length()) + line.buffer.insert(line.buffer.begin() + cindex, ' '); } return totalLines; @@ -299,28 +302,30 @@ CTextLogger::Coordinates CTextLogger::ScreenPosToCoordinates(const ImVec2& aPosi ImVec2 origin = ImGui::GetCursorScreenPos(); ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y); - int lineNo = std::max(0, static_cast(floor(local.y / m_CharAdvance.y))); + int lineNo = ImMax(0, static_cast(ImFloor(local.y / m_CharAdvance.y))); int columnCoord = 0; if (lineNo >= 0 && lineNo < static_cast(m_Lines.size())) { - const Line& line = m_Lines.at(lineNo); + const Line& line = m_Lines[lineNo]; int columnIndex = 0; float columnX = 0.0f; - while (columnIndex < static_cast(line.size())) + while (columnIndex < line.Length()) { float columnWidth = 0.0f; - if (line[columnIndex].m_Char == '\t') + if (line.buffer[columnIndex] == '\t') { float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x; float oldX = columnX; - float newColumnX = (1.0f + std::floor((1.0f + columnX) / (float(m_nTabSize) * spaceSize))) * (float(m_nTabSize) * spaceSize); + float newColumnX = (1.0f + ImFloor((1.0f + columnX) / (float(m_nTabSize) * spaceSize))) * (float(m_nTabSize) * spaceSize); columnWidth = newColumnX - oldX; - if (m_flTextStart + columnX + columnWidth * 0.5f > local.x) + + if (columnX + columnWidth * 0.5f > local.x) break; + columnX = newColumnX; columnCoord = (columnCoord / m_nTabSize) * m_nTabSize + m_nTabSize; columnIndex++; @@ -328,14 +333,18 @@ CTextLogger::Coordinates CTextLogger::ScreenPosToCoordinates(const ImVec2& aPosi else { char buf[7]; - size_t d = UTF8CharLength(line[columnIndex].m_Char); + size_t d = UTF8CharLength(line.buffer[columnIndex]); size_t i = 0; - while (i < 6 && d-- > 0 && columnIndex < line.size()) - buf[i++] = line[columnIndex++].m_Char; + + while (i < 6 && d-- > 0 && columnIndex < line.Length()) + buf[i++] = line.buffer[columnIndex++]; + buf[i] = '\0'; columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x; - if (m_flTextStart + columnX + columnWidth * 0.5f > local.x) + + if (columnX + columnWidth * 0.5f > local.x) break; + columnX += columnWidth; columnCoord++; } @@ -354,15 +363,15 @@ CTextLogger::Coordinates CTextLogger::FindWordStart(const Coordinates & aFrom) c const Line& line = m_Lines[at.m_nLine]; int cindex = GetCharacterIndex(at); - if (cindex >= static_cast(line.size())) + if (cindex >= line.Length()) return at; - while (cindex > 0 && isspace(line[cindex].m_Char)) + while (cindex > 0 && isspace(line.buffer[cindex])) --cindex; while (cindex > 0) { - Char c = line[cindex].m_Char; + Char c = line.buffer[cindex]; if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx { if (c <= 32 && isspace(c)) @@ -386,19 +395,19 @@ CTextLogger::Coordinates CTextLogger::FindWordEnd(const Coordinates & aFrom) con const Line& line = m_Lines[at.m_nLine]; int cindex = GetCharacterIndex(at); - if (cindex >= static_cast(line.size())) + if (cindex >= line.Length()) return at; - bool prevspace = static_cast(isspace(line[cindex].m_Char)); - while (cindex < static_cast(line.size())) + bool prevspace = static_cast(isspace(line.buffer[cindex])); + while (cindex < line.Length()) { - Char c = line[cindex].m_Char; + Char c = line.buffer[cindex]; int d = UTF8CharLength(c); if (prevspace != !!isspace(c)) { if (isspace(c)) - while (cindex < static_cast(line.size()) && !isspace(line[cindex].m_Char)) + while (cindex < line.Length() && !isspace(line.buffer[cindex])) ++cindex; break; } @@ -418,10 +427,11 @@ CTextLogger::Coordinates CTextLogger::FindNextWord(const Coordinates & aFrom) co int cindex = GetCharacterIndex(aFrom); bool isword = false; bool skip = false; - if (cindex < static_cast(m_Lines[at.m_nLine].size())) + + if (cindex < static_cast(m_Lines[at.m_nLine].buffer.size())) { const Line& line = m_Lines[at.m_nLine]; - isword = isalnum(line[cindex].m_Char); + isword = isalnum(line.buffer[cindex]); skip = isword; } @@ -429,14 +439,14 @@ CTextLogger::Coordinates CTextLogger::FindNextWord(const Coordinates & aFrom) co { if (at.m_nLine >= static_cast(m_Lines.size())) { - int l = std::max(0, static_cast(m_Lines.size() - 1)); + int l = ImMax(0, static_cast(m_Lines.size() - 1)); return Coordinates(l, GetLineMaxColumn(l)); } const Line& line = m_Lines[at.m_nLine]; - if (cindex < static_cast(line.size())) + if (cindex < line.Length()) { - isword = isalnum(line[cindex].m_Char); + isword = isalnum(line.buffer[cindex]); if (isword && !skip) return Coordinates(at.m_nLine, GetCharacterColumn(at.m_nLine, cindex)); @@ -467,13 +477,13 @@ int CTextLogger::GetCharacterIndex(const Coordinates& aCoordinates) const int c = 0; int i = 0; - for (; i < static_cast(line.size()) && c < aCoordinates.m_nColumn;) + for (; i < line.Length() && c < aCoordinates.m_nColumn;) { - if (line[i].m_Char == '\t') + if (line.buffer[i] == '\t') c = (c / m_nTabSize) * m_nTabSize + m_nTabSize; else ++c; - i += UTF8CharLength(line[i].m_Char); + i += UTF8CharLength(line.buffer[i]); } return i; } @@ -487,9 +497,9 @@ int CTextLogger::GetCharacterColumn(int aLine, int aIndex) const int col = 0; int i = 0; - while (i < aIndex && i < static_cast(line.size())) + while (i < aIndex && i < line.Length()) { - Char c = line[i].m_Char; + Char c = line.buffer[i]; i += UTF8CharLength(c); if (c == '\t') col = (col / m_nTabSize) * m_nTabSize + m_nTabSize; @@ -507,8 +517,8 @@ int CTextLogger::GetLineCharacterCount(int aLine) const const Line& line = m_Lines[aLine]; int c = 0; - for (size_t i = 0; i < line.size(); c++) - i += static_cast(UTF8CharLength(m_Lines[aLine][i].m_Char)); + for (size_t i = 0; i < line.Length(); c++) + i += static_cast(UTF8CharLength(m_Lines[aLine].buffer[i])); return c; } @@ -520,9 +530,9 @@ int CTextLogger::GetLineMaxColumn(int aLine) const const Line& line = m_Lines[aLine]; int col = 0; - for (size_t i = 0; i < line.size(); ) + for (size_t i = 0; i < line.Length(); ) { - Char c = line[i].m_Char; + Char c = line.buffer[i]; if (c == '\t') col = (col / m_nTabSize) * m_nTabSize + m_nTabSize; else @@ -540,10 +550,10 @@ bool CTextLogger::IsOnWordBoundary(const Coordinates & aAt) const const Line& line = m_Lines[aAt.m_nLine]; size_t cindex = static_cast(GetCharacterIndex(aAt)); - if (cindex >= line.size()) + if (cindex >= line.Length()) return true; - return isspace(line[cindex].m_Char) != isspace(line[cindex - 1].m_Char); + return isspace(line.buffer[cindex]) != isspace(line.buffer[cindex - 1]); } void CTextLogger::RemoveLine(int aStart, int aEnd) @@ -563,6 +573,7 @@ void CTextLogger::RemoveLine(int aIndex) assert(!m_Lines.empty()); } +// TODO[ AMOS ]: rename to InsertBlankLine ? CTextLogger::Line& CTextLogger::InsertLine(int aIndex) { Line& result = *m_Lines.insert(m_Lines.begin() + aIndex, Line()); @@ -586,16 +597,16 @@ std::string CTextLogger::GetWordAt(const Coordinates & aCoords) const int iend = GetCharacterIndex(end); for (int it = istart; it < iend; ++it) - r.push_back(m_Lines[aCoords.m_nLine][it].m_Char); + r.push_back(m_Lines[aCoords.m_nLine].buffer[it]); return r; } -ImU32 CTextLogger::GetGlyphColor(const Glyph & aGlyph) const -{ - ImVec4 color = aGlyph.m_Color; - return ImGui::ColorConvertFloat4ToU32(color); -} +//ImU32 CTextLogger::GetGlyphColor(const Glyph & aGlyph) const +//{ +// ImVec4 color = aGlyph.m_Color; +// return ImGui::ColorConvertFloat4ToU32(color); +//} void CTextLogger::HandleKeyboardInputs(bool bHoveredScrollbar, bool bActiveScrollbar) { @@ -657,9 +668,10 @@ void CTextLogger::HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollba if (!bHoveredScrollbar && !bActiveScrollbar && m_bWithinLoggingRect) { + bool click = ImGui::IsMouseClicked(0); + if (!bShift && !bAlt) { - bool click = ImGui::IsMouseClicked(0); bool doubleClick = ImGui::IsMouseDoubleClicked(0); double t = ImGui::GetTime(); @@ -691,6 +703,10 @@ void CTextLogger::HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollba { m_State.m_CursorPosition = m_InteractiveStart = m_InteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + // Advance cursor to the end of the selection + m_InteractiveStart = FindWordStart(m_State.m_CursorPosition); + m_State.m_CursorPosition = m_InteractiveEnd = FindWordEnd(m_State.m_CursorPosition); + if (m_SelectionMode == SelectionMode::Line) m_SelectionMode = SelectionMode::Normal; else @@ -718,39 +734,27 @@ void CTextLogger::HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollba // Mouse left button dragging (=> update selection) else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { + m_SelectionMode = SelectionMode::Normal; io.WantCaptureMouse = true; + m_State.m_CursorPosition = m_InteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); SetSelection(m_InteractiveStart, m_InteractiveEnd, m_SelectionMode); EnsureCursorVisible(); } - // Move start position of the selection when entries have been erased/inserted - if (m_nLinesOffsetAmount && ImGui::IsMouseDown(0)) + } + else if (bShift) + { + if (click) // Shift select range { - Coordinates newStart; - newStart = m_InteractiveStart; + const Coordinates newCursorPos = ScreenPosToCoordinates(ImGui::GetMousePos()); - if (m_bLinesOffsetForward) - { - newStart.m_nLine += m_nLinesOffsetAmount; - if (newStart.m_nLine >= static_cast(m_Lines.size())) - { - newStart.m_nLine = static_cast(m_Lines.size()) - 1; - newStart.m_nColumn = GetLineMaxColumn(newStart.m_nLine); - } - } - else - { - newStart.m_nLine -= m_nLinesOffsetAmount; - if (newStart.m_nLine < 0) - { - newStart.m_nLine = 0; - newStart.m_nColumn = 0; - } - } + // Set selection from old cursor pos to new cursor pos + m_SelectionMode = SelectionMode::Normal; + SetSelection(m_State.m_CursorPosition, newCursorPos, m_SelectionMode); - m_nLinesOffsetAmount = 0; - m_InteractiveStart = newStart; + m_InteractiveStart = m_State.m_SelectionStart; + m_InteractiveEnd = m_State.m_SelectionEnd; } } } @@ -760,11 +764,13 @@ void CTextLogger::Render() { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); - ImGuiWindow* pWindow = ImGui::GetCurrentWindow(); - ImGuiID activeID = ImGui::GetActiveID(); - ImGuiID hoveredID = ImGui::GetHoveredID(); - bool bHoveredScrollbar = hoveredID && (hoveredID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_X) || hoveredID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_Y)); - bool bActiveScrollbar = activeID && (activeID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_X) || activeID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_Y)); + ImGuiWindow* const pWindow = ImGui::GetCurrentWindow(); + + const ImGuiID activeID = ImGui::GetActiveID(); + const ImGuiID hoveredID = ImGui::GetHoveredID(); + + const bool bHoveredScrollbar = hoveredID && (hoveredID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_X) || hoveredID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_Y)); + const bool bActiveScrollbar = activeID && (activeID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_X) || activeID == ImGui::GetWindowScrollbarID(pWindow, ImGuiAxis_Y)); if (m_bHandleUserInputs) { @@ -778,33 +784,27 @@ void CTextLogger::Render() const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; m_CharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * m_flLineSpacing); - assert(m_svLineBuffer.empty()); + const ImVec2 contentSize = ImGui::GetWindowContentRegionMax(); + ImDrawList* const drawList = ImGui::GetWindowDrawList(); - ImVec2 contentSize = ImGui::GetWindowContentRegionMax(); - ImDrawList* drawList = ImGui::GetWindowDrawList(); - float longest(m_flTextStart); + const ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); - ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); - float scrollX = ImGui::GetScrollX(); - float scrollY = ImGui::GetScrollY(); + float longest = 0.0f; + const float scrollY = ImGui::GetScrollY(); - int lineNo = static_cast(floor(scrollY / m_CharAdvance.y)); - int lineMax = std::max(0, std::min(static_cast(m_Lines.size()) - 1, lineNo + static_cast(floor((scrollY + contentSize.y) / m_CharAdvance.y)))); - - //m_flTextStart = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x + m_nLeftMargin; + int lineNo = static_cast(ImFloor(scrollY / m_CharAdvance.y)); + const int lineMax = ImMax(0, ImMin(static_cast(m_Lines.size()) - 1, lineNo + static_cast(ImFloor((scrollY + contentSize.y) / m_CharAdvance.y)))); if (!m_Lines.empty()) { - float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; - while (lineNo <= lineMax) { - ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * m_CharAdvance.y); - ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + m_flTextStart, lineStartScreenPos.y); + const ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * m_CharAdvance.y); + const ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x, lineStartScreenPos.y); const Line& line = m_Lines[lineNo]; - longest = std::max(m_flTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest); - int columnNo = 0; + longest = ImMax(TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest); + Coordinates lineStartCoord(lineNo, 0); Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo)); @@ -823,115 +823,71 @@ void CTextLogger::Render() if (sstart != -1 && ssend != -1 && sstart < ssend) { - ImVec2 vstart(lineStartScreenPos.x + m_flTextStart + sstart, lineStartScreenPos.y); - ImVec2 vend(lineStartScreenPos.x + m_flTextStart + ssend, lineStartScreenPos.y + m_CharAdvance.y); + const ImVec2 vstart(lineStartScreenPos.x + sstart, lineStartScreenPos.y); + const ImVec2 vend(lineStartScreenPos.x + ssend, lineStartScreenPos.y + m_CharAdvance.y); + drawList->AddRectFilled(vstart, vend, ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); } - if (m_State.m_CursorPosition.m_nLine == lineNo) + // Render the cursor + if (m_State.m_CursorPosition.m_nLine == lineNo && ImGui::IsWindowFocused()) { - bool focused = ImGui::IsWindowFocused(); - ImVec2 start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); + // Initialize the cursor start render time, this is done here + // as Dear ImGui typically isn't initialized during the + // construction of this class + if (m_flCursorBlinkerStartTime == -1.0) + m_flCursorBlinkerStartTime = ImGui::GetTime(); - // Render the cursor - if (focused) + const double currTime = ImGui::GetTime(); + const double elapsed = currTime - m_flCursorBlinkerStartTime; + + if (elapsed > 0.4) { - auto timeEnd = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - auto elapsed = timeEnd - m_nStartTime; - if (elapsed > 400) - { - float width = 1.0f; - float cx = TextDistanceToLineStart(m_State.m_CursorPosition); + const float width = 1.0f; + const float cx = TextDistanceToLineStart(m_State.m_CursorPosition); - const ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); - const ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + m_CharAdvance.y); + const ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); + const ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + m_CharAdvance.y); - drawList->AddRectFilled(cstart, cend, 0xffe0e0e0); - if (elapsed > 800) - m_nStartTime = timeEnd; - } + drawList->AddRectFilled(cstart, cend, 0xffe0e0e0); + + if (elapsed > 0.8) + m_flCursorBlinkerStartTime = currTime; } } - ImVec2 bufferOffset; - for (size_t i = 0; i < line.size();) + if (!line.buffer.empty()) { - const Glyph& glyph = line[i]; - ImU32 color = 0xff605040; + ImU32 color = line.color; if (m_itFilter.IsActive()) { - if (m_itFilter.PassFilter(GetTextFromLine(line).c_str())) - color = GetGlyphColor(glyph); - } - else - color = GetGlyphColor(glyph); - - if ((glyph.m_Char == '\t' || glyph.m_Char == '\n' || glyph.m_Char == ' ') && !m_svLineBuffer.empty()) - { - const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); - const ImVec2 textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, m_svLineBuffer.c_str(), nullptr, nullptr); - - drawList->AddText(newOffset, color, m_svLineBuffer.c_str()); - bufferOffset.x += textSize.x; - m_svLineBuffer.clear(); + // Make line dark if it isn't found by the filter + if (!m_itFilter.PassFilter(line.buffer.c_str())) + color = 0xff605040; } - if (glyph.m_Char == '\t' || glyph.m_Char == '\n') - { - float oldX = bufferOffset.x; - bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(m_nTabSize) * spaceSize))) * (float(m_nTabSize) * spaceSize); - ++i; - - if (m_bShowWhiteSpaces) - { - const float s = ImGui::GetFontSize(); - const float x1 = textScreenPos.x + oldX + 1.0f; - const float x2 = textScreenPos.x + bufferOffset.x - 1.0f; - const float y = textScreenPos.y + bufferOffset.y + s * 0.5f; - const ImVec2 p1(x1, y); - const ImVec2 p2(x2, y); - const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f); - const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f); - drawList->AddLine(p1, p2, 0x90909090); - drawList->AddLine(p2, p3, 0x90909090); - drawList->AddLine(p2, p4, 0x90909090); - } - } - else if (glyph.m_Char == ' ') - { - if (m_bShowWhiteSpaces) - { - const float s = ImGui::GetFontSize(); - const float x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f; - const float y = textScreenPos.y + bufferOffset.y + s * 0.5f; - drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4); - } - bufferOffset.x += spaceSize; - i++; - } - else - { - size_t l = UTF8CharLength(glyph.m_Char); - while (l-- > 0 && i < line.size()) - m_svLineBuffer.push_back(line[i++].m_Char); - } - ++columnNo; - } - - if (!m_svLineBuffer.empty()) - { - const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); - drawList->AddText(newOffset, 0xffffffff, m_svLineBuffer.c_str()); // COLOR (obtain from glyph?) - m_svLineBuffer.clear(); + const ImVec2 newOffset(textScreenPos.x, textScreenPos.y); + drawList->AddText(newOffset, color, line.buffer.c_str()); } ++lineNo; } } - + // This dummy is here to let Dear ImGui know where the last character of + // the line had ended, so that it could properly process the horizontal + // scrollbar ImGui::Dummy(ImVec2((longest + 2), m_Lines.size() * m_CharAdvance.y)); + + m_bScrolledToStart = ImGui::GetScrollX() == 0.0f; + + if (m_bScrollToStart || (m_bAutoScroll && m_bScrolledToStart && !m_bScrollToCursor)) + { + ImGui::SetScrollHereX(0.0f); + m_bScrollToStart = false; + } + m_bScrolledToBottom = ImGui::GetScrollY() >= ImGui::GetScrollMaxY(); if (m_bScrollToBottom || (m_bAutoScroll && m_bScrolledToBottom && !m_bScrollToCursor)) @@ -939,6 +895,7 @@ void CTextLogger::Render() ImGui::SetScrollHereY(1.0f); m_bScrollToBottom = false; } + m_bScrollToCursor = false; if (m_bHandleUserInputs) @@ -957,12 +914,8 @@ void CTextLogger::Copy(bool aCopyAll) { if (!m_Lines.empty()) { - std::string str; const Line& line = m_Lines[GetActualCursorCoordinates().m_nLine]; - for (const Glyph& g : line) - str.push_back(g.m_Char); - - ImGui::SetClipboardText(str.c_str()); + ImGui::SetClipboardText(line.buffer.c_str()); } } else // Copy all lines to clipboard. @@ -970,82 +923,52 @@ void CTextLogger::Copy(bool aCopyAll) std::string str; for (const Line& line : m_Lines) { - for (const Glyph& g : line) - str.push_back(g.m_Char); + str.append(line.buffer); } + ImGui::SetClipboardText(str.c_str()); } } -void CTextLogger::SetText(const ConLog_t& aText) -{ - m_Lines.clear(); - m_Lines.emplace_back(Line()); - for (char chr : aText.m_svConLog) - { - if (chr == '\r') // ignore the carriage return character - continue; - else if (chr == '\n') - m_Lines.emplace_back(Line()); - else - m_Lines.back().emplace_back(Glyph(chr, aText.m_imColor)); - } -} +//void CTextLogger::SetText(const ConLog_t& aText) +//{ +// m_Lines.clear(); +// m_Lines.emplace_back(Line()); +// for (char chr : aText.m_svConLog) +// { +// if (chr == '\r') // ignore the carriage return character +// continue; +// else if (chr == '\n') +// m_Lines.emplace_back(Line()); +// else +// m_Lines.back().emplace_back(Glyph(chr, aText.m_imColor)); +// } +//} -void CTextLogger::SetTextLines(const std::vector& aLines) -{ - m_Lines.clear(); - - if (aLines.empty()) - { - m_Lines.emplace_back(Line()); - } - else - { - m_Lines.resize(aLines.size()); - - for (size_t i = 0; i < aLines.size(); ++i) - { - const std::string & aLine = aLines[i].m_svConLog; - - m_Lines[i].reserve(aLine.size()); - for (size_t j = 0; j < aLine.size(); ++j) - { - m_Lines[i].emplace_back(Glyph(aLine[j], aLines[i].m_imColor)); - } - } - } -} - -void CTextLogger::MoveCursor(int aLines, bool aForward) -{ - Coordinates newStart; - - if (aForward) - { - newStart = m_State.m_CursorPosition; - newStart.m_nLine += aLines; - - if (newStart.m_nLine >= static_cast(m_Lines.size())) - { - newStart.m_nLine = static_cast(m_Lines.size()) - 1; - newStart.m_nColumn = GetLineMaxColumn(newStart.m_nLine); - } - } - else - { - newStart = m_State.m_CursorPosition; - newStart.m_nLine -= aLines; - - if (newStart.m_nLine < 0) - { - newStart.m_nLine = 0; - newStart.m_nColumn = 0; - } - } - - m_State.m_CursorPosition = newStart; -} +//void CTextLogger::SetTextLines(const std::vector& aLines) +//{ +// m_Lines.clear(); +// +// if (aLines.empty()) +// { +// m_Lines.emplace_back(Line()); +// } +// else +// { +// m_Lines.resize(aLines.size()); +// +// for (size_t i = 0; i < aLines.size(); ++i) +// { +// const std::string & aLine = aLines[i].m_svConLog; +// +// m_Lines[i].reserve(aLine.size()); +// for (size_t j = 0; j < aLine.size(); ++j) +// { +// m_Lines[i].emplace_back(Glyph(aLine[j], aLines[i].m_imColor)); +// } +// } +// } +//} void CTextLogger::SetCursorPosition(const Coordinates & aPosition) { @@ -1060,14 +983,14 @@ void CTextLogger::SetSelectionStart(const Coordinates & aPosition) { m_State.m_SelectionStart = SanitizeCoordinates(aPosition); if (m_State.m_SelectionStart > m_State.m_SelectionEnd) - std::swap(m_State.m_SelectionStart, m_State.m_SelectionEnd); + ImSwap(m_State.m_SelectionStart, m_State.m_SelectionEnd); } void CTextLogger::SetSelectionEnd(const Coordinates & aPosition) { m_State.m_SelectionEnd = SanitizeCoordinates(aPosition); if (m_State.m_SelectionStart > m_State.m_SelectionEnd) - std::swap(m_State.m_SelectionStart, m_State.m_SelectionEnd); + ImSwap(m_State.m_SelectionStart, m_State.m_SelectionEnd); } void CTextLogger::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode) @@ -1077,8 +1000,9 @@ void CTextLogger::SetSelection(const Coordinates & aStart, const Coordinates & a m_State.m_SelectionStart = SanitizeCoordinates(aStart); m_State.m_SelectionEnd = SanitizeCoordinates(aEnd); + if (m_State.m_SelectionStart > m_State.m_SelectionEnd) - std::swap(m_State.m_SelectionStart, m_State.m_SelectionEnd); + ImSwap(m_State.m_SelectionStart, m_State.m_SelectionEnd); switch (aMode) { @@ -1096,7 +1020,8 @@ void CTextLogger::SetSelection(const Coordinates & aStart, const Coordinates & a const int lineNo = m_State.m_SelectionEnd.m_nLine; //const size_t lineSize = (size_t)lineNo < m_Lines.size() ? m_Lines[lineNo].size() : 0; m_State.m_SelectionStart = Coordinates(m_State.m_SelectionStart.m_nLine, 0); - m_State.m_SelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo)); + m_State.m_SelectionEnd = m_Lines.size() > lineNo + 1 ? Coordinates(lineNo + 1, 0) : Coordinates(lineNo, GetLineMaxColumn(lineNo)); + m_State.m_CursorPosition = m_State.m_SelectionEnd; break; } default: @@ -1106,26 +1031,28 @@ void CTextLogger::SetSelection(const Coordinates & aStart, const Coordinates & a void CTextLogger::SetTabSize(int aValue) { - m_nTabSize = std::max(0, std::min(32, aValue)); + m_nTabSize = ImMax(0, ImMin(32, aValue)); } -void CTextLogger::InsertText(const ConLog_t & aValue) +void CTextLogger::InsertText(const char* const text, const ImU32 color) { - if (!aValue.m_svConLog.empty()) - { - Coordinates pos = GetActualLastLineCoordinates(); + IM_ASSERT(text); - const Coordinates &start = std::min(pos, m_State.m_SelectionStart); - int totalLines = pos.m_nLine - start.m_nLine; + if (!*text) + return; - totalLines += InsertTextAt(pos, aValue.m_svConLog.c_str(), aValue.m_imColor); - } + Coordinates pos = GetActualLastLineCoordinates(); + + const Coordinates& start = ImMin(pos, m_State.m_SelectionStart); + int totalLines = pos.m_nLine - start.m_nLine; + + totalLines += InsertTextAt(pos, text, color); } void CTextLogger::MoveUp(int aAmount, bool aSelect) { const Coordinates oldPos = m_State.m_CursorPosition; - m_State.m_CursorPosition.m_nLine = std::max(0, m_State.m_CursorPosition.m_nLine - aAmount); + m_State.m_CursorPosition.m_nLine = ImMax(0, m_State.m_CursorPosition.m_nLine - aAmount); if (oldPos != m_State.m_CursorPosition) { if (aSelect) @@ -1143,7 +1070,7 @@ void CTextLogger::MoveUp(int aAmount, bool aSelect) else m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition; - SetSelection(m_InteractiveStart, m_InteractiveEnd); + SetSelection(m_InteractiveStart, m_InteractiveEnd, SelectionMode::Normal); EnsureCursorVisible(); } } @@ -1153,7 +1080,7 @@ void CTextLogger::MoveDown(int aAmount, bool aSelect) assert(m_State.m_CursorPosition.m_nColumn >= 0); Coordinates oldPos = m_State.m_CursorPosition; - m_State.m_CursorPosition.m_nLine = std::max(0, std::min(static_cast(m_Lines.size() - 1), m_State.m_CursorPosition.m_nLine + aAmount)); + m_State.m_CursorPosition.m_nLine = ImMax(0, ImMin(static_cast(m_Lines.size() - 1), m_State.m_CursorPosition.m_nLine + aAmount)); if (m_State.m_CursorPosition != oldPos) { @@ -1172,7 +1099,7 @@ void CTextLogger::MoveDown(int aAmount, bool aSelect) else m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition; - SetSelection(m_InteractiveStart, m_InteractiveEnd); + SetSelection(m_InteractiveStart, m_InteractiveEnd, SelectionMode::Normal); EnsureCursorVisible(); } } @@ -1201,7 +1128,7 @@ void CTextLogger::MoveLeft(int aAmount, bool aSelect, bool aWordMode) { --line; if (static_cast(m_Lines.size()) > line) - cindex = static_cast(m_Lines[line].size()); + cindex = static_cast(m_Lines[line].buffer.size()); else cindex = 0; } @@ -1215,11 +1142,11 @@ void CTextLogger::MoveLeft(int aAmount, bool aSelect, bool aWordMode) { const Line &lineData = m_Lines[line]; - while (cindex > 0 && IsUTFSequence(lineData[cindex].m_Char)) + while (cindex > 0 && IsUTFSequence(lineData.buffer[cindex])) --cindex; // Skip the newline character. - if (cindex > 0 && lineData[cindex].m_Char == '\n') + if (cindex > 0 && lineData.buffer[cindex] == '\n') --cindex; } } @@ -1249,7 +1176,13 @@ void CTextLogger::MoveLeft(int aAmount, bool aSelect, bool aWordMode) } } else + { + if (HasSelection() && !aWordMode) + m_State.m_CursorPosition = m_InteractiveStart; + m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition; + } + SetSelection(m_InteractiveStart, m_InteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); EnsureCursorVisible(); @@ -1269,18 +1202,18 @@ void CTextLogger::MoveRight(int aAmount, bool aSelect, bool aWordMode) const Line& line = m_Lines[lindex]; bool isNewLine = false; - const bool isLastChar = (cindex >= static_cast(line.size())-1); + const bool isLastChar = (cindex >= line.Length()-1); // If the cursor is at the last character before the newline character, // we want to skip the newline character and move to the next line. - if (isLastChar && !line.empty()) - isNewLine = line.back().m_Char == '\n'; + if (isLastChar && !line.buffer.empty()) + isNewLine = line.buffer.back() == '\n'; - if (cindex >= static_cast(line.size()) || isNewLine) + if (cindex >= line.Length() || isNewLine) { if (m_State.m_CursorPosition.m_nLine < static_cast(m_Lines.size()) - 1) { - m_State.m_CursorPosition.m_nLine = std::max(0, std::min(static_cast(m_Lines.size()) - 1, m_State.m_CursorPosition.m_nLine + 1)); + m_State.m_CursorPosition.m_nLine = ImMax(0, ImMin(static_cast(m_Lines.size()) - 1, m_State.m_CursorPosition.m_nLine + 1)); m_State.m_CursorPosition.m_nColumn = 0; } else @@ -1288,10 +1221,10 @@ void CTextLogger::MoveRight(int aAmount, bool aSelect, bool aWordMode) } else { - cindex += UTF8CharLength(line[cindex].m_Char); + cindex += UTF8CharLength(line.buffer[cindex]); m_State.m_CursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); if (aWordMode) - m_State.m_CursorPosition = FindNextWord(m_State.m_CursorPosition); + m_State.m_CursorPosition = FindWordEnd(m_State.m_CursorPosition); } } @@ -1308,7 +1241,12 @@ void CTextLogger::MoveRight(int aAmount, bool aSelect, bool aWordMode) } } else + { + if (HasSelection() && !aWordMode) + m_State.m_CursorPosition = m_InteractiveEnd; + m_InteractiveStart = m_InteractiveEnd = m_State.m_CursorPosition; + } SetSelection(m_InteractiveStart, m_InteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); EnsureCursorVisible(); @@ -1414,62 +1352,82 @@ bool CTextLogger::HasSelection() const return m_State.m_SelectionEnd > m_State.m_SelectionStart; } -void CTextLogger::MoveSelection(int aLines, bool aForward) +void CTextLogger::MoveCoordsInternal(Coordinates& coordOut, int aLines, bool aForward) { assert(aLines > 0); if (aLines < 1) return; - m_bLinesOffsetForward = aForward; - m_nLinesOffsetAmount = aLines; + if (aForward) + { + coordOut.m_nLine += aLines; + + if (coordOut.m_nLine >= static_cast(m_Lines.size())) + { + coordOut.m_nLine = static_cast(m_Lines.size()) - 1; + coordOut.m_nColumn = GetLineMaxColumn(coordOut.m_nLine); + } + } + else + { + coordOut.m_nLine -= aLines; + + if (coordOut.m_nLine < 0) + { + coordOut.m_nLine = 0; + coordOut.m_nColumn = 0; + } + } +} + +// If you click on line 100, and the first line gets removed from the vector, +// the line you clicked will now be 99. This function corrects the selection +// again after the adjustments to the vector +void CTextLogger::MoveSelection(int aLines, bool aForward) +{ + // This always has to be called as interactives are the start/end pos of a + // mouse click, which gets cached off. + MoveInteractives(aLines, aForward); if (HasSelection()) { - Coordinates newStart; - Coordinates newEnd; + Coordinates newCoords[2]; - newStart = m_State.m_SelectionStart; - newEnd = m_State.m_SelectionEnd; + newCoords[0] = m_State.m_SelectionStart; + newCoords[1] = m_State.m_SelectionEnd; - if (aForward) + for (size_t i = 0; i < IM_ARRAYSIZE(newCoords); i++) { - newStart.m_nLine += aLines; - newEnd.m_nLine += aLines; - - if (newStart.m_nLine >= static_cast(m_Lines.size())) - { - newStart.m_nLine = static_cast(m_Lines.size()) - 1; - newStart.m_nColumn = GetLineMaxColumn(newStart.m_nLine); - } - if (newEnd.m_nLine >= static_cast(m_Lines.size())) - { - newEnd.m_nLine = static_cast(m_Lines.size()) - 1; - newEnd.m_nColumn = GetLineMaxColumn(newEnd.m_nLine); - } - } - else - { - newStart.m_nLine -= aLines; - newEnd.m_nLine -= aLines; - - if (newStart.m_nLine < 0) - { - newStart.m_nLine = 0; - newStart.m_nColumn = 0; - } - if (newEnd.m_nLine < 0) - { - newEnd.m_nLine = 0; - newEnd.m_nColumn = 0; - } + MoveCoordsInternal(newCoords[i], aLines, aForward); } - SetSelectionStart(newStart); - SetSelectionEnd(newEnd); + SetSelectionStart(newCoords[0]); + SetSelectionEnd(newCoords[1]); } } +void CTextLogger::MoveInteractives(int aLines, bool aForward) +{ + Coordinates newCoords[2]; + + newCoords[0] = m_InteractiveStart; + newCoords[1] = m_InteractiveEnd; + + for (size_t i = 0; i < IM_ARRAYSIZE(newCoords); i++) + { + MoveCoordsInternal(newCoords[i], aLines, aForward); + } + + m_InteractiveStart = newCoords[0]; + m_InteractiveEnd = newCoords[1]; +} + +void CTextLogger::MoveCursor(int aLines, bool aForward) +{ + MoveCoordsInternal(m_State.m_CursorPosition, aLines, aForward); +} + std::string CTextLogger::GetText() const { return GetText(Coordinates(), Coordinates(static_cast(m_Lines.size()), 0)); @@ -1482,13 +1440,7 @@ std::vector CTextLogger::GetTextLines() const for (const Line& line : m_Lines) { - std::string text; - text.resize(line.size()); - - for (size_t i = 0; i < line.size(); ++i) - text[i] = line[i].m_Char; - - result.emplace_back(std::move(text)); + result.emplace_back(line.buffer); } return result; @@ -1507,57 +1459,28 @@ std::string CTextLogger::GetCurrentLineText()const Coordinates(m_State.m_CursorPosition.m_nLine, lineLength)); } -std::string CTextLogger::GetTextFromLine(const Line& aLine) const -{ - std::string result; - for (const Glyph& glyph : aLine) - result.push_back(glyph.m_Char); - - return result; -} - -int CTextLogger::GetTotalFilterMatches() const -{ - if (!m_itFilter.IsActive()) - return static_cast(m_Lines.size()); - - int result = 0; - for (size_t i = 0; i < m_Lines.size(); i++) - { - std::string svLineBuffer; - for (size_t j = 0; j < m_Lines[i].size(); j++) - { - svLineBuffer += m_Lines[i][j].m_Char; - } - if (m_itFilter.PassFilter(svLineBuffer.c_str())) - { - result++; - } - } - return result; -} - float CTextLogger::TextDistanceToLineStart(const Coordinates& aFrom) const { const Line& line = m_Lines[aFrom.m_nLine]; float distance = 0.0f; float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; int colIndex = GetCharacterIndex(aFrom); - for (size_t it = 0u; it < line.size() && it < colIndex; ) + + for (size_t it = 0u; it < line.Length() && it < colIndex; ) { - if (line[it].m_Char == '\t') + if (line.buffer[it] == '\t') { - distance = (1.0f + std::floor((1.0f + distance) / (float(m_nTabSize) * spaceSize))) * (float(m_nTabSize) * spaceSize); + distance = (1.0f + ImFloor((1.0f + distance) / (float(m_nTabSize) * spaceSize))) * (float(m_nTabSize) * spaceSize); ++it; } else { - size_t d = UTF8CharLength(line[it].m_Char); + size_t d = UTF8CharLength(line.buffer[it]); size_t i = 0; char tempCString[7]; - for (; i < 6 && d-- > 0 && it < line.size(); i++, it++) - tempCString[i] = line[it].m_Char; + for (; i < 6 && d-- > 0 && it < line.Length(); i++, it++) + tempCString[i] = line.buffer[it]; tempCString[i] = '\0'; distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x; @@ -1570,32 +1493,39 @@ float CTextLogger::TextDistanceToLineStart(const Coordinates& aFrom) const void CTextLogger::EnsureCursorVisible() { m_bScrollToCursor = true; - Coordinates pos = GetActualCursorCoordinates(); + const Coordinates pos = GetActualCursorCoordinates(); - float scrollX = ImGui::GetScrollX(); - float scrollY = ImGui::GetScrollY(); + const float scrollX = ImGui::GetScrollX(); + const float scrollY = ImGui::GetScrollY(); - float width = ImGui::GetWindowWidth(); - float height = ImGui::GetWindowHeight(); + const float width = ImGui::GetWindowWidth(); + const float height = ImGui::GetWindowHeight(); - int top = 1 + static_cast(ceil(scrollY / m_CharAdvance.y)); - int bottom = static_cast(ceil((scrollY + height) / m_CharAdvance.y)); + const float top = ImCeil(scrollY / m_CharAdvance.y); + const float bottom = ImCeil((scrollY + height) / m_CharAdvance.y); - int left = static_cast(ceil(scrollX / m_CharAdvance.x)); - int right = static_cast(ceil((scrollX + width) / m_CharAdvance.x)); + const float left = ImCeil(scrollX / m_CharAdvance.x); + const float right = ImCeil((scrollX + width) / m_CharAdvance.x); + + const ImGuiWindow* const window = ImGui::GetCurrentWindow(); + + // For right offset, +.1f as otherwise it would render right below the + // first pixel making up the right perimeter. + const float rightOffsetAmount = window->ScrollbarY ? 3.1f : 1.1f; + const float bottomOffsetAmount = window->ScrollbarX ? 3.f : 2.f; if (pos.m_nColumn < left) - ImGui::SetScrollX(std::max(0.0f, (pos.m_nColumn) * m_CharAdvance.x)); - if (pos.m_nColumn > right - 3) - ImGui::SetScrollX(std::max(0.0f, (pos.m_nColumn + 3) * m_CharAdvance.x - width)); + ImGui::SetScrollX(ImMax(0.0f, (pos.m_nColumn) * m_CharAdvance.x)); + if (pos.m_nColumn > right - rightOffsetAmount) + ImGui::SetScrollX(ImMax(0.0f, (pos.m_nColumn + rightOffsetAmount-1.f) * m_CharAdvance.x - width)); if (pos.m_nLine < top) - ImGui::SetScrollY(std::max(0.0f, (pos.m_nLine) * m_CharAdvance.y)); - if (pos.m_nLine > bottom - 2) - ImGui::SetScrollY(std::max(0.0f, (pos.m_nLine + 2) * m_CharAdvance.y - height)); + ImGui::SetScrollY(ImMax(0.0f, (pos.m_nLine) * m_CharAdvance.y)); + if (pos.m_nLine > bottom - bottomOffsetAmount) + ImGui::SetScrollY(ImMax(0.0f, (pos.m_nLine + bottomOffsetAmount-1.f) * m_CharAdvance.y - height)); } int CTextLogger::GetPageSize() const { float height = ImGui::GetWindowHeight() - 20.0f; - return static_cast(floor(height / m_CharAdvance.y)); + return static_cast(ImFloor(height / m_CharAdvance.y)); } diff --git a/r5dev/thirdparty/imgui/misc/imgui_logger.h b/r5dev/thirdparty/imgui/misc/imgui_logger.h index 1bf207fe..af82d45c 100644 --- a/r5dev/thirdparty/imgui/misc/imgui_logger.h +++ b/r5dev/thirdparty/imgui/misc/imgui_logger.h @@ -12,17 +12,6 @@ #include #include "imgui.h" -struct ConLog_t -{ - ConLog_t(const std::string& svConLog, const ImVec4& imColor) - { - m_svConLog = svConLog; - m_imColor = imColor; - } - std::string m_svConLog; - ImVec4 m_imColor; -}; - class CTextLogger { public: @@ -98,15 +87,34 @@ public: typedef std::string String; typedef uint8_t Char; - struct Glyph - { - Char m_Char; - ImVec4 m_Color = ImVec4(0.23f, 0.47f, 0.85f, 1.00f); + //struct Glyph + //{ + // Char m_Char; + // ImVec4 m_Color = ImVec4(0.23f, 0.47f, 0.85f, 1.00f); - Glyph(Char aChar, ImVec4 aColor = ImVec4(0.80f, 0.80f, 0.80f, 1.00f)) : m_Char(aChar), m_Color(aColor) {} + // Glyph(Char aChar, ImVec4 aColor = ImVec4(0.80f, 0.80f, 0.80f, 1.00f)) : m_Char(aChar), m_Color(aColor) {} + //}; + + //typedef std::vector Line; + + + + struct Line + { + std::string buffer; + ImU32 color; + + inline int Length() const + { + return static_cast(buffer.size()); + } + + Line(const char* const text = "", const ImU32 col = 0xFFFFFFFF) + : buffer(text) + , color(col) + {} }; - typedef std::vector Line; typedef std::vector Lines; CTextLogger(); @@ -115,41 +123,35 @@ public: void Render(); void Copy(bool aCopyAll = false); - void SetText(const ConLog_t& aText); + //void SetText(const ConLog_t& aText); std::string GetText() const; - void SetTextLines(const std::vector& aLines); + //void SetTextLines(const std::vector& aLines); std::vector GetTextLines() const; ImGuiTextFilter& GetFilter() { return m_itFilter; }; std::string GetSelectedText() const; std::string GetCurrentLineText() const; - std::string GetTextFromLine(const Line& aLine) const; - int GetTotalFilterMatches() const; int GetTotalLines() const { return (int)m_Lines.size(); } - Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); } - - void MoveCursor(int aLines, bool aForward = true); void SetCursorPosition(const Coordinates& aPosition); inline void SetHandleUserInputs (bool aValue){ m_bHandleUserInputs = aValue;} inline bool IsHandleUserInputs() const { return m_bHandleUserInputs; } - inline void SetShowWhitespaces(bool aValue) { m_bShowWhiteSpaces = aValue; } - inline bool IsShowingWhitespaces() const { return m_bShowWhiteSpaces; } + inline void ShouldScrollToStart(const bool aValue) { m_bScrollToStart = aValue; } + inline bool IsScrollingToStart() const { return m_bScrollToStart; } + inline bool IsScrolledToStart() const { return m_bScrolledToStart; } - inline void ShouldScrollToBottom(bool aValue) { m_bScrollToBottom = aValue; } + inline void ShouldScrollToBottom(const bool aValue) { m_bScrollToBottom = aValue; } inline bool IsScrollingToBottom() const { return m_bScrollToBottom; } - - inline void SetScrolledToBottom(bool aValue) { m_bScrolledToBottom = aValue; } inline bool IsScrolledToBottom() const { return m_bScrolledToBottom; } void SetTabSize(int aValue); inline int GetTabSize() const { return m_nTabSize; } - void InsertText(const ConLog_t& aValue); + void InsertText(const char* const text, const ImU32 color); void MoveUp(int aAmount = 1, bool aSelect = false); void MoveDown(int aAmount = 1, bool aSelect = false); @@ -166,7 +168,12 @@ public: void SelectWordUnderCursor(); void SelectAll(); bool HasSelection() const; + + void MoveCoordsInternal(Coordinates& coordOut, int aLines, bool aForward = true); + + void MoveCursor(int aLines, bool aForward = true); void MoveSelection(int aLines, bool aForward = true); + void MoveInteractives(int aLines, bool aForward = true); void RemoveLine(int aStart, int aEnd); void RemoveLine(int aIndex); @@ -188,8 +195,8 @@ private: Coordinates SanitizeCoordinates(const Coordinates& aValue) const; void Advance(Coordinates& aCoordinates) const; void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); - int InsertTextAt(Coordinates& aWhere, const char* aValue, const ImVec4& aColor); - void MarkNewline(Coordinates& aWhere, const ImVec4& aColor, int aIndex); + int InsertTextAt(Coordinates& aWhere, const char* aValue, const ImU32 aColor); + void MarkNewline(Coordinates& aWhere, int aIndex); Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const; Coordinates FindWordStart(const Coordinates& aFrom) const; Coordinates FindWordEnd(const Coordinates& aFrom) const; @@ -202,7 +209,7 @@ private: Line& InsertLine(int aIndex); std::string GetWordUnderCursor() const; std::string GetWordAt(const Coordinates& aCoords) const; - ImU32 GetGlyphColor(const Glyph& aGlyph) const; + //ImU32 GetGlyphColor(const Glyph& aGlyph) const; void HandleKeyboardInputs(bool bHoveredScrollbar, bool bActiveScrollbar); void HandleMouseInputs(bool bHoveredScrollbar, bool bActiveScrollbar); @@ -211,21 +218,28 @@ public: bool m_bAutoScroll; private: + // Whether to scroll to the cursor bool m_bScrollToCursor; + + // Whether to scroll to the start of the logging pane (horizontal axis), and + // whether we are already scrolled to the start + bool m_bScrollToStart; + bool m_bScrolledToStart; + + // Whether to scroll to the bottom of the logging pane (vertical axis), and + // whether we are already scrolled to the bottom bool m_bScrollToBottom; bool m_bScrolledToBottom; + bool m_bHandleUserInputs; bool m_bWithinLoggingRect; - bool m_bShowWhiteSpaces; - bool m_bLinesOffsetForward; - int m_nLinesOffsetAmount; + int m_nTabSize; int m_nLeftMargin; - float m_flTextStart; // position (in pixels) where a code line starts relative to the left of the TextLogger. float m_flLineSpacing; SelectionMode m_SelectionMode; double m_flLastClick; - uint64_t m_nStartTime; + double m_flCursorBlinkerStartTime; Coordinates m_InteractiveStart; Coordinates m_InteractiveEnd; @@ -233,7 +247,5 @@ private: ImVec2 m_CharAdvance; Lines m_Lines; - - std::string m_svLineBuffer; ImGuiTextFilter m_itFilter; }; diff --git a/r5dev/thirdparty/imgui/misc/imgui_snapshot.cpp b/r5dev/thirdparty/imgui/misc/imgui_snapshot.cpp new file mode 100644 index 00000000..343dbe90 --- /dev/null +++ b/r5dev/thirdparty/imgui/misc/imgui_snapshot.cpp @@ -0,0 +1,57 @@ +// ImGui snapshot class taken from: +// https://github.com/ocornut/imgui/issues/1860 +#include "imgui.h" +#include "imgui_snapshot.h" + +void ImDrawDataSnapshot::Clear() +{ + for (int n = 0; n < Cache.GetMapSize(); n++) + if (ImDrawDataSnapshotEntry* entry = Cache.TryGetMapData(n)) + IM_DELETE(entry->OurCopy); + Cache.Clear(); + DrawData.Clear(); +} + +void ImDrawDataSnapshot::SnapUsingSwap(ImDrawData* src, double current_time) +{ + ImDrawData* dst = &DrawData; + IM_ASSERT(src != dst && src->Valid); + + // Copy all fields except CmdLists[] + ImVector backup_draw_list; + backup_draw_list.swap(src->CmdLists); + IM_ASSERT(src->CmdLists.Data == NULL); + *dst = *src; + backup_draw_list.swap(src->CmdLists); + + // Swap and mark as used + for (ImDrawList* src_list : src->CmdLists) + { + ImDrawDataSnapshotEntry* entry = GetOrAddEntry(src_list); + if (entry->OurCopy == NULL) + { + entry->SrcCopy = src_list; + entry->OurCopy = IM_NEW(ImDrawList)(src_list->_Data); + } + IM_ASSERT(entry->SrcCopy == src_list); + entry->SrcCopy->CmdBuffer.swap(entry->OurCopy->CmdBuffer); // Cheap swap + entry->SrcCopy->IdxBuffer.swap(entry->OurCopy->IdxBuffer); + entry->SrcCopy->VtxBuffer.swap(entry->OurCopy->VtxBuffer); + entry->SrcCopy->CmdBuffer.reserve(entry->OurCopy->CmdBuffer.Capacity); // Preserve bigger size to avoid reallocs for two consecutive frames + entry->SrcCopy->IdxBuffer.reserve(entry->OurCopy->IdxBuffer.Capacity); + entry->SrcCopy->VtxBuffer.reserve(entry->OurCopy->VtxBuffer.Capacity); + entry->LastUsedTime = current_time; + dst->CmdLists.push_back(entry->OurCopy); + } + + // Cleanup unused data + const double gc_threshold = current_time - MemoryCompactTimer; + for (int n = 0; n < Cache.GetMapSize(); n++) + if (ImDrawDataSnapshotEntry* entry = Cache.TryGetMapData(n)) + { + if (entry->LastUsedTime > gc_threshold) + continue; + IM_DELETE(entry->OurCopy); + Cache.Remove(GetDrawListID(entry->SrcCopy), entry); + } +}; diff --git a/r5dev/thirdparty/imgui/misc/imgui_snapshot.h b/r5dev/thirdparty/imgui/misc/imgui_snapshot.h new file mode 100644 index 00000000..052fd75f --- /dev/null +++ b/r5dev/thirdparty/imgui/misc/imgui_snapshot.h @@ -0,0 +1,34 @@ +// Usage: +// static ImDrawDataSnapshot snapshot; // Important: make persistent across frames to reuse buffers. +// snapshot.SnapUsingSwap(ImGui::GetDrawData(), ImGui::GetTime()); +// [...] +// ImGui_ImplDX11_RenderDrawData(&snapshot.DrawData); +#pragma once + +// FIXME: Could store an ID in ImDrawList to make this easier for user. +#include "imgui_internal.h" // ImPool<>, ImHashData + +struct ImDrawDataSnapshotEntry +{ + ImDrawList* SrcCopy = NULL; // Drawlist owned by main context + ImDrawList* OurCopy = NULL; // Our copy + double LastUsedTime = 0.0; +}; + +struct ImDrawDataSnapshot +{ + // Members + ImDrawData DrawData; + ImPool Cache; + float MemoryCompactTimer = 20.0f; // Discard unused data after 20 seconds + + // Functions + ~ImDrawDataSnapshot() { Clear(); } + void Clear(); + void SnapUsingSwap(ImDrawData* src, double current_time); // Efficient snapshot by swapping data, meaning "src_list" is unusable. + //void SnapUsingCopy(ImDrawData* src, double current_time); // Deep-copy snapshot + + // Internals + ImGuiID GetDrawListID(ImDrawList* src_list) { return ImHashData(&src_list, sizeof(src_list)); } // Hash pointer + ImDrawDataSnapshotEntry* GetOrAddEntry(ImDrawList* src_list) { return Cache.GetOrAddByKey(GetDrawListID(src_list)); } +}; diff --git a/r5dev/thirdparty/imgui/misc/imgui_utility.cpp b/r5dev/thirdparty/imgui/misc/imgui_utility.cpp index 8423cb1e..7881ec27 100644 --- a/r5dev/thirdparty/imgui/misc/imgui_utility.cpp +++ b/r5dev/thirdparty/imgui/misc/imgui_utility.cpp @@ -5,7 +5,7 @@ #include "core/stdafx.h" #include "tier0/commandline.h" #include "tier0/memstd.h" -#include "vpc/keyvalues.h" +#include "tier1/keyvalues.h" #include "filesystem/filesystem.h" #include "thirdparty/imgui/misc/imgui_utility.h" @@ -15,7 +15,7 @@ void ImGuiConfig::Load() { const string svPath = Format(SDK_SYSTEM_CFG_PATH"%s", IMGUI_BIND_FILE); - DevMsg(eDLL_T::MS, "Loading ImGui config file '%s'\n", svPath.c_str()); + Msg(eDLL_T::MS, "Loading ImGui config file '%s'\n", svPath.c_str()); FileSystem()->CreateDirHierarchy(SDK_SYSTEM_CFG_PATH, "PLATFORM"); // Create directory, so ImGui can load/save 'layout.ini'. KeyValues* pKeyMapKV = FileSystem()->LoadKeyValues(IFileSystem::TYPE_COMMON, svPath.c_str(), "PLATFORM"); @@ -45,7 +45,7 @@ void ImGuiConfig::Load() void ImGuiConfig::Save() { const string svPath = Format(SDK_SYSTEM_CFG_PATH"%s", IMGUI_BIND_FILE); - DevMsg(eDLL_T::MS, "Saving ImGui config file '%s'\n", svPath.c_str()); + Msg(eDLL_T::MS, "Saving ImGui config file '%s'\n", svPath.c_str()); FileSystem()->CreateDirHierarchy(SDK_SYSTEM_CFG_PATH, "PLATFORM"); // Create directory, so ImGui can load/save 'layout.ini'. @@ -115,14 +115,9 @@ ImGuiStyle_t ImGuiConfig::InitStyle() const style.WindowBorderSize = 0.0f; style.FrameBorderSize = 1.0f; style.ChildBorderSize = 1.0f; - style.PopupBorderSize = 1.0f; - style.TabBorderSize = 1.0f; - style.WindowRounding = 4.0f; - style.FrameRounding = 1.0f; style.ChildRounding = 1.0f; style.PopupRounding = 3.0f; - style.TabRounding = 1.0f; style.ScrollbarRounding = 1.0f; result = ImGuiStyle_t::LEGACY; @@ -167,14 +162,9 @@ ImGuiStyle_t ImGuiConfig::InitStyle() const style.WindowBorderSize = 1.0f; style.FrameBorderSize = 0.0f; style.ChildBorderSize = 0.0f; - style.PopupBorderSize = 1.0f; - style.TabBorderSize = 1.0f; - style.WindowRounding = 4.0f; - style.FrameRounding = 1.0f; style.ChildRounding = 1.0f; style.PopupRounding = 3.0f; - style.TabRounding = 1.0f; style.ScrollbarRounding = 3.0f; result = ImGuiStyle_t::MODERN; @@ -223,20 +213,23 @@ ImGuiStyle_t ImGuiConfig::InitStyle() const style.WindowBorderSize = 1.0f; style.FrameBorderSize = 1.0f; style.ChildBorderSize = 0.0f; - style.PopupBorderSize = 1.0f; - style.TabBorderSize = 1.0f; - style.WindowRounding = 4.0f; - style.FrameRounding = 1.0f; style.ChildRounding = 2.0f; style.PopupRounding = 4.0f; - style.TabRounding = 1.0f; style.GrabRounding = 1.0f; style.ScrollbarRounding = 1.0f; result = ImGuiStyle_t::DEFAULT; } + style.PopupBorderSize = 1.0f; + style.TabBorderSize = 1.0f; + style.TabBarBorderSize = 1.0f; + + style.WindowRounding = 4.0f; + style.FrameRounding = 1.0f; + style.TabRounding = 1.0f; + style.ItemSpacing = ImVec2(5, 4); style.FramePadding = ImVec2(4, 4); style.WindowPadding = ImVec2(5, 5); @@ -244,4 +237,4 @@ ImGuiStyle_t ImGuiConfig::InitStyle() const return result; } -ImGuiConfig* g_pImGuiConfig = new ImGuiConfig(); +ImGuiConfig g_ImGuiConfig; diff --git a/r5dev/thirdparty/imgui/misc/imgui_utility.h b/r5dev/thirdparty/imgui/misc/imgui_utility.h index 94cdca17..f1b418d1 100644 --- a/r5dev/thirdparty/imgui/misc/imgui_utility.h +++ b/r5dev/thirdparty/imgui/misc/imgui_utility.h @@ -30,4 +30,4 @@ public: ImGuiStyle_t InitStyle() const; }; -extern ImGuiConfig* g_pImGuiConfig; +extern ImGuiConfig g_ImGuiConfig; diff --git a/r5dev/thirdparty/jwt/CMakeLists.txt b/r5dev/thirdparty/jwt/CMakeLists.txt new file mode 100644 index 00000000..3f257f5c --- /dev/null +++ b/r5dev/thirdparty/jwt/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required( VERSION 3.16 ) +add_module( "lib" "libjwt" "" ${FOLDER_CONTEXT} TRUE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "Source" + "base64.c" + "claim.c" + "decode.c" + "encode.c" + "util.c" + "version.c" +) + +add_sources( SOURCE_GROUP "Include" + "include/algs.h" + "include/base64.h" + "include/claim.h" + "include/decode.h" + "include/encode.h" + "include/retcodes.h" + "include/util.h" + "include/version.h" +) + +end_sources() +thirdparty_suppress_warnings() + +target_include_directories( ${PROJECT_NAME} PRIVATE + "${THIRDPARTY_SOURCE_DIR}/mbedtls/include/" +) diff --git a/r5dev/thirdparty/jwt/base64.c b/r5dev/thirdparty/jwt/base64.c new file mode 100644 index 00000000..2d7fe85b --- /dev/null +++ b/r5dev/thirdparty/jwt/base64.c @@ -0,0 +1,315 @@ +/* + * Base64 encoding/decoding (RFC1341) + * Copyright (c) 2005-2011, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +/* + +The above mentioned README can be found under: http://web.mit.edu/freebsd/head/contrib/wpa/ + +Here's a full paste of it (18. January 2020), in case the URL goes numb: + +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2012, Jouni Malinen and contributors +All Rights Reserved. + +These programs are licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. + + +This package may include either wpa_supplicant, hostapd, or both. See +README file respective subdirectories (wpa_supplicant/README or +hostapd/README) for more details. + +Source code files were moved around in v0.6.x releases and compared to +earlier releases, the programs are now built by first going to a +subdirectory (wpa_supplicant or hostapd) and creating build +configuration (.config) and running 'make' there (for Linux/BSD/cygwin +builds). + + +License +------- + +This software may be distributed, used, and modified under the terms of +BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + +http://web.mit.edu/freebsd/head/contrib/wpa/COPYING (at the time of writing, 18. January 2020) + +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2012, Jouni Malinen and contributors +All Rights Reserved. + + +See the README file for the current license terms. + +This software was previously distributed under BSD/GPL v2 dual license +terms that allowed either of those license alternatives to be +selected. As of February 11, 2012, the project has chosen to use only +the BSD license option for future distribution. As such, the GPL v2 +license option is no longer used. It should be noted that the BSD +license option (the one with advertisement clause removed) is compatible +with GPL and as such, does not prevent use of this software in projects +that use GPL. + +Some of the files may still include pointers to GPL version 2 license +terms. However, such copyright and license notifications are maintained +only for attribution purposes and any distribution of this software +after February 11, 2012 is no longer under the GPL v2 option. + +*/ + +/* https://github.com/gaspardpetit/base64 */ + +#include +#include +#include "include/base64.h" +#include "include/version.h" +#include "include/retcodes.h" + +static const uint8_t TABLE[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const uint8_t URL_SAFE_TABLE[64 + 1] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +int l8w8jwt_base64_encode(const int url, const uint8_t* data, const size_t data_length, char** out, size_t* out_length) +{ + if (out == NULL || data == NULL || out_length == NULL) + { + return L8W8JWT_NULL_ARG; + } + + if (data_length == 0) + { + return L8W8JWT_INVALID_ARG; + } + + size_t olen = data_length * 4 / 3 + 4; + + olen += olen / 72; + olen++; + + if (olen < data_length) + { + return L8W8JWT_OVERFLOW; + } + + *out = (char*)malloc(olen); + if (*out == NULL) + { + return L8W8JWT_OUT_OF_MEM; + } + + uint8_t* pos = (uint8_t*)(*out); + uint8_t* in = (uint8_t*)data; + uint8_t* end = (uint8_t*)data + data_length; + + int line_length = 0; + const uint8_t* table = url ? URL_SAFE_TABLE : TABLE; + + while (end - in >= 3) + { + *pos++ = table[in[0] >> 2]; + *pos++ = table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = table[in[2] & 0x3f]; + + in += 3; + + line_length += 4; + if (line_length >= 72 && !url) + { + *pos++ = '\n'; + line_length = 0; + } + } + + int sub = 0; + if (end - in) + { + *pos++ = table[in[0] >> 2]; + + if (end - in == 1) + { + *pos++ = table[(in[0] & 0x03) << 4]; + *pos++ = url ? '\0' : '='; + if (url) + { + sub++; + } + } + else + { + *pos++ = table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = table[(in[1] & 0x0f) << 2]; + } + + *pos++ = url ? '\0' : '='; + line_length += 4; + if (url) + { + sub++; + } + } + + if (line_length && !url) + { + *pos++ = '\n'; + } + + *pos = '\0'; + *out_length = (pos - (uint8_t*)*out) - sub; + + return L8W8JWT_SUCCESS; +} + +int l8w8jwt_base64_decode(const int url, const char* data, const size_t data_length, uint8_t** out, size_t* out_length) +{ + if (data == NULL || out == NULL || out_length == NULL) + { + return L8W8JWT_NULL_ARG; + } + + size_t in_length = data_length; + + if (in_length == 0) + { + return L8W8JWT_INVALID_ARG; + } + + if (*(data + in_length - 1) == '\0') + { + in_length--; + } + + size_t i; + size_t count = 0; + uint8_t dtable[256]; + const uint8_t* table = url ? URL_SAFE_TABLE : TABLE; + + memset(dtable, 0x80, 256); + + for (i = 0; i < 64; i++) + { + dtable[table[i]] = (uint8_t)i; + } + + dtable['='] = 0; + + for (i = 0; i < in_length; i++) + { + if (dtable[(unsigned char)data[i]] != 0x80) + count++; + } + + int r = (int)(count % 4); + + if (count == 0 || r == 1 || (!url && r > 0)) // Invalid input string (format or padding). + return L8W8JWT_INVALID_ARG; + + if (r == 3) + r = 1; + + *out = (uint8_t*)calloc(count / 4 * 3 + 16, sizeof(uint8_t)); + if (*out == NULL) + { + return L8W8JWT_OUT_OF_MEM; + } + + count = 0; + int pad = 0; + uint8_t tmp; + uint8_t block[4]; + uint8_t* pos = *out; + + for (i = 0; i < in_length + r; i++) + { + const unsigned char c = i < in_length ? data[i] : '='; + + tmp = dtable[c]; + + if (tmp == 0x80) + continue; + + if (c == '=') + pad++; + + block[count] = tmp; + count++; + + if (count == 4) + { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + if (pad) + { + if (pad == 1) + { + pos--; + } + else if (pad == 2) + { + pos -= 2; + } + else + { + l8w8jwt_free(*out); + *out = NULL; + return L8W8JWT_INVALID_ARG; // Invalid padding... + } + break; + } + } + } + + *out_length = pos - *out; + + return L8W8JWT_SUCCESS; +} + +/* + * All credits for this base-64 encoding/decoding implementation go to Jouni Malinen. + * I take no credit for this (not even the modifications I made to it!) whatsoever. + * More information at the top of this file. + */ diff --git a/r5dev/thirdparty/jwt/claim.c b/r5dev/thirdparty/jwt/claim.c new file mode 100644 index 00000000..8918f0bc --- /dev/null +++ b/r5dev/thirdparty/jwt/claim.c @@ -0,0 +1,146 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "include/claim.h" +#include "include/version.h" +#include "include/retcodes.h" +#include "include/chillbuff.h" + +#include +#include + +void l8w8jwt_free_claims(struct l8w8jwt_claim* claims, const size_t claims_count) +{ + if (claims != NULL && claims_count > 0) + { + for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim) + { + if (claim == NULL) + continue; + + l8w8jwt_zero(claim->key, claim->key_length); + l8w8jwt_zero(claim->value, claim->value_length); + + l8w8jwt_free(claim->key); + l8w8jwt_free(claim->value); + } + + l8w8jwt_zero(claims, claims_count * sizeof(struct l8w8jwt_claim)); + l8w8jwt_free(claims); + } +} + +static inline void l8w8jwt_escape_claim_string(struct chillbuff* stringbuilder, const char* string, const size_t string_length) +{ + for (size_t i = 0; i < string_length; ++i) + { + const char c = string[i]; + + switch (c) + { + case '\\': + chillbuff_push_back(stringbuilder, "\\\\", 2); + break; + case '\"': + chillbuff_push_back(stringbuilder, "\\\"", 2); + break; + default: + chillbuff_push_back(stringbuilder, &c, 1); + break; + } + } +} + +int l8w8jwt_write_claims(struct chillbuff* stringbuilder, struct l8w8jwt_claim* claims, const size_t claims_count) +{ + if (stringbuilder == NULL || claims == NULL) + { + return L8W8JWT_NULL_ARG; + } + + if (claims_count == 0) + { + return L8W8JWT_INVALID_ARG; + } + + struct chillbuff escape_buffer; + if (chillbuff_init(&escape_buffer, 256, sizeof(char), CHILLBUFF_GROW_LINEAR) != 0) + { + return L8W8JWT_OUT_OF_MEM; + } + + int first = 1; + for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim) + { + if (claim->key == NULL) + { + continue; + } + + if (!first) + { + chillbuff_push_back(stringbuilder, ",", 1); + } + + const size_t key_length = claim->key_length ? claim->key_length : strlen(claim->key); + const size_t value_length = claim->value_length ? claim->value_length : strlen(claim->value); + + chillbuff_clear(&escape_buffer); + l8w8jwt_escape_claim_string(&escape_buffer, claim->key, key_length); + + chillbuff_push_back(stringbuilder, "\"", 1); + chillbuff_push_back(stringbuilder, escape_buffer.array, escape_buffer.length); + chillbuff_push_back(stringbuilder, "\":", 2); + + if (claim->type == L8W8JWT_CLAIM_TYPE_STRING) + chillbuff_push_back(stringbuilder, "\"", 1); + + chillbuff_clear(&escape_buffer); + l8w8jwt_escape_claim_string(&escape_buffer, claim->value, value_length); + + chillbuff_push_back(stringbuilder, escape_buffer.array,escape_buffer.length); + + if (claim->type == L8W8JWT_CLAIM_TYPE_STRING) + chillbuff_push_back(stringbuilder, "\"", 1); + + first = 0; + } + + chillbuff_free(&escape_buffer); + return L8W8JWT_SUCCESS; +} + +struct l8w8jwt_claim* l8w8jwt_get_claim(struct l8w8jwt_claim* claims, const size_t claims_count, const char* key, const size_t key_length) +{ + if (claims == NULL || key == NULL || claims_count == 0 || key_length == 0) + return NULL; + + for (struct l8w8jwt_claim* claim = claims; claim < claims + claims_count; ++claim) + { + if (strncmp(claim->key, key, key_length) == 0) + return claim; + } + + return NULL; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/r5dev/thirdparty/jwt/decode.c b/r5dev/thirdparty/jwt/decode.c new file mode 100644 index 00000000..121a2cab --- /dev/null +++ b/r5dev/thirdparty/jwt/decode.c @@ -0,0 +1,773 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSMN_STATIC + +#include "include/util.h" +#include "include/decode.h" +#include "include/base64.h" +#include "include/chillbuff.h" +#include "include/jsmn.h" +#include "include/checknum.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if L8W8JWT_ENABLE_EDDSA +#include +#endif + +static inline void md_info_from_alg(const int alg, mbedtls_md_info_t** md_info, mbedtls_md_type_t* md_type, size_t* md_length) +{ + switch (alg) + { + case L8W8JWT_ALG_HS256: + case L8W8JWT_ALG_RS256: + case L8W8JWT_ALG_PS256: + case L8W8JWT_ALG_ES256: + case L8W8JWT_ALG_ES256K: + *md_length = 32; + *md_type = MBEDTLS_MD_SHA256; + *md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + break; + + case L8W8JWT_ALG_HS384: + case L8W8JWT_ALG_RS384: + case L8W8JWT_ALG_PS384: + case L8W8JWT_ALG_ES384: + *md_length = 48; + *md_type = MBEDTLS_MD_SHA384; + *md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); + break; + + case L8W8JWT_ALG_HS512: + case L8W8JWT_ALG_RS512: + case L8W8JWT_ALG_PS512: + case L8W8JWT_ALG_ES512: + case L8W8JWT_ALG_ED25519: + *md_length = 64; + *md_type = MBEDTLS_MD_SHA512; + *md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); + break; + + default: + break; + } +} + +static int l8w8jwt_unescape_claim(struct l8w8jwt_claim* claim, const char* key, const size_t key_length, const char* value, const size_t value_length) +{ + claim->key_length = 0; + claim->key = (char*)calloc(sizeof(char), key_length + 1); + + claim->value_length = 0; + claim->value = (char*)calloc(sizeof(char), value_length + 1); + + if (claim->key == NULL || claim->value == NULL) + { + free((void*)claim->key); + free(claim->value); + return L8W8JWT_OUT_OF_MEM; + } + + char* out_key = claim->key; + char* out_value = claim->value; + + for (size_t i = 0; i < key_length; ++i) + { + const char c = key[i]; + *out_key = c; + + if (c == '\\' && i != key_length - 1) + { + const char nc = key[i + 1]; + if (nc == '\"') + { + *out_key = '\"'; + } + ++i; + } + + ++out_key; + } + + for (size_t i = 0; i < value_length; ++i) + { + const char c = value[i]; + *out_value = c; + + if (c == '\\' && i != value_length - 1) + { + const char nc = value[i + 1]; + + switch (nc) + { + case '\"': + *out_value = '\"'; + break; + case '/': + *out_value = '/'; + break; + default: + break; + } + + ++i; + } + + ++out_value; + } + + claim->key_length = (size_t)(out_key - claim->key); + claim->value_length = (size_t)(out_value - claim->value); + + return L8W8JWT_SUCCESS; +} + +static int l8w8jwt_parse_claims(chillbuff* buffer, char* json, const size_t json_length) +{ + jsmn_parser parser; + jsmn_init(&parser); + + int r = jsmn_parse(&parser, json, json_length, NULL, 0); + + if (r == 0) + { + return L8W8JWT_SUCCESS; + } + else if (r < 0) + { + return L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + } + + jsmntok_t _tokens[64]; + jsmntok_t* tokens = r <= (sizeof(_tokens) / sizeof(_tokens[0])) ? _tokens : (jsmntok_t*)malloc(r * sizeof(jsmntok_t)); + + if (tokens == NULL) + { + return L8W8JWT_OUT_OF_MEM; + } + + jsmn_init(&parser); + r = jsmn_parse(&parser, json, json_length, tokens, r); + + if (r < 0) + { + return L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + } + + if (tokens->type != JSMN_OBJECT) + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + for (size_t i = 1; i < r; ++i) + { + struct l8w8jwt_claim claim; + + const jsmntok_t key = tokens[i]; + const jsmntok_t value = tokens[++i]; + + if (i >= r || key.type != JSMN_STRING) + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + switch (value.type) + { + case JSMN_UNDEFINED: { + claim.type = L8W8JWT_CLAIM_TYPE_OTHER; + break; + } + case JSMN_OBJECT: { + claim.type = L8W8JWT_CLAIM_TYPE_OBJECT; + break; + } + case JSMN_ARRAY: { + claim.type = L8W8JWT_CLAIM_TYPE_ARRAY; + break; + } + case JSMN_STRING: { + claim.type = L8W8JWT_CLAIM_TYPE_STRING; + break; + } + case JSMN_PRIMITIVE: { + const int value_length = value.end - value.start; + + if (value_length <= 5 && (strncmp(json + value.start, "true", 4) == 0 || strncmp(json + value.start, "false", 5) == 0)) + { + claim.type = L8W8JWT_CLAIM_TYPE_BOOLEAN; + break; + } + + if (value_length == 4 && strncmp(json + value.start, "null", 4) == 0) + { + claim.type = L8W8JWT_CLAIM_TYPE_NULL; + break; + } + + switch (checknum(json + value.start, value_length)) + { + case 1: { + claim.type = L8W8JWT_CLAIM_TYPE_INTEGER; + break; + } + case 2: { + claim.type = L8W8JWT_CLAIM_TYPE_NUMBER; + break; + } + default: { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + } + + break; + } + default: { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + } + + int ur = l8w8jwt_unescape_claim(&claim, json + key.start, (size_t)key.end - key.start, json + value.start, (size_t)value.end - value.start); + if (ur != L8W8JWT_SUCCESS) + { + r = ur; + goto exit; + } + + chillbuff_push_back(buffer, &claim, 1); + } + + r = L8W8JWT_SUCCESS; +exit: + if (tokens != _tokens) + { + l8w8jwt_free(tokens); + } + return r; +} + +void l8w8jwt_decoding_params_init(struct l8w8jwt_decoding_params* params) +{ + if (params == NULL) + { + return; + } + memset(params, 0x00, sizeof(struct l8w8jwt_decoding_params)); + params->alg = -2; +} + +int l8w8jwt_validate_decoding_params(struct l8w8jwt_decoding_params* params) +{ + if (params == NULL || params->jwt == NULL || params->verification_key == NULL) + { + return L8W8JWT_NULL_ARG; + } + + if (params->jwt_length == 0 || params->verification_key_length == 0 || params->verification_key_length > L8W8JWT_MAX_KEY_SIZE) + { + return L8W8JWT_INVALID_ARG; + } + +#if !L8W8JWT_ENABLE_EDDSA + if (params->alg == L8W8JWT_ALG_ED25519) + { + return L8W8JWT_UNSUPPORTED_ALG; + } +#endif + + return L8W8JWT_SUCCESS; +} + +int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validation_result* out_validation_result, struct l8w8jwt_claim** out_claims, size_t* out_claims_length) +{ + if (params == NULL || (out_claims != NULL && out_claims_length == NULL)) + { + return L8W8JWT_NULL_ARG; + } + + const int alg = params->alg; + unsigned validation_res = L8W8JWT_VALID; + + int r = l8w8jwt_validate_decoding_params(params); + if (r != L8W8JWT_SUCCESS) + { + return r; + } + + if (out_validation_result == NULL) + { + return L8W8JWT_NULL_ARG; + } + + *out_validation_result = (enum l8w8jwt_validation_result)~L8W8JWT_VALID; + + char* header = NULL; + size_t header_length = 0; + + char* payload = NULL; + size_t payload_length = 0; + + uint8_t* signature = NULL; + size_t signature_length = 0; + + char* current = params->jwt; + char* next = strchr(params->jwt, '.'); + + size_t current_length; + time_t ct; + + if (next == NULL) /* No payload. */ + { + return L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + } + + int is_cert = 0; // If the validation PEM is a X.509 certificate, this will be set to 1. + + mbedtls_pk_context pk; + mbedtls_pk_init(&pk); + + mbedtls_x509_crt crt; + mbedtls_x509_crt_init(&crt); + + mbedtls_entropy_context entropy; + mbedtls_entropy_init(&entropy); + + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init(&ctr_drbg); + +#if L8W8JWT_SMALL_STACK + unsigned char* key = calloc(sizeof(unsigned char), L8W8JWT_MAX_KEY_SIZE); + if (key == NULL) + { + r = L8W8JWT_OUT_OF_MEM; + goto exit; + } +#else + unsigned char key[L8W8JWT_MAX_KEY_SIZE] = { 0x00 }; +#endif + + size_t key_length = params->verification_key_length; + memcpy(key, params->verification_key, key_length); + + key_length += key[key_length - 1] != '\0'; + + chillbuff claims; + r = chillbuff_init(&claims, 16, sizeof(struct l8w8jwt_claim), CHILLBUFF_GROW_DUPLICATIVE); + if (r != CHILLBUFF_SUCCESS) + { + r = L8W8JWT_OUT_OF_MEM; + goto exit; + } + + current_length = next - current; + + r = l8w8jwt_base64_decode(true, current, current_length, (uint8_t**)&header, &header_length); + if (r != L8W8JWT_SUCCESS) + { + if (r != L8W8JWT_OUT_OF_MEM) + r = L8W8JWT_BASE64_FAILURE; + goto exit; + } + + current = next + 1; + next = strchr(current, '.'); + + if (next == NULL && alg != -1) /* No signature. */ + { + r = L8W8JWT_DECODE_FAILED_MISSING_SIGNATURE; + goto exit; + } + + current_length = (next != NULL ? next : params->jwt + params->jwt_length) - current; + + r = l8w8jwt_base64_decode(true, current, current_length, (uint8_t**)&payload, &payload_length); + if (r != L8W8JWT_SUCCESS) + { + if (r != L8W8JWT_OUT_OF_MEM) + r = L8W8JWT_BASE64_FAILURE; + goto exit; + } + + if (next != NULL) + { + current = next + 1; + current_length = (params->jwt + params->jwt_length) - current; + + r = l8w8jwt_base64_decode(true, current, current_length, &signature, &signature_length); + if (r != L8W8JWT_SUCCESS) + { + if (r != L8W8JWT_OUT_OF_MEM) + r = L8W8JWT_BASE64_FAILURE; + goto exit; + } + } + + /* Signature verification. */ + if (signature != NULL && signature_length > 0 && alg != -1) + { + is_cert = strstr((const char*)key, "-----BEGIN CERTIFICATE-----") != NULL; + if (is_cert) + { + r = mbedtls_x509_crt_parse(&crt, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + + pk = crt.pk; + } + + size_t md_length; + mbedtls_md_type_t md_type; + mbedtls_md_info_t* md_info; + + md_info_from_alg(alg, &md_info, &md_type, &md_length); + + unsigned char hash[64] = { 0x00 }; + + switch (alg) + { + case L8W8JWT_ALG_ES256: + case L8W8JWT_ALG_ES384: + case L8W8JWT_ALG_ES512: + case L8W8JWT_ALG_RS256: + case L8W8JWT_ALG_RS384: + case L8W8JWT_ALG_RS512: + case L8W8JWT_ALG_PS256: + case L8W8JWT_ALG_PS384: + case L8W8JWT_ALG_PS512: + case L8W8JWT_ALG_ES256K: { + + r = mbedtls_md(md_info, (const unsigned char*)params->jwt, (current - 1) - params->jwt, hash); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_SHA2_FAILURE; + goto exit; + } + break; + } + default: + break; + } + + switch (alg) + { + case L8W8JWT_ALG_HS256: + case L8W8JWT_ALG_HS384: + case L8W8JWT_ALG_HS512: { + + unsigned char signature_cmp[64]; + memset(signature_cmp, '\0', sizeof(signature_cmp)); + + r = mbedtls_md_hmac(md_info, key, key_length - 1, (const unsigned char*)params->jwt, (current - 1) - params->jwt, signature_cmp); + if (r != 0) + { + validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + r = memcmp(signature, signature_cmp, 32 + (16 * alg)); + if (r != 0) + { + validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; + } + case L8W8JWT_ALG_RS256: + case L8W8JWT_ALG_RS384: + case L8W8JWT_ALG_RS512: { + + if (!is_cert) + { + r = mbedtls_pk_parse_public_key(&pk, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + } + + r = mbedtls_pk_verify(&pk, md_type, hash, md_length, (const unsigned char*)signature, signature_length); + if (r != 0) + { + validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; + } + case L8W8JWT_ALG_PS256: + case L8W8JWT_ALG_PS384: + case L8W8JWT_ALG_PS512: { + + if (!is_cert) + { + r = mbedtls_pk_parse_public_key(&pk, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + } + + mbedtls_rsa_context* rsa = mbedtls_pk_rsa(pk); + mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_type); + + r = mbedtls_rsa_rsassa_pss_verify(rsa, md_type, md_length, hash, signature); + if (r != 0) + { + validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; + } + case L8W8JWT_ALG_ES256: + case L8W8JWT_ALG_ES256K: + case L8W8JWT_ALG_ES384: + case L8W8JWT_ALG_ES512: { + + if (!is_cert) + { + r = mbedtls_pk_parse_public_key(&pk, key, key_length); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + } + + const size_t half_signature_length = signature_length / 2; + + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + + mbedtls_mpi sig_r, sig_s; + mbedtls_mpi_init(&sig_r); + mbedtls_mpi_init(&sig_s); + + r = mbedtls_ecdsa_from_keypair(&ecdsa, mbedtls_pk_ec(pk)); + + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + mbedtls_ecdsa_free(&ecdsa); + mbedtls_mpi_free(&sig_r); + mbedtls_mpi_free(&sig_s); + goto exit; + } + + mbedtls_mpi_read_binary(&sig_r, signature, half_signature_length); + mbedtls_mpi_read_binary(&sig_s, signature + half_signature_length, half_signature_length); + + r = mbedtls_ecdsa_verify(&ecdsa.MBEDTLS_PRIVATE(grp), hash, md_length, &ecdsa.MBEDTLS_PRIVATE(Q), &sig_r, &sig_s); + if (r != 0) + { + validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + } + + mbedtls_ecdsa_free(&ecdsa); + mbedtls_mpi_free(&sig_r); + mbedtls_mpi_free(&sig_s); + + break; + } + case L8W8JWT_ALG_ED25519: { + +#if L8W8JWT_ENABLE_EDDSA + if (key_length != 64 && !(key_length == 65 && key[64] == 0x00)) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + unsigned char public_key[32 + 1] = { 0x00 }; + + if (l8w8jwt_hexstr2bin((const char*)key, key_length, public_key, sizeof(public_key), NULL) != 0) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + if (!ed25519_verify(signature, (const unsigned char*)params->jwt, (current - 1) - params->jwt, public_key)) + { + validation_res |= (unsigned)L8W8JWT_SIGNATURE_VERIFICATION_FAILURE; + break; + } + + break; +#else + r = L8W8JWT_UNSUPPORTED_ALG; + goto exit; +#endif + } + default: + break; + } + } + + r = l8w8jwt_parse_claims(&claims, header, header_length); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + r = l8w8jwt_parse_claims(&claims, payload, payload_length); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT; + goto exit; + } + + if (params->validate_sub != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "sub", 3); + if (c == NULL || strncmp(c->value, params->validate_sub, params->validate_sub_length ? params->validate_sub_length : strlen(params->validate_sub)) != 0) + { + validation_res |= (unsigned)L8W8JWT_SUB_FAILURE; + } + } + + if (params->validate_aud != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "aud", 3); + if (c == NULL || strncmp(c->value, params->validate_aud, params->validate_aud_length ? params->validate_aud_length : strlen(params->validate_aud)) != 0) + { + validation_res |= (unsigned)L8W8JWT_AUD_FAILURE; + } + } + + if (params->validate_iss != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "iss", 3); + if (c == NULL || strncmp(c->value, params->validate_iss, params->validate_iss_length ? params->validate_iss_length : strlen(params->validate_iss)) != 0) + { + validation_res |= (unsigned)L8W8JWT_ISS_FAILURE; + } + } + + if (params->validate_jti != NULL) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "jti", 3); + if (c == NULL || strncmp(c->value, params->validate_jti, params->validate_jti_length ? params->validate_jti_length : strlen(params->validate_jti)) != 0) + { + validation_res |= (unsigned)L8W8JWT_JTI_FAILURE; + } + } + + ct = time(NULL); + + if (params->validate_exp) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "exp", 3); + if (c == NULL || ct - params->exp_tolerance_seconds > strtoll(c->value, NULL, 10)) + { + validation_res |= (unsigned)L8W8JWT_EXP_FAILURE; + } + } + + if (params->validate_nbf) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "nbf", 3); + if (c == NULL || ct + params->nbf_tolerance_seconds < strtoll(c->value, NULL, 10)) + { + validation_res |= (unsigned)L8W8JWT_NBF_FAILURE; + } + } + + if (params->validate_iat) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "iat", 3); + if (c == NULL || ct + params->iat_tolerance_seconds < strtoll(c->value, NULL, 10)) + { + validation_res |= (unsigned)L8W8JWT_IAT_FAILURE; + } + } + + if (params->validate_typ) + { + struct l8w8jwt_claim* c = l8w8jwt_get_claim((struct l8w8jwt_claim*)claims.array, claims.length, "typ", 3); + if (c == NULL || l8w8jwt_strncmpic(c->value, params->validate_typ, params->validate_typ_length) != 0) + { + validation_res |= (unsigned)L8W8JWT_TYP_FAILURE; + } + } + + r = L8W8JWT_SUCCESS; + *out_validation_result = (enum l8w8jwt_validation_result)validation_res; + + if (out_claims != NULL && out_claims_length != NULL) + { + *out_claims_length = claims.length; + *out_claims = (struct l8w8jwt_claim*)claims.array; + } + +exit: + l8w8jwt_free(header); + l8w8jwt_free(payload); + l8w8jwt_free(signature); + + if (out_claims == NULL || r != L8W8JWT_SUCCESS) + { + l8w8jwt_free_claims((struct l8w8jwt_claim*)claims.array, claims.length); + } + + mbedtls_platform_zeroize(key, L8W8JWT_MAX_KEY_SIZE); +#if L8W8JWT_SMALL_STACK + l8w8jwt_free(key); +#endif + + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + + if (is_cert) + { + mbedtls_x509_crt_free(&crt); + } + else + { + mbedtls_pk_free(&pk); + } + + return r; +} + +#undef JSMN_STATIC + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/r5dev/thirdparty/jwt/encode.c b/r5dev/thirdparty/jwt/encode.c new file mode 100644 index 00000000..8abc0962 --- /dev/null +++ b/r5dev/thirdparty/jwt/encode.c @@ -0,0 +1,688 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "include/util.h" +#include "include/encode.h" +#include "include/base64.h" +#include "include/chillbuff.h" + +#include + +#include +#include +#include +#include +#include + +#if L8W8JWT_ENABLE_EDDSA +#include +#endif + +static inline void md_info_from_alg(const int alg, mbedtls_md_info_t** md_info, mbedtls_md_type_t* md_type, size_t* md_length) +{ + switch (alg) + { + case L8W8JWT_ALG_HS256: + case L8W8JWT_ALG_RS256: + case L8W8JWT_ALG_PS256: + case L8W8JWT_ALG_ES256: + case L8W8JWT_ALG_ES256K: + *md_length = 32; + *md_type = MBEDTLS_MD_SHA256; + *md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + break; + + case L8W8JWT_ALG_HS384: + case L8W8JWT_ALG_RS384: + case L8W8JWT_ALG_PS384: + case L8W8JWT_ALG_ES384: + *md_length = 48; + *md_type = MBEDTLS_MD_SHA384; + *md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); + break; + + case L8W8JWT_ALG_HS512: + case L8W8JWT_ALG_RS512: + case L8W8JWT_ALG_PS512: + case L8W8JWT_ALG_ES512: + *md_length = 64; + *md_type = MBEDTLS_MD_SHA512; + *md_info = (mbedtls_md_info_t*)mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); + break; + + default: + break; + } +} + +/* Step 1: prepare the token by encoding header + payload claims into a stringbuilder, ready to be signed! */ +static int write_header_and_payload(chillbuff* stringbuilder, struct l8w8jwt_encoding_params* params) +{ + int r; + chillbuff buff; + + r = chillbuff_init(&buff, 256, sizeof(char), CHILLBUFF_GROW_DUPLICATIVE); + if (r != CHILLBUFF_SUCCESS) + { + return L8W8JWT_OUT_OF_MEM; + } + + switch (params->alg) + { + case L8W8JWT_ALG_HS256: + chillbuff_push_back(&buff, "{\"alg\":\"HS256\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_HS384: + chillbuff_push_back(&buff, "{\"alg\":\"HS384\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_HS512: + chillbuff_push_back(&buff, "{\"alg\":\"HS512\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_RS256: + chillbuff_push_back(&buff, "{\"alg\":\"RS256\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_RS384: + chillbuff_push_back(&buff, "{\"alg\":\"RS384\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_RS512: + chillbuff_push_back(&buff, "{\"alg\":\"RS512\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_PS256: + chillbuff_push_back(&buff, "{\"alg\":\"PS256\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_PS384: + chillbuff_push_back(&buff, "{\"alg\":\"PS384\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_PS512: + chillbuff_push_back(&buff, "{\"alg\":\"PS512\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_ES256: + chillbuff_push_back(&buff, "{\"alg\":\"ES256\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_ES384: + chillbuff_push_back(&buff, "{\"alg\":\"ES384\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_ES512: + chillbuff_push_back(&buff, "{\"alg\":\"ES512\",\"typ\":\"JWT\"", 26); + break; + case L8W8JWT_ALG_ES256K: + chillbuff_push_back(&buff, "{\"alg\":\"ES256K\",\"typ\":\"JWT\",\"kty\":\"EC\",\"crv\":\"secp256k1\"", 56); + break; + case L8W8JWT_ALG_ED25519: + chillbuff_push_back(&buff, "{\"alg\":\"EdDSA\",\"typ\":\"JWT\",\"kty\":\"EC\",\"crv\":\"Ed25519\"", 53); + break; + default: + chillbuff_free(&buff); + return L8W8JWT_INVALID_ARG; + } + + if (params->additional_header_claims_count > 0) + { + chillbuff_push_back(&buff, ",", 1); + l8w8jwt_write_claims(&buff, params->additional_header_claims, params->additional_header_claims_count); + } + + chillbuff_push_back(&buff, "}", 1); + + char* segment; + size_t segment_length; + + r = l8w8jwt_base64_encode(1, (const uint8_t*)buff.array, buff.length, &segment, &segment_length); + if (r != L8W8JWT_SUCCESS) + { + chillbuff_free(&buff); + return r; + } + + chillbuff_push_back(stringbuilder, segment, segment_length); + + chillbuff_clear(&buff); + + l8w8jwt_free(segment); + segment = NULL; + + char iatnbfexp[64] = { 0x00 }; + + if (params->iat) + { + snprintf(iatnbfexp + 00, 21, "%" PRIu64 "", (uint64_t)params->iat); + } + + if (params->nbf) + { + snprintf(iatnbfexp + 21, 21, "%" PRIu64 "", (uint64_t)params->nbf); + } + + if (params->exp) + { + snprintf(iatnbfexp + 42, 21, "%" PRIu64 "", (uint64_t)params->exp); + } + + struct l8w8jwt_claim claims[] = { + // Setting l8w8jwt_claim::value_length to 0 makes the encoder use strlen, which in this case is fine. + { *(iatnbfexp + 00) ? (char*)"iat" : NULL, 3, iatnbfexp + 00, 0, L8W8JWT_CLAIM_TYPE_INTEGER }, + { *(iatnbfexp + 21) ? (char*)"nbf" : NULL, 3, iatnbfexp + 21, 0, L8W8JWT_CLAIM_TYPE_INTEGER }, + { *(iatnbfexp + 42) ? (char*)"exp" : NULL, 3, iatnbfexp + 42, 0, L8W8JWT_CLAIM_TYPE_INTEGER }, + { params->sub ? (char*)"sub" : NULL, 3, params->sub, params->sub_length, L8W8JWT_CLAIM_TYPE_STRING }, + { params->iss ? (char*)"iss" : NULL, 3, params->iss, params->iss_length, L8W8JWT_CLAIM_TYPE_STRING }, + { params->aud ? (char*)"aud" : NULL, 3, params->aud, params->aud_length, L8W8JWT_CLAIM_TYPE_STRING }, + { params->jti ? (char*)"jti" : NULL, 3, params->jti, params->jti_length, L8W8JWT_CLAIM_TYPE_STRING }, + }; + + chillbuff_push_back(&buff, "{", 1); + + l8w8jwt_write_claims(&buff, claims, sizeof(claims) / sizeof(struct l8w8jwt_claim)); + + if (params->additional_payload_claims_count > 0) + { + if (params->iat || params->exp || params->nbf || params->iss_length || params->sub_length || params->jti_length || params->aud_length) + chillbuff_push_back(&buff, ",", 1); + + l8w8jwt_write_claims(&buff, params->additional_payload_claims, params->additional_payload_claims_count); + } + + chillbuff_push_back(&buff, "}", 1); + + r = l8w8jwt_base64_encode(1, (const uint8_t*)buff.array, buff.length, &segment, &segment_length); + if (r != L8W8JWT_SUCCESS) + { + chillbuff_free(&buff); + return r; + } + + chillbuff_push_back(stringbuilder, ".", 1); + chillbuff_push_back(stringbuilder, segment, segment_length); + + l8w8jwt_free(segment); + chillbuff_free(&buff); + + return L8W8JWT_SUCCESS; +} + +/* Step 2: call write_header_and_payload before you call this! */ +static int write_signature(chillbuff* stringbuilder, struct l8w8jwt_encoding_params* params) +{ + int r; + const int alg = params->alg; + + unsigned char hash[64] = { 0x00 }; + + char* signature = NULL; + size_t signature_length = 0, signature_bytes_length = 0, key_length = 0; + + mbedtls_pk_context pk; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + + size_t md_length; + mbedtls_md_type_t md_type; + mbedtls_md_info_t* md_info; + + size_t half_signature_bytes_length; + + mbedtls_pk_init(&pk); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + +#if L8W8JWT_SMALL_STACK + unsigned char* signature_bytes = calloc(sizeof(unsigned char), 4096); + unsigned char* key = calloc(sizeof(unsigned char), L8W8JWT_MAX_KEY_SIZE); + + if (signature_bytes == NULL || key == NULL) + { + r = L8W8JWT_OUT_OF_MEM; + goto exit; + } +#else + unsigned char signature_bytes[4096] = { 0x00 }; + unsigned char key[L8W8JWT_MAX_KEY_SIZE] = { 0x00 }; +#endif + + key_length = params->secret_key_length; + memcpy(key, params->secret_key, key_length); + + /* + * MbedTLS requires the NUL-terminator to be included + * in the PEM-formatted key string passed to the key parse function. + * HMAC-key variants should subtract 1 from key_length again to compensate. + */ + key_length += key[key_length - 1] != '\0'; + + r = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)"l8w8jwt_mbedtls_pers.!#@", 24); + if (r != 0) + { + r = L8W8JWT_MBEDTLS_CTR_DRBG_SEED_FAILURE; + goto exit; + } + + md_info_from_alg(alg, &md_info, &md_type, &md_length); + + switch (alg) + { + case L8W8JWT_ALG_HS256: + case L8W8JWT_ALG_HS384: + case L8W8JWT_ALG_HS512: { + + /* + * "key_length - 1" because the MbedTLS implementation of HMAC + * does not require its key string to include the NUL-terminator, + * unlike the RSA/ECC PEM key parse function "mbedtls_pk_parse_key", + * which MUST include the '\0' in the PEM-formatted key string. + */ + r = mbedtls_md_hmac(md_info, key, key_length - 1, (const unsigned char*)stringbuilder->array, stringbuilder->length, signature_bytes); + if (r != 0) + { + r = L8W8JWT_SIGNATURE_CREATION_FAILURE; + goto exit; + } + + signature_bytes_length = 32 + (16 * params->alg); + break; + } + case L8W8JWT_ALG_RS256: + case L8W8JWT_ALG_RS384: + case L8W8JWT_ALG_RS512: { + + /* Parse & load the key string into the mbedtls pk instance. */ + + r = mbedtls_pk_parse_key(&pk, key, key_length, params->secret_key_pw, params->secret_key_pw_length, mbedtls_ctr_drbg_random, &ctr_drbg); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + + /* Ensure RSA functionality. */ + if (!mbedtls_pk_can_do(&pk, MBEDTLS_PK_RSA) && !mbedtls_pk_can_do(&pk, MBEDTLS_PK_RSA_ALT)) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + /* Weak RSA keys are forbidden! */ + if (mbedtls_pk_get_bitlen(&pk) < 2048) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + /* Hash the JWT header + payload. */ + r = mbedtls_md(md_info, (const unsigned char*)stringbuilder->array, stringbuilder->length, hash); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_SHA2_FAILURE; + goto exit; + } + + /* Sign the hash using the provided private key. */ + r = mbedtls_pk_sign(&pk, md_type, hash, md_length, signature_bytes, 4096, &signature_bytes_length, mbedtls_ctr_drbg_random, &ctr_drbg); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_SIGNATURE_CREATION_FAILURE; + goto exit; + } + + break; + } + case L8W8JWT_ALG_PS256: + case L8W8JWT_ALG_PS384: + case L8W8JWT_ALG_PS512: { + + r = mbedtls_pk_parse_key(&pk, key, key_length, params->secret_key_pw, params->secret_key_pw_length, mbedtls_ctr_drbg_random, &ctr_drbg); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto exit; + } + + if (!mbedtls_pk_can_do(&pk, MBEDTLS_PK_RSASSA_PSS)) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + if (mbedtls_pk_get_bitlen(&pk) < 2048) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + r = mbedtls_md(md_info, (const unsigned char*)stringbuilder->array, stringbuilder->length, hash); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_SHA2_FAILURE; + goto exit; + } + + mbedtls_rsa_context* rsa = mbedtls_pk_rsa(pk); + mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_type); + + r = mbedtls_rsa_rsassa_pss_sign(rsa, mbedtls_ctr_drbg_random, &ctr_drbg, md_type, (unsigned int)md_length, hash, signature_bytes); + if (r != 0) + { + r = L8W8JWT_SIGNATURE_CREATION_FAILURE; + goto exit; + } + + signature_bytes_length = mbedtls_pk_get_bitlen(&pk) / 8; + break; + } + case L8W8JWT_ALG_ES256: + case L8W8JWT_ALG_ES384: + case L8W8JWT_ALG_ES512: + case L8W8JWT_ALG_ES256K: { + + mbedtls_ecdsa_context ecdsa; + mbedtls_ecdsa_init(&ecdsa); + + mbedtls_mpi sig_r, sig_s; + mbedtls_mpi_init(&sig_r); + mbedtls_mpi_init(&sig_s); + + r = mbedtls_pk_parse_key(&pk, key, key_length, params->secret_key_pw, params->secret_key_pw_length, mbedtls_ctr_drbg_random, &ctr_drbg); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto ecdsa_exit; + } + + if (!mbedtls_pk_can_do(&pk, MBEDTLS_PK_ECDSA)) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto ecdsa_exit; + } + + r = mbedtls_ecdsa_from_keypair(&ecdsa, mbedtls_pk_ec(pk)); + if (r != 0) + { + r = L8W8JWT_KEY_PARSE_FAILURE; + goto ecdsa_exit; + } + + r = mbedtls_md(md_info, (const unsigned char*)stringbuilder->array, stringbuilder->length, hash); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_SHA2_FAILURE; + goto ecdsa_exit; + } + + r = 0; + switch (alg) + { + case L8W8JWT_ALG_ES256: { + + if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP256R1) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto ecdsa_exit; + } + + signature_bytes_length = 64; + r = mbedtls_pk_get_bitlen(&pk) == 256; + break; + } + case L8W8JWT_ALG_ES256K: { + + if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP256K1) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto ecdsa_exit; + } + + signature_bytes_length = 64; + r = mbedtls_pk_get_bitlen(&pk) == 256; + break; + } + case L8W8JWT_ALG_ES384: { + + if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP384R1) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto ecdsa_exit; + } + + signature_bytes_length = 96; + r = mbedtls_pk_get_bitlen(&pk) == 384; + break; + } + case L8W8JWT_ALG_ES512: { + + if (ecdsa.MBEDTLS_PRIVATE(grp).id != MBEDTLS_ECP_DP_SECP521R1) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto ecdsa_exit; + } + + signature_bytes_length = 132; + r = mbedtls_pk_get_bitlen(&pk) == 521; + break; + } + default: + break; + } + + /* + * Ensure that the passed elliptic-curve cryptography key + * has a size that is valid and compatible with the selected JWT alg. + */ + if (r == 0) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto ecdsa_exit; + } + + r = mbedtls_ecdsa_sign(&ecdsa.MBEDTLS_PRIVATE(grp), &sig_r, &sig_s, &ecdsa.MBEDTLS_PRIVATE(d), hash, md_length, mbedtls_ctr_drbg_random, &ctr_drbg); + if (r != 0) + { + r = L8W8JWT_SIGNATURE_CREATION_FAILURE; + goto ecdsa_exit; + } + + half_signature_bytes_length = signature_bytes_length / 2; + + r = mbedtls_mpi_write_binary(&sig_r, signature_bytes, half_signature_bytes_length); + if (r != 0) + { + r = L8W8JWT_SIGNATURE_CREATION_FAILURE; + goto ecdsa_exit; + } + + r = mbedtls_mpi_write_binary(&sig_s, signature_bytes + half_signature_bytes_length, half_signature_bytes_length); + if (r != 0) + { + r = L8W8JWT_SIGNATURE_CREATION_FAILURE; + goto ecdsa_exit; + } + + r = L8W8JWT_SUCCESS; + + ecdsa_exit: + mbedtls_mpi_free(&sig_r); + mbedtls_mpi_free(&sig_s); + mbedtls_ecdsa_free(&ecdsa); + if (r != L8W8JWT_SUCCESS) + { + goto exit; + } + break; + } + case L8W8JWT_ALG_ED25519: { + +#if L8W8JWT_ENABLE_EDDSA + if (params->secret_key_length != 128 && !(params->secret_key_length == 129 && params->secret_key[128] == 0x00)) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + unsigned char private_key_ref10[64 + 1] = { 0x00 }; + + if (l8w8jwt_hexstr2bin((const char*)params->secret_key, params->secret_key_length, private_key_ref10, sizeof(private_key_ref10), NULL) != 0) + { + r = L8W8JWT_WRONG_KEY_TYPE; + goto exit; + } + + ed25519_sign_ref10(signature_bytes, (const unsigned char*)stringbuilder->array, stringbuilder->length, private_key_ref10); + signature_bytes_length = 64; + + l8w8jwt_zero(private_key_ref10, sizeof(private_key_ref10)); + break; +#else + r = L8W8JWT_UNSUPPORTED_ALG; + goto exit; +#endif + } + default: { + r = L8W8JWT_INVALID_ARG; + goto exit; + } + } + + if (signature_bytes_length == 0) + { + r = L8W8JWT_SIGNATURE_CREATION_FAILURE; + goto exit; + } + + /* + * If this succeeds, it mallocs "signature" and assigns the resulting string length to "signature_length". + */ + r = l8w8jwt_base64_encode(1, (uint8_t*)signature_bytes, signature_bytes_length, &signature, &signature_length); + if (r != L8W8JWT_SUCCESS) + { + r = L8W8JWT_BASE64_FAILURE; + goto exit; + } + + chillbuff_push_back(stringbuilder, ".", 1); + chillbuff_push_back(stringbuilder, signature, signature_length); + +exit: + l8w8jwt_zero(key, L8W8JWT_MAX_KEY_SIZE); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_pk_free(&pk); + l8w8jwt_free(signature); +#if L8W8JWT_SMALL_STACK + l8w8jwt_free(key); + l8w8jwt_free(signature_bytes); +#endif + + return r; +} + +/* Step 3: finalize the token by writing it into the "out" string defined in the l8w8jwt_encoding_params argument. */ +static int write_token(chillbuff* stringbuilder, struct l8w8jwt_encoding_params* params) +{ + *(params->out) = (char*)malloc(stringbuilder->length + 1); + if (*(params->out) == NULL) + { + return L8W8JWT_OUT_OF_MEM; + } + + *(params->out_length) = stringbuilder->length; + (*(params->out))[stringbuilder->length] = '\0'; + memcpy(*(params->out), stringbuilder->array, stringbuilder->length); + + return L8W8JWT_SUCCESS; +} + +void l8w8jwt_encoding_params_init(struct l8w8jwt_encoding_params* params) +{ + if (params == NULL) + { + return; + } + memset(params, 0x00, sizeof(struct l8w8jwt_encoding_params)); + params->alg = -2; +} + +int l8w8jwt_validate_encoding_params(struct l8w8jwt_encoding_params* params) +{ + if (params == NULL || params->secret_key == NULL || params->out == NULL || params->out_length == NULL) + { + return L8W8JWT_NULL_ARG; + } + + if (params->secret_key_length == 0 || params->secret_key_length > L8W8JWT_MAX_KEY_SIZE) + { + return L8W8JWT_INVALID_ARG; + } + + if ((params->additional_payload_claims != NULL && params->additional_payload_claims_count == 0)) + { + return L8W8JWT_INVALID_ARG; + } + + if ((params->additional_header_claims != NULL && params->additional_header_claims_count == 0)) + { + return L8W8JWT_INVALID_ARG; + } + + return L8W8JWT_SUCCESS; +} + +int l8w8jwt_encode(struct l8w8jwt_encoding_params* params) +{ + int r; + chillbuff stringbuilder; + + r = l8w8jwt_validate_encoding_params(params); + if (r != L8W8JWT_SUCCESS) + { + return r; + } + + r = chillbuff_init(&stringbuilder, 1024, sizeof(char), CHILLBUFF_GROW_DUPLICATIVE); + if (r != CHILLBUFF_SUCCESS) + { + return L8W8JWT_OUT_OF_MEM; + } + + r = write_header_and_payload(&stringbuilder, params); + if (r != L8W8JWT_SUCCESS) + { + goto exit; + } + + if (params->alg != -1) + { + r = write_signature(&stringbuilder, params); + if (r != L8W8JWT_SUCCESS) + { + goto exit; + } + } + + r = write_token(&stringbuilder, params); + if (r != L8W8JWT_SUCCESS) + { + goto exit; + } + +exit: + chillbuff_free(&stringbuilder); + return r; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/r5dev/thirdparty/jwt/include/algs.h b/r5dev/thirdparty/jwt/include/algs.h new file mode 100644 index 00000000..63aabbcb --- /dev/null +++ b/r5dev/thirdparty/jwt/include/algs.h @@ -0,0 +1,111 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file algs.h + * @author Raphael Beck + * @brief JWT algorithms as defined in https://tools.ietf.org/html/rfc7518#section-3.1 + */ + +#ifndef L8W8JWT_ALGS_H +#define L8W8JWT_ALGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * HMAC-SHA256 signing algorithm. + */ +#define L8W8JWT_ALG_HS256 0 + +/** + * HMAC-SHA384 signing algorithm. + */ +#define L8W8JWT_ALG_HS384 1 + +/** + * HMAC-SHA512 signing algorithm. + */ +#define L8W8JWT_ALG_HS512 2 + +/** + * RSASSA-PKCS1-v1_5-SHA256 signing algorithm. + */ +#define L8W8JWT_ALG_RS256 3 + +/** + * RSASSA-PKCS1-v1_5-SHA384 signing algorithm. + */ +#define L8W8JWT_ALG_RS384 4 + +/** + * RSASSA-PKCS1-v1_5-SHA512 signing algorithm. + */ +#define L8W8JWT_ALG_RS512 5 + +/** + * RSASSA-PSS MGF1 SHA-256 signing algorithm. + */ +#define L8W8JWT_ALG_PS256 6 + +/** + * RSASSA-PSS MGF1 SHA-384 signing algorithm. + */ +#define L8W8JWT_ALG_PS384 7 + +/** + * RSASSA-PSS MGF1 SHA-512 signing algorithm. + */ +#define L8W8JWT_ALG_PS512 8 + +/** + * ECDSA + P-256 + SHA256 signing algorithm. + */ +#define L8W8JWT_ALG_ES256 9 + +/** + * ECDSA + P-384 + SHA384 signing algorithm. + */ +#define L8W8JWT_ALG_ES384 10 + +/** + * ECDSA + P-521 + SHA512 signing algorithm. + */ +#define L8W8JWT_ALG_ES512 11 + +/** + * ECDSA over secp256k1 + SHA256 signing algorithm. + */ +#define L8W8JWT_ALG_ES256K 12 + +/** + * EdDSA over ed25519 + SHA512 signing algorithm. + */ +#define L8W8JWT_ALG_ED25519 13 + +#ifndef L8W8JWT_ENABLE_EDDSA +/** + * Set this to \c 1 if you want to enable the EdDSA signing algorithm + */ +#define L8W8JWT_ENABLE_EDDSA 0 +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_ALGS_H diff --git a/r5dev/thirdparty/jwt/include/base64.h b/r5dev/thirdparty/jwt/include/base64.h new file mode 100644 index 00000000..2fa71771 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/base64.h @@ -0,0 +1,77 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file base64.h + * @author Raphael Beck + * @brief Base-64 encode and decode strings/bytes.

+ * @warning The caller is responsible for freeing the returned buffers!

+ * Pass true as first parameter if you want to use base64url encoding instead of base64. + * @see https://en.wikipedia.org/wiki/Base64#URL_applications + */ + +#ifndef L8W8JWT_BASE64_H +#define L8W8JWT_BASE64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "version.h" +#include +#include + +/** + * Encodes a byte array to a base-64 string.

+ * If you're encoding a string, don't include the NUL terminator + * (pass strlen(data) instead of the array's size to the data_length parameter).

+ * + * @note The output buffer is NUL-terminated to make it easier to use as a C string. + * @note The NUL terminator is NOT included in the out_length. + * @note DO NOT FORGET to free the output buffer once you're done using it! + * + * @param url base64url encode instead of base64? Set to \c 0 for \c false; anything else for \c true. + * @param data The data (array of bytes) to base-64 encode. + * @param data_length The length of the input data array (in case of a C string: array size - 1 in order to omit the NUL terminator). + * @param out Output where the base-64 encoded string should be written into (will be malloc'ed, so make sure to free() this as soon as you're done using it!). + * @param out_length Pointer to a size_t variable containing the length of the output buffer minus the NUL terminator. + * + * @return Return code as defined in retcodes.h + */ +L8W8JWT_API int l8w8jwt_base64_encode(const int url, const uint8_t* data, const size_t data_length, char** out, size_t* out_length); + +/** + * Decodes a base-64 encoded string to an array of bytes.

+ * + * @note The returned bytes buffer is NUL-terminated to allow usage as a C string. + * @note The NUL terminator is NOT included in the out_length. + * @note DO NOT FORGET to free the output buffer once you're done using it! + * + * @param url Decode using base64url instead of base64? Set to \c 0 for \c false; anything else for \c true. + * @param data The base-64 encoded string to decode (obtained via {@link #l8w8jwt_base64_encode}). + * @param data_length The length of the string to decode. + * @param out Output where the decoded bytes should be written into (will be malloc'ed, so make sure to free() this as soon as you're done using it!). + * @param out_length Pointer to a size_t variable into which to write the output buffer's length. + * + * @return Return code as defined in retcodes.h + */ +L8W8JWT_API int l8w8jwt_base64_decode(const int url, const char* data, const size_t data_length, uint8_t** out, size_t* out_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_BASE64_H diff --git a/r5dev/thirdparty/jwt/include/checknum.h b/r5dev/thirdparty/jwt/include/checknum.h new file mode 100644 index 00000000..c84a32fa --- /dev/null +++ b/r5dev/thirdparty/jwt/include/checknum.h @@ -0,0 +1,141 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file checknum.h + * @author Raphael Beck + * @brief Check whether a given string contains an integer or floating point number. + */ + +/* https://github.com/GlitchedPolygons/checknum */ + +#ifndef CHECKNUM_H +#define CHECKNUM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CHECKNUM_STATIC + #define CHECKNUM_API static +#else + #define CHECKNUM_API extern +#endif + +#include +#include +#include +#include + +/** + * Checks whether a given string contains a valid integer or floating point number.

+ * If it's an integer, 1 is returned.

+ * If it's a float or a double, 2 is returned.

+ * If the string doesn't contain a valid number at all, 0 is returned. + */ +CHECKNUM_API int checknum(char* string, size_t string_length) +{ + if (string == NULL) + return 0; + + if (string_length == 0) + string_length = strlen(string); + + char* c = string; + + while (*c == ' ' && c < string + string_length) + c++; + + while (*(string + string_length - 1) == ' ' && c < string + string_length) + string_length--; + + switch (*c) + { + case '+': + case '-': + if (++c >= string + string_length) + return 0; + default: + break; + } + + unsigned int type = 0; + + if (*c == '0') + { + type |= 1 << 0; + if (*++c != '.' && c < string + string_length) + return 0; + } + + for (; c < string + string_length; c++) + { + switch (*c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + type |= 1 << 0; + continue; + case '-': + if (type & 1 << 1 || (*(c - 1) != 'E' && *(c - 1) != 'e')) + return 0; + type |= 1 << 1; + continue; + case '.': + if (type & 1 << 2 || type & 1 << 3) + return 0; + type |= 1 << 2; + continue; + case 'E': + case 'e': + if (!(type & 1 << 0) || type & 1 << 3 || c + 1 >= string + string_length) + return 0; + type |= 1 << 3; + continue; + case '+': + if (type & 1 << 4 || (*(c - 1) != 'E' && *(c - 1) != 'e')) + return 0; + type |= 1 << 4; + continue; + default: + return 0; + } + } + + switch (type) + { + case 0: + return 0; + case 1 << 0: + return 1; + default: + return type & 1 << 0 ? 2 : 0; + } +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CHECKNUM_H diff --git a/r5dev/thirdparty/jwt/include/chillbuff.h b/r5dev/thirdparty/jwt/include/chillbuff.h new file mode 100644 index 00000000..49deca76 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/chillbuff.h @@ -0,0 +1,326 @@ +/* + Copyright 2019 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file chillbuff.h + * @author Raphael Beck + * @date 27. December 2019 + * @brief Array. Dynamic size. Push back 'n' chill. Buffer stuff. Dynamic stuff that's buff. Dynamically reallocating buff.. Yeah! + * @see https://github.com/GlitchedPolygons/chillbuff + */ + +#ifndef CHILLBUFF_H +#define CHILLBUFF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/* The following are the chillbuff exit codes returned from the various chillbuff functions. */ + +/** + * Returned from a chillbuff function when everything went smooth 'n' chill. Time to get Schwifty! + */ +#define CHILLBUFF_SUCCESS 0 + +/** + * Chill time is over, you're out of memory... Time to reconsider memory usage. + */ +#define CHILLBUFF_OUT_OF_MEM 100 + +/** + * Error code returned by a chillbuff function if you passed a NULL argument that shouldn't have been NULL. + */ +#define CHILLBUFF_NULL_ARG 200 + +/** + * This error code is returned by a chillbuff function if you passed an invalid parameter into it. + */ +#define CHILLBUFF_INVALID_ARG 300 + +/** + * Not good... + */ +#define CHILLBUFF_OVERFLOW 400 + +/** @private */ +static void (*_chillbuff_error_callback)(const char*) = NULL; + +/** + * How should the chillbuff's underlying array grow in size + * once its maximum capacity is reached during a push_back? + */ +typedef enum chillbuff_growth_method +{ + /** + * Double the capacity. + */ + CHILLBUFF_GROW_DUPLICATIVE = 0, + + /** + * Triple the capacity. + */ + CHILLBUFF_GROW_TRIPLICATIVE = 1, + + /** + * Grow by the same capacity every time the buffer is full. + */ + CHILLBUFF_GROW_LINEAR = 2, + + /** + * Multiplies the capacity by itself. Not the greatest idea... Use carefully! + */ + CHILLBUFF_GROW_EXPONENTIAL = 3 +} chillbuff_growth_method; + +/** + * Self-reallocating dynamic size array of no strictly defined type. + * Easy 'n' "chill" (hope you like segmentation fault errors). + */ +typedef struct chillbuff +{ + /** + * The buffer's underlying array that stores the data. + */ + void* array; + + /** + * The current amount of elements stored in the chillbuff. DO NOT touch this yourself, only read! + */ + size_t length; + + /** + * The current buffer capacity. This grows dynamically according to the specified {@link #chillbuff_growth_method}. + */ + size_t capacity; + + /** + * The size of each stored element. DO NOT CHANGE THIS! Only read (if necessary)... + */ + size_t element_size; + + /** + * The way the buffer's capacity is increased when it's full. + */ + chillbuff_growth_method growth_method; +} chillbuff; + +/** @private */ +static inline void _chillbuff_printerr(const char* error, const char* origin) +{ + const size_t error_length = 64 + strlen(error) + strlen(origin); + char* error_msg = (char*)malloc(error_length * sizeof(char)); // cast malloc because of compat with C++ D: + if (error_msg != NULL) + { + snprintf(error_msg, error_length, "\nCHILLBUFF ERROR: (%s) %s\n", origin, error); + if (_chillbuff_error_callback != NULL) + { + _chillbuff_error_callback(error_msg); + } + free(error_msg); + } +} + +/** + * Sets the chillbuff error callback.

+ * If errors occur, they'll be passed as a string into the provided callback function. + * @param error_callback The function to call when errors occur. + * @return Whether the callback was set up correctly or not (chillbuff exit code, see top of chillbuff.h file for more details). + */ +static inline int chillbuff_set_error_callback(void (*error_callback)(const char*)) +{ + if (error_callback == NULL) + { + _chillbuff_printerr("The passed error callback is empty; Operation cancelled!", __func__); + return CHILLBUFF_NULL_ARG; + } + + _chillbuff_error_callback = error_callback; + return CHILLBUFF_SUCCESS; +} + +/** + * Clears the chillbuff error callback (errors won't be printed anymore). + */ +static inline void chillbuff_unset_error_callback() +{ + _chillbuff_error_callback = NULL; +} + +/** + * Initializes a chillbuff instance and makes it ready to accept data. + * @param buff The chillbuff instance to init (or rather, a pointer to it). + * @param initial_capacity The initial capacity of the underlying array. If you pass 0 here, 16 is used by default. + * @param element_size How big should every array element be? E.g. if you're storing int you should pass sizeof(int). + * @param growth_method How should the buffer grow once its maximum capacity is reached? @see chillbuff_growth_method + * @return Chillbuff exit code as defined at the top of the chillbuff.h header file. 0 means success. + */ +static inline int chillbuff_init(chillbuff* buff, const size_t initial_capacity, const size_t element_size, const chillbuff_growth_method growth_method) +{ + if (buff == NULL) + { + _chillbuff_printerr("Tried to init a NULL chillbuff instance; wouldn't end well. Cancelled...", __func__); + return CHILLBUFF_NULL_ARG; + } + + if (element_size == 0) + { + _chillbuff_printerr("Storing elements of size \"0\" makes no sense...", __func__); + return CHILLBUFF_INVALID_ARG; + } + + if (growth_method < 0 || growth_method > 3) + { + _chillbuff_printerr("Invalid grow method! Please use the appropriate chillbuff_growth_method enum!", __func__); + return CHILLBUFF_INVALID_ARG; + } + + buff->length = 0; + buff->element_size = element_size; + buff->growth_method = growth_method; + buff->capacity = initial_capacity == 0 ? 16 : initial_capacity; + buff->array = calloc(buff->capacity, buff->element_size); + + if (buff->array == NULL) + { + _chillbuff_printerr("OUT OF MEMORY!", __func__); + return CHILLBUFF_OUT_OF_MEM; + } + + return CHILLBUFF_SUCCESS; +} + +/** + * Frees a chillbuff instance. + * @param buff The chillbuff to deallocate. If this is NULL, nothing happens at all. + */ +static inline void chillbuff_free(chillbuff* buff) +{ + if (buff == NULL) + { + return; + } + + memset(buff->array, '\0', buff->length); + free(buff->array); + buff->array = NULL; + buff->length = buff->capacity = buff->element_size = 0; +} + +/** + * Clears a chillbuff's data.

+ * Deletes all of the underlying array's elements and resets the length to 0.

+ * Leaves the array allocated at the current capacity. + * @param buff The chillbuff to clear. If this is NULL, nothing happens at all. + */ +static inline void chillbuff_clear(chillbuff* buff) +{ + if (buff == NULL) + { + return; + } + + memset(buff->array, '\0', buff->capacity); + buff->length = 0; +} + +/** + * Appends one or more elements to the buffer. + * If the buffer is full, it will be expanded automatically. + * @param buff The buffer into which to insert the elements. + * @param elements The array of elements to insert (pointer to the first element). + * @param elements_count Amount of elements to add (for example: if your buffer stores the type uint32_t, you'd pass sizeof(elements_to_add) / sizeof(uint32_t) here). If you're only adding a single element, pass 1. + * @return Chillbuff exit code that describes the insertion's outcome. + */ +static int chillbuff_push_back(chillbuff* buff, const void* elements, const size_t elements_count) +{ + if (buff == NULL) + { + _chillbuff_printerr("Tried to append to a NULL chillbuff instance!", __func__); + return CHILLBUFF_NULL_ARG; + } + + if (elements == NULL) + { + _chillbuff_printerr("Tried to append NULL element(s) to a chillbuff instance!", __func__); + return CHILLBUFF_NULL_ARG; + } + + if (elements_count == 0) + { + _chillbuff_printerr("The passed \"elements_count\" argument is zero; nothing to append!", __func__); + return CHILLBUFF_INVALID_ARG; + } + + for (size_t i = 0; i < elements_count; i++) + { + if (buff->length == buff->capacity) + { + size_t new_capacity; + + switch (buff->growth_method) + { + default: + _chillbuff_printerr("Invalid grow method! Please use the appropriate chillbuff_growth_method enum!", __func__); + return CHILLBUFF_INVALID_ARG; + case CHILLBUFF_GROW_DUPLICATIVE: + new_capacity = (buff->capacity * 2); + break; + case CHILLBUFF_GROW_TRIPLICATIVE: + new_capacity = (buff->capacity * 3); + break; + case CHILLBUFF_GROW_LINEAR: + new_capacity = (buff->capacity + buff->element_size); + break; + case CHILLBUFF_GROW_EXPONENTIAL: + new_capacity = (buff->capacity * buff->capacity); + break; + } + + if (new_capacity <= buff->capacity || new_capacity >= UINT64_MAX / buff->element_size) + { + _chillbuff_printerr("Couldn't push back due to buffer OVERFLOW!", __func__); + return CHILLBUFF_OVERFLOW; + } + + void* new_array = realloc(buff->array, new_capacity * buff->element_size); + if (new_array == NULL) + { + _chillbuff_printerr("Couldn't resize chillbuff underlying array; OUT OF MEMORY!", __func__); + return CHILLBUFF_OUT_OF_MEM; + } + + memset((char*)new_array + (buff->element_size * buff->length), '\0', (new_capacity - buff->length) * buff->element_size); + buff->array = new_array; + buff->capacity = new_capacity; + } + + memcpy((char*)buff->array + (buff->element_size * buff->length++), (char*)elements + (i * buff->element_size), buff->element_size); + } + + return CHILLBUFF_SUCCESS; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CHILLBUFF_H diff --git a/r5dev/thirdparty/jwt/include/claim.h b/r5dev/thirdparty/jwt/include/claim.h new file mode 100644 index 00000000..b9bf18d2 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/claim.h @@ -0,0 +1,146 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file claim.h + * @author Raphael Beck + * @brief JWT claims as described in https://auth0.com/docs/tokens/concepts/jwt-claims + */ + +#ifndef L8W8JWT_CLAIM_H +#define L8W8JWT_CLAIM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "version.h" +#include + +// Forward declare chillbuff +/** @private */ +struct chillbuff; + +/** + * JWT claim value is a string (e.g. "iss": "glitchedpolygons.com"). + */ +#define L8W8JWT_CLAIM_TYPE_STRING 0 + +/** + * JWT claim value is an integer (e.g. "exp": 1579610629) + */ +#define L8W8JWT_CLAIM_TYPE_INTEGER 1 + +/** + * JWT claim value type number (e.g. "size": 1.85). + */ +#define L8W8JWT_CLAIM_TYPE_NUMBER 2 + +/** + * JWT claim value is a boolean (e.g. "done": true). + */ +#define L8W8JWT_CLAIM_TYPE_BOOLEAN 3 + +/** + * JWT claim value is null (e.g. "ref": null). + */ +#define L8W8JWT_CLAIM_TYPE_NULL 4 + +/** + * JWT claim value type JSON array (e.g. "ids": [2, 4, 8, 16]). + */ +#define L8W8JWT_CLAIM_TYPE_ARRAY 5 + +/** + * JWT claim value type is a JSON object (e.g. "objs": { "name": "GMan", "id": 420 }). + */ +#define L8W8JWT_CLAIM_TYPE_OBJECT 6 + +/** + * JWT claim value is some other type. + */ +#define L8W8JWT_CLAIM_TYPE_OTHER 7 + +/** + * Struct containing a jwt claim key-value pair.

+ * If allocated on the heap by the decode function, + * remember to call l8w8jwt_claims_free() on it once you're done using it. + */ +struct l8w8jwt_claim +{ + /** + * The token claim key (e.g. "iss", "iat", "sub", etc...).

+ * NUL-terminated C-string! + */ + char* key; + + /** + * key string length.

+ * Set this to 0 if you want to make the encoder use strlen(key) instead. + */ + size_t key_length; + + /** + * The claim's value as a NUL-terminated C-string. + */ + char* value; + + /** + * value string length.

+ * Set this to 0 if you want to make the encoder use strlen(value) instead. + */ + size_t value_length; + + /** + * The type of the claim's value.

+ * 0 = string, 1 = integer, 2 = number, 3 = boolean, 4 = null, 5 = array, 6 = object, 7 = other. + * @see https://www.w3schools.com/js/js_json_datatypes.asp + */ + int type; +}; + +/** + * Frees a heap-allocated l8w8jwt_claim array. + * @param claims The claims to free. + * @param claims_count The size of the passed claims array. + */ +L8W8JWT_API void l8w8jwt_free_claims(struct l8w8jwt_claim* claims, const size_t claims_count); + +/** + * Writes a bunch of JWT claims into a chillbuff stringbuilder.

+ * Curly braces and trailing commas won't be written; only the "key":"value" pairs! + * @param stringbuilder The buffer into which to write the claims. + * @param claims The l8w8jwt_claim array of claims to write. + * @param claims_count The claims array size. + * @return Return code as specified inside retcodes.h + */ +L8W8JWT_API int l8w8jwt_write_claims(struct chillbuff* stringbuilder, struct l8w8jwt_claim* claims, const size_t claims_count); + +/** + * Gets a claim by key from a l8w8jwt_claim array. + * @param claims The array to look in. + * @param claims_count The claims array size. + * @param key The claim key (e.g. "sub") to look for. + * @param key_length The claim key's string length. + * @return The found claim; NULL if no such claim was found in the array. + */ +L8W8JWT_API struct l8w8jwt_claim* l8w8jwt_get_claim(struct l8w8jwt_claim* claims, const size_t claims_count, const char* key, const size_t key_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_CLAIM_H diff --git a/r5dev/thirdparty/jwt/include/decode.h b/r5dev/thirdparty/jwt/include/decode.h new file mode 100644 index 00000000..dd9ac563 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/decode.h @@ -0,0 +1,300 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file decode.h + * @author Raphael Beck + * @brief Core DECODE function for l8w8jwt. Use this to decode and validate a JWT! + */ + +#ifndef L8W8JWT_DECODE_H +#define L8W8JWT_DECODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "algs.h" +#include "claim.h" +#include "version.h" +#include "retcodes.h" +#include +#include +#include +#include + +#ifndef L8W8JWT_MAX_KEY_SIZE +#define L8W8JWT_MAX_KEY_SIZE 8192 +#endif + +/** + * Enum containing the validation result flags. + */ +enum l8w8jwt_validation_result { + /** + * The JWT is valid (according to the passed validation parameters). + */ + L8W8JWT_VALID = (unsigned)0, + + /** + * The issuer claim is invalid. + */ + L8W8JWT_ISS_FAILURE = (unsigned)1 << (unsigned)0, + + /** + * The subject claim is invalid. + */ + L8W8JWT_SUB_FAILURE = (unsigned)1 << (unsigned)1, + + /** + * The audience claim is invalid. + */ + L8W8JWT_AUD_FAILURE = (unsigned)1 << (unsigned)2, + + /** + * The JWT ID claim is invalid. + */ + L8W8JWT_JTI_FAILURE = (unsigned)1 << (unsigned)3, + + /** + * The token is expired. + */ + L8W8JWT_EXP_FAILURE = (unsigned)1 << (unsigned)4, + + /** + * The token is not yet valid. + */ + L8W8JWT_NBF_FAILURE = (unsigned)1 << (unsigned)5, + + /** + * The token was not issued yet, are you from the future? + */ + L8W8JWT_IAT_FAILURE = (unsigned)1 << (unsigned)6, + + /** + * The token was potentially tampered with: its signature couldn't be verified. + */ + L8W8JWT_SIGNATURE_VERIFICATION_FAILURE = (unsigned)1 << (unsigned)7, + + /** + * The token's "typ" claim validation failed. + */ + L8W8JWT_TYP_FAILURE = (unsigned)1 << (unsigned)8 +}; + +static void l8w8jwt_get_validation_result_desc(enum l8w8jwt_validation_result res, char* out_buffer, size_t buffer_size) +{ +#define JWT_OUTPUT_MSG(msg) snprintf(out_buffer, buffer_size, "%s", msg) +#define JWT_FLAG_STATUS(flag, msg) if(res & flag) { JWT_OUTPUT_MSG(msg); return; } + + if (res == L8W8JWT_VALID) + { + JWT_OUTPUT_MSG("Success"); + return; + } + + JWT_FLAG_STATUS(L8W8JWT_ISS_FAILURE, "Issuer claim is invalid"); + JWT_FLAG_STATUS(L8W8JWT_SUB_FAILURE, "Subject claim is invalid"); + JWT_FLAG_STATUS(L8W8JWT_AUD_FAILURE, "Audience claim is invalid"); + JWT_FLAG_STATUS(L8W8JWT_JTI_FAILURE, "JWT ID claim is invalid"); + JWT_FLAG_STATUS(L8W8JWT_EXP_FAILURE, "Token has expired"); + JWT_FLAG_STATUS(L8W8JWT_NBF_FAILURE, "Token is not yet valid"); + JWT_FLAG_STATUS(L8W8JWT_IAT_FAILURE, "Token has not been issued yet"); + JWT_FLAG_STATUS(L8W8JWT_SIGNATURE_VERIFICATION_FAILURE, "Token signature is invalid"); + JWT_FLAG_STATUS(L8W8JWT_TYP_FAILURE, "Token type is invalid"); + +#undef JWT_OUTPUT_MSG +#undef JWT_FLAG_STATUS +} + +/** + * Struct containing the parameters to use for decoding and validating a JWT. + */ +struct l8w8jwt_decoding_params +{ + /** + * The token to decode and validate. + */ + char* jwt; + + /** + * The jwt string length. + */ + size_t jwt_length; + + /** + * The signature algorithm ID.

+ * [0;2] = HS256/384/512 | [3;5] = RS256/384/512 | [6;8] = PS256/384/512 | [9;11] = ES256/384/512

+ * This affects what should be the value of {@link #verification_key} + */ + int alg; + + /** + * [OPTIONAL] The issuer claim (who issued the JWT?).

+ * Set to NULL if you don't want to validate the issuer.

+ * The JWT will only pass verification if its iss claim matches this string. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.1 + */ + char* validate_iss; + + /** + * validate_iss string length. + */ + size_t validate_iss_length; + + /** + * [OPTIONAL] The subject claim (who is the JWT about?).

+ * Set to NULL if you don't want to validate the subject claim.

+ * The JWT will only pass verification if its sub matches this string. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.2 + */ + char* validate_sub; + + /** + * validate_sub string length. + */ + size_t validate_sub_length; + + /** + * [OPTIONAL] The audience claim (who is the JWT intended for? Who is the intended JWT's recipient?).

+ * Set to NULL if you don't want to validate the audience.

+ * The JWT will only pass verification if its aud matches this string. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.3 + */ + char* validate_aud; + + /** + * validate_aud string length. + */ + size_t validate_aud_length; + + /** + * [OPTIONAL] The JWT ID. Provides a unique identifier for the token.

+ * Set to NULL if you don't want to validate the jti claim.

+ * The JWT will only pass verification if its jti matches this string. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.7 + */ + char* validate_jti; + + /** + * validate_jti claim length. + */ + size_t validate_jti_length; + + /** + * Should the expiration claim be verified? + * If this is set to 1, the exp claim will be compared to the current date and time + {@link #exp_tolerance_seconds} + */ + int validate_exp; + + /** + * Should the "not before" claim be verified? + * If this is set to 1, the nbf claim will be compared to the current date and time + {@link #nbf_tolerance_seconds} + */ + int validate_nbf; + + /** + * Should the "issued at" claim be verified? + * If this is set to 1, the iat claim will be compared to the current date and time + {@link #iat_tolerance_seconds} + */ + int validate_iat; + + /** + * Small inconsistencies in time can happen, or also latency between clients and servers. + * That's just life. You can forgive a few seconds of expiration, but don't exaggerate this!

+ * Only taken into consideration if {@link #validate_exp} is set to 1. + */ + uint8_t exp_tolerance_seconds; + + /** + * The amount of seconds to subtract from the current time when comparing the "not before" claim, to allow for a small tolerance time frame. + * Only taken into consideration if {@link #validate_nbf} is set to 1. + */ + uint8_t nbf_tolerance_seconds; + + /** + * The amount of seconds to subtract from the current time when comparing the "issued at" claim, to allow for a small tolerance time frame. + * Only taken into consideration if {@link #validate_iat} is set to 1. + */ + uint8_t iat_tolerance_seconds; + + /** + * The key to use for verifying the token's signature + * (e.g. if you chose HS256 as algorithm, this will be the HMAC secret; for RS512 this will be the PEM-formatted public RSA key string, etc...). + */ + unsigned char* verification_key; + + /** + * Length of the {@link #verification_key} + */ + size_t verification_key_length; + + /** + * [OPTIONAL] The typ claim (what type is the token?).

+ * Set to NULL if you don't want to validate the "typ" claim.

+ */ + char* validate_typ; + + /** + * validate_typ string length. + */ + size_t validate_typ_length; +}; + +/** + * Initializes a {@link #l8w8jwt_decoding_params} instance by setting its fields to default values. + * @param params The l8w8jwt_decoding_params to initialize (set to default values). + */ +L8W8JWT_API void l8w8jwt_decoding_params_init(struct l8w8jwt_decoding_params* params); + +/** + * Validates a set of l8w8jwt_decoding_params. + * @param params The l8w8jwt_decoding_params to validate. + * @return Return code as defined in retcodes.h + */ +L8W8JWT_API int l8w8jwt_validate_decoding_params(struct l8w8jwt_decoding_params* params); + +/** + * Decode (and validate) a JWT using specific parameters.

+ * The resulting {@link #l8w8jwt_validation_result} written into the passed "out_validation_result" pointer + * contains validation failure flags (see the {@link #l8w8jwt_validation_result} enum docs for more details).

+ * This only happens if decoding also succeeded: if the token is malformed, nothing will be written into "out_validation_result".

+ * If validation succeeds, the {@link #l8w8jwt_validation_result} receives the value 0 (enum value L8W8JWT_VALID).

+ * The same applies to the "out_claims" argument: it is only allocated and written to if it (obviously) isn't NULL and if the decoding was also successful! + * + * @param params The parameters to use for decoding and validating the token. + * + * @param out_validation_result Where to write the validation result flags into (0 means success). In case of a decoding failure this is set to -1 (or ~L8W8JWT_VALID)! + * + * @param out_claims + * [OPTIONAL] Where the decoded claims (header + payload claims together) should be written into. + * This pointer will be dereferenced + allocated, so make sure to pass a fresh pointer! + * If you don't need the claims, set this to NULL (they will only be validated, e.g. signature, exp, etc...). + * Check the note down below for more infos! + * + * @param out_claims_length Where to write the decoded claims count into. This will receive the value of how many claims were written into "out_claims" (0 if you decided to set "out_claims" to NULL). + * + * @note If you decide to keep the claims stored in the out_claims parameter, REMEMBER to call {@link #l8w8jwt_free_claims()} on it once you're done using them! + * + * @return Return code as defined in retcodes.h (this is NOT the validation result that's written into the out_validation_result argument; the returned int describes whether the actual parsing/decoding part failed). + */ +L8W8JWT_API int l8w8jwt_decode(struct l8w8jwt_decoding_params* params, enum l8w8jwt_validation_result* out_validation_result, struct l8w8jwt_claim** out_claims, size_t* out_claims_length); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_DECODE_H diff --git a/r5dev/thirdparty/jwt/include/encode.h b/r5dev/thirdparty/jwt/include/encode.h new file mode 100644 index 00000000..a1b8e1c9 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/encode.h @@ -0,0 +1,206 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file encode.h + * @author Raphael Beck + * @brief Core ENCODE function for l8w8jwt. Use this to encode a JWT header + payload WITHOUT signing. + */ + +#ifndef L8W8JWT_ENCODE_H +#define L8W8JWT_ENCODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "algs.h" +#include "claim.h" +#include "version.h" +#include "retcodes.h" +#include +#include + +#ifndef L8W8JWT_MAX_KEY_SIZE +#define L8W8JWT_MAX_KEY_SIZE 8192 +#endif + +/** + * Struct containing the parameters to use for creating a JWT with l8w8jwt. + */ +L8W8JWT_API struct l8w8jwt_encoding_params +{ + /** + * The signature algorithm ID.

+ * [0;2] = HS256/384/512 | [3;5] = RS256/384/512 | [6;8] = PS256/384/512 | [9;11] = ES256/384/512 + */ + int alg; + + /** + * [OPTIONAL] The issuer claim (who issued the JWT?). Can be omitted by setting this to NULL. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.1 + */ + char* iss; + + /** + * iss claim string length. + */ + size_t iss_length; + + /** + * [OPTIONAL] The subject claim (who is the JWT about?). Set to NULL if you don't want it in your token. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.2 + */ + char* sub; + + /** + * sub claim string length. + */ + size_t sub_length; + + /** + * [OPTIONAL] The audience claim (who is the JWT intended for? Who is the intended JWT's recipient?). + * Set this to NULL if you don't wish to add this claim to the token. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.3 + */ + char* aud; + + /** + * aud claim string length. + */ + size_t aud_length; + + /** + * [OPTIONAL] The JWT ID. Provides a unique identifier for the token. Can be omitted by setting this to NULL. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.7 + */ + char* jti; + + /** + * jti claim string length. + */ + size_t jti_length; + + /** + * Expiration time claim; specifies when this token should stop being valid (in seconds since Unix epoch).

+ * If you want to omit this, set this to 0, but do NOT FORGET to set it to something, + * otherwise it will be set to whatever random value was in the memory where this variable resides. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.4 + */ + time_t exp; + + /** + * "Not before" time claim; specifies when this token should start being valid (in seconds since Unix epoch).

+ * If you want to omit this, set this to 0, but do NOT FORGET to set it to something, + * otherwise it will be set to whatever random value was in the memory where this variable resides. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.5 + */ + time_t nbf; + + /** + * "Issued at" timestamp claim; specifies when this token was emitted (in seconds since Unix epoch).

+ * If you want to omit this, set this to 0, but do NOT FORGET to set it to something, + * otherwise it will be set to whatever random value was in the memory where this variable resides. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.6 + */ + time_t iat; + + /** + * [OPTIONAL] Array of additional claims to include in the JWT's header like for example "kid" or "cty"; pass NULL if you don't wish to add any!

+ * Avoid header claims such as typ and alg, since those are written by the encoding function itself. + * @see https://tools.ietf.org/html/rfc7519#section-4.1.7 + */ + struct l8w8jwt_claim* additional_header_claims; + + /** + * [OPTIONAL] The additional_header_claims array size; pass 0 if you don't wish to include any custom claims! + */ + size_t additional_header_claims_count; + + /** + * [OPTIONAL] Array of additional claims to include in the JWT's payload; pass NULL if you don't wish to add any!

+ * Registered claim names such as "iss", "exp", etc... have their own dedicated field within this struct: do not include those in this array to prevent uncomfortable duplicates! + * @see https://tools.ietf.org/html/rfc7519#section-4 + */ + struct l8w8jwt_claim* additional_payload_claims; + + /** + * [OPTIONAL] The additional_payload_claims array size; pass 0 if you don't wish to include any custom claims! + */ + size_t additional_payload_claims_count; + + /** + * The secret key to use for signing the token + * (e.g. if you chose HS256 as algorithm, this will be the HMAC secret; for RS512 this will be the private PEM-formatted RSA key string, and so on...). + */ + unsigned char* secret_key; + + /** + * Length of the secret_key + */ + size_t secret_key_length; + + /** + * If the secret key requires a password for usage, please assign it to this field.

+ * You can only omit this when using JWT algorithms "HS256", "HS384" or "HS512" (it's ignored in that case actually).

+ * Every other algorithm requires you to at least set this to NULL if the {@link #secret_key} isn't password-protected. + */ + unsigned char* secret_key_pw; + + /** + * The secret key's password length (if there is any). If there's none, set this to zero! + */ + size_t secret_key_pw_length; + + /** + * Where the encoded token should be written into + * (will be malloc'ed, so make sure to l8w8jwt_free() this as soon as you're done using it!). + */ + char** out; + + /** + * Where the output token string length should be written into. + */ + size_t* out_length; +}; + +/** + * Initializes a {@link #l8w8jwt_encoding_params} instance by setting its fields to default values. + * @param params The l8w8jwt_encoding_params to initialize (set to default values). + */ +L8W8JWT_API void l8w8jwt_encoding_params_init(struct l8w8jwt_encoding_params* params); + +/** + * Validates a set of l8w8jwt_encoding_params. + * @param params The l8w8jwt_encoding_params to validate. + * @return Return code as defined in retcodes.h + */ +L8W8JWT_API int l8w8jwt_validate_encoding_params(struct l8w8jwt_encoding_params* params); + +/** + * Creates, signs and encodes a Json-Web-Token.

+ * An example output could be: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbWUta2V5LWlkLWhlcmUtMDEyMzQ1NiJ9.eyJpYXQiOjE1Nzk2NDUzNTUsImV4cCI6MTU3OTY0NTk1NSwic3ViIjoiR29yZG9uIEZyZWVtYW4iLCJpc3MiOiJCbGFjayBNZXNhIiwiYXVkIjoiQWRtaW5pc3RyYXRvciJ9.uk4EEoq0ql_SguLto5EWzklakpzO-6GE2U26crB8vUY

+ * @param params The token encoding parameters (e.g. "alg", "iss", "exp", etc...). + * @return Return code as defined in retcodes.h + * @see l8w8jwt_encoding_params + */ +L8W8JWT_API int l8w8jwt_encode(struct l8w8jwt_encoding_params* params); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_ENCODE_H diff --git a/r5dev/thirdparty/jwt/include/jsmn.h b/r5dev/thirdparty/jwt/include/jsmn.h new file mode 100644 index 00000000..8ac14c1b --- /dev/null +++ b/r5dev/thirdparty/jwt/include/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/r5dev/thirdparty/jwt/include/retcodes.h b/r5dev/thirdparty/jwt/include/retcodes.h new file mode 100644 index 00000000..fe3bef71 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/retcodes.h @@ -0,0 +1,107 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file retcodes.h + * @author Raphael Beck + * @brief Macros for possible integer codes returned by the various l8w8jwt functions. + */ + +#ifndef L8W8JWT_RETCODES_H +#define L8W8JWT_RETCODES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Returned from a l8w8jwt function when everything went smooth 'n' chill. Time to get Schwifty, Morteyy! + */ +#define L8W8JWT_SUCCESS 0 + +/** + * Error code returned by a l8w8jwt function if you passed a NULL argument that shouldn't have been NULL. + */ +#define L8W8JWT_NULL_ARG 100 + +/** + * This error code is returned by a l8w8jwt function if you passed an invalid parameter into it. + */ +#define L8W8JWT_INVALID_ARG 200 + +/** + * This is returned if some allocation inside a l8w8jwt function failed: you're out of memory at this point. + */ +#define L8W8JWT_OUT_OF_MEM 300 + +/** + * Not good... + */ +#define L8W8JWT_OVERFLOW 310 + +/** + * Returned if signing a JWT failed. + */ +#define L8W8JWT_SIGNATURE_CREATION_FAILURE 400 + +/** + * If one of the SHA-2 functions fails (e.g. SHA-256). + */ +#define L8W8JWT_SHA2_FAILURE 410 + +/** + * Returned if some PEM-formatted key string couldn't be parsed. + */ +#define L8W8JWT_KEY_PARSE_FAILURE 420 + +/** + * Base64(URL) encoding or decoding error. + */ +#define L8W8JWT_BASE64_FAILURE 425 + +/** + * Returned if you passed the wrong private or public key type (e.g. trying to use an RSA key for ECDSA tokens, etc...).

+ * Especially for the ECDSA algorithms like ES256, ES384 and ES512 double-check that you passed keys of the correct curve!

+ * Only use the P-256 curve for ES256, P-384 (a.k.a. secp384r1) for ES384 and P-521 (a.k.a. secp521r1) for ES512. + */ +#define L8W8JWT_WRONG_KEY_TYPE 450 + +/** + * When the mbedtls_ctr_drbg_seed() function fails... + */ +#define L8W8JWT_MBEDTLS_CTR_DRBG_SEED_FAILURE 500 + +/** + * Returned if the token is invalid (format-wise). + */ +#define L8W8JWT_DECODE_FAILED_INVALID_TOKEN_FORMAT 600 + +/** + * Returned if the token is invalid because it's missing the signature (despite having specified an alg that isn't "none"). + */ +#define L8W8JWT_DECODE_FAILED_MISSING_SIGNATURE 700 + +/** + * Returned if the JWT signing alg parameter that was passed is not supported (e.g. the used l8w8jwt library was built without support for that algo, e.g. Ed25519). + * See the README.md for more details! + */ +#define L8W8JWT_UNSUPPORTED_ALG 800 + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_RETCODES_H diff --git a/r5dev/thirdparty/jwt/include/util.h b/r5dev/thirdparty/jwt/include/util.h new file mode 100644 index 00000000..ef986795 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/util.h @@ -0,0 +1,58 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file util.h + * @author Raphael Beck + * @brief Useful utility functions. + */ + +#ifndef L8W8JWT_UTIL_H +#define L8W8JWT_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "version.h" + +/** + * Converts a hex-encoded string to a binary array.

+ * A NUL-terminator is appended at the end of the output buffer, so make sure to allocate at least (hexstr_length / 2) + 1 bytes! + * @param hexstr The hex string to convert. + * @param hexstr_length Length of the \p hexstr + * @param output Where to write the converted binary data into. + * @param output_size Size of the output buffer (make sure to allocate at least (hexstr_length / 2) + 1 bytes!). + * @param output_length [OPTIONAL] Where to write the output array length into. This is always gonna be hexstr_length / 2, but you can still choose to write it out just to be sure. If you want to omit this: no problem.. just pass NULL! + * @return 0 if conversion succeeded. 1 if one or more required arguments were NULL or invalid. 2 if the hexadecimal string is in an invalid format (e.g. not divisible by 2). 3 if output buffer size was insufficient (needs to be at least (hexstr_length / 2) + 1 bytes). + */ +L8W8JWT_API int l8w8jwt_hexstr2bin(const char* hexstr, const size_t hexstr_length, unsigned char* output, const size_t output_size, size_t* output_length); + +/** + * Compares two strings ignoring UPPER vs. lowercase. + * @param str1 String to compare. + * @param str2 String to compare to. + * @param n How many characters of the string should be compared (starting from index 0)? + * @return If the strings are equal, 0 is returned. Otherwise, something else. + */ +L8W8JWT_API int l8w8jwt_strncmpic(const char* str1, const char* str2, size_t n); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_UTIL_H \ No newline at end of file diff --git a/r5dev/thirdparty/jwt/include/version.h b/r5dev/thirdparty/jwt/include/version.h new file mode 100644 index 00000000..9fce28a0 --- /dev/null +++ b/r5dev/thirdparty/jwt/include/version.h @@ -0,0 +1,87 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file version.h + * @author Raphael Beck + * @brief l8w8jwt version checking. + */ + +#ifndef L8W8JWT_VERSION_H +#define L8W8JWT_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Current l8w8jwt version number. + */ +#define L8W8JWT_VERSION 220 + +/** + * Current l8w8jwt version number (as a human-readable string). + */ +#define L8W8JWT_VERSION_STR "2.2.0" + +#if defined(_WIN32) && defined(L8W8JWT_DLL) +#ifdef L8W8JWT_BUILD_DLL +#define L8W8JWT_API __declspec(dllexport) +#else +#define L8W8JWT_API __declspec(dllimport) +#endif +#else +#define L8W8JWT_API +#endif + +#ifndef L8W8JWT_SMALL_STACK +/** + * Set this pre-processor definition to \c 1 if you're using this + * on a low-memory device with increased risk of stack overflow. + */ +#define L8W8JWT_SMALL_STACK 0 +#endif + +/** + * Free memory that was allocated by L8W8JWT. + * @param mem The memory to free. + */ +L8W8JWT_API void l8w8jwt_free(void* mem); + +/** + * Zero memory securely. + * @param mem The memory to zero. + * @param len The length to zero. + */ +L8W8JWT_API void l8w8jwt_zero(void* buf, size_t len); + +/** + * Gets the l8w8jwt version number as an integer. + * @return Version number (e.g. "2.1.4" => 214) + */ +L8W8JWT_API int l8w8jwt_get_version_number(void); + +/** + * Gets the l8w8jwt version number as a nicely formatted string. + * @param out A writable \c char buffer of at least 32B where to write the version number string into. The string will be NUL-terminated, no worries! Passing \c NULL here is a very bad idea. Undefined, unpleasant, and just... just don't! + */ +L8W8JWT_API void l8w8jwt_get_version_string(char out[32]); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // L8W8JWT_VERSION_H diff --git a/r5dev/thirdparty/jwt/util.c b/r5dev/thirdparty/jwt/util.c new file mode 100644 index 00000000..686659e9 --- /dev/null +++ b/r5dev/thirdparty/jwt/util.c @@ -0,0 +1,87 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "include/util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int l8w8jwt_hexstr2bin(const char* hexstr, const size_t hexstr_length, unsigned char* output, const size_t output_size, size_t* output_length) +{ + if (hexstr == NULL || output == NULL || hexstr_length == 0) + { + return 1; + } + + const size_t hl = hexstr[hexstr_length - 1] ? hexstr_length : hexstr_length - 1; + + if (hl % 2 != 0) + { + return 2; + } + + const size_t final_length = hl / 2; + + if (output_size < final_length + 1) + { + return 3; + } + + for (size_t i = 0, ii = 0; ii < final_length; i += 2, ii++) + { + output[ii] = (hexstr[i] % 32 + 9) % 25 * 16 + (hexstr[i + 1] % 32 + 9) % 25; + } + + output[final_length] = '\0'; + + if (output_length != NULL) + { + *output_length = final_length; + } + + return 0; +} + +int l8w8jwt_strncmpic(const char* str1, const char* str2, size_t n) +{ + size_t cmp = 0; + int ret = -1; + + if (str1 == NULL || str2 == NULL) + { + return ret; + } + + while ((*str1 || *str2) && cmp < n) + { + if ((ret = tolower((int)(*str1)) - tolower((int)(*str2))) != 0) + { + break; + } + + cmp++; + str1++; + str2++; + } + + return ret; +} + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/r5dev/thirdparty/jwt/version.c b/r5dev/thirdparty/jwt/version.c new file mode 100644 index 00000000..62604c3a --- /dev/null +++ b/r5dev/thirdparty/jwt/version.c @@ -0,0 +1,75 @@ +/* + Copyright 2020 Raphael Beck + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "include/version.h" +#include +#include + +#if defined(_WIN32) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void l8w8jwt_free(void* mem) +{ + free(mem); +} + +void l8w8jwt_zero(void* buf, size_t len) +{ + if (len > 0) + { +#if defined(MBEDTLS_PLATFORM_HAS_EXPLICIT_BZERO) + explicit_bzero(buf, len); +#if defined(HAVE_MEMORY_SANITIZER) + /* You'd think that Msan would recognize explicit_bzero() as + * equivalent to bzero(), but it actually doesn't on several + * platforms, including Linux (Ubuntu 20.04). + * https://github.com/google/sanitizers/issues/1507 + * https://github.com/openssh/openssh-portable/commit/74433a19bb6f4cef607680fa4d1d7d81ca3826aa + */ + __msan_unpoison(buf, len); +#endif +#elif defined(__STDC_LIB_EXT1__) + memset_s(buf, len, 0, len); +#elif defined(_WIN32) + RtlSecureZeroMemory(buf, len); +#else + memset_func(buf, 0, len); +#endif + } +} + +int l8w8jwt_get_version_number() +{ + return (int)L8W8JWT_VERSION; +} + +void l8w8jwt_get_version_string(char out[32]) +{ + const char version_string[] = L8W8JWT_VERSION_STR; + const size_t version_string_length = sizeof(version_string) - 1; + + memcpy(out, version_string, version_string_length); + out[version_string_length] = '\0'; +} + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/r5dev/thirdparty/lz4/CMakeLists.txt b/r5dev/thirdparty/lz4/CMakeLists.txt new file mode 100644 index 00000000..6a5ef9da --- /dev/null +++ b/r5dev/thirdparty/lz4/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required( VERSION 3.16 ) +add_module( "lib" "liblz4" "" ${FOLDER_CONTEXT} TRUE TRUE ) + +start_sources() + +add_sources( SOURCE_GROUP "Source" + "lz4hc.c" + "lz4frame.c" + "lz4file.c" + "lz4.c" +) + +add_sources( SOURCE_GROUP "Include" + "xxhash.h" + "lz4hc.h" + "lz4frame_static.h" + "lz4frame.h" + "lz4file.h" + "lz4.h" +) + +end_sources() + +whole_program_optimization() +thirdparty_suppress_warnings() + +# target_compile_definitions( ${PROJECT_NAME} PRIVATE +# ) diff --git a/r5dev/thirdparty/lz4/LICENSE b/r5dev/thirdparty/lz4/LICENSE new file mode 100644 index 00000000..48849169 --- /dev/null +++ b/r5dev/thirdparty/lz4/LICENSE @@ -0,0 +1,24 @@ +LZ4 Library +Copyright (c) 2011-2020, Yann Collet +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/r5dev/thirdparty/lz4/NEWS b/r5dev/thirdparty/lz4/NEWS new file mode 100644 index 00000000..0a569921 --- /dev/null +++ b/r5dev/thirdparty/lz4/NEWS @@ -0,0 +1,341 @@ +v1.9.4 +perf : faster decoding speed (~+20%) on aarch64 platforms +perf : faster decoding speed (~+70%) for -BD4 setting in CLI +api : new function `LZ4_decompress_safe_partial_usingDict()` by @yawqi +api : lz4frame: ability to provide custom allocators at state creation +api : can skip checksum validation for improved decoding speed +api : new experimental unit `lz4file` for file i/o API, by @anjiahao1 +api : new experimental function `LZ4F_uncompressedUpdate()`, by @alexmohr +cli : `--list` works on `stdin` input, by @Low-power +cli : `--no-crc` does not produce (compression) nor check (decompression) checksums +cli : fix: `--test` and `--list` produce an error code when parsing invalid input +cli : fix: support skippable frames when passed via `stdin`, reported by @davidmankin +build: fix: Makefile respects CFLAGS directives passed via environment variable +build: `LZ4_FREESTANDING`, new build macro for freestanding environments, by @t-mat +build: `make` and `make test` are compatible with `-j` parallel run +build: AS/400 compatibility, by @jonrumsey +build: Solaris 10 compatibility, by @pekdon +build: MSVC 2022 support, by @t-mat +build: improved meson script, by @eli-schwartz +doc : Updated LZ4 block format, provide an "implementation notes" section + +v1.9.3 +perf: highly improved speed in kernel space, by @terrelln +perf: faster speed with Visual Studio, thanks to @wolfpld and @remittor +perf: improved dictionary compression speed, by @felixhandte +perf: fixed LZ4_compress_HC_destSize() ratio, detected by @hsiangkao +perf: reduced stack usage in high compression mode, by @Yanpas +api : LZ4_decompress_safe_partial() supports unknown compressed size, requested by @jfkthame +api : improved LZ4F_compressBound() with automatic flushing, by Christopher Harvie +api : can (de)compress to/from NULL without UBs +api : fix alignment test on 32-bit systems (state initialization) +api : fix LZ4_saveDictHC() in corner case scenario, detected by @IgorKorkin +cli : `-l` legacy format is now compatible with `-m` multiple files, by Filipe Calasans +cli : benchmark mode supports dictionary, by @rkoradi +cli : fix --fast with large argument, detected by @picoHz +build: link to user-defined memory functions with LZ4_USER_MEMORY_FUNCTIONS, suggested by Yuriy Levchenko +build: contrib/cmake_unofficial/ moved to build/cmake/ +build: visual/* moved to build/ +build: updated meson script, by @neheb +build: tinycc support, by Anton Kochkov +install: Haiku support, by Jerome Duval +doc : updated LZ4 frame format, clarify EndMark + +v1.9.2 +fix : out-of-bound read in exceptional circumstances when using decompress_partial(), by @terrelln +fix : slim opportunity for out-of-bound write with compress_fast() with a large enough input and when providing an output smaller than recommended (< LZ4_compressBound(inputSize)), by @terrelln +fix : rare data corruption bug with LZ4_compress_destSize(), by @terrelln +fix : data corruption bug when Streaming with an Attached Dict in HC Mode, by @felixhandte +perf: enable LZ4_FAST_DEC_LOOP on aarch64/GCC by default, by @prekageo +perf: improved lz4frame streaming API speed, by @dreambottle +perf: speed up lz4hc on slow patterns when using external dictionary, by @terrelln +api: better in-place decompression and compression support +cli : --list supports multi-frames files, by @gstedman +cli: --version outputs to stdout +cli : add option --best as an alias of -12 , by @Low-power +misc: Integration into oss-fuzz by @cmeister2, expanded list of scenarios by @terrelln + +v1.9.1 +fix : decompression functions were reading a few bytes beyond input size (introduced in v1.9.0, reported by @ppodolsky and @danlark1) +api : fix : lz4frame initializers compatibility with c++, reported by @degski +cli : added command --list, based on a patch by @gabrielstedman +build: improved Windows build, by @JPeterMugaas +build: AIX, by Norman Green + +v1.9.0 +perf: large decompression speed improvement on x86/x64 (up to +20%) by @djwatson +api : changed : _destSize() compression variants are promoted to stable API +api : new : LZ4_initStream(HC), replacing LZ4_resetStream(HC) +api : changed : LZ4_resetStream(HC) as recommended reset function, for better performance on small data +cli : support custom block sizes, by @blezsan +build: source code can be amalgamated, by Bing Xu +build: added meson build, by @lzutao +build: new build macros : LZ4_DISTANCE_MAX, LZ4_FAST_DEC_LOOP +install: MidnightBSD, by @laffer1 +install: msys2 on Windows 10, by @vtorri + +v1.8.3 +perf: minor decompression speed improvement (~+2%) with gcc +fix : corruption in v1.8.2 at level 9 for files > 64KB under rare conditions (#560) +cli : new command --fast, by @jennifermliu +cli : fixed elapsed time, and added cpu load indicator (on -vv) (#555) +api : LZ4_decompress_safe_partial() now decodes exactly the nb of bytes requested (feature request #566) +build : added Haiku target, by @fbrosson, and MidnightBSD, by @laffer1 +doc : updated documentation regarding dictionary compression + +v1.8.2 +perf: *much* faster dictionary compression on small files, by @felixhandte +perf: improved decompression speed and binary size, by Alexey Tourbin (@svpv) +perf: slightly faster HC compression and decompression speed +perf: very small compression ratio improvement +fix : compression compatible with low memory addresses (< 0xFFFF) +fix : decompression segfault when provided with NULL input, by @terrelln +cli : new command --favor-decSpeed +cli : benchmark mode more accurate for small inputs +fullbench : can bench _destSize() variants, by @felixhandte +doc : clarified block format parsing restrictions, by Alexey Tourbin (@svpv) + +v1.8.1 +perf : faster and stronger ultra modes (levels 10+) +perf : slightly faster compression and decompression speed +perf : fix bad degenerative case, reported by @c-morgenstern +fix : decompression failed when using a combination of extDict + low memory address (#397), reported and fixed by Julian Scheid (@jscheid) +cli : support for dictionary compression (`-D`), by Felix Handte @felixhandte +cli : fix : `lz4 -d --rm` preserves timestamp (#441) +cli : fix : do not modify /dev/null permission as root, by @aliceatlas +api : `_destSize()` variant supported for all compression levels +build : `make` and `make test` compatible with `-jX`, reported by @mwgamera +build : can control LZ4LIB_VISIBILITY macro, by @mikir +install: fix man page directory (#387), reported by Stuart Cardall (@itoffshore) + +v1.8.0 +cli : fix : do not modify /dev/null permissions, reported by @Maokaman1 +cli : added GNU separator -- specifying that all following arguments are files +API : added LZ4_compress_HC_destSize(), by Oleg (@remittor) +API : added LZ4F_resetDecompressionContext() +API : lz4frame : negative compression levels trigger fast acceleration, request by Lawrence Chan +API : lz4frame : can control block checksum and dictionary ID +API : fix : expose obsolete decoding functions, reported by Chen Yufei +API : experimental : lz4frame_static : new dictionary compression API +build : fix : static lib installation, by Ido Rosen +build : dragonFlyBSD, OpenBSD, NetBSD supported +build : LZ4_MEMORY_USAGE can be modified at compile time, through external define +doc : Updated LZ4 Frame format to v1.6.0, restoring Dictionary-ID field +doc : lz4 api manual, by Przemyslaw Skibinski + +v1.7.5 +lz4hc : new high compression mode : levels 10-12 compress more and slower, by Przemyslaw Skibinski +lz4cat : fix : works with relative path (#284) and stdin (#285) (reported by @beiDei8z) +cli : fix minor notification when using -r recursive mode +API : lz4frame : LZ4F_frameBound(0) gives upper bound of *flush() and *End() operations (#290, #280) +doc : markdown version of man page, by Takayuki Matsuoka (#279) +build : Makefile : fix make -jX lib+exe concurrency (#277) +build : cmake : improvements by Michał Górny (#296) + +v1.7.4.2 +fix : Makefile : release build compatible with PIE and customized compilation directives provided through environment variables (#274, reported by Antoine Martin) + +v1.7.4 +Improved : much better speed in -mx32 mode +cli : fix : Large file support in 32-bits mode on Mac OS-X +fix : compilation on gcc 4.4 (#272), reported by Antoine Martin + +v1.7.3 +Changed : moved to versioning; package, cli and library have same version number +Improved: Small decompression speed boost +Improved: Small compression speed improvement on 64-bits systems +Improved: Small compression ratio and speed improvement on small files +Improved: Significant speed boost on ARMv6 and ARMv7 +Fix : better ratio on 64-bits big-endian targets +Improved cmake build script, by Evan Nemerson +New liblz4-dll project, by Przemyslaw Skibinki +Makefile: Generates object files (*.o) for faster (re)compilation on low power systems +cli : new : --rm and --help commands +cli : new : preserved file attributes, by Przemyslaw Skibinki +cli : fix : crash on some invalid inputs +cli : fix : -t correctly validates lz4-compressed files, by Nick Terrell +cli : fix : detects and reports fread() errors, thanks to Hiroshi Fujishima report #243 +cli : bench : new : -r recursive mode +lz4cat : can cat multiple files in a single command line (#184) +Added : doc/lz4_manual.html, by Przemyslaw Skibinski +Added : dictionary compression and frame decompression examples, by Nick Terrell +Added : Debianization, by Evgeniy Polyakov + +r131 +New : Dos/DJGPP target, thanks to Louis Santillan (#114) +Added : Example using lz4frame library, by Zbigniew Jędrzejewski-Szmek (#118) +Changed: xxhash symbols are modified (namespace emulation) within liblz4 + +r130: +Fixed : incompatibility sparse mode vs console, reported by Yongwoon Cho (#105) +Fixed : LZ4IO exits too early when frame crc not present, reported by Yongwoon Cho (#106) +Fixed : incompatibility sparse mode vs append mode, reported by Takayuki Matsuoka (#110) +Performance fix : big compression speed boost for clang (+30%) +New : cross-version test, by Takayuki Matsuoka + +r129: +Added : LZ4_compress_fast(), LZ4_compress_fast_continue() +Added : LZ4_compress_destSize() +Changed: New lz4 and lz4hc compression API. Previous function prototypes still supported. +Changed: Sparse file support enabled by default +New : LZ4 CLI improved performance compressing/decompressing multiple files (#86, kind contribution from Kyle J. Harper & Takayuki Matsuoka) +Fixed : GCC 4.9+ optimization bug - Reported by Markus Trippelsdorf, Greg Slazinski & Evan Nemerson +Changed: Enums converted to LZ4F_ namespace convention - by Takayuki Matsuoka +Added : AppVeyor CI environment, for Visual tests - Suggested by Takayuki Matsuoka +Modified:Obsolete functions generate warnings - Suggested by Evan Nemerson, contributed by Takayuki Matsuoka +Fixed : Bug #75 (unfinished stream), reported by Yongwoon Cho +Updated: Documentation converted to MarkDown format + +r128: +New : lz4cli sparse file support (Requested by Neil Wilson, and contributed by Takayuki Matsuoka) +New : command -m, to compress multiple files in a single command (suggested by Kyle J. Harper) +Fixed : Restored lz4hc compression ratio (slightly lower since r124) +New : lz4 cli supports long commands (suggested by Takayuki Matsuoka) +New : lz4frame & lz4cli frame content size support +New : lz4frame supports skippable frames, as requested by Sergey Cherepanov +Changed: Default "make install" directory is /usr/local, as notified by Ron Johnson +New : lz4 cli supports "pass-through" mode, requested by Neil Wilson +New : datagen can generate sparse files +New : scan-build tests, thanks to kind help by Takayuki Matsuoka +New : g++ compatibility tests +New : arm cross-compilation test, thanks to kind help by Takayuki Matsuoka +Fixed : Fuzzer + frametest compatibility with NetBSD (issue #48, reported by Thomas Klausner) +Added : Visual project directory +Updated: Man page & Specification + +r127: +N/A : added a file on SVN + +r126: +New : lz4frame API is now integrated into liblz4 +Fixed : GCC 4.9 bug on highest performance settings, reported by Greg Slazinski +Fixed : bug within LZ4 HC streaming mode, reported by James Boyle +Fixed : older compiler don't like nameless unions, reported by Cheyi Lin +Changed : lz4 is C90 compatible +Changed : added -pedantic option, fixed a few mminor warnings + +r125: +Changed : endian and alignment code +Changed : directory structure : new "lib" directory +Updated : lz4io, now uses lz4frame +Improved: slightly improved decoding speed +Fixed : LZ4_compress_limitedOutput(); Special thanks to Christopher Speller ! +Fixed : some alignment warnings under clang +Fixed : deprecated function LZ4_slideInputBufferHC() + +r124: +New : LZ4 HC streaming mode +Fixed : LZ4F_compressBound() using null preferencesPtr +Updated : xxHash to r38 +Updated library number, to 1.4.0 + +r123: +Added : experimental lz4frame API, thanks to Takayuki Matsuoka and Christopher Jackson for testings +Fix : s390x support, thanks to Nobuhiro Iwamatsu +Fix : test mode (-t) no longer requires confirmation, thanks to Thary Nguyen + +r122: +Fix : AIX & AIX64 support (SamG) +Fix : mips 64-bits support (lew van) +Added : Examples directory, using code examples from Takayuki Matsuoka +Updated : Framing specification, to v1.4.1 +Updated : xxHash, to r36 + +r121: +Added : Makefile : install for kFreeBSD and Hurd (Nobuhiro Iwamatsu) +Fix : Makefile : install for OS-X and BSD, thanks to Takayuki Matsuoka + +r120: +Modified : Streaming API, using strong types +Added : LZ4_versionNumber(), thanks to Takayuki Matsuoka +Fix : OS-X : library install name, thanks to Clemens Lang +Updated : Makefile : synchronize library version number with lz4.h, thanks to Takayuki Matsuoka +Updated : Makefile : stricter compilation flags +Added : pkg-config, thanks to Zbigniew Jędrzejewski-Szmek (issue 135) +Makefile : lz4-test only test native binaries, as suggested by Michał Górny (issue 136) +Updated : xxHash to r35 + +r119: +Fix : Issue 134 : extended malicious address space overflow in 32-bits mode for some specific configurations + +r118: +New : LZ4 Streaming API (Fast version), special thanks to Takayuki Matsuoka +New : datagen : parametrable synthetic data generator for tests +Improved : fuzzer, support more test cases, more parameters, ability to jump to specific test +fix : support ppc64le platform (issue 131) +fix : Issue 52 (malicious address space overflow in 32-bits mode when using large custom format) +fix : Makefile : minor issue 130 : header files permissions + +r117: +Added : man pages for lz4c and lz4cat +Added : automated tests on Travis, thanks to Takayuki Matsuoka ! +fix : block-dependency command line (issue 127) +fix : lz4fullbench (issue 128) + +r116: +hotfix (issue 124 & 125) + +r115: +Added : lz4cat utility, installed on POSX systems (issue 118) +OS-X compatible compilation of dynamic library (issue 115) + +r114: +Makefile : library correctly compiled with -O3 switch (issue 114) +Makefile : library compilation compatible with clang +Makefile : library is versioned and linked (issue 119) +lz4.h : no more static inline prototypes (issue 116) +man : improved header/footer (issue 111) +Makefile : Use system default $(CC) & $(MAKE) variables (issue 112) +xxhash : updated to r34 + +r113: +Large decompression speed improvement for GCC 32-bits. Thanks to Valery Croizier ! +LZ4HC : Compression Level is now a programmable parameter (CLI from 4 to 9) +Separated IO routines from command line (lz4io.c) +Version number into lz4.h (suggested by Francesc Alted) + +r112: +quickfix + +r111 : +Makefile : added capability to install libraries +Modified Directory tree, to better separate libraries from programs. + +r110 : +lz4 & lz4hc : added capability to allocate state & stream state with custom allocator (issue 99) +fuzzer & fullbench : updated to test new functions +man : documented -l command (Legacy format, for Linux kernel compression) (issue 102) +cmake : improved version by Mika Attila, building programs and libraries (issue 100) +xxHash : updated to r33 +Makefile : clean also delete local package .tar.gz + +r109 : +lz4.c : corrected issue 98 (LZ4_compress_limitedOutput()) +Makefile : can specify version number from makefile + +r108 : +lz4.c : corrected compression efficiency issue 97 in 64-bits chained mode (-BD) for streams > 4 GB (thanks Roman Strashkin for reporting) + +r107 : +Makefile : support DESTDIR for staged installs. Thanks Jorge Aparicio. +Makefile : make install installs both lz4 and lz4c (Jorge Aparicio) +Makefile : removed -Wno-implicit-declaration compilation switch +lz4cli.c : include for isatty() (Luca Barbato) +lz4.h : introduced LZ4_MAX_INPUT_SIZE constant (Shay Green) +lz4.h : LZ4_compressBound() : unified macro and inline definitions (Shay Green) +lz4.h : LZ4_decompressSafe_partial() : clarify comments (Shay Green) +lz4.c : LZ4_compress() verify input size condition (Shay Green) +bench.c : corrected a bug in free memory size evaluation +cmake : install into bin/ directory (Richard Yao) +cmake : check for just C compiler (Elan Ruusamae) + +r106 : +Makefile : make dist modify text files in the package to respect Unix EoL convention +lz4cli.c : corrected small display bug in HC mode + +r105 : +Makefile : New install script and man page, contributed by Prasad Pandit +lz4cli.c : Minor modifications, for easier extensibility +COPYING : added license file +LZ4_Streaming_Format.odt : modified file name to remove white space characters +Makefile : .exe suffix now properly added only for Windows target diff --git a/r5dev/thirdparty/lz4/README.md b/r5dev/thirdparty/lz4/README.md new file mode 100644 index 00000000..08d1cef2 --- /dev/null +++ b/r5dev/thirdparty/lz4/README.md @@ -0,0 +1,169 @@ +LZ4 - Library Files +================================ + +The `/lib` directory contains many files, but depending on project's objectives, +not all of them are required. +Limited systems may want to reduce the nb of source files to include +as a way to reduce binary size and dependencies. + +Capabilities are added at the "level" granularity, detailed below. + +#### Level 1 : Minimal LZ4 build + +The minimum required is **`lz4.c`** and **`lz4.h`**, +which provides the fast compression and decompression algorithms. +They generate and decode data using the [LZ4 block format]. + + +#### Level 2 : High Compression variant + +For more compression ratio at the cost of compression speed, +the High Compression variant called **lz4hc** is available. +Add files **`lz4hc.c`** and **`lz4hc.h`**. +This variant also compresses data using the [LZ4 block format], +and depends on regular `lib/lz4.*` source files. + + +#### Level 3 : Frame support, for interoperability + +In order to produce compressed data compatible with `lz4` command line utility, +it's necessary to use the [official interoperable frame format]. +This format is generated and decoded automatically by the **lz4frame** library. +Its public API is described in `lib/lz4frame.h`. +In order to work properly, lz4frame needs all other modules present in `/lib`, +including, lz4 and lz4hc, and also **xxhash**. +So it's necessary to also include `xxhash.c` and `xxhash.h`. + + +#### Level 4 : File compression operations + +As a helper around file operations, +the library has been recently extended with `lz4file.c` and `lz4file.h` +(still considered experimental at the time of this writing). +These helpers allow opening, reading, writing, and closing files +using transparent LZ4 compression / decompression. +As a consequence, using `lz4file` adds a dependency on ``. + +`lz4file` relies on `lz4frame` in order to produce compressed data +conformant to the [LZ4 Frame format] specification. +Consequently, to enable this capability, +it's necessary to include all `*.c` and `*.h` files from `lib/` directory. + + +#### Advanced / Experimental API + +Definitions which are not guaranteed to remain stable in future versions, +are protected behind macros, such as `LZ4_STATIC_LINKING_ONLY`. +As the name suggests, these definitions should only be invoked +in the context of static linking ***only***. +Otherwise, dependent application may fail on API or ABI break in the future. +The associated symbols are also not exposed by the dynamic library by default. +Should they be nonetheless needed, it's possible to force their publication +by using build macros `LZ4_PUBLISH_STATIC_FUNCTIONS` +and `LZ4F_PUBLISH_STATIC_FUNCTIONS`. + + +#### Build macros + +The following build macro can be selected to adjust source code behavior at compilation time : + +- `LZ4_FAST_DEC_LOOP` : this triggers a speed optimized decompression loop, more powerful on modern cpus. + This loop works great on `x86`, `x64` and `aarch64` cpus, and is automatically enabled for them. + It's also possible to enable or disable it manually, by passing `LZ4_FAST_DEC_LOOP=1` or `0` to the preprocessor. + For example, with `gcc` : `-DLZ4_FAST_DEC_LOOP=1`, + and with `make` : `CPPFLAGS+=-DLZ4_FAST_DEC_LOOP=1 make lz4`. + +- `LZ4_DISTANCE_MAX` : control the maximum offset that the compressor will allow. + Set to 65535 by default, which is the maximum value supported by lz4 format. + Reducing maximum distance will reduce opportunities for LZ4 to find matches, + hence will produce a worse compression ratio. + Setting a smaller max distance could allow compatibility with specific decoders with limited memory budget. + This build macro only influences the compressed output of the compressor. + +- `LZ4_DISABLE_DEPRECATE_WARNINGS` : invoking a deprecated function will make the compiler generate a warning. + This is meant to invite users to update their source code. + Should this be a problem, it's generally possible to make the compiler ignore these warnings, + for example with `-Wno-deprecated-declarations` on `gcc`, + or `_CRT_SECURE_NO_WARNINGS` for Visual Studio. + This build macro offers another project-specific method + by defining `LZ4_DISABLE_DEPRECATE_WARNINGS` before including the LZ4 header files. + +- `LZ4_FORCE_SW_BITCOUNT` : by default, the compression algorithm tries to determine lengths + by using bitcount instructions, generally implemented as fast single instructions in many cpus. + In case the target cpus doesn't support it, or compiler intrinsic doesn't work, or feature bad performance, + it's possible to use an optimized software path instead. + This is achieved by setting this build macros. + In most cases, it's not expected to be necessary, + but it can be legitimately considered for less common platforms. + +- `LZ4_ALIGN_TEST` : alignment test ensures that the memory area + passed as argument to become a compression state is suitably aligned. + This test can be disabled if it proves flaky, by setting this value to 0. + +- `LZ4_USER_MEMORY_FUNCTIONS` : replace calls to ``'s `malloc()`, `calloc()` and `free()` + by user-defined functions, which must be named `LZ4_malloc()`, `LZ4_calloc()` and `LZ4_free()`. + User functions must be available at link time. + +- `LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION` : + Remove support of dynamic memory allocation. + For more details, see description of this macro in `lib/lz4.c`. + +- `LZ4_FREESTANDING` : by setting this build macro to 1, + LZ4/HC removes dependencies on the C standard library, + including allocation functions and `memmove()`, `memcpy()`, and `memset()`. + This build macro is designed to help use LZ4/HC in restricted environments + (embedded, bootloader, etc). + For more details, see description of this macro in `lib/lz4.h`. + + + +#### Amalgamation + +lz4 source code can be amalgamated into a single file. +One can combine all source code into `lz4_all.c` by using following command: +``` +cat lz4.c lz4hc.c lz4frame.c > lz4_all.c +``` +(`cat` file order is important) then compile `lz4_all.c`. +All `*.h` files present in `/lib` remain necessary to compile `lz4_all.c`. + + +#### Windows : using MinGW+MSYS to create DLL + +DLL can be created using MinGW+MSYS with the `make liblz4` command. +This command creates `dll\liblz4.dll` and the import library `dll\liblz4.lib`. +To override the `dlltool` command when cross-compiling on Linux, just set the `DLLTOOL` variable. Example of cross compilation on Linux with mingw-w64 64 bits: +``` +make BUILD_STATIC=no CC=x86_64-w64-mingw32-gcc DLLTOOL=x86_64-w64-mingw32-dlltool OS=Windows_NT +``` +The import library is only required with Visual C++. +The header files `lz4.h`, `lz4hc.h`, `lz4frame.h` and the dynamic library +`dll\liblz4.dll` are required to compile a project using gcc/MinGW. +The dynamic library has to be added to linking options. +It means that if a project that uses LZ4 consists of a single `test-dll.c` +file it should be linked with `dll\liblz4.dll`. For example: +``` + $(CC) $(CFLAGS) -Iinclude/ test-dll.c -o test-dll dll\liblz4.dll +``` +The compiled executable will require LZ4 DLL which is available at `dll\liblz4.dll`. + + +#### Miscellaneous + +Other files present in the directory are not source code. They are : + + - `LICENSE` : contains the BSD license text + - `Makefile` : `make` script to compile and install lz4 library (static and dynamic) + - `liblz4.pc.in` : for `pkg-config` (used in `make install`) + - `README.md` : this file + +[official interoperable frame format]: ../doc/lz4_Frame_format.md +[LZ4 Frame format]: ../doc/lz4_Frame_format.md +[LZ4 block format]: ../doc/lz4_Block_format.md + + +#### License + +All source material within __lib__ directory are BSD 2-Clause licensed. +See [LICENSE](LICENSE) for details. +The license is also reminded at the top of each source file. diff --git a/r5dev/thirdparty/lz4/lz4.c b/r5dev/thirdparty/lz4/lz4.c new file mode 100644 index 00000000..654bfdf3 --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4.c @@ -0,0 +1,2722 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ + +/*-************************************ +* Tuning parameters +**************************************/ +/* + * LZ4_HEAPMODE : + * Select how default compression functions will allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#ifndef LZ4_HEAPMODE +# define LZ4_HEAPMODE 0 +#endif + +/* + * LZ4_ACCELERATION_DEFAULT : + * Select "acceleration" for LZ4_compress_fast() when parameter value <= 0 + */ +#define LZ4_ACCELERATION_DEFAULT 1 +/* + * LZ4_ACCELERATION_MAX : + * Any "acceleration" value higher than this threshold + * get treated as LZ4_ACCELERATION_MAX instead (fix #876) + */ +#define LZ4_ACCELERATION_MAX 65537 + + +/*-************************************ +* CPU Feature Detection +**************************************/ +/* LZ4_FORCE_MEMORY_ACCESS + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method is portable but violate C standard. + * It can generate buggy code on targets which assembly generation depends on alignment. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef LZ4_FORCE_MEMORY_ACCESS /* can be defined externally */ +# if defined(__GNUC__) && \ + ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \ + || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define LZ4_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__) +# define LZ4_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/* + * LZ4_FORCE_SW_BITCOUNT + * Define this parameter if your target system or compiler does not support hardware bit count + */ +#if defined(_MSC_VER) && defined(_WIN32_WCE) /* Visual Studio for WinCE doesn't support Hardware bit count */ +# undef LZ4_FORCE_SW_BITCOUNT /* avoid double def */ +# define LZ4_FORCE_SW_BITCOUNT +#endif + + + +/*-************************************ +* Dependency +**************************************/ +/* + * LZ4_SRC_INCLUDED: + * Amalgamation flag, whether lz4.c is included + */ +#ifndef LZ4_SRC_INCLUDED +# define LZ4_SRC_INCLUDED 1 +#endif + +#ifndef LZ4_STATIC_LINKING_ONLY +#define LZ4_STATIC_LINKING_ONLY +#endif + +#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS +#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */ +#endif + +#define LZ4_STATIC_LINKING_ONLY /* LZ4_DISTANCE_MAX */ +#include "lz4.h" +/* see also "memory routines" below */ + + +/*-************************************ +* Compiler Options +**************************************/ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ +# include /* only present in VS2005+ */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ +#endif /* _MSC_VER */ + +#ifndef LZ4_FORCE_INLINE +# ifdef _MSC_VER /* Visual Studio */ +# define LZ4_FORCE_INLINE static __forceinline +# else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define LZ4_FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define LZ4_FORCE_INLINE static inline +# endif +# else +# define LZ4_FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +# endif /* _MSC_VER */ +#endif /* LZ4_FORCE_INLINE */ + +/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE + * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8, + * together with a simple 8-byte copy loop as a fall-back path. + * However, this optimization hurts the decompression speed by >30%, + * because the execution does not go to the optimized loop + * for typical compressible data, and all of the preamble checks + * before going to the fall-back path become useless overhead. + * This optimization happens only with the -O3 flag, and -O2 generates + * a simple 8-byte copy loop. + * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8 + * functions are annotated with __attribute__((optimize("O2"))), + * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute + * of LZ4_wildCopy8 does not affect the compression speed. + */ +#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__) +# define LZ4_FORCE_O2 __attribute__((optimize("O2"))) +# undef LZ4_FORCE_INLINE +# define LZ4_FORCE_INLINE static __inline __attribute__((optimize("O2"),always_inline)) +#else +# define LZ4_FORCE_O2 +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) +# define expect(expr,value) (__builtin_expect ((expr),(value)) ) +#else +# define expect(expr,value) (expr) +#endif + +#ifndef likely +#define likely(expr) expect((expr) != 0, 1) +#endif +#ifndef unlikely +#define unlikely(expr) expect((expr) != 0, 0) +#endif + +/* Should the alignment test prove unreliable, for some reason, + * it can be disabled by setting LZ4_ALIGN_TEST to 0 */ +#ifndef LZ4_ALIGN_TEST /* can be externally provided */ +# define LZ4_ALIGN_TEST 1 +#endif + + +/*-************************************ +* Memory routines +**************************************/ + +/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION : + * Disable relatively high-level LZ4/HC functions that use dynamic memory + * allocation functions (malloc(), calloc(), free()). + * + * Note that this is a compile-time switch. And since it disables + * public/stable LZ4 v1 API functions, we don't recommend using this + * symbol to generate a library for distribution. + * + * The following public functions are removed when this symbol is defined. + * - lz4 : LZ4_createStream, LZ4_freeStream, + * LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated) + * - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC, + * LZ4_createHC (deprecated), LZ4_freeHC (deprecated) + * - lz4frame, lz4file : All LZ4F_* functions + */ +#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +# define ALLOC(s) lz4_error_memory_allocation_is_disabled +# define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled +# define FREEMEM(p) lz4_error_memory_allocation_is_disabled +#elif defined(LZ4_USER_MEMORY_FUNCTIONS) +/* memory management functions can be customized by user project. + * Below functions must exist somewhere in the Project + * and be available at link time */ +void* LZ4_malloc(size_t s); +void* LZ4_calloc(size_t n, size_t s); +void LZ4_free(void* p); +# define ALLOC(s) LZ4_malloc(s) +# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s) +# define FREEMEM(p) LZ4_free(p) +#else +# include /* malloc, calloc, free */ +# define ALLOC(s) malloc(s) +# define ALLOC_AND_ZERO(s) calloc(1,s) +# define FREEMEM(p) free(p) +#endif + +#if ! LZ4_FREESTANDING +# include /* memset, memcpy */ +#endif +#if !defined(LZ4_memset) +# define LZ4_memset(p,v,s) memset((p),(v),(s)) +#endif +#define MEM_INIT(p,v,s) LZ4_memset((p),(v),(s)) + + +/*-************************************ +* Common Constants +**************************************/ +#define MINMATCH 4 + +#define WILDCOPYLENGTH 8 +#define LASTLITERALS 5 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#define MFLIMIT 12 /* see ../doc/lz4_Block_format.md#parsing-restrictions */ +#define MATCH_SAFEGUARD_DISTANCE ((2*WILDCOPYLENGTH) - MINMATCH) /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */ +#define FASTLOOP_SAFE_DISTANCE 64 +static const int LZ4_minLength = (MFLIMIT+1); + +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define LZ4_DISTANCE_ABSOLUTE_MAX 65535 +#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX) /* max supported by LZ4 format */ +# error "LZ4_DISTANCE_MAX is too big : must be <= 65535" +#endif + +#define ML_BITS 4 +#define ML_MASK ((1U<=1) +# include +#else +# ifndef assert +# define assert(condition) ((void)0) +# endif +#endif + +#define LZ4_STATIC_ASSERT(c) { enum { LZ4_static_assert = 1/(int)(!!(c)) }; } /* use after variable declarations */ + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) +# include + static int g_debuglog_enable = 1; +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ + fprintf(stderr, __FILE__ ": "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + +static int LZ4_isAligned(const void* ptr, size_t alignment) +{ + return ((size_t)ptr & (alignment -1)) == 0; +} + + +/*-************************************ +* Types +**************************************/ +#include +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; + typedef uintptr_t uptrval; +#else +# if UINT_MAX != 4294967295UL +# error "LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4" +# endif + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; + typedef size_t uptrval; /* generally true, except OpenVMS-64 */ +#endif + +#if defined(__x86_64__) + typedef U64 reg_t; /* 64-bits in x32 mode */ +#else + typedef size_t reg_t; /* 32-bits in x32 mode */ +#endif + +typedef enum { + notLimited = 0, + limitedOutput = 1, + fillOutput = 2 +} limitedOutput_directive; + + +/*-************************************ +* Reading and writing into memory +**************************************/ + +/** + * LZ4 relies on memcpy with a constant size being inlined. In freestanding + * environments, the compiler can't assume the implementation of memcpy() is + * standard compliant, so it can't apply its specialized memcpy() inlining + * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze + * memcpy() as if it were standard compliant, so it can inline it in freestanding + * environments. This is needed when decompressing the Linux Kernel, for example. + */ +#if !defined(LZ4_memcpy) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) +# else +# define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) +# endif +#endif + +#if !defined(LZ4_memmove) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memmove __builtin_memmove +# else +# define LZ4_memmove memmove +# endif +#endif + +static unsigned LZ4_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} + + +#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2) +/* lie to the compiler about data alignment; use with caution */ + +static U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; } +static U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; } +static reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; } + +static void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; } +static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } + +#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; + +static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; } +static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; } +static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } + +static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; } +static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; } + +#else /* safe and portable access using memcpy() */ + +static U16 LZ4_read16(const void* memPtr) +{ + U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static U32 LZ4_read32(const void* memPtr) +{ + U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static reg_t LZ4_read_ARCH(const void* memPtr) +{ + reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val; +} + +static void LZ4_write16(void* memPtr, U16 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +static void LZ4_write32(void* memPtr, U32 value) +{ + LZ4_memcpy(memPtr, &value, sizeof(value)); +} + +#endif /* LZ4_FORCE_MEMORY_ACCESS */ + + +static U16 LZ4_readLE16(const void* memPtr) +{ + if (LZ4_isLittleEndian()) { + return LZ4_read16(memPtr); + } else { + const BYTE* p = (const BYTE*)memPtr; + return (U16)((U16)p[0] + (p[1]<<8)); + } +} + +static void LZ4_writeLE16(void* memPtr, U16 value) +{ + if (LZ4_isLittleEndian()) { + LZ4_write16(memPtr, value); + } else { + BYTE* p = (BYTE*)memPtr; + p[0] = (BYTE) value; + p[1] = (BYTE)(value>>8); + } +} + +/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */ +LZ4_FORCE_INLINE +void LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d= 16. */ +LZ4_FORCE_INLINE void +LZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* const e = (BYTE*)dstEnd; + + do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d= dstPtr + MINMATCH + * - there is at least 8 bytes available to write after dstEnd */ +LZ4_FORCE_INLINE void +LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset) +{ + BYTE v[8]; + + assert(dstEnd >= dstPtr + MINMATCH); + + switch(offset) { + case 1: + MEM_INIT(v, *srcPtr, 8); + break; + case 2: + LZ4_memcpy(v, srcPtr, 2); + LZ4_memcpy(&v[2], srcPtr, 2); +#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ +# pragma warning(push) +# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */ +#endif + LZ4_memcpy(&v[4], v, 4); +#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ +# pragma warning(pop) +#endif + break; + case 4: + LZ4_memcpy(v, srcPtr, 4); + LZ4_memcpy(&v[4], srcPtr, 4); + break; + default: + LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset); + return; + } + + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + while (dstPtr < dstEnd) { + LZ4_memcpy(dstPtr, v, 8); + dstPtr += 8; + } +} +#endif + + +/*-************************************ +* Common functions +**************************************/ +static unsigned LZ4_NbCommonBytes (reg_t val) +{ + assert(val != 0); + if (LZ4_isLittleEndian()) { + if (sizeof(val) == 8) { +# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT) +/*-************************************************************************************************* +* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. +* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics +* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC. +****************************************************************************************************/ +# if defined(__clang__) && (__clang_major__ < 10) + /* Avoid undefined clang-cl intrinsics issue. + * See https://github.com/lz4/lz4/pull/1017 for details. */ + return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3; +# else + /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ + return (unsigned)_tzcnt_u64(val) >> 3; +# endif +# elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r = 0; + _BitScanForward64(&r, (U64)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctzll((U64)val) >> 3; +# else + const U64 m = 0x0101010101010101ULL; + val ^= val - 1; + return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56); +# endif + } else /* 32 bits */ { +# if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT) + unsigned long r; + _BitScanForward(&r, (U32)val); + return (unsigned)r >> 3; +# elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_ctz((U32)val) >> 3; +# else + const U32 m = 0x01010101; + return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24; +# endif + } + } else /* Big Endian CPU */ { + if (sizeof(val)==8) { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clzll((U64)val) >> 3; +# else +#if 1 + /* this method is probably faster, + * but adds a 128 bytes lookup table */ + static const unsigned char ctz7_tab[128] = { + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + }; + U64 const mask = 0x0101010101010101ULL; + U64 const t = (((val >> 8) - mask) | val) & mask; + return ctz7_tab[(t * 0x0080402010080402ULL) >> 57]; +#else + /* this method doesn't consume memory space like the previous one, + * but it contains several branches, + * that may end up slowing execution */ + static const U32 by32 = sizeof(val)*4; /* 32 on 64 bits (goal), 16 on 32 bits. + Just to avoid some static analyzer complaining about shift by 32 on 32-bits target. + Note that this code path is never triggered in 32-bits mode. */ + unsigned r; + if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; } + if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; } + r += (!val); + return r; +#endif +# endif + } else /* 32 bits */ { +# if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \ + ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \ + !defined(LZ4_FORCE_SW_BITCOUNT) + return (unsigned)__builtin_clz((U32)val) >> 3; +# else + val >>= 8; + val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) | + (val + 0x00FF0000)) >> 24; + return (unsigned)val ^ 3; +# endif + } + } +} + + +#define STEPSIZE sizeof(reg_t) +LZ4_FORCE_INLINE +unsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit) +{ + const BYTE* const pStart = pIn; + + if (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { + pIn+=STEPSIZE; pMatch+=STEPSIZE; + } else { + return LZ4_NbCommonBytes(diff); + } } + + while (likely(pIn < pInLimit-(STEPSIZE-1))) { + reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn); + if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; } + pIn += LZ4_NbCommonBytes(diff); + return (unsigned)(pIn - pStart); + } + + if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; } + if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; } + if ((pIn compression run slower on incompressible data */ + + +/*-************************************ +* Local Structures and types +**************************************/ +typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; + +/** + * This enum distinguishes several different modes of accessing previous + * content in the stream. + * + * - noDict : There is no preceding content. + * - withPrefix64k : Table entries up to ctx->dictSize before the current blob + * blob being compressed are valid and refer to the preceding + * content (of length ctx->dictSize), which is available + * contiguously preceding in memory the content currently + * being compressed. + * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere + * else in memory, starting at ctx->dictionary with length + * ctx->dictSize. + * - usingDictCtx : Everything concerning the preceding content is + * in a separate context, pointed to by ctx->dictCtx. + * ctx->dictionary, ctx->dictSize, and table entries + * in the current context that refer to positions + * preceding the beginning of the current compression are + * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx + * ->dictSize describe the location and size of the preceding + * content, and matches are found by looking in the ctx + * ->dictCtx->hashTable. + */ +typedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive; +typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; + + +/*-************************************ +* Local Utils +**************************************/ +int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } +const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } +int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } +int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); } + + +/*-**************************************** +* Internal Definitions, used only in Tests +*******************************************/ +#if defined (__cplusplus) +extern "C" { +#endif + +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize); + +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize); +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize); +#if defined (__cplusplus) +} +#endif + +/*-****************************** +* Compression functions +********************************/ +LZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType) +{ + if (tableType == byU16) + return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1))); + else + return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG)); +} + +LZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType) +{ + const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG; + if (LZ4_isLittleEndian()) { + const U64 prime5bytes = 889523592379ULL; + return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog)); + } else { + const U64 prime8bytes = 11400714785074694791ULL; + return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog)); + } +} + +LZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType) +{ + if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType); + return LZ4_hash4(LZ4_read32(p), tableType); +} + +LZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: { /* illegal! */ assert(0); return; } + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType) +{ + switch (tableType) + { + default: /* fallthrough */ + case clearedTable: /* fallthrough */ + case byPtr: { /* illegal! */ assert(0); return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; } + case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h, + void* tableBase, tableType_t const tableType, + const BYTE* srcBase) +{ + switch (tableType) + { + case clearedTable: { /* illegal! */ assert(0); return; } + case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; } + case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; } + case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; } + } +} + +LZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + U32 const h = LZ4_hashPosition(p, tableType); + LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase); +} + +/* LZ4_getIndexOnHash() : + * Index of match position registered in hash table. + * hash position must be calculated by using base+index, or dictBase+index. + * Assumption 1 : only valid if tableType == byU32 or byU16. + * Assumption 2 : h is presumed valid (within limits of hash table) + */ +LZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType) +{ + LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2); + if (tableType == byU32) { + const U32* const hashTable = (const U32*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-2))); + return hashTable[h]; + } + if (tableType == byU16) { + const U16* const hashTable = (const U16*) tableBase; + assert(h < (1U << (LZ4_MEMORY_USAGE-1))); + return hashTable[h]; + } + assert(0); return 0; /* forbidden case */ +} + +static const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase) +{ + if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; } + if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; } + { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; } /* default, to ensure a return */ +} + +LZ4_FORCE_INLINE const BYTE* +LZ4_getPosition(const BYTE* p, + const void* tableBase, tableType_t tableType, + const BYTE* srcBase) +{ + U32 const h = LZ4_hashPosition(p, tableType); + return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase); +} + +LZ4_FORCE_INLINE void +LZ4_prepareTable(LZ4_stream_t_internal* const cctx, + const int inputSize, + const tableType_t tableType) { + /* If the table hasn't been used, it's guaranteed to be zeroed out, and is + * therefore safe to use no matter what mode we're in. Otherwise, we figure + * out if it's safe to leave as is or whether it needs to be reset. + */ + if ((tableType_t)cctx->tableType != clearedTable) { + assert(inputSize >= 0); + if ((tableType_t)cctx->tableType != tableType + || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU) + || ((tableType == byU32) && cctx->currentOffset > 1 GB) + || tableType == byPtr + || inputSize >= 4 KB) + { + DEBUGLOG(4, "LZ4_prepareTable: Resetting table in %p", cctx); + MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE); + cctx->currentOffset = 0; + cctx->tableType = (U32)clearedTable; + } else { + DEBUGLOG(4, "LZ4_prepareTable: Re-use hash table (no reset)"); + } + } + + /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, + * is faster than compressing without a gap. + * However, compressing with currentOffset == 0 is faster still, + * so we preserve that case. + */ + if (cctx->currentOffset != 0 && tableType == byU32) { + DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); + cctx->currentOffset += 64 KB; + } + + /* Finally, clear history */ + cctx->dictCtx = NULL; + cctx->dictionary = NULL; + cctx->dictSize = 0; +} + +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time. + * Presumed already validated at this stage: + * - source != NULL + * - inputSize > 0 + */ +LZ4_FORCE_INLINE int LZ4_compress_generic_validated( + LZ4_stream_t_internal* const cctx, + const char* const source, + char* const dest, + const int inputSize, + int* inputConsumed, /* only written when outputDirective == fillOutput */ + const int maxOutputSize, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + int result; + const BYTE* ip = (const BYTE*) source; + + U32 const startIndex = cctx->currentOffset; + const BYTE* base = (const BYTE*) source - startIndex; + const BYTE* lowLimit; + + const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx; + const BYTE* const dictionary = + dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary; + const U32 dictSize = + dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize; + const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0; /* make indexes in dictCtx comparable with index in current context */ + + int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx); + U32 const prefixIdxLimit = startIndex - dictSize; /* used when dictDirective == dictSmall */ + const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary; + const BYTE* anchor = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1; + const BYTE* const matchlimit = iend - LASTLITERALS; + + /* the dictCtx currentOffset is indexed on the start of the dictionary, + * while a dictionary in the current context precedes the currentOffset */ + const BYTE* dictBase = (dictionary == NULL) ? NULL : + (dictDirective == usingDictCtx) ? + dictionary + dictSize - dictCtx->currentOffset : + dictionary + dictSize - startIndex; + + BYTE* op = (BYTE*) dest; + BYTE* const olimit = op + maxOutputSize; + + U32 offset = 0; + U32 forwardH; + + DEBUGLOG(5, "LZ4_compress_generic_validated: srcSize=%i, tableType=%u", inputSize, tableType); + assert(ip != NULL); + /* If init conditions are not met, we don't have to mark stream + * as having dirty context, since no action was taken yet */ + if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */ + if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; } /* Size too large (not within 64K limit) */ + if (tableType==byPtr) assert(dictDirective==noDict); /* only supported use case with byPtr */ + assert(acceleration >= 1); + + lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0); + + /* Update context state */ + if (dictDirective == usingDictCtx) { + /* Subsequent linked blocks can't use the dictionary. */ + /* Instead, they use the block we just compressed. */ + cctx->dictCtx = NULL; + cctx->dictSize = (U32)inputSize; + } else { + cctx->dictSize += (U32)inputSize; + } + cctx->currentOffset += (U32)inputSize; + cctx->tableType = (U32)tableType; + + if (inputSizehashTable, tableType, base); + ip++; forwardH = LZ4_hashPosition(ip, tableType); + + /* Main Loop */ + for ( ; ; ) { + const BYTE* match; + BYTE* token; + const BYTE* filledIp; + + /* Find a match */ + if (tableType == byPtr) { + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base); + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base); + + } while ( (match+LZ4_DISTANCE_MAX < ip) + || (LZ4_read32(match) != LZ4_read32(ip)) ); + + } else { /* byU32, byU16 */ + + const BYTE* forwardIp = ip; + int step = 1; + int searchMatchNb = acceleration << LZ4_skipTrigger; + do { + U32 const h = forwardH; + U32 const current = (U32)(forwardIp - base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex <= current); + assert(forwardIp - base < (ptrdiff_t)(2 GB - 1)); + ip = forwardIp; + forwardIp += step; + step = (searchMatchNb++ >> LZ4_skipTrigger); + + if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals; + assert(ip < mflimitPlusOne); + + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + assert(tableType == byU32); + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + matchIndex += dictDelta; /* make dictCtx index comparable with current context */ + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else if (dictDirective == usingExtDict) { + if (matchIndex < startIndex) { + DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); + assert(startIndex - matchIndex >= MINMATCH); + assert(dictBase); + match = dictBase + matchIndex; + lowLimit = dictionary; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; + } + } else { /* single continuous memory segment */ + match = base + matchIndex; + } + forwardH = LZ4_hashPosition(forwardIp, tableType); + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + + DEBUGLOG(7, "candidate at pos=%u (offset=%u \n", matchIndex, current - matchIndex); + if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; } /* match outside of valid area */ + assert(matchIndex < current); + if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX)) + && (matchIndex+LZ4_DISTANCE_MAX < current)) { + continue; + } /* too far */ + assert((current - matchIndex) <= LZ4_DISTANCE_MAX); /* match now expected within distance */ + + if (LZ4_read32(match) == LZ4_read32(ip)) { + if (maybe_extMem) offset = current - matchIndex; + break; /* match found */ + } + + } while(1); + } + + /* Catch up */ + filledIp = ip; + while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; } + + /* Encode Literals */ + { unsigned const litLength = (unsigned)(ip - anchor); + token = op++; + if ((outputDirective == limitedOutput) && /* Check output buffer overflow */ + (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) { + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + if ((outputDirective == fillOutput) && + (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) { + op--; + goto _last_literals; + } + if (litLength >= RUN_MASK) { + int len = (int)(litLength - RUN_MASK); + *token = (RUN_MASK<= 255 ; len-=255) *op++ = 255; + *op++ = (BYTE)len; + } + else *token = (BYTE)(litLength< olimit)) { + /* the match was too close to the end, rewind and go to last literals */ + op = token; + goto _last_literals; + } + + /* Encode Offset */ + if (maybe_extMem) { /* static test */ + DEBUGLOG(6, " with offset=%u (ext if > %i)", offset, (int)(ip - (const BYTE*)source)); + assert(offset <= LZ4_DISTANCE_MAX && offset > 0); + LZ4_writeLE16(op, (U16)offset); op+=2; + } else { + DEBUGLOG(6, " with offset=%u (same segment)", (U32)(ip - match)); + assert(ip-match <= LZ4_DISTANCE_MAX); + LZ4_writeLE16(op, (U16)(ip - match)); op+=2; + } + + /* Encode MatchLength */ + { unsigned matchCode; + + if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx) + && (lowLimit==dictionary) /* match within extDict */ ) { + const BYTE* limit = ip + (dictEnd-match); + assert(dictEnd > match); + if (limit > matchlimit) limit = matchlimit; + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit); + ip += (size_t)matchCode + MINMATCH; + if (ip==limit) { + unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit); + matchCode += more; + ip += more; + } + DEBUGLOG(6, " with matchLength=%u starting in extDict", matchCode+MINMATCH); + } else { + matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit); + ip += (size_t)matchCode + MINMATCH; + DEBUGLOG(6, " with matchLength=%u", matchCode+MINMATCH); + } + + if ((outputDirective) && /* Check output buffer overflow */ + (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) { + if (outputDirective == fillOutput) { + /* Match description too long : reduce it */ + U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255; + ip -= matchCode - newMatchCode; + assert(newMatchCode < matchCode); + matchCode = newMatchCode; + if (unlikely(ip <= filledIp)) { + /* We have already filled up to filledIp so if ip ends up less than filledIp + * we have positions in the hash table beyond the current position. This is + * a problem if we reuse the hash table. So we have to remove these positions + * from the hash table. + */ + const BYTE* ptr; + DEBUGLOG(5, "Clearing %u positions", (U32)(filledIp - ip)); + for (ptr = ip; ptr <= filledIp; ++ptr) { + U32 const h = LZ4_hashPosition(ptr, tableType); + LZ4_clearHash(h, cctx->hashTable, tableType); + } + } + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + if (matchCode >= ML_MASK) { + *token += ML_MASK; + matchCode -= ML_MASK; + LZ4_write32(op, 0xFFFFFFFF); + while (matchCode >= 4*255) { + op+=4; + LZ4_write32(op, 0xFFFFFFFF); + matchCode -= 4*255; + } + op += matchCode / 255; + *op++ = (BYTE)(matchCode % 255); + } else + *token += (BYTE)(matchCode); + } + /* Ensure we have enough space for the last literals. */ + assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit)); + + anchor = ip; + + /* Test end of chunk */ + if (ip >= mflimitPlusOne) break; + + /* Fill table */ + LZ4_putPosition(ip-2, cctx->hashTable, tableType, base); + + /* Test next position */ + if (tableType == byPtr) { + + match = LZ4_getPosition(ip, cctx->hashTable, tableType, base); + LZ4_putPosition(ip, cctx->hashTable, tableType, base); + if ( (match+LZ4_DISTANCE_MAX >= ip) + && (LZ4_read32(match) == LZ4_read32(ip)) ) + { token=op++; *token=0; goto _next_match; } + + } else { /* byU32, byU16 */ + + U32 const h = LZ4_hashPosition(ip, tableType); + U32 const current = (U32)(ip-base); + U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType); + assert(matchIndex < current); + if (dictDirective == usingDictCtx) { + if (matchIndex < startIndex) { + /* there was no match, try the dictionary */ + matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32); + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + matchIndex += dictDelta; + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else if (dictDirective==usingExtDict) { + if (matchIndex < startIndex) { + assert(dictBase); + match = dictBase + matchIndex; + lowLimit = dictionary; /* required for match length counter */ + } else { + match = base + matchIndex; + lowLimit = (const BYTE*)source; /* required for match length counter */ + } + } else { /* single memory segment */ + match = base + matchIndex; + } + LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType); + assert(matchIndex < current); + if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1) + && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current)) + && (LZ4_read32(match) == LZ4_read32(ip)) ) { + token=op++; + *token=0; + if (maybe_extMem) offset = current - matchIndex; + DEBUGLOG(6, "seq.start:%i, literals=%u, match.start:%i", + (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source)); + goto _next_match; + } + } + + /* Prepare next loop */ + forwardH = LZ4_hashPosition(++ip, tableType); + + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRun = (size_t)(iend - anchor); + if ( (outputDirective) && /* Check output buffer overflow */ + (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) { + if (outputDirective == fillOutput) { + /* adapt lastRun to fill 'dst' */ + assert(olimit >= op); + lastRun = (size_t)(olimit-op) - 1/*token*/; + lastRun -= (lastRun + 256 - RUN_MASK) / 256; /*additional length tokens*/ + } else { + assert(outputDirective == limitedOutput); + return 0; /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */ + } + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRun); + if (lastRun >= RUN_MASK) { + size_t accumulator = lastRun - RUN_MASK; + *op++ = RUN_MASK << ML_BITS; + for(; accumulator >= 255 ; accumulator-=255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRun< 0); + DEBUGLOG(5, "LZ4_compress_generic: compressed %i bytes into %i bytes", inputSize, result); + return result; +} + +/** LZ4_compress_generic() : + * inlined, to ensure branches are decided at compilation time; + * takes care of src == (NULL, 0) + * and forward the rest to LZ4_compress_generic_validated */ +LZ4_FORCE_INLINE int LZ4_compress_generic( + LZ4_stream_t_internal* const cctx, + const char* const src, + char* const dst, + const int srcSize, + int *inputConsumed, /* only written when outputDirective == fillOutput */ + const int dstCapacity, + const limitedOutput_directive outputDirective, + const tableType_t tableType, + const dict_directive dictDirective, + const dictIssue_directive dictIssue, + const int acceleration) +{ + DEBUGLOG(5, "LZ4_compress_generic: srcSize=%i, dstCapacity=%i", + srcSize, dstCapacity); + + if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; } /* Unsupported srcSize, too large (or negative) */ + if (srcSize == 0) { /* src == NULL supported if srcSize == 0 */ + if (outputDirective != notLimited && dstCapacity <= 0) return 0; /* no output, can't write anything */ + DEBUGLOG(5, "Generating an empty block"); + assert(outputDirective == notLimited || dstCapacity >= 1); + assert(dst != NULL); + dst[0] = 0; + if (outputDirective == fillOutput) { + assert (inputConsumed != NULL); + *inputConsumed = 0; + } + return 1; + } + assert(src != NULL); + + return LZ4_compress_generic_validated(cctx, src, dst, srcSize, + inputConsumed, /* only written into if outputDirective == fillOutput */ + dstCapacity, outputDirective, + tableType, dictDirective, dictIssue, acceleration); +} + + +int LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse; + assert(ctx != NULL); + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + if (maxOutputSize >= LZ4_compressBound(inputSize)) { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (inputSize < LZ4_64Klimit) { + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration); + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + +/** + * LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. It is only safe + * to call if the state buffer is known to be correctly initialized already + * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of + * "correctly initialized"). + */ +int LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration) +{ + LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse; + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + + if (dstCapacity >= LZ4_compressBound(srcSize)) { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration); + } + } else { + if (srcSize < LZ4_64Klimit) { + const tableType_t tableType = byU16; + LZ4_prepareTable(ctx, srcSize, tableType); + if (ctx->currentOffset) { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration); + } else { + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } else { + const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + LZ4_prepareTable(ctx, srcSize, tableType); + return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration); + } + } +} + + +int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration) +{ + int result; +#if (LZ4_HEAPMODE) + LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctxPtr == NULL) return 0; +#else + LZ4_stream_t ctx; + LZ4_stream_t* const ctxPtr = &ctx; +#endif + result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration); + +#if (LZ4_HEAPMODE) + FREEMEM(ctxPtr); +#endif + return result; +} + + +int LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize) +{ + return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1); +} + + +/* Note!: This function leaves the stream in an unclean/broken state! + * It is not safe to subsequently use the same state with a _fastReset() or + * _continue() call without resetting it. */ +static int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ + void* const s = LZ4_initStream(state, sizeof (*state)); + assert(s != NULL); (void)s; + + if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) { /* compression success is guaranteed */ + return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1); + } else { + if (*srcSizePtr < LZ4_64Klimit) { + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1); + } else { + tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32; + return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1); + } } +} + + +int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize) +{ +#if (LZ4_HEAPMODE) + LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + if (ctx == NULL) return 0; +#else + LZ4_stream_t ctxBody; + LZ4_stream_t* ctx = &ctxBody; +#endif + + int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize); + +#if (LZ4_HEAPMODE) + FREEMEM(ctx); +#endif + return result; +} + + + +/*-****************************** +* Streaming functions +********************************/ + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_stream_t* LZ4_createStream(void) +{ + LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); + LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal)); + DEBUGLOG(4, "LZ4_createStream %p", lz4s); + if (lz4s == NULL) return NULL; + LZ4_initStream(lz4s, sizeof(*lz4s)); + return lz4s; +} +#endif + +static size_t LZ4_stream_t_alignment(void) +{ +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_stream_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_stream_t); +#else + return 1; /* effectively disabled */ +#endif +} + +LZ4_stream_t* LZ4_initStream (void* buffer, size_t size) +{ + DEBUGLOG(5, "LZ4_initStream"); + if (buffer == NULL) { return NULL; } + if (size < sizeof(LZ4_stream_t)) { return NULL; } + if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL; + MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal)); + return (LZ4_stream_t*)buffer; +} + +/* resetStream is now deprecated, + * prefer initStream() which is more general */ +void LZ4_resetStream (LZ4_stream_t* LZ4_stream) +{ + DEBUGLOG(5, "LZ4_resetStream (ctx:%p)", LZ4_stream); + MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal)); +} + +void LZ4_resetStream_fast(LZ4_stream_t* ctx) { + LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +int LZ4_freeStream (LZ4_stream_t* LZ4_stream) +{ + if (!LZ4_stream) return 0; /* support free on NULL */ + DEBUGLOG(5, "LZ4_freeStream %p", LZ4_stream); + FREEMEM(LZ4_stream); + return (0); +} +#endif + + +#define HASH_UNIT sizeof(reg_t) +int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) +{ + LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse; + const tableType_t tableType = byU32; + const BYTE* p = (const BYTE*)dictionary; + const BYTE* const dictEnd = p + dictSize; + const BYTE* base; + + DEBUGLOG(4, "LZ4_loadDict (%i bytes from %p into %p)", dictSize, dictionary, LZ4_dict); + + /* It's necessary to reset the context, + * and not just continue it with prepareTable() + * to avoid any risk of generating overflowing matchIndex + * when compressing using this dictionary */ + LZ4_resetStream(LZ4_dict); + + /* We always increment the offset by 64 KB, since, if the dict is longer, + * we truncate it to the last 64k, and if it's shorter, we still want to + * advance by a whole window length so we can provide the guarantee that + * there are only valid offsets in the window, which allows an optimization + * in LZ4_compress_fast_continue() where it uses noDictIssue even when the + * dictionary isn't a full 64k. */ + dict->currentOffset += 64 KB; + + if (dictSize < (int)HASH_UNIT) { + return 0; + } + + if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB; + base = dictEnd - dict->currentOffset; + dict->dictionary = p; + dict->dictSize = (U32)(dictEnd - p); + dict->tableType = (U32)tableType; + + while (p <= dictEnd-HASH_UNIT) { + LZ4_putPosition(p, dict->hashTable, tableType, base); + p+=3; + } + + return (int)dict->dictSize; +} + +void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) +{ + const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : + &(dictionaryStream->internal_donotuse); + + DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", + workingStream, dictionaryStream, + dictCtx != NULL ? dictCtx->dictSize : 0); + + if (dictCtx != NULL) { + /* If the current offset is zero, we will never look in the + * external dictionary context, since there is no value a table + * entry can take that indicate a miss. In that case, we need + * to bump the offset to something non-zero. + */ + if (workingStream->internal_donotuse.currentOffset == 0) { + workingStream->internal_donotuse.currentOffset = 64 KB; + } + + /* Don't actually attach an empty dictionary. + */ + if (dictCtx->dictSize == 0) { + dictCtx = NULL; + } + } + workingStream->internal_donotuse.dictCtx = dictCtx; +} + + +static void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize) +{ + assert(nextSize >= 0); + if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) { /* potential ptrdiff_t overflow (32-bits mode) */ + /* rescale hash table */ + U32 const delta = LZ4_dict->currentOffset - 64 KB; + const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize; + int i; + DEBUGLOG(4, "LZ4_renormDictT"); + for (i=0; ihashTable[i] < delta) LZ4_dict->hashTable[i]=0; + else LZ4_dict->hashTable[i] -= delta; + } + LZ4_dict->currentOffset = 64 KB; + if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB; + LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize; + } +} + + +int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, + const char* source, char* dest, + int inputSize, int maxOutputSize, + int acceleration) +{ + const tableType_t tableType = byU32; + LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse; + const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; + + DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); + + LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */ + if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; + if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; + + /* invalidate tiny dictionaries */ + if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */ + && (dictEnd != source) /* prefix mode */ + && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */ + && (streamPtr->dictCtx == NULL) /* usingDictCtx */ + ) { + DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); + /* remove dictionary existence from history, to employ faster prefix mode */ + streamPtr->dictSize = 0; + streamPtr->dictionary = (const BYTE*)source; + dictEnd = source; + } + + /* Check overlapping input/dictionary space */ + { const char* const sourceEnd = source + inputSize; + if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) { + streamPtr->dictSize = (U32)(dictEnd - sourceEnd); + if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; + if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; + streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize; + } + } + + /* prefix mode : source data follows dictionary */ + if (dictEnd == source) { + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); + else + return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration); + } + + /* external dictionary mode */ + { int result; + if (streamPtr->dictCtx) { + /* We depend here on the fact that dictCtx'es (produced by + * LZ4_loadDict) guarantee that their tables contain no references + * to offsets between dictCtx->currentOffset - 64 KB and + * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe + * to use noDictIssue even when the dict isn't a full 64 KB. + */ + if (inputSize > 4 KB) { + /* For compressing large blobs, it is faster to pay the setup + * cost to copy the dictionary's tables into the active context, + * so that the compression loop is only looking into one table. + */ + LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr)); + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); + } + } else { /* small data <= 4 KB */ + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration); + } + } + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)inputSize; + return result; + } +} + + +/* Hidden debug function, to force-test external dictionary mode */ +int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize) +{ + LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse; + int result; + + LZ4_renormDictT(streamPtr, srcSize); + + if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1); + } else { + result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1); + } + + streamPtr->dictionary = (const BYTE*)source; + streamPtr->dictSize = (U32)srcSize; + + return result; +} + + +/*! LZ4_saveDict() : + * If previously compressed data block is not guaranteed to remain available at its memory location, + * save it into a safer place (char* safeBuffer). + * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable, + * one can therefore call LZ4_compress_fast_continue() right after. + * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. + */ +int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) +{ + LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; + + DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); + + if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ + if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } + + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) { + const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; + assert(dict->dictionary); + LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize); + } + + dict->dictionary = (const BYTE*)safeBuffer; + dict->dictSize = (U32)dictSize; + + return dictSize; +} + + + +/*-******************************* + * Decompression functions + ********************************/ + +typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; + +#undef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + + +/* variant for decompress_unsafe() + * does not know end of input + * presumes input is well formed + * note : will consume at least one byte */ +size_t read_long_length_no_check(const BYTE** pp) +{ + size_t b, l = 0; + do { b = **pp; (*pp)++; l += b; } while (b==255); + DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1) + return l; +} + +/* core decoder variant for LZ4_decompress_fast*() + * for legacy support only : these entry points are deprecated. + * - Presumes input is correctly formed (no defense vs malformed inputs) + * - Does not know input size (presume input buffer is "large enough") + * - Decompress a full block (only) + * @return : nb of bytes read from input. + * Note : this variant is not optimized for speed, just for maintenance. + * the goal is to remove support of decompress_fast*() variants by v2.0 +**/ +LZ4_FORCE_INLINE int +LZ4_decompress_unsafe_generic( + const BYTE* const istart, + BYTE* const ostart, + int decompressedSize, + + size_t prefixSize, + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note: =0 if dictStart==NULL */ + ) +{ + const BYTE* ip = istart; + BYTE* op = (BYTE*)ostart; + BYTE* const oend = ostart + decompressedSize; + const BYTE* const prefixStart = ostart - prefixSize; + + DEBUGLOG(5, "LZ4_decompress_unsafe_generic"); + if (dictStart == NULL) assert(dictSize == 0); + + while (1) { + /* start new sequence */ + unsigned token = *ip++; + + /* literals */ + { size_t ll = token >> ML_BITS; + if (ll==15) { + /* long literal length */ + ll += read_long_length_no_check(&ip); + } + if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */ + LZ4_memmove(op, ip, ll); /* support in-place decompression */ + op += ll; + ip += ll; + if ((size_t)(oend-op) < MFLIMIT) { + if (op==oend) break; /* end of block */ + DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op); + /* incorrect end of block : + * last match must start at least MFLIMIT==12 bytes before end of output block */ + return -1; + } } + + /* match */ + { size_t ml = token & 15; + size_t const offset = LZ4_readLE16(ip); + ip+=2; + + if (ml==15) { + /* long literal length */ + ml += read_long_length_no_check(&ip); + } + ml += MINMATCH; + + if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */ + + { const BYTE* match = op - offset; + + /* out of range */ + if (offset > (size_t)(op - prefixStart) + dictSize) { + DEBUGLOG(6, "offset out of range"); + return -1; + } + + /* check special case : extDict */ + if (offset > (size_t)(op - prefixStart)) { + /* extDict scenario */ + const BYTE* const dictEnd = dictStart + dictSize; + const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart)); + size_t const extml = (size_t)(dictEnd - extMatch); + if (extml > ml) { + /* match entirely within extDict */ + LZ4_memmove(op, extMatch, ml); + op += ml; + ml = 0; + } else { + /* match split between extDict & prefix */ + LZ4_memmove(op, extMatch, extml); + op += extml; + ml -= extml; + } + match = prefixStart; + } + + /* match copy - slow variant, supporting overlap copy */ + { size_t u; + for (u=0; u= ipmax before start of loop. Returns initial_error if so. + * @error (output) - error code. Must be set to 0 before call. +**/ +typedef size_t Rvl_t; +static const Rvl_t rvl_error = (Rvl_t)(-1); +LZ4_FORCE_INLINE Rvl_t +read_variable_length(const BYTE** ip, const BYTE* ilimit, + int initial_check) +{ + Rvl_t s, length = 0; + assert(ip != NULL); + assert(*ip != NULL); + assert(ilimit != NULL); + if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */ + return rvl_error; + } + do { + s = **ip; + (*ip)++; + length += s; + if (unlikely((*ip) > ilimit)) { /* read limit reached */ + return rvl_error; + } + /* accumulator overflow detection (32-bit mode only) */ + if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { + return rvl_error; + } + } while (s==255); + + return length; +} + +/*! LZ4_decompress_generic() : + * This generic decompression function covers all use cases. + * It shall be instantiated several times, using different sets of directives. + * Note that it is important for performance that this function really get inlined, + * in order to remove useless branches during compilation optimization. + */ +LZ4_FORCE_INLINE int +LZ4_decompress_generic( + const char* const src, + char* const dst, + int srcSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ + + earlyEnd_directive partialDecoding, /* full, partial */ + dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ + ) +{ + if ((src == NULL) || (outputSize < 0)) { return -1; } + + { const BYTE* ip = (const BYTE*) src; + const BYTE* const iend = ip + srcSize; + + BYTE* op = (BYTE*) dst; + BYTE* const oend = op + outputSize; + BYTE* cpy; + + const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; + + const int checkOffset = (dictSize < (int)(64 KB)); + + + /* Set up the "end" pointers for the shortcut. */ + const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/; + const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/; + + const BYTE* match; + size_t offset; + unsigned token; + size_t length; + + + DEBUGLOG(5, "LZ4_decompress_generic (srcSize:%i, dstSize:%i)", srcSize, outputSize); + + /* Special cases */ + assert(lowPrefix <= op); + if (unlikely(outputSize==0)) { + /* Empty output buffer */ + if (partialDecoding) return 0; + return ((srcSize==1) && (*ip==0)) ? 0 : -1; + } + if (unlikely(srcSize==0)) { return -1; } + + /* LZ4_FAST_DEC_LOOP: + * designed for modern OoO performance cpus, + * where copying reliably 32-bytes is preferable to an unpredictable branch. + * note : fast loop may show a regression for some client arm chips. */ +#if LZ4_FAST_DEC_LOOP + if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { + DEBUGLOG(6, "skip fast decode loop"); + goto safe_decode; + } + + /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */ + while (1) { + /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ + assert(oend - op >= FASTLOOP_SAFE_DISTANCE); + assert(ip < iend); + token = *ip++; + length = token >> ML_BITS; /* literal length */ + + /* decode literal length */ + if (length == RUN_MASK) { + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + + /* copy literals */ + cpy = op+length; + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } + LZ4_wildCopy32(op, ip, cpy); + ip += length; op = cpy; + } else { + cpy = op+length; + DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); + /* We don't need to check oend, since we check it once for each loop below */ + if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } + /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */ + LZ4_memcpy(op, ip, 16); + ip += length; op = cpy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + assert(match <= op); /* overflow check */ + + /* get matchlength */ + length = token & ML_MASK; + + if (length == ML_MASK) { + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { goto _output_error; } + length += addl; + length += MINMATCH; + if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + goto safe_match_copy; + } + } else { + length += MINMATCH; + if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { + goto safe_match_copy; + } + + /* Fastpath check: skip LZ4_wildCopy32 when true */ + if ((dict == withPrefix64k) || (match >= lowPrefix)) { + if (offset >= 8) { + assert(match >= lowPrefix); + assert(match <= op); + assert(op + 18 <= oend); + + LZ4_memcpy(op, match, 8); + LZ4_memcpy(op+8, match+8, 8); + LZ4_memcpy(op+16, match+16, 2); + op += length; + continue; + } } } + + if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) { + DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); + length = MIN(length, (size_t)(oend-op)); + } else { + goto _output_error; /* end-of-block condition violated */ + } } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) { *op++ = *copyFrom++; } + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + + /* copy match within block */ + cpy = op + length; + + assert((op <= oend) && (oend-op >= 32)); + if (unlikely(offset<16)) { + LZ4_memcpy_using_offset(op, match, cpy, offset); + } else { + LZ4_wildCopy32(op, match, cpy); + } + + op = cpy; /* wildcopy correction */ + } + safe_decode: +#endif + + /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ + while (1) { + assert(ip < iend); + token = *ip++; + length = token >> ML_BITS; /* literal length */ + + /* A two-stage shortcut for the most common case: + * 1) If the literal length is 0..14, and there is enough space, + * enter the shortcut and copy 16 bytes on behalf of the literals + * (in the fast mode, only 8 bytes can be safely copied this way). + * 2) Further if the match length is 4..18, copy 18 bytes in a similar + * manner; but we ensure that there's enough space in the output for + * those 18 bytes earlier, upon entering the shortcut (in other words, + * there is a combined check for both stages). + */ + if ( (length != RUN_MASK) + /* strictly "less than" on input, to re-enter the loop with at least one byte */ + && likely((ip < shortiend) & (op <= shortoend)) ) { + /* Copy the literals */ + LZ4_memcpy(op, ip, 16); + op += length; ip += length; + + /* The second stage: prepare for match copying, decode full info. + * If it doesn't work out, the info won't be wasted. */ + length = token & ML_MASK; /* match length */ + offset = LZ4_readLE16(ip); ip += 2; + match = op - offset; + assert(match <= op); /* check overflow */ + + /* Do not deal with overlapping matches. */ + if ( (length != ML_MASK) + && (offset >= 8) + && (dict==withPrefix64k || match >= lowPrefix) ) { + /* Copy the match. */ + LZ4_memcpy(op + 0, match + 0, 8); + LZ4_memcpy(op + 8, match + 8, 8); + LZ4_memcpy(op +16, match +16, 2); + op += length + MINMATCH; + /* Both stages worked, load the next token. */ + continue; + } + + /* The second stage didn't work out, but the info is ready. + * Propel it right to the point of match copying. */ + goto _copy_match; + } + + /* decode literal length */ + if (length == RUN_MASK) { + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + } + + /* copy literals */ + cpy = op+length; +#if LZ4_FAST_DEC_LOOP + safe_literal_copy: +#endif + LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); + if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) { + /* We've either hit the input parsing restriction or the output parsing restriction. + * In the normal scenario, decoding a full block, it must be the last sequence, + * otherwise it's an error (invalid input or dimensions). + * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow. + */ + if (partialDecoding) { + /* Since we are partial decoding we may be in this block because of the output parsing + * restriction, which is not valid since the output buffer is allowed to be undersized. + */ + DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") + DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); + DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); + DEBUGLOG(7, "partialDecoding: remaining space in srcBuffer : %i", (int)(iend - ip)); + /* Finishing in the middle of a literals segment, + * due to lack of input. + */ + if (ip+length > iend) { + length = (size_t)(iend-ip); + cpy = op + length; + } + /* Finishing in the middle of a literals segment, + * due to lack of output space. + */ + if (cpy > oend) { + cpy = oend; + assert(op<=oend); + length = (size_t)(oend-op); + } + } else { + /* We must be on the last sequence (or invalid) because of the parsing limitations + * so check that we exactly consume the input and don't overrun the output buffer. + */ + if ((ip+length != iend) || (cpy > oend)) { + DEBUGLOG(6, "should have been last run of literals") + DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); + DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); + goto _output_error; + } + } + LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */ + ip += length; + op += length; + /* Necessarily EOF when !partialDecoding. + * When partialDecoding, it is EOF if we've either + * filled the output buffer or + * can't proceed with reading an offset for following match. + */ + if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) { + break; + } + } else { + LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */ + ip += length; op = cpy; + } + + /* get offset */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + + /* get matchlength */ + length = token & ML_MASK; + + _copy_match: + if (length == ML_MASK) { + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ + } + length += MINMATCH; + +#if LZ4_FAST_DEC_LOOP + safe_match_copy: +#endif + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ + /* match starting within external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); + if (unlikely(op+length > oend-LASTLITERALS)) { + if (partialDecoding) length = MIN(length, (size_t)(oend-op)); + else goto _output_error; /* doesn't respect parsing restriction */ + } + + if (length <= (size_t)(lowPrefix-match)) { + /* match fits entirely within external dictionary : just copy */ + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); + op += length; + } else { + /* match stretches into both external dictionary and current block */ + size_t const copySize = (size_t)(lowPrefix - match); + size_t const restSize = length - copySize; + LZ4_memcpy(op, dictEnd - copySize, copySize); + op += copySize; + if (restSize > (size_t)(op - lowPrefix)) { /* overlap copy */ + BYTE* const endOfMatch = op + restSize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } else { + LZ4_memcpy(op, lowPrefix, restSize); + op += restSize; + } } + continue; + } + assert(match >= lowPrefix); + + /* copy match within block */ + cpy = op + length; + + /* partialDecoding : may end anywhere within the block */ + assert(op<=oend); + if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + size_t const mlen = MIN(length, (size_t)(oend-op)); + const BYTE* const matchEnd = match + mlen; + BYTE* const copyEnd = op + mlen; + if (matchEnd > op) { /* overlap copy */ + while (op < copyEnd) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, mlen); + } + op = copyEnd; + if (op == oend) { break; } + continue; + } + + if (unlikely(offset<8)) { + LZ4_write32(op, 0); /* silence msan warning when offset==0 */ + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += inc32table[offset]; + LZ4_memcpy(op+4, match, 4); + match -= dec64table[offset]; + } else { + LZ4_memcpy(op, match, 8); + match += 8; + } + op += 8; + + if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) { + BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1); + if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ + if (op < oCopyLimit) { + LZ4_wildCopy8(op, match, oCopyLimit); + match += oCopyLimit - op; + op = oCopyLimit; + } + while (op < cpy) { *op++ = *match++; } + } else { + LZ4_memcpy(op, match, 8); + if (length > 16) { LZ4_wildCopy8(op+8, match+8, cpy); } + } + op = cpy; /* wildcopy correction */ + } + + /* end of decoding */ + DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); + return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ + + /* Overflow error detected */ + _output_error: + return (int) (-(((const char*)ip)-src))-1; + } +} + + +/*===== Instantiate the API decoding functions. =====*/ + +LZ4_FORCE_O2 +int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, + decode_full_block, noDict, + (BYTE*)dest, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, + partial_decode, + noDict, (BYTE*)dst, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_fast(const char* source, char* dest, int originalSize) +{ + DEBUGLOG(5, "LZ4_decompress_fast"); + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, NULL, 0); +} + +/*===== Instantiate a few more decoding cases, used more than once. =====*/ + +LZ4_FORCE_O2 /* Exported, an obsolete API function. */ +int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +/* Another obsolete API function, paired with the previous one. */ +int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) +{ + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 64 KB, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, + size_t prefixSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, + int compressedSize, int maxOutputSize, + const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, + const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, (const BYTE*)dictStart, dictSize); +} + +/* The "double dictionary" mode, for use with e.g. ring buffers: the first part + * of the dictionary is passed as prefix, and the second via dictStart + dictSize. + * These routines are used only once, in LZ4_decompress_*_continue(). + */ +LZ4_FORCE_INLINE +int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize, + size_t prefixSize, const void* dictStart, size_t dictSize) +{ + return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, + decode_full_block, usingExtDict, + (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); +} + +/*===== streaming decompression functions =====*/ + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_streamDecode_t* LZ4_createStreamDecode(void) +{ + LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal)); + return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); +} + +int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) +{ + if (LZ4_stream == NULL) { return 0; } /* support free on NULL */ + FREEMEM(LZ4_stream); + return 0; +} +#endif + +/*! LZ4_setStreamDecode() : + * Use this function to instruct where to find the dictionary. + * This function is not necessary if previous data is still available where it was decoded. + * Loading a size of 0 is allowed (same effect as no dictionary). + * @return : 1 if OK, 0 if error + */ +int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + lz4sd->prefixSize = (size_t)dictSize; + if (dictSize) { + assert(dictionary != NULL); + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + } else { + lz4sd->prefixEnd = (const BYTE*) dictionary; + } + lz4sd->externalDict = NULL; + lz4sd->extDictSize = 0; + return 1; +} + +/*! LZ4_decoderRingBufferSize() : + * when setting a ring buffer for streaming decompression (optional scenario), + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * Note : in a ring buffer scenario, + * blocks are presumed decompressed next to each other. + * When not enough space remains for next block (remainingSize < maxBlockSize), + * decoding resumes from beginning of ring buffer. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +int LZ4_decoderRingBufferSize(int maxBlockSize) +{ + if (maxBlockSize < 0) return 0; + if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0; + if (maxBlockSize < 16) maxBlockSize = 16; + return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize); +} + +/* +*_continue() : + These decoding functions allow decompression of multiple blocks in "streaming" mode. + Previously decoded blocks must still be available at the memory position where they were decoded. + If it's not possible, save the relevant part of decoded data into a safe buffer, + and indicate where it stands using LZ4_setStreamDecode() +*/ +LZ4_FORCE_O2 +int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize) +{ + LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + int result; + + if (lz4sd->prefixSize == 0) { + /* The first call, no dictionary yet. */ + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + /* They're rolling the current segment. */ + if (lz4sd->prefixSize >= 64 KB - 1) + result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + else if (lz4sd->extDictSize == 0) + result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize); + else + result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize, + lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)result; + lz4sd->prefixEnd += result; + } else { + /* The buffer wraps around, or they're switching to another buffer. */ + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)result; + lz4sd->prefixEnd = (BYTE*)dest + result; + } + + return result; +} + +LZ4_FORCE_O2 int +LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* source, char* dest, int originalSize) +{ + LZ4_streamDecode_t_internal* const lz4sd = + (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse); + int result; + + DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize); + assert(originalSize >= 0); + + if (lz4sd->prefixSize == 0) { + DEBUGLOG(5, "first invocation : no prefix nor extDict"); + assert(lz4sd->extDictSize == 0); + result = LZ4_decompress_fast(source, dest, originalSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } else if (lz4sd->prefixEnd == (BYTE*)dest) { + DEBUGLOG(5, "continue using existing prefix"); + result = LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + lz4sd->prefixSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize += (size_t)originalSize; + lz4sd->prefixEnd += originalSize; + } else { + DEBUGLOG(5, "prefix becomes extDict"); + lz4sd->extDictSize = lz4sd->prefixSize; + lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; + result = LZ4_decompress_fast_extDict(source, dest, originalSize, + lz4sd->externalDict, lz4sd->extDictSize); + if (result <= 0) return result; + lz4sd->prefixSize = (size_t)originalSize; + lz4sd->prefixEnd = (BYTE*)dest + originalSize; + } + + return result; +} + + +/* +Advanced decoding functions : +*_usingDict() : + These decoding functions work the same as "_continue" ones, + the dictionary must be explicitly provided within parameters +*/ + +int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); +} + +int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize); +} + +int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) +{ + if (dictSize==0 || dictStart+dictSize == dest) + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + (size_t)dictSize, NULL, 0); + assert(dictSize >= 0); + return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); +} + + +/*=************************************************* +* Obsolete Functions +***************************************************/ +/* obsolete compression functions */ +int LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize) +{ + return LZ4_compress_default(source, dest, inputSize, maxOutputSize); +} +int LZ4_compress(const char* src, char* dest, int srcSize) +{ + return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize)); +} +int LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1); +} +int LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize) +{ + return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1); +} +int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity) +{ + return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1); +} +int LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize) +{ + return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1); +} + +/* +These decompression functions are deprecated and should no longer be used. +They are only provided here for compatibility with older user programs. +- LZ4_uncompress is totally equivalent to LZ4_decompress_fast +- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe +*/ +int LZ4_uncompress (const char* source, char* dest, int outputSize) +{ + return LZ4_decompress_fast(source, dest, outputSize); +} +int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize) +{ + return LZ4_decompress_safe(source, dest, isize, maxOutputSize); +} + +/* Obsolete Streaming functions */ + +int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); } + +int LZ4_resetStreamState(void* state, char* inputBuffer) +{ + (void)inputBuffer; + LZ4_resetStream((LZ4_stream_t*)state); + return 0; +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +void* LZ4_create (char* inputBuffer) +{ + (void)inputBuffer; + return LZ4_createStream(); +} +#endif + +char* LZ4_slideInputBuffer (void* state) +{ + /* avoid const char * -> char * conversion warning */ + return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary; +} + +#endif /* LZ4_COMMONDEFS_ONLY */ diff --git a/r5dev/thirdparty/lz4/lz4.h b/r5dev/thirdparty/lz4/lz4.h new file mode 100644 index 00000000..491c6087 --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4.h @@ -0,0 +1,842 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 homepage : http://www.lz4.org + - LZ4 source repository : https://github.com/lz4/lz4 +*/ +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef LZ4_H_2983827168210 +#define LZ4_H_2983827168210 + +/* --- Dependency --- */ +#include /* size_t */ + + +/** + Introduction + + LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core, + scalable with multi-cores CPU. It features an extremely fast decoder, with speed in + multiple GB/s per core, typically reaching RAM speed limits on multi-core systems. + + The LZ4 compression library provides in-memory compression and decompression functions. + It gives full buffer control to user. + Compression can be done in: + - a single step (described as Simple Functions) + - a single step, reusing a context (described in Advanced Functions) + - unbounded multiple steps (described as Streaming compression) + + lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md). + Decompressing such a compressed block requires additional metadata. + Exact metadata depends on exact decompression function. + For the typical case of LZ4_decompress_safe(), + metadata includes block's compressed size, and maximum bound of decompressed size. + Each application is free to encode and pass such metadata in whichever way it wants. + + lz4.h only handle blocks, it can not generate Frames. + + Blocks are different from Frames (doc/lz4_Frame_format.md). + Frames bundle both blocks and metadata in a specified manner. + Embedding metadata is required for compressed data to be self-contained and portable. + Frame format is delivered through a companion API, declared in lz4frame.h. + The `lz4` CLI can only manage frames. +*/ + +/*^*************************************************************** +* Export parameters +*****************************************************************/ +/* +* LZ4_DLL_EXPORT : +* Enable exporting of functions when building a Windows DLL +* LZ4LIB_VISIBILITY : +* Control library symbols visibility. +*/ +#ifndef LZ4LIB_VISIBILITY +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default"))) +# else +# define LZ4LIB_VISIBILITY +# endif +#endif +#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) +# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY +#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) +# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/ +#else +# define LZ4LIB_API LZ4LIB_VISIBILITY +#endif + +/*! LZ4_FREESTANDING : + * When this macro is set to 1, it enables "freestanding mode" that is + * suitable for typical freestanding environment which doesn't support + * standard C library. + * + * - LZ4_FREESTANDING is a compile-time switch. + * - It requires the following macros to be defined: + * LZ4_memcpy, LZ4_memmove, LZ4_memset. + * - It only enables LZ4/HC functions which don't use heap. + * All LZ4F_* functions are not supported. + * - See tests/freestanding.c to check its basic setup. + */ +#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1) +# define LZ4_HEAPMODE 0 +# define LZ4HC_HEAPMODE 0 +# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1 +# if !defined(LZ4_memcpy) +# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'." +# endif +# if !defined(LZ4_memset) +# error "LZ4_FREESTANDING requires macro 'LZ4_memset'." +# endif +# if !defined(LZ4_memmove) +# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'." +# endif +#elif ! defined(LZ4_FREESTANDING) +# define LZ4_FREESTANDING 0 +#endif + + +/*------ Version ------*/ +#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ +#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ +#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */ + +#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) + +#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE +#define LZ4_QUOTE(str) #str +#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) +#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */ + +LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */ +LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */ + + +/*-************************************ +* Tuning parameter +**************************************/ +#define LZ4_MEMORY_USAGE_MIN 10 +#define LZ4_MEMORY_USAGE_DEFAULT 14 +#define LZ4_MEMORY_USAGE_MAX 20 + +/*! + * LZ4_MEMORY_USAGE : + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) + * Increasing memory usage improves compression ratio, at the cost of speed. + * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. + * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache + */ +#ifndef LZ4_MEMORY_USAGE +# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT +#endif + +#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) +# error "LZ4_MEMORY_USAGE is too small !" +#endif + +#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX) +# error "LZ4_MEMORY_USAGE is too large !" +#endif + +/*-************************************ +* Simple Functions +**************************************/ +/*! LZ4_compress_default() : + * Compresses 'srcSize' bytes from buffer 'src' + * into already allocated 'dst' buffer of size 'dstCapacity'. + * Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize). + * It also runs faster, so it's a recommended setting. + * If the function cannot compress 'src' into a more limited 'dst' budget, + * compression stops *immediately*, and the function result is zero. + * In which case, 'dst' content is undefined (invalid). + * srcSize : max supported value is LZ4_MAX_INPUT_SIZE. + * dstCapacity : size of buffer 'dst' (which must be already allocated) + * @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity) + * or 0 if compression fails + * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer). + */ +LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity); + +/*! LZ4_decompress_safe() : + * compressedSize : is the exact complete size of the compressed block. + * dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size. + * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity) + * If destination buffer is not large enough, decoding will stop and output an error code (negative value). + * If the source stream is detected malformed, the function will stop decoding and return a negative result. + * Note 1 : This function is protected against malicious data packets : + * it will never writes outside 'dst' buffer, nor read outside 'source' buffer, + * even if the compressed block is maliciously modified to order the decoder to do these actions. + * In such case, the decoder stops immediately, and considers the compressed block malformed. + * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them. + * The implementation is free to send / store / derive this information in whichever way is most beneficial. + * If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead. + */ +LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity); + + +/*-************************************ +* Advanced Functions +**************************************/ +#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */ +#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16) + +/*! LZ4_compressBound() : + Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible) + This function is primarily useful for memory allocation purposes (destination buffer size). + Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example). + Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize) + inputSize : max supported value is LZ4_MAX_INPUT_SIZE + return : maximum output size in a "worst case" scenario + or 0, if input size is incorrect (too large or negative) +*/ +LZ4LIB_API int LZ4_compressBound(int inputSize); + +/*! LZ4_compress_fast() : + Same as LZ4_compress_default(), but allows selection of "acceleration" factor. + The larger the acceleration value, the faster the algorithm, but also the lesser the compression. + It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed. + An acceleration value of "1" is the same as regular LZ4_compress_default() + Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c). + Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c). +*/ +LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + + +/*! LZ4_compress_fast_extState() : + * Same as LZ4_compress_fast(), using an externally allocated memory space for its state. + * Use LZ4_sizeofState() to know how much memory must be allocated, + * and allocate it on 8-bytes boundaries (using `malloc()` typically). + * Then, provide this buffer as `void* state` to compression function. + */ +LZ4LIB_API int LZ4_sizeofState(void); +LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + + +/*! LZ4_compress_destSize() : + * Reverse the logic : compresses as much data as possible from 'src' buffer + * into already allocated buffer 'dst', of size >= 'targetDestSize'. + * This function either compresses the entire 'src' content into 'dst' if it's large enough, + * or fill 'dst' buffer completely with as much data as possible from 'src'. + * note: acceleration parameter is fixed to "default". + * + * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'. + * New value is necessarily <= input value. + * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize) + * or 0 if compression fails. + * + * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+): + * the produced compressed content could, in specific circumstances, + * require to be decompressed into a destination buffer larger + * by at least 1 byte than the content to decompress. + * If an application uses `LZ4_compress_destSize()`, + * it's highly recommended to update liblz4 to v1.9.2 or better. + * If this can't be done or ensured, + * the receiving decompression function should provide + * a dstCapacity which is > decompressedSize, by at least 1 byte. + * See https://github.com/lz4/lz4/issues/859 for details + */ +LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize); + + +/*! LZ4_decompress_safe_partial() : + * Decompress an LZ4 compressed block, of size 'srcSize' at position 'src', + * into destination buffer 'dst' of size 'dstCapacity'. + * Up to 'targetOutputSize' bytes will be decoded. + * The function stops decoding on reaching this objective. + * This can be useful to boost performance + * whenever only the beginning of a block is required. + * + * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize) + * If source stream is detected malformed, function returns a negative result. + * + * Note 1 : @return can be < targetOutputSize, if compressed block contains less data. + * + * Note 2 : targetOutputSize must be <= dstCapacity + * + * Note 3 : this function effectively stops decoding on reaching targetOutputSize, + * so dstCapacity is kind of redundant. + * This is because in older versions of this function, + * decoding operation would still write complete sequences. + * Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize, + * it could write more bytes, though only up to dstCapacity. + * Some "margin" used to be required for this operation to work properly. + * Thankfully, this is no longer necessary. + * The function nonetheless keeps the same signature, in an effort to preserve API compatibility. + * + * Note 4 : If srcSize is the exact size of the block, + * then targetOutputSize can be any value, + * including larger than the block's decompressed size. + * The function will, at most, generate block's decompressed size. + * + * Note 5 : If srcSize is _larger_ than block's compressed size, + * then targetOutputSize **MUST** be <= block's decompressed size. + * Otherwise, *silent corruption will occur*. + */ +LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity); + + +/*-********************************************* +* Streaming Compression Functions +***********************************************/ +typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ + +/** + Note about RC_INVOKED + + - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio). + https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros + + - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars) + and reports warning "RC4011: identifier truncated". + + - To eliminate the warning, we surround long preprocessor symbol with + "#if !defined(RC_INVOKED) ... #endif" block that means + "skip this block when rc.exe is trying to read it". +*/ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); +LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif + +/*! LZ4_resetStream_fast() : v1.9.0+ + * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks + * (e.g., LZ4_compress_fast_continue()). + * + * An LZ4_stream_t must be initialized once before usage. + * This is automatically done when created by LZ4_createStream(). + * However, should the LZ4_stream_t be simply declared on stack (for example), + * it's necessary to initialize it first, using LZ4_initStream(). + * + * After init, start any new stream with LZ4_resetStream_fast(). + * A same LZ4_stream_t can be re-used multiple times consecutively + * and compress multiple streams, + * provided that it starts each new stream with LZ4_resetStream_fast(). + * + * LZ4_resetStream_fast() is much faster than LZ4_initStream(), + * but is not compatible with memory regions containing garbage data. + * + * Note: it's only useful to call LZ4_resetStream_fast() + * in the context of streaming compression. + * The *extState* functions perform their own resets. + * Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive. + */ +LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr); + +/*! LZ4_loadDict() : + * Use this function to reference a static dictionary into LZ4_stream_t. + * The dictionary must remain available during compression. + * LZ4_loadDict() triggers a reset, so any previous data will be forgotten. + * The same dictionary will have to be loaded on decompression side for successful decoding. + * Dictionary are useful for better compression of small data (KB range). + * While LZ4 accept any input as dictionary, + * results are generally better when using Zstandard's Dictionary Builder. + * Loading a size of 0 is allowed, and is the same as reset. + * @return : loaded dictionary size, in bytes (necessarily <= 64 KB) + */ +LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize); + +/*! LZ4_compress_fast_continue() : + * Compress 'src' content using data from previously compressed blocks, for better compression ratio. + * 'dst' buffer must be already allocated. + * If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster. + * + * @return : size of compressed block + * or 0 if there is an error (typically, cannot fit into 'dst'). + * + * Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block. + * Each block has precise boundaries. + * Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata. + * It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together. + * + * Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory ! + * + * Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB. + * Make sure that buffers are separated, by at least one byte. + * This construction ensures that each block only depends on previous block. + * + * Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB. + * + * Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed. + */ +LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_saveDict() : + * If last 64KB data cannot be guaranteed to remain available at its current memory location, + * save it into a safer place (char* safeBuffer). + * This is schematically equivalent to a memcpy() followed by LZ4_loadDict(), + * but is much faster, because LZ4_saveDict() doesn't need to rebuild tables. + * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error. + */ +LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize); + + +/*-********************************************** +* Streaming Decompression Functions +* Bufferless synchronous API +************************************************/ +typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ + +/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() : + * creation / destruction of streaming decompression tracking context. + * A tracking context can be re-used multiple times. + */ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); +LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif + +/*! LZ4_setStreamDecode() : + * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. + * Use this function to start decompression of a new stream of blocks. + * A dictionary can optionally be set. Use NULL or size 0 for a reset order. + * Dictionary is presumed stable : it must remain accessible and unmodified during next decompression. + * @return : 1 if OK, 0 if error + */ +LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize); + +/*! LZ4_decoderRingBufferSize() : v1.8.2+ + * Note : in a ring buffer scenario (optional), + * blocks are presumed decompressed next to each other + * up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize), + * at which stage it resumes from beginning of ring buffer. + * When setting such a ring buffer for streaming decompression, + * provides the minimum size of this ring buffer + * to be compatible with any source respecting maxBlockSize condition. + * @return : minimum ring buffer size, + * or 0 if there is an error (invalid maxBlockSize). + */ +LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); +#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */ + +/*! LZ4_decompress_*_continue() : + * These decoding functions allow decompression of consecutive blocks in "streaming" mode. + * A block is an unsplittable entity, it must be presented entirely to a decompression function. + * Decompression functions only accepts one block at a time. + * The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded. + * If less than 64KB of data has been decoded, all the data must be present. + * + * Special : if decompression side sets a ring buffer, it must respect one of the following conditions : + * - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize). + * maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes. + * In which case, encoding and decoding buffers do not need to be synchronized. + * Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize. + * - Synchronized mode : + * Decompression buffer size is _exactly_ the same as compression buffer size, + * and follows exactly same update rule (block boundaries at same positions), + * and decoding function is provided with exact decompressed size of each block (exception for last block of the stream), + * _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB). + * - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes. + * In which case, encoding and decoding buffers do not need to be synchronized, + * and encoding ring buffer can have any size, including small ones ( < 64 KB). + * + * Whenever these conditions are not possible, + * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, + * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. +*/ +LZ4LIB_API int +LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* src, char* dst, + int srcSize, int dstCapacity); + + +/*! LZ4_decompress_*_usingDict() : + * These decoding functions work the same as + * a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue() + * They are stand-alone, and don't need an LZ4_streamDecode_t structure. + * Dictionary is presumed stable : it must remain accessible and unmodified during decompression. + * Performance tip : Decompression speed can be substantially increased + * when dst == dictStart + dictSize. + */ +LZ4LIB_API int +LZ4_decompress_safe_usingDict(const char* src, char* dst, + int srcSize, int dstCapacity, + const char* dictStart, int dictSize); + +LZ4LIB_API int +LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, + int compressedSize, + int targetOutputSize, int maxOutputSize, + const char* dictStart, int dictSize); + +#endif /* LZ4_H_2983827168210 */ + + +/*^************************************* + * !!!!!! STATIC LINKING ONLY !!!!!! + ***************************************/ + +/*-**************************************************************************** + * Experimental section + * + * Symbols declared in this section must be considered unstable. Their + * signatures or semantics may change, or they may be removed altogether in the + * future. They are therefore only safe to depend on when the caller is + * statically linked against the library. + * + * To protect against unsafe usage, not only are the declarations guarded, + * the definitions are hidden by default + * when building LZ4 as a shared/dynamic library. + * + * In order to access these declarations, + * define LZ4_STATIC_LINKING_ONLY in your application + * before including LZ4's headers. + * + * In order to make their implementations accessible dynamically, you must + * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library. + ******************************************************************************/ + +#ifdef LZ4_STATIC_LINKING_ONLY + +#ifndef LZ4_STATIC_3504398509 +#define LZ4_STATIC_3504398509 + +#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS +#define LZ4LIB_STATIC_API LZ4LIB_API +#else +#define LZ4LIB_STATIC_API +#endif + + +/*! LZ4_compress_fast_extState_fastReset() : + * A variant of LZ4_compress_fast_extState(). + * + * Using this variant avoids an expensive initialization step. + * It is only safe to call if the state buffer is known to be correctly initialized already + * (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized"). + * From a high level, the difference is that + * this function initializes the provided state with a call to something like LZ4_resetStream_fast() + * while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream(). + */ +LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration); + +/*! LZ4_attach_dictionary() : + * This is an experimental API that allows + * efficient use of a static dictionary many times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a + * working LZ4_stream_t, this function introduces a no-copy setup mechanism, + * in which the working stream references the dictionary stream in-place. + * + * Several assumptions are made about the state of the dictionary stream. + * Currently, only streams which have been prepared by LZ4_loadDict() should + * be expected to work. + * + * Alternatively, the provided dictionaryStream may be NULL, + * in which case any existing dictionary stream is unset. + * + * If a dictionary is provided, it replaces any pre-existing stream history. + * The dictionary contents are the only history that can be referenced and + * logically immediately precede the data compressed in the first subsequent + * compression call. + * + * The dictionary will only remain attached to the working stream through the + * first compression call, at the end of which it is cleared. The dictionary + * stream (and source buffer) must remain in-place / accessible / unchanged + * through the completion of the first compression call on the stream. + */ +LZ4LIB_STATIC_API void +LZ4_attach_dictionary(LZ4_stream_t* workingStream, + const LZ4_stream_t* dictionaryStream); + + +/*! In-place compression and decompression + * + * It's possible to have input and output sharing the same buffer, + * for highly constrained memory environments. + * In both cases, it requires input to lay at the end of the buffer, + * and decompression to start at beginning of the buffer. + * Buffer size must feature some margin, hence be larger than final size. + * + * |<------------------------buffer--------------------------------->| + * |<-----------compressed data--------->| + * |<-----------decompressed size------------------>| + * |<----margin---->| + * + * This technique is more useful for decompression, + * since decompressed size is typically larger, + * and margin is short. + * + * In-place decompression will work inside any buffer + * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize). + * This presumes that decompressedSize > compressedSize. + * Otherwise, it means compression actually expanded data, + * and it would be more efficient to store such data with a flag indicating it's not compressed. + * This can happen when data is not compressible (already compressed, or encrypted). + * + * For in-place compression, margin is larger, as it must be able to cope with both + * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX, + * and data expansion, which can happen when input is not compressible. + * As a consequence, buffer size requirements are much higher, + * and memory savings offered by in-place compression are more limited. + * + * There are ways to limit this cost for compression : + * - Reduce history size, by modifying LZ4_DISTANCE_MAX. + * Note that it is a compile-time constant, so all compressions will apply this limit. + * Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX, + * so it's a reasonable trick when inputs are known to be small. + * - Require the compressor to deliver a "maximum compressed size". + * This is the `dstCapacity` parameter in `LZ4_compress*()`. + * When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail, + * in which case, the return code will be 0 (zero). + * The caller must be ready for these cases to happen, + * and typically design a backup scheme to send data uncompressed. + * The combination of both techniques can significantly reduce + * the amount of margin required for in-place compression. + * + * In-place compression can work in any buffer + * which size is >= (maxCompressedSize) + * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success. + * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX, + * so it's possible to reduce memory requirements by playing with them. + */ + +#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32) +#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */ + +#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */ +# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */ +#endif + +#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */ +#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */ + +#endif /* LZ4_STATIC_3504398509 */ +#endif /* LZ4_STATIC_LINKING_ONLY */ + + + +#ifndef LZ4_H_98237428734687 +#define LZ4_H_98237428734687 + +/*-************************************************************ + * Private Definitions + ************************************************************** + * Do not use these definitions directly. + * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`. + * Accessing members will expose user code to API and/or ABI break in future versions of the library. + **************************************************************/ +#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2) +#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE) +#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */ + +#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# include + typedef int8_t LZ4_i8; + typedef uint8_t LZ4_byte; + typedef uint16_t LZ4_u16; + typedef uint32_t LZ4_u32; +#else + typedef signed char LZ4_i8; + typedef unsigned char LZ4_byte; + typedef unsigned short LZ4_u16; + typedef unsigned int LZ4_u32; +#endif + +/*! LZ4_stream_t : + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_stream_t object. +**/ + +typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; +struct LZ4_stream_t_internal { + LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; + const LZ4_byte* dictionary; + const LZ4_stream_t_internal* dictCtx; + LZ4_u32 currentOffset; + LZ4_u32 tableType; + LZ4_u32 dictSize; + /* Implicit padding to ensure structure is aligned */ +}; + +#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ +union LZ4_stream_u { + char minStateSize[LZ4_STREAM_MINSIZE]; + LZ4_stream_t_internal internal_donotuse; +}; /* previously typedef'd to LZ4_stream_t */ + + +/*! LZ4_initStream() : v1.9.0+ + * An LZ4_stream_t structure must be initialized at least once. + * This is automatically done when invoking LZ4_createStream(), + * but it's not when the structure is simply declared on stack (for example). + * + * Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t. + * It can also initialize any arbitrary buffer of sufficient size, + * and will @return a pointer of proper type upon initialization. + * + * Note : initialization fails if size and alignment conditions are not respected. + * In which case, the function will @return NULL. + * Note2: An LZ4_stream_t structure guarantees correct alignment and size. + * Note3: Before v1.9.0, use LZ4_resetStream() instead +**/ +LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); + + +/*! LZ4_streamDecode_t : + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_streamDecode_t object. +**/ +typedef struct { + const LZ4_byte* externalDict; + const LZ4_byte* prefixEnd; + size_t extDictSize; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + +#define LZ4_STREAMDECODE_MINSIZE 32 +union LZ4_streamDecode_u { + char minStateSize[LZ4_STREAMDECODE_MINSIZE]; + LZ4_streamDecode_t_internal internal_donotuse; +} ; /* previously typedef'd to LZ4_streamDecode_t */ + + + +/*-************************************ +* Obsolete Functions +**************************************/ + +/*! Deprecation warnings + * + * Deprecated functions make the compiler generate a warning when invoked. + * This is meant to invite users to update their source code. + * Should deprecation warnings be a problem, it is generally possible to disable them, + * typically with -Wno-deprecated-declarations for gcc + * or _CRT_SECURE_NO_WARNINGS in Visual. + * + * Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS + * before including the header file. + */ +#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS +# define LZ4_DEPRECATED(message) /* disable deprecation warnings */ +#else +# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */ +# define LZ4_DEPRECATED(message) [[deprecated(message)]] +# elif defined(_MSC_VER) +# define LZ4_DEPRECATED(message) __declspec(deprecated(message)) +# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45)) +# define LZ4_DEPRECATED(message) __attribute__((deprecated(message))) +# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31) +# define LZ4_DEPRECATED(message) __attribute__((deprecated)) +# else +# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler") +# define LZ4_DEPRECATED(message) /* disabled */ +# endif +#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */ + +/*! Obsolete compression functions (since v1.7.3) */ +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize); +LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize); +LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize); + +/*! Obsolete decompression functions (since v1.8.0) */ +LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize); +LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize); + +/* Obsolete streaming functions (since v1.7.0) + * degraded functionality; do not use! + * + * In order to perform streaming compression, these functions depended on data + * that is no longer tracked in the state. They have been preserved as well as + * possible: using them will still produce a correct output. However, they don't + * actually retain any history between compression calls. The compression ratio + * achieved will therefore be no better than compressing each chunk + * independently. + */ +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void); +LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer); +LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state); + +/*! Obsolete streaming decoding functions (since v1.7.0) */ +LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize); +LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize); + +/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) : + * These functions used to be faster than LZ4_decompress_safe(), + * but this is no longer the case. They are now slower. + * This is because LZ4_decompress_fast() doesn't know the input size, + * and therefore must progress more cautiously into the input buffer to not read beyond the end of block. + * On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability. + * As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated. + * + * The last remaining LZ4_decompress_fast() specificity is that + * it can decompress a block without knowing its compressed size. + * Such functionality can be achieved in a more secure manner + * by employing LZ4_decompress_safe_partial(). + * + * Parameters: + * originalSize : is the uncompressed size to regenerate. + * `dst` must be already allocated, its size must be >= 'originalSize' bytes. + * @return : number of bytes read from source buffer (== compressed size). + * The function expects to finish at block's end exactly. + * If the source stream is detected malformed, the function stops decoding and returns a negative result. + * note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer. + * However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds. + * Also, since match offsets are not validated, match reads from 'src' may underflow too. + * These issues never happen if input (compressed) data is correct. + * But they may happen if input data is invalid (error or intentional tampering). + * As a consequence, use these functions in trusted environments with trusted data **only**. + */ +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead") +LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead") +LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize); +LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead") +LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize); + +/*! LZ4_resetStream() : + * An LZ4_stream_t structure must be initialized at least once. + * This is done with LZ4_initStream(), or LZ4_resetStream(). + * Consider switching to LZ4_initStream(), + * invoking LZ4_resetStream() will trigger deprecation warnings in the future. + */ +LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr); + + +#endif /* LZ4_H_98237428734687 */ + + +#if defined (__cplusplus) +} +#endif diff --git a/r5dev/thirdparty/lz4/lz4file.c b/r5dev/thirdparty/lz4/lz4file.c new file mode 100644 index 00000000..eaf9b170 --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4file.c @@ -0,0 +1,311 @@ +/* + * LZ4 file library + * Copyright (C) 2022, Xiaomi Inc. + * + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://www.lz4.org + * - LZ4 source repository : https://github.com/lz4/lz4 + */ +#include +#include +#include "lz4.h" +#include "lz4file.h" + +struct LZ4_readFile_s { + LZ4F_dctx* dctxPtr; + FILE* fp; + LZ4_byte* srcBuf; + size_t srcBufNext; + size_t srcBufSize; + size_t srcBufMaxSize; +}; + +struct LZ4_writeFile_s { + LZ4F_cctx* cctxPtr; + FILE* fp; + LZ4_byte* dstBuf; + size_t maxWriteSize; + size_t dstBufMaxSize; + LZ4F_errorCode_t errCode; +}; + +LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp) +{ + char buf[LZ4F_HEADER_SIZE_MAX]; + size_t consumedSize; + LZ4F_errorCode_t ret; + LZ4F_frameInfo_t info; + + if (fp == NULL || lz4fRead == NULL) { + return -LZ4F_ERROR_GENERIC; + } + + *lz4fRead = (LZ4_readFile_t*)calloc(1, sizeof(LZ4_readFile_t)); + if (*lz4fRead == NULL) { + return -LZ4F_ERROR_allocation_failed; + } + + ret = LZ4F_createDecompressionContext(&(*lz4fRead)->dctxPtr, LZ4F_getVersion()); + if (LZ4F_isError(ret)) { + free(*lz4fRead); + return ret; + } + + (*lz4fRead)->fp = fp; + consumedSize = fread(buf, 1, sizeof(buf), (*lz4fRead)->fp); + if (consumedSize != sizeof(buf)) { + free(*lz4fRead); + return -LZ4F_ERROR_GENERIC; + } + + ret = LZ4F_getFrameInfo((*lz4fRead)->dctxPtr, &info, buf, &consumedSize); + if (LZ4F_isError(ret)) { + LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr); + free(*lz4fRead); + return ret; + } + + switch (info.blockSizeID) { + case LZ4F_default : + case LZ4F_max64KB : + (*lz4fRead)->srcBufMaxSize = 64 * 1024; + break; + case LZ4F_max256KB: + (*lz4fRead)->srcBufMaxSize = 256 * 1024; + break; + case LZ4F_max1MB: + (*lz4fRead)->srcBufMaxSize = 1 * 1024 * 1024; + break; + case LZ4F_max4MB: + (*lz4fRead)->srcBufMaxSize = 4 * 1024 * 1024; + break; + default: + LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr); + free(*lz4fRead); + return -LZ4F_ERROR_maxBlockSize_invalid; + } + + (*lz4fRead)->srcBuf = (LZ4_byte*)malloc((*lz4fRead)->srcBufMaxSize); + if ((*lz4fRead)->srcBuf == NULL) { + LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr); + free(lz4fRead); + return -LZ4F_ERROR_allocation_failed; + } + + (*lz4fRead)->srcBufSize = sizeof(buf) - consumedSize; + memcpy((*lz4fRead)->srcBuf, buf + consumedSize, (*lz4fRead)->srcBufSize); + + return ret; +} + +size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size) +{ + LZ4_byte* p = (LZ4_byte*)buf; + size_t next = 0; + + if (lz4fRead == NULL || buf == NULL) + return -LZ4F_ERROR_GENERIC; + + while (next < size) { + size_t srcsize = lz4fRead->srcBufSize - lz4fRead->srcBufNext; + size_t dstsize = size - next; + size_t ret; + + if (srcsize == 0) { + ret = fread(lz4fRead->srcBuf, 1, lz4fRead->srcBufMaxSize, lz4fRead->fp); + if (ret > 0) { + lz4fRead->srcBufSize = ret; + srcsize = lz4fRead->srcBufSize; + lz4fRead->srcBufNext = 0; + } + else if (ret == 0) { + break; + } + else { + return -LZ4F_ERROR_GENERIC; + } + } + + ret = LZ4F_decompress(lz4fRead->dctxPtr, + p, &dstsize, + lz4fRead->srcBuf + lz4fRead->srcBufNext, + &srcsize, + NULL); + if (LZ4F_isError(ret)) { + return ret; + } + + lz4fRead->srcBufNext += srcsize; + next += dstsize; + p += dstsize; + } + + return next; +} + +LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead) +{ + if (lz4fRead == NULL) + return -LZ4F_ERROR_GENERIC; + LZ4F_freeDecompressionContext(lz4fRead->dctxPtr); + free(lz4fRead->srcBuf); + free(lz4fRead); + return LZ4F_OK_NoError; +} + +LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr) +{ + LZ4_byte buf[LZ4F_HEADER_SIZE_MAX]; + size_t ret; + + if (fp == NULL || lz4fWrite == NULL) + return -LZ4F_ERROR_GENERIC; + + *lz4fWrite = (LZ4_writeFile_t*)malloc(sizeof(LZ4_writeFile_t)); + if (*lz4fWrite == NULL) { + return -LZ4F_ERROR_allocation_failed; + } + if (prefsPtr != NULL) { + switch (prefsPtr->frameInfo.blockSizeID) { + case LZ4F_default : + case LZ4F_max64KB : + (*lz4fWrite)->maxWriteSize = 64 * 1024; + break; + case LZ4F_max256KB: + (*lz4fWrite)->maxWriteSize = 256 * 1024; + break; + case LZ4F_max1MB: + (*lz4fWrite)->maxWriteSize = 1 * 1024 * 1024; + break; + case LZ4F_max4MB: + (*lz4fWrite)->maxWriteSize = 4 * 1024 * 1024; + break; + default: + free(lz4fWrite); + return -LZ4F_ERROR_maxBlockSize_invalid; + } + } else { + (*lz4fWrite)->maxWriteSize = 64 * 1024; + } + + (*lz4fWrite)->dstBufMaxSize = LZ4F_compressBound((*lz4fWrite)->maxWriteSize, prefsPtr); + (*lz4fWrite)->dstBuf = (LZ4_byte*)malloc((*lz4fWrite)->dstBufMaxSize); + if ((*lz4fWrite)->dstBuf == NULL) { + free(*lz4fWrite); + return -LZ4F_ERROR_allocation_failed; + } + + ret = LZ4F_createCompressionContext(&(*lz4fWrite)->cctxPtr, LZ4F_getVersion()); + if (LZ4F_isError(ret)) { + free((*lz4fWrite)->dstBuf); + free(*lz4fWrite); + return ret; + } + + ret = LZ4F_compressBegin((*lz4fWrite)->cctxPtr, buf, LZ4F_HEADER_SIZE_MAX, prefsPtr); + if (LZ4F_isError(ret)) { + LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr); + free((*lz4fWrite)->dstBuf); + free(*lz4fWrite); + return ret; + } + + if (ret != fwrite(buf, 1, ret, fp)) { + LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr); + free((*lz4fWrite)->dstBuf); + free(*lz4fWrite); + return -LZ4F_ERROR_GENERIC; + } + + (*lz4fWrite)->fp = fp; + (*lz4fWrite)->errCode = LZ4F_OK_NoError; + return LZ4F_OK_NoError; +} + +size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, void* buf, size_t size) +{ + LZ4_byte* p = (LZ4_byte*)buf; + size_t remain = size; + size_t chunk; + size_t ret; + + if (lz4fWrite == NULL || buf == NULL) + return -LZ4F_ERROR_GENERIC; + while (remain) { + if (remain > lz4fWrite->maxWriteSize) + chunk = lz4fWrite->maxWriteSize; + else + chunk = remain; + + ret = LZ4F_compressUpdate(lz4fWrite->cctxPtr, + lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize, + p, chunk, + NULL); + if (LZ4F_isError(ret)) { + lz4fWrite->errCode = ret; + return ret; + } + + if(ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) { + lz4fWrite->errCode = -LZ4F_ERROR_GENERIC; + return -LZ4F_ERROR_GENERIC; + } + + p += chunk; + remain -= chunk; + } + + return size; +} + +LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite) +{ + LZ4F_errorCode_t ret = LZ4F_OK_NoError; + + if (lz4fWrite == NULL) + return -LZ4F_ERROR_GENERIC; + + if (lz4fWrite->errCode == LZ4F_OK_NoError) { + ret = LZ4F_compressEnd(lz4fWrite->cctxPtr, + lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize, + NULL); + if (LZ4F_isError(ret)) { + goto out; + } + + if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) { + ret = -LZ4F_ERROR_GENERIC; + } + } + +out: + LZ4F_freeCompressionContext(lz4fWrite->cctxPtr); + free(lz4fWrite->dstBuf); + free(lz4fWrite); + return ret; +} diff --git a/r5dev/thirdparty/lz4/lz4file.h b/r5dev/thirdparty/lz4/lz4file.h new file mode 100644 index 00000000..55271307 --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4file.h @@ -0,0 +1,93 @@ +/* + LZ4 file library + Header File + Copyright (C) 2022, Xiaomi Inc. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef LZ4FILE_H +#define LZ4FILE_H + +#include +#include "lz4frame_static.h" + +typedef struct LZ4_readFile_s LZ4_readFile_t; +typedef struct LZ4_writeFile_s LZ4_writeFile_t; + +/*! LZ4F_readOpen() : + * Set read lz4file handle. + * `lz4f` will set a lz4file handle. + * `fp` must be the return value of the lz4 file opened by fopen. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp); + +/*! LZ4F_read() : + * Read lz4file content to buffer. + * `lz4f` must use LZ4_readOpen to set first. + * `buf` read data buffer. + * `size` read data buffer size. + */ +LZ4FLIB_STATIC_API size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size); + +/*! LZ4F_readClose() : + * Close lz4file handle. + * `lz4f` must use LZ4_readOpen to set first. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead); + +/*! LZ4F_writeOpen() : + * Set write lz4file handle. + * `lz4f` will set a lz4file handle. + * `fp` must be the return value of the lz4 file opened by fopen. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr); + +/*! LZ4F_write() : + * Write buffer to lz4file. + * `lz4f` must use LZ4F_writeOpen to set first. + * `buf` write data buffer. + * `size` write data buffer size. + */ +LZ4FLIB_STATIC_API size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, void* buf, size_t size); + +/*! LZ4F_writeClose() : + * Close lz4file handle. + * `lz4f` must use LZ4F_writeOpen to set first. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite); + +#endif /* LZ4FILE_H */ + +#if defined (__cplusplus) +} +#endif diff --git a/r5dev/thirdparty/lz4/lz4frame.c b/r5dev/thirdparty/lz4/lz4frame.c new file mode 100644 index 00000000..174f9ae4 --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4frame.c @@ -0,0 +1,2078 @@ +/* + * LZ4 auto-framing library + * Copyright (C) 2011-2016, Yann Collet. + * + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://www.lz4.org + * - LZ4 source repository : https://github.com/lz4/lz4 + */ + +/* LZ4F is a stand-alone API to create LZ4-compressed Frames + * in full conformance with specification v1.6.1 . + * This library rely upon memory management capabilities (malloc, free) + * provided either by , + * or redirected towards another library of user's choice + * (see Memory Routines below). + */ + + +/*-************************************ +* Compiler Options +**************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +#endif + + +/*-************************************ +* Tuning parameters +**************************************/ +/* + * LZ4F_HEAPMODE : + * Select how default compression functions will allocate memory for their hash table, + * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()). + */ +#ifndef LZ4F_HEAPMODE +# define LZ4F_HEAPMODE 0 +#endif + + +/*-************************************ +* Library declarations +**************************************/ +#define LZ4F_STATIC_LINKING_ONLY +#include "lz4frame.h" +#define LZ4_STATIC_LINKING_ONLY +#include "lz4.h" +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.h" +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/*-************************************ +* Memory routines +**************************************/ +/* + * User may redirect invocations of + * malloc(), calloc() and free() + * towards another library or solution of their choice + * by modifying below section. +**/ + +#include /* memset, memcpy, memmove */ +#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */ +# define MEM_INIT(p,v,s) memset((p),(v),(s)) +#endif + +#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */ +# include /* malloc, calloc, free */ +# define ALLOC(s) malloc(s) +# define ALLOC_AND_ZERO(s) calloc(1,(s)) +# define FREEMEM(p) free(p) +#endif + +static void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem) +{ + /* custom calloc defined : use it */ + if (cmem.customCalloc != NULL) { + return cmem.customCalloc(cmem.opaqueState, s); + } + /* nothing defined : use default 's calloc() */ + if (cmem.customAlloc == NULL) { + return ALLOC_AND_ZERO(s); + } + /* only custom alloc defined : use it, and combine it with memset() */ + { void* const p = cmem.customAlloc(cmem.opaqueState, s); + if (p != NULL) MEM_INIT(p, 0, s); + return p; +} } + +static void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem) +{ + /* custom malloc defined : use it */ + if (cmem.customAlloc != NULL) { + return cmem.customAlloc(cmem.opaqueState, s); + } + /* nothing defined : use default 's malloc() */ + return ALLOC(s); +} + +static void LZ4F_free(void* p, LZ4F_CustomMem cmem) +{ + /* custom malloc defined : use it */ + if (cmem.customFree != NULL) { + cmem.customFree(cmem.opaqueState, p); + return; + } + /* nothing defined : use default 's free() */ + FREEMEM(p); +} + + +/*-************************************ +* Debug +**************************************/ +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1) +# include +#else +# ifndef assert +# define assert(condition) ((void)0) +# endif +#endif + +#define LZ4F_STATIC_ASSERT(c) { enum { LZ4F_static_assert = 1/(int)(!!(c)) }; } /* use only *after* variable declarations */ + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) && !defined(DEBUGLOG) +# include +static int g_debuglog_enable = 1; +# define DEBUGLOG(l, ...) { \ + if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) { \ + fprintf(stderr, __FILE__ ": "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, " \n"); \ + } } +#else +# define DEBUGLOG(l, ...) {} /* disabled */ +#endif + + +/*-************************************ +* Basic Types +**************************************/ +#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; + typedef int32_t S32; + typedef uint64_t U64; +#else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; + typedef signed int S32; + typedef unsigned long long U64; +#endif + + +/* unoptimized version; solves endianness & alignment issues */ +static U32 LZ4F_readLE32 (const void* src) +{ + const BYTE* const srcPtr = (const BYTE*)src; + U32 value32 = srcPtr[0]; + value32 += ((U32)srcPtr[1])<< 8; + value32 += ((U32)srcPtr[2])<<16; + value32 += ((U32)srcPtr[3])<<24; + return value32; +} + +static void LZ4F_writeLE32 (void* dst, U32 value32) +{ + BYTE* const dstPtr = (BYTE*)dst; + dstPtr[0] = (BYTE)value32; + dstPtr[1] = (BYTE)(value32 >> 8); + dstPtr[2] = (BYTE)(value32 >> 16); + dstPtr[3] = (BYTE)(value32 >> 24); +} + +static U64 LZ4F_readLE64 (const void* src) +{ + const BYTE* const srcPtr = (const BYTE*)src; + U64 value64 = srcPtr[0]; + value64 += ((U64)srcPtr[1]<<8); + value64 += ((U64)srcPtr[2]<<16); + value64 += ((U64)srcPtr[3]<<24); + value64 += ((U64)srcPtr[4]<<32); + value64 += ((U64)srcPtr[5]<<40); + value64 += ((U64)srcPtr[6]<<48); + value64 += ((U64)srcPtr[7]<<56); + return value64; +} + +static void LZ4F_writeLE64 (void* dst, U64 value64) +{ + BYTE* const dstPtr = (BYTE*)dst; + dstPtr[0] = (BYTE)value64; + dstPtr[1] = (BYTE)(value64 >> 8); + dstPtr[2] = (BYTE)(value64 >> 16); + dstPtr[3] = (BYTE)(value64 >> 24); + dstPtr[4] = (BYTE)(value64 >> 32); + dstPtr[5] = (BYTE)(value64 >> 40); + dstPtr[6] = (BYTE)(value64 >> 48); + dstPtr[7] = (BYTE)(value64 >> 56); +} + + +/*-************************************ +* Constants +**************************************/ +#ifndef LZ4_SRC_INCLUDED /* avoid double definition */ +# define KB *(1<<10) +# define MB *(1<<20) +# define GB *(1<<30) +#endif + +#define _1BIT 0x01 +#define _2BITS 0x03 +#define _3BITS 0x07 +#define _4BITS 0x0F +#define _8BITS 0xFF + +#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U +#define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB + +static const size_t minFHSize = LZ4F_HEADER_SIZE_MIN; /* 7 */ +static const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX; /* 19 */ +static const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE; /* block header : size, and compress flag */ +static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE; /* block footer : checksum (optional) */ + + +/*-************************************ +* Structures and local types +**************************************/ + +typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_blockCompression_t; + +typedef struct LZ4F_cctx_s +{ + LZ4F_CustomMem cmem; + LZ4F_preferences_t prefs; + U32 version; + U32 cStage; + const LZ4F_CDict* cdict; + size_t maxBlockSize; + size_t maxBufferSize; + BYTE* tmpBuff; /* internal buffer, for streaming */ + BYTE* tmpIn; /* starting position of data compress within internal buffer (>= tmpBuff) */ + size_t tmpInSize; /* amount of data to compress after tmpIn */ + U64 totalInSize; + XXH32_state_t xxh; + void* lz4CtxPtr; + U16 lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */ + U16 lz4CtxState; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */ + LZ4F_blockCompression_t blockCompression; +} LZ4F_cctx_t; + + +/*-************************************ +* Error management +**************************************/ +#define LZ4F_GENERATE_STRING(STRING) #STRING, +static const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) }; + + +unsigned LZ4F_isError(LZ4F_errorCode_t code) +{ + return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode)); +} + +const char* LZ4F_getErrorName(LZ4F_errorCode_t code) +{ + static const char* codeError = "Unspecified error code"; + if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)]; + return codeError; +} + +LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult) +{ + if (!LZ4F_isError(functionResult)) return LZ4F_OK_NoError; + return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult); +} + +static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code) +{ + /* A compilation error here means sizeof(ptrdiff_t) is not large enough */ + LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t)); + return (LZ4F_errorCode_t)-(ptrdiff_t)code; +} + +#define RETURN_ERROR(e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e) + +#define RETURN_ERROR_IF(c,e) if (c) RETURN_ERROR(e) + +#define FORWARD_IF_ERROR(r) if (LZ4F_isError(r)) return (r) + +unsigned LZ4F_getVersion(void) { return LZ4F_VERSION; } + +int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX; } + +size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID) +{ + static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB }; + + if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT; + if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB) + RETURN_ERROR(maxBlockSize_invalid); + { int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB; + return blockSizes[blockSizeIdx]; +} } + +/*-************************************ +* Private functions +**************************************/ +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +static BYTE LZ4F_headerChecksum (const void* header, size_t length) +{ + U32 const xxh = XXH32(header, length, 0); + return (BYTE)(xxh >> 8); +} + + +/*-************************************ +* Simple-pass compression functions +**************************************/ +static LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID, + const size_t srcSize) +{ + LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB; + size_t maxBlockSize = 64 KB; + while (requestedBSID > proposedBSID) { + if (srcSize <= maxBlockSize) + return proposedBSID; + proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1); + maxBlockSize <<= 2; + } + return requestedBSID; +} + +/*! LZ4F_compressBound_internal() : + * Provides dstCapacity given a srcSize to guarantee operation success in worst case situations. + * prefsPtr is optional : if NULL is provided, preferences will be set to cover worst case scenario. + * @return is always the same for a srcSize and prefsPtr, so it can be relied upon to size reusable buffers. + * When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() operations. + */ +static size_t LZ4F_compressBound_internal(size_t srcSize, + const LZ4F_preferences_t* preferencesPtr, + size_t alreadyBuffered) +{ + LZ4F_preferences_t prefsNull = LZ4F_INIT_PREFERENCES; + prefsNull.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled; /* worst case */ + prefsNull.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled; /* worst case */ + { const LZ4F_preferences_t* const prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr; + U32 const flush = prefsPtr->autoFlush | (srcSize==0); + LZ4F_blockSizeID_t const blockID = prefsPtr->frameInfo.blockSizeID; + size_t const blockSize = LZ4F_getBlockSize(blockID); + size_t const maxBuffered = blockSize - 1; + size_t const bufferedSize = MIN(alreadyBuffered, maxBuffered); + size_t const maxSrcSize = srcSize + bufferedSize; + unsigned const nbFullBlocks = (unsigned)(maxSrcSize / blockSize); + size_t const partialBlockSize = maxSrcSize & (blockSize-1); + size_t const lastBlockSize = flush ? partialBlockSize : 0; + unsigned const nbBlocks = nbFullBlocks + (lastBlockSize>0); + + size_t const blockCRCSize = BFSize * prefsPtr->frameInfo.blockChecksumFlag; + size_t const frameEnd = BHSize + (prefsPtr->frameInfo.contentChecksumFlag*BFSize); + + return ((BHSize + blockCRCSize) * nbBlocks) + + (blockSize * nbFullBlocks) + lastBlockSize + frameEnd; + } +} + +size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr) +{ + LZ4F_preferences_t prefs; + size_t const headerSize = maxFHSize; /* max header size, including optional fields */ + + if (preferencesPtr!=NULL) prefs = *preferencesPtr; + else MEM_INIT(&prefs, 0, sizeof(prefs)); + prefs.autoFlush = 1; + + return headerSize + LZ4F_compressBound_internal(srcSize, &prefs, 0);; +} + + +/*! LZ4F_compressFrame_usingCDict() : + * Compress srcBuffer using a dictionary, in a single step. + * cdict can be NULL, in which case, no dictionary is used. + * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * The LZ4F_preferences_t structure is optional : you may provide NULL as argument, + * however, it's the only way to provide a dictID, so it's not recommended. + * @return : number of bytes written into dstBuffer, + * or an error code if it fails (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* preferencesPtr) +{ + LZ4F_preferences_t prefs; + LZ4F_compressOptions_t options; + BYTE* const dstStart = (BYTE*) dstBuffer; + BYTE* dstPtr = dstStart; + BYTE* const dstEnd = dstStart + dstCapacity; + + if (preferencesPtr!=NULL) + prefs = *preferencesPtr; + else + MEM_INIT(&prefs, 0, sizeof(prefs)); + if (prefs.frameInfo.contentSize != 0) + prefs.frameInfo.contentSize = (U64)srcSize; /* auto-correct content size if selected (!=0) */ + + prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize); + prefs.autoFlush = 1; + if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID)) + prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* only one block => no need for inter-block link */ + + MEM_INIT(&options, 0, sizeof(options)); + options.stableSrc = 1; + + RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall); + + { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs); /* write header */ + FORWARD_IF_ERROR(headerSize); + dstPtr += headerSize; /* header size */ } + + assert(dstEnd >= dstPtr); + { size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options); + FORWARD_IF_ERROR(cSize); + dstPtr += cSize; } + + assert(dstEnd >= dstPtr); + { size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options); /* flush last block, and generate suffix */ + FORWARD_IF_ERROR(tailSize); + dstPtr += tailSize; } + + assert(dstEnd >= dstStart); + return (size_t)(dstPtr - dstStart); +} + + +/*! LZ4F_compressFrame() : + * Compress an entire srcBuffer into a valid LZ4 frame, in a single step. + * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default. + * @return : number of bytes written into dstBuffer. + * or an error code if it fails (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_preferences_t* preferencesPtr) +{ + size_t result; +#if (LZ4F_HEAPMODE) + LZ4F_cctx_t* cctxPtr; + result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION); + FORWARD_IF_ERROR(result); +#else + LZ4F_cctx_t cctx; + LZ4_stream_t lz4ctx; + LZ4F_cctx_t* const cctxPtr = &cctx; + + MEM_INIT(&cctx, 0, sizeof(cctx)); + cctx.version = LZ4F_VERSION; + cctx.maxBufferSize = 5 MB; /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */ + if ( preferencesPtr == NULL + || preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) { + LZ4_initStream(&lz4ctx, sizeof(lz4ctx)); + cctxPtr->lz4CtxPtr = &lz4ctx; + cctxPtr->lz4CtxAlloc = 1; + cctxPtr->lz4CtxState = 1; + } +#endif + DEBUGLOG(4, "LZ4F_compressFrame"); + + result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity, + srcBuffer, srcSize, + NULL, preferencesPtr); + +#if (LZ4F_HEAPMODE) + LZ4F_freeCompressionContext(cctxPtr); +#else + if ( preferencesPtr != NULL + && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) { + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); + } +#endif + return result; +} + + +/*-*************************************************** +* Dictionary compression +*****************************************************/ + +struct LZ4F_CDict_s { + LZ4F_CustomMem cmem; + void* dictContent; + LZ4_stream_t* fastCtx; + LZ4_streamHC_t* HCCtx; +}; /* typedef'd to LZ4F_CDict within lz4frame_static.h */ + +LZ4F_CDict* +LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize) +{ + const char* dictStart = (const char*)dictBuffer; + LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem); + DEBUGLOG(4, "LZ4F_createCDict_advanced"); + if (!cdict) return NULL; + cdict->cmem = cmem; + if (dictSize > 64 KB) { + dictStart += dictSize - 64 KB; + dictSize = 64 KB; + } + cdict->dictContent = LZ4F_malloc(dictSize, cmem); + cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem); + if (cdict->fastCtx) + LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t)); + cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem); + if (cdict->HCCtx) + LZ4_initStream(cdict->HCCtx, sizeof(LZ4_streamHC_t)); + if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) { + LZ4F_freeCDict(cdict); + return NULL; + } + memcpy(cdict->dictContent, dictStart, dictSize); + LZ4_loadDict (cdict->fastCtx, (const char*)cdict->dictContent, (int)dictSize); + LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT); + LZ4_loadDictHC(cdict->HCCtx, (const char*)cdict->dictContent, (int)dictSize); + return cdict; +} + +/*! LZ4F_createCDict() : + * When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once. + * LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. + * LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. + * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict + * @return : digested dictionary for compression, or NULL if failed */ +LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize) +{ + DEBUGLOG(4, "LZ4F_createCDict"); + return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize); +} + +void LZ4F_freeCDict(LZ4F_CDict* cdict) +{ + if (cdict==NULL) return; /* support free on NULL */ + LZ4F_free(cdict->dictContent, cdict->cmem); + LZ4F_free(cdict->fastCtx, cdict->cmem); + LZ4F_free(cdict->HCCtx, cdict->cmem); + LZ4F_free(cdict, cdict->cmem); +} + + +/*-********************************* +* Advanced compression functions +***********************************/ + +LZ4F_cctx* +LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version) +{ + LZ4F_cctx* const cctxPtr = + (LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem); + if (cctxPtr==NULL) return NULL; + + cctxPtr->cmem = customMem; + cctxPtr->version = version; + cctxPtr->cStage = 0; /* Uninitialized. Next stage : init cctx */ + + return cctxPtr; +} + +/*! LZ4F_createCompressionContext() : + * The first thing to do is to create a compressionContext object, which will be used in all compression operations. + * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure. + * The version provided MUST be LZ4F_VERSION. It is intended to track potential incompatible differences between different binaries. + * The function will provide a pointer to an allocated LZ4F_compressionContext_t object. + * If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation. + * Object can release its memory using LZ4F_freeCompressionContext(); +**/ +LZ4F_errorCode_t +LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version) +{ + assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */ + /* in case it nonetheless happen in production */ + RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null); + + *LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version); + RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed); + return LZ4F_OK_NoError; +} + + +LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr) +{ + if (cctxPtr != NULL) { /* support free on NULL */ + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */ + LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem); + LZ4F_free(cctxPtr, cctxPtr->cmem); + } + return LZ4F_OK_NoError; +} + + +/** + * This function prepares the internal LZ4(HC) stream for a new compression, + * resetting the context and attaching the dictionary, if there is one. + * + * It needs to be called at the beginning of each independent compression + * stream (i.e., at the beginning of a frame in blockLinked mode, or at the + * beginning of each block in blockIndependent mode). + */ +static void LZ4F_initStream(void* ctx, + const LZ4F_CDict* cdict, + int level, + LZ4F_blockMode_t blockMode) { + if (level < LZ4HC_CLEVEL_MIN) { + if (cdict != NULL || blockMode == LZ4F_blockLinked) { + /* In these cases, we will call LZ4_compress_fast_continue(), + * which needs an already reset context. Otherwise, we'll call a + * one-shot API. The non-continued APIs internally perform their own + * resets at the beginning of their calls, where they know what + * tableType they need the context to be in. So in that case this + * would be misguided / wasted work. */ + LZ4_resetStream_fast((LZ4_stream_t*)ctx); + } + LZ4_attach_dictionary((LZ4_stream_t *)ctx, cdict ? cdict->fastCtx : NULL); + } else { + LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level); + LZ4_attach_HC_dictionary((LZ4_streamHC_t *)ctx, cdict ? cdict->HCCtx : NULL); + } +} + +static int ctxTypeID_to_size(int ctxTypeID) { + switch(ctxTypeID) { + case 1: + return LZ4_sizeofState(); + case 2: + return LZ4_sizeofStateHC(); + default: + return 0; + } +} + +/*! LZ4F_compressBegin_usingCDict() : + * init streaming compression AND writes frame header into @dstBuffer. + * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * @return : number of bytes written into @dstBuffer for the header + * or an error code (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* preferencesPtr) +{ + LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES; + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + + RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall); + if (preferencesPtr == NULL) preferencesPtr = &prefNull; + cctxPtr->prefs = *preferencesPtr; + + /* cctx Management */ + { U16 const ctxTypeID = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2; + int requiredSize = ctxTypeID_to_size(ctxTypeID); + int allocatedSize = ctxTypeID_to_size(cctxPtr->lz4CtxAlloc); + if (allocatedSize < requiredSize) { + /* not enough space allocated */ + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); + if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) { + /* must take ownership of memory allocation, + * in order to respect custom allocator contract */ + cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_stream_t), cctxPtr->cmem); + if (cctxPtr->lz4CtxPtr) + LZ4_initStream(cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t)); + } else { + cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctxPtr->cmem); + if (cctxPtr->lz4CtxPtr) + LZ4_initStreamHC(cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t)); + } + RETURN_ERROR_IF(cctxPtr->lz4CtxPtr == NULL, allocation_failed); + cctxPtr->lz4CtxAlloc = ctxTypeID; + cctxPtr->lz4CtxState = ctxTypeID; + } else if (cctxPtr->lz4CtxState != ctxTypeID) { + /* otherwise, a sufficient buffer is already allocated, + * but we need to reset it to the correct context type */ + if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) { + LZ4_initStream((LZ4_stream_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t)); + } else { + LZ4_initStreamHC((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t)); + LZ4_setCompressionLevel((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel); + } + cctxPtr->lz4CtxState = ctxTypeID; + } } + + /* Buffer Management */ + if (cctxPtr->prefs.frameInfo.blockSizeID == 0) + cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT; + cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID); + + { size_t const requiredBuffSize = preferencesPtr->autoFlush ? + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 64 KB : 0) : /* only needs past data up to window size */ + cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB : 0); + + if (cctxPtr->maxBufferSize < requiredBuffSize) { + cctxPtr->maxBufferSize = 0; + LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem); + cctxPtr->tmpBuff = (BYTE*)LZ4F_calloc(requiredBuffSize, cctxPtr->cmem); + RETURN_ERROR_IF(cctxPtr->tmpBuff == NULL, allocation_failed); + cctxPtr->maxBufferSize = requiredBuffSize; + } } + cctxPtr->tmpIn = cctxPtr->tmpBuff; + cctxPtr->tmpInSize = 0; + (void)XXH32_reset(&(cctxPtr->xxh), 0); + + /* context init */ + cctxPtr->cdict = cdict; + if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) { + /* frame init only for blockLinked : blockIndependent will be init at each block */ + LZ4F_initStream(cctxPtr->lz4CtxPtr, cdict, cctxPtr->prefs.compressionLevel, LZ4F_blockLinked); + } + if (preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN) { + LZ4_favorDecompressionSpeed((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, (int)preferencesPtr->favorDecSpeed); + } + + /* Magic Number */ + LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER); + dstPtr += 4; + { BYTE* const headerStart = dstPtr; + + /* FLG Byte */ + *dstPtr++ = (BYTE)(((1 & _2BITS) << 6) /* Version('01') */ + + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5) + + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4) + + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3) + + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2) + + (cctxPtr->prefs.frameInfo.dictID > 0) ); + /* BD Byte */ + *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4); + /* Optional Frame content size field */ + if (cctxPtr->prefs.frameInfo.contentSize) { + LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize); + dstPtr += 8; + cctxPtr->totalInSize = 0; + } + /* Optional dictionary ID field */ + if (cctxPtr->prefs.frameInfo.dictID) { + LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID); + dstPtr += 4; + } + /* Header CRC Byte */ + *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart)); + dstPtr++; + } + + cctxPtr->cStage = 1; /* header written, now request input data block */ + return (size_t)(dstPtr - dstStart); +} + + +/*! LZ4F_compressBegin() : + * init streaming compression AND writes frame header into @dstBuffer. + * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * @preferencesPtr can be NULL, in which case default parameters are selected. + * @return : number of bytes written into dstBuffer for the header + * or an error code (can be tested using LZ4F_isError()) + */ +size_t LZ4F_compressBegin(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_preferences_t* preferencesPtr) +{ + return LZ4F_compressBegin_usingCDict(cctxPtr, dstBuffer, dstCapacity, + NULL, preferencesPtr); +} + + +/* LZ4F_compressBound() : + * @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario. + * LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario. + * This function cannot fail. + */ +size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr) +{ + if (preferencesPtr && preferencesPtr->autoFlush) { + return LZ4F_compressBound_internal(srcSize, preferencesPtr, 0); + } + return LZ4F_compressBound_internal(srcSize, preferencesPtr, (size_t)-1); +} + + +typedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict); + + +/*! LZ4F_makeBlock(): + * compress a single block, add header and optional checksum. + * assumption : dst buffer capacity is >= BHSize + srcSize + crcSize + */ +static size_t LZ4F_makeBlock(void* dst, + const void* src, size_t srcSize, + compressFunc_t compress, void* lz4ctx, int level, + const LZ4F_CDict* cdict, + LZ4F_blockChecksum_t crcFlag) +{ + BYTE* const cSizePtr = (BYTE*)dst; + U32 cSize; + assert(compress != NULL); + cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize), + (int)(srcSize), (int)(srcSize-1), + level, cdict); + + if (cSize == 0 || cSize >= srcSize) { + cSize = (U32)srcSize; + LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG); + memcpy(cSizePtr+BHSize, src, srcSize); + } else { + LZ4F_writeLE32(cSizePtr, cSize); + } + if (crcFlag) { + U32 const crc32 = XXH32(cSizePtr+BHSize, cSize, 0); /* checksum of compressed data */ + LZ4F_writeLE32(cSizePtr+BHSize+cSize, crc32); + } + return BHSize + cSize + ((U32)crcFlag)*BFSize; +} + + +static int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + int const acceleration = (level < 0) ? -level + 1 : 1; + DEBUGLOG(5, "LZ4F_compressBlock (srcSize=%i)", srcSize); + LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent); + if (cdict) { + return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration); + } else { + return LZ4_compress_fast_extState_fastReset(ctx, src, dst, srcSize, dstCapacity, acceleration); + } +} + +static int LZ4F_compressBlock_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + int const acceleration = (level < 0) ? -level + 1 : 1; + (void)cdict; /* init once at beginning of frame */ + DEBUGLOG(5, "LZ4F_compressBlock_continue (srcSize=%i)", srcSize); + return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration); +} + +static int LZ4F_compressBlockHC(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent); + if (cdict) { + return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity); + } + return LZ4_compress_HC_extStateHC_fastReset(ctx, src, dst, srcSize, dstCapacity, level); +} + +static int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + (void)level; (void)cdict; /* init once at beginning of frame */ + return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity); +} + +static int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + (void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict; + return 0; +} + +static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_blockCompression_t compressMode) +{ + if (compressMode == LZ4B_UNCOMPRESSED) return LZ4F_doNotCompressBlock; + if (level < LZ4HC_CLEVEL_MIN) { + if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock; + return LZ4F_compressBlock_continue; + } + if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlockHC; + return LZ4F_compressBlockHC_continue; +} + +/* Save history (up to 64KB) into @tmpBuff */ +static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr) +{ + if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) + return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB); + return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB); +} + +typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus; + +static const LZ4F_compressOptions_t k_cOptionsNull = { 0, { 0, 0, 0 } }; + + + /*! LZ4F_compressUpdateImpl() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If the block compression does not match the compression of the previous block, the old data is flushed + * and operations continue with the new compression mode. + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on. + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr, + LZ4F_blockCompression_t blockCompression) + { + size_t const blockSize = cctxPtr->maxBlockSize; + const BYTE* srcPtr = (const BYTE*)srcBuffer; + const BYTE* const srcEnd = srcPtr + srcSize; + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + LZ4F_lastBlockStatus lastBlockCompressed = notDone; + compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression); + size_t bytesWritten; + DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize); + + RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); /* state must be initialized and waiting for next block */ + if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize)) + RETURN_ERROR(dstMaxSize_tooSmall); + + if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize) + RETURN_ERROR(dstMaxSize_tooSmall); + + /* flush currently written block, to continue with new block compression */ + if (cctxPtr->blockCompression != blockCompression) { + bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); + dstPtr += bytesWritten; + cctxPtr->blockCompression = blockCompression; + } + + if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull; + + /* complete tmp buffer */ + if (cctxPtr->tmpInSize > 0) { /* some data already within tmp buffer */ + size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize; + assert(blockSize > cctxPtr->tmpInSize); + if (sizeToCopy > srcSize) { + /* add src to tmpIn buffer */ + memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize); + srcPtr = srcEnd; + cctxPtr->tmpInSize += srcSize; + /* still needs some CRC */ + } else { + /* complete tmpIn block and then compress it */ + lastBlockCompressed = fromTmpBuffer; + memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy); + srcPtr += sizeToCopy; + + dstPtr += LZ4F_makeBlock(dstPtr, + cctxPtr->tmpIn, blockSize, + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize; + cctxPtr->tmpInSize = 0; + } } + + while ((size_t)(srcEnd - srcPtr) >= blockSize) { + /* compress full blocks */ + lastBlockCompressed = fromSrcBuffer; + dstPtr += LZ4F_makeBlock(dstPtr, + srcPtr, blockSize, + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + srcPtr += blockSize; + } + + if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) { + /* autoFlush : remaining input (< blockSize) is compressed */ + lastBlockCompressed = fromSrcBuffer; + dstPtr += LZ4F_makeBlock(dstPtr, + srcPtr, (size_t)(srcEnd - srcPtr), + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + srcPtr = srcEnd; + } + + /* preserve dictionary within @tmpBuff whenever necessary */ + if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) { + /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */ + assert(blockCompression == LZ4B_COMPRESSED); + if (compressOptionsPtr->stableSrc) { + cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */ + } else { + int const realDictSize = LZ4F_localSaveDict(cctxPtr); + assert(0 <= realDictSize && realDictSize <= 64 KB); + cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; + } + } + + /* keep tmpIn within limits */ + if (!(cctxPtr->prefs.autoFlush) /* no autoflush : there may be some data left within internal buffer */ + && (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) ) /* not enough room to store next block */ + { + /* only preserve 64KB within internal buffer. Ensures there is enough room for next block. + * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */ + int const realDictSize = LZ4F_localSaveDict(cctxPtr); + cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; + assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)); + } + + /* some input data left, necessarily < blockSize */ + if (srcPtr < srcEnd) { + /* fill tmp buffer */ + size_t const sizeToCopy = (size_t)(srcEnd - srcPtr); + memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy); + cctxPtr->tmpInSize = sizeToCopy; + } + + if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) + (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize); + + cctxPtr->totalInSize += srcSize; + return (size_t)(dstPtr - dstStart); +} + +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr) +{ + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_COMPRESSED); +} + +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * This is only supported when LZ4F_blockIndependent is used + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr) { + RETURN_ERROR_IF(cctxPtr->prefs.frameInfo.blockMode != LZ4F_blockIndependent, blockMode_invalid); + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_UNCOMPRESSED); +} + + +/*! LZ4F_flush() : + * When compressed data must be sent immediately, without waiting for a block to be filled, + * invoke LZ4_flush(), which will immediately compress any remaining data stored within LZ4F_cctx. + * The result of the function is the number of bytes written into dstBuffer. + * It can be zero, this means there was no data left within LZ4F_cctx. + * The function outputs an error code if it fails (can be tested using LZ4F_isError()) + * LZ4F_compressOptions_t* is optional. NULL is a valid argument. + */ +size_t LZ4F_flush(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* compressOptionsPtr) +{ + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + compressFunc_t compress; + + if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */ + RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); + RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall); + (void)compressOptionsPtr; /* not useful (yet) */ + + /* select compression function */ + compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompression); + + /* compress tmp buffer */ + dstPtr += LZ4F_makeBlock(dstPtr, + cctxPtr->tmpIn, cctxPtr->tmpInSize, + compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, + cctxPtr->cdict, + cctxPtr->prefs.frameInfo.blockChecksumFlag); + assert(((void)"flush overflows dstBuffer!", (size_t)(dstPtr - dstStart) <= dstCapacity)); + + if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) + cctxPtr->tmpIn += cctxPtr->tmpInSize; + cctxPtr->tmpInSize = 0; + + /* keep tmpIn within limits */ + if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) { /* necessarily LZ4F_blockLinked */ + int const realDictSize = LZ4F_localSaveDict(cctxPtr); + cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; + } + + return (size_t)(dstPtr - dstStart); +} + + +/*! LZ4F_compressEnd() : + * When you want to properly finish the compressed frame, just call LZ4F_compressEnd(). + * It will flush whatever data remained within compressionContext (like LZ4_flush()) + * but also properly finalize the frame, with an endMark and an (optional) checksum. + * LZ4F_compressOptions_t structure is optional : you can provide NULL as argument. + * @return: the number of bytes written into dstBuffer (necessarily >= 4 (endMark size)) + * or an error code if it fails (can be tested using LZ4F_isError()) + * The context can then be used again to compress a new frame, starting with LZ4F_compressBegin(). + */ +size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* compressOptionsPtr) +{ + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* dstPtr = dstStart; + + size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); + DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity); + FORWARD_IF_ERROR(flushSize); + dstPtr += flushSize; + + assert(flushSize <= dstCapacity); + dstCapacity -= flushSize; + + RETURN_ERROR_IF(dstCapacity < 4, dstMaxSize_tooSmall); + LZ4F_writeLE32(dstPtr, 0); + dstPtr += 4; /* endMark */ + + if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) { + U32 const xxh = XXH32_digest(&(cctxPtr->xxh)); + RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall); + DEBUGLOG(5,"Writing 32-bit content checksum"); + LZ4F_writeLE32(dstPtr, xxh); + dstPtr+=4; /* content Checksum */ + } + + cctxPtr->cStage = 0; /* state is now re-usable (with identical preferences) */ + cctxPtr->maxBufferSize = 0; /* reuse HC context */ + + if (cctxPtr->prefs.frameInfo.contentSize) { + if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize) + RETURN_ERROR(frameSize_wrong); + } + + return (size_t)(dstPtr - dstStart); +} + + +/*-*************************************************** +* Frame Decompression +*****************************************************/ + +typedef enum { + dstage_getFrameHeader=0, dstage_storeFrameHeader, + dstage_init, + dstage_getBlockHeader, dstage_storeBlockHeader, + dstage_copyDirect, dstage_getBlockChecksum, + dstage_getCBlock, dstage_storeCBlock, + dstage_flushOut, + dstage_getSuffix, dstage_storeSuffix, + dstage_getSFrameSize, dstage_storeSFrameSize, + dstage_skipSkippable +} dStage_t; + +struct LZ4F_dctx_s { + LZ4F_CustomMem cmem; + LZ4F_frameInfo_t frameInfo; + U32 version; + dStage_t dStage; + U64 frameRemainingSize; + size_t maxBlockSize; + size_t maxBufferSize; + BYTE* tmpIn; + size_t tmpInSize; + size_t tmpInTarget; + BYTE* tmpOutBuffer; + const BYTE* dict; + size_t dictSize; + BYTE* tmpOut; + size_t tmpOutSize; + size_t tmpOutStart; + XXH32_state_t xxh; + XXH32_state_t blockChecksum; + int skipChecksum; + BYTE header[LZ4F_HEADER_SIZE_MAX]; +}; /* typedef'd to LZ4F_dctx in lz4frame.h */ + + +LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version) +{ + LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem); + if (dctx == NULL) return NULL; + + dctx->cmem = customMem; + dctx->version = version; + return dctx; +} + +/*! LZ4F_createDecompressionContext() : + * Create a decompressionContext object, which will track all decompression operations. + * Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object. + * Object can later be released using LZ4F_freeDecompressionContext(). + * @return : if != 0, there was an error during context creation. + */ +LZ4F_errorCode_t +LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber) +{ + assert(LZ4F_decompressionContextPtr != NULL); /* violation of narrow contract */ + RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null); /* in case it nonetheless happen in production */ + + *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber); + if (*LZ4F_decompressionContextPtr == NULL) { /* failed allocation */ + RETURN_ERROR(allocation_failed); + } + return LZ4F_OK_NoError; +} + +LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx) +{ + LZ4F_errorCode_t result = LZ4F_OK_NoError; + if (dctx != NULL) { /* can accept NULL input, like free() */ + result = (LZ4F_errorCode_t)dctx->dStage; + LZ4F_free(dctx->tmpIn, dctx->cmem); + LZ4F_free(dctx->tmpOutBuffer, dctx->cmem); + LZ4F_free(dctx, dctx->cmem); + } + return result; +} + + +/*==--- Streaming Decompression operations ---==*/ + +void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx) +{ + dctx->dStage = dstage_getFrameHeader; + dctx->dict = NULL; + dctx->dictSize = 0; + dctx->skipChecksum = 0; +} + + +/*! LZ4F_decodeHeader() : + * input : `src` points at the **beginning of the frame** + * output : set internal values of dctx, such as + * dctx->frameInfo and dctx->dStage. + * Also allocates internal buffers. + * @return : nb Bytes read from src (necessarily <= srcSize) + * or an error code (testable with LZ4F_isError()) + */ +static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize) +{ + unsigned blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictIDFlag, blockSizeID; + size_t frameHeaderSize; + const BYTE* srcPtr = (const BYTE*)src; + + DEBUGLOG(5, "LZ4F_decodeHeader"); + /* need to decode header to get frameInfo */ + RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete); /* minimal frame header size */ + MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo)); + + /* special case : skippable frames */ + if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) { + dctx->frameInfo.frameType = LZ4F_skippableFrame; + if (src == (void*)(dctx->header)) { + dctx->tmpInSize = srcSize; + dctx->tmpInTarget = 8; + dctx->dStage = dstage_storeSFrameSize; + return srcSize; + } else { + dctx->dStage = dstage_getSFrameSize; + return 4; + } } + + /* control magic number */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) { + DEBUGLOG(4, "frame header error : unknown magic number"); + RETURN_ERROR(frameType_unknown); + } +#endif + dctx->frameInfo.frameType = LZ4F_frame; + + /* Flags */ + { U32 const FLG = srcPtr[4]; + U32 const version = (FLG>>6) & _2BITS; + blockChecksumFlag = (FLG>>4) & _1BIT; + blockMode = (FLG>>5) & _1BIT; + contentSizeFlag = (FLG>>3) & _1BIT; + contentChecksumFlag = (FLG>>2) & _1BIT; + dictIDFlag = FLG & _1BIT; + /* validate */ + if (((FLG>>1)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */ + if (version != 1) RETURN_ERROR(headerVersion_wrong); /* Version Number, only supported value */ + } + + /* Frame Header Size */ + frameHeaderSize = minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0); + + if (srcSize < frameHeaderSize) { + /* not enough input to fully decode frame header */ + if (srcPtr != dctx->header) + memcpy(dctx->header, srcPtr, srcSize); + dctx->tmpInSize = srcSize; + dctx->tmpInTarget = frameHeaderSize; + dctx->dStage = dstage_storeFrameHeader; + return srcSize; + } + + { U32 const BD = srcPtr[5]; + blockSizeID = (BD>>4) & _3BITS; + /* validate */ + if (((BD>>7)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */ + if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid); /* 4-7 only supported values for the time being */ + if (((BD>>0)&_4BITS) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bits */ + } + + /* check header */ + assert(frameHeaderSize > 5); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + { BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5); + RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid); + } +#endif + + /* save */ + dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode; + dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag; + dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag; + dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID; + dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID); + if (contentSizeFlag) + dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6); + if (dictIDFlag) + dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5); + + dctx->dStage = dstage_init; + + return frameHeaderSize; +} + + +/*! LZ4F_headerSize() : + * @return : size of frame header + * or an error code, which can be tested using LZ4F_isError() + */ +size_t LZ4F_headerSize(const void* src, size_t srcSize) +{ + RETURN_ERROR_IF(src == NULL, srcPtr_wrong); + + /* minimal srcSize to determine header size */ + if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH) + RETURN_ERROR(frameHeader_incomplete); + + /* special case : skippable frames */ + if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) + return 8; + + /* control magic number */ +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER) + RETURN_ERROR(frameType_unknown); +#endif + + /* Frame Header Size */ + { BYTE const FLG = ((const BYTE*)src)[4]; + U32 const contentSizeFlag = (FLG>>3) & _1BIT; + U32 const dictIDFlag = FLG & _1BIT; + return minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0); + } +} + +/*! LZ4F_getFrameInfo() : + * This function extracts frame parameters (max blockSize, frame checksum, etc.). + * Usage is optional. Objective is to provide relevant information for allocation purposes. + * This function works in 2 situations : + * - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process. + * Amount of input data provided must be large enough to successfully decode the frame header. + * A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum. + * - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx. + * The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value). + * Decompression must resume from (srcBuffer + *srcSizePtr). + * @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call, + * or an error code which can be tested using LZ4F_isError() + * note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped. + * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure. + */ +LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx, + LZ4F_frameInfo_t* frameInfoPtr, + const void* srcBuffer, size_t* srcSizePtr) +{ + LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader); + if (dctx->dStage > dstage_storeFrameHeader) { + /* frameInfo already decoded */ + size_t o=0, i=0; + *srcSizePtr = 0; + *frameInfoPtr = dctx->frameInfo; + /* returns : recommended nb of bytes for LZ4F_decompress() */ + return LZ4F_decompress(dctx, NULL, &o, NULL, &i, NULL); + } else { + if (dctx->dStage == dstage_storeFrameHeader) { + /* frame decoding already started, in the middle of header => automatic fail */ + *srcSizePtr = 0; + RETURN_ERROR(frameDecoding_alreadyStarted); + } else { + size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr); + if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; } + if (*srcSizePtr < hSize) { + *srcSizePtr=0; + RETURN_ERROR(frameHeader_incomplete); + } + + { size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize); + if (LZ4F_isError(decodeResult)) { + *srcSizePtr = 0; + } else { + *srcSizePtr = decodeResult; + decodeResult = BHSize; /* block header size */ + } + *frameInfoPtr = dctx->frameInfo; + return decodeResult; + } } } +} + + +/* LZ4F_updateDict() : + * only used for LZ4F_blockLinked mode + * Condition : @dstPtr != NULL + */ +static void LZ4F_updateDict(LZ4F_dctx* dctx, + const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart, + unsigned withinTmp) +{ + assert(dstPtr != NULL); + if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr; /* will lead to prefix mode */ + assert(dctx->dict != NULL); + + if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */ + dctx->dictSize += dstSize; + return; + } + + assert(dstPtr >= dstBufferStart); + if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB) { /* history in dstBuffer becomes large enough to become dictionary */ + dctx->dict = (const BYTE*)dstBufferStart; + dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize; + return; + } + + assert(dstSize < 64 KB); /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */ + + /* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */ + assert(dctx->tmpOutBuffer != NULL); + + if (withinTmp && (dctx->dict == dctx->tmpOutBuffer)) { /* continue history within tmpOutBuffer */ + /* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */ + assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart); + dctx->dictSize += dstSize; + return; + } + + if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */ + size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer); + size_t copySize = 64 KB - dctx->tmpOutSize; + const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart; + if (dctx->tmpOutSize > 64 KB) copySize = 0; + if (copySize > preserveSize) copySize = preserveSize; + + memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize); + + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = preserveSize + dctx->tmpOutStart + dstSize; + return; + } + + if (dctx->dict == dctx->tmpOutBuffer) { /* copy dst into tmp to complete dict */ + if (dctx->dictSize + dstSize > dctx->maxBufferSize) { /* tmp buffer not large enough */ + size_t const preserveSize = 64 KB - dstSize; + memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize); + dctx->dictSize = preserveSize; + } + memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize); + dctx->dictSize += dstSize; + return; + } + + /* join dict & dest into tmp */ + { size_t preserveSize = 64 KB - dstSize; + if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize; + memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize); + memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize); + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = preserveSize + dstSize; + } +} + + +/*! LZ4F_decompress() : + * Call this function repetitively to regenerate compressed data in srcBuffer. + * The function will attempt to decode up to *srcSizePtr bytes from srcBuffer + * into dstBuffer of capacity *dstSizePtr. + * + * The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value). + * + * The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value). + * If number of bytes read is < number of bytes provided, then decompression operation is not complete. + * Remaining data will have to be presented again in a subsequent invocation. + * + * The function result is an hint of the better srcSize to use for next call to LZ4F_decompress. + * Schematically, it's the size of the current (or remaining) compressed block + header of next block. + * Respecting the hint provides a small boost to performance, since it allows less buffer shuffling. + * Note that this is just a hint, and it's always possible to any srcSize value. + * When a frame is fully decoded, @return will be 0. + * If decompression failed, @return is an error code which can be tested using LZ4F_isError(). + */ +size_t LZ4F_decompress(LZ4F_dctx* dctx, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const LZ4F_decompressOptions_t* decompressOptionsPtr) +{ + LZ4F_decompressOptions_t optionsNull; + const BYTE* const srcStart = (const BYTE*)srcBuffer; + const BYTE* const srcEnd = srcStart + *srcSizePtr; + const BYTE* srcPtr = srcStart; + BYTE* const dstStart = (BYTE*)dstBuffer; + BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL; + BYTE* dstPtr = dstStart; + const BYTE* selectedIn = NULL; + unsigned doAnotherStage = 1; + size_t nextSrcSizeHint = 1; + + + DEBUGLOG(5, "LZ4F_decompress : %p,%u => %p,%u", + srcBuffer, (unsigned)*srcSizePtr, dstBuffer, (unsigned)*dstSizePtr); + if (dstBuffer == NULL) assert(*dstSizePtr == 0); + MEM_INIT(&optionsNull, 0, sizeof(optionsNull)); + if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull; + *srcSizePtr = 0; + *dstSizePtr = 0; + assert(dctx != NULL); + dctx->skipChecksum |= (decompressOptionsPtr->skipChecksums != 0); /* once set, disable for the remainder of the frame */ + + /* behaves as a state machine */ + + while (doAnotherStage) { + + switch(dctx->dStage) + { + + case dstage_getFrameHeader: + DEBUGLOG(6, "dstage_getFrameHeader"); + if ((size_t)(srcEnd-srcPtr) >= maxFHSize) { /* enough to decode - shortcut */ + size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr)); /* will update dStage appropriately */ + FORWARD_IF_ERROR(hSize); + srcPtr += hSize; + break; + } + dctx->tmpInSize = 0; + if (srcEnd-srcPtr == 0) return minFHSize; /* 0-size input */ + dctx->tmpInTarget = minFHSize; /* minimum size to decode header */ + dctx->dStage = dstage_storeFrameHeader; + /* fall-through */ + + case dstage_storeFrameHeader: + DEBUGLOG(6, "dstage_storeFrameHeader"); + { size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr)); + memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy); + dctx->tmpInSize += sizeToCopy; + srcPtr += sizeToCopy; + } + if (dctx->tmpInSize < dctx->tmpInTarget) { + nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize; /* rest of header + nextBlockHeader */ + doAnotherStage = 0; /* not enough src data, ask for some more */ + break; + } + FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */ + break; + + case dstage_init: + DEBUGLOG(6, "dstage_init"); + if (dctx->frameInfo.contentChecksumFlag) (void)XXH32_reset(&(dctx->xxh), 0); + /* internal buffers allocation */ + { size_t const bufferNeeded = dctx->maxBlockSize + + ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB : 0); + if (bufferNeeded > dctx->maxBufferSize) { /* tmp buffers too small */ + dctx->maxBufferSize = 0; /* ensure allocation will be re-attempted on next entry*/ + LZ4F_free(dctx->tmpIn, dctx->cmem); + dctx->tmpIn = (BYTE*)LZ4F_malloc(dctx->maxBlockSize + BFSize /* block checksum */, dctx->cmem); + RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed); + LZ4F_free(dctx->tmpOutBuffer, dctx->cmem); + dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(bufferNeeded, dctx->cmem); + RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed); + dctx->maxBufferSize = bufferNeeded; + } } + dctx->tmpInSize = 0; + dctx->tmpInTarget = 0; + dctx->tmpOut = dctx->tmpOutBuffer; + dctx->tmpOutStart = 0; + dctx->tmpOutSize = 0; + + dctx->dStage = dstage_getBlockHeader; + /* fall-through */ + + case dstage_getBlockHeader: + if ((size_t)(srcEnd - srcPtr) >= BHSize) { + selectedIn = srcPtr; + srcPtr += BHSize; + } else { + /* not enough input to read cBlockSize field */ + dctx->tmpInSize = 0; + dctx->dStage = dstage_storeBlockHeader; + } + + if (dctx->dStage == dstage_storeBlockHeader) /* can be skipped */ + case dstage_storeBlockHeader: + { size_t const remainingInput = (size_t)(srcEnd - srcPtr); + size_t const wantedData = BHSize - dctx->tmpInSize; + size_t const sizeToCopy = MIN(wantedData, remainingInput); + memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy); + srcPtr += sizeToCopy; + dctx->tmpInSize += sizeToCopy; + + if (dctx->tmpInSize < BHSize) { /* not enough input for cBlockSize */ + nextSrcSizeHint = BHSize - dctx->tmpInSize; + doAnotherStage = 0; + break; + } + selectedIn = dctx->tmpIn; + } /* if (dctx->dStage == dstage_storeBlockHeader) */ + + /* decode block header */ + { U32 const blockHeader = LZ4F_readLE32(selectedIn); + size_t const nextCBlockSize = blockHeader & 0x7FFFFFFFU; + size_t const crcSize = dctx->frameInfo.blockChecksumFlag * BFSize; + if (blockHeader==0) { /* frameEnd signal, no more block */ + DEBUGLOG(5, "end of frame"); + dctx->dStage = dstage_getSuffix; + break; + } + if (nextCBlockSize > dctx->maxBlockSize) { + RETURN_ERROR(maxBlockSize_invalid); + } + if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) { + /* next block is uncompressed */ + dctx->tmpInTarget = nextCBlockSize; + DEBUGLOG(5, "next block is uncompressed (size %u)", (U32)nextCBlockSize); + if (dctx->frameInfo.blockChecksumFlag) { + (void)XXH32_reset(&dctx->blockChecksum, 0); + } + dctx->dStage = dstage_copyDirect; + break; + } + /* next block is a compressed block */ + dctx->tmpInTarget = nextCBlockSize + crcSize; + dctx->dStage = dstage_getCBlock; + if (dstPtr==dstEnd || srcPtr==srcEnd) { + nextSrcSizeHint = BHSize + nextCBlockSize + crcSize; + doAnotherStage = 0; + } + break; + } + + case dstage_copyDirect: /* uncompressed block */ + DEBUGLOG(6, "dstage_copyDirect"); + { size_t sizeToCopy; + if (dstPtr == NULL) { + sizeToCopy = 0; + } else { + size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr)); + sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize); + memcpy(dstPtr, srcPtr, sizeToCopy); + if (!dctx->skipChecksum) { + if (dctx->frameInfo.blockChecksumFlag) { + (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy); + } + if (dctx->frameInfo.contentChecksumFlag) + (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy); + } + if (dctx->frameInfo.contentSize) + dctx->frameRemainingSize -= sizeToCopy; + + /* history management (linked blocks only)*/ + if (dctx->frameInfo.blockMode == LZ4F_blockLinked) { + LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 0); + } } + + srcPtr += sizeToCopy; + dstPtr += sizeToCopy; + if (sizeToCopy == dctx->tmpInTarget) { /* all done */ + if (dctx->frameInfo.blockChecksumFlag) { + dctx->tmpInSize = 0; + dctx->dStage = dstage_getBlockChecksum; + } else + dctx->dStage = dstage_getBlockHeader; /* new block */ + break; + } + dctx->tmpInTarget -= sizeToCopy; /* need to copy more */ + } + nextSrcSizeHint = dctx->tmpInTarget + + +(dctx->frameInfo.blockChecksumFlag ? BFSize : 0) + + BHSize /* next header size */; + doAnotherStage = 0; + break; + + /* check block checksum for recently transferred uncompressed block */ + case dstage_getBlockChecksum: + DEBUGLOG(6, "dstage_getBlockChecksum"); + { const void* crcSrc; + if ((srcEnd-srcPtr >= 4) && (dctx->tmpInSize==0)) { + crcSrc = srcPtr; + srcPtr += 4; + } else { + size_t const stillToCopy = 4 - dctx->tmpInSize; + size_t const sizeToCopy = MIN(stillToCopy, (size_t)(srcEnd-srcPtr)); + memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy); + dctx->tmpInSize += sizeToCopy; + srcPtr += sizeToCopy; + if (dctx->tmpInSize < 4) { /* all input consumed */ + doAnotherStage = 0; + break; + } + crcSrc = dctx->header; + } + if (!dctx->skipChecksum) { + U32 const readCRC = LZ4F_readLE32(crcSrc); + U32 const calcCRC = XXH32_digest(&dctx->blockChecksum); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + DEBUGLOG(6, "compare block checksum"); + if (readCRC != calcCRC) { + DEBUGLOG(4, "incorrect block checksum: %08X != %08X", + readCRC, calcCRC); + RETURN_ERROR(blockChecksum_invalid); + } +#else + (void)readCRC; + (void)calcCRC; +#endif + } } + dctx->dStage = dstage_getBlockHeader; /* new block */ + break; + + case dstage_getCBlock: + DEBUGLOG(6, "dstage_getCBlock"); + if ((size_t)(srcEnd-srcPtr) < dctx->tmpInTarget) { + dctx->tmpInSize = 0; + dctx->dStage = dstage_storeCBlock; + break; + } + /* input large enough to read full block directly */ + selectedIn = srcPtr; + srcPtr += dctx->tmpInTarget; + + if (0) /* always jump over next block */ + case dstage_storeCBlock: + { size_t const wantedData = dctx->tmpInTarget - dctx->tmpInSize; + size_t const inputLeft = (size_t)(srcEnd-srcPtr); + size_t const sizeToCopy = MIN(wantedData, inputLeft); + memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy); + dctx->tmpInSize += sizeToCopy; + srcPtr += sizeToCopy; + if (dctx->tmpInSize < dctx->tmpInTarget) { /* need more input */ + nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + + (dctx->frameInfo.blockChecksumFlag ? BFSize : 0) + + BHSize /* next header size */; + doAnotherStage = 0; + break; + } + selectedIn = dctx->tmpIn; + } + + /* At this stage, input is large enough to decode a block */ + + /* First, decode and control block checksum if it exists */ + if (dctx->frameInfo.blockChecksumFlag) { + assert(dctx->tmpInTarget >= 4); + dctx->tmpInTarget -= 4; + assert(selectedIn != NULL); /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */ + { U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget); + U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid); +#else + (void)readBlockCrc; + (void)calcBlockCrc; +#endif + } } + + /* decode directly into destination buffer if there is enough room */ + if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize) + /* unless the dictionary is stored in tmpOut: + * in which case it's faster to decode within tmpOut + * to benefit from prefix speedup */ + && !(dctx->dict!= NULL && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) ) + { + const char* dict = (const char*)dctx->dict; + size_t dictSize = dctx->dictSize; + int decodedSize; + assert(dstPtr != NULL); + if (dict && dictSize > 1 GB) { + /* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */ + dict += dictSize - 64 KB; + dictSize = 64 KB; + } + decodedSize = LZ4_decompress_safe_usingDict( + (const char*)selectedIn, (char*)dstPtr, + (int)dctx->tmpInTarget, (int)dctx->maxBlockSize, + dict, (int)dictSize); + RETURN_ERROR_IF(decodedSize < 0, decompressionFailed); + if ((dctx->frameInfo.contentChecksumFlag) && (!dctx->skipChecksum)) + XXH32_update(&(dctx->xxh), dstPtr, (size_t)decodedSize); + if (dctx->frameInfo.contentSize) + dctx->frameRemainingSize -= (size_t)decodedSize; + + /* dictionary management */ + if (dctx->frameInfo.blockMode==LZ4F_blockLinked) { + LZ4F_updateDict(dctx, dstPtr, (size_t)decodedSize, dstStart, 0); + } + + dstPtr += decodedSize; + dctx->dStage = dstage_getBlockHeader; /* end of block, let's get another one */ + break; + } + + /* not enough place into dst : decode into tmpOut */ + + /* manage dictionary */ + if (dctx->frameInfo.blockMode == LZ4F_blockLinked) { + if (dctx->dict == dctx->tmpOutBuffer) { + /* truncate dictionary to 64 KB if too big */ + if (dctx->dictSize > 128 KB) { + memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB); + dctx->dictSize = 64 KB; + } + dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize; + } else { /* dict not within tmpOut */ + size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB); + dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace; + } } + + /* Decode block into tmpOut */ + { const char* dict = (const char*)dctx->dict; + size_t dictSize = dctx->dictSize; + int decodedSize; + if (dict && dictSize > 1 GB) { + /* the dictSize param is an int, avoid truncation / sign issues */ + dict += dictSize - 64 KB; + dictSize = 64 KB; + } + decodedSize = LZ4_decompress_safe_usingDict( + (const char*)selectedIn, (char*)dctx->tmpOut, + (int)dctx->tmpInTarget, (int)dctx->maxBlockSize, + dict, (int)dictSize); + RETURN_ERROR_IF(decodedSize < 0, decompressionFailed); + if (dctx->frameInfo.contentChecksumFlag && !dctx->skipChecksum) + XXH32_update(&(dctx->xxh), dctx->tmpOut, (size_t)decodedSize); + if (dctx->frameInfo.contentSize) + dctx->frameRemainingSize -= (size_t)decodedSize; + dctx->tmpOutSize = (size_t)decodedSize; + dctx->tmpOutStart = 0; + dctx->dStage = dstage_flushOut; + } + /* fall-through */ + + case dstage_flushOut: /* flush decoded data from tmpOut to dstBuffer */ + DEBUGLOG(6, "dstage_flushOut"); + if (dstPtr != NULL) { + size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr)); + memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy); + + /* dictionary management */ + if (dctx->frameInfo.blockMode == LZ4F_blockLinked) + LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1 /*withinTmp*/); + + dctx->tmpOutStart += sizeToCopy; + dstPtr += sizeToCopy; + } + if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */ + dctx->dStage = dstage_getBlockHeader; /* get next block */ + break; + } + /* could not flush everything : stop there, just request a block header */ + doAnotherStage = 0; + nextSrcSizeHint = BHSize; + break; + + case dstage_getSuffix: + RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong); /* incorrect frame size decoded */ + if (!dctx->frameInfo.contentChecksumFlag) { /* no checksum, frame is completed */ + nextSrcSizeHint = 0; + LZ4F_resetDecompressionContext(dctx); + doAnotherStage = 0; + break; + } + if ((srcEnd - srcPtr) < 4) { /* not enough size for entire CRC */ + dctx->tmpInSize = 0; + dctx->dStage = dstage_storeSuffix; + } else { + selectedIn = srcPtr; + srcPtr += 4; + } + + if (dctx->dStage == dstage_storeSuffix) /* can be skipped */ + case dstage_storeSuffix: + { size_t const remainingInput = (size_t)(srcEnd - srcPtr); + size_t const wantedData = 4 - dctx->tmpInSize; + size_t const sizeToCopy = MIN(wantedData, remainingInput); + memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy); + srcPtr += sizeToCopy; + dctx->tmpInSize += sizeToCopy; + if (dctx->tmpInSize < 4) { /* not enough input to read complete suffix */ + nextSrcSizeHint = 4 - dctx->tmpInSize; + doAnotherStage=0; + break; + } + selectedIn = dctx->tmpIn; + } /* if (dctx->dStage == dstage_storeSuffix) */ + + /* case dstage_checkSuffix: */ /* no direct entry, avoid initialization risks */ + if (!dctx->skipChecksum) { + U32 const readCRC = LZ4F_readLE32(selectedIn); + U32 const resultCRC = XXH32_digest(&(dctx->xxh)); +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid); +#else + (void)readCRC; + (void)resultCRC; +#endif + } + nextSrcSizeHint = 0; + LZ4F_resetDecompressionContext(dctx); + doAnotherStage = 0; + break; + + case dstage_getSFrameSize: + if ((srcEnd - srcPtr) >= 4) { + selectedIn = srcPtr; + srcPtr += 4; + } else { + /* not enough input to read cBlockSize field */ + dctx->tmpInSize = 4; + dctx->tmpInTarget = 8; + dctx->dStage = dstage_storeSFrameSize; + } + + if (dctx->dStage == dstage_storeSFrameSize) + case dstage_storeSFrameSize: + { size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, + (size_t)(srcEnd - srcPtr) ); + memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy); + srcPtr += sizeToCopy; + dctx->tmpInSize += sizeToCopy; + if (dctx->tmpInSize < dctx->tmpInTarget) { + /* not enough input to get full sBlockSize; wait for more */ + nextSrcSizeHint = dctx->tmpInTarget - dctx->tmpInSize; + doAnotherStage = 0; + break; + } + selectedIn = dctx->header + 4; + } /* if (dctx->dStage == dstage_storeSFrameSize) */ + + /* case dstage_decodeSFrameSize: */ /* no direct entry */ + { size_t const SFrameSize = LZ4F_readLE32(selectedIn); + dctx->frameInfo.contentSize = SFrameSize; + dctx->tmpInTarget = SFrameSize; + dctx->dStage = dstage_skipSkippable; + break; + } + + case dstage_skipSkippable: + { size_t const skipSize = MIN(dctx->tmpInTarget, (size_t)(srcEnd-srcPtr)); + srcPtr += skipSize; + dctx->tmpInTarget -= skipSize; + doAnotherStage = 0; + nextSrcSizeHint = dctx->tmpInTarget; + if (nextSrcSizeHint) break; /* still more to skip */ + /* frame fully skipped : prepare context for a new frame */ + LZ4F_resetDecompressionContext(dctx); + break; + } + } /* switch (dctx->dStage) */ + } /* while (doAnotherStage) */ + + /* preserve history within tmpOut whenever necessary */ + LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2); + if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked) /* next block will use up to 64KB from previous ones */ + && (dctx->dict != dctx->tmpOutBuffer) /* dictionary is not already within tmp */ + && (dctx->dict != NULL) /* dictionary exists */ + && (!decompressOptionsPtr->stableDst) /* cannot rely on dst data to remain there for next call */ + && ((unsigned)(dctx->dStage)-2 < (unsigned)(dstage_getSuffix)-2) ) /* valid stages : [init ... getSuffix[ */ + { + if (dctx->dStage == dstage_flushOut) { + size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer); + size_t copySize = 64 KB - dctx->tmpOutSize; + const BYTE* oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart; + if (dctx->tmpOutSize > 64 KB) copySize = 0; + if (copySize > preserveSize) copySize = preserveSize; + assert(dctx->tmpOutBuffer != NULL); + + memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize); + + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = preserveSize + dctx->tmpOutStart; + } else { + const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize; + size_t const newDictSize = MIN(dctx->dictSize, 64 KB); + + memcpy(dctx->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize); + + dctx->dict = dctx->tmpOutBuffer; + dctx->dictSize = newDictSize; + dctx->tmpOut = dctx->tmpOutBuffer + newDictSize; + } + } + + *srcSizePtr = (size_t)(srcPtr - srcStart); + *dstSizePtr = (size_t)(dstPtr - dstStart); + return nextSrcSizeHint; +} + +/*! LZ4F_decompress_usingDict() : + * Same as LZ4F_decompress(), using a predefined dictionary. + * Dictionary is used "in place", without any preprocessing. + * It must remain accessible throughout the entire frame decoding. + */ +size_t LZ4F_decompress_usingDict(LZ4F_dctx* dctx, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const void* dict, size_t dictSize, + const LZ4F_decompressOptions_t* decompressOptionsPtr) +{ + if (dctx->dStage <= dstage_init) { + dctx->dict = (const BYTE*)dict; + dctx->dictSize = dictSize; + } + return LZ4F_decompress(dctx, dstBuffer, dstSizePtr, + srcBuffer, srcSizePtr, + decompressOptionsPtr); +} diff --git a/r5dev/thirdparty/lz4/lz4frame.h b/r5dev/thirdparty/lz4/lz4frame.h new file mode 100644 index 00000000..1bdf6c4f --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4frame.h @@ -0,0 +1,692 @@ +/* + LZ4F - LZ4-Frame library + Header File + Copyright (C) 2011-2020, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +/* LZ4F is a stand-alone API able to create and decode LZ4 frames + * conformant with specification v1.6.1 in doc/lz4_Frame_format.md . + * Generated frames are compatible with `lz4` CLI. + * + * LZ4F also offers streaming capabilities. + * + * lz4.h is not required when using lz4frame.h, + * except to extract common constants such as LZ4_VERSION_NUMBER. + * */ + +#ifndef LZ4F_H_09782039843 +#define LZ4F_H_09782039843 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* --- Dependency --- */ +#include /* size_t */ + + +/** + * Introduction + * + * lz4frame.h implements LZ4 frame specification: see doc/lz4_Frame_format.md . + * LZ4 Frames are compatible with `lz4` CLI, + * and designed to be interoperable with any system. +**/ + +/*-*************************************************************** + * Compiler specifics + *****************************************************************/ +/* LZ4_DLL_EXPORT : + * Enable exporting of functions when building a Windows DLL + * LZ4FLIB_VISIBILITY : + * Control library symbols visibility. + */ +#ifndef LZ4FLIB_VISIBILITY +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4FLIB_VISIBILITY __attribute__ ((visibility ("default"))) +# else +# define LZ4FLIB_VISIBILITY +# endif +#endif +#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1) +# define LZ4FLIB_API __declspec(dllexport) LZ4FLIB_VISIBILITY +#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1) +# define LZ4FLIB_API __declspec(dllimport) LZ4FLIB_VISIBILITY +#else +# define LZ4FLIB_API LZ4FLIB_VISIBILITY +#endif + +#ifdef LZ4F_DISABLE_DEPRECATE_WARNINGS +# define LZ4F_DEPRECATE(x) x +#else +# if defined(_MSC_VER) +# define LZ4F_DEPRECATE(x) x /* __declspec(deprecated) x - only works with C++ */ +# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6)) +# define LZ4F_DEPRECATE(x) x __attribute__((deprecated)) +# else +# define LZ4F_DEPRECATE(x) x /* no deprecation warning for this compiler */ +# endif +#endif + + +/*-************************************ + * Error management + **************************************/ +typedef size_t LZ4F_errorCode_t; + +LZ4FLIB_API unsigned LZ4F_isError(LZ4F_errorCode_t code); /**< tells when a function result is an error code */ +LZ4FLIB_API const char* LZ4F_getErrorName(LZ4F_errorCode_t code); /**< return error code string; for debugging */ + + +/*-************************************ + * Frame compression types + ************************************* */ +/* #define LZ4F_ENABLE_OBSOLETE_ENUMS // uncomment to enable obsolete enums */ +#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS +# define LZ4F_OBSOLETE_ENUM(x) , LZ4F_DEPRECATE(x) = LZ4F_##x +#else +# define LZ4F_OBSOLETE_ENUM(x) +#endif + +/* The larger the block size, the (slightly) better the compression ratio, + * though there are diminishing returns. + * Larger blocks also increase memory usage on both compression and decompression sides. + */ +typedef enum { + LZ4F_default=0, + LZ4F_max64KB=4, + LZ4F_max256KB=5, + LZ4F_max1MB=6, + LZ4F_max4MB=7 + LZ4F_OBSOLETE_ENUM(max64KB) + LZ4F_OBSOLETE_ENUM(max256KB) + LZ4F_OBSOLETE_ENUM(max1MB) + LZ4F_OBSOLETE_ENUM(max4MB) +} LZ4F_blockSizeID_t; + +/* Linked blocks sharply reduce inefficiencies when using small blocks, + * they compress better. + * However, some LZ4 decoders are only compatible with independent blocks */ +typedef enum { + LZ4F_blockLinked=0, + LZ4F_blockIndependent + LZ4F_OBSOLETE_ENUM(blockLinked) + LZ4F_OBSOLETE_ENUM(blockIndependent) +} LZ4F_blockMode_t; + +typedef enum { + LZ4F_noContentChecksum=0, + LZ4F_contentChecksumEnabled + LZ4F_OBSOLETE_ENUM(noContentChecksum) + LZ4F_OBSOLETE_ENUM(contentChecksumEnabled) +} LZ4F_contentChecksum_t; + +typedef enum { + LZ4F_noBlockChecksum=0, + LZ4F_blockChecksumEnabled +} LZ4F_blockChecksum_t; + +typedef enum { + LZ4F_frame=0, + LZ4F_skippableFrame + LZ4F_OBSOLETE_ENUM(skippableFrame) +} LZ4F_frameType_t; + +#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS +typedef LZ4F_blockSizeID_t blockSizeID_t; +typedef LZ4F_blockMode_t blockMode_t; +typedef LZ4F_frameType_t frameType_t; +typedef LZ4F_contentChecksum_t contentChecksum_t; +#endif + +/*! LZ4F_frameInfo_t : + * makes it possible to set or read frame parameters. + * Structure must be first init to 0, using memset() or LZ4F_INIT_FRAMEINFO, + * setting all parameters to default. + * It's then possible to update selectively some parameters */ +typedef struct { + LZ4F_blockSizeID_t blockSizeID; /* max64KB, max256KB, max1MB, max4MB; 0 == default */ + LZ4F_blockMode_t blockMode; /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */ + LZ4F_contentChecksum_t contentChecksumFlag; /* 1: frame terminated with 32-bit checksum of decompressed data; 0: disabled (default) */ + LZ4F_frameType_t frameType; /* read-only field : LZ4F_frame or LZ4F_skippableFrame */ + unsigned long long contentSize; /* Size of uncompressed content ; 0 == unknown */ + unsigned dictID; /* Dictionary ID, sent by compressor to help decoder select correct dictionary; 0 == no dictID provided */ + LZ4F_blockChecksum_t blockChecksumFlag; /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */ +} LZ4F_frameInfo_t; + +#define LZ4F_INIT_FRAMEINFO { LZ4F_default, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0ULL, 0U, LZ4F_noBlockChecksum } /* v1.8.3+ */ + +/*! LZ4F_preferences_t : + * makes it possible to supply advanced compression instructions to streaming interface. + * Structure must be first init to 0, using memset() or LZ4F_INIT_PREFERENCES, + * setting all parameters to default. + * All reserved fields must be set to zero. */ +typedef struct { + LZ4F_frameInfo_t frameInfo; + int compressionLevel; /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger "fast acceleration" */ + unsigned autoFlush; /* 1: always flush; reduces usage of internal buffers */ + unsigned favorDecSpeed; /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */ /* v1.8.2+ */ + unsigned reserved[3]; /* must be zero for forward compatibility */ +} LZ4F_preferences_t; + +#define LZ4F_INIT_PREFERENCES { LZ4F_INIT_FRAMEINFO, 0, 0u, 0u, { 0u, 0u, 0u } } /* v1.8.3+ */ + + +/*-********************************* +* Simple compression function +***********************************/ + +LZ4FLIB_API int LZ4F_compressionLevel_max(void); /* v1.8.0+ */ + +/*! LZ4F_compressFrameBound() : + * Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences. + * `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences. + * Note : this result is only usable with LZ4F_compressFrame(). + * It may also be relevant to LZ4F_compressUpdate() _only if_ no flush() operation is ever performed. + */ +LZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr); + +/*! LZ4F_compressFrame() : + * Compress an entire srcBuffer into a valid LZ4 frame. + * dstCapacity MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default. + * @return : number of bytes written into dstBuffer. + * or an error code if it fails (can be tested using LZ4F_isError()) + */ +LZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_preferences_t* preferencesPtr); + + +/*-*********************************** +* Advanced compression functions +*************************************/ +typedef struct LZ4F_cctx_s LZ4F_cctx; /* incomplete type */ +typedef LZ4F_cctx* LZ4F_compressionContext_t; /* for compatibility with older APIs, prefer using LZ4F_cctx */ + +typedef struct { + unsigned stableSrc; /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */ + unsigned reserved[3]; +} LZ4F_compressOptions_t; + +/*--- Resource Management ---*/ + +#define LZ4F_VERSION 100 /* This number can be used to check for an incompatible API breaking change */ +LZ4FLIB_API unsigned LZ4F_getVersion(void); + +/*! LZ4F_createCompressionContext() : + * The first thing to do is to create a compressionContext object, + * which will keep track of operation state during streaming compression. + * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version, + * and a pointer to LZ4F_cctx*, to write the resulting pointer into. + * @version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL. + * The function provides a pointer to a fully allocated LZ4F_cctx object. + * @cctxPtr MUST be != NULL. + * If @return != zero, context creation failed. + * A created compression context can be employed multiple times for consecutive streaming operations. + * Once all streaming compression jobs are completed, + * the state object can be released using LZ4F_freeCompressionContext(). + * Note1 : LZ4F_freeCompressionContext() is always successful. Its return value can be ignored. + * Note2 : LZ4F_freeCompressionContext() works fine with NULL input pointers (do nothing). +**/ +LZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version); +LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx); + + +/*---- Compression ----*/ + +#define LZ4F_HEADER_SIZE_MIN 7 /* LZ4 Frame header size can vary, depending on selected parameters */ +#define LZ4F_HEADER_SIZE_MAX 19 + +/* Size in bytes of a block header in little-endian format. Highest bit indicates if block data is uncompressed */ +#define LZ4F_BLOCK_HEADER_SIZE 4 + +/* Size in bytes of a block checksum footer in little-endian format. */ +#define LZ4F_BLOCK_CHECKSUM_SIZE 4 + +/* Size in bytes of the content checksum. */ +#define LZ4F_CONTENT_CHECKSUM_SIZE 4 + +/*! LZ4F_compressBegin() : + * will write the frame header into dstBuffer. + * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * `prefsPtr` is optional : you can provide NULL as argument, all preferences will then be set to default. + * @return : number of bytes written into dstBuffer for the header + * or an error code (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_API size_t LZ4F_compressBegin(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_preferences_t* prefsPtr); + +/*! LZ4F_compressBound() : + * Provides minimum dstCapacity required to guarantee success of + * LZ4F_compressUpdate(), given a srcSize and preferences, for a worst case scenario. + * When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() instead. + * Note that the result is only valid for a single invocation of LZ4F_compressUpdate(). + * When invoking LZ4F_compressUpdate() multiple times, + * if the output buffer is gradually filled up instead of emptied and re-used from its start, + * one must check if there is enough remaining capacity before each invocation, using LZ4F_compressBound(). + * @return is always the same for a srcSize and prefsPtr. + * prefsPtr is optional : when NULL is provided, preferences will be set to cover worst case scenario. + * tech details : + * @return if automatic flushing is not enabled, includes the possibility that internal buffer might already be filled by up to (blockSize-1) bytes. + * It also includes frame footer (ending + checksum), since it might be generated by LZ4F_compressEnd(). + * @return doesn't include frame header, as it was already generated by LZ4F_compressBegin(). + */ +LZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr); + +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * Important rule: dstCapacity MUST be large enough to ensure operation success even in worst case situations. + * This value is provided by LZ4F_compressBound(). + * If this condition is not respected, LZ4F_compress() will fail (result is an errorCode). + * After an error, the state is left in a UB state, and must be re-initialized or freed. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). + * or an error code if it fails (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); + +/*! LZ4F_flush() : + * When data must be generated and sent immediately, without waiting for a block to be completely filled, + * it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx. + * `dstCapacity` must be large enough to ensure the operation will be successful. + * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default. + * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx) + * or an error code if it fails (which can be tested using LZ4F_isError()) + * Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). + */ +LZ4FLIB_API size_t LZ4F_flush(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* cOptPtr); + +/*! LZ4F_compressEnd() : + * To properly finish an LZ4 frame, invoke LZ4F_compressEnd(). + * It will flush whatever data remained within `cctx` (like LZ4_flush()) + * and properly finalize the frame, with an endMark and a checksum. + * `cOptPtr` is optional : NULL can be provided, in which case all options will be set to default. + * @return : nb of bytes written into dstBuffer, necessarily >= 4 (endMark), + * or an error code if it fails (which can be tested using LZ4F_isError()) + * Note : LZ4F_compressEnd() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr). + * A successful call to LZ4F_compressEnd() makes `cctx` available again for another compression task. + */ +LZ4FLIB_API size_t LZ4F_compressEnd(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_compressOptions_t* cOptPtr); + + +/*-********************************* +* Decompression functions +***********************************/ +typedef struct LZ4F_dctx_s LZ4F_dctx; /* incomplete type */ +typedef LZ4F_dctx* LZ4F_decompressionContext_t; /* compatibility with previous API versions */ + +typedef struct { + unsigned stableDst; /* pledges that last 64KB decompressed data will remain available unmodified between invocations. + * This optimization skips storage operations in tmp buffers. */ + unsigned skipChecksums; /* disable checksum calculation and verification, even when one is present in frame, to save CPU time. + * Setting this option to 1 once disables all checksums for the rest of the frame. */ + unsigned reserved1; /* must be set to zero for forward compatibility */ + unsigned reserved0; /* idem */ +} LZ4F_decompressOptions_t; + + +/* Resource management */ + +/*! LZ4F_createDecompressionContext() : + * Create an LZ4F_dctx object, to track all decompression operations. + * @version provided MUST be LZ4F_VERSION. + * @dctxPtr MUST be valid. + * The function fills @dctxPtr with the value of a pointer to an allocated and initialized LZ4F_dctx object. + * The @return is an errorCode, which can be tested using LZ4F_isError(). + * dctx memory can be released using LZ4F_freeDecompressionContext(); + * Result of LZ4F_freeDecompressionContext() indicates current state of decompressionContext when being released. + * That is, it should be == 0 if decompression has been completed fully and correctly. + */ +LZ4FLIB_API LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, unsigned version); +LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx); + + +/*-*********************************** +* Streaming decompression functions +*************************************/ + +#define LZ4F_MAGICNUMBER 0x184D2204U +#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U +#define LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH 5 + +/*! LZ4F_headerSize() : v1.9.0+ + * Provide the header size of a frame starting at `src`. + * `srcSize` must be >= LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH, + * which is enough to decode the header length. + * @return : size of frame header + * or an error code, which can be tested using LZ4F_isError() + * note : Frame header size is variable, but is guaranteed to be + * >= LZ4F_HEADER_SIZE_MIN bytes, and <= LZ4F_HEADER_SIZE_MAX bytes. + */ +LZ4FLIB_API size_t LZ4F_headerSize(const void* src, size_t srcSize); + +/*! LZ4F_getFrameInfo() : + * This function extracts frame parameters (max blockSize, dictID, etc.). + * Its usage is optional: user can also invoke LZ4F_decompress() directly. + * + * Extracted information will fill an existing LZ4F_frameInfo_t structure. + * This can be useful for allocation and dictionary identification purposes. + * + * LZ4F_getFrameInfo() can work in the following situations : + * + * 1) At the beginning of a new frame, before any invocation of LZ4F_decompress(). + * It will decode header from `srcBuffer`, + * consuming the header and starting the decoding process. + * + * Input size must be large enough to contain the full frame header. + * Frame header size can be known beforehand by LZ4F_headerSize(). + * Frame header size is variable, but is guaranteed to be >= LZ4F_HEADER_SIZE_MIN bytes, + * and not more than <= LZ4F_HEADER_SIZE_MAX bytes. + * Hence, blindly providing LZ4F_HEADER_SIZE_MAX bytes or more will always work. + * It's allowed to provide more input data than the header size, + * LZ4F_getFrameInfo() will only consume the header. + * + * If input size is not large enough, + * aka if it's smaller than header size, + * function will fail and return an error code. + * + * 2) After decoding has been started, + * it's possible to invoke LZ4F_getFrameInfo() anytime + * to extract already decoded frame parameters stored within dctx. + * + * Note that, if decoding has barely started, + * and not yet read enough information to decode the header, + * LZ4F_getFrameInfo() will fail. + * + * The number of bytes consumed from srcBuffer will be updated in *srcSizePtr (necessarily <= original value). + * LZ4F_getFrameInfo() only consumes bytes when decoding has not yet started, + * and when decoding the header has been successful. + * Decompression must then resume from (srcBuffer + *srcSizePtr). + * + * @return : a hint about how many srcSize bytes LZ4F_decompress() expects for next call, + * or an error code which can be tested using LZ4F_isError(). + * note 1 : in case of error, dctx is not modified. Decoding operation can resume from beginning safely. + * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure. + */ +LZ4FLIB_API size_t +LZ4F_getFrameInfo(LZ4F_dctx* dctx, + LZ4F_frameInfo_t* frameInfoPtr, + const void* srcBuffer, size_t* srcSizePtr); + +/*! LZ4F_decompress() : + * Call this function repetitively to regenerate data compressed in `srcBuffer`. + * + * The function requires a valid dctx state. + * It will read up to *srcSizePtr bytes from srcBuffer, + * and decompress data into dstBuffer, of capacity *dstSizePtr. + * + * The nb of bytes consumed from srcBuffer will be written into *srcSizePtr (necessarily <= original value). + * The nb of bytes decompressed into dstBuffer will be written into *dstSizePtr (necessarily <= original value). + * + * The function does not necessarily read all input bytes, so always check value in *srcSizePtr. + * Unconsumed source data must be presented again in subsequent invocations. + * + * `dstBuffer` can freely change between each consecutive function invocation. + * `dstBuffer` content will be overwritten. + * + * @return : an hint of how many `srcSize` bytes LZ4F_decompress() expects for next call. + * Schematically, it's the size of the current (or remaining) compressed block + header of next block. + * Respecting the hint provides some small speed benefit, because it skips intermediate buffers. + * This is just a hint though, it's always possible to provide any srcSize. + * + * When a frame is fully decoded, @return will be 0 (no more data expected). + * When provided with more bytes than necessary to decode a frame, + * LZ4F_decompress() will stop reading exactly at end of current frame, and @return 0. + * + * If decompression failed, @return is an error code, which can be tested using LZ4F_isError(). + * After a decompression error, the `dctx` context is not resumable. + * Use LZ4F_resetDecompressionContext() to return to clean state. + * + * After a frame is fully decoded, dctx can be used again to decompress another frame. + */ +LZ4FLIB_API size_t +LZ4F_decompress(LZ4F_dctx* dctx, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const LZ4F_decompressOptions_t* dOptPtr); + + +/*! LZ4F_resetDecompressionContext() : added in v1.8.0 + * In case of an error, the context is left in "undefined" state. + * In which case, it's necessary to reset it, before re-using it. + * This method can also be used to abruptly stop any unfinished decompression, + * and start a new one using same context resources. */ +LZ4FLIB_API void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx); /* always successful */ + + + +#if defined (__cplusplus) +} +#endif + +#endif /* LZ4F_H_09782039843 */ + +#if defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) +#define LZ4F_H_STATIC_09782039843 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* These declarations are not stable and may change in the future. + * They are therefore only safe to depend on + * when the caller is statically linked against the library. + * To access their declarations, define LZ4F_STATIC_LINKING_ONLY. + * + * By default, these symbols aren't published into shared/dynamic libraries. + * You can override this behavior and force them to be published + * by defining LZ4F_PUBLISH_STATIC_FUNCTIONS. + * Use at your own risk. + */ +#ifdef LZ4F_PUBLISH_STATIC_FUNCTIONS +# define LZ4FLIB_STATIC_API LZ4FLIB_API +#else +# define LZ4FLIB_STATIC_API +#endif + + +/* --- Error List --- */ +#define LZ4F_LIST_ERRORS(ITEM) \ + ITEM(OK_NoError) \ + ITEM(ERROR_GENERIC) \ + ITEM(ERROR_maxBlockSize_invalid) \ + ITEM(ERROR_blockMode_invalid) \ + ITEM(ERROR_contentChecksumFlag_invalid) \ + ITEM(ERROR_compressionLevel_invalid) \ + ITEM(ERROR_headerVersion_wrong) \ + ITEM(ERROR_blockChecksum_invalid) \ + ITEM(ERROR_reservedFlag_set) \ + ITEM(ERROR_allocation_failed) \ + ITEM(ERROR_srcSize_tooLarge) \ + ITEM(ERROR_dstMaxSize_tooSmall) \ + ITEM(ERROR_frameHeader_incomplete) \ + ITEM(ERROR_frameType_unknown) \ + ITEM(ERROR_frameSize_wrong) \ + ITEM(ERROR_srcPtr_wrong) \ + ITEM(ERROR_decompressionFailed) \ + ITEM(ERROR_headerChecksum_invalid) \ + ITEM(ERROR_contentChecksum_invalid) \ + ITEM(ERROR_frameDecoding_alreadyStarted) \ + ITEM(ERROR_compressionState_uninitialized) \ + ITEM(ERROR_parameter_null) \ + ITEM(ERROR_maxCode) + +#define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM, + +/* enum list is exposed, to handle specific errors */ +typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM) + _LZ4F_dummy_error_enum_for_c89_never_used } LZ4F_errorCodes; + +LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult); + + +/*! LZ4F_getBlockSize() : + * Return, in scalar format (size_t), + * the maximum block size associated with blockSizeID. +**/ +LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID); + +/*! LZ4F_uncompressedUpdate() : + * LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. + * Important rule: dstCapacity MUST be large enough to store the entire source buffer as + * no compression is done for this operation + * If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). + * After an error, the state is left in a UB state, and must be re-initialized or freed. + * If previously a compressed block was written, buffered data is flushed + * before appending uncompressed data is continued. + * This is only supported when LZ4F_blockIndependent is used + * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). + * or an error code if it fails (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_STATIC_API size_t +LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); + +/********************************** + * Bulk processing dictionary API + *********************************/ + +/* A Dictionary is useful for the compression of small messages (KB range). + * It dramatically improves compression efficiency. + * + * LZ4 can ingest any input as dictionary, though only the last 64 KB are useful. + * Best results are generally achieved by using Zstandard's Dictionary Builder + * to generate a high-quality dictionary from a set of samples. + * + * Loading a dictionary has a cost, since it involves construction of tables. + * The Bulk processing dictionary API makes it possible to share this cost + * over an arbitrary number of compression jobs, even concurrently, + * markedly improving compression latency for these cases. + * + * The same dictionary will have to be used on the decompression side + * for decoding to be successful. + * To help identify the correct dictionary at decoding stage, + * the frame header allows optional embedding of a dictID field. + */ +typedef struct LZ4F_CDict_s LZ4F_CDict; + +/*! LZ4_createCDict() : + * When compressing multiple messages / blocks using the same dictionary, it's recommended to load it just once. + * LZ4_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. + * LZ4_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. + * `dictBuffer` can be released after LZ4_CDict creation, since its content is copied within CDict */ +LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize); +LZ4FLIB_STATIC_API void LZ4F_freeCDict(LZ4F_CDict* CDict); + + +/*! LZ4_compressFrame_usingCDict() : + * Compress an entire srcBuffer into a valid LZ4 frame using a digested Dictionary. + * cctx must point to a context created by LZ4F_createCompressionContext(). + * If cdict==NULL, compress without a dictionary. + * dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr). + * If this condition is not respected, function will fail (@return an errorCode). + * The LZ4F_preferences_t structure is optional : you may provide NULL as argument, + * but it's not recommended, as it's the only way to provide dictID in the frame header. + * @return : number of bytes written into dstBuffer. + * or an error code if it fails (can be tested using LZ4F_isError()) */ +LZ4FLIB_STATIC_API size_t +LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* preferencesPtr); + + +/*! LZ4F_compressBegin_usingCDict() : + * Inits streaming dictionary compression, and writes the frame header into dstBuffer. + * dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * `prefsPtr` is optional : you may provide NULL as argument, + * however, it's the only way to provide dictID in the frame header. + * @return : number of bytes written into dstBuffer for the header, + * or an error code (which can be tested using LZ4F_isError()) */ +LZ4FLIB_STATIC_API size_t +LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* prefsPtr); + + +/*! LZ4F_decompress_usingDict() : + * Same as LZ4F_decompress(), using a predefined dictionary. + * Dictionary is used "in place", without any preprocessing. +** It must remain accessible throughout the entire frame decoding. */ +LZ4FLIB_STATIC_API size_t +LZ4F_decompress_usingDict(LZ4F_dctx* dctxPtr, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const void* dict, size_t dictSize, + const LZ4F_decompressOptions_t* decompressOptionsPtr); + + +/*! Custom memory allocation : + * These prototypes make it possible to pass custom allocation/free functions. + * LZ4F_customMem is provided at state creation time, using LZ4F_create*_advanced() listed below. + * All allocation/free operations will be completed using these custom variants instead of regular ones. + */ +typedef void* (*LZ4F_AllocFunction) (void* opaqueState, size_t size); +typedef void* (*LZ4F_CallocFunction) (void* opaqueState, size_t size); +typedef void (*LZ4F_FreeFunction) (void* opaqueState, void* address); +typedef struct { + LZ4F_AllocFunction customAlloc; + LZ4F_CallocFunction customCalloc; /* optional; when not defined, uses customAlloc + memset */ + LZ4F_FreeFunction customFree; + void* opaqueState; +} LZ4F_CustomMem; +static +#ifdef __GNUC__ +__attribute__((__unused__)) +#endif +LZ4F_CustomMem const LZ4F_defaultCMem = { NULL, NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */ + +LZ4FLIB_STATIC_API LZ4F_cctx* LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version); +LZ4FLIB_STATIC_API LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version); +LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict_advanced(LZ4F_CustomMem customMem, const void* dictBuffer, size_t dictSize); + + +#if defined (__cplusplus) +} +#endif + +#endif /* defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) */ diff --git a/r5dev/thirdparty/lz4/lz4frame_static.h b/r5dev/thirdparty/lz4/lz4frame_static.h new file mode 100644 index 00000000..2b44a631 --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4frame_static.h @@ -0,0 +1,47 @@ +/* + LZ4 auto-framing library + Header File for static linking only + Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + +#ifndef LZ4FRAME_STATIC_H_0398209384 +#define LZ4FRAME_STATIC_H_0398209384 + +/* The declarations that formerly were made here have been merged into + * lz4frame.h, protected by the LZ4F_STATIC_LINKING_ONLY macro. Going forward, + * it is recommended to simply include that header directly. + */ + +#define LZ4F_STATIC_LINKING_ONLY +#include "lz4frame.h" + +#endif /* LZ4FRAME_STATIC_H_0398209384 */ diff --git a/r5dev/thirdparty/lz4/lz4hc.c b/r5dev/thirdparty/lz4/lz4hc.c new file mode 100644 index 00000000..b21ad6bb --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4hc.c @@ -0,0 +1,1631 @@ +/* + LZ4 HC - High Compression Mode of LZ4 + Copyright (C) 2011-2020, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */ + + +/* ************************************* +* Tuning Parameter +***************************************/ + +/*! HEAPMODE : + * Select how default compression function will allocate workplace memory, + * in stack (0:fastest), or in heap (1:requires malloc()). + * Since workplace is rather large, heap mode is recommended. +**/ +#ifndef LZ4HC_HEAPMODE +# define LZ4HC_HEAPMODE 1 +#endif + + +/*=== Dependency ===*/ +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.h" + + +/*=== Common definitions ===*/ +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif +#if defined (__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#endif + +#define LZ4_COMMONDEFS_ONLY +#ifndef LZ4_SRC_INCLUDED +#include "lz4.c" /* LZ4_count, constants, mem */ +#endif + + +/*=== Enums ===*/ +typedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive; + + +/*=== Constants ===*/ +#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH) +#define LZ4_OPT_NUM (1<<12) + + +/*=== Macros ===*/ +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) +#define HASH_FUNCTION(i) (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG)) +#define DELTANEXTMAXD(p) chainTable[(p) & LZ4HC_MAXD_MASK] /* flexible, LZ4HC_MAXD dependent */ +#define DELTANEXTU16(table, pos) table[(U16)(pos)] /* faster */ +/* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */ +#define UPDATABLE(ip, op, anchor) &ip, &op, &anchor + +static U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); } + + +/************************************** +* HC Compression +**************************************/ +static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) +{ + MEM_INIT(hc4->hashTable, 0, sizeof(hc4->hashTable)); + MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable)); +} + +static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) +{ + size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart); + size_t newStartingOffset = bufferSize + hc4->dictLimit; + assert(newStartingOffset >= bufferSize); /* check overflow */ + if (newStartingOffset > 1 GB) { + LZ4HC_clearTables(hc4); + newStartingOffset = 0; + } + newStartingOffset += 64 KB; + hc4->nextToUpdate = (U32)newStartingOffset; + hc4->prefixStart = start; + hc4->end = start; + hc4->dictStart = start; + hc4->dictLimit = (U32)newStartingOffset; + hc4->lowLimit = (U32)newStartingOffset; +} + + +/* Update chains up to ip (excluded) */ +LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) +{ + U16* const chainTable = hc4->chainTable; + U32* const hashTable = hc4->hashTable; + const BYTE* const prefixPtr = hc4->prefixStart; + U32 const prefixIdx = hc4->dictLimit; + U32 const target = (U32)(ip - prefixPtr) + prefixIdx; + U32 idx = hc4->nextToUpdate; + assert(ip >= prefixPtr); + assert(target >= prefixIdx); + + while (idx < target) { + U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx); + size_t delta = idx - hashTable[h]; + if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX; + DELTANEXTU16(chainTable, idx) = (U16)delta; + hashTable[h] = idx; + idx++; + } + + hc4->nextToUpdate = target; +} + +/** LZ4HC_countBack() : + * @return : negative value, nb of common bytes before ip/match */ +LZ4_FORCE_INLINE +int LZ4HC_countBack(const BYTE* const ip, const BYTE* const match, + const BYTE* const iMin, const BYTE* const mMin) +{ + int back = 0; + int const min = (int)MAX(iMin - ip, mMin - match); + assert(min <= 0); + assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31)); + assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31)); + while ( (back > min) + && (ip[back-1] == match[back-1]) ) + back--; + return back; +} + +#if defined(_MSC_VER) +# define LZ4HC_rotl32(x,r) _rotl(x,r) +#else +# define LZ4HC_rotl32(x,r) ((x << r) | (x >> (32 - r))) +#endif + + +static U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern) +{ + size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3; + if (bitsToRotate == 0) return pattern; + return LZ4HC_rotl32(pattern, (int)bitsToRotate); +} + +/* LZ4HC_countPattern() : + * pattern32 must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) */ +static unsigned +LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32) +{ + const BYTE* const iStart = ip; + reg_t const pattern = (sizeof(pattern)==8) ? + (reg_t)pattern32 + (((reg_t)pattern32) << (sizeof(pattern)*4)) : pattern32; + + while (likely(ip < iEnd-(sizeof(pattern)-1))) { + reg_t const diff = LZ4_read_ARCH(ip) ^ pattern; + if (!diff) { ip+=sizeof(pattern); continue; } + ip += LZ4_NbCommonBytes(diff); + return (unsigned)(ip - iStart); + } + + if (LZ4_isLittleEndian()) { + reg_t patternByte = pattern; + while ((ip>= 8; + } + } else { /* big endian */ + U32 bitOffset = (sizeof(pattern)*8) - 8; + while (ip < iEnd) { + BYTE const byte = (BYTE)(pattern >> bitOffset); + if (*ip != byte) break; + ip ++; bitOffset -= 8; + } } + + return (unsigned)(ip - iStart); +} + +/* LZ4HC_reverseCountPattern() : + * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) + * read using natural platform endianness */ +static unsigned +LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) +{ + const BYTE* const iStart = ip; + + while (likely(ip >= iLow+4)) { + if (LZ4_read32(ip-4) != pattern) break; + ip -= 4; + } + { const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */ + while (likely(ip>iLow)) { + if (ip[-1] != *bytePtr) break; + ip--; bytePtr--; + } } + return (unsigned)(iStart - ip); +} + +/* LZ4HC_protectDictEnd() : + * Checks if the match is in the last 3 bytes of the dictionary, so reading the + * 4 byte MINMATCH would overflow. + * @returns true if the match index is okay. + */ +static int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex) +{ + return ((U32)((dictLimit - 1) - matchIndex) >= 3); +} + +typedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e; +typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e; + +LZ4_FORCE_INLINE int +LZ4HC_InsertAndGetWiderMatch ( + LZ4HC_CCtx_internal* const hc4, + const BYTE* const ip, + const BYTE* const iLowLimit, const BYTE* const iHighLimit, + int longest, + const BYTE** matchpos, + const BYTE** startpos, + const int maxNbAttempts, + const int patternAnalysis, const int chainSwap, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + U16* const chainTable = hc4->chainTable; + U32* const HashTable = hc4->hashTable; + const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx; + const BYTE* const prefixPtr = hc4->prefixStart; + const U32 prefixIdx = hc4->dictLimit; + const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; + const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex); + const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX; + const BYTE* const dictStart = hc4->dictStart; + const U32 dictIdx = hc4->lowLimit; + const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx; + int const lookBackLength = (int)(ip-iLowLimit); + int nbAttempts = maxNbAttempts; + U32 matchChainPos = 0; + U32 const pattern = LZ4_read32(ip); + U32 matchIndex; + repeat_state_e repeat = rep_untested; + size_t srcPatternLength = 0; + + DEBUGLOG(7, "LZ4HC_InsertAndGetWiderMatch"); + /* First Match */ + LZ4HC_Insert(hc4, ip); + matchIndex = HashTable[LZ4HC_hashPtr(ip)]; + DEBUGLOG(7, "First match at index %u / %u (lowestMatchIndex)", + matchIndex, lowestMatchIndex); + + while ((matchIndex>=lowestMatchIndex) && (nbAttempts>0)) { + int matchLength=0; + nbAttempts--; + assert(matchIndex < ipIndex); + if (favorDecSpeed && (ipIndex - matchIndex < 8)) { + /* do nothing */ + } else if (matchIndex >= prefixIdx) { /* within current Prefix */ + const BYTE* const matchPtr = prefixPtr + matchIndex - prefixIdx; + assert(matchPtr < ip); + assert(longest >= 1); + if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) { + if (LZ4_read32(matchPtr) == pattern) { + int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0; + matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit); + matchLength -= back; + if (matchLength > longest) { + longest = matchLength; + *matchpos = matchPtr + back; + *startpos = ip + back; + } } } + } else { /* lowestMatchIndex <= matchIndex < dictLimit */ + const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx); + assert(matchIndex >= dictIdx); + if ( likely(matchIndex <= prefixIdx - 4) + && (LZ4_read32(matchPtr) == pattern) ) { + int back = 0; + const BYTE* vLimit = ip + (prefixIdx - matchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + if ((ip+matchLength == vLimit) && (vLimit < iHighLimit)) + matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit); + back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0; + matchLength -= back; + if (matchLength > longest) { + longest = matchLength; + *matchpos = prefixPtr - prefixIdx + matchIndex + back; /* virtual pos, relative to ip, to retrieve offset */ + *startpos = ip + back; + } } } + + if (chainSwap && matchLength==longest) { /* better match => select a better chain */ + assert(lookBackLength==0); /* search forward only */ + if (matchIndex + (U32)longest <= ipIndex) { + int const kTrigger = 4; + U32 distanceToNextMatch = 1; + int const end = longest - MINMATCH + 1; + int step = 1; + int accel = 1 << kTrigger; + int pos; + for (pos = 0; pos < end; pos += step) { + U32 const candidateDist = DELTANEXTU16(chainTable, matchIndex + (U32)pos); + step = (accel++ >> kTrigger); + if (candidateDist > distanceToNextMatch) { + distanceToNextMatch = candidateDist; + matchChainPos = (U32)pos; + accel = 1 << kTrigger; + } } + if (distanceToNextMatch > 1) { + if (distanceToNextMatch > matchIndex) break; /* avoid overflow */ + matchIndex -= distanceToNextMatch; + continue; + } } } + + { U32 const distNextMatch = DELTANEXTU16(chainTable, matchIndex); + if (patternAnalysis && distNextMatch==1 && matchChainPos==0) { + U32 const matchCandidateIdx = matchIndex-1; + /* may be a repeated pattern */ + if (repeat == rep_untested) { + if ( ((pattern & 0xFFFF) == (pattern >> 16)) + & ((pattern & 0xFF) == (pattern >> 24)) ) { + repeat = rep_confirmed; + srcPatternLength = LZ4HC_countPattern(ip+sizeof(pattern), iHighLimit, pattern) + sizeof(pattern); + } else { + repeat = rep_not; + } } + if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex) + && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) ) { + const int extDict = matchCandidateIdx < prefixIdx; + const BYTE* const matchPtr = (extDict ? dictStart - dictIdx : prefixPtr - prefixIdx) + matchCandidateIdx; + if (LZ4_read32(matchPtr) == pattern) { /* good candidate */ + const BYTE* const iLimit = extDict ? dictEnd : iHighLimit; + size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern); + if (extDict && matchPtr + forwardPatternLength == iLimit) { + U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern); + forwardPatternLength += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern); + } + { const BYTE* const lowestMatchPtr = extDict ? dictStart : prefixPtr; + size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern); + size_t currentSegmentLength; + if (!extDict + && matchPtr - backLength == prefixPtr + && dictIdx < prefixIdx) { + U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern); + backLength += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern); + } + /* Limit backLength not go further than lowestMatchIndex */ + backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex); + assert(matchCandidateIdx - backLength >= lowestMatchIndex); + currentSegmentLength = backLength + forwardPatternLength; + /* Adjust to end of pattern if the source pattern fits, otherwise the beginning of the pattern */ + if ( (currentSegmentLength >= srcPatternLength) /* current pattern segment large enough to contain full srcPatternLength */ + && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */ + U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength; /* best position, full pattern, might be followed by more match */ + if (LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) + matchIndex = newMatchIndex; + else { + /* Can only happen if started in the prefix */ + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; + } + } else { + U32 const newMatchIndex = matchCandidateIdx - (U32)backLength; /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */ + if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) { + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; + } else { + matchIndex = newMatchIndex; + if (lookBackLength==0) { /* no back possible */ + size_t const maxML = MIN(currentSegmentLength, srcPatternLength); + if ((size_t)longest < maxML) { + assert(prefixPtr - prefixIdx + matchIndex != ip); + if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX) break; + assert(maxML < 2 GB); + longest = (int)maxML; + *matchpos = prefixPtr - prefixIdx + matchIndex; /* virtual pos, relative to ip, to retrieve offset */ + *startpos = ip; + } + { U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex); + if (distToNextPattern > matchIndex) break; /* avoid overflow */ + matchIndex -= distToNextPattern; + } } } } } + continue; + } } + } } /* PA optimization */ + + /* follow current chain */ + matchIndex -= DELTANEXTU16(chainTable, matchIndex + matchChainPos); + + } /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */ + + if ( dict == usingDictCtxHc + && nbAttempts > 0 + && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) { + size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; + U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; + assert(dictEndOffset <= 1 GB); + matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset; + while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) { + const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex; + + if (LZ4_read32(matchPtr) == pattern) { + int mlt; + int back = 0; + const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex); + if (vLimit > iHighLimit) vLimit = iHighLimit; + mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; + back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; + mlt -= back; + if (mlt > longest) { + longest = mlt; + *matchpos = prefixPtr - prefixIdx + matchIndex + back; + *startpos = ip + back; + } } + + { U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, dictMatchIndex); + dictMatchIndex -= nextOffset; + matchIndex -= nextOffset; + } } } + + return longest; +} + +LZ4_FORCE_INLINE int +LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table will be updated */ + const BYTE* const ip, const BYTE* const iLimit, + const BYTE** matchpos, + const int maxNbAttempts, + const int patternAnalysis, + const dictCtx_directive dict) +{ + const BYTE* uselessPtr = ip; + /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), + * but this won't be the case here, as we define iLowLimit==ip, + * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ + return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, matchpos, &uselessPtr, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio); +} + +/* LZ4HC_encodeSequence() : + * @return : 0 if ok, + * 1 if buffer issue detected */ +LZ4_FORCE_INLINE int LZ4HC_encodeSequence ( + const BYTE** _ip, + BYTE** _op, + const BYTE** _anchor, + int matchLength, + const BYTE* const match, + limitedOutput_directive limit, + BYTE* oend) +{ +#define ip (*_ip) +#define op (*_op) +#define anchor (*_anchor) + + size_t length; + BYTE* const token = op++; + +#if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6) + static const BYTE* start = NULL; + static U32 totalCost = 0; + U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start); + U32 const ll = (U32)(ip - anchor); + U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0; + U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0; + U32 const cost = 1 + llAdd + ll + 2 + mlAdd; + if (start==NULL) start = anchor; /* only works for single segment */ + /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */ + DEBUGLOG(6, "pos:%7u -- literals:%4u, match:%4i, offset:%5u, cost:%4u + %5u", + pos, + (U32)(ip - anchor), matchLength, (U32)(ip-match), + cost, totalCost); + totalCost += cost; +#endif + + /* Encode Literal length */ + length = (size_t)(ip - anchor); + LZ4_STATIC_ASSERT(notLimited == 0); + /* Check output limit */ + if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) { + DEBUGLOG(6, "Not enough room to write %i literals (%i bytes remaining)", + (int)length, (int)(oend - op)); + return 1; + } + if (length >= RUN_MASK) { + size_t len = length - RUN_MASK; + *token = (RUN_MASK << ML_BITS); + for(; len >= 255 ; len -= 255) *op++ = 255; + *op++ = (BYTE)len; + } else { + *token = (BYTE)(length << ML_BITS); + } + + /* Copy Literals */ + LZ4_wildCopy8(op, anchor, op + length); + op += length; + + /* Encode Offset */ + assert( (ip - match) <= LZ4_DISTANCE_MAX ); /* note : consider providing offset as a value, rather than as a pointer difference */ + LZ4_writeLE16(op, (U16)(ip - match)); op += 2; + + /* Encode MatchLength */ + assert(matchLength >= MINMATCH); + length = (size_t)matchLength - MINMATCH; + if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) { + DEBUGLOG(6, "Not enough room to write match length"); + return 1; /* Check output limit */ + } + if (length >= ML_MASK) { + *token += ML_MASK; + length -= ML_MASK; + for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; } + if (length >= 255) { length -= 255; *op++ = 255; } + *op++ = (BYTE)length; + } else { + *token += (BYTE)(length); + } + + /* Prepare next loop */ + ip += matchLength; + anchor = ip; + + return 0; +} +#undef ip +#undef op +#undef anchor + +LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( + LZ4HC_CCtx_internal* const ctx, + const char* const source, + char* const dest, + int* srcSizePtr, + int const maxOutputSize, + int maxNbAttempts, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + const int inputSize = *srcSizePtr; + const int patternAnalysis = (maxNbAttempts > 128); /* levels 9+ */ + + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + inputSize; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = (iend - LASTLITERALS); + + BYTE* optr = (BYTE*) dest; + BYTE* op = (BYTE*) dest; + BYTE* oend = op + maxOutputSize; + + int ml0, ml, ml2, ml3; + const BYTE* start0; + const BYTE* ref0; + const BYTE* ref = NULL; + const BYTE* start2 = NULL; + const BYTE* ref2 = NULL; + const BYTE* start3 = NULL; + const BYTE* ref3 = NULL; + + /* init */ + *srcSizePtr = 0; + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (inputSize < LZ4_minLength) goto _last_literals; /* Input too small, no compression (all literals) */ + + /* Main Loop */ + while (ip <= mflimit) { + ml = LZ4HC_InsertAndFindBestMatch(ctx, ip, matchlimit, &ref, maxNbAttempts, patternAnalysis, dict); + if (ml encode ML1 */ + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + continue; + } + + if (start0 < ip) { /* first match was skipped at least once */ + if (start2 < ip + ml0) { /* squeezing ML1 between ML0(original ML1) and ML2 */ + ip = start0; ref = ref0; ml = ml0; /* restore initial ML1 */ + } } + + /* Here, start0==ip */ + if ((start2 - ip) < 3) { /* First Match too small : removed */ + ml = ml2; + ip = start2; + ref =ref2; + goto _Search2; + } + +_Search3: + /* At this stage, we have : + * ml2 > ml1, and + * ip1+3 <= ip2 (usually < ip1+ml1) */ + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + int new_ml = ml; + if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML; + if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = new_ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } + /* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */ + + if (start2 + ml2 <= mflimit) { + ml3 = LZ4HC_InsertAndGetWiderMatch(ctx, + start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3, + maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio); + } else { + ml3 = ml2; + } + + if (ml3 == ml2) { /* No better match => encode ML1 and ML2 */ + /* ip & ref are known; Now for ml */ + if (start2 < ip+ml) ml = (int)(start2 - ip); + /* Now, encode 2 sequences */ + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + ip = start2; + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml2, ref2, limit, oend)) { + ml = ml2; + ref = ref2; + goto _dest_overflow; + } + continue; + } + + if (start3 < ip+ml+3) { /* Not enough space for match 2 : remove it */ + if (start3 >= (ip+ml)) { /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */ + if (start2 < ip+ml) { + int correction = (int)(ip+ml - start2); + start2 += correction; + ref2 += correction; + ml2 -= correction; + if (ml2 < MINMATCH) { + start2 = start3; + ref2 = ref3; + ml2 = ml3; + } + } + + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + ip = start3; + ref = ref3; + ml = ml3; + + start0 = start2; + ref0 = ref2; + ml0 = ml2; + goto _Search2; + } + + start2 = start3; + ref2 = ref3; + ml2 = ml3; + goto _Search3; + } + + /* + * OK, now we have 3 ascending matches; + * let's write the first one ML1. + * ip & ref are known; Now decide ml. + */ + if (start2 < ip+ml) { + if ((start2 - ip) < OPTIMAL_ML) { + int correction; + if (ml > OPTIMAL_ML) ml = OPTIMAL_ML; + if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH; + correction = ml - (int)(start2 - ip); + if (correction > 0) { + start2 += correction; + ref2 += correction; + ml2 -= correction; + } + } else { + ml = (int)(start2 - ip); + } + } + optr = op; + if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow; + + /* ML2 becomes ML1 */ + ip = start2; ref = ref2; ml = ml2; + + /* ML3 becomes ML2 */ + start2 = start3; ref2 = ref3; ml2 = ml3; + + /* let's find a new ML3 */ + goto _Search3; + } + +_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) return 0; + /* adapt lastRunSize to fill 'dest' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int) (((const char*)ip) - source); + return (int) (((char*)op)-dest); + +_dest_overflow: + if (limit == fillOutput) { + /* Assumption : ip, anchor, ml and ref must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence overflowing"); + op = optr; /* restore correct out pointer */ + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); assert(ml >= 0); + if ((size_t)ml > maxMlSize) ml = (int)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ml >= MFLIMIT) { + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, notLimited, oend); + } } + goto _last_literals; + } + /* compression failed */ + return 0; +} + + +static int LZ4HC_compress_optimal( LZ4HC_CCtx_internal* ctx, + const char* const source, char* dst, + int* srcSizePtr, int dstCapacity, + int const nbSearches, size_t sufficient_len, + const limitedOutput_directive limit, int const fullUpdate, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed); + + +LZ4_FORCE_INLINE int LZ4HC_compress_generic_internal ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + const limitedOutput_directive limit, + const dictCtx_directive dict + ) +{ + typedef enum { lz4hc, lz4opt } lz4hc_strat_e; + typedef struct { + lz4hc_strat_e strat; + int nbSearches; + U32 targetLength; + } cParams_t; + static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = { + { lz4hc, 2, 16 }, /* 0, unused */ + { lz4hc, 2, 16 }, /* 1, unused */ + { lz4hc, 2, 16 }, /* 2, unused */ + { lz4hc, 4, 16 }, /* 3 */ + { lz4hc, 8, 16 }, /* 4 */ + { lz4hc, 16, 16 }, /* 5 */ + { lz4hc, 32, 16 }, /* 6 */ + { lz4hc, 64, 16 }, /* 7 */ + { lz4hc, 128, 16 }, /* 8 */ + { lz4hc, 256, 16 }, /* 9 */ + { lz4opt, 96, 64 }, /*10==LZ4HC_CLEVEL_OPT_MIN*/ + { lz4opt, 512,128 }, /*11 */ + { lz4opt,16384,LZ4_OPT_NUM }, /* 12==LZ4HC_CLEVEL_MAX */ + }; + + DEBUGLOG(4, "LZ4HC_compress_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)", + ctx, src, *srcSizePtr, limit); + + if (limit == fillOutput && dstCapacity < 1) return 0; /* Impossible to store anything */ + if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0; /* Unsupported input size (too large or negative) */ + + ctx->end += *srcSizePtr; + if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT; /* note : convention is different from lz4frame, maybe something to review */ + cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel); + { cParams_t const cParam = clTable[cLevel]; + HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio; + int result; + + if (cParam.strat == lz4hc) { + result = LZ4HC_compress_hashChain(ctx, + src, dst, srcSizePtr, dstCapacity, + cParam.nbSearches, limit, dict); + } else { + assert(cParam.strat == lz4opt); + result = LZ4HC_compress_optimal(ctx, + src, dst, srcSizePtr, dstCapacity, + cParam.nbSearches, cParam.targetLength, limit, + cLevel == LZ4HC_CLEVEL_MAX, /* ultra mode */ + dict, favor); + } + if (result <= 0) ctx->dirty = 1; + return result; + } +} + +static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock); + +static int +LZ4HC_compress_generic_noDictCtx ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + assert(ctx->dictCtx == NULL); + return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, noDictCtx); +} + +static int +LZ4HC_compress_generic_dictCtx ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit); + assert(ctx->dictCtx != NULL); + if (position >= 64 KB) { + ctx->dictCtx = NULL; + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else if (position == 0 && *srcSizePtr > 4 KB) { + LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal)); + LZ4HC_setExternalDict(ctx, (const BYTE *)src); + ctx->compressionLevel = (short)cLevel; + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else { + return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, usingDictCtxHc); + } +} + +static int +LZ4HC_compress_generic ( + LZ4HC_CCtx_internal* const ctx, + const char* const src, + char* const dst, + int* const srcSizePtr, + int const dstCapacity, + int cLevel, + limitedOutput_directive limit + ) +{ + if (ctx->dictCtx == NULL) { + return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } else { + return LZ4HC_compress_generic_dictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); + } +} + + +int LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); } + +static size_t LZ4_streamHC_t_alignment(void) +{ +#if LZ4_ALIGN_TEST + typedef struct { char c; LZ4_streamHC_t t; } t_a; + return sizeof(t_a) - sizeof(LZ4_streamHC_t); +#else + return 1; /* effectively disabled */ +#endif +} + +/* state is presumed correctly initialized, + * in which case its size and alignment have already been validate */ +int LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse; + if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0; + LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel); + LZ4HC_init_internal (ctx, (const BYTE*)src); + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput); + else + return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, notLimited); +} + +int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); + if (ctx==NULL) return 0; /* init failure */ + return LZ4_compress_HC_extStateHC_fastReset(state, src, dst, srcSize, dstCapacity, compressionLevel); +} + +int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) +{ + int cSize; +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t)); + if (statePtr==NULL) return 0; +#else + LZ4_streamHC_t state; + LZ4_streamHC_t* const statePtr = &state; +#endif + cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + FREEMEM(statePtr); +#endif + return cSize; +} + +/* state is presumed sized correctly (>= sizeof(LZ4_streamHC_t)) */ +int LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel) +{ + LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx)); + if (ctx==NULL) return 0; /* init failure */ + LZ4HC_init_internal(&ctx->internal_donotuse, (const BYTE*) source); + LZ4_setCompressionLevel(ctx, cLevel); + return LZ4HC_compress_generic(&ctx->internal_donotuse, source, dest, sourceSizePtr, targetDestSize, cLevel, fillOutput); +} + + + +/************************************** +* Streaming Functions +**************************************/ +/* allocation */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +LZ4_streamHC_t* LZ4_createStreamHC(void) +{ + LZ4_streamHC_t* const state = + (LZ4_streamHC_t*)ALLOC_AND_ZERO(sizeof(LZ4_streamHC_t)); + if (state == NULL) return NULL; + LZ4_setCompressionLevel(state, LZ4HC_CLEVEL_DEFAULT); + return state; +} + +int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) +{ + DEBUGLOG(4, "LZ4_freeStreamHC(%p)", LZ4_streamHCPtr); + if (!LZ4_streamHCPtr) return 0; /* support free on NULL */ + FREEMEM(LZ4_streamHCPtr); + return 0; +} +#endif + + +LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) +{ + LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer; + DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size); + /* check conditions */ + if (buffer == NULL) return NULL; + if (size < sizeof(LZ4_streamHC_t)) return NULL; + if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL; + /* init */ + { LZ4HC_CCtx_internal* const hcstate = &(LZ4_streamHCPtr->internal_donotuse); + MEM_INIT(hcstate, 0, sizeof(*hcstate)); } + LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT); + return LZ4_streamHCPtr; +} + +/* just a stub */ +void LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); +} + +void LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + DEBUGLOG(4, "LZ4_resetStreamHC_fast(%p, %d)", LZ4_streamHCPtr, compressionLevel); + if (LZ4_streamHCPtr->internal_donotuse.dirty) { + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + } else { + /* preserve end - prefixStart : can trigger clearTable's threshold */ + if (LZ4_streamHCPtr->internal_donotuse.end != NULL) { + LZ4_streamHCPtr->internal_donotuse.end -= (uptrval)LZ4_streamHCPtr->internal_donotuse.prefixStart; + } else { + assert(LZ4_streamHCPtr->internal_donotuse.prefixStart == NULL); + } + LZ4_streamHCPtr->internal_donotuse.prefixStart = NULL; + LZ4_streamHCPtr->internal_donotuse.dictCtx = NULL; + } + LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); +} + +void LZ4_setCompressionLevel(LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel) +{ + DEBUGLOG(5, "LZ4_setCompressionLevel(%p, %d)", LZ4_streamHCPtr, compressionLevel); + if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT; + if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX; + LZ4_streamHCPtr->internal_donotuse.compressionLevel = (short)compressionLevel; +} + +void LZ4_favorDecompressionSpeed(LZ4_streamHC_t* LZ4_streamHCPtr, int favor) +{ + LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = (favor!=0); +} + +/* LZ4_loadDictHC() : + * LZ4_streamHCPtr is presumed properly initialized */ +int LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, + const char* dictionary, int dictSize) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(4, "LZ4_loadDictHC(ctx:%p, dict:%p, dictSize:%d)", LZ4_streamHCPtr, dictionary, dictSize); + assert(LZ4_streamHCPtr != NULL); + if (dictSize > 64 KB) { + dictionary += (size_t)dictSize - 64 KB; + dictSize = 64 KB; + } + /* need a full initialization, there are bad side-effects when using resetFast() */ + { int const cLevel = ctxPtr->compressionLevel; + LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); + LZ4_setCompressionLevel(LZ4_streamHCPtr, cLevel); + } + LZ4HC_init_internal (ctxPtr, (const BYTE*)dictionary); + ctxPtr->end = (const BYTE*)dictionary + dictSize; + if (dictSize >= 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); + return dictSize; +} + +void LZ4_attach_HC_dictionary(LZ4_streamHC_t *working_stream, const LZ4_streamHC_t *dictionary_stream) { + working_stream->internal_donotuse.dictCtx = dictionary_stream != NULL ? &(dictionary_stream->internal_donotuse) : NULL; +} + +/* compression */ + +static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) +{ + DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock); + if (ctxPtr->end >= ctxPtr->prefixStart + 4) + LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */ + + /* Only one memory segment for extDict, so any previous extDict is lost at this stage */ + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart); + ctxPtr->prefixStart = newBlock; + ctxPtr->end = newBlock; + ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */ + + /* cannot reference an extDict and a dictCtx at the same time */ + ctxPtr->dictCtx = NULL; +} + +static int +LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int dstCapacity, + limitedOutput_directive limit) +{ + LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse; + DEBUGLOG(5, "LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)", + LZ4_streamHCPtr, src, *srcSizePtr, limit); + assert(ctxPtr != NULL); + /* auto-init if forgotten */ + if (ctxPtr->prefixStart == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src); + + /* Check overflow */ + if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB) { + size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart); + if (dictSize > 64 KB) dictSize = 64 KB; + LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize); + } + + /* Check if blocks follow each other */ + if ((const BYTE*)src != ctxPtr->end) + LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src); + + /* Check overlapping input/dictionary space */ + { const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr; + const BYTE* const dictBegin = ctxPtr->dictStart; + const BYTE* const dictEnd = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit); + if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) { + if (sourceEnd > dictEnd) sourceEnd = dictEnd; + ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart); + ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart); + if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) { + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + } } } + + return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit); +} + +int LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity) +{ + if (dstCapacity < LZ4_compressBound(srcSize)) + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput); + else + return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, notLimited); +} + +int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize) +{ + return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, fillOutput); +} + + + +/* LZ4_saveDictHC : + * save history content + * into a user-provided buffer + * which is then used to continue compression + */ +int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) +{ + LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; + int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart); + DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); + assert(prefixSize >= 0); + if (dictSize > 64 KB) dictSize = 64 KB; + if (dictSize < 4) dictSize = 0; + if (dictSize > prefixSize) dictSize = prefixSize; + if (safeBuffer == NULL) assert(dictSize == 0); + if (dictSize > 0) + LZ4_memmove(safeBuffer, streamPtr->end - dictSize, dictSize); + { U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit; + streamPtr->end = (const BYTE*)safeBuffer + dictSize; + streamPtr->prefixStart = streamPtr->end - dictSize; + streamPtr->dictLimit = endIndex - (U32)dictSize; + streamPtr->lowLimit = endIndex - (U32)dictSize; + streamPtr->dictStart = streamPtr->prefixStart; + if (streamPtr->nextToUpdate < streamPtr->dictLimit) + streamPtr->nextToUpdate = streamPtr->dictLimit; + } + return dictSize; +} + + +/*************************************************** +* Deprecated Functions +***************************************************/ + +/* These functions currently generate deprecation warnings */ + +/* Wrappers for deprecated compression functions */ +int LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); } +int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); } +int LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); } +int LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); } +int LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); } +int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); } + + +/* Deprecated streaming functions */ +int LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); } + +/* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t) + * @return : 0 on success, !=0 if error */ +int LZ4_resetStreamStateHC(void* state, char* inputBuffer) +{ + LZ4_streamHC_t* const hc4 = LZ4_initStreamHC(state, sizeof(*hc4)); + if (hc4 == NULL) return 1; /* init failed */ + LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); + return 0; +} + +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +void* LZ4_createHC (const char* inputBuffer) +{ + LZ4_streamHC_t* const hc4 = LZ4_createStreamHC(); + if (hc4 == NULL) return NULL; /* not enough memory */ + LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer); + return hc4; +} + +int LZ4_freeHC (void* LZ4HC_Data) +{ + if (!LZ4HC_Data) return 0; /* support free on NULL */ + FREEMEM(LZ4HC_Data); + return 0; +} +#endif + +int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, 0, cLevel, notLimited); +} + +int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel) +{ + return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, dstCapacity, cLevel, limitedOutput); +} + +char* LZ4_slideInputBufferHC(void* LZ4HC_Data) +{ + LZ4_streamHC_t* const ctx = (LZ4_streamHC_t*)LZ4HC_Data; + const BYTE* bufferStart = ctx->internal_donotuse.prefixStart - ctx->internal_donotuse.dictLimit + ctx->internal_donotuse.lowLimit; + LZ4_resetStreamHC_fast(ctx, ctx->internal_donotuse.compressionLevel); + /* avoid const char * -> char * conversion warning :( */ + return (char*)(uptrval)bufferStart; +} + + +/* ================================================ + * LZ4 Optimal parser (levels [LZ4HC_CLEVEL_OPT_MIN - LZ4HC_CLEVEL_MAX]) + * ===============================================*/ +typedef struct { + int price; + int off; + int mlen; + int litlen; +} LZ4HC_optimal_t; + +/* price in bytes */ +LZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen) +{ + int price = litlen; + assert(litlen >= 0); + if (litlen >= (int)RUN_MASK) + price += 1 + ((litlen-(int)RUN_MASK) / 255); + return price; +} + + +/* requires mlen >= MINMATCH */ +LZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen) +{ + int price = 1 + 2 ; /* token + 16-bit offset */ + assert(litlen >= 0); + assert(mlen >= MINMATCH); + + price += LZ4HC_literalsPrice(litlen); + + if (mlen >= (int)(ML_MASK+MINMATCH)) + price += 1 + ((mlen-(int)(ML_MASK+MINMATCH)) / 255); + + return price; +} + + +typedef struct { + int off; + int len; +} LZ4HC_match_t; + +LZ4_FORCE_INLINE LZ4HC_match_t +LZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx, + const BYTE* ip, const BYTE* const iHighLimit, + int minLen, int nbSearches, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + LZ4HC_match_t match = { 0 , 0 }; + const BYTE* matchPtr = NULL; + /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), + * but this won't be the case here, as we define iLowLimit==ip, + * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */ + int matchLength = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, &matchPtr, &ip, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed); + if (matchLength <= minLen) return match; + if (favorDecSpeed) { + if ((matchLength>18) & (matchLength<=36)) matchLength=18; /* favor shortcut */ + } + match.len = matchLength; + match.off = (int)(ip-matchPtr); + return match; +} + + +static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, + const char* const source, + char* dst, + int* srcSizePtr, + int dstCapacity, + int const nbSearches, + size_t sufficient_len, + const limitedOutput_directive limit, + int const fullUpdate, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) +{ + int retval = 0; +#define TRAILING_LITERALS 3 +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS)); +#else + LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS]; /* ~64 KB, which is a bit large for stack... */ +#endif + + const BYTE* ip = (const BYTE*) source; + const BYTE* anchor = ip; + const BYTE* const iend = ip + *srcSizePtr; + const BYTE* const mflimit = iend - MFLIMIT; + const BYTE* const matchlimit = iend - LASTLITERALS; + BYTE* op = (BYTE*) dst; + BYTE* opSaved = (BYTE*) dst; + BYTE* oend = op + dstCapacity; + int ovml = MINMATCH; /* overflow - last sequence */ + const BYTE* ovref = NULL; + + /* init */ +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + if (opt == NULL) goto _return_label; +#endif + DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity); + *srcSizePtr = 0; + if (limit == fillOutput) oend -= LASTLITERALS; /* Hack for support LZ4 format restriction */ + if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1; + + /* Main Loop */ + while (ip <= mflimit) { + int const llen = (int)(ip - anchor); + int best_mlen, best_off; + int cur, last_match_pos = 0; + + LZ4HC_match_t const firstMatch = LZ4HC_FindLongerMatch(ctx, ip, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); + if (firstMatch.len==0) { ip++; continue; } + + if ((size_t)firstMatch.len > sufficient_len) { + /* good enough solution : immediate encoding */ + int const firstML = firstMatch.len; + const BYTE* const matchPos = ip - firstMatch.off; + opSaved = op; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, matchPos, limit, oend) ) { /* updates ip, op and anchor */ + ovml = firstML; + ovref = matchPos; + goto _dest_overflow; + } + continue; + } + + /* set prices for first positions (literals) */ + { int rPos; + for (rPos = 0 ; rPos < MINMATCH ; rPos++) { + int const cost = LZ4HC_literalsPrice(llen + rPos); + opt[rPos].mlen = 1; + opt[rPos].off = 0; + opt[rPos].litlen = llen + rPos; + opt[rPos].price = cost; + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", + rPos, cost, opt[rPos].litlen); + } } + /* set prices using initial match */ + { int mlen = MINMATCH; + int const matchML = firstMatch.len; /* necessarily < sufficient_len < LZ4_OPT_NUM */ + int const offset = firstMatch.off; + assert(matchML < LZ4_OPT_NUM); + for ( ; mlen <= matchML ; mlen++) { + int const cost = LZ4HC_sequencePrice(llen, mlen); + opt[mlen].mlen = mlen; + opt[mlen].off = offset; + opt[mlen].litlen = llen; + opt[mlen].price = cost; + DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i) -- initial setup", + mlen, cost, mlen); + } } + last_match_pos = firstMatch.len; + { int addLit; + for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { + opt[last_match_pos+addLit].mlen = 1; /* literal */ + opt[last_match_pos+addLit].off = 0; + opt[last_match_pos+addLit].litlen = addLit; + opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i) -- initial setup", + last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); + } } + + /* check further positions */ + for (cur = 1; cur < last_match_pos; cur++) { + const BYTE* const curPtr = ip + cur; + LZ4HC_match_t newMatch; + + if (curPtr > mflimit) break; + DEBUGLOG(7, "rPos:%u[%u] vs [%u]%u", + cur, opt[cur].price, opt[cur+1].price, cur+1); + if (fullUpdate) { + /* not useful to search here if next position has same (or lower) cost */ + if ( (opt[cur+1].price <= opt[cur].price) + /* in some cases, next position has same cost, but cost rises sharply after, so a small match would still be beneficial */ + && (opt[cur+MINMATCH].price < opt[cur].price + 3/*min seq price*/) ) + continue; + } else { + /* not useful to search here if next position has same (or lower) cost */ + if (opt[cur+1].price <= opt[cur].price) continue; + } + + DEBUGLOG(7, "search at rPos:%u", cur); + if (fullUpdate) + newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed); + else + /* only test matches of minimum length; slightly faster, but misses a few bytes */ + newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, last_match_pos - cur, nbSearches, dict, favorDecSpeed); + if (!newMatch.len) continue; + + if ( ((size_t)newMatch.len > sufficient_len) + || (newMatch.len + cur >= LZ4_OPT_NUM) ) { + /* immediate encoding */ + best_mlen = newMatch.len; + best_off = newMatch.off; + last_match_pos = cur + 1; + goto encode; + } + + /* before match : set price with literals at beginning */ + { int const baseLitlen = opt[cur].litlen; + int litlen; + for (litlen = 1; litlen < MINMATCH; litlen++) { + int const price = opt[cur].price - LZ4HC_literalsPrice(baseLitlen) + LZ4HC_literalsPrice(baseLitlen+litlen); + int const pos = cur + litlen; + if (price < opt[pos].price) { + opt[pos].mlen = 1; /* literal */ + opt[pos].off = 0; + opt[pos].litlen = baseLitlen+litlen; + opt[pos].price = price; + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", + pos, price, opt[pos].litlen); + } } } + + /* set prices using match at position = cur */ + { int const matchML = newMatch.len; + int ml = MINMATCH; + + assert(cur + newMatch.len < LZ4_OPT_NUM); + for ( ; ml <= matchML ; ml++) { + int const pos = cur + ml; + int const offset = newMatch.off; + int price; + int ll; + DEBUGLOG(7, "testing price rPos %i (last_match_pos=%i)", + pos, last_match_pos); + if (opt[cur].mlen == 1) { + ll = opt[cur].litlen; + price = ((cur > ll) ? opt[cur - ll].price : 0) + + LZ4HC_sequencePrice(ll, ml); + } else { + ll = 0; + price = opt[cur].price + LZ4HC_sequencePrice(0, ml); + } + + assert((U32)favorDecSpeed <= 1); + if (pos > last_match_pos+TRAILING_LITERALS + || price <= opt[pos].price - (int)favorDecSpeed) { + DEBUGLOG(7, "rPos:%3i => price:%3i (matchlen=%i)", + pos, price, ml); + assert(pos < LZ4_OPT_NUM); + if ( (ml == matchML) /* last pos of last match */ + && (last_match_pos < pos) ) + last_match_pos = pos; + opt[pos].mlen = ml; + opt[pos].off = offset; + opt[pos].litlen = ll; + opt[pos].price = price; + } } } + /* complete following positions with literals */ + { int addLit; + for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) { + opt[last_match_pos+addLit].mlen = 1; /* literal */ + opt[last_match_pos+addLit].off = 0; + opt[last_match_pos+addLit].litlen = addLit; + opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit); + DEBUGLOG(7, "rPos:%3i => price:%3i (litlen=%i)", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit); + } } + } /* for (cur = 1; cur <= last_match_pos; cur++) */ + + assert(last_match_pos < LZ4_OPT_NUM + TRAILING_LITERALS); + best_mlen = opt[last_match_pos].mlen; + best_off = opt[last_match_pos].off; + cur = last_match_pos - best_mlen; + +encode: /* cur, last_match_pos, best_mlen, best_off must be set */ + assert(cur < LZ4_OPT_NUM); + assert(last_match_pos >= 1); /* == 1 when only one candidate */ + DEBUGLOG(6, "reverse traversal, looking for shortest path (last_match_pos=%i)", last_match_pos); + { int candidate_pos = cur; + int selected_matchLength = best_mlen; + int selected_offset = best_off; + while (1) { /* from end to beginning */ + int const next_matchLength = opt[candidate_pos].mlen; /* can be 1, means literal */ + int const next_offset = opt[candidate_pos].off; + DEBUGLOG(7, "pos %i: sequence length %i", candidate_pos, selected_matchLength); + opt[candidate_pos].mlen = selected_matchLength; + opt[candidate_pos].off = selected_offset; + selected_matchLength = next_matchLength; + selected_offset = next_offset; + if (next_matchLength > candidate_pos) break; /* last match elected, first match to encode */ + assert(next_matchLength > 0); /* can be 1, means literal */ + candidate_pos -= next_matchLength; + } } + + /* encode all recorded sequences in order */ + { int rPos = 0; /* relative position (to ip) */ + while (rPos < last_match_pos) { + int const ml = opt[rPos].mlen; + int const offset = opt[rPos].off; + if (ml == 1) { ip++; rPos++; continue; } /* literal; note: can end up with several literals, in which case, skip them */ + rPos += ml; + assert(ml >= MINMATCH); + assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX)); + opSaved = op; + if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ip - offset, limit, oend) ) { /* updates ip, op and anchor */ + ovml = ml; + ovref = ip - offset; + goto _dest_overflow; + } } } + } /* while (ip <= mflimit) */ + +_last_literals: + /* Encode Last Literals */ + { size_t lastRunSize = (size_t)(iend - anchor); /* literals */ + size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255; + size_t const totalSize = 1 + llAdd + lastRunSize; + if (limit == fillOutput) oend += LASTLITERALS; /* restore correct value */ + if (limit && (op + totalSize > oend)) { + if (limit == limitedOutput) { /* Check output limit */ + retval = 0; + goto _return_label; + } + /* adapt lastRunSize to fill 'dst' */ + lastRunSize = (size_t)(oend - op) - 1 /*token*/; + llAdd = (lastRunSize + 256 - RUN_MASK) / 256; + lastRunSize -= llAdd; + } + DEBUGLOG(6, "Final literal run : %i literals", (int)lastRunSize); + ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */ + + if (lastRunSize >= RUN_MASK) { + size_t accumulator = lastRunSize - RUN_MASK; + *op++ = (RUN_MASK << ML_BITS); + for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255; + *op++ = (BYTE) accumulator; + } else { + *op++ = (BYTE)(lastRunSize << ML_BITS); + } + LZ4_memcpy(op, anchor, lastRunSize); + op += lastRunSize; + } + + /* End */ + *srcSizePtr = (int) (((const char*)ip) - source); + retval = (int) ((char*)op-dst); + goto _return_label; + +_dest_overflow: +if (limit == fillOutput) { + /* Assumption : ip, anchor, ovml and ovref must be set correctly */ + size_t const ll = (size_t)(ip - anchor); + size_t const ll_addbytes = (ll + 240) / 255; + size_t const ll_totalCost = 1 + ll_addbytes + ll; + BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */ + DEBUGLOG(6, "Last sequence overflowing (only %i bytes remaining)", (int)(oend-1-opSaved)); + op = opSaved; /* restore correct out pointer */ + if (op + ll_totalCost <= maxLitPos) { + /* ll validated; now adjust match length */ + size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost)); + size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255); + assert(maxMlSize < INT_MAX); assert(ovml >= 0); + if ((size_t)ovml > maxMlSize) ovml = (int)maxMlSize; + if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ovml >= MFLIMIT) { + DEBUGLOG(6, "Space to end : %i + ml (%i)", (int)((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1), ovml); + DEBUGLOG(6, "Before : ip = %p, anchor = %p", ip, anchor); + LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ovml, ovref, notLimited, oend); + DEBUGLOG(6, "After : ip = %p, anchor = %p", ip, anchor); + } } + goto _last_literals; +} +_return_label: +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 + FREEMEM(opt); +#endif + return retval; +} diff --git a/r5dev/thirdparty/lz4/lz4hc.h b/r5dev/thirdparty/lz4/lz4hc.h new file mode 100644 index 00000000..e937acfe --- /dev/null +++ b/r5dev/thirdparty/lz4/lz4hc.h @@ -0,0 +1,413 @@ +/* + LZ4 HC - High Compression Mode of LZ4 + Header File + Copyright (C) 2011-2020, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#ifndef LZ4_HC_H_19834876238432 +#define LZ4_HC_H_19834876238432 + +#if defined (__cplusplus) +extern "C" { +#endif + +/* --- Dependency --- */ +/* note : lz4hc requires lz4.h/lz4.c for compilation */ +#include "lz4.h" /* stddef, LZ4LIB_API, LZ4_DEPRECATED */ + + +/* --- Useful constants --- */ +#define LZ4HC_CLEVEL_MIN 3 +#define LZ4HC_CLEVEL_DEFAULT 9 +#define LZ4HC_CLEVEL_OPT_MIN 10 +#define LZ4HC_CLEVEL_MAX 12 + + +/*-************************************ + * Block Compression + **************************************/ +/*! LZ4_compress_HC() : + * Compress data from `src` into `dst`, using the powerful but slower "HC" algorithm. + * `dst` must be already allocated. + * Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see "lz4.h") + * Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see "lz4.h") + * `compressionLevel` : any value between 1 and LZ4HC_CLEVEL_MAX will work. + * Values > LZ4HC_CLEVEL_MAX behave the same as LZ4HC_CLEVEL_MAX. + * @return : the number of bytes written into 'dst' + * or 0 if compression fails. + */ +LZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel); + + +/* Note : + * Decompression functions are provided within "lz4.h" (BSD license) + */ + + +/*! LZ4_compress_HC_extStateHC() : + * Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`. + * `state` size is provided by LZ4_sizeofStateHC(). + * Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() should do properly). + */ +LZ4LIB_API int LZ4_sizeofStateHC(void); +LZ4LIB_API int LZ4_compress_HC_extStateHC(void* stateHC, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel); + + +/*! LZ4_compress_HC_destSize() : v1.9.0+ + * Will compress as much data as possible from `src` + * to fit into `targetDstSize` budget. + * Result is provided in 2 parts : + * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) + * or 0 if compression fails. + * `srcSizePtr` : on success, *srcSizePtr is updated to indicate how much bytes were read from `src` + */ +LZ4LIB_API int LZ4_compress_HC_destSize(void* stateHC, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize, + int compressionLevel); + + +/*-************************************ + * Streaming Compression + * Bufferless synchronous API + **************************************/ + typedef union LZ4_streamHC_u LZ4_streamHC_t; /* incomplete type (defined later) */ + +/*! LZ4_createStreamHC() and LZ4_freeStreamHC() : + * These functions create and release memory for LZ4 HC streaming state. + * Newly created states are automatically initialized. + * A same state can be used multiple times consecutively, + * starting with LZ4_resetStreamHC_fast() to start a new stream of blocks. + */ +LZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void); +LZ4LIB_API int LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr); + +/* + These functions compress data in successive blocks of any size, + using previous blocks as dictionary, to improve compression ratio. + One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks. + There is an exception for ring buffers, which can be smaller than 64 KB. + Ring-buffer scenario is automatically detected and handled within LZ4_compress_HC_continue(). + + Before starting compression, state must be allocated and properly initialized. + LZ4_createStreamHC() does both, though compression level is set to LZ4HC_CLEVEL_DEFAULT. + + Selecting the compression level can be done with LZ4_resetStreamHC_fast() (starts a new stream) + or LZ4_setCompressionLevel() (anytime, between blocks in the same stream) (experimental). + LZ4_resetStreamHC_fast() only works on states which have been properly initialized at least once, + which is automatically the case when state is created using LZ4_createStreamHC(). + + After reset, a first "fictional block" can be designated as initial dictionary, + using LZ4_loadDictHC() (Optional). + + Invoke LZ4_compress_HC_continue() to compress each successive block. + The number of blocks is unlimited. + Previous input blocks, including initial dictionary when present, + must remain accessible and unmodified during compression. + + It's allowed to update compression level anytime between blocks, + using LZ4_setCompressionLevel() (experimental). + + 'dst' buffer should be sized to handle worst case scenarios + (see LZ4_compressBound(), it ensures compression success). + In case of failure, the API does not guarantee recovery, + so the state _must_ be reset. + To ensure compression success + whenever `dst` buffer size cannot be made >= LZ4_compressBound(), + consider using LZ4_compress_HC_continue_destSize(). + + Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks, + it's possible to copy the last blocks into a more stable memory space, using LZ4_saveDictHC(). + Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer' (<= 64 KB) + + After completing a streaming compression, + it's possible to start a new stream of blocks, using the same LZ4_streamHC_t state, + just by resetting it, using LZ4_resetStreamHC_fast(). +*/ + +LZ4LIB_API void LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, int compressionLevel); /* v1.9.0+ */ +LZ4LIB_API int LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize); + +LZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr, + const char* src, char* dst, + int srcSize, int maxDstSize); + +/*! LZ4_compress_HC_continue_destSize() : v1.9.0+ + * Similar to LZ4_compress_HC_continue(), + * but will read as much data as possible from `src` + * to fit into `targetDstSize` budget. + * Result is provided into 2 parts : + * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize) + * or 0 if compression fails. + * `srcSizePtr` : on success, *srcSizePtr will be updated to indicate how much bytes were read from `src`. + * Note that this function may not consume the entire input. + */ +LZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr, + const char* src, char* dst, + int* srcSizePtr, int targetDstSize); + +LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize); + + + +/*^********************************************** + * !!!!!! STATIC LINKING ONLY !!!!!! + ***********************************************/ + +/*-****************************************************************** + * PRIVATE DEFINITIONS : + * Do not use these definitions directly. + * They are merely exposed to allow static allocation of `LZ4_streamHC_t`. + * Declare an `LZ4_streamHC_t` directly, rather than any type below. + * Even then, only do so in the context of static linking, as definitions may change between versions. + ********************************************************************/ + +#define LZ4HC_DICTIONARY_LOGSIZE 16 +#define LZ4HC_MAXD (1<= LZ4HC_CLEVEL_OPT_MIN. + */ +LZ4LIB_STATIC_API void LZ4_favorDecompressionSpeed( + LZ4_streamHC_t* LZ4_streamHCPtr, int favor); + +/*! LZ4_resetStreamHC_fast() : v1.9.0+ + * When an LZ4_streamHC_t is known to be in a internally coherent state, + * it can often be prepared for a new compression with almost no work, only + * sometimes falling back to the full, expensive reset that is always required + * when the stream is in an indeterminate state (i.e., the reset performed by + * LZ4_resetStreamHC()). + * + * LZ4_streamHCs are guaranteed to be in a valid state when: + * - returned from LZ4_createStreamHC() + * - reset by LZ4_resetStreamHC() + * - memset(stream, 0, sizeof(LZ4_streamHC_t)) + * - the stream was in a valid state and was reset by LZ4_resetStreamHC_fast() + * - the stream was in a valid state and was then used in any compression call + * that returned success + * - the stream was in an indeterminate state and was used in a compression + * call that fully reset the state (LZ4_compress_HC_extStateHC()) and that + * returned success + * + * Note: + * A stream that was last used in a compression call that returned an error + * may be passed to this function. However, it will be fully reset, which will + * clear any existing history and settings from the context. + */ +LZ4LIB_STATIC_API void LZ4_resetStreamHC_fast( + LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel); + +/*! LZ4_compress_HC_extStateHC_fastReset() : + * A variant of LZ4_compress_HC_extStateHC(). + * + * Using this variant avoids an expensive initialization step. It is only safe + * to call if the state buffer is known to be correctly initialized already + * (see above comment on LZ4_resetStreamHC_fast() for a definition of + * "correctly initialized"). From a high level, the difference is that this + * function initializes the provided state with a call to + * LZ4_resetStreamHC_fast() while LZ4_compress_HC_extStateHC() starts with a + * call to LZ4_resetStreamHC(). + */ +LZ4LIB_STATIC_API int LZ4_compress_HC_extStateHC_fastReset ( + void* state, + const char* src, char* dst, + int srcSize, int dstCapacity, + int compressionLevel); + +/*! LZ4_attach_HC_dictionary() : + * This is an experimental API that allows for the efficient use of a + * static dictionary many times. + * + * Rather than re-loading the dictionary buffer into a working context before + * each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a + * working LZ4_streamHC_t, this function introduces a no-copy setup mechanism, + * in which the working stream references the dictionary stream in-place. + * + * Several assumptions are made about the state of the dictionary stream. + * Currently, only streams which have been prepared by LZ4_loadDictHC() should + * be expected to work. + * + * Alternatively, the provided dictionary stream pointer may be NULL, in which + * case any existing dictionary stream is unset. + * + * A dictionary should only be attached to a stream without any history (i.e., + * a stream that has just been reset). + * + * The dictionary will remain attached to the working stream only for the + * current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the + * dictionary context association from the working stream. The dictionary + * stream (and source buffer) must remain in-place / accessible / unchanged + * through the lifetime of the stream session. + */ +LZ4LIB_STATIC_API void LZ4_attach_HC_dictionary( + LZ4_streamHC_t *working_stream, + const LZ4_streamHC_t *dictionary_stream); + +#if defined (__cplusplus) +} +#endif + +#endif /* LZ4_HC_SLO_098092834 */ +#endif /* LZ4_HC_STATIC_LINKING_ONLY */ diff --git a/r5dev/thirdparty/lz4/xxhash.c b/r5dev/thirdparty/lz4/xxhash.c new file mode 100644 index 00000000..ff28749e --- /dev/null +++ b/r5dev/thirdparty/lz4/xxhash.c @@ -0,0 +1,1030 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#include /* assert */ + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN +static int XXH_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +/* mix all bits */ +static U32 XXH32_avalanche(U32 h32) +{ + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +static U32 +XXH32_finalize(U32 h32, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) + +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1 \ + h32 += (*p++) * PRIME32_5; \ + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + +#define PROCESS4 \ + h32 += XXH_get32bits(p) * PRIME32_3; \ + p+=4; \ + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + + switch(len&15) /* or switch(bEnd - p) */ + { + case 12: PROCESS4; + /* fallthrough */ + case 8: PROCESS4; + /* fallthrough */ + case 4: PROCESS4; + return XXH32_avalanche(h32); + + case 13: PROCESS4; + /* fallthrough */ + case 9: PROCESS4; + /* fallthrough */ + case 5: PROCESS4; + PROCESS1; + return XXH32_avalanche(h32); + + case 14: PROCESS4; + /* fallthrough */ + case 10: PROCESS4; + /* fallthrough */ + case 6: PROCESS4; + PROCESS1; + PROCESS1; + return XXH32_avalanche(h32); + + case 15: PROCESS4; + /* fallthrough */ + case 11: PROCESS4; + /* fallthrough */ + case 7: PROCESS4; + /* fallthrough */ + case 3: PROCESS1; + /* fallthrough */ + case 2: PROCESS1; + /* fallthrough */ + case 1: PROCESS1; + /* fallthrough */ + case 0: return XXH32_avalanche(h32); + } + assert(0); + return h32; /* reaching this point is deemed impossible */ +} + + +FORCE_INLINE U32 +XXH32_endian_align(const void* input, size_t len, U32 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 15; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32)len; + + return XXH32_finalize(h32, p, len&15, endian, align); +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +FORCE_INLINE XXH_errorcode +XXH32_update_endian(XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const U32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + +FORCE_INLINE U32 +XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned); +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +static U64 XXH64_avalanche(U64 h64) +{ + h64 ^= h64 >> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +static U64 +XXH64_finalize(U64 h64, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1_64 \ + h64 ^= (*p++) * PRIME64_5; \ + h64 = XXH_rotl64(h64, 11) * PRIME64_1; + +#define PROCESS4_64 \ + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \ + p+=4; \ + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + +#define PROCESS8_64 { \ + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \ + p+=8; \ + h64 ^= k1; \ + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \ +} + + switch(len&31) { + case 24: PROCESS8_64; + /* fallthrough */ + case 16: PROCESS8_64; + /* fallthrough */ + case 8: PROCESS8_64; + return XXH64_avalanche(h64); + + case 28: PROCESS8_64; + /* fallthrough */ + case 20: PROCESS8_64; + /* fallthrough */ + case 12: PROCESS8_64; + /* fallthrough */ + case 4: PROCESS4_64; + return XXH64_avalanche(h64); + + case 25: PROCESS8_64; + /* fallthrough */ + case 17: PROCESS8_64; + /* fallthrough */ + case 9: PROCESS8_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 29: PROCESS8_64; + /* fallthrough */ + case 21: PROCESS8_64; + /* fallthrough */ + case 13: PROCESS8_64; + /* fallthrough */ + case 5: PROCESS4_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 26: PROCESS8_64; + /* fallthrough */ + case 18: PROCESS8_64; + /* fallthrough */ + case 10: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 30: PROCESS8_64; + /* fallthrough */ + case 22: PROCESS8_64; + /* fallthrough */ + case 14: PROCESS8_64; + /* fallthrough */ + case 6: PROCESS4_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 27: PROCESS8_64; + /* fallthrough */ + case 19: PROCESS8_64; + /* fallthrough */ + case 11: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 31: PROCESS8_64; + /* fallthrough */ + case 23: PROCESS8_64; + /* fallthrough */ + case 15: PROCESS8_64; + /* fallthrough */ + case 7: PROCESS4_64; + /* fallthrough */ + case 3: PROCESS1_64; + /* fallthrough */ + case 2: PROCESS1_64; + /* fallthrough */ + case 1: PROCESS1_64; + /* fallthrough */ + case 0: return XXH64_avalanche(h64); + } + + /* impossible to reach */ + assert(0); + return 0; /* unreachable, but some compilers complain without it */ +} + +FORCE_INLINE U64 +XXH64_endian_align(const void* input, size_t len, U64 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + return XXH64_finalize(h64, p, len, endian, align); +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +FORCE_INLINE XXH_errorcode +XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 /*seed*/ + PRIME64_5; + } + + h64 += (U64) state->total_len; + + return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned); +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/r5dev/thirdparty/lz4/xxhash.h b/r5dev/thirdparty/lz4/xxhash.h new file mode 100644 index 00000000..d6bad943 --- /dev/null +++ b/r5dev/thirdparty/lz4/xxhash.h @@ -0,0 +1,328 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** + * API modifier + ******************************/ +/** XXH_INLINE_ALL (and XXH_PRIVATE_API) + * This is useful to include xxhash functions in `static` mode + * in order to inline them, and remove their symbol from the public list. + * Inlining can offer dramatic performance improvement on small keys. + * Methodology : + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * `xxhash.c` is automatically included. + * It's not useful to compile and link it as a separate module. + */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/*! XXH_NAMESPACE, aka Namespace Emulation : + * + * If you want to include _and expose_ xxHash functions from within your own library, + * but also want to avoid symbol collisions with other libraries which may also include xxHash, + * + * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library + * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + * + * Note that no change is required within the calling program as long as it includes `xxhash.h` : + * regular symbol name will be automatically translated by this header. + */ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 5 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* + * Streaming functions generate the xxHash of an input provided in multiple segments. + * Note that, for small input, they are slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * XXH state must first be allocated, using XXH*_createState() . + * + * Start a new hash by initializing state with a seed, using XXH*_reset(). + * + * Then, feed the hash state by calling XXH*_update() as many times as necessary. + * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using XXH*_digest(). + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a digest, + * and generate some new hashes later on, by calling again XXH*_digest(). + * + * When done, free XXH state space if it was allocated dynamically. + */ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. + * The canonical representation uses human-readable write convention, aka big-endian (large digits first). + * These functions allow transformation of hash result into and from its canonical format. + * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. + */ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only present to allow + * static allocation of XXH state, on stack or in a struct for example. + * Never **ever** use members directly. */ + +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + +struct XXH32_state_s { + uint32_t total_len_32; + uint32_t large_len; + uint32_t v1; + uint32_t v2; + uint32_t v3; + uint32_t v4; + uint32_t mem32[4]; + uint32_t memsize; + uint32_t reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +struct XXH64_state_s { + uint64_t total_len; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t v4; + uint64_t mem64[4]; + uint32_t memsize; + uint32_t reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ + +# else + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; + unsigned memsize; + unsigned reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +# ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; + unsigned memsize; + unsigned reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +# endif + +# endif + + +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/r5dev/thirdparty/mbedtls/.gitignore b/r5dev/thirdparty/mbedtls/.gitignore new file mode 100644 index 00000000..3a63a63a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/.gitignore @@ -0,0 +1,4 @@ +*.o +libmbed* +*.sln +*.vcxproj diff --git a/r5dev/thirdparty/mbedtls/3rdparty/.gitignore b/r5dev/thirdparty/mbedtls/3rdparty/.gitignore new file mode 100644 index 00000000..5fc607b9 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/.gitignore @@ -0,0 +1 @@ +/Makefile diff --git a/r5dev/thirdparty/mbedtls/3rdparty/CMakeLists.txt b/r5dev/thirdparty/mbedtls/3rdparty/CMakeLists.txt new file mode 100644 index 00000000..e81ff513 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/CMakeLists.txt @@ -0,0 +1,5 @@ +execute_process(COMMAND ${MBEDTLS_PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../scripts/config.py -f ${CMAKE_CURRENT_SOURCE_DIR}/../include/mbedtls/mbedtls_config.h get MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED RESULT_VARIABLE result) + +if(${result} EQUAL 0) + add_subdirectory(everest) +endif() diff --git a/r5dev/thirdparty/mbedtls/3rdparty/Makefile.inc b/r5dev/thirdparty/mbedtls/3rdparty/Makefile.inc new file mode 100644 index 00000000..0ed85af5 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/Makefile.inc @@ -0,0 +1,2 @@ +THIRDPARTY_DIR = $(dir $(lastword $(MAKEFILE_LIST))) +include $(THIRDPARTY_DIR)/everest/Makefile.inc diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/.gitignore b/r5dev/thirdparty/mbedtls/3rdparty/everest/.gitignore new file mode 100644 index 00000000..6eb25f66 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/.gitignore @@ -0,0 +1,2 @@ +*.o +Makefile diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/CMakeLists.txt b/r5dev/thirdparty/mbedtls/3rdparty/everest/CMakeLists.txt new file mode 100644 index 00000000..4ad367e1 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/CMakeLists.txt @@ -0,0 +1,27 @@ +add_library(everest + library/everest.c + library/x25519.c + library/Hacl_Curve25519_joined.c) + +target_include_directories(everest + PUBLIC $ + $ + $ + PRIVATE include/everest + include/everest/kremlib + ${MBEDTLS_DIR}/library/) + +if(INSTALL_MBEDTLS_HEADERS) + + install(DIRECTORY include/everest + DESTINATION include + FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + FILES_MATCHING PATTERN "*.h") + +endif(INSTALL_MBEDTLS_HEADERS) + +install(TARGETS everest + EXPORT MbedTLSTargets + DESTINATION ${CMAKE_INSTALL_LIBDIR} + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/Makefile.inc b/r5dev/thirdparty/mbedtls/3rdparty/everest/Makefile.inc new file mode 100644 index 00000000..77a6b496 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/Makefile.inc @@ -0,0 +1,6 @@ +THIRDPARTY_INCLUDES+=-I../3rdparty/everest/include -I../3rdparty/everest/include/everest -I../3rdparty/everest/include/everest/kremlib + +THIRDPARTY_CRYPTO_OBJECTS+= \ + ../3rdparty/everest/library/everest.o \ + ../3rdparty/everest/library/x25519.o \ + ../3rdparty/everest/library/Hacl_Curve25519_joined.o diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/README.md b/r5dev/thirdparty/mbedtls/3rdparty/everest/README.md new file mode 100644 index 00000000..bcf12c0c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/README.md @@ -0,0 +1,5 @@ +The files in this directory stem from [Project Everest](https://project-everest.github.io/) and are distributed under the Apache 2.0 license. + +This is a formally verified implementation of Curve25519-based handshakes. The C code is automatically derived from the (verified) [original implementation](https://github.com/project-everest/hacl-star/tree/master/code/curve25519) in the [F* language](https://github.com/fstarlang/fstar) by [KreMLin](https://github.com/fstarlang/kremlin). In addition to the improved safety and security of the implementation, it is also significantly faster than the default implementation of Curve25519 in mbedTLS. + +The caveat is that not all platforms are supported, although the version in `everest/library/legacy` should work on most systems. The main issue is that some platforms do not provide a 128-bit integer type and KreMLin therefore has to use additional (also verified) code to simulate them, resulting in less of a performance gain overall. Explicitly supported platforms are currently `x86` and `x86_64` using gcc or clang, and Visual C (2010 and later). diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/Hacl_Curve25519.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/Hacl_Curve25519.h new file mode 100644 index 00000000..e3f5ba44 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/Hacl_Curve25519.h @@ -0,0 +1,21 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: /mnt/e/everest/verify/kremlin/krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -fbuiltin-uint128 -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -I /mnt/e/everest/verify/hacl-star/code/lib/kremlin -I /mnt/e/everest/verify/kremlin/kremlib/compat -I /mnt/e/everest/verify/hacl-star/specs -I /mnt/e/everest/verify/hacl-star/specs/old -I . -ccopt -march=native -verbose -ldopt -flto -tmpdir x25519-c -I ../bignum -bundle Hacl.Curve25519=* -minimal -add-include "kremlib.h" -skip-compilation x25519-c/out.krml -o x25519-c/Hacl_Curve25519.c + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + + +#ifndef __Hacl_Curve25519_H +#define __Hacl_Curve25519_H + + +#include "kremlib.h" + +void Hacl_Curve25519_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint); + +#define __Hacl_Curve25519_H_DEFINED +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/everest.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/everest.h new file mode 100644 index 00000000..392e7924 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/everest.h @@ -0,0 +1,234 @@ +/* + * Interface to code from Project Everest + * + * Copyright 2016-2018 INRIA and Microsoft Corporation + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org). + */ + +#ifndef MBEDTLS_EVEREST_H +#define MBEDTLS_EVEREST_H + +#include "everest/x25519.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Defines the source of the imported EC key. + */ +typedef enum +{ + MBEDTLS_EVEREST_ECDH_OURS, /**< Our key. */ + MBEDTLS_EVEREST_ECDH_THEIRS, /**< The key of the peer. */ +} mbedtls_everest_ecdh_side; + +typedef struct { + mbedtls_x25519_context ctx; +} mbedtls_ecdh_context_everest; + + +/** + * \brief This function sets up the ECDH context with the information + * given. + * + * This function should be called after mbedtls_ecdh_init() but + * before mbedtls_ecdh_make_params(). There is no need to call + * this function before mbedtls_ecdh_read_params(). + * + * This is the first function used by a TLS server for ECDHE + * ciphersuites. + * + * \param ctx The ECDH context to set up. + * \param grp_id The group id of the group to set up the context for. + * + * \return \c 0 on success. + */ +int mbedtls_everest_setup( mbedtls_ecdh_context_everest *ctx, int grp_id ); + +/** + * \brief This function frees a context. + * + * \param ctx The context to free. + */ +void mbedtls_everest_free( mbedtls_ecdh_context_everest *ctx ); + +/** + * \brief This function generates a public key and a TLS + * ServerKeyExchange payload. + * + * This is the second function used by a TLS server for ECDHE + * ciphersuites. (It is called after mbedtls_ecdh_setup().) + * + * \note This function assumes that the ECP group (grp) of the + * \p ctx context has already been properly set, + * for example, using mbedtls_ecp_group_load(). + * + * \see ecp.h + * + * \param ctx The ECDH context. + * \param olen The number of characters written. + * \param buf The destination buffer. + * \param blen The length of the destination buffer. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_everest_make_params( mbedtls_ecdh_context_everest *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )( void *, unsigned char *, size_t ), + void *p_rng ); + +/** + * \brief This function parses and processes a TLS ServerKeyExchange + * payload. + * + * This is the first function used by a TLS client for ECDHE + * ciphersuites. + * + * \see ecp.h + * + * \param ctx The ECDH context. + * \param buf The pointer to the start of the input buffer. + * \param end The address for one Byte past the end of the buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + * + */ +int mbedtls_everest_read_params( mbedtls_ecdh_context_everest *ctx, + const unsigned char **buf, const unsigned char *end ); + +/** + * \brief This function parses and processes a TLS ServerKeyExchange + * payload. + * + * This is the first function used by a TLS client for ECDHE + * ciphersuites. + * + * \see ecp.h + * + * \param ctx The ECDH context. + * \param buf The pointer to the start of the input buffer. + * \param end The address for one Byte past the end of the buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + * + */ +int mbedtls_everest_read_params( mbedtls_ecdh_context_everest *ctx, + const unsigned char **buf, const unsigned char *end ); + +/** + * \brief This function sets up an ECDH context from an EC key. + * + * It is used by clients and servers in place of the + * ServerKeyEchange for static ECDH, and imports ECDH + * parameters from the EC key information of a certificate. + * + * \see ecp.h + * + * \param ctx The ECDH context to set up. + * \param key The EC key to use. + * \param side Defines the source of the key: 1: Our key, or + * 0: The key of the peer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + * + */ +int mbedtls_everest_get_params( mbedtls_ecdh_context_everest *ctx, const mbedtls_ecp_keypair *key, + mbedtls_everest_ecdh_side side ); + +/** + * \brief This function generates a public key and a TLS + * ClientKeyExchange payload. + * + * This is the second function used by a TLS client for ECDH(E) + * ciphersuites. + * + * \see ecp.h + * + * \param ctx The ECDH context. + * \param olen The number of Bytes written. + * \param buf The destination buffer. + * \param blen The size of the destination buffer. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_everest_make_public( mbedtls_ecdh_context_everest *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )( void *, unsigned char *, size_t ), + void *p_rng ); + +/** + * \brief This function parses and processes a TLS ClientKeyExchange + * payload. + * + * This is the third function used by a TLS server for ECDH(E) + * ciphersuites. (It is called after mbedtls_ecdh_setup() and + * mbedtls_ecdh_make_params().) + * + * \see ecp.h + * + * \param ctx The ECDH context. + * \param buf The start of the input buffer. + * \param blen The length of the input buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_everest_read_public( mbedtls_ecdh_context_everest *ctx, + const unsigned char *buf, size_t blen ); + +/** + * \brief This function derives and exports the shared secret. + * + * This is the last function used by both TLS client + * and servers. + * + * \note If \p f_rng is not NULL, it is used to implement + * countermeasures against side-channel attacks. + * For more information, see mbedtls_ecp_mul(). + * + * \see ecp.h + * + * \param ctx The ECDH context. + * \param olen The number of Bytes written. + * \param buf The destination buffer. + * \param blen The length of the destination buffer. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_everest_calc_secret( mbedtls_ecdh_context_everest *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )( void *, unsigned char *, size_t ), + void *p_rng ); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_EVEREST_H */ diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib.h new file mode 100644 index 00000000..f06663f0 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib.h @@ -0,0 +1,29 @@ +/* + * Copyright 2016-2018 INRIA and Microsoft Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) and + * originated from Project Everest (https://project-everest.github.io/) + */ + +#ifndef __KREMLIB_H +#define __KREMLIB_H + +#include "kremlin/internal/target.h" +#include "kremlin/internal/types.h" +#include "kremlin/c_endianness.h" + +#endif /* __KREMLIB_H */ diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib/FStar_UInt128.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib/FStar_UInt128.h new file mode 100644 index 00000000..d71c8820 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib/FStar_UInt128.h @@ -0,0 +1,124 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: ../krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrB9w -minimal -fparentheses -fcurly-braces -fno-shadow -header copyright-header.txt -minimal -tmpdir dist/uint128 -skip-compilation -extract-uints -add-include -add-include -add-include "kremlin/internal/types.h" -bundle FStar.UInt128=* extracted/prims.krml extracted/FStar_Pervasives_Native.krml extracted/FStar_Pervasives.krml extracted/FStar_Mul.krml extracted/FStar_Squash.krml extracted/FStar_Classical.krml extracted/FStar_StrongExcludedMiddle.krml extracted/FStar_FunctionalExtensionality.krml extracted/FStar_List_Tot_Base.krml extracted/FStar_List_Tot_Properties.krml extracted/FStar_List_Tot.krml extracted/FStar_Seq_Base.krml extracted/FStar_Seq_Properties.krml extracted/FStar_Seq.krml extracted/FStar_Math_Lib.krml extracted/FStar_Math_Lemmas.krml extracted/FStar_BitVector.krml extracted/FStar_UInt.krml extracted/FStar_UInt32.krml extracted/FStar_Int.krml extracted/FStar_Int16.krml extracted/FStar_Preorder.krml extracted/FStar_Ghost.krml extracted/FStar_ErasedLogic.krml extracted/FStar_UInt64.krml extracted/FStar_Set.krml extracted/FStar_PropositionalExtensionality.krml extracted/FStar_PredicateExtensionality.krml extracted/FStar_TSet.krml extracted/FStar_Monotonic_Heap.krml extracted/FStar_Heap.krml extracted/FStar_Map.krml extracted/FStar_Monotonic_HyperHeap.krml extracted/FStar_Monotonic_HyperStack.krml extracted/FStar_HyperStack.krml extracted/FStar_Monotonic_Witnessed.krml extracted/FStar_HyperStack_ST.krml extracted/FStar_HyperStack_All.krml extracted/FStar_Date.krml extracted/FStar_Universe.krml extracted/FStar_GSet.krml extracted/FStar_ModifiesGen.krml extracted/LowStar_Monotonic_Buffer.krml extracted/LowStar_Buffer.krml extracted/Spec_Loops.krml extracted/LowStar_BufferOps.krml extracted/C_Loops.krml extracted/FStar_UInt8.krml extracted/FStar_Kremlin_Endianness.krml extracted/FStar_UInt63.krml extracted/FStar_Exn.krml extracted/FStar_ST.krml extracted/FStar_All.krml extracted/FStar_Dyn.krml extracted/FStar_Int63.krml extracted/FStar_Int64.krml extracted/FStar_Int32.krml extracted/FStar_Int8.krml extracted/FStar_UInt16.krml extracted/FStar_Int_Cast.krml extracted/FStar_UInt128.krml extracted/C_Endianness.krml extracted/FStar_List.krml extracted/FStar_Float.krml extracted/FStar_IO.krml extracted/C.krml extracted/FStar_Char.krml extracted/FStar_String.krml extracted/LowStar_Modifies.krml extracted/C_String.krml extracted/FStar_Bytes.krml extracted/FStar_HyperStack_IO.krml extracted/C_Failure.krml extracted/TestLib.krml extracted/FStar_Int_Cast_Full.krml + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + + +#ifndef __FStar_UInt128_H +#define __FStar_UInt128_H + + +#include +#include +#include "kremlin/internal/types.h" + +uint64_t FStar_UInt128___proj__Mkuint128__item__low(FStar_UInt128_uint128 projectee); + +uint64_t FStar_UInt128___proj__Mkuint128__item__high(FStar_UInt128_uint128 projectee); + +typedef FStar_UInt128_uint128 FStar_UInt128_t; + +FStar_UInt128_uint128 FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 +FStar_UInt128_add_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 +FStar_UInt128_sub_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a); + +FStar_UInt128_uint128 FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s); + +FStar_UInt128_uint128 FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s); + +bool FStar_UInt128_eq(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +bool FStar_UInt128_gt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +bool FStar_UInt128_lt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +bool FStar_UInt128_gte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +bool FStar_UInt128_lte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b); + +FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a); + +uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Plus_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Plus_Question_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Plus_Percent_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Subtraction_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Subtraction_Question_Hat)( + FStar_UInt128_uint128 x0, + FStar_UInt128_uint128 x1 +); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Subtraction_Percent_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Amp_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Hat_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Bar_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Less_Less_Hat)(FStar_UInt128_uint128 x0, uint32_t x1); + +extern FStar_UInt128_uint128 +(*FStar_UInt128_op_Greater_Greater_Hat)(FStar_UInt128_uint128 x0, uint32_t x1); + +extern bool (*FStar_UInt128_op_Equals_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern bool +(*FStar_UInt128_op_Greater_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern bool (*FStar_UInt128_op_Less_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern bool +(*FStar_UInt128_op_Greater_Equals_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern bool +(*FStar_UInt128_op_Less_Equals_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +FStar_UInt128_uint128 FStar_UInt128_mul32(uint64_t x, uint32_t y); + +FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y); + +#define __FStar_UInt128_H_DEFINED +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib/FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib/FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.h new file mode 100644 index 00000000..21560c4a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib/FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.h @@ -0,0 +1,280 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: ../krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrB9w -minimal -fparentheses -fcurly-braces -fno-shadow -header copyright-header.txt -minimal -tmpdir dist/minimal -skip-compilation -extract-uints -add-include -add-include -add-include "kremlin/internal/compat.h" -add-include "kremlin/internal/types.h" -bundle FStar.UInt64+FStar.UInt32+FStar.UInt16+FStar.UInt8=* extracted/prims.krml extracted/FStar_Pervasives_Native.krml extracted/FStar_Pervasives.krml extracted/FStar_Mul.krml extracted/FStar_Squash.krml extracted/FStar_Classical.krml extracted/FStar_StrongExcludedMiddle.krml extracted/FStar_FunctionalExtensionality.krml extracted/FStar_List_Tot_Base.krml extracted/FStar_List_Tot_Properties.krml extracted/FStar_List_Tot.krml extracted/FStar_Seq_Base.krml extracted/FStar_Seq_Properties.krml extracted/FStar_Seq.krml extracted/FStar_Math_Lib.krml extracted/FStar_Math_Lemmas.krml extracted/FStar_BitVector.krml extracted/FStar_UInt.krml extracted/FStar_UInt32.krml extracted/FStar_Int.krml extracted/FStar_Int16.krml extracted/FStar_Preorder.krml extracted/FStar_Ghost.krml extracted/FStar_ErasedLogic.krml extracted/FStar_UInt64.krml extracted/FStar_Set.krml extracted/FStar_PropositionalExtensionality.krml extracted/FStar_PredicateExtensionality.krml extracted/FStar_TSet.krml extracted/FStar_Monotonic_Heap.krml extracted/FStar_Heap.krml extracted/FStar_Map.krml extracted/FStar_Monotonic_HyperHeap.krml extracted/FStar_Monotonic_HyperStack.krml extracted/FStar_HyperStack.krml extracted/FStar_Monotonic_Witnessed.krml extracted/FStar_HyperStack_ST.krml extracted/FStar_HyperStack_All.krml extracted/FStar_Date.krml extracted/FStar_Universe.krml extracted/FStar_GSet.krml extracted/FStar_ModifiesGen.krml extracted/LowStar_Monotonic_Buffer.krml extracted/LowStar_Buffer.krml extracted/Spec_Loops.krml extracted/LowStar_BufferOps.krml extracted/C_Loops.krml extracted/FStar_UInt8.krml extracted/FStar_Kremlin_Endianness.krml extracted/FStar_UInt63.krml extracted/FStar_Exn.krml extracted/FStar_ST.krml extracted/FStar_All.krml extracted/FStar_Dyn.krml extracted/FStar_Int63.krml extracted/FStar_Int64.krml extracted/FStar_Int32.krml extracted/FStar_Int8.krml extracted/FStar_UInt16.krml extracted/FStar_Int_Cast.krml extracted/FStar_UInt128.krml extracted/C_Endianness.krml extracted/FStar_List.krml extracted/FStar_Float.krml extracted/FStar_IO.krml extracted/C.krml extracted/FStar_Char.krml extracted/FStar_String.krml extracted/LowStar_Modifies.krml extracted/C_String.krml extracted/FStar_Bytes.krml extracted/FStar_HyperStack_IO.krml extracted/C_Failure.krml extracted/TestLib.krml extracted/FStar_Int_Cast_Full.krml + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + + +#ifndef __FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8_H +#define __FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8_H + + +#include +#include +#include "kremlin/internal/compat.h" +#include "kremlin/internal/types.h" + +extern Prims_int FStar_UInt64_n; + +extern Prims_int FStar_UInt64_v(uint64_t x0); + +extern uint64_t FStar_UInt64_uint_to_t(Prims_int x0); + +extern uint64_t FStar_UInt64_add(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_add_underspec(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_add_mod(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_sub(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_sub_underspec(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_sub_mod(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_mul(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_mul_underspec(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_mul_mod(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_mul_div(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_div(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_rem(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_logand(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_logxor(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_logor(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_lognot(uint64_t x0); + +extern uint64_t FStar_UInt64_shift_right(uint64_t x0, uint32_t x1); + +extern uint64_t FStar_UInt64_shift_left(uint64_t x0, uint32_t x1); + +extern bool FStar_UInt64_eq(uint64_t x0, uint64_t x1); + +extern bool FStar_UInt64_gt(uint64_t x0, uint64_t x1); + +extern bool FStar_UInt64_gte(uint64_t x0, uint64_t x1); + +extern bool FStar_UInt64_lt(uint64_t x0, uint64_t x1); + +extern bool FStar_UInt64_lte(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_minus(uint64_t x0); + +extern uint32_t FStar_UInt64_n_minus_one; + +uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b); + +uint64_t FStar_UInt64_gte_mask(uint64_t a, uint64_t b); + +extern Prims_string FStar_UInt64_to_string(uint64_t x0); + +extern uint64_t FStar_UInt64_of_string(Prims_string x0); + +extern Prims_int FStar_UInt32_n; + +extern Prims_int FStar_UInt32_v(uint32_t x0); + +extern uint32_t FStar_UInt32_uint_to_t(Prims_int x0); + +extern uint32_t FStar_UInt32_add(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_add_underspec(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_add_mod(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_sub(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_sub_underspec(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_sub_mod(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_mul(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_mul_underspec(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_mul_mod(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_mul_div(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_div(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_rem(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_logand(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_logxor(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_logor(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_lognot(uint32_t x0); + +extern uint32_t FStar_UInt32_shift_right(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_shift_left(uint32_t x0, uint32_t x1); + +extern bool FStar_UInt32_eq(uint32_t x0, uint32_t x1); + +extern bool FStar_UInt32_gt(uint32_t x0, uint32_t x1); + +extern bool FStar_UInt32_gte(uint32_t x0, uint32_t x1); + +extern bool FStar_UInt32_lt(uint32_t x0, uint32_t x1); + +extern bool FStar_UInt32_lte(uint32_t x0, uint32_t x1); + +extern uint32_t FStar_UInt32_minus(uint32_t x0); + +extern uint32_t FStar_UInt32_n_minus_one; + +uint32_t FStar_UInt32_eq_mask(uint32_t a, uint32_t b); + +uint32_t FStar_UInt32_gte_mask(uint32_t a, uint32_t b); + +extern Prims_string FStar_UInt32_to_string(uint32_t x0); + +extern uint32_t FStar_UInt32_of_string(Prims_string x0); + +extern Prims_int FStar_UInt16_n; + +extern Prims_int FStar_UInt16_v(uint16_t x0); + +extern uint16_t FStar_UInt16_uint_to_t(Prims_int x0); + +extern uint16_t FStar_UInt16_add(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_add_underspec(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_add_mod(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_sub(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_sub_underspec(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_sub_mod(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_mul(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_mul_underspec(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_mul_mod(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_mul_div(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_div(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_rem(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_logand(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_logxor(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_logor(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_lognot(uint16_t x0); + +extern uint16_t FStar_UInt16_shift_right(uint16_t x0, uint32_t x1); + +extern uint16_t FStar_UInt16_shift_left(uint16_t x0, uint32_t x1); + +extern bool FStar_UInt16_eq(uint16_t x0, uint16_t x1); + +extern bool FStar_UInt16_gt(uint16_t x0, uint16_t x1); + +extern bool FStar_UInt16_gte(uint16_t x0, uint16_t x1); + +extern bool FStar_UInt16_lt(uint16_t x0, uint16_t x1); + +extern bool FStar_UInt16_lte(uint16_t x0, uint16_t x1); + +extern uint16_t FStar_UInt16_minus(uint16_t x0); + +extern uint32_t FStar_UInt16_n_minus_one; + +uint16_t FStar_UInt16_eq_mask(uint16_t a, uint16_t b); + +uint16_t FStar_UInt16_gte_mask(uint16_t a, uint16_t b); + +extern Prims_string FStar_UInt16_to_string(uint16_t x0); + +extern uint16_t FStar_UInt16_of_string(Prims_string x0); + +extern Prims_int FStar_UInt8_n; + +extern Prims_int FStar_UInt8_v(uint8_t x0); + +extern uint8_t FStar_UInt8_uint_to_t(Prims_int x0); + +extern uint8_t FStar_UInt8_add(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_add_underspec(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_add_mod(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_sub(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_sub_underspec(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_sub_mod(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_mul(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_mul_underspec(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_mul_mod(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_mul_div(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_div(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_rem(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_logand(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_logxor(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_logor(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_lognot(uint8_t x0); + +extern uint8_t FStar_UInt8_shift_right(uint8_t x0, uint32_t x1); + +extern uint8_t FStar_UInt8_shift_left(uint8_t x0, uint32_t x1); + +extern bool FStar_UInt8_eq(uint8_t x0, uint8_t x1); + +extern bool FStar_UInt8_gt(uint8_t x0, uint8_t x1); + +extern bool FStar_UInt8_gte(uint8_t x0, uint8_t x1); + +extern bool FStar_UInt8_lt(uint8_t x0, uint8_t x1); + +extern bool FStar_UInt8_lte(uint8_t x0, uint8_t x1); + +extern uint8_t FStar_UInt8_minus(uint8_t x0); + +extern uint32_t FStar_UInt8_n_minus_one; + +uint8_t FStar_UInt8_eq_mask(uint8_t a, uint8_t b); + +uint8_t FStar_UInt8_gte_mask(uint8_t a, uint8_t b); + +extern Prims_string FStar_UInt8_to_string(uint8_t x0); + +extern uint8_t FStar_UInt8_of_string(Prims_string x0); + +typedef uint8_t FStar_UInt8_byte; + +#define __FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8_H_DEFINED +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/c_endianness.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/c_endianness.h new file mode 100644 index 00000000..5cfde5d9 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/c_endianness.h @@ -0,0 +1,204 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __KREMLIN_ENDIAN_H +#define __KREMLIN_ENDIAN_H + +#include +#include + +/******************************************************************************/ +/* Implementing C.fst (part 2: endian-ness macros) */ +/******************************************************************************/ + +/* ... for Linux */ +#if defined(__linux__) || defined(__CYGWIN__) +# include + +/* ... for OSX */ +#elif defined(__APPLE__) +# include +# define htole64(x) OSSwapHostToLittleInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) +# define htobe64(x) OSSwapHostToBigInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) + +# define htole16(x) OSSwapHostToLittleInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) +# define htobe16(x) OSSwapHostToBigInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) + +# define htole32(x) OSSwapHostToLittleInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) +# define htobe32(x) OSSwapHostToBigInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) + +/* ... for Solaris */ +#elif defined(__sun__) +# include +# define htole64(x) LE_64(x) +# define le64toh(x) LE_64(x) +# define htobe64(x) BE_64(x) +# define be64toh(x) BE_64(x) + +# define htole16(x) LE_16(x) +# define le16toh(x) LE_16(x) +# define htobe16(x) BE_16(x) +# define be16toh(x) BE_16(x) + +# define htole32(x) LE_32(x) +# define le32toh(x) LE_32(x) +# define htobe32(x) BE_32(x) +# define be32toh(x) BE_32(x) + +/* ... for the BSDs */ +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +# include +#elif defined(__OpenBSD__) +# include + +/* ... for Windows (MSVC)... not targeting XBOX 360! */ +#elif defined(_MSC_VER) + +# include +# define htobe16(x) _byteswap_ushort(x) +# define htole16(x) (x) +# define be16toh(x) _byteswap_ushort(x) +# define le16toh(x) (x) + +# define htobe32(x) _byteswap_ulong(x) +# define htole32(x) (x) +# define be32toh(x) _byteswap_ulong(x) +# define le32toh(x) (x) + +# define htobe64(x) _byteswap_uint64(x) +# define htole64(x) (x) +# define be64toh(x) _byteswap_uint64(x) +# define le64toh(x) (x) + +/* ... for Windows (GCC-like, e.g. mingw or clang) */ +#elif (defined(_WIN32) || defined(_WIN64)) && \ + (defined(__GNUC__) || defined(__clang__)) + +# define htobe16(x) __builtin_bswap16(x) +# define htole16(x) (x) +# define be16toh(x) __builtin_bswap16(x) +# define le16toh(x) (x) + +# define htobe32(x) __builtin_bswap32(x) +# define htole32(x) (x) +# define be32toh(x) __builtin_bswap32(x) +# define le32toh(x) (x) + +# define htobe64(x) __builtin_bswap64(x) +# define htole64(x) (x) +# define be64toh(x) __builtin_bswap64(x) +# define le64toh(x) (x) + +/* ... generic big-endian fallback code */ +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + +/* byte swapping code inspired by: + * https://github.com/rweather/arduinolibs/blob/master/libraries/Crypto/utility/EndianUtil.h + * */ + +# define htobe32(x) (x) +# define be32toh(x) (x) +# define htole32(x) \ + (__extension__({ \ + uint32_t _temp = (x); \ + ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ + ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ + })) +# define le32toh(x) (htole32((x))) + +# define htobe64(x) (x) +# define be64toh(x) (x) +# define htole64(x) \ + (__extension__({ \ + uint64_t __temp = (x); \ + uint32_t __low = htobe32((uint32_t)__temp); \ + uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ + (((uint64_t)__low) << 32) | __high; \ + })) +# define le64toh(x) (htole64((x))) + +/* ... generic little-endian fallback code */ +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + +# define htole32(x) (x) +# define le32toh(x) (x) +# define htobe32(x) \ + (__extension__({ \ + uint32_t _temp = (x); \ + ((_temp >> 24) & 0x000000FF) | ((_temp >> 8) & 0x0000FF00) | \ + ((_temp << 8) & 0x00FF0000) | ((_temp << 24) & 0xFF000000); \ + })) +# define be32toh(x) (htobe32((x))) + +# define htole64(x) (x) +# define le64toh(x) (x) +# define htobe64(x) \ + (__extension__({ \ + uint64_t __temp = (x); \ + uint32_t __low = htobe32((uint32_t)__temp); \ + uint32_t __high = htobe32((uint32_t)(__temp >> 32)); \ + (((uint64_t)__low) << 32) | __high; \ + })) +# define be64toh(x) (htobe64((x))) + +/* ... couldn't determine endian-ness of the target platform */ +#else +# error "Please define __BYTE_ORDER__!" + +#endif /* defined(__linux__) || ... */ + +/* Loads and stores. These avoid undefined behavior due to unaligned memory + * accesses, via memcpy. */ + +inline static uint16_t load16(uint8_t *b) { + uint16_t x; + memcpy(&x, b, 2); + return x; +} + +inline static uint32_t load32(uint8_t *b) { + uint32_t x; + memcpy(&x, b, 4); + return x; +} + +inline static uint64_t load64(uint8_t *b) { + uint64_t x; + memcpy(&x, b, 8); + return x; +} + +inline static void store16(uint8_t *b, uint16_t i) { + memcpy(b, &i, 2); +} + +inline static void store32(uint8_t *b, uint32_t i) { + memcpy(b, &i, 4); +} + +inline static void store64(uint8_t *b, uint64_t i) { + memcpy(b, &i, 8); +} + +#define load16_le(b) (le16toh(load16(b))) +#define store16_le(b, i) (store16(b, htole16(i))) +#define load16_be(b) (be16toh(load16(b))) +#define store16_be(b, i) (store16(b, htobe16(i))) + +#define load32_le(b) (le32toh(load32(b))) +#define store32_le(b, i) (store32(b, htole32(i))) +#define load32_be(b) (be32toh(load32(b))) +#define store32_be(b, i) (store32(b, htobe32(i))) + +#define load64_le(b) (le64toh(load64(b))) +#define store64_le(b, i) (store64(b, htole64(i))) +#define load64_be(b) (be64toh(load64(b))) +#define store64_be(b, i) (store64(b, htobe64(i))) + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/builtin.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/builtin.h new file mode 100644 index 00000000..219b2668 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/builtin.h @@ -0,0 +1,16 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __KREMLIN_BUILTIN_H +#define __KREMLIN_BUILTIN_H + +/* For alloca, when using KreMLin's -falloca */ +#if (defined(_WIN32) || defined(_WIN64)) +# include +#endif + +/* If some globals need to be initialized before the main, then kremlin will + * generate and try to link last a function with this type: */ +void kremlinit_globals(void); + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/callconv.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/callconv.h new file mode 100644 index 00000000..bf631ff4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/callconv.h @@ -0,0 +1,46 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __KREMLIN_CALLCONV_H +#define __KREMLIN_CALLCONV_H + +/******************************************************************************/ +/* Some macros to ease compatibility */ +/******************************************************************************/ + +/* We want to generate __cdecl safely without worrying about it being undefined. + * When using MSVC, these are always defined. When using MinGW, these are + * defined too. They have no meaning for other platforms, so we define them to + * be empty macros in other situations. */ +#ifndef _MSC_VER +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __stdcall +#define __stdcall +#endif +#ifndef __fastcall +#define __fastcall +#endif +#endif + +/* Since KreMLin emits the inline keyword unconditionally, we follow the + * guidelines at https://gcc.gnu.org/onlinedocs/gcc/Inline.html and make this + * __inline__ to ensure the code compiles with -std=c90 and earlier. */ +#ifdef __GNUC__ +# define inline __inline__ +#endif + +/* GCC-specific attribute syntax; everyone else gets the standard C inline + * attribute. */ +#ifdef __GNU_C__ +# ifndef __clang__ +# define force_inline inline __attribute__((always_inline)) +# else +# define force_inline inline +# endif +#else +# define force_inline inline +#endif + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/compat.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/compat.h new file mode 100644 index 00000000..a5b8889d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/compat.h @@ -0,0 +1,34 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef KRML_COMPAT_H +#define KRML_COMPAT_H + +#include + +/* A series of macros that define C implementations of types that are not Low*, + * to facilitate porting programs to Low*. */ + +typedef const char *Prims_string; + +typedef struct { + uint32_t length; + const char *data; +} FStar_Bytes_bytes; + +typedef int32_t Prims_pos, Prims_nat, Prims_nonzero, Prims_int, + krml_checked_int_t; + +#define RETURN_OR(x) \ + do { \ + int64_t __ret = x; \ + if (__ret < INT32_MIN || INT32_MAX < __ret) { \ + KRML_HOST_PRINTF( \ + "Prims.{int,nat,pos} integer overflow at %s:%d\n", __FILE__, \ + __LINE__); \ + KRML_HOST_EXIT(252); \ + } \ + return (int32_t)__ret; \ + } while (0) + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/debug.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/debug.h new file mode 100644 index 00000000..44ac22cd --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/debug.h @@ -0,0 +1,57 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __KREMLIN_DEBUG_H +#define __KREMLIN_DEBUG_H + +#include + +#include "kremlin/internal/target.h" + +/******************************************************************************/ +/* Debugging helpers - intended only for KreMLin developers */ +/******************************************************************************/ + +/* In support of "-wasm -d force-c": we might need this function to be + * forward-declared, because the dependency on WasmSupport appears very late, + * after SimplifyWasm, and sadly, after the topological order has been done. */ +void WasmSupport_check_buffer_size(uint32_t s); + +/* A series of GCC atrocities to trace function calls (kremlin's [-d c-calls] + * option). Useful when trying to debug, say, Wasm, to compare traces. */ +/* clang-format off */ +#ifdef __GNUC__ +#define KRML_FORMAT(X) _Generic((X), \ + uint8_t : "0x%08" PRIx8, \ + uint16_t: "0x%08" PRIx16, \ + uint32_t: "0x%08" PRIx32, \ + uint64_t: "0x%08" PRIx64, \ + int8_t : "0x%08" PRIx8, \ + int16_t : "0x%08" PRIx16, \ + int32_t : "0x%08" PRIx32, \ + int64_t : "0x%08" PRIx64, \ + default : "%s") + +#define KRML_FORMAT_ARG(X) _Generic((X), \ + uint8_t : X, \ + uint16_t: X, \ + uint32_t: X, \ + uint64_t: X, \ + int8_t : X, \ + int16_t : X, \ + int32_t : X, \ + int64_t : X, \ + default : "unknown") +/* clang-format on */ + +# define KRML_DEBUG_RETURN(X) \ + ({ \ + __auto_type _ret = (X); \ + KRML_HOST_PRINTF("returning: "); \ + KRML_HOST_PRINTF(KRML_FORMAT(_ret), KRML_FORMAT_ARG(_ret)); \ + KRML_HOST_PRINTF(" \n"); \ + _ret; \ + }) +#endif + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/target.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/target.h new file mode 100644 index 00000000..b552f52b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/target.h @@ -0,0 +1,102 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef __KREMLIN_TARGET_H +#define __KREMLIN_TARGET_H + +#include +#include +#include +#include +#include + +#include "kremlin/internal/callconv.h" + +/******************************************************************************/ +/* Macros that KreMLin will generate. */ +/******************************************************************************/ + +/* For "bare" targets that do not have a C stdlib, the user might want to use + * [-add-early-include '"mydefinitions.h"'] and override these. */ +#ifndef KRML_HOST_PRINTF +# define KRML_HOST_PRINTF printf +#endif + +#if ( \ + (defined __STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + (!(defined KRML_HOST_EPRINTF))) +# define KRML_HOST_EPRINTF(...) fprintf(stderr, __VA_ARGS__) +#endif + +#ifndef KRML_HOST_EXIT +# define KRML_HOST_EXIT exit +#endif + +#ifndef KRML_HOST_MALLOC +# define KRML_HOST_MALLOC malloc +#endif + +#ifndef KRML_HOST_CALLOC +# define KRML_HOST_CALLOC calloc +#endif + +#ifndef KRML_HOST_FREE +# define KRML_HOST_FREE free +#endif + +#ifndef KRML_HOST_TIME + +# include + +/* Prims_nat not yet in scope */ +inline static int32_t krml_time() { + return (int32_t)time(NULL); +} + +# define KRML_HOST_TIME krml_time +#endif + +/* In statement position, exiting is easy. */ +#define KRML_EXIT \ + do { \ + KRML_HOST_PRINTF("Unimplemented function at %s:%d\n", __FILE__, __LINE__); \ + KRML_HOST_EXIT(254); \ + } while (0) + +/* In expression position, use the comma-operator and a malloc to return an + * expression of the right size. KreMLin passes t as the parameter to the macro. + */ +#define KRML_EABORT(t, msg) \ + (KRML_HOST_PRINTF("KreMLin abort at %s:%d\n%s\n", __FILE__, __LINE__, msg), \ + KRML_HOST_EXIT(255), *((t *)KRML_HOST_MALLOC(sizeof(t)))) + +/* In FStar.Buffer.fst, the size of arrays is uint32_t, but it's a number of + * *elements*. Do an ugly, run-time check (some of which KreMLin can eliminate). + */ + +#ifdef __GNUC__ +# define _KRML_CHECK_SIZE_PRAGMA \ + _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") +#else +# define _KRML_CHECK_SIZE_PRAGMA +#endif + +#define KRML_CHECK_SIZE(size_elt, sz) \ + do { \ + _KRML_CHECK_SIZE_PRAGMA \ + if (((size_t)(sz)) > ((size_t)(SIZE_MAX / (size_elt)))) { \ + KRML_HOST_PRINTF( \ + "Maximum allocatable size exceeded, aborting before overflow at " \ + "%s:%d\n", \ + __FILE__, __LINE__); \ + KRML_HOST_EXIT(253); \ + } \ + } while (0) + +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define KRML_HOST_SNPRINTF(buf, sz, fmt, arg) _snprintf_s(buf, sz, _TRUNCATE, fmt, arg) +#else +# define KRML_HOST_SNPRINTF(buf, sz, fmt, arg) snprintf(buf, sz, fmt, arg) +#endif + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/types.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/types.h new file mode 100644 index 00000000..b936f00d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/types.h @@ -0,0 +1,61 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +#ifndef KRML_TYPES_H +#define KRML_TYPES_H + +#include +#include +#include + +/* Types which are either abstract, meaning that have to be implemented in C, or + * which are models, meaning that they are swapped out at compile-time for + * hand-written C types (in which case they're marked as noextract). */ + +typedef uint64_t FStar_UInt64_t, FStar_UInt64_t_; +typedef int64_t FStar_Int64_t, FStar_Int64_t_; +typedef uint32_t FStar_UInt32_t, FStar_UInt32_t_; +typedef int32_t FStar_Int32_t, FStar_Int32_t_; +typedef uint16_t FStar_UInt16_t, FStar_UInt16_t_; +typedef int16_t FStar_Int16_t, FStar_Int16_t_; +typedef uint8_t FStar_UInt8_t, FStar_UInt8_t_; +typedef int8_t FStar_Int8_t, FStar_Int8_t_; + +/* Only useful when building Kremlib, because it's in the dependency graph of + * FStar.Int.Cast. */ +typedef uint64_t FStar_UInt63_t, FStar_UInt63_t_; +typedef int64_t FStar_Int63_t, FStar_Int63_t_; + +typedef double FStar_Float_float; +typedef uint32_t FStar_Char_char; +typedef FILE *FStar_IO_fd_read, *FStar_IO_fd_write; + +typedef void *FStar_Dyn_dyn; + +typedef const char *C_String_t, *C_String_t_; + +typedef int exit_code; +typedef FILE *channel; + +typedef unsigned long long TestLib_cycles; + +typedef uint64_t FStar_Date_dateTime, FStar_Date_timeSpan; + +/* The uint128 type is a special case since we offer several implementations of + * it, depending on the compiler and whether the user wants the verified + * implementation or not. */ +#if !defined(KRML_VERIFIED_UINT128) && defined(_MSC_VER) && defined(_M_X64) +# include +typedef __m128i FStar_UInt128_uint128; +#elif !defined(KRML_VERIFIED_UINT128) && !defined(_MSC_VER) +typedef unsigned __int128 FStar_UInt128_uint128; +#else +typedef struct FStar_UInt128_uint128_s { + uint64_t low; + uint64_t high; +} FStar_UInt128_uint128; +#endif + +typedef FStar_UInt128_uint128 FStar_UInt128_t, FStar_UInt128_t_, uint128_t; + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/wasmsupport.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/wasmsupport.h new file mode 100644 index 00000000..b44fa3f7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlin/internal/wasmsupport.h @@ -0,0 +1,5 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file is automatically included when compiling with -wasm -d force-c */ +#define WasmSupport_check_buffer_size(X) diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/Hacl_Curve25519.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/Hacl_Curve25519.h new file mode 100644 index 00000000..27ebe079 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/Hacl_Curve25519.h @@ -0,0 +1,21 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: /mnt/e/everest/verify/kremlin/krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -I /mnt/e/everest/verify/hacl-star/code/lib/kremlin -I /mnt/e/everest/verify/kremlin/kremlib/compat -I /mnt/e/everest/verify/hacl-star/specs -I /mnt/e/everest/verify/hacl-star/specs/old -I . -ccopt -march=native -verbose -ldopt -flto -tmpdir x25519-c -I ../bignum -bundle Hacl.Curve25519=* -minimal -add-include "kremlib.h" -skip-compilation x25519-c/out.krml -o x25519-c/Hacl_Curve25519.c + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + + +#ifndef __Hacl_Curve25519_H +#define __Hacl_Curve25519_H + + +#include "kremlib.h" + +void Hacl_Curve25519_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint); + +#define __Hacl_Curve25519_H_DEFINED +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/inttypes.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/inttypes.h new file mode 100644 index 00000000..d53f87f2 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/inttypes.h @@ -0,0 +1,36 @@ +/* + * Custom inttypes.h for VS2010 KreMLin requires these definitions, + * but VS2010 doesn't provide them. + * + * Copyright 2016-2018 INRIA and Microsoft Corporation + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef _INTTYPES_H_VS2010 +#define _INTTYPES_H_VS2010 + +#include + +#ifdef _MSC_VER +#define inline __inline +#endif + +/* VS2010 unsigned long == 8 bytes */ + +#define PRIu64 "I64u" + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/stdbool.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/stdbool.h new file mode 100644 index 00000000..5b7039c4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/vs2013/stdbool.h @@ -0,0 +1,31 @@ +/* + * Custom stdbool.h for VS2010 KreMLin requires these definitions, + * but VS2010 doesn't provide them. + * + * Copyright 2016-2018 INRIA and Microsoft Corporation + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef _STDBOOL_H_VS2010 +#define _STDBOOL_H_VS2010 + +typedef int bool; + +static bool true = 1; +static bool false = 0; + +#endif diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/x25519.h b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/x25519.h new file mode 100644 index 00000000..7a973dcf --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/include/everest/x25519.h @@ -0,0 +1,190 @@ +/* + * ECDH with curve-optimized implementation multiplexing + * + * Copyright 2016-2018 INRIA and Microsoft Corporation + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_X25519_H +#define MBEDTLS_X25519_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MBEDTLS_ECP_TLS_CURVE25519 0x1d +#define MBEDTLS_X25519_KEY_SIZE_BYTES 32 + +/** + * Defines the source of the imported EC key. + */ +typedef enum +{ + MBEDTLS_X25519_ECDH_OURS, /**< Our key. */ + MBEDTLS_X25519_ECDH_THEIRS, /**< The key of the peer. */ +} mbedtls_x25519_ecdh_side; + +/** + * \brief The x25519 context structure. + */ +typedef struct +{ + unsigned char our_secret[MBEDTLS_X25519_KEY_SIZE_BYTES]; + unsigned char peer_point[MBEDTLS_X25519_KEY_SIZE_BYTES]; +} mbedtls_x25519_context; + +/** + * \brief This function initializes an x25519 context. + * + * \param ctx The x25519 context to initialize. + */ +void mbedtls_x25519_init( mbedtls_x25519_context *ctx ); + +/** + * \brief This function frees a context. + * + * \param ctx The context to free. + */ +void mbedtls_x25519_free( mbedtls_x25519_context *ctx ); + +/** + * \brief This function generates a public key and a TLS + * ServerKeyExchange payload. + * + * This is the first function used by a TLS server for x25519. + * + * + * \param ctx The x25519 context. + * \param olen The number of characters written. + * \param buf The destination buffer. + * \param blen The length of the destination buffer. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_x25519_make_params( mbedtls_x25519_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function parses and processes a TLS ServerKeyExchange + * payload. + * + * + * \param ctx The x25519 context. + * \param buf The pointer to the start of the input buffer. + * \param end The address for one Byte past the end of the buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + * + */ +int mbedtls_x25519_read_params( mbedtls_x25519_context *ctx, + const unsigned char **buf, const unsigned char *end ); + +/** + * \brief This function sets up an x25519 context from an EC key. + * + * It is used by clients and servers in place of the + * ServerKeyEchange for static ECDH, and imports ECDH + * parameters from the EC key information of a certificate. + * + * \see ecp.h + * + * \param ctx The x25519 context to set up. + * \param key The EC key to use. + * \param side Defines the source of the key: 1: Our key, or + * 0: The key of the peer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + * + */ +int mbedtls_x25519_get_params( mbedtls_x25519_context *ctx, const mbedtls_ecp_keypair *key, + mbedtls_x25519_ecdh_side side ); + +/** + * \brief This function derives and exports the shared secret. + * + * This is the last function used by both TLS client + * and servers. + * + * + * \param ctx The x25519 context. + * \param olen The number of Bytes written. + * \param buf The destination buffer. + * \param blen The length of the destination buffer. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_x25519_calc_secret( mbedtls_x25519_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function generates a public key and a TLS + * ClientKeyExchange payload. + * + * This is the second function used by a TLS client for x25519. + * + * \see ecp.h + * + * \param ctx The x25519 context. + * \param olen The number of Bytes written. + * \param buf The destination buffer. + * \param blen The size of the destination buffer. + * \param f_rng The RNG function. + * \param p_rng The RNG context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_x25519_make_public( mbedtls_x25519_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )(void *, unsigned char *, size_t), + void *p_rng ); + +/** + * \brief This function parses and processes a TLS ClientKeyExchange + * payload. + * + * This is the second function used by a TLS server for x25519. + * + * \see ecp.h + * + * \param ctx The x25519 context. + * \param buf The start of the input buffer. + * \param blen The length of the input buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_x25519_read_public( mbedtls_x25519_context *ctx, + const unsigned char *buf, size_t blen ); + +#ifdef __cplusplus +} +#endif + +#endif /* x25519.h */ diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/library/Hacl_Curve25519.c b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/Hacl_Curve25519.c new file mode 100644 index 00000000..450b9f8d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/Hacl_Curve25519.c @@ -0,0 +1,760 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: /mnt/e/everest/verify/kremlin/krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -fbuiltin-uint128 -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -I /mnt/e/everest/verify/hacl-star/code/lib/kremlin -I /mnt/e/everest/verify/kremlin/kremlib/compat -I /mnt/e/everest/verify/hacl-star/specs -I /mnt/e/everest/verify/hacl-star/specs/old -I . -ccopt -march=native -verbose -ldopt -flto -tmpdir x25519-c -I ../bignum -bundle Hacl.Curve25519=* -minimal -add-include "kremlib.h" -skip-compilation x25519-c/out.krml -o x25519-c/Hacl_Curve25519.c + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + +#include "Hacl_Curve25519.h" + +extern uint64_t FStar_UInt64_eq_mask(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_gte_mask(uint64_t x0, uint64_t x1); + +extern uint128_t FStar_UInt128_add(uint128_t x0, uint128_t x1); + +extern uint128_t FStar_UInt128_add_mod(uint128_t x0, uint128_t x1); + +extern uint128_t FStar_UInt128_logand(uint128_t x0, uint128_t x1); + +extern uint128_t FStar_UInt128_shift_right(uint128_t x0, uint32_t x1); + +extern uint128_t FStar_UInt128_uint64_to_uint128(uint64_t x0); + +extern uint64_t FStar_UInt128_uint128_to_uint64(uint128_t x0); + +extern uint128_t FStar_UInt128_mul_wide(uint64_t x0, uint64_t x1); + +static void Hacl_Bignum_Modulo_carry_top(uint64_t *b) +{ + uint64_t b4 = b[4U]; + uint64_t b0 = b[0U]; + uint64_t b4_ = b4 & (uint64_t)0x7ffffffffffffU; + uint64_t b0_ = b0 + (uint64_t)19U * (b4 >> (uint32_t)51U); + b[4U] = b4_; + b[0U] = b0_; +} + +inline static void Hacl_Bignum_Fproduct_copy_from_wide_(uint64_t *output, uint128_t *input) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint128_t xi = input[i]; + output[i] = (uint64_t)xi; + } +} + +inline static void +Hacl_Bignum_Fproduct_sum_scalar_multiplication_(uint128_t *output, uint64_t *input, uint64_t s) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint128_t xi = output[i]; + uint64_t yi = input[i]; + output[i] = xi + (uint128_t)yi * s; + } +} + +inline static void Hacl_Bignum_Fproduct_carry_wide_(uint128_t *tmp) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)4U; i = i + (uint32_t)1U) + { + uint32_t ctr = i; + uint128_t tctr = tmp[ctr]; + uint128_t tctrp1 = tmp[ctr + (uint32_t)1U]; + uint64_t r0 = (uint64_t)tctr & (uint64_t)0x7ffffffffffffU; + uint128_t c = tctr >> (uint32_t)51U; + tmp[ctr] = (uint128_t)r0; + tmp[ctr + (uint32_t)1U] = tctrp1 + c; + } +} + +inline static void Hacl_Bignum_Fmul_shift_reduce(uint64_t *output) +{ + uint64_t tmp = output[4U]; + uint64_t b0; + { + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)4U; i = i + (uint32_t)1U) + { + uint32_t ctr = (uint32_t)5U - i - (uint32_t)1U; + uint64_t z = output[ctr - (uint32_t)1U]; + output[ctr] = z; + } + } + output[0U] = tmp; + b0 = output[0U]; + output[0U] = (uint64_t)19U * b0; +} + +static void +Hacl_Bignum_Fmul_mul_shift_reduce_(uint128_t *output, uint64_t *input, uint64_t *input2) +{ + uint32_t i; + uint64_t input2i; + { + uint32_t i0; + for (i0 = (uint32_t)0U; i0 < (uint32_t)4U; i0 = i0 + (uint32_t)1U) + { + uint64_t input2i0 = input2[i0]; + Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i0); + Hacl_Bignum_Fmul_shift_reduce(input); + } + } + i = (uint32_t)4U; + input2i = input2[i]; + Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i); +} + +inline static void Hacl_Bignum_Fmul_fmul(uint64_t *output, uint64_t *input, uint64_t *input2) +{ + uint64_t tmp[5U] = { 0U }; + memcpy(tmp, input, (uint32_t)5U * sizeof input[0U]); + KRML_CHECK_SIZE(sizeof (uint128_t), (uint32_t)5U); + { + uint128_t t[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + t[_i] = (uint128_t)(uint64_t)0U; + } + { + uint128_t b4; + uint128_t b0; + uint128_t b4_; + uint128_t b0_; + uint64_t i0; + uint64_t i1; + uint64_t i0_; + uint64_t i1_; + Hacl_Bignum_Fmul_mul_shift_reduce_(t, tmp, input2); + Hacl_Bignum_Fproduct_carry_wide_(t); + b4 = t[4U]; + b0 = t[0U]; + b4_ = b4 & (uint128_t)(uint64_t)0x7ffffffffffffU; + b0_ = b0 + (uint128_t)(uint64_t)19U * (uint64_t)(b4 >> (uint32_t)51U); + t[4U] = b4_; + t[0U] = b0_; + Hacl_Bignum_Fproduct_copy_from_wide_(output, t); + i0 = output[0U]; + i1 = output[1U]; + i0_ = i0 & (uint64_t)0x7ffffffffffffU; + i1_ = i1 + (i0 >> (uint32_t)51U); + output[0U] = i0_; + output[1U] = i1_; + } + } +} + +inline static void Hacl_Bignum_Fsquare_fsquare__(uint128_t *tmp, uint64_t *output) +{ + uint64_t r0 = output[0U]; + uint64_t r1 = output[1U]; + uint64_t r2 = output[2U]; + uint64_t r3 = output[3U]; + uint64_t r4 = output[4U]; + uint64_t d0 = r0 * (uint64_t)2U; + uint64_t d1 = r1 * (uint64_t)2U; + uint64_t d2 = r2 * (uint64_t)2U * (uint64_t)19U; + uint64_t d419 = r4 * (uint64_t)19U; + uint64_t d4 = d419 * (uint64_t)2U; + uint128_t s0 = (uint128_t)r0 * r0 + (uint128_t)d4 * r1 + (uint128_t)d2 * r3; + uint128_t s1 = (uint128_t)d0 * r1 + (uint128_t)d4 * r2 + (uint128_t)(r3 * (uint64_t)19U) * r3; + uint128_t s2 = (uint128_t)d0 * r2 + (uint128_t)r1 * r1 + (uint128_t)d4 * r3; + uint128_t s3 = (uint128_t)d0 * r3 + (uint128_t)d1 * r2 + (uint128_t)r4 * d419; + uint128_t s4 = (uint128_t)d0 * r4 + (uint128_t)d1 * r3 + (uint128_t)r2 * r2; + tmp[0U] = s0; + tmp[1U] = s1; + tmp[2U] = s2; + tmp[3U] = s3; + tmp[4U] = s4; +} + +inline static void Hacl_Bignum_Fsquare_fsquare_(uint128_t *tmp, uint64_t *output) +{ + uint128_t b4; + uint128_t b0; + uint128_t b4_; + uint128_t b0_; + uint64_t i0; + uint64_t i1; + uint64_t i0_; + uint64_t i1_; + Hacl_Bignum_Fsquare_fsquare__(tmp, output); + Hacl_Bignum_Fproduct_carry_wide_(tmp); + b4 = tmp[4U]; + b0 = tmp[0U]; + b4_ = b4 & (uint128_t)(uint64_t)0x7ffffffffffffU; + b0_ = b0 + (uint128_t)(uint64_t)19U * (uint64_t)(b4 >> (uint32_t)51U); + tmp[4U] = b4_; + tmp[0U] = b0_; + Hacl_Bignum_Fproduct_copy_from_wide_(output, tmp); + i0 = output[0U]; + i1 = output[1U]; + i0_ = i0 & (uint64_t)0x7ffffffffffffU; + i1_ = i1 + (i0 >> (uint32_t)51U); + output[0U] = i0_; + output[1U] = i1_; +} + +static void +Hacl_Bignum_Fsquare_fsquare_times_(uint64_t *input, uint128_t *tmp, uint32_t count1) +{ + uint32_t i; + Hacl_Bignum_Fsquare_fsquare_(tmp, input); + for (i = (uint32_t)1U; i < count1; i = i + (uint32_t)1U) + Hacl_Bignum_Fsquare_fsquare_(tmp, input); +} + +inline static void +Hacl_Bignum_Fsquare_fsquare_times(uint64_t *output, uint64_t *input, uint32_t count1) +{ + KRML_CHECK_SIZE(sizeof (uint128_t), (uint32_t)5U); + { + uint128_t t[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + t[_i] = (uint128_t)(uint64_t)0U; + } + memcpy(output, input, (uint32_t)5U * sizeof input[0U]); + Hacl_Bignum_Fsquare_fsquare_times_(output, t, count1); + } +} + +inline static void Hacl_Bignum_Fsquare_fsquare_times_inplace(uint64_t *output, uint32_t count1) +{ + KRML_CHECK_SIZE(sizeof (uint128_t), (uint32_t)5U); + { + uint128_t t[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + t[_i] = (uint128_t)(uint64_t)0U; + } + Hacl_Bignum_Fsquare_fsquare_times_(output, t, count1); + } +} + +inline static void Hacl_Bignum_Crecip_crecip(uint64_t *out, uint64_t *z) +{ + uint64_t buf[20U] = { 0U }; + uint64_t *a0 = buf; + uint64_t *t00 = buf + (uint32_t)5U; + uint64_t *b0 = buf + (uint32_t)10U; + uint64_t *t01; + uint64_t *b1; + uint64_t *c0; + uint64_t *a; + uint64_t *t0; + uint64_t *b; + uint64_t *c; + Hacl_Bignum_Fsquare_fsquare_times(a0, z, (uint32_t)1U); + Hacl_Bignum_Fsquare_fsquare_times(t00, a0, (uint32_t)2U); + Hacl_Bignum_Fmul_fmul(b0, t00, z); + Hacl_Bignum_Fmul_fmul(a0, b0, a0); + Hacl_Bignum_Fsquare_fsquare_times(t00, a0, (uint32_t)1U); + Hacl_Bignum_Fmul_fmul(b0, t00, b0); + Hacl_Bignum_Fsquare_fsquare_times(t00, b0, (uint32_t)5U); + t01 = buf + (uint32_t)5U; + b1 = buf + (uint32_t)10U; + c0 = buf + (uint32_t)15U; + Hacl_Bignum_Fmul_fmul(b1, t01, b1); + Hacl_Bignum_Fsquare_fsquare_times(t01, b1, (uint32_t)10U); + Hacl_Bignum_Fmul_fmul(c0, t01, b1); + Hacl_Bignum_Fsquare_fsquare_times(t01, c0, (uint32_t)20U); + Hacl_Bignum_Fmul_fmul(t01, t01, c0); + Hacl_Bignum_Fsquare_fsquare_times_inplace(t01, (uint32_t)10U); + Hacl_Bignum_Fmul_fmul(b1, t01, b1); + Hacl_Bignum_Fsquare_fsquare_times(t01, b1, (uint32_t)50U); + a = buf; + t0 = buf + (uint32_t)5U; + b = buf + (uint32_t)10U; + c = buf + (uint32_t)15U; + Hacl_Bignum_Fmul_fmul(c, t0, b); + Hacl_Bignum_Fsquare_fsquare_times(t0, c, (uint32_t)100U); + Hacl_Bignum_Fmul_fmul(t0, t0, c); + Hacl_Bignum_Fsquare_fsquare_times_inplace(t0, (uint32_t)50U); + Hacl_Bignum_Fmul_fmul(t0, t0, b); + Hacl_Bignum_Fsquare_fsquare_times_inplace(t0, (uint32_t)5U); + Hacl_Bignum_Fmul_fmul(out, t0, a); +} + +inline static void Hacl_Bignum_fsum(uint64_t *a, uint64_t *b) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint64_t xi = a[i]; + uint64_t yi = b[i]; + a[i] = xi + yi; + } +} + +inline static void Hacl_Bignum_fdifference(uint64_t *a, uint64_t *b) +{ + uint64_t tmp[5U] = { 0U }; + uint64_t b0; + uint64_t b1; + uint64_t b2; + uint64_t b3; + uint64_t b4; + memcpy(tmp, b, (uint32_t)5U * sizeof b[0U]); + b0 = tmp[0U]; + b1 = tmp[1U]; + b2 = tmp[2U]; + b3 = tmp[3U]; + b4 = tmp[4U]; + tmp[0U] = b0 + (uint64_t)0x3fffffffffff68U; + tmp[1U] = b1 + (uint64_t)0x3ffffffffffff8U; + tmp[2U] = b2 + (uint64_t)0x3ffffffffffff8U; + tmp[3U] = b3 + (uint64_t)0x3ffffffffffff8U; + tmp[4U] = b4 + (uint64_t)0x3ffffffffffff8U; + { + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint64_t xi = a[i]; + uint64_t yi = tmp[i]; + a[i] = yi - xi; + } + } +} + +inline static void Hacl_Bignum_fscalar(uint64_t *output, uint64_t *b, uint64_t s) +{ + KRML_CHECK_SIZE(sizeof (uint128_t), (uint32_t)5U); + { + uint128_t tmp[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + tmp[_i] = (uint128_t)(uint64_t)0U; + } + { + uint128_t b4; + uint128_t b0; + uint128_t b4_; + uint128_t b0_; + { + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint64_t xi = b[i]; + tmp[i] = (uint128_t)xi * s; + } + } + Hacl_Bignum_Fproduct_carry_wide_(tmp); + b4 = tmp[4U]; + b0 = tmp[0U]; + b4_ = b4 & (uint128_t)(uint64_t)0x7ffffffffffffU; + b0_ = b0 + (uint128_t)(uint64_t)19U * (uint64_t)(b4 >> (uint32_t)51U); + tmp[4U] = b4_; + tmp[0U] = b0_; + Hacl_Bignum_Fproduct_copy_from_wide_(output, tmp); + } + } +} + +inline static void Hacl_Bignum_fmul(uint64_t *output, uint64_t *a, uint64_t *b) +{ + Hacl_Bignum_Fmul_fmul(output, a, b); +} + +inline static void Hacl_Bignum_crecip(uint64_t *output, uint64_t *input) +{ + Hacl_Bignum_Crecip_crecip(output, input); +} + +static void +Hacl_EC_Point_swap_conditional_step(uint64_t *a, uint64_t *b, uint64_t swap1, uint32_t ctr) +{ + uint32_t i = ctr - (uint32_t)1U; + uint64_t ai = a[i]; + uint64_t bi = b[i]; + uint64_t x = swap1 & (ai ^ bi); + uint64_t ai1 = ai ^ x; + uint64_t bi1 = bi ^ x; + a[i] = ai1; + b[i] = bi1; +} + +static void +Hacl_EC_Point_swap_conditional_(uint64_t *a, uint64_t *b, uint64_t swap1, uint32_t ctr) +{ + if (!(ctr == (uint32_t)0U)) + { + uint32_t i; + Hacl_EC_Point_swap_conditional_step(a, b, swap1, ctr); + i = ctr - (uint32_t)1U; + Hacl_EC_Point_swap_conditional_(a, b, swap1, i); + } +} + +static void Hacl_EC_Point_swap_conditional(uint64_t *a, uint64_t *b, uint64_t iswap) +{ + uint64_t swap1 = (uint64_t)0U - iswap; + Hacl_EC_Point_swap_conditional_(a, b, swap1, (uint32_t)5U); + Hacl_EC_Point_swap_conditional_(a + (uint32_t)5U, b + (uint32_t)5U, swap1, (uint32_t)5U); +} + +static void Hacl_EC_Point_copy(uint64_t *output, uint64_t *input) +{ + memcpy(output, input, (uint32_t)5U * sizeof input[0U]); + memcpy(output + (uint32_t)5U, + input + (uint32_t)5U, + (uint32_t)5U * sizeof (input + (uint32_t)5U)[0U]); +} + +static void Hacl_EC_Format_fexpand(uint64_t *output, uint8_t *input) +{ + uint64_t i0 = load64_le(input); + uint8_t *x00 = input + (uint32_t)6U; + uint64_t i1 = load64_le(x00); + uint8_t *x01 = input + (uint32_t)12U; + uint64_t i2 = load64_le(x01); + uint8_t *x02 = input + (uint32_t)19U; + uint64_t i3 = load64_le(x02); + uint8_t *x0 = input + (uint32_t)24U; + uint64_t i4 = load64_le(x0); + uint64_t output0 = i0 & (uint64_t)0x7ffffffffffffU; + uint64_t output1 = i1 >> (uint32_t)3U & (uint64_t)0x7ffffffffffffU; + uint64_t output2 = i2 >> (uint32_t)6U & (uint64_t)0x7ffffffffffffU; + uint64_t output3 = i3 >> (uint32_t)1U & (uint64_t)0x7ffffffffffffU; + uint64_t output4 = i4 >> (uint32_t)12U & (uint64_t)0x7ffffffffffffU; + output[0U] = output0; + output[1U] = output1; + output[2U] = output2; + output[3U] = output3; + output[4U] = output4; +} + +static void Hacl_EC_Format_fcontract_first_carry_pass(uint64_t *input) +{ + uint64_t t0 = input[0U]; + uint64_t t1 = input[1U]; + uint64_t t2 = input[2U]; + uint64_t t3 = input[3U]; + uint64_t t4 = input[4U]; + uint64_t t1_ = t1 + (t0 >> (uint32_t)51U); + uint64_t t0_ = t0 & (uint64_t)0x7ffffffffffffU; + uint64_t t2_ = t2 + (t1_ >> (uint32_t)51U); + uint64_t t1__ = t1_ & (uint64_t)0x7ffffffffffffU; + uint64_t t3_ = t3 + (t2_ >> (uint32_t)51U); + uint64_t t2__ = t2_ & (uint64_t)0x7ffffffffffffU; + uint64_t t4_ = t4 + (t3_ >> (uint32_t)51U); + uint64_t t3__ = t3_ & (uint64_t)0x7ffffffffffffU; + input[0U] = t0_; + input[1U] = t1__; + input[2U] = t2__; + input[3U] = t3__; + input[4U] = t4_; +} + +static void Hacl_EC_Format_fcontract_first_carry_full(uint64_t *input) +{ + Hacl_EC_Format_fcontract_first_carry_pass(input); + Hacl_Bignum_Modulo_carry_top(input); +} + +static void Hacl_EC_Format_fcontract_second_carry_pass(uint64_t *input) +{ + uint64_t t0 = input[0U]; + uint64_t t1 = input[1U]; + uint64_t t2 = input[2U]; + uint64_t t3 = input[3U]; + uint64_t t4 = input[4U]; + uint64_t t1_ = t1 + (t0 >> (uint32_t)51U); + uint64_t t0_ = t0 & (uint64_t)0x7ffffffffffffU; + uint64_t t2_ = t2 + (t1_ >> (uint32_t)51U); + uint64_t t1__ = t1_ & (uint64_t)0x7ffffffffffffU; + uint64_t t3_ = t3 + (t2_ >> (uint32_t)51U); + uint64_t t2__ = t2_ & (uint64_t)0x7ffffffffffffU; + uint64_t t4_ = t4 + (t3_ >> (uint32_t)51U); + uint64_t t3__ = t3_ & (uint64_t)0x7ffffffffffffU; + input[0U] = t0_; + input[1U] = t1__; + input[2U] = t2__; + input[3U] = t3__; + input[4U] = t4_; +} + +static void Hacl_EC_Format_fcontract_second_carry_full(uint64_t *input) +{ + uint64_t i0; + uint64_t i1; + uint64_t i0_; + uint64_t i1_; + Hacl_EC_Format_fcontract_second_carry_pass(input); + Hacl_Bignum_Modulo_carry_top(input); + i0 = input[0U]; + i1 = input[1U]; + i0_ = i0 & (uint64_t)0x7ffffffffffffU; + i1_ = i1 + (i0 >> (uint32_t)51U); + input[0U] = i0_; + input[1U] = i1_; +} + +static void Hacl_EC_Format_fcontract_trim(uint64_t *input) +{ + uint64_t a0 = input[0U]; + uint64_t a1 = input[1U]; + uint64_t a2 = input[2U]; + uint64_t a3 = input[3U]; + uint64_t a4 = input[4U]; + uint64_t mask0 = FStar_UInt64_gte_mask(a0, (uint64_t)0x7ffffffffffedU); + uint64_t mask1 = FStar_UInt64_eq_mask(a1, (uint64_t)0x7ffffffffffffU); + uint64_t mask2 = FStar_UInt64_eq_mask(a2, (uint64_t)0x7ffffffffffffU); + uint64_t mask3 = FStar_UInt64_eq_mask(a3, (uint64_t)0x7ffffffffffffU); + uint64_t mask4 = FStar_UInt64_eq_mask(a4, (uint64_t)0x7ffffffffffffU); + uint64_t mask = (((mask0 & mask1) & mask2) & mask3) & mask4; + uint64_t a0_ = a0 - ((uint64_t)0x7ffffffffffedU & mask); + uint64_t a1_ = a1 - ((uint64_t)0x7ffffffffffffU & mask); + uint64_t a2_ = a2 - ((uint64_t)0x7ffffffffffffU & mask); + uint64_t a3_ = a3 - ((uint64_t)0x7ffffffffffffU & mask); + uint64_t a4_ = a4 - ((uint64_t)0x7ffffffffffffU & mask); + input[0U] = a0_; + input[1U] = a1_; + input[2U] = a2_; + input[3U] = a3_; + input[4U] = a4_; +} + +static void Hacl_EC_Format_fcontract_store(uint8_t *output, uint64_t *input) +{ + uint64_t t0 = input[0U]; + uint64_t t1 = input[1U]; + uint64_t t2 = input[2U]; + uint64_t t3 = input[3U]; + uint64_t t4 = input[4U]; + uint64_t o0 = t1 << (uint32_t)51U | t0; + uint64_t o1 = t2 << (uint32_t)38U | t1 >> (uint32_t)13U; + uint64_t o2 = t3 << (uint32_t)25U | t2 >> (uint32_t)26U; + uint64_t o3 = t4 << (uint32_t)12U | t3 >> (uint32_t)39U; + uint8_t *b0 = output; + uint8_t *b1 = output + (uint32_t)8U; + uint8_t *b2 = output + (uint32_t)16U; + uint8_t *b3 = output + (uint32_t)24U; + store64_le(b0, o0); + store64_le(b1, o1); + store64_le(b2, o2); + store64_le(b3, o3); +} + +static void Hacl_EC_Format_fcontract(uint8_t *output, uint64_t *input) +{ + Hacl_EC_Format_fcontract_first_carry_full(input); + Hacl_EC_Format_fcontract_second_carry_full(input); + Hacl_EC_Format_fcontract_trim(input); + Hacl_EC_Format_fcontract_store(output, input); +} + +static void Hacl_EC_Format_scalar_of_point(uint8_t *scalar, uint64_t *point) +{ + uint64_t *x = point; + uint64_t *z = point + (uint32_t)5U; + uint64_t buf[10U] = { 0U }; + uint64_t *zmone = buf; + uint64_t *sc = buf + (uint32_t)5U; + Hacl_Bignum_crecip(zmone, z); + Hacl_Bignum_fmul(sc, x, zmone); + Hacl_EC_Format_fcontract(scalar, sc); +} + +static void +Hacl_EC_AddAndDouble_fmonty( + uint64_t *pp, + uint64_t *ppq, + uint64_t *p, + uint64_t *pq, + uint64_t *qmqp +) +{ + uint64_t *qx = qmqp; + uint64_t *x2 = pp; + uint64_t *z2 = pp + (uint32_t)5U; + uint64_t *x3 = ppq; + uint64_t *z3 = ppq + (uint32_t)5U; + uint64_t *x = p; + uint64_t *z = p + (uint32_t)5U; + uint64_t *xprime = pq; + uint64_t *zprime = pq + (uint32_t)5U; + uint64_t buf[40U] = { 0U }; + uint64_t *origx = buf; + uint64_t *origxprime0 = buf + (uint32_t)5U; + uint64_t *xxprime0 = buf + (uint32_t)25U; + uint64_t *zzprime0 = buf + (uint32_t)30U; + uint64_t *origxprime; + uint64_t *xx0; + uint64_t *zz0; + uint64_t *xxprime; + uint64_t *zzprime; + uint64_t *zzzprime; + uint64_t *zzz; + uint64_t *xx; + uint64_t *zz; + uint64_t scalar; + memcpy(origx, x, (uint32_t)5U * sizeof x[0U]); + Hacl_Bignum_fsum(x, z); + Hacl_Bignum_fdifference(z, origx); + memcpy(origxprime0, xprime, (uint32_t)5U * sizeof xprime[0U]); + Hacl_Bignum_fsum(xprime, zprime); + Hacl_Bignum_fdifference(zprime, origxprime0); + Hacl_Bignum_fmul(xxprime0, xprime, z); + Hacl_Bignum_fmul(zzprime0, x, zprime); + origxprime = buf + (uint32_t)5U; + xx0 = buf + (uint32_t)15U; + zz0 = buf + (uint32_t)20U; + xxprime = buf + (uint32_t)25U; + zzprime = buf + (uint32_t)30U; + zzzprime = buf + (uint32_t)35U; + memcpy(origxprime, xxprime, (uint32_t)5U * sizeof xxprime[0U]); + Hacl_Bignum_fsum(xxprime, zzprime); + Hacl_Bignum_fdifference(zzprime, origxprime); + Hacl_Bignum_Fsquare_fsquare_times(x3, xxprime, (uint32_t)1U); + Hacl_Bignum_Fsquare_fsquare_times(zzzprime, zzprime, (uint32_t)1U); + Hacl_Bignum_fmul(z3, zzzprime, qx); + Hacl_Bignum_Fsquare_fsquare_times(xx0, x, (uint32_t)1U); + Hacl_Bignum_Fsquare_fsquare_times(zz0, z, (uint32_t)1U); + zzz = buf + (uint32_t)10U; + xx = buf + (uint32_t)15U; + zz = buf + (uint32_t)20U; + Hacl_Bignum_fmul(x2, xx, zz); + Hacl_Bignum_fdifference(zz, xx); + scalar = (uint64_t)121665U; + Hacl_Bignum_fscalar(zzz, zz, scalar); + Hacl_Bignum_fsum(zzz, xx); + Hacl_Bignum_fmul(z2, zzz, zz); +} + +static void +Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step( + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint8_t byt +) +{ + uint64_t bit0 = (uint64_t)(byt >> (uint32_t)7U); + uint64_t bit; + Hacl_EC_Point_swap_conditional(nq, nqpq, bit0); + Hacl_EC_AddAndDouble_fmonty(nq2, nqpq2, nq, nqpq, q); + bit = (uint64_t)(byt >> (uint32_t)7U); + Hacl_EC_Point_swap_conditional(nq2, nqpq2, bit); +} + +static void +Hacl_EC_Ladder_SmallLoop_cmult_small_loop_double_step( + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint8_t byt +) +{ + uint8_t byt1; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt); + byt1 = byt << (uint32_t)1U; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1); +} + +static void +Hacl_EC_Ladder_SmallLoop_cmult_small_loop( + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint8_t byt, + uint32_t i +) +{ + if (!(i == (uint32_t)0U)) + { + uint32_t i_ = i - (uint32_t)1U; + uint8_t byt_; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop_double_step(nq, nqpq, nq2, nqpq2, q, byt); + byt_ = byt << (uint32_t)2U; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q, byt_, i_); + } +} + +static void +Hacl_EC_Ladder_BigLoop_cmult_big_loop( + uint8_t *n1, + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint32_t i +) +{ + if (!(i == (uint32_t)0U)) + { + uint32_t i1 = i - (uint32_t)1U; + uint8_t byte = n1[i1]; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q, byte, (uint32_t)4U); + Hacl_EC_Ladder_BigLoop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, i1); + } +} + +static void Hacl_EC_Ladder_cmult(uint64_t *result, uint8_t *n1, uint64_t *q) +{ + uint64_t point_buf[40U] = { 0U }; + uint64_t *nq = point_buf; + uint64_t *nqpq = point_buf + (uint32_t)10U; + uint64_t *nq2 = point_buf + (uint32_t)20U; + uint64_t *nqpq2 = point_buf + (uint32_t)30U; + Hacl_EC_Point_copy(nqpq, q); + nq[0U] = (uint64_t)1U; + Hacl_EC_Ladder_BigLoop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, (uint32_t)32U); + Hacl_EC_Point_copy(result, nq); +} + +void Hacl_Curve25519_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint) +{ + uint64_t buf0[10U] = { 0U }; + uint64_t *x0 = buf0; + uint64_t *z = buf0 + (uint32_t)5U; + uint64_t *q; + Hacl_EC_Format_fexpand(x0, basepoint); + z[0U] = (uint64_t)1U; + q = buf0; + { + uint8_t e[32U] = { 0U }; + uint8_t e0; + uint8_t e31; + uint8_t e01; + uint8_t e311; + uint8_t e312; + uint8_t *scalar; + memcpy(e, secret, (uint32_t)32U * sizeof secret[0U]); + e0 = e[0U]; + e31 = e[31U]; + e01 = e0 & (uint8_t)248U; + e311 = e31 & (uint8_t)127U; + e312 = e311 | (uint8_t)64U; + e[0U] = e01; + e[31U] = e312; + scalar = e; + { + uint64_t buf[15U] = { 0U }; + uint64_t *nq = buf; + uint64_t *x = nq; + x[0U] = (uint64_t)1U; + Hacl_EC_Ladder_cmult(nq, scalar, q); + Hacl_EC_Format_scalar_of_point(mypublic, nq); + } + } +} + diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/library/Hacl_Curve25519_joined.c b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/Hacl_Curve25519_joined.c new file mode 100644 index 00000000..957294f6 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/Hacl_Curve25519_joined.c @@ -0,0 +1,50 @@ +/* + * Interface to code from Project Everest + * + * Copyright 2016-2018 INRIA and Microsoft Corporation + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#ifndef _BSD_SOURCE +/* Required to get htole64() from gcc/glibc's endian.h (older systems) + * when we compile with -std=c99 */ +#define _BSD_SOURCE +#endif +#ifndef _DEFAULT_SOURCE +/* (modern version of _BSD_SOURCE) */ +#define _DEFAULT_SOURCE +#endif + +#include "common.h" + +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + +#if defined(__SIZEOF_INT128__) && (__SIZEOF_INT128__ == 16) +#define MBEDTLS_HAVE_INT128 +#endif + +#if defined(MBEDTLS_HAVE_INT128) +#include "Hacl_Curve25519.c" +#else +#define KRML_VERIFIED_UINT128 +#include "kremlib/FStar_UInt128_extracted.c" +#include "legacy/Hacl_Curve25519.c" +#endif + +#include "kremlib/FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.c" + +#endif /* defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) */ + diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/library/everest.c b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/everest.c new file mode 100644 index 00000000..fefc6a2c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/everest.c @@ -0,0 +1,102 @@ +/* + * Interface to code from Project Everest + * + * Copyright 2016-2018 INRIA and Microsoft Corporation + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org). + */ + +#include "common.h" + +#include + +#include "mbedtls/ecdh.h" + +#include "everest/x25519.h" +#include "everest/everest.h" + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + +int mbedtls_everest_setup( mbedtls_ecdh_context_everest *ctx, int grp_id ) +{ + if( grp_id != MBEDTLS_ECP_DP_CURVE25519 ) + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + mbedtls_x25519_init( &ctx->ctx ); + return 0; +} + +void mbedtls_everest_free( mbedtls_ecdh_context_everest *ctx ) +{ + mbedtls_x25519_free( &ctx->ctx ); +} + +int mbedtls_everest_make_params( mbedtls_ecdh_context_everest *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )( void *, unsigned char *, size_t ), + void *p_rng ) +{ + mbedtls_x25519_context *x25519_ctx = &ctx->ctx; + return mbedtls_x25519_make_params( x25519_ctx, olen, buf, blen, f_rng, p_rng ); +} + +int mbedtls_everest_read_params( mbedtls_ecdh_context_everest *ctx, + const unsigned char **buf, + const unsigned char *end ) +{ + mbedtls_x25519_context *x25519_ctx = &ctx->ctx; + return mbedtls_x25519_read_params( x25519_ctx, buf, end ); +} + +int mbedtls_everest_get_params( mbedtls_ecdh_context_everest *ctx, + const mbedtls_ecp_keypair *key, + mbedtls_everest_ecdh_side side ) +{ + mbedtls_x25519_context *x25519_ctx = &ctx->ctx; + mbedtls_x25519_ecdh_side s = side == MBEDTLS_EVEREST_ECDH_OURS ? + MBEDTLS_X25519_ECDH_OURS : + MBEDTLS_X25519_ECDH_THEIRS; + return mbedtls_x25519_get_params( x25519_ctx, key, s ); +} + +int mbedtls_everest_make_public( mbedtls_ecdh_context_everest *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )( void *, unsigned char *, size_t ), + void *p_rng ) +{ + mbedtls_x25519_context *x25519_ctx = &ctx->ctx; + return mbedtls_x25519_make_public( x25519_ctx, olen, buf, blen, f_rng, p_rng ); +} + +int mbedtls_everest_read_public( mbedtls_ecdh_context_everest *ctx, + const unsigned char *buf, size_t blen ) +{ + mbedtls_x25519_context *x25519_ctx = &ctx->ctx; + return mbedtls_x25519_read_public ( x25519_ctx, buf, blen ); +} + +int mbedtls_everest_calc_secret( mbedtls_ecdh_context_everest *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )( void *, unsigned char *, size_t ), + void *p_rng ) +{ + mbedtls_x25519_context *x25519_ctx = &ctx->ctx; + return mbedtls_x25519_calc_secret( x25519_ctx, olen, buf, blen, f_rng, p_rng ); +} + +#endif /* MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED */ + diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/library/kremlib/FStar_UInt128_extracted.c b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/kremlib/FStar_UInt128_extracted.c new file mode 100644 index 00000000..1060515d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/kremlib/FStar_UInt128_extracted.c @@ -0,0 +1,413 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: ../krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrB9w -minimal -fparentheses -fcurly-braces -fno-shadow -header copyright-header.txt -minimal -tmpdir extracted -warn-error +9+11 -skip-compilation -extract-uints -add-include -add-include "kremlib.h" -add-include "kremlin/internal/compat.h" extracted/prims.krml extracted/FStar_Pervasives_Native.krml extracted/FStar_Pervasives.krml extracted/FStar_Mul.krml extracted/FStar_Squash.krml extracted/FStar_Classical.krml extracted/FStar_StrongExcludedMiddle.krml extracted/FStar_FunctionalExtensionality.krml extracted/FStar_List_Tot_Base.krml extracted/FStar_List_Tot_Properties.krml extracted/FStar_List_Tot.krml extracted/FStar_Seq_Base.krml extracted/FStar_Seq_Properties.krml extracted/FStar_Seq.krml extracted/FStar_Math_Lib.krml extracted/FStar_Math_Lemmas.krml extracted/FStar_BitVector.krml extracted/FStar_UInt.krml extracted/FStar_UInt32.krml extracted/FStar_Int.krml extracted/FStar_Int16.krml extracted/FStar_Preorder.krml extracted/FStar_Ghost.krml extracted/FStar_ErasedLogic.krml extracted/FStar_UInt64.krml extracted/FStar_Set.krml extracted/FStar_PropositionalExtensionality.krml extracted/FStar_PredicateExtensionality.krml extracted/FStar_TSet.krml extracted/FStar_Monotonic_Heap.krml extracted/FStar_Heap.krml extracted/FStar_Map.krml extracted/FStar_Monotonic_HyperHeap.krml extracted/FStar_Monotonic_HyperStack.krml extracted/FStar_HyperStack.krml extracted/FStar_Monotonic_Witnessed.krml extracted/FStar_HyperStack_ST.krml extracted/FStar_HyperStack_All.krml extracted/FStar_Date.krml extracted/FStar_Universe.krml extracted/FStar_GSet.krml extracted/FStar_ModifiesGen.krml extracted/LowStar_Monotonic_Buffer.krml extracted/LowStar_Buffer.krml extracted/Spec_Loops.krml extracted/LowStar_BufferOps.krml extracted/C_Loops.krml extracted/FStar_UInt8.krml extracted/FStar_Kremlin_Endianness.krml extracted/FStar_UInt63.krml extracted/FStar_Exn.krml extracted/FStar_ST.krml extracted/FStar_All.krml extracted/FStar_Dyn.krml extracted/FStar_Int63.krml extracted/FStar_Int64.krml extracted/FStar_Int32.krml extracted/FStar_Int8.krml extracted/FStar_UInt16.krml extracted/FStar_Int_Cast.krml extracted/FStar_UInt128.krml extracted/C_Endianness.krml extracted/FStar_List.krml extracted/FStar_Float.krml extracted/FStar_IO.krml extracted/C.krml extracted/FStar_Char.krml extracted/FStar_String.krml extracted/LowStar_Modifies.krml extracted/C_String.krml extracted/FStar_Bytes.krml extracted/FStar_HyperStack_IO.krml extracted/C_Failure.krml extracted/TestLib.krml extracted/FStar_Int_Cast_Full.krml + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + +#include "FStar_UInt128.h" +#include "kremlin/c_endianness.h" +#include "FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.h" + +uint64_t FStar_UInt128___proj__Mkuint128__item__low(FStar_UInt128_uint128 projectee) +{ + return projectee.low; +} + +uint64_t FStar_UInt128___proj__Mkuint128__item__high(FStar_UInt128_uint128 projectee) +{ + return projectee.high; +} + +static uint64_t FStar_UInt128_constant_time_carry(uint64_t a, uint64_t b) +{ + return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (uint32_t)63U; +} + +static uint64_t FStar_UInt128_carry(uint64_t a, uint64_t b) +{ + return FStar_UInt128_constant_time_carry(a, b); +} + +FStar_UInt128_uint128 FStar_UInt128_add(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = { a.low + b.low, a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low) }; + return flat; +} + +FStar_UInt128_uint128 +FStar_UInt128_add_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = { a.low + b.low, a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low) }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_add_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = { a.low + b.low, a.high + b.high + FStar_UInt128_carry(a.low + b.low, b.low) }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_sub(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = { a.low - b.low, a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low) }; + return flat; +} + +FStar_UInt128_uint128 +FStar_UInt128_sub_underspec(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = { a.low - b.low, a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low) }; + return flat; +} + +static FStar_UInt128_uint128 +FStar_UInt128_sub_mod_impl(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = { a.low - b.low, a.high - b.high - FStar_UInt128_carry(a.low, a.low - b.low) }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_sub_mod(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return FStar_UInt128_sub_mod_impl(a, b); +} + +FStar_UInt128_uint128 FStar_UInt128_logand(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 flat = { a.low & b.low, a.high & b.high }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_logxor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 flat = { a.low ^ b.low, a.high ^ b.high }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_logor(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 flat = { a.low | b.low, a.high | b.high }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_lognot(FStar_UInt128_uint128 a) +{ + FStar_UInt128_uint128 flat = { ~a.low, ~a.high }; + return flat; +} + +static uint32_t FStar_UInt128_u32_64 = (uint32_t)64U; + +static uint64_t FStar_UInt128_add_u64_shift_left(uint64_t hi, uint64_t lo, uint32_t s) +{ + return (hi << s) + (lo >> (FStar_UInt128_u32_64 - s)); +} + +static uint64_t FStar_UInt128_add_u64_shift_left_respec(uint64_t hi, uint64_t lo, uint32_t s) +{ + return FStar_UInt128_add_u64_shift_left(hi, lo, s); +} + +static FStar_UInt128_uint128 +FStar_UInt128_shift_left_small(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s == (uint32_t)0U) + { + return a; + } + else + { + FStar_UInt128_uint128 + flat = { a.low << s, FStar_UInt128_add_u64_shift_left_respec(a.high, a.low, s) }; + return flat; + } +} + +static FStar_UInt128_uint128 +FStar_UInt128_shift_left_large(FStar_UInt128_uint128 a, uint32_t s) +{ + FStar_UInt128_uint128 flat = { (uint64_t)0U, a.low << (s - FStar_UInt128_u32_64) }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_shift_left(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s < FStar_UInt128_u32_64) + { + return FStar_UInt128_shift_left_small(a, s); + } + else + { + return FStar_UInt128_shift_left_large(a, s); + } +} + +static uint64_t FStar_UInt128_add_u64_shift_right(uint64_t hi, uint64_t lo, uint32_t s) +{ + return (lo >> s) + (hi << (FStar_UInt128_u32_64 - s)); +} + +static uint64_t FStar_UInt128_add_u64_shift_right_respec(uint64_t hi, uint64_t lo, uint32_t s) +{ + return FStar_UInt128_add_u64_shift_right(hi, lo, s); +} + +static FStar_UInt128_uint128 +FStar_UInt128_shift_right_small(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s == (uint32_t)0U) + { + return a; + } + else + { + FStar_UInt128_uint128 + flat = { FStar_UInt128_add_u64_shift_right_respec(a.high, a.low, s), a.high >> s }; + return flat; + } +} + +static FStar_UInt128_uint128 +FStar_UInt128_shift_right_large(FStar_UInt128_uint128 a, uint32_t s) +{ + FStar_UInt128_uint128 flat = { a.high >> (s - FStar_UInt128_u32_64), (uint64_t)0U }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_shift_right(FStar_UInt128_uint128 a, uint32_t s) +{ + if (s < FStar_UInt128_u32_64) + { + return FStar_UInt128_shift_right_small(a, s); + } + else + { + return FStar_UInt128_shift_right_large(a, s); + } +} + +bool FStar_UInt128_eq(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.low == b.low && a.high == b.high; +} + +bool FStar_UInt128_gt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high > b.high || (a.high == b.high && a.low > b.low); +} + +bool FStar_UInt128_lt(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high < b.high || (a.high == b.high && a.low < b.low); +} + +bool FStar_UInt128_gte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high > b.high || (a.high == b.high && a.low >= b.low); +} + +bool FStar_UInt128_lte(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + return a.high < b.high || (a.high == b.high && a.low <= b.low); +} + +FStar_UInt128_uint128 FStar_UInt128_eq_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = + { + FStar_UInt64_eq_mask(a.low, + b.low) + & FStar_UInt64_eq_mask(a.high, b.high), + FStar_UInt64_eq_mask(a.low, + b.low) + & FStar_UInt64_eq_mask(a.high, b.high) + }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_gte_mask(FStar_UInt128_uint128 a, FStar_UInt128_uint128 b) +{ + FStar_UInt128_uint128 + flat = + { + (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) + | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)), + (FStar_UInt64_gte_mask(a.high, b.high) & ~FStar_UInt64_eq_mask(a.high, b.high)) + | (FStar_UInt64_eq_mask(a.high, b.high) & FStar_UInt64_gte_mask(a.low, b.low)) + }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t a) +{ + FStar_UInt128_uint128 flat = { a, (uint64_t)0U }; + return flat; +} + +uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 a) +{ + return a.low; +} + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Plus_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_add; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Plus_Question_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_add_underspec; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Plus_Percent_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_add_mod; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Subtraction_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_sub; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Subtraction_Question_Hat)( + FStar_UInt128_uint128 x0, + FStar_UInt128_uint128 x1 +) = FStar_UInt128_sub_underspec; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Subtraction_Percent_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_sub_mod; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Amp_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_logand; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Hat_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_logxor; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Bar_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_logor; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Less_Less_Hat)(FStar_UInt128_uint128 x0, uint32_t x1) = + FStar_UInt128_shift_left; + +FStar_UInt128_uint128 +(*FStar_UInt128_op_Greater_Greater_Hat)(FStar_UInt128_uint128 x0, uint32_t x1) = + FStar_UInt128_shift_right; + +bool +(*FStar_UInt128_op_Equals_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_eq; + +bool +(*FStar_UInt128_op_Greater_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_gt; + +bool +(*FStar_UInt128_op_Less_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_lt; + +bool +(*FStar_UInt128_op_Greater_Equals_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_gte; + +bool +(*FStar_UInt128_op_Less_Equals_Hat)(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1) = + FStar_UInt128_lte; + +static uint64_t FStar_UInt128_u64_mod_32(uint64_t a) +{ + return a & (uint64_t)0xffffffffU; +} + +static uint32_t FStar_UInt128_u32_32 = (uint32_t)32U; + +static uint64_t FStar_UInt128_u32_combine(uint64_t hi, uint64_t lo) +{ + return lo + (hi << FStar_UInt128_u32_32); +} + +FStar_UInt128_uint128 FStar_UInt128_mul32(uint64_t x, uint32_t y) +{ + FStar_UInt128_uint128 + flat = + { + FStar_UInt128_u32_combine((x >> FStar_UInt128_u32_32) + * (uint64_t)y + + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32), + FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * (uint64_t)y)), + ((x >> FStar_UInt128_u32_32) + * (uint64_t)y + + (FStar_UInt128_u64_mod_32(x) * (uint64_t)y >> FStar_UInt128_u32_32)) + >> FStar_UInt128_u32_32 + }; + return flat; +} + +typedef struct K___uint64_t_uint64_t_uint64_t_uint64_t_s +{ + uint64_t fst; + uint64_t snd; + uint64_t thd; + uint64_t f3; +} +K___uint64_t_uint64_t_uint64_t_uint64_t; + +static K___uint64_t_uint64_t_uint64_t_uint64_t +FStar_UInt128_mul_wide_impl_t_(uint64_t x, uint64_t y) +{ + K___uint64_t_uint64_t_uint64_t_uint64_t + flat = + { + FStar_UInt128_u64_mod_32(x), + FStar_UInt128_u64_mod_32(FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y)), + x + >> FStar_UInt128_u32_32, + (x >> FStar_UInt128_u32_32) + * FStar_UInt128_u64_mod_32(y) + + (FStar_UInt128_u64_mod_32(x) * FStar_UInt128_u64_mod_32(y) >> FStar_UInt128_u32_32) + }; + return flat; +} + +static uint64_t FStar_UInt128_u32_combine_(uint64_t hi, uint64_t lo) +{ + return lo + (hi << FStar_UInt128_u32_32); +} + +static FStar_UInt128_uint128 FStar_UInt128_mul_wide_impl(uint64_t x, uint64_t y) +{ + K___uint64_t_uint64_t_uint64_t_uint64_t scrut = FStar_UInt128_mul_wide_impl_t_(x, y); + uint64_t u1 = scrut.fst; + uint64_t w3 = scrut.snd; + uint64_t x_ = scrut.thd; + uint64_t t_ = scrut.f3; + FStar_UInt128_uint128 + flat = + { + FStar_UInt128_u32_combine_(u1 * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32(t_), + w3), + x_ + * (y >> FStar_UInt128_u32_32) + + (t_ >> FStar_UInt128_u32_32) + + ((u1 * (y >> FStar_UInt128_u32_32) + FStar_UInt128_u64_mod_32(t_)) >> FStar_UInt128_u32_32) + }; + return flat; +} + +FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x, uint64_t y) +{ + return FStar_UInt128_mul_wide_impl(x, y); +} + diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/library/kremlib/FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.c b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/kremlib/FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.c new file mode 100644 index 00000000..08265248 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/kremlib/FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.c @@ -0,0 +1,100 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: ../krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrB9w -minimal -fparentheses -fcurly-braces -fno-shadow -header copyright-header.txt -minimal -tmpdir dist/minimal -skip-compilation -extract-uints -add-include -add-include -add-include "kremlin/internal/compat.h" -add-include "kremlin/internal/types.h" -bundle FStar.UInt64+FStar.UInt32+FStar.UInt16+FStar.UInt8=* extracted/prims.krml extracted/FStar_Pervasives_Native.krml extracted/FStar_Pervasives.krml extracted/FStar_Mul.krml extracted/FStar_Squash.krml extracted/FStar_Classical.krml extracted/FStar_StrongExcludedMiddle.krml extracted/FStar_FunctionalExtensionality.krml extracted/FStar_List_Tot_Base.krml extracted/FStar_List_Tot_Properties.krml extracted/FStar_List_Tot.krml extracted/FStar_Seq_Base.krml extracted/FStar_Seq_Properties.krml extracted/FStar_Seq.krml extracted/FStar_Math_Lib.krml extracted/FStar_Math_Lemmas.krml extracted/FStar_BitVector.krml extracted/FStar_UInt.krml extracted/FStar_UInt32.krml extracted/FStar_Int.krml extracted/FStar_Int16.krml extracted/FStar_Preorder.krml extracted/FStar_Ghost.krml extracted/FStar_ErasedLogic.krml extracted/FStar_UInt64.krml extracted/FStar_Set.krml extracted/FStar_PropositionalExtensionality.krml extracted/FStar_PredicateExtensionality.krml extracted/FStar_TSet.krml extracted/FStar_Monotonic_Heap.krml extracted/FStar_Heap.krml extracted/FStar_Map.krml extracted/FStar_Monotonic_HyperHeap.krml extracted/FStar_Monotonic_HyperStack.krml extracted/FStar_HyperStack.krml extracted/FStar_Monotonic_Witnessed.krml extracted/FStar_HyperStack_ST.krml extracted/FStar_HyperStack_All.krml extracted/FStar_Date.krml extracted/FStar_Universe.krml extracted/FStar_GSet.krml extracted/FStar_ModifiesGen.krml extracted/LowStar_Monotonic_Buffer.krml extracted/LowStar_Buffer.krml extracted/Spec_Loops.krml extracted/LowStar_BufferOps.krml extracted/C_Loops.krml extracted/FStar_UInt8.krml extracted/FStar_Kremlin_Endianness.krml extracted/FStar_UInt63.krml extracted/FStar_Exn.krml extracted/FStar_ST.krml extracted/FStar_All.krml extracted/FStar_Dyn.krml extracted/FStar_Int63.krml extracted/FStar_Int64.krml extracted/FStar_Int32.krml extracted/FStar_Int8.krml extracted/FStar_UInt16.krml extracted/FStar_Int_Cast.krml extracted/FStar_UInt128.krml extracted/C_Endianness.krml extracted/FStar_List.krml extracted/FStar_Float.krml extracted/FStar_IO.krml extracted/C.krml extracted/FStar_Char.krml extracted/FStar_String.krml extracted/LowStar_Modifies.krml extracted/C_String.krml extracted/FStar_Bytes.krml extracted/FStar_HyperStack_IO.krml extracted/C_Failure.krml extracted/TestLib.krml extracted/FStar_Int_Cast_Full.krml + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + +#include "FStar_UInt64_FStar_UInt32_FStar_UInt16_FStar_UInt8.h" + +uint64_t FStar_UInt64_eq_mask(uint64_t a, uint64_t b) +{ + uint64_t x = a ^ b; + uint64_t minus_x = ~x + (uint64_t)1U; + uint64_t x_or_minus_x = x | minus_x; + uint64_t xnx = x_or_minus_x >> (uint32_t)63U; + return xnx - (uint64_t)1U; +} + +uint64_t FStar_UInt64_gte_mask(uint64_t a, uint64_t b) +{ + uint64_t x = a; + uint64_t y = b; + uint64_t x_xor_y = x ^ y; + uint64_t x_sub_y = x - y; + uint64_t x_sub_y_xor_y = x_sub_y ^ y; + uint64_t q = x_xor_y | x_sub_y_xor_y; + uint64_t x_xor_q = x ^ q; + uint64_t x_xor_q_ = x_xor_q >> (uint32_t)63U; + return x_xor_q_ - (uint64_t)1U; +} + +uint32_t FStar_UInt32_eq_mask(uint32_t a, uint32_t b) +{ + uint32_t x = a ^ b; + uint32_t minus_x = ~x + (uint32_t)1U; + uint32_t x_or_minus_x = x | minus_x; + uint32_t xnx = x_or_minus_x >> (uint32_t)31U; + return xnx - (uint32_t)1U; +} + +uint32_t FStar_UInt32_gte_mask(uint32_t a, uint32_t b) +{ + uint32_t x = a; + uint32_t y = b; + uint32_t x_xor_y = x ^ y; + uint32_t x_sub_y = x - y; + uint32_t x_sub_y_xor_y = x_sub_y ^ y; + uint32_t q = x_xor_y | x_sub_y_xor_y; + uint32_t x_xor_q = x ^ q; + uint32_t x_xor_q_ = x_xor_q >> (uint32_t)31U; + return x_xor_q_ - (uint32_t)1U; +} + +uint16_t FStar_UInt16_eq_mask(uint16_t a, uint16_t b) +{ + uint16_t x = a ^ b; + uint16_t minus_x = ~x + (uint16_t)1U; + uint16_t x_or_minus_x = x | minus_x; + uint16_t xnx = x_or_minus_x >> (uint32_t)15U; + return xnx - (uint16_t)1U; +} + +uint16_t FStar_UInt16_gte_mask(uint16_t a, uint16_t b) +{ + uint16_t x = a; + uint16_t y = b; + uint16_t x_xor_y = x ^ y; + uint16_t x_sub_y = x - y; + uint16_t x_sub_y_xor_y = x_sub_y ^ y; + uint16_t q = x_xor_y | x_sub_y_xor_y; + uint16_t x_xor_q = x ^ q; + uint16_t x_xor_q_ = x_xor_q >> (uint32_t)15U; + return x_xor_q_ - (uint16_t)1U; +} + +uint8_t FStar_UInt8_eq_mask(uint8_t a, uint8_t b) +{ + uint8_t x = a ^ b; + uint8_t minus_x = ~x + (uint8_t)1U; + uint8_t x_or_minus_x = x | minus_x; + uint8_t xnx = x_or_minus_x >> (uint32_t)7U; + return xnx - (uint8_t)1U; +} + +uint8_t FStar_UInt8_gte_mask(uint8_t a, uint8_t b) +{ + uint8_t x = a; + uint8_t y = b; + uint8_t x_xor_y = x ^ y; + uint8_t x_sub_y = x - y; + uint8_t x_sub_y_xor_y = x_sub_y ^ y; + uint8_t q = x_xor_y | x_sub_y_xor_y; + uint8_t x_xor_q = x ^ q; + uint8_t x_xor_q_ = x_xor_q >> (uint32_t)7U; + return x_xor_q_ - (uint8_t)1U; +} + diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/library/legacy/Hacl_Curve25519.c b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/legacy/Hacl_Curve25519.c new file mode 100644 index 00000000..babebe4f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/legacy/Hacl_Curve25519.c @@ -0,0 +1,805 @@ +/* Copyright (c) INRIA and Microsoft Corporation. All rights reserved. + Licensed under the Apache 2.0 License. */ + +/* This file was generated by KreMLin + * KreMLin invocation: /mnt/e/everest/verify/kremlin/krml -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -fc89 -fparentheses -fno-shadow -header /mnt/e/everest/verify/hdrcLh -minimal -I /mnt/e/everest/verify/hacl-star/code/lib/kremlin -I /mnt/e/everest/verify/kremlin/kremlib/compat -I /mnt/e/everest/verify/hacl-star/specs -I /mnt/e/everest/verify/hacl-star/specs/old -I . -ccopt -march=native -verbose -ldopt -flto -tmpdir x25519-c -I ../bignum -bundle Hacl.Curve25519=* -minimal -add-include "kremlib.h" -skip-compilation x25519-c/out.krml -o x25519-c/Hacl_Curve25519.c + * F* version: 059db0c8 + * KreMLin version: 916c37ac + */ + + +#include "Hacl_Curve25519.h" + +extern uint64_t FStar_UInt64_eq_mask(uint64_t x0, uint64_t x1); + +extern uint64_t FStar_UInt64_gte_mask(uint64_t x0, uint64_t x1); + +extern FStar_UInt128_uint128 +FStar_UInt128_add(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +FStar_UInt128_add_mod(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 +FStar_UInt128_logand(FStar_UInt128_uint128 x0, FStar_UInt128_uint128 x1); + +extern FStar_UInt128_uint128 FStar_UInt128_shift_right(FStar_UInt128_uint128 x0, uint32_t x1); + +extern FStar_UInt128_uint128 FStar_UInt128_uint64_to_uint128(uint64_t x0); + +extern uint64_t FStar_UInt128_uint128_to_uint64(FStar_UInt128_uint128 x0); + +extern FStar_UInt128_uint128 FStar_UInt128_mul_wide(uint64_t x0, uint64_t x1); + +static void Hacl_Bignum_Modulo_carry_top(uint64_t *b) +{ + uint64_t b4 = b[4U]; + uint64_t b0 = b[0U]; + uint64_t b4_ = b4 & (uint64_t)0x7ffffffffffffU; + uint64_t b0_ = b0 + (uint64_t)19U * (b4 >> (uint32_t)51U); + b[4U] = b4_; + b[0U] = b0_; +} + +inline static void +Hacl_Bignum_Fproduct_copy_from_wide_(uint64_t *output, FStar_UInt128_uint128 *input) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + FStar_UInt128_uint128 xi = input[i]; + output[i] = FStar_UInt128_uint128_to_uint64(xi); + } +} + +inline static void +Hacl_Bignum_Fproduct_sum_scalar_multiplication_( + FStar_UInt128_uint128 *output, + uint64_t *input, + uint64_t s +) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + FStar_UInt128_uint128 xi = output[i]; + uint64_t yi = input[i]; + output[i] = FStar_UInt128_add_mod(xi, FStar_UInt128_mul_wide(yi, s)); + } +} + +inline static void Hacl_Bignum_Fproduct_carry_wide_(FStar_UInt128_uint128 *tmp) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)4U; i = i + (uint32_t)1U) + { + uint32_t ctr = i; + FStar_UInt128_uint128 tctr = tmp[ctr]; + FStar_UInt128_uint128 tctrp1 = tmp[ctr + (uint32_t)1U]; + uint64_t r0 = FStar_UInt128_uint128_to_uint64(tctr) & (uint64_t)0x7ffffffffffffU; + FStar_UInt128_uint128 c = FStar_UInt128_shift_right(tctr, (uint32_t)51U); + tmp[ctr] = FStar_UInt128_uint64_to_uint128(r0); + tmp[ctr + (uint32_t)1U] = FStar_UInt128_add(tctrp1, c); + } +} + +inline static void Hacl_Bignum_Fmul_shift_reduce(uint64_t *output) +{ + uint64_t tmp = output[4U]; + uint64_t b0; + { + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)4U; i = i + (uint32_t)1U) + { + uint32_t ctr = (uint32_t)5U - i - (uint32_t)1U; + uint64_t z = output[ctr - (uint32_t)1U]; + output[ctr] = z; + } + } + output[0U] = tmp; + b0 = output[0U]; + output[0U] = (uint64_t)19U * b0; +} + +static void +Hacl_Bignum_Fmul_mul_shift_reduce_( + FStar_UInt128_uint128 *output, + uint64_t *input, + uint64_t *input2 +) +{ + uint32_t i; + uint64_t input2i; + { + uint32_t i0; + for (i0 = (uint32_t)0U; i0 < (uint32_t)4U; i0 = i0 + (uint32_t)1U) + { + uint64_t input2i0 = input2[i0]; + Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i0); + Hacl_Bignum_Fmul_shift_reduce(input); + } + } + i = (uint32_t)4U; + input2i = input2[i]; + Hacl_Bignum_Fproduct_sum_scalar_multiplication_(output, input, input2i); +} + +inline static void Hacl_Bignum_Fmul_fmul(uint64_t *output, uint64_t *input, uint64_t *input2) +{ + uint64_t tmp[5U] = { 0U }; + memcpy(tmp, input, (uint32_t)5U * sizeof input[0U]); + KRML_CHECK_SIZE(sizeof (FStar_UInt128_uint128), (uint32_t)5U); + { + FStar_UInt128_uint128 t[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + t[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U); + } + { + FStar_UInt128_uint128 b4; + FStar_UInt128_uint128 b0; + FStar_UInt128_uint128 b4_; + FStar_UInt128_uint128 b0_; + uint64_t i0; + uint64_t i1; + uint64_t i0_; + uint64_t i1_; + Hacl_Bignum_Fmul_mul_shift_reduce_(t, tmp, input2); + Hacl_Bignum_Fproduct_carry_wide_(t); + b4 = t[4U]; + b0 = t[0U]; + b4_ = FStar_UInt128_logand(b4, FStar_UInt128_uint64_to_uint128((uint64_t)0x7ffffffffffffU)); + b0_ = + FStar_UInt128_add(b0, + FStar_UInt128_mul_wide((uint64_t)19U, + FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(b4, (uint32_t)51U)))); + t[4U] = b4_; + t[0U] = b0_; + Hacl_Bignum_Fproduct_copy_from_wide_(output, t); + i0 = output[0U]; + i1 = output[1U]; + i0_ = i0 & (uint64_t)0x7ffffffffffffU; + i1_ = i1 + (i0 >> (uint32_t)51U); + output[0U] = i0_; + output[1U] = i1_; + } + } +} + +inline static void Hacl_Bignum_Fsquare_fsquare__(FStar_UInt128_uint128 *tmp, uint64_t *output) +{ + uint64_t r0 = output[0U]; + uint64_t r1 = output[1U]; + uint64_t r2 = output[2U]; + uint64_t r3 = output[3U]; + uint64_t r4 = output[4U]; + uint64_t d0 = r0 * (uint64_t)2U; + uint64_t d1 = r1 * (uint64_t)2U; + uint64_t d2 = r2 * (uint64_t)2U * (uint64_t)19U; + uint64_t d419 = r4 * (uint64_t)19U; + uint64_t d4 = d419 * (uint64_t)2U; + FStar_UInt128_uint128 + s0 = + FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(r0, r0), + FStar_UInt128_mul_wide(d4, r1)), + FStar_UInt128_mul_wide(d2, r3)); + FStar_UInt128_uint128 + s1 = + FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r1), + FStar_UInt128_mul_wide(d4, r2)), + FStar_UInt128_mul_wide(r3 * (uint64_t)19U, r3)); + FStar_UInt128_uint128 + s2 = + FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r2), + FStar_UInt128_mul_wide(r1, r1)), + FStar_UInt128_mul_wide(d4, r3)); + FStar_UInt128_uint128 + s3 = + FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r3), + FStar_UInt128_mul_wide(d1, r2)), + FStar_UInt128_mul_wide(r4, d419)); + FStar_UInt128_uint128 + s4 = + FStar_UInt128_add(FStar_UInt128_add(FStar_UInt128_mul_wide(d0, r4), + FStar_UInt128_mul_wide(d1, r3)), + FStar_UInt128_mul_wide(r2, r2)); + tmp[0U] = s0; + tmp[1U] = s1; + tmp[2U] = s2; + tmp[3U] = s3; + tmp[4U] = s4; +} + +inline static void Hacl_Bignum_Fsquare_fsquare_(FStar_UInt128_uint128 *tmp, uint64_t *output) +{ + FStar_UInt128_uint128 b4; + FStar_UInt128_uint128 b0; + FStar_UInt128_uint128 b4_; + FStar_UInt128_uint128 b0_; + uint64_t i0; + uint64_t i1; + uint64_t i0_; + uint64_t i1_; + Hacl_Bignum_Fsquare_fsquare__(tmp, output); + Hacl_Bignum_Fproduct_carry_wide_(tmp); + b4 = tmp[4U]; + b0 = tmp[0U]; + b4_ = FStar_UInt128_logand(b4, FStar_UInt128_uint64_to_uint128((uint64_t)0x7ffffffffffffU)); + b0_ = + FStar_UInt128_add(b0, + FStar_UInt128_mul_wide((uint64_t)19U, + FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(b4, (uint32_t)51U)))); + tmp[4U] = b4_; + tmp[0U] = b0_; + Hacl_Bignum_Fproduct_copy_from_wide_(output, tmp); + i0 = output[0U]; + i1 = output[1U]; + i0_ = i0 & (uint64_t)0x7ffffffffffffU; + i1_ = i1 + (i0 >> (uint32_t)51U); + output[0U] = i0_; + output[1U] = i1_; +} + +static void +Hacl_Bignum_Fsquare_fsquare_times_( + uint64_t *input, + FStar_UInt128_uint128 *tmp, + uint32_t count1 +) +{ + uint32_t i; + Hacl_Bignum_Fsquare_fsquare_(tmp, input); + for (i = (uint32_t)1U; i < count1; i = i + (uint32_t)1U) + Hacl_Bignum_Fsquare_fsquare_(tmp, input); +} + +inline static void +Hacl_Bignum_Fsquare_fsquare_times(uint64_t *output, uint64_t *input, uint32_t count1) +{ + KRML_CHECK_SIZE(sizeof (FStar_UInt128_uint128), (uint32_t)5U); + { + FStar_UInt128_uint128 t[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + t[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U); + } + memcpy(output, input, (uint32_t)5U * sizeof input[0U]); + Hacl_Bignum_Fsquare_fsquare_times_(output, t, count1); + } +} + +inline static void Hacl_Bignum_Fsquare_fsquare_times_inplace(uint64_t *output, uint32_t count1) +{ + KRML_CHECK_SIZE(sizeof (FStar_UInt128_uint128), (uint32_t)5U); + { + FStar_UInt128_uint128 t[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + t[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U); + } + Hacl_Bignum_Fsquare_fsquare_times_(output, t, count1); + } +} + +inline static void Hacl_Bignum_Crecip_crecip(uint64_t *out, uint64_t *z) +{ + uint64_t buf[20U] = { 0U }; + uint64_t *a0 = buf; + uint64_t *t00 = buf + (uint32_t)5U; + uint64_t *b0 = buf + (uint32_t)10U; + uint64_t *t01; + uint64_t *b1; + uint64_t *c0; + uint64_t *a; + uint64_t *t0; + uint64_t *b; + uint64_t *c; + Hacl_Bignum_Fsquare_fsquare_times(a0, z, (uint32_t)1U); + Hacl_Bignum_Fsquare_fsquare_times(t00, a0, (uint32_t)2U); + Hacl_Bignum_Fmul_fmul(b0, t00, z); + Hacl_Bignum_Fmul_fmul(a0, b0, a0); + Hacl_Bignum_Fsquare_fsquare_times(t00, a0, (uint32_t)1U); + Hacl_Bignum_Fmul_fmul(b0, t00, b0); + Hacl_Bignum_Fsquare_fsquare_times(t00, b0, (uint32_t)5U); + t01 = buf + (uint32_t)5U; + b1 = buf + (uint32_t)10U; + c0 = buf + (uint32_t)15U; + Hacl_Bignum_Fmul_fmul(b1, t01, b1); + Hacl_Bignum_Fsquare_fsquare_times(t01, b1, (uint32_t)10U); + Hacl_Bignum_Fmul_fmul(c0, t01, b1); + Hacl_Bignum_Fsquare_fsquare_times(t01, c0, (uint32_t)20U); + Hacl_Bignum_Fmul_fmul(t01, t01, c0); + Hacl_Bignum_Fsquare_fsquare_times_inplace(t01, (uint32_t)10U); + Hacl_Bignum_Fmul_fmul(b1, t01, b1); + Hacl_Bignum_Fsquare_fsquare_times(t01, b1, (uint32_t)50U); + a = buf; + t0 = buf + (uint32_t)5U; + b = buf + (uint32_t)10U; + c = buf + (uint32_t)15U; + Hacl_Bignum_Fmul_fmul(c, t0, b); + Hacl_Bignum_Fsquare_fsquare_times(t0, c, (uint32_t)100U); + Hacl_Bignum_Fmul_fmul(t0, t0, c); + Hacl_Bignum_Fsquare_fsquare_times_inplace(t0, (uint32_t)50U); + Hacl_Bignum_Fmul_fmul(t0, t0, b); + Hacl_Bignum_Fsquare_fsquare_times_inplace(t0, (uint32_t)5U); + Hacl_Bignum_Fmul_fmul(out, t0, a); +} + +inline static void Hacl_Bignum_fsum(uint64_t *a, uint64_t *b) +{ + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint64_t xi = a[i]; + uint64_t yi = b[i]; + a[i] = xi + yi; + } +} + +inline static void Hacl_Bignum_fdifference(uint64_t *a, uint64_t *b) +{ + uint64_t tmp[5U] = { 0U }; + uint64_t b0; + uint64_t b1; + uint64_t b2; + uint64_t b3; + uint64_t b4; + memcpy(tmp, b, (uint32_t)5U * sizeof b[0U]); + b0 = tmp[0U]; + b1 = tmp[1U]; + b2 = tmp[2U]; + b3 = tmp[3U]; + b4 = tmp[4U]; + tmp[0U] = b0 + (uint64_t)0x3fffffffffff68U; + tmp[1U] = b1 + (uint64_t)0x3ffffffffffff8U; + tmp[2U] = b2 + (uint64_t)0x3ffffffffffff8U; + tmp[3U] = b3 + (uint64_t)0x3ffffffffffff8U; + tmp[4U] = b4 + (uint64_t)0x3ffffffffffff8U; + { + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint64_t xi = a[i]; + uint64_t yi = tmp[i]; + a[i] = yi - xi; + } + } +} + +inline static void Hacl_Bignum_fscalar(uint64_t *output, uint64_t *b, uint64_t s) +{ + KRML_CHECK_SIZE(sizeof (FStar_UInt128_uint128), (uint32_t)5U); + { + FStar_UInt128_uint128 tmp[5U]; + { + uint32_t _i; + for (_i = 0U; _i < (uint32_t)5U; ++_i) + tmp[_i] = FStar_UInt128_uint64_to_uint128((uint64_t)0U); + } + { + FStar_UInt128_uint128 b4; + FStar_UInt128_uint128 b0; + FStar_UInt128_uint128 b4_; + FStar_UInt128_uint128 b0_; + { + uint32_t i; + for (i = (uint32_t)0U; i < (uint32_t)5U; i = i + (uint32_t)1U) + { + uint64_t xi = b[i]; + tmp[i] = FStar_UInt128_mul_wide(xi, s); + } + } + Hacl_Bignum_Fproduct_carry_wide_(tmp); + b4 = tmp[4U]; + b0 = tmp[0U]; + b4_ = FStar_UInt128_logand(b4, FStar_UInt128_uint64_to_uint128((uint64_t)0x7ffffffffffffU)); + b0_ = + FStar_UInt128_add(b0, + FStar_UInt128_mul_wide((uint64_t)19U, + FStar_UInt128_uint128_to_uint64(FStar_UInt128_shift_right(b4, (uint32_t)51U)))); + tmp[4U] = b4_; + tmp[0U] = b0_; + Hacl_Bignum_Fproduct_copy_from_wide_(output, tmp); + } + } +} + +inline static void Hacl_Bignum_fmul(uint64_t *output, uint64_t *a, uint64_t *b) +{ + Hacl_Bignum_Fmul_fmul(output, a, b); +} + +inline static void Hacl_Bignum_crecip(uint64_t *output, uint64_t *input) +{ + Hacl_Bignum_Crecip_crecip(output, input); +} + +static void +Hacl_EC_Point_swap_conditional_step(uint64_t *a, uint64_t *b, uint64_t swap1, uint32_t ctr) +{ + uint32_t i = ctr - (uint32_t)1U; + uint64_t ai = a[i]; + uint64_t bi = b[i]; + uint64_t x = swap1 & (ai ^ bi); + uint64_t ai1 = ai ^ x; + uint64_t bi1 = bi ^ x; + a[i] = ai1; + b[i] = bi1; +} + +static void +Hacl_EC_Point_swap_conditional_(uint64_t *a, uint64_t *b, uint64_t swap1, uint32_t ctr) +{ + if (!(ctr == (uint32_t)0U)) + { + uint32_t i; + Hacl_EC_Point_swap_conditional_step(a, b, swap1, ctr); + i = ctr - (uint32_t)1U; + Hacl_EC_Point_swap_conditional_(a, b, swap1, i); + } +} + +static void Hacl_EC_Point_swap_conditional(uint64_t *a, uint64_t *b, uint64_t iswap) +{ + uint64_t swap1 = (uint64_t)0U - iswap; + Hacl_EC_Point_swap_conditional_(a, b, swap1, (uint32_t)5U); + Hacl_EC_Point_swap_conditional_(a + (uint32_t)5U, b + (uint32_t)5U, swap1, (uint32_t)5U); +} + +static void Hacl_EC_Point_copy(uint64_t *output, uint64_t *input) +{ + memcpy(output, input, (uint32_t)5U * sizeof input[0U]); + memcpy(output + (uint32_t)5U, + input + (uint32_t)5U, + (uint32_t)5U * sizeof (input + (uint32_t)5U)[0U]); +} + +static void Hacl_EC_Format_fexpand(uint64_t *output, uint8_t *input) +{ + uint64_t i0 = load64_le(input); + uint8_t *x00 = input + (uint32_t)6U; + uint64_t i1 = load64_le(x00); + uint8_t *x01 = input + (uint32_t)12U; + uint64_t i2 = load64_le(x01); + uint8_t *x02 = input + (uint32_t)19U; + uint64_t i3 = load64_le(x02); + uint8_t *x0 = input + (uint32_t)24U; + uint64_t i4 = load64_le(x0); + uint64_t output0 = i0 & (uint64_t)0x7ffffffffffffU; + uint64_t output1 = i1 >> (uint32_t)3U & (uint64_t)0x7ffffffffffffU; + uint64_t output2 = i2 >> (uint32_t)6U & (uint64_t)0x7ffffffffffffU; + uint64_t output3 = i3 >> (uint32_t)1U & (uint64_t)0x7ffffffffffffU; + uint64_t output4 = i4 >> (uint32_t)12U & (uint64_t)0x7ffffffffffffU; + output[0U] = output0; + output[1U] = output1; + output[2U] = output2; + output[3U] = output3; + output[4U] = output4; +} + +static void Hacl_EC_Format_fcontract_first_carry_pass(uint64_t *input) +{ + uint64_t t0 = input[0U]; + uint64_t t1 = input[1U]; + uint64_t t2 = input[2U]; + uint64_t t3 = input[3U]; + uint64_t t4 = input[4U]; + uint64_t t1_ = t1 + (t0 >> (uint32_t)51U); + uint64_t t0_ = t0 & (uint64_t)0x7ffffffffffffU; + uint64_t t2_ = t2 + (t1_ >> (uint32_t)51U); + uint64_t t1__ = t1_ & (uint64_t)0x7ffffffffffffU; + uint64_t t3_ = t3 + (t2_ >> (uint32_t)51U); + uint64_t t2__ = t2_ & (uint64_t)0x7ffffffffffffU; + uint64_t t4_ = t4 + (t3_ >> (uint32_t)51U); + uint64_t t3__ = t3_ & (uint64_t)0x7ffffffffffffU; + input[0U] = t0_; + input[1U] = t1__; + input[2U] = t2__; + input[3U] = t3__; + input[4U] = t4_; +} + +static void Hacl_EC_Format_fcontract_first_carry_full(uint64_t *input) +{ + Hacl_EC_Format_fcontract_first_carry_pass(input); + Hacl_Bignum_Modulo_carry_top(input); +} + +static void Hacl_EC_Format_fcontract_second_carry_pass(uint64_t *input) +{ + uint64_t t0 = input[0U]; + uint64_t t1 = input[1U]; + uint64_t t2 = input[2U]; + uint64_t t3 = input[3U]; + uint64_t t4 = input[4U]; + uint64_t t1_ = t1 + (t0 >> (uint32_t)51U); + uint64_t t0_ = t0 & (uint64_t)0x7ffffffffffffU; + uint64_t t2_ = t2 + (t1_ >> (uint32_t)51U); + uint64_t t1__ = t1_ & (uint64_t)0x7ffffffffffffU; + uint64_t t3_ = t3 + (t2_ >> (uint32_t)51U); + uint64_t t2__ = t2_ & (uint64_t)0x7ffffffffffffU; + uint64_t t4_ = t4 + (t3_ >> (uint32_t)51U); + uint64_t t3__ = t3_ & (uint64_t)0x7ffffffffffffU; + input[0U] = t0_; + input[1U] = t1__; + input[2U] = t2__; + input[3U] = t3__; + input[4U] = t4_; +} + +static void Hacl_EC_Format_fcontract_second_carry_full(uint64_t *input) +{ + uint64_t i0; + uint64_t i1; + uint64_t i0_; + uint64_t i1_; + Hacl_EC_Format_fcontract_second_carry_pass(input); + Hacl_Bignum_Modulo_carry_top(input); + i0 = input[0U]; + i1 = input[1U]; + i0_ = i0 & (uint64_t)0x7ffffffffffffU; + i1_ = i1 + (i0 >> (uint32_t)51U); + input[0U] = i0_; + input[1U] = i1_; +} + +static void Hacl_EC_Format_fcontract_trim(uint64_t *input) +{ + uint64_t a0 = input[0U]; + uint64_t a1 = input[1U]; + uint64_t a2 = input[2U]; + uint64_t a3 = input[3U]; + uint64_t a4 = input[4U]; + uint64_t mask0 = FStar_UInt64_gte_mask(a0, (uint64_t)0x7ffffffffffedU); + uint64_t mask1 = FStar_UInt64_eq_mask(a1, (uint64_t)0x7ffffffffffffU); + uint64_t mask2 = FStar_UInt64_eq_mask(a2, (uint64_t)0x7ffffffffffffU); + uint64_t mask3 = FStar_UInt64_eq_mask(a3, (uint64_t)0x7ffffffffffffU); + uint64_t mask4 = FStar_UInt64_eq_mask(a4, (uint64_t)0x7ffffffffffffU); + uint64_t mask = (((mask0 & mask1) & mask2) & mask3) & mask4; + uint64_t a0_ = a0 - ((uint64_t)0x7ffffffffffedU & mask); + uint64_t a1_ = a1 - ((uint64_t)0x7ffffffffffffU & mask); + uint64_t a2_ = a2 - ((uint64_t)0x7ffffffffffffU & mask); + uint64_t a3_ = a3 - ((uint64_t)0x7ffffffffffffU & mask); + uint64_t a4_ = a4 - ((uint64_t)0x7ffffffffffffU & mask); + input[0U] = a0_; + input[1U] = a1_; + input[2U] = a2_; + input[3U] = a3_; + input[4U] = a4_; +} + +static void Hacl_EC_Format_fcontract_store(uint8_t *output, uint64_t *input) +{ + uint64_t t0 = input[0U]; + uint64_t t1 = input[1U]; + uint64_t t2 = input[2U]; + uint64_t t3 = input[3U]; + uint64_t t4 = input[4U]; + uint64_t o0 = t1 << (uint32_t)51U | t0; + uint64_t o1 = t2 << (uint32_t)38U | t1 >> (uint32_t)13U; + uint64_t o2 = t3 << (uint32_t)25U | t2 >> (uint32_t)26U; + uint64_t o3 = t4 << (uint32_t)12U | t3 >> (uint32_t)39U; + uint8_t *b0 = output; + uint8_t *b1 = output + (uint32_t)8U; + uint8_t *b2 = output + (uint32_t)16U; + uint8_t *b3 = output + (uint32_t)24U; + store64_le(b0, o0); + store64_le(b1, o1); + store64_le(b2, o2); + store64_le(b3, o3); +} + +static void Hacl_EC_Format_fcontract(uint8_t *output, uint64_t *input) +{ + Hacl_EC_Format_fcontract_first_carry_full(input); + Hacl_EC_Format_fcontract_second_carry_full(input); + Hacl_EC_Format_fcontract_trim(input); + Hacl_EC_Format_fcontract_store(output, input); +} + +static void Hacl_EC_Format_scalar_of_point(uint8_t *scalar, uint64_t *point) +{ + uint64_t *x = point; + uint64_t *z = point + (uint32_t)5U; + uint64_t buf[10U] = { 0U }; + uint64_t *zmone = buf; + uint64_t *sc = buf + (uint32_t)5U; + Hacl_Bignum_crecip(zmone, z); + Hacl_Bignum_fmul(sc, x, zmone); + Hacl_EC_Format_fcontract(scalar, sc); +} + +static void +Hacl_EC_AddAndDouble_fmonty( + uint64_t *pp, + uint64_t *ppq, + uint64_t *p, + uint64_t *pq, + uint64_t *qmqp +) +{ + uint64_t *qx = qmqp; + uint64_t *x2 = pp; + uint64_t *z2 = pp + (uint32_t)5U; + uint64_t *x3 = ppq; + uint64_t *z3 = ppq + (uint32_t)5U; + uint64_t *x = p; + uint64_t *z = p + (uint32_t)5U; + uint64_t *xprime = pq; + uint64_t *zprime = pq + (uint32_t)5U; + uint64_t buf[40U] = { 0U }; + uint64_t *origx = buf; + uint64_t *origxprime0 = buf + (uint32_t)5U; + uint64_t *xxprime0 = buf + (uint32_t)25U; + uint64_t *zzprime0 = buf + (uint32_t)30U; + uint64_t *origxprime; + uint64_t *xx0; + uint64_t *zz0; + uint64_t *xxprime; + uint64_t *zzprime; + uint64_t *zzzprime; + uint64_t *zzz; + uint64_t *xx; + uint64_t *zz; + uint64_t scalar; + memcpy(origx, x, (uint32_t)5U * sizeof x[0U]); + Hacl_Bignum_fsum(x, z); + Hacl_Bignum_fdifference(z, origx); + memcpy(origxprime0, xprime, (uint32_t)5U * sizeof xprime[0U]); + Hacl_Bignum_fsum(xprime, zprime); + Hacl_Bignum_fdifference(zprime, origxprime0); + Hacl_Bignum_fmul(xxprime0, xprime, z); + Hacl_Bignum_fmul(zzprime0, x, zprime); + origxprime = buf + (uint32_t)5U; + xx0 = buf + (uint32_t)15U; + zz0 = buf + (uint32_t)20U; + xxprime = buf + (uint32_t)25U; + zzprime = buf + (uint32_t)30U; + zzzprime = buf + (uint32_t)35U; + memcpy(origxprime, xxprime, (uint32_t)5U * sizeof xxprime[0U]); + Hacl_Bignum_fsum(xxprime, zzprime); + Hacl_Bignum_fdifference(zzprime, origxprime); + Hacl_Bignum_Fsquare_fsquare_times(x3, xxprime, (uint32_t)1U); + Hacl_Bignum_Fsquare_fsquare_times(zzzprime, zzprime, (uint32_t)1U); + Hacl_Bignum_fmul(z3, zzzprime, qx); + Hacl_Bignum_Fsquare_fsquare_times(xx0, x, (uint32_t)1U); + Hacl_Bignum_Fsquare_fsquare_times(zz0, z, (uint32_t)1U); + zzz = buf + (uint32_t)10U; + xx = buf + (uint32_t)15U; + zz = buf + (uint32_t)20U; + Hacl_Bignum_fmul(x2, xx, zz); + Hacl_Bignum_fdifference(zz, xx); + scalar = (uint64_t)121665U; + Hacl_Bignum_fscalar(zzz, zz, scalar); + Hacl_Bignum_fsum(zzz, xx); + Hacl_Bignum_fmul(z2, zzz, zz); +} + +static void +Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step( + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint8_t byt +) +{ + uint64_t bit0 = (uint64_t)(byt >> (uint32_t)7U); + uint64_t bit; + Hacl_EC_Point_swap_conditional(nq, nqpq, bit0); + Hacl_EC_AddAndDouble_fmonty(nq2, nqpq2, nq, nqpq, q); + bit = (uint64_t)(byt >> (uint32_t)7U); + Hacl_EC_Point_swap_conditional(nq2, nqpq2, bit); +} + +static void +Hacl_EC_Ladder_SmallLoop_cmult_small_loop_double_step( + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint8_t byt +) +{ + uint8_t byt1; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt); + byt1 = byt << (uint32_t)1U; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1); +} + +static void +Hacl_EC_Ladder_SmallLoop_cmult_small_loop( + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint8_t byt, + uint32_t i +) +{ + if (!(i == (uint32_t)0U)) + { + uint32_t i_ = i - (uint32_t)1U; + uint8_t byt_; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop_double_step(nq, nqpq, nq2, nqpq2, q, byt); + byt_ = byt << (uint32_t)2U; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q, byt_, i_); + } +} + +static void +Hacl_EC_Ladder_BigLoop_cmult_big_loop( + uint8_t *n1, + uint64_t *nq, + uint64_t *nqpq, + uint64_t *nq2, + uint64_t *nqpq2, + uint64_t *q, + uint32_t i +) +{ + if (!(i == (uint32_t)0U)) + { + uint32_t i1 = i - (uint32_t)1U; + uint8_t byte = n1[i1]; + Hacl_EC_Ladder_SmallLoop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q, byte, (uint32_t)4U); + Hacl_EC_Ladder_BigLoop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, i1); + } +} + +static void Hacl_EC_Ladder_cmult(uint64_t *result, uint8_t *n1, uint64_t *q) +{ + uint64_t point_buf[40U] = { 0U }; + uint64_t *nq = point_buf; + uint64_t *nqpq = point_buf + (uint32_t)10U; + uint64_t *nq2 = point_buf + (uint32_t)20U; + uint64_t *nqpq2 = point_buf + (uint32_t)30U; + Hacl_EC_Point_copy(nqpq, q); + nq[0U] = (uint64_t)1U; + Hacl_EC_Ladder_BigLoop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, (uint32_t)32U); + Hacl_EC_Point_copy(result, nq); +} + +void Hacl_Curve25519_crypto_scalarmult(uint8_t *mypublic, uint8_t *secret, uint8_t *basepoint) +{ + uint64_t buf0[10U] = { 0U }; + uint64_t *x0 = buf0; + uint64_t *z = buf0 + (uint32_t)5U; + uint64_t *q; + Hacl_EC_Format_fexpand(x0, basepoint); + z[0U] = (uint64_t)1U; + q = buf0; + { + uint8_t e[32U] = { 0U }; + uint8_t e0; + uint8_t e31; + uint8_t e01; + uint8_t e311; + uint8_t e312; + uint8_t *scalar; + memcpy(e, secret, (uint32_t)32U * sizeof secret[0U]); + e0 = e[0U]; + e31 = e[31U]; + e01 = e0 & (uint8_t)248U; + e311 = e31 & (uint8_t)127U; + e312 = e311 | (uint8_t)64U; + e[0U] = e01; + e[31U] = e312; + scalar = e; + { + uint64_t buf[15U] = { 0U }; + uint64_t *nq = buf; + uint64_t *x = nq; + x[0U] = (uint64_t)1U; + Hacl_EC_Ladder_cmult(nq, scalar, q); + Hacl_EC_Format_scalar_of_point(mypublic, nq); + } + } +} + diff --git a/r5dev/thirdparty/mbedtls/3rdparty/everest/library/x25519.c b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/x25519.c new file mode 100644 index 00000000..9faa9ab7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/3rdparty/everest/library/x25519.c @@ -0,0 +1,186 @@ +/* + * ECDH with curve-optimized implementation multiplexing + * + * Copyright 2016-2018 INRIA and Microsoft Corporation + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#include "common.h" + +#if defined(MBEDTLS_ECDH_C) && defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + +#include + +#if !(defined(__SIZEOF_INT128__) && (__SIZEOF_INT128__ == 16)) +#define KRML_VERIFIED_UINT128 +#endif + +#include +#include + +#include "x25519.h" + +#include + +/* + * Initialize context + */ +void mbedtls_x25519_init( mbedtls_x25519_context *ctx ) +{ + mbedtls_platform_zeroize( ctx, sizeof( mbedtls_x25519_context ) ); +} + +/* + * Free context + */ +void mbedtls_x25519_free( mbedtls_x25519_context *ctx ) +{ + if( ctx == NULL ) + return; + + mbedtls_platform_zeroize( ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES ); + mbedtls_platform_zeroize( ctx->peer_point, MBEDTLS_X25519_KEY_SIZE_BYTES ); +} + +int mbedtls_x25519_make_params( mbedtls_x25519_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret = 0; + + uint8_t base[MBEDTLS_X25519_KEY_SIZE_BYTES] = {0}; + + if( ( ret = f_rng( p_rng, ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES ) ) != 0 ) + return ret; + + *olen = MBEDTLS_X25519_KEY_SIZE_BYTES + 4; + if( blen < *olen ) + return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL ); + + *buf++ = MBEDTLS_ECP_TLS_NAMED_CURVE; + *buf++ = MBEDTLS_ECP_TLS_CURVE25519 >> 8; + *buf++ = MBEDTLS_ECP_TLS_CURVE25519 & 0xFF; + *buf++ = MBEDTLS_X25519_KEY_SIZE_BYTES; + + base[0] = 9; + Hacl_Curve25519_crypto_scalarmult( buf, ctx->our_secret, base ); + + base[0] = 0; + if( memcmp( buf, base, MBEDTLS_X25519_KEY_SIZE_BYTES) == 0 ) + return MBEDTLS_ERR_ECP_RANDOM_FAILED; + + return( 0 ); +} + +int mbedtls_x25519_read_params( mbedtls_x25519_context *ctx, + const unsigned char **buf, const unsigned char *end ) +{ + if( end - *buf < MBEDTLS_X25519_KEY_SIZE_BYTES + 1 ) + return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); + + if( ( *(*buf)++ != MBEDTLS_X25519_KEY_SIZE_BYTES ) ) + return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); + + memcpy( ctx->peer_point, *buf, MBEDTLS_X25519_KEY_SIZE_BYTES ); + *buf += MBEDTLS_X25519_KEY_SIZE_BYTES; + return( 0 ); +} + +int mbedtls_x25519_get_params( mbedtls_x25519_context *ctx, const mbedtls_ecp_keypair *key, + mbedtls_x25519_ecdh_side side ) +{ + size_t olen = 0; + + switch( side ) { + case MBEDTLS_X25519_ECDH_THEIRS: + return mbedtls_ecp_point_write_binary( &key->grp, &key->Q, MBEDTLS_ECP_PF_COMPRESSED, &olen, ctx->peer_point, MBEDTLS_X25519_KEY_SIZE_BYTES ); + case MBEDTLS_X25519_ECDH_OURS: + return mbedtls_mpi_write_binary_le( &key->d, ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES ); + default: + return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); + } +} + +int mbedtls_x25519_calc_secret( mbedtls_x25519_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )(void *, unsigned char *, size_t), + void *p_rng ) +{ + /* f_rng and p_rng are not used here because this implementation does not + need blinding since it has constant trace. */ + (( void )f_rng); + (( void )p_rng); + + *olen = MBEDTLS_X25519_KEY_SIZE_BYTES; + + if( blen < *olen ) + return( MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL ); + + Hacl_Curve25519_crypto_scalarmult( buf, ctx->our_secret, ctx->peer_point); + + /* Wipe the DH secret and don't let the peer chose a small subgroup point */ + mbedtls_platform_zeroize( ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES ); + + if( memcmp( buf, ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES) == 0 ) + return MBEDTLS_ERR_ECP_RANDOM_FAILED; + + return( 0 ); +} + +int mbedtls_x25519_make_public( mbedtls_x25519_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int( *f_rng )(void *, unsigned char *, size_t), + void *p_rng ) +{ + int ret = 0; + unsigned char base[MBEDTLS_X25519_KEY_SIZE_BYTES] = { 0 }; + + if( ctx == NULL ) + return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA ); + + if( ( ret = f_rng( p_rng, ctx->our_secret, MBEDTLS_X25519_KEY_SIZE_BYTES ) ) != 0 ) + return ret; + + *olen = MBEDTLS_X25519_KEY_SIZE_BYTES + 1; + if( blen < *olen ) + return(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL); + *buf++ = MBEDTLS_X25519_KEY_SIZE_BYTES; + + base[0] = 9; + Hacl_Curve25519_crypto_scalarmult( buf, ctx->our_secret, base ); + + base[0] = 0; + if( memcmp( buf, base, MBEDTLS_X25519_KEY_SIZE_BYTES ) == 0 ) + return MBEDTLS_ERR_ECP_RANDOM_FAILED; + + return( ret ); +} + +int mbedtls_x25519_read_public( mbedtls_x25519_context *ctx, + const unsigned char *buf, size_t blen ) +{ + if( blen < MBEDTLS_X25519_KEY_SIZE_BYTES + 1 ) + return(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL); + if( (*buf++ != MBEDTLS_X25519_KEY_SIZE_BYTES) ) + return(MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + memcpy( ctx->peer_point, buf, MBEDTLS_X25519_KEY_SIZE_BYTES ); + return( 0 ); +} + + +#endif /* MBEDTLS_ECDH_C && MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED */ diff --git a/r5dev/thirdparty/mbedtls/CMakeLists.txt b/r5dev/thirdparty/mbedtls/CMakeLists.txt new file mode 100644 index 00000000..9b3c9c86 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/CMakeLists.txt @@ -0,0 +1,277 @@ +cmake_minimum_required( VERSION 3.16 ) + +macro( add_mbed_project PROJECT_NAME ) +add_module( "lib" ${PROJECT_NAME} "" ${FOLDER_CONTEXT} TRUE TRUE ) + +if( ${PROJECT_NAME} STREQUAL "libmbedcrypto" ) +add_sources( SOURCE_GROUP "Source" + "aes.c" + "aesni.c" + "aesce.c" + "aria.c" + "asn1parse.c" + "asn1write.c" + "base64.c" + "bignum.c" + "bignum_core.c" + "bignum_mod.c" + "bignum_mod_raw.c" + "camellia.c" + "ccm.c" + "chacha20.c" + "chachapoly.c" + "cipher.c" + "cipher_wrap.c" + "constant_time.c" + "cmac.c" + "ctr_drbg.c" + "des.c" + "dhm.c" + "ecdh.c" + "ecdsa.c" + "ecjpake.c" + "ecp.c" + "ecp_curves.c" + "entropy.c" + "entropy_poll.c" + "error.c" + "gcm.c" + "hash_info.c" + "hkdf.c" + "hmac_drbg.c" + "lmots.c" + "lms.c" + "md.c" + "md5.c" + "memory_buffer_alloc.c" + "nist_kw.c" + "oid.c" + "padlock.c" + "pem.c" + "pk.c" + "pk_wrap.c" + "pkcs12.c" + "pkcs5.c" + "pkparse.c" + "pkwrite.c" + "platform.c" + "platform_util.c" + "poly1305.c" + "psa_crypto.c" + "psa_crypto_aead.c" + "psa_crypto_cipher.c" + "psa_crypto_client.c" + "psa_crypto_driver_wrappers.c" + "psa_crypto_ecp.c" + "psa_crypto_hash.c" + "psa_crypto_mac.c" + "psa_crypto_pake.c" + "psa_crypto_rsa.c" + "psa_crypto_se.c" + "psa_crypto_slot_management.c" + "psa_crypto_storage.c" + "psa_its_file.c" + "psa_util.c" + "ripemd160.c" + "rsa.c" + "rsa_alt_helpers.c" + "sha1.c" + "sha256.c" + "sha512.c" + "threading.c" + "timing.c" + "version.c" + "version_features.c" +) + +add_sources( SOURCE_GROUP "include/MbedTLS" + "aesce.h" + "aesni.h" + "bignum_core.h" + "bignum_mod.h" + "bignum_mod_raw.h" + "bignum_mod_raw_invasive.h" + "bn_mul.h" + "check_crypto_config.h" + "cipher_wrap.h" + "constant_time_internal.h" + "constant_time_invasive.h" + "ecp_internal_alt.h" + "ecp_invasive.h" + "entropy_poll.h" + "hash_info.h" + "lmots.h" + "md_wrap.h" + "padlock.h" + "pkwrite.h" + "pk_wrap.h" + "rsa_alt_helpers.h" + "include/mbedtls/aes.h" + "include/mbedtls/aria.h" + "include/mbedtls/asn1.h" + "include/mbedtls/asn1write.h" + "include/mbedtls/base64.h" + "include/mbedtls/bignum.h" + "include/mbedtls/build_info.h" + "include/mbedtls/camellia.h" + "include/mbedtls/ccm.h" + "include/mbedtls/chacha20.h" + "include/mbedtls/chachapoly.h" + "include/mbedtls/check_config.h" + "include/mbedtls/cipher.h" + "include/mbedtls/cmac.h" + "include/mbedtls/compat-2.x.h" + "include/mbedtls/config_psa.h" + "include/mbedtls/constant_time.h" + "include/mbedtls/ctr_drbg.h" + "include/mbedtls/debug.h" + "include/mbedtls/des.h" + "include/mbedtls/dhm.h" + "include/mbedtls/ecdh.h" + "include/mbedtls/ecdsa.h" + "include/mbedtls/ecjpake.h" + "include/mbedtls/ecp.h" + "include/mbedtls/entropy.h" + "include/mbedtls/error.h" + "include/mbedtls/gcm.h" + "include/mbedtls/hkdf.h" + "include/mbedtls/hmac_drbg.h" + "include/mbedtls/legacy_or_psa.h" + "include/mbedtls/lms.h" + "include/mbedtls/mbedtls_config.h" + "include/mbedtls/md.h" + "include/mbedtls/md5.h" + "include/mbedtls/memory_buffer_alloc.h" + "include/mbedtls/nist_kw.h" + "include/mbedtls/oid.h" + "include/mbedtls/pem.h" + "include/mbedtls/pk.h" + "include/mbedtls/pkcs12.h" + "include/mbedtls/pkcs5.h" + "include/mbedtls/platform.h" + "include/mbedtls/platform_time.h" + "include/mbedtls/platform_util.h" + "include/mbedtls/poly1305.h" + "include/mbedtls/private_access.h" + "include/mbedtls/psa_util.h" + "include/mbedtls/ripemd160.h" + "include/mbedtls/rsa.h" + "include/mbedtls/sha1.h" + "include/mbedtls/sha256.h" + "include/mbedtls/sha512.h" + "include/mbedtls/ssl.h" + "include/mbedtls/threading.h" + "include/mbedtls/timing.h" + "include/mbedtls/version.h" +) + +add_sources( SOURCE_GROUP "include/PSA" + "psa_crypto_aead.h" + "psa_crypto_cipher.h" + "psa_crypto_core.h" + "psa_crypto_driver_wrappers.h" + "psa_crypto_ecp.h" + "psa_crypto_hash.h" + "psa_crypto_invasive.h" + "psa_crypto_its.h" + "psa_crypto_mac.h" + "psa_crypto_pake.h" + "psa_crypto_random_impl.h" + "psa_crypto_rsa.h" + "psa_crypto_se.h" + "psa_crypto_slot_management.h" + "psa_crypto_storage.h" + "include/psa/crypto.h" + "include/psa/crypto_builtin_composites.h" + "include/psa/crypto_builtin_primitives.h" + "include/psa/crypto_compat.h" + "include/psa/crypto_config.h" + "include/psa/crypto_driver_common.h" + "include/psa/crypto_driver_contexts_composites.h" + "include/psa/crypto_driver_contexts_primitives.h" + "include/psa/crypto_extra.h" + "include/psa/crypto_platform.h" + "include/psa/crypto_se_driver.h" + "include/psa/crypto_sizes.h" + "include/psa/crypto_struct.h" + "include/psa/crypto_types.h" + "include/psa/crypto_values.h" +) +endif() + +if( ${PROJECT_NAME} STREQUAL "libmbedx509" ) +add_sources( SOURCE_GROUP "Source" + "pkcs7.c" + "x509.c" + "x509_create.c" + "x509_crl.c" + "x509_crt.c" + "x509_csr.c" + "x509write_crt.c" + "x509write_csr.c" +) + +add_sources( SOURCE_GROUP "Include" + "include/mbedtls/pkcs7.h" + "include/mbedtls/x509.h" + "include/mbedtls/x509_crl.h" + "include/mbedtls/x509_crt.h" + "include/mbedtls/x509_csr.h" +) +endif() + +if( ${PROJECT_NAME} STREQUAL "libmbedtls" ) +add_sources( SOURCE_GROUP "Source" + "debug.c" + "mps_reader.c" + "mps_trace.c" + "net_sockets.c" + "ssl_cache.c" + "ssl_ciphersuites.c" + "ssl_client.c" + "ssl_cookie.c" + "ssl_debug_helpers_generated.c" + "ssl_msg.c" + "ssl_ticket.c" + "ssl_tls.c" + "ssl_tls12_client.c" + "ssl_tls12_server.c" + "ssl_tls13_keys.c" + "ssl_tls13_server.c" + "ssl_tls13_client.c" + "ssl_tls13_generic.c" +) + +add_sources( SOURCE_GROUP "Include" + "alignment.h" + "common.h" + "mps_common.h" + "mps_error.h" + "mps_reader.h" + "mps_trace.h" + "ssl_client.h" + "ssl_debug_helpers.h" + "ssl_misc.h" + "ssl_tls13_invasive.h" + "ssl_tls13_keys.h" + "include/mbedtls/debug.h" + "include/mbedtls/net_sockets.h" + "include/mbedtls/ssl_cache.h" + "include/mbedtls/ssl_ciphersuites.h" + "include/mbedtls/ssl_cookie.h" + "include/mbedtls/ssl_ticket.h" +) +endif() + +end_sources() + +target_include_directories( ${PROJECT_NAME} PRIVATE + "${THIRDPARTY_SOURCE_DIR}/mbedtls/include/" +) + +thirdparty_suppress_warnings() +endmacro() + +add_mbed_project( "libmbedcrypto" ) +add_mbed_project( "libmbedx509" ) +add_mbed_project( "libmbedtls" ) diff --git a/r5dev/thirdparty/mbedtls/Makefile b/r5dev/thirdparty/mbedtls/Makefile new file mode 100644 index 00000000..160aa6be --- /dev/null +++ b/r5dev/thirdparty/mbedtls/Makefile @@ -0,0 +1,361 @@ + +# Also see "include/mbedtls/mbedtls_config.h" + +CFLAGS ?= -O2 +WARNING_CFLAGS ?= -Wall -Wextra -Wformat=2 -Wno-format-nonliteral +LDFLAGS ?= + +# Include ../include for public headers and . for private headers. +# Note that . needs to be included explicitly for the sake of library +# files that are not in the /library directory (which currently means +# under /3rdparty). +LOCAL_CFLAGS = $(WARNING_CFLAGS) -I. -I../include -D_FILE_OFFSET_BITS=64 +LOCAL_LDFLAGS = + +ifdef DEBUG +LOCAL_CFLAGS += -g3 +endif + +# MicroBlaze specific options: +# CFLAGS += -mno-xl-soft-mul -mxl-barrel-shift + +# To compile on Plan9: +# CFLAGS += -D_BSD_EXTENSION + +PERL ?= perl + +ifdef WINDOWS +PYTHON ?= python +else +PYTHON ?= $(shell if type python3 >/dev/null 2>/dev/null; then echo python3; else echo python; fi) +endif + +# if were running on Windows build for Windows +ifdef WINDOWS +WINDOWS_BUILD=1 +else ifeq ($(shell uname -s),Darwin) +ifeq ($(AR),ar) +APPLE_BUILD ?= 1 +endif +endif + +# To compile as a shared library: +ifdef SHARED +# all code is position-indep with mingw, avoid warning about useless flag +ifndef WINDOWS_BUILD +LOCAL_CFLAGS += -fPIC -fpic +endif +endif + +SOEXT_TLS?=so.19 +SOEXT_X509?=so.5 +SOEXT_CRYPTO?=so.14 + +# Set AR_DASH= (empty string) to use an ar implementation that does not accept +# the - prefix for command line options (e.g. llvm-ar) +AR_DASH ?= - + +ARFLAGS = $(AR_DASH)src +ifdef APPLE_BUILD +ifneq ($(APPLE_BUILD),0) +ARFLAGS = $(AR_DASH)Src +RLFLAGS = -no_warning_for_no_symbols -c +RL ?= ranlib +endif +endif + +DLEXT ?= so +ifdef WINDOWS_BUILD +# Windows shared library extension: +DLEXT = dll +else ifdef APPLE_BUILD +ifneq ($(APPLE_BUILD),0) +# Mac OS X shared library extension: +DLEXT = dylib +endif +endif + +OBJS_CRYPTO= \ + aes.o \ + aesni.o \ + aesce.o \ + aria.o \ + asn1parse.o \ + asn1write.o \ + base64.o \ + bignum.o \ + bignum_core.o \ + bignum_mod.o \ + bignum_mod_raw.o \ + camellia.o \ + ccm.o \ + chacha20.o \ + chachapoly.o \ + cipher.o \ + cipher_wrap.o \ + cmac.o \ + constant_time.o \ + ctr_drbg.o \ + des.o \ + dhm.o \ + ecdh.o \ + ecdsa.o \ + ecjpake.o \ + ecp.o \ + ecp_curves.o \ + entropy.o \ + entropy_poll.o \ + error.o \ + gcm.o \ + hash_info.o \ + hkdf.o \ + hmac_drbg.o \ + lmots.o \ + lms.o \ + md.o \ + md5.o \ + memory_buffer_alloc.o \ + nist_kw.o \ + oid.o \ + padlock.o \ + pem.o \ + pk.o \ + pk_wrap.o \ + pkcs12.o \ + pkcs5.o \ + pkparse.o \ + pkwrite.o \ + platform.o \ + platform_util.o \ + poly1305.o \ + psa_crypto.o \ + psa_crypto_aead.o \ + psa_crypto_cipher.o \ + psa_crypto_client.o \ + psa_crypto_driver_wrappers.o \ + psa_crypto_ecp.o \ + psa_crypto_hash.o \ + psa_crypto_mac.o \ + psa_crypto_pake.o \ + psa_crypto_rsa.o \ + psa_crypto_se.o \ + psa_crypto_slot_management.o \ + psa_crypto_storage.o \ + psa_its_file.o \ + psa_util.o \ + ripemd160.o \ + rsa.o \ + rsa_alt_helpers.o \ + sha1.o \ + sha256.o \ + sha512.o \ + threading.o \ + timing.o \ + version.o \ + version_features.o \ + # This line is intentionally left blank + +include ../3rdparty/Makefile.inc +LOCAL_CFLAGS+=$(THIRDPARTY_INCLUDES) +OBJS_CRYPTO+=$(THIRDPARTY_CRYPTO_OBJECTS) + +OBJS_X509= \ + x509.o \ + x509_create.o \ + x509_crl.o \ + x509_crt.o \ + x509_csr.o \ + x509write_crt.o \ + x509write_csr.o \ + pkcs7.o \ + # This line is intentionally left blank + +OBJS_TLS= \ + debug.o \ + mps_reader.o \ + mps_trace.o \ + net_sockets.o \ + ssl_cache.o \ + ssl_ciphersuites.o \ + ssl_client.o \ + ssl_cookie.o \ + ssl_debug_helpers_generated.o \ + ssl_msg.o \ + ssl_ticket.o \ + ssl_tls.o \ + ssl_tls12_client.o \ + ssl_tls12_server.o \ + ssl_tls13_keys.o \ + ssl_tls13_client.o \ + ssl_tls13_server.o \ + ssl_tls13_generic.o \ + # This line is intentionally left blank + +.SILENT: + +.PHONY: all static shared clean + +ifndef SHARED +all: static +else +all: shared static +endif + +static: libmbedcrypto.a libmbedx509.a libmbedtls.a + cd ../tests && echo "This is a seedfile that contains 64 bytes (65 on Windows)......" > seedfile + +shared: libmbedcrypto.$(DLEXT) libmbedx509.$(DLEXT) libmbedtls.$(DLEXT) + +# Windows builds under Mingw can fail if make tries to create archives in the same +# directory at the same time - see https://bugs.launchpad.net/gcc-arm-embedded/+bug/1848002. +# This forces builds of the .a files to be serialised. +ifdef WINDOWS +libmbedtls.a: | libmbedx509.a +libmbedx509.a: | libmbedcrypto.a +endif + +# tls +libmbedtls.a: $(OBJS_TLS) + echo " AR $@" + $(AR) $(ARFLAGS) $@ $(OBJS_TLS) +ifdef APPLE_BUILD +ifneq ($(APPLE_BUILD),0) + echo " RL $@" + $(RL) $(RLFLAGS) $@ +endif +endif + +libmbedtls.$(SOEXT_TLS): $(OBJS_TLS) libmbedx509.so + echo " LD $@" + $(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS_TLS) -L. -lmbedx509 -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS) + +ifneq ($(SOEXT_TLS),so) +libmbedtls.so: libmbedtls.$(SOEXT_TLS) + echo " LN $@ -> $<" + ln -sf $< $@ +endif + +libmbedtls.dylib: $(OBJS_TLS) libmbedx509.dylib + echo " LD $@" + $(CC) -dynamiclib -o $@ $(OBJS_TLS) -L. -lmbedx509 -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS) + +libmbedtls.dll: $(OBJS_TLS) libmbedx509.dll + echo " LD $@" + $(CC) -shared -Wl,-soname,$@ -Wl,--out-implib,$@.a -o $@ $(OBJS_TLS) -lws2_32 -lwinmm -lgdi32 -L. -lmbedx509 -lmbedcrypto -static-libgcc $(LOCAL_LDFLAGS) $(LDFLAGS) + +# x509 +libmbedx509.a: $(OBJS_X509) + echo " AR $@" + $(AR) $(ARFLAGS) $@ $(OBJS_X509) +ifdef APPLE_BUILD +ifneq ($(APPLE_BUILD),0) + echo " RL $@" + $(RL) $(RLFLAGS) $@ +endif +endif + +libmbedx509.$(SOEXT_X509): $(OBJS_X509) libmbedcrypto.so + echo " LD $@" + $(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS_X509) -L. -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS) + +ifneq ($(SOEXT_X509),so) +libmbedx509.so: libmbedx509.$(SOEXT_X509) + echo " LN $@ -> $<" + ln -sf $< $@ +endif + +libmbedx509.dylib: $(OBJS_X509) libmbedcrypto.dylib + echo " LD $@" + $(CC) -dynamiclib -o $@ $(OBJS_X509) -L. -lmbedcrypto $(LOCAL_LDFLAGS) $(LDFLAGS) + +libmbedx509.dll: $(OBJS_X509) libmbedcrypto.dll + echo " LD $@" + $(CC) -shared -Wl,-soname,$@ -Wl,--out-implib,$@.a -o $@ $(OBJS_X509) -lws2_32 -lwinmm -lgdi32 -L. -lmbedcrypto -static-libgcc $(LOCAL_LDFLAGS) $(LDFLAGS) + +# crypto +libmbedcrypto.a: $(OBJS_CRYPTO) + echo " AR $@" + $(AR) $(ARFLAGS) $@ $(OBJS_CRYPTO) +ifdef APPLE_BUILD +ifneq ($(APPLE_BUILD),0) + echo " RL $@" + $(RL) $(RLFLAGS) $@ +endif +endif + +libmbedcrypto.$(SOEXT_CRYPTO): $(OBJS_CRYPTO) + echo " LD $@" + $(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS_CRYPTO) $(LOCAL_LDFLAGS) $(LDFLAGS) + +ifneq ($(SOEXT_CRYPTO),so) +libmbedcrypto.so: libmbedcrypto.$(SOEXT_CRYPTO) + echo " LN $@ -> $<" + ln -sf $< $@ +endif + +libmbedcrypto.dylib: $(OBJS_CRYPTO) + echo " LD $@" + $(CC) -dynamiclib -o $@ $(OBJS_CRYPTO) $(LOCAL_LDFLAGS) $(LDFLAGS) + +libmbedcrypto.dll: $(OBJS_CRYPTO) + echo " LD $@" + $(CC) -shared -Wl,-soname,$@ -Wl,--out-implib,$@.a -o $@ $(OBJS_CRYPTO) -lws2_32 -lwinmm -lgdi32 -static-libgcc $(LOCAL_LDFLAGS) $(LDFLAGS) + +.c.o: + echo " CC $<" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) -o $@ -c $< + +.PHONY: generated_files +GENERATED_FILES = \ + error.c version_features.c \ + ssl_debug_helpers_generated.c \ + psa_crypto_driver_wrappers.c +generated_files: $(GENERATED_FILES) + +error.c: ../scripts/generate_errors.pl +error.c: ../scripts/data_files/error.fmt +error.c: $(filter-out %config%,$(wildcard ../include/mbedtls/*.h)) +error.c: + echo " Gen $@" + $(PERL) ../scripts/generate_errors.pl + +ssl_debug_helpers_generated.c: ../scripts/generate_ssl_debug_helpers.py +ssl_debug_helpers_generated.c: $(filter-out %config%,$(wildcard ../include/mbedtls/*.h)) +ssl_debug_helpers_generated.c: + echo " Gen $@" + $(PYTHON) ../scripts/generate_ssl_debug_helpers.py --mbedtls-root .. . + +version_features.c: ../scripts/generate_features.pl +version_features.c: ../scripts/data_files/version_features.fmt +## The generated file only depends on the options that are present in mbedtls_config.h, +## not on which options are set. To avoid regenerating this file all the time +## when switching between configurations, don't declare mbedtls_config.h as a +## dependency. Remove this file from your working tree if you've just added or +## removed an option in mbedtls_config.h. +#version_features.c: ../include/mbedtls/mbedtls_config.h +version_features.c: + echo " Gen $@" + $(PERL) ../scripts/generate_features.pl + +psa_crypto_driver_wrappers.c: ../scripts/generate_driver_wrappers.py +psa_crypto_driver_wrappers.c: ../scripts/data_files/driver_templates/psa_crypto_driver_wrappers.c.jinja +psa_crypto_driver_wrappers.c: + echo " Gen $@" + $(PYTHON) ../scripts/generate_driver_wrappers.py + +clean: +ifndef WINDOWS + rm -f *.o libmbed* + rm -f $(THIRDPARTY_CRYPTO_OBJECTS) +else + if exist *.o del /Q /F *.o + if exist libmbed* del /Q /F libmbed* + del /Q /F del_errors_out_if_the_file_list_is_empty_but_not_if_a_file_does_not_exist $(subst /,\,$(THIRDPARTY_CRYPTO_OBJECTS)) +endif + +neat: clean +ifndef WINDOWS + rm -f $(GENERATED_FILES) +else + for %f in ($(subst /,\,$(GENERATED_FILES))) if exist %f del /Q /F %f +endif diff --git a/r5dev/thirdparty/mbedtls/aes.c b/r5dev/thirdparty/mbedtls/aes.c new file mode 100644 index 00000000..69da5828 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/aes.c @@ -0,0 +1,2178 @@ +/* + * FIPS-197 compliant AES implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The AES block cipher was designed by Vincent Rijmen and Joan Daemen. + * + * http://csrc.nist.gov/encryption/aes/rijndael/Rijndael.pdf + * http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_AES_C) + +#include + +#include "mbedtls/aes.h" +#include "mbedtls/platform.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#if defined(MBEDTLS_PADLOCK_C) +#include "padlock.h" +#endif +#if defined(MBEDTLS_AESNI_C) +#include "aesni.h" +#endif +#if defined(MBEDTLS_AESCE_C) +#include "aesce.h" +#endif + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_AES_ALT) + +#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86) +static int aes_padlock_ace = -1; +#endif + +#if defined(MBEDTLS_AES_ROM_TABLES) +/* + * Forward S-box + */ +static const unsigned char FSb[256] = +{ + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, + 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, + 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, + 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, + 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, + 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, + 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, + 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, + 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, + 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, + 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, + 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; + +/* + * Forward tables + */ +#define FT \ +\ + V(A5, 63, 63, C6), V(84, 7C, 7C, F8), V(99, 77, 77, EE), V(8D, 7B, 7B, F6), \ + V(0D, F2, F2, FF), V(BD, 6B, 6B, D6), V(B1, 6F, 6F, DE), V(54, C5, C5, 91), \ + V(50, 30, 30, 60), V(03, 01, 01, 02), V(A9, 67, 67, CE), V(7D, 2B, 2B, 56), \ + V(19, FE, FE, E7), V(62, D7, D7, B5), V(E6, AB, AB, 4D), V(9A, 76, 76, EC), \ + V(45, CA, CA, 8F), V(9D, 82, 82, 1F), V(40, C9, C9, 89), V(87, 7D, 7D, FA), \ + V(15, FA, FA, EF), V(EB, 59, 59, B2), V(C9, 47, 47, 8E), V(0B, F0, F0, FB), \ + V(EC, AD, AD, 41), V(67, D4, D4, B3), V(FD, A2, A2, 5F), V(EA, AF, AF, 45), \ + V(BF, 9C, 9C, 23), V(F7, A4, A4, 53), V(96, 72, 72, E4), V(5B, C0, C0, 9B), \ + V(C2, B7, B7, 75), V(1C, FD, FD, E1), V(AE, 93, 93, 3D), V(6A, 26, 26, 4C), \ + V(5A, 36, 36, 6C), V(41, 3F, 3F, 7E), V(02, F7, F7, F5), V(4F, CC, CC, 83), \ + V(5C, 34, 34, 68), V(F4, A5, A5, 51), V(34, E5, E5, D1), V(08, F1, F1, F9), \ + V(93, 71, 71, E2), V(73, D8, D8, AB), V(53, 31, 31, 62), V(3F, 15, 15, 2A), \ + V(0C, 04, 04, 08), V(52, C7, C7, 95), V(65, 23, 23, 46), V(5E, C3, C3, 9D), \ + V(28, 18, 18, 30), V(A1, 96, 96, 37), V(0F, 05, 05, 0A), V(B5, 9A, 9A, 2F), \ + V(09, 07, 07, 0E), V(36, 12, 12, 24), V(9B, 80, 80, 1B), V(3D, E2, E2, DF), \ + V(26, EB, EB, CD), V(69, 27, 27, 4E), V(CD, B2, B2, 7F), V(9F, 75, 75, EA), \ + V(1B, 09, 09, 12), V(9E, 83, 83, 1D), V(74, 2C, 2C, 58), V(2E, 1A, 1A, 34), \ + V(2D, 1B, 1B, 36), V(B2, 6E, 6E, DC), V(EE, 5A, 5A, B4), V(FB, A0, A0, 5B), \ + V(F6, 52, 52, A4), V(4D, 3B, 3B, 76), V(61, D6, D6, B7), V(CE, B3, B3, 7D), \ + V(7B, 29, 29, 52), V(3E, E3, E3, DD), V(71, 2F, 2F, 5E), V(97, 84, 84, 13), \ + V(F5, 53, 53, A6), V(68, D1, D1, B9), V(00, 00, 00, 00), V(2C, ED, ED, C1), \ + V(60, 20, 20, 40), V(1F, FC, FC, E3), V(C8, B1, B1, 79), V(ED, 5B, 5B, B6), \ + V(BE, 6A, 6A, D4), V(46, CB, CB, 8D), V(D9, BE, BE, 67), V(4B, 39, 39, 72), \ + V(DE, 4A, 4A, 94), V(D4, 4C, 4C, 98), V(E8, 58, 58, B0), V(4A, CF, CF, 85), \ + V(6B, D0, D0, BB), V(2A, EF, EF, C5), V(E5, AA, AA, 4F), V(16, FB, FB, ED), \ + V(C5, 43, 43, 86), V(D7, 4D, 4D, 9A), V(55, 33, 33, 66), V(94, 85, 85, 11), \ + V(CF, 45, 45, 8A), V(10, F9, F9, E9), V(06, 02, 02, 04), V(81, 7F, 7F, FE), \ + V(F0, 50, 50, A0), V(44, 3C, 3C, 78), V(BA, 9F, 9F, 25), V(E3, A8, A8, 4B), \ + V(F3, 51, 51, A2), V(FE, A3, A3, 5D), V(C0, 40, 40, 80), V(8A, 8F, 8F, 05), \ + V(AD, 92, 92, 3F), V(BC, 9D, 9D, 21), V(48, 38, 38, 70), V(04, F5, F5, F1), \ + V(DF, BC, BC, 63), V(C1, B6, B6, 77), V(75, DA, DA, AF), V(63, 21, 21, 42), \ + V(30, 10, 10, 20), V(1A, FF, FF, E5), V(0E, F3, F3, FD), V(6D, D2, D2, BF), \ + V(4C, CD, CD, 81), V(14, 0C, 0C, 18), V(35, 13, 13, 26), V(2F, EC, EC, C3), \ + V(E1, 5F, 5F, BE), V(A2, 97, 97, 35), V(CC, 44, 44, 88), V(39, 17, 17, 2E), \ + V(57, C4, C4, 93), V(F2, A7, A7, 55), V(82, 7E, 7E, FC), V(47, 3D, 3D, 7A), \ + V(AC, 64, 64, C8), V(E7, 5D, 5D, BA), V(2B, 19, 19, 32), V(95, 73, 73, E6), \ + V(A0, 60, 60, C0), V(98, 81, 81, 19), V(D1, 4F, 4F, 9E), V(7F, DC, DC, A3), \ + V(66, 22, 22, 44), V(7E, 2A, 2A, 54), V(AB, 90, 90, 3B), V(83, 88, 88, 0B), \ + V(CA, 46, 46, 8C), V(29, EE, EE, C7), V(D3, B8, B8, 6B), V(3C, 14, 14, 28), \ + V(79, DE, DE, A7), V(E2, 5E, 5E, BC), V(1D, 0B, 0B, 16), V(76, DB, DB, AD), \ + V(3B, E0, E0, DB), V(56, 32, 32, 64), V(4E, 3A, 3A, 74), V(1E, 0A, 0A, 14), \ + V(DB, 49, 49, 92), V(0A, 06, 06, 0C), V(6C, 24, 24, 48), V(E4, 5C, 5C, B8), \ + V(5D, C2, C2, 9F), V(6E, D3, D3, BD), V(EF, AC, AC, 43), V(A6, 62, 62, C4), \ + V(A8, 91, 91, 39), V(A4, 95, 95, 31), V(37, E4, E4, D3), V(8B, 79, 79, F2), \ + V(32, E7, E7, D5), V(43, C8, C8, 8B), V(59, 37, 37, 6E), V(B7, 6D, 6D, DA), \ + V(8C, 8D, 8D, 01), V(64, D5, D5, B1), V(D2, 4E, 4E, 9C), V(E0, A9, A9, 49), \ + V(B4, 6C, 6C, D8), V(FA, 56, 56, AC), V(07, F4, F4, F3), V(25, EA, EA, CF), \ + V(AF, 65, 65, CA), V(8E, 7A, 7A, F4), V(E9, AE, AE, 47), V(18, 08, 08, 10), \ + V(D5, BA, BA, 6F), V(88, 78, 78, F0), V(6F, 25, 25, 4A), V(72, 2E, 2E, 5C), \ + V(24, 1C, 1C, 38), V(F1, A6, A6, 57), V(C7, B4, B4, 73), V(51, C6, C6, 97), \ + V(23, E8, E8, CB), V(7C, DD, DD, A1), V(9C, 74, 74, E8), V(21, 1F, 1F, 3E), \ + V(DD, 4B, 4B, 96), V(DC, BD, BD, 61), V(86, 8B, 8B, 0D), V(85, 8A, 8A, 0F), \ + V(90, 70, 70, E0), V(42, 3E, 3E, 7C), V(C4, B5, B5, 71), V(AA, 66, 66, CC), \ + V(D8, 48, 48, 90), V(05, 03, 03, 06), V(01, F6, F6, F7), V(12, 0E, 0E, 1C), \ + V(A3, 61, 61, C2), V(5F, 35, 35, 6A), V(F9, 57, 57, AE), V(D0, B9, B9, 69), \ + V(91, 86, 86, 17), V(58, C1, C1, 99), V(27, 1D, 1D, 3A), V(B9, 9E, 9E, 27), \ + V(38, E1, E1, D9), V(13, F8, F8, EB), V(B3, 98, 98, 2B), V(33, 11, 11, 22), \ + V(BB, 69, 69, D2), V(70, D9, D9, A9), V(89, 8E, 8E, 07), V(A7, 94, 94, 33), \ + V(B6, 9B, 9B, 2D), V(22, 1E, 1E, 3C), V(92, 87, 87, 15), V(20, E9, E9, C9), \ + V(49, CE, CE, 87), V(FF, 55, 55, AA), V(78, 28, 28, 50), V(7A, DF, DF, A5), \ + V(8F, 8C, 8C, 03), V(F8, A1, A1, 59), V(80, 89, 89, 09), V(17, 0D, 0D, 1A), \ + V(DA, BF, BF, 65), V(31, E6, E6, D7), V(C6, 42, 42, 84), V(B8, 68, 68, D0), \ + V(C3, 41, 41, 82), V(B0, 99, 99, 29), V(77, 2D, 2D, 5A), V(11, 0F, 0F, 1E), \ + V(CB, B0, B0, 7B), V(FC, 54, 54, A8), V(D6, BB, BB, 6D), V(3A, 16, 16, 2C) + +#define V(a, b, c, d) 0x##a##b##c##d +static const uint32_t FT0[256] = { FT }; +#undef V + +#if !defined(MBEDTLS_AES_FEWER_TABLES) + +#define V(a, b, c, d) 0x##b##c##d##a +static const uint32_t FT1[256] = { FT }; +#undef V + +#define V(a, b, c, d) 0x##c##d##a##b +static const uint32_t FT2[256] = { FT }; +#undef V + +#define V(a, b, c, d) 0x##d##a##b##c +static const uint32_t FT3[256] = { FT }; +#undef V + +#endif /* !MBEDTLS_AES_FEWER_TABLES */ + +#undef FT + +/* + * Reverse S-box + */ +static const unsigned char RSb[256] = +{ + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, + 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, + 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, + 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, + 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, + 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, + 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, + 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, + 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, + 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, + 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, + 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D +}; + +/* + * Reverse tables + */ +#define RT \ +\ + V(50, A7, F4, 51), V(53, 65, 41, 7E), V(C3, A4, 17, 1A), V(96, 5E, 27, 3A), \ + V(CB, 6B, AB, 3B), V(F1, 45, 9D, 1F), V(AB, 58, FA, AC), V(93, 03, E3, 4B), \ + V(55, FA, 30, 20), V(F6, 6D, 76, AD), V(91, 76, CC, 88), V(25, 4C, 02, F5), \ + V(FC, D7, E5, 4F), V(D7, CB, 2A, C5), V(80, 44, 35, 26), V(8F, A3, 62, B5), \ + V(49, 5A, B1, DE), V(67, 1B, BA, 25), V(98, 0E, EA, 45), V(E1, C0, FE, 5D), \ + V(02, 75, 2F, C3), V(12, F0, 4C, 81), V(A3, 97, 46, 8D), V(C6, F9, D3, 6B), \ + V(E7, 5F, 8F, 03), V(95, 9C, 92, 15), V(EB, 7A, 6D, BF), V(DA, 59, 52, 95), \ + V(2D, 83, BE, D4), V(D3, 21, 74, 58), V(29, 69, E0, 49), V(44, C8, C9, 8E), \ + V(6A, 89, C2, 75), V(78, 79, 8E, F4), V(6B, 3E, 58, 99), V(DD, 71, B9, 27), \ + V(B6, 4F, E1, BE), V(17, AD, 88, F0), V(66, AC, 20, C9), V(B4, 3A, CE, 7D), \ + V(18, 4A, DF, 63), V(82, 31, 1A, E5), V(60, 33, 51, 97), V(45, 7F, 53, 62), \ + V(E0, 77, 64, B1), V(84, AE, 6B, BB), V(1C, A0, 81, FE), V(94, 2B, 08, F9), \ + V(58, 68, 48, 70), V(19, FD, 45, 8F), V(87, 6C, DE, 94), V(B7, F8, 7B, 52), \ + V(23, D3, 73, AB), V(E2, 02, 4B, 72), V(57, 8F, 1F, E3), V(2A, AB, 55, 66), \ + V(07, 28, EB, B2), V(03, C2, B5, 2F), V(9A, 7B, C5, 86), V(A5, 08, 37, D3), \ + V(F2, 87, 28, 30), V(B2, A5, BF, 23), V(BA, 6A, 03, 02), V(5C, 82, 16, ED), \ + V(2B, 1C, CF, 8A), V(92, B4, 79, A7), V(F0, F2, 07, F3), V(A1, E2, 69, 4E), \ + V(CD, F4, DA, 65), V(D5, BE, 05, 06), V(1F, 62, 34, D1), V(8A, FE, A6, C4), \ + V(9D, 53, 2E, 34), V(A0, 55, F3, A2), V(32, E1, 8A, 05), V(75, EB, F6, A4), \ + V(39, EC, 83, 0B), V(AA, EF, 60, 40), V(06, 9F, 71, 5E), V(51, 10, 6E, BD), \ + V(F9, 8A, 21, 3E), V(3D, 06, DD, 96), V(AE, 05, 3E, DD), V(46, BD, E6, 4D), \ + V(B5, 8D, 54, 91), V(05, 5D, C4, 71), V(6F, D4, 06, 04), V(FF, 15, 50, 60), \ + V(24, FB, 98, 19), V(97, E9, BD, D6), V(CC, 43, 40, 89), V(77, 9E, D9, 67), \ + V(BD, 42, E8, B0), V(88, 8B, 89, 07), V(38, 5B, 19, E7), V(DB, EE, C8, 79), \ + V(47, 0A, 7C, A1), V(E9, 0F, 42, 7C), V(C9, 1E, 84, F8), V(00, 00, 00, 00), \ + V(83, 86, 80, 09), V(48, ED, 2B, 32), V(AC, 70, 11, 1E), V(4E, 72, 5A, 6C), \ + V(FB, FF, 0E, FD), V(56, 38, 85, 0F), V(1E, D5, AE, 3D), V(27, 39, 2D, 36), \ + V(64, D9, 0F, 0A), V(21, A6, 5C, 68), V(D1, 54, 5B, 9B), V(3A, 2E, 36, 24), \ + V(B1, 67, 0A, 0C), V(0F, E7, 57, 93), V(D2, 96, EE, B4), V(9E, 91, 9B, 1B), \ + V(4F, C5, C0, 80), V(A2, 20, DC, 61), V(69, 4B, 77, 5A), V(16, 1A, 12, 1C), \ + V(0A, BA, 93, E2), V(E5, 2A, A0, C0), V(43, E0, 22, 3C), V(1D, 17, 1B, 12), \ + V(0B, 0D, 09, 0E), V(AD, C7, 8B, F2), V(B9, A8, B6, 2D), V(C8, A9, 1E, 14), \ + V(85, 19, F1, 57), V(4C, 07, 75, AF), V(BB, DD, 99, EE), V(FD, 60, 7F, A3), \ + V(9F, 26, 01, F7), V(BC, F5, 72, 5C), V(C5, 3B, 66, 44), V(34, 7E, FB, 5B), \ + V(76, 29, 43, 8B), V(DC, C6, 23, CB), V(68, FC, ED, B6), V(63, F1, E4, B8), \ + V(CA, DC, 31, D7), V(10, 85, 63, 42), V(40, 22, 97, 13), V(20, 11, C6, 84), \ + V(7D, 24, 4A, 85), V(F8, 3D, BB, D2), V(11, 32, F9, AE), V(6D, A1, 29, C7), \ + V(4B, 2F, 9E, 1D), V(F3, 30, B2, DC), V(EC, 52, 86, 0D), V(D0, E3, C1, 77), \ + V(6C, 16, B3, 2B), V(99, B9, 70, A9), V(FA, 48, 94, 11), V(22, 64, E9, 47), \ + V(C4, 8C, FC, A8), V(1A, 3F, F0, A0), V(D8, 2C, 7D, 56), V(EF, 90, 33, 22), \ + V(C7, 4E, 49, 87), V(C1, D1, 38, D9), V(FE, A2, CA, 8C), V(36, 0B, D4, 98), \ + V(CF, 81, F5, A6), V(28, DE, 7A, A5), V(26, 8E, B7, DA), V(A4, BF, AD, 3F), \ + V(E4, 9D, 3A, 2C), V(0D, 92, 78, 50), V(9B, CC, 5F, 6A), V(62, 46, 7E, 54), \ + V(C2, 13, 8D, F6), V(E8, B8, D8, 90), V(5E, F7, 39, 2E), V(F5, AF, C3, 82), \ + V(BE, 80, 5D, 9F), V(7C, 93, D0, 69), V(A9, 2D, D5, 6F), V(B3, 12, 25, CF), \ + V(3B, 99, AC, C8), V(A7, 7D, 18, 10), V(6E, 63, 9C, E8), V(7B, BB, 3B, DB), \ + V(09, 78, 26, CD), V(F4, 18, 59, 6E), V(01, B7, 9A, EC), V(A8, 9A, 4F, 83), \ + V(65, 6E, 95, E6), V(7E, E6, FF, AA), V(08, CF, BC, 21), V(E6, E8, 15, EF), \ + V(D9, 9B, E7, BA), V(CE, 36, 6F, 4A), V(D4, 09, 9F, EA), V(D6, 7C, B0, 29), \ + V(AF, B2, A4, 31), V(31, 23, 3F, 2A), V(30, 94, A5, C6), V(C0, 66, A2, 35), \ + V(37, BC, 4E, 74), V(A6, CA, 82, FC), V(B0, D0, 90, E0), V(15, D8, A7, 33), \ + V(4A, 98, 04, F1), V(F7, DA, EC, 41), V(0E, 50, CD, 7F), V(2F, F6, 91, 17), \ + V(8D, D6, 4D, 76), V(4D, B0, EF, 43), V(54, 4D, AA, CC), V(DF, 04, 96, E4), \ + V(E3, B5, D1, 9E), V(1B, 88, 6A, 4C), V(B8, 1F, 2C, C1), V(7F, 51, 65, 46), \ + V(04, EA, 5E, 9D), V(5D, 35, 8C, 01), V(73, 74, 87, FA), V(2E, 41, 0B, FB), \ + V(5A, 1D, 67, B3), V(52, D2, DB, 92), V(33, 56, 10, E9), V(13, 47, D6, 6D), \ + V(8C, 61, D7, 9A), V(7A, 0C, A1, 37), V(8E, 14, F8, 59), V(89, 3C, 13, EB), \ + V(EE, 27, A9, CE), V(35, C9, 61, B7), V(ED, E5, 1C, E1), V(3C, B1, 47, 7A), \ + V(59, DF, D2, 9C), V(3F, 73, F2, 55), V(79, CE, 14, 18), V(BF, 37, C7, 73), \ + V(EA, CD, F7, 53), V(5B, AA, FD, 5F), V(14, 6F, 3D, DF), V(86, DB, 44, 78), \ + V(81, F3, AF, CA), V(3E, C4, 68, B9), V(2C, 34, 24, 38), V(5F, 40, A3, C2), \ + V(72, C3, 1D, 16), V(0C, 25, E2, BC), V(8B, 49, 3C, 28), V(41, 95, 0D, FF), \ + V(71, 01, A8, 39), V(DE, B3, 0C, 08), V(9C, E4, B4, D8), V(90, C1, 56, 64), \ + V(61, 84, CB, 7B), V(70, B6, 32, D5), V(74, 5C, 6C, 48), V(42, 57, B8, D0) + +#define V(a, b, c, d) 0x##a##b##c##d +static const uint32_t RT0[256] = { RT }; +#undef V + +#if !defined(MBEDTLS_AES_FEWER_TABLES) + +#define V(a, b, c, d) 0x##b##c##d##a +static const uint32_t RT1[256] = { RT }; +#undef V + +#define V(a, b, c, d) 0x##c##d##a##b +static const uint32_t RT2[256] = { RT }; +#undef V + +#define V(a, b, c, d) 0x##d##a##b##c +static const uint32_t RT3[256] = { RT }; +#undef V + +#endif /* !MBEDTLS_AES_FEWER_TABLES */ + +#undef RT + +/* + * Round constants + */ +static const uint32_t RCON[10] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x0000001B, 0x00000036 +}; + +#else /* MBEDTLS_AES_ROM_TABLES */ + +/* + * Forward S-box & tables + */ +static unsigned char FSb[256]; +static uint32_t FT0[256]; +#if !defined(MBEDTLS_AES_FEWER_TABLES) +static uint32_t FT1[256]; +static uint32_t FT2[256]; +static uint32_t FT3[256]; +#endif /* !MBEDTLS_AES_FEWER_TABLES */ + +/* + * Reverse S-box & tables + */ +static unsigned char RSb[256]; +static uint32_t RT0[256]; +#if !defined(MBEDTLS_AES_FEWER_TABLES) +static uint32_t RT1[256]; +static uint32_t RT2[256]; +static uint32_t RT3[256]; +#endif /* !MBEDTLS_AES_FEWER_TABLES */ + +/* + * Round constants + */ +static uint32_t RCON[10]; + +/* + * Tables generation code + */ +#define ROTL8(x) (((x) << 8) & 0xFFFFFFFF) | ((x) >> 24) +#define XTIME(x) (((x) << 1) ^ (((x) & 0x80) ? 0x1B : 0x00)) +#define MUL(x, y) (((x) && (y)) ? pow[(log[(x)]+log[(y)]) % 255] : 0) + +static int aes_init_done = 0; + +static void aes_gen_tables(void) +{ + int i, x, y, z; + int pow[256]; + int log[256]; + + /* + * compute pow and log tables over GF(2^8) + */ + for (i = 0, x = 1; i < 256; i++) { + pow[i] = x; + log[x] = i; + x = MBEDTLS_BYTE_0(x ^ XTIME(x)); + } + + /* + * calculate the round constants + */ + for (i = 0, x = 1; i < 10; i++) { + RCON[i] = (uint32_t) x; + x = MBEDTLS_BYTE_0(XTIME(x)); + } + + /* + * generate the forward and reverse S-boxes + */ + FSb[0x00] = 0x63; + RSb[0x63] = 0x00; + + for (i = 1; i < 256; i++) { + x = pow[255 - log[i]]; + + y = x; y = MBEDTLS_BYTE_0((y << 1) | (y >> 7)); + x ^= y; y = MBEDTLS_BYTE_0((y << 1) | (y >> 7)); + x ^= y; y = MBEDTLS_BYTE_0((y << 1) | (y >> 7)); + x ^= y; y = MBEDTLS_BYTE_0((y << 1) | (y >> 7)); + x ^= y ^ 0x63; + + FSb[i] = (unsigned char) x; + RSb[x] = (unsigned char) i; + } + + /* + * generate the forward and reverse tables + */ + for (i = 0; i < 256; i++) { + x = FSb[i]; + y = MBEDTLS_BYTE_0(XTIME(x)); + z = MBEDTLS_BYTE_0(y ^ x); + + FT0[i] = ((uint32_t) y) ^ + ((uint32_t) x << 8) ^ + ((uint32_t) x << 16) ^ + ((uint32_t) z << 24); + +#if !defined(MBEDTLS_AES_FEWER_TABLES) + FT1[i] = ROTL8(FT0[i]); + FT2[i] = ROTL8(FT1[i]); + FT3[i] = ROTL8(FT2[i]); +#endif /* !MBEDTLS_AES_FEWER_TABLES */ + + x = RSb[i]; + + RT0[i] = ((uint32_t) MUL(0x0E, x)) ^ + ((uint32_t) MUL(0x09, x) << 8) ^ + ((uint32_t) MUL(0x0D, x) << 16) ^ + ((uint32_t) MUL(0x0B, x) << 24); + +#if !defined(MBEDTLS_AES_FEWER_TABLES) + RT1[i] = ROTL8(RT0[i]); + RT2[i] = ROTL8(RT1[i]); + RT3[i] = ROTL8(RT2[i]); +#endif /* !MBEDTLS_AES_FEWER_TABLES */ + } +} + +#undef ROTL8 + +#endif /* MBEDTLS_AES_ROM_TABLES */ + +#if defined(MBEDTLS_AES_FEWER_TABLES) + +#define ROTL8(x) ((uint32_t) ((x) << 8) + (uint32_t) ((x) >> 24)) +#define ROTL16(x) ((uint32_t) ((x) << 16) + (uint32_t) ((x) >> 16)) +#define ROTL24(x) ((uint32_t) ((x) << 24) + (uint32_t) ((x) >> 8)) + +#define AES_RT0(idx) RT0[idx] +#define AES_RT1(idx) ROTL8(RT0[idx]) +#define AES_RT2(idx) ROTL16(RT0[idx]) +#define AES_RT3(idx) ROTL24(RT0[idx]) + +#define AES_FT0(idx) FT0[idx] +#define AES_FT1(idx) ROTL8(FT0[idx]) +#define AES_FT2(idx) ROTL16(FT0[idx]) +#define AES_FT3(idx) ROTL24(FT0[idx]) + +#else /* MBEDTLS_AES_FEWER_TABLES */ + +#define AES_RT0(idx) RT0[idx] +#define AES_RT1(idx) RT1[idx] +#define AES_RT2(idx) RT2[idx] +#define AES_RT3(idx) RT3[idx] + +#define AES_FT0(idx) FT0[idx] +#define AES_FT1(idx) FT1[idx] +#define AES_FT2(idx) FT2[idx] +#define AES_FT3(idx) FT3[idx] + +#endif /* MBEDTLS_AES_FEWER_TABLES */ + +void mbedtls_aes_init(mbedtls_aes_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_aes_context)); +} + +void mbedtls_aes_free(mbedtls_aes_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_aes_context)); +} + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +void mbedtls_aes_xts_init(mbedtls_aes_xts_context *ctx) +{ + mbedtls_aes_init(&ctx->crypt); + mbedtls_aes_init(&ctx->tweak); +} + +void mbedtls_aes_xts_free(mbedtls_aes_xts_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_aes_free(&ctx->crypt); + mbedtls_aes_free(&ctx->tweak); +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +/* Some implementations need the round keys to be aligned. + * Return an offset to be added to buf, such that (buf + offset) is + * correctly aligned. + * Note that the offset is in units of elements of buf, i.e. 32-bit words, + * i.e. an offset of 1 means 4 bytes and so on. + */ +#if (defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86)) || \ + (defined(MBEDTLS_AESNI_C) && MBEDTLS_AESNI_HAVE_CODE == 2) +#define MAY_NEED_TO_ALIGN +#endif +static unsigned mbedtls_aes_rk_offset(uint32_t *buf) +{ +#if defined(MAY_NEED_TO_ALIGN) + int align_16_bytes = 0; + +#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86) + if (aes_padlock_ace == -1) { + aes_padlock_ace = mbedtls_padlock_has_support(MBEDTLS_PADLOCK_ACE); + } + if (aes_padlock_ace) { + align_16_bytes = 1; + } +#endif + +#if defined(MBEDTLS_AESNI_C) && MBEDTLS_AESNI_HAVE_CODE == 2 + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) { + align_16_bytes = 1; + } +#endif + + if (align_16_bytes) { + /* These implementations needs 16-byte alignment + * for the round key array. */ + unsigned delta = ((uintptr_t) buf & 0x0000000fU) / 4; + if (delta == 0) { + return 0; + } else { + return 4 - delta; // 16 bytes = 4 uint32_t + } + } +#else /* MAY_NEED_TO_ALIGN */ + (void) buf; +#endif /* MAY_NEED_TO_ALIGN */ + + return 0; +} + +/* + * AES key schedule (encryption) + */ +#if !defined(MBEDTLS_AES_SETKEY_ENC_ALT) +int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits) +{ + unsigned int i; + uint32_t *RK; + + switch (keybits) { + case 128: ctx->nr = 10; break; + case 192: ctx->nr = 12; break; + case 256: ctx->nr = 14; break; + default: return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + } + +#if !defined(MBEDTLS_AES_ROM_TABLES) + if (aes_init_done == 0) { + aes_gen_tables(); + aes_init_done = 1; + } +#endif + + ctx->rk_offset = mbedtls_aes_rk_offset(ctx->buf); + RK = ctx->buf + ctx->rk_offset; + +#if defined(MBEDTLS_AESNI_HAVE_CODE) + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) { + return mbedtls_aesni_setkey_enc((unsigned char *) RK, key, keybits); + } +#endif + +#if defined(MBEDTLS_AESCE_C) && defined(MBEDTLS_HAVE_ARM64) + if (mbedtls_aesce_has_support()) { + return mbedtls_aesce_setkey_enc((unsigned char *) RK, key, keybits); + } +#endif + + for (i = 0; i < (keybits >> 5); i++) { + RK[i] = MBEDTLS_GET_UINT32_LE(key, i << 2); + } + + switch (ctx->nr) { + case 10: + + for (i = 0; i < 10; i++, RK += 4) { + RK[4] = RK[0] ^ RCON[i] ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(RK[3])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(RK[3])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(RK[3])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_0(RK[3])] << 24); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + + for (i = 0; i < 8; i++, RK += 6) { + RK[6] = RK[0] ^ RCON[i] ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(RK[5])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(RK[5])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(RK[5])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_0(RK[5])] << 24); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + + for (i = 0; i < 7; i++, RK += 8) { + RK[8] = RK[0] ^ RCON[i] ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(RK[7])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(RK[7])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(RK[7])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_0(RK[7])] << 24); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ + ((uint32_t) FSb[MBEDTLS_BYTE_0(RK[11])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(RK[11])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(RK[11])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(RK[11])] << 24); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + } + + return 0; +} +#endif /* !MBEDTLS_AES_SETKEY_ENC_ALT */ + +/* + * AES key schedule (decryption) + */ +#if !defined(MBEDTLS_AES_SETKEY_DEC_ALT) +int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits) +{ + int i, j, ret; + mbedtls_aes_context cty; + uint32_t *RK; + uint32_t *SK; + + mbedtls_aes_init(&cty); + + ctx->rk_offset = mbedtls_aes_rk_offset(ctx->buf); + RK = ctx->buf + ctx->rk_offset; + + /* Also checks keybits */ + if ((ret = mbedtls_aes_setkey_enc(&cty, key, keybits)) != 0) { + goto exit; + } + + ctx->nr = cty.nr; + +#if defined(MBEDTLS_AESNI_HAVE_CODE) + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) { + mbedtls_aesni_inverse_key((unsigned char *) RK, + (const unsigned char *) (cty.buf + cty.rk_offset), ctx->nr); + goto exit; + } +#endif + +#if defined(MBEDTLS_AESCE_C) && defined(MBEDTLS_HAVE_ARM64) + if (mbedtls_aesce_has_support()) { + mbedtls_aesce_inverse_key( + (unsigned char *) RK, + (const unsigned char *) (cty.buf + cty.rk_offset), + ctx->nr); + goto exit; + } +#endif + + SK = cty.buf + cty.rk_offset + cty.nr * 4; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for (i = ctx->nr - 1, SK -= 8; i > 0; i--, SK -= 8) { + for (j = 0; j < 4; j++, SK++) { + *RK++ = AES_RT0(FSb[MBEDTLS_BYTE_0(*SK)]) ^ + AES_RT1(FSb[MBEDTLS_BYTE_1(*SK)]) ^ + AES_RT2(FSb[MBEDTLS_BYTE_2(*SK)]) ^ + AES_RT3(FSb[MBEDTLS_BYTE_3(*SK)]); + } + } + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + +exit: + mbedtls_aes_free(&cty); + + return ret; +} +#endif /* !MBEDTLS_AES_SETKEY_DEC_ALT */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +static int mbedtls_aes_xts_decode_keys(const unsigned char *key, + unsigned int keybits, + const unsigned char **key1, + unsigned int *key1bits, + const unsigned char **key2, + unsigned int *key2bits) +{ + const unsigned int half_keybits = keybits / 2; + const unsigned int half_keybytes = half_keybits / 8; + + switch (keybits) { + case 256: break; + case 512: break; + default: return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + } + + *key1bits = half_keybits; + *key2bits = half_keybits; + *key1 = &key[0]; + *key2 = &key[half_keybytes]; + + return 0; +} + +int mbedtls_aes_xts_setkey_enc(mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *key1, *key2; + unsigned int key1bits, key2bits; + + ret = mbedtls_aes_xts_decode_keys(key, keybits, &key1, &key1bits, + &key2, &key2bits); + if (ret != 0) { + return ret; + } + + /* Set the tweak key. Always set tweak key for the encryption mode. */ + ret = mbedtls_aes_setkey_enc(&ctx->tweak, key2, key2bits); + if (ret != 0) { + return ret; + } + + /* Set crypt key for encryption. */ + return mbedtls_aes_setkey_enc(&ctx->crypt, key1, key1bits); +} + +int mbedtls_aes_xts_setkey_dec(mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *key1, *key2; + unsigned int key1bits, key2bits; + + ret = mbedtls_aes_xts_decode_keys(key, keybits, &key1, &key1bits, + &key2, &key2bits); + if (ret != 0) { + return ret; + } + + /* Set the tweak key. Always set tweak key for encryption. */ + ret = mbedtls_aes_setkey_enc(&ctx->tweak, key2, key2bits); + if (ret != 0) { + return ret; + } + + /* Set crypt key for decryption. */ + return mbedtls_aes_setkey_dec(&ctx->crypt, key1, key1bits); +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +#define AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \ + do \ + { \ + (X0) = *RK++ ^ AES_FT0(MBEDTLS_BYTE_0(Y0)) ^ \ + AES_FT1(MBEDTLS_BYTE_1(Y1)) ^ \ + AES_FT2(MBEDTLS_BYTE_2(Y2)) ^ \ + AES_FT3(MBEDTLS_BYTE_3(Y3)); \ + \ + (X1) = *RK++ ^ AES_FT0(MBEDTLS_BYTE_0(Y1)) ^ \ + AES_FT1(MBEDTLS_BYTE_1(Y2)) ^ \ + AES_FT2(MBEDTLS_BYTE_2(Y3)) ^ \ + AES_FT3(MBEDTLS_BYTE_3(Y0)); \ + \ + (X2) = *RK++ ^ AES_FT0(MBEDTLS_BYTE_0(Y2)) ^ \ + AES_FT1(MBEDTLS_BYTE_1(Y3)) ^ \ + AES_FT2(MBEDTLS_BYTE_2(Y0)) ^ \ + AES_FT3(MBEDTLS_BYTE_3(Y1)); \ + \ + (X3) = *RK++ ^ AES_FT0(MBEDTLS_BYTE_0(Y3)) ^ \ + AES_FT1(MBEDTLS_BYTE_1(Y0)) ^ \ + AES_FT2(MBEDTLS_BYTE_2(Y1)) ^ \ + AES_FT3(MBEDTLS_BYTE_3(Y2)); \ + } while (0) + +#define AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \ + do \ + { \ + (X0) = *RK++ ^ AES_RT0(MBEDTLS_BYTE_0(Y0)) ^ \ + AES_RT1(MBEDTLS_BYTE_1(Y3)) ^ \ + AES_RT2(MBEDTLS_BYTE_2(Y2)) ^ \ + AES_RT3(MBEDTLS_BYTE_3(Y1)); \ + \ + (X1) = *RK++ ^ AES_RT0(MBEDTLS_BYTE_0(Y1)) ^ \ + AES_RT1(MBEDTLS_BYTE_1(Y0)) ^ \ + AES_RT2(MBEDTLS_BYTE_2(Y3)) ^ \ + AES_RT3(MBEDTLS_BYTE_3(Y2)); \ + \ + (X2) = *RK++ ^ AES_RT0(MBEDTLS_BYTE_0(Y2)) ^ \ + AES_RT1(MBEDTLS_BYTE_1(Y1)) ^ \ + AES_RT2(MBEDTLS_BYTE_2(Y0)) ^ \ + AES_RT3(MBEDTLS_BYTE_3(Y3)); \ + \ + (X3) = *RK++ ^ AES_RT0(MBEDTLS_BYTE_0(Y3)) ^ \ + AES_RT1(MBEDTLS_BYTE_1(Y2)) ^ \ + AES_RT2(MBEDTLS_BYTE_2(Y1)) ^ \ + AES_RT3(MBEDTLS_BYTE_3(Y0)); \ + } while (0) + +/* + * AES-ECB block encryption + */ +#if !defined(MBEDTLS_AES_ENCRYPT_ALT) +int mbedtls_internal_aes_encrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]) +{ + int i; + uint32_t *RK = ctx->buf + ctx->rk_offset; + struct { + uint32_t X[4]; + uint32_t Y[4]; + } t; + + t.X[0] = MBEDTLS_GET_UINT32_LE(input, 0); t.X[0] ^= *RK++; + t.X[1] = MBEDTLS_GET_UINT32_LE(input, 4); t.X[1] ^= *RK++; + t.X[2] = MBEDTLS_GET_UINT32_LE(input, 8); t.X[2] ^= *RK++; + t.X[3] = MBEDTLS_GET_UINT32_LE(input, 12); t.X[3] ^= *RK++; + + for (i = (ctx->nr >> 1) - 1; i > 0; i--) { + AES_FROUND(t.Y[0], t.Y[1], t.Y[2], t.Y[3], t.X[0], t.X[1], t.X[2], t.X[3]); + AES_FROUND(t.X[0], t.X[1], t.X[2], t.X[3], t.Y[0], t.Y[1], t.Y[2], t.Y[3]); + } + + AES_FROUND(t.Y[0], t.Y[1], t.Y[2], t.Y[3], t.X[0], t.X[1], t.X[2], t.X[3]); + + t.X[0] = *RK++ ^ \ + ((uint32_t) FSb[MBEDTLS_BYTE_0(t.Y[0])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(t.Y[1])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(t.Y[2])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(t.Y[3])] << 24); + + t.X[1] = *RK++ ^ \ + ((uint32_t) FSb[MBEDTLS_BYTE_0(t.Y[1])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(t.Y[2])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(t.Y[3])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(t.Y[0])] << 24); + + t.X[2] = *RK++ ^ \ + ((uint32_t) FSb[MBEDTLS_BYTE_0(t.Y[2])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(t.Y[3])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(t.Y[0])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(t.Y[1])] << 24); + + t.X[3] = *RK++ ^ \ + ((uint32_t) FSb[MBEDTLS_BYTE_0(t.Y[3])]) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_1(t.Y[0])] << 8) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_2(t.Y[1])] << 16) ^ + ((uint32_t) FSb[MBEDTLS_BYTE_3(t.Y[2])] << 24); + + MBEDTLS_PUT_UINT32_LE(t.X[0], output, 0); + MBEDTLS_PUT_UINT32_LE(t.X[1], output, 4); + MBEDTLS_PUT_UINT32_LE(t.X[2], output, 8); + MBEDTLS_PUT_UINT32_LE(t.X[3], output, 12); + + mbedtls_platform_zeroize(&t, sizeof(t)); + + return 0; +} +#endif /* !MBEDTLS_AES_ENCRYPT_ALT */ + +/* + * AES-ECB block decryption + */ +#if !defined(MBEDTLS_AES_DECRYPT_ALT) +int mbedtls_internal_aes_decrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]) +{ + int i; + uint32_t *RK = ctx->buf + ctx->rk_offset; + struct { + uint32_t X[4]; + uint32_t Y[4]; + } t; + + t.X[0] = MBEDTLS_GET_UINT32_LE(input, 0); t.X[0] ^= *RK++; + t.X[1] = MBEDTLS_GET_UINT32_LE(input, 4); t.X[1] ^= *RK++; + t.X[2] = MBEDTLS_GET_UINT32_LE(input, 8); t.X[2] ^= *RK++; + t.X[3] = MBEDTLS_GET_UINT32_LE(input, 12); t.X[3] ^= *RK++; + + for (i = (ctx->nr >> 1) - 1; i > 0; i--) { + AES_RROUND(t.Y[0], t.Y[1], t.Y[2], t.Y[3], t.X[0], t.X[1], t.X[2], t.X[3]); + AES_RROUND(t.X[0], t.X[1], t.X[2], t.X[3], t.Y[0], t.Y[1], t.Y[2], t.Y[3]); + } + + AES_RROUND(t.Y[0], t.Y[1], t.Y[2], t.Y[3], t.X[0], t.X[1], t.X[2], t.X[3]); + + t.X[0] = *RK++ ^ \ + ((uint32_t) RSb[MBEDTLS_BYTE_0(t.Y[0])]) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_1(t.Y[3])] << 8) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_2(t.Y[2])] << 16) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_3(t.Y[1])] << 24); + + t.X[1] = *RK++ ^ \ + ((uint32_t) RSb[MBEDTLS_BYTE_0(t.Y[1])]) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_1(t.Y[0])] << 8) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_2(t.Y[3])] << 16) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_3(t.Y[2])] << 24); + + t.X[2] = *RK++ ^ \ + ((uint32_t) RSb[MBEDTLS_BYTE_0(t.Y[2])]) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_1(t.Y[1])] << 8) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_2(t.Y[0])] << 16) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_3(t.Y[3])] << 24); + + t.X[3] = *RK++ ^ \ + ((uint32_t) RSb[MBEDTLS_BYTE_0(t.Y[3])]) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_1(t.Y[2])] << 8) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_2(t.Y[1])] << 16) ^ + ((uint32_t) RSb[MBEDTLS_BYTE_3(t.Y[0])] << 24); + + MBEDTLS_PUT_UINT32_LE(t.X[0], output, 0); + MBEDTLS_PUT_UINT32_LE(t.X[1], output, 4); + MBEDTLS_PUT_UINT32_LE(t.X[2], output, 8); + MBEDTLS_PUT_UINT32_LE(t.X[3], output, 12); + + mbedtls_platform_zeroize(&t, sizeof(t)); + + return 0; +} +#endif /* !MBEDTLS_AES_DECRYPT_ALT */ + +#if defined(MAY_NEED_TO_ALIGN) +/* VIA Padlock and our intrinsics-based implementation of AESNI require + * the round keys to be aligned on a 16-byte boundary. We take care of this + * before creating them, but the AES context may have moved (this can happen + * if the library is called from a language with managed memory), and in later + * calls it might have a different alignment with respect to 16-byte memory. + * So we may need to realign. + */ +static void aes_maybe_realign(mbedtls_aes_context *ctx) +{ + unsigned new_offset = mbedtls_aes_rk_offset(ctx->buf); + if (new_offset != ctx->rk_offset) { + memmove(ctx->buf + new_offset, // new address + ctx->buf + ctx->rk_offset, // current address + (ctx->nr + 1) * 16); // number of round keys * bytes per rk + ctx->rk_offset = new_offset; + } +} +#endif + +/* + * AES-ECB block encryption/decryption + */ +int mbedtls_aes_crypt_ecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + if (mode != MBEDTLS_AES_ENCRYPT && mode != MBEDTLS_AES_DECRYPT) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + +#if defined(MAY_NEED_TO_ALIGN) + aes_maybe_realign(ctx); +#endif + +#if defined(MBEDTLS_AESNI_HAVE_CODE) + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) { + return mbedtls_aesni_crypt_ecb(ctx, mode, input, output); + } +#endif + +#if defined(MBEDTLS_AESCE_C) && defined(MBEDTLS_HAVE_ARM64) + if (mbedtls_aesce_has_support()) { + return mbedtls_aesce_crypt_ecb(ctx, mode, input, output); + } +#endif + +#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86) + if (aes_padlock_ace > 0) { + return mbedtls_padlock_xcryptecb(ctx, mode, input, output); + } +#endif + + if (mode == MBEDTLS_AES_ENCRYPT) { + return mbedtls_internal_aes_encrypt(ctx, input, output); + } else { + return mbedtls_internal_aes_decrypt(ctx, input, output); + } +} + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/* + * AES-CBC buffer encryption/decryption + */ +int mbedtls_aes_crypt_cbc(mbedtls_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char temp[16]; + + if (mode != MBEDTLS_AES_ENCRYPT && mode != MBEDTLS_AES_DECRYPT) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + + if (length % 16) { + return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; + } + +#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86) + if (aes_padlock_ace > 0) { + if (mbedtls_padlock_xcryptcbc(ctx, mode, length, iv, input, output) == 0) { + return 0; + } + + // If padlock data misaligned, we just fall back to + // unaccelerated mode + // + } +#endif + + if (mode == MBEDTLS_AES_DECRYPT) { + while (length > 0) { + memcpy(temp, input, 16); + ret = mbedtls_aes_crypt_ecb(ctx, mode, input, output); + if (ret != 0) { + goto exit; + } + + mbedtls_xor(output, output, iv, 16); + + memcpy(iv, temp, 16); + + input += 16; + output += 16; + length -= 16; + } + } else { + while (length > 0) { + mbedtls_xor(output, input, iv, 16); + + ret = mbedtls_aes_crypt_ecb(ctx, mode, output, output); + if (ret != 0) { + goto exit; + } + memcpy(iv, output, 16); + + input += 16; + output += 16; + length -= 16; + } + } + ret = 0; + +exit: + return ret; +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) + +typedef unsigned char mbedtls_be128[16]; + +/* + * GF(2^128) multiplication function + * + * This function multiplies a field element by x in the polynomial field + * representation. It uses 64-bit word operations to gain speed but compensates + * for machine endianness and hence works correctly on both big and little + * endian machines. + */ +static void mbedtls_gf128mul_x_ble(unsigned char r[16], + const unsigned char x[16]) +{ + uint64_t a, b, ra, rb; + + a = MBEDTLS_GET_UINT64_LE(x, 0); + b = MBEDTLS_GET_UINT64_LE(x, 8); + + ra = (a << 1) ^ 0x0087 >> (8 - ((b >> 63) << 3)); + rb = (a >> 63) | (b << 1); + + MBEDTLS_PUT_UINT64_LE(ra, r, 0); + MBEDTLS_PUT_UINT64_LE(rb, r, 8); +} + +/* + * AES-XTS buffer encryption/decryption + */ +int mbedtls_aes_crypt_xts(mbedtls_aes_xts_context *ctx, + int mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t blocks = length / 16; + size_t leftover = length % 16; + unsigned char tweak[16]; + unsigned char prev_tweak[16]; + unsigned char tmp[16]; + + if (mode != MBEDTLS_AES_ENCRYPT && mode != MBEDTLS_AES_DECRYPT) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + + /* Data units must be at least 16 bytes long. */ + if (length < 16) { + return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; + } + + /* NIST SP 800-38E disallows data units larger than 2**20 blocks. */ + if (length > (1 << 20) * 16) { + return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH; + } + + /* Compute the tweak. */ + ret = mbedtls_aes_crypt_ecb(&ctx->tweak, MBEDTLS_AES_ENCRYPT, + data_unit, tweak); + if (ret != 0) { + return ret; + } + + while (blocks--) { + if (leftover && (mode == MBEDTLS_AES_DECRYPT) && blocks == 0) { + /* We are on the last block in a decrypt operation that has + * leftover bytes, so we need to use the next tweak for this block, + * and this tweak for the leftover bytes. Save the current tweak for + * the leftovers and then update the current tweak for use on this, + * the last full block. */ + memcpy(prev_tweak, tweak, sizeof(tweak)); + mbedtls_gf128mul_x_ble(tweak, tweak); + } + + mbedtls_xor(tmp, input, tweak, 16); + + ret = mbedtls_aes_crypt_ecb(&ctx->crypt, mode, tmp, tmp); + if (ret != 0) { + return ret; + } + + mbedtls_xor(output, tmp, tweak, 16); + + /* Update the tweak for the next block. */ + mbedtls_gf128mul_x_ble(tweak, tweak); + + output += 16; + input += 16; + } + + if (leftover) { + /* If we are on the leftover bytes in a decrypt operation, we need to + * use the previous tweak for these bytes (as saved in prev_tweak). */ + unsigned char *t = mode == MBEDTLS_AES_DECRYPT ? prev_tweak : tweak; + + /* We are now on the final part of the data unit, which doesn't divide + * evenly by 16. It's time for ciphertext stealing. */ + size_t i; + unsigned char *prev_output = output - 16; + + /* Copy ciphertext bytes from the previous block to our output for each + * byte of ciphertext we won't steal. */ + for (i = 0; i < leftover; i++) { + output[i] = prev_output[i]; + } + + /* Copy the remainder of the input for this final round. */ + mbedtls_xor(tmp, input, t, leftover); + + /* Copy ciphertext bytes from the previous block for input in this + * round. */ + mbedtls_xor(tmp + i, prev_output + i, t + i, 16 - i); + + ret = mbedtls_aes_crypt_ecb(&ctx->crypt, mode, tmp, tmp); + if (ret != 0) { + return ret; + } + + /* Write the result back to the previous block, overriding the previous + * output we copied. */ + mbedtls_xor(prev_output, tmp, t, 16); + } + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +/* + * AES-CFB128 buffer encryption/decryption + */ +int mbedtls_aes_crypt_cfb128(mbedtls_aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + int c; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + + if (mode != MBEDTLS_AES_ENCRYPT && mode != MBEDTLS_AES_DECRYPT) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + + n = *iv_off; + + if (n > 15) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + + if (mode == MBEDTLS_AES_DECRYPT) { + while (length--) { + if (n == 0) { + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, iv, iv); + if (ret != 0) { + goto exit; + } + } + + c = *input++; + *output++ = (unsigned char) (c ^ iv[n]); + iv[n] = (unsigned char) c; + + n = (n + 1) & 0x0F; + } + } else { + while (length--) { + if (n == 0) { + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, iv, iv); + if (ret != 0) { + goto exit; + } + } + + iv[n] = *output++ = (unsigned char) (iv[n] ^ *input++); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; + ret = 0; + +exit: + return ret; +} + +/* + * AES-CFB8 buffer encryption/decryption + */ +int mbedtls_aes_crypt_cfb8(mbedtls_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char c; + unsigned char ov[17]; + + if (mode != MBEDTLS_AES_ENCRYPT && mode != MBEDTLS_AES_DECRYPT) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + while (length--) { + memcpy(ov, iv, 16); + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, iv, iv); + if (ret != 0) { + goto exit; + } + + if (mode == MBEDTLS_AES_DECRYPT) { + ov[16] = *input; + } + + c = *output++ = (unsigned char) (iv[0] ^ *input++); + + if (mode == MBEDTLS_AES_ENCRYPT) { + ov[16] = c; + } + + memcpy(iv, ov + 1, 16); + } + ret = 0; + +exit: + return ret; +} +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) +/* + * AES-OFB (Output Feedback Mode) buffer encryption/decryption + */ +int mbedtls_aes_crypt_ofb(mbedtls_aes_context *ctx, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + int ret = 0; + size_t n; + + n = *iv_off; + + if (n > 15) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + + while (length--) { + if (n == 0) { + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, iv, iv); + if (ret != 0) { + goto exit; + } + } + *output++ = *input++ ^ iv[n]; + + n = (n + 1) & 0x0F; + } + + *iv_off = n; + +exit: + return ret; +} +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/* + * AES-CTR buffer encryption/decryption + */ +int mbedtls_aes_crypt_ctr(mbedtls_aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output) +{ + int c, i; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + + n = *nc_off; + + if (n > 0x0F) { + return MBEDTLS_ERR_AES_BAD_INPUT_DATA; + } + + while (length--) { + if (n == 0) { + ret = mbedtls_aes_crypt_ecb(ctx, MBEDTLS_AES_ENCRYPT, nonce_counter, stream_block); + if (ret != 0) { + goto exit; + } + + for (i = 16; i > 0; i--) { + if (++nonce_counter[i - 1] != 0) { + break; + } + } + } + c = *input++; + *output++ = (unsigned char) (c ^ stream_block[n]); + + n = (n + 1) & 0x0F; + } + + *nc_off = n; + ret = 0; + +exit: + return ret; +} +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#endif /* !MBEDTLS_AES_ALT */ + +#if defined(MBEDTLS_SELF_TEST) +/* + * AES test vectors from: + * + * http://csrc.nist.gov/archive/aes/rijndael/rijndael-vals.zip + */ +static const unsigned char aes_test_ecb_dec[3][16] = +{ + { 0x44, 0x41, 0x6A, 0xC2, 0xD1, 0xF5, 0x3C, 0x58, + 0x33, 0x03, 0x91, 0x7E, 0x6B, 0xE9, 0xEB, 0xE0 }, + { 0x48, 0xE3, 0x1E, 0x9E, 0x25, 0x67, 0x18, 0xF2, + 0x92, 0x29, 0x31, 0x9C, 0x19, 0xF1, 0x5B, 0xA4 }, + { 0x05, 0x8C, 0xCF, 0xFD, 0xBB, 0xCB, 0x38, 0x2D, + 0x1F, 0x6F, 0x56, 0x58, 0x5D, 0x8A, 0x4A, 0xDE } +}; + +static const unsigned char aes_test_ecb_enc[3][16] = +{ + { 0xC3, 0x4C, 0x05, 0x2C, 0xC0, 0xDA, 0x8D, 0x73, + 0x45, 0x1A, 0xFE, 0x5F, 0x03, 0xBE, 0x29, 0x7F }, + { 0xF3, 0xF6, 0x75, 0x2A, 0xE8, 0xD7, 0x83, 0x11, + 0x38, 0xF0, 0x41, 0x56, 0x06, 0x31, 0xB1, 0x14 }, + { 0x8B, 0x79, 0xEE, 0xCC, 0x93, 0xA0, 0xEE, 0x5D, + 0xFF, 0x30, 0xB4, 0xEA, 0x21, 0x63, 0x6D, 0xA4 } +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const unsigned char aes_test_cbc_dec[3][16] = +{ + { 0xFA, 0xCA, 0x37, 0xE0, 0xB0, 0xC8, 0x53, 0x73, + 0xDF, 0x70, 0x6E, 0x73, 0xF7, 0xC9, 0xAF, 0x86 }, + { 0x5D, 0xF6, 0x78, 0xDD, 0x17, 0xBA, 0x4E, 0x75, + 0xB6, 0x17, 0x68, 0xC6, 0xAD, 0xEF, 0x7C, 0x7B }, + { 0x48, 0x04, 0xE1, 0x81, 0x8F, 0xE6, 0x29, 0x75, + 0x19, 0xA3, 0xE8, 0x8C, 0x57, 0x31, 0x04, 0x13 } +}; + +static const unsigned char aes_test_cbc_enc[3][16] = +{ + { 0x8A, 0x05, 0xFC, 0x5E, 0x09, 0x5A, 0xF4, 0x84, + 0x8A, 0x08, 0xD3, 0x28, 0xD3, 0x68, 0x8E, 0x3D }, + { 0x7B, 0xD9, 0x66, 0xD5, 0x3A, 0xD8, 0xC1, 0xBB, + 0x85, 0xD2, 0xAD, 0xFA, 0xE8, 0x7B, 0xB1, 0x04 }, + { 0xFE, 0x3C, 0x53, 0x65, 0x3E, 0x2F, 0x45, 0xB5, + 0x6F, 0xCD, 0x88, 0xB2, 0xCC, 0x89, 0x8F, 0xF0 } +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +/* + * AES-CFB128 test vectors from: + * + * http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + */ +static const unsigned char aes_test_cfb128_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_cfb128_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_cfb128_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_cfb128_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0xC8, 0xA6, 0x45, 0x37, 0xA0, 0xB3, 0xA9, 0x3F, + 0xCD, 0xE3, 0xCD, 0xAD, 0x9F, 0x1C, 0xE5, 0x8B, + 0x26, 0x75, 0x1F, 0x67, 0xA3, 0xCB, 0xB1, 0x40, + 0xB1, 0x80, 0x8C, 0xF1, 0x87, 0xA4, 0xF4, 0xDF, + 0xC0, 0x4B, 0x05, 0x35, 0x7C, 0x5D, 0x1C, 0x0E, + 0xEA, 0xC4, 0xC6, 0x6F, 0x9F, 0xF7, 0xF2, 0xE6 }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0x67, 0xCE, 0x7F, 0x7F, 0x81, 0x17, 0x36, 0x21, + 0x96, 0x1A, 0x2B, 0x70, 0x17, 0x1D, 0x3D, 0x7A, + 0x2E, 0x1E, 0x8A, 0x1D, 0xD5, 0x9B, 0x88, 0xB1, + 0xC8, 0xE6, 0x0F, 0xED, 0x1E, 0xFA, 0xC4, 0xC9, + 0xC0, 0x5F, 0x9F, 0x9C, 0xA9, 0x83, 0x4F, 0xA0, + 0x42, 0xAE, 0x8F, 0xBA, 0x58, 0x4B, 0x09, 0xFF }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x39, 0xFF, 0xED, 0x14, 0x3B, 0x28, 0xB1, 0xC8, + 0x32, 0x11, 0x3C, 0x63, 0x31, 0xE5, 0x40, 0x7B, + 0xDF, 0x10, 0x13, 0x24, 0x15, 0xE5, 0x4B, 0x92, + 0xA1, 0x3E, 0xD0, 0xA8, 0x26, 0x7A, 0xE2, 0xF9, + 0x75, 0xA3, 0x85, 0x74, 0x1A, 0xB9, 0xCE, 0xF8, + 0x20, 0x31, 0x62, 0x3D, 0x55, 0xB1, 0xE4, 0x71 } +}; +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) +/* + * AES-OFB test vectors from: + * + * https://csrc.nist.gov/publications/detail/sp/800-38a/final + */ +static const unsigned char aes_test_ofb_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C }, + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B }, + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char aes_test_ofb_iv[16] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; + +static const unsigned char aes_test_ofb_pt[64] = +{ + 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, + 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF, + 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B, 0x17, + 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10 +}; + +static const unsigned char aes_test_ofb_ct[3][64] = +{ + { 0x3B, 0x3F, 0xD9, 0x2E, 0xB7, 0x2D, 0xAD, 0x20, + 0x33, 0x34, 0x49, 0xF8, 0xE8, 0x3C, 0xFB, 0x4A, + 0x77, 0x89, 0x50, 0x8d, 0x16, 0x91, 0x8f, 0x03, + 0xf5, 0x3c, 0x52, 0xda, 0xc5, 0x4e, 0xd8, 0x25, + 0x97, 0x40, 0x05, 0x1e, 0x9c, 0x5f, 0xec, 0xf6, + 0x43, 0x44, 0xf7, 0xa8, 0x22, 0x60, 0xed, 0xcc, + 0x30, 0x4c, 0x65, 0x28, 0xf6, 0x59, 0xc7, 0x78, + 0x66, 0xa5, 0x10, 0xd9, 0xc1, 0xd6, 0xae, 0x5e }, + { 0xCD, 0xC8, 0x0D, 0x6F, 0xDD, 0xF1, 0x8C, 0xAB, + 0x34, 0xC2, 0x59, 0x09, 0xC9, 0x9A, 0x41, 0x74, + 0xfc, 0xc2, 0x8b, 0x8d, 0x4c, 0x63, 0x83, 0x7c, + 0x09, 0xe8, 0x17, 0x00, 0xc1, 0x10, 0x04, 0x01, + 0x8d, 0x9a, 0x9a, 0xea, 0xc0, 0xf6, 0x59, 0x6f, + 0x55, 0x9c, 0x6d, 0x4d, 0xaf, 0x59, 0xa5, 0xf2, + 0x6d, 0x9f, 0x20, 0x08, 0x57, 0xca, 0x6c, 0x3e, + 0x9c, 0xac, 0x52, 0x4b, 0xd9, 0xac, 0xc9, 0x2a }, + { 0xDC, 0x7E, 0x84, 0xBF, 0xDA, 0x79, 0x16, 0x4B, + 0x7E, 0xCD, 0x84, 0x86, 0x98, 0x5D, 0x38, 0x60, + 0x4f, 0xeb, 0xdc, 0x67, 0x40, 0xd2, 0x0b, 0x3a, + 0xc8, 0x8f, 0x6a, 0xd8, 0x2a, 0x4f, 0xb0, 0x8d, + 0x71, 0xab, 0x47, 0xa0, 0x86, 0xe8, 0x6e, 0xed, + 0xf3, 0x9d, 0x1c, 0x5b, 0xba, 0x97, 0xc4, 0x08, + 0x01, 0x26, 0x14, 0x1d, 0x67, 0xf3, 0x7b, 0xe8, + 0x53, 0x8f, 0x5a, 0x8b, 0xe7, 0x40, 0xe4, 0x84 } +}; +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/* + * AES-CTR test vectors from: + * + * http://www.faqs.org/rfcs/rfc3686.html + */ + +static const unsigned char aes_test_ctr_key[3][16] = +{ + { 0xAE, 0x68, 0x52, 0xF8, 0x12, 0x10, 0x67, 0xCC, + 0x4B, 0xF7, 0xA5, 0x76, 0x55, 0x77, 0xF3, 0x9E }, + { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7, + 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63 }, + { 0x76, 0x91, 0xBE, 0x03, 0x5E, 0x50, 0x20, 0xA8, + 0xAC, 0x6E, 0x61, 0x85, 0x29, 0xF9, 0xA0, 0xDC } +}; + +static const unsigned char aes_test_ctr_nonce_counter[3][16] = +{ + { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59, + 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0xE0, 0x01, 0x7B, 0x27, 0x77, 0x7F, 0x3F, + 0x4A, 0x17, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x01 } +}; + +static const unsigned char aes_test_ctr_pt[3][48] = +{ + { 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x62, + 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x6D, 0x73, 0x67 }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23 } +}; + +static const unsigned char aes_test_ctr_ct[3][48] = +{ + { 0xE4, 0x09, 0x5D, 0x4F, 0xB7, 0xA7, 0xB3, 0x79, + 0x2D, 0x61, 0x75, 0xA3, 0x26, 0x13, 0x11, 0xB8 }, + { 0x51, 0x04, 0xA1, 0x06, 0x16, 0x8A, 0x72, 0xD9, + 0x79, 0x0D, 0x41, 0xEE, 0x8E, 0xDA, 0xD3, 0x88, + 0xEB, 0x2E, 0x1E, 0xFC, 0x46, 0xDA, 0x57, 0xC8, + 0xFC, 0xE6, 0x30, 0xDF, 0x91, 0x41, 0xBE, 0x28 }, + { 0xC1, 0xCF, 0x48, 0xA8, 0x9F, 0x2F, 0xFD, 0xD9, + 0xCF, 0x46, 0x52, 0xE9, 0xEF, 0xDB, 0x72, 0xD7, + 0x45, 0x40, 0xA4, 0x2B, 0xDE, 0x6D, 0x78, 0x36, + 0xD5, 0x9A, 0x5C, 0xEA, 0xAE, 0xF3, 0x10, 0x53, + 0x25, 0xB2, 0x07, 0x2F } +}; + +static const int aes_test_ctr_len[3] = +{ 16, 32, 36 }; +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/* + * AES-XTS test vectors from: + * + * IEEE P1619/D16 Annex B + * https://web.archive.org/web/20150629024421/http://grouper.ieee.org/groups/1619/email/pdf00086.pdf + * (Archived from original at http://grouper.ieee.org/groups/1619/email/pdf00086.pdf) + */ +static const unsigned char aes_test_xts_key[][32] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, + { 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 }, +}; + +static const unsigned char aes_test_xts_pt32[][32] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, + { 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44 }, +}; + +static const unsigned char aes_test_xts_ct32[][32] = +{ + { 0x91, 0x7c, 0xf6, 0x9e, 0xbd, 0x68, 0xb2, 0xec, + 0x9b, 0x9f, 0xe9, 0xa3, 0xea, 0xdd, 0xa6, 0x92, + 0xcd, 0x43, 0xd2, 0xf5, 0x95, 0x98, 0xed, 0x85, + 0x8c, 0x02, 0xc2, 0x65, 0x2f, 0xbf, 0x92, 0x2e }, + { 0xc4, 0x54, 0x18, 0x5e, 0x6a, 0x16, 0x93, 0x6e, + 0x39, 0x33, 0x40, 0x38, 0xac, 0xef, 0x83, 0x8b, + 0xfb, 0x18, 0x6f, 0xff, 0x74, 0x80, 0xad, 0xc4, + 0x28, 0x93, 0x82, 0xec, 0xd6, 0xd3, 0x94, 0xf0 }, + { 0xaf, 0x85, 0x33, 0x6b, 0x59, 0x7a, 0xfc, 0x1a, + 0x90, 0x0b, 0x2e, 0xb2, 0x1e, 0xc9, 0x49, 0xd2, + 0x92, 0xdf, 0x4c, 0x04, 0x7e, 0x0b, 0x21, 0x53, + 0x21, 0x86, 0xa5, 0x97, 0x1a, 0x22, 0x7a, 0x89 }, +}; + +static const unsigned char aes_test_xts_data_unit[][16] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x33, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; + +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +/* + * Checkup routine + */ +int mbedtls_aes_self_test(int verbose) +{ + int ret = 0, i, j, u, mode; + unsigned int keybits; + unsigned char key[32]; + unsigned char buf[64]; + const unsigned char *aes_tests; +#if defined(MBEDTLS_CIPHER_MODE_CBC) || defined(MBEDTLS_CIPHER_MODE_CFB) || \ + defined(MBEDTLS_CIPHER_MODE_OFB) + unsigned char iv[16]; +#endif +#if defined(MBEDTLS_CIPHER_MODE_CBC) + unsigned char prv[16]; +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) || defined(MBEDTLS_CIPHER_MODE_CFB) || \ + defined(MBEDTLS_CIPHER_MODE_OFB) + size_t offset; +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) || defined(MBEDTLS_CIPHER_MODE_XTS) + int len; +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + unsigned char nonce_counter[16]; + unsigned char stream_block[16]; +#endif + mbedtls_aes_context ctx; + + memset(key, 0, 32); + mbedtls_aes_init(&ctx); + + if (verbose != 0) { +#if defined(MBEDTLS_AES_ALT) + mbedtls_printf(" AES note: alternative implementation.\n"); +#else /* MBEDTLS_AES_ALT */ +#if defined(MBEDTLS_PADLOCK_C) && defined(MBEDTLS_HAVE_X86) + if (mbedtls_padlock_has_support(MBEDTLS_PADLOCK_ACE)) { + mbedtls_printf(" AES note: using VIA Padlock.\n"); + } else +#endif +#if defined(MBEDTLS_AESNI_HAVE_CODE) + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_AES)) { + mbedtls_printf(" AES note: using AESNI.\n"); + } else +#endif +#if defined(MBEDTLS_AESCE_C) && defined(MBEDTLS_HAVE_ARM64) + if (mbedtls_aesce_has_support()) { + mbedtls_printf(" AES note: using AESCE.\n"); + } else +#endif + mbedtls_printf(" AES note: built-in implementation.\n"); +#endif /* MBEDTLS_AES_ALT */ + } + + /* + * ECB mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + keybits = 128 + u * 64; + mode = i & 1; + + if (verbose != 0) { + mbedtls_printf(" AES-ECB-%3u (%s): ", keybits, + (mode == MBEDTLS_AES_DECRYPT) ? "dec" : "enc"); + } + + memset(buf, 0, 16); + + if (mode == MBEDTLS_AES_DECRYPT) { + ret = mbedtls_aes_setkey_dec(&ctx, key, keybits); + aes_tests = aes_test_ecb_dec[u]; + } else { + ret = mbedtls_aes_setkey_enc(&ctx, key, keybits); + aes_tests = aes_test_ecb_enc[u]; + } + + /* + * AES-192 is an optional feature that may be unavailable when + * there is an alternative underlying implementation i.e. when + * MBEDTLS_AES_ALT is defined. + */ + if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED && keybits == 192) { + mbedtls_printf("skipped\n"); + continue; + } else if (ret != 0) { + goto exit; + } + + for (j = 0; j < 10000; j++) { + ret = mbedtls_aes_crypt_ecb(&ctx, mode, buf, buf); + if (ret != 0) { + goto exit; + } + } + + if (memcmp(buf, aes_tests, 16) != 0) { + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + /* + * CBC mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + keybits = 128 + u * 64; + mode = i & 1; + + if (verbose != 0) { + mbedtls_printf(" AES-CBC-%3u (%s): ", keybits, + (mode == MBEDTLS_AES_DECRYPT) ? "dec" : "enc"); + } + + memset(iv, 0, 16); + memset(prv, 0, 16); + memset(buf, 0, 16); + + if (mode == MBEDTLS_AES_DECRYPT) { + ret = mbedtls_aes_setkey_dec(&ctx, key, keybits); + aes_tests = aes_test_cbc_dec[u]; + } else { + ret = mbedtls_aes_setkey_enc(&ctx, key, keybits); + aes_tests = aes_test_cbc_enc[u]; + } + + /* + * AES-192 is an optional feature that may be unavailable when + * there is an alternative underlying implementation i.e. when + * MBEDTLS_AES_ALT is defined. + */ + if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED && keybits == 192) { + mbedtls_printf("skipped\n"); + continue; + } else if (ret != 0) { + goto exit; + } + + for (j = 0; j < 10000; j++) { + if (mode == MBEDTLS_AES_ENCRYPT) { + unsigned char tmp[16]; + + memcpy(tmp, prv, 16); + memcpy(prv, buf, 16); + memcpy(buf, tmp, 16); + } + + ret = mbedtls_aes_crypt_cbc(&ctx, mode, 16, iv, buf, buf); + if (ret != 0) { + goto exit; + } + + } + + if (memcmp(buf, aes_tests, 16) != 0) { + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) + /* + * CFB128 mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + keybits = 128 + u * 64; + mode = i & 1; + + if (verbose != 0) { + mbedtls_printf(" AES-CFB128-%3u (%s): ", keybits, + (mode == MBEDTLS_AES_DECRYPT) ? "dec" : "enc"); + } + + memcpy(iv, aes_test_cfb128_iv, 16); + memcpy(key, aes_test_cfb128_key[u], keybits / 8); + + offset = 0; + ret = mbedtls_aes_setkey_enc(&ctx, key, keybits); + /* + * AES-192 is an optional feature that may be unavailable when + * there is an alternative underlying implementation i.e. when + * MBEDTLS_AES_ALT is defined. + */ + if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED && keybits == 192) { + mbedtls_printf("skipped\n"); + continue; + } else if (ret != 0) { + goto exit; + } + + if (mode == MBEDTLS_AES_DECRYPT) { + memcpy(buf, aes_test_cfb128_ct[u], 64); + aes_tests = aes_test_cfb128_pt; + } else { + memcpy(buf, aes_test_cfb128_pt, 64); + aes_tests = aes_test_cfb128_ct[u]; + } + + ret = mbedtls_aes_crypt_cfb128(&ctx, mode, 64, &offset, iv, buf, buf); + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, aes_tests, 64) != 0) { + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) + /* + * OFB mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + keybits = 128 + u * 64; + mode = i & 1; + + if (verbose != 0) { + mbedtls_printf(" AES-OFB-%3u (%s): ", keybits, + (mode == MBEDTLS_AES_DECRYPT) ? "dec" : "enc"); + } + + memcpy(iv, aes_test_ofb_iv, 16); + memcpy(key, aes_test_ofb_key[u], keybits / 8); + + offset = 0; + ret = mbedtls_aes_setkey_enc(&ctx, key, keybits); + /* + * AES-192 is an optional feature that may be unavailable when + * there is an alternative underlying implementation i.e. when + * MBEDTLS_AES_ALT is defined. + */ + if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED && keybits == 192) { + mbedtls_printf("skipped\n"); + continue; + } else if (ret != 0) { + goto exit; + } + + if (mode == MBEDTLS_AES_DECRYPT) { + memcpy(buf, aes_test_ofb_ct[u], 64); + aes_tests = aes_test_ofb_pt; + } else { + memcpy(buf, aes_test_ofb_pt, 64); + aes_tests = aes_test_ofb_ct[u]; + } + + ret = mbedtls_aes_crypt_ofb(&ctx, 64, &offset, iv, buf, buf); + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, aes_tests, 64) != 0) { + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) + /* + * CTR mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + mode = i & 1; + + if (verbose != 0) { + mbedtls_printf(" AES-CTR-128 (%s): ", + (mode == MBEDTLS_AES_DECRYPT) ? "dec" : "enc"); + } + + memcpy(nonce_counter, aes_test_ctr_nonce_counter[u], 16); + memcpy(key, aes_test_ctr_key[u], 16); + + offset = 0; + if ((ret = mbedtls_aes_setkey_enc(&ctx, key, 128)) != 0) { + goto exit; + } + + len = aes_test_ctr_len[u]; + + if (mode == MBEDTLS_AES_DECRYPT) { + memcpy(buf, aes_test_ctr_ct[u], len); + aes_tests = aes_test_ctr_pt[u]; + } else { + memcpy(buf, aes_test_ctr_pt[u], len); + aes_tests = aes_test_ctr_ct[u]; + } + + ret = mbedtls_aes_crypt_ctr(&ctx, len, &offset, nonce_counter, + stream_block, buf, buf); + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, aes_tests, len) != 0) { + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) + { + static const int num_tests = + sizeof(aes_test_xts_key) / sizeof(*aes_test_xts_key); + mbedtls_aes_xts_context ctx_xts; + + /* + * XTS mode + */ + mbedtls_aes_xts_init(&ctx_xts); + + for (i = 0; i < num_tests << 1; i++) { + const unsigned char *data_unit; + u = i >> 1; + mode = i & 1; + + if (verbose != 0) { + mbedtls_printf(" AES-XTS-128 (%s): ", + (mode == MBEDTLS_AES_DECRYPT) ? "dec" : "enc"); + } + + memset(key, 0, sizeof(key)); + memcpy(key, aes_test_xts_key[u], 32); + data_unit = aes_test_xts_data_unit[u]; + + len = sizeof(*aes_test_xts_ct32); + + if (mode == MBEDTLS_AES_DECRYPT) { + ret = mbedtls_aes_xts_setkey_dec(&ctx_xts, key, 256); + if (ret != 0) { + goto exit; + } + memcpy(buf, aes_test_xts_ct32[u], len); + aes_tests = aes_test_xts_pt32[u]; + } else { + ret = mbedtls_aes_xts_setkey_enc(&ctx_xts, key, 256); + if (ret != 0) { + goto exit; + } + memcpy(buf, aes_test_xts_pt32[u], len); + aes_tests = aes_test_xts_ct32[u]; + } + + + ret = mbedtls_aes_crypt_xts(&ctx_xts, mode, len, data_unit, + buf, buf); + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, aes_tests, len) != 0) { + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + mbedtls_aes_xts_free(&ctx_xts); + } +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + + ret = 0; + +exit: + if (ret != 0 && verbose != 0) { + mbedtls_printf("failed\n"); + } + + mbedtls_aes_free(&ctx); + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_AES_C */ diff --git a/r5dev/thirdparty/mbedtls/aesce.c b/r5dev/thirdparty/mbedtls/aesce.c new file mode 100644 index 00000000..fe056dc4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/aesce.c @@ -0,0 +1,411 @@ +/* + * Armv8-A Cryptographic Extension support functions for Aarch64 + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(__aarch64__) && !defined(__ARM_FEATURE_CRYPTO) && \ + defined(__clang__) && __clang_major__ >= 4 +/* TODO: Re-consider above after https://reviews.llvm.org/D131064 merged. + * + * The intrinsic declaration are guarded by predefined ACLE macros in clang: + * these are normally only enabled by the -march option on the command line. + * By defining the macros ourselves we gain access to those declarations without + * requiring -march on the command line. + * + * `arm_neon.h` could be included by any header file, so we put these defines + * at the top of this file, before any includes. + */ +#define __ARM_FEATURE_CRYPTO 1 +/* See: https://arm-software.github.io/acle/main/acle.html#cryptographic-extensions + * + * `__ARM_FEATURE_CRYPTO` is deprecated, but we need to continue to specify it + * for older compilers. + */ +#define __ARM_FEATURE_AES 1 +#define MBEDTLS_ENABLE_ARM_CRYPTO_EXTENSIONS_COMPILER_FLAG +#endif + +#include +#include "common.h" + +#if defined(MBEDTLS_AESCE_C) + +#include "aesce.h" + +#if defined(MBEDTLS_HAVE_ARM64) + +#if !defined(__ARM_FEATURE_AES) || defined(MBEDTLS_ENABLE_ARM_CRYPTO_EXTENSIONS_COMPILER_FLAG) +# if defined(__clang__) +# if __clang_major__ < 4 +# error "A more recent Clang is required for MBEDTLS_AESCE_C" +# endif +# pragma clang attribute push (__attribute__((target("crypto"))), apply_to=function) +# define MBEDTLS_POP_TARGET_PRAGMA +# elif defined(__GNUC__) +# if __GNUC__ < 6 +# error "A more recent GCC is required for MBEDTLS_AESCE_C" +# endif +# pragma GCC push_options +# pragma GCC target ("arch=armv8-a+crypto") +# define MBEDTLS_POP_TARGET_PRAGMA +# else +# error "Only GCC and Clang supported for MBEDTLS_AESCE_C" +# endif +#endif /* !__ARM_FEATURE_AES || MBEDTLS_ENABLE_ARM_CRYPTO_EXTENSIONS_COMPILER_FLAG */ + +#include + +#if defined(__linux__) +#include +#include +#endif + +/* + * AES instruction support detection routine + */ +int mbedtls_aesce_has_support(void) +{ +#if defined(__linux__) + unsigned long auxval = getauxval(AT_HWCAP); + return (auxval & (HWCAP_ASIMD | HWCAP_AES)) == + (HWCAP_ASIMD | HWCAP_AES); +#else + /* Assume AES instructions are supported. */ + return 1; +#endif +} + +static uint8x16_t aesce_encrypt_block(uint8x16_t block, + unsigned char *keys, + int rounds) +{ + for (int i = 0; i < rounds - 1; i++) { + /* AES AddRoundKey, SubBytes, ShiftRows (in this order). + * AddRoundKey adds the round key for the previous round. */ + block = vaeseq_u8(block, vld1q_u8(keys + i * 16)); + /* AES mix columns */ + block = vaesmcq_u8(block); + } + + /* AES AddRoundKey for the previous round. + * SubBytes, ShiftRows for the final round. */ + block = vaeseq_u8(block, vld1q_u8(keys + (rounds -1) * 16)); + + /* Final round: no MixColumns */ + + /* Final AddRoundKey */ + block = veorq_u8(block, vld1q_u8(keys + rounds * 16)); + + return block; +} + +static uint8x16_t aesce_decrypt_block(uint8x16_t block, + unsigned char *keys, + int rounds) +{ + + for (int i = 0; i < rounds - 1; i++) { + /* AES AddRoundKey, SubBytes, ShiftRows */ + block = vaesdq_u8(block, vld1q_u8(keys + i * 16)); + /* AES inverse MixColumns for the next round. + * + * This means that we switch the order of the inverse AddRoundKey and + * inverse MixColumns operations. We have to do this as AddRoundKey is + * done in an atomic instruction together with the inverses of SubBytes + * and ShiftRows. + * + * It works because MixColumns is a linear operation over GF(2^8) and + * AddRoundKey is an exclusive or, which is equivalent to addition over + * GF(2^8). (The inverse of MixColumns needs to be applied to the + * affected round keys separately which has been done when the + * decryption round keys were calculated.) */ + block = vaesimcq_u8(block); + } + + /* The inverses of AES AddRoundKey, SubBytes, ShiftRows finishing up the + * last full round. */ + block = vaesdq_u8(block, vld1q_u8(keys + (rounds - 1) * 16)); + + /* Inverse AddRoundKey for inverting the initial round key addition. */ + block = veorq_u8(block, vld1q_u8(keys + rounds * 16)); + + return block; +} + +/* + * AES-ECB block en(de)cryption + */ +int mbedtls_aesce_crypt_ecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + uint8x16_t block = vld1q_u8(&input[0]); + unsigned char *keys = (unsigned char *) (ctx->buf + ctx->rk_offset); + + if (mode == MBEDTLS_AES_ENCRYPT) { + block = aesce_encrypt_block(block, keys, ctx->nr); + } else { + block = aesce_decrypt_block(block, keys, ctx->nr); + } + vst1q_u8(&output[0], block); + + return 0; +} + +/* + * Compute decryption round keys from encryption round keys + */ +void mbedtls_aesce_inverse_key(unsigned char *invkey, + const unsigned char *fwdkey, + int nr) +{ + int i, j; + j = nr; + vst1q_u8(invkey, vld1q_u8(fwdkey + j * 16)); + for (i = 1, j--; j > 0; i++, j--) { + vst1q_u8(invkey + i * 16, + vaesimcq_u8(vld1q_u8(fwdkey + j * 16))); + } + vst1q_u8(invkey + i * 16, vld1q_u8(fwdkey + j * 16)); + +} + +static inline uint32_t aes_rot_word(uint32_t word) +{ + return (word << (32 - 8)) | (word >> 8); +} + +static inline uint32_t aes_sub_word(uint32_t in) +{ + uint8x16_t v = vreinterpretq_u8_u32(vdupq_n_u32(in)); + uint8x16_t zero = vdupq_n_u8(0); + + /* vaeseq_u8 does both SubBytes and ShiftRows. Taking the first row yields + * the correct result as ShiftRows doesn't change the first row. */ + v = vaeseq_u8(zero, v); + return vgetq_lane_u32(vreinterpretq_u32_u8(v), 0); +} + +/* + * Key expansion function + */ +static void aesce_setkey_enc(unsigned char *rk, + const unsigned char *key, + const size_t key_bit_length) +{ + static uint8_t const rcon[] = { 0x01, 0x02, 0x04, 0x08, 0x10, + 0x20, 0x40, 0x80, 0x1b, 0x36 }; + /* See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf + * - Section 5, Nr = Nk + 6 + * - Section 5.2, the length of round keys is Nb*(Nr+1) + */ + const uint32_t key_len_in_words = key_bit_length / 32; /* Nk */ + const size_t round_key_len_in_words = 4; /* Nb */ + const size_t rounds_needed = key_len_in_words + 6; /* Nr */ + const size_t round_keys_len_in_words = + round_key_len_in_words * (rounds_needed + 1); /* Nb*(Nr+1) */ + const uint32_t *rko_end = (uint32_t *) rk + round_keys_len_in_words; + + memcpy(rk, key, key_len_in_words * 4); + + for (uint32_t *rki = (uint32_t *) rk; + rki + key_len_in_words < rko_end; + rki += key_len_in_words) { + + size_t iteration = (rki - (uint32_t *) rk) / key_len_in_words; + uint32_t *rko; + rko = rki + key_len_in_words; + rko[0] = aes_rot_word(aes_sub_word(rki[key_len_in_words - 1])); + rko[0] ^= rcon[iteration] ^ rki[0]; + rko[1] = rko[0] ^ rki[1]; + rko[2] = rko[1] ^ rki[2]; + rko[3] = rko[2] ^ rki[3]; + if (rko + key_len_in_words > rko_end) { + /* Do not write overflow words.*/ + continue; + } + switch (key_bit_length) { + case 128: + break; + case 192: + rko[4] = rko[3] ^ rki[4]; + rko[5] = rko[4] ^ rki[5]; + break; + case 256: + rko[4] = aes_sub_word(rko[3]) ^ rki[4]; + rko[5] = rko[4] ^ rki[5]; + rko[6] = rko[5] ^ rki[6]; + rko[7] = rko[6] ^ rki[7]; + break; + } + } +} + +/* + * Key expansion, wrapper + */ +int mbedtls_aesce_setkey_enc(unsigned char *rk, + const unsigned char *key, + size_t bits) +{ + switch (bits) { + case 128: + case 192: + case 256: + aesce_setkey_enc(rk, key, bits); + break; + default: + return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + } + + return 0; +} + +#if defined(MBEDTLS_GCM_C) + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 5 +/* Some intrinsics are not available for GCC 5.X. */ +#define vreinterpretq_p64_u8(a) ((poly64x2_t) a) +#define vreinterpretq_u8_p128(a) ((uint8x16_t) a) +static inline poly64_t vget_low_p64(poly64x2_t __a) +{ + uint64x2_t tmp = (uint64x2_t) (__a); + uint64x1_t lo = vcreate_u64(vgetq_lane_u64(tmp, 0)); + return (poly64_t) (lo); +} +#endif /* !__clang__ && __GNUC__ && __GNUC__ == 5*/ + +/* vmull_p64/vmull_high_p64 wrappers. + * + * Older compilers miss some intrinsic functions for `poly*_t`. We use + * uint8x16_t and uint8x16x3_t as input/output parameters. + */ +static inline uint8x16_t pmull_low(uint8x16_t a, uint8x16_t b) +{ + return vreinterpretq_u8_p128( + vmull_p64( + (poly64_t) vget_low_p64(vreinterpretq_p64_u8(a)), + (poly64_t) vget_low_p64(vreinterpretq_p64_u8(b)))); +} + +static inline uint8x16_t pmull_high(uint8x16_t a, uint8x16_t b) +{ + return vreinterpretq_u8_p128( + vmull_high_p64(vreinterpretq_p64_u8(a), + vreinterpretq_p64_u8(b))); +} + +/* GHASH does 128b polynomial multiplication on block in GF(2^128) defined by + * `x^128 + x^7 + x^2 + x + 1`. + * + * Arm64 only has 64b->128b polynomial multipliers, we need to do 4 64b + * multiplies to generate a 128b. + * + * `poly_mult_128` executes polynomial multiplication and outputs 256b that + * represented by 3 128b due to code size optimization. + * + * Output layout: + * | | | | + * |------------|-------------|-------------| + * | ret.val[0] | h3:h2:00:00 | high 128b | + * | ret.val[1] | :m2:m1:00 | middle 128b | + * | ret.val[2] | : :l1:l0 | low 128b | + */ +static inline uint8x16x3_t poly_mult_128(uint8x16_t a, uint8x16_t b) +{ + uint8x16x3_t ret; + uint8x16_t h, m, l; /* retval high/middle/low */ + uint8x16_t c, d, e; + + h = pmull_high(a, b); /* h3:h2:00:00 = a1*b1 */ + l = pmull_low(a, b); /* : :l1:l0 = a0*b0 */ + c = vextq_u8(b, b, 8); /* :c1:c0 = b0:b1 */ + d = pmull_high(a, c); /* :d2:d1:00 = a1*b0 */ + e = pmull_low(a, c); /* :e2:e1:00 = a0*b1 */ + m = veorq_u8(d, e); /* :m2:m1:00 = d + e */ + + ret.val[0] = h; + ret.val[1] = m; + ret.val[2] = l; + return ret; +} + +/* + * Modulo reduction. + * + * See: https://www.researchgate.net/publication/285612706_Implementing_GCM_on_ARMv8 + * + * Section 4.3 + * + * Modular reduction is slightly more complex. Write the GCM modulus as f(z) = + * z^128 +r(z), where r(z) = z^7+z^2+z+ 1. The well known approach is to + * consider that z^128 ≡r(z) (mod z^128 +r(z)), allowing us to write the 256-bit + * operand to be reduced as a(z) = h(z)z^128 +l(z)≡h(z)r(z) + l(z). That is, we + * simply multiply the higher part of the operand by r(z) and add it to l(z). If + * the result is still larger than 128 bits, we reduce again. + */ +static inline uint8x16_t poly_mult_reduce(uint8x16x3_t input) +{ + uint8x16_t const ZERO = vdupq_n_u8(0); + /* use 'asm' as an optimisation barrier to prevent loading MODULO from memory */ + uint64x2_t r = vreinterpretq_u64_u8(vdupq_n_u8(0x87)); + asm ("" : "+w" (r)); + uint8x16_t const MODULO = vreinterpretq_u8_u64(vshrq_n_u64(r, 64 - 8)); + uint8x16_t h, m, l; /* input high/middle/low 128b */ + uint8x16_t c, d, e, f, g, n, o; + h = input.val[0]; /* h3:h2:00:00 */ + m = input.val[1]; /* :m2:m1:00 */ + l = input.val[2]; /* : :l1:l0 */ + c = pmull_high(h, MODULO); /* :c2:c1:00 = reduction of h3 */ + d = pmull_low(h, MODULO); /* : :d1:d0 = reduction of h2 */ + e = veorq_u8(c, m); /* :e2:e1:00 = m2:m1:00 + c2:c1:00 */ + f = pmull_high(e, MODULO); /* : :f1:f0 = reduction of e2 */ + g = vextq_u8(ZERO, e, 8); /* : :g1:00 = e1:00 */ + n = veorq_u8(d, l); /* : :n1:n0 = d1:d0 + l1:l0 */ + o = veorq_u8(n, f); /* o1:o0 = f1:f0 + n1:n0 */ + return veorq_u8(o, g); /* = o1:o0 + g1:00 */ +} + +/* + * GCM multiplication: c = a times b in GF(2^128) + */ +void mbedtls_aesce_gcm_mult(unsigned char c[16], + const unsigned char a[16], + const unsigned char b[16]) +{ + uint8x16_t va, vb, vc; + va = vrbitq_u8(vld1q_u8(&a[0])); + vb = vrbitq_u8(vld1q_u8(&b[0])); + vc = vrbitq_u8(poly_mult_reduce(poly_mult_128(va, vb))); + vst1q_u8(&c[0], vc); +} + +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_POP_TARGET_PRAGMA) +#if defined(__clang__) +#pragma clang attribute pop +#elif defined(__GNUC__) +#pragma GCC pop_options +#endif +#undef MBEDTLS_POP_TARGET_PRAGMA +#endif + +#endif /* MBEDTLS_HAVE_ARM64 */ + +#endif /* MBEDTLS_AESCE_C */ diff --git a/r5dev/thirdparty/mbedtls/aesce.h b/r5dev/thirdparty/mbedtls/aesce.h new file mode 100644 index 00000000..12ddc74b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/aesce.h @@ -0,0 +1,116 @@ +/** + * \file aesce.h + * + * \brief Support hardware AES acceleration on Armv8-A processors with + * the Armv8-A Cryptographic Extension in AArch64 execution state. + * + * \warning These functions are only for internal use by other library + * functions; you must not call them directly. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_AESCE_H +#define MBEDTLS_AESCE_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/aes.h" + + +#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) && \ + defined(__aarch64__) && !defined(MBEDTLS_HAVE_ARM64) +#define MBEDTLS_HAVE_ARM64 +#endif + +#if defined(MBEDTLS_HAVE_ARM64) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Internal function to detect the crypto extension in CPUs. + * + * \return 1 if CPU has support for the feature, 0 otherwise + */ +int mbedtls_aesce_has_support(void); + +/** + * \brief Internal AES-ECB block encryption and decryption + * + * \param ctx AES context + * \param mode MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 on success (cannot fail) + */ +int mbedtls_aesce_crypt_ecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]); + +/** + * \brief Internal GCM multiplication: c = a * b in GF(2^128) + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param c Result + * \param a First operand + * \param b Second operand + * + * \note Both operands and result are bit strings interpreted as + * elements of GF(2^128) as per the GCM spec. + */ +void mbedtls_aesce_gcm_mult(unsigned char c[16], + const unsigned char a[16], + const unsigned char b[16]); + + +/** + * \brief Internal round key inversion. This function computes + * decryption round keys from the encryption round keys. + * + * \param invkey Round keys for the equivalent inverse cipher + * \param fwdkey Original round keys (for encryption) + * \param nr Number of rounds (that is, number of round keys minus one) + */ +void mbedtls_aesce_inverse_key(unsigned char *invkey, + const unsigned char *fwdkey, + int nr); + +/** + * \brief Internal key expansion for encryption + * + * \param rk Destination buffer where the round keys are written + * \param key Encryption key + * \param bits Key size in bits (must be 128, 192 or 256) + * + * \return 0 if successful, or MBEDTLS_ERR_AES_INVALID_KEY_LENGTH + */ +int mbedtls_aesce_setkey_enc(unsigned char *rk, + const unsigned char *key, + size_t bits); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_HAVE_ARM64 */ + +#endif /* MBEDTLS_AESCE_H */ diff --git a/r5dev/thirdparty/mbedtls/aesni.c b/r5dev/thirdparty/mbedtls/aesni.c new file mode 100644 index 00000000..78dd8b09 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/aesni.c @@ -0,0 +1,802 @@ +/* + * AES-NI support functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * [AES-WP] https://www.intel.com/content/www/us/en/developer/articles/tool/intel-advanced-encryption-standard-aes-instructions-set.html + * [CLMUL-WP] https://www.intel.com/content/www/us/en/develop/download/intel-carry-less-multiplication-instruction-and-its-usage-for-computing-the-gcm-mode.html + */ + +#include "common.h" + +#if defined(MBEDTLS_AESNI_C) + +#include "aesni.h" + +#include + +#if defined(MBEDTLS_AESNI_HAVE_CODE) + +#if MBEDTLS_AESNI_HAVE_CODE == 2 +#if !defined(_WIN32) +#include +#else +#include +#endif +#include +#endif + +/* + * AES-NI support detection routine + */ +int mbedtls_aesni_has_support(unsigned int what) +{ + static int done = 0; + static unsigned int c = 0; + + if (!done) { +#if MBEDTLS_AESNI_HAVE_CODE == 2 + static unsigned info[4] = { 0, 0, 0, 0 }; +#if defined(_MSC_VER) + __cpuid(info, 1); +#else + __cpuid(1, info[0], info[1], info[2], info[3]); +#endif + c = info[2]; +#else /* AESNI using asm */ + asm ("movl $1, %%eax \n\t" + "cpuid \n\t" + : "=c" (c) + : + : "eax", "ebx", "edx"); +#endif /* MBEDTLS_AESNI_HAVE_CODE */ + done = 1; + } + + return (c & what) != 0; +} + +#if MBEDTLS_AESNI_HAVE_CODE == 2 + +/* + * AES-NI AES-ECB block en(de)cryption + */ +int mbedtls_aesni_crypt_ecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + const __m128i *rk = (const __m128i *) (ctx->buf + ctx->rk_offset); + unsigned nr = ctx->nr; // Number of remaining rounds + + // Load round key 0 + __m128i state; + memcpy(&state, input, 16); + state = _mm_xor_si128(state, rk[0]); // state ^= *rk; + ++rk; + --nr; + + if (mode == 0) { + while (nr != 0) { + state = _mm_aesdec_si128(state, *rk); + ++rk; + --nr; + } + state = _mm_aesdeclast_si128(state, *rk); + } else { + while (nr != 0) { + state = _mm_aesenc_si128(state, *rk); + ++rk; + --nr; + } + state = _mm_aesenclast_si128(state, *rk); + } + + memcpy(output, &state, 16); + return 0; +} + +/* + * GCM multiplication: c = a times b in GF(2^128) + * Based on [CLMUL-WP] algorithms 1 (with equation 27) and 5. + */ + +static void gcm_clmul(const __m128i aa, const __m128i bb, + __m128i *cc, __m128i *dd) +{ + /* + * Caryless multiplication dd:cc = aa * bb + * using [CLMUL-WP] algorithm 1 (p. 12). + */ + *cc = _mm_clmulepi64_si128(aa, bb, 0x00); // a0*b0 = c1:c0 + *dd = _mm_clmulepi64_si128(aa, bb, 0x11); // a1*b1 = d1:d0 + __m128i ee = _mm_clmulepi64_si128(aa, bb, 0x10); // a0*b1 = e1:e0 + __m128i ff = _mm_clmulepi64_si128(aa, bb, 0x01); // a1*b0 = f1:f0 + ff = _mm_xor_si128(ff, ee); // e1+f1:e0+f0 + ee = ff; // e1+f1:e0+f0 + ff = _mm_srli_si128(ff, 8); // 0:e1+f1 + ee = _mm_slli_si128(ee, 8); // e0+f0:0 + *dd = _mm_xor_si128(*dd, ff); // d1:d0+e1+f1 + *cc = _mm_xor_si128(*cc, ee); // c1+e0+f0:c0 +} + +static void gcm_shift(__m128i *cc, __m128i *dd) +{ + /* [CMUCL-WP] Algorithm 5 Step 1: shift cc:dd one bit to the left, + * taking advantage of [CLMUL-WP] eq 27 (p. 18). */ + // // *cc = r1:r0 + // // *dd = r3:r2 + __m128i cc_lo = _mm_slli_epi64(*cc, 1); // r1<<1:r0<<1 + __m128i dd_lo = _mm_slli_epi64(*dd, 1); // r3<<1:r2<<1 + __m128i cc_hi = _mm_srli_epi64(*cc, 63); // r1>>63:r0>>63 + __m128i dd_hi = _mm_srli_epi64(*dd, 63); // r3>>63:r2>>63 + __m128i xmm5 = _mm_srli_si128(cc_hi, 8); // 0:r1>>63 + cc_hi = _mm_slli_si128(cc_hi, 8); // r0>>63:0 + dd_hi = _mm_slli_si128(dd_hi, 8); // 0:r1>>63 + + *cc = _mm_or_si128(cc_lo, cc_hi); // r1<<1|r0>>63:r0<<1 + *dd = _mm_or_si128(_mm_or_si128(dd_lo, dd_hi), xmm5); // r3<<1|r2>>62:r2<<1|r1>>63 +} + +static __m128i gcm_reduce(__m128i xx) +{ + // // xx = x1:x0 + /* [CLMUL-WP] Algorithm 5 Step 2 */ + __m128i aa = _mm_slli_epi64(xx, 63); // x1<<63:x0<<63 = stuff:a + __m128i bb = _mm_slli_epi64(xx, 62); // x1<<62:x0<<62 = stuff:b + __m128i cc = _mm_slli_epi64(xx, 57); // x1<<57:x0<<57 = stuff:c + __m128i dd = _mm_slli_si128(_mm_xor_si128(_mm_xor_si128(aa, bb), cc), 8); // a+b+c:0 + return _mm_xor_si128(dd, xx); // x1+a+b+c:x0 = d:x0 +} + +static __m128i gcm_mix(__m128i dx) +{ + /* [CLMUL-WP] Algorithm 5 Steps 3 and 4 */ + __m128i ee = _mm_srli_epi64(dx, 1); // e1:x0>>1 = e1:e0' + __m128i ff = _mm_srli_epi64(dx, 2); // f1:x0>>2 = f1:f0' + __m128i gg = _mm_srli_epi64(dx, 7); // g1:x0>>7 = g1:g0' + + // e0'+f0'+g0' is almost e0+f0+g0, except for some missing + // bits carried from d. Now get those bits back in. + __m128i eh = _mm_slli_epi64(dx, 63); // d<<63:stuff + __m128i fh = _mm_slli_epi64(dx, 62); // d<<62:stuff + __m128i gh = _mm_slli_epi64(dx, 57); // d<<57:stuff + __m128i hh = _mm_srli_si128(_mm_xor_si128(_mm_xor_si128(eh, fh), gh), 8); // 0:missing bits of d + + return _mm_xor_si128(_mm_xor_si128(_mm_xor_si128(_mm_xor_si128(ee, ff), gg), hh), dx); +} + +void mbedtls_aesni_gcm_mult(unsigned char c[16], + const unsigned char a[16], + const unsigned char b[16]) +{ + __m128i aa, bb, cc, dd; + + /* The inputs are in big-endian order, so byte-reverse them */ + for (size_t i = 0; i < 16; i++) { + ((uint8_t *) &aa)[i] = a[15 - i]; + ((uint8_t *) &bb)[i] = b[15 - i]; + } + + gcm_clmul(aa, bb, &cc, &dd); + gcm_shift(&cc, &dd); + /* + * Now reduce modulo the GCM polynomial x^128 + x^7 + x^2 + x + 1 + * using [CLMUL-WP] algorithm 5 (p. 18). + * Currently dd:cc holds x3:x2:x1:x0 (already shifted). + */ + __m128i dx = gcm_reduce(cc); + __m128i xh = gcm_mix(dx); + cc = _mm_xor_si128(xh, dd); // x3+h1:x2+h0 + + /* Now byte-reverse the outputs */ + for (size_t i = 0; i < 16; i++) { + c[i] = ((uint8_t *) &cc)[15 - i]; + } + + return; +} + +/* + * Compute decryption round keys from encryption round keys + */ +void mbedtls_aesni_inverse_key(unsigned char *invkey, + const unsigned char *fwdkey, int nr) +{ + __m128i *ik = (__m128i *) invkey; + const __m128i *fk = (const __m128i *) fwdkey + nr; + + *ik = *fk; + for (--fk, ++ik; fk > (const __m128i *) fwdkey; --fk, ++ik) { + *ik = _mm_aesimc_si128(*fk); + } + *ik = *fk; +} + +/* + * Key expansion, 128-bit case + */ +static __m128i aesni_set_rk_128(__m128i state, __m128i xword) +{ + /* + * Finish generating the next round key. + * + * On entry state is r3:r2:r1:r0 and xword is X:stuff:stuff:stuff + * with X = rot( sub( r3 ) ) ^ RCON (obtained with AESKEYGENASSIST). + * + * On exit, xword is r7:r6:r5:r4 + * with r4 = X + r0, r5 = r4 + r1, r6 = r5 + r2, r7 = r6 + r3 + * and this is returned, to be written to the round key buffer. + */ + xword = _mm_shuffle_epi32(xword, 0xff); // X:X:X:X + xword = _mm_xor_si128(xword, state); // X+r3:X+r2:X+r1:r4 + state = _mm_slli_si128(state, 4); // r2:r1:r0:0 + xword = _mm_xor_si128(xword, state); // X+r3+r2:X+r2+r1:r5:r4 + state = _mm_slli_si128(state, 4); // r1:r0:0:0 + xword = _mm_xor_si128(xword, state); // X+r3+r2+r1:r6:r5:r4 + state = _mm_slli_si128(state, 4); // r0:0:0:0 + state = _mm_xor_si128(xword, state); // r7:r6:r5:r4 + return state; +} + +static void aesni_setkey_enc_128(unsigned char *rk_bytes, + const unsigned char *key) +{ + __m128i *rk = (__m128i *) rk_bytes; + + memcpy(&rk[0], key, 16); + rk[1] = aesni_set_rk_128(rk[0], _mm_aeskeygenassist_si128(rk[0], 0x01)); + rk[2] = aesni_set_rk_128(rk[1], _mm_aeskeygenassist_si128(rk[1], 0x02)); + rk[3] = aesni_set_rk_128(rk[2], _mm_aeskeygenassist_si128(rk[2], 0x04)); + rk[4] = aesni_set_rk_128(rk[3], _mm_aeskeygenassist_si128(rk[3], 0x08)); + rk[5] = aesni_set_rk_128(rk[4], _mm_aeskeygenassist_si128(rk[4], 0x10)); + rk[6] = aesni_set_rk_128(rk[5], _mm_aeskeygenassist_si128(rk[5], 0x20)); + rk[7] = aesni_set_rk_128(rk[6], _mm_aeskeygenassist_si128(rk[6], 0x40)); + rk[8] = aesni_set_rk_128(rk[7], _mm_aeskeygenassist_si128(rk[7], 0x80)); + rk[9] = aesni_set_rk_128(rk[8], _mm_aeskeygenassist_si128(rk[8], 0x1B)); + rk[10] = aesni_set_rk_128(rk[9], _mm_aeskeygenassist_si128(rk[9], 0x36)); +} + +/* + * Key expansion, 192-bit case + */ +static void aesni_set_rk_192(__m128i *state0, __m128i *state1, __m128i xword, + unsigned char *rk) +{ + /* + * Finish generating the next 6 quarter-keys. + * + * On entry state0 is r3:r2:r1:r0, state1 is stuff:stuff:r5:r4 + * and xword is stuff:stuff:X:stuff with X = rot( sub( r3 ) ) ^ RCON + * (obtained with AESKEYGENASSIST). + * + * On exit, state0 is r9:r8:r7:r6 and state1 is stuff:stuff:r11:r10 + * and those are written to the round key buffer. + */ + xword = _mm_shuffle_epi32(xword, 0x55); // X:X:X:X + xword = _mm_xor_si128(xword, *state0); // X+r3:X+r2:X+r1:X+r0 + *state0 = _mm_slli_si128(*state0, 4); // r2:r1:r0:0 + xword = _mm_xor_si128(xword, *state0); // X+r3+r2:X+r2+r1:X+r1+r0:X+r0 + *state0 = _mm_slli_si128(*state0, 4); // r1:r0:0:0 + xword = _mm_xor_si128(xword, *state0); // X+r3+r2+r1:X+r2+r1+r0:X+r1+r0:X+r0 + *state0 = _mm_slli_si128(*state0, 4); // r0:0:0:0 + xword = _mm_xor_si128(xword, *state0); // X+r3+r2+r1+r0:X+r2+r1+r0:X+r1+r0:X+r0 + *state0 = xword; // = r9:r8:r7:r6 + + xword = _mm_shuffle_epi32(xword, 0xff); // r9:r9:r9:r9 + xword = _mm_xor_si128(xword, *state1); // stuff:stuff:r9+r5:r9+r4 + *state1 = _mm_slli_si128(*state1, 4); // stuff:stuff:r4:0 + xword = _mm_xor_si128(xword, *state1); // stuff:stuff:r9+r5+r4:r9+r4 + *state1 = xword; // = stuff:stuff:r11:r10 + + /* Store state0 and the low half of state1 into rk, which is conceptually + * an array of 24-byte elements. Since 24 is not a multiple of 16, + * rk is not necessarily aligned so just `*rk = *state0` doesn't work. */ + memcpy(rk, state0, 16); + memcpy(rk + 16, state1, 8); +} + +static void aesni_setkey_enc_192(unsigned char *rk, + const unsigned char *key) +{ + /* First round: use original key */ + memcpy(rk, key, 24); + /* aes.c guarantees that rk is aligned on a 16-byte boundary. */ + __m128i state0 = ((__m128i *) rk)[0]; + __m128i state1 = _mm_loadl_epi64(((__m128i *) rk) + 1); + + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x01), rk + 24 * 1); + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x02), rk + 24 * 2); + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x04), rk + 24 * 3); + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x08), rk + 24 * 4); + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x10), rk + 24 * 5); + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x20), rk + 24 * 6); + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x40), rk + 24 * 7); + aesni_set_rk_192(&state0, &state1, _mm_aeskeygenassist_si128(state1, 0x80), rk + 24 * 8); +} + +/* + * Key expansion, 256-bit case + */ +static void aesni_set_rk_256(__m128i state0, __m128i state1, __m128i xword, + __m128i *rk0, __m128i *rk1) +{ + /* + * Finish generating the next two round keys. + * + * On entry state0 is r3:r2:r1:r0, state1 is r7:r6:r5:r4 and + * xword is X:stuff:stuff:stuff with X = rot( sub( r7 )) ^ RCON + * (obtained with AESKEYGENASSIST). + * + * On exit, *rk0 is r11:r10:r9:r8 and *rk1 is r15:r14:r13:r12 + */ + xword = _mm_shuffle_epi32(xword, 0xff); + xword = _mm_xor_si128(xword, state0); + state0 = _mm_slli_si128(state0, 4); + xword = _mm_xor_si128(xword, state0); + state0 = _mm_slli_si128(state0, 4); + xword = _mm_xor_si128(xword, state0); + state0 = _mm_slli_si128(state0, 4); + state0 = _mm_xor_si128(state0, xword); + *rk0 = state0; + + /* Set xword to stuff:Y:stuff:stuff with Y = subword( r11 ) + * and proceed to generate next round key from there */ + xword = _mm_aeskeygenassist_si128(state0, 0x00); + xword = _mm_shuffle_epi32(xword, 0xaa); + xword = _mm_xor_si128(xword, state1); + state1 = _mm_slli_si128(state1, 4); + xword = _mm_xor_si128(xword, state1); + state1 = _mm_slli_si128(state1, 4); + xword = _mm_xor_si128(xword, state1); + state1 = _mm_slli_si128(state1, 4); + state1 = _mm_xor_si128(state1, xword); + *rk1 = state1; +} + +static void aesni_setkey_enc_256(unsigned char *rk_bytes, + const unsigned char *key) +{ + __m128i *rk = (__m128i *) rk_bytes; + + memcpy(&rk[0], key, 16); + memcpy(&rk[1], key + 16, 16); + + /* + * Main "loop" - Generating one more key than necessary, + * see definition of mbedtls_aes_context.buf + */ + aesni_set_rk_256(rk[0], rk[1], _mm_aeskeygenassist_si128(rk[1], 0x01), &rk[2], &rk[3]); + aesni_set_rk_256(rk[2], rk[3], _mm_aeskeygenassist_si128(rk[3], 0x02), &rk[4], &rk[5]); + aesni_set_rk_256(rk[4], rk[5], _mm_aeskeygenassist_si128(rk[5], 0x04), &rk[6], &rk[7]); + aesni_set_rk_256(rk[6], rk[7], _mm_aeskeygenassist_si128(rk[7], 0x08), &rk[8], &rk[9]); + aesni_set_rk_256(rk[8], rk[9], _mm_aeskeygenassist_si128(rk[9], 0x10), &rk[10], &rk[11]); + aesni_set_rk_256(rk[10], rk[11], _mm_aeskeygenassist_si128(rk[11], 0x20), &rk[12], &rk[13]); + aesni_set_rk_256(rk[12], rk[13], _mm_aeskeygenassist_si128(rk[13], 0x40), &rk[14], &rk[15]); +} + +#else /* MBEDTLS_AESNI_HAVE_CODE == 1 */ + +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#warning \ + "MBEDTLS_AESNI_C is known to cause spurious error reports with some memory sanitizers as they do not understand the assembly code." +#endif +#endif + +/* + * Binutils needs to be at least 2.19 to support AES-NI instructions. + * Unfortunately, a lot of users have a lower version now (2014-04). + * Emit bytecode directly in order to support "old" version of gas. + * + * Opcodes from the Intel architecture reference manual, vol. 3. + * We always use registers, so we don't need prefixes for memory operands. + * Operand macros are in gas order (src, dst) as opposed to Intel order + * (dst, src) in order to blend better into the surrounding assembly code. + */ +#define AESDEC(regs) ".byte 0x66,0x0F,0x38,0xDE," regs "\n\t" +#define AESDECLAST(regs) ".byte 0x66,0x0F,0x38,0xDF," regs "\n\t" +#define AESENC(regs) ".byte 0x66,0x0F,0x38,0xDC," regs "\n\t" +#define AESENCLAST(regs) ".byte 0x66,0x0F,0x38,0xDD," regs "\n\t" +#define AESIMC(regs) ".byte 0x66,0x0F,0x38,0xDB," regs "\n\t" +#define AESKEYGENA(regs, imm) ".byte 0x66,0x0F,0x3A,0xDF," regs "," imm "\n\t" +#define PCLMULQDQ(regs, imm) ".byte 0x66,0x0F,0x3A,0x44," regs "," imm "\n\t" + +#define xmm0_xmm0 "0xC0" +#define xmm0_xmm1 "0xC8" +#define xmm0_xmm2 "0xD0" +#define xmm0_xmm3 "0xD8" +#define xmm0_xmm4 "0xE0" +#define xmm1_xmm0 "0xC1" +#define xmm1_xmm2 "0xD1" + +/* + * AES-NI AES-ECB block en(de)cryption + */ +int mbedtls_aesni_crypt_ecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + asm ("movdqu (%3), %%xmm0 \n\t" // load input + "movdqu (%1), %%xmm1 \n\t" // load round key 0 + "pxor %%xmm1, %%xmm0 \n\t" // round 0 + "add $16, %1 \n\t" // point to next round key + "subl $1, %0 \n\t" // normal rounds = nr - 1 + "test %2, %2 \n\t" // mode? + "jz 2f \n\t" // 0 = decrypt + + "1: \n\t" // encryption loop + "movdqu (%1), %%xmm1 \n\t" // load round key + AESENC(xmm1_xmm0) // do round + "add $16, %1 \n\t" // point to next round key + "subl $1, %0 \n\t" // loop + "jnz 1b \n\t" + "movdqu (%1), %%xmm1 \n\t" // load round key + AESENCLAST(xmm1_xmm0) // last round + "jmp 3f \n\t" + + "2: \n\t" // decryption loop + "movdqu (%1), %%xmm1 \n\t" + AESDEC(xmm1_xmm0) // do round + "add $16, %1 \n\t" + "subl $1, %0 \n\t" + "jnz 2b \n\t" + "movdqu (%1), %%xmm1 \n\t" // load round key + AESDECLAST(xmm1_xmm0) // last round + + "3: \n\t" + "movdqu %%xmm0, (%4) \n\t" // export output + : + : "r" (ctx->nr), "r" (ctx->buf + ctx->rk_offset), "r" (mode), "r" (input), "r" (output) + : "memory", "cc", "xmm0", "xmm1"); + + + return 0; +} + +/* + * GCM multiplication: c = a times b in GF(2^128) + * Based on [CLMUL-WP] algorithms 1 (with equation 27) and 5. + */ +void mbedtls_aesni_gcm_mult(unsigned char c[16], + const unsigned char a[16], + const unsigned char b[16]) +{ + unsigned char aa[16], bb[16], cc[16]; + size_t i; + + /* The inputs are in big-endian order, so byte-reverse them */ + for (i = 0; i < 16; i++) { + aa[i] = a[15 - i]; + bb[i] = b[15 - i]; + } + + asm ("movdqu (%0), %%xmm0 \n\t" // a1:a0 + "movdqu (%1), %%xmm1 \n\t" // b1:b0 + + /* + * Caryless multiplication xmm2:xmm1 = xmm0 * xmm1 + * using [CLMUL-WP] algorithm 1 (p. 12). + */ + "movdqa %%xmm1, %%xmm2 \n\t" // copy of b1:b0 + "movdqa %%xmm1, %%xmm3 \n\t" // same + "movdqa %%xmm1, %%xmm4 \n\t" // same + PCLMULQDQ(xmm0_xmm1, "0x00") // a0*b0 = c1:c0 + PCLMULQDQ(xmm0_xmm2, "0x11") // a1*b1 = d1:d0 + PCLMULQDQ(xmm0_xmm3, "0x10") // a0*b1 = e1:e0 + PCLMULQDQ(xmm0_xmm4, "0x01") // a1*b0 = f1:f0 + "pxor %%xmm3, %%xmm4 \n\t" // e1+f1:e0+f0 + "movdqa %%xmm4, %%xmm3 \n\t" // same + "psrldq $8, %%xmm4 \n\t" // 0:e1+f1 + "pslldq $8, %%xmm3 \n\t" // e0+f0:0 + "pxor %%xmm4, %%xmm2 \n\t" // d1:d0+e1+f1 + "pxor %%xmm3, %%xmm1 \n\t" // c1+e0+f1:c0 + + /* + * Now shift the result one bit to the left, + * taking advantage of [CLMUL-WP] eq 27 (p. 18) + */ + "movdqa %%xmm1, %%xmm3 \n\t" // r1:r0 + "movdqa %%xmm2, %%xmm4 \n\t" // r3:r2 + "psllq $1, %%xmm1 \n\t" // r1<<1:r0<<1 + "psllq $1, %%xmm2 \n\t" // r3<<1:r2<<1 + "psrlq $63, %%xmm3 \n\t" // r1>>63:r0>>63 + "psrlq $63, %%xmm4 \n\t" // r3>>63:r2>>63 + "movdqa %%xmm3, %%xmm5 \n\t" // r1>>63:r0>>63 + "pslldq $8, %%xmm3 \n\t" // r0>>63:0 + "pslldq $8, %%xmm4 \n\t" // r2>>63:0 + "psrldq $8, %%xmm5 \n\t" // 0:r1>>63 + "por %%xmm3, %%xmm1 \n\t" // r1<<1|r0>>63:r0<<1 + "por %%xmm4, %%xmm2 \n\t" // r3<<1|r2>>62:r2<<1 + "por %%xmm5, %%xmm2 \n\t" // r3<<1|r2>>62:r2<<1|r1>>63 + + /* + * Now reduce modulo the GCM polynomial x^128 + x^7 + x^2 + x + 1 + * using [CLMUL-WP] algorithm 5 (p. 18). + * Currently xmm2:xmm1 holds x3:x2:x1:x0 (already shifted). + */ + /* Step 2 (1) */ + "movdqa %%xmm1, %%xmm3 \n\t" // x1:x0 + "movdqa %%xmm1, %%xmm4 \n\t" // same + "movdqa %%xmm1, %%xmm5 \n\t" // same + "psllq $63, %%xmm3 \n\t" // x1<<63:x0<<63 = stuff:a + "psllq $62, %%xmm4 \n\t" // x1<<62:x0<<62 = stuff:b + "psllq $57, %%xmm5 \n\t" // x1<<57:x0<<57 = stuff:c + + /* Step 2 (2) */ + "pxor %%xmm4, %%xmm3 \n\t" // stuff:a+b + "pxor %%xmm5, %%xmm3 \n\t" // stuff:a+b+c + "pslldq $8, %%xmm3 \n\t" // a+b+c:0 + "pxor %%xmm3, %%xmm1 \n\t" // x1+a+b+c:x0 = d:x0 + + /* Steps 3 and 4 */ + "movdqa %%xmm1,%%xmm0 \n\t" // d:x0 + "movdqa %%xmm1,%%xmm4 \n\t" // same + "movdqa %%xmm1,%%xmm5 \n\t" // same + "psrlq $1, %%xmm0 \n\t" // e1:x0>>1 = e1:e0' + "psrlq $2, %%xmm4 \n\t" // f1:x0>>2 = f1:f0' + "psrlq $7, %%xmm5 \n\t" // g1:x0>>7 = g1:g0' + "pxor %%xmm4, %%xmm0 \n\t" // e1+f1:e0'+f0' + "pxor %%xmm5, %%xmm0 \n\t" // e1+f1+g1:e0'+f0'+g0' + // e0'+f0'+g0' is almost e0+f0+g0, ex\tcept for some missing + // bits carried from d. Now get those\t bits back in. + "movdqa %%xmm1,%%xmm3 \n\t" // d:x0 + "movdqa %%xmm1,%%xmm4 \n\t" // same + "movdqa %%xmm1,%%xmm5 \n\t" // same + "psllq $63, %%xmm3 \n\t" // d<<63:stuff + "psllq $62, %%xmm4 \n\t" // d<<62:stuff + "psllq $57, %%xmm5 \n\t" // d<<57:stuff + "pxor %%xmm4, %%xmm3 \n\t" // d<<63+d<<62:stuff + "pxor %%xmm5, %%xmm3 \n\t" // missing bits of d:stuff + "psrldq $8, %%xmm3 \n\t" // 0:missing bits of d + "pxor %%xmm3, %%xmm0 \n\t" // e1+f1+g1:e0+f0+g0 + "pxor %%xmm1, %%xmm0 \n\t" // h1:h0 + "pxor %%xmm2, %%xmm0 \n\t" // x3+h1:x2+h0 + + "movdqu %%xmm0, (%2) \n\t" // done + : + : "r" (aa), "r" (bb), "r" (cc) + : "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"); + + /* Now byte-reverse the outputs */ + for (i = 0; i < 16; i++) { + c[i] = cc[15 - i]; + } + + return; +} + +/* + * Compute decryption round keys from encryption round keys + */ +void mbedtls_aesni_inverse_key(unsigned char *invkey, + const unsigned char *fwdkey, int nr) +{ + unsigned char *ik = invkey; + const unsigned char *fk = fwdkey + 16 * nr; + + memcpy(ik, fk, 16); + + for (fk -= 16, ik += 16; fk > fwdkey; fk -= 16, ik += 16) { + asm ("movdqu (%0), %%xmm0 \n\t" + AESIMC(xmm0_xmm0) + "movdqu %%xmm0, (%1) \n\t" + : + : "r" (fk), "r" (ik) + : "memory", "xmm0"); + } + + memcpy(ik, fk, 16); +} + +/* + * Key expansion, 128-bit case + */ +static void aesni_setkey_enc_128(unsigned char *rk, + const unsigned char *key) +{ + asm ("movdqu (%1), %%xmm0 \n\t" // copy the original key + "movdqu %%xmm0, (%0) \n\t" // as round key 0 + "jmp 2f \n\t" // skip auxiliary routine + + /* + * Finish generating the next round key. + * + * On entry xmm0 is r3:r2:r1:r0 and xmm1 is X:stuff:stuff:stuff + * with X = rot( sub( r3 ) ) ^ RCON. + * + * On exit, xmm0 is r7:r6:r5:r4 + * with r4 = X + r0, r5 = r4 + r1, r6 = r5 + r2, r7 = r6 + r3 + * and those are written to the round key buffer. + */ + "1: \n\t" + "pshufd $0xff, %%xmm1, %%xmm1 \n\t" // X:X:X:X + "pxor %%xmm0, %%xmm1 \n\t" // X+r3:X+r2:X+r1:r4 + "pslldq $4, %%xmm0 \n\t" // r2:r1:r0:0 + "pxor %%xmm0, %%xmm1 \n\t" // X+r3+r2:X+r2+r1:r5:r4 + "pslldq $4, %%xmm0 \n\t" // etc + "pxor %%xmm0, %%xmm1 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm1, %%xmm0 \n\t" // update xmm0 for next time! + "add $16, %0 \n\t" // point to next round key + "movdqu %%xmm0, (%0) \n\t" // write it + "ret \n\t" + + /* Main "loop" */ + "2: \n\t" + AESKEYGENA(xmm0_xmm1, "0x01") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x02") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x04") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x08") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x10") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x20") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x40") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x80") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x1B") "call 1b \n\t" + AESKEYGENA(xmm0_xmm1, "0x36") "call 1b \n\t" + : + : "r" (rk), "r" (key) + : "memory", "cc", "0"); +} + +/* + * Key expansion, 192-bit case + */ +static void aesni_setkey_enc_192(unsigned char *rk, + const unsigned char *key) +{ + asm ("movdqu (%1), %%xmm0 \n\t" // copy original round key + "movdqu %%xmm0, (%0) \n\t" + "add $16, %0 \n\t" + "movq 16(%1), %%xmm1 \n\t" + "movq %%xmm1, (%0) \n\t" + "add $8, %0 \n\t" + "jmp 2f \n\t" // skip auxiliary routine + + /* + * Finish generating the next 6 quarter-keys. + * + * On entry xmm0 is r3:r2:r1:r0, xmm1 is stuff:stuff:r5:r4 + * and xmm2 is stuff:stuff:X:stuff with X = rot( sub( r3 ) ) ^ RCON. + * + * On exit, xmm0 is r9:r8:r7:r6 and xmm1 is stuff:stuff:r11:r10 + * and those are written to the round key buffer. + */ + "1: \n\t" + "pshufd $0x55, %%xmm2, %%xmm2 \n\t" // X:X:X:X + "pxor %%xmm0, %%xmm2 \n\t" // X+r3:X+r2:X+r1:r4 + "pslldq $4, %%xmm0 \n\t" // etc + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm2, %%xmm0 \n\t" // update xmm0 = r9:r8:r7:r6 + "movdqu %%xmm0, (%0) \n\t" + "add $16, %0 \n\t" + "pshufd $0xff, %%xmm0, %%xmm2 \n\t" // r9:r9:r9:r9 + "pxor %%xmm1, %%xmm2 \n\t" // stuff:stuff:r9+r5:r10 + "pslldq $4, %%xmm1 \n\t" // r2:r1:r0:0 + "pxor %%xmm2, %%xmm1 \n\t" // xmm1 = stuff:stuff:r11:r10 + "movq %%xmm1, (%0) \n\t" + "add $8, %0 \n\t" + "ret \n\t" + + "2: \n\t" + AESKEYGENA(xmm1_xmm2, "0x01") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x02") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x04") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x08") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x10") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x20") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x40") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x80") "call 1b \n\t" + + : + : "r" (rk), "r" (key) + : "memory", "cc", "0"); +} + +/* + * Key expansion, 256-bit case + */ +static void aesni_setkey_enc_256(unsigned char *rk, + const unsigned char *key) +{ + asm ("movdqu (%1), %%xmm0 \n\t" + "movdqu %%xmm0, (%0) \n\t" + "add $16, %0 \n\t" + "movdqu 16(%1), %%xmm1 \n\t" + "movdqu %%xmm1, (%0) \n\t" + "jmp 2f \n\t" // skip auxiliary routine + + /* + * Finish generating the next two round keys. + * + * On entry xmm0 is r3:r2:r1:r0, xmm1 is r7:r6:r5:r4 and + * xmm2 is X:stuff:stuff:stuff with X = rot( sub( r7 )) ^ RCON + * + * On exit, xmm0 is r11:r10:r9:r8 and xmm1 is r15:r14:r13:r12 + * and those have been written to the output buffer. + */ + "1: \n\t" + "pshufd $0xff, %%xmm2, %%xmm2 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm0, %%xmm2 \n\t" + "pslldq $4, %%xmm0 \n\t" + "pxor %%xmm2, %%xmm0 \n\t" + "add $16, %0 \n\t" + "movdqu %%xmm0, (%0) \n\t" + + /* Set xmm2 to stuff:Y:stuff:stuff with Y = subword( r11 ) + * and proceed to generate next round key from there */ + AESKEYGENA(xmm0_xmm2, "0x00") + "pshufd $0xaa, %%xmm2, %%xmm2 \n\t" + "pxor %%xmm1, %%xmm2 \n\t" + "pslldq $4, %%xmm1 \n\t" + "pxor %%xmm1, %%xmm2 \n\t" + "pslldq $4, %%xmm1 \n\t" + "pxor %%xmm1, %%xmm2 \n\t" + "pslldq $4, %%xmm1 \n\t" + "pxor %%xmm2, %%xmm1 \n\t" + "add $16, %0 \n\t" + "movdqu %%xmm1, (%0) \n\t" + "ret \n\t" + + /* + * Main "loop" - Generating one more key than necessary, + * see definition of mbedtls_aes_context.buf + */ + "2: \n\t" + AESKEYGENA(xmm1_xmm2, "0x01") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x02") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x04") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x08") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x10") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x20") "call 1b \n\t" + AESKEYGENA(xmm1_xmm2, "0x40") "call 1b \n\t" + : + : "r" (rk), "r" (key) + : "memory", "cc", "0"); +} + +#endif /* MBEDTLS_AESNI_HAVE_CODE */ + +/* + * Key expansion, wrapper + */ +int mbedtls_aesni_setkey_enc(unsigned char *rk, + const unsigned char *key, + size_t bits) +{ + switch (bits) { + case 128: aesni_setkey_enc_128(rk, key); break; + case 192: aesni_setkey_enc_192(rk, key); break; + case 256: aesni_setkey_enc_256(rk, key); break; + default: return MBEDTLS_ERR_AES_INVALID_KEY_LENGTH; + } + + return 0; +} + +#endif /* MBEDTLS_AESNI_HAVE_CODE */ + +#endif /* MBEDTLS_AESNI_C */ diff --git a/r5dev/thirdparty/mbedtls/aesni.h b/r5dev/thirdparty/mbedtls/aesni.h new file mode 100644 index 00000000..51b770f3 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/aesni.h @@ -0,0 +1,166 @@ +/** + * \file aesni.h + * + * \brief AES-NI for hardware AES acceleration on some Intel processors + * + * \warning These functions are only for internal use by other library + * functions; you must not call them directly. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_AESNI_H +#define MBEDTLS_AESNI_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/aes.h" + +#define MBEDTLS_AESNI_AES 0x02000000u +#define MBEDTLS_AESNI_CLMUL 0x00000002u + +/* Can we do AESNI with inline assembly? + * (Only implemented with gas syntax, only for 64-bit.) + */ +#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) && \ + (defined(__amd64__) || defined(__x86_64__)) && \ + !defined(MBEDTLS_HAVE_X86_64) +#define MBEDTLS_HAVE_X86_64 +#endif + +#if defined(MBEDTLS_AESNI_C) + +/* Can we do AESNI with intrinsics? + * (Only implemented with certain compilers, only for certain targets.) + */ +#undef MBEDTLS_AESNI_HAVE_INTRINSICS +#if defined(_MSC_VER) +/* Visual Studio supports AESNI intrinsics since VS 2008 SP1. We only support + * VS 2013 and up for other reasons anyway, so no need to check the version. */ +#define MBEDTLS_AESNI_HAVE_INTRINSICS +#endif +/* GCC-like compilers: currently, we only support intrinsics if the requisite + * target flag is enabled when building the library (e.g. `gcc -mpclmul -msse2` + * or `clang -maes -mpclmul`). */ +#if defined(__GNUC__) && defined(__AES__) && defined(__PCLMUL__) +#define MBEDTLS_AESNI_HAVE_INTRINSICS +#endif + +/* Choose the implementation of AESNI, if one is available. */ +#undef MBEDTLS_AESNI_HAVE_CODE +/* To minimize disruption when releasing the intrinsics-based implementation, + * favor the assembly-based implementation if it's available. We intend to + * revise this in a later release of Mbed TLS 3.x. In the long run, we will + * likely remove the assembly implementation. */ +#if defined(MBEDTLS_HAVE_X86_64) +#define MBEDTLS_AESNI_HAVE_CODE 1 // via assembly +#elif defined(MBEDTLS_AESNI_HAVE_INTRINSICS) +#define MBEDTLS_AESNI_HAVE_CODE 2 // via intrinsics +#endif + +#if defined(MBEDTLS_AESNI_HAVE_CODE) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Internal function to detect the AES-NI feature in CPUs. + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param what The feature to detect + * (MBEDTLS_AESNI_AES or MBEDTLS_AESNI_CLMUL) + * + * \return 1 if CPU has support for the feature, 0 otherwise + */ +int mbedtls_aesni_has_support(unsigned int what); + +/** + * \brief Internal AES-NI AES-ECB block encryption and decryption + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param ctx AES context + * \param mode MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 on success (cannot fail) + */ +int mbedtls_aesni_crypt_ecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]); + +/** + * \brief Internal GCM multiplication: c = a * b in GF(2^128) + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param c Result + * \param a First operand + * \param b Second operand + * + * \note Both operands and result are bit strings interpreted as + * elements of GF(2^128) as per the GCM spec. + */ +void mbedtls_aesni_gcm_mult(unsigned char c[16], + const unsigned char a[16], + const unsigned char b[16]); + +/** + * \brief Internal round key inversion. This function computes + * decryption round keys from the encryption round keys. + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param invkey Round keys for the equivalent inverse cipher + * \param fwdkey Original round keys (for encryption) + * \param nr Number of rounds (that is, number of round keys minus one) + */ +void mbedtls_aesni_inverse_key(unsigned char *invkey, + const unsigned char *fwdkey, + int nr); + +/** + * \brief Internal key expansion for encryption + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param rk Destination buffer where the round keys are written + * \param key Encryption key + * \param bits Key size in bits (must be 128, 192 or 256) + * + * \return 0 if successful, or MBEDTLS_ERR_AES_INVALID_KEY_LENGTH + */ +int mbedtls_aesni_setkey_enc(unsigned char *rk, + const unsigned char *key, + size_t bits); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_AESNI_HAVE_CODE */ +#endif /* MBEDTLS_AESNI_C */ + +#endif /* MBEDTLS_AESNI_H */ diff --git a/r5dev/thirdparty/mbedtls/alignment.h b/r5dev/thirdparty/mbedtls/alignment.h new file mode 100644 index 00000000..a518a8a3 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/alignment.h @@ -0,0 +1,520 @@ +/** + * \file alignment.h + * + * \brief Utility code for dealing with unaligned memory accesses + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_LIBRARY_ALIGNMENT_H +#define MBEDTLS_LIBRARY_ALIGNMENT_H + +#include +#include +#include + +#include "mbedtls/build_info.h" + +/* + * Define MBEDTLS_EFFICIENT_UNALIGNED_ACCESS for architectures where unaligned memory + * accesses are known to be efficient. + * + * All functions defined here will behave correctly regardless, but might be less + * efficient when this is not defined. + */ +#if defined(__ARM_FEATURE_UNALIGNED) \ + || defined(__i386__) || defined(__amd64__) || defined(__x86_64__) +/* + * __ARM_FEATURE_UNALIGNED is defined where appropriate by armcc, gcc 7, clang 9 + * (and later versions) for Arm v7 and later; all x86 platforms should have + * efficient unaligned access. + */ +#define MBEDTLS_EFFICIENT_UNALIGNED_ACCESS +#endif + +/** + * Read the unsigned 16 bits integer from the given address, which need not + * be aligned. + * + * \param p pointer to 2 bytes of data + * \return Data at the given address + */ +inline uint16_t mbedtls_get_unaligned_uint16(const void *p) +{ + uint16_t r; + memcpy(&r, p, sizeof(r)); + return r; +} + +/** + * Write the unsigned 16 bits integer to the given address, which need not + * be aligned. + * + * \param p pointer to 2 bytes of data + * \param x data to write + */ +inline void mbedtls_put_unaligned_uint16(void *p, uint16_t x) +{ + memcpy(p, &x, sizeof(x)); +} + +/** + * Read the unsigned 32 bits integer from the given address, which need not + * be aligned. + * + * \param p pointer to 4 bytes of data + * \return Data at the given address + */ +inline uint32_t mbedtls_get_unaligned_uint32(const void *p) +{ + uint32_t r; + memcpy(&r, p, sizeof(r)); + return r; +} + +/** + * Write the unsigned 32 bits integer to the given address, which need not + * be aligned. + * + * \param p pointer to 4 bytes of data + * \param x data to write + */ +inline void mbedtls_put_unaligned_uint32(void *p, uint32_t x) +{ + memcpy(p, &x, sizeof(x)); +} + +/** + * Read the unsigned 64 bits integer from the given address, which need not + * be aligned. + * + * \param p pointer to 8 bytes of data + * \return Data at the given address + */ +inline uint64_t mbedtls_get_unaligned_uint64(const void *p) +{ + uint64_t r; + memcpy(&r, p, sizeof(r)); + return r; +} + +/** + * Write the unsigned 64 bits integer to the given address, which need not + * be aligned. + * + * \param p pointer to 8 bytes of data + * \param x data to write + */ +inline void mbedtls_put_unaligned_uint64(void *p, uint64_t x) +{ + memcpy(p, &x, sizeof(x)); +} + +/** Byte Reading Macros + * + * Given a multi-byte integer \p x, MBEDTLS_BYTE_n retrieves the n-th + * byte from x, where byte 0 is the least significant byte. + */ +#define MBEDTLS_BYTE_0(x) ((uint8_t) ((x) & 0xff)) +#define MBEDTLS_BYTE_1(x) ((uint8_t) (((x) >> 8) & 0xff)) +#define MBEDTLS_BYTE_2(x) ((uint8_t) (((x) >> 16) & 0xff)) +#define MBEDTLS_BYTE_3(x) ((uint8_t) (((x) >> 24) & 0xff)) +#define MBEDTLS_BYTE_4(x) ((uint8_t) (((x) >> 32) & 0xff)) +#define MBEDTLS_BYTE_5(x) ((uint8_t) (((x) >> 40) & 0xff)) +#define MBEDTLS_BYTE_6(x) ((uint8_t) (((x) >> 48) & 0xff)) +#define MBEDTLS_BYTE_7(x) ((uint8_t) (((x) >> 56) & 0xff)) + +/* + * Detect GCC built-in byteswap routines + */ +#if defined(__GNUC__) && defined(__GNUC_PREREQ) +#if __GNUC_PREREQ(4, 8) +#define MBEDTLS_BSWAP16 __builtin_bswap16 +#endif /* __GNUC_PREREQ(4,8) */ +#if __GNUC_PREREQ(4, 3) +#define MBEDTLS_BSWAP32 __builtin_bswap32 +#define MBEDTLS_BSWAP64 __builtin_bswap64 +#endif /* __GNUC_PREREQ(4,3) */ +#endif /* defined(__GNUC__) && defined(__GNUC_PREREQ) */ + +/* + * Detect Clang built-in byteswap routines + */ +#if defined(__clang__) && defined(__has_builtin) +#if __has_builtin(__builtin_bswap16) && !defined(MBEDTLS_BSWAP16) +#define MBEDTLS_BSWAP16 __builtin_bswap16 +#endif /* __has_builtin(__builtin_bswap16) */ +#if __has_builtin(__builtin_bswap32) && !defined(MBEDTLS_BSWAP32) +#define MBEDTLS_BSWAP32 __builtin_bswap32 +#endif /* __has_builtin(__builtin_bswap32) */ +#if __has_builtin(__builtin_bswap64) && !defined(MBEDTLS_BSWAP64) +#define MBEDTLS_BSWAP64 __builtin_bswap64 +#endif /* __has_builtin(__builtin_bswap64) */ +#endif /* defined(__clang__) && defined(__has_builtin) */ + +/* + * Detect MSVC built-in byteswap routines + */ +#if defined(_MSC_VER) +#if !defined(MBEDTLS_BSWAP16) +#define MBEDTLS_BSWAP16 _byteswap_ushort +#endif +#if !defined(MBEDTLS_BSWAP32) +#define MBEDTLS_BSWAP32 _byteswap_ulong +#endif +#if !defined(MBEDTLS_BSWAP64) +#define MBEDTLS_BSWAP64 _byteswap_uint64 +#endif +#endif /* defined(_MSC_VER) */ + +/* Detect armcc built-in byteswap routine */ +#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 410000) && !defined(MBEDTLS_BSWAP32) +#define MBEDTLS_BSWAP32 __rev +#endif + +/* + * Where compiler built-ins are not present, fall back to C code that the + * compiler may be able to detect and transform into the relevant bswap or + * similar instruction. + */ +#if !defined(MBEDTLS_BSWAP16) +static inline uint16_t mbedtls_bswap16(uint16_t x) +{ + return + (x & 0x00ff) << 8 | + (x & 0xff00) >> 8; +} +#define MBEDTLS_BSWAP16 mbedtls_bswap16 +#endif /* !defined(MBEDTLS_BSWAP16) */ + +#if !defined(MBEDTLS_BSWAP32) +static inline uint32_t mbedtls_bswap32(uint32_t x) +{ + return + (x & 0x000000ff) << 24 | + (x & 0x0000ff00) << 8 | + (x & 0x00ff0000) >> 8 | + (x & 0xff000000) >> 24; +} +#define MBEDTLS_BSWAP32 mbedtls_bswap32 +#endif /* !defined(MBEDTLS_BSWAP32) */ + +#if !defined(MBEDTLS_BSWAP64) +static inline uint64_t mbedtls_bswap64(uint64_t x) +{ + return + (x & 0x00000000000000ffULL) << 56 | + (x & 0x000000000000ff00ULL) << 40 | + (x & 0x0000000000ff0000ULL) << 24 | + (x & 0x00000000ff000000ULL) << 8 | + (x & 0x000000ff00000000ULL) >> 8 | + (x & 0x0000ff0000000000ULL) >> 24 | + (x & 0x00ff000000000000ULL) >> 40 | + (x & 0xff00000000000000ULL) >> 56; +} +#define MBEDTLS_BSWAP64 mbedtls_bswap64 +#endif /* !defined(MBEDTLS_BSWAP64) */ + +#if !defined(__BYTE_ORDER__) +static const uint16_t mbedtls_byte_order_detector = { 0x100 }; +#define MBEDTLS_IS_BIG_ENDIAN (*((unsigned char *) (&mbedtls_byte_order_detector)) == 0x01) +#else +#define MBEDTLS_IS_BIG_ENDIAN ((__BYTE_ORDER__) == (__ORDER_BIG_ENDIAN__)) +#endif /* !defined(__BYTE_ORDER__) */ + +/** + * Get the unsigned 32 bits integer corresponding to four bytes in + * big-endian order (MSB first). + * + * \param data Base address of the memory to get the four bytes from. + * \param offset Offset from \p data of the first and most significant + * byte of the four bytes to build the 32 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT32_BE(data, offset) \ + ((MBEDTLS_IS_BIG_ENDIAN) \ + ? mbedtls_get_unaligned_uint32((data) + (offset)) \ + : MBEDTLS_BSWAP32(mbedtls_get_unaligned_uint32((data) + (offset))) \ + ) + +/** + * Put in memory a 32 bits unsigned integer in big-endian order. + * + * \param n 32 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 32 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the most significant + * byte of the 32 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT32_BE(n, data, offset) \ + { \ + if (MBEDTLS_IS_BIG_ENDIAN) \ + { \ + mbedtls_put_unaligned_uint32((data) + (offset), (uint32_t) (n)); \ + } \ + else \ + { \ + mbedtls_put_unaligned_uint32((data) + (offset), MBEDTLS_BSWAP32((uint32_t) (n))); \ + } \ + } + +/** + * Get the unsigned 32 bits integer corresponding to four bytes in + * little-endian order (LSB first). + * + * \param data Base address of the memory to get the four bytes from. + * \param offset Offset from \p data of the first and least significant + * byte of the four bytes to build the 32 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT32_LE(data, offset) \ + ((MBEDTLS_IS_BIG_ENDIAN) \ + ? MBEDTLS_BSWAP32(mbedtls_get_unaligned_uint32((data) + (offset))) \ + : mbedtls_get_unaligned_uint32((data) + (offset)) \ + ) + + +/** + * Put in memory a 32 bits unsigned integer in little-endian order. + * + * \param n 32 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 32 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the least significant + * byte of the 32 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT32_LE(n, data, offset) \ + { \ + if (MBEDTLS_IS_BIG_ENDIAN) \ + { \ + mbedtls_put_unaligned_uint32((data) + (offset), MBEDTLS_BSWAP32((uint32_t) (n))); \ + } \ + else \ + { \ + mbedtls_put_unaligned_uint32((data) + (offset), ((uint32_t) (n))); \ + } \ + } + +/** + * Get the unsigned 16 bits integer corresponding to two bytes in + * little-endian order (LSB first). + * + * \param data Base address of the memory to get the two bytes from. + * \param offset Offset from \p data of the first and least significant + * byte of the two bytes to build the 16 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT16_LE(data, offset) \ + ((MBEDTLS_IS_BIG_ENDIAN) \ + ? MBEDTLS_BSWAP16(mbedtls_get_unaligned_uint16((data) + (offset))) \ + : mbedtls_get_unaligned_uint16((data) + (offset)) \ + ) + +/** + * Put in memory a 16 bits unsigned integer in little-endian order. + * + * \param n 16 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 16 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the least significant + * byte of the 16 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT16_LE(n, data, offset) \ + { \ + if (MBEDTLS_IS_BIG_ENDIAN) \ + { \ + mbedtls_put_unaligned_uint16((data) + (offset), MBEDTLS_BSWAP16((uint16_t) (n))); \ + } \ + else \ + { \ + mbedtls_put_unaligned_uint16((data) + (offset), (uint16_t) (n)); \ + } \ + } + +/** + * Get the unsigned 16 bits integer corresponding to two bytes in + * big-endian order (MSB first). + * + * \param data Base address of the memory to get the two bytes from. + * \param offset Offset from \p data of the first and most significant + * byte of the two bytes to build the 16 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT16_BE(data, offset) \ + ((MBEDTLS_IS_BIG_ENDIAN) \ + ? mbedtls_get_unaligned_uint16((data) + (offset)) \ + : MBEDTLS_BSWAP16(mbedtls_get_unaligned_uint16((data) + (offset))) \ + ) + +/** + * Put in memory a 16 bits unsigned integer in big-endian order. + * + * \param n 16 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 16 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the most significant + * byte of the 16 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT16_BE(n, data, offset) \ + { \ + if (MBEDTLS_IS_BIG_ENDIAN) \ + { \ + mbedtls_put_unaligned_uint16((data) + (offset), (uint16_t) (n)); \ + } \ + else \ + { \ + mbedtls_put_unaligned_uint16((data) + (offset), MBEDTLS_BSWAP16((uint16_t) (n))); \ + } \ + } + +/** + * Get the unsigned 24 bits integer corresponding to three bytes in + * big-endian order (MSB first). + * + * \param data Base address of the memory to get the three bytes from. + * \param offset Offset from \p data of the first and most significant + * byte of the three bytes to build the 24 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT24_BE(data, offset) \ + ( \ + ((uint32_t) (data)[(offset)] << 16) \ + | ((uint32_t) (data)[(offset) + 1] << 8) \ + | ((uint32_t) (data)[(offset) + 2]) \ + ) + +/** + * Put in memory a 24 bits unsigned integer in big-endian order. + * + * \param n 24 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 24 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the most significant + * byte of the 24 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT24_BE(n, data, offset) \ + { \ + (data)[(offset)] = MBEDTLS_BYTE_2(n); \ + (data)[(offset) + 1] = MBEDTLS_BYTE_1(n); \ + (data)[(offset) + 2] = MBEDTLS_BYTE_0(n); \ + } + +/** + * Get the unsigned 24 bits integer corresponding to three bytes in + * little-endian order (LSB first). + * + * \param data Base address of the memory to get the three bytes from. + * \param offset Offset from \p data of the first and least significant + * byte of the three bytes to build the 24 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT24_LE(data, offset) \ + ( \ + ((uint32_t) (data)[(offset)]) \ + | ((uint32_t) (data)[(offset) + 1] << 8) \ + | ((uint32_t) (data)[(offset) + 2] << 16) \ + ) + +/** + * Put in memory a 24 bits unsigned integer in little-endian order. + * + * \param n 24 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 24 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the least significant + * byte of the 24 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT24_LE(n, data, offset) \ + { \ + (data)[(offset)] = MBEDTLS_BYTE_0(n); \ + (data)[(offset) + 1] = MBEDTLS_BYTE_1(n); \ + (data)[(offset) + 2] = MBEDTLS_BYTE_2(n); \ + } + +/** + * Get the unsigned 64 bits integer corresponding to eight bytes in + * big-endian order (MSB first). + * + * \param data Base address of the memory to get the eight bytes from. + * \param offset Offset from \p data of the first and most significant + * byte of the eight bytes to build the 64 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT64_BE(data, offset) \ + ((MBEDTLS_IS_BIG_ENDIAN) \ + ? mbedtls_get_unaligned_uint64((data) + (offset)) \ + : MBEDTLS_BSWAP64(mbedtls_get_unaligned_uint64((data) + (offset))) \ + ) + +/** + * Put in memory a 64 bits unsigned integer in big-endian order. + * + * \param n 64 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 64 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the most significant + * byte of the 64 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT64_BE(n, data, offset) \ + { \ + if (MBEDTLS_IS_BIG_ENDIAN) \ + { \ + mbedtls_put_unaligned_uint64((data) + (offset), (uint64_t) (n)); \ + } \ + else \ + { \ + mbedtls_put_unaligned_uint64((data) + (offset), MBEDTLS_BSWAP64((uint64_t) (n))); \ + } \ + } + +/** + * Get the unsigned 64 bits integer corresponding to eight bytes in + * little-endian order (LSB first). + * + * \param data Base address of the memory to get the eight bytes from. + * \param offset Offset from \p data of the first and least significant + * byte of the eight bytes to build the 64 bits unsigned + * integer from. + */ +#define MBEDTLS_GET_UINT64_LE(data, offset) \ + ((MBEDTLS_IS_BIG_ENDIAN) \ + ? MBEDTLS_BSWAP64(mbedtls_get_unaligned_uint64((data) + (offset))) \ + : mbedtls_get_unaligned_uint64((data) + (offset)) \ + ) + +/** + * Put in memory a 64 bits unsigned integer in little-endian order. + * + * \param n 64 bits unsigned integer to put in memory. + * \param data Base address of the memory where to put the 64 + * bits unsigned integer in. + * \param offset Offset from \p data where to put the least significant + * byte of the 64 bits unsigned integer \p n. + */ +#define MBEDTLS_PUT_UINT64_LE(n, data, offset) \ + { \ + if (MBEDTLS_IS_BIG_ENDIAN) \ + { \ + mbedtls_put_unaligned_uint64((data) + (offset), MBEDTLS_BSWAP64((uint64_t) (n))); \ + } \ + else \ + { \ + mbedtls_put_unaligned_uint64((data) + (offset), (uint64_t) (n)); \ + } \ + } + +#endif /* MBEDTLS_LIBRARY_ALIGNMENT_H */ diff --git a/r5dev/thirdparty/mbedtls/aria.c b/r5dev/thirdparty/mbedtls/aria.c new file mode 100644 index 00000000..09803622 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/aria.c @@ -0,0 +1,1003 @@ +/* + * ARIA implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This implementation is based on the following standards: + * [1] http://210.104.33.10/ARIA/doc/ARIA-specification-e.pdf + * [2] https://tools.ietf.org/html/rfc5794 + */ + +#include "common.h" + +#if defined(MBEDTLS_ARIA_C) + +#include "mbedtls/aria.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_ARIA_ALT) + +#include "mbedtls/platform_util.h" + +/* Parameter validation macros */ +#define ARIA_VALIDATE_RET(cond) \ + MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_ARIA_BAD_INPUT_DATA) +#define ARIA_VALIDATE(cond) \ + MBEDTLS_INTERNAL_VALIDATE(cond) + +/* + * modify byte order: ( A B C D ) -> ( B A D C ), i.e. swap pairs of bytes + * + * This is submatrix P1 in [1] Appendix B.1 + * + * Common compilers fail to translate this to minimal number of instructions, + * so let's provide asm versions for common platforms with C fallback. + */ +#if defined(MBEDTLS_HAVE_ASM) +#if defined(__arm__) /* rev16 available from v6 up */ +/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */ +#if defined(__GNUC__) && \ + (!defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000) && \ + __ARM_ARCH >= 6 +static inline uint32_t aria_p1(uint32_t x) +{ + uint32_t r; + __asm("rev16 %0, %1" : "=l" (r) : "l" (x)); + return r; +} +#define ARIA_P1 aria_p1 +#elif defined(__ARMCC_VERSION) && __ARMCC_VERSION < 6000000 && \ + (__TARGET_ARCH_ARM >= 6 || __TARGET_ARCH_THUMB >= 3) +static inline uint32_t aria_p1(uint32_t x) +{ + uint32_t r; + __asm("rev16 r, x"); + return r; +} +#define ARIA_P1 aria_p1 +#endif +#endif /* arm */ +#if defined(__GNUC__) && \ + defined(__i386__) || defined(__amd64__) || defined(__x86_64__) +/* I couldn't find an Intel equivalent of rev16, so two instructions */ +#define ARIA_P1(x) ARIA_P2(ARIA_P3(x)) +#endif /* x86 gnuc */ +#endif /* MBEDTLS_HAVE_ASM && GNUC */ +#if !defined(ARIA_P1) +#define ARIA_P1(x) ((((x) >> 8) & 0x00FF00FF) ^ (((x) & 0x00FF00FF) << 8)) +#endif + +/* + * modify byte order: ( A B C D ) -> ( C D A B ), i.e. rotate by 16 bits + * + * This is submatrix P2 in [1] Appendix B.1 + * + * Common compilers will translate this to a single instruction. + */ +#define ARIA_P2(x) (((x) >> 16) ^ ((x) << 16)) + +/* + * modify byte order: ( A B C D ) -> ( D C B A ), i.e. change endianness + * + * This is submatrix P3 in [1] Appendix B.1 + */ +#define ARIA_P3(x) MBEDTLS_BSWAP32(x) + +/* + * ARIA Affine Transform + * (a, b, c, d) = state in/out + * + * If we denote the first byte of input by 0, ..., the last byte by f, + * then inputs are: a = 0123, b = 4567, c = 89ab, d = cdef. + * + * Reading [1] 2.4 or [2] 2.4.3 in columns and performing simple + * rearrangements on adjacent pairs, output is: + * + * a = 3210 + 4545 + 6767 + 88aa + 99bb + dccd + effe + * = 3210 + 4567 + 6745 + 89ab + 98ba + dcfe + efcd + * b = 0101 + 2323 + 5476 + 8998 + baab + eecc + ffdd + * = 0123 + 2301 + 5476 + 89ab + ba98 + efcd + fedc + * c = 0022 + 1133 + 4554 + 7667 + ab89 + dcdc + fefe + * = 0123 + 1032 + 4567 + 7654 + ab89 + dcfe + fedc + * d = 1001 + 2332 + 6644 + 7755 + 9898 + baba + cdef + * = 1032 + 2301 + 6745 + 7654 + 98ba + ba98 + cdef + * + * Note: another presentation of the A transform can be found as the first + * half of App. B.1 in [1] in terms of 4-byte operators P1, P2, P3 and P4. + * The implementation below uses only P1 and P2 as they are sufficient. + */ +static inline void aria_a(uint32_t *a, uint32_t *b, + uint32_t *c, uint32_t *d) +{ + uint32_t ta, tb, tc; + ta = *b; // 4567 + *b = *a; // 0123 + *a = ARIA_P2(ta); // 6745 + tb = ARIA_P2(*d); // efcd + *d = ARIA_P1(*c); // 98ba + *c = ARIA_P1(tb); // fedc + ta ^= *d; // 4567+98ba + tc = ARIA_P2(*b); // 2301 + ta = ARIA_P1(ta) ^ tc ^ *c; // 2301+5476+89ab+fedc + tb ^= ARIA_P2(*d); // ba98+efcd + tc ^= ARIA_P1(*a); // 2301+7654 + *b ^= ta ^ tb; // 0123+2301+5476+89ab+ba98+efcd+fedc OUT + tb = ARIA_P2(tb) ^ ta; // 2301+5476+89ab+98ba+cdef+fedc + *a ^= ARIA_P1(tb); // 3210+4567+6745+89ab+98ba+dcfe+efcd OUT + ta = ARIA_P2(ta); // 0123+7654+ab89+dcfe + *d ^= ARIA_P1(ta) ^ tc; // 1032+2301+6745+7654+98ba+ba98+cdef OUT + tc = ARIA_P2(tc); // 0123+5476 + *c ^= ARIA_P1(tc) ^ ta; // 0123+1032+4567+7654+ab89+dcfe+fedc OUT +} + +/* + * ARIA Substitution Layer SL1 / SL2 + * (a, b, c, d) = state in/out + * (sa, sb, sc, sd) = 256 8-bit S-Boxes (see below) + * + * By passing sb1, sb2, is1, is2 as S-Boxes you get SL1 + * By passing is1, is2, sb1, sb2 as S-Boxes you get SL2 + */ +static inline void aria_sl(uint32_t *a, uint32_t *b, + uint32_t *c, uint32_t *d, + const uint8_t sa[256], const uint8_t sb[256], + const uint8_t sc[256], const uint8_t sd[256]) +{ + *a = ((uint32_t) sa[MBEDTLS_BYTE_0(*a)]) ^ + (((uint32_t) sb[MBEDTLS_BYTE_1(*a)]) << 8) ^ + (((uint32_t) sc[MBEDTLS_BYTE_2(*a)]) << 16) ^ + (((uint32_t) sd[MBEDTLS_BYTE_3(*a)]) << 24); + *b = ((uint32_t) sa[MBEDTLS_BYTE_0(*b)]) ^ + (((uint32_t) sb[MBEDTLS_BYTE_1(*b)]) << 8) ^ + (((uint32_t) sc[MBEDTLS_BYTE_2(*b)]) << 16) ^ + (((uint32_t) sd[MBEDTLS_BYTE_3(*b)]) << 24); + *c = ((uint32_t) sa[MBEDTLS_BYTE_0(*c)]) ^ + (((uint32_t) sb[MBEDTLS_BYTE_1(*c)]) << 8) ^ + (((uint32_t) sc[MBEDTLS_BYTE_2(*c)]) << 16) ^ + (((uint32_t) sd[MBEDTLS_BYTE_3(*c)]) << 24); + *d = ((uint32_t) sa[MBEDTLS_BYTE_0(*d)]) ^ + (((uint32_t) sb[MBEDTLS_BYTE_1(*d)]) << 8) ^ + (((uint32_t) sc[MBEDTLS_BYTE_2(*d)]) << 16) ^ + (((uint32_t) sd[MBEDTLS_BYTE_3(*d)]) << 24); +} + +/* + * S-Boxes + */ +static const uint8_t aria_sb1[256] = +{ + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, + 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, + 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, + 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, + 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, + 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, + 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, + 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, + 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, + 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, + 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, + 0xB0, 0x54, 0xBB, 0x16 +}; + +static const uint8_t aria_sb2[256] = +{ + 0xE2, 0x4E, 0x54, 0xFC, 0x94, 0xC2, 0x4A, 0xCC, 0x62, 0x0D, 0x6A, 0x46, + 0x3C, 0x4D, 0x8B, 0xD1, 0x5E, 0xFA, 0x64, 0xCB, 0xB4, 0x97, 0xBE, 0x2B, + 0xBC, 0x77, 0x2E, 0x03, 0xD3, 0x19, 0x59, 0xC1, 0x1D, 0x06, 0x41, 0x6B, + 0x55, 0xF0, 0x99, 0x69, 0xEA, 0x9C, 0x18, 0xAE, 0x63, 0xDF, 0xE7, 0xBB, + 0x00, 0x73, 0x66, 0xFB, 0x96, 0x4C, 0x85, 0xE4, 0x3A, 0x09, 0x45, 0xAA, + 0x0F, 0xEE, 0x10, 0xEB, 0x2D, 0x7F, 0xF4, 0x29, 0xAC, 0xCF, 0xAD, 0x91, + 0x8D, 0x78, 0xC8, 0x95, 0xF9, 0x2F, 0xCE, 0xCD, 0x08, 0x7A, 0x88, 0x38, + 0x5C, 0x83, 0x2A, 0x28, 0x47, 0xDB, 0xB8, 0xC7, 0x93, 0xA4, 0x12, 0x53, + 0xFF, 0x87, 0x0E, 0x31, 0x36, 0x21, 0x58, 0x48, 0x01, 0x8E, 0x37, 0x74, + 0x32, 0xCA, 0xE9, 0xB1, 0xB7, 0xAB, 0x0C, 0xD7, 0xC4, 0x56, 0x42, 0x26, + 0x07, 0x98, 0x60, 0xD9, 0xB6, 0xB9, 0x11, 0x40, 0xEC, 0x20, 0x8C, 0xBD, + 0xA0, 0xC9, 0x84, 0x04, 0x49, 0x23, 0xF1, 0x4F, 0x50, 0x1F, 0x13, 0xDC, + 0xD8, 0xC0, 0x9E, 0x57, 0xE3, 0xC3, 0x7B, 0x65, 0x3B, 0x02, 0x8F, 0x3E, + 0xE8, 0x25, 0x92, 0xE5, 0x15, 0xDD, 0xFD, 0x17, 0xA9, 0xBF, 0xD4, 0x9A, + 0x7E, 0xC5, 0x39, 0x67, 0xFE, 0x76, 0x9D, 0x43, 0xA7, 0xE1, 0xD0, 0xF5, + 0x68, 0xF2, 0x1B, 0x34, 0x70, 0x05, 0xA3, 0x8A, 0xD5, 0x79, 0x86, 0xA8, + 0x30, 0xC6, 0x51, 0x4B, 0x1E, 0xA6, 0x27, 0xF6, 0x35, 0xD2, 0x6E, 0x24, + 0x16, 0x82, 0x5F, 0xDA, 0xE6, 0x75, 0xA2, 0xEF, 0x2C, 0xB2, 0x1C, 0x9F, + 0x5D, 0x6F, 0x80, 0x0A, 0x72, 0x44, 0x9B, 0x6C, 0x90, 0x0B, 0x5B, 0x33, + 0x7D, 0x5A, 0x52, 0xF3, 0x61, 0xA1, 0xF7, 0xB0, 0xD6, 0x3F, 0x7C, 0x6D, + 0xED, 0x14, 0xE0, 0xA5, 0x3D, 0x22, 0xB3, 0xF8, 0x89, 0xDE, 0x71, 0x1A, + 0xAF, 0xBA, 0xB5, 0x81 +}; + +static const uint8_t aria_is1[256] = +{ + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, + 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, + 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, + 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, + 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, + 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, + 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, + 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, + 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, + 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0C, 0x7D +}; + +static const uint8_t aria_is2[256] = +{ + 0x30, 0x68, 0x99, 0x1B, 0x87, 0xB9, 0x21, 0x78, 0x50, 0x39, 0xDB, 0xE1, + 0x72, 0x09, 0x62, 0x3C, 0x3E, 0x7E, 0x5E, 0x8E, 0xF1, 0xA0, 0xCC, 0xA3, + 0x2A, 0x1D, 0xFB, 0xB6, 0xD6, 0x20, 0xC4, 0x8D, 0x81, 0x65, 0xF5, 0x89, + 0xCB, 0x9D, 0x77, 0xC6, 0x57, 0x43, 0x56, 0x17, 0xD4, 0x40, 0x1A, 0x4D, + 0xC0, 0x63, 0x6C, 0xE3, 0xB7, 0xC8, 0x64, 0x6A, 0x53, 0xAA, 0x38, 0x98, + 0x0C, 0xF4, 0x9B, 0xED, 0x7F, 0x22, 0x76, 0xAF, 0xDD, 0x3A, 0x0B, 0x58, + 0x67, 0x88, 0x06, 0xC3, 0x35, 0x0D, 0x01, 0x8B, 0x8C, 0xC2, 0xE6, 0x5F, + 0x02, 0x24, 0x75, 0x93, 0x66, 0x1E, 0xE5, 0xE2, 0x54, 0xD8, 0x10, 0xCE, + 0x7A, 0xE8, 0x08, 0x2C, 0x12, 0x97, 0x32, 0xAB, 0xB4, 0x27, 0x0A, 0x23, + 0xDF, 0xEF, 0xCA, 0xD9, 0xB8, 0xFA, 0xDC, 0x31, 0x6B, 0xD1, 0xAD, 0x19, + 0x49, 0xBD, 0x51, 0x96, 0xEE, 0xE4, 0xA8, 0x41, 0xDA, 0xFF, 0xCD, 0x55, + 0x86, 0x36, 0xBE, 0x61, 0x52, 0xF8, 0xBB, 0x0E, 0x82, 0x48, 0x69, 0x9A, + 0xE0, 0x47, 0x9E, 0x5C, 0x04, 0x4B, 0x34, 0x15, 0x79, 0x26, 0xA7, 0xDE, + 0x29, 0xAE, 0x92, 0xD7, 0x84, 0xE9, 0xD2, 0xBA, 0x5D, 0xF3, 0xC5, 0xB0, + 0xBF, 0xA4, 0x3B, 0x71, 0x44, 0x46, 0x2B, 0xFC, 0xEB, 0x6F, 0xD5, 0xF6, + 0x14, 0xFE, 0x7C, 0x70, 0x5A, 0x7D, 0xFD, 0x2F, 0x18, 0x83, 0x16, 0xA5, + 0x91, 0x1F, 0x05, 0x95, 0x74, 0xA9, 0xC1, 0x5B, 0x4A, 0x85, 0x6D, 0x13, + 0x07, 0x4F, 0x4E, 0x45, 0xB2, 0x0F, 0xC9, 0x1C, 0xA6, 0xBC, 0xEC, 0x73, + 0x90, 0x7B, 0xCF, 0x59, 0x8F, 0xA1, 0xF9, 0x2D, 0xF2, 0xB1, 0x00, 0x94, + 0x37, 0x9F, 0xD0, 0x2E, 0x9C, 0x6E, 0x28, 0x3F, 0x80, 0xF0, 0x3D, 0xD3, + 0x25, 0x8A, 0xB5, 0xE7, 0x42, 0xB3, 0xC7, 0xEA, 0xF7, 0x4C, 0x11, 0x33, + 0x03, 0xA2, 0xAC, 0x60 +}; + +/* + * Helper for key schedule: r = FO( p, k ) ^ x + */ +static void aria_fo_xor(uint32_t r[4], const uint32_t p[4], + const uint32_t k[4], const uint32_t x[4]) +{ + uint32_t a, b, c, d; + + a = p[0] ^ k[0]; + b = p[1] ^ k[1]; + c = p[2] ^ k[2]; + d = p[3] ^ k[3]; + + aria_sl(&a, &b, &c, &d, aria_sb1, aria_sb2, aria_is1, aria_is2); + aria_a(&a, &b, &c, &d); + + r[0] = a ^ x[0]; + r[1] = b ^ x[1]; + r[2] = c ^ x[2]; + r[3] = d ^ x[3]; +} + +/* + * Helper for key schedule: r = FE( p, k ) ^ x + */ +static void aria_fe_xor(uint32_t r[4], const uint32_t p[4], + const uint32_t k[4], const uint32_t x[4]) +{ + uint32_t a, b, c, d; + + a = p[0] ^ k[0]; + b = p[1] ^ k[1]; + c = p[2] ^ k[2]; + d = p[3] ^ k[3]; + + aria_sl(&a, &b, &c, &d, aria_is1, aria_is2, aria_sb1, aria_sb2); + aria_a(&a, &b, &c, &d); + + r[0] = a ^ x[0]; + r[1] = b ^ x[1]; + r[2] = c ^ x[2]; + r[3] = d ^ x[3]; +} + +/* + * Big endian 128-bit rotation: r = a ^ (b <<< n), used only in key setup. + * + * We chose to store bytes into 32-bit words in little-endian format (see + * MBEDTLS_GET_UINT32_LE / MBEDTLS_PUT_UINT32_LE ) so we need to reverse + * bytes here. + */ +static void aria_rot128(uint32_t r[4], const uint32_t a[4], + const uint32_t b[4], uint8_t n) +{ + uint8_t i, j; + uint32_t t, u; + + const uint8_t n1 = n % 32; // bit offset + const uint8_t n2 = n1 ? 32 - n1 : 0; // reverse bit offset + + j = (n / 32) % 4; // initial word offset + t = ARIA_P3(b[j]); // big endian + for (i = 0; i < 4; i++) { + j = (j + 1) % 4; // get next word, big endian + u = ARIA_P3(b[j]); + t <<= n1; // rotate + t |= u >> n2; + t = ARIA_P3(t); // back to little endian + r[i] = a[i] ^ t; // store + t = u; // move to next word + } +} + +/* + * Set encryption key + */ +int mbedtls_aria_setkey_enc(mbedtls_aria_context *ctx, + const unsigned char *key, unsigned int keybits) +{ + /* round constant masks */ + const uint32_t rc[3][4] = + { + { 0xB7C17C51, 0x940A2227, 0xE8AB13FE, 0xE06E9AFA }, + { 0xCC4AB16D, 0x20C8219E, 0xD5B128FF, 0xB0E25DEF }, + { 0x1D3792DB, 0x70E92621, 0x75972403, 0x0EC9E804 } + }; + + int i; + uint32_t w[4][4], *w2; + ARIA_VALIDATE_RET(ctx != NULL); + ARIA_VALIDATE_RET(key != NULL); + + if (keybits != 128 && keybits != 192 && keybits != 256) { + return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA; + } + + /* Copy key to W0 (and potential remainder to W1) */ + w[0][0] = MBEDTLS_GET_UINT32_LE(key, 0); + w[0][1] = MBEDTLS_GET_UINT32_LE(key, 4); + w[0][2] = MBEDTLS_GET_UINT32_LE(key, 8); + w[0][3] = MBEDTLS_GET_UINT32_LE(key, 12); + + memset(w[1], 0, 16); + if (keybits >= 192) { + w[1][0] = MBEDTLS_GET_UINT32_LE(key, 16); // 192 bit key + w[1][1] = MBEDTLS_GET_UINT32_LE(key, 20); + } + if (keybits == 256) { + w[1][2] = MBEDTLS_GET_UINT32_LE(key, 24); // 256 bit key + w[1][3] = MBEDTLS_GET_UINT32_LE(key, 28); + } + + i = (keybits - 128) >> 6; // index: 0, 1, 2 + ctx->nr = 12 + 2 * i; // no. rounds: 12, 14, 16 + + aria_fo_xor(w[1], w[0], rc[i], w[1]); // W1 = FO(W0, CK1) ^ KR + i = i < 2 ? i + 1 : 0; + aria_fe_xor(w[2], w[1], rc[i], w[0]); // W2 = FE(W1, CK2) ^ W0 + i = i < 2 ? i + 1 : 0; + aria_fo_xor(w[3], w[2], rc[i], w[1]); // W3 = FO(W2, CK3) ^ W1 + + for (i = 0; i < 4; i++) { // create round keys + w2 = w[(i + 1) & 3]; + aria_rot128(ctx->rk[i], w[i], w2, 128 - 19); + aria_rot128(ctx->rk[i + 4], w[i], w2, 128 - 31); + aria_rot128(ctx->rk[i + 8], w[i], w2, 61); + aria_rot128(ctx->rk[i + 12], w[i], w2, 31); + } + aria_rot128(ctx->rk[16], w[0], w[1], 19); + + /* w holds enough info to reconstruct the round keys */ + mbedtls_platform_zeroize(w, sizeof(w)); + + return 0; +} + +/* + * Set decryption key + */ +int mbedtls_aria_setkey_dec(mbedtls_aria_context *ctx, + const unsigned char *key, unsigned int keybits) +{ + int i, j, k, ret; + ARIA_VALIDATE_RET(ctx != NULL); + ARIA_VALIDATE_RET(key != NULL); + + ret = mbedtls_aria_setkey_enc(ctx, key, keybits); + if (ret != 0) { + return ret; + } + + /* flip the order of round keys */ + for (i = 0, j = ctx->nr; i < j; i++, j--) { + for (k = 0; k < 4; k++) { + uint32_t t = ctx->rk[i][k]; + ctx->rk[i][k] = ctx->rk[j][k]; + ctx->rk[j][k] = t; + } + } + + /* apply affine transform to middle keys */ + for (i = 1; i < ctx->nr; i++) { + aria_a(&ctx->rk[i][0], &ctx->rk[i][1], + &ctx->rk[i][2], &ctx->rk[i][3]); + } + + return 0; +} + +/* + * Encrypt a block + */ +int mbedtls_aria_crypt_ecb(mbedtls_aria_context *ctx, + const unsigned char input[MBEDTLS_ARIA_BLOCKSIZE], + unsigned char output[MBEDTLS_ARIA_BLOCKSIZE]) +{ + int i; + + uint32_t a, b, c, d; + ARIA_VALIDATE_RET(ctx != NULL); + ARIA_VALIDATE_RET(input != NULL); + ARIA_VALIDATE_RET(output != NULL); + + a = MBEDTLS_GET_UINT32_LE(input, 0); + b = MBEDTLS_GET_UINT32_LE(input, 4); + c = MBEDTLS_GET_UINT32_LE(input, 8); + d = MBEDTLS_GET_UINT32_LE(input, 12); + + i = 0; + while (1) { + a ^= ctx->rk[i][0]; + b ^= ctx->rk[i][1]; + c ^= ctx->rk[i][2]; + d ^= ctx->rk[i][3]; + i++; + + aria_sl(&a, &b, &c, &d, aria_sb1, aria_sb2, aria_is1, aria_is2); + aria_a(&a, &b, &c, &d); + + a ^= ctx->rk[i][0]; + b ^= ctx->rk[i][1]; + c ^= ctx->rk[i][2]; + d ^= ctx->rk[i][3]; + i++; + + aria_sl(&a, &b, &c, &d, aria_is1, aria_is2, aria_sb1, aria_sb2); + if (i >= ctx->nr) { + break; + } + aria_a(&a, &b, &c, &d); + } + + /* final key mixing */ + a ^= ctx->rk[i][0]; + b ^= ctx->rk[i][1]; + c ^= ctx->rk[i][2]; + d ^= ctx->rk[i][3]; + + MBEDTLS_PUT_UINT32_LE(a, output, 0); + MBEDTLS_PUT_UINT32_LE(b, output, 4); + MBEDTLS_PUT_UINT32_LE(c, output, 8); + MBEDTLS_PUT_UINT32_LE(d, output, 12); + + return 0; +} + +/* Initialize context */ +void mbedtls_aria_init(mbedtls_aria_context *ctx) +{ + ARIA_VALIDATE(ctx != NULL); + memset(ctx, 0, sizeof(mbedtls_aria_context)); +} + +/* Clear context */ +void mbedtls_aria_free(mbedtls_aria_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_aria_context)); +} + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/* + * ARIA-CBC buffer encryption/decryption + */ +int mbedtls_aria_crypt_cbc(mbedtls_aria_context *ctx, + int mode, + size_t length, + unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE], + const unsigned char *input, + unsigned char *output) +{ + unsigned char temp[MBEDTLS_ARIA_BLOCKSIZE]; + + ARIA_VALIDATE_RET(ctx != NULL); + ARIA_VALIDATE_RET(mode == MBEDTLS_ARIA_ENCRYPT || + mode == MBEDTLS_ARIA_DECRYPT); + ARIA_VALIDATE_RET(length == 0 || input != NULL); + ARIA_VALIDATE_RET(length == 0 || output != NULL); + ARIA_VALIDATE_RET(iv != NULL); + + if (length % MBEDTLS_ARIA_BLOCKSIZE) { + return MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_ARIA_DECRYPT) { + while (length > 0) { + memcpy(temp, input, MBEDTLS_ARIA_BLOCKSIZE); + mbedtls_aria_crypt_ecb(ctx, input, output); + + mbedtls_xor(output, output, iv, MBEDTLS_ARIA_BLOCKSIZE); + + memcpy(iv, temp, MBEDTLS_ARIA_BLOCKSIZE); + + input += MBEDTLS_ARIA_BLOCKSIZE; + output += MBEDTLS_ARIA_BLOCKSIZE; + length -= MBEDTLS_ARIA_BLOCKSIZE; + } + } else { + while (length > 0) { + mbedtls_xor(output, input, iv, MBEDTLS_ARIA_BLOCKSIZE); + + mbedtls_aria_crypt_ecb(ctx, output, output); + memcpy(iv, output, MBEDTLS_ARIA_BLOCKSIZE); + + input += MBEDTLS_ARIA_BLOCKSIZE; + output += MBEDTLS_ARIA_BLOCKSIZE; + length -= MBEDTLS_ARIA_BLOCKSIZE; + } + } + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +/* + * ARIA-CFB128 buffer encryption/decryption + */ +int mbedtls_aria_crypt_cfb128(mbedtls_aria_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE], + const unsigned char *input, + unsigned char *output) +{ + unsigned char c; + size_t n; + + ARIA_VALIDATE_RET(ctx != NULL); + ARIA_VALIDATE_RET(mode == MBEDTLS_ARIA_ENCRYPT || + mode == MBEDTLS_ARIA_DECRYPT); + ARIA_VALIDATE_RET(length == 0 || input != NULL); + ARIA_VALIDATE_RET(length == 0 || output != NULL); + ARIA_VALIDATE_RET(iv != NULL); + ARIA_VALIDATE_RET(iv_off != NULL); + + n = *iv_off; + + /* An overly large value of n can lead to an unlimited + * buffer overflow. Therefore, guard against this + * outside of parameter validation. */ + if (n >= MBEDTLS_ARIA_BLOCKSIZE) { + return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA; + } + + if (mode == MBEDTLS_ARIA_DECRYPT) { + while (length--) { + if (n == 0) { + mbedtls_aria_crypt_ecb(ctx, iv, iv); + } + + c = *input++; + *output++ = c ^ iv[n]; + iv[n] = c; + + n = (n + 1) & 0x0F; + } + } else { + while (length--) { + if (n == 0) { + mbedtls_aria_crypt_ecb(ctx, iv, iv); + } + + iv[n] = *output++ = (unsigned char) (iv[n] ^ *input++); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/* + * ARIA-CTR buffer encryption/decryption + */ +int mbedtls_aria_crypt_ctr(mbedtls_aria_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[MBEDTLS_ARIA_BLOCKSIZE], + unsigned char stream_block[MBEDTLS_ARIA_BLOCKSIZE], + const unsigned char *input, + unsigned char *output) +{ + int c, i; + size_t n; + + ARIA_VALIDATE_RET(ctx != NULL); + ARIA_VALIDATE_RET(length == 0 || input != NULL); + ARIA_VALIDATE_RET(length == 0 || output != NULL); + ARIA_VALIDATE_RET(nonce_counter != NULL); + ARIA_VALIDATE_RET(stream_block != NULL); + ARIA_VALIDATE_RET(nc_off != NULL); + + n = *nc_off; + /* An overly large value of n can lead to an unlimited + * buffer overflow. Therefore, guard against this + * outside of parameter validation. */ + if (n >= MBEDTLS_ARIA_BLOCKSIZE) { + return MBEDTLS_ERR_ARIA_BAD_INPUT_DATA; + } + + while (length--) { + if (n == 0) { + mbedtls_aria_crypt_ecb(ctx, nonce_counter, + stream_block); + + for (i = MBEDTLS_ARIA_BLOCKSIZE; i > 0; i--) { + if (++nonce_counter[i - 1] != 0) { + break; + } + } + } + c = *input++; + *output++ = (unsigned char) (c ^ stream_block[n]); + + n = (n + 1) & 0x0F; + } + + *nc_off = n; + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_CTR */ +#endif /* !MBEDTLS_ARIA_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +/* + * Basic ARIA ECB test vectors from RFC 5794 + */ +static const uint8_t aria_test1_ecb_key[32] = // test key +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 128 bit + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // 192 bit + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F // 256 bit +}; + +static const uint8_t aria_test1_ecb_pt[MBEDTLS_ARIA_BLOCKSIZE] = // plaintext +{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // same for all + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF // key sizes +}; + +static const uint8_t aria_test1_ecb_ct[3][MBEDTLS_ARIA_BLOCKSIZE] = // ciphertext +{ + { 0xD7, 0x18, 0xFB, 0xD6, 0xAB, 0x64, 0x4C, 0x73, // 128 bit + 0x9D, 0xA9, 0x5F, 0x3B, 0xE6, 0x45, 0x17, 0x78 }, + { 0x26, 0x44, 0x9C, 0x18, 0x05, 0xDB, 0xE7, 0xAA, // 192 bit + 0x25, 0xA4, 0x68, 0xCE, 0x26, 0x3A, 0x9E, 0x79 }, + { 0xF9, 0x2B, 0xD7, 0xC7, 0x9F, 0xB7, 0x2E, 0x2F, // 256 bit + 0x2B, 0x8F, 0x80, 0xC1, 0x97, 0x2D, 0x24, 0xFC } +}; + +/* + * Mode tests from "Test Vectors for ARIA" Version 1.0 + * http://210.104.33.10/ARIA/doc/ARIA-testvector-e.pdf + */ +#if (defined(MBEDTLS_CIPHER_MODE_CBC) || defined(MBEDTLS_CIPHER_MODE_CFB) || \ + defined(MBEDTLS_CIPHER_MODE_CTR)) +static const uint8_t aria_test2_key[32] = +{ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 128 bit + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 192 bit + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff // 256 bit +}; + +static const uint8_t aria_test2_pt[48] = +{ + 0x11, 0x11, 0x11, 0x11, 0xaa, 0xaa, 0xaa, 0xaa, // same for all + 0x11, 0x11, 0x11, 0x11, 0xbb, 0xbb, 0xbb, 0xbb, + 0x11, 0x11, 0x11, 0x11, 0xcc, 0xcc, 0xcc, 0xcc, + 0x11, 0x11, 0x11, 0x11, 0xdd, 0xdd, 0xdd, 0xdd, + 0x22, 0x22, 0x22, 0x22, 0xaa, 0xaa, 0xaa, 0xaa, + 0x22, 0x22, 0x22, 0x22, 0xbb, 0xbb, 0xbb, 0xbb, +}; +#endif + +#if (defined(MBEDTLS_CIPHER_MODE_CBC) || defined(MBEDTLS_CIPHER_MODE_CFB)) +static const uint8_t aria_test2_iv[MBEDTLS_ARIA_BLOCKSIZE] = +{ + 0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78, // same for CBC, CFB + 0x87, 0x96, 0xa5, 0xb4, 0xc3, 0xd2, 0xe1, 0xf0 // CTR has zero IV +}; +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const uint8_t aria_test2_cbc_ct[3][48] = // CBC ciphertext +{ + { 0x49, 0xd6, 0x18, 0x60, 0xb1, 0x49, 0x09, 0x10, // 128-bit key + 0x9c, 0xef, 0x0d, 0x22, 0xa9, 0x26, 0x81, 0x34, + 0xfa, 0xdf, 0x9f, 0xb2, 0x31, 0x51, 0xe9, 0x64, + 0x5f, 0xba, 0x75, 0x01, 0x8b, 0xdb, 0x15, 0x38, + 0xb5, 0x33, 0x34, 0x63, 0x4b, 0xbf, 0x7d, 0x4c, + 0xd4, 0xb5, 0x37, 0x70, 0x33, 0x06, 0x0c, 0x15 }, + { 0xaf, 0xe6, 0xcf, 0x23, 0x97, 0x4b, 0x53, 0x3c, // 192-bit key + 0x67, 0x2a, 0x82, 0x62, 0x64, 0xea, 0x78, 0x5f, + 0x4e, 0x4f, 0x7f, 0x78, 0x0d, 0xc7, 0xf3, 0xf1, + 0xe0, 0x96, 0x2b, 0x80, 0x90, 0x23, 0x86, 0xd5, + 0x14, 0xe9, 0xc3, 0xe7, 0x72, 0x59, 0xde, 0x92, + 0xdd, 0x11, 0x02, 0xff, 0xab, 0x08, 0x6c, 0x1e }, + { 0x52, 0x3a, 0x8a, 0x80, 0x6a, 0xe6, 0x21, 0xf1, // 256-bit key + 0x55, 0xfd, 0xd2, 0x8d, 0xbc, 0x34, 0xe1, 0xab, + 0x7b, 0x9b, 0x42, 0x43, 0x2a, 0xd8, 0xb2, 0xef, + 0xb9, 0x6e, 0x23, 0xb1, 0x3f, 0x0a, 0x6e, 0x52, + 0xf3, 0x61, 0x85, 0xd5, 0x0a, 0xd0, 0x02, 0xc5, + 0xf6, 0x01, 0xbe, 0xe5, 0x49, 0x3f, 0x11, 0x8b } +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +static const uint8_t aria_test2_cfb_ct[3][48] = // CFB ciphertext +{ + { 0x37, 0x20, 0xe5, 0x3b, 0xa7, 0xd6, 0x15, 0x38, // 128-bit key + 0x34, 0x06, 0xb0, 0x9f, 0x0a, 0x05, 0xa2, 0x00, + 0xc0, 0x7c, 0x21, 0xe6, 0x37, 0x0f, 0x41, 0x3a, + 0x5d, 0x13, 0x25, 0x00, 0xa6, 0x82, 0x85, 0x01, + 0x7c, 0x61, 0xb4, 0x34, 0xc7, 0xb7, 0xca, 0x96, + 0x85, 0xa5, 0x10, 0x71, 0x86, 0x1e, 0x4d, 0x4b }, + { 0x41, 0x71, 0xf7, 0x19, 0x2b, 0xf4, 0x49, 0x54, // 192-bit key + 0x94, 0xd2, 0x73, 0x61, 0x29, 0x64, 0x0f, 0x5c, + 0x4d, 0x87, 0xa9, 0xa2, 0x13, 0x66, 0x4c, 0x94, + 0x48, 0x47, 0x7c, 0x6e, 0xcc, 0x20, 0x13, 0x59, + 0x8d, 0x97, 0x66, 0x95, 0x2d, 0xd8, 0xc3, 0x86, + 0x8f, 0x17, 0xe3, 0x6e, 0xf6, 0x6f, 0xd8, 0x4b }, + { 0x26, 0x83, 0x47, 0x05, 0xb0, 0xf2, 0xc0, 0xe2, // 256-bit key + 0x58, 0x8d, 0x4a, 0x7f, 0x09, 0x00, 0x96, 0x35, + 0xf2, 0x8b, 0xb9, 0x3d, 0x8c, 0x31, 0xf8, 0x70, + 0xec, 0x1e, 0x0b, 0xdb, 0x08, 0x2b, 0x66, 0xfa, + 0x40, 0x2d, 0xd9, 0xc2, 0x02, 0xbe, 0x30, 0x0c, + 0x45, 0x17, 0xd1, 0x96, 0xb1, 0x4d, 0x4c, 0xe1 } +}; +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +static const uint8_t aria_test2_ctr_ct[3][48] = // CTR ciphertext +{ + { 0xac, 0x5d, 0x7d, 0xe8, 0x05, 0xa0, 0xbf, 0x1c, // 128-bit key + 0x57, 0xc8, 0x54, 0x50, 0x1a, 0xf6, 0x0f, 0xa1, + 0x14, 0x97, 0xe2, 0xa3, 0x45, 0x19, 0xde, 0xa1, + 0x56, 0x9e, 0x91, 0xe5, 0xb5, 0xcc, 0xae, 0x2f, + 0xf3, 0xbf, 0xa1, 0xbf, 0x97, 0x5f, 0x45, 0x71, + 0xf4, 0x8b, 0xe1, 0x91, 0x61, 0x35, 0x46, 0xc3 }, + { 0x08, 0x62, 0x5c, 0xa8, 0xfe, 0x56, 0x9c, 0x19, // 192-bit key + 0xba, 0x7a, 0xf3, 0x76, 0x0a, 0x6e, 0xd1, 0xce, + 0xf4, 0xd1, 0x99, 0x26, 0x3e, 0x99, 0x9d, 0xde, + 0x14, 0x08, 0x2d, 0xbb, 0xa7, 0x56, 0x0b, 0x79, + 0xa4, 0xc6, 0xb4, 0x56, 0xb8, 0x70, 0x7d, 0xce, + 0x75, 0x1f, 0x98, 0x54, 0xf1, 0x88, 0x93, 0xdf }, + { 0x30, 0x02, 0x6c, 0x32, 0x96, 0x66, 0x14, 0x17, // 256-bit key + 0x21, 0x17, 0x8b, 0x99, 0xc0, 0xa1, 0xf1, 0xb2, + 0xf0, 0x69, 0x40, 0x25, 0x3f, 0x7b, 0x30, 0x89, + 0xe2, 0xa3, 0x0e, 0xa8, 0x6a, 0xa3, 0xc8, 0x8f, + 0x59, 0x40, 0xf0, 0x5a, 0xd7, 0xee, 0x41, 0xd7, + 0x13, 0x47, 0xbb, 0x72, 0x61, 0xe3, 0x48, 0xf1 } +}; +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#define ARIA_SELF_TEST_ASSERT(cond) \ + do { \ + if (cond) { \ + if (verbose) \ + mbedtls_printf("failed\n"); \ + goto exit; \ + } else { \ + if (verbose) \ + mbedtls_printf("passed\n"); \ + } \ + } while (0) + +/* + * Checkup routine + */ +int mbedtls_aria_self_test(int verbose) +{ + int i; + uint8_t blk[MBEDTLS_ARIA_BLOCKSIZE]; + mbedtls_aria_context ctx; + int ret = 1; + +#if (defined(MBEDTLS_CIPHER_MODE_CFB) || defined(MBEDTLS_CIPHER_MODE_CTR)) + size_t j; +#endif + +#if (defined(MBEDTLS_CIPHER_MODE_CBC) || \ + defined(MBEDTLS_CIPHER_MODE_CFB) || \ + defined(MBEDTLS_CIPHER_MODE_CTR)) + uint8_t buf[48], iv[MBEDTLS_ARIA_BLOCKSIZE]; +#endif + + mbedtls_aria_init(&ctx); + + /* + * Test set 1 + */ + for (i = 0; i < 3; i++) { + /* test ECB encryption */ + if (verbose) { + mbedtls_printf(" ARIA-ECB-%d (enc): ", 128 + 64 * i); + } + mbedtls_aria_setkey_enc(&ctx, aria_test1_ecb_key, 128 + 64 * i); + mbedtls_aria_crypt_ecb(&ctx, aria_test1_ecb_pt, blk); + ARIA_SELF_TEST_ASSERT( + memcmp(blk, aria_test1_ecb_ct[i], MBEDTLS_ARIA_BLOCKSIZE) + != 0); + + /* test ECB decryption */ + if (verbose) { + mbedtls_printf(" ARIA-ECB-%d (dec): ", 128 + 64 * i); + } + mbedtls_aria_setkey_dec(&ctx, aria_test1_ecb_key, 128 + 64 * i); + mbedtls_aria_crypt_ecb(&ctx, aria_test1_ecb_ct[i], blk); + ARIA_SELF_TEST_ASSERT( + memcmp(blk, aria_test1_ecb_pt, MBEDTLS_ARIA_BLOCKSIZE) + != 0); + } + if (verbose) { + mbedtls_printf("\n"); + } + + /* + * Test set 2 + */ +#if defined(MBEDTLS_CIPHER_MODE_CBC) + for (i = 0; i < 3; i++) { + /* Test CBC encryption */ + if (verbose) { + mbedtls_printf(" ARIA-CBC-%d (enc): ", 128 + 64 * i); + } + mbedtls_aria_setkey_enc(&ctx, aria_test2_key, 128 + 64 * i); + memcpy(iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE); + memset(buf, 0x55, sizeof(buf)); + mbedtls_aria_crypt_cbc(&ctx, MBEDTLS_ARIA_ENCRYPT, 48, iv, + aria_test2_pt, buf); + ARIA_SELF_TEST_ASSERT(memcmp(buf, aria_test2_cbc_ct[i], 48) + != 0); + + /* Test CBC decryption */ + if (verbose) { + mbedtls_printf(" ARIA-CBC-%d (dec): ", 128 + 64 * i); + } + mbedtls_aria_setkey_dec(&ctx, aria_test2_key, 128 + 64 * i); + memcpy(iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE); + memset(buf, 0xAA, sizeof(buf)); + mbedtls_aria_crypt_cbc(&ctx, MBEDTLS_ARIA_DECRYPT, 48, iv, + aria_test2_cbc_ct[i], buf); + ARIA_SELF_TEST_ASSERT(memcmp(buf, aria_test2_pt, 48) != 0); + } + if (verbose) { + mbedtls_printf("\n"); + } + +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) + for (i = 0; i < 3; i++) { + /* Test CFB encryption */ + if (verbose) { + mbedtls_printf(" ARIA-CFB-%d (enc): ", 128 + 64 * i); + } + mbedtls_aria_setkey_enc(&ctx, aria_test2_key, 128 + 64 * i); + memcpy(iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE); + memset(buf, 0x55, sizeof(buf)); + j = 0; + mbedtls_aria_crypt_cfb128(&ctx, MBEDTLS_ARIA_ENCRYPT, 48, &j, iv, + aria_test2_pt, buf); + ARIA_SELF_TEST_ASSERT(memcmp(buf, aria_test2_cfb_ct[i], 48) != 0); + + /* Test CFB decryption */ + if (verbose) { + mbedtls_printf(" ARIA-CFB-%d (dec): ", 128 + 64 * i); + } + mbedtls_aria_setkey_enc(&ctx, aria_test2_key, 128 + 64 * i); + memcpy(iv, aria_test2_iv, MBEDTLS_ARIA_BLOCKSIZE); + memset(buf, 0xAA, sizeof(buf)); + j = 0; + mbedtls_aria_crypt_cfb128(&ctx, MBEDTLS_ARIA_DECRYPT, 48, &j, + iv, aria_test2_cfb_ct[i], buf); + ARIA_SELF_TEST_ASSERT(memcmp(buf, aria_test2_pt, 48) != 0); + } + if (verbose) { + mbedtls_printf("\n"); + } +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) + for (i = 0; i < 3; i++) { + /* Test CTR encryption */ + if (verbose) { + mbedtls_printf(" ARIA-CTR-%d (enc): ", 128 + 64 * i); + } + mbedtls_aria_setkey_enc(&ctx, aria_test2_key, 128 + 64 * i); + memset(iv, 0, MBEDTLS_ARIA_BLOCKSIZE); // IV = 0 + memset(buf, 0x55, sizeof(buf)); + j = 0; + mbedtls_aria_crypt_ctr(&ctx, 48, &j, iv, blk, + aria_test2_pt, buf); + ARIA_SELF_TEST_ASSERT(memcmp(buf, aria_test2_ctr_ct[i], 48) != 0); + + /* Test CTR decryption */ + if (verbose) { + mbedtls_printf(" ARIA-CTR-%d (dec): ", 128 + 64 * i); + } + mbedtls_aria_setkey_enc(&ctx, aria_test2_key, 128 + 64 * i); + memset(iv, 0, MBEDTLS_ARIA_BLOCKSIZE); // IV = 0 + memset(buf, 0xAA, sizeof(buf)); + j = 0; + mbedtls_aria_crypt_ctr(&ctx, 48, &j, iv, blk, + aria_test2_ctr_ct[i], buf); + ARIA_SELF_TEST_ASSERT(memcmp(buf, aria_test2_pt, 48) != 0); + } + if (verbose) { + mbedtls_printf("\n"); + } +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + + ret = 0; + +exit: + mbedtls_aria_free(&ctx); + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_ARIA_C */ diff --git a/r5dev/thirdparty/mbedtls/asn1parse.c b/r5dev/thirdparty/mbedtls/asn1parse.c new file mode 100644 index 00000000..3cd34605 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/asn1parse.c @@ -0,0 +1,507 @@ +/* + * Generic ASN.1 parsing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_ASN1_PARSE_C) + +#include "mbedtls/asn1.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#include "mbedtls/platform.h" + +/* + * ASN.1 DER decoding routines + */ +int mbedtls_asn1_get_len(unsigned char **p, + const unsigned char *end, + size_t *len) +{ + if ((end - *p) < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + if ((**p & 0x80) == 0) { + *len = *(*p)++; + } else { + switch (**p & 0x7F) { + case 1: + if ((end - *p) < 2) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + *len = (*p)[1]; + (*p) += 2; + break; + + case 2: + if ((end - *p) < 3) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + *len = ((size_t) (*p)[1] << 8) | (*p)[2]; + (*p) += 3; + break; + + case 3: + if ((end - *p) < 4) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + *len = ((size_t) (*p)[1] << 16) | + ((size_t) (*p)[2] << 8) | (*p)[3]; + (*p) += 4; + break; + + case 4: + if ((end - *p) < 5) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + *len = ((size_t) (*p)[1] << 24) | ((size_t) (*p)[2] << 16) | + ((size_t) (*p)[3] << 8) | (*p)[4]; + (*p) += 5; + break; + + default: + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + } + + if (*len > (size_t) (end - *p)) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + return 0; +} + +int mbedtls_asn1_get_tag(unsigned char **p, + const unsigned char *end, + size_t *len, int tag) +{ + if ((end - *p) < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + if (**p != tag) { + return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG; + } + + (*p)++; + + return mbedtls_asn1_get_len(p, end, len); +} + +int mbedtls_asn1_get_bool(unsigned char **p, + const unsigned char *end, + int *val) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_BOOLEAN)) != 0) { + return ret; + } + + if (len != 1) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + *val = (**p != 0) ? 1 : 0; + (*p)++; + + return 0; +} + +static int asn1_get_tagged_int(unsigned char **p, + const unsigned char *end, + int tag, int *val) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, tag)) != 0) { + return ret; + } + + /* + * len==0 is malformed (0 must be represented as 020100 for INTEGER, + * or 0A0100 for ENUMERATED tags + */ + if (len == 0) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + /* This is a cryptography library. Reject negative integers. */ + if ((**p & 0x80) != 0) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + /* Skip leading zeros. */ + while (len > 0 && **p == 0) { + ++(*p); + --len; + } + + /* Reject integers that don't fit in an int. This code assumes that + * the int type has no padding bit. */ + if (len > sizeof(int)) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + if (len == sizeof(int) && (**p & 0x80) != 0) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + *val = 0; + while (len-- > 0) { + *val = (*val << 8) | **p; + (*p)++; + } + + return 0; +} + +int mbedtls_asn1_get_int(unsigned char **p, + const unsigned char *end, + int *val) +{ + return asn1_get_tagged_int(p, end, MBEDTLS_ASN1_INTEGER, val); +} + +int mbedtls_asn1_get_enum(unsigned char **p, + const unsigned char *end, + int *val) +{ + return asn1_get_tagged_int(p, end, MBEDTLS_ASN1_ENUMERATED, val); +} + +#if defined(MBEDTLS_BIGNUM_C) +int mbedtls_asn1_get_mpi(unsigned char **p, + const unsigned char *end, + mbedtls_mpi *X) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) { + return ret; + } + + ret = mbedtls_mpi_read_binary(X, *p, len); + + *p += len; + + return ret; +} +#endif /* MBEDTLS_BIGNUM_C */ + +int mbedtls_asn1_get_bitstring(unsigned char **p, const unsigned char *end, + mbedtls_asn1_bitstring *bs) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Certificate type is a single byte bitstring */ + if ((ret = mbedtls_asn1_get_tag(p, end, &bs->len, MBEDTLS_ASN1_BIT_STRING)) != 0) { + return ret; + } + + /* Check length, subtract one for actual bit string length */ + if (bs->len < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + bs->len -= 1; + + /* Get number of unused bits, ensure unused bits <= 7 */ + bs->unused_bits = **p; + if (bs->unused_bits > 7) { + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + (*p)++; + + /* Get actual bitstring */ + bs->p = *p; + *p += bs->len; + + if (*p != end) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + return 0; +} + +/* + * Traverse an ASN.1 "SEQUENCE OF " + * and call a callback for each entry found. + */ +int mbedtls_asn1_traverse_sequence_of( + unsigned char **p, + const unsigned char *end, + unsigned char tag_must_mask, unsigned char tag_must_val, + unsigned char tag_may_mask, unsigned char tag_may_val, + int (*cb)(void *ctx, int tag, + unsigned char *start, size_t len), + void *ctx) +{ + int ret; + size_t len; + + /* Get main sequence tag */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + if (*p + len != end) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + while (*p < end) { + unsigned char const tag = *(*p)++; + + if ((tag & tag_must_mask) != tag_must_val) { + return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG; + } + + if ((ret = mbedtls_asn1_get_len(p, end, &len)) != 0) { + return ret; + } + + if ((tag & tag_may_mask) == tag_may_val) { + if (cb != NULL) { + ret = cb(ctx, tag, *p, len); + if (ret != 0) { + return ret; + } + } + } + + *p += len; + } + + return 0; +} + +/* + * Get a bit string without unused bits + */ +int mbedtls_asn1_get_bitstring_null(unsigned char **p, const unsigned char *end, + size_t *len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_tag(p, end, len, MBEDTLS_ASN1_BIT_STRING)) != 0) { + return ret; + } + + if (*len == 0) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + --(*len); + + if (**p != 0) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + ++(*p); + + return 0; +} + +void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq) +{ + while (seq != NULL) { + mbedtls_asn1_sequence *next = seq->next; + mbedtls_free(seq); + seq = next; + } +} + +typedef struct { + int tag; + mbedtls_asn1_sequence *cur; +} asn1_get_sequence_of_cb_ctx_t; + +static int asn1_get_sequence_of_cb(void *ctx, + int tag, + unsigned char *start, + size_t len) +{ + asn1_get_sequence_of_cb_ctx_t *cb_ctx = + (asn1_get_sequence_of_cb_ctx_t *) ctx; + mbedtls_asn1_sequence *cur = + cb_ctx->cur; + + if (cur->buf.p != NULL) { + cur->next = + mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + + if (cur->next == NULL) { + return MBEDTLS_ERR_ASN1_ALLOC_FAILED; + } + + cur = cur->next; + } + + cur->buf.p = start; + cur->buf.len = len; + cur->buf.tag = tag; + + cb_ctx->cur = cur; + return 0; +} + +/* + * Parses and splits an ASN.1 "SEQUENCE OF " + */ +int mbedtls_asn1_get_sequence_of(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_sequence *cur, + const int tag) +{ + asn1_get_sequence_of_cb_ctx_t cb_ctx = { tag, cur }; + + memset(cur, 0, sizeof(mbedtls_asn1_sequence)); + return mbedtls_asn1_traverse_sequence_of( + p, end, 0xFF, tag, 0, 0, + asn1_get_sequence_of_cb, &cb_ctx); +} + +int mbedtls_asn1_get_alg(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + if ((end - *p) < 1) { + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + alg->tag = **p; + end = *p + len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &alg->len, MBEDTLS_ASN1_OID)) != 0) { + return ret; + } + + alg->p = *p; + *p += alg->len; + + if (*p == end) { + mbedtls_platform_zeroize(params, sizeof(mbedtls_asn1_buf)); + return 0; + } + + params->tag = **p; + (*p)++; + + if ((ret = mbedtls_asn1_get_len(p, end, ¶ms->len)) != 0) { + return ret; + } + + params->p = *p; + *p += params->len; + + if (*p != end) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + return 0; +} + +int mbedtls_asn1_get_alg_null(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_asn1_buf params; + + memset(¶ms, 0, sizeof(mbedtls_asn1_buf)); + + if ((ret = mbedtls_asn1_get_alg(p, end, alg, ¶ms)) != 0) { + return ret; + } + + if ((params.tag != MBEDTLS_ASN1_NULL && params.tag != 0) || params.len != 0) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + + return 0; +} + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_asn1_free_named_data(mbedtls_asn1_named_data *cur) +{ + if (cur == NULL) { + return; + } + + mbedtls_free(cur->oid.p); + mbedtls_free(cur->val.p); + + mbedtls_platform_zeroize(cur, sizeof(mbedtls_asn1_named_data)); +} +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +void mbedtls_asn1_free_named_data_list(mbedtls_asn1_named_data **head) +{ + mbedtls_asn1_named_data *cur; + + while ((cur = *head) != NULL) { + *head = cur->next; + mbedtls_free(cur->oid.p); + mbedtls_free(cur->val.p); + mbedtls_free(cur); + } +} + +void mbedtls_asn1_free_named_data_list_shallow(mbedtls_asn1_named_data *name) +{ + for (mbedtls_asn1_named_data *next; name != NULL; name = next) { + next = name->next; + mbedtls_free(name); + } +} + +const mbedtls_asn1_named_data *mbedtls_asn1_find_named_data(const mbedtls_asn1_named_data *list, + const char *oid, size_t len) +{ + while (list != NULL) { + if (list->oid.len == len && + memcmp(list->oid.p, oid, len) == 0) { + break; + } + + list = list->next; + } + + return list; +} + +#endif /* MBEDTLS_ASN1_PARSE_C */ diff --git a/r5dev/thirdparty/mbedtls/asn1write.c b/r5dev/thirdparty/mbedtls/asn1write.c new file mode 100644 index 00000000..b9d586ae --- /dev/null +++ b/r5dev/thirdparty/mbedtls/asn1write.c @@ -0,0 +1,481 @@ +/* + * ASN.1 buffer writing functionality + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_ASN1_WRITE_C) + +#include "mbedtls/asn1write.h" +#include "mbedtls/error.h" + +#include + +#include "mbedtls/platform.h" + +int mbedtls_asn1_write_len(unsigned char **p, const unsigned char *start, size_t len) +{ + if (len < 0x80) { + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = (unsigned char) len; + return 1; + } + + if (len <= 0xFF) { + if (*p - start < 2) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = (unsigned char) len; + *--(*p) = 0x81; + return 2; + } + + if (len <= 0xFFFF) { + if (*p - start < 3) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = MBEDTLS_BYTE_0(len); + *--(*p) = MBEDTLS_BYTE_1(len); + *--(*p) = 0x82; + return 3; + } + + if (len <= 0xFFFFFF) { + if (*p - start < 4) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = MBEDTLS_BYTE_0(len); + *--(*p) = MBEDTLS_BYTE_1(len); + *--(*p) = MBEDTLS_BYTE_2(len); + *--(*p) = 0x83; + return 4; + } + + int len_is_valid = 1; +#if SIZE_MAX > 0xFFFFFFFF + len_is_valid = (len <= 0xFFFFFFFF); +#endif + if (len_is_valid) { + if (*p - start < 5) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = MBEDTLS_BYTE_0(len); + *--(*p) = MBEDTLS_BYTE_1(len); + *--(*p) = MBEDTLS_BYTE_2(len); + *--(*p) = MBEDTLS_BYTE_3(len); + *--(*p) = 0x84; + return 5; + } + + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; +} + +int mbedtls_asn1_write_tag(unsigned char **p, const unsigned char *start, unsigned char tag) +{ + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = tag; + + return 1; +} + +int mbedtls_asn1_write_raw_buffer(unsigned char **p, const unsigned char *start, + const unsigned char *buf, size_t size) +{ + size_t len = 0; + + if (*p < start || (size_t) (*p - start) < size) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + len = size; + (*p) -= len; + memcpy(*p, buf, len); + + return (int) len; +} + +#if defined(MBEDTLS_BIGNUM_C) +int mbedtls_asn1_write_mpi(unsigned char **p, const unsigned char *start, const mbedtls_mpi *X) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + // Write the MPI + // + len = mbedtls_mpi_size(X); + + /* DER represents 0 with a sign bit (0=nonnegative) and 7 value bits, not + * as 0 digits. We need to end up with 020100, not with 0200. */ + if (len == 0) { + len = 1; + } + + if (*p < start || (size_t) (*p - start) < len) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + (*p) -= len; + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(X, *p, len)); + + // DER format assumes 2s complement for numbers, so the leftmost bit + // should be 0 for positive numbers and 1 for negative numbers. + // + if (X->s == 1 && **p & 0x80) { + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = 0x00; + len += 1; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_INTEGER)); + + ret = (int) len; + +cleanup: + return ret; +} +#endif /* MBEDTLS_BIGNUM_C */ + +int mbedtls_asn1_write_null(unsigned char **p, const unsigned char *start) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + // Write NULL + // + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, 0)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_NULL)); + + return (int) len; +} + +int mbedtls_asn1_write_oid(unsigned char **p, const unsigned char *start, + const char *oid, size_t oid_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, + (const unsigned char *) oid, oid_len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OID)); + + return (int) len; +} + +int mbedtls_asn1_write_algorithm_identifier(unsigned char **p, const unsigned char *start, + const char *oid, size_t oid_len, + size_t par_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + if (par_len == 0) { + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_null(p, start)); + } else { + len += par_len; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid, oid_len)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + return (int) len; +} + +int mbedtls_asn1_write_bool(unsigned char **p, const unsigned char *start, int boolean) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = (boolean) ? 255 : 0; + len++; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_BOOLEAN)); + + return (int) len; +} + +static int asn1_write_tagged_int(unsigned char **p, const unsigned char *start, int val, int tag) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + do { + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + len += 1; + *--(*p) = val & 0xff; + val >>= 8; + } while (val > 0); + + if (**p & 0x80) { + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + *--(*p) = 0x00; + len += 1; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, tag)); + + return (int) len; +} + +int mbedtls_asn1_write_int(unsigned char **p, const unsigned char *start, int val) +{ + return asn1_write_tagged_int(p, start, val, MBEDTLS_ASN1_INTEGER); +} + +int mbedtls_asn1_write_enum(unsigned char **p, const unsigned char *start, int val) +{ + return asn1_write_tagged_int(p, start, val, MBEDTLS_ASN1_ENUMERATED); +} + +int mbedtls_asn1_write_tagged_string(unsigned char **p, const unsigned char *start, int tag, + const char *text, size_t text_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, + (const unsigned char *) text, + text_len)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, tag)); + + return (int) len; +} + +int mbedtls_asn1_write_utf8_string(unsigned char **p, const unsigned char *start, + const char *text, size_t text_len) +{ + return mbedtls_asn1_write_tagged_string(p, start, MBEDTLS_ASN1_UTF8_STRING, text, text_len); +} + +int mbedtls_asn1_write_printable_string(unsigned char **p, const unsigned char *start, + const char *text, size_t text_len) +{ + return mbedtls_asn1_write_tagged_string(p, start, MBEDTLS_ASN1_PRINTABLE_STRING, text, + text_len); +} + +int mbedtls_asn1_write_ia5_string(unsigned char **p, const unsigned char *start, + const char *text, size_t text_len) +{ + return mbedtls_asn1_write_tagged_string(p, start, MBEDTLS_ASN1_IA5_STRING, text, text_len); +} + +int mbedtls_asn1_write_named_bitstring(unsigned char **p, + const unsigned char *start, + const unsigned char *buf, + size_t bits) +{ + size_t unused_bits, byte_len; + const unsigned char *cur_byte; + unsigned char cur_byte_shifted; + unsigned char bit; + + byte_len = (bits + 7) / 8; + unused_bits = (byte_len * 8) - bits; + + /* + * Named bitstrings require that trailing 0s are excluded in the encoding + * of the bitstring. Trailing 0s are considered part of the 'unused' bits + * when encoding this value in the first content octet + */ + if (bits != 0) { + cur_byte = buf + byte_len - 1; + cur_byte_shifted = *cur_byte >> unused_bits; + + for (;;) { + bit = cur_byte_shifted & 0x1; + cur_byte_shifted >>= 1; + + if (bit != 0) { + break; + } + + bits--; + if (bits == 0) { + break; + } + + if (bits % 8 == 0) { + cur_byte_shifted = *--cur_byte; + } + } + } + + return mbedtls_asn1_write_bitstring(p, start, buf, bits); +} + +int mbedtls_asn1_write_bitstring(unsigned char **p, const unsigned char *start, + const unsigned char *buf, size_t bits) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + size_t unused_bits, byte_len; + + byte_len = (bits + 7) / 8; + unused_bits = (byte_len * 8) - bits; + + if (*p < start || (size_t) (*p - start) < byte_len + 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + len = byte_len + 1; + + /* Write the bitstring. Ensure the unused bits are zeroed */ + if (byte_len > 0) { + byte_len--; + *--(*p) = buf[byte_len] & ~((0x1 << unused_bits) - 1); + (*p) -= byte_len; + memcpy(*p, buf, byte_len); + } + + /* Write unused bits */ + *--(*p) = (unsigned char) unused_bits; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_BIT_STRING)); + + return (int) len; +} + +int mbedtls_asn1_write_octet_string(unsigned char **p, const unsigned char *start, + const unsigned char *buf, size_t size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, buf, size)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OCTET_STRING)); + + return (int) len; +} + + +/* This is a copy of the ASN.1 parsing function mbedtls_asn1_find_named_data(), + * which is replicated to avoid a dependency ASN1_WRITE_C on ASN1_PARSE_C. */ +static mbedtls_asn1_named_data *asn1_find_named_data( + mbedtls_asn1_named_data *list, + const char *oid, size_t len) +{ + while (list != NULL) { + if (list->oid.len == len && + memcmp(list->oid.p, oid, len) == 0) { + break; + } + + list = list->next; + } + + return list; +} + +mbedtls_asn1_named_data *mbedtls_asn1_store_named_data( + mbedtls_asn1_named_data **head, + const char *oid, size_t oid_len, + const unsigned char *val, + size_t val_len) +{ + mbedtls_asn1_named_data *cur; + + if ((cur = asn1_find_named_data(*head, oid, oid_len)) == NULL) { + // Add new entry if not present yet based on OID + // + cur = (mbedtls_asn1_named_data *) mbedtls_calloc(1, + sizeof(mbedtls_asn1_named_data)); + if (cur == NULL) { + return NULL; + } + + cur->oid.len = oid_len; + cur->oid.p = mbedtls_calloc(1, oid_len); + if (cur->oid.p == NULL) { + mbedtls_free(cur); + return NULL; + } + + memcpy(cur->oid.p, oid, oid_len); + + cur->val.len = val_len; + if (val_len != 0) { + cur->val.p = mbedtls_calloc(1, val_len); + if (cur->val.p == NULL) { + mbedtls_free(cur->oid.p); + mbedtls_free(cur); + return NULL; + } + } + + cur->next = *head; + *head = cur; + } else if (val_len == 0) { + mbedtls_free(cur->val.p); + cur->val.p = NULL; + } else if (cur->val.len != val_len) { + /* + * Enlarge existing value buffer if needed + * Preserve old data until the allocation succeeded, to leave list in + * a consistent state in case allocation fails. + */ + void *p = mbedtls_calloc(1, val_len); + if (p == NULL) { + return NULL; + } + + mbedtls_free(cur->val.p); + cur->val.p = p; + cur->val.len = val_len; + } + + if (val != NULL && val_len != 0) { + memcpy(cur->val.p, val, val_len); + } + + return cur; +} +#endif /* MBEDTLS_ASN1_WRITE_C */ diff --git a/r5dev/thirdparty/mbedtls/base64.c b/r5dev/thirdparty/mbedtls/base64.c new file mode 100644 index 00000000..41706106 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/base64.c @@ -0,0 +1,277 @@ +/* + * RFC 1521 base64 encoding/decoding + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_BASE64_C) + +#include "mbedtls/base64.h" +#include "constant_time_internal.h" + +#include + +#if defined(MBEDTLS_SELF_TEST) +#include +#include "mbedtls/platform.h" +#endif /* MBEDTLS_SELF_TEST */ + +#define BASE64_SIZE_T_MAX ((size_t) -1) /* SIZE_T_MAX is not standard */ + +/* + * Encode a buffer into base64 format + */ +int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen) +{ + size_t i, n; + int C1, C2, C3; + unsigned char *p; + + if (slen == 0) { + *olen = 0; + return 0; + } + + n = slen / 3 + (slen % 3 != 0); + + if (n > (BASE64_SIZE_T_MAX - 1) / 4) { + *olen = BASE64_SIZE_T_MAX; + return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; + } + + n *= 4; + + if ((dlen < n + 1) || (NULL == dst)) { + *olen = n + 1; + return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; + } + + n = (slen / 3) * 3; + + for (i = 0, p = dst; i < n; i += 3) { + C1 = *src++; + C2 = *src++; + C3 = *src++; + + *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); + *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) + & 0x3F); + *p++ = mbedtls_ct_base64_enc_char((((C2 & 15) << 2) + (C3 >> 6)) + & 0x3F); + *p++ = mbedtls_ct_base64_enc_char(C3 & 0x3F); + } + + if (i < slen) { + C1 = *src++; + C2 = ((i + 1) < slen) ? *src++ : 0; + + *p++ = mbedtls_ct_base64_enc_char((C1 >> 2) & 0x3F); + *p++ = mbedtls_ct_base64_enc_char((((C1 & 3) << 4) + (C2 >> 4)) + & 0x3F); + + if ((i + 1) < slen) { + *p++ = mbedtls_ct_base64_enc_char(((C2 & 15) << 2) & 0x3F); + } else { + *p++ = '='; + } + + *p++ = '='; + } + + *olen = p - dst; + *p = 0; + + return 0; +} + +/* + * Decode a base64-formatted buffer + */ +int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen) +{ + size_t i; /* index in source */ + size_t n; /* number of digits or trailing = in source */ + uint32_t x; /* value accumulator */ + unsigned accumulated_digits = 0; + unsigned equals = 0; + int spaces_present = 0; + unsigned char *p; + + /* First pass: check for validity and get output length */ + for (i = n = 0; i < slen; i++) { + /* Skip spaces before checking for EOL */ + spaces_present = 0; + while (i < slen && src[i] == ' ') { + ++i; + spaces_present = 1; + } + + /* Spaces at end of buffer are OK */ + if (i == slen) { + break; + } + + if ((slen - i) >= 2 && + src[i] == '\r' && src[i + 1] == '\n') { + continue; + } + + if (src[i] == '\n') { + continue; + } + + /* Space inside a line is an error */ + if (spaces_present) { + return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; + } + + if (src[i] > 127) { + return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; + } + + if (src[i] == '=') { + if (++equals > 2) { + return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; + } + } else { + if (equals != 0) { + return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; + } + if (mbedtls_ct_base64_dec_value(src[i]) < 0) { + return MBEDTLS_ERR_BASE64_INVALID_CHARACTER; + } + } + n++; + } + + if (n == 0) { + *olen = 0; + return 0; + } + + /* The following expression is to calculate the following formula without + * risk of integer overflow in n: + * n = ( ( n * 6 ) + 7 ) >> 3; + */ + n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3); + n -= equals; + + if (dst == NULL || dlen < n) { + *olen = n; + return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; + } + + equals = 0; + for (x = 0, p = dst; i > 0; i--, src++) { + if (*src == '\r' || *src == '\n' || *src == ' ') { + continue; + } + + x = x << 6; + if (*src == '=') { + ++equals; + } else { + x |= mbedtls_ct_base64_dec_value(*src); + } + + if (++accumulated_digits == 4) { + accumulated_digits = 0; + *p++ = MBEDTLS_BYTE_2(x); + if (equals <= 1) { + *p++ = MBEDTLS_BYTE_1(x); + } + if (equals <= 0) { + *p++ = MBEDTLS_BYTE_0(x); + } + } + } + + *olen = p - dst; + + return 0; +} + +#if defined(MBEDTLS_SELF_TEST) + +static const unsigned char base64_test_dec[64] = +{ + 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, + 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, + 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, + 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, + 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, + 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, + 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, + 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 +}; + +static const unsigned char base64_test_enc[] = + "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" + "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; + +/* + * Checkup routine + */ +int mbedtls_base64_self_test(int verbose) +{ + size_t len; + const unsigned char *src; + unsigned char buffer[128]; + + if (verbose != 0) { + mbedtls_printf(" Base64 encoding test: "); + } + + src = base64_test_dec; + + if (mbedtls_base64_encode(buffer, sizeof(buffer), &len, src, 64) != 0 || + memcmp(base64_test_enc, buffer, 88) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return 1; + } + + if (verbose != 0) { + mbedtls_printf("passed\n Base64 decoding test: "); + } + + src = base64_test_enc; + + if (mbedtls_base64_decode(buffer, sizeof(buffer), &len, src, 88) != 0 || + memcmp(base64_test_dec, buffer, 64) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return 1; + } + + if (verbose != 0) { + mbedtls_printf("passed\n\n"); + } + + return 0; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_BASE64_C */ diff --git a/r5dev/thirdparty/mbedtls/bignum.c b/r5dev/thirdparty/mbedtls/bignum.c new file mode 100644 index 00000000..d3a1b00d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum.c @@ -0,0 +1,2706 @@ +/* + * Multi-precision integer library + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The following sources were referenced in the design of this Multi-precision + * Integer library: + * + * [1] Handbook of Applied Cryptography - 1997 + * Menezes, van Oorschot and Vanstone + * + * [2] Multi-Precision Math + * Tom St Denis + * https://github.com/libtom/libtommath/blob/develop/tommath.pdf + * + * [3] GNU Multi-Precision Arithmetic Library + * https://gmplib.org/manual/index.html + * + */ + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) + +#include "mbedtls/bignum.h" +#include "bignum_core.h" +#include "bn_mul.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "constant_time_internal.h" + +#include +#include + +#include "mbedtls/platform.h" + +#define MPI_VALIDATE_RET(cond) \ + MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_MPI_BAD_INPUT_DATA) +#define MPI_VALIDATE(cond) \ + MBEDTLS_INTERNAL_VALIDATE(cond) + +#define MPI_SIZE_T_MAX ((size_t) -1) /* SIZE_T_MAX is not standard */ + +/* Implementation that should never be optimized out by the compiler */ +static void mbedtls_mpi_zeroize(mbedtls_mpi_uint *v, size_t n) +{ + mbedtls_platform_zeroize(v, ciL * n); +} + +/* + * Initialize one MPI + */ +void mbedtls_mpi_init(mbedtls_mpi *X) +{ + MPI_VALIDATE(X != NULL); + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Unallocate one MPI + */ +void mbedtls_mpi_free(mbedtls_mpi *X) +{ + if (X == NULL) { + return; + } + + if (X->p != NULL) { + mbedtls_mpi_zeroize(X->p, X->n); + mbedtls_free(X->p); + } + + X->s = 1; + X->n = 0; + X->p = NULL; +} + +/* + * Enlarge to the specified number of limbs + */ +int mbedtls_mpi_grow(mbedtls_mpi *X, size_t nblimbs) +{ + mbedtls_mpi_uint *p; + MPI_VALIDATE_RET(X != NULL); + + if (nblimbs > MBEDTLS_MPI_MAX_LIMBS) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + if (X->n < nblimbs) { + if ((p = (mbedtls_mpi_uint *) mbedtls_calloc(nblimbs, ciL)) == NULL) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + if (X->p != NULL) { + memcpy(p, X->p, X->n * ciL); + mbedtls_mpi_zeroize(X->p, X->n); + mbedtls_free(X->p); + } + + X->n = nblimbs; + X->p = p; + } + + return 0; +} + +/* + * Resize down as much as possible, + * while keeping at least the specified number of limbs + */ +int mbedtls_mpi_shrink(mbedtls_mpi *X, size_t nblimbs) +{ + mbedtls_mpi_uint *p; + size_t i; + MPI_VALIDATE_RET(X != NULL); + + if (nblimbs > MBEDTLS_MPI_MAX_LIMBS) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + /* Actually resize up if there are currently fewer than nblimbs limbs. */ + if (X->n <= nblimbs) { + return mbedtls_mpi_grow(X, nblimbs); + } + /* After this point, then X->n > nblimbs and in particular X->n > 0. */ + + for (i = X->n - 1; i > 0; i--) { + if (X->p[i] != 0) { + break; + } + } + i++; + + if (i < nblimbs) { + i = nblimbs; + } + + if ((p = (mbedtls_mpi_uint *) mbedtls_calloc(i, ciL)) == NULL) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + if (X->p != NULL) { + memcpy(p, X->p, i * ciL); + mbedtls_mpi_zeroize(X->p, X->n); + mbedtls_free(X->p); + } + + X->n = i; + X->p = p; + + return 0; +} + +/* Resize X to have exactly n limbs and set it to 0. */ +static int mbedtls_mpi_resize_clear(mbedtls_mpi *X, size_t limbs) +{ + if (limbs == 0) { + mbedtls_mpi_free(X); + return 0; + } else if (X->n == limbs) { + memset(X->p, 0, limbs * ciL); + X->s = 1; + return 0; + } else { + mbedtls_mpi_free(X); + return mbedtls_mpi_grow(X, limbs); + } +} + +/* + * Copy the contents of Y into X. + * + * This function is not constant-time. Leading zeros in Y may be removed. + * + * Ensure that X does not shrink. This is not guaranteed by the public API, + * but some code in the bignum module relies on this property, for example + * in mbedtls_mpi_exp_mod(). + */ +int mbedtls_mpi_copy(mbedtls_mpi *X, const mbedtls_mpi *Y) +{ + int ret = 0; + size_t i; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(Y != NULL); + + if (X == Y) { + return 0; + } + + if (Y->n == 0) { + if (X->n != 0) { + X->s = 1; + memset(X->p, 0, X->n * ciL); + } + return 0; + } + + for (i = Y->n - 1; i > 0; i--) { + if (Y->p[i] != 0) { + break; + } + } + i++; + + X->s = Y->s; + + if (X->n < i) { + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, i)); + } else { + memset(X->p + i, 0, (X->n - i) * ciL); + } + + memcpy(X->p, Y->p, i * ciL); + +cleanup: + + return ret; +} + +/* + * Swap the contents of X and Y + */ +void mbedtls_mpi_swap(mbedtls_mpi *X, mbedtls_mpi *Y) +{ + mbedtls_mpi T; + MPI_VALIDATE(X != NULL); + MPI_VALIDATE(Y != NULL); + + memcpy(&T, X, sizeof(mbedtls_mpi)); + memcpy(X, Y, sizeof(mbedtls_mpi)); + memcpy(Y, &T, sizeof(mbedtls_mpi)); +} + +static inline mbedtls_mpi_uint mpi_sint_abs(mbedtls_mpi_sint z) +{ + if (z >= 0) { + return z; + } + /* Take care to handle the most negative value (-2^(biL-1)) correctly. + * A naive -z would have undefined behavior. + * Write this in a way that makes popular compilers happy (GCC, Clang, + * MSVC). */ + return (mbedtls_mpi_uint) 0 - (mbedtls_mpi_uint) z; +} + +/* + * Set value from integer + */ +int mbedtls_mpi_lset(mbedtls_mpi *X, mbedtls_mpi_sint z) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MPI_VALIDATE_RET(X != NULL); + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, 1)); + memset(X->p, 0, X->n * ciL); + + X->p[0] = mpi_sint_abs(z); + X->s = (z < 0) ? -1 : 1; + +cleanup: + + return ret; +} + +/* + * Get a specific bit + */ +int mbedtls_mpi_get_bit(const mbedtls_mpi *X, size_t pos) +{ + MPI_VALIDATE_RET(X != NULL); + + if (X->n * biL <= pos) { + return 0; + } + + return (X->p[pos / biL] >> (pos % biL)) & 0x01; +} + +/* + * Set a bit to a specific value of 0 or 1 + */ +int mbedtls_mpi_set_bit(mbedtls_mpi *X, size_t pos, unsigned char val) +{ + int ret = 0; + size_t off = pos / biL; + size_t idx = pos % biL; + MPI_VALIDATE_RET(X != NULL); + + if (val != 0 && val != 1) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (X->n * biL <= pos) { + if (val == 0) { + return 0; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, off + 1)); + } + + X->p[off] &= ~((mbedtls_mpi_uint) 0x01 << idx); + X->p[off] |= (mbedtls_mpi_uint) val << idx; + +cleanup: + + return ret; +} + +/* + * Return the number of less significant zero-bits + */ +size_t mbedtls_mpi_lsb(const mbedtls_mpi *X) +{ + size_t i, j, count = 0; + MBEDTLS_INTERNAL_VALIDATE_RET(X != NULL, 0); + + for (i = 0; i < X->n; i++) { + for (j = 0; j < biL; j++, count++) { + if (((X->p[i] >> j) & 1) != 0) { + return count; + } + } + } + + return 0; +} + +/* + * Return the number of bits + */ +size_t mbedtls_mpi_bitlen(const mbedtls_mpi *X) +{ + return mbedtls_mpi_core_bitlen(X->p, X->n); +} + +/* + * Return the total size in bytes + */ +size_t mbedtls_mpi_size(const mbedtls_mpi *X) +{ + return (mbedtls_mpi_bitlen(X) + 7) >> 3; +} + +/* + * Convert an ASCII character to digit value + */ +static int mpi_get_digit(mbedtls_mpi_uint *d, int radix, char c) +{ + *d = 255; + + if (c >= 0x30 && c <= 0x39) { + *d = c - 0x30; + } + if (c >= 0x41 && c <= 0x46) { + *d = c - 0x37; + } + if (c >= 0x61 && c <= 0x66) { + *d = c - 0x57; + } + + if (*d >= (mbedtls_mpi_uint) radix) { + return MBEDTLS_ERR_MPI_INVALID_CHARACTER; + } + + return 0; +} + +/* + * Import from an ASCII string + */ +int mbedtls_mpi_read_string(mbedtls_mpi *X, int radix, const char *s) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, j, slen, n; + int sign = 1; + mbedtls_mpi_uint d; + mbedtls_mpi T; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(s != NULL); + + if (radix < 2 || radix > 16) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + mbedtls_mpi_init(&T); + + if (s[0] == 0) { + mbedtls_mpi_free(X); + return 0; + } + + if (s[0] == '-') { + ++s; + sign = -1; + } + + slen = strlen(s); + + if (radix == 16) { + if (slen > MPI_SIZE_T_MAX >> 2) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + n = BITS_TO_LIMBS(slen << 2); + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, n)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(X, 0)); + + for (i = slen, j = 0; i > 0; i--, j++) { + MBEDTLS_MPI_CHK(mpi_get_digit(&d, radix, s[i - 1])); + X->p[j / (2 * ciL)] |= d << ((j % (2 * ciL)) << 2); + } + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(X, 0)); + + for (i = 0; i < slen; i++) { + MBEDTLS_MPI_CHK(mpi_get_digit(&d, radix, s[i])); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_int(&T, X, radix)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(X, &T, d)); + } + } + + if (sign < 0 && mbedtls_mpi_bitlen(X) != 0) { + X->s = -1; + } + +cleanup: + + mbedtls_mpi_free(&T); + + return ret; +} + +/* + * Helper to write the digits high-order first. + */ +static int mpi_write_hlp(mbedtls_mpi *X, int radix, + char **p, const size_t buflen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi_uint r; + size_t length = 0; + char *p_end = *p + buflen; + + do { + if (length >= buflen) { + return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_int(&r, X, radix)); + MBEDTLS_MPI_CHK(mbedtls_mpi_div_int(X, NULL, X, radix)); + /* + * Write the residue in the current position, as an ASCII character. + */ + if (r < 0xA) { + *(--p_end) = (char) ('0' + r); + } else { + *(--p_end) = (char) ('A' + (r - 0xA)); + } + + length++; + } while (mbedtls_mpi_cmp_int(X, 0) != 0); + + memmove(*p, p_end, length); + *p += length; + +cleanup: + + return ret; +} + +/* + * Export into an ASCII string + */ +int mbedtls_mpi_write_string(const mbedtls_mpi *X, int radix, + char *buf, size_t buflen, size_t *olen) +{ + int ret = 0; + size_t n; + char *p; + mbedtls_mpi T; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(olen != NULL); + MPI_VALIDATE_RET(buflen == 0 || buf != NULL); + + if (radix < 2 || radix > 16) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + n = mbedtls_mpi_bitlen(X); /* Number of bits necessary to present `n`. */ + if (radix >= 4) { + n >>= 1; /* Number of 4-adic digits necessary to present + * `n`. If radix > 4, this might be a strict + * overapproximation of the number of + * radix-adic digits needed to present `n`. */ + } + if (radix >= 16) { + n >>= 1; /* Number of hexadecimal digits necessary to + * present `n`. */ + + } + n += 1; /* Terminating null byte */ + n += 1; /* Compensate for the divisions above, which round down `n` + * in case it's not even. */ + n += 1; /* Potential '-'-sign. */ + n += (n & 1); /* Make n even to have enough space for hexadecimal writing, + * which always uses an even number of hex-digits. */ + + if (buflen < n) { + *olen = n; + return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL; + } + + p = buf; + mbedtls_mpi_init(&T); + + if (X->s == -1) { + *p++ = '-'; + buflen--; + } + + if (radix == 16) { + int c; + size_t i, j, k; + + for (i = X->n, k = 0; i > 0; i--) { + for (j = ciL; j > 0; j--) { + c = (X->p[i - 1] >> ((j - 1) << 3)) & 0xFF; + + if (c == 0 && k == 0 && (i + j) != 2) { + continue; + } + + *(p++) = "0123456789ABCDEF" [c / 16]; + *(p++) = "0123456789ABCDEF" [c % 16]; + k = 1; + } + } + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&T, X)); + + if (T.s == -1) { + T.s = 1; + } + + MBEDTLS_MPI_CHK(mpi_write_hlp(&T, radix, &p, buflen)); + } + + *p++ = '\0'; + *olen = p - buf; + +cleanup: + + mbedtls_mpi_free(&T); + + return ret; +} + +#if defined(MBEDTLS_FS_IO) +/* + * Read X from an opened file + */ +int mbedtls_mpi_read_file(mbedtls_mpi *X, int radix, FILE *fin) +{ + mbedtls_mpi_uint d; + size_t slen; + char *p; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[MBEDTLS_MPI_RW_BUFFER_SIZE]; + + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(fin != NULL); + + if (radix < 2 || radix > 16) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + memset(s, 0, sizeof(s)); + if (fgets(s, sizeof(s) - 1, fin) == NULL) { + return MBEDTLS_ERR_MPI_FILE_IO_ERROR; + } + + slen = strlen(s); + if (slen == sizeof(s) - 2) { + return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL; + } + + if (slen > 0 && s[slen - 1] == '\n') { + slen--; s[slen] = '\0'; + } + if (slen > 0 && s[slen - 1] == '\r') { + slen--; s[slen] = '\0'; + } + + p = s + slen; + while (p-- > s) { + if (mpi_get_digit(&d, radix, *p) != 0) { + break; + } + } + + return mbedtls_mpi_read_string(X, radix, p + 1); +} + +/* + * Write X into an opened file (or stdout if fout == NULL) + */ +int mbedtls_mpi_write_file(const char *p, const mbedtls_mpi *X, int radix, FILE *fout) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n, slen, plen; + /* + * Buffer should have space for (short) label and decimal formatted MPI, + * newline characters and '\0' + */ + char s[MBEDTLS_MPI_RW_BUFFER_SIZE]; + MPI_VALIDATE_RET(X != NULL); + + if (radix < 2 || radix > 16) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + memset(s, 0, sizeof(s)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(X, radix, s, sizeof(s) - 2, &n)); + + if (p == NULL) { + p = ""; + } + + plen = strlen(p); + slen = strlen(s); + s[slen++] = '\r'; + s[slen++] = '\n'; + + if (fout != NULL) { + if (fwrite(p, 1, plen, fout) != plen || + fwrite(s, 1, slen, fout) != slen) { + return MBEDTLS_ERR_MPI_FILE_IO_ERROR; + } + } else { + mbedtls_printf("%s%s", p, s); + } + +cleanup: + + return ret; +} +#endif /* MBEDTLS_FS_IO */ + +/* + * Import X from unsigned binary data, little endian + * + * This function is guaranteed to return an MPI with exactly the necessary + * number of limbs (in particular, it does not skip 0s in the input). + */ +int mbedtls_mpi_read_binary_le(mbedtls_mpi *X, + const unsigned char *buf, size_t buflen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const size_t limbs = CHARS_TO_LIMBS(buflen); + + /* Ensure that target MPI has exactly the necessary number of limbs */ + MBEDTLS_MPI_CHK(mbedtls_mpi_resize_clear(X, limbs)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_core_read_le(X->p, X->n, buf, buflen)); + +cleanup: + + /* + * This function is also used to import keys. However, wiping the buffers + * upon failure is not necessary because failure only can happen before any + * input is copied. + */ + return ret; +} + +/* + * Import X from unsigned binary data, big endian + * + * This function is guaranteed to return an MPI with exactly the necessary + * number of limbs (in particular, it does not skip 0s in the input). + */ +int mbedtls_mpi_read_binary(mbedtls_mpi *X, const unsigned char *buf, size_t buflen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const size_t limbs = CHARS_TO_LIMBS(buflen); + + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(buflen == 0 || buf != NULL); + + /* Ensure that target MPI has exactly the necessary number of limbs */ + MBEDTLS_MPI_CHK(mbedtls_mpi_resize_clear(X, limbs)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_core_read_be(X->p, X->n, buf, buflen)); + +cleanup: + + /* + * This function is also used to import keys. However, wiping the buffers + * upon failure is not necessary because failure only can happen before any + * input is copied. + */ + return ret; +} + +/* + * Export X into unsigned binary data, little endian + */ +int mbedtls_mpi_write_binary_le(const mbedtls_mpi *X, + unsigned char *buf, size_t buflen) +{ + return mbedtls_mpi_core_write_le(X->p, X->n, buf, buflen); +} + +/* + * Export X into unsigned binary data, big endian + */ +int mbedtls_mpi_write_binary(const mbedtls_mpi *X, + unsigned char *buf, size_t buflen) +{ + return mbedtls_mpi_core_write_be(X->p, X->n, buf, buflen); +} + +/* + * Left-shift: X <<= count + */ +int mbedtls_mpi_shift_l(mbedtls_mpi *X, size_t count) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, v0, t1; + mbedtls_mpi_uint r0 = 0, r1; + MPI_VALIDATE_RET(X != NULL); + + v0 = count / (biL); + t1 = count & (biL - 1); + + i = mbedtls_mpi_bitlen(X) + count; + + if (X->n * biL < i) { + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, BITS_TO_LIMBS(i))); + } + + ret = 0; + + /* + * shift by count / limb_size + */ + if (v0 > 0) { + for (i = X->n; i > v0; i--) { + X->p[i - 1] = X->p[i - v0 - 1]; + } + + for (; i > 0; i--) { + X->p[i - 1] = 0; + } + } + + /* + * shift by count % limb_size + */ + if (t1 > 0) { + for (i = v0; i < X->n; i++) { + r1 = X->p[i] >> (biL - t1); + X->p[i] <<= t1; + X->p[i] |= r0; + r0 = r1; + } + } + +cleanup: + + return ret; +} + +/* + * Right-shift: X >>= count + */ +int mbedtls_mpi_shift_r(mbedtls_mpi *X, size_t count) +{ + MPI_VALIDATE_RET(X != NULL); + if (X->n != 0) { + mbedtls_mpi_core_shift_r(X->p, X->n, count); + } + return 0; +} + +/* + * Compare unsigned values + */ +int mbedtls_mpi_cmp_abs(const mbedtls_mpi *X, const mbedtls_mpi *Y) +{ + size_t i, j; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(Y != NULL); + + for (i = X->n; i > 0; i--) { + if (X->p[i - 1] != 0) { + break; + } + } + + for (j = Y->n; j > 0; j--) { + if (Y->p[j - 1] != 0) { + break; + } + } + + if (i == 0 && j == 0) { + return 0; + } + + if (i > j) { + return 1; + } + if (j > i) { + return -1; + } + + for (; i > 0; i--) { + if (X->p[i - 1] > Y->p[i - 1]) { + return 1; + } + if (X->p[i - 1] < Y->p[i - 1]) { + return -1; + } + } + + return 0; +} + +/* + * Compare signed values + */ +int mbedtls_mpi_cmp_mpi(const mbedtls_mpi *X, const mbedtls_mpi *Y) +{ + size_t i, j; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(Y != NULL); + + for (i = X->n; i > 0; i--) { + if (X->p[i - 1] != 0) { + break; + } + } + + for (j = Y->n; j > 0; j--) { + if (Y->p[j - 1] != 0) { + break; + } + } + + if (i == 0 && j == 0) { + return 0; + } + + if (i > j) { + return X->s; + } + if (j > i) { + return -Y->s; + } + + if (X->s > 0 && Y->s < 0) { + return 1; + } + if (Y->s > 0 && X->s < 0) { + return -1; + } + + for (; i > 0; i--) { + if (X->p[i - 1] > Y->p[i - 1]) { + return X->s; + } + if (X->p[i - 1] < Y->p[i - 1]) { + return -X->s; + } + } + + return 0; +} + +/* + * Compare signed values + */ +int mbedtls_mpi_cmp_int(const mbedtls_mpi *X, mbedtls_mpi_sint z) +{ + mbedtls_mpi Y; + mbedtls_mpi_uint p[1]; + MPI_VALIDATE_RET(X != NULL); + + *p = mpi_sint_abs(z); + Y.s = (z < 0) ? -1 : 1; + Y.n = 1; + Y.p = p; + + return mbedtls_mpi_cmp_mpi(X, &Y); +} + +/* + * Unsigned addition: X = |A| + |B| (HAC 14.7) + */ +int mbedtls_mpi_add_abs(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t j; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(B != NULL); + + if (X == B) { + const mbedtls_mpi *T = A; A = X; B = T; + } + + if (X != A) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, A)); + } + + /* + * X must always be positive as a result of unsigned additions. + */ + X->s = 1; + + for (j = B->n; j > 0; j--) { + if (B->p[j - 1] != 0) { + break; + } + } + + /* Exit early to avoid undefined behavior on NULL+0 when X->n == 0 + * and B is 0 (of any size). */ + if (j == 0) { + return 0; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, j)); + + /* j is the number of non-zero limbs of B. Add those to X. */ + + mbedtls_mpi_uint *p = X->p; + + mbedtls_mpi_uint c = mbedtls_mpi_core_add(p, p, B->p, j); + + p += j; + + /* Now propagate any carry */ + + while (c != 0) { + if (j >= X->n) { + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, j + 1)); + p = X->p + j; + } + + *p += c; c = (*p < c); j++; p++; + } + +cleanup: + + return ret; +} + +/* + * Unsigned subtraction: X = |A| - |B| (HAC 14.9, 14.10) + */ +int mbedtls_mpi_sub_abs(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + mbedtls_mpi_uint carry; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(B != NULL); + + for (n = B->n; n > 0; n--) { + if (B->p[n - 1] != 0) { + break; + } + } + if (n > A->n) { + /* B >= (2^ciL)^n > A */ + ret = MBEDTLS_ERR_MPI_NEGATIVE_VALUE; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, A->n)); + + /* Set the high limbs of X to match A. Don't touch the lower limbs + * because X might be aliased to B, and we must not overwrite the + * significant digits of B. */ + if (A->n > n && A != X) { + memcpy(X->p + n, A->p + n, (A->n - n) * ciL); + } + if (X->n > A->n) { + memset(X->p + A->n, 0, (X->n - A->n) * ciL); + } + + carry = mbedtls_mpi_core_sub(X->p, A->p, B->p, n); + if (carry != 0) { + /* Propagate the carry through the rest of X. */ + carry = mbedtls_mpi_core_sub_int(X->p + n, X->p + n, carry, X->n - n); + + /* If we have further carry/borrow, the result is negative. */ + if (carry != 0) { + ret = MBEDTLS_ERR_MPI_NEGATIVE_VALUE; + goto cleanup; + } + } + + /* X should always be positive as a result of unsigned subtractions. */ + X->s = 1; + +cleanup: + return ret; +} + +/* Common function for signed addition and subtraction. + * Calculate A + B * flip_B where flip_B is 1 or -1. + */ +static int add_sub_mpi(mbedtls_mpi *X, + const mbedtls_mpi *A, const mbedtls_mpi *B, + int flip_B) +{ + int ret, s; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(B != NULL); + + s = A->s; + if (A->s * B->s * flip_B < 0) { + int cmp = mbedtls_mpi_cmp_abs(A, B); + if (cmp >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_abs(X, A, B)); + /* If |A| = |B|, the result is 0 and we must set the sign bit + * to +1 regardless of which of A or B was negative. Otherwise, + * since |A| > |B|, the sign is the sign of A. */ + X->s = cmp == 0 ? 1 : s; + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_abs(X, B, A)); + /* Since |A| < |B|, the sign is the opposite of A. */ + X->s = -s; + } + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_abs(X, A, B)); + X->s = s; + } + +cleanup: + + return ret; +} + +/* + * Signed addition: X = A + B + */ +int mbedtls_mpi_add_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B) +{ + return add_sub_mpi(X, A, B, 1); +} + +/* + * Signed subtraction: X = A - B + */ +int mbedtls_mpi_sub_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B) +{ + return add_sub_mpi(X, A, B, -1); +} + +/* + * Signed addition: X = A + b + */ +int mbedtls_mpi_add_int(mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b) +{ + mbedtls_mpi B; + mbedtls_mpi_uint p[1]; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + + p[0] = mpi_sint_abs(b); + B.s = (b < 0) ? -1 : 1; + B.n = 1; + B.p = p; + + return mbedtls_mpi_add_mpi(X, A, &B); +} + +/* + * Signed subtraction: X = A - b + */ +int mbedtls_mpi_sub_int(mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b) +{ + mbedtls_mpi B; + mbedtls_mpi_uint p[1]; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + + p[0] = mpi_sint_abs(b); + B.s = (b < 0) ? -1 : 1; + B.n = 1; + B.p = p; + + return mbedtls_mpi_sub_mpi(X, A, &B); +} + +/* + * Baseline multiplication: X = A * B (HAC 14.12) + */ +int mbedtls_mpi_mul_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, j; + mbedtls_mpi TA, TB; + int result_is_zero = 0; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(B != NULL); + + mbedtls_mpi_init(&TA); mbedtls_mpi_init(&TB); + + if (X == A) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TA, A)); A = &TA; + } + if (X == B) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TB, B)); B = &TB; + } + + for (i = A->n; i > 0; i--) { + if (A->p[i - 1] != 0) { + break; + } + } + if (i == 0) { + result_is_zero = 1; + } + + for (j = B->n; j > 0; j--) { + if (B->p[j - 1] != 0) { + break; + } + } + if (j == 0) { + result_is_zero = 1; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, i + j)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(X, 0)); + + for (size_t k = 0; k < j; k++) { + /* We know that there cannot be any carry-out since we're + * iterating from bottom to top. */ + (void) mbedtls_mpi_core_mla(X->p + k, i + 1, + A->p, i, + B->p[k]); + } + + /* If the result is 0, we don't shortcut the operation, which reduces + * but does not eliminate side channels leaking the zero-ness. We do + * need to take care to set the sign bit properly since the library does + * not fully support an MPI object with a value of 0 and s == -1. */ + if (result_is_zero) { + X->s = 1; + } else { + X->s = A->s * B->s; + } + +cleanup: + + mbedtls_mpi_free(&TB); mbedtls_mpi_free(&TA); + + return ret; +} + +/* + * Baseline multiplication: X = A * b + */ +int mbedtls_mpi_mul_int(mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_uint b) +{ + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + + size_t n = A->n; + while (n > 0 && A->p[n - 1] == 0) { + --n; + } + + /* The general method below doesn't work if b==0. */ + if (b == 0 || n == 0) { + return mbedtls_mpi_lset(X, 0); + } + + /* Calculate A*b as A + A*(b-1) to take advantage of mbedtls_mpi_core_mla */ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + /* In general, A * b requires 1 limb more than b. If + * A->p[n - 1] * b / b == A->p[n - 1], then A * b fits in the same + * number of limbs as A and the call to grow() is not required since + * copy() will take care of the growth if needed. However, experimentally, + * making the call to grow() unconditional causes slightly fewer + * calls to calloc() in ECP code, presumably because it reuses the + * same mpi for a while and this way the mpi is more likely to directly + * grow to its final size. + * + * Note that calculating A*b as 0 + A*b doesn't work as-is because + * A,X can be the same. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, n + 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, A)); + mbedtls_mpi_core_mla(X->p, X->n, A->p, n, b - 1); + +cleanup: + return ret; +} + +/* + * Unsigned integer divide - double mbedtls_mpi_uint dividend, u1/u0, and + * mbedtls_mpi_uint divisor, d + */ +static mbedtls_mpi_uint mbedtls_int_div_int(mbedtls_mpi_uint u1, + mbedtls_mpi_uint u0, + mbedtls_mpi_uint d, + mbedtls_mpi_uint *r) +{ +#if defined(MBEDTLS_HAVE_UDBL) + mbedtls_t_udbl dividend, quotient; +#else + const mbedtls_mpi_uint radix = (mbedtls_mpi_uint) 1 << biH; + const mbedtls_mpi_uint uint_halfword_mask = ((mbedtls_mpi_uint) 1 << biH) - 1; + mbedtls_mpi_uint d0, d1, q0, q1, rAX, r0, quotient; + mbedtls_mpi_uint u0_msw, u0_lsw; + size_t s; +#endif + + /* + * Check for overflow + */ + if (0 == d || u1 >= d) { + if (r != NULL) { + *r = ~(mbedtls_mpi_uint) 0u; + } + + return ~(mbedtls_mpi_uint) 0u; + } + +#if defined(MBEDTLS_HAVE_UDBL) + dividend = (mbedtls_t_udbl) u1 << biL; + dividend |= (mbedtls_t_udbl) u0; + quotient = dividend / d; + if (quotient > ((mbedtls_t_udbl) 1 << biL) - 1) { + quotient = ((mbedtls_t_udbl) 1 << biL) - 1; + } + + if (r != NULL) { + *r = (mbedtls_mpi_uint) (dividend - (quotient * d)); + } + + return (mbedtls_mpi_uint) quotient; +#else + + /* + * Algorithm D, Section 4.3.1 - The Art of Computer Programming + * Vol. 2 - Seminumerical Algorithms, Knuth + */ + + /* + * Normalize the divisor, d, and dividend, u0, u1 + */ + s = mbedtls_mpi_core_clz(d); + d = d << s; + + u1 = u1 << s; + u1 |= (u0 >> (biL - s)) & (-(mbedtls_mpi_sint) s >> (biL - 1)); + u0 = u0 << s; + + d1 = d >> biH; + d0 = d & uint_halfword_mask; + + u0_msw = u0 >> biH; + u0_lsw = u0 & uint_halfword_mask; + + /* + * Find the first quotient and remainder + */ + q1 = u1 / d1; + r0 = u1 - d1 * q1; + + while (q1 >= radix || (q1 * d0 > radix * r0 + u0_msw)) { + q1 -= 1; + r0 += d1; + + if (r0 >= radix) { + break; + } + } + + rAX = (u1 * radix) + (u0_msw - q1 * d); + q0 = rAX / d1; + r0 = rAX - q0 * d1; + + while (q0 >= radix || (q0 * d0 > radix * r0 + u0_lsw)) { + q0 -= 1; + r0 += d1; + + if (r0 >= radix) { + break; + } + } + + if (r != NULL) { + *r = (rAX * radix + u0_lsw - q0 * d) >> s; + } + + quotient = q1 * radix + q0; + + return quotient; +#endif +} + +/* + * Division by mbedtls_mpi: A = Q * B + R (HAC 14.20) + */ +int mbedtls_mpi_div_mpi(mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, + const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, n, t, k; + mbedtls_mpi X, Y, Z, T1, T2; + mbedtls_mpi_uint TP2[3]; + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(B != NULL); + + if (mbedtls_mpi_cmp_int(B, 0) == 0) { + return MBEDTLS_ERR_MPI_DIVISION_BY_ZERO; + } + + mbedtls_mpi_init(&X); mbedtls_mpi_init(&Y); mbedtls_mpi_init(&Z); + mbedtls_mpi_init(&T1); + /* + * Avoid dynamic memory allocations for constant-size T2. + * + * T2 is used for comparison only and the 3 limbs are assigned explicitly, + * so nobody increase the size of the MPI and we're safe to use an on-stack + * buffer. + */ + T2.s = 1; + T2.n = sizeof(TP2) / sizeof(*TP2); + T2.p = TP2; + + if (mbedtls_mpi_cmp_abs(A, B) < 0) { + if (Q != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(Q, 0)); + } + if (R != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(R, A)); + } + return 0; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&X, A)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&Y, B)); + X.s = Y.s = 1; + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&Z, A->n + 2)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&Z, 0)); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&T1, A->n + 2)); + + k = mbedtls_mpi_bitlen(&Y) % biL; + if (k < biL - 1) { + k = biL - 1 - k; + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&X, k)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&Y, k)); + } else { + k = 0; + } + + n = X.n - 1; + t = Y.n - 1; + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&Y, biL * (n - t))); + + while (mbedtls_mpi_cmp_mpi(&X, &Y) >= 0) { + Z.p[n - t]++; + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&X, &X, &Y)); + } + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&Y, biL * (n - t))); + + for (i = n; i > t; i--) { + if (X.p[i] >= Y.p[t]) { + Z.p[i - t - 1] = ~(mbedtls_mpi_uint) 0u; + } else { + Z.p[i - t - 1] = mbedtls_int_div_int(X.p[i], X.p[i - 1], + Y.p[t], NULL); + } + + T2.p[0] = (i < 2) ? 0 : X.p[i - 2]; + T2.p[1] = (i < 1) ? 0 : X.p[i - 1]; + T2.p[2] = X.p[i]; + + Z.p[i - t - 1]++; + do { + Z.p[i - t - 1]--; + + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&T1, 0)); + T1.p[0] = (t < 1) ? 0 : Y.p[t - 1]; + T1.p[1] = Y.p[t]; + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_int(&T1, &T1, Z.p[i - t - 1])); + } while (mbedtls_mpi_cmp_mpi(&T1, &T2) > 0); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_int(&T1, &Y, Z.p[i - t - 1])); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&T1, biL * (i - t - 1))); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&X, &X, &T1)); + + if (mbedtls_mpi_cmp_int(&X, 0) < 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&T1, &Y)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&T1, biL * (i - t - 1))); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&X, &X, &T1)); + Z.p[i - t - 1]--; + } + } + + if (Q != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(Q, &Z)); + Q->s = A->s * B->s; + } + + if (R != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&X, k)); + X.s = A->s; + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(R, &X)); + + if (mbedtls_mpi_cmp_int(R, 0) == 0) { + R->s = 1; + } + } + +cleanup: + + mbedtls_mpi_free(&X); mbedtls_mpi_free(&Y); mbedtls_mpi_free(&Z); + mbedtls_mpi_free(&T1); + mbedtls_platform_zeroize(TP2, sizeof(TP2)); + + return ret; +} + +/* + * Division by int: A = Q * b + R + */ +int mbedtls_mpi_div_int(mbedtls_mpi *Q, mbedtls_mpi *R, + const mbedtls_mpi *A, + mbedtls_mpi_sint b) +{ + mbedtls_mpi B; + mbedtls_mpi_uint p[1]; + MPI_VALIDATE_RET(A != NULL); + + p[0] = mpi_sint_abs(b); + B.s = (b < 0) ? -1 : 1; + B.n = 1; + B.p = p; + + return mbedtls_mpi_div_mpi(Q, R, A, &B); +} + +/* + * Modulo: R = A mod B + */ +int mbedtls_mpi_mod_mpi(mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MPI_VALIDATE_RET(R != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(B != NULL); + + if (mbedtls_mpi_cmp_int(B, 0) < 0) { + return MBEDTLS_ERR_MPI_NEGATIVE_VALUE; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(NULL, R, A, B)); + + while (mbedtls_mpi_cmp_int(R, 0) < 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(R, R, B)); + } + + while (mbedtls_mpi_cmp_mpi(R, B) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(R, R, B)); + } + +cleanup: + + return ret; +} + +/* + * Modulo: r = A mod b + */ +int mbedtls_mpi_mod_int(mbedtls_mpi_uint *r, const mbedtls_mpi *A, mbedtls_mpi_sint b) +{ + size_t i; + mbedtls_mpi_uint x, y, z; + MPI_VALIDATE_RET(r != NULL); + MPI_VALIDATE_RET(A != NULL); + + if (b == 0) { + return MBEDTLS_ERR_MPI_DIVISION_BY_ZERO; + } + + if (b < 0) { + return MBEDTLS_ERR_MPI_NEGATIVE_VALUE; + } + + /* + * handle trivial cases + */ + if (b == 1 || A->n == 0) { + *r = 0; + return 0; + } + + if (b == 2) { + *r = A->p[0] & 1; + return 0; + } + + /* + * general case + */ + for (i = A->n, y = 0; i > 0; i--) { + x = A->p[i - 1]; + y = (y << biH) | (x >> biH); + z = y / b; + y -= z * b; + + x <<= biH; + y = (y << biH) | (x >> biH); + z = y / b; + y -= z * b; + } + + /* + * If A is negative, then the current y represents a negative value. + * Flipping it to the positive side. + */ + if (A->s < 0 && y != 0) { + y = b - y; + } + + *r = y; + + return 0; +} + +static void mpi_montg_init(mbedtls_mpi_uint *mm, const mbedtls_mpi *N) +{ + *mm = mbedtls_mpi_core_montmul_init(N->p); +} + +/** Montgomery multiplication: A = A * B * R^-1 mod N (HAC 14.36) + * + * \param[in,out] A One of the numbers to multiply. + * It must have at least as many limbs as N + * (A->n >= N->n), and any limbs beyond n are ignored. + * On successful completion, A contains the result of + * the multiplication A * B * R^-1 mod N where + * R = (2^ciL)^n. + * \param[in] B One of the numbers to multiply. + * It must be nonzero and must not have more limbs than N + * (B->n <= N->n). + * \param[in] N The modulus. \p N must be odd. + * \param mm The value calculated by `mpi_montg_init(&mm, N)`. + * This is -N^-1 mod 2^ciL. + * \param[in,out] T A bignum for temporary storage. + * It must be at least twice the limb size of N plus 1 + * (T->n >= 2 * N->n + 1). + * Its initial content is unused and + * its final content is indeterminate. + * It does not get reallocated. + */ +static void mpi_montmul(mbedtls_mpi *A, const mbedtls_mpi *B, + const mbedtls_mpi *N, mbedtls_mpi_uint mm, + mbedtls_mpi *T) +{ + mbedtls_mpi_core_montmul(A->p, A->p, B->p, B->n, N->p, N->n, mm, T->p); +} + +/* + * Montgomery reduction: A = A * R^-1 mod N + * + * See mpi_montmul() regarding constraints and guarantees on the parameters. + */ +static void mpi_montred(mbedtls_mpi *A, const mbedtls_mpi *N, + mbedtls_mpi_uint mm, mbedtls_mpi *T) +{ + mbedtls_mpi_uint z = 1; + mbedtls_mpi U; + + U.n = U.s = (int) z; + U.p = &z; + + mpi_montmul(A, &U, N, mm, T); +} + +/** + * Select an MPI from a table without leaking the index. + * + * This is functionally equivalent to mbedtls_mpi_copy(R, T[idx]) except it + * reads the entire table in order to avoid leaking the value of idx to an + * attacker able to observe memory access patterns. + * + * \param[out] R Where to write the selected MPI. + * \param[in] T The table to read from. + * \param[in] T_size The number of elements in the table. + * \param[in] idx The index of the element to select; + * this must satisfy 0 <= idx < T_size. + * + * \return \c 0 on success, or a negative error code. + */ +static int mpi_select(mbedtls_mpi *R, const mbedtls_mpi *T, size_t T_size, size_t idx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + for (size_t i = 0; i < T_size; i++) { + MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_assign(R, &T[i], + (unsigned char) mbedtls_ct_size_bool_eq(i, + idx))); + } + +cleanup: + return ret; +} + +/* + * Sliding-window exponentiation: X = A^E mod N (HAC 14.85) + */ +int mbedtls_mpi_exp_mod(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *E, const mbedtls_mpi *N, + mbedtls_mpi *prec_RR) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t window_bitsize; + size_t i, j, nblimbs; + size_t bufsize, nbits; + size_t exponent_bits_in_window = 0; + mbedtls_mpi_uint ei, mm, state; + mbedtls_mpi RR, T, W[(size_t) 1 << MBEDTLS_MPI_WINDOW_SIZE], WW, Apos; + int neg; + + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(E != NULL); + MPI_VALIDATE_RET(N != NULL); + + if (mbedtls_mpi_cmp_int(N, 0) <= 0 || (N->p[0] & 1) == 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (mbedtls_mpi_cmp_int(E, 0) < 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (mbedtls_mpi_bitlen(E) > MBEDTLS_MPI_MAX_BITS || + mbedtls_mpi_bitlen(N) > MBEDTLS_MPI_MAX_BITS) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + /* + * Init temps and window size + */ + mpi_montg_init(&mm, N); + mbedtls_mpi_init(&RR); mbedtls_mpi_init(&T); + mbedtls_mpi_init(&Apos); + mbedtls_mpi_init(&WW); + memset(W, 0, sizeof(W)); + + i = mbedtls_mpi_bitlen(E); + + window_bitsize = (i > 671) ? 6 : (i > 239) ? 5 : + (i > 79) ? 4 : (i > 23) ? 3 : 1; + +#if (MBEDTLS_MPI_WINDOW_SIZE < 6) + if (window_bitsize > MBEDTLS_MPI_WINDOW_SIZE) { + window_bitsize = MBEDTLS_MPI_WINDOW_SIZE; + } +#endif + + const size_t w_table_used_size = (size_t) 1 << window_bitsize; + + /* + * This function is not constant-trace: its memory accesses depend on the + * exponent value. To defend against timing attacks, callers (such as RSA + * and DHM) should use exponent blinding. However this is not enough if the + * adversary can find the exponent in a single trace, so this function + * takes extra precautions against adversaries who can observe memory + * access patterns. + * + * This function performs a series of multiplications by table elements and + * squarings, and we want the prevent the adversary from finding out which + * table element was used, and from distinguishing between multiplications + * and squarings. Firstly, when multiplying by an element of the window + * W[i], we do a constant-trace table lookup to obfuscate i. This leaves + * squarings as having a different memory access patterns from other + * multiplications. So secondly, we put the accumulator X in the table as + * well, and also do a constant-trace table lookup to multiply by X. + * + * This way, all multiplications take the form of a lookup-and-multiply. + * The number of lookup-and-multiply operations inside each iteration of + * the main loop still depends on the bits of the exponent, but since the + * other operations in the loop don't have an easily recognizable memory + * trace, an adversary is unlikely to be able to observe the exact + * patterns. + * + * An adversary may still be able to recover the exponent if they can + * observe both memory accesses and branches. However, branch prediction + * exploitation typically requires many traces of execution over the same + * data, which is defeated by randomized blinding. + * + * To achieve this, we make a copy of X and we use the table entry in each + * calculation from this point on. + */ + const size_t x_index = 0; + mbedtls_mpi_init(&W[x_index]); + mbedtls_mpi_copy(&W[x_index], X); + + j = N->n + 1; + /* All W[i] and X must have at least N->n limbs for the mpi_montmul() + * and mpi_montred() calls later. Here we ensure that W[1] and X are + * large enough, and later we'll grow other W[i] to the same length. + * They must not be shrunk midway through this function! + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[x_index], j)); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[1], j)); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&T, j * 2)); + + /* + * Compensate for negative A (and correct at the end) + */ + neg = (A->s == -1); + if (neg) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&Apos, A)); + Apos.s = 1; + A = &Apos; + } + + /* + * If 1st call, pre-compute R^2 mod N + */ + if (prec_RR == NULL || prec_RR->p == NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&RR, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&RR, N->n * 2 * biL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&RR, &RR, N)); + + if (prec_RR != NULL) { + memcpy(prec_RR, &RR, sizeof(mbedtls_mpi)); + } + } else { + memcpy(&RR, prec_RR, sizeof(mbedtls_mpi)); + } + + /* + * W[1] = A * R^2 * R^-1 mod N = A * R mod N + */ + if (mbedtls_mpi_cmp_mpi(A, N) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&W[1], A, N)); + /* This should be a no-op because W[1] is already that large before + * mbedtls_mpi_mod_mpi(), but it's necessary to avoid an overflow + * in mpi_montmul() below, so let's make sure. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[1], N->n + 1)); + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[1], A)); + } + + /* Note that this is safe because W[1] always has at least N->n limbs + * (it grew above and was preserved by mbedtls_mpi_copy()). */ + mpi_montmul(&W[1], &RR, N, mm, &T); + + /* + * W[x_index] = R^2 * R^-1 mod N = R mod N + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[x_index], &RR)); + mpi_montred(&W[x_index], N, mm, &T); + + + if (window_bitsize > 1) { + /* + * W[i] = W[1] ^ i + * + * The first bit of the sliding window is always 1 and therefore we + * only need to store the second half of the table. + * + * (There are two special elements in the table: W[0] for the + * accumulator/result and W[1] for A in Montgomery form. Both of these + * are already set at this point.) + */ + j = w_table_used_size / 2; + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[j], N->n + 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[j], &W[1])); + + for (i = 0; i < window_bitsize - 1; i++) { + mpi_montmul(&W[j], &W[j], N, mm, &T); + } + + /* + * W[i] = W[i - 1] * W[1] + */ + for (i = j + 1; i < w_table_used_size; i++) { + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&W[i], N->n + 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&W[i], &W[i - 1])); + + mpi_montmul(&W[i], &W[1], N, mm, &T); + } + } + + nblimbs = E->n; + bufsize = 0; + nbits = 0; + state = 0; + + while (1) { + if (bufsize == 0) { + if (nblimbs == 0) { + break; + } + + nblimbs--; + + bufsize = sizeof(mbedtls_mpi_uint) << 3; + } + + bufsize--; + + ei = (E->p[nblimbs] >> bufsize) & 1; + + /* + * skip leading 0s + */ + if (ei == 0 && state == 0) { + continue; + } + + if (ei == 0 && state == 1) { + /* + * out of window, square W[x_index] + */ + MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, x_index)); + mpi_montmul(&W[x_index], &WW, N, mm, &T); + continue; + } + + /* + * add ei to current window + */ + state = 2; + + nbits++; + exponent_bits_in_window |= (ei << (window_bitsize - nbits)); + + if (nbits == window_bitsize) { + /* + * W[x_index] = W[x_index]^window_bitsize R^-1 mod N + */ + for (i = 0; i < window_bitsize; i++) { + MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, + x_index)); + mpi_montmul(&W[x_index], &WW, N, mm, &T); + } + + /* + * W[x_index] = W[x_index] * W[exponent_bits_in_window] R^-1 mod N + */ + MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, + exponent_bits_in_window)); + mpi_montmul(&W[x_index], &WW, N, mm, &T); + + state--; + nbits = 0; + exponent_bits_in_window = 0; + } + } + + /* + * process the remaining bits + */ + for (i = 0; i < nbits; i++) { + MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, x_index)); + mpi_montmul(&W[x_index], &WW, N, mm, &T); + + exponent_bits_in_window <<= 1; + + if ((exponent_bits_in_window & ((size_t) 1 << window_bitsize)) != 0) { + MBEDTLS_MPI_CHK(mpi_select(&WW, W, w_table_used_size, 1)); + mpi_montmul(&W[x_index], &WW, N, mm, &T); + } + } + + /* + * W[x_index] = A^E * R * R^-1 mod N = A^E mod N + */ + mpi_montred(&W[x_index], N, mm, &T); + + if (neg && E->n != 0 && (E->p[0] & 1) != 0) { + W[x_index].s = -1; + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&W[x_index], N, &W[x_index])); + } + + /* + * Load the result in the output variable. + */ + mbedtls_mpi_copy(X, &W[x_index]); + +cleanup: + + /* The first bit of the sliding window is always 1 and therefore the first + * half of the table was unused. */ + for (i = w_table_used_size/2; i < w_table_used_size; i++) { + mbedtls_mpi_free(&W[i]); + } + + mbedtls_mpi_free(&W[x_index]); + mbedtls_mpi_free(&W[1]); + mbedtls_mpi_free(&T); + mbedtls_mpi_free(&Apos); + mbedtls_mpi_free(&WW); + + if (prec_RR == NULL || prec_RR->p == NULL) { + mbedtls_mpi_free(&RR); + } + + return ret; +} + +/* + * Greatest common divisor: G = gcd(A, B) (HAC 14.54) + */ +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; + + MPI_VALIDATE_RET(G != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(B != NULL); + + mbedtls_mpi_init(&TA); mbedtls_mpi_init(&TB); + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TA, A)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&TB, B)); + + lz = mbedtls_mpi_lsb(&TA); + lzt = mbedtls_mpi_lsb(&TB); + + /* 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); + goto cleanup; + } + + if (lzt < lz) { + lz = lzt; + } + + 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) + */ + + /* 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. */ + } + + /* 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_shift_l(&TB, lz)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(G, &TB)); + +cleanup: + + mbedtls_mpi_free(&TA); mbedtls_mpi_free(&TB); + + return ret; +} + +/* + * Fill X with size bytes of random. + * The bytes returned from the RNG are used in a specific order which + * is suitable for deterministic ECDSA (see the specification of + * mbedtls_mpi_random() and the implementation in mbedtls_mpi_fill_random()). + */ +int mbedtls_mpi_fill_random(mbedtls_mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const size_t limbs = CHARS_TO_LIMBS(size); + + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(f_rng != NULL); + + /* Ensure that target MPI has exactly the necessary number of limbs */ + MBEDTLS_MPI_CHK(mbedtls_mpi_resize_clear(X, limbs)); + if (size == 0) { + return 0; + } + + ret = mbedtls_mpi_core_fill_random(X->p, X->n, size, f_rng, p_rng); + +cleanup: + return ret; +} + +int mbedtls_mpi_random(mbedtls_mpi *X, + mbedtls_mpi_sint min, + const mbedtls_mpi *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + if (min < 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + if (mbedtls_mpi_cmp_int(N, min) <= 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + /* Ensure that target MPI has exactly the same number of limbs + * as the upper bound, even if the upper bound has leading zeros. + * This is necessary for mbedtls_mpi_core_random. */ + int ret = mbedtls_mpi_resize_clear(X, N->n); + if (ret != 0) { + return ret; + } + + return mbedtls_mpi_core_random(X->p, min, N->p, X->n, f_rng, p_rng); +} + +/* + * Modular inverse: X = A^-1 mod N (HAC 14.61 / 14.64) + */ +int mbedtls_mpi_inv_mod(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; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(A != NULL); + MPI_VALIDATE_RET(N != NULL); + + if (mbedtls_mpi_cmp_int(N, 1) <= 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + 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_gcd(&G, A, N)); + + 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)); + + 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)); + + do { + while ((TU.p[0] & 1) == 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TU, 1)); + + 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)); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&U1, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&U2, 1)); + } + + while ((TV.p[0] & 1) == 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&TV, 1)); + + 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)); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&V1, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&V2, 1)); + } + + 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); + + while (mbedtls_mpi_cmp_int(&V1, 0) < 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&V1, &V1, N)); + } + + while (mbedtls_mpi_cmp_mpi(&V1, N) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&V1, &V1, 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); + + return ret; +} + +#if defined(MBEDTLS_GENPRIME) + +static const int small_prime[] = +{ + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251, 257, 263, 269, + 271, 277, 281, 283, 293, 307, 311, 313, + 317, 331, 337, 347, 349, 353, 359, 367, + 373, 379, 383, 389, 397, 401, 409, 419, + 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, + 521, 523, 541, 547, 557, 563, 569, 571, + 577, 587, 593, 599, 601, 607, 613, 617, + 619, 631, 641, 643, 647, 653, 659, 661, + 673, 677, 683, 691, 701, 709, 719, 727, + 733, 739, 743, 751, 757, 761, 769, 773, + 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, + 887, 907, 911, 919, 929, 937, 941, 947, + 953, 967, 971, 977, 983, 991, 997, -103 +}; + +/* + * Small divisors test (X must be positive) + * + * Return values: + * 0: no small factor (possible prime, more tests needed) + * 1: certain prime + * MBEDTLS_ERR_MPI_NOT_ACCEPTABLE: certain non-prime + * other negative: error + */ +static int mpi_check_small_factors(const mbedtls_mpi *X) +{ + int ret = 0; + size_t i; + mbedtls_mpi_uint r; + + if ((X->p[0] & 1) == 0) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + + for (i = 0; small_prime[i] > 0; i++) { + if (mbedtls_mpi_cmp_int(X, small_prime[i]) <= 0) { + return 1; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_int(&r, X, small_prime[i])); + + if (r == 0) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + } + +cleanup: + return ret; +} + +/* + * Miller-Rabin pseudo-primality test (HAC 4.24) + */ +static int mpi_miller_rabin(const mbedtls_mpi *X, size_t rounds, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret, count; + size_t i, j, k, s; + mbedtls_mpi W, R, T, A, RR; + + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(f_rng != NULL); + + mbedtls_mpi_init(&W); mbedtls_mpi_init(&R); + mbedtls_mpi_init(&T); mbedtls_mpi_init(&A); + mbedtls_mpi_init(&RR); + + /* + * W = |X| - 1 + * R = W >> lsb( W ) + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&W, X, 1)); + s = mbedtls_mpi_lsb(&W); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&R, &W)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&R, s)); + + for (i = 0; i < rounds; i++) { + /* + * pick a random A, 1 < A < |X| - 1 + */ + count = 0; + do { + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&A, X->n * ciL, f_rng, p_rng)); + + j = mbedtls_mpi_bitlen(&A); + k = mbedtls_mpi_bitlen(&W); + if (j > k) { + A.p[A.n - 1] &= ((mbedtls_mpi_uint) 1 << (k - (A.n - 1) * biL - 1)) - 1; + } + + if (count++ > 30) { + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + } while (mbedtls_mpi_cmp_mpi(&A, &W) >= 0 || + mbedtls_mpi_cmp_int(&A, 1) <= 0); + + /* + * A = A^R mod |X| + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&A, &A, &R, X, &RR)); + + if (mbedtls_mpi_cmp_mpi(&A, &W) == 0 || + mbedtls_mpi_cmp_int(&A, 1) == 0) { + continue; + } + + j = 1; + while (j < s && mbedtls_mpi_cmp_mpi(&A, &W) != 0) { + /* + * A = A * A mod |X| + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&T, &A, &A)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&A, &T, X)); + + if (mbedtls_mpi_cmp_int(&A, 1) == 0) { + break; + } + + j++; + } + + /* + * not prime if A != |X| - 1 or A == 1 + */ + if (mbedtls_mpi_cmp_mpi(&A, &W) != 0 || + mbedtls_mpi_cmp_int(&A, 1) == 0) { + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + break; + } + } + +cleanup: + mbedtls_mpi_free(&W); mbedtls_mpi_free(&R); + mbedtls_mpi_free(&T); mbedtls_mpi_free(&A); + mbedtls_mpi_free(&RR); + + return ret; +} + +/* + * Pseudo-primality test: small factors, then Miller-Rabin + */ +int mbedtls_mpi_is_prime_ext(const mbedtls_mpi *X, int rounds, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi XX; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(f_rng != NULL); + + XX.s = 1; + XX.n = X->n; + XX.p = X->p; + + if (mbedtls_mpi_cmp_int(&XX, 0) == 0 || + mbedtls_mpi_cmp_int(&XX, 1) == 0) { + return MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + } + + if (mbedtls_mpi_cmp_int(&XX, 2) == 0) { + return 0; + } + + if ((ret = mpi_check_small_factors(&XX)) != 0) { + if (ret == 1) { + return 0; + } + + return ret; + } + + return mpi_miller_rabin(&XX, rounds, f_rng, p_rng); +} + +/* + * Prime number generation + * + * To generate an RSA key in a way recommended by FIPS 186-4, both primes must + * be either 1024 bits or 1536 bits long, and flags must contain + * MBEDTLS_MPI_GEN_PRIME_FLAG_LOW_ERR. + */ +int mbedtls_mpi_gen_prime(mbedtls_mpi *X, size_t nbits, int flags, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ +#ifdef MBEDTLS_HAVE_INT64 +// ceil(2^63.5) +#define CEIL_MAXUINT_DIV_SQRT2 0xb504f333f9de6485ULL +#else +// ceil(2^31.5) +#define CEIL_MAXUINT_DIV_SQRT2 0xb504f334U +#endif + int ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + size_t k, n; + int rounds; + mbedtls_mpi_uint r; + mbedtls_mpi Y; + + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(f_rng != NULL); + + if (nbits < 3 || nbits > MBEDTLS_MPI_MAX_BITS) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + mbedtls_mpi_init(&Y); + + n = BITS_TO_LIMBS(nbits); + + if ((flags & MBEDTLS_MPI_GEN_PRIME_FLAG_LOW_ERR) == 0) { + /* + * 2^-80 error probability, number of rounds chosen per HAC, table 4.4 + */ + rounds = ((nbits >= 1300) ? 2 : (nbits >= 850) ? 3 : + (nbits >= 650) ? 4 : (nbits >= 350) ? 8 : + (nbits >= 250) ? 12 : (nbits >= 150) ? 18 : 27); + } else { + /* + * 2^-100 error probability, number of rounds computed based on HAC, + * fact 4.48 + */ + rounds = ((nbits >= 1450) ? 4 : (nbits >= 1150) ? 5 : + (nbits >= 1000) ? 6 : (nbits >= 850) ? 7 : + (nbits >= 750) ? 8 : (nbits >= 500) ? 13 : + (nbits >= 250) ? 28 : (nbits >= 150) ? 40 : 51); + } + + while (1) { + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(X, n * ciL, f_rng, p_rng)); + /* make sure generated number is at least (nbits-1)+0.5 bits (FIPS 186-4 §B.3.3 steps 4.4, 5.5) */ + if (X->p[n-1] < CEIL_MAXUINT_DIV_SQRT2) { + continue; + } + + k = n * biL; + if (k > nbits) { + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(X, k - nbits)); + } + X->p[0] |= 1; + + if ((flags & MBEDTLS_MPI_GEN_PRIME_FLAG_DH) == 0) { + ret = mbedtls_mpi_is_prime_ext(X, rounds, f_rng, p_rng); + + if (ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { + goto cleanup; + } + } else { + /* + * A necessary condition for Y and X = 2Y + 1 to be prime + * is X = 2 mod 3 (which is equivalent to Y = 2 mod 3). + * Make sure it is satisfied, while keeping X = 3 mod 4 + */ + + X->p[0] |= 2; + + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_int(&r, X, 3)); + if (r == 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(X, X, 8)); + } else if (r == 1) { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(X, X, 4)); + } + + /* Set Y = (X-1) / 2, which is X / 2 because X is odd */ + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&Y, X)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&Y, 1)); + + while (1) { + /* + * First, check small factors for X and Y + * before doing Miller-Rabin on any of them + */ + if ((ret = mpi_check_small_factors(X)) == 0 && + (ret = mpi_check_small_factors(&Y)) == 0 && + (ret = mpi_miller_rabin(X, rounds, f_rng, p_rng)) + == 0 && + (ret = mpi_miller_rabin(&Y, rounds, f_rng, p_rng)) + == 0) { + goto cleanup; + } + + if (ret != MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { + goto cleanup; + } + + /* + * Next candidates. We want to preserve Y = (X-1) / 2 and + * Y = 1 mod 2 and Y = 2 mod 3 (eq X = 3 mod 4 and X = 2 mod 3) + * so up Y by 6 and X by 12. + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(X, X, 12)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&Y, &Y, 6)); + } + } + } + +cleanup: + + mbedtls_mpi_free(&Y); + + return ret; +} + +#endif /* MBEDTLS_GENPRIME */ + +#if defined(MBEDTLS_SELF_TEST) + +#define GCD_PAIR_COUNT 3 + +static const int gcd_pairs[GCD_PAIR_COUNT][3] = +{ + { 693, 609, 21 }, + { 1764, 868, 28 }, + { 768454923, 542167814, 1 } +}; + +/* + * Checkup routine + */ +int mbedtls_mpi_self_test(int verbose) +{ + int ret, i; + mbedtls_mpi A, E, N, X, Y, U, V; + + mbedtls_mpi_init(&A); mbedtls_mpi_init(&E); mbedtls_mpi_init(&N); mbedtls_mpi_init(&X); + mbedtls_mpi_init(&Y); mbedtls_mpi_init(&U); mbedtls_mpi_init(&V); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&A, 16, + "EFE021C2645FD1DC586E69184AF4A31E" \ + "D5F53E93B5F123FA41680867BA110131" \ + "944FE7952E2517337780CB0DB80E61AA" \ + "E7C8DDC6C5C6AADEB34EB38A2F40D5E6")); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&E, 16, + "B2E7EFD37075B9F03FF989C7C5051C20" \ + "34D2A323810251127E7BF8625A4F49A5" \ + "F3E27F4DA8BD59C47D6DAABA4C8127BD" \ + "5B5C25763222FEFCCFC38B832366C29E")); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 16, + "0066A198186C18C10B2F5ED9B522752A" \ + "9830B69916E535C8F047518A889A43A5" \ + "94B6BED27A168D31D4A52F88925AA8F5")); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&X, &A, &N)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&U, 16, + "602AB7ECA597A3D6B56FF9829A5E8B85" \ + "9E857EA95A03512E2BAE7391688D264A" \ + "A5663B0341DB9CCFD2C4C5F421FEC814" \ + "8001B72E848A38CAE1C65F78E56ABDEF" \ + "E12D3C039B8A02D6BE593F0BBBDA56F1" \ + "ECF677152EF804370C1A305CAF3B5BF1" \ + "30879B56C61DE584A0F53A2447A51E")); + + if (verbose != 0) { + mbedtls_printf(" MPI test #1 (mul_mpi): "); + } + + if (mbedtls_mpi_cmp_mpi(&X, &U) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(&X, &Y, &A, &N)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&U, 16, + "256567336059E52CAE22925474705F39A94")); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&V, 16, + "6613F26162223DF488E9CD48CC132C7A" \ + "0AC93C701B001B092E4E5B9F73BCD27B" \ + "9EE50D0657C77F374E903CDFA4C642")); + + if (verbose != 0) { + mbedtls_printf(" MPI test #2 (div_mpi): "); + } + + if (mbedtls_mpi_cmp_mpi(&X, &U) != 0 || + mbedtls_mpi_cmp_mpi(&Y, &V) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&X, &A, &E, &N, NULL)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&U, 16, + "36E139AEA55215609D2816998ED020BB" \ + "BD96C37890F65171D948E9BC7CBAA4D9" \ + "325D24D6A3C12710F10A09FA08AB87")); + + if (verbose != 0) { + mbedtls_printf(" MPI test #3 (exp_mod): "); + } + + if (mbedtls_mpi_cmp_mpi(&X, &U) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&X, &A, &N)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&U, 16, + "003A0AAEDD7E784FC07D8F9EC6E3BFD5" \ + "C3DBA76456363A10869622EAC2DD84EC" \ + "C5B8A74DAC4D09E03B5E0BE779F2DF61")); + + if (verbose != 0) { + mbedtls_printf(" MPI test #4 (inv_mod): "); + } + + if (mbedtls_mpi_cmp_mpi(&X, &U) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + if (verbose != 0) { + mbedtls_printf(" MPI test #5 (simple gcd): "); + } + + for (i = 0; i < GCD_PAIR_COUNT; i++) { + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&X, gcd_pairs[i][0])); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&Y, gcd_pairs[i][1])); + + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(&A, &X, &Y)); + + if (mbedtls_mpi_cmp_int(&A, gcd_pairs[i][2]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed at %d\n", i); + } + + ret = 1; + goto cleanup; + } + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + +cleanup: + + if (ret != 0 && verbose != 0) { + mbedtls_printf("Unexpected error, return code = %08X\n", (unsigned int) ret); + } + + mbedtls_mpi_free(&A); mbedtls_mpi_free(&E); mbedtls_mpi_free(&N); mbedtls_mpi_free(&X); + mbedtls_mpi_free(&Y); mbedtls_mpi_free(&U); mbedtls_mpi_free(&V); + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_BIGNUM_C */ diff --git a/r5dev/thirdparty/mbedtls/bignum_core.c b/r5dev/thirdparty/mbedtls/bignum_core.c new file mode 100644 index 00000000..e50f043c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum_core.c @@ -0,0 +1,871 @@ +/* + * Core bignum functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) + +#include + +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" +#include "constant_time_internal.h" + +#include "mbedtls/platform.h" + +#include "bignum_core.h" +#include "bn_mul.h" +#include "constant_time_internal.h" + +size_t mbedtls_mpi_core_clz(mbedtls_mpi_uint a) +{ + size_t j; + mbedtls_mpi_uint mask = (mbedtls_mpi_uint) 1 << (biL - 1); + + for (j = 0; j < biL; j++) { + if (a & mask) { + break; + } + + mask >>= 1; + } + + return j; +} + +size_t mbedtls_mpi_core_bitlen(const mbedtls_mpi_uint *A, size_t A_limbs) +{ + size_t i, j; + + if (A_limbs == 0) { + return 0; + } + + for (i = A_limbs - 1; i > 0; i--) { + if (A[i] != 0) { + break; + } + } + + j = biL - mbedtls_mpi_core_clz(A[i]); + + return (i * biL) + j; +} + +/* Convert a big-endian byte array aligned to the size of mbedtls_mpi_uint + * into the storage form used by mbedtls_mpi. */ +static mbedtls_mpi_uint mpi_bigendian_to_host_c(mbedtls_mpi_uint a) +{ + uint8_t i; + unsigned char *a_ptr; + mbedtls_mpi_uint tmp = 0; + + for (i = 0, a_ptr = (unsigned char *) &a; i < ciL; i++, a_ptr++) { + tmp <<= CHAR_BIT; + tmp |= (mbedtls_mpi_uint) *a_ptr; + } + + return tmp; +} + +static mbedtls_mpi_uint mpi_bigendian_to_host(mbedtls_mpi_uint a) +{ + if (MBEDTLS_IS_BIG_ENDIAN) { + /* Nothing to do on bigendian systems. */ + return a; + } else { + switch (sizeof(mbedtls_mpi_uint)) { + case 4: + return (mbedtls_mpi_uint) MBEDTLS_BSWAP32((uint32_t) a); + case 8: + return (mbedtls_mpi_uint) MBEDTLS_BSWAP64((uint64_t) a); + } + + /* Fall back to C-based reordering if we don't know the byte order + * or we couldn't use a compiler-specific builtin. */ + return mpi_bigendian_to_host_c(a); + } +} + +void mbedtls_mpi_core_bigendian_to_host(mbedtls_mpi_uint *A, + size_t A_limbs) +{ + mbedtls_mpi_uint *cur_limb_left; + mbedtls_mpi_uint *cur_limb_right; + if (A_limbs == 0) { + return; + } + + /* + * Traverse limbs and + * - adapt byte-order in each limb + * - swap the limbs themselves. + * For that, simultaneously traverse the limbs from left to right + * and from right to left, as long as the left index is not bigger + * than the right index (it's not a problem if limbs is odd and the + * indices coincide in the last iteration). + */ + for (cur_limb_left = A, cur_limb_right = A + (A_limbs - 1); + cur_limb_left <= cur_limb_right; + cur_limb_left++, cur_limb_right--) { + mbedtls_mpi_uint tmp; + /* Note that if cur_limb_left == cur_limb_right, + * this code effectively swaps the bytes only once. */ + tmp = mpi_bigendian_to_host(*cur_limb_left); + *cur_limb_left = mpi_bigendian_to_host(*cur_limb_right); + *cur_limb_right = tmp; + } +} + +/* Whether min <= A, in constant time. + * A_limbs must be at least 1. */ +unsigned mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min, + const mbedtls_mpi_uint *A, + size_t A_limbs) +{ + /* min <= least significant limb? */ + unsigned min_le_lsl = 1 ^ mbedtls_ct_mpi_uint_lt(A[0], min); + + /* limbs other than the least significant one are all zero? */ + mbedtls_mpi_uint msll_mask = 0; + for (size_t i = 1; i < A_limbs; i++) { + msll_mask |= A[i]; + } + /* The most significant limbs of A are not all zero iff msll_mask != 0. */ + unsigned msll_nonzero = mbedtls_ct_mpi_uint_mask(msll_mask) & 1; + + /* min <= A iff the lowest limb of A is >= min or the other limbs + * are not all zero. */ + return min_le_lsl | msll_nonzero; +} + +void mbedtls_mpi_core_cond_assign(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + size_t limbs, + unsigned char assign) +{ + if (X == A) { + return; + } + + mbedtls_ct_mpi_uint_cond_assign(limbs, X, A, assign); +} + +void mbedtls_mpi_core_cond_swap(mbedtls_mpi_uint *X, + mbedtls_mpi_uint *Y, + size_t limbs, + unsigned char swap) +{ + if (X == Y) { + return; + } + + /* all-bits 1 if swap is 1, all-bits 0 if swap is 0 */ + mbedtls_mpi_uint limb_mask = mbedtls_ct_mpi_uint_mask(swap); + + for (size_t i = 0; i < limbs; i++) { + mbedtls_mpi_uint tmp = X[i]; + X[i] = (X[i] & ~limb_mask) | (Y[i] & limb_mask); + Y[i] = (Y[i] & ~limb_mask) | (tmp & limb_mask); + } +} + +int mbedtls_mpi_core_read_le(mbedtls_mpi_uint *X, + size_t X_limbs, + const unsigned char *input, + size_t input_length) +{ + const size_t limbs = CHARS_TO_LIMBS(input_length); + + if (X_limbs < limbs) { + return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL; + } + + if (X != NULL) { + memset(X, 0, X_limbs * ciL); + + for (size_t i = 0; i < input_length; i++) { + size_t offset = ((i % ciL) << 3); + X[i / ciL] |= ((mbedtls_mpi_uint) input[i]) << offset; + } + } + + return 0; +} + +int mbedtls_mpi_core_read_be(mbedtls_mpi_uint *X, + size_t X_limbs, + const unsigned char *input, + size_t input_length) +{ + const size_t limbs = CHARS_TO_LIMBS(input_length); + + if (X_limbs < limbs) { + return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL; + } + + /* If X_limbs is 0, input_length must also be 0 (from previous test). + * Nothing to do. */ + if (X_limbs == 0) { + return 0; + } + + memset(X, 0, X_limbs * ciL); + + /* memcpy() with (NULL, 0) is undefined behaviour */ + if (input_length != 0) { + size_t overhead = (X_limbs * ciL) - input_length; + unsigned char *Xp = (unsigned char *) X; + memcpy(Xp + overhead, input, input_length); + } + + mbedtls_mpi_core_bigendian_to_host(X, X_limbs); + + return 0; +} + +int mbedtls_mpi_core_write_le(const mbedtls_mpi_uint *A, + size_t A_limbs, + unsigned char *output, + size_t output_length) +{ + size_t stored_bytes = A_limbs * ciL; + size_t bytes_to_copy; + + if (stored_bytes < output_length) { + bytes_to_copy = stored_bytes; + } else { + bytes_to_copy = output_length; + + /* The output buffer is smaller than the allocated size of A. + * However A may fit if its leading bytes are zero. */ + for (size_t i = bytes_to_copy; i < stored_bytes; i++) { + if (GET_BYTE(A, i) != 0) { + return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL; + } + } + } + + for (size_t i = 0; i < bytes_to_copy; i++) { + output[i] = GET_BYTE(A, i); + } + + if (stored_bytes < output_length) { + /* Write trailing 0 bytes */ + memset(output + stored_bytes, 0, output_length - stored_bytes); + } + + return 0; +} + +int mbedtls_mpi_core_write_be(const mbedtls_mpi_uint *X, + size_t X_limbs, + unsigned char *output, + size_t output_length) +{ + size_t stored_bytes; + size_t bytes_to_copy; + unsigned char *p; + + stored_bytes = X_limbs * ciL; + + if (stored_bytes < output_length) { + /* There is enough space in the output buffer. Write initial + * null bytes and record the position at which to start + * writing the significant bytes. In this case, the execution + * trace of this function does not depend on the value of the + * number. */ + bytes_to_copy = stored_bytes; + p = output + output_length - stored_bytes; + memset(output, 0, output_length - stored_bytes); + } else { + /* The output buffer is smaller than the allocated size of X. + * However X may fit if its leading bytes are zero. */ + bytes_to_copy = output_length; + p = output; + for (size_t i = bytes_to_copy; i < stored_bytes; i++) { + if (GET_BYTE(X, i) != 0) { + return MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL; + } + } + } + + for (size_t i = 0; i < bytes_to_copy; i++) { + p[bytes_to_copy - i - 1] = GET_BYTE(X, i); + } + + return 0; +} + +void mbedtls_mpi_core_shift_r(mbedtls_mpi_uint *X, size_t limbs, + size_t count) +{ + size_t i, v0, v1; + mbedtls_mpi_uint r0 = 0, r1; + + v0 = count / biL; + v1 = count & (biL - 1); + + if (v0 > limbs || (v0 == limbs && v1 > 0)) { + memset(X, 0, limbs * ciL); + return; + } + + /* + * shift by count / limb_size + */ + if (v0 > 0) { + for (i = 0; i < limbs - v0; i++) { + X[i] = X[i + v0]; + } + + for (; i < limbs; i++) { + X[i] = 0; + } + } + + /* + * shift by count % limb_size + */ + if (v1 > 0) { + for (i = limbs; i > 0; i--) { + r1 = X[i - 1] << (biL - v1); + X[i - 1] >>= v1; + X[i - 1] |= r0; + r0 = r1; + } + } +} + +mbedtls_mpi_uint mbedtls_mpi_core_add(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + size_t limbs) +{ + mbedtls_mpi_uint c = 0; + + for (size_t i = 0; i < limbs; i++) { + mbedtls_mpi_uint t = c + A[i]; + c = (t < A[i]); + t += B[i]; + c += (t < B[i]); + X[i] = t; + } + + return c; +} + +mbedtls_mpi_uint mbedtls_mpi_core_add_if(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + size_t limbs, + unsigned cond) +{ + mbedtls_mpi_uint c = 0; + + /* all-bits 0 if cond is 0, all-bits 1 if cond is non-0 */ + const mbedtls_mpi_uint mask = mbedtls_ct_mpi_uint_mask(cond); + + for (size_t i = 0; i < limbs; i++) { + mbedtls_mpi_uint add = mask & A[i]; + mbedtls_mpi_uint t = c + X[i]; + c = (t < X[i]); + t += add; + c += (t < add); + X[i] = t; + } + + return c; +} + +mbedtls_mpi_uint mbedtls_mpi_core_sub(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + size_t limbs) +{ + mbedtls_mpi_uint c = 0; + + for (size_t i = 0; i < limbs; i++) { + mbedtls_mpi_uint z = (A[i] < c); + mbedtls_mpi_uint t = A[i] - c; + c = (t < B[i]) + z; + X[i] = t - B[i]; + } + + return c; +} + +mbedtls_mpi_uint mbedtls_mpi_core_mla(mbedtls_mpi_uint *d, size_t d_len, + const mbedtls_mpi_uint *s, size_t s_len, + mbedtls_mpi_uint b) +{ + mbedtls_mpi_uint c = 0; /* carry */ + /* + * It is a documented precondition of this function that d_len >= s_len. + * If that's not the case, we swap these round: this turns what would be + * a buffer overflow into an incorrect result. + */ + if (d_len < s_len) { + s_len = d_len; + } + size_t excess_len = d_len - s_len; + size_t steps_x8 = s_len / 8; + size_t steps_x1 = s_len & 7; + + while (steps_x8--) { + MULADDC_X8_INIT + MULADDC_X8_CORE + MULADDC_X8_STOP + } + + while (steps_x1--) { + MULADDC_X1_INIT + MULADDC_X1_CORE + MULADDC_X1_STOP + } + + while (excess_len--) { + *d += c; + c = (*d < c); + d++; + } + + return c; +} + +/* + * Fast Montgomery initialization (thanks to Tom St Denis). + */ +mbedtls_mpi_uint mbedtls_mpi_core_montmul_init(const mbedtls_mpi_uint *N) +{ + mbedtls_mpi_uint x = N[0]; + + x += ((N[0] + 2) & 4) << 1; + + for (unsigned int i = biL; i >= 8; i /= 2) { + x *= (2 - (N[0] * x)); + } + + return ~x + 1; +} + +void mbedtls_mpi_core_montmul(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + size_t B_limbs, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + mbedtls_mpi_uint mm, + mbedtls_mpi_uint *T) +{ + memset(T, 0, (2 * AN_limbs + 1) * ciL); + + for (size_t i = 0; i < AN_limbs; i++) { + /* T = (T + u0*B + u1*N) / 2^biL */ + mbedtls_mpi_uint u0 = A[i]; + mbedtls_mpi_uint u1 = (T[0] + u0 * B[0]) * mm; + + (void) mbedtls_mpi_core_mla(T, AN_limbs + 2, B, B_limbs, u0); + (void) mbedtls_mpi_core_mla(T, AN_limbs + 2, N, AN_limbs, u1); + + T++; + } + + /* + * The result we want is (T >= N) ? T - N : T. + * + * For better constant-time properties in this function, we always do the + * subtraction, with the result in X. + * + * We also look to see if there was any carry in the final additions in the + * loop above. + */ + + mbedtls_mpi_uint carry = T[AN_limbs]; + mbedtls_mpi_uint borrow = mbedtls_mpi_core_sub(X, T, N, AN_limbs); + + /* + * Using R as the Montgomery radix (auxiliary modulus) i.e. 2^(biL*AN_limbs): + * + * T can be in one of 3 ranges: + * + * 1) T < N : (carry, borrow) = (0, 1): we want T + * 2) N <= T < R : (carry, borrow) = (0, 0): we want X + * 3) T >= R : (carry, borrow) = (1, 1): we want X + * + * and (carry, borrow) = (1, 0) can't happen. + * + * So the correct return value is already in X if (carry ^ borrow) = 0, + * but is in (the lower AN_limbs limbs of) T if (carry ^ borrow) = 1. + */ + mbedtls_ct_mpi_uint_cond_assign(AN_limbs, X, T, (unsigned char) (carry ^ borrow)); +} + +int mbedtls_mpi_core_get_mont_r2_unsafe(mbedtls_mpi *X, + const mbedtls_mpi *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(X, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(X, N->n * 2 * biL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(X, X, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shrink(X, N->n)); + +cleanup: + return ret; +} + +MBEDTLS_STATIC_TESTABLE +void mbedtls_mpi_core_ct_uint_table_lookup(mbedtls_mpi_uint *dest, + const mbedtls_mpi_uint *table, + size_t limbs, + size_t count, + size_t index) +{ + for (size_t i = 0; i < count; i++, table += limbs) { + unsigned char assign = mbedtls_ct_size_bool_eq(i, index); + mbedtls_mpi_core_cond_assign(dest, table, limbs, assign); + } +} + +/* Fill X with n_bytes random bytes. + * X must already have room for those bytes. + * The ordering of the bytes returned from the RNG is suitable for + * deterministic ECDSA (see RFC 6979 §3.3 and the specification of + * mbedtls_mpi_core_random()). + */ +int mbedtls_mpi_core_fill_random( + mbedtls_mpi_uint *X, size_t X_limbs, + size_t n_bytes, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const size_t limbs = CHARS_TO_LIMBS(n_bytes); + const size_t overhead = (limbs * ciL) - n_bytes; + + if (X_limbs < limbs) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + memset(X, 0, overhead); + memset((unsigned char *) X + limbs * ciL, 0, (X_limbs - limbs) * ciL); + MBEDTLS_MPI_CHK(f_rng(p_rng, (unsigned char *) X + overhead, n_bytes)); + mbedtls_mpi_core_bigendian_to_host(X, limbs); + +cleanup: + return ret; +} + +int mbedtls_mpi_core_random(mbedtls_mpi_uint *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_uint *N, + size_t limbs, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + unsigned ge_lower = 1, lt_upper = 0; + size_t n_bits = mbedtls_mpi_core_bitlen(N, limbs); + size_t n_bytes = (n_bits + 7) / 8; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * When min == 0, each try has at worst a probability 1/2 of failing + * (the msb has a probability 1/2 of being 0, and then the result will + * be < N), so after 30 tries failure probability is a most 2**(-30). + * + * When N is just below a power of 2, as is the case when generating + * a random scalar on most elliptic curves, 1 try is enough with + * overwhelming probability. When N is just above a power of 2, + * as when generating a random scalar on secp224k1, each try has + * a probability of failing that is almost 1/2. + * + * The probabilities are almost the same if min is nonzero but negligible + * compared to N. This is always the case when N is crypto-sized, but + * it's convenient to support small N for testing purposes. When N + * is small, use a higher repeat count, otherwise the probability of + * failure is macroscopic. + */ + int count = (n_bytes > 4 ? 30 : 250); + + /* + * Match the procedure given in RFC 6979 §3.3 (deterministic ECDSA) + * when f_rng is a suitably parametrized instance of HMAC_DRBG: + * - use the same byte ordering; + * - keep the leftmost n_bits bits of the generated octet string; + * - try until result is in the desired range. + * This also avoids any bias, which is especially important for ECDSA. + */ + do { + MBEDTLS_MPI_CHK(mbedtls_mpi_core_fill_random(X, limbs, + n_bytes, + f_rng, p_rng)); + mbedtls_mpi_core_shift_r(X, limbs, 8 * n_bytes - n_bits); + + if (--count == 0) { + ret = MBEDTLS_ERR_MPI_NOT_ACCEPTABLE; + goto cleanup; + } + + ge_lower = mbedtls_mpi_core_uint_le_mpi(min, X, limbs); + lt_upper = mbedtls_mpi_core_lt_ct(X, N, limbs); + } while (ge_lower == 0 || lt_upper == 0); + +cleanup: + return ret; +} + +/* BEGIN MERGE SLOT 1 */ + +static size_t exp_mod_get_window_size(size_t Ebits) +{ + size_t wsize = (Ebits > 671) ? 6 : (Ebits > 239) ? 5 : + (Ebits > 79) ? 4 : 1; + +#if (MBEDTLS_MPI_WINDOW_SIZE < 6) + if (wsize > MBEDTLS_MPI_WINDOW_SIZE) { + wsize = MBEDTLS_MPI_WINDOW_SIZE; + } +#endif + + return wsize; +} + +size_t mbedtls_mpi_core_exp_mod_working_limbs(size_t AN_limbs, size_t E_limbs) +{ + const size_t wsize = exp_mod_get_window_size(E_limbs * biL); + const size_t welem = ((size_t) 1) << wsize; + + /* How big does each part of the working memory pool need to be? */ + const size_t table_limbs = welem * AN_limbs; + const size_t select_limbs = AN_limbs; + const size_t temp_limbs = 2 * AN_limbs + 1; + + return table_limbs + select_limbs + temp_limbs; +} + +static void exp_mod_precompute_window(const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + mbedtls_mpi_uint mm, + const mbedtls_mpi_uint *RR, + size_t welem, + mbedtls_mpi_uint *Wtable, + mbedtls_mpi_uint *temp) +{ + /* W[0] = 1 (in Montgomery presentation) */ + memset(Wtable, 0, AN_limbs * ciL); + Wtable[0] = 1; + mbedtls_mpi_core_montmul(Wtable, Wtable, RR, AN_limbs, N, AN_limbs, mm, temp); + + /* W[1] = A (already in Montgomery presentation) */ + mbedtls_mpi_uint *W1 = Wtable + AN_limbs; + memcpy(W1, A, AN_limbs * ciL); + + /* W[i+1] = W[i] * W[1], i >= 2 */ + mbedtls_mpi_uint *Wprev = W1; + for (size_t i = 2; i < welem; i++) { + mbedtls_mpi_uint *Wcur = Wprev + AN_limbs; + mbedtls_mpi_core_montmul(Wcur, Wprev, W1, AN_limbs, N, AN_limbs, mm, temp); + Wprev = Wcur; + } +} + +/* Exponentiation: X := A^E mod N. + * + * A must already be in Montgomery form. + * + * As in other bignum functions, assume that AN_limbs and E_limbs are nonzero. + * + * RR must contain 2^{2*biL} mod N. + * + * The algorithm is a variant of Left-to-right k-ary exponentiation: HAC 14.82 + * (The difference is that the body in our loop processes a single bit instead + * of a full window.) + */ +void mbedtls_mpi_core_exp_mod(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + const mbedtls_mpi_uint *E, + size_t E_limbs, + const mbedtls_mpi_uint *RR, + mbedtls_mpi_uint *T) +{ + const size_t wsize = exp_mod_get_window_size(E_limbs * biL); + const size_t welem = ((size_t) 1) << wsize; + + /* This is how we will use the temporary storage T, which must have space + * for table_limbs, select_limbs and (2 * AN_limbs + 1) for montmul. */ + const size_t table_limbs = welem * AN_limbs; + const size_t select_limbs = AN_limbs; + + /* Pointers to specific parts of the temporary working memory pool */ + mbedtls_mpi_uint *const Wtable = T; + mbedtls_mpi_uint *const Wselect = Wtable + table_limbs; + mbedtls_mpi_uint *const temp = Wselect + select_limbs; + + /* + * Window precomputation + */ + + const mbedtls_mpi_uint mm = mbedtls_mpi_core_montmul_init(N); + + /* Set Wtable[i] = A^(2^i) (in Montgomery representation) */ + exp_mod_precompute_window(A, N, AN_limbs, + mm, RR, + welem, Wtable, temp); + + /* + * Fixed window exponentiation + */ + + /* X = 1 (in Montgomery presentation) initially */ + memcpy(X, Wtable, AN_limbs * ciL); + + /* We'll process the bits of E from most significant + * (limb_index=E_limbs-1, E_bit_index=biL-1) to least significant + * (limb_index=0, E_bit_index=0). */ + size_t E_limb_index = E_limbs; + size_t E_bit_index = 0; + /* At any given time, window contains window_bits bits from E. + * window_bits can go up to wsize. */ + size_t window_bits = 0; + mbedtls_mpi_uint window = 0; + + do { + /* Square */ + mbedtls_mpi_core_montmul(X, X, X, AN_limbs, N, AN_limbs, mm, temp); + + /* Move to the next bit of the exponent */ + if (E_bit_index == 0) { + --E_limb_index; + E_bit_index = biL - 1; + } else { + --E_bit_index; + } + /* Insert next exponent bit into window */ + ++window_bits; + window <<= 1; + window |= (E[E_limb_index] >> E_bit_index) & 1; + + /* Clear window if it's full. Also clear the window at the end, + * when we've finished processing the exponent. */ + if (window_bits == wsize || + (E_bit_index == 0 && E_limb_index == 0)) { + /* Select Wtable[window] without leaking window through + * memory access patterns. */ + mbedtls_mpi_core_ct_uint_table_lookup(Wselect, Wtable, + AN_limbs, welem, window); + /* Multiply X by the selected element. */ + mbedtls_mpi_core_montmul(X, X, Wselect, AN_limbs, N, AN_limbs, mm, + temp); + window = 0; + window_bits = 0; + } + } while (!(E_bit_index == 0 && E_limb_index == 0)); +} + +/* END MERGE SLOT 1 */ + +/* BEGIN MERGE SLOT 2 */ + +/* END MERGE SLOT 2 */ + +/* BEGIN MERGE SLOT 3 */ + +mbedtls_mpi_uint mbedtls_mpi_core_sub_int(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + mbedtls_mpi_uint c, /* doubles as carry */ + size_t limbs) +{ + for (size_t i = 0; i < limbs; i++) { + mbedtls_mpi_uint s = A[i]; + mbedtls_mpi_uint t = s - c; + c = (t > s); + X[i] = t; + } + + return c; +} + +mbedtls_mpi_uint mbedtls_mpi_core_check_zero_ct(const mbedtls_mpi_uint *A, + size_t limbs) +{ + mbedtls_mpi_uint bits = 0; + + for (size_t i = 0; i < limbs; i++) { + bits |= A[i]; + } + + return bits; +} + +void mbedtls_mpi_core_to_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + mbedtls_mpi_uint mm, + const mbedtls_mpi_uint *rr, + mbedtls_mpi_uint *T) +{ + mbedtls_mpi_core_montmul(X, A, rr, AN_limbs, N, AN_limbs, mm, T); +} + +void mbedtls_mpi_core_from_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + mbedtls_mpi_uint mm, + mbedtls_mpi_uint *T) +{ + const mbedtls_mpi_uint Rinv = 1; /* 1/R in Mont. rep => 1 */ + + mbedtls_mpi_core_montmul(X, A, &Rinv, 1, N, AN_limbs, mm, T); +} + +/* END MERGE SLOT 3 */ + +/* BEGIN MERGE SLOT 4 */ + +/* END MERGE SLOT 4 */ + +/* BEGIN MERGE SLOT 5 */ + +/* END MERGE SLOT 5 */ + +/* BEGIN MERGE SLOT 6 */ + +/* END MERGE SLOT 6 */ + +/* BEGIN MERGE SLOT 7 */ + +/* END MERGE SLOT 7 */ + +/* BEGIN MERGE SLOT 8 */ + +/* END MERGE SLOT 8 */ + +/* BEGIN MERGE SLOT 9 */ + +/* END MERGE SLOT 9 */ + +/* BEGIN MERGE SLOT 10 */ + +/* END MERGE SLOT 10 */ + +#endif /* MBEDTLS_BIGNUM_C */ diff --git a/r5dev/thirdparty/mbedtls/bignum_core.h b/r5dev/thirdparty/mbedtls/bignum_core.h new file mode 100644 index 00000000..05bc923d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum_core.h @@ -0,0 +1,767 @@ +/** + * Core bignum functions + * + * This interface should only be used by the legacy bignum module (bignum.h) + * and the modular bignum modules (bignum_mod.c, bignum_mod_raw.c). All other + * modules should use the high-level modular bignum interface (bignum_mod.h) + * or the legacy bignum interface (bignum.h). + * + * This module is about processing non-negative integers with a fixed upper + * bound that's of the form 2^n-1 where n is a multiple of #biL. + * These can be thought of integers written in base 2^#biL with a fixed + * number of digits. Digits in this base are called *limbs*. + * Many operations treat these numbers as the principal representation of + * a number modulo 2^n or a smaller bound. + * + * The functions in this module obey the following conventions unless + * explicitly indicated otherwise: + * + * - **Overflow**: some functions indicate overflow from the range + * [0, 2^n-1] by returning carry parameters, while others operate + * modulo and so cannot overflow. This should be clear from the function + * documentation. + * - **Bignum parameters**: Bignums are passed as pointers to an array of + * limbs. A limb has the type #mbedtls_mpi_uint. Unless otherwise specified: + * - Bignum parameters called \p A, \p B, ... are inputs, and are + * not modified by the function. + * - For operations modulo some number, the modulus is called \p N + * and is input-only. + * - Bignum parameters called \p X, \p Y are outputs or input-output. + * The initial content of output-only parameters is ignored. + * - Some functions use different names that reflect traditional + * naming of operands of certain operations (e.g. + * divisor/dividend/quotient/remainder). + * - \p T is a temporary storage area. The initial content of such + * parameter is ignored and the final content is unspecified. + * - **Bignum sizes**: bignum sizes are always expressed in limbs. + * Most functions work on bignums of a given size and take a single + * \p limbs parameter that applies to all parameters that are limb arrays. + * All bignum sizes must be at least 1 and must be significantly less than + * #SIZE_MAX. The behavior if a size is 0 is undefined. The behavior if the + * total size of all parameters overflows #SIZE_MAX is undefined. + * - **Parameter ordering**: for bignum parameters, outputs come before inputs. + * Temporaries come last. + * - **Aliasing**: in general, output bignums may be aliased to one or more + * inputs. As an exception, parameters that are documented as a modulus value + * may not be aliased to an output. Outputs may not be aliased to one another. + * Temporaries may not be aliased to any other parameter. + * - **Overlap**: apart from aliasing of limb array pointers (where two + * arguments are equal pointers), overlap is not supported and may result + * in undefined behavior. + * - **Error handling**: This is a low-level module. Functions generally do not + * try to protect against invalid arguments such as nonsensical sizes or + * null pointers. Note that some functions that operate on bignums of + * different sizes have constraints about their size, and violating those + * constraints may lead to buffer overflows. + * - **Modular representatives**: functions that operate modulo \p N expect + * all modular inputs to be in the range [0, \p N - 1] and guarantee outputs + * in the range [0, \p N - 1]. If an input is out of range, outputs are + * fully unspecified, though bignum values out of range should not cause + * buffer overflows (beware that this is not extensively tested). + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_BIGNUM_CORE_H +#define MBEDTLS_BIGNUM_CORE_H + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#define ciL (sizeof(mbedtls_mpi_uint)) /** chars in limb */ +#define biL (ciL << 3) /** bits in limb */ +#define biH (ciL << 2) /** half limb size */ + +/* + * Convert between bits/chars and number of limbs + * Divide first in order to avoid potential overflows + */ +#define BITS_TO_LIMBS(i) ((i) / biL + ((i) % biL != 0)) +#define CHARS_TO_LIMBS(i) ((i) / ciL + ((i) % ciL != 0)) +/* Get a specific byte, without range checks. */ +#define GET_BYTE(X, i) \ + (((X)[(i) / ciL] >> (((i) % ciL) * 8)) & 0xff) + +/** Count leading zero bits in a given integer. + * + * \param a Integer to count leading zero bits. + * + * \return The number of leading zero bits in \p a. + */ +size_t mbedtls_mpi_core_clz(mbedtls_mpi_uint a); + +/** Return the minimum number of bits required to represent the value held + * in the MPI. + * + * \note This function returns 0 if all the limbs of \p A are 0. + * + * \param[in] A The address of the MPI. + * \param A_limbs The number of limbs of \p A. + * + * \return The number of bits in \p A. + */ +size_t mbedtls_mpi_core_bitlen(const mbedtls_mpi_uint *A, size_t A_limbs); + +/** Convert a big-endian byte array aligned to the size of mbedtls_mpi_uint + * into the storage form used by mbedtls_mpi. + * + * \param[in,out] A The address of the MPI. + * \param A_limbs The number of limbs of \p A. + */ +void mbedtls_mpi_core_bigendian_to_host(mbedtls_mpi_uint *A, + size_t A_limbs); + +/** \brief Compare a machine integer with an MPI. + * + * This function operates in constant time with respect + * to the values of \p min and \p A. + * + * \param min A machine integer. + * \param[in] A An MPI. + * \param A_limbs The number of limbs of \p A. + * This must be at least 1. + * + * \return 1 if \p min is less than or equal to \p A, otherwise 0. + */ +unsigned mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min, + const mbedtls_mpi_uint *A, + size_t A_limbs); + +/** + * \brief Perform a safe conditional copy of an MPI which doesn't reveal + * whether assignment was done or not. + * + * \param[out] X The address of the destination MPI. + * This must be initialized. Must have enough limbs to + * store the full value of \p A. + * \param[in] A The address of the source MPI. This must be initialized. + * \param limbs The number of limbs of \p A. + * \param assign The condition deciding whether to perform the + * assignment or not. Must be either 0 or 1: + * * \c 1: Perform the assignment `X = A`. + * * \c 0: Keep the original value of \p X. + * + * \note This function avoids leaking any information about whether + * the assignment was done or not. + * + * \warning If \p assign is neither 0 nor 1, the result of this function + * is indeterminate, and the resulting value in \p X might be + * neither its original value nor the value in \p A. + */ +void mbedtls_mpi_core_cond_assign(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + size_t limbs, + unsigned char assign); + +/** + * \brief Perform a safe conditional swap of two MPIs which doesn't reveal + * whether the swap was done or not. + * + * \param[in,out] X The address of the first MPI. + * This must be initialized. + * \param[in,out] Y The address of the second MPI. + * This must be initialized. + * \param limbs The number of limbs of \p X and \p Y. + * \param swap The condition deciding whether to perform + * the swap or not. Must be either 0 or 1: + * * \c 1: Swap the values of \p X and \p Y. + * * \c 0: Keep the original values of \p X and \p Y. + * + * \note This function avoids leaking any information about whether + * the swap was done or not. + * + * \warning If \p swap is neither 0 nor 1, the result of this function + * is indeterminate, and both \p X and \p Y might end up with + * values different to either of the original ones. + */ +void mbedtls_mpi_core_cond_swap(mbedtls_mpi_uint *X, + mbedtls_mpi_uint *Y, + size_t limbs, + unsigned char swap); + +/** Import X from unsigned binary data, little-endian. + * + * The MPI needs to have enough limbs to store the full value (including any + * most significant zero bytes in the input). + * + * \param[out] X The address of the MPI. + * \param X_limbs The number of limbs of \p X. + * \param[in] input The input buffer to import from. + * \param input_length The length bytes of \p input. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p X isn't + * large enough to hold the value in \p input. + */ +int mbedtls_mpi_core_read_le(mbedtls_mpi_uint *X, + size_t X_limbs, + const unsigned char *input, + size_t input_length); + +/** Import X from unsigned binary data, big-endian. + * + * The MPI needs to have enough limbs to store the full value (including any + * most significant zero bytes in the input). + * + * \param[out] X The address of the MPI. + * May only be #NULL if \p X_limbs is 0 and \p input_length + * is 0. + * \param X_limbs The number of limbs of \p X. + * \param[in] input The input buffer to import from. + * May only be #NULL if \p input_length is 0. + * \param input_length The length in bytes of \p input. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p X isn't + * large enough to hold the value in \p input. + */ +int mbedtls_mpi_core_read_be(mbedtls_mpi_uint *X, + size_t X_limbs, + const unsigned char *input, + size_t input_length); + +/** Export A into unsigned binary data, little-endian. + * + * \note If \p output is shorter than \p A the export is still successful if the + * value held in \p A fits in the buffer (that is, if enough of the most + * significant bytes of \p A are 0). + * + * \param[in] A The address of the MPI. + * \param A_limbs The number of limbs of \p A. + * \param[out] output The output buffer to export to. + * \param output_length The length in bytes of \p output. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p output isn't + * large enough to hold the value of \p A. + */ +int mbedtls_mpi_core_write_le(const mbedtls_mpi_uint *A, + size_t A_limbs, + unsigned char *output, + size_t output_length); + +/** Export A into unsigned binary data, big-endian. + * + * \note If \p output is shorter than \p A the export is still successful if the + * value held in \p A fits in the buffer (that is, if enough of the most + * significant bytes of \p A are 0). + * + * \param[in] A The address of the MPI. + * \param A_limbs The number of limbs of \p A. + * \param[out] output The output buffer to export to. + * \param output_length The length in bytes of \p output. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p output isn't + * large enough to hold the value of \p A. + */ +int mbedtls_mpi_core_write_be(const mbedtls_mpi_uint *A, + size_t A_limbs, + unsigned char *output, + size_t output_length); + +/** \brief Shift an MPI right in place by a number of bits. + * + * Shifting by more bits than there are bit positions + * in \p X is valid and results in setting \p X to 0. + * + * This function's execution time depends on the value + * of \p count (and of course \p limbs). + * + * \param[in,out] X The number to shift. + * \param limbs The number of limbs of \p X. This must be at least 1. + * \param count The number of bits to shift by. + */ +void mbedtls_mpi_core_shift_r(mbedtls_mpi_uint *X, size_t limbs, + size_t count); + +/** + * \brief Add two fixed-size large unsigned integers, returning the carry. + * + * Calculates `A + B` where `A` and `B` have the same size. + * + * This function operates modulo `2^(biL*limbs)` and returns the carry + * (1 if there was a wraparound, and 0 otherwise). + * + * \p X may be aliased to \p A or \p B. + * + * \param[out] X The result of the addition. + * \param[in] A Little-endian presentation of the left operand. + * \param[in] B Little-endian presentation of the right operand. + * \param limbs Number of limbs of \p X, \p A and \p B. + * + * \return 1 if `A + B >= 2^(biL*limbs)`, 0 otherwise. + */ +mbedtls_mpi_uint mbedtls_mpi_core_add(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + size_t limbs); + +/** + * \brief Conditional addition of two fixed-size large unsigned integers, + * returning the carry. + * + * Functionally equivalent to + * + * ``` + * if( cond ) + * X += A; + * return carry; + * ``` + * + * This function operates modulo `2^(biL*limbs)`. + * + * \param[in,out] X The pointer to the (little-endian) array + * representing the bignum to accumulate onto. + * \param[in] A The pointer to the (little-endian) array + * representing the bignum to conditionally add + * to \p X. This may be aliased to \p X but may not + * overlap otherwise. + * \param limbs Number of limbs of \p X and \p A. + * \param cond Condition bit dictating whether addition should + * happen or not. This must be \c 0 or \c 1. + * + * \warning If \p cond is neither 0 nor 1, the result of this function + * is unspecified, and the resulting value in \p X might be + * neither its original value nor \p X + \p A. + * + * \return 1 if `X + cond * A >= 2^(biL*limbs)`, 0 otherwise. + */ +mbedtls_mpi_uint mbedtls_mpi_core_add_if(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + size_t limbs, + unsigned cond); + +/** + * \brief Subtract two fixed-size large unsigned integers, returning the borrow. + * + * Calculate `A - B` where \p A and \p B have the same size. + * This function operates modulo `2^(biL*limbs)` and returns the carry + * (1 if there was a wraparound, i.e. if `A < B`, and 0 otherwise). + * + * \p X may be aliased to \p A or \p B, or even both, but may not overlap + * either otherwise. + * + * \param[out] X The result of the subtraction. + * \param[in] A Little-endian presentation of left operand. + * \param[in] B Little-endian presentation of right operand. + * \param limbs Number of limbs of \p X, \p A and \p B. + * + * \return 1 if `A < B`. + * 0 if `A >= B`. + */ +mbedtls_mpi_uint mbedtls_mpi_core_sub(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + size_t limbs); + +/** + * \brief Perform a fixed-size multiply accumulate operation: X += b * A + * + * \p X may be aliased to \p A (when \p X_limbs == \p A_limbs), but may not + * otherwise overlap. + * + * This function operates modulo `2^(biL*X_limbs)`. + * + * \param[in,out] X The pointer to the (little-endian) array + * representing the bignum to accumulate onto. + * \param X_limbs The number of limbs of \p X. This must be + * at least \p A_limbs. + * \param[in] A The pointer to the (little-endian) array + * representing the bignum to multiply with. + * This may be aliased to \p X but may not overlap + * otherwise. + * \param A_limbs The number of limbs of \p A. + * \param b X scalar to multiply with. + * + * \return The carry at the end of the operation. + */ +mbedtls_mpi_uint mbedtls_mpi_core_mla(mbedtls_mpi_uint *X, size_t X_limbs, + const mbedtls_mpi_uint *A, size_t A_limbs, + mbedtls_mpi_uint b); + +/** + * \brief Calculate initialisation value for fast Montgomery modular + * multiplication + * + * \param[in] N Little-endian presentation of the modulus. This must have + * at least one limb. + * + * \return The initialisation value for fast Montgomery modular multiplication + */ +mbedtls_mpi_uint mbedtls_mpi_core_montmul_init(const mbedtls_mpi_uint *N); + +/** + * \brief Montgomery multiplication: X = A * B * R^-1 mod N (HAC 14.36) + * + * \p A and \p B must be in canonical form. That is, < \p N. + * + * \p X may be aliased to \p A or \p N, or even \p B (if \p AN_limbs == + * \p B_limbs) but may not overlap any parameters otherwise. + * + * \p A and \p B may alias each other, if \p AN_limbs == \p B_limbs. They may + * not alias \p N (since they must be in canonical form, they cannot == \p N). + * + * \param[out] X The destination MPI, as a little-endian array of + * length \p AN_limbs. + * On successful completion, X contains the result of + * the multiplication `A * B * R^-1` mod N where + * `R = 2^(biL*AN_limbs)`. + * \param[in] A Little-endian presentation of first operand. + * Must have the same number of limbs as \p N. + * \param[in] B Little-endian presentation of second operand. + * \param[in] B_limbs The number of limbs in \p B. + * Must be <= \p AN_limbs. + * \param[in] N Little-endian presentation of the modulus. + * This must be odd, and have exactly the same number + * of limbs as \p A. + * It may alias \p X, but must not alias or otherwise + * overlap any of the other parameters. + * \param[in] AN_limbs The number of limbs in \p X, \p A and \p N. + * \param mm The Montgomery constant for \p N: -N^-1 mod 2^biL. + * This can be calculated by `mbedtls_mpi_core_montmul_init()`. + * \param[in,out] T Temporary storage of size at least 2*AN_limbs+1 limbs. + * 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_montmul(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, size_t B_limbs, + const mbedtls_mpi_uint *N, size_t AN_limbs, + mbedtls_mpi_uint mm, mbedtls_mpi_uint *T); + +/** + * \brief Calculate the square of the Montgomery constant. (Needed + * for conversion and operations in Montgomery form.) + * + * \param[out] X A pointer to the result of the calculation of + * the square of the Montgomery constant: + * 2^{2*n*biL} mod N. + * \param[in] N Little-endian presentation of the modulus, which must be odd. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if there is not enough space + * to store the value of Montgomery constant squared. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p N modulus is zero. + * \return #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p N modulus is negative. + */ +int mbedtls_mpi_core_get_mont_r2_unsafe(mbedtls_mpi *X, + const mbedtls_mpi *N); + +#if defined(MBEDTLS_TEST_HOOKS) +/** + * Copy an MPI from a table without leaking the index. + * + * \param dest The destination buffer. This must point to a writable + * buffer of at least \p limbs limbs. + * \param table The address of the table. This must point to a readable + * array of \p count elements of \p limbs limbs each. + * \param limbs The number of limbs in each table entry. + * \param count The number of entries in \p table. + * \param index The (secret) table index to look up. This must be in the + * range `0 .. count-1`. + */ +void mbedtls_mpi_core_ct_uint_table_lookup(mbedtls_mpi_uint *dest, + const mbedtls_mpi_uint *table, + size_t limbs, + size_t count, + size_t index); +#endif /* MBEDTLS_TEST_HOOKS */ + +/** + * \brief Fill an integer with a number of random bytes. + * + * \param X The destination MPI. + * \param X_limbs The number of limbs of \p X. + * \param bytes The number of random bytes to generate. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p X does not have + * enough room for \p bytes bytes. + * \return A negative error code on RNG failure. + * + * \note The bytes obtained from the RNG are interpreted + * as a big-endian representation of an MPI; this can + * be relevant in applications like deterministic ECDSA. + */ +int mbedtls_mpi_core_fill_random(mbedtls_mpi_uint *X, size_t X_limbs, + size_t bytes, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** Generate a random number uniformly in a range. + * + * This function generates a random number between \p min inclusive and + * \p N exclusive. + * + * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA) + * when the RNG is a suitably parametrized instance of HMAC_DRBG + * and \p min is \c 1. + * + * \note There are `N - min` possible outputs. The lower bound + * \p min can be reached, but the upper bound \p N cannot. + * + * \param X The destination MPI, with \p limbs limbs. + * It must not be aliased with \p N or otherwise overlap it. + * \param min The minimum value to return. + * \param N The upper bound of the range, exclusive, with \p limbs limbs. + * In other words, this is one plus the maximum value to return. + * \p N must be strictly larger than \p min. + * \param limbs The number of limbs of \p N and \p X. + * This must not be 0. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was + * unable to find a suitable value within a limited number + * of attempts. This has a negligible probability if \p N + * is significantly larger than \p min, which is the case + * for all usual cryptographic applications. + */ +int mbedtls_mpi_core_random(mbedtls_mpi_uint *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_uint *N, + size_t limbs, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/* BEGIN MERGE SLOT 1 */ + +/** + * \brief Returns the number of limbs of working memory required for + * a call to `mbedtls_mpi_core_exp_mod()`. + * + * \note This will always be at least + * `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)`, + * i.e. sufficient for a call to `mbedtls_mpi_core_montmul()`. + * + * \param AN_limbs The number of limbs in the input `A` and the modulus `N` + * (they must be the same size) that will be given to + * `mbedtls_mpi_core_exp_mod()`. + * \param E_limbs The number of limbs in the exponent `E` that will be given + * to `mbedtls_mpi_core_exp_mod()`. + * + * \return The number of limbs of working memory required by + * `mbedtls_mpi_core_exp_mod()`. + */ +size_t mbedtls_mpi_core_exp_mod_working_limbs(size_t AN_limbs, size_t E_limbs); + +/** + * \brief Perform a modular exponentiation with secret exponent: + * X = A^E mod N, where \p A is already in Montgomery form. + * + * \p X may be aliased to \p A, but not to \p RR or \p E, even if \p E_limbs == + * \p AN_limbs. + * + * \param[out] X The destination MPI, as a little endian array of length + * \p AN_limbs. + * \param[in] A The base MPI, as a little endian array of length \p AN_limbs. + * Must be in Montgomery form. + * \param[in] N The modulus, as a little endian array of length \p AN_limbs. + * \param AN_limbs The number of limbs in \p X, \p A, \p N, \p RR. + * \param[in] E The exponent, as a little endian array of length \p E_limbs. + * \param E_limbs The number of limbs in \p E. + * \param[in] RR The precomputed residue of 2^{2*biL} modulo N, as a little + * endian array of length \p AN_limbs. + * \param[in,out] T Temporary storage of at least the number of limbs returned + * by `mbedtls_mpi_core_exp_mod_working_limbs()`. + * Its initial content is unused and its final content is + * indeterminate. + * It must not alias or otherwise overlap any of the other + * parameters. + * It is up to the caller to zeroize \p T when it is no + * longer needed, and before freeing it if it was dynamically + * allocated. + */ +void mbedtls_mpi_core_exp_mod(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, size_t AN_limbs, + const mbedtls_mpi_uint *E, size_t E_limbs, + const mbedtls_mpi_uint *RR, + mbedtls_mpi_uint *T); + +/* END MERGE SLOT 1 */ + +/* BEGIN MERGE SLOT 2 */ + +/* END MERGE SLOT 2 */ + +/* BEGIN MERGE SLOT 3 */ + +/** + * \brief Subtract unsigned integer from known-size large unsigned integers. + * Return the borrow. + * + * \param[out] X The result of the subtraction. + * \param[in] A The left operand. + * \param b The unsigned scalar to subtract. + * \param limbs Number of limbs of \p X and \p A. + * + * \return 1 if `A < b`. + * 0 if `A >= b`. + */ +mbedtls_mpi_uint mbedtls_mpi_core_sub_int(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + mbedtls_mpi_uint b, + size_t limbs); + +/** + * \brief Determine if a given MPI has the value \c 0 in constant time with + * respect to the value (but not with respect to the number of limbs). + * + * \param[in] A The MPI to test. + * \param limbs Number of limbs in \p A. + * + * \return 0 if `A == 0` + * non-0 (may be any value) if `A != 0`. + */ +mbedtls_mpi_uint mbedtls_mpi_core_check_zero_ct(const mbedtls_mpi_uint *A, + size_t limbs); + +/** + * \brief Returns the number of limbs of working memory required for + * a call to `mbedtls_mpi_core_montmul()`. + * + * \param AN_limbs The number of limbs in the input `A` and the modulus `N` + * (they must be the same size) that will be given to + * `mbedtls_mpi_core_montmul()` or one of the other functions + * that specifies this as the amount of working memory needed. + * + * \return The number of limbs of working memory required by + * `mbedtls_mpi_core_montmul()` (or other similar function). + */ +static inline size_t mbedtls_mpi_core_montmul_working_limbs(size_t AN_limbs) +{ + return 2 * AN_limbs + 1; +} + +/** Convert an MPI into Montgomery form. + * + * \p X may be aliased to \p A, but may not otherwise overlap it. + * + * \p X may not alias \p N (it is in canonical form, so must be strictly less + * than \p N). Nor may it alias or overlap \p rr (this is unlikely to be + * required in practice.) + * + * This function is a thin wrapper around `mbedtls_mpi_core_montmul()` that is + * an alternative to calling `mbedtls_mpi_mod_raw_to_mont_rep()` when we + * don't want to allocate memory. + * + * \param[out] X The result of the conversion. + * Must have the same number of limbs as \p A. + * \param[in] A The MPI to convert into Montgomery form. + * Must have the same number of limbs as the modulus. + * \param[in] N The address of the modulus, which gives the size of + * the base `R` = 2^(biL*N->limbs). + * \param[in] AN_limbs The number of limbs in \p X, \p A, \p N and \p rr. + * \param mm The Montgomery constant for \p N: -N^-1 mod 2^biL. + * This can be determined by calling + * `mbedtls_mpi_core_montmul_init()`. + * \param[in] rr The residue for `2^{2*n*biL} mod N`. + * \param[in,out] T Temporary storage of size at least + * `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)` + * limbs. + * 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_to_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + mbedtls_mpi_uint mm, + const mbedtls_mpi_uint *rr, + mbedtls_mpi_uint *T); + +/** Convert an MPI from Montgomery form. + * + * \p X may be aliased to \p A, but may not otherwise overlap it. + * + * \p X may not alias \p N (it is in canonical form, so must be strictly less + * than \p N). + * + * This function is a thin wrapper around `mbedtls_mpi_core_montmul()` that is + * an alternative to calling `mbedtls_mpi_mod_raw_from_mont_rep()` when we + * don't want to allocate memory. + * + * \param[out] X The result of the conversion. + * Must have the same number of limbs as \p A. + * \param[in] A The MPI to convert from Montgomery form. + * Must have the same number of limbs as the modulus. + * \param[in] N The address of the modulus, which gives the size of + * the base `R` = 2^(biL*N->limbs). + * \param[in] AN_limbs The number of limbs in \p X, \p A and \p N. + * \param mm The Montgomery constant for \p N: -N^-1 mod 2^biL. + * This can be determined by calling + * `mbedtls_mpi_core_montmul_init()`. + * \param[in,out] T Temporary storage of size at least + * `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)` + * limbs. + * 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_from_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + mbedtls_mpi_uint mm, + mbedtls_mpi_uint *T); + +/* END MERGE SLOT 3 */ + +/* BEGIN MERGE SLOT 4 */ + +/* END MERGE SLOT 4 */ + +/* BEGIN MERGE SLOT 5 */ + +/* END MERGE SLOT 5 */ + +/* BEGIN MERGE SLOT 6 */ + +/* END MERGE SLOT 6 */ + +/* BEGIN MERGE SLOT 7 */ + +/* END MERGE SLOT 7 */ + +/* BEGIN MERGE SLOT 8 */ + +/* END MERGE SLOT 8 */ + +/* BEGIN MERGE SLOT 9 */ + +/* END MERGE SLOT 9 */ + +/* BEGIN MERGE SLOT 10 */ + +/* END MERGE SLOT 10 */ + +#endif /* MBEDTLS_BIGNUM_CORE_H */ diff --git a/r5dev/thirdparty/mbedtls/bignum_mod.c b/r5dev/thirdparty/mbedtls/bignum_mod.c new file mode 100644 index 00000000..e986865a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum_mod.c @@ -0,0 +1,434 @@ +/** + * Modular bignum functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) + +#include + +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "mbedtls/bignum.h" + +#include "mbedtls/platform.h" + +#include "bignum_core.h" +#include "bignum_mod.h" +#include "bignum_mod_raw.h" +#include "constant_time_internal.h" + +int mbedtls_mpi_mod_residue_setup(mbedtls_mpi_mod_residue *r, + const mbedtls_mpi_mod_modulus *N, + mbedtls_mpi_uint *p, + size_t p_limbs) +{ + if (p_limbs != N->limbs || !mbedtls_mpi_core_lt_ct(p, N->p, N->limbs)) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + r->limbs = N->limbs; + r->p = p; + + return 0; +} + +void mbedtls_mpi_mod_residue_release(mbedtls_mpi_mod_residue *r) +{ + if (r == NULL) { + return; + } + + r->limbs = 0; + r->p = NULL; +} + +void mbedtls_mpi_mod_modulus_init(mbedtls_mpi_mod_modulus *N) +{ + if (N == NULL) { + return; + } + + N->p = NULL; + N->limbs = 0; + N->bits = 0; + N->int_rep = MBEDTLS_MPI_MOD_REP_INVALID; +} + +void mbedtls_mpi_mod_modulus_free(mbedtls_mpi_mod_modulus *N) +{ + if (N == NULL) { + return; + } + + switch (N->int_rep) { + case MBEDTLS_MPI_MOD_REP_MONTGOMERY: + if (N->rep.mont.rr != NULL) { + mbedtls_platform_zeroize((mbedtls_mpi_uint *) N->rep.mont.rr, + N->limbs * sizeof(mbedtls_mpi_uint)); + mbedtls_free((mbedtls_mpi_uint *) N->rep.mont.rr); + N->rep.mont.rr = NULL; + } + N->rep.mont.mm = 0; + break; + case MBEDTLS_MPI_MOD_REP_OPT_RED: + mbedtls_free(N->rep.ored); + break; + case MBEDTLS_MPI_MOD_REP_INVALID: + break; + } + + N->p = NULL; + N->limbs = 0; + N->bits = 0; + N->int_rep = MBEDTLS_MPI_MOD_REP_INVALID; +} + +static int set_mont_const_square(const mbedtls_mpi_uint **X, + const mbedtls_mpi_uint *A, + size_t limbs) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi N; + mbedtls_mpi RR; + *X = NULL; + + mbedtls_mpi_init(&N); + mbedtls_mpi_init(&RR); + + if (A == NULL || limbs == 0 || limbs >= (MBEDTLS_MPI_MAX_LIMBS / 2) - 2) { + goto cleanup; + } + + if (mbedtls_mpi_grow(&N, limbs)) { + goto cleanup; + } + + memcpy(N.p, A, sizeof(mbedtls_mpi_uint) * limbs); + + ret = mbedtls_mpi_core_get_mont_r2_unsafe(&RR, &N); + + if (ret == 0) { + *X = RR.p; + RR.p = NULL; + } + +cleanup: + mbedtls_mpi_free(&N); + mbedtls_mpi_free(&RR); + ret = (ret != 0) ? MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED : 0; + return ret; +} + +int mbedtls_mpi_mod_modulus_setup(mbedtls_mpi_mod_modulus *N, + const mbedtls_mpi_uint *p, + size_t p_limbs, + mbedtls_mpi_mod_rep_selector int_rep) +{ + int ret = 0; + + N->p = p; + N->limbs = p_limbs; + N->bits = mbedtls_mpi_core_bitlen(p, p_limbs); + + switch (int_rep) { + case MBEDTLS_MPI_MOD_REP_MONTGOMERY: + N->int_rep = int_rep; + N->rep.mont.mm = mbedtls_mpi_core_montmul_init(N->p); + ret = set_mont_const_square(&N->rep.mont.rr, N->p, N->limbs); + break; + case MBEDTLS_MPI_MOD_REP_OPT_RED: + N->int_rep = int_rep; + N->rep.ored = NULL; + break; + default: + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto exit; + } + +exit: + + if (ret != 0) { + mbedtls_mpi_mod_modulus_free(N); + } + + return ret; +} + +/* BEGIN MERGE SLOT 1 */ + +/* END MERGE SLOT 1 */ + +/* BEGIN MERGE SLOT 2 */ + +int mbedtls_mpi_mod_mul(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_residue *B, + const mbedtls_mpi_mod_modulus *N) +{ + if (N->limbs == 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (X->limbs != N->limbs || A->limbs != N->limbs || B->limbs != N->limbs) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + mbedtls_mpi_uint *T = mbedtls_calloc(N->limbs * 2 + 1, ciL); + if (T == NULL) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + mbedtls_mpi_mod_raw_mul(X->p, A->p, B->p, N, T); + + mbedtls_free(T); + + return 0; +} + +/* END MERGE SLOT 2 */ + +/* BEGIN MERGE SLOT 3 */ +int mbedtls_mpi_mod_sub(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_residue *B, + const mbedtls_mpi_mod_modulus *N) +{ + if (X->limbs != N->limbs || A->limbs != N->limbs || B->limbs != N->limbs) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + mbedtls_mpi_mod_raw_sub(X->p, A->p, B->p, N); + + return 0; +} + +static int mbedtls_mpi_mod_inv_mont(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_modulus *N, + mbedtls_mpi_uint *working_memory) +{ + /* Input already in Montgomery form, so there's little to do */ + mbedtls_mpi_mod_raw_inv_prime(X->p, A->p, + N->p, N->limbs, + N->rep.mont.rr, + working_memory); + return 0; +} + +static int mbedtls_mpi_mod_inv_non_mont(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_modulus *N, + mbedtls_mpi_uint *working_memory) +{ + /* Need to convert input into Montgomery form */ + + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_mpi_mod_modulus Nmont; + mbedtls_mpi_mod_modulus_init(&Nmont); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_modulus_setup(&Nmont, N->p, N->limbs, + MBEDTLS_MPI_MOD_REP_MONTGOMERY)); + + /* We'll use X->p to hold the Montgomery form of the input A->p */ + mbedtls_mpi_core_to_mont_rep(X->p, A->p, Nmont.p, Nmont.limbs, + Nmont.rep.mont.mm, Nmont.rep.mont.rr, + working_memory); + + mbedtls_mpi_mod_raw_inv_prime(X->p, X->p, + Nmont.p, Nmont.limbs, + Nmont.rep.mont.rr, + working_memory); + + /* And convert back from Montgomery form */ + + mbedtls_mpi_core_from_mont_rep(X->p, X->p, Nmont.p, Nmont.limbs, + Nmont.rep.mont.mm, working_memory); + +cleanup: + mbedtls_mpi_mod_modulus_free(&Nmont); + return ret; +} + +int mbedtls_mpi_mod_inv(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_modulus *N) +{ + if (X->limbs != N->limbs || A->limbs != N->limbs) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + /* Zero has the same value regardless of Montgomery form or not */ + if (mbedtls_mpi_core_check_zero_ct(A->p, A->limbs) == 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + size_t working_limbs = + mbedtls_mpi_mod_raw_inv_prime_working_limbs(N->limbs); + + mbedtls_mpi_uint *working_memory = mbedtls_calloc(working_limbs, + sizeof(mbedtls_mpi_uint)); + if (working_memory == NULL) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + switch (N->int_rep) { + case MBEDTLS_MPI_MOD_REP_MONTGOMERY: + ret = mbedtls_mpi_mod_inv_mont(X, A, N, working_memory); + break; + case MBEDTLS_MPI_MOD_REP_OPT_RED: + ret = mbedtls_mpi_mod_inv_non_mont(X, A, N, working_memory); + break; + default: + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + break; + } + + mbedtls_platform_zeroize(working_memory, + working_limbs * sizeof(mbedtls_mpi_uint)); + mbedtls_free(working_memory); + + return ret; +} +/* END MERGE SLOT 3 */ + +/* BEGIN MERGE SLOT 4 */ + +/* END MERGE SLOT 4 */ + +/* BEGIN MERGE SLOT 5 */ +int mbedtls_mpi_mod_add(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_residue *B, + const mbedtls_mpi_mod_modulus *N) +{ + if (X->limbs != N->limbs || A->limbs != N->limbs || B->limbs != N->limbs) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + mbedtls_mpi_mod_raw_add(X->p, A->p, B->p, N); + + return 0; +} +/* END MERGE SLOT 5 */ + +/* BEGIN MERGE SLOT 6 */ + +int mbedtls_mpi_mod_random(mbedtls_mpi_mod_residue *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_mod_modulus *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + if (X->limbs != N->limbs) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + return mbedtls_mpi_mod_raw_random(X->p, min, N, f_rng, p_rng); +} + +/* END MERGE SLOT 6 */ + +/* BEGIN MERGE SLOT 7 */ +int mbedtls_mpi_mod_read(mbedtls_mpi_mod_residue *r, + const mbedtls_mpi_mod_modulus *N, + const unsigned char *buf, + size_t buflen, + mbedtls_mpi_mod_ext_rep ext_rep) +{ + int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + + /* Do our best to check if r and m have been set up */ + if (r->limbs == 0 || N->limbs == 0) { + goto cleanup; + } + if (r->limbs != N->limbs) { + goto cleanup; + } + + ret = mbedtls_mpi_mod_raw_read(r->p, N, buf, buflen, ext_rep); + if (ret != 0) { + goto cleanup; + } + + r->limbs = N->limbs; + + ret = mbedtls_mpi_mod_raw_canonical_to_modulus_rep(r->p, N); + +cleanup: + return ret; +} + +int mbedtls_mpi_mod_write(const mbedtls_mpi_mod_residue *r, + const mbedtls_mpi_mod_modulus *N, + unsigned char *buf, + size_t buflen, + mbedtls_mpi_mod_ext_rep ext_rep) +{ + int ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + + /* Do our best to check if r and m have been set up */ + if (r->limbs == 0 || N->limbs == 0) { + goto cleanup; + } + if (r->limbs != N->limbs) { + goto cleanup; + } + + if (N->int_rep == MBEDTLS_MPI_MOD_REP_MONTGOMERY) { + ret = mbedtls_mpi_mod_raw_from_mont_rep(r->p, N); + if (ret != 0) { + goto cleanup; + } + } + + ret = mbedtls_mpi_mod_raw_write(r->p, N, buf, buflen, ext_rep); + + if (N->int_rep == MBEDTLS_MPI_MOD_REP_MONTGOMERY) { + /* If this fails, the value of r is corrupted and we want to return + * this error (as opposed to the error code from the write above) to + * let the caller know. If it succeeds, we want to return the error + * code from write above. */ + int conv_ret = mbedtls_mpi_mod_raw_to_mont_rep(r->p, N); + if (ret == 0) { + ret = conv_ret; + } + } + +cleanup: + + return ret; +} +/* END MERGE SLOT 7 */ + +/* BEGIN MERGE SLOT 8 */ + +/* END MERGE SLOT 8 */ + +/* BEGIN MERGE SLOT 9 */ + +/* END MERGE SLOT 9 */ + +/* BEGIN MERGE SLOT 10 */ + +/* END MERGE SLOT 10 */ + +#endif /* MBEDTLS_BIGNUM_C */ diff --git a/r5dev/thirdparty/mbedtls/bignum_mod.h b/r5dev/thirdparty/mbedtls/bignum_mod.h new file mode 100644 index 00000000..d4c1d5d6 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum_mod.h @@ -0,0 +1,481 @@ +/** + * Modular bignum functions + * + * This module implements operations on integers modulo some fixed modulus. + * + * The functions in this module obey the following conventions unless + * explicitly indicated otherwise: + * + * - **Modulus parameters**: the modulus is passed as a pointer to a structure + * of type #mbedtls_mpi_mod_modulus. The structure must be set up with an + * array of limbs storing the bignum value of the modulus. The modulus must + * be odd and is assumed to have no leading zeroes. The modulus is usually + * named \c N and is usually input-only. Functions which take a parameter + * of type \c const #mbedtls_mpi_mod_modulus* must not modify its value. + * - **Bignum parameters**: Bignums are passed as pointers to an array of + * limbs or to a #mbedtls_mpi_mod_residue structure. A limb has the type + * #mbedtls_mpi_uint. Residues must be initialized before use, and must be + * associated with the modulus \c N. Unless otherwise specified: + * - Bignum parameters called \c A, \c B, ... are inputs and are not + * modified by the function. Functions which take a parameter of + * type \c const #mbedtls_mpi_mod_residue* must not modify its value. + * - Bignum parameters called \c X, \c Y, ... are outputs or input-output. + * The initial bignum value of output-only parameters is ignored, but + * they must be set up and associated with the modulus \c N. Some + * functions (typically constant-flow) require that the limbs in an + * output residue are initialized. + * - Bignum parameters called \c p are inputs used to set up a modulus or + * residue. These must be pointers to an array of limbs. + * - \c T is a temporary storage area. The initial content of such a + * parameter is ignored and the final content is unspecified. + * - Some functions use different names, such as \c r for the residue. + * - **Bignum sizes**: bignum sizes are always expressed in limbs. Both + * #mbedtls_mpi_mod_modulus and #mbedtls_mpi_mod_residue have a \c limbs + * member storing its size. All bignum parameters must have the same + * number of limbs as the modulus. All bignum sizes must be at least 1 and + * must be significantly less than #SIZE_MAX. The behavior if a size is 0 is + * undefined. + * - **Bignum representation**: the representation of inputs and outputs is + * specified by the \c int_rep field of the modulus. + * - **Parameter ordering**: for bignum parameters, outputs come before inputs. + * The modulus is passed after residues. Temporaries come last. + * - **Aliasing**: in general, output bignums may be aliased to one or more + * inputs. Modulus values may not be aliased to any other parameter. Outputs + * may not be aliased to one another. Temporaries may not be aliased to any + * other parameter. + * - **Overlap**: apart from aliasing of residue pointers (where two residue + * arguments are equal pointers), overlap is not supported and may result + * in undefined behavior. + * - **Error handling**: functions generally check compatibility of input + * sizes. Most functions will not check that input values are in canonical + * form (i.e. that \c A < \c N), this is only checked during setup of a + * residue structure. + * - **Modular representatives**: all functions expect inputs to be in the + * range [0, \c N - 1] and guarantee outputs in the range [0, \c N - 1]. + * Residues are set up with an associated modulus, and operations are only + * guaranteed to work if the modulus is associated with all residue + * parameters. If a residue is passed with a modulus other than the one it + * is associated with, then it may be out of range. If an input is out of + * range, outputs are fully unspecified, though bignum values out of range + * should not cause buffer overflows (beware that this is not extensively + * tested). + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_BIGNUM_MOD_H +#define MBEDTLS_BIGNUM_MOD_H + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +/** How residues associated with a modulus are represented. + * + * This also determines which fields of the modulus structure are valid and + * what their contents are (see #mbedtls_mpi_mod_modulus). + */ +typedef enum { + /** Representation not chosen (makes the modulus structure invalid). */ + MBEDTLS_MPI_MOD_REP_INVALID = 0, + /* Skip 1 as it is slightly easier to accidentally pass to functions. */ + /** Montgomery representation. */ + MBEDTLS_MPI_MOD_REP_MONTGOMERY = 2, + /** TODO: document this. + * + * Residues are in canonical representation. + */ + MBEDTLS_MPI_MOD_REP_OPT_RED, +} mbedtls_mpi_mod_rep_selector; + +/* Make mbedtls_mpi_mod_rep_selector and mbedtls_mpi_mod_ext_rep disjoint to + * make it easier to catch when they are accidentally swapped. */ +typedef enum { + MBEDTLS_MPI_MOD_EXT_REP_INVALID = 0, + MBEDTLS_MPI_MOD_EXT_REP_LE = 8, + MBEDTLS_MPI_MOD_EXT_REP_BE +} mbedtls_mpi_mod_ext_rep; + +typedef struct { + mbedtls_mpi_uint *p; + size_t limbs; +} mbedtls_mpi_mod_residue; + +typedef struct { + mbedtls_mpi_uint const *rr; /* The residue for 2^{2*n*biL} mod N */ + mbedtls_mpi_uint mm; /* Montgomery const for -N^{-1} mod 2^{ciL} */ +} mbedtls_mpi_mont_struct; + +typedef void *mbedtls_mpi_opt_red_struct; + +typedef struct { + const mbedtls_mpi_uint *p; + size_t limbs; // number of limbs + size_t bits; // bitlen of p + mbedtls_mpi_mod_rep_selector int_rep; // selector to signal the active member of the union + union rep { + /* if int_rep == #MBEDTLS_MPI_MOD_REP_MONTGOMERY */ + mbedtls_mpi_mont_struct mont; + /* if int_rep == #MBEDTLS_MPI_MOD_REP_OPT_RED */ + mbedtls_mpi_opt_red_struct ored; + } rep; +} mbedtls_mpi_mod_modulus; + +/** Setup a residue structure. + * + * The residue will be set up with the buffer \p p and modulus \p N. + * + * The memory pointed to by \p p will be used by the resulting residue structure. + * The value at the pointed-to memory will be the initial value of \p r and must + * hold a value that is less than the modulus. This value will be used as-is + * and interpreted according to the value of the `N->int_rep` field. + * + * The modulus \p N will be the modulus associated with \p r. The residue \p r + * should only be used in operations where the modulus is \p N. + * + * \param[out] r The address of the residue to setup. + * \param[in] N The address of the modulus related to \p r. + * \param[in] p The address of the limb array containing the value of \p r. + * The memory pointed to by \p p will be used by \p r and must + * not be modified in any way until after + * mbedtls_mpi_mod_residue_release() is called. The data + * pointed to by \p p must be less than the modulus (the value + * pointed to by `N->p`) and already in the representation + * indicated by `N->int_rep`. + * \param p_limbs The number of limbs of \p p. Must be the same as the number + * of limbs in the modulus \p N. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p p_limbs is less than the + * limbs in \p N or if \p p is not less than \p N. + */ +int mbedtls_mpi_mod_residue_setup(mbedtls_mpi_mod_residue *r, + const mbedtls_mpi_mod_modulus *N, + mbedtls_mpi_uint *p, + size_t p_limbs); + +/** Unbind elements of a residue structure. + * + * This function removes the reference to the limb array that was passed to + * mbedtls_mpi_mod_residue_setup() to make it safe to free or use again. + * + * This function invalidates \p r and it must not be used until after + * mbedtls_mpi_mod_residue_setup() is called on it again. + * + * \param[out] r The address of residue to release. + */ +void mbedtls_mpi_mod_residue_release(mbedtls_mpi_mod_residue *r); + +/** Initialize a modulus structure. + * + * \param[out] N The address of the modulus structure to initialize. + */ +void mbedtls_mpi_mod_modulus_init(mbedtls_mpi_mod_modulus *N); + +/** Setup a modulus structure. + * + * \param[out] N The address of the modulus structure to populate. + * \param[in] p The address of the limb array storing the value of \p N. + * The memory pointed to by \p p will be used by \p N and must + * not be modified in any way until after + * mbedtls_mpi_mod_modulus_free() is called. + * \param p_limbs The number of limbs of \p p. + * \param int_rep The internal representation to be used for residues + * associated with \p N (see #mbedtls_mpi_mod_rep_selector). + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p int_rep is invalid. + */ +int mbedtls_mpi_mod_modulus_setup(mbedtls_mpi_mod_modulus *N, + const mbedtls_mpi_uint *p, + size_t p_limbs, + mbedtls_mpi_mod_rep_selector int_rep); + +/** Free elements of a modulus structure. + * + * This function frees any memory allocated by mbedtls_mpi_mod_modulus_setup(). + * + * \warning This function does not free the limb array passed to + * mbedtls_mpi_mod_modulus_setup() only removes the reference to it, + * making it safe to free or to use it again. + * + * \param[in,out] N The address of the modulus structure to free. + */ +void mbedtls_mpi_mod_modulus_free(mbedtls_mpi_mod_modulus *N); + +/* BEGIN MERGE SLOT 1 */ + +/* END MERGE SLOT 1 */ + +/* BEGIN MERGE SLOT 2 */ + +/** \brief Multiply two residues, returning the residue modulo the specified + * modulus. + * + * \note Currently handles the case when `N->int_rep` is + * MBEDTLS_MPI_MOD_REP_MONTGOMERY. + * + * The size of the operation is determined by \p N. \p A, \p B and \p X must + * all be associated with the modulus \p N and must all have the same number + * of limbs as \p N. + * + * \p X may be aliased to \p A or \p B, or even both, but may not overlap + * either otherwise. They may not alias \p N (since they must be in canonical + * form, they cannot == \p N). + * + * \param[out] X The address of the result MPI. Must have the same + * number of limbs as \p N. + * On successful completion, \p X contains the result of + * the multiplication `A * B * R^-1` mod N where + * `R = 2^(biL * N->limbs)`. + * \param[in] A The address of the first MPI. + * \param[in] B The address of the second MPI. + * \param[in] N The address of the modulus. Used to perform a modulo + * operation on the result of the multiplication. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if all the parameters do not + * have the same number of limbs or \p N is invalid. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + */ +int mbedtls_mpi_mod_mul(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_residue *B, + const mbedtls_mpi_mod_modulus *N); + +/* END MERGE SLOT 2 */ + +/* BEGIN MERGE SLOT 3 */ +/** + * \brief Perform a fixed-size modular subtraction. + * + * Calculate `A - B modulo N`. + * + * \p A, \p B and \p X must all have the same number of limbs as \p N. + * + * \p X may be aliased to \p A or \p B, or even both, but may not overlap + * either otherwise. + * + * \note This function does not check that \p A or \p B are in canonical + * form (that is, are < \p N) - that will have been done by + * mbedtls_mpi_mod_residue_setup(). + * + * \param[out] X The address of the result MPI. Must be initialized. + * Must have the same number of limbs as the modulus \p N. + * \param[in] A The address of the first MPI. + * \param[in] B The address of the second MPI. + * \param[in] N The address of the modulus. Used to perform a modulo + * operation on the result of the subtraction. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the given MPIs do not + * have the correct number of limbs. + */ +int mbedtls_mpi_mod_sub(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_residue *B, + const mbedtls_mpi_mod_modulus *N); + +/** + * \brief Perform modular inversion of an MPI with respect to a modulus \p N. + * + * \p A and \p X must be associated with the modulus \p N and will therefore + * have the same number of limbs as \p N. + * + * \p X may be aliased to \p A. + * + * \warning Currently only supports prime moduli, but does not check for them. + * + * \param[out] X The modular inverse of \p A with respect to \p N. + * \param[in] A The number to calculate the modular inverse of. + * Must not be 0. + * \param[in] N The modulus to use. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p A and \p N do not + * have the same number of limbs. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p A is zero. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if couldn't allocate enough + * memory (needed for conversion to and from Mongtomery form + * when not in Montgomery form already, and for temporary use + * by the inversion calculation itself). + */ + +int mbedtls_mpi_mod_inv(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_modulus *N); +/* END MERGE SLOT 3 */ + +/* BEGIN MERGE SLOT 4 */ + +/* END MERGE SLOT 4 */ + +/* BEGIN MERGE SLOT 5 */ +/** + * \brief Perform a fixed-size modular addition. + * + * Calculate `A + B modulo N`. + * + * \p A, \p B and \p X must all be associated with the modulus \p N and must + * all have the same number of limbs as \p N. + * + * \p X may be aliased to \p A or \p B, or even both, but may not overlap + * either otherwise. + * + * \note This function does not check that \p A or \p B are in canonical + * form (that is, are < \p N) - that will have been done by + * mbedtls_mpi_mod_residue_setup(). + * + * \param[out] X The address of the result residue. Must be initialized. + * Must have the same number of limbs as the modulus \p N. + * \param[in] A The address of the first input residue. + * \param[in] B The address of the second input residue. + * \param[in] N The address of the modulus. Used to perform a modulo + * operation on the result of the addition. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the given MPIs do not + * have the correct number of limbs. + */ +int mbedtls_mpi_mod_add(mbedtls_mpi_mod_residue *X, + const mbedtls_mpi_mod_residue *A, + const mbedtls_mpi_mod_residue *B, + const mbedtls_mpi_mod_modulus *N); +/* END MERGE SLOT 5 */ + +/* BEGIN MERGE SLOT 6 */ + +/** Generate a random number uniformly in a range. + * + * This function generates a random number between \p min inclusive and + * \p N exclusive. + * + * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA) + * when the RNG is a suitably parametrized instance of HMAC_DRBG + * and \p min is \c 1. + * + * \note There are `N - min` possible outputs. The lower bound + * \p min can be reached, but the upper bound \p N cannot. + * + * \param X The destination residue. + * \param min The minimum value to return. It must be strictly smaller + * than \b N. + * \param N The modulus. + * This is the upper bound of the output range, exclusive. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was + * unable to find a suitable value within a limited number + * of attempts. This has a negligible probability if \p N + * is significantly larger than \p min, which is the case + * for all usual cryptographic applications. + */ +int mbedtls_mpi_mod_random(mbedtls_mpi_mod_residue *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_mod_modulus *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/* END MERGE SLOT 6 */ + +/* BEGIN MERGE SLOT 7 */ +/** Read a residue from a byte buffer. + * + * The residue will be automatically converted to the internal representation + * based on the value of the `N->int_rep` field. + * + * The modulus \p N will be the modulus associated with \p r. The residue \p r + * should only be used in operations where the modulus is \p N or a modulus + * equivalent to \p N (in the sense that all their fields or memory pointed by + * their fields hold the same value). + * + * \param[out] r The address of the residue. It must have exactly the same + * number of limbs as the modulus \p N. + * \param[in] N The address of the modulus. + * \param[in] buf The input buffer to import from. + * \param buflen The length in bytes of \p buf. + * \param ext_rep The endianness of the number in the input buffer. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p r isn't + * large enough to hold the value in \p buf. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p ext_rep + * is invalid or the value in the buffer is not less than \p N. + */ +int mbedtls_mpi_mod_read(mbedtls_mpi_mod_residue *r, + const mbedtls_mpi_mod_modulus *N, + const unsigned char *buf, + size_t buflen, + mbedtls_mpi_mod_ext_rep ext_rep); + +/** Write a residue into a byte buffer. + * + * The modulus \p N must be the modulus associated with \p r (see + * mbedtls_mpi_mod_residue_setup() and mbedtls_mpi_mod_read()). + * + * The residue will be automatically converted from the internal representation + * based on the value of `N->int_rep` field. + * + * \warning If the buffer is smaller than `N->bits`, the number of + * leading zeroes is leaked through timing. If \p r is + * secret, the caller must ensure that \p buflen is at least + * (`N->bits`+7)/8. + * + * \param[in] r The address of the residue. It must have the same number of + * limbs as the modulus \p N. (\p r is an input parameter, but + * its value will be modified during execution and restored + * before the function returns.) + * \param[in] N The address of the modulus associated with \p r. + * \param[out] buf The output buffer to export to. + * \param buflen The length in bytes of \p buf. + * \param ext_rep The endianness in which the number should be written into + * the output buffer. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p buf isn't + * large enough to hold the value of \p r (without leading + * zeroes). + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p ext_rep is invalid. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if couldn't allocate enough + * memory for conversion. Can occur only for moduli with + * MBEDTLS_MPI_MOD_REP_MONTGOMERY. + */ +int mbedtls_mpi_mod_write(const mbedtls_mpi_mod_residue *r, + const mbedtls_mpi_mod_modulus *N, + unsigned char *buf, + size_t buflen, + mbedtls_mpi_mod_ext_rep ext_rep); +/* END MERGE SLOT 7 */ + +/* BEGIN MERGE SLOT 8 */ + +/* END MERGE SLOT 8 */ + +/* BEGIN MERGE SLOT 9 */ + +/* END MERGE SLOT 9 */ + +/* BEGIN MERGE SLOT 10 */ + +/* END MERGE SLOT 10 */ + +#endif /* MBEDTLS_BIGNUM_MOD_H */ diff --git a/r5dev/thirdparty/mbedtls/bignum_mod_raw.c b/r5dev/thirdparty/mbedtls/bignum_mod_raw.c new file mode 100644 index 00000000..bf0cb252 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum_mod_raw.c @@ -0,0 +1,306 @@ +/* + * Low-level modular bignum functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) + +#include + +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" + +#include "mbedtls/platform.h" + +#include "bignum_core.h" +#include "bignum_mod_raw.h" +#include "bignum_mod.h" +#include "constant_time_internal.h" + +#include "bignum_mod_raw_invasive.h" + +void mbedtls_mpi_mod_raw_cond_assign(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_mod_modulus *N, + unsigned char assign) +{ + mbedtls_mpi_core_cond_assign(X, A, N->limbs, assign); +} + +void mbedtls_mpi_mod_raw_cond_swap(mbedtls_mpi_uint *X, + mbedtls_mpi_uint *Y, + const mbedtls_mpi_mod_modulus *N, + unsigned char swap) +{ + mbedtls_mpi_core_cond_swap(X, Y, N->limbs, swap); +} + +int mbedtls_mpi_mod_raw_read(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N, + const unsigned char *input, + size_t input_length, + mbedtls_mpi_mod_ext_rep ext_rep) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + switch (ext_rep) { + case MBEDTLS_MPI_MOD_EXT_REP_LE: + ret = mbedtls_mpi_core_read_le(X, N->limbs, + input, input_length); + break; + case MBEDTLS_MPI_MOD_EXT_REP_BE: + ret = mbedtls_mpi_core_read_be(X, N->limbs, + input, input_length); + break; + default: + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (ret != 0) { + goto cleanup; + } + + if (!mbedtls_mpi_core_lt_ct(X, N->p, N->limbs)) { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + +cleanup: + + return ret; +} + +int mbedtls_mpi_mod_raw_write(const mbedtls_mpi_uint *A, + const mbedtls_mpi_mod_modulus *N, + unsigned char *output, + size_t output_length, + mbedtls_mpi_mod_ext_rep ext_rep) +{ + switch (ext_rep) { + case MBEDTLS_MPI_MOD_EXT_REP_LE: + return mbedtls_mpi_core_write_le(A, N->limbs, + output, output_length); + case MBEDTLS_MPI_MOD_EXT_REP_BE: + return mbedtls_mpi_core_write_be(A, N->limbs, + output, output_length); + default: + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } +} + +/* BEGIN MERGE SLOT 1 */ + +/* END MERGE SLOT 1 */ + +/* BEGIN MERGE SLOT 2 */ + +void mbedtls_mpi_mod_raw_sub(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + const mbedtls_mpi_mod_modulus *N) +{ + mbedtls_mpi_uint c = mbedtls_mpi_core_sub(X, A, B, N->limbs); + + (void) mbedtls_mpi_core_add_if(X, N->p, N->limbs, (unsigned) c); +} + +#if defined(MBEDTLS_TEST_HOOKS) + +MBEDTLS_STATIC_TESTABLE +void mbedtls_mpi_mod_raw_fix_quasi_reduction(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N) +{ + mbedtls_mpi_uint c = mbedtls_mpi_core_sub(X, X, N->p, N->limbs); + + (void) mbedtls_mpi_core_add_if(X, N->p, N->limbs, (unsigned) c); +} + +#endif /* MBEDTLS_TEST_HOOKS */ + +void mbedtls_mpi_mod_raw_mul(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + const mbedtls_mpi_mod_modulus *N, + mbedtls_mpi_uint *T) +{ + mbedtls_mpi_core_montmul(X, A, B, N->limbs, N->p, N->limbs, + N->rep.mont.mm, T); +} + +/* END MERGE SLOT 2 */ + +/* BEGIN MERGE SLOT 3 */ + +size_t mbedtls_mpi_mod_raw_inv_prime_working_limbs(size_t AN_limbs) +{ + /* mbedtls_mpi_mod_raw_inv_prime() needs a temporary for the exponent, + * which will be the same size as the modulus and input (AN_limbs), + * and additional space to pass to mbedtls_mpi_core_exp_mod(). */ + return AN_limbs + + mbedtls_mpi_core_exp_mod_working_limbs(AN_limbs, AN_limbs); +} + +void mbedtls_mpi_mod_raw_inv_prime(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + const mbedtls_mpi_uint *RR, + mbedtls_mpi_uint *T) +{ + /* Inversion by power: g^|G| = 1 => g^(-1) = g^(|G|-1), and + * |G| = N - 1, so we want + * g^(|G|-1) = g^(N - 2) + */ + + /* Use the first AN_limbs of T to hold N - 2 */ + mbedtls_mpi_uint *Nminus2 = T; + (void) mbedtls_mpi_core_sub_int(Nminus2, N, 2, AN_limbs); + + /* Rest of T is given to exp_mod for its working space */ + mbedtls_mpi_core_exp_mod(X, + A, N, AN_limbs, Nminus2, AN_limbs, + RR, T + AN_limbs); +} + +/* END MERGE SLOT 3 */ + +/* BEGIN MERGE SLOT 4 */ + +/* END MERGE SLOT 4 */ + +/* BEGIN MERGE SLOT 5 */ +void mbedtls_mpi_mod_raw_add(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + const mbedtls_mpi_mod_modulus *N) +{ + mbedtls_mpi_uint carry, borrow; + carry = mbedtls_mpi_core_add(X, A, B, N->limbs); + borrow = mbedtls_mpi_core_sub(X, X, N->p, N->limbs); + (void) mbedtls_mpi_core_add_if(X, N->p, N->limbs, (unsigned) (carry ^ borrow)); +} +/* END MERGE SLOT 5 */ + +/* BEGIN MERGE SLOT 6 */ + +int mbedtls_mpi_mod_raw_canonical_to_modulus_rep( + mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N) +{ + switch (N->int_rep) { + case MBEDTLS_MPI_MOD_REP_MONTGOMERY: + return mbedtls_mpi_mod_raw_to_mont_rep(X, N); + case MBEDTLS_MPI_MOD_REP_OPT_RED: + return 0; + default: + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } +} + +int mbedtls_mpi_mod_raw_modulus_to_canonical_rep( + mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N) +{ + switch (N->int_rep) { + case MBEDTLS_MPI_MOD_REP_MONTGOMERY: + return mbedtls_mpi_mod_raw_from_mont_rep(X, N); + case MBEDTLS_MPI_MOD_REP_OPT_RED: + return 0; + default: + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } +} + +int mbedtls_mpi_mod_raw_random(mbedtls_mpi_uint *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_mod_modulus *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = mbedtls_mpi_core_random(X, min, N->p, N->limbs, f_rng, p_rng); + if (ret != 0) { + return ret; + } + return mbedtls_mpi_mod_raw_canonical_to_modulus_rep(X, N); +} + +/* END MERGE SLOT 6 */ + +/* BEGIN MERGE SLOT 7 */ +int mbedtls_mpi_mod_raw_to_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N) +{ + mbedtls_mpi_uint *T; + const size_t t_limbs = mbedtls_mpi_core_montmul_working_limbs(N->limbs); + + if ((T = (mbedtls_mpi_uint *) mbedtls_calloc(t_limbs, ciL)) == NULL) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + mbedtls_mpi_core_to_mont_rep(X, X, N->p, N->limbs, + N->rep.mont.mm, N->rep.mont.rr, T); + + mbedtls_platform_zeroize(T, t_limbs * ciL); + mbedtls_free(T); + return 0; +} + +int mbedtls_mpi_mod_raw_from_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N) +{ + const size_t t_limbs = mbedtls_mpi_core_montmul_working_limbs(N->limbs); + mbedtls_mpi_uint *T; + + if ((T = (mbedtls_mpi_uint *) mbedtls_calloc(t_limbs, ciL)) == NULL) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + mbedtls_mpi_core_from_mont_rep(X, X, N->p, N->limbs, N->rep.mont.mm, T); + + mbedtls_platform_zeroize(T, t_limbs * ciL); + mbedtls_free(T); + return 0; +} + +void mbedtls_mpi_mod_raw_neg(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_mod_modulus *N) +{ + mbedtls_mpi_core_sub(X, N->p, A, N->limbs); + + /* If A=0 initially, then X=N now. Detect this by + * subtracting N and catching the carry. */ + mbedtls_mpi_uint borrow = mbedtls_mpi_core_sub(X, X, N->p, N->limbs); + (void) mbedtls_mpi_core_add_if(X, N->p, N->limbs, (unsigned) borrow); +} +/* END MERGE SLOT 7 */ + +/* BEGIN MERGE SLOT 8 */ + +/* END MERGE SLOT 8 */ + +/* BEGIN MERGE SLOT 9 */ + +/* END MERGE SLOT 9 */ + +/* BEGIN MERGE SLOT 10 */ + +/* END MERGE SLOT 10 */ + +#endif /* MBEDTLS_BIGNUM_C */ diff --git a/r5dev/thirdparty/mbedtls/bignum_mod_raw.h b/r5dev/thirdparty/mbedtls/bignum_mod_raw.h new file mode 100644 index 00000000..a32500fe --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum_mod_raw.h @@ -0,0 +1,464 @@ +/** + * Low-level modular bignum functions + * + * This interface should only be used by the higher-level modular bignum + * module (bignum_mod.c) and the ECP module (ecp.c, ecp_curves.c). All other + * modules should use the high-level modular bignum interface (bignum_mod.h) + * or the legacy bignum interface (bignum.h). + * + * This is a low-level interface to operations on integers modulo which + * has no protection against passing invalid arguments such as arrays of + * the wrong size. The functions in bignum_mod.h provide a higher-level + * interface that includes protections against accidental misuse, at the + * expense of code size and sometimes more cumbersome memory management. + * + * The functions in this module obey the following conventions unless + * explicitly indicated otherwise: + * - **Modulus parameters**: the modulus is passed as a pointer to a structure + * of type #mbedtls_mpi_mod_modulus. The structure must be set up with an + * array of limbs storing the bignum value of the modulus. The modulus must + * be odd and is assumed to have no leading zeroes. The modulus is usually + * named \c N and is usually input-only. + * - **Bignum parameters**: Bignums are passed as pointers to an array of + * limbs. A limb has the type #mbedtls_mpi_uint. Unless otherwise specified: + * - Bignum parameters called \c A, \c B, ... are inputs, and are not + * modified by the function. + * - Bignum parameters called \c X, \c Y are outputs or input-output. + * The initial content of output-only parameters is ignored. + * - \c T is a temporary storage area. The initial content of such a + * parameter is ignored and the final content is unspecified. + * - **Bignum sizes**: bignum sizes are usually expressed by the \c limbs + * member of the modulus argument. All bignum parameters must have the same + * number of limbs as the modulus. All bignum sizes must be at least 1 and + * must be significantly less than #SIZE_MAX. The behavior if a size is 0 is + * undefined. + * - **Bignum representation**: the representation of inputs and outputs is + * specified by the \c int_rep field of the modulus for arithmetic + * functions. Utility functions may allow for different representation. + * - **Parameter ordering**: for bignum parameters, outputs come before inputs. + * The modulus is passed after other bignum input parameters. Temporaries + * come last. + * - **Aliasing**: in general, output bignums may be aliased to one or more + * inputs. Modulus values may not be aliased to any other parameter. Outputs + * may not be aliased to one another. Temporaries may not be aliased to any + * other parameter. + * - **Overlap**: apart from aliasing of limb array pointers (where two + * arguments are equal pointers), overlap is not supported and may result + * in undefined behavior. + * - **Error handling**: This is a low-level module. Functions generally do not + * try to protect against invalid arguments such as nonsensical sizes or + * null pointers. Note that passing bignums with a different size than the + * modulus may lead to buffer overflows. Some functions which allocate + * memory or handle reading/writing of bignums will return an error if + * memory allocation fails or if buffer sizes are invalid. + * - **Modular representatives**: all functions expect inputs to be in the + * range [0, \c N - 1] and guarantee outputs in the range [0, \c N - 1]. If + * an input is out of range, outputs are fully unspecified, though bignum + * values out of range should not cause buffer overflows (beware that this is + * not extensively tested). + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_BIGNUM_MOD_RAW_H +#define MBEDTLS_BIGNUM_MOD_RAW_H + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#include "bignum_mod.h" + +/** + * \brief Perform a safe conditional copy of an MPI which doesn't reveal + * whether the assignment was done or not. + * + * The size to copy is determined by \p N. + * + * \param[out] X The address of the destination MPI. + * This must be initialized. Must have enough limbs to + * store the full value of \p A. + * \param[in] A The address of the source MPI. This must be initialized. + * \param[in] N The address of the modulus related to \p X and \p A. + * \param assign The condition deciding whether to perform the + * assignment or not. Must be either 0 or 1: + * * \c 1: Perform the assignment `X = A`. + * * \c 0: Keep the original value of \p X. + * + * \note This function avoids leaking any information about whether + * the assignment was done or not. + * + * \warning If \p assign is neither 0 nor 1, the result of this function + * is indeterminate, and the resulting value in \p X might be + * neither its original value nor the value in \p A. + */ +void mbedtls_mpi_mod_raw_cond_assign(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_mod_modulus *N, + unsigned char assign); + +/** + * \brief Perform a safe conditional swap of two MPIs which doesn't reveal + * whether the swap was done or not. + * + * The size to swap is determined by \p N. + * + * \param[in,out] X The address of the first MPI. This must be initialized. + * \param[in,out] Y The address of the second MPI. This must be initialized. + * \param[in] N The address of the modulus related to \p X and \p Y. + * \param swap The condition deciding whether to perform + * the swap or not. Must be either 0 or 1: + * * \c 1: Swap the values of \p X and \p Y. + * * \c 0: Keep the original values of \p X and \p Y. + * + * \note This function avoids leaking any information about whether + * the swap was done or not. + * + * \warning If \p swap is neither 0 nor 1, the result of this function + * is indeterminate, and both \p X and \p Y might end up with + * values different to either of the original ones. + */ +void mbedtls_mpi_mod_raw_cond_swap(mbedtls_mpi_uint *X, + mbedtls_mpi_uint *Y, + const mbedtls_mpi_mod_modulus *N, + unsigned char swap); + +/** Import X from unsigned binary data. + * + * The MPI needs to have enough limbs to store the full value (including any + * most significant zero bytes in the input). + * + * \param[out] X The address of the MPI. The size is determined by \p N. + * (In particular, it must have at least as many limbs as + * the modulus \p N.) + * \param[in] N The address of the modulus related to \p X. + * \param[in] input The input buffer to import from. + * \param input_length The length in bytes of \p input. + * \param ext_rep The endianness of the number in the input buffer. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p X isn't + * large enough to hold the value in \p input. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the external representation + * of \p N is invalid or \p X is not less than \p N. + */ +int mbedtls_mpi_mod_raw_read(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N, + const unsigned char *input, + size_t input_length, + mbedtls_mpi_mod_ext_rep ext_rep); + +/** Export A into unsigned binary data. + * + * \param[in] A The address of the MPI. The size is determined by \p N. + * (In particular, it must have at least as many limbs as + * the modulus \p N.) + * \param[in] N The address of the modulus related to \p A. + * \param[out] output The output buffer to export to. + * \param output_length The length in bytes of \p output. + * \param ext_rep The endianness in which the number should be written into the output buffer. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p output isn't + * large enough to hold the value of \p A. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the external representation + * of \p N is invalid. + */ +int mbedtls_mpi_mod_raw_write(const mbedtls_mpi_uint *A, + const mbedtls_mpi_mod_modulus *N, + unsigned char *output, + size_t output_length, + mbedtls_mpi_mod_ext_rep ext_rep); + +/* BEGIN MERGE SLOT 1 */ + +/* END MERGE SLOT 1 */ + +/* BEGIN MERGE SLOT 2 */ + +/** \brief Subtract two MPIs, returning the residue modulo the specified + * modulus. + * + * The size of the operation is determined by \p N. \p A and \p B must have + * the same number of limbs as \p N. + * + * \p X may be aliased to \p A or \p B, or even both, but may not overlap + * either otherwise. + * + * \param[out] X The address of the result MPI. + * This must be initialized. Must have enough limbs to + * store the full value of the result. + * \param[in] A The address of the first MPI. This must be initialized. + * \param[in] B The address of the second MPI. This must be initialized. + * \param[in] N The address of the modulus. Used to perform a modulo + * operation on the result of the subtraction. + */ +void mbedtls_mpi_mod_raw_sub(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + const mbedtls_mpi_mod_modulus *N); + +/** \brief Multiply two MPIs, returning the residue modulo the specified + * modulus. + * + * \note Currently handles the case when `N->int_rep` is + * MBEDTLS_MPI_MOD_REP_MONTGOMERY. + * + * The size of the operation is determined by \p N. \p A, \p B and \p X must + * all be associated with the modulus \p N and must all have the same number + * of limbs as \p N. + * + * \p X may be aliased to \p A or \p B, or even both, but may not overlap + * either otherwise. They may not alias \p N (since they must be in canonical + * form, they cannot == \p N). + * + * \param[out] X The address of the result MPI. Must have the same + * number of limbs as \p N. + * On successful completion, \p X contains the result of + * the multiplication `A * B * R^-1` mod N where + * `R = 2^(biL * N->limbs)`. + * \param[in] A The address of the first MPI. + * \param[in] B The address of the second MPI. + * \param[in] N The address of the modulus. Used to perform a modulo + * operation on the result of the multiplication. + * \param[in,out] T Temporary storage of size at least 2 * N->limbs + 1 + * limbs. 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_mod_raw_mul(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + const mbedtls_mpi_mod_modulus *N, + mbedtls_mpi_uint *T); + +/* END MERGE SLOT 2 */ + +/* BEGIN MERGE SLOT 3 */ + +/** + * \brief Returns the number of limbs of working memory required for + * a call to `mbedtls_mpi_mod_raw_inv_prime()`. + * + * \note This will always be at least + * `mbedtls_mpi_core_montmul_working_limbs(AN_limbs)`, + * i.e. sufficient for a call to `mbedtls_mpi_core_montmul()`. + * + * \param AN_limbs The number of limbs in the input `A` and the modulus `N` + * (they must be the same size) that will be given to + * `mbedtls_mpi_mod_raw_inv_prime()`. + * + * \return The number of limbs of working memory required by + * `mbedtls_mpi_mod_raw_inv_prime()`. + */ +size_t mbedtls_mpi_mod_raw_inv_prime_working_limbs(size_t AN_limbs); + +/** + * \brief Perform fixed-width modular inversion of a Montgomery-form MPI with + * respect to a modulus \p N that must be prime. + * + * \p X may be aliased to \p A, but not to \p N or \p RR. + * + * \param[out] X The modular inverse of \p A with respect to \p N. + * Will be in Montgomery form. + * \param[in] A The number to calculate the modular inverse of. + * Must be in Montgomery form. Must not be 0. + * \param[in] N The modulus, as a little-endian array of length \p AN_limbs. + * Must be prime. + * \param AN_limbs The number of limbs in \p A, \p N and \p RR. + * \param[in] RR The precomputed residue of 2^{2*biL} modulo N, as a little- + * endian array of length \p AN_limbs. + * \param[in,out] T Temporary storage of at least the number of limbs returned + * by `mbedtls_mpi_mod_raw_inv_prime_working_limbs()`. + * Its initial content is unused and its final content is + * indeterminate. + * It must not alias or otherwise overlap any of the other + * parameters. + * It is up to the caller to zeroize \p T when it is no + * longer needed, and before freeing it if it was dynamically + * allocated. + */ +void mbedtls_mpi_mod_raw_inv_prime(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *N, + size_t AN_limbs, + const mbedtls_mpi_uint *RR, + mbedtls_mpi_uint *T); + +/* END MERGE SLOT 3 */ + +/* BEGIN MERGE SLOT 4 */ + +/* END MERGE SLOT 4 */ + +/* BEGIN MERGE SLOT 5 */ +/** + * \brief Perform a known-size modular addition. + * + * Calculate `A + B modulo N`. + * + * The number of limbs in each operand, and the result, is given by the + * modulus \p N. + * + * \p X may be aliased to \p A or \p B, or even both, but may not overlap + * either otherwise. + * + * \param[out] X The result of the modular addition. + * \param[in] A Little-endian presentation of the left operand. This + * must be smaller than \p N. + * \param[in] B Little-endian presentation of the right operand. This + * must be smaller than \p N. + * \param[in] N The address of the modulus. + */ +void mbedtls_mpi_mod_raw_add(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + const mbedtls_mpi_mod_modulus *N); +/* END MERGE SLOT 5 */ + +/* BEGIN MERGE SLOT 6 */ + +/** Convert an MPI from canonical representation (little-endian limb array) + * to the representation associated with the modulus. + * + * \param[in,out] X The limb array to convert. + * It must have as many limbs as \p N. + * It is converted in place. + * If this function returns an error, the content of \p X + * is unspecified. + * \param[in] N The modulus structure. + * + * \return \c 0 if successful. + * Otherwise an \c MBEDTLS_ERR_MPI_xxx error code. + */ +int mbedtls_mpi_mod_raw_canonical_to_modulus_rep( + mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N); + +/** Convert an MPI from the representation associated with the modulus + * to canonical representation (little-endian limb array). + * + * \param[in,out] X The limb array to convert. + * It must have as many limbs as \p N. + * It is converted in place. + * If this function returns an error, the content of \p X + * is unspecified. + * \param[in] N The modulus structure. + * + * \return \c 0 if successful. + * Otherwise an \c MBEDTLS_ERR_MPI_xxx error code. + */ +int mbedtls_mpi_mod_raw_modulus_to_canonical_rep( + mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N); + +/** Generate a random number uniformly in a range. + * + * This function generates a random number between \p min inclusive and + * \p N exclusive. + * + * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA) + * when the RNG is a suitably parametrized instance of HMAC_DRBG + * and \p min is \c 1. + * + * \note There are `N - min` possible outputs. The lower bound + * \p min can be reached, but the upper bound \p N cannot. + * + * \param X The destination MPI, in canonical representation modulo \p N. + * It must not be aliased with \p N or otherwise overlap it. + * \param min The minimum value to return. It must be strictly smaller + * than \b N. + * \param N The modulus. + * This is the upper bound of the output range, exclusive. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was + * unable to find a suitable value within a limited number + * of attempts. This has a negligible probability if \p N + * is significantly larger than \p min, which is the case + * for all usual cryptographic applications. + */ +int mbedtls_mpi_mod_raw_random(mbedtls_mpi_uint *X, + mbedtls_mpi_uint min, + const mbedtls_mpi_mod_modulus *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/* END MERGE SLOT 6 */ + +/* BEGIN MERGE SLOT 7 */ +/** Convert an MPI into Montgomery form. + * + * \param X The address of the MPI. + * Must have the same number of limbs as \p N. + * \param N The address of the modulus, which gives the size of + * the base `R` = 2^(biL*N->limbs). + * + * \return \c 0 if successful. + */ +int mbedtls_mpi_mod_raw_to_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N); + +/** Convert an MPI back from Montgomery representation. + * + * \param X The address of the MPI. + * Must have the same number of limbs as \p N. + * \param N The address of the modulus, which gives the size of + * the base `R`= 2^(biL*N->limbs). + * + * \return \c 0 if successful. + */ +int mbedtls_mpi_mod_raw_from_mont_rep(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N); + +/** \brief Perform fixed width modular negation. + * + * The size of the operation is determined by \p N. \p A must have + * the same number of limbs as \p N. + * + * \p X may be aliased to \p A. + * + * \param[out] X The result of the modular negation. + * This must be initialized. + * \param[in] A Little-endian presentation of the input operand. This + * must be less than or equal to \p N. + * \param[in] N The modulus to use. + */ +void mbedtls_mpi_mod_raw_neg(mbedtls_mpi_uint *X, + const mbedtls_mpi_uint *A, + const mbedtls_mpi_mod_modulus *N); +/* END MERGE SLOT 7 */ + +/* BEGIN MERGE SLOT 8 */ + +/* END MERGE SLOT 8 */ + +/* BEGIN MERGE SLOT 9 */ + +/* END MERGE SLOT 9 */ + +/* BEGIN MERGE SLOT 10 */ + +/* END MERGE SLOT 10 */ + +#endif /* MBEDTLS_BIGNUM_MOD_RAW_H */ diff --git a/r5dev/thirdparty/mbedtls/bignum_mod_raw_invasive.h b/r5dev/thirdparty/mbedtls/bignum_mod_raw_invasive.h new file mode 100644 index 00000000..ead83942 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bignum_mod_raw_invasive.h @@ -0,0 +1,46 @@ +/** + * \file bignum_mod_raw_invasive.h + * + * \brief Function declarations for invasive functions of Low-level + * modular bignum. + */ +/** + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_BIGNUM_MOD_RAW_INVASIVE_H +#define MBEDTLS_BIGNUM_MOD_RAW_INVASIVE_H + +#include "common.h" +#include "mbedtls/bignum.h" +#include "bignum_mod.h" + +#if defined(MBEDTLS_TEST_HOOKS) + +/** Convert the result of a quasi-reduction to its canonical representative. + * + * \param[in,out] X The address of the MPI to be converted. Must have the + * same number of limbs as \p N. The input value must + * be in range 0 <= X < 2N. + * \param[in] N The address of the modulus. + */ +MBEDTLS_STATIC_TESTABLE +void mbedtls_mpi_mod_raw_fix_quasi_reduction(mbedtls_mpi_uint *X, + const mbedtls_mpi_mod_modulus *N); + +#endif /* MBEDTLS_TEST_HOOKS */ + +#endif /* MBEDTLS_BIGNUM_MOD_RAW_INVASIVE_H */ diff --git a/r5dev/thirdparty/mbedtls/bn_mul.h b/r5dev/thirdparty/mbedtls/bn_mul.h new file mode 100644 index 00000000..ab59fbd6 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/bn_mul.h @@ -0,0 +1,1072 @@ +/** + * \file bn_mul.h + * + * \brief Multi-precision integer library + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Multiply source vector [s] with b, add result + * to destination vector [d] and set carry c. + * + * Currently supports: + * + * . IA-32 (386+) . AMD64 / EM64T + * . IA-32 (SSE2) . Motorola 68000 + * . PowerPC, 32-bit . MicroBlaze + * . PowerPC, 64-bit . TriCore + * . SPARC v8 . ARM v3+ + * . Alpha . MIPS32 + * . C, longlong . C, generic + */ +#ifndef MBEDTLS_BN_MUL_H +#define MBEDTLS_BN_MUL_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/bignum.h" + + +/* + * Conversion macros for embedded constants: + * build lists of mbedtls_mpi_uint's from lists of unsigned char's grouped by 8, 4 or 2 + */ +#if defined(MBEDTLS_HAVE_INT32) + +#define MBEDTLS_BYTES_TO_T_UINT_4(a, b, c, d) \ + ((mbedtls_mpi_uint) (a) << 0) | \ + ((mbedtls_mpi_uint) (b) << 8) | \ + ((mbedtls_mpi_uint) (c) << 16) | \ + ((mbedtls_mpi_uint) (d) << 24) + +#define MBEDTLS_BYTES_TO_T_UINT_2(a, b) \ + MBEDTLS_BYTES_TO_T_UINT_4(a, b, 0, 0) + +#define MBEDTLS_BYTES_TO_T_UINT_8(a, b, c, d, e, f, g, h) \ + MBEDTLS_BYTES_TO_T_UINT_4(a, b, c, d), \ + MBEDTLS_BYTES_TO_T_UINT_4(e, f, g, h) + +#else /* 64-bits */ + +#define MBEDTLS_BYTES_TO_T_UINT_8(a, b, c, d, e, f, g, h) \ + ((mbedtls_mpi_uint) (a) << 0) | \ + ((mbedtls_mpi_uint) (b) << 8) | \ + ((mbedtls_mpi_uint) (c) << 16) | \ + ((mbedtls_mpi_uint) (d) << 24) | \ + ((mbedtls_mpi_uint) (e) << 32) | \ + ((mbedtls_mpi_uint) (f) << 40) | \ + ((mbedtls_mpi_uint) (g) << 48) | \ + ((mbedtls_mpi_uint) (h) << 56) + +#define MBEDTLS_BYTES_TO_T_UINT_4(a, b, c, d) \ + MBEDTLS_BYTES_TO_T_UINT_8(a, b, c, d, 0, 0, 0, 0) + +#define MBEDTLS_BYTES_TO_T_UINT_2(a, b) \ + MBEDTLS_BYTES_TO_T_UINT_8(a, b, 0, 0, 0, 0, 0, 0) + +#endif /* bits in mbedtls_mpi_uint */ + +/* *INDENT-OFF* */ +#if defined(MBEDTLS_HAVE_ASM) + +/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */ +#if defined(__GNUC__) && \ + ( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 ) + +/* + * GCC < 5.0 treated the x86 ebx (which is used for the GOT) as a + * fixed reserved register when building as PIC, leading to errors + * like: bn_mul.h:46:13: error: PIC register clobbered by 'ebx' in 'asm' + * + * This is fixed by an improved register allocator in GCC 5+. From the + * release notes: + * Register allocation improvements: Reuse of the PIC hard register, + * instead of using a fixed register, was implemented on x86/x86-64 + * targets. This improves generated PIC code performance as more hard + * registers can be used. + */ +#if defined(__GNUC__) && __GNUC__ < 5 && defined(__PIC__) +#define MULADDC_CANNOT_USE_EBX +#endif + +/* + * Disable use of the i386 assembly code below if option -O0, to disable all + * compiler optimisations, is passed, detected with __OPTIMIZE__ + * This is done as the number of registers used in the assembly code doesn't + * work with the -O0 option. + */ +#if defined(__i386__) && defined(__OPTIMIZE__) && !defined(MULADDC_CANNOT_USE_EBX) + +#define MULADDC_X1_INIT \ + { mbedtls_mpi_uint t; \ + asm( \ + "movl %%ebx, %0 \n\t" \ + "movl %5, %%esi \n\t" \ + "movl %6, %%edi \n\t" \ + "movl %7, %%ecx \n\t" \ + "movl %8, %%ebx \n\t" + +#define MULADDC_X1_CORE \ + "lodsl \n\t" \ + "mull %%ebx \n\t" \ + "addl %%ecx, %%eax \n\t" \ + "adcl $0, %%edx \n\t" \ + "addl (%%edi), %%eax \n\t" \ + "adcl $0, %%edx \n\t" \ + "movl %%edx, %%ecx \n\t" \ + "stosl \n\t" + +#define MULADDC_X1_STOP \ + "movl %4, %%ebx \n\t" \ + "movl %%ecx, %1 \n\t" \ + "movl %%edi, %2 \n\t" \ + "movl %%esi, %3 \n\t" \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ebx", "ecx", "edx", "esi", "edi" \ + ); } + +#if defined(MBEDTLS_HAVE_SSE2) + +#define MULADDC_X8_INIT MULADDC_X1_INIT + +#define MULADDC_X8_CORE \ + "movd %%ecx, %%mm1 \n\t" \ + "movd %%ebx, %%mm0 \n\t" \ + "movd (%%edi), %%mm3 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd (%%esi), %%mm2 \n\t" \ + "pmuludq %%mm0, %%mm2 \n\t" \ + "movd 4(%%esi), %%mm4 \n\t" \ + "pmuludq %%mm0, %%mm4 \n\t" \ + "movd 8(%%esi), %%mm6 \n\t" \ + "pmuludq %%mm0, %%mm6 \n\t" \ + "movd 12(%%esi), %%mm7 \n\t" \ + "pmuludq %%mm0, %%mm7 \n\t" \ + "paddq %%mm2, %%mm1 \n\t" \ + "movd 4(%%edi), %%mm3 \n\t" \ + "paddq %%mm4, %%mm3 \n\t" \ + "movd 8(%%edi), %%mm5 \n\t" \ + "paddq %%mm6, %%mm5 \n\t" \ + "movd 12(%%edi), %%mm4 \n\t" \ + "paddq %%mm4, %%mm7 \n\t" \ + "movd %%mm1, (%%edi) \n\t" \ + "movd 16(%%esi), %%mm2 \n\t" \ + "pmuludq %%mm0, %%mm2 \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd 20(%%esi), %%mm4 \n\t" \ + "pmuludq %%mm0, %%mm4 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd 24(%%esi), %%mm6 \n\t" \ + "pmuludq %%mm0, %%mm6 \n\t" \ + "movd %%mm1, 4(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd 28(%%esi), %%mm3 \n\t" \ + "pmuludq %%mm0, %%mm3 \n\t" \ + "paddq %%mm5, %%mm1 \n\t" \ + "movd 16(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm2 \n\t" \ + "movd %%mm1, 8(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm7, %%mm1 \n\t" \ + "movd 20(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm4 \n\t" \ + "movd %%mm1, 12(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm2, %%mm1 \n\t" \ + "movd 24(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm6 \n\t" \ + "movd %%mm1, 16(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm4, %%mm1 \n\t" \ + "movd 28(%%edi), %%mm5 \n\t" \ + "paddq %%mm5, %%mm3 \n\t" \ + "movd %%mm1, 20(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm6, %%mm1 \n\t" \ + "movd %%mm1, 24(%%edi) \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "paddq %%mm3, %%mm1 \n\t" \ + "movd %%mm1, 28(%%edi) \n\t" \ + "addl $32, %%edi \n\t" \ + "addl $32, %%esi \n\t" \ + "psrlq $32, %%mm1 \n\t" \ + "movd %%mm1, %%ecx \n\t" + +#define MULADDC_X8_STOP \ + "emms \n\t" \ + "movl %4, %%ebx \n\t" \ + "movl %%ecx, %1 \n\t" \ + "movl %%edi, %2 \n\t" \ + "movl %%esi, %3 \n\t" \ + : "=m" (t), "=m" (c), "=m" (d), "=m" (s) \ + : "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \ + : "eax", "ebx", "ecx", "edx", "esi", "edi" \ + ); } \ + +#endif /* SSE2 */ + +#endif /* i386 */ + +#if defined(__amd64__) || defined (__x86_64__) + +#define MULADDC_X1_INIT \ + asm( \ + "xorq %%r8, %%r8\n" + +#define MULADDC_X1_CORE \ + "movq (%%rsi), %%rax\n" \ + "mulq %%rbx\n" \ + "addq $8, %%rsi\n" \ + "addq %%rcx, %%rax\n" \ + "movq %%r8, %%rcx\n" \ + "adcq $0, %%rdx\n" \ + "nop \n" \ + "addq %%rax, (%%rdi)\n" \ + "adcq %%rdx, %%rcx\n" \ + "addq $8, %%rdi\n" + +#define MULADDC_X1_STOP \ + : "+c" (c), "+D" (d), "+S" (s), "+m" (*(uint64_t (*)[16]) d) \ + : "b" (b), "m" (*(const uint64_t (*)[16]) s) \ + : "rax", "rdx", "r8" \ + ); + +#endif /* AMD64 */ + +#if defined(__aarch64__) + +#define MULADDC_X1_INIT \ + asm( + +#define MULADDC_X1_CORE \ + "ldr x4, [%2], #8 \n\t" \ + "ldr x5, [%1] \n\t" \ + "mul x6, x4, %4 \n\t" \ + "umulh x7, x4, %4 \n\t" \ + "adds x5, x5, x6 \n\t" \ + "adc x7, x7, xzr \n\t" \ + "adds x5, x5, %0 \n\t" \ + "adc %0, x7, xzr \n\t" \ + "str x5, [%1], #8 \n\t" + +#define MULADDC_X1_STOP \ + : "+r" (c), "+r" (d), "+r" (s), "+m" (*(uint64_t (*)[16]) d) \ + : "r" (b), "m" (*(const uint64_t (*)[16]) s) \ + : "x4", "x5", "x6", "x7", "cc" \ + ); + +#endif /* Aarch64 */ + +#if defined(__mc68020__) || defined(__mcpu32__) + +#define MULADDC_X1_INIT \ + asm( \ + "movl %3, %%a2 \n\t" \ + "movl %4, %%a3 \n\t" \ + "movl %5, %%d3 \n\t" \ + "movl %6, %%d2 \n\t" \ + "moveq #0, %%d0 \n\t" + +#define MULADDC_X1_CORE \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "moveq #0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "addxl %%d4, %%d3 \n\t" + +#define MULADDC_X1_STOP \ + "movl %%d3, %0 \n\t" \ + "movl %%a3, %1 \n\t" \ + "movl %%a2, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "d0", "d1", "d2", "d3", "d4", "a2", "a3" \ + ); + +#define MULADDC_X8_INIT MULADDC_X1_INIT + +#define MULADDC_X8_CORE \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d4:%%d1 \n\t" \ + "addxl %%d3, %%d1 \n\t" \ + "addxl %%d0, %%d4 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "movel %%a2@+, %%d1 \n\t" \ + "mulul %%d2, %%d3:%%d1 \n\t" \ + "addxl %%d4, %%d1 \n\t" \ + "addxl %%d0, %%d3 \n\t" \ + "addl %%d1, %%a3@+ \n\t" \ + "addxl %%d0, %%d3 \n\t" + +#define MULADDC_X8_STOP MULADDC_X1_STOP + +#endif /* MC68000 */ + +#if defined(__powerpc64__) || defined(__ppc64__) + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_X1_INIT \ + asm( \ + "ld r3, %3 \n\t" \ + "ld r4, %4 \n\t" \ + "ld r5, %5 \n\t" \ + "ld r6, %6 \n\t" \ + "addi r3, r3, -8 \n\t" \ + "addi r4, r4, -8 \n\t" \ + "addic r5, r5, 0 \n\t" + +#define MULADDC_X1_CORE \ + "ldu r7, 8(r3) \n\t" \ + "mulld r8, r7, r6 \n\t" \ + "mulhdu r9, r7, r6 \n\t" \ + "adde r8, r8, r5 \n\t" \ + "ld r7, 8(r4) \n\t" \ + "addze r5, r9 \n\t" \ + "addc r8, r8, r7 \n\t" \ + "stdu r8, 8(r4) \n\t" + +#define MULADDC_X1_STOP \ + "addze r5, r5 \n\t" \ + "addi r4, r4, 8 \n\t" \ + "addi r3, r3, 8 \n\t" \ + "std r5, %0 \n\t" \ + "std r4, %1 \n\t" \ + "std r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + + +#else /* __MACH__ && __APPLE__ */ + +#define MULADDC_X1_INIT \ + asm( \ + "ld %%r3, %3 \n\t" \ + "ld %%r4, %4 \n\t" \ + "ld %%r5, %5 \n\t" \ + "ld %%r6, %6 \n\t" \ + "addi %%r3, %%r3, -8 \n\t" \ + "addi %%r4, %%r4, -8 \n\t" \ + "addic %%r5, %%r5, 0 \n\t" + +#define MULADDC_X1_CORE \ + "ldu %%r7, 8(%%r3) \n\t" \ + "mulld %%r8, %%r7, %%r6 \n\t" \ + "mulhdu %%r9, %%r7, %%r6 \n\t" \ + "adde %%r8, %%r8, %%r5 \n\t" \ + "ld %%r7, 8(%%r4) \n\t" \ + "addze %%r5, %%r9 \n\t" \ + "addc %%r8, %%r8, %%r7 \n\t" \ + "stdu %%r8, 8(%%r4) \n\t" + +#define MULADDC_X1_STOP \ + "addze %%r5, %%r5 \n\t" \ + "addi %%r4, %%r4, 8 \n\t" \ + "addi %%r3, %%r3, 8 \n\t" \ + "std %%r5, %0 \n\t" \ + "std %%r4, %1 \n\t" \ + "std %%r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#endif /* __MACH__ && __APPLE__ */ + +#elif defined(__powerpc__) || defined(__ppc__) /* end PPC64/begin PPC32 */ + +#if defined(__MACH__) && defined(__APPLE__) + +#define MULADDC_X1_INIT \ + asm( \ + "lwz r3, %3 \n\t" \ + "lwz r4, %4 \n\t" \ + "lwz r5, %5 \n\t" \ + "lwz r6, %6 \n\t" \ + "addi r3, r3, -4 \n\t" \ + "addi r4, r4, -4 \n\t" \ + "addic r5, r5, 0 \n\t" + +#define MULADDC_X1_CORE \ + "lwzu r7, 4(r3) \n\t" \ + "mullw r8, r7, r6 \n\t" \ + "mulhwu r9, r7, r6 \n\t" \ + "adde r8, r8, r5 \n\t" \ + "lwz r7, 4(r4) \n\t" \ + "addze r5, r9 \n\t" \ + "addc r8, r8, r7 \n\t" \ + "stwu r8, 4(r4) \n\t" + +#define MULADDC_X1_STOP \ + "addze r5, r5 \n\t" \ + "addi r4, r4, 4 \n\t" \ + "addi r3, r3, 4 \n\t" \ + "stw r5, %0 \n\t" \ + "stw r4, %1 \n\t" \ + "stw r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#else /* __MACH__ && __APPLE__ */ + +#define MULADDC_X1_INIT \ + asm( \ + "lwz %%r3, %3 \n\t" \ + "lwz %%r4, %4 \n\t" \ + "lwz %%r5, %5 \n\t" \ + "lwz %%r6, %6 \n\t" \ + "addi %%r3, %%r3, -4 \n\t" \ + "addi %%r4, %%r4, -4 \n\t" \ + "addic %%r5, %%r5, 0 \n\t" + +#define MULADDC_X1_CORE \ + "lwzu %%r7, 4(%%r3) \n\t" \ + "mullw %%r8, %%r7, %%r6 \n\t" \ + "mulhwu %%r9, %%r7, %%r6 \n\t" \ + "adde %%r8, %%r8, %%r5 \n\t" \ + "lwz %%r7, 4(%%r4) \n\t" \ + "addze %%r5, %%r9 \n\t" \ + "addc %%r8, %%r8, %%r7 \n\t" \ + "stwu %%r8, 4(%%r4) \n\t" + +#define MULADDC_X1_STOP \ + "addze %%r5, %%r5 \n\t" \ + "addi %%r4, %%r4, 4 \n\t" \ + "addi %%r3, %%r3, 4 \n\t" \ + "stw %%r5, %0 \n\t" \ + "stw %%r4, %1 \n\t" \ + "stw %%r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", "r9" \ + ); + +#endif /* __MACH__ && __APPLE__ */ + +#endif /* PPC32 */ + +/* + * The Sparc(64) assembly is reported to be broken. + * Disable it for now, until we're able to fix it. + */ +#if 0 && defined(__sparc__) +#if defined(__sparc64__) + +#define MULADDC_X1_INIT \ + asm( \ + "ldx %3, %%o0 \n\t" \ + "ldx %4, %%o1 \n\t" \ + "ld %5, %%o2 \n\t" \ + "ld %6, %%o3 \n\t" + +#define MULADDC_X1_CORE \ + "ld [%%o0], %%o4 \n\t" \ + "inc 4, %%o0 \n\t" \ + "ld [%%o1], %%o5 \n\t" \ + "umul %%o3, %%o4, %%o4 \n\t" \ + "addcc %%o4, %%o2, %%o4 \n\t" \ + "rd %%y, %%g1 \n\t" \ + "addx %%g1, 0, %%g1 \n\t" \ + "addcc %%o4, %%o5, %%o4 \n\t" \ + "st %%o4, [%%o1] \n\t" \ + "addx %%g1, 0, %%o2 \n\t" \ + "inc 4, %%o1 \n\t" + +#define MULADDC_X1_STOP \ + "st %%o2, %0 \n\t" \ + "stx %%o1, %1 \n\t" \ + "stx %%o0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); + +#else /* __sparc64__ */ + +#define MULADDC_X1_INIT \ + asm( \ + "ld %3, %%o0 \n\t" \ + "ld %4, %%o1 \n\t" \ + "ld %5, %%o2 \n\t" \ + "ld %6, %%o3 \n\t" + +#define MULADDC_X1_CORE \ + "ld [%%o0], %%o4 \n\t" \ + "inc 4, %%o0 \n\t" \ + "ld [%%o1], %%o5 \n\t" \ + "umul %%o3, %%o4, %%o4 \n\t" \ + "addcc %%o4, %%o2, %%o4 \n\t" \ + "rd %%y, %%g1 \n\t" \ + "addx %%g1, 0, %%g1 \n\t" \ + "addcc %%o4, %%o5, %%o4 \n\t" \ + "st %%o4, [%%o1] \n\t" \ + "addx %%g1, 0, %%o2 \n\t" \ + "inc 4, %%o1 \n\t" + +#define MULADDC_X1_STOP \ + "st %%o2, %0 \n\t" \ + "st %%o1, %1 \n\t" \ + "st %%o0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "g1", "o0", "o1", "o2", "o3", "o4", \ + "o5" \ + ); + +#endif /* __sparc64__ */ +#endif /* __sparc__ */ + +#if defined(__microblaze__) || defined(microblaze) + +#define MULADDC_X1_INIT \ + asm( \ + "lwi r3, %3 \n\t" \ + "lwi r4, %4 \n\t" \ + "lwi r5, %5 \n\t" \ + "lwi r6, %6 \n\t" \ + "andi r7, r6, 0xffff \n\t" \ + "bsrli r6, r6, 16 \n\t" + +#if(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define MULADDC_LHUI \ + "lhui r9, r3, 0 \n\t" \ + "addi r3, r3, 2 \n\t" \ + "lhui r8, r3, 0 \n\t" +#else +#define MULADDC_LHUI \ + "lhui r8, r3, 0 \n\t" \ + "addi r3, r3, 2 \n\t" \ + "lhui r9, r3, 0 \n\t" +#endif + +#define MULADDC_X1_CORE \ + MULADDC_LHUI \ + "addi r3, r3, 2 \n\t" \ + "mul r10, r9, r6 \n\t" \ + "mul r11, r8, r7 \n\t" \ + "mul r12, r9, r7 \n\t" \ + "mul r13, r8, r6 \n\t" \ + "bsrli r8, r10, 16 \n\t" \ + "bsrli r9, r11, 16 \n\t" \ + "add r13, r13, r8 \n\t" \ + "add r13, r13, r9 \n\t" \ + "bslli r10, r10, 16 \n\t" \ + "bslli r11, r11, 16 \n\t" \ + "add r12, r12, r10 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "add r12, r12, r11 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "lwi r10, r4, 0 \n\t" \ + "add r12, r12, r10 \n\t" \ + "addc r13, r13, r0 \n\t" \ + "add r12, r12, r5 \n\t" \ + "addc r5, r13, r0 \n\t" \ + "swi r12, r4, 0 \n\t" \ + "addi r4, r4, 4 \n\t" + +#define MULADDC_X1_STOP \ + "swi r5, %0 \n\t" \ + "swi r4, %1 \n\t" \ + "swi r3, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r3", "r4", "r5", "r6", "r7", "r8", \ + "r9", "r10", "r11", "r12", "r13" \ + ); + +#endif /* MicroBlaze */ + +#if defined(__tricore__) + +#define MULADDC_X1_INIT \ + asm( \ + "ld.a %%a2, %3 \n\t" \ + "ld.a %%a3, %4 \n\t" \ + "ld.w %%d4, %5 \n\t" \ + "ld.w %%d1, %6 \n\t" \ + "xor %%d5, %%d5 \n\t" + +#define MULADDC_X1_CORE \ + "ld.w %%d0, [%%a2+] \n\t" \ + "madd.u %%e2, %%e4, %%d0, %%d1 \n\t" \ + "ld.w %%d0, [%%a3] \n\t" \ + "addx %%d2, %%d2, %%d0 \n\t" \ + "addc %%d3, %%d3, 0 \n\t" \ + "mov %%d4, %%d3 \n\t" \ + "st.w [%%a3+], %%d2 \n\t" + +#define MULADDC_X1_STOP \ + "st.w %0, %%d4 \n\t" \ + "st.a %1, %%a3 \n\t" \ + "st.a %2, %%a2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "d0", "d1", "e2", "d4", "a2", "a3" \ + ); + +#endif /* TriCore */ + +/* + * Note, gcc -O0 by default uses r7 for the frame pointer, so it complains about + * our use of r7 below, unless -fomit-frame-pointer is passed. + * + * On the other hand, -fomit-frame-pointer is implied by any -Ox options with + * x !=0, which we can detect using __OPTIMIZE__ (which is also defined by + * clang and armcc5 under the same conditions). + * + * So, only use the optimized assembly below for optimized build, which avoids + * the build error and is pretty reasonable anyway. + */ +#if defined(__GNUC__) && !defined(__OPTIMIZE__) +#define MULADDC_CANNOT_USE_R7 +#endif + +#if defined(__arm__) && !defined(MULADDC_CANNOT_USE_R7) + +#if defined(__thumb__) && !defined(__thumb2__) + +#define MULADDC_X1_INIT \ + asm( \ + "ldr r0, %3 \n\t" \ + "ldr r1, %4 \n\t" \ + "ldr r2, %5 \n\t" \ + "ldr r3, %6 \n\t" \ + "lsr r7, r3, #16 \n\t" \ + "mov r9, r7 \n\t" \ + "lsl r7, r3, #16 \n\t" \ + "lsr r7, r7, #16 \n\t" \ + "mov r8, r7 \n\t" + +#define MULADDC_X1_CORE \ + "ldmia r0!, {r6} \n\t" \ + "lsr r7, r6, #16 \n\t" \ + "lsl r6, r6, #16 \n\t" \ + "lsr r6, r6, #16 \n\t" \ + "mov r4, r8 \n\t" \ + "mul r4, r6 \n\t" \ + "mov r3, r9 \n\t" \ + "mul r6, r3 \n\t" \ + "mov r5, r9 \n\t" \ + "mul r5, r7 \n\t" \ + "mov r3, r8 \n\t" \ + "mul r7, r3 \n\t" \ + "lsr r3, r6, #16 \n\t" \ + "add r5, r5, r3 \n\t" \ + "lsr r3, r7, #16 \n\t" \ + "add r5, r5, r3 \n\t" \ + "add r4, r4, r2 \n\t" \ + "mov r2, #0 \n\t" \ + "adc r5, r2 \n\t" \ + "lsl r3, r6, #16 \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r5, r2 \n\t" \ + "lsl r3, r7, #16 \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r5, r2 \n\t" \ + "ldr r3, [r1] \n\t" \ + "add r4, r4, r3 \n\t" \ + "adc r2, r5 \n\t" \ + "stmia r1!, {r4} \n\t" + +#define MULADDC_X1_STOP \ + "str r2, %0 \n\t" \ + "str r1, %1 \n\t" \ + "str r0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "r8", "r9", "cc" \ + ); + +#elif (__ARM_ARCH >= 6) && \ + defined (__ARM_FEATURE_DSP) && (__ARM_FEATURE_DSP == 1) + +#define MULADDC_X1_INIT \ + { \ + mbedtls_mpi_uint tmp_a, tmp_b; \ + asm volatile ( + +#define MULADDC_X1_CORE \ + ".p2align 2 \n\t" \ + "ldr %[a], [%[in]], #4 \n\t" \ + "ldr %[b], [%[acc]] \n\t" \ + "umaal %[b], %[carry], %[scalar], %[a] \n\t" \ + "str %[b], [%[acc]], #4 \n\t" + +#define MULADDC_X1_STOP \ + : [a] "=&r" (tmp_a), \ + [b] "=&r" (tmp_b), \ + [in] "+r" (s), \ + [acc] "+r" (d), \ + [carry] "+l" (c) \ + : [scalar] "r" (b) \ + : "memory" \ + ); \ + } + +#define MULADDC_X2_INIT \ + { \ + mbedtls_mpi_uint tmp_a0, tmp_b0; \ + mbedtls_mpi_uint tmp_a1, tmp_b1; \ + asm volatile ( + + /* - Make sure loop is 4-byte aligned to avoid stalls + * upon repeated non-word aligned instructions in + * some microarchitectures. + * - Don't use ldm with post-increment or back-to-back + * loads with post-increment and same address register + * to avoid stalls on some microarchitectures. + * - Bunch loads and stores to reduce latency on some + * microarchitectures. E.g., on Cortex-M4, the first + * in a series of load/store operations has latency + * 2 cycles, while subsequent loads/stores are single-cycle. */ +#define MULADDC_X2_CORE \ + ".p2align 2 \n\t" \ + "ldr %[a0], [%[in]], #+8 \n\t" \ + "ldr %[b0], [%[acc]], #+8 \n\t" \ + "ldr %[a1], [%[in], #-4] \n\t" \ + "ldr %[b1], [%[acc], #-4] \n\t" \ + "umaal %[b0], %[carry], %[scalar], %[a0] \n\t" \ + "umaal %[b1], %[carry], %[scalar], %[a1] \n\t" \ + "str %[b0], [%[acc], #-8] \n\t" \ + "str %[b1], [%[acc], #-4] \n\t" + +#define MULADDC_X2_STOP \ + : [a0] "=&r" (tmp_a0), \ + [b0] "=&r" (tmp_b0), \ + [a1] "=&r" (tmp_a1), \ + [b1] "=&r" (tmp_b1), \ + [in] "+r" (s), \ + [acc] "+r" (d), \ + [carry] "+l" (c) \ + : [scalar] "r" (b) \ + : "memory" \ + ); \ + } + +#else + +#define MULADDC_X1_INIT \ + asm( \ + "ldr r0, %3 \n\t" \ + "ldr r1, %4 \n\t" \ + "ldr r2, %5 \n\t" \ + "ldr r3, %6 \n\t" + +#define MULADDC_X1_CORE \ + "ldr r4, [r0], #4 \n\t" \ + "mov r5, #0 \n\t" \ + "ldr r6, [r1] \n\t" \ + "umlal r2, r5, r3, r4 \n\t" \ + "adds r7, r6, r2 \n\t" \ + "adc r2, r5, #0 \n\t" \ + "str r7, [r1], #4 \n\t" + +#define MULADDC_X1_STOP \ + "str r2, %0 \n\t" \ + "str r1, %1 \n\t" \ + "str r0, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "r0", "r1", "r2", "r3", "r4", "r5", \ + "r6", "r7", "cc" \ + ); + +#endif /* Thumb */ + +#endif /* ARMv3 */ + +#if defined(__alpha__) + +#define MULADDC_X1_INIT \ + asm( \ + "ldq $1, %3 \n\t" \ + "ldq $2, %4 \n\t" \ + "ldq $3, %5 \n\t" \ + "ldq $4, %6 \n\t" + +#define MULADDC_X1_CORE \ + "ldq $6, 0($1) \n\t" \ + "addq $1, 8, $1 \n\t" \ + "mulq $6, $4, $7 \n\t" \ + "umulh $6, $4, $6 \n\t" \ + "addq $7, $3, $7 \n\t" \ + "cmpult $7, $3, $3 \n\t" \ + "ldq $5, 0($2) \n\t" \ + "addq $7, $5, $7 \n\t" \ + "cmpult $7, $5, $5 \n\t" \ + "stq $7, 0($2) \n\t" \ + "addq $2, 8, $2 \n\t" \ + "addq $6, $3, $3 \n\t" \ + "addq $5, $3, $3 \n\t" + +#define MULADDC_X1_STOP \ + "stq $3, %0 \n\t" \ + "stq $2, %1 \n\t" \ + "stq $1, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "$1", "$2", "$3", "$4", "$5", "$6", "$7" \ + ); +#endif /* Alpha */ + +#if defined(__mips__) && !defined(__mips64) + +#define MULADDC_X1_INIT \ + asm( \ + "lw $10, %3 \n\t" \ + "lw $11, %4 \n\t" \ + "lw $12, %5 \n\t" \ + "lw $13, %6 \n\t" + +#define MULADDC_X1_CORE \ + "lw $14, 0($10) \n\t" \ + "multu $13, $14 \n\t" \ + "addi $10, $10, 4 \n\t" \ + "mflo $14 \n\t" \ + "mfhi $9 \n\t" \ + "addu $14, $12, $14 \n\t" \ + "lw $15, 0($11) \n\t" \ + "sltu $12, $14, $12 \n\t" \ + "addu $15, $14, $15 \n\t" \ + "sltu $14, $15, $14 \n\t" \ + "addu $12, $12, $9 \n\t" \ + "sw $15, 0($11) \n\t" \ + "addu $12, $12, $14 \n\t" \ + "addi $11, $11, 4 \n\t" + +#define MULADDC_X1_STOP \ + "sw $12, %0 \n\t" \ + "sw $11, %1 \n\t" \ + "sw $10, %2 \n\t" \ + : "=m" (c), "=m" (d), "=m" (s) \ + : "m" (s), "m" (d), "m" (c), "m" (b) \ + : "$9", "$10", "$11", "$12", "$13", "$14", "$15", "lo", "hi" \ + ); + +#endif /* MIPS */ +#endif /* GNUC */ + +#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__) + +#define MULADDC_X1_INIT \ + __asm mov esi, s \ + __asm mov edi, d \ + __asm mov ecx, c \ + __asm mov ebx, b + +#define MULADDC_X1_CORE \ + __asm lodsd \ + __asm mul ebx \ + __asm add eax, ecx \ + __asm adc edx, 0 \ + __asm add eax, [edi] \ + __asm adc edx, 0 \ + __asm mov ecx, edx \ + __asm stosd + +#define MULADDC_X1_STOP \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi + +#if defined(MBEDTLS_HAVE_SSE2) + +#define EMIT __asm _emit + +#define MULADDC_X8_INIT MULADDC_X1_INIT + +#define MULADDC_X8_CORE \ + EMIT 0x0F EMIT 0x6E EMIT 0xC9 \ + EMIT 0x0F EMIT 0x6E EMIT 0xC3 \ + EMIT 0x0F EMIT 0x6E EMIT 0x1F \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x16 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xEE \ + EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xFC \ + EMIT 0x0F EMIT 0x7E EMIT 0x0F \ + EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \ + EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \ + EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCD \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCF \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCA \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCC \ + EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \ + EMIT 0x0F EMIT 0xD4 EMIT 0xDD \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCE \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0xD4 EMIT 0xCB \ + EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \ + EMIT 0x83 EMIT 0xC7 EMIT 0x20 \ + EMIT 0x83 EMIT 0xC6 EMIT 0x20 \ + EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \ + EMIT 0x0F EMIT 0x7E EMIT 0xC9 + +#define MULADDC_X8_STOP \ + EMIT 0x0F EMIT 0x77 \ + __asm mov c, ecx \ + __asm mov d, edi \ + __asm mov s, esi + +#endif /* SSE2 */ +#endif /* MSVC */ + +#endif /* MBEDTLS_HAVE_ASM */ + +#if !defined(MULADDC_X1_CORE) +#if defined(MBEDTLS_HAVE_UDBL) + +#define MULADDC_X1_INIT \ +{ \ + mbedtls_t_udbl r; \ + mbedtls_mpi_uint r0, r1; + +#define MULADDC_X1_CORE \ + r = *(s++) * (mbedtls_t_udbl) b; \ + r0 = (mbedtls_mpi_uint) r; \ + r1 = (mbedtls_mpi_uint)( r >> biL ); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_X1_STOP \ +} + +#else /* MBEDTLS_HAVE_UDBL */ + +#define MULADDC_X1_INIT \ +{ \ + mbedtls_mpi_uint s0, s1, b0, b1; \ + mbedtls_mpi_uint r0, r1, rx, ry; \ + b0 = ( b << biH ) >> biH; \ + b1 = ( b >> biH ); + +#define MULADDC_X1_CORE \ + s0 = ( *s << biH ) >> biH; \ + s1 = ( *s >> biH ); s++; \ + rx = s0 * b1; r0 = s0 * b0; \ + ry = s1 * b0; r1 = s1 * b1; \ + r1 += ( rx >> biH ); \ + r1 += ( ry >> biH ); \ + rx <<= biH; ry <<= biH; \ + r0 += rx; r1 += (r0 < rx); \ + r0 += ry; r1 += (r0 < ry); \ + r0 += c; r1 += (r0 < c); \ + r0 += *d; r1 += (r0 < *d); \ + c = r1; *(d++) = r0; + +#define MULADDC_X1_STOP \ +} + +#endif /* C (longlong) */ +#endif /* C (generic) */ + +#if !defined(MULADDC_X2_CORE) +#define MULADDC_X2_INIT MULADDC_X1_INIT +#define MULADDC_X2_STOP MULADDC_X1_STOP +#define MULADDC_X2_CORE MULADDC_X1_CORE MULADDC_X1_CORE +#endif /* MULADDC_X2_CORE */ + +#if !defined(MULADDC_X4_CORE) +#define MULADDC_X4_INIT MULADDC_X2_INIT +#define MULADDC_X4_STOP MULADDC_X2_STOP +#define MULADDC_X4_CORE MULADDC_X2_CORE MULADDC_X2_CORE +#endif /* MULADDC_X4_CORE */ + +#if !defined(MULADDC_X8_CORE) +#define MULADDC_X8_INIT MULADDC_X4_INIT +#define MULADDC_X8_STOP MULADDC_X4_STOP +#define MULADDC_X8_CORE MULADDC_X4_CORE MULADDC_X4_CORE +#endif /* MULADDC_X8_CORE */ + +/* *INDENT-ON* */ +#endif /* bn_mul.h */ diff --git a/r5dev/thirdparty/mbedtls/camellia.c b/r5dev/thirdparty/mbedtls/camellia.c new file mode 100644 index 00000000..409727d0 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/camellia.c @@ -0,0 +1,1056 @@ +/* + * Camellia implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The Camellia block cipher was designed by NTT and Mitsubishi Electric + * Corporation. + * + * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/01espec.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_CAMELLIA_C) + +#include "mbedtls/camellia.h" +#include "mbedtls/platform_util.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_CAMELLIA_ALT) + +static const unsigned char SIGMA_CHARS[6][8] = +{ + { 0xa0, 0x9e, 0x66, 0x7f, 0x3b, 0xcc, 0x90, 0x8b }, + { 0xb6, 0x7a, 0xe8, 0x58, 0x4c, 0xaa, 0x73, 0xb2 }, + { 0xc6, 0xef, 0x37, 0x2f, 0xe9, 0x4f, 0x82, 0xbe }, + { 0x54, 0xff, 0x53, 0xa5, 0xf1, 0xd3, 0x6f, 0x1c }, + { 0x10, 0xe5, 0x27, 0xfa, 0xde, 0x68, 0x2d, 0x1d }, + { 0xb0, 0x56, 0x88, 0xc2, 0xb3, 0xe6, 0xc1, 0xfd } +}; + +#if defined(MBEDTLS_CAMELLIA_SMALL_MEMORY) + +static const unsigned char FSb[256] = +{ + 112, 130, 44, 236, 179, 39, 192, 229, 228, 133, 87, 53, 234, 12, 174, 65, + 35, 239, 107, 147, 69, 25, 165, 33, 237, 14, 79, 78, 29, 101, 146, 189, + 134, 184, 175, 143, 124, 235, 31, 206, 62, 48, 220, 95, 94, 197, 11, 26, + 166, 225, 57, 202, 213, 71, 93, 61, 217, 1, 90, 214, 81, 86, 108, 77, + 139, 13, 154, 102, 251, 204, 176, 45, 116, 18, 43, 32, 240, 177, 132, 153, + 223, 76, 203, 194, 52, 126, 118, 5, 109, 183, 169, 49, 209, 23, 4, 215, + 20, 88, 58, 97, 222, 27, 17, 28, 50, 15, 156, 22, 83, 24, 242, 34, + 254, 68, 207, 178, 195, 181, 122, 145, 36, 8, 232, 168, 96, 252, 105, 80, + 170, 208, 160, 125, 161, 137, 98, 151, 84, 91, 30, 149, 224, 255, 100, 210, + 16, 196, 0, 72, 163, 247, 117, 219, 138, 3, 230, 218, 9, 63, 221, 148, + 135, 92, 131, 2, 205, 74, 144, 51, 115, 103, 246, 243, 157, 127, 191, 226, + 82, 155, 216, 38, 200, 55, 198, 59, 129, 150, 111, 75, 19, 190, 99, 46, + 233, 121, 167, 140, 159, 110, 188, 142, 41, 245, 249, 182, 47, 253, 180, 89, + 120, 152, 6, 106, 231, 70, 113, 186, 212, 37, 171, 66, 136, 162, 141, 250, + 114, 7, 185, 85, 248, 238, 172, 10, 54, 73, 42, 104, 60, 56, 241, 164, + 64, 40, 211, 123, 187, 201, 67, 193, 21, 227, 173, 244, 119, 199, 128, 158 +}; + +#define SBOX1(n) FSb[(n)] +#define SBOX2(n) (unsigned char) ((FSb[(n)] >> 7 ^ FSb[(n)] << 1) & 0xff) +#define SBOX3(n) (unsigned char) ((FSb[(n)] >> 1 ^ FSb[(n)] << 7) & 0xff) +#define SBOX4(n) FSb[((n) << 1 ^ (n) >> 7) &0xff] + +#else /* MBEDTLS_CAMELLIA_SMALL_MEMORY */ + +static const unsigned char FSb[256] = +{ + 112, 130, 44, 236, 179, 39, 192, 229, 228, 133, 87, 53, 234, 12, 174, 65, + 35, 239, 107, 147, 69, 25, 165, 33, 237, 14, 79, 78, 29, 101, 146, 189, + 134, 184, 175, 143, 124, 235, 31, 206, 62, 48, 220, 95, 94, 197, 11, 26, + 166, 225, 57, 202, 213, 71, 93, 61, 217, 1, 90, 214, 81, 86, 108, 77, + 139, 13, 154, 102, 251, 204, 176, 45, 116, 18, 43, 32, 240, 177, 132, 153, + 223, 76, 203, 194, 52, 126, 118, 5, 109, 183, 169, 49, 209, 23, 4, 215, + 20, 88, 58, 97, 222, 27, 17, 28, 50, 15, 156, 22, 83, 24, 242, 34, + 254, 68, 207, 178, 195, 181, 122, 145, 36, 8, 232, 168, 96, 252, 105, 80, + 170, 208, 160, 125, 161, 137, 98, 151, 84, 91, 30, 149, 224, 255, 100, 210, + 16, 196, 0, 72, 163, 247, 117, 219, 138, 3, 230, 218, 9, 63, 221, 148, + 135, 92, 131, 2, 205, 74, 144, 51, 115, 103, 246, 243, 157, 127, 191, 226, + 82, 155, 216, 38, 200, 55, 198, 59, 129, 150, 111, 75, 19, 190, 99, 46, + 233, 121, 167, 140, 159, 110, 188, 142, 41, 245, 249, 182, 47, 253, 180, 89, + 120, 152, 6, 106, 231, 70, 113, 186, 212, 37, 171, 66, 136, 162, 141, 250, + 114, 7, 185, 85, 248, 238, 172, 10, 54, 73, 42, 104, 60, 56, 241, 164, + 64, 40, 211, 123, 187, 201, 67, 193, 21, 227, 173, 244, 119, 199, 128, 158 +}; + +static const unsigned char FSb2[256] = +{ + 224, 5, 88, 217, 103, 78, 129, 203, 201, 11, 174, 106, 213, 24, 93, 130, + 70, 223, 214, 39, 138, 50, 75, 66, 219, 28, 158, 156, 58, 202, 37, 123, + 13, 113, 95, 31, 248, 215, 62, 157, 124, 96, 185, 190, 188, 139, 22, 52, + 77, 195, 114, 149, 171, 142, 186, 122, 179, 2, 180, 173, 162, 172, 216, 154, + 23, 26, 53, 204, 247, 153, 97, 90, 232, 36, 86, 64, 225, 99, 9, 51, + 191, 152, 151, 133, 104, 252, 236, 10, 218, 111, 83, 98, 163, 46, 8, 175, + 40, 176, 116, 194, 189, 54, 34, 56, 100, 30, 57, 44, 166, 48, 229, 68, + 253, 136, 159, 101, 135, 107, 244, 35, 72, 16, 209, 81, 192, 249, 210, 160, + 85, 161, 65, 250, 67, 19, 196, 47, 168, 182, 60, 43, 193, 255, 200, 165, + 32, 137, 0, 144, 71, 239, 234, 183, 21, 6, 205, 181, 18, 126, 187, 41, + 15, 184, 7, 4, 155, 148, 33, 102, 230, 206, 237, 231, 59, 254, 127, 197, + 164, 55, 177, 76, 145, 110, 141, 118, 3, 45, 222, 150, 38, 125, 198, 92, + 211, 242, 79, 25, 63, 220, 121, 29, 82, 235, 243, 109, 94, 251, 105, 178, + 240, 49, 12, 212, 207, 140, 226, 117, 169, 74, 87, 132, 17, 69, 27, 245, + 228, 14, 115, 170, 241, 221, 89, 20, 108, 146, 84, 208, 120, 112, 227, 73, + 128, 80, 167, 246, 119, 147, 134, 131, 42, 199, 91, 233, 238, 143, 1, 61 +}; + +static const unsigned char FSb3[256] = +{ + 56, 65, 22, 118, 217, 147, 96, 242, 114, 194, 171, 154, 117, 6, 87, 160, + 145, 247, 181, 201, 162, 140, 210, 144, 246, 7, 167, 39, 142, 178, 73, 222, + 67, 92, 215, 199, 62, 245, 143, 103, 31, 24, 110, 175, 47, 226, 133, 13, + 83, 240, 156, 101, 234, 163, 174, 158, 236, 128, 45, 107, 168, 43, 54, 166, + 197, 134, 77, 51, 253, 102, 88, 150, 58, 9, 149, 16, 120, 216, 66, 204, + 239, 38, 229, 97, 26, 63, 59, 130, 182, 219, 212, 152, 232, 139, 2, 235, + 10, 44, 29, 176, 111, 141, 136, 14, 25, 135, 78, 11, 169, 12, 121, 17, + 127, 34, 231, 89, 225, 218, 61, 200, 18, 4, 116, 84, 48, 126, 180, 40, + 85, 104, 80, 190, 208, 196, 49, 203, 42, 173, 15, 202, 112, 255, 50, 105, + 8, 98, 0, 36, 209, 251, 186, 237, 69, 129, 115, 109, 132, 159, 238, 74, + 195, 46, 193, 1, 230, 37, 72, 153, 185, 179, 123, 249, 206, 191, 223, 113, + 41, 205, 108, 19, 100, 155, 99, 157, 192, 75, 183, 165, 137, 95, 177, 23, + 244, 188, 211, 70, 207, 55, 94, 71, 148, 250, 252, 91, 151, 254, 90, 172, + 60, 76, 3, 53, 243, 35, 184, 93, 106, 146, 213, 33, 68, 81, 198, 125, + 57, 131, 220, 170, 124, 119, 86, 5, 27, 164, 21, 52, 30, 28, 248, 82, + 32, 20, 233, 189, 221, 228, 161, 224, 138, 241, 214, 122, 187, 227, 64, 79 +}; + +static const unsigned char FSb4[256] = +{ + 112, 44, 179, 192, 228, 87, 234, 174, 35, 107, 69, 165, 237, 79, 29, 146, + 134, 175, 124, 31, 62, 220, 94, 11, 166, 57, 213, 93, 217, 90, 81, 108, + 139, 154, 251, 176, 116, 43, 240, 132, 223, 203, 52, 118, 109, 169, 209, 4, + 20, 58, 222, 17, 50, 156, 83, 242, 254, 207, 195, 122, 36, 232, 96, 105, + 170, 160, 161, 98, 84, 30, 224, 100, 16, 0, 163, 117, 138, 230, 9, 221, + 135, 131, 205, 144, 115, 246, 157, 191, 82, 216, 200, 198, 129, 111, 19, 99, + 233, 167, 159, 188, 41, 249, 47, 180, 120, 6, 231, 113, 212, 171, 136, 141, + 114, 185, 248, 172, 54, 42, 60, 241, 64, 211, 187, 67, 21, 173, 119, 128, + 130, 236, 39, 229, 133, 53, 12, 65, 239, 147, 25, 33, 14, 78, 101, 189, + 184, 143, 235, 206, 48, 95, 197, 26, 225, 202, 71, 61, 1, 214, 86, 77, + 13, 102, 204, 45, 18, 32, 177, 153, 76, 194, 126, 5, 183, 49, 23, 215, + 88, 97, 27, 28, 15, 22, 24, 34, 68, 178, 181, 145, 8, 168, 252, 80, + 208, 125, 137, 151, 91, 149, 255, 210, 196, 72, 247, 219, 3, 218, 63, 148, + 92, 2, 74, 51, 103, 243, 127, 226, 155, 38, 55, 59, 150, 75, 190, 46, + 121, 140, 110, 142, 245, 182, 253, 89, 152, 106, 70, 186, 37, 66, 162, 250, + 7, 85, 238, 10, 73, 104, 56, 164, 40, 123, 201, 193, 227, 244, 199, 158 +}; + +#define SBOX1(n) FSb[(n)] +#define SBOX2(n) FSb2[(n)] +#define SBOX3(n) FSb3[(n)] +#define SBOX4(n) FSb4[(n)] + +#endif /* MBEDTLS_CAMELLIA_SMALL_MEMORY */ + +static const unsigned char shifts[2][4][4] = +{ + { + { 1, 1, 1, 1 }, /* KL */ + { 0, 0, 0, 0 }, /* KR */ + { 1, 1, 1, 1 }, /* KA */ + { 0, 0, 0, 0 } /* KB */ + }, + { + { 1, 0, 1, 1 }, /* KL */ + { 1, 1, 0, 1 }, /* KR */ + { 1, 1, 1, 0 }, /* KA */ + { 1, 1, 0, 1 } /* KB */ + } +}; + +static const signed char indexes[2][4][20] = +{ + { + { 0, 1, 2, 3, 8, 9, 10, 11, 38, 39, + 36, 37, 23, 20, 21, 22, 27, -1, -1, 26 }, /* KL -> RK */ + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, /* KR -> RK */ + { 4, 5, 6, 7, 12, 13, 14, 15, 16, 17, + 18, 19, -1, 24, 25, -1, 31, 28, 29, 30 }, /* KA -> RK */ + { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } /* KB -> RK */ + }, + { + { 0, 1, 2, 3, 61, 62, 63, 60, -1, -1, + -1, -1, 27, 24, 25, 26, 35, 32, 33, 34 }, /* KL -> RK */ + { -1, -1, -1, -1, 8, 9, 10, 11, 16, 17, + 18, 19, -1, -1, -1, -1, 39, 36, 37, 38 }, /* KR -> RK */ + { -1, -1, -1, -1, 12, 13, 14, 15, 58, 59, + 56, 57, 31, 28, 29, 30, -1, -1, -1, -1 }, /* KA -> RK */ + { 4, 5, 6, 7, 65, 66, 67, 64, 20, 21, + 22, 23, -1, -1, -1, -1, 43, 40, 41, 42 } /* KB -> RK */ + } +}; + +static const signed char transposes[2][20] = +{ + { + 21, 22, 23, 20, + -1, -1, -1, -1, + 18, 19, 16, 17, + 11, 8, 9, 10, + 15, 12, 13, 14 + }, + { + 25, 26, 27, 24, + 29, 30, 31, 28, + 18, 19, 16, 17, + -1, -1, -1, -1, + -1, -1, -1, -1 + } +}; + +/* Shift macro for 128 bit strings with rotation smaller than 32 bits (!) */ +#define ROTL(DEST, SRC, SHIFT) \ + { \ + (DEST)[0] = (SRC)[0] << (SHIFT) ^ (SRC)[1] >> (32 - (SHIFT)); \ + (DEST)[1] = (SRC)[1] << (SHIFT) ^ (SRC)[2] >> (32 - (SHIFT)); \ + (DEST)[2] = (SRC)[2] << (SHIFT) ^ (SRC)[3] >> (32 - (SHIFT)); \ + (DEST)[3] = (SRC)[3] << (SHIFT) ^ (SRC)[0] >> (32 - (SHIFT)); \ + } + +#define FL(XL, XR, KL, KR) \ + { \ + (XR) = ((((XL) &(KL)) << 1) | (((XL) &(KL)) >> 31)) ^ (XR); \ + (XL) = ((XR) | (KR)) ^ (XL); \ + } + +#define FLInv(YL, YR, KL, KR) \ + { \ + (YL) = ((YR) | (KR)) ^ (YL); \ + (YR) = ((((YL) &(KL)) << 1) | (((YL) &(KL)) >> 31)) ^ (YR); \ + } + +#define SHIFT_AND_PLACE(INDEX, OFFSET) \ + { \ + TK[0] = KC[(OFFSET) * 4 + 0]; \ + TK[1] = KC[(OFFSET) * 4 + 1]; \ + TK[2] = KC[(OFFSET) * 4 + 2]; \ + TK[3] = KC[(OFFSET) * 4 + 3]; \ + \ + for (i = 1; i <= 4; i++) \ + if (shifts[(INDEX)][(OFFSET)][i -1]) \ + ROTL(TK + i * 4, TK, (15 * i) % 32); \ + \ + for (i = 0; i < 20; i++) \ + if (indexes[(INDEX)][(OFFSET)][i] != -1) { \ + RK[indexes[(INDEX)][(OFFSET)][i]] = TK[i]; \ + } \ + } + +static void camellia_feistel(const uint32_t x[2], const uint32_t k[2], + uint32_t z[2]) +{ + uint32_t I0, I1; + I0 = x[0] ^ k[0]; + I1 = x[1] ^ k[1]; + + I0 = ((uint32_t) SBOX1(MBEDTLS_BYTE_3(I0)) << 24) | + ((uint32_t) SBOX2(MBEDTLS_BYTE_2(I0)) << 16) | + ((uint32_t) SBOX3(MBEDTLS_BYTE_1(I0)) << 8) | + ((uint32_t) SBOX4(MBEDTLS_BYTE_0(I0))); + I1 = ((uint32_t) SBOX2(MBEDTLS_BYTE_3(I1)) << 24) | + ((uint32_t) SBOX3(MBEDTLS_BYTE_2(I1)) << 16) | + ((uint32_t) SBOX4(MBEDTLS_BYTE_1(I1)) << 8) | + ((uint32_t) SBOX1(MBEDTLS_BYTE_0(I1))); + + I0 ^= (I1 << 8) | (I1 >> 24); + I1 ^= (I0 << 16) | (I0 >> 16); + I0 ^= (I1 >> 8) | (I1 << 24); + I1 ^= (I0 >> 8) | (I0 << 24); + + z[0] ^= I1; + z[1] ^= I0; +} + +void mbedtls_camellia_init(mbedtls_camellia_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_camellia_context)); +} + +void mbedtls_camellia_free(mbedtls_camellia_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_camellia_context)); +} + +/* + * Camellia key schedule (encryption) + */ +int mbedtls_camellia_setkey_enc(mbedtls_camellia_context *ctx, + const unsigned char *key, + unsigned int keybits) +{ + int idx; + size_t i; + uint32_t *RK; + unsigned char t[64]; + uint32_t SIGMA[6][2]; + uint32_t KC[16]; + uint32_t TK[20]; + + RK = ctx->rk; + + memset(t, 0, 64); + memset(RK, 0, sizeof(ctx->rk)); + + switch (keybits) { + case 128: ctx->nr = 3; idx = 0; break; + case 192: + case 256: ctx->nr = 4; idx = 1; break; + default: return MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA; + } + + for (i = 0; i < keybits / 8; ++i) { + t[i] = key[i]; + } + + if (keybits == 192) { + for (i = 0; i < 8; i++) { + t[24 + i] = ~t[16 + i]; + } + } + + /* + * Prepare SIGMA values + */ + for (i = 0; i < 6; i++) { + SIGMA[i][0] = MBEDTLS_GET_UINT32_BE(SIGMA_CHARS[i], 0); + SIGMA[i][1] = MBEDTLS_GET_UINT32_BE(SIGMA_CHARS[i], 4); + } + + /* + * Key storage in KC + * Order: KL, KR, KA, KB + */ + memset(KC, 0, sizeof(KC)); + + /* Store KL, KR */ + for (i = 0; i < 8; i++) { + KC[i] = MBEDTLS_GET_UINT32_BE(t, i * 4); + } + + /* Generate KA */ + for (i = 0; i < 4; ++i) { + KC[8 + i] = KC[i] ^ KC[4 + i]; + } + + camellia_feistel(KC + 8, SIGMA[0], KC + 10); + camellia_feistel(KC + 10, SIGMA[1], KC + 8); + + for (i = 0; i < 4; ++i) { + KC[8 + i] ^= KC[i]; + } + + camellia_feistel(KC + 8, SIGMA[2], KC + 10); + camellia_feistel(KC + 10, SIGMA[3], KC + 8); + + if (keybits > 128) { + /* Generate KB */ + for (i = 0; i < 4; ++i) { + KC[12 + i] = KC[4 + i] ^ KC[8 + i]; + } + + camellia_feistel(KC + 12, SIGMA[4], KC + 14); + camellia_feistel(KC + 14, SIGMA[5], KC + 12); + } + + /* + * Generating subkeys + */ + + /* Manipulating KL */ + SHIFT_AND_PLACE(idx, 0); + + /* Manipulating KR */ + if (keybits > 128) { + SHIFT_AND_PLACE(idx, 1); + } + + /* Manipulating KA */ + SHIFT_AND_PLACE(idx, 2); + + /* Manipulating KB */ + if (keybits > 128) { + SHIFT_AND_PLACE(idx, 3); + } + + /* Do transpositions */ + for (i = 0; i < 20; i++) { + if (transposes[idx][i] != -1) { + RK[32 + 12 * idx + i] = RK[transposes[idx][i]]; + } + } + + return 0; +} + +/* + * Camellia key schedule (decryption) + */ +int mbedtls_camellia_setkey_dec(mbedtls_camellia_context *ctx, + const unsigned char *key, + unsigned int keybits) +{ + int idx, ret; + size_t i; + mbedtls_camellia_context cty; + uint32_t *RK; + uint32_t *SK; + + mbedtls_camellia_init(&cty); + + /* Also checks keybits */ + if ((ret = mbedtls_camellia_setkey_enc(&cty, key, keybits)) != 0) { + goto exit; + } + + ctx->nr = cty.nr; + idx = (ctx->nr == 4); + + RK = ctx->rk; + SK = cty.rk + 24 * 2 + 8 * idx * 2; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + + for (i = 22 + 8 * idx, SK -= 6; i > 0; i--, SK -= 4) { + *RK++ = *SK++; + *RK++ = *SK++; + } + + SK -= 2; + + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + *RK++ = *SK++; + +exit: + mbedtls_camellia_free(&cty); + + return ret; +} + +/* + * Camellia-ECB block encryption/decryption + */ +int mbedtls_camellia_crypt_ecb(mbedtls_camellia_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + int NR; + uint32_t *RK, X[4]; + if (mode != MBEDTLS_CAMELLIA_ENCRYPT && mode != MBEDTLS_CAMELLIA_DECRYPT) { + return MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA; + } + + ((void) mode); + + NR = ctx->nr; + RK = ctx->rk; + + X[0] = MBEDTLS_GET_UINT32_BE(input, 0); + X[1] = MBEDTLS_GET_UINT32_BE(input, 4); + X[2] = MBEDTLS_GET_UINT32_BE(input, 8); + X[3] = MBEDTLS_GET_UINT32_BE(input, 12); + + X[0] ^= *RK++; + X[1] ^= *RK++; + X[2] ^= *RK++; + X[3] ^= *RK++; + + while (NR) { + --NR; + camellia_feistel(X, RK, X + 2); + RK += 2; + camellia_feistel(X + 2, RK, X); + RK += 2; + camellia_feistel(X, RK, X + 2); + RK += 2; + camellia_feistel(X + 2, RK, X); + RK += 2; + camellia_feistel(X, RK, X + 2); + RK += 2; + camellia_feistel(X + 2, RK, X); + RK += 2; + + if (NR) { + FL(X[0], X[1], RK[0], RK[1]); + RK += 2; + FLInv(X[2], X[3], RK[0], RK[1]); + RK += 2; + } + } + + X[2] ^= *RK++; + X[3] ^= *RK++; + X[0] ^= *RK++; + X[1] ^= *RK++; + + MBEDTLS_PUT_UINT32_BE(X[2], output, 0); + MBEDTLS_PUT_UINT32_BE(X[3], output, 4); + MBEDTLS_PUT_UINT32_BE(X[0], output, 8); + MBEDTLS_PUT_UINT32_BE(X[1], output, 12); + + return 0; +} + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/* + * Camellia-CBC buffer encryption/decryption + */ +int mbedtls_camellia_crypt_cbc(mbedtls_camellia_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + unsigned char temp[16]; + if (mode != MBEDTLS_CAMELLIA_ENCRYPT && mode != MBEDTLS_CAMELLIA_DECRYPT) { + return MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA; + } + + if (length % 16) { + return MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_CAMELLIA_DECRYPT) { + while (length > 0) { + memcpy(temp, input, 16); + mbedtls_camellia_crypt_ecb(ctx, mode, input, output); + + mbedtls_xor(output, output, iv, 16); + + memcpy(iv, temp, 16); + + input += 16; + output += 16; + length -= 16; + } + } else { + while (length > 0) { + mbedtls_xor(output, input, iv, 16); + + mbedtls_camellia_crypt_ecb(ctx, mode, output, output); + memcpy(iv, output, 16); + + input += 16; + output += 16; + length -= 16; + } + } + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +/* + * Camellia-CFB128 buffer encryption/decryption + */ +int mbedtls_camellia_crypt_cfb128(mbedtls_camellia_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + int c; + size_t n; + if (mode != MBEDTLS_CAMELLIA_ENCRYPT && mode != MBEDTLS_CAMELLIA_DECRYPT) { + return MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA; + } + + n = *iv_off; + if (n >= 16) { + return MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA; + } + + if (mode == MBEDTLS_CAMELLIA_DECRYPT) { + while (length--) { + if (n == 0) { + mbedtls_camellia_crypt_ecb(ctx, MBEDTLS_CAMELLIA_ENCRYPT, iv, iv); + } + + c = *input++; + *output++ = (unsigned char) (c ^ iv[n]); + iv[n] = (unsigned char) c; + + n = (n + 1) & 0x0F; + } + } else { + while (length--) { + if (n == 0) { + mbedtls_camellia_crypt_ecb(ctx, MBEDTLS_CAMELLIA_ENCRYPT, iv, iv); + } + + iv[n] = *output++ = (unsigned char) (iv[n] ^ *input++); + + n = (n + 1) & 0x0F; + } + } + + *iv_off = n; + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/* + * Camellia-CTR buffer encryption/decryption + */ +int mbedtls_camellia_crypt_ctr(mbedtls_camellia_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output) +{ + int c, i; + size_t n; + + n = *nc_off; + if (n >= 16) { + return MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA; + } + + while (length--) { + if (n == 0) { + mbedtls_camellia_crypt_ecb(ctx, MBEDTLS_CAMELLIA_ENCRYPT, nonce_counter, + stream_block); + + for (i = 16; i > 0; i--) { + if (++nonce_counter[i - 1] != 0) { + break; + } + } + } + c = *input++; + *output++ = (unsigned char) (c ^ stream_block[n]); + + n = (n + 1) & 0x0F; + } + + *nc_off = n; + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_CTR */ +#endif /* !MBEDTLS_CAMELLIA_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +/* + * Camellia test vectors from: + * + * http://info.isl.ntt.co.jp/crypt/eng/camellia/technology.html: + * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/cryptrec/intermediate.txt + * http://info.isl.ntt.co.jp/crypt/eng/camellia/dl/cryptrec/t_camellia.txt + * (For each bitlength: Key 0, Nr 39) + */ +#define CAMELLIA_TESTS_ECB 2 + +static const unsigned char camellia_test_ecb_key[3][CAMELLIA_TESTS_ECB][32] = +{ + { + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, + { + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + }, +}; + +static const unsigned char camellia_test_ecb_plain[CAMELLIA_TESTS_ECB][16] = +{ + { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }, + { 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +static const unsigned char camellia_test_ecb_cipher[3][CAMELLIA_TESTS_ECB][16] = +{ + { + { 0x67, 0x67, 0x31, 0x38, 0x54, 0x96, 0x69, 0x73, + 0x08, 0x57, 0x06, 0x56, 0x48, 0xea, 0xbe, 0x43 }, + { 0x38, 0x3C, 0x6C, 0x2A, 0xAB, 0xEF, 0x7F, 0xDE, + 0x25, 0xCD, 0x47, 0x0B, 0xF7, 0x74, 0xA3, 0x31 } + }, + { + { 0xb4, 0x99, 0x34, 0x01, 0xb3, 0xe9, 0x96, 0xf8, + 0x4e, 0xe5, 0xce, 0xe7, 0xd7, 0x9b, 0x09, 0xb9 }, + { 0xD1, 0x76, 0x3F, 0xC0, 0x19, 0xD7, 0x7C, 0xC9, + 0x30, 0xBF, 0xF2, 0xA5, 0x6F, 0x7C, 0x93, 0x64 } + }, + { + { 0x9a, 0xcc, 0x23, 0x7d, 0xff, 0x16, 0xd7, 0x6c, + 0x20, 0xef, 0x7c, 0x91, 0x9e, 0x3a, 0x75, 0x09 }, + { 0x05, 0x03, 0xFB, 0x10, 0xAB, 0x24, 0x1E, 0x7C, + 0xF4, 0x5D, 0x8C, 0xDE, 0xEE, 0x47, 0x43, 0x35 } + } +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#define CAMELLIA_TESTS_CBC 3 + +static const unsigned char camellia_test_cbc_key[3][32] = +{ + { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, + 0xAB, 0xF7, 0x15, 0x88, 0x09, 0xCF, 0x4F, 0x3C } + , + { 0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, + 0xC8, 0x10, 0xF3, 0x2B, 0x80, 0x90, 0x79, 0xE5, + 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C, 0x6B, 0x7B } + , + { 0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, + 0x2B, 0x73, 0xAE, 0xF0, 0x85, 0x7D, 0x77, 0x81, + 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61, 0x08, 0xD7, + 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4 } +}; + +static const unsigned char camellia_test_cbc_iv[16] = + +{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F } +; + +static const unsigned char camellia_test_cbc_plain[CAMELLIA_TESTS_CBC][16] = +{ + { 0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A }, + { 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 }, + { 0x30, 0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, + 0xE5, 0xFB, 0xC1, 0x19, 0x1A, 0x0A, 0x52, 0xEF } + +}; + +static const unsigned char camellia_test_cbc_cipher[3][CAMELLIA_TESTS_CBC][16] = +{ + { + { 0x16, 0x07, 0xCF, 0x49, 0x4B, 0x36, 0xBB, 0xF0, + 0x0D, 0xAE, 0xB0, 0xB5, 0x03, 0xC8, 0x31, 0xAB }, + { 0xA2, 0xF2, 0xCF, 0x67, 0x16, 0x29, 0xEF, 0x78, + 0x40, 0xC5, 0xA5, 0xDF, 0xB5, 0x07, 0x48, 0x87 }, + { 0x0F, 0x06, 0x16, 0x50, 0x08, 0xCF, 0x8B, 0x8B, + 0x5A, 0x63, 0x58, 0x63, 0x62, 0x54, 0x3E, 0x54 } + }, + { + { 0x2A, 0x48, 0x30, 0xAB, 0x5A, 0xC4, 0xA1, 0xA2, + 0x40, 0x59, 0x55, 0xFD, 0x21, 0x95, 0xCF, 0x93 }, + { 0x5D, 0x5A, 0x86, 0x9B, 0xD1, 0x4C, 0xE5, 0x42, + 0x64, 0xF8, 0x92, 0xA6, 0xDD, 0x2E, 0xC3, 0xD5 }, + { 0x37, 0xD3, 0x59, 0xC3, 0x34, 0x98, 0x36, 0xD8, + 0x84, 0xE3, 0x10, 0xAD, 0xDF, 0x68, 0xC4, 0x49 } + }, + { + { 0xE6, 0xCF, 0xA3, 0x5F, 0xC0, 0x2B, 0x13, 0x4A, + 0x4D, 0x2C, 0x0B, 0x67, 0x37, 0xAC, 0x3E, 0xDA }, + { 0x36, 0xCB, 0xEB, 0x73, 0xBD, 0x50, 0x4B, 0x40, + 0x70, 0xB1, 0xB7, 0xDE, 0x2B, 0x21, 0xEB, 0x50 }, + { 0xE3, 0x1A, 0x60, 0x55, 0x29, 0x7D, 0x96, 0xCA, + 0x33, 0x30, 0xCD, 0xF1, 0xB1, 0x86, 0x0A, 0x83 } + } +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/* + * Camellia-CTR test vectors from: + * + * http://www.faqs.org/rfcs/rfc5528.html + */ + +static const unsigned char camellia_test_ctr_key[3][16] = +{ + { 0xAE, 0x68, 0x52, 0xF8, 0x12, 0x10, 0x67, 0xCC, + 0x4B, 0xF7, 0xA5, 0x76, 0x55, 0x77, 0xF3, 0x9E }, + { 0x7E, 0x24, 0x06, 0x78, 0x17, 0xFA, 0xE0, 0xD7, + 0x43, 0xD6, 0xCE, 0x1F, 0x32, 0x53, 0x91, 0x63 }, + { 0x76, 0x91, 0xBE, 0x03, 0x5E, 0x50, 0x20, 0xA8, + 0xAC, 0x6E, 0x61, 0x85, 0x29, 0xF9, 0xA0, 0xDC } +}; + +static const unsigned char camellia_test_ctr_nonce_counter[3][16] = +{ + { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0x6C, 0xB6, 0xDB, 0xC0, 0x54, 0x3B, 0x59, + 0xDA, 0x48, 0xD9, 0x0B, 0x00, 0x00, 0x00, 0x01 }, + { 0x00, 0xE0, 0x01, 0x7B, 0x27, 0x77, 0x7F, 0x3F, + 0x4A, 0x17, 0x86, 0xF0, 0x00, 0x00, 0x00, 0x01 } +}; + +static const unsigned char camellia_test_ctr_pt[3][48] = +{ + { 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x62, + 0x6C, 0x6F, 0x63, 0x6B, 0x20, 0x6D, 0x73, 0x67 }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F }, + + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23 } +}; + +static const unsigned char camellia_test_ctr_ct[3][48] = +{ + { 0xD0, 0x9D, 0xC2, 0x9A, 0x82, 0x14, 0x61, 0x9A, + 0x20, 0x87, 0x7C, 0x76, 0xDB, 0x1F, 0x0B, 0x3F }, + { 0xDB, 0xF3, 0xC7, 0x8D, 0xC0, 0x83, 0x96, 0xD4, + 0xDA, 0x7C, 0x90, 0x77, 0x65, 0xBB, 0xCB, 0x44, + 0x2B, 0x8E, 0x8E, 0x0F, 0x31, 0xF0, 0xDC, 0xA7, + 0x2C, 0x74, 0x17, 0xE3, 0x53, 0x60, 0xE0, 0x48 }, + { 0xB1, 0x9D, 0x1F, 0xCD, 0xCB, 0x75, 0xEB, 0x88, + 0x2F, 0x84, 0x9C, 0xE2, 0x4D, 0x85, 0xCF, 0x73, + 0x9C, 0xE6, 0x4B, 0x2B, 0x5C, 0x9D, 0x73, 0xF1, + 0x4F, 0x2D, 0x5D, 0x9D, 0xCE, 0x98, 0x89, 0xCD, + 0xDF, 0x50, 0x86, 0x96 } +}; + +static const int camellia_test_ctr_len[3] = +{ 16, 32, 36 }; +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +/* + * Checkup routine + */ +int mbedtls_camellia_self_test(int verbose) +{ + int i, j, u, v; + unsigned char key[32]; + unsigned char buf[64]; + unsigned char src[16]; + unsigned char dst[16]; +#if defined(MBEDTLS_CIPHER_MODE_CBC) + unsigned char iv[16]; +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + size_t offset, len; + unsigned char nonce_counter[16]; + unsigned char stream_block[16]; +#endif + int ret = 1; + + mbedtls_camellia_context ctx; + + mbedtls_camellia_init(&ctx); + memset(key, 0, 32); + + for (j = 0; j < 6; j++) { + u = j >> 1; + v = j & 1; + + if (verbose != 0) { + mbedtls_printf(" CAMELLIA-ECB-%3d (%s): ", 128 + u * 64, + (v == MBEDTLS_CAMELLIA_DECRYPT) ? "dec" : "enc"); + } + + for (i = 0; i < CAMELLIA_TESTS_ECB; i++) { + memcpy(key, camellia_test_ecb_key[u][i], 16 + 8 * u); + + if (v == MBEDTLS_CAMELLIA_DECRYPT) { + mbedtls_camellia_setkey_dec(&ctx, key, 128 + u * 64); + memcpy(src, camellia_test_ecb_cipher[u][i], 16); + memcpy(dst, camellia_test_ecb_plain[i], 16); + } else { /* MBEDTLS_CAMELLIA_ENCRYPT */ + mbedtls_camellia_setkey_enc(&ctx, key, 128 + u * 64); + memcpy(src, camellia_test_ecb_plain[i], 16); + memcpy(dst, camellia_test_ecb_cipher[u][i], 16); + } + + mbedtls_camellia_crypt_ecb(&ctx, v, src, buf); + + if (memcmp(buf, dst, 16) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + goto exit; + } + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + /* + * CBC mode + */ + for (j = 0; j < 6; j++) { + u = j >> 1; + v = j & 1; + + if (verbose != 0) { + mbedtls_printf(" CAMELLIA-CBC-%3d (%s): ", 128 + u * 64, + (v == MBEDTLS_CAMELLIA_DECRYPT) ? "dec" : "enc"); + } + + memcpy(src, camellia_test_cbc_iv, 16); + memcpy(dst, camellia_test_cbc_iv, 16); + memcpy(key, camellia_test_cbc_key[u], 16 + 8 * u); + + if (v == MBEDTLS_CAMELLIA_DECRYPT) { + mbedtls_camellia_setkey_dec(&ctx, key, 128 + u * 64); + } else { + mbedtls_camellia_setkey_enc(&ctx, key, 128 + u * 64); + } + + for (i = 0; i < CAMELLIA_TESTS_CBC; i++) { + + if (v == MBEDTLS_CAMELLIA_DECRYPT) { + memcpy(iv, src, 16); + memcpy(src, camellia_test_cbc_cipher[u][i], 16); + memcpy(dst, camellia_test_cbc_plain[i], 16); + } else { /* MBEDTLS_CAMELLIA_ENCRYPT */ + memcpy(iv, dst, 16); + memcpy(src, camellia_test_cbc_plain[i], 16); + memcpy(dst, camellia_test_cbc_cipher[u][i], 16); + } + + mbedtls_camellia_crypt_cbc(&ctx, v, 16, iv, src, buf); + + if (memcmp(buf, dst, 16) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + goto exit; + } + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + + if (verbose != 0) { + mbedtls_printf("\n"); + } + +#if defined(MBEDTLS_CIPHER_MODE_CTR) + /* + * CTR mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + v = i & 1; + + if (verbose != 0) { + mbedtls_printf(" CAMELLIA-CTR-128 (%s): ", + (v == MBEDTLS_CAMELLIA_DECRYPT) ? "dec" : "enc"); + } + + memcpy(nonce_counter, camellia_test_ctr_nonce_counter[u], 16); + memcpy(key, camellia_test_ctr_key[u], 16); + + offset = 0; + mbedtls_camellia_setkey_enc(&ctx, key, 128); + + if (v == MBEDTLS_CAMELLIA_DECRYPT) { + len = camellia_test_ctr_len[u]; + memcpy(buf, camellia_test_ctr_ct[u], len); + + mbedtls_camellia_crypt_ctr(&ctx, len, &offset, nonce_counter, stream_block, + buf, buf); + + if (memcmp(buf, camellia_test_ctr_pt[u], len) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + goto exit; + } + } else { + len = camellia_test_ctr_len[u]; + memcpy(buf, camellia_test_ctr_pt[u], len); + + mbedtls_camellia_crypt_ctr(&ctx, len, &offset, nonce_counter, stream_block, + buf, buf); + + if (memcmp(buf, camellia_test_ctr_ct[u], len) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + goto exit; + } + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + + ret = 0; + +exit: + mbedtls_camellia_free(&ctx); + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_CAMELLIA_C */ diff --git a/r5dev/thirdparty/mbedtls/ccm.c b/r5dev/thirdparty/mbedtls/ccm.c new file mode 100644 index 00000000..36c999e7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ccm.c @@ -0,0 +1,729 @@ +/* + * NIST SP800-38C compliant CCM implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Definition of CCM: + * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + * RFC 3610 "Counter with CBC-MAC (CCM)" + * + * Related: + * RFC 5116 "An Interface and Algorithms for Authenticated Encryption" + */ + +#include "common.h" + +#if defined(MBEDTLS_CCM_C) + +#include "mbedtls/ccm.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) +#include +#define mbedtls_printf printf +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ +#endif /* MBEDTLS_PLATFORM_C */ + +#if !defined(MBEDTLS_CCM_ALT) + + +/* + * Initialize context + */ +void mbedtls_ccm_init(mbedtls_ccm_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_ccm_context)); +} + +int mbedtls_ccm_setkey(mbedtls_ccm_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_cipher_info_t *cipher_info; + + cipher_info = mbedtls_cipher_info_from_values(cipher, keybits, + MBEDTLS_MODE_ECB); + if (cipher_info == NULL) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + if (cipher_info->block_size != 16) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + mbedtls_cipher_free(&ctx->cipher_ctx); + + if ((ret = mbedtls_cipher_setup(&ctx->cipher_ctx, cipher_info)) != 0) { + return ret; + } + + if ((ret = mbedtls_cipher_setkey(&ctx->cipher_ctx, key, keybits, + MBEDTLS_ENCRYPT)) != 0) { + return ret; + } + + return 0; +} + +/* + * Free context + */ +void mbedtls_ccm_free(mbedtls_ccm_context *ctx) +{ + if (ctx == NULL) { + return; + } + mbedtls_cipher_free(&ctx->cipher_ctx); + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_ccm_context)); +} + +#define CCM_STATE__CLEAR 0 +#define CCM_STATE__STARTED (1 << 0) +#define CCM_STATE__LENGTHS_SET (1 << 1) +#define CCM_STATE__AUTH_DATA_STARTED (1 << 2) +#define CCM_STATE__AUTH_DATA_FINISHED (1 << 3) +#define CCM_STATE__ERROR (1 << 4) + +/* + * Encrypt or decrypt a partial block with CTR + */ +static int mbedtls_ccm_crypt(mbedtls_ccm_context *ctx, + size_t offset, size_t use_len, + const unsigned char *input, + unsigned char *output) +{ + size_t olen = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char tmp_buf[16] = { 0 }; + + if ((ret = mbedtls_cipher_update(&ctx->cipher_ctx, ctx->ctr, 16, tmp_buf, + &olen)) != 0) { + ctx->state |= CCM_STATE__ERROR; + mbedtls_platform_zeroize(tmp_buf, sizeof(tmp_buf)); + return ret; + } + + mbedtls_xor(output, input, tmp_buf + offset, use_len); + + mbedtls_platform_zeroize(tmp_buf, sizeof(tmp_buf)); + return ret; +} + +static void mbedtls_ccm_clear_state(mbedtls_ccm_context *ctx) +{ + ctx->state = CCM_STATE__CLEAR; + memset(ctx->y, 0, 16); + memset(ctx->ctr, 0, 16); +} + +static int ccm_calculate_first_block_if_ready(mbedtls_ccm_context *ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char i; + size_t len_left, olen; + + /* length calculation can be done only after both + * mbedtls_ccm_starts() and mbedtls_ccm_set_lengths() have been executed + */ + if (!(ctx->state & CCM_STATE__STARTED) || !(ctx->state & CCM_STATE__LENGTHS_SET)) { + return 0; + } + + /* CCM expects non-empty tag. + * CCM* allows empty tag. For CCM* without tag, ignore plaintext length. + */ + if (ctx->tag_len == 0) { + if (ctx->mode == MBEDTLS_CCM_STAR_ENCRYPT || ctx->mode == MBEDTLS_CCM_STAR_DECRYPT) { + ctx->plaintext_len = 0; + } else { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + } + + /* + * First block: + * 0 .. 0 flags + * 1 .. iv_len nonce (aka iv) - set by: mbedtls_ccm_starts() + * iv_len+1 .. 15 length + * + * With flags as (bits): + * 7 0 + * 6 add present? + * 5 .. 3 (t - 2) / 2 + * 2 .. 0 q - 1 + */ + ctx->y[0] |= (ctx->add_len > 0) << 6; + ctx->y[0] |= ((ctx->tag_len - 2) / 2) << 3; + ctx->y[0] |= ctx->q - 1; + + for (i = 0, len_left = ctx->plaintext_len; i < ctx->q; i++, len_left >>= 8) { + ctx->y[15-i] = MBEDTLS_BYTE_0(len_left); + } + + if (len_left > 0) { + ctx->state |= CCM_STATE__ERROR; + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + /* Start CBC-MAC with first block*/ + if ((ret = mbedtls_cipher_update(&ctx->cipher_ctx, ctx->y, 16, ctx->y, &olen)) != 0) { + ctx->state |= CCM_STATE__ERROR; + return ret; + } + + return 0; +} + +int mbedtls_ccm_starts(mbedtls_ccm_context *ctx, + int mode, + const unsigned char *iv, + size_t iv_len) +{ + /* Also implies q is within bounds */ + if (iv_len < 7 || iv_len > 13) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + ctx->mode = mode; + ctx->q = 16 - 1 - (unsigned char) iv_len; + + /* + * Prepare counter block for encryption: + * 0 .. 0 flags + * 1 .. iv_len nonce (aka iv) + * iv_len+1 .. 15 counter (initially 1) + * + * With flags as (bits): + * 7 .. 3 0 + * 2 .. 0 q - 1 + */ + memset(ctx->ctr, 0, 16); + ctx->ctr[0] = ctx->q - 1; + memcpy(ctx->ctr + 1, iv, iv_len); + memset(ctx->ctr + 1 + iv_len, 0, ctx->q); + ctx->ctr[15] = 1; + + /* + * See ccm_calculate_first_block_if_ready() for block layout description + */ + memcpy(ctx->y + 1, iv, iv_len); + + ctx->state |= CCM_STATE__STARTED; + return ccm_calculate_first_block_if_ready(ctx); +} + +int mbedtls_ccm_set_lengths(mbedtls_ccm_context *ctx, + size_t total_ad_len, + size_t plaintext_len, + size_t tag_len) +{ + /* + * Check length requirements: SP800-38C A.1 + * Additional requirement: a < 2^16 - 2^8 to simplify the code. + * 'length' checked later (when writing it to the first block) + * + * Also, loosen the requirements to enable support for CCM* (IEEE 802.15.4). + */ + if (tag_len == 2 || tag_len > 16 || tag_len % 2 != 0) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + if (total_ad_len >= 0xFF00) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + ctx->plaintext_len = plaintext_len; + ctx->add_len = total_ad_len; + ctx->tag_len = tag_len; + ctx->processed = 0; + + ctx->state |= CCM_STATE__LENGTHS_SET; + return ccm_calculate_first_block_if_ready(ctx); +} + +int mbedtls_ccm_update_ad(mbedtls_ccm_context *ctx, + const unsigned char *add, + size_t add_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen, use_len, offset; + + if (ctx->state & CCM_STATE__ERROR) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + if (add_len > 0) { + if (ctx->state & CCM_STATE__AUTH_DATA_FINISHED) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + if (!(ctx->state & CCM_STATE__AUTH_DATA_STARTED)) { + if (add_len > ctx->add_len) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + ctx->y[0] ^= (unsigned char) ((ctx->add_len >> 8) & 0xFF); + ctx->y[1] ^= (unsigned char) ((ctx->add_len) & 0xFF); + + ctx->state |= CCM_STATE__AUTH_DATA_STARTED; + } else if (ctx->processed + add_len > ctx->add_len) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + while (add_len > 0) { + offset = (ctx->processed + 2) % 16; /* account for y[0] and y[1] + * holding total auth data length */ + use_len = 16 - offset; + + if (use_len > add_len) { + use_len = add_len; + } + + mbedtls_xor(ctx->y + offset, ctx->y + offset, add, use_len); + + ctx->processed += use_len; + add_len -= use_len; + add += use_len; + + if (use_len + offset == 16 || ctx->processed == ctx->add_len) { + if ((ret = + mbedtls_cipher_update(&ctx->cipher_ctx, ctx->y, 16, ctx->y, &olen)) != 0) { + ctx->state |= CCM_STATE__ERROR; + return ret; + } + } + } + + if (ctx->processed == ctx->add_len) { + ctx->state |= CCM_STATE__AUTH_DATA_FINISHED; + ctx->processed = 0; // prepare for mbedtls_ccm_update() + } + } + + return 0; +} + +int mbedtls_ccm_update(mbedtls_ccm_context *ctx, + const unsigned char *input, size_t input_len, + unsigned char *output, size_t output_size, + size_t *output_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char i; + size_t use_len, offset, olen; + + unsigned char local_output[16]; + + if (ctx->state & CCM_STATE__ERROR) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + /* Check against plaintext length only if performing operation with + * authentication + */ + if (ctx->tag_len != 0 && ctx->processed + input_len > ctx->plaintext_len) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + if (output_size < input_len) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + *output_len = input_len; + + ret = 0; + + while (input_len > 0) { + offset = ctx->processed % 16; + + use_len = 16 - offset; + + if (use_len > input_len) { + use_len = input_len; + } + + ctx->processed += use_len; + + if (ctx->mode == MBEDTLS_CCM_ENCRYPT || \ + ctx->mode == MBEDTLS_CCM_STAR_ENCRYPT) { + mbedtls_xor(ctx->y + offset, ctx->y + offset, input, use_len); + + if (use_len + offset == 16 || ctx->processed == ctx->plaintext_len) { + if ((ret = + mbedtls_cipher_update(&ctx->cipher_ctx, ctx->y, 16, ctx->y, &olen)) != 0) { + ctx->state |= CCM_STATE__ERROR; + goto exit; + } + } + + ret = mbedtls_ccm_crypt(ctx, offset, use_len, input, output); + if (ret != 0) { + goto exit; + } + } + + if (ctx->mode == MBEDTLS_CCM_DECRYPT || \ + ctx->mode == MBEDTLS_CCM_STAR_DECRYPT) { + /* Since output may be in shared memory, we cannot be sure that + * it will contain what we wrote to it. Therefore, we should avoid using + * it as input to any operations. + * Write decrypted data to local_output to avoid using output variable as + * input in the XOR operation for Y. + */ + ret = mbedtls_ccm_crypt(ctx, offset, use_len, input, local_output); + if (ret != 0) { + goto exit; + } + + mbedtls_xor(ctx->y + offset, ctx->y + offset, local_output, use_len); + + memcpy(output, local_output, use_len); + mbedtls_platform_zeroize(local_output, 16); + + if (use_len + offset == 16 || ctx->processed == ctx->plaintext_len) { + if ((ret = + mbedtls_cipher_update(&ctx->cipher_ctx, ctx->y, 16, ctx->y, &olen)) != 0) { + ctx->state |= CCM_STATE__ERROR; + goto exit; + } + } + } + + if (use_len + offset == 16 || ctx->processed == ctx->plaintext_len) { + for (i = 0; i < ctx->q; i++) { + if (++(ctx->ctr)[15-i] != 0) { + break; + } + } + } + + input_len -= use_len; + input += use_len; + output += use_len; + } + +exit: + mbedtls_platform_zeroize(local_output, 16); + + return ret; +} + +int mbedtls_ccm_finish(mbedtls_ccm_context *ctx, + unsigned char *tag, size_t tag_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char i; + + if (ctx->state & CCM_STATE__ERROR) { + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } + + if (ctx->add_len > 0 && !(ctx->state & CCM_STATE__AUTH_DATA_FINISHED)) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + if (ctx->plaintext_len > 0 && ctx->processed != ctx->plaintext_len) { + return MBEDTLS_ERR_CCM_BAD_INPUT; + } + + /* + * Authentication: reset counter and crypt/mask internal tag + */ + for (i = 0; i < ctx->q; i++) { + ctx->ctr[15-i] = 0; + } + + ret = mbedtls_ccm_crypt(ctx, 0, 16, ctx->y, ctx->y); + if (ret != 0) { + return ret; + } + if (tag != NULL) { + memcpy(tag, ctx->y, tag_len); + } + mbedtls_ccm_clear_state(ctx); + + return 0; +} + +/* + * Authenticated encryption or decryption + */ +static int ccm_auth_crypt(mbedtls_ccm_context *ctx, int mode, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen; + + if ((ret = mbedtls_ccm_starts(ctx, mode, iv, iv_len)) != 0) { + return ret; + } + + if ((ret = mbedtls_ccm_set_lengths(ctx, add_len, length, tag_len)) != 0) { + return ret; + } + + if ((ret = mbedtls_ccm_update_ad(ctx, add, add_len)) != 0) { + return ret; + } + + if ((ret = mbedtls_ccm_update(ctx, input, length, + output, length, &olen)) != 0) { + return ret; + } + + if ((ret = mbedtls_ccm_finish(ctx, tag, tag_len)) != 0) { + return ret; + } + + return 0; +} + +/* + * Authenticated encryption + */ +int mbedtls_ccm_star_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len) +{ + return ccm_auth_crypt(ctx, MBEDTLS_CCM_STAR_ENCRYPT, length, iv, iv_len, + add, add_len, input, output, tag, tag_len); +} + +int mbedtls_ccm_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len) +{ + return ccm_auth_crypt(ctx, MBEDTLS_CCM_ENCRYPT, length, iv, iv_len, + add, add_len, input, output, tag, tag_len); +} + +/* + * Authenticated decryption + */ +static int mbedtls_ccm_compare_tags(const unsigned char *tag1, + const unsigned char *tag2, + size_t tag_len) +{ + unsigned char i; + int diff; + + /* Check tag in "constant-time" */ + for (diff = 0, i = 0; i < tag_len; i++) { + diff |= tag1[i] ^ tag2[i]; + } + + if (diff != 0) { + return MBEDTLS_ERR_CCM_AUTH_FAILED; + } + + return 0; +} + +static int ccm_auth_decrypt(mbedtls_ccm_context *ctx, int mode, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char check_tag[16]; + + if ((ret = ccm_auth_crypt(ctx, mode, length, + iv, iv_len, add, add_len, + input, output, check_tag, tag_len)) != 0) { + return ret; + } + + if ((ret = mbedtls_ccm_compare_tags(tag, check_tag, tag_len)) != 0) { + mbedtls_platform_zeroize(output, length); + return ret; + } + + return 0; +} + +int mbedtls_ccm_star_auth_decrypt(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len) +{ + return ccm_auth_decrypt(ctx, MBEDTLS_CCM_STAR_DECRYPT, length, + iv, iv_len, add, add_len, + input, output, tag, tag_len); +} + +int mbedtls_ccm_auth_decrypt(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *add, size_t add_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len) +{ + return ccm_auth_decrypt(ctx, MBEDTLS_CCM_DECRYPT, length, + iv, iv_len, add, add_len, + input, output, tag, tag_len); +} +#endif /* !MBEDTLS_CCM_ALT */ + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) +/* + * Examples 1 to 3 from SP800-38C Appendix C + */ + +#define NB_TESTS 3 +#define CCM_SELFTEST_PT_MAX_LEN 24 +#define CCM_SELFTEST_CT_MAX_LEN 32 +/* + * The data is the same for all tests, only the used length changes + */ +static const unsigned char key_test_data[] = { + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f +}; + +static const unsigned char iv_test_data[] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b +}; + +static const unsigned char ad_test_data[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 +}; + +static const unsigned char msg_test_data[CCM_SELFTEST_PT_MAX_LEN] = { + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +}; + +static const size_t iv_len_test_data[NB_TESTS] = { 7, 8, 12 }; +static const size_t add_len_test_data[NB_TESTS] = { 8, 16, 20 }; +static const size_t msg_len_test_data[NB_TESTS] = { 4, 16, 24 }; +static const size_t tag_len_test_data[NB_TESTS] = { 4, 6, 8 }; + +static const unsigned char res_test_data[NB_TESTS][CCM_SELFTEST_CT_MAX_LEN] = { + { 0x71, 0x62, 0x01, 0x5b, 0x4d, 0xac, 0x25, 0x5d }, + { 0xd2, 0xa1, 0xf0, 0xe0, 0x51, 0xea, 0x5f, 0x62, + 0x08, 0x1a, 0x77, 0x92, 0x07, 0x3d, 0x59, 0x3d, + 0x1f, 0xc6, 0x4f, 0xbf, 0xac, 0xcd }, + { 0xe3, 0xb2, 0x01, 0xa9, 0xf5, 0xb7, 0x1a, 0x7a, + 0x9b, 0x1c, 0xea, 0xec, 0xcd, 0x97, 0xe7, 0x0b, + 0x61, 0x76, 0xaa, 0xd9, 0xa4, 0x42, 0x8a, 0xa5, + 0x48, 0x43, 0x92, 0xfb, 0xc1, 0xb0, 0x99, 0x51 } +}; + +int mbedtls_ccm_self_test(int verbose) +{ + mbedtls_ccm_context ctx; + /* + * Some hardware accelerators require the input and output buffers + * would be in RAM, because the flash is not accessible. + * Use buffers on the stack to hold the test vectors data. + */ + unsigned char plaintext[CCM_SELFTEST_PT_MAX_LEN]; + unsigned char ciphertext[CCM_SELFTEST_CT_MAX_LEN]; + size_t i; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_ccm_init(&ctx); + + if (mbedtls_ccm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key_test_data, + 8 * sizeof(key_test_data)) != 0) { + if (verbose != 0) { + mbedtls_printf(" CCM: setup failed"); + } + + return 1; + } + + for (i = 0; i < NB_TESTS; i++) { + if (verbose != 0) { + mbedtls_printf(" CCM-AES #%u: ", (unsigned int) i + 1); + } + + memset(plaintext, 0, CCM_SELFTEST_PT_MAX_LEN); + memset(ciphertext, 0, CCM_SELFTEST_CT_MAX_LEN); + memcpy(plaintext, msg_test_data, msg_len_test_data[i]); + + ret = mbedtls_ccm_encrypt_and_tag(&ctx, msg_len_test_data[i], + iv_test_data, iv_len_test_data[i], + ad_test_data, add_len_test_data[i], + plaintext, ciphertext, + ciphertext + msg_len_test_data[i], + tag_len_test_data[i]); + + if (ret != 0 || + memcmp(ciphertext, res_test_data[i], + msg_len_test_data[i] + tag_len_test_data[i]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return 1; + } + memset(plaintext, 0, CCM_SELFTEST_PT_MAX_LEN); + + ret = mbedtls_ccm_auth_decrypt(&ctx, msg_len_test_data[i], + iv_test_data, iv_len_test_data[i], + ad_test_data, add_len_test_data[i], + ciphertext, plaintext, + ciphertext + msg_len_test_data[i], + tag_len_test_data[i]); + + if (ret != 0 || + memcmp(plaintext, msg_test_data, msg_len_test_data[i]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return 1; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + mbedtls_ccm_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; +} + +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#endif /* MBEDTLS_CCM_C */ diff --git a/r5dev/thirdparty/mbedtls/chacha20.c b/r5dev/thirdparty/mbedtls/chacha20.c new file mode 100644 index 00000000..cbb01f4a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/chacha20.c @@ -0,0 +1,509 @@ +/** + * \file chacha20.c + * + * \brief ChaCha20 cipher. + * + * \author Daniel King + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_CHACHA20_C) + +#include "mbedtls/chacha20.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_CHACHA20_ALT) + +#define ROTL32(value, amount) \ + ((uint32_t) ((value) << (amount)) | ((value) >> (32 - (amount)))) + +#define CHACHA20_CTR_INDEX (12U) + +#define CHACHA20_BLOCK_SIZE_BYTES (4U * 16U) + +/** + * \brief ChaCha20 quarter round operation. + * + * The quarter round is defined as follows (from RFC 7539): + * 1. a += b; d ^= a; d <<<= 16; + * 2. c += d; b ^= c; b <<<= 12; + * 3. a += b; d ^= a; d <<<= 8; + * 4. c += d; b ^= c; b <<<= 7; + * + * \param state ChaCha20 state to modify. + * \param a The index of 'a' in the state. + * \param b The index of 'b' in the state. + * \param c The index of 'c' in the state. + * \param d The index of 'd' in the state. + */ +static inline void chacha20_quarter_round(uint32_t state[16], + size_t a, + size_t b, + size_t c, + size_t d) +{ + /* a += b; d ^= a; d <<<= 16; */ + state[a] += state[b]; + state[d] ^= state[a]; + state[d] = ROTL32(state[d], 16); + + /* c += d; b ^= c; b <<<= 12 */ + state[c] += state[d]; + state[b] ^= state[c]; + state[b] = ROTL32(state[b], 12); + + /* a += b; d ^= a; d <<<= 8; */ + state[a] += state[b]; + state[d] ^= state[a]; + state[d] = ROTL32(state[d], 8); + + /* c += d; b ^= c; b <<<= 7; */ + state[c] += state[d]; + state[b] ^= state[c]; + state[b] = ROTL32(state[b], 7); +} + +/** + * \brief Perform the ChaCha20 inner block operation. + * + * This function performs two rounds: the column round and the + * diagonal round. + * + * \param state The ChaCha20 state to update. + */ +static void chacha20_inner_block(uint32_t state[16]) +{ + chacha20_quarter_round(state, 0, 4, 8, 12); + chacha20_quarter_round(state, 1, 5, 9, 13); + chacha20_quarter_round(state, 2, 6, 10, 14); + chacha20_quarter_round(state, 3, 7, 11, 15); + + chacha20_quarter_round(state, 0, 5, 10, 15); + chacha20_quarter_round(state, 1, 6, 11, 12); + chacha20_quarter_round(state, 2, 7, 8, 13); + chacha20_quarter_round(state, 3, 4, 9, 14); +} + +/** + * \brief Generates a keystream block. + * + * \param initial_state The initial ChaCha20 state (key, nonce, counter). + * \param keystream Generated keystream bytes are written to this buffer. + */ +static void chacha20_block(const uint32_t initial_state[16], + unsigned char keystream[64]) +{ + uint32_t working_state[16]; + size_t i; + + memcpy(working_state, + initial_state, + CHACHA20_BLOCK_SIZE_BYTES); + + for (i = 0U; i < 10U; i++) { + chacha20_inner_block(working_state); + } + + working_state[0] += initial_state[0]; + working_state[1] += initial_state[1]; + working_state[2] += initial_state[2]; + working_state[3] += initial_state[3]; + working_state[4] += initial_state[4]; + working_state[5] += initial_state[5]; + working_state[6] += initial_state[6]; + working_state[7] += initial_state[7]; + working_state[8] += initial_state[8]; + working_state[9] += initial_state[9]; + working_state[10] += initial_state[10]; + working_state[11] += initial_state[11]; + working_state[12] += initial_state[12]; + working_state[13] += initial_state[13]; + working_state[14] += initial_state[14]; + working_state[15] += initial_state[15]; + + for (i = 0U; i < 16; i++) { + size_t offset = i * 4U; + + MBEDTLS_PUT_UINT32_LE(working_state[i], keystream, offset); + } + + mbedtls_platform_zeroize(working_state, sizeof(working_state)); +} + +void mbedtls_chacha20_init(mbedtls_chacha20_context *ctx) +{ + mbedtls_platform_zeroize(ctx->state, sizeof(ctx->state)); + mbedtls_platform_zeroize(ctx->keystream8, sizeof(ctx->keystream8)); + + /* Initially, there's no keystream bytes available */ + ctx->keystream_bytes_used = CHACHA20_BLOCK_SIZE_BYTES; +} + +void mbedtls_chacha20_free(mbedtls_chacha20_context *ctx) +{ + if (ctx != NULL) { + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_chacha20_context)); + } +} + +int mbedtls_chacha20_setkey(mbedtls_chacha20_context *ctx, + const unsigned char key[32]) +{ + /* ChaCha20 constants - the string "expand 32-byte k" */ + ctx->state[0] = 0x61707865; + ctx->state[1] = 0x3320646e; + ctx->state[2] = 0x79622d32; + ctx->state[3] = 0x6b206574; + + /* Set key */ + ctx->state[4] = MBEDTLS_GET_UINT32_LE(key, 0); + ctx->state[5] = MBEDTLS_GET_UINT32_LE(key, 4); + ctx->state[6] = MBEDTLS_GET_UINT32_LE(key, 8); + ctx->state[7] = MBEDTLS_GET_UINT32_LE(key, 12); + ctx->state[8] = MBEDTLS_GET_UINT32_LE(key, 16); + ctx->state[9] = MBEDTLS_GET_UINT32_LE(key, 20); + ctx->state[10] = MBEDTLS_GET_UINT32_LE(key, 24); + ctx->state[11] = MBEDTLS_GET_UINT32_LE(key, 28); + + return 0; +} + +int mbedtls_chacha20_starts(mbedtls_chacha20_context *ctx, + const unsigned char nonce[12], + uint32_t counter) +{ + /* Counter */ + ctx->state[12] = counter; + + /* Nonce */ + ctx->state[13] = MBEDTLS_GET_UINT32_LE(nonce, 0); + ctx->state[14] = MBEDTLS_GET_UINT32_LE(nonce, 4); + ctx->state[15] = MBEDTLS_GET_UINT32_LE(nonce, 8); + + mbedtls_platform_zeroize(ctx->keystream8, sizeof(ctx->keystream8)); + + /* Initially, there's no keystream bytes available */ + ctx->keystream_bytes_used = CHACHA20_BLOCK_SIZE_BYTES; + + return 0; +} + +int mbedtls_chacha20_update(mbedtls_chacha20_context *ctx, + size_t size, + const unsigned char *input, + unsigned char *output) +{ + size_t offset = 0U; + + /* Use leftover keystream bytes, if available */ + while (size > 0U && ctx->keystream_bytes_used < CHACHA20_BLOCK_SIZE_BYTES) { + output[offset] = input[offset] + ^ ctx->keystream8[ctx->keystream_bytes_used]; + + ctx->keystream_bytes_used++; + offset++; + size--; + } + + /* Process full blocks */ + while (size >= CHACHA20_BLOCK_SIZE_BYTES) { + /* Generate new keystream block and increment counter */ + chacha20_block(ctx->state, ctx->keystream8); + ctx->state[CHACHA20_CTR_INDEX]++; + + mbedtls_xor(output + offset, input + offset, ctx->keystream8, 64U); + + offset += CHACHA20_BLOCK_SIZE_BYTES; + size -= CHACHA20_BLOCK_SIZE_BYTES; + } + + /* Last (partial) block */ + if (size > 0U) { + /* Generate new keystream block and increment counter */ + chacha20_block(ctx->state, ctx->keystream8); + ctx->state[CHACHA20_CTR_INDEX]++; + + mbedtls_xor(output + offset, input + offset, ctx->keystream8, size); + + ctx->keystream_bytes_used = size; + + } + + return 0; +} + +int mbedtls_chacha20_crypt(const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter, + size_t data_len, + const unsigned char *input, + unsigned char *output) +{ + mbedtls_chacha20_context ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_chacha20_init(&ctx); + + ret = mbedtls_chacha20_setkey(&ctx, key); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_chacha20_starts(&ctx, nonce, counter); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_chacha20_update(&ctx, data_len, input, output); + +cleanup: + mbedtls_chacha20_free(&ctx); + return ret; +} + +#endif /* !MBEDTLS_CHACHA20_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +static const unsigned char test_keys[2][32] = +{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + } +}; + +static const unsigned char test_nonces[2][12] = +{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02 + } +}; + +static const uint32_t test_counters[2] = +{ + 0U, + 1U +}; + +static const unsigned char test_input[2][375] = +{ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + { + 0x41, 0x6e, 0x79, 0x20, 0x73, 0x75, 0x62, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x49, 0x45, + 0x54, 0x46, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, + 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x6f, 0x72, + 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, + 0x20, 0x61, 0x6e, 0x20, 0x49, 0x45, 0x54, 0x46, + 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x20, + 0x6f, 0x72, 0x20, 0x52, 0x46, 0x43, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x20, 0x6d, 0x61, 0x64, 0x65, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x49, + 0x45, 0x54, 0x46, 0x20, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x20, 0x69, 0x73, 0x20, + 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, + 0x65, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x22, 0x49, + 0x45, 0x54, 0x46, 0x20, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x2e, 0x20, 0x53, 0x75, 0x63, 0x68, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x20, 0x6f, 0x72, 0x61, 0x6c, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, + 0x54, 0x46, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x61, 0x73, 0x20, + 0x77, 0x65, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x20, + 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x72, 0x6f, 0x6e, 0x69, 0x63, 0x20, 0x63, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6d, 0x61, + 0x64, 0x65, 0x20, 0x61, 0x74, 0x20, 0x61, 0x6e, + 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x20, 0x6f, + 0x72, 0x20, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x2c, + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x61, + 0x72, 0x65, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f + } +}; + +static const unsigned char test_output[2][375] = +{ + { + 0x76, 0xb8, 0xe0, 0xad, 0xa0, 0xf1, 0x3d, 0x90, + 0x40, 0x5d, 0x6a, 0xe5, 0x53, 0x86, 0xbd, 0x28, + 0xbd, 0xd2, 0x19, 0xb8, 0xa0, 0x8d, 0xed, 0x1a, + 0xa8, 0x36, 0xef, 0xcc, 0x8b, 0x77, 0x0d, 0xc7, + 0xda, 0x41, 0x59, 0x7c, 0x51, 0x57, 0x48, 0x8d, + 0x77, 0x24, 0xe0, 0x3f, 0xb8, 0xd8, 0x4a, 0x37, + 0x6a, 0x43, 0xb8, 0xf4, 0x15, 0x18, 0xa1, 0x1c, + 0xc3, 0x87, 0xb6, 0x69, 0xb2, 0xee, 0x65, 0x86 + }, + { + 0xa3, 0xfb, 0xf0, 0x7d, 0xf3, 0xfa, 0x2f, 0xde, + 0x4f, 0x37, 0x6c, 0xa2, 0x3e, 0x82, 0x73, 0x70, + 0x41, 0x60, 0x5d, 0x9f, 0x4f, 0x4f, 0x57, 0xbd, + 0x8c, 0xff, 0x2c, 0x1d, 0x4b, 0x79, 0x55, 0xec, + 0x2a, 0x97, 0x94, 0x8b, 0xd3, 0x72, 0x29, 0x15, + 0xc8, 0xf3, 0xd3, 0x37, 0xf7, 0xd3, 0x70, 0x05, + 0x0e, 0x9e, 0x96, 0xd6, 0x47, 0xb7, 0xc3, 0x9f, + 0x56, 0xe0, 0x31, 0xca, 0x5e, 0xb6, 0x25, 0x0d, + 0x40, 0x42, 0xe0, 0x27, 0x85, 0xec, 0xec, 0xfa, + 0x4b, 0x4b, 0xb5, 0xe8, 0xea, 0xd0, 0x44, 0x0e, + 0x20, 0xb6, 0xe8, 0xdb, 0x09, 0xd8, 0x81, 0xa7, + 0xc6, 0x13, 0x2f, 0x42, 0x0e, 0x52, 0x79, 0x50, + 0x42, 0xbd, 0xfa, 0x77, 0x73, 0xd8, 0xa9, 0x05, + 0x14, 0x47, 0xb3, 0x29, 0x1c, 0xe1, 0x41, 0x1c, + 0x68, 0x04, 0x65, 0x55, 0x2a, 0xa6, 0xc4, 0x05, + 0xb7, 0x76, 0x4d, 0x5e, 0x87, 0xbe, 0xa8, 0x5a, + 0xd0, 0x0f, 0x84, 0x49, 0xed, 0x8f, 0x72, 0xd0, + 0xd6, 0x62, 0xab, 0x05, 0x26, 0x91, 0xca, 0x66, + 0x42, 0x4b, 0xc8, 0x6d, 0x2d, 0xf8, 0x0e, 0xa4, + 0x1f, 0x43, 0xab, 0xf9, 0x37, 0xd3, 0x25, 0x9d, + 0xc4, 0xb2, 0xd0, 0xdf, 0xb4, 0x8a, 0x6c, 0x91, + 0x39, 0xdd, 0xd7, 0xf7, 0x69, 0x66, 0xe9, 0x28, + 0xe6, 0x35, 0x55, 0x3b, 0xa7, 0x6c, 0x5c, 0x87, + 0x9d, 0x7b, 0x35, 0xd4, 0x9e, 0xb2, 0xe6, 0x2b, + 0x08, 0x71, 0xcd, 0xac, 0x63, 0x89, 0x39, 0xe2, + 0x5e, 0x8a, 0x1e, 0x0e, 0xf9, 0xd5, 0x28, 0x0f, + 0xa8, 0xca, 0x32, 0x8b, 0x35, 0x1c, 0x3c, 0x76, + 0x59, 0x89, 0xcb, 0xcf, 0x3d, 0xaa, 0x8b, 0x6c, + 0xcc, 0x3a, 0xaf, 0x9f, 0x39, 0x79, 0xc9, 0x2b, + 0x37, 0x20, 0xfc, 0x88, 0xdc, 0x95, 0xed, 0x84, + 0xa1, 0xbe, 0x05, 0x9c, 0x64, 0x99, 0xb9, 0xfd, + 0xa2, 0x36, 0xe7, 0xe8, 0x18, 0xb0, 0x4b, 0x0b, + 0xc3, 0x9c, 0x1e, 0x87, 0x6b, 0x19, 0x3b, 0xfe, + 0x55, 0x69, 0x75, 0x3f, 0x88, 0x12, 0x8c, 0xc0, + 0x8a, 0xaa, 0x9b, 0x63, 0xd1, 0xa1, 0x6f, 0x80, + 0xef, 0x25, 0x54, 0xd7, 0x18, 0x9c, 0x41, 0x1f, + 0x58, 0x69, 0xca, 0x52, 0xc5, 0xb8, 0x3f, 0xa3, + 0x6f, 0xf2, 0x16, 0xb9, 0xc1, 0xd3, 0x00, 0x62, + 0xbe, 0xbc, 0xfd, 0x2d, 0xc5, 0xbc, 0xe0, 0x91, + 0x19, 0x34, 0xfd, 0xa7, 0x9a, 0x86, 0xf6, 0xe6, + 0x98, 0xce, 0xd7, 0x59, 0xc3, 0xff, 0x9b, 0x64, + 0x77, 0x33, 0x8f, 0x3d, 0xa4, 0xf9, 0xcd, 0x85, + 0x14, 0xea, 0x99, 0x82, 0xcc, 0xaf, 0xb3, 0x41, + 0xb2, 0x38, 0x4d, 0xd9, 0x02, 0xf3, 0xd1, 0xab, + 0x7a, 0xc6, 0x1d, 0xd2, 0x9c, 0x6f, 0x21, 0xba, + 0x5b, 0x86, 0x2f, 0x37, 0x30, 0xe3, 0x7c, 0xfd, + 0xc4, 0xfd, 0x80, 0x6c, 0x22, 0xf2, 0x21 + } +}; + +static const size_t test_lengths[2] = +{ + 64U, + 375U +}; + +/* Make sure no other definition is already present. */ +#undef ASSERT + +#define ASSERT(cond, args) \ + do \ + { \ + if (!(cond)) \ + { \ + if (verbose != 0) \ + mbedtls_printf args; \ + \ + return -1; \ + } \ + } \ + while (0) + +int mbedtls_chacha20_self_test(int verbose) +{ + unsigned char output[381]; + unsigned i; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + for (i = 0U; i < 2U; i++) { + if (verbose != 0) { + mbedtls_printf(" ChaCha20 test %u ", i); + } + + ret = mbedtls_chacha20_crypt(test_keys[i], + test_nonces[i], + test_counters[i], + test_lengths[i], + test_input[i], + output); + + ASSERT(0 == ret, ("error code: %i\n", ret)); + + ASSERT(0 == memcmp(output, test_output[i], test_lengths[i]), + ("failed (output)\n")); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* !MBEDTLS_CHACHA20_C */ diff --git a/r5dev/thirdparty/mbedtls/chachapoly.c b/r5dev/thirdparty/mbedtls/chachapoly.c new file mode 100644 index 00000000..0124d757 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/chachapoly.c @@ -0,0 +1,492 @@ +/** + * \file chachapoly.c + * + * \brief ChaCha20-Poly1305 AEAD construction based on RFC 7539. + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "common.h" + +#if defined(MBEDTLS_CHACHAPOLY_C) + +#include "mbedtls/chachapoly.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_CHACHAPOLY_ALT) + +#define CHACHAPOLY_STATE_INIT (0) +#define CHACHAPOLY_STATE_AAD (1) +#define CHACHAPOLY_STATE_CIPHERTEXT (2) /* Encrypting or decrypting */ +#define CHACHAPOLY_STATE_FINISHED (3) + +/** + * \brief Adds nul bytes to pad the AAD for Poly1305. + * + * \param ctx The ChaCha20-Poly1305 context. + */ +static int chachapoly_pad_aad(mbedtls_chachapoly_context *ctx) +{ + uint32_t partial_block_len = (uint32_t) (ctx->aad_len % 16U); + unsigned char zeroes[15]; + + if (partial_block_len == 0U) { + return 0; + } + + memset(zeroes, 0, sizeof(zeroes)); + + return mbedtls_poly1305_update(&ctx->poly1305_ctx, + zeroes, + 16U - partial_block_len); +} + +/** + * \brief Adds nul bytes to pad the ciphertext for Poly1305. + * + * \param ctx The ChaCha20-Poly1305 context. + */ +static int chachapoly_pad_ciphertext(mbedtls_chachapoly_context *ctx) +{ + uint32_t partial_block_len = (uint32_t) (ctx->ciphertext_len % 16U); + unsigned char zeroes[15]; + + if (partial_block_len == 0U) { + return 0; + } + + memset(zeroes, 0, sizeof(zeroes)); + return mbedtls_poly1305_update(&ctx->poly1305_ctx, + zeroes, + 16U - partial_block_len); +} + +void mbedtls_chachapoly_init(mbedtls_chachapoly_context *ctx) +{ + mbedtls_chacha20_init(&ctx->chacha20_ctx); + mbedtls_poly1305_init(&ctx->poly1305_ctx); + ctx->aad_len = 0U; + ctx->ciphertext_len = 0U; + ctx->state = CHACHAPOLY_STATE_INIT; + ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT; +} + +void mbedtls_chachapoly_free(mbedtls_chachapoly_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_chacha20_free(&ctx->chacha20_ctx); + mbedtls_poly1305_free(&ctx->poly1305_ctx); + ctx->aad_len = 0U; + ctx->ciphertext_len = 0U; + ctx->state = CHACHAPOLY_STATE_INIT; + ctx->mode = MBEDTLS_CHACHAPOLY_ENCRYPT; +} + +int mbedtls_chachapoly_setkey(mbedtls_chachapoly_context *ctx, + const unsigned char key[32]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_chacha20_setkey(&ctx->chacha20_ctx, key); + + return ret; +} + +int mbedtls_chachapoly_starts(mbedtls_chachapoly_context *ctx, + const unsigned char nonce[12], + mbedtls_chachapoly_mode_t mode) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char poly1305_key[64]; + + /* Set counter = 0, will be update to 1 when generating Poly1305 key */ + ret = mbedtls_chacha20_starts(&ctx->chacha20_ctx, nonce, 0U); + if (ret != 0) { + goto cleanup; + } + + /* Generate the Poly1305 key by getting the ChaCha20 keystream output with + * counter = 0. This is the same as encrypting a buffer of zeroes. + * Only the first 256-bits (32 bytes) of the key is used for Poly1305. + * The other 256 bits are discarded. + */ + memset(poly1305_key, 0, sizeof(poly1305_key)); + ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, sizeof(poly1305_key), + poly1305_key, poly1305_key); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_poly1305_starts(&ctx->poly1305_ctx, poly1305_key); + + if (ret == 0) { + ctx->aad_len = 0U; + ctx->ciphertext_len = 0U; + ctx->state = CHACHAPOLY_STATE_AAD; + ctx->mode = mode; + } + +cleanup: + mbedtls_platform_zeroize(poly1305_key, 64U); + return ret; +} + +int mbedtls_chachapoly_update_aad(mbedtls_chachapoly_context *ctx, + const unsigned char *aad, + size_t aad_len) +{ + if (ctx->state != CHACHAPOLY_STATE_AAD) { + return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE; + } + + ctx->aad_len += aad_len; + + return mbedtls_poly1305_update(&ctx->poly1305_ctx, aad, aad_len); +} + +int mbedtls_chachapoly_update(mbedtls_chachapoly_context *ctx, + size_t len, + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ctx->state != CHACHAPOLY_STATE_AAD) && + (ctx->state != CHACHAPOLY_STATE_CIPHERTEXT)) { + return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE; + } + + if (ctx->state == CHACHAPOLY_STATE_AAD) { + ctx->state = CHACHAPOLY_STATE_CIPHERTEXT; + + ret = chachapoly_pad_aad(ctx); + if (ret != 0) { + return ret; + } + } + + ctx->ciphertext_len += len; + + if (ctx->mode == MBEDTLS_CHACHAPOLY_ENCRYPT) { + ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, len, input, output); + if (ret != 0) { + return ret; + } + + ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, output, len); + if (ret != 0) { + return ret; + } + } else { /* DECRYPT */ + ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, input, len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_chacha20_update(&ctx->chacha20_ctx, len, input, output); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +int mbedtls_chachapoly_finish(mbedtls_chachapoly_context *ctx, + unsigned char mac[16]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char len_block[16]; + + if (ctx->state == CHACHAPOLY_STATE_INIT) { + return MBEDTLS_ERR_CHACHAPOLY_BAD_STATE; + } + + if (ctx->state == CHACHAPOLY_STATE_AAD) { + ret = chachapoly_pad_aad(ctx); + if (ret != 0) { + return ret; + } + } else if (ctx->state == CHACHAPOLY_STATE_CIPHERTEXT) { + ret = chachapoly_pad_ciphertext(ctx); + if (ret != 0) { + return ret; + } + } + + ctx->state = CHACHAPOLY_STATE_FINISHED; + + /* The lengths of the AAD and ciphertext are processed by + * Poly1305 as the final 128-bit block, encoded as little-endian integers. + */ + MBEDTLS_PUT_UINT64_LE(ctx->aad_len, len_block, 0); + MBEDTLS_PUT_UINT64_LE(ctx->ciphertext_len, len_block, 8); + + ret = mbedtls_poly1305_update(&ctx->poly1305_ctx, len_block, 16U); + if (ret != 0) { + return ret; + } + + ret = mbedtls_poly1305_finish(&ctx->poly1305_ctx, mac); + + return ret; +} + +static int chachapoly_crypt_and_tag(mbedtls_chachapoly_context *ctx, + mbedtls_chachapoly_mode_t mode, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char *input, + unsigned char *output, + unsigned char tag[16]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_chachapoly_starts(ctx, nonce, mode); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_chachapoly_update_aad(ctx, aad, aad_len); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_chachapoly_update(ctx, length, input, output); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_chachapoly_finish(ctx, tag); + +cleanup: + return ret; +} + +int mbedtls_chachapoly_encrypt_and_tag(mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char *input, + unsigned char *output, + unsigned char tag[16]) +{ + return chachapoly_crypt_and_tag(ctx, MBEDTLS_CHACHAPOLY_ENCRYPT, + length, nonce, aad, aad_len, + input, output, tag); +} + +int mbedtls_chachapoly_auth_decrypt(mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char tag[16], + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char check_tag[16]; + size_t i; + int diff; + + if ((ret = chachapoly_crypt_and_tag(ctx, + MBEDTLS_CHACHAPOLY_DECRYPT, length, nonce, + aad, aad_len, input, output, check_tag)) != 0) { + return ret; + } + + /* Check tag in "constant-time" */ + for (diff = 0, i = 0; i < sizeof(check_tag); i++) { + diff |= tag[i] ^ check_tag[i]; + } + + if (diff != 0) { + mbedtls_platform_zeroize(output, length); + return MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED; + } + + return 0; +} + +#endif /* MBEDTLS_CHACHAPOLY_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +static const unsigned char test_key[1][32] = +{ + { + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f + } +}; + +static const unsigned char test_nonce[1][12] = +{ + { + 0x07, 0x00, 0x00, 0x00, /* 32-bit common part */ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47 /* 64-bit IV */ + } +}; + +static const unsigned char test_aad[1][12] = +{ + { + 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7 + } +}; + +static const size_t test_aad_len[1] = +{ + 12U +}; + +static const unsigned char test_input[1][114] = +{ + { + 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, + 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, + 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, + 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, + 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, + 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, + 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, + 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, + 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, + 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, + 0x74, 0x2e + } +}; + +static const unsigned char test_output[1][114] = +{ + { + 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, + 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, + 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, + 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, + 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, + 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, + 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, + 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, + 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, + 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, + 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, + 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, + 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, + 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, + 0x61, 0x16 + } +}; + +static const size_t test_input_len[1] = +{ + 114U +}; + +static const unsigned char test_mac[1][16] = +{ + { + 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, + 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91 + } +}; + +/* Make sure no other definition is already present. */ +#undef ASSERT + +#define ASSERT(cond, args) \ + do \ + { \ + if (!(cond)) \ + { \ + if (verbose != 0) \ + mbedtls_printf args; \ + \ + return -1; \ + } \ + } \ + while (0) + +int mbedtls_chachapoly_self_test(int verbose) +{ + mbedtls_chachapoly_context ctx; + unsigned i; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char output[200]; + unsigned char mac[16]; + + for (i = 0U; i < 1U; i++) { + if (verbose != 0) { + mbedtls_printf(" ChaCha20-Poly1305 test %u ", i); + } + + mbedtls_chachapoly_init(&ctx); + + ret = mbedtls_chachapoly_setkey(&ctx, test_key[i]); + ASSERT(0 == ret, ("setkey() error code: %i\n", ret)); + + ret = mbedtls_chachapoly_encrypt_and_tag(&ctx, + test_input_len[i], + test_nonce[i], + test_aad[i], + test_aad_len[i], + test_input[i], + output, + mac); + + ASSERT(0 == ret, ("crypt_and_tag() error code: %i\n", ret)); + + ASSERT(0 == memcmp(output, test_output[i], test_input_len[i]), + ("failure (wrong output)\n")); + + ASSERT(0 == memcmp(mac, test_mac[i], 16U), + ("failure (wrong MAC)\n")); + + mbedtls_chachapoly_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_CHACHAPOLY_C */ diff --git a/r5dev/thirdparty/mbedtls/check_crypto_config.h b/r5dev/thirdparty/mbedtls/check_crypto_config.h new file mode 100644 index 00000000..58175e37 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/check_crypto_config.h @@ -0,0 +1,101 @@ +/** + * \file check_crypto_config.h + * + * \brief Consistency checks for PSA configuration options + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * It is recommended to include this file from your crypto_config.h + * in order to catch dependency issues early. + */ + +#ifndef MBEDTLS_CHECK_CRYPTO_CONFIG_H +#define MBEDTLS_CHECK_CRYPTO_CONFIG_H + +#if defined(PSA_WANT_ALG_CCM) && \ + !(defined(PSA_WANT_KEY_TYPE_AES) || \ + defined(PSA_WANT_KEY_TYPE_CAMELLIA)) +#error "PSA_WANT_ALG_CCM defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_CMAC) && \ + !(defined(PSA_WANT_KEY_TYPE_AES) || \ + defined(PSA_WANT_KEY_TYPE_CAMELLIA) || \ + defined(PSA_WANT_KEY_TYPE_DES)) +#error "PSA_WANT_ALG_CMAC defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_DETERMINISTIC_ECDSA) && \ + !(defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)) +#error "PSA_WANT_ALG_DETERMINISTIC_ECDSA defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_ECDSA) && \ + !(defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)) +#error "PSA_WANT_ALG_ECDSA defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_GCM) && \ + !(defined(PSA_WANT_KEY_TYPE_AES) || \ + defined(PSA_WANT_KEY_TYPE_CAMELLIA)) +#error "PSA_WANT_ALG_GCM defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_RSA_PKCS1V15_CRYPT) && \ + !(defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY)) +#error "PSA_WANT_ALG_RSA_PKCS1V15_CRYPT defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN) && \ + !(defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY)) +#error "PSA_WANT_ALG_RSA_PKCS1V15_SIGN defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_RSA_OAEP) && \ + !(defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY)) +#error "PSA_WANT_ALG_RSA_OAEP defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_RSA_PSS) && \ + !(defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY)) +#error "PSA_WANT_ALG_RSA_PSS defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) && \ + !defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +#error "PSA_WANT_KEY_TYPE_ECC_KEY_PAIR defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_USE_PSA_CRYPTO) && \ + !(defined(PSA_WANT_ALG_SHA_1) || defined(PSA_WANT_ALG_SHA_256) || defined(PSA_WANT_ALG_SHA_512)) +#error "MBEDTLS_SSL_PROTO_TLS1_2 defined, but not all prerequisites" +#endif + +#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS) && \ + !defined(PSA_WANT_ALG_SHA_256) +#error "PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS defined, but not all prerequisites" +#endif + +#endif /* MBEDTLS_CHECK_CRYPTO_CONFIG_H */ diff --git a/r5dev/thirdparty/mbedtls/cipher.c b/r5dev/thirdparty/mbedtls/cipher.c new file mode 100644 index 00000000..81e855d4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/cipher.c @@ -0,0 +1,1583 @@ +/** + * \file cipher.c + * + * \brief Generic cipher wrapper for mbed TLS + * + * \author Adriaan de Jong + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_CIPHER_C) + +#include "mbedtls/cipher.h" +#include "cipher_wrap.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "mbedtls/constant_time.h" + +#include +#include + +#if defined(MBEDTLS_CHACHAPOLY_C) +#include "mbedtls/chachapoly.h" +#endif + +#if defined(MBEDTLS_GCM_C) +#include "mbedtls/gcm.h" +#endif + +#if defined(MBEDTLS_CCM_C) +#include "mbedtls/ccm.h" +#endif + +#if defined(MBEDTLS_CHACHA20_C) +#include "mbedtls/chacha20.h" +#endif + +#if defined(MBEDTLS_CMAC_C) +#include "mbedtls/cmac.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_NIST_KW_C) +#include "mbedtls/nist_kw.h" +#endif + +#include "mbedtls/platform.h" + +static int supported_init = 0; + +const int *mbedtls_cipher_list(void) +{ + const mbedtls_cipher_definition_t *def; + int *type; + + if (!supported_init) { + def = mbedtls_cipher_definitions; + type = mbedtls_cipher_supported; + + while (def->type != 0) { + *type++ = (*def++).type; + } + + *type = 0; + + supported_init = 1; + } + + return mbedtls_cipher_supported; +} + +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_type( + const mbedtls_cipher_type_t cipher_type) +{ + const mbedtls_cipher_definition_t *def; + + for (def = mbedtls_cipher_definitions; def->info != NULL; def++) { + if (def->type == cipher_type) { + return def->info; + } + } + + return NULL; +} + +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_string( + const char *cipher_name) +{ + const mbedtls_cipher_definition_t *def; + + if (NULL == cipher_name) { + return NULL; + } + + for (def = mbedtls_cipher_definitions; def->info != NULL; def++) { + if (!strcmp(def->info->name, cipher_name)) { + return def->info; + } + } + + return NULL; +} + +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values( + const mbedtls_cipher_id_t cipher_id, + int key_bitlen, + const mbedtls_cipher_mode_t mode) +{ + const mbedtls_cipher_definition_t *def; + + for (def = mbedtls_cipher_definitions; def->info != NULL; def++) { + if (def->info->base->cipher == cipher_id && + def->info->key_bitlen == (unsigned) key_bitlen && + def->info->mode == mode) { + return def->info; + } + } + + return NULL; +} + +void mbedtls_cipher_init(mbedtls_cipher_context_t *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_cipher_context_t)); +} + +void mbedtls_cipher_free(mbedtls_cipher_context_t *ctx) +{ + if (ctx == NULL) { + return; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + if (ctx->cipher_ctx != NULL) { + mbedtls_cipher_context_psa * const cipher_psa = + (mbedtls_cipher_context_psa *) ctx->cipher_ctx; + + if (cipher_psa->slot_state == MBEDTLS_CIPHER_PSA_KEY_OWNED) { + /* xxx_free() doesn't allow to return failures. */ + (void) psa_destroy_key(cipher_psa->slot); + } + + mbedtls_platform_zeroize(cipher_psa, sizeof(*cipher_psa)); + mbedtls_free(cipher_psa); + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_cipher_context_t)); + return; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_CMAC_C) + if (ctx->cmac_ctx) { + mbedtls_platform_zeroize(ctx->cmac_ctx, + sizeof(mbedtls_cmac_context_t)); + mbedtls_free(ctx->cmac_ctx); + } +#endif + + if (ctx->cipher_ctx) { + ctx->cipher_info->base->ctx_free_func(ctx->cipher_ctx); + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_cipher_context_t)); +} + +int mbedtls_cipher_setup(mbedtls_cipher_context_t *ctx, + const mbedtls_cipher_info_t *cipher_info) +{ + if (cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + memset(ctx, 0, sizeof(mbedtls_cipher_context_t)); + + if (NULL == (ctx->cipher_ctx = cipher_info->base->ctx_alloc_func())) { + return MBEDTLS_ERR_CIPHER_ALLOC_FAILED; + } + + ctx->cipher_info = cipher_info; + +#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) + /* + * Ignore possible errors caused by a cipher mode that doesn't use padding + */ +#if defined(MBEDTLS_CIPHER_PADDING_PKCS7) + (void) mbedtls_cipher_set_padding_mode(ctx, MBEDTLS_PADDING_PKCS7); +#else + (void) mbedtls_cipher_set_padding_mode(ctx, MBEDTLS_PADDING_NONE); +#endif +#endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ + + return 0; +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +int mbedtls_cipher_setup_psa(mbedtls_cipher_context_t *ctx, + const mbedtls_cipher_info_t *cipher_info, + size_t taglen) +{ + psa_algorithm_t alg; + mbedtls_cipher_context_psa *cipher_psa; + + if (NULL == cipher_info || NULL == ctx) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + /* Check that the underlying cipher mode and cipher type are + * supported by the underlying PSA Crypto implementation. */ + alg = mbedtls_psa_translate_cipher_mode(cipher_info->mode, taglen); + if (alg == 0) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + if (mbedtls_psa_translate_cipher_type(cipher_info->type) == 0) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + memset(ctx, 0, sizeof(mbedtls_cipher_context_t)); + + cipher_psa = mbedtls_calloc(1, sizeof(mbedtls_cipher_context_psa)); + if (cipher_psa == NULL) { + return MBEDTLS_ERR_CIPHER_ALLOC_FAILED; + } + cipher_psa->alg = alg; + ctx->cipher_ctx = cipher_psa; + ctx->cipher_info = cipher_info; + ctx->psa_enabled = 1; + return 0; +} +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +int mbedtls_cipher_setkey(mbedtls_cipher_context_t *ctx, + const unsigned char *key, + int key_bitlen, + const mbedtls_operation_t operation) +{ + if (operation != MBEDTLS_ENCRYPT && operation != MBEDTLS_DECRYPT) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + mbedtls_cipher_context_psa * const cipher_psa = + (mbedtls_cipher_context_psa *) ctx->cipher_ctx; + + size_t const key_bytelen = ((size_t) key_bitlen + 7) / 8; + + psa_status_t status; + psa_key_type_t key_type; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + + /* PSA Crypto API only accepts byte-aligned keys. */ + if (key_bitlen % 8 != 0) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + /* Don't allow keys to be set multiple times. */ + if (cipher_psa->slot_state != MBEDTLS_CIPHER_PSA_KEY_UNSET) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + key_type = mbedtls_psa_translate_cipher_type( + ctx->cipher_info->type); + if (key_type == 0) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + psa_set_key_type(&attributes, key_type); + + /* Mbed TLS' cipher layer doesn't enforce the mode of operation + * (encrypt vs. decrypt): it is possible to setup a key for encryption + * and use it for AEAD decryption. Until tests relying on this + * are changed, allow any usage in PSA. */ + psa_set_key_usage_flags(&attributes, + /* mbedtls_psa_translate_cipher_operation( operation ); */ + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, cipher_psa->alg); + + status = psa_import_key(&attributes, key, key_bytelen, + &cipher_psa->slot); + switch (status) { + case PSA_SUCCESS: + break; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_CIPHER_ALLOC_FAILED; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + default: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + /* Indicate that we own the key slot and need to + * destroy it in mbedtls_cipher_free(). */ + cipher_psa->slot_state = MBEDTLS_CIPHER_PSA_KEY_OWNED; + + ctx->key_bitlen = key_bitlen; + ctx->operation = operation; + return 0; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if ((ctx->cipher_info->flags & MBEDTLS_CIPHER_VARIABLE_KEY_LEN) == 0 && + (int) ctx->cipher_info->key_bitlen != key_bitlen) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + ctx->key_bitlen = key_bitlen; + ctx->operation = operation; + + /* + * For OFB, CFB and CTR mode always use the encryption key schedule + */ + if (MBEDTLS_ENCRYPT == operation || + MBEDTLS_MODE_CFB == ctx->cipher_info->mode || + MBEDTLS_MODE_OFB == ctx->cipher_info->mode || + MBEDTLS_MODE_CTR == ctx->cipher_info->mode) { + return ctx->cipher_info->base->setkey_enc_func(ctx->cipher_ctx, key, + ctx->key_bitlen); + } + + if (MBEDTLS_DECRYPT == operation) { + return ctx->cipher_info->base->setkey_dec_func(ctx->cipher_ctx, key, + ctx->key_bitlen); + } + + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; +} + +int mbedtls_cipher_set_iv(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, + size_t iv_len) +{ + size_t actual_iv_size; + + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* While PSA Crypto has an API for multipart + * operations, we currently don't make it + * accessible through the cipher layer. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* avoid buffer overflow in ctx->iv */ + if (iv_len > MBEDTLS_MAX_IV_LENGTH) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + if ((ctx->cipher_info->flags & MBEDTLS_CIPHER_VARIABLE_IV_LEN) != 0) { + actual_iv_size = iv_len; + } else { + actual_iv_size = ctx->cipher_info->iv_size; + + /* avoid reading past the end of input buffer */ + if (actual_iv_size > iv_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + } + +#if defined(MBEDTLS_CHACHA20_C) + if (ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20) { + /* Even though the actual_iv_size is overwritten with a correct value + * of 12 from the cipher info, return an error to indicate that + * the input iv_len is wrong. */ + if (iv_len != 12) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (0 != mbedtls_chacha20_starts((mbedtls_chacha20_context *) ctx->cipher_ctx, + iv, + 0U)) { /* Initial counter value */ + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + } +#if defined(MBEDTLS_CHACHAPOLY_C) + if (ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305 && + iv_len != 12) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } +#endif +#endif + +#if defined(MBEDTLS_GCM_C) + if (MBEDTLS_MODE_GCM == ctx->cipher_info->mode) { + return mbedtls_gcm_starts((mbedtls_gcm_context *) ctx->cipher_ctx, + ctx->operation, + iv, iv_len); + } +#endif + +#if defined(MBEDTLS_CCM_C) + if (MBEDTLS_MODE_CCM_STAR_NO_TAG == ctx->cipher_info->mode) { + int set_lengths_result; + int ccm_star_mode; + + set_lengths_result = mbedtls_ccm_set_lengths( + (mbedtls_ccm_context *) ctx->cipher_ctx, + 0, 0, 0); + if (set_lengths_result != 0) { + return set_lengths_result; + } + + if (ctx->operation == MBEDTLS_DECRYPT) { + ccm_star_mode = MBEDTLS_CCM_STAR_DECRYPT; + } else if (ctx->operation == MBEDTLS_ENCRYPT) { + ccm_star_mode = MBEDTLS_CCM_STAR_ENCRYPT; + } else { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return mbedtls_ccm_starts((mbedtls_ccm_context *) ctx->cipher_ctx, + ccm_star_mode, + iv, iv_len); + } +#endif + + if (actual_iv_size != 0) { + memcpy(ctx->iv, iv, actual_iv_size); + ctx->iv_size = actual_iv_size; + } + + return 0; +} + +int mbedtls_cipher_reset(mbedtls_cipher_context_t *ctx) +{ + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* We don't support resetting PSA-based + * cipher contexts, yet. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + ctx->unprocessed_len = 0; + + return 0; +} + +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) +int mbedtls_cipher_update_ad(mbedtls_cipher_context_t *ctx, + const unsigned char *ad, size_t ad_len) +{ + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* While PSA Crypto has an API for multipart + * operations, we currently don't make it + * accessible through the cipher layer. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_GCM_C) + if (MBEDTLS_MODE_GCM == ctx->cipher_info->mode) { + return mbedtls_gcm_update_ad((mbedtls_gcm_context *) ctx->cipher_ctx, + ad, ad_len); + } +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + if (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type) { + int result; + mbedtls_chachapoly_mode_t mode; + + mode = (ctx->operation == MBEDTLS_ENCRYPT) + ? MBEDTLS_CHACHAPOLY_ENCRYPT + : MBEDTLS_CHACHAPOLY_DECRYPT; + + result = mbedtls_chachapoly_starts((mbedtls_chachapoly_context *) ctx->cipher_ctx, + ctx->iv, + mode); + if (result != 0) { + return result; + } + + return mbedtls_chachapoly_update_aad((mbedtls_chachapoly_context *) ctx->cipher_ctx, + ad, ad_len); + } +#endif + + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +} +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ + +int mbedtls_cipher_update(mbedtls_cipher_context_t *ctx, const unsigned char *input, + size_t ilen, unsigned char *output, size_t *olen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t block_size; + + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* While PSA Crypto has an API for multipart + * operations, we currently don't make it + * accessible through the cipher layer. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + *olen = 0; + block_size = mbedtls_cipher_get_block_size(ctx); + if (0 == block_size) { + return MBEDTLS_ERR_CIPHER_INVALID_CONTEXT; + } + + if (ctx->cipher_info->mode == MBEDTLS_MODE_ECB) { + if (ilen != block_size) { + return MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED; + } + + *olen = ilen; + + if (0 != (ret = ctx->cipher_info->base->ecb_func(ctx->cipher_ctx, + ctx->operation, input, output))) { + return ret; + } + + return 0; + } + +#if defined(MBEDTLS_GCM_C) + if (ctx->cipher_info->mode == MBEDTLS_MODE_GCM) { + return mbedtls_gcm_update((mbedtls_gcm_context *) ctx->cipher_ctx, + input, ilen, + output, ilen, olen); + } +#endif + +#if defined(MBEDTLS_CCM_C) + if (ctx->cipher_info->mode == MBEDTLS_MODE_CCM_STAR_NO_TAG) { + return mbedtls_ccm_update((mbedtls_ccm_context *) ctx->cipher_ctx, + input, ilen, + output, ilen, olen); + } +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + if (ctx->cipher_info->type == MBEDTLS_CIPHER_CHACHA20_POLY1305) { + *olen = ilen; + return mbedtls_chachapoly_update((mbedtls_chachapoly_context *) ctx->cipher_ctx, + ilen, input, output); + } +#endif + + if (input == output && + (ctx->unprocessed_len != 0 || ilen % block_size)) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + if (ctx->cipher_info->mode == MBEDTLS_MODE_CBC) { + size_t copy_len = 0; + + /* + * If there is not enough data for a full block, cache it. + */ + if ((ctx->operation == MBEDTLS_DECRYPT && NULL != ctx->add_padding && + ilen <= block_size - ctx->unprocessed_len) || + (ctx->operation == MBEDTLS_DECRYPT && NULL == ctx->add_padding && + ilen < block_size - ctx->unprocessed_len) || + (ctx->operation == MBEDTLS_ENCRYPT && + ilen < block_size - ctx->unprocessed_len)) { + memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]), input, + ilen); + + ctx->unprocessed_len += ilen; + return 0; + } + + /* + * Process cached data first + */ + if (0 != ctx->unprocessed_len) { + copy_len = block_size - ctx->unprocessed_len; + + memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]), input, + copy_len); + + if (0 != (ret = ctx->cipher_info->base->cbc_func(ctx->cipher_ctx, + ctx->operation, block_size, ctx->iv, + ctx->unprocessed_data, output))) { + return ret; + } + + *olen += block_size; + output += block_size; + ctx->unprocessed_len = 0; + + input += copy_len; + ilen -= copy_len; + } + + /* + * Cache final, incomplete block + */ + if (0 != ilen) { + /* Encryption: only cache partial blocks + * Decryption w/ padding: always keep at least one whole block + * Decryption w/o padding: only cache partial blocks + */ + copy_len = ilen % block_size; + if (copy_len == 0 && + ctx->operation == MBEDTLS_DECRYPT && + NULL != ctx->add_padding) { + copy_len = block_size; + } + + memcpy(ctx->unprocessed_data, &(input[ilen - copy_len]), + copy_len); + + ctx->unprocessed_len += copy_len; + ilen -= copy_len; + } + + /* + * Process remaining full blocks + */ + if (ilen) { + if (0 != (ret = ctx->cipher_info->base->cbc_func(ctx->cipher_ctx, + ctx->operation, ilen, ctx->iv, input, + output))) { + return ret; + } + + *olen += ilen; + } + + return 0; + } +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) + if (ctx->cipher_info->mode == MBEDTLS_MODE_CFB) { + if (0 != (ret = ctx->cipher_info->base->cfb_func(ctx->cipher_ctx, + ctx->operation, ilen, + &ctx->unprocessed_len, ctx->iv, + input, output))) { + return ret; + } + + *olen = ilen; + + return 0; + } +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) + if (ctx->cipher_info->mode == MBEDTLS_MODE_OFB) { + if (0 != (ret = ctx->cipher_info->base->ofb_func(ctx->cipher_ctx, + ilen, &ctx->unprocessed_len, ctx->iv, + input, output))) { + return ret; + } + + *olen = ilen; + + return 0; + } +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) + if (ctx->cipher_info->mode == MBEDTLS_MODE_CTR) { + if (0 != (ret = ctx->cipher_info->base->ctr_func(ctx->cipher_ctx, + ilen, &ctx->unprocessed_len, ctx->iv, + ctx->unprocessed_data, input, output))) { + return ret; + } + + *olen = ilen; + + return 0; + } +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) + if (ctx->cipher_info->mode == MBEDTLS_MODE_XTS) { + if (ctx->unprocessed_len > 0) { + /* We can only process an entire data unit at a time. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + ret = ctx->cipher_info->base->xts_func(ctx->cipher_ctx, + ctx->operation, ilen, ctx->iv, input, output); + if (ret != 0) { + return ret; + } + + *olen = ilen; + + return 0; + } +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + if (ctx->cipher_info->mode == MBEDTLS_MODE_STREAM) { + if (0 != (ret = ctx->cipher_info->base->stream_func(ctx->cipher_ctx, + ilen, input, output))) { + return ret; + } + + *olen = ilen; + + return 0; + } +#endif /* MBEDTLS_CIPHER_MODE_STREAM */ + + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +} + +#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) +#if defined(MBEDTLS_CIPHER_PADDING_PKCS7) +/* + * PKCS7 (and PKCS5) padding: fill with ll bytes, with ll = padding_len + */ +static void add_pkcs_padding(unsigned char *output, size_t output_len, + size_t data_len) +{ + size_t padding_len = output_len - data_len; + unsigned char i; + + for (i = 0; i < padding_len; i++) { + output[data_len + i] = (unsigned char) padding_len; + } +} + +static int get_pkcs_padding(unsigned char *input, size_t input_len, + size_t *data_len) +{ + size_t i, pad_idx; + unsigned char padding_len, bad = 0; + + if (NULL == input || NULL == data_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + padding_len = input[input_len - 1]; + *data_len = input_len - padding_len; + + /* Avoid logical || since it results in a branch */ + bad |= padding_len > input_len; + bad |= padding_len == 0; + + /* The number of bytes checked must be independent of padding_len, + * so pick input_len, which is usually 8 or 16 (one block) */ + pad_idx = input_len - padding_len; + for (i = 0; i < input_len; i++) { + bad |= (input[i] ^ padding_len) * (i >= pad_idx); + } + + return MBEDTLS_ERR_CIPHER_INVALID_PADDING * (bad != 0); +} +#endif /* MBEDTLS_CIPHER_PADDING_PKCS7 */ + +#if defined(MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS) +/* + * One and zeros padding: fill with 80 00 ... 00 + */ +static void add_one_and_zeros_padding(unsigned char *output, + size_t output_len, size_t data_len) +{ + size_t padding_len = output_len - data_len; + unsigned char i = 0; + + output[data_len] = 0x80; + for (i = 1; i < padding_len; i++) { + output[data_len + i] = 0x00; + } +} + +static int get_one_and_zeros_padding(unsigned char *input, size_t input_len, + size_t *data_len) +{ + size_t i; + unsigned char done = 0, prev_done, bad; + + if (NULL == input || NULL == data_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + bad = 0x80; + *data_len = 0; + for (i = input_len; i > 0; i--) { + prev_done = done; + done |= (input[i - 1] != 0); + *data_len |= (i - 1) * (done != prev_done); + bad ^= input[i - 1] * (done != prev_done); + } + + return MBEDTLS_ERR_CIPHER_INVALID_PADDING * (bad != 0); + +} +#endif /* MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS */ + +#if defined(MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN) +/* + * Zeros and len padding: fill with 00 ... 00 ll, where ll is padding length + */ +static void add_zeros_and_len_padding(unsigned char *output, + size_t output_len, size_t data_len) +{ + size_t padding_len = output_len - data_len; + unsigned char i = 0; + + for (i = 1; i < padding_len; i++) { + output[data_len + i - 1] = 0x00; + } + output[output_len - 1] = (unsigned char) padding_len; +} + +static int get_zeros_and_len_padding(unsigned char *input, size_t input_len, + size_t *data_len) +{ + size_t i, pad_idx; + unsigned char padding_len, bad = 0; + + if (NULL == input || NULL == data_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + padding_len = input[input_len - 1]; + *data_len = input_len - padding_len; + + /* Avoid logical || since it results in a branch */ + bad |= padding_len > input_len; + bad |= padding_len == 0; + + /* The number of bytes checked must be independent of padding_len */ + pad_idx = input_len - padding_len; + for (i = 0; i < input_len - 1; i++) { + bad |= input[i] * (i >= pad_idx); + } + + return MBEDTLS_ERR_CIPHER_INVALID_PADDING * (bad != 0); +} +#endif /* MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN */ + +#if defined(MBEDTLS_CIPHER_PADDING_ZEROS) +/* + * Zero padding: fill with 00 ... 00 + */ +static void add_zeros_padding(unsigned char *output, + size_t output_len, size_t data_len) +{ + size_t i; + + for (i = data_len; i < output_len; i++) { + output[i] = 0x00; + } +} + +static int get_zeros_padding(unsigned char *input, size_t input_len, + size_t *data_len) +{ + size_t i; + unsigned char done = 0, prev_done; + + if (NULL == input || NULL == data_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + *data_len = 0; + for (i = input_len; i > 0; i--) { + prev_done = done; + done |= (input[i-1] != 0); + *data_len |= i * (done != prev_done); + } + + return 0; +} +#endif /* MBEDTLS_CIPHER_PADDING_ZEROS */ + +/* + * No padding: don't pad :) + * + * There is no add_padding function (check for NULL in mbedtls_cipher_finish) + * but a trivial get_padding function + */ +static int get_no_padding(unsigned char *input, size_t input_len, + size_t *data_len) +{ + if (NULL == input || NULL == data_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + *data_len = input_len; + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ + +int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, + unsigned char *output, size_t *olen) +{ + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* While PSA Crypto has an API for multipart + * operations, we currently don't make it + * accessible through the cipher layer. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + *olen = 0; + + if (MBEDTLS_MODE_CFB == ctx->cipher_info->mode || + MBEDTLS_MODE_OFB == ctx->cipher_info->mode || + MBEDTLS_MODE_CTR == ctx->cipher_info->mode || + MBEDTLS_MODE_GCM == ctx->cipher_info->mode || + MBEDTLS_MODE_CCM_STAR_NO_TAG == ctx->cipher_info->mode || + MBEDTLS_MODE_XTS == ctx->cipher_info->mode || + MBEDTLS_MODE_STREAM == ctx->cipher_info->mode) { + return 0; + } + + if ((MBEDTLS_CIPHER_CHACHA20 == ctx->cipher_info->type) || + (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type)) { + return 0; + } + + if (MBEDTLS_MODE_ECB == ctx->cipher_info->mode) { + if (ctx->unprocessed_len != 0) { + return MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED; + } + + return 0; + } + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + if (MBEDTLS_MODE_CBC == ctx->cipher_info->mode) { + int ret = 0; + + if (MBEDTLS_ENCRYPT == ctx->operation) { + /* check for 'no padding' mode */ + if (NULL == ctx->add_padding) { + if (0 != ctx->unprocessed_len) { + return MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED; + } + + return 0; + } + + ctx->add_padding(ctx->unprocessed_data, mbedtls_cipher_get_iv_size(ctx), + ctx->unprocessed_len); + } else if (mbedtls_cipher_get_block_size(ctx) != ctx->unprocessed_len) { + /* + * For decrypt operations, expect a full block, + * or an empty block if no padding + */ + if (NULL == ctx->add_padding && 0 == ctx->unprocessed_len) { + return 0; + } + + return MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED; + } + + /* cipher block */ + if (0 != (ret = ctx->cipher_info->base->cbc_func(ctx->cipher_ctx, + ctx->operation, + mbedtls_cipher_get_block_size(ctx), + ctx->iv, + ctx->unprocessed_data, output))) { + return ret; + } + + /* Set output size for decryption */ + if (MBEDTLS_DECRYPT == ctx->operation) { + return ctx->get_padding(output, mbedtls_cipher_get_block_size(ctx), + olen); + } + + /* Set output size for encryption */ + *olen = mbedtls_cipher_get_block_size(ctx); + return 0; + } +#else + ((void) output); +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +} + +#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) +int mbedtls_cipher_set_padding_mode(mbedtls_cipher_context_t *ctx, + mbedtls_cipher_padding_t mode) +{ + if (NULL == ctx->cipher_info || MBEDTLS_MODE_CBC != ctx->cipher_info->mode) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* While PSA Crypto knows about CBC padding + * schemes, we currently don't make them + * accessible through the cipher layer. */ + if (mode != MBEDTLS_PADDING_NONE) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + return 0; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + switch (mode) { +#if defined(MBEDTLS_CIPHER_PADDING_PKCS7) + case MBEDTLS_PADDING_PKCS7: + ctx->add_padding = add_pkcs_padding; + ctx->get_padding = get_pkcs_padding; + break; +#endif +#if defined(MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS) + case MBEDTLS_PADDING_ONE_AND_ZEROS: + ctx->add_padding = add_one_and_zeros_padding; + ctx->get_padding = get_one_and_zeros_padding; + break; +#endif +#if defined(MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN) + case MBEDTLS_PADDING_ZEROS_AND_LEN: + ctx->add_padding = add_zeros_and_len_padding; + ctx->get_padding = get_zeros_and_len_padding; + break; +#endif +#if defined(MBEDTLS_CIPHER_PADDING_ZEROS) + case MBEDTLS_PADDING_ZEROS: + ctx->add_padding = add_zeros_padding; + ctx->get_padding = get_zeros_padding; + break; +#endif + case MBEDTLS_PADDING_NONE: + ctx->add_padding = NULL; + ctx->get_padding = get_no_padding; + break; + + default: + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + return 0; +} +#endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ + +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) +int mbedtls_cipher_write_tag(mbedtls_cipher_context_t *ctx, + unsigned char *tag, size_t tag_len) +{ + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (MBEDTLS_ENCRYPT != ctx->operation) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* While PSA Crypto has an API for multipart + * operations, we currently don't make it + * accessible through the cipher layer. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_GCM_C) + if (MBEDTLS_MODE_GCM == ctx->cipher_info->mode) { + size_t output_length; + /* The code here doesn't yet support alternative implementations + * that can delay up to a block of output. */ + return mbedtls_gcm_finish((mbedtls_gcm_context *) ctx->cipher_ctx, + NULL, 0, &output_length, + tag, tag_len); + } +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + if (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type) { + /* Don't allow truncated MAC for Poly1305 */ + if (tag_len != 16U) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return mbedtls_chachapoly_finish( + (mbedtls_chachapoly_context *) ctx->cipher_ctx, tag); + } +#endif + + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +} + +int mbedtls_cipher_check_tag(mbedtls_cipher_context_t *ctx, + const unsigned char *tag, size_t tag_len) +{ + unsigned char check_tag[16]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ctx->cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (MBEDTLS_DECRYPT != ctx->operation) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* While PSA Crypto has an API for multipart + * operations, we currently don't make it + * accessible through the cipher layer. */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* Status to return on a non-authenticated algorithm. */ + ret = MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + +#if defined(MBEDTLS_GCM_C) + if (MBEDTLS_MODE_GCM == ctx->cipher_info->mode) { + size_t output_length; + /* The code here doesn't yet support alternative implementations + * that can delay up to a block of output. */ + + if (tag_len > sizeof(check_tag)) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (0 != (ret = mbedtls_gcm_finish( + (mbedtls_gcm_context *) ctx->cipher_ctx, + NULL, 0, &output_length, + check_tag, tag_len))) { + return ret; + } + + /* Check the tag in "constant-time" */ + if (mbedtls_ct_memcmp(tag, check_tag, tag_len) != 0) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + goto exit; + } + } +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CHACHAPOLY_C) + if (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type) { + /* Don't allow truncated MAC for Poly1305 */ + if (tag_len != sizeof(check_tag)) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + ret = mbedtls_chachapoly_finish( + (mbedtls_chachapoly_context *) ctx->cipher_ctx, check_tag); + if (ret != 0) { + return ret; + } + + /* Check the tag in "constant-time" */ + if (mbedtls_ct_memcmp(tag, check_tag, tag_len) != 0) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + goto exit; + } + } +#endif /* MBEDTLS_CHACHAPOLY_C */ + +exit: + mbedtls_platform_zeroize(check_tag, tag_len); + return ret; +} +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ + +/* + * Packet-oriented wrapper for non-AEAD modes + */ +int mbedtls_cipher_crypt(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t finish_olen; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* As in the non-PSA case, we don't check that + * a key has been set. If not, the key slot will + * still be in its default state of 0, which is + * guaranteed to be invalid, hence the PSA-call + * below will gracefully fail. */ + mbedtls_cipher_context_psa * const cipher_psa = + (mbedtls_cipher_context_psa *) ctx->cipher_ctx; + + psa_status_t status; + psa_cipher_operation_t cipher_op = PSA_CIPHER_OPERATION_INIT; + size_t part_len; + + if (ctx->operation == MBEDTLS_DECRYPT) { + status = psa_cipher_decrypt_setup(&cipher_op, + cipher_psa->slot, + cipher_psa->alg); + } else if (ctx->operation == MBEDTLS_ENCRYPT) { + status = psa_cipher_encrypt_setup(&cipher_op, + cipher_psa->slot, + cipher_psa->alg); + } else { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + /* In the following, we can immediately return on an error, + * because the PSA Crypto API guarantees that cipher operations + * are terminated by unsuccessful calls to psa_cipher_update(), + * and by any call to psa_cipher_finish(). */ + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + + if (ctx->cipher_info->mode != MBEDTLS_MODE_ECB) { + status = psa_cipher_set_iv(&cipher_op, iv, iv_len); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + } + + status = psa_cipher_update(&cipher_op, + input, ilen, + output, ilen, olen); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + + status = psa_cipher_finish(&cipher_op, + output + *olen, ilen - *olen, + &part_len); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + + *olen += part_len; + return 0; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if ((ret = mbedtls_cipher_set_iv(ctx, iv, iv_len)) != 0) { + return ret; + } + + if ((ret = mbedtls_cipher_reset(ctx)) != 0) { + return ret; + } + + if ((ret = mbedtls_cipher_update(ctx, input, ilen, + output, olen)) != 0) { + return ret; + } + + if ((ret = mbedtls_cipher_finish(ctx, output + *olen, + &finish_olen)) != 0) { + return ret; + } + + *olen += finish_olen; + + return 0; +} + +#if defined(MBEDTLS_CIPHER_MODE_AEAD) +/* + * Packet-oriented encryption for AEAD modes: internal function used by + * mbedtls_cipher_auth_encrypt_ext(). + */ +static int mbedtls_cipher_aead_encrypt(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, + unsigned char *tag, size_t tag_len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* As in the non-PSA case, we don't check that + * a key has been set. If not, the key slot will + * still be in its default state of 0, which is + * guaranteed to be invalid, hence the PSA-call + * below will gracefully fail. */ + mbedtls_cipher_context_psa * const cipher_psa = + (mbedtls_cipher_context_psa *) ctx->cipher_ctx; + + psa_status_t status; + + /* PSA Crypto API always writes the authentication tag + * at the end of the encrypted message. */ + if (output == NULL || tag != output + ilen) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + status = psa_aead_encrypt(cipher_psa->slot, + cipher_psa->alg, + iv, iv_len, + ad, ad_len, + input, ilen, + output, ilen + tag_len, olen); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + + *olen -= tag_len; + return 0; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_GCM_C) + if (MBEDTLS_MODE_GCM == ctx->cipher_info->mode) { + *olen = ilen; + return mbedtls_gcm_crypt_and_tag(ctx->cipher_ctx, MBEDTLS_GCM_ENCRYPT, + ilen, iv, iv_len, ad, ad_len, + input, output, tag_len, tag); + } +#endif /* MBEDTLS_GCM_C */ +#if defined(MBEDTLS_CCM_C) + if (MBEDTLS_MODE_CCM == ctx->cipher_info->mode) { + *olen = ilen; + return mbedtls_ccm_encrypt_and_tag(ctx->cipher_ctx, ilen, + iv, iv_len, ad, ad_len, input, output, + tag, tag_len); + } +#endif /* MBEDTLS_CCM_C */ +#if defined(MBEDTLS_CHACHAPOLY_C) + if (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type) { + /* ChachaPoly has fixed length nonce and MAC (tag) */ + if ((iv_len != ctx->cipher_info->iv_size) || + (tag_len != 16U)) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + *olen = ilen; + return mbedtls_chachapoly_encrypt_and_tag(ctx->cipher_ctx, + ilen, iv, ad, ad_len, input, output, tag); + } +#endif /* MBEDTLS_CHACHAPOLY_C */ + + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +} + +/* + * Packet-oriented encryption for AEAD modes: internal function used by + * mbedtls_cipher_auth_encrypt_ext(). + */ +static int mbedtls_cipher_aead_decrypt(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, + const unsigned char *tag, size_t tag_len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ctx->psa_enabled == 1) { + /* As in the non-PSA case, we don't check that + * a key has been set. If not, the key slot will + * still be in its default state of 0, which is + * guaranteed to be invalid, hence the PSA-call + * below will gracefully fail. */ + mbedtls_cipher_context_psa * const cipher_psa = + (mbedtls_cipher_context_psa *) ctx->cipher_ctx; + + psa_status_t status; + + /* PSA Crypto API always writes the authentication tag + * at the end of the encrypted message. */ + if (input == NULL || tag != input + ilen) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + status = psa_aead_decrypt(cipher_psa->slot, + cipher_psa->alg, + iv, iv_len, + ad, ad_len, + input, ilen + tag_len, + output, ilen, olen); + if (status == PSA_ERROR_INVALID_SIGNATURE) { + return MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } else if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + + return 0; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_GCM_C) + if (MBEDTLS_MODE_GCM == ctx->cipher_info->mode) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + *olen = ilen; + ret = mbedtls_gcm_auth_decrypt(ctx->cipher_ctx, ilen, + iv, iv_len, ad, ad_len, + tag, tag_len, input, output); + + if (ret == MBEDTLS_ERR_GCM_AUTH_FAILED) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + return ret; + } +#endif /* MBEDTLS_GCM_C */ +#if defined(MBEDTLS_CCM_C) + if (MBEDTLS_MODE_CCM == ctx->cipher_info->mode) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + *olen = ilen; + ret = mbedtls_ccm_auth_decrypt(ctx->cipher_ctx, ilen, + iv, iv_len, ad, ad_len, + input, output, tag, tag_len); + + if (ret == MBEDTLS_ERR_CCM_AUTH_FAILED) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + return ret; + } +#endif /* MBEDTLS_CCM_C */ +#if defined(MBEDTLS_CHACHAPOLY_C) + if (MBEDTLS_CIPHER_CHACHA20_POLY1305 == ctx->cipher_info->type) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* ChachaPoly has fixed length nonce and MAC (tag) */ + if ((iv_len != ctx->cipher_info->iv_size) || + (tag_len != 16U)) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + *olen = ilen; + ret = mbedtls_chachapoly_auth_decrypt(ctx->cipher_ctx, ilen, + iv, ad, ad_len, tag, input, output); + + if (ret == MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + return ret; + } +#endif /* MBEDTLS_CHACHAPOLY_C */ + + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +} +#endif /* MBEDTLS_CIPHER_MODE_AEAD */ + +#if defined(MBEDTLS_CIPHER_MODE_AEAD) || defined(MBEDTLS_NIST_KW_C) +/* + * Packet-oriented encryption for AEAD/NIST_KW: public function. + */ +int mbedtls_cipher_auth_encrypt_ext(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t output_len, + size_t *olen, size_t tag_len) +{ +#if defined(MBEDTLS_NIST_KW_C) + if ( +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ctx->psa_enabled == 0 && +#endif + (MBEDTLS_MODE_KW == ctx->cipher_info->mode || + MBEDTLS_MODE_KWP == ctx->cipher_info->mode)) { + mbedtls_nist_kw_mode_t mode = (MBEDTLS_MODE_KW == ctx->cipher_info->mode) ? + MBEDTLS_KW_MODE_KW : MBEDTLS_KW_MODE_KWP; + + /* There is no iv, tag or ad associated with KW and KWP, + * so these length should be 0 as documented. */ + if (iv_len != 0 || tag_len != 0 || ad_len != 0) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + (void) iv; + (void) ad; + + return mbedtls_nist_kw_wrap(ctx->cipher_ctx, mode, input, ilen, + output, olen, output_len); + } +#endif /* MBEDTLS_NIST_KW_C */ + +#if defined(MBEDTLS_CIPHER_MODE_AEAD) + /* AEAD case: check length before passing on to shared function */ + if (output_len < ilen + tag_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + int ret = mbedtls_cipher_aead_encrypt(ctx, iv, iv_len, ad, ad_len, + input, ilen, output, olen, + output + ilen, tag_len); + *olen += tag_len; + return ret; +#else + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +#endif /* MBEDTLS_CIPHER_MODE_AEAD */ +} + +/* + * Packet-oriented decryption for AEAD/NIST_KW: public function. + */ +int mbedtls_cipher_auth_decrypt_ext(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t output_len, + size_t *olen, size_t tag_len) +{ +#if defined(MBEDTLS_NIST_KW_C) + if ( +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ctx->psa_enabled == 0 && +#endif + (MBEDTLS_MODE_KW == ctx->cipher_info->mode || + MBEDTLS_MODE_KWP == ctx->cipher_info->mode)) { + mbedtls_nist_kw_mode_t mode = (MBEDTLS_MODE_KW == ctx->cipher_info->mode) ? + MBEDTLS_KW_MODE_KW : MBEDTLS_KW_MODE_KWP; + + /* There is no iv, tag or ad associated with KW and KWP, + * so these length should be 0 as documented. */ + if (iv_len != 0 || tag_len != 0 || ad_len != 0) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + (void) iv; + (void) ad; + + return mbedtls_nist_kw_unwrap(ctx->cipher_ctx, mode, input, ilen, + output, olen, output_len); + } +#endif /* MBEDTLS_NIST_KW_C */ + +#if defined(MBEDTLS_CIPHER_MODE_AEAD) + /* AEAD case: check length before passing on to shared function */ + if (ilen < tag_len || output_len < ilen - tag_len) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return mbedtls_cipher_aead_decrypt(ctx, iv, iv_len, ad, ad_len, + input, ilen - tag_len, output, olen, + input + ilen - tag_len, tag_len); +#else + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; +#endif /* MBEDTLS_CIPHER_MODE_AEAD */ +} +#endif /* MBEDTLS_CIPHER_MODE_AEAD || MBEDTLS_NIST_KW_C */ + +#endif /* MBEDTLS_CIPHER_C */ diff --git a/r5dev/thirdparty/mbedtls/cipher_wrap.c b/r5dev/thirdparty/mbedtls/cipher_wrap.c new file mode 100644 index 00000000..c99627c0 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/cipher_wrap.c @@ -0,0 +1,2280 @@ +/** + * \file cipher_wrap.c + * + * \brief Generic cipher wrapper for mbed TLS + * + * \author Adriaan de Jong + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_CIPHER_C) + +#include "cipher_wrap.h" +#include "mbedtls/error.h" + +#if defined(MBEDTLS_CHACHAPOLY_C) +#include "mbedtls/chachapoly.h" +#endif + +#if defined(MBEDTLS_AES_C) +#include "mbedtls/aes.h" +#endif + +#if defined(MBEDTLS_CAMELLIA_C) +#include "mbedtls/camellia.h" +#endif + +#if defined(MBEDTLS_ARIA_C) +#include "mbedtls/aria.h" +#endif + +#if defined(MBEDTLS_DES_C) +#include "mbedtls/des.h" +#endif + +#if defined(MBEDTLS_CHACHA20_C) +#include "mbedtls/chacha20.h" +#endif + +#if defined(MBEDTLS_GCM_C) +#include "mbedtls/gcm.h" +#endif + +#if defined(MBEDTLS_CCM_C) +#include "mbedtls/ccm.h" +#endif + +#if defined(MBEDTLS_NIST_KW_C) +#include "mbedtls/nist_kw.h" +#endif + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +#include +#endif + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_GCM_C) +/* shared by all GCM ciphers */ +static void *gcm_ctx_alloc(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_gcm_context)); + + if (ctx != NULL) { + mbedtls_gcm_init((mbedtls_gcm_context *) ctx); + } + + return ctx; +} + +static void gcm_ctx_free(void *ctx) +{ + mbedtls_gcm_free(ctx); + mbedtls_free(ctx); +} +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CCM_C) +/* shared by all CCM ciphers */ +static void *ccm_ctx_alloc(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_ccm_context)); + + if (ctx != NULL) { + mbedtls_ccm_init((mbedtls_ccm_context *) ctx); + } + + return ctx; +} + +static void ccm_ctx_free(void *ctx) +{ + mbedtls_ccm_free(ctx); + mbedtls_free(ctx); +} +#endif /* MBEDTLS_CCM_C */ + +#if defined(MBEDTLS_AES_C) + +static int aes_crypt_ecb_wrap(void *ctx, mbedtls_operation_t operation, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_aes_crypt_ecb((mbedtls_aes_context *) ctx, operation, input, output); +} + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static int aes_crypt_cbc_wrap(void *ctx, mbedtls_operation_t operation, size_t length, + unsigned char *iv, const unsigned char *input, unsigned char *output) +{ + return mbedtls_aes_crypt_cbc((mbedtls_aes_context *) ctx, operation, length, iv, input, + output); +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +static int aes_crypt_cfb128_wrap(void *ctx, mbedtls_operation_t operation, + size_t length, size_t *iv_off, unsigned char *iv, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_aes_crypt_cfb128((mbedtls_aes_context *) ctx, operation, length, iv_off, iv, + input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) +static int aes_crypt_ofb_wrap(void *ctx, size_t length, size_t *iv_off, + unsigned char *iv, const unsigned char *input, unsigned char *output) +{ + return mbedtls_aes_crypt_ofb((mbedtls_aes_context *) ctx, length, iv_off, + iv, input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +static int aes_crypt_ctr_wrap(void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_aes_crypt_ctr((mbedtls_aes_context *) ctx, length, nc_off, nonce_counter, + stream_block, input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +static int aes_crypt_xts_wrap(void *ctx, mbedtls_operation_t operation, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + int mode; + + switch (operation) { + case MBEDTLS_ENCRYPT: + mode = MBEDTLS_AES_ENCRYPT; + break; + case MBEDTLS_DECRYPT: + mode = MBEDTLS_AES_DECRYPT; + break; + default: + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return mbedtls_aes_crypt_xts(xts_ctx, mode, length, + data_unit, input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +static int aes_setkey_dec_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_aes_setkey_dec((mbedtls_aes_context *) ctx, key, key_bitlen); +} + +static int aes_setkey_enc_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_aes_setkey_enc((mbedtls_aes_context *) ctx, key, key_bitlen); +} + +static void *aes_ctx_alloc(void) +{ + mbedtls_aes_context *aes = mbedtls_calloc(1, sizeof(mbedtls_aes_context)); + + if (aes == NULL) { + return NULL; + } + + mbedtls_aes_init(aes); + + return aes; +} + +static void aes_ctx_free(void *ctx) +{ + mbedtls_aes_free((mbedtls_aes_context *) ctx); + mbedtls_free(ctx); +} + +static const mbedtls_cipher_base_t aes_info = { + MBEDTLS_CIPHER_ID_AES, + aes_crypt_ecb_wrap, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + aes_crypt_cbc_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + aes_crypt_cfb128_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + aes_crypt_ofb_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + aes_crypt_ctr_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + aes_setkey_enc_wrap, + aes_setkey_dec_wrap, + aes_ctx_alloc, + aes_ctx_free +}; + +static const mbedtls_cipher_info_t aes_128_ecb_info = { + MBEDTLS_CIPHER_AES_128_ECB, + MBEDTLS_MODE_ECB, + 128, + "AES-128-ECB", + 0, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_192_ecb_info = { + MBEDTLS_CIPHER_AES_192_ECB, + MBEDTLS_MODE_ECB, + 192, + "AES-192-ECB", + 0, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_256_ecb_info = { + MBEDTLS_CIPHER_AES_256_ECB, + MBEDTLS_MODE_ECB, + 256, + "AES-256-ECB", + 0, + 0, + 16, + &aes_info +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const mbedtls_cipher_info_t aes_128_cbc_info = { + MBEDTLS_CIPHER_AES_128_CBC, + MBEDTLS_MODE_CBC, + 128, + "AES-128-CBC", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_192_cbc_info = { + MBEDTLS_CIPHER_AES_192_CBC, + MBEDTLS_MODE_CBC, + 192, + "AES-192-CBC", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_256_cbc_info = { + MBEDTLS_CIPHER_AES_256_CBC, + MBEDTLS_MODE_CBC, + 256, + "AES-256-CBC", + 16, + 0, + 16, + &aes_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +static const mbedtls_cipher_info_t aes_128_cfb128_info = { + MBEDTLS_CIPHER_AES_128_CFB128, + MBEDTLS_MODE_CFB, + 128, + "AES-128-CFB128", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_192_cfb128_info = { + MBEDTLS_CIPHER_AES_192_CFB128, + MBEDTLS_MODE_CFB, + 192, + "AES-192-CFB128", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_256_cfb128_info = { + MBEDTLS_CIPHER_AES_256_CFB128, + MBEDTLS_MODE_CFB, + 256, + "AES-256-CFB128", + 16, + 0, + 16, + &aes_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) +static const mbedtls_cipher_info_t aes_128_ofb_info = { + MBEDTLS_CIPHER_AES_128_OFB, + MBEDTLS_MODE_OFB, + 128, + "AES-128-OFB", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_192_ofb_info = { + MBEDTLS_CIPHER_AES_192_OFB, + MBEDTLS_MODE_OFB, + 192, + "AES-192-OFB", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_256_ofb_info = { + MBEDTLS_CIPHER_AES_256_OFB, + MBEDTLS_MODE_OFB, + 256, + "AES-256-OFB", + 16, + 0, + 16, + &aes_info +}; +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +static const mbedtls_cipher_info_t aes_128_ctr_info = { + MBEDTLS_CIPHER_AES_128_CTR, + MBEDTLS_MODE_CTR, + 128, + "AES-128-CTR", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_192_ctr_info = { + MBEDTLS_CIPHER_AES_192_CTR, + MBEDTLS_MODE_CTR, + 192, + "AES-192-CTR", + 16, + 0, + 16, + &aes_info +}; + +static const mbedtls_cipher_info_t aes_256_ctr_info = { + MBEDTLS_CIPHER_AES_256_CTR, + MBEDTLS_MODE_CTR, + 256, + "AES-256-CTR", + 16, + 0, + 16, + &aes_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +static int xts_aes_setkey_enc_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + return mbedtls_aes_xts_setkey_enc(xts_ctx, key, key_bitlen); +} + +static int xts_aes_setkey_dec_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + return mbedtls_aes_xts_setkey_dec(xts_ctx, key, key_bitlen); +} + +static void *xts_aes_ctx_alloc(void) +{ + mbedtls_aes_xts_context *xts_ctx = mbedtls_calloc(1, sizeof(*xts_ctx)); + + if (xts_ctx != NULL) { + mbedtls_aes_xts_init(xts_ctx); + } + + return xts_ctx; +} + +static void xts_aes_ctx_free(void *ctx) +{ + mbedtls_aes_xts_context *xts_ctx = ctx; + + if (xts_ctx == NULL) { + return; + } + + mbedtls_aes_xts_free(xts_ctx); + mbedtls_free(xts_ctx); +} + +static const mbedtls_cipher_base_t xts_aes_info = { + MBEDTLS_CIPHER_ID_AES, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + aes_crypt_xts_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + xts_aes_setkey_enc_wrap, + xts_aes_setkey_dec_wrap, + xts_aes_ctx_alloc, + xts_aes_ctx_free +}; + +static const mbedtls_cipher_info_t aes_128_xts_info = { + MBEDTLS_CIPHER_AES_128_XTS, + MBEDTLS_MODE_XTS, + 256, + "AES-128-XTS", + 16, + 0, + 16, + &xts_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_xts_info = { + MBEDTLS_CIPHER_AES_256_XTS, + MBEDTLS_MODE_XTS, + 512, + "AES-256-XTS", + 16, + 0, + 16, + &xts_aes_info +}; +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +#if defined(MBEDTLS_GCM_C) +static int gcm_aes_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_gcm_setkey((mbedtls_gcm_context *) ctx, MBEDTLS_CIPHER_ID_AES, + key, key_bitlen); +} + +static const mbedtls_cipher_base_t gcm_aes_info = { + MBEDTLS_CIPHER_ID_AES, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + gcm_aes_setkey_wrap, + gcm_aes_setkey_wrap, + gcm_ctx_alloc, + gcm_ctx_free, +}; + +static const mbedtls_cipher_info_t aes_128_gcm_info = { + MBEDTLS_CIPHER_AES_128_GCM, + MBEDTLS_MODE_GCM, + 128, + "AES-128-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aes_info +}; + +static const mbedtls_cipher_info_t aes_192_gcm_info = { + MBEDTLS_CIPHER_AES_192_GCM, + MBEDTLS_MODE_GCM, + 192, + "AES-192-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_gcm_info = { + MBEDTLS_CIPHER_AES_256_GCM, + MBEDTLS_MODE_GCM, + 256, + "AES-256-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aes_info +}; +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CCM_C) +static int ccm_aes_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_ccm_setkey((mbedtls_ccm_context *) ctx, MBEDTLS_CIPHER_ID_AES, + key, key_bitlen); +} + +static const mbedtls_cipher_base_t ccm_aes_info = { + MBEDTLS_CIPHER_ID_AES, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + ccm_aes_setkey_wrap, + ccm_aes_setkey_wrap, + ccm_ctx_alloc, + ccm_ctx_free, +}; + +static const mbedtls_cipher_info_t aes_128_ccm_info = { + MBEDTLS_CIPHER_AES_128_CCM, + MBEDTLS_MODE_CCM, + 128, + "AES-128-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; + +static const mbedtls_cipher_info_t aes_192_ccm_info = { + MBEDTLS_CIPHER_AES_192_CCM, + MBEDTLS_MODE_CCM, + 192, + "AES-192-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_ccm_info = { + MBEDTLS_CIPHER_AES_256_CCM, + MBEDTLS_MODE_CCM, + 256, + "AES-256-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; + +static const mbedtls_cipher_info_t aes_128_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_AES_128_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 128, + "AES-128-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; + +static const mbedtls_cipher_info_t aes_192_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_AES_192_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 192, + "AES-192-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_AES_256_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 256, + "AES-256-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aes_info +}; +#endif /* MBEDTLS_CCM_C */ + +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) + +static int camellia_crypt_ecb_wrap(void *ctx, mbedtls_operation_t operation, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_camellia_crypt_ecb((mbedtls_camellia_context *) ctx, operation, input, + output); +} + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static int camellia_crypt_cbc_wrap(void *ctx, mbedtls_operation_t operation, + size_t length, unsigned char *iv, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_camellia_crypt_cbc((mbedtls_camellia_context *) ctx, operation, length, iv, + input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +static int camellia_crypt_cfb128_wrap(void *ctx, mbedtls_operation_t operation, + size_t length, size_t *iv_off, unsigned char *iv, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_camellia_crypt_cfb128((mbedtls_camellia_context *) ctx, operation, length, + iv_off, iv, input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +static int camellia_crypt_ctr_wrap(void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_camellia_crypt_ctr((mbedtls_camellia_context *) ctx, length, nc_off, + nonce_counter, stream_block, input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +static int camellia_setkey_dec_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_camellia_setkey_dec((mbedtls_camellia_context *) ctx, key, key_bitlen); +} + +static int camellia_setkey_enc_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_camellia_setkey_enc((mbedtls_camellia_context *) ctx, key, key_bitlen); +} + +static void *camellia_ctx_alloc(void) +{ + mbedtls_camellia_context *ctx; + ctx = mbedtls_calloc(1, sizeof(mbedtls_camellia_context)); + + if (ctx == NULL) { + return NULL; + } + + mbedtls_camellia_init(ctx); + + return ctx; +} + +static void camellia_ctx_free(void *ctx) +{ + mbedtls_camellia_free((mbedtls_camellia_context *) ctx); + mbedtls_free(ctx); +} + +static const mbedtls_cipher_base_t camellia_info = { + MBEDTLS_CIPHER_ID_CAMELLIA, + camellia_crypt_ecb_wrap, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + camellia_crypt_cbc_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + camellia_crypt_cfb128_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + camellia_crypt_ctr_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + camellia_setkey_enc_wrap, + camellia_setkey_dec_wrap, + camellia_ctx_alloc, + camellia_ctx_free +}; + +static const mbedtls_cipher_info_t camellia_128_ecb_info = { + MBEDTLS_CIPHER_CAMELLIA_128_ECB, + MBEDTLS_MODE_ECB, + 128, + "CAMELLIA-128-ECB", + 0, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_192_ecb_info = { + MBEDTLS_CIPHER_CAMELLIA_192_ECB, + MBEDTLS_MODE_ECB, + 192, + "CAMELLIA-192-ECB", + 0, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_256_ecb_info = { + MBEDTLS_CIPHER_CAMELLIA_256_ECB, + MBEDTLS_MODE_ECB, + 256, + "CAMELLIA-256-ECB", + 0, + 0, + 16, + &camellia_info +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const mbedtls_cipher_info_t camellia_128_cbc_info = { + MBEDTLS_CIPHER_CAMELLIA_128_CBC, + MBEDTLS_MODE_CBC, + 128, + "CAMELLIA-128-CBC", + 16, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_192_cbc_info = { + MBEDTLS_CIPHER_CAMELLIA_192_CBC, + MBEDTLS_MODE_CBC, + 192, + "CAMELLIA-192-CBC", + 16, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_256_cbc_info = { + MBEDTLS_CIPHER_CAMELLIA_256_CBC, + MBEDTLS_MODE_CBC, + 256, + "CAMELLIA-256-CBC", + 16, + 0, + 16, + &camellia_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +static const mbedtls_cipher_info_t camellia_128_cfb128_info = { + MBEDTLS_CIPHER_CAMELLIA_128_CFB128, + MBEDTLS_MODE_CFB, + 128, + "CAMELLIA-128-CFB128", + 16, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_192_cfb128_info = { + MBEDTLS_CIPHER_CAMELLIA_192_CFB128, + MBEDTLS_MODE_CFB, + 192, + "CAMELLIA-192-CFB128", + 16, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_256_cfb128_info = { + MBEDTLS_CIPHER_CAMELLIA_256_CFB128, + MBEDTLS_MODE_CFB, + 256, + "CAMELLIA-256-CFB128", + 16, + 0, + 16, + &camellia_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +static const mbedtls_cipher_info_t camellia_128_ctr_info = { + MBEDTLS_CIPHER_CAMELLIA_128_CTR, + MBEDTLS_MODE_CTR, + 128, + "CAMELLIA-128-CTR", + 16, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_192_ctr_info = { + MBEDTLS_CIPHER_CAMELLIA_192_CTR, + MBEDTLS_MODE_CTR, + 192, + "CAMELLIA-192-CTR", + 16, + 0, + 16, + &camellia_info +}; + +static const mbedtls_cipher_info_t camellia_256_ctr_info = { + MBEDTLS_CIPHER_CAMELLIA_256_CTR, + MBEDTLS_MODE_CTR, + 256, + "CAMELLIA-256-CTR", + 16, + 0, + 16, + &camellia_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_GCM_C) +static int gcm_camellia_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_gcm_setkey((mbedtls_gcm_context *) ctx, MBEDTLS_CIPHER_ID_CAMELLIA, + key, key_bitlen); +} + +static const mbedtls_cipher_base_t gcm_camellia_info = { + MBEDTLS_CIPHER_ID_CAMELLIA, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + gcm_camellia_setkey_wrap, + gcm_camellia_setkey_wrap, + gcm_ctx_alloc, + gcm_ctx_free, +}; + +static const mbedtls_cipher_info_t camellia_128_gcm_info = { + MBEDTLS_CIPHER_CAMELLIA_128_GCM, + MBEDTLS_MODE_GCM, + 128, + "CAMELLIA-128-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_camellia_info +}; + +static const mbedtls_cipher_info_t camellia_192_gcm_info = { + MBEDTLS_CIPHER_CAMELLIA_192_GCM, + MBEDTLS_MODE_GCM, + 192, + "CAMELLIA-192-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_camellia_info +}; + +static const mbedtls_cipher_info_t camellia_256_gcm_info = { + MBEDTLS_CIPHER_CAMELLIA_256_GCM, + MBEDTLS_MODE_GCM, + 256, + "CAMELLIA-256-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_camellia_info +}; +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CCM_C) +static int ccm_camellia_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_ccm_setkey((mbedtls_ccm_context *) ctx, MBEDTLS_CIPHER_ID_CAMELLIA, + key, key_bitlen); +} + +static const mbedtls_cipher_base_t ccm_camellia_info = { + MBEDTLS_CIPHER_ID_CAMELLIA, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + ccm_camellia_setkey_wrap, + ccm_camellia_setkey_wrap, + ccm_ctx_alloc, + ccm_ctx_free, +}; + +static const mbedtls_cipher_info_t camellia_128_ccm_info = { + MBEDTLS_CIPHER_CAMELLIA_128_CCM, + MBEDTLS_MODE_CCM, + 128, + "CAMELLIA-128-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; + +static const mbedtls_cipher_info_t camellia_192_ccm_info = { + MBEDTLS_CIPHER_CAMELLIA_192_CCM, + MBEDTLS_MODE_CCM, + 192, + "CAMELLIA-192-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; + +static const mbedtls_cipher_info_t camellia_256_ccm_info = { + MBEDTLS_CIPHER_CAMELLIA_256_CCM, + MBEDTLS_MODE_CCM, + 256, + "CAMELLIA-256-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; + +static const mbedtls_cipher_info_t camellia_128_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_CAMELLIA_128_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 128, + "CAMELLIA-128-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; + +static const mbedtls_cipher_info_t camellia_192_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_CAMELLIA_192_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 192, + "CAMELLIA-192-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; + +static const mbedtls_cipher_info_t camellia_256_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_CAMELLIA_256_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 256, + "CAMELLIA-256-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_camellia_info +}; +#endif /* MBEDTLS_CCM_C */ + +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_ARIA_C) + +static int aria_crypt_ecb_wrap(void *ctx, mbedtls_operation_t operation, + const unsigned char *input, unsigned char *output) +{ + (void) operation; + return mbedtls_aria_crypt_ecb((mbedtls_aria_context *) ctx, input, + output); +} + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static int aria_crypt_cbc_wrap(void *ctx, mbedtls_operation_t operation, + size_t length, unsigned char *iv, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_aria_crypt_cbc((mbedtls_aria_context *) ctx, operation, length, iv, + input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +static int aria_crypt_cfb128_wrap(void *ctx, mbedtls_operation_t operation, + size_t length, size_t *iv_off, unsigned char *iv, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_aria_crypt_cfb128((mbedtls_aria_context *) ctx, operation, length, + iv_off, iv, input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +static int aria_crypt_ctr_wrap(void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output) +{ + return mbedtls_aria_crypt_ctr((mbedtls_aria_context *) ctx, length, nc_off, + nonce_counter, stream_block, input, output); +} +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +static int aria_setkey_dec_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_aria_setkey_dec((mbedtls_aria_context *) ctx, key, key_bitlen); +} + +static int aria_setkey_enc_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_aria_setkey_enc((mbedtls_aria_context *) ctx, key, key_bitlen); +} + +static void *aria_ctx_alloc(void) +{ + mbedtls_aria_context *ctx; + ctx = mbedtls_calloc(1, sizeof(mbedtls_aria_context)); + + if (ctx == NULL) { + return NULL; + } + + mbedtls_aria_init(ctx); + + return ctx; +} + +static void aria_ctx_free(void *ctx) +{ + mbedtls_aria_free((mbedtls_aria_context *) ctx); + mbedtls_free(ctx); +} + +static const mbedtls_cipher_base_t aria_info = { + MBEDTLS_CIPHER_ID_ARIA, + aria_crypt_ecb_wrap, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + aria_crypt_cbc_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + aria_crypt_cfb128_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + aria_crypt_ctr_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + aria_setkey_enc_wrap, + aria_setkey_dec_wrap, + aria_ctx_alloc, + aria_ctx_free +}; + +static const mbedtls_cipher_info_t aria_128_ecb_info = { + MBEDTLS_CIPHER_ARIA_128_ECB, + MBEDTLS_MODE_ECB, + 128, + "ARIA-128-ECB", + 0, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_192_ecb_info = { + MBEDTLS_CIPHER_ARIA_192_ECB, + MBEDTLS_MODE_ECB, + 192, + "ARIA-192-ECB", + 0, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_256_ecb_info = { + MBEDTLS_CIPHER_ARIA_256_ECB, + MBEDTLS_MODE_ECB, + 256, + "ARIA-256-ECB", + 0, + 0, + 16, + &aria_info +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const mbedtls_cipher_info_t aria_128_cbc_info = { + MBEDTLS_CIPHER_ARIA_128_CBC, + MBEDTLS_MODE_CBC, + 128, + "ARIA-128-CBC", + 16, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_192_cbc_info = { + MBEDTLS_CIPHER_ARIA_192_CBC, + MBEDTLS_MODE_CBC, + 192, + "ARIA-192-CBC", + 16, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_256_cbc_info = { + MBEDTLS_CIPHER_ARIA_256_CBC, + MBEDTLS_MODE_CBC, + 256, + "ARIA-256-CBC", + 16, + 0, + 16, + &aria_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +static const mbedtls_cipher_info_t aria_128_cfb128_info = { + MBEDTLS_CIPHER_ARIA_128_CFB128, + MBEDTLS_MODE_CFB, + 128, + "ARIA-128-CFB128", + 16, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_192_cfb128_info = { + MBEDTLS_CIPHER_ARIA_192_CFB128, + MBEDTLS_MODE_CFB, + 192, + "ARIA-192-CFB128", + 16, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_256_cfb128_info = { + MBEDTLS_CIPHER_ARIA_256_CFB128, + MBEDTLS_MODE_CFB, + 256, + "ARIA-256-CFB128", + 16, + 0, + 16, + &aria_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +static const mbedtls_cipher_info_t aria_128_ctr_info = { + MBEDTLS_CIPHER_ARIA_128_CTR, + MBEDTLS_MODE_CTR, + 128, + "ARIA-128-CTR", + 16, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_192_ctr_info = { + MBEDTLS_CIPHER_ARIA_192_CTR, + MBEDTLS_MODE_CTR, + 192, + "ARIA-192-CTR", + 16, + 0, + 16, + &aria_info +}; + +static const mbedtls_cipher_info_t aria_256_ctr_info = { + MBEDTLS_CIPHER_ARIA_256_CTR, + MBEDTLS_MODE_CTR, + 256, + "ARIA-256-CTR", + 16, + 0, + 16, + &aria_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_GCM_C) +static int gcm_aria_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_gcm_setkey((mbedtls_gcm_context *) ctx, MBEDTLS_CIPHER_ID_ARIA, + key, key_bitlen); +} + +static const mbedtls_cipher_base_t gcm_aria_info = { + MBEDTLS_CIPHER_ID_ARIA, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + gcm_aria_setkey_wrap, + gcm_aria_setkey_wrap, + gcm_ctx_alloc, + gcm_ctx_free, +}; + +static const mbedtls_cipher_info_t aria_128_gcm_info = { + MBEDTLS_CIPHER_ARIA_128_GCM, + MBEDTLS_MODE_GCM, + 128, + "ARIA-128-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aria_info +}; + +static const mbedtls_cipher_info_t aria_192_gcm_info = { + MBEDTLS_CIPHER_ARIA_192_GCM, + MBEDTLS_MODE_GCM, + 192, + "ARIA-192-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aria_info +}; + +static const mbedtls_cipher_info_t aria_256_gcm_info = { + MBEDTLS_CIPHER_ARIA_256_GCM, + MBEDTLS_MODE_GCM, + 256, + "ARIA-256-GCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &gcm_aria_info +}; +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CCM_C) +static int ccm_aria_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_ccm_setkey((mbedtls_ccm_context *) ctx, MBEDTLS_CIPHER_ID_ARIA, + key, key_bitlen); +} + +static const mbedtls_cipher_base_t ccm_aria_info = { + MBEDTLS_CIPHER_ID_ARIA, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + ccm_aria_setkey_wrap, + ccm_aria_setkey_wrap, + ccm_ctx_alloc, + ccm_ctx_free, +}; + +static const mbedtls_cipher_info_t aria_128_ccm_info = { + MBEDTLS_CIPHER_ARIA_128_CCM, + MBEDTLS_MODE_CCM, + 128, + "ARIA-128-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aria_info +}; + +static const mbedtls_cipher_info_t aria_192_ccm_info = { + MBEDTLS_CIPHER_ARIA_192_CCM, + MBEDTLS_MODE_CCM, + 192, + "ARIA-192-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aria_info +}; + +static const mbedtls_cipher_info_t aria_256_ccm_info = { + MBEDTLS_CIPHER_ARIA_256_CCM, + MBEDTLS_MODE_CCM, + 256, + "ARIA-256-CCM", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aria_info +}; + +static const mbedtls_cipher_info_t aria_128_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_ARIA_128_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 128, + "ARIA-128-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aria_info +}; + +static const mbedtls_cipher_info_t aria_192_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_ARIA_192_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 192, + "ARIA-192-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aria_info +}; + +static const mbedtls_cipher_info_t aria_256_ccm_star_no_tag_info = { + MBEDTLS_CIPHER_ARIA_256_CCM_STAR_NO_TAG, + MBEDTLS_MODE_CCM_STAR_NO_TAG, + 256, + "ARIA-256-CCM*-NO-TAG", + 12, + MBEDTLS_CIPHER_VARIABLE_IV_LEN, + 16, + &ccm_aria_info +}; +#endif /* MBEDTLS_CCM_C */ + +#endif /* MBEDTLS_ARIA_C */ + +#if defined(MBEDTLS_DES_C) + +static int des_crypt_ecb_wrap(void *ctx, mbedtls_operation_t operation, + const unsigned char *input, unsigned char *output) +{ + ((void) operation); + return mbedtls_des_crypt_ecb((mbedtls_des_context *) ctx, input, output); +} + +static int des3_crypt_ecb_wrap(void *ctx, mbedtls_operation_t operation, + const unsigned char *input, unsigned char *output) +{ + ((void) operation); + return mbedtls_des3_crypt_ecb((mbedtls_des3_context *) ctx, input, output); +} + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static int des_crypt_cbc_wrap(void *ctx, mbedtls_operation_t operation, size_t length, + unsigned char *iv, const unsigned char *input, unsigned char *output) +{ + return mbedtls_des_crypt_cbc((mbedtls_des_context *) ctx, operation, length, iv, input, + output); +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static int des3_crypt_cbc_wrap(void *ctx, mbedtls_operation_t operation, size_t length, + unsigned char *iv, const unsigned char *input, unsigned char *output) +{ + return mbedtls_des3_crypt_cbc((mbedtls_des3_context *) ctx, operation, length, iv, input, + output); +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +static int des_setkey_dec_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + ((void) key_bitlen); + + return mbedtls_des_setkey_dec((mbedtls_des_context *) ctx, key); +} + +static int des_setkey_enc_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + ((void) key_bitlen); + + return mbedtls_des_setkey_enc((mbedtls_des_context *) ctx, key); +} + +static int des3_set2key_dec_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + ((void) key_bitlen); + + return mbedtls_des3_set2key_dec((mbedtls_des3_context *) ctx, key); +} + +static int des3_set2key_enc_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + ((void) key_bitlen); + + return mbedtls_des3_set2key_enc((mbedtls_des3_context *) ctx, key); +} + +static int des3_set3key_dec_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + ((void) key_bitlen); + + return mbedtls_des3_set3key_dec((mbedtls_des3_context *) ctx, key); +} + +static int des3_set3key_enc_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + ((void) key_bitlen); + + return mbedtls_des3_set3key_enc((mbedtls_des3_context *) ctx, key); +} + +static void *des_ctx_alloc(void) +{ + mbedtls_des_context *des = mbedtls_calloc(1, sizeof(mbedtls_des_context)); + + if (des == NULL) { + return NULL; + } + + mbedtls_des_init(des); + + return des; +} + +static void des_ctx_free(void *ctx) +{ + mbedtls_des_free((mbedtls_des_context *) ctx); + mbedtls_free(ctx); +} + +static void *des3_ctx_alloc(void) +{ + mbedtls_des3_context *des3; + des3 = mbedtls_calloc(1, sizeof(mbedtls_des3_context)); + + if (des3 == NULL) { + return NULL; + } + + mbedtls_des3_init(des3); + + return des3; +} + +static void des3_ctx_free(void *ctx) +{ + mbedtls_des3_free((mbedtls_des3_context *) ctx); + mbedtls_free(ctx); +} + +static const mbedtls_cipher_base_t des_info = { + MBEDTLS_CIPHER_ID_DES, + des_crypt_ecb_wrap, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + des_crypt_cbc_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + des_setkey_enc_wrap, + des_setkey_dec_wrap, + des_ctx_alloc, + des_ctx_free +}; + +static const mbedtls_cipher_info_t des_ecb_info = { + MBEDTLS_CIPHER_DES_ECB, + MBEDTLS_MODE_ECB, + MBEDTLS_KEY_LENGTH_DES, + "DES-ECB", + 0, + 0, + 8, + &des_info +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const mbedtls_cipher_info_t des_cbc_info = { + MBEDTLS_CIPHER_DES_CBC, + MBEDTLS_MODE_CBC, + MBEDTLS_KEY_LENGTH_DES, + "DES-CBC", + 8, + 0, + 8, + &des_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +static const mbedtls_cipher_base_t des_ede_info = { + MBEDTLS_CIPHER_ID_DES, + des3_crypt_ecb_wrap, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + des3_crypt_cbc_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + des3_set2key_enc_wrap, + des3_set2key_dec_wrap, + des3_ctx_alloc, + des3_ctx_free +}; + +static const mbedtls_cipher_info_t des_ede_ecb_info = { + MBEDTLS_CIPHER_DES_EDE_ECB, + MBEDTLS_MODE_ECB, + MBEDTLS_KEY_LENGTH_DES_EDE, + "DES-EDE-ECB", + 0, + 0, + 8, + &des_ede_info +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const mbedtls_cipher_info_t des_ede_cbc_info = { + MBEDTLS_CIPHER_DES_EDE_CBC, + MBEDTLS_MODE_CBC, + MBEDTLS_KEY_LENGTH_DES_EDE, + "DES-EDE-CBC", + 8, + 0, + 8, + &des_ede_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +static const mbedtls_cipher_base_t des_ede3_info = { + MBEDTLS_CIPHER_ID_3DES, + des3_crypt_ecb_wrap, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + des3_crypt_cbc_wrap, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + des3_set3key_enc_wrap, + des3_set3key_dec_wrap, + des3_ctx_alloc, + des3_ctx_free +}; + +static const mbedtls_cipher_info_t des_ede3_ecb_info = { + MBEDTLS_CIPHER_DES_EDE3_ECB, + MBEDTLS_MODE_ECB, + MBEDTLS_KEY_LENGTH_DES_EDE3, + "DES-EDE3-ECB", + 0, + 0, + 8, + &des_ede3_info +}; +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const mbedtls_cipher_info_t des_ede3_cbc_info = { + MBEDTLS_CIPHER_DES_EDE3_CBC, + MBEDTLS_MODE_CBC, + MBEDTLS_KEY_LENGTH_DES_EDE3, + "DES-EDE3-CBC", + 8, + 0, + 8, + &des_ede3_info +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_CHACHA20_C) + +static int chacha20_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + if (key_bitlen != 256U) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (0 != mbedtls_chacha20_setkey((mbedtls_chacha20_context *) ctx, key)) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return 0; +} + +static int chacha20_stream_wrap(void *ctx, size_t length, + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_chacha20_update(ctx, length, input, output); + if (ret == MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return ret; +} + +static void *chacha20_ctx_alloc(void) +{ + mbedtls_chacha20_context *ctx; + ctx = mbedtls_calloc(1, sizeof(mbedtls_chacha20_context)); + + if (ctx == NULL) { + return NULL; + } + + mbedtls_chacha20_init(ctx); + + return ctx; +} + +static void chacha20_ctx_free(void *ctx) +{ + mbedtls_chacha20_free((mbedtls_chacha20_context *) ctx); + mbedtls_free(ctx); +} + +static const mbedtls_cipher_base_t chacha20_base_info = { + MBEDTLS_CIPHER_ID_CHACHA20, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + chacha20_stream_wrap, +#endif + chacha20_setkey_wrap, + chacha20_setkey_wrap, + chacha20_ctx_alloc, + chacha20_ctx_free +}; +static const mbedtls_cipher_info_t chacha20_info = { + MBEDTLS_CIPHER_CHACHA20, + MBEDTLS_MODE_STREAM, + 256, + "CHACHA20", + 12, + 0, + 1, + &chacha20_base_info +}; +#endif /* MBEDTLS_CHACHA20_C */ + +#if defined(MBEDTLS_CHACHAPOLY_C) + +static int chachapoly_setkey_wrap(void *ctx, + const unsigned char *key, + unsigned int key_bitlen) +{ + if (key_bitlen != 256U) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (0 != mbedtls_chachapoly_setkey((mbedtls_chachapoly_context *) ctx, key)) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + return 0; +} + +static void *chachapoly_ctx_alloc(void) +{ + mbedtls_chachapoly_context *ctx; + ctx = mbedtls_calloc(1, sizeof(mbedtls_chachapoly_context)); + + if (ctx == NULL) { + return NULL; + } + + mbedtls_chachapoly_init(ctx); + + return ctx; +} + +static void chachapoly_ctx_free(void *ctx) +{ + mbedtls_chachapoly_free((mbedtls_chachapoly_context *) ctx); + mbedtls_free(ctx); +} + +static const mbedtls_cipher_base_t chachapoly_base_info = { + MBEDTLS_CIPHER_ID_CHACHA20, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + chachapoly_setkey_wrap, + chachapoly_setkey_wrap, + chachapoly_ctx_alloc, + chachapoly_ctx_free +}; +static const mbedtls_cipher_info_t chachapoly_info = { + MBEDTLS_CIPHER_CHACHA20_POLY1305, + MBEDTLS_MODE_CHACHAPOLY, + 256, + "CHACHA20-POLY1305", + 12, + 0, + 1, + &chachapoly_base_info +}; +#endif /* MBEDTLS_CHACHAPOLY_C */ + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +static int null_crypt_stream(void *ctx, size_t length, + const unsigned char *input, + unsigned char *output) +{ + ((void) ctx); + memmove(output, input, length); + return 0; +} + +static int null_setkey(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + ((void) ctx); + ((void) key); + ((void) key_bitlen); + + return 0; +} + +static void *null_ctx_alloc(void) +{ + return (void *) 1; +} + +static void null_ctx_free(void *ctx) +{ + ((void) ctx); +} + +static const mbedtls_cipher_base_t null_base_info = { + MBEDTLS_CIPHER_ID_NULL, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + null_crypt_stream, +#endif + null_setkey, + null_setkey, + null_ctx_alloc, + null_ctx_free +}; + +static const mbedtls_cipher_info_t null_cipher_info = { + MBEDTLS_CIPHER_NULL, + MBEDTLS_MODE_STREAM, + 0, + "NULL", + 0, + 0, + 1, + &null_base_info +}; +#endif /* defined(MBEDTLS_CIPHER_NULL_CIPHER) */ + +#if defined(MBEDTLS_NIST_KW_C) +static void *kw_ctx_alloc(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_nist_kw_context)); + + if (ctx != NULL) { + mbedtls_nist_kw_init((mbedtls_nist_kw_context *) ctx); + } + + return ctx; +} + +static void kw_ctx_free(void *ctx) +{ + mbedtls_nist_kw_free(ctx); + mbedtls_free(ctx); +} + +static int kw_aes_setkey_wrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_nist_kw_setkey((mbedtls_nist_kw_context *) ctx, + MBEDTLS_CIPHER_ID_AES, key, key_bitlen, 1); +} + +static int kw_aes_setkey_unwrap(void *ctx, const unsigned char *key, + unsigned int key_bitlen) +{ + return mbedtls_nist_kw_setkey((mbedtls_nist_kw_context *) ctx, + MBEDTLS_CIPHER_ID_AES, key, key_bitlen, 0); +} + +static const mbedtls_cipher_base_t kw_aes_info = { + MBEDTLS_CIPHER_ID_AES, + NULL, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + NULL, +#endif +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + NULL, +#endif + kw_aes_setkey_wrap, + kw_aes_setkey_unwrap, + kw_ctx_alloc, + kw_ctx_free, +}; + +static const mbedtls_cipher_info_t aes_128_nist_kw_info = { + MBEDTLS_CIPHER_AES_128_KW, + MBEDTLS_MODE_KW, + 128, + "AES-128-KW", + 0, + 0, + 16, + &kw_aes_info +}; + +static const mbedtls_cipher_info_t aes_192_nist_kw_info = { + MBEDTLS_CIPHER_AES_192_KW, + MBEDTLS_MODE_KW, + 192, + "AES-192-KW", + 0, + 0, + 16, + &kw_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_nist_kw_info = { + MBEDTLS_CIPHER_AES_256_KW, + MBEDTLS_MODE_KW, + 256, + "AES-256-KW", + 0, + 0, + 16, + &kw_aes_info +}; + +static const mbedtls_cipher_info_t aes_128_nist_kwp_info = { + MBEDTLS_CIPHER_AES_128_KWP, + MBEDTLS_MODE_KWP, + 128, + "AES-128-KWP", + 0, + 0, + 16, + &kw_aes_info +}; + +static const mbedtls_cipher_info_t aes_192_nist_kwp_info = { + MBEDTLS_CIPHER_AES_192_KWP, + MBEDTLS_MODE_KWP, + 192, + "AES-192-KWP", + 0, + 0, + 16, + &kw_aes_info +}; + +static const mbedtls_cipher_info_t aes_256_nist_kwp_info = { + MBEDTLS_CIPHER_AES_256_KWP, + MBEDTLS_MODE_KWP, + 256, + "AES-256-KWP", + 0, + 0, + 16, + &kw_aes_info +}; +#endif /* MBEDTLS_NIST_KW_C */ + +const mbedtls_cipher_definition_t mbedtls_cipher_definitions[] = +{ +#if defined(MBEDTLS_AES_C) + { MBEDTLS_CIPHER_AES_128_ECB, &aes_128_ecb_info }, + { MBEDTLS_CIPHER_AES_192_ECB, &aes_192_ecb_info }, + { MBEDTLS_CIPHER_AES_256_ECB, &aes_256_ecb_info }, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_CIPHER_AES_128_CBC, &aes_128_cbc_info }, + { MBEDTLS_CIPHER_AES_192_CBC, &aes_192_cbc_info }, + { MBEDTLS_CIPHER_AES_256_CBC, &aes_256_cbc_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + { MBEDTLS_CIPHER_AES_128_CFB128, &aes_128_cfb128_info }, + { MBEDTLS_CIPHER_AES_192_CFB128, &aes_192_cfb128_info }, + { MBEDTLS_CIPHER_AES_256_CFB128, &aes_256_cfb128_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_OFB) + { MBEDTLS_CIPHER_AES_128_OFB, &aes_128_ofb_info }, + { MBEDTLS_CIPHER_AES_192_OFB, &aes_192_ofb_info }, + { MBEDTLS_CIPHER_AES_256_OFB, &aes_256_ofb_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + { MBEDTLS_CIPHER_AES_128_CTR, &aes_128_ctr_info }, + { MBEDTLS_CIPHER_AES_192_CTR, &aes_192_ctr_info }, + { MBEDTLS_CIPHER_AES_256_CTR, &aes_256_ctr_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_XTS) + { MBEDTLS_CIPHER_AES_128_XTS, &aes_128_xts_info }, + { MBEDTLS_CIPHER_AES_256_XTS, &aes_256_xts_info }, +#endif +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_CIPHER_AES_128_GCM, &aes_128_gcm_info }, + { MBEDTLS_CIPHER_AES_192_GCM, &aes_192_gcm_info }, + { MBEDTLS_CIPHER_AES_256_GCM, &aes_256_gcm_info }, +#endif +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_CIPHER_AES_128_CCM, &aes_128_ccm_info }, + { MBEDTLS_CIPHER_AES_192_CCM, &aes_192_ccm_info }, + { MBEDTLS_CIPHER_AES_256_CCM, &aes_256_ccm_info }, + { MBEDTLS_CIPHER_AES_128_CCM_STAR_NO_TAG, &aes_128_ccm_star_no_tag_info }, + { MBEDTLS_CIPHER_AES_192_CCM_STAR_NO_TAG, &aes_192_ccm_star_no_tag_info }, + { MBEDTLS_CIPHER_AES_256_CCM_STAR_NO_TAG, &aes_256_ccm_star_no_tag_info }, +#endif +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) + { MBEDTLS_CIPHER_CAMELLIA_128_ECB, &camellia_128_ecb_info }, + { MBEDTLS_CIPHER_CAMELLIA_192_ECB, &camellia_192_ecb_info }, + { MBEDTLS_CIPHER_CAMELLIA_256_ECB, &camellia_256_ecb_info }, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_CIPHER_CAMELLIA_128_CBC, &camellia_128_cbc_info }, + { MBEDTLS_CIPHER_CAMELLIA_192_CBC, &camellia_192_cbc_info }, + { MBEDTLS_CIPHER_CAMELLIA_256_CBC, &camellia_256_cbc_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + { MBEDTLS_CIPHER_CAMELLIA_128_CFB128, &camellia_128_cfb128_info }, + { MBEDTLS_CIPHER_CAMELLIA_192_CFB128, &camellia_192_cfb128_info }, + { MBEDTLS_CIPHER_CAMELLIA_256_CFB128, &camellia_256_cfb128_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + { MBEDTLS_CIPHER_CAMELLIA_128_CTR, &camellia_128_ctr_info }, + { MBEDTLS_CIPHER_CAMELLIA_192_CTR, &camellia_192_ctr_info }, + { MBEDTLS_CIPHER_CAMELLIA_256_CTR, &camellia_256_ctr_info }, +#endif +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_CIPHER_CAMELLIA_128_GCM, &camellia_128_gcm_info }, + { MBEDTLS_CIPHER_CAMELLIA_192_GCM, &camellia_192_gcm_info }, + { MBEDTLS_CIPHER_CAMELLIA_256_GCM, &camellia_256_gcm_info }, +#endif +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_CIPHER_CAMELLIA_128_CCM, &camellia_128_ccm_info }, + { MBEDTLS_CIPHER_CAMELLIA_192_CCM, &camellia_192_ccm_info }, + { MBEDTLS_CIPHER_CAMELLIA_256_CCM, &camellia_256_ccm_info }, + { MBEDTLS_CIPHER_CAMELLIA_128_CCM_STAR_NO_TAG, &camellia_128_ccm_star_no_tag_info }, + { MBEDTLS_CIPHER_CAMELLIA_192_CCM_STAR_NO_TAG, &camellia_192_ccm_star_no_tag_info }, + { MBEDTLS_CIPHER_CAMELLIA_256_CCM_STAR_NO_TAG, &camellia_256_ccm_star_no_tag_info }, +#endif +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_ARIA_C) + { MBEDTLS_CIPHER_ARIA_128_ECB, &aria_128_ecb_info }, + { MBEDTLS_CIPHER_ARIA_192_ECB, &aria_192_ecb_info }, + { MBEDTLS_CIPHER_ARIA_256_ECB, &aria_256_ecb_info }, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_CIPHER_ARIA_128_CBC, &aria_128_cbc_info }, + { MBEDTLS_CIPHER_ARIA_192_CBC, &aria_192_cbc_info }, + { MBEDTLS_CIPHER_ARIA_256_CBC, &aria_256_cbc_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CFB) + { MBEDTLS_CIPHER_ARIA_128_CFB128, &aria_128_cfb128_info }, + { MBEDTLS_CIPHER_ARIA_192_CFB128, &aria_192_cfb128_info }, + { MBEDTLS_CIPHER_ARIA_256_CFB128, &aria_256_cfb128_info }, +#endif +#if defined(MBEDTLS_CIPHER_MODE_CTR) + { MBEDTLS_CIPHER_ARIA_128_CTR, &aria_128_ctr_info }, + { MBEDTLS_CIPHER_ARIA_192_CTR, &aria_192_ctr_info }, + { MBEDTLS_CIPHER_ARIA_256_CTR, &aria_256_ctr_info }, +#endif +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_CIPHER_ARIA_128_GCM, &aria_128_gcm_info }, + { MBEDTLS_CIPHER_ARIA_192_GCM, &aria_192_gcm_info }, + { MBEDTLS_CIPHER_ARIA_256_GCM, &aria_256_gcm_info }, +#endif +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_CIPHER_ARIA_128_CCM, &aria_128_ccm_info }, + { MBEDTLS_CIPHER_ARIA_192_CCM, &aria_192_ccm_info }, + { MBEDTLS_CIPHER_ARIA_256_CCM, &aria_256_ccm_info }, + { MBEDTLS_CIPHER_ARIA_128_CCM_STAR_NO_TAG, &aria_128_ccm_star_no_tag_info }, + { MBEDTLS_CIPHER_ARIA_192_CCM_STAR_NO_TAG, &aria_192_ccm_star_no_tag_info }, + { MBEDTLS_CIPHER_ARIA_256_CCM_STAR_NO_TAG, &aria_256_ccm_star_no_tag_info }, +#endif +#endif /* MBEDTLS_ARIA_C */ + +#if defined(MBEDTLS_DES_C) + { MBEDTLS_CIPHER_DES_ECB, &des_ecb_info }, + { MBEDTLS_CIPHER_DES_EDE_ECB, &des_ede_ecb_info }, + { MBEDTLS_CIPHER_DES_EDE3_ECB, &des_ede3_ecb_info }, +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_CIPHER_DES_CBC, &des_cbc_info }, + { MBEDTLS_CIPHER_DES_EDE_CBC, &des_ede_cbc_info }, + { MBEDTLS_CIPHER_DES_EDE3_CBC, &des_ede3_cbc_info }, +#endif +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_CHACHA20_C) + { MBEDTLS_CIPHER_CHACHA20, &chacha20_info }, +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) + { MBEDTLS_CIPHER_CHACHA20_POLY1305, &chachapoly_info }, +#endif + +#if defined(MBEDTLS_NIST_KW_C) + { MBEDTLS_CIPHER_AES_128_KW, &aes_128_nist_kw_info }, + { MBEDTLS_CIPHER_AES_192_KW, &aes_192_nist_kw_info }, + { MBEDTLS_CIPHER_AES_256_KW, &aes_256_nist_kw_info }, + { MBEDTLS_CIPHER_AES_128_KWP, &aes_128_nist_kwp_info }, + { MBEDTLS_CIPHER_AES_192_KWP, &aes_192_nist_kwp_info }, + { MBEDTLS_CIPHER_AES_256_KWP, &aes_256_nist_kwp_info }, +#endif + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) + { MBEDTLS_CIPHER_NULL, &null_cipher_info }, +#endif /* MBEDTLS_CIPHER_NULL_CIPHER */ + + { MBEDTLS_CIPHER_NONE, NULL } +}; + +#define NUM_CIPHERS (sizeof(mbedtls_cipher_definitions) / \ + sizeof(mbedtls_cipher_definitions[0])) +int mbedtls_cipher_supported[NUM_CIPHERS]; + +#endif /* MBEDTLS_CIPHER_C */ diff --git a/r5dev/thirdparty/mbedtls/cipher_wrap.h b/r5dev/thirdparty/mbedtls/cipher_wrap.h new file mode 100644 index 00000000..052cddbe --- /dev/null +++ b/r5dev/thirdparty/mbedtls/cipher_wrap.h @@ -0,0 +1,142 @@ +/** + * \file cipher_wrap.h + * + * \brief Cipher wrappers. + * + * \author Adriaan de Jong + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_CIPHER_WRAP_H +#define MBEDTLS_CIPHER_WRAP_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/cipher.h" + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Base cipher information. The non-mode specific functions and values. + */ +struct mbedtls_cipher_base_t { + /** Base Cipher type (e.g. MBEDTLS_CIPHER_ID_AES) */ + mbedtls_cipher_id_t cipher; + + /** Encrypt using ECB */ + int (*ecb_func)(void *ctx, mbedtls_operation_t mode, + const unsigned char *input, unsigned char *output); + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + /** Encrypt using CBC */ + int (*cbc_func)(void *ctx, mbedtls_operation_t mode, size_t length, + unsigned char *iv, const unsigned char *input, + unsigned char *output); +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CFB) + /** Encrypt using CFB (Full length) */ + int (*cfb_func)(void *ctx, mbedtls_operation_t mode, size_t length, size_t *iv_off, + unsigned char *iv, const unsigned char *input, + unsigned char *output); +#endif + +#if defined(MBEDTLS_CIPHER_MODE_OFB) + /** Encrypt using OFB (Full length) */ + int (*ofb_func)(void *ctx, size_t length, size_t *iv_off, + unsigned char *iv, + const unsigned char *input, + unsigned char *output); +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CTR) + /** Encrypt using CTR */ + int (*ctr_func)(void *ctx, size_t length, size_t *nc_off, + unsigned char *nonce_counter, unsigned char *stream_block, + const unsigned char *input, unsigned char *output); +#endif + +#if defined(MBEDTLS_CIPHER_MODE_XTS) + /** Encrypt or decrypt using XTS. */ + int (*xts_func)(void *ctx, mbedtls_operation_t mode, size_t length, + const unsigned char data_unit[16], + const unsigned char *input, unsigned char *output); +#endif + +#if defined(MBEDTLS_CIPHER_MODE_STREAM) + /** Encrypt using STREAM */ + int (*stream_func)(void *ctx, size_t length, + const unsigned char *input, unsigned char *output); +#endif + + /** Set key for encryption purposes */ + int (*setkey_enc_func)(void *ctx, const unsigned char *key, + unsigned int key_bitlen); + + /** Set key for decryption purposes */ + int (*setkey_dec_func)(void *ctx, const unsigned char *key, + unsigned int key_bitlen); + + /** Allocate a new context */ + void * (*ctx_alloc_func)(void); + + /** Free the given context */ + void (*ctx_free_func)(void *ctx); + +}; + +typedef struct { + mbedtls_cipher_type_t type; + const mbedtls_cipher_info_t *info; +} mbedtls_cipher_definition_t; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +typedef enum { + MBEDTLS_CIPHER_PSA_KEY_UNSET = 0, + MBEDTLS_CIPHER_PSA_KEY_OWNED, /* Used for PSA-based cipher contexts which */ + /* use raw key material internally imported */ + /* as a volatile key, and which hence need */ + /* to destroy that key when the context is */ + /* freed. */ + MBEDTLS_CIPHER_PSA_KEY_NOT_OWNED, /* Used for PSA-based cipher contexts */ + /* which use a key provided by the */ + /* user, and which hence will not be */ + /* destroyed when the context is freed. */ +} mbedtls_cipher_psa_key_ownership; + +typedef struct { + psa_algorithm_t alg; + mbedtls_svc_key_id_t slot; + mbedtls_cipher_psa_key_ownership slot_state; +} mbedtls_cipher_context_psa; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +extern const mbedtls_cipher_definition_t mbedtls_cipher_definitions[]; + +extern int mbedtls_cipher_supported[]; + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CIPHER_WRAP_H */ diff --git a/r5dev/thirdparty/mbedtls/cmac.c b/r5dev/thirdparty/mbedtls/cmac.c new file mode 100644 index 00000000..7d90ad2f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/cmac.c @@ -0,0 +1,1071 @@ +/** + * \file cmac.c + * + * \brief NIST SP800-38B compliant CMAC implementation for AES and 3DES + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * References: + * + * - NIST SP 800-38B Recommendation for Block Cipher Modes of Operation: The + * CMAC Mode for Authentication + * http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38b.pdf + * + * - RFC 4493 - The AES-CMAC Algorithm + * https://tools.ietf.org/html/rfc4493 + * + * - RFC 4615 - The Advanced Encryption Standard-Cipher-based Message + * Authentication Code-Pseudo-Random Function-128 (AES-CMAC-PRF-128) + * Algorithm for the Internet Key Exchange Protocol (IKE) + * https://tools.ietf.org/html/rfc4615 + * + * Additional test vectors: ISO/IEC 9797-1 + * + */ + +#include "common.h" + +#if defined(MBEDTLS_CMAC_C) + +#include "mbedtls/cmac.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "mbedtls/platform.h" + +#include + +#if !defined(MBEDTLS_CMAC_ALT) || defined(MBEDTLS_SELF_TEST) + +/* + * Multiplication by u in the Galois field of GF(2^n) + * + * As explained in NIST SP 800-38B, this can be computed: + * + * If MSB(p) = 0, then p = (p << 1) + * If MSB(p) = 1, then p = (p << 1) ^ R_n + * with R_64 = 0x1B and R_128 = 0x87 + * + * Input and output MUST NOT point to the same buffer + * Block size must be 8 bytes or 16 bytes - the block sizes for DES and AES. + */ +static int cmac_multiply_by_u(unsigned char *output, + const unsigned char *input, + size_t blocksize) +{ + const unsigned char R_128 = 0x87; + const unsigned char R_64 = 0x1B; + unsigned char R_n, mask; + unsigned char overflow = 0x00; + int i; + + if (blocksize == MBEDTLS_AES_BLOCK_SIZE) { + R_n = R_128; + } else if (blocksize == MBEDTLS_DES3_BLOCK_SIZE) { + R_n = R_64; + } else { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + for (i = (int) blocksize - 1; i >= 0; i--) { + output[i] = input[i] << 1 | overflow; + overflow = input[i] >> 7; + } + + /* mask = ( input[0] >> 7 ) ? 0xff : 0x00 + * using bit operations to avoid branches */ + + /* MSVC has a warning about unary minus on unsigned, but this is + * well-defined and precisely what we want to do here */ +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + mask = -(input[0] >> 7); +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif + + output[blocksize - 1] ^= R_n & mask; + + return 0; +} + +/* + * Generate subkeys + * + * - as specified by RFC 4493, section 2.3 Subkey Generation Algorithm + */ +static int cmac_generate_subkeys(mbedtls_cipher_context_t *ctx, + unsigned char *K1, unsigned char *K2) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char L[MBEDTLS_CIPHER_BLKSIZE_MAX]; + size_t olen, block_size; + + mbedtls_platform_zeroize(L, sizeof(L)); + + block_size = ctx->cipher_info->block_size; + + /* Calculate Ek(0) */ + if ((ret = mbedtls_cipher_update(ctx, L, block_size, L, &olen)) != 0) { + goto exit; + } + + /* + * Generate K1 and K2 + */ + if ((ret = cmac_multiply_by_u(K1, L, block_size)) != 0) { + goto exit; + } + + if ((ret = cmac_multiply_by_u(K2, K1, block_size)) != 0) { + goto exit; + } + +exit: + mbedtls_platform_zeroize(L, sizeof(L)); + + return ret; +} +#endif /* !defined(MBEDTLS_CMAC_ALT) || defined(MBEDTLS_SELF_TEST) */ + +#if !defined(MBEDTLS_CMAC_ALT) + +/* + * Create padded last block from (partial) last block. + * + * We can't use the padding option from the cipher layer, as it only works for + * CBC and we use ECB mode, and anyway we need to XOR K1 or K2 in addition. + */ +static void cmac_pad(unsigned char padded_block[MBEDTLS_CIPHER_BLKSIZE_MAX], + size_t padded_block_len, + const unsigned char *last_block, + size_t last_block_len) +{ + size_t j; + + for (j = 0; j < padded_block_len; j++) { + if (j < last_block_len) { + padded_block[j] = last_block[j]; + } else if (j == last_block_len) { + padded_block[j] = 0x80; + } else { + padded_block[j] = 0x00; + } + } +} + +int mbedtls_cipher_cmac_starts(mbedtls_cipher_context_t *ctx, + const unsigned char *key, size_t keybits) +{ + mbedtls_cipher_type_t type; + mbedtls_cmac_context_t *cmac_ctx; + int retval; + + if (ctx == NULL || ctx->cipher_info == NULL || key == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if ((retval = mbedtls_cipher_setkey(ctx, key, (int) keybits, + MBEDTLS_ENCRYPT)) != 0) { + return retval; + } + + type = ctx->cipher_info->type; + + switch (type) { + case MBEDTLS_CIPHER_AES_128_ECB: + case MBEDTLS_CIPHER_AES_192_ECB: + case MBEDTLS_CIPHER_AES_256_ECB: + case MBEDTLS_CIPHER_DES_EDE3_ECB: + break; + default: + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + /* Allocated and initialise in the cipher context memory for the CMAC + * context */ + cmac_ctx = mbedtls_calloc(1, sizeof(mbedtls_cmac_context_t)); + if (cmac_ctx == NULL) { + return MBEDTLS_ERR_CIPHER_ALLOC_FAILED; + } + + ctx->cmac_ctx = cmac_ctx; + + mbedtls_platform_zeroize(cmac_ctx->state, sizeof(cmac_ctx->state)); + + return 0; +} + +int mbedtls_cipher_cmac_update(mbedtls_cipher_context_t *ctx, + const unsigned char *input, size_t ilen) +{ + mbedtls_cmac_context_t *cmac_ctx; + unsigned char *state; + int ret = 0; + size_t n, j, olen, block_size; + + if (ctx == NULL || ctx->cipher_info == NULL || input == NULL || + ctx->cmac_ctx == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + cmac_ctx = ctx->cmac_ctx; + block_size = ctx->cipher_info->block_size; + state = ctx->cmac_ctx->state; + + /* Is there data still to process from the last call, that's greater in + * size than a block? */ + if (cmac_ctx->unprocessed_len > 0 && + ilen > block_size - cmac_ctx->unprocessed_len) { + memcpy(&cmac_ctx->unprocessed_block[cmac_ctx->unprocessed_len], + input, + block_size - cmac_ctx->unprocessed_len); + + mbedtls_xor(state, cmac_ctx->unprocessed_block, state, block_size); + + if ((ret = mbedtls_cipher_update(ctx, state, block_size, state, + &olen)) != 0) { + goto exit; + } + + input += block_size - cmac_ctx->unprocessed_len; + ilen -= block_size - cmac_ctx->unprocessed_len; + cmac_ctx->unprocessed_len = 0; + } + + /* n is the number of blocks including any final partial block */ + n = (ilen + block_size - 1) / block_size; + + /* Iterate across the input data in block sized chunks, excluding any + * final partial or complete block */ + for (j = 1; j < n; j++) { + mbedtls_xor(state, input, state, block_size); + + if ((ret = mbedtls_cipher_update(ctx, state, block_size, state, + &olen)) != 0) { + goto exit; + } + + ilen -= block_size; + input += block_size; + } + + /* If there is data left over that wasn't aligned to a block */ + if (ilen > 0) { + memcpy(&cmac_ctx->unprocessed_block[cmac_ctx->unprocessed_len], + input, + ilen); + cmac_ctx->unprocessed_len += ilen; + } + +exit: + return ret; +} + +int mbedtls_cipher_cmac_finish(mbedtls_cipher_context_t *ctx, + unsigned char *output) +{ + mbedtls_cmac_context_t *cmac_ctx; + unsigned char *state, *last_block; + unsigned char K1[MBEDTLS_CIPHER_BLKSIZE_MAX]; + unsigned char K2[MBEDTLS_CIPHER_BLKSIZE_MAX]; + unsigned char M_last[MBEDTLS_CIPHER_BLKSIZE_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen, block_size; + + if (ctx == NULL || ctx->cipher_info == NULL || ctx->cmac_ctx == NULL || + output == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + cmac_ctx = ctx->cmac_ctx; + block_size = ctx->cipher_info->block_size; + state = cmac_ctx->state; + + mbedtls_platform_zeroize(K1, sizeof(K1)); + mbedtls_platform_zeroize(K2, sizeof(K2)); + cmac_generate_subkeys(ctx, K1, K2); + + last_block = cmac_ctx->unprocessed_block; + + /* Calculate last block */ + if (cmac_ctx->unprocessed_len < block_size) { + cmac_pad(M_last, block_size, last_block, cmac_ctx->unprocessed_len); + mbedtls_xor(M_last, M_last, K2, block_size); + } else { + /* Last block is complete block */ + mbedtls_xor(M_last, last_block, K1, block_size); + } + + + mbedtls_xor(state, M_last, state, block_size); + if ((ret = mbedtls_cipher_update(ctx, state, block_size, state, + &olen)) != 0) { + goto exit; + } + + memcpy(output, state, block_size); + +exit: + /* Wipe the generated keys on the stack, and any other transients to avoid + * side channel leakage */ + mbedtls_platform_zeroize(K1, sizeof(K1)); + mbedtls_platform_zeroize(K2, sizeof(K2)); + + cmac_ctx->unprocessed_len = 0; + mbedtls_platform_zeroize(cmac_ctx->unprocessed_block, + sizeof(cmac_ctx->unprocessed_block)); + + mbedtls_platform_zeroize(state, MBEDTLS_CIPHER_BLKSIZE_MAX); + return ret; +} + +int mbedtls_cipher_cmac_reset(mbedtls_cipher_context_t *ctx) +{ + mbedtls_cmac_context_t *cmac_ctx; + + if (ctx == NULL || ctx->cipher_info == NULL || ctx->cmac_ctx == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + cmac_ctx = ctx->cmac_ctx; + + /* Reset the internal state */ + cmac_ctx->unprocessed_len = 0; + mbedtls_platform_zeroize(cmac_ctx->unprocessed_block, + sizeof(cmac_ctx->unprocessed_block)); + mbedtls_platform_zeroize(cmac_ctx->state, + sizeof(cmac_ctx->state)); + + return 0; +} + +int mbedtls_cipher_cmac(const mbedtls_cipher_info_t *cipher_info, + const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output) +{ + mbedtls_cipher_context_t ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (cipher_info == NULL || key == NULL || input == NULL || output == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + mbedtls_cipher_init(&ctx); + + if ((ret = mbedtls_cipher_setup(&ctx, cipher_info)) != 0) { + goto exit; + } + + ret = mbedtls_cipher_cmac_starts(&ctx, key, keylen); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_cipher_cmac_update(&ctx, input, ilen); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_cipher_cmac_finish(&ctx, output); + +exit: + mbedtls_cipher_free(&ctx); + + return ret; +} + +#if defined(MBEDTLS_AES_C) +/* + * Implementation of AES-CMAC-PRF-128 defined in RFC 4615 + */ +int mbedtls_aes_cmac_prf_128(const unsigned char *key, size_t key_length, + const unsigned char *input, size_t in_len, + unsigned char output[16]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_cipher_info_t *cipher_info; + unsigned char zero_key[MBEDTLS_AES_BLOCK_SIZE]; + unsigned char int_key[MBEDTLS_AES_BLOCK_SIZE]; + + if (key == NULL || input == NULL || output == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB); + if (cipher_info == NULL) { + /* Failing at this point must be due to a build issue */ + ret = MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + goto exit; + } + + if (key_length == MBEDTLS_AES_BLOCK_SIZE) { + /* Use key as is */ + memcpy(int_key, key, MBEDTLS_AES_BLOCK_SIZE); + } else { + memset(zero_key, 0, MBEDTLS_AES_BLOCK_SIZE); + + ret = mbedtls_cipher_cmac(cipher_info, zero_key, 128, key, + key_length, int_key); + if (ret != 0) { + goto exit; + } + } + + ret = mbedtls_cipher_cmac(cipher_info, int_key, 128, input, in_len, + output); + +exit: + mbedtls_platform_zeroize(int_key, sizeof(int_key)); + + return ret; +} +#endif /* MBEDTLS_AES_C */ + +#endif /* !MBEDTLS_CMAC_ALT */ + +#if defined(MBEDTLS_SELF_TEST) +/* + * CMAC test data for SP800-38B + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_CMAC.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/TDES_CMAC.pdf + * + * AES-CMAC-PRF-128 test data from RFC 4615 + * https://tools.ietf.org/html/rfc4615#page-4 + */ + +#define NB_CMAC_TESTS_PER_KEY 4 +#define NB_PRF_TESTS 3 + +#if defined(MBEDTLS_AES_C) || defined(MBEDTLS_DES_C) +/* All CMAC test inputs are truncated from the same 64 byte buffer. */ +static const unsigned char test_message[] = { + /* PT */ + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 +}; +#endif /* MBEDTLS_AES_C || MBEDTLS_DES_C */ + +#if defined(MBEDTLS_AES_C) +/* Truncation point of message for AES CMAC tests */ +static const unsigned int aes_message_lengths[NB_CMAC_TESTS_PER_KEY] = { + /* Mlen */ + 0, + 16, + 20, + 64 +}; + +/* CMAC-AES128 Test Data */ +static const unsigned char aes_128_key[16] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c +}; +static const unsigned char aes_128_subkeys[2][MBEDTLS_AES_BLOCK_SIZE] = { + { + /* K1 */ + 0xfb, 0xee, 0xd6, 0x18, 0x35, 0x71, 0x33, 0x66, + 0x7c, 0x85, 0xe0, 0x8f, 0x72, 0x36, 0xa8, 0xde + }, + { + /* K2 */ + 0xf7, 0xdd, 0xac, 0x30, 0x6a, 0xe2, 0x66, 0xcc, + 0xf9, 0x0b, 0xc1, 0x1e, 0xe4, 0x6d, 0x51, 0x3b + } +}; +static const unsigned char aes_128_expected_result[NB_CMAC_TESTS_PER_KEY][MBEDTLS_AES_BLOCK_SIZE] = +{ + { + /* Example #1 */ + 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, + 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 + }, + { + /* Example #2 */ + 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, + 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c + }, + { + /* Example #3 */ + 0x7d, 0x85, 0x44, 0x9e, 0xa6, 0xea, 0x19, 0xc8, + 0x23, 0xa7, 0xbf, 0x78, 0x83, 0x7d, 0xfa, 0xde + }, + { + /* Example #4 */ + 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, + 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe + } +}; + +/* CMAC-AES192 Test Data */ +static const unsigned char aes_192_key[24] = { + 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, + 0xc8, 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, + 0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b +}; +static const unsigned char aes_192_subkeys[2][MBEDTLS_AES_BLOCK_SIZE] = { + { + /* K1 */ + 0x44, 0x8a, 0x5b, 0x1c, 0x93, 0x51, 0x4b, 0x27, + 0x3e, 0xe6, 0x43, 0x9d, 0xd4, 0xda, 0xa2, 0x96 + }, + { + /* K2 */ + 0x89, 0x14, 0xb6, 0x39, 0x26, 0xa2, 0x96, 0x4e, + 0x7d, 0xcc, 0x87, 0x3b, 0xa9, 0xb5, 0x45, 0x2c + } +}; +static const unsigned char aes_192_expected_result[NB_CMAC_TESTS_PER_KEY][MBEDTLS_AES_BLOCK_SIZE] = +{ + { + /* Example #1 */ + 0xd1, 0x7d, 0xdf, 0x46, 0xad, 0xaa, 0xcd, 0xe5, + 0x31, 0xca, 0xc4, 0x83, 0xde, 0x7a, 0x93, 0x67 + }, + { + /* Example #2 */ + 0x9e, 0x99, 0xa7, 0xbf, 0x31, 0xe7, 0x10, 0x90, + 0x06, 0x62, 0xf6, 0x5e, 0x61, 0x7c, 0x51, 0x84 + }, + { + /* Example #3 */ + 0x3d, 0x75, 0xc1, 0x94, 0xed, 0x96, 0x07, 0x04, + 0x44, 0xa9, 0xfa, 0x7e, 0xc7, 0x40, 0xec, 0xf8 + }, + { + /* Example #4 */ + 0xa1, 0xd5, 0xdf, 0x0e, 0xed, 0x79, 0x0f, 0x79, + 0x4d, 0x77, 0x58, 0x96, 0x59, 0xf3, 0x9a, 0x11 + } +}; + +/* CMAC-AES256 Test Data */ +static const unsigned char aes_256_key[32] = { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, + 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 +}; +static const unsigned char aes_256_subkeys[2][MBEDTLS_AES_BLOCK_SIZE] = { + { + /* K1 */ + 0xca, 0xd1, 0xed, 0x03, 0x29, 0x9e, 0xed, 0xac, + 0x2e, 0x9a, 0x99, 0x80, 0x86, 0x21, 0x50, 0x2f + }, + { + /* K2 */ + 0x95, 0xa3, 0xda, 0x06, 0x53, 0x3d, 0xdb, 0x58, + 0x5d, 0x35, 0x33, 0x01, 0x0c, 0x42, 0xa0, 0xd9 + } +}; +static const unsigned char aes_256_expected_result[NB_CMAC_TESTS_PER_KEY][MBEDTLS_AES_BLOCK_SIZE] = +{ + { + /* Example #1 */ + 0x02, 0x89, 0x62, 0xf6, 0x1b, 0x7b, 0xf8, 0x9e, + 0xfc, 0x6b, 0x55, 0x1f, 0x46, 0x67, 0xd9, 0x83 + }, + { + /* Example #2 */ + 0x28, 0xa7, 0x02, 0x3f, 0x45, 0x2e, 0x8f, 0x82, + 0xbd, 0x4b, 0xf2, 0x8d, 0x8c, 0x37, 0xc3, 0x5c + }, + { + /* Example #3 */ + 0x15, 0x67, 0x27, 0xdc, 0x08, 0x78, 0x94, 0x4a, + 0x02, 0x3c, 0x1f, 0xe0, 0x3b, 0xad, 0x6d, 0x93 + }, + { + /* Example #4 */ + 0xe1, 0x99, 0x21, 0x90, 0x54, 0x9f, 0x6e, 0xd5, + 0x69, 0x6a, 0x2c, 0x05, 0x6c, 0x31, 0x54, 0x10 + } +}; +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_DES_C) +/* Truncation point of message for 3DES CMAC tests */ +static const unsigned int des3_message_lengths[NB_CMAC_TESTS_PER_KEY] = { + 0, + 16, + 20, + 32 +}; + +/* CMAC-TDES (Generation) - 2 Key Test Data */ +static const unsigned char des3_2key_key[24] = { + /* Key1 */ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + /* Key2 */ + 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xEF, 0x01, + /* Key3 */ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef +}; +static const unsigned char des3_2key_subkeys[2][8] = { + { + /* K1 */ + 0x0d, 0xd2, 0xcb, 0x7a, 0x3d, 0x88, 0x88, 0xd9 + }, + { + /* K2 */ + 0x1b, 0xa5, 0x96, 0xf4, 0x7b, 0x11, 0x11, 0xb2 + } +}; +static const unsigned char des3_2key_expected_result[NB_CMAC_TESTS_PER_KEY][MBEDTLS_DES3_BLOCK_SIZE] + = { + { + /* Sample #1 */ + 0x79, 0xce, 0x52, 0xa7, 0xf7, 0x86, 0xa9, 0x60 + }, + { + /* Sample #2 */ + 0xcc, 0x18, 0xa0, 0xb7, 0x9a, 0xf2, 0x41, 0x3b + }, + { + /* Sample #3 */ + 0xc0, 0x6d, 0x37, 0x7e, 0xcd, 0x10, 0x19, 0x69 + }, + { + /* Sample #4 */ + 0x9c, 0xd3, 0x35, 0x80, 0xf9, 0xb6, 0x4d, 0xfb + } + }; + +/* CMAC-TDES (Generation) - 3 Key Test Data */ +static const unsigned char des3_3key_key[24] = { + /* Key1 */ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xaa, 0xcd, 0xef, + /* Key2 */ + 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, + /* Key3 */ + 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23 +}; +static const unsigned char des3_3key_subkeys[2][8] = { + { + /* K1 */ + 0x9d, 0x74, 0xe7, 0x39, 0x33, 0x17, 0x96, 0xc0 + }, + { + /* K2 */ + 0x3a, 0xe9, 0xce, 0x72, 0x66, 0x2f, 0x2d, 0x9b + } +}; +static const unsigned char des3_3key_expected_result[NB_CMAC_TESTS_PER_KEY][MBEDTLS_DES3_BLOCK_SIZE] + = { + { + /* Sample #1 */ + 0x7d, 0xb0, 0xd3, 0x7d, 0xf9, 0x36, 0xc5, 0x50 + }, + { + /* Sample #2 */ + 0x30, 0x23, 0x9c, 0xf1, 0xf5, 0x2e, 0x66, 0x09 + }, + { + /* Sample #3 */ + 0x6c, 0x9f, 0x3e, 0xe4, 0x92, 0x3f, 0x6b, 0xe2 + }, + { + /* Sample #4 */ + 0x99, 0x42, 0x9b, 0xd0, 0xbF, 0x79, 0x04, 0xe5 + } + }; + +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_AES_C) +/* AES AES-CMAC-PRF-128 Test Data */ +static const unsigned char PRFK[] = { + /* Key */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0xed, 0xcb +}; + +/* Sizes in bytes */ +static const size_t PRFKlen[NB_PRF_TESTS] = { + 18, + 16, + 10 +}; + +/* Message */ +static const unsigned char PRFM[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 +}; + +static const unsigned char PRFT[NB_PRF_TESTS][16] = { + { + 0x84, 0xa3, 0x48, 0xa4, 0xa4, 0x5d, 0x23, 0x5b, + 0xab, 0xff, 0xfc, 0x0d, 0x2b, 0x4d, 0xa0, 0x9a + }, + { + 0x98, 0x0a, 0xe8, 0x7b, 0x5f, 0x4c, 0x9c, 0x52, + 0x14, 0xf5, 0xb6, 0xa8, 0x45, 0x5e, 0x4c, 0x2d + }, + { + 0x29, 0x0d, 0x9e, 0x11, 0x2e, 0xdb, 0x09, 0xee, + 0x14, 0x1f, 0xcf, 0x64, 0xc0, 0xb7, 0x2f, 0x3d + } +}; +#endif /* MBEDTLS_AES_C */ + +static int cmac_test_subkeys(int verbose, + const char *testname, + const unsigned char *key, + int keybits, + const unsigned char *subkeys, + mbedtls_cipher_type_t cipher_type, + int block_size, + int num_tests) +{ + int i, ret = 0; + mbedtls_cipher_context_t ctx; + const mbedtls_cipher_info_t *cipher_info; + unsigned char K1[MBEDTLS_CIPHER_BLKSIZE_MAX]; + unsigned char K2[MBEDTLS_CIPHER_BLKSIZE_MAX]; + + cipher_info = mbedtls_cipher_info_from_type(cipher_type); + if (cipher_info == NULL) { + /* Failing at this point must be due to a build issue */ + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + for (i = 0; i < num_tests; i++) { + if (verbose != 0) { + mbedtls_printf(" %s CMAC subkey #%d: ", testname, i + 1); + } + + mbedtls_cipher_init(&ctx); + + if ((ret = mbedtls_cipher_setup(&ctx, cipher_info)) != 0) { + if (verbose != 0) { + mbedtls_printf("test execution failed\n"); + } + + goto cleanup; + } + + if ((ret = mbedtls_cipher_setkey(&ctx, key, keybits, + MBEDTLS_ENCRYPT)) != 0) { + /* When CMAC is implemented by an alternative implementation, or + * the underlying primitive itself is implemented alternatively, + * AES-192 may be unavailable. This should not cause the selftest + * function to fail. */ + if ((ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED || + ret == MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE) && + cipher_type == MBEDTLS_CIPHER_AES_192_ECB) { + if (verbose != 0) { + mbedtls_printf("skipped\n"); + } + goto next_test; + } + + if (verbose != 0) { + mbedtls_printf("test execution failed\n"); + } + + goto cleanup; + } + + ret = cmac_generate_subkeys(&ctx, K1, K2); + if (ret != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + goto cleanup; + } + + if ((ret = memcmp(K1, subkeys, block_size)) != 0 || + (ret = memcmp(K2, &subkeys[block_size], block_size)) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + +next_test: + mbedtls_cipher_free(&ctx); + } + + ret = 0; + goto exit; + +cleanup: + mbedtls_cipher_free(&ctx); + +exit: + return ret; +} + +static int cmac_test_wth_cipher(int verbose, + const char *testname, + const unsigned char *key, + int keybits, + const unsigned char *messages, + const unsigned int message_lengths[4], + const unsigned char *expected_result, + mbedtls_cipher_type_t cipher_type, + int block_size, + int num_tests) +{ + const mbedtls_cipher_info_t *cipher_info; + int i, ret = 0; + unsigned char output[MBEDTLS_CIPHER_BLKSIZE_MAX]; + + cipher_info = mbedtls_cipher_info_from_type(cipher_type); + if (cipher_info == NULL) { + /* Failing at this point must be due to a build issue */ + ret = MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + goto exit; + } + + for (i = 0; i < num_tests; i++) { + if (verbose != 0) { + mbedtls_printf(" %s CMAC #%d: ", testname, i + 1); + } + + if ((ret = mbedtls_cipher_cmac(cipher_info, key, keybits, messages, + message_lengths[i], output)) != 0) { + /* When CMAC is implemented by an alternative implementation, or + * the underlying primitive itself is implemented alternatively, + * AES-192 and/or 3DES may be unavailable. This should not cause + * the selftest function to fail. */ + if ((ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED || + ret == MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE) && + (cipher_type == MBEDTLS_CIPHER_AES_192_ECB || + cipher_type == MBEDTLS_CIPHER_DES_EDE3_ECB)) { + if (verbose != 0) { + mbedtls_printf("skipped\n"); + } + continue; + } + + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + goto exit; + } + + if ((ret = memcmp(output, &expected_result[i * block_size], block_size)) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + ret = 0; + +exit: + return ret; +} + +#if defined(MBEDTLS_AES_C) +static int test_aes128_cmac_prf(int verbose) +{ + int i; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char output[MBEDTLS_AES_BLOCK_SIZE]; + + for (i = 0; i < NB_PRF_TESTS; i++) { + mbedtls_printf(" AES CMAC 128 PRF #%d: ", i); + ret = mbedtls_aes_cmac_prf_128(PRFK, PRFKlen[i], PRFM, 20, output); + if (ret != 0 || + memcmp(output, PRFT[i], MBEDTLS_AES_BLOCK_SIZE) != 0) { + + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return ret; + } else if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + return ret; +} +#endif /* MBEDTLS_AES_C */ + +int mbedtls_cmac_self_test(int verbose) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_AES_C) + /* AES-128 */ + if ((ret = cmac_test_subkeys(verbose, + "AES 128", + aes_128_key, + 128, + (const unsigned char *) aes_128_subkeys, + MBEDTLS_CIPHER_AES_128_ECB, + MBEDTLS_AES_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + if ((ret = cmac_test_wth_cipher(verbose, + "AES 128", + aes_128_key, + 128, + test_message, + aes_message_lengths, + (const unsigned char *) aes_128_expected_result, + MBEDTLS_CIPHER_AES_128_ECB, + MBEDTLS_AES_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + /* AES-192 */ + if ((ret = cmac_test_subkeys(verbose, + "AES 192", + aes_192_key, + 192, + (const unsigned char *) aes_192_subkeys, + MBEDTLS_CIPHER_AES_192_ECB, + MBEDTLS_AES_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + if ((ret = cmac_test_wth_cipher(verbose, + "AES 192", + aes_192_key, + 192, + test_message, + aes_message_lengths, + (const unsigned char *) aes_192_expected_result, + MBEDTLS_CIPHER_AES_192_ECB, + MBEDTLS_AES_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + /* AES-256 */ + if ((ret = cmac_test_subkeys(verbose, + "AES 256", + aes_256_key, + 256, + (const unsigned char *) aes_256_subkeys, + MBEDTLS_CIPHER_AES_256_ECB, + MBEDTLS_AES_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + if ((ret = cmac_test_wth_cipher(verbose, + "AES 256", + aes_256_key, + 256, + test_message, + aes_message_lengths, + (const unsigned char *) aes_256_expected_result, + MBEDTLS_CIPHER_AES_256_ECB, + MBEDTLS_AES_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_DES_C) + /* 3DES 2 key */ + if ((ret = cmac_test_subkeys(verbose, + "3DES 2 key", + des3_2key_key, + 192, + (const unsigned char *) des3_2key_subkeys, + MBEDTLS_CIPHER_DES_EDE3_ECB, + MBEDTLS_DES3_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + if ((ret = cmac_test_wth_cipher(verbose, + "3DES 2 key", + des3_2key_key, + 192, + test_message, + des3_message_lengths, + (const unsigned char *) des3_2key_expected_result, + MBEDTLS_CIPHER_DES_EDE3_ECB, + MBEDTLS_DES3_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + /* 3DES 3 key */ + if ((ret = cmac_test_subkeys(verbose, + "3DES 3 key", + des3_3key_key, + 192, + (const unsigned char *) des3_3key_subkeys, + MBEDTLS_CIPHER_DES_EDE3_ECB, + MBEDTLS_DES3_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } + + if ((ret = cmac_test_wth_cipher(verbose, + "3DES 3 key", + des3_3key_key, + 192, + test_message, + des3_message_lengths, + (const unsigned char *) des3_3key_expected_result, + MBEDTLS_CIPHER_DES_EDE3_ECB, + MBEDTLS_DES3_BLOCK_SIZE, + NB_CMAC_TESTS_PER_KEY)) != 0) { + return ret; + } +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_AES_C) + if ((ret = test_aes128_cmac_prf(verbose)) != 0) { + return ret; + } +#endif /* MBEDTLS_AES_C */ + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_CMAC_C */ diff --git a/r5dev/thirdparty/mbedtls/common.h b/r5dev/thirdparty/mbedtls/common.h new file mode 100644 index 00000000..eb159a7c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/common.h @@ -0,0 +1,167 @@ +/** + * \file common.h + * + * \brief Utility macros for internal use in the library + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_LIBRARY_COMMON_H +#define MBEDTLS_LIBRARY_COMMON_H + +#include "mbedtls/build_info.h" +#include "alignment.h" + +#include +#include +#include +#include + +/** Helper to define a function as static except when building invasive tests. + * + * If a function is only used inside its own source file and should be + * declared `static` to allow the compiler to optimize for code size, + * but that function has unit tests, define it with + * ``` + * MBEDTLS_STATIC_TESTABLE int mbedtls_foo(...) { ... } + * ``` + * and declare it in a header in the `library/` directory with + * ``` + * #if defined(MBEDTLS_TEST_HOOKS) + * int mbedtls_foo(...); + * #endif + * ``` + */ +#if defined(MBEDTLS_TEST_HOOKS) +#define MBEDTLS_STATIC_TESTABLE +#else +#define MBEDTLS_STATIC_TESTABLE static +#endif + +#if defined(MBEDTLS_TEST_HOOKS) +extern void (*mbedtls_test_hook_test_fail)(const char *test, int line, const char *file); +#define MBEDTLS_TEST_HOOK_TEST_ASSERT(TEST) \ + do { \ + if ((!(TEST)) && ((*mbedtls_test_hook_test_fail) != NULL)) \ + { \ + (*mbedtls_test_hook_test_fail)( #TEST, __LINE__, __FILE__); \ + } \ + } while (0) +#else +#define MBEDTLS_TEST_HOOK_TEST_ASSERT(TEST) +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + +/** Allow library to access its structs' private members. + * + * Although structs defined in header files are publicly available, + * their members are private and should not be accessed by the user. + */ +#define MBEDTLS_ALLOW_PRIVATE_ACCESS + +/** Return an offset into a buffer. + * + * This is just the addition of an offset to a pointer, except that this + * function also accepts an offset of 0 into a buffer whose pointer is null. + * (`p + n` has undefined behavior when `p` is null, even when `n == 0`. + * A null pointer is a valid buffer pointer when the size is 0, for example + * as the result of `malloc(0)` on some platforms.) + * + * \param p Pointer to a buffer of at least n bytes. + * This may be \p NULL if \p n is zero. + * \param n An offset in bytes. + * \return Pointer to offset \p n in the buffer \p p. + * Note that this is only a valid pointer if the size of the + * buffer is at least \p n + 1. + */ +static inline unsigned char *mbedtls_buffer_offset( + unsigned char *p, size_t n) +{ + return p == NULL ? NULL : p + n; +} + +/** Return an offset into a read-only buffer. + * + * Similar to mbedtls_buffer_offset(), but for const pointers. + * + * \param p Pointer to a buffer of at least n bytes. + * This may be \p NULL if \p n is zero. + * \param n An offset in bytes. + * \return Pointer to offset \p n in the buffer \p p. + * Note that this is only a valid pointer if the size of the + * buffer is at least \p n + 1. + */ +static inline const unsigned char *mbedtls_buffer_offset_const( + const unsigned char *p, size_t n) +{ + return p == NULL ? NULL : p + n; +} + +/** + * Perform a fast block XOR operation, such that + * r[i] = a[i] ^ b[i] where 0 <= i < n + * + * \param r Pointer to result (buffer of at least \p n bytes). \p r + * may be equal to either \p a or \p b, but behaviour when + * it overlaps in other ways is undefined. + * \param a Pointer to input (buffer of at least \p n bytes) + * \param b Pointer to input (buffer of at least \p n bytes) + * \param n Number of bytes to process. + */ +inline void mbedtls_xor(unsigned char *r, const unsigned char *a, const unsigned char *b, size_t n) +{ + size_t i = 0; +#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) + for (; (i + 4) <= n; i += 4) { + uint32_t x = mbedtls_get_unaligned_uint32(a + i) ^ mbedtls_get_unaligned_uint32(b + i); + mbedtls_put_unaligned_uint32(r + i, x); + } +#endif + for (; i < n; i++) { + r[i] = a[i] ^ b[i]; + } +} + +/* Fix MSVC C99 compatible issue + * MSVC support __func__ from visual studio 2015( 1900 ) + * Use MSVC predefine macro to avoid name check fail. + */ +#if (defined(_MSC_VER) && (_MSC_VER <= 1900)) +#define /*no-check-names*/ __func__ __FUNCTION__ +#endif + +/* Define `asm` for compilers which don't define it. */ +/* *INDENT-OFF* */ +#ifndef asm +#define asm __asm__ +#endif +/* *INDENT-ON* */ + +/* Always provide a static assert macro, so it can be used unconditionally. + * It will expand to nothing on some systems. + * Can be used outside functions (but don't add a trailing ';' in that case: + * the semicolon is included here to avoid triggering -Wextra-semi when + * MBEDTLS_STATIC_ASSERT() expands to nothing). + * Can't use the C11-style `defined(static_assert)` on FreeBSD, since it + * defines static_assert even with -std=c99, but then complains about it. + */ +#if defined(static_assert) && !defined(__FreeBSD__) +#define MBEDTLS_STATIC_ASSERT(expr, msg) static_assert(expr, msg); +#else +#define MBEDTLS_STATIC_ASSERT(expr, msg) +#endif + +#endif /* MBEDTLS_LIBRARY_COMMON_H */ diff --git a/r5dev/thirdparty/mbedtls/configs/README.txt b/r5dev/thirdparty/mbedtls/configs/README.txt new file mode 100644 index 00000000..9e5a243f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/configs/README.txt @@ -0,0 +1,24 @@ +This directory contains example configuration files. + +The examples are generally focused on a particular usage case (eg, support for +a restricted number of ciphersuites) and aim at minimizing resource usage for +this target. They can be used as a basis for custom configurations. + +These files are complete replacements for the default mbedtls_config.h. To use one of +them, you can pick one of the following methods: + +1. Replace the default file include/mbedtls/mbedtls_config.h with the chosen one. + +2. Define MBEDTLS_CONFIG_FILE and adjust the include path accordingly. + For example, using make: + + CFLAGS="-I$PWD/configs -DMBEDTLS_CONFIG_FILE=''" make + + Or, using cmake: + + find . -iname '*cmake*' -not -name CMakeLists.txt -exec rm -rf {} + + CFLAGS="-I$PWD/configs -DMBEDTLS_CONFIG_FILE=''" cmake . + make + +Note that the second method also works if you want to keep your custom +configuration file outside the mbed TLS tree. diff --git a/r5dev/thirdparty/mbedtls/configs/config-ccm-psk-dtls1_2.h b/r5dev/thirdparty/mbedtls/configs/config-ccm-psk-dtls1_2.h new file mode 100644 index 00000000..af2415fe --- /dev/null +++ b/r5dev/thirdparty/mbedtls/configs/config-ccm-psk-dtls1_2.h @@ -0,0 +1,104 @@ +/** + * \file config-ccm-psk-dtls1_2.h + * + * \brief Small configuration for DTLS 1.2 with PSK and AES-CCM ciphersuites + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Minimal configuration for DTLS 1.2 with PSK and AES-CCM ciphersuites + * + * Distinguishing features: + * - Optimized for small code size, low bandwidth (on an unreliable transport), + * and low RAM usage. + * - No asymmetric cryptography (no certificates, no Diffie-Hellman key + * exchange). + * - Fully modern and secure (provided the pre-shared keys are generated and + * stored securely). + * - Very low record overhead with CCM-8. + * - Includes several optional DTLS features typically used in IoT. + * + * See README.txt for usage instructions. + */ + +/* System support */ +//#define MBEDTLS_HAVE_TIME /* Optionally used in Hello messages */ +/* Other MBEDTLS_HAVE_XXX flags irrelevant for this configuration */ + +/* Mbed TLS modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_CCM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_MD_C +#define MBEDTLS_NET_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_COOKIE_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_TIMING_C + +/* TLS protocol feature support */ +#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_SSL_PROTO_DTLS +#define MBEDTLS_SSL_DTLS_ANTI_REPLAY +#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE +#define MBEDTLS_SSL_DTLS_CONNECTION_ID +#define MBEDTLS_SSL_DTLS_HELLO_VERIFY +#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + +/* + * Use only CCM_8 ciphersuites, and + * save ROM and a few bytes of RAM by specifying our own ciphersuite list + */ +#define MBEDTLS_SSL_CIPHERSUITES \ + MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, \ + MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8 + +/* + * Save RAM at the expense of interoperability: do this only if you control + * both ends of the connection! (See comments in "mbedtls/ssl.h".) + * The optimal size here depends on the typical size of records. + */ +#define MBEDTLS_SSL_IN_CONTENT_LEN 256 +#define MBEDTLS_SSL_OUT_CONTENT_LEN 256 + +/* Save RAM at the expense of ROM */ +#define MBEDTLS_AES_ROM_TABLES + +/* Save some RAM by adjusting to your exact needs */ +#define MBEDTLS_PSK_MAX_LEN 16 /* 128-bits keys are generally enough */ + +/* + * You should adjust this to the exact number of sources you're using: default + * is the "platform_entropy_poll" source, but you may want to add other ones + * Minimum is 2 for the entropy test suite. + */ +#define MBEDTLS_ENTROPY_MAX_SOURCES 2 + +/* These defines are present so that the config modifying scripts can enable + * them during tests/scripts/test-ref-configs.pl */ +//#define MBEDTLS_USE_PSA_CRYPTO +//#define MBEDTLS_PSA_CRYPTO_C + +/* Error messages and TLS debugging traces + * (huge code size increase, needed for tests/ssl-opt.sh) */ +//#define MBEDTLS_DEBUG_C +//#define MBEDTLS_ERROR_C diff --git a/r5dev/thirdparty/mbedtls/configs/config-ccm-psk-tls1_2.h b/r5dev/thirdparty/mbedtls/configs/config-ccm-psk-tls1_2.h new file mode 100644 index 00000000..62c1d801 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/configs/config-ccm-psk-tls1_2.h @@ -0,0 +1,95 @@ +/** + * \file config-ccm-psk-tls1_2.h + * + * \brief Minimal configuration for TLS 1.2 with PSK and AES-CCM ciphersuites + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Minimal configuration for TLS 1.2 with PSK and AES-CCM ciphersuites + * + * Distinguishing features: + * - Optimized for small code size, low bandwidth (on a reliable transport), + * and low RAM usage. + * - No asymmetric cryptography (no certificates, no Diffie-Hellman key + * exchange). + * - Fully modern and secure (provided the pre-shared keys are generated and + * stored securely). + * - Very low record overhead with CCM-8. + * + * See README.txt for usage instructions. + */ + +/* System support */ +//#define MBEDTLS_HAVE_TIME /* Optionally used in Hello messages */ +/* Other MBEDTLS_HAVE_XXX flags irrelevant for this configuration */ + +/* Mbed TLS modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_CCM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_MD_C +#define MBEDTLS_NET_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C + +/* TLS protocol feature support */ +#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +#define MBEDTLS_SSL_PROTO_TLS1_2 + +/* + * Use only CCM_8 ciphersuites, and + * save ROM and a few bytes of RAM by specifying our own ciphersuite list + */ +#define MBEDTLS_SSL_CIPHERSUITES \ + MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, \ + MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8 + +/* + * Save RAM at the expense of interoperability: do this only if you control + * both ends of the connection! (See comments in "mbedtls/ssl.h".) + * The optimal size here depends on the typical size of records. + */ +#define MBEDTLS_SSL_IN_CONTENT_LEN 1024 +#define MBEDTLS_SSL_OUT_CONTENT_LEN 1024 + +/* Save RAM at the expense of ROM */ +#define MBEDTLS_AES_ROM_TABLES + +/* Save some RAM by adjusting to your exact needs */ +#define MBEDTLS_PSK_MAX_LEN 16 /* 128-bits keys are generally enough */ + +/* + * You should adjust this to the exact number of sources you're using: default + * is the "platform_entropy_poll" source, but you may want to add other ones + * Minimum is 2 for the entropy test suite. + */ +#define MBEDTLS_ENTROPY_MAX_SOURCES 2 + +/* These defines are present so that the config modifying scripts can enable + * them during tests/scripts/test-ref-configs.pl */ +//#define MBEDTLS_USE_PSA_CRYPTO +//#define MBEDTLS_PSA_CRYPTO_C + +/* Error messages and TLS debugging traces + * (huge code size increase, needed for tests/ssl-opt.sh) */ +//#define MBEDTLS_DEBUG_C +//#define MBEDTLS_ERROR_C diff --git a/r5dev/thirdparty/mbedtls/configs/config-no-entropy.h b/r5dev/thirdparty/mbedtls/configs/config-no-entropy.h new file mode 100644 index 00000000..31fab4e9 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/configs/config-no-entropy.h @@ -0,0 +1,85 @@ +/** + * \file config-no-entropy.h + * + * \brief Minimal configuration of features that do not require an entropy source + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Minimal configuration of features that do not require an entropy source + * Distinguishing features: + * - no entropy module + * - no TLS protocol implementation available due to absence of an entropy + * source + * + * See README.txt for usage instructions. + */ + +/* System support */ +#define MBEDTLS_HAVE_ASM +#define MBEDTLS_HAVE_TIME + +/* mbed TLS feature support */ +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_CIPHER_PADDING_PKCS7 +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_ECP_NIST_OPTIM +#define MBEDTLS_ECDSA_DETERMINISTIC +#define MBEDTLS_PK_RSA_ALT_SUPPORT +#define MBEDTLS_PKCS1_V15 +#define MBEDTLS_PKCS1_V21 +#define MBEDTLS_SELF_TEST +#define MBEDTLS_VERSION_FEATURES + +/* mbed TLS modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BASE64_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CCM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ERROR_C +#define MBEDTLS_GCM_C +#define MBEDTLS_HMAC_DRBG_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_PEM_PARSE_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PK_WRITE_C +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_RSA_C +/* The library does not currently support enabling SHA-224 without SHA-256. + * A future version of the library will have this option disabled + * by default. */ +#define MBEDTLS_SHA224_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA384_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_VERSION_C +#define MBEDTLS_X509_USE_C +#define MBEDTLS_X509_CRT_PARSE_C +#define MBEDTLS_X509_CRL_PARSE_C +//#define MBEDTLS_CMAC_C + +/* Miscellaneous options */ +#define MBEDTLS_AES_ROM_TABLES diff --git a/r5dev/thirdparty/mbedtls/configs/config-suite-b.h b/r5dev/thirdparty/mbedtls/configs/config-suite-b.h new file mode 100644 index 00000000..89898b33 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/configs/config-suite-b.h @@ -0,0 +1,118 @@ +/** + * \file config-suite-b.h + * + * \brief Minimal configuration for TLS NSA Suite B Profile (RFC 6460) + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Minimal configuration for TLS NSA Suite B Profile (RFC 6460) + * + * Distinguishing features: + * - no RSA or classic DH, fully based on ECC + * - optimized for low RAM usage + * + * Possible improvements: + * - if 128-bit security is enough, disable secp384r1 and SHA-512 + * - use embedded certs in DER format and disable PEM_PARSE_C and BASE64_C + * + * See README.txt for usage instructions. + */ + +/* System support */ +#define MBEDTLS_HAVE_ASM +#define MBEDTLS_HAVE_TIME + +/* mbed TLS feature support */ +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#define MBEDTLS_SSL_PROTO_TLS1_2 + +/* mbed TLS modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +#define MBEDTLS_ECDH_C +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_GCM_C +#define MBEDTLS_MD_C +#define MBEDTLS_NET_C +#define MBEDTLS_OID_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA384_C +#define MBEDTLS_SHA512_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_X509_CRT_PARSE_C +#define MBEDTLS_X509_USE_C + +/* For test certificates */ +#define MBEDTLS_BASE64_C +#define MBEDTLS_PEM_PARSE_C + +/* Save RAM at the expense of ROM */ +#define MBEDTLS_AES_ROM_TABLES + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_MPI_MAX_SIZE 48 // 384-bit EC curve = 48 bytes + +/* Save RAM at the expense of speed, see ecp.h */ +#define MBEDTLS_ECP_WINDOW_SIZE 2 +#define MBEDTLS_ECP_FIXED_POINT_OPTIM 0 + +/* Significant speed benefit at the expense of some ROM */ +#define MBEDTLS_ECP_NIST_OPTIM + +/* + * You should adjust this to the exact number of sources you're using: default + * is the "mbedtls_platform_entropy_poll" source, but you may want to add other ones. + * Minimum is 2 for the entropy test suite. + */ +#define MBEDTLS_ENTROPY_MAX_SOURCES 2 + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES \ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, \ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + +/* + * Save RAM at the expense of interoperability: do this only if you control + * both ends of the connection! (See comments in "mbedtls/ssl.h".) + * The minimum size here depends on the certificate chain used as well as the + * typical size of records. + */ +#define MBEDTLS_SSL_IN_CONTENT_LEN 1024 +#define MBEDTLS_SSL_OUT_CONTENT_LEN 1024 + +/* These defines are present so that the config modifying scripts can enable + * them during tests/scripts/test-ref-configs.pl */ +//#define MBEDTLS_USE_PSA_CRYPTO +//#define MBEDTLS_PSA_CRYPTO_C + +/* Error messages and TLS debugging traces + * (huge code size increase, needed for tests/ssl-opt.sh) */ +//#define MBEDTLS_DEBUG_C +//#define MBEDTLS_ERROR_C diff --git a/r5dev/thirdparty/mbedtls/configs/config-symmetric-only.h b/r5dev/thirdparty/mbedtls/configs/config-symmetric-only.h new file mode 100644 index 00000000..6aff42f1 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/configs/config-symmetric-only.h @@ -0,0 +1,89 @@ +/** + * \file config-symmetric-only.h + * + * \brief Configuration without any asymmetric cryptography. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* System support */ +//#define MBEDTLS_HAVE_ASM +#define MBEDTLS_HAVE_TIME +#define MBEDTLS_HAVE_TIME_DATE + +/* Mbed Crypto feature support */ +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_CIPHER_MODE_CFB +#define MBEDTLS_CIPHER_MODE_CTR +#define MBEDTLS_CIPHER_MODE_OFB +#define MBEDTLS_CIPHER_MODE_XTS +#define MBEDTLS_CIPHER_PADDING_PKCS7 +#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS +#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN +#define MBEDTLS_CIPHER_PADDING_ZEROS +#define MBEDTLS_ERROR_STRERROR_DUMMY +#define MBEDTLS_FS_IO +#define MBEDTLS_ENTROPY_NV_SEED +#define MBEDTLS_SELF_TEST +#define MBEDTLS_USE_PSA_CRYPTO +#define MBEDTLS_VERSION_FEATURES + +/* Mbed Crypto modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BASE64_C +#define MBEDTLS_CAMELLIA_C +#define MBEDTLS_ARIA_C +#define MBEDTLS_CCM_C +#define MBEDTLS_CHACHA20_C +#define MBEDTLS_CHACHAPOLY_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CMAC_C +#define MBEDTLS_CTR_DRBG_C +#define MBEDTLS_DES_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_ERROR_C +#define MBEDTLS_GCM_C +#define MBEDTLS_HKDF_C +#define MBEDTLS_HMAC_DRBG_C +#define MBEDTLS_NIST_KW_C +#define MBEDTLS_MD_C +#define MBEDTLS_MD5_C +#define MBEDTLS_OID_C +#define MBEDTLS_PEM_PARSE_C +#define MBEDTLS_PEM_WRITE_C +#define MBEDTLS_PKCS5_C +#define MBEDTLS_PKCS12_C +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_POLY1305_C +#define MBEDTLS_PSA_CRYPTO_C +#define MBEDTLS_PSA_CRYPTO_SE_C +#define MBEDTLS_PSA_CRYPTO_STORAGE_C +#define MBEDTLS_PSA_ITS_FILE_C +#define MBEDTLS_RIPEMD160_C +#define MBEDTLS_SHA1_C +/* The library does not currently support enabling SHA-224 without SHA-256. + * A future version of the library will have this option disabled + * by default. */ +#define MBEDTLS_SHA224_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SHA384_C +#define MBEDTLS_SHA512_C +//#define MBEDTLS_THREADING_C +#define MBEDTLS_TIMING_C +#define MBEDTLS_VERSION_C diff --git a/r5dev/thirdparty/mbedtls/configs/config-thread.h b/r5dev/thirdparty/mbedtls/configs/config-thread.h new file mode 100644 index 00000000..0652136a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/configs/config-thread.h @@ -0,0 +1,88 @@ +/** + * \file config-thread.h + * + * \brief Minimal configuration for using TLS as part of Thread + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Minimal configuration for using TLS a part of Thread + * http://threadgroup.org/ + * + * Distinguishing features: + * - no RSA or classic DH, fully based on ECC + * - no X.509 + * - support for experimental EC J-PAKE key exchange + * + * See README.txt for usage instructions. + */ + +/* System support */ +#define MBEDTLS_HAVE_ASM + +/* mbed TLS feature support */ +#define MBEDTLS_AES_ROM_TABLES +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_NIST_OPTIM +#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED +#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_SSL_PROTO_DTLS +#define MBEDTLS_SSL_DTLS_ANTI_REPLAY +#define MBEDTLS_SSL_DTLS_HELLO_VERIFY + +/* mbed TLS modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CCM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +#define MBEDTLS_CMAC_C +#define MBEDTLS_ECJPAKE_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_HMAC_DRBG_C +#define MBEDTLS_MD_C +#define MBEDTLS_OID_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SSL_COOKIE_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C + +/* For tests using ssl-opt.sh */ +#define MBEDTLS_NET_C +#define MBEDTLS_TIMING_C + +/* Save RAM at the expense of ROM */ +#define MBEDTLS_AES_ROM_TABLES + +/* Save RAM by adjusting to our exact needs */ +#define MBEDTLS_MPI_MAX_SIZE 32 // 256-bit EC curve = 32 bytes + +/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */ +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + +/* These defines are present so that the config modifying scripts can enable + * them during tests/scripts/test-ref-configs.pl */ +//#define MBEDTLS_USE_PSA_CRYPTO +//#define MBEDTLS_PSA_CRYPTO_C diff --git a/r5dev/thirdparty/mbedtls/constant_time.c b/r5dev/thirdparty/mbedtls/constant_time.c new file mode 100644 index 00000000..552a918f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/constant_time.c @@ -0,0 +1,1042 @@ +/** + * Constant-time functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The following functions are implemented without using comparison operators, as those + * might be translated to branches by some compilers on some platforms. + */ + +#include "common.h" +#include "constant_time_internal.h" +#include "mbedtls/constant_time.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#include "bignum_core.h" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) +#include "ssl_misc.h" +#endif + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif + +#if defined(MBEDTLS_BASE64_C) +#include "constant_time_invasive.h" +#endif + +#include +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) +#endif + +/* + * Define MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS where assembly is present to + * perform fast unaligned access to volatile data. + * + * This is needed because mbedtls_get_unaligned_uintXX etc don't support volatile + * memory accesses. + * + * Some of these definitions could be moved into alignment.h but for now they are + * only used here. + */ +#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) && defined(MBEDTLS_HAVE_ASM) +#if defined(__arm__) || defined(__thumb__) || defined(__thumb2__) || defined(__aarch64__) +#define MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS +#endif +#endif + +#if defined(MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS) +static inline uint32_t mbedtls_get_unaligned_volatile_uint32(volatile const unsigned char *p) +{ + /* This is UB, even where it's safe: + * return *((volatile uint32_t*)p); + * so instead the same thing is expressed in assembly below. + */ + uint32_t r; +#if defined(__arm__) || defined(__thumb__) || defined(__thumb2__) + asm volatile ("ldr %0, [%1]" : "=r" (r) : "r" (p) :); +#elif defined(__aarch64__) + asm volatile ("ldr %w0, [%1]" : "=r" (r) : "r" (p) :); +#endif + return r; +} +#endif /* MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS */ + +int mbedtls_ct_memcmp(const void *a, + const void *b, + size_t n) +{ + size_t i = 0; + /* + * `A` and `B` are cast to volatile to ensure that the compiler + * generates code that always fully reads both buffers. + * Otherwise it could generate a test to exit early if `diff` has all + * bits set early in the loop. + */ + volatile const unsigned char *A = (volatile const unsigned char *) a; + volatile const unsigned char *B = (volatile const unsigned char *) b; + uint32_t diff = 0; + +#if defined(MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS) + for (; (i + 4) <= n; i += 4) { + uint32_t x = mbedtls_get_unaligned_volatile_uint32(A + i); + uint32_t y = mbedtls_get_unaligned_volatile_uint32(B + i); + diff |= x ^ y; + } +#endif + + for (; i < n; i++) { + /* Read volatile data in order before computing diff. + * This avoids IAR compiler warning: + * 'the order of volatile accesses is undefined ..' */ + unsigned char x = A[i], y = B[i]; + diff |= x ^ y; + } + + return (int) diff; +} + +unsigned mbedtls_ct_uint_mask(unsigned value) +{ + /* MSVC has a warning about unary minus on unsigned, but this is + * well-defined and precisely what we want to do here */ +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + return -((value | -value) >> (sizeof(value) * 8 - 1)); +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif +} + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + +size_t mbedtls_ct_size_mask(size_t value) +{ + /* MSVC has a warning about unary minus on unsigned integer types, + * but this is well-defined and precisely what we want to do here. */ +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + return -((value | -value) >> (sizeof(value) * 8 - 1)); +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif +} + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + +#if defined(MBEDTLS_BIGNUM_C) + +mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask(mbedtls_mpi_uint value) +{ + /* MSVC has a warning about unary minus on unsigned, but this is + * well-defined and precisely what we want to do here */ +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + return -((value | -value) >> (sizeof(value) * 8 - 1)); +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif +} + +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) + +/** Constant-flow mask generation for "less than" comparison: + * - if \p x < \p y, return all-bits 1, that is (size_t) -1 + * - otherwise, return all bits 0, that is 0 + * + * This function can be used to write constant-time code by replacing branches + * with bit operations using masks. + * + * \param x The first value to analyze. + * \param y The second value to analyze. + * + * \return All-bits-one if \p x is less than \p y, otherwise zero. + */ +static size_t mbedtls_ct_size_mask_lt(size_t x, + size_t y) +{ + /* This has the most significant bit set if and only if x < y */ + const size_t sub = x - y; + + /* sub1 = (x < y) ? 1 : 0 */ + const size_t sub1 = sub >> (sizeof(sub) * 8 - 1); + + /* mask = (x < y) ? 0xff... : 0x00... */ + const size_t mask = mbedtls_ct_size_mask(sub1); + + return mask; +} + +size_t mbedtls_ct_size_mask_ge(size_t x, + size_t y) +{ + return ~mbedtls_ct_size_mask_lt(x, y); +} + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ + +#if defined(MBEDTLS_BASE64_C) + +/* Return 0xff if low <= c <= high, 0 otherwise. + * + * Constant flow with respect to c. + */ +MBEDTLS_STATIC_TESTABLE +unsigned char mbedtls_ct_uchar_mask_of_range(unsigned char low, + unsigned char high, + unsigned char c) +{ + /* low_mask is: 0 if low <= c, 0x...ff if low > c */ + unsigned low_mask = ((unsigned) c - low) >> 8; + /* high_mask is: 0 if c <= high, 0x...ff if c > high */ + unsigned high_mask = ((unsigned) high - c) >> 8; + return ~(low_mask | high_mask) & 0xff; +} + +#endif /* MBEDTLS_BASE64_C */ + +unsigned mbedtls_ct_size_bool_eq(size_t x, + size_t y) +{ + /* diff = 0 if x == y, non-zero otherwise */ + const size_t diff = x ^ y; + + /* MSVC has a warning about unary minus on unsigned integer types, + * but this is well-defined and precisely what we want to do here. */ +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + + /* diff_msb's most significant bit is equal to x != y */ + const size_t diff_msb = (diff | (size_t) -diff); + +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif + + /* diff1 = (x != y) ? 1 : 0 */ + const unsigned diff1 = diff_msb >> (sizeof(diff_msb) * 8 - 1); + + return 1 ^ diff1; +} + +#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) + +/** Constant-flow "greater than" comparison: + * return x > y + * + * This is equivalent to \p x > \p y, but is likely to be compiled + * to code using bitwise operation rather than a branch. + * + * \param x The first value to analyze. + * \param y The second value to analyze. + * + * \return 1 if \p x greater than \p y, otherwise 0. + */ +static unsigned mbedtls_ct_size_gt(size_t x, + size_t y) +{ + /* Return the sign bit (1 for negative) of (y - x). */ + return (y - x) >> (sizeof(size_t) * 8 - 1); +} + +#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ + +#if defined(MBEDTLS_BIGNUM_C) + +unsigned mbedtls_ct_mpi_uint_lt(const mbedtls_mpi_uint x, + const mbedtls_mpi_uint y) +{ + mbedtls_mpi_uint ret; + mbedtls_mpi_uint cond; + + /* + * Check if the most significant bits (MSB) of the operands are different. + */ + cond = (x ^ y); + /* + * If the MSB are the same then the difference x-y will be negative (and + * have its MSB set to 1 during conversion to unsigned) if and only if x> (sizeof(mbedtls_mpi_uint) * 8 - 1); + + return (unsigned) ret; +} + +#endif /* MBEDTLS_BIGNUM_C */ + +unsigned mbedtls_ct_uint_if(unsigned condition, + unsigned if1, + unsigned if0) +{ + unsigned mask = mbedtls_ct_uint_mask(condition); + return (mask & if1) | (~mask & if0); +} + +#if defined(MBEDTLS_BIGNUM_C) + +/** Select between two sign values without branches. + * + * This is functionally equivalent to `condition ? if1 : if0` but uses only bit + * operations in order to avoid branches. + * + * \note if1 and if0 must be either 1 or -1, otherwise the result + * is undefined. + * + * \param condition Condition to test; must be either 0 or 1. + * \param if1 The first sign; must be either +1 or -1. + * \param if0 The second sign; must be either +1 or -1. + * + * \return \c if1 if \p condition is nonzero, otherwise \c if0. + * */ +static int mbedtls_ct_cond_select_sign(unsigned char condition, + int if1, + int if0) +{ + /* In order to avoid questions about what we can reasonably assume about + * the representations of signed integers, move everything to unsigned + * by taking advantage of the fact that if1 and if0 are either +1 or -1. */ + unsigned uif1 = if1 + 1; + unsigned uif0 = if0 + 1; + + /* condition was 0 or 1, mask is 0 or 2 as are uif1 and uif0 */ + const unsigned mask = condition << 1; + + /* select uif1 or uif0 */ + unsigned ur = (uif0 & ~mask) | (uif1 & mask); + + /* ur is now 0 or 2, convert back to -1 or +1 */ + return (int) ur - 1; +} + +void mbedtls_ct_mpi_uint_cond_assign(size_t n, + mbedtls_mpi_uint *dest, + const mbedtls_mpi_uint *src, + unsigned char condition) +{ + size_t i; + + /* MSVC has a warning about unary minus on unsigned integer types, + * but this is well-defined and precisely what we want to do here. */ +#if defined(_MSC_VER) +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + + /* all-bits 1 if condition is 1, all-bits 0 if condition is 0 */ + const mbedtls_mpi_uint mask = -condition; + +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif + + for (i = 0; i < n; i++) { + dest[i] = (src[i] & mask) | (dest[i] & ~mask); + } +} + +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_BASE64_C) + +unsigned char mbedtls_ct_base64_enc_char(unsigned char value) +{ + unsigned char digit = 0; + /* For each range of values, if value is in that range, mask digit with + * the corresponding value. Since value can only be in a single range, + * only at most one masking will change digit. */ + digit |= mbedtls_ct_uchar_mask_of_range(0, 25, value) & ('A' + value); + digit |= mbedtls_ct_uchar_mask_of_range(26, 51, value) & ('a' + value - 26); + digit |= mbedtls_ct_uchar_mask_of_range(52, 61, value) & ('0' + value - 52); + digit |= mbedtls_ct_uchar_mask_of_range(62, 62, value) & '+'; + digit |= mbedtls_ct_uchar_mask_of_range(63, 63, value) & '/'; + return digit; +} + +signed char mbedtls_ct_base64_dec_value(unsigned char c) +{ + unsigned char val = 0; + /* For each range of digits, if c is in that range, mask val with + * the corresponding value. Since c can only be in a single range, + * only at most one masking will change val. Set val to one plus + * the desired value so that it stays 0 if c is in none of the ranges. */ + val |= mbedtls_ct_uchar_mask_of_range('A', 'Z', c) & (c - 'A' + 0 + 1); + val |= mbedtls_ct_uchar_mask_of_range('a', 'z', c) & (c - 'a' + 26 + 1); + val |= mbedtls_ct_uchar_mask_of_range('0', '9', c) & (c - '0' + 52 + 1); + val |= mbedtls_ct_uchar_mask_of_range('+', '+', c) & (c - '+' + 62 + 1); + val |= mbedtls_ct_uchar_mask_of_range('/', '/', c) & (c - '/' + 63 + 1); + /* At this point, val is 0 if c is an invalid digit and v+1 if c is + * a digit with the value v. */ + return val - 1; +} + +#endif /* MBEDTLS_BASE64_C */ + +#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) + +/** Shift some data towards the left inside a buffer. + * + * `mbedtls_ct_mem_move_to_left(start, total, offset)` is functionally + * equivalent to + * ``` + * memmove(start, start + offset, total - offset); + * memset(start + offset, 0, total - offset); + * ``` + * but it strives to use a memory access pattern (and thus total timing) + * that does not depend on \p offset. This timing independence comes at + * the expense of performance. + * + * \param start Pointer to the start of the buffer. + * \param total Total size of the buffer. + * \param offset Offset from which to copy \p total - \p offset bytes. + */ +static void mbedtls_ct_mem_move_to_left(void *start, + size_t total, + size_t offset) +{ + volatile unsigned char *buf = start; + size_t i, n; + if (total == 0) { + return; + } + for (i = 0; i < total; i++) { + unsigned no_op = mbedtls_ct_size_gt(total - offset, i); + /* The first `total - offset` passes are a no-op. The last + * `offset` passes shift the data one byte to the left and + * zero out the last byte. */ + for (n = 0; n < total - 1; n++) { + unsigned char current = buf[n]; + unsigned char next = buf[n+1]; + buf[n] = mbedtls_ct_uint_if(no_op, current, next); + } + buf[total-1] = mbedtls_ct_uint_if(no_op, buf[total-1], 0); + } +} + +#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + +void mbedtls_ct_memcpy_if_eq(unsigned char *dest, + const unsigned char *src, + size_t len, + size_t c1, + size_t c2) +{ + /* mask = c1 == c2 ? 0xff : 0x00 */ + const size_t equal = mbedtls_ct_size_bool_eq(c1, c2); + + /* dest[i] = c1 == c2 ? src[i] : dest[i] */ + size_t i = 0; +#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) + const uint32_t mask32 = (uint32_t) mbedtls_ct_size_mask(equal); + const unsigned char mask = (unsigned char) mask32 & 0xff; + + for (; (i + 4) <= len; i += 4) { + uint32_t a = mbedtls_get_unaligned_uint32(src + i) & mask32; + uint32_t b = mbedtls_get_unaligned_uint32(dest + i) & ~mask32; + mbedtls_put_unaligned_uint32(dest + i, a | b); + } +#else + const unsigned char mask = (unsigned char) mbedtls_ct_size_mask(equal); +#endif /* MBEDTLS_EFFICIENT_UNALIGNED_ACCESS */ + for (; i < len; i++) { + dest[i] = (src[i] & mask) | (dest[i] & ~mask); + } +} + +void mbedtls_ct_memcpy_offset(unsigned char *dest, + const unsigned char *src, + size_t offset, + size_t offset_min, + size_t offset_max, + size_t len) +{ + size_t offsetval; + + for (offsetval = offset_min; offsetval <= offset_max; offsetval++) { + mbedtls_ct_memcpy_if_eq(dest, src + offsetval, len, + offsetval, offset); + } +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + +#if defined(PSA_WANT_ALG_SHA_384) +#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_384) +#elif defined(PSA_WANT_ALG_SHA_256) +#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_256) +#else /* See check_config.h */ +#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_1) +#endif + +int mbedtls_ct_hmac(mbedtls_svc_key_id_t key, + psa_algorithm_t mac_alg, + const unsigned char *add_data, + size_t add_data_len, + const unsigned char *data, + size_t data_len_secret, + size_t min_data_len, + size_t max_data_len, + unsigned char *output) +{ + /* + * This function breaks the HMAC abstraction and uses psa_hash_clone() + * extension in order to get constant-flow behaviour. + * + * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means + * concatenation, and okey/ikey are the XOR of the key with some fixed bit + * patterns (see RFC 2104, sec. 2). + * + * We'll first compute ikey/okey, then inner_hash = HASH(ikey + msg) by + * hashing up to minlen, then cloning the context, and for each byte up + * to maxlen finishing up the hash computation, keeping only the + * correct result. + * + * Then we only need to compute HASH(okey + inner_hash) and we're done. + */ + psa_algorithm_t hash_alg = PSA_ALG_HMAC_GET_HASH(mac_alg); + const size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg); + unsigned char key_buf[MAX_HASH_BLOCK_LENGTH]; + const size_t hash_size = PSA_HASH_LENGTH(hash_alg); + psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + size_t hash_length; + + unsigned char aux_out[PSA_HASH_MAX_SIZE]; + psa_hash_operation_t aux_operation = PSA_HASH_OPERATION_INIT; + size_t offset; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + size_t mac_key_length; + size_t i; + +#define PSA_CHK(func_call) \ + do { \ + status = (func_call); \ + if (status != PSA_SUCCESS) \ + goto cleanup; \ + } while (0) + + /* Export MAC key + * We assume key length is always exactly the output size + * which is never more than the block size, thus we use block_size + * as the key buffer size. + */ + PSA_CHK(psa_export_key(key, key_buf, block_size, &mac_key_length)); + + /* Calculate ikey */ + for (i = 0; i < mac_key_length; i++) { + key_buf[i] = (unsigned char) (key_buf[i] ^ 0x36); + } + for (; i < block_size; ++i) { + key_buf[i] = 0x36; + } + + PSA_CHK(psa_hash_setup(&operation, hash_alg)); + + /* Now compute inner_hash = HASH(ikey + msg) */ + PSA_CHK(psa_hash_update(&operation, key_buf, block_size)); + PSA_CHK(psa_hash_update(&operation, add_data, add_data_len)); + PSA_CHK(psa_hash_update(&operation, data, min_data_len)); + + /* Fill the hash buffer in advance with something that is + * not a valid hash (barring an attack on the hash and + * deliberately-crafted input), in case the caller doesn't + * check the return status properly. */ + memset(output, '!', hash_size); + + /* For each possible length, compute the hash up to that point */ + for (offset = min_data_len; offset <= max_data_len; offset++) { + PSA_CHK(psa_hash_clone(&operation, &aux_operation)); + PSA_CHK(psa_hash_finish(&aux_operation, aux_out, + PSA_HASH_MAX_SIZE, &hash_length)); + /* Keep only the correct inner_hash in the output buffer */ + mbedtls_ct_memcpy_if_eq(output, aux_out, hash_size, + offset, data_len_secret); + + if (offset < max_data_len) { + PSA_CHK(psa_hash_update(&operation, data + offset, 1)); + } + } + + /* Abort current operation to prepare for final operation */ + PSA_CHK(psa_hash_abort(&operation)); + + /* Calculate okey */ + for (i = 0; i < mac_key_length; i++) { + key_buf[i] = (unsigned char) ((key_buf[i] ^ 0x36) ^ 0x5C); + } + for (; i < block_size; ++i) { + key_buf[i] = 0x5C; + } + + /* Now compute HASH(okey + inner_hash) */ + PSA_CHK(psa_hash_setup(&operation, hash_alg)); + PSA_CHK(psa_hash_update(&operation, key_buf, block_size)); + PSA_CHK(psa_hash_update(&operation, output, hash_size)); + PSA_CHK(psa_hash_finish(&operation, output, hash_size, &hash_length)); + +#undef PSA_CHK + +cleanup: + mbedtls_platform_zeroize(key_buf, MAX_HASH_BLOCK_LENGTH); + mbedtls_platform_zeroize(aux_out, PSA_HASH_MAX_SIZE); + + psa_hash_abort(&operation); + psa_hash_abort(&aux_operation); + return PSA_TO_MBEDTLS_ERR(status); +} + +#undef MAX_HASH_BLOCK_LENGTH + +#else +int mbedtls_ct_hmac(mbedtls_md_context_t *ctx, + const unsigned char *add_data, + size_t add_data_len, + const unsigned char *data, + size_t data_len_secret, + size_t min_data_len, + size_t max_data_len, + unsigned char *output) +{ + /* + * This function breaks the HMAC abstraction and uses the md_clone() + * extension to the MD API in order to get constant-flow behaviour. + * + * HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means + * concatenation, and okey/ikey are the XOR of the key with some fixed bit + * patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx. + * + * We'll first compute inner_hash = HASH(ikey + msg) by hashing up to + * minlen, then cloning the context, and for each byte up to maxlen + * finishing up the hash computation, keeping only the correct result. + * + * Then we only need to compute HASH(okey + inner_hash) and we're done. + */ + const mbedtls_md_type_t md_alg = mbedtls_md_get_type(ctx->md_info); + /* TLS 1.2 only supports SHA-384, SHA-256, SHA-1, MD-5, + * all of which have the same block size except SHA-384. */ + const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64; + const unsigned char * const ikey = ctx->hmac_ctx; + const unsigned char * const okey = ikey + block_size; + const size_t hash_size = mbedtls_md_get_size(ctx->md_info); + + unsigned char aux_out[MBEDTLS_MD_MAX_SIZE]; + mbedtls_md_context_t aux; + size_t offset; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_md_init(&aux); + +#define MD_CHK(func_call) \ + do { \ + ret = (func_call); \ + if (ret != 0) \ + goto cleanup; \ + } while (0) + + MD_CHK(mbedtls_md_setup(&aux, ctx->md_info, 0)); + + /* After hmac_start() of hmac_reset(), ikey has already been hashed, + * so we can start directly with the message */ + MD_CHK(mbedtls_md_update(ctx, add_data, add_data_len)); + MD_CHK(mbedtls_md_update(ctx, data, min_data_len)); + + /* Fill the hash buffer in advance with something that is + * not a valid hash (barring an attack on the hash and + * deliberately-crafted input), in case the caller doesn't + * check the return status properly. */ + memset(output, '!', hash_size); + + /* For each possible length, compute the hash up to that point */ + for (offset = min_data_len; offset <= max_data_len; offset++) { + MD_CHK(mbedtls_md_clone(&aux, ctx)); + MD_CHK(mbedtls_md_finish(&aux, aux_out)); + /* Keep only the correct inner_hash in the output buffer */ + mbedtls_ct_memcpy_if_eq(output, aux_out, hash_size, + offset, data_len_secret); + + if (offset < max_data_len) { + MD_CHK(mbedtls_md_update(ctx, data + offset, 1)); + } + } + + /* The context needs to finish() before it starts() again */ + MD_CHK(mbedtls_md_finish(ctx, aux_out)); + + /* Now compute HASH(okey + inner_hash) */ + MD_CHK(mbedtls_md_starts(ctx)); + MD_CHK(mbedtls_md_update(ctx, okey, block_size)); + MD_CHK(mbedtls_md_update(ctx, output, hash_size)); + MD_CHK(mbedtls_md_finish(ctx, output)); + + /* Done, get ready for next time */ + MD_CHK(mbedtls_md_hmac_reset(ctx)); + +#undef MD_CHK + +cleanup: + mbedtls_md_free(&aux); + return ret; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + +#if defined(MBEDTLS_BIGNUM_C) + +#define MPI_VALIDATE_RET(cond) \ + MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_MPI_BAD_INPUT_DATA) + +/* + * Conditionally assign X = Y, without leaking information + * about whether the assignment was made or not. + * (Leaking information about the respective sizes of X and Y is ok however.) + */ +#if defined(_MSC_VER) && defined(_M_ARM64) && (_MSC_FULL_VER < 193131103) +/* + * MSVC miscompiles this function if it's inlined prior to Visual Studio 2022 version 17.1. See: + * https://developercommunity.visualstudio.com/t/c-compiler-miscompiles-part-of-mbedtls-library-on/1646989 + */ +__declspec(noinline) +#endif +int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X, + const mbedtls_mpi *Y, + unsigned char assign) +{ + int ret = 0; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(Y != NULL); + + /* all-bits 1 if assign is 1, all-bits 0 if assign is 0 */ + mbedtls_mpi_uint limb_mask = mbedtls_ct_mpi_uint_mask(assign); + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n)); + + X->s = mbedtls_ct_cond_select_sign(assign, Y->s, X->s); + + mbedtls_mpi_core_cond_assign(X->p, Y->p, Y->n, assign); + + for (size_t i = Y->n; i < X->n; i++) { + X->p[i] &= ~limb_mask; + } + +cleanup: + return ret; +} + +/* + * Conditionally swap X and Y, without leaking information + * about whether the swap was made or not. + * Here it is not ok to simply swap the pointers, which would lead to + * different memory access patterns when X and Y are used afterwards. + */ +int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X, + mbedtls_mpi *Y, + unsigned char swap) +{ + int ret = 0; + int s; + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(Y != NULL); + + if (X == Y) { + return 0; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n)); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Y, X->n)); + + s = X->s; + X->s = mbedtls_ct_cond_select_sign(swap, Y->s, X->s); + Y->s = mbedtls_ct_cond_select_sign(swap, s, Y->s); + + mbedtls_mpi_core_cond_swap(X->p, Y->p, X->n, swap); + +cleanup: + return ret; +} + +/* + * Compare unsigned values in constant time + */ +unsigned mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + size_t limbs) +{ + unsigned ret, cond, done; + + /* The value of any of these variables is either 0 or 1 for the rest of + * their scope. */ + ret = cond = done = 0; + + for (size_t i = limbs; i > 0; i--) { + /* + * If B[i - 1] < A[i - 1] then A < B is false and the result must + * remain 0. + * + * Again even if we can make a decision, we just mark the result and + * the fact that we are done and continue looping. + */ + cond = mbedtls_ct_mpi_uint_lt(B[i - 1], A[i - 1]); + done |= cond; + + /* + * If A[i - 1] < B[i - 1] then A < B is true. + * + * Again even if we can make a decision, we just mark the result and + * the fact that we are done and continue looping. + */ + cond = mbedtls_ct_mpi_uint_lt(A[i - 1], B[i - 1]); + ret |= cond & (1 - done); + done |= cond; + } + + /* + * If all the limbs were equal, then the numbers are equal, A < B is false + * and leaving the result 0 is correct. + */ + + return ret; +} + +/* + * Compare signed values in constant time + */ +int mbedtls_mpi_lt_mpi_ct(const mbedtls_mpi *X, + const mbedtls_mpi *Y, + unsigned *ret) +{ + size_t i; + /* The value of any of these variables is either 0 or 1 at all times. */ + unsigned cond, done, X_is_negative, Y_is_negative; + + MPI_VALIDATE_RET(X != NULL); + MPI_VALIDATE_RET(Y != NULL); + MPI_VALIDATE_RET(ret != NULL); + + if (X->n != Y->n) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + /* + * Set sign_N to 1 if N >= 0, 0 if N < 0. + * We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0. + */ + X_is_negative = (X->s & 2) >> 1; + Y_is_negative = (Y->s & 2) >> 1; + + /* + * If the signs are different, then the positive operand is the bigger. + * That is if X is negative (X_is_negative == 1), then X < Y is true and it + * is false if X is positive (X_is_negative == 0). + */ + cond = (X_is_negative ^ Y_is_negative); + *ret = cond & X_is_negative; + + /* + * This is a constant-time function. We might have the result, but we still + * need to go through the loop. Record if we have the result already. + */ + done = cond; + + for (i = X->n; i > 0; i--) { + /* + * If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both + * X and Y are negative. + * + * Again even if we can make a decision, we just mark the result and + * the fact that we are done and continue looping. + */ + cond = mbedtls_ct_mpi_uint_lt(Y->p[i - 1], X->p[i - 1]); + *ret |= cond & (1 - done) & X_is_negative; + done |= cond; + + /* + * If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both + * X and Y are positive. + * + * Again even if we can make a decision, we just mark the result and + * the fact that we are done and continue looping. + */ + cond = mbedtls_ct_mpi_uint_lt(X->p[i - 1], Y->p[i - 1]); + *ret |= cond & (1 - done) & (1 - X_is_negative); + done |= cond; + } + + return 0; +} + +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) + +int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input, + size_t ilen, + unsigned char *output, + size_t output_max_len, + size_t *olen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, plaintext_max_size; + + /* The following variables take sensitive values: their value must + * not leak into the observable behavior of the function other than + * the designated outputs (output, olen, return value). Otherwise + * this would open the execution of the function to + * side-channel-based variants of the Bleichenbacher padding oracle + * attack. Potential side channels include overall timing, memory + * access patterns (especially visible to an adversary who has access + * to a shared memory cache), and branches (especially visible to + * an adversary who has access to a shared code cache or to a shared + * branch predictor). */ + size_t pad_count = 0; + unsigned bad = 0; + unsigned char pad_done = 0; + size_t plaintext_size = 0; + unsigned output_too_large; + + plaintext_max_size = (output_max_len > ilen - 11) ? ilen - 11 + : output_max_len; + + /* Check and get padding length in constant time and constant + * memory trace. The first byte must be 0. */ + bad |= input[0]; + + + /* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00 + * where PS must be at least 8 nonzero bytes. */ + bad |= input[1] ^ MBEDTLS_RSA_CRYPT; + + /* Read the whole buffer. Set pad_done to nonzero if we find + * the 0x00 byte and remember the padding length in pad_count. */ + for (i = 2; i < ilen; i++) { + pad_done |= ((input[i] | (unsigned char) -input[i]) >> 7) ^ 1; + pad_count += ((pad_done | (unsigned char) -pad_done) >> 7) ^ 1; + } + + + /* If pad_done is still zero, there's no data, only unfinished padding. */ + bad |= mbedtls_ct_uint_if(pad_done, 0, 1); + + /* There must be at least 8 bytes of padding. */ + bad |= mbedtls_ct_size_gt(8, pad_count); + + /* If the padding is valid, set plaintext_size to the number of + * remaining bytes after stripping the padding. If the padding + * is invalid, avoid leaking this fact through the size of the + * output: use the maximum message size that fits in the output + * buffer. Do it without branches to avoid leaking the padding + * validity through timing. RSA keys are small enough that all the + * size_t values involved fit in unsigned int. */ + plaintext_size = mbedtls_ct_uint_if( + bad, (unsigned) plaintext_max_size, + (unsigned) (ilen - pad_count - 3)); + + /* Set output_too_large to 0 if the plaintext fits in the output + * buffer and to 1 otherwise. */ + output_too_large = mbedtls_ct_size_gt(plaintext_size, + plaintext_max_size); + + /* Set ret without branches to avoid timing attacks. Return: + * - INVALID_PADDING if the padding is bad (bad != 0). + * - OUTPUT_TOO_LARGE if the padding is good but the decrypted + * plaintext does not fit in the output buffer. + * - 0 if the padding is correct. */ + ret = -(int) mbedtls_ct_uint_if( + bad, -MBEDTLS_ERR_RSA_INVALID_PADDING, + mbedtls_ct_uint_if(output_too_large, + -MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE, + 0)); + + /* If the padding is bad or the plaintext is too large, zero the + * data that we're about to copy to the output buffer. + * We need to copy the same amount of data + * from the same buffer whether the padding is good or not to + * avoid leaking the padding validity through overall timing or + * through memory or cache access patterns. */ + bad = mbedtls_ct_uint_mask(bad | output_too_large); + for (i = 11; i < ilen; i++) { + input[i] &= ~bad; + } + + /* If the plaintext is too large, truncate it to the buffer size. + * Copy anyway to avoid revealing the length through timing, because + * revealing the length is as bad as revealing the padding validity + * for a Bleichenbacher attack. */ + plaintext_size = mbedtls_ct_uint_if(output_too_large, + (unsigned) plaintext_max_size, + (unsigned) plaintext_size); + + /* Move the plaintext to the leftmost position where it can start in + * the working buffer, i.e. make it start plaintext_max_size from + * the end of the buffer. Do this with a memory access trace that + * does not depend on the plaintext size. After this move, the + * starting location of the plaintext is no longer sensitive + * information. */ + mbedtls_ct_mem_move_to_left(input + ilen - plaintext_max_size, + plaintext_max_size, + plaintext_max_size - plaintext_size); + + /* Finally copy the decrypted plaintext plus trailing zeros into the output + * buffer. If output_max_len is 0, then output may be an invalid pointer + * and the result of memcpy() would be undefined; prevent undefined + * behavior making sure to depend only on output_max_len (the size of the + * user-provided output buffer), which is independent from plaintext + * length, validity of padding, success of the decryption, and other + * secrets. */ + if (output_max_len != 0) { + memcpy(output, input + ilen - plaintext_max_size, plaintext_max_size); + } + + /* Report the amount of data we copied to the output buffer. In case + * of errors (bad padding or output too large), the value of *olen + * when this function returns is not specified. Making it equivalent + * to the good case limits the risks of leaking the padding validity. */ + *olen = plaintext_size; + + return ret; +} + +#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ diff --git a/r5dev/thirdparty/mbedtls/constant_time_internal.h b/r5dev/thirdparty/mbedtls/constant_time_internal.h new file mode 100644 index 00000000..c4a32c7f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/constant_time_internal.h @@ -0,0 +1,363 @@ +/** + * Constant-time functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CONSTANT_TIME_INTERNAL_H +#define MBEDTLS_CONSTANT_TIME_INTERNAL_H + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) +#include "ssl_misc.h" +#endif + +#include + + +/** Turn a value into a mask: + * - if \p value == 0, return the all-bits 0 mask, aka 0 + * - otherwise, return the all-bits 1 mask, aka (unsigned) -1 + * + * This function can be used to write constant-time code by replacing branches + * with bit operations using masks. + * + * \param value The value to analyze. + * + * \return Zero if \p value is zero, otherwise all-bits-one. + */ +unsigned mbedtls_ct_uint_mask(unsigned value); + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + +/** Turn a value into a mask: + * - if \p value == 0, return the all-bits 0 mask, aka 0 + * - otherwise, return the all-bits 1 mask, aka (size_t) -1 + * + * This function can be used to write constant-time code by replacing branches + * with bit operations using masks. + * + * \param value The value to analyze. + * + * \return Zero if \p value is zero, otherwise all-bits-one. + */ +size_t mbedtls_ct_size_mask(size_t value); + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + +#if defined(MBEDTLS_BIGNUM_C) + +/** Turn a value into a mask: + * - if \p value == 0, return the all-bits 0 mask, aka 0 + * - otherwise, return the all-bits 1 mask, aka (mbedtls_mpi_uint) -1 + * + * This function can be used to write constant-time code by replacing branches + * with bit operations using masks. + * + * \param value The value to analyze. + * + * \return Zero if \p value is zero, otherwise all-bits-one. + */ +mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask(mbedtls_mpi_uint value); + +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) + +/** Constant-flow mask generation for "greater or equal" comparison: + * - if \p x >= \p y, return all-bits 1, that is (size_t) -1 + * - otherwise, return all bits 0, that is 0 + * + * This function can be used to write constant-time code by replacing branches + * with bit operations using masks. + * + * \param x The first value to analyze. + * \param y The second value to analyze. + * + * \return All-bits-one if \p x is greater or equal than \p y, + * otherwise zero. + */ +size_t mbedtls_ct_size_mask_ge(size_t x, + size_t y); + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ + +/** Constant-flow boolean "equal" comparison: + * return x == y + * + * This is equivalent to \p x == \p y, but is likely to be compiled + * to code using bitwise operation rather than a branch. + * + * \param x The first value to analyze. + * \param y The second value to analyze. + * + * \return 1 if \p x equals to \p y, otherwise 0. + */ +unsigned mbedtls_ct_size_bool_eq(size_t x, + size_t y); + +#if defined(MBEDTLS_BIGNUM_C) + +/** Decide if an integer is less than the other, without branches. + * + * This is equivalent to \p x < \p y, but is likely to be compiled + * to code using bitwise operation rather than a branch. + * + * \param x The first value to analyze. + * \param y The second value to analyze. + * + * \return 1 if \p x is less than \p y, otherwise 0. + */ +unsigned mbedtls_ct_mpi_uint_lt(const mbedtls_mpi_uint x, + const mbedtls_mpi_uint y); + +/** + * \brief Check if one unsigned MPI is less than another in constant + * time. + * + * \param A The left-hand MPI. This must point to an array of limbs + * with the same allocated length as \p B. + * \param B The right-hand MPI. This must point to an array of limbs + * with the same allocated length as \p A. + * \param limbs The number of limbs in \p A and \p B. + * This must not be 0. + * + * \return The result of the comparison: + * \c 1 if \p A is less than \p B. + * \c 0 if \p A is greater than or equal to \p B. + */ +unsigned mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A, + const mbedtls_mpi_uint *B, + size_t limbs); +#endif /* MBEDTLS_BIGNUM_C */ + +/** Choose between two integer values without branches. + * + * This is equivalent to `condition ? if1 : if0`, but is likely to be compiled + * to code using bitwise operation rather than a branch. + * + * \param condition Condition to test. + * \param if1 Value to use if \p condition is nonzero. + * \param if0 Value to use if \p condition is zero. + * + * \return \c if1 if \p condition is nonzero, otherwise \c if0. + */ +unsigned mbedtls_ct_uint_if(unsigned condition, + unsigned if1, + unsigned if0); + +#if defined(MBEDTLS_BIGNUM_C) + +/** Conditionally assign a value without branches. + * + * This is equivalent to `if ( condition ) dest = src`, but is likely + * to be compiled to code using bitwise operation rather than a branch. + * + * \param n \p dest and \p src must be arrays of limbs of size n. + * \param dest The MPI to conditionally assign to. This must point + * to an initialized MPI. + * \param src The MPI to be assigned from. This must point to an + * initialized MPI. + * \param condition Condition to test, must be 0 or 1. + */ +void mbedtls_ct_mpi_uint_cond_assign(size_t n, + mbedtls_mpi_uint *dest, + const mbedtls_mpi_uint *src, + unsigned char condition); + +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_BASE64_C) + +/** Given a value in the range 0..63, return the corresponding Base64 digit. + * + * The implementation assumes that letters are consecutive (e.g. ASCII + * but not EBCDIC). + * + * \param value A value in the range 0..63. + * + * \return A base64 digit converted from \p value. + */ +unsigned char mbedtls_ct_base64_enc_char(unsigned char value); + +/** Given a Base64 digit, return its value. + * + * If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'), + * return -1. + * + * The implementation assumes that letters are consecutive (e.g. ASCII + * but not EBCDIC). + * + * \param c A base64 digit. + * + * \return The value of the base64 digit \p c. + */ +signed char mbedtls_ct_base64_dec_value(unsigned char c); + +#endif /* MBEDTLS_BASE64_C */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + +/** Conditional memcpy without branches. + * + * This is equivalent to `if ( c1 == c2 ) memcpy(dest, src, len)`, but is likely + * to be compiled to code using bitwise operation rather than a branch. + * + * \param dest The pointer to conditionally copy to. + * \param src The pointer to copy from. Shouldn't overlap with \p dest. + * \param len The number of bytes to copy. + * \param c1 The first value to analyze in the condition. + * \param c2 The second value to analyze in the condition. + */ +void mbedtls_ct_memcpy_if_eq(unsigned char *dest, + const unsigned char *src, + size_t len, + size_t c1, size_t c2); + +/** Copy data from a secret position with constant flow. + * + * This function copies \p len bytes from \p src_base + \p offset_secret to \p + * dst, with a code flow and memory access pattern that does not depend on \p + * offset_secret, but only on \p offset_min, \p offset_max and \p len. + * Functionally equivalent to `memcpy(dst, src + offset_secret, len)`. + * + * \note This function reads from \p dest, but the value that + * is read does not influence the result and this + * function's behavior is well-defined regardless of the + * contents of the buffers. This may result in false + * positives from static or dynamic analyzers, especially + * if \p dest is not initialized. + * + * \param dest The destination buffer. This must point to a writable + * buffer of at least \p len bytes. + * \param src The base of the source buffer. This must point to a + * readable buffer of at least \p offset_max + \p len + * bytes. Shouldn't overlap with \p dest. + * \param offset The offset in the source buffer from which to copy. + * This must be no less than \p offset_min and no greater + * than \p offset_max. + * \param offset_min The minimal value of \p offset. + * \param offset_max The maximal value of \p offset. + * \param len The number of bytes to copy. + */ +void mbedtls_ct_memcpy_offset(unsigned char *dest, + const unsigned char *src, + size_t offset, + size_t offset_min, + size_t offset_max, + size_t len); + +/** Compute the HMAC of variable-length data with constant flow. + * + * This function computes the HMAC of the concatenation of \p add_data and \p + * data, and does with a code flow and memory access pattern that does not + * depend on \p data_len_secret, but only on \p min_data_len and \p + * max_data_len. In particular, this function always reads exactly \p + * max_data_len bytes from \p data. + * + * \param ctx The HMAC context. It must have keys configured + * with mbedtls_md_hmac_starts() and use one of the + * following hashes: SHA-384, SHA-256, SHA-1 or MD-5. + * It is reset using mbedtls_md_hmac_reset() after + * the computation is complete to prepare for the + * next computation. + * \param add_data The first part of the message whose HMAC is being + * calculated. This must point to a readable buffer + * of \p add_data_len bytes. + * \param add_data_len The length of \p add_data in bytes. + * \param data The buffer containing the second part of the + * message. This must point to a readable buffer + * of \p max_data_len bytes. + * \param data_len_secret The length of the data to process in \p data. + * This must be no less than \p min_data_len and no + * greater than \p max_data_len. + * \param min_data_len The minimal length of the second part of the + * message, read from \p data. + * \param max_data_len The maximal length of the second part of the + * message, read from \p data. + * \param output The HMAC will be written here. This must point to + * a writable buffer of sufficient size to hold the + * HMAC value. + * + * \retval 0 on success. + * \retval #MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED + * The hardware accelerator failed. + */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) +int mbedtls_ct_hmac(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const unsigned char *add_data, + size_t add_data_len, + const unsigned char *data, + size_t data_len_secret, + size_t min_data_len, + size_t max_data_len, + unsigned char *output); +#else +int mbedtls_ct_hmac(mbedtls_md_context_t *ctx, + const unsigned char *add_data, + size_t add_data_len, + const unsigned char *data, + size_t data_len_secret, + size_t min_data_len, + size_t max_data_len, + unsigned char *output); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + +#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) + +/** This function performs the unpadding part of a PKCS#1 v1.5 decryption + * operation (EME-PKCS1-v1_5 decoding). + * + * \note The return value from this function is a sensitive value + * (this is unusual). #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE shouldn't happen + * in a well-written application, but 0 vs #MBEDTLS_ERR_RSA_INVALID_PADDING + * is often a situation that an attacker can provoke and leaking which + * one is the result is precisely the information the attacker wants. + * + * \param input The input buffer which is the payload inside PKCS#1v1.5 + * encryption padding, called the "encoded message EM" + * by the terminology. + * \param ilen The length of the payload in the \p input buffer. + * \param output The buffer for the payload, called "message M" by the + * PKCS#1 terminology. This must be a writable buffer of + * length \p output_max_len bytes. + * \param olen The address at which to store the length of + * the payload. This must not be \c NULL. + * \param output_max_len The length in bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE + * The output buffer is too small for the unpadded payload. + * \return #MBEDTLS_ERR_RSA_INVALID_PADDING + * The input doesn't contain properly formatted padding. + */ +int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input, + size_t ilen, + unsigned char *output, + size_t output_max_len, + size_t *olen); + +#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ + +#endif /* MBEDTLS_CONSTANT_TIME_INTERNAL_H */ diff --git a/r5dev/thirdparty/mbedtls/constant_time_invasive.h b/r5dev/thirdparty/mbedtls/constant_time_invasive.h new file mode 100644 index 00000000..c176b28f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/constant_time_invasive.h @@ -0,0 +1,51 @@ +/** + * \file constant_time_invasive.h + * + * \brief Constant-time module: interfaces for invasive testing only. + * + * The interfaces in this file are intended for testing purposes only. + * They SHOULD NOT be made available in library integrations except when + * building the library for testing. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CONSTANT_TIME_INVASIVE_H +#define MBEDTLS_CONSTANT_TIME_INVASIVE_H + +#include "common.h" + +#if defined(MBEDTLS_TEST_HOOKS) + +/** Turn a value into a mask: + * - if \p low <= \p c <= \p high, + * return the all-bits 1 mask, aka (unsigned) -1 + * - otherwise, return the all-bits 0 mask, aka 0 + * + * \param low The value to analyze. + * \param high The value to analyze. + * \param c The value to analyze. + * + * \return All-bits-one if \p low <= \p c <= \p high, otherwise zero. + */ +unsigned char mbedtls_ct_uchar_mask_of_range(unsigned char low, + unsigned char high, + unsigned char c); + +#endif /* MBEDTLS_TEST_HOOKS */ + +#endif /* MBEDTLS_CONSTANT_TIME_INVASIVE_H */ diff --git a/r5dev/thirdparty/mbedtls/ctr_drbg.c b/r5dev/thirdparty/mbedtls/ctr_drbg.c new file mode 100644 index 00000000..acc4208c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ctr_drbg.c @@ -0,0 +1,893 @@ +/* + * CTR_DRBG implementation based on AES-256 (NIST SP 800-90) + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The NIST SP 800-90 DRBGs are described in the following publication. + * + * http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_CTR_DRBG_C) + +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_FS_IO) +#include +#endif + +#include "mbedtls/platform.h" + +/* + * CTR_DRBG context initialization + */ +void mbedtls_ctr_drbg_init(mbedtls_ctr_drbg_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_ctr_drbg_context)); + mbedtls_aes_init(&ctx->aes_ctx); + /* Indicate that the entropy nonce length is not set explicitly. + * See mbedtls_ctr_drbg_set_nonce_len(). */ + ctx->reseed_counter = -1; + + ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL; +} + +/* + * This function resets CTR_DRBG context to the state immediately + * after initial call of mbedtls_ctr_drbg_init(). + */ +void mbedtls_ctr_drbg_free(mbedtls_ctr_drbg_context *ctx) +{ + if (ctx == NULL) { + return; + } + +#if defined(MBEDTLS_THREADING_C) + /* The mutex is initialized iff f_entropy is set. */ + if (ctx->f_entropy != NULL) { + mbedtls_mutex_free(&ctx->mutex); + } +#endif + mbedtls_aes_free(&ctx->aes_ctx); + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_ctr_drbg_context)); + ctx->reseed_interval = MBEDTLS_CTR_DRBG_RESEED_INTERVAL; + ctx->reseed_counter = -1; +} + +void mbedtls_ctr_drbg_set_prediction_resistance(mbedtls_ctr_drbg_context *ctx, + int resistance) +{ + ctx->prediction_resistance = resistance; +} + +void mbedtls_ctr_drbg_set_entropy_len(mbedtls_ctr_drbg_context *ctx, + size_t len) +{ + ctx->entropy_len = len; +} + +int mbedtls_ctr_drbg_set_nonce_len(mbedtls_ctr_drbg_context *ctx, + size_t len) +{ + /* If mbedtls_ctr_drbg_seed() has already been called, it's + * too late. Return the error code that's closest to making sense. */ + if (ctx->f_entropy != NULL) { + return MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED; + } + + if (len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT) { + return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + } + + /* This shouldn't be an issue because + * MBEDTLS_CTR_DRBG_MAX_SEED_INPUT < INT_MAX in any sensible + * configuration, but make sure anyway. */ + if (len > INT_MAX) { + return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + } + + /* For backward compatibility with Mbed TLS <= 2.19, store the + * entropy nonce length in a field that already exists, but isn't + * used until after the initial seeding. */ + /* Due to the capping of len above, the value fits in an int. */ + ctx->reseed_counter = (int) len; + return 0; +} + +void mbedtls_ctr_drbg_set_reseed_interval(mbedtls_ctr_drbg_context *ctx, + int interval) +{ + ctx->reseed_interval = interval; +} + +static int block_cipher_df(unsigned char *output, + const unsigned char *data, size_t data_len) +{ + unsigned char buf[MBEDTLS_CTR_DRBG_MAX_SEED_INPUT + + MBEDTLS_CTR_DRBG_BLOCKSIZE + 16]; + unsigned char tmp[MBEDTLS_CTR_DRBG_SEEDLEN]; + unsigned char key[MBEDTLS_CTR_DRBG_KEYSIZE]; + unsigned char chain[MBEDTLS_CTR_DRBG_BLOCKSIZE]; + unsigned char *p, *iv; + mbedtls_aes_context aes_ctx; + int ret = 0; + + int i, j; + size_t buf_len, use_len; + + if (data_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT) { + return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + } + + memset(buf, 0, MBEDTLS_CTR_DRBG_MAX_SEED_INPUT + + MBEDTLS_CTR_DRBG_BLOCKSIZE + 16); + mbedtls_aes_init(&aes_ctx); + + /* + * Construct IV (16 bytes) and S in buffer + * IV = Counter (in 32-bits) padded to 16 with zeroes + * S = Length input string (in 32-bits) || Length of output (in 32-bits) || + * data || 0x80 + * (Total is padded to a multiple of 16-bytes with zeroes) + */ + p = buf + MBEDTLS_CTR_DRBG_BLOCKSIZE; + MBEDTLS_PUT_UINT32_BE(data_len, p, 0); + p += 4 + 3; + *p++ = MBEDTLS_CTR_DRBG_SEEDLEN; + memcpy(p, data, data_len); + p[data_len] = 0x80; + + buf_len = MBEDTLS_CTR_DRBG_BLOCKSIZE + 8 + data_len + 1; + + for (i = 0; i < MBEDTLS_CTR_DRBG_KEYSIZE; i++) { + key[i] = i; + } + + if ((ret = mbedtls_aes_setkey_enc(&aes_ctx, key, + MBEDTLS_CTR_DRBG_KEYBITS)) != 0) { + goto exit; + } + + /* + * Reduce data to MBEDTLS_CTR_DRBG_SEEDLEN bytes of data + */ + for (j = 0; j < MBEDTLS_CTR_DRBG_SEEDLEN; j += MBEDTLS_CTR_DRBG_BLOCKSIZE) { + p = buf; + memset(chain, 0, MBEDTLS_CTR_DRBG_BLOCKSIZE); + use_len = buf_len; + + while (use_len > 0) { + mbedtls_xor(chain, chain, p, MBEDTLS_CTR_DRBG_BLOCKSIZE); + p += MBEDTLS_CTR_DRBG_BLOCKSIZE; + use_len -= (use_len >= MBEDTLS_CTR_DRBG_BLOCKSIZE) ? + MBEDTLS_CTR_DRBG_BLOCKSIZE : use_len; + + if ((ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, + chain, chain)) != 0) { + goto exit; + } + } + + memcpy(tmp + j, chain, MBEDTLS_CTR_DRBG_BLOCKSIZE); + + /* + * Update IV + */ + buf[3]++; + } + + /* + * Do final encryption with reduced data + */ + if ((ret = mbedtls_aes_setkey_enc(&aes_ctx, tmp, + MBEDTLS_CTR_DRBG_KEYBITS)) != 0) { + goto exit; + } + iv = tmp + MBEDTLS_CTR_DRBG_KEYSIZE; + p = output; + + for (j = 0; j < MBEDTLS_CTR_DRBG_SEEDLEN; j += MBEDTLS_CTR_DRBG_BLOCKSIZE) { + if ((ret = mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, + iv, iv)) != 0) { + goto exit; + } + memcpy(p, iv, MBEDTLS_CTR_DRBG_BLOCKSIZE); + p += MBEDTLS_CTR_DRBG_BLOCKSIZE; + } +exit: + mbedtls_aes_free(&aes_ctx); + /* + * tidy up the stack + */ + mbedtls_platform_zeroize(buf, sizeof(buf)); + mbedtls_platform_zeroize(tmp, sizeof(tmp)); + mbedtls_platform_zeroize(key, sizeof(key)); + mbedtls_platform_zeroize(chain, sizeof(chain)); + if (0 != ret) { + /* + * wipe partial seed from memory + */ + mbedtls_platform_zeroize(output, MBEDTLS_CTR_DRBG_SEEDLEN); + } + + return ret; +} + +/* CTR_DRBG_Update (SP 800-90A §10.2.1.2) + * ctr_drbg_update_internal(ctx, provided_data) + * implements + * CTR_DRBG_Update(provided_data, Key, V) + * with inputs and outputs + * ctx->aes_ctx = Key + * ctx->counter = V + */ +static int ctr_drbg_update_internal(mbedtls_ctr_drbg_context *ctx, + const unsigned char data[MBEDTLS_CTR_DRBG_SEEDLEN]) +{ + unsigned char tmp[MBEDTLS_CTR_DRBG_SEEDLEN]; + unsigned char *p = tmp; + int i, j; + int ret = 0; + + memset(tmp, 0, MBEDTLS_CTR_DRBG_SEEDLEN); + + for (j = 0; j < MBEDTLS_CTR_DRBG_SEEDLEN; j += MBEDTLS_CTR_DRBG_BLOCKSIZE) { + /* + * Increase counter + */ + for (i = MBEDTLS_CTR_DRBG_BLOCKSIZE; i > 0; i--) { + if (++ctx->counter[i - 1] != 0) { + break; + } + } + + /* + * Crypt counter block + */ + if ((ret = mbedtls_aes_crypt_ecb(&ctx->aes_ctx, MBEDTLS_AES_ENCRYPT, + ctx->counter, p)) != 0) { + goto exit; + } + + p += MBEDTLS_CTR_DRBG_BLOCKSIZE; + } + + for (i = 0; i < MBEDTLS_CTR_DRBG_SEEDLEN; i++) { + tmp[i] ^= data[i]; + } + + /* + * Update key and counter + */ + if ((ret = mbedtls_aes_setkey_enc(&ctx->aes_ctx, tmp, + MBEDTLS_CTR_DRBG_KEYBITS)) != 0) { + goto exit; + } + memcpy(ctx->counter, tmp + MBEDTLS_CTR_DRBG_KEYSIZE, + MBEDTLS_CTR_DRBG_BLOCKSIZE); + +exit: + mbedtls_platform_zeroize(tmp, sizeof(tmp)); + return ret; +} + +/* CTR_DRBG_Instantiate with derivation function (SP 800-90A §10.2.1.3.2) + * mbedtls_ctr_drbg_update(ctx, additional, add_len) + * implements + * CTR_DRBG_Instantiate(entropy_input, nonce, personalization_string, + * security_strength) -> initial_working_state + * with inputs + * ctx->counter = all-bits-0 + * ctx->aes_ctx = context from all-bits-0 key + * additional[:add_len] = entropy_input || nonce || personalization_string + * and with outputs + * ctx = initial_working_state + */ +int mbedtls_ctr_drbg_update(mbedtls_ctr_drbg_context *ctx, + const unsigned char *additional, + size_t add_len) +{ + unsigned char add_input[MBEDTLS_CTR_DRBG_SEEDLEN]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (add_len == 0) { + return 0; + } + + if ((ret = block_cipher_df(add_input, additional, add_len)) != 0) { + goto exit; + } + if ((ret = ctr_drbg_update_internal(ctx, add_input)) != 0) { + goto exit; + } + +exit: + mbedtls_platform_zeroize(add_input, sizeof(add_input)); + return ret; +} + +/* CTR_DRBG_Reseed with derivation function (SP 800-90A §10.2.1.4.2) + * mbedtls_ctr_drbg_reseed(ctx, additional, len, nonce_len) + * implements + * CTR_DRBG_Reseed(working_state, entropy_input, additional_input) + * -> new_working_state + * with inputs + * ctx contains working_state + * additional[:len] = additional_input + * and entropy_input comes from calling ctx->f_entropy + * for (ctx->entropy_len + nonce_len) bytes + * and with output + * ctx contains new_working_state + */ +static int mbedtls_ctr_drbg_reseed_internal(mbedtls_ctr_drbg_context *ctx, + const unsigned char *additional, + size_t len, + size_t nonce_len) +{ + unsigned char seed[MBEDTLS_CTR_DRBG_MAX_SEED_INPUT]; + size_t seedlen = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ctx->entropy_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT) { + return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + } + if (nonce_len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - ctx->entropy_len) { + return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + } + if (len > MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - ctx->entropy_len - nonce_len) { + return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + } + + memset(seed, 0, MBEDTLS_CTR_DRBG_MAX_SEED_INPUT); + + /* Gather entropy_len bytes of entropy to seed state. */ + if (0 != ctx->f_entropy(ctx->p_entropy, seed, ctx->entropy_len)) { + return MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED; + } + seedlen += ctx->entropy_len; + + /* Gather entropy for a nonce if requested. */ + if (nonce_len != 0) { + if (0 != ctx->f_entropy(ctx->p_entropy, seed + seedlen, nonce_len)) { + return MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED; + } + seedlen += nonce_len; + } + + /* Add additional data if provided. */ + if (additional != NULL && len != 0) { + memcpy(seed + seedlen, additional, len); + seedlen += len; + } + + /* Reduce to 384 bits. */ + if ((ret = block_cipher_df(seed, seed, seedlen)) != 0) { + goto exit; + } + + /* Update state. */ + if ((ret = ctr_drbg_update_internal(ctx, seed)) != 0) { + goto exit; + } + ctx->reseed_counter = 1; + +exit: + mbedtls_platform_zeroize(seed, sizeof(seed)); + return ret; +} + +int mbedtls_ctr_drbg_reseed(mbedtls_ctr_drbg_context *ctx, + const unsigned char *additional, size_t len) +{ + return mbedtls_ctr_drbg_reseed_internal(ctx, additional, len, 0); +} + +/* Return a "good" nonce length for CTR_DRBG. The chosen nonce length + * is sufficient to achieve the maximum security strength given the key + * size and entropy length. If there is enough entropy in the initial + * call to the entropy function to serve as both the entropy input and + * the nonce, don't make a second call to get a nonce. */ +static size_t good_nonce_len(size_t entropy_len) +{ + if (entropy_len >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2) { + return 0; + } else { + return (entropy_len + 1) / 2; + } +} + +/* CTR_DRBG_Instantiate with derivation function (SP 800-90A §10.2.1.3.2) + * mbedtls_ctr_drbg_seed(ctx, f_entropy, p_entropy, custom, len) + * implements + * CTR_DRBG_Instantiate(entropy_input, nonce, personalization_string, + * security_strength) -> initial_working_state + * with inputs + * custom[:len] = nonce || personalization_string + * where entropy_input comes from f_entropy for ctx->entropy_len bytes + * and with outputs + * ctx = initial_working_state + */ +int mbedtls_ctr_drbg_seed(mbedtls_ctr_drbg_context *ctx, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char key[MBEDTLS_CTR_DRBG_KEYSIZE]; + size_t nonce_len; + + memset(key, 0, MBEDTLS_CTR_DRBG_KEYSIZE); + + /* The mutex is initialized iff f_entropy is set. */ +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&ctx->mutex); +#endif + + ctx->f_entropy = f_entropy; + ctx->p_entropy = p_entropy; + + if (ctx->entropy_len == 0) { + ctx->entropy_len = MBEDTLS_CTR_DRBG_ENTROPY_LEN; + } + /* ctx->reseed_counter contains the desired amount of entropy to + * grab for a nonce (see mbedtls_ctr_drbg_set_nonce_len()). + * If it's -1, indicating that the entropy nonce length was not set + * explicitly, use a sufficiently large nonce for security. */ + nonce_len = (ctx->reseed_counter >= 0 ? + (size_t) ctx->reseed_counter : + good_nonce_len(ctx->entropy_len)); + + /* Initialize with an empty key. */ + if ((ret = mbedtls_aes_setkey_enc(&ctx->aes_ctx, key, + MBEDTLS_CTR_DRBG_KEYBITS)) != 0) { + return ret; + } + + /* Do the initial seeding. */ + if ((ret = mbedtls_ctr_drbg_reseed_internal(ctx, custom, len, + nonce_len)) != 0) { + return ret; + } + return 0; +} + +/* CTR_DRBG_Generate with derivation function (SP 800-90A §10.2.1.5.2) + * mbedtls_ctr_drbg_random_with_add(ctx, output, output_len, additional, add_len) + * implements + * CTR_DRBG_Reseed(working_state, entropy_input, additional[:add_len]) + * -> working_state_after_reseed + * if required, then + * CTR_DRBG_Generate(working_state_after_reseed, + * requested_number_of_bits, additional_input) + * -> status, returned_bits, new_working_state + * with inputs + * ctx contains working_state + * requested_number_of_bits = 8 * output_len + * additional[:add_len] = additional_input + * and entropy_input comes from calling ctx->f_entropy + * and with outputs + * status = SUCCESS (this function does the reseed internally) + * returned_bits = output[:output_len] + * ctx contains new_working_state + */ +int mbedtls_ctr_drbg_random_with_add(void *p_rng, + unsigned char *output, size_t output_len, + const unsigned char *additional, size_t add_len) +{ + int ret = 0; + mbedtls_ctr_drbg_context *ctx = (mbedtls_ctr_drbg_context *) p_rng; + unsigned char add_input[MBEDTLS_CTR_DRBG_SEEDLEN]; + unsigned char *p = output; + unsigned char tmp[MBEDTLS_CTR_DRBG_BLOCKSIZE]; + int i; + size_t use_len; + + if (output_len > MBEDTLS_CTR_DRBG_MAX_REQUEST) { + return MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG; + } + + if (add_len > MBEDTLS_CTR_DRBG_MAX_INPUT) { + return MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + } + + memset(add_input, 0, MBEDTLS_CTR_DRBG_SEEDLEN); + + if (ctx->reseed_counter > ctx->reseed_interval || + ctx->prediction_resistance) { + if ((ret = mbedtls_ctr_drbg_reseed(ctx, additional, add_len)) != 0) { + return ret; + } + add_len = 0; + } + + if (add_len > 0) { + if ((ret = block_cipher_df(add_input, additional, add_len)) != 0) { + goto exit; + } + if ((ret = ctr_drbg_update_internal(ctx, add_input)) != 0) { + goto exit; + } + } + + while (output_len > 0) { + /* + * Increase counter + */ + for (i = MBEDTLS_CTR_DRBG_BLOCKSIZE; i > 0; i--) { + if (++ctx->counter[i - 1] != 0) { + break; + } + } + + /* + * Crypt counter block + */ + if ((ret = mbedtls_aes_crypt_ecb(&ctx->aes_ctx, MBEDTLS_AES_ENCRYPT, + ctx->counter, tmp)) != 0) { + goto exit; + } + + use_len = (output_len > MBEDTLS_CTR_DRBG_BLOCKSIZE) + ? MBEDTLS_CTR_DRBG_BLOCKSIZE : output_len; + /* + * Copy random block to destination + */ + memcpy(p, tmp, use_len); + p += use_len; + output_len -= use_len; + } + + if ((ret = ctr_drbg_update_internal(ctx, add_input)) != 0) { + goto exit; + } + + ctx->reseed_counter++; + +exit: + mbedtls_platform_zeroize(add_input, sizeof(add_input)); + mbedtls_platform_zeroize(tmp, sizeof(tmp)); + return ret; +} + +int mbedtls_ctr_drbg_random(void *p_rng, unsigned char *output, + size_t output_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ctr_drbg_context *ctx = (mbedtls_ctr_drbg_context *) p_rng; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + ret = mbedtls_ctr_drbg_random_with_add(ctx, output, output_len, NULL, 0); + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +#if defined(MBEDTLS_FS_IO) +int mbedtls_ctr_drbg_write_seed_file(mbedtls_ctr_drbg_context *ctx, + const char *path) +{ + int ret = MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR; + FILE *f; + unsigned char buf[MBEDTLS_CTR_DRBG_MAX_INPUT]; + + if ((f = fopen(path, "wb")) == NULL) { + return MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + if ((ret = mbedtls_ctr_drbg_random(ctx, buf, + MBEDTLS_CTR_DRBG_MAX_INPUT)) != 0) { + goto exit; + } + + if (fwrite(buf, 1, MBEDTLS_CTR_DRBG_MAX_INPUT, f) != + MBEDTLS_CTR_DRBG_MAX_INPUT) { + ret = MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR; + } else { + ret = 0; + } + +exit: + mbedtls_platform_zeroize(buf, sizeof(buf)); + + fclose(f); + return ret; +} + +int mbedtls_ctr_drbg_update_seed_file(mbedtls_ctr_drbg_context *ctx, + const char *path) +{ + int ret = 0; + FILE *f = NULL; + size_t n; + unsigned char buf[MBEDTLS_CTR_DRBG_MAX_INPUT]; + unsigned char c; + + if ((f = fopen(path, "rb")) == NULL) { + return MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + n = fread(buf, 1, sizeof(buf), f); + if (fread(&c, 1, 1, f) != 0) { + ret = MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG; + goto exit; + } + if (n == 0 || ferror(f)) { + ret = MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR; + goto exit; + } + fclose(f); + f = NULL; + + ret = mbedtls_ctr_drbg_update(ctx, buf, n); + +exit: + mbedtls_platform_zeroize(buf, sizeof(buf)); + if (f != NULL) { + fclose(f); + } + if (ret != 0) { + return ret; + } + return mbedtls_ctr_drbg_write_seed_file(ctx, path); +} +#endif /* MBEDTLS_FS_IO */ + +#if defined(MBEDTLS_SELF_TEST) + +/* The CTR_DRBG NIST test vectors used here are available at + * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip + * + * The parameters used to derive the test data are: + * + * [AES-128 use df] + * [PredictionResistance = True/False] + * [EntropyInputLen = 128] + * [NonceLen = 64] + * [PersonalizationStringLen = 128] + * [AdditionalInputLen = 0] + * [ReturnedBitsLen = 512] + * + * [AES-256 use df] + * [PredictionResistance = True/False] + * [EntropyInputLen = 256] + * [NonceLen = 128] + * [PersonalizationStringLen = 256] + * [AdditionalInputLen = 0] + * [ReturnedBitsLen = 512] + * + */ + +#if defined(MBEDTLS_CTR_DRBG_USE_128_BIT_KEY) +static const unsigned char entropy_source_pr[] = +{ 0x04, 0xd9, 0x49, 0xa6, 0xdc, 0xe8, 0x6e, 0xbb, + 0xf1, 0x08, 0x77, 0x2b, 0x9e, 0x08, 0xca, 0x92, + 0x65, 0x16, 0xda, 0x99, 0xa2, 0x59, 0xf3, 0xe8, + 0x38, 0x7e, 0x3f, 0x6b, 0x51, 0x70, 0x7b, 0x20, + 0xec, 0x53, 0xd0, 0x66, 0xc3, 0x0f, 0xe3, 0xb0, + 0xe0, 0x86, 0xa6, 0xaa, 0x5f, 0x72, 0x2f, 0xad, + 0xf7, 0xef, 0x06, 0xb8, 0xd6, 0x9c, 0x9d, 0xe8 }; + +static const unsigned char entropy_source_nopr[] = +{ 0x07, 0x0d, 0x59, 0x63, 0x98, 0x73, 0xa5, 0x45, + 0x27, 0x38, 0x22, 0x7b, 0x76, 0x85, 0xd1, 0xa9, + 0x74, 0x18, 0x1f, 0x3c, 0x22, 0xf6, 0x49, 0x20, + 0x4a, 0x47, 0xc2, 0xf3, 0x85, 0x16, 0xb4, 0x6f, + 0x00, 0x2e, 0x71, 0xda, 0xed, 0x16, 0x9b, 0x5c }; + +static const unsigned char pers_pr[] = +{ 0xbf, 0xa4, 0x9a, 0x8f, 0x7b, 0xd8, 0xb1, 0x7a, + 0x9d, 0xfa, 0x45, 0xed, 0x21, 0x52, 0xb3, 0xad }; + +static const unsigned char pers_nopr[] = +{ 0x4e, 0x61, 0x79, 0xd4, 0xc2, 0x72, 0xa1, 0x4c, + 0xf1, 0x3d, 0xf6, 0x5e, 0xa3, 0xa6, 0xe5, 0x0f }; + +static const unsigned char result_pr[] = +{ 0xc9, 0x0a, 0xaf, 0x85, 0x89, 0x71, 0x44, 0x66, + 0x4f, 0x25, 0x0b, 0x2b, 0xde, 0xd8, 0xfa, 0xff, + 0x52, 0x5a, 0x1b, 0x32, 0x5e, 0x41, 0x7a, 0x10, + 0x1f, 0xef, 0x1e, 0x62, 0x23, 0xe9, 0x20, 0x30, + 0xc9, 0x0d, 0xad, 0x69, 0xb4, 0x9c, 0x5b, 0xf4, + 0x87, 0x42, 0xd5, 0xae, 0x5e, 0x5e, 0x43, 0xcc, + 0xd9, 0xfd, 0x0b, 0x93, 0x4a, 0xe3, 0xd4, 0x06, + 0x37, 0x36, 0x0f, 0x3f, 0x72, 0x82, 0x0c, 0xcf }; + +static const unsigned char result_nopr[] = +{ 0x31, 0xc9, 0x91, 0x09, 0xf8, 0xc5, 0x10, 0x13, + 0x3c, 0xd3, 0x96, 0xf9, 0xbc, 0x2c, 0x12, 0xc0, + 0x7c, 0xc1, 0x61, 0x5f, 0xa3, 0x09, 0x99, 0xaf, + 0xd7, 0xf2, 0x36, 0xfd, 0x40, 0x1a, 0x8b, 0xf2, + 0x33, 0x38, 0xee, 0x1d, 0x03, 0x5f, 0x83, 0xb7, + 0xa2, 0x53, 0xdc, 0xee, 0x18, 0xfc, 0xa7, 0xf2, + 0xee, 0x96, 0xc6, 0xc2, 0xcd, 0x0c, 0xff, 0x02, + 0x76, 0x70, 0x69, 0xaa, 0x69, 0xd1, 0x3b, 0xe8 }; +#else /* MBEDTLS_CTR_DRBG_USE_128_BIT_KEY */ + +static const unsigned char entropy_source_pr[] = +{ 0xca, 0x58, 0xfd, 0xf2, 0xb9, 0x77, 0xcb, 0x49, + 0xd4, 0xe0, 0x5b, 0xe2, 0x39, 0x50, 0xd9, 0x8a, + 0x6a, 0xb3, 0xc5, 0x2f, 0xdf, 0x74, 0xd5, 0x85, + 0x8f, 0xd1, 0xba, 0x64, 0x54, 0x7b, 0xdb, 0x1e, + 0xc5, 0xea, 0x24, 0xc0, 0xfa, 0x0c, 0x90, 0x15, + 0x09, 0x20, 0x92, 0x42, 0x32, 0x36, 0x45, 0x45, + 0x7d, 0x20, 0x76, 0x6b, 0xcf, 0xa2, 0x15, 0xc8, + 0x2f, 0x9f, 0xbc, 0x88, 0x3f, 0x80, 0xd1, 0x2c, + 0xb7, 0x16, 0xd1, 0x80, 0x9e, 0xe1, 0xc9, 0xb3, + 0x88, 0x1b, 0x21, 0x45, 0xef, 0xa1, 0x7f, 0xce, + 0xc8, 0x92, 0x35, 0x55, 0x2a, 0xd9, 0x1d, 0x8e, + 0x12, 0x38, 0xac, 0x01, 0x4e, 0x38, 0x18, 0x76, + 0x9c, 0xf2, 0xb6, 0xd4, 0x13, 0xb6, 0x2c, 0x77, + 0xc0, 0xe7, 0xe6, 0x0c, 0x47, 0x44, 0x95, 0xbe }; + +static const unsigned char entropy_source_nopr[] = +{ 0x4c, 0xfb, 0x21, 0x86, 0x73, 0x34, 0x6d, 0x9d, + 0x50, 0xc9, 0x22, 0xe4, 0x9b, 0x0d, 0xfc, 0xd0, + 0x90, 0xad, 0xf0, 0x4f, 0x5c, 0x3b, 0xa4, 0x73, + 0x27, 0xdf, 0xcd, 0x6f, 0xa6, 0x3a, 0x78, 0x5c, + 0x01, 0x69, 0x62, 0xa7, 0xfd, 0x27, 0x87, 0xa2, + 0x4b, 0xf6, 0xbe, 0x47, 0xef, 0x37, 0x83, 0xf1, + 0xb7, 0xec, 0x46, 0x07, 0x23, 0x63, 0x83, 0x4a, + 0x1b, 0x01, 0x33, 0xf2, 0xc2, 0x38, 0x91, 0xdb, + 0x4f, 0x11, 0xa6, 0x86, 0x51, 0xf2, 0x3e, 0x3a, + 0x8b, 0x1f, 0xdc, 0x03, 0xb1, 0x92, 0xc7, 0xe7 }; + +static const unsigned char pers_pr[] = +{ 0x5a, 0x70, 0x95, 0xe9, 0x81, 0x40, 0x52, 0x33, + 0x91, 0x53, 0x7e, 0x75, 0xd6, 0x19, 0x9d, 0x1e, + 0xad, 0x0d, 0xc6, 0xa7, 0xde, 0x6c, 0x1f, 0xe0, + 0xea, 0x18, 0x33, 0xa8, 0x7e, 0x06, 0x20, 0xe9 }; + +static const unsigned char pers_nopr[] = +{ 0x88, 0xee, 0xb8, 0xe0, 0xe8, 0x3b, 0xf3, 0x29, + 0x4b, 0xda, 0xcd, 0x60, 0x99, 0xeb, 0xe4, 0xbf, + 0x55, 0xec, 0xd9, 0x11, 0x3f, 0x71, 0xe5, 0xeb, + 0xcb, 0x45, 0x75, 0xf3, 0xd6, 0xa6, 0x8a, 0x6b }; + +static const unsigned char result_pr[] = +{ 0xce, 0x2f, 0xdb, 0xb6, 0xd9, 0xb7, 0x39, 0x85, + 0x04, 0xc5, 0xc0, 0x42, 0xc2, 0x31, 0xc6, 0x1d, + 0x9b, 0x5a, 0x59, 0xf8, 0x7e, 0x0d, 0xcc, 0x62, + 0x7b, 0x65, 0x11, 0x55, 0x10, 0xeb, 0x9e, 0x3d, + 0xa4, 0xfb, 0x1c, 0x6a, 0x18, 0xc0, 0x74, 0xdb, + 0xdd, 0xe7, 0x02, 0x23, 0x63, 0x21, 0xd0, 0x39, + 0xf9, 0xa7, 0xc4, 0x52, 0x84, 0x3b, 0x49, 0x40, + 0x72, 0x2b, 0xb0, 0x6c, 0x9c, 0xdb, 0xc3, 0x43 }; + +static const unsigned char result_nopr[] = +{ 0xa5, 0x51, 0x80, 0xa1, 0x90, 0xbe, 0xf3, 0xad, + 0xaf, 0x28, 0xf6, 0xb7, 0x95, 0xe9, 0xf1, 0xf3, + 0xd6, 0xdf, 0xa1, 0xb2, 0x7d, 0xd0, 0x46, 0x7b, + 0x0c, 0x75, 0xf5, 0xfa, 0x93, 0x1e, 0x97, 0x14, + 0x75, 0xb2, 0x7c, 0xae, 0x03, 0xa2, 0x96, 0x54, + 0xe2, 0xf4, 0x09, 0x66, 0xea, 0x33, 0x64, 0x30, + 0x40, 0xd1, 0x40, 0x0f, 0xe6, 0x77, 0x87, 0x3a, + 0xf8, 0x09, 0x7c, 0x1f, 0xe9, 0xf0, 0x02, 0x98 }; +#endif /* MBEDTLS_CTR_DRBG_USE_128_BIT_KEY */ + +static size_t test_offset; +static int ctr_drbg_self_test_entropy(void *data, unsigned char *buf, + size_t len) +{ + const unsigned char *p = data; + memcpy(buf, p + test_offset, len); + test_offset += len; + return 0; +} + +#define CHK(c) if ((c) != 0) \ + { \ + if (verbose != 0) \ + mbedtls_printf("failed\n"); \ + return 1; \ + } + +#define SELF_TEST_OUTPUT_DISCARD_LENGTH 64 + +/* + * Checkup routine + */ +int mbedtls_ctr_drbg_self_test(int verbose) +{ + mbedtls_ctr_drbg_context ctx; + unsigned char buf[sizeof(result_pr)]; + + mbedtls_ctr_drbg_init(&ctx); + + /* + * Based on a NIST CTR_DRBG test vector (PR = True) + */ + if (verbose != 0) { + mbedtls_printf(" CTR_DRBG (PR = TRUE) : "); + } + + test_offset = 0; + mbedtls_ctr_drbg_set_entropy_len(&ctx, MBEDTLS_CTR_DRBG_KEYSIZE); + mbedtls_ctr_drbg_set_nonce_len(&ctx, MBEDTLS_CTR_DRBG_KEYSIZE / 2); + CHK(mbedtls_ctr_drbg_seed(&ctx, + ctr_drbg_self_test_entropy, + (void *) entropy_source_pr, + pers_pr, MBEDTLS_CTR_DRBG_KEYSIZE)); + mbedtls_ctr_drbg_set_prediction_resistance(&ctx, MBEDTLS_CTR_DRBG_PR_ON); + CHK(mbedtls_ctr_drbg_random(&ctx, buf, SELF_TEST_OUTPUT_DISCARD_LENGTH)); + CHK(mbedtls_ctr_drbg_random(&ctx, buf, sizeof(result_pr))); + CHK(memcmp(buf, result_pr, sizeof(result_pr))); + + mbedtls_ctr_drbg_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + /* + * Based on a NIST CTR_DRBG test vector (PR = FALSE) + */ + if (verbose != 0) { + mbedtls_printf(" CTR_DRBG (PR = FALSE): "); + } + + mbedtls_ctr_drbg_init(&ctx); + + test_offset = 0; + mbedtls_ctr_drbg_set_entropy_len(&ctx, MBEDTLS_CTR_DRBG_KEYSIZE); + mbedtls_ctr_drbg_set_nonce_len(&ctx, MBEDTLS_CTR_DRBG_KEYSIZE / 2); + CHK(mbedtls_ctr_drbg_seed(&ctx, + ctr_drbg_self_test_entropy, + (void *) entropy_source_nopr, + pers_nopr, MBEDTLS_CTR_DRBG_KEYSIZE)); + CHK(mbedtls_ctr_drbg_reseed(&ctx, NULL, 0)); + CHK(mbedtls_ctr_drbg_random(&ctx, buf, SELF_TEST_OUTPUT_DISCARD_LENGTH)); + CHK(mbedtls_ctr_drbg_random(&ctx, buf, sizeof(result_nopr))); + CHK(memcmp(buf, result_nopr, sizeof(result_nopr))); + + mbedtls_ctr_drbg_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; +} +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_CTR_DRBG_C */ diff --git a/r5dev/thirdparty/mbedtls/debug.c b/r5dev/thirdparty/mbedtls/debug.c new file mode 100644 index 00000000..12559afe --- /dev/null +++ b/r5dev/thirdparty/mbedtls/debug.c @@ -0,0 +1,389 @@ +/* + * Debugging routines + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_DEBUG_C) + +#include "mbedtls/platform.h" + +#include "mbedtls/debug.h" +#include "mbedtls/error.h" + +#include +#include +#include + +#define DEBUG_BUF_SIZE 512 + +static int debug_threshold = 0; + +void mbedtls_debug_set_threshold(int threshold) +{ + debug_threshold = threshold; +} + +/* + * All calls to f_dbg must be made via this function + */ +static inline void debug_send_line(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *str) +{ + /* + * If in a threaded environment, we need a thread identifier. + * Since there is no portable way to get one, use the address of the ssl + * context instead, as it shouldn't be shared between threads. + */ +#if defined(MBEDTLS_THREADING_C) + char idstr[20 + DEBUG_BUF_SIZE]; /* 0x + 16 nibbles + ': ' */ + mbedtls_snprintf(idstr, sizeof(idstr), "%p: %s", (void *) ssl, str); + ssl->conf->f_dbg(ssl->conf->p_dbg, level, file, line, idstr); +#else + ssl->conf->f_dbg(ssl->conf->p_dbg, level, file, line, str); +#endif +} + +MBEDTLS_PRINTF_ATTRIBUTE(5, 6) +void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *format, ...) +{ + va_list argp; + char str[DEBUG_BUF_SIZE]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (NULL == ssl || + NULL == ssl->conf || + NULL == ssl->conf->f_dbg || + level > debug_threshold) { + return; + } + + va_start(argp, format); + ret = mbedtls_vsnprintf(str, DEBUG_BUF_SIZE, format, argp); + va_end(argp); + + if (ret >= 0 && ret < DEBUG_BUF_SIZE - 1) { + str[ret] = '\n'; + str[ret + 1] = '\0'; + } + + debug_send_line(ssl, level, file, line, str); +} + +void mbedtls_debug_print_ret(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, int ret) +{ + char str[DEBUG_BUF_SIZE]; + + if (NULL == ssl || + NULL == ssl->conf || + NULL == ssl->conf->f_dbg || + level > debug_threshold) { + return; + } + + /* + * With non-blocking I/O and examples that just retry immediately, + * the logs would be quickly flooded with WANT_READ, so ignore that. + * Don't ignore WANT_WRITE however, since it is usually rare. + */ + if (ret == MBEDTLS_ERR_SSL_WANT_READ) { + return; + } + + mbedtls_snprintf(str, sizeof(str), "%s() returned %d (-0x%04x)\n", + text, ret, (unsigned int) -ret); + + debug_send_line(ssl, level, file, line, str); +} + +void mbedtls_debug_print_buf(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, const char *text, + const unsigned char *buf, size_t len) +{ + char str[DEBUG_BUF_SIZE]; + char txt[17]; + size_t i, idx = 0; + + if (NULL == ssl || + NULL == ssl->conf || + NULL == ssl->conf->f_dbg || + level > debug_threshold) { + return; + } + + mbedtls_snprintf(str + idx, sizeof(str) - idx, "dumping '%s' (%u bytes)\n", + text, (unsigned int) len); + + debug_send_line(ssl, level, file, line, str); + + idx = 0; + memset(txt, 0, sizeof(txt)); + for (i = 0; i < len; i++) { + if (i >= 4096) { + break; + } + + if (i % 16 == 0) { + if (i > 0) { + mbedtls_snprintf(str + idx, sizeof(str) - idx, " %s\n", txt); + debug_send_line(ssl, level, file, line, str); + + idx = 0; + memset(txt, 0, sizeof(txt)); + } + + idx += mbedtls_snprintf(str + idx, sizeof(str) - idx, "%04x: ", + (unsigned int) i); + + } + + idx += mbedtls_snprintf(str + idx, sizeof(str) - idx, " %02x", + (unsigned int) buf[i]); + txt[i % 16] = (buf[i] > 31 && buf[i] < 127) ? buf[i] : '.'; + } + + if (len > 0) { + for (/* i = i */; i % 16 != 0; i++) { + idx += mbedtls_snprintf(str + idx, sizeof(str) - idx, " "); + } + + mbedtls_snprintf(str + idx, sizeof(str) - idx, " %s\n", txt); + debug_send_line(ssl, level, file, line, str); + } +} + +#if defined(MBEDTLS_ECP_C) +void mbedtls_debug_print_ecp(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mbedtls_ecp_point *X) +{ + char str[DEBUG_BUF_SIZE]; + + if (NULL == ssl || + NULL == ssl->conf || + NULL == ssl->conf->f_dbg || + level > debug_threshold) { + return; + } + + mbedtls_snprintf(str, sizeof(str), "%s(X)", text); + mbedtls_debug_print_mpi(ssl, level, file, line, str, &X->X); + + mbedtls_snprintf(str, sizeof(str), "%s(Y)", text); + mbedtls_debug_print_mpi(ssl, level, file, line, str, &X->Y); +} +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_BIGNUM_C) +void mbedtls_debug_print_mpi(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mbedtls_mpi *X) +{ + char str[DEBUG_BUF_SIZE]; + size_t bitlen; + size_t idx = 0; + + if (NULL == ssl || + NULL == ssl->conf || + NULL == ssl->conf->f_dbg || + NULL == X || + level > debug_threshold) { + return; + } + + bitlen = mbedtls_mpi_bitlen(X); + + mbedtls_snprintf(str, sizeof(str), "value of '%s' (%u bits) is:\n", + text, (unsigned) bitlen); + debug_send_line(ssl, level, file, line, str); + + if (bitlen == 0) { + str[0] = ' '; str[1] = '0'; str[2] = '0'; + idx = 3; + } else { + int n; + for (n = (int) ((bitlen - 1) / 8); n >= 0; n--) { + size_t limb_offset = n / sizeof(mbedtls_mpi_uint); + size_t offset_in_limb = n % sizeof(mbedtls_mpi_uint); + unsigned char octet = + (X->p[limb_offset] >> (offset_in_limb * 8)) & 0xff; + mbedtls_snprintf(str + idx, sizeof(str) - idx, " %02x", octet); + idx += 3; + /* Wrap lines after 16 octets that each take 3 columns */ + if (idx >= 3 * 16) { + mbedtls_snprintf(str + idx, sizeof(str) - idx, "\n"); + debug_send_line(ssl, level, file, line, str); + idx = 0; + } + } + } + + if (idx != 0) { + mbedtls_snprintf(str + idx, sizeof(str) - idx, "\n"); + debug_send_line(ssl, level, file, line, str); + } +} +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && !defined(MBEDTLS_X509_REMOVE_INFO) +static void debug_print_pk(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mbedtls_pk_context *pk) +{ + size_t i; + mbedtls_pk_debug_item items[MBEDTLS_PK_DEBUG_MAX_ITEMS]; + char name[16]; + + memset(items, 0, sizeof(items)); + + if (mbedtls_pk_debug(pk, items) != 0) { + debug_send_line(ssl, level, file, line, + "invalid PK context\n"); + return; + } + + for (i = 0; i < MBEDTLS_PK_DEBUG_MAX_ITEMS; i++) { + if (items[i].type == MBEDTLS_PK_DEBUG_NONE) { + return; + } + + mbedtls_snprintf(name, sizeof(name), "%s%s", text, items[i].name); + name[sizeof(name) - 1] = '\0'; + + if (items[i].type == MBEDTLS_PK_DEBUG_MPI) { + mbedtls_debug_print_mpi(ssl, level, file, line, name, items[i].value); + } else +#if defined(MBEDTLS_ECP_C) + if (items[i].type == MBEDTLS_PK_DEBUG_ECP) { + mbedtls_debug_print_ecp(ssl, level, file, line, name, items[i].value); + } else +#endif + { debug_send_line(ssl, level, file, line, + "should not happen\n"); } + } +} + +static void debug_print_line_by_line(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, const char *text) +{ + char str[DEBUG_BUF_SIZE]; + const char *start, *cur; + + start = text; + for (cur = text; *cur != '\0'; cur++) { + if (*cur == '\n') { + size_t len = cur - start + 1; + if (len > DEBUG_BUF_SIZE - 1) { + len = DEBUG_BUF_SIZE - 1; + } + + memcpy(str, start, len); + str[len] = '\0'; + + debug_send_line(ssl, level, file, line, str); + + start = cur + 1; + } + } +} + +void mbedtls_debug_print_crt(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mbedtls_x509_crt *crt) +{ + char str[DEBUG_BUF_SIZE]; + int i = 0; + + if (NULL == ssl || + NULL == ssl->conf || + NULL == ssl->conf->f_dbg || + NULL == crt || + level > debug_threshold) { + return; + } + + while (crt != NULL) { + char buf[1024]; + + mbedtls_snprintf(str, sizeof(str), "%s #%d:\n", text, ++i); + debug_send_line(ssl, level, file, line, str); + + mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt); + debug_print_line_by_line(ssl, level, file, line, buf); + + debug_print_pk(ssl, level, file, line, "crt->", &crt->pk); + + crt = crt->next; + } +} +#endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_X509_REMOVE_INFO */ + +#if defined(MBEDTLS_ECDH_C) +static void mbedtls_debug_printf_ecdh_internal(const mbedtls_ssl_context *ssl, + int level, const char *file, + int line, + const mbedtls_ecdh_context *ecdh, + mbedtls_debug_ecdh_attr attr) +{ +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + const mbedtls_ecdh_context *ctx = ecdh; +#else + const mbedtls_ecdh_context_mbed *ctx = &ecdh->ctx.mbed_ecdh; +#endif + + switch (attr) { + case MBEDTLS_DEBUG_ECDH_Q: + mbedtls_debug_print_ecp(ssl, level, file, line, "ECDH: Q", + &ctx->Q); + break; + case MBEDTLS_DEBUG_ECDH_QP: + mbedtls_debug_print_ecp(ssl, level, file, line, "ECDH: Qp", + &ctx->Qp); + break; + case MBEDTLS_DEBUG_ECDH_Z: + mbedtls_debug_print_mpi(ssl, level, file, line, "ECDH: z", + &ctx->z); + break; + default: + break; + } +} + +void mbedtls_debug_printf_ecdh(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const mbedtls_ecdh_context *ecdh, + mbedtls_debug_ecdh_attr attr) +{ +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + mbedtls_debug_printf_ecdh_internal(ssl, level, file, line, ecdh, attr); +#else + switch (ecdh->var) { + default: + mbedtls_debug_printf_ecdh_internal(ssl, level, file, line, ecdh, + attr); + } +#endif +} +#endif /* MBEDTLS_ECDH_C */ + +#endif /* MBEDTLS_DEBUG_C */ diff --git a/r5dev/thirdparty/mbedtls/des.c b/r5dev/thirdparty/mbedtls/des.c new file mode 100644 index 00000000..eaddf282 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/des.c @@ -0,0 +1,1054 @@ +/* + * FIPS-46-3 compliant Triple-DES implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * DES, on which TDES is based, was originally designed by Horst Feistel + * at IBM in 1974, and was adopted as a standard by NIST (formerly NBS). + * + * http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_DES_C) + +#include "mbedtls/des.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_DES_ALT) + +/* + * Expanded DES S-boxes + */ +static const uint32_t SB1[64] = +{ + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 +}; + +static const uint32_t SB2[64] = +{ + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 +}; + +static const uint32_t SB3[64] = +{ + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 +}; + +static const uint32_t SB4[64] = +{ + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 +}; + +static const uint32_t SB5[64] = +{ + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 +}; + +static const uint32_t SB6[64] = +{ + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 +}; + +static const uint32_t SB7[64] = +{ + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 +}; + +static const uint32_t SB8[64] = +{ + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 +}; + +/* + * PC1: left and right halves bit-swap + */ +static const uint32_t LHs[16] = +{ + 0x00000000, 0x00000001, 0x00000100, 0x00000101, + 0x00010000, 0x00010001, 0x00010100, 0x00010101, + 0x01000000, 0x01000001, 0x01000100, 0x01000101, + 0x01010000, 0x01010001, 0x01010100, 0x01010101 +}; + +static const uint32_t RHs[16] = +{ + 0x00000000, 0x01000000, 0x00010000, 0x01010000, + 0x00000100, 0x01000100, 0x00010100, 0x01010100, + 0x00000001, 0x01000001, 0x00010001, 0x01010001, + 0x00000101, 0x01000101, 0x00010101, 0x01010101, +}; + +/* + * Initial Permutation macro + */ +#define DES_IP(X, Y) \ + do \ + { \ + T = (((X) >> 4) ^ (Y)) & 0x0F0F0F0F; (Y) ^= T; (X) ^= (T << 4); \ + T = (((X) >> 16) ^ (Y)) & 0x0000FFFF; (Y) ^= T; (X) ^= (T << 16); \ + T = (((Y) >> 2) ^ (X)) & 0x33333333; (X) ^= T; (Y) ^= (T << 2); \ + T = (((Y) >> 8) ^ (X)) & 0x00FF00FF; (X) ^= T; (Y) ^= (T << 8); \ + (Y) = (((Y) << 1) | ((Y) >> 31)) & 0xFFFFFFFF; \ + T = ((X) ^ (Y)) & 0xAAAAAAAA; (Y) ^= T; (X) ^= T; \ + (X) = (((X) << 1) | ((X) >> 31)) & 0xFFFFFFFF; \ + } while (0) + +/* + * Final Permutation macro + */ +#define DES_FP(X, Y) \ + do \ + { \ + (X) = (((X) << 31) | ((X) >> 1)) & 0xFFFFFFFF; \ + T = ((X) ^ (Y)) & 0xAAAAAAAA; (X) ^= T; (Y) ^= T; \ + (Y) = (((Y) << 31) | ((Y) >> 1)) & 0xFFFFFFFF; \ + T = (((Y) >> 8) ^ (X)) & 0x00FF00FF; (X) ^= T; (Y) ^= (T << 8); \ + T = (((Y) >> 2) ^ (X)) & 0x33333333; (X) ^= T; (Y) ^= (T << 2); \ + T = (((X) >> 16) ^ (Y)) & 0x0000FFFF; (Y) ^= T; (X) ^= (T << 16); \ + T = (((X) >> 4) ^ (Y)) & 0x0F0F0F0F; (Y) ^= T; (X) ^= (T << 4); \ + } while (0) + +/* + * DES round macro + */ +#define DES_ROUND(X, Y) \ + do \ + { \ + T = *SK++ ^ (X); \ + (Y) ^= SB8[(T) & 0x3F] ^ \ + SB6[(T >> 8) & 0x3F] ^ \ + SB4[(T >> 16) & 0x3F] ^ \ + SB2[(T >> 24) & 0x3F]; \ + \ + T = *SK++ ^ (((X) << 28) | ((X) >> 4)); \ + (Y) ^= SB7[(T) & 0x3F] ^ \ + SB5[(T >> 8) & 0x3F] ^ \ + SB3[(T >> 16) & 0x3F] ^ \ + SB1[(T >> 24) & 0x3F]; \ + } while (0) + +#define SWAP(a, b) \ + do \ + { \ + uint32_t t = (a); (a) = (b); (b) = t; t = 0; \ + } while (0) + +void mbedtls_des_init(mbedtls_des_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_des_context)); +} + +void mbedtls_des_free(mbedtls_des_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_des_context)); +} + +void mbedtls_des3_init(mbedtls_des3_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_des3_context)); +} + +void mbedtls_des3_free(mbedtls_des3_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_des3_context)); +} + +static const unsigned char odd_parity_table[128] = { 1, 2, 4, 7, 8, + 11, 13, 14, 16, 19, 21, 22, 25, 26, 28, 31, 32, + 35, 37, 38, 41, 42, 44, + 47, 49, 50, 52, 55, 56, 59, 61, 62, 64, 67, 69, + 70, 73, 74, 76, 79, 81, + 82, 84, 87, 88, 91, 93, 94, 97, 98, 100, 103, + 104, 107, 109, 110, 112, + 115, 117, 118, 121, 122, 124, 127, 128, 131, + 133, 134, 137, 138, 140, + 143, 145, 146, 148, 151, 152, 155, 157, 158, + 161, 162, 164, 167, 168, + 171, 173, 174, 176, 179, 181, 182, 185, 186, + 188, 191, 193, 194, 196, + 199, 200, 203, 205, 206, 208, 211, 213, 214, + 217, 218, 220, 223, 224, + 227, 229, 230, 233, 234, 236, 239, 241, 242, + 244, 247, 248, 251, 253, + 254 }; + +void mbedtls_des_key_set_parity(unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + int i; + + for (i = 0; i < MBEDTLS_DES_KEY_SIZE; i++) { + key[i] = odd_parity_table[key[i] / 2]; + } +} + +/* + * Check the given key's parity, returns 1 on failure, 0 on SUCCESS + */ +int mbedtls_des_key_check_key_parity(const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + int i; + + for (i = 0; i < MBEDTLS_DES_KEY_SIZE; i++) { + if (key[i] != odd_parity_table[key[i] / 2]) { + return 1; + } + } + + return 0; +} + +/* + * Table of weak and semi-weak keys + * + * Source: http://en.wikipedia.org/wiki/Weak_key + * + * Weak: + * Alternating ones + zeros (0x0101010101010101) + * Alternating 'F' + 'E' (0xFEFEFEFEFEFEFEFE) + * '0xE0E0E0E0F1F1F1F1' + * '0x1F1F1F1F0E0E0E0E' + * + * Semi-weak: + * 0x011F011F010E010E and 0x1F011F010E010E01 + * 0x01E001E001F101F1 and 0xE001E001F101F101 + * 0x01FE01FE01FE01FE and 0xFE01FE01FE01FE01 + * 0x1FE01FE00EF10EF1 and 0xE01FE01FF10EF10E + * 0x1FFE1FFE0EFE0EFE and 0xFE1FFE1FFE0EFE0E + * 0xE0FEE0FEF1FEF1FE and 0xFEE0FEE0FEF1FEF1 + * + */ + +#define WEAK_KEY_COUNT 16 + +static const unsigned char weak_key_table[WEAK_KEY_COUNT][MBEDTLS_DES_KEY_SIZE] = +{ + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, + { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE }, + { 0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E }, + { 0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1 }, + + { 0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E }, + { 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01 }, + { 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1 }, + { 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01 }, + { 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE }, + { 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01 }, + { 0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1 }, + { 0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E }, + { 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE }, + { 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E }, + { 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE }, + { 0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1 } +}; + +int mbedtls_des_key_check_weak(const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + int i; + + for (i = 0; i < WEAK_KEY_COUNT; i++) { + if (memcmp(weak_key_table[i], key, MBEDTLS_DES_KEY_SIZE) == 0) { + return 1; + } + } + + return 0; +} + +#if !defined(MBEDTLS_DES_SETKEY_ALT) +void mbedtls_des_setkey(uint32_t SK[32], const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + int i; + uint32_t X, Y, T; + + X = MBEDTLS_GET_UINT32_BE(key, 0); + Y = MBEDTLS_GET_UINT32_BE(key, 4); + + /* + * Permuted Choice 1 + */ + T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4); + T = ((Y) ^ X) & 0x10101010; X ^= T; Y ^= (T); + + X = (LHs[(X) & 0xF] << 3) | (LHs[(X >> 8) & 0xF] << 2) + | (LHs[(X >> 16) & 0xF] << 1) | (LHs[(X >> 24) & 0xF]) + | (LHs[(X >> 5) & 0xF] << 7) | (LHs[(X >> 13) & 0xF] << 6) + | (LHs[(X >> 21) & 0xF] << 5) | (LHs[(X >> 29) & 0xF] << 4); + + Y = (RHs[(Y >> 1) & 0xF] << 3) | (RHs[(Y >> 9) & 0xF] << 2) + | (RHs[(Y >> 17) & 0xF] << 1) | (RHs[(Y >> 25) & 0xF]) + | (RHs[(Y >> 4) & 0xF] << 7) | (RHs[(Y >> 12) & 0xF] << 6) + | (RHs[(Y >> 20) & 0xF] << 5) | (RHs[(Y >> 28) & 0xF] << 4); + + X &= 0x0FFFFFFF; + Y &= 0x0FFFFFFF; + + /* + * calculate subkeys + */ + for (i = 0; i < 16; i++) { + if (i < 2 || i == 8 || i == 15) { + X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF; + Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF; + } else { + X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF; + Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF; + } + + *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000) + | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000) + | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000) + | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000) + | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000) + | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000) + | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400) + | ((Y >> 14) & 0x00000200) | ((Y) & 0x00000100) + | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010) + | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004) + | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001); + + *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000) + | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000) + | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000) + | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000) + | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000) + | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000) + | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000) + | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400) + | ((Y) & 0x00000200) | ((Y << 7) & 0x00000100) + | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011) + | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002); + } +} +#endif /* !MBEDTLS_DES_SETKEY_ALT */ + +/* + * DES key schedule (56-bit, encryption) + */ +int mbedtls_des_setkey_enc(mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + mbedtls_des_setkey(ctx->sk, key); + + return 0; +} + +/* + * DES key schedule (56-bit, decryption) + */ +int mbedtls_des_setkey_dec(mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]) +{ + int i; + + mbedtls_des_setkey(ctx->sk, key); + + for (i = 0; i < 16; i += 2) { + SWAP(ctx->sk[i], ctx->sk[30 - i]); + SWAP(ctx->sk[i + 1], ctx->sk[31 - i]); + } + + return 0; +} + +static void des3_set2key(uint32_t esk[96], + uint32_t dsk[96], + const unsigned char key[MBEDTLS_DES_KEY_SIZE*2]) +{ + int i; + + mbedtls_des_setkey(esk, key); + mbedtls_des_setkey(dsk + 32, key + 8); + + for (i = 0; i < 32; i += 2) { + dsk[i] = esk[30 - i]; + dsk[i + 1] = esk[31 - i]; + + esk[i + 32] = dsk[62 - i]; + esk[i + 33] = dsk[63 - i]; + + esk[i + 64] = esk[i]; + esk[i + 65] = esk[i + 1]; + + dsk[i + 64] = dsk[i]; + dsk[i + 65] = dsk[i + 1]; + } +} + +/* + * Triple-DES key schedule (112-bit, encryption) + */ +int mbedtls_des3_set2key_enc(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2]) +{ + uint32_t sk[96]; + + des3_set2key(ctx->sk, sk, key); + mbedtls_platform_zeroize(sk, sizeof(sk)); + + return 0; +} + +/* + * Triple-DES key schedule (112-bit, decryption) + */ +int mbedtls_des3_set2key_dec(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2]) +{ + uint32_t sk[96]; + + des3_set2key(sk, ctx->sk, key); + mbedtls_platform_zeroize(sk, sizeof(sk)); + + return 0; +} + +static void des3_set3key(uint32_t esk[96], + uint32_t dsk[96], + const unsigned char key[24]) +{ + int i; + + mbedtls_des_setkey(esk, key); + mbedtls_des_setkey(dsk + 32, key + 8); + mbedtls_des_setkey(esk + 64, key + 16); + + for (i = 0; i < 32; i += 2) { + dsk[i] = esk[94 - i]; + dsk[i + 1] = esk[95 - i]; + + esk[i + 32] = dsk[62 - i]; + esk[i + 33] = dsk[63 - i]; + + dsk[i + 64] = esk[30 - i]; + dsk[i + 65] = esk[31 - i]; + } +} + +/* + * Triple-DES key schedule (168-bit, encryption) + */ +int mbedtls_des3_set3key_enc(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]) +{ + uint32_t sk[96]; + + des3_set3key(ctx->sk, sk, key); + mbedtls_platform_zeroize(sk, sizeof(sk)); + + return 0; +} + +/* + * Triple-DES key schedule (168-bit, decryption) + */ +int mbedtls_des3_set3key_dec(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]) +{ + uint32_t sk[96]; + + des3_set3key(sk, ctx->sk, key); + mbedtls_platform_zeroize(sk, sizeof(sk)); + + return 0; +} + +/* + * DES-ECB block encryption/decryption + */ +#if !defined(MBEDTLS_DES_CRYPT_ECB_ALT) +int mbedtls_des_crypt_ecb(mbedtls_des_context *ctx, + const unsigned char input[8], + unsigned char output[8]) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + X = MBEDTLS_GET_UINT32_BE(input, 0); + Y = MBEDTLS_GET_UINT32_BE(input, 4); + + DES_IP(X, Y); + + for (i = 0; i < 8; i++) { + DES_ROUND(Y, X); + DES_ROUND(X, Y); + } + + DES_FP(Y, X); + + MBEDTLS_PUT_UINT32_BE(Y, output, 0); + MBEDTLS_PUT_UINT32_BE(X, output, 4); + + return 0; +} +#endif /* !MBEDTLS_DES_CRYPT_ECB_ALT */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/* + * DES-CBC buffer encryption/decryption + */ +int mbedtls_des_crypt_cbc(mbedtls_des_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char temp[8]; + + if (length % 8) { + return MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_DES_ENCRYPT) { + while (length > 0) { + mbedtls_xor(output, input, iv, 8); + + ret = mbedtls_des_crypt_ecb(ctx, output, output); + if (ret != 0) { + goto exit; + } + memcpy(iv, output, 8); + + input += 8; + output += 8; + length -= 8; + } + } else { /* MBEDTLS_DES_DECRYPT */ + while (length > 0) { + memcpy(temp, input, 8); + ret = mbedtls_des_crypt_ecb(ctx, input, output); + if (ret != 0) { + goto exit; + } + + mbedtls_xor(output, output, iv, 8); + + memcpy(iv, temp, 8); + + input += 8; + output += 8; + length -= 8; + } + } + ret = 0; + +exit: + return ret; +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +/* + * 3DES-ECB block encryption/decryption + */ +#if !defined(MBEDTLS_DES3_CRYPT_ECB_ALT) +int mbedtls_des3_crypt_ecb(mbedtls_des3_context *ctx, + const unsigned char input[8], + unsigned char output[8]) +{ + int i; + uint32_t X, Y, T, *SK; + + SK = ctx->sk; + + X = MBEDTLS_GET_UINT32_BE(input, 0); + Y = MBEDTLS_GET_UINT32_BE(input, 4); + + DES_IP(X, Y); + + for (i = 0; i < 8; i++) { + DES_ROUND(Y, X); + DES_ROUND(X, Y); + } + + for (i = 0; i < 8; i++) { + DES_ROUND(X, Y); + DES_ROUND(Y, X); + } + + for (i = 0; i < 8; i++) { + DES_ROUND(Y, X); + DES_ROUND(X, Y); + } + + DES_FP(Y, X); + + MBEDTLS_PUT_UINT32_BE(Y, output, 0); + MBEDTLS_PUT_UINT32_BE(X, output, 4); + + return 0; +} +#endif /* !MBEDTLS_DES3_CRYPT_ECB_ALT */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/* + * 3DES-CBC buffer encryption/decryption + */ +int mbedtls_des3_crypt_cbc(mbedtls_des3_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char temp[8]; + + if (length % 8) { + return MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH; + } + + if (mode == MBEDTLS_DES_ENCRYPT) { + while (length > 0) { + mbedtls_xor(output, input, iv, 8); + + ret = mbedtls_des3_crypt_ecb(ctx, output, output); + if (ret != 0) { + goto exit; + } + memcpy(iv, output, 8); + + input += 8; + output += 8; + length -= 8; + } + } else { /* MBEDTLS_DES_DECRYPT */ + while (length > 0) { + memcpy(temp, input, 8); + ret = mbedtls_des3_crypt_ecb(ctx, input, output); + if (ret != 0) { + goto exit; + } + + mbedtls_xor(output, output, iv, 8); + + memcpy(iv, temp, 8); + + input += 8; + output += 8; + length -= 8; + } + } + ret = 0; + +exit: + return ret; +} +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#endif /* !MBEDTLS_DES_ALT */ + +#if defined(MBEDTLS_SELF_TEST) +/* + * DES and 3DES test vectors from: + * + * http://csrc.nist.gov/groups/STM/cavp/documents/des/tripledes-vectors.zip + */ +static const unsigned char des3_test_keys[24] = +{ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, + 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 +}; + +static const unsigned char des3_test_buf[8] = +{ + 0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74 +}; + +static const unsigned char des3_test_ecb_dec[3][8] = +{ + { 0x37, 0x2B, 0x98, 0xBF, 0x52, 0x65, 0xB0, 0x59 }, + { 0xC2, 0x10, 0x19, 0x9C, 0x38, 0x5A, 0x65, 0xA1 }, + { 0xA2, 0x70, 0x56, 0x68, 0x69, 0xE5, 0x15, 0x1D } +}; + +static const unsigned char des3_test_ecb_enc[3][8] = +{ + { 0x1C, 0xD5, 0x97, 0xEA, 0x84, 0x26, 0x73, 0xFB }, + { 0xB3, 0x92, 0x4D, 0xF3, 0xC5, 0xB5, 0x42, 0x93 }, + { 0xDA, 0x37, 0x64, 0x41, 0xBA, 0x6F, 0x62, 0x6F } +}; + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +static const unsigned char des3_test_iv[8] = +{ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, +}; + +static const unsigned char des3_test_cbc_dec[3][8] = +{ + { 0x58, 0xD9, 0x48, 0xEF, 0x85, 0x14, 0x65, 0x9A }, + { 0x5F, 0xC8, 0x78, 0xD4, 0xD7, 0x92, 0xD9, 0x54 }, + { 0x25, 0xF9, 0x75, 0x85, 0xA8, 0x1E, 0x48, 0xBF } +}; + +static const unsigned char des3_test_cbc_enc[3][8] = +{ + { 0x91, 0x1C, 0x6D, 0xCF, 0x48, 0xA7, 0xC3, 0x4D }, + { 0x60, 0x1A, 0x76, 0x8F, 0xA1, 0xF9, 0x66, 0xF1 }, + { 0xA1, 0x50, 0x0F, 0x99, 0xB2, 0xCD, 0x64, 0x76 } +}; +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +/* + * Checkup routine + */ +int mbedtls_des_self_test(int verbose) +{ + int i, j, u, v, ret = 0; + mbedtls_des_context ctx; + mbedtls_des3_context ctx3; + unsigned char buf[8]; +#if defined(MBEDTLS_CIPHER_MODE_CBC) + unsigned char prv[8]; + unsigned char iv[8]; +#endif + + mbedtls_des_init(&ctx); + mbedtls_des3_init(&ctx3); + /* + * ECB mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + v = i & 1; + + if (verbose != 0) { + mbedtls_printf(" DES%c-ECB-%3d (%s): ", + (u == 0) ? ' ' : '3', 56 + u * 56, + (v == MBEDTLS_DES_DECRYPT) ? "dec" : "enc"); + } + + memcpy(buf, des3_test_buf, 8); + + switch (i) { + case 0: + ret = mbedtls_des_setkey_dec(&ctx, des3_test_keys); + break; + + case 1: + ret = mbedtls_des_setkey_enc(&ctx, des3_test_keys); + break; + + case 2: + ret = mbedtls_des3_set2key_dec(&ctx3, des3_test_keys); + break; + + case 3: + ret = mbedtls_des3_set2key_enc(&ctx3, des3_test_keys); + break; + + case 4: + ret = mbedtls_des3_set3key_dec(&ctx3, des3_test_keys); + break; + + case 5: + ret = mbedtls_des3_set3key_enc(&ctx3, des3_test_keys); + break; + + default: + return 1; + } + if (ret != 0) { + goto exit; + } + + for (j = 0; j < 100; j++) { + if (u == 0) { + ret = mbedtls_des_crypt_ecb(&ctx, buf, buf); + } else { + ret = mbedtls_des3_crypt_ecb(&ctx3, buf, buf); + } + if (ret != 0) { + goto exit; + } + } + + if ((v == MBEDTLS_DES_DECRYPT && + memcmp(buf, des3_test_ecb_dec[u], 8) != 0) || + (v != MBEDTLS_DES_DECRYPT && + memcmp(buf, des3_test_ecb_enc[u], 8) != 0)) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + /* + * CBC mode + */ + for (i = 0; i < 6; i++) { + u = i >> 1; + v = i & 1; + + if (verbose != 0) { + mbedtls_printf(" DES%c-CBC-%3d (%s): ", + (u == 0) ? ' ' : '3', 56 + u * 56, + (v == MBEDTLS_DES_DECRYPT) ? "dec" : "enc"); + } + + memcpy(iv, des3_test_iv, 8); + memcpy(prv, des3_test_iv, 8); + memcpy(buf, des3_test_buf, 8); + + switch (i) { + case 0: + ret = mbedtls_des_setkey_dec(&ctx, des3_test_keys); + break; + + case 1: + ret = mbedtls_des_setkey_enc(&ctx, des3_test_keys); + break; + + case 2: + ret = mbedtls_des3_set2key_dec(&ctx3, des3_test_keys); + break; + + case 3: + ret = mbedtls_des3_set2key_enc(&ctx3, des3_test_keys); + break; + + case 4: + ret = mbedtls_des3_set3key_dec(&ctx3, des3_test_keys); + break; + + case 5: + ret = mbedtls_des3_set3key_enc(&ctx3, des3_test_keys); + break; + + default: + return 1; + } + if (ret != 0) { + goto exit; + } + + if (v == MBEDTLS_DES_DECRYPT) { + for (j = 0; j < 100; j++) { + if (u == 0) { + ret = mbedtls_des_crypt_cbc(&ctx, v, 8, iv, buf, buf); + } else { + ret = mbedtls_des3_crypt_cbc(&ctx3, v, 8, iv, buf, buf); + } + if (ret != 0) { + goto exit; + } + } + } else { + for (j = 0; j < 100; j++) { + unsigned char tmp[8]; + + if (u == 0) { + ret = mbedtls_des_crypt_cbc(&ctx, v, 8, iv, buf, buf); + } else { + ret = mbedtls_des3_crypt_cbc(&ctx3, v, 8, iv, buf, buf); + } + if (ret != 0) { + goto exit; + } + + memcpy(tmp, prv, 8); + memcpy(prv, buf, 8); + memcpy(buf, tmp, 8); + } + + memcpy(buf, prv, 8); + } + + if ((v == MBEDTLS_DES_DECRYPT && + memcmp(buf, des3_test_cbc_dec[u], 8) != 0) || + (v != MBEDTLS_DES_DECRYPT && + memcmp(buf, des3_test_cbc_enc[u], 8) != 0)) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + + if (verbose != 0) { + mbedtls_printf("\n"); + } + +exit: + mbedtls_des_free(&ctx); + mbedtls_des3_free(&ctx3); + + if (ret != 0) { + ret = 1; + } + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_DES_C */ diff --git a/r5dev/thirdparty/mbedtls/dhm.c b/r5dev/thirdparty/mbedtls/dhm.c new file mode 100644 index 00000000..94137a26 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/dhm.c @@ -0,0 +1,726 @@ +/* + * Diffie-Hellman-Merkle key exchange + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The following sources were referenced in the design of this implementation + * of the Diffie-Hellman-Merkle algorithm: + * + * [1] Handbook of Applied Cryptography - 1997, Chapter 12 + * Menezes, van Oorschot and Vanstone + * + */ + +#include "common.h" + +#if defined(MBEDTLS_DHM_C) + +#include "mbedtls/dhm.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_PEM_PARSE_C) +#include "mbedtls/pem.h" +#endif + +#if defined(MBEDTLS_ASN1_PARSE_C) +#include "mbedtls/asn1.h" +#endif + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_DHM_ALT) + +/* + * helper to validate the mbedtls_mpi size and import it + */ +static int dhm_read_bignum(mbedtls_mpi *X, + unsigned char **p, + const unsigned char *end) +{ + int ret, n; + + if (end - *p < 2) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + + n = ((*p)[0] << 8) | (*p)[1]; + (*p) += 2; + + if ((int) (end - *p) < n) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + + if ((ret = mbedtls_mpi_read_binary(X, *p, n)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_READ_PARAMS_FAILED, ret); + } + + (*p) += n; + + return 0; +} + +/* + * Verify sanity of parameter with regards to P + * + * Parameter should be: 2 <= public_param <= P - 2 + * + * This means that we need to return an error if + * public_param < 2 or public_param > P-2 + * + * For more information on the attack, see: + * http://www.cl.cam.ac.uk/~rja14/Papers/psandqs.pdf + * http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2005-2643 + */ +static int dhm_check_range(const mbedtls_mpi *param, const mbedtls_mpi *P) +{ + mbedtls_mpi U; + int ret = 0; + + mbedtls_mpi_init(&U); + + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&U, P, 2)); + + if (mbedtls_mpi_cmp_int(param, 2) < 0 || + mbedtls_mpi_cmp_mpi(param, &U) > 0) { + ret = MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + +cleanup: + mbedtls_mpi_free(&U); + return ret; +} + +void mbedtls_dhm_init(mbedtls_dhm_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_dhm_context)); +} + +size_t mbedtls_dhm_get_bitlen(const mbedtls_dhm_context *ctx) +{ + return mbedtls_mpi_bitlen(&ctx->P); +} + +size_t mbedtls_dhm_get_len(const mbedtls_dhm_context *ctx) +{ + return mbedtls_mpi_size(&ctx->P); +} + +int mbedtls_dhm_get_value(const mbedtls_dhm_context *ctx, + mbedtls_dhm_parameter param, + mbedtls_mpi *dest) +{ + const mbedtls_mpi *src = NULL; + switch (param) { + case MBEDTLS_DHM_PARAM_P: + src = &ctx->P; + break; + case MBEDTLS_DHM_PARAM_G: + src = &ctx->G; + break; + case MBEDTLS_DHM_PARAM_X: + src = &ctx->X; + break; + case MBEDTLS_DHM_PARAM_GX: + src = &ctx->GX; + break; + case MBEDTLS_DHM_PARAM_GY: + src = &ctx->GY; + break; + case MBEDTLS_DHM_PARAM_K: + src = &ctx->K; + break; + default: + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + return mbedtls_mpi_copy(dest, src); +} + +/* + * Parse the ServerKeyExchange parameters + */ +int mbedtls_dhm_read_params(mbedtls_dhm_context *ctx, + unsigned char **p, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = dhm_read_bignum(&ctx->P, p, end)) != 0 || + (ret = dhm_read_bignum(&ctx->G, p, end)) != 0 || + (ret = dhm_read_bignum(&ctx->GY, p, end)) != 0) { + return ret; + } + + if ((ret = dhm_check_range(&ctx->GY, &ctx->P)) != 0) { + return ret; + } + + return 0; +} + +/* + * Pick a random R in the range [2, M-2] for blinding or key generation. + */ +static int dhm_random_below(mbedtls_mpi *R, const mbedtls_mpi *M, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret; + + MBEDTLS_MPI_CHK(mbedtls_mpi_random(R, 3, M, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(R, R, 1)); + +cleanup: + return ret; +} + +static int dhm_make_common(mbedtls_dhm_context *ctx, int x_size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = 0; + + if (mbedtls_mpi_cmp_int(&ctx->P, 0) == 0) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + if (x_size < 0) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + + if ((unsigned) x_size < mbedtls_mpi_size(&ctx->P)) { + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&ctx->X, x_size, f_rng, p_rng)); + } else { + /* Generate X as large as possible ( <= P - 2 ) */ + ret = dhm_random_below(&ctx->X, &ctx->P, f_rng, p_rng); + if (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { + return MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED; + } + if (ret != 0) { + return ret; + } + } + + /* + * Calculate GX = G^X mod P + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->GX, &ctx->G, &ctx->X, + &ctx->P, &ctx->RP)); + + if ((ret = dhm_check_range(&ctx->GX, &ctx->P)) != 0) { + return ret; + } + +cleanup: + return ret; +} + +/* + * Setup and write the ServerKeyExchange parameters + */ +int mbedtls_dhm_make_params(mbedtls_dhm_context *ctx, int x_size, + unsigned char *output, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret; + size_t n1, n2, n3; + unsigned char *p; + + ret = dhm_make_common(ctx, x_size, f_rng, p_rng); + if (ret != 0) { + goto cleanup; + } + + /* + * Export P, G, GX. RFC 5246 §4.4 states that "leading zero octets are + * not required". We omit leading zeros for compactness. + */ +#define DHM_MPI_EXPORT(X, n) \ + do { \ + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary((X), \ + p + 2, \ + (n))); \ + *p++ = MBEDTLS_BYTE_1(n); \ + *p++ = MBEDTLS_BYTE_0(n); \ + p += (n); \ + } while (0) + + n1 = mbedtls_mpi_size(&ctx->P); + n2 = mbedtls_mpi_size(&ctx->G); + n3 = mbedtls_mpi_size(&ctx->GX); + + p = output; + DHM_MPI_EXPORT(&ctx->P, n1); + DHM_MPI_EXPORT(&ctx->G, n2); + DHM_MPI_EXPORT(&ctx->GX, n3); + + *olen = p - output; + +cleanup: + if (ret != 0 && ret > -128) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED, ret); + } + return ret; +} + +/* + * Set prime modulus and generator + */ +int mbedtls_dhm_set_group(mbedtls_dhm_context *ctx, + const mbedtls_mpi *P, + const mbedtls_mpi *G) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_mpi_copy(&ctx->P, P)) != 0 || + (ret = mbedtls_mpi_copy(&ctx->G, G)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_SET_GROUP_FAILED, ret); + } + + return 0; +} + +/* + * Import the peer's public value G^Y + */ +int mbedtls_dhm_read_public(mbedtls_dhm_context *ctx, + const unsigned char *input, size_t ilen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ilen < 1 || ilen > mbedtls_dhm_get_len(ctx)) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + + if ((ret = mbedtls_mpi_read_binary(&ctx->GY, input, ilen)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED, ret); + } + + return 0; +} + +/* + * Create own private value X and export G^X + */ +int mbedtls_dhm_make_public(mbedtls_dhm_context *ctx, int x_size, + unsigned char *output, size_t olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret; + + if (olen < 1 || olen > mbedtls_dhm_get_len(ctx)) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + + ret = dhm_make_common(ctx, x_size, f_rng, p_rng); + if (ret == MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED) { + return MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED; + } + if (ret != 0) { + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->GX, output, olen)); + +cleanup: + if (ret != 0 && ret > -128) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED, ret); + } + return ret; +} + + +/* + * Use the blinding method and optimisation suggested in section 10 of: + * KOCHER, Paul C. Timing attacks on implementations of Diffie-Hellman, RSA, + * DSS, and other systems. In : Advances in Cryptology-CRYPTO'96. Springer + * Berlin Heidelberg, 1996. p. 104-113. + */ +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, + * but remember it to use blinding next time. + */ + if (mbedtls_mpi_cmp_mpi(&ctx->X, &ctx->pX) != 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&ctx->pX, &ctx->X)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&ctx->Vi, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&ctx->Vf, 1)); + + return 0; + } + + /* + * Ok, we need blinding. Can we re-use existing values? + * If yes, just update them by squaring them. + */ + if (mbedtls_mpi_cmp_int(&ctx->Vi, 1) != 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vi, &ctx->Vi, &ctx->Vi)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vi, &ctx->Vi, &ctx->P)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vf, &ctx->Vf)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->P)); + + return 0; + } + + /* + * We need to generate blinding values from scratch + */ + + /* 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)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->Vf, &ctx->Vf, &ctx->X, &ctx->P, &ctx->RP)); + +cleanup: + mbedtls_mpi_free(&R); + + return ret; +} + +/* + * Derive and export the shared secret (G^Y)^X mod P + */ +int mbedtls_dhm_calc_secret(mbedtls_dhm_context *ctx, + unsigned char *output, size_t output_size, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi GYb; + + if (f_rng == NULL) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + + if (output_size < mbedtls_dhm_get_len(ctx)) { + return MBEDTLS_ERR_DHM_BAD_INPUT_DATA; + } + + if ((ret = dhm_check_range(&ctx->GY, &ctx->P)) != 0) { + return ret; + } + + mbedtls_mpi_init(&GYb); + + /* Blind peer's value */ + MBEDTLS_MPI_CHK(dhm_update_blinding(ctx, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&GYb, &ctx->GY, &ctx->Vi)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&GYb, &GYb, &ctx->P)); + + /* Do modular exponentiation */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->K, &GYb, &ctx->X, + &ctx->P, &ctx->RP)); + + /* Unblind secret value */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->K, &ctx->K, &ctx->Vf)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->K, &ctx->K, &ctx->P)); + + /* Output the secret without any leading zero byte. This is mandatory + * for TLS per RFC 5246 §8.1.2. */ + *olen = mbedtls_mpi_size(&ctx->K); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->K, output, *olen)); + +cleanup: + mbedtls_mpi_free(&GYb); + + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_CALC_SECRET_FAILED, ret); + } + + return 0; +} + +/* + * Free the components of a DHM key + */ +void mbedtls_dhm_free(mbedtls_dhm_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_mpi_free(&ctx->pX); + mbedtls_mpi_free(&ctx->Vf); + mbedtls_mpi_free(&ctx->Vi); + mbedtls_mpi_free(&ctx->RP); + mbedtls_mpi_free(&ctx->K); + mbedtls_mpi_free(&ctx->GY); + mbedtls_mpi_free(&ctx->GX); + mbedtls_mpi_free(&ctx->X); + mbedtls_mpi_free(&ctx->G); + mbedtls_mpi_free(&ctx->P); + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_dhm_context)); +} + +#if defined(MBEDTLS_ASN1_PARSE_C) +/* + * Parse DHM parameters + */ +int mbedtls_dhm_parse_dhm(mbedtls_dhm_context *dhm, const unsigned char *dhmin, + size_t dhminlen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + unsigned char *p, *end; +#if defined(MBEDTLS_PEM_PARSE_C) + mbedtls_pem_context pem; +#endif /* MBEDTLS_PEM_PARSE_C */ + +#if defined(MBEDTLS_PEM_PARSE_C) + mbedtls_pem_init(&pem); + + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (dhminlen == 0 || dhmin[dhminlen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN DH PARAMETERS-----", + "-----END DH PARAMETERS-----", + dhmin, NULL, 0, &dhminlen); + } + + if (ret == 0) { + /* + * Was PEM encoded + */ + dhminlen = pem.buflen; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + goto exit; + } + + p = (ret == 0) ? pem.buf : (unsigned char *) dhmin; +#else + p = (unsigned char *) dhmin; +#endif /* MBEDTLS_PEM_PARSE_C */ + end = p + dhminlen; + + /* + * DHParams ::= SEQUENCE { + * prime INTEGER, -- P + * generator INTEGER, -- g + * privateValueLength INTEGER OPTIONAL + * } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, ret); + goto exit; + } + + end = p + len; + + if ((ret = mbedtls_asn1_get_mpi(&p, end, &dhm->P)) != 0 || + (ret = mbedtls_asn1_get_mpi(&p, end, &dhm->G)) != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, ret); + goto exit; + } + + if (p != end) { + /* This might be the optional privateValueLength. + * If so, we can cleanly discard it */ + mbedtls_mpi rec; + mbedtls_mpi_init(&rec); + ret = mbedtls_asn1_get_mpi(&p, end, &rec); + mbedtls_mpi_free(&rec); + if (ret != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, ret); + goto exit; + } + if (p != end) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_DHM_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + goto exit; + } + } + + ret = 0; + +exit: +#if defined(MBEDTLS_PEM_PARSE_C) + mbedtls_pem_free(&pem); +#endif + if (ret != 0) { + mbedtls_dhm_free(dhm); + } + + return ret; +} + +#if defined(MBEDTLS_FS_IO) +/* + * Load all data from a file into a given buffer. + * + * The file is expected to contain either PEM or DER encoded data. + * A terminating null byte is always appended. It is included in the announced + * length only if the data looks like it is PEM encoded. + */ +static int load_file(const char *path, unsigned char **buf, size_t *n) +{ + FILE *f; + long size; + + if ((f = fopen(path, "rb")) == NULL) { + return MBEDTLS_ERR_DHM_FILE_IO_ERROR; + } + /* The data loaded here is public, so don't bother disabling buffering. */ + + fseek(f, 0, SEEK_END); + if ((size = ftell(f)) == -1) { + fclose(f); + return MBEDTLS_ERR_DHM_FILE_IO_ERROR; + } + fseek(f, 0, SEEK_SET); + + *n = (size_t) size; + + if (*n + 1 == 0 || + (*buf = mbedtls_calloc(1, *n + 1)) == NULL) { + fclose(f); + return MBEDTLS_ERR_DHM_ALLOC_FAILED; + } + + if (fread(*buf, 1, *n, f) != *n) { + fclose(f); + + mbedtls_platform_zeroize(*buf, *n + 1); + mbedtls_free(*buf); + + return MBEDTLS_ERR_DHM_FILE_IO_ERROR; + } + + fclose(f); + + (*buf)[*n] = '\0'; + + if (strstr((const char *) *buf, "-----BEGIN ") != NULL) { + ++*n; + } + + return 0; +} + +/* + * Load and parse DHM parameters + */ +int mbedtls_dhm_parse_dhmfile(mbedtls_dhm_context *dhm, const char *path) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + unsigned char *buf; + + if ((ret = load_file(path, &buf, &n)) != 0) { + return ret; + } + + ret = mbedtls_dhm_parse_dhm(dhm, buf, n); + + mbedtls_platform_zeroize(buf, n); + mbedtls_free(buf); + + return ret; +} +#endif /* MBEDTLS_FS_IO */ +#endif /* MBEDTLS_ASN1_PARSE_C */ +#endif /* MBEDTLS_DHM_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +#if defined(MBEDTLS_PEM_PARSE_C) +static const char mbedtls_test_dhm_params[] = + "-----BEGIN DH PARAMETERS-----\r\n" + "MIGHAoGBAJ419DBEOgmQTzo5qXl5fQcN9TN455wkOL7052HzxxRVMyhYmwQcgJvh\r\n" + "1sa18fyfR9OiVEMYglOpkqVoGLN7qd5aQNNi5W7/C+VBdHTBJcGZJyyP5B3qcz32\r\n" + "9mLJKudlVudV0Qxk5qUJaPZ/xupz0NyoVpviuiBOI1gNi8ovSXWzAgEC\r\n" + "-----END DH PARAMETERS-----\r\n"; +#else /* MBEDTLS_PEM_PARSE_C */ +static const char mbedtls_test_dhm_params[] = { + 0x30, 0x81, 0x87, 0x02, 0x81, 0x81, 0x00, 0x9e, 0x35, 0xf4, 0x30, 0x44, + 0x3a, 0x09, 0x90, 0x4f, 0x3a, 0x39, 0xa9, 0x79, 0x79, 0x7d, 0x07, 0x0d, + 0xf5, 0x33, 0x78, 0xe7, 0x9c, 0x24, 0x38, 0xbe, 0xf4, 0xe7, 0x61, 0xf3, + 0xc7, 0x14, 0x55, 0x33, 0x28, 0x58, 0x9b, 0x04, 0x1c, 0x80, 0x9b, 0xe1, + 0xd6, 0xc6, 0xb5, 0xf1, 0xfc, 0x9f, 0x47, 0xd3, 0xa2, 0x54, 0x43, 0x18, + 0x82, 0x53, 0xa9, 0x92, 0xa5, 0x68, 0x18, 0xb3, 0x7b, 0xa9, 0xde, 0x5a, + 0x40, 0xd3, 0x62, 0xe5, 0x6e, 0xff, 0x0b, 0xe5, 0x41, 0x74, 0x74, 0xc1, + 0x25, 0xc1, 0x99, 0x27, 0x2c, 0x8f, 0xe4, 0x1d, 0xea, 0x73, 0x3d, 0xf6, + 0xf6, 0x62, 0xc9, 0x2a, 0xe7, 0x65, 0x56, 0xe7, 0x55, 0xd1, 0x0c, 0x64, + 0xe6, 0xa5, 0x09, 0x68, 0xf6, 0x7f, 0xc6, 0xea, 0x73, 0xd0, 0xdc, 0xa8, + 0x56, 0x9b, 0xe2, 0xba, 0x20, 0x4e, 0x23, 0x58, 0x0d, 0x8b, 0xca, 0x2f, + 0x49, 0x75, 0xb3, 0x02, 0x01, 0x02 +}; +#endif /* MBEDTLS_PEM_PARSE_C */ + +static const size_t mbedtls_test_dhm_params_len = sizeof(mbedtls_test_dhm_params); + +/* + * Checkup routine + */ +int mbedtls_dhm_self_test(int verbose) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_dhm_context dhm; + + mbedtls_dhm_init(&dhm); + + if (verbose != 0) { + mbedtls_printf(" DHM parameter load: "); + } + + if ((ret = mbedtls_dhm_parse_dhm(&dhm, + (const unsigned char *) mbedtls_test_dhm_params, + mbedtls_test_dhm_params_len)) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n\n"); + } + +exit: + mbedtls_dhm_free(&dhm); + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_DHM_C */ diff --git a/r5dev/thirdparty/mbedtls/ecdh.c b/r5dev/thirdparty/mbedtls/ecdh.c new file mode 100644 index 00000000..b529af59 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ecdh.c @@ -0,0 +1,697 @@ +/* + * Elliptic curve Diffie-Hellman + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * References: + * + * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + * RFC 4492 + */ + +#include "common.h" + +#if defined(MBEDTLS_ECDH_C) + +#include "mbedtls/ecdh.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) +typedef mbedtls_ecdh_context mbedtls_ecdh_context_mbed; +#endif + +static mbedtls_ecp_group_id mbedtls_ecdh_grp_id( + const mbedtls_ecdh_context *ctx) +{ +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ctx->grp.id; +#else + return ctx->grp_id; +#endif +} + +int mbedtls_ecdh_can_do(mbedtls_ecp_group_id gid) +{ + /* At this time, all groups support ECDH. */ + (void) gid; + return 1; +} + +#if !defined(MBEDTLS_ECDH_GEN_PUBLIC_ALT) +/* + * Generate public key (restartable version) + * + * Note: this internal function relies on its caller preserving the value of + * the output parameter 'd' across continuation calls. This would not be + * acceptable for a public function but is OK here as we control call sites. + */ +static int ecdh_gen_public_restartable(mbedtls_ecp_group *grp, + mbedtls_mpi *d, mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + int restarting = 0; +#if defined(MBEDTLS_ECP_RESTARTABLE) + restarting = (rs_ctx != NULL && rs_ctx->rsm != NULL); +#endif + /* If multiplication is in progress, we already generated a privkey */ + if (!restarting) { + MBEDTLS_MPI_CHK(mbedtls_ecp_gen_privkey(grp, d, f_rng, p_rng)); + } + + MBEDTLS_MPI_CHK(mbedtls_ecp_mul_restartable(grp, Q, d, &grp->G, + f_rng, p_rng, rs_ctx)); + +cleanup: + return ret; +} + +/* + * Generate public key + */ +int mbedtls_ecdh_gen_public(mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + return ecdh_gen_public_restartable(grp, d, Q, f_rng, p_rng, NULL); +} +#endif /* !MBEDTLS_ECDH_GEN_PUBLIC_ALT */ + +#if !defined(MBEDTLS_ECDH_COMPUTE_SHARED_ALT) +/* + * Compute shared secret (SEC1 3.3.1) + */ +static int ecdh_compute_shared_restartable(mbedtls_ecp_group *grp, + mbedtls_mpi *z, + const mbedtls_ecp_point *Q, const mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point P; + + mbedtls_ecp_point_init(&P); + + MBEDTLS_MPI_CHK(mbedtls_ecp_mul_restartable(grp, &P, d, Q, + f_rng, p_rng, rs_ctx)); + + if (mbedtls_ecp_is_zero(&P)) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(z, &P.X)); + +cleanup: + mbedtls_ecp_point_free(&P); + + return ret; +} + +/* + * Compute shared secret (SEC1 3.3.1) + */ +int mbedtls_ecdh_compute_shared(mbedtls_ecp_group *grp, mbedtls_mpi *z, + const mbedtls_ecp_point *Q, const mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + return ecdh_compute_shared_restartable(grp, z, Q, d, + f_rng, p_rng, NULL); +} +#endif /* !MBEDTLS_ECDH_COMPUTE_SHARED_ALT */ + +static void ecdh_init_internal(mbedtls_ecdh_context_mbed *ctx) +{ + mbedtls_ecp_group_init(&ctx->grp); + mbedtls_mpi_init(&ctx->d); + mbedtls_ecp_point_init(&ctx->Q); + mbedtls_ecp_point_init(&ctx->Qp); + mbedtls_mpi_init(&ctx->z); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + mbedtls_ecp_restart_init(&ctx->rs); +#endif +} + +/* + * Initialize context + */ +void mbedtls_ecdh_init(mbedtls_ecdh_context *ctx) +{ +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + ecdh_init_internal(ctx); + mbedtls_ecp_point_init(&ctx->Vi); + mbedtls_ecp_point_init(&ctx->Vf); + mbedtls_mpi_init(&ctx->_d); +#else + memset(ctx, 0, sizeof(mbedtls_ecdh_context)); + + ctx->var = MBEDTLS_ECDH_VARIANT_NONE; +#endif + ctx->point_format = MBEDTLS_ECP_PF_UNCOMPRESSED; +#if defined(MBEDTLS_ECP_RESTARTABLE) + ctx->restart_enabled = 0; +#endif +} + +static int ecdh_setup_internal(mbedtls_ecdh_context_mbed *ctx, + mbedtls_ecp_group_id grp_id) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_ecp_group_load(&ctx->grp, grp_id); + if (ret != 0) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + return 0; +} + +/* + * Setup context + */ +int mbedtls_ecdh_setup(mbedtls_ecdh_context *ctx, mbedtls_ecp_group_id grp_id) +{ +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ecdh_setup_internal(ctx, grp_id); +#else + switch (grp_id) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECP_DP_CURVE25519: + ctx->point_format = MBEDTLS_ECP_PF_COMPRESSED; + ctx->var = MBEDTLS_ECDH_VARIANT_EVEREST; + ctx->grp_id = grp_id; + return mbedtls_everest_setup(&ctx->ctx.everest_ecdh, grp_id); +#endif + default: + ctx->point_format = MBEDTLS_ECP_PF_UNCOMPRESSED; + ctx->var = MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0; + ctx->grp_id = grp_id; + ecdh_init_internal(&ctx->ctx.mbed_ecdh); + return ecdh_setup_internal(&ctx->ctx.mbed_ecdh, grp_id); + } +#endif +} + +static void ecdh_free_internal(mbedtls_ecdh_context_mbed *ctx) +{ + mbedtls_ecp_group_free(&ctx->grp); + mbedtls_mpi_free(&ctx->d); + mbedtls_ecp_point_free(&ctx->Q); + mbedtls_ecp_point_free(&ctx->Qp); + mbedtls_mpi_free(&ctx->z); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + mbedtls_ecp_restart_free(&ctx->rs); +#endif +} + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/* + * Enable restartable operations for context + */ +void mbedtls_ecdh_enable_restart(mbedtls_ecdh_context *ctx) +{ + ctx->restart_enabled = 1; +} +#endif + +/* + * Free context + */ +void mbedtls_ecdh_free(mbedtls_ecdh_context *ctx) +{ + if (ctx == NULL) { + return; + } + +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + mbedtls_ecp_point_free(&ctx->Vi); + mbedtls_ecp_point_free(&ctx->Vf); + mbedtls_mpi_free(&ctx->_d); + ecdh_free_internal(ctx); +#else + switch (ctx->var) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECDH_VARIANT_EVEREST: + mbedtls_everest_free(&ctx->ctx.everest_ecdh); + break; +#endif + case MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0: + ecdh_free_internal(&ctx->ctx.mbed_ecdh); + break; + default: + break; + } + + ctx->point_format = MBEDTLS_ECP_PF_UNCOMPRESSED; + ctx->var = MBEDTLS_ECDH_VARIANT_NONE; + ctx->grp_id = MBEDTLS_ECP_DP_NONE; +#endif +} + +static int ecdh_make_params_internal(mbedtls_ecdh_context_mbed *ctx, + size_t *olen, int point_format, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, + unsigned char *, + size_t), + void *p_rng, + int restart_enabled) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t grp_len, pt_len; +#if defined(MBEDTLS_ECP_RESTARTABLE) + mbedtls_ecp_restart_ctx *rs_ctx = NULL; +#endif + + if (ctx->grp.pbits == 0) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (restart_enabled) { + rs_ctx = &ctx->rs; + } +#else + (void) restart_enabled; +#endif + + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if ((ret = ecdh_gen_public_restartable(&ctx->grp, &ctx->d, &ctx->Q, + f_rng, p_rng, rs_ctx)) != 0) { + return ret; + } +#else + if ((ret = mbedtls_ecdh_gen_public(&ctx->grp, &ctx->d, &ctx->Q, + f_rng, p_rng)) != 0) { + return ret; + } +#endif /* MBEDTLS_ECP_RESTARTABLE */ + + if ((ret = mbedtls_ecp_tls_write_group(&ctx->grp, &grp_len, buf, + blen)) != 0) { + return ret; + } + + buf += grp_len; + blen -= grp_len; + + if ((ret = mbedtls_ecp_tls_write_point(&ctx->grp, &ctx->Q, point_format, + &pt_len, buf, blen)) != 0) { + return ret; + } + + *olen = grp_len + pt_len; + return 0; +} + +/* + * Setup and write the ServerKeyExchange parameters (RFC 4492) + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ +int mbedtls_ecdh_make_params(mbedtls_ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int restart_enabled = 0; +#if defined(MBEDTLS_ECP_RESTARTABLE) + restart_enabled = ctx->restart_enabled; +#else + (void) restart_enabled; +#endif + +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ecdh_make_params_internal(ctx, olen, ctx->point_format, buf, blen, + f_rng, p_rng, restart_enabled); +#else + switch (ctx->var) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECDH_VARIANT_EVEREST: + return mbedtls_everest_make_params(&ctx->ctx.everest_ecdh, olen, + buf, blen, f_rng, p_rng); +#endif + case MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0: + return ecdh_make_params_internal(&ctx->ctx.mbed_ecdh, olen, + ctx->point_format, buf, blen, + f_rng, p_rng, + restart_enabled); + default: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } +#endif +} + +static int ecdh_read_params_internal(mbedtls_ecdh_context_mbed *ctx, + const unsigned char **buf, + const unsigned char *end) +{ + return mbedtls_ecp_tls_read_point(&ctx->grp, &ctx->Qp, buf, + end - *buf); +} + +/* + * Read the ServerKeyExchange parameters (RFC 4492) + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ +int mbedtls_ecdh_read_params(mbedtls_ecdh_context *ctx, + const unsigned char **buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_group_id grp_id; + if ((ret = mbedtls_ecp_tls_read_group_id(&grp_id, buf, end - *buf)) + != 0) { + return ret; + } + + if ((ret = mbedtls_ecdh_setup(ctx, grp_id)) != 0) { + return ret; + } + +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ecdh_read_params_internal(ctx, buf, end); +#else + switch (ctx->var) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECDH_VARIANT_EVEREST: + return mbedtls_everest_read_params(&ctx->ctx.everest_ecdh, + buf, end); +#endif + case MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0: + return ecdh_read_params_internal(&ctx->ctx.mbed_ecdh, + buf, end); + default: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } +#endif +} + +static int ecdh_get_params_internal(mbedtls_ecdh_context_mbed *ctx, + const mbedtls_ecp_keypair *key, + mbedtls_ecdh_side side) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* If it's not our key, just import the public part as Qp */ + if (side == MBEDTLS_ECDH_THEIRS) { + return mbedtls_ecp_copy(&ctx->Qp, &key->Q); + } + + /* Our key: import public (as Q) and private parts */ + if (side != MBEDTLS_ECDH_OURS) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + if ((ret = mbedtls_ecp_copy(&ctx->Q, &key->Q)) != 0 || + (ret = mbedtls_mpi_copy(&ctx->d, &key->d)) != 0) { + return ret; + } + + return 0; +} + +/* + * Get parameters from a keypair + */ +int mbedtls_ecdh_get_params(mbedtls_ecdh_context *ctx, + const mbedtls_ecp_keypair *key, + mbedtls_ecdh_side side) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + if (side != MBEDTLS_ECDH_OURS && side != MBEDTLS_ECDH_THEIRS) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + if (mbedtls_ecdh_grp_id(ctx) == MBEDTLS_ECP_DP_NONE) { + /* This is the first call to get_params(). Set up the context + * for use with the group. */ + if ((ret = mbedtls_ecdh_setup(ctx, key->grp.id)) != 0) { + return ret; + } + } else { + /* This is not the first call to get_params(). Check that the + * current key's group is the same as the context's, which was set + * from the first key's group. */ + if (mbedtls_ecdh_grp_id(ctx) != key->grp.id) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + } + +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ecdh_get_params_internal(ctx, key, side); +#else + switch (ctx->var) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECDH_VARIANT_EVEREST: + { + mbedtls_everest_ecdh_side s = side == MBEDTLS_ECDH_OURS ? + MBEDTLS_EVEREST_ECDH_OURS : + MBEDTLS_EVEREST_ECDH_THEIRS; + return mbedtls_everest_get_params(&ctx->ctx.everest_ecdh, + key, s); + } +#endif + case MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0: + return ecdh_get_params_internal(&ctx->ctx.mbed_ecdh, + key, side); + default: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } +#endif +} + +static int ecdh_make_public_internal(mbedtls_ecdh_context_mbed *ctx, + size_t *olen, int point_format, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, + unsigned char *, + size_t), + void *p_rng, + int restart_enabled) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#if defined(MBEDTLS_ECP_RESTARTABLE) + mbedtls_ecp_restart_ctx *rs_ctx = NULL; +#endif + + if (ctx->grp.pbits == 0) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (restart_enabled) { + rs_ctx = &ctx->rs; + } +#else + (void) restart_enabled; +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if ((ret = ecdh_gen_public_restartable(&ctx->grp, &ctx->d, &ctx->Q, + f_rng, p_rng, rs_ctx)) != 0) { + return ret; + } +#else + if ((ret = mbedtls_ecdh_gen_public(&ctx->grp, &ctx->d, &ctx->Q, + f_rng, p_rng)) != 0) { + return ret; + } +#endif /* MBEDTLS_ECP_RESTARTABLE */ + + return mbedtls_ecp_tls_write_point(&ctx->grp, &ctx->Q, point_format, olen, + buf, blen); +} + +/* + * Setup and export the client public value + */ +int mbedtls_ecdh_make_public(mbedtls_ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int restart_enabled = 0; +#if defined(MBEDTLS_ECP_RESTARTABLE) + restart_enabled = ctx->restart_enabled; +#endif + +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ecdh_make_public_internal(ctx, olen, ctx->point_format, buf, blen, + f_rng, p_rng, restart_enabled); +#else + switch (ctx->var) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECDH_VARIANT_EVEREST: + return mbedtls_everest_make_public(&ctx->ctx.everest_ecdh, olen, + buf, blen, f_rng, p_rng); +#endif + case MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0: + return ecdh_make_public_internal(&ctx->ctx.mbed_ecdh, olen, + ctx->point_format, buf, blen, + f_rng, p_rng, + restart_enabled); + default: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } +#endif +} + +static int ecdh_read_public_internal(mbedtls_ecdh_context_mbed *ctx, + const unsigned char *buf, size_t blen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + + if ((ret = mbedtls_ecp_tls_read_point(&ctx->grp, &ctx->Qp, &p, + blen)) != 0) { + return ret; + } + + if ((size_t) (p - buf) != blen) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + return 0; +} + +/* + * Parse and import the client's public value + */ +int mbedtls_ecdh_read_public(mbedtls_ecdh_context *ctx, + const unsigned char *buf, size_t blen) +{ +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ecdh_read_public_internal(ctx, buf, blen); +#else + switch (ctx->var) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECDH_VARIANT_EVEREST: + return mbedtls_everest_read_public(&ctx->ctx.everest_ecdh, + buf, blen); +#endif + case MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0: + return ecdh_read_public_internal(&ctx->ctx.mbed_ecdh, + buf, blen); + default: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } +#endif +} + +static int ecdh_calc_secret_internal(mbedtls_ecdh_context_mbed *ctx, + size_t *olen, unsigned char *buf, + size_t blen, + int (*f_rng)(void *, + unsigned char *, + size_t), + void *p_rng, + int restart_enabled) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#if defined(MBEDTLS_ECP_RESTARTABLE) + mbedtls_ecp_restart_ctx *rs_ctx = NULL; +#endif + + if (ctx == NULL || ctx->grp.pbits == 0) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (restart_enabled) { + rs_ctx = &ctx->rs; + } +#else + (void) restart_enabled; +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if ((ret = ecdh_compute_shared_restartable(&ctx->grp, &ctx->z, &ctx->Qp, + &ctx->d, f_rng, p_rng, + rs_ctx)) != 0) { + return ret; + } +#else + if ((ret = mbedtls_ecdh_compute_shared(&ctx->grp, &ctx->z, &ctx->Qp, + &ctx->d, f_rng, p_rng)) != 0) { + return ret; + } +#endif /* MBEDTLS_ECP_RESTARTABLE */ + + if (mbedtls_mpi_size(&ctx->z) > blen) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + *olen = ctx->grp.pbits / 8 + ((ctx->grp.pbits % 8) != 0); + + if (mbedtls_ecp_get_type(&ctx->grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + return mbedtls_mpi_write_binary_le(&ctx->z, buf, *olen); + } + + return mbedtls_mpi_write_binary(&ctx->z, buf, *olen); +} + +/* + * Derive and export the shared secret + */ +int mbedtls_ecdh_calc_secret(mbedtls_ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int restart_enabled = 0; +#if defined(MBEDTLS_ECP_RESTARTABLE) + restart_enabled = ctx->restart_enabled; +#endif + +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + return ecdh_calc_secret_internal(ctx, olen, buf, blen, f_rng, p_rng, + restart_enabled); +#else + switch (ctx->var) { +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + case MBEDTLS_ECDH_VARIANT_EVEREST: + return mbedtls_everest_calc_secret(&ctx->ctx.everest_ecdh, olen, + buf, blen, f_rng, p_rng); +#endif + case MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0: + return ecdh_calc_secret_internal(&ctx->ctx.mbed_ecdh, olen, buf, + blen, f_rng, p_rng, + restart_enabled); + default: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } +#endif +} +#endif /* MBEDTLS_ECDH_C */ diff --git a/r5dev/thirdparty/mbedtls/ecdsa.c b/r5dev/thirdparty/mbedtls/ecdsa.c new file mode 100644 index 00000000..eb3c3031 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ecdsa.c @@ -0,0 +1,879 @@ +/* + * Elliptic curve DSA + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * References: + * + * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + */ + +#include "common.h" + +#if defined(MBEDTLS_ECDSA_C) + +#include "mbedtls/ecdsa.h" +#include "mbedtls/asn1write.h" + +#include + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +#include "mbedtls/hmac_drbg.h" +#endif + +#include "mbedtls/platform.h" + +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#if defined(MBEDTLS_ECP_RESTARTABLE) + +/* + * Sub-context for ecdsa_verify() + */ +struct mbedtls_ecdsa_restart_ver { + mbedtls_mpi u1, u2; /* intermediate values */ + enum { /* what to do next? */ + ecdsa_ver_init = 0, /* getting started */ + ecdsa_ver_muladd, /* muladd step */ + } state; +}; + +/* + * Init verify restart sub-context + */ +static void ecdsa_restart_ver_init(mbedtls_ecdsa_restart_ver_ctx *ctx) +{ + mbedtls_mpi_init(&ctx->u1); + mbedtls_mpi_init(&ctx->u2); + ctx->state = ecdsa_ver_init; +} + +/* + * Free the components of a verify restart sub-context + */ +static void ecdsa_restart_ver_free(mbedtls_ecdsa_restart_ver_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_mpi_free(&ctx->u1); + mbedtls_mpi_free(&ctx->u2); + + ecdsa_restart_ver_init(ctx); +} + +/* + * Sub-context for ecdsa_sign() + */ +struct mbedtls_ecdsa_restart_sig { + int sign_tries; + int key_tries; + mbedtls_mpi k; /* per-signature random */ + mbedtls_mpi r; /* r value */ + enum { /* what to do next? */ + ecdsa_sig_init = 0, /* getting started */ + ecdsa_sig_mul, /* doing ecp_mul() */ + ecdsa_sig_modn, /* mod N computations */ + } state; +}; + +/* + * Init verify sign sub-context + */ +static void ecdsa_restart_sig_init(mbedtls_ecdsa_restart_sig_ctx *ctx) +{ + ctx->sign_tries = 0; + ctx->key_tries = 0; + mbedtls_mpi_init(&ctx->k); + mbedtls_mpi_init(&ctx->r); + ctx->state = ecdsa_sig_init; +} + +/* + * Free the components of a sign restart sub-context + */ +static void ecdsa_restart_sig_free(mbedtls_ecdsa_restart_sig_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_mpi_free(&ctx->k); + mbedtls_mpi_free(&ctx->r); +} + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +/* + * Sub-context for ecdsa_sign_det() + */ +struct mbedtls_ecdsa_restart_det { + mbedtls_hmac_drbg_context rng_ctx; /* DRBG state */ + enum { /* what to do next? */ + ecdsa_det_init = 0, /* getting started */ + ecdsa_det_sign, /* make signature */ + } state; +}; + +/* + * Init verify sign_det sub-context + */ +static void ecdsa_restart_det_init(mbedtls_ecdsa_restart_det_ctx *ctx) +{ + mbedtls_hmac_drbg_init(&ctx->rng_ctx); + ctx->state = ecdsa_det_init; +} + +/* + * Free the components of a sign_det restart sub-context + */ +static void ecdsa_restart_det_free(mbedtls_ecdsa_restart_det_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_hmac_drbg_free(&ctx->rng_ctx); + + ecdsa_restart_det_init(ctx); +} +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ + +#define ECDSA_RS_ECP (rs_ctx == NULL ? NULL : &rs_ctx->ecp) + +/* Utility macro for checking and updating ops budget */ +#define ECDSA_BUDGET(ops) \ + MBEDTLS_MPI_CHK(mbedtls_ecp_check_budget(grp, ECDSA_RS_ECP, ops)); + +/* Call this when entering a function that needs its own sub-context */ +#define ECDSA_RS_ENTER(SUB) do { \ + /* reset ops count for this call if top-level */ \ + if (rs_ctx != NULL && rs_ctx->ecp.depth++ == 0) \ + rs_ctx->ecp.ops_done = 0; \ + \ + /* set up our own sub-context if needed */ \ + if (mbedtls_ecp_restart_is_enabled() && \ + rs_ctx != NULL && rs_ctx->SUB == NULL) \ + { \ + rs_ctx->SUB = mbedtls_calloc(1, sizeof(*rs_ctx->SUB)); \ + if (rs_ctx->SUB == NULL) \ + return MBEDTLS_ERR_ECP_ALLOC_FAILED; \ + \ + ecdsa_restart_## SUB ##_init(rs_ctx->SUB); \ + } \ +} while (0) + +/* Call this when leaving a function that needs its own sub-context */ +#define ECDSA_RS_LEAVE(SUB) do { \ + /* clear our sub-context when not in progress (done or error) */ \ + if (rs_ctx != NULL && rs_ctx->SUB != NULL && \ + ret != MBEDTLS_ERR_ECP_IN_PROGRESS) \ + { \ + ecdsa_restart_## SUB ##_free(rs_ctx->SUB); \ + mbedtls_free(rs_ctx->SUB); \ + rs_ctx->SUB = NULL; \ + } \ + \ + if (rs_ctx != NULL) \ + rs_ctx->ecp.depth--; \ +} while (0) + +#else /* MBEDTLS_ECP_RESTARTABLE */ + +#define ECDSA_RS_ECP NULL + +#define ECDSA_BUDGET(ops) /* no-op; for compatibility */ + +#define ECDSA_RS_ENTER(SUB) (void) rs_ctx +#define ECDSA_RS_LEAVE(SUB) (void) rs_ctx + +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) || \ + !defined(MBEDTLS_ECDSA_SIGN_ALT) || \ + !defined(MBEDTLS_ECDSA_VERIFY_ALT) +/* + * Derive a suitable integer for group grp from a buffer of length len + * SEC1 4.1.3 step 5 aka SEC1 4.1.4 step 3 + */ +static int derive_mpi(const mbedtls_ecp_group *grp, mbedtls_mpi *x, + const unsigned char *buf, size_t blen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n_size = (grp->nbits + 7) / 8; + size_t use_size = blen > n_size ? n_size : blen; + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(x, buf, use_size)); + if (use_size * 8 > grp->nbits) { + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(x, use_size * 8 - grp->nbits)); + } + + /* While at it, reduce modulo N */ + if (mbedtls_mpi_cmp_mpi(x, &grp->N) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(x, x, &grp->N)); + } + +cleanup: + return ret; +} +#endif /* ECDSA_DETERMINISTIC || !ECDSA_SIGN_ALT || !ECDSA_VERIFY_ALT */ + +#if !defined(MBEDTLS_ECDSA_SIGN_ALT) +/* + * Compute ECDSA signature of a hashed message (SEC1 4.1.3) + * Obviously, compared to SEC1 4.1.3, we skip step 4 (hash message) + */ +int mbedtls_ecdsa_sign_restartable(mbedtls_ecp_group *grp, + mbedtls_mpi *r, mbedtls_mpi *s, + const mbedtls_mpi *d, const unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + int (*f_rng_blind)(void *, unsigned char *, size_t), + void *p_rng_blind, + mbedtls_ecdsa_restart_ctx *rs_ctx) +{ + 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 *pk = &k, *pr = r; + + /* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */ + if (!mbedtls_ecdsa_can_do(grp->id) || grp->N.p == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* Make sure d is in range 1..n-1 */ + if (mbedtls_mpi_cmp_int(d, 1) < 0 || mbedtls_mpi_cmp_mpi(d, &grp->N) >= 0) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + mbedtls_ecp_point_init(&R); + mbedtls_mpi_init(&k); mbedtls_mpi_init(&e); mbedtls_mpi_init(&t); + + ECDSA_RS_ENTER(sig); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->sig != NULL) { + /* redirect to our context */ + p_sign_tries = &rs_ctx->sig->sign_tries; + p_key_tries = &rs_ctx->sig->key_tries; + pk = &rs_ctx->sig->k; + pr = &rs_ctx->sig->r; + + /* jump to current step */ + if (rs_ctx->sig->state == ecdsa_sig_mul) { + goto mul; + } + if (rs_ctx->sig->state == ecdsa_sig_modn) { + goto modn; + } + } +#endif /* MBEDTLS_ECP_RESTARTABLE */ + + *p_sign_tries = 0; + do { + if ((*p_sign_tries)++ > 10) { + ret = MBEDTLS_ERR_ECP_RANDOM_FAILED; + goto cleanup; + } + + /* + * Steps 1-3: generate a suitable ephemeral keypair + * and set r = xR mod n + */ + *p_key_tries = 0; + do { + if ((*p_key_tries)++ > 10) { + ret = MBEDTLS_ERR_ECP_RANDOM_FAILED; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_ecp_gen_privkey(grp, pk, f_rng, p_rng)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->sig != NULL) { + rs_ctx->sig->state = ecdsa_sig_mul; + } + +mul: +#endif + MBEDTLS_MPI_CHK(mbedtls_ecp_mul_restartable(grp, &R, pk, &grp->G, + f_rng_blind, + p_rng_blind, + ECDSA_RS_ECP)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(pr, &R.X, &grp->N)); + } while (mbedtls_mpi_cmp_int(pr, 0) == 0); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->sig != NULL) { + rs_ctx->sig->state = ecdsa_sig_modn; + } + +modn: +#endif + /* + * Accounting for everything up to the end of the loop + * (step 6, but checking now avoids saving e and t) + */ + ECDSA_BUDGET(MBEDTLS_ECP_OPS_INV + 4); + + /* + * Step 5: derive MPI from hashed message + */ + 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 + */ + 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_mul_mpi(s, s, &e)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(s, s, &grp->N)); + } while (mbedtls_mpi_cmp_int(s, 0) == 0); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->sig != NULL) { + mbedtls_mpi_copy(r, pr); + } +#endif + +cleanup: + mbedtls_ecp_point_free(&R); + mbedtls_mpi_free(&k); mbedtls_mpi_free(&e); mbedtls_mpi_free(&t); + + ECDSA_RS_LEAVE(sig); + + return ret; +} + +int mbedtls_ecdsa_can_do(mbedtls_ecp_group_id gid) +{ + switch (gid) { +#ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED + case MBEDTLS_ECP_DP_CURVE25519: return 0; +#endif +#ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED + case MBEDTLS_ECP_DP_CURVE448: return 0; +#endif + default: return 1; + } +} + +/* + * Compute ECDSA signature of a hashed message + */ +int mbedtls_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s, + const mbedtls_mpi *d, const unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + /* Use the same RNG for both blinding and ephemeral key generation */ + return mbedtls_ecdsa_sign_restartable(grp, r, s, d, buf, blen, + f_rng, p_rng, f_rng, p_rng, NULL); +} +#endif /* !MBEDTLS_ECDSA_SIGN_ALT */ + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +/* + * Deterministic signature wrapper + * + * note: The f_rng_blind parameter must not be NULL. + * + */ +int mbedtls_ecdsa_sign_det_restartable(mbedtls_ecp_group *grp, + mbedtls_mpi *r, mbedtls_mpi *s, + const mbedtls_mpi *d, const unsigned char *buf, size_t blen, + mbedtls_md_type_t md_alg, + int (*f_rng_blind)(void *, unsigned char *, size_t), + void *p_rng_blind, + mbedtls_ecdsa_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_hmac_drbg_context rng_ctx; + mbedtls_hmac_drbg_context *p_rng = &rng_ctx; + unsigned char data[2 * MBEDTLS_ECP_MAX_BYTES]; + size_t grp_len = (grp->nbits + 7) / 8; + const mbedtls_md_info_t *md_info; + mbedtls_mpi h; + + if ((md_info = mbedtls_md_info_from_type(md_alg)) == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + mbedtls_mpi_init(&h); + mbedtls_hmac_drbg_init(&rng_ctx); + + ECDSA_RS_ENTER(det); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->det != NULL) { + /* redirect to our context */ + p_rng = &rs_ctx->det->rng_ctx; + + /* jump to current step */ + if (rs_ctx->det->state == ecdsa_det_sign) { + goto sign; + } + } +#endif /* MBEDTLS_ECP_RESTARTABLE */ + + /* Use private key and message hash (reduced) to initialize HMAC_DRBG */ + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(d, data, grp_len)); + MBEDTLS_MPI_CHK(derive_mpi(grp, &h, buf, blen)); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&h, data + grp_len, grp_len)); + mbedtls_hmac_drbg_seed_buf(p_rng, md_info, data, 2 * grp_len); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->det != NULL) { + rs_ctx->det->state = ecdsa_det_sign; + } + +sign: +#endif +#if defined(MBEDTLS_ECDSA_SIGN_ALT) + (void) f_rng_blind; + (void) p_rng_blind; + ret = mbedtls_ecdsa_sign(grp, r, s, d, buf, blen, + mbedtls_hmac_drbg_random, p_rng); +#else + ret = mbedtls_ecdsa_sign_restartable(grp, r, s, d, buf, blen, + mbedtls_hmac_drbg_random, p_rng, + f_rng_blind, p_rng_blind, rs_ctx); +#endif /* MBEDTLS_ECDSA_SIGN_ALT */ + +cleanup: + mbedtls_hmac_drbg_free(&rng_ctx); + mbedtls_mpi_free(&h); + + ECDSA_RS_LEAVE(det); + + return ret; +} + +/* + * Deterministic signature wrapper + */ +int mbedtls_ecdsa_sign_det_ext(mbedtls_ecp_group *grp, mbedtls_mpi *r, + mbedtls_mpi *s, const mbedtls_mpi *d, + const unsigned char *buf, size_t blen, + mbedtls_md_type_t md_alg, + int (*f_rng_blind)(void *, unsigned char *, + size_t), + void *p_rng_blind) +{ + return mbedtls_ecdsa_sign_det_restartable(grp, r, s, d, buf, blen, md_alg, + f_rng_blind, p_rng_blind, NULL); +} +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ + +#if !defined(MBEDTLS_ECDSA_VERIFY_ALT) +/* + * Verify ECDSA signature of hashed message (SEC1 4.1.4) + * Obviously, compared to SEC1 4.1.3, we skip step 2 (hash message) + */ +int mbedtls_ecdsa_verify_restartable(mbedtls_ecp_group *grp, + const unsigned char *buf, size_t blen, + const mbedtls_ecp_point *Q, + const mbedtls_mpi *r, + const mbedtls_mpi *s, + mbedtls_ecdsa_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi e, s_inv, u1, u2; + mbedtls_ecp_point R; + mbedtls_mpi *pu1 = &u1, *pu2 = &u2; + + mbedtls_ecp_point_init(&R); + mbedtls_mpi_init(&e); mbedtls_mpi_init(&s_inv); + mbedtls_mpi_init(&u1); mbedtls_mpi_init(&u2); + + /* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */ + if (!mbedtls_ecdsa_can_do(grp->id) || grp->N.p == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + ECDSA_RS_ENTER(ver); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->ver != NULL) { + /* redirect to our context */ + pu1 = &rs_ctx->ver->u1; + pu2 = &rs_ctx->ver->u2; + + /* jump to current step */ + if (rs_ctx->ver->state == ecdsa_ver_muladd) { + goto muladd; + } + } +#endif /* MBEDTLS_ECP_RESTARTABLE */ + + /* + * Step 1: make sure r and s are in range 1..n-1 + */ + if (mbedtls_mpi_cmp_int(r, 1) < 0 || mbedtls_mpi_cmp_mpi(r, &grp->N) >= 0 || + mbedtls_mpi_cmp_int(s, 1) < 0 || mbedtls_mpi_cmp_mpi(s, &grp->N) >= 0) { + ret = MBEDTLS_ERR_ECP_VERIFY_FAILED; + goto cleanup; + } + + /* + * Step 3: derive MPI from hashed message + */ + MBEDTLS_MPI_CHK(derive_mpi(grp, &e, buf, blen)); + + /* + * Step 4: u1 = e / s mod n, u2 = r / s mod n + */ + 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_mul_mpi(pu1, &e, &s_inv)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(pu1, pu1, &grp->N)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(pu2, r, &s_inv)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(pu2, pu2, &grp->N)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->ver != NULL) { + rs_ctx->ver->state = ecdsa_ver_muladd; + } + +muladd: +#endif + /* + * Step 5: R = u1 G + u2 Q + */ + MBEDTLS_MPI_CHK(mbedtls_ecp_muladd_restartable(grp, + &R, pu1, &grp->G, pu2, Q, ECDSA_RS_ECP)); + + if (mbedtls_ecp_is_zero(&R)) { + ret = MBEDTLS_ERR_ECP_VERIFY_FAILED; + goto cleanup; + } + + /* + * Step 6: convert xR to an integer (no-op) + * Step 7: reduce xR mod n (gives v) + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&R.X, &R.X, &grp->N)); + + /* + * Step 8: check if v (that is, R.X) is equal to r + */ + if (mbedtls_mpi_cmp_mpi(&R.X, r) != 0) { + ret = MBEDTLS_ERR_ECP_VERIFY_FAILED; + goto cleanup; + } + +cleanup: + mbedtls_ecp_point_free(&R); + mbedtls_mpi_free(&e); mbedtls_mpi_free(&s_inv); + mbedtls_mpi_free(&u1); mbedtls_mpi_free(&u2); + + ECDSA_RS_LEAVE(ver); + + return ret; +} + +/* + * Verify ECDSA signature of hashed message + */ +int mbedtls_ecdsa_verify(mbedtls_ecp_group *grp, + const unsigned char *buf, size_t blen, + const mbedtls_ecp_point *Q, + const mbedtls_mpi *r, + const mbedtls_mpi *s) +{ + return mbedtls_ecdsa_verify_restartable(grp, buf, blen, Q, r, s, NULL); +} +#endif /* !MBEDTLS_ECDSA_VERIFY_ALT */ + +/* + * Convert a signature (given by context) to ASN.1 + */ +static int ecdsa_signature_to_asn1(const mbedtls_mpi *r, const mbedtls_mpi *s, + unsigned char *sig, size_t sig_size, + size_t *slen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char buf[MBEDTLS_ECDSA_MAX_LEN] = { 0 }; + unsigned char *p = buf + sizeof(buf); + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, s)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, r)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + if (len > sig_size) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + memcpy(sig, p, len); + *slen = len; + + return 0; +} + +/* + * Compute and write signature + */ +int mbedtls_ecdsa_write_signature_restartable(mbedtls_ecdsa_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecdsa_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi r, s; + if (f_rng == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign_det_restartable(&ctx->grp, &r, &s, &ctx->d, + hash, hlen, md_alg, f_rng, + p_rng, rs_ctx)); +#else + (void) md_alg; + +#if defined(MBEDTLS_ECDSA_SIGN_ALT) + (void) rs_ctx; + + MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign(&ctx->grp, &r, &s, &ctx->d, + hash, hlen, f_rng, p_rng)); +#else + /* Use the same RNG for both blinding and ephemeral key generation */ + MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign_restartable(&ctx->grp, &r, &s, &ctx->d, + hash, hlen, f_rng, p_rng, f_rng, + p_rng, rs_ctx)); +#endif /* MBEDTLS_ECDSA_SIGN_ALT */ +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ + + MBEDTLS_MPI_CHK(ecdsa_signature_to_asn1(&r, &s, sig, sig_size, slen)); + +cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + + return ret; +} + +/* + * Compute and write signature + */ +int mbedtls_ecdsa_write_signature(mbedtls_ecdsa_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + return mbedtls_ecdsa_write_signature_restartable( + ctx, md_alg, hash, hlen, sig, sig_size, slen, + f_rng, p_rng, NULL); +} + +/* + * Read and check signature + */ +int mbedtls_ecdsa_read_signature(mbedtls_ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen) +{ + return mbedtls_ecdsa_read_signature_restartable( + ctx, hash, hlen, sig, slen, NULL); +} + +/* + * Restartable read and check signature + */ +int mbedtls_ecdsa_read_signature_restartable(mbedtls_ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen, + mbedtls_ecdsa_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = (unsigned char *) sig; + const unsigned char *end = sig + slen; + size_t len; + mbedtls_mpi r, s; + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + ret += MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + if (p + len != end) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_ECP_BAD_INPUT_DATA, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + goto cleanup; + } + + if ((ret = mbedtls_asn1_get_mpi(&p, end, &r)) != 0 || + (ret = mbedtls_asn1_get_mpi(&p, end, &s)) != 0) { + ret += MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } +#if defined(MBEDTLS_ECDSA_VERIFY_ALT) + (void) rs_ctx; + + if ((ret = mbedtls_ecdsa_verify(&ctx->grp, hash, hlen, + &ctx->Q, &r, &s)) != 0) { + goto cleanup; + } +#else + if ((ret = mbedtls_ecdsa_verify_restartable(&ctx->grp, hash, hlen, + &ctx->Q, &r, &s, rs_ctx)) != 0) { + goto cleanup; + } +#endif /* MBEDTLS_ECDSA_VERIFY_ALT */ + + /* At this point we know that the buffer starts with a valid signature. + * Return 0 if the buffer just contains the signature, and a specific + * error code if the valid signature is followed by more data. */ + if (p != end) { + ret = MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH; + } + +cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + + return ret; +} + +#if !defined(MBEDTLS_ECDSA_GENKEY_ALT) +/* + * Generate key pair + */ +int mbedtls_ecdsa_genkey(mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = 0; + ret = mbedtls_ecp_group_load(&ctx->grp, gid); + if (ret != 0) { + return ret; + } + + return mbedtls_ecp_gen_keypair(&ctx->grp, &ctx->d, + &ctx->Q, f_rng, p_rng); +} +#endif /* !MBEDTLS_ECDSA_GENKEY_ALT */ + +/* + * Set context from an mbedtls_ecp_keypair + */ +int mbedtls_ecdsa_from_keypair(mbedtls_ecdsa_context *ctx, const mbedtls_ecp_keypair *key) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + if ((ret = mbedtls_ecp_group_copy(&ctx->grp, &key->grp)) != 0 || + (ret = mbedtls_mpi_copy(&ctx->d, &key->d)) != 0 || + (ret = mbedtls_ecp_copy(&ctx->Q, &key->Q)) != 0) { + mbedtls_ecdsa_free(ctx); + } + + return ret; +} + +/* + * Initialize context + */ +void mbedtls_ecdsa_init(mbedtls_ecdsa_context *ctx) +{ + mbedtls_ecp_keypair_init(ctx); +} + +/* + * Free context + */ +void mbedtls_ecdsa_free(mbedtls_ecdsa_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_ecp_keypair_free(ctx); +} + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/* + * Initialize a restart context + */ +void mbedtls_ecdsa_restart_init(mbedtls_ecdsa_restart_ctx *ctx) +{ + mbedtls_ecp_restart_init(&ctx->ecp); + + ctx->ver = NULL; + ctx->sig = NULL; +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + ctx->det = NULL; +#endif +} + +/* + * Free the components of a restart context + */ +void mbedtls_ecdsa_restart_free(mbedtls_ecdsa_restart_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_ecp_restart_free(&ctx->ecp); + + ecdsa_restart_ver_free(ctx->ver); + mbedtls_free(ctx->ver); + ctx->ver = NULL; + + ecdsa_restart_sig_free(ctx->sig); + mbedtls_free(ctx->sig); + ctx->sig = NULL; + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + ecdsa_restart_det_free(ctx->det); + mbedtls_free(ctx->det); + ctx->det = NULL; +#endif +} +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +#endif /* MBEDTLS_ECDSA_C */ diff --git a/r5dev/thirdparty/mbedtls/ecjpake.c b/r5dev/thirdparty/mbedtls/ecjpake.c new file mode 100644 index 00000000..36c1327b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ecjpake.c @@ -0,0 +1,1259 @@ +/* + * Elliptic curve J-PAKE + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * References in the code are to the Thread v1.0 Specification, + * available to members of the Thread Group http://threadgroup.org/ + */ + +#include "common.h" + +#if defined(MBEDTLS_ECJPAKE_C) + +#include "mbedtls/ecjpake.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +/* We use MD first if it's available (for compatibility reasons) + * and "fall back" to PSA otherwise (which needs psa_crypto_init()). */ +#if !defined(MBEDTLS_MD_C) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#if !defined(MBEDTLS_ECJPAKE_ALT) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_md_errors, \ + psa_generic_status_to_mbedtls) +#endif /* !MBEDTLS_ECJPAKE_ALT */ +#endif /* !MBEDTLS_MD_C */ + +#include "hash_info.h" + +#include + +#if !defined(MBEDTLS_ECJPAKE_ALT) + +/* + * Convert a mbedtls_ecjpake_role to identifier string + */ +static const char * const ecjpake_id[] = { + "client", + "server" +}; + +#define ID_MINE (ecjpake_id[ctx->role]) +#define ID_PEER (ecjpake_id[1 - ctx->role]) + +/** + * Helper to Compute a hash from md_type + */ +static int mbedtls_ecjpake_compute_hash(mbedtls_md_type_t md_type, + const unsigned char *input, size_t ilen, + unsigned char *output) +{ +#if defined(MBEDTLS_MD_C) + return mbedtls_md(mbedtls_md_info_from_type(md_type), + input, ilen, output); +#else + psa_algorithm_t alg = mbedtls_psa_translate_md(md_type); + psa_status_t status; + size_t out_size = PSA_HASH_LENGTH(alg); + size_t out_len; + + status = psa_hash_compute(alg, input, ilen, output, out_size, &out_len); + + return PSA_TO_MBEDTLS_ERR(status); +#endif /* !MBEDTLS_MD_C */ +} + +/* + * Initialize context + */ +void mbedtls_ecjpake_init(mbedtls_ecjpake_context *ctx) +{ + ctx->md_type = MBEDTLS_MD_NONE; + mbedtls_ecp_group_init(&ctx->grp); + ctx->point_format = MBEDTLS_ECP_PF_UNCOMPRESSED; + + mbedtls_ecp_point_init(&ctx->Xm1); + mbedtls_ecp_point_init(&ctx->Xm2); + mbedtls_ecp_point_init(&ctx->Xp1); + mbedtls_ecp_point_init(&ctx->Xp2); + mbedtls_ecp_point_init(&ctx->Xp); + + mbedtls_mpi_init(&ctx->xm1); + mbedtls_mpi_init(&ctx->xm2); + mbedtls_mpi_init(&ctx->s); +} + +/* + * Free context + */ +void mbedtls_ecjpake_free(mbedtls_ecjpake_context *ctx) +{ + if (ctx == NULL) { + return; + } + + ctx->md_type = MBEDTLS_MD_NONE; + mbedtls_ecp_group_free(&ctx->grp); + + mbedtls_ecp_point_free(&ctx->Xm1); + mbedtls_ecp_point_free(&ctx->Xm2); + mbedtls_ecp_point_free(&ctx->Xp1); + mbedtls_ecp_point_free(&ctx->Xp2); + mbedtls_ecp_point_free(&ctx->Xp); + + mbedtls_mpi_free(&ctx->xm1); + mbedtls_mpi_free(&ctx->xm2); + mbedtls_mpi_free(&ctx->s); +} + +/* + * Setup context + */ +int mbedtls_ecjpake_setup(mbedtls_ecjpake_context *ctx, + mbedtls_ecjpake_role role, + mbedtls_md_type_t hash, + mbedtls_ecp_group_id curve, + const unsigned char *secret, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (role != MBEDTLS_ECJPAKE_CLIENT && role != MBEDTLS_ECJPAKE_SERVER) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + ctx->role = role; + +#if defined(MBEDTLS_MD_C) + if ((mbedtls_md_info_from_type(hash)) == NULL) { + return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; + } +#else + if (mbedtls_psa_translate_md(hash) == MBEDTLS_MD_NONE) { + return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; + } +#endif + + ctx->md_type = hash; + + MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&ctx->grp, curve)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->s, secret, len)); + +cleanup: + if (ret != 0) { + mbedtls_ecjpake_free(ctx); + } + + return ret; +} + +int mbedtls_ecjpake_set_point_format(mbedtls_ecjpake_context *ctx, + int point_format) +{ + switch (point_format) { + case MBEDTLS_ECP_PF_UNCOMPRESSED: + case MBEDTLS_ECP_PF_COMPRESSED: + ctx->point_format = point_format; + return 0; + default: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } +} + +/* + * Check if context is ready for use + */ +int mbedtls_ecjpake_check(const mbedtls_ecjpake_context *ctx) +{ + if (ctx->md_type == MBEDTLS_MD_NONE || + ctx->grp.id == MBEDTLS_ECP_DP_NONE || + ctx->s.p == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + return 0; +} + +/* + * Write a point plus its length to a buffer + */ +static int ecjpake_write_len_point(unsigned char **p, + const unsigned char *end, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *P) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + /* Need at least 4 for length plus 1 for point */ + if (end < *p || end - *p < 5) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + ret = mbedtls_ecp_point_write_binary(grp, P, pf, + &len, *p + 4, end - (*p + 4)); + if (ret != 0) { + return ret; + } + + MBEDTLS_PUT_UINT32_BE(len, *p, 0); + + *p += 4 + len; + + return 0; +} + +/* + * Size of the temporary buffer for ecjpake_hash: + * 3 EC points plus their length, plus ID and its length (4 + 6 bytes) + */ +#define ECJPAKE_HASH_BUF_LEN (3 * (4 + MBEDTLS_ECP_MAX_PT_LEN) + 4 + 6) + +/* + * Compute hash for ZKP (7.4.2.2.2.1) + */ +static int ecjpake_hash(const mbedtls_md_type_t md_type, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *G, + const mbedtls_ecp_point *V, + const mbedtls_ecp_point *X, + const char *id, + mbedtls_mpi *h) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char buf[ECJPAKE_HASH_BUF_LEN]; + unsigned char *p = buf; + const unsigned char *end = buf + sizeof(buf); + const size_t id_len = strlen(id); + unsigned char hash[MBEDTLS_HASH_MAX_SIZE]; + + /* Write things to temporary buffer */ + MBEDTLS_MPI_CHK(ecjpake_write_len_point(&p, end, grp, pf, G)); + MBEDTLS_MPI_CHK(ecjpake_write_len_point(&p, end, grp, pf, V)); + MBEDTLS_MPI_CHK(ecjpake_write_len_point(&p, end, grp, pf, X)); + + if (end - p < 4) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + MBEDTLS_PUT_UINT32_BE(id_len, p, 0); + p += 4; + + if (end < p || (size_t) (end - p) < id_len) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + memcpy(p, id, id_len); + p += id_len; + + /* Compute hash */ + MBEDTLS_MPI_CHK(mbedtls_ecjpake_compute_hash(md_type, + buf, p - buf, hash)); + + /* Turn it into an integer mod n */ + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(h, hash, + mbedtls_hash_info_get_size(md_type))); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(h, h, &grp->N)); + +cleanup: + return ret; +} + +/* + * Parse a ECShnorrZKP (7.4.2.2.2) and verify it (7.4.2.3.3) + */ +static int ecjpake_zkp_read(const mbedtls_md_type_t md_type, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *G, + const mbedtls_ecp_point *X, + const char *id, + const unsigned char **p, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point V, VV; + mbedtls_mpi r, h; + size_t r_len; + + mbedtls_ecp_point_init(&V); + mbedtls_ecp_point_init(&VV); + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&h); + + /* + * struct { + * ECPoint V; + * opaque r<1..2^8-1>; + * } ECSchnorrZKP; + */ + if (end < *p) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + MBEDTLS_MPI_CHK(mbedtls_ecp_tls_read_point(grp, &V, p, end - *p)); + + if (end < *p || (size_t) (end - *p) < 1) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + r_len = *(*p)++; + + if (end < *p || (size_t) (end - *p) < r_len || r_len == 0) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&r, *p, r_len)); + *p += r_len; + + /* + * Verification + */ + MBEDTLS_MPI_CHK(ecjpake_hash(md_type, grp, pf, G, &V, X, id, &h)); + MBEDTLS_MPI_CHK(mbedtls_ecp_muladd((mbedtls_ecp_group *) grp, + &VV, &h, X, &r, G)); + + if (mbedtls_ecp_point_cmp(&VV, &V) != 0) { + ret = MBEDTLS_ERR_ECP_VERIFY_FAILED; + goto cleanup; + } + +cleanup: + mbedtls_ecp_point_free(&V); + mbedtls_ecp_point_free(&VV); + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&h); + + return ret; +} + +/* + * Generate ZKP (7.4.2.3.2) and write it as ECSchnorrZKP (7.4.2.2.2) + */ +static int ecjpake_zkp_write(const mbedtls_md_type_t md_type, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *G, + const mbedtls_mpi *x, + const mbedtls_ecp_point *X, + const char *id, + unsigned char **p, + const unsigned char *end, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point V; + mbedtls_mpi v; + mbedtls_mpi h; /* later recycled to hold r */ + size_t len; + + if (end < *p) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + mbedtls_ecp_point_init(&V); + mbedtls_mpi_init(&v); + mbedtls_mpi_init(&h); + + /* Compute signature */ + MBEDTLS_MPI_CHK(mbedtls_ecp_gen_keypair_base((mbedtls_ecp_group *) grp, + G, &v, &V, f_rng, p_rng)); + MBEDTLS_MPI_CHK(ecjpake_hash(md_type, grp, pf, G, &V, X, id, &h)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&h, &h, x)); /* x*h */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&h, &v, &h)); /* v - x*h */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&h, &h, &grp->N)); /* r */ + + /* Write it out */ + MBEDTLS_MPI_CHK(mbedtls_ecp_tls_write_point(grp, &V, + pf, &len, *p, end - *p)); + *p += len; + + len = mbedtls_mpi_size(&h); /* actually r */ + if (end < *p || (size_t) (end - *p) < 1 + len || len > 255) { + ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + goto cleanup; + } + + *(*p)++ = MBEDTLS_BYTE_0(len); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&h, *p, len)); /* r */ + *p += len; + +cleanup: + mbedtls_ecp_point_free(&V); + mbedtls_mpi_free(&v); + mbedtls_mpi_free(&h); + + return ret; +} + +/* + * Parse a ECJPAKEKeyKP (7.4.2.2.1) and check proof + * Output: verified public key X + */ +static int ecjpake_kkp_read(const mbedtls_md_type_t md_type, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *G, + mbedtls_ecp_point *X, + const char *id, + const unsigned char **p, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (end < *p) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* + * struct { + * ECPoint X; + * ECSchnorrZKP zkp; + * } ECJPAKEKeyKP; + */ + MBEDTLS_MPI_CHK(mbedtls_ecp_tls_read_point(grp, X, p, end - *p)); + if (mbedtls_ecp_is_zero(X)) { + ret = MBEDTLS_ERR_ECP_INVALID_KEY; + goto cleanup; + } + + MBEDTLS_MPI_CHK(ecjpake_zkp_read(md_type, grp, pf, G, X, id, p, end)); + +cleanup: + return ret; +} + +/* + * Generate an ECJPAKEKeyKP + * Output: the serialized structure, plus private/public key pair + */ +static int ecjpake_kkp_write(const mbedtls_md_type_t md_type, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *G, + mbedtls_mpi *x, + mbedtls_ecp_point *X, + const char *id, + unsigned char **p, + const unsigned char *end, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if (end < *p) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + /* Generate key (7.4.2.3.1) and write it out */ + MBEDTLS_MPI_CHK(mbedtls_ecp_gen_keypair_base((mbedtls_ecp_group *) grp, G, x, X, + f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_ecp_tls_write_point(grp, X, + pf, &len, *p, end - *p)); + *p += len; + + /* Generate and write proof */ + MBEDTLS_MPI_CHK(ecjpake_zkp_write(md_type, grp, pf, G, x, X, id, + p, end, f_rng, p_rng)); + +cleanup: + return ret; +} + +/* + * Read a ECJPAKEKeyKPPairList (7.4.2.3) and check proofs + * Outputs: verified peer public keys Xa, Xb + */ +static int ecjpake_kkpp_read(const mbedtls_md_type_t md_type, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *G, + mbedtls_ecp_point *Xa, + mbedtls_ecp_point *Xb, + const char *id, + const unsigned char *buf, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + const unsigned char *end = buf + len; + + /* + * struct { + * ECJPAKEKeyKP ecjpake_key_kp_pair_list[2]; + * } ECJPAKEKeyKPPairList; + */ + MBEDTLS_MPI_CHK(ecjpake_kkp_read(md_type, grp, pf, G, Xa, id, &p, end)); + MBEDTLS_MPI_CHK(ecjpake_kkp_read(md_type, grp, pf, G, Xb, id, &p, end)); + + if (p != end) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + +cleanup: + return ret; +} + +/* + * Generate a ECJPAKEKeyKPPairList + * Outputs: the serialized structure, plus two private/public key pairs + */ +static int ecjpake_kkpp_write(const mbedtls_md_type_t md_type, + const mbedtls_ecp_group *grp, + const int pf, + const mbedtls_ecp_point *G, + mbedtls_mpi *xm1, + mbedtls_ecp_point *Xa, + mbedtls_mpi *xm2, + mbedtls_ecp_point *Xb, + const char *id, + unsigned char *buf, + size_t len, + size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + const unsigned char *end = buf + len; + + MBEDTLS_MPI_CHK(ecjpake_kkp_write(md_type, grp, pf, G, xm1, Xa, id, + &p, end, f_rng, p_rng)); + MBEDTLS_MPI_CHK(ecjpake_kkp_write(md_type, grp, pf, G, xm2, Xb, id, + &p, end, f_rng, p_rng)); + + *olen = p - buf; + +cleanup: + return ret; +} + +/* + * Read and process the first round message + */ +int mbedtls_ecjpake_read_round_one(mbedtls_ecjpake_context *ctx, + const unsigned char *buf, + size_t len) +{ + return ecjpake_kkpp_read(ctx->md_type, &ctx->grp, ctx->point_format, + &ctx->grp.G, + &ctx->Xp1, &ctx->Xp2, ID_PEER, + buf, len); +} + +/* + * Generate and write the first round message + */ +int mbedtls_ecjpake_write_round_one(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + return ecjpake_kkpp_write(ctx->md_type, &ctx->grp, ctx->point_format, + &ctx->grp.G, + &ctx->xm1, &ctx->Xm1, &ctx->xm2, &ctx->Xm2, + ID_MINE, buf, len, olen, f_rng, p_rng); +} + +/* + * Compute the sum of three points R = A + B + C + */ +static int ecjpake_ecp_add3(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_ecp_point *A, + const mbedtls_ecp_point *B, + const mbedtls_ecp_point *C) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi one; + + mbedtls_mpi_init(&one); + + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&one, 1)); + MBEDTLS_MPI_CHK(mbedtls_ecp_muladd(grp, R, &one, A, &one, B)); + MBEDTLS_MPI_CHK(mbedtls_ecp_muladd(grp, R, &one, R, &one, C)); + +cleanup: + mbedtls_mpi_free(&one); + + return ret; +} + +/* + * Read and process second round message (C: 7.4.2.5, S: 7.4.2.6) + */ +int mbedtls_ecjpake_read_round_two(mbedtls_ecjpake_context *ctx, + const unsigned char *buf, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + const unsigned char *end = buf + len; + mbedtls_ecp_group grp; + mbedtls_ecp_point G; /* C: GB, S: GA */ + + mbedtls_ecp_group_init(&grp); + mbedtls_ecp_point_init(&G); + + /* + * Server: GA = X3 + X4 + X1 (7.4.2.6.1) + * Client: GB = X1 + X2 + X3 (7.4.2.5.1) + * Unified: G = Xm1 + Xm2 + Xp1 + * We need that before parsing in order to check Xp as we read it + */ + MBEDTLS_MPI_CHK(ecjpake_ecp_add3(&ctx->grp, &G, + &ctx->Xm1, &ctx->Xm2, &ctx->Xp1)); + + /* + * struct { + * ECParameters curve_params; // only client reading server msg + * ECJPAKEKeyKP ecjpake_key_kp; + * } Client/ServerECJPAKEParams; + */ + if (ctx->role == MBEDTLS_ECJPAKE_CLIENT) { + MBEDTLS_MPI_CHK(mbedtls_ecp_tls_read_group(&grp, &p, len)); + if (grp.id != ctx->grp.id) { + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + goto cleanup; + } + } + + MBEDTLS_MPI_CHK(ecjpake_kkp_read(ctx->md_type, &ctx->grp, + ctx->point_format, + &G, &ctx->Xp, ID_PEER, &p, end)); + + if (p != end) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + +cleanup: + mbedtls_ecp_group_free(&grp); + mbedtls_ecp_point_free(&G); + + return ret; +} + +/* + * Compute R = +/- X * S mod N, taking care not to leak S + */ +static int ecjpake_mul_secret(mbedtls_mpi *R, int sign, + const mbedtls_mpi *X, + const mbedtls_mpi *S, + const mbedtls_mpi *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi b; /* Blinding value, then s + N * blinding */ + + mbedtls_mpi_init(&b); + + /* b = s + rnd-128-bit * N */ + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&b, 16, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&b, &b, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&b, &b, S)); + + /* R = sign * X * b mod N */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(R, X, &b)); + R->s *= sign; + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(R, R, N)); + +cleanup: + mbedtls_mpi_free(&b); + + return ret; +} + +/* + * Generate and write the second round message (S: 7.4.2.5, C: 7.4.2.6) + */ +int mbedtls_ecjpake_write_round_two(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point G; /* C: GA, S: GB */ + mbedtls_ecp_point Xm; /* C: Xc, S: Xs */ + mbedtls_mpi xm; /* C: xc, S: xs */ + unsigned char *p = buf; + const unsigned char *end = buf + len; + size_t ec_len; + + mbedtls_ecp_point_init(&G); + mbedtls_ecp_point_init(&Xm); + mbedtls_mpi_init(&xm); + + /* + * First generate private/public key pair (S: 7.4.2.5.1, C: 7.4.2.6.1) + * + * Client: GA = X1 + X3 + X4 | xs = x2 * s | Xc = xc * GA + * Server: GB = X3 + X1 + X2 | xs = x4 * s | Xs = xs * GB + * Unified: G = Xm1 + Xp1 + Xp2 | xm = xm2 * s | Xm = xm * G + */ + MBEDTLS_MPI_CHK(ecjpake_ecp_add3(&ctx->grp, &G, + &ctx->Xp1, &ctx->Xp2, &ctx->Xm1)); + MBEDTLS_MPI_CHK(ecjpake_mul_secret(&xm, 1, &ctx->xm2, &ctx->s, + &ctx->grp.N, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(&ctx->grp, &Xm, &xm, &G, f_rng, p_rng)); + + /* + * Now write things out + * + * struct { + * ECParameters curve_params; // only server writing its message + * ECJPAKEKeyKP ecjpake_key_kp; + * } Client/ServerECJPAKEParams; + */ + if (ctx->role == MBEDTLS_ECJPAKE_SERVER) { + if (end < p) { + ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + goto cleanup; + } + MBEDTLS_MPI_CHK(mbedtls_ecp_tls_write_group(&ctx->grp, &ec_len, + p, end - p)); + p += ec_len; + } + + if (end < p) { + ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + goto cleanup; + } + MBEDTLS_MPI_CHK(mbedtls_ecp_tls_write_point(&ctx->grp, &Xm, + ctx->point_format, &ec_len, p, end - p)); + p += ec_len; + + MBEDTLS_MPI_CHK(ecjpake_zkp_write(ctx->md_type, &ctx->grp, + ctx->point_format, + &G, &xm, &Xm, ID_MINE, + &p, end, f_rng, p_rng)); + + *olen = p - buf; + +cleanup: + mbedtls_ecp_point_free(&G); + mbedtls_ecp_point_free(&Xm); + mbedtls_mpi_free(&xm); + + return ret; +} + +/* + * Derive PMS (7.4.2.7 / 7.4.2.8) + */ +static int mbedtls_ecjpake_derive_k(mbedtls_ecjpake_context *ctx, + mbedtls_ecp_point *K, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi m_xm2_s, one; + + mbedtls_mpi_init(&m_xm2_s); + mbedtls_mpi_init(&one); + + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&one, 1)); + + /* + * Client: K = ( Xs - X4 * x2 * s ) * x2 + * Server: K = ( Xc - X2 * x4 * s ) * x4 + * Unified: K = ( Xp - Xp2 * xm2 * s ) * xm2 + */ + MBEDTLS_MPI_CHK(ecjpake_mul_secret(&m_xm2_s, -1, &ctx->xm2, &ctx->s, + &ctx->grp.N, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_ecp_muladd(&ctx->grp, K, + &one, &ctx->Xp, + &m_xm2_s, &ctx->Xp2)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(&ctx->grp, K, &ctx->xm2, K, + f_rng, p_rng)); + +cleanup: + mbedtls_mpi_free(&m_xm2_s); + mbedtls_mpi_free(&one); + + return ret; +} + +int mbedtls_ecjpake_derive_secret(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point K; + unsigned char kx[MBEDTLS_ECP_MAX_BYTES]; + size_t x_bytes; + + *olen = mbedtls_hash_info_get_size(ctx->md_type); + if (len < *olen) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + mbedtls_ecp_point_init(&K); + + ret = mbedtls_ecjpake_derive_k(ctx, &K, f_rng, p_rng); + if (ret) { + goto cleanup; + } + + /* PMS = SHA-256( K.X ) */ + x_bytes = (ctx->grp.pbits + 7) / 8; + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&K.X, kx, x_bytes)); + MBEDTLS_MPI_CHK(mbedtls_ecjpake_compute_hash(ctx->md_type, + kx, x_bytes, buf)); + +cleanup: + mbedtls_ecp_point_free(&K); + + return ret; +} + +int mbedtls_ecjpake_write_shared_key(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point K; + + mbedtls_ecp_point_init(&K); + + ret = mbedtls_ecjpake_derive_k(ctx, &K, f_rng, p_rng); + if (ret) { + goto cleanup; + } + + ret = mbedtls_ecp_point_write_binary(&ctx->grp, &K, ctx->point_format, + olen, buf, len); + if (ret != 0) { + goto cleanup; + } + +cleanup: + mbedtls_ecp_point_free(&K); + + return ret; +} + +#undef ID_MINE +#undef ID_PEER + +#endif /* ! MBEDTLS_ECJPAKE_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ + !defined(MBEDTLS_SHA256_C) +int mbedtls_ecjpake_self_test(int verbose) +{ + (void) verbose; + return 0; +} +#else + +static const unsigned char ecjpake_test_password[] = { + 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x6a, 0x70, 0x61, 0x6b, 0x65, 0x74, + 0x65, 0x73, 0x74 +}; + +#if !defined(MBEDTLS_ECJPAKE_ALT) + +static const unsigned char ecjpake_test_x1[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x21 +}; + +static const unsigned char ecjpake_test_x2[] = { + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x81 +}; + +static const unsigned char ecjpake_test_x3[] = { + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x81 +}; + +static const unsigned char ecjpake_test_x4[] = { + 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, + 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1 +}; + +static const unsigned char ecjpake_test_cli_one[] = { + 0x41, 0x04, 0xac, 0xcf, 0x01, 0x06, 0xef, 0x85, 0x8f, 0xa2, 0xd9, 0x19, + 0x33, 0x13, 0x46, 0x80, 0x5a, 0x78, 0xb5, 0x8b, 0xba, 0xd0, 0xb8, 0x44, + 0xe5, 0xc7, 0x89, 0x28, 0x79, 0x14, 0x61, 0x87, 0xdd, 0x26, 0x66, 0xad, + 0xa7, 0x81, 0xbb, 0x7f, 0x11, 0x13, 0x72, 0x25, 0x1a, 0x89, 0x10, 0x62, + 0x1f, 0x63, 0x4d, 0xf1, 0x28, 0xac, 0x48, 0xe3, 0x81, 0xfd, 0x6e, 0xf9, + 0x06, 0x07, 0x31, 0xf6, 0x94, 0xa4, 0x41, 0x04, 0x1d, 0xd0, 0xbd, 0x5d, + 0x45, 0x66, 0xc9, 0xbe, 0xd9, 0xce, 0x7d, 0xe7, 0x01, 0xb5, 0xe8, 0x2e, + 0x08, 0xe8, 0x4b, 0x73, 0x04, 0x66, 0x01, 0x8a, 0xb9, 0x03, 0xc7, 0x9e, + 0xb9, 0x82, 0x17, 0x22, 0x36, 0xc0, 0xc1, 0x72, 0x8a, 0xe4, 0xbf, 0x73, + 0x61, 0x0d, 0x34, 0xde, 0x44, 0x24, 0x6e, 0xf3, 0xd9, 0xc0, 0x5a, 0x22, + 0x36, 0xfb, 0x66, 0xa6, 0x58, 0x3d, 0x74, 0x49, 0x30, 0x8b, 0xab, 0xce, + 0x20, 0x72, 0xfe, 0x16, 0x66, 0x29, 0x92, 0xe9, 0x23, 0x5c, 0x25, 0x00, + 0x2f, 0x11, 0xb1, 0x50, 0x87, 0xb8, 0x27, 0x38, 0xe0, 0x3c, 0x94, 0x5b, + 0xf7, 0xa2, 0x99, 0x5d, 0xda, 0x1e, 0x98, 0x34, 0x58, 0x41, 0x04, 0x7e, + 0xa6, 0xe3, 0xa4, 0x48, 0x70, 0x37, 0xa9, 0xe0, 0xdb, 0xd7, 0x92, 0x62, + 0xb2, 0xcc, 0x27, 0x3e, 0x77, 0x99, 0x30, 0xfc, 0x18, 0x40, 0x9a, 0xc5, + 0x36, 0x1c, 0x5f, 0xe6, 0x69, 0xd7, 0x02, 0xe1, 0x47, 0x79, 0x0a, 0xeb, + 0x4c, 0xe7, 0xfd, 0x65, 0x75, 0xab, 0x0f, 0x6c, 0x7f, 0xd1, 0xc3, 0x35, + 0x93, 0x9a, 0xa8, 0x63, 0xba, 0x37, 0xec, 0x91, 0xb7, 0xe3, 0x2b, 0xb0, + 0x13, 0xbb, 0x2b, 0x41, 0x04, 0xa4, 0x95, 0x58, 0xd3, 0x2e, 0xd1, 0xeb, + 0xfc, 0x18, 0x16, 0xaf, 0x4f, 0xf0, 0x9b, 0x55, 0xfc, 0xb4, 0xca, 0x47, + 0xb2, 0xa0, 0x2d, 0x1e, 0x7c, 0xaf, 0x11, 0x79, 0xea, 0x3f, 0xe1, 0x39, + 0x5b, 0x22, 0xb8, 0x61, 0x96, 0x40, 0x16, 0xfa, 0xba, 0xf7, 0x2c, 0x97, + 0x56, 0x95, 0xd9, 0x3d, 0x4d, 0xf0, 0xe5, 0x19, 0x7f, 0xe9, 0xf0, 0x40, + 0x63, 0x4e, 0xd5, 0x97, 0x64, 0x93, 0x77, 0x87, 0xbe, 0x20, 0xbc, 0x4d, + 0xee, 0xbb, 0xf9, 0xb8, 0xd6, 0x0a, 0x33, 0x5f, 0x04, 0x6c, 0xa3, 0xaa, + 0x94, 0x1e, 0x45, 0x86, 0x4c, 0x7c, 0xad, 0xef, 0x9c, 0xf7, 0x5b, 0x3d, + 0x8b, 0x01, 0x0e, 0x44, 0x3e, 0xf0 +}; + +static const unsigned char ecjpake_test_srv_one[] = { + 0x41, 0x04, 0x7e, 0xa6, 0xe3, 0xa4, 0x48, 0x70, 0x37, 0xa9, 0xe0, 0xdb, + 0xd7, 0x92, 0x62, 0xb2, 0xcc, 0x27, 0x3e, 0x77, 0x99, 0x30, 0xfc, 0x18, + 0x40, 0x9a, 0xc5, 0x36, 0x1c, 0x5f, 0xe6, 0x69, 0xd7, 0x02, 0xe1, 0x47, + 0x79, 0x0a, 0xeb, 0x4c, 0xe7, 0xfd, 0x65, 0x75, 0xab, 0x0f, 0x6c, 0x7f, + 0xd1, 0xc3, 0x35, 0x93, 0x9a, 0xa8, 0x63, 0xba, 0x37, 0xec, 0x91, 0xb7, + 0xe3, 0x2b, 0xb0, 0x13, 0xbb, 0x2b, 0x41, 0x04, 0x09, 0xf8, 0x5b, 0x3d, + 0x20, 0xeb, 0xd7, 0x88, 0x5c, 0xe4, 0x64, 0xc0, 0x8d, 0x05, 0x6d, 0x64, + 0x28, 0xfe, 0x4d, 0xd9, 0x28, 0x7a, 0xa3, 0x65, 0xf1, 0x31, 0xf4, 0x36, + 0x0f, 0xf3, 0x86, 0xd8, 0x46, 0x89, 0x8b, 0xc4, 0xb4, 0x15, 0x83, 0xc2, + 0xa5, 0x19, 0x7f, 0x65, 0xd7, 0x87, 0x42, 0x74, 0x6c, 0x12, 0xa5, 0xec, + 0x0a, 0x4f, 0xfe, 0x2f, 0x27, 0x0a, 0x75, 0x0a, 0x1d, 0x8f, 0xb5, 0x16, + 0x20, 0x93, 0x4d, 0x74, 0xeb, 0x43, 0xe5, 0x4d, 0xf4, 0x24, 0xfd, 0x96, + 0x30, 0x6c, 0x01, 0x17, 0xbf, 0x13, 0x1a, 0xfa, 0xbf, 0x90, 0xa9, 0xd3, + 0x3d, 0x11, 0x98, 0xd9, 0x05, 0x19, 0x37, 0x35, 0x14, 0x41, 0x04, 0x19, + 0x0a, 0x07, 0x70, 0x0f, 0xfa, 0x4b, 0xe6, 0xae, 0x1d, 0x79, 0xee, 0x0f, + 0x06, 0xae, 0xb5, 0x44, 0xcd, 0x5a, 0xdd, 0xaa, 0xbe, 0xdf, 0x70, 0xf8, + 0x62, 0x33, 0x21, 0x33, 0x2c, 0x54, 0xf3, 0x55, 0xf0, 0xfb, 0xfe, 0xc7, + 0x83, 0xed, 0x35, 0x9e, 0x5d, 0x0b, 0xf7, 0x37, 0x7a, 0x0f, 0xc4, 0xea, + 0x7a, 0xce, 0x47, 0x3c, 0x9c, 0x11, 0x2b, 0x41, 0xcc, 0xd4, 0x1a, 0xc5, + 0x6a, 0x56, 0x12, 0x41, 0x04, 0x36, 0x0a, 0x1c, 0xea, 0x33, 0xfc, 0xe6, + 0x41, 0x15, 0x64, 0x58, 0xe0, 0xa4, 0xea, 0xc2, 0x19, 0xe9, 0x68, 0x31, + 0xe6, 0xae, 0xbc, 0x88, 0xb3, 0xf3, 0x75, 0x2f, 0x93, 0xa0, 0x28, 0x1d, + 0x1b, 0xf1, 0xfb, 0x10, 0x60, 0x51, 0xdb, 0x96, 0x94, 0xa8, 0xd6, 0xe8, + 0x62, 0xa5, 0xef, 0x13, 0x24, 0xa3, 0xd9, 0xe2, 0x78, 0x94, 0xf1, 0xee, + 0x4f, 0x7c, 0x59, 0x19, 0x99, 0x65, 0xa8, 0xdd, 0x4a, 0x20, 0x91, 0x84, + 0x7d, 0x2d, 0x22, 0xdf, 0x3e, 0xe5, 0x5f, 0xaa, 0x2a, 0x3f, 0xb3, 0x3f, + 0xd2, 0xd1, 0xe0, 0x55, 0xa0, 0x7a, 0x7c, 0x61, 0xec, 0xfb, 0x8d, 0x80, + 0xec, 0x00, 0xc2, 0xc9, 0xeb, 0x12 +}; + +static const unsigned char ecjpake_test_srv_two[] = { + 0x03, 0x00, 0x17, 0x41, 0x04, 0x0f, 0xb2, 0x2b, 0x1d, 0x5d, 0x11, 0x23, + 0xe0, 0xef, 0x9f, 0xeb, 0x9d, 0x8a, 0x2e, 0x59, 0x0a, 0x1f, 0x4d, 0x7c, + 0xed, 0x2c, 0x2b, 0x06, 0x58, 0x6e, 0x8f, 0x2a, 0x16, 0xd4, 0xeb, 0x2f, + 0xda, 0x43, 0x28, 0xa2, 0x0b, 0x07, 0xd8, 0xfd, 0x66, 0x76, 0x54, 0xca, + 0x18, 0xc5, 0x4e, 0x32, 0xa3, 0x33, 0xa0, 0x84, 0x54, 0x51, 0xe9, 0x26, + 0xee, 0x88, 0x04, 0xfd, 0x7a, 0xf0, 0xaa, 0xa7, 0xa6, 0x41, 0x04, 0x55, + 0x16, 0xea, 0x3e, 0x54, 0xa0, 0xd5, 0xd8, 0xb2, 0xce, 0x78, 0x6b, 0x38, + 0xd3, 0x83, 0x37, 0x00, 0x29, 0xa5, 0xdb, 0xe4, 0x45, 0x9c, 0x9d, 0xd6, + 0x01, 0xb4, 0x08, 0xa2, 0x4a, 0xe6, 0x46, 0x5c, 0x8a, 0xc9, 0x05, 0xb9, + 0xeb, 0x03, 0xb5, 0xd3, 0x69, 0x1c, 0x13, 0x9e, 0xf8, 0x3f, 0x1c, 0xd4, + 0x20, 0x0f, 0x6c, 0x9c, 0xd4, 0xec, 0x39, 0x22, 0x18, 0xa5, 0x9e, 0xd2, + 0x43, 0xd3, 0xc8, 0x20, 0xff, 0x72, 0x4a, 0x9a, 0x70, 0xb8, 0x8c, 0xb8, + 0x6f, 0x20, 0xb4, 0x34, 0xc6, 0x86, 0x5a, 0xa1, 0xcd, 0x79, 0x06, 0xdd, + 0x7c, 0x9b, 0xce, 0x35, 0x25, 0xf5, 0x08, 0x27, 0x6f, 0x26, 0x83, 0x6c +}; + +static const unsigned char ecjpake_test_cli_two[] = { + 0x41, 0x04, 0x69, 0xd5, 0x4e, 0xe8, 0x5e, 0x90, 0xce, 0x3f, 0x12, 0x46, + 0x74, 0x2d, 0xe5, 0x07, 0xe9, 0x39, 0xe8, 0x1d, 0x1d, 0xc1, 0xc5, 0xcb, + 0x98, 0x8b, 0x58, 0xc3, 0x10, 0xc9, 0xfd, 0xd9, 0x52, 0x4d, 0x93, 0x72, + 0x0b, 0x45, 0x54, 0x1c, 0x83, 0xee, 0x88, 0x41, 0x19, 0x1d, 0xa7, 0xce, + 0xd8, 0x6e, 0x33, 0x12, 0xd4, 0x36, 0x23, 0xc1, 0xd6, 0x3e, 0x74, 0x98, + 0x9a, 0xba, 0x4a, 0xff, 0xd1, 0xee, 0x41, 0x04, 0x07, 0x7e, 0x8c, 0x31, + 0xe2, 0x0e, 0x6b, 0xed, 0xb7, 0x60, 0xc1, 0x35, 0x93, 0xe6, 0x9f, 0x15, + 0xbe, 0x85, 0xc2, 0x7d, 0x68, 0xcd, 0x09, 0xcc, 0xb8, 0xc4, 0x18, 0x36, + 0x08, 0x91, 0x7c, 0x5c, 0x3d, 0x40, 0x9f, 0xac, 0x39, 0xfe, 0xfe, 0xe8, + 0x2f, 0x72, 0x92, 0xd3, 0x6f, 0x0d, 0x23, 0xe0, 0x55, 0x91, 0x3f, 0x45, + 0xa5, 0x2b, 0x85, 0xdd, 0x8a, 0x20, 0x52, 0xe9, 0xe1, 0x29, 0xbb, 0x4d, + 0x20, 0x0f, 0x01, 0x1f, 0x19, 0x48, 0x35, 0x35, 0xa6, 0xe8, 0x9a, 0x58, + 0x0c, 0x9b, 0x00, 0x03, 0xba, 0xf2, 0x14, 0x62, 0xec, 0xe9, 0x1a, 0x82, + 0xcc, 0x38, 0xdb, 0xdc, 0xae, 0x60, 0xd9, 0xc5, 0x4c +}; + +static const unsigned char ecjpake_test_shared_key[] = { + 0x04, 0x01, 0xab, 0xe9, 0xf2, 0xc7, 0x3a, 0x99, 0x14, 0xcb, 0x1f, 0x80, + 0xfb, 0x9d, 0xdb, 0x7e, 0x00, 0x12, 0xa8, 0x9c, 0x2f, 0x39, 0x27, 0x79, + 0xf9, 0x64, 0x40, 0x14, 0x75, 0xea, 0xc1, 0x31, 0x28, 0x43, 0x8f, 0xe1, + 0x12, 0x41, 0xd6, 0xc1, 0xe5, 0x5f, 0x7b, 0x80, 0x88, 0x94, 0xc9, 0xc0, + 0x27, 0xa3, 0x34, 0x41, 0xf5, 0xcb, 0xa1, 0xfe, 0x6c, 0xc7, 0xe6, 0x12, + 0x17, 0xc3, 0xde, 0x27, 0xb4, +}; + +static const unsigned char ecjpake_test_pms[] = { + 0xf3, 0xd4, 0x7f, 0x59, 0x98, 0x44, 0xdb, 0x92, 0xa5, 0x69, 0xbb, 0xe7, + 0x98, 0x1e, 0x39, 0xd9, 0x31, 0xfd, 0x74, 0x3b, 0xf2, 0x2e, 0x98, 0xf9, + 0xb4, 0x38, 0xf7, 0x19, 0xd3, 0xc4, 0xf3, 0x51 +}; + +/* + * PRNG for test - !!!INSECURE NEVER USE IN PRODUCTION!!! + * + * This is the linear congruential generator from numerical recipes, + * except we only use the low byte as the output. See + * https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use + */ +static int self_test_rng(void *ctx, unsigned char *out, size_t len) +{ + static uint32_t state = 42; + + (void) ctx; + + for (size_t i = 0; i < len; i++) { + state = state * 1664525u + 1013904223u; + out[i] = (unsigned char) state; + } + + return 0; +} + +/* Load my private keys and generate the corresponding public keys */ +static int ecjpake_test_load(mbedtls_ecjpake_context *ctx, + const unsigned char *xm1, size_t len1, + const unsigned char *xm2, size_t len2) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->xm1, xm1, len1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->xm2, xm2, len2)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(&ctx->grp, &ctx->Xm1, &ctx->xm1, + &ctx->grp.G, self_test_rng, NULL)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(&ctx->grp, &ctx->Xm2, &ctx->xm2, + &ctx->grp.G, self_test_rng, NULL)); + +cleanup: + return ret; +} + +#endif /* ! MBEDTLS_ECJPAKE_ALT */ + +/* For tests we don't need a secure RNG; + * use the LGC from Numerical Recipes for simplicity */ +static int ecjpake_lgc(void *p, unsigned char *out, size_t len) +{ + static uint32_t x = 42; + (void) p; + + while (len > 0) { + size_t use_len = len > 4 ? 4 : len; + x = 1664525 * x + 1013904223; + memcpy(out, &x, use_len); + out += use_len; + len -= use_len; + } + + return 0; +} + +#define TEST_ASSERT(x) \ + do { \ + if (x) \ + ret = 0; \ + else \ + { \ + ret = 1; \ + goto cleanup; \ + } \ + } while (0) + +/* + * Checkup routine + */ +int mbedtls_ecjpake_self_test(int verbose) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecjpake_context cli; + mbedtls_ecjpake_context srv; + unsigned char buf[512], pms[32]; + size_t len, pmslen; + + mbedtls_ecjpake_init(&cli); + mbedtls_ecjpake_init(&srv); + + if (verbose != 0) { + mbedtls_printf(" ECJPAKE test #0 (setup): "); + } + + TEST_ASSERT(mbedtls_ecjpake_setup(&cli, MBEDTLS_ECJPAKE_CLIENT, + MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1, + ecjpake_test_password, + sizeof(ecjpake_test_password)) == 0); + + TEST_ASSERT(mbedtls_ecjpake_setup(&srv, MBEDTLS_ECJPAKE_SERVER, + MBEDTLS_MD_SHA256, MBEDTLS_ECP_DP_SECP256R1, + ecjpake_test_password, + sizeof(ecjpake_test_password)) == 0); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + if (verbose != 0) { + mbedtls_printf(" ECJPAKE test #1 (random handshake): "); + } + + TEST_ASSERT(mbedtls_ecjpake_write_round_one(&cli, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(mbedtls_ecjpake_read_round_one(&srv, buf, len) == 0); + + TEST_ASSERT(mbedtls_ecjpake_write_round_one(&srv, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(mbedtls_ecjpake_read_round_one(&cli, buf, len) == 0); + + TEST_ASSERT(mbedtls_ecjpake_write_round_two(&srv, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(mbedtls_ecjpake_read_round_two(&cli, buf, len) == 0); + + TEST_ASSERT(mbedtls_ecjpake_derive_secret(&cli, + pms, sizeof(pms), &pmslen, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(mbedtls_ecjpake_write_round_two(&cli, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(mbedtls_ecjpake_read_round_two(&srv, buf, len) == 0); + + TEST_ASSERT(mbedtls_ecjpake_derive_secret(&srv, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(len == pmslen); + TEST_ASSERT(memcmp(buf, pms, len) == 0); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + +#if !defined(MBEDTLS_ECJPAKE_ALT) + /* 'reference handshake' tests can only be run against implementations + * for which we have 100% control over how the random ephemeral keys + * are generated. This is only the case for the internal mbed TLS + * implementation, so these tests are skipped in case the internal + * implementation is swapped out for an alternative one. */ + if (verbose != 0) { + mbedtls_printf(" ECJPAKE test #2 (reference handshake): "); + } + + /* Simulate generation of round one */ + MBEDTLS_MPI_CHK(ecjpake_test_load(&cli, + ecjpake_test_x1, sizeof(ecjpake_test_x1), + ecjpake_test_x2, sizeof(ecjpake_test_x2))); + + MBEDTLS_MPI_CHK(ecjpake_test_load(&srv, + ecjpake_test_x3, sizeof(ecjpake_test_x3), + ecjpake_test_x4, sizeof(ecjpake_test_x4))); + + /* Read round one */ + TEST_ASSERT(mbedtls_ecjpake_read_round_one(&srv, + ecjpake_test_cli_one, + sizeof(ecjpake_test_cli_one)) == 0); + + TEST_ASSERT(mbedtls_ecjpake_read_round_one(&cli, + ecjpake_test_srv_one, + sizeof(ecjpake_test_srv_one)) == 0); + + /* Skip generation of round two, read round two */ + TEST_ASSERT(mbedtls_ecjpake_read_round_two(&cli, + ecjpake_test_srv_two, + sizeof(ecjpake_test_srv_two)) == 0); + + TEST_ASSERT(mbedtls_ecjpake_read_round_two(&srv, + ecjpake_test_cli_two, + sizeof(ecjpake_test_cli_two)) == 0); + + /* Server derives PMS */ + TEST_ASSERT(mbedtls_ecjpake_derive_secret(&srv, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(len == sizeof(ecjpake_test_pms)); + TEST_ASSERT(memcmp(buf, ecjpake_test_pms, len) == 0); + + /* Server derives K as unsigned binary data */ + TEST_ASSERT(mbedtls_ecjpake_write_shared_key(&srv, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(len == sizeof(ecjpake_test_shared_key)); + TEST_ASSERT(memcmp(buf, ecjpake_test_shared_key, len) == 0); + + memset(buf, 0, len); /* Avoid interferences with next step */ + + /* Client derives PMS */ + TEST_ASSERT(mbedtls_ecjpake_derive_secret(&cli, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(len == sizeof(ecjpake_test_pms)); + TEST_ASSERT(memcmp(buf, ecjpake_test_pms, len) == 0); + + /* Client derives K as unsigned binary data */ + TEST_ASSERT(mbedtls_ecjpake_write_shared_key(&cli, + buf, sizeof(buf), &len, ecjpake_lgc, NULL) == 0); + + TEST_ASSERT(len == sizeof(ecjpake_test_shared_key)); + TEST_ASSERT(memcmp(buf, ecjpake_test_shared_key, len) == 0); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } +#endif /* ! MBEDTLS_ECJPAKE_ALT */ + +cleanup: + mbedtls_ecjpake_free(&cli); + mbedtls_ecjpake_free(&srv); + + if (ret != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return ret; +} + +#undef TEST_ASSERT + +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED && MBEDTLS_SHA256_C */ + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_ECJPAKE_C */ diff --git a/r5dev/thirdparty/mbedtls/ecp.c b/r5dev/thirdparty/mbedtls/ecp.c new file mode 100644 index 00000000..08fbe86c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ecp.c @@ -0,0 +1,3618 @@ +/* + * Elliptic curves over GF(p): generic functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * References: + * + * SEC1 http://www.secg.org/index.php?action=secg,docs_secg + * GECC = Guide to Elliptic Curve Cryptography - Hankerson, Menezes, Vanstone + * FIPS 186-3 http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf + * RFC 4492 for the related TLS structures and constants + * RFC 7748 for the Curve448 and Curve25519 curve definitions + * + * [Curve25519] http://cr.yp.to/ecdh/curve25519-20060209.pdf + * + * [2] CORON, Jean-S'ebastien. Resistance against differential power analysis + * for elliptic curve cryptosystems. In : Cryptographic Hardware and + * Embedded Systems. Springer Berlin Heidelberg, 1999. p. 292-302. + * + * + * [3] HEDABOU, Mustapha, PINEL, Pierre, et B'EN'ETEAU, Lucien. A comb method to + * render ECC resistant against Side Channel Attacks. IACR Cryptology + * ePrint Archive, 2004, vol. 2004, p. 342. + * + */ + +#include "common.h" + +/** + * \brief Function level alternative implementation. + * + * The MBEDTLS_ECP_INTERNAL_ALT macro enables alternative implementations to + * replace certain functions in this module. The alternative implementations are + * typically hardware accelerators and need to activate the hardware before the + * computation starts and deactivate it after it finishes. The + * mbedtls_internal_ecp_init() and mbedtls_internal_ecp_free() functions serve + * this purpose. + * + * To preserve the correct functionality the following conditions must hold: + * + * - The alternative implementation must be activated by + * mbedtls_internal_ecp_init() before any of the replaceable functions is + * called. + * - mbedtls_internal_ecp_free() must \b only be called when the alternative + * implementation is activated. + * - mbedtls_internal_ecp_init() must \b not be called when the alternative + * implementation is activated. + * - Public functions must not return while the alternative implementation is + * activated. + * - Replaceable functions are guarded by \c MBEDTLS_ECP_XXX_ALT macros and + * before calling them an \code if( mbedtls_internal_ecp_grp_capable( grp ) ) + * \endcode ensures that the alternative implementation supports the current + * group. + */ +#if defined(MBEDTLS_ECP_INTERNAL_ALT) +#endif + +#if defined(MBEDTLS_ECP_C) + +#include "mbedtls/ecp.h" +#include "mbedtls/threading.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include "bn_mul.h" +#include "ecp_invasive.h" + +#include + +#if !defined(MBEDTLS_ECP_ALT) + +#include "mbedtls/platform.h" + +#include "ecp_internal_alt.h" + +#if defined(MBEDTLS_SELF_TEST) +/* + * Counts of point addition and doubling, and field multiplications. + * Used to test resistance of point multiplication to simple timing attacks. + */ +static unsigned long add_count, dbl_count, mul_count; +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/* + * Maximum number of "basic operations" to be done in a row. + * + * Default value 0 means that ECC operations will not yield. + * Note that regardless of the value of ecp_max_ops, always at + * least one step is performed before yielding. + * + * Setting ecp_max_ops=1 can be suitable for testing purposes + * as it will interrupt computation at all possible points. + */ +static unsigned ecp_max_ops = 0; + +/* + * Set ecp_max_ops + */ +void mbedtls_ecp_set_max_ops(unsigned max_ops) +{ + ecp_max_ops = max_ops; +} + +/* + * Check if restart is enabled + */ +int mbedtls_ecp_restart_is_enabled(void) +{ + return ecp_max_ops != 0; +} + +/* + * Restart sub-context for ecp_mul_comb() + */ +struct mbedtls_ecp_restart_mul { + mbedtls_ecp_point R; /* current intermediate result */ + size_t i; /* current index in various loops, 0 outside */ + mbedtls_ecp_point *T; /* table for precomputed points */ + unsigned char T_size; /* number of points in table T */ + enum { /* what were we doing last time we returned? */ + ecp_rsm_init = 0, /* nothing so far, dummy initial state */ + ecp_rsm_pre_dbl, /* precompute 2^n multiples */ + ecp_rsm_pre_norm_dbl, /* normalize precomputed 2^n multiples */ + ecp_rsm_pre_add, /* precompute remaining points by adding */ + ecp_rsm_pre_norm_add, /* normalize all precomputed points */ + ecp_rsm_comb_core, /* ecp_mul_comb_core() */ + ecp_rsm_final_norm, /* do the final normalization */ + } state; +}; + +/* + * Init restart_mul sub-context + */ +static void ecp_restart_rsm_init(mbedtls_ecp_restart_mul_ctx *ctx) +{ + mbedtls_ecp_point_init(&ctx->R); + ctx->i = 0; + ctx->T = NULL; + ctx->T_size = 0; + ctx->state = ecp_rsm_init; +} + +/* + * Free the components of a restart_mul sub-context + */ +static void ecp_restart_rsm_free(mbedtls_ecp_restart_mul_ctx *ctx) +{ + unsigned char i; + + if (ctx == NULL) { + return; + } + + mbedtls_ecp_point_free(&ctx->R); + + if (ctx->T != NULL) { + for (i = 0; i < ctx->T_size; i++) { + mbedtls_ecp_point_free(ctx->T + i); + } + mbedtls_free(ctx->T); + } + + ecp_restart_rsm_init(ctx); +} + +/* + * Restart context for ecp_muladd() + */ +struct mbedtls_ecp_restart_muladd { + mbedtls_ecp_point mP; /* mP value */ + mbedtls_ecp_point R; /* R intermediate result */ + enum { /* what should we do next? */ + ecp_rsma_mul1 = 0, /* first multiplication */ + ecp_rsma_mul2, /* second multiplication */ + ecp_rsma_add, /* addition */ + ecp_rsma_norm, /* normalization */ + } state; +}; + +/* + * Init restart_muladd sub-context + */ +static void ecp_restart_ma_init(mbedtls_ecp_restart_muladd_ctx *ctx) +{ + mbedtls_ecp_point_init(&ctx->mP); + mbedtls_ecp_point_init(&ctx->R); + ctx->state = ecp_rsma_mul1; +} + +/* + * Free the components of a restart_muladd sub-context + */ +static void ecp_restart_ma_free(mbedtls_ecp_restart_muladd_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_ecp_point_free(&ctx->mP); + mbedtls_ecp_point_free(&ctx->R); + + ecp_restart_ma_init(ctx); +} + +/* + * Initialize a restart context + */ +void mbedtls_ecp_restart_init(mbedtls_ecp_restart_ctx *ctx) +{ + ctx->ops_done = 0; + ctx->depth = 0; + ctx->rsm = NULL; + ctx->ma = NULL; +} + +/* + * Free the components of a restart context + */ +void mbedtls_ecp_restart_free(mbedtls_ecp_restart_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + + ecp_restart_rsm_free(ctx->rsm); + mbedtls_free(ctx->rsm); + + ecp_restart_ma_free(ctx->ma); + mbedtls_free(ctx->ma); + + mbedtls_ecp_restart_init(ctx); +} + +/* + * Check if we can do the next step + */ +int mbedtls_ecp_check_budget(const mbedtls_ecp_group *grp, + mbedtls_ecp_restart_ctx *rs_ctx, + unsigned ops) +{ + if (rs_ctx != NULL && ecp_max_ops != 0) { + /* scale depending on curve size: the chosen reference is 256-bit, + * and multiplication is quadratic. Round to the closest integer. */ + if (grp->pbits >= 512) { + ops *= 4; + } else if (grp->pbits >= 384) { + ops *= 2; + } + + /* Avoid infinite loops: always allow first step. + * Because of that, however, it's not generally true + * that ops_done <= ecp_max_ops, so the check + * ops_done > ecp_max_ops below is mandatory. */ + if ((rs_ctx->ops_done != 0) && + (rs_ctx->ops_done > ecp_max_ops || + ops > ecp_max_ops - rs_ctx->ops_done)) { + return MBEDTLS_ERR_ECP_IN_PROGRESS; + } + + /* update running count */ + rs_ctx->ops_done += ops; + } + + return 0; +} + +/* Call this when entering a function that needs its own sub-context */ +#define ECP_RS_ENTER(SUB) do { \ + /* reset ops count for this call if top-level */ \ + if (rs_ctx != NULL && rs_ctx->depth++ == 0) \ + rs_ctx->ops_done = 0; \ + \ + /* set up our own sub-context if needed */ \ + if (mbedtls_ecp_restart_is_enabled() && \ + rs_ctx != NULL && rs_ctx->SUB == NULL) \ + { \ + rs_ctx->SUB = mbedtls_calloc(1, sizeof(*rs_ctx->SUB)); \ + if (rs_ctx->SUB == NULL) \ + return MBEDTLS_ERR_ECP_ALLOC_FAILED; \ + \ + ecp_restart_## SUB ##_init(rs_ctx->SUB); \ + } \ +} while (0) + +/* Call this when leaving a function that needs its own sub-context */ +#define ECP_RS_LEAVE(SUB) do { \ + /* clear our sub-context when not in progress (done or error) */ \ + if (rs_ctx != NULL && rs_ctx->SUB != NULL && \ + ret != MBEDTLS_ERR_ECP_IN_PROGRESS) \ + { \ + ecp_restart_## SUB ##_free(rs_ctx->SUB); \ + mbedtls_free(rs_ctx->SUB); \ + rs_ctx->SUB = NULL; \ + } \ + \ + if (rs_ctx != NULL) \ + rs_ctx->depth--; \ +} while (0) + +#else /* MBEDTLS_ECP_RESTARTABLE */ + +#define ECP_RS_ENTER(sub) (void) rs_ctx; +#define ECP_RS_LEAVE(sub) (void) rs_ctx; + +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +static void mpi_init_many(mbedtls_mpi *arr, size_t size) +{ + while (size--) { + mbedtls_mpi_init(arr++); + } +} + +static void mpi_free_many(mbedtls_mpi *arr, size_t size) +{ + while (size--) { + mbedtls_mpi_free(arr++); + } +} + +/* + * List of supported curves: + * - internal ID + * - TLS NamedCurve ID (RFC 4492 sec. 5.1.1, RFC 7071 sec. 2, RFC 8446 sec. 4.2.7) + * - size in bits + * - readable name + * + * Curves are listed in order: largest curves first, and for a given size, + * fastest curves first. + * + * Reminder: update profiles in x509_crt.c and ssl_tls.c when adding a new curve! + */ +static const mbedtls_ecp_curve_info ecp_supported_curves[] = +{ +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + { MBEDTLS_ECP_DP_SECP521R1, 25, 521, "secp521r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) + { MBEDTLS_ECP_DP_BP512R1, 28, 512, "brainpoolP512r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + { MBEDTLS_ECP_DP_SECP384R1, 24, 384, "secp384r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) + { MBEDTLS_ECP_DP_BP384R1, 27, 384, "brainpoolP384r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + { MBEDTLS_ECP_DP_SECP256R1, 23, 256, "secp256r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) + { MBEDTLS_ECP_DP_SECP256K1, 22, 256, "secp256k1" }, +#endif +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) + { MBEDTLS_ECP_DP_BP256R1, 26, 256, "brainpoolP256r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + { MBEDTLS_ECP_DP_SECP224R1, 21, 224, "secp224r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) + { MBEDTLS_ECP_DP_SECP224K1, 20, 224, "secp224k1" }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + { MBEDTLS_ECP_DP_SECP192R1, 19, 192, "secp192r1" }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) + { MBEDTLS_ECP_DP_SECP192K1, 18, 192, "secp192k1" }, +#endif +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + { MBEDTLS_ECP_DP_CURVE25519, 29, 256, "x25519" }, +#endif +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + { MBEDTLS_ECP_DP_CURVE448, 30, 448, "x448" }, +#endif + { MBEDTLS_ECP_DP_NONE, 0, 0, NULL }, +}; + +#define ECP_NB_CURVES sizeof(ecp_supported_curves) / \ + sizeof(ecp_supported_curves[0]) + +static mbedtls_ecp_group_id ecp_supported_grp_id[ECP_NB_CURVES]; + +/* + * List of supported curves and associated info + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_list(void) +{ + return ecp_supported_curves; +} + +/* + * List of supported curves, group ID only + */ +const mbedtls_ecp_group_id *mbedtls_ecp_grp_id_list(void) +{ + static int init_done = 0; + + if (!init_done) { + size_t i = 0; + const mbedtls_ecp_curve_info *curve_info; + + for (curve_info = mbedtls_ecp_curve_list(); + curve_info->grp_id != MBEDTLS_ECP_DP_NONE; + curve_info++) { + ecp_supported_grp_id[i++] = curve_info->grp_id; + } + ecp_supported_grp_id[i] = MBEDTLS_ECP_DP_NONE; + + init_done = 1; + } + + return ecp_supported_grp_id; +} + +/* + * Get the curve info for the internal identifier + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_grp_id(mbedtls_ecp_group_id grp_id) +{ + const mbedtls_ecp_curve_info *curve_info; + + for (curve_info = mbedtls_ecp_curve_list(); + curve_info->grp_id != MBEDTLS_ECP_DP_NONE; + curve_info++) { + if (curve_info->grp_id == grp_id) { + return curve_info; + } + } + + return NULL; +} + +/* + * Get the curve info from the TLS identifier + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_tls_id(uint16_t tls_id) +{ + const mbedtls_ecp_curve_info *curve_info; + + for (curve_info = mbedtls_ecp_curve_list(); + curve_info->grp_id != MBEDTLS_ECP_DP_NONE; + curve_info++) { + if (curve_info->tls_id == tls_id) { + return curve_info; + } + } + + return NULL; +} + +/* + * Get the curve info from the name + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_name(const char *name) +{ + const mbedtls_ecp_curve_info *curve_info; + + if (name == NULL) { + return NULL; + } + + for (curve_info = mbedtls_ecp_curve_list(); + curve_info->grp_id != MBEDTLS_ECP_DP_NONE; + curve_info++) { + if (strcmp(curve_info->name, name) == 0) { + return curve_info; + } + } + + return NULL; +} + +/* + * Get the type of a curve + */ +mbedtls_ecp_curve_type mbedtls_ecp_get_type(const mbedtls_ecp_group *grp) +{ + if (grp->G.X.p == NULL) { + return MBEDTLS_ECP_TYPE_NONE; + } + + if (grp->G.Y.p == NULL) { + return MBEDTLS_ECP_TYPE_MONTGOMERY; + } else { + return MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS; + } +} + +/* + * Initialize (the components of) a point + */ +void mbedtls_ecp_point_init(mbedtls_ecp_point *pt) +{ + mbedtls_mpi_init(&pt->X); + mbedtls_mpi_init(&pt->Y); + mbedtls_mpi_init(&pt->Z); +} + +/* + * Initialize (the components of) a group + */ +void mbedtls_ecp_group_init(mbedtls_ecp_group *grp) +{ + grp->id = MBEDTLS_ECP_DP_NONE; + mbedtls_mpi_init(&grp->P); + mbedtls_mpi_init(&grp->A); + mbedtls_mpi_init(&grp->B); + mbedtls_ecp_point_init(&grp->G); + mbedtls_mpi_init(&grp->N); + grp->pbits = 0; + grp->nbits = 0; + grp->h = 0; + grp->modp = NULL; + grp->t_pre = NULL; + grp->t_post = NULL; + grp->t_data = NULL; + grp->T = NULL; + grp->T_size = 0; +} + +/* + * Initialize (the components of) a key pair + */ +void mbedtls_ecp_keypair_init(mbedtls_ecp_keypair *key) +{ + mbedtls_ecp_group_init(&key->grp); + mbedtls_mpi_init(&key->d); + mbedtls_ecp_point_init(&key->Q); +} + +/* + * Unallocate (the components of) a point + */ +void mbedtls_ecp_point_free(mbedtls_ecp_point *pt) +{ + if (pt == NULL) { + return; + } + + mbedtls_mpi_free(&(pt->X)); + mbedtls_mpi_free(&(pt->Y)); + mbedtls_mpi_free(&(pt->Z)); +} + +/* + * Check that the comb table (grp->T) is static initialized. + */ +static int ecp_group_is_static_comb_table(const mbedtls_ecp_group *grp) +{ +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 + return grp->T != NULL && grp->T_size == 0; +#else + (void) grp; + return 0; +#endif +} + +/* + * Unallocate (the components of) a group + */ +void mbedtls_ecp_group_free(mbedtls_ecp_group *grp) +{ + size_t i; + + if (grp == NULL) { + return; + } + + if (grp->h != 1) { + mbedtls_mpi_free(&grp->A); + mbedtls_mpi_free(&grp->B); + mbedtls_ecp_point_free(&grp->G); + } + + if (!ecp_group_is_static_comb_table(grp) && grp->T != NULL) { + for (i = 0; i < grp->T_size; i++) { + mbedtls_ecp_point_free(&grp->T[i]); + } + mbedtls_free(grp->T); + } + + mbedtls_platform_zeroize(grp, sizeof(mbedtls_ecp_group)); +} + +/* + * Unallocate (the components of) a key pair + */ +void mbedtls_ecp_keypair_free(mbedtls_ecp_keypair *key) +{ + if (key == NULL) { + return; + } + + mbedtls_ecp_group_free(&key->grp); + mbedtls_mpi_free(&key->d); + mbedtls_ecp_point_free(&key->Q); +} + +/* + * Copy the contents of a point + */ +int mbedtls_ecp_copy(mbedtls_ecp_point *P, const mbedtls_ecp_point *Q) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&P->X, &Q->X)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&P->Y, &Q->Y)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&P->Z, &Q->Z)); + +cleanup: + return ret; +} + +/* + * Copy the contents of a group object + */ +int mbedtls_ecp_group_copy(mbedtls_ecp_group *dst, const mbedtls_ecp_group *src) +{ + return mbedtls_ecp_group_load(dst, src->id); +} + +/* + * Set point to zero + */ +int mbedtls_ecp_set_zero(mbedtls_ecp_point *pt) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&pt->X, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&pt->Y, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&pt->Z, 0)); + +cleanup: + return ret; +} + +/* + * Tell if a point is zero + */ +int mbedtls_ecp_is_zero(mbedtls_ecp_point *pt) +{ + return mbedtls_mpi_cmp_int(&pt->Z, 0) == 0; +} + +/* + * Compare two points lazily + */ +int mbedtls_ecp_point_cmp(const mbedtls_ecp_point *P, + const mbedtls_ecp_point *Q) +{ + if (mbedtls_mpi_cmp_mpi(&P->X, &Q->X) == 0 && + mbedtls_mpi_cmp_mpi(&P->Y, &Q->Y) == 0 && + mbedtls_mpi_cmp_mpi(&P->Z, &Q->Z) == 0) { + return 0; + } + + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; +} + +/* + * Import a non-zero point from ASCII strings + */ +int mbedtls_ecp_point_read_string(mbedtls_ecp_point *P, int radix, + const char *x, const char *y) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&P->X, radix, x)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&P->Y, radix, y)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&P->Z, 1)); + +cleanup: + return ret; +} + +/* + * Export a point into unsigned binary data (SEC1 2.3.3 and RFC7748) + */ +int mbedtls_ecp_point_write_binary(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *P, + int format, size_t *olen, + unsigned char *buf, size_t buflen) +{ + int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + size_t plen; + if (format != MBEDTLS_ECP_PF_UNCOMPRESSED && + format != MBEDTLS_ECP_PF_COMPRESSED) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + plen = mbedtls_mpi_size(&grp->P); + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + (void) format; /* Montgomery curves always use the same point format */ + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + *olen = plen; + if (buflen < *olen) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary_le(&P->X, buf, plen)); + } +#endif +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + /* + * Common case: P == 0 + */ + if (mbedtls_mpi_cmp_int(&P->Z, 0) == 0) { + if (buflen < 1) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + buf[0] = 0x00; + *olen = 1; + + return 0; + } + + if (format == MBEDTLS_ECP_PF_UNCOMPRESSED) { + *olen = 2 * plen + 1; + + if (buflen < *olen) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + buf[0] = 0x04; + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&P->X, buf + 1, plen)); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&P->Y, buf + 1 + plen, plen)); + } else if (format == MBEDTLS_ECP_PF_COMPRESSED) { + *olen = plen + 1; + + if (buflen < *olen) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + buf[0] = 0x02 + mbedtls_mpi_get_bit(&P->Y, 0); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&P->X, buf + 1, plen)); + } + } +#endif + +cleanup: + return ret; +} + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +static int mbedtls_ecp_sw_derive_y(const mbedtls_ecp_group *grp, + const mbedtls_mpi *X, + mbedtls_mpi *Y, + int parity_bit); +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +/* + * Import a point from unsigned binary data (SEC1 2.3.4 and RFC7748) + */ +int mbedtls_ecp_point_read_binary(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt, + const unsigned char *buf, size_t ilen) +{ + int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + size_t plen; + if (ilen < 1) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + plen = mbedtls_mpi_size(&grp->P); + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + if (plen != ilen) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary_le(&pt->X, buf, plen)); + mbedtls_mpi_free(&pt->Y); + + if (grp->id == MBEDTLS_ECP_DP_CURVE25519) { + /* Set most significant bit to 0 as prescribed in RFC7748 §5 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&pt->X, plen * 8 - 1, 0)); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&pt->Z, 1)); + } +#endif +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + if (buf[0] == 0x00) { + if (ilen == 1) { + return mbedtls_ecp_set_zero(pt); + } else { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + } + + if (ilen < 1 + plen) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&pt->X, buf + 1, plen)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&pt->Z, 1)); + + if (buf[0] == 0x04) { + /* format == MBEDTLS_ECP_PF_UNCOMPRESSED */ + if (ilen != 1 + plen * 2) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + return mbedtls_mpi_read_binary(&pt->Y, buf + 1 + plen, plen); + } else if (buf[0] == 0x02 || buf[0] == 0x03) { + /* format == MBEDTLS_ECP_PF_COMPRESSED */ + if (ilen != 1 + plen) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + return mbedtls_ecp_sw_derive_y(grp, &pt->X, &pt->Y, + (buf[0] & 1)); + } else { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + } +#endif + +cleanup: + return ret; +} + +/* + * Import a point from a TLS ECPoint record (RFC 4492) + * struct { + * opaque point <1..2^8-1>; + * } ECPoint; + */ +int mbedtls_ecp_tls_read_point(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt, + const unsigned char **buf, size_t buf_len) +{ + unsigned char data_len; + const unsigned char *buf_start; + /* + * We must have at least two bytes (1 for length, at least one for data) + */ + if (buf_len < 2) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + data_len = *(*buf)++; + if (data_len < 1 || data_len > buf_len - 1) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* + * Save buffer start for read_binary and update buf + */ + buf_start = *buf; + *buf += data_len; + + return mbedtls_ecp_point_read_binary(grp, pt, buf_start, data_len); +} + +/* + * Export a point as a TLS ECPoint record (RFC 4492) + * struct { + * opaque point <1..2^8-1>; + * } ECPoint; + */ +int mbedtls_ecp_tls_write_point(const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt, + int format, size_t *olen, + unsigned char *buf, size_t blen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + if (format != MBEDTLS_ECP_PF_UNCOMPRESSED && + format != MBEDTLS_ECP_PF_COMPRESSED) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* + * buffer length must be at least one, for our length byte + */ + if (blen < 1) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + if ((ret = mbedtls_ecp_point_write_binary(grp, pt, format, + olen, buf + 1, blen - 1)) != 0) { + return ret; + } + + /* + * write length to the first byte and update total length + */ + buf[0] = (unsigned char) *olen; + ++*olen; + + return 0; +} + +/* + * Set a group from an ECParameters record (RFC 4492) + */ +int mbedtls_ecp_tls_read_group(mbedtls_ecp_group *grp, + const unsigned char **buf, size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_group_id grp_id; + if ((ret = mbedtls_ecp_tls_read_group_id(&grp_id, buf, len)) != 0) { + return ret; + } + + return mbedtls_ecp_group_load(grp, grp_id); +} + +/* + * Read a group id from an ECParameters record (RFC 4492) and convert it to + * mbedtls_ecp_group_id. + */ +int mbedtls_ecp_tls_read_group_id(mbedtls_ecp_group_id *grp, + const unsigned char **buf, size_t len) +{ + uint16_t tls_id; + const mbedtls_ecp_curve_info *curve_info; + /* + * We expect at least three bytes (see below) + */ + if (len < 3) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* + * First byte is curve_type; only named_curve is handled + */ + if (*(*buf)++ != MBEDTLS_ECP_TLS_NAMED_CURVE) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* + * Next two bytes are the namedcurve value + */ + tls_id = *(*buf)++; + tls_id <<= 8; + tls_id |= *(*buf)++; + + if ((curve_info = mbedtls_ecp_curve_info_from_tls_id(tls_id)) == NULL) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + *grp = curve_info->grp_id; + + return 0; +} + +/* + * Write the ECParameters record corresponding to a group (RFC 4492) + */ +int mbedtls_ecp_tls_write_group(const mbedtls_ecp_group *grp, size_t *olen, + unsigned char *buf, size_t blen) +{ + const mbedtls_ecp_curve_info *curve_info; + if ((curve_info = mbedtls_ecp_curve_info_from_grp_id(grp->id)) == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* + * We are going to write 3 bytes (see below) + */ + *olen = 3; + if (blen < *olen) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + /* + * First byte is curve_type, always named_curve + */ + *buf++ = MBEDTLS_ECP_TLS_NAMED_CURVE; + + /* + * Next two bytes are the namedcurve value + */ + MBEDTLS_PUT_UINT16_BE(curve_info->tls_id, buf, 0); + + return 0; +} + +/* + * Wrapper around fast quasi-modp functions, with fall-back to mbedtls_mpi_mod_mpi. + * See the documentation of struct mbedtls_ecp_group. + * + * This function is in the critial loop for mbedtls_ecp_mul, so pay attention to perf. + */ +static int ecp_modp(mbedtls_mpi *N, const mbedtls_ecp_group *grp) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (grp->modp == NULL) { + return mbedtls_mpi_mod_mpi(N, N, &grp->P); + } + + /* N->s < 0 is a much faster test, which fails only if N is 0 */ + if ((N->s < 0 && mbedtls_mpi_cmp_int(N, 0) != 0) || + mbedtls_mpi_bitlen(N) > 2 * grp->pbits) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + MBEDTLS_MPI_CHK(grp->modp(N)); + + /* N->s < 0 is a much faster test, which fails only if N is 0 */ + while (N->s < 0 && mbedtls_mpi_cmp_int(N, 0) != 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(N, N, &grp->P)); + } + + while (mbedtls_mpi_cmp_mpi(N, &grp->P) >= 0) { + /* we known P, N and the result are positive */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_abs(N, N, &grp->P)); + } + +cleanup: + return ret; +} + +/* + * Fast mod-p functions expect their argument to be in the 0..p^2 range. + * + * In order to guarantee that, we need to ensure that operands of + * mbedtls_mpi_mul_mpi are in the 0..p range. So, after each operation we will + * bring the result back to this range. + * + * The following macros are shortcuts for doing that. + */ + +/* + * Reduce a mbedtls_mpi mod p in-place, general case, to use after mbedtls_mpi_mul_mpi + */ +#if defined(MBEDTLS_SELF_TEST) +#define INC_MUL_COUNT mul_count++; +#else +#define INC_MUL_COUNT +#endif + +#define MOD_MUL(N) \ + do \ + { \ + MBEDTLS_MPI_CHK(ecp_modp(&(N), grp)); \ + INC_MUL_COUNT \ + } while (0) + +static inline int mbedtls_mpi_mul_mod(const mbedtls_ecp_group *grp, + mbedtls_mpi *X, + const mbedtls_mpi *A, + const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(X, A, B)); + MOD_MUL(*X); +cleanup: + return ret; +} + +/* + * Reduce a mbedtls_mpi mod p in-place, to use after mbedtls_mpi_sub_mpi + * N->s < 0 is a very fast test, which fails only if N is 0 + */ +#define MOD_SUB(N) \ + do { \ + while ((N)->s < 0 && mbedtls_mpi_cmp_int((N), 0) != 0) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi((N), (N), &grp->P)); \ + } while (0) + +#if (defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) && \ + !(defined(MBEDTLS_ECP_NO_FALLBACK) && \ + defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) && \ + defined(MBEDTLS_ECP_ADD_MIXED_ALT))) || \ + (defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) && \ + !(defined(MBEDTLS_ECP_NO_FALLBACK) && \ + defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT))) +static inline int mbedtls_mpi_sub_mod(const mbedtls_ecp_group *grp, + mbedtls_mpi *X, + const mbedtls_mpi *A, + const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(X, A, B)); + MOD_SUB(X); +cleanup: + return ret; +} +#endif /* All functions referencing mbedtls_mpi_sub_mod() are alt-implemented without fallback */ + +/* + * Reduce a mbedtls_mpi mod p in-place, to use after mbedtls_mpi_add_mpi and mbedtls_mpi_mul_int. + * We known P, N and the result are positive, so sub_abs is correct, and + * a bit faster. + */ +#define MOD_ADD(N) \ + while (mbedtls_mpi_cmp_mpi((N), &grp->P) >= 0) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_abs((N), (N), &grp->P)) + +static inline int mbedtls_mpi_add_mod(const mbedtls_ecp_group *grp, + mbedtls_mpi *X, + const mbedtls_mpi *A, + const mbedtls_mpi *B) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(X, A, B)); + MOD_ADD(X); +cleanup: + return ret; +} + +static inline int mbedtls_mpi_mul_int_mod(const mbedtls_ecp_group *grp, + mbedtls_mpi *X, + const mbedtls_mpi *A, + mbedtls_mpi_uint c) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_int(X, A, c)); + MOD_ADD(X); +cleanup: + return ret; +} + +static inline int mbedtls_mpi_sub_int_mod(const mbedtls_ecp_group *grp, + mbedtls_mpi *X, + const mbedtls_mpi *A, + mbedtls_mpi_uint c) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(X, A, c)); + MOD_SUB(X); +cleanup: + return ret; +} + +#define MPI_ECP_SUB_INT(X, A, c) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int_mod(grp, X, A, c)) + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) && \ + !(defined(MBEDTLS_ECP_NO_FALLBACK) && \ + defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) && \ + defined(MBEDTLS_ECP_ADD_MIXED_ALT)) +static inline int mbedtls_mpi_shift_l_mod(const mbedtls_ecp_group *grp, + mbedtls_mpi *X, + size_t count) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(X, count)); + MOD_ADD(X); +cleanup: + return ret; +} +#endif \ + /* All functions referencing mbedtls_mpi_shift_l_mod() are alt-implemented without fallback */ + +/* + * Macro wrappers around ECP modular arithmetic + * + * Currently, these wrappers are defined via the bignum module. + */ + +#define MPI_ECP_ADD(X, A, B) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mod(grp, X, A, B)) + +#define MPI_ECP_SUB(X, A, B) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mod(grp, X, A, B)) + +#define MPI_ECP_MUL(X, A, B) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mod(grp, X, A, B)) + +#define MPI_ECP_SQR(X, A) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mod(grp, X, A, A)) + +#define MPI_ECP_MUL_INT(X, A, c) \ + 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)) + +#define MPI_ECP_MOV(X, A) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(X, A)) + +#define MPI_ECP_SHIFT_L(X, count) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l_mod(grp, X, count)) + +#define MPI_ECP_LSET(X, c) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(X, c)) + +#define MPI_ECP_CMP_INT(X, c) \ + mbedtls_mpi_cmp_int(X, c) + +#define MPI_ECP_CMP(X, Y) \ + mbedtls_mpi_cmp_mpi(X, Y) + +/* Needs f_rng, p_rng to be defined. */ +#define MPI_ECP_RAND(X) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_random((X), 2, &grp->P, f_rng, p_rng)) + +/* Conditional negation + * Needs grp and a temporary MPI tmp to be defined. */ +#define MPI_ECP_COND_NEG(X, cond) \ + do \ + { \ + unsigned char nonzero = mbedtls_mpi_cmp_int((X), 0) != 0; \ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&tmp, &grp->P, (X))); \ + MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_assign((X), &tmp, \ + nonzero & cond)); \ + } while (0) + +#define MPI_ECP_NEG(X) MPI_ECP_COND_NEG((X), 1) + +#define MPI_ECP_VALID(X) \ + ((X)->p != NULL) + +#define MPI_ECP_COND_ASSIGN(X, Y, cond) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_assign((X), (Y), (cond))) + +#define MPI_ECP_COND_SWAP(X, Y, cond) \ + MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_swap((X), (Y), (cond))) + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + +/* + * Computes the right-hand side of the Short Weierstrass equation + * RHS = X^3 + A X + B + */ +static int ecp_sw_rhs(const mbedtls_ecp_group *grp, + mbedtls_mpi *rhs, + const mbedtls_mpi *X) +{ + int ret; + + /* Compute X^3 + A X + B as X (X^2 + A) + B */ + MPI_ECP_SQR(rhs, X); + + /* Special case for A = -3 */ + if (grp->A.p == NULL) { + MPI_ECP_SUB_INT(rhs, rhs, 3); + } else { + MPI_ECP_ADD(rhs, rhs, &grp->A); + } + + MPI_ECP_MUL(rhs, rhs, X); + MPI_ECP_ADD(rhs, rhs, &grp->B); + +cleanup: + return ret; +} + +/* + * Derive Y from X and a parity bit + */ +static int mbedtls_ecp_sw_derive_y(const mbedtls_ecp_group *grp, + const mbedtls_mpi *X, + mbedtls_mpi *Y, + int parity_bit) +{ + /* w = y^2 = x^3 + ax + b + * y = sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) + * + * Note: this method for extracting square root does not validate that w + * was indeed a square so this function will return garbage in Y if X + * does not correspond to a point on the curve. + */ + + /* Check prerequisite p = 3 mod 4 */ + if (mbedtls_mpi_get_bit(&grp->P, 0) != 1 || + mbedtls_mpi_get_bit(&grp->P, 1) != 1) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + int ret; + mbedtls_mpi exp; + mbedtls_mpi_init(&exp); + + /* use Y to store intermediate result, actually w above */ + MBEDTLS_MPI_CHK(ecp_sw_rhs(grp, Y, X)); + + /* w = y^2 */ /* Y contains y^2 intermediate result */ + /* exp = ((p+1)/4) */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&exp, &grp->P, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&exp, 2)); + /* sqrt(w) = w^((p+1)/4) mod p (for prime p where p = 3 mod 4) */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(Y, Y /*y^2*/, &exp, &grp->P, NULL)); + + /* check parity bit match or else invert Y */ + /* This quick inversion implementation is valid because Y != 0 for all + * Short Weierstrass curves supported by mbedtls, as each supported curve + * has an order that is a large prime, so each supported curve does not + * have any point of order 2, and a point with Y == 0 would be of order 2 */ + if (mbedtls_mpi_get_bit(Y, 0) != parity_bit) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(Y, &grp->P, Y)); + } + +cleanup: + + mbedtls_mpi_free(&exp); + return ret; +} + +/* + * For curves in short Weierstrass form, we do all the internal operations in + * Jacobian coordinates. + * + * For multiplication, we'll use a comb method with countermeasures against + * SPA, hence timing attacks. + */ + +/* + * Normalize jacobian coordinates so that Z == 0 || Z == 1 (GECC 3.2.1) + * Cost: 1N := 1I + 3M + 1S + */ +static int ecp_normalize_jac(const mbedtls_ecp_group *grp, mbedtls_ecp_point *pt) +{ + if (MPI_ECP_CMP_INT(&pt->Z, 0) == 0) { + return 0; + } + +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_normalize_jac(grp, pt); + } +#endif /* MBEDTLS_ECP_NORMALIZE_JAC_ALT */ + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi T; + mbedtls_mpi_init(&T); + + MPI_ECP_INV(&T, &pt->Z); /* T <- 1 / Z */ + MPI_ECP_MUL(&pt->Y, &pt->Y, &T); /* Y' <- Y*T = Y / Z */ + MPI_ECP_SQR(&T, &T); /* T <- T^2 = 1 / Z^2 */ + MPI_ECP_MUL(&pt->X, &pt->X, &T); /* X <- X * T = X / Z^2 */ + MPI_ECP_MUL(&pt->Y, &pt->Y, &T); /* Y'' <- Y' * T = Y / Z^3 */ + + MPI_ECP_LSET(&pt->Z, 1); + +cleanup: + + mbedtls_mpi_free(&T); + + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT) */ +} + +/* + * Normalize jacobian coordinates of an array of (pointers to) points, + * using Montgomery's trick to perform only one inversion mod P. + * (See for example Cohen's "A Course in Computational Algebraic Number + * Theory", Algorithm 10.3.4.) + * + * Warning: fails (returning an error) if one of the points is zero! + * This should never happen, see choice of w in ecp_mul_comb(). + * + * Cost: 1N(t) := 1I + (6t - 3)M + 1S + */ +static int ecp_normalize_jac_many(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *T[], size_t T_size) +{ + if (T_size < 2) { + return ecp_normalize_jac(grp, *T); + } + +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_normalize_jac_many(grp, T, T_size); + } +#endif + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i; + mbedtls_mpi *c, t; + + if ((c = mbedtls_calloc(T_size, sizeof(mbedtls_mpi))) == NULL) { + return MBEDTLS_ERR_ECP_ALLOC_FAILED; + } + + mbedtls_mpi_init(&t); + + mpi_init_many(c, T_size); + /* + * c[i] = Z_0 * ... * Z_i, i = 0,..,n := T_size-1 + */ + MPI_ECP_MOV(&c[0], &T[0]->Z); + for (i = 1; i < T_size; i++) { + MPI_ECP_MUL(&c[i], &c[i-1], &T[i]->Z); + } + + /* + * c[n] = 1 / (Z_0 * ... * Z_n) mod P + */ + MPI_ECP_INV(&c[T_size-1], &c[T_size-1]); + + for (i = T_size - 1;; i--) { + /* At the start of iteration i (note that i decrements), we have + * - c[j] = Z_0 * .... * Z_j for j < i, + * - c[j] = 1 / (Z_0 * .... * Z_j) for j == i, + * + * This is maintained via + * - c[i-1] <- c[i] * Z_i + * + * We also derive 1/Z_i = c[i] * c[i-1] for i>0 and use that + * to do the actual normalization. For i==0, we already have + * c[0] = 1 / Z_0. + */ + + if (i > 0) { + /* Compute 1/Z_i and establish invariant for the next iteration. */ + MPI_ECP_MUL(&t, &c[i], &c[i-1]); + MPI_ECP_MUL(&c[i-1], &c[i], &T[i]->Z); + } else { + MPI_ECP_MOV(&t, &c[0]); + } + + /* Now t holds 1 / Z_i; normalize as in ecp_normalize_jac() */ + MPI_ECP_MUL(&T[i]->Y, &T[i]->Y, &t); + MPI_ECP_SQR(&t, &t); + MPI_ECP_MUL(&T[i]->X, &T[i]->X, &t); + MPI_ECP_MUL(&T[i]->Y, &T[i]->Y, &t); + + /* + * Post-precessing: reclaim some memory by shrinking coordinates + * - not storing Z (always 1) + * - shrinking other coordinates, but still keeping the same number of + * limbs as P, as otherwise it will too likely be regrown too fast. + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_shrink(&T[i]->X, grp->P.n)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shrink(&T[i]->Y, grp->P.n)); + + MPI_ECP_LSET(&T[i]->Z, 1); + + if (i == 0) { + break; + } + } + +cleanup: + + mbedtls_mpi_free(&t); + mpi_free_many(c, T_size); + mbedtls_free(c); + + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT) */ +} + +/* + * Conditional point inversion: Q -> -Q = (Q.X, -Q.Y, Q.Z) without leak. + * "inv" must be 0 (don't invert) or 1 (invert) or the result will be invalid + */ +static int ecp_safe_invert_jac(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *Q, + unsigned char inv) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi tmp; + mbedtls_mpi_init(&tmp); + + MPI_ECP_COND_NEG(&Q->Y, inv); + +cleanup: + mbedtls_mpi_free(&tmp); + return ret; +} + +/* + * Point doubling R = 2 P, Jacobian coordinates + * + * Based on http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-1998-cmo-2 . + * + * We follow the variable naming fairly closely. The formula variations that trade a MUL for a SQR + * (plus a few ADDs) aren't useful as our bignum implementation doesn't distinguish squaring. + * + * Standard optimizations are applied when curve parameter A is one of { 0, -3 }. + * + * Cost: 1D := 3M + 4S (A == 0) + * 4M + 4S (A == -3) + * 3M + 6S + 1a otherwise + */ +static int ecp_double_jac(const mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_ecp_point *P, + mbedtls_mpi tmp[4]) +{ +#if defined(MBEDTLS_SELF_TEST) + dbl_count++; +#endif + +#if defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_double_jac(grp, R, P); + } +#endif /* MBEDTLS_ECP_DOUBLE_JAC_ALT */ + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Special case for A = -3 */ + if (grp->A.p == NULL) { + /* tmp[0] <- M = 3(X + Z^2)(X - Z^2) */ + MPI_ECP_SQR(&tmp[1], &P->Z); + MPI_ECP_ADD(&tmp[2], &P->X, &tmp[1]); + MPI_ECP_SUB(&tmp[3], &P->X, &tmp[1]); + MPI_ECP_MUL(&tmp[1], &tmp[2], &tmp[3]); + MPI_ECP_MUL_INT(&tmp[0], &tmp[1], 3); + } else { + /* tmp[0] <- M = 3.X^2 + A.Z^4 */ + MPI_ECP_SQR(&tmp[1], &P->X); + MPI_ECP_MUL_INT(&tmp[0], &tmp[1], 3); + + /* Optimize away for "koblitz" curves with A = 0 */ + if (MPI_ECP_CMP_INT(&grp->A, 0) != 0) { + /* M += A.Z^4 */ + MPI_ECP_SQR(&tmp[1], &P->Z); + MPI_ECP_SQR(&tmp[2], &tmp[1]); + MPI_ECP_MUL(&tmp[1], &tmp[2], &grp->A); + MPI_ECP_ADD(&tmp[0], &tmp[0], &tmp[1]); + } + } + + /* tmp[1] <- S = 4.X.Y^2 */ + MPI_ECP_SQR(&tmp[2], &P->Y); + MPI_ECP_SHIFT_L(&tmp[2], 1); + MPI_ECP_MUL(&tmp[1], &P->X, &tmp[2]); + MPI_ECP_SHIFT_L(&tmp[1], 1); + + /* tmp[3] <- U = 8.Y^4 */ + MPI_ECP_SQR(&tmp[3], &tmp[2]); + MPI_ECP_SHIFT_L(&tmp[3], 1); + + /* tmp[2] <- T = M^2 - 2.S */ + MPI_ECP_SQR(&tmp[2], &tmp[0]); + MPI_ECP_SUB(&tmp[2], &tmp[2], &tmp[1]); + MPI_ECP_SUB(&tmp[2], &tmp[2], &tmp[1]); + + /* tmp[1] <- S = M(S - T) - U */ + MPI_ECP_SUB(&tmp[1], &tmp[1], &tmp[2]); + MPI_ECP_MUL(&tmp[1], &tmp[1], &tmp[0]); + MPI_ECP_SUB(&tmp[1], &tmp[1], &tmp[3]); + + /* tmp[3] <- U = 2.Y.Z */ + MPI_ECP_MUL(&tmp[3], &P->Y, &P->Z); + MPI_ECP_SHIFT_L(&tmp[3], 1); + + /* Store results */ + MPI_ECP_MOV(&R->X, &tmp[2]); + MPI_ECP_MOV(&R->Y, &tmp[1]); + MPI_ECP_MOV(&R->Z, &tmp[3]); + +cleanup: + + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) */ +} + +/* + * Addition: R = P + Q, mixed affine-Jacobian coordinates (GECC 3.22) + * + * The coordinates of Q must be normalized (= affine), + * but those of P don't need to. R is not normalized. + * + * P,Q,R may alias, but only at the level of EC points: they must be either + * equal as pointers, or disjoint (including the coordinate data buffers). + * Fine-grained aliasing at the level of coordinates is not supported. + * + * Special cases: (1) P or Q is zero, (2) R is zero, (3) P == Q. + * None of these cases can happen as intermediate step in ecp_mul_comb(): + * - at each step, P, Q and R are multiples of the base point, the factor + * being less than its order, so none of them is zero; + * - Q is an odd multiple of the base point, P an even multiple, + * due to the choice of precomputed points in the modified comb method. + * So branches for these cases do not leak secret information. + * + * Cost: 1A := 8M + 3S + */ +static int ecp_add_mixed(const mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_ecp_point *P, const mbedtls_ecp_point *Q, + mbedtls_mpi tmp[4]) +{ +#if defined(MBEDTLS_SELF_TEST) + add_count++; +#endif + +#if defined(MBEDTLS_ECP_ADD_MIXED_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_add_mixed(grp, R, P, Q); + } +#endif /* MBEDTLS_ECP_ADD_MIXED_ALT */ + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_ADD_MIXED_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* NOTE: Aliasing between input and output is allowed, so one has to make + * sure that at the point X,Y,Z are written, {P,Q}->{X,Y,Z} are no + * longer read from. */ + mbedtls_mpi * const X = &R->X; + mbedtls_mpi * const Y = &R->Y; + mbedtls_mpi * const Z = &R->Z; + + if (!MPI_ECP_VALID(&Q->Z)) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* + * Trivial cases: P == 0 or Q == 0 (case 1) + */ + if (MPI_ECP_CMP_INT(&P->Z, 0) == 0) { + return mbedtls_ecp_copy(R, Q); + } + + if (MPI_ECP_CMP_INT(&Q->Z, 0) == 0) { + return mbedtls_ecp_copy(R, P); + } + + /* + * Make sure Q coordinates are normalized + */ + if (MPI_ECP_CMP_INT(&Q->Z, 1) != 0) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + MPI_ECP_SQR(&tmp[0], &P->Z); + MPI_ECP_MUL(&tmp[1], &tmp[0], &P->Z); + MPI_ECP_MUL(&tmp[0], &tmp[0], &Q->X); + MPI_ECP_MUL(&tmp[1], &tmp[1], &Q->Y); + MPI_ECP_SUB(&tmp[0], &tmp[0], &P->X); + MPI_ECP_SUB(&tmp[1], &tmp[1], &P->Y); + + /* Special cases (2) and (3) */ + if (MPI_ECP_CMP_INT(&tmp[0], 0) == 0) { + if (MPI_ECP_CMP_INT(&tmp[1], 0) == 0) { + ret = ecp_double_jac(grp, R, P, tmp); + goto cleanup; + } else { + ret = mbedtls_ecp_set_zero(R); + goto cleanup; + } + } + + /* {P,Q}->Z no longer used, so OK to write to Z even if there's aliasing. */ + MPI_ECP_MUL(Z, &P->Z, &tmp[0]); + MPI_ECP_SQR(&tmp[2], &tmp[0]); + MPI_ECP_MUL(&tmp[3], &tmp[2], &tmp[0]); + MPI_ECP_MUL(&tmp[2], &tmp[2], &P->X); + + MPI_ECP_MOV(&tmp[0], &tmp[2]); + MPI_ECP_SHIFT_L(&tmp[0], 1); + + /* {P,Q}->X no longer used, so OK to write to X even if there's aliasing. */ + MPI_ECP_SQR(X, &tmp[1]); + MPI_ECP_SUB(X, X, &tmp[0]); + MPI_ECP_SUB(X, X, &tmp[3]); + MPI_ECP_SUB(&tmp[2], &tmp[2], X); + MPI_ECP_MUL(&tmp[2], &tmp[2], &tmp[1]); + MPI_ECP_MUL(&tmp[3], &tmp[3], &P->Y); + /* {P,Q}->Y no longer used, so OK to write to Y even if there's aliasing. */ + MPI_ECP_SUB(Y, &tmp[2], &tmp[3]); + +cleanup: + + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_ADD_MIXED_ALT) */ +} + +/* + * Randomize jacobian coordinates: + * (X, Y, Z) -> (l^2 X, l^3 Y, l Z) for random l + * This is sort of the reverse operation of ecp_normalize_jac(). + * + * This countermeasure was first suggested in [2]. + */ +static int ecp_randomize_jac(const mbedtls_ecp_group *grp, mbedtls_ecp_point *pt, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ +#if defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_randomize_jac(grp, pt, f_rng, p_rng); + } +#endif /* MBEDTLS_ECP_RANDOMIZE_JAC_ALT */ + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi l; + + mbedtls_mpi_init(&l); + + /* Generate l such that 1 < l < p */ + MPI_ECP_RAND(&l); + + /* Z' = l * Z */ + MPI_ECP_MUL(&pt->Z, &pt->Z, &l); + + /* Y' = l * Y */ + MPI_ECP_MUL(&pt->Y, &pt->Y, &l); + + /* X' = l^2 * X */ + MPI_ECP_SQR(&l, &l); + MPI_ECP_MUL(&pt->X, &pt->X, &l); + + /* Y'' = l^2 * Y' = l^3 * Y */ + MPI_ECP_MUL(&pt->Y, &pt->Y, &l); + +cleanup: + mbedtls_mpi_free(&l); + + if (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { + ret = MBEDTLS_ERR_ECP_RANDOM_FAILED; + } + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) */ +} + +/* + * Check and define parameters used by the comb method (see below for details) + */ +#if MBEDTLS_ECP_WINDOW_SIZE < 2 || MBEDTLS_ECP_WINDOW_SIZE > 7 +#error "MBEDTLS_ECP_WINDOW_SIZE out of bounds" +#endif + +/* d = ceil( n / w ) */ +#define COMB_MAX_D (MBEDTLS_ECP_MAX_BITS + 1) / 2 + +/* number of precomputed points */ +#define COMB_MAX_PRE (1 << (MBEDTLS_ECP_WINDOW_SIZE - 1)) + +/* + * Compute the representation of m that will be used with our comb method. + * + * The basic comb method is described in GECC 3.44 for example. We use a + * modified version that provides resistance to SPA by avoiding zero + * digits in the representation as in [3]. We modify the method further by + * requiring that all K_i be odd, which has the small cost that our + * representation uses one more K_i, due to carries, but saves on the size of + * the precomputed table. + * + * Summary of the comb method and its modifications: + * + * - The goal is to compute m*P for some w*d-bit integer m. + * + * - The basic comb method splits m into the w-bit integers + * x[0] .. x[d-1] where x[i] consists of the bits in m whose + * index has residue i modulo d, and computes m * P as + * S[x[0]] + 2 * S[x[1]] + .. + 2^(d-1) S[x[d-1]], where + * S[i_{w-1} .. i_0] := i_{w-1} 2^{(w-1)d} P + ... + i_1 2^d P + i_0 P. + * + * - If it happens that, say, x[i+1]=0 (=> S[x[i+1]]=0), one can replace the sum by + * .. + 2^{i-1} S[x[i-1]] - 2^i S[x[i]] + 2^{i+1} S[x[i]] + 2^{i+2} S[x[i+2]] .., + * thereby successively converting it into a form where all summands + * are nonzero, at the cost of negative summands. This is the basic idea of [3]. + * + * - More generally, even if x[i+1] != 0, we can first transform the sum as + * .. - 2^i S[x[i]] + 2^{i+1} ( S[x[i]] + S[x[i+1]] ) + 2^{i+2} S[x[i+2]] .., + * and then replace S[x[i]] + S[x[i+1]] = S[x[i] ^ x[i+1]] + 2 S[x[i] & x[i+1]]. + * Performing and iterating this procedure for those x[i] that are even + * (keeping track of carry), we can transform the original sum into one of the form + * S[x'[0]] +- 2 S[x'[1]] +- .. +- 2^{d-1} S[x'[d-1]] + 2^d S[x'[d]] + * with all x'[i] odd. It is therefore only necessary to know S at odd indices, + * which is why we are only computing half of it in the first place in + * ecp_precompute_comb and accessing it with index abs(i) / 2 in ecp_select_comb. + * + * - For the sake of compactness, only the seven low-order bits of x[i] + * are used to represent its absolute value (K_i in the paper), and the msb + * of x[i] encodes the sign (s_i in the paper): it is set if and only if + * if s_i == -1; + * + * Calling conventions: + * - x is an array of size d + 1 + * - w is the size, ie number of teeth, of the comb, and must be between + * 2 and 7 (in practice, between 2 and MBEDTLS_ECP_WINDOW_SIZE) + * - m is the MPI, expected to be odd and such that bitlength(m) <= w * d + * (the result will be incorrect if these assumptions are not satisfied) + */ +static void ecp_comb_recode_core(unsigned char x[], size_t d, + unsigned char w, const mbedtls_mpi *m) +{ + size_t i, j; + unsigned char c, cc, adjust; + + memset(x, 0, d+1); + + /* First get the classical comb values (except for x_d = 0) */ + for (i = 0; i < d; i++) { + for (j = 0; j < w; j++) { + x[i] |= mbedtls_mpi_get_bit(m, i + d * j) << j; + } + } + + /* Now make sure x_1 .. x_d are odd */ + c = 0; + for (i = 1; i <= d; i++) { + /* Add carry and update it */ + cc = x[i] & c; + x[i] = x[i] ^ c; + c = cc; + + /* Adjust if needed, avoiding branches */ + adjust = 1 - (x[i] & 0x01); + c |= x[i] & (x[i-1] * adjust); + x[i] = x[i] ^ (x[i-1] * adjust); + x[i-1] |= adjust << 7; + } +} + +/* + * Precompute points for the adapted comb method + * + * Assumption: T must be able to hold 2^{w - 1} elements. + * + * Operation: If i = i_{w-1} ... i_1 is the binary representation of i, + * sets T[i] = i_{w-1} 2^{(w-1)d} P + ... + i_1 2^d P + P. + * + * Cost: d(w-1) D + (2^{w-1} - 1) A + 1 N(w-1) + 1 N(2^{w-1} - 1) + * + * Note: Even comb values (those where P would be omitted from the + * sum defining T[i] above) are not needed in our adaption + * the comb method. See ecp_comb_recode_core(). + * + * This function currently works in four steps: + * (1) [dbl] Computation of intermediate T[i] for 2-power values of i + * (2) [norm_dbl] Normalization of coordinates of these T[i] + * (3) [add] Computation of all T[i] + * (4) [norm_add] Normalization of all T[i] + * + * Step 1 can be interrupted but not the others; together with the final + * coordinate normalization they are the largest steps done at once, depending + * on the window size. Here are operation counts for P-256: + * + * step (2) (3) (4) + * w = 5 142 165 208 + * w = 4 136 77 160 + * w = 3 130 33 136 + * w = 2 124 11 124 + * + * So if ECC operations are blocking for too long even with a low max_ops + * value, it's useful to set MBEDTLS_ECP_WINDOW_SIZE to a lower value in order + * to minimize maximum blocking time. + */ +static int ecp_precompute_comb(const mbedtls_ecp_group *grp, + mbedtls_ecp_point T[], const mbedtls_ecp_point *P, + unsigned char w, size_t d, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char i; + size_t j = 0; + const unsigned char T_size = 1U << (w - 1); + mbedtls_ecp_point *cur, *TT[COMB_MAX_PRE - 1] = { NULL }; + + mbedtls_mpi tmp[4]; + + mpi_init_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + if (rs_ctx->rsm->state == ecp_rsm_pre_dbl) { + goto dbl; + } + if (rs_ctx->rsm->state == ecp_rsm_pre_norm_dbl) { + goto norm_dbl; + } + if (rs_ctx->rsm->state == ecp_rsm_pre_add) { + goto add; + } + if (rs_ctx->rsm->state == ecp_rsm_pre_norm_add) { + goto norm_add; + } + } +#else + (void) rs_ctx; +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + rs_ctx->rsm->state = ecp_rsm_pre_dbl; + + /* initial state for the loop */ + rs_ctx->rsm->i = 0; + } + +dbl: +#endif + /* + * Set T[0] = P and + * T[2^{l-1}] = 2^{dl} P for l = 1 .. w-1 (this is not the final value) + */ + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(&T[0], P)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL && rs_ctx->rsm->i != 0) { + j = rs_ctx->rsm->i; + } else +#endif + j = 0; + + for (; j < d * (w - 1); j++) { + MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_DBL); + + i = 1U << (j / d); + cur = T + i; + + if (j % d == 0) { + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(cur, T + (i >> 1))); + } + + MBEDTLS_MPI_CHK(ecp_double_jac(grp, cur, cur, tmp)); + } + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + rs_ctx->rsm->state = ecp_rsm_pre_norm_dbl; + } + +norm_dbl: +#endif + /* + * Normalize current elements in T to allow them to be used in + * ecp_add_mixed() below, which requires one normalized input. + * + * As T has holes, use an auxiliary array of pointers to elements in T. + * + */ + j = 0; + for (i = 1; i < T_size; i <<= 1) { + TT[j++] = T + i; + } + + MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_INV + 6 * j - 2); + + MBEDTLS_MPI_CHK(ecp_normalize_jac_many(grp, TT, j)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + rs_ctx->rsm->state = ecp_rsm_pre_add; + } + +add: +#endif + /* + * Compute the remaining ones using the minimal number of additions + * Be careful to update T[2^l] only after using it! + */ + MBEDTLS_ECP_BUDGET((T_size - 1) * MBEDTLS_ECP_OPS_ADD); + + for (i = 1; i < T_size; i <<= 1) { + j = i; + while (j--) { + MBEDTLS_MPI_CHK(ecp_add_mixed(grp, &T[i + j], &T[j], &T[i], tmp)); + } + } + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + rs_ctx->rsm->state = ecp_rsm_pre_norm_add; + } + +norm_add: +#endif + /* + * Normalize final elements in T. Even though there are no holes now, we + * still need the auxiliary array for homogeneity with the previous + * call. Also, skip T[0] which is already normalised, being a copy of P. + */ + for (j = 0; j + 1 < T_size; j++) { + TT[j] = T + j + 1; + } + + MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_INV + 6 * j - 2); + + MBEDTLS_MPI_CHK(ecp_normalize_jac_many(grp, TT, j)); + + /* Free Z coordinate (=1 after normalization) to save RAM. + * This makes T[i] invalid as mbedtls_ecp_points, but this is OK + * since from this point onwards, they are only accessed indirectly + * via the getter function ecp_select_comb() which does set the + * target's Z coordinate to 1. */ + for (i = 0; i < T_size; i++) { + mbedtls_mpi_free(&T[i].Z); + } + +cleanup: + + mpi_free_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL && + ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + if (rs_ctx->rsm->state == ecp_rsm_pre_dbl) { + rs_ctx->rsm->i = j; + } + } +#endif + + return ret; +} + +/* + * Select precomputed point: R = sign(i) * T[ abs(i) / 2 ] + * + * See ecp_comb_recode_core() for background + */ +static int ecp_select_comb(const mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_ecp_point T[], unsigned char T_size, + unsigned char i) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char ii, j; + + /* Ignore the "sign" bit and scale down */ + ii = (i & 0x7Fu) >> 1; + + /* Read the whole table to thwart cache-based timing attacks */ + for (j = 0; j < T_size; j++) { + MPI_ECP_COND_ASSIGN(&R->X, &T[j].X, j == ii); + MPI_ECP_COND_ASSIGN(&R->Y, &T[j].Y, j == ii); + } + + /* Safely invert result if i is "negative" */ + MBEDTLS_MPI_CHK(ecp_safe_invert_jac(grp, R, i >> 7)); + + MPI_ECP_LSET(&R->Z, 1); + +cleanup: + return ret; +} + +/* + * Core multiplication algorithm for the (modified) comb method. + * This part is actually common with the basic comb method (GECC 3.44) + * + * Cost: d A + d D + 1 R + */ +static int ecp_mul_comb_core(const mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_ecp_point T[], unsigned char T_size, + const unsigned char x[], size_t d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point Txi; + mbedtls_mpi tmp[4]; + size_t i; + + mbedtls_ecp_point_init(&Txi); + mpi_init_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + +#if !defined(MBEDTLS_ECP_RESTARTABLE) + (void) rs_ctx; +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL && + rs_ctx->rsm->state != ecp_rsm_comb_core) { + rs_ctx->rsm->i = 0; + rs_ctx->rsm->state = ecp_rsm_comb_core; + } + + /* new 'if' instead of nested for the sake of the 'else' branch */ + if (rs_ctx != NULL && rs_ctx->rsm != NULL && rs_ctx->rsm->i != 0) { + /* restore current index (R already pointing to rs_ctx->rsm->R) */ + i = rs_ctx->rsm->i; + } else +#endif + { + /* Start with a non-zero point and randomize its coordinates */ + i = d; + MBEDTLS_MPI_CHK(ecp_select_comb(grp, R, T, T_size, x[i])); + if (f_rng != 0) { + MBEDTLS_MPI_CHK(ecp_randomize_jac(grp, R, f_rng, p_rng)); + } + } + + while (i != 0) { + MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_DBL + MBEDTLS_ECP_OPS_ADD); + --i; + + MBEDTLS_MPI_CHK(ecp_double_jac(grp, R, R, tmp)); + MBEDTLS_MPI_CHK(ecp_select_comb(grp, &Txi, T, T_size, x[i])); + MBEDTLS_MPI_CHK(ecp_add_mixed(grp, R, R, &Txi, tmp)); + } + +cleanup: + + mbedtls_ecp_point_free(&Txi); + mpi_free_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL && + ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + rs_ctx->rsm->i = i; + /* no need to save R, already pointing to rs_ctx->rsm->R */ + } +#endif + + return ret; +} + +/* + * Recode the scalar to get constant-time comb multiplication + * + * As the actual scalar recoding needs an odd scalar as a starting point, + * this wrapper ensures that by replacing m by N - m if necessary, and + * informs the caller that the result of multiplication will be negated. + * + * This works because we only support large prime order for Short Weierstrass + * curves, so N is always odd hence either m or N - m is. + * + * See ecp_comb_recode_core() for background. + */ +static int ecp_comb_recode_scalar(const mbedtls_ecp_group *grp, + const mbedtls_mpi *m, + unsigned char k[COMB_MAX_D + 1], + size_t d, + unsigned char w, + unsigned char *parity_trick) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi M, mm; + + mbedtls_mpi_init(&M); + mbedtls_mpi_init(&mm); + + /* N is always odd (see above), just make extra sure */ + if (mbedtls_mpi_get_bit(&grp->N, 0) != 1) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* do we need the parity trick? */ + *parity_trick = (mbedtls_mpi_get_bit(m, 0) == 0); + + /* execute parity fix in constant time */ + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&M, m)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&mm, &grp->N, m)); + MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_assign(&M, &mm, *parity_trick)); + + /* actual scalar recoding */ + ecp_comb_recode_core(k, d, w, &M); + +cleanup: + mbedtls_mpi_free(&mm); + mbedtls_mpi_free(&M); + + return ret; +} + +/* + * Perform comb multiplication (for short Weierstrass curves) + * once the auxiliary table has been pre-computed. + * + * Scalar recoding may use a parity trick that makes us compute -m * P, + * if that is the case we'll need to recover m * P at the end. + */ +static int ecp_mul_comb_after_precomp(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *R, + const mbedtls_mpi *m, + const mbedtls_ecp_point *T, + unsigned char T_size, + unsigned char w, + size_t d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char parity_trick; + unsigned char k[COMB_MAX_D + 1]; + mbedtls_ecp_point *RR = R; + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + RR = &rs_ctx->rsm->R; + + if (rs_ctx->rsm->state == ecp_rsm_final_norm) { + goto final_norm; + } + } +#endif + + MBEDTLS_MPI_CHK(ecp_comb_recode_scalar(grp, m, k, d, w, + &parity_trick)); + MBEDTLS_MPI_CHK(ecp_mul_comb_core(grp, RR, T, T_size, k, d, + f_rng, p_rng, rs_ctx)); + MBEDTLS_MPI_CHK(ecp_safe_invert_jac(grp, RR, parity_trick)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + rs_ctx->rsm->state = ecp_rsm_final_norm; + } + +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) + if (rs_ctx != NULL && rs_ctx->rsm != NULL) { + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, RR)); + } +#endif + +cleanup: + return ret; +} + +/* + * Pick window size based on curve size and whether we optimize for base point + */ +static unsigned char ecp_pick_window_size(const mbedtls_ecp_group *grp, + unsigned char p_eq_g) +{ + unsigned char w; + + /* + * Minimize the number of multiplications, that is minimize + * 10 * d * w + 18 * 2^(w-1) + 11 * d + 7 * w, with d = ceil( nbits / w ) + * (see costs of the various parts, with 1S = 1M) + */ + w = grp->nbits >= 384 ? 5 : 4; + + /* + * If P == G, pre-compute a bit more, since this may be re-used later. + * Just adding one avoids upping the cost of the first mul too much, + * and the memory cost too. + */ + if (p_eq_g) { + w++; + } + + /* + * If static comb table may not be used (!p_eq_g) or static comb table does + * not exists, make sure w is within bounds. + * (The last test is useful only for very small curves in the test suite.) + * + * The user reduces MBEDTLS_ECP_WINDOW_SIZE does not changes the size of + * static comb table, because the size of static comb table is fixed when + * it is generated. + */ +#if (MBEDTLS_ECP_WINDOW_SIZE < 6) + if ((!p_eq_g || !ecp_group_is_static_comb_table(grp)) && w > MBEDTLS_ECP_WINDOW_SIZE) { + w = MBEDTLS_ECP_WINDOW_SIZE; + } +#endif + if (w >= grp->nbits) { + w = 2; + } + + return w; +} + +/* + * Multiplication using the comb method - for curves in short Weierstrass form + * + * This function is mainly responsible for administrative work: + * - managing the restart context if enabled + * - managing the table of precomputed points (passed between the below two + * functions): allocation, computation, ownership transfer, freeing. + * + * It delegates the actual arithmetic work to: + * ecp_precompute_comb() and ecp_mul_comb_with_precomp() + * + * See comments on ecp_comb_recode_core() regarding the computation strategy. + */ +static int ecp_mul_comb(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char w, p_eq_g, i; + size_t d; + unsigned char T_size = 0, T_ok = 0; + mbedtls_ecp_point *T = NULL; + + ECP_RS_ENTER(rsm); + + /* Is P the base point ? */ +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 + p_eq_g = (MPI_ECP_CMP(&P->Y, &grp->G.Y) == 0 && + MPI_ECP_CMP(&P->X, &grp->G.X) == 0); +#else + p_eq_g = 0; +#endif + + /* Pick window size and deduce related sizes */ + w = ecp_pick_window_size(grp, p_eq_g); + T_size = 1U << (w - 1); + d = (grp->nbits + w - 1) / w; + + /* Pre-computed table: do we have it already for the base point? */ + if (p_eq_g && grp->T != NULL) { + /* second pointer to the same table, will be deleted on exit */ + T = grp->T; + T_ok = 1; + } else +#if defined(MBEDTLS_ECP_RESTARTABLE) + /* Pre-computed table: do we have one in progress? complete? */ + if (rs_ctx != NULL && rs_ctx->rsm != NULL && rs_ctx->rsm->T != NULL) { + /* transfer ownership of T from rsm to local function */ + T = rs_ctx->rsm->T; + rs_ctx->rsm->T = NULL; + rs_ctx->rsm->T_size = 0; + + /* This effectively jumps to the call to mul_comb_after_precomp() */ + T_ok = rs_ctx->rsm->state >= ecp_rsm_comb_core; + } else +#endif + /* Allocate table if we didn't have any */ + { + T = mbedtls_calloc(T_size, sizeof(mbedtls_ecp_point)); + if (T == NULL) { + ret = MBEDTLS_ERR_ECP_ALLOC_FAILED; + goto cleanup; + } + + for (i = 0; i < T_size; i++) { + mbedtls_ecp_point_init(&T[i]); + } + + T_ok = 0; + } + + /* Compute table (or finish computing it) if not done already */ + if (!T_ok) { + MBEDTLS_MPI_CHK(ecp_precompute_comb(grp, T, P, w, d, rs_ctx)); + + if (p_eq_g) { + /* almost transfer ownership of T to the group, but keep a copy of + * the pointer to use for calling the next function more easily */ + grp->T = T; + grp->T_size = T_size; + } + } + + /* Actual comb multiplication using precomputed points */ + MBEDTLS_MPI_CHK(ecp_mul_comb_after_precomp(grp, R, m, + T, T_size, w, d, + f_rng, p_rng, rs_ctx)); + +cleanup: + + /* does T belong to the group? */ + if (T == grp->T) { + T = NULL; + } + + /* does T belong to the restart context? */ +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->rsm != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS && T != NULL) { + /* transfer ownership of T from local function to rsm */ + rs_ctx->rsm->T_size = T_size; + rs_ctx->rsm->T = T; + T = NULL; + } +#endif + + /* did T belong to us? then let's destroy it! */ + if (T != NULL) { + for (i = 0; i < T_size; i++) { + mbedtls_ecp_point_free(&T[i]); + } + mbedtls_free(T); + } + + /* prevent caller from using invalid value */ + int should_free_R = (ret != 0); +#if defined(MBEDTLS_ECP_RESTARTABLE) + /* don't free R while in progress in case R == P */ + if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + should_free_R = 0; + } +#endif + if (should_free_R) { + mbedtls_ecp_point_free(R); + } + + ECP_RS_LEAVE(rsm); + + return ret; +} + +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) +/* + * For Montgomery curves, we do all the internal arithmetic in projective + * coordinates. Import/export of points uses only the x coordinates, which is + * internally represented as X / Z. + * + * For scalar multiplication, we'll use a Montgomery ladder. + */ + +/* + * Normalize Montgomery x/z coordinates: X = X/Z, Z = 1 + * Cost: 1M + 1I + */ +static int ecp_normalize_mxz(const mbedtls_ecp_group *grp, mbedtls_ecp_point *P) +{ +#if defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_normalize_mxz(grp, P); + } +#endif /* MBEDTLS_ECP_NORMALIZE_MXZ_ALT */ + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MPI_ECP_INV(&P->Z, &P->Z); + MPI_ECP_MUL(&P->X, &P->X, &P->Z); + MPI_ECP_LSET(&P->Z, 1); + +cleanup: + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT) */ +} + +/* + * Randomize projective x/z coordinates: + * (X, Z) -> (l X, l Z) for random l + * This is sort of the reverse operation of ecp_normalize_mxz(). + * + * This countermeasure was first suggested in [2]. + * Cost: 2M + */ +static int ecp_randomize_mxz(const mbedtls_ecp_group *grp, mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ +#if defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_randomize_mxz(grp, P, f_rng, p_rng); + } +#endif /* MBEDTLS_ECP_RANDOMIZE_MXZ_ALT */ + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi l; + mbedtls_mpi_init(&l); + + /* Generate l such that 1 < l < p */ + MPI_ECP_RAND(&l); + + MPI_ECP_MUL(&P->X, &P->X, &l); + MPI_ECP_MUL(&P->Z, &P->Z, &l); + +cleanup: + mbedtls_mpi_free(&l); + + if (ret == MBEDTLS_ERR_MPI_NOT_ACCEPTABLE) { + ret = MBEDTLS_ERR_ECP_RANDOM_FAILED; + } + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT) */ +} + +/* + * Double-and-add: R = 2P, S = P + Q, with d = X(P - Q), + * for Montgomery curves in x/z coordinates. + * + * http://www.hyperelliptic.org/EFD/g1p/auto-code/montgom/xz/ladder/mladd-1987-m.op3 + * with + * d = X1 + * P = (X2, Z2) + * Q = (X3, Z3) + * R = (X4, Z4) + * S = (X5, Z5) + * and eliminating temporary variables tO, ..., t4. + * + * Cost: 5M + 4S + */ +static int ecp_double_add_mxz(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *R, mbedtls_ecp_point *S, + const mbedtls_ecp_point *P, const mbedtls_ecp_point *Q, + const mbedtls_mpi *d, + mbedtls_mpi T[4]) +{ +#if defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT) + if (mbedtls_internal_ecp_grp_capable(grp)) { + return mbedtls_internal_ecp_double_add_mxz(grp, R, S, P, Q, d); + } +#endif /* MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT */ + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT) + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MPI_ECP_ADD(&T[0], &P->X, &P->Z); /* Pp := PX + PZ */ + MPI_ECP_SUB(&T[1], &P->X, &P->Z); /* Pm := PX - PZ */ + MPI_ECP_ADD(&T[2], &Q->X, &Q->Z); /* Qp := QX + XZ */ + MPI_ECP_SUB(&T[3], &Q->X, &Q->Z); /* Qm := QX - QZ */ + MPI_ECP_MUL(&T[3], &T[3], &T[0]); /* Qm * Pp */ + MPI_ECP_MUL(&T[2], &T[2], &T[1]); /* Qp * Pm */ + MPI_ECP_SQR(&T[0], &T[0]); /* Pp^2 */ + MPI_ECP_SQR(&T[1], &T[1]); /* Pm^2 */ + MPI_ECP_MUL(&R->X, &T[0], &T[1]); /* Pp^2 * Pm^2 */ + MPI_ECP_SUB(&T[0], &T[0], &T[1]); /* Pp^2 - Pm^2 */ + MPI_ECP_MUL(&R->Z, &grp->A, &T[0]); /* A * (Pp^2 - Pm^2) */ + MPI_ECP_ADD(&R->Z, &T[1], &R->Z); /* [ A * (Pp^2-Pm^2) ] + Pm^2 */ + MPI_ECP_ADD(&S->X, &T[3], &T[2]); /* Qm*Pp + Qp*Pm */ + MPI_ECP_SQR(&S->X, &S->X); /* (Qm*Pp + Qp*Pm)^2 */ + MPI_ECP_SUB(&S->Z, &T[3], &T[2]); /* Qm*Pp - Qp*Pm */ + MPI_ECP_SQR(&S->Z, &S->Z); /* (Qm*Pp - Qp*Pm)^2 */ + MPI_ECP_MUL(&S->Z, d, &S->Z); /* d * ( Qm*Pp - Qp*Pm )^2 */ + MPI_ECP_MUL(&R->Z, &T[0], &R->Z); /* [A*(Pp^2-Pm^2)+Pm^2]*(Pp^2-Pm^2) */ + +cleanup: + + return ret; +#endif /* !defined(MBEDTLS_ECP_NO_FALLBACK) || !defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT) */ +} + +/* + * Multiplication with Montgomery ladder in x/z coordinates, + * for curves in Montgomery form + */ +static int ecp_mul_mxz(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i; + unsigned char b; + mbedtls_ecp_point RP; + mbedtls_mpi PX; + mbedtls_mpi tmp[4]; + mbedtls_ecp_point_init(&RP); mbedtls_mpi_init(&PX); + + mpi_init_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + + if (f_rng == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* Save PX and read from P before writing to R, in case P == R */ + MPI_ECP_MOV(&PX, &P->X); + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(&RP, P)); + + /* Set R to zero in modified x/z coordinates */ + MPI_ECP_LSET(&R->X, 1); + MPI_ECP_LSET(&R->Z, 0); + mbedtls_mpi_free(&R->Y); + + /* RP.X might be slightly larger than P, so reduce it */ + MOD_ADD(&RP.X); + + /* Randomize coordinates of the starting point */ + MBEDTLS_MPI_CHK(ecp_randomize_mxz(grp, &RP, f_rng, p_rng)); + + /* Loop invariant: R = result so far, RP = R + P */ + i = grp->nbits + 1; /* one past the (zero-based) required msb for private keys */ + while (i-- > 0) { + b = mbedtls_mpi_get_bit(m, i); + /* + * if (b) R = 2R + P else R = 2R, + * which is: + * if (b) double_add( RP, R, RP, R ) + * else double_add( R, RP, R, RP ) + * but using safe conditional swaps to avoid leaks + */ + MPI_ECP_COND_SWAP(&R->X, &RP.X, b); + MPI_ECP_COND_SWAP(&R->Z, &RP.Z, b); + MBEDTLS_MPI_CHK(ecp_double_add_mxz(grp, R, &RP, R, &RP, &PX, tmp)); + MPI_ECP_COND_SWAP(&R->X, &RP.X, b); + 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: + mbedtls_ecp_point_free(&RP); mbedtls_mpi_free(&PX); + + mpi_free_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + return ret; +} + +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +/* + * Restartable multiplication R = m * P + * + * This internal function can be called without an RNG in case where we know + * the inputs are not sensitive. + */ +static int ecp_mul_restartable_internal(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + char is_grp_capable = 0; +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) + /* reset ops count for this call if top-level */ + if (rs_ctx != NULL && rs_ctx->depth++ == 0) { + rs_ctx->ops_done = 0; + } +#else + (void) rs_ctx; +#endif + +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + if ((is_grp_capable = mbedtls_internal_ecp_grp_capable(grp))) { + MBEDTLS_MPI_CHK(mbedtls_internal_ecp_init(grp)); + } +#endif /* MBEDTLS_ECP_INTERNAL_ALT */ + + int restarting = 0; +#if defined(MBEDTLS_ECP_RESTARTABLE) + restarting = (rs_ctx != NULL && rs_ctx->rsm != NULL); +#endif + /* skip argument check when restarting */ + if (!restarting) { + /* check_privkey is free */ + MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_CHK); + + /* Common sanity checks */ + MBEDTLS_MPI_CHK(mbedtls_ecp_check_privkey(grp, m)); + MBEDTLS_MPI_CHK(mbedtls_ecp_check_pubkey(grp, P)); + } + + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + MBEDTLS_MPI_CHK(ecp_mul_mxz(grp, R, m, P, f_rng, p_rng)); + } +#endif +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + MBEDTLS_MPI_CHK(ecp_mul_comb(grp, R, m, P, f_rng, p_rng, rs_ctx)); + } +#endif + +cleanup: + +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + if (is_grp_capable) { + mbedtls_internal_ecp_free(grp); + } +#endif /* MBEDTLS_ECP_INTERNAL_ALT */ + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL) { + rs_ctx->depth--; + } +#endif + + return ret; +} + +/* + * Restartable multiplication R = m * P + */ +int mbedtls_ecp_mul_restartable(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + if (f_rng == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + return ecp_mul_restartable_internal(grp, R, m, P, f_rng, p_rng, rs_ctx); +} + +/* + * Multiplication R = m * P + */ +int mbedtls_ecp_mul(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + return mbedtls_ecp_mul_restartable(grp, R, m, P, f_rng, p_rng, NULL); +} + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +/* + * Check that an affine point is valid as a public key, + * short weierstrass curves (SEC1 3.2.3.1) + */ +static int ecp_check_pubkey_sw(const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi YY, RHS; + + /* pt coordinates must be normalized for our checks */ + if (mbedtls_mpi_cmp_int(&pt->X, 0) < 0 || + mbedtls_mpi_cmp_int(&pt->Y, 0) < 0 || + mbedtls_mpi_cmp_mpi(&pt->X, &grp->P) >= 0 || + mbedtls_mpi_cmp_mpi(&pt->Y, &grp->P) >= 0) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + mbedtls_mpi_init(&YY); mbedtls_mpi_init(&RHS); + + /* + * YY = Y^2 + * RHS = X^3 + A X + B + */ + MPI_ECP_SQR(&YY, &pt->Y); + MBEDTLS_MPI_CHK(ecp_sw_rhs(grp, &RHS, &pt->X)); + + if (MPI_ECP_CMP(&YY, &RHS) != 0) { + ret = MBEDTLS_ERR_ECP_INVALID_KEY; + } + +cleanup: + + mbedtls_mpi_free(&YY); mbedtls_mpi_free(&RHS); + + return ret; +} +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +/* + * R = m * P with shortcuts for m == 0, m == 1 and m == -1 + * NOT constant-time - ONLY for short Weierstrass! + */ +static int mbedtls_ecp_mul_shortcuts(mbedtls_ecp_group *grp, + mbedtls_ecp_point *R, + const mbedtls_mpi *m, + const mbedtls_ecp_point *P, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi tmp; + mbedtls_mpi_init(&tmp); + + if (mbedtls_mpi_cmp_int(m, 0) == 0) { + MBEDTLS_MPI_CHK(mbedtls_ecp_check_pubkey(grp, P)); + MBEDTLS_MPI_CHK(mbedtls_ecp_set_zero(R)); + } else if (mbedtls_mpi_cmp_int(m, 1) == 0) { + MBEDTLS_MPI_CHK(mbedtls_ecp_check_pubkey(grp, P)); + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, P)); + } else if (mbedtls_mpi_cmp_int(m, -1) == 0) { + MBEDTLS_MPI_CHK(mbedtls_ecp_check_pubkey(grp, P)); + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, P)); + MPI_ECP_NEG(&R->Y); + } else { + MBEDTLS_MPI_CHK(ecp_mul_restartable_internal(grp, R, m, P, + NULL, NULL, rs_ctx)); + } + +cleanup: + mbedtls_mpi_free(&tmp); + + return ret; +} + +/* + * Restartable linear combination + * NOT constant-time + */ +int mbedtls_ecp_muladd_restartable( + mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q, + mbedtls_ecp_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point mP; + mbedtls_ecp_point *pmP = &mP; + mbedtls_ecp_point *pR = R; + mbedtls_mpi tmp[4]; +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + char is_grp_capable = 0; +#endif + if (mbedtls_ecp_get_type(grp) != MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + mbedtls_ecp_point_init(&mP); + mpi_init_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + + ECP_RS_ENTER(ma); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->ma != NULL) { + /* redirect intermediate results to restart context */ + pmP = &rs_ctx->ma->mP; + pR = &rs_ctx->ma->R; + + /* jump to next operation */ + if (rs_ctx->ma->state == ecp_rsma_mul2) { + goto mul2; + } + if (rs_ctx->ma->state == ecp_rsma_add) { + goto add; + } + if (rs_ctx->ma->state == ecp_rsma_norm) { + goto norm; + } + } +#endif /* MBEDTLS_ECP_RESTARTABLE */ + + MBEDTLS_MPI_CHK(mbedtls_ecp_mul_shortcuts(grp, pmP, m, P, rs_ctx)); +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->ma != NULL) { + rs_ctx->ma->state = ecp_rsma_mul2; + } + +mul2: +#endif + MBEDTLS_MPI_CHK(mbedtls_ecp_mul_shortcuts(grp, pR, n, Q, rs_ctx)); + +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + if ((is_grp_capable = mbedtls_internal_ecp_grp_capable(grp))) { + MBEDTLS_MPI_CHK(mbedtls_internal_ecp_init(grp)); + } +#endif /* MBEDTLS_ECP_INTERNAL_ALT */ + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->ma != NULL) { + rs_ctx->ma->state = ecp_rsma_add; + } + +add: +#endif + MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_ADD); + MBEDTLS_MPI_CHK(ecp_add_mixed(grp, pR, pmP, pR, tmp)); +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->ma != NULL) { + rs_ctx->ma->state = ecp_rsma_norm; + } + +norm: +#endif + MBEDTLS_ECP_BUDGET(MBEDTLS_ECP_OPS_INV); + MBEDTLS_MPI_CHK(ecp_normalize_jac(grp, pR)); + +#if defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && rs_ctx->ma != NULL) { + MBEDTLS_MPI_CHK(mbedtls_ecp_copy(R, pR)); + } +#endif + +cleanup: + + mpi_free_many(tmp, sizeof(tmp) / sizeof(mbedtls_mpi)); + +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + if (is_grp_capable) { + mbedtls_internal_ecp_free(grp); + } +#endif /* MBEDTLS_ECP_INTERNAL_ALT */ + + mbedtls_ecp_point_free(&mP); + + ECP_RS_LEAVE(ma); + + return ret; +} + +/* + * Linear combination + * NOT constant-time + */ +int mbedtls_ecp_muladd(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q) +{ + return mbedtls_ecp_muladd_restartable(grp, R, m, P, n, Q, NULL); +} +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +#define ECP_MPI_INIT(s, n, p) { s, (n), (mbedtls_mpi_uint *) (p) } +#define ECP_MPI_INIT_ARRAY(x) \ + ECP_MPI_INIT(1, sizeof(x) / sizeof(mbedtls_mpi_uint), x) +/* + * Constants for the two points other than 0, 1, -1 (mod p) in + * https://cr.yp.to/ecdh.html#validate + * See ecp_check_pubkey_x25519(). + */ +static const mbedtls_mpi_uint x25519_bad_point_1[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a), + MBEDTLS_BYTES_TO_T_UINT_8(0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00), +}; +static const mbedtls_mpi_uint x25519_bad_point_2[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57), +}; +static const mbedtls_mpi ecp_x25519_bad_point_1 = ECP_MPI_INIT_ARRAY( + x25519_bad_point_1); +static const mbedtls_mpi ecp_x25519_bad_point_2 = ECP_MPI_INIT_ARRAY( + x25519_bad_point_2); +#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */ + +/* + * Check that the input point is not one of the low-order points. + * This is recommended by the "May the Fourth" paper: + * https://eprint.iacr.org/2017/806.pdf + * Those points are never sent by an honest peer. + */ +static int ecp_check_bad_points_mx(const mbedtls_mpi *X, const mbedtls_mpi *P, + const mbedtls_ecp_group_id grp_id) +{ + int ret; + mbedtls_mpi XmP; + + mbedtls_mpi_init(&XmP); + + /* Reduce X mod P so that we only need to check values less than P. + * We know X < 2^256 so we can proceed by subtraction. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&XmP, X)); + while (mbedtls_mpi_cmp_mpi(&XmP, P) >= 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&XmP, &XmP, P)); + } + + /* Check against the known bad values that are less than P. For Curve448 + * these are 0, 1 and -1. For Curve25519 we check the values less than P + * from the following list: https://cr.yp.to/ecdh.html#validate */ + if (mbedtls_mpi_cmp_int(&XmP, 1) <= 0) { /* takes care of 0 and 1 */ + ret = MBEDTLS_ERR_ECP_INVALID_KEY; + goto cleanup; + } + +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + if (grp_id == MBEDTLS_ECP_DP_CURVE25519) { + if (mbedtls_mpi_cmp_mpi(&XmP, &ecp_x25519_bad_point_1) == 0) { + ret = MBEDTLS_ERR_ECP_INVALID_KEY; + goto cleanup; + } + + if (mbedtls_mpi_cmp_mpi(&XmP, &ecp_x25519_bad_point_2) == 0) { + ret = MBEDTLS_ERR_ECP_INVALID_KEY; + goto cleanup; + } + } +#else + (void) grp_id; +#endif + + /* Final check: check if XmP + 1 is P (final because it changes XmP!) */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&XmP, &XmP, 1)); + if (mbedtls_mpi_cmp_mpi(&XmP, P) == 0) { + ret = MBEDTLS_ERR_ECP_INVALID_KEY; + goto cleanup; + } + + ret = 0; + +cleanup: + mbedtls_mpi_free(&XmP); + + return ret; +} + +/* + * Check validity of a public key for Montgomery curves with x-only schemes + */ +static int ecp_check_pubkey_mx(const mbedtls_ecp_group *grp, const mbedtls_ecp_point *pt) +{ + /* [Curve25519 p. 5] Just check X is the correct number of bytes */ + /* Allow any public value, if it's too big then we'll just reduce it mod p + * (RFC 7748 sec. 5 para. 3). */ + if (mbedtls_mpi_size(&pt->X) > (grp->nbits + 7) / 8) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + /* Implicit in all standards (as they don't consider negative numbers): + * X must be non-negative. This is normally ensured by the way it's + * encoded for transmission, but let's be extra sure. */ + if (mbedtls_mpi_cmp_int(&pt->X, 0) < 0) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + return ecp_check_bad_points_mx(&pt->X, &grp->P, grp->id); +} +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +/* + * Check that a point is valid as a public key + */ +int mbedtls_ecp_check_pubkey(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *pt) +{ + /* Must use affine coordinates */ + if (mbedtls_mpi_cmp_int(&pt->Z, 1) != 0) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + return ecp_check_pubkey_mx(grp, pt); + } +#endif +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + return ecp_check_pubkey_sw(grp, pt); + } +#endif + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; +} + +/* + * Check that an mbedtls_mpi is valid as a private key + */ +int mbedtls_ecp_check_privkey(const mbedtls_ecp_group *grp, + const mbedtls_mpi *d) +{ +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + /* see RFC 7748 sec. 5 para. 5 */ + if (mbedtls_mpi_get_bit(d, 0) != 0 || + mbedtls_mpi_get_bit(d, 1) != 0 || + mbedtls_mpi_bitlen(d) - 1 != grp->nbits) { /* mbedtls_mpi_bitlen is one-based! */ + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + /* see [Curve25519] page 5 */ + if (grp->nbits == 254 && mbedtls_mpi_get_bit(d, 2) != 0) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + return 0; + } +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + /* see SEC1 3.2 */ + if (mbedtls_mpi_cmp_int(d, 1) < 0 || + mbedtls_mpi_cmp_mpi(d, &grp->N) >= 0) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } else { + return 0; + } + } +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; +} + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_gen_privkey_mx(size_t high_bit, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + size_t n_random_bytes = high_bit / 8 + 1; + + /* [Curve25519] page 5 */ + /* Generate a (high_bit+1)-bit random number by generating just enough + * random bytes, then shifting out extra bits from the top (necessary + * when (high_bit+1) is not a multiple of 8). */ + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(d, n_random_bytes, + f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(d, 8 * n_random_bytes - high_bit - 1)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(d, high_bit, 1)); + + /* Make sure the last two bits are unset for Curve448, three bits for + Curve25519 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(d, 0, 0)); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(d, 1, 0)); + if (high_bit == 254) { + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(d, 2, 0)); + } + +cleanup: + return ret; +} +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +static int mbedtls_ecp_gen_privkey_sw( + const mbedtls_mpi *N, mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = mbedtls_mpi_random(d, 1, N, f_rng, p_rng); + switch (ret) { + case MBEDTLS_ERR_MPI_NOT_ACCEPTABLE: + return MBEDTLS_ERR_ECP_RANDOM_FAILED; + default: + return ret; + } +} +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +/* + * Generate a private key + */ +int mbedtls_ecp_gen_privkey(const mbedtls_ecp_group *grp, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + return mbedtls_ecp_gen_privkey_mx(grp->nbits, d, f_rng, p_rng); + } +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + return mbedtls_ecp_gen_privkey_sw(&grp->N, d, f_rng, p_rng); + } +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; +} + +/* + * Generate a keypair with configurable base point + */ +int mbedtls_ecp_gen_keypair_base(mbedtls_ecp_group *grp, + const mbedtls_ecp_point *G, + mbedtls_mpi *d, mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_MPI_CHK(mbedtls_ecp_gen_privkey(grp, d, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(grp, Q, d, G, f_rng, p_rng)); + +cleanup: + return ret; +} + +/* + * Generate key pair, wrapper for conventional base point + */ +int mbedtls_ecp_gen_keypair(mbedtls_ecp_group *grp, + mbedtls_mpi *d, mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + return mbedtls_ecp_gen_keypair_base(grp, &grp->G, d, Q, f_rng, p_rng); +} + +/* + * Generate a keypair, prettier wrapper + */ +int mbedtls_ecp_gen_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + if ((ret = mbedtls_ecp_group_load(&key->grp, grp_id)) != 0) { + return ret; + } + + return mbedtls_ecp_gen_keypair(&key->grp, &key->d, &key->Q, f_rng, p_rng); +} + +#define ECP_CURVE25519_KEY_SIZE 32 +#define ECP_CURVE448_KEY_SIZE 56 +/* + * Read a private key. + */ +int mbedtls_ecp_read_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, + const unsigned char *buf, size_t buflen) +{ + int ret = 0; + + if ((ret = mbedtls_ecp_group_load(&key->grp, grp_id)) != 0) { + return ret; + } + + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + /* + * Mask the key as mandated by RFC7748 for Curve25519 and Curve448. + */ + if (grp_id == MBEDTLS_ECP_DP_CURVE25519) { + if (buflen != ECP_CURVE25519_KEY_SIZE) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary_le(&key->d, buf, buflen)); + + /* Set the three least significant bits to 0 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&key->d, 0, 0)); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&key->d, 1, 0)); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&key->d, 2, 0)); + + /* Set the most significant bit to 0 */ + MBEDTLS_MPI_CHK( + mbedtls_mpi_set_bit(&key->d, + ECP_CURVE25519_KEY_SIZE * 8 - 1, 0) + ); + + /* Set the second most significant bit to 1 */ + MBEDTLS_MPI_CHK( + mbedtls_mpi_set_bit(&key->d, + ECP_CURVE25519_KEY_SIZE * 8 - 2, 1) + ); + } else if (grp_id == MBEDTLS_ECP_DP_CURVE448) { + if (buflen != ECP_CURVE448_KEY_SIZE) { + return MBEDTLS_ERR_ECP_INVALID_KEY; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary_le(&key->d, buf, buflen)); + + /* Set the two least significant bits to 0 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&key->d, 0, 0)); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(&key->d, 1, 0)); + + /* Set the most significant bit to 1 */ + MBEDTLS_MPI_CHK( + mbedtls_mpi_set_bit(&key->d, + ECP_CURVE448_KEY_SIZE * 8 - 1, 1) + ); + } + } + +#endif +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&key->d, buf, buflen)); + + MBEDTLS_MPI_CHK(mbedtls_ecp_check_privkey(&key->grp, &key->d)); + } + +#endif +cleanup: + + if (ret != 0) { + mbedtls_mpi_free(&key->d); + } + + return ret; +} + +/* + * Write a private key. + */ +int mbedtls_ecp_write_key(mbedtls_ecp_keypair *key, + unsigned char *buf, size_t buflen) +{ + int ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_MONTGOMERY) { + if (key->grp.id == MBEDTLS_ECP_DP_CURVE25519) { + if (buflen < ECP_CURVE25519_KEY_SIZE) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + } else if (key->grp.id == MBEDTLS_ECP_DP_CURVE448) { + if (buflen < ECP_CURVE448_KEY_SIZE) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + } + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary_le(&key->d, buf, buflen)); + } +#endif +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + if (mbedtls_ecp_get_type(&key->grp) == MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&key->d, buf, buflen)); + } + +#endif +cleanup: + + return ret; +} + + +/* + * Check a public-private key pair + */ +int mbedtls_ecp_check_pub_priv( + const mbedtls_ecp_keypair *pub, const mbedtls_ecp_keypair *prv, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_point Q; + mbedtls_ecp_group grp; + if (pub->grp.id == MBEDTLS_ECP_DP_NONE || + pub->grp.id != prv->grp.id || + mbedtls_mpi_cmp_mpi(&pub->Q.X, &prv->Q.X) || + mbedtls_mpi_cmp_mpi(&pub->Q.Y, &prv->Q.Y) || + mbedtls_mpi_cmp_mpi(&pub->Q.Z, &prv->Q.Z)) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + mbedtls_ecp_point_init(&Q); + mbedtls_ecp_group_init(&grp); + + /* mbedtls_ecp_mul() needs a non-const group... */ + mbedtls_ecp_group_copy(&grp, &prv->grp); + + /* Also checks d is valid */ + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(&grp, &Q, &prv->d, &prv->grp.G, f_rng, p_rng)); + + if (mbedtls_mpi_cmp_mpi(&Q.X, &prv->Q.X) || + mbedtls_mpi_cmp_mpi(&Q.Y, &prv->Q.Y) || + mbedtls_mpi_cmp_mpi(&Q.Z, &prv->Q.Z)) { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + +cleanup: + mbedtls_ecp_point_free(&Q); + mbedtls_ecp_group_free(&grp); + + return ret; +} + +/* + * Export generic key-pair parameters. + */ +int mbedtls_ecp_export(const mbedtls_ecp_keypair *key, mbedtls_ecp_group *grp, + mbedtls_mpi *d, mbedtls_ecp_point *Q) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_ecp_group_copy(grp, &key->grp)) != 0) { + return ret; + } + + if ((ret = mbedtls_mpi_copy(d, &key->d)) != 0) { + return ret; + } + + if ((ret = mbedtls_ecp_copy(Q, &key->Q)) != 0) { + return ret; + } + + return 0; +} + +#if defined(MBEDTLS_SELF_TEST) + +/* + * PRNG for test - !!!INSECURE NEVER USE IN PRODUCTION!!! + * + * This is the linear congruential generator from numerical recipes, + * except we only use the low byte as the output. See + * https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use + */ +static int self_test_rng(void *ctx, unsigned char *out, size_t len) +{ + static uint32_t state = 42; + + (void) ctx; + + for (size_t i = 0; i < len; i++) { + state = state * 1664525u + 1013904223u; + out[i] = (unsigned char) state; + } + + return 0; +} + +/* Adjust the exponent to be a valid private point for the specified curve. + * This is sometimes necessary because we use a single set of exponents + * for all curves but the validity of values depends on the curve. */ +static int self_test_adjust_exponent(const mbedtls_ecp_group *grp, + mbedtls_mpi *m) +{ + int ret = 0; + switch (grp->id) { + /* If Curve25519 is available, then that's what we use for the + * Montgomery test, so we don't need the adjustment code. */ +#if !defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + case MBEDTLS_ECP_DP_CURVE448: + /* Move highest bit from 254 to N-1. Setting bit N-1 is + * necessary to enforce the highest-bit-set constraint. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(m, 254, 0)); + MBEDTLS_MPI_CHK(mbedtls_mpi_set_bit(m, grp->nbits, 1)); + /* Copy second-highest bit from 253 to N-2. This is not + * necessary but improves the test variety a bit. */ + MBEDTLS_MPI_CHK( + mbedtls_mpi_set_bit(m, grp->nbits - 1, + mbedtls_mpi_get_bit(m, 253))); + break; +#endif +#endif /* ! defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) */ + default: + /* Non-Montgomery curves and Curve25519 need no adjustment. */ + (void) grp; + (void) m; + goto cleanup; + } +cleanup: + return ret; +} + +/* Calculate R = m.P for each m in exponents. Check that the number of + * basic operations doesn't depend on the value of m. */ +static int self_test_point(int verbose, + mbedtls_ecp_group *grp, + mbedtls_ecp_point *R, + mbedtls_mpi *m, + const mbedtls_ecp_point *P, + const char *const *exponents, + size_t n_exponents) +{ + int ret = 0; + size_t i = 0; + unsigned long add_c_prev, dbl_c_prev, mul_c_prev; + add_count = 0; + dbl_count = 0; + mul_count = 0; + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(m, 16, exponents[0])); + MBEDTLS_MPI_CHK(self_test_adjust_exponent(grp, m)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(grp, R, m, P, self_test_rng, NULL)); + + for (i = 1; i < n_exponents; i++) { + add_c_prev = add_count; + dbl_c_prev = dbl_count; + mul_c_prev = mul_count; + add_count = 0; + dbl_count = 0; + mul_count = 0; + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(m, 16, exponents[i])); + MBEDTLS_MPI_CHK(self_test_adjust_exponent(grp, m)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(grp, R, m, P, self_test_rng, NULL)); + + if (add_count != add_c_prev || + dbl_count != dbl_c_prev || + mul_count != mul_c_prev) { + ret = 1; + break; + } + } + +cleanup: + if (verbose != 0) { + if (ret != 0) { + mbedtls_printf("failed (%u)\n", (unsigned int) i); + } else { + mbedtls_printf("passed\n"); + } + } + return ret; +} + +/* + * Checkup routine + */ +int mbedtls_ecp_self_test(int verbose) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_group grp; + mbedtls_ecp_point R, P; + mbedtls_mpi m; + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + /* Exponents especially adapted for secp192k1, which has the lowest + * order n of all supported curves (secp192r1 is in a slightly larger + * field but the order of its base point is slightly smaller). */ + const char *sw_exponents[] = + { + "000000000000000000000000000000000000000000000001", /* one */ + "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8C", /* n - 1 */ + "5EA6F389A38B8BC81E767753B15AA5569E1782E30ABE7D25", /* random */ + "400000000000000000000000000000000000000000000000", /* one and zeros */ + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", /* all ones */ + "555555555555555555555555555555555555555555555555", /* 101010... */ + }; +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + const char *m_exponents[] = + { + /* Valid private values for Curve25519. In a build with Curve448 + * but not Curve25519, they will be adjusted in + * self_test_adjust_exponent(). */ + "4000000000000000000000000000000000000000000000000000000000000000", + "5C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C30", + "5715ECCE24583F7A7023C24164390586842E816D7280A49EF6DF4EAE6B280BF8", + "41A2B017516F6D254E1F002BCCBADD54BE30F8CEC737A0E912B4963B6BA74460", + "5555555555555555555555555555555555555555555555555555555555555550", + "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8", + }; +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + + mbedtls_ecp_group_init(&grp); + mbedtls_ecp_point_init(&R); + mbedtls_ecp_point_init(&P); + mbedtls_mpi_init(&m); + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + /* Use secp192r1 if available, or any available curve */ +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_SECP192R1)); +#else + MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&grp, mbedtls_ecp_curve_list()->grp_id)); +#endif + + if (verbose != 0) { + mbedtls_printf(" ECP SW test #1 (constant op_count, base point G): "); + } + /* Do a dummy multiplication first to trigger precomputation */ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&m, 2)); + MBEDTLS_MPI_CHK(mbedtls_ecp_mul(&grp, &P, &m, &grp.G, self_test_rng, NULL)); + ret = self_test_point(verbose, + &grp, &R, &m, &grp.G, + sw_exponents, + sizeof(sw_exponents) / sizeof(sw_exponents[0])); + if (ret != 0) { + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf(" ECP SW test #2 (constant op_count, other point): "); + } + /* We computed P = 2G last time, use it */ + ret = self_test_point(verbose, + &grp, &R, &m, &P, + sw_exponents, + sizeof(sw_exponents) / sizeof(sw_exponents[0])); + if (ret != 0) { + goto cleanup; + } + + mbedtls_ecp_group_free(&grp); + mbedtls_ecp_point_free(&R); +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + if (verbose != 0) { + mbedtls_printf(" ECP Montgomery test (constant op_count): "); + } +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_CURVE25519)); +#elif defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&grp, MBEDTLS_ECP_DP_CURVE448)); +#else +#error "MBEDTLS_ECP_MONTGOMERY_ENABLED is defined, but no curve is supported for self-test" +#endif + ret = self_test_point(verbose, + &grp, &R, &m, &grp.G, + m_exponents, + sizeof(m_exponents) / sizeof(m_exponents[0])); + if (ret != 0) { + goto cleanup; + } +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +cleanup: + + if (ret < 0 && verbose != 0) { + mbedtls_printf("Unexpected error, return code = %08X\n", (unsigned int) ret); + } + + mbedtls_ecp_group_free(&grp); + mbedtls_ecp_point_free(&R); + mbedtls_ecp_point_free(&P); + mbedtls_mpi_free(&m); + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* !MBEDTLS_ECP_ALT */ + +#endif /* MBEDTLS_ECP_C */ diff --git a/r5dev/thirdparty/mbedtls/ecp_curves.c b/r5dev/thirdparty/mbedtls/ecp_curves.c new file mode 100644 index 00000000..db21d7d8 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ecp_curves.c @@ -0,0 +1,5913 @@ +/* + * Elliptic curves over GF(p): curve-specific data and functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_ECP_C) + +#include "mbedtls/ecp.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include "bn_mul.h" +#include "bignum_core.h" +#include "ecp_invasive.h" + +#include + +#if !defined(MBEDTLS_ECP_ALT) + +/* Parameter validation macros based on platform_util.h */ +#define ECP_VALIDATE_RET(cond) \ + MBEDTLS_INTERNAL_VALIDATE_RET(cond, MBEDTLS_ERR_ECP_BAD_INPUT_DATA) +#define ECP_VALIDATE(cond) \ + MBEDTLS_INTERNAL_VALIDATE(cond) + +#define ECP_MPI_INIT(s, n, p) { s, (n), (mbedtls_mpi_uint *) (p) } + +#define ECP_MPI_INIT_ARRAY(x) \ + ECP_MPI_INIT(1, sizeof(x) / sizeof(mbedtls_mpi_uint), x) + +#define ECP_POINT_INIT_XY_Z0(x, y) { \ + ECP_MPI_INIT_ARRAY(x), ECP_MPI_INIT_ARRAY(y), ECP_MPI_INIT(1, 0, NULL) } +#define ECP_POINT_INIT_XY_Z1(x, y) { \ + ECP_MPI_INIT_ARRAY(x), ECP_MPI_INIT_ARRAY(y), ECP_MPI_INIT(1, 1, mpi_one) } + +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +/* For these curves, we build the group parameters dynamically. */ +#define ECP_LOAD_GROUP +static mbedtls_mpi_uint mpi_one[] = { 1 }; +#endif + +/* + * Note: the constants are in little-endian order + * to be directly usable in MPIs + */ + +/* + * Domain parameters for secp192r1 + */ +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) +static const mbedtls_mpi_uint secp192r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; +static const mbedtls_mpi_uint secp192r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0xB9, 0x46, 0xC1, 0xEC, 0xDE, 0xB8, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0x30, 0x24, 0x72, 0xAB, 0xE9, 0xA7, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0x80, 0x9C, 0xE5, 0x19, 0x05, 0x21, 0x64), +}; +static const mbedtls_mpi_uint secp192r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0x10, 0xFF, 0x82, 0xFD, 0x0A, 0xFF, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x88, 0xA1, 0x43, 0xEB, 0x20, 0xBF, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0x90, 0x30, 0xB0, 0x0E, 0xA8, 0x8D, 0x18), +}; +static const mbedtls_mpi_uint secp192r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x48, 0x79, 0x1E, 0xA1, 0x77, 0xF9, 0x73), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0xCD, 0x24, 0x6B, 0xED, 0x11, 0x10, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0x78, 0xDA, 0xC8, 0xFF, 0x95, 0x2B, 0x19, 0x07), +}; +static const mbedtls_mpi_uint secp192r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x31, 0x28, 0xD2, 0xB4, 0xB1, 0xC9, 0x6B, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0xF8, 0xDE, 0x99, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp192r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0x10, 0xFF, 0x82, 0xFD, 0x0A, 0xFF, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x88, 0xA1, 0x43, 0xEB, 0x20, 0xBF, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0x90, 0x30, 0xB0, 0x0E, 0xA8, 0x8D, 0x18), +}; +static const mbedtls_mpi_uint secp192r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x48, 0x79, 0x1E, 0xA1, 0x77, 0xF9, 0x73), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0xCD, 0x24, 0x6B, 0xED, 0x11, 0x10, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0x78, 0xDA, 0xC8, 0xFF, 0x95, 0x2B, 0x19, 0x07), +}; +static const mbedtls_mpi_uint secp192r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x9E, 0xE3, 0x60, 0x59, 0xD1, 0xC4, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0xBD, 0x22, 0xD7, 0x2D, 0x07, 0xBD, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x2A, 0xCF, 0x33, 0xF0, 0xBE, 0xD1, 0xED), +}; +static const mbedtls_mpi_uint secp192r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0x71, 0x4B, 0xA8, 0xED, 0x7E, 0xC9, 0x1A), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x2A, 0xF6, 0xDF, 0x0E, 0xE8, 0x4C, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0x35, 0xF7, 0x8A, 0xC3, 0xEC, 0xDE, 0x1E), +}; +static const mbedtls_mpi_uint secp192r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x67, 0xC2, 0x1D, 0x32, 0x8F, 0x10, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x2D, 0x17, 0xF3, 0xE4, 0xFE, 0xD8, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x45, 0x10, 0x70, 0x2C, 0x3E, 0x52, 0x3E), +}; +static const mbedtls_mpi_uint secp192r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0xF1, 0x04, 0x5D, 0xEE, 0xD4, 0x56, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0x78, 0xB7, 0x38, 0x27, 0x61, 0xAA, 0x81, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x37, 0xD7, 0x0E, 0x29, 0x0E, 0x11, 0x14), +}; +static const mbedtls_mpi_uint secp192r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x35, 0x52, 0xC6, 0x31, 0xB7, 0x27, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xD4, 0x15, 0x98, 0x0F, 0xE7, 0xF3, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x31, 0x70, 0x35, 0x09, 0xA0, 0x2B, 0xC2), +}; +static const mbedtls_mpi_uint secp192r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x75, 0xA7, 0x4C, 0x88, 0xCF, 0x5B, 0xE4), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0x17, 0x48, 0x8D, 0xF2, 0xF0, 0x86, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0xCF, 0xFE, 0x6B, 0xB0, 0xA5, 0x06, 0xAB), +}; +static const mbedtls_mpi_uint secp192r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0x6A, 0xDC, 0x9A, 0x6D, 0x7B, 0x47, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0xFC, 0x51, 0x12, 0x62, 0x66, 0x0B, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0x40, 0x93, 0xA0, 0xB5, 0x5A, 0x58, 0xD7), +}; +static const mbedtls_mpi_uint secp192r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0xCB, 0xAF, 0xDC, 0x0B, 0xA1, 0x26, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x36, 0x9D, 0xA3, 0xD7, 0x3B, 0xAD, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x3B, 0x05, 0x9A, 0xA8, 0xAA, 0x69, 0xB2), +}; +static const mbedtls_mpi_uint secp192r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xD9, 0xD1, 0x4D, 0x4A, 0x6E, 0x96, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0x66, 0x32, 0x39, 0xC6, 0x57, 0x7D, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0xA0, 0x36, 0xC2, 0x45, 0xF9, 0x00, 0x62), +}; +static const mbedtls_mpi_uint secp192r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0xEF, 0x59, 0x46, 0xDC, 0x60, 0xD9, 0x8F), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0xB0, 0xE9, 0x41, 0xA4, 0x87, 0x76, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0xD4, 0x0E, 0xB2, 0xFA, 0x16, 0x56, 0xDC), +}; +static const mbedtls_mpi_uint secp192r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0x62, 0xD2, 0xB1, 0x34, 0xB2, 0xF1, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0xB2, 0xED, 0x55, 0xC5, 0x47, 0xB5, 0x07, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xF6, 0x2F, 0x94, 0xC3, 0xDD, 0x54, 0x2F), +}; +static const mbedtls_mpi_uint secp192r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0xA6, 0xD4, 0x8C, 0xA9, 0xCE, 0x4D, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0x4B, 0x46, 0xCC, 0xB2, 0x55, 0xC8, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0xAE, 0x31, 0xED, 0x89, 0x65, 0x59, 0x55), +}; +static const mbedtls_mpi_uint secp192r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x0A, 0xD1, 0x1A, 0xC5, 0xF6, 0xEA, 0x43), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0xFC, 0x0C, 0x1A, 0xFB, 0xA0, 0xC8, 0x70), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xFD, 0x53, 0x6F, 0x6D, 0xBF, 0xBA, 0xAF), +}; +static const mbedtls_mpi_uint secp192r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0xB0, 0x7D, 0x83, 0x96, 0xE3, 0xCB, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x6E, 0x55, 0x2C, 0x20, 0x53, 0x2F, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0xA6, 0x66, 0x00, 0x17, 0x08, 0xFE, 0xAC, 0x31), +}; +static const mbedtls_mpi_uint secp192r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x12, 0x97, 0x3A, 0xC7, 0x57, 0x45, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x25, 0x99, 0x00, 0xF6, 0x97, 0xB4, 0x64), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x74, 0xE6, 0xE6, 0xA3, 0xDF, 0x9C, 0xCC), +}; +static const mbedtls_mpi_uint secp192r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0xF4, 0x76, 0xD5, 0x5F, 0x2A, 0xFD, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x80, 0x7E, 0x3E, 0xE5, 0xE8, 0xD6, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0xAD, 0x1E, 0x70, 0x79, 0x3E, 0x3D, 0x83), +}; +static const mbedtls_mpi_uint secp192r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x15, 0xBB, 0xB3, 0x42, 0x6A, 0xA1, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x58, 0xCB, 0x43, 0x25, 0x00, 0x14, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0x4E, 0x93, 0x11, 0xE0, 0x32, 0x54, 0x98), +}; +static const mbedtls_mpi_uint secp192r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x52, 0xA2, 0xB4, 0x57, 0x32, 0xB9, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x43, 0xA1, 0xB1, 0xFB, 0x01, 0xE1, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0xA6, 0xFB, 0x5A, 0x11, 0xB8, 0xC2, 0x03, 0xE5), +}; +static const mbedtls_mpi_uint secp192r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x2B, 0x71, 0x26, 0x4E, 0x7C, 0xC5, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0xF5, 0xD3, 0xA8, 0xE4, 0x95, 0x48, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0xAE, 0xD9, 0x5D, 0x9F, 0x6A, 0x22, 0xAD), +}; +static const mbedtls_mpi_uint secp192r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0xCC, 0xA3, 0x4D, 0xA0, 0x1C, 0x34, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0x3C, 0x62, 0xF8, 0x5E, 0xA6, 0x58, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x6E, 0x66, 0x8A, 0x3D, 0x17, 0xFF, 0x0F), +}; +static const mbedtls_mpi_uint secp192r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0xCD, 0xA8, 0xDD, 0xD1, 0x20, 0x5C, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0xFE, 0x17, 0xE2, 0xCF, 0xEA, 0x63, 0xDE), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x51, 0xC9, 0x16, 0xDE, 0xB4, 0xB2, 0xDD), +}; +static const mbedtls_mpi_uint secp192r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xBE, 0x12, 0xD7, 0xA3, 0x0A, 0x50, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0x53, 0x87, 0xC5, 0x8A, 0x76, 0x57, 0x07, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0x1F, 0xC6, 0x1B, 0x66, 0xC4, 0x3D, 0x8A), +}; +static const mbedtls_mpi_uint secp192r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0xA4, 0x85, 0x13, 0x8F, 0xA7, 0x35, 0x19), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x0D, 0xFD, 0xFF, 0x1B, 0xD1, 0xD6, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x7A, 0xD0, 0xC3, 0xB4, 0xEF, 0x39, 0x66), +}; +static const mbedtls_mpi_uint secp192r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0xFE, 0xA5, 0x9C, 0x34, 0x30, 0x49, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0xDE, 0xC5, 0x39, 0x26, 0x06, 0xE3, 0x01, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0x2B, 0x66, 0xFC, 0x95, 0x5F, 0x35, 0xF7), +}; +static const mbedtls_mpi_uint secp192r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0xCF, 0x54, 0x63, 0x99, 0x57, 0x05, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x6F, 0x00, 0x5F, 0x65, 0x08, 0x47, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x2A, 0x90, 0x6D, 0x67, 0xC6, 0xBC, 0x45), +}; +static const mbedtls_mpi_uint secp192r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0x4D, 0x88, 0x0A, 0x35, 0x9E, 0x33, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x17, 0x0C, 0xF8, 0xE1, 0x7A, 0x49, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x44, 0x06, 0x8F, 0x0B, 0x70, 0x2F, 0x71), +}; +static const mbedtls_mpi_uint secp192r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x85, 0x4B, 0xCB, 0xF9, 0x8E, 0x6A, 0xDA, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x43, 0xA1, 0x3F, 0xCE, 0x17, 0xD2, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x0D, 0xD2, 0x6C, 0x82, 0x37, 0xE5, 0xFC), +}; +static const mbedtls_mpi_uint secp192r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0x3C, 0xF4, 0x92, 0xB4, 0x8A, 0x95, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x85, 0x96, 0xF1, 0x0A, 0x34, 0x2F, 0x74, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0xA1, 0xAA, 0xBA, 0x86, 0x77, 0x4F, 0xA2), +}; +static const mbedtls_mpi_uint secp192r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0x7F, 0xEF, 0x60, 0x50, 0x80, 0xD7, 0xD4), + MBEDTLS_BYTES_TO_T_UINT_8(0x31, 0xAC, 0xC9, 0xFE, 0xEC, 0x0A, 0x1A, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x2F, 0xBE, 0x91, 0xD7, 0xB7, 0x38, 0x48), +}; +static const mbedtls_mpi_uint secp192r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0xAE, 0x85, 0x98, 0xFE, 0x05, 0x7F, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0xBE, 0xFD, 0x11, 0x31, 0x3D, 0x14, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0x75, 0xE8, 0x30, 0x01, 0xCB, 0x9B, 0x1C), +}; +static const mbedtls_ecp_point secp192r1_T[16] = { + ECP_POINT_INIT_XY_Z1(secp192r1_T_0_X, secp192r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_1_X, secp192r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_2_X, secp192r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_3_X, secp192r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_4_X, secp192r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_5_X, secp192r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_6_X, secp192r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_7_X, secp192r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_8_X, secp192r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_9_X, secp192r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_10_X, secp192r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_11_X, secp192r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_12_X, secp192r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_13_X, secp192r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_14_X, secp192r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp192r1_T_15_X, secp192r1_T_15_Y), +}; +#else +#define secp192r1_T NULL +#endif +#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */ + +/* + * Domain parameters for secp224r1 + */ +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) +static const mbedtls_mpi_uint secp224r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0xFF, 0x55, 0x23, 0x43, 0x39, 0x0B, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0xD8, 0xBF, 0xD7, 0xB7, 0xB0, 0x44, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x56, 0x32, 0x41, 0xF5, 0xAB, 0xB3, 0x04, 0x0C), + MBEDTLS_BYTES_TO_T_UINT_4(0x85, 0x0A, 0x05, 0xB4), +}; +static const mbedtls_mpi_uint secp224r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x1D, 0x5C, 0x11, 0xD6, 0x80, 0x32, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0x11, 0xC2, 0x56, 0xD3, 0xC1, 0x03, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0x90, 0x13, 0x32, 0x7F, 0xBF, 0xB4, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_4(0xBD, 0x0C, 0x0E, 0xB7), +}; +static const mbedtls_mpi_uint secp224r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x7E, 0x00, 0x85, 0x99, 0x81, 0xD5, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x47, 0x07, 0x5A, 0xA0, 0x75, 0x43, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0xDF, 0x22, 0x4C, 0xFB, 0x23, 0xF7, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_4(0x88, 0x63, 0x37, 0xBD), +}; +static const mbedtls_mpi_uint secp224r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0x2A, 0x5C, 0x5C, 0x45, 0x29, 0xDD, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0xF0, 0xB8, 0xE0, 0xA2, 0x16, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_4(0xFF, 0xFF, 0xFF, 0xFF), +}; +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp224r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x1D, 0x5C, 0x11, 0xD6, 0x80, 0x32, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0x11, 0xC2, 0x56, 0xD3, 0xC1, 0x03, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0x90, 0x13, 0x32, 0x7F, 0xBF, 0xB4, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0x0C, 0x0E, 0xB7, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x7E, 0x00, 0x85, 0x99, 0x81, 0xD5, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x47, 0x07, 0x5A, 0xA0, 0x75, 0x43, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0xDF, 0x22, 0x4C, 0xFB, 0x23, 0xF7, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0x63, 0x37, 0xBD, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0xF9, 0xB8, 0xD0, 0x3D, 0xD2, 0xD3, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xFD, 0x99, 0x26, 0x19, 0xFE, 0x13, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x0E, 0x4C, 0x48, 0x7C, 0xA2, 0x17, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xA3, 0x13, 0x57, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0x16, 0x5C, 0x8F, 0xAA, 0xED, 0x0F, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0xC5, 0x43, 0x34, 0x93, 0x05, 0x2A, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0xE3, 0x6C, 0xCA, 0xC6, 0x14, 0xC2, 0x25), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x43, 0x6C, 0xD7, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0x5A, 0x98, 0x1E, 0xC8, 0xA5, 0x42, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x49, 0x56, 0x78, 0xF8, 0xEF, 0xED, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0xBB, 0x64, 0xB6, 0x4C, 0x54, 0x5F, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0x0C, 0x33, 0xCC, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x79, 0xCB, 0x2E, 0x08, 0xFF, 0xD8, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0x2E, 0x1F, 0xD4, 0xD7, 0x57, 0xE9, 0x39, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0xD6, 0x3B, 0x0A, 0x1C, 0x87, 0xB7, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0x30, 0xD8, 0x05, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x79, 0x74, 0x9A, 0xE6, 0xBB, 0xC2, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x5B, 0xA6, 0x67, 0xC1, 0x91, 0xE7, 0x64), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0xDF, 0x38, 0x82, 0x19, 0x2C, 0x4C, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0x2E, 0x39, 0xC5, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x36, 0x78, 0x4E, 0xAE, 0x5B, 0x02, 0x76), + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0xF6, 0x8B, 0xF8, 0xF4, 0x92, 0x6B, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x4D, 0x71, 0x35, 0xE7, 0x0C, 0x2C, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0xA5, 0x1F, 0xAE, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0x1C, 0x4B, 0xDF, 0x5B, 0xF2, 0x51, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0x74, 0xB1, 0x5A, 0xC6, 0x0F, 0x0E, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x24, 0x09, 0x62, 0xAF, 0xFC, 0xDB, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0xE1, 0x80, 0x55, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0x82, 0xFE, 0xAD, 0xC3, 0xE5, 0xCF, 0xD8), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0xA2, 0x62, 0x17, 0x76, 0xF0, 0x5A, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0xB8, 0xE5, 0xAC, 0xB7, 0x66, 0x38, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0xFD, 0x86, 0x05, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xD3, 0x0C, 0x3C, 0xD1, 0x66, 0xB0, 0xF1), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x59, 0xB4, 0x8D, 0x90, 0x10, 0xB7, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x47, 0x9B, 0xE6, 0x55, 0x8A, 0xE4, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0x49, 0xDB, 0x78, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x97, 0xED, 0xDE, 0xFF, 0xB3, 0xDF, 0x48), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0xB9, 0x83, 0xB7, 0xEB, 0xBE, 0x40, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xD3, 0xD3, 0xCD, 0x0E, 0x82, 0x79, 0x3D), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x83, 0x1B, 0xF0, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x22, 0xBB, 0x54, 0xD3, 0x31, 0x56, 0xFC), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x36, 0xE5, 0xE0, 0x89, 0x96, 0x8E, 0x71), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0xEF, 0x0A, 0xED, 0xD0, 0x11, 0x4A, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x00, 0x57, 0x27, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0xCA, 0x3D, 0xF7, 0x64, 0x9B, 0x6E, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0xE3, 0x70, 0x6B, 0x41, 0xD7, 0xED, 0x8F), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0x44, 0x44, 0x80, 0xCE, 0x13, 0x37, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x73, 0x80, 0x79, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x4D, 0x70, 0x7D, 0x31, 0x0F, 0x1C, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x35, 0x88, 0x47, 0xC4, 0x24, 0x78, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0xF0, 0xCD, 0x91, 0x81, 0xB3, 0xDE, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0xCE, 0xC6, 0xF7, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0x9C, 0x2D, 0xE8, 0xD2, 0x00, 0x8F, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0x5E, 0x7C, 0x0E, 0x0C, 0x6E, 0x58, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x81, 0x21, 0xCE, 0x43, 0xF4, 0x24, 0x3D), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0xBC, 0xF0, 0xF4, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x10, 0xC2, 0x74, 0x4A, 0x8F, 0x8A, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0x89, 0x67, 0xF4, 0x2B, 0x38, 0x2B, 0x35, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xE7, 0x0C, 0xA9, 0xFA, 0x77, 0x5C, 0xBD), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x33, 0x19, 0x2B, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0x3E, 0x96, 0x22, 0x53, 0xE1, 0xE9, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x13, 0xBC, 0xA1, 0x16, 0xEC, 0x01, 0x1A), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0x00, 0xC9, 0x7A, 0xC3, 0x73, 0xA5, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0xF4, 0x5E, 0xC1, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x95, 0xD6, 0xD9, 0x32, 0x30, 0x2B, 0xD0), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x42, 0x09, 0x05, 0x61, 0x2A, 0x7E, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0x84, 0xA2, 0x05, 0x88, 0x64, 0x65, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0x2D, 0x90, 0xB3, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0xE7, 0x2E, 0x85, 0x55, 0x80, 0x7C, 0x79), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0xC1, 0xAC, 0x78, 0xB4, 0xAF, 0xFB, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0xC3, 0x28, 0x8E, 0x79, 0x18, 0x1F, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x46, 0xCF, 0x49, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0x5F, 0xA8, 0x6C, 0x46, 0x83, 0x43, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0xA9, 0x93, 0x11, 0xB6, 0x07, 0x57, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x2A, 0x9D, 0x03, 0x89, 0x7E, 0xD7, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0x8C, 0x62, 0xCF, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x44, 0x2C, 0x13, 0x59, 0xCC, 0xFA, 0x84, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0xB9, 0x48, 0xBC, 0x57, 0xC7, 0xB3, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x0A, 0x38, 0x24, 0x2E, 0x3A, 0x28, 0x25), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x0A, 0x43, 0xB8, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0x25, 0xAB, 0xC1, 0xEE, 0x70, 0x3C, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0xDB, 0x45, 0x1D, 0x4A, 0x80, 0x75, 0x35), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x1F, 0x4D, 0x2D, 0x9A, 0x05, 0xF4, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x10, 0xF0, 0x5A, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0x95, 0xE1, 0xDC, 0x15, 0x86, 0xC3, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0xDC, 0x27, 0xD1, 0x56, 0xA1, 0x14, 0x0D), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0x0B, 0xD6, 0x77, 0x4E, 0x44, 0xA2, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x42, 0x71, 0x1F, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x86, 0xB2, 0xB0, 0xC8, 0x2F, 0x7B, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xEF, 0xCB, 0xDB, 0xBC, 0x9E, 0x3B, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x03, 0x86, 0xDD, 0x5B, 0xF5, 0x8D, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x95, 0x79, 0xD6, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0x32, 0x14, 0xDA, 0x9B, 0x4F, 0x07, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0x3E, 0xFB, 0x06, 0xEE, 0xA7, 0x40, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0x1F, 0xDF, 0x71, 0x61, 0xFD, 0x8B, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x8B, 0xAB, 0x8B, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x34, 0xB3, 0xB4, 0xBC, 0x9F, 0xB0, 0x5E), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x58, 0x48, 0xA8, 0x77, 0xBB, 0x13, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0xC6, 0xF7, 0x34, 0xCC, 0x89, 0x21, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x33, 0xDD, 0x1F, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x81, 0xEF, 0xA4, 0xF2, 0x10, 0x0B, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0xF7, 0x6E, 0x72, 0x4A, 0xDF, 0xDD, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0x23, 0x0A, 0x53, 0x03, 0x16, 0x62, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x76, 0xFD, 0x3C, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x14, 0xA1, 0xFA, 0xA0, 0x18, 0xBE, 0x07), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0x2A, 0xE1, 0xD7, 0xB0, 0x6C, 0xA0, 0xDE), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0xC0, 0xB0, 0xC6, 0x63, 0x24, 0xCD, 0x4E), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0x38, 0x2C, 0xB1, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0xCD, 0x7D, 0x20, 0x0C, 0xFE, 0xAC, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x97, 0x9F, 0xA2, 0xB6, 0x45, 0xF7, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x99, 0xF3, 0xD2, 0x20, 0x02, 0xEB, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0x18, 0x5B, 0x7B, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0xDD, 0x77, 0x91, 0x60, 0xEA, 0xFD, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0xD3, 0xB5, 0xD6, 0x90, 0x17, 0x0E, 0x1A), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0xF4, 0x28, 0xC1, 0xF2, 0x53, 0xF6, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0x58, 0xDC, 0x61, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x20, 0x01, 0xFB, 0xF1, 0xBD, 0x5F, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x7F, 0x06, 0xDA, 0x11, 0xCB, 0xBA, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x41, 0x00, 0xA4, 0x1B, 0x30, 0x33, 0x79), + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0xFF, 0x27, 0xCA, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_ecp_point secp224r1_T[16] = { + ECP_POINT_INIT_XY_Z1(secp224r1_T_0_X, secp224r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_1_X, secp224r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_2_X, secp224r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_3_X, secp224r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_4_X, secp224r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_5_X, secp224r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_6_X, secp224r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_7_X, secp224r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_8_X, secp224r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_9_X, secp224r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_10_X, secp224r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_11_X, secp224r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_12_X, secp224r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_13_X, secp224r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_14_X, secp224r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp224r1_T_15_X, secp224r1_T_15_Y), +}; +#else +#define secp224r1_T NULL +#endif +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */ + +/* + * Domain parameters for secp256r1 + */ +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) +static const mbedtls_mpi_uint secp256r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF), +}; +static const mbedtls_mpi_uint secp256r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0x60, 0xD2, 0x27, 0x3E, 0x3C, 0xCE, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0xB0, 0x53, 0xCC, 0xB0, 0x06, 0x1D, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x86, 0x98, 0x76, 0x55, 0xBD, 0xEB, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0x93, 0x3A, 0xAA, 0xD8, 0x35, 0xC6, 0x5A), +}; +static const mbedtls_mpi_uint secp256r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xC2, 0x98, 0xD8, 0x45, 0x39, 0xA1, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x33, 0xEB, 0x2D, 0x81, 0x7D, 0x03, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0x40, 0xA4, 0x63, 0xE5, 0xE6, 0xBC, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x42, 0x2C, 0xE1, 0xF2, 0xD1, 0x17, 0x6B), +}; +static const mbedtls_mpi_uint secp256r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x51, 0xBF, 0x37, 0x68, 0x40, 0xB6, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0x5E, 0x31, 0x6B, 0x57, 0x33, 0xCE, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0x9E, 0x0F, 0x7C, 0x4A, 0xEB, 0xE7, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x7F, 0x1A, 0xFE, 0xE2, 0x42, 0xE3, 0x4F), +}; +static const mbedtls_mpi_uint secp256r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF), +}; +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp256r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xC2, 0x98, 0xD8, 0x45, 0x39, 0xA1, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x33, 0xEB, 0x2D, 0x81, 0x7D, 0x03, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0x40, 0xA4, 0x63, 0xE5, 0xE6, 0xBC, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x42, 0x2C, 0xE1, 0xF2, 0xD1, 0x17, 0x6B), +}; +static const mbedtls_mpi_uint secp256r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x51, 0xBF, 0x37, 0x68, 0x40, 0xB6, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0x5E, 0x31, 0x6B, 0x57, 0x33, 0xCE, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0x9E, 0x0F, 0x7C, 0x4A, 0xEB, 0xE7, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x7F, 0x1A, 0xFE, 0xE2, 0x42, 0xE3, 0x4F), +}; +static const mbedtls_mpi_uint secp256r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0xC8, 0xBA, 0x04, 0xB7, 0x4B, 0xD2, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0xC6, 0x23, 0x3A, 0xA0, 0x09, 0x3A, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x9D, 0x4C, 0xF9, 0x58, 0x23, 0xCC, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0xED, 0x7B, 0x29, 0x87, 0x0F, 0xFA, 0x3C), +}; +static const mbedtls_mpi_uint secp256r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x69, 0xF2, 0x40, 0x0B, 0xA3, 0x98, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xA8, 0x48, 0x02, 0x0D, 0x1C, 0x12, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0xAF, 0x09, 0x83, 0x80, 0xAA, 0x58, 0xA7), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0x12, 0xBE, 0x70, 0x94, 0x76, 0xE3, 0xE4), +}; +static const mbedtls_mpi_uint secp256r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x7D, 0xEF, 0x86, 0xFF, 0xE3, 0x37, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x86, 0x8B, 0x08, 0x27, 0x7C, 0xD7, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x54, 0x4C, 0x25, 0x4F, 0x9A, 0xFE, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0xFD, 0xF0, 0x6D, 0x37, 0x03, 0x69, 0xD6), +}; +static const mbedtls_mpi_uint secp256r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xD5, 0xDA, 0xAD, 0x92, 0x49, 0xF0, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x73, 0x43, 0x9E, 0xAF, 0xA7, 0xD1, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0x41, 0x07, 0xDF, 0x78, 0x95, 0x3E, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0x3D, 0xD1, 0xE6, 0x3C, 0xA5, 0xE2, 0x20), +}; +static const mbedtls_mpi_uint secp256r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0x6A, 0x5D, 0x52, 0x35, 0xD7, 0xBF, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0xA2, 0xBE, 0x96, 0xF4, 0xF8, 0x02, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x20, 0x49, 0x54, 0xEA, 0xB3, 0x82, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x2E, 0xDB, 0xEA, 0x02, 0xD1, 0x75, 0x1C, 0x62), +}; +static const mbedtls_mpi_uint secp256r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x85, 0xF4, 0x9E, 0x4C, 0xDC, 0x39, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0x6D, 0xC4, 0x57, 0xD8, 0x03, 0x5D, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x7F, 0x2D, 0x52, 0x6F, 0xC9, 0xDA, 0x4F), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x64, 0xFA, 0xB4, 0xFE, 0xA4, 0xC4, 0xD7), +}; +static const mbedtls_mpi_uint secp256r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0x37, 0xB9, 0xC0, 0xAA, 0x59, 0xC6, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x58, 0xD9, 0xED, 0x58, 0x99, 0x65, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0x7D, 0x26, 0x8C, 0x4A, 0xF9, 0x05, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x73, 0x9A, 0xC9, 0xE7, 0x46, 0xDC, 0x00), +}; +static const mbedtls_mpi_uint secp256r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0xD0, 0x55, 0xDF, 0x00, 0x0A, 0xF5, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0xBF, 0x56, 0x81, 0x2D, 0x20, 0xEB, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0xC1, 0x28, 0x52, 0xAB, 0xE3, 0xD1, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0x34, 0x79, 0x45, 0x57, 0xA5, 0x12, 0x03), +}; +static const mbedtls_mpi_uint secp256r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0xCF, 0xB8, 0x7E, 0xF7, 0x92, 0x96, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0x01, 0x8C, 0x0D, 0x23, 0xF2, 0xE3, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0x2E, 0xE3, 0x84, 0x52, 0x7A, 0x34, 0x76), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0xA1, 0xB0, 0x15, 0x90, 0xE2, 0x53, 0x3C), +}; +static const mbedtls_mpi_uint secp256r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x98, 0xE7, 0xFA, 0xA5, 0x7D, 0x8B, 0x53), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x35, 0xD2, 0x00, 0xD1, 0x1B, 0x9F, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x69, 0x08, 0x9A, 0x72, 0xF0, 0xA9, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0xFE, 0x0E, 0x14, 0xDA, 0x7C, 0x0E, 0xD3), +}; +static const mbedtls_mpi_uint secp256r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0xF6, 0xE8, 0xF8, 0x87, 0xF7, 0xFC, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0xBE, 0x7F, 0x3F, 0x7A, 0x2B, 0xD7, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0xCF, 0x32, 0xF2, 0x2D, 0x94, 0x6D, 0x42, 0xFD), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x9A, 0xE3, 0x5F, 0x42, 0xBB, 0x84, 0xED), +}; +static const mbedtls_mpi_uint secp256r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x95, 0x29, 0x73, 0xA1, 0x67, 0x3E, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x30, 0x54, 0x35, 0x8E, 0x0A, 0xDD, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0xD7, 0xA1, 0x97, 0x61, 0x3B, 0xF8, 0x0C), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0x33, 0x3C, 0x58, 0x55, 0x34, 0x23, 0xA3), +}; +static const mbedtls_mpi_uint secp256r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x5D, 0x16, 0x5F, 0x7B, 0xBC, 0xBB, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0xEE, 0x4E, 0x8A, 0xC1, 0x51, 0xCC, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x0D, 0x4D, 0x1B, 0x53, 0x23, 0x1D, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x2A, 0x38, 0x66, 0x52, 0x84, 0xE1, 0x95), +}; +static const mbedtls_mpi_uint secp256r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0x9B, 0x83, 0x0A, 0x81, 0x4F, 0xAD, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0xFF, 0x42, 0x41, 0x6E, 0xA9, 0xA2, 0xA0), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0xA1, 0x4F, 0x1F, 0x89, 0x82, 0xAA, 0x3E), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0xB8, 0x0F, 0x6B, 0x8F, 0x8C, 0xD6, 0x68), +}; +static const mbedtls_mpi_uint secp256r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0xB3, 0xBB, 0x51, 0x69, 0xA2, 0x11, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x4F, 0x0F, 0x8D, 0xBD, 0x26, 0x0F, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0xCB, 0xEC, 0x6B, 0x34, 0xC3, 0x3D, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x5D, 0x1E, 0x10, 0xD5, 0x44, 0xE2, 0x54), +}; +static const mbedtls_mpi_uint secp256r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x9E, 0xB1, 0xF1, 0x6E, 0x4C, 0xAD, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xE3, 0xC2, 0x58, 0xC0, 0xFB, 0x34, 0x43), + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0x9C, 0xDF, 0x35, 0x07, 0x41, 0xBD, 0x19), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x6E, 0x10, 0xEC, 0x0E, 0xEC, 0xBB, 0xD6), +}; +static const mbedtls_mpi_uint secp256r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0xCF, 0xEF, 0x3F, 0x83, 0x1A, 0x88, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x29, 0xB5, 0xB9, 0xE0, 0xC9, 0xA3, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0x46, 0x1E, 0x77, 0xCD, 0x7E, 0xB3, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x21, 0xD0, 0xD4, 0xA3, 0x16, 0x08, 0xEE), +}; +static const mbedtls_mpi_uint secp256r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0xCA, 0xA8, 0xB3, 0xBF, 0x29, 0x99, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0xF2, 0x05, 0xC1, 0xCF, 0x5D, 0x91, 0x48), + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0x01, 0x49, 0xDB, 0x82, 0xDF, 0x5F, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0x06, 0x90, 0xAD, 0xE3, 0x38, 0xA4, 0xC4), +}; +static const mbedtls_mpi_uint secp256r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0xD2, 0x3A, 0xE8, 0x03, 0xC5, 0x6D, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0x35, 0xD0, 0xAE, 0x1D, 0x7A, 0x9F, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0x1E, 0xD2, 0xCB, 0xAC, 0x88, 0x27, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0xB9, 0x9C, 0xE0, 0x31, 0xDD, 0x99, 0x86), +}; +static const mbedtls_mpi_uint secp256r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0xF9, 0x9B, 0x32, 0x96, 0x41, 0x58, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x5A, 0x2A, 0xB8, 0x96, 0x0E, 0xB2, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x78, 0x2C, 0xC7, 0x08, 0x99, 0x19, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x59, 0x28, 0xE9, 0x84, 0x54, 0xE6, 0x16), +}; +static const mbedtls_mpi_uint secp256r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x38, 0x30, 0xDB, 0x70, 0x2C, 0x0A, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x5C, 0x9D, 0xE9, 0xD5, 0x46, 0x0B, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x0B, 0x60, 0x4B, 0x37, 0x7D, 0xB9, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0x24, 0xF3, 0x3D, 0x79, 0x7F, 0x6C, 0x18), +}; +static const mbedtls_mpi_uint secp256r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7F, 0xE5, 0x1C, 0x4F, 0x60, 0x24, 0xF7, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0xD8, 0xE2, 0x91, 0x7F, 0x89, 0x49, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0xA7, 0x2E, 0x8D, 0x6A, 0xB3, 0x39, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0x89, 0xB5, 0x9A, 0xB8, 0x8D, 0x42, 0x9C), +}; +static const mbedtls_mpi_uint secp256r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0x45, 0xE6, 0x4B, 0x3F, 0x4F, 0x1E, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x65, 0x5E, 0x59, 0x22, 0xCC, 0x72, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x93, 0x1A, 0x27, 0x1E, 0x34, 0xC5, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0xF2, 0xA5, 0x58, 0x5C, 0x15, 0x2E, 0xC6), +}; +static const mbedtls_mpi_uint secp256r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0x7F, 0xBA, 0x58, 0x5A, 0x84, 0x6F, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0xA6, 0x36, 0x7E, 0xDC, 0xF7, 0xE1, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x4D, 0xAA, 0xEE, 0x57, 0x76, 0x3A, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0x7E, 0x26, 0x18, 0x22, 0x23, 0x9F, 0xFF), +}; +static const mbedtls_mpi_uint secp256r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x4C, 0x64, 0xC7, 0x55, 0x02, 0x3F, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0x02, 0x90, 0xBB, 0xC3, 0xEC, 0x30, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0x6F, 0x64, 0xF4, 0x16, 0x69, 0x48, 0xA4), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x44, 0x9C, 0x95, 0x0C, 0x7D, 0x67, 0x5E), +}; +static const mbedtls_mpi_uint secp256r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x44, 0x91, 0x8B, 0xD8, 0xD0, 0xD7, 0xE7, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0xF9, 0x48, 0x62, 0x6F, 0xA8, 0x93, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0x3A, 0x99, 0x02, 0xD5, 0x0B, 0x3D, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xD3, 0x00, 0x31, 0xE6, 0x0C, 0x9F, 0x44), +}; +static const mbedtls_mpi_uint secp256r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x56, 0xB2, 0xAA, 0xFD, 0x88, 0x15, 0xDF, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x4C, 0x35, 0x27, 0x31, 0x44, 0xCD, 0xC0, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x53, 0xF8, 0x91, 0xA5, 0x71, 0x94, 0x84, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0xCB, 0xD0, 0x93, 0xE9, 0x88, 0xDA, 0xE4), +}; +static const mbedtls_mpi_uint secp256r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0xC6, 0x39, 0x16, 0x5D, 0xA3, 0x1E, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x07, 0x37, 0x26, 0x36, 0x2A, 0xFE, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0xBC, 0xF3, 0xD0, 0xDE, 0x50, 0xFC, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x2E, 0x06, 0x10, 0x15, 0x4D, 0xFA, 0xF7), +}; +static const mbedtls_mpi_uint secp256r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x65, 0x69, 0x5B, 0x66, 0xA2, 0x75, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0x16, 0x00, 0x5A, 0xB0, 0x30, 0x25, 0x1A), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0xFB, 0x86, 0x42, 0x80, 0xC1, 0xC4, 0x76), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0x1D, 0x83, 0x8E, 0x94, 0x01, 0x5F, 0x82), +}; +static const mbedtls_mpi_uint secp256r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0x37, 0x70, 0xEF, 0x1F, 0xA1, 0xF0, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x10, 0x5B, 0xCE, 0xC4, 0x9B, 0x6F, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x11, 0x11, 0x24, 0x4F, 0x4C, 0x79, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0x3A, 0x72, 0xBC, 0xFE, 0x72, 0x58, 0x43), +}; +static const mbedtls_ecp_point secp256r1_T[16] = { + ECP_POINT_INIT_XY_Z1(secp256r1_T_0_X, secp256r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_1_X, secp256r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_2_X, secp256r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_3_X, secp256r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_4_X, secp256r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_5_X, secp256r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_6_X, secp256r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_7_X, secp256r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_8_X, secp256r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_9_X, secp256r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_10_X, secp256r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_11_X, secp256r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_12_X, secp256r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_13_X, secp256r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_14_X, secp256r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp256r1_T_15_X, secp256r1_T_15_Y), +}; +#else +#define secp256r1_T NULL +#endif + +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */ + +/* + * Domain parameters for secp384r1 + */ +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +static const mbedtls_mpi_uint secp384r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; +static const mbedtls_mpi_uint secp384r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0x2A, 0xEC, 0xD3, 0xED, 0xC8, 0x85, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0xD1, 0x2E, 0x8A, 0x8D, 0x39, 0x56, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x87, 0x13, 0x50, 0x8F, 0x08, 0x14, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0x41, 0x81, 0xFE, 0x6E, 0x9C, 0x1D, 0x18), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x2D, 0xF8, 0xE3, 0x6B, 0x05, 0x8E, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0xE7, 0x3E, 0xE2, 0xA7, 0x2F, 0x31, 0xB3), +}; +static const mbedtls_mpi_uint secp384r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x0A, 0x76, 0x72, 0x38, 0x5E, 0x54, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x29, 0x55, 0xBF, 0x5D, 0xF2, 0x02, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x2A, 0x54, 0x82, 0xE0, 0x41, 0xF7, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x9B, 0xA7, 0x8B, 0x62, 0x3B, 0x1D, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0xAD, 0x20, 0xF3, 0x1E, 0xC7, 0xB1, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x05, 0x8B, 0xBE, 0x22, 0xCA, 0x87, 0xAA), +}; +static const mbedtls_mpi_uint secp384r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x0E, 0xEA, 0x90, 0x7C, 0x1D, 0x43, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x81, 0x7E, 0x1D, 0xCE, 0xB1, 0x60, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0xB8, 0xF0, 0xB5, 0x13, 0x31, 0xDA, 0xE9), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x14, 0x9A, 0x28, 0xBD, 0x1D, 0xF4, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0xDC, 0x92, 0x92, 0xBF, 0x98, 0x9E, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x2C, 0x26, 0x96, 0x4A, 0xDE, 0x17, 0x36), +}; +static const mbedtls_mpi_uint secp384r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0x29, 0xC5, 0xCC, 0x6A, 0x19, 0xEC, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x7A, 0xA7, 0xB0, 0x48, 0xB2, 0x0D, 0x1A, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0x2D, 0x37, 0xF4, 0x81, 0x4D, 0x63, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp384r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x0A, 0x76, 0x72, 0x38, 0x5E, 0x54, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x29, 0x55, 0xBF, 0x5D, 0xF2, 0x02, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x2A, 0x54, 0x82, 0xE0, 0x41, 0xF7, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x9B, 0xA7, 0x8B, 0x62, 0x3B, 0x1D, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0xAD, 0x20, 0xF3, 0x1E, 0xC7, 0xB1, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x05, 0x8B, 0xBE, 0x22, 0xCA, 0x87, 0xAA), +}; +static const mbedtls_mpi_uint secp384r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x0E, 0xEA, 0x90, 0x7C, 0x1D, 0x43, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x81, 0x7E, 0x1D, 0xCE, 0xB1, 0x60, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0xB8, 0xF0, 0xB5, 0x13, 0x31, 0xDA, 0xE9), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x14, 0x9A, 0x28, 0xBD, 0x1D, 0xF4, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0xDC, 0x92, 0x92, 0xBF, 0x98, 0x9E, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x2C, 0x26, 0x96, 0x4A, 0xDE, 0x17, 0x36), +}; +static const mbedtls_mpi_uint secp384r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0x92, 0x00, 0x2C, 0x78, 0xDB, 0x1F, 0x37), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xF3, 0xEB, 0xB7, 0x06, 0xF7, 0xB6, 0xBC), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xBC, 0x2C, 0xCF, 0xD8, 0xED, 0x53, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0x75, 0x7B, 0xA3, 0xAB, 0xC3, 0x2C, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0x9D, 0x78, 0x41, 0xF6, 0x76, 0x84, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x56, 0xE8, 0x52, 0xB3, 0xCB, 0xA8, 0xBD), +}; +static const mbedtls_mpi_uint secp384r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xF2, 0xAE, 0xA4, 0xB6, 0x89, 0x1B, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x0F, 0xCE, 0x1C, 0x7C, 0xF6, 0x50, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0x4C, 0xEB, 0x90, 0xE6, 0x4D, 0xC7, 0xD4, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0x49, 0x2D, 0x8A, 0x01, 0x99, 0x60, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x80, 0x9B, 0x9B, 0x6A, 0xB0, 0x07, 0xD9), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0xA2, 0xEE, 0x59, 0xBE, 0x95, 0xBC, 0x23), +}; +static const mbedtls_mpi_uint secp384r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x9D, 0x56, 0xAE, 0x59, 0xFB, 0x1F, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0xCF, 0xAC, 0x91, 0x80, 0x87, 0xA8, 0x6E, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x08, 0xA7, 0x08, 0x94, 0x32, 0xFC, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0x29, 0x9E, 0x84, 0xF4, 0xE5, 0x6E, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x21, 0xB9, 0x50, 0x24, 0xF8, 0x9C, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x04, 0x01, 0xC2, 0xFB, 0x77, 0x3E, 0xDE), +}; +static const mbedtls_mpi_uint secp384r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x38, 0xEE, 0xE3, 0xC7, 0x9D, 0xEC, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x88, 0xCF, 0x43, 0xFA, 0x92, 0x5E, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0xCA, 0x43, 0xF8, 0x3B, 0x49, 0x7E, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0xE7, 0xEB, 0x17, 0x45, 0x86, 0xC2, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x69, 0x57, 0x32, 0xE0, 0x9C, 0xD1, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x10, 0xB8, 0x4D, 0xB8, 0xF4, 0x0D, 0xE3), +}; +static const mbedtls_mpi_uint secp384r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x60, 0xDC, 0x9A, 0xB2, 0x79, 0x39, 0x27, 0x16), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0x71, 0xE4, 0x3B, 0x4D, 0x60, 0x0C, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0xBD, 0x19, 0x40, 0xFA, 0x19, 0x2A, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0xF8, 0x1E, 0x43, 0xA1, 0x50, 0x8D, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0x18, 0x7C, 0x41, 0xFA, 0x7C, 0x1B, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x59, 0x24, 0xC4, 0xE9, 0xB7, 0xD3, 0xAD), +}; +static const mbedtls_mpi_uint secp384r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x01, 0x3D, 0x63, 0x54, 0x45, 0x6F, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0xB2, 0x19, 0xA3, 0x86, 0x1D, 0x42, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0x02, 0x87, 0x18, 0x92, 0x52, 0x1A, 0x71), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x18, 0xB1, 0x5D, 0x18, 0x1B, 0x37, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0x74, 0x61, 0xBA, 0x18, 0xAF, 0x40, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x7D, 0x3C, 0x52, 0x0F, 0x07, 0xB0, 0x6F), +}; +static const mbedtls_mpi_uint secp384r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x39, 0x13, 0xAA, 0x60, 0x15, 0x99, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0x00, 0xCB, 0xC6, 0xB1, 0xDB, 0x97, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0xFA, 0x60, 0xB8, 0x24, 0xE4, 0x7D, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x75, 0xB3, 0x70, 0xB2, 0x83, 0xB1, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0xE3, 0x6C, 0xCD, 0x33, 0x62, 0x7A, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0x30, 0xDC, 0x0F, 0x9F, 0xBB, 0xB8, 0xAA), +}; +static const mbedtls_mpi_uint secp384r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA6, 0xD5, 0x0A, 0x60, 0x81, 0xB9, 0xC5, 0x16), + MBEDTLS_BYTES_TO_T_UINT_8(0x44, 0xAA, 0x2F, 0xD6, 0xF2, 0x73, 0xDF, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0x7B, 0x74, 0xC9, 0xB3, 0x5B, 0x95, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x04, 0xEB, 0x15, 0xC8, 0x5F, 0x00, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0x50, 0x20, 0x28, 0xD1, 0x01, 0xAF, 0xF0), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x6D, 0x4F, 0x31, 0x81, 0x2F, 0x94, 0x48), +}; +static const mbedtls_mpi_uint secp384r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0x2F, 0xD8, 0xB6, 0x63, 0x7C, 0xE9, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x8C, 0xB9, 0x14, 0xD9, 0x37, 0x63, 0xDE), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0x02, 0xB8, 0x46, 0xAD, 0xCE, 0x7B, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0x82, 0x47, 0x2D, 0x66, 0xA7, 0xE9, 0x33, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0xF9, 0x93, 0x94, 0xA8, 0x48, 0xB3, 0x4F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0x4A, 0xAC, 0x51, 0x08, 0x72, 0x2F, 0x1A), +}; +static const mbedtls_mpi_uint secp384r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0xAD, 0xA0, 0xF9, 0x81, 0xE1, 0x78, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x9A, 0x63, 0xD8, 0xBA, 0x79, 0x1A, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x31, 0x7B, 0x7A, 0x5A, 0x5D, 0x7D, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x96, 0x12, 0x4B, 0x19, 0x09, 0xE0, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x8A, 0x57, 0xEE, 0x4E, 0x6E, 0x7E, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x9D, 0x69, 0xDC, 0xB3, 0xDA, 0xD8, 0x08), +}; +static const mbedtls_mpi_uint secp384r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x49, 0x03, 0x03, 0x33, 0x6F, 0x28, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0xDB, 0xA7, 0x05, 0x8C, 0xF3, 0x4D, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x92, 0xB1, 0xA8, 0xEC, 0x0D, 0x64, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0xFC, 0xFD, 0xD0, 0x4B, 0x88, 0x1B, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x9C, 0x51, 0x69, 0xCE, 0x71, 0x73, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0x5A, 0x14, 0x23, 0x1A, 0x46, 0x63, 0x5F), +}; +static const mbedtls_mpi_uint secp384r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x4C, 0x70, 0x44, 0x18, 0xCD, 0xEF, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x49, 0xDD, 0x64, 0x7E, 0x7E, 0x4D, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0xA2, 0x32, 0x7C, 0x09, 0xD0, 0x3F, 0xD6, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xE0, 0x4F, 0x65, 0x0C, 0x7A, 0x54, 0x3E), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0xFA, 0xFB, 0x4A, 0xB4, 0x79, 0x5A, 0x8C), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x5D, 0x1B, 0x2B, 0xDA, 0xBC, 0x9A, 0x74), +}; +static const mbedtls_mpi_uint secp384r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0xAC, 0x56, 0xF7, 0x5F, 0x51, 0x68, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0xE0, 0x1D, 0xBC, 0x13, 0x4E, 0xAC, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xF5, 0xC5, 0xE6, 0xD2, 0x88, 0xBA, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x0E, 0x28, 0x23, 0x58, 0x67, 0xFA, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0x80, 0x4B, 0xD8, 0xC4, 0xDF, 0x15, 0xE4), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x0E, 0x58, 0xE6, 0x2C, 0x59, 0xC2, 0x03), +}; +static const mbedtls_mpi_uint secp384r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x26, 0x27, 0x99, 0x16, 0x2B, 0x22, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0xF3, 0x8F, 0xC3, 0x2A, 0x9B, 0xFC, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x2E, 0x83, 0x3D, 0xFE, 0x9E, 0x3C, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x57, 0xCD, 0x2D, 0xC1, 0x49, 0x38, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0x42, 0x8B, 0x33, 0x89, 0x1F, 0xEA, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0x1D, 0x13, 0xD7, 0x50, 0xBB, 0x3E, 0xEB), +}; +static const mbedtls_mpi_uint secp384r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD2, 0x9A, 0x52, 0xD2, 0x54, 0x7C, 0x97, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x33, 0x6E, 0xED, 0xD9, 0x87, 0x50, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x35, 0x7E, 0x16, 0x40, 0x15, 0x83, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0x2B, 0xA4, 0xAB, 0x03, 0x91, 0xEA, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x47, 0x39, 0xEF, 0x05, 0x59, 0xD0, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0x24, 0x0D, 0x76, 0x11, 0x53, 0x08, 0xAF), +}; +static const mbedtls_mpi_uint secp384r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x2F, 0xDD, 0xBD, 0x50, 0x48, 0xB1, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x1C, 0x84, 0x55, 0x78, 0x14, 0xEB, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x5E, 0x3E, 0xA6, 0xAF, 0xF6, 0xC7, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0x11, 0xE2, 0x65, 0xCA, 0x41, 0x95, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x83, 0xD8, 0xE6, 0x4D, 0x22, 0x06, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x7F, 0x25, 0x2A, 0xAA, 0x28, 0x46, 0x97), +}; +static const mbedtls_mpi_uint secp384r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0xDB, 0x15, 0x56, 0x84, 0xCB, 0xC0, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0x56, 0xDB, 0x0E, 0x08, 0xC9, 0xF5, 0xD4, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x62, 0xD0, 0x1A, 0x7C, 0x13, 0xD5, 0x07), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0xAD, 0x53, 0xE0, 0x32, 0x21, 0xA0, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0x38, 0x81, 0x21, 0x23, 0x0E, 0xD2, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x51, 0x05, 0xD0, 0x1E, 0x82, 0xA9, 0x71), +}; +static const mbedtls_mpi_uint secp384r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0xC3, 0x27, 0xBF, 0xC6, 0xAA, 0xB7, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x65, 0x45, 0xDF, 0xB9, 0x46, 0x17, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x38, 0x3F, 0xB2, 0xB1, 0x5D, 0xCA, 0x1C), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0x29, 0x6C, 0x63, 0xE9, 0xD7, 0x48, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xF1, 0xD7, 0x99, 0x8C, 0xC2, 0x05, 0x99), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xE6, 0x5E, 0x82, 0x6D, 0xE5, 0x7E, 0xD5), +}; +static const mbedtls_mpi_uint secp384r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0x61, 0xFA, 0x7D, 0x01, 0xDB, 0xB6, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0xC6, 0x58, 0x39, 0xF4, 0xC6, 0x82, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x5A, 0x7A, 0x80, 0x08, 0xCD, 0xAA, 0xD8), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x8C, 0xC6, 0x3F, 0x3C, 0xA5, 0x68, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0xF5, 0xD5, 0x17, 0xAE, 0x36, 0xD8, 0x8A), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0xAD, 0x92, 0xC5, 0x57, 0x6C, 0xDA, 0x91), +}; +static const mbedtls_mpi_uint secp384r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x67, 0x17, 0xC0, 0x40, 0x78, 0x8C, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x9F, 0xF4, 0xAA, 0xDA, 0x5C, 0x7E, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xDB, 0x42, 0x3E, 0x72, 0x64, 0xA0, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0xF9, 0x41, 0x17, 0x43, 0xE3, 0xE8, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0xDD, 0xCC, 0x43, 0x7E, 0x16, 0x05, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0x4B, 0xCF, 0x48, 0x8F, 0x41, 0x90, 0xE5), +}; +static const mbedtls_mpi_uint secp384r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x0C, 0x6B, 0x9D, 0x22, 0x04, 0xBC, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x63, 0x79, 0x2F, 0x6A, 0x0E, 0x8A, 0xDE), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x67, 0x3F, 0x02, 0xB8, 0x91, 0x7F, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x14, 0x64, 0xA0, 0x33, 0xF4, 0x6B, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x44, 0x71, 0x87, 0xB8, 0x88, 0x3F, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x2B, 0x85, 0x05, 0xC5, 0x44, 0x53, 0x15), +}; +static const mbedtls_mpi_uint secp384r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0x2B, 0xFE, 0xD1, 0x1C, 0x73, 0xE3, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0x33, 0xA1, 0xD3, 0x69, 0x1C, 0x9D, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x5A, 0xBA, 0xB6, 0xAE, 0x1B, 0x94, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0x74, 0x90, 0x5C, 0x57, 0xB0, 0x3A, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x2F, 0x93, 0x20, 0x24, 0x54, 0x1D, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x78, 0x9D, 0x71, 0x67, 0x5D, 0x49, 0x98), +}; +static const mbedtls_mpi_uint secp384r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0xC8, 0x0E, 0x11, 0x8D, 0xE0, 0x8F, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0x7F, 0x79, 0x6C, 0x5F, 0xB7, 0xBC, 0xB1), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0xE1, 0x83, 0x3C, 0x12, 0xBB, 0xEE, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xC2, 0xC4, 0x1B, 0x41, 0x71, 0xB9, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0xEE, 0xBB, 0x1D, 0x89, 0x50, 0x88, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x1C, 0x55, 0x74, 0xEB, 0xDE, 0x92, 0x3F), +}; +static const mbedtls_mpi_uint secp384r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0x38, 0x92, 0x06, 0x19, 0xD0, 0xB3, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0x99, 0x26, 0xA3, 0x5F, 0xE2, 0xC1, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0xFC, 0xFD, 0xC3, 0xB6, 0x26, 0x24, 0x8F), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xAD, 0xE7, 0x49, 0xB7, 0x64, 0x4B, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x4E, 0x95, 0xAD, 0x07, 0xFE, 0xB6, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0x15, 0xE7, 0x2D, 0x19, 0xA9, 0x08, 0x10), +}; +static const mbedtls_mpi_uint secp384r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0xBD, 0xAC, 0x0A, 0x3F, 0x6B, 0xFF, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0xE4, 0x74, 0x14, 0xD9, 0x70, 0x1D, 0x71), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0xB0, 0x71, 0xBB, 0xD8, 0x18, 0x96, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0xB8, 0x19, 0x90, 0x80, 0xB5, 0xEE, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x21, 0x20, 0xA6, 0x17, 0x48, 0x03, 0x6F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x1D, 0xBB, 0x6D, 0x94, 0x20, 0x34, 0xF1), +}; +static const mbedtls_mpi_uint secp384r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0x82, 0x67, 0x4B, 0x8E, 0x4E, 0xBE, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0xDA, 0x77, 0xF8, 0x23, 0x55, 0x2B, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0x02, 0xDE, 0x25, 0x35, 0x2D, 0x74, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x0C, 0xB8, 0x0B, 0x39, 0xBA, 0xAD, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0xA6, 0x0E, 0x28, 0x4D, 0xE1, 0x3D, 0xE4, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0xEC, 0x0A, 0xD4, 0xB8, 0xC4, 0x8D, 0xB0), +}; +static const mbedtls_mpi_uint secp384r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0x68, 0xCE, 0xC2, 0x55, 0x4D, 0x0C, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x20, 0x93, 0x32, 0x90, 0xD6, 0xAE, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x78, 0xAB, 0x43, 0x9E, 0xEB, 0x73, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0x97, 0xC3, 0x83, 0xA6, 0x3C, 0xF1, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0x25, 0x25, 0x66, 0x08, 0x26, 0xFA, 0x4B), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0xFB, 0x44, 0x5D, 0x82, 0xEC, 0x3B, 0xAC), +}; +static const mbedtls_mpi_uint secp384r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x90, 0xEA, 0xB5, 0x04, 0x99, 0xD0, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0xF2, 0x22, 0xA0, 0xEB, 0xFD, 0x45, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0xA4, 0x81, 0x32, 0xFC, 0xFA, 0xEE, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0xBB, 0xA4, 0x6A, 0x77, 0x41, 0x5C, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x1E, 0xAA, 0x4F, 0xF0, 0x10, 0xB3, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x74, 0x13, 0x14, 0x9E, 0x90, 0xD7, 0xE6), +}; +static const mbedtls_mpi_uint secp384r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0xBD, 0x70, 0x4F, 0xA8, 0xD1, 0x06, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x4E, 0x2E, 0x68, 0xFC, 0x35, 0xFA, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x60, 0x53, 0x75, 0xED, 0xF2, 0x5F, 0xC2, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0x87, 0x6B, 0x9F, 0x05, 0xE2, 0x22, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0x1A, 0xA8, 0xB7, 0x03, 0x9E, 0x6D, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0xD0, 0x69, 0x88, 0xA8, 0x39, 0x9E, 0x3A), +}; +static const mbedtls_mpi_uint secp384r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0xEF, 0x68, 0xFE, 0xEC, 0x24, 0x08, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x06, 0x4B, 0x92, 0x0D, 0xB7, 0x34, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0xF4, 0xDD, 0x1A, 0xA0, 0x4A, 0xE4, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0x63, 0x4F, 0x4F, 0xCE, 0xBB, 0xD6, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0xEE, 0x8D, 0xDF, 0x3F, 0x73, 0xB7, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0x06, 0xB6, 0x80, 0x4D, 0x81, 0xD9, 0x53), +}; +static const mbedtls_mpi_uint secp384r1_T_16_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0xF5, 0x13, 0xDF, 0x13, 0x19, 0x97, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0xF9, 0xB3, 0x33, 0x66, 0x82, 0x21, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xFC, 0x39, 0x16, 0x23, 0x43, 0x76, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x48, 0x25, 0xA1, 0x64, 0x95, 0x1C, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0xAC, 0x15, 0x57, 0xD9, 0xDE, 0xA0, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0x5F, 0xB8, 0x3D, 0x48, 0x91, 0x24, 0xCC), +}; +static const mbedtls_mpi_uint secp384r1_T_16_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0xF2, 0xC8, 0x54, 0xD1, 0x32, 0xBD, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0x3B, 0xF0, 0xAA, 0x9D, 0xD8, 0xF4, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0xC3, 0xBB, 0x6C, 0x66, 0xAC, 0x25, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x25, 0x10, 0xB2, 0xE1, 0x41, 0xDE, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0xE8, 0x30, 0xB8, 0x37, 0xBC, 0x2A, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x57, 0x01, 0x4A, 0x1E, 0x78, 0x9F, 0x85), +}; +static const mbedtls_mpi_uint secp384r1_T_17_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0x19, 0xCD, 0x12, 0x0B, 0x51, 0x4F, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x4B, 0x3D, 0x24, 0xA4, 0x16, 0x59, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xEB, 0xD3, 0x59, 0x2E, 0x75, 0x7C, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0xB9, 0xB4, 0xA5, 0xD9, 0x2E, 0x29, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x16, 0x05, 0x75, 0x02, 0xB3, 0x06, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0x7C, 0x9F, 0x79, 0x91, 0xF1, 0x4F, 0x23), +}; +static const mbedtls_mpi_uint secp384r1_T_17_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x98, 0x7C, 0x84, 0xE1, 0xFF, 0x30, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0xE2, 0xC2, 0x5F, 0x55, 0x40, 0xBD, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0x65, 0x87, 0x3F, 0xC4, 0xC2, 0x24, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0x30, 0x0A, 0x60, 0x15, 0xD1, 0x24, 0x48), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0x99, 0xD9, 0xB6, 0xAE, 0xB1, 0xAF, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x80, 0xEE, 0xA2, 0x0F, 0x74, 0xB9, 0xF3), +}; +static const mbedtls_mpi_uint secp384r1_T_18_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0xE6, 0x0F, 0x37, 0xC1, 0x10, 0x99, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0xAD, 0x9D, 0x5D, 0x80, 0x01, 0xA6, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x0F, 0x10, 0x2A, 0x9D, 0x20, 0x38, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x60, 0xCB, 0xCE, 0x5A, 0xA0, 0xA7, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0xCF, 0x14, 0xDF, 0xBF, 0xE5, 0x74, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0x12, 0x1A, 0xDD, 0x59, 0x02, 0x5D, 0xC6), +}; +static const mbedtls_mpi_uint secp384r1_T_18_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0xC9, 0xF8, 0xF5, 0xB6, 0x13, 0x4D, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0x45, 0xB1, 0x93, 0xB3, 0xA2, 0x79, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0xF6, 0xCF, 0xF7, 0xE6, 0x29, 0x9C, 0xCC), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x50, 0x65, 0x80, 0xBC, 0x59, 0x0A, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0xF0, 0x24, 0x35, 0xA2, 0x46, 0xF0, 0x0C), + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0x26, 0xC0, 0x9D, 0x61, 0x56, 0x62, 0x67), +}; +static const mbedtls_mpi_uint secp384r1_T_19_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0xBB, 0xC2, 0x24, 0x43, 0x2E, 0x37, 0x54), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0xF7, 0xCE, 0x35, 0xFC, 0x77, 0xF3, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0x34, 0x96, 0xD5, 0x4A, 0x76, 0x9D, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0x3B, 0x0F, 0xEA, 0xA8, 0x12, 0x0B, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0x3F, 0x5D, 0x2D, 0x1C, 0xD4, 0x9E, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x2E, 0xDD, 0xC7, 0x6E, 0xAB, 0xAF, 0xDC), +}; +static const mbedtls_mpi_uint secp384r1_T_19_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0xB2, 0x7B, 0x0C, 0x9A, 0x83, 0x8E, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x51, 0x90, 0x92, 0x79, 0x32, 0x19, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0x89, 0xF9, 0xD0, 0xCF, 0x2C, 0xA5, 0x8F), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0x50, 0x21, 0xDE, 0x50, 0x41, 0x9D, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x7D, 0x2B, 0x9E, 0x9D, 0x95, 0xA8, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0xA5, 0x20, 0x87, 0x88, 0x97, 0x5F, 0xAA), +}; +static const mbedtls_mpi_uint secp384r1_T_20_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x59, 0xB4, 0x66, 0x7E, 0xE8, 0x5A, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0x5C, 0x7E, 0xB2, 0xAD, 0xD9, 0xC9, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0x82, 0x97, 0x49, 0xA3, 0x13, 0x83, 0x07, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x26, 0xC7, 0x13, 0x35, 0x0D, 0xB0, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x60, 0xAB, 0xFA, 0x4B, 0x93, 0x18, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x2D, 0x1C, 0x31, 0x4C, 0xE4, 0x61, 0xAE), +}; +static const mbedtls_mpi_uint secp384r1_T_20_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDE, 0x4D, 0x1E, 0x51, 0x59, 0x6E, 0x91, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x54, 0x4D, 0x51, 0xED, 0x36, 0xCC, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0xA8, 0x56, 0xC7, 0x78, 0x27, 0x33, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0xB7, 0x95, 0xC9, 0x8B, 0xC8, 0x6A, 0xBC), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0xE9, 0x13, 0x96, 0xB3, 0xE1, 0xF9, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x46, 0xB0, 0x5E, 0xC3, 0x94, 0x03, 0x05), +}; +static const mbedtls_mpi_uint secp384r1_T_21_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x5B, 0x29, 0x30, 0x41, 0x1A, 0x9E, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0xCA, 0x83, 0x31, 0x5B, 0xA7, 0xCB, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x41, 0x50, 0x44, 0x4D, 0x64, 0x31, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0xCF, 0x84, 0xC2, 0x5D, 0x97, 0xA5, 0x3C, 0x18), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x0F, 0xA5, 0xFD, 0x8E, 0x5A, 0x47, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x58, 0x02, 0x2D, 0x40, 0xB1, 0x0B, 0xBA), +}; +static const mbedtls_mpi_uint secp384r1_T_21_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x33, 0x8C, 0x67, 0xCE, 0x23, 0x43, 0x99), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0x53, 0x47, 0x72, 0x44, 0x1F, 0x5B, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0xC1, 0xD9, 0xA4, 0x50, 0x88, 0x63, 0x18), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0xF2, 0x75, 0x69, 0x73, 0x00, 0xC4, 0x31), + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0x90, 0x1D, 0xDF, 0x1A, 0x00, 0xD8, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0xB1, 0x89, 0x48, 0xA8, 0x70, 0x62, 0xEF), +}; +static const mbedtls_mpi_uint secp384r1_T_22_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x8A, 0x55, 0x50, 0x7B, 0xEF, 0x8A, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0x1B, 0x23, 0x48, 0x23, 0x63, 0x91, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0x04, 0x54, 0x3C, 0x24, 0x9B, 0xC7, 0x9A), + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0x38, 0xC3, 0x84, 0xFB, 0xFF, 0x9F, 0x49), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0x2A, 0xE0, 0x6D, 0x68, 0x8A, 0x5C, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0x93, 0x53, 0x85, 0xA1, 0x0D, 0xAF, 0x63), +}; +static const mbedtls_mpi_uint secp384r1_T_22_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x88, 0x95, 0x4C, 0x0B, 0xD0, 0x06, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0xAF, 0x8D, 0x49, 0xA2, 0xC8, 0xB4, 0xE0), + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0x76, 0x53, 0x09, 0x88, 0x43, 0x87, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0xA4, 0x77, 0x3F, 0x5E, 0x21, 0xB4, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0x9E, 0x86, 0x64, 0xCC, 0x91, 0xC1, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x17, 0x56, 0xCB, 0xC3, 0x7D, 0x5B, 0xB1), +}; +static const mbedtls_mpi_uint secp384r1_T_23_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x74, 0x9F, 0xB5, 0x91, 0x21, 0xB1, 0x1C), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xED, 0xE1, 0x11, 0xEF, 0x45, 0xAF, 0xC1), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x31, 0xBE, 0xB2, 0xBC, 0x72, 0x65, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0x4B, 0x8C, 0x77, 0xCE, 0x1E, 0x42, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xC9, 0xAA, 0xB9, 0xD9, 0x86, 0x99, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x23, 0x80, 0xC6, 0x4E, 0x35, 0x0B, 0x6D), +}; +static const mbedtls_mpi_uint secp384r1_T_23_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0xD8, 0xA2, 0x0A, 0x39, 0x32, 0x1D, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0xC8, 0x86, 0xF1, 0x12, 0x9A, 0x4A, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xF1, 0x7C, 0xAA, 0x70, 0x8E, 0xBC, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x01, 0x47, 0x8F, 0xDD, 0x8B, 0xA5, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x08, 0x21, 0xF4, 0xAB, 0xC7, 0xF5, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0x76, 0xA5, 0x95, 0xC4, 0x0F, 0x88, 0x1D), +}; +static const mbedtls_mpi_uint secp384r1_T_24_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x42, 0x2A, 0x52, 0xCD, 0x75, 0x51, 0x49), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0x36, 0xE5, 0x04, 0x2B, 0x44, 0xC6, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0xEE, 0x16, 0x13, 0x07, 0x83, 0xB5, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0x59, 0xC6, 0xA2, 0x19, 0x05, 0xD3, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x8B, 0xA8, 0x16, 0x09, 0xB7, 0xEA, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0xEE, 0x14, 0xAF, 0xB5, 0xFD, 0xD0, 0xEF), +}; +static const mbedtls_mpi_uint secp384r1_T_24_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0x7C, 0xCA, 0x71, 0x3E, 0x6E, 0x66, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0x31, 0x0E, 0x3F, 0xE5, 0x91, 0xC4, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x3D, 0xC2, 0x3E, 0x95, 0x37, 0x58, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x1F, 0x02, 0x03, 0xF3, 0xEF, 0xEE, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x5B, 0x1A, 0xFC, 0x38, 0xCD, 0xE8, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0x57, 0x42, 0x85, 0xC6, 0x21, 0x68, 0x71), +}; +static const mbedtls_mpi_uint secp384r1_T_25_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xA2, 0x4A, 0x66, 0xB1, 0x0A, 0xE6, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x0C, 0x94, 0x9D, 0x5E, 0x99, 0xB2, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x03, 0x40, 0xCA, 0xB2, 0xB3, 0x30, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x78, 0x48, 0x27, 0x34, 0x1E, 0xE2, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x72, 0x5B, 0xAC, 0xC1, 0x6D, 0xE3, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0xAB, 0x46, 0xCB, 0xEA, 0x5E, 0x4B, 0x0B), +}; +static const mbedtls_mpi_uint secp384r1_T_25_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x08, 0xAD, 0x4E, 0x51, 0x9F, 0x2A, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x5C, 0x7D, 0x4C, 0xD6, 0xCF, 0xDD, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0x76, 0x26, 0xE0, 0x8B, 0x10, 0xD9, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0xA7, 0x23, 0x4E, 0x5F, 0xD2, 0x42, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0xE5, 0xA4, 0xEC, 0x77, 0x21, 0x34, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0x14, 0x65, 0xEA, 0x4A, 0x85, 0xC3, 0x2F), +}; +static const mbedtls_mpi_uint secp384r1_T_26_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0xD8, 0x40, 0x27, 0x73, 0x15, 0x7E, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0xBB, 0x53, 0x7E, 0x0F, 0x40, 0xC8, 0xD4), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0x37, 0x19, 0x73, 0xEF, 0x5A, 0x5E, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0x73, 0x2B, 0x49, 0x7E, 0xAC, 0x97, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0xB2, 0xC3, 0x1E, 0x0E, 0xE7, 0xD2, 0x21), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0x08, 0xD6, 0xDD, 0xAC, 0x21, 0xD6, 0x3E), +}; +static const mbedtls_mpi_uint secp384r1_T_26_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0x26, 0xBE, 0x6D, 0x6D, 0xF2, 0x38, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x6C, 0x31, 0xA7, 0x49, 0x50, 0x3A, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0x99, 0xC6, 0xF5, 0xD2, 0xC2, 0x30, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xE4, 0xF6, 0x8B, 0x8B, 0x97, 0xE9, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x21, 0xB7, 0x0D, 0xFC, 0x15, 0x54, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x83, 0x1C, 0xA4, 0xCD, 0x6B, 0x9D, 0xF2), +}; +static const mbedtls_mpi_uint secp384r1_T_27_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0xE8, 0x4C, 0x48, 0xE4, 0xAA, 0x69, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x7A, 0x27, 0xFC, 0x37, 0x96, 0x1A, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0xE7, 0x30, 0xA5, 0xCF, 0x13, 0x46, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0xD8, 0xAF, 0x74, 0x23, 0x4D, 0x56, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x3D, 0x44, 0x14, 0x1B, 0x97, 0x83, 0xF0), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x47, 0xD7, 0x5F, 0xFD, 0x98, 0x38, 0xF7), +}; +static const mbedtls_mpi_uint secp384r1_T_27_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0x73, 0x64, 0x36, 0xFD, 0x7B, 0xC1, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0x5D, 0x32, 0xD2, 0x47, 0x94, 0x89, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0xE9, 0x30, 0xAC, 0x06, 0xC8, 0x65, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x6C, 0xB9, 0x1B, 0xF7, 0x61, 0x49, 0x53), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0xFF, 0x32, 0x43, 0x80, 0xDA, 0xA6, 0xB1), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xF8, 0x04, 0x01, 0x95, 0x35, 0xCE, 0x21), +}; +static const mbedtls_mpi_uint secp384r1_T_28_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x06, 0x46, 0x0D, 0x51, 0xE2, 0xD8, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0x57, 0x1D, 0x6F, 0x79, 0xA0, 0xCD, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0xFB, 0x36, 0xCA, 0xAD, 0xF5, 0x9E, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x7A, 0x1D, 0x9E, 0x1D, 0x95, 0x48, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0x26, 0xA5, 0xB7, 0x15, 0x2C, 0xC2, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x42, 0x72, 0xAA, 0x11, 0xDC, 0xC9, 0xB6), +}; +static const mbedtls_mpi_uint secp384r1_T_28_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x6C, 0x64, 0xA7, 0x62, 0x3C, 0xAB, 0xD4), + MBEDTLS_BYTES_TO_T_UINT_8(0x48, 0x6A, 0x44, 0xD8, 0x60, 0xC0, 0xA8, 0x80), + MBEDTLS_BYTES_TO_T_UINT_8(0x82, 0x76, 0x58, 0x12, 0x57, 0x3C, 0x89, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0x82, 0x4F, 0x83, 0xCE, 0xCB, 0xB8, 0xD0, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0x84, 0x04, 0xB0, 0xAD, 0xEB, 0xFA, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xA4, 0xC3, 0x41, 0x44, 0x4E, 0x65, 0x3E), +}; +static const mbedtls_mpi_uint secp384r1_T_29_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x16, 0xA9, 0x1C, 0xE7, 0x65, 0x20, 0xC1), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x53, 0x32, 0xF8, 0xC0, 0xA6, 0xBD, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xF0, 0xE6, 0x57, 0x31, 0xCC, 0x26, 0x6F), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0xE3, 0x54, 0x1C, 0x34, 0xD3, 0x17, 0xBC), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xAE, 0xED, 0xFB, 0xCD, 0xE7, 0x1E, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x16, 0x1C, 0x34, 0x40, 0x00, 0x1F, 0xB6), +}; +static const mbedtls_mpi_uint secp384r1_T_29_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x32, 0x00, 0xC2, 0xD4, 0x3B, 0x1A, 0x09), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xE0, 0x99, 0x8F, 0x0C, 0x4A, 0x16, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x73, 0x18, 0x1B, 0xD4, 0x94, 0x29, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0xA4, 0x2D, 0xB1, 0x9D, 0x74, 0x32, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0xF4, 0xB1, 0x0C, 0x37, 0x62, 0x8B, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0xFF, 0xDA, 0xE2, 0x35, 0xA3, 0xB6, 0x42), +}; +static const mbedtls_mpi_uint secp384r1_T_30_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x49, 0x99, 0x65, 0xC5, 0xED, 0x16, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0x42, 0x9A, 0xF3, 0xA7, 0x4E, 0x6F, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0x0A, 0x7E, 0xC0, 0xD7, 0x4E, 0x07, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x7A, 0x31, 0x69, 0xA6, 0xB9, 0x15, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0xE0, 0x72, 0xA4, 0x3F, 0xB9, 0xF8, 0x0C), + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0x75, 0x32, 0x85, 0xA2, 0xDE, 0x37, 0x12), +}; +static const mbedtls_mpi_uint secp384r1_T_30_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xC0, 0x0D, 0xCF, 0x25, 0x41, 0xA4, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0xFC, 0xB2, 0x48, 0xC3, 0x85, 0x83, 0x4B), + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0xBE, 0x0B, 0x58, 0x2D, 0x7A, 0x9A, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0xF3, 0x81, 0x18, 0x1B, 0x74, 0x4F, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0x43, 0xA3, 0x0A, 0x16, 0x8B, 0xA3, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0x18, 0x81, 0x7B, 0x8D, 0xA2, 0x35, 0x77), +}; +static const mbedtls_mpi_uint secp384r1_T_31_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0xC4, 0x3F, 0x2C, 0xE7, 0x5F, 0x99, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x2B, 0xB7, 0xB6, 0xAD, 0x5A, 0x56, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x00, 0xA4, 0x48, 0xC8, 0xE8, 0xBA, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0xA1, 0xB5, 0x13, 0x5A, 0xCD, 0x99, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x95, 0xAD, 0xFC, 0xE2, 0x7E, 0xE7, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x6B, 0xD1, 0x34, 0x99, 0x53, 0x63, 0x0B), +}; +static const mbedtls_mpi_uint secp384r1_T_31_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x8A, 0x77, 0x5D, 0x2B, 0xAB, 0x01, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0x85, 0xD0, 0xD5, 0x49, 0x83, 0x4D, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0xC6, 0x91, 0x30, 0x3B, 0x00, 0xAF, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0xAE, 0x61, 0x07, 0xE1, 0xB6, 0xE2, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0x43, 0x41, 0xFE, 0x9B, 0xB6, 0xF0, 0xA5), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x97, 0xAE, 0xAD, 0x89, 0x88, 0x9E, 0x41), +}; +static const mbedtls_ecp_point secp384r1_T[32] = { + ECP_POINT_INIT_XY_Z1(secp384r1_T_0_X, secp384r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_1_X, secp384r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_2_X, secp384r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_3_X, secp384r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_4_X, secp384r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_5_X, secp384r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_6_X, secp384r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_7_X, secp384r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_8_X, secp384r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_9_X, secp384r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_10_X, secp384r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_11_X, secp384r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_12_X, secp384r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_13_X, secp384r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_14_X, secp384r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_15_X, secp384r1_T_15_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_16_X, secp384r1_T_16_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_17_X, secp384r1_T_17_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_18_X, secp384r1_T_18_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_19_X, secp384r1_T_19_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_20_X, secp384r1_T_20_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_21_X, secp384r1_T_21_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_22_X, secp384r1_T_22_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_23_X, secp384r1_T_23_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_24_X, secp384r1_T_24_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_25_X, secp384r1_T_25_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_26_X, secp384r1_T_26_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_27_X, secp384r1_T_27_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_28_X, secp384r1_T_28_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_29_X, secp384r1_T_29_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_30_X, secp384r1_T_30_Y), + ECP_POINT_INIT_XY_Z0(secp384r1_T_31_X, secp384r1_T_31_Y), +}; +#else +#define secp384r1_T NULL +#endif + +#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */ + +/* + * Domain parameters for secp521r1 + */ +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) +static const mbedtls_mpi_uint secp521r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_2(0xFF, 0x01), +}; +static const mbedtls_mpi_uint secp521r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x3F, 0x50, 0x6B, 0xD4, 0x1F, 0x45, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x34, 0x2C, 0x3D, 0x88, 0xDF, 0x73, 0x35), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xBF, 0xB1, 0x3B, 0xBD, 0xC0, 0x52, 0x16), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0x93, 0x7E, 0xEC, 0x51, 0x39, 0x19, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0x09, 0xF1, 0x8E, 0x91, 0x89, 0xB4, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0x15, 0xB3, 0x99, 0x5B, 0x72, 0xDA, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0x40, 0x85, 0xB6, 0xA0, 0x21, 0x9A, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x9A, 0x1C, 0x8E, 0x61, 0xB9, 0x3E, 0x95), + MBEDTLS_BYTES_TO_T_UINT_2(0x51, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0xBD, 0xE5, 0xC2, 0x31, 0x7E, 0x7E, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x42, 0x6A, 0x85, 0xC1, 0xB3, 0x48, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0xDE, 0xA8, 0xFF, 0xA2, 0x27, 0xC1, 0x1D, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x59, 0xE7, 0xEF, 0x77, 0x5E, 0x4B, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x3D, 0x4D, 0x6B, 0x60, 0xAF, 0x28, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0xB5, 0x3F, 0x05, 0x39, 0x81, 0x64, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0xB4, 0x95, 0x23, 0x66, 0xCB, 0x3E, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0xE9, 0x04, 0x04, 0xB7, 0x06, 0x8E, 0x85), + MBEDTLS_BYTES_TO_T_UINT_2(0xC6, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x66, 0xD1, 0x9F, 0x76, 0x94, 0xBE, 0x88), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0xC2, 0x72, 0xA2, 0x86, 0x70, 0x3C, 0x35), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x07, 0xAD, 0x3F, 0x01, 0xB9, 0x50, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x26, 0xF4, 0x5E, 0x99, 0x72, 0xEE, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x2C, 0x66, 0x3E, 0x27, 0x17, 0xBD, 0xAF, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x44, 0x9B, 0x57, 0x49, 0x44, 0xF5, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x1B, 0x7D, 0x2C, 0xB4, 0x5F, 0x8A, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0xC0, 0x3B, 0x9A, 0x78, 0x6A, 0x29, 0x39), + MBEDTLS_BYTES_TO_T_UINT_2(0x18, 0x01), +}; +static const mbedtls_mpi_uint secp521r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x64, 0x38, 0x91, 0x1E, 0xB7, 0x6F, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x47, 0x9C, 0x89, 0xB8, 0xC9, 0xB5, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0xA5, 0x09, 0xF7, 0x48, 0x01, 0xCC, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x96, 0x2F, 0xBF, 0x83, 0x87, 0x86, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_2(0xFF, 0x01), +}; +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp521r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0xBD, 0xE5, 0xC2, 0x31, 0x7E, 0x7E, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x42, 0x6A, 0x85, 0xC1, 0xB3, 0x48, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0xDE, 0xA8, 0xFF, 0xA2, 0x27, 0xC1, 0x1D, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x59, 0xE7, 0xEF, 0x77, 0x5E, 0x4B, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x3D, 0x4D, 0x6B, 0x60, 0xAF, 0x28, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0xB5, 0x3F, 0x05, 0x39, 0x81, 0x64, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0xB4, 0x95, 0x23, 0x66, 0xCB, 0x3E, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0xE9, 0x04, 0x04, 0xB7, 0x06, 0x8E, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x66, 0xD1, 0x9F, 0x76, 0x94, 0xBE, 0x88), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0xC2, 0x72, 0xA2, 0x86, 0x70, 0x3C, 0x35), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x07, 0xAD, 0x3F, 0x01, 0xB9, 0x50, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x26, 0xF4, 0x5E, 0x99, 0x72, 0xEE, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x2C, 0x66, 0x3E, 0x27, 0x17, 0xBD, 0xAF, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x44, 0x9B, 0x57, 0x49, 0x44, 0xF5, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x1B, 0x7D, 0x2C, 0xB4, 0x5F, 0x8A, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0xC0, 0x3B, 0x9A, 0x78, 0x6A, 0x29, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0xB1, 0x2D, 0xEB, 0x27, 0x2F, 0xE8, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x4B, 0x44, 0x25, 0xDB, 0x5C, 0x5F, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0x85, 0x28, 0x78, 0x2E, 0x75, 0x34, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0x57, 0x0F, 0x73, 0x78, 0x7A, 0xE3, 0x53), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xD8, 0xEC, 0xDC, 0xDA, 0x04, 0xAD, 0xAB), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x8A, 0x09, 0xF3, 0x58, 0x79, 0xD8, 0x29), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0x03, 0xCB, 0x50, 0x1A, 0x7F, 0x56, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0xA6, 0x78, 0x38, 0x85, 0x67, 0x0B, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0xD5, 0xD2, 0x22, 0xC4, 0x00, 0x3B, 0xBA), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0x93, 0x0E, 0x7B, 0x85, 0x51, 0xC3, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xA6, 0x5F, 0x54, 0x49, 0x02, 0x81, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0xE9, 0x6B, 0x3A, 0x92, 0xE7, 0x72, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x5F, 0x28, 0x9E, 0x91, 0x27, 0x88, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0x28, 0x31, 0xB3, 0x84, 0xCA, 0x12, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xF9, 0xAC, 0x22, 0x10, 0x0A, 0x64, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0xC6, 0x33, 0x1F, 0x69, 0x19, 0x18, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x48, 0xB8, 0xC7, 0x37, 0x5A, 0x00, 0x36), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xCC, 0x32, 0xE0, 0xEE, 0x03, 0xC2, 0xBA), + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0x29, 0xC2, 0xE4, 0x6E, 0x24, 0x20, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0x6B, 0x7F, 0x7B, 0xF9, 0xB0, 0xB8, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x7B, 0x3C, 0xE1, 0x19, 0xA1, 0x23, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xE3, 0xC2, 0x53, 0xC0, 0x07, 0x13, 0xA9), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xFE, 0x36, 0x35, 0x9F, 0x5E, 0x59, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x55, 0x89, 0x84, 0xBC, 0xEF, 0xA2, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0x1A, 0x08, 0x67, 0xB4, 0xE7, 0x22, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0x26, 0xDF, 0x81, 0x3C, 0x5F, 0x1C, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x4D, 0xD0, 0x0A, 0x48, 0x06, 0xF4, 0x48), + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0x18, 0x39, 0xF7, 0xD1, 0x20, 0x77, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0x78, 0x8F, 0x44, 0x13, 0xCB, 0x78, 0x11, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0xE2, 0x49, 0xEA, 0x43, 0x79, 0x08, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0xD1, 0xD8, 0x73, 0x2C, 0x71, 0x2F, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0xE5, 0xE7, 0xF4, 0x46, 0xAB, 0x20, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0x0B, 0xB9, 0x71, 0x1A, 0x27, 0xB7, 0xA7), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xA2, 0x2C, 0xD1, 0xDA, 0xBC, 0xC1, 0xBD), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0xA3, 0x10, 0x1F, 0x90, 0xF2, 0xA5, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0xFB, 0x20, 0xF4, 0xC0, 0x70, 0xC0, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0x8F, 0xA7, 0x99, 0xF0, 0xA5, 0xD3, 0x09, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0xE8, 0x14, 0x39, 0xBE, 0xCB, 0x60, 0xAF), + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0xD6, 0x14, 0xA9, 0xC9, 0x20, 0xC3, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0xA8, 0x5B, 0xFD, 0x2D, 0x96, 0xBC, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x04, 0x45, 0xBE, 0xCE, 0x75, 0x95, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0xDA, 0x58, 0x49, 0x35, 0x09, 0x8D, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0xF0, 0xC0, 0x36, 0xF2, 0xA6, 0x2D, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0xFC, 0x3D, 0xA8, 0xFB, 0x3C, 0xD2, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x4D, 0x71, 0x09, 0x18, 0x42, 0xF0, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xC1, 0xCE, 0x9E, 0x6A, 0x49, 0x60, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0xB1, 0x00, 0xF7, 0xA1, 0x7A, 0x31, 0xB4), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0xC3, 0x86, 0xCD, 0x20, 0x4A, 0x17, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0xAB, 0x8B, 0x47, 0x8D, 0xAA, 0xA6, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0x97, 0xF0, 0xBC, 0x2D, 0xDC, 0x9D, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x86, 0xB0, 0x74, 0xB2, 0xF4, 0xF6, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xBD, 0xAC, 0xE3, 0x8F, 0x43, 0x5C, 0xB1), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0xC3, 0xE2, 0x6E, 0x25, 0x49, 0xCD, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x5E, 0x08, 0xB3, 0xB9, 0xAC, 0x5F, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0xB7, 0xD1, 0xF4, 0xDC, 0x19, 0xE9, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0xE4, 0xFA, 0xE1, 0x36, 0x3E, 0xED, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x67, 0x92, 0x84, 0x6E, 0x48, 0x03, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0x95, 0xEF, 0x8F, 0xB2, 0x82, 0x6B, 0x1C), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xFA, 0xB9, 0x55, 0x23, 0xFE, 0x09, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0x79, 0x85, 0x4B, 0x0E, 0xD4, 0x35, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0x27, 0x45, 0x81, 0xE0, 0x88, 0x52, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0x63, 0xA2, 0x4B, 0xBC, 0x5D, 0xB1, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x8C, 0x83, 0xD9, 0x3E, 0xD3, 0x42, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x03, 0x3A, 0x31, 0xBA, 0xE9, 0x3A, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0x10, 0xCD, 0x2D, 0x00, 0xFE, 0x32, 0xA7), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x6E, 0x1F, 0xDA, 0xF8, 0x6F, 0x4D, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x79, 0x7D, 0x09, 0xE5, 0xD3, 0x03, 0x21), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0xC3, 0xBE, 0xDF, 0x07, 0x65, 0x49, 0xCC), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x57, 0x33, 0xEF, 0xAE, 0x4F, 0x04, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0xE9, 0x9B, 0xFE, 0xBF, 0xE6, 0x85, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0xBA, 0xAA, 0x06, 0xC4, 0xC6, 0xB8, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0x83, 0x01, 0xA9, 0xF6, 0x51, 0xE7, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0xA6, 0x15, 0x8E, 0xAB, 0x1F, 0x10, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x08, 0x27, 0x1A, 0xA1, 0x21, 0xAD, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0x09, 0x90, 0x6E, 0x50, 0x90, 0x9A, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x9A, 0xFE, 0xD7, 0xA1, 0xF5, 0xA2, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x7D, 0xE3, 0xDC, 0x21, 0xFB, 0xA4, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0xBF, 0x07, 0xFF, 0x45, 0xDF, 0x51, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x5C, 0x34, 0x02, 0x62, 0x9B, 0x08, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0xCE, 0x9A, 0x6A, 0xEC, 0x75, 0xF6, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x59, 0xF4, 0x78, 0x3C, 0x60, 0xB1, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0x37, 0x84, 0x6A, 0xDC, 0xF2, 0x9A, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x9A, 0x9A, 0x15, 0x36, 0xE0, 0x2B, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0x38, 0x9C, 0x50, 0x3D, 0x1E, 0x37, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x79, 0xF0, 0x92, 0xF2, 0x8B, 0x18, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xE0, 0x82, 0x1E, 0x80, 0x82, 0x4B, 0xD7), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0xBB, 0x59, 0x6B, 0x8A, 0x77, 0x41, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0xF9, 0xD4, 0xB8, 0x4A, 0x82, 0xCF, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x8C, 0xC8, 0x9B, 0x72, 0x9E, 0xF7, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0xCE, 0xE9, 0x77, 0x0A, 0x19, 0x59, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0xA1, 0x41, 0x6A, 0x72, 0x4B, 0xB4, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x35, 0x43, 0xE2, 0x8C, 0xBE, 0x0D, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0xEB, 0xAD, 0xF3, 0xA9, 0xA6, 0x68, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0x2F, 0xE2, 0x48, 0x0C, 0xDB, 0x1F, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0x1E, 0x60, 0x9B, 0x2A, 0xD2, 0xC1, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0x64, 0xB5, 0xD2, 0xF6, 0xF6, 0x6E, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0x3D, 0x30, 0x78, 0x10, 0x18, 0x41, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x1D, 0x1C, 0xE0, 0x6D, 0x83, 0xD1, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0x03, 0x0B, 0xF5, 0x2F, 0x6C, 0x04, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x3E, 0xD5, 0xFC, 0x31, 0x5B, 0x3A, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x82, 0x2F, 0xFB, 0xFE, 0xF8, 0x76, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0x85, 0x26, 0xDA, 0x9C, 0x36, 0xF5, 0x93, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0x4C, 0xE7, 0x6E, 0xD2, 0x7D, 0x81, 0x09, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x03, 0xF9, 0x58, 0x48, 0x24, 0xA2, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x79, 0x0C, 0x8E, 0x6B, 0x95, 0xF3, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0x10, 0x5C, 0x87, 0x03, 0x39, 0xCF, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0xF0, 0xF7, 0xC1, 0x07, 0xA4, 0xF4, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0xE8, 0x02, 0x89, 0x65, 0xC4, 0x72, 0x36), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x88, 0xEA, 0x96, 0x67, 0x0B, 0x5D, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x75, 0x60, 0xA8, 0xBD, 0x74, 0xDF, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x6E, 0xE5, 0x71, 0x50, 0x67, 0xD0, 0xD2, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0xFC, 0xE5, 0xC7, 0x77, 0xB0, 0x7F, 0x8C), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x86, 0x69, 0xCD, 0x0D, 0x9A, 0xBD, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x17, 0xBC, 0xBB, 0x59, 0x85, 0x7D, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xA8, 0x76, 0xAC, 0x80, 0xA9, 0x72, 0xE0), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x78, 0xC1, 0xE2, 0x4D, 0xAF, 0xF9, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x97, 0x8E, 0x74, 0xC4, 0x4B, 0xB2, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xD8, 0xF6, 0xF3, 0xAF, 0x2F, 0x52, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0x57, 0xF4, 0xCE, 0xEE, 0x43, 0xED, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x46, 0x38, 0xDE, 0x20, 0xFD, 0x59, 0x18), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x18, 0xE8, 0x58, 0xB9, 0x76, 0x2C, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0x54, 0xE4, 0xFE, 0xC7, 0xBC, 0x31, 0x37), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xF8, 0x89, 0xEE, 0x70, 0xB5, 0xB0, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x22, 0x26, 0x9A, 0x53, 0xB9, 0x38, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0xA7, 0x19, 0x8C, 0x74, 0x7E, 0x88, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0xDA, 0x0A, 0xE8, 0xDA, 0xA5, 0xBE, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0x5C, 0xF7, 0xB1, 0x0C, 0x72, 0xFB, 0x09), + MBEDTLS_BYTES_TO_T_UINT_8(0x78, 0xE2, 0x23, 0xE7, 0x46, 0xB7, 0xE0, 0x91), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x36, 0xBC, 0xBD, 0x48, 0x11, 0x8E, 0x72), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0xBB, 0xA1, 0xF7, 0x0B, 0x9E, 0xBF, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x28, 0xE1, 0xA2, 0x8F, 0xFC, 0xFC, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0xFE, 0x19, 0x0A, 0xE5, 0xE7, 0x69, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0xCD, 0x12, 0xF5, 0xBE, 0xD3, 0x04, 0xF1), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xA8, 0x0D, 0x81, 0x59, 0xC4, 0x79, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0xF3, 0x4B, 0x92, 0x65, 0xC3, 0x31, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0xB5, 0x4F, 0x4D, 0x91, 0xD4, 0xE2, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x09, 0x41, 0x79, 0x1D, 0x4D, 0x0D, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x31, 0x18, 0xBA, 0xA0, 0xF2, 0x6E, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x5B, 0x4D, 0x4F, 0xAF, 0xC9, 0x8C, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x48, 0x99, 0x9C, 0x06, 0x68, 0xDE, 0xD8, 0x29), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x04, 0xE1, 0xB5, 0x9D, 0x00, 0xBC, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x95, 0x92, 0x8D, 0x72, 0xD3, 0x37, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0x4B, 0x27, 0xA2, 0xE8, 0xA4, 0x26, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0x45, 0x9C, 0xA9, 0xCB, 0x9F, 0xBA, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x7E, 0x1B, 0x64, 0xF4, 0xE8, 0xA5, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0x20, 0xA9, 0xCA, 0xF3, 0x89, 0xE5, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0xED, 0xFC, 0xAB, 0xD9, 0x0A, 0xB9, 0x07), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x6F, 0x46, 0x7C, 0xCD, 0x78, 0xFF, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0xAB, 0x71, 0x5A, 0x94, 0xAB, 0x20, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x2E, 0xEE, 0x87, 0x57, 0x1F, 0xAD, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x4C, 0x3D, 0xFB, 0x7E, 0xA1, 0x8B, 0x07), + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0xCF, 0x07, 0x86, 0xBA, 0x53, 0x37, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x26, 0xB2, 0xB9, 0xE2, 0x91, 0xE3, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0xC9, 0x54, 0x84, 0x08, 0x3D, 0x0B, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0xA8, 0x77, 0x2F, 0x64, 0x45, 0x99, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x96, 0x16, 0x1F, 0xDB, 0x96, 0x28, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x2B, 0x8D, 0xFF, 0xA2, 0x4F, 0x55, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0xE6, 0x48, 0xBD, 0x99, 0x3D, 0x12, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x84, 0x59, 0xDA, 0xB9, 0xB6, 0x66, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x78, 0x41, 0x92, 0xDF, 0xF4, 0x3F, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x86, 0x6F, 0x4F, 0xBF, 0x67, 0xDF, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0x2B, 0x1E, 0x5F, 0x00, 0xEA, 0xF6, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0xB9, 0x6A, 0x89, 0xD8, 0xC0, 0xD7, 0xA7), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x9A, 0x32, 0x23, 0xA0, 0x02, 0x91, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0x7F, 0x6A, 0x15, 0x64, 0x6A, 0x8B, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0x57, 0x82, 0x58, 0xA9, 0x56, 0xB5, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x50, 0x92, 0x60, 0xCC, 0x81, 0x24, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0x3D, 0xAD, 0xDA, 0xD9, 0x51, 0x3E, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0xFE, 0x8F, 0xB0, 0x0B, 0xDE, 0x2E, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0xD2, 0xBE, 0xEF, 0xAC, 0x76, 0x71, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0xE8, 0x72, 0x0B, 0xAC, 0xFE, 0xCA, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x5B, 0xC7, 0xFC, 0xE3, 0x3C, 0x7C, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x04, 0xA7, 0xB9, 0x9B, 0x93, 0xC0, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x48, 0x4B, 0x8E, 0x32, 0xC5, 0xF0, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x42, 0x07, 0xC1, 0xF2, 0xF1, 0x72, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x37, 0x54, 0x9C, 0x88, 0xD2, 0x62, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x19, 0x8A, 0x89, 0x58, 0xA2, 0x0F, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0xCC, 0x4C, 0x97, 0x30, 0x66, 0x34, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x6A, 0x1E, 0x1F, 0xDB, 0xC9, 0x5E, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x4D, 0x49, 0xFF, 0x9B, 0x9C, 0xAC, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0xE4, 0x4B, 0xF2, 0xD4, 0x1A, 0xD2, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0xDA, 0xE8, 0x61, 0x9F, 0xC8, 0x49, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0xCB, 0xF2, 0x2D, 0x85, 0xF6, 0x8D, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xC5, 0xCD, 0x2C, 0x79, 0xC6, 0x0E, 0x4F), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0x1D, 0x55, 0x0F, 0xF8, 0x22, 0x9F, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0x56, 0xBA, 0xE7, 0x57, 0x32, 0xEC, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x9A, 0xC6, 0x4C, 0x09, 0xC4, 0x52, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x1E, 0x6F, 0xF4, 0x7D, 0x27, 0xDD, 0xAF), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x11, 0x16, 0xEC, 0x79, 0x83, 0xAD, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0x4E, 0x92, 0x1F, 0x19, 0x7D, 0x65, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0xFF, 0x78, 0x15, 0x45, 0x63, 0x32, 0xE4), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0x91, 0xD0, 0x78, 0x58, 0xDA, 0x50, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x23, 0xDE, 0x40, 0xF6, 0x41, 0xB4, 0x3B, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0x8D, 0xE0, 0xE1, 0xA9, 0xF0, 0x35, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0xD4, 0xBA, 0x7B, 0xCC, 0x1B, 0x3A, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x5A, 0x2E, 0x74, 0x47, 0x14, 0xC3, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0xF0, 0x8B, 0x06, 0x15, 0x8E, 0x0E, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0xD2, 0xEB, 0x97, 0x50, 0x7D, 0x31, 0xFC), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0x93, 0x4C, 0xDB, 0x97, 0x79, 0x44, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0xA2, 0xA0, 0x0B, 0xC8, 0x3A, 0x8A, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0x50, 0x92, 0x9E, 0x24, 0x1F, 0xCB, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x16, 0xC9, 0xC5, 0x3D, 0x5A, 0xAF, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0xE3, 0x97, 0xE4, 0xA8, 0x50, 0xF6, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0x57, 0x97, 0x42, 0x78, 0x92, 0x49, 0x0D), + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0xEB, 0x62, 0x24, 0xFB, 0x8F, 0x32, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0x0C, 0x36, 0x6E, 0x8F, 0xE8, 0xE8, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0xD3, 0x7C, 0xC7, 0x8D, 0x3F, 0x5C, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x64, 0x6A, 0x73, 0x10, 0x79, 0xB8, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0xF9, 0xEF, 0xA5, 0x20, 0x4A, 0x5C, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0xF3, 0xF4, 0x49, 0x5B, 0x73, 0xAA, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0xF2, 0xEA, 0x0F, 0x00, 0xAD, 0x53, 0xAB), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0xB8, 0x66, 0xED, 0xC4, 0x2B, 0x4C, 0x35), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x2F, 0xC1, 0x9A, 0x37, 0xD2, 0x7F, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0xA7, 0x81, 0x38, 0x64, 0xC9, 0x37, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0x3B, 0x6C, 0x9F, 0x5B, 0xD9, 0x8B, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x14, 0xD9, 0x08, 0xD8, 0xD2, 0x7E, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x71, 0xE6, 0x3D, 0xD1, 0xB0, 0xE7, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0x81, 0x23, 0xEC, 0x2D, 0x42, 0x45, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0x5B, 0x44, 0x6B, 0x89, 0x03, 0x67, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0x27, 0xAE, 0x80, 0x5A, 0x33, 0xBE, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0xB6, 0x64, 0x1A, 0xDF, 0xD3, 0x85, 0x91), + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0x8C, 0x22, 0xBA, 0xD0, 0xBD, 0xCC, 0xA0), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0x3C, 0x01, 0x3A, 0xFF, 0x9D, 0xC7, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0xC7, 0x64, 0xB4, 0x59, 0x4E, 0x9F, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x85, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x34, 0x0A, 0x41, 0x94, 0xA8, 0xF2, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0xD4, 0xE4, 0xF0, 0x97, 0x45, 0x6D, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x8F, 0x1F, 0x4D, 0x6D, 0xFE, 0xA0, 0xC4, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x28, 0x5C, 0x40, 0xBB, 0x65, 0xD4, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0xA8, 0x87, 0x35, 0x20, 0x3A, 0x89, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0xFD, 0x4F, 0xAB, 0x2D, 0xD1, 0xD0, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0xE8, 0x00, 0xFC, 0x69, 0x52, 0xF8, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0x9A, 0x99, 0xE1, 0xDC, 0x9C, 0x3F, 0xD9), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0x08, 0x98, 0xD9, 0xCA, 0x73, 0xD5, 0xA9), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0x2C, 0xE0, 0xA7, 0x3E, 0x91, 0xD7, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x04, 0xB0, 0x54, 0x09, 0xF4, 0x72, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0xEE, 0x28, 0xCC, 0xE8, 0x50, 0x78, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0x91, 0x03, 0x76, 0xDB, 0x68, 0x24, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0x7A, 0xE0, 0x56, 0xB2, 0x5D, 0x12, 0xD3, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0x42, 0x59, 0x8B, 0xDF, 0x67, 0xB5, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0xCC, 0xE5, 0x31, 0x53, 0x7A, 0x46, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_16_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x8D, 0x59, 0xB5, 0x1B, 0x0F, 0xF4, 0xAF), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x2F, 0xD1, 0x2C, 0xE0, 0xD8, 0x04, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xF4, 0xD7, 0xBA, 0xB0, 0xA3, 0x7E, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0x08, 0x51, 0x56, 0xA6, 0x76, 0x67, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0x17, 0x63, 0xFE, 0x56, 0xD0, 0xD9, 0x71), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0xF6, 0xC3, 0x14, 0x47, 0xC5, 0xA7, 0x31), + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x4C, 0x80, 0xF6, 0xA2, 0x57, 0xA7, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0xB3, 0x7B, 0xF8, 0x2F, 0xE1, 0x3E, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_16_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0xF4, 0xF9, 0x6B, 0x7B, 0x90, 0xDF, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x82, 0xEF, 0x62, 0xA1, 0x4C, 0x53, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x99, 0x76, 0x01, 0xBA, 0x8D, 0x0F, 0x54), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xF4, 0x58, 0x73, 0x56, 0xFE, 0xDD, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0xCE, 0xF9, 0xE8, 0xA1, 0x34, 0xC3, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x5F, 0xDC, 0x6A, 0x3D, 0xD8, 0x7F, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0xF4, 0x51, 0xB8, 0xB8, 0xC1, 0xD7, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x7D, 0x58, 0xD1, 0xD4, 0x1B, 0x4D, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_17_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0x95, 0xDF, 0x00, 0xD8, 0x21, 0xDE, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0x47, 0x3C, 0xC3, 0xB2, 0x01, 0x53, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x17, 0x43, 0x23, 0xBD, 0xCA, 0x71, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0xBA, 0x0F, 0x4F, 0xDC, 0x41, 0x54, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x39, 0x26, 0x70, 0x53, 0x32, 0x18, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x46, 0x07, 0x97, 0x3A, 0x57, 0xE0, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x92, 0x4F, 0xCE, 0xDF, 0x25, 0x80, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0x6F, 0x9A, 0x03, 0x05, 0x4B, 0xD1, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_17_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0x01, 0x72, 0x30, 0x90, 0x17, 0x51, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0xFB, 0x41, 0x65, 0x5C, 0xB4, 0x2D, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0xCD, 0xCD, 0xAA, 0x41, 0xCC, 0xBB, 0x07), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0xCE, 0x08, 0x0A, 0x63, 0xE9, 0xA2, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xA8, 0x21, 0x7F, 0x7A, 0x5B, 0x9B, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0x6B, 0x89, 0x44, 0x0A, 0x7F, 0x85, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0xDE, 0x7C, 0x19, 0x5C, 0x65, 0x26, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0xAC, 0x62, 0x29, 0x4A, 0xF1, 0xD0, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_18_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x00, 0x40, 0x87, 0xEB, 0xA9, 0x58, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0x51, 0x0B, 0xFF, 0x56, 0x35, 0x51, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0xAC, 0x08, 0x94, 0x71, 0xDA, 0xEC, 0x99), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x4D, 0xC5, 0x7B, 0x31, 0x8B, 0x8D, 0x5E), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x05, 0xF1, 0x3E, 0x9E, 0x8F, 0x17, 0x8F), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x9C, 0x4B, 0x62, 0x94, 0xAD, 0x49, 0xFC), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0xC9, 0xC6, 0x8F, 0xFD, 0x33, 0x44, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x96, 0x17, 0x7F, 0x42, 0xBE, 0xF7, 0x0D), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_18_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0x29, 0x39, 0x13, 0x08, 0x8D, 0x91, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0x79, 0xF9, 0x2F, 0xA9, 0x0A, 0xCF, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0x87, 0x7A, 0xA3, 0x19, 0xAB, 0x55, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x0B, 0x01, 0xC5, 0x56, 0x19, 0x9D, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0xDE, 0x82, 0x3B, 0xEA, 0xD3, 0x0B, 0x8C), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x6B, 0xC7, 0xF3, 0x0F, 0x82, 0x87, 0x6C), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0x2E, 0x23, 0xF2, 0x39, 0x9D, 0x49, 0x70), + MBEDTLS_BYTES_TO_T_UINT_8(0x31, 0xDE, 0xAF, 0x7A, 0xEE, 0xB0, 0xDA, 0x70), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_19_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0x4E, 0x2A, 0x50, 0xFD, 0x8E, 0xC0, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0x0F, 0x7C, 0x76, 0x63, 0xD8, 0x89, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0x2D, 0xB9, 0x4E, 0xF4, 0xEE, 0x85, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x95, 0x5C, 0x96, 0x5D, 0xAA, 0x59, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0xDB, 0xD2, 0x68, 0x8E, 0x5A, 0x94, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x02, 0xBF, 0x77, 0x9F, 0xB9, 0x4C, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0xDC, 0xC0, 0xCF, 0x81, 0x1E, 0xC4, 0x6C), + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0xCC, 0x37, 0x86, 0xDC, 0xE2, 0x64, 0x72), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_19_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2C, 0x30, 0xB1, 0x59, 0x20, 0x9D, 0x98, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x0C, 0x9D, 0xF8, 0x20, 0xDC, 0x90, 0xBA), + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0xA0, 0xF4, 0xE7, 0x3E, 0x9C, 0x9E, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0x25, 0xA2, 0xB0, 0x54, 0xCD, 0x2E, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xD9, 0x42, 0xB0, 0x80, 0xB0, 0xA3, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0xFE, 0x9D, 0x8D, 0x40, 0xFF, 0x27, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x9D, 0xA6, 0x88, 0x3A, 0x8B, 0x6F, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x39, 0xEE, 0x1F, 0x3F, 0xB1, 0x4F, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_20_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xD7, 0x9E, 0xFF, 0xD2, 0x35, 0x67, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x4F, 0x15, 0x5D, 0xE3, 0xE8, 0x53, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0xF7, 0x24, 0x98, 0xA2, 0xCB, 0x11, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0x2E, 0x25, 0xE1, 0x94, 0xC5, 0xA3, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x82, 0x6E, 0xBA, 0xE7, 0x43, 0x25, 0xB0), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0x65, 0xB4, 0x49, 0x73, 0x18, 0x35, 0x54), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0x5B, 0xBC, 0x62, 0x86, 0x4C, 0xC1, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0xF2, 0x95, 0xA2, 0xBB, 0xA2, 0x35, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_20_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x59, 0x62, 0xB0, 0x4B, 0x1E, 0xB4, 0xD8), + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0x55, 0xCE, 0xB0, 0x69, 0xBA, 0x63, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x6E, 0x69, 0x86, 0xDB, 0x34, 0x7D, 0x68, 0x64), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x06, 0xCA, 0x55, 0x44, 0x36, 0x2B, 0xBA), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0xD4, 0xC4, 0x3D, 0xCD, 0x9E, 0x69, 0xA4), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x44, 0xE4, 0xBF, 0x31, 0xE6, 0x40, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x4F, 0xFA, 0x75, 0xE3, 0xFB, 0x97, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0xC0, 0xBD, 0x1C, 0x48, 0xB0, 0x26, 0xD0), + MBEDTLS_BYTES_TO_T_UINT_8(0xD2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_21_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x7B, 0x32, 0xFA, 0xF2, 0x6D, 0x84, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x21, 0x03, 0x1D, 0x0D, 0x22, 0x55, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0xF9, 0x42, 0x03, 0x9C, 0xC2, 0xCB, 0xBA), + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0xA1, 0x96, 0xD9, 0x9D, 0x11, 0x6F, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x40, 0x57, 0xEB, 0x40, 0x2D, 0xC0, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0x96, 0xBB, 0x4F, 0x2F, 0x23, 0xA8, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x29, 0x85, 0x21, 0xA5, 0x50, 0x62, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x7D, 0x92, 0xCF, 0x87, 0x0C, 0x22, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_21_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x0E, 0xA5, 0x32, 0x5B, 0xDF, 0x9C, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x96, 0x37, 0x2C, 0x88, 0x35, 0x30, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0xB4, 0x69, 0xFF, 0xEB, 0xC6, 0x94, 0x08), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x55, 0x60, 0xAD, 0xAA, 0x58, 0x14, 0x88), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0xFF, 0xF2, 0xB2, 0xD5, 0xA7, 0xD9, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0xAE, 0x54, 0xD2, 0x60, 0x31, 0xF3, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x92, 0x83, 0xE3, 0xF1, 0x42, 0x83, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0xD2, 0xC8, 0xB7, 0x76, 0x45, 0x7F, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_22_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0x11, 0xA4, 0xFB, 0x7A, 0x01, 0xBC, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0x27, 0x73, 0x8D, 0x02, 0x91, 0x27, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x62, 0xF6, 0xDD, 0x6B, 0xFA, 0x5B, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0xCA, 0xA2, 0x44, 0x2C, 0xF0, 0x28, 0xD8), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0xF1, 0x7A, 0xA2, 0x42, 0x4C, 0x50, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0x83, 0x3E, 0x50, 0xAB, 0x9C, 0xF7, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0xED, 0x78, 0xCB, 0x76, 0x69, 0xDA, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x01, 0x1E, 0x43, 0x27, 0x47, 0x6E, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_22_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x4F, 0x54, 0xB9, 0x3E, 0xBD, 0xD5, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x35, 0x40, 0x69, 0x7F, 0x74, 0x9D, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x06, 0x6F, 0x67, 0x68, 0x2B, 0x4D, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0x65, 0x41, 0xFC, 0x7C, 0x1E, 0xE8, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0x79, 0x37, 0xAF, 0xFD, 0xD2, 0xDA, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0xA8, 0x69, 0x56, 0x62, 0xA4, 0xE4, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0x71, 0x73, 0x21, 0x8A, 0x17, 0x81, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0x55, 0x8F, 0x7B, 0xB8, 0xAF, 0xF7, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_23_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0xD1, 0xBD, 0xBE, 0x8C, 0xBC, 0x60, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0xA6, 0x57, 0x8C, 0xAE, 0x5C, 0x19, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x7A, 0x43, 0xE4, 0xD9, 0xD8, 0x7B, 0xE7, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0xB9, 0xE4, 0x85, 0x7C, 0x2E, 0xFC, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0x2E, 0x01, 0x2A, 0x6D, 0x56, 0xBE, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x0C, 0x25, 0x9B, 0xAE, 0x86, 0x37, 0x43), + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0x22, 0xB3, 0xCB, 0x99, 0x66, 0xB7, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0x56, 0xF7, 0x90, 0xF0, 0x1B, 0x09, 0x27, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_23_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x16, 0x08, 0xEF, 0x39, 0x64, 0x49, 0x31), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0xA0, 0xE3, 0x97, 0xA9, 0x07, 0x54, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0xFF, 0xE2, 0x00, 0x07, 0x21, 0x88, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0xFD, 0x59, 0x53, 0x05, 0x6C, 0x42, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0x8F, 0xF7, 0x39, 0x5C, 0x82, 0x36, 0xE8, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x2E, 0x83, 0xA8, 0xE2, 0xA8, 0x43, 0x07, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0xAF, 0x2B, 0x79, 0xED, 0xD8, 0x39, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x20, 0x91, 0x7A, 0xC4, 0x07, 0xEF, 0x6C), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_24_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x10, 0x2F, 0xAA, 0x0C, 0x94, 0x0E, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x81, 0x87, 0x41, 0x23, 0xEB, 0x55, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0x53, 0xCC, 0x79, 0xB6, 0xEB, 0x6C, 0xCC), + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0x77, 0x73, 0x9D, 0xFC, 0x64, 0x6F, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0x40, 0xE3, 0x6D, 0x1C, 0x16, 0x71, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0xF4, 0x1B, 0xFF, 0x1C, 0x2F, 0xA5, 0xD7), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0x0E, 0x0B, 0x11, 0xF4, 0x8D, 0x93, 0xAF), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0xC5, 0x64, 0x6F, 0x24, 0x19, 0xF2, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_24_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0xB3, 0xAF, 0xA5, 0x0E, 0x4F, 0x5E, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0x77, 0xCA, 0xF2, 0x6D, 0xC5, 0xF6, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0x18, 0x8E, 0x33, 0x68, 0x6C, 0xE8, 0xE0), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x8B, 0x80, 0x90, 0x19, 0x7F, 0x90, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0x80, 0x6B, 0x68, 0xE2, 0x7D, 0xD4, 0xD0), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xC1, 0x67, 0xB3, 0x72, 0xCB, 0xBF, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0xD5, 0xD3, 0x1D, 0x14, 0x58, 0x0A, 0x80), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0x7A, 0x65, 0x98, 0xB3, 0x07, 0x4B, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_25_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0x87, 0x0F, 0x5F, 0xCF, 0xA2, 0x01, 0x08), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0xC9, 0xC8, 0x6E, 0x35, 0x87, 0xA5, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x3E, 0x91, 0xA0, 0xAB, 0x24, 0x1E, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0xBC, 0x02, 0x35, 0x70, 0xC1, 0x5F, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x59, 0xA0, 0x50, 0x04, 0x80, 0x52, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x56, 0x6E, 0x42, 0x8F, 0x8C, 0x91, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xA2, 0xCB, 0xA5, 0xDE, 0x14, 0x24, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0xCB, 0x74, 0x28, 0xE6, 0xA7, 0xE7, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_25_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0x73, 0xA8, 0x8F, 0x9E, 0x0E, 0x63, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0x1B, 0x77, 0xC7, 0xC1, 0x38, 0xF9, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0x3C, 0xCF, 0xA8, 0x7A, 0xD7, 0xF3, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x5F, 0x9A, 0xC9, 0xAD, 0xE9, 0x1A, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0xCF, 0x2B, 0x5E, 0xD5, 0x81, 0x95, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x88, 0x75, 0x29, 0x1F, 0xC7, 0xC7, 0xD0), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0xA9, 0x5A, 0x4D, 0x63, 0x95, 0xF9, 0x4E), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0xCD, 0x04, 0x8F, 0xCD, 0x91, 0xDE, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_26_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0xD4, 0xFD, 0x25, 0x11, 0x99, 0x6E, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x83, 0x01, 0x3D, 0xFB, 0x56, 0xA5, 0x4E), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x3A, 0xDC, 0x74, 0xC2, 0xD7, 0xCF, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0x8F, 0xBD, 0xF1, 0xDD, 0xA3, 0x07, 0x03, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0xBE, 0xE9, 0x2E, 0x58, 0x84, 0x66, 0xFC), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x20, 0x78, 0x37, 0x79, 0x0B, 0xA6, 0x64), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0xF2, 0xAC, 0x65, 0xC8, 0xC9, 0x2F, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x93, 0xE5, 0x0D, 0x0C, 0xC6, 0xB8, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_26_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0xAD, 0x5C, 0x19, 0x12, 0x61, 0x0E, 0x25), + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0x4F, 0x0B, 0x1F, 0x49, 0x7E, 0xCD, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0x2E, 0x30, 0x61, 0xDB, 0x08, 0x68, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x78, 0xAF, 0xB3, 0x08, 0xC1, 0x69, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0x5F, 0x5D, 0xC1, 0x57, 0x6F, 0xD8, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0xD3, 0x6A, 0xF7, 0xFD, 0x86, 0xE5, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x63, 0xBD, 0x70, 0x7B, 0x47, 0xE8, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0x62, 0xC8, 0x7E, 0x9D, 0x11, 0x2B, 0xA5), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_27_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0x84, 0xFD, 0xD5, 0x9A, 0x56, 0x7F, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0xBB, 0xA4, 0x6F, 0x12, 0x6E, 0x4D, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x08, 0xA1, 0x82, 0x9C, 0x62, 0x74, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0x58, 0x22, 0x05, 0x1D, 0x15, 0x35, 0x79), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0x88, 0xCF, 0x5C, 0x05, 0x78, 0xFB, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x6B, 0x2F, 0x79, 0x09, 0x73, 0x67, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0xA0, 0x80, 0xD8, 0xE8, 0xEC, 0xFB, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xE7, 0x0B, 0xB7, 0x81, 0x48, 0x7B, 0xD9), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_27_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x53, 0xA9, 0xED, 0x61, 0x92, 0xD7, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x49, 0xD9, 0x5D, 0x9B, 0x4E, 0x89, 0x35), + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0x12, 0xEB, 0x9A, 0xC9, 0xCB, 0xC1, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0xDC, 0x95, 0x16, 0xFE, 0x29, 0x70, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x33, 0xB1, 0xD6, 0x78, 0xB9, 0xE2, 0x36), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xCE, 0x88, 0xC3, 0xFD, 0x7A, 0x6B, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x1E, 0x50, 0x1E, 0xAF, 0xB1, 0x25, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0xE7, 0xD7, 0xD5, 0xBD, 0x7A, 0x12, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_28_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0xAA, 0xA2, 0x80, 0x5D, 0x8F, 0xCD, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0x48, 0x39, 0x79, 0x64, 0xA1, 0x67, 0x3C, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xC7, 0x49, 0xFF, 0x7F, 0xAC, 0xAB, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0x54, 0x3E, 0x83, 0xF0, 0x3D, 0xBC, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x92, 0x4A, 0x38, 0x42, 0x8A, 0xAB, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0x0B, 0x4F, 0xEE, 0x9E, 0x92, 0xA5, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0xDD, 0x19, 0x96, 0xF2, 0xF0, 0x6B, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0xFC, 0xDD, 0xB2, 0x8A, 0xE5, 0x4C, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_28_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x06, 0x49, 0xAC, 0x99, 0x7E, 0xF8, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0xC8, 0x01, 0x51, 0xEA, 0xF6, 0x52, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0x89, 0x66, 0x2B, 0x1F, 0x9B, 0x2A, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0x0F, 0x95, 0x07, 0x2B, 0x6C, 0x6E, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0xC3, 0xB4, 0xBB, 0x91, 0x1F, 0xA3, 0x72), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x6E, 0x54, 0x28, 0x7B, 0x9C, 0x79, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0x45, 0xFF, 0xA6, 0xDA, 0xA2, 0x83, 0x71), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0xDE, 0x8F, 0x17, 0x37, 0x82, 0xCB, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_29_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0x94, 0x3F, 0x26, 0xC9, 0x1D, 0xD9, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x97, 0x28, 0x20, 0xCD, 0xC1, 0xF3, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0xC9, 0xB5, 0x60, 0x9B, 0x1E, 0xDC, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0xB9, 0x5B, 0x7D, 0xA0, 0xB2, 0x8C, 0xF0), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0xD1, 0x42, 0xE6, 0x39, 0x33, 0x6D, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0xC0, 0xFC, 0xD2, 0x14, 0x5D, 0x3E, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0x78, 0x4A, 0x3E, 0x40, 0x16, 0x93, 0x15, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x24, 0xC1, 0x27, 0x27, 0xE5, 0x4B, 0xD8), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_29_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x50, 0xD8, 0xBC, 0xC1, 0x46, 0x22, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x0E, 0x60, 0xA1, 0xB3, 0x50, 0xD4, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0xB1, 0x26, 0xB6, 0x6D, 0x47, 0x5A, 0x6F), + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0xAC, 0x11, 0x35, 0x3E, 0xB9, 0xF4, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x97, 0xFA, 0xBB, 0x6B, 0x39, 0x13, 0xD8), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x7B, 0x34, 0x12, 0x75, 0x8E, 0x9B, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0x2C, 0x9E, 0xCD, 0x29, 0xB6, 0xEF, 0x8D, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0xAC, 0xE9, 0x25, 0x27, 0xBB, 0x78, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_30_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x7A, 0xA8, 0xD3, 0xE3, 0x66, 0xE5, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0x4C, 0xC4, 0x2C, 0x76, 0x81, 0x50, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0x71, 0x08, 0xB8, 0x52, 0x7C, 0xAF, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0x59, 0x24, 0xDD, 0xFB, 0x2F, 0xD0, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xCD, 0x56, 0xE9, 0xAC, 0x91, 0xE6, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0x64, 0x20, 0xC6, 0x9F, 0xE4, 0xEF, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x2C, 0x8F, 0x8C, 0x97, 0xF6, 0x22, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xF4, 0x88, 0xAA, 0xA8, 0xD7, 0xA5, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_30_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x6C, 0xAE, 0x83, 0xB1, 0x55, 0x55, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x67, 0x84, 0x47, 0x7C, 0x83, 0x5C, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0x10, 0x4D, 0xDD, 0x30, 0x60, 0xB0, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xA7, 0x36, 0x76, 0x24, 0x32, 0x9F, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x42, 0x81, 0xFB, 0xA4, 0x2E, 0x13, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x94, 0x91, 0xFF, 0x99, 0xA0, 0x09, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x83, 0xA1, 0x76, 0xAF, 0x37, 0x5C, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xA8, 0x04, 0x86, 0xC4, 0xA9, 0x79, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_31_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x8C, 0xC2, 0x34, 0xFB, 0x83, 0x28, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x03, 0x7D, 0x5E, 0x9E, 0x0E, 0xB0, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0xA2, 0x02, 0x46, 0x7F, 0xB9, 0xAC, 0xBB, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0xED, 0x48, 0xC2, 0x96, 0x4D, 0x56, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0x44, 0xB5, 0xC5, 0xD1, 0xE6, 0x1C, 0x7E, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x2E, 0x18, 0x71, 0x2D, 0x7B, 0xD7, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0x46, 0x9D, 0xDE, 0xAA, 0x78, 0x8E, 0xB1), + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0xD7, 0x69, 0x2E, 0xE1, 0xD9, 0x48, 0xDE), + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp521r1_T_31_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xFF, 0x9E, 0x09, 0x22, 0x22, 0xE6, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x14, 0x28, 0x13, 0x1B, 0x62, 0x12, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x7F, 0x67, 0x03, 0xB0, 0xC0, 0xF3, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0xC3, 0x0F, 0xFB, 0x25, 0x48, 0x3E, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x6E, 0x53, 0x98, 0x36, 0xB3, 0xD3, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0x81, 0x54, 0x22, 0xA4, 0xCC, 0xC1, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xBA, 0xFC, 0xA9, 0xDF, 0x68, 0x86, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x92, 0x0E, 0xC3, 0xF2, 0x58, 0xE8, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_ecp_point secp521r1_T[32] = { + ECP_POINT_INIT_XY_Z1(secp521r1_T_0_X, secp521r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_1_X, secp521r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_2_X, secp521r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_3_X, secp521r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_4_X, secp521r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_5_X, secp521r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_6_X, secp521r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_7_X, secp521r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_8_X, secp521r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_9_X, secp521r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_10_X, secp521r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_11_X, secp521r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_12_X, secp521r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_13_X, secp521r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_14_X, secp521r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_15_X, secp521r1_T_15_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_16_X, secp521r1_T_16_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_17_X, secp521r1_T_17_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_18_X, secp521r1_T_18_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_19_X, secp521r1_T_19_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_20_X, secp521r1_T_20_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_21_X, secp521r1_T_21_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_22_X, secp521r1_T_22_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_23_X, secp521r1_T_23_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_24_X, secp521r1_T_24_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_25_X, secp521r1_T_25_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_26_X, secp521r1_T_26_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_27_X, secp521r1_T_27_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_28_X, secp521r1_T_28_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_29_X, secp521r1_T_29_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_30_X, secp521r1_T_30_Y), + ECP_POINT_INIT_XY_Z0(secp521r1_T_31_X, secp521r1_T_31_Y), +}; +#else +#define secp521r1_T NULL +#endif +#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) +static const mbedtls_mpi_uint secp192k1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0xEE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; +static const mbedtls_mpi_uint secp192k1_a[] = { + MBEDTLS_BYTES_TO_T_UINT_2(0x00, 0x00), +}; +static const mbedtls_mpi_uint secp192k1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_2(0x03, 0x00), +}; +static const mbedtls_mpi_uint secp192k1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x6C, 0xE0, 0xEA, 0xB1, 0xD1, 0xA5, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xF4, 0xB7, 0x80, 0x02, 0x7D, 0xB0, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0xE9, 0x57, 0xC0, 0x0E, 0xF1, 0x4F, 0xDB), +}; +static const mbedtls_mpi_uint secp192k1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x2F, 0x5E, 0xD9, 0x88, 0xAA, 0x82, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x86, 0xBE, 0x15, 0xD0, 0x63, 0x41, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x28, 0x56, 0x9C, 0x6D, 0x2F, 0x2F, 0x9B), +}; +static const mbedtls_mpi_uint secp192k1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xFD, 0xDE, 0x74, 0x6A, 0x46, 0x69, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xFC, 0xF2, 0x26, 0xFE, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp192k1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x6C, 0xE0, 0xEA, 0xB1, 0xD1, 0xA5, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xF4, 0xB7, 0x80, 0x02, 0x7D, 0xB0, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0xE9, 0x57, 0xC0, 0x0E, 0xF1, 0x4F, 0xDB), +}; +static const mbedtls_mpi_uint secp192k1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x2F, 0x5E, 0xD9, 0x88, 0xAA, 0x82, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x86, 0xBE, 0x15, 0xD0, 0x63, 0x41, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x28, 0x56, 0x9C, 0x6D, 0x2F, 0x2F, 0x9B), +}; +static const mbedtls_mpi_uint secp192k1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x77, 0x3D, 0x0D, 0x85, 0x48, 0xA8, 0xA9), + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x07, 0xDF, 0x1D, 0xB3, 0xB3, 0x01, 0x54), + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0x86, 0xF6, 0xAF, 0x19, 0x2A, 0x88, 0x2E), +}; +static const mbedtls_mpi_uint secp192k1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0x90, 0xB6, 0x2F, 0x48, 0x36, 0x4C, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x11, 0x14, 0xA6, 0xCB, 0xBA, 0x15, 0xD9), + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0xB0, 0xF2, 0xD4, 0xC9, 0xDA, 0xBA, 0xD7), +}; +static const mbedtls_mpi_uint secp192k1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0xC1, 0x9C, 0xE6, 0xBB, 0xFB, 0xCF, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x19, 0xAC, 0x5A, 0xC9, 0x8A, 0x1C, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0xF6, 0x76, 0x86, 0x89, 0x27, 0x8D, 0x28), +}; +static const mbedtls_mpi_uint secp192k1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0xE0, 0x6F, 0x34, 0xBA, 0x5E, 0xD3, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0xDC, 0xA6, 0x87, 0xC9, 0x9D, 0xC0, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x11, 0x7E, 0xD6, 0xF7, 0x33, 0xFC, 0xE4), +}; +static const mbedtls_mpi_uint secp192k1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x37, 0x3E, 0xC0, 0x7F, 0x62, 0xE7, 0x54), + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0x3B, 0x69, 0x9D, 0x44, 0xBC, 0x82, 0x99), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x84, 0xB3, 0x5F, 0x2B, 0xA5, 0x9E, 0x2C), +}; +static const mbedtls_mpi_uint secp192k1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x95, 0xEB, 0x4C, 0x04, 0xB4, 0xF4, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0xAD, 0x4B, 0xD5, 0x9A, 0xEB, 0xC4, 0x4E), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0xB1, 0xC5, 0x59, 0xE3, 0xD5, 0x16, 0x2A), +}; +static const mbedtls_mpi_uint secp192k1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x48, 0x2A, 0xCC, 0xAC, 0xD0, 0xEE, 0x50, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x83, 0xE0, 0x5B, 0x14, 0x44, 0x52, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x15, 0x2D, 0x78, 0xF6, 0x51, 0x32, 0xCF), +}; +static const mbedtls_mpi_uint secp192k1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x36, 0x9B, 0xDD, 0xF8, 0xDD, 0xEF, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0xB1, 0x6A, 0x2B, 0xAF, 0xEB, 0x2B, 0xB1), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x87, 0x7A, 0x66, 0x5D, 0x5B, 0xDF, 0x8F), +}; +static const mbedtls_mpi_uint secp192k1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x45, 0xE5, 0x81, 0x9B, 0xEB, 0x37, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0x29, 0xE2, 0x20, 0x64, 0x23, 0x6B, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0x1D, 0x41, 0xE1, 0x9B, 0x61, 0x7B, 0xD9), +}; +static const mbedtls_mpi_uint secp192k1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0x57, 0xA3, 0x0A, 0x13, 0xE4, 0x59, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0x6E, 0x4A, 0x48, 0x84, 0x90, 0xAC, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0xB8, 0xF5, 0xF3, 0xDE, 0xA0, 0xA1, 0x1D), +}; +static const mbedtls_mpi_uint secp192k1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0x32, 0x81, 0xA9, 0x91, 0x5A, 0x4E, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0xA8, 0x90, 0xBE, 0x0F, 0xEC, 0xC0, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x30, 0xD7, 0x08, 0xAE, 0xC4, 0x3A, 0xA5), +}; +static const mbedtls_mpi_uint secp192k1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x55, 0xE3, 0x76, 0xB3, 0x64, 0x74, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x75, 0xD4, 0xDB, 0x98, 0xD7, 0x39, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0xEB, 0x8A, 0xAB, 0x16, 0xD9, 0xD4, 0x0B), +}; +static const mbedtls_mpi_uint secp192k1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0xBE, 0xF9, 0xC7, 0xC7, 0xBA, 0xF3, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x85, 0x59, 0xF3, 0x60, 0x41, 0x02, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0x1C, 0x4A, 0xA4, 0xC7, 0xED, 0x66, 0xBC), +}; +static const mbedtls_mpi_uint secp192k1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0x9C, 0x2E, 0x46, 0x52, 0x18, 0x87, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x35, 0x5A, 0x75, 0xAC, 0x4D, 0x75, 0x91), + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0x2F, 0xAC, 0xFC, 0xBC, 0xE6, 0x93, 0x5E), +}; +static const mbedtls_mpi_uint secp192k1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x4D, 0xC9, 0x18, 0xE9, 0x00, 0xEB, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x69, 0x72, 0x07, 0x5A, 0x59, 0xA8, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x65, 0x83, 0x20, 0x10, 0xF9, 0x69, 0x82), +}; +static const mbedtls_mpi_uint secp192k1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0x56, 0x7F, 0x9F, 0xBF, 0x46, 0x0C, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0xCF, 0xF0, 0xDC, 0xDF, 0x2D, 0xE6, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0xF0, 0x72, 0x3A, 0x7A, 0x03, 0xE5, 0x22), +}; +static const mbedtls_mpi_uint secp192k1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0xAA, 0x57, 0x13, 0x37, 0xA7, 0x2C, 0xD4), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0xAC, 0xA2, 0x23, 0xF9, 0x84, 0x60, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0xEB, 0x51, 0x70, 0x64, 0x78, 0xCA, 0x05), +}; +static const mbedtls_mpi_uint secp192k1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0xCC, 0x30, 0x62, 0x93, 0x46, 0x13, 0xE9), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x26, 0xCC, 0x6C, 0x3D, 0x5C, 0xDA, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0xAA, 0xB8, 0x03, 0xA4, 0x1A, 0x00, 0x96), +}; +static const mbedtls_mpi_uint secp192k1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x9D, 0xE6, 0xCC, 0x4E, 0x2E, 0xC2, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0xC3, 0x8A, 0xAE, 0x6F, 0x40, 0x05, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x8F, 0x4A, 0x4D, 0x35, 0xD3, 0x50, 0x9D), +}; +static const mbedtls_mpi_uint secp192k1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0xFD, 0x98, 0xAB, 0xC7, 0x03, 0xB4, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x40, 0xD2, 0x9F, 0xCA, 0xD0, 0x53, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x84, 0x00, 0x6F, 0xC8, 0xAD, 0xED, 0x8D), +}; +static const mbedtls_mpi_uint secp192k1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0xD3, 0x57, 0xD7, 0xC3, 0x07, 0xBD, 0xD7), + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0xBA, 0x47, 0x1D, 0x3D, 0xEF, 0x98, 0x6C), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xC0, 0x6C, 0x7F, 0x12, 0xEE, 0x9F, 0x67), +}; +static const mbedtls_mpi_uint secp192k1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x02, 0xDA, 0x79, 0xAA, 0xC9, 0x27, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x79, 0xC7, 0x71, 0x84, 0xCB, 0xE5, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x37, 0x06, 0xBA, 0xB5, 0xD5, 0x18, 0x4C), +}; +static const mbedtls_mpi_uint secp192k1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x65, 0x72, 0x6C, 0xF2, 0x63, 0x27, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0xBC, 0x71, 0xDF, 0x75, 0xF8, 0x98, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x70, 0x9B, 0xDC, 0xE7, 0x18, 0x71, 0xFF), +}; +static const mbedtls_mpi_uint secp192k1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x5B, 0x9F, 0x00, 0x5A, 0xB6, 0x80, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xE0, 0xBB, 0xFC, 0x5E, 0x78, 0x9C, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0x60, 0x03, 0x68, 0x83, 0x3D, 0x2E, 0x4C, 0xDD), +}; +static const mbedtls_mpi_uint secp192k1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3B, 0x49, 0x23, 0xA8, 0xCB, 0x3B, 0x1A, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0x3D, 0xA7, 0x46, 0xCF, 0x75, 0xB6, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0xFD, 0x30, 0x01, 0xB6, 0xEF, 0xF9, 0xE8), +}; +static const mbedtls_mpi_uint secp192k1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDC, 0xFA, 0xDA, 0xB8, 0x29, 0x42, 0xC9, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0xD7, 0xA0, 0xE6, 0x6B, 0x86, 0x61, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0xE9, 0xD3, 0x37, 0xD8, 0xE7, 0x35, 0xA9), +}; +static const mbedtls_mpi_uint secp192k1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0xC8, 0x8E, 0xB1, 0xCB, 0xB1, 0xB5, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0xD7, 0x46, 0x7D, 0xAF, 0xE2, 0xDC, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x46, 0xE7, 0xD8, 0x76, 0x31, 0x90, 0x76), +}; +static const mbedtls_mpi_uint secp192k1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0xD3, 0xF4, 0x74, 0xE1, 0x67, 0xD8, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0x70, 0x3C, 0xC8, 0xAF, 0x5F, 0xF4, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0x4E, 0xED, 0x5C, 0x43, 0xB3, 0x16, 0x35), +}; +static const mbedtls_mpi_uint secp192k1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0xAE, 0xD1, 0xDD, 0x31, 0x14, 0xD3, 0xF0), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x14, 0x06, 0x13, 0x12, 0x1C, 0x81, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0xA6, 0xF9, 0x0C, 0x91, 0xF7, 0x67, 0x59, 0x63), +}; +static const mbedtls_mpi_uint secp192k1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0x91, 0xE2, 0xF4, 0x9D, 0xEB, 0x88, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x82, 0x30, 0x9C, 0xAE, 0x18, 0x4D, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0x79, 0xCF, 0x17, 0xA5, 0x1E, 0xE8, 0xC8), +}; +static const mbedtls_ecp_point secp192k1_T[16] = { + ECP_POINT_INIT_XY_Z1(secp192k1_T_0_X, secp192k1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_1_X, secp192k1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_2_X, secp192k1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_3_X, secp192k1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_4_X, secp192k1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_5_X, secp192k1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_6_X, secp192k1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_7_X, secp192k1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_8_X, secp192k1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_9_X, secp192k1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_10_X, secp192k1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_11_X, secp192k1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_12_X, secp192k1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_13_X, secp192k1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_14_X, secp192k1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp192k1_T_15_X, secp192k1_T_15_Y), +}; +#else +#define secp192k1_T NULL +#endif + +#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) +static const mbedtls_mpi_uint secp224k1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xE5, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_4(0xFF, 0xFF, 0xFF, 0xFF), +}; +static const mbedtls_mpi_uint secp224k1_a[] = { + MBEDTLS_BYTES_TO_T_UINT_2(0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_2(0x05, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0xA4, 0xB7, 0xB6, 0x0E, 0x65, 0x7E, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0x75, 0x70, 0xE4, 0xE9, 0x67, 0xA4, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x28, 0xFC, 0x30, 0xDF, 0x99, 0xF0, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_4(0x33, 0x5B, 0x45, 0xA1), +}; +static const mbedtls_mpi_uint secp224k1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0x61, 0x6D, 0x55, 0xDB, 0x4B, 0xCA, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xBD, 0xB0, 0xC0, 0xF7, 0x19, 0xE3, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0xFB, 0xCA, 0x82, 0x42, 0x34, 0xBA, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_4(0xED, 0x9F, 0x08, 0x7E), +}; +static const mbedtls_mpi_uint secp224k1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0xB1, 0x9F, 0x76, 0x71, 0xA9, 0xF0, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0x61, 0xEC, 0xD2, 0xE8, 0xDC, 0x01, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00), +}; + +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp224k1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0xA4, 0xB7, 0xB6, 0x0E, 0x65, 0x7E, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0x75, 0x70, 0xE4, 0xE9, 0x67, 0xA4, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x28, 0xFC, 0x30, 0xDF, 0x99, 0xF0, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0x5B, 0x45, 0xA1, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0x61, 0x6D, 0x55, 0xDB, 0x4B, 0xCA, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xBD, 0xB0, 0xC0, 0xF7, 0x19, 0xE3, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0xFB, 0xCA, 0x82, 0x42, 0x34, 0xBA, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0x9F, 0x08, 0x7E, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x6C, 0x22, 0x22, 0x40, 0x89, 0xAE, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0x92, 0xE1, 0x87, 0x56, 0x35, 0xAF, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0xAF, 0x08, 0x35, 0x27, 0xEA, 0x04, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x53, 0xFD, 0xCF, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0xD0, 0x9F, 0x8D, 0xF3, 0x63, 0x54, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0xDB, 0x0F, 0x61, 0x54, 0x26, 0xD1, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x21, 0xF7, 0x1B, 0xB5, 0x1D, 0xF6, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x05, 0xDA, 0x8F, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0x26, 0x73, 0xBC, 0xE4, 0x29, 0x62, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x95, 0x17, 0x8B, 0xC3, 0x9B, 0xAC, 0xCC), + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0xDB, 0x77, 0xDF, 0xDD, 0x13, 0x04, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0xFC, 0x22, 0x93, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x65, 0xF1, 0x5A, 0x37, 0xEF, 0x79, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x01, 0x37, 0xAC, 0x9A, 0x5B, 0x51, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x75, 0x13, 0xA9, 0x4A, 0xAD, 0xFE, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x82, 0x6F, 0x66, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0x5E, 0xF0, 0x40, 0xC3, 0xA6, 0xE2, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x9A, 0x6F, 0xCF, 0x11, 0x26, 0x66, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0x73, 0xA8, 0xCF, 0x2B, 0x12, 0x36, 0x37), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0xB3, 0x0A, 0x58, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x79, 0x00, 0x55, 0x04, 0x34, 0x90, 0x1A), + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0x54, 0x1C, 0xC2, 0x45, 0x0C, 0x1B, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x19, 0xAB, 0xA8, 0xFC, 0x73, 0xDC, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0xFB, 0x93, 0xCE, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x75, 0xD0, 0x66, 0x95, 0x86, 0xCA, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xEA, 0x29, 0x16, 0x6A, 0x38, 0xDF, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0xA2, 0x36, 0x2F, 0xDC, 0xBB, 0x5E, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x89, 0x59, 0x49, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0xA3, 0x99, 0x9D, 0xB8, 0x77, 0x9D, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0x93, 0x43, 0x47, 0xC6, 0x5C, 0xF9, 0xFD), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0x00, 0x79, 0x42, 0x64, 0xB8, 0x25, 0x3E), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x54, 0xB4, 0x33, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x0C, 0x42, 0x90, 0x83, 0x0B, 0x31, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x2E, 0xAE, 0xC8, 0xC7, 0x5F, 0xD2, 0x70), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0xBC, 0xAD, 0x41, 0xE7, 0x32, 0x3A, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0x97, 0x52, 0x83, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x13, 0x7A, 0xBD, 0xAE, 0x94, 0x60, 0xFD), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x9B, 0x95, 0xB4, 0x6E, 0x68, 0xB2, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x49, 0xBE, 0x51, 0xFE, 0x66, 0x15, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x37, 0xE4, 0xFE, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0x9B, 0xEE, 0x64, 0xC9, 0x1B, 0xBD, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x5F, 0x34, 0xA9, 0x0B, 0xB7, 0x25, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0x13, 0xB1, 0x38, 0xFB, 0x9D, 0x78, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0xE7, 0x1B, 0xFA, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0xB3, 0xB7, 0x44, 0x92, 0x6B, 0x00, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x82, 0x44, 0x3E, 0x18, 0x1A, 0x58, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0xF8, 0xC0, 0xE4, 0xEE, 0xC1, 0xBF, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x32, 0x27, 0xB2, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0x9A, 0x42, 0x62, 0x8B, 0x26, 0x54, 0x21), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0x85, 0x74, 0xA0, 0x79, 0xA8, 0xEE, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x36, 0x60, 0xB3, 0x28, 0x4D, 0x55, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x27, 0x82, 0x29, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0xFC, 0x73, 0x77, 0xAF, 0x5C, 0xAC, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0xED, 0xE5, 0xF6, 0x1D, 0xA8, 0x67, 0x43), + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0xDE, 0x33, 0x1C, 0xF1, 0x80, 0x73, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xE2, 0xDE, 0x3C, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0x3E, 0x6B, 0xFE, 0xF0, 0x04, 0x28, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0xB2, 0x14, 0x9D, 0x18, 0x11, 0x7D, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xC4, 0xD6, 0x2E, 0x6E, 0x57, 0x4D, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0x55, 0x1B, 0xDE, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xF7, 0x17, 0xBC, 0x45, 0xAB, 0x16, 0xAB), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0xB0, 0xEF, 0x61, 0xE3, 0x20, 0x7C, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x85, 0x41, 0x4D, 0xF1, 0x7E, 0x4D, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0xC2, 0x9B, 0x5E, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x2E, 0x49, 0x3D, 0x3E, 0x4B, 0xD3, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0x2B, 0x9D, 0xD5, 0x27, 0xFA, 0xCA, 0xE0), + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0xB3, 0x6A, 0xE0, 0x79, 0x14, 0x28, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x1E, 0xDC, 0xF5, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x44, 0x56, 0xCD, 0xFC, 0x9F, 0x09, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0x8C, 0x59, 0xA4, 0x64, 0x2A, 0x3A, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0xA0, 0xB5, 0x86, 0x4E, 0x69, 0xDA, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x8B, 0x11, 0x38, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x17, 0x16, 0x12, 0x17, 0xDC, 0x00, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0x76, 0x24, 0x6C, 0x97, 0x2C, 0xB5, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0x82, 0x71, 0xE3, 0xB0, 0xBB, 0x4E, 0x50, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x6E, 0x48, 0x26, 0xD5, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0x5F, 0x28, 0xF6, 0x01, 0x5A, 0x60, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x95, 0xFE, 0xD0, 0xAD, 0x15, 0xD4, 0xD9), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x5B, 0x7A, 0xFD, 0x80, 0xF7, 0x9F, 0x64), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0xBC, 0x1B, 0xDF, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0xE6, 0xDF, 0x14, 0x29, 0xF4, 0xD4, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0x12, 0xDD, 0xEC, 0x5B, 0x8A, 0x59, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x92, 0x3E, 0x35, 0x08, 0xE9, 0xCF, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0x35, 0x29, 0x97, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0xDB, 0xD6, 0x6A, 0xC5, 0x43, 0xA4, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x33, 0x50, 0x61, 0x70, 0xA1, 0xE9, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x15, 0x6E, 0x5F, 0x01, 0x0C, 0x8C, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0x85, 0xA1, 0x9A, 0x9D, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6E, 0xC6, 0xF7, 0xE2, 0x4A, 0xCD, 0x9B, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x4D, 0x5A, 0xB8, 0xE2, 0x6D, 0xA6, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x3F, 0xB6, 0x17, 0xE3, 0x2C, 0x6F, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xA4, 0x59, 0x51, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x4F, 0x7C, 0x49, 0xCD, 0x6E, 0xEB, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0xC9, 0x1F, 0xB7, 0x4D, 0x98, 0xC7, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0x4C, 0xFD, 0x98, 0x20, 0x95, 0xBB, 0x20, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0xF2, 0x73, 0x92, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0xEF, 0xFB, 0x30, 0xFA, 0x12, 0x1A, 0xB0), + MBEDTLS_BYTES_TO_T_UINT_8(0x7A, 0x4C, 0x24, 0xB4, 0x5B, 0xC9, 0x4C, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x7A, 0xDD, 0x5E, 0x84, 0x95, 0x4D, 0x26, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0xFA, 0xF9, 0x3A, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0xA3, 0x2E, 0x7A, 0xDC, 0xA7, 0x53, 0xA9), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x9F, 0x81, 0x84, 0xB2, 0x0D, 0xFE, 0x31), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x89, 0x1B, 0x77, 0x0C, 0x89, 0x71, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0xFF, 0x7F, 0xB2, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0xE9, 0x2C, 0x79, 0xA6, 0x3C, 0xAD, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0xE0, 0x23, 0x02, 0x86, 0x0F, 0x77, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0x93, 0x6D, 0xE9, 0xF9, 0x3C, 0xBE, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0xE7, 0x24, 0x92, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x3C, 0x5B, 0x4B, 0x1B, 0x25, 0x37, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0xE8, 0x38, 0x1B, 0xA1, 0x5A, 0x2E, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0x19, 0xFD, 0xF4, 0x78, 0x01, 0x6B, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0x69, 0x37, 0x4F, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0xE2, 0xBF, 0xD3, 0xEC, 0x95, 0x9C, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x7B, 0xFC, 0xD5, 0xD3, 0x25, 0x5E, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0x55, 0x09, 0xA2, 0x58, 0x6A, 0xC9, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0xCC, 0x3B, 0xD9, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_mpi_uint secp224k1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8F, 0x08, 0x65, 0x5E, 0xCB, 0xAB, 0x48, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0x79, 0x8B, 0xC0, 0x11, 0xC0, 0x69, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0xE8, 0x8C, 0x4C, 0xC5, 0x28, 0xE4, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0x1F, 0x34, 0x5C, 0x00, 0x00, 0x00, 0x00), +}; +static const mbedtls_ecp_point secp224k1_T[16] = { + ECP_POINT_INIT_XY_Z1(secp224k1_T_0_X, secp224k1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_1_X, secp224k1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_2_X, secp224k1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_3_X, secp224k1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_4_X, secp224k1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_5_X, secp224k1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_6_X, secp224k1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_7_X, secp224k1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_8_X, secp224k1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_9_X, secp224k1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_10_X, secp224k1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_11_X, secp224k1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_12_X, secp224k1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_13_X, secp224k1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_14_X, secp224k1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp224k1_T_15_X, secp224k1_T_15_Y), +}; +#else +#define secp224k1_T NULL +#endif +#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +static const mbedtls_mpi_uint secp256k1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; +static const mbedtls_mpi_uint secp256k1_a[] = { + MBEDTLS_BYTES_TO_T_UINT_2(0x00, 0x00), +}; +static const mbedtls_mpi_uint secp256k1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_2(0x07, 0x00), +}; +static const mbedtls_mpi_uint secp256k1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x17, 0xF8, 0x16, 0x5B, 0x81, 0xF2, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x28, 0xCE, 0x2D, 0xDB, 0xFC, 0x9B, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0x0B, 0x87, 0xCE, 0x95, 0x62, 0xA0, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xBB, 0xDC, 0xF9, 0x7E, 0x66, 0xBE, 0x79), +}; +static const mbedtls_mpi_uint secp256k1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0xD4, 0x10, 0xFB, 0x8F, 0xD0, 0x47, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x54, 0x85, 0xA6, 0x48, 0xB4, 0x17, 0xFD), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x08, 0x11, 0x0E, 0xFC, 0xFB, 0xA4, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0xC4, 0xA3, 0x26, 0x77, 0xDA, 0x3A, 0x48), +}; +static const mbedtls_mpi_uint secp256k1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint secp256k1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x17, 0xF8, 0x16, 0x5B, 0x81, 0xF2, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x28, 0xCE, 0x2D, 0xDB, 0xFC, 0x9B, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0x0B, 0x87, 0xCE, 0x95, 0x62, 0xA0, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xBB, 0xDC, 0xF9, 0x7E, 0x66, 0xBE, 0x79), +}; +static const mbedtls_mpi_uint secp256k1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0xD4, 0x10, 0xFB, 0x8F, 0xD0, 0x47, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x54, 0x85, 0xA6, 0x48, 0xB4, 0x17, 0xFD), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x08, 0x11, 0x0E, 0xFC, 0xFB, 0xA4, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0xC4, 0xA3, 0x26, 0x77, 0xDA, 0x3A, 0x48), +}; +static const mbedtls_mpi_uint secp256k1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0xEE, 0xD7, 0x1E, 0x67, 0x86, 0x32, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x23, 0x73, 0xB1, 0xA9, 0xD5, 0xCC, 0x27, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x0E, 0x11, 0x01, 0x71, 0xFE, 0x92, 0x73), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0x28, 0x63, 0x6D, 0x72, 0x09, 0xA6, 0xC0), +}; +static const mbedtls_mpi_uint secp256k1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0xE1, 0x69, 0xDC, 0x3E, 0x2C, 0x75, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0xB7, 0x3F, 0x30, 0x26, 0x3C, 0xDF, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xBE, 0xB9, 0x5D, 0x0E, 0xE8, 0x5E, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0xC3, 0x05, 0xD6, 0xB7, 0xD5, 0x24, 0xFC), +}; +static const mbedtls_mpi_uint secp256k1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0xCF, 0x7B, 0xDC, 0xCD, 0xC3, 0x39, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0xDA, 0xB9, 0xE5, 0x64, 0xA7, 0x47, 0x91), + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0x46, 0xA8, 0x61, 0xF6, 0x23, 0xEB, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0xC1, 0xFF, 0xE4, 0x55, 0xD5, 0xC2, 0xBF), +}; +static const mbedtls_mpi_uint secp256k1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0xBE, 0xB9, 0x59, 0x24, 0x13, 0x4A, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x45, 0x12, 0xDE, 0xBA, 0x4F, 0xEF, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0x08, 0xBF, 0xC1, 0x66, 0xAA, 0x0A, 0xBC), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0xFE, 0x30, 0x55, 0x31, 0x86, 0xA7, 0xB4), +}; +static const mbedtls_mpi_uint secp256k1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0xBF, 0x18, 0x81, 0x67, 0x27, 0x42, 0xBD), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x05, 0x83, 0xA4, 0xDD, 0x57, 0xD3, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x20, 0x63, 0xAB, 0xE4, 0x90, 0x70, 0xD0, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x5D, 0xFD, 0xA0, 0xEF, 0xCF, 0x1C, 0x54), +}; +static const mbedtls_mpi_uint secp256k1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0x80, 0xE4, 0xF6, 0x09, 0xBC, 0x57, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x9F, 0x6E, 0x88, 0x54, 0x6E, 0x51, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x5F, 0x85, 0xFB, 0x84, 0x3E, 0x4A, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x19, 0xF5, 0x55, 0xC9, 0x07, 0xD8, 0xCE), +}; +static const mbedtls_mpi_uint secp256k1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0xB4, 0xC3, 0xD9, 0x5C, 0xA0, 0xD4, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0x30, 0xAF, 0x59, 0x9B, 0xF8, 0x04, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0xA6, 0xFD, 0x66, 0x7B, 0xC3, 0x39, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0xE0, 0xBF, 0xF0, 0xC2, 0xE9, 0x71, 0xA4, 0x9E), +}; +static const mbedtls_mpi_uint secp256k1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0x2D, 0xB9, 0x88, 0x28, 0xF1, 0xBE, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0xF3, 0x1A, 0x0E, 0xB9, 0x01, 0x66, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0xA7, 0xA4, 0xF4, 0x05, 0xD0, 0xAA, 0x53), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x39, 0x1E, 0x47, 0xE5, 0x68, 0xC8, 0xC0), +}; +static const mbedtls_mpi_uint secp256k1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0xB9, 0xFC, 0xE0, 0x33, 0x8A, 0x7D, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0x93, 0xA5, 0x53, 0x55, 0x16, 0xB4, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0x5F, 0xEA, 0x9B, 0x29, 0x52, 0x71, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0xB2, 0xF0, 0x24, 0xB8, 0x7D, 0xB7, 0xA0, 0x9B), +}; +static const mbedtls_mpi_uint secp256k1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x00, 0x27, 0xB2, 0xDF, 0x73, 0xA2, 0xE0), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x2E, 0x4D, 0x7C, 0xDE, 0x7A, 0x23, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x65, 0x60, 0xC7, 0x97, 0x1E, 0xA4, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0x13, 0x5B, 0x77, 0x59, 0xCB, 0x36, 0xE1), +}; +static const mbedtls_mpi_uint secp256k1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0xBC, 0x9F, 0x9E, 0x2D, 0x53, 0x2A, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x5F, 0x64, 0x9F, 0x1A, 0x19, 0xE6, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0x7B, 0x39, 0xD2, 0xDB, 0x85, 0x84, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0xC7, 0x0D, 0x58, 0x6E, 0x3F, 0x52, 0x15), +}; +static const mbedtls_mpi_uint secp256k1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x68, 0x19, 0x0B, 0x68, 0xC9, 0x1E, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0xD2, 0x4E, 0x21, 0x49, 0x3D, 0x55, 0xCC, 0x25), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xF9, 0x25, 0x45, 0x54, 0x45, 0xB1, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0xB3, 0xF7, 0xCD, 0x80, 0xA4, 0x04, 0x05), +}; +static const mbedtls_mpi_uint secp256k1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x1E, 0x88, 0xC4, 0xAA, 0x18, 0x7E, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0xAC, 0xD9, 0xB2, 0xA1, 0xC0, 0x71, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0xA2, 0xF1, 0x15, 0xA6, 0x5F, 0x6C, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0x5B, 0x05, 0xBC, 0xB7, 0xC6, 0x4E, 0x72), +}; +static const mbedtls_mpi_uint secp256k1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x80, 0xF8, 0x5C, 0x20, 0x2A, 0xE1, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x48, 0x2E, 0x68, 0x82, 0x7F, 0xEB, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0xA2, 0x3B, 0x25, 0xDB, 0x32, 0x4D, 0x88, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0x6E, 0xA6, 0xB6, 0x6D, 0x62, 0x78, 0x22), +}; +static const mbedtls_mpi_uint secp256k1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x4D, 0x3E, 0x86, 0x58, 0xC3, 0xEB, 0xBA), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x89, 0x33, 0x18, 0x21, 0x1D, 0x9B, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x9D, 0xFF, 0xC3, 0x79, 0xC1, 0x88, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0xD4, 0x48, 0x53, 0xE8, 0xAD, 0x21, 0x16), +}; +static const mbedtls_mpi_uint secp256k1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x7B, 0xDE, 0xCB, 0xD8, 0x39, 0x17, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0xF3, 0x03, 0xF2, 0x5C, 0xBC, 0xC8, 0x8A), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0xAE, 0x4C, 0xB0, 0x16, 0xA4, 0x93, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x8B, 0x6B, 0xDC, 0xD7, 0x9A, 0x3E, 0x7E), +}; +static const mbedtls_mpi_uint secp256k1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x2D, 0x7A, 0xD2, 0x59, 0x05, 0xA2, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0x56, 0x09, 0x32, 0xF1, 0xE8, 0xE3, 0x72), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0xCA, 0xE5, 0x2E, 0xF0, 0xFB, 0x18, 0x19), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x85, 0xA9, 0x23, 0x15, 0x31, 0x1F, 0x0E), +}; +static const mbedtls_mpi_uint secp256k1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0xE5, 0xB1, 0x86, 0xB9, 0x6E, 0x8D, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x77, 0xFC, 0xC9, 0xA3, 0x3F, 0x89, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x6A, 0xDC, 0x25, 0xB0, 0xC7, 0x41, 0x54), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0x11, 0x6B, 0xA6, 0x11, 0x62, 0xD4, 0x2D), +}; +static const mbedtls_mpi_uint secp256k1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x7D, 0x34, 0xB3, 0x20, 0x7F, 0x37, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0xD4, 0x45, 0xE8, 0xC2, 0xE9, 0xC5, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x32, 0x3B, 0x25, 0x7E, 0x79, 0xAF, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0xE4, 0x54, 0x71, 0xBE, 0x35, 0x4E, 0xD0), +}; +static const mbedtls_mpi_uint secp256k1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x94, 0xDD, 0x8F, 0xB5, 0xC2, 0xDD, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0x49, 0xE9, 0x1C, 0x2F, 0x08, 0x49, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0xB6, 0x03, 0x88, 0x6F, 0xB8, 0x15, 0x67), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0xD3, 0x1C, 0xF3, 0xA5, 0xEB, 0x79, 0x01), +}; +static const mbedtls_mpi_uint secp256k1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0xF9, 0x43, 0x88, 0x89, 0x0D, 0x06, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0x2D, 0xF5, 0x98, 0x32, 0xF6, 0xB1, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0x23, 0x73, 0x8F, 0x2B, 0x50, 0x27, 0x0A, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0xE3, 0xBD, 0x16, 0x05, 0xC8, 0x93, 0x12), +}; +static const mbedtls_mpi_uint secp256k1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0x6A, 0xF7, 0xE3, 0x3D, 0xDE, 0x5F, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0xA3, 0x9C, 0x22, 0x3C, 0x33, 0x36, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x20, 0x24, 0x4C, 0x69, 0x45, 0x78, 0x14, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xF8, 0xD4, 0xBF, 0xB8, 0xC0, 0xA1, 0x25), +}; +static const mbedtls_mpi_uint secp256k1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x88, 0xE1, 0x91, 0x03, 0xEB, 0xB3, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0x11, 0xA1, 0xEF, 0x14, 0x0D, 0xC4, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0xD4, 0x0D, 0x1D, 0x96, 0x33, 0x5C, 0x19), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x45, 0x2A, 0x1A, 0xE6, 0x57, 0x04, 0x9B), +}; +static const mbedtls_mpi_uint secp256k1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0xB5, 0xA7, 0x80, 0xE9, 0x93, 0x97, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0xB9, 0x7C, 0xA0, 0xC9, 0x57, 0x26, 0x43), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0xEF, 0x56, 0xDA, 0x66, 0xF6, 0x1B, 0x9A), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x89, 0x6B, 0x91, 0xE0, 0xA9, 0x65, 0x2B), +}; +static const mbedtls_mpi_uint secp256k1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x98, 0x96, 0x9B, 0x06, 0x7D, 0x5E, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0xFA, 0xC1, 0x5F, 0x19, 0x37, 0x94, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0xCF, 0xBE, 0x6B, 0x1A, 0x05, 0xE4, 0xBF, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0xCD, 0x5D, 0x35, 0xB4, 0x51, 0xF7, 0x64), +}; +static const mbedtls_mpi_uint secp256k1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0xEF, 0x96, 0xDB, 0xF2, 0x61, 0x63, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x04, 0x88, 0xC9, 0x9F, 0x1B, 0x94, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x30, 0x79, 0x7E, 0x24, 0xE7, 0x5F, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0xB8, 0x90, 0xB7, 0x94, 0x25, 0xBB, 0x0F), +}; +static const mbedtls_mpi_uint secp256k1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x79, 0xEA, 0xAD, 0xC0, 0x6D, 0x18, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0xA4, 0x58, 0x2A, 0x8D, 0x95, 0xB3, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0xC4, 0xC2, 0x12, 0x0D, 0x79, 0xE2, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0x6F, 0xBE, 0x97, 0x4D, 0xA4, 0x20, 0x07), +}; +static const mbedtls_mpi_uint secp256k1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x31, 0x71, 0xC6, 0xA6, 0x91, 0xEB, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x9B, 0xA8, 0x4A, 0xE7, 0x77, 0xE1, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0x06, 0xD3, 0x3D, 0x94, 0x30, 0xEF, 0x8C), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0xDF, 0xCA, 0xFA, 0xF5, 0x28, 0xF8, 0xC9), +}; +static const mbedtls_mpi_uint secp256k1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0xE1, 0x32, 0xFD, 0x3E, 0x81, 0xF8, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0xF2, 0x4B, 0x1D, 0x19, 0xC9, 0x0F, 0xCC), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xB1, 0x8A, 0x22, 0x8B, 0x05, 0x6B, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0x21, 0xEF, 0x30, 0xEC, 0x09, 0x2A, 0x89), +}; +static const mbedtls_mpi_uint secp256k1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x84, 0x4A, 0x46, 0x07, 0x6C, 0x3C, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x18, 0x3A, 0xF4, 0xCC, 0xF5, 0xB2, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0x8F, 0xCD, 0x0A, 0x9C, 0xF4, 0xBD, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x89, 0x7F, 0x8A, 0xB1, 0x52, 0x3A, 0xAB), +}; +static const mbedtls_ecp_point secp256k1_T[16] = { + ECP_POINT_INIT_XY_Z1(secp256k1_T_0_X, secp256k1_T_0_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_1_X, secp256k1_T_1_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_2_X, secp256k1_T_2_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_3_X, secp256k1_T_3_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_4_X, secp256k1_T_4_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_5_X, secp256k1_T_5_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_6_X, secp256k1_T_6_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_7_X, secp256k1_T_7_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_8_X, secp256k1_T_8_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_9_X, secp256k1_T_9_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_10_X, secp256k1_T_10_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_11_X, secp256k1_T_11_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_12_X, secp256k1_T_12_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_13_X, secp256k1_T_13_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_14_X, secp256k1_T_14_Y), + ECP_POINT_INIT_XY_Z0(secp256k1_T_15_X, secp256k1_T_15_Y), +}; +#else +#define secp256k1_T NULL +#endif +#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */ + +/* + * Domain parameters for brainpoolP256r1 (RFC 5639 3.4) + */ +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) +static const mbedtls_mpi_uint brainpoolP256r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x53, 0x6E, 0x1F, 0x1D, 0x48, 0x13, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x20, 0x26, 0xD5, 0x23, 0xF6, 0x3B, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x8D, 0x83, 0x9D, 0x90, 0x0A, 0x66, 0x3E), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xA9, 0xEE, 0xA1, 0xDB, 0x57, 0xFB, 0xA9), +}; +static const mbedtls_mpi_uint brainpoolP256r1_a[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0xB5, 0x30, 0xF3, 0x44, 0x4B, 0x4A, 0xE9), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x5C, 0xDC, 0x26, 0xC1, 0x55, 0x80, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0xFF, 0x7A, 0x41, 0x30, 0x75, 0xF6, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0x30, 0x2C, 0xFC, 0x75, 0x09, 0x5A, 0x7D), +}; +static const mbedtls_mpi_uint brainpoolP256r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x07, 0x8C, 0xFF, 0x18, 0xDC, 0xCC, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0xE1, 0xF7, 0x5C, 0x29, 0x16, 0x84, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0x7C, 0xD7, 0xBB, 0xD9, 0xB5, 0x30, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0x44, 0x4B, 0x4A, 0xE9, 0x6C, 0x5C, 0xDC, 0x26), +}; +static const mbedtls_mpi_uint brainpoolP256r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x32, 0xCE, 0x9A, 0xBD, 0x53, 0x44, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x23, 0xBD, 0xE3, 0xE1, 0x27, 0xDE, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xB7, 0x81, 0xFC, 0x2F, 0x48, 0x4B, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x57, 0x7E, 0xCB, 0xB9, 0xAE, 0xD2, 0x8B), +}; +static const mbedtls_mpi_uint brainpoolP256r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x69, 0x04, 0x2F, 0xC7, 0x54, 0x1D, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x8E, 0xED, 0x2D, 0x13, 0x45, 0x77, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x1D, 0x61, 0x14, 0x1A, 0x46, 0xF8, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0xC4, 0xDA, 0xC3, 0x35, 0xF8, 0x7E, 0x54), +}; +static const mbedtls_mpi_uint brainpoolP256r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x56, 0x48, 0x97, 0x82, 0x0E, 0x1E, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0xA6, 0x61, 0xB5, 0xA3, 0x7A, 0x39, 0x8C), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x8D, 0x83, 0x9D, 0x90, 0x0A, 0x66, 0x3E), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xA9, 0xEE, 0xA1, 0xDB, 0x57, 0xFB, 0xA9), +}; + +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint brainpoolP256r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0x32, 0xCE, 0x9A, 0xBD, 0x53, 0x44, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x23, 0xBD, 0xE3, 0xE1, 0x27, 0xDE, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xB7, 0x81, 0xFC, 0x2F, 0x48, 0x4B, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x57, 0x7E, 0xCB, 0xB9, 0xAE, 0xD2, 0x8B), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x69, 0x04, 0x2F, 0xC7, 0x54, 0x1D, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x8E, 0xED, 0x2D, 0x13, 0x45, 0x77, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x1D, 0x61, 0x14, 0x1A, 0x46, 0xF8, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0xC4, 0xDA, 0xC3, 0x35, 0xF8, 0x7E, 0x54), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0xA2, 0xED, 0x52, 0xC9, 0x8C, 0xE3, 0xA5), + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0xC9, 0xC4, 0x87, 0x3F, 0x93, 0x7A, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x12, 0x53, 0x61, 0x3E, 0x76, 0x08, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0x8C, 0x74, 0xF4, 0x08, 0xC3, 0x76, 0x80), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0xDD, 0x09, 0xA6, 0xED, 0xEE, 0xC4, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0xD9, 0xBE, 0x4B, 0xA5, 0xB7, 0x2B, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0x20, 0x12, 0xCA, 0x0A, 0x38, 0x24, 0xAB), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x72, 0x71, 0x90, 0x7A, 0x2E, 0xB7, 0x23), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2C, 0x66, 0xA1, 0x93, 0x10, 0x2A, 0x51, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0x10, 0x11, 0x12, 0xBC, 0xB0, 0xB6, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0x58, 0xD7, 0x0A, 0x84, 0x05, 0xA3, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0x8E, 0x95, 0x61, 0xD3, 0x0B, 0xDF, 0x36), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x92, 0x12, 0x0F, 0x5E, 0x87, 0x70, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0xE9, 0x9B, 0xEB, 0x3A, 0xFB, 0xCF, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0xDC, 0x92, 0xB9, 0xF7, 0x45, 0xD3, 0x06, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0x82, 0x28, 0x65, 0xE1, 0xC5, 0x6C, 0x57, 0x18), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x0E, 0x77, 0x01, 0x81, 0x9E, 0x38, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0xF0, 0xD5, 0xA5, 0x91, 0x2B, 0xDF, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0xEE, 0xB6, 0x25, 0xD6, 0x98, 0xDE, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0xA1, 0x55, 0x63, 0x39, 0xEB, 0xB5, 0x47), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0xD6, 0xB8, 0xE3, 0x13, 0xED, 0x7F, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0xE8, 0xAE, 0x36, 0xB8, 0xCD, 0x19, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x82, 0x83, 0x7A, 0x7B, 0x46, 0x56, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0x60, 0x46, 0x15, 0x5A, 0xAC, 0x99, 0x30), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0x61, 0x50, 0xC6, 0xFF, 0x10, 0x7D, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x51, 0xDF, 0xA9, 0x7D, 0x78, 0x26, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x56, 0x15, 0x9A, 0xF7, 0x01, 0xC1, 0xBB, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x0F, 0xE6, 0x2A, 0xBD, 0x4A, 0x9E, 0x87), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0xF8, 0xD1, 0x77, 0xD2, 0x49, 0xB3, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0x86, 0xFB, 0x9E, 0x1F, 0x5A, 0x60, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0xC4, 0x8D, 0xCD, 0x86, 0x61, 0x2F, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0xF6, 0xB9, 0xAC, 0x37, 0x9D, 0xE9, 0x28), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x77, 0xAA, 0x97, 0x9C, 0x0B, 0x04, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0xA6, 0x60, 0x81, 0xCE, 0x25, 0x13, 0x3E), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0x00, 0xF3, 0xBB, 0x82, 0x99, 0x95, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x5A, 0xCE, 0x90, 0x71, 0x38, 0x2F, 0x10), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x1A, 0xC0, 0x84, 0x27, 0xD6, 0x9D, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x37, 0x52, 0x16, 0x13, 0x0E, 0xCE, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xBF, 0x5A, 0xDB, 0xDB, 0x6E, 0x1E, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0xB7, 0x5E, 0xF9, 0x86, 0xDD, 0x8A, 0x5C), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xAB, 0x5C, 0x8D, 0x1D, 0xF2, 0x2D, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0xC5, 0xF8, 0xF7, 0x1D, 0x96, 0x0B, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x4C, 0xA7, 0x45, 0x20, 0x6A, 0x1E, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x5D, 0xEF, 0xDE, 0xEE, 0x39, 0x44, 0x19), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0x2F, 0x6D, 0x52, 0xC9, 0x58, 0x60, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0xC9, 0x62, 0xCB, 0x38, 0x3C, 0x55, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xA5, 0x09, 0x10, 0x88, 0xDB, 0xE3, 0xBD), + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0xE0, 0x3C, 0xCE, 0x06, 0x0B, 0x4B, 0x5D), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0x1D, 0xB4, 0x10, 0x76, 0x8F, 0xBA, 0x09), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0x70, 0x5A, 0x07, 0xF5, 0x1A, 0x74, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0xE9, 0x94, 0xA8, 0xC0, 0xD5, 0x4A, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0x6D, 0xD4, 0xE8, 0x9B, 0xE9, 0x6D, 0x0E), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x00, 0x32, 0x41, 0x57, 0x84, 0x89, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0xC7, 0x14, 0xEC, 0xE9, 0x27, 0xFF, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0x67, 0x9E, 0xFB, 0xB6, 0xB8, 0x96, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0x4A, 0xE3, 0x97, 0x4B, 0x58, 0xDE, 0x30), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA6, 0x1E, 0x5C, 0xF5, 0x7F, 0xD5, 0xD4, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x08, 0x7A, 0xF1, 0xBD, 0x89, 0xC7, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0xF9, 0x11, 0x1B, 0xF5, 0x3C, 0x6D, 0x8C), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x50, 0xE5, 0x69, 0x1D, 0x59, 0xFC, 0x0C), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x2F, 0xF8, 0x3F, 0xEC, 0x55, 0x99, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0xA7, 0x29, 0x90, 0x43, 0x81, 0x31, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0x18, 0x44, 0x50, 0x5D, 0x76, 0xCB, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0xC5, 0x5B, 0x9A, 0x03, 0xE6, 0x17, 0x39), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x89, 0xFC, 0x55, 0x94, 0x91, 0x6A, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x46, 0x35, 0xF2, 0x3A, 0x42, 0x08, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0xD2, 0x76, 0x49, 0x42, 0x87, 0xD3, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0xEA, 0xA0, 0x52, 0xF1, 0x6A, 0x30, 0x57), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0xB2, 0x57, 0xA3, 0x8A, 0x4D, 0x1B, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0xA3, 0x99, 0x94, 0xB5, 0x3D, 0x64, 0x09), + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0xC3, 0xD7, 0x53, 0xF6, 0x49, 0x1C, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x23, 0x41, 0x4D, 0xFB, 0x7A, 0x5C, 0x53), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0xB8, 0x15, 0x65, 0x5C, 0x85, 0x94, 0xD7), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x37, 0xC7, 0xF8, 0x7E, 0xAE, 0x6C, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x53, 0xD8, 0x11, 0x54, 0x98, 0x44, 0xE3, 0xF1), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x4D, 0xA6, 0x4B, 0x28, 0xF2, 0x57, 0x9E), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0xD0, 0xEB, 0x1E, 0xAA, 0x30, 0xD3, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x9B, 0x4D, 0xA7, 0x73, 0x6E, 0xB6, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x47, 0xF6, 0xED, 0x37, 0xEF, 0x71, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0xB5, 0x49, 0x61, 0x5E, 0x45, 0xF6, 0x4A), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0x0E, 0xB3, 0x84, 0x3A, 0x63, 0x72, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x53, 0x5C, 0xA7, 0xC6, 0x2E, 0xAB, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0x0F, 0x8F, 0x87, 0x50, 0x28, 0xB4, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0x98, 0x4A, 0x98, 0x31, 0x86, 0xCA, 0x51), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0xC9, 0xE2, 0xFD, 0x5D, 0x1F, 0xE8, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0x90, 0x91, 0xC4, 0x84, 0xF0, 0xBA, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x5A, 0xB3, 0x4E, 0xFB, 0xE0, 0x57, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x0B, 0x90, 0xA6, 0xFD, 0x9D, 0x8E, 0x02), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x41, 0x8F, 0x31, 0xFA, 0x5A, 0xF6, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xE9, 0xE3, 0xF6, 0xE0, 0x4A, 0xE7, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0x4E, 0xCD, 0xA2, 0x22, 0x14, 0xD4, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0xED, 0x21, 0xB7, 0x0F, 0x53, 0x10, 0x17), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0x06, 0x24, 0x2C, 0x4E, 0xD1, 0x1E, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0x3F, 0xC1, 0x9F, 0xAB, 0xF0, 0x37, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0x03, 0x5E, 0x12, 0xCE, 0x83, 0x1B, 0x2A, 0x18), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x65, 0xCF, 0xE8, 0x5C, 0xA5, 0xA2, 0x70), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0x86, 0x76, 0x3A, 0x94, 0xF6, 0x1D, 0xC1), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0xDA, 0xC9, 0xA6, 0x29, 0x93, 0x15, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x61, 0x6A, 0x7D, 0xC7, 0xA9, 0xF3, 0x76), + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0x03, 0x71, 0xA2, 0x15, 0xCE, 0x50, 0x72), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0xD0, 0xA8, 0x1E, 0x91, 0xC4, 0x4F, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0x4B, 0x7E, 0xD7, 0x71, 0x58, 0x7E, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x45, 0xAF, 0x2A, 0x18, 0x93, 0x95, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x8F, 0xC7, 0xFA, 0x4C, 0x7A, 0x86, 0x54), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0xAF, 0x68, 0x3A, 0x23, 0xC1, 0x2E, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0x89, 0x50, 0x11, 0x67, 0x39, 0xB9, 0xAF, 0x48), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x86, 0xAA, 0x1E, 0x88, 0x21, 0x29, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0x28, 0xA4, 0x9D, 0x89, 0xA9, 0x9A, 0x10), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0xBA, 0x04, 0x67, 0xB7, 0x01, 0x40, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0xE9, 0x09, 0xA3, 0xCA, 0xA6, 0x37, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x97, 0xA8, 0xB6, 0x3C, 0xEE, 0x90, 0x3D), + MBEDTLS_BYTES_TO_T_UINT_8(0xDC, 0xED, 0xC4, 0xF7, 0xC3, 0x95, 0xEC, 0x85), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x84, 0xBD, 0xEB, 0xD5, 0x64, 0xBB, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x9B, 0xE2, 0x28, 0x50, 0xC2, 0x72, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0xF2, 0x74, 0xD1, 0x26, 0xBF, 0x32, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0xCB, 0xAF, 0x72, 0xDB, 0x6D, 0x30, 0x98), +}; +static const mbedtls_mpi_uint brainpoolP256r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0x50, 0x85, 0xF4, 0x2B, 0x48, 0xC1, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0x28, 0xBB, 0x11, 0xBA, 0x5B, 0x22, 0x6C), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0xA1, 0xE5, 0x5C, 0xC9, 0x1D, 0x44, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0xE8, 0xE6, 0x6F, 0xBB, 0xC1, 0x81, 0x7F), +}; +static const mbedtls_ecp_point brainpoolP256r1_T[16] = { + ECP_POINT_INIT_XY_Z1(brainpoolP256r1_T_0_X, brainpoolP256r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_1_X, brainpoolP256r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_2_X, brainpoolP256r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_3_X, brainpoolP256r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_4_X, brainpoolP256r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_5_X, brainpoolP256r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_6_X, brainpoolP256r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_7_X, brainpoolP256r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_8_X, brainpoolP256r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_9_X, brainpoolP256r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_10_X, brainpoolP256r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_11_X, brainpoolP256r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_12_X, brainpoolP256r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_13_X, brainpoolP256r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_14_X, brainpoolP256r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP256r1_T_15_X, brainpoolP256r1_T_15_Y), +}; +#else +#define brainpoolP256r1_T NULL +#endif + +#endif /* MBEDTLS_ECP_DP_BP256R1_ENABLED */ + +/* + * Domain parameters for brainpoolP384r1 (RFC 5639 3.6) + */ +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) +static const mbedtls_mpi_uint brainpoolP384r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x53, 0xEC, 0x07, 0x31, 0x13, 0x00, 0x47, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x1A, 0x1D, 0x90, 0x29, 0xA7, 0xD3, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0x23, 0x11, 0xB7, 0x7F, 0x19, 0xDA, 0xB1, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x56, 0x54, 0xED, 0x09, 0x71, 0x2F, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0x41, 0xE6, 0x50, 0x7E, 0x6F, 0x5D, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x6D, 0x38, 0xA3, 0x82, 0x1E, 0xB9, 0x8C), +}; +static const mbedtls_mpi_uint brainpoolP384r1_a[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x28, 0xCE, 0x22, 0xDD, 0xC7, 0xA8, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0xD4, 0x3A, 0x50, 0x4A, 0x81, 0xA5, 0x8A), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0xF9, 0x91, 0xBA, 0xEF, 0x65, 0x91, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x27, 0xB2, 0x4F, 0x8E, 0xA2, 0xBE, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0xAF, 0x05, 0xCE, 0x0A, 0x08, 0x72, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0x15, 0x8C, 0x3D, 0xC6, 0x82, 0xC3, 0x7B), +}; +static const mbedtls_mpi_uint brainpoolP384r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x4C, 0x50, 0xFA, 0x96, 0x86, 0xB7, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0xC9, 0xDB, 0x95, 0x02, 0x39, 0xB4, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0x62, 0xEB, 0x3E, 0xA5, 0x0E, 0x88, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0xA6, 0xD2, 0xDC, 0x07, 0xE1, 0x7D, 0xB7, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x44, 0xF0, 0x16, 0x54, 0xB5, 0x39, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x28, 0xCE, 0x22, 0xDD, 0xC7, 0xA8, 0x04), +}; +static const mbedtls_mpi_uint brainpoolP384r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xAF, 0xD4, 0x47, 0xE2, 0xB2, 0x87, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0x46, 0xD6, 0x36, 0x34, 0xE0, 0x26, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x10, 0xBD, 0x0C, 0xFE, 0xCA, 0x7F, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x4F, 0xF1, 0x7E, 0xE7, 0xA3, 0x47, 0x88), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x3F, 0xC1, 0xB7, 0x81, 0x3A, 0xA6, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x45, 0xCF, 0x68, 0xF0, 0x64, 0x1C, 0x1D), +}; +static const mbedtls_mpi_uint brainpoolP384r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x53, 0x3C, 0x26, 0x41, 0x03, 0x82, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x81, 0x91, 0x77, 0x21, 0x46, 0x46, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x29, 0x91, 0xF9, 0x4F, 0x05, 0x9C, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x58, 0xEC, 0xFE, 0x29, 0x0B, 0xB7, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0xD5, 0xCF, 0x95, 0x8E, 0xEB, 0xB1, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0xC2, 0xF9, 0x20, 0x75, 0x1D, 0xBE, 0x8A), +}; +static const mbedtls_mpi_uint brainpoolP384r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x65, 0x04, 0xE9, 0x02, 0x32, 0x88, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0xC3, 0x7F, 0x6B, 0xAF, 0xB6, 0x3A, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x25, 0x04, 0xAC, 0x6C, 0x6E, 0x16, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0x56, 0x54, 0xED, 0x09, 0x71, 0x2F, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0x41, 0xE6, 0x50, 0x7E, 0x6F, 0x5D, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x6D, 0x38, 0xA3, 0x82, 0x1E, 0xB9, 0x8C), +}; + +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint brainpoolP384r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xAF, 0xD4, 0x47, 0xE2, 0xB2, 0x87, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0x46, 0xD6, 0x36, 0x34, 0xE0, 0x26, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x10, 0xBD, 0x0C, 0xFE, 0xCA, 0x7F, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x4F, 0xF1, 0x7E, 0xE7, 0xA3, 0x47, 0x88), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x3F, 0xC1, 0xB7, 0x81, 0x3A, 0xA6, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x45, 0xCF, 0x68, 0xF0, 0x64, 0x1C, 0x1D), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x53, 0x3C, 0x26, 0x41, 0x03, 0x82, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x81, 0x91, 0x77, 0x21, 0x46, 0x46, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x29, 0x91, 0xF9, 0x4F, 0x05, 0x9C, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x58, 0xEC, 0xFE, 0x29, 0x0B, 0xB7, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0xD5, 0xCF, 0x95, 0x8E, 0xEB, 0xB1, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0xC2, 0xF9, 0x20, 0x75, 0x1D, 0xBE, 0x8A), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0xD8, 0x8A, 0x54, 0x41, 0xD6, 0x6B, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0x3B, 0xF1, 0x22, 0xFD, 0x2D, 0x4B, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x55, 0xE3, 0x33, 0xF0, 0x73, 0x52, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x3F, 0x30, 0x26, 0xCA, 0x7F, 0x52, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0xD3, 0x6E, 0x17, 0x9B, 0xD5, 0x2A, 0x4A, 0x31), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0xDA, 0x6B, 0xE5, 0x03, 0x07, 0x1D, 0x2E), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0x7A, 0xAF, 0x98, 0xE3, 0xA4, 0xF6, 0x19), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0x7D, 0xFE, 0x51, 0x40, 0x3B, 0x47, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x88, 0xEC, 0xC4, 0xE2, 0x8F, 0xCB, 0xA4), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0xE2, 0x88, 0x2D, 0x4E, 0x50, 0xEB, 0x9A), + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0x54, 0x94, 0x5E, 0xF4, 0x7F, 0x3A, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0x07, 0x1C, 0xE1, 0xBD, 0x0F, 0xF8, 0x63), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x92, 0x28, 0x2E, 0x32, 0x04, 0xB1, 0x4D), + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0x82, 0x44, 0x43, 0x76, 0x0D, 0x55, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0xE3, 0xFF, 0x89, 0x46, 0xDE, 0x4E, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0x22, 0xBB, 0x67, 0x1A, 0x81, 0xEE, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0x54, 0xE2, 0x7A, 0xAE, 0xDA, 0x2C, 0xD0), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x9A, 0x90, 0xAA, 0x6E, 0x8B, 0xCC, 0x5F), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0x40, 0xAC, 0xED, 0x7D, 0x37, 0x87, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0xF8, 0xB1, 0x80, 0x4C, 0x8C, 0x04, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x98, 0x2C, 0xAD, 0x30, 0x69, 0x35, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x2E, 0x00, 0x2F, 0x44, 0x8C, 0xF0, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0x58, 0x07, 0xD7, 0xCD, 0x60, 0xA1, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0xFB, 0x7B, 0x03, 0x05, 0x5E, 0x79, 0x73), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0x17, 0xCE, 0x38, 0x4B, 0x5E, 0x5B, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0x60, 0x0E, 0x0A, 0x61, 0x9D, 0x7C, 0x62, 0x08), + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0xF0, 0x98, 0x71, 0x7F, 0x17, 0x26, 0xD7), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0xD3, 0xFA, 0x3C, 0xF0, 0x70, 0x07, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x47, 0x5C, 0x09, 0x43, 0xB7, 0x65, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0xA9, 0xA7, 0x3E, 0xFA, 0xF3, 0xEC, 0x22), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x78, 0x22, 0x2B, 0x58, 0x71, 0xFA, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x30, 0xCE, 0x6A, 0xB3, 0xB0, 0x4F, 0x83), + MBEDTLS_BYTES_TO_T_UINT_8(0xCF, 0x95, 0x20, 0xA9, 0x23, 0xC2, 0x65, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0xCF, 0x03, 0x5B, 0x8A, 0x80, 0x44, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0xF8, 0x91, 0xF7, 0xD5, 0xED, 0xEA, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x5B, 0x16, 0x10, 0x25, 0xAC, 0x2A, 0x17), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0xEC, 0xDC, 0xC4, 0x7B, 0x8C, 0x6B, 0xE9), + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0xBB, 0x1C, 0xD3, 0x5A, 0xEE, 0xD9, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x5D, 0x30, 0x5E, 0xF7, 0xB2, 0x41, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0xCE, 0x0F, 0x1A, 0xC6, 0x41, 0x64, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0x18, 0xE1, 0xE3, 0x82, 0x15, 0x66, 0x4B), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0xE2, 0x24, 0x04, 0x72, 0x39, 0xA0, 0x7C), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0x51, 0xA2, 0x58, 0x88, 0x62, 0xE1, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0xD2, 0x65, 0x14, 0xE9, 0x4C, 0x82, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0xDC, 0xE1, 0xAC, 0x87, 0xAE, 0x31, 0x1A, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x85, 0x4F, 0x96, 0x1E, 0x85, 0x7A, 0xC3, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x86, 0xBB, 0xF0, 0xC0, 0x9D, 0x08, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0x53, 0x03, 0x09, 0x80, 0x91, 0xEF, 0x68), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0xD7, 0xAF, 0x6F, 0x69, 0x7B, 0x88, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0x13, 0xE4, 0x30, 0xA2, 0x47, 0xB5, 0xC1), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0xD2, 0xC0, 0xDD, 0x8A, 0x1C, 0x3C, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x8C, 0xB3, 0x4C, 0xBA, 0x8B, 0x6D, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0xC7, 0xA1, 0xA8, 0x6E, 0x3C, 0x4F, 0xF1), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x4A, 0x97, 0xC8, 0x03, 0x6F, 0x01, 0x82), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x18, 0x12, 0xA9, 0x39, 0xD5, 0x22, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0xA7, 0xC0, 0xBD, 0x9D, 0x8D, 0x78, 0x38), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0xB3, 0xD0, 0x7F, 0xDF, 0xD0, 0x30, 0xDE), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x25, 0x73, 0x96, 0xEC, 0xA8, 0x1D, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0xD1, 0x65, 0x66, 0xDC, 0xD9, 0xCF, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0xED, 0x7B, 0x37, 0xAD, 0xE2, 0xBE, 0x2D), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x79, 0x42, 0x6A, 0x07, 0x66, 0xB1, 0xBD), + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0x53, 0x62, 0x65, 0x92, 0x09, 0x4C, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0xAF, 0xC3, 0x03, 0xF6, 0xF4, 0x2D, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0xCA, 0x41, 0xD9, 0xA2, 0x69, 0x9B, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0xB2, 0xA6, 0x8D, 0xE1, 0xAA, 0x61, 0x76), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0xBA, 0x4D, 0x12, 0xB6, 0xBE, 0xF3, 0x7E), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0xD9, 0x92, 0x22, 0x07, 0xCE, 0xC9, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0xA1, 0x7C, 0x91, 0xDB, 0x32, 0xF7, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x49, 0x4B, 0x6D, 0xFB, 0xD9, 0x70, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0xFB, 0x4E, 0x4C, 0x5E, 0x66, 0x81, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0xB3, 0xE1, 0x00, 0xB7, 0xD9, 0xCC, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0x36, 0x8B, 0xC4, 0x39, 0x20, 0xFD, 0x30), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x1F, 0x60, 0x03, 0xBB, 0xD7, 0x60, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x3C, 0x62, 0xDD, 0x71, 0x95, 0xE9, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x5B, 0x7A, 0x5F, 0x68, 0x81, 0xC5, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xAF, 0xB5, 0xB9, 0x98, 0x42, 0x28, 0xA5), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0x29, 0x8E, 0x11, 0x49, 0xB4, 0xD7, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x3E, 0xD2, 0x30, 0xA1, 0xBA, 0xCA, 0x03), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x37, 0x64, 0x44, 0x2F, 0x03, 0xE5, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0x42, 0xBC, 0xFF, 0xA2, 0x1A, 0x5F, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0x04, 0xAB, 0x04, 0xE0, 0x24, 0xAD, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0x45, 0x17, 0x67, 0x1F, 0x3E, 0x53, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0x0F, 0xB3, 0x1B, 0x57, 0x54, 0xC2, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xD3, 0xF8, 0xC4, 0x1B, 0x9B, 0xFA, 0x30), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x90, 0xFD, 0xFB, 0xCA, 0x49, 0x38, 0x4E), + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0xCF, 0xC6, 0xDD, 0xF0, 0xFF, 0x8C, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0x69, 0x9D, 0xBD, 0x5F, 0x33, 0xE9, 0xB4), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x19, 0x82, 0x3D, 0xAC, 0x1C, 0x40, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0xC7, 0x02, 0x46, 0x14, 0x77, 0x00, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x05, 0xF2, 0x77, 0x3A, 0x66, 0x5C, 0x39), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xE6, 0x17, 0xDE, 0xB2, 0xA1, 0xE5, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0x71, 0xEC, 0x9D, 0xD8, 0xF5, 0xD4, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0xC6, 0x42, 0x5E, 0xE7, 0x18, 0xBA, 0xD0), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0x21, 0x68, 0x5A, 0x26, 0xFB, 0xD7, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x00, 0x5C, 0xBA, 0x8A, 0x34, 0xEC, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0x9C, 0x3C, 0xAF, 0x53, 0xE8, 0x65, 0x35), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0xEF, 0x28, 0xDC, 0x67, 0x05, 0xC8, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x78, 0xC3, 0x85, 0x49, 0xA0, 0xBC, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0x3E, 0x2D, 0xA0, 0xCF, 0xD4, 0x7A, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0x93, 0xFE, 0x60, 0xB3, 0x6E, 0x99, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x62, 0xAD, 0x04, 0xE7, 0x49, 0xAF, 0x5E, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x7A, 0xED, 0xA6, 0x9E, 0x18, 0x09, 0x31), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x05, 0x94, 0x44, 0xDC, 0xB8, 0x85, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0xB7, 0x37, 0xC2, 0x50, 0x75, 0x15, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0xC6, 0x0F, 0xB2, 0xA9, 0x91, 0x3E, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0x81, 0xAD, 0x25, 0xA1, 0x26, 0x73, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0xF1, 0xD1, 0x61, 0x7C, 0x76, 0x8F, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0xDB, 0x4A, 0xFF, 0x14, 0xA7, 0x48, 0x0B), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0x73, 0xC6, 0xC2, 0xCC, 0xF1, 0x57, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0xED, 0x73, 0x27, 0x70, 0x82, 0xB6, 0x5E), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0xBA, 0xAC, 0x3A, 0xCF, 0xF4, 0xEA, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0xD6, 0xB1, 0x8F, 0x0E, 0x08, 0x2C, 0x5E), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xE3, 0x8F, 0x2F, 0x0E, 0xA1, 0xF3, 0x07), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0xF5, 0x7C, 0x9B, 0x29, 0x0A, 0xF6, 0x28), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0xEE, 0x17, 0x47, 0x34, 0x15, 0xA3, 0xAF), + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0xBE, 0x88, 0x48, 0xE7, 0xA2, 0xBB, 0xDE), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0xAD, 0xDC, 0x65, 0x61, 0x37, 0x0F, 0xC1), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x67, 0xAD, 0xA2, 0x3A, 0x1C, 0x91, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x07, 0x0C, 0x3A, 0x41, 0x6E, 0x13, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0xBD, 0x7E, 0xED, 0xAA, 0x14, 0xDD, 0x61), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC3, 0xDC, 0x20, 0x01, 0x72, 0x11, 0x48, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xC4, 0x7B, 0xF8, 0x62, 0x3D, 0xF0, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0xC2, 0x3D, 0x2E, 0x52, 0xA3, 0x4A, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0xE2, 0x53, 0x46, 0x5E, 0x21, 0xF8, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0xC7, 0x8F, 0xA9, 0x26, 0x42, 0x32, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0xA6, 0xA0, 0x8D, 0x4B, 0x9A, 0x19, 0x03), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xAB, 0x6D, 0x1E, 0xFB, 0xEE, 0x60, 0x0C), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x56, 0x3C, 0xC5, 0x5D, 0x10, 0x79, 0x1C), + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0xBC, 0x41, 0x9F, 0x71, 0xEF, 0x02, 0xF9), + MBEDTLS_BYTES_TO_T_UINT_8(0xA2, 0x36, 0xC4, 0xD0, 0x88, 0x9B, 0x32, 0xFC), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0xD4, 0x5D, 0x17, 0x39, 0xE6, 0x22, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x7B, 0x26, 0x01, 0xCE, 0xBE, 0x4A, 0x9C, 0x27), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0x6D, 0x11, 0xCA, 0x6C, 0x5A, 0x93, 0x0C), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0x96, 0x26, 0xAF, 0x2F, 0xE4, 0x30, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0xC1, 0x4C, 0xC6, 0x30, 0x1F, 0x5C, 0x04), + MBEDTLS_BYTES_TO_T_UINT_8(0x59, 0xB3, 0xE8, 0xFC, 0x35, 0xEB, 0x63, 0x6C), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0x1D, 0xCA, 0xFC, 0x50, 0x36, 0x4B, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x0E, 0x23, 0x5B, 0xAF, 0xEB, 0x2D, 0x31), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0x88, 0xB6, 0xD7, 0x74, 0x4A, 0x23, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0x66, 0xE2, 0xBB, 0x29, 0xA6, 0x4F, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0x6F, 0x7E, 0x68, 0x6E, 0xA0, 0x14, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0x3B, 0x73, 0xD4, 0xE8, 0xAB, 0x5B, 0xF6, 0x0D), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0xE0, 0x3C, 0x24, 0x00, 0x95, 0xE9, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x0D, 0x4F, 0x81, 0xD0, 0xF2, 0x3F, 0x00), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0x1D, 0xCD, 0x78, 0x39, 0xC4, 0x6B, 0xD9), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0x45, 0xC7, 0xB8, 0x2F, 0xAA, 0x5D, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0x8C, 0x6E, 0xA3, 0x24, 0xB2, 0xDB, 0x4B), + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0x2D, 0xD9, 0xF1, 0xC7, 0x9B, 0x8A, 0xAF), + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0xE1, 0x2C, 0xB9, 0x40, 0x37, 0x91, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0x2C, 0xB5, 0x23, 0x03, 0x2B, 0xAF, 0x2F), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0x9D, 0x5A, 0x20, 0x10, 0xA9, 0x84, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x30, 0x89, 0x20, 0x13, 0xE9, 0xB2, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x6E, 0x52, 0xEB, 0x03, 0x18, 0x1F, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x9E, 0x1C, 0x35, 0x87, 0x92, 0x69, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0xC9, 0x88, 0xAF, 0xC6, 0x6C, 0x83, 0x72), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0xD5, 0x7A, 0x54, 0x34, 0x99, 0xB6, 0x6F), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0xAD, 0x45, 0x9B, 0x4B, 0x41, 0x4D, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0x5D, 0xAB, 0x7F, 0x35, 0x34, 0xE9, 0x29), + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0xBE, 0x78, 0x34, 0x44, 0xF3, 0x4A, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0xDE, 0xE3, 0xC4, 0xEE, 0x0B, 0xF9, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0x86, 0x16, 0x48, 0x32, 0xB8, 0x74, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0xEE, 0x7C, 0xBA, 0xBD, 0x81, 0xE3, 0x55), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x6A, 0xFA, 0x84, 0xDA, 0xB8, 0xD5, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0xB2, 0x9F, 0x8A, 0xD5, 0x1B, 0x2E, 0x1A, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x0C, 0x61, 0xE2, 0xFF, 0x5B, 0xE6, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0x62, 0xC1, 0x87, 0x53, 0x1B, 0x92, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x90, 0x00, 0xD1, 0x6A, 0x0C, 0x0E, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0x2E, 0xB5, 0x3B, 0x44, 0xB5, 0xA0, 0x78), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0x5D, 0x02, 0x58, 0xB5, 0xBE, 0x45, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0xEF, 0x8E, 0x90, 0x4D, 0x2A, 0x32, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0x48, 0x99, 0x75, 0x5C, 0x0A, 0x33, 0x8F, 0x36), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0x6C, 0x95, 0xD4, 0x1F, 0xF3, 0xEB, 0xDA), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0xE4, 0x4C, 0x91, 0x20, 0xF3, 0x25, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x95, 0xEB, 0x29, 0x6F, 0x20, 0x34, 0x81), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0x15, 0xE5, 0x13, 0x7E, 0x64, 0x8B, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0xBC, 0x0D, 0x18, 0x7E, 0x37, 0x9E, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x82, 0x20, 0xF7, 0x2D, 0x7A, 0x77, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x29, 0xA2, 0xDB, 0x7A, 0xE6, 0x6F, 0xA5), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0xC6, 0x50, 0x5C, 0xBC, 0xE6, 0x4F, 0xBD), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x9F, 0xD5, 0xE8, 0xC5, 0x3D, 0xB7, 0x30), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_16_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0x03, 0x55, 0x10, 0xDB, 0xA6, 0x8B, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0x17, 0xAE, 0x78, 0xC9, 0x1D, 0x43, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0x35, 0x49, 0xD4, 0x47, 0x84, 0x8D, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0x95, 0x2F, 0xEA, 0xBC, 0xB4, 0x18, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x48, 0xAE, 0x89, 0xF5, 0x65, 0x3D, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0xF2, 0x2B, 0x20, 0xD1, 0x75, 0x50, 0x63), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_16_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0xE6, 0x5C, 0x2C, 0xE0, 0x7D, 0xDF, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0x07, 0x3E, 0xCE, 0x9F, 0x18, 0xB6, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0xF8, 0xF0, 0xD5, 0xFA, 0x42, 0x1D, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x6C, 0x1D, 0x03, 0xC9, 0x0E, 0x2B, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0x18, 0x52, 0xA5, 0xB4, 0x63, 0xE1, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x0A, 0xD9, 0xC4, 0xFD, 0x16, 0x60, 0x54), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_17_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0x7D, 0xDE, 0xDF, 0x4B, 0x4A, 0xB0, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x4E, 0x8C, 0x94, 0xC1, 0xE2, 0x85, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x4F, 0xF0, 0xEA, 0xB5, 0x9B, 0x70, 0xEF, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x56, 0xC2, 0x39, 0x5D, 0xF3, 0x2C, 0xD9, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0x1C, 0x2E, 0xCC, 0x2F, 0x54, 0x87, 0x80), + MBEDTLS_BYTES_TO_T_UINT_8(0xB0, 0x72, 0xC7, 0xB5, 0x50, 0xA3, 0x84, 0x77), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_17_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xD1, 0xAF, 0xA9, 0xB4, 0x8B, 0x5D, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0xF6, 0x52, 0x8A, 0xC3, 0x56, 0xA5, 0x5E), + MBEDTLS_BYTES_TO_T_UINT_8(0x3B, 0x52, 0xFF, 0xEA, 0x05, 0x42, 0x77, 0x83), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x08, 0x90, 0x72, 0x86, 0xC4, 0xC3, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0x15, 0xF8, 0xF1, 0x16, 0x67, 0xC6, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0x87, 0xAC, 0x8F, 0x71, 0xEC, 0x83, 0x81), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_18_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0xE1, 0xE6, 0x2D, 0x0E, 0x11, 0xA1, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0xE2, 0xA8, 0x32, 0xE6, 0xE3, 0x83, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x56, 0xE5, 0xCD, 0xB7, 0x2B, 0x67, 0x6F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0xED, 0xC9, 0x65, 0x6D, 0x87, 0xE1, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x8E, 0xFD, 0x9A, 0x53, 0x0E, 0xFA, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0x4C, 0x4A, 0xE2, 0x23, 0x84, 0xFA, 0x01), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_18_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0xFE, 0x49, 0x81, 0xD1, 0x3E, 0xF4, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0x72, 0xE0, 0xEF, 0x0D, 0xB8, 0x3E, 0x6F), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0x00, 0x0F, 0x5F, 0xCE, 0x60, 0x72, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xCC, 0xD8, 0x03, 0x07, 0x6E, 0x5A, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x3A, 0x35, 0x50, 0x4E, 0x1F, 0xCA, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0xEA, 0x88, 0x55, 0xBD, 0x6E, 0x05, 0x7F), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_19_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0x6D, 0xF1, 0x97, 0xA6, 0x69, 0x39, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0x41, 0x99, 0xFF, 0x3B, 0xA1, 0x26, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0x2F, 0x95, 0x80, 0x12, 0x4A, 0x1B, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xBF, 0x51, 0xAA, 0xAE, 0x2D, 0xDA, 0xCF), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0x1C, 0xB3, 0x52, 0x36, 0x49, 0xD4, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0xC1, 0x1F, 0x3A, 0xD3, 0x3E, 0x5C, 0x1A), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_19_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0x51, 0xF7, 0x2B, 0xC8, 0xA9, 0xA7, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0x4E, 0x7F, 0x98, 0x41, 0x66, 0xB0, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x1D, 0xC0, 0x42, 0xCD, 0xF8, 0xC3, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x41, 0x91, 0x7D, 0xCC, 0x8B, 0xCC, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xAE, 0x76, 0xED, 0x56, 0x18, 0xC5, 0xAB), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0x6A, 0x06, 0xA3, 0x7F, 0x65, 0x10, 0x1F), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_20_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0xEC, 0x3C, 0x05, 0x05, 0xCA, 0xF6, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0x48, 0xCD, 0x02, 0x51, 0x12, 0x16, 0x3C, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0xEB, 0xB3, 0x43, 0x7B, 0xDD, 0xB2, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x59, 0x90, 0x41, 0xDB, 0xE4, 0xF5, 0x91), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x0E, 0x18, 0x2A, 0x5A, 0x83, 0x7C, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0x37, 0xA1, 0x0D, 0xF1, 0x2F, 0x63, 0x79), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_20_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0xC0, 0xFA, 0x6F, 0x1F, 0x67, 0xCF, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x34, 0x45, 0xBB, 0xF4, 0xF9, 0x9B, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0x69, 0xFE, 0x67, 0x1D, 0x64, 0x8F, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x39, 0xBF, 0xD8, 0xB3, 0xC7, 0xAD, 0x8A), + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0x93, 0xFF, 0xF3, 0x28, 0xFA, 0x39, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0xF9, 0xC3, 0x85, 0x26, 0x7A, 0x88, 0x89), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_21_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0xD5, 0x79, 0xD8, 0x11, 0xDE, 0xEB, 0x4E), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x46, 0xA4, 0x6A, 0xDA, 0x74, 0x34, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0xBD, 0xD3, 0xF5, 0x14, 0xEE, 0xFE, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x4C, 0xA3, 0x71, 0x43, 0x65, 0xF8, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x6C, 0x35, 0xFA, 0x90, 0x25, 0xD8, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x34, 0x84, 0x96, 0xA1, 0x43, 0x03, 0x4D), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_21_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x3B, 0x3B, 0x2F, 0xCA, 0x59, 0xF2, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xCD, 0x48, 0x24, 0x74, 0xD8, 0x72, 0x90, 0xA3), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x42, 0x74, 0x8C, 0x6F, 0x52, 0x19, 0x3D), + MBEDTLS_BYTES_TO_T_UINT_8(0x40, 0x9E, 0x41, 0x63, 0x68, 0x78, 0x4C, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x53, 0x94, 0xB6, 0x6B, 0x38, 0x52, 0xA8, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0x30, 0x25, 0x93, 0xA1, 0x6F, 0x6E, 0x68), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_22_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x2F, 0x4B, 0x64, 0x79, 0x50, 0xFF, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0x36, 0xED, 0x57, 0x39, 0x3B, 0xE7, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x85, 0xEA, 0x35, 0xD6, 0xC0, 0xA0, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x89, 0x3A, 0xCC, 0x22, 0x1C, 0x46, 0x02), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x7A, 0xB0, 0xA1, 0x1B, 0x69, 0x62, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0xB8, 0x8A, 0x6C, 0x18, 0x85, 0x0D, 0x88), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_22_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0xB6, 0x50, 0xE9, 0x4E, 0x7F, 0xE8, 0x07), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x5B, 0x5C, 0xD1, 0x4B, 0x11, 0x9A, 0xD8), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x25, 0x56, 0x74, 0x51, 0x9C, 0xEC, 0x9C), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x7F, 0xB6, 0x8A, 0xCB, 0x3A, 0x10, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0x60, 0x33, 0x07, 0x01, 0xE9, 0x49, 0x59, 0xE6), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0xA5, 0x2E, 0xF2, 0xBA, 0x32, 0x63, 0x44), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_23_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x06, 0x0B, 0xA5, 0x44, 0x27, 0x7F, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x74, 0xAC, 0x0F, 0xCC, 0x4F, 0x13, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0xB1, 0xBF, 0x97, 0x49, 0xA5, 0x1C, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0x64, 0x68, 0x7B, 0x0F, 0xCC, 0x77, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x39, 0xF9, 0x4E, 0x84, 0x9C, 0xF6, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xCF, 0x6D, 0xE2, 0xA1, 0x2D, 0xF9, 0x2B), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_23_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0xC4, 0x90, 0x57, 0x31, 0x01, 0x05, 0x5E), + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x1E, 0xBB, 0xBF, 0x98, 0xA4, 0x7C, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0x89, 0xE3, 0xA0, 0xB2, 0xCD, 0x39, 0x9A, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x34, 0x60, 0x7A, 0x89, 0x98, 0xB5, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0x20, 0x3D, 0x3A, 0x04, 0x8F, 0x5A, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0x26, 0xB6, 0x49, 0x09, 0x9C, 0x0F, 0x59), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_24_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x66, 0xD2, 0x38, 0x2A, 0x62, 0x81, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0xC8, 0x20, 0x5E, 0x28, 0xA3, 0x81, 0xA7), + MBEDTLS_BYTES_TO_T_UINT_8(0x20, 0x31, 0xA4, 0xF1, 0xEA, 0x7D, 0x87, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0x8F, 0x2C, 0x99, 0x09, 0x6F, 0x63, 0xEB, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x73, 0x76, 0xDA, 0x1A, 0x06, 0xBE, 0xDE, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x09, 0x2E, 0x75, 0x39, 0x30, 0x2D, 0x42), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_24_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x9B, 0xC1, 0x5A, 0x17, 0xC3, 0x8C, 0x31), + MBEDTLS_BYTES_TO_T_UINT_8(0x58, 0x8D, 0x94, 0x4D, 0x3D, 0xAB, 0x60, 0xD4), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xFD, 0x1E, 0x0F, 0x43, 0xAE, 0x9D, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0xF2, 0xF3, 0x20, 0x1B, 0xAA, 0xB7, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0x5B, 0xA4, 0xF4, 0x90, 0x3B, 0xE3, 0x71), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0x78, 0x72, 0xBD, 0x65, 0x09, 0x0B, 0x01), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_25_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x37, 0x2A, 0x6C, 0x16, 0x4F, 0x64, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0xCE, 0xA3, 0x90, 0xB4, 0x9A, 0xBC, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x38, 0x55, 0x63, 0x1D, 0x3A, 0x6E, 0x18), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0xB4, 0xAA, 0x99, 0x22, 0x45, 0x89, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0x7C, 0x8C, 0xA6, 0x3D, 0xA7, 0x3E, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x06, 0x42, 0xDC, 0xA6, 0xE3, 0xC6, 0x12), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_25_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x8C, 0x3D, 0x5D, 0x47, 0x31, 0x7C, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0x85, 0xEE, 0x46, 0x7E, 0x13, 0x04, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0x3C, 0x8B, 0x43, 0x2E, 0x74, 0xF5, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x88, 0x8E, 0x07, 0x29, 0x08, 0x03, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0x9B, 0x89, 0xEB, 0x08, 0xE8, 0x43, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0x07, 0x67, 0xFD, 0xD9, 0x73, 0x6F, 0x18), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_26_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0xEB, 0x21, 0x8D, 0x98, 0x43, 0x74, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x88, 0xCC, 0x14, 0xD8, 0x08, 0xBB, 0xA6, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0x98, 0xF2, 0x6A, 0x18, 0xC3, 0xDD, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0x38, 0x91, 0xA0, 0x03, 0xF2, 0x04, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x7A, 0xAF, 0xE8, 0xFD, 0xFB, 0x13, 0x70, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x93, 0x87, 0x98, 0x4A, 0xE0, 0x00, 0x12), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_26_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x2E, 0x69, 0x9C, 0xA2, 0x2D, 0x03, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0xFE, 0xF3, 0xB9, 0xC1, 0x85, 0x2A, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0xFD, 0x86, 0xB1, 0xCD, 0xBF, 0x41, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0xD8, 0x9A, 0x21, 0xF3, 0xFE, 0xCB, 0xF1), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0x78, 0x04, 0x60, 0xB7, 0xA9, 0xA2, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x1E, 0x66, 0x2A, 0x54, 0x51, 0xBD, 0x8B), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_27_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x16, 0x36, 0xEF, 0x61, 0x2D, 0xEE, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0x5F, 0x88, 0xA0, 0x13, 0x12, 0xF7, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0xA9, 0xC6, 0xAD, 0x4A, 0x4A, 0x07, 0x01, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0xB8, 0x74, 0xB1, 0x4F, 0xEB, 0xBD, 0xD5, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0xF9, 0x71, 0xA2, 0x06, 0x4F, 0xD7, 0xBC), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x8B, 0x4D, 0x48, 0xE0, 0x98, 0xFB, 0x6A), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_27_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0xBA, 0x10, 0xA3, 0x0D, 0x52, 0xAC, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0xD0, 0xE0, 0x36, 0xE6, 0x07, 0x3A, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x80, 0xF0, 0xAA, 0x49, 0x22, 0x4B, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xC7, 0xAB, 0x1C, 0x89, 0xCD, 0x24, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x82, 0x2A, 0xFC, 0xB3, 0x6D, 0x45, 0x96, 0x49), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0xE4, 0xDB, 0x52, 0x3F, 0xC4, 0xB4, 0x19), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_28_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5B, 0xCC, 0xC8, 0x7F, 0xBB, 0x6B, 0x87, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0x21, 0x3C, 0x69, 0x7D, 0x38, 0x57, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0x4C, 0x18, 0x3C, 0x53, 0xA5, 0x48, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0xC3, 0x64, 0x45, 0xDB, 0xC4, 0x6D, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0xCC, 0xD1, 0xBB, 0x17, 0xB8, 0x34, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x69, 0x71, 0xFA, 0xA0, 0x28, 0x4A, 0x3D), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_28_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0xE8, 0x9E, 0x39, 0xEA, 0x8D, 0x38, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x9C, 0xBB, 0xCD, 0x80, 0x1A, 0xEE, 0xB7), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xA0, 0x45, 0xBF, 0xD9, 0x22, 0x11, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x7C, 0x5C, 0xD9, 0xC0, 0x9F, 0x69, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x8A, 0xA6, 0x79, 0x4E, 0x35, 0xB9, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0xCC, 0x8B, 0x9A, 0x3E, 0xA1, 0xB8, 0x28, 0x10), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_29_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x2F, 0xEF, 0xBB, 0xA9, 0x72, 0x7F, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0x34, 0xB7, 0x12, 0xB9, 0xE7, 0xC3, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x1D, 0xD9, 0x42, 0x77, 0x0C, 0x71, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0x01, 0x59, 0xA7, 0x56, 0x03, 0x91, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x91, 0x99, 0x33, 0x30, 0x3E, 0xEF, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0xC9, 0x5A, 0x9A, 0x54, 0x66, 0xF1, 0x70), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_29_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0x2C, 0xB7, 0x6E, 0x71, 0x7D, 0x35, 0x30), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x0D, 0xEF, 0xD1, 0x2D, 0x99, 0x63, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x31, 0xAF, 0x2D, 0xC9, 0xC6, 0xC2, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0xC0, 0xDF, 0x80, 0x54, 0xC4, 0xAC, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x6B, 0xA0, 0x84, 0x96, 0xF7, 0x31, 0xC8), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0xE2, 0x7C, 0x7A, 0x41, 0x45, 0x75, 0x6A), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_30_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0xEE, 0x58, 0x31, 0xE8, 0x68, 0xD6, 0x76), + MBEDTLS_BYTES_TO_T_UINT_8(0xD2, 0x2E, 0x48, 0xB7, 0x09, 0x9F, 0xD4, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xA9, 0x5C, 0xE7, 0x64, 0x43, 0x5D, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x9E, 0x58, 0x9F, 0x50, 0xAB, 0x68, 0xFF, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0x88, 0x2D, 0xBA, 0x12, 0xBF, 0x8D, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0xDF, 0x6F, 0xB3, 0x75, 0xA4, 0x55, 0x73), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_30_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x17, 0x92, 0x39, 0xB7, 0x13, 0x37, 0x6F), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0x43, 0x71, 0xA7, 0xCA, 0x17, 0x1B, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0xE7, 0xB9, 0xB0, 0x78, 0xEF, 0xA0, 0xDA, 0x83), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0x84, 0xF2, 0x0F, 0x85, 0xA2, 0xB6, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0x72, 0x65, 0x2E, 0x6E, 0x45, 0xB9, 0x4C, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0x6A, 0x8C, 0x2B, 0x77, 0x96, 0x36, 0x22), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_31_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x7A, 0x13, 0x4A, 0x97, 0x63, 0x02, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x1E, 0x06, 0x03, 0x8F, 0xB9, 0xEE, 0x64), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0xEE, 0x8B, 0x89, 0xA9, 0x70, 0xDB, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x7B, 0x81, 0xC9, 0x70, 0x8D, 0x62, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0xDA, 0x46, 0xF8, 0xF9, 0x3A, 0xBE, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0x9C, 0x7A, 0x97, 0x62, 0xEB, 0xFA, 0x0F), +}; +static const mbedtls_mpi_uint brainpoolP384r1_T_31_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB2, 0x03, 0x3D, 0x3C, 0x46, 0x27, 0x9E, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x08, 0x1C, 0xD5, 0x25, 0xAF, 0xE9, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0x69, 0xDC, 0x59, 0xF4, 0x8A, 0x7C, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x9A, 0x7A, 0x99, 0x21, 0x0C, 0x4E, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xCE, 0x85, 0x5F, 0xAC, 0xAA, 0x82, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x83, 0x57, 0x69, 0x90, 0x76, 0xF3, 0x53, 0x3F), +}; +static const mbedtls_ecp_point brainpoolP384r1_T[32] = { + ECP_POINT_INIT_XY_Z1(brainpoolP384r1_T_0_X, brainpoolP384r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_1_X, brainpoolP384r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_2_X, brainpoolP384r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_3_X, brainpoolP384r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_4_X, brainpoolP384r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_5_X, brainpoolP384r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_6_X, brainpoolP384r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_7_X, brainpoolP384r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_8_X, brainpoolP384r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_9_X, brainpoolP384r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_10_X, brainpoolP384r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_11_X, brainpoolP384r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_12_X, brainpoolP384r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_13_X, brainpoolP384r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_14_X, brainpoolP384r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_15_X, brainpoolP384r1_T_15_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_16_X, brainpoolP384r1_T_16_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_17_X, brainpoolP384r1_T_17_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_18_X, brainpoolP384r1_T_18_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_19_X, brainpoolP384r1_T_19_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_20_X, brainpoolP384r1_T_20_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_21_X, brainpoolP384r1_T_21_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_22_X, brainpoolP384r1_T_22_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_23_X, brainpoolP384r1_T_23_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_24_X, brainpoolP384r1_T_24_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_25_X, brainpoolP384r1_T_25_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_26_X, brainpoolP384r1_T_26_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_27_X, brainpoolP384r1_T_27_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_28_X, brainpoolP384r1_T_28_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_29_X, brainpoolP384r1_T_29_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_30_X, brainpoolP384r1_T_30_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP384r1_T_31_X, brainpoolP384r1_T_31_Y), +}; +#else +#define brainpoolP384r1_T NULL +#endif + +#endif /* MBEDTLS_ECP_DP_BP384R1_ENABLED */ + +/* + * Domain parameters for brainpoolP512r1 (RFC 5639 3.7) + */ +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) +static const mbedtls_mpi_uint brainpoolP512r1_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0x48, 0x3A, 0x58, 0x56, 0x60, 0xAA, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0x85, 0xC6, 0x82, 0x2D, 0x2F, 0xFF, 0x81, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x80, 0xA3, 0xE6, 0x2A, 0xA1, 0xCD, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0x68, 0xC6, 0x9B, 0x00, 0x9B, 0x4D, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x08, 0x33, 0x70, 0xCA, 0x9C, 0x63, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0xD2, 0xC9, 0xB3, 0xB3, 0x8D, 0x30, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xFC, 0xC9, 0x33, 0xAE, 0xE6, 0xD4, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0xC4, 0xE9, 0xDB, 0xB8, 0x9D, 0xDD, 0xAA), +}; +static const mbedtls_mpi_uint brainpoolP512r1_a[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x94, 0xFC, 0x77, 0x4D, 0xAC, 0xC1, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0xC7, 0xF2, 0x2B, 0xA7, 0x17, 0x11, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0xC8, 0x9A, 0x8B, 0xC9, 0xF1, 0x2E, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x3A, 0x25, 0xA8, 0x5A, 0x5D, 0xED, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0x63, 0x98, 0xEA, 0xCA, 0x41, 0x34, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0x16, 0xF9, 0x3D, 0x8D, 0xDD, 0xCB, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0x4C, 0x23, 0xAC, 0x45, 0x71, 0x32, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x89, 0x3B, 0x60, 0x8B, 0x31, 0xA3, 0x30, 0x78), +}; +static const mbedtls_mpi_uint brainpoolP512r1_b[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x23, 0xF7, 0x16, 0x80, 0x63, 0xBD, 0x09, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0xE5, 0xBA, 0x5E, 0xB7, 0x50, 0x40, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0x3E, 0x08, 0xDC, 0xCA, 0x94, 0xFC, 0x77), + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0xAC, 0xC1, 0xE7, 0xB9, 0xC7, 0xF2, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x17, 0x11, 0x7F, 0xB5, 0xC8, 0x9A, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0xF1, 0x2E, 0x0A, 0xA1, 0x3A, 0x25, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x5D, 0xED, 0x2D, 0xBC, 0x63, 0x98, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0x41, 0x34, 0xA8, 0x10, 0x16, 0xF9, 0x3D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_gx[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0xF8, 0xB9, 0xBC, 0x09, 0x22, 0x35, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x5E, 0x6A, 0x40, 0x47, 0x50, 0x6D, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x7D, 0xB9, 0x93, 0x7B, 0x68, 0xD1, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xD4, 0xD0, 0xE2, 0x78, 0x1F, 0x3B, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x09, 0xD0, 0xF4, 0xEE, 0x62, 0x3B, 0xB4), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x16, 0xD9, 0xB5, 0x70, 0x9F, 0xED, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x6A, 0x4C, 0x9C, 0x2E, 0x32, 0x21, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0xD9, 0x2E, 0xD8, 0xBD, 0xE4, 0xAE, 0x81), +}; +static const mbedtls_mpi_uint brainpoolP512r1_gy[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x08, 0xD8, 0x3A, 0x0F, 0x1E, 0xCD, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0x54, 0xF0, 0xA8, 0x2F, 0x2B, 0xCA, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x63, 0x27, 0x8A, 0xD8, 0x4B, 0xCA, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0x48, 0x5F, 0x4A, 0x49, 0xDE, 0xDC, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x81, 0x1F, 0x88, 0x5B, 0xC5, 0x00, 0xA0), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x7B, 0xA5, 0x24, 0x00, 0xF7, 0x09, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0x22, 0x78, 0xCF, 0xA9, 0xBF, 0xEA, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0x32, 0x63, 0x56, 0x5D, 0x38, 0xDE, 0x7D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x69, 0x00, 0xA9, 0x9C, 0x82, 0x96, 0x87, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0xDA, 0x5D, 0x08, 0x81, 0xD3, 0xB1, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x47, 0x10, 0xAC, 0x7F, 0x19, 0x61, 0x86, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x26, 0xA9, 0x4C, 0x41, 0x5C, 0x3E, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x08, 0x33, 0x70, 0xCA, 0x9C, 0x63, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0xD2, 0xC9, 0xB3, 0xB3, 0x8D, 0x30, 0xCB), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xFC, 0xC9, 0x33, 0xAE, 0xE6, 0xD4, 0x3F), + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0xC4, 0xE9, 0xDB, 0xB8, 0x9D, 0xDD, 0xAA), +}; + +#if MBEDTLS_ECP_FIXED_POINT_OPTIM == 1 +static const mbedtls_mpi_uint brainpoolP512r1_T_0_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0xF8, 0xB9, 0xBC, 0x09, 0x22, 0x35, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x5E, 0x6A, 0x40, 0x47, 0x50, 0x6D, 0x7C), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0x7D, 0xB9, 0x93, 0x7B, 0x68, 0xD1, 0x50), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xD4, 0xD0, 0xE2, 0x78, 0x1F, 0x3B, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x09, 0xD0, 0xF4, 0xEE, 0x62, 0x3B, 0xB4), + MBEDTLS_BYTES_TO_T_UINT_8(0xC1, 0x16, 0xD9, 0xB5, 0x70, 0x9F, 0xED, 0x85), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x6A, 0x4C, 0x9C, 0x2E, 0x32, 0x21, 0x5A), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0xD9, 0x2E, 0xD8, 0xBD, 0xE4, 0xAE, 0x81), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_0_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x08, 0xD8, 0x3A, 0x0F, 0x1E, 0xCD, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x06, 0x54, 0xF0, 0xA8, 0x2F, 0x2B, 0xCA, 0xD1), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0x63, 0x27, 0x8A, 0xD8, 0x4B, 0xCA, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0x48, 0x5F, 0x4A, 0x49, 0xDE, 0xDC, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0x81, 0x1F, 0x88, 0x5B, 0xC5, 0x00, 0xA0), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x7B, 0xA5, 0x24, 0x00, 0xF7, 0x09, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0x22, 0x78, 0xCF, 0xA9, 0xBF, 0xEA, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0x32, 0x63, 0x56, 0x5D, 0x38, 0xDE, 0x7D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_1_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xE9, 0x6B, 0x8C, 0x6F, 0x9D, 0x88, 0x43), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x4F, 0x86, 0x96, 0xA7, 0x56, 0xD1, 0x37), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0xAB, 0xFA, 0xEE, 0xA7, 0xF5, 0x0E, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x40, 0xEF, 0x9E, 0x6D, 0xD6, 0x32, 0x33), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0xED, 0x56, 0x14, 0x57, 0x1A, 0x8D, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0xED, 0x4D, 0x3A, 0xFA, 0x71, 0x75, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0xC5, 0x76, 0x1C, 0x14, 0xBE, 0xB5, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0x5A, 0xCB, 0xE7, 0x36, 0x1D, 0x52, 0x1C), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_1_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x8D, 0x7A, 0xEB, 0xA3, 0x8B, 0xD5, 0xB0), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0xA3, 0x41, 0xF8, 0xAC, 0x9E, 0xAB, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x12, 0xE3, 0x65, 0x0D, 0x1C, 0xFE, 0x09, 0x2B), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0xCA, 0x13, 0x3F, 0xC5, 0xF9, 0x7E, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x2C, 0x5D, 0x63, 0x28, 0xA6, 0x89, 0xD3, 0x91), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x95, 0x3F, 0x7A, 0x82, 0xD4, 0x77, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xBB, 0x92, 0x32, 0x00, 0xF4, 0x66, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x58, 0x31, 0xD1, 0x17, 0x9F, 0x2A, 0x22), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_2_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0x36, 0xA9, 0xCD, 0x80, 0xA5, 0x2D, 0x78), + MBEDTLS_BYTES_TO_T_UINT_8(0x91, 0x44, 0xAB, 0xCE, 0x71, 0xFF, 0x0C, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0x24, 0x58, 0x35, 0x5A, 0x21, 0x32, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0x1B, 0xA6, 0x28, 0xF8, 0x7A, 0x97, 0xAE, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0xE7, 0x08, 0xFA, 0x47, 0xC9, 0x55, 0x09), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xAC, 0x2E, 0x84, 0xA4, 0xF5, 0x52, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x58, 0x05, 0x9D, 0xA7, 0xC8, 0x71, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0x92, 0xB4, 0x92, 0xC1, 0x92, 0xEC, 0x6B), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_2_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4A, 0x48, 0x2D, 0x79, 0x5E, 0x58, 0xE5, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x85, 0x26, 0xEC, 0xE9, 0x6E, 0xD4, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x68, 0x26, 0x87, 0x38, 0xA2, 0xD2, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0x17, 0x60, 0xCE, 0x75, 0xF8, 0xA5, 0x6F), + MBEDTLS_BYTES_TO_T_UINT_8(0x20, 0x51, 0xDB, 0xA9, 0xAE, 0x87, 0xF1, 0x15), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x49, 0x92, 0x3B, 0x19, 0x96, 0xF5, 0xB0), + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0xD5, 0x52, 0x52, 0x8C, 0xCE, 0xFD, 0xFA), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0x18, 0x0A, 0xE6, 0xF6, 0xAE, 0x08, 0x41), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_3_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x2B, 0xD8, 0x54, 0xCE, 0xB0, 0x57, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0xB0, 0xF8, 0x9E, 0x03, 0x03, 0x3C, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x0E, 0x29, 0x29, 0x00, 0xF3, 0x70, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0x33, 0x99, 0x0E, 0x00, 0x5D, 0xFE, 0x4B), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0x2D, 0xF2, 0x59, 0x32, 0xCF, 0x03, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0x3B, 0xC9, 0x72, 0xAE, 0x0C, 0xEF, 0xD1, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x5A, 0x27, 0xBF, 0x2F, 0x45, 0xF9, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0xD4, 0xBE, 0xE5, 0x2C, 0xFF, 0x5B, 0x1E, 0x88), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_3_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0xAC, 0xBB, 0xD8, 0x83, 0xC2, 0x46, 0xF6), + MBEDTLS_BYTES_TO_T_UINT_8(0xCF, 0xDC, 0xCE, 0x15, 0xB4, 0xEF, 0xCF, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0xDB, 0x5E, 0x94, 0x31, 0x0B, 0xB2, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0xB9, 0xE3, 0xE3, 0x11, 0x71, 0x41, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0xE3, 0x01, 0xB7, 0x7D, 0xBC, 0x65, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x07, 0x65, 0x87, 0xA7, 0xE8, 0x48, 0xE3), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0x48, 0x8F, 0xD4, 0x30, 0x8E, 0xB4, 0x6C), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0xE0, 0x73, 0xBE, 0x1E, 0xBF, 0x56, 0x36), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_4_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xFE, 0x0E, 0x5E, 0x87, 0xC5, 0xAB, 0x0E, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0xF9, 0x5F, 0x80, 0x24, 0x4C, 0x2A, 0xF1), + MBEDTLS_BYTES_TO_T_UINT_8(0xDE, 0x15, 0x21, 0x54, 0x92, 0x84, 0x8D, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0xA8, 0x8A, 0x47, 0x74, 0xDC, 0x42, 0xB1, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0xF7, 0x30, 0xFD, 0xC1, 0x9B, 0x0C, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0x6C, 0xCC, 0xDF, 0xC5, 0xE3, 0xA9, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x67, 0x59, 0x10, 0x5C, 0x51, 0x54, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x37, 0xFB, 0x6E, 0xB0, 0x78, 0x63, 0x8E), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_4_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0xEF, 0xC4, 0x39, 0x20, 0xF1, 0x46, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0x62, 0xAE, 0xFF, 0x10, 0xE4, 0xE2, 0xE9), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x5C, 0xF5, 0x2E, 0x22, 0x89, 0xE5, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x89, 0x0C, 0x29, 0xA8, 0x62, 0xAE, 0xDB, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0x9E, 0x0F, 0xCA, 0x87, 0x2A, 0x6F, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xCE, 0xDC, 0x9B, 0x9F, 0x65, 0xD4, 0xAD, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0xC3, 0x08, 0x0F, 0xCF, 0x67, 0xE9, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x5C, 0xD7, 0xFF, 0x41, 0x9C, 0xCB, 0x26), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_5_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x25, 0x05, 0x12, 0xAD, 0x73, 0x63, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0x99, 0x07, 0x86, 0x57, 0xE7, 0x94, 0xB1), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x4B, 0xA5, 0xBF, 0x18, 0xA9, 0xEF, 0x6A), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0x4C, 0xC4, 0x09, 0xF2, 0x2F, 0x0C, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0x3A, 0x04, 0xEA, 0x89, 0x6C, 0x91, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x6C, 0x3A, 0xE7, 0xA3, 0xEC, 0x24, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0xA1, 0x26, 0x21, 0x04, 0xE3, 0xB9, 0x40), + MBEDTLS_BYTES_TO_T_UINT_8(0x53, 0x71, 0x4B, 0x7B, 0xC2, 0x89, 0xCD, 0xA2), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_5_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB7, 0xB9, 0xA8, 0x9D, 0xFD, 0x00, 0x3A, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0x41, 0x6C, 0xBB, 0x5A, 0xCA, 0x1F, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0xD7, 0xE2, 0x6C, 0x6B, 0xA7, 0x48, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x19, 0xAD, 0xA7, 0xC1, 0x7E, 0x4F, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0xF7, 0x19, 0x3C, 0x06, 0x74, 0x2C, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0x23, 0x4F, 0x0C, 0x09, 0xB0, 0x80, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0x4E, 0x74, 0x34, 0x08, 0x44, 0x7E, 0xA3, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0xCC, 0x8D, 0x12, 0x6E, 0xE1, 0x3D, 0x0B), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_6_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x18, 0xB1, 0x71, 0x02, 0x93, 0xC2, 0xA4), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x89, 0x40, 0xE2, 0x1F, 0xE7, 0x5E, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x8E, 0xAE, 0x89, 0x01, 0xD4, 0x0C, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0xAE, 0xDA, 0x58, 0x70, 0x24, 0xF2, 0xE4, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0xC7, 0x1D, 0xD6, 0x4A, 0x6F, 0x66, 0x4F), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x1D, 0x7E, 0x4A, 0x2C, 0xCA, 0xEC, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0xA1, 0x06, 0x7F, 0xA8, 0x99, 0xE4, 0xD3, 0x4E), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0x1D, 0x5A, 0xDF, 0x5E, 0x58, 0x36, 0x49), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_6_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0xB9, 0x32, 0x69, 0x1F, 0x72, 0x2A, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0x73, 0xE2, 0x03, 0x39, 0x35, 0xAA, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0x5E, 0x5D, 0x48, 0xEF, 0xAE, 0x30, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x7F, 0x60, 0x19, 0xAF, 0xEC, 0x9D, 0xFC), + MBEDTLS_BYTES_TO_T_UINT_8(0xCA, 0xD9, 0x19, 0xE4, 0x1B, 0x56, 0x15, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xD7, 0x33, 0x59, 0x1F, 0x43, 0x59, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0xCE, 0xEE, 0xCA, 0xA4, 0x7F, 0x63, 0xD4), + MBEDTLS_BYTES_TO_T_UINT_8(0xBD, 0x40, 0xC0, 0xF6, 0x19, 0x89, 0x43, 0x20), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_7_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0x92, 0xEA, 0x07, 0x65, 0x79, 0x86, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0xB7, 0x13, 0x75, 0xD3, 0xC5, 0x0A, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x9E, 0xFA, 0xE1, 0x1F, 0x0C, 0xF9, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x8C, 0xED, 0x5C, 0x21, 0xE9, 0x09, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0x4D, 0xD8, 0x18, 0xC4, 0xF6, 0x36, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0xC9, 0xAC, 0x5C, 0xFA, 0x69, 0xA4, 0xA0), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0x8C, 0x94, 0x1C, 0x7B, 0x71, 0x36, 0x58), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0xBD, 0x46, 0xCE, 0xB7, 0x1D, 0x9C, 0x5E), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_7_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xD6, 0x96, 0x4B, 0xA6, 0x47, 0xEB, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0xF1, 0x5F, 0x15, 0xDE, 0x99, 0x6F, 0x66), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0xBD, 0xE5, 0x04, 0xB8, 0xE6, 0xC0, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0x49, 0xD3, 0xF0, 0x04, 0x00, 0xE4, 0x05, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xF3, 0x06, 0xA3, 0x1A, 0xFF, 0xEA, 0x73), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x32, 0xAA, 0x99, 0x33, 0x09, 0xB6, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x6E, 0xEF, 0xFC, 0x61, 0x10, 0x42, 0x31, 0x94), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0xF1, 0xF4, 0x33, 0xCF, 0x28, 0x90, 0x9C), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_8_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0xDE, 0xF9, 0x88, 0x87, 0x7B, 0xEB, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0xB8, 0xDA, 0xFA, 0xDA, 0x3D, 0xA6, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0xF0, 0x62, 0x82, 0x53, 0x32, 0x55, 0x03), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0xA5, 0x32, 0x4A, 0x19, 0x11, 0x9C, 0x10), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0xB3, 0x27, 0xE9, 0x75, 0x90, 0x05, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0x1C, 0x90, 0x48, 0x77, 0x01, 0x85, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0xD6, 0x9B, 0x84, 0xA8, 0xD7, 0xC5, 0x28), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0x7A, 0xCB, 0xB3, 0x11, 0x46, 0xD7, 0x99), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_8_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0x23, 0xBF, 0x75, 0x75, 0xA1, 0x95, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0x4B, 0x66, 0x5D, 0x34, 0x13, 0xA9, 0x03, 0xBE), + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x80, 0x9D, 0x5F, 0xD2, 0x44, 0xE1, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x5D, 0xBD, 0xA8, 0xBF, 0xB4, 0x25, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x99, 0x1F, 0x53, 0xF1, 0x57, 0xDB, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x7C, 0xE5, 0xC5, 0x51, 0x0B, 0x4C, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x6B, 0xB0, 0x1A, 0x9C, 0x16, 0xB0, 0x32, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0xE3, 0xCF, 0xDD, 0x48, 0xB4, 0x7B, 0x33), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_9_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0xDD, 0x9E, 0x3C, 0x98, 0x0E, 0x77, 0x65), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0xAB, 0x01, 0xD3, 0x87, 0x74, 0x25, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0xA3, 0xE3, 0x76, 0x43, 0x87, 0x12, 0xBD), + MBEDTLS_BYTES_TO_T_UINT_8(0x54, 0xB1, 0x3B, 0x60, 0x66, 0xEB, 0x98, 0x54), + MBEDTLS_BYTES_TO_T_UINT_8(0xD2, 0x78, 0xC8, 0xD7, 0x4E, 0x75, 0xCA, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xDF, 0x71, 0x19, 0xE7, 0x07, 0x36, 0xB5), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0xC9, 0xA8, 0x5F, 0x91, 0xBF, 0x47, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x96, 0x58, 0x96, 0x18, 0xB6, 0xFA, 0x01), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_9_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x2D, 0xA9, 0x9B, 0x86, 0xDB, 0x0C, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x0B, 0x2D, 0x56, 0x4A, 0xD3, 0x93, 0x8A), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0x15, 0xE2, 0x65, 0x12, 0x86, 0x0E, 0xB2), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x41, 0x4D, 0xC1, 0xCB, 0xE4, 0xC3, 0xD7), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x53, 0x10, 0xCA, 0xA3, 0xAC, 0x83, 0x26), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0x01, 0x22, 0x96, 0x10, 0xAD, 0x69, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x42, 0x46, 0x4E, 0xD8, 0xEA, 0xD6, 0x9D, 0xF3), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0x2F, 0x7F, 0x62, 0x62, 0x80, 0xD0, 0x14), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_10_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB2, 0xDA, 0x00, 0x63, 0x09, 0xBD, 0x6A, 0x83), + MBEDTLS_BYTES_TO_T_UINT_8(0x0F, 0xD4, 0x6E, 0x48, 0x05, 0xB7, 0xF7, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0x4D, 0xD7, 0x00, 0x4A, 0x15, 0x27, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x15, 0xAA, 0x37, 0x27, 0x34, 0x18, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x20, 0x2C, 0x84, 0x1B, 0x88, 0xBA, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x09, 0xD6, 0x04, 0xA2, 0x60, 0x84, 0x72), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0x04, 0x94, 0x08, 0xD4, 0xED, 0x47, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0xF3, 0xE4, 0x3E, 0xB9, 0x5B, 0x35, 0x42), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_10_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5F, 0xD8, 0xB6, 0x80, 0xD6, 0xF1, 0x30, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x14, 0xA6, 0x85, 0xEE, 0xA7, 0xD8, 0x61), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x49, 0x2A, 0x1E, 0x7C, 0xE9, 0x2D, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x3A, 0x87, 0x56, 0x91, 0x03, 0x77, 0x4D, 0x55), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0x52, 0xD4, 0xAA, 0xF7, 0xFA, 0xB0, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x5D, 0x11, 0x39, 0xB1, 0xE7, 0x76, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x13, 0xBC, 0x37, 0x5D, 0x74, 0xCD, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x48, 0x14, 0x23, 0x30, 0xF8, 0x46, 0x37), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_11_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x27, 0xB0, 0xD9, 0xB2, 0x74, 0xB4, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0xEA, 0xA6, 0xB9, 0x6F, 0x9F, 0x64, 0x36, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0x2E, 0x2B, 0x78, 0x40, 0x05, 0x2B, 0x7B, 0xA9), + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0x68, 0x3A, 0xB6, 0x4A, 0xE2, 0xDB, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x1E, 0x33, 0xD7, 0x34, 0x8B, 0x25, 0x45, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0x89, 0xCE, 0xA8, 0xC9, 0x01, 0xFB, 0x0E, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0xF9, 0x51, 0x4C, 0x12, 0x9F, 0x60, 0xE4), + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0x85, 0xBD, 0x30, 0x37, 0x84, 0x39, 0x44), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_11_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x26, 0x33, 0xAF, 0x2E, 0xB8, 0x2E, 0xCC, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0xB1, 0x73, 0x59, 0x4E, 0x0C, 0x09, 0x4A), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0x24, 0x89, 0x81, 0x12, 0xFF, 0xBB, 0x6E), + MBEDTLS_BYTES_TO_T_UINT_8(0x71, 0x37, 0x1A, 0x66, 0xEE, 0xED, 0xB6, 0x9B), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0xBD, 0x04, 0x20, 0x5D, 0xFB, 0xBF, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0xF8, 0x34, 0xA3, 0xFF, 0x45, 0xDE, 0x92), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x18, 0x73, 0xF1, 0x32, 0x25, 0x58, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0xC1, 0x14, 0xE3, 0x9E, 0x40, 0x0F, 0x12), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_12_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x07, 0x9D, 0x9C, 0x00, 0xF7, 0x56, 0x19), + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0xBA, 0x87, 0xF9, 0x15, 0x0C, 0x66, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0x1F, 0xC1, 0x28, 0xB0, 0x47, 0x0D, 0xF5), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0xCA, 0x27, 0xEE, 0x4B, 0x23, 0x2B, 0x89), + MBEDTLS_BYTES_TO_T_UINT_8(0x7E, 0xB5, 0x68, 0xC8, 0x17, 0x5D, 0xC3, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0x02, 0x08, 0xEE, 0x20, 0x9D, 0xEA, 0x64), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x14, 0x50, 0xD4, 0x7D, 0x5F, 0xCF, 0xA0), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0xFA, 0xF8, 0xA7, 0xC6, 0xDC, 0x14, 0x8C), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_12_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x76, 0xBD, 0x0A, 0x1A, 0x18, 0x98, 0xDC, 0xB0), + MBEDTLS_BYTES_TO_T_UINT_8(0x63, 0x63, 0x02, 0xB7, 0xD5, 0x5B, 0x5A, 0xC6), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0xB1, 0xD7, 0x4B, 0x15, 0x39, 0x61, 0x5D), + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0x32, 0xE1, 0x9E, 0x70, 0x1B, 0xCE, 0x51), + MBEDTLS_BYTES_TO_T_UINT_8(0x64, 0xD8, 0x18, 0x83, 0x52, 0x9B, 0x6D, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x55, 0x56, 0x19, 0x34, 0xA4, 0xEA, 0xFC), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0xA9, 0x55, 0x80, 0xE3, 0x15, 0x36, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0x06, 0xC8, 0x1D, 0x17, 0x0D, 0xAD, 0x16), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_13_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x20, 0xD6, 0xF0, 0xCC, 0xF3, 0x63, 0x53, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x5A, 0xDC, 0x46, 0xBD, 0x0D, 0xAD, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0x2F, 0x11, 0x60, 0x15, 0x51, 0x4A, 0xEA), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0xE3, 0x93, 0x38, 0xD5, 0x83, 0xAA, 0x0D), + MBEDTLS_BYTES_TO_T_UINT_8(0x90, 0xA6, 0xCC, 0xB1, 0xFD, 0xBB, 0x1A, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x3B, 0x54, 0xC8, 0x54, 0x6F, 0x79, 0x1A, 0x59), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x4A, 0xDA, 0x28, 0x92, 0x97, 0x9D, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0xD6, 0x4B, 0xDB, 0xC7, 0x52, 0xC5, 0x66, 0x34), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_13_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0x7E, 0x92, 0x53, 0x30, 0x93, 0xFD, 0xFF), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x16, 0x6A, 0xB1, 0x91, 0x0A, 0xB4, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x6D, 0x9D, 0x40, 0x3F, 0xE3, 0xF1, 0x01, 0x46), + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0x0E, 0xD8, 0xED, 0x11, 0x8E, 0x4C, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0x86, 0x4A, 0x1B, 0x88, 0xDF, 0x8D, 0x29, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x23, 0x21, 0x11, 0xAB, 0x77, 0x81, 0x62), + MBEDTLS_BYTES_TO_T_UINT_8(0x0B, 0xAF, 0x11, 0xFA, 0xBA, 0x40, 0x63, 0xE7), + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0x6F, 0x8D, 0x80, 0xDF, 0x67, 0xF5, 0x44), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_14_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB2, 0x8B, 0xB7, 0x08, 0xF4, 0xD7, 0x2D, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0x2B, 0x30, 0x02, 0x45, 0x71, 0x08, 0x49), + MBEDTLS_BYTES_TO_T_UINT_8(0x97, 0x3A, 0xCA, 0x50, 0xF6, 0xC2, 0x19, 0x8C), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xB9, 0x9B, 0x3E, 0x73, 0x95, 0x1D, 0x49), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x60, 0x59, 0x48, 0xCB, 0xD8, 0xD6, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0xB9, 0x6C, 0x89, 0xAB, 0x99, 0xA8, 0xF8), + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0xA1, 0x8B, 0x4E, 0x06, 0x19, 0xEC, 0x99), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x95, 0x04, 0xCF, 0xD5, 0x94, 0xB3, 0x02), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_14_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x29, 0x35, 0x93, 0x7C, 0xB3, 0xB8, 0x9E, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0xC4, 0x45, 0x5C, 0x7E, 0xBF, 0x75, 0x81, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0xDC, 0xE8, 0x24, 0xDF, 0xEC, 0x2F, 0x7D, 0xB9), + MBEDTLS_BYTES_TO_T_UINT_8(0xF2, 0x8B, 0xD5, 0x6A, 0x9B, 0xA0, 0xE0, 0x4F), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0xE3, 0x27, 0x82, 0xDE, 0xDD, 0xCA, 0x4B), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x57, 0x56, 0x46, 0x05, 0x06, 0x01, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0x74, 0x35, 0xA7, 0x47, 0xE2, 0x6B, 0x2C, 0x4F), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x9D, 0x4C, 0xEC, 0x1F, 0x11, 0x75, 0x2B), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_15_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0xAA, 0x41, 0xC1, 0xE9, 0x0E, 0xE9, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0xCF, 0x9C, 0x4B, 0xE8, 0xED, 0x0A, 0x49), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0x73, 0xCA, 0x0C, 0x46, 0x0A, 0x9C, 0xE4), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0xE1, 0x9E, 0xBC, 0xFE, 0x44, 0x63, 0x6D), + MBEDTLS_BYTES_TO_T_UINT_8(0x31, 0x43, 0x71, 0xEE, 0xF8, 0xC1, 0x8C, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x6A, 0x4B, 0xF0, 0x69, 0x25, 0xBD, 0x71, 0x1A), + MBEDTLS_BYTES_TO_T_UINT_8(0xFD, 0x9A, 0xFE, 0x82, 0xE7, 0xC1, 0xC1, 0xEE), + MBEDTLS_BYTES_TO_T_UINT_8(0xFC, 0x5A, 0x6E, 0x5E, 0x97, 0x6A, 0x35, 0x8D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_15_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA2, 0x18, 0x6C, 0x7E, 0xB8, 0x9E, 0x57, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x35, 0xB9, 0xC1, 0xD0, 0xFE, 0x78, 0xFB, 0x32), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x08, 0xAE, 0x46, 0x34, 0xEA, 0x7A, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x1C, 0x56, 0xA9, 0x18, 0x37, 0xD4, 0x9E), + MBEDTLS_BYTES_TO_T_UINT_8(0x28, 0x63, 0xE9, 0x0A, 0xB6, 0x38, 0x3C, 0xC1), + MBEDTLS_BYTES_TO_T_UINT_8(0x3E, 0x4F, 0xA4, 0x6E, 0x85, 0x31, 0x23, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x0D, 0xAD, 0xC4, 0xC3, 0xB1, 0x4B, 0x1C, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x56, 0x4A, 0x38, 0xB3, 0x6B, 0x6F, 0x2C), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_16_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x67, 0xC7, 0x19, 0xDE, 0x21, 0xED, 0x89, 0xD0), + MBEDTLS_BYTES_TO_T_UINT_8(0x2F, 0xBE, 0xA6, 0xAE, 0xEB, 0x9D, 0xA7, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x0E, 0x13, 0x1E, 0x86, 0x57, 0xC3, 0x3B), + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x4B, 0x30, 0x46, 0x52, 0xC1, 0xEC, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x6E, 0xD5, 0x44, 0x31, 0x96, 0x3B, 0x26, 0x27), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x68, 0xA8, 0x67, 0x78, 0x39, 0xE8, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0x78, 0xB7, 0xDD, 0xF2, 0x58, 0xB6, 0x3D), + MBEDTLS_BYTES_TO_T_UINT_8(0x81, 0x3C, 0xB3, 0x26, 0xC4, 0x2C, 0x8C, 0xA5), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_16_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB9, 0x24, 0xE5, 0x73, 0xEE, 0x9A, 0x02, 0xA9), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0x6A, 0x65, 0x60, 0xF3, 0x62, 0xE3, 0xE9), + MBEDTLS_BYTES_TO_T_UINT_8(0xFB, 0x07, 0x84, 0xE6, 0x3B, 0x46, 0x65, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0x8F, 0x0C, 0xB0, 0xE1, 0x04, 0x82, 0x9D), + MBEDTLS_BYTES_TO_T_UINT_8(0xEB, 0x13, 0xBF, 0x3D, 0xA0, 0x48, 0xA2, 0x74), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0x26, 0x76, 0x74, 0xAB, 0x0B, 0x29, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0x30, 0x6E, 0x5F, 0x03, 0x34, 0x7C, 0x38, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0x72, 0xF9, 0x3B, 0x3C, 0xA4, 0xBC, 0x7C), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_17_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x5C, 0xCE, 0x18, 0x80, 0xB8, 0x24, 0x45, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x09, 0x03, 0xB8, 0x06, 0x64, 0xF7, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0xF1, 0x26, 0xB1, 0x10, 0x6D, 0x71, 0x12, 0x2E), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x12, 0xC6, 0x6E, 0x1E, 0x6A, 0xC3, 0x80), + MBEDTLS_BYTES_TO_T_UINT_8(0xE5, 0xD3, 0x0A, 0xDE, 0xD8, 0x6B, 0x04, 0x5C), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x87, 0x5B, 0xAE, 0xDB, 0x3C, 0xC0, 0xC5), + MBEDTLS_BYTES_TO_T_UINT_8(0x8E, 0xF5, 0xF9, 0xC1, 0x9A, 0x89, 0xBB, 0x7E), + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0x69, 0x72, 0x8B, 0xAE, 0x32, 0x13, 0x11), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_17_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x16, 0x07, 0x50, 0xFA, 0x4C, 0xCF, 0xE8), + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x50, 0x21, 0xE9, 0xDE, 0xEC, 0x7E, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x2F, 0xE8, 0x83, 0x30, 0x0B, 0x65, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0x0B, 0x99, 0xAC, 0xC9, 0xBA, 0x6C, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x59, 0x5A, 0x0D, 0x7B, 0x9E, 0x08, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0x34, 0x91, 0xB2, 0xDC, 0x90, 0xCE, 0x67, 0xED), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x93, 0x60, 0x0C, 0xD7, 0x1F, 0x2F, 0x17), + MBEDTLS_BYTES_TO_T_UINT_8(0x19, 0x7F, 0x9D, 0x40, 0xF8, 0x78, 0x7A, 0x54), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_18_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x13, 0x22, 0x95, 0xE8, 0xEF, 0x31, 0x57, 0x35), + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0x88, 0x53, 0xFE, 0xAF, 0x7C, 0x47, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0xCE, 0xCC, 0x79, 0xE8, 0x9F, 0x8C, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0xDB, 0x16, 0xDD, 0x77, 0x6E, 0x8A, 0x73, 0x97), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0x07, 0x97, 0x21, 0x3B, 0xF8, 0x5F, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0xC6, 0xB5, 0xD2, 0x81, 0x84, 0xF0, 0xE7, 0x9F), + MBEDTLS_BYTES_TO_T_UINT_8(0xCB, 0x8F, 0x75, 0x09, 0x6A, 0x0E, 0x53, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x4F, 0x70, 0x97, 0xC7, 0xAC, 0x7D, 0x3F), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_18_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF9, 0x3C, 0x6A, 0xB4, 0x10, 0xA9, 0xC8, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0xC5, 0xD6, 0x69, 0x16, 0xB8, 0xAC, 0x25), + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x44, 0xDC, 0xEB, 0x48, 0x54, 0x5D, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0x6F, 0x48, 0x9B, 0xD7, 0x72, 0x69, 0xA4, 0x8A), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x0D, 0x36, 0x9A, 0x66, 0x0B, 0xEC, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0xC6, 0xD4, 0xB6, 0x60, 0xE5, 0xC3, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xBA, 0x29, 0x42, 0xE0, 0x9D, 0xFD, 0x7C, 0x3E), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0x10, 0xBA, 0x55, 0xBC, 0x3B, 0x38, 0x5D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_19_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x25, 0x66, 0xFA, 0x05, 0x73, 0x03, 0x1B, 0x69), + MBEDTLS_BYTES_TO_T_UINT_8(0x11, 0xA4, 0x66, 0x12, 0x96, 0x7B, 0x02, 0x4C), + MBEDTLS_BYTES_TO_T_UINT_8(0x44, 0xB5, 0xDE, 0x6D, 0x98, 0xD1, 0xD5, 0xA8), + MBEDTLS_BYTES_TO_T_UINT_8(0xE2, 0xF5, 0x44, 0xB8, 0x8E, 0xF6, 0x8C, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x15, 0x2B, 0x72, 0xBC, 0x49, 0xE5, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0x6C, 0x44, 0xD7, 0xDF, 0x8F, 0xEB, 0x8D, 0x80), + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0x64, 0x88, 0xAA, 0xB7, 0xE4, 0x70, 0x1D), + MBEDTLS_BYTES_TO_T_UINT_8(0x9C, 0x14, 0xBB, 0xE9, 0x9B, 0xB9, 0x65, 0x5D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_19_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x66, 0x8E, 0x88, 0xF5, 0xF1, 0xC1, 0x89, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0x16, 0x30, 0x53, 0xE6, 0xFB, 0x2D, 0x82, 0xB4), + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0xE4, 0xFF, 0xBA, 0x31, 0x79, 0xAB, 0xC2), + MBEDTLS_BYTES_TO_T_UINT_8(0x45, 0x09, 0xF7, 0xB7, 0x09, 0x78, 0x4C, 0x90), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0xAE, 0xC2, 0x44, 0xDC, 0x17, 0x78, 0x47), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0xD4, 0x17, 0x43, 0x19, 0x74, 0x9E, 0x23), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x64, 0x3B, 0x73, 0xA2, 0x99, 0x27, 0x76), + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0x74, 0x36, 0x5F, 0xD3, 0x14, 0xB1, 0x31), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_20_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAC, 0x07, 0xAB, 0xFD, 0x9B, 0x03, 0xC5, 0xD5), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0xBE, 0xB0, 0x1D, 0xF2, 0x0C, 0x73, 0x73), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0xE7, 0x7B, 0x87, 0xD3, 0x34, 0xFD, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x9A, 0x25, 0x3D, 0xC7, 0x36, 0x83, 0x53, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0x7C, 0xCF, 0x63, 0x55, 0x12, 0x11, 0xB0), + MBEDTLS_BYTES_TO_T_UINT_8(0xC0, 0x34, 0x4D, 0x27, 0x92, 0xAC, 0x18, 0x16), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x42, 0x61, 0x9D, 0x2E, 0xFF, 0x13, 0x16), + MBEDTLS_BYTES_TO_T_UINT_8(0xF4, 0xDE, 0x92, 0x65, 0x57, 0x0D, 0xBC, 0x0A), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_20_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xEF, 0x7B, 0x6E, 0xC6, 0x2A, 0x21, 0x74, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0xA7, 0x53, 0x4D, 0x29, 0x36, 0xEF, 0xE5), + MBEDTLS_BYTES_TO_T_UINT_8(0xE1, 0xD6, 0x41, 0xC7, 0x99, 0xAD, 0x50, 0x53), + MBEDTLS_BYTES_TO_T_UINT_8(0x99, 0xAC, 0x41, 0x9F, 0xFB, 0x4C, 0x86, 0xF1), + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0xBB, 0xE6, 0x25, 0x28, 0xAA, 0xEB, 0x1E), + MBEDTLS_BYTES_TO_T_UINT_8(0x92, 0x04, 0xA2, 0xC3, 0xAA, 0x08, 0x8A, 0xCC), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x2B, 0x5B, 0xE2, 0x8D, 0x76, 0xEA, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0xB3, 0x33, 0xD2, 0x21, 0x4D, 0x62, 0xE3, 0x8E), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_21_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0x06, 0x8B, 0x2B, 0xC2, 0xC4, 0xB1, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0xFA, 0xF5, 0xA1, 0xC0, 0x03, 0x6A, 0x29, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0xF5, 0xA9, 0xEF, 0x55, 0xB6, 0x1A, 0x9F, 0x6B), + MBEDTLS_BYTES_TO_T_UINT_8(0x9B, 0x54, 0x32, 0xBE, 0x06, 0x43, 0xB5, 0xFD), + MBEDTLS_BYTES_TO_T_UINT_8(0xF7, 0xD6, 0xD9, 0x20, 0x89, 0xBE, 0xD4, 0x1B), + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0x26, 0x95, 0x10, 0xCE, 0xB4, 0x88, 0x79), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0xA6, 0x27, 0xAC, 0x32, 0xBA, 0xBD, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0xA6, 0xAE, 0x9C, 0x7B, 0xBE, 0xA1, 0x63), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_21_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8B, 0xCD, 0x4D, 0x3D, 0xDF, 0x96, 0xBB, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0xA7, 0x11, 0x06, 0xCC, 0x0E, 0x31, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0x20, 0xE4, 0xF4, 0xAD, 0x7B, 0x5F, 0xF1, 0xEF), + MBEDTLS_BYTES_TO_T_UINT_8(0xE4, 0x54, 0xBE, 0xF4, 0x8A, 0x03, 0x47, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0x53, 0x00, 0x7F, 0xB0, 0x8A, 0x68, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x16, 0xB1, 0x73, 0x6F, 0x5B, 0x0E, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0x32, 0xE3, 0x43, 0x64, 0x75, 0xFB, 0xFB), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x18, 0x55, 0x8A, 0x4E, 0x6E, 0x35, 0x54), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_22_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x80, 0x97, 0x15, 0x1E, 0xCB, 0xF2, 0x9C, 0xA5), + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0xD1, 0xBB, 0xF3, 0x70, 0xAD, 0x13, 0xAD), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0x96, 0xA4, 0xC5, 0x5E, 0xDA, 0xD5, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0x81, 0xE9, 0x65, 0x66, 0x76, 0x47, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x35, 0x87, 0x06, 0x73, 0xCF, 0x34, 0xD2), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x81, 0x15, 0x42, 0xA2, 0x79, 0x5B, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x08, 0xA2, 0x7D, 0x09, 0x14, 0x64, 0xC6, 0xAE), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0x6D, 0xC4, 0xED, 0xF1, 0xD6, 0xE9, 0x24), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_22_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xB4, 0xD5, 0xBB, 0x25, 0xA3, 0xDD, 0xA3, 0x88), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0xF2, 0x68, 0x67, 0x39, 0x8F, 0x73, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x76, 0x28, 0x89, 0xAD, 0x32, 0xE0, 0xDF), + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0x90, 0xCC, 0x57, 0x58, 0xAA, 0xC9, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0xD7, 0x43, 0xD2, 0xCE, 0x5E, 0xA0, 0x08), + MBEDTLS_BYTES_TO_T_UINT_8(0x33, 0xB0, 0xB8, 0xA4, 0x9E, 0x96, 0x26, 0x86), + MBEDTLS_BYTES_TO_T_UINT_8(0x94, 0x61, 0x1D, 0xF3, 0x65, 0x5E, 0x60, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xC7, 0x1E, 0x65, 0xED, 0xCF, 0x07, 0x60, 0x20), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_23_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x30, 0x17, 0x8A, 0x91, 0x88, 0x0A, 0xA4), + MBEDTLS_BYTES_TO_T_UINT_8(0x05, 0x7D, 0x18, 0xA4, 0xAC, 0x59, 0xFC, 0x5F), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x31, 0x8B, 0x25, 0x65, 0x39, 0x9A, 0xDC), + MBEDTLS_BYTES_TO_T_UINT_8(0x15, 0x16, 0x4B, 0x68, 0xBA, 0x59, 0x13, 0x2F), + MBEDTLS_BYTES_TO_T_UINT_8(0x8D, 0xFD, 0xD3, 0xC5, 0x56, 0xC9, 0x8C, 0x5E), + MBEDTLS_BYTES_TO_T_UINT_8(0xBC, 0xC6, 0x9F, 0xF4, 0xE6, 0xF7, 0xB4, 0x01), + MBEDTLS_BYTES_TO_T_UINT_8(0x2D, 0x7C, 0x03, 0x00, 0x26, 0x9F, 0xD8, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0x1D, 0x6E, 0x00, 0xB9, 0x00, 0x6E, 0x93), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_23_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0x63, 0xDA, 0x03, 0x2B, 0xD5, 0x0B, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x46, 0xFC, 0xE2, 0xC8, 0x47, 0xF0, 0xAE, 0xF2), + MBEDTLS_BYTES_TO_T_UINT_8(0x51, 0x4C, 0xF7, 0x50, 0x0C, 0x48, 0x06, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0xDF, 0x2B, 0x32, 0x98, 0x0E, 0x7E, 0x61, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x02, 0x27, 0xFE, 0x75, 0x86, 0xDF, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0x2B, 0x30, 0xB1, 0x22, 0x32, 0x1B, 0xFE, 0x24), + MBEDTLS_BYTES_TO_T_UINT_8(0xC2, 0x27, 0xF7, 0x78, 0x6F, 0xD7, 0xFD, 0xE4), + MBEDTLS_BYTES_TO_T_UINT_8(0xA0, 0x78, 0xCC, 0xEA, 0xC0, 0x50, 0x24, 0x44), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_24_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x2B, 0x4F, 0x7F, 0x58, 0xE6, 0xC2, 0x70), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x43, 0xD5, 0xA7, 0x35, 0x3C, 0x80, 0xB8), + MBEDTLS_BYTES_TO_T_UINT_8(0x1A, 0x6D, 0x4B, 0x12, 0x00, 0x7B, 0xE6, 0xA6), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x15, 0xBD, 0xD0, 0x9B, 0xCA, 0xAA, 0x81), + MBEDTLS_BYTES_TO_T_UINT_8(0xCF, 0xCE, 0x9C, 0xE3, 0x8B, 0x60, 0x7A, 0x53), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0xDA, 0x4B, 0x03, 0xA7, 0x8D, 0x43, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0xAF, 0x00, 0x2B, 0x32, 0xF0, 0x22, 0x68), + MBEDTLS_BYTES_TO_T_UINT_8(0xDC, 0xD9, 0x99, 0x99, 0xBE, 0x43, 0x99, 0x3E), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_24_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x1F, 0x71, 0x41, 0xF4, 0xB5, 0xFD, 0xDD, 0x36), + MBEDTLS_BYTES_TO_T_UINT_8(0x9D, 0xE2, 0x20, 0x4C, 0xD1, 0x2E, 0x1F, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0x96, 0x43, 0x48, 0x76, 0x8A, 0x49, 0xAC, 0x87), + MBEDTLS_BYTES_TO_T_UINT_8(0x0C, 0x1A, 0x55, 0xA8, 0xA3, 0xD4, 0x57, 0x75), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0xA6, 0x84, 0x39, 0xC9, 0x13, 0xBB, 0x60), + MBEDTLS_BYTES_TO_T_UINT_8(0xD9, 0xFA, 0xA9, 0x70, 0xDE, 0x83, 0xDD, 0xC9), + MBEDTLS_BYTES_TO_T_UINT_8(0xEC, 0xC9, 0xD9, 0x3E, 0x44, 0x91, 0x68, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xB6, 0x9F, 0x85, 0x6D, 0xF7, 0x54, 0x36, 0x82), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_25_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x68, 0x6B, 0xA6, 0xA3, 0xE5, 0xD4, 0x46, 0xDB), + MBEDTLS_BYTES_TO_T_UINT_8(0x23, 0x3E, 0xDC, 0x84, 0x7C, 0x7B, 0x24, 0x34), + MBEDTLS_BYTES_TO_T_UINT_8(0x14, 0xED, 0x7F, 0x86, 0x07, 0x6C, 0x57, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0x95, 0x06, 0xFE, 0x52, 0x12, 0x79, 0x69, 0x56), + MBEDTLS_BYTES_TO_T_UINT_8(0x84, 0xD1, 0x44, 0x5F, 0x21, 0x3A, 0xC3, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x5E, 0xD9, 0x4A, 0xC0, 0x75, 0xAB, 0x17, 0xAC), + MBEDTLS_BYTES_TO_T_UINT_8(0xFF, 0x81, 0x94, 0xB6, 0x80, 0x6B, 0x6F, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xBE, 0x8E, 0xA5, 0xAA, 0xBC, 0x1E, 0x3E), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_25_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x89, 0xC7, 0x85, 0xA6, 0x59, 0x9B, 0xB1, 0x52), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0xCE, 0x40, 0xD1, 0xFB, 0xDF, 0x94, 0xF7), + MBEDTLS_BYTES_TO_T_UINT_8(0x18, 0xB8, 0x5E, 0xBF, 0x45, 0xA8, 0x2D, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x98, 0x9C, 0x06, 0x1B, 0xA9, 0x57, 0xB9, 0x79), + MBEDTLS_BYTES_TO_T_UINT_8(0x53, 0xE9, 0xCE, 0xA2, 0xD3, 0x74, 0xA1, 0x3C), + MBEDTLS_BYTES_TO_T_UINT_8(0xAA, 0x5F, 0x34, 0x78, 0xDB, 0xAE, 0x3A, 0x14), + MBEDTLS_BYTES_TO_T_UINT_8(0x7D, 0x32, 0x84, 0x3E, 0x68, 0x6A, 0x43, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0xBC, 0x39, 0x36, 0xA4, 0xC5, 0xBB, 0x11), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_26_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x8C, 0x07, 0xA2, 0xB5, 0xC9, 0x0F, 0x4D, 0x0F), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0x1D, 0x67, 0xE6, 0xF1, 0x46, 0xEB, 0x71), + MBEDTLS_BYTES_TO_T_UINT_8(0xD7, 0x41, 0x23, 0x95, 0xE7, 0xE0, 0x10, 0xDD), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0x69, 0xFE, 0x68, 0x8C, 0xC6, 0x5F, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0xE3, 0xB9, 0x2B, 0x3D, 0xD2, 0x4F, 0xD8, 0x1A), + MBEDTLS_BYTES_TO_T_UINT_8(0xA3, 0x09, 0xF5, 0x5F, 0xCF, 0xF6, 0x91, 0x57), + MBEDTLS_BYTES_TO_T_UINT_8(0x65, 0x15, 0x42, 0x6B, 0x6D, 0xB5, 0xF3, 0xB6), + MBEDTLS_BYTES_TO_T_UINT_8(0xBF, 0x56, 0x9D, 0xC5, 0xFF, 0xCA, 0x13, 0x9B), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_26_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x4D, 0x38, 0xE6, 0x23, 0x63, 0x48, 0x3C, 0xCA), + MBEDTLS_BYTES_TO_T_UINT_8(0xD2, 0x68, 0x3C, 0xD1, 0x3B, 0xE9, 0x3B, 0x82), + MBEDTLS_BYTES_TO_T_UINT_8(0xB5, 0x08, 0x54, 0x49, 0xD1, 0x46, 0x45, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0x70, 0x52, 0x6E, 0x79, 0xC4, 0x5E, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0xDF, 0xE8, 0x5A, 0x32, 0x81, 0xDA, 0xD3), + MBEDTLS_BYTES_TO_T_UINT_8(0x3C, 0x2D, 0x94, 0x5B, 0xB5, 0x35, 0x9F, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0x2A, 0x12, 0x8D, 0xC3, 0x36, 0x36, 0xB2, 0x2A), + MBEDTLS_BYTES_TO_T_UINT_8(0x39, 0x2F, 0x22, 0x38, 0x5B, 0x18, 0x4C, 0x35), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_27_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0xC1, 0x22, 0x0E, 0xF0, 0x73, 0x11, 0x05), + MBEDTLS_BYTES_TO_T_UINT_8(0xB2, 0xAE, 0xA4, 0x56, 0x18, 0x61, 0x66, 0x12), + MBEDTLS_BYTES_TO_T_UINT_8(0x79, 0xFB, 0x72, 0x08, 0x84, 0x38, 0x51, 0xB0), + MBEDTLS_BYTES_TO_T_UINT_8(0xDA, 0x86, 0xA8, 0xB9, 0x31, 0x99, 0x29, 0xC3), + MBEDTLS_BYTES_TO_T_UINT_8(0x8A, 0xFB, 0xC3, 0x42, 0xB3, 0xC7, 0x6F, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0xD8, 0xF8, 0xE1, 0x09, 0xBE, 0x75, 0xB0, 0x22), + MBEDTLS_BYTES_TO_T_UINT_8(0x5A, 0x7D, 0xFF, 0xF4, 0x99, 0xFC, 0x13, 0xAB), + MBEDTLS_BYTES_TO_T_UINT_8(0xE6, 0x1B, 0x84, 0x81, 0x42, 0x22, 0xC6, 0x3D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_27_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x21, 0xE0, 0x37, 0xA4, 0xA0, 0x2F, 0x38, 0x7F), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x3D, 0xB7, 0x40, 0x2F, 0x39, 0x3C, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x7A, 0x3B, 0x8A, 0x51, 0xAE, 0x40, 0x49, 0x7A), + MBEDTLS_BYTES_TO_T_UINT_8(0x36, 0x20, 0x9F, 0xDD, 0xA9, 0xD0, 0x77, 0xC7), + MBEDTLS_BYTES_TO_T_UINT_8(0x78, 0x1D, 0x64, 0xDA, 0xA0, 0x53, 0xC7, 0x7D), + MBEDTLS_BYTES_TO_T_UINT_8(0x37, 0x7B, 0x66, 0x55, 0x94, 0xD1, 0x51, 0x44), + MBEDTLS_BYTES_TO_T_UINT_8(0x0E, 0xA9, 0xB5, 0x5B, 0x38, 0x35, 0x40, 0xC0), + MBEDTLS_BYTES_TO_T_UINT_8(0xC8, 0xC9, 0x0F, 0xF0, 0x73, 0x79, 0x43, 0x61), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_28_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0x47, 0x45, 0x69, 0x80, 0x72, 0x72, 0x42), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x11, 0x99, 0x59, 0xDB, 0x48, 0x80, 0x39), + MBEDTLS_BYTES_TO_T_UINT_8(0x75, 0x6E, 0x3D, 0xFC, 0x37, 0x15, 0xF4, 0xBF), + MBEDTLS_BYTES_TO_T_UINT_8(0x17, 0xBB, 0x5B, 0xA6, 0x35, 0x8D, 0x28, 0x20), + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0x1A, 0x3B, 0x2C, 0x8F, 0xD3, 0xAA, 0x2D), + MBEDTLS_BYTES_TO_T_UINT_8(0x55, 0x1C, 0x1A, 0xF8, 0x02, 0xD9, 0x7B, 0x41), + MBEDTLS_BYTES_TO_T_UINT_8(0xAF, 0x69, 0xAC, 0xF8, 0x54, 0x31, 0x14, 0xA1), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x8A, 0xE6, 0xDE, 0x58, 0xB9, 0xC4, 0x7A), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_28_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x57, 0x83, 0x52, 0xFE, 0xF9, 0x7B, 0xE9, 0x1F), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0xA2, 0x55, 0x46, 0x15, 0x49, 0xC1, 0x3A), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0xBC, 0x5C, 0x91, 0xBD, 0xB9, 0x9C, 0xF4), + MBEDTLS_BYTES_TO_T_UINT_8(0xBB, 0xFD, 0xB1, 0x4E, 0x5F, 0x74, 0xEE, 0x53), + MBEDTLS_BYTES_TO_T_UINT_8(0xB1, 0x8B, 0xD8, 0x8B, 0x17, 0x73, 0x1B, 0x96), + MBEDTLS_BYTES_TO_T_UINT_8(0x22, 0x92, 0xD7, 0x67, 0x06, 0xAD, 0x25, 0xCD), + MBEDTLS_BYTES_TO_T_UINT_8(0x01, 0x0F, 0x80, 0x24, 0xE2, 0x27, 0x5F, 0x8B), + MBEDTLS_BYTES_TO_T_UINT_8(0x61, 0x1C, 0xCE, 0xD0, 0x67, 0xCA, 0xD4, 0x0B), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_29_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x87, 0xF1, 0xDD, 0x33, 0x66, 0xF9, 0x05, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0x1D, 0xE5, 0x6B, 0x79, 0xBD, 0x48, 0x42, 0xAA), + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0x14, 0x52, 0xE3, 0x53, 0xB4, 0x50, 0xD4), + MBEDTLS_BYTES_TO_T_UINT_8(0x32, 0x84, 0x6C, 0xCF, 0xDA, 0xB2, 0x20, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0xD6, 0x1A, 0xE5, 0xE2, 0x29, 0x70, 0xCE), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0x61, 0xFE, 0xBB, 0x21, 0x82, 0xD1, 0xFE), + MBEDTLS_BYTES_TO_T_UINT_8(0x2C, 0xF0, 0x9C, 0x8B, 0x1A, 0x42, 0x30, 0x06), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0xD6, 0x49, 0x81, 0x92, 0xF1, 0xD0, 0x90), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_29_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x91, 0x93, 0x6A, 0xA6, 0x22, 0xE9, 0xD6), + MBEDTLS_BYTES_TO_T_UINT_8(0x09, 0xDC, 0xC3, 0x69, 0x11, 0x95, 0x7D, 0xEC), + MBEDTLS_BYTES_TO_T_UINT_8(0x1C, 0xA3, 0x9D, 0x87, 0x5E, 0x64, 0x41, 0xA2), + MBEDTLS_BYTES_TO_T_UINT_8(0xBE, 0x87, 0x5A, 0x15, 0xBD, 0x6E, 0x3C, 0x8D), + MBEDTLS_BYTES_TO_T_UINT_8(0xD0, 0x8D, 0x50, 0xCC, 0xCF, 0xB7, 0x8F, 0x0B), + MBEDTLS_BYTES_TO_T_UINT_8(0x38, 0x65, 0xCD, 0x31, 0x30, 0xF1, 0x68, 0x13), + MBEDTLS_BYTES_TO_T_UINT_8(0x10, 0x5C, 0x66, 0x67, 0x92, 0x30, 0x57, 0x95), + MBEDTLS_BYTES_TO_T_UINT_8(0x23, 0x9B, 0x01, 0x3D, 0x20, 0x8B, 0xD1, 0x0D), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_30_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xAB, 0xC0, 0xE6, 0x4F, 0xDE, 0x62, 0xAB, 0xB3), + MBEDTLS_BYTES_TO_T_UINT_8(0xA4, 0x48, 0xB3, 0x1C, 0x0F, 0x16, 0x93, 0x45), + MBEDTLS_BYTES_TO_T_UINT_8(0x77, 0x63, 0xBD, 0x1F, 0x16, 0x50, 0x56, 0x98), + MBEDTLS_BYTES_TO_T_UINT_8(0x5D, 0x06, 0xBC, 0xE9, 0x27, 0x1C, 0x9A, 0x7B), + MBEDTLS_BYTES_TO_T_UINT_8(0xF8, 0xFE, 0x21, 0xC5, 0x39, 0x55, 0xE1, 0xFD), + MBEDTLS_BYTES_TO_T_UINT_8(0xF6, 0xA8, 0xD0, 0x96, 0x0E, 0xB5, 0xB2, 0x84), + MBEDTLS_BYTES_TO_T_UINT_8(0x3D, 0xE7, 0x4B, 0xF3, 0x11, 0x0C, 0xC9, 0x5B), + MBEDTLS_BYTES_TO_T_UINT_8(0x43, 0x3A, 0xC4, 0x87, 0x71, 0xEE, 0xFA, 0x18), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_30_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xA7, 0x77, 0xEE, 0x81, 0x5E, 0x96, 0xEA, 0x4B), + MBEDTLS_BYTES_TO_T_UINT_8(0xEE, 0xDF, 0xA9, 0xF4, 0x4F, 0x7C, 0xB2, 0x43), + MBEDTLS_BYTES_TO_T_UINT_8(0x9F, 0xD4, 0xDF, 0x35, 0x63, 0x47, 0x25, 0x8A), + MBEDTLS_BYTES_TO_T_UINT_8(0xA5, 0x3D, 0xFF, 0xA4, 0x02, 0xC3, 0x95, 0x11), + MBEDTLS_BYTES_TO_T_UINT_8(0xD5, 0x10, 0x78, 0xD1, 0x2B, 0xB7, 0xBE, 0x0E), + MBEDTLS_BYTES_TO_T_UINT_8(0x0A, 0xE9, 0x57, 0xF9, 0xE0, 0xD8, 0xFC, 0xBC), + MBEDTLS_BYTES_TO_T_UINT_8(0xF3, 0xC4, 0x01, 0xD6, 0xB4, 0xE7, 0x78, 0xE2), + MBEDTLS_BYTES_TO_T_UINT_8(0x02, 0x6C, 0xB9, 0x13, 0xA4, 0xE8, 0x6D, 0x6F), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_31_X[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xE8, 0xB0, 0xC9, 0xCD, 0xBF, 0xA2, 0x1E, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0xDD, 0x4F, 0x86, 0x22, 0x9B, 0xEA, 0xE8, 0xBB), + MBEDTLS_BYTES_TO_T_UINT_8(0x50, 0x46, 0xDF, 0x43, 0xB9, 0x82, 0x2D, 0x0A), + MBEDTLS_BYTES_TO_T_UINT_8(0x07, 0x32, 0xF1, 0x4E, 0x95, 0x41, 0xAE, 0x8E), + MBEDTLS_BYTES_TO_T_UINT_8(0x52, 0x93, 0x26, 0xFC, 0xD3, 0x90, 0xDC, 0xEB), + MBEDTLS_BYTES_TO_T_UINT_8(0x04, 0x05, 0x45, 0xCA, 0xF9, 0x5A, 0x89, 0x93), + MBEDTLS_BYTES_TO_T_UINT_8(0xC5, 0x82, 0x63, 0x4E, 0x55, 0x1D, 0x3A, 0x08), + MBEDTLS_BYTES_TO_T_UINT_8(0x7C, 0x69, 0x52, 0x49, 0xE9, 0xED, 0x57, 0x34), +}; +static const mbedtls_mpi_uint brainpoolP512r1_T_31_Y[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x70, 0x64, 0xE9, 0xAC, 0x4C, 0x4A, 0xEA, 0x25), + MBEDTLS_BYTES_TO_T_UINT_8(0xE9, 0xE9, 0x0B, 0x99, 0xE7, 0xF9, 0xA9, 0x2C), + MBEDTLS_BYTES_TO_T_UINT_8(0x24, 0x0C, 0xC1, 0xF4, 0x8D, 0x07, 0xB6, 0xB1), + MBEDTLS_BYTES_TO_T_UINT_8(0xAD, 0x68, 0xFA, 0x35, 0xE4, 0x9E, 0xAE, 0xD9), + MBEDTLS_BYTES_TO_T_UINT_8(0xF0, 0x2D, 0x1A, 0x13, 0x8E, 0x02, 0xE2, 0x63), + MBEDTLS_BYTES_TO_T_UINT_8(0x27, 0x38, 0x28, 0x86, 0x46, 0x7B, 0x3A, 0xE1), + MBEDTLS_BYTES_TO_T_UINT_8(0x3F, 0x4C, 0x64, 0x59, 0x0A, 0xF9, 0x02, 0xC4), + MBEDTLS_BYTES_TO_T_UINT_8(0x41, 0x4F, 0x23, 0xA2, 0xC3, 0xD5, 0xEF, 0x42), +}; +static const mbedtls_ecp_point brainpoolP512r1_T[32] = { + ECP_POINT_INIT_XY_Z1(brainpoolP512r1_T_0_X, brainpoolP512r1_T_0_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_1_X, brainpoolP512r1_T_1_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_2_X, brainpoolP512r1_T_2_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_3_X, brainpoolP512r1_T_3_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_4_X, brainpoolP512r1_T_4_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_5_X, brainpoolP512r1_T_5_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_6_X, brainpoolP512r1_T_6_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_7_X, brainpoolP512r1_T_7_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_8_X, brainpoolP512r1_T_8_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_9_X, brainpoolP512r1_T_9_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_10_X, brainpoolP512r1_T_10_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_11_X, brainpoolP512r1_T_11_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_12_X, brainpoolP512r1_T_12_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_13_X, brainpoolP512r1_T_13_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_14_X, brainpoolP512r1_T_14_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_15_X, brainpoolP512r1_T_15_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_16_X, brainpoolP512r1_T_16_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_17_X, brainpoolP512r1_T_17_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_18_X, brainpoolP512r1_T_18_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_19_X, brainpoolP512r1_T_19_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_20_X, brainpoolP512r1_T_20_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_21_X, brainpoolP512r1_T_21_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_22_X, brainpoolP512r1_T_22_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_23_X, brainpoolP512r1_T_23_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_24_X, brainpoolP512r1_T_24_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_25_X, brainpoolP512r1_T_25_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_26_X, brainpoolP512r1_T_26_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_27_X, brainpoolP512r1_T_27_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_28_X, brainpoolP512r1_T_28_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_29_X, brainpoolP512r1_T_29_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_30_X, brainpoolP512r1_T_30_Y), + ECP_POINT_INIT_XY_Z0(brainpoolP512r1_T_31_X, brainpoolP512r1_T_31_Y), +}; +#else +#define brainpoolP512r1_T NULL +#endif +#endif /* MBEDTLS_ECP_DP_BP512R1_ENABLED */ + + +#if defined(ECP_LOAD_GROUP) || defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) || \ + defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +/* + * Create an MPI from embedded constants + * (assumes len is an exact multiple of sizeof(mbedtls_mpi_uint)) + */ +static inline void ecp_mpi_load(mbedtls_mpi *X, const mbedtls_mpi_uint *p, size_t len) +{ + X->s = 1; + X->n = len / sizeof(mbedtls_mpi_uint); + X->p = (mbedtls_mpi_uint *) p; +} +#endif + +#if defined(ECP_LOAD_GROUP) +/* + * Set an MPI to static value 1 + */ +static inline void ecp_mpi_set1(mbedtls_mpi *X) +{ + X->s = 1; + X->n = 1; + X->p = mpi_one; +} + +/* + * Make group available from embedded constants + */ +static int ecp_group_load(mbedtls_ecp_group *grp, + const mbedtls_mpi_uint *p, size_t plen, + const mbedtls_mpi_uint *a, size_t alen, + const mbedtls_mpi_uint *b, size_t blen, + const mbedtls_mpi_uint *gx, size_t gxlen, + const mbedtls_mpi_uint *gy, size_t gylen, + const mbedtls_mpi_uint *n, size_t nlen, + const mbedtls_ecp_point *T) +{ + ecp_mpi_load(&grp->P, p, plen); + if (a != NULL) { + ecp_mpi_load(&grp->A, a, alen); + } + ecp_mpi_load(&grp->B, b, blen); + ecp_mpi_load(&grp->N, n, nlen); + + ecp_mpi_load(&grp->G.X, gx, gxlen); + ecp_mpi_load(&grp->G.Y, gy, gylen); + ecp_mpi_set1(&grp->G.Z); + + grp->pbits = mbedtls_mpi_bitlen(&grp->P); + grp->nbits = mbedtls_mpi_bitlen(&grp->N); + + grp->h = 1; + + grp->T = (mbedtls_ecp_point *) T; + /* + * Set T_size to 0 to prevent T free by mbedtls_ecp_group_free. + */ + grp->T_size = 0; + + return 0; +} +#endif /* ECP_LOAD_GROUP */ + +#if defined(MBEDTLS_ECP_NIST_OPTIM) +/* Forward declarations */ +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) +static int ecp_mod_p192(mbedtls_mpi *); +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p192_raw(mbedtls_mpi_uint *Np, size_t Nn); +#endif +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) +static int ecp_mod_p224(mbedtls_mpi *); +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p224_raw(mbedtls_mpi_uint *X, size_t X_limbs); +#endif +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) +static int ecp_mod_p256(mbedtls_mpi *); +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p256_raw(mbedtls_mpi_uint *X, size_t X_limbs); +#endif +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +static int ecp_mod_p384(mbedtls_mpi *); +#endif +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) +static int ecp_mod_p521(mbedtls_mpi *); +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p521_raw(mbedtls_mpi_uint *N_p, size_t N_n); +#endif + +#define NIST_MODP(P) grp->modp = ecp_mod_ ## P; +#else +#define NIST_MODP(P) +#endif /* MBEDTLS_ECP_NIST_OPTIM */ + +/* Additional forward declarations */ +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +static int ecp_mod_p255(mbedtls_mpi *); +#endif +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +static int ecp_mod_p448(mbedtls_mpi *); +#endif +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) +static int ecp_mod_p192k1(mbedtls_mpi *); +#endif +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) +static int ecp_mod_p224k1(mbedtls_mpi *); +#endif +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +static int ecp_mod_p256k1(mbedtls_mpi *); +#endif + +#if defined(ECP_LOAD_GROUP) +#define LOAD_GROUP_A(G) ecp_group_load(grp, \ + G ## _p, sizeof(G ## _p), \ + G ## _a, sizeof(G ## _a), \ + G ## _b, sizeof(G ## _b), \ + G ## _gx, sizeof(G ## _gx), \ + G ## _gy, sizeof(G ## _gy), \ + G ## _n, sizeof(G ## _n), \ + G ## _T \ + ) + +#define LOAD_GROUP(G) ecp_group_load(grp, \ + G ## _p, sizeof(G ## _p), \ + NULL, 0, \ + G ## _b, sizeof(G ## _b), \ + G ## _gx, sizeof(G ## _gx), \ + G ## _gy, sizeof(G ## _gy), \ + G ## _n, sizeof(G ## _n), \ + G ## _T \ + ) +#endif /* ECP_LOAD_GROUP */ + +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +/* Constants used by ecp_use_curve25519() */ +static const mbedtls_mpi_sint curve25519_a24 = 0x01DB42; + +/* P = 2^255 - 19 */ +static const mbedtls_mpi_uint curve25519_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xED, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X7F) +}; + +/* N = 2^252 + 27742317777372353535851937790883648493 */ +static const mbedtls_mpi_uint curve25519_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0XED, 0XD3, 0XF5, 0X5C, 0X1A, 0X63, 0X12, 0X58), + MBEDTLS_BYTES_TO_T_UINT_8(0XD6, 0X9C, 0XF7, 0XA2, 0XDE, 0XF9, 0XDE, 0X14), + MBEDTLS_BYTES_TO_T_UINT_8(0X00, 0X00, 0X00, 0X00, 0x00, 0x00, 0x00, 0x00), + MBEDTLS_BYTES_TO_T_UINT_8(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10) +}; + +/* + * Specialized function for creating the Curve25519 group + */ +static int ecp_use_curve25519(mbedtls_ecp_group *grp) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Actually ( A + 2 ) / 4 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&grp->A, curve25519_a24)); + + ecp_mpi_load(&grp->P, curve25519_p, sizeof(curve25519_p)); + + grp->pbits = mbedtls_mpi_bitlen(&grp->P); + + ecp_mpi_load(&grp->N, curve25519_n, sizeof(curve25519_n)); + + /* Y intentionally not set, since we use x/z coordinates. + * This is used as a marker to identify Montgomery curves! */ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&grp->G.X, 9)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&grp->G.Z, 1)); + mbedtls_mpi_free(&grp->G.Y); + + /* Actually, the required msb for private keys */ + grp->nbits = 254; + +cleanup: + if (ret != 0) { + mbedtls_ecp_group_free(grp); + } + + return ret; +} +#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +/* Constants used by ecp_use_curve448() */ +static const mbedtls_mpi_sint curve448_a24 = 0x98AA; + +/* P = 2^448 - 2^224 - 1 */ +static const mbedtls_mpi_uint curve448_p[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFE, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00) +}; + +/* N = 2^446 - 13818066809895115352007386748515426880336692474882178609894547503885 */ +static const mbedtls_mpi_uint curve448_n[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0XF3, 0X44, 0X58, 0XAB, 0X92, 0XC2, 0X78, 0X23), + MBEDTLS_BYTES_TO_T_UINT_8(0X55, 0X8F, 0XC5, 0X8D, 0X72, 0XC2, 0X6C, 0X21), + MBEDTLS_BYTES_TO_T_UINT_8(0X90, 0X36, 0XD6, 0XAE, 0X49, 0XDB, 0X4E, 0XC4), + MBEDTLS_BYTES_TO_T_UINT_8(0XE9, 0X23, 0XCA, 0X7C, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF), + MBEDTLS_BYTES_TO_T_UINT_8(0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0X3F), + MBEDTLS_BYTES_TO_T_UINT_8(0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00) +}; + +/* + * Specialized function for creating the Curve448 group + */ +static int ecp_use_curve448(mbedtls_ecp_group *grp) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Actually ( A + 2 ) / 4 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&grp->A, curve448_a24)); + + ecp_mpi_load(&grp->P, curve448_p, sizeof(curve448_p)); + grp->pbits = mbedtls_mpi_bitlen(&grp->P); + + /* Y intentionally not set, since we use x/z coordinates. + * This is used as a marker to identify Montgomery curves! */ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&grp->G.X, 5)); + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&grp->G.Z, 1)); + mbedtls_mpi_free(&grp->G.Y); + + ecp_mpi_load(&grp->N, curve448_n, sizeof(curve448_n)); + + /* Actually, the required msb for private keys */ + grp->nbits = 447; + +cleanup: + if (ret != 0) { + mbedtls_ecp_group_free(grp); + } + + return ret; +} +#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */ + +/* + * Set a group using well-known domain parameters + */ +int mbedtls_ecp_group_load(mbedtls_ecp_group *grp, mbedtls_ecp_group_id id) +{ + ECP_VALIDATE_RET(grp != NULL); + mbedtls_ecp_group_free(grp); + + mbedtls_ecp_group_init(grp); + + grp->id = id; + + switch (id) { +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + case MBEDTLS_ECP_DP_SECP192R1: + NIST_MODP(p192); + return LOAD_GROUP(secp192r1); +#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + case MBEDTLS_ECP_DP_SECP224R1: + NIST_MODP(p224); + return LOAD_GROUP(secp224r1); +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + case MBEDTLS_ECP_DP_SECP256R1: + NIST_MODP(p256); + return LOAD_GROUP(secp256r1); +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + case MBEDTLS_ECP_DP_SECP384R1: + NIST_MODP(p384); + return LOAD_GROUP(secp384r1); +#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + case MBEDTLS_ECP_DP_SECP521R1: + NIST_MODP(p521); + return LOAD_GROUP(secp521r1); +#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) + case MBEDTLS_ECP_DP_SECP192K1: + grp->modp = ecp_mod_p192k1; + return LOAD_GROUP_A(secp192k1); +#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) + case MBEDTLS_ECP_DP_SECP224K1: + grp->modp = ecp_mod_p224k1; + return LOAD_GROUP_A(secp224k1); +#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) + case MBEDTLS_ECP_DP_SECP256K1: + grp->modp = ecp_mod_p256k1; + return LOAD_GROUP_A(secp256k1); +#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) + case MBEDTLS_ECP_DP_BP256R1: + return LOAD_GROUP_A(brainpoolP256r1); +#endif /* MBEDTLS_ECP_DP_BP256R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) + case MBEDTLS_ECP_DP_BP384R1: + return LOAD_GROUP_A(brainpoolP384r1); +#endif /* MBEDTLS_ECP_DP_BP384R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) + case MBEDTLS_ECP_DP_BP512R1: + return LOAD_GROUP_A(brainpoolP512r1); +#endif /* MBEDTLS_ECP_DP_BP512R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + case MBEDTLS_ECP_DP_CURVE25519: + grp->modp = ecp_mod_p255; + return ecp_use_curve25519(grp); +#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + case MBEDTLS_ECP_DP_CURVE448: + grp->modp = ecp_mod_p448; + return ecp_use_curve448(grp); +#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */ + + default: + grp->id = MBEDTLS_ECP_DP_NONE; + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } +} + +#if defined(MBEDTLS_ECP_NIST_OPTIM) +/* + * Fast reduction modulo the primes used by the NIST curves. + * + * These functions are critical for speed, but not needed for correct + * operations. So, we make the choice to heavily rely on the internals of our + * bignum library, which creates a tight coupling between these functions and + * our MPI implementation. However, the coupling between the ECP module and + * MPI remains loose, since these functions can be deactivated at will. + */ + +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) +/* + * Compared to the way things are presented in FIPS 186-3 D.2, + * we proceed in columns, from right (least significant chunk) to left, + * adding chunks to N in place, and keeping a carry for the next chunk. + * This avoids moving things around in memory, and uselessly adding zeros, + * compared to the more straightforward, line-oriented approach. + * + * For this prime we need to handle data in chunks of 64 bits. + * Since this is always a multiple of our basic mbedtls_mpi_uint, we can + * use a mbedtls_mpi_uint * to designate such a chunk, and small loops to handle it. + */ + +/* Add 64-bit chunks (dst += src) and update carry */ +static inline void add64(mbedtls_mpi_uint *dst, mbedtls_mpi_uint *src, mbedtls_mpi_uint *carry) +{ + unsigned char i; + mbedtls_mpi_uint c = 0; + for (i = 0; i < 8 / sizeof(mbedtls_mpi_uint); i++, dst++, src++) { + *dst += c; c = (*dst < c); + *dst += *src; c += (*dst < *src); + } + *carry += c; +} + +/* Add carry to a 64-bit chunk and update carry */ +static inline void carry64(mbedtls_mpi_uint *dst, mbedtls_mpi_uint *carry) +{ + unsigned char i; + for (i = 0; i < 8 / sizeof(mbedtls_mpi_uint); i++, dst++) { + *dst += *carry; + *carry = (*dst < *carry); + } +} + +#define WIDTH 8 / sizeof(mbedtls_mpi_uint) +#define A(i) Np + (i) * WIDTH +#define ADD(i) add64(p, A(i), &c) +#define NEXT p += WIDTH; carry64(p, &c) +#define LAST p += WIDTH; *p = c; while (++p < end) *p = 0 +#define RESET last_carry[0] = c; c = 0; p = Np +#define ADD_LAST add64(p, last_carry, &c) + +/* + * Fast quasi-reduction modulo p192 (FIPS 186-3 D.2.1) + */ +static int ecp_mod_p192(mbedtls_mpi *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t expected_width = 2 * ((192 + biL - 1) / biL); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(N, expected_width)); + ret = mbedtls_ecp_mod_p192_raw(N->p, expected_width); + +cleanup: + return ret; +} + +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p192_raw(mbedtls_mpi_uint *Np, size_t Nn) +{ + mbedtls_mpi_uint c = 0, last_carry[WIDTH] = { 0 }; + mbedtls_mpi_uint *p, *end; + + if (Nn != 2*((192 + biL - 1)/biL)) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + p = Np; + end = p + Nn; + + ADD(3); ADD(5); NEXT; // A0 += A3 + A5 + ADD(3); ADD(4); ADD(5); NEXT; // A1 += A3 + A4 + A5 + ADD(4); ADD(5); // A2 += A4 + A5 + + RESET; + + /* Use the reduction for the carry as well: + * 2^192 * last_carry = 2^64 * last_carry + last_carry mod P192 + */ + ADD_LAST; NEXT; // A0 += last_carry + ADD_LAST; NEXT; // A1 += last_carry + + LAST; // A2 += carry + + return 0; +} + +#undef WIDTH +#undef A +#undef ADD +#undef NEXT +#undef LAST +#undef RESET +#undef ADD_LAST +#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + +/* + * The reader is advised to first understand ecp_mod_p192() since the same + * general structure is used here, but with additional complications: + * (1) chunks of 32 bits, and (2) subtractions. + */ + +/* + * For these primes, we need to handle data in chunks of 32 bits. + * This makes it more complicated if we use 64 bits limbs in MPI, + * which prevents us from using a uniform access method as for p192. + * + * So, we define a mini abstraction layer to access 32 bit chunks, + * load them in 'cur' for work, and store them back from 'cur' when done. + * + * While at it, also define the size of N in terms of 32-bit chunks. + */ +#define LOAD32 cur = A(i); + +#if defined(MBEDTLS_HAVE_INT32) /* 32 bit */ + +#define MAX32 X_limbs +#define A(j) X[j] +#define STORE32 X[i] = (mbedtls_mpi_uint) cur; +#define STORE0 X[i] = 0; + +#else /* 64 bit */ + +#define MAX32 X_limbs * 2 +#define A(j) \ + (j) % 2 ? \ + (uint32_t) (X[(j) / 2] >> 32) : \ + (uint32_t) (X[(j) / 2]) +#define STORE32 \ + if (i % 2) { \ + X[i/2] &= 0x00000000FFFFFFFF; \ + X[i/2] |= (uint64_t) (cur) << 32; \ + } else { \ + X[i/2] &= 0xFFFFFFFF00000000; \ + X[i/2] |= (uint32_t) cur; \ + } + +#define STORE0 \ + if (i % 2) { \ + X[i/2] &= 0x00000000FFFFFFFF; \ + } else { \ + X[i/2] &= 0xFFFFFFFF00000000; \ + } + +#endif + +static inline int8_t extract_carry(int64_t cur) +{ + return (int8_t) (cur >> 32); +} + +#define ADD(j) cur += A(j) +#define SUB(j) cur -= A(j) + +#define ADD_CARRY(cc) cur += (cc) +#define SUB_CARRY(cc) cur -= (cc) + +#define ADD_LAST ADD_CARRY(last_c) +#define SUB_LAST SUB_CARRY(last_c) + +/* + * Helpers for the main 'loop' + */ +#define INIT(b) \ + int8_t c = 0, last_c; \ + int64_t cur; \ + size_t i = 0; \ + LOAD32; + +#define NEXT \ + c = extract_carry(cur); \ + STORE32; i++; LOAD32; \ + ADD_CARRY(c); + +#define RESET \ + c = extract_carry(cur); \ + last_c = c; \ + STORE32; i = 0; LOAD32; \ + c = 0; \ + +#define LAST \ + c = extract_carry(cur); \ + STORE32; i++; \ + if (c != 0) \ + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; \ + while (i < MAX32) { STORE0; i++; } + +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + +/* + * Fast quasi-reduction modulo p224 (FIPS 186-3 D.2.2) + */ +static int ecp_mod_p224(mbedtls_mpi *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t expected_width = 2 * 224 / biL; + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(N, expected_width)); + ret = mbedtls_ecp_mod_p224_raw(N->p, expected_width); +cleanup: + return ret; +} + +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p224_raw(mbedtls_mpi_uint *X, size_t X_limbs) +{ + if (X_limbs != 2 * 224 / biL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + INIT(224); + + SUB(7); SUB(11); NEXT; // A0 += -A7 - A11 + SUB(8); SUB(12); NEXT; // A1 += -A8 - A12 + SUB(9); SUB(13); NEXT; // A2 += -A9 - A13 + SUB(10); ADD(7); ADD(11); NEXT; // A3 += -A10 + A7 + A11 + SUB(11); ADD(8); ADD(12); NEXT; // A4 += -A11 + A8 + A12 + SUB(12); ADD(9); ADD(13); NEXT; // A5 += -A12 + A9 + A13 + SUB(13); ADD(10); // A6 += -A13 + A10 + + RESET; + + /* Use 2^224 = P + 2^96 - 1 to modulo reduce the final carry */ + SUB_LAST; NEXT; // A0 -= last_c + ; NEXT; // A1 + ; NEXT; // A2 + ADD_LAST; NEXT; // A3 += last_c + ; NEXT; // A4 + ; NEXT; // A5 + // A6 + + /* The carry reduction cannot generate a carry + * (see commit 73e8553 for details)*/ + + LAST; + + return 0; +} + +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + +/* + * Fast quasi-reduction modulo p256 (FIPS 186-3 D.2.3) + */ +static int ecp_mod_p256(mbedtls_mpi *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t expected_width = 2 * 256 / biL; + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(N, expected_width)); + ret = mbedtls_ecp_mod_p256_raw(N->p, expected_width); +cleanup: + return ret; +} + +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p256_raw(mbedtls_mpi_uint *X, size_t X_limbs) +{ + if (X_limbs != 2 * 256 / biL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + INIT(256); + + ADD(8); ADD(9); + SUB(11); SUB(12); SUB(13); SUB(14); NEXT; // A0 + + ADD(9); ADD(10); + SUB(12); SUB(13); SUB(14); SUB(15); NEXT; // A1 + + ADD(10); ADD(11); + SUB(13); SUB(14); SUB(15); NEXT; // A2 + + ADD(11); ADD(11); ADD(12); ADD(12); ADD(13); + SUB(15); SUB(8); SUB(9); NEXT; // A3 + + ADD(12); ADD(12); ADD(13); ADD(13); ADD(14); + SUB(9); SUB(10); NEXT; // A4 + + ADD(13); ADD(13); ADD(14); ADD(14); ADD(15); + SUB(10); SUB(11); NEXT; // A5 + + ADD(14); ADD(14); ADD(15); ADD(15); ADD(14); ADD(13); + SUB(8); SUB(9); NEXT; // A6 + + ADD(15); ADD(15); ADD(15); ADD(8); + SUB(10); SUB(11); SUB(12); SUB(13); // A7 + + RESET; + + /* Use 2^224 * (2^32 - 1) + 2^192 + 2^96 - 1 + * to modulo reduce the final carry. */ + ADD_LAST; NEXT; // A0 + ; NEXT; // A1 + ; NEXT; // A2 + SUB_LAST; NEXT; // A3 + ; NEXT; // A4 + ; NEXT; // A5 + SUB_LAST; NEXT; // A6 + ADD_LAST; // A7 + + RESET; + + /* Use 2^224 * (2^32 - 1) + 2^192 + 2^96 - 1 + * to modulo reduce the carry generated by the previous reduction. */ + ADD_LAST; NEXT; // A0 + ; NEXT; // A1 + ; NEXT; // A2 + SUB_LAST; NEXT; // A3 + ; NEXT; // A4 + ; NEXT; // A5 + SUB_LAST; NEXT; // A6 + ADD_LAST; // A7 + + LAST; + + return 0; +} + +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */ + +#undef LOAD32 +#undef MAX32 +#undef A +#undef STORE32 +#undef STORE0 +#undef ADD +#undef SUB +#undef ADD_CARRY +#undef SUB_CARRY +#undef ADD_LAST +#undef SUB_LAST +#undef INIT +#undef NEXT +#undef RESET +#undef LAST + +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED || + MBEDTLS_ECP_DP_SECP256R1_ENABLED || + MBEDTLS_ECP_DP_SECP384R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +/* + * The reader is advised to first understand ecp_mod_p192() since the same + * general structure is used here, but with additional complications: + * (1) chunks of 32 bits, and (2) subtractions. + */ + +/* + * For these primes, we need to handle data in chunks of 32 bits. + * This makes it more complicated if we use 64 bits limbs in MPI, + * which prevents us from using a uniform access method as for p192. + * + * So, we define a mini abstraction layer to access 32 bit chunks, + * load them in 'cur' for work, and store them back from 'cur' when done. + * + * While at it, also define the size of N in terms of 32-bit chunks. + */ +#define LOAD32 cur = A(i); + +#if defined(MBEDTLS_HAVE_INT32) /* 32 bit */ + +#define MAX32 N->n +#define A(j) N->p[j] +#define STORE32 N->p[i] = cur; + +#else /* 64-bit */ + +#define MAX32 N->n * 2 +#define A(j) (j) % 2 ? (uint32_t) (N->p[(j)/2] >> 32) : \ + (uint32_t) (N->p[(j)/2]) +#define STORE32 \ + if (i % 2) { \ + N->p[i/2] &= 0x00000000FFFFFFFF; \ + N->p[i/2] |= ((mbedtls_mpi_uint) cur) << 32; \ + } else { \ + N->p[i/2] &= 0xFFFFFFFF00000000; \ + N->p[i/2] |= (mbedtls_mpi_uint) cur; \ + } + +#endif /* sizeof( mbedtls_mpi_uint ) */ + +/* + * Helpers for addition and subtraction of chunks, with signed carry. + */ +static inline void add32(uint32_t *dst, uint32_t src, signed char *carry) +{ + *dst += src; + *carry += (*dst < src); +} + +static inline void sub32(uint32_t *dst, uint32_t src, signed char *carry) +{ + *carry -= (*dst < src); + *dst -= src; +} + +#define ADD(j) add32(&cur, A(j), &c); +#define SUB(j) sub32(&cur, A(j), &c); + +/* + * Helpers for the main 'loop' + */ +#define INIT(b) \ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; \ + signed char c = 0, cc; \ + uint32_t cur; \ + size_t i = 0, bits = (b); \ + /* N is the size of the product of two b-bit numbers, plus one */ \ + /* limb for fix_negative */ \ + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(N, (b) * 2 / biL + 1)); \ + LOAD32; + +#define NEXT \ + STORE32; i++; LOAD32; \ + cc = c; c = 0; \ + if (cc < 0) \ + sub32(&cur, -cc, &c); \ + else \ + add32(&cur, cc, &c); \ + +#define LAST \ + STORE32; i++; \ + cur = c > 0 ? c : 0; STORE32; \ + cur = 0; while (++i < MAX32) { STORE32; } \ + if (c < 0) mbedtls_ecp_fix_negative(N, c, bits); + +/* + * If the result is negative, we get it in the form + * c * 2^bits + N, with c negative and N positive shorter than 'bits' + */ +MBEDTLS_STATIC_TESTABLE +void mbedtls_ecp_fix_negative(mbedtls_mpi *N, signed char c, size_t bits) +{ + size_t i; + + /* Set N := 2^bits - 1 - N. We know that 0 <= N < 2^bits, so + * set the absolute value to 0xfff...fff - N. There is no carry + * since we're subtracting from all-bits-one. */ + for (i = 0; i <= bits / 8 / sizeof(mbedtls_mpi_uint); i++) { + N->p[i] = ~(mbedtls_mpi_uint) 0 - N->p[i]; + } + /* Add 1, taking care of the carry. */ + i = 0; + do { + ++N->p[i]; + } while (N->p[i++] == 0 && i <= bits / 8 / sizeof(mbedtls_mpi_uint)); + /* Invert the sign. + * Now N = N0 - 2^bits where N0 is the initial value of N. */ + N->s = -1; + + /* Add |c| * 2^bits to the absolute value. Since c and N are + * negative, this adds c * 2^bits. */ + mbedtls_mpi_uint msw = (mbedtls_mpi_uint) -c; +#if defined(MBEDTLS_HAVE_INT64) + if (bits == 224) { + msw <<= 32; + } +#endif + N->p[bits / 8 / sizeof(mbedtls_mpi_uint)] += msw; +} + +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +/* + * Fast quasi-reduction modulo p384 (FIPS 186-3 D.2.4) + */ +static int ecp_mod_p384(mbedtls_mpi *N) +{ + INIT(384); + + ADD(12); ADD(21); ADD(20); + SUB(23); NEXT; // A0 + + ADD(13); ADD(22); ADD(23); + SUB(12); SUB(20); NEXT; // A2 + + ADD(14); ADD(23); + SUB(13); SUB(21); NEXT; // A2 + + ADD(15); ADD(12); ADD(20); ADD(21); + SUB(14); SUB(22); SUB(23); NEXT; // A3 + + ADD(21); ADD(21); ADD(16); ADD(13); ADD(12); ADD(20); ADD(22); + SUB(15); SUB(23); SUB(23); NEXT; // A4 + + ADD(22); ADD(22); ADD(17); ADD(14); ADD(13); ADD(21); ADD(23); + SUB(16); NEXT; // A5 + + ADD(23); ADD(23); ADD(18); ADD(15); ADD(14); ADD(22); + SUB(17); NEXT; // A6 + + ADD(19); ADD(16); ADD(15); ADD(23); + SUB(18); NEXT; // A7 + + ADD(20); ADD(17); ADD(16); + SUB(19); NEXT; // A8 + + ADD(21); ADD(18); ADD(17); + SUB(20); NEXT; // A9 + + ADD(22); ADD(19); ADD(18); + SUB(21); NEXT; // A10 + + ADD(23); ADD(20); ADD(19); + SUB(22); LAST; // A11 + +cleanup: + return ret; +} +#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */ + +#undef A +#undef LOAD32 +#undef STORE32 +#undef MAX32 +#undef INIT +#undef NEXT +#undef LAST + +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED || + MBEDTLS_ECP_DP_SECP384R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) +/* Size of p521 in terms of mbedtls_mpi_uint */ +#define P521_WIDTH (521 / 8 / sizeof(mbedtls_mpi_uint) + 1) + +/* Bits to keep in the most significant mbedtls_mpi_uint */ +#define P521_MASK 0x01FF + +/* + * Fast quasi-reduction modulo p521 = 2^521 - 1 (FIPS 186-3 D.2.5) + */ +static int ecp_mod_p521(mbedtls_mpi *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t expected_width = 2 * P521_WIDTH; + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(N, expected_width)); + ret = mbedtls_ecp_mod_p521_raw(N->p, expected_width); +cleanup: + return ret; +} + +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p521_raw(mbedtls_mpi_uint *X, size_t X_limbs) +{ + mbedtls_mpi_uint carry = 0; + + if (X_limbs != 2 * P521_WIDTH || X[2 * P521_WIDTH - 1] != 0) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* Step 1: Reduction to P521_WIDTH limbs */ + /* Helper references for bottom part of X */ + mbedtls_mpi_uint *X0 = X; + size_t X0_limbs = P521_WIDTH; + /* Helper references for top part of X */ + mbedtls_mpi_uint *X1 = X + X0_limbs; + size_t X1_limbs = X_limbs - X0_limbs; + /* Split X as X0 + 2^P521_WIDTH X1 and compute X0 + 2^(biL - 9) X1. + * (We are using that 2^P521_WIDTH = 2^(512 + biL) and that + * 2^(512 + biL) X1 = 2^(biL - 9) X1 mod P521.) + * The high order limb of the result will be held in carry and the rest + * in X0 (that is the result will be represented as + * 2^P521_WIDTH carry + X0). + * + * Also, note that the resulting carry is either 0 or 1: + * X0 < 2^P521_WIDTH = 2^(512 + biL) and X1 < 2^(P521_WIDTH-biL) = 2^512 + * therefore + * X0 + 2^(biL - 9) X1 < 2^(512 + biL) + 2^(512 + biL - 9) + * which in turn is less than 2 * 2^(512 + biL). + */ + mbedtls_mpi_uint shift = ((mbedtls_mpi_uint) 1u) << (biL - 9); + carry = mbedtls_mpi_core_mla(X0, X0_limbs, X1, X1_limbs, shift); + /* Set X to X0 (by clearing the top part). */ + memset(X1, 0, X1_limbs * sizeof(mbedtls_mpi_uint)); + + /* Step 2: Reduction modulo P521 + * + * At this point X is reduced to P521_WIDTH limbs. What remains is to add + * the carry (that is 2^P521_WIDTH carry) and to reduce mod P521. */ + + /* 2^P521_WIDTH carry = 2^(512 + biL) carry = 2^(biL - 9) carry mod P521. + * Also, recall that carry is either 0 or 1. */ + mbedtls_mpi_uint addend = carry << (biL - 9); + /* Keep the top 9 bits and reduce the rest, using 2^521 = 1 mod P521. */ + addend += (X[P521_WIDTH - 1] >> 9); + X[P521_WIDTH - 1] &= P521_MASK; + + /* Reuse the top part of X (already zeroed) as a helper array for + * carrying out the addition. */ + mbedtls_mpi_uint *addend_arr = X + P521_WIDTH; + addend_arr[0] = addend; + (void) mbedtls_mpi_core_add(X, X, addend_arr, P521_WIDTH); + /* Both addends were less than P521 therefore X < 2 * P521. (This also means + * that the result fit in P521_WIDTH limbs and there won't be any carry.) */ + + /* Clear the reused part of X. */ + addend_arr[0] = 0; + + return 0; +} + +#undef P521_WIDTH +#undef P521_MASK + +#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */ + +#endif /* MBEDTLS_ECP_NIST_OPTIM */ + +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + +/* Size of p255 in terms of mbedtls_mpi_uint */ +#define P255_WIDTH (255 / 8 / sizeof(mbedtls_mpi_uint) + 1) + +/* + * Fast quasi-reduction modulo p255 = 2^255 - 19 + * Write N as A0 + 2^256 A1, return A0 + 38 * A1 + */ +static int ecp_mod_p255(mbedtls_mpi *N) +{ + mbedtls_mpi_uint Mp[P255_WIDTH]; + + /* Helper references for top part of N */ + mbedtls_mpi_uint * const NT_p = N->p + P255_WIDTH; + const size_t NT_n = N->n - P255_WIDTH; + if (N->n <= P255_WIDTH) { + return 0; + } + if (NT_n > P255_WIDTH) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* Split N as N + 2^256 M */ + memcpy(Mp, NT_p, sizeof(mbedtls_mpi_uint) * NT_n); + memset(NT_p, 0, sizeof(mbedtls_mpi_uint) * NT_n); + + /* N = A0 + 38 * A1 */ + mbedtls_mpi_core_mla(N->p, P255_WIDTH + 1, + Mp, NT_n, + 38); + + return 0; +} +#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + +/* Size of p448 in terms of mbedtls_mpi_uint */ +#define P448_WIDTH (448 / 8 / sizeof(mbedtls_mpi_uint)) + +/* Number of limbs fully occupied by 2^224 (max), and limbs used by it (min) */ +#define DIV_ROUND_UP(X, Y) (((X) + (Y) -1) / (Y)) +#define P224_WIDTH_MIN (28 / sizeof(mbedtls_mpi_uint)) +#define P224_WIDTH_MAX DIV_ROUND_UP(28, sizeof(mbedtls_mpi_uint)) +#define P224_UNUSED_BITS ((P224_WIDTH_MAX * sizeof(mbedtls_mpi_uint) * 8) - 224) + +/* + * Fast quasi-reduction modulo p448 = 2^448 - 2^224 - 1 + * Write N as A0 + 2^448 A1 and A1 as B0 + 2^224 B1, and return + * A0 + A1 + B1 + (B0 + B1) * 2^224. This is different to the reference + * implementation of Curve448, which uses its own special 56-bit limbs rather + * than a generic bignum library. We could squeeze some extra speed out on + * 32-bit machines by splitting N up into 32-bit limbs and doing the + * arithmetic using the limbs directly as we do for the NIST primes above, + * but for 64-bit targets it should use half the number of operations if we do + * the reduction with 224-bit limbs, since mpi_add_mpi will then use 64-bit adds. + */ +static int ecp_mod_p448(mbedtls_mpi *N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i; + mbedtls_mpi M, Q; + mbedtls_mpi_uint Mp[P448_WIDTH + 1], Qp[P448_WIDTH]; + + if (N->n <= P448_WIDTH) { + return 0; + } + + /* M = A1 */ + M.s = 1; + M.n = N->n - (P448_WIDTH); + if (M.n > P448_WIDTH) { + /* Shouldn't be called with N larger than 2^896! */ + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + M.p = Mp; + memset(Mp, 0, sizeof(Mp)); + memcpy(Mp, N->p + P448_WIDTH, M.n * sizeof(mbedtls_mpi_uint)); + + /* N = A0 */ + for (i = P448_WIDTH; i < N->n; i++) { + N->p[i] = 0; + } + + /* N += A1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(N, N, &M)); + + /* Q = B1, N += B1 */ + Q = M; + Q.p = Qp; + memcpy(Qp, Mp, sizeof(Qp)); + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&Q, 224)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(N, N, &Q)); + + /* M = (B0 + B1) * 2^224, N += M */ + if (sizeof(mbedtls_mpi_uint) > 4) { + Mp[P224_WIDTH_MIN] &= ((mbedtls_mpi_uint)-1) >> (P224_UNUSED_BITS); + } + for (i = P224_WIDTH_MAX; i < M.n; ++i) { + Mp[i] = 0; + } + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&M, &M, &Q)); + M.n = P448_WIDTH + 1; /* Make room for shifted carry bit from the addition */ + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_l(&M, 224)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(N, N, &M)); + +cleanup: + return ret; +} +#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +/* + * Fast quasi-reduction modulo P = 2^s - R, + * with R about 33 bits, used by the Koblitz curves. + * + * Write N as A0 + 2^224 A1, return A0 + R * A1. + * Actually do two passes, since R is big. + */ +#define P_KOBLITZ_MAX (256 / 8 / sizeof(mbedtls_mpi_uint)) // Max limbs in P +#define P_KOBLITZ_R (8 / sizeof(mbedtls_mpi_uint)) // Limbs in R +static inline int ecp_mod_koblitz(mbedtls_mpi *N, mbedtls_mpi_uint *Rp, size_t p_limbs, + size_t adjust, size_t shift, mbedtls_mpi_uint mask) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i; + mbedtls_mpi M, R; + mbedtls_mpi_uint Mp[P_KOBLITZ_MAX + P_KOBLITZ_R + 1]; + + if (N->n < p_limbs) { + return 0; + } + + /* Init R */ + R.s = 1; + R.p = Rp; + R.n = P_KOBLITZ_R; + + /* Common setup for M */ + M.s = 1; + M.p = Mp; + + /* M = A1 */ + M.n = N->n - (p_limbs - adjust); + if (M.n > p_limbs + adjust) { + M.n = p_limbs + adjust; + } + memset(Mp, 0, sizeof(Mp)); + memcpy(Mp, N->p + p_limbs - adjust, M.n * sizeof(mbedtls_mpi_uint)); + if (shift != 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&M, shift)); + } + M.n += R.n; /* Make room for multiplication by R */ + + /* N = A0 */ + if (mask != 0) { + N->p[p_limbs - 1] &= mask; + } + for (i = p_limbs; i < N->n; i++) { + N->p[i] = 0; + } + + /* N = A0 + R * A1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&M, &M, &R)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_abs(N, N, &M)); + + /* Second pass */ + + /* M = A1 */ + M.n = N->n - (p_limbs - adjust); + if (M.n > p_limbs + adjust) { + M.n = p_limbs + adjust; + } + memset(Mp, 0, sizeof(Mp)); + memcpy(Mp, N->p + p_limbs - adjust, M.n * sizeof(mbedtls_mpi_uint)); + if (shift != 0) { + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&M, shift)); + } + M.n += R.n; /* Make room for multiplication by R */ + + /* N = A0 */ + if (mask != 0) { + N->p[p_limbs - 1] &= mask; + } + for (i = p_limbs; i < N->n; i++) { + N->p[i] = 0; + } + + /* N = A0 + R * A1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&M, &M, &R)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_abs(N, N, &M)); + +cleanup: + return ret; +} +#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED) || + MBEDTLS_ECP_DP_SECP224K1_ENABLED) || + MBEDTLS_ECP_DP_SECP256K1_ENABLED) */ + +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) +/* + * Fast quasi-reduction modulo p192k1 = 2^192 - R, + * with R = 2^32 + 2^12 + 2^8 + 2^7 + 2^6 + 2^3 + 1 = 0x0100001119 + */ +static int ecp_mod_p192k1(mbedtls_mpi *N) +{ + static mbedtls_mpi_uint Rp[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xC9, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00) + }; + + return ecp_mod_koblitz(N, Rp, 192 / 8 / sizeof(mbedtls_mpi_uint), 0, 0, + 0); +} +#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) +/* + * Fast quasi-reduction modulo p224k1 = 2^224 - R, + * with R = 2^32 + 2^12 + 2^11 + 2^9 + 2^7 + 2^4 + 2 + 1 = 0x0100001A93 + */ +static int ecp_mod_p224k1(mbedtls_mpi *N) +{ + static mbedtls_mpi_uint Rp[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0x93, 0x1A, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00) + }; + +#if defined(MBEDTLS_HAVE_INT64) + return ecp_mod_koblitz(N, Rp, 4, 1, 32, 0xFFFFFFFF); +#else + return ecp_mod_koblitz(N, Rp, 224 / 8 / sizeof(mbedtls_mpi_uint), 0, 0, + 0); +#endif +} + +#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +/* + * Fast quasi-reduction modulo p256k1 = 2^256 - R, + * with R = 2^32 + 2^9 + 2^8 + 2^7 + 2^6 + 2^4 + 1 = 0x01000003D1 + */ +static int ecp_mod_p256k1(mbedtls_mpi *N) +{ + static mbedtls_mpi_uint Rp[] = { + MBEDTLS_BYTES_TO_T_UINT_8(0xD1, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00) + }; + return ecp_mod_koblitz(N, Rp, 256 / 8 / sizeof(mbedtls_mpi_uint), 0, 0, + 0); +} +#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */ + +#if defined(MBEDTLS_TEST_HOOKS) +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_modulus_setup(mbedtls_mpi_mod_modulus *N, + const mbedtls_ecp_group_id id, + const mbedtls_ecp_curve_type ctype) +{ + mbedtls_mpi_uint *p = NULL; + size_t p_limbs; + + if (!(ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE || \ + ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_SCALAR)) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + switch (id) { +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + case MBEDTLS_ECP_DP_SECP192R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp192r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp192r1_p)); + } else { + p = (mbedtls_mpi_uint *) secp192r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp192r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + case MBEDTLS_ECP_DP_SECP224R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp224r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp224r1_p)); + } else { + p = (mbedtls_mpi_uint *) secp224r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp224r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + case MBEDTLS_ECP_DP_SECP256R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp256r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp256r1_p)); + } else { + p = (mbedtls_mpi_uint *) secp256r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp256r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + case MBEDTLS_ECP_DP_SECP384R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp384r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp384r1_p)); + } else { + p = (mbedtls_mpi_uint *) secp384r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp384r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + case MBEDTLS_ECP_DP_SECP521R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp521r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp521r1_p)); + } else { + p = (mbedtls_mpi_uint *) secp521r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp521r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) + case MBEDTLS_ECP_DP_BP256R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) brainpoolP256r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(brainpoolP256r1_p)); + } else { + p = (mbedtls_mpi_uint *) brainpoolP256r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(brainpoolP256r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) + case MBEDTLS_ECP_DP_BP384R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) brainpoolP384r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(brainpoolP384r1_p)); + } else { + p = (mbedtls_mpi_uint *) brainpoolP384r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(brainpoolP384r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) + case MBEDTLS_ECP_DP_BP512R1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) brainpoolP512r1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(brainpoolP512r1_p)); + } else { + p = (mbedtls_mpi_uint *) brainpoolP512r1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(brainpoolP512r1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + case MBEDTLS_ECP_DP_CURVE25519: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) curve25519_p; + p_limbs = CHARS_TO_LIMBS(sizeof(curve25519_p)); + } else { + p = (mbedtls_mpi_uint *) curve25519_n; + p_limbs = CHARS_TO_LIMBS(sizeof(curve25519_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) + case MBEDTLS_ECP_DP_SECP192K1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp192k1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp192k1_p)); + } else { + p = (mbedtls_mpi_uint *) secp192k1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp192k1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) + case MBEDTLS_ECP_DP_SECP224K1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp224k1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp224k1_p)); + } else { + p = (mbedtls_mpi_uint *) secp224k1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp224k1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) + case MBEDTLS_ECP_DP_SECP256K1: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) secp256k1_p; + p_limbs = CHARS_TO_LIMBS(sizeof(secp256k1_p)); + } else { + p = (mbedtls_mpi_uint *) secp256k1_n; + p_limbs = CHARS_TO_LIMBS(sizeof(secp256k1_n)); + } + break; +#endif + +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + case MBEDTLS_ECP_DP_CURVE448: + if (ctype == (mbedtls_ecp_curve_type) MBEDTLS_ECP_MOD_COORDINATE) { + p = (mbedtls_mpi_uint *) curve448_p; + p_limbs = CHARS_TO_LIMBS(sizeof(curve448_p)); + } else { + p = (mbedtls_mpi_uint *) curve448_n; + p_limbs = CHARS_TO_LIMBS(sizeof(curve448_n)); + } + break; +#endif + + default: + case MBEDTLS_ECP_DP_NONE: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + if (mbedtls_mpi_mod_modulus_setup(N, p, p_limbs, + MBEDTLS_MPI_MOD_REP_MONTGOMERY)) { + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } + return 0; +} +#endif /* MBEDTLS_TEST_HOOKS */ +#endif /* !MBEDTLS_ECP_ALT */ +#endif /* MBEDTLS_ECP_C */ diff --git a/r5dev/thirdparty/mbedtls/ecp_internal_alt.h b/r5dev/thirdparty/mbedtls/ecp_internal_alt.h new file mode 100644 index 00000000..f663d673 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ecp_internal_alt.h @@ -0,0 +1,299 @@ +/** + * \file ecp_internal_alt.h + * + * \brief Function declarations for alternative implementation of elliptic curve + * point arithmetic. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * References: + * + * [1] BERNSTEIN, Daniel J. Curve25519: new Diffie-Hellman speed records. + * + * + * [2] CORON, Jean-S'ebastien. Resistance against differential power analysis + * for elliptic curve cryptosystems. In : Cryptographic Hardware and + * Embedded Systems. Springer Berlin Heidelberg, 1999. p. 292-302. + * + * + * [3] HEDABOU, Mustapha, PINEL, Pierre, et B'EN'ETEAU, Lucien. A comb method to + * render ECC resistant against Side Channel Attacks. IACR Cryptology + * ePrint Archive, 2004, vol. 2004, p. 342. + * + * + * [4] Certicom Research. SEC 2: Recommended Elliptic Curve Domain Parameters. + * + * + * [5] HANKERSON, Darrel, MENEZES, Alfred J., VANSTONE, Scott. Guide to Elliptic + * Curve Cryptography. + * + * [6] Digital Signature Standard (DSS), FIPS 186-4. + * + * + * [7] Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer + * Security (TLS), RFC 4492. + * + * + * [8] + * + * [9] COHEN, Henri. A Course in Computational Algebraic Number Theory. + * Springer Science & Business Media, 1 Aug 2000 + */ + +#ifndef MBEDTLS_ECP_INTERNAL_H +#define MBEDTLS_ECP_INTERNAL_H + +#include "mbedtls/build_info.h" + +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + +/** + * \brief Indicate if the Elliptic Curve Point module extension can + * handle the group. + * + * \param grp The pointer to the elliptic curve group that will be the + * basis of the cryptographic computations. + * + * \return Non-zero if successful. + */ +unsigned char mbedtls_internal_ecp_grp_capable(const mbedtls_ecp_group *grp); + +/** + * \brief Initialise the Elliptic Curve Point module extension. + * + * If mbedtls_internal_ecp_grp_capable returns true for a + * group, this function has to be able to initialise the + * module for it. + * + * This module can be a driver to a crypto hardware + * accelerator, for which this could be an initialise function. + * + * \param grp The pointer to the group the module needs to be + * initialised for. + * + * \return 0 if successful. + */ +int mbedtls_internal_ecp_init(const mbedtls_ecp_group *grp); + +/** + * \brief Frees and deallocates the Elliptic Curve Point module + * extension. + * + * \param grp The pointer to the group the module was initialised for. + */ +void mbedtls_internal_ecp_free(const mbedtls_ecp_group *grp); + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) + +#if defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) +/** + * \brief Randomize jacobian coordinates: + * (X, Y, Z) -> (l^2 X, l^3 Y, l Z) for random l. + * + * \param grp Pointer to the group representing the curve. + * + * \param pt The point on the curve to be randomised, given with Jacobian + * coordinates. + * + * \param f_rng A function pointer to the random number generator. + * + * \param p_rng A pointer to the random number generator state. + * + * \return 0 if successful. + */ +int mbedtls_internal_ecp_randomize_jac(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt, int (*f_rng)(void *, + unsigned char *, + size_t), + void *p_rng); +#endif + +#if defined(MBEDTLS_ECP_ADD_MIXED_ALT) +/** + * \brief Addition: R = P + Q, mixed affine-Jacobian coordinates. + * + * The coordinates of Q must be normalized (= affine), + * but those of P don't need to. R is not normalized. + * + * This function is used only as a subrutine of + * ecp_mul_comb(). + * + * Special cases: (1) P or Q is zero, (2) R is zero, + * (3) P == Q. + * None of these cases can happen as intermediate step in + * ecp_mul_comb(): + * - at each step, P, Q and R are multiples of the base + * point, the factor being less than its order, so none of + * them is zero; + * - Q is an odd multiple of the base point, P an even + * multiple, due to the choice of precomputed points in the + * modified comb method. + * So branches for these cases do not leak secret information. + * + * We accept Q->Z being unset (saving memory in tables) as + * meaning 1. + * + * Cost in field operations if done by [5] 3.22: + * 1A := 8M + 3S + * + * \param grp Pointer to the group representing the curve. + * + * \param R Pointer to a point structure to hold the result. + * + * \param P Pointer to the first summand, given with Jacobian + * coordinates + * + * \param Q Pointer to the second summand, given with affine + * coordinates. + * + * \return 0 if successful. + */ +int mbedtls_internal_ecp_add_mixed(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *R, const mbedtls_ecp_point *P, + const mbedtls_ecp_point *Q); +#endif + +/** + * \brief Point doubling R = 2 P, Jacobian coordinates. + * + * Cost: 1D := 3M + 4S (A == 0) + * 4M + 4S (A == -3) + * 3M + 6S + 1a otherwise + * when the implementation is based on the "dbl-1998-cmo-2" + * doubling formulas in [8] and standard optimizations are + * applied when curve parameter A is one of { 0, -3 }. + * + * \param grp Pointer to the group representing the curve. + * + * \param R Pointer to a point structure to hold the result. + * + * \param P Pointer to the point that has to be doubled, given with + * Jacobian coordinates. + * + * \return 0 if successful. + */ +#if defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) +int mbedtls_internal_ecp_double_jac(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *R, const mbedtls_ecp_point *P); +#endif + +/** + * \brief Normalize jacobian coordinates of an array of (pointers to) + * points. + * + * Using Montgomery's trick to perform only one inversion mod P + * the cost is: + * 1N(t) := 1I + (6t - 3)M + 1S + * (See for example Algorithm 10.3.4. in [9]) + * + * This function is used only as a subrutine of + * ecp_mul_comb(). + * + * Warning: fails (returning an error) if one of the points is + * zero! + * This should never happen, see choice of w in ecp_mul_comb(). + * + * \param grp Pointer to the group representing the curve. + * + * \param T Array of pointers to the points to normalise. + * + * \param t_len Number of elements in the array. + * + * \return 0 if successful, + * an error if one of the points is zero. + */ +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT) +int mbedtls_internal_ecp_normalize_jac_many(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *T[], size_t t_len); +#endif + +/** + * \brief Normalize jacobian coordinates so that Z == 0 || Z == 1. + * + * Cost in field operations if done by [5] 3.2.1: + * 1N := 1I + 3M + 1S + * + * \param grp Pointer to the group representing the curve. + * + * \param pt pointer to the point to be normalised. This is an + * input/output parameter. + * + * \return 0 if successful. + */ +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT) +int mbedtls_internal_ecp_normalize_jac(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt); +#endif + +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) + +#if defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT) +int mbedtls_internal_ecp_double_add_mxz(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *R, + mbedtls_ecp_point *S, + const mbedtls_ecp_point *P, + const mbedtls_ecp_point *Q, + const mbedtls_mpi *d); +#endif + +/** + * \brief Randomize projective x/z coordinates: + * (X, Z) -> (l X, l Z) for random l + * + * \param grp pointer to the group representing the curve + * + * \param P the point on the curve to be randomised given with + * projective coordinates. This is an input/output parameter. + * + * \param f_rng a function pointer to the random number generator + * + * \param p_rng a pointer to the random number generator state + * + * \return 0 if successful + */ +#if defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT) +int mbedtls_internal_ecp_randomize_mxz(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *P, int (*f_rng)(void *, + unsigned char *, + size_t), + void *p_rng); +#endif + +/** + * \brief Normalize Montgomery x/z coordinates: X = X/Z, Z = 1. + * + * \param grp pointer to the group representing the curve + * + * \param P pointer to the point to be normalised. This is an + * input/output parameter. + * + * \return 0 if successful + */ +#if defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT) +int mbedtls_internal_ecp_normalize_mxz(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *P); +#endif + +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +#endif /* MBEDTLS_ECP_INTERNAL_ALT */ + +#endif /* ecp_internal_alt.h */ diff --git a/r5dev/thirdparty/mbedtls/ecp_invasive.h b/r5dev/thirdparty/mbedtls/ecp_invasive.h new file mode 100644 index 00000000..cb16d237 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ecp_invasive.h @@ -0,0 +1,187 @@ +/** + * \file ecp_invasive.h + * + * \brief ECP module: interfaces for invasive testing only. + * + * The interfaces in this file are intended for testing purposes only. + * They SHOULD NOT be made available in library integrations except when + * building the library for testing. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ECP_INVASIVE_H +#define MBEDTLS_ECP_INVASIVE_H + +#include "common.h" +#include "mbedtls/bignum.h" +#include "bignum_mod.h" +#include "mbedtls/ecp.h" + +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_ECP_C) + +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +/* Preconditions: + * - bits is a multiple of 64 or is 224 + * - c is -1 or -2 + * - 0 <= N < 2^bits + * - N has room for bits plus one limb + * + * Behavior: + * Set N to c * 2^bits + old_value_of_N. + */ +void mbedtls_ecp_fix_negative(mbedtls_mpi *N, signed char c, size_t bits); +#endif + +#if defined(MBEDTLS_ECP_MONTGOMERY_ENABLED) +/** Generate a private key on a Montgomery curve (Curve25519 or Curve448). + * + * This function implements key generation for the set of secret keys + * specified in [Curve25519] p. 5 and in [Curve448]. The resulting value + * has the lower bits masked but is not necessarily canonical. + * + * \note - [Curve25519] http://cr.yp.to/ecdh/curve25519-20060209.pdf + * - [RFC7748] https://tools.ietf.org/html/rfc7748 + * + * \p high_bit The position of the high-order bit of the key to generate. + * This is the bit-size of the key minus 1: + * 254 for Curve25519 or 447 for Curve448. + * \param d The randomly generated key. This is a number of size + * exactly \p n_bits + 1 bits, with the least significant bits + * masked as specified in [Curve25519] and in [RFC7748] §5. + * \param f_rng The RNG function. + * \param p_rng The RNG context to be passed to \p f_rng. + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_ECP_xxx or MBEDTLS_ERR_MPI_xxx on failure. + */ +int mbedtls_ecp_gen_privkey_mx(size_t n_bits, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + +/** Fast quasi-reduction modulo p192 (FIPS 186-3 D.2.1) + * + * This operation expects a 384 bit MPI and the result of the reduction + * is a 192 bit MPI. + * + * \param[in,out] Np The address of the MPI to be converted. + * Must have twice as many limbs as the modulus. + * Upon return this holds the reduced value. The bitlength + * of the reduced value is the same as that of the modulus + * (192 bits). + * \param[in] Nn The length of \p Np in limbs. + */ +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p192_raw(mbedtls_mpi_uint *Np, size_t Nn); + +#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + +/** Fast quasi-reduction modulo p224 (FIPS 186-3 D.2.2) + * + * \param[in,out] X The address of the MPI to be converted. + * Must have exact limb size that stores a 448-bit MPI + * (double the bitlength of the modulus). + * Upon return holds the reduced value which is + * in range `0 <= X < 2 * N` (where N is the modulus). + * The bitlength of the reduced value is the same as + * that of the modulus (224 bits). + * \param[in] X_limbs The length of \p X in limbs. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if \p X_limbs is not the + * limb size that sores a 448-bit MPI. + */ +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p224_raw(mbedtls_mpi_uint *X, size_t X_limbs); + +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */ + +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + +/** Fast quasi-reduction modulo p256 (FIPS 186-3 D.2.3) + * + * \param[in,out] X The address of the MPI to be converted. + * Must have exact limb size that stores a 512-bit MPI + * (double the bitlength of the modulus). + * Upon return holds the reduced value which is + * in range `0 <= X < 2 * N` (where N is the modulus). + * The bitlength of the reduced value is the same as + * that of the modulus (256 bits). + * \param[in] X_limbs The length of \p X in limbs. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if \p X_limbs is not the + * limb size that sores a 512-bit MPI. + */ +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p256_raw(mbedtls_mpi_uint *X, size_t X_limbs); + +#endif + +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + +/** Fast quasi-reduction modulo p521 = 2^521 - 1 (FIPS 186-3 D.2.5) + * + * \param[in,out] X The address of the MPI to be converted. + * Must have twice as many limbs as the modulus + * (the modulus is 521 bits long). Upon return this + * holds the reduced value. The reduced value is + * in range `0 <= X < 2 * N` (where N is the modulus). + * and its the bitlength is one plus the bitlength + * of the modulus. + * \param[in] X_limbs The length of \p X in limbs. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if \p X_limbs does not have + * twice as many limbs as the modulus. + */ +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_mod_p521_raw(mbedtls_mpi_uint *X, size_t X_limbs); + +#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */ + +/** Initialise a modulus with hard-coded const curve data. + * + * \note The caller is responsible for the \p N modulus' memory. + * mbedtls_mpi_mod_modulus_free(&N) should be invoked at the + * end of its lifecycle. + * + * \param[in,out] N The address of the modulus structure to populate. + * Must be initialized. + * \param[in] id The mbedtls_ecp_group_id for which to initialise the modulus. + * \param[in] ctype The mbedtls_ecp_curve_type identifier for a coordinate modulus (P) + * or a scalar modulus (N). + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the given MPIs do not + * have the correct number of limbs. + * + */ +MBEDTLS_STATIC_TESTABLE +int mbedtls_ecp_modulus_setup(mbedtls_mpi_mod_modulus *N, + const mbedtls_ecp_group_id id, + const mbedtls_ecp_curve_type ctype); + +#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_ECP_C */ + +#endif /* MBEDTLS_ECP_INVASIVE_H */ diff --git a/r5dev/thirdparty/mbedtls/entropy.c b/r5dev/thirdparty/mbedtls/entropy.c new file mode 100644 index 00000000..e55410c7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/entropy.c @@ -0,0 +1,732 @@ +/* + * Entropy accumulator implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_ENTROPY_C) + +#include "mbedtls/entropy.h" +#include "entropy_poll.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_FS_IO) +#include +#endif + +#include "mbedtls/platform.h" + +#include "mbedtls/platform.h" + + +#define ENTROPY_MAX_LOOP 256 /**< Maximum amount to loop before error */ + +void mbedtls_entropy_init(mbedtls_entropy_context *ctx) +{ + ctx->source_count = 0; + memset(ctx->source, 0, sizeof(ctx->source)); + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&ctx->mutex); +#endif + + ctx->accumulator_started = 0; +#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR) + mbedtls_sha512_init(&ctx->accumulator); +#else + mbedtls_sha256_init(&ctx->accumulator); +#endif + + /* Reminder: Update ENTROPY_HAVE_STRONG in the test files + * when adding more strong entropy sources here. */ + +#if !defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) +#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) + mbedtls_entropy_add_source(ctx, mbedtls_platform_entropy_poll, NULL, + MBEDTLS_ENTROPY_MIN_PLATFORM, + MBEDTLS_ENTROPY_SOURCE_STRONG); +#endif +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) + mbedtls_entropy_add_source(ctx, mbedtls_hardware_poll, NULL, + MBEDTLS_ENTROPY_MIN_HARDWARE, + MBEDTLS_ENTROPY_SOURCE_STRONG); +#endif +#if defined(MBEDTLS_ENTROPY_NV_SEED) + mbedtls_entropy_add_source(ctx, mbedtls_nv_seed_poll, NULL, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_SOURCE_STRONG); + ctx->initial_entropy_run = 0; +#endif +#endif /* MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES */ +} + +void mbedtls_entropy_free(mbedtls_entropy_context *ctx) +{ + /* If the context was already free, don't call free() again. + * This is important for mutexes which don't allow double-free. */ + if (ctx->accumulator_started == -1) { + return; + } + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_free(&ctx->mutex); +#endif +#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR) + mbedtls_sha512_free(&ctx->accumulator); +#else + mbedtls_sha256_free(&ctx->accumulator); +#endif +#if defined(MBEDTLS_ENTROPY_NV_SEED) + ctx->initial_entropy_run = 0; +#endif + ctx->source_count = 0; + mbedtls_platform_zeroize(ctx->source, sizeof(ctx->source)); + ctx->accumulator_started = -1; +} + +int mbedtls_entropy_add_source(mbedtls_entropy_context *ctx, + mbedtls_entropy_f_source_ptr f_source, void *p_source, + size_t threshold, int strong) +{ + int idx, ret = 0; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + idx = ctx->source_count; + if (idx >= MBEDTLS_ENTROPY_MAX_SOURCES) { + ret = MBEDTLS_ERR_ENTROPY_MAX_SOURCES; + goto exit; + } + + ctx->source[idx].f_source = f_source; + ctx->source[idx].p_source = p_source; + ctx->source[idx].threshold = threshold; + ctx->source[idx].strong = strong; + + ctx->source_count++; + +exit: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +/* + * Entropy accumulator update + */ +static int entropy_update(mbedtls_entropy_context *ctx, unsigned char source_id, + const unsigned char *data, size_t len) +{ + unsigned char header[2]; + unsigned char tmp[MBEDTLS_ENTROPY_BLOCK_SIZE]; + size_t use_len = len; + const unsigned char *p = data; + int ret = 0; + + if (use_len > MBEDTLS_ENTROPY_BLOCK_SIZE) { +#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR) + if ((ret = mbedtls_sha512(data, len, tmp, 0)) != 0) { + goto cleanup; + } +#else + if ((ret = mbedtls_sha256(data, len, tmp, 0)) != 0) { + goto cleanup; + } +#endif + p = tmp; + use_len = MBEDTLS_ENTROPY_BLOCK_SIZE; + } + + header[0] = source_id; + header[1] = use_len & 0xFF; + + /* + * Start the accumulator if this has not already happened. Note that + * it is sufficient to start the accumulator here only because all calls to + * gather entropy eventually execute this code. + */ +#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR) + if (ctx->accumulator_started == 0 && + (ret = mbedtls_sha512_starts(&ctx->accumulator, 0)) != 0) { + goto cleanup; + } else { + ctx->accumulator_started = 1; + } + if ((ret = mbedtls_sha512_update(&ctx->accumulator, header, 2)) != 0) { + goto cleanup; + } + ret = mbedtls_sha512_update(&ctx->accumulator, p, use_len); +#else + if (ctx->accumulator_started == 0 && + (ret = mbedtls_sha256_starts(&ctx->accumulator, 0)) != 0) { + goto cleanup; + } else { + ctx->accumulator_started = 1; + } + if ((ret = mbedtls_sha256_update(&ctx->accumulator, header, 2)) != 0) { + goto cleanup; + } + ret = mbedtls_sha256_update(&ctx->accumulator, p, use_len); +#endif + +cleanup: + mbedtls_platform_zeroize(tmp, sizeof(tmp)); + + return ret; +} + +int mbedtls_entropy_update_manual(mbedtls_entropy_context *ctx, + const unsigned char *data, size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + ret = entropy_update(ctx, MBEDTLS_ENTROPY_SOURCE_MANUAL, data, len); + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +/* + * Run through the different sources to add entropy to our accumulator + */ +static int entropy_gather_internal(mbedtls_entropy_context *ctx) +{ + int ret = MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + int i; + int have_one_strong = 0; + unsigned char buf[MBEDTLS_ENTROPY_MAX_GATHER]; + size_t olen; + + if (ctx->source_count == 0) { + return MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED; + } + + /* + * Run through our entropy sources + */ + for (i = 0; i < ctx->source_count; i++) { + if (ctx->source[i].strong == MBEDTLS_ENTROPY_SOURCE_STRONG) { + have_one_strong = 1; + } + + olen = 0; + if ((ret = ctx->source[i].f_source(ctx->source[i].p_source, + buf, MBEDTLS_ENTROPY_MAX_GATHER, &olen)) != 0) { + goto cleanup; + } + + /* + * Add if we actually gathered something + */ + if (olen > 0) { + if ((ret = entropy_update(ctx, (unsigned char) i, + buf, olen)) != 0) { + return ret; + } + ctx->source[i].size += olen; + } + } + + if (have_one_strong == 0) { + ret = MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE; + } + +cleanup: + mbedtls_platform_zeroize(buf, sizeof(buf)); + + return ret; +} + +/* + * Thread-safe wrapper for entropy_gather_internal() + */ +int mbedtls_entropy_gather(mbedtls_entropy_context *ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + ret = entropy_gather_internal(ctx); + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +int mbedtls_entropy_func(void *data, unsigned char *output, size_t len) +{ + int ret, count = 0, i, thresholds_reached; + size_t strong_size; + mbedtls_entropy_context *ctx = (mbedtls_entropy_context *) data; + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; + + if (len > MBEDTLS_ENTROPY_BLOCK_SIZE) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + +#if defined(MBEDTLS_ENTROPY_NV_SEED) + /* Update the NV entropy seed before generating any entropy for outside + * use. + */ + if (ctx->initial_entropy_run == 0) { + ctx->initial_entropy_run = 1; + if ((ret = mbedtls_entropy_update_nv_seed(ctx)) != 0) { + return ret; + } + } +#endif + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + /* + * Always gather extra entropy before a call + */ + do { + if (count++ > ENTROPY_MAX_LOOP) { + ret = MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + goto exit; + } + + if ((ret = entropy_gather_internal(ctx)) != 0) { + goto exit; + } + + thresholds_reached = 1; + strong_size = 0; + for (i = 0; i < ctx->source_count; i++) { + if (ctx->source[i].size < ctx->source[i].threshold) { + thresholds_reached = 0; + } + if (ctx->source[i].strong == MBEDTLS_ENTROPY_SOURCE_STRONG) { + strong_size += ctx->source[i].size; + } + } + } while (!thresholds_reached || strong_size < MBEDTLS_ENTROPY_BLOCK_SIZE); + + memset(buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE); + +#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR) + /* + * Note that at this stage it is assumed that the accumulator was started + * in a previous call to entropy_update(). If this is not guaranteed, the + * code below will fail. + */ + if ((ret = mbedtls_sha512_finish(&ctx->accumulator, buf)) != 0) { + goto exit; + } + + /* + * Reset accumulator and counters and recycle existing entropy + */ + mbedtls_sha512_free(&ctx->accumulator); + mbedtls_sha512_init(&ctx->accumulator); + if ((ret = mbedtls_sha512_starts(&ctx->accumulator, 0)) != 0) { + goto exit; + } + if ((ret = mbedtls_sha512_update(&ctx->accumulator, buf, + MBEDTLS_ENTROPY_BLOCK_SIZE)) != 0) { + goto exit; + } + + /* + * Perform second SHA-512 on entropy + */ + if ((ret = mbedtls_sha512(buf, MBEDTLS_ENTROPY_BLOCK_SIZE, + buf, 0)) != 0) { + goto exit; + } +#else /* MBEDTLS_ENTROPY_SHA512_ACCUMULATOR */ + if ((ret = mbedtls_sha256_finish(&ctx->accumulator, buf)) != 0) { + goto exit; + } + + /* + * Reset accumulator and counters and recycle existing entropy + */ + mbedtls_sha256_free(&ctx->accumulator); + mbedtls_sha256_init(&ctx->accumulator); + if ((ret = mbedtls_sha256_starts(&ctx->accumulator, 0)) != 0) { + goto exit; + } + if ((ret = mbedtls_sha256_update(&ctx->accumulator, buf, + MBEDTLS_ENTROPY_BLOCK_SIZE)) != 0) { + goto exit; + } + + /* + * Perform second SHA-256 on entropy + */ + if ((ret = mbedtls_sha256(buf, MBEDTLS_ENTROPY_BLOCK_SIZE, + buf, 0)) != 0) { + goto exit; + } +#endif /* MBEDTLS_ENTROPY_SHA512_ACCUMULATOR */ + + for (i = 0; i < ctx->source_count; i++) { + ctx->source[i].size = 0; + } + + memcpy(output, buf, len); + + ret = 0; + +exit: + mbedtls_platform_zeroize(buf, sizeof(buf)); + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +#if defined(MBEDTLS_ENTROPY_NV_SEED) +int mbedtls_entropy_update_nv_seed(mbedtls_entropy_context *ctx) +{ + int ret = MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR; + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; + + /* Read new seed and write it to NV */ + if ((ret = mbedtls_entropy_func(ctx, buf, MBEDTLS_ENTROPY_BLOCK_SIZE)) != 0) { + return ret; + } + + if (mbedtls_nv_seed_write(buf, MBEDTLS_ENTROPY_BLOCK_SIZE) < 0) { + return MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR; + } + + /* Manually update the remaining stream with a separator value to diverge */ + memset(buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE); + ret = mbedtls_entropy_update_manual(ctx, buf, MBEDTLS_ENTROPY_BLOCK_SIZE); + + return ret; +} +#endif /* MBEDTLS_ENTROPY_NV_SEED */ + +#if defined(MBEDTLS_FS_IO) +int mbedtls_entropy_write_seed_file(mbedtls_entropy_context *ctx, const char *path) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + FILE *f = NULL; + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; + + if ((ret = mbedtls_entropy_func(ctx, buf, MBEDTLS_ENTROPY_BLOCK_SIZE)) != 0) { + ret = MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + goto exit; + } + + if ((f = fopen(path, "wb")) == NULL) { + ret = MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR; + goto exit; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + if (fwrite(buf, 1, MBEDTLS_ENTROPY_BLOCK_SIZE, f) != MBEDTLS_ENTROPY_BLOCK_SIZE) { + ret = MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR; + goto exit; + } + + ret = 0; + +exit: + mbedtls_platform_zeroize(buf, sizeof(buf)); + + if (f != NULL) { + fclose(f); + } + + return ret; +} + +int mbedtls_entropy_update_seed_file(mbedtls_entropy_context *ctx, const char *path) +{ + int ret = 0; + FILE *f; + size_t n; + unsigned char buf[MBEDTLS_ENTROPY_MAX_SEED_SIZE]; + + if ((f = fopen(path, "rb")) == NULL) { + return MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + fseek(f, 0, SEEK_END); + n = (size_t) ftell(f); + fseek(f, 0, SEEK_SET); + + if (n > MBEDTLS_ENTROPY_MAX_SEED_SIZE) { + n = MBEDTLS_ENTROPY_MAX_SEED_SIZE; + } + + if (fread(buf, 1, n, f) != n) { + ret = MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR; + } else { + ret = mbedtls_entropy_update_manual(ctx, buf, n); + } + + fclose(f); + + mbedtls_platform_zeroize(buf, sizeof(buf)); + + if (ret != 0) { + return ret; + } + + return mbedtls_entropy_write_seed_file(ctx, path); +} +#endif /* MBEDTLS_FS_IO */ + +#if defined(MBEDTLS_SELF_TEST) +/* + * Dummy source function + */ +static int entropy_dummy_source(void *data, unsigned char *output, + size_t len, size_t *olen) +{ + ((void) data); + + memset(output, 0x2a, len); + *olen = len; + + return 0; +} + +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) + +static int mbedtls_entropy_source_self_test_gather(unsigned char *buf, size_t buf_len) +{ + int ret = 0; + size_t entropy_len = 0; + size_t olen = 0; + size_t attempts = buf_len; + + while (attempts > 0 && entropy_len < buf_len) { + if ((ret = mbedtls_hardware_poll(NULL, buf + entropy_len, + buf_len - entropy_len, &olen)) != 0) { + return ret; + } + + entropy_len += olen; + attempts--; + } + + if (entropy_len < buf_len) { + ret = 1; + } + + return ret; +} + + +static int mbedtls_entropy_source_self_test_check_bits(const unsigned char *buf, + size_t buf_len) +{ + unsigned char set = 0xFF; + unsigned char unset = 0x00; + size_t i; + + for (i = 0; i < buf_len; i++) { + set &= buf[i]; + unset |= buf[i]; + } + + return set == 0xFF || unset == 0x00; +} + +/* + * A test to ensure that the entropy sources are functioning correctly + * and there is no obvious failure. The test performs the following checks: + * - The entropy source is not providing only 0s (all bits unset) or 1s (all + * bits set). + * - The entropy source is not providing values in a pattern. Because the + * hardware could be providing data in an arbitrary length, this check polls + * the hardware entropy source twice and compares the result to ensure they + * are not equal. + * - The error code returned by the entropy source is not an error. + */ +int mbedtls_entropy_source_self_test(int verbose) +{ + int ret = 0; + unsigned char buf0[2 * sizeof(unsigned long long int)]; + unsigned char buf1[2 * sizeof(unsigned long long int)]; + + if (verbose != 0) { + mbedtls_printf(" ENTROPY_BIAS test: "); + } + + memset(buf0, 0x00, sizeof(buf0)); + memset(buf1, 0x00, sizeof(buf1)); + + if ((ret = mbedtls_entropy_source_self_test_gather(buf0, sizeof(buf0))) != 0) { + goto cleanup; + } + if ((ret = mbedtls_entropy_source_self_test_gather(buf1, sizeof(buf1))) != 0) { + goto cleanup; + } + + /* Make sure that the returned values are not all 0 or 1 */ + if ((ret = mbedtls_entropy_source_self_test_check_bits(buf0, sizeof(buf0))) != 0) { + goto cleanup; + } + if ((ret = mbedtls_entropy_source_self_test_check_bits(buf1, sizeof(buf1))) != 0) { + goto cleanup; + } + + /* Make sure that the entropy source is not returning values in a + * pattern */ + ret = memcmp(buf0, buf1, sizeof(buf0)) == 0; + +cleanup: + if (verbose != 0) { + if (ret != 0) { + mbedtls_printf("failed\n"); + } else { + mbedtls_printf("passed\n"); + } + + mbedtls_printf("\n"); + } + + return ret != 0; +} + +#endif /* MBEDTLS_ENTROPY_HARDWARE_ALT */ + +/* + * The actual entropy quality is hard to test, but we can at least + * test that the functions don't cause errors and write the correct + * amount of data to buffers. + */ +int mbedtls_entropy_self_test(int verbose) +{ + int ret = 1; + mbedtls_entropy_context ctx; + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE] = { 0 }; + unsigned char acc[MBEDTLS_ENTROPY_BLOCK_SIZE] = { 0 }; + size_t i, j; + + if (verbose != 0) { + mbedtls_printf(" ENTROPY test: "); + } + + mbedtls_entropy_init(&ctx); + + /* First do a gather to make sure we have default sources */ + if ((ret = mbedtls_entropy_gather(&ctx)) != 0) { + goto cleanup; + } + + ret = mbedtls_entropy_add_source(&ctx, entropy_dummy_source, NULL, 16, + MBEDTLS_ENTROPY_SOURCE_WEAK); + if (ret != 0) { + goto cleanup; + } + + if ((ret = mbedtls_entropy_update_manual(&ctx, buf, sizeof(buf))) != 0) { + goto cleanup; + } + + /* + * To test that mbedtls_entropy_func writes correct number of bytes: + * - use the whole buffer and rely on ASan to detect overruns + * - collect entropy 8 times and OR the result in an accumulator: + * any byte should then be 0 with probably 2^(-64), so requiring + * each of the 32 or 64 bytes to be non-zero has a false failure rate + * of at most 2^(-58) which is acceptable. + */ + for (i = 0; i < 8; i++) { + if ((ret = mbedtls_entropy_func(&ctx, buf, sizeof(buf))) != 0) { + goto cleanup; + } + + for (j = 0; j < sizeof(buf); j++) { + acc[j] |= buf[j]; + } + } + + for (j = 0; j < sizeof(buf); j++) { + if (acc[j] == 0) { + ret = 1; + goto cleanup; + } + } + +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) + if ((ret = mbedtls_entropy_source_self_test(0)) != 0) { + goto cleanup; + } +#endif + +cleanup: + mbedtls_entropy_free(&ctx); + + if (verbose != 0) { + if (ret != 0) { + mbedtls_printf("failed\n"); + } else { + mbedtls_printf("passed\n"); + } + + mbedtls_printf("\n"); + } + + return ret != 0; +} +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_ENTROPY_C */ diff --git a/r5dev/thirdparty/mbedtls/entropy_poll.c b/r5dev/thirdparty/mbedtls/entropy_poll.c new file mode 100644 index 00000000..b5024c83 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/entropy_poll.c @@ -0,0 +1,239 @@ +/* + * Platform-specific and custom entropy polling functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(__linux__) && !defined(_GNU_SOURCE) +/* Ensure that syscall() is available even when compiling with -std=c99 */ +#define _GNU_SOURCE +#endif + +#include "common.h" + +#include + +#if defined(MBEDTLS_ENTROPY_C) + +#include "mbedtls/entropy.h" +#include "entropy_poll.h" +#include "mbedtls/error.h" + +#if defined(MBEDTLS_TIMING_C) +#include "mbedtls/timing.h" +#endif +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) + +#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) && !defined(__midipix__) +#error \ + "Platform entropy sources only work on Unix and Windows, see MBEDTLS_NO_PLATFORM_ENTROPY in mbedtls_config.h" +#endif + +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x0400 +#endif +#include +#include + +int mbedtls_platform_entropy_poll(void *data, unsigned char *output, size_t len, + size_t *olen) +{ + HCRYPTPROV provider; + ((void) data); + *olen = 0; + + if (CryptAcquireContext(&provider, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) == FALSE) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + + if (CryptGenRandom(provider, (DWORD) len, output) == FALSE) { + CryptReleaseContext(provider, 0); + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + + CryptReleaseContext(provider, 0); + *olen = len; + + return 0; +} +#else /* _WIN32 && !EFIX64 && !EFI32 */ + +/* + * Test for Linux getrandom() support. + * Since there is no wrapper in the libc yet, use the generic syscall wrapper + * available in GNU libc and compatible libc's (eg uClibc). + */ +#if ((defined(__linux__) && defined(__GLIBC__)) || defined(__midipix__)) +#include +#include +#if defined(SYS_getrandom) +#define HAVE_GETRANDOM +#include + +static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags) +{ + /* MemSan cannot understand that the syscall writes to the buffer */ +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + memset(buf, 0, buflen); +#endif +#endif + return syscall(SYS_getrandom, buf, buflen, flags); +} +#endif /* SYS_getrandom */ +#endif /* __linux__ || __midipix__ */ + +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include +#if (defined(__FreeBSD__) && __FreeBSD_version >= 1200000) || \ + (defined(__DragonFly__) && __DragonFly_version >= 500700) +#include +#include +#define HAVE_GETRANDOM +static int getrandom_wrapper(void *buf, size_t buflen, unsigned int flags) +{ + return getrandom(buf, buflen, flags); +} +#endif /* (__FreeBSD__ && __FreeBSD_version >= 1200000) || + (__DragonFly__ && __DragonFly_version >= 500700) */ +#endif /* __FreeBSD__ || __DragonFly__ */ + +/* + * Some BSD systems provide KERN_ARND. + * This is equivalent to reading from /dev/urandom, only it doesn't require an + * open file descriptor, and provides up to 256 bytes per call (basically the + * same as getentropy(), but with a longer history). + * + * Documentation: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7 + */ +#if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM) +#include +#include +#if defined(KERN_ARND) +#define HAVE_SYSCTL_ARND + +static int sysctl_arnd_wrapper(unsigned char *buf, size_t buflen) +{ + int name[2]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_ARND; + + while (buflen > 0) { + len = buflen > 256 ? 256 : buflen; + if (sysctl(name, 2, buf, &len, NULL, 0) == -1) { + return -1; + } + buflen -= len; + buf += len; + } + return 0; +} +#endif /* KERN_ARND */ +#endif /* __FreeBSD__ || __NetBSD__ */ + +#include + +int mbedtls_platform_entropy_poll(void *data, + unsigned char *output, size_t len, size_t *olen) +{ + FILE *file; + size_t read_len; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + ((void) data); + +#if defined(HAVE_GETRANDOM) + ret = getrandom_wrapper(output, len, 0); + if (ret >= 0) { + *olen = ret; + return 0; + } else if (errno != ENOSYS) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + /* Fall through if the system call isn't known. */ +#else + ((void) ret); +#endif /* HAVE_GETRANDOM */ + +#if defined(HAVE_SYSCTL_ARND) + ((void) file); + ((void) read_len); + if (sysctl_arnd_wrapper(output, len) == -1) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + *olen = len; + return 0; +#else + + *olen = 0; + + file = fopen("/dev/urandom", "rb"); + if (file == NULL) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(file, NULL); + + read_len = fread(output, 1, len, file); + if (read_len != len) { + fclose(file); + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + + fclose(file); + *olen = len; + + return 0; +#endif /* HAVE_SYSCTL_ARND */ +} +#endif /* _WIN32 && !EFIX64 && !EFI32 */ +#endif /* !MBEDTLS_NO_PLATFORM_ENTROPY */ + +#if defined(MBEDTLS_ENTROPY_NV_SEED) +int mbedtls_nv_seed_poll(void *data, + unsigned char *output, size_t len, size_t *olen) +{ + unsigned char buf[MBEDTLS_ENTROPY_BLOCK_SIZE]; + size_t use_len = MBEDTLS_ENTROPY_BLOCK_SIZE; + ((void) data); + + memset(buf, 0, MBEDTLS_ENTROPY_BLOCK_SIZE); + + if (mbedtls_nv_seed_read(buf, MBEDTLS_ENTROPY_BLOCK_SIZE) < 0) { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } + + if (len < use_len) { + use_len = len; + } + + memcpy(output, buf, use_len); + *olen = use_len; + + return 0; +} +#endif /* MBEDTLS_ENTROPY_NV_SEED */ + +#endif /* MBEDTLS_ENTROPY_C */ diff --git a/r5dev/thirdparty/mbedtls/entropy_poll.h b/r5dev/thirdparty/mbedtls/entropy_poll.h new file mode 100644 index 00000000..3cfd4a44 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/entropy_poll.h @@ -0,0 +1,76 @@ +/** + * \file entropy_poll.h + * + * \brief Platform-specific and custom entropy polling functions + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ENTROPY_POLL_H +#define MBEDTLS_ENTROPY_POLL_H + +#include "mbedtls/build_info.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Default thresholds for built-in sources, in bytes + */ +#define MBEDTLS_ENTROPY_MIN_PLATFORM 32 /**< Minimum for platform source */ +#if !defined(MBEDTLS_ENTROPY_MIN_HARDWARE) +#define MBEDTLS_ENTROPY_MIN_HARDWARE 32 /**< Minimum for the hardware source */ +#endif + +#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) +/** + * \brief Platform-specific entropy poll callback + */ +int mbedtls_platform_entropy_poll(void *data, + unsigned char *output, size_t len, size_t *olen); +#endif + +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) +/** + * \brief Entropy poll callback for a hardware source + * + * \warning This is not provided by mbed TLS! + * See \c MBEDTLS_ENTROPY_HARDWARE_ALT in mbedtls_config.h. + * + * \note This must accept NULL as its first argument. + */ +int mbedtls_hardware_poll(void *data, + unsigned char *output, size_t len, size_t *olen); +#endif + +#if defined(MBEDTLS_ENTROPY_NV_SEED) +/** + * \brief Entropy poll callback for a non-volatile seed file + * + * \note This must accept NULL as its first argument. + */ +int mbedtls_nv_seed_poll(void *data, + unsigned char *output, size_t len, size_t *olen); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* entropy_poll.h */ diff --git a/r5dev/thirdparty/mbedtls/error.c b/r5dev/thirdparty/mbedtls/error.c new file mode 100644 index 00000000..85e31543 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/error.c @@ -0,0 +1,879 @@ +/* + * Error message information + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#include "mbedtls/error.h" + +#if defined(MBEDTLS_ERROR_C) || defined(MBEDTLS_ERROR_STRERROR_DUMMY) + +#if defined(MBEDTLS_ERROR_C) + +#include "mbedtls/platform.h" + +#include +#include + +#if defined(MBEDTLS_AES_C) +#include "mbedtls/aes.h" +#endif + +#if defined(MBEDTLS_ARIA_C) +#include "mbedtls/aria.h" +#endif + +#if defined(MBEDTLS_ASN1_PARSE_C) +#include "mbedtls/asn1.h" +#endif + +#if defined(MBEDTLS_BASE64_C) +#include "mbedtls/base64.h" +#endif + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +#if defined(MBEDTLS_CAMELLIA_C) +#include "mbedtls/camellia.h" +#endif + +#if defined(MBEDTLS_CCM_C) +#include "mbedtls/ccm.h" +#endif + +#if defined(MBEDTLS_CHACHA20_C) +#include "mbedtls/chacha20.h" +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) +#include "mbedtls/chachapoly.h" +#endif + +#if defined(MBEDTLS_CIPHER_C) +#include "mbedtls/cipher.h" +#endif + +#if defined(MBEDTLS_CTR_DRBG_C) +#include "mbedtls/ctr_drbg.h" +#endif + +#if defined(MBEDTLS_DES_C) +#include "mbedtls/des.h" +#endif + +#if defined(MBEDTLS_DHM_C) +#include "mbedtls/dhm.h" +#endif + +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif + +#if defined(MBEDTLS_ENTROPY_C) +#include "mbedtls/entropy.h" +#endif + +#if defined(MBEDTLS_ERROR_C) +#include "mbedtls/error.h" +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#endif + +#if defined(MBEDTLS_GCM_C) +#include "mbedtls/gcm.h" +#endif + +#if defined(MBEDTLS_HKDF_C) +#include "mbedtls/hkdf.h" +#endif + +#if defined(MBEDTLS_HMAC_DRBG_C) +#include "mbedtls/hmac_drbg.h" +#endif + +#if defined(MBEDTLS_LMS_C) +#include "mbedtls/lms.h" +#endif + +#if defined(MBEDTLS_MD_C) +#include "mbedtls/md.h" +#endif + +#if defined(MBEDTLS_NET_C) +#include "mbedtls/net_sockets.h" +#endif + +#if defined(MBEDTLS_OID_C) +#include "mbedtls/oid.h" +#endif + +#if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) +#include "mbedtls/pem.h" +#endif + +#if defined(MBEDTLS_PK_C) +#include "mbedtls/pk.h" +#endif + +#if defined(MBEDTLS_PKCS12_C) +#include "mbedtls/pkcs12.h" +#endif + +#if defined(MBEDTLS_PKCS5_C) +#include "mbedtls/pkcs5.h" +#endif + +#if defined(MBEDTLS_PKCS7_C) +#include "mbedtls/pkcs7.h" +#endif + +#if defined(MBEDTLS_POLY1305_C) +#include "mbedtls/poly1305.h" +#endif + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif + +#if defined(MBEDTLS_SHA1_C) +#include "mbedtls/sha1.h" +#endif + +#if defined(MBEDTLS_SHA256_C) +#include "mbedtls/sha256.h" +#endif + +#if defined(MBEDTLS_SHA512_C) +#include "mbedtls/sha512.h" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) +#include "mbedtls/ssl.h" +#endif + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) +#include "mbedtls/x509.h" +#endif + + +const char *mbedtls_high_level_strerr(int error_code) +{ + int high_level_error_code; + + if (error_code < 0) { + error_code = -error_code; + } + + /* Extract the high-level part from the error code. */ + high_level_error_code = error_code & 0xFF80; + + switch (high_level_error_code) { + /* Begin Auto-Generated Code. */ + #if defined(MBEDTLS_CIPHER_C) + case -(MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE): + return( "CIPHER - The selected feature is not available" ); + case -(MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA): + return( "CIPHER - Bad input parameters" ); + case -(MBEDTLS_ERR_CIPHER_ALLOC_FAILED): + return( "CIPHER - Failed to allocate memory" ); + case -(MBEDTLS_ERR_CIPHER_INVALID_PADDING): + return( "CIPHER - Input data contains invalid padding and is rejected" ); + case -(MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED): + return( "CIPHER - Decryption of block requires a full block" ); + case -(MBEDTLS_ERR_CIPHER_AUTH_FAILED): + return( "CIPHER - Authentication failed (for AEAD modes)" ); + case -(MBEDTLS_ERR_CIPHER_INVALID_CONTEXT): + return( "CIPHER - The context is invalid. For example, because it was freed" ); +#endif /* MBEDTLS_CIPHER_C */ + +#if defined(MBEDTLS_DHM_C) + case -(MBEDTLS_ERR_DHM_BAD_INPUT_DATA): + return( "DHM - Bad input parameters" ); + case -(MBEDTLS_ERR_DHM_READ_PARAMS_FAILED): + return( "DHM - Reading of the DHM parameters failed" ); + case -(MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED): + return( "DHM - Making of the DHM parameters failed" ); + case -(MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED): + return( "DHM - Reading of the public values failed" ); + case -(MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED): + return( "DHM - Making of the public value failed" ); + case -(MBEDTLS_ERR_DHM_CALC_SECRET_FAILED): + return( "DHM - Calculation of the DHM secret failed" ); + case -(MBEDTLS_ERR_DHM_INVALID_FORMAT): + return( "DHM - The ASN.1 data is not formatted correctly" ); + case -(MBEDTLS_ERR_DHM_ALLOC_FAILED): + return( "DHM - Allocation of memory failed" ); + case -(MBEDTLS_ERR_DHM_FILE_IO_ERROR): + return( "DHM - Read or write of file failed" ); + case -(MBEDTLS_ERR_DHM_SET_GROUP_FAILED): + return( "DHM - Setting the modulus and generator failed" ); +#endif /* MBEDTLS_DHM_C */ + +#if defined(MBEDTLS_ECP_C) + case -(MBEDTLS_ERR_ECP_BAD_INPUT_DATA): + return( "ECP - Bad input parameters to function" ); + case -(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL): + return( "ECP - The buffer is too small to write to" ); + case -(MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE): + return( "ECP - The requested feature is not available, for example, the requested curve is not supported" ); + case -(MBEDTLS_ERR_ECP_VERIFY_FAILED): + return( "ECP - The signature is not valid" ); + case -(MBEDTLS_ERR_ECP_ALLOC_FAILED): + return( "ECP - Memory allocation failed" ); + case -(MBEDTLS_ERR_ECP_RANDOM_FAILED): + return( "ECP - Generation of random value, such as ephemeral key, failed" ); + case -(MBEDTLS_ERR_ECP_INVALID_KEY): + return( "ECP - Invalid private or public key" ); + case -(MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH): + return( "ECP - The buffer contains a valid signature followed by more data" ); + case -(MBEDTLS_ERR_ECP_IN_PROGRESS): + return( "ECP - Operation in progress, call again with the same parameters to continue" ); +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_MD_C) + case -(MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE): + return( "MD - The selected feature is not available" ); + case -(MBEDTLS_ERR_MD_BAD_INPUT_DATA): + return( "MD - Bad input parameters to function" ); + case -(MBEDTLS_ERR_MD_ALLOC_FAILED): + return( "MD - Failed to allocate memory" ); + case -(MBEDTLS_ERR_MD_FILE_IO_ERROR): + return( "MD - Opening or reading of file failed" ); +#endif /* MBEDTLS_MD_C */ + +#if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) + case -(MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT): + return( "PEM - No PEM header or footer found" ); + case -(MBEDTLS_ERR_PEM_INVALID_DATA): + return( "PEM - PEM string is not as expected" ); + case -(MBEDTLS_ERR_PEM_ALLOC_FAILED): + return( "PEM - Failed to allocate memory" ); + case -(MBEDTLS_ERR_PEM_INVALID_ENC_IV): + return( "PEM - RSA IV is not in hex-format" ); + case -(MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG): + return( "PEM - Unsupported key encryption algorithm" ); + case -(MBEDTLS_ERR_PEM_PASSWORD_REQUIRED): + return( "PEM - Private key password can't be empty" ); + case -(MBEDTLS_ERR_PEM_PASSWORD_MISMATCH): + return( "PEM - Given private key password does not allow for correct decryption" ); + case -(MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE): + return( "PEM - Unavailable feature, e.g. hashing/encryption combination" ); + case -(MBEDTLS_ERR_PEM_BAD_INPUT_DATA): + return( "PEM - Bad input parameters to function" ); +#endif /* MBEDTLS_PEM_PARSE_C || MBEDTLS_PEM_WRITE_C */ + +#if defined(MBEDTLS_PK_C) + case -(MBEDTLS_ERR_PK_ALLOC_FAILED): + return( "PK - Memory allocation failed" ); + case -(MBEDTLS_ERR_PK_TYPE_MISMATCH): + return( "PK - Type mismatch, eg attempt to encrypt with an ECDSA key" ); + case -(MBEDTLS_ERR_PK_BAD_INPUT_DATA): + return( "PK - Bad input parameters to function" ); + case -(MBEDTLS_ERR_PK_FILE_IO_ERROR): + return( "PK - Read/write of file failed" ); + case -(MBEDTLS_ERR_PK_KEY_INVALID_VERSION): + return( "PK - Unsupported key version" ); + case -(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT): + return( "PK - Invalid key tag or value" ); + case -(MBEDTLS_ERR_PK_UNKNOWN_PK_ALG): + return( "PK - Key algorithm is unsupported (only RSA and EC are supported)" ); + case -(MBEDTLS_ERR_PK_PASSWORD_REQUIRED): + return( "PK - Private key password can't be empty" ); + case -(MBEDTLS_ERR_PK_PASSWORD_MISMATCH): + return( "PK - Given private key password does not allow for correct decryption" ); + case -(MBEDTLS_ERR_PK_INVALID_PUBKEY): + return( "PK - The pubkey tag or value is invalid (only RSA and EC are supported)" ); + case -(MBEDTLS_ERR_PK_INVALID_ALG): + return( "PK - The algorithm tag or value is invalid" ); + case -(MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE): + return( "PK - Elliptic curve is unsupported (only NIST curves are supported)" ); + case -(MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE): + return( "PK - Unavailable feature, e.g. RSA disabled for RSA key" ); + case -(MBEDTLS_ERR_PK_SIG_LEN_MISMATCH): + return( "PK - The buffer contains a valid signature followed by more data" ); + case -(MBEDTLS_ERR_PK_BUFFER_TOO_SMALL): + return( "PK - The output buffer is too small" ); +#endif /* MBEDTLS_PK_C */ + +#if defined(MBEDTLS_PKCS12_C) + case -(MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA): + return( "PKCS12 - Bad input parameters to function" ); + case -(MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE): + return( "PKCS12 - Feature not available, e.g. unsupported encryption scheme" ); + case -(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT): + return( "PKCS12 - PBE ASN.1 data not as expected" ); + case -(MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH): + return( "PKCS12 - Given private key password does not allow for correct decryption" ); +#endif /* MBEDTLS_PKCS12_C */ + +#if defined(MBEDTLS_PKCS5_C) + case -(MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA): + return( "PKCS5 - Bad input parameters to function" ); + case -(MBEDTLS_ERR_PKCS5_INVALID_FORMAT): + return( "PKCS5 - Unexpected ASN.1 data" ); + case -(MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE): + return( "PKCS5 - Requested encryption or digest alg not available" ); + case -(MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH): + return( "PKCS5 - Given private key password does not allow for correct decryption" ); +#endif /* MBEDTLS_PKCS5_C */ + +#if defined(MBEDTLS_PKCS7_C) + case -(MBEDTLS_ERR_PKCS7_INVALID_FORMAT): + return( "PKCS7 - The format is invalid, e.g. different type expected" ); + case -(MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE): + return( "PKCS7 - Unavailable feature, e.g. anything other than signed data" ); + case -(MBEDTLS_ERR_PKCS7_INVALID_VERSION): + return( "PKCS7 - The PKCS #7 version element is invalid or cannot be parsed" ); + case -(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO): + return( "PKCS7 - The PKCS #7 content info is invalid or cannot be parsed" ); + case -(MBEDTLS_ERR_PKCS7_INVALID_ALG): + return( "PKCS7 - The algorithm tag or value is invalid or cannot be parsed" ); + case -(MBEDTLS_ERR_PKCS7_INVALID_CERT): + return( "PKCS7 - The certificate tag or value is invalid or cannot be parsed" ); + case -(MBEDTLS_ERR_PKCS7_INVALID_SIGNATURE): + return( "PKCS7 - Error parsing the signature" ); + case -(MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO): + return( "PKCS7 - Error parsing the signer's info" ); + case -(MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA): + return( "PKCS7 - Input invalid" ); + case -(MBEDTLS_ERR_PKCS7_ALLOC_FAILED): + return( "PKCS7 - Allocation of memory failed" ); + case -(MBEDTLS_ERR_PKCS7_VERIFY_FAIL): + return( "PKCS7 - Verification Failed" ); + case -(MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID): + return( "PKCS7 - The PKCS #7 date issued/expired dates are invalid" ); +#endif /* MBEDTLS_PKCS7_C */ + +#if defined(MBEDTLS_RSA_C) + case -(MBEDTLS_ERR_RSA_BAD_INPUT_DATA): + return( "RSA - Bad input parameters to function" ); + case -(MBEDTLS_ERR_RSA_INVALID_PADDING): + return( "RSA - Input data contains invalid padding and is rejected" ); + case -(MBEDTLS_ERR_RSA_KEY_GEN_FAILED): + return( "RSA - Something failed during generation of a key" ); + case -(MBEDTLS_ERR_RSA_KEY_CHECK_FAILED): + return( "RSA - Key failed to pass the validity check of the library" ); + case -(MBEDTLS_ERR_RSA_PUBLIC_FAILED): + return( "RSA - The public key operation failed" ); + case -(MBEDTLS_ERR_RSA_PRIVATE_FAILED): + return( "RSA - The private key operation failed" ); + case -(MBEDTLS_ERR_RSA_VERIFY_FAILED): + return( "RSA - The PKCS#1 verification failed" ); + case -(MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE): + return( "RSA - The output buffer for decryption is not large enough" ); + case -(MBEDTLS_ERR_RSA_RNG_FAILED): + return( "RSA - The random generator failed to generate non-zeros" ); +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_SSL_TLS_C) + case -(MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS): + return( "SSL - A cryptographic operation is in progress. Try again later" ); + case -(MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE): + return( "SSL - The requested feature is not available" ); + case -(MBEDTLS_ERR_SSL_BAD_INPUT_DATA): + return( "SSL - Bad input parameters to function" ); + case -(MBEDTLS_ERR_SSL_INVALID_MAC): + return( "SSL - Verification of the message MAC failed" ); + case -(MBEDTLS_ERR_SSL_INVALID_RECORD): + return( "SSL - An invalid SSL record was received" ); + case -(MBEDTLS_ERR_SSL_CONN_EOF): + return( "SSL - The connection indicated an EOF" ); + case -(MBEDTLS_ERR_SSL_DECODE_ERROR): + return( "SSL - A message could not be parsed due to a syntactic error" ); + case -(MBEDTLS_ERR_SSL_NO_RNG): + return( "SSL - No RNG was provided to the SSL module" ); + case -(MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE): + return( "SSL - No client certification received from the client, but required by the authentication mode" ); + case -(MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION): + return( "SSL - Client received an extended server hello containing an unsupported extension" ); + case -(MBEDTLS_ERR_SSL_NO_APPLICATION_PROTOCOL): + return( "SSL - No ALPN protocols supported that the client advertises" ); + case -(MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED): + return( "SSL - The own private key or pre-shared key is not set, but needed" ); + case -(MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED): + return( "SSL - No CA Chain is set, but required to operate" ); + case -(MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE): + return( "SSL - An unexpected message was received from our peer" ); + case -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE): + return( "SSL - A fatal alert message was received from our peer" ); + case -(MBEDTLS_ERR_SSL_UNRECOGNIZED_NAME): + return( "SSL - No server could be identified matching the client's SNI" ); + case -(MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY): + return( "SSL - The peer notified us that the connection is going to be closed" ); + case -(MBEDTLS_ERR_SSL_BAD_CERTIFICATE): + return( "SSL - Processing of the Certificate handshake message failed" ); + case -(MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET): + return( "SSL - * Received NewSessionTicket Post Handshake Message. This error code is experimental and may be changed or removed without notice" ); + case -(MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA): + return( "SSL - Not possible to read early data" ); + case -(MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA): + return( "SSL - Not possible to write early data" ); + case -(MBEDTLS_ERR_SSL_ALLOC_FAILED): + return( "SSL - Memory allocation failed" ); + case -(MBEDTLS_ERR_SSL_HW_ACCEL_FAILED): + return( "SSL - Hardware acceleration function returned with error" ); + case -(MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH): + return( "SSL - Hardware acceleration function skipped / left alone data" ); + case -(MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION): + return( "SSL - Handshake protocol not within min/max boundaries" ); + case -(MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE): + return( "SSL - The handshake negotiation failed" ); + case -(MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED): + return( "SSL - Session ticket has expired" ); + case -(MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH): + return( "SSL - Public key type mismatch (eg, asked for RSA key exchange and presented EC key)" ); + case -(MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY): + return( "SSL - Unknown identity received (eg, PSK identity)" ); + case -(MBEDTLS_ERR_SSL_INTERNAL_ERROR): + return( "SSL - Internal error (eg, unexpected failure in lower-level module)" ); + case -(MBEDTLS_ERR_SSL_COUNTER_WRAPPING): + return( "SSL - A counter would wrap (eg, too many messages exchanged)" ); + case -(MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO): + return( "SSL - Unexpected message at ServerHello in renegotiation" ); + case -(MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED): + return( "SSL - DTLS client must retry for hello verification" ); + case -(MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL): + return( "SSL - A buffer is too small to receive or write a message" ); + case -(MBEDTLS_ERR_SSL_WANT_READ): + return( "SSL - No data of requested type currently available on underlying transport" ); + case -(MBEDTLS_ERR_SSL_WANT_WRITE): + return( "SSL - Connection requires a write call" ); + case -(MBEDTLS_ERR_SSL_TIMEOUT): + return( "SSL - The operation timed out" ); + case -(MBEDTLS_ERR_SSL_CLIENT_RECONNECT): + return( "SSL - The client initiated a reconnect from the same port" ); + case -(MBEDTLS_ERR_SSL_UNEXPECTED_RECORD): + return( "SSL - Record header looks valid but is not expected" ); + case -(MBEDTLS_ERR_SSL_NON_FATAL): + return( "SSL - The alert message received indicates a non-fatal error" ); + case -(MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER): + return( "SSL - A field in a message was incorrect or inconsistent with other fields" ); + case -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING): + return( "SSL - Internal-only message signaling that further message-processing should be done" ); + case -(MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS): + return( "SSL - The asynchronous operation is not completed yet" ); + case -(MBEDTLS_ERR_SSL_EARLY_MESSAGE): + return( "SSL - Internal-only message signaling that a message arrived early" ); + case -(MBEDTLS_ERR_SSL_UNEXPECTED_CID): + return( "SSL - An encrypted DTLS-frame with an unexpected CID was received" ); + case -(MBEDTLS_ERR_SSL_VERSION_MISMATCH): + return( "SSL - An operation failed due to an unexpected version or configuration" ); + case -(MBEDTLS_ERR_SSL_BAD_CONFIG): + return( "SSL - Invalid value in SSL config" ); +#endif /* MBEDTLS_SSL_TLS_C */ + +#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C) + case -(MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE): + return( "X509 - Unavailable feature, e.g. RSA hashing/encryption combination" ); + case -(MBEDTLS_ERR_X509_UNKNOWN_OID): + return( "X509 - Requested OID is unknown" ); + case -(MBEDTLS_ERR_X509_INVALID_FORMAT): + return( "X509 - The CRT/CRL/CSR format is invalid, e.g. different type expected" ); + case -(MBEDTLS_ERR_X509_INVALID_VERSION): + return( "X509 - The CRT/CRL/CSR version element is invalid" ); + case -(MBEDTLS_ERR_X509_INVALID_SERIAL): + return( "X509 - The serial tag or value is invalid" ); + case -(MBEDTLS_ERR_X509_INVALID_ALG): + return( "X509 - The algorithm tag or value is invalid" ); + case -(MBEDTLS_ERR_X509_INVALID_NAME): + return( "X509 - The name tag or value is invalid" ); + case -(MBEDTLS_ERR_X509_INVALID_DATE): + return( "X509 - The date tag or value is invalid" ); + case -(MBEDTLS_ERR_X509_INVALID_SIGNATURE): + return( "X509 - The signature tag or value invalid" ); + case -(MBEDTLS_ERR_X509_INVALID_EXTENSIONS): + return( "X509 - The extension tag or value is invalid" ); + case -(MBEDTLS_ERR_X509_UNKNOWN_VERSION): + return( "X509 - CRT/CRL/CSR has an unsupported version number" ); + case -(MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG): + return( "X509 - Signature algorithm (oid) is unsupported" ); + case -(MBEDTLS_ERR_X509_SIG_MISMATCH): + return( "X509 - Signature algorithms do not match. (see \\c ::mbedtls_x509_crt sig_oid)" ); + case -(MBEDTLS_ERR_X509_CERT_VERIFY_FAILED): + return( "X509 - Certificate verification failed, e.g. CRL, CA or signature check failed" ); + case -(MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT): + return( "X509 - Format not recognized as DER or PEM" ); + case -(MBEDTLS_ERR_X509_BAD_INPUT_DATA): + return( "X509 - Input invalid" ); + case -(MBEDTLS_ERR_X509_ALLOC_FAILED): + return( "X509 - Allocation of memory failed" ); + case -(MBEDTLS_ERR_X509_FILE_IO_ERROR): + return( "X509 - Read/write of file failed" ); + case -(MBEDTLS_ERR_X509_BUFFER_TOO_SMALL): + return( "X509 - Destination buffer is too small" ); + case -(MBEDTLS_ERR_X509_FATAL_ERROR): + return( "X509 - A fatal error occurred, eg the chain is too long or the vrfy callback failed" ); +#endif /* MBEDTLS_X509_USE_C || MBEDTLS_X509_CREATE_C */ + /* End Auto-Generated Code. */ + + default: + break; + } + + return NULL; +} + +const char *mbedtls_low_level_strerr(int error_code) +{ + int low_level_error_code; + + if (error_code < 0) { + error_code = -error_code; + } + + /* Extract the low-level part from the error code. */ + low_level_error_code = error_code & ~0xFF80; + + switch (low_level_error_code) { + /* Begin Auto-Generated Code. */ + #if defined(MBEDTLS_AES_C) + case -(MBEDTLS_ERR_AES_INVALID_KEY_LENGTH): + return( "AES - Invalid key length" ); + case -(MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH): + return( "AES - Invalid data input length" ); + case -(MBEDTLS_ERR_AES_BAD_INPUT_DATA): + return( "AES - Invalid input data" ); +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_ARIA_C) + case -(MBEDTLS_ERR_ARIA_BAD_INPUT_DATA): + return( "ARIA - Bad input data" ); + case -(MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH): + return( "ARIA - Invalid data input length" ); +#endif /* MBEDTLS_ARIA_C */ + +#if defined(MBEDTLS_ASN1_PARSE_C) + case -(MBEDTLS_ERR_ASN1_OUT_OF_DATA): + return( "ASN1 - Out of data when parsing an ASN1 data structure" ); + case -(MBEDTLS_ERR_ASN1_UNEXPECTED_TAG): + return( "ASN1 - ASN1 tag was of an unexpected value" ); + case -(MBEDTLS_ERR_ASN1_INVALID_LENGTH): + return( "ASN1 - Error when trying to determine the length or invalid length" ); + case -(MBEDTLS_ERR_ASN1_LENGTH_MISMATCH): + return( "ASN1 - Actual length differs from expected length" ); + case -(MBEDTLS_ERR_ASN1_INVALID_DATA): + return( "ASN1 - Data is invalid" ); + case -(MBEDTLS_ERR_ASN1_ALLOC_FAILED): + return( "ASN1 - Memory allocation failed" ); + case -(MBEDTLS_ERR_ASN1_BUF_TOO_SMALL): + return( "ASN1 - Buffer too small when writing ASN.1 data structure" ); +#endif /* MBEDTLS_ASN1_PARSE_C */ + +#if defined(MBEDTLS_BASE64_C) + case -(MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL): + return( "BASE64 - Output buffer too small" ); + case -(MBEDTLS_ERR_BASE64_INVALID_CHARACTER): + return( "BASE64 - Invalid character in input" ); +#endif /* MBEDTLS_BASE64_C */ + +#if defined(MBEDTLS_BIGNUM_C) + case -(MBEDTLS_ERR_MPI_FILE_IO_ERROR): + return( "BIGNUM - An error occurred while reading from or writing to a file" ); + case -(MBEDTLS_ERR_MPI_BAD_INPUT_DATA): + return( "BIGNUM - Bad input parameters to function" ); + case -(MBEDTLS_ERR_MPI_INVALID_CHARACTER): + return( "BIGNUM - There is an invalid character in the digit string" ); + case -(MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + return( "BIGNUM - The buffer is too small to write to" ); + case -(MBEDTLS_ERR_MPI_NEGATIVE_VALUE): + return( "BIGNUM - The input arguments are negative or result in illegal output" ); + case -(MBEDTLS_ERR_MPI_DIVISION_BY_ZERO): + return( "BIGNUM - The input argument for division is zero, which is not allowed" ); + case -(MBEDTLS_ERR_MPI_NOT_ACCEPTABLE): + return( "BIGNUM - The input arguments are not acceptable" ); + case -(MBEDTLS_ERR_MPI_ALLOC_FAILED): + return( "BIGNUM - Memory allocation failed" ); +#endif /* MBEDTLS_BIGNUM_C */ + +#if defined(MBEDTLS_CAMELLIA_C) + case -(MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA): + return( "CAMELLIA - Bad input data" ); + case -(MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH): + return( "CAMELLIA - Invalid data input length" ); +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_CCM_C) + case -(MBEDTLS_ERR_CCM_BAD_INPUT): + return( "CCM - Bad input parameters to the function" ); + case -(MBEDTLS_ERR_CCM_AUTH_FAILED): + return( "CCM - Authenticated decryption failed" ); +#endif /* MBEDTLS_CCM_C */ + +#if defined(MBEDTLS_CHACHA20_C) + case -(MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA): + return( "CHACHA20 - Invalid input parameter(s)" ); +#endif /* MBEDTLS_CHACHA20_C */ + +#if defined(MBEDTLS_CHACHAPOLY_C) + case -(MBEDTLS_ERR_CHACHAPOLY_BAD_STATE): + return( "CHACHAPOLY - The requested operation is not permitted in the current state" ); + case -(MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED): + return( "CHACHAPOLY - Authenticated decryption failed: data was not authentic" ); +#endif /* MBEDTLS_CHACHAPOLY_C */ + +#if defined(MBEDTLS_CTR_DRBG_C) + case -(MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED): + return( "CTR_DRBG - The entropy source failed" ); + case -(MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG): + return( "CTR_DRBG - The requested random buffer length is too big" ); + case -(MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG): + return( "CTR_DRBG - The input (entropy + additional data) is too large" ); + case -(MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR): + return( "CTR_DRBG - Read or write error in file" ); +#endif /* MBEDTLS_CTR_DRBG_C */ + +#if defined(MBEDTLS_DES_C) + case -(MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH): + return( "DES - The data input has an invalid length" ); +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_ENTROPY_C) + case -(MBEDTLS_ERR_ENTROPY_SOURCE_FAILED): + return( "ENTROPY - Critical entropy source failure" ); + case -(MBEDTLS_ERR_ENTROPY_MAX_SOURCES): + return( "ENTROPY - No more sources can be added" ); + case -(MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED): + return( "ENTROPY - No sources have been added to poll" ); + case -(MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE): + return( "ENTROPY - No strong sources have been added to poll" ); + case -(MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR): + return( "ENTROPY - Read/write error in file" ); +#endif /* MBEDTLS_ENTROPY_C */ + +#if defined(MBEDTLS_ERROR_C) + case -(MBEDTLS_ERR_ERROR_GENERIC_ERROR): + return( "ERROR - Generic error" ); + case -(MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED): + return( "ERROR - This is a bug in the library" ); +#endif /* MBEDTLS_ERROR_C */ + +#if defined(MBEDTLS_PLATFORM_C) + case -(MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED): + return( "PLATFORM - Hardware accelerator failed" ); + case -(MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED): + return( "PLATFORM - The requested feature is not supported by the platform" ); +#endif /* MBEDTLS_PLATFORM_C */ + +#if defined(MBEDTLS_GCM_C) + case -(MBEDTLS_ERR_GCM_AUTH_FAILED): + return( "GCM - Authenticated decryption failed" ); + case -(MBEDTLS_ERR_GCM_BAD_INPUT): + return( "GCM - Bad input parameters to function" ); + case -(MBEDTLS_ERR_GCM_BUFFER_TOO_SMALL): + return( "GCM - An output buffer is too small" ); +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_HKDF_C) + case -(MBEDTLS_ERR_HKDF_BAD_INPUT_DATA): + return( "HKDF - Bad input parameters to function" ); +#endif /* MBEDTLS_HKDF_C */ + +#if defined(MBEDTLS_HMAC_DRBG_C) + case -(MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG): + return( "HMAC_DRBG - Too many random requested in single call" ); + case -(MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG): + return( "HMAC_DRBG - Input too large (Entropy + additional)" ); + case -(MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR): + return( "HMAC_DRBG - Read/write error in file" ); + case -(MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED): + return( "HMAC_DRBG - The entropy source failed" ); +#endif /* MBEDTLS_HMAC_DRBG_C */ + +#if defined(MBEDTLS_LMS_C) + case -(MBEDTLS_ERR_LMS_BAD_INPUT_DATA): + return( "LMS - Bad data has been input to an LMS function" ); + case -(MBEDTLS_ERR_LMS_OUT_OF_PRIVATE_KEYS): + return( "LMS - Specified LMS key has utilised all of its private keys" ); + case -(MBEDTLS_ERR_LMS_VERIFY_FAILED): + return( "LMS - LMS signature verification failed" ); + case -(MBEDTLS_ERR_LMS_ALLOC_FAILED): + return( "LMS - LMS failed to allocate space for a private key" ); + case -(MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL): + return( "LMS - Input/output buffer is too small to contain requited data" ); +#endif /* MBEDTLS_LMS_C */ + +#if defined(MBEDTLS_NET_C) + case -(MBEDTLS_ERR_NET_SOCKET_FAILED): + return( "NET - Failed to open a socket" ); + case -(MBEDTLS_ERR_NET_CONNECT_FAILED): + return( "NET - The connection to the given server / port failed" ); + case -(MBEDTLS_ERR_NET_BIND_FAILED): + return( "NET - Binding of the socket failed" ); + case -(MBEDTLS_ERR_NET_LISTEN_FAILED): + return( "NET - Could not listen on the socket" ); + case -(MBEDTLS_ERR_NET_ACCEPT_FAILED): + return( "NET - Could not accept the incoming connection" ); + case -(MBEDTLS_ERR_NET_RECV_FAILED): + return( "NET - Reading information from the socket failed" ); + case -(MBEDTLS_ERR_NET_SEND_FAILED): + return( "NET - Sending information through the socket failed" ); + case -(MBEDTLS_ERR_NET_CONN_RESET): + return( "NET - Connection was reset by peer" ); + case -(MBEDTLS_ERR_NET_UNKNOWN_HOST): + return( "NET - Failed to get an IP address for the given hostname" ); + case -(MBEDTLS_ERR_NET_BUFFER_TOO_SMALL): + return( "NET - Buffer is too small to hold the data" ); + case -(MBEDTLS_ERR_NET_INVALID_CONTEXT): + return( "NET - The context is invalid, eg because it was free()ed" ); + case -(MBEDTLS_ERR_NET_POLL_FAILED): + return( "NET - Polling the net context failed" ); + case -(MBEDTLS_ERR_NET_BAD_INPUT_DATA): + return( "NET - Input invalid" ); +#endif /* MBEDTLS_NET_C */ + +#if defined(MBEDTLS_OID_C) + case -(MBEDTLS_ERR_OID_NOT_FOUND): + return( "OID - OID is not found" ); + case -(MBEDTLS_ERR_OID_BUF_TOO_SMALL): + return( "OID - output buffer is too small" ); +#endif /* MBEDTLS_OID_C */ + +#if defined(MBEDTLS_POLY1305_C) + case -(MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA): + return( "POLY1305 - Invalid input parameter(s)" ); +#endif /* MBEDTLS_POLY1305_C */ + +#if defined(MBEDTLS_SHA1_C) + case -(MBEDTLS_ERR_SHA1_BAD_INPUT_DATA): + return( "SHA1 - SHA-1 input data was malformed" ); +#endif /* MBEDTLS_SHA1_C */ + +#if defined(MBEDTLS_SHA256_C) + case -(MBEDTLS_ERR_SHA256_BAD_INPUT_DATA): + return( "SHA256 - SHA-256 input data was malformed" ); +#endif /* MBEDTLS_SHA256_C */ + +#if defined(MBEDTLS_SHA512_C) + case -(MBEDTLS_ERR_SHA512_BAD_INPUT_DATA): + return( "SHA512 - SHA-512 input data was malformed" ); +#endif /* MBEDTLS_SHA512_C */ + +#if defined(MBEDTLS_THREADING_C) + case -(MBEDTLS_ERR_THREADING_BAD_INPUT_DATA): + return( "THREADING - Bad input parameters to function" ); + case -(MBEDTLS_ERR_THREADING_MUTEX_ERROR): + return( "THREADING - Locking / unlocking / free failed with error code" ); +#endif /* MBEDTLS_THREADING_C */ + /* End Auto-Generated Code. */ + + default: + break; + } + + return NULL; +} + +void mbedtls_strerror(int ret, char *buf, size_t buflen) +{ + size_t len; + int use_ret; + const char *high_level_error_description = NULL; + const char *low_level_error_description = NULL; + + if (buflen == 0) { + return; + } + + memset(buf, 0x00, buflen); + + if (ret < 0) { + ret = -ret; + } + + if (ret & 0xFF80) { + use_ret = ret & 0xFF80; + + // Translate high level error code. + high_level_error_description = mbedtls_high_level_strerr(ret); + + if (high_level_error_description == NULL) { + mbedtls_snprintf(buf, buflen, "UNKNOWN ERROR CODE (%04X)", (unsigned int) use_ret); + } else { + mbedtls_snprintf(buf, buflen, "%s", high_level_error_description); + } + +#if defined(MBEDTLS_SSL_TLS_C) + // Early return in case of a fatal error - do not try to translate low + // level code. + if (use_ret == -(MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE)) { + return; + } +#endif /* MBEDTLS_SSL_TLS_C */ + } + + use_ret = ret & ~0xFF80; + + if (use_ret == 0) { + return; + } + + // If high level code is present, make a concatenation between both + // error strings. + // + len = strlen(buf); + + if (len > 0) { + if (buflen - len < 5) { + return; + } + + mbedtls_snprintf(buf + len, buflen - len, " : "); + + buf += len + 3; + buflen -= len + 3; + } + + // Translate low level error code. + low_level_error_description = mbedtls_low_level_strerr(ret); + + if (low_level_error_description == NULL) { + mbedtls_snprintf(buf, buflen, "UNKNOWN ERROR CODE (%04X)", (unsigned int) use_ret); + } else { + mbedtls_snprintf(buf, buflen, "%s", low_level_error_description); + } +} + +#else /* MBEDTLS_ERROR_C */ + +/* + * Provide a dummy implementation when MBEDTLS_ERROR_C is not defined + */ +void mbedtls_strerror(int ret, char *buf, size_t buflen) +{ + ((void) ret); + + if (buflen > 0) { + buf[0] = '\0'; + } +} + +#endif /* MBEDTLS_ERROR_C */ + +#if defined(MBEDTLS_TEST_HOOKS) +void (*mbedtls_test_hook_error_add)(int, int, const char *, int); +#endif + +#endif /* MBEDTLS_ERROR_C || MBEDTLS_ERROR_STRERROR_DUMMY */ diff --git a/r5dev/thirdparty/mbedtls/gcm.c b/r5dev/thirdparty/mbedtls/gcm.c new file mode 100644 index 00000000..71fcc354 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/gcm.c @@ -0,0 +1,1168 @@ +/* + * NIST SP800-38D compliant GCM implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf + * + * See also: + * [MGV] http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf + * + * We use the algorithm described as Shoup's method with 4-bit tables in + * [MGV] 4.1, pp. 12-13, to enhance speed without using too much memory. + */ + +#include "common.h" + +#if defined(MBEDTLS_GCM_C) + +#include "mbedtls/gcm.h" +#include "mbedtls/platform.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_AESNI_C) +#include "aesni.h" +#endif + +#if defined(MBEDTLS_AESCE_C) +#include "aesce.h" +#endif + +#if !defined(MBEDTLS_GCM_ALT) + +/* + * Initialize a context + */ +void mbedtls_gcm_init(mbedtls_gcm_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_gcm_context)); +} + +/* + * Precompute small multiples of H, that is set + * HH[i] || HL[i] = H times i, + * where i is seen as a field element as in [MGV], ie high-order bits + * correspond to low powers of P. The result is stored in the same way, that + * is the high-order bit of HH corresponds to P^0 and the low-order bit of HL + * corresponds to P^127. + */ +static int gcm_gen_table(mbedtls_gcm_context *ctx) +{ + int ret, i, j; + uint64_t hi, lo; + uint64_t vl, vh; + unsigned char h[16]; + size_t olen = 0; + + memset(h, 0, 16); + if ((ret = mbedtls_cipher_update(&ctx->cipher_ctx, h, 16, h, &olen)) != 0) { + return ret; + } + + /* pack h as two 64-bits ints, big-endian */ + hi = MBEDTLS_GET_UINT32_BE(h, 0); + lo = MBEDTLS_GET_UINT32_BE(h, 4); + vh = (uint64_t) hi << 32 | lo; + + hi = MBEDTLS_GET_UINT32_BE(h, 8); + lo = MBEDTLS_GET_UINT32_BE(h, 12); + vl = (uint64_t) hi << 32 | lo; + + /* 8 = 1000 corresponds to 1 in GF(2^128) */ + ctx->HL[8] = vl; + ctx->HH[8] = vh; + +#if defined(MBEDTLS_AESNI_HAVE_CODE) + /* With CLMUL support, we need only h, not the rest of the table */ + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) { + return 0; + } +#endif + +#if defined(MBEDTLS_AESCE_C) && defined(MBEDTLS_HAVE_ARM64) + if (mbedtls_aesce_has_support()) { + return 0; + } +#endif + + /* 0 corresponds to 0 in GF(2^128) */ + ctx->HH[0] = 0; + ctx->HL[0] = 0; + + for (i = 4; i > 0; i >>= 1) { + uint32_t T = (vl & 1) * 0xe1000000U; + vl = (vh << 63) | (vl >> 1); + vh = (vh >> 1) ^ ((uint64_t) T << 32); + + ctx->HL[i] = vl; + ctx->HH[i] = vh; + } + + for (i = 2; i <= 8; i *= 2) { + uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i; + vh = *HiH; + vl = *HiL; + for (j = 1; j < i; j++) { + HiH[j] = vh ^ ctx->HH[j]; + HiL[j] = vl ^ ctx->HL[j]; + } + } + + return 0; +} + +int mbedtls_gcm_setkey(mbedtls_gcm_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_cipher_info_t *cipher_info; + + if (keybits != 128 && keybits != 192 && keybits != 256) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + cipher_info = mbedtls_cipher_info_from_values(cipher, keybits, + MBEDTLS_MODE_ECB); + if (cipher_info == NULL) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + if (cipher_info->block_size != 16) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + mbedtls_cipher_free(&ctx->cipher_ctx); + + if ((ret = mbedtls_cipher_setup(&ctx->cipher_ctx, cipher_info)) != 0) { + return ret; + } + + if ((ret = mbedtls_cipher_setkey(&ctx->cipher_ctx, key, keybits, + MBEDTLS_ENCRYPT)) != 0) { + return ret; + } + + if ((ret = gcm_gen_table(ctx)) != 0) { + return ret; + } + + return 0; +} + +/* + * Shoup's method for multiplication use this table with + * last4[x] = x times P^128 + * where x and last4[x] are seen as elements of GF(2^128) as in [MGV] + */ +static const uint64_t last4[16] = +{ + 0x0000, 0x1c20, 0x3840, 0x2460, + 0x7080, 0x6ca0, 0x48c0, 0x54e0, + 0xe100, 0xfd20, 0xd940, 0xc560, + 0x9180, 0x8da0, 0xa9c0, 0xb5e0 +}; + +/* + * Sets output to x times H using the precomputed tables. + * x and output are seen as elements of GF(2^128) as in [MGV]. + */ +static void gcm_mult(mbedtls_gcm_context *ctx, const unsigned char x[16], + unsigned char output[16]) +{ + int i = 0; + unsigned char lo, hi, rem; + uint64_t zh, zl; + +#if defined(MBEDTLS_AESNI_HAVE_CODE) + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) { + unsigned char h[16]; + + /* mbedtls_aesni_gcm_mult needs big-endian input */ + MBEDTLS_PUT_UINT32_BE(ctx->HH[8] >> 32, h, 0); + MBEDTLS_PUT_UINT32_BE(ctx->HH[8], h, 4); + MBEDTLS_PUT_UINT32_BE(ctx->HL[8] >> 32, h, 8); + MBEDTLS_PUT_UINT32_BE(ctx->HL[8], h, 12); + + mbedtls_aesni_gcm_mult(output, x, h); + return; + } +#endif /* MBEDTLS_AESNI_HAVE_CODE */ + +#if defined(MBEDTLS_AESCE_C) && defined(MBEDTLS_HAVE_ARM64) + if (mbedtls_aesce_has_support()) { + unsigned char h[16]; + + /* mbedtls_aesce_gcm_mult needs big-endian input */ + MBEDTLS_PUT_UINT32_BE(ctx->HH[8] >> 32, h, 0); + MBEDTLS_PUT_UINT32_BE(ctx->HH[8], h, 4); + MBEDTLS_PUT_UINT32_BE(ctx->HL[8] >> 32, h, 8); + MBEDTLS_PUT_UINT32_BE(ctx->HL[8], h, 12); + + mbedtls_aesce_gcm_mult(output, x, h); + return; + } +#endif + + lo = x[15] & 0xf; + + zh = ctx->HH[lo]; + zl = ctx->HL[lo]; + + for (i = 15; i >= 0; i--) { + lo = x[i] & 0xf; + hi = (x[i] >> 4) & 0xf; + + if (i != 15) { + rem = (unsigned char) zl & 0xf; + zl = (zh << 60) | (zl >> 4); + zh = (zh >> 4); + zh ^= (uint64_t) last4[rem] << 48; + zh ^= ctx->HH[lo]; + zl ^= ctx->HL[lo]; + + } + + rem = (unsigned char) zl & 0xf; + zl = (zh << 60) | (zl >> 4); + zh = (zh >> 4); + zh ^= (uint64_t) last4[rem] << 48; + zh ^= ctx->HH[hi]; + zl ^= ctx->HL[hi]; + } + + MBEDTLS_PUT_UINT32_BE(zh >> 32, output, 0); + MBEDTLS_PUT_UINT32_BE(zh, output, 4); + MBEDTLS_PUT_UINT32_BE(zl >> 32, output, 8); + MBEDTLS_PUT_UINT32_BE(zl, output, 12); +} + +int mbedtls_gcm_starts(mbedtls_gcm_context *ctx, + int mode, + const unsigned char *iv, size_t iv_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char work_buf[16]; + const unsigned char *p; + size_t use_len, olen = 0; + uint64_t iv_bits; + + /* IV is limited to 2^64 bits, so 2^61 bytes */ + /* IV is not allowed to be zero length */ + if (iv_len == 0 || (uint64_t) iv_len >> 61 != 0) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + memset(ctx->y, 0x00, sizeof(ctx->y)); + memset(ctx->buf, 0x00, sizeof(ctx->buf)); + + ctx->mode = mode; + ctx->len = 0; + ctx->add_len = 0; + + if (iv_len == 12) { + memcpy(ctx->y, iv, iv_len); + ctx->y[15] = 1; + } else { + memset(work_buf, 0x00, 16); + iv_bits = (uint64_t) iv_len * 8; + MBEDTLS_PUT_UINT64_BE(iv_bits, work_buf, 8); + + p = iv; + while (iv_len > 0) { + use_len = (iv_len < 16) ? iv_len : 16; + + mbedtls_xor(ctx->y, ctx->y, p, use_len); + + gcm_mult(ctx, ctx->y, ctx->y); + + iv_len -= use_len; + p += use_len; + } + + mbedtls_xor(ctx->y, ctx->y, work_buf, 16); + + gcm_mult(ctx, ctx->y, ctx->y); + } + + if ((ret = mbedtls_cipher_update(&ctx->cipher_ctx, ctx->y, 16, + ctx->base_ectr, &olen)) != 0) { + return ret; + } + + return 0; +} + +/** + * mbedtls_gcm_context::buf contains the partial state of the computation of + * the authentication tag. + * mbedtls_gcm_context::add_len and mbedtls_gcm_context::len indicate + * different stages of the computation: + * * len == 0 && add_len == 0: initial state + * * len == 0 && add_len % 16 != 0: the first `add_len % 16` bytes have + * a partial block of AD that has been + * xored in but not yet multiplied in. + * * len == 0 && add_len % 16 == 0: the authentication tag is correct if + * the data ends now. + * * len % 16 != 0: the first `len % 16` bytes have + * a partial block of ciphertext that has + * been xored in but not yet multiplied in. + * * len > 0 && len % 16 == 0: the authentication tag is correct if + * the data ends now. + */ +int mbedtls_gcm_update_ad(mbedtls_gcm_context *ctx, + const unsigned char *add, size_t add_len) +{ + const unsigned char *p; + size_t use_len, offset; + + /* IV is limited to 2^64 bits, so 2^61 bytes */ + if ((uint64_t) add_len >> 61 != 0) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + offset = ctx->add_len % 16; + p = add; + + if (offset != 0) { + use_len = 16 - offset; + if (use_len > add_len) { + use_len = add_len; + } + + mbedtls_xor(ctx->buf + offset, ctx->buf + offset, p, use_len); + + if (offset + use_len == 16) { + gcm_mult(ctx, ctx->buf, ctx->buf); + } + + ctx->add_len += use_len; + add_len -= use_len; + p += use_len; + } + + ctx->add_len += add_len; + + while (add_len >= 16) { + mbedtls_xor(ctx->buf, ctx->buf, p, 16); + + gcm_mult(ctx, ctx->buf, ctx->buf); + + add_len -= 16; + p += 16; + } + + if (add_len > 0) { + mbedtls_xor(ctx->buf, ctx->buf, p, add_len); + } + + return 0; +} + +/* Increment the counter. */ +static void gcm_incr(unsigned char y[16]) +{ + size_t i; + for (i = 16; i > 12; i--) { + if (++y[i - 1] != 0) { + break; + } + } +} + +/* Calculate and apply the encryption mask. Process use_len bytes of data, + * starting at position offset in the mask block. */ +static int gcm_mask(mbedtls_gcm_context *ctx, + unsigned char ectr[16], + size_t offset, size_t use_len, + const unsigned char *input, + unsigned char *output) +{ + size_t olen = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_cipher_update(&ctx->cipher_ctx, ctx->y, 16, ectr, + &olen)) != 0) { + mbedtls_platform_zeroize(ectr, 16); + return ret; + } + + if (ctx->mode == MBEDTLS_GCM_DECRYPT) { + mbedtls_xor(ctx->buf + offset, ctx->buf + offset, input, use_len); + } + mbedtls_xor(output, ectr + offset, input, use_len); + if (ctx->mode == MBEDTLS_GCM_ENCRYPT) { + mbedtls_xor(ctx->buf + offset, ctx->buf + offset, output, use_len); + } + + return 0; +} + +int mbedtls_gcm_update(mbedtls_gcm_context *ctx, + const unsigned char *input, size_t input_length, + unsigned char *output, size_t output_size, + size_t *output_length) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = input; + unsigned char *out_p = output; + size_t offset; + unsigned char ectr[16] = { 0 }; + + if (output_size < input_length) { + return MBEDTLS_ERR_GCM_BUFFER_TOO_SMALL; + } + *output_length = input_length; + + /* Exit early if input_length==0 so that we don't do any pointer arithmetic + * on a potentially null pointer. + * Returning early also means that the last partial block of AD remains + * untouched for mbedtls_gcm_finish */ + if (input_length == 0) { + return 0; + } + + if (output > input && (size_t) (output - input) < input_length) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + /* Total length is restricted to 2^39 - 256 bits, ie 2^36 - 2^5 bytes + * Also check for possible overflow */ + if (ctx->len + input_length < ctx->len || + (uint64_t) ctx->len + input_length > 0xFFFFFFFE0ull) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + if (ctx->len == 0 && ctx->add_len % 16 != 0) { + gcm_mult(ctx, ctx->buf, ctx->buf); + } + + offset = ctx->len % 16; + if (offset != 0) { + size_t use_len = 16 - offset; + if (use_len > input_length) { + use_len = input_length; + } + + if ((ret = gcm_mask(ctx, ectr, offset, use_len, p, out_p)) != 0) { + return ret; + } + + if (offset + use_len == 16) { + gcm_mult(ctx, ctx->buf, ctx->buf); + } + + ctx->len += use_len; + input_length -= use_len; + p += use_len; + out_p += use_len; + } + + ctx->len += input_length; + + while (input_length >= 16) { + gcm_incr(ctx->y); + if ((ret = gcm_mask(ctx, ectr, 0, 16, p, out_p)) != 0) { + return ret; + } + + gcm_mult(ctx, ctx->buf, ctx->buf); + + input_length -= 16; + p += 16; + out_p += 16; + } + + if (input_length > 0) { + gcm_incr(ctx->y); + if ((ret = gcm_mask(ctx, ectr, 0, input_length, p, out_p)) != 0) { + return ret; + } + } + + mbedtls_platform_zeroize(ectr, sizeof(ectr)); + return 0; +} + +int mbedtls_gcm_finish(mbedtls_gcm_context *ctx, + unsigned char *output, size_t output_size, + size_t *output_length, + unsigned char *tag, size_t tag_len) +{ + unsigned char work_buf[16]; + uint64_t orig_len; + uint64_t orig_add_len; + + /* We never pass any output in finish(). The output parameter exists only + * for the sake of alternative implementations. */ + (void) output; + (void) output_size; + *output_length = 0; + + orig_len = ctx->len * 8; + orig_add_len = ctx->add_len * 8; + + if (ctx->len == 0 && ctx->add_len % 16 != 0) { + gcm_mult(ctx, ctx->buf, ctx->buf); + } + + if (tag_len > 16 || tag_len < 4) { + return MBEDTLS_ERR_GCM_BAD_INPUT; + } + + if (ctx->len % 16 != 0) { + gcm_mult(ctx, ctx->buf, ctx->buf); + } + + memcpy(tag, ctx->base_ectr, tag_len); + + if (orig_len || orig_add_len) { + memset(work_buf, 0x00, 16); + + MBEDTLS_PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0); + MBEDTLS_PUT_UINT32_BE((orig_add_len), work_buf, 4); + MBEDTLS_PUT_UINT32_BE((orig_len >> 32), work_buf, 8); + MBEDTLS_PUT_UINT32_BE((orig_len), work_buf, 12); + + mbedtls_xor(ctx->buf, ctx->buf, work_buf, 16); + + gcm_mult(ctx, ctx->buf, ctx->buf); + + mbedtls_xor(tag, tag, ctx->buf, tag_len); + } + + return 0; +} + +int mbedtls_gcm_crypt_and_tag(mbedtls_gcm_context *ctx, + int mode, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *input, + unsigned char *output, + size_t tag_len, + unsigned char *tag) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen; + + if ((ret = mbedtls_gcm_starts(ctx, mode, iv, iv_len)) != 0) { + return ret; + } + + if ((ret = mbedtls_gcm_update_ad(ctx, add, add_len)) != 0) { + return ret; + } + + if ((ret = mbedtls_gcm_update(ctx, input, length, + output, length, &olen)) != 0) { + return ret; + } + + if ((ret = mbedtls_gcm_finish(ctx, NULL, 0, &olen, tag, tag_len)) != 0) { + return ret; + } + + return 0; +} + +int mbedtls_gcm_auth_decrypt(mbedtls_gcm_context *ctx, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *tag, + size_t tag_len, + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char check_tag[16]; + size_t i; + int diff; + + if ((ret = mbedtls_gcm_crypt_and_tag(ctx, MBEDTLS_GCM_DECRYPT, length, + iv, iv_len, add, add_len, + input, output, tag_len, check_tag)) != 0) { + return ret; + } + + /* Check tag in "constant-time" */ + for (diff = 0, i = 0; i < tag_len; i++) { + diff |= tag[i] ^ check_tag[i]; + } + + if (diff != 0) { + mbedtls_platform_zeroize(output, length); + return MBEDTLS_ERR_GCM_AUTH_FAILED; + } + + return 0; +} + +void mbedtls_gcm_free(mbedtls_gcm_context *ctx) +{ + if (ctx == NULL) { + return; + } + mbedtls_cipher_free(&ctx->cipher_ctx); + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_gcm_context)); +} + +#endif /* !MBEDTLS_GCM_ALT */ + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) +/* + * AES-GCM test vectors from: + * + * http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip + */ +#define MAX_TESTS 6 + +static const int key_index_test_data[MAX_TESTS] = +{ 0, 0, 1, 1, 1, 1 }; + +static const unsigned char key_test_data[MAX_TESTS][32] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }, +}; + +static const size_t iv_len_test_data[MAX_TESTS] = +{ 12, 12, 12, 12, 8, 60 }; + +static const int iv_index_test_data[MAX_TESTS] = +{ 0, 0, 1, 1, 1, 2 }; + +static const unsigned char iv_test_data[MAX_TESTS][64] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }, + { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, + 0xde, 0xca, 0xf8, 0x88 }, + { 0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5, + 0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa, + 0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1, + 0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28, + 0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39, + 0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54, + 0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57, + 0xa6, 0x37, 0xb3, 0x9b }, +}; + +static const size_t add_len_test_data[MAX_TESTS] = +{ 0, 0, 0, 20, 20, 20 }; + +static const int add_index_test_data[MAX_TESTS] = +{ 0, 0, 0, 1, 1, 1 }; + +static const unsigned char additional_test_data[MAX_TESTS][64] = +{ + { 0x00 }, + { 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xab, 0xad, 0xda, 0xd2 }, +}; + +static const size_t pt_len_test_data[MAX_TESTS] = +{ 0, 16, 64, 60, 60, 60 }; + +static const int pt_index_test_data[MAX_TESTS] = +{ 0, 0, 1, 1, 1, 1 }; + +static const unsigned char pt_test_data[MAX_TESTS][64] = +{ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, + 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 }, +}; + +static const unsigned char ct_test_data[MAX_TESTS * 3][64] = +{ + { 0x00 }, + { 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, + 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78 }, + { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }, + { 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91 }, + { 0x61, 0x35, 0x3b, 0x4c, 0x28, 0x06, 0x93, 0x4a, + 0x77, 0x7f, 0xf5, 0x1f, 0xa2, 0x2a, 0x47, 0x55, + 0x69, 0x9b, 0x2a, 0x71, 0x4f, 0xcd, 0xc6, 0xf8, + 0x37, 0x66, 0xe5, 0xf9, 0x7b, 0x6c, 0x74, 0x23, + 0x73, 0x80, 0x69, 0x00, 0xe4, 0x9f, 0x24, 0xb2, + 0x2b, 0x09, 0x75, 0x44, 0xd4, 0x89, 0x6b, 0x42, + 0x49, 0x89, 0xb5, 0xe1, 0xeb, 0xac, 0x0f, 0x07, + 0xc2, 0x3f, 0x45, 0x98 }, + { 0x8c, 0xe2, 0x49, 0x98, 0x62, 0x56, 0x15, 0xb6, + 0x03, 0xa0, 0x33, 0xac, 0xa1, 0x3f, 0xb8, 0x94, + 0xbe, 0x91, 0x12, 0xa5, 0xc3, 0xa2, 0x11, 0xa8, + 0xba, 0x26, 0x2a, 0x3c, 0xca, 0x7e, 0x2c, 0xa7, + 0x01, 0xe4, 0xa9, 0xa4, 0xfb, 0xa4, 0x3c, 0x90, + 0xcc, 0xdc, 0xb2, 0x81, 0xd4, 0x8c, 0x7c, 0x6f, + 0xd6, 0x28, 0x75, 0xd2, 0xac, 0xa4, 0x17, 0x03, + 0x4c, 0x34, 0xae, 0xe5 }, + { 0x00 }, + { 0x98, 0xe7, 0x24, 0x7c, 0x07, 0xf0, 0xfe, 0x41, + 0x1c, 0x26, 0x7e, 0x43, 0x84, 0xb0, 0xf6, 0x00 }, + { 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, + 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, + 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, + 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, + 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, + 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, + 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, + 0xcc, 0xda, 0x27, 0x10, 0xac, 0xad, 0xe2, 0x56 }, + { 0x39, 0x80, 0xca, 0x0b, 0x3c, 0x00, 0xe8, 0x41, + 0xeb, 0x06, 0xfa, 0xc4, 0x87, 0x2a, 0x27, 0x57, + 0x85, 0x9e, 0x1c, 0xea, 0xa6, 0xef, 0xd9, 0x84, + 0x62, 0x85, 0x93, 0xb4, 0x0c, 0xa1, 0xe1, 0x9c, + 0x7d, 0x77, 0x3d, 0x00, 0xc1, 0x44, 0xc5, 0x25, + 0xac, 0x61, 0x9d, 0x18, 0xc8, 0x4a, 0x3f, 0x47, + 0x18, 0xe2, 0x44, 0x8b, 0x2f, 0xe3, 0x24, 0xd9, + 0xcc, 0xda, 0x27, 0x10 }, + { 0x0f, 0x10, 0xf5, 0x99, 0xae, 0x14, 0xa1, 0x54, + 0xed, 0x24, 0xb3, 0x6e, 0x25, 0x32, 0x4d, 0xb8, + 0xc5, 0x66, 0x63, 0x2e, 0xf2, 0xbb, 0xb3, 0x4f, + 0x83, 0x47, 0x28, 0x0f, 0xc4, 0x50, 0x70, 0x57, + 0xfd, 0xdc, 0x29, 0xdf, 0x9a, 0x47, 0x1f, 0x75, + 0xc6, 0x65, 0x41, 0xd4, 0xd4, 0xda, 0xd1, 0xc9, + 0xe9, 0x3a, 0x19, 0xa5, 0x8e, 0x8b, 0x47, 0x3f, + 0xa0, 0xf0, 0x62, 0xf7 }, + { 0xd2, 0x7e, 0x88, 0x68, 0x1c, 0xe3, 0x24, 0x3c, + 0x48, 0x30, 0x16, 0x5a, 0x8f, 0xdc, 0xf9, 0xff, + 0x1d, 0xe9, 0xa1, 0xd8, 0xe6, 0xb4, 0x47, 0xef, + 0x6e, 0xf7, 0xb7, 0x98, 0x28, 0x66, 0x6e, 0x45, + 0x81, 0xe7, 0x90, 0x12, 0xaf, 0x34, 0xdd, 0xd9, + 0xe2, 0xf0, 0x37, 0x58, 0x9b, 0x29, 0x2d, 0xb3, + 0xe6, 0x7c, 0x03, 0x67, 0x45, 0xfa, 0x22, 0xe7, + 0xe9, 0xb7, 0x37, 0x3b }, + { 0x00 }, + { 0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e, + 0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18 }, + { 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad }, + { 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62 }, + { 0xc3, 0x76, 0x2d, 0xf1, 0xca, 0x78, 0x7d, 0x32, + 0xae, 0x47, 0xc1, 0x3b, 0xf1, 0x98, 0x44, 0xcb, + 0xaf, 0x1a, 0xe1, 0x4d, 0x0b, 0x97, 0x6a, 0xfa, + 0xc5, 0x2f, 0xf7, 0xd7, 0x9b, 0xba, 0x9d, 0xe0, + 0xfe, 0xb5, 0x82, 0xd3, 0x39, 0x34, 0xa4, 0xf0, + 0x95, 0x4c, 0xc2, 0x36, 0x3b, 0xc7, 0x3f, 0x78, + 0x62, 0xac, 0x43, 0x0e, 0x64, 0xab, 0xe4, 0x99, + 0xf4, 0x7c, 0x9b, 0x1f }, + { 0x5a, 0x8d, 0xef, 0x2f, 0x0c, 0x9e, 0x53, 0xf1, + 0xf7, 0x5d, 0x78, 0x53, 0x65, 0x9e, 0x2a, 0x20, + 0xee, 0xb2, 0xb2, 0x2a, 0xaf, 0xde, 0x64, 0x19, + 0xa0, 0x58, 0xab, 0x4f, 0x6f, 0x74, 0x6b, 0xf4, + 0x0f, 0xc0, 0xc3, 0xb7, 0x80, 0xf2, 0x44, 0x45, + 0x2d, 0xa3, 0xeb, 0xf1, 0xc5, 0xd8, 0x2c, 0xde, + 0xa2, 0x41, 0x89, 0x97, 0x20, 0x0e, 0xf8, 0x2e, + 0x44, 0xae, 0x7e, 0x3f }, +}; + +static const unsigned char tag_test_data[MAX_TESTS * 3][16] = +{ + { 0x58, 0xe2, 0xfc, 0xce, 0xfa, 0x7e, 0x30, 0x61, + 0x36, 0x7f, 0x1d, 0x57, 0xa4, 0xe7, 0x45, 0x5a }, + { 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, + 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf }, + { 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, + 0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 }, + { 0x5b, 0xc9, 0x4f, 0xbc, 0x32, 0x21, 0xa5, 0xdb, + 0x94, 0xfa, 0xe9, 0x5a, 0xe7, 0x12, 0x1a, 0x47 }, + { 0x36, 0x12, 0xd2, 0xe7, 0x9e, 0x3b, 0x07, 0x85, + 0x56, 0x1b, 0xe1, 0x4a, 0xac, 0xa2, 0xfc, 0xcb }, + { 0x61, 0x9c, 0xc5, 0xae, 0xff, 0xfe, 0x0b, 0xfa, + 0x46, 0x2a, 0xf4, 0x3c, 0x16, 0x99, 0xd0, 0x50 }, + { 0xcd, 0x33, 0xb2, 0x8a, 0xc7, 0x73, 0xf7, 0x4b, + 0xa0, 0x0e, 0xd1, 0xf3, 0x12, 0x57, 0x24, 0x35 }, + { 0x2f, 0xf5, 0x8d, 0x80, 0x03, 0x39, 0x27, 0xab, + 0x8e, 0xf4, 0xd4, 0x58, 0x75, 0x14, 0xf0, 0xfb }, + { 0x99, 0x24, 0xa7, 0xc8, 0x58, 0x73, 0x36, 0xbf, + 0xb1, 0x18, 0x02, 0x4d, 0xb8, 0x67, 0x4a, 0x14 }, + { 0x25, 0x19, 0x49, 0x8e, 0x80, 0xf1, 0x47, 0x8f, + 0x37, 0xba, 0x55, 0xbd, 0x6d, 0x27, 0x61, 0x8c }, + { 0x65, 0xdc, 0xc5, 0x7f, 0xcf, 0x62, 0x3a, 0x24, + 0x09, 0x4f, 0xcc, 0xa4, 0x0d, 0x35, 0x33, 0xf8 }, + { 0xdc, 0xf5, 0x66, 0xff, 0x29, 0x1c, 0x25, 0xbb, + 0xb8, 0x56, 0x8f, 0xc3, 0xd3, 0x76, 0xa6, 0xd9 }, + { 0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, + 0xa9, 0x63, 0xb4, 0xf1, 0xc4, 0xcb, 0x73, 0x8b }, + { 0xd0, 0xd1, 0xc8, 0xa7, 0x99, 0x99, 0x6b, 0xf0, + 0x26, 0x5b, 0x98, 0xb5, 0xd4, 0x8a, 0xb9, 0x19 }, + { 0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd, + 0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c }, + { 0x76, 0xfc, 0x6e, 0xce, 0x0f, 0x4e, 0x17, 0x68, + 0xcd, 0xdf, 0x88, 0x53, 0xbb, 0x2d, 0x55, 0x1b }, + { 0x3a, 0x33, 0x7d, 0xbf, 0x46, 0xa7, 0x92, 0xc4, + 0x5e, 0x45, 0x49, 0x13, 0xfe, 0x2e, 0xa8, 0xf2 }, + { 0xa4, 0x4a, 0x82, 0x66, 0xee, 0x1c, 0x8e, 0xb0, + 0xc8, 0xb5, 0xd4, 0xcf, 0x5a, 0xe9, 0xf1, 0x9a }, +}; + +int mbedtls_gcm_self_test(int verbose) +{ + mbedtls_gcm_context ctx; + unsigned char buf[64]; + unsigned char tag_buf[16]; + int i, j, ret; + mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES; + size_t olen; + + if (verbose != 0) { +#if defined(MBEDTLS_GCM_ALT) + mbedtls_printf(" GCM note: alternative implementation.\n"); +#else /* MBEDTLS_GCM_ALT */ +#if defined(MBEDTLS_AESNI_HAVE_CODE) + if (mbedtls_aesni_has_support(MBEDTLS_AESNI_CLMUL)) { + mbedtls_printf(" GCM note: using AESNI.\n"); + } else +#endif + mbedtls_printf(" GCM note: built-in implementation.\n"); +#endif /* MBEDTLS_GCM_ALT */ + } + + for (j = 0; j < 3; j++) { + int key_len = 128 + 64 * j; + + for (i = 0; i < MAX_TESTS; i++) { + mbedtls_gcm_init(&ctx); + + if (verbose != 0) { + mbedtls_printf(" AES-GCM-%3d #%d (%s): ", + key_len, i, "enc"); + } + + ret = mbedtls_gcm_setkey(&ctx, cipher, + key_test_data[key_index_test_data[i]], + key_len); + /* + * AES-192 is an optional feature that may be unavailable when + * there is an alternative underlying implementation i.e. when + * MBEDTLS_AES_ALT is defined. + */ + if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED && key_len == 192) { + mbedtls_printf("skipped\n"); + break; + } else if (ret != 0) { + goto exit; + } + + ret = mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_ENCRYPT, + pt_len_test_data[i], + iv_test_data[iv_index_test_data[i]], + iv_len_test_data[i], + additional_test_data[add_index_test_data[i]], + add_len_test_data[i], + pt_test_data[pt_index_test_data[i]], + buf, 16, tag_buf); +#if defined(MBEDTLS_GCM_ALT) + /* Allow alternative implementations to only support 12-byte nonces. */ + if (ret == MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED && + iv_len_test_data[i] != 12) { + mbedtls_printf("skipped\n"); + break; + } +#endif /* defined(MBEDTLS_GCM_ALT) */ + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, ct_test_data[j * 6 + i], + pt_len_test_data[i]) != 0 || + memcmp(tag_buf, tag_test_data[j * 6 + i], 16) != 0) { + ret = 1; + goto exit; + } + + mbedtls_gcm_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + mbedtls_gcm_init(&ctx); + + if (verbose != 0) { + mbedtls_printf(" AES-GCM-%3d #%d (%s): ", + key_len, i, "dec"); + } + + ret = mbedtls_gcm_setkey(&ctx, cipher, + key_test_data[key_index_test_data[i]], + key_len); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_DECRYPT, + pt_len_test_data[i], + iv_test_data[iv_index_test_data[i]], + iv_len_test_data[i], + additional_test_data[add_index_test_data[i]], + add_len_test_data[i], + ct_test_data[j * 6 + i], buf, 16, tag_buf); + + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, pt_test_data[pt_index_test_data[i]], + pt_len_test_data[i]) != 0 || + memcmp(tag_buf, tag_test_data[j * 6 + i], 16) != 0) { + ret = 1; + goto exit; + } + + mbedtls_gcm_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + mbedtls_gcm_init(&ctx); + + if (verbose != 0) { + mbedtls_printf(" AES-GCM-%3d #%d split (%s): ", + key_len, i, "enc"); + } + + ret = mbedtls_gcm_setkey(&ctx, cipher, + key_test_data[key_index_test_data[i]], + key_len); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_gcm_starts(&ctx, MBEDTLS_GCM_ENCRYPT, + iv_test_data[iv_index_test_data[i]], + iv_len_test_data[i]); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_gcm_update_ad(&ctx, + additional_test_data[add_index_test_data[i]], + add_len_test_data[i]); + if (ret != 0) { + goto exit; + } + + if (pt_len_test_data[i] > 32) { + size_t rest_len = pt_len_test_data[i] - 32; + ret = mbedtls_gcm_update(&ctx, + pt_test_data[pt_index_test_data[i]], + 32, + buf, sizeof(buf), &olen); + if (ret != 0) { + goto exit; + } + if (olen != 32) { + goto exit; + } + + ret = mbedtls_gcm_update(&ctx, + pt_test_data[pt_index_test_data[i]] + 32, + rest_len, + buf + 32, sizeof(buf) - 32, &olen); + if (ret != 0) { + goto exit; + } + if (olen != rest_len) { + goto exit; + } + } else { + ret = mbedtls_gcm_update(&ctx, + pt_test_data[pt_index_test_data[i]], + pt_len_test_data[i], + buf, sizeof(buf), &olen); + if (ret != 0) { + goto exit; + } + if (olen != pt_len_test_data[i]) { + goto exit; + } + } + + ret = mbedtls_gcm_finish(&ctx, NULL, 0, &olen, tag_buf, 16); + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, ct_test_data[j * 6 + i], + pt_len_test_data[i]) != 0 || + memcmp(tag_buf, tag_test_data[j * 6 + i], 16) != 0) { + ret = 1; + goto exit; + } + + mbedtls_gcm_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + mbedtls_gcm_init(&ctx); + + if (verbose != 0) { + mbedtls_printf(" AES-GCM-%3d #%d split (%s): ", + key_len, i, "dec"); + } + + ret = mbedtls_gcm_setkey(&ctx, cipher, + key_test_data[key_index_test_data[i]], + key_len); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_gcm_starts(&ctx, MBEDTLS_GCM_DECRYPT, + iv_test_data[iv_index_test_data[i]], + iv_len_test_data[i]); + if (ret != 0) { + goto exit; + } + ret = mbedtls_gcm_update_ad(&ctx, + additional_test_data[add_index_test_data[i]], + add_len_test_data[i]); + if (ret != 0) { + goto exit; + } + + if (pt_len_test_data[i] > 32) { + size_t rest_len = pt_len_test_data[i] - 32; + ret = mbedtls_gcm_update(&ctx, + ct_test_data[j * 6 + i], 32, + buf, sizeof(buf), &olen); + if (ret != 0) { + goto exit; + } + if (olen != 32) { + goto exit; + } + + ret = mbedtls_gcm_update(&ctx, + ct_test_data[j * 6 + i] + 32, + rest_len, + buf + 32, sizeof(buf) - 32, &olen); + if (ret != 0) { + goto exit; + } + if (olen != rest_len) { + goto exit; + } + } else { + ret = mbedtls_gcm_update(&ctx, + ct_test_data[j * 6 + i], + pt_len_test_data[i], + buf, sizeof(buf), &olen); + if (ret != 0) { + goto exit; + } + if (olen != pt_len_test_data[i]) { + goto exit; + } + } + + ret = mbedtls_gcm_finish(&ctx, NULL, 0, &olen, tag_buf, 16); + if (ret != 0) { + goto exit; + } + + if (memcmp(buf, pt_test_data[pt_index_test_data[i]], + pt_len_test_data[i]) != 0 || + memcmp(tag_buf, tag_test_data[j * 6 + i], 16) != 0) { + ret = 1; + goto exit; + } + + mbedtls_gcm_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + ret = 0; + +exit: + if (ret != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + mbedtls_gcm_free(&ctx); + } + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#endif /* MBEDTLS_GCM_C */ diff --git a/r5dev/thirdparty/mbedtls/hash_info.c b/r5dev/thirdparty/mbedtls/hash_info.c new file mode 100644 index 00000000..0e445b6c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/hash_info.c @@ -0,0 +1,123 @@ +/* + * Hash information that's independent from the crypto implementation. + * + * (See the corresponding header file for usage notes.) + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hash_info.h" +#include "mbedtls/legacy_or_psa.h" +#include "mbedtls/error.h" + +typedef struct { + psa_algorithm_t psa_alg; + mbedtls_md_type_t md_type; + unsigned char size; + unsigned char block_size; +} hash_entry; + +static const hash_entry hash_table[] = { +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_LOWLEVEL_OR_PSA) + { PSA_ALG_MD5, MBEDTLS_MD_MD5, 16, 64 }, +#endif +#if defined(MBEDTLS_HAS_ALG_RIPEMD160_VIA_LOWLEVEL_OR_PSA) + { PSA_ALG_RIPEMD160, MBEDTLS_MD_RIPEMD160, 20, 64 }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA) + { PSA_ALG_SHA_1, MBEDTLS_MD_SHA1, 20, 64 }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_LOWLEVEL_OR_PSA) + { PSA_ALG_SHA_224, MBEDTLS_MD_SHA224, 28, 64 }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA) + { PSA_ALG_SHA_256, MBEDTLS_MD_SHA256, 32, 64 }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA) + { PSA_ALG_SHA_384, MBEDTLS_MD_SHA384, 48, 128 }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA) + { PSA_ALG_SHA_512, MBEDTLS_MD_SHA512, 64, 128 }, +#endif + { PSA_ALG_NONE, MBEDTLS_MD_NONE, 0, 0 }, +}; + +/* Get size from MD type */ +unsigned char mbedtls_hash_info_get_size(mbedtls_md_type_t md_type) +{ + const hash_entry *entry = hash_table; + while (entry->md_type != MBEDTLS_MD_NONE && + entry->md_type != md_type) { + entry++; + } + + return entry->size; +} + +/* Get block size from MD type */ +unsigned char mbedtls_hash_info_get_block_size(mbedtls_md_type_t md_type) +{ + const hash_entry *entry = hash_table; + while (entry->md_type != MBEDTLS_MD_NONE && + entry->md_type != md_type) { + entry++; + } + + return entry->block_size; +} + +/* Get PSA from MD */ +psa_algorithm_t mbedtls_hash_info_psa_from_md(mbedtls_md_type_t md_type) +{ + const hash_entry *entry = hash_table; + while (entry->md_type != MBEDTLS_MD_NONE && + entry->md_type != md_type) { + entry++; + } + + return entry->psa_alg; +} + +/* Get MD from PSA */ +mbedtls_md_type_t mbedtls_hash_info_md_from_psa(psa_algorithm_t psa_alg) +{ + const hash_entry *entry = hash_table; + while (entry->md_type != MBEDTLS_MD_NONE && + entry->psa_alg != psa_alg) { + entry++; + } + + return entry->md_type; +} + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +int mbedtls_md_error_from_psa(psa_status_t status) +{ + switch (status) { + case PSA_SUCCESS: + return 0; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; + case PSA_ERROR_INVALID_ARGUMENT: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_MD_ALLOC_FAILED; + default: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } +} +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ diff --git a/r5dev/thirdparty/mbedtls/hash_info.h b/r5dev/thirdparty/mbedtls/hash_info.h new file mode 100644 index 00000000..f984c824 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/hash_info.h @@ -0,0 +1,101 @@ +/** + * Hash information that's independent from the crypto implementation. + * + * This can be used by: + * - code based on PSA + * - code based on the legacy API + * - code based on either of them depending on MBEDTLS_USE_PSA_CRYPTO + * - code based on either of them depending on what's available + * + * Note: this internal module will go away when everything becomes based on + * PSA Crypto; it is a helper for the transition while hash algorithms are + * still represented using mbedtls_md_type_t in most places even when PSA is + * used for the actual crypto computations. + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_HASH_INFO_H +#define MBEDTLS_HASH_INFO_H + +#include "common.h" + +#include "mbedtls/md.h" +#include "psa/crypto.h" +#include "mbedtls/platform_util.h" + +/** \def MBEDTLS_HASH_MAX_SIZE + * + * Maximum size of a hash based on configuration. + */ +#if defined(MBEDTLS_MD_C) && ( \ + !defined(MBEDTLS_PSA_CRYPTO_C) || \ + MBEDTLS_MD_MAX_SIZE >= PSA_HASH_MAX_SIZE) +#define MBEDTLS_HASH_MAX_SIZE MBEDTLS_MD_MAX_SIZE +#elif defined(MBEDTLS_PSA_CRYPTO_C) && ( \ + !defined(MBEDTLS_MD_C) || \ + PSA_HASH_MAX_SIZE >= MBEDTLS_MD_MAX_SIZE) +#define MBEDTLS_HASH_MAX_SIZE PSA_HASH_MAX_SIZE +#endif + +/** Get the output length of the given hash type from its MD type. + * + * \note To get the output length from the PSA alg, use \c PSA_HASH_LENGTH(). + * + * \param md_type The hash MD type. + * + * \return The output length in bytes, or 0 if not known. + */ +unsigned char mbedtls_hash_info_get_size(mbedtls_md_type_t md_type); + +/** Get the block size of the given hash type from its MD type. + * + * \note To get the output length from the PSA alg, use + * \c PSA_HASH_BLOCK_LENGTH(). + * + * \param md_type The hash MD type. + * + * \return The block size in bytes, or 0 if not known. + */ +unsigned char mbedtls_hash_info_get_block_size(mbedtls_md_type_t md_type); + +/** Get the PSA alg from the MD type. + * + * \param md_type The hash MD type. + * + * \return The corresponding PSA algorithm identifier, + * or PSA_ALG_NONE if not known. + */ +psa_algorithm_t mbedtls_hash_info_psa_from_md(mbedtls_md_type_t md_type); + +/** Get the MD type alg from the PSA algorithm identifier. + * + * \param psa_alg The PSA hash algorithm. + * + * \return The corresponding MD type, + * or MBEDTLS_MD_NONE if not known. + */ +mbedtls_md_type_t mbedtls_hash_info_md_from_psa(psa_algorithm_t psa_alg); + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** Convert PSA status to MD error code. + * + * \param status PSA status. + * + * \return The corresponding MD error code, + */ +int MBEDTLS_DEPRECATED mbedtls_md_error_from_psa(psa_status_t status); +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_HASH_INFO_H */ diff --git a/r5dev/thirdparty/mbedtls/hkdf.c b/r5dev/thirdparty/mbedtls/hkdf.c new file mode 100644 index 00000000..a3f071ec --- /dev/null +++ b/r5dev/thirdparty/mbedtls/hkdf.c @@ -0,0 +1,173 @@ +/* + * HKDF implementation -- RFC 5869 + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "common.h" + +#if defined(MBEDTLS_HKDF_C) + +#include +#include "mbedtls/hkdf.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +int mbedtls_hkdf(const mbedtls_md_info_t *md, const unsigned char *salt, + size_t salt_len, const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char prk[MBEDTLS_MD_MAX_SIZE]; + + ret = mbedtls_hkdf_extract(md, salt, salt_len, ikm, ikm_len, prk); + + if (ret == 0) { + ret = mbedtls_hkdf_expand(md, prk, mbedtls_md_get_size(md), + info, info_len, okm, okm_len); + } + + mbedtls_platform_zeroize(prk, sizeof(prk)); + + return ret; +} + +int mbedtls_hkdf_extract(const mbedtls_md_info_t *md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk) +{ + unsigned char null_salt[MBEDTLS_MD_MAX_SIZE] = { '\0' }; + + if (salt == NULL) { + size_t hash_len; + + if (salt_len != 0) { + return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA; + } + + hash_len = mbedtls_md_get_size(md); + + if (hash_len == 0) { + return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA; + } + + salt = null_salt; + salt_len = hash_len; + } + + return mbedtls_md_hmac(md, salt, salt_len, ikm, ikm_len, prk); +} + +int mbedtls_hkdf_expand(const mbedtls_md_info_t *md, const unsigned char *prk, + size_t prk_len, const unsigned char *info, + size_t info_len, unsigned char *okm, size_t okm_len) +{ + size_t hash_len; + size_t where = 0; + size_t n; + size_t t_len = 0; + size_t i; + int ret = 0; + mbedtls_md_context_t ctx; + unsigned char t[MBEDTLS_MD_MAX_SIZE]; + + if (okm == NULL) { + return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA; + } + + hash_len = mbedtls_md_get_size(md); + + if (prk_len < hash_len || hash_len == 0) { + return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA; + } + + if (info == NULL) { + info = (const unsigned char *) ""; + info_len = 0; + } + + n = okm_len / hash_len; + + if (okm_len % hash_len != 0) { + n++; + } + + /* + * Per RFC 5869 Section 2.3, okm_len must not exceed + * 255 times the hash length + */ + if (n > 255) { + return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA; + } + + mbedtls_md_init(&ctx); + + if ((ret = mbedtls_md_setup(&ctx, md, 1)) != 0) { + goto exit; + } + + memset(t, 0, hash_len); + + /* + * Compute T = T(1) | T(2) | T(3) | ... | T(N) + * Where T(N) is defined in RFC 5869 Section 2.3 + */ + for (i = 1; i <= n; i++) { + size_t num_to_copy; + unsigned char c = i & 0xff; + + ret = mbedtls_md_hmac_starts(&ctx, prk, prk_len); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_md_hmac_update(&ctx, t, t_len); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_md_hmac_update(&ctx, info, info_len); + if (ret != 0) { + goto exit; + } + + /* The constant concatenated to the end of each T(n) is a single octet. + * */ + ret = mbedtls_md_hmac_update(&ctx, &c, 1); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_md_hmac_finish(&ctx, t); + if (ret != 0) { + goto exit; + } + + num_to_copy = i != n ? hash_len : okm_len - where; + memcpy(okm + where, t, num_to_copy); + where += hash_len; + t_len = hash_len; + } + +exit: + mbedtls_md_free(&ctx); + mbedtls_platform_zeroize(t, sizeof(t)); + + return ret; +} + +#endif /* MBEDTLS_HKDF_C */ diff --git a/r5dev/thirdparty/mbedtls/hmac_drbg.c b/r5dev/thirdparty/mbedtls/hmac_drbg.c new file mode 100644 index 00000000..b1573023 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/hmac_drbg.c @@ -0,0 +1,645 @@ +/* + * HMAC_DRBG implementation (NIST SP 800-90) + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The NIST SP 800-90A DRBGs are described in the following publication. + * http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf + * References below are based on rev. 1 (January 2012). + */ + +#include "common.h" + +#if defined(MBEDTLS_HMAC_DRBG_C) + +#include "mbedtls/hmac_drbg.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_FS_IO) +#include +#endif + +#include "mbedtls/platform.h" + +/* + * HMAC_DRBG context initialization + */ +void mbedtls_hmac_drbg_init(mbedtls_hmac_drbg_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_hmac_drbg_context)); + + ctx->reseed_interval = MBEDTLS_HMAC_DRBG_RESEED_INTERVAL; +} + +/* + * HMAC_DRBG update, using optional additional data (10.1.2.2) + */ +int mbedtls_hmac_drbg_update(mbedtls_hmac_drbg_context *ctx, + const unsigned char *additional, + size_t add_len) +{ + size_t md_len = mbedtls_md_get_size(ctx->md_ctx.md_info); + unsigned char rounds = (additional != NULL && add_len != 0) ? 2 : 1; + unsigned char sep[1]; + unsigned char K[MBEDTLS_MD_MAX_SIZE]; + int ret = MBEDTLS_ERR_MD_BAD_INPUT_DATA; + + for (sep[0] = 0; sep[0] < rounds; sep[0]++) { + /* Step 1 or 4 */ + if ((ret = mbedtls_md_hmac_reset(&ctx->md_ctx)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_hmac_update(&ctx->md_ctx, + ctx->V, md_len)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_hmac_update(&ctx->md_ctx, + sep, 1)) != 0) { + goto exit; + } + if (rounds == 2) { + if ((ret = mbedtls_md_hmac_update(&ctx->md_ctx, + additional, add_len)) != 0) { + goto exit; + } + } + if ((ret = mbedtls_md_hmac_finish(&ctx->md_ctx, K)) != 0) { + goto exit; + } + + /* Step 2 or 5 */ + if ((ret = mbedtls_md_hmac_starts(&ctx->md_ctx, K, md_len)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_hmac_update(&ctx->md_ctx, + ctx->V, md_len)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_hmac_finish(&ctx->md_ctx, ctx->V)) != 0) { + goto exit; + } + } + +exit: + mbedtls_platform_zeroize(K, sizeof(K)); + return ret; +} + +/* + * Simplified HMAC_DRBG initialisation (for use with deterministic ECDSA) + */ +int mbedtls_hmac_drbg_seed_buf(mbedtls_hmac_drbg_context *ctx, + const mbedtls_md_info_t *md_info, + const unsigned char *data, size_t data_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_md_setup(&ctx->md_ctx, md_info, 1)) != 0) { + return ret; + } + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&ctx->mutex); +#endif + + /* + * Set initial working state. + * Use the V memory location, which is currently all 0, to initialize the + * MD context with an all-zero key. Then set V to its initial value. + */ + if ((ret = mbedtls_md_hmac_starts(&ctx->md_ctx, ctx->V, + mbedtls_md_get_size(md_info))) != 0) { + return ret; + } + memset(ctx->V, 0x01, mbedtls_md_get_size(md_info)); + + if ((ret = mbedtls_hmac_drbg_update(ctx, data, data_len)) != 0) { + return ret; + } + + return 0; +} + +/* + * Internal function used both for seeding and reseeding the DRBG. + * Comments starting with arabic numbers refer to section 10.1.2.4 + * of SP800-90A, while roman numbers refer to section 9.2. + */ +static int hmac_drbg_reseed_core(mbedtls_hmac_drbg_context *ctx, + const unsigned char *additional, size_t len, + int use_nonce) +{ + unsigned char seed[MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT]; + size_t seedlen = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + { + size_t total_entropy_len; + + if (use_nonce == 0) { + total_entropy_len = ctx->entropy_len; + } else { + total_entropy_len = ctx->entropy_len * 3 / 2; + } + + /* III. Check input length */ + if (len > MBEDTLS_HMAC_DRBG_MAX_INPUT || + total_entropy_len + len > MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT) { + return MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG; + } + } + + memset(seed, 0, MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT); + + /* IV. Gather entropy_len bytes of entropy for the seed */ + if ((ret = ctx->f_entropy(ctx->p_entropy, + seed, ctx->entropy_len)) != 0) { + return MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED; + } + seedlen += ctx->entropy_len; + + /* For initial seeding, allow adding of nonce generated + * from the entropy source. See Sect 8.6.7 in SP800-90A. */ + if (use_nonce) { + /* Note: We don't merge the two calls to f_entropy() in order + * to avoid requesting too much entropy from f_entropy() + * at once. Specifically, if the underlying digest is not + * SHA-1, 3 / 2 * entropy_len is at least 36 Bytes, which + * is larger than the maximum of 32 Bytes that our own + * entropy source implementation can emit in a single + * call in configurations disabling SHA-512. */ + if ((ret = ctx->f_entropy(ctx->p_entropy, + seed + seedlen, + ctx->entropy_len / 2)) != 0) { + return MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED; + } + + seedlen += ctx->entropy_len / 2; + } + + + /* 1. Concatenate entropy and additional data if any */ + if (additional != NULL && len != 0) { + memcpy(seed + seedlen, additional, len); + seedlen += len; + } + + /* 2. Update state */ + if ((ret = mbedtls_hmac_drbg_update(ctx, seed, seedlen)) != 0) { + goto exit; + } + + /* 3. Reset reseed_counter */ + ctx->reseed_counter = 1; + +exit: + /* 4. Done */ + mbedtls_platform_zeroize(seed, seedlen); + return ret; +} + +/* + * HMAC_DRBG reseeding: 10.1.2.4 + 9.2 + */ +int mbedtls_hmac_drbg_reseed(mbedtls_hmac_drbg_context *ctx, + const unsigned char *additional, size_t len) +{ + return hmac_drbg_reseed_core(ctx, additional, len, 0); +} + +/* + * HMAC_DRBG initialisation (10.1.2.3 + 9.1) + * + * The nonce is not passed as a separate parameter but extracted + * from the entropy source as suggested in 8.6.7. + */ +int mbedtls_hmac_drbg_seed(mbedtls_hmac_drbg_context *ctx, + const mbedtls_md_info_t *md_info, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t md_size; + + if ((ret = mbedtls_md_setup(&ctx->md_ctx, md_info, 1)) != 0) { + return ret; + } + + /* The mutex is initialized iff the md context is set up. */ +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&ctx->mutex); +#endif + + md_size = mbedtls_md_get_size(md_info); + + /* + * Set initial working state. + * Use the V memory location, which is currently all 0, to initialize the + * MD context with an all-zero key. Then set V to its initial value. + */ + if ((ret = mbedtls_md_hmac_starts(&ctx->md_ctx, ctx->V, md_size)) != 0) { + return ret; + } + memset(ctx->V, 0x01, md_size); + + ctx->f_entropy = f_entropy; + ctx->p_entropy = p_entropy; + + if (ctx->entropy_len == 0) { + /* + * See SP800-57 5.6.1 (p. 65-66) for the security strength provided by + * each hash function, then according to SP800-90A rev1 10.1 table 2, + * min_entropy_len (in bits) is security_strength. + * + * (This also matches the sizes used in the NIST test vectors.) + */ + ctx->entropy_len = md_size <= 20 ? 16 : /* 160-bits hash -> 128 bits */ + md_size <= 28 ? 24 : /* 224-bits hash -> 192 bits */ + 32; /* better (256+) -> 256 bits */ + } + + if ((ret = hmac_drbg_reseed_core(ctx, custom, len, + 1 /* add nonce */)) != 0) { + return ret; + } + + return 0; +} + +/* + * Set prediction resistance + */ +void mbedtls_hmac_drbg_set_prediction_resistance(mbedtls_hmac_drbg_context *ctx, + int resistance) +{ + ctx->prediction_resistance = resistance; +} + +/* + * Set entropy length grabbed for seeding + */ +void mbedtls_hmac_drbg_set_entropy_len(mbedtls_hmac_drbg_context *ctx, size_t len) +{ + ctx->entropy_len = len; +} + +/* + * Set reseed interval + */ +void mbedtls_hmac_drbg_set_reseed_interval(mbedtls_hmac_drbg_context *ctx, int interval) +{ + ctx->reseed_interval = interval; +} + +/* + * HMAC_DRBG random function with optional additional data: + * 10.1.2.5 (arabic) + 9.3 (Roman) + */ +int mbedtls_hmac_drbg_random_with_add(void *p_rng, + unsigned char *output, size_t out_len, + const unsigned char *additional, size_t add_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_hmac_drbg_context *ctx = (mbedtls_hmac_drbg_context *) p_rng; + size_t md_len = mbedtls_md_get_size(ctx->md_ctx.md_info); + size_t left = out_len; + unsigned char *out = output; + + /* II. Check request length */ + if (out_len > MBEDTLS_HMAC_DRBG_MAX_REQUEST) { + return MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG; + } + + /* III. Check input length */ + if (add_len > MBEDTLS_HMAC_DRBG_MAX_INPUT) { + return MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG; + } + + /* 1. (aka VII and IX) Check reseed counter and PR */ + if (ctx->f_entropy != NULL && /* For no-reseeding instances */ + (ctx->prediction_resistance == MBEDTLS_HMAC_DRBG_PR_ON || + ctx->reseed_counter > ctx->reseed_interval)) { + if ((ret = mbedtls_hmac_drbg_reseed(ctx, additional, add_len)) != 0) { + return ret; + } + + add_len = 0; /* VII.4 */ + } + + /* 2. Use additional data if any */ + if (additional != NULL && add_len != 0) { + if ((ret = mbedtls_hmac_drbg_update(ctx, + additional, add_len)) != 0) { + goto exit; + } + } + + /* 3, 4, 5. Generate bytes */ + while (left != 0) { + size_t use_len = left > md_len ? md_len : left; + + if ((ret = mbedtls_md_hmac_reset(&ctx->md_ctx)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_hmac_update(&ctx->md_ctx, + ctx->V, md_len)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_hmac_finish(&ctx->md_ctx, ctx->V)) != 0) { + goto exit; + } + + memcpy(out, ctx->V, use_len); + out += use_len; + left -= use_len; + } + + /* 6. Update */ + if ((ret = mbedtls_hmac_drbg_update(ctx, + additional, add_len)) != 0) { + goto exit; + } + + /* 7. Update reseed counter */ + ctx->reseed_counter++; + +exit: + /* 8. Done */ + return ret; +} + +/* + * HMAC_DRBG random function + */ +int mbedtls_hmac_drbg_random(void *p_rng, unsigned char *output, size_t out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_hmac_drbg_context *ctx = (mbedtls_hmac_drbg_context *) p_rng; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + ret = mbedtls_hmac_drbg_random_with_add(ctx, output, out_len, NULL, 0); + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +/* + * This function resets HMAC_DRBG context to the state immediately + * after initial call of mbedtls_hmac_drbg_init(). + */ +void mbedtls_hmac_drbg_free(mbedtls_hmac_drbg_context *ctx) +{ + if (ctx == NULL) { + return; + } + +#if defined(MBEDTLS_THREADING_C) + /* The mutex is initialized iff the md context is set up. */ + if (ctx->md_ctx.md_info != NULL) { + mbedtls_mutex_free(&ctx->mutex); + } +#endif + mbedtls_md_free(&ctx->md_ctx); + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_hmac_drbg_context)); + ctx->reseed_interval = MBEDTLS_HMAC_DRBG_RESEED_INTERVAL; +} + +#if defined(MBEDTLS_FS_IO) +int mbedtls_hmac_drbg_write_seed_file(mbedtls_hmac_drbg_context *ctx, const char *path) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + FILE *f; + unsigned char buf[MBEDTLS_HMAC_DRBG_MAX_INPUT]; + + if ((f = fopen(path, "wb")) == NULL) { + return MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + if ((ret = mbedtls_hmac_drbg_random(ctx, buf, sizeof(buf))) != 0) { + goto exit; + } + + if (fwrite(buf, 1, sizeof(buf), f) != sizeof(buf)) { + ret = MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR; + goto exit; + } + + ret = 0; + +exit: + fclose(f); + mbedtls_platform_zeroize(buf, sizeof(buf)); + + return ret; +} + +int mbedtls_hmac_drbg_update_seed_file(mbedtls_hmac_drbg_context *ctx, const char *path) +{ + int ret = 0; + FILE *f = NULL; + size_t n; + unsigned char buf[MBEDTLS_HMAC_DRBG_MAX_INPUT]; + unsigned char c; + + if ((f = fopen(path, "rb")) == NULL) { + return MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + n = fread(buf, 1, sizeof(buf), f); + if (fread(&c, 1, 1, f) != 0) { + ret = MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG; + goto exit; + } + if (n == 0 || ferror(f)) { + ret = MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR; + goto exit; + } + fclose(f); + f = NULL; + + ret = mbedtls_hmac_drbg_update(ctx, buf, n); + +exit: + mbedtls_platform_zeroize(buf, sizeof(buf)); + if (f != NULL) { + fclose(f); + } + if (ret != 0) { + return ret; + } + return mbedtls_hmac_drbg_write_seed_file(ctx, path); +} +#endif /* MBEDTLS_FS_IO */ + + +#if defined(MBEDTLS_SELF_TEST) + +#if !defined(MBEDTLS_SHA1_C) +/* Dummy checkup routine */ +int mbedtls_hmac_drbg_self_test(int verbose) +{ + (void) verbose; + return 0; +} +#else + +#define OUTPUT_LEN 80 + +/* From a NIST PR=true test vector */ +static const unsigned char entropy_pr[] = { + 0xa0, 0xc9, 0xab, 0x58, 0xf1, 0xe2, 0xe5, 0xa4, 0xde, 0x3e, 0xbd, 0x4f, + 0xf7, 0x3e, 0x9c, 0x5b, 0x64, 0xef, 0xd8, 0xca, 0x02, 0x8c, 0xf8, 0x11, + 0x48, 0xa5, 0x84, 0xfe, 0x69, 0xab, 0x5a, 0xee, 0x42, 0xaa, 0x4d, 0x42, + 0x17, 0x60, 0x99, 0xd4, 0x5e, 0x13, 0x97, 0xdc, 0x40, 0x4d, 0x86, 0xa3, + 0x7b, 0xf5, 0x59, 0x54, 0x75, 0x69, 0x51, 0xe4 +}; +static const unsigned char result_pr[OUTPUT_LEN] = { + 0x9a, 0x00, 0xa2, 0xd0, 0x0e, 0xd5, 0x9b, 0xfe, 0x31, 0xec, 0xb1, 0x39, + 0x9b, 0x60, 0x81, 0x48, 0xd1, 0x96, 0x9d, 0x25, 0x0d, 0x3c, 0x1e, 0x94, + 0x10, 0x10, 0x98, 0x12, 0x93, 0x25, 0xca, 0xb8, 0xfc, 0xcc, 0x2d, 0x54, + 0x73, 0x19, 0x70, 0xc0, 0x10, 0x7a, 0xa4, 0x89, 0x25, 0x19, 0x95, 0x5e, + 0x4b, 0xc6, 0x00, 0x1d, 0x7f, 0x4e, 0x6a, 0x2b, 0xf8, 0xa3, 0x01, 0xab, + 0x46, 0x05, 0x5c, 0x09, 0xa6, 0x71, 0x88, 0xf1, 0xa7, 0x40, 0xee, 0xf3, + 0xe1, 0x5c, 0x02, 0x9b, 0x44, 0xaf, 0x03, 0x44 +}; + +/* From a NIST PR=false test vector */ +static const unsigned char entropy_nopr[] = { + 0x79, 0x34, 0x9b, 0xbf, 0x7c, 0xdd, 0xa5, 0x79, 0x95, 0x57, 0x86, 0x66, + 0x21, 0xc9, 0x13, 0x83, 0x11, 0x46, 0x73, 0x3a, 0xbf, 0x8c, 0x35, 0xc8, + 0xc7, 0x21, 0x5b, 0x5b, 0x96, 0xc4, 0x8e, 0x9b, 0x33, 0x8c, 0x74, 0xe3, + 0xe9, 0x9d, 0xfe, 0xdf +}; +static const unsigned char result_nopr[OUTPUT_LEN] = { + 0xc6, 0xa1, 0x6a, 0xb8, 0xd4, 0x20, 0x70, 0x6f, 0x0f, 0x34, 0xab, 0x7f, + 0xec, 0x5a, 0xdc, 0xa9, 0xd8, 0xca, 0x3a, 0x13, 0x3e, 0x15, 0x9c, 0xa6, + 0xac, 0x43, 0xc6, 0xf8, 0xa2, 0xbe, 0x22, 0x83, 0x4a, 0x4c, 0x0a, 0x0a, + 0xff, 0xb1, 0x0d, 0x71, 0x94, 0xf1, 0xc1, 0xa5, 0xcf, 0x73, 0x22, 0xec, + 0x1a, 0xe0, 0x96, 0x4e, 0xd4, 0xbf, 0x12, 0x27, 0x46, 0xe0, 0x87, 0xfd, + 0xb5, 0xb3, 0xe9, 0x1b, 0x34, 0x93, 0xd5, 0xbb, 0x98, 0xfa, 0xed, 0x49, + 0xe8, 0x5f, 0x13, 0x0f, 0xc8, 0xa4, 0x59, 0xb7 +}; + +/* "Entropy" from buffer */ +static size_t test_offset; +static int hmac_drbg_self_test_entropy(void *data, + unsigned char *buf, size_t len) +{ + const unsigned char *p = data; + memcpy(buf, p + test_offset, len); + test_offset += len; + return 0; +} + +#define CHK(c) if ((c) != 0) \ + { \ + if (verbose != 0) \ + mbedtls_printf("failed\n"); \ + return 1; \ + } + +/* + * Checkup routine for HMAC_DRBG with SHA-1 + */ +int mbedtls_hmac_drbg_self_test(int verbose) +{ + mbedtls_hmac_drbg_context ctx; + unsigned char buf[OUTPUT_LEN]; + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + + mbedtls_hmac_drbg_init(&ctx); + + /* + * PR = True + */ + if (verbose != 0) { + mbedtls_printf(" HMAC_DRBG (PR = True) : "); + } + + test_offset = 0; + CHK(mbedtls_hmac_drbg_seed(&ctx, md_info, + hmac_drbg_self_test_entropy, (void *) entropy_pr, + NULL, 0)); + mbedtls_hmac_drbg_set_prediction_resistance(&ctx, MBEDTLS_HMAC_DRBG_PR_ON); + CHK(mbedtls_hmac_drbg_random(&ctx, buf, OUTPUT_LEN)); + CHK(mbedtls_hmac_drbg_random(&ctx, buf, OUTPUT_LEN)); + CHK(memcmp(buf, result_pr, OUTPUT_LEN)); + mbedtls_hmac_drbg_free(&ctx); + + mbedtls_hmac_drbg_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + /* + * PR = False + */ + if (verbose != 0) { + mbedtls_printf(" HMAC_DRBG (PR = False) : "); + } + + mbedtls_hmac_drbg_init(&ctx); + + test_offset = 0; + CHK(mbedtls_hmac_drbg_seed(&ctx, md_info, + hmac_drbg_self_test_entropy, (void *) entropy_nopr, + NULL, 0)); + CHK(mbedtls_hmac_drbg_reseed(&ctx, NULL, 0)); + CHK(mbedtls_hmac_drbg_random(&ctx, buf, OUTPUT_LEN)); + CHK(mbedtls_hmac_drbg_random(&ctx, buf, OUTPUT_LEN)); + CHK(memcmp(buf, result_nopr, OUTPUT_LEN)); + mbedtls_hmac_drbg_free(&ctx); + + mbedtls_hmac_drbg_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; +} +#endif /* MBEDTLS_SHA1_C */ +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_HMAC_DRBG_C */ diff --git a/r5dev/thirdparty/mbedtls/include/.gitignore b/r5dev/thirdparty/mbedtls/include/.gitignore new file mode 100644 index 00000000..bf67d02e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/.gitignore @@ -0,0 +1,4 @@ +Makefile +*.sln +*.vcxproj +mbedtls/check_config diff --git a/r5dev/thirdparty/mbedtls/include/CMakeLists.txt b/r5dev/thirdparty/mbedtls/include/CMakeLists.txt new file mode 100644 index 00000000..e693bc17 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/CMakeLists.txt @@ -0,0 +1,22 @@ +option(INSTALL_MBEDTLS_HEADERS "Install mbed TLS headers." ON) + +if(INSTALL_MBEDTLS_HEADERS) + + file(GLOB headers "mbedtls/*.h") + file(GLOB psa_headers "psa/*.h") + + install(FILES ${headers} + DESTINATION include/mbedtls + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + + install(FILES ${psa_headers} + DESTINATION include/psa + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) + +endif(INSTALL_MBEDTLS_HEADERS) + +# Make mbedtls_config.h available in an out-of-source build. ssl-opt.sh requires it. +if (ENABLE_TESTING AND NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + link_to_source(mbedtls) + link_to_source(psa) +endif() diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/aes.h b/r5dev/thirdparty/mbedtls/include/mbedtls/aes.h new file mode 100644 index 00000000..42e1fe85 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/aes.h @@ -0,0 +1,634 @@ +/** + * \file aes.h + * + * \brief This file contains AES definitions and functions. + * + * The Advanced Encryption Standard (AES) specifies a FIPS-approved + * cryptographic algorithm that can be used to protect electronic + * data. + * + * The AES algorithm is a symmetric block cipher that can + * encrypt and decrypt information. For more information, see + * FIPS Publication 197: Advanced Encryption Standard and + * ISO/IEC 18033-2:2006: Information technology -- Security + * techniques -- Encryption algorithms -- Part 2: Asymmetric + * ciphers. + * + * The AES-XTS block mode is standardized by NIST SP 800-38E + * + * and described in detail by IEEE P1619 + * . + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_AES_H +#define MBEDTLS_AES_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" +#include "mbedtls/platform_util.h" + +#include +#include + +/* padlock.c and aesni.c rely on these values! */ +#define MBEDTLS_AES_ENCRYPT 1 /**< AES encryption. */ +#define MBEDTLS_AES_DECRYPT 0 /**< AES decryption. */ + +/* Error codes in range 0x0020-0x0022 */ +/** Invalid key length. */ +#define MBEDTLS_ERR_AES_INVALID_KEY_LENGTH -0x0020 +/** Invalid data input length. */ +#define MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH -0x0022 + +/* Error codes in range 0x0021-0x0025 */ +/** Invalid input data. */ +#define MBEDTLS_ERR_AES_BAD_INPUT_DATA -0x0021 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_AES_ALT) +// Regular implementation +// + +/** + * \brief The AES context-type definition. + */ +typedef struct mbedtls_aes_context { + int MBEDTLS_PRIVATE(nr); /*!< The number of rounds. */ + size_t MBEDTLS_PRIVATE(rk_offset); /*!< The offset in array elements to AES + round keys in the buffer. */ + uint32_t MBEDTLS_PRIVATE(buf)[68]; /*!< Unaligned data buffer. This buffer can + hold 32 extra Bytes, which can be used for + one of the following purposes: +

  • Alignment if VIA padlock is + used.
  • +
  • Simplifying key expansion in the 256-bit + case by generating an extra round key. +
*/ +} +mbedtls_aes_context; + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief The AES XTS context-type definition. + */ +typedef struct mbedtls_aes_xts_context { + mbedtls_aes_context MBEDTLS_PRIVATE(crypt); /*!< The AES context to use for AES block + encryption or decryption. */ + mbedtls_aes_context MBEDTLS_PRIVATE(tweak); /*!< The AES context used for tweak + computation. */ +} mbedtls_aes_xts_context; +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +#else /* MBEDTLS_AES_ALT */ +#include "aes_alt.h" +#endif /* MBEDTLS_AES_ALT */ + +/** + * \brief This function initializes the specified AES context. + * + * It must be the first API called before using + * the context. + * + * \param ctx The AES context to initialize. This must not be \c NULL. + */ +void mbedtls_aes_init(mbedtls_aes_context *ctx); + +/** + * \brief This function releases and clears the specified AES context. + * + * \param ctx The AES context to clear. + * If this is \c NULL, this function does nothing. + * Otherwise, the context must have been at least initialized. + */ +void mbedtls_aes_free(mbedtls_aes_context *ctx); + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief This function initializes the specified AES XTS context. + * + * It must be the first API called before using + * the context. + * + * \param ctx The AES XTS context to initialize. This must not be \c NULL. + */ +void mbedtls_aes_xts_init(mbedtls_aes_xts_context *ctx); + +/** + * \brief This function releases and clears the specified AES XTS context. + * + * \param ctx The AES XTS context to clear. + * If this is \c NULL, this function does nothing. + * Otherwise, the context must have been at least initialized. + */ +void mbedtls_aes_xts_free(mbedtls_aes_xts_context *ctx); +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +/** + * \brief This function sets the encryption key. + * + * \param ctx The AES context to which the key should be bound. + * It must be initialized. + * \param key The encryption key. + * This must be a readable buffer of size \p keybits bits. + * \param keybits The size of data passed in bits. Valid options are: + *
  • 128 bits
  • + *
  • 192 bits
  • + *
  • 256 bits
+ * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_setkey_enc(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits); + +/** + * \brief This function sets the decryption key. + * + * \param ctx The AES context to which the key should be bound. + * It must be initialized. + * \param key The decryption key. + * This must be a readable buffer of size \p keybits bits. + * \param keybits The size of data passed. Valid options are: + *
  • 128 bits
  • + *
  • 192 bits
  • + *
  • 256 bits
+ * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_setkey_dec(mbedtls_aes_context *ctx, const unsigned char *key, + unsigned int keybits); + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief This function prepares an XTS context for encryption and + * sets the encryption key. + * + * \param ctx The AES XTS context to which the key should be bound. + * It must be initialized. + * \param key The encryption key. This is comprised of the XTS key1 + * concatenated with the XTS key2. + * This must be a readable buffer of size \p keybits bits. + * \param keybits The size of \p key passed in bits. Valid options are: + *
  • 256 bits (each of key1 and key2 is a 128-bit key)
  • + *
  • 512 bits (each of key1 and key2 is a 256-bit key)
+ * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_xts_setkey_enc(mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits); + +/** + * \brief This function prepares an XTS context for decryption and + * sets the decryption key. + * + * \param ctx The AES XTS context to which the key should be bound. + * It must be initialized. + * \param key The decryption key. This is comprised of the XTS key1 + * concatenated with the XTS key2. + * This must be a readable buffer of size \p keybits bits. + * \param keybits The size of \p key passed in bits. Valid options are: + *
  • 256 bits (each of key1 and key2 is a 128-bit key)
  • + *
  • 512 bits (each of key1 and key2 is a 256-bit key)
+ * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_xts_setkey_dec(mbedtls_aes_xts_context *ctx, + const unsigned char *key, + unsigned int keybits); +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +/** + * \brief This function performs an AES single-block encryption or + * decryption operation. + * + * It performs the operation defined in the \p mode parameter + * (encrypt or decrypt), on the input data buffer defined in + * the \p input parameter. + * + * mbedtls_aes_init(), and either mbedtls_aes_setkey_enc() or + * mbedtls_aes_setkey_dec() must be called before the first + * call to this API with the same context. + * + * \param ctx The AES context to use for encryption or decryption. + * It must be initialized and bound to a key. + * \param mode The AES operation: #MBEDTLS_AES_ENCRYPT or + * #MBEDTLS_AES_DECRYPT. + * \param input The buffer holding the input data. + * It must be readable and at least \c 16 Bytes long. + * \param output The buffer where the output data will be written. + * It must be writeable and at least \c 16 Bytes long. + + * \return \c 0 on success. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_crypt_ecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]); + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/** + * \brief This function performs an AES-CBC encryption or decryption operation + * on full blocks. + * + * It performs the operation defined in the \p mode + * parameter (encrypt/decrypt), on the input data buffer defined in + * the \p input parameter. + * + * It can be called as many times as needed, until all the input + * data is processed. mbedtls_aes_init(), and either + * mbedtls_aes_setkey_enc() or mbedtls_aes_setkey_dec() must be called + * before the first call to this API with the same context. + * + * \note This function operates on full blocks, that is, the input size + * must be a multiple of the AES block size of \c 16 Bytes. + * + * \note Upon exit, the content of the IV is updated so that you can + * call the same function again on the next + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If you need to retain the contents of the IV, you should + * either save it manually or use the cipher module instead. + * + * + * \param ctx The AES context to use for encryption or decryption. + * It must be initialized and bound to a key. + * \param mode The AES operation: #MBEDTLS_AES_ENCRYPT or + * #MBEDTLS_AES_DECRYPT. + * \param length The length of the input data in Bytes. This must be a + * multiple of the block size (\c 16 Bytes). + * \param iv Initialization vector (updated after use). + * It must be a readable and writeable buffer of \c 16 Bytes. + * \param input The buffer holding the input data. + * It must be readable and of size \p length Bytes. + * \param output The buffer holding the output data. + * It must be writeable and of size \p length Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH + * on failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_crypt_cbc(mbedtls_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_XTS) +/** + * \brief This function performs an AES-XTS encryption or decryption + * operation for an entire XTS data unit. + * + * AES-XTS encrypts or decrypts blocks based on their location as + * defined by a data unit number. The data unit number must be + * provided by \p data_unit. + * + * NIST SP 800-38E limits the maximum size of a data unit to 2^20 + * AES blocks. If the data unit is larger than this, this function + * returns #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH. + * + * \param ctx The AES XTS context to use for AES XTS operations. + * It must be initialized and bound to a key. + * \param mode The AES operation: #MBEDTLS_AES_ENCRYPT or + * #MBEDTLS_AES_DECRYPT. + * \param length The length of a data unit in Bytes. This can be any + * length between 16 bytes and 2^24 bytes inclusive + * (between 1 and 2^20 block cipher blocks). + * \param data_unit The address of the data unit encoded as an array of 16 + * bytes in little-endian format. For disk encryption, this + * is typically the index of the block device sector that + * contains the data. + * \param input The buffer holding the input data (which is an entire + * data unit). This function reads \p length Bytes from \p + * input. + * \param output The buffer holding the output data (which is an entire + * data unit). This function writes \p length Bytes to \p + * output. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH if \p length is + * smaller than an AES block in size (16 Bytes) or if \p + * length is larger than 2^20 blocks (16 MiB). + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_crypt_xts(mbedtls_aes_xts_context *ctx, + int mode, + size_t length, + const unsigned char data_unit[16], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +/** + * \brief This function performs an AES-CFB128 encryption or decryption + * operation. + * + * It performs the operation defined in the \p mode + * parameter (encrypt or decrypt), on the input data buffer + * defined in the \p input parameter. + * + * For CFB, you must set up the context with mbedtls_aes_setkey_enc(), + * regardless of whether you are performing an encryption or decryption + * operation, that is, regardless of the \p mode parameter. This is + * because CFB mode uses the same key schedule for encryption and + * decryption. + * + * \note Upon exit, the content of the IV is updated so that you can + * call the same function again on the next + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If you need to retain the contents of the + * IV, you must either save it manually or use the cipher + * module instead. + * + * + * \param ctx The AES context to use for encryption or decryption. + * It must be initialized and bound to a key. + * \param mode The AES operation: #MBEDTLS_AES_ENCRYPT or + * #MBEDTLS_AES_DECRYPT. + * \param length The length of the input data in Bytes. + * \param iv_off The offset in IV (updated after use). + * It must point to a valid \c size_t. + * \param iv The initialization vector (updated after use). + * It must be a readable and writeable buffer of \c 16 Bytes. + * \param input The buffer holding the input data. + * It must be readable and of size \p length Bytes. + * \param output The buffer holding the output data. + * It must be writeable and of size \p length Bytes. + * + * \return \c 0 on success. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_crypt_cfb128(mbedtls_aes_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function performs an AES-CFB8 encryption or decryption + * operation. + * + * It performs the operation defined in the \p mode + * parameter (encrypt/decrypt), on the input data buffer defined + * in the \p input parameter. + * + * Due to the nature of CFB, you must use the same key schedule for + * both encryption and decryption operations. Therefore, you must + * use the context initialized with mbedtls_aes_setkey_enc() for + * both #MBEDTLS_AES_ENCRYPT and #MBEDTLS_AES_DECRYPT. + * + * \note Upon exit, the content of the IV is updated so that you can + * call the same function again on the next + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If you need to retain the contents of the + * IV, you should either save it manually or use the cipher + * module instead. + * + * + * \param ctx The AES context to use for encryption or decryption. + * It must be initialized and bound to a key. + * \param mode The AES operation: #MBEDTLS_AES_ENCRYPT or + * #MBEDTLS_AES_DECRYPT + * \param length The length of the input data. + * \param iv The initialization vector (updated after use). + * It must be a readable and writeable buffer of \c 16 Bytes. + * \param input The buffer holding the input data. + * It must be readable and of size \p length Bytes. + * \param output The buffer holding the output data. + * It must be writeable and of size \p length Bytes. + * + * \return \c 0 on success. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_crypt_cfb8(mbedtls_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output); +#endif /*MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_OFB) +/** + * \brief This function performs an AES-OFB (Output Feedback Mode) + * encryption or decryption operation. + * + * For OFB, you must set up the context with + * mbedtls_aes_setkey_enc(), regardless of whether you are + * performing an encryption or decryption operation. This is + * because OFB mode uses the same key schedule for encryption and + * decryption. + * + * The OFB operation is identical for encryption or decryption, + * therefore no operation mode needs to be specified. + * + * \note Upon exit, the content of iv, the Initialisation Vector, is + * updated so that you can call the same function again on the next + * block(s) of data and get the same result as if it was encrypted + * in one call. This allows a "streaming" usage, by initialising + * iv_off to 0 before the first call, and preserving its value + * between calls. + * + * For non-streaming use, the iv should be initialised on each call + * to a unique value, and iv_off set to 0 on each call. + * + * If you need to retain the contents of the initialisation vector, + * you must either save it manually or use the cipher module + * instead. + * + * \warning For the OFB mode, the initialisation vector must be unique + * every encryption operation. Reuse of an initialisation vector + * will compromise security. + * + * \param ctx The AES context to use for encryption or decryption. + * It must be initialized and bound to a key. + * \param length The length of the input data. + * \param iv_off The offset in IV (updated after use). + * It must point to a valid \c size_t. + * \param iv The initialization vector (updated after use). + * It must be a readable and writeable buffer of \c 16 Bytes. + * \param input The buffer holding the input data. + * It must be readable and of size \p length Bytes. + * \param output The buffer holding the output data. + * It must be writeable and of size \p length Bytes. + * + * \return \c 0 on success. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_crypt_ofb(mbedtls_aes_context *ctx, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output); + +#endif /* MBEDTLS_CIPHER_MODE_OFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/** + * \brief This function performs an AES-CTR encryption or decryption + * operation. + * + * Due to the nature of CTR, you must use the same key schedule + * for both encryption and decryption operations. Therefore, you + * must use the context initialized with mbedtls_aes_setkey_enc() + * for both #MBEDTLS_AES_ENCRYPT and #MBEDTLS_AES_DECRYPT. + * + * \warning You must never reuse a nonce value with the same key. Doing so + * would void the encryption for the two messages encrypted with + * the same nonce and key. + * + * There are two common strategies for managing nonces with CTR: + * + * 1. You can handle everything as a single message processed over + * successive calls to this function. In that case, you want to + * set \p nonce_counter and \p nc_off to 0 for the first call, and + * then preserve the values of \p nonce_counter, \p nc_off and \p + * stream_block across calls to this function as they will be + * updated by this function. + * + * With this strategy, you must not encrypt more than 2**128 + * blocks of data with the same key. + * + * 2. You can encrypt separate messages by dividing the \p + * nonce_counter buffer in two areas: the first one used for a + * per-message nonce, handled by yourself, and the second one + * updated by this function internally. + * + * For example, you might reserve the first 12 bytes for the + * per-message nonce, and the last 4 bytes for internal use. In that + * case, before calling this function on a new message you need to + * set the first 12 bytes of \p nonce_counter to your chosen nonce + * value, the last 4 to 0, and \p nc_off to 0 (which will cause \p + * stream_block to be ignored). That way, you can encrypt at most + * 2**96 messages of up to 2**32 blocks each with the same key. + * + * The per-message nonce (or information sufficient to reconstruct + * it) needs to be communicated with the ciphertext and must be unique. + * The recommended way to ensure uniqueness is to use a message + * counter. An alternative is to generate random nonces, but this + * limits the number of messages that can be securely encrypted: + * for example, with 96-bit random nonces, you should not encrypt + * more than 2**32 messages with the same key. + * + * Note that for both strategies, sizes are measured in blocks and + * that an AES block is 16 bytes. + * + * \warning Upon return, \p stream_block contains sensitive data. Its + * content must not be written to insecure storage and should be + * securely discarded as soon as it's no longer needed. + * + * \param ctx The AES context to use for encryption or decryption. + * It must be initialized and bound to a key. + * \param length The length of the input data. + * \param nc_off The offset in the current \p stream_block, for + * resuming within the current cipher stream. The + * offset pointer should be 0 at the start of a stream. + * It must point to a valid \c size_t. + * \param nonce_counter The 128-bit nonce and counter. + * It must be a readable-writeable buffer of \c 16 Bytes. + * \param stream_block The saved stream block for resuming. This is + * overwritten by the function. + * It must be a readable-writeable buffer of \c 16 Bytes. + * \param input The buffer holding the input data. + * It must be readable and of size \p length Bytes. + * \param output The buffer holding the output data. + * It must be writeable and of size \p length Bytes. + * + * \return \c 0 on success. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_aes_crypt_ctr(mbedtls_aes_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +/** + * \brief Internal AES block encryption function. This is only + * exposed to allow overriding it using + * \c MBEDTLS_AES_ENCRYPT_ALT. + * + * \param ctx The AES context to use for encryption. + * \param input The plaintext block. + * \param output The output (ciphertext) block. + * + * \return \c 0 on success. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_internal_aes_encrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]); + +/** + * \brief Internal AES block decryption function. This is only + * exposed to allow overriding it using see + * \c MBEDTLS_AES_DECRYPT_ALT. + * + * \param ctx The AES context to use for decryption. + * \param input The ciphertext block. + * \param output The output (plaintext) block. + * + * \return \c 0 on success. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_internal_aes_decrypt(mbedtls_aes_context *ctx, + const unsigned char input[16], + unsigned char output[16]); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief Checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_aes_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* aes.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/aria.h b/r5dev/thirdparty/mbedtls/include/mbedtls/aria.h new file mode 100644 index 00000000..587cbf8b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/aria.h @@ -0,0 +1,357 @@ +/** + * \file aria.h + * + * \brief ARIA block cipher + * + * The ARIA algorithm is a symmetric block cipher that can encrypt and + * decrypt information. It is defined by the Korean Agency for + * Technology and Standards (KATS) in KS X 1213:2004 (in + * Korean, but see http://210.104.33.10/ARIA/index-e.html in English) + * and also described by the IETF in RFC 5794. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_ARIA_H +#define MBEDTLS_ARIA_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +#include "mbedtls/platform_util.h" + +#define MBEDTLS_ARIA_ENCRYPT 1 /**< ARIA encryption. */ +#define MBEDTLS_ARIA_DECRYPT 0 /**< ARIA decryption. */ + +#define MBEDTLS_ARIA_BLOCKSIZE 16 /**< ARIA block size in bytes. */ +#define MBEDTLS_ARIA_MAX_ROUNDS 16 /**< Maximum number of rounds in ARIA. */ +#define MBEDTLS_ARIA_MAX_KEYSIZE 32 /**< Maximum size of an ARIA key in bytes. */ + +/** Bad input data. */ +#define MBEDTLS_ERR_ARIA_BAD_INPUT_DATA -0x005C + +/** Invalid data input length. */ +#define MBEDTLS_ERR_ARIA_INVALID_INPUT_LENGTH -0x005E + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_ARIA_ALT) +// Regular implementation +// + +/** + * \brief The ARIA context-type definition. + */ +typedef struct mbedtls_aria_context { + unsigned char MBEDTLS_PRIVATE(nr); /*!< The number of rounds (12, 14 or 16) */ + /*! The ARIA round keys. */ + uint32_t MBEDTLS_PRIVATE(rk)[MBEDTLS_ARIA_MAX_ROUNDS + 1][MBEDTLS_ARIA_BLOCKSIZE / 4]; +} +mbedtls_aria_context; + +#else /* MBEDTLS_ARIA_ALT */ +#include "aria_alt.h" +#endif /* MBEDTLS_ARIA_ALT */ + +/** + * \brief This function initializes the specified ARIA context. + * + * It must be the first API called before using + * the context. + * + * \param ctx The ARIA context to initialize. This must not be \c NULL. + */ +void mbedtls_aria_init(mbedtls_aria_context *ctx); + +/** + * \brief This function releases and clears the specified ARIA context. + * + * \param ctx The ARIA context to clear. This may be \c NULL, in which + * case this function returns immediately. If it is not \c NULL, + * it must point to an initialized ARIA context. + */ +void mbedtls_aria_free(mbedtls_aria_context *ctx); + +/** + * \brief This function sets the encryption key. + * + * \param ctx The ARIA context to which the key should be bound. + * This must be initialized. + * \param key The encryption key. This must be a readable buffer + * of size \p keybits Bits. + * \param keybits The size of \p key in Bits. Valid options are: + *
  • 128 bits
  • + *
  • 192 bits
  • + *
  • 256 bits
+ * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_aria_setkey_enc(mbedtls_aria_context *ctx, + const unsigned char *key, + unsigned int keybits); + +/** + * \brief This function sets the decryption key. + * + * \param ctx The ARIA context to which the key should be bound. + * This must be initialized. + * \param key The decryption key. This must be a readable buffer + * of size \p keybits Bits. + * \param keybits The size of data passed. Valid options are: + *
  • 128 bits
  • + *
  • 192 bits
  • + *
  • 256 bits
+ * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_aria_setkey_dec(mbedtls_aria_context *ctx, + const unsigned char *key, + unsigned int keybits); + +/** + * \brief This function performs an ARIA single-block encryption or + * decryption operation. + * + * It performs encryption or decryption (depending on whether + * the key was set for encryption on decryption) on the input + * data buffer defined in the \p input parameter. + * + * mbedtls_aria_init(), and either mbedtls_aria_setkey_enc() or + * mbedtls_aria_setkey_dec() must be called before the first + * call to this API with the same context. + * + * \param ctx The ARIA context to use for encryption or decryption. + * This must be initialized and bound to a key. + * \param input The 16-Byte buffer holding the input data. + * \param output The 16-Byte buffer holding the output data. + + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_aria_crypt_ecb(mbedtls_aria_context *ctx, + const unsigned char input[MBEDTLS_ARIA_BLOCKSIZE], + unsigned char output[MBEDTLS_ARIA_BLOCKSIZE]); + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/** + * \brief This function performs an ARIA-CBC encryption or decryption operation + * on full blocks. + * + * It performs the operation defined in the \p mode + * parameter (encrypt/decrypt), on the input data buffer defined in + * the \p input parameter. + * + * It can be called as many times as needed, until all the input + * data is processed. mbedtls_aria_init(), and either + * mbedtls_aria_setkey_enc() or mbedtls_aria_setkey_dec() must be called + * before the first call to this API with the same context. + * + * \note This function operates on aligned blocks, that is, the input size + * must be a multiple of the ARIA block size of 16 Bytes. + * + * \note Upon exit, the content of the IV is updated so that you can + * call the same function again on the next + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If you need to retain the contents of the IV, you should + * either save it manually or use the cipher module instead. + * + * + * \param ctx The ARIA context to use for encryption or decryption. + * This must be initialized and bound to a key. + * \param mode The mode of operation. This must be either + * #MBEDTLS_ARIA_ENCRYPT for encryption, or + * #MBEDTLS_ARIA_DECRYPT for decryption. + * \param length The length of the input data in Bytes. This must be a + * multiple of the block size (16 Bytes). + * \param iv Initialization vector (updated after use). + * This must be a readable buffer of size 16 Bytes. + * \param input The buffer holding the input data. This must + * be a readable buffer of length \p length Bytes. + * \param output The buffer holding the output data. This must + * be a writable buffer of length \p length Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_aria_crypt_cbc(mbedtls_aria_context *ctx, + int mode, + size_t length, + unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +/** + * \brief This function performs an ARIA-CFB128 encryption or decryption + * operation. + * + * It performs the operation defined in the \p mode + * parameter (encrypt or decrypt), on the input data buffer + * defined in the \p input parameter. + * + * For CFB, you must set up the context with mbedtls_aria_setkey_enc(), + * regardless of whether you are performing an encryption or decryption + * operation, that is, regardless of the \p mode parameter. This is + * because CFB mode uses the same key schedule for encryption and + * decryption. + * + * \note Upon exit, the content of the IV is updated so that you can + * call the same function again on the next + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If you need to retain the contents of the + * IV, you must either save it manually or use the cipher + * module instead. + * + * + * \param ctx The ARIA context to use for encryption or decryption. + * This must be initialized and bound to a key. + * \param mode The mode of operation. This must be either + * #MBEDTLS_ARIA_ENCRYPT for encryption, or + * #MBEDTLS_ARIA_DECRYPT for decryption. + * \param length The length of the input data \p input in Bytes. + * \param iv_off The offset in IV (updated after use). + * This must not be larger than 15. + * \param iv The initialization vector (updated after use). + * This must be a readable buffer of size 16 Bytes. + * \param input The buffer holding the input data. This must + * be a readable buffer of length \p length Bytes. + * \param output The buffer holding the output data. This must + * be a writable buffer of length \p length Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_aria_crypt_cfb128(mbedtls_aria_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[MBEDTLS_ARIA_BLOCKSIZE], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/** + * \brief This function performs an ARIA-CTR encryption or decryption + * operation. + * + * This function performs the operation defined in the \p mode + * parameter (encrypt/decrypt), on the input data buffer + * defined in the \p input parameter. + * + * Due to the nature of CTR, you must use the same key schedule + * for both encryption and decryption operations. Therefore, you + * must use the context initialized with mbedtls_aria_setkey_enc() + * for both #MBEDTLS_ARIA_ENCRYPT and #MBEDTLS_ARIA_DECRYPT. + * + * \warning You must never reuse a nonce value with the same key. Doing so + * would void the encryption for the two messages encrypted with + * the same nonce and key. + * + * There are two common strategies for managing nonces with CTR: + * + * 1. You can handle everything as a single message processed over + * successive calls to this function. In that case, you want to + * set \p nonce_counter and \p nc_off to 0 for the first call, and + * then preserve the values of \p nonce_counter, \p nc_off and \p + * stream_block across calls to this function as they will be + * updated by this function. + * + * With this strategy, you must not encrypt more than 2**128 + * blocks of data with the same key. + * + * 2. You can encrypt separate messages by dividing the \p + * nonce_counter buffer in two areas: the first one used for a + * per-message nonce, handled by yourself, and the second one + * updated by this function internally. + * + * For example, you might reserve the first 12 bytes for the + * per-message nonce, and the last 4 bytes for internal use. In that + * case, before calling this function on a new message you need to + * set the first 12 bytes of \p nonce_counter to your chosen nonce + * value, the last 4 to 0, and \p nc_off to 0 (which will cause \p + * stream_block to be ignored). That way, you can encrypt at most + * 2**96 messages of up to 2**32 blocks each with the same key. + * + * The per-message nonce (or information sufficient to reconstruct + * it) needs to be communicated with the ciphertext and must be unique. + * The recommended way to ensure uniqueness is to use a message + * counter. An alternative is to generate random nonces, but this + * limits the number of messages that can be securely encrypted: + * for example, with 96-bit random nonces, you should not encrypt + * more than 2**32 messages with the same key. + * + * Note that for both strategies, sizes are measured in blocks and + * that an ARIA block is 16 bytes. + * + * \warning Upon return, \p stream_block contains sensitive data. Its + * content must not be written to insecure storage and should be + * securely discarded as soon as it's no longer needed. + * + * \param ctx The ARIA context to use for encryption or decryption. + * This must be initialized and bound to a key. + * \param length The length of the input data \p input in Bytes. + * \param nc_off The offset in Bytes in the current \p stream_block, + * for resuming within the current cipher stream. The + * offset pointer should be \c 0 at the start of a + * stream. This must not be larger than \c 15 Bytes. + * \param nonce_counter The 128-bit nonce and counter. This must point to + * a read/write buffer of length \c 16 bytes. + * \param stream_block The saved stream block for resuming. This must + * point to a read/write buffer of length \c 16 bytes. + * This is overwritten by the function. + * \param input The buffer holding the input data. This must + * be a readable buffer of length \p length Bytes. + * \param output The buffer holding the output data. This must + * be a writable buffer of length \p length Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_aria_crypt_ctr(mbedtls_aria_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[MBEDTLS_ARIA_BLOCKSIZE], + unsigned char stream_block[MBEDTLS_ARIA_BLOCKSIZE], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief Checkup routine. + * + * \return \c 0 on success, or \c 1 on failure. + */ +int mbedtls_aria_self_test(int verbose); +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* aria.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/asn1.h b/r5dev/thirdparty/mbedtls/include/mbedtls/asn1.h new file mode 100644 index 00000000..b07983be --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/asn1.h @@ -0,0 +1,649 @@ +/** + * \file asn1.h + * + * \brief Generic ASN.1 parsing + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ASN1_H +#define MBEDTLS_ASN1_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" +#include "mbedtls/platform_util.h" + +#include + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + +/** + * \addtogroup asn1_module + * \{ + */ + +/** + * \name ASN1 Error codes + * These error codes are combined with other error codes for + * higher error granularity. + * e.g. X.509 and PKCS #7 error codes + * ASN1 is a standard to specify data structures. + * \{ + */ +/** Out of data when parsing an ASN1 data structure. */ +#define MBEDTLS_ERR_ASN1_OUT_OF_DATA -0x0060 +/** ASN1 tag was of an unexpected value. */ +#define MBEDTLS_ERR_ASN1_UNEXPECTED_TAG -0x0062 +/** Error when trying to determine the length or invalid length. */ +#define MBEDTLS_ERR_ASN1_INVALID_LENGTH -0x0064 +/** Actual length differs from expected length. */ +#define MBEDTLS_ERR_ASN1_LENGTH_MISMATCH -0x0066 +/** Data is invalid. */ +#define MBEDTLS_ERR_ASN1_INVALID_DATA -0x0068 +/** Memory allocation failed */ +#define MBEDTLS_ERR_ASN1_ALLOC_FAILED -0x006A +/** Buffer too small when writing ASN.1 data structure. */ +#define MBEDTLS_ERR_ASN1_BUF_TOO_SMALL -0x006C + +/** \} name ASN1 Error codes */ + +/** + * \name DER constants + * These constants comply with the DER encoded ASN.1 type tags. + * DER encoding uses hexadecimal representation. + * An example DER sequence is:\n + * - 0x02 -- tag indicating INTEGER + * - 0x01 -- length in octets + * - 0x05 -- value + * Such sequences are typically read into \c ::mbedtls_x509_buf. + * \{ + */ +#define MBEDTLS_ASN1_BOOLEAN 0x01 +#define MBEDTLS_ASN1_INTEGER 0x02 +#define MBEDTLS_ASN1_BIT_STRING 0x03 +#define MBEDTLS_ASN1_OCTET_STRING 0x04 +#define MBEDTLS_ASN1_NULL 0x05 +#define MBEDTLS_ASN1_OID 0x06 +#define MBEDTLS_ASN1_ENUMERATED 0x0A +#define MBEDTLS_ASN1_UTF8_STRING 0x0C +#define MBEDTLS_ASN1_SEQUENCE 0x10 +#define MBEDTLS_ASN1_SET 0x11 +#define MBEDTLS_ASN1_PRINTABLE_STRING 0x13 +#define MBEDTLS_ASN1_T61_STRING 0x14 +#define MBEDTLS_ASN1_IA5_STRING 0x16 +#define MBEDTLS_ASN1_UTC_TIME 0x17 +#define MBEDTLS_ASN1_GENERALIZED_TIME 0x18 +#define MBEDTLS_ASN1_UNIVERSAL_STRING 0x1C +#define MBEDTLS_ASN1_BMP_STRING 0x1E +#define MBEDTLS_ASN1_PRIMITIVE 0x00 +#define MBEDTLS_ASN1_CONSTRUCTED 0x20 +#define MBEDTLS_ASN1_CONTEXT_SPECIFIC 0x80 + +/* Slightly smaller way to check if tag is a string tag + * compared to canonical implementation. */ +#define MBEDTLS_ASN1_IS_STRING_TAG(tag) \ + ((tag) < 32u && ( \ + ((1u << (tag)) & ((1u << MBEDTLS_ASN1_BMP_STRING) | \ + (1u << MBEDTLS_ASN1_UTF8_STRING) | \ + (1u << MBEDTLS_ASN1_T61_STRING) | \ + (1u << MBEDTLS_ASN1_IA5_STRING) | \ + (1u << MBEDTLS_ASN1_UNIVERSAL_STRING) | \ + (1u << MBEDTLS_ASN1_PRINTABLE_STRING) | \ + (1u << MBEDTLS_ASN1_BIT_STRING))) != 0)) + +/* + * Bit masks for each of the components of an ASN.1 tag as specified in + * ITU X.690 (08/2015), section 8.1 "General rules for encoding", + * paragraph 8.1.2.2: + * + * Bit 8 7 6 5 1 + * +-------+-----+------------+ + * | Class | P/C | Tag number | + * +-------+-----+------------+ + */ +#define MBEDTLS_ASN1_TAG_CLASS_MASK 0xC0 +#define MBEDTLS_ASN1_TAG_PC_MASK 0x20 +#define MBEDTLS_ASN1_TAG_VALUE_MASK 0x1F + +/** \} name DER constants */ + +/** Returns the size of the binary string, without the trailing \\0 */ +#define MBEDTLS_OID_SIZE(x) (sizeof(x) - 1) + +/** + * Compares an mbedtls_asn1_buf structure to a reference OID. + * + * Only works for 'defined' oid_str values (MBEDTLS_OID_HMAC_SHA1), you cannot use a + * 'unsigned char *oid' here! + */ +#define MBEDTLS_OID_CMP(oid_str, oid_buf) \ + ((MBEDTLS_OID_SIZE(oid_str) != (oid_buf)->len) || \ + memcmp((oid_str), (oid_buf)->p, (oid_buf)->len) != 0) + +#define MBEDTLS_OID_CMP_RAW(oid_str, oid_buf, oid_buf_len) \ + ((MBEDTLS_OID_SIZE(oid_str) != (oid_buf_len)) || \ + memcmp((oid_str), (oid_buf), (oid_buf_len)) != 0) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name Functions to parse ASN.1 data structures + * \{ + */ + +/** + * Type-length-value structure that allows for ASN1 using DER. + */ +typedef struct mbedtls_asn1_buf { + int tag; /**< ASN1 type, e.g. MBEDTLS_ASN1_UTF8_STRING. */ + size_t len; /**< ASN1 length, in octets. */ + unsigned char *p; /**< ASN1 data, e.g. in ASCII. */ +} +mbedtls_asn1_buf; + +/** + * Container for ASN1 bit strings. + */ +typedef struct mbedtls_asn1_bitstring { + size_t len; /**< ASN1 length, in octets. */ + unsigned char unused_bits; /**< Number of unused bits at the end of the string */ + unsigned char *p; /**< Raw ASN1 data for the bit string */ +} +mbedtls_asn1_bitstring; + +/** + * Container for a sequence of ASN.1 items + */ +typedef struct mbedtls_asn1_sequence { + mbedtls_asn1_buf buf; /**< Buffer containing the given ASN.1 item. */ + + /** The next entry in the sequence. + * + * The details of memory management for sequences are not documented and + * may change in future versions. Set this field to \p NULL when + * initializing a structure, and do not modify it except via Mbed TLS + * library functions. + */ + struct mbedtls_asn1_sequence *next; +} +mbedtls_asn1_sequence; + +/** + * Container for a sequence or list of 'named' ASN.1 data items + */ +typedef struct mbedtls_asn1_named_data { + mbedtls_asn1_buf oid; /**< The object identifier. */ + mbedtls_asn1_buf val; /**< The named value. */ + + /** The next entry in the sequence. + * + * The details of memory management for named data sequences are not + * documented and may change in future versions. Set this field to \p NULL + * when initializing a structure, and do not modify it except via Mbed TLS + * library functions. + */ + struct mbedtls_asn1_named_data *next; + + /** Merge next item into the current one? + * + * This field exists for the sake of Mbed TLS's X.509 certificate parsing + * code and may change in future versions of the library. + */ + unsigned char MBEDTLS_PRIVATE(next_merged); +} +mbedtls_asn1_named_data; + +/** + * \brief Get the length of an ASN.1 element. + * Updates the pointer to immediately behind the length. + * + * \param p On entry, \c *p points to the first byte of the length, + * i.e. immediately after the tag. + * On successful completion, \c *p points to the first byte + * after the length, i.e. the first byte of the content. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On successful completion, \c *len contains the length + * read from the ASN.1 input. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element + * would end beyond \p end. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparsable. + */ +int mbedtls_asn1_get_len(unsigned char **p, + const unsigned char *end, + size_t *len); + +/** + * \brief Get the tag and length of the element. + * Check for the requested tag. + * Updates the pointer to immediately behind the tag and length. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * after the length, i.e. the first byte of the content. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On successful completion, \c *len contains the length + * read from the ASN.1 input. + * \param tag The expected tag. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the data does not start + * with the requested tag. + * \return #MBEDTLS_ERR_ASN1_OUT_OF_DATA if the ASN.1 element + * would end beyond \p end. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the length is unparsable. + */ +int mbedtls_asn1_get_tag(unsigned char **p, + const unsigned char *end, + size_t *len, int tag); + +/** + * \brief Retrieve a boolean ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value (\c 0 or \c 1). + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BOOLEAN. + */ +int mbedtls_asn1_get_bool(unsigned char **p, + const unsigned char *end, + int *val); + +/** + * \brief Retrieve an integer ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value. + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + */ +int mbedtls_asn1_get_int(unsigned char **p, + const unsigned char *end, + int *val); + +/** + * \brief Retrieve an enumerated ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param val On success, the parsed value. + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 ENUMERATED. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + */ +int mbedtls_asn1_get_enum(unsigned char **p, + const unsigned char *end, + int *val); + +/** + * \brief Retrieve a bitstring ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p is equal to \p end. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param bs On success, ::mbedtls_asn1_bitstring information about + * the parsed value. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input contains + * extra data after a valid BIT STRING. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BIT STRING. + */ +int mbedtls_asn1_get_bitstring(unsigned char **p, const unsigned char *end, + mbedtls_asn1_bitstring *bs); + +/** + * \brief Retrieve a bitstring ASN.1 tag without unused bits and its + * value. + * Updates the pointer to the beginning of the bit/octet string. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * of the content of the BIT STRING. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param len On success, \c *len is the length of the content in bytes. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_INVALID_DATA if the input starts with + * a valid BIT STRING with a nonzero number of unused bits. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 BIT STRING. + */ +int mbedtls_asn1_get_bitstring_null(unsigned char **p, + const unsigned char *end, + size_t *len); + +/** + * \brief Parses and splits an ASN.1 "SEQUENCE OF ". + * Updates the pointer to immediately behind the full sequence tag. + * + * This function allocates memory for the sequence elements. You can free + * the allocated memory with mbedtls_asn1_sequence_free(). + * + * \note On error, this function may return a partial list in \p cur. + * You must set `cur->next = NULL` before calling this function! + * Otherwise it is impossible to distinguish a previously non-null + * pointer from a pointer to an object allocated by this function. + * + * \note If the sequence is empty, this function does not modify + * \c *cur. If the sequence is valid and non-empty, this + * function sets `cur->buf.tag` to \p tag. This allows + * callers to distinguish between an empty sequence and + * a one-element sequence. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p is equal to \p end. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param cur A ::mbedtls_asn1_sequence which this function fills. + * When this function returns, \c *cur is the head of a linked + * list. Each node in this list is allocated with + * mbedtls_calloc() apart from \p cur itself, and should + * therefore be freed with mbedtls_free(). + * The list describes the content of the sequence. + * The head of the list (i.e. \c *cur itself) describes the + * first element, `*cur->next` describes the second element, etc. + * For each element, `buf.tag == tag`, `buf.len` is the length + * of the content of the content of the element, and `buf.p` + * points to the first byte of the content (i.e. immediately + * past the length of the element). + * Note that list elements may be allocated even on error. + * \param tag Each element of the sequence must have this tag. + * + * \return 0 if successful. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input contains + * extra data after a valid SEQUENCE OF \p tag. + * \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the input starts with + * an ASN.1 SEQUENCE in which an element has a tag that + * is different from \p tag. + * \return #MBEDTLS_ERR_ASN1_ALLOC_FAILED if a memory allocation failed. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 SEQUENCE. + */ +int mbedtls_asn1_get_sequence_of(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_sequence *cur, + const int tag); +/** + * \brief Free a heap-allocated linked list presentation of + * an ASN.1 sequence, including the first element. + * + * There are two common ways to manage the memory used for the representation + * of a parsed ASN.1 sequence: + * - Allocate a head node `mbedtls_asn1_sequence *head` with mbedtls_calloc(). + * Pass this node as the `cur` argument to mbedtls_asn1_get_sequence_of(). + * When you have finished processing the sequence, + * call mbedtls_asn1_sequence_free() on `head`. + * - Allocate a head node `mbedtls_asn1_sequence *head` in any manner, + * for example on the stack. Make sure that `head->next == NULL`. + * Pass `head` as the `cur` argument to mbedtls_asn1_get_sequence_of(). + * When you have finished processing the sequence, + * call mbedtls_asn1_sequence_free() on `head->cur`, + * then free `head` itself in the appropriate manner. + * + * \param seq The address of the first sequence component. This may + * be \c NULL, in which case this functions returns + * immediately. + */ +void mbedtls_asn1_sequence_free(mbedtls_asn1_sequence *seq); + +/** + * \brief Traverse an ASN.1 SEQUENCE container and + * call a callback for each entry. + * + * This function checks that the input is a SEQUENCE of elements that + * each have a "must" tag, and calls a callback function on the elements + * that have a "may" tag. + * + * For example, to validate that the input is a SEQUENCE of `tag1` and call + * `cb` on each element, use + * ``` + * mbedtls_asn1_traverse_sequence_of(&p, end, 0xff, tag1, 0, 0, cb, ctx); + * ``` + * + * To validate that the input is a SEQUENCE of ANY and call `cb` on + * each element, use + * ``` + * mbedtls_asn1_traverse_sequence_of(&p, end, 0, 0, 0, 0, cb, ctx); + * ``` + * + * To validate that the input is a SEQUENCE of CHOICE {NULL, OCTET STRING} + * and call `cb` on each element that is an OCTET STRING, use + * ``` + * mbedtls_asn1_traverse_sequence_of(&p, end, 0xfe, 0x04, 0xff, 0x04, cb, ctx); + * ``` + * + * The callback is called on the elements with a "may" tag from left to + * right. If the input is not a valid SEQUENCE of elements with a "must" tag, + * the callback is called on the elements up to the leftmost point where + * the input is invalid. + * + * \warning This function is still experimental and may change + * at any time. + * + * \param p The address of the pointer to the beginning of + * the ASN.1 SEQUENCE header. This is updated to + * point to the end of the ASN.1 SEQUENCE container + * on a successful invocation. + * \param end The end of the ASN.1 SEQUENCE container. + * \param tag_must_mask A mask to be applied to the ASN.1 tags found within + * the SEQUENCE before comparing to \p tag_must_value. + * \param tag_must_val The required value of each ASN.1 tag found in the + * SEQUENCE, after masking with \p tag_must_mask. + * Mismatching tags lead to an error. + * For example, a value of \c 0 for both \p tag_must_mask + * and \p tag_must_val means that every tag is allowed, + * while a value of \c 0xFF for \p tag_must_mask means + * that \p tag_must_val is the only allowed tag. + * \param tag_may_mask A mask to be applied to the ASN.1 tags found within + * the SEQUENCE before comparing to \p tag_may_value. + * \param tag_may_val The desired value of each ASN.1 tag found in the + * SEQUENCE, after masking with \p tag_may_mask. + * Mismatching tags will be silently ignored. + * For example, a value of \c 0 for \p tag_may_mask and + * \p tag_may_val means that any tag will be considered, + * while a value of \c 0xFF for \p tag_may_mask means + * that all tags with value different from \p tag_may_val + * will be ignored. + * \param cb The callback to trigger for each component + * in the ASN.1 SEQUENCE that matches \p tag_may_val. + * The callback function is called with the following + * parameters: + * - \p ctx. + * - The tag of the current element. + * - A pointer to the start of the current element's + * content inside the input. + * - The length of the content of the current element. + * If the callback returns a non-zero value, + * the function stops immediately, + * forwarding the callback's return value. + * \param ctx The context to be passed to the callback \p cb. + * + * \return \c 0 if successful the entire ASN.1 SEQUENCE + * was traversed without parsing or callback errors. + * \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input + * contains extra data after a valid SEQUENCE + * of elements with an accepted tag. + * \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the input starts + * with an ASN.1 SEQUENCE in which an element has a tag + * that is not accepted. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 SEQUENCE. + * \return A non-zero error code forwarded from the callback + * \p cb in case the latter returns a non-zero value. + */ +int mbedtls_asn1_traverse_sequence_of( + unsigned char **p, + const unsigned char *end, + unsigned char tag_must_mask, unsigned char tag_must_val, + unsigned char tag_may_mask, unsigned char tag_may_val, + int (*cb)(void *ctx, int tag, + unsigned char *start, size_t len), + void *ctx); + +#if defined(MBEDTLS_BIGNUM_C) +/** + * \brief Retrieve an integer ASN.1 tag and its value. + * Updates the pointer to immediately behind the full tag. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the ASN.1 element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param X On success, the parsed value. + * + * \return 0 if successful. + * \return An ASN.1 error code if the input does not start with + * a valid ASN.1 INTEGER. + * \return #MBEDTLS_ERR_ASN1_INVALID_LENGTH if the parsed value does + * not fit in an \c int. + * \return An MPI error code if the parsed value is too large. + */ +int mbedtls_asn1_get_mpi(unsigned char **p, + const unsigned char *end, + mbedtls_mpi *X); +#endif /* MBEDTLS_BIGNUM_C */ + +/** + * \brief Retrieve an AlgorithmIdentifier ASN.1 sequence. + * Updates the pointer to immediately behind the full + * AlgorithmIdentifier. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the AlgorithmIdentifier element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param alg The buffer to receive the OID. + * \param params The buffer to receive the parameters. + * This is zeroized if there are no parameters. + * + * \return 0 if successful or a specific ASN.1 or MPI error code. + */ +int mbedtls_asn1_get_alg(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params); + +/** + * \brief Retrieve an AlgorithmIdentifier ASN.1 sequence with NULL or no + * params. + * Updates the pointer to immediately behind the full + * AlgorithmIdentifier. + * + * \param p On entry, \c *p points to the start of the ASN.1 element. + * On successful completion, \c *p points to the first byte + * beyond the AlgorithmIdentifier element. + * On error, the value of \c *p is undefined. + * \param end End of data. + * \param alg The buffer to receive the OID. + * + * \return 0 if successful or a specific ASN.1 or MPI error code. + */ +int mbedtls_asn1_get_alg_null(unsigned char **p, + const unsigned char *end, + mbedtls_asn1_buf *alg); + +/** + * \brief Find a specific named_data entry in a sequence or list based on + * the OID. + * + * \param list The list to seek through + * \param oid The OID to look for + * \param len Size of the OID + * + * \return NULL if not found, or a pointer to the existing entry. + */ +const mbedtls_asn1_named_data *mbedtls_asn1_find_named_data(const mbedtls_asn1_named_data *list, + const char *oid, size_t len); + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief Free a mbedtls_asn1_named_data entry + * + * \deprecated This function is deprecated and will be removed in a + * future version of the library. + * Please use mbedtls_asn1_free_named_data_list() + * or mbedtls_asn1_free_named_data_list_shallow(). + * + * \param entry The named data entry to free. + * This function calls mbedtls_free() on + * `entry->oid.p` and `entry->val.p`. + */ +void MBEDTLS_DEPRECATED mbedtls_asn1_free_named_data(mbedtls_asn1_named_data *entry); +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +/** + * \brief Free all entries in a mbedtls_asn1_named_data list. + * + * \param head Pointer to the head of the list of named data entries to free. + * This function calls mbedtls_free() on + * `entry->oid.p` and `entry->val.p` and then on `entry` + * for each list entry, and sets \c *head to \c NULL. + */ +void mbedtls_asn1_free_named_data_list(mbedtls_asn1_named_data **head); + +/** + * \brief Free all shallow entries in a mbedtls_asn1_named_data list, + * but do not free internal pointer targets. + * + * \param name Head of the list of named data entries to free. + * This function calls mbedtls_free() on each list element. + */ +void mbedtls_asn1_free_named_data_list_shallow(mbedtls_asn1_named_data *name); + +/** \} name Functions to parse ASN.1 data structures */ +/** \} addtogroup asn1_module */ + +#ifdef __cplusplus +} +#endif + +#endif /* asn1.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/asn1write.h b/r5dev/thirdparty/mbedtls/include/mbedtls/asn1write.h new file mode 100644 index 00000000..da737596 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/asn1write.h @@ -0,0 +1,375 @@ +/** + * \file asn1write.h + * + * \brief ASN.1 buffer writing functionality + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ASN1_WRITE_H +#define MBEDTLS_ASN1_WRITE_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/asn1.h" + +#define MBEDTLS_ASN1_CHK_ADD(g, f) \ + do \ + { \ + if ((ret = (f)) < 0) \ + return ret; \ + else \ + (g) += ret; \ + } while (0) + +#define MBEDTLS_ASN1_CHK_CLEANUP_ADD(g, f) \ + do \ + { \ + if ((ret = (f)) < 0) \ + goto cleanup; \ + else \ + (g) += ret; \ + } while (0) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Write a length field in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param len The length value to write. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_len(unsigned char **p, const unsigned char *start, + size_t len); +/** + * \brief Write an ASN.1 tag in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param tag The tag to write. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_tag(unsigned char **p, const unsigned char *start, + unsigned char tag); + +/** + * \brief Write raw buffer data. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param buf The data buffer to write. + * \param size The length of the data buffer. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_raw_buffer(unsigned char **p, const unsigned char *start, + const unsigned char *buf, size_t size); + +#if defined(MBEDTLS_BIGNUM_C) +/** + * \brief Write an arbitrary-precision number (#MBEDTLS_ASN1_INTEGER) + * in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param X The MPI to write. + * It must be non-negative. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_mpi(unsigned char **p, const unsigned char *start, + const mbedtls_mpi *X); +#endif /* MBEDTLS_BIGNUM_C */ + +/** + * \brief Write a NULL tag (#MBEDTLS_ASN1_NULL) with zero data + * in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_null(unsigned char **p, const unsigned char *start); + +/** + * \brief Write an OID tag (#MBEDTLS_ASN1_OID) and data + * in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param oid The OID to write. + * \param oid_len The length of the OID. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_oid(unsigned char **p, const unsigned char *start, + const char *oid, size_t oid_len); + +/** + * \brief Write an AlgorithmIdentifier sequence in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param oid The OID of the algorithm to write. + * \param oid_len The length of the algorithm's OID. + * \param par_len The length of the parameters, which must be already written. + * If 0, NULL parameters are added + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_algorithm_identifier(unsigned char **p, + const unsigned char *start, + const char *oid, size_t oid_len, + size_t par_len); + +/** + * \brief Write a boolean tag (#MBEDTLS_ASN1_BOOLEAN) and value + * in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param boolean The boolean value to write, either \c 0 or \c 1. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_bool(unsigned char **p, const unsigned char *start, + int boolean); + +/** + * \brief Write an int tag (#MBEDTLS_ASN1_INTEGER) and value + * in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param val The integer value to write. + * It must be non-negative. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_int(unsigned char **p, const unsigned char *start, int val); + +/** + * \brief Write an enum tag (#MBEDTLS_ASN1_ENUMERATED) and value + * in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param val The integer value to write. + * + * \return The number of bytes written to \p p on success. + * \return A negative \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_asn1_write_enum(unsigned char **p, const unsigned char *start, int val); + +/** + * \brief Write a string in ASN.1 format using a specific + * string encoding tag. + + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param tag The string encoding tag to write, e.g. + * #MBEDTLS_ASN1_UTF8_STRING. + * \param text The string to write. + * \param text_len The length of \p text in bytes (which might + * be strictly larger than the number of characters). + * + * \return The number of bytes written to \p p on success. + * \return A negative error code on failure. + */ +int mbedtls_asn1_write_tagged_string(unsigned char **p, const unsigned char *start, + int tag, const char *text, + size_t text_len); + +/** + * \brief Write a string in ASN.1 format using the PrintableString + * string encoding tag (#MBEDTLS_ASN1_PRINTABLE_STRING). + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param text The string to write. + * \param text_len The length of \p text in bytes (which might + * be strictly larger than the number of characters). + * + * \return The number of bytes written to \p p on success. + * \return A negative error code on failure. + */ +int mbedtls_asn1_write_printable_string(unsigned char **p, + const unsigned char *start, + const char *text, size_t text_len); + +/** + * \brief Write a UTF8 string in ASN.1 format using the UTF8String + * string encoding tag (#MBEDTLS_ASN1_UTF8_STRING). + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param text The string to write. + * \param text_len The length of \p text in bytes (which might + * be strictly larger than the number of characters). + * + * \return The number of bytes written to \p p on success. + * \return A negative error code on failure. + */ +int mbedtls_asn1_write_utf8_string(unsigned char **p, const unsigned char *start, + const char *text, size_t text_len); + +/** + * \brief Write a string in ASN.1 format using the IA5String + * string encoding tag (#MBEDTLS_ASN1_IA5_STRING). + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param text The string to write. + * \param text_len The length of \p text in bytes (which might + * be strictly larger than the number of characters). + * + * \return The number of bytes written to \p p on success. + * \return A negative error code on failure. + */ +int mbedtls_asn1_write_ia5_string(unsigned char **p, const unsigned char *start, + const char *text, size_t text_len); + +/** + * \brief Write a bitstring tag (#MBEDTLS_ASN1_BIT_STRING) and + * value in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param buf The bitstring to write. + * \param bits The total number of bits in the bitstring. + * + * \return The number of bytes written to \p p on success. + * \return A negative error code on failure. + */ +int mbedtls_asn1_write_bitstring(unsigned char **p, const unsigned char *start, + const unsigned char *buf, size_t bits); + +/** + * \brief This function writes a named bitstring tag + * (#MBEDTLS_ASN1_BIT_STRING) and value in ASN.1 format. + * + * As stated in RFC 5280 Appendix B, trailing zeroes are + * omitted when encoding named bitstrings in DER. + * + * \note This function works backwards within the data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer which is used for bounds-checking. + * \param buf The bitstring to write. + * \param bits The total number of bits in the bitstring. + * + * \return The number of bytes written to \p p on success. + * \return A negative error code on failure. + */ +int mbedtls_asn1_write_named_bitstring(unsigned char **p, + const unsigned char *start, + const unsigned char *buf, + size_t bits); + +/** + * \brief Write an octet string tag (#MBEDTLS_ASN1_OCTET_STRING) + * and value in ASN.1 format. + * + * \note This function works backwards in data buffer. + * + * \param p The reference to the current position pointer. + * \param start The start of the buffer, for bounds-checking. + * \param buf The buffer holding the data to write. + * \param size The length of the data buffer \p buf. + * + * \return The number of bytes written to \p p on success. + * \return A negative error code on failure. + */ +int mbedtls_asn1_write_octet_string(unsigned char **p, const unsigned char *start, + const unsigned char *buf, size_t size); + +/** + * \brief Create or find a specific named_data entry for writing in a + * sequence or list based on the OID. If not already in there, + * a new entry is added to the head of the list. + * Warning: Destructive behaviour for the val data! + * + * \param list The pointer to the location of the head of the list to seek + * through (will be updated in case of a new entry). + * \param oid The OID to look for. + * \param oid_len The size of the OID. + * \param val The associated data to store. If this is \c NULL, + * no data is copied to the new or existing buffer. + * \param val_len The minimum length of the data buffer needed. + * If this is 0, do not allocate a buffer for the associated + * data. + * If the OID was already present, enlarge, shrink or free + * the existing buffer to fit \p val_len. + * + * \return A pointer to the new / existing entry on success. + * \return \c NULL if there was a memory allocation error. + */ +mbedtls_asn1_named_data *mbedtls_asn1_store_named_data(mbedtls_asn1_named_data **list, + const char *oid, size_t oid_len, + const unsigned char *val, + size_t val_len); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_ASN1_WRITE_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/base64.h b/r5dev/thirdparty/mbedtls/include/mbedtls/base64.h new file mode 100644 index 00000000..635be713 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/base64.h @@ -0,0 +1,94 @@ +/** + * \file base64.h + * + * \brief RFC 1521 base64 encoding/decoding + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_BASE64_H +#define MBEDTLS_BASE64_H + +#include "mbedtls/build_info.h" + +#include + +/** Output buffer too small. */ +#define MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL -0x002A +/** Invalid character in input. */ +#define MBEDTLS_ERR_BASE64_INVALID_CHARACTER -0x002C + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Encode a buffer into base64 format + * + * \param dst destination buffer + * \param dlen size of the destination buffer + * \param olen number of bytes written + * \param src source buffer + * \param slen amount of data to be encoded + * + * \return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL. + * *olen is always updated to reflect the amount + * of data that has (or would have) been written. + * If that length cannot be represented, then no data is + * written to the buffer and *olen is set to the maximum + * length representable as a size_t. + * + * \note Call this function with dlen = 0 to obtain the + * required buffer size in *olen + */ +int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen); + +/** + * \brief Decode a base64-formatted buffer + * + * \param dst destination buffer (can be NULL for checking size) + * \param dlen size of the destination buffer + * \param olen number of bytes written + * \param src source buffer + * \param slen amount of data to be decoded + * + * \return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or + * MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is + * not correct. *olen is always updated to reflect the amount + * of data that has (or would have) been written. + * + * \note Call this function with *dst = NULL or dlen = 0 to obtain + * the required buffer size in *olen + */ +int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, + const unsigned char *src, size_t slen); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_base64_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* base64.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/bignum.h b/r5dev/thirdparty/mbedtls/include/mbedtls/bignum.h new file mode 100644 index 00000000..5c2c8431 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/bignum.h @@ -0,0 +1,1071 @@ +/** + * \file bignum.h + * + * \brief Multi-precision integer library + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_BIGNUM_H +#define MBEDTLS_BIGNUM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +#if defined(MBEDTLS_FS_IO) +#include +#endif + +/** An error occurred while reading from or writing to a file. */ +#define MBEDTLS_ERR_MPI_FILE_IO_ERROR -0x0002 +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_MPI_BAD_INPUT_DATA -0x0004 +/** There is an invalid character in the digit string. */ +#define MBEDTLS_ERR_MPI_INVALID_CHARACTER -0x0006 +/** The buffer is too small to write to. */ +#define MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL -0x0008 +/** The input arguments are negative or result in illegal output. */ +#define MBEDTLS_ERR_MPI_NEGATIVE_VALUE -0x000A +/** The input argument for division is zero, which is not allowed. */ +#define MBEDTLS_ERR_MPI_DIVISION_BY_ZERO -0x000C +/** The input arguments are not acceptable. */ +#define MBEDTLS_ERR_MPI_NOT_ACCEPTABLE -0x000E +/** Memory allocation failed. */ +#define MBEDTLS_ERR_MPI_ALLOC_FAILED -0x0010 + +#define MBEDTLS_MPI_CHK(f) \ + do \ + { \ + if ((ret = (f)) != 0) \ + goto cleanup; \ + } while (0) + +/* + * Maximum size MPIs are allowed to grow to in number of limbs. + */ +#define MBEDTLS_MPI_MAX_LIMBS 10000 + +#if !defined(MBEDTLS_MPI_WINDOW_SIZE) +/* + * Maximum window size used for modular exponentiation. Default: 2 + * Minimum value: 1. Maximum value: 6. + * + * Result is an array of ( 2 ** MBEDTLS_MPI_WINDOW_SIZE ) MPIs used + * for the sliding window calculation. (So 64 by default) + * + * Reduction in size, reduces speed. + */ +#define MBEDTLS_MPI_WINDOW_SIZE 2 /**< Maximum window size used. */ +#endif /* !MBEDTLS_MPI_WINDOW_SIZE */ + +#if !defined(MBEDTLS_MPI_MAX_SIZE) +/* + * Maximum size of MPIs allowed in bits and bytes for user-MPIs. + * ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits ) + * + * Note: Calculations can temporarily result in larger MPIs. So the number + * of limbs required (MBEDTLS_MPI_MAX_LIMBS) is higher. + */ +#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */ +#endif /* !MBEDTLS_MPI_MAX_SIZE */ + +#define MBEDTLS_MPI_MAX_BITS (8 * MBEDTLS_MPI_MAX_SIZE) /**< Maximum number of bits for usable MPIs. */ + +/* + * When reading from files with mbedtls_mpi_read_file() and writing to files with + * mbedtls_mpi_write_file() the buffer should have space + * for a (short) label, the MPI (in the provided radix), the newline + * characters and the '\0'. + * + * By default we assume at least a 10 char label, a minimum radix of 10 + * (decimal) and a maximum of 4096 bit numbers (1234 decimal chars). + * Autosized at compile time for at least a 10 char label, a minimum radix + * of 10 (decimal) for a number of MBEDTLS_MPI_MAX_BITS size. + * + * This used to be statically sized to 1250 for a maximum of 4096 bit + * numbers (1234 decimal chars). + * + * Calculate using the formula: + * MBEDTLS_MPI_RW_BUFFER_SIZE = ceil(MBEDTLS_MPI_MAX_BITS / ln(10) * ln(2)) + + * LabelSize + 6 + */ +#define MBEDTLS_MPI_MAX_BITS_SCALE100 (100 * MBEDTLS_MPI_MAX_BITS) +#define MBEDTLS_LN_2_DIV_LN_10_SCALE100 332 +#define MBEDTLS_MPI_RW_BUFFER_SIZE (((MBEDTLS_MPI_MAX_BITS_SCALE100 + \ + MBEDTLS_LN_2_DIV_LN_10_SCALE100 - 1) / \ + MBEDTLS_LN_2_DIV_LN_10_SCALE100) + 10 + 6) + +/* + * Define the base integer type, architecture-wise. + * + * 32 or 64-bit integer types can be forced regardless of the underlying + * architecture by defining MBEDTLS_HAVE_INT32 or MBEDTLS_HAVE_INT64 + * respectively and undefining MBEDTLS_HAVE_ASM. + * + * Double-width integers (e.g. 128-bit in 64-bit architectures) can be + * disabled by defining MBEDTLS_NO_UDBL_DIVISION. + */ +#if !defined(MBEDTLS_HAVE_INT32) + #if defined(_MSC_VER) && defined(_M_AMD64) +/* Always choose 64-bit when using MSC */ + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* !MBEDTLS_HAVE_INT64 */ +typedef int64_t mbedtls_mpi_sint; +typedef uint64_t mbedtls_mpi_uint; + #elif defined(__GNUC__) && ( \ + defined(__amd64__) || defined(__x86_64__) || \ + defined(__ppc64__) || defined(__powerpc64__) || \ + defined(__ia64__) || defined(__alpha__) || \ + (defined(__sparc__) && defined(__arch64__)) || \ + defined(__s390x__) || defined(__mips64) || \ + defined(__aarch64__)) + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* MBEDTLS_HAVE_INT64 */ +typedef int64_t mbedtls_mpi_sint; +typedef uint64_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) +/* mbedtls_t_udbl defined as 128-bit unsigned int */ +typedef unsigned int mbedtls_t_udbl __attribute__((mode(TI))); + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ + #elif defined(__ARMCC_VERSION) && defined(__aarch64__) +/* + * __ARMCC_VERSION is defined for both armcc and armclang and + * __aarch64__ is only defined by armclang when compiling 64-bit code + */ + #if !defined(MBEDTLS_HAVE_INT64) + #define MBEDTLS_HAVE_INT64 + #endif /* !MBEDTLS_HAVE_INT64 */ +typedef int64_t mbedtls_mpi_sint; +typedef uint64_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) +/* mbedtls_t_udbl defined as 128-bit unsigned int */ +typedef __uint128_t mbedtls_t_udbl; + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ + #elif defined(MBEDTLS_HAVE_INT64) +/* Force 64-bit integers with unknown compiler */ +typedef int64_t mbedtls_mpi_sint; +typedef uint64_t mbedtls_mpi_uint; + #endif +#endif /* !MBEDTLS_HAVE_INT32 */ + +#if !defined(MBEDTLS_HAVE_INT64) +/* Default to 32-bit compilation */ + #if !defined(MBEDTLS_HAVE_INT32) + #define MBEDTLS_HAVE_INT32 + #endif /* !MBEDTLS_HAVE_INT32 */ +typedef int32_t mbedtls_mpi_sint; +typedef uint32_t mbedtls_mpi_uint; + #if !defined(MBEDTLS_NO_UDBL_DIVISION) +typedef uint64_t mbedtls_t_udbl; + #define MBEDTLS_HAVE_UDBL + #endif /* !MBEDTLS_NO_UDBL_DIVISION */ +#endif /* !MBEDTLS_HAVE_INT64 */ + +/** \typedef mbedtls_mpi_uint + * \brief The type of machine digits in a bignum, called _limbs_. + * + * This is always an unsigned integer type with no padding bits. The size + * is platform-dependent. + */ + +/** \typedef mbedtls_mpi_sint + * \brief The signed type corresponding to #mbedtls_mpi_uint. + * + * This is always an signed integer type with no padding bits. The size + * is platform-dependent. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief MPI structure + */ +typedef struct mbedtls_mpi { + /** Sign: -1 if the mpi is negative, 1 otherwise. + * + * The number 0 must be represented with `s = +1`. Although many library + * functions treat all-limbs-zero as equivalent to a valid representation + * of 0 regardless of the sign bit, there are exceptions, so bignum + * functions and external callers must always set \c s to +1 for the + * number zero. + * + * Note that this implies that calloc() or `... = {0}` does not create + * a valid MPI representation. You must call mbedtls_mpi_init(). + */ + int MBEDTLS_PRIVATE(s); + + /** Total number of limbs in \c p. */ + size_t MBEDTLS_PRIVATE(n); + + /** Pointer to limbs. + * + * This may be \c NULL if \c n is 0. + */ + mbedtls_mpi_uint *MBEDTLS_PRIVATE(p); +} +mbedtls_mpi; + +/** + * \brief Initialize an MPI context. + * + * This makes the MPI ready to be set or freed, + * but does not define a value for the MPI. + * + * \param X The MPI context to initialize. This must not be \c NULL. + */ +void mbedtls_mpi_init(mbedtls_mpi *X); + +/** + * \brief This function frees the components of an MPI context. + * + * \param X The MPI context to be cleared. This may be \c NULL, + * in which case this function is a no-op. If it is + * not \c NULL, it must point to an initialized MPI. + */ +void mbedtls_mpi_free(mbedtls_mpi *X); + +/** + * \brief Enlarge an MPI to the specified number of limbs. + * + * \note This function does nothing if the MPI is + * already large enough. + * + * \param X The MPI to grow. It must be initialized. + * \param nblimbs The target number of limbs. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_grow(mbedtls_mpi *X, size_t nblimbs); + +/** + * \brief This function resizes an MPI downwards, keeping at least the + * specified number of limbs. + * + * If \c X is smaller than \c nblimbs, it is resized up + * instead. + * + * \param X The MPI to shrink. This must point to an initialized MPI. + * \param nblimbs The minimum number of limbs to keep. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed + * (this can only happen when resizing up). + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_shrink(mbedtls_mpi *X, size_t nblimbs); + +/** + * \brief Make a copy of an MPI. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param Y The source MPI. This must point to an initialized MPI. + * + * \note The limb-buffer in the destination MPI is enlarged + * if necessary to hold the value in the source MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_copy(mbedtls_mpi *X, const mbedtls_mpi *Y); + +/** + * \brief Swap the contents of two MPIs. + * + * \param X The first MPI. It must be initialized. + * \param Y The second MPI. It must be initialized. + */ +void mbedtls_mpi_swap(mbedtls_mpi *X, mbedtls_mpi *Y); + +/** + * \brief Perform a safe conditional copy of MPI which doesn't + * reveal whether the condition was true or not. + * + * \param X The MPI to conditionally assign to. This must point + * to an initialized MPI. + * \param Y The MPI to be assigned from. This must point to an + * initialized MPI. + * \param assign The condition deciding whether to perform the + * assignment or not. Must be either 0 or 1: + * * \c 1: Perform the assignment `X = Y`. + * * \c 0: Keep the original value of \p X. + * + * \note This function is equivalent to + * `if( assign ) mbedtls_mpi_copy( X, Y );` + * except that it avoids leaking any information about whether + * the assignment was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + * + * \warning If \p assign is neither 0 nor 1, the result of this function + * is indeterminate, and the resulting value in \p X might be + * neither its original value nor the value in \p Y. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign); + +/** + * \brief Perform a safe conditional swap which doesn't + * reveal whether the condition was true or not. + * + * \param X The first MPI. This must be initialized. + * \param Y The second MPI. This must be initialized. + * \param swap The condition deciding whether to perform + * the swap or not. Must be either 0 or 1: + * * \c 1: Swap the values of \p X and \p Y. + * * \c 0: Keep the original values of \p X and \p Y. + * + * \note This function is equivalent to + * if( swap ) mbedtls_mpi_swap( X, Y ); + * except that it avoids leaking any information about whether + * the swap was done or not (the above code may leak + * information through branch prediction and/or memory access + * patterns analysis). + * + * \warning If \p swap is neither 0 nor 1, the result of this function + * is indeterminate, and both \p X and \p Y might end up with + * values different to either of the original ones. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + * + */ +int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char swap); + +/** + * \brief Store integer value in MPI. + * + * \param X The MPI to set. This must be initialized. + * \param z The value to use. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_lset(mbedtls_mpi *X, mbedtls_mpi_sint z); + +/** + * \brief Get a specific bit from an MPI. + * + * \param X The MPI to query. This must be initialized. + * \param pos Zero-based index of the bit to query. + * + * \return \c 0 or \c 1 on success, depending on whether bit \c pos + * of \c X is unset or set. + * \return A negative error code on failure. + */ +int mbedtls_mpi_get_bit(const mbedtls_mpi *X, size_t pos); + +/** + * \brief Modify a specific bit in an MPI. + * + * \note This function will grow the target MPI if necessary to set a + * bit to \c 1 in a not yet existing limb. It will not grow if + * the bit should be set to \c 0. + * + * \param X The MPI to modify. This must be initialized. + * \param pos Zero-based index of the bit to modify. + * \param val The desired value of bit \c pos: \c 0 or \c 1. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_set_bit(mbedtls_mpi *X, size_t pos, unsigned char val); + +/** + * \brief Return the number of bits of value \c 0 before the + * least significant bit of value \c 1. + * + * \note This is the same as the zero-based index of + * the least significant bit of value \c 1. + * + * \param X The MPI to query. + * + * \return The number of bits of value \c 0 before the least significant + * bit of value \c 1 in \p X. + */ +size_t mbedtls_mpi_lsb(const mbedtls_mpi *X); + +/** + * \brief Return the number of bits up to and including the most + * significant bit of value \c 1. + * + * * \note This is same as the one-based index of the most + * significant bit of value \c 1. + * + * \param X The MPI to query. This must point to an initialized MPI. + * + * \return The number of bits up to and including the most + * significant bit of value \c 1. + */ +size_t mbedtls_mpi_bitlen(const mbedtls_mpi *X); + +/** + * \brief Return the total size of an MPI value in bytes. + * + * \param X The MPI to use. This must point to an initialized MPI. + * + * \note The value returned by this function may be less than + * the number of bytes used to store \p X internally. + * This happens if and only if there are trailing bytes + * of value zero. + * + * \return The least number of bytes capable of storing + * the absolute value of \p X. + */ +size_t mbedtls_mpi_size(const mbedtls_mpi *X); + +/** + * \brief Import an MPI from an ASCII string. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param radix The numeric base of the input string. + * \param s Null-terminated string buffer. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_mpi_read_string(mbedtls_mpi *X, int radix, const char *s); + +/** + * \brief Export an MPI to an ASCII string. + * + * \param X The source MPI. This must point to an initialized MPI. + * \param radix The numeric base of the output string. + * \param buf The buffer to write the string to. This must be writable + * buffer of length \p buflen Bytes. + * \param buflen The available size in Bytes of \p buf. + * \param olen The address at which to store the length of the string + * written, including the final \c NULL byte. This must + * not be \c NULL. + * + * \note You can call this function with `buflen == 0` to obtain the + * minimum required buffer size in `*olen`. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if the target buffer \p buf + * is too small to hold the value of \p X in the desired base. + * In this case, `*olen` is nonetheless updated to contain the + * size of \p buf required for a successful call. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_write_string(const mbedtls_mpi *X, int radix, + char *buf, size_t buflen, size_t *olen); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief Read an MPI from a line in an opened file. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param radix The numeric base of the string representation used + * in the source line. + * \param fin The input file handle to use. This must not be \c NULL. + * + * \note On success, this function advances the file stream + * to the end of the current line or to EOF. + * + * The function returns \c 0 on an empty line. + * + * Leading whitespaces are ignored, as is a + * '0x' prefix for radix \c 16. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if the file read buffer + * is too small. + * \return Another negative error code on failure. + */ +int mbedtls_mpi_read_file(mbedtls_mpi *X, int radix, FILE *fin); + +/** + * \brief Export an MPI into an opened file. + * + * \param p A string prefix to emit prior to the MPI data. + * For example, this might be a label, or "0x" when + * printing in base \c 16. This may be \c NULL if no prefix + * is needed. + * \param X The source MPI. This must point to an initialized MPI. + * \param radix The numeric base to be used in the emitted string. + * \param fout The output file handle. This may be \c NULL, in which case + * the output is written to \c stdout. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_mpi_write_file(const char *p, const mbedtls_mpi *X, + int radix, FILE *fout); +#endif /* MBEDTLS_FS_IO */ + +/** + * \brief Import an MPI from unsigned big endian binary data. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param buf The input buffer. This must be a readable buffer of length + * \p buflen Bytes. + * \param buflen The length of the input buffer \p p in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_read_binary(mbedtls_mpi *X, const unsigned char *buf, + size_t buflen); + +/** + * \brief Import X from unsigned binary data, little endian + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param buf The input buffer. This must be a readable buffer of length + * \p buflen Bytes. + * \param buflen The length of the input buffer \p p in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_read_binary_le(mbedtls_mpi *X, + const unsigned char *buf, size_t buflen); + +/** + * \brief Export X into unsigned binary data, big endian. + * Always fills the whole buffer, which will start with zeros + * if the number is smaller. + * + * \param X The source MPI. This must point to an initialized MPI. + * \param buf The output buffer. This must be a writable buffer of length + * \p buflen Bytes. + * \param buflen The size of the output buffer \p buf in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p buf isn't + * large enough to hold the value of \p X. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_write_binary(const mbedtls_mpi *X, unsigned char *buf, + size_t buflen); + +/** + * \brief Export X into unsigned binary data, little endian. + * Always fills the whole buffer, which will end with zeros + * if the number is smaller. + * + * \param X The source MPI. This must point to an initialized MPI. + * \param buf The output buffer. This must be a writable buffer of length + * \p buflen Bytes. + * \param buflen The size of the output buffer \p buf in Bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if \p buf isn't + * large enough to hold the value of \p X. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_write_binary_le(const mbedtls_mpi *X, + unsigned char *buf, size_t buflen); + +/** + * \brief Perform a left-shift on an MPI: X <<= count + * + * \param X The MPI to shift. This must point to an initialized MPI. + * \param count The number of bits to shift by. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_shift_l(mbedtls_mpi *X, size_t count); + +/** + * \brief Perform a right-shift on an MPI: X >>= count + * + * \param X The MPI to shift. This must point to an initialized MPI. + * \param count The number of bits to shift by. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_shift_r(mbedtls_mpi *X, size_t count); + +/** + * \brief Compare the absolute values of two MPIs. + * + * \param X The left-hand MPI. This must point to an initialized MPI. + * \param Y The right-hand MPI. This must point to an initialized MPI. + * + * \return \c 1 if `|X|` is greater than `|Y|`. + * \return \c -1 if `|X|` is lesser than `|Y|`. + * \return \c 0 if `|X|` is equal to `|Y|`. + */ +int mbedtls_mpi_cmp_abs(const mbedtls_mpi *X, const mbedtls_mpi *Y); + +/** + * \brief Compare two MPIs. + * + * \param X The left-hand MPI. This must point to an initialized MPI. + * \param Y The right-hand MPI. This must point to an initialized MPI. + * + * \return \c 1 if \p X is greater than \p Y. + * \return \c -1 if \p X is lesser than \p Y. + * \return \c 0 if \p X is equal to \p Y. + */ +int mbedtls_mpi_cmp_mpi(const mbedtls_mpi *X, const mbedtls_mpi *Y); + +/** + * \brief Check if an MPI is less than the other in constant time. + * + * \param X The left-hand MPI. This must point to an initialized MPI + * with the same allocated length as Y. + * \param Y The right-hand MPI. This must point to an initialized MPI + * with the same allocated length as X. + * \param ret The result of the comparison: + * \c 1 if \p X is less than \p Y. + * \c 0 if \p X is greater than or equal to \p Y. + * + * \return 0 on success. + * \return MBEDTLS_ERR_MPI_BAD_INPUT_DATA if the allocated length of + * the two input MPIs is not the same. + */ +int mbedtls_mpi_lt_mpi_ct(const mbedtls_mpi *X, const mbedtls_mpi *Y, + unsigned *ret); + +/** + * \brief Compare an MPI with an integer. + * + * \param X The left-hand MPI. This must point to an initialized MPI. + * \param z The integer value to compare \p X to. + * + * \return \c 1 if \p X is greater than \p z. + * \return \c -1 if \p X is lesser than \p z. + * \return \c 0 if \p X is equal to \p z. + */ +int mbedtls_mpi_cmp_int(const mbedtls_mpi *X, mbedtls_mpi_sint z); + +/** + * \brief Perform an unsigned addition of MPIs: X = |A| + |B| + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first summand. This must point to an initialized MPI. + * \param B The second summand. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_add_abs(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Perform an unsigned subtraction of MPIs: X = |A| - |B| + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The minuend. This must point to an initialized MPI. + * \param B The subtrahend. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p B is greater than \p A. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_sub_abs(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Perform a signed addition of MPIs: X = A + B + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first summand. This must point to an initialized MPI. + * \param B The second summand. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_add_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Perform a signed subtraction of MPIs: X = A - B + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The minuend. This must point to an initialized MPI. + * \param B The subtrahend. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_sub_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Perform a signed addition of an MPI and an integer: X = A + b + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first summand. This must point to an initialized MPI. + * \param b The second summand. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_add_int(mbedtls_mpi *X, const mbedtls_mpi *A, + mbedtls_mpi_sint b); + +/** + * \brief Perform a signed subtraction of an MPI and an integer: + * X = A - b + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The minuend. This must point to an initialized MPI. + * \param b The subtrahend. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_sub_int(mbedtls_mpi *X, const mbedtls_mpi *A, + mbedtls_mpi_sint b); + +/** + * \brief Perform a multiplication of two MPIs: X = A * B + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first factor. This must point to an initialized MPI. + * \param B The second factor. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_mul_mpi(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Perform a multiplication of an MPI with an unsigned integer: + * X = A * b + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The first factor. This must point to an initialized MPI. + * \param b The second factor. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_mul_int(mbedtls_mpi *X, const mbedtls_mpi *A, + mbedtls_mpi_uint b); + +/** + * \brief Perform a division with remainder of two MPIs: + * A = Q * B + R + * + * \param Q The destination MPI for the quotient. + * This may be \c NULL if the value of the + * quotient is not needed. This must not alias A or B. + * \param R The destination MPI for the remainder value. + * This may be \c NULL if the value of the + * remainder is not needed. This must not alias A or B. + * \param A The dividend. This must point to an initialized MPI. + * \param B The divisor. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p B equals zero. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_div_mpi(mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Perform a division with remainder of an MPI by an integer: + * A = Q * b + R + * + * \param Q The destination MPI for the quotient. + * This may be \c NULL if the value of the + * quotient is not needed. This must not alias A. + * \param R The destination MPI for the remainder value. + * This may be \c NULL if the value of the + * remainder is not needed. This must not alias A. + * \param A The dividend. This must point to an initialized MPi. + * \param b The divisor. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p b equals zero. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_div_int(mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, + mbedtls_mpi_sint b); + +/** + * \brief Perform a modular reduction. R = A mod B + * + * \param R The destination MPI for the residue value. + * This must point to an initialized MPI. + * \param A The MPI to compute the residue of. + * This must point to an initialized MPI. + * \param B The base of the modular reduction. + * This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p B equals zero. + * \return #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p B is negative. + * \return Another negative error code on different kinds of failure. + * + */ +int mbedtls_mpi_mod_mpi(mbedtls_mpi *R, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Perform a modular reduction with respect to an integer. + * r = A mod b + * + * \param r The address at which to store the residue. + * This must not be \c NULL. + * \param A The MPI to compute the residue of. + * This must point to an initialized MPi. + * \param b The integer base of the modular reduction. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if \p b equals zero. + * \return #MBEDTLS_ERR_MPI_NEGATIVE_VALUE if \p b is negative. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_mod_int(mbedtls_mpi_uint *r, const mbedtls_mpi *A, + mbedtls_mpi_sint b); + +/** + * \brief Perform a sliding-window exponentiation: X = A^E mod N + * + * \param X The destination MPI. This must point to an initialized MPI. + * This must not alias E or N. + * \param A The base of the exponentiation. + * This must point to an initialized MPI. + * \param E The exponent MPI. This must point to an initialized MPI. + * \param N The base for the modular reduction. This must point to an + * initialized MPI. + * \param prec_RR A helper MPI depending solely on \p N which can be used to + * speed-up multiple modular exponentiations for the same value + * of \p N. This may be \c NULL. If it is not \c NULL, it must + * point to an initialized MPI. If it hasn't been used after + * the call to mbedtls_mpi_init(), this function will compute + * the helper value and store it in \p prec_RR for reuse on + * subsequent calls to this function. Otherwise, the function + * will assume that \p prec_RR holds the helper value set by a + * previous call to mbedtls_mpi_exp_mod(), and reuse it. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \c N is negative or + * even, or if \c E is negative. + * \return Another negative error code on different kinds of failures. + * + */ +int mbedtls_mpi_exp_mod(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *E, const mbedtls_mpi *N, + mbedtls_mpi *prec_RR); + +/** + * \brief Fill an MPI with a number of random bytes. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param size The number of random bytes to generate. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on failure. + * + * \note The bytes obtained from the RNG are interpreted + * as a big-endian representation of an MPI; this can + * be relevant in applications like deterministic ECDSA. + */ +int mbedtls_mpi_fill_random(mbedtls_mpi *X, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** Generate a random number uniformly in a range. + * + * This function generates a random number between \p min inclusive and + * \p N exclusive. + * + * The procedure complies with RFC 6979 §3.3 (deterministic ECDSA) + * when the RNG is a suitably parametrized instance of HMAC_DRBG + * and \p min is \c 1. + * + * \note There are `N - min` possible outputs. The lower bound + * \p min can be reached, but the upper bound \p N cannot. + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param min The minimum value to return. + * It must be nonnegative. + * \param N The upper bound of the range, exclusive. + * In other words, this is one plus the maximum value to return. + * \p N must be strictly larger than \p min. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p min or \p N is invalid + * or if they are incompatible. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if the implementation was + * unable to find a suitable value within a limited number + * of attempts. This has a negligible probability if \p N + * is significantly larger than \p min, which is the case + * for all usual cryptographic applications. + * \return Another negative error code on failure. + */ +int mbedtls_mpi_random(mbedtls_mpi *X, + mbedtls_mpi_sint min, + const mbedtls_mpi *N, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Compute the greatest common divisor: G = gcd(A, B) + * + * \param G The destination MPI. This must point to an initialized MPI. + * \param A The first operand. This must point to an initialized MPI. + * \param B The second operand. This must point to an initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_mpi_gcd(mbedtls_mpi *G, const mbedtls_mpi *A, + const mbedtls_mpi *B); + +/** + * \brief Compute the modular inverse: X = A^-1 mod N + * + * \param X The destination MPI. This must point to an initialized MPI. + * \param A The MPI to calculate the modular inverse of. This must point + * to an initialized MPI. + * \param N The base of the modular inversion. This must point to an + * initialized MPI. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if \p N is less than + * or equal to one. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p has no modular inverse + * with respect to \p N. + */ +int mbedtls_mpi_inv_mod(mbedtls_mpi *X, const mbedtls_mpi *A, + const mbedtls_mpi *N); + +/** + * \brief Miller-Rabin primality test. + * + * \warning If \p X is potentially generated by an adversary, for example + * when validating cryptographic parameters that you didn't + * generate yourself and that are supposed to be prime, then + * \p rounds should be at least the half of the security + * strength of the cryptographic algorithm. On the other hand, + * if \p X is chosen uniformly or non-adversarially (as is the + * case when mbedtls_mpi_gen_prime calls this function), then + * \p rounds can be much lower. + * + * \param X The MPI to check for primality. + * This must point to an initialized MPI. + * \param rounds The number of bases to perform the Miller-Rabin primality + * test for. The probability of returning 0 on a composite is + * at most 2-2*\p rounds . + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * This may be \c NULL if \p f_rng doesn't use + * a context parameter. + * + * \return \c 0 if successful, i.e. \p X is probably prime. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if \p X is not prime. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_mpi_is_prime_ext(const mbedtls_mpi *X, int rounds, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); +/** + * \brief Flags for mbedtls_mpi_gen_prime() + * + * Each of these flags is a constraint on the result X returned by + * mbedtls_mpi_gen_prime(). + */ +typedef enum { + MBEDTLS_MPI_GEN_PRIME_FLAG_DH = 0x0001, /**< (X-1)/2 is prime too */ + MBEDTLS_MPI_GEN_PRIME_FLAG_LOW_ERR = 0x0002, /**< lower error rate from 2-80 to 2-128 */ +} mbedtls_mpi_gen_prime_flag_t; + +/** + * \brief Generate a prime number. + * + * \param X The destination MPI to store the generated prime in. + * This must point to an initialized MPi. + * \param nbits The required size of the destination MPI in bits. + * This must be between \c 3 and #MBEDTLS_MPI_MAX_BITS. + * \param flags A mask of flags of type #mbedtls_mpi_gen_prime_flag_t. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. + * This may be \c NULL if \p f_rng doesn't use + * a context parameter. + * + * \return \c 0 if successful, in which case \p X holds a + * probably prime number. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if a memory allocation failed. + * \return #MBEDTLS_ERR_MPI_BAD_INPUT_DATA if `nbits` is not between + * \c 3 and #MBEDTLS_MPI_MAX_BITS. + */ +int mbedtls_mpi_gen_prime(mbedtls_mpi *X, size_t nbits, int flags, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_mpi_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* bignum.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/build_info.h b/r5dev/thirdparty/mbedtls/include/mbedtls/build_info.h new file mode 100644 index 00000000..cffa05bd --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/build_info.h @@ -0,0 +1,153 @@ +/** + * \file build_info.h + * + * \brief Build-time configuration info + * + * Include this file if you need to depend on the + * configuration options defined in mbedtls_config.h or MBEDTLS_CONFIG_FILE + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_BUILD_INFO_H +#define MBEDTLS_BUILD_INFO_H + +/* + * This set of compile-time defines can be used to determine the version number + * of the Mbed TLS library used. Run-time variables for the same can be found in + * version.h + */ + +/** + * The version number x.y.z is split into three parts. + * Major, Minor, Patchlevel + */ +#define MBEDTLS_VERSION_MAJOR 3 +#define MBEDTLS_VERSION_MINOR 4 +#define MBEDTLS_VERSION_PATCH 1 + +/** + * The single version number has the following structure: + * MMNNPP00 + * Major version | Minor version | Patch version + */ +#define MBEDTLS_VERSION_NUMBER 0x03040100 +#define MBEDTLS_VERSION_STRING "3.4.1" +#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 3.4.1" + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +/* Define `inline` on some non-C99-compliant compilers. */ +#if (defined(__ARMCC_VERSION) || defined(_MSC_VER)) && \ + !defined(inline) && !defined(__cplusplus) +#define inline __inline +#endif + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/mbedtls_config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_CONFIG_VERSION) && ( \ + MBEDTLS_CONFIG_VERSION < 0x03000000 || \ + MBEDTLS_CONFIG_VERSION > MBEDTLS_VERSION_NUMBER) +#error "Invalid config version, defined value of MBEDTLS_CONFIG_VERSION is unsupported" +#endif + +/* Target and application specific configurations + * + * Allow user to override any previous default. + * + */ +#if defined(MBEDTLS_USER_CONFIG_FILE) +#include MBEDTLS_USER_CONFIG_FILE +#endif + +/* Auto-enable MBEDTLS_MD_LIGHT based on MBEDTLS_MD_C. + * This allows checking for MD_LIGHT rather than MD_LIGHT || MD_C. + */ +#if defined(MBEDTLS_MD_C) +#define MBEDTLS_MD_LIGHT +#endif + +/* Auto-enable MBEDTLS_MD_LIGHT if some module needs it. + */ +#if defined(MBEDTLS_PEM_PARSE_C) || \ + defined(MBEDTLS_RSA_C) +#define MBEDTLS_MD_LIGHT +#endif + +/* If MBEDTLS_PSA_CRYPTO_C is defined, make sure MBEDTLS_PSA_CRYPTO_CLIENT + * is defined as well to include all PSA code. + */ +#if defined(MBEDTLS_PSA_CRYPTO_C) +#define MBEDTLS_PSA_CRYPTO_CLIENT +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +/* The PK wrappers need pk_write functions to format RSA key objects + * when they are dispatching to the PSA API. This happens under USE_PSA_CRYPTO, + * and also even without USE_PSA_CRYPTO for mbedtls_pk_sign_ext(). */ +#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_RSA_C) +#define MBEDTLS_PK_C +#define MBEDTLS_PK_WRITE_C +#define MBEDTLS_PK_PARSE_C +#endif + +#if !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#undef MBEDTLS_KEY_EXCHANGE_RSA_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED +#undef MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED +#endif + +#if !defined(MBEDTLS_SSL_PROTO_TLS1_3) +#undef MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED +#undef MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED +#undef MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED +#undef MBEDTLS_SSL_EARLY_DATA +#endif + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED) || \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED +#endif + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) || \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED +#endif + +/* Make sure all configuration symbols are set before including check_config.h, + * even the ones that are calculated programmatically. */ +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) /* PSA_WANT_xxx influences MBEDTLS_xxx */ || \ + defined(MBEDTLS_PSA_CRYPTO_C) /* MBEDTLS_xxx influences PSA_WANT_xxx */ +#include "mbedtls/config_psa.h" +#endif + +#include "mbedtls/check_config.h" + +#endif /* MBEDTLS_BUILD_INFO_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/camellia.h b/r5dev/thirdparty/mbedtls/include/mbedtls/camellia.h new file mode 100644 index 00000000..f4aa0029 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/camellia.h @@ -0,0 +1,315 @@ +/** + * \file camellia.h + * + * \brief Camellia block cipher + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_CAMELLIA_H +#define MBEDTLS_CAMELLIA_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +#include "mbedtls/platform_util.h" + +#define MBEDTLS_CAMELLIA_ENCRYPT 1 +#define MBEDTLS_CAMELLIA_DECRYPT 0 + +/** Bad input data. */ +#define MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA -0x0024 + +/** Invalid data input length. */ +#define MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH -0x0026 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_CAMELLIA_ALT) +// Regular implementation +// + +/** + * \brief CAMELLIA context structure + */ +typedef struct mbedtls_camellia_context { + int MBEDTLS_PRIVATE(nr); /*!< number of rounds */ + uint32_t MBEDTLS_PRIVATE(rk)[68]; /*!< CAMELLIA round keys */ +} +mbedtls_camellia_context; + +#else /* MBEDTLS_CAMELLIA_ALT */ +#include "camellia_alt.h" +#endif /* MBEDTLS_CAMELLIA_ALT */ + +/** + * \brief Initialize a CAMELLIA context. + * + * \param ctx The CAMELLIA context to be initialized. + * This must not be \c NULL. + */ +void mbedtls_camellia_init(mbedtls_camellia_context *ctx); + +/** + * \brief Clear a CAMELLIA context. + * + * \param ctx The CAMELLIA context to be cleared. This may be \c NULL, + * in which case this function returns immediately. If it is not + * \c NULL, it must be initialized. + */ +void mbedtls_camellia_free(mbedtls_camellia_context *ctx); + +/** + * \brief Perform a CAMELLIA key schedule operation for encryption. + * + * \param ctx The CAMELLIA context to use. This must be initialized. + * \param key The encryption key to use. This must be a readable buffer + * of size \p keybits Bits. + * \param keybits The length of \p key in Bits. This must be either \c 128, + * \c 192 or \c 256. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_camellia_setkey_enc(mbedtls_camellia_context *ctx, + const unsigned char *key, + unsigned int keybits); + +/** + * \brief Perform a CAMELLIA key schedule operation for decryption. + * + * \param ctx The CAMELLIA context to use. This must be initialized. + * \param key The decryption key. This must be a readable buffer + * of size \p keybits Bits. + * \param keybits The length of \p key in Bits. This must be either \c 128, + * \c 192 or \c 256. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_camellia_setkey_dec(mbedtls_camellia_context *ctx, + const unsigned char *key, + unsigned int keybits); + +/** + * \brief Perform a CAMELLIA-ECB block encryption/decryption operation. + * + * \param ctx The CAMELLIA context to use. This must be initialized + * and bound to a key. + * \param mode The mode of operation. This must be either + * #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT. + * \param input The input block. This must be a readable buffer + * of size \c 16 Bytes. + * \param output The output block. This must be a writable buffer + * of size \c 16 Bytes. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_camellia_crypt_ecb(mbedtls_camellia_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]); + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/** + * \brief Perform a CAMELLIA-CBC buffer encryption/decryption operation. + * + * \note Upon exit, the content of the IV is updated so that you can + * call the function same function again on the following + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If on the other hand you need to retain the contents of the + * IV, you should either save it manually or use the cipher + * module instead. + * + * \param ctx The CAMELLIA context to use. This must be initialized + * and bound to a key. + * \param mode The mode of operation. This must be either + * #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT. + * \param length The length in Bytes of the input data \p input. + * This must be a multiple of \c 16 Bytes. + * \param iv The initialization vector. This must be a read/write buffer + * of length \c 16 Bytes. It is updated to allow streaming + * use as explained above. + * \param input The buffer holding the input data. This must point to a + * readable buffer of length \p length Bytes. + * \param output The buffer holding the output data. This must point to a + * writable buffer of length \p length Bytes. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_camellia_crypt_cbc(mbedtls_camellia_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +/** + * \brief Perform a CAMELLIA-CFB128 buffer encryption/decryption + * operation. + * + * \note Due to the nature of CFB mode, you should use the same + * key for both encryption and decryption. In particular, calls + * to this function should be preceded by a key-schedule via + * mbedtls_camellia_setkey_enc() regardless of whether \p mode + * is #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT. + * + * \note Upon exit, the content of the IV is updated so that you can + * call the function same function again on the following + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If on the other hand you need to retain the contents of the + * IV, you should either save it manually or use the cipher + * module instead. + * + * \param ctx The CAMELLIA context to use. This must be initialized + * and bound to a key. + * \param mode The mode of operation. This must be either + * #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT. + * \param length The length of the input data \p input. Any value is allowed. + * \param iv_off The current offset in the IV. This must be smaller + * than \c 16 Bytes. It is updated after this call to allow + * the aforementioned streaming usage. + * \param iv The initialization vector. This must be a read/write buffer + * of length \c 16 Bytes. It is updated after this call to + * allow the aforementioned streaming usage. + * \param input The buffer holding the input data. This must be a readable + * buffer of size \p length Bytes. + * \param output The buffer to hold the output data. This must be a writable + * buffer of length \p length Bytes. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_camellia_crypt_cfb128(mbedtls_camellia_context *ctx, + int mode, + size_t length, + size_t *iv_off, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CFB */ + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +/** + * \brief Perform a CAMELLIA-CTR buffer encryption/decryption operation. + * + * *note Due to the nature of CTR mode, you should use the same + * key for both encryption and decryption. In particular, calls + * to this function should be preceded by a key-schedule via + * mbedtls_camellia_setkey_enc() regardless of whether \p mode + * is #MBEDTLS_CAMELLIA_ENCRYPT or #MBEDTLS_CAMELLIA_DECRYPT. + * + * \warning You must never reuse a nonce value with the same key. Doing so + * would void the encryption for the two messages encrypted with + * the same nonce and key. + * + * There are two common strategies for managing nonces with CTR: + * + * 1. You can handle everything as a single message processed over + * successive calls to this function. In that case, you want to + * set \p nonce_counter and \p nc_off to 0 for the first call, and + * then preserve the values of \p nonce_counter, \p nc_off and \p + * stream_block across calls to this function as they will be + * updated by this function. + * + * With this strategy, you must not encrypt more than 2**128 + * blocks of data with the same key. + * + * 2. You can encrypt separate messages by dividing the \p + * nonce_counter buffer in two areas: the first one used for a + * per-message nonce, handled by yourself, and the second one + * updated by this function internally. + * + * For example, you might reserve the first \c 12 Bytes for the + * per-message nonce, and the last \c 4 Bytes for internal use. + * In that case, before calling this function on a new message you + * need to set the first \c 12 Bytes of \p nonce_counter to your + * chosen nonce value, the last four to \c 0, and \p nc_off to \c 0 + * (which will cause \p stream_block to be ignored). That way, you + * can encrypt at most \c 2**96 messages of up to \c 2**32 blocks + * each with the same key. + * + * The per-message nonce (or information sufficient to reconstruct + * it) needs to be communicated with the ciphertext and must be + * unique. The recommended way to ensure uniqueness is to use a + * message counter. An alternative is to generate random nonces, + * but this limits the number of messages that can be securely + * encrypted: for example, with 96-bit random nonces, you should + * not encrypt more than 2**32 messages with the same key. + * + * Note that for both strategies, sizes are measured in blocks and + * that a CAMELLIA block is \c 16 Bytes. + * + * \warning Upon return, \p stream_block contains sensitive data. Its + * content must not be written to insecure storage and should be + * securely discarded as soon as it's no longer needed. + * + * \param ctx The CAMELLIA context to use. This must be initialized + * and bound to a key. + * \param length The length of the input data \p input in Bytes. + * Any value is allowed. + * \param nc_off The offset in the current \p stream_block (for resuming + * within current cipher stream). The offset pointer to + * should be \c 0 at the start of a stream. It is updated + * at the end of this call. + * \param nonce_counter The 128-bit nonce and counter. This must be a read/write + * buffer of length \c 16 Bytes. + * \param stream_block The saved stream-block for resuming. This must be a + * read/write buffer of length \c 16 Bytes. + * \param input The input data stream. This must be a readable buffer of + * size \p length Bytes. + * \param output The output data stream. This must be a writable buffer + * of size \p length Bytes. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_camellia_crypt_ctr(mbedtls_camellia_context *ctx, + size_t length, + size_t *nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CTR */ + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_camellia_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* camellia.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ccm.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ccm.h new file mode 100644 index 00000000..6c225528 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ccm.h @@ -0,0 +1,531 @@ +/** + * \file ccm.h + * + * \brief This file provides an API for the CCM authenticated encryption + * mode for block ciphers. + * + * CCM combines Counter mode encryption with CBC-MAC authentication + * for 128-bit block ciphers. + * + * Input to CCM includes the following elements: + *
  • Payload - data that is both authenticated and encrypted.
  • + *
  • Associated data (Adata) - data that is authenticated but not + * encrypted, For example, a header.
  • + *
  • Nonce - A unique value that is assigned to the payload and the + * associated data.
+ * + * Definition of CCM: + * http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf + * RFC 3610 "Counter with CBC-MAC (CCM)" + * + * Related: + * RFC 5116 "An Interface and Algorithms for Authenticated Encryption" + * + * Definition of CCM*: + * IEEE 802.15.4 - IEEE Standard for Local and metropolitan area networks + * Integer representation is fixed most-significant-octet-first order and + * the representation of octets is most-significant-bit-first order. This is + * consistent with RFC 3610. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CCM_H +#define MBEDTLS_CCM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/cipher.h" + +#define MBEDTLS_CCM_DECRYPT 0 +#define MBEDTLS_CCM_ENCRYPT 1 +#define MBEDTLS_CCM_STAR_DECRYPT 2 +#define MBEDTLS_CCM_STAR_ENCRYPT 3 + +/** Bad input parameters to the function. */ +#define MBEDTLS_ERR_CCM_BAD_INPUT -0x000D +/** Authenticated decryption failed. */ +#define MBEDTLS_ERR_CCM_AUTH_FAILED -0x000F + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_CCM_ALT) +// Regular implementation +// + +/** + * \brief The CCM context-type definition. The CCM context is passed + * to the APIs called. + */ +typedef struct mbedtls_ccm_context { + unsigned char MBEDTLS_PRIVATE(y)[16]; /*!< The Y working buffer */ + unsigned char MBEDTLS_PRIVATE(ctr)[16]; /*!< The counter buffer */ + mbedtls_cipher_context_t MBEDTLS_PRIVATE(cipher_ctx); /*!< The cipher context used. */ + size_t MBEDTLS_PRIVATE(plaintext_len); /*!< Total plaintext length */ + size_t MBEDTLS_PRIVATE(add_len); /*!< Total authentication data length */ + size_t MBEDTLS_PRIVATE(tag_len); /*!< Total tag length */ + size_t MBEDTLS_PRIVATE(processed); /*!< Track how many bytes of input data + were processed (chunked input). + Used independently for both auth data + and plaintext/ciphertext. + This variable is set to zero after + auth data input is finished. */ + unsigned char MBEDTLS_PRIVATE(q); /*!< The Q working value */ + unsigned char MBEDTLS_PRIVATE(mode); /*!< The operation to perform: + #MBEDTLS_CCM_ENCRYPT or + #MBEDTLS_CCM_DECRYPT or + #MBEDTLS_CCM_STAR_ENCRYPT or + #MBEDTLS_CCM_STAR_DECRYPT. */ + int MBEDTLS_PRIVATE(state); /*!< Working value holding context's + state. Used for chunked data + input */ +} +mbedtls_ccm_context; + +#else /* MBEDTLS_CCM_ALT */ +#include "ccm_alt.h" +#endif /* MBEDTLS_CCM_ALT */ + +/** + * \brief This function initializes the specified CCM context, + * to make references valid, and prepare the context + * for mbedtls_ccm_setkey() or mbedtls_ccm_free(). + * + * \param ctx The CCM context to initialize. This must not be \c NULL. + */ +void mbedtls_ccm_init(mbedtls_ccm_context *ctx); + +/** + * \brief This function initializes the CCM context set in the + * \p ctx parameter and sets the encryption key. + * + * \param ctx The CCM context to initialize. This must be an initialized + * context. + * \param cipher The 128-bit block cipher to use. + * \param key The encryption key. This must not be \c NULL. + * \param keybits The key size in bits. This must be acceptable by the cipher. + * + * \return \c 0 on success. + * \return A CCM or cipher-specific error code on failure. + */ +int mbedtls_ccm_setkey(mbedtls_ccm_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits); + +/** + * \brief This function releases and clears the specified CCM context + * and underlying cipher sub-context. + * + * \param ctx The CCM context to clear. If this is \c NULL, the function + * has no effect. Otherwise, this must be initialized. + */ +void mbedtls_ccm_free(mbedtls_ccm_context *ctx); + +/** + * \brief This function encrypts a buffer using CCM. + * + * \note The tag is written to a separate buffer. To concatenate + * the \p tag with the \p output, as done in RFC-3610: + * Counter with CBC-MAC (CCM), use + * \p tag = \p output + \p length, and make sure that the + * output buffer is at least \p length + \p tag_len wide. + * + * \param ctx The CCM context to use for encryption. This must be + * initialized and bound to a key. + * \param length The length of the input data in Bytes. + * \param iv The initialization vector (nonce). This must be a readable + * buffer of at least \p iv_len Bytes. + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. + * \param ad The additional data field. If \p ad_len is greater than + * zero, \p ad must be a readable buffer of at least that + * length. + * \param ad_len The length of additional data in Bytes. + * This must be less than `2^16 - 2^8`. + * \param input The buffer holding the input data. If \p length is greater + * than zero, \p input must be a readable buffer of at least + * that length. + * \param output The buffer holding the output data. If \p length is greater + * than zero, \p output must be a writable buffer of at least + * that length. + * \param tag The buffer holding the authentication field. This must be a + * writable buffer of at least \p tag_len Bytes. + * \param tag_len The length of the authentication field to generate in Bytes: + * 4, 6, 8, 10, 12, 14 or 16. + * + * \return \c 0 on success. + * \return A CCM or cipher-specific error code on failure. + */ +int mbedtls_ccm_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len); + +/** + * \brief This function encrypts a buffer using CCM*. + * + * \note The tag is written to a separate buffer. To concatenate + * the \p tag with the \p output, as done in RFC-3610: + * Counter with CBC-MAC (CCM), use + * \p tag = \p output + \p length, and make sure that the + * output buffer is at least \p length + \p tag_len wide. + * + * \note When using this function in a variable tag length context, + * the tag length has to be encoded into the \p iv passed to + * this function. + * + * \param ctx The CCM context to use for encryption. This must be + * initialized and bound to a key. + * \param length The length of the input data in Bytes. + * For tag length = 0, input length is ignored. + * \param iv The initialization vector (nonce). This must be a readable + * buffer of at least \p iv_len Bytes. + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. + * \param ad The additional data field. This must be a readable buffer of + * at least \p ad_len Bytes. + * \param ad_len The length of additional data in Bytes. + * This must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. If \p length is greater + * than zero, \p input must be a readable buffer of at least + * that length. + * \param output The buffer holding the output data. If \p length is greater + * than zero, \p output must be a writable buffer of at least + * that length. + * \param tag The buffer holding the authentication field. This must be a + * writable buffer of at least \p tag_len Bytes. + * \param tag_len The length of the authentication field to generate in Bytes: + * 0, 4, 6, 8, 10, 12, 14 or 16. + * + * \warning Passing \c 0 as \p tag_len means that the message is no + * longer authenticated. + * + * \return \c 0 on success. + * \return A CCM or cipher-specific error code on failure. + */ +int mbedtls_ccm_star_encrypt_and_tag(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, unsigned char *output, + unsigned char *tag, size_t tag_len); + +/** + * \brief This function performs a CCM authenticated decryption of a + * buffer. + * + * \param ctx The CCM context to use for decryption. This must be + * initialized and bound to a key. + * \param length The length of the input data in Bytes. + * \param iv The initialization vector (nonce). This must be a readable + * buffer of at least \p iv_len Bytes. + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. + * \param ad The additional data field. This must be a readable buffer + * of at least that \p ad_len Bytes.. + * \param ad_len The length of additional data in Bytes. + * This must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. If \p length is greater + * than zero, \p input must be a readable buffer of at least + * that length. + * \param output The buffer holding the output data. If \p length is greater + * than zero, \p output must be a writable buffer of at least + * that length. + * \param tag The buffer holding the authentication field. This must be a + * readable buffer of at least \p tag_len Bytes. + * \param tag_len The length of the authentication field to generate in Bytes: + * 4, 6, 8, 10, 12, 14 or 16. + * + * \return \c 0 on success. This indicates that the message is authentic. + * \return #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match. + * \return A cipher-specific error code on calculation failure. + */ +int mbedtls_ccm_auth_decrypt(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len); + +/** + * \brief This function performs a CCM* authenticated decryption of a + * buffer. + * + * \note When using this function in a variable tag length context, + * the tag length has to be decoded from \p iv and passed to + * this function as \p tag_len. (\p tag needs to be adjusted + * accordingly.) + * + * \param ctx The CCM context to use for decryption. This must be + * initialized and bound to a key. + * \param length The length of the input data in Bytes. + * For tag length = 0, input length is ignored. + * \param iv The initialization vector (nonce). This must be a readable + * buffer of at least \p iv_len Bytes. + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. + * \param ad The additional data field. This must be a readable buffer of + * at least that \p ad_len Bytes. + * \param ad_len The length of additional data in Bytes. + * This must be less than 2^16 - 2^8. + * \param input The buffer holding the input data. If \p length is greater + * than zero, \p input must be a readable buffer of at least + * that length. + * \param output The buffer holding the output data. If \p length is greater + * than zero, \p output must be a writable buffer of at least + * that length. + * \param tag The buffer holding the authentication field. This must be a + * readable buffer of at least \p tag_len Bytes. + * \param tag_len The length of the authentication field in Bytes. + * 0, 4, 6, 8, 10, 12, 14 or 16. + * + * \warning Passing \c 0 as \p tag_len means that the message is nos + * longer authenticated. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match. + * \return A cipher-specific error code on calculation failure. + */ +int mbedtls_ccm_star_auth_decrypt(mbedtls_ccm_context *ctx, size_t length, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, unsigned char *output, + const unsigned char *tag, size_t tag_len); + +/** + * \brief This function starts a CCM encryption or decryption + * operation. + * + * This function and mbedtls_ccm_set_lengths() must be called + * before calling mbedtls_ccm_update_ad() or + * mbedtls_ccm_update(). This function can be called before + * or after mbedtls_ccm_set_lengths(). + * + * \note This function is not implemented in Mbed TLS yet. + * + * \param ctx The CCM context. This must be initialized. + * \param mode The operation to perform: #MBEDTLS_CCM_ENCRYPT or + * #MBEDTLS_CCM_DECRYPT or #MBEDTLS_CCM_STAR_ENCRYPT or + * #MBEDTLS_CCM_STAR_DECRYPT. + * \param iv The initialization vector. This must be a readable buffer + * of at least \p iv_len Bytes. + * \param iv_len The length of the nonce in Bytes: 7, 8, 9, 10, 11, 12, + * or 13. The length L of the message length field is + * 15 - \p iv_len. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CCM_BAD_INPUT on failure: + * \p ctx is in an invalid state, + * \p mode is invalid, + * \p iv_len is invalid (lower than \c 7 or greater than + * \c 13). + */ +int mbedtls_ccm_starts(mbedtls_ccm_context *ctx, + int mode, + const unsigned char *iv, + size_t iv_len); + +/** + * \brief This function declares the lengths of the message + * and additional data for a CCM encryption or decryption + * operation. + * + * This function and mbedtls_ccm_starts() must be called + * before calling mbedtls_ccm_update_ad() or + * mbedtls_ccm_update(). This function can be called before + * or after mbedtls_ccm_starts(). + * + * \note This function is not implemented in Mbed TLS yet. + * + * \param ctx The CCM context. This must be initialized. + * \param total_ad_len The total length of additional data in bytes. + * This must be less than `2^16 - 2^8`. + * \param plaintext_len The length in bytes of the plaintext to encrypt or + * result of the decryption (thus not encompassing the + * additional data that are not encrypted). + * \param tag_len The length of the tag to generate in Bytes: + * 4, 6, 8, 10, 12, 14 or 16. + * For CCM*, zero is also valid. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CCM_BAD_INPUT on failure: + * \p ctx is in an invalid state, + * \p total_ad_len is greater than \c 0xFF00. + */ +int mbedtls_ccm_set_lengths(mbedtls_ccm_context *ctx, + size_t total_ad_len, + size_t plaintext_len, + size_t tag_len); + +/** + * \brief This function feeds an input buffer as associated data + * (authenticated but not encrypted data) in a CCM + * encryption or decryption operation. + * + * You may call this function zero, one or more times + * to pass successive parts of the additional data. The + * lengths \p ad_len of the data parts should eventually add + * up exactly to the total length of additional data + * \c total_ad_len passed to mbedtls_ccm_set_lengths(). You + * may not call this function after calling + * mbedtls_ccm_update(). + * + * \note This function is not implemented in Mbed TLS yet. + * + * \param ctx The CCM context. This must have been started with + * mbedtls_ccm_starts(), the lengths of the message and + * additional data must have been declared with + * mbedtls_ccm_set_lengths() and this must not have yet + * received any input with mbedtls_ccm_update(). + * \param ad The buffer holding the additional data, or \c NULL + * if \p ad_len is \c 0. + * \param ad_len The length of the additional data. If \c 0, + * \p ad may be \c NULL. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CCM_BAD_INPUT on failure: + * \p ctx is in an invalid state, + * total input length too long. + */ +int mbedtls_ccm_update_ad(mbedtls_ccm_context *ctx, + const unsigned char *ad, + size_t ad_len); + +/** + * \brief This function feeds an input buffer into an ongoing CCM + * encryption or decryption operation. + * + * You may call this function zero, one or more times + * to pass successive parts of the input: the plaintext to + * encrypt, or the ciphertext (not including the tag) to + * decrypt. After the last part of the input, call + * mbedtls_ccm_finish(). The lengths \p input_len of the + * data parts should eventually add up exactly to the + * plaintext length \c plaintext_len passed to + * mbedtls_ccm_set_lengths(). + * + * This function may produce output in one of the following + * ways: + * - Immediate output: the output length is always equal + * to the input length. + * - Buffered output: except for the last part of input data, + * the output consists of a whole number of 16-byte blocks. + * If the total input length so far (not including + * associated data) is 16 \* *B* + *A* with *A* < 16 then + * the total output length is 16 \* *B*. + * For the last part of input data, the output length is + * equal to the input length plus the number of bytes (*A*) + * buffered in the previous call to the function (if any). + * The function uses the plaintext length + * \c plaintext_len passed to mbedtls_ccm_set_lengths() + * to detect the last part of input data. + * + * In particular: + * - It is always correct to call this function with + * \p output_size >= \p input_len + 15. + * - If \p input_len is a multiple of 16 for all the calls + * to this function during an operation (not necessary for + * the last one) then it is correct to use \p output_size + * =\p input_len. + * + * \note This function is not implemented in Mbed TLS yet. + * + * \param ctx The CCM context. This must have been started with + * mbedtls_ccm_starts() and the lengths of the message and + * additional data must have been declared with + * mbedtls_ccm_set_lengths(). + * \param input The buffer holding the input data. If \p input_len + * is greater than zero, this must be a readable buffer + * of at least \p input_len bytes. + * \param input_len The length of the input data in bytes. + * \param output The buffer for the output data. If \p output_size + * is greater than zero, this must be a writable buffer of + * at least \p output_size bytes. + * \param output_size The size of the output buffer in bytes. + * See the function description regarding the output size. + * \param output_len On success, \p *output_len contains the actual + * length of the output written in \p output. + * On failure, the content of \p *output_len is + * unspecified. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CCM_BAD_INPUT on failure: + * \p ctx is in an invalid state, + * total input length too long, + * or \p output_size too small. + */ +int mbedtls_ccm_update(mbedtls_ccm_context *ctx, + const unsigned char *input, size_t input_len, + unsigned char *output, size_t output_size, + size_t *output_len); + +/** + * \brief This function finishes the CCM operation and generates + * the authentication tag. + * + * It wraps up the CCM stream, and generates the + * tag. The tag can have a maximum length of 16 Bytes. + * + * \note This function is not implemented in Mbed TLS yet. + * + * \param ctx The CCM context. This must have been started with + * mbedtls_ccm_starts() and the lengths of the message and + * additional data must have been declared with + * mbedtls_ccm_set_lengths(). + * \param tag The buffer for holding the tag. If \p tag_len is greater + * than zero, this must be a writable buffer of at least \p + * tag_len Bytes. + * \param tag_len The length of the tag. Must match the tag length passed to + * mbedtls_ccm_set_lengths() function. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CCM_BAD_INPUT on failure: + * \p ctx is in an invalid state, + * invalid value of \p tag_len, + * the total amount of additional data passed to + * mbedtls_ccm_update_ad() was lower than the total length of + * additional data \c total_ad_len passed to + * mbedtls_ccm_set_lengths(), + * the total amount of input data passed to + * mbedtls_ccm_update() was lower than the plaintext length + * \c plaintext_len passed to mbedtls_ccm_set_lengths(). + */ +int mbedtls_ccm_finish(mbedtls_ccm_context *ctx, + unsigned char *tag, size_t tag_len); + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) +/** + * \brief The CCM checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_ccm_self_test(int verbose); +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CCM_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/chacha20.h b/r5dev/thirdparty/mbedtls/include/mbedtls/chacha20.h new file mode 100644 index 00000000..e24e56b9 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/chacha20.h @@ -0,0 +1,214 @@ +/** + * \file chacha20.h + * + * \brief This file contains ChaCha20 definitions and functions. + * + * ChaCha20 is a stream cipher that can encrypt and decrypt + * information. ChaCha was created by Daniel Bernstein as a variant of + * its Salsa cipher https://cr.yp.to/chacha/chacha-20080128.pdf + * ChaCha20 is the variant with 20 rounds, that was also standardized + * in RFC 7539. + * + * \author Daniel King + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CHACHA20_H +#define MBEDTLS_CHACHA20_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +/** Invalid input parameter(s). */ +#define MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA -0x0051 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_CHACHA20_ALT) + +typedef struct mbedtls_chacha20_context { + uint32_t MBEDTLS_PRIVATE(state)[16]; /*! The state (before round operations). */ + uint8_t MBEDTLS_PRIVATE(keystream8)[64]; /*! Leftover keystream bytes. */ + size_t MBEDTLS_PRIVATE(keystream_bytes_used); /*! Number of keystream bytes already used. */ +} +mbedtls_chacha20_context; + +#else /* MBEDTLS_CHACHA20_ALT */ +#include "chacha20_alt.h" +#endif /* MBEDTLS_CHACHA20_ALT */ + +/** + * \brief This function initializes the specified ChaCha20 context. + * + * It must be the first API called before using + * the context. + * + * It is usually followed by calls to + * \c mbedtls_chacha20_setkey() and + * \c mbedtls_chacha20_starts(), then one or more calls to + * to \c mbedtls_chacha20_update(), and finally to + * \c mbedtls_chacha20_free(). + * + * \param ctx The ChaCha20 context to initialize. + * This must not be \c NULL. + */ +void mbedtls_chacha20_init(mbedtls_chacha20_context *ctx); + +/** + * \brief This function releases and clears the specified + * ChaCha20 context. + * + * \param ctx The ChaCha20 context to clear. This may be \c NULL, + * in which case this function is a no-op. If it is not + * \c NULL, it must point to an initialized context. + * + */ +void mbedtls_chacha20_free(mbedtls_chacha20_context *ctx); + +/** + * \brief This function sets the encryption/decryption key. + * + * \note After using this function, you must also call + * \c mbedtls_chacha20_starts() to set a nonce before you + * start encrypting/decrypting data with + * \c mbedtls_chacha_update(). + * + * \param ctx The ChaCha20 context to which the key should be bound. + * It must be initialized. + * \param key The encryption/decryption key. This must be \c 32 Bytes + * in length. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if ctx or key is NULL. + */ +int mbedtls_chacha20_setkey(mbedtls_chacha20_context *ctx, + const unsigned char key[32]); + +/** + * \brief This function sets the nonce and initial counter value. + * + * \note A ChaCha20 context can be re-used with the same key by + * calling this function to change the nonce. + * + * \warning You must never use the same nonce twice with the same key. + * This would void any confidentiality guarantees for the + * messages encrypted with the same nonce and key. + * + * \param ctx The ChaCha20 context to which the nonce should be bound. + * It must be initialized and bound to a key. + * \param nonce The nonce. This must be \c 12 Bytes in size. + * \param counter The initial counter value. This is usually \c 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA if ctx or nonce is + * NULL. + */ +int mbedtls_chacha20_starts(mbedtls_chacha20_context *ctx, + const unsigned char nonce[12], + uint32_t counter); + +/** + * \brief This function encrypts or decrypts data. + * + * Since ChaCha20 is a stream cipher, the same operation is + * used for encrypting and decrypting data. + * + * \note The \p input and \p output pointers must either be equal or + * point to non-overlapping buffers. + * + * \note \c mbedtls_chacha20_setkey() and + * \c mbedtls_chacha20_starts() must be called at least once + * to setup the context before this function can be called. + * + * \note This function can be called multiple times in a row in + * order to encrypt of decrypt data piecewise with the same + * key and nonce. + * + * \param ctx The ChaCha20 context to use for encryption or decryption. + * It must be initialized and bound to a key and nonce. + * \param size The length of the input data in Bytes. + * \param input The buffer holding the input data. + * This pointer can be \c NULL if `size == 0`. + * \param output The buffer holding the output data. + * This must be able to hold \p size Bytes. + * This pointer can be \c NULL if `size == 0`. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_chacha20_update(mbedtls_chacha20_context *ctx, + size_t size, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function encrypts or decrypts data with ChaCha20 and + * the given key and nonce. + * + * Since ChaCha20 is a stream cipher, the same operation is + * used for encrypting and decrypting data. + * + * \warning You must never use the same (key, nonce) pair more than + * once. This would void any confidentiality guarantees for + * the messages encrypted with the same nonce and key. + * + * \note The \p input and \p output pointers must either be equal or + * point to non-overlapping buffers. + * + * \param key The encryption/decryption key. + * This must be \c 32 Bytes in length. + * \param nonce The nonce. This must be \c 12 Bytes in size. + * \param counter The initial counter value. This is usually \c 0. + * \param size The length of the input data in Bytes. + * \param input The buffer holding the input data. + * This pointer can be \c NULL if `size == 0`. + * \param output The buffer holding the output data. + * This must be able to hold \p size Bytes. + * This pointer can be \c NULL if `size == 0`. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_chacha20_crypt(const unsigned char key[32], + const unsigned char nonce[12], + uint32_t counter, + size_t size, + const unsigned char *input, + unsigned char *output); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The ChaCha20 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_chacha20_self_test(int verbose); +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CHACHA20_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/chachapoly.h b/r5dev/thirdparty/mbedtls/include/mbedtls/chachapoly.h new file mode 100644 index 00000000..19baadef --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/chachapoly.h @@ -0,0 +1,354 @@ +/** + * \file chachapoly.h + * + * \brief This file contains the AEAD-ChaCha20-Poly1305 definitions and + * functions. + * + * ChaCha20-Poly1305 is an algorithm for Authenticated Encryption + * with Associated Data (AEAD) that can be used to encrypt and + * authenticate data. It is based on ChaCha20 and Poly1305 by Daniel + * Bernstein and was standardized in RFC 7539. + * + * \author Daniel King + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CHACHAPOLY_H +#define MBEDTLS_CHACHAPOLY_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +/* for shared error codes */ +#include "mbedtls/poly1305.h" + +/** The requested operation is not permitted in the current state. */ +#define MBEDTLS_ERR_CHACHAPOLY_BAD_STATE -0x0054 +/** Authenticated decryption failed: data was not authentic. */ +#define MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED -0x0056 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MBEDTLS_CHACHAPOLY_ENCRYPT, /**< The mode value for performing encryption. */ + MBEDTLS_CHACHAPOLY_DECRYPT /**< The mode value for performing decryption. */ +} +mbedtls_chachapoly_mode_t; + +#if !defined(MBEDTLS_CHACHAPOLY_ALT) + +#include "mbedtls/chacha20.h" + +typedef struct mbedtls_chachapoly_context { + mbedtls_chacha20_context MBEDTLS_PRIVATE(chacha20_ctx); /**< The ChaCha20 context. */ + mbedtls_poly1305_context MBEDTLS_PRIVATE(poly1305_ctx); /**< The Poly1305 context. */ + uint64_t MBEDTLS_PRIVATE(aad_len); /**< The length (bytes) of the Additional Authenticated Data. */ + uint64_t MBEDTLS_PRIVATE(ciphertext_len); /**< The length (bytes) of the ciphertext. */ + int MBEDTLS_PRIVATE(state); /**< The current state of the context. */ + mbedtls_chachapoly_mode_t MBEDTLS_PRIVATE(mode); /**< Cipher mode (encrypt or decrypt). */ +} +mbedtls_chachapoly_context; + +#else /* !MBEDTLS_CHACHAPOLY_ALT */ +#include "chachapoly_alt.h" +#endif /* !MBEDTLS_CHACHAPOLY_ALT */ + +/** + * \brief This function initializes the specified ChaCha20-Poly1305 context. + * + * It must be the first API called before using + * the context. It must be followed by a call to + * \c mbedtls_chachapoly_setkey() before any operation can be + * done, and to \c mbedtls_chachapoly_free() once all + * operations with that context have been finished. + * + * In order to encrypt or decrypt full messages at once, for + * each message you should make a single call to + * \c mbedtls_chachapoly_crypt_and_tag() or + * \c mbedtls_chachapoly_auth_decrypt(). + * + * In order to encrypt messages piecewise, for each + * message you should make a call to + * \c mbedtls_chachapoly_starts(), then 0 or more calls to + * \c mbedtls_chachapoly_update_aad(), then 0 or more calls to + * \c mbedtls_chachapoly_update(), then one call to + * \c mbedtls_chachapoly_finish(). + * + * \warning Decryption with the piecewise API is discouraged! Always + * use \c mbedtls_chachapoly_auth_decrypt() when possible! + * + * If however this is not possible because the data is too + * large to fit in memory, you need to: + * + * - call \c mbedtls_chachapoly_starts() and (if needed) + * \c mbedtls_chachapoly_update_aad() as above, + * - call \c mbedtls_chachapoly_update() multiple times and + * ensure its output (the plaintext) is NOT used in any other + * way than placing it in temporary storage at this point, + * - call \c mbedtls_chachapoly_finish() to compute the + * authentication tag and compared it in constant time to the + * tag received with the ciphertext. + * + * If the tags are not equal, you must immediately discard + * all previous outputs of \c mbedtls_chachapoly_update(), + * otherwise you can now safely use the plaintext. + * + * \param ctx The ChachaPoly context to initialize. Must not be \c NULL. + */ +void mbedtls_chachapoly_init(mbedtls_chachapoly_context *ctx); + +/** + * \brief This function releases and clears the specified + * ChaCha20-Poly1305 context. + * + * \param ctx The ChachaPoly context to clear. This may be \c NULL, in which + * case this function is a no-op. + */ +void mbedtls_chachapoly_free(mbedtls_chachapoly_context *ctx); + +/** + * \brief This function sets the ChaCha20-Poly1305 + * symmetric encryption key. + * + * \param ctx The ChaCha20-Poly1305 context to which the key should be + * bound. This must be initialized. + * \param key The \c 256 Bit (\c 32 Bytes) key. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_chachapoly_setkey(mbedtls_chachapoly_context *ctx, + const unsigned char key[32]); + +/** + * \brief This function starts a ChaCha20-Poly1305 encryption or + * decryption operation. + * + * \warning You must never use the same nonce twice with the same key. + * This would void any confidentiality and authenticity + * guarantees for the messages encrypted with the same nonce + * and key. + * + * \note If the context is being used for AAD only (no data to + * encrypt or decrypt) then \p mode can be set to any value. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \param ctx The ChaCha20-Poly1305 context. This must be initialized + * and bound to a key. + * \param nonce The nonce/IV to use for the message. + * This must be a readable buffer of length \c 12 Bytes. + * \param mode The operation to perform: #MBEDTLS_CHACHAPOLY_ENCRYPT or + * #MBEDTLS_CHACHAPOLY_DECRYPT (discouraged, see warning). + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_chachapoly_starts(mbedtls_chachapoly_context *ctx, + const unsigned char nonce[12], + mbedtls_chachapoly_mode_t mode); + +/** + * \brief This function feeds additional data to be authenticated + * into an ongoing ChaCha20-Poly1305 operation. + * + * The Additional Authenticated Data (AAD), also called + * Associated Data (AD) is only authenticated but not + * encrypted nor included in the encrypted output. It is + * usually transmitted separately from the ciphertext or + * computed locally by each party. + * + * \note This function is called before data is encrypted/decrypted. + * I.e. call this function to process the AAD before calling + * \c mbedtls_chachapoly_update(). + * + * You may call this function multiple times to process + * an arbitrary amount of AAD. It is permitted to call + * this function 0 times, if no AAD is used. + * + * This function cannot be called any more if data has + * been processed by \c mbedtls_chachapoly_update(), + * or if the context has been finished. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \param ctx The ChaCha20-Poly1305 context. This must be initialized + * and bound to a key. + * \param aad_len The length in Bytes of the AAD. The length has no + * restrictions. + * \param aad Buffer containing the AAD. + * This pointer can be \c NULL if `aad_len == 0`. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA + * if \p ctx or \p aad are NULL. + * \return #MBEDTLS_ERR_CHACHAPOLY_BAD_STATE + * if the operations has not been started or has been + * finished, or if the AAD has been finished. + */ +int mbedtls_chachapoly_update_aad(mbedtls_chachapoly_context *ctx, + const unsigned char *aad, + size_t aad_len); + +/** + * \brief Thus function feeds data to be encrypted or decrypted + * into an on-going ChaCha20-Poly1305 + * operation. + * + * The direction (encryption or decryption) depends on the + * mode that was given when calling + * \c mbedtls_chachapoly_starts(). + * + * You may call this function multiple times to process + * an arbitrary amount of data. It is permitted to call + * this function 0 times, if no data is to be encrypted + * or decrypted. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \param ctx The ChaCha20-Poly1305 context to use. This must be initialized. + * \param len The length (in bytes) of the data to encrypt or decrypt. + * \param input The buffer containing the data to encrypt or decrypt. + * This pointer can be \c NULL if `len == 0`. + * \param output The buffer to where the encrypted or decrypted data is + * written. This must be able to hold \p len bytes. + * This pointer can be \c NULL if `len == 0`. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHAPOLY_BAD_STATE + * if the operation has not been started or has been + * finished. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_chachapoly_update(mbedtls_chachapoly_context *ctx, + size_t len, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function finished the ChaCha20-Poly1305 operation and + * generates the MAC (authentication tag). + * + * \param ctx The ChaCha20-Poly1305 context to use. This must be initialized. + * \param mac The buffer to where the 128-bit (16 bytes) MAC is written. + * + * \warning Decryption with the piecewise API is discouraged, see the + * warning on \c mbedtls_chachapoly_init(). + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHAPOLY_BAD_STATE + * if the operation has not been started or has been + * finished. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_chachapoly_finish(mbedtls_chachapoly_context *ctx, + unsigned char mac[16]); + +/** + * \brief This function performs a complete ChaCha20-Poly1305 + * authenticated encryption with the previously-set key. + * + * \note Before using this function, you must set the key with + * \c mbedtls_chachapoly_setkey(). + * + * \warning You must never use the same nonce twice with the same key. + * This would void any confidentiality and authenticity + * guarantees for the messages encrypted with the same nonce + * and key. + * + * \param ctx The ChaCha20-Poly1305 context to use (holds the key). + * This must be initialized. + * \param length The length (in bytes) of the data to encrypt or decrypt. + * \param nonce The 96-bit (12 bytes) nonce/IV to use. + * \param aad The buffer containing the additional authenticated + * data (AAD). This pointer can be \c NULL if `aad_len == 0`. + * \param aad_len The length (in bytes) of the AAD data to process. + * \param input The buffer containing the data to encrypt or decrypt. + * This pointer can be \c NULL if `ilen == 0`. + * \param output The buffer to where the encrypted or decrypted data + * is written. This pointer can be \c NULL if `ilen == 0`. + * \param tag The buffer to where the computed 128-bit (16 bytes) MAC + * is written. This must not be \c NULL. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_chachapoly_encrypt_and_tag(mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char *input, + unsigned char *output, + unsigned char tag[16]); + +/** + * \brief This function performs a complete ChaCha20-Poly1305 + * authenticated decryption with the previously-set key. + * + * \note Before using this function, you must set the key with + * \c mbedtls_chachapoly_setkey(). + * + * \param ctx The ChaCha20-Poly1305 context to use (holds the key). + * \param length The length (in Bytes) of the data to decrypt. + * \param nonce The \c 96 Bit (\c 12 bytes) nonce/IV to use. + * \param aad The buffer containing the additional authenticated data (AAD). + * This pointer can be \c NULL if `aad_len == 0`. + * \param aad_len The length (in bytes) of the AAD data to process. + * \param tag The buffer holding the authentication tag. + * This must be a readable buffer of length \c 16 Bytes. + * \param input The buffer containing the data to decrypt. + * This pointer can be \c NULL if `ilen == 0`. + * \param output The buffer to where the decrypted data is written. + * This pointer can be \c NULL if `ilen == 0`. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED + * if the data was not authentic. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_chachapoly_auth_decrypt(mbedtls_chachapoly_context *ctx, + size_t length, + const unsigned char nonce[12], + const unsigned char *aad, + size_t aad_len, + const unsigned char tag[16], + const unsigned char *input, + unsigned char *output); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The ChaCha20-Poly1305 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_chachapoly_self_test(int verbose); +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CHACHAPOLY_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/check_config.h b/r5dev/thirdparty/mbedtls/include/mbedtls/check_config.h new file mode 100644 index 00000000..bd683ddd --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/check_config.h @@ -0,0 +1,1108 @@ +/** + * \file check_config.h + * + * \brief Consistency checks for configuration options + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CHECK_CONFIG_H +#define MBEDTLS_CHECK_CONFIG_H + +/* *INDENT-OFF* */ +/* + * We assume CHAR_BIT is 8 in many places. In practice, this is true on our + * target platforms, so not an issue, but let's just be extra sure. + */ +#include +#if CHAR_BIT != 8 +#error "mbed TLS requires a platform with 8-bit chars" +#endif + +#include + +#if defined(_WIN32) +#if !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_C is required on Windows" +#endif + +/* Fix the config here. Not convenient to put an #ifdef _WIN32 in mbedtls_config.h as + * it would confuse config.py. */ +#if !defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) && \ + !defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) +#define MBEDTLS_PLATFORM_SNPRINTF_ALT +#endif + +#if !defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) && \ + !defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) +#define MBEDTLS_PLATFORM_VSNPRINTF_ALT +#endif +#endif /* _WIN32 */ + +#if defined(TARGET_LIKE_MBED) && defined(MBEDTLS_NET_C) +#error "The NET module is not available for mbed OS - please use the network functions provided by Mbed OS" +#endif + +#if defined(MBEDTLS_DEPRECATED_WARNING) && \ + !defined(__GNUC__) && !defined(__clang__) +#error "MBEDTLS_DEPRECATED_WARNING only works with GCC and Clang" +#endif + +#if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_HAVE_TIME) +#error "MBEDTLS_HAVE_TIME_DATE without MBEDTLS_HAVE_TIME does not make sense" +#endif + +#if defined(__aarch64__) && defined(__GNUC__) +/* We don't do anything with MBEDTLS_AESCE_C on systems without ^ these two */ +#if defined(MBEDTLS_AESCE_C) && !defined(MBEDTLS_HAVE_ASM) +#error "MBEDTLS_AESCE_C defined, but not all prerequisites" +#endif +#endif + +#if defined(MBEDTLS_CTR_DRBG_C) && !defined(MBEDTLS_AES_C) +#error "MBEDTLS_CTR_DRBG_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_DHM_C) && !defined(MBEDTLS_BIGNUM_C) +#error "MBEDTLS_DHM_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_CMAC_C) && \ + ( !defined(MBEDTLS_CIPHER_C ) || ( !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_DES_C) ) ) +#error "MBEDTLS_CMAC_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_NIST_KW_C) && \ + ( !defined(MBEDTLS_AES_C) || !defined(MBEDTLS_CIPHER_C) ) +#error "MBEDTLS_NIST_KW_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECDH_C) && !defined(MBEDTLS_ECP_C) +#error "MBEDTLS_ECDH_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECDSA_C) && \ + ( !defined(MBEDTLS_ECP_C) || \ + !( defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) ) || \ + !defined(MBEDTLS_ASN1_PARSE_C) || \ + !defined(MBEDTLS_ASN1_WRITE_C) ) +#error "MBEDTLS_ECDSA_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECJPAKE_C) && \ + ( !defined(MBEDTLS_ECP_C) || \ + !( defined(MBEDTLS_MD_C) || defined(MBEDTLS_PSA_CRYPTO_C) ) ) +#error "MBEDTLS_ECJPAKE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) && \ + ( defined(MBEDTLS_ECDH_COMPUTE_SHARED_ALT) || \ + defined(MBEDTLS_ECDH_GEN_PUBLIC_ALT) || \ + defined(MBEDTLS_ECDSA_SIGN_ALT) || \ + defined(MBEDTLS_ECDSA_VERIFY_ALT) || \ + defined(MBEDTLS_ECDSA_GENKEY_ALT) || \ + defined(MBEDTLS_ECP_INTERNAL_ALT) || \ + defined(MBEDTLS_ECP_ALT) ) +#error "MBEDTLS_ECP_RESTARTABLE defined, but it cannot coexist with an alternative ECP implementation" +#endif + +#if defined(MBEDTLS_ECP_RESTARTABLE) && \ + !defined(MBEDTLS_ECP_C) +#error "MBEDTLS_ECP_RESTARTABLE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) && !defined(MBEDTLS_HMAC_DRBG_C) +#error "MBEDTLS_ECDSA_DETERMINISTIC defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_C) && ( !defined(MBEDTLS_BIGNUM_C) || ( \ + !defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) && \ + !defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) ) ) +#error "MBEDTLS_ECP_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_PARSE_C) && !defined(MBEDTLS_ASN1_PARSE_C) +#error "MBEDTLS_PK_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PKCS12_C) && !defined(MBEDTLS_CIPHER_C) +#error "MBEDTLS_PKCS12_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PKCS5_C) && \ + ( !( defined(MBEDTLS_MD_C) || defined(MBEDTLS_PSA_CRYPTO_C) ) || \ + !defined(MBEDTLS_CIPHER_C) ) +#error "MBEDTLS_PKCS5_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PKCS12_C) && \ + !( defined(MBEDTLS_MD_C) || defined(MBEDTLS_PSA_CRYPTO_C) ) +#error "MBEDTLS_PKCS12_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PKCS1_V21) && \ + !( defined(MBEDTLS_MD_C) || defined(MBEDTLS_PSA_CRYPTO_C) ) +#error "MBEDTLS_PKCS1_V21 defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ENTROPY_C) && (!defined(MBEDTLS_SHA512_C) && \ + !defined(MBEDTLS_SHA256_C)) +#error "MBEDTLS_ENTROPY_C defined, but not all prerequisites" +#endif +#if defined(MBEDTLS_ENTROPY_C) && defined(MBEDTLS_SHA512_C) && \ + defined(MBEDTLS_CTR_DRBG_ENTROPY_LEN) && (MBEDTLS_CTR_DRBG_ENTROPY_LEN > 64) +#error "MBEDTLS_CTR_DRBG_ENTROPY_LEN value too high" +#endif +#if defined(MBEDTLS_ENTROPY_C) && \ + ( !defined(MBEDTLS_SHA512_C) || defined(MBEDTLS_ENTROPY_FORCE_SHA256) ) \ + && defined(MBEDTLS_CTR_DRBG_ENTROPY_LEN) && (MBEDTLS_CTR_DRBG_ENTROPY_LEN > 32) +#error "MBEDTLS_CTR_DRBG_ENTROPY_LEN value too high" +#endif +#if defined(MBEDTLS_ENTROPY_C) && \ + defined(MBEDTLS_ENTROPY_FORCE_SHA256) && !defined(MBEDTLS_SHA256_C) +#error "MBEDTLS_ENTROPY_FORCE_SHA256 defined, but not all prerequisites" +#endif + +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#define MBEDTLS_HAS_MEMSAN +#endif +#endif +#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) && !defined(MBEDTLS_HAS_MEMSAN) +#error "MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN requires building with MemorySanitizer" +#endif +#undef MBEDTLS_HAS_MEMSAN + +#if defined(MBEDTLS_CCM_C) && ( \ + !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_CAMELLIA_C) && !defined(MBEDTLS_ARIA_C) ) +#error "MBEDTLS_CCM_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_CCM_C) && !defined(MBEDTLS_CIPHER_C) +#error "MBEDTLS_CCM_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_GCM_C) && ( \ + !defined(MBEDTLS_AES_C) && !defined(MBEDTLS_CAMELLIA_C) && !defined(MBEDTLS_ARIA_C) ) +#error "MBEDTLS_GCM_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_GCM_C) && !defined(MBEDTLS_CIPHER_C) +#error "MBEDTLS_GCM_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) && !defined(MBEDTLS_CHACHA20_C) +#error "MBEDTLS_CHACHAPOLY_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_CHACHAPOLY_C) && !defined(MBEDTLS_POLY1305_C) +#error "MBEDTLS_CHACHAPOLY_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_RANDOMIZE_JAC_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_ADD_MIXED_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_ADD_MIXED_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_DOUBLE_JAC_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NORMALIZE_JAC_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_RANDOMIZE_MXZ_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NORMALIZE_MXZ_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ECP_NO_FALLBACK) && !defined(MBEDTLS_ECP_INTERNAL_ALT) +#error "MBEDTLS_ECP_NO_FALLBACK defined, but no alternative implementation enabled" +#endif + +#if defined(MBEDTLS_HKDF_C) && !defined(MBEDTLS_MD_C) +#error "MBEDTLS_HKDF_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_HMAC_DRBG_C) && !defined(MBEDTLS_MD_C) +#error "MBEDTLS_HMAC_DRBG_C defined, but not all prerequisites" +#endif + +/* Helper for ECDSA dependencies, will be undefined at the end of the file */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(PSA_HAVE_FULL_ECDSA) +#define MBEDTLS_PK_HAVE_ECDSA +#endif +#else /* MBEDTLS_USE_PSA_CRYPTO */ +#if defined(MBEDTLS_ECDSA_C) +#define MBEDTLS_PK_HAVE_ECDSA +#endif +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +/* Helper for JPAKE dependencies, will be undefined at the end of the file */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(PSA_HAVE_FULL_JPAKE) +#define MBEDTLS_PK_HAVE_JPAKE +#endif +#else /* MBEDTLS_USE_PSA_CRYPTO */ +#if defined(MBEDTLS_ECJPAKE_C) +#define MBEDTLS_PK_HAVE_JPAKE +#endif +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || \ + !defined(MBEDTLS_PK_HAVE_ECDSA) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) && !defined(MBEDTLS_DHM_C) +#error "MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) && \ + !defined(MBEDTLS_ECDH_C) +#error "MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) && \ + ( !defined(MBEDTLS_DHM_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || !defined(MBEDTLS_RSA_C) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) || !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) && \ + ( !defined(MBEDTLS_ECDH_C) || \ + !defined(MBEDTLS_PK_HAVE_ECDSA) || \ + !defined(MBEDTLS_X509_CRT_PARSE_C) ) +#error "MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) && \ + ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) || \ + !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) && \ + ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_X509_CRT_PARSE_C) || \ + !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_KEY_EXCHANGE_RSA_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \ + ( !defined(MBEDTLS_PK_HAVE_JPAKE) || \ + !defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) ) +#error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites" +#endif + +/* Use of EC J-PAKE in TLS requires SHA-256. + * This will be taken from MD if it is present, or from PSA if MD is absent. + * Note: MBEDTLS_ECJPAKE_C depends on MBEDTLS_MD_C || MBEDTLS_PSA_CRYPTO_C. */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \ + !( defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA256_C) ) && \ + !( !defined(MBEDTLS_MD_C) && defined(PSA_WANT_ALG_SHA_256) ) +#error "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) && \ + !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) && \ + ( !defined(MBEDTLS_SHA256_C) && \ + !defined(MBEDTLS_SHA512_C) && \ + !defined(MBEDTLS_SHA1_C) ) +#error "!MBEDTLS_SSL_KEEP_PEER_CERTIFICATE requires MBEDTLS_SHA512_C, MBEDTLS_SHA256_C or MBEDTLS_SHA1_C" +#endif + +#if defined(MBEDTLS_MD_C) && !( \ + defined(MBEDTLS_MD5_C) || \ + defined(MBEDTLS_RIPEMD160_C) || \ + defined(MBEDTLS_SHA1_C) || \ + defined(MBEDTLS_SHA224_C) || \ + defined(MBEDTLS_SHA256_C) || \ + defined(MBEDTLS_SHA384_C) || \ + defined(MBEDTLS_SHA512_C) ) +#error "MBEDTLS_MD_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_LMS_C) && \ + ! ( defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_256) ) +#error "MBEDTLS_LMS_C requires MBEDTLS_PSA_CRYPTO_C and PSA_WANT_ALG_SHA_256" +#endif + +#if defined(MBEDTLS_LMS_PRIVATE) && \ + ( !defined(MBEDTLS_LMS_C) ) +#error "MBEDTLS_LMS_PRIVATE requires MBEDTLS_LMS_C" +#endif + +#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) ) +#error "MBEDTLS_MEMORY_BUFFER_ALLOC_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_MEMORY_BACKTRACE) && !defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) +#error "MBEDTLS_MEMORY_BACKTRACE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_MEMORY_DEBUG) && !defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) +#error "MBEDTLS_MEMORY_DEBUG defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PADLOCK_C) && !defined(MBEDTLS_HAVE_ASM) +#error "MBEDTLS_PADLOCK_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PEM_PARSE_C) && !defined(MBEDTLS_BASE64_C) +#error "MBEDTLS_PEM_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PEM_WRITE_C) && !defined(MBEDTLS_BASE64_C) +#error "MBEDTLS_PEM_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_C) && \ + !defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_ECP_C) +#error "MBEDTLS_PK_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_PARSE_C) && !defined(MBEDTLS_PK_C) +#error "MBEDTLS_PK_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PK_WRITE_C) && !defined(MBEDTLS_PK_C) +#error "MBEDTLS_PK_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_EXIT_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_EXIT_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_EXIT_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_EXIT_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_EXIT_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_EXIT) ||\ + defined(MBEDTLS_PLATFORM_EXIT_ALT) ) +#error "MBEDTLS_PLATFORM_EXIT_MACRO and MBEDTLS_PLATFORM_STD_EXIT/MBEDTLS_PLATFORM_EXIT_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_SETBUF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_SETBUF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_SETBUF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_SETBUF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_SETBUF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_SETBUF) ||\ + defined(MBEDTLS_PLATFORM_SETBUF_ALT) ) +#error "MBEDTLS_PLATFORM_SETBUF_MACRO and MBEDTLS_PLATFORM_STD_SETBUF/MBEDTLS_PLATFORM_SETBUF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_ALT) &&\ + ( !defined(MBEDTLS_PLATFORM_C) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_TIME_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_TIME_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_TYPE_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_TIME_TYPE_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_TIME) ||\ + defined(MBEDTLS_PLATFORM_TIME_ALT) ) +#error "MBEDTLS_PLATFORM_TIME_MACRO and MBEDTLS_PLATFORM_STD_TIME/MBEDTLS_PLATFORM_TIME_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_TIME_TYPE_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_TIME) ||\ + defined(MBEDTLS_PLATFORM_TIME_ALT) ) +#error "MBEDTLS_PLATFORM_TIME_TYPE_MACRO and MBEDTLS_PLATFORM_STD_TIME/MBEDTLS_PLATFORM_TIME_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_FPRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_FPRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_FPRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_FPRINTF) ||\ + defined(MBEDTLS_PLATFORM_FPRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_FPRINTF_MACRO and MBEDTLS_PLATFORM_STD_FPRINTF/MBEDTLS_PLATFORM_FPRINTF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) ) +#error "MBEDTLS_PLATFORM_FREE_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) &&\ + defined(MBEDTLS_PLATFORM_STD_FREE) +#error "MBEDTLS_PLATFORM_FREE_MACRO and MBEDTLS_PLATFORM_STD_FREE cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) && !defined(MBEDTLS_PLATFORM_CALLOC_MACRO) +#error "MBEDTLS_PLATFORM_CALLOC_MACRO must be defined if MBEDTLS_PLATFORM_FREE_MACRO is" +#endif + +#if defined(MBEDTLS_PLATFORM_CALLOC_MACRO) &&\ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_PLATFORM_MEMORY) ) +#error "MBEDTLS_PLATFORM_CALLOC_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_CALLOC_MACRO) &&\ + defined(MBEDTLS_PLATFORM_STD_CALLOC) +#error "MBEDTLS_PLATFORM_CALLOC_MACRO and MBEDTLS_PLATFORM_STD_CALLOC cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_CALLOC_MACRO) && !defined(MBEDTLS_PLATFORM_FREE_MACRO) +#error "MBEDTLS_PLATFORM_FREE_MACRO must be defined if MBEDTLS_PLATFORM_CALLOC_MACRO is" +#endif + +#if defined(MBEDTLS_PLATFORM_MEMORY) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_MEMORY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_PRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_PRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_PRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_PRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_PRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_PRINTF) ||\ + defined(MBEDTLS_PLATFORM_PRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_PRINTF_MACRO and MBEDTLS_PLATFORM_STD_PRINTF/MBEDTLS_PLATFORM_PRINTF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_SNPRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_SNPRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_SNPRINTF) ||\ + defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_SNPRINTF_MACRO and MBEDTLS_PLATFORM_STD_SNPRINTF/MBEDTLS_PLATFORM_SNPRINTF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_VSNPRINTF_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) && !defined(MBEDTLS_PLATFORM_C) +#error "MBEDTLS_PLATFORM_VSNPRINTF_MACRO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_VSNPRINTF) ||\ + defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) ) +#error "MBEDTLS_PLATFORM_VSNPRINTF_MACRO and MBEDTLS_PLATFORM_STD_VSNPRINTF/MBEDTLS_PLATFORM_VSNPRINTF_ALT cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_MEM_HDR) &&\ + !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) +#error "MBEDTLS_PLATFORM_STD_MEM_HDR defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_CALLOC) && !defined(MBEDTLS_PLATFORM_MEMORY) +#error "MBEDTLS_PLATFORM_STD_CALLOC defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_FREE) && !defined(MBEDTLS_PLATFORM_MEMORY) +#error "MBEDTLS_PLATFORM_STD_FREE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_EXIT) &&\ + !defined(MBEDTLS_PLATFORM_EXIT_ALT) +#error "MBEDTLS_PLATFORM_STD_EXIT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_TIME) &&\ + ( !defined(MBEDTLS_PLATFORM_TIME_ALT) ||\ + !defined(MBEDTLS_HAVE_TIME) ) +#error "MBEDTLS_PLATFORM_STD_TIME defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_FPRINTF) &&\ + !defined(MBEDTLS_PLATFORM_FPRINTF_ALT) +#error "MBEDTLS_PLATFORM_STD_FPRINTF defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_PRINTF) &&\ + !defined(MBEDTLS_PLATFORM_PRINTF_ALT) +#error "MBEDTLS_PLATFORM_STD_PRINTF defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_SNPRINTF) &&\ + !defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) +#error "MBEDTLS_PLATFORM_STD_SNPRINTF defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_ENTROPY_NV_SEED) &&\ + ( !defined(MBEDTLS_PLATFORM_C) || !defined(MBEDTLS_ENTROPY_C) ) +#error "MBEDTLS_ENTROPY_NV_SEED defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_ALT) &&\ + !defined(MBEDTLS_ENTROPY_NV_SEED) +#error "MBEDTLS_PLATFORM_NV_SEED_ALT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_NV_SEED_READ) &&\ + !defined(MBEDTLS_PLATFORM_NV_SEED_ALT) +#error "MBEDTLS_PLATFORM_STD_NV_SEED_READ defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_STD_NV_SEED_WRITE) &&\ + !defined(MBEDTLS_PLATFORM_NV_SEED_ALT) +#error "MBEDTLS_PLATFORM_STD_NV_SEED_WRITE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_READ_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_NV_SEED_READ) ||\ + defined(MBEDTLS_PLATFORM_NV_SEED_ALT) ) +#error "MBEDTLS_PLATFORM_NV_SEED_READ_MACRO and MBEDTLS_PLATFORM_STD_NV_SEED_READ cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO) &&\ + ( defined(MBEDTLS_PLATFORM_STD_NV_SEED_WRITE) ||\ + defined(MBEDTLS_PLATFORM_NV_SEED_ALT) ) +#error "MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO and MBEDTLS_PLATFORM_STD_NV_SEED_WRITE cannot be defined simultaneously" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_C) && \ + !( ( ( defined(MBEDTLS_CTR_DRBG_C) || defined(MBEDTLS_HMAC_DRBG_C) ) && \ + defined(MBEDTLS_ENTROPY_C) ) || \ + defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) ) +#error "MBEDTLS_PSA_CRYPTO_C defined, but not all prerequisites (missing RNG)" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_C) && !defined(MBEDTLS_CIPHER_C ) +#error "MBEDTLS_PSA_CRYPTO_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_SPM) && !defined(MBEDTLS_PSA_CRYPTO_C) +#error "MBEDTLS_PSA_CRYPTO_SPM defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) && \ + ! ( defined(MBEDTLS_PSA_CRYPTO_C) && \ + defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) ) +#error "MBEDTLS_PSA_CRYPTO_SE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +#if defined(MBEDTLS_DEPRECATED_REMOVED) +#error "MBEDTLS_PSA_CRYPTO_SE_C is deprecated and will be removed in a future version of Mbed TLS" +#elif defined(MBEDTLS_DEPRECATED_WARNING) +#warning "MBEDTLS_PSA_CRYPTO_SE_C is deprecated and will be removed in a future version of Mbed TLS" +#endif +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) && \ + ! defined(MBEDTLS_PSA_CRYPTO_C) +#error "MBEDTLS_PSA_CRYPTO_STORAGE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + !( defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) && \ + defined(MBEDTLS_ENTROPY_NV_SEED) ) +#error "MBEDTLS_PSA_INJECT_ENTROPY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + !defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) +#error "MBEDTLS_PSA_INJECT_ENTROPY is not compatible with actual entropy sources" +#endif + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +#error "MBEDTLS_PSA_INJECT_ENTROPY is not compatible with MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG" +#endif + +#if defined(MBEDTLS_PSA_ITS_FILE_C) && \ + !defined(MBEDTLS_FS_IO) +#error "MBEDTLS_PSA_ITS_FILE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_RSA_C) && ( !defined(MBEDTLS_BIGNUM_C) || \ + !defined(MBEDTLS_OID_C) ) +#error "MBEDTLS_RSA_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_RSA_C) && ( !defined(MBEDTLS_PKCS1_V21) && \ + !defined(MBEDTLS_PKCS1_V15) ) +#error "MBEDTLS_RSA_C defined, but none of the PKCS1 versions enabled" +#endif + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ + ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_PKCS1_V21) ) +#error "MBEDTLS_X509_RSASSA_PSS_SUPPORT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) && \ + defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) +#error "Must only define one of MBEDTLS_SHA512_USE_A64_CRYPTO_*" +#endif + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) +#if !defined(MBEDTLS_SHA512_C) +#error "MBEDTLS_SHA512_USE_A64_CRYPTO_* defined without MBEDTLS_SHA512_C" +#endif +#if defined(MBEDTLS_SHA512_ALT) || defined(MBEDTLS_SHA512_PROCESS_ALT) +#error "MBEDTLS_SHA512_*ALT can't be used with MBEDTLS_SHA512_USE_A64_CRYPTO_*" +#endif + +#endif /* MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT || MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY */ + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) && !defined(__aarch64__) +#error "MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY defined on non-Aarch64 system" +#endif + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) && \ + defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) +#error "Must only define one of MBEDTLS_SHA256_USE_A64_CRYPTO_*" +#endif + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) +#if !defined(MBEDTLS_SHA256_C) +#error "MBEDTLS_SHA256_USE_A64_CRYPTO_* defined without MBEDTLS_SHA256_C" +#endif +#if defined(MBEDTLS_SHA256_ALT) || defined(MBEDTLS_SHA256_PROCESS_ALT) +#error "MBEDTLS_SHA256_*ALT can't be used with MBEDTLS_SHA256_USE_A64_CRYPTO_*" +#endif + +#endif + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) && \ + !defined(__aarch64__) && !defined(_M_ARM64) +#error "MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY defined on non-Aarch64 system" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && !defined(MBEDTLS_USE_PSA_CRYPTO) && \ + !( defined(MBEDTLS_SHA1_C) || defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA512_C) ) +#error "MBEDTLS_SSL_PROTO_TLS1_2 defined, but not all prerequisites" +#endif + +/* TLS 1.3 requires separate HKDF parts from PSA */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + !( defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_HKDF_EXTRACT) && defined(PSA_WANT_ALG_HKDF_EXPAND) ) +#error "MBEDTLS_SSL_PROTO_TLS1_3 defined, but not all prerequisites" +#endif + +/* TLS 1.3 requires at least one ciphersuite, so at least SHA-256 or SHA-384 */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +/* We always need at least one of the hashes via PSA (for use with HKDF) */ +#if !( defined(PSA_WANT_ALG_SHA_256) || defined(PSA_WANT_ALG_SHA_384) ) +#error "MBEDTLS_SSL_PROTO_TLS1_3 defined, but not all prerequisites" +#endif /* !(PSA_WANT_ALG_SHA_256 || PSA_WANT_ALG_SHA_384) */ +#if !defined(MBEDTLS_USE_PSA_CRYPTO) +/* When USE_PSA_CRYPTO is not defined, we also need SHA-256 or SHA-384 via the + * legacy interface, including via the MD layer, for the parts of the code + * that are shared with TLS 1.2 (running handshake hash). */ +#if !defined(MBEDTLS_MD_C) || \ + !( defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA384_C) ) +#error "MBEDTLS_SSL_PROTO_TLS1_3 defined, but not all prerequisites" +#endif /* !MBEDTLS_MD_C || !(MBEDTLS_SHA256_C || MBEDTLS_SHA384_C) */ +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +#if !( defined(MBEDTLS_ECDH_C) && defined(MBEDTLS_X509_CRT_PARSE_C) && \ + ( defined(MBEDTLS_PK_HAVE_ECDSA) || defined(MBEDTLS_PKCS1_V21) ) ) +#error "MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED defined, but not all prerequisites" +#endif +#endif + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) +#if !( defined(MBEDTLS_ECDH_C) ) +#error "MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED defined, but not all prerequisites" +#endif +#endif + +/* + * The current implementation of TLS 1.3 requires MBEDTLS_SSL_KEEP_PEER_CERTIFICATE. + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +#error "MBEDTLS_SSL_PROTO_TLS1_3 defined without MBEDTLS_SSL_KEEP_PEER_CERTIFICATE" +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \ + !(defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) ) +#error "One or more versions of the TLS protocol are enabled " \ + "but no key exchange methods defined with MBEDTLS_KEY_EXCHANGE_xxxx" +#endif + +#if defined(MBEDTLS_SSL_EARLY_DATA) && \ + ( !defined(MBEDTLS_SSL_SESSION_TICKETS) || \ + ( !defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED) && \ + !defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) ) ) +#error "MBEDTLS_SSL_EARLY_DATA defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_SRV_C) && \ + ( !defined(MBEDTLS_SSL_MAX_EARLY_DATA_SIZE) || \ + ( MBEDTLS_SSL_MAX_EARLY_DATA_SIZE < 0 ) || \ + ( MBEDTLS_SSL_MAX_EARLY_DATA_SIZE > UINT32_MAX ) ) +#error "MBEDTLS_SSL_MAX_EARLY_DATA_SIZE MUST be defined and in range(0..UINT32_MAX)" +#endif + +#if defined(MBEDTLS_SSL_PROTO_DTLS) && \ + !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_PROTO_DTLS defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_CLI_C) && !defined(MBEDTLS_SSL_TLS_C) +#error "MBEDTLS_SSL_CLI_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) && !defined(MBEDTLS_X509_CRT_PARSE_C) +#error "MBEDTLS_SSL_ASYNC_PRIVATE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) && ( !defined(MBEDTLS_CIPHER_C) || \ + ( !defined(MBEDTLS_MD_C) && !defined(MBEDTLS_USE_PSA_CRYPTO) ) ) +#error "MBEDTLS_SSL_TLS_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_SRV_C) && !defined(MBEDTLS_SSL_TLS_C) +#error "MBEDTLS_SSL_SRV_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_TLS_C) && \ + !( defined(MBEDTLS_SSL_PROTO_TLS1_2) || defined(MBEDTLS_SSL_PROTO_TLS1_3) ) +#error "MBEDTLS_SSL_TLS_C defined, but no protocols are active" +#endif + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && !defined(MBEDTLS_SSL_PROTO_DTLS) +#error "MBEDTLS_SSL_DTLS_HELLO_VERIFY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && \ + !defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) +#error "MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) && \ + ( !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) ) +#error "MBEDTLS_SSL_DTLS_ANTI_REPLAY defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + ( !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_PROTO_DTLS) ) +#error "MBEDTLS_SSL_DTLS_CONNECTION_ID defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + defined(MBEDTLS_SSL_CID_IN_LEN_MAX) && \ + MBEDTLS_SSL_CID_IN_LEN_MAX > 255 +#error "MBEDTLS_SSL_CID_IN_LEN_MAX too large (max 255)" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + defined(MBEDTLS_SSL_CID_OUT_LEN_MAX) && \ + MBEDTLS_SSL_CID_OUT_LEN_MAX > 255 +#error "MBEDTLS_SSL_CID_OUT_LEN_MAX too large (max 255)" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT) && \ + !defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +#error "MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT) && MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT != 0 +#if defined(MBEDTLS_DEPRECATED_REMOVED) +#error "MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT is deprecated and will be removed in a future version of Mbed TLS" +#elif defined(MBEDTLS_DEPRECATED_WARNING) +#warning "MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT is deprecated and will be removed in a future version of Mbed TLS" +#endif +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT && MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT != 0 */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) && \ + !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_ENCRYPT_THEN_MAC defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) && \ + !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_EXTENDED_MASTER_SECRET defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_RENEGOTIATION) && \ + !defined(MBEDTLS_SSL_PROTO_TLS1_2) +#error "MBEDTLS_SSL_RENEGOTIATION defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_TICKET_C) && ( !defined(MBEDTLS_CIPHER_C) && \ + !defined(MBEDTLS_USE_PSA_CRYPTO) ) +#error "MBEDTLS_SSL_TICKET_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_TICKET_C) && \ + !( defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) ) +#error "MBEDTLS_SSL_TICKET_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_TLS1_3_TICKET_NONCE_LENGTH) && \ + MBEDTLS_SSL_TLS1_3_TICKET_NONCE_LENGTH >= 256 +#error "MBEDTLS_SSL_TLS1_3_TICKET_NONCE_LENGTH must be less than 256" +#endif + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + !defined(MBEDTLS_X509_CRT_PARSE_C) +#error "MBEDTLS_SSL_SERVER_NAME_INDICATION defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_THREADING_PTHREAD) +#if !defined(MBEDTLS_THREADING_C) || defined(MBEDTLS_THREADING_IMPL) +#error "MBEDTLS_THREADING_PTHREAD defined, but not all prerequisites" +#endif +#define MBEDTLS_THREADING_IMPL +#endif + +#if defined(MBEDTLS_THREADING_ALT) +#if !defined(MBEDTLS_THREADING_C) || defined(MBEDTLS_THREADING_IMPL) +#error "MBEDTLS_THREADING_ALT defined, but not all prerequisites" +#endif +#define MBEDTLS_THREADING_IMPL +#endif + +#if defined(MBEDTLS_THREADING_C) && !defined(MBEDTLS_THREADING_IMPL) +#error "MBEDTLS_THREADING_C defined, single threading implementation required" +#endif +#undef MBEDTLS_THREADING_IMPL + +#if defined(MBEDTLS_USE_PSA_CRYPTO) && !defined(MBEDTLS_PSA_CRYPTO_C) +#error "MBEDTLS_USE_PSA_CRYPTO defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_VERSION_FEATURES) && !defined(MBEDTLS_VERSION_C) +#error "MBEDTLS_VERSION_FEATURES defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_USE_C) && ( !defined(MBEDTLS_BIGNUM_C) || \ + !defined(MBEDTLS_OID_C) || !defined(MBEDTLS_ASN1_PARSE_C) || \ + !defined(MBEDTLS_PK_PARSE_C) || \ + ( !defined(MBEDTLS_MD_C) && !defined(MBEDTLS_USE_PSA_CRYPTO) ) ) +#error "MBEDTLS_X509_USE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CREATE_C) && ( !defined(MBEDTLS_BIGNUM_C) || \ + !defined(MBEDTLS_OID_C) || !defined(MBEDTLS_ASN1_WRITE_C) || \ + !defined(MBEDTLS_PK_PARSE_C) || \ + ( !defined(MBEDTLS_MD_C) && !defined(MBEDTLS_USE_PSA_CRYPTO) ) ) +#error "MBEDTLS_X509_CREATE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && ( !defined(MBEDTLS_X509_USE_C) ) +#error "MBEDTLS_X509_CRT_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CRL_PARSE_C) && ( !defined(MBEDTLS_X509_USE_C) ) +#error "MBEDTLS_X509_CRL_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CSR_PARSE_C) && ( !defined(MBEDTLS_X509_USE_C) ) +#error "MBEDTLS_X509_CSR_PARSE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CRT_WRITE_C) && ( !defined(MBEDTLS_X509_CREATE_C) ) +#error "MBEDTLS_X509_CRT_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_CSR_WRITE_C) && ( !defined(MBEDTLS_X509_CREATE_C) ) +#error "MBEDTLS_X509_CSR_WRITE_C defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) && \ + ( !defined(MBEDTLS_X509_CRT_PARSE_C) ) +#error "MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_HAVE_INT32) && defined(MBEDTLS_HAVE_INT64) +#error "MBEDTLS_HAVE_INT32 and MBEDTLS_HAVE_INT64 cannot be defined simultaneously" +#endif /* MBEDTLS_HAVE_INT32 && MBEDTLS_HAVE_INT64 */ + +#if ( defined(MBEDTLS_HAVE_INT32) || defined(MBEDTLS_HAVE_INT64) ) && \ + defined(MBEDTLS_HAVE_ASM) +#error "MBEDTLS_HAVE_INT32/MBEDTLS_HAVE_INT64 and MBEDTLS_HAVE_ASM cannot be defined simultaneously" +#endif /* (MBEDTLS_HAVE_INT32 || MBEDTLS_HAVE_INT64) && MBEDTLS_HAVE_ASM */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) && ( !defined(MBEDTLS_SSL_PROTO_DTLS) ) +#error "MBEDTLS_SSL_DTLS_SRTP defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) && ( !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) ) +#error "MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) && ( !defined(MBEDTLS_SSL_PROTO_TLS1_3) ) +#error "MBEDTLS_SSL_RECORD_SIZE_LIMIT defined, but not all prerequisites" +#endif + +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) && !( defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) ) +#error "MBEDTLS_SSL_CONTEXT_SERIALIZATION defined, but not all prerequisites" +#endif + +/* Reject attempts to enable options that have been removed and that could + * cause a build to succeed but with features removed. */ + +#if defined(MBEDTLS_HAVEGE_C) //no-check-names +#error "MBEDTLS_HAVEGE_C was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/2599" +#endif + +#if defined(MBEDTLS_SSL_HW_RECORD_ACCEL) //no-check-names +#error "MBEDTLS_SSL_HW_RECORD_ACCEL was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_SSL_PROTO_SSL3) //no-check-names +#error "MBEDTLS_SSL_PROTO_SSL3 (SSL v3.0 support) was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO) //no-check-names +#error "MBEDTLS_SSL_SRV_SUPPORT_SSLV2_CLIENT_HELLO (SSL v2 ClientHello support) was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT) //no-check-names +#error "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT (compatibility with the buggy implementation of truncated HMAC in Mbed TLS up to 2.7) was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES) //no-check-names +#error "MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES was removed in Mbed TLS 3.0. See the ChangeLog entry if you really need SHA-1-signed certificates." +#endif + +#if defined(MBEDTLS_ZLIB_SUPPORT) //no-check-names +#error "MBEDTLS_ZLIB_SUPPORT was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4031" +#endif + +#if defined(MBEDTLS_CHECK_PARAMS) //no-check-names +#error "MBEDTLS_CHECK_PARAMS was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4313" +#endif + +#if defined(MBEDTLS_SSL_CID_PADDING_GRANULARITY) //no-check-names +#error "MBEDTLS_SSL_CID_PADDING_GRANULARITY was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4335" +#endif + +#if defined(MBEDTLS_SSL_TLS1_3_PADDING_GRANULARITY) //no-check-names +#error "MBEDTLS_SSL_TLS1_3_PADDING_GRANULARITY was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4335" +#endif + +#if defined(MBEDTLS_SSL_TRUNCATED_HMAC) //no-check-names +#error "MBEDTLS_SSL_TRUNCATED_HMAC was removed in Mbed TLS 3.0. See https://github.com/Mbed-TLS/mbedtls/issues/4341" +#endif + +#if defined(MBEDTLS_PKCS7_C) && ( ( !defined(MBEDTLS_ASN1_PARSE_C) ) || \ + ( !defined(MBEDTLS_OID_C) ) || ( !defined(MBEDTLS_PK_PARSE_C) ) || \ + ( !defined(MBEDTLS_X509_CRT_PARSE_C) ) ||\ + ( !defined(MBEDTLS_X509_CRL_PARSE_C) ) || ( !defined(MBEDTLS_BIGNUM_C) ) || \ + ( !defined(MBEDTLS_MD_C) ) ) +#error "MBEDTLS_PKCS7_C is defined, but not all prerequisites" +#endif + +/* Undefine helper symbols */ +#undef MBEDTLS_PK_HAVE_ECDSA +#undef MBEDTLS_PK_HAVE_JPAKE + +/* + * Avoid warning from -pedantic. This is a convenient place for this + * workaround since this is included by every single file before the + * #if defined(MBEDTLS_xxx_C) that results in empty translation units. + */ +typedef int mbedtls_iso_c_forbids_empty_translation_units; + +/* *INDENT-ON* */ +#endif /* MBEDTLS_CHECK_CONFIG_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/cipher.h b/r5dev/thirdparty/mbedtls/include/mbedtls/cipher.h new file mode 100644 index 00000000..2f890407 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/cipher.h @@ -0,0 +1,1181 @@ +/** + * \file cipher.h + * + * \brief This file contains an abstraction interface for use with the cipher + * primitives provided by the library. It provides a common interface to all of + * the available cipher operations. + * + * \author Adriaan de Jong + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CIPHER_H +#define MBEDTLS_CIPHER_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include "mbedtls/platform_util.h" + +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CCM_C) || defined(MBEDTLS_CHACHAPOLY_C) +#define MBEDTLS_CIPHER_MODE_AEAD +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#define MBEDTLS_CIPHER_MODE_WITH_PADDING +#endif + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) || \ + defined(MBEDTLS_CHACHA20_C) +#define MBEDTLS_CIPHER_MODE_STREAM +#endif + +/** The selected feature is not available. */ +#define MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE -0x6080 +/** Bad input parameters. */ +#define MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA -0x6100 +/** Failed to allocate memory. */ +#define MBEDTLS_ERR_CIPHER_ALLOC_FAILED -0x6180 +/** Input data contains invalid padding and is rejected. */ +#define MBEDTLS_ERR_CIPHER_INVALID_PADDING -0x6200 +/** Decryption of block requires a full block. */ +#define MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED -0x6280 +/** Authentication failed (for AEAD modes). */ +#define MBEDTLS_ERR_CIPHER_AUTH_FAILED -0x6300 +/** The context is invalid. For example, because it was freed. */ +#define MBEDTLS_ERR_CIPHER_INVALID_CONTEXT -0x6380 + +#define MBEDTLS_CIPHER_VARIABLE_IV_LEN 0x01 /**< Cipher accepts IVs of variable length. */ +#define MBEDTLS_CIPHER_VARIABLE_KEY_LEN 0x02 /**< Cipher accepts keys of variable length. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Supported cipher types. + * + * \warning DES/3DES are considered weak ciphers and their use + * constitutes a security risk. We recommend considering stronger + * ciphers instead. + */ +typedef enum { + MBEDTLS_CIPHER_ID_NONE = 0, /**< Placeholder to mark the end of cipher ID lists. */ + MBEDTLS_CIPHER_ID_NULL, /**< The identity cipher, treated as a stream cipher. */ + MBEDTLS_CIPHER_ID_AES, /**< The AES cipher. */ + MBEDTLS_CIPHER_ID_DES, /**< The DES cipher. \warning DES is considered weak. */ + MBEDTLS_CIPHER_ID_3DES, /**< The Triple DES cipher. \warning 3DES is considered weak. */ + MBEDTLS_CIPHER_ID_CAMELLIA, /**< The Camellia cipher. */ + MBEDTLS_CIPHER_ID_ARIA, /**< The Aria cipher. */ + MBEDTLS_CIPHER_ID_CHACHA20, /**< The ChaCha20 cipher. */ +} mbedtls_cipher_id_t; + +/** + * \brief Supported {cipher type, cipher mode} pairs. + * + * \warning DES/3DES are considered weak ciphers and their use + * constitutes a security risk. We recommend considering stronger + * ciphers instead. + */ +typedef enum { + MBEDTLS_CIPHER_NONE = 0, /**< Placeholder to mark the end of cipher-pair lists. */ + MBEDTLS_CIPHER_NULL, /**< The identity stream cipher. */ + MBEDTLS_CIPHER_AES_128_ECB, /**< AES cipher with 128-bit ECB mode. */ + MBEDTLS_CIPHER_AES_192_ECB, /**< AES cipher with 192-bit ECB mode. */ + MBEDTLS_CIPHER_AES_256_ECB, /**< AES cipher with 256-bit ECB mode. */ + MBEDTLS_CIPHER_AES_128_CBC, /**< AES cipher with 128-bit CBC mode. */ + MBEDTLS_CIPHER_AES_192_CBC, /**< AES cipher with 192-bit CBC mode. */ + MBEDTLS_CIPHER_AES_256_CBC, /**< AES cipher with 256-bit CBC mode. */ + MBEDTLS_CIPHER_AES_128_CFB128, /**< AES cipher with 128-bit CFB128 mode. */ + MBEDTLS_CIPHER_AES_192_CFB128, /**< AES cipher with 192-bit CFB128 mode. */ + MBEDTLS_CIPHER_AES_256_CFB128, /**< AES cipher with 256-bit CFB128 mode. */ + MBEDTLS_CIPHER_AES_128_CTR, /**< AES cipher with 128-bit CTR mode. */ + MBEDTLS_CIPHER_AES_192_CTR, /**< AES cipher with 192-bit CTR mode. */ + MBEDTLS_CIPHER_AES_256_CTR, /**< AES cipher with 256-bit CTR mode. */ + MBEDTLS_CIPHER_AES_128_GCM, /**< AES cipher with 128-bit GCM mode. */ + MBEDTLS_CIPHER_AES_192_GCM, /**< AES cipher with 192-bit GCM mode. */ + MBEDTLS_CIPHER_AES_256_GCM, /**< AES cipher with 256-bit GCM mode. */ + MBEDTLS_CIPHER_CAMELLIA_128_ECB, /**< Camellia cipher with 128-bit ECB mode. */ + MBEDTLS_CIPHER_CAMELLIA_192_ECB, /**< Camellia cipher with 192-bit ECB mode. */ + MBEDTLS_CIPHER_CAMELLIA_256_ECB, /**< Camellia cipher with 256-bit ECB mode. */ + MBEDTLS_CIPHER_CAMELLIA_128_CBC, /**< Camellia cipher with 128-bit CBC mode. */ + MBEDTLS_CIPHER_CAMELLIA_192_CBC, /**< Camellia cipher with 192-bit CBC mode. */ + MBEDTLS_CIPHER_CAMELLIA_256_CBC, /**< Camellia cipher with 256-bit CBC mode. */ + MBEDTLS_CIPHER_CAMELLIA_128_CFB128, /**< Camellia cipher with 128-bit CFB128 mode. */ + MBEDTLS_CIPHER_CAMELLIA_192_CFB128, /**< Camellia cipher with 192-bit CFB128 mode. */ + MBEDTLS_CIPHER_CAMELLIA_256_CFB128, /**< Camellia cipher with 256-bit CFB128 mode. */ + MBEDTLS_CIPHER_CAMELLIA_128_CTR, /**< Camellia cipher with 128-bit CTR mode. */ + MBEDTLS_CIPHER_CAMELLIA_192_CTR, /**< Camellia cipher with 192-bit CTR mode. */ + MBEDTLS_CIPHER_CAMELLIA_256_CTR, /**< Camellia cipher with 256-bit CTR mode. */ + MBEDTLS_CIPHER_CAMELLIA_128_GCM, /**< Camellia cipher with 128-bit GCM mode. */ + MBEDTLS_CIPHER_CAMELLIA_192_GCM, /**< Camellia cipher with 192-bit GCM mode. */ + MBEDTLS_CIPHER_CAMELLIA_256_GCM, /**< Camellia cipher with 256-bit GCM mode. */ + MBEDTLS_CIPHER_DES_ECB, /**< DES cipher with ECB mode. \warning DES is considered weak. */ + MBEDTLS_CIPHER_DES_CBC, /**< DES cipher with CBC mode. \warning DES is considered weak. */ + MBEDTLS_CIPHER_DES_EDE_ECB, /**< DES cipher with EDE ECB mode. \warning 3DES is considered weak. */ + MBEDTLS_CIPHER_DES_EDE_CBC, /**< DES cipher with EDE CBC mode. \warning 3DES is considered weak. */ + MBEDTLS_CIPHER_DES_EDE3_ECB, /**< DES cipher with EDE3 ECB mode. \warning 3DES is considered weak. */ + MBEDTLS_CIPHER_DES_EDE3_CBC, /**< DES cipher with EDE3 CBC mode. \warning 3DES is considered weak. */ + MBEDTLS_CIPHER_AES_128_CCM, /**< AES cipher with 128-bit CCM mode. */ + MBEDTLS_CIPHER_AES_192_CCM, /**< AES cipher with 192-bit CCM mode. */ + MBEDTLS_CIPHER_AES_256_CCM, /**< AES cipher with 256-bit CCM mode. */ + MBEDTLS_CIPHER_AES_128_CCM_STAR_NO_TAG, /**< AES cipher with 128-bit CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_AES_192_CCM_STAR_NO_TAG, /**< AES cipher with 192-bit CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_AES_256_CCM_STAR_NO_TAG, /**< AES cipher with 256-bit CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_CAMELLIA_128_CCM, /**< Camellia cipher with 128-bit CCM mode. */ + MBEDTLS_CIPHER_CAMELLIA_192_CCM, /**< Camellia cipher with 192-bit CCM mode. */ + MBEDTLS_CIPHER_CAMELLIA_256_CCM, /**< Camellia cipher with 256-bit CCM mode. */ + MBEDTLS_CIPHER_CAMELLIA_128_CCM_STAR_NO_TAG, /**< Camellia cipher with 128-bit CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_CAMELLIA_192_CCM_STAR_NO_TAG, /**< Camellia cipher with 192-bit CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_CAMELLIA_256_CCM_STAR_NO_TAG, /**< Camellia cipher with 256-bit CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_ARIA_128_ECB, /**< Aria cipher with 128-bit key and ECB mode. */ + MBEDTLS_CIPHER_ARIA_192_ECB, /**< Aria cipher with 192-bit key and ECB mode. */ + MBEDTLS_CIPHER_ARIA_256_ECB, /**< Aria cipher with 256-bit key and ECB mode. */ + MBEDTLS_CIPHER_ARIA_128_CBC, /**< Aria cipher with 128-bit key and CBC mode. */ + MBEDTLS_CIPHER_ARIA_192_CBC, /**< Aria cipher with 192-bit key and CBC mode. */ + MBEDTLS_CIPHER_ARIA_256_CBC, /**< Aria cipher with 256-bit key and CBC mode. */ + MBEDTLS_CIPHER_ARIA_128_CFB128, /**< Aria cipher with 128-bit key and CFB-128 mode. */ + MBEDTLS_CIPHER_ARIA_192_CFB128, /**< Aria cipher with 192-bit key and CFB-128 mode. */ + MBEDTLS_CIPHER_ARIA_256_CFB128, /**< Aria cipher with 256-bit key and CFB-128 mode. */ + MBEDTLS_CIPHER_ARIA_128_CTR, /**< Aria cipher with 128-bit key and CTR mode. */ + MBEDTLS_CIPHER_ARIA_192_CTR, /**< Aria cipher with 192-bit key and CTR mode. */ + MBEDTLS_CIPHER_ARIA_256_CTR, /**< Aria cipher with 256-bit key and CTR mode. */ + MBEDTLS_CIPHER_ARIA_128_GCM, /**< Aria cipher with 128-bit key and GCM mode. */ + MBEDTLS_CIPHER_ARIA_192_GCM, /**< Aria cipher with 192-bit key and GCM mode. */ + MBEDTLS_CIPHER_ARIA_256_GCM, /**< Aria cipher with 256-bit key and GCM mode. */ + MBEDTLS_CIPHER_ARIA_128_CCM, /**< Aria cipher with 128-bit key and CCM mode. */ + MBEDTLS_CIPHER_ARIA_192_CCM, /**< Aria cipher with 192-bit key and CCM mode. */ + MBEDTLS_CIPHER_ARIA_256_CCM, /**< Aria cipher with 256-bit key and CCM mode. */ + MBEDTLS_CIPHER_ARIA_128_CCM_STAR_NO_TAG, /**< Aria cipher with 128-bit key and CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_ARIA_192_CCM_STAR_NO_TAG, /**< Aria cipher with 192-bit key and CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_ARIA_256_CCM_STAR_NO_TAG, /**< Aria cipher with 256-bit key and CCM_STAR_NO_TAG mode. */ + MBEDTLS_CIPHER_AES_128_OFB, /**< AES 128-bit cipher in OFB mode. */ + MBEDTLS_CIPHER_AES_192_OFB, /**< AES 192-bit cipher in OFB mode. */ + MBEDTLS_CIPHER_AES_256_OFB, /**< AES 256-bit cipher in OFB mode. */ + MBEDTLS_CIPHER_AES_128_XTS, /**< AES 128-bit cipher in XTS block mode. */ + MBEDTLS_CIPHER_AES_256_XTS, /**< AES 256-bit cipher in XTS block mode. */ + MBEDTLS_CIPHER_CHACHA20, /**< ChaCha20 stream cipher. */ + MBEDTLS_CIPHER_CHACHA20_POLY1305, /**< ChaCha20-Poly1305 AEAD cipher. */ + MBEDTLS_CIPHER_AES_128_KW, /**< AES cipher with 128-bit NIST KW mode. */ + MBEDTLS_CIPHER_AES_192_KW, /**< AES cipher with 192-bit NIST KW mode. */ + MBEDTLS_CIPHER_AES_256_KW, /**< AES cipher with 256-bit NIST KW mode. */ + MBEDTLS_CIPHER_AES_128_KWP, /**< AES cipher with 128-bit NIST KWP mode. */ + MBEDTLS_CIPHER_AES_192_KWP, /**< AES cipher with 192-bit NIST KWP mode. */ + MBEDTLS_CIPHER_AES_256_KWP, /**< AES cipher with 256-bit NIST KWP mode. */ +} mbedtls_cipher_type_t; + +/** Supported cipher modes. */ +typedef enum { + MBEDTLS_MODE_NONE = 0, /**< None. */ + MBEDTLS_MODE_ECB, /**< The ECB cipher mode. */ + MBEDTLS_MODE_CBC, /**< The CBC cipher mode. */ + MBEDTLS_MODE_CFB, /**< The CFB cipher mode. */ + MBEDTLS_MODE_OFB, /**< The OFB cipher mode. */ + MBEDTLS_MODE_CTR, /**< The CTR cipher mode. */ + MBEDTLS_MODE_GCM, /**< The GCM cipher mode. */ + MBEDTLS_MODE_STREAM, /**< The stream cipher mode. */ + MBEDTLS_MODE_CCM, /**< The CCM cipher mode. */ + MBEDTLS_MODE_CCM_STAR_NO_TAG, /**< The CCM*-no-tag cipher mode. */ + MBEDTLS_MODE_XTS, /**< The XTS cipher mode. */ + MBEDTLS_MODE_CHACHAPOLY, /**< The ChaCha-Poly cipher mode. */ + MBEDTLS_MODE_KW, /**< The SP800-38F KW mode */ + MBEDTLS_MODE_KWP, /**< The SP800-38F KWP mode */ +} mbedtls_cipher_mode_t; + +/** Supported cipher padding types. */ +typedef enum { + MBEDTLS_PADDING_PKCS7 = 0, /**< PKCS7 padding (default). */ + MBEDTLS_PADDING_ONE_AND_ZEROS, /**< ISO/IEC 7816-4 padding. */ + MBEDTLS_PADDING_ZEROS_AND_LEN, /**< ANSI X.923 padding. */ + MBEDTLS_PADDING_ZEROS, /**< Zero padding (not reversible). */ + MBEDTLS_PADDING_NONE, /**< Never pad (full blocks only). */ +} mbedtls_cipher_padding_t; + +/** Type of operation. */ +typedef enum { + MBEDTLS_OPERATION_NONE = -1, + MBEDTLS_DECRYPT = 0, + MBEDTLS_ENCRYPT, +} mbedtls_operation_t; + +enum { + /** Undefined key length. */ + MBEDTLS_KEY_LENGTH_NONE = 0, + /** Key length, in bits (including parity), for DES keys. \warning DES is considered weak. */ + MBEDTLS_KEY_LENGTH_DES = 64, + /** Key length in bits, including parity, for DES in two-key EDE. \warning 3DES is considered weak. */ + MBEDTLS_KEY_LENGTH_DES_EDE = 128, + /** Key length in bits, including parity, for DES in three-key EDE. \warning 3DES is considered weak. */ + MBEDTLS_KEY_LENGTH_DES_EDE3 = 192, +}; + +/** Maximum length of any IV, in Bytes. */ +/* This should ideally be derived automatically from list of ciphers. + * This should be kept in sync with MBEDTLS_SSL_MAX_IV_LENGTH defined + * in library/ssl_misc.h. */ +#define MBEDTLS_MAX_IV_LENGTH 16 + +/** Maximum block size of any cipher, in Bytes. */ +/* This should ideally be derived automatically from list of ciphers. + * This should be kept in sync with MBEDTLS_SSL_MAX_BLOCK_LENGTH defined + * in library/ssl_misc.h. */ +#define MBEDTLS_MAX_BLOCK_LENGTH 16 + +/** Maximum key length, in Bytes. */ +/* This should ideally be derived automatically from list of ciphers. + * For now, only check whether XTS is enabled which uses 64 Byte keys, + * and use 32 Bytes as an upper bound for the maximum key length otherwise. + * This should be kept in sync with MBEDTLS_SSL_MAX_BLOCK_LENGTH defined + * in library/ssl_misc.h, which however deliberately ignores the case of XTS + * since the latter isn't used in SSL/TLS. */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) +#define MBEDTLS_MAX_KEY_LENGTH 64 +#else +#define MBEDTLS_MAX_KEY_LENGTH 32 +#endif /* MBEDTLS_CIPHER_MODE_XTS */ + +/** + * Base cipher information (opaque struct). + */ +typedef struct mbedtls_cipher_base_t mbedtls_cipher_base_t; + +/** + * CMAC context (opaque struct). + */ +typedef struct mbedtls_cmac_context_t mbedtls_cmac_context_t; + +/** + * Cipher information. Allows calling cipher functions + * in a generic way. + * + * \note The library does not support custom cipher info structures, + * only built-in structures returned by the functions + * mbedtls_cipher_info_from_string(), + * mbedtls_cipher_info_from_type(), + * mbedtls_cipher_info_from_values(), + * mbedtls_cipher_info_from_psa(). + */ +typedef struct mbedtls_cipher_info_t { + /** Full cipher identifier. For example, + * MBEDTLS_CIPHER_AES_256_CBC. + */ + mbedtls_cipher_type_t MBEDTLS_PRIVATE(type); + + /** The cipher mode. For example, MBEDTLS_MODE_CBC. */ + mbedtls_cipher_mode_t MBEDTLS_PRIVATE(mode); + + /** The cipher key length, in bits. This is the + * default length for variable sized ciphers. + * Includes parity bits for ciphers like DES. + */ + unsigned int MBEDTLS_PRIVATE(key_bitlen); + + /** Name of the cipher. */ + const char *MBEDTLS_PRIVATE(name); + + /** IV or nonce size, in Bytes. + * For ciphers that accept variable IV sizes, + * this is the recommended size. + */ + unsigned int MBEDTLS_PRIVATE(iv_size); + + /** Bitflag comprised of MBEDTLS_CIPHER_VARIABLE_IV_LEN and + * MBEDTLS_CIPHER_VARIABLE_KEY_LEN indicating whether the + * cipher supports variable IV or variable key sizes, respectively. + */ + int MBEDTLS_PRIVATE(flags); + + /** The block size, in Bytes. */ + unsigned int MBEDTLS_PRIVATE(block_size); + + /** Struct for base cipher information and functions. */ + const mbedtls_cipher_base_t *MBEDTLS_PRIVATE(base); + +} mbedtls_cipher_info_t; + +/** + * Generic cipher context. + */ +typedef struct mbedtls_cipher_context_t { + /** Information about the associated cipher. */ + const mbedtls_cipher_info_t *MBEDTLS_PRIVATE(cipher_info); + + /** Key length to use. */ + int MBEDTLS_PRIVATE(key_bitlen); + + /** Operation that the key of the context has been + * initialized for. + */ + mbedtls_operation_t MBEDTLS_PRIVATE(operation); + +#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) + /** 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); +#endif + + /** Buffer for input that has not been processed yet. */ + unsigned char MBEDTLS_PRIVATE(unprocessed_data)[MBEDTLS_MAX_BLOCK_LENGTH]; + + /** Number of Bytes that have not been processed yet. */ + size_t MBEDTLS_PRIVATE(unprocessed_len); + + /** Current IV or NONCE_COUNTER for CTR-mode, data unit (or sector) number + * for XTS-mode. */ + unsigned char MBEDTLS_PRIVATE(iv)[MBEDTLS_MAX_IV_LENGTH]; + + /** IV size in Bytes, for ciphers with variable-length IVs. */ + size_t MBEDTLS_PRIVATE(iv_size); + + /** The cipher-specific context. */ + void *MBEDTLS_PRIVATE(cipher_ctx); + +#if defined(MBEDTLS_CMAC_C) + /** CMAC-specific context. */ + mbedtls_cmac_context_t *MBEDTLS_PRIVATE(cmac_ctx); +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + /** Indicates whether the cipher operations should be performed + * by Mbed TLS' own crypto library or an external implementation + * of the PSA Crypto API. + * This is unset if the cipher context was established through + * mbedtls_cipher_setup(), and set if it was established through + * mbedtls_cipher_setup_psa(). + */ + unsigned char MBEDTLS_PRIVATE(psa_enabled); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +} mbedtls_cipher_context_t; + +/** + * \brief This function retrieves the list of ciphers supported + * by the generic cipher module. + * + * For any cipher identifier in the returned list, you can + * obtain the corresponding generic cipher information structure + * via mbedtls_cipher_info_from_type(), which can then be used + * to prepare a cipher context via mbedtls_cipher_setup(). + * + * + * \return A statically-allocated array of cipher identifiers + * of type cipher_type_t. The last entry is zero. + */ +const int *mbedtls_cipher_list(void); + +/** + * \brief This function retrieves the cipher-information + * structure associated with the given cipher name. + * + * \param cipher_name Name of the cipher to search for. This must not be + * \c NULL. + * + * \return The cipher information structure associated with the + * given \p cipher_name. + * \return \c NULL if the associated cipher information is not found. + */ +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_string(const char *cipher_name); + +/** + * \brief This function retrieves the cipher-information + * structure associated with the given cipher type. + * + * \param cipher_type Type of the cipher to search for. + * + * \return The cipher information structure associated with the + * given \p cipher_type. + * \return \c NULL if the associated cipher information is not found. + */ +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_type(const mbedtls_cipher_type_t cipher_type); + +/** + * \brief This function retrieves the cipher-information + * structure associated with the given cipher ID, + * key size and mode. + * + * \param cipher_id The ID of the cipher to search for. For example, + * #MBEDTLS_CIPHER_ID_AES. + * \param key_bitlen The length of the key in bits. + * \param mode The cipher mode. For example, #MBEDTLS_MODE_CBC. + * + * \return The cipher information structure associated with the + * given \p cipher_id. + * \return \c NULL if the associated cipher information is not found. + */ +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_values(const mbedtls_cipher_id_t cipher_id, + int key_bitlen, + const mbedtls_cipher_mode_t mode); + +/** + * \brief Retrieve the identifier for a cipher info structure. + * + * \param[in] info The cipher info structure to query. + * This may be \c NULL. + * + * \return The full cipher identifier (\c MBEDTLS_CIPHER_xxx). + * \return #MBEDTLS_CIPHER_NONE if \p info is \c NULL. + */ +static inline mbedtls_cipher_type_t mbedtls_cipher_info_get_type( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return MBEDTLS_CIPHER_NONE; + } else { + return info->MBEDTLS_PRIVATE(type); + } +} + +/** + * \brief Retrieve the operation mode for a cipher info structure. + * + * \param[in] info The cipher info structure to query. + * This may be \c NULL. + * + * \return The cipher mode (\c MBEDTLS_MODE_xxx). + * \return #MBEDTLS_MODE_NONE if \p info is \c NULL. + */ +static inline mbedtls_cipher_mode_t mbedtls_cipher_info_get_mode( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return MBEDTLS_MODE_NONE; + } else { + return info->MBEDTLS_PRIVATE(mode); + } +} + +/** + * \brief Retrieve the key size for a cipher info structure. + * + * \param[in] info The cipher info structure to query. + * This may be \c NULL. + * + * \return The key length in bits. + * For variable-sized ciphers, this is the default length. + * For DES, this includes the parity bits. + * \return \c 0 if \p info is \c NULL. + */ +static inline size_t mbedtls_cipher_info_get_key_bitlen( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return 0; + } else { + return info->MBEDTLS_PRIVATE(key_bitlen); + } +} + +/** + * \brief Retrieve the human-readable name for a + * cipher info structure. + * + * \param[in] info The cipher info structure to query. + * This may be \c NULL. + * + * \return The cipher name, which is a human readable string, + * with static storage duration. + * \return \c NULL if \c info is \p NULL. + */ +static inline const char *mbedtls_cipher_info_get_name( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return NULL; + } else { + return info->MBEDTLS_PRIVATE(name); + } +} + +/** + * \brief This function returns the size of the IV or nonce + * for the cipher info structure, in bytes. + * + * \param info The cipher info structure. This may be \c NULL. + * + * \return The recommended IV size. + * \return \c 0 for ciphers not using an IV or a nonce. + * \return \c 0 if \p info is \c NULL. + */ +static inline size_t mbedtls_cipher_info_get_iv_size( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return 0; + } + + return (size_t) info->MBEDTLS_PRIVATE(iv_size); +} + +/** + * \brief This function returns the block size of the given + * cipher info structure in bytes. + * + * \param info The cipher info structure. This may be \c NULL. + * + * \return The block size of the cipher. + * \return \c 1 if the cipher is a stream cipher. + * \return \c 0 if \p info is \c NULL. + */ +static inline size_t mbedtls_cipher_info_get_block_size( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return 0; + } + + return (size_t) info->MBEDTLS_PRIVATE(block_size); +} + +/** + * \brief This function returns a non-zero value if the key length for + * the given cipher is variable. + * + * \param info The cipher info structure. This may be \c NULL. + * + * \return Non-zero if the key length is variable, \c 0 otherwise. + * \return \c 0 if the given pointer is \c NULL. + */ +static inline int mbedtls_cipher_info_has_variable_key_bitlen( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return 0; + } + + return info->MBEDTLS_PRIVATE(flags) & MBEDTLS_CIPHER_VARIABLE_KEY_LEN; +} + +/** + * \brief This function returns a non-zero value if the IV size for + * the given cipher is variable. + * + * \param info The cipher info structure. This may be \c NULL. + * + * \return Non-zero if the IV size is variable, \c 0 otherwise. + * \return \c 0 if the given pointer is \c NULL. + */ +static inline int mbedtls_cipher_info_has_variable_iv_size( + const mbedtls_cipher_info_t *info) +{ + if (info == NULL) { + return 0; + } + + return info->MBEDTLS_PRIVATE(flags) & MBEDTLS_CIPHER_VARIABLE_IV_LEN; +} + +/** + * \brief This function initializes a \p cipher_context as NONE. + * + * \param ctx The context to be initialized. This must not be \c NULL. + */ +void mbedtls_cipher_init(mbedtls_cipher_context_t *ctx); + +/** + * \brief This function frees and clears the cipher-specific + * context of \p ctx. Freeing \p ctx itself remains the + * responsibility of the caller. + * + * \param ctx The context to be freed. If this is \c NULL, the + * function has no effect, otherwise this must point to an + * initialized context. + */ +void mbedtls_cipher_free(mbedtls_cipher_context_t *ctx); + + +/** + * \brief This function prepares a cipher context for + * use with the given cipher primitive. + * + * \note After calling this function, you should call + * mbedtls_cipher_setkey() and, if the mode uses padding, + * mbedtls_cipher_set_padding_mode(), then for each + * message to encrypt or decrypt with this key, either: + * - mbedtls_cipher_crypt() for one-shot processing with + * non-AEAD modes; + * - mbedtls_cipher_auth_encrypt_ext() or + * mbedtls_cipher_auth_decrypt_ext() for one-shot + * processing with AEAD modes or NIST_KW; + * - for multi-part processing, see the documentation of + * mbedtls_cipher_reset(). + * + * \param ctx The context to prepare. This must be initialized by + * a call to mbedtls_cipher_init() first. + * \param cipher_info The cipher to use. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + * \return #MBEDTLS_ERR_CIPHER_ALLOC_FAILED if allocation of the + * cipher-specific context fails. + */ +int mbedtls_cipher_setup(mbedtls_cipher_context_t *ctx, + const mbedtls_cipher_info_t *cipher_info); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief This function initializes a cipher context for + * PSA-based use with the given cipher primitive. + * + * \deprecated This function is deprecated and will be removed in a + * future version of the library. + * Please use psa_aead_xxx() / psa_cipher_xxx() directly + * instead. + * + * \note See #MBEDTLS_USE_PSA_CRYPTO for information on PSA. + * + * \param ctx The context to initialize. May not be \c NULL. + * \param cipher_info The cipher to use. + * \param taglen For AEAD ciphers, the length in bytes of the + * authentication tag to use. Subsequent uses of + * mbedtls_cipher_auth_encrypt_ext() or + * mbedtls_cipher_auth_decrypt_ext() must provide + * the same tag length. + * For non-AEAD ciphers, the value must be \c 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + * \return #MBEDTLS_ERR_CIPHER_ALLOC_FAILED if allocation of the + * cipher-specific context fails. + */ +int MBEDTLS_DEPRECATED mbedtls_cipher_setup_psa(mbedtls_cipher_context_t *ctx, + const mbedtls_cipher_info_t *cipher_info, + size_t taglen); +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +/** + * \brief This function returns the block size of the given cipher + * in bytes. + * + * \param ctx The context of the cipher. + * + * \return The block size of the underlying cipher. + * \return \c 1 if the cipher is a stream cipher. + * \return \c 0 if \p ctx has not been initialized. + */ +static inline unsigned int mbedtls_cipher_get_block_size( + const mbedtls_cipher_context_t *ctx) +{ + MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, 0); + if (ctx->MBEDTLS_PRIVATE(cipher_info) == NULL) { + return 0; + } + + return ctx->MBEDTLS_PRIVATE(cipher_info)->MBEDTLS_PRIVATE(block_size); +} + +/** + * \brief This function returns the mode of operation for + * the cipher. For example, MBEDTLS_MODE_CBC. + * + * \param ctx The context of the cipher. This must be initialized. + * + * \return The mode of operation. + * \return #MBEDTLS_MODE_NONE if \p ctx has not been initialized. + */ +static inline mbedtls_cipher_mode_t mbedtls_cipher_get_cipher_mode( + const mbedtls_cipher_context_t *ctx) +{ + MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, MBEDTLS_MODE_NONE); + if (ctx->MBEDTLS_PRIVATE(cipher_info) == NULL) { + return MBEDTLS_MODE_NONE; + } + + return ctx->MBEDTLS_PRIVATE(cipher_info)->MBEDTLS_PRIVATE(mode); +} + +/** + * \brief This function returns the size of the IV or nonce + * of the cipher, in Bytes. + * + * \param ctx The context of the cipher. This must be initialized. + * + * \return The recommended IV size if no IV has been set. + * \return \c 0 for ciphers not using an IV or a nonce. + * \return The actual size if an IV has been set. + */ +static inline int mbedtls_cipher_get_iv_size( + const mbedtls_cipher_context_t *ctx) +{ + MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, 0); + if (ctx->MBEDTLS_PRIVATE(cipher_info) == NULL) { + return 0; + } + + if (ctx->MBEDTLS_PRIVATE(iv_size) != 0) { + return (int) ctx->MBEDTLS_PRIVATE(iv_size); + } + + return (int) ctx->MBEDTLS_PRIVATE(cipher_info)->MBEDTLS_PRIVATE(iv_size); +} + +/** + * \brief This function returns the type of the given cipher. + * + * \param ctx The context of the cipher. This must be initialized. + * + * \return The type of the cipher. + * \return #MBEDTLS_CIPHER_NONE if \p ctx has not been initialized. + */ +static inline mbedtls_cipher_type_t mbedtls_cipher_get_type( + const mbedtls_cipher_context_t *ctx) +{ + MBEDTLS_INTERNAL_VALIDATE_RET( + ctx != NULL, MBEDTLS_CIPHER_NONE); + if (ctx->MBEDTLS_PRIVATE(cipher_info) == NULL) { + return MBEDTLS_CIPHER_NONE; + } + + return ctx->MBEDTLS_PRIVATE(cipher_info)->MBEDTLS_PRIVATE(type); +} + +/** + * \brief This function returns the name of the given cipher + * as a string. + * + * \param ctx The context of the cipher. This must be initialized. + * + * \return The name of the cipher. + * \return NULL if \p ctx has not been not initialized. + */ +static inline const char *mbedtls_cipher_get_name( + const mbedtls_cipher_context_t *ctx) +{ + MBEDTLS_INTERNAL_VALIDATE_RET(ctx != NULL, 0); + if (ctx->MBEDTLS_PRIVATE(cipher_info) == NULL) { + return 0; + } + + return ctx->MBEDTLS_PRIVATE(cipher_info)->MBEDTLS_PRIVATE(name); +} + +/** + * \brief This function returns the key length of the cipher. + * + * \param ctx The context of the cipher. This must be initialized. + * + * \return The key length of the cipher in bits. + * \return #MBEDTLS_KEY_LENGTH_NONE if ctx \p has not been + * initialized. + */ +static inline int mbedtls_cipher_get_key_bitlen( + const mbedtls_cipher_context_t *ctx) +{ + MBEDTLS_INTERNAL_VALIDATE_RET( + ctx != NULL, MBEDTLS_KEY_LENGTH_NONE); + if (ctx->MBEDTLS_PRIVATE(cipher_info) == NULL) { + return MBEDTLS_KEY_LENGTH_NONE; + } + + return (int) ctx->MBEDTLS_PRIVATE(cipher_info)->MBEDTLS_PRIVATE(key_bitlen); +} + +/** + * \brief This function returns the operation of the given cipher. + * + * \param ctx The context of the cipher. This must be initialized. + * + * \return The type of operation: #MBEDTLS_ENCRYPT or #MBEDTLS_DECRYPT. + * \return #MBEDTLS_OPERATION_NONE if \p ctx has not been initialized. + */ +static inline mbedtls_operation_t mbedtls_cipher_get_operation( + const mbedtls_cipher_context_t *ctx) +{ + MBEDTLS_INTERNAL_VALIDATE_RET( + ctx != NULL, MBEDTLS_OPERATION_NONE); + if (ctx->MBEDTLS_PRIVATE(cipher_info) == NULL) { + return MBEDTLS_OPERATION_NONE; + } + + return ctx->MBEDTLS_PRIVATE(operation); +} + +/** + * \brief This function sets the key to use with the given context. + * + * \param ctx The generic cipher context. This must be initialized and + * bound to a cipher information structure. + * \param key The key to use. This must be a readable buffer of at + * least \p key_bitlen Bits. + * \param key_bitlen The key length to use, in Bits. + * \param operation The operation that the key will be used for: + * #MBEDTLS_ENCRYPT or #MBEDTLS_DECRYPT. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_setkey(mbedtls_cipher_context_t *ctx, + const unsigned char *key, + int key_bitlen, + const mbedtls_operation_t operation); + +#if defined(MBEDTLS_CIPHER_MODE_WITH_PADDING) +/** + * \brief This function sets the padding mode, for cipher modes + * that use padding. + * + * The default passing mode is PKCS7 padding. + * + * \param ctx The generic cipher context. This must be initialized and + * bound to a cipher information structure. + * \param mode The padding mode. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE + * if the selected padding mode is not supported. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA if the cipher mode + * does not support padding. + */ +int mbedtls_cipher_set_padding_mode(mbedtls_cipher_context_t *ctx, + mbedtls_cipher_padding_t mode); +#endif /* MBEDTLS_CIPHER_MODE_WITH_PADDING */ + +/** + * \brief This function sets the initialization vector (IV) + * or nonce. + * + * \note Some ciphers do not use IVs nor nonce. For these + * ciphers, this function has no effect. + * + * \note For #MBEDTLS_CIPHER_CHACHA20, the nonce length must + * be 12, and the initial counter value is 0. + * + * \note For #MBEDTLS_CIPHER_CHACHA20_POLY1305, the nonce length + * must be 12. + * + * \param ctx The generic cipher context. This must be initialized and + * bound to a cipher information structure. + * \param iv The IV to use, or NONCE_COUNTER for CTR-mode ciphers. This + * must be a readable buffer of at least \p iv_len Bytes. + * \param iv_len The IV length for ciphers with variable-size IV. + * This parameter is discarded by ciphers with fixed-size IV. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + */ +int mbedtls_cipher_set_iv(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, + size_t iv_len); + +/** + * \brief This function resets the cipher state. + * + * \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() + * . + * 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() + * 6. mbedtls_cipher_check_tag() (for decryption) or + * mbedtls_cipher_write_tag() (for encryption). + * . + * This sequence can be repeated to encrypt or decrypt multiple + * messages with the same key. + * + * \param ctx The generic cipher context. This must be bound to a key. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + */ +int mbedtls_cipher_reset(mbedtls_cipher_context_t *ctx); + +#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C) +/** + * \brief This function adds additional data for AEAD ciphers. + * Currently supported with GCM and ChaCha20+Poly1305. + * + * \param ctx The generic cipher context. This must be initialized. + * \param ad The additional data to use. This must be a readable + * buffer of at least \p ad_len Bytes. + * \param ad_len The length of \p ad in Bytes. + * + * \return \c 0 on success. + * \return A specific error code on failure. + */ +int mbedtls_cipher_update_ad(mbedtls_cipher_context_t *ctx, + const unsigned char *ad, size_t ad_len); +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ + +/** + * \brief The generic cipher update function. It encrypts or + * decrypts using the given cipher context. Writes as + * 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. + * Exception: For MBEDTLS_MODE_ECB, expects a single block + * in size. For example, 16 Bytes for AES. + * + * \param ctx The generic cipher context. This must be initialized and + * bound to a key. + * \param input The buffer holding the input data. This must be a + * readable buffer of at least \p ilen Bytes. + * \param ilen The length of the input data. + * \param output The buffer for the output data. This must be able to + * hold at least `ilen + block_size`. This must not be the + * same buffer as \p input. + * \param olen The length of the output data, to be updated with the + * actual number of Bytes written. This must not be + * \c NULL. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + * \return #MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE on an + * unsupported mode for a cipher. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_update(mbedtls_cipher_context_t *ctx, + const unsigned char *input, + size_t ilen, 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. + * + * \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 \p block_size Bytes. + * \param olen The length of the data written to the \p output buffer. + * This may not be \c NULL. + * + * \return \c 0 on success. + * \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 #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding + * while decrypting. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_finish(mbedtls_cipher_context_t *ctx, + unsigned char *output, size_t *olen); + +#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(). + * + * \param ctx The generic cipher context. This must be initialized, + * bound to a key, and have just completed a cipher + * operation through mbedtls_cipher_finish() the tag for + * which should be written. + * \param tag The buffer to write the tag to. This must be a writable + * buffer of at least \p tag_len Bytes. + * \param tag_len The length of the tag to write. + * + * \return \c 0 on success. + * \return A specific error code on failure. + */ +int mbedtls_cipher_write_tag(mbedtls_cipher_context_t *ctx, + unsigned char *tag, size_t tag_len); + +/** + * \brief This function checks the tag for AEAD ciphers. + * Currently supported with GCM and ChaCha20+Poly1305. + * This must be called after mbedtls_cipher_finish(). + * + * \param ctx The generic cipher context. This must be initialized. + * \param tag The buffer holding the tag. This must be a readable + * buffer of at least \p tag_len Bytes. + * \param tag_len The length of the tag to check. + * + * \return \c 0 on success. + * \return A specific error code on failure. + */ +int mbedtls_cipher_check_tag(mbedtls_cipher_context_t *ctx, + const unsigned char *tag, size_t tag_len); +#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */ + +/** + * \brief The generic all-in-one encryption/decryption function, + * for all ciphers except AEAD constructs. + * + * \param ctx The generic cipher context. This must be initialized. + * \param iv The IV to use, or NONCE_COUNTER for CTR-mode ciphers. + * This must be a readable buffer of at least \p iv_len + * Bytes. + * \param iv_len The IV length for ciphers with variable-size IV. + * This parameter is discarded by ciphers with fixed-size + * IV. + * \param input The buffer holding the input data. This must be a + * readable buffer of at least \p ilen Bytes. + * \param ilen The length of the input data in Bytes. + * \param output The buffer for the output data. This must be able to + * hold at least `ilen + block_size`. This must not be the + * same buffer as \p input. + * \param olen The length of the output data, to be updated with the + * actual number of Bytes written. This must not be + * \c NULL. + * + * \note Some ciphers do not use IVs nor nonce. For these + * ciphers, use \p iv = NULL and \p iv_len = 0. + * + * \return \c 0 on success. + * \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 #MBEDTLS_ERR_CIPHER_INVALID_PADDING on invalid padding + * while decrypting. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_crypt(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen); + +#if defined(MBEDTLS_CIPHER_MODE_AEAD) || defined(MBEDTLS_NIST_KW_C) +/** + * \brief The authenticated encryption (AEAD/NIST_KW) function. + * + * \note For AEAD modes, the tag will be appended to the + * ciphertext, as recommended by RFC 5116. + * (NIST_KW doesn't have a separate tag.) + * + * \param ctx The generic cipher context. This must be initialized and + * bound to a key, with an AEAD algorithm or NIST_KW. + * \param iv The nonce to use. This must be a readable buffer of + * at least \p iv_len Bytes and may be \c NULL if \p + * iv_len is \c 0. + * \param iv_len The length of the nonce. For AEAD ciphers, this must + * satisfy the constraints imposed by the cipher used. + * For NIST_KW, this must be \c 0. + * \param ad The additional data to authenticate. This must be a + * readable buffer of at least \p ad_len Bytes, and may + * be \c NULL is \p ad_len is \c 0. + * \param ad_len The length of \p ad. For NIST_KW, this must be \c 0. + * \param input The buffer holding the input data. This must be a + * readable buffer of at least \p ilen Bytes, and may be + * \c NULL if \p ilen is \c 0. + * \param ilen The length of the input data. + * \param output The buffer for the output data. This must be a + * writable buffer of at least \p output_len Bytes, and + * must not be \c NULL. + * \param output_len The length of the \p output buffer in Bytes. For AEAD + * ciphers, this must be at least \p ilen + \p tag_len. + * For NIST_KW, this must be at least \p ilen + 8 + * (rounded up to a multiple of 8 if KWP is used); + * \p ilen + 15 is always a safe value. + * \param olen This will be filled with the actual number of Bytes + * written to the \p output buffer. This must point to a + * writable object of type \c size_t. + * \param tag_len The desired length of the authentication tag. For AEAD + * ciphers, this must match the constraints imposed by + * the cipher used, and in particular must not be \c 0. + * For NIST_KW, this must be \c 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_auth_encrypt_ext(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t output_len, + size_t *olen, size_t tag_len); + +/** + * \brief The authenticated encryption (AEAD/NIST_KW) function. + * + * \note If the data is not authentic, then the output buffer + * is zeroed out to prevent the unauthentic plaintext being + * used, making this interface safer. + * + * \note For AEAD modes, the tag must be appended to the + * ciphertext, as recommended by RFC 5116. + * (NIST_KW doesn't have a separate tag.) + * + * \param ctx The generic cipher context. This must be initialized and + * bound to a key, with an AEAD algorithm or NIST_KW. + * \param iv The nonce to use. This must be a readable buffer of + * at least \p iv_len Bytes and may be \c NULL if \p + * iv_len is \c 0. + * \param iv_len The length of the nonce. For AEAD ciphers, this must + * satisfy the constraints imposed by the cipher used. + * For NIST_KW, this must be \c 0. + * \param ad The additional data to authenticate. This must be a + * readable buffer of at least \p ad_len Bytes, and may + * be \c NULL is \p ad_len is \c 0. + * \param ad_len The length of \p ad. For NIST_KW, this must be \c 0. + * \param input The buffer holding the input data. This must be a + * readable buffer of at least \p ilen Bytes, and may be + * \c NULL if \p ilen is \c 0. + * \param ilen The length of the input data. For AEAD ciphers this + * must be at least \p tag_len. For NIST_KW this must be + * at least \c 8. + * \param output The buffer for the output data. This must be a + * writable buffer of at least \p output_len Bytes, and + * may be \c NULL if \p output_len is \c 0. + * \param output_len The length of the \p output buffer in Bytes. For AEAD + * ciphers, this must be at least \p ilen - \p tag_len. + * For NIST_KW, this must be at least \p ilen - 8. + * \param olen This will be filled with the actual number of Bytes + * written to the \p output buffer. This must point to a + * writable object of type \c size_t. + * \param tag_len The actual length of the authentication tag. For AEAD + * ciphers, this must match the constraints imposed by + * the cipher used, and in particular must not be \c 0. + * For NIST_KW, this must be \c 0. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA on + * parameter-verification failure. + * \return #MBEDTLS_ERR_CIPHER_AUTH_FAILED if data is not authentic. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_auth_decrypt_ext(mbedtls_cipher_context_t *ctx, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t output_len, + size_t *olen, size_t tag_len); +#endif /* MBEDTLS_CIPHER_MODE_AEAD || MBEDTLS_NIST_KW_C */ +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CIPHER_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/cmac.h b/r5dev/thirdparty/mbedtls/include/mbedtls/cmac.h new file mode 100644 index 00000000..3125e702 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/cmac.h @@ -0,0 +1,239 @@ +/** + * \file cmac.h + * + * \brief This file contains CMAC definitions and functions. + * + * The Cipher-based Message Authentication Code (CMAC) Mode for + * Authentication is defined in RFC-4493: The AES-CMAC Algorithm. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CMAC_H +#define MBEDTLS_CMAC_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/cipher.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MBEDTLS_AES_BLOCK_SIZE 16 +#define MBEDTLS_DES3_BLOCK_SIZE 8 + +#if defined(MBEDTLS_AES_C) +#define MBEDTLS_CIPHER_BLKSIZE_MAX 16 /**< The longest block used by CMAC is that of AES. */ +#else +#define MBEDTLS_CIPHER_BLKSIZE_MAX 8 /**< The longest block used by CMAC is that of 3DES. */ +#endif + +#if !defined(MBEDTLS_CMAC_ALT) + +/** + * The CMAC context structure. + */ +struct mbedtls_cmac_context_t { + /** The internal state of the CMAC algorithm. */ + unsigned char MBEDTLS_PRIVATE(state)[MBEDTLS_CIPHER_BLKSIZE_MAX]; + + /** Unprocessed data - either data that was not block aligned and is still + * pending processing, or the final block. */ + unsigned char MBEDTLS_PRIVATE(unprocessed_block)[MBEDTLS_CIPHER_BLKSIZE_MAX]; + + /** The length of data pending processing. */ + size_t MBEDTLS_PRIVATE(unprocessed_len); +}; + +#else /* !MBEDTLS_CMAC_ALT */ +#include "cmac_alt.h" +#endif /* !MBEDTLS_CMAC_ALT */ + +/** + * \brief This function starts a new CMAC computation + * by setting the CMAC key, and preparing to authenticate + * the input data. + * It must be called with an initialized cipher context. + * + * Once this function has completed, data can be supplied + * to the CMAC computation by calling + * mbedtls_cipher_cmac_update(). + * + * To start a CMAC computation using the same key as a previous + * CMAC computation, use mbedtls_cipher_cmac_finish(). + * + * \note When the CMAC implementation is supplied by an alternate + * implementation (through #MBEDTLS_CMAC_ALT), some ciphers + * may not be supported by that implementation, and thus + * return an error. Alternate implementations must support + * AES-128 and AES-256, and may support AES-192 and 3DES. + * + * \param ctx The cipher context used for the CMAC operation, initialized + * as one of the following types: MBEDTLS_CIPHER_AES_128_ECB, + * MBEDTLS_CIPHER_AES_192_ECB, MBEDTLS_CIPHER_AES_256_ECB, + * or MBEDTLS_CIPHER_DES_EDE3_ECB. + * \param key The CMAC key. + * \param keybits The length of the CMAC key in bits. + * Must be supported by the cipher. + * + * \return \c 0 on success. + * \return A cipher-specific error code on failure. + */ +int mbedtls_cipher_cmac_starts(mbedtls_cipher_context_t *ctx, + const unsigned char *key, size_t keybits); + +/** + * \brief This function feeds an input buffer into an ongoing CMAC + * computation. + * + * The CMAC computation must have previously been started + * by calling mbedtls_cipher_cmac_starts() or + * mbedtls_cipher_cmac_reset(). + * + * Call this function as many times as needed to input the + * data to be authenticated. + * Once all of the required data has been input, + * call mbedtls_cipher_cmac_finish() to obtain the result + * of the CMAC operation. + * + * \param ctx The cipher context used for the CMAC operation. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA + * if parameter verification fails. + */ +int mbedtls_cipher_cmac_update(mbedtls_cipher_context_t *ctx, + const unsigned char *input, size_t ilen); + +/** + * \brief This function finishes an ongoing CMAC operation, and + * writes the result to the output buffer. + * + * It should be followed either by + * mbedtls_cipher_cmac_reset(), which starts another CMAC + * operation with the same key, or mbedtls_cipher_free(), + * which clears the cipher context. + * + * \param ctx The cipher context used for the CMAC operation. + * \param output The output buffer for the CMAC checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA + * if parameter verification fails. + */ +int mbedtls_cipher_cmac_finish(mbedtls_cipher_context_t *ctx, + unsigned char *output); + +/** + * \brief This function starts a new CMAC operation with the same + * key as the previous one. + * + * It should be called after finishing the previous CMAC + * operation with mbedtls_cipher_cmac_finish(). + * After calling this function, + * call mbedtls_cipher_cmac_update() to supply the new + * CMAC operation with data. + * + * \param ctx The cipher context used for the CMAC operation. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA + * if parameter verification fails. + */ +int mbedtls_cipher_cmac_reset(mbedtls_cipher_context_t *ctx); + +/** + * \brief This function calculates the full generic CMAC + * on the input buffer with the provided key. + * + * The function allocates the context, performs the + * calculation, and frees the context. + * + * The CMAC result is calculated as + * output = generic CMAC(cmac key, input buffer). + * + * \note When the CMAC implementation is supplied by an alternate + * implementation (through #MBEDTLS_CMAC_ALT), some ciphers + * may not be supported by that implementation, and thus + * return an error. Alternate implementations must support + * AES-128 and AES-256, and may support AES-192 and 3DES. + * + * \param cipher_info The cipher information. + * \param key The CMAC key. + * \param keylen The length of the CMAC key in bits. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * \param output The buffer for the generic CMAC result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA + * if parameter verification fails. + */ +int mbedtls_cipher_cmac(const mbedtls_cipher_info_t *cipher_info, + const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output); + +#if defined(MBEDTLS_AES_C) +/** + * \brief This function implements the AES-CMAC-PRF-128 pseudorandom + * function, as defined in + * RFC-4615: The Advanced Encryption Standard-Cipher-based + * Message Authentication Code-Pseudo-Random Function-128 + * (AES-CMAC-PRF-128) Algorithm for the Internet Key + * Exchange Protocol (IKE). + * + * \param key The key to use. + * \param key_len The key length in Bytes. + * \param input The buffer holding the input data. + * \param in_len The length of the input data in Bytes. + * \param output The buffer holding the generated 16 Bytes of + * pseudorandom output. + * + * \return \c 0 on success. + */ +int mbedtls_aes_cmac_prf_128(const unsigned char *key, size_t key_len, + const unsigned char *input, size_t in_len, + unsigned char output[16]); +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_SELF_TEST) && (defined(MBEDTLS_AES_C) || defined(MBEDTLS_DES_C)) +/** + * \brief The CMAC checkup routine. + * + * \note In case the CMAC routines are provided by an alternative + * implementation (i.e. #MBEDTLS_CMAC_ALT is defined), the + * checkup routine will succeed even if the implementation does + * not support the less widely used AES-192 or 3DES primitives. + * The self-test requires at least AES-128 and AES-256 to be + * supported by the underlying implementation. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_cmac_self_test(int verbose); +#endif /* MBEDTLS_SELF_TEST && ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CMAC_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/compat-2.x.h b/r5dev/thirdparty/mbedtls/include/mbedtls/compat-2.x.h new file mode 100644 index 00000000..cdf81dcb --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/compat-2.x.h @@ -0,0 +1,58 @@ +/** + * \file compat-2.x.h + * + * \brief Compatibility definitions + * + * \deprecated Use the new names directly instead + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined(MBEDTLS_DEPRECATED_WARNING) +#warning "Including compat-2.x.h is deprecated" +#endif + +#ifndef MBEDTLS_COMPAT2X_H +#define MBEDTLS_COMPAT2X_H + +/* + * Macros for renamed functions + */ +#define mbedtls_ctr_drbg_update_ret mbedtls_ctr_drbg_update +#define mbedtls_hmac_drbg_update_ret mbedtls_hmac_drbg_update +#define mbedtls_md5_starts_ret mbedtls_md5_starts +#define mbedtls_md5_update_ret mbedtls_md5_update +#define mbedtls_md5_finish_ret mbedtls_md5_finish +#define mbedtls_md5_ret mbedtls_md5 +#define mbedtls_ripemd160_starts_ret mbedtls_ripemd160_starts +#define mbedtls_ripemd160_update_ret mbedtls_ripemd160_update +#define mbedtls_ripemd160_finish_ret mbedtls_ripemd160_finish +#define mbedtls_ripemd160_ret mbedtls_ripemd160 +#define mbedtls_sha1_starts_ret mbedtls_sha1_starts +#define mbedtls_sha1_update_ret mbedtls_sha1_update +#define mbedtls_sha1_finish_ret mbedtls_sha1_finish +#define mbedtls_sha1_ret mbedtls_sha1 +#define mbedtls_sha256_starts_ret mbedtls_sha256_starts +#define mbedtls_sha256_update_ret mbedtls_sha256_update +#define mbedtls_sha256_finish_ret mbedtls_sha256_finish +#define mbedtls_sha256_ret mbedtls_sha256 +#define mbedtls_sha512_starts_ret mbedtls_sha512_starts +#define mbedtls_sha512_update_ret mbedtls_sha512_update +#define mbedtls_sha512_finish_ret mbedtls_sha512_finish +#define mbedtls_sha512_ret mbedtls_sha512 + +#endif /* MBEDTLS_COMPAT2X_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/config_psa.h b/r5dev/thirdparty/mbedtls/include/mbedtls/config_psa.h new file mode 100644 index 00000000..cedce1e7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/config_psa.h @@ -0,0 +1,866 @@ +/** + * \file mbedtls/config_psa.h + * \brief PSA crypto configuration options (set of defines) + * + * This set of compile-time options takes settings defined in + * include/mbedtls/mbedtls_config.h and include/psa/crypto_config.h and uses + * those definitions to define symbols used in the library code. + * + * Users and integrators should not edit this file, please edit + * include/mbedtls/mbedtls_config.h for MBEDTLS_XXX settings or + * include/psa/crypto_config.h for PSA_WANT_XXX settings. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CONFIG_PSA_H +#define MBEDTLS_CONFIG_PSA_H + +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG_FILE) +#include MBEDTLS_PSA_CRYPTO_CONFIG_FILE +#else +#include "psa/crypto_config.h" +#endif +#endif /* defined(MBEDTLS_PSA_CRYPTO_CONFIG) */ + +#if defined(MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE) +#include MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + + +/****************************************************************/ +/* De facto synonyms */ +/****************************************************************/ + +#if defined(PSA_WANT_ALG_ECDSA_ANY) && !defined(PSA_WANT_ALG_ECDSA) +#define PSA_WANT_ALG_ECDSA PSA_WANT_ALG_ECDSA_ANY +#elif !defined(PSA_WANT_ALG_ECDSA_ANY) && defined(PSA_WANT_ALG_ECDSA) +#define PSA_WANT_ALG_ECDSA_ANY PSA_WANT_ALG_ECDSA +#endif + +#if defined(PSA_WANT_ALG_CCM_STAR_NO_TAG) && !defined(PSA_WANT_ALG_CCM) +#define PSA_WANT_ALG_CCM PSA_WANT_ALG_CCM_STAR_NO_TAG +#elif !defined(PSA_WANT_ALG_CCM_STAR_NO_TAG) && defined(PSA_WANT_ALG_CCM) +#define PSA_WANT_ALG_CCM_STAR_NO_TAG PSA_WANT_ALG_CCM +#endif + +#if defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW) && !defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN) +#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW +#elif !defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW) && defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN) +#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW PSA_WANT_ALG_RSA_PKCS1V15_SIGN +#endif + +#if defined(PSA_WANT_ALG_RSA_PSS_ANY_SALT) && !defined(PSA_WANT_ALG_RSA_PSS) +#define PSA_WANT_ALG_RSA_PSS PSA_WANT_ALG_RSA_PSS_ANY_SALT +#elif !defined(PSA_WANT_ALG_RSA_PSS_ANY_SALT) && defined(PSA_WANT_ALG_RSA_PSS) +#define PSA_WANT_ALG_RSA_PSS_ANY_SALT PSA_WANT_ALG_RSA_PSS +#endif + + + +/****************************************************************/ +/* Require built-in implementations based on PSA requirements */ +/****************************************************************/ + +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) + +#if defined(PSA_WANT_ALG_DETERMINISTIC_ECDSA) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_DETERMINISTIC_ECDSA) +#define MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA 1 +#define MBEDTLS_ECDSA_DETERMINISTIC +#define MBEDTLS_ECDSA_C +#define MBEDTLS_HMAC_DRBG_C +#define MBEDTLS_MD_C +#endif /* !MBEDTLS_PSA_ACCEL_ALG_DETERMINISTIC_ECDSA */ +#endif /* PSA_WANT_ALG_DETERMINISTIC_ECDSA */ + +#if defined(PSA_WANT_ALG_ECDH) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_ECDH) +#define MBEDTLS_PSA_BUILTIN_ALG_ECDH 1 +#define MBEDTLS_ECDH_C +#define MBEDTLS_ECP_C +#define MBEDTLS_BIGNUM_C +#endif /* !MBEDTLS_PSA_ACCEL_ALG_ECDH */ +#endif /* PSA_WANT_ALG_ECDH */ + +#if defined(PSA_WANT_ALG_ECDSA) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_ECDSA) +#define MBEDTLS_PSA_BUILTIN_ALG_ECDSA 1 +#define MBEDTLS_ECDSA_C +#define MBEDTLS_ECP_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#endif /* !MBEDTLS_PSA_ACCEL_ALG_ECDSA */ +#endif /* PSA_WANT_ALG_ECDSA */ + +#if defined(PSA_WANT_ALG_HKDF) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_HKDF) +#define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1 +#define MBEDTLS_PSA_BUILTIN_ALG_HKDF 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_HKDF */ +#endif /* PSA_WANT_ALG_HKDF */ + +#if defined(PSA_WANT_ALG_HKDF_EXTRACT) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_HKDF_EXTRACT) +#define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1 +#define MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_HKDF_EXTRACT */ +#endif /* PSA_WANT_ALG_HKDF_EXTRACT */ + +#if defined(PSA_WANT_ALG_HKDF_EXPAND) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_HKDF_EXPAND) +#define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1 +#define MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_HKDF_EXPAND */ +#endif /* PSA_WANT_ALG_HKDF_EXPAND */ + +#if defined(PSA_WANT_ALG_HMAC) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_HMAC) +#define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_HMAC */ +#endif /* PSA_WANT_ALG_HMAC */ + +#if defined(PSA_WANT_ALG_MD5) && !defined(MBEDTLS_PSA_ACCEL_ALG_MD5) +#define MBEDTLS_PSA_BUILTIN_ALG_MD5 1 +#define MBEDTLS_MD5_C +#endif + +#if defined(PSA_WANT_ALG_JPAKE) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_JPAKE) +#define MBEDTLS_PSA_BUILTIN_PAKE 1 +#define MBEDTLS_PSA_BUILTIN_ALG_JPAKE 1 +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_ECP_C +#define MBEDTLS_ECJPAKE_C +#endif /* MBEDTLS_PSA_ACCEL_ALG_JPAKE */ +#endif /* PSA_WANT_ALG_JPAKE */ + +#if defined(PSA_WANT_ALG_RIPEMD160) && !defined(MBEDTLS_PSA_ACCEL_ALG_RIPEMD160) +#define MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160 1 +#define MBEDTLS_RIPEMD160_C +#endif + +#if defined(PSA_WANT_ALG_RSA_OAEP) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_RSA_OAEP) +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP 1 +#define MBEDTLS_RSA_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_OID_C +#define MBEDTLS_PKCS1_V21 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_RSA_OAEP */ +#endif /* PSA_WANT_ALG_RSA_OAEP */ + +#if defined(PSA_WANT_ALG_RSA_PKCS1V15_CRYPT) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_RSA_PKCS1V15_CRYPT) +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT 1 +#define MBEDTLS_RSA_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_OID_C +#define MBEDTLS_PKCS1_V15 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_RSA_PKCS1V15_CRYPT */ +#endif /* PSA_WANT_ALG_RSA_PKCS1V15_CRYPT */ + +#if defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_RSA_PKCS1V15_SIGN) +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN 1 +#define MBEDTLS_RSA_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_OID_C +#define MBEDTLS_PKCS1_V15 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_RSA_PKCS1V15_SIGN */ +#endif /* PSA_WANT_ALG_RSA_PKCS1V15_SIGN */ + +#if defined(PSA_WANT_ALG_RSA_PSS) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_RSA_PSS) +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS 1 +#define MBEDTLS_RSA_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_OID_C +#define MBEDTLS_PKCS1_V21 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_RSA_PSS */ +#endif /* PSA_WANT_ALG_RSA_PSS */ + +#if defined(PSA_WANT_ALG_SHA_1) && !defined(MBEDTLS_PSA_ACCEL_ALG_SHA_1) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_1 1 +#define MBEDTLS_SHA1_C +#endif + +#if defined(PSA_WANT_ALG_SHA_224) && !defined(MBEDTLS_PSA_ACCEL_ALG_SHA_224) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_224 1 +#define MBEDTLS_SHA224_C +#endif + +#if defined(PSA_WANT_ALG_SHA_256) && !defined(MBEDTLS_PSA_ACCEL_ALG_SHA_256) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_256 1 +#define MBEDTLS_SHA256_C +#endif + +#if defined(PSA_WANT_ALG_SHA_384) && !defined(MBEDTLS_PSA_ACCEL_ALG_SHA_384) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_384 1 +#define MBEDTLS_SHA384_C +#endif + +#if defined(PSA_WANT_ALG_SHA_512) && !defined(MBEDTLS_PSA_ACCEL_ALG_SHA_512) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_512 1 +#define MBEDTLS_SHA512_C +#endif + +#if defined(PSA_WANT_ALG_TLS12_PRF) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_TLS12_PRF) +#define MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_TLS12_PRF */ +#endif /* PSA_WANT_ALG_TLS12_PRF */ + +#if defined(PSA_WANT_ALG_TLS12_PSK_TO_MS) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_TLS12_PSK_TO_MS) +#define MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_TLS12_PSK_TO_MS */ +#endif /* PSA_WANT_ALG_TLS12_PSK_TO_MS */ + +#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_TLS12_ECJPAKE_TO_PMS) +#define MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_TLS12_ECJPAKE_TO_PMS */ +#endif /* PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS */ + +#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_KEY_PAIR) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR 1 +#define MBEDTLS_ECP_C +#define MBEDTLS_BIGNUM_C +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_KEY_PAIR */ +#endif /* PSA_WANT_KEY_TYPE_ECC_KEY_PAIR */ + +#if defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_PUBLIC_KEY) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY 1 +#define MBEDTLS_ECP_C +#define MBEDTLS_BIGNUM_C +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_PUBLIC_KEY */ +#endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */ + +#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_KEY_PAIR) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR 1 +#define MBEDTLS_RSA_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_OID_C +#define MBEDTLS_GENPRIME +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PK_WRITE_C +#define MBEDTLS_PK_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_KEY_PAIR */ +#endif /* PSA_WANT_KEY_TYPE_RSA_KEY_PAIR */ + +#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_PUBLIC_KEY) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY 1 +#define MBEDTLS_RSA_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_OID_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PK_WRITE_C +#define MBEDTLS_PK_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_RSA_PUBLIC_KEY */ +#endif /* PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY */ + +/* If any of the block modes are requested that don't have an + * associated HW assist, define PSA_HAVE_SOFT_BLOCK_MODE for checking + * in the block cipher key types. */ +#if (defined(PSA_WANT_ALG_CTR) && !defined(MBEDTLS_PSA_ACCEL_ALG_CTR)) || \ + (defined(PSA_WANT_ALG_CFB) && !defined(MBEDTLS_PSA_ACCEL_ALG_CFB)) || \ + (defined(PSA_WANT_ALG_OFB) && !defined(MBEDTLS_PSA_ACCEL_ALG_OFB)) || \ + defined(PSA_WANT_ALG_ECB_NO_PADDING) || \ + (defined(PSA_WANT_ALG_CBC_NO_PADDING) && \ + !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING)) || \ + (defined(PSA_WANT_ALG_CBC_PKCS7) && \ + !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7)) || \ + (defined(PSA_WANT_ALG_CMAC) && !defined(MBEDTLS_PSA_ACCEL_ALG_CMAC)) +#define PSA_HAVE_SOFT_BLOCK_MODE 1 +#endif + +#if (defined(PSA_WANT_ALG_GCM) && !defined(MBEDTLS_PSA_ACCEL_ALG_GCM)) || \ + (defined(PSA_WANT_ALG_CCM) && !defined(MBEDTLS_PSA_ACCEL_ALG_CCM)) +#define PSA_HAVE_SOFT_BLOCK_AEAD 1 +#endif + +#if defined(PSA_WANT_KEY_TYPE_AES) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_AES) +#define PSA_HAVE_SOFT_KEY_TYPE_AES 1 +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_AES */ +#if defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \ + defined(PSA_HAVE_SOFT_BLOCK_MODE) || \ + defined(PSA_HAVE_SOFT_BLOCK_AEAD) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES 1 +#define MBEDTLS_AES_C +#endif /* PSA_HAVE_SOFT_KEY_TYPE_AES || PSA_HAVE_SOFT_BLOCK_MODE */ +#endif /* PSA_WANT_KEY_TYPE_AES */ + +#if defined(PSA_WANT_KEY_TYPE_ARIA) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_ARIA) +#define PSA_HAVE_SOFT_KEY_TYPE_ARIA 1 +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_ARIA */ +#if defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \ + defined(PSA_HAVE_SOFT_BLOCK_MODE) || \ + defined(PSA_HAVE_SOFT_BLOCK_AEAD) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA 1 +#define MBEDTLS_ARIA_C +#endif /* PSA_HAVE_SOFT_KEY_TYPE_ARIA || PSA_HAVE_SOFT_BLOCK_MODE */ +#endif /* PSA_WANT_KEY_TYPE_ARIA */ + +#if defined(PSA_WANT_KEY_TYPE_CAMELLIA) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_CAMELLIA) +#define PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA 1 +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_CAMELLIA */ +#if defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA) || \ + defined(PSA_HAVE_SOFT_BLOCK_MODE) || \ + defined(PSA_HAVE_SOFT_BLOCK_AEAD) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA 1 +#define MBEDTLS_CAMELLIA_C +#endif /* PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA || PSA_HAVE_SOFT_BLOCK_MODE */ +#endif /* PSA_WANT_KEY_TYPE_CAMELLIA */ + +#if defined(PSA_WANT_KEY_TYPE_DES) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_DES) +#define PSA_HAVE_SOFT_KEY_TYPE_DES 1 +#endif /* !MBEDTLS_PSA_ACCEL_KEY_TYPE_DES */ +#if defined(PSA_HAVE_SOFT_KEY_TYPE_DES) || \ + defined(PSA_HAVE_SOFT_BLOCK_MODE) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES 1 +#define MBEDTLS_DES_C +#endif /*PSA_HAVE_SOFT_KEY_TYPE_DES || PSA_HAVE_SOFT_BLOCK_MODE */ +#endif /* PSA_WANT_KEY_TYPE_DES */ + +#if defined(PSA_WANT_KEY_TYPE_CHACHA20) +#if !defined(MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20 1 +#define MBEDTLS_CHACHA20_C +#endif /*!MBEDTLS_PSA_ACCEL_KEY_TYPE_CHACHA20 */ +#endif /* PSA_WANT_KEY_TYPE_CHACHA20 */ + +/* If any of the software block ciphers are selected, define + * PSA_HAVE_SOFT_BLOCK_CIPHER, which can be used in any of these + * situations. */ +#if defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_DES) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA) +#define PSA_HAVE_SOFT_BLOCK_CIPHER 1 +#endif + +#if defined(PSA_WANT_ALG_STREAM_CIPHER) +#define MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER 1 +#endif /* PSA_WANT_ALG_STREAM_CIPHER */ + +#if defined(PSA_WANT_ALG_CBC_MAC) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_MAC) +#error "CBC-MAC is not yet supported via the PSA API in Mbed TLS." +#define MBEDTLS_PSA_BUILTIN_ALG_CBC_MAC 1 +#endif /* !MBEDTLS_PSA_ACCEL_ALG_CBC_MAC */ +#endif /* PSA_WANT_ALG_CBC_MAC */ + +#if defined(PSA_WANT_ALG_CMAC) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CMAC) || \ + defined(PSA_HAVE_SOFT_BLOCK_CIPHER) +#define MBEDTLS_PSA_BUILTIN_ALG_CMAC 1 +#define MBEDTLS_CMAC_C +#endif /* !MBEDTLS_PSA_ACCEL_ALG_CMAC */ +#endif /* PSA_WANT_ALG_CMAC */ + +#if defined(PSA_WANT_ALG_CTR) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CTR) || \ + defined(PSA_HAVE_SOFT_BLOCK_CIPHER) +#define MBEDTLS_PSA_BUILTIN_ALG_CTR 1 +#define MBEDTLS_CIPHER_MODE_CTR +#endif +#endif /* PSA_WANT_ALG_CTR */ + +#if defined(PSA_WANT_ALG_CFB) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CFB) || \ + defined(PSA_HAVE_SOFT_BLOCK_CIPHER) +#define MBEDTLS_PSA_BUILTIN_ALG_CFB 1 +#define MBEDTLS_CIPHER_MODE_CFB +#endif +#endif /* PSA_WANT_ALG_CFB */ + +#if defined(PSA_WANT_ALG_OFB) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_OFB) || \ + defined(PSA_HAVE_SOFT_BLOCK_CIPHER) +#define MBEDTLS_PSA_BUILTIN_ALG_OFB 1 +#define MBEDTLS_CIPHER_MODE_OFB +#endif +#endif /* PSA_WANT_ALG_OFB */ + +#if defined(PSA_WANT_ALG_ECB_NO_PADDING) && \ + !defined(MBEDTLS_PSA_ACCEL_ALG_ECB_NO_PADDING) +#define MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING 1 +#endif + +#if defined(PSA_WANT_ALG_CBC_NO_PADDING) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING) || \ + defined(PSA_HAVE_SOFT_BLOCK_CIPHER) +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING 1 +#endif +#endif /* PSA_WANT_ALG_CBC_NO_PADDING */ + +#if defined(PSA_WANT_ALG_CBC_PKCS7) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7) || \ + defined(PSA_HAVE_SOFT_BLOCK_CIPHER) +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7 1 +#define MBEDTLS_CIPHER_PADDING_PKCS7 +#endif +#endif /* PSA_WANT_ALG_CBC_PKCS7 */ + +#if defined(PSA_WANT_ALG_CCM) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CCM) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA) +#define MBEDTLS_PSA_BUILTIN_ALG_CCM 1 +#define MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG 1 +#define MBEDTLS_CCM_C +#endif +#endif /* PSA_WANT_ALG_CCM */ + +#if defined(PSA_WANT_ALG_GCM) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_GCM) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_AES) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_ARIA) || \ + defined(PSA_HAVE_SOFT_KEY_TYPE_CAMELLIA) +#define MBEDTLS_PSA_BUILTIN_ALG_GCM 1 +#define MBEDTLS_GCM_C +#endif +#endif /* PSA_WANT_ALG_GCM */ + +#if defined(PSA_WANT_ALG_CHACHA20_POLY1305) +#if !defined(MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305) +#if defined(PSA_WANT_KEY_TYPE_CHACHA20) +#define MBEDTLS_CHACHAPOLY_C +#define MBEDTLS_CHACHA20_C +#define MBEDTLS_POLY1305_C +#define MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 1 +#endif /* PSA_WANT_KEY_TYPE_CHACHA20 */ +#endif /* !MBEDTLS_PSA_ACCEL_ALG_CHACHA20_POLY1305 */ +#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */ + +#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_256) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_256) +#define MBEDTLS_ECP_DP_BP256R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_256 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_256 */ +#endif /* PSA_WANT_ECC_BRAINPOOL_P_R1_256 */ + +#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_384) +#define MBEDTLS_ECP_DP_BP384R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_384 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_384 */ +#endif /* PSA_WANT_ECC_BRAINPOOL_P_R1_384 */ + +#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_512) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_512) +#define MBEDTLS_ECP_DP_BP512R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_512 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_BRAINPOOL_P_R1_512 */ +#endif /* PSA_WANT_ECC_BRAINPOOL_P_R1_512 */ + +#if defined(PSA_WANT_ECC_MONTGOMERY_255) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_255) +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_255 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_255 */ +#endif /* PSA_WANT_ECC_MONTGOMERY_255 */ + +#if defined(PSA_WANT_ECC_MONTGOMERY_448) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_448) +#define MBEDTLS_ECP_DP_CURVE448_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_448 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_MONTGOMERY_448 */ +#endif /* PSA_WANT_ECC_MONTGOMERY_448 */ + +#if defined(PSA_WANT_ECC_SECP_R1_192) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_192) +#define MBEDTLS_ECP_DP_SECP192R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_192 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_192 */ +#endif /* PSA_WANT_ECC_SECP_R1_192 */ + +#if defined(PSA_WANT_ECC_SECP_R1_224) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_224) +#define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_224 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_224 */ +#endif /* PSA_WANT_ECC_SECP_R1_224 */ + +#if defined(PSA_WANT_ECC_SECP_R1_256) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_256) +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_256 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_256 */ +#endif /* PSA_WANT_ECC_SECP_R1_256 */ + +#if defined(PSA_WANT_ECC_SECP_R1_384) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_384) +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_384 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_384 */ +#endif /* PSA_WANT_ECC_SECP_R1_384 */ + +#if defined(PSA_WANT_ECC_SECP_R1_521) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_R1_521) +#define MBEDTLS_ECP_DP_SECP521R1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_521 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_R1_521 */ +#endif /* PSA_WANT_ECC_SECP_R1_521 */ + +#if defined(PSA_WANT_ECC_SECP_K1_192) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_K1_192) +#define MBEDTLS_ECP_DP_SECP192K1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_192 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_K1_192 */ +#endif /* PSA_WANT_ECC_SECP_K1_192 */ + +#if defined(PSA_WANT_ECC_SECP_K1_224) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_K1_224) +/* + * SECP224K1 is buggy via the PSA API in Mbed TLS + * (https://github.com/Mbed-TLS/mbedtls/issues/3541). + */ +#error "SECP224K1 is buggy via the PSA API in Mbed TLS." +#define MBEDTLS_ECP_DP_SECP224K1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_224 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_K1_224 */ +#endif /* PSA_WANT_ECC_SECP_K1_224 */ + +#if defined(PSA_WANT_ECC_SECP_K1_256) +#if !defined(MBEDTLS_PSA_ACCEL_ECC_SECP_K1_256) +#define MBEDTLS_ECP_DP_SECP256K1_ENABLED +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_256 1 +#endif /* !MBEDTLS_PSA_ACCEL_ECC_SECP_K1_256 */ +#endif /* PSA_WANT_ECC_SECP_K1_256 */ + + + +/****************************************************************/ +/* Infer PSA requirements from Mbed TLS capabilities */ +/****************************************************************/ + +#else /* MBEDTLS_PSA_CRYPTO_CONFIG */ + +/* + * Ensure PSA_WANT_* defines are setup properly if MBEDTLS_PSA_CRYPTO_CONFIG + * is not defined + */ + +#if defined(MBEDTLS_CCM_C) +#define MBEDTLS_PSA_BUILTIN_ALG_CCM 1 +#define MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG 1 +#define PSA_WANT_ALG_CCM 1 +#define PSA_WANT_ALG_CCM_STAR_NO_TAG 1 +#endif /* MBEDTLS_CCM_C */ + +#if defined(MBEDTLS_CMAC_C) +#define MBEDTLS_PSA_BUILTIN_ALG_CMAC 1 +#define PSA_WANT_ALG_CMAC 1 +#endif /* MBEDTLS_CMAC_C */ + +#if defined(MBEDTLS_ECDH_C) +#define MBEDTLS_PSA_BUILTIN_ALG_ECDH 1 +#define PSA_WANT_ALG_ECDH 1 +#endif /* MBEDTLS_ECDH_C */ + +#if defined(MBEDTLS_ECDSA_C) +#define MBEDTLS_PSA_BUILTIN_ALG_ECDSA 1 +#define PSA_WANT_ALG_ECDSA 1 +#define PSA_WANT_ALG_ECDSA_ANY 1 + +// Only add in DETERMINISTIC support if ECDSA is also enabled +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +#define MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA 1 +#define PSA_WANT_ALG_DETERMINISTIC_ECDSA 1 +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ + +#endif /* MBEDTLS_ECDSA_C */ + +#if defined(MBEDTLS_ECP_C) +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR 1 +#define PSA_WANT_KEY_TYPE_ECC_KEY_PAIR 1 +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY 1 +#define PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY 1 +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_GCM_C) +#define MBEDTLS_PSA_BUILTIN_ALG_GCM 1 +#define PSA_WANT_ALG_GCM 1 +#endif /* MBEDTLS_GCM_C */ + +/* Enable PSA HKDF algorithm if mbedtls HKDF is supported. + * PSA HKDF EXTRACT and PSA HKDF EXPAND have minimal cost when + * PSA HKDF is enabled, so enable both algorithms together + * with PSA HKDF. */ +#if defined(MBEDTLS_HKDF_C) +#define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1 +#define PSA_WANT_ALG_HMAC 1 +#define MBEDTLS_PSA_BUILTIN_ALG_HKDF 1 +#define PSA_WANT_ALG_HKDF 1 +#define MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT 1 +#define PSA_WANT_ALG_HKDF_EXTRACT 1 +#define MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND 1 +#define PSA_WANT_ALG_HKDF_EXPAND 1 +#endif /* MBEDTLS_HKDF_C */ + +#define MBEDTLS_PSA_BUILTIN_ALG_HMAC 1 +#define PSA_WANT_ALG_HMAC 1 +#define PSA_WANT_KEY_TYPE_HMAC + +#if defined(MBEDTLS_MD_C) +#define MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF 1 +#define PSA_WANT_ALG_TLS12_PRF 1 +#define MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS 1 +#define PSA_WANT_ALG_TLS12_PSK_TO_MS 1 +#endif /* MBEDTLS_MD_C */ + +#if defined(MBEDTLS_MD5_C) +#define MBEDTLS_PSA_BUILTIN_ALG_MD5 1 +#define PSA_WANT_ALG_MD5 1 +#endif + +#if defined(MBEDTLS_ECJPAKE_C) +#define MBEDTLS_PSA_BUILTIN_PAKE 1 +#define MBEDTLS_PSA_BUILTIN_ALG_JPAKE 1 +#define PSA_WANT_ALG_JPAKE 1 +#endif + +#if defined(MBEDTLS_RIPEMD160_C) +#define MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160 1 +#define PSA_WANT_ALG_RIPEMD160 1 +#endif + +#if defined(MBEDTLS_RSA_C) +#if defined(MBEDTLS_PKCS1_V15) +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT 1 +#define PSA_WANT_ALG_RSA_PKCS1V15_CRYPT 1 +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN 1 +#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN 1 +#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW 1 +#endif /* MBEDTLS_PKCS1_V15 */ +#if defined(MBEDTLS_PKCS1_V21) +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP 1 +#define PSA_WANT_ALG_RSA_OAEP 1 +#define MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS 1 +#define PSA_WANT_ALG_RSA_PSS 1 +#endif /* MBEDTLS_PKCS1_V21 */ +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR 1 +#define PSA_WANT_KEY_TYPE_RSA_KEY_PAIR 1 +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY 1 +#define PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY 1 +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_SHA1_C) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_1 1 +#define PSA_WANT_ALG_SHA_1 1 +#endif + +#if defined(MBEDTLS_SHA224_C) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_224 1 +#define PSA_WANT_ALG_SHA_224 1 +#endif + +#if defined(MBEDTLS_SHA256_C) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_256 1 +#define PSA_WANT_ALG_SHA_256 1 +#endif + +#if defined(MBEDTLS_SHA384_C) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_384 1 +#define PSA_WANT_ALG_SHA_384 1 +#endif + +#if defined(MBEDTLS_SHA512_C) +#define MBEDTLS_PSA_BUILTIN_ALG_SHA_512 1 +#define PSA_WANT_ALG_SHA_512 1 +#endif + +#if defined(MBEDTLS_AES_C) +#define PSA_WANT_KEY_TYPE_AES 1 +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES 1 +#endif + +#if defined(MBEDTLS_ARIA_C) +#define PSA_WANT_KEY_TYPE_ARIA 1 +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA 1 +#endif + +#if defined(MBEDTLS_CAMELLIA_C) +#define PSA_WANT_KEY_TYPE_CAMELLIA 1 +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA 1 +#endif + +#if defined(MBEDTLS_DES_C) +#define PSA_WANT_KEY_TYPE_DES 1 +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES 1 +#endif + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) +#define MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS 1 +#define PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS 1 +#endif + +#if defined(MBEDTLS_CHACHA20_C) +#define PSA_WANT_KEY_TYPE_CHACHA20 1 +#define PSA_WANT_ALG_STREAM_CIPHER 1 +#define MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20 1 +#define MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER 1 +#if defined(MBEDTLS_CHACHAPOLY_C) +#define PSA_WANT_ALG_CHACHA20_POLY1305 1 +#define MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 1 +#endif +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#define MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING 1 +#define PSA_WANT_ALG_CBC_NO_PADDING 1 +#if defined(MBEDTLS_CIPHER_PADDING_PKCS7) +#define MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7 1 +#define PSA_WANT_ALG_CBC_PKCS7 1 +#endif +#endif + +#if defined(MBEDTLS_AES_C) || defined(MBEDTLS_DES_C) || \ + defined(MBEDTLS_ARIA_C) || defined(MBEDTLS_CAMELLIA_C) +#define MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING 1 +#define PSA_WANT_ALG_ECB_NO_PADDING 1 +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CFB) +#define MBEDTLS_PSA_BUILTIN_ALG_CFB 1 +#define PSA_WANT_ALG_CFB 1 +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CTR) +#define MBEDTLS_PSA_BUILTIN_ALG_CTR 1 +#define PSA_WANT_ALG_CTR 1 +#endif + +#if defined(MBEDTLS_CIPHER_MODE_OFB) +#define MBEDTLS_PSA_BUILTIN_ALG_OFB 1 +#define PSA_WANT_ALG_OFB 1 +#endif + +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_256 1 +#define PSA_WANT_ECC_BRAINPOOL_P_R1_256 +#endif + +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_384 1 +#define PSA_WANT_ECC_BRAINPOOL_P_R1_384 +#endif + +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_BRAINPOOL_P_R1_512 1 +#define PSA_WANT_ECC_BRAINPOOL_P_R1_512 +#endif + +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_255 1 +#define PSA_WANT_ECC_MONTGOMERY_255 +#endif + +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_MONTGOMERY_448 1 +#define PSA_WANT_ECC_MONTGOMERY_448 +#endif + +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_192 1 +#define PSA_WANT_ECC_SECP_R1_192 +#endif + +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_224 1 +#define PSA_WANT_ECC_SECP_R1_224 +#endif + +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_256 1 +#define PSA_WANT_ECC_SECP_R1_256 +#endif + +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_384 1 +#define PSA_WANT_ECC_SECP_R1_384 +#endif + +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_R1_521 1 +#define PSA_WANT_ECC_SECP_R1_521 +#endif + +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_192 1 +#define PSA_WANT_ECC_SECP_K1_192 +#endif + +/* SECP224K1 is buggy via the PSA API (https://github.com/Mbed-TLS/mbedtls/issues/3541) */ +#if 0 && defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_224 1 +#define PSA_WANT_ECC_SECP_K1_224 +#endif + +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +#define MBEDTLS_PSA_BUILTIN_ECC_SECP_K1_256 1 +#define PSA_WANT_ECC_SECP_K1_256 +#endif + +#endif /* MBEDTLS_PSA_CRYPTO_CONFIG */ + +#if defined(PSA_WANT_ALG_ECDSA) && defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) && \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +#define PSA_HAVE_FULL_ECDSA 1 +#endif + +#if defined(PSA_WANT_ALG_JPAKE) && defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) && \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +#define PSA_HAVE_FULL_JPAKE 1 +#endif + +/* These features are always enabled. */ +#define PSA_WANT_KEY_TYPE_DERIVE 1 +#define PSA_WANT_KEY_TYPE_PASSWORD 1 +#define PSA_WANT_KEY_TYPE_PASSWORD_HASH 1 +#define PSA_WANT_KEY_TYPE_RAW_DATA 1 + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_CONFIG_PSA_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/constant_time.h b/r5dev/thirdparty/mbedtls/include/mbedtls/constant_time.h new file mode 100644 index 00000000..91a9e7fc --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/constant_time.h @@ -0,0 +1,46 @@ +/** + * Constant-time functions + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CONSTANT_TIME_H +#define MBEDTLS_CONSTANT_TIME_H + +#include + + +/** Constant-time buffer comparison without branches. + * + * This is equivalent to the standard memcmp function, but is likely to be + * compiled to code using bitwise operation rather than a branch. + * + * This function can be used to write constant-time code by replacing branches + * with bit operations using masks. + * + * \param a Pointer to the first buffer. + * \param b Pointer to the second buffer. + * \param n The number of bytes to compare in the buffer. + * + * \return Zero if the content of the two buffer is the same, + * otherwise non-zero. + */ +int mbedtls_ct_memcmp(const void *a, + const void *b, + size_t n); + +#endif /* MBEDTLS_CONSTANT_TIME_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h new file mode 100644 index 00000000..8d28150d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ctr_drbg.h @@ -0,0 +1,578 @@ +/** + * \file ctr_drbg.h + * + * \brief This file contains definitions and functions for the + * CTR_DRBG pseudorandom generator. + * + * CTR_DRBG is a standardized way of building a PRNG from a block-cipher + * in counter mode operation, as defined in NIST SP 800-90A: + * Recommendation for Random Number Generation Using Deterministic Random + * Bit Generators. + * + * The Mbed TLS implementation of CTR_DRBG uses AES-256 (default) or AES-128 + * (if \c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is enabled at compile time) + * as the underlying block cipher, with a derivation function. + * + * The security strength as defined in NIST SP 800-90A is + * 128 bits when AES-128 is used (\c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY enabled) + * and 256 bits otherwise, provided that #MBEDTLS_CTR_DRBG_ENTROPY_LEN is + * kept at its default value (and not overridden in mbedtls_config.h) and that the + * DRBG instance is set up with default parameters. + * See the documentation of mbedtls_ctr_drbg_seed() for more + * information. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CTR_DRBG_H +#define MBEDTLS_CTR_DRBG_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/aes.h" + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +/** The entropy source failed. */ +#define MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED -0x0034 +/** The requested random buffer length is too big. */ +#define MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG -0x0036 +/** The input (entropy + additional data) is too large. */ +#define MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG -0x0038 +/** Read or write error in file. */ +#define MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR -0x003A + +#define MBEDTLS_CTR_DRBG_BLOCKSIZE 16 /**< The block size used by the cipher. */ + +#if defined(MBEDTLS_CTR_DRBG_USE_128_BIT_KEY) +#define MBEDTLS_CTR_DRBG_KEYSIZE 16 +/**< The key size in bytes used by the cipher. + * + * Compile-time choice: 16 bytes (128 bits) + * because #MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is enabled. + */ +#else +#define MBEDTLS_CTR_DRBG_KEYSIZE 32 +/**< The key size in bytes used by the cipher. + * + * Compile-time choice: 32 bytes (256 bits) + * because \c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is disabled. + */ +#endif + +#define MBEDTLS_CTR_DRBG_KEYBITS (MBEDTLS_CTR_DRBG_KEYSIZE * 8) /**< The key size for the DRBG operation, in bits. */ +#define MBEDTLS_CTR_DRBG_SEEDLEN (MBEDTLS_CTR_DRBG_KEYSIZE + MBEDTLS_CTR_DRBG_BLOCKSIZE) /**< The seed length, calculated as (counter + AES key). */ + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them using the compiler command + * line. + * \{ + */ + +/** \def MBEDTLS_CTR_DRBG_ENTROPY_LEN + * + * \brief The amount of entropy used per seed by default, in bytes. + */ +#if !defined(MBEDTLS_CTR_DRBG_ENTROPY_LEN) +#if defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_ENTROPY_FORCE_SHA256) +/** This is 48 bytes because the entropy module uses SHA-512 + * (\c MBEDTLS_ENTROPY_FORCE_SHA256 is disabled). + */ +#define MBEDTLS_CTR_DRBG_ENTROPY_LEN 48 + +#else /* defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_ENTROPY_FORCE_SHA256) */ + +/** This is 32 bytes because the entropy module uses SHA-256 + * (the SHA512 module is disabled or + * \c MBEDTLS_ENTROPY_FORCE_SHA256 is enabled). + */ +#if !defined(MBEDTLS_CTR_DRBG_USE_128_BIT_KEY) +/** \warning To achieve a 256-bit security strength, you must pass a nonce + * to mbedtls_ctr_drbg_seed(). + */ +#endif /* !defined(MBEDTLS_CTR_DRBG_USE_128_BIT_KEY) */ +#define MBEDTLS_CTR_DRBG_ENTROPY_LEN 32 +#endif /* defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_ENTROPY_FORCE_SHA256) */ +#endif /* !defined(MBEDTLS_CTR_DRBG_ENTROPY_LEN) */ + +#if !defined(MBEDTLS_CTR_DRBG_RESEED_INTERVAL) +#define MBEDTLS_CTR_DRBG_RESEED_INTERVAL 10000 +/**< The interval before reseed is performed by default. */ +#endif + +#if !defined(MBEDTLS_CTR_DRBG_MAX_INPUT) +#define MBEDTLS_CTR_DRBG_MAX_INPUT 256 +/**< The maximum number of additional input Bytes. */ +#endif + +#if !defined(MBEDTLS_CTR_DRBG_MAX_REQUEST) +#define MBEDTLS_CTR_DRBG_MAX_REQUEST 1024 +/**< The maximum number of requested Bytes per call. */ +#endif + +#if !defined(MBEDTLS_CTR_DRBG_MAX_SEED_INPUT) +#define MBEDTLS_CTR_DRBG_MAX_SEED_INPUT 384 +/**< The maximum size of seed or reseed buffer in bytes. */ +#endif + +/** \} name SECTION: Module settings */ + +#define MBEDTLS_CTR_DRBG_PR_OFF 0 +/**< Prediction resistance is disabled. */ +#define MBEDTLS_CTR_DRBG_PR_ON 1 +/**< Prediction resistance is enabled. */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if MBEDTLS_CTR_DRBG_ENTROPY_LEN >= MBEDTLS_CTR_DRBG_KEYSIZE * 3 / 2 +/** The default length of the nonce read from the entropy source. + * + * This is \c 0 because a single read from the entropy source is sufficient + * to include a nonce. + * See the documentation of mbedtls_ctr_drbg_seed() for more information. + */ +#define MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN 0 +#else +/** The default length of the nonce read from the entropy source. + * + * This is half of the default entropy length because a single read from + * the entropy source does not provide enough material to form a nonce. + * See the documentation of mbedtls_ctr_drbg_seed() for more information. + */ +#define MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN (MBEDTLS_CTR_DRBG_ENTROPY_LEN + 1) / 2 +#endif + +/** + * \brief The CTR_DRBG context structure. + */ +typedef struct mbedtls_ctr_drbg_context { + unsigned char MBEDTLS_PRIVATE(counter)[16]; /*!< The counter (V). */ + int MBEDTLS_PRIVATE(reseed_counter); /*!< The reseed counter. + * This is the number of requests that have + * been made since the last (re)seeding, + * minus one. + * Before the initial seeding, this field + * contains the amount of entropy in bytes + * to use as a nonce for the initial seeding, + * or -1 if no nonce length has been explicitly + * set (see mbedtls_ctr_drbg_set_nonce_len()). + */ + int MBEDTLS_PRIVATE(prediction_resistance); /*!< This determines whether prediction + resistance is enabled, that is + whether to systematically reseed before + each random generation. */ + size_t MBEDTLS_PRIVATE(entropy_len); /*!< The amount of entropy grabbed on each + seed or reseed operation, in bytes. */ + int MBEDTLS_PRIVATE(reseed_interval); /*!< The reseed interval. + * This is the maximum number of requests + * that can be made between reseedings. */ + + mbedtls_aes_context MBEDTLS_PRIVATE(aes_ctx); /*!< The AES context. */ + + /* + * Callbacks (Entropy) + */ + int(*MBEDTLS_PRIVATE(f_entropy))(void *, unsigned char *, size_t); + /*!< The entropy callback function. */ + + void *MBEDTLS_PRIVATE(p_entropy); /*!< The context for the entropy function. */ + +#if defined(MBEDTLS_THREADING_C) + /* Invariant: the mutex is initialized if and only if f_entropy != NULL. + * This means that the mutex is initialized during the initial seeding + * in mbedtls_ctr_drbg_seed() and freed in mbedtls_ctr_drbg_free(). + * + * Note that this invariant may change without notice. Do not rely on it + * and do not access the mutex directly in application code. + */ + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); +#endif +} +mbedtls_ctr_drbg_context; + +/** + * \brief This function initializes the CTR_DRBG context, + * and prepares it for mbedtls_ctr_drbg_seed() + * or mbedtls_ctr_drbg_free(). + * + * \note The reseed interval is + * #MBEDTLS_CTR_DRBG_RESEED_INTERVAL by default. + * You can override it by calling + * mbedtls_ctr_drbg_set_reseed_interval(). + * + * \param ctx The CTR_DRBG context to initialize. + */ +void mbedtls_ctr_drbg_init(mbedtls_ctr_drbg_context *ctx); + +/** + * \brief This function seeds and sets up the CTR_DRBG + * entropy source for future reseeds. + * + * A typical choice for the \p f_entropy and \p p_entropy parameters is + * to use the entropy module: + * - \p f_entropy is mbedtls_entropy_func(); + * - \p p_entropy is an instance of ::mbedtls_entropy_context initialized + * with mbedtls_entropy_init() (which registers the platform's default + * entropy sources). + * + * The entropy length is #MBEDTLS_CTR_DRBG_ENTROPY_LEN by default. + * You can override it by calling mbedtls_ctr_drbg_set_entropy_len(). + * + * The entropy nonce length is: + * - \c 0 if the entropy length is at least 3/2 times the entropy length, + * which guarantees that the security strength is the maximum permitted + * by the key size and entropy length according to NIST SP 800-90A §10.2.1; + * - Half the entropy length otherwise. + * You can override it by calling mbedtls_ctr_drbg_set_nonce_len(). + * With the default entropy length, the entropy nonce length is + * #MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN. + * + * You can provide a nonce and personalization string in addition to the + * entropy source, to make this instantiation as unique as possible. + * See SP 800-90A §8.6.7 for more details about nonces. + * + * The _seed_material_ value passed to the derivation function in + * the CTR_DRBG Instantiate Process described in NIST SP 800-90A §10.2.1.3.2 + * is the concatenation of the following strings: + * - A string obtained by calling \p f_entropy function for the entropy + * length. + */ +#if MBEDTLS_CTR_DRBG_ENTROPY_NONCE_LEN == 0 +/** + * - If mbedtls_ctr_drbg_set_nonce_len() has been called, a string + * obtained by calling \p f_entropy function for the specified length. + */ +#else +/** + * - A string obtained by calling \p f_entropy function for the entropy nonce + * length. If the entropy nonce length is \c 0, this function does not + * make a second call to \p f_entropy. + */ +#endif +#if defined(MBEDTLS_THREADING_C) +/** + * \note When Mbed TLS is built with threading support, + * after this function returns successfully, + * it is safe to call mbedtls_ctr_drbg_random() + * from multiple threads. Other operations, including + * reseeding, are not thread-safe. + */ +#endif /* MBEDTLS_THREADING_C */ +/** + * - The \p custom string. + * + * \note To achieve the nominal security strength permitted + * by CTR_DRBG, the entropy length must be: + * - at least 16 bytes for a 128-bit strength + * (maximum achievable strength when using AES-128); + * - at least 32 bytes for a 256-bit strength + * (maximum achievable strength when using AES-256). + * + * In addition, if you do not pass a nonce in \p custom, + * the sum of the entropy length + * and the entropy nonce length must be: + * - at least 24 bytes for a 128-bit strength + * (maximum achievable strength when using AES-128); + * - at least 48 bytes for a 256-bit strength + * (maximum achievable strength when using AES-256). + * + * \param ctx The CTR_DRBG context to seed. + * It must have been initialized with + * mbedtls_ctr_drbg_init(). + * After a successful call to mbedtls_ctr_drbg_seed(), + * you may not call mbedtls_ctr_drbg_seed() again on + * the same context unless you call + * mbedtls_ctr_drbg_free() and mbedtls_ctr_drbg_init() + * again first. + * After a failed call to mbedtls_ctr_drbg_seed(), + * you must call mbedtls_ctr_drbg_free(). + * \param f_entropy The entropy callback, taking as arguments the + * \p p_entropy context, the buffer to fill, and the + * length of the buffer. + * \p f_entropy is always called with a buffer size + * less than or equal to the entropy length. + * \param p_entropy The entropy context to pass to \p f_entropy. + * \param custom The personalization string. + * This can be \c NULL, in which case the personalization + * string is empty regardless of the value of \p len. + * \param len The length of the personalization string. + * This must be at most + * #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT + * - #MBEDTLS_CTR_DRBG_ENTROPY_LEN. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure. + */ +int mbedtls_ctr_drbg_seed(mbedtls_ctr_drbg_context *ctx, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len); + +/** + * \brief This function resets CTR_DRBG context to the state immediately + * after initial call of mbedtls_ctr_drbg_init(). + * + * \param ctx The CTR_DRBG context to clear. + */ +void mbedtls_ctr_drbg_free(mbedtls_ctr_drbg_context *ctx); + +/** + * \brief This function turns prediction resistance on or off. + * The default value is off. + * + * \note If enabled, entropy is gathered at the beginning of + * every call to mbedtls_ctr_drbg_random_with_add() + * or mbedtls_ctr_drbg_random(). + * Only use this if your entropy source has sufficient + * throughput. + * + * \param ctx The CTR_DRBG context. + * \param resistance #MBEDTLS_CTR_DRBG_PR_ON or #MBEDTLS_CTR_DRBG_PR_OFF. + */ +void mbedtls_ctr_drbg_set_prediction_resistance(mbedtls_ctr_drbg_context *ctx, + int resistance); + +/** + * \brief This function sets the amount of entropy grabbed on each + * seed or reseed. + * + * The default value is #MBEDTLS_CTR_DRBG_ENTROPY_LEN. + * + * \note The security strength of CTR_DRBG is bounded by the + * entropy length. Thus: + * - When using AES-256 + * (\c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is disabled, + * which is the default), + * \p len must be at least 32 (in bytes) + * to achieve a 256-bit strength. + * - When using AES-128 + * (\c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY is enabled) + * \p len must be at least 16 (in bytes) + * to achieve a 128-bit strength. + * + * \param ctx The CTR_DRBG context. + * \param len The amount of entropy to grab, in bytes. + * This must be at most #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT + * and at most the maximum length accepted by the + * entropy function that is set in the context. + */ +void mbedtls_ctr_drbg_set_entropy_len(mbedtls_ctr_drbg_context *ctx, + size_t len); + +/** + * \brief This function sets the amount of entropy grabbed + * as a nonce for the initial seeding. + * + * Call this function before calling mbedtls_ctr_drbg_seed() to read + * a nonce from the entropy source during the initial seeding. + * + * \param ctx The CTR_DRBG context. + * \param len The amount of entropy to grab for the nonce, in bytes. + * This must be at most #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT + * and at most the maximum length accepted by the + * entropy function that is set in the context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG if \p len is + * more than #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT. + * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED + * if the initial seeding has already taken place. + */ +int mbedtls_ctr_drbg_set_nonce_len(mbedtls_ctr_drbg_context *ctx, + size_t len); + +/** + * \brief This function sets the reseed interval. + * + * The reseed interval is the number of calls to mbedtls_ctr_drbg_random() + * or mbedtls_ctr_drbg_random_with_add() after which the entropy function + * is called again. + * + * The default value is #MBEDTLS_CTR_DRBG_RESEED_INTERVAL. + * + * \param ctx The CTR_DRBG context. + * \param interval The reseed interval. + */ +void mbedtls_ctr_drbg_set_reseed_interval(mbedtls_ctr_drbg_context *ctx, + int interval); + +/** + * \brief This function reseeds the CTR_DRBG context, that is + * extracts data from the entropy source. + * + * \note This function is not thread-safe. It is not safe + * to call this function if another thread might be + * concurrently obtaining random numbers from the same + * context or updating or reseeding the same context. + * + * \param ctx The CTR_DRBG context. + * \param additional Additional data to add to the state. Can be \c NULL. + * \param len The length of the additional data. + * This must be less than + * #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - \c entropy_len + * where \c entropy_len is the entropy length + * configured for the context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on failure. + */ +int mbedtls_ctr_drbg_reseed(mbedtls_ctr_drbg_context *ctx, + const unsigned char *additional, size_t len); + +/** + * \brief This function updates the state of the CTR_DRBG context. + * + * \note This function is not thread-safe. It is not safe + * to call this function if another thread might be + * concurrently obtaining random numbers from the same + * context or updating or reseeding the same context. + * + * \param ctx The CTR_DRBG context. + * \param additional The data to update the state with. This must not be + * \c NULL unless \p add_len is \c 0. + * \param add_len Length of \p additional in bytes. This must be at + * most #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG if + * \p add_len is more than + * #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT. + * \return An error from the underlying AES cipher on failure. + */ +int mbedtls_ctr_drbg_update(mbedtls_ctr_drbg_context *ctx, + const unsigned char *additional, + size_t add_len); + +/** + * \brief This function updates a CTR_DRBG instance with additional + * data and uses it to generate random data. + * + * This function automatically reseeds if the reseed counter is exceeded + * or prediction resistance is enabled. + * + * \note This function is not thread-safe. It is not safe + * to call this function if another thread might be + * concurrently obtaining random numbers from the same + * context or updating or reseeding the same context. + * + * \param p_rng The CTR_DRBG context. This must be a pointer to a + * #mbedtls_ctr_drbg_context structure. + * \param output The buffer to fill. + * \param output_len The length of the buffer in bytes. + * \param additional Additional data to update. Can be \c NULL, in which + * case the additional data is empty regardless of + * the value of \p add_len. + * \param add_len The length of the additional data + * if \p additional is not \c NULL. + * This must be less than #MBEDTLS_CTR_DRBG_MAX_INPUT + * and less than + * #MBEDTLS_CTR_DRBG_MAX_SEED_INPUT - \c entropy_len + * where \c entropy_len is the entropy length + * configured for the context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or + * #MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG on failure. + */ +int mbedtls_ctr_drbg_random_with_add(void *p_rng, + unsigned char *output, size_t output_len, + const unsigned char *additional, size_t add_len); + +/** + * \brief This function uses CTR_DRBG to generate random data. + * + * This function automatically reseeds if the reseed counter is exceeded + * or prediction resistance is enabled. + */ +#if defined(MBEDTLS_THREADING_C) +/** + * \note When Mbed TLS is built with threading support, + * it is safe to call mbedtls_ctr_drbg_random() + * from multiple threads. Other operations, including + * reseeding, are not thread-safe. + */ +#endif /* MBEDTLS_THREADING_C */ +/** + * \param p_rng The CTR_DRBG context. This must be a pointer to a + * #mbedtls_ctr_drbg_context structure. + * \param output The buffer to fill. + * \param output_len The length of the buffer in bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED or + * #MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG on failure. + */ +int mbedtls_ctr_drbg_random(void *p_rng, + unsigned char *output, size_t output_len); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief This function writes a seed file. + * + * \param ctx The CTR_DRBG context. + * \param path The name of the file. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR on file error. + * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on reseed + * failure. + */ +int mbedtls_ctr_drbg_write_seed_file(mbedtls_ctr_drbg_context *ctx, const char *path); + +/** + * \brief This function reads and updates a seed file. The seed + * is added to this instance. + * + * \param ctx The CTR_DRBG context. + * \param path The name of the file. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR on file error. + * \return #MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED on + * reseed failure. + * \return #MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG if the existing + * seed file is too large. + */ +int mbedtls_ctr_drbg_update_seed_file(mbedtls_ctr_drbg_context *ctx, const char *path); +#endif /* MBEDTLS_FS_IO */ + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The CTR_DRBG checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_ctr_drbg_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* ctr_drbg.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/debug.h b/r5dev/thirdparty/mbedtls/include/mbedtls/debug.h new file mode 100644 index 00000000..2b0d00e4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/debug.h @@ -0,0 +1,312 @@ +/** + * \file debug.h + * + * \brief Functions for controlling and providing debug output from the library. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_DEBUG_H +#define MBEDTLS_DEBUG_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/ssl.h" + +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif + +#if defined(MBEDTLS_DEBUG_C) + +#define MBEDTLS_DEBUG_STRIP_PARENS(...) __VA_ARGS__ + +#define MBEDTLS_SSL_DEBUG_MSG(level, args) \ + mbedtls_debug_print_msg(ssl, level, __FILE__, __LINE__, \ + MBEDTLS_DEBUG_STRIP_PARENS args) + +#define MBEDTLS_SSL_DEBUG_RET(level, text, ret) \ + mbedtls_debug_print_ret(ssl, level, __FILE__, __LINE__, text, ret) + +#define MBEDTLS_SSL_DEBUG_BUF(level, text, buf, len) \ + mbedtls_debug_print_buf(ssl, level, __FILE__, __LINE__, text, buf, len) + +#if defined(MBEDTLS_BIGNUM_C) +#define MBEDTLS_SSL_DEBUG_MPI(level, text, X) \ + mbedtls_debug_print_mpi(ssl, level, __FILE__, __LINE__, text, X) +#endif + +#if defined(MBEDTLS_ECP_C) +#define MBEDTLS_SSL_DEBUG_ECP(level, text, X) \ + mbedtls_debug_print_ecp(ssl, level, __FILE__, __LINE__, text, X) +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if !defined(MBEDTLS_X509_REMOVE_INFO) +#define MBEDTLS_SSL_DEBUG_CRT(level, text, crt) \ + mbedtls_debug_print_crt(ssl, level, __FILE__, __LINE__, text, crt) +#else +#define MBEDTLS_SSL_DEBUG_CRT(level, text, crt) do { } while (0) +#endif /* MBEDTLS_X509_REMOVE_INFO */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_ECDH_C) +#define MBEDTLS_SSL_DEBUG_ECDH(level, ecdh, attr) \ + mbedtls_debug_printf_ecdh(ssl, level, __FILE__, __LINE__, ecdh, attr) +#endif + +#else /* MBEDTLS_DEBUG_C */ + +#define MBEDTLS_SSL_DEBUG_MSG(level, args) do { } while (0) +#define MBEDTLS_SSL_DEBUG_RET(level, text, ret) do { } while (0) +#define MBEDTLS_SSL_DEBUG_BUF(level, text, buf, len) do { } while (0) +#define MBEDTLS_SSL_DEBUG_MPI(level, text, X) do { } while (0) +#define MBEDTLS_SSL_DEBUG_ECP(level, text, X) do { } while (0) +#define MBEDTLS_SSL_DEBUG_CRT(level, text, crt) do { } while (0) +#define MBEDTLS_SSL_DEBUG_ECDH(level, ecdh, attr) do { } while (0) + +#endif /* MBEDTLS_DEBUG_C */ + +/** + * \def MBEDTLS_PRINTF_ATTRIBUTE + * + * Mark a function as having printf attributes, and thus enable checking + * via -wFormat and other flags. This does nothing on builds with compilers + * that do not support the format attribute + * + * Module: library/debug.c + * Caller: + * + * This module provides debugging functions. + */ +#if defined(__has_attribute) +#if __has_attribute(format) +#if defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 1 +#define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((__format__(gnu_printf, string_index, first_to_check))) +#else /* defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 1 */ +#define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check) \ + __attribute__((format(printf, string_index, first_to_check))) +#endif +#else /* __has_attribute(format) */ +#define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check) +#endif /* __has_attribute(format) */ +#else /* defined(__has_attribute) */ +#define MBEDTLS_PRINTF_ATTRIBUTE(string_index, first_to_check) +#endif + +/** + * \def MBEDTLS_PRINTF_SIZET + * + * MBEDTLS_PRINTF_xxx: Due to issues with older window compilers + * and MinGW we need to define the printf specifier for size_t + * and long long per platform. + * + * Module: library/debug.c + * Caller: + * + * This module provides debugging functions. + */ +#if (defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) + #include + #define MBEDTLS_PRINTF_SIZET PRIuPTR + #define MBEDTLS_PRINTF_LONGLONG "I64d" +#else \ + /* (defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) */ + #define MBEDTLS_PRINTF_SIZET "zu" + #define MBEDTLS_PRINTF_LONGLONG "lld" +#endif \ + /* (defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 0) || (defined(_MSC_VER) && _MSC_VER < 1800) */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Set the threshold error level to handle globally all debug output. + * Debug messages that have a level over the threshold value are + * discarded. + * (Default value: 0 = No debug ) + * + * \param threshold threshold level of messages to filter on. Messages at a + * higher level will be discarded. + * - Debug levels + * - 0 No debug + * - 1 Error + * - 2 State change + * - 3 Informational + * - 4 Verbose + */ +void mbedtls_debug_set_threshold(int threshold); + +/** + * \brief Print a message to the debug output. This function is always used + * through the MBEDTLS_SSL_DEBUG_MSG() macro, which supplies the ssl + * context, file and line number parameters. + * + * \param ssl SSL context + * \param level error level of the debug message + * \param file file the message has occurred in + * \param line line number the message has occurred at + * \param format format specifier, in printf format + * \param ... variables used by the format specifier + * + * \attention This function is intended for INTERNAL usage within the + * library only. + */ +void mbedtls_debug_print_msg(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *format, ...) MBEDTLS_PRINTF_ATTRIBUTE(5, 6); + +/** + * \brief Print the return value of a function to the debug output. This + * function is always used through the MBEDTLS_SSL_DEBUG_RET() macro, + * which supplies the ssl context, file and line number parameters. + * + * \param ssl SSL context + * \param level error level of the debug message + * \param file file the error has occurred in + * \param line line number the error has occurred in + * \param text the name of the function that returned the error + * \param ret the return code value + * + * \attention This function is intended for INTERNAL usage within the + * library only. + */ +void mbedtls_debug_print_ret(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, int ret); + +/** + * \brief Output a buffer of size len bytes to the debug output. This function + * is always used through the MBEDTLS_SSL_DEBUG_BUF() macro, + * which supplies the ssl context, file and line number parameters. + * + * \param ssl SSL context + * \param level error level of the debug message + * \param file file the error has occurred in + * \param line line number the error has occurred in + * \param text a name or label for the buffer being dumped. Normally the + * variable or buffer name + * \param buf the buffer to be outputted + * \param len length of the buffer + * + * \attention This function is intended for INTERNAL usage within the + * library only. + */ +void mbedtls_debug_print_buf(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, const char *text, + const unsigned char *buf, size_t len); + +#if defined(MBEDTLS_BIGNUM_C) +/** + * \brief Print a MPI variable to the debug output. This function is always + * used through the MBEDTLS_SSL_DEBUG_MPI() macro, which supplies the + * ssl context, file and line number parameters. + * + * \param ssl SSL context + * \param level error level of the debug message + * \param file file the error has occurred in + * \param line line number the error has occurred in + * \param text a name or label for the MPI being output. Normally the + * variable name + * \param X the MPI variable + * + * \attention This function is intended for INTERNAL usage within the + * library only. + */ +void mbedtls_debug_print_mpi(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mbedtls_mpi *X); +#endif + +#if defined(MBEDTLS_ECP_C) +/** + * \brief Print an ECP point to the debug output. This function is always + * used through the MBEDTLS_SSL_DEBUG_ECP() macro, which supplies the + * ssl context, file and line number parameters. + * + * \param ssl SSL context + * \param level error level of the debug message + * \param file file the error has occurred in + * \param line line number the error has occurred in + * \param text a name or label for the ECP point being output. Normally the + * variable name + * \param X the ECP point + * + * \attention This function is intended for INTERNAL usage within the + * library only. + */ +void mbedtls_debug_print_ecp(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mbedtls_ecp_point *X); +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && !defined(MBEDTLS_X509_REMOVE_INFO) +/** + * \brief Print a X.509 certificate structure to the debug output. This + * function is always used through the MBEDTLS_SSL_DEBUG_CRT() macro, + * which supplies the ssl context, file and line number parameters. + * + * \param ssl SSL context + * \param level error level of the debug message + * \param file file the error has occurred in + * \param line line number the error has occurred in + * \param text a name or label for the certificate being output + * \param crt X.509 certificate structure + * + * \attention This function is intended for INTERNAL usage within the + * library only. + */ +void mbedtls_debug_print_crt(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const char *text, const mbedtls_x509_crt *crt); +#endif + +#if defined(MBEDTLS_ECDH_C) +typedef enum { + MBEDTLS_DEBUG_ECDH_Q, + MBEDTLS_DEBUG_ECDH_QP, + MBEDTLS_DEBUG_ECDH_Z, +} mbedtls_debug_ecdh_attr; + +/** + * \brief Print a field of the ECDH structure in the SSL context to the debug + * output. This function is always used through the + * MBEDTLS_SSL_DEBUG_ECDH() macro, which supplies the ssl context, file + * and line number parameters. + * + * \param ssl SSL context + * \param level error level of the debug message + * \param file file the error has occurred in + * \param line line number the error has occurred in + * \param ecdh the ECDH context + * \param attr the identifier of the attribute being output + * + * \attention This function is intended for INTERNAL usage within the + * library only. + */ +void mbedtls_debug_printf_ecdh(const mbedtls_ssl_context *ssl, int level, + const char *file, int line, + const mbedtls_ecdh_context *ecdh, + mbedtls_debug_ecdh_attr attr); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* debug.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/des.h b/r5dev/thirdparty/mbedtls/include/mbedtls/des.h new file mode 100644 index 00000000..f445102d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/des.h @@ -0,0 +1,397 @@ +/** + * \file des.h + * + * \brief DES block cipher + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef MBEDTLS_DES_H +#define MBEDTLS_DES_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" +#include "mbedtls/platform_util.h" + +#include +#include + +#define MBEDTLS_DES_ENCRYPT 1 +#define MBEDTLS_DES_DECRYPT 0 + +/** The data input has an invalid length. */ +#define MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH -0x0032 + +#define MBEDTLS_DES_KEY_SIZE 8 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_DES_ALT) +// Regular implementation +// + +/** + * \brief DES context structure + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +typedef struct mbedtls_des_context { + uint32_t MBEDTLS_PRIVATE(sk)[32]; /*!< DES subkeys */ +} +mbedtls_des_context; + +/** + * \brief Triple-DES context structure + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +typedef struct mbedtls_des3_context { + uint32_t MBEDTLS_PRIVATE(sk)[96]; /*!< 3DES subkeys */ +} +mbedtls_des3_context; + +#else /* MBEDTLS_DES_ALT */ +#include "des_alt.h" +#endif /* MBEDTLS_DES_ALT */ + +/** + * \brief Initialize DES context + * + * \param ctx DES context to be initialized + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +void mbedtls_des_init(mbedtls_des_context *ctx); + +/** + * \brief Clear DES context + * + * \param ctx DES context to be cleared + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +void mbedtls_des_free(mbedtls_des_context *ctx); + +/** + * \brief Initialize Triple-DES context + * + * \param ctx DES3 context to be initialized + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +void mbedtls_des3_init(mbedtls_des3_context *ctx); + +/** + * \brief Clear Triple-DES context + * + * \param ctx DES3 context to be cleared + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +void mbedtls_des3_free(mbedtls_des3_context *ctx); + +/** + * \brief Set key parity on the given key to odd. + * + * DES keys are 56 bits long, but each byte is padded with + * a parity bit to allow verification. + * + * \param key 8-byte secret key + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +void mbedtls_des_key_set_parity(unsigned char key[MBEDTLS_DES_KEY_SIZE]); + +/** + * \brief Check that key parity on the given key is odd. + * + * DES keys are 56 bits long, but each byte is padded with + * a parity bit to allow verification. + * + * \param key 8-byte secret key + * + * \return 0 is parity was ok, 1 if parity was not correct. + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des_key_check_key_parity(const unsigned char key[MBEDTLS_DES_KEY_SIZE]); + +/** + * \brief Check that key is not a weak or semi-weak DES key + * + * \param key 8-byte secret key + * + * \return 0 if no weak key was found, 1 if a weak key was identified. + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des_key_check_weak(const unsigned char key[MBEDTLS_DES_KEY_SIZE]); + +/** + * \brief DES key schedule (56-bit, encryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + * + * \return 0 + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des_setkey_enc(mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]); + +/** + * \brief DES key schedule (56-bit, decryption) + * + * \param ctx DES context to be initialized + * \param key 8-byte secret key + * + * \return 0 + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des_setkey_dec(mbedtls_des_context *ctx, const unsigned char key[MBEDTLS_DES_KEY_SIZE]); + +/** + * \brief Triple-DES key schedule (112-bit, encryption) + * + * \param ctx 3DES context to be initialized + * \param key 16-byte secret key + * + * \return 0 + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des3_set2key_enc(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2]); + +/** + * \brief Triple-DES key schedule (112-bit, decryption) + * + * \param ctx 3DES context to be initialized + * \param key 16-byte secret key + * + * \return 0 + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des3_set2key_dec(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 2]); + +/** + * \brief Triple-DES key schedule (168-bit, encryption) + * + * \param ctx 3DES context to be initialized + * \param key 24-byte secret key + * + * \return 0 + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des3_set3key_enc(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]); + +/** + * \brief Triple-DES key schedule (168-bit, decryption) + * + * \param ctx 3DES context to be initialized + * \param key 24-byte secret key + * + * \return 0 + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des3_set3key_dec(mbedtls_des3_context *ctx, + const unsigned char key[MBEDTLS_DES_KEY_SIZE * 3]); + +/** + * \brief DES-ECB block encryption/decryption + * + * \param ctx DES context + * \param input 64-bit input block + * \param output 64-bit output block + * + * \return 0 if successful + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des_crypt_ecb(mbedtls_des_context *ctx, + const unsigned char input[8], + unsigned char output[8]); + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/** + * \brief DES-CBC buffer encryption/decryption + * + * \note Upon exit, the content of the IV is updated so that you can + * call the function same function again on the following + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If on the other hand you need to retain the contents of the + * IV, you should either save it manually or use the cipher + * module instead. + * + * \param ctx DES context + * \param mode MBEDTLS_DES_ENCRYPT or MBEDTLS_DES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des_crypt_cbc(mbedtls_des_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +/** + * \brief 3DES-ECB block encryption/decryption + * + * \param ctx 3DES context + * \param input 64-bit input block + * \param output 64-bit output block + * + * \return 0 if successful + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des3_crypt_ecb(mbedtls_des3_context *ctx, + const unsigned char input[8], + unsigned char output[8]); + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +/** + * \brief 3DES-CBC buffer encryption/decryption + * + * \note Upon exit, the content of the IV is updated so that you can + * call the function same function again on the following + * block(s) of data and get the same result as if it was + * encrypted in one call. This allows a "streaming" usage. + * If on the other hand you need to retain the contents of the + * IV, you should either save it manually or use the cipher + * module instead. + * + * \param ctx 3DES context + * \param mode MBEDTLS_DES_ENCRYPT or MBEDTLS_DES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if successful, or MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_des3_crypt_cbc(mbedtls_des3_context *ctx, + int mode, + size_t length, + unsigned char iv[8], + const unsigned char *input, + unsigned char *output); +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +/** + * \brief Internal function for key expansion. + * (Only exposed to allow overriding it, + * see MBEDTLS_DES_SETKEY_ALT) + * + * \param SK Round keys + * \param key Base key + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers + * instead. + */ +void mbedtls_des_setkey(uint32_t SK[32], + const unsigned char key[MBEDTLS_DES_KEY_SIZE]); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_des_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* des.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/dhm.h b/r5dev/thirdparty/mbedtls/include/mbedtls/dhm.h new file mode 100644 index 00000000..6ffe681c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/dhm.h @@ -0,0 +1,984 @@ +/** + * \file dhm.h + * + * \brief This file contains Diffie-Hellman-Merkle (DHM) key exchange + * definitions and functions. + * + * Diffie-Hellman-Merkle (DHM) key exchange is defined in + * RFC-2631: Diffie-Hellman Key Agreement Method and + * Public-Key Cryptography Standards (PKCS) #3: Diffie + * Hellman Key Agreement Standard. + * + * RFC-3526: More Modular Exponential (MODP) Diffie-Hellman groups for + * Internet Key Exchange (IKE) defines a number of standardized + * Diffie-Hellman groups for IKE. + * + * RFC-5114: Additional Diffie-Hellman Groups for Use with IETF + * Standards defines a number of standardized Diffie-Hellman + * groups that can be used. + * + * \warning The security of the DHM key exchange relies on the proper choice + * of prime modulus - optimally, it should be a safe prime. The usage + * of non-safe primes both decreases the difficulty of the underlying + * discrete logarithm problem and can lead to small subgroup attacks + * leaking private exponent bits when invalid public keys are used + * and not detected. This is especially relevant if the same DHM + * parameters are reused for multiple key exchanges as in static DHM, + * while the criticality of small-subgroup attacks is lower for + * ephemeral DHM. + * + * \warning For performance reasons, the code does neither perform primality + * nor safe primality tests, nor the expensive checks for invalid + * subgroups. Moreover, even if these were performed, non-standardized + * primes cannot be trusted because of the possibility of backdoors + * that can't be effectively checked for. + * + * \warning Diffie-Hellman-Merkle is therefore a security risk when not using + * standardized primes generated using a trustworthy ("nothing up + * my sleeve") method, such as the RFC 3526 / 7919 primes. In the TLS + * protocol, DH parameters need to be negotiated, so using the default + * primes systematically is not always an option. If possible, use + * Elliptic Curve Diffie-Hellman (ECDH), which has better performance, + * and for which the TLS protocol mandates the use of standard + * parameters. + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_DHM_H +#define MBEDTLS_DHM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" +#include "mbedtls/bignum.h" + +/* + * DHM Error codes + */ +/** Bad input parameters. */ +#define MBEDTLS_ERR_DHM_BAD_INPUT_DATA -0x3080 +/** Reading of the DHM parameters failed. */ +#define MBEDTLS_ERR_DHM_READ_PARAMS_FAILED -0x3100 +/** Making of the DHM parameters failed. */ +#define MBEDTLS_ERR_DHM_MAKE_PARAMS_FAILED -0x3180 +/** Reading of the public values failed. */ +#define MBEDTLS_ERR_DHM_READ_PUBLIC_FAILED -0x3200 +/** Making of the public value failed. */ +#define MBEDTLS_ERR_DHM_MAKE_PUBLIC_FAILED -0x3280 +/** Calculation of the DHM secret failed. */ +#define MBEDTLS_ERR_DHM_CALC_SECRET_FAILED -0x3300 +/** The ASN.1 data is not formatted correctly. */ +#define MBEDTLS_ERR_DHM_INVALID_FORMAT -0x3380 +/** Allocation of memory failed. */ +#define MBEDTLS_ERR_DHM_ALLOC_FAILED -0x3400 +/** Read or write of file failed. */ +#define MBEDTLS_ERR_DHM_FILE_IO_ERROR -0x3480 +/** Setting the modulus and generator failed. */ +#define MBEDTLS_ERR_DHM_SET_GROUP_FAILED -0x3580 + +/** Which parameter to access in mbedtls_dhm_get_value(). */ +typedef enum { + MBEDTLS_DHM_PARAM_P, /*!< The prime modulus. */ + MBEDTLS_DHM_PARAM_G, /*!< The generator. */ + MBEDTLS_DHM_PARAM_X, /*!< Our secret value. */ + MBEDTLS_DHM_PARAM_GX, /*!< Our public key = \c G^X mod \c P. */ + MBEDTLS_DHM_PARAM_GY, /*!< The public key of the peer = \c G^Y mod \c P. */ + MBEDTLS_DHM_PARAM_K, /*!< The shared secret = \c G^(XY) mod \c P. */ +} mbedtls_dhm_parameter; + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_DHM_ALT) + +/** + * \brief The DHM context structure. + */ +typedef struct mbedtls_dhm_context { + mbedtls_mpi MBEDTLS_PRIVATE(P); /*!< The prime modulus. */ + mbedtls_mpi MBEDTLS_PRIVATE(G); /*!< The generator. */ + mbedtls_mpi MBEDTLS_PRIVATE(X); /*!< Our secret value. */ + mbedtls_mpi MBEDTLS_PRIVATE(GX); /*!< Our public key = \c G^X mod \c P. */ + mbedtls_mpi MBEDTLS_PRIVATE(GY); /*!< The public key of the peer = \c G^Y mod \c P. */ + mbedtls_mpi MBEDTLS_PRIVATE(K); /*!< The shared secret = \c G^(XY) mod \c P. */ + mbedtls_mpi MBEDTLS_PRIVATE(RP); /*!< The cached value = \c R^2 mod \c P. */ + mbedtls_mpi MBEDTLS_PRIVATE(Vi); /*!< The blinding value. */ + mbedtls_mpi MBEDTLS_PRIVATE(Vf); /*!< The unblinding value. */ + mbedtls_mpi MBEDTLS_PRIVATE(pX); /*!< The previous \c X. */ +} +mbedtls_dhm_context; + +#else /* MBEDTLS_DHM_ALT */ +#include "dhm_alt.h" +#endif /* MBEDTLS_DHM_ALT */ + +/** + * \brief This function initializes the DHM context. + * + * \param ctx The DHM context to initialize. + */ +void mbedtls_dhm_init(mbedtls_dhm_context *ctx); + +/** + * \brief This function parses the DHM parameters in a + * TLS ServerKeyExchange handshake message + * (DHM modulus, generator, and public key). + * + * \note In a TLS handshake, this is the how the client + * sets up its DHM context from the server's public + * DHM key material. + * + * \param ctx The DHM context to use. This must be initialized. + * \param p On input, *p must be the start of the input buffer. + * On output, *p is updated to point to the end of the data + * that has been read. On success, this is the first byte + * past the end of the ServerKeyExchange parameters. + * On error, this is the point at which an error has been + * detected, which is usually not useful except to debug + * failures. + * \param end The end of the input buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure. + */ +int mbedtls_dhm_read_params(mbedtls_dhm_context *ctx, + unsigned char **p, + const unsigned char *end); + +/** + * \brief This function generates a DHM key pair and exports its + * public part together with the DHM parameters in the format + * used in a TLS ServerKeyExchange handshake message. + * + * \note This function assumes that the DHM parameters \c ctx->P + * and \c ctx->G have already been properly set. For that, use + * mbedtls_dhm_set_group() below in conjunction with + * mbedtls_mpi_read_binary() and mbedtls_mpi_read_string(). + * + * \note In a TLS handshake, this is the how the server generates + * and exports its DHM key material. + * + * \param ctx The DHM context to use. This must be initialized + * and have the DHM parameters set. It may or may not + * already have imported the peer's public key. + * \param x_size The private key size in Bytes. + * \param olen The address at which to store the number of Bytes + * written on success. This must not be \c NULL. + * \param output The destination buffer. This must be a writable buffer of + * sufficient size to hold the reduced binary presentation of + * the modulus, the generator and the public key, each wrapped + * with a 2-byte length field. It is the responsibility of the + * caller to ensure that enough space is available. Refer to + * mbedtls_mpi_size() to computing the byte-size of an MPI. + * \param f_rng The RNG function. Must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure. + */ +int mbedtls_dhm_make_params(mbedtls_dhm_context *ctx, int x_size, + unsigned char *output, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function sets the prime modulus and generator. + * + * \note This function can be used to set \c ctx->P, \c ctx->G + * in preparation for mbedtls_dhm_make_params(). + * + * \param ctx The DHM context to configure. This must be initialized. + * \param P The MPI holding the DHM prime modulus. This must be + * an initialized MPI. + * \param G The MPI holding the DHM generator. This must be an + * initialized MPI. + * + * \return \c 0 if successful. + * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure. + */ +int mbedtls_dhm_set_group(mbedtls_dhm_context *ctx, + const mbedtls_mpi *P, + const mbedtls_mpi *G); + +/** + * \brief This function imports the raw public value of the peer. + * + * \note In a TLS handshake, this is the how the server imports + * the Client's public DHM key. + * + * \param ctx The DHM context to use. This must be initialized and have + * its DHM parameters set, e.g. via mbedtls_dhm_set_group(). + * It may or may not already have generated its own private key. + * \param input The input buffer containing the \c G^Y value of the peer. + * This must be a readable buffer of size \p ilen Bytes. + * \param ilen The size of the input buffer \p input in Bytes. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure. + */ +int mbedtls_dhm_read_public(mbedtls_dhm_context *ctx, + const unsigned char *input, size_t ilen); + +/** + * \brief This function creates a DHM key pair and exports + * the raw public key in big-endian format. + * + * \note The destination buffer is always fully written + * so as to contain a big-endian representation of G^X mod P. + * If it is larger than \c ctx->len, it is padded accordingly + * with zero-bytes at the beginning. + * + * \param ctx The DHM context to use. This must be initialized and + * have the DHM parameters set. It may or may not already + * have imported the peer's public key. + * \param x_size The private key size in Bytes. + * \param output The destination buffer. This must be a writable buffer of + * size \p olen Bytes. + * \param olen The length of the destination buffer. This must be at least + * equal to `ctx->len` (the size of \c P). + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure. + */ +int mbedtls_dhm_make_public(mbedtls_dhm_context *ctx, int x_size, + unsigned char *output, size_t olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function derives and exports the shared secret + * \c (G^Y)^X mod \c P. + * + * \note If \p f_rng is not \c NULL, it is used to blind the input as + * a countermeasure against timing attacks. Blinding is used + * only if our private key \c X is re-used, and not used + * otherwise. We recommend always passing a non-NULL + * \p f_rng argument. + * + * \param ctx The DHM context to use. This must be initialized + * and have its own private key generated and the peer's + * public key imported. + * \param output The buffer to write the generated shared key to. This + * must be a writable buffer of size \p output_size Bytes. + * \param output_size The size of the destination buffer. This must be at + * least the size of \c ctx->len (the size of \c P). + * \param olen On exit, holds the actual number of Bytes written. + * \param f_rng The RNG function. Must not be \c NULL. Used for + * blinding. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_DHM_XXX error code on failure. + */ +int mbedtls_dhm_calc_secret(mbedtls_dhm_context *ctx, + unsigned char *output, size_t output_size, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function returns the size of the prime modulus in bits. + * + * \param ctx The DHM context to query. + * + * \return The size of the prime modulus in bits, + * i.e. the number n such that 2^(n-1) <= P < 2^n. + */ +size_t mbedtls_dhm_get_bitlen(const mbedtls_dhm_context *ctx); + +/** + * \brief This function returns the size of the prime modulus in bytes. + * + * \param ctx The DHM context to query. + * + * \return The size of the prime modulus in bytes, + * i.e. the number n such that 2^(8*(n-1)) <= P < 2^(8*n). + */ +size_t mbedtls_dhm_get_len(const mbedtls_dhm_context *ctx); + +/** + * \brief This function copies a parameter of a DHM key. + * + * \param ctx The DHM context to query. + * \param param The parameter to copy. + * \param dest The MPI object to copy the value into. It must be + * initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_DHM_BAD_INPUT_DATA if \p field is invalid. + * \return An \c MBEDTLS_ERR_MPI_XXX error code if the copy fails. + */ +int mbedtls_dhm_get_value(const mbedtls_dhm_context *ctx, + mbedtls_dhm_parameter param, + mbedtls_mpi *dest); + +/** + * \brief This function frees and clears the components + * of a DHM context. + * + * \param ctx The DHM context to free and clear. This may be \c NULL, + * in which case this function is a no-op. If it is not \c NULL, + * it must point to an initialized DHM context. + */ +void mbedtls_dhm_free(mbedtls_dhm_context *ctx); + +#if defined(MBEDTLS_ASN1_PARSE_C) +/** + * \brief This function parses DHM parameters in PEM or DER format. + * + * \param dhm The DHM context to import the DHM parameters into. + * This must be initialized. + * \param dhmin The input buffer. This must be a readable buffer of + * length \p dhminlen Bytes. + * \param dhminlen The size of the input buffer \p dhmin, including the + * terminating \c NULL Byte for PEM data. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_DHM_XXX or \c MBEDTLS_ERR_PEM_XXX error + * code on failure. + */ +int mbedtls_dhm_parse_dhm(mbedtls_dhm_context *dhm, const unsigned char *dhmin, + size_t dhminlen); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief This function loads and parses DHM parameters from a file. + * + * \param dhm The DHM context to load the parameters to. + * This must be initialized. + * \param path The filename to read the DHM parameters from. + * This must not be \c NULL. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_DHM_XXX or \c MBEDTLS_ERR_PEM_XXX + * error code on failure. + */ +int mbedtls_dhm_parse_dhmfile(mbedtls_dhm_context *dhm, const char *path); +#endif /* MBEDTLS_FS_IO */ +#endif /* MBEDTLS_ASN1_PARSE_C */ + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The DMH checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_dhm_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ +#ifdef __cplusplus +} +#endif + +/** + * RFC 3526, RFC 5114 and RFC 7919 standardize a number of + * Diffie-Hellman groups, some of which are included here + * for use within the SSL/TLS module and the user's convenience + * when configuring the Diffie-Hellman parameters by hand + * through \c mbedtls_ssl_conf_dh_param. + * + * The following lists the source of the above groups in the standards: + * - RFC 5114 section 2.2: 2048-bit MODP Group with 224-bit Prime Order Subgroup + * - RFC 3526 section 3: 2048-bit MODP Group + * - RFC 3526 section 4: 3072-bit MODP Group + * - RFC 3526 section 5: 4096-bit MODP Group + * - RFC 7919 section A.1: ffdhe2048 + * - RFC 7919 section A.2: ffdhe3072 + * - RFC 7919 section A.3: ffdhe4096 + * - RFC 7919 section A.4: ffdhe6144 + * - RFC 7919 section A.5: ffdhe8192 + * + * The constants with suffix "_p" denote the chosen prime moduli, while + * the constants with suffix "_g" denote the chosen generator + * of the associated prime field. + * + * The constants further suffixed with "_bin" are provided in binary format, + * while all other constants represent null-terminated strings holding the + * hexadecimal presentation of the respective numbers. + * + * The primes from RFC 3526 and RFC 7919 have been generating by the following + * trust-worthy procedure: + * - Fix N in { 2048, 3072, 4096, 6144, 8192 } and consider the N-bit number + * the first and last 64 bits are all 1, and the remaining N - 128 bits of + * which are 0x7ff...ff. + * - Add the smallest multiple of the first N - 129 bits of the binary expansion + * of pi (for RFC 5236) or e (for RFC 7919) to this intermediate bit-string + * such that the resulting integer is a safe-prime. + * - The result is the respective RFC 3526 / 7919 prime, and the corresponding + * generator is always chosen to be 2 (which is a square for these prime, + * hence the corresponding subgroup has order (p-1)/2 and avoids leaking a + * bit in the private exponent). + * + */ + +/* + * Trustworthy DHM parameters in binary form + */ + +#define MBEDTLS_DHM_RFC3526_MODP_2048_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, \ + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, \ + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, \ + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, \ + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, \ + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, \ + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, \ + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, \ + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, \ + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, \ + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, \ + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, \ + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, \ + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, \ + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, \ + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, \ + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, \ + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, \ + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, \ + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, \ + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, \ + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, \ + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, \ + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, \ + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, \ + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, \ + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, \ + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, \ + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, \ + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +#define MBEDTLS_DHM_RFC3526_MODP_2048_G_BIN { 0x02 } + +#define MBEDTLS_DHM_RFC3526_MODP_3072_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, \ + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, \ + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, \ + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, \ + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, \ + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, \ + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, \ + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, \ + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, \ + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, \ + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, \ + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, \ + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, \ + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, \ + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, \ + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, \ + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, \ + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, \ + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, \ + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, \ + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, \ + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, \ + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, \ + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, \ + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, \ + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, \ + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, \ + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, \ + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, \ + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, \ + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, \ + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, \ + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, \ + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, \ + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, \ + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, \ + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, \ + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, \ + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, \ + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, \ + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, \ + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, \ + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, \ + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, \ + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, \ + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +#define MBEDTLS_DHM_RFC3526_MODP_3072_G_BIN { 0x02 } + +#define MBEDTLS_DHM_RFC3526_MODP_4096_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, \ + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, \ + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, \ + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, \ + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, \ + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, \ + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, \ + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, \ + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, \ + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, \ + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, \ + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, \ + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, \ + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, \ + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, \ + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, \ + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, \ + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, \ + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, \ + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, \ + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, \ + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, \ + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, \ + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, \ + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, \ + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, \ + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, \ + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, \ + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, \ + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, \ + 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33, \ + 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, \ + 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, \ + 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, \ + 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7, \ + 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, \ + 0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D, \ + 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, \ + 0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, \ + 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, \ + 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, \ + 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, \ + 0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, \ + 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31, \ + 0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, \ + 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01, \ + 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7, \ + 0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, \ + 0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C, \ + 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA, \ + 0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, \ + 0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9, \ + 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6, \ + 0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, \ + 0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2, \ + 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED, \ + 0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, \ + 0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C, \ + 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9, \ + 0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, \ + 0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F, \ + 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +#define MBEDTLS_DHM_RFC3526_MODP_4096_G_BIN { 0x02 } + +#define MBEDTLS_DHM_RFC7919_FFDHE2048_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \ + 0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \ + 0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \ + 0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \ + 0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \ + 0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \ + 0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \ + 0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \ + 0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \ + 0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \ + 0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \ + 0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \ + 0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \ + 0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \ + 0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \ + 0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \ + 0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \ + 0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \ + 0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \ + 0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \ + 0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \ + 0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \ + 0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \ + 0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \ + 0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \ + 0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \ + 0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \ + 0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \ + 0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \ + 0x88, 0x6B, 0x42, 0x38, 0x61, 0x28, 0x5C, 0x97, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, } + +#define MBEDTLS_DHM_RFC7919_FFDHE2048_G_BIN { 0x02 } + +#define MBEDTLS_DHM_RFC7919_FFDHE3072_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \ + 0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \ + 0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \ + 0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \ + 0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \ + 0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \ + 0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \ + 0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \ + 0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \ + 0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \ + 0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \ + 0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \ + 0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \ + 0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \ + 0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \ + 0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \ + 0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \ + 0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \ + 0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \ + 0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \ + 0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \ + 0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \ + 0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \ + 0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \ + 0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \ + 0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \ + 0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \ + 0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \ + 0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \ + 0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \ + 0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \ + 0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \ + 0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \ + 0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \ + 0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \ + 0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \ + 0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \ + 0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \ + 0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \ + 0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \ + 0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \ + 0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \ + 0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \ + 0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \ + 0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \ + 0x25, 0xE4, 0x1D, 0x2B, 0x66, 0xC6, 0x2E, 0x37, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +#define MBEDTLS_DHM_RFC7919_FFDHE3072_G_BIN { 0x02 } + +#define MBEDTLS_DHM_RFC7919_FFDHE4096_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \ + 0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \ + 0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \ + 0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \ + 0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \ + 0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \ + 0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \ + 0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \ + 0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \ + 0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \ + 0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \ + 0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \ + 0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \ + 0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \ + 0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \ + 0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \ + 0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \ + 0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \ + 0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \ + 0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \ + 0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \ + 0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \ + 0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \ + 0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \ + 0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \ + 0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \ + 0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \ + 0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \ + 0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \ + 0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \ + 0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \ + 0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \ + 0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \ + 0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \ + 0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \ + 0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \ + 0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \ + 0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \ + 0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \ + 0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \ + 0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \ + 0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \ + 0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \ + 0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \ + 0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \ + 0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \ + 0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \ + 0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \ + 0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \ + 0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \ + 0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \ + 0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \ + 0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \ + 0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \ + 0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \ + 0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \ + 0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \ + 0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \ + 0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \ + 0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \ + 0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \ + 0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x65, 0x5F, 0x6A, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +#define MBEDTLS_DHM_RFC7919_FFDHE4096_G_BIN { 0x02 } + +#define MBEDTLS_DHM_RFC7919_FFDHE6144_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \ + 0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \ + 0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \ + 0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \ + 0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \ + 0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \ + 0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \ + 0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \ + 0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \ + 0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \ + 0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \ + 0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \ + 0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \ + 0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \ + 0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \ + 0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \ + 0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \ + 0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \ + 0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \ + 0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \ + 0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \ + 0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \ + 0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \ + 0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \ + 0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \ + 0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \ + 0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \ + 0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \ + 0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \ + 0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \ + 0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \ + 0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \ + 0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \ + 0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \ + 0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \ + 0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \ + 0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \ + 0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \ + 0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \ + 0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \ + 0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \ + 0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \ + 0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \ + 0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \ + 0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \ + 0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \ + 0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \ + 0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \ + 0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \ + 0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \ + 0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \ + 0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \ + 0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \ + 0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \ + 0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \ + 0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \ + 0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \ + 0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \ + 0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \ + 0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \ + 0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \ + 0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02, \ + 0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A, \ + 0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A, \ + 0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6, \ + 0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8, \ + 0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C, \ + 0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A, \ + 0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71, \ + 0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F, \ + 0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77, \ + 0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10, \ + 0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8, \ + 0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3, \ + 0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E, \ + 0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3, \ + 0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4, \ + 0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1, \ + 0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92, \ + 0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6, \ + 0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82, \ + 0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE, \ + 0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C, \ + 0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E, \ + 0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46, \ + 0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A, \ + 0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17, \ + 0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03, \ + 0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04, \ + 0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6, \ + 0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69, \ + 0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1, \ + 0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4, \ + 0xA4, 0x0E, 0x32, 0x9C, 0xD0, 0xE4, 0x0E, 0x65, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +#define MBEDTLS_DHM_RFC7919_FFDHE6144_G_BIN { 0x02 } + +#define MBEDTLS_DHM_RFC7919_FFDHE8192_P_BIN { \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, \ + 0xAD, 0xF8, 0x54, 0x58, 0xA2, 0xBB, 0x4A, 0x9A, \ + 0xAF, 0xDC, 0x56, 0x20, 0x27, 0x3D, 0x3C, 0xF1, \ + 0xD8, 0xB9, 0xC5, 0x83, 0xCE, 0x2D, 0x36, 0x95, \ + 0xA9, 0xE1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xFB, \ + 0xCC, 0x93, 0x9D, 0xCE, 0x24, 0x9B, 0x3E, 0xF9, \ + 0x7D, 0x2F, 0xE3, 0x63, 0x63, 0x0C, 0x75, 0xD8, \ + 0xF6, 0x81, 0xB2, 0x02, 0xAE, 0xC4, 0x61, 0x7A, \ + 0xD3, 0xDF, 0x1E, 0xD5, 0xD5, 0xFD, 0x65, 0x61, \ + 0x24, 0x33, 0xF5, 0x1F, 0x5F, 0x06, 0x6E, 0xD0, \ + 0x85, 0x63, 0x65, 0x55, 0x3D, 0xED, 0x1A, 0xF3, \ + 0xB5, 0x57, 0x13, 0x5E, 0x7F, 0x57, 0xC9, 0x35, \ + 0x98, 0x4F, 0x0C, 0x70, 0xE0, 0xE6, 0x8B, 0x77, \ + 0xE2, 0xA6, 0x89, 0xDA, 0xF3, 0xEF, 0xE8, 0x72, \ + 0x1D, 0xF1, 0x58, 0xA1, 0x36, 0xAD, 0xE7, 0x35, \ + 0x30, 0xAC, 0xCA, 0x4F, 0x48, 0x3A, 0x79, 0x7A, \ + 0xBC, 0x0A, 0xB1, 0x82, 0xB3, 0x24, 0xFB, 0x61, \ + 0xD1, 0x08, 0xA9, 0x4B, 0xB2, 0xC8, 0xE3, 0xFB, \ + 0xB9, 0x6A, 0xDA, 0xB7, 0x60, 0xD7, 0xF4, 0x68, \ + 0x1D, 0x4F, 0x42, 0xA3, 0xDE, 0x39, 0x4D, 0xF4, \ + 0xAE, 0x56, 0xED, 0xE7, 0x63, 0x72, 0xBB, 0x19, \ + 0x0B, 0x07, 0xA7, 0xC8, 0xEE, 0x0A, 0x6D, 0x70, \ + 0x9E, 0x02, 0xFC, 0xE1, 0xCD, 0xF7, 0xE2, 0xEC, \ + 0xC0, 0x34, 0x04, 0xCD, 0x28, 0x34, 0x2F, 0x61, \ + 0x91, 0x72, 0xFE, 0x9C, 0xE9, 0x85, 0x83, 0xFF, \ + 0x8E, 0x4F, 0x12, 0x32, 0xEE, 0xF2, 0x81, 0x83, \ + 0xC3, 0xFE, 0x3B, 0x1B, 0x4C, 0x6F, 0xAD, 0x73, \ + 0x3B, 0xB5, 0xFC, 0xBC, 0x2E, 0xC2, 0x20, 0x05, \ + 0xC5, 0x8E, 0xF1, 0x83, 0x7D, 0x16, 0x83, 0xB2, \ + 0xC6, 0xF3, 0x4A, 0x26, 0xC1, 0xB2, 0xEF, 0xFA, \ + 0x88, 0x6B, 0x42, 0x38, 0x61, 0x1F, 0xCF, 0xDC, \ + 0xDE, 0x35, 0x5B, 0x3B, 0x65, 0x19, 0x03, 0x5B, \ + 0xBC, 0x34, 0xF4, 0xDE, 0xF9, 0x9C, 0x02, 0x38, \ + 0x61, 0xB4, 0x6F, 0xC9, 0xD6, 0xE6, 0xC9, 0x07, \ + 0x7A, 0xD9, 0x1D, 0x26, 0x91, 0xF7, 0xF7, 0xEE, \ + 0x59, 0x8C, 0xB0, 0xFA, 0xC1, 0x86, 0xD9, 0x1C, \ + 0xAE, 0xFE, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, \ + 0xB4, 0x13, 0x0C, 0x93, 0xBC, 0x43, 0x79, 0x44, \ + 0xF4, 0xFD, 0x44, 0x52, 0xE2, 0xD7, 0x4D, 0xD3, \ + 0x64, 0xF2, 0xE2, 0x1E, 0x71, 0xF5, 0x4B, 0xFF, \ + 0x5C, 0xAE, 0x82, 0xAB, 0x9C, 0x9D, 0xF6, 0x9E, \ + 0xE8, 0x6D, 0x2B, 0xC5, 0x22, 0x36, 0x3A, 0x0D, \ + 0xAB, 0xC5, 0x21, 0x97, 0x9B, 0x0D, 0xEA, 0xDA, \ + 0x1D, 0xBF, 0x9A, 0x42, 0xD5, 0xC4, 0x48, 0x4E, \ + 0x0A, 0xBC, 0xD0, 0x6B, 0xFA, 0x53, 0xDD, 0xEF, \ + 0x3C, 0x1B, 0x20, 0xEE, 0x3F, 0xD5, 0x9D, 0x7C, \ + 0x25, 0xE4, 0x1D, 0x2B, 0x66, 0x9E, 0x1E, 0xF1, \ + 0x6E, 0x6F, 0x52, 0xC3, 0x16, 0x4D, 0xF4, 0xFB, \ + 0x79, 0x30, 0xE9, 0xE4, 0xE5, 0x88, 0x57, 0xB6, \ + 0xAC, 0x7D, 0x5F, 0x42, 0xD6, 0x9F, 0x6D, 0x18, \ + 0x77, 0x63, 0xCF, 0x1D, 0x55, 0x03, 0x40, 0x04, \ + 0x87, 0xF5, 0x5B, 0xA5, 0x7E, 0x31, 0xCC, 0x7A, \ + 0x71, 0x35, 0xC8, 0x86, 0xEF, 0xB4, 0x31, 0x8A, \ + 0xED, 0x6A, 0x1E, 0x01, 0x2D, 0x9E, 0x68, 0x32, \ + 0xA9, 0x07, 0x60, 0x0A, 0x91, 0x81, 0x30, 0xC4, \ + 0x6D, 0xC7, 0x78, 0xF9, 0x71, 0xAD, 0x00, 0x38, \ + 0x09, 0x29, 0x99, 0xA3, 0x33, 0xCB, 0x8B, 0x7A, \ + 0x1A, 0x1D, 0xB9, 0x3D, 0x71, 0x40, 0x00, 0x3C, \ + 0x2A, 0x4E, 0xCE, 0xA9, 0xF9, 0x8D, 0x0A, 0xCC, \ + 0x0A, 0x82, 0x91, 0xCD, 0xCE, 0xC9, 0x7D, 0xCF, \ + 0x8E, 0xC9, 0xB5, 0x5A, 0x7F, 0x88, 0xA4, 0x6B, \ + 0x4D, 0xB5, 0xA8, 0x51, 0xF4, 0x41, 0x82, 0xE1, \ + 0xC6, 0x8A, 0x00, 0x7E, 0x5E, 0x0D, 0xD9, 0x02, \ + 0x0B, 0xFD, 0x64, 0xB6, 0x45, 0x03, 0x6C, 0x7A, \ + 0x4E, 0x67, 0x7D, 0x2C, 0x38, 0x53, 0x2A, 0x3A, \ + 0x23, 0xBA, 0x44, 0x42, 0xCA, 0xF5, 0x3E, 0xA6, \ + 0x3B, 0xB4, 0x54, 0x32, 0x9B, 0x76, 0x24, 0xC8, \ + 0x91, 0x7B, 0xDD, 0x64, 0xB1, 0xC0, 0xFD, 0x4C, \ + 0xB3, 0x8E, 0x8C, 0x33, 0x4C, 0x70, 0x1C, 0x3A, \ + 0xCD, 0xAD, 0x06, 0x57, 0xFC, 0xCF, 0xEC, 0x71, \ + 0x9B, 0x1F, 0x5C, 0x3E, 0x4E, 0x46, 0x04, 0x1F, \ + 0x38, 0x81, 0x47, 0xFB, 0x4C, 0xFD, 0xB4, 0x77, \ + 0xA5, 0x24, 0x71, 0xF7, 0xA9, 0xA9, 0x69, 0x10, \ + 0xB8, 0x55, 0x32, 0x2E, 0xDB, 0x63, 0x40, 0xD8, \ + 0xA0, 0x0E, 0xF0, 0x92, 0x35, 0x05, 0x11, 0xE3, \ + 0x0A, 0xBE, 0xC1, 0xFF, 0xF9, 0xE3, 0xA2, 0x6E, \ + 0x7F, 0xB2, 0x9F, 0x8C, 0x18, 0x30, 0x23, 0xC3, \ + 0x58, 0x7E, 0x38, 0xDA, 0x00, 0x77, 0xD9, 0xB4, \ + 0x76, 0x3E, 0x4E, 0x4B, 0x94, 0xB2, 0xBB, 0xC1, \ + 0x94, 0xC6, 0x65, 0x1E, 0x77, 0xCA, 0xF9, 0x92, \ + 0xEE, 0xAA, 0xC0, 0x23, 0x2A, 0x28, 0x1B, 0xF6, \ + 0xB3, 0xA7, 0x39, 0xC1, 0x22, 0x61, 0x16, 0x82, \ + 0x0A, 0xE8, 0xDB, 0x58, 0x47, 0xA6, 0x7C, 0xBE, \ + 0xF9, 0xC9, 0x09, 0x1B, 0x46, 0x2D, 0x53, 0x8C, \ + 0xD7, 0x2B, 0x03, 0x74, 0x6A, 0xE7, 0x7F, 0x5E, \ + 0x62, 0x29, 0x2C, 0x31, 0x15, 0x62, 0xA8, 0x46, \ + 0x50, 0x5D, 0xC8, 0x2D, 0xB8, 0x54, 0x33, 0x8A, \ + 0xE4, 0x9F, 0x52, 0x35, 0xC9, 0x5B, 0x91, 0x17, \ + 0x8C, 0xCF, 0x2D, 0xD5, 0xCA, 0xCE, 0xF4, 0x03, \ + 0xEC, 0x9D, 0x18, 0x10, 0xC6, 0x27, 0x2B, 0x04, \ + 0x5B, 0x3B, 0x71, 0xF9, 0xDC, 0x6B, 0x80, 0xD6, \ + 0x3F, 0xDD, 0x4A, 0x8E, 0x9A, 0xDB, 0x1E, 0x69, \ + 0x62, 0xA6, 0x95, 0x26, 0xD4, 0x31, 0x61, 0xC1, \ + 0xA4, 0x1D, 0x57, 0x0D, 0x79, 0x38, 0xDA, 0xD4, \ + 0xA4, 0x0E, 0x32, 0x9C, 0xCF, 0xF4, 0x6A, 0xAA, \ + 0x36, 0xAD, 0x00, 0x4C, 0xF6, 0x00, 0xC8, 0x38, \ + 0x1E, 0x42, 0x5A, 0x31, 0xD9, 0x51, 0xAE, 0x64, \ + 0xFD, 0xB2, 0x3F, 0xCE, 0xC9, 0x50, 0x9D, 0x43, \ + 0x68, 0x7F, 0xEB, 0x69, 0xED, 0xD1, 0xCC, 0x5E, \ + 0x0B, 0x8C, 0xC3, 0xBD, 0xF6, 0x4B, 0x10, 0xEF, \ + 0x86, 0xB6, 0x31, 0x42, 0xA3, 0xAB, 0x88, 0x29, \ + 0x55, 0x5B, 0x2F, 0x74, 0x7C, 0x93, 0x26, 0x65, \ + 0xCB, 0x2C, 0x0F, 0x1C, 0xC0, 0x1B, 0xD7, 0x02, \ + 0x29, 0x38, 0x88, 0x39, 0xD2, 0xAF, 0x05, 0xE4, \ + 0x54, 0x50, 0x4A, 0xC7, 0x8B, 0x75, 0x82, 0x82, \ + 0x28, 0x46, 0xC0, 0xBA, 0x35, 0xC3, 0x5F, 0x5C, \ + 0x59, 0x16, 0x0C, 0xC0, 0x46, 0xFD, 0x82, 0x51, \ + 0x54, 0x1F, 0xC6, 0x8C, 0x9C, 0x86, 0xB0, 0x22, \ + 0xBB, 0x70, 0x99, 0x87, 0x6A, 0x46, 0x0E, 0x74, \ + 0x51, 0xA8, 0xA9, 0x31, 0x09, 0x70, 0x3F, 0xEE, \ + 0x1C, 0x21, 0x7E, 0x6C, 0x38, 0x26, 0xE5, 0x2C, \ + 0x51, 0xAA, 0x69, 0x1E, 0x0E, 0x42, 0x3C, 0xFC, \ + 0x99, 0xE9, 0xE3, 0x16, 0x50, 0xC1, 0x21, 0x7B, \ + 0x62, 0x48, 0x16, 0xCD, 0xAD, 0x9A, 0x95, 0xF9, \ + 0xD5, 0xB8, 0x01, 0x94, 0x88, 0xD9, 0xC0, 0xA0, \ + 0xA1, 0xFE, 0x30, 0x75, 0xA5, 0x77, 0xE2, 0x31, \ + 0x83, 0xF8, 0x1D, 0x4A, 0x3F, 0x2F, 0xA4, 0x57, \ + 0x1E, 0xFC, 0x8C, 0xE0, 0xBA, 0x8A, 0x4F, 0xE8, \ + 0xB6, 0x85, 0x5D, 0xFE, 0x72, 0xB0, 0xA6, 0x6E, \ + 0xDE, 0xD2, 0xFB, 0xAB, 0xFB, 0xE5, 0x8A, 0x30, \ + 0xFA, 0xFA, 0xBE, 0x1C, 0x5D, 0x71, 0xA8, 0x7E, \ + 0x2F, 0x74, 0x1E, 0xF8, 0xC1, 0xFE, 0x86, 0xFE, \ + 0xA6, 0xBB, 0xFD, 0xE5, 0x30, 0x67, 0x7F, 0x0D, \ + 0x97, 0xD1, 0x1D, 0x49, 0xF7, 0xA8, 0x44, 0x3D, \ + 0x08, 0x22, 0xE5, 0x06, 0xA9, 0xF4, 0x61, 0x4E, \ + 0x01, 0x1E, 0x2A, 0x94, 0x83, 0x8F, 0xF8, 0x8C, \ + 0xD6, 0x8C, 0x8B, 0xB7, 0xC5, 0xC6, 0x42, 0x4C, \ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } + +#define MBEDTLS_DHM_RFC7919_FFDHE8192_G_BIN { 0x02 } + +#endif /* dhm.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ecdh.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ecdh.h new file mode 100644 index 00000000..67c94f0f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ecdh.h @@ -0,0 +1,453 @@ +/** + * \file ecdh.h + * + * \brief This file contains ECDH definitions and functions. + * + * The Elliptic Curve Diffie-Hellman (ECDH) protocol is an anonymous + * key agreement protocol allowing two parties to establish a shared + * secret over an insecure channel. Each party must have an + * elliptic-curve public–private key pair. + * + * For more information, see NIST SP 800-56A Rev. 2: Recommendation for + * Pair-Wise Key Establishment Schemes Using Discrete Logarithm + * Cryptography. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_ECDH_H +#define MBEDTLS_ECDH_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/ecp.h" + +/* + * Mbed TLS supports two formats for ECDH contexts (#mbedtls_ecdh_context + * defined in `ecdh.h`). For most applications, the choice of format makes + * no difference, since all library functions can work with either format, + * except that the new format is incompatible with MBEDTLS_ECP_RESTARTABLE. + + * The new format used when this option is disabled is smaller + * (56 bytes on a 32-bit platform). In future versions of the library, it + * will support alternative implementations of ECDH operations. + * The new format is incompatible with applications that access + * context fields directly and with restartable ECP operations. + */ + +#if defined(MBEDTLS_ECP_RESTARTABLE) +#define MBEDTLS_ECDH_LEGACY_CONTEXT +#else +#undef MBEDTLS_ECDH_LEGACY_CONTEXT +#endif + +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) +#undef MBEDTLS_ECDH_LEGACY_CONTEXT +#include "everest/everest.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Defines the source of the imported EC key. + */ +typedef enum { + MBEDTLS_ECDH_OURS, /**< Our key. */ + MBEDTLS_ECDH_THEIRS, /**< The key of the peer. */ +} mbedtls_ecdh_side; + +#if !defined(MBEDTLS_ECDH_LEGACY_CONTEXT) +/** + * Defines the ECDH implementation used. + * + * Later versions of the library may add new variants, therefore users should + * not make any assumptions about them. + */ +typedef enum { + MBEDTLS_ECDH_VARIANT_NONE = 0, /*!< Implementation not defined. */ + MBEDTLS_ECDH_VARIANT_MBEDTLS_2_0,/*!< The default Mbed TLS implementation */ +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + MBEDTLS_ECDH_VARIANT_EVEREST /*!< Everest implementation */ +#endif +} mbedtls_ecdh_variant; + +/** + * The context used by the default ECDH implementation. + * + * Later versions might change the structure of this context, therefore users + * should not make any assumptions about the structure of + * mbedtls_ecdh_context_mbed. + */ +typedef struct mbedtls_ecdh_context_mbed { + mbedtls_ecp_group MBEDTLS_PRIVATE(grp); /*!< The elliptic curve used. */ + mbedtls_mpi MBEDTLS_PRIVATE(d); /*!< The private key. */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Q); /*!< The public key. */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Qp); /*!< The value of the public key of the peer. */ + mbedtls_mpi MBEDTLS_PRIVATE(z); /*!< The shared secret. */ +#if defined(MBEDTLS_ECP_RESTARTABLE) + mbedtls_ecp_restart_ctx MBEDTLS_PRIVATE(rs); /*!< The restart context for EC computations. */ +#endif +} mbedtls_ecdh_context_mbed; +#endif + +/** + * + * \warning Performing multiple operations concurrently on the same + * ECDSA context is not supported; objects of this type + * should not be shared between multiple threads. + * \brief The ECDH context structure. + */ +typedef struct mbedtls_ecdh_context { +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + mbedtls_ecp_group MBEDTLS_PRIVATE(grp); /*!< The elliptic curve used. */ + mbedtls_mpi MBEDTLS_PRIVATE(d); /*!< The private key. */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Q); /*!< The public key. */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Qp); /*!< The value of the public key of the peer. */ + mbedtls_mpi MBEDTLS_PRIVATE(z); /*!< The shared secret. */ + int MBEDTLS_PRIVATE(point_format); /*!< The format of point export in TLS messages. */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Vi); /*!< The blinding value. */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Vf); /*!< The unblinding value. */ + mbedtls_mpi MBEDTLS_PRIVATE(_d); /*!< The previous \p d. */ +#if defined(MBEDTLS_ECP_RESTARTABLE) + int MBEDTLS_PRIVATE(restart_enabled); /*!< The flag for restartable mode. */ + mbedtls_ecp_restart_ctx MBEDTLS_PRIVATE(rs); /*!< The restart context for EC computations. */ +#endif /* MBEDTLS_ECP_RESTARTABLE */ +#else + uint8_t MBEDTLS_PRIVATE(point_format); /*!< The format of point export in TLS messages + as defined in RFC 4492. */ + mbedtls_ecp_group_id MBEDTLS_PRIVATE(grp_id);/*!< The elliptic curve used. */ + mbedtls_ecdh_variant MBEDTLS_PRIVATE(var); /*!< The ECDH implementation/structure used. */ + union { + mbedtls_ecdh_context_mbed MBEDTLS_PRIVATE(mbed_ecdh); +#if defined(MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED) + mbedtls_ecdh_context_everest MBEDTLS_PRIVATE(everest_ecdh); +#endif + } MBEDTLS_PRIVATE(ctx); /*!< Implementation-specific context. The + context in use is specified by the \c var + field. */ +#if defined(MBEDTLS_ECP_RESTARTABLE) + uint8_t MBEDTLS_PRIVATE(restart_enabled); /*!< The flag for restartable mode. Functions of + an alternative implementation not supporting + restartable mode must return + MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED error + if this flag is set. */ +#endif /* MBEDTLS_ECP_RESTARTABLE */ +#endif /* MBEDTLS_ECDH_LEGACY_CONTEXT */ +} +mbedtls_ecdh_context; + +/** + * \brief Check whether a given group can be used for ECDH. + * + * \param gid The ECP group ID to check. + * + * \return \c 1 if the group can be used, \c 0 otherwise + */ +int mbedtls_ecdh_can_do(mbedtls_ecp_group_id gid); + +/** + * \brief This function generates an ECDH keypair on an elliptic + * curve. + * + * This function performs the first of two core computations + * implemented during the ECDH key exchange. The second core + * computation is performed by mbedtls_ecdh_compute_shared(). + * + * \see ecp.h + * + * \param grp The ECP group to use. This must be initialized and have + * domain parameters loaded, for example through + * mbedtls_ecp_load() or mbedtls_ecp_tls_read_group(). + * \param d The destination MPI (private key). + * This must be initialized. + * \param Q The destination point (public key). + * This must be initialized. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL in case \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return Another \c MBEDTLS_ERR_ECP_XXX or + * \c MBEDTLS_MPI_XXX error code on failure. + */ +int mbedtls_ecdh_gen_public(mbedtls_ecp_group *grp, mbedtls_mpi *d, mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function computes the shared secret. + * + * This function performs the second of two core computations + * implemented during the ECDH key exchange. The first core + * computation is performed by mbedtls_ecdh_gen_public(). + * + * \see ecp.h + * + * \note If \p f_rng is not NULL, it is used to implement + * countermeasures against side-channel attacks. + * For more information, see mbedtls_ecp_mul(). + * + * \param grp The ECP group to use. This must be initialized and have + * domain parameters loaded, for example through + * mbedtls_ecp_load() or mbedtls_ecp_tls_read_group(). + * \param z The destination MPI (shared secret). + * This must be initialized. + * \param Q The public key from another party. + * This must be initialized. + * \param d Our secret exponent (private key). + * This must be initialized. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng is \c NULL or doesn't need a + * context argument. + * + * \return \c 0 on success. + * \return Another \c MBEDTLS_ERR_ECP_XXX or + * \c MBEDTLS_MPI_XXX error code on failure. + */ +int mbedtls_ecdh_compute_shared(mbedtls_ecp_group *grp, mbedtls_mpi *z, + const mbedtls_ecp_point *Q, const mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function initializes an ECDH context. + * + * \param ctx The ECDH context to initialize. This must not be \c NULL. + */ +void mbedtls_ecdh_init(mbedtls_ecdh_context *ctx); + +/** + * \brief This function sets up the ECDH context with the information + * given. + * + * This function should be called after mbedtls_ecdh_init() but + * before mbedtls_ecdh_make_params(). There is no need to call + * this function before mbedtls_ecdh_read_params(). + * + * This is the first function used by a TLS server for ECDHE + * ciphersuites. + * + * \param ctx The ECDH context to set up. This must be initialized. + * \param grp_id The group id of the group to set up the context for. + * + * \return \c 0 on success. + */ +int mbedtls_ecdh_setup(mbedtls_ecdh_context *ctx, + mbedtls_ecp_group_id grp_id); + +/** + * \brief This function frees a context. + * + * \param ctx The context to free. This may be \c NULL, in which + * case this function does nothing. If it is not \c NULL, + * it must point to an initialized ECDH context. + */ +void mbedtls_ecdh_free(mbedtls_ecdh_context *ctx); + +/** + * \brief This function generates an EC key pair and exports its + * in the format used in a TLS ServerKeyExchange handshake + * message. + * + * This is the second function used by a TLS server for ECDHE + * ciphersuites. (It is called after mbedtls_ecdh_setup().) + * + * \see ecp.h + * + * \param ctx The ECDH context to use. This must be initialized + * and bound to a group, for example via mbedtls_ecdh_setup(). + * \param olen The address at which to store the number of Bytes written. + * \param buf The destination buffer. This must be a writable buffer of + * length \p blen Bytes. + * \param blen The length of the destination buffer \p buf in Bytes. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL in case \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_ecdh_make_params(mbedtls_ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function parses the ECDHE parameters in a + * TLS ServerKeyExchange handshake message. + * + * \note In a TLS handshake, this is the how the client + * sets up its ECDHE context from the server's public + * ECDHE key material. + * + * \see ecp.h + * + * \param ctx The ECDHE context to use. This must be initialized. + * \param buf On input, \c *buf must be the start of the input buffer. + * On output, \c *buf is updated to point to the end of the + * data that has been read. On success, this is the first byte + * past the end of the ServerKeyExchange parameters. + * On error, this is the point at which an error has been + * detected, which is usually not useful except to debug + * failures. + * \param end The end of the input buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + * + */ +int mbedtls_ecdh_read_params(mbedtls_ecdh_context *ctx, + const unsigned char **buf, + const unsigned char *end); + +/** + * \brief This function sets up an ECDH context from an EC key. + * + * It is used by clients and servers in place of the + * ServerKeyEchange for static ECDH, and imports ECDH + * parameters from the EC key information of a certificate. + * + * \see ecp.h + * + * \param ctx The ECDH context to set up. This must be initialized. + * \param key The EC key to use. This must be initialized. + * \param side Defines the source of the key. Possible values are: + * - #MBEDTLS_ECDH_OURS: The key is ours. + * - #MBEDTLS_ECDH_THEIRS: The key is that of the peer. + * + * \return \c 0 on success. + * \return Another \c MBEDTLS_ERR_ECP_XXX error code on failure. + * + */ +int mbedtls_ecdh_get_params(mbedtls_ecdh_context *ctx, + const mbedtls_ecp_keypair *key, + mbedtls_ecdh_side side); + +/** + * \brief This function generates a public key and exports it + * as a TLS ClientKeyExchange payload. + * + * This is the second function used by a TLS client for ECDH(E) + * ciphersuites. + * + * \see ecp.h + * + * \param ctx The ECDH context to use. This must be initialized + * and bound to a group, the latter usually by + * mbedtls_ecdh_read_params(). + * \param olen The address at which to store the number of Bytes written. + * This must not be \c NULL. + * \param buf The destination buffer. This must be a writable buffer + * of length \p blen Bytes. + * \param blen The size of the destination buffer \p buf in Bytes. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL in case \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_ecdh_make_public(mbedtls_ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function parses and processes the ECDHE payload of a + * TLS ClientKeyExchange message. + * + * This is the third function used by a TLS server for ECDH(E) + * ciphersuites. (It is called after mbedtls_ecdh_setup() and + * mbedtls_ecdh_make_params().) + * + * \see ecp.h + * + * \param ctx The ECDH context to use. This must be initialized + * and bound to a group, for example via mbedtls_ecdh_setup(). + * \param buf The pointer to the ClientKeyExchange payload. This must + * be a readable buffer of length \p blen Bytes. + * \param blen The length of the input buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_ecdh_read_public(mbedtls_ecdh_context *ctx, + const unsigned char *buf, size_t blen); + +/** + * \brief This function derives and exports the shared secret. + * + * This is the last function used by both TLS client + * and servers. + * + * \note If \p f_rng is not NULL, it is used to implement + * countermeasures against side-channel attacks. + * For more information, see mbedtls_ecp_mul(). + * + * \see ecp.h + + * \param ctx The ECDH context to use. This must be initialized + * and have its own private key generated and the peer's + * public key imported. + * \param olen The address at which to store the total number of + * Bytes written on success. This must not be \c NULL. + * \param buf The buffer to write the generated shared key to. This + * must be a writable buffer of size \p blen Bytes. + * \param blen The length of the destination buffer \p buf in Bytes. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context. This may be \c NULL if \p f_rng + * doesn't need a context argument. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX error code on failure. + */ +int mbedtls_ecdh_calc_secret(mbedtls_ecdh_context *ctx, size_t *olen, + unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief This function enables restartable EC computations for this + * context. (Default: disabled.) + * + * \see \c mbedtls_ecp_set_max_ops() + * + * \note It is not possible to safely disable restartable + * computations once enabled, except by free-ing the context, + * which cancels possible in-progress operations. + * + * \param ctx The ECDH context to use. This must be initialized. + */ +void mbedtls_ecdh_enable_restart(mbedtls_ecdh_context *ctx); +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +#ifdef __cplusplus +} +#endif + +#endif /* ecdh.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ecdsa.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ecdsa.h new file mode 100644 index 00000000..c5d9701f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ecdsa.h @@ -0,0 +1,681 @@ +/** + * \file ecdsa.h + * + * \brief This file contains ECDSA definitions and functions. + * + * The Elliptic Curve Digital Signature Algorithm (ECDSA) is defined in + * Standards for Efficient Cryptography Group (SECG): + * SEC1 Elliptic Curve Cryptography. + * The use of ECDSA for TLS is defined in RFC-4492: Elliptic Curve + * Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS). + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_ECDSA_H +#define MBEDTLS_ECDSA_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/md.h" + +/** + * \brief Maximum ECDSA signature size for a given curve bit size + * + * \param bits Curve size in bits + * \return Maximum signature size in bytes + * + * \note This macro returns a compile-time constant if its argument + * is one. It may evaluate its argument multiple times. + */ +/* + * Ecdsa-Sig-Value ::= SEQUENCE { + * r INTEGER, + * s INTEGER + * } + * + * For each of r and s, the value (V) may include an extra initial "0" bit. + */ +#define MBEDTLS_ECDSA_MAX_SIG_LEN(bits) \ + (/*T,L of SEQUENCE*/ ((bits) >= 61 * 8 ? 3 : 2) + \ + /*T,L of r,s*/ 2 * (((bits) >= 127 * 8 ? 3 : 2) + \ + /*V of r,s*/ ((bits) + 8) / 8)) + +/** The maximal size of an ECDSA signature in Bytes. */ +#define MBEDTLS_ECDSA_MAX_LEN MBEDTLS_ECDSA_MAX_SIG_LEN(MBEDTLS_ECP_MAX_BITS) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief The ECDSA context structure. + * + * \warning Performing multiple operations concurrently on the same + * ECDSA context is not supported; objects of this type + * should not be shared between multiple threads. + * + * \note pk_wrap module assumes that "ecdsa_context" is identical + * to "ecp_keypair" (see for example structure + * "mbedtls_eckey_info" where ECDSA sign/verify functions + * are used also for EC key) + */ +typedef mbedtls_ecp_keypair mbedtls_ecdsa_context; + +#if defined(MBEDTLS_ECP_RESTARTABLE) + +/** + * \brief Internal restart context for ecdsa_verify() + * + * \note Opaque struct, defined in ecdsa.c + */ +typedef struct mbedtls_ecdsa_restart_ver mbedtls_ecdsa_restart_ver_ctx; + +/** + * \brief Internal restart context for ecdsa_sign() + * + * \note Opaque struct, defined in ecdsa.c + */ +typedef struct mbedtls_ecdsa_restart_sig mbedtls_ecdsa_restart_sig_ctx; + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +/** + * \brief Internal restart context for ecdsa_sign_det() + * + * \note Opaque struct, defined in ecdsa.c + */ +typedef struct mbedtls_ecdsa_restart_det mbedtls_ecdsa_restart_det_ctx; +#endif + +/** + * \brief General context for resuming ECDSA operations + */ +typedef struct { + mbedtls_ecp_restart_ctx MBEDTLS_PRIVATE(ecp); /*!< base context for ECP restart and + shared administrative info */ + mbedtls_ecdsa_restart_ver_ctx *MBEDTLS_PRIVATE(ver); /*!< ecdsa_verify() sub-context */ + mbedtls_ecdsa_restart_sig_ctx *MBEDTLS_PRIVATE(sig); /*!< ecdsa_sign() sub-context */ +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + mbedtls_ecdsa_restart_det_ctx *MBEDTLS_PRIVATE(det); /*!< ecdsa_sign_det() sub-context */ +#endif +} mbedtls_ecdsa_restart_ctx; + +#else /* MBEDTLS_ECP_RESTARTABLE */ + +/* Now we can declare functions that take a pointer to that */ +typedef void mbedtls_ecdsa_restart_ctx; + +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief This function checks whether a given group can be used + * for ECDSA. + * + * \param gid The ECP group ID to check. + * + * \return \c 1 if the group can be used, \c 0 otherwise + */ +int mbedtls_ecdsa_can_do(mbedtls_ecp_group_id gid); + +/** + * \brief This function computes the ECDSA signature of a + * previously-hashed message. + * + * \note The deterministic version implemented in + * mbedtls_ecdsa_sign_det_ext() is usually preferred. + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated + * as defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.3, step 5. + * + * \see ecp.h + * + * \param grp The context for the elliptic curve to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param r The MPI context in which to store the first part + * the signature. This must be initialized. + * \param s The MPI context in which to store the second part + * the signature. This must be initialized. + * \param d The private signing key. This must be initialized. + * \param buf The content to be signed. This is usually the hash of + * the original data to be signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX + * or \c MBEDTLS_MPI_XXX error code on failure. + */ +int mbedtls_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s, + const mbedtls_mpi *d, const unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) +/** + * \brief This function computes the ECDSA signature of a + * previously-hashed message, deterministic version. + * + * For more information, see RFC-6979: Deterministic + * Usage of the Digital Signature Algorithm (DSA) and Elliptic + * Curve Digital Signature Algorithm (ECDSA). + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.3, step 5. + * + * \see ecp.h + * + * \param grp The context for the elliptic curve to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param r The MPI context in which to store the first part + * the signature. This must be initialized. + * \param s The MPI context in which to store the second part + * the signature. This must be initialized. + * \param d The private signing key. This must be initialized + * and setup, for example through mbedtls_ecp_gen_privkey(). + * \param buf The hashed content to be signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param md_alg The hash algorithm used to hash the original data. + * \param f_rng_blind The RNG function used for blinding. This must not be + * \c NULL. + * \param p_rng_blind The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX + * error code on failure. + */ +int mbedtls_ecdsa_sign_det_ext(mbedtls_ecp_group *grp, mbedtls_mpi *r, + mbedtls_mpi *s, const mbedtls_mpi *d, + const unsigned char *buf, size_t blen, + mbedtls_md_type_t md_alg, + int (*f_rng_blind)(void *, unsigned char *, size_t), + void *p_rng_blind); +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ + +#if !defined(MBEDTLS_ECDSA_SIGN_ALT) +/** + * \brief This function computes the ECDSA signature of a + * previously-hashed message, in a restartable way. + * + * \note The deterministic version implemented in + * mbedtls_ecdsa_sign_det_restartable() is usually + * preferred. + * + * \note This function is like \c mbedtls_ecdsa_sign() but + * it can return early and restart according to the + * limit set with \c mbedtls_ecp_set_max_ops() to + * reduce blocking. + * + * \note If the bitlength of the message hash is larger + * than the bitlength of the group order, then the + * hash is truncated as defined in Standards for + * Efficient Cryptography Group (SECG): SEC1 Elliptic + * Curve Cryptography, section 4.1.3, step 5. + * + * \see ecp.h + * + * \param grp The context for the elliptic curve to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param r The MPI context in which to store the first part + * the signature. This must be initialized. + * \param s The MPI context in which to store the second part + * the signature. This must be initialized. + * \param d The private signing key. This must be initialized + * and setup, for example through + * mbedtls_ecp_gen_privkey(). + * \param buf The hashed content to be signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * \param f_rng_blind The RNG function used for blinding. This must not be + * \c NULL. + * \param p_rng_blind The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * \param rs_ctx The restart context to use. This may be \c NULL + * to disable restarting. If it is not \c NULL, it + * must point to an initialized restart context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c + * mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX, \c + * MBEDTLS_ERR_MPI_XXX or \c MBEDTLS_ERR_ASN1_XXX + * error code on failure. + */ +int mbedtls_ecdsa_sign_restartable( + mbedtls_ecp_group *grp, + mbedtls_mpi *r, mbedtls_mpi *s, + const mbedtls_mpi *d, + const unsigned char *buf, size_t blen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + int (*f_rng_blind)(void *, unsigned char *, size_t), + void *p_rng_blind, + mbedtls_ecdsa_restart_ctx *rs_ctx); + +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + +/** + * \brief This function computes the ECDSA signature of a + * previously-hashed message, in a restartable way. + * + * \note This function is like \c + * mbedtls_ecdsa_sign_det_ext() but it can return + * early and restart according to the limit set with + * \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \note If the bitlength of the message hash is larger + * than the bitlength of the group order, then the + * hash is truncated as defined in Standards for + * Efficient Cryptography Group (SECG): SEC1 Elliptic + * Curve Cryptography, section 4.1.3, step 5. + * + * \see ecp.h + * + * \param grp The context for the elliptic curve to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param r The MPI context in which to store the first part + * the signature. This must be initialized. + * \param s The MPI context in which to store the second part + * the signature. This must be initialized. + * \param d The private signing key. This must be initialized + * and setup, for example through + * mbedtls_ecp_gen_privkey(). + * \param buf The hashed content to be signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param f_rng_blind The RNG function used for blinding. This must not be + * \c NULL. + * \param p_rng_blind The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context parameter. + * \param rs_ctx The restart context to use. This may be \c NULL + * to disable restarting. If it is not \c NULL, it + * must point to an initialized restart context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c + * mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX, \c + * MBEDTLS_ERR_MPI_XXX or \c MBEDTLS_ERR_ASN1_XXX + * error code on failure. + */ +int mbedtls_ecdsa_sign_det_restartable( + mbedtls_ecp_group *grp, + mbedtls_mpi *r, mbedtls_mpi *s, + const mbedtls_mpi *d, const unsigned char *buf, size_t blen, + mbedtls_md_type_t md_alg, + int (*f_rng_blind)(void *, unsigned char *, size_t), + void *p_rng_blind, + mbedtls_ecdsa_restart_ctx *rs_ctx); + +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ + +#endif /* !MBEDTLS_ECDSA_SIGN_ALT */ + +/** + * \brief This function verifies the ECDSA signature of a + * previously-hashed message. + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.4, step 3. + * + * \see ecp.h + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param buf The hashed content that was signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param Q The public key to use for verification. This must be + * initialized and setup. + * \param r The first integer of the signature. + * This must be initialized. + * \param s The second integer of the signature. + * This must be initialized. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX + * error code on failure. + */ +int mbedtls_ecdsa_verify(mbedtls_ecp_group *grp, + const unsigned char *buf, size_t blen, + const mbedtls_ecp_point *Q, const mbedtls_mpi *r, + const mbedtls_mpi *s); + +#if !defined(MBEDTLS_ECDSA_VERIFY_ALT) +/** + * \brief This function verifies the ECDSA signature of a + * previously-hashed message, in a restartable manner + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.4, step 3. + * + * \see ecp.h + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param buf The hashed content that was signed. This must be a readable + * buffer of length \p blen Bytes. It may be \c NULL if + * \p blen is zero. + * \param blen The length of \p buf in Bytes. + * \param Q The public key to use for verification. This must be + * initialized and setup. + * \param r The first integer of the signature. + * This must be initialized. + * \param s The second integer of the signature. + * This must be initialized. + * \param rs_ctx The restart context to use. This may be \c NULL to disable + * restarting. If it is not \c NULL, it must point to an + * initialized restart context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX + * error code on failure. + */ +int mbedtls_ecdsa_verify_restartable(mbedtls_ecp_group *grp, + const unsigned char *buf, size_t blen, + const mbedtls_ecp_point *Q, + const mbedtls_mpi *r, + const mbedtls_mpi *s, + mbedtls_ecdsa_restart_ctx *rs_ctx); + +#endif /* !MBEDTLS_ECDSA_VERIFY_ALT */ + +/** + * \brief This function computes the ECDSA signature and writes it + * to a buffer, serialized as defined in RFC-4492: + * Elliptic Curve Cryptography (ECC) Cipher Suites for + * Transport Layer Security (TLS). + * + * \warning It is not thread-safe to use the same context in + * multiple threads. + * + * \note The deterministic version is used if + * #MBEDTLS_ECDSA_DETERMINISTIC is defined. For more + * information, see RFC-6979: Deterministic Usage + * of the Digital Signature Algorithm (DSA) and Elliptic + * Curve Digital Signature Algorithm (ECDSA). + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.3, step 5. + * + * \see ecp.h + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and private key bound to it, for example + * via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair(). + * \param md_alg The message digest that was used to hash the message. + * \param hash The message hash to be signed. This must be a readable + * buffer of length \p blen Bytes. + * \param hlen The length of the hash \p hash in Bytes. + * \param sig The buffer to which to write the signature. This must be a + * writable buffer of length at least twice as large as the + * size of the curve used, plus 9. For example, 73 Bytes if + * a 256-bit curve is used. A buffer length of + * #MBEDTLS_ECDSA_MAX_LEN is always safe. + * \param sig_size The size of the \p sig buffer in bytes. + * \param slen The address at which to store the actual length of + * the signature written. Must not be \c NULL. + * \param f_rng The RNG function. This must not be \c NULL if + * #MBEDTLS_ECDSA_DETERMINISTIC is unset. Otherwise, + * it is used only for blinding and may be set to \c NULL, but + * doing so is DEPRECATED. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng is \c NULL or doesn't use a context. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or + * \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_ecdsa_write_signature(mbedtls_ecdsa_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function computes the ECDSA signature and writes it + * to a buffer, in a restartable way. + * + * \see \c mbedtls_ecdsa_write_signature() + * + * \note This function is like \c mbedtls_ecdsa_write_signature() + * but it can return early and restart according to the limit + * set with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and private key bound to it, for example + * via mbedtls_ecdsa_genkey() or mbedtls_ecdsa_from_keypair(). + * \param md_alg The message digest that was used to hash the message. + * \param hash The message hash to be signed. This must be a readable + * buffer of length \p blen Bytes. + * \param hlen The length of the hash \p hash in Bytes. + * \param sig The buffer to which to write the signature. This must be a + * writable buffer of length at least twice as large as the + * size of the curve used, plus 9. For example, 73 Bytes if + * a 256-bit curve is used. A buffer length of + * #MBEDTLS_ECDSA_MAX_LEN is always safe. + * \param sig_size The size of the \p sig buffer in bytes. + * \param slen The address at which to store the actual length of + * the signature written. Must not be \c NULL. + * \param f_rng The RNG function. This must not be \c NULL if + * #MBEDTLS_ECDSA_DETERMINISTIC is unset. Otherwise, + * it is unused and may be set to \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng is \c NULL or doesn't use a context. + * \param rs_ctx The restart context to use. This may be \c NULL to disable + * restarting. If it is not \c NULL, it must point to an + * initialized restart context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX, \c MBEDTLS_ERR_MPI_XXX or + * \c MBEDTLS_ERR_ASN1_XXX error code on failure. + */ +int mbedtls_ecdsa_write_signature_restartable(mbedtls_ecdsa_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hlen, + unsigned char *sig, size_t sig_size, size_t *slen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_ecdsa_restart_ctx *rs_ctx); + +/** + * \brief This function reads and verifies an ECDSA signature. + * + * \note If the bitlength of the message hash is larger than the + * bitlength of the group order, then the hash is truncated as + * defined in Standards for Efficient Cryptography Group + * (SECG): SEC1 Elliptic Curve Cryptography, section + * 4.1.4, step 3. + * + * \see ecp.h + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and public key bound to it. + * \param hash The message hash that was signed. This must be a readable + * buffer of length \p size Bytes. + * \param hlen The size of the hash \p hash. + * \param sig The signature to read and verify. This must be a readable + * buffer of length \p slen Bytes. + * \param slen The size of \p sig in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if signature is invalid. + * \return #MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH if there is a valid + * signature in \p sig, but its length is less than \p siglen. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX + * error code on failure for any other reason. + */ +int mbedtls_ecdsa_read_signature(mbedtls_ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen); + +/** + * \brief This function reads and verifies an ECDSA signature, + * in a restartable way. + * + * \see \c mbedtls_ecdsa_read_signature() + * + * \note This function is like \c mbedtls_ecdsa_read_signature() + * but it can return early and restart according to the limit + * set with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param ctx The ECDSA context to use. This must be initialized + * and have a group and public key bound to it. + * \param hash The message hash that was signed. This must be a readable + * buffer of length \p size Bytes. + * \param hlen The size of the hash \p hash. + * \param sig The signature to read and verify. This must be a readable + * buffer of length \p slen Bytes. + * \param slen The size of \p sig in Bytes. + * \param rs_ctx The restart context to use. This may be \c NULL to disable + * restarting. If it is not \c NULL, it must point to an + * initialized restart context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if signature is invalid. + * \return #MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH if there is a valid + * signature in \p sig, but its length is less than \p siglen. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_ERR_MPI_XXX + * error code on failure for any other reason. + */ +int mbedtls_ecdsa_read_signature_restartable(mbedtls_ecdsa_context *ctx, + const unsigned char *hash, size_t hlen, + const unsigned char *sig, size_t slen, + mbedtls_ecdsa_restart_ctx *rs_ctx); + +/** + * \brief This function generates an ECDSA keypair on the given curve. + * + * \see ecp.h + * + * \param ctx The ECDSA context to store the keypair in. + * This must be initialized. + * \param gid The elliptic curve to use. One of the various + * \c MBEDTLS_ECP_DP_XXX macros depending on configuration. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX code on failure. + */ +int mbedtls_ecdsa_genkey(mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +/** + * \brief This function sets up an ECDSA context from an EC key pair. + * + * \see ecp.h + * + * \param ctx The ECDSA context to setup. This must be initialized. + * \param key The EC key to use. This must be initialized and hold + * a private-public key pair or a public key. In the former + * case, the ECDSA context may be used for signature creation + * and verification after this call. In the latter case, it + * may be used for signature verification. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX code on failure. + */ +int mbedtls_ecdsa_from_keypair(mbedtls_ecdsa_context *ctx, + const mbedtls_ecp_keypair *key); + +/** + * \brief This function initializes an ECDSA context. + * + * \param ctx The ECDSA context to initialize. + * This must not be \c NULL. + */ +void mbedtls_ecdsa_init(mbedtls_ecdsa_context *ctx); + +/** + * \brief This function frees an ECDSA context. + * + * \param ctx The ECDSA context to free. This may be \c NULL, + * in which case this function does nothing. If it + * is not \c NULL, it must be initialized. + */ +void mbedtls_ecdsa_free(mbedtls_ecdsa_context *ctx); + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Initialize a restart context. + * + * \param ctx The restart context to initialize. + * This must not be \c NULL. + */ +void mbedtls_ecdsa_restart_init(mbedtls_ecdsa_restart_ctx *ctx); + +/** + * \brief Free the components of a restart context. + * + * \param ctx The restart context to free. This may be \c NULL, + * in which case this function does nothing. If it + * is not \c NULL, it must be initialized. + */ +void mbedtls_ecdsa_restart_free(mbedtls_ecdsa_restart_ctx *ctx); +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +#ifdef __cplusplus +} +#endif + +#endif /* ecdsa.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ecjpake.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ecjpake.h new file mode 100644 index 00000000..a63bb32f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ecjpake.h @@ -0,0 +1,309 @@ +/** + * \file ecjpake.h + * + * \brief Elliptic curve J-PAKE + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ECJPAKE_H +#define MBEDTLS_ECJPAKE_H +#include "mbedtls/private_access.h" + +/* + * J-PAKE is a password-authenticated key exchange that allows deriving a + * strong shared secret from a (potentially low entropy) pre-shared + * passphrase, with forward secrecy and mutual authentication. + * https://en.wikipedia.org/wiki/Password_Authenticated_Key_Exchange_by_Juggling + * + * This file implements the Elliptic Curve variant of J-PAKE, + * as defined in Chapter 7.4 of the Thread v1.0 Specification, + * available to members of the Thread Group http://threadgroup.org/ + * + * As the J-PAKE algorithm is inherently symmetric, so is our API. + * Each party needs to send its first round message, in any order, to the + * other party, then each sends its second round message, in any order. + * The payloads are serialized in a way suitable for use in TLS, but could + * also be use outside TLS. + */ +#include "mbedtls/build_info.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Roles in the EC J-PAKE exchange + */ +typedef enum { + MBEDTLS_ECJPAKE_CLIENT = 0, /**< Client */ + MBEDTLS_ECJPAKE_SERVER, /**< Server */ +} mbedtls_ecjpake_role; + +#if !defined(MBEDTLS_ECJPAKE_ALT) +/** + * EC J-PAKE context structure. + * + * J-PAKE is a symmetric protocol, except for the identifiers used in + * Zero-Knowledge Proofs, and the serialization of the second message + * (KeyExchange) as defined by the Thread spec. + * + * In order to benefit from this symmetry, we choose a different naming + * convention from the Thread v1.0 spec. Correspondence is indicated in the + * description as a pair C: client name, S: server name + */ +typedef struct mbedtls_ecjpake_context { + mbedtls_md_type_t MBEDTLS_PRIVATE(md_type); /**< Hash to use */ + mbedtls_ecp_group MBEDTLS_PRIVATE(grp); /**< Elliptic curve */ + mbedtls_ecjpake_role MBEDTLS_PRIVATE(role); /**< Are we client or server? */ + int MBEDTLS_PRIVATE(point_format); /**< Format for point export */ + + mbedtls_ecp_point MBEDTLS_PRIVATE(Xm1); /**< My public key 1 C: X1, S: X3 */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Xm2); /**< My public key 2 C: X2, S: X4 */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Xp1); /**< Peer public key 1 C: X3, S: X1 */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Xp2); /**< Peer public key 2 C: X4, S: X2 */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Xp); /**< Peer public key C: Xs, S: Xc */ + + mbedtls_mpi MBEDTLS_PRIVATE(xm1); /**< My private key 1 C: x1, S: x3 */ + mbedtls_mpi MBEDTLS_PRIVATE(xm2); /**< My private key 2 C: x2, S: x4 */ + + mbedtls_mpi MBEDTLS_PRIVATE(s); /**< Pre-shared secret (passphrase) */ +} mbedtls_ecjpake_context; + +#else /* MBEDTLS_ECJPAKE_ALT */ +#include "ecjpake_alt.h" +#endif /* MBEDTLS_ECJPAKE_ALT */ + +/** + * \brief Initialize an ECJPAKE context. + * + * \param ctx The ECJPAKE context to initialize. + * This must not be \c NULL. + */ +void mbedtls_ecjpake_init(mbedtls_ecjpake_context *ctx); + +/** + * \brief Set up an ECJPAKE context for use. + * + * \note Currently the only values for hash/curve allowed by the + * standard are #MBEDTLS_MD_SHA256/#MBEDTLS_ECP_DP_SECP256R1. + * + * \param ctx The ECJPAKE context to set up. This must be initialized. + * \param role The role of the caller. This must be either + * #MBEDTLS_ECJPAKE_CLIENT or #MBEDTLS_ECJPAKE_SERVER. + * \param hash The identifier of the hash function to use, + * for example #MBEDTLS_MD_SHA256. + * \param curve The identifier of the elliptic curve to use, + * for example #MBEDTLS_ECP_DP_SECP256R1. + * \param secret The pre-shared secret (passphrase). This must be + * a readable not empty buffer of length \p len Bytes. It need + * only be valid for the duration of this call. + * \param len The length of the pre-shared secret \p secret. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_ecjpake_setup(mbedtls_ecjpake_context *ctx, + mbedtls_ecjpake_role role, + mbedtls_md_type_t hash, + mbedtls_ecp_group_id curve, + const unsigned char *secret, + size_t len); + +/** + * \brief Set the point format for future reads and writes. + * + * \param ctx The ECJPAKE context to configure. + * \param point_format The point format to use: + * #MBEDTLS_ECP_PF_UNCOMPRESSED (default) + * or #MBEDTLS_ECP_PF_COMPRESSED. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if \p point_format + * is invalid. + */ +int mbedtls_ecjpake_set_point_format(mbedtls_ecjpake_context *ctx, + int point_format); + +/** + * \brief Check if an ECJPAKE context is ready for use. + * + * \param ctx The ECJPAKE context to check. This must be + * initialized. + * + * \return \c 0 if the context is ready for use. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA otherwise. + */ +int mbedtls_ecjpake_check(const mbedtls_ecjpake_context *ctx); + +/** + * \brief Generate and write the first round message + * (TLS: contents of the Client/ServerHello extension, + * excluding extension type and length bytes). + * + * \param ctx The ECJPAKE context to use. This must be + * initialized and set up. + * \param buf The buffer to write the contents to. This must be a + * writable buffer of length \p len Bytes. + * \param len The length of \p buf in Bytes. + * \param olen The address at which to store the total number + * of Bytes written to \p buf. This must not be \c NULL. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This + * may be \c NULL if \p f_rng doesn't use a context. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_ecjpake_write_round_one(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Read and process the first round message + * (TLS: contents of the Client/ServerHello extension, + * excluding extension type and length bytes). + * + * \param ctx The ECJPAKE context to use. This must be initialized + * and set up. + * \param buf The buffer holding the first round message. This must + * be a readable buffer of length \p len Bytes. + * \param len The length in Bytes of \p buf. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_ecjpake_read_round_one(mbedtls_ecjpake_context *ctx, + const unsigned char *buf, + size_t len); + +/** + * \brief Generate and write the second round message + * (TLS: contents of the Client/ServerKeyExchange). + * + * \param ctx The ECJPAKE context to use. This must be initialized, + * set up, and already have performed round one. + * \param buf The buffer to write the round two contents to. + * This must be a writable buffer of length \p len Bytes. + * \param len The size of \p buf in Bytes. + * \param olen The address at which to store the total number of Bytes + * written to \p buf. This must not be \c NULL. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This + * may be \c NULL if \p f_rng doesn't use a context. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_ecjpake_write_round_two(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Read and process the second round message + * (TLS: contents of the Client/ServerKeyExchange). + * + * \param ctx The ECJPAKE context to use. This must be initialized + * and set up and already have performed round one. + * \param buf The buffer holding the second round message. This must + * be a readable buffer of length \p len Bytes. + * \param len The length in Bytes of \p buf. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_ecjpake_read_round_two(mbedtls_ecjpake_context *ctx, + const unsigned char *buf, + size_t len); + +/** + * \brief Derive the shared secret + * (TLS: Pre-Master Secret). + * + * \param ctx The ECJPAKE context to use. This must be initialized, + * set up and have performed both round one and two. + * \param buf The buffer to write the derived secret to. This must + * be a writable buffer of length \p len Bytes. + * \param len The length of \p buf in Bytes. + * \param olen The address at which to store the total number of Bytes + * written to \p buf. This must not be \c NULL. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This + * may be \c NULL if \p f_rng doesn't use a context. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_ecjpake_derive_secret(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Write the shared key material to be passed to a Key + * Derivation Function as described in RFC8236. + * + * \param ctx The ECJPAKE context to use. This must be initialized, + * set up and have performed both round one and two. + * \param buf The buffer to write the derived secret to. This must + * be a writable buffer of length \p len Bytes. + * \param len The length of \p buf in Bytes. + * \param olen The address at which to store the total number of bytes + * written to \p buf. This must not be \c NULL. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This + * may be \c NULL if \p f_rng doesn't use a context. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_ecjpake_write_shared_key(mbedtls_ecjpake_context *ctx, + unsigned char *buf, size_t len, size_t *olen, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This clears an ECJPAKE context and frees any + * embedded data structure. + * + * \param ctx The ECJPAKE context to free. This may be \c NULL, + * in which case this function does nothing. If it is not + * \c NULL, it must point to an initialized ECJPAKE context. + */ +void mbedtls_ecjpake_free(mbedtls_ecjpake_context *ctx); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if a test failed + */ +int mbedtls_ecjpake_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ecjpake.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ecp.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ecp.h new file mode 100644 index 00000000..b6144d9a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ecp.h @@ -0,0 +1,1339 @@ +/** + * \file ecp.h + * + * \brief This file provides an API for Elliptic Curves over GF(P) (ECP). + * + * The use of ECP in cryptography and TLS is defined in + * Standards for Efficient Cryptography Group (SECG): SEC1 + * Elliptic Curve Cryptography and + * RFC-4492: Elliptic Curve Cryptography (ECC) Cipher Suites + * for Transport Layer Security (TLS). + * + * RFC-2409: The Internet Key Exchange (IKE) defines ECP + * group types. + * + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_ECP_H +#define MBEDTLS_ECP_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/bignum.h" + +/* + * ECP error codes + */ +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_ECP_BAD_INPUT_DATA -0x4F80 +/** The buffer is too small to write to. */ +#define MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL -0x4F00 +/** The requested feature is not available, for example, the requested curve is not supported. */ +#define MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE -0x4E80 +/** The signature is not valid. */ +#define MBEDTLS_ERR_ECP_VERIFY_FAILED -0x4E00 +/** Memory allocation failed. */ +#define MBEDTLS_ERR_ECP_ALLOC_FAILED -0x4D80 +/** Generation of random value, such as ephemeral key, failed. */ +#define MBEDTLS_ERR_ECP_RANDOM_FAILED -0x4D00 +/** Invalid private or public key. */ +#define MBEDTLS_ERR_ECP_INVALID_KEY -0x4C80 +/** The buffer contains a valid signature followed by more data. */ +#define MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH -0x4C00 +/** Operation in progress, call again with the same parameters to continue. */ +#define MBEDTLS_ERR_ECP_IN_PROGRESS -0x4B00 + +/* Flags indicating whether to include code that is specific to certain + * types of curves. These flags are for internal library use only. */ +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || \ + defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +#define MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED +#endif +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) || \ + defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +#define MBEDTLS_ECP_MONTGOMERY_ENABLED +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Domain-parameter identifiers: curve, subgroup, and generator. + * + * \note Only curves over prime fields are supported. + * + * \warning This library does not support validation of arbitrary domain + * parameters. Therefore, only standardized domain parameters from trusted + * sources should be used. See mbedtls_ecp_group_load(). + */ +/* Note: when adding a new curve: + * - Add it at the end of this enum, otherwise you'll break the ABI by + * changing the numerical value for existing curves. + * - Increment MBEDTLS_ECP_DP_MAX below if needed. + * - Update the calculation of MBEDTLS_ECP_MAX_BITS below. + * - Add the corresponding MBEDTLS_ECP_DP_xxx_ENABLED macro definition to + * mbedtls_config.h. + * - List the curve as a dependency of MBEDTLS_ECP_C and + * MBEDTLS_ECDSA_C if supported in check_config.h. + * - Add the curve to the appropriate curve type macro + * MBEDTLS_ECP_yyy_ENABLED above. + * - Add the necessary definitions to ecp_curves.c. + * - Add the curve to the ecp_supported_curves array in ecp.c. + * - Add the curve to applicable profiles in x509_crt.c. + * - Add the curve to applicable presets in ssl_tls.c. + */ +typedef enum { + MBEDTLS_ECP_DP_NONE = 0, /*!< Curve not defined. */ + MBEDTLS_ECP_DP_SECP192R1, /*!< Domain parameters for the 192-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP224R1, /*!< Domain parameters for the 224-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP256R1, /*!< Domain parameters for the 256-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP384R1, /*!< Domain parameters for the 384-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_SECP521R1, /*!< Domain parameters for the 521-bit curve defined by FIPS 186-4 and SEC1. */ + MBEDTLS_ECP_DP_BP256R1, /*!< Domain parameters for 256-bit Brainpool curve. */ + MBEDTLS_ECP_DP_BP384R1, /*!< Domain parameters for 384-bit Brainpool curve. */ + MBEDTLS_ECP_DP_BP512R1, /*!< Domain parameters for 512-bit Brainpool curve. */ + MBEDTLS_ECP_DP_CURVE25519, /*!< Domain parameters for Curve25519. */ + MBEDTLS_ECP_DP_SECP192K1, /*!< Domain parameters for 192-bit "Koblitz" curve. */ + MBEDTLS_ECP_DP_SECP224K1, /*!< Domain parameters for 224-bit "Koblitz" curve. */ + MBEDTLS_ECP_DP_SECP256K1, /*!< Domain parameters for 256-bit "Koblitz" curve. */ + MBEDTLS_ECP_DP_CURVE448, /*!< Domain parameters for Curve448. */ +} mbedtls_ecp_group_id; + +/** + * The number of supported curves, plus one for #MBEDTLS_ECP_DP_NONE. + */ +#define MBEDTLS_ECP_DP_MAX 14 + +/* + * Curve types + */ +typedef enum { + MBEDTLS_ECP_TYPE_NONE = 0, + MBEDTLS_ECP_TYPE_SHORT_WEIERSTRASS, /* y^2 = x^3 + a x + b */ + MBEDTLS_ECP_TYPE_MONTGOMERY, /* y^2 = x^3 + a x^2 + x */ +} mbedtls_ecp_curve_type; + +/* + * Curve modulus types + */ +typedef enum { + MBEDTLS_ECP_MOD_NONE = 0, + MBEDTLS_ECP_MOD_COORDINATE, + MBEDTLS_ECP_MOD_SCALAR +} mbedtls_ecp_modulus_type; + +/** + * Curve information, for use by other modules. + * + * The fields of this structure are part of the public API and can be + * accessed directly by applications. Future versions of the library may + * add extra fields or reorder existing fields. + */ +typedef struct mbedtls_ecp_curve_info { + mbedtls_ecp_group_id grp_id; /*!< An internal identifier. */ + uint16_t tls_id; /*!< The TLS NamedCurve identifier. */ + uint16_t bit_size; /*!< The curve size in bits. */ + const char *name; /*!< A human-friendly name. */ +} mbedtls_ecp_curve_info; + +/** + * \brief The ECP point structure, in Jacobian coordinates. + * + * \note All functions expect and return points satisfying + * the following condition: Z == 0 or + * Z == 1. Other values of \p Z are + * used only by internal functions. + * The point is zero, or "at infinity", if Z == 0. + * Otherwise, \p X and \p Y are its standard (affine) + * coordinates. + */ +typedef struct mbedtls_ecp_point { + mbedtls_mpi MBEDTLS_PRIVATE(X); /*!< The X coordinate of the ECP point. */ + mbedtls_mpi MBEDTLS_PRIVATE(Y); /*!< The Y coordinate of the ECP point. */ + mbedtls_mpi MBEDTLS_PRIVATE(Z); /*!< The Z coordinate of the ECP point. */ +} +mbedtls_ecp_point; + +#if !defined(MBEDTLS_ECP_ALT) +/* + * default mbed TLS elliptic curve arithmetic implementation + * + * (in case MBEDTLS_ECP_ALT is defined then the developer has to provide an + * alternative implementation for the whole module and it will replace this + * one.) + */ + +/** + * \brief The ECP group structure. + * + * We consider two types of curve equations: + *
  • Short Weierstrass: y^2 = x^3 + A x + B mod P + * (SEC1 + RFC-4492)
  • + *
  • Montgomery: y^2 = x^3 + A x^2 + x mod P (Curve25519, + * Curve448)
+ * In both cases, the generator (\p G) for a prime-order subgroup is fixed. + * + * For Short Weierstrass, this subgroup is the whole curve, and its + * cardinality is denoted by \p N. Our code requires that \p N is an + * odd prime as mbedtls_ecp_mul() requires an odd number, and + * mbedtls_ecdsa_sign() requires that it is prime for blinding purposes. + * + * For Montgomery curves, we do not store \p A, but (A + 2) / 4, + * which is the quantity used in the formulas. Additionally, \p nbits is + * not the size of \p N but the required size for private keys. + * + * If \p modp is NULL, reduction modulo \p P is done using a generic algorithm. + * Otherwise, \p modp must point to a function that takes an \p mbedtls_mpi in the + * range of 0..2^(2*pbits)-1, and transforms it in-place to an integer + * which is congruent mod \p P to the given MPI, and is close enough to \p pbits + * in size, so that it may be efficiently brought in the 0..P-1 range by a few + * additions or subtractions. Therefore, it is only an approximative modular + * reduction. It must return 0 on success and non-zero on failure. + * + * \note Alternative implementations of the ECP module must obey the + * following constraints. + * * Group IDs must be distinct: if two group structures have + * the same ID, then they must be identical. + * * The fields \c id, \c P, \c A, \c B, \c G, \c N, + * \c pbits and \c nbits must have the same type and semantics + * as in the built-in implementation. + * They must be available for reading, but direct modification + * of these fields does not need to be supported. + * They do not need to be at the same offset in the structure. + */ +typedef struct mbedtls_ecp_group { + mbedtls_ecp_group_id id; /*!< An internal group identifier. */ + mbedtls_mpi P; /*!< The prime modulus of the base field. */ + mbedtls_mpi A; /*!< For Short Weierstrass: \p A in the equation. For + Montgomery curves: (A + 2) / 4. */ + mbedtls_mpi B; /*!< For Short Weierstrass: \p B in the equation. + For Montgomery curves: unused. */ + mbedtls_ecp_point G; /*!< The generator of the subgroup used. */ + mbedtls_mpi N; /*!< The order of \p G. */ + size_t pbits; /*!< The number of bits in \p P.*/ + size_t nbits; /*!< For Short Weierstrass: The number of bits in \p P. + For Montgomery curves: the number of bits in the + private keys. */ + /* End of public fields */ + + unsigned int MBEDTLS_PRIVATE(h); /*!< \internal 1 if the constants are static. */ + int(*MBEDTLS_PRIVATE(modp))(mbedtls_mpi *); /*!< The function for fast pseudo-reduction + mod \p P (see above).*/ + int(*MBEDTLS_PRIVATE(t_pre))(mbedtls_ecp_point *, void *); /*!< Unused. */ + int(*MBEDTLS_PRIVATE(t_post))(mbedtls_ecp_point *, void *); /*!< Unused. */ + void *MBEDTLS_PRIVATE(t_data); /*!< Unused. */ + mbedtls_ecp_point *MBEDTLS_PRIVATE(T); /*!< Pre-computed points for ecp_mul_comb(). */ + size_t MBEDTLS_PRIVATE(T_size); /*!< The number of dynamic allocated pre-computed points. */ +} +mbedtls_ecp_group; + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h, or define them using the compiler command line. + * \{ + */ + +#if !defined(MBEDTLS_ECP_WINDOW_SIZE) +/* + * Maximum "window" size used for point multiplication. + * Default: a point where higher memory usage yields diminishing performance + * returns. + * Minimum value: 2. Maximum value: 7. + * + * Result is an array of at most ( 1 << ( MBEDTLS_ECP_WINDOW_SIZE - 1 ) ) + * points used for point multiplication. This value is directly tied to EC + * peak memory usage, so decreasing it by one should roughly cut memory usage + * by two (if large curves are in use). + * + * Reduction in size may reduce speed, but larger curves are impacted first. + * Sample performances (in ECDHE handshakes/s, with FIXED_POINT_OPTIM = 1): + * w-size: 6 5 4 3 2 + * 521 145 141 135 120 97 + * 384 214 209 198 177 146 + * 256 320 320 303 262 226 + * 224 475 475 453 398 342 + * 192 640 640 633 587 476 + */ +#define MBEDTLS_ECP_WINDOW_SIZE 4 /**< The maximum window size used. */ +#endif /* MBEDTLS_ECP_WINDOW_SIZE */ + +#if !defined(MBEDTLS_ECP_FIXED_POINT_OPTIM) +/* + * Trade code size for speed on fixed-point multiplication. + * + * This speeds up repeated multiplication of the generator (that is, the + * multiplication in ECDSA signatures, and half of the multiplications in + * ECDSA verification and ECDHE) by a factor roughly 3 to 4. + * + * For each n-bit Short Weierstrass curve that is enabled, this adds 4n bytes + * of code size if n < 384 and 8n otherwise. + * + * Change this value to 0 to reduce code size. + */ +#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up. */ +#endif /* MBEDTLS_ECP_FIXED_POINT_OPTIM */ + +/** \} name SECTION: Module settings */ + +#else /* MBEDTLS_ECP_ALT */ +#include "ecp_alt.h" +#endif /* MBEDTLS_ECP_ALT */ + +/** + * The maximum size of the groups, that is, of \c N and \c P. + */ +#if !defined(MBEDTLS_ECP_C) +/* Dummy definition to help code that has optional ECP support and + * defines an MBEDTLS_ECP_MAX_BYTES-sized array unconditionally. */ +#define MBEDTLS_ECP_MAX_BITS 1 +/* Note: the curves must be listed in DECREASING size! */ +#elif defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 521 +#elif defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 512 +#elif defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 448 +#elif defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 384 +#elif defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 384 +#elif defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 256 +#elif defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 256 +#elif defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 256 +#elif defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 255 +#elif defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 225 // n is slightly above 2^224 +#elif defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 224 +#elif defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 192 +#elif defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) +#define MBEDTLS_ECP_MAX_BITS 192 +#else +#error "Missing definition of MBEDTLS_ECP_MAX_BITS" +#endif + +#define MBEDTLS_ECP_MAX_BYTES ((MBEDTLS_ECP_MAX_BITS + 7) / 8) +#define MBEDTLS_ECP_MAX_PT_LEN (2 * MBEDTLS_ECP_MAX_BYTES + 1) + +#if defined(MBEDTLS_ECP_RESTARTABLE) + +/** + * \brief Internal restart context for multiplication + * + * \note Opaque struct + */ +typedef struct mbedtls_ecp_restart_mul mbedtls_ecp_restart_mul_ctx; + +/** + * \brief Internal restart context for ecp_muladd() + * + * \note Opaque struct + */ +typedef struct mbedtls_ecp_restart_muladd mbedtls_ecp_restart_muladd_ctx; + +/** + * \brief General context for resuming ECC operations + */ +typedef struct { + unsigned MBEDTLS_PRIVATE(ops_done); /*!< current ops count */ + unsigned MBEDTLS_PRIVATE(depth); /*!< call depth (0 = top-level) */ + mbedtls_ecp_restart_mul_ctx *MBEDTLS_PRIVATE(rsm); /*!< ecp_mul_comb() sub-context */ + mbedtls_ecp_restart_muladd_ctx *MBEDTLS_PRIVATE(ma); /*!< ecp_muladd() sub-context */ +} mbedtls_ecp_restart_ctx; + +/* + * Operation counts for restartable functions + */ +#define MBEDTLS_ECP_OPS_CHK 3 /*!< basic ops count for ecp_check_pubkey() */ +#define MBEDTLS_ECP_OPS_DBL 8 /*!< basic ops count for ecp_double_jac() */ +#define MBEDTLS_ECP_OPS_ADD 11 /*!< basic ops count for see ecp_add_mixed() */ +#define MBEDTLS_ECP_OPS_INV 120 /*!< empirical equivalent for mpi_mod_inv() */ + +/** + * \brief Internal; for restartable functions in other modules. + * Check and update basic ops budget. + * + * \param grp Group structure + * \param rs_ctx Restart context + * \param ops Number of basic ops to do + * + * \return \c 0 if doing \p ops basic ops is still allowed, + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS otherwise. + */ +int mbedtls_ecp_check_budget(const mbedtls_ecp_group *grp, + mbedtls_ecp_restart_ctx *rs_ctx, + unsigned ops); + +/* Utility macro for checking and updating ops budget */ +#define MBEDTLS_ECP_BUDGET(ops) \ + MBEDTLS_MPI_CHK(mbedtls_ecp_check_budget(grp, rs_ctx, \ + (unsigned) (ops))); + +#else /* MBEDTLS_ECP_RESTARTABLE */ + +#define MBEDTLS_ECP_BUDGET(ops) /* no-op; for compatibility */ + +/* We want to declare restartable versions of existing functions anyway */ +typedef void mbedtls_ecp_restart_ctx; + +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief The ECP key-pair structure. + * + * A generic key-pair that may be used for ECDSA and fixed ECDH, for example. + * + * \note Members are deliberately in the same order as in the + * ::mbedtls_ecdsa_context structure. + */ +typedef struct mbedtls_ecp_keypair { + mbedtls_ecp_group MBEDTLS_PRIVATE(grp); /*!< Elliptic curve and base point */ + mbedtls_mpi MBEDTLS_PRIVATE(d); /*!< our secret value */ + mbedtls_ecp_point MBEDTLS_PRIVATE(Q); /*!< our public value */ +} +mbedtls_ecp_keypair; + +/** + * The uncompressed point format for Short Weierstrass curves + * (MBEDTLS_ECP_DP_SECP_XXX and MBEDTLS_ECP_DP_BP_XXX). + */ +#define MBEDTLS_ECP_PF_UNCOMPRESSED 0 +/** + * The compressed point format for Short Weierstrass curves + * (MBEDTLS_ECP_DP_SECP_XXX and MBEDTLS_ECP_DP_BP_XXX). + * + * \warning While this format is supported for all concerned curves for + * writing, when it comes to parsing, it is not supported for all + * curves. Specifically, parsing compressed points on + * MBEDTLS_ECP_DP_SECP224R1 and MBEDTLS_ECP_DP_SECP224K1 is not + * supported. + */ +#define MBEDTLS_ECP_PF_COMPRESSED 1 + +/* + * Some other constants from RFC 4492 + */ +#define MBEDTLS_ECP_TLS_NAMED_CURVE 3 /**< The named_curve of ECCurveType. */ + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Set the maximum number of basic operations done in a row. + * + * If more operations are needed to complete a computation, + * #MBEDTLS_ERR_ECP_IN_PROGRESS will be returned by the + * function performing the computation. It is then the + * caller's responsibility to either call again with the same + * parameters until it returns 0 or an error code; or to free + * the restart context if the operation is to be aborted. + * + * It is strictly required that all input parameters and the + * restart context be the same on successive calls for the + * same operation, but output parameters need not be the + * same; they must not be used until the function finally + * returns 0. + * + * This only applies to functions whose documentation + * mentions they may return #MBEDTLS_ERR_ECP_IN_PROGRESS (or + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS for functions in the + * SSL module). For functions that accept a "restart context" + * argument, passing NULL disables restart and makes the + * function equivalent to the function with the same name + * with \c _restartable removed. For functions in the ECDH + * module, restart is disabled unless the function accepts + * an "ECDH context" argument and + * mbedtls_ecdh_enable_restart() was previously called on + * that context. For function in the SSL module, restart is + * only enabled for specific sides and key exchanges + * (currently only for clients and ECDHE-ECDSA). + * + * \warning Using the PSA interruptible interfaces with keys in local + * storage and no accelerator driver will also call this + * function to set the values specified via those interfaces, + * overwriting values previously set. Care should be taken if + * mixing these two interfaces. + * + * \param max_ops Maximum number of basic operations done in a row. + * Default: 0 (unlimited). + * Lower (non-zero) values mean ECC functions will block for + * a lesser maximum amount of time. + * + * \note A "basic operation" is defined as a rough equivalent of a + * multiplication in GF(p) for the NIST P-256 curve. + * As an indication, with default settings, a scalar + * multiplication (full run of \c mbedtls_ecp_mul()) is: + * - about 3300 basic operations for P-256 + * - about 9400 basic operations for P-384 + * + * \note Very low values are not always respected: sometimes + * functions need to block for a minimum number of + * operations, and will do so even if max_ops is set to a + * lower value. That minimum depends on the curve size, and + * can be made lower by decreasing the value of + * \c MBEDTLS_ECP_WINDOW_SIZE. As an indication, here is the + * lowest effective value for various curves and values of + * that parameter (w for short): + * w=6 w=5 w=4 w=3 w=2 + * P-256 208 208 160 136 124 + * P-384 682 416 320 272 248 + * P-521 1364 832 640 544 496 + * + * \note This setting is currently ignored by Curve25519. + */ +void mbedtls_ecp_set_max_ops(unsigned max_ops); + +/** + * \brief Check if restart is enabled (max_ops != 0) + * + * \return \c 0 if \c max_ops == 0 (restart disabled) + * \return \c 1 otherwise (restart enabled) + */ +int mbedtls_ecp_restart_is_enabled(void); +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/* + * Get the type of a curve + */ +mbedtls_ecp_curve_type mbedtls_ecp_get_type(const mbedtls_ecp_group *grp); + +/** + * \brief This function retrieves the information defined in + * mbedtls_ecp_curve_info() for all supported curves. + * + * \note This function returns information about all curves + * supported by the library. Some curves may not be + * supported for all algorithms. Call mbedtls_ecdh_can_do() + * or mbedtls_ecdsa_can_do() to check if a curve is + * supported for ECDH or ECDSA. + * + * \return A statically allocated array. The last entry is 0. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_list(void); + +/** + * \brief This function retrieves the list of internal group + * identifiers of all supported curves in the order of + * preference. + * + * \note This function returns information about all curves + * supported by the library. Some curves may not be + * supported for all algorithms. Call mbedtls_ecdh_can_do() + * or mbedtls_ecdsa_can_do() to check if a curve is + * supported for ECDH or ECDSA. + * + * \return A statically allocated array, + * terminated with MBEDTLS_ECP_DP_NONE. + */ +const mbedtls_ecp_group_id *mbedtls_ecp_grp_id_list(void); + +/** + * \brief This function retrieves curve information from an internal + * group identifier. + * + * \param grp_id An \c MBEDTLS_ECP_DP_XXX value. + * + * \return The associated curve information on success. + * \return NULL on failure. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_grp_id(mbedtls_ecp_group_id grp_id); + +/** + * \brief This function retrieves curve information from a TLS + * NamedCurve value. + * + * \param tls_id An \c MBEDTLS_ECP_DP_XXX value. + * + * \return The associated curve information on success. + * \return NULL on failure. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_tls_id(uint16_t tls_id); + +/** + * \brief This function retrieves curve information from a + * human-readable name. + * + * \param name The human-readable name. + * + * \return The associated curve information on success. + * \return NULL on failure. + */ +const mbedtls_ecp_curve_info *mbedtls_ecp_curve_info_from_name(const char *name); + +/** + * \brief This function initializes a point as zero. + * + * \param pt The point to initialize. + */ +void mbedtls_ecp_point_init(mbedtls_ecp_point *pt); + +/** + * \brief This function initializes an ECP group context + * without loading any domain parameters. + * + * \note After this function is called, domain parameters + * for various ECP groups can be loaded through the + * mbedtls_ecp_group_load() or mbedtls_ecp_tls_read_group() + * functions. + */ +void mbedtls_ecp_group_init(mbedtls_ecp_group *grp); + +/** + * \brief This function initializes a key pair as an invalid one. + * + * \param key The key pair to initialize. + */ +void mbedtls_ecp_keypair_init(mbedtls_ecp_keypair *key); + +/** + * \brief This function frees the components of a point. + * + * \param pt The point to free. + */ +void mbedtls_ecp_point_free(mbedtls_ecp_point *pt); + +/** + * \brief This function frees the components of an ECP group. + * + * \param grp The group to free. This may be \c NULL, in which + * case this function returns immediately. If it is not + * \c NULL, it must point to an initialized ECP group. + */ +void mbedtls_ecp_group_free(mbedtls_ecp_group *grp); + +/** + * \brief This function frees the components of a key pair. + * + * \param key The key pair to free. This may be \c NULL, in which + * case this function returns immediately. If it is not + * \c NULL, it must point to an initialized ECP key pair. + */ +void mbedtls_ecp_keypair_free(mbedtls_ecp_keypair *key); + +#if defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Initialize a restart context. + * + * \param ctx The restart context to initialize. This must + * not be \c NULL. + */ +void mbedtls_ecp_restart_init(mbedtls_ecp_restart_ctx *ctx); + +/** + * \brief Free the components of a restart context. + * + * \param ctx The restart context to free. This may be \c NULL, in which + * case this function returns immediately. If it is not + * \c NULL, it must point to an initialized restart context. + */ +void mbedtls_ecp_restart_free(mbedtls_ecp_restart_ctx *ctx); +#endif /* MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief This function copies the contents of point \p Q into + * point \p P. + * + * \param P The destination point. This must be initialized. + * \param Q The source point. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code for other kinds of failure. + */ +int mbedtls_ecp_copy(mbedtls_ecp_point *P, const mbedtls_ecp_point *Q); + +/** + * \brief This function copies the contents of group \p src into + * group \p dst. + * + * \param dst The destination group. This must be initialized. + * \param src The source group. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_group_copy(mbedtls_ecp_group *dst, + const mbedtls_ecp_group *src); + +/** + * \brief This function sets a point to the point at infinity. + * + * \param pt The point to set. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_set_zero(mbedtls_ecp_point *pt); + +/** + * \brief This function checks if a point is the point at infinity. + * + * \param pt The point to test. This must be initialized. + * + * \return \c 1 if the point is zero. + * \return \c 0 if the point is non-zero. + * \return A negative error code on failure. + */ +int mbedtls_ecp_is_zero(mbedtls_ecp_point *pt); + +/** + * \brief This function compares two points. + * + * \note This assumes that the points are normalized. Otherwise, + * they may compare as "not equal" even if they are. + * + * \param P The first point to compare. This must be initialized. + * \param Q The second point to compare. This must be initialized. + * + * \return \c 0 if the points are equal. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the points are not equal. + */ +int mbedtls_ecp_point_cmp(const mbedtls_ecp_point *P, + const mbedtls_ecp_point *Q); + +/** + * \brief This function imports a non-zero point from two ASCII + * strings. + * + * \param P The destination point. This must be initialized. + * \param radix The numeric base of the input. + * \param x The first affine coordinate, as a null-terminated string. + * \param y The second affine coordinate, as a null-terminated string. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_MPI_XXX error code on failure. + */ +int mbedtls_ecp_point_read_string(mbedtls_ecp_point *P, int radix, + const char *x, const char *y); + +/** + * \brief This function exports a point into unsigned binary data. + * + * \param grp The group to which the point should belong. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param P The point to export. This must be initialized. + * \param format The point format. This must be either + * #MBEDTLS_ECP_PF_COMPRESSED or #MBEDTLS_ECP_PF_UNCOMPRESSED. + * (For groups without these formats, this parameter is + * ignored. But it still has to be either of the above + * values.) + * \param olen The address at which to store the length of + * the output in Bytes. This must not be \c NULL. + * \param buf The output buffer. This must be a writable buffer + * of length \p buflen Bytes. + * \param buflen The length of the output buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the output buffer + * is too small to hold the point. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the point format + * or the export for the given group is not implemented. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_point_write_binary(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *P, + int format, size_t *olen, + unsigned char *buf, size_t buflen); + +/** + * \brief This function imports a point from unsigned binary data. + * + * \note This function does not check that the point actually + * belongs to the given group, see mbedtls_ecp_check_pubkey() + * for that. + * + * \note For compressed points, see #MBEDTLS_ECP_PF_COMPRESSED for + * limitations. + * + * \param grp The group to which the point should belong. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param P The destination context to import the point to. + * This must be initialized. + * \param buf The input buffer. This must be a readable buffer + * of length \p ilen Bytes. + * \param ilen The length of the input buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the input is invalid. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the import for the + * given group is not implemented. + */ +int mbedtls_ecp_point_read_binary(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *P, + const unsigned char *buf, size_t ilen); + +/** + * \brief This function imports a point from a TLS ECPoint record. + * + * \note On function return, \p *buf is updated to point immediately + * after the ECPoint record. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param pt The destination point. + * \param buf The address of the pointer to the start of the input buffer. + * \param len The length of the buffer. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_MPI_XXX error code on initialization + * failure. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid. + */ +int mbedtls_ecp_tls_read_point(const mbedtls_ecp_group *grp, + mbedtls_ecp_point *pt, + const unsigned char **buf, size_t len); + +/** + * \brief This function exports a point as a TLS ECPoint record + * defined in RFC 4492, Section 5.4. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param pt The point to be exported. This must be initialized. + * \param format The point format to use. This must be either + * #MBEDTLS_ECP_PF_COMPRESSED or #MBEDTLS_ECP_PF_UNCOMPRESSED. + * \param olen The address at which to store the length in Bytes + * of the data written. + * \param buf The target buffer. This must be a writable buffer of + * length \p blen Bytes. + * \param blen The length of the target buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the input is invalid. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the target buffer + * is too small to hold the exported point. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_write_point(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *pt, + int format, size_t *olen, + unsigned char *buf, size_t blen); + +/** + * \brief This function sets up an ECP group context + * from a standardized set of domain parameters. + * + * \note The index should be a value of the NamedCurve enum, + * as defined in RFC-4492: Elliptic Curve Cryptography + * (ECC) Cipher Suites for Transport Layer Security (TLS), + * usually in the form of an \c MBEDTLS_ECP_DP_XXX macro. + * + * \param grp The group context to setup. This must be initialized. + * \param id The identifier of the domain parameter set to load. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if \p id doesn't + * correspond to a known group. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_group_load(mbedtls_ecp_group *grp, mbedtls_ecp_group_id id); + +/** + * \brief This function sets up an ECP group context from a TLS + * ECParameters record as defined in RFC 4492, Section 5.4. + * + * \note The read pointer \p buf is updated to point right after + * the ECParameters record on exit. + * + * \param grp The group context to setup. This must be initialized. + * \param buf The address of the pointer to the start of the input buffer. + * \param len The length of the input buffer \c *buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the group is not + * recognized. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_read_group(mbedtls_ecp_group *grp, + const unsigned char **buf, size_t len); + +/** + * \brief This function extracts an elliptic curve group ID from a + * TLS ECParameters record as defined in RFC 4492, Section 5.4. + * + * \note The read pointer \p buf is updated to point right after + * the ECParameters record on exit. + * + * \param grp The address at which to store the group id. + * This must not be \c NULL. + * \param buf The address of the pointer to the start of the input buffer. + * \param len The length of the input buffer \c *buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if input is invalid. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the group is not + * recognized. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_read_group_id(mbedtls_ecp_group_id *grp, + const unsigned char **buf, + size_t len); +/** + * \brief This function exports an elliptic curve as a TLS + * ECParameters record as defined in RFC 4492, Section 5.4. + * + * \param grp The ECP group to be exported. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param olen The address at which to store the number of Bytes written. + * This must not be \c NULL. + * \param buf The buffer to write to. This must be a writable buffer + * of length \p blen Bytes. + * \param blen The length of the output buffer \p buf in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the output + * buffer is too small to hold the exported group. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_tls_write_group(const mbedtls_ecp_group *grp, + size_t *olen, + unsigned char *buf, size_t blen); + +/** + * \brief This function performs a scalar multiplication of a point + * by an integer: \p R = \p m * \p P. + * + * It is not thread-safe to use same group in multiple threads. + * + * \note To prevent timing attacks, this function + * executes the exact same sequence of base-field + * operations for any valid \p m. It avoids any if-branch or + * array index depending on the value of \p m. It also uses + * \p f_rng to randomize some intermediate results. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply. This must be initialized. + * \param P The point to multiply. This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c + * NULL if \p f_rng doesn't need a context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m is not a valid private + * key, or \p P is not a valid public key. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_mul(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +/** + * \brief This function performs multiplication of a point by + * an integer: \p R = \p m * \p P in a restartable way. + * + * \see mbedtls_ecp_mul() + * + * \note This function does the same as \c mbedtls_ecp_mul(), but + * it can return early and restart according to the limit set + * with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply. This must be initialized. + * \param P The point to multiply. This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c + * NULL if \p f_rng doesn't need a context. + * \param rs_ctx The restart context (NULL disables restart). + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m is not a valid private + * key, or \p P is not a valid public key. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_mul_restartable(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_ecp_restart_ctx *rs_ctx); + +#if defined(MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED) +/** + * \brief This function performs multiplication and addition of two + * points by integers: \p R = \p m * \p P + \p n * \p Q + * + * It is not thread-safe to use same group in multiple threads. + * + * \note In contrast to mbedtls_ecp_mul(), this function does not + * guarantee a constant execution flow and timing. + * + * \note This function is only defined for short Weierstrass curves. + * It may not be included in builds without any short + * Weierstrass curve. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply \p P. + * This must be initialized. + * \param P The point to multiply by \p m. This must be initialized. + * \param n The integer by which to multiply \p Q. + * This must be initialized. + * \param Q The point to be multiplied by \p n. + * This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m or \p n are not + * valid private keys, or \p P or \p Q are not valid public + * keys. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if \p grp does not + * designate a short Weierstrass curve. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_muladd(mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q); + +/** + * \brief This function performs multiplication and addition of two + * points by integers: \p R = \p m * \p P + \p n * \p Q in a + * restartable way. + * + * \see \c mbedtls_ecp_muladd() + * + * \note This function works the same as \c mbedtls_ecp_muladd(), + * but it can return early and restart according to the limit + * set with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \note This function is only defined for short Weierstrass curves. + * It may not be included in builds without any short + * Weierstrass curve. + * + * \param grp The ECP group to use. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param R The point in which to store the result of the calculation. + * This must be initialized. + * \param m The integer by which to multiply \p P. + * This must be initialized. + * \param P The point to multiply by \p m. This must be initialized. + * \param n The integer by which to multiply \p Q. + * This must be initialized. + * \param Q The point to be multiplied by \p n. + * This must be initialized. + * \param rs_ctx The restart context (NULL disables restart). + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if \p m or \p n are not + * valid private keys, or \p P or \p Q are not valid public + * keys. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if \p grp does not + * designate a short Weierstrass curve. + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_muladd_restartable( + mbedtls_ecp_group *grp, mbedtls_ecp_point *R, + const mbedtls_mpi *m, const mbedtls_ecp_point *P, + const mbedtls_mpi *n, const mbedtls_ecp_point *Q, + mbedtls_ecp_restart_ctx *rs_ctx); +#endif /* MBEDTLS_ECP_SHORT_WEIERSTRASS_ENABLED */ + +/** + * \brief This function checks that a point is a valid public key + * on this curve. + * + * It only checks that the point is non-zero, has + * valid coordinates and lies on the curve. It does not verify + * that it is indeed a multiple of \p G. This additional + * check is computationally more expensive, is not required + * by standards, and should not be necessary if the group + * used has a small cofactor. In particular, it is useless for + * the NIST groups which all have a cofactor of 1. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure, to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group the point should belong to. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param pt The point to check. This must be initialized. + * + * \return \c 0 if the point is a valid public key. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if the point is not + * a valid public key for the given curve. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_check_pubkey(const mbedtls_ecp_group *grp, + const mbedtls_ecp_point *pt); + +/** + * \brief This function checks that an \p mbedtls_mpi is a + * valid private key for this curve. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group the private key should belong to. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param d The integer to check. This must be initialized. + * + * \return \c 0 if the point is a valid private key. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY if the point is not a valid + * private key for the given curve. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_check_privkey(const mbedtls_ecp_group *grp, + const mbedtls_mpi *d); + +/** + * \brief This function generates a private key. + * + * \param grp The ECP group to generate a private key for. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param d The destination MPI (secret part). This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG parameter to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_privkey(const mbedtls_ecp_group *grp, + mbedtls_mpi *d, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function generates a keypair with a configurable base + * point. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group to generate a key pair for. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param G The base point to use. This must be initialized + * and belong to \p grp. It replaces the default base + * point \c grp->G used by mbedtls_ecp_gen_keypair(). + * \param d The destination MPI (secret part). + * This must be initialized. + * \param Q The destination point (public part). + * This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_keypair_base(mbedtls_ecp_group *grp, + const mbedtls_ecp_point *G, + mbedtls_mpi *d, mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function generates an ECP keypair. + * + * \note This function uses bare components rather than an + * ::mbedtls_ecp_keypair structure to ease use with other + * structures, such as ::mbedtls_ecdh_context or + * ::mbedtls_ecdsa_context. + * + * \param grp The ECP group to generate a key pair for. + * This must be initialized and have group parameters + * set, for example through mbedtls_ecp_group_load(). + * \param d The destination MPI (secret part). + * This must be initialized. + * \param Q The destination point (public part). + * This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_keypair(mbedtls_ecp_group *grp, mbedtls_mpi *d, + mbedtls_ecp_point *Q, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function generates an ECP key. + * + * \param grp_id The ECP group identifier. + * \param key The destination key. This must be initialized. + * \param f_rng The RNG function to use. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_ECP_XXX or \c MBEDTLS_MPI_XXX error code + * on failure. + */ +int mbedtls_ecp_gen_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief This function reads an elliptic curve private key. + * + * \param grp_id The ECP group identifier. + * \param key The destination key. + * \param buf The buffer containing the binary representation of the + * key. (Big endian integer for Weierstrass curves, byte + * string for Montgomery curves.) + * \param buflen The length of the buffer in bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_INVALID_KEY error if the key is + * invalid. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the operation for + * the group is not implemented. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_ecp_read_key(mbedtls_ecp_group_id grp_id, mbedtls_ecp_keypair *key, + const unsigned char *buf, size_t buflen); + +/** + * \brief This function exports an elliptic curve private key. + * + * \param key The private key. + * \param buf The output buffer for containing the binary representation + * of the key. (Big endian integer for Weierstrass curves, byte + * string for Montgomery curves.) + * \param buflen The total length of the buffer in bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL if the \p key + representation is larger than the available space in \p buf. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if the operation for + * the group is not implemented. + * \return Another negative error code on different kinds of failure. + */ +int mbedtls_ecp_write_key(mbedtls_ecp_keypair *key, + unsigned char *buf, size_t buflen); + +/** + * \brief This function checks that the keypair objects + * \p pub and \p prv have the same group and the + * same public point, and that the private key in + * \p prv is consistent with the public key. + * + * \param pub The keypair structure holding the public key. This + * must be initialized. If it contains a private key, that + * part is ignored. + * \param prv The keypair structure holding the full keypair. + * This must be initialized. + * \param f_rng The RNG function. This must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c + * NULL if \p f_rng doesn't need a context. + * + * \return \c 0 on success, meaning that the keys are valid and match. + * \return #MBEDTLS_ERR_ECP_BAD_INPUT_DATA if the keys are invalid or do not match. + * \return An \c MBEDTLS_ERR_ECP_XXX or an \c MBEDTLS_ERR_MPI_XXX + * error code on calculation failure. + */ +int mbedtls_ecp_check_pub_priv( + const mbedtls_ecp_keypair *pub, const mbedtls_ecp_keypair *prv, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +/** + * \brief This function exports generic key-pair parameters. + * + * \param key The key pair to export from. + * \param grp Slot for exported ECP group. + * It must point to an initialized ECP group. + * \param d Slot for the exported secret value. + * It must point to an initialized mpi. + * \param Q Slot for the exported public value. + * It must point to an initialized ECP point. + * + * \return \c 0 on success, + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory-allocation failure. + * \return #MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE if key id doesn't + * correspond to a known group. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ecp_export(const mbedtls_ecp_keypair *key, mbedtls_ecp_group *grp, + mbedtls_mpi *d, mbedtls_ecp_point *Q); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The ECP checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_ecp_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* ecp.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/entropy.h b/r5dev/thirdparty/mbedtls/include/mbedtls/entropy.h new file mode 100644 index 00000000..2c8b7503 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/entropy.h @@ -0,0 +1,291 @@ +/** + * \file entropy.h + * + * \brief Entropy accumulator implementation + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ENTROPY_H +#define MBEDTLS_ENTROPY_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include + +#if defined(MBEDTLS_SHA512_C) && !defined(MBEDTLS_ENTROPY_FORCE_SHA256) +#include "mbedtls/sha512.h" +#define MBEDTLS_ENTROPY_SHA512_ACCUMULATOR +#else +#if defined(MBEDTLS_SHA256_C) +#define MBEDTLS_ENTROPY_SHA256_ACCUMULATOR +#include "mbedtls/sha256.h" +#endif +#endif + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + + +/** Critical entropy source failure. */ +#define MBEDTLS_ERR_ENTROPY_SOURCE_FAILED -0x003C +/** No more sources can be added. */ +#define MBEDTLS_ERR_ENTROPY_MAX_SOURCES -0x003E +/** No sources have been added to poll. */ +#define MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED -0x0040 +/** No strong sources have been added to poll. */ +#define MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE -0x003D +/** Read/write error in file. */ +#define MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR -0x003F + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(MBEDTLS_ENTROPY_MAX_SOURCES) +#define MBEDTLS_ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +#endif + +#if !defined(MBEDTLS_ENTROPY_MAX_GATHER) +#define MBEDTLS_ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ +#endif + +/** \} name SECTION: Module settings */ + +#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR) +#define MBEDTLS_ENTROPY_BLOCK_SIZE 64 /**< Block size of entropy accumulator (SHA-512) */ +#else +#define MBEDTLS_ENTROPY_BLOCK_SIZE 32 /**< Block size of entropy accumulator (SHA-256) */ +#endif + +#define MBEDTLS_ENTROPY_MAX_SEED_SIZE 1024 /**< Maximum size of seed we read from seed file */ +#define MBEDTLS_ENTROPY_SOURCE_MANUAL MBEDTLS_ENTROPY_MAX_SOURCES + +#define MBEDTLS_ENTROPY_SOURCE_STRONG 1 /**< Entropy source is strong */ +#define MBEDTLS_ENTROPY_SOURCE_WEAK 0 /**< Entropy source is weak */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Entropy poll callback pointer + * + * \param data Callback-specific data pointer + * \param output Data to fill + * \param len Maximum size to provide + * \param olen The actual amount of bytes put into the buffer (Can be 0) + * + * \return 0 if no critical failures occurred, + * MBEDTLS_ERR_ENTROPY_SOURCE_FAILED otherwise + */ +typedef int (*mbedtls_entropy_f_source_ptr)(void *data, unsigned char *output, size_t len, + size_t *olen); + +/** + * \brief Entropy source state + */ +typedef struct mbedtls_entropy_source_state { + mbedtls_entropy_f_source_ptr MBEDTLS_PRIVATE(f_source); /**< The entropy source callback */ + void *MBEDTLS_PRIVATE(p_source); /**< The callback data pointer */ + size_t MBEDTLS_PRIVATE(size); /**< Amount received in bytes */ + size_t MBEDTLS_PRIVATE(threshold); /**< Minimum bytes required before release */ + int MBEDTLS_PRIVATE(strong); /**< Is the source strong? */ +} +mbedtls_entropy_source_state; + +/** + * \brief Entropy context structure + */ +typedef struct mbedtls_entropy_context { + int MBEDTLS_PRIVATE(accumulator_started); /* 0 after init. + * 1 after the first update. + * -1 after free. */ +#if defined(MBEDTLS_ENTROPY_SHA512_ACCUMULATOR) + mbedtls_sha512_context MBEDTLS_PRIVATE(accumulator); +#elif defined(MBEDTLS_ENTROPY_SHA256_ACCUMULATOR) + mbedtls_sha256_context MBEDTLS_PRIVATE(accumulator); +#endif + int MBEDTLS_PRIVATE(source_count); /* Number of entries used in source. */ + mbedtls_entropy_source_state MBEDTLS_PRIVATE(source)[MBEDTLS_ENTROPY_MAX_SOURCES]; +#if defined(MBEDTLS_THREADING_C) + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); /*!< mutex */ +#endif +#if defined(MBEDTLS_ENTROPY_NV_SEED) + int MBEDTLS_PRIVATE(initial_entropy_run); +#endif +} +mbedtls_entropy_context; + +#if !defined(MBEDTLS_NO_PLATFORM_ENTROPY) +/** + * \brief Platform-specific entropy poll callback + */ +int mbedtls_platform_entropy_poll(void *data, + unsigned char *output, size_t len, size_t *olen); +#endif + +/** + * \brief Initialize the context + * + * \param ctx Entropy context to initialize + */ +void mbedtls_entropy_init(mbedtls_entropy_context *ctx); + +/** + * \brief Free the data in the context + * + * \param ctx Entropy context to free + */ +void mbedtls_entropy_free(mbedtls_entropy_context *ctx); + +/** + * \brief Adds an entropy source to poll + * (Thread-safe if MBEDTLS_THREADING_C is enabled) + * + * \param ctx Entropy context + * \param f_source Entropy function + * \param p_source Function data + * \param threshold Minimum required from source before entropy is released + * ( with mbedtls_entropy_func() ) (in bytes) + * \param strong MBEDTLS_ENTROPY_SOURCE_STRONG or + * MBEDTLS_ENTROPY_SOURCE_WEAK. + * At least one strong source needs to be added. + * Weaker sources (such as the cycle counter) can be used as + * a complement. + * + * \return 0 if successful or MBEDTLS_ERR_ENTROPY_MAX_SOURCES + */ +int mbedtls_entropy_add_source(mbedtls_entropy_context *ctx, + mbedtls_entropy_f_source_ptr f_source, void *p_source, + size_t threshold, int strong); + +/** + * \brief Trigger an extra gather poll for the accumulator + * (Thread-safe if MBEDTLS_THREADING_C is enabled) + * + * \param ctx Entropy context + * + * \return 0 if successful, or MBEDTLS_ERR_ENTROPY_SOURCE_FAILED + */ +int mbedtls_entropy_gather(mbedtls_entropy_context *ctx); + +/** + * \brief Retrieve entropy from the accumulator + * (Maximum length: MBEDTLS_ENTROPY_BLOCK_SIZE) + * (Thread-safe if MBEDTLS_THREADING_C is enabled) + * + * \param data Entropy context + * \param output Buffer to fill + * \param len Number of bytes desired, must be at most MBEDTLS_ENTROPY_BLOCK_SIZE + * + * \return 0 if successful, or MBEDTLS_ERR_ENTROPY_SOURCE_FAILED + */ +int mbedtls_entropy_func(void *data, unsigned char *output, size_t len); + +/** + * \brief Add data to the accumulator manually + * (Thread-safe if MBEDTLS_THREADING_C is enabled) + * + * \param ctx Entropy context + * \param data Data to add + * \param len Length of data + * + * \return 0 if successful + */ +int mbedtls_entropy_update_manual(mbedtls_entropy_context *ctx, + const unsigned char *data, size_t len); + +#if defined(MBEDTLS_ENTROPY_NV_SEED) +/** + * \brief Trigger an update of the seed file in NV by using the + * current entropy pool. + * + * \param ctx Entropy context + * + * \return 0 if successful + */ +int mbedtls_entropy_update_nv_seed(mbedtls_entropy_context *ctx); +#endif /* MBEDTLS_ENTROPY_NV_SEED */ + +#if defined(MBEDTLS_FS_IO) +/** + * \brief Write a seed file + * + * \param ctx Entropy context + * \param path Name of the file + * + * \return 0 if successful, + * MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR on file error, or + * MBEDTLS_ERR_ENTROPY_SOURCE_FAILED + */ +int mbedtls_entropy_write_seed_file(mbedtls_entropy_context *ctx, const char *path); + +/** + * \brief Read and update a seed file. Seed is added to this + * instance. No more than MBEDTLS_ENTROPY_MAX_SEED_SIZE bytes are + * read from the seed file. The rest is ignored. + * + * \param ctx Entropy context + * \param path Name of the file + * + * \return 0 if successful, + * MBEDTLS_ERR_ENTROPY_FILE_IO_ERROR on file error, + * MBEDTLS_ERR_ENTROPY_SOURCE_FAILED + */ +int mbedtls_entropy_update_seed_file(mbedtls_entropy_context *ctx, const char *path); +#endif /* MBEDTLS_FS_IO */ + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief Checkup routine + * + * This module self-test also calls the entropy self-test, + * mbedtls_entropy_source_self_test(); + * + * \return 0 if successful, or 1 if a test failed + */ +int mbedtls_entropy_self_test(int verbose); + +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) +/** + * \brief Checkup routine + * + * Verifies the integrity of the hardware entropy source + * provided by the function 'mbedtls_hardware_poll()'. + * + * Note this is the only hardware entropy source that is known + * at link time, and other entropy sources configured + * dynamically at runtime by the function + * mbedtls_entropy_add_source() will not be tested. + * + * \return 0 if successful, or 1 if a test failed + */ +int mbedtls_entropy_source_self_test(int verbose); +#endif /* MBEDTLS_ENTROPY_HARDWARE_ALT */ +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* entropy.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/error.h b/r5dev/thirdparty/mbedtls/include/mbedtls/error.h new file mode 100644 index 00000000..23a83f2c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/error.h @@ -0,0 +1,212 @@ +/** + * \file error.h + * + * \brief Error to string translation + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_ERROR_H +#define MBEDTLS_ERROR_H + +#include "mbedtls/build_info.h" + +#include + +/** + * Error code layout. + * + * Currently we try to keep all error codes within the negative space of 16 + * bits signed integers to support all platforms (-0x0001 - -0x7FFF). In + * addition we'd like to give two layers of information on the error if + * possible. + * + * For that purpose the error codes are segmented in the following manner: + * + * 16 bit error code bit-segmentation + * + * 1 bit - Unused (sign bit) + * 3 bits - High level module ID + * 5 bits - Module-dependent error code + * 7 bits - Low level module errors + * + * For historical reasons, low-level error codes are divided in even and odd, + * even codes were assigned first, and -1 is reserved for other errors. + * + * Low-level module errors (0x0002-0x007E, 0x0001-0x007F) + * + * Module Nr Codes assigned + * ERROR 2 0x006E 0x0001 + * MPI 7 0x0002-0x0010 + * GCM 3 0x0012-0x0016 0x0013-0x0013 + * THREADING 3 0x001A-0x001E + * AES 5 0x0020-0x0022 0x0021-0x0025 + * CAMELLIA 3 0x0024-0x0026 0x0027-0x0027 + * BASE64 2 0x002A-0x002C + * OID 1 0x002E-0x002E 0x000B-0x000B + * PADLOCK 1 0x0030-0x0030 + * DES 2 0x0032-0x0032 0x0033-0x0033 + * CTR_DBRG 4 0x0034-0x003A + * ENTROPY 3 0x003C-0x0040 0x003D-0x003F + * NET 13 0x0042-0x0052 0x0043-0x0049 + * ARIA 4 0x0058-0x005E + * ASN1 7 0x0060-0x006C + * CMAC 1 0x007A-0x007A + * PBKDF2 1 0x007C-0x007C + * HMAC_DRBG 4 0x0003-0x0009 + * CCM 3 0x000D-0x0011 + * MD5 1 0x002F-0x002F + * RIPEMD160 1 0x0031-0x0031 + * SHA1 1 0x0035-0x0035 0x0073-0x0073 + * SHA256 1 0x0037-0x0037 0x0074-0x0074 + * SHA512 1 0x0039-0x0039 0x0075-0x0075 + * CHACHA20 3 0x0051-0x0055 + * POLY1305 3 0x0057-0x005B + * CHACHAPOLY 2 0x0054-0x0056 + * PLATFORM 2 0x0070-0x0072 + * LMS 5 0x0011-0x0019 + * + * High-level module nr (3 bits - 0x0...-0x7...) + * Name ID Nr of Errors + * PEM 1 9 + * PKCS#12 1 4 (Started from top) + * X509 2 20 + * PKCS5 2 4 (Started from top) + * DHM 3 11 + * PK 3 15 (Started from top) + * RSA 4 11 + * ECP 4 10 (Started from top) + * MD 5 5 + * HKDF 5 1 (Started from top) + * PKCS7 5 12 (Started from 0x5300) + * SSL 5 2 (Started from 0x5F00) + * CIPHER 6 8 (Started from 0x6080) + * SSL 6 22 (Started from top, plus 0x6000) + * SSL 7 20 (Started from 0x7000, gaps at + * 0x7380, 0x7900-0x7980, 0x7A80-0x7E80) + * + * Module dependent error code (5 bits 0x.00.-0x.F8.) + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** Generic error */ +#define MBEDTLS_ERR_ERROR_GENERIC_ERROR -0x0001 +/** This is a bug in the library */ +#define MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED -0x006E + +/** Hardware accelerator failed */ +#define MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED -0x0070 +/** The requested feature is not supported by the platform */ +#define MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED -0x0072 + +/** + * \brief Combines a high-level and low-level error code together. + * + * Wrapper macro for mbedtls_error_add(). See that function for + * more details. + */ +#define MBEDTLS_ERROR_ADD(high, low) \ + mbedtls_error_add(high, low, __FILE__, __LINE__) + +#if defined(MBEDTLS_TEST_HOOKS) +/** + * \brief Testing hook called before adding/combining two error codes together. + * Only used when invasive testing is enabled via MBEDTLS_TEST_HOOKS. + */ +extern void (*mbedtls_test_hook_error_add)(int, int, const char *, int); +#endif + +/** + * \brief Combines a high-level and low-level error code together. + * + * This function can be called directly however it is usually + * called via the #MBEDTLS_ERROR_ADD macro. + * + * While a value of zero is not a negative error code, it is still an + * error code (that denotes success) and can be combined with both a + * negative error code or another value of zero. + * + * \note When invasive testing is enabled via #MBEDTLS_TEST_HOOKS, also try to + * call \link mbedtls_test_hook_error_add \endlink. + * + * \param high high-level error code. See error.h for more details. + * \param low low-level error code. See error.h for more details. + * \param file file where this error code addition occurred. + * \param line line where this error code addition occurred. + */ +static inline int mbedtls_error_add(int high, int low, + const char *file, int line) +{ +#if defined(MBEDTLS_TEST_HOOKS) + if (*mbedtls_test_hook_error_add != NULL) { + (*mbedtls_test_hook_error_add)(high, low, file, line); + } +#endif + (void) file; + (void) line; + + return high + low; +} + +/** + * \brief Translate a mbed TLS error code into a string representation, + * Result is truncated if necessary and always includes a terminating + * null byte. + * + * \param errnum error code + * \param buffer buffer to place representation in + * \param buflen length of the buffer + */ +void mbedtls_strerror(int errnum, char *buffer, size_t buflen); + +/** + * \brief Translate the high-level part of an Mbed TLS error code into a string + * representation. + * + * This function returns a const pointer to an un-modifiable string. The caller + * must not try to modify the string. It is intended to be used mostly for + * logging purposes. + * + * \param error_code error code + * + * \return The string representation of the error code, or \c NULL if the error + * code is unknown. + */ +const char *mbedtls_high_level_strerr(int error_code); + +/** + * \brief Translate the low-level part of an Mbed TLS error code into a string + * representation. + * + * This function returns a const pointer to an un-modifiable string. The caller + * must not try to modify the string. It is intended to be used mostly for + * logging purposes. + * + * \param error_code error code + * + * \return The string representation of the error code, or \c NULL if the error + * code is unknown. + */ +const char *mbedtls_low_level_strerr(int error_code); + +#ifdef __cplusplus +} +#endif + +#endif /* error.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/gcm.h b/r5dev/thirdparty/mbedtls/include/mbedtls/gcm.h new file mode 100644 index 00000000..c3343e6a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/gcm.h @@ -0,0 +1,382 @@ +/** + * \file gcm.h + * + * \brief This file contains GCM definitions and functions. + * + * The Galois/Counter Mode (GCM) for 128-bit block ciphers is defined + * in D. McGrew, J. Viega, The Galois/Counter Mode of Operation + * (GCM), Natl. Inst. Stand. Technol. + * + * For more information on GCM, see NIST SP 800-38D: Recommendation for + * Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC. + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_GCM_H +#define MBEDTLS_GCM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/cipher.h" + +#include + +#define MBEDTLS_GCM_ENCRYPT 1 +#define MBEDTLS_GCM_DECRYPT 0 + +/** Authenticated decryption failed. */ +#define MBEDTLS_ERR_GCM_AUTH_FAILED -0x0012 +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_GCM_BAD_INPUT -0x0014 +/** An output buffer is too small. */ +#define MBEDTLS_ERR_GCM_BUFFER_TOO_SMALL -0x0016 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_GCM_ALT) + +/** + * \brief The GCM context structure. + */ +typedef struct mbedtls_gcm_context { + mbedtls_cipher_context_t MBEDTLS_PRIVATE(cipher_ctx); /*!< The cipher context used. */ + uint64_t MBEDTLS_PRIVATE(HL)[16]; /*!< Precalculated HTable low. */ + uint64_t MBEDTLS_PRIVATE(HH)[16]; /*!< Precalculated HTable high. */ + uint64_t MBEDTLS_PRIVATE(len); /*!< The total length of the encrypted data. */ + uint64_t MBEDTLS_PRIVATE(add_len); /*!< The total length of the additional data. */ + unsigned char MBEDTLS_PRIVATE(base_ectr)[16]; /*!< The first ECTR for tag. */ + unsigned char MBEDTLS_PRIVATE(y)[16]; /*!< The Y working value. */ + unsigned char MBEDTLS_PRIVATE(buf)[16]; /*!< The buf working value. */ + int MBEDTLS_PRIVATE(mode); /*!< The operation to perform: + #MBEDTLS_GCM_ENCRYPT or + #MBEDTLS_GCM_DECRYPT. */ +} +mbedtls_gcm_context; + +#else /* !MBEDTLS_GCM_ALT */ +#include "gcm_alt.h" +#endif /* !MBEDTLS_GCM_ALT */ + +/** + * \brief This function initializes the specified GCM context, + * to make references valid, and prepares the context + * for mbedtls_gcm_setkey() or mbedtls_gcm_free(). + * + * The function does not bind the GCM context to a particular + * cipher, nor set the key. For this purpose, use + * mbedtls_gcm_setkey(). + * + * \param ctx The GCM context to initialize. This must not be \c NULL. + */ +void mbedtls_gcm_init(mbedtls_gcm_context *ctx); + +/** + * \brief This function associates a GCM context with a + * cipher algorithm and a key. + * + * \param ctx The GCM context. This must be initialized. + * \param cipher The 128-bit block cipher to use. + * \param key The encryption key. This must be a readable buffer of at + * least \p keybits bits. + * \param keybits The key size in bits. Valid options are: + *
  • 128 bits
  • + *
  • 192 bits
  • + *
  • 256 bits
+ * + * \return \c 0 on success. + * \return A cipher-specific error code on failure. + */ +int mbedtls_gcm_setkey(mbedtls_gcm_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits); + +/** + * \brief This function performs GCM encryption or decryption of a buffer. + * + * \note For encryption, the output buffer can be the same as the + * input buffer. For decryption, the output buffer cannot be + * the same as input buffer. If the buffers overlap, the output + * buffer must trail at least 8 Bytes behind the input buffer. + * + * \warning When this function performs a decryption, it outputs the + * authentication tag and does not verify that the data is + * authentic. You should use this function to perform encryption + * only. For decryption, use mbedtls_gcm_auth_decrypt() instead. + * + * \param ctx The GCM context to use for encryption or decryption. This + * must be initialized. + * \param mode The operation to perform: + * - #MBEDTLS_GCM_ENCRYPT to perform authenticated encryption. + * The ciphertext is written to \p output and the + * authentication tag is written to \p tag. + * - #MBEDTLS_GCM_DECRYPT to perform decryption. + * The plaintext is written to \p output and the + * authentication tag is written to \p tag. + * Note that this mode is not recommended, because it does + * not verify the authenticity of the data. For this reason, + * you should use mbedtls_gcm_auth_decrypt() instead of + * calling this function in decryption mode. + * \param length The length of the input data, which is equal to the length + * of the output data. + * \param iv The initialization vector. This must be a readable buffer of + * at least \p iv_len Bytes. + * \param iv_len The length of the IV. + * \param add The buffer holding the additional data. This must be of at + * least that size in Bytes. + * \param add_len The length of the additional data. + * \param input The buffer holding the input data. If \p length is greater + * than zero, this must be a readable buffer of at least that + * size in Bytes. + * \param output The buffer for holding the output data. If \p length is greater + * than zero, this must be a writable buffer of at least that + * size in Bytes. + * \param tag_len The length of the tag to generate. + * \param tag The buffer for holding the tag. This must be a writable + * buffer of at least \p tag_len Bytes. + * + * \return \c 0 if the encryption or decryption was performed + * successfully. Note that in #MBEDTLS_GCM_DECRYPT mode, + * this does not indicate that the data is authentic. + * \return #MBEDTLS_ERR_GCM_BAD_INPUT if the lengths or pointers are + * not valid or a cipher-specific error code if the encryption + * or decryption failed. + */ +int mbedtls_gcm_crypt_and_tag(mbedtls_gcm_context *ctx, + int mode, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *input, + unsigned char *output, + size_t tag_len, + unsigned char *tag); + +/** + * \brief This function performs a GCM authenticated decryption of a + * buffer. + * + * \note For decryption, the output buffer cannot be the same as + * input buffer. If the buffers overlap, the output buffer + * must trail at least 8 Bytes behind the input buffer. + * + * \param ctx The GCM context. This must be initialized. + * \param length The length of the ciphertext to decrypt, which is also + * the length of the decrypted plaintext. + * \param iv The initialization vector. This must be a readable buffer + * of at least \p iv_len Bytes. + * \param iv_len The length of the IV. + * \param add The buffer holding the additional data. This must be of at + * least that size in Bytes. + * \param add_len The length of the additional data. + * \param tag The buffer holding the tag to verify. This must be a + * readable buffer of at least \p tag_len Bytes. + * \param tag_len The length of the tag to verify. + * \param input The buffer holding the ciphertext. If \p length is greater + * than zero, this must be a readable buffer of at least that + * size. + * \param output The buffer for holding the decrypted plaintext. If \p length + * is greater than zero, this must be a writable buffer of at + * least that size. + * + * \return \c 0 if successful and authenticated. + * \return #MBEDTLS_ERR_GCM_AUTH_FAILED if the tag does not match. + * \return #MBEDTLS_ERR_GCM_BAD_INPUT if the lengths or pointers are + * not valid or a cipher-specific error code if the decryption + * failed. + */ +int mbedtls_gcm_auth_decrypt(mbedtls_gcm_context *ctx, + size_t length, + const unsigned char *iv, + size_t iv_len, + const unsigned char *add, + size_t add_len, + const unsigned char *tag, + size_t tag_len, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function starts a GCM encryption or decryption + * operation. + * + * \param ctx The GCM context. This must be initialized. + * \param mode The operation to perform: #MBEDTLS_GCM_ENCRYPT or + * #MBEDTLS_GCM_DECRYPT. + * \param iv The initialization vector. This must be a readable buffer of + * at least \p iv_len Bytes. + * \param iv_len The length of the IV. + * + * \return \c 0 on success. + */ +int mbedtls_gcm_starts(mbedtls_gcm_context *ctx, + int mode, + const unsigned char *iv, + size_t iv_len); + +/** + * \brief This function feeds an input buffer as associated data + * (authenticated but not encrypted data) in a GCM + * encryption or decryption operation. + * + * Call this function after mbedtls_gcm_starts() to pass + * the associated data. If the associated data is empty, + * you do not need to call this function. You may not + * call this function after calling mbedtls_cipher_update(). + * + * \param ctx The GCM context. This must have been started with + * mbedtls_gcm_starts() and must not have yet received + * any input with mbedtls_gcm_update(). + * \param add The buffer holding the additional data, or \c NULL + * if \p add_len is \c 0. + * \param add_len The length of the additional data. If \c 0, + * \p add may be \c NULL. + * + * \return \c 0 on success. + */ +int mbedtls_gcm_update_ad(mbedtls_gcm_context *ctx, + const unsigned char *add, + size_t add_len); + +/** + * \brief This function feeds an input buffer into an ongoing GCM + * encryption or decryption operation. + * + * You may call this function zero, one or more times + * to pass successive parts of the input: the plaintext to + * encrypt, or the ciphertext (not including the tag) to + * decrypt. After the last part of the input, call + * mbedtls_gcm_finish(). + * + * This function may produce output in one of the following + * ways: + * - Immediate output: the output length is always equal + * to the input length. + * - Buffered output: the output consists of a whole number + * of 16-byte blocks. If the total input length so far + * (not including associated data) is 16 \* *B* + *A* + * with *A* < 16 then the total output length is 16 \* *B*. + * + * In particular: + * - It is always correct to call this function with + * \p output_size >= \p input_length + 15. + * - If \p input_length is a multiple of 16 for all the calls + * to this function during an operation, then it is + * correct to use \p output_size = \p input_length. + * + * \note For decryption, the output buffer cannot be the same as + * input buffer. If the buffers overlap, the output buffer + * must trail at least 8 Bytes behind the input buffer. + * + * \param ctx The GCM context. This must be initialized. + * \param input The buffer holding the input data. If \p input_length + * is greater than zero, this must be a readable buffer + * of at least \p input_length bytes. + * \param input_length The length of the input data in bytes. + * \param output The buffer for the output data. If \p output_size + * is greater than zero, this must be a writable buffer of + * of at least \p output_size bytes. + * \param output_size The size of the output buffer in bytes. + * See the function description regarding the output size. + * \param output_length On success, \p *output_length contains the actual + * length of the output written in \p output. + * On failure, the content of \p *output_length is + * unspecified. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure: + * total input length too long, + * unsupported input/output buffer overlap detected, + * or \p output_size too small. + */ +int mbedtls_gcm_update(mbedtls_gcm_context *ctx, + const unsigned char *input, size_t input_length, + unsigned char *output, size_t output_size, + size_t *output_length); + +/** + * \brief This function finishes the GCM operation and generates + * the authentication tag. + * + * It wraps up the GCM stream, and generates the + * tag. The tag can have a maximum length of 16 Bytes. + * + * \param ctx The GCM context. This must be initialized. + * \param tag The buffer for holding the tag. This must be a writable + * buffer of at least \p tag_len Bytes. + * \param tag_len The length of the tag to generate. This must be at least + * four. + * \param output The buffer for the final output. + * If \p output_size is nonzero, this must be a writable + * buffer of at least \p output_size bytes. + * \param output_size The size of the \p output buffer in bytes. + * This must be large enough for the output that + * mbedtls_gcm_update() has not produced. In particular: + * - If mbedtls_gcm_update() produces immediate output, + * or if the total input size is a multiple of \c 16, + * then mbedtls_gcm_finish() never produces any output, + * so \p output_size can be \c 0. + * - \p output_size never needs to be more than \c 15. + * \param output_length On success, \p *output_length contains the actual + * length of the output written in \p output. + * On failure, the content of \p *output_length is + * unspecified. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_GCM_BAD_INPUT on failure: + * invalid value of \p tag_len, + * or \p output_size too small. + */ +int mbedtls_gcm_finish(mbedtls_gcm_context *ctx, + unsigned char *output, size_t output_size, + size_t *output_length, + unsigned char *tag, size_t tag_len); + +/** + * \brief This function clears a GCM context and the underlying + * cipher sub-context. + * + * \param ctx The GCM context to clear. If this is \c NULL, the call has + * no effect. Otherwise, this must be initialized. + */ +void mbedtls_gcm_free(mbedtls_gcm_context *ctx); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The GCM checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_gcm_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + + +#endif /* gcm.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/hkdf.h b/r5dev/thirdparty/mbedtls/include/mbedtls/hkdf.h new file mode 100644 index 00000000..699c6d9e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/hkdf.h @@ -0,0 +1,136 @@ +/** + * \file hkdf.h + * + * \brief This file contains the HKDF interface. + * + * The HMAC-based Extract-and-Expand Key Derivation Function (HKDF) is + * specified by RFC 5869. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_HKDF_H +#define MBEDTLS_HKDF_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/md.h" + +/** + * \name HKDF Error codes + * \{ + */ +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_HKDF_BAD_INPUT_DATA -0x5F80 +/** \} name */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief This is the HMAC-based Extract-and-Expand Key Derivation Function + * (HKDF). + * + * \param md A hash function; md.size denotes the length of the hash + * function output in bytes. + * \param salt An optional salt value (a non-secret random value); + * if the salt is not provided, a string of all zeros of + * md.size length is used as the salt. + * \param salt_len The length in bytes of the optional \p salt. + * \param ikm The input keying material. + * \param ikm_len The length in bytes of \p ikm. + * \param info An optional context and application specific information + * string. This can be a zero-length string. + * \param info_len The length of \p info in bytes. + * \param okm The output keying material of \p okm_len bytes. + * \param okm_len The length of the output keying material in bytes. This + * must be less than or equal to 255 * md.size bytes. + * + * \return 0 on success. + * \return #MBEDTLS_ERR_HKDF_BAD_INPUT_DATA when the parameters are invalid. + * \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying + * MD layer. + */ +int mbedtls_hkdf(const mbedtls_md_info_t *md, const unsigned char *salt, + size_t salt_len, const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len); + +/** + * \brief Take the input keying material \p ikm and extract from it a + * fixed-length pseudorandom key \p prk. + * + * \warning This function should only be used if the security of it has been + * studied and established in that particular context (eg. TLS 1.3 + * key schedule). For standard HKDF security guarantees use + * \c mbedtls_hkdf instead. + * + * \param md A hash function; md.size denotes the length of the + * hash function output in bytes. + * \param salt An optional salt value (a non-secret random value); + * if the salt is not provided, a string of all zeros + * of md.size length is used as the salt. + * \param salt_len The length in bytes of the optional \p salt. + * \param ikm The input keying material. + * \param ikm_len The length in bytes of \p ikm. + * \param[out] prk A pseudorandom key of at least md.size bytes. + * + * \return 0 on success. + * \return #MBEDTLS_ERR_HKDF_BAD_INPUT_DATA when the parameters are invalid. + * \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying + * MD layer. + */ +int mbedtls_hkdf_extract(const mbedtls_md_info_t *md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk); + +/** + * \brief Expand the supplied \p prk into several additional pseudorandom + * keys, which is the output of the HKDF. + * + * \warning This function should only be used if the security of it has been + * studied and established in that particular context (eg. TLS 1.3 + * key schedule). For standard HKDF security guarantees use + * \c mbedtls_hkdf instead. + * + * \param md A hash function; md.size denotes the length of the hash + * function output in bytes. + * \param prk A pseudorandom key of at least md.size bytes. \p prk is + * usually the output from the HKDF extract step. + * \param prk_len The length in bytes of \p prk. + * \param info An optional context and application specific information + * string. This can be a zero-length string. + * \param info_len The length of \p info in bytes. + * \param okm The output keying material of \p okm_len bytes. + * \param okm_len The length of the output keying material in bytes. This + * must be less than or equal to 255 * md.size bytes. + * + * \return 0 on success. + * \return #MBEDTLS_ERR_HKDF_BAD_INPUT_DATA when the parameters are invalid. + * \return An MBEDTLS_ERR_MD_* error for errors returned from the underlying + * MD layer. + */ +int mbedtls_hkdf_expand(const mbedtls_md_info_t *md, const unsigned char *prk, + size_t prk_len, const unsigned char *info, + size_t info_len, unsigned char *okm, size_t okm_len); + +#ifdef __cplusplus +} +#endif + +#endif /* hkdf.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h b/r5dev/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h new file mode 100644 index 00000000..4ca6b082 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/hmac_drbg.h @@ -0,0 +1,446 @@ +/** + * \file hmac_drbg.h + * + * \brief The HMAC_DRBG pseudorandom generator. + * + * This module implements the HMAC_DRBG pseudorandom generator described + * in NIST SP 800-90A: Recommendation for Random Number Generation Using + * Deterministic Random Bit Generators. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_HMAC_DRBG_H +#define MBEDTLS_HMAC_DRBG_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/md.h" + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +/* + * Error codes + */ +/** Too many random requested in single call. */ +#define MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG -0x0003 +/** Input too large (Entropy + additional). */ +#define MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG -0x0005 +/** Read/write error in file. */ +#define MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR -0x0007 +/** The entropy source failed. */ +#define MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED -0x0009 + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(MBEDTLS_HMAC_DRBG_RESEED_INTERVAL) +#define MBEDTLS_HMAC_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +#endif + +#if !defined(MBEDTLS_HMAC_DRBG_MAX_INPUT) +#define MBEDTLS_HMAC_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +#endif + +#if !defined(MBEDTLS_HMAC_DRBG_MAX_REQUEST) +#define MBEDTLS_HMAC_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +#endif + +#if !defined(MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT) +#define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ +#endif + +/** \} name SECTION: Module settings */ + +#define MBEDTLS_HMAC_DRBG_PR_OFF 0 /**< No prediction resistance */ +#define MBEDTLS_HMAC_DRBG_PR_ON 1 /**< Prediction resistance enabled */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * HMAC_DRBG context. + */ +typedef struct mbedtls_hmac_drbg_context { + /* Working state: the key K is not stored explicitly, + * but is implied by the HMAC context */ + mbedtls_md_context_t MBEDTLS_PRIVATE(md_ctx); /*!< HMAC context (inc. K) */ + unsigned char MBEDTLS_PRIVATE(V)[MBEDTLS_MD_MAX_SIZE]; /*!< V in the spec */ + int MBEDTLS_PRIVATE(reseed_counter); /*!< reseed counter */ + + /* Administrative state */ + size_t MBEDTLS_PRIVATE(entropy_len); /*!< entropy bytes grabbed on each (re)seed */ + int MBEDTLS_PRIVATE(prediction_resistance); /*!< enable prediction resistance (Automatic + reseed before every random generation) */ + int MBEDTLS_PRIVATE(reseed_interval); /*!< reseed interval */ + + /* Callbacks */ + int(*MBEDTLS_PRIVATE(f_entropy))(void *, unsigned char *, size_t); /*!< entropy function */ + void *MBEDTLS_PRIVATE(p_entropy); /*!< context for the entropy function */ + +#if defined(MBEDTLS_THREADING_C) + /* Invariant: the mutex is initialized if and only if + * md_ctx->md_info != NULL. This means that the mutex is initialized + * during the initial seeding in mbedtls_hmac_drbg_seed() or + * mbedtls_hmac_drbg_seed_buf() and freed in mbedtls_ctr_drbg_free(). + * + * Note that this invariant may change without notice. Do not rely on it + * and do not access the mutex directly in application code. + */ + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); +#endif +} mbedtls_hmac_drbg_context; + +/** + * \brief HMAC_DRBG context initialization. + * + * This function makes the context ready for mbedtls_hmac_drbg_seed(), + * mbedtls_hmac_drbg_seed_buf() or mbedtls_hmac_drbg_free(). + * + * \note The reseed interval is #MBEDTLS_HMAC_DRBG_RESEED_INTERVAL + * by default. Override this value by calling + * mbedtls_hmac_drbg_set_reseed_interval(). + * + * \param ctx HMAC_DRBG context to be initialized. + */ +void mbedtls_hmac_drbg_init(mbedtls_hmac_drbg_context *ctx); + +/** + * \brief HMAC_DRBG initial seeding. + * + * Set the initial seed and set up the entropy source for future reseeds. + * + * A typical choice for the \p f_entropy and \p p_entropy parameters is + * to use the entropy module: + * - \p f_entropy is mbedtls_entropy_func(); + * - \p p_entropy is an instance of ::mbedtls_entropy_context initialized + * with mbedtls_entropy_init() (which registers the platform's default + * entropy sources). + * + * You can provide a personalization string in addition to the + * entropy source, to make this instantiation as unique as possible. + * + * \note By default, the security strength as defined by NIST is: + * - 128 bits if \p md_info is SHA-1; + * - 192 bits if \p md_info is SHA-224; + * - 256 bits if \p md_info is SHA-256, SHA-384 or SHA-512. + * Note that SHA-256 is just as efficient as SHA-224. + * The security strength can be reduced if a smaller + * entropy length is set with + * mbedtls_hmac_drbg_set_entropy_len(). + * + * \note The default entropy length is the security strength + * (converted from bits to bytes). You can override + * it by calling mbedtls_hmac_drbg_set_entropy_len(). + * + * \note During the initial seeding, this function calls + * the entropy source to obtain a nonce + * whose length is half the entropy length. + */ +#if defined(MBEDTLS_THREADING_C) +/** + * \note When Mbed TLS is built with threading support, + * after this function returns successfully, + * it is safe to call mbedtls_hmac_drbg_random() + * from multiple threads. Other operations, including + * reseeding, are not thread-safe. + */ +#endif /* MBEDTLS_THREADING_C */ +/** + * \param ctx HMAC_DRBG context to be seeded. + * \param md_info MD algorithm to use for HMAC_DRBG. + * \param f_entropy The entropy callback, taking as arguments the + * \p p_entropy context, the buffer to fill, and the + * length of the buffer. + * \p f_entropy is always called with a length that is + * less than or equal to the entropy length. + * \param p_entropy The entropy context to pass to \p f_entropy. + * \param custom The personalization string. + * This can be \c NULL, in which case the personalization + * string is empty regardless of the value of \p len. + * \param len The length of the personalization string. + * This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT + * and also at most + * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len * 3 / 2 + * where \p entropy_len is the entropy length + * described above. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA if \p md_info is + * invalid. + * \return #MBEDTLS_ERR_MD_ALLOC_FAILED if there was not enough + * memory to allocate context data. + * \return #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED + * if the call to \p f_entropy failed. + */ +int mbedtls_hmac_drbg_seed(mbedtls_hmac_drbg_context *ctx, + const mbedtls_md_info_t *md_info, + int (*f_entropy)(void *, unsigned char *, size_t), + void *p_entropy, + const unsigned char *custom, + size_t len); + +/** + * \brief Initialisation of simplified HMAC_DRBG (never reseeds). + * + * This function is meant for use in algorithms that need a pseudorandom + * input such as deterministic ECDSA. + */ +#if defined(MBEDTLS_THREADING_C) +/** + * \note When Mbed TLS is built with threading support, + * after this function returns successfully, + * it is safe to call mbedtls_hmac_drbg_random() + * from multiple threads. Other operations, including + * reseeding, are not thread-safe. + */ +#endif /* MBEDTLS_THREADING_C */ +/** + * \param ctx HMAC_DRBG context to be initialised. + * \param md_info MD algorithm to use for HMAC_DRBG. + * \param data Concatenation of the initial entropy string and + * the additional data. + * \param data_len Length of \p data in bytes. + * + * \return \c 0 if successful. or + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA if \p md_info is + * invalid. + * \return #MBEDTLS_ERR_MD_ALLOC_FAILED if there was not enough + * memory to allocate context data. + */ +int mbedtls_hmac_drbg_seed_buf(mbedtls_hmac_drbg_context *ctx, + const mbedtls_md_info_t *md_info, + const unsigned char *data, size_t data_len); + +/** + * \brief This function turns prediction resistance on or off. + * The default value is off. + * + * \note If enabled, entropy is gathered at the beginning of + * every call to mbedtls_hmac_drbg_random_with_add() + * or mbedtls_hmac_drbg_random(). + * Only use this if your entropy source has sufficient + * throughput. + * + * \param ctx The HMAC_DRBG context. + * \param resistance #MBEDTLS_HMAC_DRBG_PR_ON or #MBEDTLS_HMAC_DRBG_PR_OFF. + */ +void mbedtls_hmac_drbg_set_prediction_resistance(mbedtls_hmac_drbg_context *ctx, + int resistance); + +/** + * \brief This function sets the amount of entropy grabbed on each + * seed or reseed. + * + * See the documentation of mbedtls_hmac_drbg_seed() for the default value. + * + * \param ctx The HMAC_DRBG context. + * \param len The amount of entropy to grab, in bytes. + */ +void mbedtls_hmac_drbg_set_entropy_len(mbedtls_hmac_drbg_context *ctx, + size_t len); + +/** + * \brief Set the reseed interval. + * + * The reseed interval is the number of calls to mbedtls_hmac_drbg_random() + * or mbedtls_hmac_drbg_random_with_add() after which the entropy function + * is called again. + * + * The default value is #MBEDTLS_HMAC_DRBG_RESEED_INTERVAL. + * + * \param ctx The HMAC_DRBG context. + * \param interval The reseed interval. + */ +void mbedtls_hmac_drbg_set_reseed_interval(mbedtls_hmac_drbg_context *ctx, + int interval); + +/** + * \brief This function updates the state of the HMAC_DRBG context. + * + * \note This function is not thread-safe. It is not safe + * to call this function if another thread might be + * concurrently obtaining random numbers from the same + * context or updating or reseeding the same context. + * + * \param ctx The HMAC_DRBG context. + * \param additional The data to update the state with. + * If this is \c NULL, there is no additional data. + * \param add_len Length of \p additional in bytes. + * Unused if \p additional is \c NULL. + * + * \return \c 0 on success, or an error from the underlying + * hash calculation. + */ +int mbedtls_hmac_drbg_update(mbedtls_hmac_drbg_context *ctx, + const unsigned char *additional, size_t add_len); + +/** + * \brief This function reseeds the HMAC_DRBG context, that is + * extracts data from the entropy source. + * + * \note This function is not thread-safe. It is not safe + * to call this function if another thread might be + * concurrently obtaining random numbers from the same + * context or updating or reseeding the same context. + * + * \param ctx The HMAC_DRBG context. + * \param additional Additional data to add to the state. + * If this is \c NULL, there is no additional data + * and \p len should be \c 0. + * \param len The length of the additional data. + * This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT + * and also at most + * #MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT - \p entropy_len + * where \p entropy_len is the entropy length + * (see mbedtls_hmac_drbg_set_entropy_len()). + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED + * if a call to the entropy function failed. + */ +int mbedtls_hmac_drbg_reseed(mbedtls_hmac_drbg_context *ctx, + const unsigned char *additional, size_t len); + +/** + * \brief This function updates an HMAC_DRBG instance with additional + * data and uses it to generate random data. + * + * This function automatically reseeds if the reseed counter is exceeded + * or prediction resistance is enabled. + * + * \note This function is not thread-safe. It is not safe + * to call this function if another thread might be + * concurrently obtaining random numbers from the same + * context or updating or reseeding the same context. + * + * \param p_rng The HMAC_DRBG context. This must be a pointer to a + * #mbedtls_hmac_drbg_context structure. + * \param output The buffer to fill. + * \param output_len The length of the buffer in bytes. + * This must be at most #MBEDTLS_HMAC_DRBG_MAX_REQUEST. + * \param additional Additional data to update with. + * If this is \c NULL, there is no additional data + * and \p add_len should be \c 0. + * \param add_len The length of the additional data. + * This must be at most #MBEDTLS_HMAC_DRBG_MAX_INPUT. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED + * if a call to the entropy source failed. + * \return #MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG if + * \p output_len > #MBEDTLS_HMAC_DRBG_MAX_REQUEST. + * \return #MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG if + * \p add_len > #MBEDTLS_HMAC_DRBG_MAX_INPUT. + */ +int mbedtls_hmac_drbg_random_with_add(void *p_rng, + unsigned char *output, size_t output_len, + const unsigned char *additional, + size_t add_len); + +/** + * \brief This function uses HMAC_DRBG to generate random data. + * + * This function automatically reseeds if the reseed counter is exceeded + * or prediction resistance is enabled. + */ +#if defined(MBEDTLS_THREADING_C) +/** + * \note When Mbed TLS is built with threading support, + * it is safe to call mbedtls_ctr_drbg_random() + * from multiple threads. Other operations, including + * reseeding, are not thread-safe. + */ +#endif /* MBEDTLS_THREADING_C */ +/** + * \param p_rng The HMAC_DRBG context. This must be a pointer to a + * #mbedtls_hmac_drbg_context structure. + * \param output The buffer to fill. + * \param out_len The length of the buffer in bytes. + * This must be at most #MBEDTLS_HMAC_DRBG_MAX_REQUEST. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED + * if a call to the entropy source failed. + * \return #MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG if + * \p out_len > #MBEDTLS_HMAC_DRBG_MAX_REQUEST. + */ +int mbedtls_hmac_drbg_random(void *p_rng, unsigned char *output, size_t out_len); + +/** + * \brief This function resets HMAC_DRBG context to the state immediately + * after initial call of mbedtls_hmac_drbg_init(). + * + * \param ctx The HMAC_DRBG context to free. + */ +void mbedtls_hmac_drbg_free(mbedtls_hmac_drbg_context *ctx); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief This function writes a seed file. + * + * \param ctx The HMAC_DRBG context. + * \param path The name of the file. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR on file error. + * \return #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED on reseed + * failure. + */ +int mbedtls_hmac_drbg_write_seed_file(mbedtls_hmac_drbg_context *ctx, const char *path); + +/** + * \brief This function reads and updates a seed file. The seed + * is added to this instance. + * + * \param ctx The HMAC_DRBG context. + * \param path The name of the file. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR on file error. + * \return #MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED on + * reseed failure. + * \return #MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG if the existing + * seed file is too large. + */ +int mbedtls_hmac_drbg_update_seed_file(mbedtls_hmac_drbg_context *ctx, const char *path); +#endif /* MBEDTLS_FS_IO */ + + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The HMAC_DRBG Checkup routine. + * + * \return \c 0 if successful. + * \return \c 1 if the test failed. + */ +int mbedtls_hmac_drbg_self_test(int verbose); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* hmac_drbg.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/legacy_or_psa.h b/r5dev/thirdparty/mbedtls/include/mbedtls/legacy_or_psa.h new file mode 100644 index 00000000..e9bdb778 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/legacy_or_psa.h @@ -0,0 +1,215 @@ +/** + * Macros to express dependencies for code and tests that may use either the + * legacy API or PSA in various builds. This whole header file is currently + * for internal use only and both the header file and the macros it defines + * may change or be removed without notice. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Note: applications that are targeting a specific configuration do not need + * to use these macros; instead they should directly use the functions they + * know are available in their configuration. + * + * Note: code that is purely based on PSA Crypto (psa_xxx() functions) + * does not need to use these macros; instead it should use the relevant + * PSA_WANT_xxx macros. + * + * Note: code that is purely based on the legacy crypto APIs (mbedtls_xxx()) + * does not need to use these macros; instead it should use the relevant + * MBEDTLS_xxx macros. + * + * These macros are for code that wants to use and will do so + * using or PSA depending on , where: + * - will generally be an algorithm (SHA-256, ECDH) but may + * also be a key type (AES, RSA, EC) or domain parameters (elliptic curve); + * - will be either: + * - low-level module API (aes.h, sha256.h), or + * - an abstraction layer (md.h, cipher.h); + * - will be either: + * - depending on what's available in the build: + * legacy API used if available, PSA otherwise + * (this is done to ensure backwards compatibility); or + * - depending on whether MBEDTLS_USE_PSA_CRYPTO is defined. + * + * Examples: + * - TLS 1.2 will compute hashes using either mbedtls_md_xxx() (and + * mbedtls_sha256_xxx()) or psa_aead_xxx() depending on whether + * MBEDTLS_USE_PSA_CRYPTO is defined; + * - RSA PKCS#1 v2.1 will compute hashes (for padding) using either + * `mbedtls_md()` if it's available, or `psa_hash_compute()` otherwise; + * - PEM decoding of PEM-encrypted keys will compute MD5 hashes using either + * `mbedtls_md5_xxx()` if it's available, or `psa_hash_xxx()` otherwise. + * + * Note: the macros are essential to express test dependencies. Inside code, + * we could instead just use the equivalent pre-processor condition, but + * that's not possible in test dependencies where we need a single macro. + * Hopefully, using these macros in code will also help with consistency. + * + * The naming scheme for these macros is: + * MBEDTLS_HAS_feature_VIA_legacy_OR_PSA(_condition) + * where: + * - feature is expressed the same way as in PSA_WANT_xxx macros, for example: + * KEY_TYPE_AES, ALG_SHA_256, ECC_SECP_R1_256; + * - legacy is either LOWLEVEL or the name of the layer: MD, CIPHER; + * - condition is omitted if it's based on availability, else it's + * BASED_ON_USE_PSA. + * + * Coming back to the examples above: + * - TLS 1.2 will determine if it can use SHA-256 using + * MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA + * for the purposes of negotiation, and in test dependencies; + * - RSA PKCS#1 v2.1 tests that used SHA-256 will depend on + * MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA + * - PEM decoding code and its associated tests will depend on + * MBEDTLS_HAS_ALG_MD5_VIA_LOWLEVEL_OR_PSA + * + * Note: every time it's possible to use, say SHA-256, via the MD API, then + * it's also possible to use it via the low-level API. So, code that wants to + * use SHA-256 via both APIs only needs to depend on the MD macro. Also, it + * just so happens that all the code choosing which API to use based on + * MBEDTLS_USE_PSA_CRYPTO (X.509, TLS 1.2/shared), always uses the abstraction + * layer (sometimes in addition to the low-level API), so we don't need the + * MBEDTLS_HAS_feature_VIA_LOWLEVEL_OR_PSA_BASED_ON_USE_PSA macros. + * (PK, while obeying MBEDTLS_USE_PSA_CRYPTO, doesn't compute hashes itself, + * even less makes use of ciphers.) + * + * Note: the macros MBEDTLS_HAS_feature_VIA_LOWLEVEL_OR_PSA are the minimal + * condition for being able to use at all. As such, they should be + * used for guarding data about , such as OIDs or size. For example, + * OID values related to SHA-256 are only useful when SHA-256 can be used at + * least in some way. + */ + +#ifndef MBEDTLS_OR_PSA_HELPERS_H +#define MBEDTLS_OR_PSA_HELPERS_H + +#include "mbedtls/build_info.h" +#if defined(MBEDTLS_PSA_CRYPTO_C) +#include "psa/crypto.h" +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +/* + * Hashes + */ + +/* Hashes using low-level or PSA based on availability */ +#if defined(MBEDTLS_MD5_C) || \ + (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_MD5)) +#define MBEDTLS_HAS_ALG_MD5_VIA_LOWLEVEL_OR_PSA +#endif +#if defined(MBEDTLS_RIPEMD160_C) || \ + (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_RIPEMD160)) +#define MBEDTLS_HAS_ALG_RIPEMD160_VIA_LOWLEVEL_OR_PSA +#endif +#if defined(MBEDTLS_SHA1_C) || \ + (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_1)) +#define MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA +#endif +#if defined(MBEDTLS_SHA224_C) || \ + (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_224)) +#define MBEDTLS_HAS_ALG_SHA_224_VIA_LOWLEVEL_OR_PSA +#endif +#if defined(MBEDTLS_SHA256_C) || \ + (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_256)) +#define MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA +#endif +#if defined(MBEDTLS_SHA384_C) || \ + (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_384)) +#define MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA +#endif +#if defined(MBEDTLS_SHA512_C) || \ + (defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_512)) +#define MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA +#endif + +/* Hashes using MD or PSA based on availability */ +#if (defined(MBEDTLS_MD_C) && defined(MBEDTLS_MD5_C)) || \ + (!defined(MBEDTLS_MD_C) && \ + defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_MD5)) +#define MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA +#endif +#if (defined(MBEDTLS_MD_C) && defined(MBEDTLS_RIPEMD160_C)) || \ + (!defined(MBEDTLS_MD_C) && \ + defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_RIPEMD160)) +#define MBEDTLS_HAS_ALG_RIPEMD160_VIA_MD_OR_PSA +#endif +#if (defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA1_C)) || \ + (!defined(MBEDTLS_MD_C) && \ + defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_1)) +#define MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA +#endif +#if (defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA224_C)) || \ + (!defined(MBEDTLS_MD_C) && \ + defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_224)) +#define MBEDTLS_HAS_ALG_SHA_224_VIA_MD_OR_PSA +#endif +#if (defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA256_C)) || \ + (!defined(MBEDTLS_MD_C) && \ + defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_256)) +#define MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA +#endif +#if (defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA384_C)) || \ + (!defined(MBEDTLS_MD_C) && \ + defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_384)) +#define MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA +#endif +#if (defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA512_C)) || \ + (!defined(MBEDTLS_MD_C) && \ + defined(MBEDTLS_PSA_CRYPTO_C) && defined(PSA_WANT_ALG_SHA_512)) +#define MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA +#endif + +/* Hashes using MD or PSA based on MBEDTLS_USE_PSA_CRYPTO */ +#if (!defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_MD_C) && defined(MBEDTLS_MD5_C)) || \ + (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_ALG_MD5)) +#define MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA +#endif +#if (!defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_MD_C) && defined(MBEDTLS_RIPEMD160_C)) || \ + (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_ALG_RIPEMD160)) +#define MBEDTLS_HAS_ALG_RIPEMD160_VIA_MD_OR_PSA_BASED_ON_USE_PSA +#endif +#if (!defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA1_C)) || \ + (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_ALG_SHA_1)) +#define MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA +#endif +#if (!defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA224_C)) || \ + (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_ALG_SHA_224)) +#define MBEDTLS_HAS_ALG_SHA_224_VIA_MD_OR_PSA_BASED_ON_USE_PSA +#endif +#if (!defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA256_C)) || \ + (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_ALG_SHA_256)) +#define MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA +#endif +#if (!defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA384_C)) || \ + (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_ALG_SHA_384)) +#define MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA +#endif +#if (!defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_MD_C) && defined(MBEDTLS_SHA512_C)) || \ + (defined(MBEDTLS_USE_PSA_CRYPTO) && defined(PSA_WANT_ALG_SHA_512)) +#define MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA +#endif + +#endif /* MBEDTLS_OR_PSA_HELPERS_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/lms.h b/r5dev/thirdparty/mbedtls/include/mbedtls/lms.h new file mode 100644 index 00000000..5c8df42f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/lms.h @@ -0,0 +1,452 @@ +/** + * \file lms.h + * + * \brief This file provides an API for the LMS post-quantum-safe stateful-hash + public-key signature scheme as defined in RFC8554 and NIST.SP.200-208. + * This implementation currently only supports a single parameter set + * MBEDTLS_LMS_SHA256_M32_H10 in order to reduce complexity. This is one + * of the signature schemes recommended by the IETF draft SUIT standard + * for IOT firmware upgrades (RFC9019). + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_LMS_H +#define MBEDTLS_LMS_H + +#include +#include + +#include "mbedtls/private_access.h" +#include "mbedtls/build_info.h" + +#define MBEDTLS_ERR_LMS_BAD_INPUT_DATA -0x0011 /**< Bad data has been input to an LMS function */ +#define MBEDTLS_ERR_LMS_OUT_OF_PRIVATE_KEYS -0x0013 /**< Specified LMS key has utilised all of its private keys */ +#define MBEDTLS_ERR_LMS_VERIFY_FAILED -0x0015 /**< LMS signature verification failed */ +#define MBEDTLS_ERR_LMS_ALLOC_FAILED -0x0017 /**< LMS failed to allocate space for a private key */ +#define MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL -0x0019 /**< Input/output buffer is too small to contain requited data */ + +/* Currently only defined for SHA256, 32 is the max hash output size */ +#define MBEDTLS_LMOTS_N_HASH_LEN_MAX (32u) +#define MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX (34u) +#define MBEDTLS_LMOTS_N_HASH_LEN(type) ((type) == MBEDTLS_LMOTS_SHA256_N32_W8 ? 32u : 0) +#define MBEDTLS_LMOTS_I_KEY_ID_LEN (16u) +#define MBEDTLS_LMOTS_Q_LEAF_ID_LEN (4u) +#define MBEDTLS_LMOTS_TYPE_LEN (4u) +#define MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(type) ((type) == MBEDTLS_LMOTS_SHA256_N32_W8 ? 34u : 0) +#define MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(type) (MBEDTLS_LMOTS_N_HASH_LEN(type)) + +#define MBEDTLS_LMOTS_SIG_LEN(type) (MBEDTLS_LMOTS_TYPE_LEN + \ + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(type) + \ + (MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(type) * \ + MBEDTLS_LMOTS_N_HASH_LEN(type))) + + +#define MBEDTLS_LMS_TYPE_LEN (4) +#define MBEDTLS_LMS_H_TREE_HEIGHT(type) ((type) == MBEDTLS_LMS_SHA256_M32_H10 ? 10u : 0) + +/* The length of a hash output, Currently only implemented for SHA256. + * Max is 32 bytes. + */ +#define MBEDTLS_LMS_M_NODE_BYTES(type) ((type) == MBEDTLS_LMS_SHA256_M32_H10 ? 32 : 0) +#define MBEDTLS_LMS_M_NODE_BYTES_MAX 32 + +#define MBEDTLS_LMS_SIG_LEN(type, otstype) (MBEDTLS_LMOTS_Q_LEAF_ID_LEN + \ + MBEDTLS_LMOTS_SIG_LEN(otstype) + \ + MBEDTLS_LMS_TYPE_LEN + \ + (MBEDTLS_LMS_H_TREE_HEIGHT(type) * \ + MBEDTLS_LMS_M_NODE_BYTES(type))) + +#define MBEDTLS_LMS_PUBLIC_KEY_LEN(type) (MBEDTLS_LMS_TYPE_LEN + \ + MBEDTLS_LMOTS_TYPE_LEN + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN + \ + MBEDTLS_LMS_M_NODE_BYTES(type)) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** The Identifier of the LMS parameter set, as per + * https://www.iana.org/assignments/leighton-micali-signatures/leighton-micali-signatures.xhtml + * We are only implementing a subset of the types, particularly H10, for the sake of simplicity. + */ +typedef enum { + MBEDTLS_LMS_SHA256_M32_H10 = 0x6, +} mbedtls_lms_algorithm_type_t; + +/** The Identifier of the LMOTS parameter set, as per + * https://www.iana.org/assignments/leighton-micali-signatures/leighton-micali-signatures.xhtml. + * We are only implementing a subset of the types, particularly N32_W8, for the sake of simplicity. + */ +typedef enum { + MBEDTLS_LMOTS_SHA256_N32_W8 = 4 +} mbedtls_lmots_algorithm_type_t; + +/** LMOTS parameters structure. + * + * This contains the metadata associated with an LMOTS key, detailing the + * algorithm type, the key ID, and the leaf identifier should be key be part of + * a LMS key. + */ +typedef struct { + unsigned char MBEDTLS_PRIVATE(I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN]); /*!< The key + identifier. */ + unsigned char MBEDTLS_PRIVATE(q_leaf_identifier[MBEDTLS_LMOTS_Q_LEAF_ID_LEN]); /*!< Which + leaf of the LMS key this is. + 0 if the key is not part of an LMS key. */ + mbedtls_lmots_algorithm_type_t MBEDTLS_PRIVATE(type); /*!< The LM-OTS key type identifier as + per IANA. Only SHA256_N32_W8 is + currently supported. */ +} mbedtls_lmots_parameters_t; + +/** LMOTS public context structure. + * + * A LMOTS public key is a hash output, and the applicable parameter set. + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lmots_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PUBLIC_KEY -> INIT [label="free"]; + * INIT -> HAVE_PUBLIC_KEY [label="import_public_key"]; + * INIT -> HAVE_PUBLIC_KEY [label="calculate_public_key from private key"]; + * HAVE_PUBLIC_KEY -> HAVE_PUBLIC_KEY [label="export_public_key"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lmots_parameters_t MBEDTLS_PRIVATE(params); + unsigned char MBEDTLS_PRIVATE(public_key)[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char MBEDTLS_PRIVATE(have_public_key); /*!< Whether the context contains a public key. + Boolean values only. */ +} mbedtls_lmots_public_t; + +#if defined(MBEDTLS_LMS_PRIVATE) +/** LMOTS private context structure. + * + * A LMOTS private key is one hash output for each of digit of the digest + + * checksum, and the applicable parameter set. + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lmots_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PRIVATE_KEY -> INIT [label="free"]; + * INIT -> HAVE_PRIVATE_KEY [label="generate_private_key"]; + * HAVE_PRIVATE_KEY -> INIT [label="sign"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lmots_parameters_t MBEDTLS_PRIVATE(params); + unsigned char MBEDTLS_PRIVATE(private_key)[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][ + MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char MBEDTLS_PRIVATE(have_private_key); /*!< Whether the context contains a private key. + Boolean values only. */ +} mbedtls_lmots_private_t; +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + + +/** LMS parameters structure. + * + * This contains the metadata associated with an LMS key, detailing the + * algorithm type, the type of the underlying OTS algorithm, and the key ID. + */ +typedef struct { + unsigned char MBEDTLS_PRIVATE(I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN]); /*!< The key + identifier. */ + mbedtls_lmots_algorithm_type_t MBEDTLS_PRIVATE(otstype); /*!< The LM-OTS key type identifier as + per IANA. Only SHA256_N32_W8 is + currently supported. */ + mbedtls_lms_algorithm_type_t MBEDTLS_PRIVATE(type); /*!< The LMS key type identifier as per + IANA. Only SHA256_M32_H10 is currently + supported. */ +} mbedtls_lms_parameters_t; + +/** LMS public context structure. + * + * A LMS public key is the hash output that is the root of the Merkle tree, and + * the applicable parameter set + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lms_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PUBLIC_KEY -> INIT [label="free"]; + * INIT -> HAVE_PUBLIC_KEY [label="import_public_key"]; + * INIT -> HAVE_PUBLIC_KEY [label="calculate_public_key from private key"]; + * HAVE_PUBLIC_KEY -> HAVE_PUBLIC_KEY [label="export_public_key"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lms_parameters_t MBEDTLS_PRIVATE(params); + unsigned char MBEDTLS_PRIVATE(T_1_pub_key)[MBEDTLS_LMS_M_NODE_BYTES_MAX]; /*!< The public key, in + the form of the Merkle tree root node. */ + unsigned char MBEDTLS_PRIVATE(have_public_key); /*!< Whether the context contains a public key. + Boolean values only. */ +} mbedtls_lms_public_t; + + +#if defined(MBEDTLS_LMS_PRIVATE) +/** LMS private context structure. + * + * A LMS private key is a set of LMOTS private keys, an index to the next usable + * key, and the applicable parameter set. + * + * The context must be initialized before it is used. A public key must either + * be imported or generated from a private context. + * + * \dot + * digraph lms_public_t { + * UNINITIALIZED -> INIT [label="init"]; + * HAVE_PRIVATE_KEY -> INIT [label="free"]; + * INIT -> HAVE_PRIVATE_KEY [label="generate_private_key"]; + * } + * \enddot + */ +typedef struct { + mbedtls_lms_parameters_t MBEDTLS_PRIVATE(params); + uint32_t MBEDTLS_PRIVATE(q_next_usable_key); /*!< The index of the next OTS key that has not + been used. */ + mbedtls_lmots_private_t *MBEDTLS_PRIVATE(ots_private_keys); /*!< The private key material. One OTS key + for each leaf node in the Merkle tree. NULL + when have_private_key is 0 and non-NULL otherwise. + is 2^MBEDTLS_LMS_H_TREE_HEIGHT(type) in length. */ + mbedtls_lmots_public_t *MBEDTLS_PRIVATE(ots_public_keys); /*!< The OTS key public keys, used to + build the Merkle tree. NULL + when have_private_key is 0 and + non-NULL otherwise. + Is 2^MBEDTLS_LMS_H_TREE_HEIGHT(type) + in length. */ + unsigned char MBEDTLS_PRIVATE(have_private_key); /*!< Whether the context contains a private key. + Boolean values only. */ +} mbedtls_lms_private_t; +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + +/** + * \brief This function initializes an LMS public context + * + * \param ctx The uninitialized LMS context that will then be + * initialized. + */ +void mbedtls_lms_public_init(mbedtls_lms_public_t *ctx); + +/** + * \brief This function uninitializes an LMS public context + * + * \param ctx The initialized LMS context that will then be + * uninitialized. + */ +void mbedtls_lms_public_free(mbedtls_lms_public_t *ctx); + +/** + * \brief This function imports an LMS public key into a + * public LMS context. + * + * \note Before this function is called, the context must + * have been initialized. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMS context store the key in. + * \param key The buffer from which the key will be read. + * #MBEDTLS_LMS_PUBLIC_KEY_LEN bytes will be read from + * this. + * \param key_size The size of the key being imported. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_import_public_key(mbedtls_lms_public_t *ctx, + const unsigned char *key, size_t key_size); + +/** + * \brief This function exports an LMS public key from a + * LMS public context that already contains a public + * key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a public key. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMS public context that contains + * the public key. + * \param key The buffer into which the key will be output. Must + * be at least #MBEDTLS_LMS_PUBLIC_KEY_LEN in size. + * \param key_size The size of the key buffer. + * \param key_len If not NULL, will be written with the size of the + * key. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_export_public_key(const mbedtls_lms_public_t *ctx, + unsigned char *key, size_t key_size, + size_t *key_len); + +/** + * \brief This function verifies a LMS signature, using a + * LMS context that contains a public key. + * + * \note Before this function is called, the context must + * have been initialized and must contain a public key + * (either by import or generation). + * + * \param ctx The initialized LMS public context from which the + * public key will be read. + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf from which the signature will be read. + * #MBEDTLS_LMS_SIG_LEN bytes will be read from + * this. + * \param sig_size The size of the signature to be verified. + * + * \return \c 0 on successful verification. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_verify(const mbedtls_lms_public_t *ctx, + const unsigned char *msg, size_t msg_size, + const unsigned char *sig, size_t sig_size); + +#if defined(MBEDTLS_LMS_PRIVATE) +/** + * \brief This function initializes an LMS private context + * + * \param ctx The uninitialized LMS private context that will + * then be initialized. */ +void mbedtls_lms_private_init(mbedtls_lms_private_t *ctx); + +/** + * \brief This function uninitializes an LMS private context + * + * \param ctx The initialized LMS private context that will then + * be uninitialized. + */ +void mbedtls_lms_private_free(mbedtls_lms_private_t *ctx); + +/** + * \brief This function generates an LMS private key, and + * stores in into an LMS private context. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note The seed must have at least 256 bits of entropy. + * + * \param ctx The initialized LMOTS context to generate the key + * into. + * \param type The LMS parameter set identifier. + * \param otstype The LMOTS parameter set identifier. + * \param f_rng The RNG function to be used to generate the key ID. + * \param p_rng The RNG context to be passed to f_rng + * \param seed The seed used to deterministically generate the + * key. + * \param seed_size The length of the seed. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_generate_private_key(mbedtls_lms_private_t *ctx, + mbedtls_lms_algorithm_type_t type, + mbedtls_lmots_algorithm_type_t otstype, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *seed, + size_t seed_size); + +/** + * \brief This function calculates an LMS public key from a + * LMS context that already contains a private key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a private key. + * + * \param ctx The initialized LMS public context to calculate the key + * from and store it into. + * + * \param priv_ctx The LMS private context to read the private key + * from. This must have been initialized and contain a + * private key. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_calculate_public_key(mbedtls_lms_public_t *ctx, + const mbedtls_lms_private_t *priv_ctx); + +/** + * \brief This function creates a LMS signature, using a + * LMS context that contains unused private keys. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note Before this function is called, the context must + * have been initialized and must contain a private + * key. + * + * \note Each of the LMOTS private keys inside a LMS private + * key can only be used once. If they are reused, then + * attackers may be able to forge signatures with that + * key. This is all handled transparently, but it is + * important to not perform copy operations on LMS + * contexts that contain private key material. + * + * \param ctx The initialized LMS private context from which the + * private key will be read. + * \param f_rng The RNG function to be used for signature + * generation. + * \param p_rng The RNG context to be passed to f_rng + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf into which the signature will be stored. + * Must be at least #MBEDTLS_LMS_SIG_LEN in size. + * \param sig_size The size of the buffer the signature will be + * written into. + * \param sig_len If not NULL, will be written with the size of the + * signature. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lms_sign(mbedtls_lms_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *msg, + unsigned int msg_size, unsigned char *sig, size_t sig_size, + size_t *sig_len); +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_LMS_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/mbedtls_config.h b/r5dev/thirdparty/mbedtls/include/mbedtls/mbedtls_config.h new file mode 100644 index 00000000..bcf68521 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/mbedtls_config.h @@ -0,0 +1,3920 @@ +/** + * \file mbedtls_config.h + * + * \brief Configuration options (set of defines) + * + * This set of compile-time options may be used to enable + * or disable features selectively, and reduce the global + * memory footprint. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This is an optional version symbol that enables compatibility handling of + * config files. + * + * It is equal to the #MBEDTLS_VERSION_NUMBER of the Mbed TLS version that + * introduced the config format we want to be compatible with. + */ +//#define MBEDTLS_CONFIG_VERSION 0x03000000 + +/** + * \name SECTION: System support + * + * This section sets system specific settings. + * \{ + */ + +/** + * \def MBEDTLS_HAVE_ASM + * + * The compiler has support for asm(). + * + * Requires support for asm() in compiler. + * + * Used in: + * library/aesni.h + * library/aria.c + * library/bn_mul.h + * library/constant_time.c + * library/padlock.h + * + * Required by: + * MBEDTLS_AESCE_C + * MBEDTLS_AESNI_C (on some platforms) + * MBEDTLS_PADLOCK_C + * + * Comment to disable the use of assembly code. + */ +#define MBEDTLS_HAVE_ASM + +/** + * \def MBEDTLS_NO_UDBL_DIVISION + * + * The platform lacks support for double-width integer division (64-bit + * division on a 32-bit platform, 128-bit division on a 64-bit platform). + * + * Used in: + * include/mbedtls/bignum.h + * library/bignum.c + * + * The bignum code uses double-width division to speed up some operations. + * Double-width division is often implemented in software that needs to + * be linked with the program. The presence of a double-width integer + * type is usually detected automatically through preprocessor macros, + * but the automatic detection cannot know whether the code needs to + * and can be linked with an implementation of division for that type. + * By default division is assumed to be usable if the type is present. + * Uncomment this option to prevent the use of double-width division. + * + * Note that division for the native integer type is always required. + * Furthermore, a 64-bit type is always required even on a 32-bit + * platform, but it need not support multiplication or division. In some + * cases it is also desirable to disable some double-width operations. For + * example, if double-width division is implemented in software, disabling + * it can reduce code size in some embedded targets. + */ +//#define MBEDTLS_NO_UDBL_DIVISION + +/** + * \def MBEDTLS_NO_64BIT_MULTIPLICATION + * + * The platform lacks support for 32x32 -> 64-bit multiplication. + * + * Used in: + * library/poly1305.c + * + * Some parts of the library may use multiplication of two unsigned 32-bit + * operands with a 64-bit result in order to speed up computations. On some + * platforms, this is not available in hardware and has to be implemented in + * software, usually in a library provided by the toolchain. + * + * Sometimes it is not desirable to have to link to that library. This option + * removes the dependency of that library on platforms that lack a hardware + * 64-bit multiplier by embedding a software implementation in Mbed TLS. + * + * Note that depending on the compiler, this may decrease performance compared + * to using the library function provided by the toolchain. + */ +//#define MBEDTLS_NO_64BIT_MULTIPLICATION + +/** + * \def MBEDTLS_HAVE_SSE2 + * + * CPU supports SSE2 instruction set. + * + * Uncomment if the CPU supports SSE2 (IA-32 specific). + */ +//#define MBEDTLS_HAVE_SSE2 + +/** + * \def MBEDTLS_HAVE_TIME + * + * System has time.h and time(). + * The time does not need to be correct, only time differences are used, + * by contrast with MBEDTLS_HAVE_TIME_DATE + * + * Defining MBEDTLS_HAVE_TIME allows you to specify MBEDTLS_PLATFORM_TIME_ALT, + * MBEDTLS_PLATFORM_TIME_MACRO, MBEDTLS_PLATFORM_TIME_TYPE_MACRO and + * MBEDTLS_PLATFORM_STD_TIME. + * + * Comment if your system does not support time functions. + * + * \note If MBEDTLS_TIMING_C is set - to enable the semi-portable timing + * interface - timing.c will include time.h on suitable platforms + * regardless of the setting of MBEDTLS_HAVE_TIME, unless + * MBEDTLS_TIMING_ALT is used. See timing.c for more information. + */ +#define MBEDTLS_HAVE_TIME + +/** + * \def MBEDTLS_HAVE_TIME_DATE + * + * System has time.h, time(), and an implementation for + * mbedtls_platform_gmtime_r() (see below). + * The time needs to be correct (not necessarily very accurate, but at least + * the date should be correct). This is used to verify the validity period of + * X.509 certificates. + * + * Comment if your system does not have a correct clock. + * + * \note mbedtls_platform_gmtime_r() is an abstraction in platform_util.h that + * behaves similarly to the gmtime_r() function from the C standard. Refer to + * the documentation for mbedtls_platform_gmtime_r() for more information. + * + * \note It is possible to configure an implementation for + * mbedtls_platform_gmtime_r() at compile-time by using the macro + * MBEDTLS_PLATFORM_GMTIME_R_ALT. + */ +#define MBEDTLS_HAVE_TIME_DATE + +/** + * \def MBEDTLS_PLATFORM_MEMORY + * + * Enable the memory allocation layer. + * + * By default mbed TLS uses the system-provided calloc() and free(). + * This allows different allocators (self-implemented or provided) to be + * provided to the platform abstraction layer. + * + * Enabling MBEDTLS_PLATFORM_MEMORY without the + * MBEDTLS_PLATFORM_{FREE,CALLOC}_MACROs will provide + * "mbedtls_platform_set_calloc_free()" allowing you to set an alternative calloc() and + * free() function pointer at runtime. + * + * Enabling MBEDTLS_PLATFORM_MEMORY and specifying + * MBEDTLS_PLATFORM_{CALLOC,FREE}_MACROs will allow you to specify the + * alternate function at compile time. + * + * Requires: MBEDTLS_PLATFORM_C + * + * Enable this layer to allow use of alternative memory allocators. + */ +//#define MBEDTLS_PLATFORM_MEMORY + +/** + * \def MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + * + * Do not assign standard functions in the platform layer (e.g. calloc() to + * MBEDTLS_PLATFORM_STD_CALLOC and printf() to MBEDTLS_PLATFORM_STD_PRINTF) + * + * This makes sure there are no linking errors on platforms that do not support + * these functions. You will HAVE to provide alternatives, either at runtime + * via the platform_set_xxx() functions or at compile time by setting + * the MBEDTLS_PLATFORM_STD_XXX defines, or enabling a + * MBEDTLS_PLATFORM_XXX_MACRO. + * + * Requires: MBEDTLS_PLATFORM_C + * + * Uncomment to prevent default assignment of standard functions in the + * platform layer. + */ +//#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS + +/** + * \def MBEDTLS_PLATFORM_EXIT_ALT + * + * MBEDTLS_PLATFORM_XXX_ALT: Uncomment a macro to let mbed TLS support the + * function in the platform abstraction layer. + * + * Example: In case you uncomment MBEDTLS_PLATFORM_PRINTF_ALT, mbed TLS will + * provide a function "mbedtls_platform_set_printf()" that allows you to set an + * alternative printf function pointer. + * + * All these define require MBEDTLS_PLATFORM_C to be defined! + * + * \note MBEDTLS_PLATFORM_SNPRINTF_ALT is required on Windows; + * it will be enabled automatically by check_config.h + * + * \warning MBEDTLS_PLATFORM_XXX_ALT cannot be defined at the same time as + * MBEDTLS_PLATFORM_XXX_MACRO! + * + * Requires: MBEDTLS_PLATFORM_TIME_ALT requires MBEDTLS_HAVE_TIME + * + * Uncomment a macro to enable alternate implementation of specific base + * platform function + */ +//#define MBEDTLS_PLATFORM_SETBUF_ALT +//#define MBEDTLS_PLATFORM_EXIT_ALT +//#define MBEDTLS_PLATFORM_TIME_ALT +//#define MBEDTLS_PLATFORM_FPRINTF_ALT +//#define MBEDTLS_PLATFORM_PRINTF_ALT +//#define MBEDTLS_PLATFORM_SNPRINTF_ALT +//#define MBEDTLS_PLATFORM_VSNPRINTF_ALT +//#define MBEDTLS_PLATFORM_NV_SEED_ALT +//#define MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT + +/** + * \def MBEDTLS_DEPRECATED_WARNING + * + * Mark deprecated functions and features so that they generate a warning if + * used. Functionality deprecated in one version will usually be removed in the + * next version. You can enable this to help you prepare the transition to a + * new major version by making sure your code is not using this functionality. + * + * This only works with GCC and Clang. With other compilers, you may want to + * use MBEDTLS_DEPRECATED_REMOVED + * + * Uncomment to get warnings on using deprecated functions and features. + */ +//#define MBEDTLS_DEPRECATED_WARNING + +/** + * \def MBEDTLS_DEPRECATED_REMOVED + * + * Remove deprecated functions and features so that they generate an error if + * used. Functionality deprecated in one version will usually be removed in the + * next version. You can enable this to help you prepare the transition to a + * new major version by making sure your code is not using this functionality. + * + * Uncomment to get errors on using deprecated functions and features. + */ +//#define MBEDTLS_DEPRECATED_REMOVED + +/** \} name SECTION: System support */ + +/** + * \name SECTION: mbed TLS feature support + * + * This section sets support for features that are or are not needed + * within the modules that are enabled. + * \{ + */ + +/** + * \def MBEDTLS_TIMING_ALT + * + * Uncomment to provide your own alternate implementation for + * mbedtls_timing_get_timer(), mbedtls_set_alarm(), mbedtls_set/get_delay() + * + * Only works if you have MBEDTLS_TIMING_C enabled. + * + * You will need to provide a header "timing_alt.h" and an implementation at + * compile time. + */ +//#define MBEDTLS_TIMING_ALT + +/** + * \def MBEDTLS_AES_ALT + * + * MBEDTLS__MODULE_NAME__ALT: Uncomment a macro to let mbed TLS use your + * alternate core implementation of a symmetric crypto, an arithmetic or hash + * module (e.g. platform specific assembly optimized implementations). Keep + * in mind that the function prototypes should remain the same. + * + * This replaces the whole module. If you only want to replace one of the + * functions, use one of the MBEDTLS__FUNCTION_NAME__ALT flags. + * + * Example: In case you uncomment MBEDTLS_AES_ALT, mbed TLS will no longer + * provide the "struct mbedtls_aes_context" definition and omit the base + * function declarations and implementations. "aes_alt.h" will be included from + * "aes.h" to include the new function definitions. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * module. + * + * \warning MD5, DES and SHA-1 are considered weak and their + * use constitutes a security risk. If possible, we recommend + * avoiding dependencies on them, and considering stronger message + * digests and ciphers instead. + * + */ +//#define MBEDTLS_AES_ALT +//#define MBEDTLS_ARIA_ALT +//#define MBEDTLS_CAMELLIA_ALT +//#define MBEDTLS_CCM_ALT +//#define MBEDTLS_CHACHA20_ALT +//#define MBEDTLS_CHACHAPOLY_ALT +//#define MBEDTLS_CMAC_ALT +//#define MBEDTLS_DES_ALT +//#define MBEDTLS_DHM_ALT +//#define MBEDTLS_ECJPAKE_ALT +//#define MBEDTLS_GCM_ALT +//#define MBEDTLS_NIST_KW_ALT +//#define MBEDTLS_MD5_ALT +//#define MBEDTLS_POLY1305_ALT +//#define MBEDTLS_RIPEMD160_ALT +//#define MBEDTLS_RSA_ALT +//#define MBEDTLS_SHA1_ALT +//#define MBEDTLS_SHA256_ALT +//#define MBEDTLS_SHA512_ALT + +/* + * When replacing the elliptic curve module, please consider, that it is + * implemented with two .c files: + * - ecp.c + * - ecp_curves.c + * You can replace them very much like all the other MBEDTLS__MODULE_NAME__ALT + * macros as described above. The only difference is that you have to make sure + * that you provide functionality for both .c files. + */ +//#define MBEDTLS_ECP_ALT + +/** + * \def MBEDTLS_SHA256_PROCESS_ALT + * + * MBEDTLS__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use you + * alternate core implementation of symmetric crypto or hash function. Keep in + * mind that function prototypes should remain the same. + * + * This replaces only one function. The header file from mbed TLS is still + * used, in contrast to the MBEDTLS__MODULE_NAME__ALT flags. + * + * Example: In case you uncomment MBEDTLS_SHA256_PROCESS_ALT, mbed TLS will + * no longer provide the mbedtls_sha1_process() function, but it will still provide + * the other function (using your mbedtls_sha1_process() function) and the definition + * of mbedtls_sha1_context, so your implementation of mbedtls_sha1_process must be compatible + * with this definition. + * + * \note If you use the AES_xxx_ALT macros, then it is recommended to also set + * MBEDTLS_AES_ROM_TABLES in order to help the linker garbage-collect the AES + * tables. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * function. + * + * \warning MD5, DES and SHA-1 are considered weak and their use + * constitutes a security risk. If possible, we recommend avoiding + * dependencies on them, and considering stronger message digests + * and ciphers instead. + * + * \warning If both MBEDTLS_ECDSA_SIGN_ALT and MBEDTLS_ECDSA_DETERMINISTIC are + * enabled, then the deterministic ECDH signature functions pass the + * the static HMAC-DRBG as RNG to mbedtls_ecdsa_sign(). Therefore + * alternative implementations should use the RNG only for generating + * the ephemeral key and nothing else. If this is not possible, then + * MBEDTLS_ECDSA_DETERMINISTIC should be disabled and an alternative + * implementation should be provided for mbedtls_ecdsa_sign_det_ext(). + * + */ +//#define MBEDTLS_MD5_PROCESS_ALT +//#define MBEDTLS_RIPEMD160_PROCESS_ALT +//#define MBEDTLS_SHA1_PROCESS_ALT +//#define MBEDTLS_SHA256_PROCESS_ALT +//#define MBEDTLS_SHA512_PROCESS_ALT +//#define MBEDTLS_DES_SETKEY_ALT +//#define MBEDTLS_DES_CRYPT_ECB_ALT +//#define MBEDTLS_DES3_CRYPT_ECB_ALT +//#define MBEDTLS_AES_SETKEY_ENC_ALT +//#define MBEDTLS_AES_SETKEY_DEC_ALT +//#define MBEDTLS_AES_ENCRYPT_ALT +//#define MBEDTLS_AES_DECRYPT_ALT +//#define MBEDTLS_ECDH_GEN_PUBLIC_ALT +//#define MBEDTLS_ECDH_COMPUTE_SHARED_ALT +//#define MBEDTLS_ECDSA_VERIFY_ALT +//#define MBEDTLS_ECDSA_SIGN_ALT +//#define MBEDTLS_ECDSA_GENKEY_ALT + +/** + * \def MBEDTLS_ECP_INTERNAL_ALT + * + * Expose a part of the internal interface of the Elliptic Curve Point module. + * + * MBEDTLS_ECP__FUNCTION_NAME__ALT: Uncomment a macro to let mbed TLS use your + * alternative core implementation of elliptic curve arithmetic. Keep in mind + * that function prototypes should remain the same. + * + * This partially replaces one function. The header file from mbed TLS is still + * used, in contrast to the MBEDTLS_ECP_ALT flag. The original implementation + * is still present and it is used for group structures not supported by the + * alternative. + * + * The original implementation can in addition be removed by setting the + * MBEDTLS_ECP_NO_FALLBACK option, in which case any function for which the + * corresponding MBEDTLS_ECP__FUNCTION_NAME__ALT macro is defined will not be + * able to fallback to curves not supported by the alternative implementation. + * + * Any of these options become available by defining MBEDTLS_ECP_INTERNAL_ALT + * and implementing the following functions: + * unsigned char mbedtls_internal_ecp_grp_capable( + * const mbedtls_ecp_group *grp ) + * int mbedtls_internal_ecp_init( const mbedtls_ecp_group *grp ) + * void mbedtls_internal_ecp_free( const mbedtls_ecp_group *grp ) + * The mbedtls_internal_ecp_grp_capable function should return 1 if the + * replacement functions implement arithmetic for the given group and 0 + * otherwise. + * The functions mbedtls_internal_ecp_init and mbedtls_internal_ecp_free are + * called before and after each point operation and provide an opportunity to + * implement optimized set up and tear down instructions. + * + * Example: In case you set MBEDTLS_ECP_INTERNAL_ALT and + * MBEDTLS_ECP_DOUBLE_JAC_ALT, mbed TLS will still provide the ecp_double_jac() + * function, but will use your mbedtls_internal_ecp_double_jac() if the group + * for the operation is supported by your implementation (i.e. your + * mbedtls_internal_ecp_grp_capable() function returns 1 for this group). If the + * group is not supported by your implementation, then the original mbed TLS + * implementation of ecp_double_jac() is used instead, unless this fallback + * behaviour is disabled by setting MBEDTLS_ECP_NO_FALLBACK (in which case + * ecp_double_jac() will return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE). + * + * The function prototypes and the definition of mbedtls_ecp_group and + * mbedtls_ecp_point will not change based on MBEDTLS_ECP_INTERNAL_ALT, so your + * implementation of mbedtls_internal_ecp__function_name__ must be compatible + * with their definitions. + * + * Uncomment a macro to enable alternate implementation of the corresponding + * function. + */ +/* Required for all the functions in this section */ +//#define MBEDTLS_ECP_INTERNAL_ALT +/* Turn off software fallback for curves not supported in hardware */ +//#define MBEDTLS_ECP_NO_FALLBACK +/* Support for Weierstrass curves with Jacobi representation */ +//#define MBEDTLS_ECP_RANDOMIZE_JAC_ALT +//#define MBEDTLS_ECP_ADD_MIXED_ALT +//#define MBEDTLS_ECP_DOUBLE_JAC_ALT +//#define MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT +//#define MBEDTLS_ECP_NORMALIZE_JAC_ALT +/* Support for curves with Montgomery arithmetic */ +//#define MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT +//#define MBEDTLS_ECP_RANDOMIZE_MXZ_ALT +//#define MBEDTLS_ECP_NORMALIZE_MXZ_ALT + +/** + * \def MBEDTLS_ENTROPY_HARDWARE_ALT + * + * Uncomment this macro to let mbed TLS use your own implementation of a + * hardware entropy collector. + * + * Your function must be called \c mbedtls_hardware_poll(), have the same + * prototype as declared in library/entropy_poll.h, and accept NULL as first + * argument. + * + * Uncomment to use your own hardware entropy collector. + */ +//#define MBEDTLS_ENTROPY_HARDWARE_ALT + +/** + * \def MBEDTLS_AES_ROM_TABLES + * + * Use precomputed AES tables stored in ROM. + * + * Uncomment this macro to use precomputed AES tables stored in ROM. + * Comment this macro to generate AES tables in RAM at runtime. + * + * Tradeoff: Using precomputed ROM tables reduces RAM usage by ~8kb + * (or ~2kb if \c MBEDTLS_AES_FEWER_TABLES is used) and reduces the + * initialization time before the first AES operation can be performed. + * It comes at the cost of additional ~8kb ROM use (resp. ~2kb if \c + * MBEDTLS_AES_FEWER_TABLES below is used), and potentially degraded + * performance if ROM access is slower than RAM access. + * + * This option is independent of \c MBEDTLS_AES_FEWER_TABLES. + * + */ +//#define MBEDTLS_AES_ROM_TABLES + +/** + * \def MBEDTLS_AES_FEWER_TABLES + * + * Use less ROM/RAM for AES tables. + * + * Uncommenting this macro omits 75% of the AES tables from + * ROM / RAM (depending on the value of \c MBEDTLS_AES_ROM_TABLES) + * by computing their values on the fly during operations + * (the tables are entry-wise rotations of one another). + * + * Tradeoff: Uncommenting this reduces the RAM / ROM footprint + * by ~6kb but at the cost of more arithmetic operations during + * runtime. Specifically, one has to compare 4 accesses within + * different tables to 4 accesses with additional arithmetic + * operations within the same table. The performance gain/loss + * depends on the system and memory details. + * + * This option is independent of \c MBEDTLS_AES_ROM_TABLES. + * + */ +//#define MBEDTLS_AES_FEWER_TABLES + +/** + * \def MBEDTLS_CAMELLIA_SMALL_MEMORY + * + * Use less ROM for the Camellia implementation (saves about 768 bytes). + * + * Uncomment this macro to use less memory for Camellia. + */ +//#define MBEDTLS_CAMELLIA_SMALL_MEMORY + +/** + * \def MBEDTLS_CHECK_RETURN_WARNING + * + * If this macro is defined, emit a compile-time warning if application code + * calls a function without checking its return value, but the return value + * should generally be checked in portable applications. + * + * This is only supported on platforms where #MBEDTLS_CHECK_RETURN is + * implemented. Otherwise this option has no effect. + * + * Uncomment to get warnings on using fallible functions without checking + * their return value. + * + * \note This feature is a work in progress. + * Warnings will be added to more functions in the future. + * + * \note A few functions are considered critical, and ignoring the return + * value of these functions will trigger a warning even if this + * macro is not defined. To completely disable return value check + * warnings, define #MBEDTLS_CHECK_RETURN with an empty expansion. + */ +//#define MBEDTLS_CHECK_RETURN_WARNING + +/** + * \def MBEDTLS_CIPHER_MODE_CBC + * + * Enable Cipher Block Chaining mode (CBC) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CBC + +/** + * \def MBEDTLS_CIPHER_MODE_CFB + * + * Enable Cipher Feedback mode (CFB) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CFB + +/** + * \def MBEDTLS_CIPHER_MODE_CTR + * + * Enable Counter Block Cipher mode (CTR) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_CTR + +/** + * \def MBEDTLS_CIPHER_MODE_OFB + * + * Enable Output Feedback mode (OFB) for symmetric ciphers. + */ +#define MBEDTLS_CIPHER_MODE_OFB + +/** + * \def MBEDTLS_CIPHER_MODE_XTS + * + * Enable Xor-encrypt-xor with ciphertext stealing mode (XTS) for AES. + */ +#define MBEDTLS_CIPHER_MODE_XTS + +/** + * \def MBEDTLS_CIPHER_NULL_CIPHER + * + * Enable NULL cipher. + * Warning: Only do so when you know what you are doing. This allows for + * encryption or channels without any security! + * + * To enable the following ciphersuites: + * MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_RSA_WITH_NULL_SHA256 + * MBEDTLS_TLS_RSA_WITH_NULL_SHA + * MBEDTLS_TLS_RSA_WITH_NULL_MD5 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA + * MBEDTLS_TLS_PSK_WITH_NULL_SHA384 + * MBEDTLS_TLS_PSK_WITH_NULL_SHA256 + * MBEDTLS_TLS_PSK_WITH_NULL_SHA + * + * Uncomment this macro to enable the NULL cipher and ciphersuites + */ +//#define MBEDTLS_CIPHER_NULL_CIPHER + +/** + * \def MBEDTLS_CIPHER_PADDING_PKCS7 + * + * MBEDTLS_CIPHER_PADDING_XXX: Uncomment or comment macros to add support for + * specific padding modes in the cipher layer with cipher modes that support + * padding (e.g. CBC) + * + * If you disable all padding modes, only full blocks can be used with CBC. + * + * Enable padding modes in the cipher layer. + */ +#define MBEDTLS_CIPHER_PADDING_PKCS7 +#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS +#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN +#define MBEDTLS_CIPHER_PADDING_ZEROS + +/** \def MBEDTLS_CTR_DRBG_USE_128_BIT_KEY + * + * Uncomment this macro to use a 128-bit key in the CTR_DRBG module. + * By default, CTR_DRBG uses a 256-bit key. + */ +//#define MBEDTLS_CTR_DRBG_USE_128_BIT_KEY + +/** + * \def MBEDTLS_ECP_DP_SECP192R1_ENABLED + * + * MBEDTLS_ECP_XXXX_ENABLED: Enables specific curves within the Elliptic Curve + * module. By default all supported curves are enabled. + * + * Comment macros to disable the curve and functions for it + */ +/* Short Weierstrass curves (supporting ECP, ECDH, ECDSA) */ +#define MBEDTLS_ECP_DP_SECP192R1_ENABLED +#define MBEDTLS_ECP_DP_SECP224R1_ENABLED +#define MBEDTLS_ECP_DP_SECP256R1_ENABLED +#define MBEDTLS_ECP_DP_SECP384R1_ENABLED +#define MBEDTLS_ECP_DP_SECP521R1_ENABLED +#define MBEDTLS_ECP_DP_SECP192K1_ENABLED +#define MBEDTLS_ECP_DP_SECP224K1_ENABLED +#define MBEDTLS_ECP_DP_SECP256K1_ENABLED +#define MBEDTLS_ECP_DP_BP256R1_ENABLED +#define MBEDTLS_ECP_DP_BP384R1_ENABLED +#define MBEDTLS_ECP_DP_BP512R1_ENABLED +/* Montgomery curves (supporting ECP) */ +#define MBEDTLS_ECP_DP_CURVE25519_ENABLED +#define MBEDTLS_ECP_DP_CURVE448_ENABLED + +/** + * \def MBEDTLS_ECP_NIST_OPTIM + * + * Enable specific 'modulo p' routines for each NIST prime. + * Depending on the prime and architecture, makes operations 4 to 8 times + * faster on the corresponding curve. + * + * Comment this macro to disable NIST curves optimisation. + */ +#define MBEDTLS_ECP_NIST_OPTIM + +/** + * \def MBEDTLS_ECP_RESTARTABLE + * + * Enable "non-blocking" ECC operations that can return early and be resumed. + * + * This allows various functions to pause by returning + * #MBEDTLS_ERR_ECP_IN_PROGRESS (or, for functions in the SSL module, + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) and then be called later again in + * order to further progress and eventually complete their operation. This is + * controlled through mbedtls_ecp_set_max_ops() which limits the maximum + * number of ECC operations a function may perform before pausing; see + * mbedtls_ecp_set_max_ops() for more information. + * + * This is useful in non-threaded environments if you want to avoid blocking + * for too long on ECC (and, hence, X.509 or SSL/TLS) operations. + * + * This option: + * - Adds xxx_restartable() variants of existing operations in the + * following modules, with corresponding restart context types: + * - ECP (for Short Weierstrass curves only): scalar multiplication (mul), + * linear combination (muladd); + * - ECDSA: signature generation & verification; + * - PK: signature generation & verification; + * - X509: certificate chain verification. + * - Adds mbedtls_ecdh_enable_restart() in the ECDH module. + * - Changes the behaviour of TLS 1.2 clients (not servers) when using the + * ECDHE-ECDSA key exchange (not other key exchanges) to make all ECC + * computations restartable: + * - ECDH operations from the key exchange, only for Short Weierstrass + * curves, only when MBEDTLS_USE_PSA_CRYPTO is not enabled. + * - verification of the server's key exchange signature; + * - verification of the server's certificate chain; + * - generation of the client's signature if client authentication is used, + * with an ECC key/certificate. + * + * \note In the cases above, the usual SSL/TLS functions, such as + * mbedtls_ssl_handshake(), can now return + * MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS. + * + * \note When this option and MBEDTLS_USE_PSA_CRYPTO are both enabled, + * restartable operations in PK, X.509 and TLS (see above) are not + * using PSA. On the other hand, ECDH computations in TLS are using + * PSA, and are not restartable. These are temporary limitations that + * should be lifted in the future. + * + * \note This option only works with the default software implementation of + * elliptic curve functionality. It is incompatible with + * MBEDTLS_ECP_ALT, MBEDTLS_ECDH_XXX_ALT, MBEDTLS_ECDSA_XXX_ALT. + * + * Requires: MBEDTLS_ECP_C + * + * Uncomment this macro to enable restartable ECC computations. + */ +//#define MBEDTLS_ECP_RESTARTABLE + +/** + * \def MBEDTLS_ECDSA_DETERMINISTIC + * + * Enable deterministic ECDSA (RFC 6979). + * Standard ECDSA is "fragile" in the sense that lack of entropy when signing + * may result in a compromise of the long-term signing key. This is avoided by + * the deterministic variant. + * + * Requires: MBEDTLS_HMAC_DRBG_C, MBEDTLS_ECDSA_C + * + * Comment this macro to disable deterministic ECDSA. + */ +#define MBEDTLS_ECDSA_DETERMINISTIC + +/** + * \def MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + * + * Enable the PSK based ciphersuite modes in SSL / TLS. + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED + * + * Enable the DHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_DHM_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED + * + * Enable the ECDHE-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED + * + * Enable the RSA-PSK based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_RSA_ENABLED + * + * Enable the RSA-only based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + */ +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED + * + * Enable the DHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_DHM_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED + * + * Enable the ECDHE-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_RSA_C, MBEDTLS_PKCS1_V15, + * MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + * + * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_ECDSA_C, MBEDTLS_X509_CRT_PARSE_C, + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + * + * Enable the ECDH-ECDSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_ECDSA_C, MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED + * + * Enable the ECDH-RSA based ciphersuite modes in SSL / TLS. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_RSA_C, MBEDTLS_X509_CRT_PARSE_C + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + */ +#define MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED + +/** + * \def MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED + * + * Enable the ECJPAKE based ciphersuite modes in SSL / TLS. + * + * \warning This is currently experimental. EC J-PAKE support is based on the + * Thread v1.0.0 specification; incompatible changes to the specification + * might still happen. For this reason, this is disabled by default. + * + * Requires: MBEDTLS_ECJPAKE_C + * SHA-256 (via MD if present, or via PSA, see MBEDTLS_ECJPAKE_C) + * MBEDTLS_ECP_DP_SECP256R1_ENABLED + * + * This enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 + */ +//#define MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED + +/** + * \def MBEDTLS_PK_PARSE_EC_EXTENDED + * + * Enhance support for reading EC keys using variants of SEC1 not allowed by + * RFC 5915 and RFC 5480. + * + * Currently this means parsing the SpecifiedECDomain choice of EC + * parameters (only known groups are supported, not arbitrary domains, to + * avoid validation issues). + * + * Disable if you only need to support RFC 5915 + 5480 key formats. + */ +#define MBEDTLS_PK_PARSE_EC_EXTENDED + +/** + * \def MBEDTLS_ERROR_STRERROR_DUMMY + * + * Enable a dummy error function to make use of mbedtls_strerror() in + * third party libraries easier when MBEDTLS_ERROR_C is disabled + * (no effect when MBEDTLS_ERROR_C is enabled). + * + * You can safely disable this if MBEDTLS_ERROR_C is enabled, or if you're + * not using mbedtls_strerror() or error_strerror() in your application. + * + * Disable if you run into name conflicts and want to really remove the + * mbedtls_strerror() + */ +#define MBEDTLS_ERROR_STRERROR_DUMMY + +/** + * \def MBEDTLS_GENPRIME + * + * Enable the prime-number generation code. + * + * Requires: MBEDTLS_BIGNUM_C + */ +#define MBEDTLS_GENPRIME + +/** + * \def MBEDTLS_FS_IO + * + * Enable functions that use the filesystem. + */ +#define MBEDTLS_FS_IO + +/** + * \def MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + * + * Do not add default entropy sources in mbedtls_entropy_init(). + * + * This is useful to have more control over the added entropy sources in an + * application. + * + * Uncomment this macro to prevent loading of default entropy functions. + */ +//#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES + +/** + * \def MBEDTLS_NO_PLATFORM_ENTROPY + * + * Do not use built-in platform entropy functions. + * This is useful if your platform does not support + * standards like the /dev/urandom or Windows CryptoAPI. + * + * Uncomment this macro to disable the built-in platform entropy functions. + */ +//#define MBEDTLS_NO_PLATFORM_ENTROPY + +/** + * \def MBEDTLS_ENTROPY_FORCE_SHA256 + * + * Force the entropy accumulator to use a SHA-256 accumulator instead of the + * default SHA-512 based one (if both are available). + * + * Requires: MBEDTLS_SHA256_C + * + * On 32-bit systems SHA-256 can be much faster than SHA-512. Use this option + * if you have performance concerns. + * + * This option is only useful if both MBEDTLS_SHA256_C and + * MBEDTLS_SHA512_C are defined. Otherwise the available hash module is used. + */ +//#define MBEDTLS_ENTROPY_FORCE_SHA256 + +/** + * \def MBEDTLS_ENTROPY_NV_SEED + * + * Enable the non-volatile (NV) seed file-based entropy source. + * (Also enables the NV seed read/write functions in the platform layer) + * + * This is crucial (if not required) on systems that do not have a + * cryptographic entropy source (in hardware or kernel) available. + * + * Requires: MBEDTLS_ENTROPY_C, MBEDTLS_PLATFORM_C + * + * \note The read/write functions that are used by the entropy source are + * determined in the platform layer, and can be modified at runtime and/or + * compile-time depending on the flags (MBEDTLS_PLATFORM_NV_SEED_*) used. + * + * \note If you use the default implementation functions that read a seedfile + * with regular fopen(), please make sure you make a seedfile with the + * proper name (defined in MBEDTLS_PLATFORM_STD_NV_SEED_FILE) and at + * least MBEDTLS_ENTROPY_BLOCK_SIZE bytes in size that can be read from + * and written to or you will get an entropy source error! The default + * implementation will only use the first MBEDTLS_ENTROPY_BLOCK_SIZE + * bytes from the file. + * + * \note The entropy collector will write to the seed file before entropy is + * given to an external source, to update it. + */ +//#define MBEDTLS_ENTROPY_NV_SEED + +/* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER + * + * Enable key identifiers that encode a key owner identifier. + * + * The owner of a key is identified by a value of type ::mbedtls_key_owner_id_t + * which is currently hard-coded to be int32_t. + * + * Note that this option is meant for internal use only and may be removed + * without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER + +/** + * \def MBEDTLS_MEMORY_DEBUG + * + * Enable debugging of buffer allocator memory issues. Automatically prints + * (to stderr) all (fatal) messages on memory allocation issues. Enables + * function for 'debug output' of allocated memory. + * + * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C + * + * Uncomment this macro to let the buffer allocator print out error messages. + */ +//#define MBEDTLS_MEMORY_DEBUG + +/** + * \def MBEDTLS_MEMORY_BACKTRACE + * + * Include backtrace information with each allocated block. + * + * Requires: MBEDTLS_MEMORY_BUFFER_ALLOC_C + * GLIBC-compatible backtrace() and backtrace_symbols() support + * + * Uncomment this macro to include backtrace information + */ +//#define MBEDTLS_MEMORY_BACKTRACE + +/** + * \def MBEDTLS_PK_RSA_ALT_SUPPORT + * + * Support external private RSA keys (eg from a HSM) in the PK layer. + * + * Comment this macro to disable support for external private RSA keys. + */ +#define MBEDTLS_PK_RSA_ALT_SUPPORT + +/** + * \def MBEDTLS_PKCS1_V15 + * + * Enable support for PKCS#1 v1.5 encoding. + * + * Requires: MBEDTLS_RSA_C + * + * This enables support for PKCS#1 v1.5 operations. + */ +#define MBEDTLS_PKCS1_V15 + +/** + * \def MBEDTLS_PKCS1_V21 + * + * Enable support for PKCS#1 v2.1 encoding. + * + * Requires: MBEDTLS_RSA_C and (MBEDTLS_MD_C or MBEDTLS_PSA_CRYPTO_C). + * + * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init() + * before doing any PKCS#1 v2.1 operation. + * + * \warning When building with MBEDTLS_MD_C, all hashes used with this + * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C, + * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by + * this module in builds where MBEDTLS_MD_C is disabled. + * + * This enables support for RSAES-OAEP and RSASSA-PSS operations. + */ +#define MBEDTLS_PKCS1_V21 + +/** \def MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS + * + * Enable support for platform built-in keys. If you enable this feature, + * you must implement the function mbedtls_psa_platform_get_builtin_key(). + * See the documentation of that function for more information. + * + * Built-in keys are typically derived from a hardware unique key or + * stored in a secure element. + * + * Requires: MBEDTLS_PSA_CRYPTO_C. + * + * \warning This interface is experimental and may change or be removed + * without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS + +/** \def MBEDTLS_PSA_CRYPTO_CLIENT + * + * Enable support for PSA crypto client. + * + * \note This option allows to include the code necessary for a PSA + * crypto client when the PSA crypto implementation is not included in + * the library (MBEDTLS_PSA_CRYPTO_C disabled). The code included is the + * code to set and get PSA key attributes. + * The development of PSA drivers partially relying on the library to + * fulfill the hardware gaps is another possible usage of this option. + * + * \warning This interface is experimental and may change or be removed + * without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_CLIENT + +/** \def MBEDTLS_PSA_CRYPTO_DRIVERS + * + * Enable support for the experimental PSA crypto driver interface. + * + * Requires: MBEDTLS_PSA_CRYPTO_C + * + * \warning This interface is experimental. We intend to maintain backward + * compatibility with application code that relies on drivers, + * but the driver interfaces may change without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_DRIVERS + +/** \def MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + * + * Make the PSA Crypto module use an external random generator provided + * by a driver, instead of Mbed TLS's entropy and DRBG modules. + * + * \note This random generator must deliver random numbers with cryptographic + * quality and high performance. It must supply unpredictable numbers + * with a uniform distribution. The implementation of this function + * is responsible for ensuring that the random generator is seeded + * with sufficient entropy. If you have a hardware TRNG which is slow + * or delivers non-uniform output, declare it as an entropy source + * with mbedtls_entropy_add_source() instead of enabling this option. + * + * If you enable this option, you must configure the type + * ::mbedtls_psa_external_random_context_t in psa/crypto_platform.h + * and define a function called mbedtls_psa_external_get_random() + * with the following prototype: + * ``` + * psa_status_t mbedtls_psa_external_get_random( + * mbedtls_psa_external_random_context_t *context, + * uint8_t *output, size_t output_size, size_t *output_length); + * ); + * ``` + * The \c context value is initialized to 0 before the first call. + * The function must fill the \c output buffer with \p output_size bytes + * of random data and set \c *output_length to \p output_size. + * + * Requires: MBEDTLS_PSA_CRYPTO_C + * + * \warning If you enable this option, code that uses the PSA cryptography + * interface will not use any of the entropy sources set up for + * the entropy module, nor the NV seed that MBEDTLS_ENTROPY_NV_SEED + * enables. + * + * \note This option is experimental and may be removed without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG + +/** + * \def MBEDTLS_PSA_CRYPTO_SPM + * + * When MBEDTLS_PSA_CRYPTO_SPM is defined, the code is built for SPM (Secure + * Partition Manager) integration which separates the code into two parts: a + * NSPE (Non-Secure Process Environment) and an SPE (Secure Process + * Environment). + * + * Module: library/psa_crypto.c + * Requires: MBEDTLS_PSA_CRYPTO_C + * + */ +//#define MBEDTLS_PSA_CRYPTO_SPM + +/** + * \def MBEDTLS_PSA_INJECT_ENTROPY + * + * Enable support for entropy injection at first boot. This feature is + * required on systems that do not have a built-in entropy source (TRNG). + * This feature is currently not supported on systems that have a built-in + * entropy source. + * + * Requires: MBEDTLS_PSA_CRYPTO_STORAGE_C, MBEDTLS_ENTROPY_NV_SEED + * + */ +//#define MBEDTLS_PSA_INJECT_ENTROPY + +/** + * \def MBEDTLS_RSA_NO_CRT + * + * Do not use the Chinese Remainder Theorem + * for the RSA private operation. + * + * Uncomment this macro to disable the use of CRT in RSA. + * + */ +//#define MBEDTLS_RSA_NO_CRT + +/** + * \def MBEDTLS_SELF_TEST + * + * Enable the checkup functions (*_self_test). + */ +#define MBEDTLS_SELF_TEST + +/** + * \def MBEDTLS_SHA256_SMALLER + * + * Enable an implementation of SHA-256 that has lower ROM footprint but also + * lower performance. + * + * The default implementation is meant to be a reasonable compromise between + * performance and size. This version optimizes more aggressively for size at + * the expense of performance. Eg on Cortex-M4 it reduces the size of + * mbedtls_sha256_process() from ~2KB to ~0.5KB for a performance hit of about + * 30%. + * + * Uncomment to enable the smaller implementation of SHA256. + */ +//#define MBEDTLS_SHA256_SMALLER + +/** + * \def MBEDTLS_SHA512_SMALLER + * + * Enable an implementation of SHA-512 that has lower ROM footprint but also + * lower performance. + * + * Uncomment to enable the smaller implementation of SHA512. + */ +//#define MBEDTLS_SHA512_SMALLER + +/** + * \def MBEDTLS_SSL_ALL_ALERT_MESSAGES + * + * Enable sending of alert messages in case of encountered errors as per RFC. + * If you choose not to send the alert messages, mbed TLS can still communicate + * with other servers, only debugging of failures is harder. + * + * The advantage of not sending alert messages, is that no information is given + * about reasons for failures thus preventing adversaries of gaining intel. + * + * Enable sending of all alert messages + */ +#define MBEDTLS_SSL_ALL_ALERT_MESSAGES + +/** + * \def MBEDTLS_SSL_DTLS_CONNECTION_ID + * + * Enable support for the DTLS Connection ID (CID) extension, + * which allows to identify DTLS connections across changes + * in the underlying transport. The CID functionality is described + * in RFC 9146. + * + * Setting this option enables the SSL APIs `mbedtls_ssl_set_cid()`, + * mbedtls_ssl_get_own_cid()`, `mbedtls_ssl_get_peer_cid()` and + * `mbedtls_ssl_conf_cid()`. See the corresponding documentation for + * more information. + * + * The maximum lengths of outgoing and incoming CIDs can be configured + * through the options + * - MBEDTLS_SSL_CID_OUT_LEN_MAX + * - MBEDTLS_SSL_CID_IN_LEN_MAX. + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Uncomment to enable the Connection ID extension. + */ +#define MBEDTLS_SSL_DTLS_CONNECTION_ID + + +/** + * \def MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT + * + * Defines whether RFC 9146 (default) or the legacy version + * (version draft-ietf-tls-dtls-connection-id-05, + * https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05) + * is used. + * + * Set the value to 0 for the standard version, and + * 1 for the legacy draft version. + * + * \deprecated Support for the legacy version of the DTLS + * Connection ID feature is deprecated. Please + * switch to the standardized version defined + * in RFC 9146 enabled by utilizing + * MBEDTLS_SSL_DTLS_CONNECTION_ID without use + * of MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT. + * + * Requires: MBEDTLS_SSL_DTLS_CONNECTION_ID + */ +#define MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT 0 + +/** + * \def MBEDTLS_SSL_ASYNC_PRIVATE + * + * Enable asynchronous external private key operations in SSL. This allows + * you to configure an SSL connection to call an external cryptographic + * module to perform private key operations instead of performing the + * operation inside the library. + * + * Requires: MBEDTLS_X509_CRT_PARSE_C + */ +//#define MBEDTLS_SSL_ASYNC_PRIVATE + +/** + * \def MBEDTLS_SSL_CONTEXT_SERIALIZATION + * + * Enable serialization of the TLS context structures, through use of the + * functions mbedtls_ssl_context_save() and mbedtls_ssl_context_load(). + * + * This pair of functions allows one side of a connection to serialize the + * context associated with the connection, then free or re-use that context + * while the serialized state is persisted elsewhere, and finally deserialize + * that state to a live context for resuming read/write operations on the + * connection. From a protocol perspective, the state of the connection is + * unaffected, in particular this is entirely transparent to the peer. + * + * Note: this is distinct from TLS session resumption, which is part of the + * protocol and fully visible by the peer. TLS session resumption enables + * establishing new connections associated to a saved session with shorter, + * lighter handshakes, while context serialization is a local optimization in + * handling a single, potentially long-lived connection. + * + * Enabling these APIs makes some SSL structures larger, as 64 extra bytes are + * saved after the handshake to allow for more efficient serialization, so if + * you don't need this feature you'll save RAM by disabling it. + * + * Requires: MBEDTLS_GCM_C or MBEDTLS_CCM_C or MBEDTLS_CHACHAPOLY_C + * + * Comment to disable the context serialization APIs. + */ +#define MBEDTLS_SSL_CONTEXT_SERIALIZATION + +/** + * \def MBEDTLS_SSL_DEBUG_ALL + * + * Enable the debug messages in SSL module for all issues. + * Debug messages have been disabled in some places to prevent timing + * attacks due to (unbalanced) debugging function calls. + * + * If you need all error reporting you should enable this during debugging, + * but remove this for production servers that should log as well. + * + * Uncomment this macro to report all debug messages on errors introducing + * a timing side-channel. + * + */ +//#define MBEDTLS_SSL_DEBUG_ALL + +/** \def MBEDTLS_SSL_ENCRYPT_THEN_MAC + * + * Enable support for Encrypt-then-MAC, RFC 7366. + * + * This allows peers that both support it to use a more robust protection for + * ciphersuites using CBC, providing deep resistance against timing attacks + * on the padding or underlying cipher. + * + * This only affects CBC ciphersuites, and is useless if none is defined. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for Encrypt-then-MAC + */ +#define MBEDTLS_SSL_ENCRYPT_THEN_MAC + +/** \def MBEDTLS_SSL_EXTENDED_MASTER_SECRET + * + * Enable support for RFC 7627: Session Hash and Extended Master Secret + * Extension. + * + * This was introduced as "the proper fix" to the Triple Handshake family of + * attacks, but it is recommended to always use it (even if you disable + * renegotiation), since it actually fixes a more fundamental issue in the + * original SSL/TLS design, and has implications beyond Triple Handshake. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for Extended Master Secret. + */ +#define MBEDTLS_SSL_EXTENDED_MASTER_SECRET + +/** + * \def MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + * + * This option controls the availability of the API mbedtls_ssl_get_peer_cert() + * giving access to the peer's certificate after completion of the handshake. + * + * Unless you need mbedtls_ssl_peer_cert() in your application, it is + * recommended to disable this option for reduced RAM usage. + * + * \note If this option is disabled, mbedtls_ssl_get_peer_cert() is still + * defined, but always returns \c NULL. + * + * \note This option has no influence on the protection against the + * triple handshake attack. Even if it is disabled, Mbed TLS will + * still ensure that certificates do not change during renegotiation, + * for example by keeping a hash of the peer's certificate. + * + * \note This option is required if MBEDTLS_SSL_PROTO_TLS1_3 is set. + * + * Comment this macro to disable storing the peer's certificate + * after the handshake. + */ +#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + +/** + * \def MBEDTLS_SSL_RENEGOTIATION + * + * Enable support for TLS renegotiation. + * + * The two main uses of renegotiation are (1) refresh keys on long-lived + * connections and (2) client authentication after the initial handshake. + * If you don't need renegotiation, it's probably better to disable it, since + * it has been associated with security issues in the past and is easy to + * misuse/misunderstand. + * + * Comment this to disable support for renegotiation. + * + * \note Even if this option is disabled, both client and server are aware + * of the Renegotiation Indication Extension (RFC 5746) used to + * prevent the SSL renegotiation attack (see RFC 5746 Sect. 1). + * (See \c mbedtls_ssl_conf_legacy_renegotiation for the + * configuration of this extension). + * + */ +#define MBEDTLS_SSL_RENEGOTIATION + +/** + * \def MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + * + * Enable support for RFC 6066 max_fragment_length extension in SSL. + * + * Comment this macro to disable support for the max_fragment_length extension + */ +#define MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + +/** + * \def MBEDTLS_SSL_RECORD_SIZE_LIMIT + * + * Enable support for RFC 8449 record_size_limit extension in SSL (TLS 1.3 only). + * + * \warning This extension is currently in development and must NOT be used except + * for testing purposes. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_3 + * + * Uncomment this macro to enable support for the record_size_limit extension + */ +//#define MBEDTLS_SSL_RECORD_SIZE_LIMIT + +/** + * \def MBEDTLS_SSL_PROTO_TLS1_2 + * + * Enable support for TLS 1.2 (and DTLS 1.2 if DTLS is enabled). + * + * Requires: Without MBEDTLS_USE_PSA_CRYPTO: MBEDTLS_MD_C and + * (MBEDTLS_SHA1_C or MBEDTLS_SHA256_C or MBEDTLS_SHA512_C) + * With MBEDTLS_USE_PSA_CRYPTO: + * PSA_WANT_ALG_SHA_1 or PSA_WANT_ALG_SHA_256 or + * PSA_WANT_ALG_SHA_512 + * + * \warning If building with MBEDTLS_USE_PSA_CRYPTO, you must call + * psa_crypto_init() before doing any TLS operations. + * + * Comment this macro to disable support for TLS 1.2 / DTLS 1.2 + */ +#define MBEDTLS_SSL_PROTO_TLS1_2 + +/** + * \def MBEDTLS_SSL_PROTO_TLS1_3 + * + * Enable support for TLS 1.3. + * + * \note The support for TLS 1.3 is not comprehensive yet, in particular + * pre-shared keys are not supported. + * See docs/architecture/tls13-support.md for a description of the TLS + * 1.3 support that this option enables. + * + * Requires: MBEDTLS_SSL_KEEP_PEER_CERTIFICATE + * Requires: MBEDTLS_PSA_CRYPTO_C + * + * \note TLS 1.3 uses PSA crypto for cryptographic operations that are + * directly performed by TLS 1.3 code. As a consequence, you must + * call psa_crypto_init() before the first TLS 1.3 handshake. + * + * \note Cryptographic operations performed indirectly via another module + * (X.509, PK) or by code shared with TLS 1.2 (record protection, + * running handshake hash) only use PSA crypto if + * #MBEDTLS_USE_PSA_CRYPTO is enabled. + * + * Uncomment this macro to enable the support for TLS 1.3. + */ +//#define MBEDTLS_SSL_PROTO_TLS1_3 + +/** + * \def MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE + * + * Enable TLS 1.3 middlebox compatibility mode. + * + * As specified in Section D.4 of RFC 8446, TLS 1.3 offers a compatibility + * mode to make a TLS 1.3 connection more likely to pass through middle boxes + * expecting TLS 1.2 traffic. + * + * Turning on the compatibility mode comes at the cost of a few added bytes + * on the wire, but it doesn't affect compatibility with TLS 1.3 implementations + * that don't use it. Therefore, unless transmission bandwidth is critical and + * you know that middlebox compatibility issues won't occur, it is therefore + * recommended to set this option. + * + * Comment to disable compatibility mode for TLS 1.3. If + * MBEDTLS_SSL_PROTO_TLS1_3 is not enabled, this option does not have any + * effect on the build. + * + */ +//#define MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE + +/** + * \def MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED + * + * Enable TLS 1.3 PSK key exchange mode. + * + * Comment to disable support for the PSK key exchange mode in TLS 1.3. If + * MBEDTLS_SSL_PROTO_TLS1_3 is not enabled, this option does not have any + * effect on the build. + * + */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED + +/** + * \def MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED + * + * Enable TLS 1.3 ephemeral key exchange mode. + * + * Requires: MBEDTLS_ECDH_C, MBEDTLS_X509_CRT_PARSE_C, MBEDTLS_ECDSA_C or + * MBEDTLS_PKCS1_V21 + * + * Comment to disable support for the ephemeral key exchange mode in TLS 1.3. + * If MBEDTLS_SSL_PROTO_TLS1_3 is not enabled, this option does not have any + * effect on the build. + * + */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED + +/** + * \def MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED + * + * Enable TLS 1.3 PSK ephemeral key exchange mode. + * + * Requires: MBEDTLS_ECDH_C + * + * Comment to disable support for the PSK ephemeral key exchange mode in + * TLS 1.3. If MBEDTLS_SSL_PROTO_TLS1_3 is not enabled, this option does not + * have any effect on the build. + * + */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED + +/** + * \def MBEDTLS_SSL_EARLY_DATA + * + * Enable support for RFC 8446 TLS 1.3 early data. + * + * Requires: MBEDTLS_SSL_SESSION_TICKETS and either + * MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED or + * MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED + * + * Comment this to disable support for early data. If MBEDTLS_SSL_PROTO_TLS1_3 + * is not enabled, this option does not have any effect on the build. + * + * This feature is experimental, not completed and thus not ready for + * production. + * + */ +//#define MBEDTLS_SSL_EARLY_DATA + +/** + * \def MBEDTLS_SSL_MAX_EARLY_DATA_SIZE + * + * The default maximum amount of 0-RTT data. See the documentation of + * \c mbedtls_ssl_tls13_conf_max_early_data_size() for more information. + * + * It must be positive and smaller than UINT32_MAX. + * + * If MBEDTLS_SSL_EARLY_DATA is not defined, this default value does not + * have any impact on the build. + * + * This feature is experimental, not completed and thus not ready for + * production. + * + */ +#define MBEDTLS_SSL_MAX_EARLY_DATA_SIZE 1024 + +/** + * \def MBEDTLS_SSL_PROTO_DTLS + * + * Enable support for DTLS (all available versions). + * + * Enable this and MBEDTLS_SSL_PROTO_TLS1_2 to enable DTLS 1.2. + * + * Requires: MBEDTLS_SSL_PROTO_TLS1_2 + * + * Comment this macro to disable support for DTLS + */ +#define MBEDTLS_SSL_PROTO_DTLS + +/** + * \def MBEDTLS_SSL_ALPN + * + * Enable support for RFC 7301 Application Layer Protocol Negotiation. + * + * Comment this macro to disable support for ALPN. + */ +#define MBEDTLS_SSL_ALPN + +/** + * \def MBEDTLS_SSL_DTLS_ANTI_REPLAY + * + * Enable support for the anti-replay mechanism in DTLS. + * + * Requires: MBEDTLS_SSL_TLS_C + * MBEDTLS_SSL_PROTO_DTLS + * + * \warning Disabling this is often a security risk! + * See mbedtls_ssl_conf_dtls_anti_replay() for details. + * + * Comment this to disable anti-replay in DTLS. + */ +#define MBEDTLS_SSL_DTLS_ANTI_REPLAY + +/** + * \def MBEDTLS_SSL_DTLS_HELLO_VERIFY + * + * Enable support for HelloVerifyRequest on DTLS servers. + * + * This feature is highly recommended to prevent DTLS servers being used as + * amplifiers in DoS attacks against other hosts. It should always be enabled + * unless you know for sure amplification cannot be a problem in the + * environment in which your server operates. + * + * \warning Disabling this can be a security risk! (see above) + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Comment this to disable support for HelloVerifyRequest. + */ +#define MBEDTLS_SSL_DTLS_HELLO_VERIFY + +/** + * \def MBEDTLS_SSL_DTLS_SRTP + * + * Enable support for negotiation of DTLS-SRTP (RFC 5764) + * through the use_srtp extension. + * + * \note This feature provides the minimum functionality required + * to negotiate the use of DTLS-SRTP and to allow the derivation of + * the associated SRTP packet protection key material. + * In particular, the SRTP packet protection itself, as well as the + * demultiplexing of RTP and DTLS packets at the datagram layer + * (see Section 5 of RFC 5764), are not handled by this feature. + * Instead, after successful completion of a handshake negotiating + * the use of DTLS-SRTP, the extended key exporter API + * mbedtls_ssl_conf_export_keys_cb() should be used to implement + * the key exporter described in Section 4.2 of RFC 5764 and RFC 5705 + * (this is implemented in the SSL example programs). + * The resulting key should then be passed to an SRTP stack. + * + * Setting this option enables the runtime API + * mbedtls_ssl_conf_dtls_srtp_protection_profiles() + * through which the supported DTLS-SRTP protection + * profiles can be configured. You must call this API at + * runtime if you wish to negotiate the use of DTLS-SRTP. + * + * Requires: MBEDTLS_SSL_PROTO_DTLS + * + * Uncomment this to enable support for use_srtp extension. + */ +//#define MBEDTLS_SSL_DTLS_SRTP + +/** + * \def MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + * + * Enable server-side support for clients that reconnect from the same port. + * + * Some clients unexpectedly close the connection and try to reconnect using the + * same source port. This needs special support from the server to handle the + * new connection securely, as described in section 4.2.8 of RFC 6347. This + * flag enables that support. + * + * Requires: MBEDTLS_SSL_DTLS_HELLO_VERIFY + * + * Comment this to disable support for clients reusing the source port. + */ +#define MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE + +/** + * \def MBEDTLS_SSL_SESSION_TICKETS + * + * Enable support for RFC 5077 session tickets in SSL. + * Client-side, provides full support for session tickets (maintenance of a + * session store remains the responsibility of the application, though). + * Server-side, you also need to provide callbacks for writing and parsing + * tickets, including authenticated encryption and key management. Example + * callbacks are provided by MBEDTLS_SSL_TICKET_C. + * + * Comment this macro to disable support for SSL session tickets + */ +#define MBEDTLS_SSL_SESSION_TICKETS + +/** + * \def MBEDTLS_SSL_SERVER_NAME_INDICATION + * + * Enable support for RFC 6066 server name indication (SNI) in SSL. + * + * Requires: MBEDTLS_X509_CRT_PARSE_C + * + * Comment this macro to disable support for server name indication in SSL + */ +#define MBEDTLS_SSL_SERVER_NAME_INDICATION + +/** + * \def MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH + * + * When this option is enabled, the SSL buffer will be resized automatically + * based on the negotiated maximum fragment length in each direction. + * + * Requires: MBEDTLS_SSL_MAX_FRAGMENT_LENGTH + */ +//#define MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH + +/** + * \def MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN + * + * Enable testing of the constant-flow nature of some sensitive functions with + * clang's MemorySanitizer. This causes some existing tests to also test + * this non-functional property of the code under test. + * + * This setting requires compiling with clang -fsanitize=memory. The test + * suites can then be run normally. + * + * \warning This macro is only used for extended testing; it is not considered + * part of the library's API, so it may change or disappear at any time. + * + * Uncomment to enable testing of the constant-flow nature of selected code. + */ +//#define MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN + +/** + * \def MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND + * + * Enable testing of the constant-flow nature of some sensitive functions with + * valgrind's memcheck tool. This causes some existing tests to also test + * this non-functional property of the code under test. + * + * This setting requires valgrind headers for building, and is only useful for + * testing if the tests suites are run with valgrind's memcheck. This can be + * done for an individual test suite with 'valgrind ./test_suite_xxx', or when + * using CMake, this can be done for all test suites with 'make memcheck'. + * + * \warning This macro is only used for extended testing; it is not considered + * part of the library's API, so it may change or disappear at any time. + * + * Uncomment to enable testing of the constant-flow nature of selected code. + */ +//#define MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND + +/** + * \def MBEDTLS_TEST_HOOKS + * + * Enable features for invasive testing such as introspection functions and + * hooks for fault injection. This enables additional unit tests. + * + * Merely enabling this feature should not change the behavior of the product. + * It only adds new code, and new branching points where the default behavior + * is the same as when this feature is disabled. + * However, this feature increases the attack surface: there is an added + * risk of vulnerabilities, and more gadgets that can make exploits easier. + * Therefore this feature must never be enabled in production. + * + * See `docs/architecture/testing/mbed-crypto-invasive-testing.md` for more + * information. + * + * Uncomment to enable invasive tests. + */ +//#define MBEDTLS_TEST_HOOKS + +/** + * \def MBEDTLS_THREADING_ALT + * + * Provide your own alternate threading implementation. + * + * Requires: MBEDTLS_THREADING_C + * + * Uncomment this to allow your own alternate threading implementation. + */ +//#define MBEDTLS_THREADING_ALT + +/** + * \def MBEDTLS_THREADING_PTHREAD + * + * Enable the pthread wrapper layer for the threading layer. + * + * Requires: MBEDTLS_THREADING_C + * + * Uncomment this to enable pthread mutexes. + */ +//#define MBEDTLS_THREADING_PTHREAD + +/** + * \def MBEDTLS_USE_PSA_CRYPTO + * + * Make the X.509 and TLS library use PSA for cryptographic operations, and + * enable new APIs for using keys handled by PSA Crypto. + * + * \note Development of this option is currently in progress, and parts of Mbed + * TLS's X.509 and TLS modules are not ported to PSA yet. However, these parts + * will still continue to work as usual, so enabling this option should not + * break backwards compatibility. + * + * \note See docs/use-psa-crypto.md for a complete description of what this + * option currently does, and of parts that are not affected by it so far. + * + * \warning If you enable this option, you need to call `psa_crypto_init()` + * before calling any function from the SSL/TLS, X.509 or PK modules. + * + * Requires: MBEDTLS_PSA_CRYPTO_C. + * + * Uncomment this to enable internal use of PSA Crypto and new associated APIs. + */ +//#define MBEDTLS_USE_PSA_CRYPTO + +/** + * \def MBEDTLS_PSA_CRYPTO_CONFIG + * + * This setting allows support for cryptographic mechanisms through the PSA + * API to be configured separately from support through the mbedtls API. + * + * When this option is disabled, the PSA API exposes the cryptographic + * mechanisms that can be implemented on top of the `mbedtls_xxx` API + * configured with `MBEDTLS_XXX` symbols. + * + * When this option is enabled, the PSA API exposes the cryptographic + * mechanisms requested by the `PSA_WANT_XXX` symbols defined in + * include/psa/crypto_config.h. The corresponding `MBEDTLS_XXX` settings are + * automatically enabled if required (i.e. if no PSA driver provides the + * mechanism). You may still freely enable additional `MBEDTLS_XXX` symbols + * in mbedtls_config.h. + * + * If the symbol #MBEDTLS_PSA_CRYPTO_CONFIG_FILE is defined, it specifies + * an alternative header to include instead of include/psa/crypto_config.h. + * + * This feature is still experimental and is not ready for production since + * it is not completed. + */ +//#define MBEDTLS_PSA_CRYPTO_CONFIG + +/** + * \def MBEDTLS_VERSION_FEATURES + * + * Allow run-time checking of compile-time enabled features. Thus allowing users + * to check at run-time if the library is for instance compiled with threading + * support via mbedtls_version_check_feature(). + * + * Requires: MBEDTLS_VERSION_C + * + * Comment this to disable run-time checking and save ROM space + */ +#define MBEDTLS_VERSION_FEATURES + +/** + * \def MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK + * + * If set, this enables the X.509 API `mbedtls_x509_crt_verify_with_ca_cb()` + * and the SSL API `mbedtls_ssl_conf_ca_cb()` which allow users to configure + * the set of trusted certificates through a callback instead of a linked + * list. + * + * This is useful for example in environments where a large number of trusted + * certificates is present and storing them in a linked list isn't efficient + * enough, or when the set of trusted certificates changes frequently. + * + * See the documentation of `mbedtls_x509_crt_verify_with_ca_cb()` and + * `mbedtls_ssl_conf_ca_cb()` for more information. + * + * Requires: MBEDTLS_X509_CRT_PARSE_C + * + * Uncomment to enable trusted certificate callbacks. + */ +//#define MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK + +/** + * \def MBEDTLS_X509_REMOVE_INFO + * + * Disable mbedtls_x509_*_info() and related APIs. + * + * Uncomment to omit mbedtls_x509_*_info(), as well as mbedtls_debug_print_crt() + * and other functions/constants only used by these functions, thus reducing + * the code footprint by several KB. + */ +//#define MBEDTLS_X509_REMOVE_INFO + +/** + * \def MBEDTLS_X509_RSASSA_PSS_SUPPORT + * + * Enable parsing and verification of X.509 certificates, CRLs and CSRS + * signed with RSASSA-PSS (aka PKCS#1 v2.1). + * + * Comment this macro to disallow using RSASSA-PSS in certificates. + */ +#define MBEDTLS_X509_RSASSA_PSS_SUPPORT +/** \} name SECTION: mbed TLS feature support */ + +/** + * \name SECTION: mbed TLS modules + * + * This section enables or disables entire modules in mbed TLS + * \{ + */ + +/** + * \def MBEDTLS_AESNI_C + * + * Enable AES-NI support on x86-64 or x86-32. + * + * \note AESNI is only supported with certain compilers and target options: + * - Visual Studio 2013: supported. + * - GCC, x86-64, target not explicitly supporting AESNI: + * requires MBEDTLS_HAVE_ASM. + * - GCC, x86-32, target not explicitly supporting AESNI: + * not supported. + * - GCC, x86-64 or x86-32, target supporting AESNI: supported. + * For this assembly-less implementation, you must currently compile + * `library/aesni.c` and `library/aes.c` with machine options to enable + * SSE2 and AESNI instructions: `gcc -msse2 -maes -mpclmul` or + * `clang -maes -mpclmul`. + * - Non-x86 targets: this option is silently ignored. + * - Other compilers: this option is silently ignored. + * + * \note + * Above, "GCC" includes compatible compilers such as Clang. + * The limitations on target support are likely to be relaxed in the future. + * + * Module: library/aesni.c + * Caller: library/aes.c + * + * Requires: MBEDTLS_HAVE_ASM (on some platforms, see note) + * + * This modules adds support for the AES-NI instructions on x86. + */ +#define MBEDTLS_AESNI_C + +/** + * \def MBEDTLS_AESCE_C + * + * Enable AES cryptographic extension support on 64-bit Arm. + * + * Module: library/aesce.c + * Caller: library/aes.c + * + * Requires: MBEDTLS_HAVE_ASM, MBEDTLS_AES_C + * + * \warning Runtime detection only works on Linux. For non-Linux operating + * system, Armv8-A Cryptographic Extensions must be supported by + * the CPU when this option is enabled. + * + * This module adds support for the AES Armv8-A Cryptographic Extensions on Aarch64 systems. + */ +#define MBEDTLS_AESCE_C + +/** + * \def MBEDTLS_AES_C + * + * Enable the AES block cipher. + * + * Module: library/aes.c + * Caller: library/cipher.c + * library/pem.c + * library/ctr_drbg.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA + * MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA + * + * PEM_PARSE uses AES for decrypting encrypted keys. + */ +#define MBEDTLS_AES_C + +/** + * \def MBEDTLS_ASN1_PARSE_C + * + * Enable the generic ASN1 parser. + * + * Module: library/asn1.c + * Caller: library/x509.c + * library/dhm.c + * library/pkcs12.c + * library/pkcs5.c + * library/pkparse.c + */ +#define MBEDTLS_ASN1_PARSE_C + +/** + * \def MBEDTLS_ASN1_WRITE_C + * + * Enable the generic ASN1 writer. + * + * Module: library/asn1write.c + * Caller: library/ecdsa.c + * library/pkwrite.c + * library/x509_create.c + * library/x509write_crt.c + * library/x509write_csr.c + */ +#define MBEDTLS_ASN1_WRITE_C + +/** + * \def MBEDTLS_BASE64_C + * + * Enable the Base64 module. + * + * Module: library/base64.c + * Caller: library/pem.c + * + * This module is required for PEM support (required by X.509). + */ +#define MBEDTLS_BASE64_C + +/** + * \def MBEDTLS_BIGNUM_C + * + * Enable the multi-precision integer library. + * + * Module: library/bignum.c + * library/bignum_core.c + * library/bignum_mod.c + * library/bignum_mod_raw.c + * Caller: library/dhm.c + * library/ecp.c + * library/ecdsa.c + * library/rsa.c + * library/rsa_alt_helpers.c + * library/ssl_tls.c + * + * This module is required for RSA, DHM and ECC (ECDH, ECDSA) support. + */ +#define MBEDTLS_BIGNUM_C + +/** + * \def MBEDTLS_CAMELLIA_C + * + * Enable the Camellia block cipher. + * + * Module: library/camellia.c + * Caller: library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 + */ +#define MBEDTLS_CAMELLIA_C + +/** + * \def MBEDTLS_ARIA_C + * + * Enable the ARIA block cipher. + * + * Module: library/aria.c + * Caller: library/cipher.c + * + * This module enables the following ciphersuites (if other requisites are + * enabled as well): + * + * MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 + * MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 + * MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 + * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 + * MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 + */ +#define MBEDTLS_ARIA_C + +/** + * \def MBEDTLS_CCM_C + * + * Enable the Counter with CBC-MAC (CCM) mode for 128-bit block cipher. + * + * Module: library/ccm.c + * + * Requires: MBEDTLS_CIPHER_C, MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C or + * MBEDTLS_ARIA_C + * + * This module enables the AES-CCM ciphersuites, if other requisites are + * enabled as well. + */ +#define MBEDTLS_CCM_C + +/** + * \def MBEDTLS_CHACHA20_C + * + * Enable the ChaCha20 stream cipher. + * + * Module: library/chacha20.c + */ +#define MBEDTLS_CHACHA20_C + +/** + * \def MBEDTLS_CHACHAPOLY_C + * + * Enable the ChaCha20-Poly1305 AEAD algorithm. + * + * Module: library/chachapoly.c + * + * This module requires: MBEDTLS_CHACHA20_C, MBEDTLS_POLY1305_C + */ +#define MBEDTLS_CHACHAPOLY_C + +/** + * \def MBEDTLS_CIPHER_C + * + * Enable the generic cipher layer. + * + * Module: library/cipher.c + * Caller: library/ccm.c + * library/cmac.c + * library/gcm.c + * library/nist_kw.c + * library/pkcs12.c + * library/pkcs5.c + * library/psa_crypto_aead.c + * library/psa_crypto_mac.c + * library/ssl_ciphersuites.c + * library/ssl_msg.c + * library/ssl_ticket.c (unless MBEDTLS_USE_PSA_CRYPTO is enabled) + * + * Uncomment to enable generic cipher wrappers. + */ +#define MBEDTLS_CIPHER_C + +/** + * \def MBEDTLS_CMAC_C + * + * Enable the CMAC (Cipher-based Message Authentication Code) mode for block + * ciphers. + * + * \note When #MBEDTLS_CMAC_ALT is active, meaning that the underlying + * implementation of the CMAC algorithm is provided by an alternate + * implementation, that alternate implementation may opt to not support + * AES-192 or 3DES as underlying block ciphers for the CMAC operation. + * + * Module: library/cmac.c + * + * Requires: MBEDTLS_CIPHER_C, MBEDTLS_AES_C or MBEDTLS_DES_C + * + */ +#define MBEDTLS_CMAC_C + +/** + * \def MBEDTLS_CTR_DRBG_C + * + * Enable the CTR_DRBG AES-based random generator. + * The CTR_DRBG generator uses AES-256 by default. + * To use AES-128 instead, enable \c MBEDTLS_CTR_DRBG_USE_128_BIT_KEY above. + * + * \note To achieve a 256-bit security strength with CTR_DRBG, + * you must use AES-256 *and* use sufficient entropy. + * See ctr_drbg.h for more details. + * + * Module: library/ctr_drbg.c + * Caller: + * + * Requires: MBEDTLS_AES_C + * + * This module provides the CTR_DRBG AES random number generator. + */ +#define MBEDTLS_CTR_DRBG_C + +/** + * \def MBEDTLS_DEBUG_C + * + * Enable the debug functions. + * + * Module: library/debug.c + * Caller: library/ssl_msg.c + * library/ssl_tls.c + * library/ssl_tls12_*.c + * library/ssl_tls13_*.c + * + * This module provides debugging functions. + */ +#define MBEDTLS_DEBUG_C + +/** + * \def MBEDTLS_DES_C + * + * Enable the DES block cipher. + * + * Module: library/des.c + * Caller: library/pem.c + * library/cipher.c + * + * PEM_PARSE uses DES/3DES for decrypting encrypted keys. + * + * \warning DES/3DES are considered weak ciphers and their use constitutes a + * security risk. We recommend considering stronger ciphers instead. + */ +#define MBEDTLS_DES_C + +/** + * \def MBEDTLS_DHM_C + * + * Enable the Diffie-Hellman-Merkle module. + * + * Module: library/dhm.c + * Caller: library/ssl_tls.c + * library/ssl*_client.c + * library/ssl*_server.c + * + * This module is used by the following key exchanges: + * DHE-RSA, DHE-PSK + * + * \warning Using DHE constitutes a security risk as it + * is not possible to validate custom DH parameters. + * If possible, it is recommended users should consider + * preferring other methods of key exchange. + * See dhm.h for more details. + * + */ +#define MBEDTLS_DHM_C + +/** + * \def MBEDTLS_ECDH_C + * + * Enable the elliptic curve Diffie-Hellman library. + * + * Module: library/ecdh.c + * Caller: library/psa_crypto.c + * library/ssl_tls.c + * library/ssl*_client.c + * library/ssl*_server.c + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA, ECDHE-RSA, DHE-PSK + * + * Requires: MBEDTLS_ECP_C + */ +#define MBEDTLS_ECDH_C + +/** + * \def MBEDTLS_ECDSA_C + * + * Enable the elliptic curve DSA library. + * + * Module: library/ecdsa.c + * Caller: + * + * This module is used by the following key exchanges: + * ECDHE-ECDSA + * + * Requires: MBEDTLS_ECP_C, MBEDTLS_ASN1_WRITE_C, MBEDTLS_ASN1_PARSE_C, + * and at least one MBEDTLS_ECP_DP_XXX_ENABLED for a + * short Weierstrass curve. + */ +#define MBEDTLS_ECDSA_C + +/** + * \def MBEDTLS_ECJPAKE_C + * + * Enable the elliptic curve J-PAKE library. + * + * \note EC J-PAKE support is based on the Thread v1.0.0 specification. + * It has not been reviewed for compliance with newer standards such as + * Thread v1.1 or RFC 8236. + * + * Module: library/ecjpake.c + * Caller: + * + * This module is used by the following key exchanges: + * ECJPAKE + * + * Requires: MBEDTLS_ECP_C and either MBEDTLS_MD_C or MBEDTLS_PSA_CRYPTO_C + * + * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init() + * before doing any EC J-PAKE operations. + * + * \warning When building with MBEDTLS_MD_C, all hashes used with this + * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C, + * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by + * this module in builds where MBEDTLS_MD_C is disabled. + */ +#define MBEDTLS_ECJPAKE_C + +/** + * \def MBEDTLS_ECP_C + * + * Enable the elliptic curve over GF(p) library. + * + * Module: library/ecp.c + * Caller: library/ecdh.c + * library/ecdsa.c + * library/ecjpake.c + * + * Requires: MBEDTLS_BIGNUM_C and at least one MBEDTLS_ECP_DP_XXX_ENABLED + */ +#define MBEDTLS_ECP_C + +/** + * \def MBEDTLS_ENTROPY_C + * + * Enable the platform-specific entropy code. + * + * Module: library/entropy.c + * Caller: + * + * Requires: MBEDTLS_SHA512_C or MBEDTLS_SHA256_C + * + * This module provides a generic entropy pool + */ +#define MBEDTLS_ENTROPY_C + +/** + * \def MBEDTLS_ERROR_C + * + * Enable error code to error string conversion. + * + * Module: library/error.c + * Caller: + * + * This module enables mbedtls_strerror(). + */ +#define MBEDTLS_ERROR_C + +/** + * \def MBEDTLS_GCM_C + * + * Enable the Galois/Counter Mode (GCM). + * + * Module: library/gcm.c + * + * Requires: MBEDTLS_CIPHER_C, MBEDTLS_AES_C or MBEDTLS_CAMELLIA_C or + * MBEDTLS_ARIA_C + * + * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other + * requisites are enabled as well. + */ +#define MBEDTLS_GCM_C + +/** + * \def MBEDTLS_HKDF_C + * + * Enable the HKDF algorithm (RFC 5869). + * + * Module: library/hkdf.c + * Caller: + * + * Requires: MBEDTLS_MD_C + * + * This module adds support for the Hashed Message Authentication Code + * (HMAC)-based key derivation function (HKDF). + */ +#define MBEDTLS_HKDF_C + +/** + * \def MBEDTLS_HMAC_DRBG_C + * + * Enable the HMAC_DRBG random generator. + * + * Module: library/hmac_drbg.c + * Caller: + * + * Requires: MBEDTLS_MD_C + * + * Uncomment to enable the HMAC_DRBG random number generator. + */ +#define MBEDTLS_HMAC_DRBG_C + +/** + * \def MBEDTLS_LMS_C + * + * Enable the LMS stateful-hash asymmetric signature algorithm. + * + * Module: library/lms.c + * Caller: + * + * Requires: MBEDTLS_PSA_CRYPTO_C + * + * Uncomment to enable the LMS verification algorithm and public key operations. + */ +//#define MBEDTLS_LMS_C + +/** + * \def MBEDTLS_LMS_PRIVATE + * + * Enable LMS private-key operations and signing code. Functions enabled by this + * option are experimental, and should not be used in production. + * + * Requires: MBEDTLS_LMS_C + * + * Uncomment to enable the LMS signature algorithm and private key operations. + */ +//#define MBEDTLS_LMS_PRIVATE + +/** + * \def MBEDTLS_NIST_KW_C + * + * Enable the Key Wrapping mode for 128-bit block ciphers, + * as defined in NIST SP 800-38F. Only KW and KWP modes + * are supported. At the moment, only AES is approved by NIST. + * + * Module: library/nist_kw.c + * + * Requires: MBEDTLS_AES_C and MBEDTLS_CIPHER_C + */ +#define MBEDTLS_NIST_KW_C + +/** + * \def MBEDTLS_MD_C + * + * Enable the generic layer for message digest (hashing) and HMAC. + * + * Requires: one of: MBEDTLS_MD5_C, MBEDTLS_RIPEMD160_C, MBEDTLS_SHA1_C, + * MBEDTLS_SHA224_C, MBEDTLS_SHA256_C, MBEDTLS_SHA384_C, + * MBEDTLS_SHA512_C. + * Module: library/md.c + * Caller: library/constant_time.c + * library/ecdsa.c + * library/ecjpake.c + * library/hkdf.c + * library/hmac_drbg.c + * library/pk.c + * library/pkcs5.c + * library/pkcs12.c + * library/psa_crypto_ecp.c + * library/psa_crypto_rsa.c + * library/rsa.c + * library/ssl_cookie.c + * library/ssl_msg.c + * library/ssl_tls.c + * library/x509.c + * library/x509_crt.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * Uncomment to enable generic message digest wrappers. + */ +#define MBEDTLS_MD_C + +/** + * \def MBEDTLS_MD5_C + * + * Enable the MD5 hash algorithm. + * + * Module: library/md5.c + * Caller: library/md.c + * library/pem.c + * library/ssl_tls.c + * + * This module is required for TLS 1.2 depending on the handshake parameters. + * Further, it is used for checking MD5-signed certificates, and for PBKDF1 + * when decrypting PEM-encoded encrypted keys. + * + * \warning MD5 is considered a weak message digest and its use constitutes a + * security risk. If possible, we recommend avoiding dependencies on + * it, and considering stronger message digests instead. + * + */ +#define MBEDTLS_MD5_C + +/** + * \def MBEDTLS_MEMORY_BUFFER_ALLOC_C + * + * Enable the buffer allocator implementation that makes use of a (stack) + * based buffer to 'allocate' dynamic memory. (replaces calloc() and free() + * calls) + * + * Module: library/memory_buffer_alloc.c + * + * Requires: MBEDTLS_PLATFORM_C + * MBEDTLS_PLATFORM_MEMORY (to use it within mbed TLS) + * + * Enable this module to enable the buffer memory allocator. + */ +//#define MBEDTLS_MEMORY_BUFFER_ALLOC_C + +/** + * \def MBEDTLS_NET_C + * + * Enable the TCP and UDP over IPv6/IPv4 networking routines. + * + * \note This module only works on POSIX/Unix (including Linux, BSD and OS X) + * and Windows. For other platforms, you'll want to disable it, and write your + * own networking callbacks to be passed to \c mbedtls_ssl_set_bio(). + * + * \note See also our Knowledge Base article about porting to a new + * environment: + * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * + * Module: library/net_sockets.c + * + * This module provides networking routines. + */ +#define MBEDTLS_NET_C + +/** + * \def MBEDTLS_OID_C + * + * Enable the OID database. + * + * Module: library/oid.c + * Caller: library/asn1write.c + * library/pkcs5.c + * library/pkparse.c + * library/pkwrite.c + * library/rsa.c + * library/x509.c + * library/x509_create.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * This modules translates between OIDs and internal values. + */ +#define MBEDTLS_OID_C + +/** + * \def MBEDTLS_PADLOCK_C + * + * Enable VIA Padlock support on x86. + * + * Module: library/padlock.c + * Caller: library/aes.c + * + * Requires: MBEDTLS_HAVE_ASM + * + * This modules adds support for the VIA PadLock on x86. + */ +#define MBEDTLS_PADLOCK_C + +/** + * \def MBEDTLS_PEM_PARSE_C + * + * Enable PEM decoding / parsing. + * + * Module: library/pem.c + * Caller: library/dhm.c + * library/pkparse.c + * library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_BASE64_C + * + * This modules adds support for decoding / parsing PEM files. + */ +#define MBEDTLS_PEM_PARSE_C + +/** + * \def MBEDTLS_PEM_WRITE_C + * + * Enable PEM encoding / writing. + * + * Module: library/pem.c + * Caller: library/pkwrite.c + * library/x509write_crt.c + * library/x509write_csr.c + * + * Requires: MBEDTLS_BASE64_C + * + * This modules adds support for encoding / writing PEM files. + */ +#define MBEDTLS_PEM_WRITE_C + +/** + * \def MBEDTLS_PK_C + * + * Enable the generic public (asymmetric) key layer. + * + * Module: library/pk.c + * Caller: library/psa_crypto_rsa.c + * library/ssl_tls.c + * library/ssl*_client.c + * library/ssl*_server.c + * library/x509.c + * + * Requires: MBEDTLS_MD_C, MBEDTLS_RSA_C or MBEDTLS_ECP_C + * + * Uncomment to enable generic public key wrappers. + */ +#define MBEDTLS_PK_C + +/** + * \def MBEDTLS_PK_PARSE_C + * + * Enable the generic public (asymmetric) key parser. + * + * Module: library/pkparse.c + * Caller: library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_PK_C + * + * Uncomment to enable generic public key parse functions. + */ +#define MBEDTLS_PK_PARSE_C + +/** + * \def MBEDTLS_PK_WRITE_C + * + * Enable the generic public (asymmetric) key writer. + * + * Module: library/pkwrite.c + * Caller: library/x509write.c + * + * Requires: MBEDTLS_PK_C + * + * Uncomment to enable generic public key write functions. + */ +#define MBEDTLS_PK_WRITE_C + +/** + * \def MBEDTLS_PKCS5_C + * + * Enable PKCS#5 functions. + * + * Module: library/pkcs5.c + * + * Requires: MBEDTLS_CIPHER_C and either MBEDTLS_MD_C or MBEDTLS_PSA_CRYPTO_C. + * + * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init() + * before doing any PKCS5 operation. + * + * \warning When building with MBEDTLS_MD_C, all hashes used with this + * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C, + * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by + * this module in builds where MBEDTLS_MD_C is disabled. + * + * This module adds support for the PKCS#5 functions. + */ +#define MBEDTLS_PKCS5_C + +/** + * \def MBEDTLS_PKCS7_C + * + * Enable PKCS #7 core for using PKCS #7-formatted signatures. + * RFC Link - https://tools.ietf.org/html/rfc2315 + * + * Module: library/pkcs7.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_OID_C, MBEDTLS_PK_PARSE_C, + * MBEDTLS_X509_CRT_PARSE_C MBEDTLS_X509_CRL_PARSE_C, + * MBEDTLS_BIGNUM_C, MBEDTLS_MD_C + * + * This module is required for the PKCS #7 parsing modules. + */ +#define MBEDTLS_PKCS7_C + +/** + * \def MBEDTLS_PKCS12_C + * + * Enable PKCS#12 PBE functions. + * Adds algorithms for parsing PKCS#8 encrypted private keys + * + * Module: library/pkcs12.c + * Caller: library/pkparse.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_CIPHER_C and either + * MBEDTLS_MD_C or MBEDTLS_PSA_CRYPTO_C. + * + * \warning If building without MBEDTLS_MD_C, you must call psa_crypto_init() + * before doing any PKCS12 operation. + * + * \warning When building with MBEDTLS_MD_C, all hashes used with this + * need to be available as built-ins (that is, for SHA-256, MBEDTLS_SHA256_C, + * etc.) as opposed to just PSA drivers. So far, PSA drivers are only used by + * this module in builds where MBEDTLS_MD_C is disabled. + * + * This module enables PKCS#12 functions. + */ +#define MBEDTLS_PKCS12_C + +/** + * \def MBEDTLS_PLATFORM_C + * + * Enable the platform abstraction layer that allows you to re-assign + * functions like calloc(), free(), snprintf(), printf(), fprintf(), exit(). + * + * Enabling MBEDTLS_PLATFORM_C enables to use of MBEDTLS_PLATFORM_XXX_ALT + * or MBEDTLS_PLATFORM_XXX_MACRO directives, allowing the functions mentioned + * above to be specified at runtime or compile time respectively. + * + * \note This abstraction layer must be enabled on Windows (including MSYS2) + * as other modules rely on it for a fixed snprintf implementation. + * + * Module: library/platform.c + * Caller: Most other .c files + * + * This module enables abstraction of common (libc) functions. + */ +#define MBEDTLS_PLATFORM_C + +/** + * \def MBEDTLS_POLY1305_C + * + * Enable the Poly1305 MAC algorithm. + * + * Module: library/poly1305.c + * Caller: library/chachapoly.c + */ +#define MBEDTLS_POLY1305_C + +/** + * \def MBEDTLS_PSA_CRYPTO_C + * + * Enable the Platform Security Architecture cryptography API. + * + * Module: library/psa_crypto.c + * + * Requires: MBEDTLS_CIPHER_C, + * either MBEDTLS_CTR_DRBG_C and MBEDTLS_ENTROPY_C, + * or MBEDTLS_HMAC_DRBG_C and MBEDTLS_ENTROPY_C, + * or MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG. + * + */ +//#define MBEDTLS_PSA_CRYPTO_C + +/** + * \def MBEDTLS_PSA_CRYPTO_SE_C + * + * Enable dynamic secure element support in the Platform Security Architecture + * cryptography API. + * + * \deprecated This feature is deprecated. Please switch to the driver + * interface enabled by #MBEDTLS_PSA_CRYPTO_DRIVERS. + * + * Module: library/psa_crypto_se.c + * + * Requires: MBEDTLS_PSA_CRYPTO_C, MBEDTLS_PSA_CRYPTO_STORAGE_C + * + */ +//#define MBEDTLS_PSA_CRYPTO_SE_C + +/** + * \def MBEDTLS_PSA_CRYPTO_STORAGE_C + * + * Enable the Platform Security Architecture persistent key storage. + * + * Module: library/psa_crypto_storage.c + * + * Requires: MBEDTLS_PSA_CRYPTO_C, + * either MBEDTLS_PSA_ITS_FILE_C or a native implementation of + * the PSA ITS interface + */ +//#define MBEDTLS_PSA_CRYPTO_STORAGE_C + +/** + * \def MBEDTLS_PSA_ITS_FILE_C + * + * Enable the emulation of the Platform Security Architecture + * Internal Trusted Storage (PSA ITS) over files. + * + * Module: library/psa_its_file.c + * + * Requires: MBEDTLS_FS_IO + */ +#define MBEDTLS_PSA_ITS_FILE_C + +/** + * \def MBEDTLS_RIPEMD160_C + * + * Enable the RIPEMD-160 hash algorithm. + * + * Module: library/ripemd160.c + * Caller: library/md.c + * + */ +#define MBEDTLS_RIPEMD160_C + +/** + * \def MBEDTLS_RSA_C + * + * Enable the RSA public-key cryptosystem. + * + * Module: library/rsa.c + * library/rsa_alt_helpers.c + * Caller: library/pk.c + * library/psa_crypto.c + * library/ssl_tls.c + * library/ssl*_client.c + * library/ssl*_server.c + * + * This module is used by the following key exchanges: + * RSA, DHE-RSA, ECDHE-RSA, RSA-PSK + * + * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C + */ +#define MBEDTLS_RSA_C + +/** + * \def MBEDTLS_SHA1_C + * + * Enable the SHA1 cryptographic hash algorithm. + * + * Module: library/sha1.c + * Caller: library/md.c + * library/psa_crypto_hash.c + * + * This module is required for TLS 1.2 depending on the handshake parameters, + * and for SHA1-signed certificates. + * + * \warning SHA-1 is considered a weak message digest and its use constitutes + * a security risk. If possible, we recommend avoiding dependencies + * on it, and considering stronger message digests instead. + * + */ +#define MBEDTLS_SHA1_C + +/** + * \def MBEDTLS_SHA224_C + * + * Enable the SHA-224 cryptographic hash algorithm. + * + * Module: library/sha256.c + * Caller: library/md.c + * library/ssl_cookie.c + * + * This module adds support for SHA-224. + */ +#define MBEDTLS_SHA224_C + +/** + * \def MBEDTLS_SHA256_C + * + * Enable the SHA-256 cryptographic hash algorithm. + * + * Module: library/sha256.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_tls.c + * library/ssl*_client.c + * library/ssl*_server.c + * + * This module adds support for SHA-256. + * This module is required for the SSL/TLS 1.2 PRF function. + */ +#define MBEDTLS_SHA256_C + +/** + * \def MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT + * + * Enable acceleration of the SHA-256 and SHA-224 cryptographic hash algorithms + * with the ARMv8 cryptographic extensions if they are available at runtime. + * If not, the library will fall back to the C implementation. + * + * \note If MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT is defined when building + * for a non-Aarch64 build it will be silently ignored. + * + * \warning MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT cannot be defined at the + * same time as MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY. + * + * Requires: MBEDTLS_SHA256_C. + * + * Module: library/sha256.c + * + * Uncomment to have the library check for the A64 SHA-256 crypto extensions + * and use them if available. + */ +//#define MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT + +/** + * \def MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY + * + * Enable acceleration of the SHA-256 and SHA-224 cryptographic hash algorithms + * with the ARMv8 cryptographic extensions, which must be available at runtime + * or else an illegal instruction fault will occur. + * + * \note This allows builds with a smaller code size than with + * MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT + * + * \warning MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY cannot be defined at the same + * time as MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT. + * + * Requires: MBEDTLS_SHA256_C. + * + * Module: library/sha256.c + * + * Uncomment to have the library use the A64 SHA-256 crypto extensions + * unconditionally. + */ +//#define MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY + +/** + * \def MBEDTLS_SHA384_C + * + * Enable the SHA-384 cryptographic hash algorithm. + * + * Module: library/sha512.c + * Caller: library/md.c + * library/psa_crypto_hash.c + * library/ssl_tls.c + * library/ssl*_client.c + * library/ssl*_server.c + * + * Comment to disable SHA-384 + */ +#define MBEDTLS_SHA384_C + +/** + * \def MBEDTLS_SHA512_C + * + * Enable SHA-512 cryptographic hash algorithms. + * + * Module: library/sha512.c + * Caller: library/entropy.c + * library/md.c + * library/ssl_tls.c + * library/ssl_cookie.c + * + * This module adds support for SHA-512. + */ +#define MBEDTLS_SHA512_C + +/** + * \def MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT + * + * Enable acceleration of the SHA-512 and SHA-384 cryptographic hash algorithms + * with the ARMv8 cryptographic extensions if they are available at runtime. + * If not, the library will fall back to the C implementation. + * + * \note If MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT is defined when building + * for a non-Aarch64 build it will be silently ignored. + * + * \note The code uses the SHA-512 Neon intrinsics, so requires GCC >= 8 or + * Clang >= 7. + * + * \warning MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT cannot be defined at the + * same time as MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY. + * + * Requires: MBEDTLS_SHA512_C. + * + * Module: library/sha512.c + * + * Uncomment to have the library check for the A64 SHA-512 crypto extensions + * and use them if available. + */ +//#define MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT + +/** + * \def MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY + * + * Enable acceleration of the SHA-512 and SHA-384 cryptographic hash algorithms + * with the ARMv8 cryptographic extensions, which must be available at runtime + * or else an illegal instruction fault will occur. + * + * \note This allows builds with a smaller code size than with + * MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT + * + * \note The code uses the SHA-512 Neon intrinsics, so requires GCC >= 8 or + * Clang >= 7. + * + * \warning MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY cannot be defined at the same + * time as MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT. + * + * Requires: MBEDTLS_SHA512_C. + * + * Module: library/sha512.c + * + * Uncomment to have the library use the A64 SHA-512 crypto extensions + * unconditionally. + */ +//#define MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY + +/** + * \def MBEDTLS_SSL_CACHE_C + * + * Enable simple SSL cache implementation. + * + * Module: library/ssl_cache.c + * Caller: + * + * Requires: MBEDTLS_SSL_CACHE_C + */ +#define MBEDTLS_SSL_CACHE_C + +/** + * \def MBEDTLS_SSL_COOKIE_C + * + * Enable basic implementation of DTLS cookies for hello verification. + * + * Module: library/ssl_cookie.c + * Caller: + */ +#define MBEDTLS_SSL_COOKIE_C + +/** + * \def MBEDTLS_SSL_TICKET_C + * + * Enable an implementation of TLS server-side callbacks for session tickets. + * + * Module: library/ssl_ticket.c + * Caller: + * + * Requires: (MBEDTLS_CIPHER_C || MBEDTLS_USE_PSA_CRYPTO) && + * (MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C) + */ +#define MBEDTLS_SSL_TICKET_C + +/** + * \def MBEDTLS_SSL_CLI_C + * + * Enable the SSL/TLS client code. + * + * Module: library/ssl*_client.c + * Caller: + * + * Requires: MBEDTLS_SSL_TLS_C + * + * This module is required for SSL/TLS client support. + */ +#define MBEDTLS_SSL_CLI_C + +/** + * \def MBEDTLS_SSL_SRV_C + * + * Enable the SSL/TLS server code. + * + * Module: library/ssl*_server.c + * Caller: + * + * Requires: MBEDTLS_SSL_TLS_C + * + * This module is required for SSL/TLS server support. + */ +#define MBEDTLS_SSL_SRV_C + +/** + * \def MBEDTLS_SSL_TLS_C + * + * Enable the generic SSL/TLS code. + * + * Module: library/ssl_tls.c + * Caller: library/ssl*_client.c + * library/ssl*_server.c + * + * Requires: MBEDTLS_CIPHER_C, MBEDTLS_MD_C + * and at least one of the MBEDTLS_SSL_PROTO_XXX defines + * + * This module is required for SSL/TLS. + */ +#define MBEDTLS_SSL_TLS_C + +/** + * \def MBEDTLS_THREADING_C + * + * Enable the threading abstraction layer. + * By default mbed TLS assumes it is used in a non-threaded environment or that + * contexts are not shared between threads. If you do intend to use contexts + * between threads, you will need to enable this layer to prevent race + * conditions. See also our Knowledge Base article about threading: + * https://mbed-tls.readthedocs.io/en/latest/kb/development/thread-safety-and-multi-threading + * + * Module: library/threading.c + * + * This allows different threading implementations (self-implemented or + * provided). + * + * You will have to enable either MBEDTLS_THREADING_ALT or + * MBEDTLS_THREADING_PTHREAD. + * + * Enable this layer to allow use of mutexes within mbed TLS + */ +//#define MBEDTLS_THREADING_C + +/** + * \def MBEDTLS_TIMING_C + * + * Enable the semi-portable timing interface. + * + * \note The provided implementation only works on POSIX/Unix (including Linux, + * BSD and OS X) and Windows. On other platforms, you can either disable that + * module and provide your own implementations of the callbacks needed by + * \c mbedtls_ssl_set_timer_cb() for DTLS, or leave it enabled and provide + * your own implementation of the whole module by setting + * \c MBEDTLS_TIMING_ALT in the current file. + * + * \note The timing module will include time.h on suitable platforms + * regardless of the setting of MBEDTLS_HAVE_TIME, unless + * MBEDTLS_TIMING_ALT is used. See timing.c for more information. + * + * \note See also our Knowledge Base article about porting to a new + * environment: + * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/how-do-i-port-mbed-tls-to-a-new-environment-OS + * + * Module: library/timing.c + */ +#define MBEDTLS_TIMING_C + +/** + * \def MBEDTLS_VERSION_C + * + * Enable run-time version information. + * + * Module: library/version.c + * + * This module provides run-time version information. + */ +#define MBEDTLS_VERSION_C + +/** + * \def MBEDTLS_X509_USE_C + * + * Enable X.509 core for using certificates. + * + * Module: library/x509.c + * Caller: library/x509_crl.c + * library/x509_crt.c + * library/x509_csr.c + * + * Requires: MBEDTLS_ASN1_PARSE_C, MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, MBEDTLS_PK_PARSE_C, + * (MBEDTLS_MD_C or MBEDTLS_USE_PSA_CRYPTO) + * + * \warning If building with MBEDTLS_USE_PSA_CRYPTO, you must call + * psa_crypto_init() before doing any X.509 operation. + * + * This module is required for the X.509 parsing modules. + */ +#define MBEDTLS_X509_USE_C + +/** + * \def MBEDTLS_X509_CRT_PARSE_C + * + * Enable X.509 certificate parsing. + * + * Module: library/x509_crt.c + * Caller: library/ssl_tls.c + * library/ssl*_client.c + * library/ssl*_server.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is required for X.509 certificate parsing. + */ +#define MBEDTLS_X509_CRT_PARSE_C + +/** + * \def MBEDTLS_X509_CRL_PARSE_C + * + * Enable X.509 CRL parsing. + * + * Module: library/x509_crl.c + * Caller: library/x509_crt.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is required for X.509 CRL parsing. + */ +#define MBEDTLS_X509_CRL_PARSE_C + +/** + * \def MBEDTLS_X509_CSR_PARSE_C + * + * Enable X.509 Certificate Signing Request (CSR) parsing. + * + * Module: library/x509_csr.c + * Caller: library/x509_crt_write.c + * + * Requires: MBEDTLS_X509_USE_C + * + * This module is used for reading X.509 certificate request. + */ +#define MBEDTLS_X509_CSR_PARSE_C + +/** + * \def MBEDTLS_X509_CREATE_C + * + * Enable X.509 core for creating certificates. + * + * Module: library/x509_create.c + * + * Requires: MBEDTLS_BIGNUM_C, MBEDTLS_OID_C, MBEDTLS_PK_PARSE_C, + * (MBEDTLS_MD_C or MBEDTLS_USE_PSA_CRYPTO) + * + * \warning If building with MBEDTLS_USE_PSA_CRYPTO, you must call + * psa_crypto_init() before doing any X.509 create operation. + * + * This module is the basis for creating X.509 certificates and CSRs. + */ +#define MBEDTLS_X509_CREATE_C + +/** + * \def MBEDTLS_X509_CRT_WRITE_C + * + * Enable creating X.509 certificates. + * + * Module: library/x509_crt_write.c + * + * Requires: MBEDTLS_X509_CREATE_C + * + * This module is required for X.509 certificate creation. + */ +#define MBEDTLS_X509_CRT_WRITE_C + +/** + * \def MBEDTLS_X509_CSR_WRITE_C + * + * Enable creating X.509 Certificate Signing Requests (CSR). + * + * Module: library/x509_csr_write.c + * + * Requires: MBEDTLS_X509_CREATE_C + * + * This module is required for X.509 certificate request writing. + */ +#define MBEDTLS_X509_CSR_WRITE_C + +/** \} name SECTION: mbed TLS modules */ + +/** + * \name SECTION: General configuration options + * + * This section contains Mbed TLS build settings that are not associated + * with a particular module. + * + * \{ + */ + +/** + * \def MBEDTLS_CONFIG_FILE + * + * If defined, this is a header which will be included instead of + * `"mbedtls/mbedtls_config.h"`. + * This header file specifies the compile-time configuration of Mbed TLS. + * Unlike other configuration options, this one must be defined on the + * compiler command line: a definition in `mbedtls_config.h` would have + * no effect. + * + * This macro is expanded after an \#include directive. This is a popular but + * non-standard feature of the C language, so this feature is only available + * with compilers that perform macro expansion on an \#include line. + * + * The value of this symbol is typically a path in double quotes, either + * absolute or relative to a directory on the include search path. + */ +//#define MBEDTLS_CONFIG_FILE "mbedtls/mbedtls_config.h" + +/** + * \def MBEDTLS_USER_CONFIG_FILE + * + * If defined, this is a header which will be included after + * `"mbedtls/mbedtls_config.h"` or #MBEDTLS_CONFIG_FILE. + * This allows you to modify the default configuration, including the ability + * to undefine options that are enabled by default. + * + * This macro is expanded after an \#include directive. This is a popular but + * non-standard feature of the C language, so this feature is only available + * with compilers that perform macro expansion on an \#include line. + * + * The value of this symbol is typically a path in double quotes, either + * absolute or relative to a directory on the include search path. + */ +//#define MBEDTLS_USER_CONFIG_FILE "/dev/null" + +/** + * \def MBEDTLS_PSA_CRYPTO_CONFIG_FILE + * + * If defined, this is a header which will be included instead of + * `"psa/crypto_config.h"`. + * This header file specifies which cryptographic mechanisms are available + * through the PSA API when #MBEDTLS_PSA_CRYPTO_CONFIG is enabled, and + * is not used when #MBEDTLS_PSA_CRYPTO_CONFIG is disabled. + * + * This macro is expanded after an \#include directive. This is a popular but + * non-standard feature of the C language, so this feature is only available + * with compilers that perform macro expansion on an \#include line. + * + * The value of this symbol is typically a path in double quotes, either + * absolute or relative to a directory on the include search path. + */ +//#define MBEDTLS_PSA_CRYPTO_CONFIG_FILE "psa/crypto_config.h" + +/** + * \def MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE + * + * If defined, this is a header which will be included after + * `"psa/crypto_config.h"` or #MBEDTLS_PSA_CRYPTO_CONFIG_FILE. + * This allows you to modify the default configuration, including the ability + * to undefine options that are enabled by default. + * + * This macro is expanded after an \#include directive. This is a popular but + * non-standard feature of the C language, so this feature is only available + * with compilers that perform macro expansion on an \#include line. + * + * The value of this symbol is typically a path in double quotes, either + * absolute or relative to a directory on the include search path. + */ +//#define MBEDTLS_PSA_CRYPTO_USER_CONFIG_FILE "/dev/null" + +/** + * \def MBEDTLS_PSA_CRYPTO_PLATFORM_FILE + * + * If defined, this is a header which will be included instead of + * `"psa/crypto_platform.h"`. This file should declare the same identifiers + * as the one in Mbed TLS, but with definitions adapted to the platform on + * which the library code will run. + * + * \note The required content of this header can vary from one version of + * Mbed TLS to the next. Integrators who provide an alternative file + * should review the changes in the original file whenever they + * upgrade Mbed TLS. + * + * This macro is expanded after an \#include directive. This is a popular but + * non-standard feature of the C language, so this feature is only available + * with compilers that perform macro expansion on an \#include line. + * + * The value of this symbol is typically a path in double quotes, either + * absolute or relative to a directory on the include search path. + */ +//#define MBEDTLS_PSA_CRYPTO_PLATFORM_FILE "psa/crypto_platform_alt.h" + +/** + * \def MBEDTLS_PSA_CRYPTO_STRUCT_FILE + * + * If defined, this is a header which will be included instead of + * `"psa/crypto_struct.h"`. This file should declare the same identifiers + * as the one in Mbed TLS, but with definitions adapted to the environment + * in which the library code will run. The typical use for this feature + * is to provide alternative type definitions on the client side in + * client-server integrations of PSA crypto, where operation structures + * contain handles instead of cryptographic data. + * + * \note The required content of this header can vary from one version of + * Mbed TLS to the next. Integrators who provide an alternative file + * should review the changes in the original file whenever they + * upgrade Mbed TLS. + * + * This macro is expanded after an \#include directive. This is a popular but + * non-standard feature of the C language, so this feature is only available + * with compilers that perform macro expansion on an \#include line. + * + * The value of this symbol is typically a path in double quotes, either + * absolute or relative to a directory on the include search path. + */ +//#define MBEDTLS_PSA_CRYPTO_STRUCT_FILE "psa/crypto_struct_alt.h" + +/** \} name SECTION: General configuration options */ + +/** + * \name SECTION: Module configuration options + * + * This section allows for the setting of module specific sizes and + * configuration options. The default values are already present in the + * relevant header files and should suffice for the regular use cases. + * + * Our advice is to enable options and change their values here + * only if you have a good reason and know the consequences. + * \{ + */ +/* The Doxygen documentation here is used when a user comments out a + * setting and runs doxygen themselves. On the other hand, when we typeset + * the full documentation including disabled settings, the documentation + * in specific modules' header files is used if present. When editing this + * file, make sure that each option is documented in exactly one place, + * plus optionally a same-line Doxygen comment here if there is a Doxygen + * comment in the specific module. */ + +/* MPI / BIGNUM options */ +//#define MBEDTLS_MPI_WINDOW_SIZE 2 /**< Maximum window size used. */ +//#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */ + +/* CTR_DRBG options */ +//#define MBEDTLS_CTR_DRBG_ENTROPY_LEN 48 /**< Amount of entropy used per seed by default (48 with SHA-512, 32 with SHA-256) */ +//#define MBEDTLS_CTR_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define MBEDTLS_CTR_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define MBEDTLS_CTR_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define MBEDTLS_CTR_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* HMAC_DRBG options */ +//#define MBEDTLS_HMAC_DRBG_RESEED_INTERVAL 10000 /**< Interval before reseed is performed by default */ +//#define MBEDTLS_HMAC_DRBG_MAX_INPUT 256 /**< Maximum number of additional input bytes */ +//#define MBEDTLS_HMAC_DRBG_MAX_REQUEST 1024 /**< Maximum number of requested bytes per call */ +//#define MBEDTLS_HMAC_DRBG_MAX_SEED_INPUT 384 /**< Maximum size of (re)seed buffer */ + +/* ECP options */ +//#define MBEDTLS_ECP_WINDOW_SIZE 4 /**< Maximum window size used */ +//#define MBEDTLS_ECP_FIXED_POINT_OPTIM 1 /**< Enable fixed-point speed-up */ + +/* Entropy options */ +//#define MBEDTLS_ENTROPY_MAX_SOURCES 20 /**< Maximum number of sources supported */ +//#define MBEDTLS_ENTROPY_MAX_GATHER 128 /**< Maximum amount requested from entropy sources */ +//#define MBEDTLS_ENTROPY_MIN_HARDWARE 32 /**< Default minimum number of bytes required for the hardware entropy source mbedtls_hardware_poll() before entropy is released */ + +/* Memory buffer allocator options */ +//#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ + +/* Platform options */ +//#define MBEDTLS_PLATFORM_STD_MEM_HDR /**< Header to include if MBEDTLS_PLATFORM_NO_STD_FUNCTIONS is defined. Don't define if no header is needed. */ +//#define MBEDTLS_PLATFORM_STD_CALLOC calloc /**< Default allocator to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_FREE free /**< Default free to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_SETBUF setbuf /**< Default setbuf to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT exit /**< Default exit to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_TIME time /**< Default time to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_STD_FPRINTF fprintf /**< Default fprintf to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_PRINTF printf /**< Default printf to use, can be undefined */ +/* Note: your snprintf must correctly zero-terminate the buffer! */ +//#define MBEDTLS_PLATFORM_STD_SNPRINTF snprintf /**< Default snprintf to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT_SUCCESS 0 /**< Default exit value to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_EXIT_FAILURE 1 /**< Default exit value to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_READ mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_WRITE mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_STD_NV_SEED_FILE "seedfile" /**< Seed file to read/write with default implementation */ + +/* To Use Function Macros MBEDTLS_PLATFORM_C must be enabled */ +/* MBEDTLS_PLATFORM_XXX_MACRO and MBEDTLS_PLATFORM_XXX_ALT cannot both be defined */ +//#define MBEDTLS_PLATFORM_CALLOC_MACRO calloc /**< Default allocator macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_FREE_MACRO free /**< Default free macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_EXIT_MACRO exit /**< Default exit macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_SETBUF_MACRO setbuf /**< Default setbuf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_TIME_MACRO time /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_TIME_TYPE_MACRO time_t /**< Default time macro to use, can be undefined. MBEDTLS_HAVE_TIME must be enabled */ +//#define MBEDTLS_PLATFORM_FPRINTF_MACRO fprintf /**< Default fprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_PRINTF_MACRO printf /**< Default printf macro to use, can be undefined */ +/* Note: your snprintf must correctly zero-terminate the buffer! */ +//#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf /**< Default snprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_VSNPRINTF_MACRO vsnprintf /**< Default vsnprintf macro to use, can be undefined */ +//#define MBEDTLS_PLATFORM_NV_SEED_READ_MACRO mbedtls_platform_std_nv_seed_read /**< Default nv_seed_read function to use, can be undefined */ +//#define MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO mbedtls_platform_std_nv_seed_write /**< Default nv_seed_write function to use, can be undefined */ + +/** \def MBEDTLS_CHECK_RETURN + * + * This macro is used at the beginning of the declaration of a function + * to indicate that its return value should be checked. It should + * instruct the compiler to emit a warning or an error if the function + * is called without checking its return value. + * + * There is a default implementation for popular compilers in platform_util.h. + * You can override the default implementation by defining your own here. + * + * If the implementation here is empty, this will effectively disable the + * checking of functions' return values. + */ +//#define MBEDTLS_CHECK_RETURN __attribute__((__warn_unused_result__)) + +/** \def MBEDTLS_IGNORE_RETURN + * + * This macro requires one argument, which should be a C function call. + * If that function call would cause a #MBEDTLS_CHECK_RETURN warning, this + * warning is suppressed. + */ +//#define MBEDTLS_IGNORE_RETURN( result ) ((void) !(result)) + +/* PSA options */ +/** + * Use HMAC_DRBG with the specified hash algorithm for HMAC_DRBG for the + * PSA crypto subsystem. + * + * If this option is unset: + * - If CTR_DRBG is available, the PSA subsystem uses it rather than HMAC_DRBG. + * - Otherwise, the PSA subsystem uses HMAC_DRBG with either + * #MBEDTLS_MD_SHA512 or #MBEDTLS_MD_SHA256 based on availability and + * on unspecified heuristics. + */ +//#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA256 + +/** \def MBEDTLS_PSA_KEY_SLOT_COUNT + * Restrict the PSA library to supporting a maximum amount of simultaneously + * loaded keys. A loaded key is a key stored by the PSA Crypto core as a + * volatile key, or a persistent key which is loaded temporarily by the + * library as part of a crypto operation in flight. + * + * If this option is unset, the library will fall back to a default value of + * 32 keys. + */ +//#define MBEDTLS_PSA_KEY_SLOT_COUNT 32 + +/* SSL Cache options */ +//#define MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT 86400 /**< 1 day */ +//#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /**< Maximum entries in cache */ + +/* SSL options */ + +/** \def MBEDTLS_SSL_IN_CONTENT_LEN + * + * Maximum length (in bytes) of incoming plaintext fragments. + * + * This determines the size of the incoming TLS I/O buffer in such a way + * that it is capable of holding the specified amount of plaintext data, + * regardless of the protection mechanism used. + * + * \note When using a value less than the default of 16KB on the client, it is + * recommended to use the Maximum Fragment Length (MFL) extension to + * inform the server about this limitation. On the server, there + * is no supported, standardized way of informing the client about + * restriction on the maximum size of incoming messages, and unless + * the limitation has been communicated by other means, it is recommended + * to only change the outgoing buffer size #MBEDTLS_SSL_OUT_CONTENT_LEN + * while keeping the default value of 16KB for the incoming buffer. + * + * Uncomment to set the maximum plaintext size of the incoming I/O buffer. + */ +//#define MBEDTLS_SSL_IN_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_CID_IN_LEN_MAX + * + * The maximum length of CIDs used for incoming DTLS messages. + * + */ +//#define MBEDTLS_SSL_CID_IN_LEN_MAX 32 + +/** \def MBEDTLS_SSL_CID_OUT_LEN_MAX + * + * The maximum length of CIDs used for outgoing DTLS messages. + * + */ +//#define MBEDTLS_SSL_CID_OUT_LEN_MAX 32 + +/** \def MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY + * + * This option controls the use of record plaintext padding + * in TLS 1.3 and when using the Connection ID extension in DTLS 1.2. + * + * The padding will always be chosen so that the length of the + * padded plaintext is a multiple of the value of this option. + * + * Note: A value of \c 1 means that no padding will be used + * for outgoing records. + * + * Note: On systems lacking division instructions, + * a power of two should be preferred. + */ +//#define MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY 16 + +/** \def MBEDTLS_SSL_OUT_CONTENT_LEN + * + * Maximum length (in bytes) of outgoing plaintext fragments. + * + * This determines the size of the outgoing TLS I/O buffer in such a way + * that it is capable of holding the specified amount of plaintext data, + * regardless of the protection mechanism used. + * + * It is possible to save RAM by setting a smaller outward buffer, while keeping + * the default inward 16384 byte buffer to conform to the TLS specification. + * + * The minimum required outward buffer size is determined by the handshake + * protocol's usage. Handshaking will fail if the outward buffer is too small. + * The specific size requirement depends on the configured ciphers and any + * certificate data which is sent during the handshake. + * + * Uncomment to set the maximum plaintext size of the outgoing I/O buffer. + */ +//#define MBEDTLS_SSL_OUT_CONTENT_LEN 16384 + +/** \def MBEDTLS_SSL_DTLS_MAX_BUFFERING + * + * Maximum number of heap-allocated bytes for the purpose of + * DTLS handshake message reassembly and future message buffering. + * + * This should be at least 9/8 * MBEDTLS_SSL_IN_CONTENT_LEN + * to account for a reassembled handshake message of maximum size, + * together with its reassembly bitmap. + * + * A value of 2 * MBEDTLS_SSL_IN_CONTENT_LEN (32768 by default) + * should be sufficient for all practical situations as it allows + * to reassembly a large handshake message (such as a certificate) + * while buffering multiple smaller handshake messages. + * + */ +//#define MBEDTLS_SSL_DTLS_MAX_BUFFERING 32768 + +//#define MBEDTLS_PSK_MAX_LEN 32 /**< Max size of TLS pre-shared keys, in bytes (default 256 or 384 bits) */ +//#define MBEDTLS_SSL_COOKIE_TIMEOUT 60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */ + +/** + * Complete list of ciphersuites to use, in order of preference. + * + * \warning No dependency checking is done on that field! This option can only + * be used to restrict the set of available ciphersuites. It is your + * responsibility to make sure the needed modules are active. + * + * Use this to save a few hundred bytes of ROM (default ordering of all + * available ciphersuites) and a few to a few hundred bytes of RAM. + * + * The value below is only an example, not the default. + */ +//#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + +/** + * \def MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE + * + * Maximum time difference in milliseconds tolerated between the age of a + * ticket from the server and client point of view. + * From the client point of view, the age of a ticket is the time difference + * between the time when the client proposes to the server to use the ticket + * (time of writing of the Pre-Shared Key Extension including the ticket) and + * the time the client received the ticket from the server. + * From the server point of view, the age of a ticket is the time difference + * between the time when the server receives a proposition from the client + * to use the ticket and the time when the ticket was created by the server. + * The server age is expected to be always greater than the client one and + * MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE defines the + * maximum difference tolerated for the server to accept the ticket. + * This is not used in TLS 1.2. + * + */ +#define MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE 6000 + +/** + * \def MBEDTLS_SSL_TLS1_3_TICKET_NONCE_LENGTH + * + * Size in bytes of a ticket nonce. This is not used in TLS 1.2. + * + * This must be less than 256. + */ +#define MBEDTLS_SSL_TLS1_3_TICKET_NONCE_LENGTH 32 + +/** + * \def MBEDTLS_SSL_TLS1_3_DEFAULT_NEW_SESSION_TICKETS + * + * Default number of NewSessionTicket messages to be sent by a TLS 1.3 server + * after handshake completion. This is not used in TLS 1.2 and relevant only if + * the MBEDTLS_SSL_SESSION_TICKETS option is enabled. + * + */ +#define MBEDTLS_SSL_TLS1_3_DEFAULT_NEW_SESSION_TICKETS 1 + +/* X509 options */ +//#define MBEDTLS_X509_MAX_INTERMEDIATE_CA 8 /**< Maximum number of intermediate CAs in a verification chain. */ +//#define MBEDTLS_X509_MAX_FILE_PATH_LEN 512 /**< Maximum length of a path/filename string in bytes including the null terminator character ('\0'). */ + +/** + * Uncomment the macro to let mbed TLS use your alternate implementation of + * mbedtls_platform_zeroize(). This replaces the default implementation in + * platform_util.c. + * + * mbedtls_platform_zeroize() is a widely used function across the library to + * zero a block of memory. The implementation is expected to be secure in the + * sense that it has been written to prevent the compiler from removing calls + * to mbedtls_platform_zeroize() as part of redundant code elimination + * optimizations. However, it is difficult to guarantee that calls to + * mbedtls_platform_zeroize() will not be optimized by the compiler as older + * versions of the C language standards do not provide a secure implementation + * of memset(). Therefore, MBEDTLS_PLATFORM_ZEROIZE_ALT enables users to + * configure their own implementation of mbedtls_platform_zeroize(), for + * example by using directives specific to their compiler, features from newer + * C standards (e.g using memset_s() in C11) or calling a secure memset() from + * their system (e.g explicit_bzero() in BSD). + */ +//#define MBEDTLS_PLATFORM_ZEROIZE_ALT + +/** + * Uncomment the macro to let Mbed TLS use your alternate implementation of + * mbedtls_platform_gmtime_r(). This replaces the default implementation in + * platform_util.c. + * + * gmtime() is not a thread-safe function as defined in the C standard. The + * library will try to use safer implementations of this function, such as + * gmtime_r() when available. However, if Mbed TLS cannot identify the target + * system, the implementation of mbedtls_platform_gmtime_r() will default to + * using the standard gmtime(). In this case, calls from the library to + * gmtime() will be guarded by the global mutex mbedtls_threading_gmtime_mutex + * if MBEDTLS_THREADING_C is enabled. We recommend that calls from outside the + * library are also guarded with this mutex to avoid race conditions. However, + * if the macro MBEDTLS_PLATFORM_GMTIME_R_ALT is defined, Mbed TLS will + * unconditionally use the implementation for mbedtls_platform_gmtime_r() + * supplied at compile time. + */ +//#define MBEDTLS_PLATFORM_GMTIME_R_ALT + +/** + * Enable the verified implementations of ECDH primitives from Project Everest + * (currently only Curve25519). This feature changes the layout of ECDH + * contexts and therefore is a compatibility break for applications that access + * fields of a mbedtls_ecdh_context structure directly. See also + * MBEDTLS_ECDH_LEGACY_CONTEXT in include/mbedtls/ecdh.h. + */ +//#define MBEDTLS_ECDH_VARIANT_EVEREST_ENABLED + +/** \} name SECTION: Module configuration options */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/md.h b/r5dev/thirdparty/mbedtls/include/mbedtls/md.h new file mode 100644 index 00000000..7bad24dc --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/md.h @@ -0,0 +1,588 @@ +/** + * \file md.h + * + * \brief This file contains the generic functions for message-digest + * (hashing) and HMAC. + * + * \author Adriaan de Jong + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_MD_H +#define MBEDTLS_MD_H +#include "mbedtls/private_access.h" + +#include + +#include "mbedtls/build_info.h" +#include "mbedtls/platform_util.h" + +#if defined(MBEDTLS_MD_LIGHT) + +/* + * - MBEDTLS_MD_CAN_xxx is defined if the md module can perform xxx. + * - MBEDTLS_MD_xxx_VIA_PSA is defined if the md module may perform xxx via PSA + * (see below). + * - MBEDTLS_MD_SOME_PSA is defined if at least one algorithm may be performed + * via PSA (see below). + * - MBEDTLS_MD_SOME_LEGACY is defined if at least one algorithm may be performed + * via a direct legacy call (see below). + * + * The md module performs an algorithm via PSA if there is a PSA hash + * accelerator and the PSA driver subsytem is initialized at the time the + * operation is started, and makes a direct legacy call otherwise. + */ + +/* PSA accelerated implementations */ +#if defined(MBEDTLS_PSA_CRYPTO_C) +#if defined(MBEDTLS_PSA_ACCEL_ALG_MD5) +#define MBEDTLS_MD_CAN_MD5 +#define MBEDTLS_MD_MD5_VIA_PSA +#define MBEDTLS_MD_SOME_PSA +#endif +#if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_1) +#define MBEDTLS_MD_CAN_SHA1 +#define MBEDTLS_MD_SHA1_VIA_PSA +#define MBEDTLS_MD_SOME_PSA +#endif +#if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_224) +#define MBEDTLS_MD_CAN_SHA224 +#define MBEDTLS_MD_SHA224_VIA_PSA +#define MBEDTLS_MD_SOME_PSA +#endif +#if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_256) +#define MBEDTLS_MD_CAN_SHA256 +#define MBEDTLS_MD_SHA256_VIA_PSA +#define MBEDTLS_MD_SOME_PSA +#endif +#if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_384) +#define MBEDTLS_MD_CAN_SHA384 +#define MBEDTLS_MD_SHA384_VIA_PSA +#define MBEDTLS_MD_SOME_PSA +#endif +#if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_512) +#define MBEDTLS_MD_CAN_SHA512 +#define MBEDTLS_MD_SHA512_VIA_PSA +#define MBEDTLS_MD_SOME_PSA +#endif +#if defined(MBEDTLS_PSA_ACCEL_ALG_RIPEMD160) +#define MBEDTLS_MD_CAN_RIPEMD160 +#define MBEDTLS_MD_RIPEMD160_VIA_PSA +#define MBEDTLS_MD_SOME_PSA +#endif +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +/* Built-in implementations */ +#if defined(MBEDTLS_MD5_C) +#define MBEDTLS_MD_CAN_MD5 +#define MBEDTLS_MD_SOME_LEGACY +#endif +#if defined(MBEDTLS_SHA1_C) +#define MBEDTLS_MD_CAN_SHA1 +#define MBEDTLS_MD_SOME_LEGACY +#endif +#if defined(MBEDTLS_SHA224_C) +#define MBEDTLS_MD_CAN_SHA224 +#define MBEDTLS_MD_SOME_LEGACY +#endif +#if defined(MBEDTLS_SHA256_C) +#define MBEDTLS_MD_CAN_SHA256 +#define MBEDTLS_MD_SOME_LEGACY +#endif +#if defined(MBEDTLS_SHA384_C) +#define MBEDTLS_MD_CAN_SHA384 +#define MBEDTLS_MD_SOME_LEGACY +#endif +#if defined(MBEDTLS_SHA512_C) +#define MBEDTLS_MD_CAN_SHA512 +#define MBEDTLS_MD_SOME_LEGACY +#endif +#if defined(MBEDTLS_RIPEMD160_C) +#define MBEDTLS_MD_CAN_RIPEMD160 +#define MBEDTLS_MD_SOME_LEGACY +#endif + +#endif /* MBEDTLS_MD_LIGHT */ + +/** The selected feature is not available. */ +#define MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE -0x5080 +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_MD_BAD_INPUT_DATA -0x5100 +/** Failed to allocate memory. */ +#define MBEDTLS_ERR_MD_ALLOC_FAILED -0x5180 +/** Opening or reading of file failed. */ +#define MBEDTLS_ERR_MD_FILE_IO_ERROR -0x5200 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Supported message digests. + * + * \warning MD5 and SHA-1 are considered weak message digests and + * their use constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +typedef enum { + MBEDTLS_MD_NONE=0, /**< None. */ + MBEDTLS_MD_MD5, /**< The MD5 message digest. */ + MBEDTLS_MD_SHA1, /**< The SHA-1 message digest. */ + MBEDTLS_MD_SHA224, /**< The SHA-224 message digest. */ + MBEDTLS_MD_SHA256, /**< The SHA-256 message digest. */ + MBEDTLS_MD_SHA384, /**< The SHA-384 message digest. */ + MBEDTLS_MD_SHA512, /**< The SHA-512 message digest. */ + MBEDTLS_MD_RIPEMD160, /**< The RIPEMD-160 message digest. */ +} mbedtls_md_type_t; + +#if defined(MBEDTLS_MD_CAN_SHA512) +#define MBEDTLS_MD_MAX_SIZE 64 /* longest known is SHA512 */ +#elif defined(MBEDTLS_MD_CAN_SHA384) +#define MBEDTLS_MD_MAX_SIZE 48 /* longest known is SHA384 */ +#elif defined(MBEDTLS_MD_CAN_SHA256) +#define MBEDTLS_MD_MAX_SIZE 32 /* longest known is SHA256 */ +#elif defined(MBEDTLS_MD_CAN_SHA224) +#define MBEDTLS_MD_MAX_SIZE 28 /* longest known is SHA224 */ +#else +#define MBEDTLS_MD_MAX_SIZE 20 /* longest known is SHA1 or RIPE MD-160 + or smaller (MD5 and earlier) */ +#endif + +#if defined(MBEDTLS_MD_CAN_SHA512) +#define MBEDTLS_MD_MAX_BLOCK_SIZE 128 +#else +#define MBEDTLS_MD_MAX_BLOCK_SIZE 64 +#endif + +/** + * Opaque struct. + * + * Constructed using either #mbedtls_md_info_from_string or + * #mbedtls_md_info_from_type. + * + * Fields can be accessed with #mbedtls_md_get_size, + * #mbedtls_md_get_type and #mbedtls_md_get_name. + */ +/* Defined internally in library/md_wrap.h. */ +typedef struct mbedtls_md_info_t mbedtls_md_info_t; + +/** + * Used internally to indicate whether a context uses legacy or PSA. + * + * Internal use only. + */ +typedef enum { + MBEDTLS_MD_ENGINE_LEGACY = 0, + MBEDTLS_MD_ENGINE_PSA, +} mbedtls_md_engine_t; + +/** + * The generic message-digest context. + */ +typedef struct mbedtls_md_context_t { + /** Information about the associated message digest. */ + const mbedtls_md_info_t *MBEDTLS_PRIVATE(md_info); + +#if defined(MBEDTLS_MD_SOME_PSA) + /** Are hash operations dispatched to PSA or legacy? */ + mbedtls_md_engine_t MBEDTLS_PRIVATE(engine); +#endif + + /** The digest-specific context (legacy) or the PSA operation. */ + void *MBEDTLS_PRIVATE(md_ctx); + +#if defined(MBEDTLS_MD_C) + /** The HMAC part of the context. */ + void *MBEDTLS_PRIVATE(hmac_ctx); +#endif +} mbedtls_md_context_t; + +/** + * \brief This function returns the message-digest information + * associated with the given digest type. + * + * \param md_type The type of digest to search for. + * + * \return The message-digest information associated with \p md_type. + * \return NULL if the associated message-digest information is not found. + */ +const mbedtls_md_info_t *mbedtls_md_info_from_type(mbedtls_md_type_t md_type); + +/** + * \brief This function initializes a message-digest context without + * binding it to a particular message-digest algorithm. + * + * This function should always be called first. It prepares the + * context for mbedtls_md_setup() for binding it to a + * message-digest algorithm. + */ +void mbedtls_md_init(mbedtls_md_context_t *ctx); + +/** + * \brief This function clears the internal structure of \p ctx and + * frees any embedded internal structure, but does not free + * \p ctx itself. + * + * If you have called mbedtls_md_setup() on \p ctx, you must + * call mbedtls_md_free() when you are no longer using the + * context. + * Calling this function if you have previously + * called mbedtls_md_init() and nothing else is optional. + * You must not call this function if you have not called + * mbedtls_md_init(). + */ +void mbedtls_md_free(mbedtls_md_context_t *ctx); + + +/** + * \brief This function selects the message digest algorithm to use, + * and allocates internal structures. + * + * It should be called after mbedtls_md_init() or + * mbedtls_md_free(). Makes it necessary to call + * mbedtls_md_free() later. + * + * \param ctx The context to set up. + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param hmac Defines if HMAC is used. 0: HMAC is not used (saves some memory), + * or non-zero: HMAC is used with this context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + * \return #MBEDTLS_ERR_MD_ALLOC_FAILED on memory-allocation failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_setup(mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac); + +/** + * \brief This function clones the state of a message-digest + * context. + * + * \note You must call mbedtls_md_setup() on \c dst before calling + * this function. + * + * \note The two contexts must have the same type, + * for example, both are SHA-256. + * + * \warning This function clones the message-digest state, not the + * HMAC state. + * + * \param dst The destination context. + * \param src The context to be cloned. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification failure. + * \return #MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE if both contexts are + * not using the same engine. This can be avoided by moving + * the call to psa_crypto_init() before the first call to + * mbedtls_md_setup(). + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_clone(mbedtls_md_context_t *dst, + const mbedtls_md_context_t *src); + +/** + * \brief This function extracts the message-digest size from the + * message-digest information structure. + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * + * \return The size of the message-digest output in Bytes. + */ +unsigned char mbedtls_md_get_size(const mbedtls_md_info_t *md_info); + +/** + * \brief This function extracts the message-digest type from the + * message-digest information structure. + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * + * \return The type of the message digest. + */ +mbedtls_md_type_t mbedtls_md_get_type(const mbedtls_md_info_t *md_info); + +/** + * \brief This function starts a message-digest computation. + * + * You must call this function after setting up the context + * with mbedtls_md_setup(), and before passing data with + * mbedtls_md_update(). + * + * \param ctx The generic message-digest context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_starts(mbedtls_md_context_t *ctx); + +/** + * \brief This function feeds an input buffer into an ongoing + * message-digest computation. + * + * You must call mbedtls_md_starts() before calling this + * function. You may call this function multiple times. + * Afterwards, call mbedtls_md_finish(). + * + * \param ctx The generic message-digest context. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_update(mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen); + +/** + * \brief This function finishes the digest operation, + * and writes the result to the output buffer. + * + * Call this function after a call to mbedtls_md_starts(), + * followed by any number of calls to mbedtls_md_update(). + * Afterwards, you may either clear the context with + * mbedtls_md_free(), or call mbedtls_md_starts() to reuse + * the context for another digest operation with the same + * algorithm. + * + * \param ctx The generic message-digest context. + * \param output The buffer for the generic message-digest checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_finish(mbedtls_md_context_t *ctx, unsigned char *output); + +/** + * \brief This function calculates the message-digest of a buffer, + * with respect to a configurable message-digest algorithm + * in a single call. + * + * The result is calculated as + * Output = message_digest(input buffer). + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param input The buffer holding the data. + * \param ilen The length of the input data. + * \param output The generic message-digest checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md(const mbedtls_md_info_t *md_info, const unsigned char *input, size_t ilen, + unsigned char *output); + +/** + * \brief This function returns the list of digests supported by the + * generic digest module. + * + * \note The list starts with the strongest available hashes. + * + * \return A statically allocated array of digests. Each element + * in the returned list is an integer belonging to the + * message-digest enumeration #mbedtls_md_type_t. + * The last entry is 0. + */ +const int *mbedtls_md_list(void); + +/** + * \brief This function returns the message-digest information + * associated with the given digest name. + * + * \param md_name The name of the digest to search for. + * + * \return The message-digest information associated with \p md_name. + * \return NULL if the associated message-digest information is not found. + */ +const mbedtls_md_info_t *mbedtls_md_info_from_string(const char *md_name); + +/** + * \brief This function extracts the message-digest name from the + * message-digest information structure. + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * + * \return The name of the message digest. + */ +const char *mbedtls_md_get_name(const mbedtls_md_info_t *md_info); + +/** + * \brief This function returns the message-digest information + * from the given context. + * + * \param ctx The context from which to extract the information. + * This must be initialized (or \c NULL). + * + * \return The message-digest information associated with \p ctx. + * \return \c NULL if \p ctx is \c NULL. + */ +const mbedtls_md_info_t *mbedtls_md_info_from_ctx( + const mbedtls_md_context_t *ctx); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief This function calculates the message-digest checksum + * result of the contents of the provided file. + * + * The result is calculated as + * Output = message_digest(file contents). + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param path The input file name. + * \param output The generic message-digest checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_FILE_IO_ERROR on an I/O error accessing + * the file pointed by \p path. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA if \p md_info was NULL. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_file(const mbedtls_md_info_t *md_info, const char *path, + unsigned char *output); +#endif /* MBEDTLS_FS_IO */ + +/** + * \brief This function sets the HMAC key and prepares to + * authenticate a new message. + * + * Call this function after mbedtls_md_setup(), to use + * the MD context for an HMAC calculation, then call + * mbedtls_md_hmac_update() to provide the input data, and + * mbedtls_md_hmac_finish() to get the HMAC value. + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * \param key The HMAC secret key. + * \param keylen The length of the HMAC key in Bytes. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_hmac_starts(mbedtls_md_context_t *ctx, const unsigned char *key, + size_t keylen); + +/** + * \brief This function feeds an input buffer into an ongoing HMAC + * computation. + * + * Call mbedtls_md_hmac_starts() or mbedtls_md_hmac_reset() + * before calling this function. + * You may call this function multiple times to pass the + * input piecewise. + * Afterwards, call mbedtls_md_hmac_finish(). + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_hmac_update(mbedtls_md_context_t *ctx, const unsigned char *input, + size_t ilen); + +/** + * \brief This function finishes the HMAC operation, and writes + * the result to the output buffer. + * + * Call this function after mbedtls_md_hmac_starts() and + * mbedtls_md_hmac_update() to get the HMAC value. Afterwards + * you may either call mbedtls_md_free() to clear the context, + * or call mbedtls_md_hmac_reset() to reuse the context with + * the same HMAC key. + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * \param output The generic HMAC checksum result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_hmac_finish(mbedtls_md_context_t *ctx, unsigned char *output); + +/** + * \brief This function prepares to authenticate a new message with + * the same key as the previous HMAC operation. + * + * You may call this function after mbedtls_md_hmac_finish(). + * Afterwards call mbedtls_md_hmac_update() to pass the new + * input. + * + * \param ctx The message digest context containing an embedded HMAC + * context. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_hmac_reset(mbedtls_md_context_t *ctx); + +/** + * \brief This function calculates the full generic HMAC + * on the input buffer with the provided key. + * + * The function allocates the context, performs the + * calculation, and frees the context. + * + * The HMAC result is calculated as + * output = generic HMAC(hmac key, input buffer). + * + * \param md_info The information structure of the message-digest algorithm + * to use. + * \param key The HMAC secret key. + * \param keylen The length of the HMAC secret key in Bytes. + * \param input The buffer holding the input data. + * \param ilen The length of the input data. + * \param output The generic HMAC result. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MD_BAD_INPUT_DATA on parameter-verification + * failure. + */ +MBEDTLS_CHECK_RETURN_TYPICAL +int mbedtls_md_hmac(const mbedtls_md_info_t *md_info, const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_MD_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/md5.h b/r5dev/thirdparty/mbedtls/include/mbedtls/md5.h new file mode 100644 index 00000000..80818869 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/md5.h @@ -0,0 +1,202 @@ +/** + * \file md5.h + * + * \brief MD5 message digest algorithm (hash function) + * + * \warning MD5 is considered a weak message digest and its use constitutes a + * security risk. We recommend considering stronger message + * digests instead. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_MD5_H +#define MBEDTLS_MD5_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_MD5_ALT) +// Regular implementation +// + +/** + * \brief MD5 context structure + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +typedef struct mbedtls_md5_context { + uint32_t MBEDTLS_PRIVATE(total)[2]; /*!< number of bytes processed */ + uint32_t MBEDTLS_PRIVATE(state)[4]; /*!< intermediate digest state */ + unsigned char MBEDTLS_PRIVATE(buffer)[64]; /*!< data block being processed */ +} +mbedtls_md5_context; + +#else /* MBEDTLS_MD5_ALT */ +#include "md5_alt.h" +#endif /* MBEDTLS_MD5_ALT */ + +/** + * \brief Initialize MD5 context + * + * \param ctx MD5 context to be initialized + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +void mbedtls_md5_init(mbedtls_md5_context *ctx); + +/** + * \brief Clear MD5 context + * + * \param ctx MD5 context to be cleared + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +void mbedtls_md5_free(mbedtls_md5_context *ctx); + +/** + * \brief Clone (the state of) an MD5 context + * + * \param dst The destination context + * \param src The context to be cloned + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +void mbedtls_md5_clone(mbedtls_md5_context *dst, + const mbedtls_md5_context *src); + +/** + * \brief MD5 context setup + * + * \param ctx context to be initialized + * + * \return 0 if successful + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +int mbedtls_md5_starts(mbedtls_md5_context *ctx); + +/** + * \brief MD5 process buffer + * + * \param ctx MD5 context + * \param input buffer holding the data + * \param ilen length of the input data + * + * \return 0 if successful + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +int mbedtls_md5_update(mbedtls_md5_context *ctx, + const unsigned char *input, + size_t ilen); + +/** + * \brief MD5 final digest + * + * \param ctx MD5 context + * \param output MD5 checksum result + * + * \return 0 if successful + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +int mbedtls_md5_finish(mbedtls_md5_context *ctx, + unsigned char output[16]); + +/** + * \brief MD5 process data block (internal use only) + * + * \param ctx MD5 context + * \param data buffer holding one block of data + * + * \return 0 if successful + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +int mbedtls_internal_md5_process(mbedtls_md5_context *ctx, + const unsigned char data[64]); + +/** + * \brief Output = MD5( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output MD5 checksum result + * + * \return 0 if successful + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +int mbedtls_md5(const unsigned char *input, + size_t ilen, + unsigned char output[16]); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + * + * \warning MD5 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +int mbedtls_md5_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_md5.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/memory_buffer_alloc.h b/r5dev/thirdparty/mbedtls/include/mbedtls/memory_buffer_alloc.h new file mode 100644 index 00000000..9694d245 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/memory_buffer_alloc.h @@ -0,0 +1,154 @@ +/** + * \file memory_buffer_alloc.h + * + * \brief Buffer-based memory allocator + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_MEMORY_BUFFER_ALLOC_H +#define MBEDTLS_MEMORY_BUFFER_ALLOC_H + +#include "mbedtls/build_info.h" + +#include + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(MBEDTLS_MEMORY_ALIGN_MULTIPLE) +#define MBEDTLS_MEMORY_ALIGN_MULTIPLE 4 /**< Align on multiples of this value */ +#endif + +/** \} name SECTION: Module settings */ + +#define MBEDTLS_MEMORY_VERIFY_NONE 0 +#define MBEDTLS_MEMORY_VERIFY_ALLOC (1 << 0) +#define MBEDTLS_MEMORY_VERIFY_FREE (1 << 1) +#define MBEDTLS_MEMORY_VERIFY_ALWAYS (MBEDTLS_MEMORY_VERIFY_ALLOC | \ + MBEDTLS_MEMORY_VERIFY_FREE) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Initialize use of stack-based memory allocator. + * The stack-based allocator does memory management inside the + * presented buffer and does not call calloc() and free(). + * It sets the global mbedtls_calloc() and mbedtls_free() pointers + * to its own functions. + * (Provided mbedtls_calloc() and mbedtls_free() are thread-safe if + * MBEDTLS_THREADING_C is defined) + * + * \note This code is not optimized and provides a straight-forward + * implementation of a stack-based memory allocator. + * + * \param buf buffer to use as heap + * \param len size of the buffer + */ +void mbedtls_memory_buffer_alloc_init(unsigned char *buf, size_t len); + +/** + * \brief Free the mutex for thread-safety and clear remaining memory + */ +void mbedtls_memory_buffer_alloc_free(void); + +/** + * \brief Determine when the allocator should automatically verify the state + * of the entire chain of headers / meta-data. + * (Default: MBEDTLS_MEMORY_VERIFY_NONE) + * + * \param verify One of MBEDTLS_MEMORY_VERIFY_NONE, MBEDTLS_MEMORY_VERIFY_ALLOC, + * MBEDTLS_MEMORY_VERIFY_FREE or MBEDTLS_MEMORY_VERIFY_ALWAYS + */ +void mbedtls_memory_buffer_set_verify(int verify); + +#if defined(MBEDTLS_MEMORY_DEBUG) +/** + * \brief Print out the status of the allocated memory (primarily for use + * after a program should have de-allocated all memory) + * Prints out a list of 'still allocated' blocks and their stack + * trace if MBEDTLS_MEMORY_BACKTRACE is defined. + */ +void mbedtls_memory_buffer_alloc_status(void); + +/** + * \brief Get the number of alloc/free so far. + * + * \param alloc_count Number of allocations. + * \param free_count Number of frees. + */ +void mbedtls_memory_buffer_alloc_count_get(size_t *alloc_count, size_t *free_count); + +/** + * \brief Get the peak heap usage so far + * + * \param max_used Peak number of bytes in use or committed. This + * includes bytes in allocated blocks too small to split + * into smaller blocks but larger than the requested size. + * \param max_blocks Peak number of blocks in use, including free and used + */ +void mbedtls_memory_buffer_alloc_max_get(size_t *max_used, size_t *max_blocks); + +/** + * \brief Reset peak statistics + */ +void mbedtls_memory_buffer_alloc_max_reset(void); + +/** + * \brief Get the current heap usage + * + * \param cur_used Current number of bytes in use or committed. This + * includes bytes in allocated blocks too small to split + * into smaller blocks but larger than the requested size. + * \param cur_blocks Current number of blocks in use, including free and used + */ +void mbedtls_memory_buffer_alloc_cur_get(size_t *cur_used, size_t *cur_blocks); +#endif /* MBEDTLS_MEMORY_DEBUG */ + +/** + * \brief Verifies that all headers in the memory buffer are correct + * and contain sane values. Helps debug buffer-overflow errors. + * + * Prints out first failure if MBEDTLS_MEMORY_DEBUG is defined. + * Prints out full header information if MBEDTLS_MEMORY_DEBUG + * is defined. (Includes stack trace information for each block if + * MBEDTLS_MEMORY_BACKTRACE is defined as well). + * + * \return 0 if verified, 1 otherwise + */ +int mbedtls_memory_buffer_alloc_verify(void); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if a test failed + */ +int mbedtls_memory_buffer_alloc_self_test(int verbose); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* memory_buffer_alloc.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/net_sockets.h b/r5dev/thirdparty/mbedtls/include/mbedtls/net_sockets.h new file mode 100644 index 00000000..14316fbe --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/net_sockets.h @@ -0,0 +1,303 @@ +/** + * \file net_sockets.h + * + * \brief Network sockets abstraction layer to integrate Mbed TLS into a + * BSD-style sockets API. + * + * The network sockets module provides an example integration of the + * Mbed TLS library into a BSD sockets implementation. The module is + * intended to be an example of how Mbed TLS can be integrated into a + * networking stack, as well as to be Mbed TLS's network integration + * for its supported platforms. + * + * The module is intended only to be used with the Mbed TLS library and + * is not intended to be used by third party application software + * directly. + * + * The supported platforms are as follows: + * * Microsoft Windows and Windows CE + * * POSIX/Unix platforms including Linux, OS X + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_NET_SOCKETS_H +#define MBEDTLS_NET_SOCKETS_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/ssl.h" + +#include +#include + +/** Failed to open a socket. */ +#define MBEDTLS_ERR_NET_SOCKET_FAILED -0x0042 +/** The connection to the given server / port failed. */ +#define MBEDTLS_ERR_NET_CONNECT_FAILED -0x0044 +/** Binding of the socket failed. */ +#define MBEDTLS_ERR_NET_BIND_FAILED -0x0046 +/** Could not listen on the socket. */ +#define MBEDTLS_ERR_NET_LISTEN_FAILED -0x0048 +/** Could not accept the incoming connection. */ +#define MBEDTLS_ERR_NET_ACCEPT_FAILED -0x004A +/** Reading information from the socket failed. */ +#define MBEDTLS_ERR_NET_RECV_FAILED -0x004C +/** Sending information through the socket failed. */ +#define MBEDTLS_ERR_NET_SEND_FAILED -0x004E +/** Connection was reset by peer. */ +#define MBEDTLS_ERR_NET_CONN_RESET -0x0050 +/** Failed to get an IP address for the given hostname. */ +#define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 +/** Buffer is too small to hold the data. */ +#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL -0x0043 +/** The context is invalid, eg because it was free()ed. */ +#define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 +/** Polling the net context failed. */ +#define MBEDTLS_ERR_NET_POLL_FAILED -0x0047 +/** Input invalid. */ +#define MBEDTLS_ERR_NET_BAD_INPUT_DATA -0x0049 + +#define MBEDTLS_NET_LISTEN_BACKLOG 10 /**< The backlog that listen() should use. */ + +#define MBEDTLS_NET_PROTO_TCP 0 /**< The TCP transport protocol */ +#define MBEDTLS_NET_PROTO_UDP 1 /**< The UDP transport protocol */ + +#define MBEDTLS_NET_POLL_READ 1 /**< Used in \c mbedtls_net_poll to check for pending data */ +#define MBEDTLS_NET_POLL_WRITE 2 /**< Used in \c mbedtls_net_poll to check if write possible */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Wrapper type for sockets. + * + * Currently backed by just a file descriptor, but might be more in the future + * (eg two file descriptors for combined IPv4 + IPv6 support, or additional + * structures for hand-made UDP demultiplexing). + */ +typedef struct mbedtls_net_context { + /** The underlying file descriptor. + * + * This field is only guaranteed to be present on POSIX/Unix-like platforms. + * On other platforms, it may have a different type, have a different + * meaning, or be absent altogether. + */ + int fd; +} +mbedtls_net_context; + +/** + * \brief Initialize a context + * Just makes the context ready to be used or freed safely. + * + * \param ctx Context to initialize + */ +void mbedtls_net_init(mbedtls_net_context *ctx); + +/** + * \brief Initiate a connection with host:port in the given protocol + * + * \param ctx Socket to use + * \param host Host to connect to + * \param port Port to connect to + * \param proto Protocol: MBEDTLS_NET_PROTO_TCP or MBEDTLS_NET_PROTO_UDP + * + * \return 0 if successful, or one of: + * MBEDTLS_ERR_NET_SOCKET_FAILED, + * MBEDTLS_ERR_NET_UNKNOWN_HOST, + * MBEDTLS_ERR_NET_CONNECT_FAILED + * + * \note Sets the socket in connected mode even with UDP. + */ +int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto); + +/** + * \brief Create a receiving socket on bind_ip:port in the chosen + * protocol. If bind_ip == NULL, all interfaces are bound. + * + * \param ctx Socket to use + * \param bind_ip IP to bind to, can be NULL + * \param port Port number to use + * \param proto Protocol: MBEDTLS_NET_PROTO_TCP or MBEDTLS_NET_PROTO_UDP + * + * \return 0 if successful, or one of: + * MBEDTLS_ERR_NET_SOCKET_FAILED, + * MBEDTLS_ERR_NET_UNKNOWN_HOST, + * MBEDTLS_ERR_NET_BIND_FAILED, + * MBEDTLS_ERR_NET_LISTEN_FAILED + * + * \note Regardless of the protocol, opens the sockets and binds it. + * In addition, make the socket listening if protocol is TCP. + */ +int mbedtls_net_bind(mbedtls_net_context *ctx, const char *bind_ip, const char *port, int proto); + +/** + * \brief Accept a connection from a remote client + * + * \param bind_ctx Relevant socket + * \param client_ctx Will contain the connected client socket + * \param client_ip Will contain the client IP address, can be NULL + * \param buf_size Size of the client_ip buffer + * \param ip_len Will receive the size of the client IP written, + * can be NULL if client_ip is null + * + * \return 0 if successful, or + * MBEDTLS_ERR_NET_SOCKET_FAILED, + * MBEDTLS_ERR_NET_BIND_FAILED, + * MBEDTLS_ERR_NET_ACCEPT_FAILED, or + * MBEDTLS_ERR_NET_BUFFER_TOO_SMALL if buf_size is too small, + * MBEDTLS_ERR_SSL_WANT_READ if bind_fd was set to + * non-blocking and accept() would block. + */ +int mbedtls_net_accept(mbedtls_net_context *bind_ctx, + mbedtls_net_context *client_ctx, + void *client_ip, size_t buf_size, size_t *ip_len); + +/** + * \brief Check and wait for the context to be ready for read/write + * + * \note The current implementation of this function uses + * select() and returns an error if the file descriptor + * is \c FD_SETSIZE or greater. + * + * \param ctx Socket to check + * \param rw Bitflag composed of MBEDTLS_NET_POLL_READ and + * MBEDTLS_NET_POLL_WRITE specifying the events + * to wait for: + * - If MBEDTLS_NET_POLL_READ is set, the function + * will return as soon as the net context is available + * for reading. + * - If MBEDTLS_NET_POLL_WRITE is set, the function + * will return as soon as the net context is available + * for writing. + * \param timeout Maximal amount of time to wait before returning, + * in milliseconds. If \c timeout is zero, the + * function returns immediately. If \c timeout is + * -1u, the function blocks potentially indefinitely. + * + * \return Bitmask composed of MBEDTLS_NET_POLL_READ/WRITE + * on success or timeout, or a negative return code otherwise. + */ +int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout); + +/** + * \brief Set the socket blocking + * + * \param ctx Socket to set + * + * \return 0 if successful, or a non-zero error code + */ +int mbedtls_net_set_block(mbedtls_net_context *ctx); + +/** + * \brief Set the socket non-blocking + * + * \param ctx Socket to set + * + * \return 0 if successful, or a non-zero error code + */ +int mbedtls_net_set_nonblock(mbedtls_net_context *ctx); + +/** + * \brief Portable usleep helper + * + * \param usec Amount of microseconds to sleep + * + * \note Real amount of time slept will not be less than + * select()'s timeout granularity (typically, 10ms). + */ +void mbedtls_net_usleep(unsigned long usec); + +/** + * \brief Read at most 'len' characters. If no error occurs, + * the actual amount read is returned. + * + * \param ctx Socket + * \param buf The buffer to write to + * \param len Maximum length of the buffer + * + * \return the number of bytes received, + * or a non-zero error code; with a non-blocking socket, + * MBEDTLS_ERR_SSL_WANT_READ indicates read() would block. + */ +int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len); + +/** + * \brief Write at most 'len' characters. If no error occurs, + * the actual amount read is returned. + * + * \param ctx Socket + * \param buf The buffer to read from + * \param len The length of the buffer + * + * \return the number of bytes sent, + * or a non-zero error code; with a non-blocking socket, + * MBEDTLS_ERR_SSL_WANT_WRITE indicates write() would block. + */ +int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len); + +/** + * \brief Read at most 'len' characters, blocking for at most + * 'timeout' seconds. If no error occurs, the actual amount + * read is returned. + * + * \note The current implementation of this function uses + * select() and returns an error if the file descriptor + * is \c FD_SETSIZE or greater. + * + * \param ctx Socket + * \param buf The buffer to write to + * \param len Maximum length of the buffer + * \param timeout Maximum number of milliseconds to wait for data + * 0 means no timeout (wait forever) + * + * \return The number of bytes received if successful. + * MBEDTLS_ERR_SSL_TIMEOUT if the operation timed out. + * MBEDTLS_ERR_SSL_WANT_READ if interrupted by a signal. + * Another negative error code (MBEDTLS_ERR_NET_xxx) + * for other failures. + * + * \note This function will block (until data becomes available or + * timeout is reached) even if the socket is set to + * non-blocking. Handling timeouts with non-blocking reads + * requires a different strategy. + */ +int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf, size_t len, + uint32_t timeout); + +/** + * \brief Closes down the connection and free associated data + * + * \param ctx The context to close + */ +void mbedtls_net_close(mbedtls_net_context *ctx); + +/** + * \brief Gracefully shutdown the connection and free associated data + * + * \param ctx The context to free + */ +void mbedtls_net_free(mbedtls_net_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* net_sockets.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/nist_kw.h b/r5dev/thirdparty/mbedtls/include/mbedtls/nist_kw.h new file mode 100644 index 00000000..0c95c902 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/nist_kw.h @@ -0,0 +1,178 @@ +/** + * \file nist_kw.h + * + * \brief This file provides an API for key wrapping (KW) and key wrapping with + * padding (KWP) as defined in NIST SP 800-38F. + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf + * + * Key wrapping specifies a deterministic authenticated-encryption mode + * of operation, according to NIST SP 800-38F: Recommendation for + * Block Cipher Modes of Operation: Methods for Key Wrapping. Its + * purpose is to protect cryptographic keys. + * + * Its equivalent is RFC 3394 for KW, and RFC 5649 for KWP. + * https://tools.ietf.org/html/rfc3394 + * https://tools.ietf.org/html/rfc5649 + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_NIST_KW_H +#define MBEDTLS_NIST_KW_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/cipher.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MBEDTLS_KW_MODE_KW = 0, + MBEDTLS_KW_MODE_KWP = 1 +} mbedtls_nist_kw_mode_t; + +#if !defined(MBEDTLS_NIST_KW_ALT) +// Regular implementation +// + +/** + * \brief The key wrapping context-type definition. The key wrapping context is passed + * to the APIs called. + * + * \note The definition of this type may change in future library versions. + * Don't make any assumptions on this context! + */ +typedef struct { + mbedtls_cipher_context_t MBEDTLS_PRIVATE(cipher_ctx); /*!< The cipher context used. */ +} mbedtls_nist_kw_context; + +#else /* MBEDTLS_NIST_key wrapping_ALT */ +#include "nist_kw_alt.h" +#endif /* MBEDTLS_NIST_KW_ALT */ + +/** + * \brief This function initializes the specified key wrapping context + * to make references valid and prepare the context + * for mbedtls_nist_kw_setkey() or mbedtls_nist_kw_free(). + * + * \param ctx The key wrapping context to initialize. + * + */ +void mbedtls_nist_kw_init(mbedtls_nist_kw_context *ctx); + +/** + * \brief This function initializes the key wrapping context set in the + * \p ctx parameter and sets the encryption key. + * + * \param ctx The key wrapping context. + * \param cipher The 128-bit block cipher to use. Only AES is supported. + * \param key The Key Encryption Key (KEK). + * \param keybits The KEK size in bits. This must be acceptable by the cipher. + * \param is_wrap Specify whether the operation within the context is wrapping or unwrapping + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA for any invalid input. + * \return \c MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE for 128-bit block ciphers + * which are not supported. + * \return cipher-specific error code on failure of the underlying cipher. + */ +int mbedtls_nist_kw_setkey(mbedtls_nist_kw_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits, + const int is_wrap); + +/** + * \brief This function releases and clears the specified key wrapping context + * and underlying cipher sub-context. + * + * \param ctx The key wrapping context to clear. + */ +void mbedtls_nist_kw_free(mbedtls_nist_kw_context *ctx); + +/** + * \brief This function encrypts a buffer using key wrapping. + * + * \param ctx The key wrapping context to use for encryption. + * \param mode The key wrapping mode to use (MBEDTLS_KW_MODE_KW or MBEDTLS_KW_MODE_KWP) + * \param input The buffer holding the input data. + * \param in_len The length of the input data in Bytes. + * The input uses units of 8 Bytes called semiblocks. + *
  • For KW mode: a multiple of 8 bytes between 16 and 2^57-8 inclusive.
  • + *
  • For KWP mode: any length between 1 and 2^32-1 inclusive.
+ * \param[out] output The buffer holding the output data. + *
  • For KW mode: Must be at least 8 bytes larger than \p in_len.
  • + *
  • For KWP mode: Must be at least 8 bytes larger rounded up to a multiple of + * 8 bytes for KWP (15 bytes at most).
+ * \param[out] out_len The number of bytes written to the output buffer. \c 0 on failure. + * \param[in] out_size The capacity of the output buffer. + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA for invalid input length. + * \return cipher-specific error code on failure of the underlying cipher. + */ +int mbedtls_nist_kw_wrap(mbedtls_nist_kw_context *ctx, mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t *out_len, size_t out_size); + +/** + * \brief This function decrypts a buffer using key wrapping. + * + * \param ctx The key wrapping context to use for decryption. + * \param mode The key wrapping mode to use (MBEDTLS_KW_MODE_KW or MBEDTLS_KW_MODE_KWP) + * \param input The buffer holding the input data. + * \param in_len The length of the input data in Bytes. + * The input uses units of 8 Bytes called semiblocks. + * The input must be a multiple of semiblocks. + *
  • For KW mode: a multiple of 8 bytes between 24 and 2^57 inclusive.
  • + *
  • For KWP mode: a multiple of 8 bytes between 16 and 2^32 inclusive.
+ * \param[out] output The buffer holding the output data. + * The output buffer's minimal length is 8 bytes shorter than \p in_len. + * \param[out] out_len The number of bytes written to the output buffer. \c 0 on failure. + * For KWP mode, the length could be up to 15 bytes shorter than \p in_len, + * depending on how much padding was added to the data. + * \param[in] out_size The capacity of the output buffer. + * + * \return \c 0 on success. + * \return \c MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA for invalid input length. + * \return \c MBEDTLS_ERR_CIPHER_AUTH_FAILED for verification failure of the ciphertext. + * \return cipher-specific error code on failure of the underlying cipher. + */ +int mbedtls_nist_kw_unwrap(mbedtls_nist_kw_context *ctx, mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t *out_len, size_t out_size); + + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) +/** + * \brief The key wrapping checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_nist_kw_self_test(int verbose); +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_NIST_KW_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/oid.h b/r5dev/thirdparty/mbedtls/include/mbedtls/oid.h new file mode 100644 index 00000000..a592e63c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/oid.h @@ -0,0 +1,655 @@ +/** + * \file oid.h + * + * \brief Object Identifier (OID) database + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_OID_H +#define MBEDTLS_OID_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/asn1.h" +#include "mbedtls/pk.h" + +#include + +#if defined(MBEDTLS_CIPHER_C) +#include "mbedtls/cipher.h" +#endif + +#include "mbedtls/md.h" + +/** OID is not found. */ +#define MBEDTLS_ERR_OID_NOT_FOUND -0x002E +/** output buffer is too small */ +#define MBEDTLS_ERR_OID_BUF_TOO_SMALL -0x000B + +/* This is for the benefit of X.509, but defined here in order to avoid + * having a "backwards" include of x.509.h here */ +/* + * X.509 extension types (internal, arbitrary values for bitsets) + */ +#define MBEDTLS_OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER (1 << 0) +#define MBEDTLS_OID_X509_EXT_SUBJECT_KEY_IDENTIFIER (1 << 1) +#define MBEDTLS_OID_X509_EXT_KEY_USAGE (1 << 2) +#define MBEDTLS_OID_X509_EXT_CERTIFICATE_POLICIES (1 << 3) +#define MBEDTLS_OID_X509_EXT_POLICY_MAPPINGS (1 << 4) +#define MBEDTLS_OID_X509_EXT_SUBJECT_ALT_NAME (1 << 5) +#define MBEDTLS_OID_X509_EXT_ISSUER_ALT_NAME (1 << 6) +#define MBEDTLS_OID_X509_EXT_SUBJECT_DIRECTORY_ATTRS (1 << 7) +#define MBEDTLS_OID_X509_EXT_BASIC_CONSTRAINTS (1 << 8) +#define MBEDTLS_OID_X509_EXT_NAME_CONSTRAINTS (1 << 9) +#define MBEDTLS_OID_X509_EXT_POLICY_CONSTRAINTS (1 << 10) +#define MBEDTLS_OID_X509_EXT_EXTENDED_KEY_USAGE (1 << 11) +#define MBEDTLS_OID_X509_EXT_CRL_DISTRIBUTION_POINTS (1 << 12) +#define MBEDTLS_OID_X509_EXT_INIHIBIT_ANYPOLICY (1 << 13) +#define MBEDTLS_OID_X509_EXT_FRESHEST_CRL (1 << 14) +#define MBEDTLS_OID_X509_EXT_NS_CERT_TYPE (1 << 16) + +/* + * Top level OID tuples + */ +#define MBEDTLS_OID_ISO_MEMBER_BODIES "\x2a" /* {iso(1) member-body(2)} */ +#define MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x2b" /* {iso(1) identified-organization(3)} */ +#define MBEDTLS_OID_ISO_CCITT_DS "\x55" /* {joint-iso-ccitt(2) ds(5)} */ +#define MBEDTLS_OID_ISO_ITU_COUNTRY "\x60" /* {joint-iso-itu-t(2) country(16)} */ + +/* + * ISO Member bodies OID parts + */ +#define MBEDTLS_OID_COUNTRY_US "\x86\x48" /* {us(840)} */ +#define MBEDTLS_OID_ORG_RSA_DATA_SECURITY "\x86\xf7\x0d" /* {rsadsi(113549)} */ +#define MBEDTLS_OID_RSA_COMPANY MBEDTLS_OID_ISO_MEMBER_BODIES MBEDTLS_OID_COUNTRY_US \ + MBEDTLS_OID_ORG_RSA_DATA_SECURITY /* {iso(1) member-body(2) us(840) rsadsi(113549)} */ +#define MBEDTLS_OID_ORG_ANSI_X9_62 "\xce\x3d" /* ansi-X9-62(10045) */ +#define MBEDTLS_OID_ANSI_X9_62 MBEDTLS_OID_ISO_MEMBER_BODIES MBEDTLS_OID_COUNTRY_US \ + MBEDTLS_OID_ORG_ANSI_X9_62 + +/* + * ISO Identified organization OID parts + */ +#define MBEDTLS_OID_ORG_DOD "\x06" /* {dod(6)} */ +#define MBEDTLS_OID_ORG_OIW "\x0e" +#define MBEDTLS_OID_OIW_SECSIG MBEDTLS_OID_ORG_OIW "\x03" +#define MBEDTLS_OID_OIW_SECSIG_ALG MBEDTLS_OID_OIW_SECSIG "\x02" +#define MBEDTLS_OID_OIW_SECSIG_SHA1 MBEDTLS_OID_OIW_SECSIG_ALG "\x1a" +#define MBEDTLS_OID_ORG_CERTICOM "\x81\x04" /* certicom(132) */ +#define MBEDTLS_OID_CERTICOM MBEDTLS_OID_ISO_IDENTIFIED_ORG \ + MBEDTLS_OID_ORG_CERTICOM +#define MBEDTLS_OID_ORG_TELETRUST "\x24" /* teletrust(36) */ +#define MBEDTLS_OID_TELETRUST MBEDTLS_OID_ISO_IDENTIFIED_ORG \ + MBEDTLS_OID_ORG_TELETRUST + +/* + * ISO ITU OID parts + */ +#define MBEDTLS_OID_ORGANIZATION "\x01" /* {organization(1)} */ +#define MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ISO_ITU_COUNTRY MBEDTLS_OID_COUNTRY_US \ + MBEDTLS_OID_ORGANIZATION /* {joint-iso-itu-t(2) country(16) us(840) organization(1)} */ + +#define MBEDTLS_OID_ORG_GOV "\x65" /* {gov(101)} */ +#define MBEDTLS_OID_GOV MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ORG_GOV /* {joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101)} */ + +#define MBEDTLS_OID_ORG_NETSCAPE "\x86\xF8\x42" /* {netscape(113730)} */ +#define MBEDTLS_OID_NETSCAPE MBEDTLS_OID_ISO_ITU_US_ORG MBEDTLS_OID_ORG_NETSCAPE /* Netscape OID {joint-iso-itu-t(2) country(16) us(840) organization(1) netscape(113730)} */ + +/* ISO arc for standard certificate and CRL extensions */ +#define MBEDTLS_OID_ID_CE MBEDTLS_OID_ISO_CCITT_DS "\x1D" /**< id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} */ + +#define MBEDTLS_OID_NIST_ALG MBEDTLS_OID_GOV "\x03\x04" /** { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) */ + +/** + * Private Internet Extensions + * { iso(1) identified-organization(3) dod(6) internet(1) + * security(5) mechanisms(5) pkix(7) } + */ +#define MBEDTLS_OID_INTERNET MBEDTLS_OID_ISO_IDENTIFIED_ORG MBEDTLS_OID_ORG_DOD \ + "\x01" +#define MBEDTLS_OID_PKIX MBEDTLS_OID_INTERNET "\x05\x05\x07" + +/* + * Arc for standard naming attributes + */ +#define MBEDTLS_OID_AT MBEDTLS_OID_ISO_CCITT_DS "\x04" /**< id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} */ +#define MBEDTLS_OID_AT_CN MBEDTLS_OID_AT "\x03" /**< id-at-commonName AttributeType:= {id-at 3} */ +#define MBEDTLS_OID_AT_SUR_NAME MBEDTLS_OID_AT "\x04" /**< id-at-surName AttributeType:= {id-at 4} */ +#define MBEDTLS_OID_AT_SERIAL_NUMBER MBEDTLS_OID_AT "\x05" /**< id-at-serialNumber AttributeType:= {id-at 5} */ +#define MBEDTLS_OID_AT_COUNTRY MBEDTLS_OID_AT "\x06" /**< id-at-countryName AttributeType:= {id-at 6} */ +#define MBEDTLS_OID_AT_LOCALITY MBEDTLS_OID_AT "\x07" /**< id-at-locality AttributeType:= {id-at 7} */ +#define MBEDTLS_OID_AT_STATE MBEDTLS_OID_AT "\x08" /**< id-at-state AttributeType:= {id-at 8} */ +#define MBEDTLS_OID_AT_ORGANIZATION MBEDTLS_OID_AT "\x0A" /**< id-at-organizationName AttributeType:= {id-at 10} */ +#define MBEDTLS_OID_AT_ORG_UNIT MBEDTLS_OID_AT "\x0B" /**< id-at-organizationalUnitName AttributeType:= {id-at 11} */ +#define MBEDTLS_OID_AT_TITLE MBEDTLS_OID_AT "\x0C" /**< id-at-title AttributeType:= {id-at 12} */ +#define MBEDTLS_OID_AT_POSTAL_ADDRESS MBEDTLS_OID_AT "\x10" /**< id-at-postalAddress AttributeType:= {id-at 16} */ +#define MBEDTLS_OID_AT_POSTAL_CODE MBEDTLS_OID_AT "\x11" /**< id-at-postalCode AttributeType:= {id-at 17} */ +#define MBEDTLS_OID_AT_GIVEN_NAME MBEDTLS_OID_AT "\x2A" /**< id-at-givenName AttributeType:= {id-at 42} */ +#define MBEDTLS_OID_AT_INITIALS MBEDTLS_OID_AT "\x2B" /**< id-at-initials AttributeType:= {id-at 43} */ +#define MBEDTLS_OID_AT_GENERATION_QUALIFIER MBEDTLS_OID_AT "\x2C" /**< id-at-generationQualifier AttributeType:= {id-at 44} */ +#define MBEDTLS_OID_AT_UNIQUE_IDENTIFIER MBEDTLS_OID_AT "\x2D" /**< id-at-uniqueIdentifier AttributeType:= {id-at 45} */ +#define MBEDTLS_OID_AT_DN_QUALIFIER MBEDTLS_OID_AT "\x2E" /**< id-at-dnQualifier AttributeType:= {id-at 46} */ +#define MBEDTLS_OID_AT_PSEUDONYM MBEDTLS_OID_AT "\x41" /**< id-at-pseudonym AttributeType:= {id-at 65} */ + +#define MBEDTLS_OID_UID "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x01" /** id-domainComponent AttributeType:= {itu-t(0) data(9) pss(2342) ucl(19200300) pilot(100) pilotAttributeType(1) uid(1)} */ +#define MBEDTLS_OID_DOMAIN_COMPONENT "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19" /** id-domainComponent AttributeType:= {itu-t(0) data(9) pss(2342) ucl(19200300) pilot(100) pilotAttributeType(1) domainComponent(25)} */ + +/* + * OIDs for standard certificate extensions + */ +#define MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER MBEDTLS_OID_ID_CE "\x23" /**< id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } */ +#define MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER MBEDTLS_OID_ID_CE "\x0E" /**< id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 } */ +#define MBEDTLS_OID_KEY_USAGE MBEDTLS_OID_ID_CE "\x0F" /**< id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } */ +#define MBEDTLS_OID_CERTIFICATE_POLICIES MBEDTLS_OID_ID_CE "\x20" /**< id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } */ +#define MBEDTLS_OID_POLICY_MAPPINGS MBEDTLS_OID_ID_CE "\x21" /**< id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 } */ +#define MBEDTLS_OID_SUBJECT_ALT_NAME MBEDTLS_OID_ID_CE "\x11" /**< id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } */ +#define MBEDTLS_OID_ISSUER_ALT_NAME MBEDTLS_OID_ID_CE "\x12" /**< id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 } */ +#define MBEDTLS_OID_SUBJECT_DIRECTORY_ATTRS MBEDTLS_OID_ID_CE "\x09" /**< id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 } */ +#define MBEDTLS_OID_BASIC_CONSTRAINTS MBEDTLS_OID_ID_CE "\x13" /**< id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } */ +#define MBEDTLS_OID_NAME_CONSTRAINTS MBEDTLS_OID_ID_CE "\x1E" /**< id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 } */ +#define MBEDTLS_OID_POLICY_CONSTRAINTS MBEDTLS_OID_ID_CE "\x24" /**< id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } */ +#define MBEDTLS_OID_EXTENDED_KEY_USAGE MBEDTLS_OID_ID_CE "\x25" /**< id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 } */ +#define MBEDTLS_OID_CRL_DISTRIBUTION_POINTS MBEDTLS_OID_ID_CE "\x1F" /**< id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= { id-ce 31 } */ +#define MBEDTLS_OID_INIHIBIT_ANYPOLICY MBEDTLS_OID_ID_CE "\x36" /**< id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 } */ +#define MBEDTLS_OID_FRESHEST_CRL MBEDTLS_OID_ID_CE "\x2E" /**< id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 } */ + +/* + * Certificate policies + */ +#define MBEDTLS_OID_ANY_POLICY MBEDTLS_OID_CERTIFICATE_POLICIES "\x00" /**< anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 } */ + +/* + * Netscape certificate extensions + */ +#define MBEDTLS_OID_NS_CERT MBEDTLS_OID_NETSCAPE "\x01" +#define MBEDTLS_OID_NS_CERT_TYPE MBEDTLS_OID_NS_CERT "\x01" +#define MBEDTLS_OID_NS_BASE_URL MBEDTLS_OID_NS_CERT "\x02" +#define MBEDTLS_OID_NS_REVOCATION_URL MBEDTLS_OID_NS_CERT "\x03" +#define MBEDTLS_OID_NS_CA_REVOCATION_URL MBEDTLS_OID_NS_CERT "\x04" +#define MBEDTLS_OID_NS_RENEWAL_URL MBEDTLS_OID_NS_CERT "\x07" +#define MBEDTLS_OID_NS_CA_POLICY_URL MBEDTLS_OID_NS_CERT "\x08" +#define MBEDTLS_OID_NS_SSL_SERVER_NAME MBEDTLS_OID_NS_CERT "\x0C" +#define MBEDTLS_OID_NS_COMMENT MBEDTLS_OID_NS_CERT "\x0D" +#define MBEDTLS_OID_NS_DATA_TYPE MBEDTLS_OID_NETSCAPE "\x02" +#define MBEDTLS_OID_NS_CERT_SEQUENCE MBEDTLS_OID_NS_DATA_TYPE "\x05" + +/* + * OIDs for CRL extensions + */ +#define MBEDTLS_OID_PRIVATE_KEY_USAGE_PERIOD MBEDTLS_OID_ID_CE "\x10" +#define MBEDTLS_OID_CRL_NUMBER MBEDTLS_OID_ID_CE "\x14" /**< id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 } */ + +/* + * X.509 v3 Extended key usage OIDs + */ +#define MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE MBEDTLS_OID_EXTENDED_KEY_USAGE "\x00" /**< anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 } */ + +#define MBEDTLS_OID_KP MBEDTLS_OID_PKIX "\x03" /**< id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } */ +#define MBEDTLS_OID_SERVER_AUTH MBEDTLS_OID_KP "\x01" /**< id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } */ +#define MBEDTLS_OID_CLIENT_AUTH MBEDTLS_OID_KP "\x02" /**< id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } */ +#define MBEDTLS_OID_CODE_SIGNING MBEDTLS_OID_KP "\x03" /**< id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } */ +#define MBEDTLS_OID_EMAIL_PROTECTION MBEDTLS_OID_KP "\x04" /**< id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } */ +#define MBEDTLS_OID_TIME_STAMPING MBEDTLS_OID_KP "\x08" /**< id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 } */ +#define MBEDTLS_OID_OCSP_SIGNING MBEDTLS_OID_KP "\x09" /**< id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } */ + +/** + * Wi-SUN Alliance Field Area Network + * { iso(1) identified-organization(3) dod(6) internet(1) + * private(4) enterprise(1) WiSUN(45605) FieldAreaNetwork(1) } + */ +#define MBEDTLS_OID_WISUN_FAN MBEDTLS_OID_INTERNET "\x04\x01\x82\xe4\x25\x01" + +#define MBEDTLS_OID_ON MBEDTLS_OID_PKIX "\x08" /**< id-on OBJECT IDENTIFIER ::= { id-pkix 8 } */ +#define MBEDTLS_OID_ON_HW_MODULE_NAME MBEDTLS_OID_ON "\x04" /**< id-on-hardwareModuleName OBJECT IDENTIFIER ::= { id-on 4 } */ + +/* + * PKCS definition OIDs + */ + +#define MBEDTLS_OID_PKCS MBEDTLS_OID_RSA_COMPANY "\x01" /**< pkcs OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) 1 } */ +#define MBEDTLS_OID_PKCS1 MBEDTLS_OID_PKCS "\x01" /**< pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } */ +#define MBEDTLS_OID_PKCS5 MBEDTLS_OID_PKCS "\x05" /**< pkcs-5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } */ +#define MBEDTLS_OID_PKCS7 MBEDTLS_OID_PKCS "\x07" /**< pkcs-7 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 } */ +#define MBEDTLS_OID_PKCS9 MBEDTLS_OID_PKCS "\x09" /**< pkcs-9 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } */ +#define MBEDTLS_OID_PKCS12 MBEDTLS_OID_PKCS "\x0c" /**< pkcs-12 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } */ + +/* + * PKCS#1 OIDs + */ +#define MBEDTLS_OID_PKCS1_RSA MBEDTLS_OID_PKCS1 "\x01" /**< rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 } */ +#define MBEDTLS_OID_PKCS1_MD5 MBEDTLS_OID_PKCS1 "\x04" /**< md5WithRSAEncryption ::= { pkcs-1 4 } */ +#define MBEDTLS_OID_PKCS1_SHA1 MBEDTLS_OID_PKCS1 "\x05" /**< sha1WithRSAEncryption ::= { pkcs-1 5 } */ +#define MBEDTLS_OID_PKCS1_SHA224 MBEDTLS_OID_PKCS1 "\x0e" /**< sha224WithRSAEncryption ::= { pkcs-1 14 } */ +#define MBEDTLS_OID_PKCS1_SHA256 MBEDTLS_OID_PKCS1 "\x0b" /**< sha256WithRSAEncryption ::= { pkcs-1 11 } */ +#define MBEDTLS_OID_PKCS1_SHA384 MBEDTLS_OID_PKCS1 "\x0c" /**< sha384WithRSAEncryption ::= { pkcs-1 12 } */ +#define MBEDTLS_OID_PKCS1_SHA512 MBEDTLS_OID_PKCS1 "\x0d" /**< sha512WithRSAEncryption ::= { pkcs-1 13 } */ + +#define MBEDTLS_OID_RSA_SHA_OBS "\x2B\x0E\x03\x02\x1D" + +#define MBEDTLS_OID_PKCS9_EMAIL MBEDTLS_OID_PKCS9 "\x01" /**< emailAddress AttributeType ::= { pkcs-9 1 } */ + +/* RFC 4055 */ +#define MBEDTLS_OID_RSASSA_PSS MBEDTLS_OID_PKCS1 "\x0a" /**< id-RSASSA-PSS ::= { pkcs-1 10 } */ +#define MBEDTLS_OID_MGF1 MBEDTLS_OID_PKCS1 "\x08" /**< id-mgf1 ::= { pkcs-1 8 } */ + +/* + * Digest algorithms + */ +#define MBEDTLS_OID_DIGEST_ALG_MD5 MBEDTLS_OID_RSA_COMPANY "\x02\x05" /**< id-mbedtls_md5 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 5 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA1 MBEDTLS_OID_ISO_IDENTIFIED_ORG \ + MBEDTLS_OID_OIW_SECSIG_SHA1 /**< id-mbedtls_sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA224 MBEDTLS_OID_NIST_ALG "\x02\x04" /**< id-sha224 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 4 } */ +#define MBEDTLS_OID_DIGEST_ALG_SHA256 MBEDTLS_OID_NIST_ALG "\x02\x01" /**< id-mbedtls_sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1 } */ + +#define MBEDTLS_OID_DIGEST_ALG_SHA384 MBEDTLS_OID_NIST_ALG "\x02\x02" /**< id-sha384 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2 } */ + +#define MBEDTLS_OID_DIGEST_ALG_SHA512 MBEDTLS_OID_NIST_ALG "\x02\x03" /**< id-mbedtls_sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 3 } */ + +#define MBEDTLS_OID_DIGEST_ALG_RIPEMD160 MBEDTLS_OID_TELETRUST "\x03\x02\x01" /**< id-ripemd160 OBJECT IDENTIFIER :: { iso(1) identified-organization(3) teletrust(36) algorithm(3) hashAlgorithm(2) ripemd160(1) } */ + +#define MBEDTLS_OID_HMAC_SHA1 MBEDTLS_OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */ + +#define MBEDTLS_OID_HMAC_SHA224 MBEDTLS_OID_RSA_COMPANY "\x02\x08" /**< id-hmacWithSHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 8 } */ + +#define MBEDTLS_OID_HMAC_SHA256 MBEDTLS_OID_RSA_COMPANY "\x02\x09" /**< id-hmacWithSHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 9 } */ + +#define MBEDTLS_OID_HMAC_SHA384 MBEDTLS_OID_RSA_COMPANY "\x02\x0A" /**< id-hmacWithSHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 10 } */ + +#define MBEDTLS_OID_HMAC_SHA512 MBEDTLS_OID_RSA_COMPANY "\x02\x0B" /**< id-hmacWithSHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 11 } */ + +/* + * Encryption algorithms + */ +#define MBEDTLS_OID_DES_CBC MBEDTLS_OID_ISO_IDENTIFIED_ORG \ + MBEDTLS_OID_OIW_SECSIG_ALG "\x07" /**< desCBC OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 7 } */ +#define MBEDTLS_OID_DES_EDE3_CBC MBEDTLS_OID_RSA_COMPANY "\x03\x07" /**< des-ede3-cbc OBJECT IDENTIFIER ::= { iso(1) member-body(2) -- us(840) rsadsi(113549) encryptionAlgorithm(3) 7 } */ +#define MBEDTLS_OID_AES MBEDTLS_OID_NIST_ALG "\x01" /** aes OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) 1 } */ + +/* + * Key Wrapping algorithms + */ +/* + * RFC 5649 + */ +#define MBEDTLS_OID_AES128_KW MBEDTLS_OID_AES "\x05" /** id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } */ +#define MBEDTLS_OID_AES128_KWP MBEDTLS_OID_AES "\x08" /** id-aes128-wrap-pad OBJECT IDENTIFIER ::= { aes 8 } */ +#define MBEDTLS_OID_AES192_KW MBEDTLS_OID_AES "\x19" /** id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } */ +#define MBEDTLS_OID_AES192_KWP MBEDTLS_OID_AES "\x1c" /** id-aes192-wrap-pad OBJECT IDENTIFIER ::= { aes 28 } */ +#define MBEDTLS_OID_AES256_KW MBEDTLS_OID_AES "\x2d" /** id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } */ +#define MBEDTLS_OID_AES256_KWP MBEDTLS_OID_AES "\x30" /** id-aes256-wrap-pad OBJECT IDENTIFIER ::= { aes 48 } */ +/* + * PKCS#5 OIDs + */ +#define MBEDTLS_OID_PKCS5_PBKDF2 MBEDTLS_OID_PKCS5 "\x0c" /**< id-PBKDF2 OBJECT IDENTIFIER ::= {pkcs-5 12} */ +#define MBEDTLS_OID_PKCS5_PBES2 MBEDTLS_OID_PKCS5 "\x0d" /**< id-PBES2 OBJECT IDENTIFIER ::= {pkcs-5 13} */ +#define MBEDTLS_OID_PKCS5_PBMAC1 MBEDTLS_OID_PKCS5 "\x0e" /**< id-PBMAC1 OBJECT IDENTIFIER ::= {pkcs-5 14} */ + +/* + * PKCS#5 PBES1 algorithms + */ +#define MBEDTLS_OID_PKCS5_PBE_MD5_DES_CBC MBEDTLS_OID_PKCS5 "\x03" /**< pbeWithMD5AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 3} */ +#define MBEDTLS_OID_PKCS5_PBE_MD5_RC2_CBC MBEDTLS_OID_PKCS5 "\x06" /**< pbeWithMD5AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 6} */ +#define MBEDTLS_OID_PKCS5_PBE_SHA1_DES_CBC MBEDTLS_OID_PKCS5 "\x0a" /**< pbeWithSHA1AndDES-CBC OBJECT IDENTIFIER ::= {pkcs-5 10} */ +#define MBEDTLS_OID_PKCS5_PBE_SHA1_RC2_CBC MBEDTLS_OID_PKCS5 "\x0b" /**< pbeWithSHA1AndRC2-CBC OBJECT IDENTIFIER ::= {pkcs-5 11} */ + +/* + * PKCS#7 OIDs + */ +#define MBEDTLS_OID_PKCS7_DATA MBEDTLS_OID_PKCS7 "\x01" /**< Content type is Data OBJECT IDENTIFIER ::= {pkcs-7 1} */ +#define MBEDTLS_OID_PKCS7_SIGNED_DATA MBEDTLS_OID_PKCS7 "\x02" /**< Content type is Signed Data OBJECT IDENTIFIER ::= {pkcs-7 2} */ +#define MBEDTLS_OID_PKCS7_ENVELOPED_DATA MBEDTLS_OID_PKCS7 "\x03" /**< Content type is Enveloped Data OBJECT IDENTIFIER ::= {pkcs-7 3} */ +#define MBEDTLS_OID_PKCS7_SIGNED_AND_ENVELOPED_DATA MBEDTLS_OID_PKCS7 "\x04" /**< Content type is Signed and Enveloped Data OBJECT IDENTIFIER ::= {pkcs-7 4} */ +#define MBEDTLS_OID_PKCS7_DIGESTED_DATA MBEDTLS_OID_PKCS7 "\x05" /**< Content type is Digested Data OBJECT IDENTIFIER ::= {pkcs-7 5} */ +#define MBEDTLS_OID_PKCS7_ENCRYPTED_DATA MBEDTLS_OID_PKCS7 "\x06" /**< Content type is Encrypted Data OBJECT IDENTIFIER ::= {pkcs-7 6} */ + +/* + * PKCS#8 OIDs + */ +#define MBEDTLS_OID_PKCS9_CSR_EXT_REQ MBEDTLS_OID_PKCS9 "\x0e" /**< extensionRequest OBJECT IDENTIFIER ::= {pkcs-9 14} */ + +/* + * PKCS#12 PBE OIDs + */ +#define MBEDTLS_OID_PKCS12_PBE MBEDTLS_OID_PKCS12 "\x01" /**< pkcs-12PbeIds OBJECT IDENTIFIER ::= {pkcs-12 1} */ + +#define MBEDTLS_OID_PKCS12_PBE_SHA1_DES3_EDE_CBC MBEDTLS_OID_PKCS12_PBE "\x03" /**< pbeWithSHAAnd3-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 3} */ +#define MBEDTLS_OID_PKCS12_PBE_SHA1_DES2_EDE_CBC MBEDTLS_OID_PKCS12_PBE "\x04" /**< pbeWithSHAAnd2-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 4} */ +#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC2_128_CBC MBEDTLS_OID_PKCS12_PBE "\x05" /**< pbeWithSHAAnd128BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 5} */ +#define MBEDTLS_OID_PKCS12_PBE_SHA1_RC2_40_CBC MBEDTLS_OID_PKCS12_PBE "\x06" /**< pbeWithSHAAnd40BitRC2-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 6} */ + +/* + * EC key algorithms from RFC 5480 + */ + +/* id-ecPublicKey OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } */ +#define MBEDTLS_OID_EC_ALG_UNRESTRICTED MBEDTLS_OID_ANSI_X9_62 "\x02\01" + +/* id-ecDH OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) + * schemes(1) ecdh(12) } */ +#define MBEDTLS_OID_EC_ALG_ECDH MBEDTLS_OID_CERTICOM "\x01\x0c" + +/* + * ECParameters namedCurve identifiers, from RFC 5480, RFC 5639, and SEC2 + */ + +/* secp192r1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 1 } */ +#define MBEDTLS_OID_EC_GRP_SECP192R1 MBEDTLS_OID_ANSI_X9_62 "\x03\x01\x01" + +/* secp224r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 33 } */ +#define MBEDTLS_OID_EC_GRP_SECP224R1 MBEDTLS_OID_CERTICOM "\x00\x21" + +/* secp256r1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3) prime(1) 7 } */ +#define MBEDTLS_OID_EC_GRP_SECP256R1 MBEDTLS_OID_ANSI_X9_62 "\x03\x01\x07" + +/* secp384r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 34 } */ +#define MBEDTLS_OID_EC_GRP_SECP384R1 MBEDTLS_OID_CERTICOM "\x00\x22" + +/* secp521r1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 35 } */ +#define MBEDTLS_OID_EC_GRP_SECP521R1 MBEDTLS_OID_CERTICOM "\x00\x23" + +/* secp192k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 31 } */ +#define MBEDTLS_OID_EC_GRP_SECP192K1 MBEDTLS_OID_CERTICOM "\x00\x1f" + +/* secp224k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 32 } */ +#define MBEDTLS_OID_EC_GRP_SECP224K1 MBEDTLS_OID_CERTICOM "\x00\x20" + +/* secp256k1 OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) 10 } */ +#define MBEDTLS_OID_EC_GRP_SECP256K1 MBEDTLS_OID_CERTICOM "\x00\x0a" + +/* RFC 5639 4.1 + * ecStdCurvesAndGeneration OBJECT IDENTIFIER::= {iso(1) + * identified-organization(3) teletrust(36) algorithm(3) signature- + * algorithm(3) ecSign(2) 8} + * ellipticCurve OBJECT IDENTIFIER ::= {ecStdCurvesAndGeneration 1} + * versionOne OBJECT IDENTIFIER ::= {ellipticCurve 1} */ +#define MBEDTLS_OID_EC_BRAINPOOL_V1 MBEDTLS_OID_TELETRUST "\x03\x03\x02\x08\x01\x01" + +/* brainpoolP256r1 OBJECT IDENTIFIER ::= {versionOne 7} */ +#define MBEDTLS_OID_EC_GRP_BP256R1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x07" + +/* brainpoolP384r1 OBJECT IDENTIFIER ::= {versionOne 11} */ +#define MBEDTLS_OID_EC_GRP_BP384R1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x0B" + +/* brainpoolP512r1 OBJECT IDENTIFIER ::= {versionOne 13} */ +#define MBEDTLS_OID_EC_GRP_BP512R1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x0D" + +/* + * SEC1 C.1 + * + * prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } + * id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1)} + */ +#define MBEDTLS_OID_ANSI_X9_62_FIELD_TYPE MBEDTLS_OID_ANSI_X9_62 "\x01" +#define MBEDTLS_OID_ANSI_X9_62_PRIME_FIELD MBEDTLS_OID_ANSI_X9_62_FIELD_TYPE "\x01" + +/* + * ECDSA signature identifiers, from RFC 5480 + */ +#define MBEDTLS_OID_ANSI_X9_62_SIG MBEDTLS_OID_ANSI_X9_62 "\x04" /* signatures(4) */ +#define MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 MBEDTLS_OID_ANSI_X9_62_SIG "\x03" /* ecdsa-with-SHA2(3) */ + +/* ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) 1 } */ +#define MBEDTLS_OID_ECDSA_SHA1 MBEDTLS_OID_ANSI_X9_62_SIG "\x01" + +/* ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 1 } */ +#define MBEDTLS_OID_ECDSA_SHA224 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x01" + +/* ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 2 } */ +#define MBEDTLS_OID_ECDSA_SHA256 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x02" + +/* ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 3 } */ +#define MBEDTLS_OID_ECDSA_SHA384 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x03" + +/* ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) + * ecdsa-with-SHA2(3) 4 } */ +#define MBEDTLS_OID_ECDSA_SHA512 MBEDTLS_OID_ANSI_X9_62_SIG_SHA2 "\x04" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Base OID descriptor structure + */ +typedef struct mbedtls_oid_descriptor_t { + const char *MBEDTLS_PRIVATE(asn1); /*!< OID ASN.1 representation */ + size_t MBEDTLS_PRIVATE(asn1_len); /*!< length of asn1 */ +#if !defined(MBEDTLS_X509_REMOVE_INFO) + const char *MBEDTLS_PRIVATE(name); /*!< official name (e.g. from RFC) */ + const char *MBEDTLS_PRIVATE(description); /*!< human friendly description */ +#endif +} mbedtls_oid_descriptor_t; + +/** + * \brief Translate an ASN.1 OID into its numeric representation + * (e.g. "\x2A\x86\x48\x86\xF7\x0D" into "1.2.840.113549") + * + * \param buf buffer to put representation in + * \param size size of the buffer + * \param oid OID to translate + * + * \return Length of the string written (excluding final NULL) or + * MBEDTLS_ERR_OID_BUF_TOO_SMALL in case of error + */ +int mbedtls_oid_get_numeric_string(char *buf, size_t size, const mbedtls_asn1_buf *oid); + +/** + * \brief Translate an X.509 extension OID into local values + * + * \param oid OID to use + * \param ext_type place to store the extension type + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_x509_ext_type(const mbedtls_asn1_buf *oid, int *ext_type); + +/** + * \brief Translate an X.509 attribute type OID into the short name + * (e.g. the OID for an X520 Common Name into "CN") + * + * \param oid OID to use + * \param short_name place to store the string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_attr_short_name(const mbedtls_asn1_buf *oid, const char **short_name); + +/** + * \brief Translate PublicKeyAlgorithm OID into pk_type + * + * \param oid OID to use + * \param pk_alg place to store public key algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_pk_alg(const mbedtls_asn1_buf *oid, mbedtls_pk_type_t *pk_alg); + +/** + * \brief Translate pk_type into PublicKeyAlgorithm OID + * + * \param pk_alg Public key type to look for + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_pk_alg(mbedtls_pk_type_t pk_alg, + const char **oid, size_t *olen); + +#if defined(MBEDTLS_ECP_C) +/** + * \brief Translate NamedCurve OID into an EC group identifier + * + * \param oid OID to use + * \param grp_id place to store group id + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_ec_grp(const mbedtls_asn1_buf *oid, mbedtls_ecp_group_id *grp_id); + +/** + * \brief Translate EC group identifier into NamedCurve OID + * + * \param grp_id EC group identifier + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_ec_grp(mbedtls_ecp_group_id grp_id, + const char **oid, size_t *olen); +#endif /* MBEDTLS_ECP_C */ + +/** + * \brief Translate SignatureAlgorithm OID into md_type and pk_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * \param pk_alg place to store public key algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_sig_alg(const mbedtls_asn1_buf *oid, + mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg); + +/** + * \brief Translate SignatureAlgorithm OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_sig_alg_desc(const mbedtls_asn1_buf *oid, const char **desc); + +/** + * \brief Translate md_type and pk_type into SignatureAlgorithm OID + * + * \param md_alg message digest algorithm + * \param pk_alg public key algorithm + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_sig_alg(mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg, + const char **oid, size_t *olen); + +/** + * \brief Translate hmac algorithm OID into md_type + * + * \param oid OID to use + * \param md_hmac place to store message hmac algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_md_hmac(const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_hmac); + +/** + * \brief Translate hash algorithm OID into md_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_md_alg(const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg); + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/** + * \brief Translate Extended Key Usage OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_extended_key_usage(const mbedtls_asn1_buf *oid, const char **desc); +#endif + +/** + * \brief Translate certificate policies OID into description + * + * \param oid OID to use + * \param desc place to store string pointer + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_certificate_policies(const mbedtls_asn1_buf *oid, const char **desc); + +/** + * \brief Translate md_type into hash algorithm OID + * + * \param md_alg message digest algorithm + * \param oid place to store ASN.1 OID string pointer + * \param olen length of the OID + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_oid_by_md(mbedtls_md_type_t md_alg, const char **oid, size_t *olen); + +#if defined(MBEDTLS_CIPHER_C) +/** + * \brief Translate encryption algorithm OID into cipher_type + * + * \param oid OID to use + * \param cipher_alg place to store cipher algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_cipher_alg(const mbedtls_asn1_buf *oid, mbedtls_cipher_type_t *cipher_alg); +#endif /* MBEDTLS_CIPHER_C */ + +#if defined(MBEDTLS_PKCS12_C) +/** + * \brief Translate PKCS#12 PBE algorithm OID into md_type and + * cipher_type + * + * \param oid OID to use + * \param md_alg place to store message digest algorithm + * \param cipher_alg place to store cipher algorithm + * + * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND + */ +int mbedtls_oid_get_pkcs12_pbe_alg(const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg, + mbedtls_cipher_type_t *cipher_alg); +#endif /* MBEDTLS_PKCS12_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* oid.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/pem.h b/r5dev/thirdparty/mbedtls/include/mbedtls/pem.h new file mode 100644 index 00000000..a33fc65e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/pem.h @@ -0,0 +1,172 @@ +/** + * \file pem.h + * + * \brief Privacy Enhanced Mail (PEM) decoding + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PEM_H +#define MBEDTLS_PEM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include + +/** + * \name PEM Error codes + * These error codes are returned in case of errors reading the + * PEM data. + * \{ + */ +/** No PEM header or footer found. */ +#define MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT -0x1080 +/** PEM string is not as expected. */ +#define MBEDTLS_ERR_PEM_INVALID_DATA -0x1100 +/** Failed to allocate memory. */ +#define MBEDTLS_ERR_PEM_ALLOC_FAILED -0x1180 +/** RSA IV is not in hex-format. */ +#define MBEDTLS_ERR_PEM_INVALID_ENC_IV -0x1200 +/** Unsupported key encryption algorithm. */ +#define MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG -0x1280 +/** Private key password can't be empty. */ +#define MBEDTLS_ERR_PEM_PASSWORD_REQUIRED -0x1300 +/** Given private key password does not allow for correct decryption. */ +#define MBEDTLS_ERR_PEM_PASSWORD_MISMATCH -0x1380 +/** Unavailable feature, e.g. hashing/encryption combination. */ +#define MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE -0x1400 +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_PEM_BAD_INPUT_DATA -0x1480 +/** \} name PEM Error codes */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MBEDTLS_PEM_PARSE_C) +/** + * \brief PEM context structure + */ +typedef struct mbedtls_pem_context { + unsigned char *MBEDTLS_PRIVATE(buf); /*!< buffer for decoded data */ + size_t MBEDTLS_PRIVATE(buflen); /*!< length of the buffer */ + unsigned char *MBEDTLS_PRIVATE(info); /*!< buffer for extra header information */ +} +mbedtls_pem_context; + +/** + * \brief PEM context setup + * + * \param ctx context to be initialized + */ +void mbedtls_pem_init(mbedtls_pem_context *ctx); + +/** + * \brief Read a buffer for PEM information and store the resulting + * data into the specified context buffers. + * + * \param ctx context to use + * \param header header string to seek and expect + * \param footer footer string to seek and expect + * \param data source data to look in (must be nul-terminated) + * \param pwd password for decryption (can be NULL) + * \param pwdlen length of password + * \param use_len destination for total length used (set after header is + * correctly read, so unless you get + * MBEDTLS_ERR_PEM_BAD_INPUT_DATA or + * MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT, use_len is + * the length to skip) + * + * \note Attempts to check password correctness by verifying if + * the decrypted text starts with an ASN.1 sequence of + * appropriate length + * + * \note \c mbedtls_pem_free must be called on PEM context before + * the PEM context can be reused in another call to + * \c mbedtls_pem_read_buffer + * + * \return 0 on success, or a specific PEM error code + */ +int mbedtls_pem_read_buffer(mbedtls_pem_context *ctx, const char *header, const char *footer, + const unsigned char *data, + const unsigned char *pwd, + size_t pwdlen, size_t *use_len); + +/** + * \brief Get the pointer to the decoded binary data in a PEM context. + * + * \param ctx PEM context to access. + * \param buflen On success, this will contain the length of the binary data. + * This must be a valid (non-null) pointer. + * + * \return A pointer to the decoded binary data. + * + * \note The returned pointer remains valid only until \p ctx is + modified or freed. + */ +static inline const unsigned char *mbedtls_pem_get_buffer(mbedtls_pem_context *ctx, size_t *buflen) +{ + *buflen = ctx->MBEDTLS_PRIVATE(buflen); + return ctx->MBEDTLS_PRIVATE(buf); +} + + +/** + * \brief PEM context memory freeing + * + * \param ctx context to be freed + */ +void mbedtls_pem_free(mbedtls_pem_context *ctx); +#endif /* MBEDTLS_PEM_PARSE_C */ + +#if defined(MBEDTLS_PEM_WRITE_C) +/** + * \brief Write a buffer of PEM information from a DER encoded + * buffer. + * + * \param header The header string to write. + * \param footer The footer string to write. + * \param der_data The DER data to encode. + * \param der_len The length of the DER data \p der_data in Bytes. + * \param buf The buffer to write to. + * \param buf_len The length of the output buffer \p buf in Bytes. + * \param olen The address at which to store the total length written + * or required (if \p buf_len is not enough). + * + * \note You may pass \c NULL for \p buf and \c 0 for \p buf_len + * to request the length of the resulting PEM buffer in + * `*olen`. + * + * \note This function may be called with overlapping \p der_data + * and \p buf buffers. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL if \p buf isn't large + * enough to hold the PEM buffer. In this case, `*olen` holds + * the required minimum size of \p buf. + * \return Another PEM or BASE64 error code on other kinds of failure. + */ +int mbedtls_pem_write_buffer(const char *header, const char *footer, + const unsigned char *der_data, size_t der_len, + unsigned char *buf, size_t buf_len, size_t *olen); +#endif /* MBEDTLS_PEM_WRITE_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* pem.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/pk.h b/r5dev/thirdparty/mbedtls/include/mbedtls/pk.h new file mode 100644 index 00000000..3de7a8fa --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/pk.h @@ -0,0 +1,1052 @@ +/** + * \file pk.h + * + * \brief Public Key abstraction layer + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_PK_H +#define MBEDTLS_PK_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/md.h" + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif + +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif + +#if defined(MBEDTLS_ECDSA_C) +#include "mbedtls/ecdsa.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#endif + +/** Memory allocation failed. */ +#define MBEDTLS_ERR_PK_ALLOC_FAILED -0x3F80 +/** Type mismatch, eg attempt to encrypt with an ECDSA key */ +#define MBEDTLS_ERR_PK_TYPE_MISMATCH -0x3F00 +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_PK_BAD_INPUT_DATA -0x3E80 +/** Read/write of file failed. */ +#define MBEDTLS_ERR_PK_FILE_IO_ERROR -0x3E00 +/** Unsupported key version */ +#define MBEDTLS_ERR_PK_KEY_INVALID_VERSION -0x3D80 +/** Invalid key tag or value. */ +#define MBEDTLS_ERR_PK_KEY_INVALID_FORMAT -0x3D00 +/** Key algorithm is unsupported (only RSA and EC are supported). */ +#define MBEDTLS_ERR_PK_UNKNOWN_PK_ALG -0x3C80 +/** Private key password can't be empty. */ +#define MBEDTLS_ERR_PK_PASSWORD_REQUIRED -0x3C00 +/** Given private key password does not allow for correct decryption. */ +#define MBEDTLS_ERR_PK_PASSWORD_MISMATCH -0x3B80 +/** The pubkey tag or value is invalid (only RSA and EC are supported). */ +#define MBEDTLS_ERR_PK_INVALID_PUBKEY -0x3B00 +/** The algorithm tag or value is invalid. */ +#define MBEDTLS_ERR_PK_INVALID_ALG -0x3A80 +/** Elliptic curve is unsupported (only NIST curves are supported). */ +#define MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE -0x3A00 +/** Unavailable feature, e.g. RSA disabled for RSA key. */ +#define MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE -0x3980 +/** The buffer contains a valid signature followed by more data. */ +#define MBEDTLS_ERR_PK_SIG_LEN_MISMATCH -0x3900 +/** The output buffer is too small. */ +#define MBEDTLS_ERR_PK_BUFFER_TOO_SMALL -0x3880 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Public key types + */ +typedef enum { + MBEDTLS_PK_NONE=0, + MBEDTLS_PK_RSA, + MBEDTLS_PK_ECKEY, + MBEDTLS_PK_ECKEY_DH, + MBEDTLS_PK_ECDSA, + MBEDTLS_PK_RSA_ALT, + MBEDTLS_PK_RSASSA_PSS, + MBEDTLS_PK_OPAQUE, +} mbedtls_pk_type_t; + +/** + * \brief Options for RSASSA-PSS signature verification. + * See \c mbedtls_rsa_rsassa_pss_verify_ext() + */ +typedef struct mbedtls_pk_rsassa_pss_options { + /** The digest to use for MGF1 in PSS. + * + * \note When #MBEDTLS_USE_PSA_CRYPTO is enabled and #MBEDTLS_RSA_C is + * disabled, this must be equal to the \c md_alg argument passed + * to mbedtls_pk_verify_ext(). In a future version of the library, + * this constraint may apply whenever #MBEDTLS_USE_PSA_CRYPTO is + * enabled regardless of the status of #MBEDTLS_RSA_C. + */ + mbedtls_md_type_t mgf1_hash_id; + + /** The expected length of the salt, in bytes. This may be + * #MBEDTLS_RSA_SALT_LEN_ANY to accept any salt length. + * + * \note When #MBEDTLS_USE_PSA_CRYPTO is enabled, only + * #MBEDTLS_RSA_SALT_LEN_ANY is valid. Any other value may be + * ignored (allowing any salt length). + */ + int expected_salt_len; + +} mbedtls_pk_rsassa_pss_options; + +/** + * \brief Maximum size of a signature made by mbedtls_pk_sign(). + */ +/* We need to set MBEDTLS_PK_SIGNATURE_MAX_SIZE to the maximum signature + * size among the supported signature types. Do it by starting at 0, + * then incrementally increasing to be large enough for each supported + * signature mechanism. + * + * The resulting value can be 0, for example if MBEDTLS_ECDH_C is enabled + * (which allows the pk module to be included) but neither MBEDTLS_ECDSA_C + * nor MBEDTLS_RSA_C nor any opaque signature mechanism (PSA or RSA_ALT). + */ +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE 0 + +#if (defined(MBEDTLS_RSA_C) || defined(MBEDTLS_PK_RSA_ALT_SUPPORT)) && \ + MBEDTLS_MPI_MAX_SIZE > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* For RSA, the signature can be as large as the bignum module allows. + * For RSA_ALT, the signature size is not necessarily tied to what the + * bignum module can do, but in the absence of any specific setting, + * we use that (rsa_alt_sign_wrap in library/pk_wrap.h will check). */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_MPI_MAX_SIZE +#endif + +#if defined(MBEDTLS_ECDSA_C) && \ + MBEDTLS_ECDSA_MAX_LEN > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* For ECDSA, the ecdsa module exports a constant for the maximum + * signature size. */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE MBEDTLS_ECDSA_MAX_LEN +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if PSA_SIGNATURE_MAX_SIZE > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* PSA_SIGNATURE_MAX_SIZE is the maximum size of a signature made + * through the PSA API in the PSA representation. */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE PSA_SIGNATURE_MAX_SIZE +#endif + +#if PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE + 11 > MBEDTLS_PK_SIGNATURE_MAX_SIZE +/* The Mbed TLS representation is different for ECDSA signatures: + * PSA uses the raw concatenation of r and s, + * whereas Mbed TLS uses the ASN.1 representation (SEQUENCE of two INTEGERs). + * Add the overhead of ASN.1: up to (1+2) + 2 * (1+2+1) for the + * types, lengths (represented by up to 2 bytes), and potential leading + * zeros of the INTEGERs and the SEQUENCE. */ +#undef MBEDTLS_PK_SIGNATURE_MAX_SIZE +#define MBEDTLS_PK_SIGNATURE_MAX_SIZE (PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE + 11) +#endif +#endif /* defined(MBEDTLS_USE_PSA_CRYPTO) */ + +/** + * \brief The following defines are meant to list ECDSA capabilities of the + * PK module in a general way (without any reference to how this + * is achieved, which can be either through PSA driver or + * MBEDTLS_ECDSA_C) + */ +#if !defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(MBEDTLS_ECDSA_C) +#define MBEDTLS_PK_CAN_ECDSA_SIGN +#define MBEDTLS_PK_CAN_ECDSA_VERIFY +#endif +#else /* MBEDTLS_USE_PSA_CRYPTO */ +#if defined(PSA_WANT_ALG_ECDSA) +#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) +#define MBEDTLS_PK_CAN_ECDSA_SIGN +#endif +#if defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +#define MBEDTLS_PK_CAN_ECDSA_VERIFY +#endif +#endif /* PSA_WANT_ALG_ECDSA */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_PK_CAN_ECDSA_VERIFY) || defined(MBEDTLS_PK_CAN_ECDSA_SIGN) +#define MBEDTLS_PK_CAN_ECDSA_SOME +#endif + +/** + * \brief Types for interfacing with the debug module + */ +typedef enum { + MBEDTLS_PK_DEBUG_NONE = 0, + MBEDTLS_PK_DEBUG_MPI, + MBEDTLS_PK_DEBUG_ECP, +} mbedtls_pk_debug_type; + +/** + * \brief Item to send to the debug module + */ +typedef struct mbedtls_pk_debug_item { + mbedtls_pk_debug_type MBEDTLS_PRIVATE(type); + const char *MBEDTLS_PRIVATE(name); + void *MBEDTLS_PRIVATE(value); +} mbedtls_pk_debug_item; + +/** Maximum number of item send for debugging, plus 1 */ +#define MBEDTLS_PK_DEBUG_MAX_ITEMS 3 + +/** + * \brief Public key information and operations + * + * \note The library does not support custom pk info structures, + * only built-in structures returned by + * mbedtls_cipher_info_from_type(). + */ +typedef struct mbedtls_pk_info_t mbedtls_pk_info_t; + +/** + * \brief Public key container + */ +typedef struct mbedtls_pk_context { + const mbedtls_pk_info_t *MBEDTLS_PRIVATE(pk_info); /**< Public key information */ + void *MBEDTLS_PRIVATE(pk_ctx); /**< Underlying public key context */ +} mbedtls_pk_context; + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Context for resuming operations + */ +typedef struct { + const mbedtls_pk_info_t *MBEDTLS_PRIVATE(pk_info); /**< Public key information */ + void *MBEDTLS_PRIVATE(rs_ctx); /**< Underlying restart context */ +} mbedtls_pk_restart_ctx; +#else /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ +/* Now we can declare functions that take a pointer to that */ +typedef void mbedtls_pk_restart_ctx; +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +/** + * \brief Types for RSA-alt abstraction + */ +typedef int (*mbedtls_pk_rsa_alt_decrypt_func)(void *ctx, size_t *olen, + const unsigned char *input, unsigned char *output, + size_t output_max_len); +typedef int (*mbedtls_pk_rsa_alt_sign_func)(void *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, unsigned int hashlen, + const unsigned char *hash, unsigned char *sig); +typedef size_t (*mbedtls_pk_rsa_alt_key_len_func)(void *ctx); +#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */ + +/** + * \brief Return information associated with the given PK type + * + * \param pk_type PK type to search for. + * + * \return The PK info associated with the type or NULL if not found. + */ +const mbedtls_pk_info_t *mbedtls_pk_info_from_type(mbedtls_pk_type_t pk_type); + +/** + * \brief Initialize a #mbedtls_pk_context (as NONE). + * + * \param ctx The context to initialize. + * This must not be \c NULL. + */ +void mbedtls_pk_init(mbedtls_pk_context *ctx); + +/** + * \brief Free the components of a #mbedtls_pk_context. + * + * \param ctx The context to clear. It must have been initialized. + * If this is \c NULL, this function does nothing. + * + * \note For contexts that have been set up with + * mbedtls_pk_setup_opaque(), this does not free the underlying + * PSA key and you still need to call psa_destroy_key() + * independently if you want to destroy that key. + */ +void mbedtls_pk_free(mbedtls_pk_context *ctx); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Initialize a restart context + * + * \param ctx The context to initialize. + * This must not be \c NULL. + */ +void mbedtls_pk_restart_init(mbedtls_pk_restart_ctx *ctx); + +/** + * \brief Free the components of a restart context + * + * \param ctx The context to clear. It must have been initialized. + * If this is \c NULL, this function does nothing. + */ +void mbedtls_pk_restart_free(mbedtls_pk_restart_ctx *ctx); +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +/** + * \brief Initialize a PK context with the information given + * and allocates the type-specific PK subcontext. + * + * \param ctx Context to initialize. It must not have been set + * up yet (type #MBEDTLS_PK_NONE). + * \param info Information to use + * + * \return 0 on success, + * MBEDTLS_ERR_PK_BAD_INPUT_DATA on invalid input, + * MBEDTLS_ERR_PK_ALLOC_FAILED on allocation failure. + * + * \note For contexts holding an RSA-alt key, use + * \c mbedtls_pk_setup_rsa_alt() instead. + */ +int mbedtls_pk_setup(mbedtls_pk_context *ctx, const mbedtls_pk_info_t *info); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * \brief Initialize a PK context to wrap a PSA key. + * + * \note This function replaces mbedtls_pk_setup() for contexts + * that wrap a (possibly opaque) PSA key instead of + * storing and manipulating the key material directly. + * + * \param ctx The context to initialize. It must be empty (type NONE). + * \param key The PSA key to wrap, which must hold an ECC or RSA key + * pair (see notes below). + * + * \note The wrapped key must remain valid as long as the + * wrapping PK context is in use, that is at least between + * the point this function is called and the point + * mbedtls_pk_free() is called on this context. The wrapped + * key might then be independently used or destroyed. + * + * \note This function is currently only available for ECC or RSA + * key pairs (that is, keys containing private key material). + * Support for other key types may be added later. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_PK_BAD_INPUT_DATA on invalid input + * (context already used, invalid key identifier). + * \return #MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE if the key is not an + * ECC key pair. + * \return #MBEDTLS_ERR_PK_ALLOC_FAILED on allocation failure. + */ +int mbedtls_pk_setup_opaque(mbedtls_pk_context *ctx, + const mbedtls_svc_key_id_t key); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +/** + * \brief Initialize an RSA-alt context + * + * \param ctx Context to initialize. It must not have been set + * up yet (type #MBEDTLS_PK_NONE). + * \param key RSA key pointer + * \param decrypt_func Decryption function + * \param sign_func Signing function + * \param key_len_func Function returning key length in bytes + * + * \return 0 on success, or MBEDTLS_ERR_PK_BAD_INPUT_DATA if the + * context wasn't already initialized as RSA_ALT. + * + * \note This function replaces \c mbedtls_pk_setup() for RSA-alt. + */ +int mbedtls_pk_setup_rsa_alt(mbedtls_pk_context *ctx, void *key, + mbedtls_pk_rsa_alt_decrypt_func decrypt_func, + mbedtls_pk_rsa_alt_sign_func sign_func, + mbedtls_pk_rsa_alt_key_len_func key_len_func); +#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */ + +/** + * \brief Get the size in bits of the underlying key + * + * \param ctx The context to query. It must have been initialized. + * + * \return Key size in bits, or 0 on error + */ +size_t mbedtls_pk_get_bitlen(const mbedtls_pk_context *ctx); + +/** + * \brief Get the length in bytes of the underlying key + * + * \param ctx The context to query. It must have been initialized. + * + * \return Key length in bytes, or 0 on error + */ +static inline size_t mbedtls_pk_get_len(const mbedtls_pk_context *ctx) +{ + return (mbedtls_pk_get_bitlen(ctx) + 7) / 8; +} + +/** + * \brief Tell if a context can do the operation given by type + * + * \param ctx The context to query. It must have been initialized. + * \param type The desired type. + * + * \return 1 if the context can do operations on the given type. + * \return 0 if the context cannot do the operations on the given + * type. This is always the case for a context that has + * been initialized but not set up, or that has been + * cleared with mbedtls_pk_free(). + */ +int mbedtls_pk_can_do(const mbedtls_pk_context *ctx, mbedtls_pk_type_t type); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * \brief Tell if context can do the operation given by PSA algorithm + * + * \param ctx The context to query. It must have been initialized. + * \param alg PSA algorithm to check against, the following are allowed: + * PSA_ALG_RSA_PKCS1V15_SIGN(hash), + * PSA_ALG_RSA_PSS(hash), + * PSA_ALG_RSA_PKCS1V15_CRYPT, + * PSA_ALG_ECDSA(hash), + * PSA_ALG_ECDH, where hash is a specific hash. + * \param usage PSA usage flag to check against, must be composed of: + * PSA_KEY_USAGE_SIGN_HASH + * PSA_KEY_USAGE_DECRYPT + * PSA_KEY_USAGE_DERIVE. + * Context key must match all passed usage flags. + * + * \warning Since the set of allowed algorithms and usage flags may be + * expanded in the future, the return value \c 0 should not + * be taken in account for non-allowed algorithms and usage + * flags. + * + * \return 1 if the context can do operations on the given type. + * \return 0 if the context cannot do the operations on the given + * type, for non-allowed algorithms and usage flags, or + * for a context that has been initialized but not set up + * or that has been cleared with mbedtls_pk_free(). + */ +int mbedtls_pk_can_do_ext(const mbedtls_pk_context *ctx, psa_algorithm_t alg, + psa_key_usage_t usage); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +/** + * \brief Verify signature (including padding if relevant). + * + * \param ctx The PK context to use. It must have been set up. + * \param md_alg Hash algorithm used. + * This can be #MBEDTLS_MD_NONE if the signature algorithm + * does not rely on a hash algorithm (non-deterministic + * ECDSA, RSA PKCS#1 v1.5). + * For PKCS#1 v1.5, if \p md_alg is #MBEDTLS_MD_NONE, then + * \p hash is the DigestInfo structure used by RFC 8017 + * §9.2 steps 3–6. If \p md_alg is a valid hash + * algorithm then \p hash is the digest itself, and this + * function calculates the DigestInfo encoding internally. + * \param hash Hash of the message to sign + * \param hash_len Hash length + * \param sig Signature to verify + * \param sig_len Signature length + * + * \return 0 on success (signature is valid), + * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid + * signature in sig but its length is less than \p siglen, + * or a specific error code. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * Use \c mbedtls_pk_verify_ext( MBEDTLS_PK_RSASSA_PSS, ... ) + * to verify RSASSA_PSS signatures. + */ +int mbedtls_pk_verify(mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len); + +/** + * \brief Restartable version of \c mbedtls_pk_verify() + * + * \note Performs the same job as \c mbedtls_pk_verify(), but can + * return early and restart according to the limit set with + * \c mbedtls_ecp_set_max_ops() to reduce blocking for ECC + * operations. For RSA, same as \c mbedtls_pk_verify(). + * + * \param ctx The PK context to use. It must have been set up. + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length or 0 (see notes) + * \param sig Signature to verify + * \param sig_len Signature length + * \param rs_ctx Restart context (NULL to disable restart) + * + * \return See \c mbedtls_pk_verify(), or + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + */ +int mbedtls_pk_verify_restartable(mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len, + mbedtls_pk_restart_ctx *rs_ctx); + +/** + * \brief Verify signature, with options. + * (Includes verification of the padding depending on type.) + * + * \param type Signature type (inc. possible padding type) to verify + * \param options Pointer to type-specific options, or NULL + * \param ctx The PK context to use. It must have been set up. + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length or 0 (see notes) + * \param sig Signature to verify + * \param sig_len Signature length + * + * \return 0 on success (signature is valid), + * #MBEDTLS_ERR_PK_TYPE_MISMATCH if the PK context can't be + * used for this type of signatures, + * #MBEDTLS_ERR_PK_SIG_LEN_MISMATCH if there is a valid + * signature in sig but its length is less than \p siglen, + * or a specific error code. + * + * \note If hash_len is 0, then the length associated with md_alg + * is used instead, or an error returned if it is invalid. + * + * \note md_alg may be MBEDTLS_MD_NONE, only if hash_len != 0 + * + * \note If type is MBEDTLS_PK_RSASSA_PSS, then options must point + * to a mbedtls_pk_rsassa_pss_options structure, + * otherwise it must be NULL. Note that if + * #MBEDTLS_USE_PSA_CRYPTO is defined, the salt length is not + * verified as PSA_ALG_RSA_PSS_ANY_SALT is used. + */ +int mbedtls_pk_verify_ext(mbedtls_pk_type_t type, const void *options, + mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len); + +/** + * \brief Make signature, including padding if relevant. + * + * \param ctx The PK context to use. It must have been set up + * with a private key. + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length + * \param sig Place to write the signature. + * It must have enough room for the signature. + * #MBEDTLS_PK_SIGNATURE_MAX_SIZE is always enough. + * You may use a smaller buffer if it is large enough + * given the key type. + * \param sig_size The size of the \p sig buffer in bytes. + * \param sig_len On successful return, + * the number of bytes written to \p sig. + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \return 0 on success, or a specific error code. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * There is no interface in the PK module to make RSASSA-PSS + * signatures yet. + * + * \note For RSA, md_alg may be MBEDTLS_MD_NONE if hash_len != 0. + * For ECDSA, md_alg may never be MBEDTLS_MD_NONE. + */ +int mbedtls_pk_sign(mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +#if defined(MBEDTLS_PSA_CRYPTO_C) +/** + * \brief Make signature given a signature type. + * + * \param pk_type Signature type. + * \param ctx The PK context to use. It must have been set up + * with a private key. + * \param md_alg Hash algorithm used (see notes) + * \param hash Hash of the message to sign + * \param hash_len Hash length + * \param sig Place to write the signature. + * It must have enough room for the signature. + * #MBEDTLS_PK_SIGNATURE_MAX_SIZE is always enough. + * You may use a smaller buffer if it is large enough + * given the key type. + * \param sig_size The size of the \p sig buffer in bytes. + * \param sig_len On successful return, + * the number of bytes written to \p sig. + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \return 0 on success, or a specific error code. + * + * \note When \p pk_type is #MBEDTLS_PK_RSASSA_PSS, + * see #PSA_ALG_RSA_PSS for a description of PSS options used. + * + * \note For RSA, md_alg may be MBEDTLS_MD_NONE if hash_len != 0. + * For ECDSA, md_alg may never be MBEDTLS_MD_NONE. + * + */ +int mbedtls_pk_sign_ext(mbedtls_pk_type_t pk_type, + mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +/** + * \brief Restartable version of \c mbedtls_pk_sign() + * + * \note Performs the same job as \c mbedtls_pk_sign(), but can + * return early and restart according to the limit set with + * \c mbedtls_ecp_set_max_ops() to reduce blocking for ECC + * operations. For RSA, same as \c mbedtls_pk_sign(). + * + * \param ctx The PK context to use. It must have been set up + * with a private key. + * \param md_alg Hash algorithm used (see notes for mbedtls_pk_sign()) + * \param hash Hash of the message to sign + * \param hash_len Hash length + * \param sig Place to write the signature. + * It must have enough room for the signature. + * #MBEDTLS_PK_SIGNATURE_MAX_SIZE is always enough. + * You may use a smaller buffer if it is large enough + * given the key type. + * \param sig_size The size of the \p sig buffer in bytes. + * \param sig_len On successful return, + * the number of bytes written to \p sig. + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * \param rs_ctx Restart context (NULL to disable restart) + * + * \return See \c mbedtls_pk_sign(). + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + */ +int mbedtls_pk_sign_restartable(mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_pk_restart_ctx *rs_ctx); + +/** + * \brief Decrypt message (including padding if relevant). + * + * \param ctx The PK context to use. It must have been set up + * with a private key. + * \param input Input to decrypt + * \param ilen Input size + * \param output Decrypted output + * \param olen Decrypted message length + * \param osize Size of the output buffer + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * + * \return 0 on success, or a specific error code. + */ +int mbedtls_pk_decrypt(mbedtls_pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +/** + * \brief Encrypt message (including padding if relevant). + * + * \param ctx The PK context to use. It must have been set up. + * \param input Message to encrypt + * \param ilen Message size + * \param output Encrypted output + * \param olen Encrypted output length + * \param osize Size of the output buffer + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \note \p f_rng is used for padding generation. + * + * \note For RSA keys, the default padding type is PKCS#1 v1.5. + * + * \return 0 on success, or a specific error code. + */ +int mbedtls_pk_encrypt(mbedtls_pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +/** + * \brief Check if a public-private pair of keys matches. + * + * \param pub Context holding a public key. + * \param prv Context holding a private (and public) key. + * \param f_rng RNG function, must not be \c NULL. + * \param p_rng RNG parameter + * + * \return \c 0 on success (keys were checked and match each other). + * \return #MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE if the keys could not + * be checked - in that case they may or may not match. + * \return #MBEDTLS_ERR_PK_BAD_INPUT_DATA if a context is invalid. + * \return Another non-zero value if the keys do not match. + */ +int mbedtls_pk_check_pair(const mbedtls_pk_context *pub, + const mbedtls_pk_context *prv, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Export debug information + * + * \param ctx The PK context to use. It must have been initialized. + * \param items Place to write debug items + * + * \return 0 on success or MBEDTLS_ERR_PK_BAD_INPUT_DATA + */ +int mbedtls_pk_debug(const mbedtls_pk_context *ctx, mbedtls_pk_debug_item *items); + +/** + * \brief Access the type name + * + * \param ctx The PK context to use. It must have been initialized. + * + * \return Type name on success, or "invalid PK" + */ +const char *mbedtls_pk_get_name(const mbedtls_pk_context *ctx); + +/** + * \brief Get the key type + * + * \param ctx The PK context to use. It must have been initialized. + * + * \return Type on success. + * \return #MBEDTLS_PK_NONE for a context that has not been set up. + */ +mbedtls_pk_type_t mbedtls_pk_get_type(const mbedtls_pk_context *ctx); + +#if defined(MBEDTLS_RSA_C) +/** + * Quick access to an RSA context inside a PK context. + * + * \warning This function can only be used when the type of the context, as + * returned by mbedtls_pk_get_type(), is #MBEDTLS_PK_RSA. + * Ensuring that is the caller's responsibility. + * Alternatively, you can check whether this function returns NULL. + * + * \return The internal RSA context held by the PK context, or NULL. + */ +static inline mbedtls_rsa_context *mbedtls_pk_rsa(const mbedtls_pk_context pk) +{ + switch (mbedtls_pk_get_type(&pk)) { + case MBEDTLS_PK_RSA: + return (mbedtls_rsa_context *) (pk).MBEDTLS_PRIVATE(pk_ctx); + default: + return NULL; + } +} +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) +/** + * Quick access to an EC context inside a PK context. + * + * \warning This function can only be used when the type of the context, as + * returned by mbedtls_pk_get_type(), is #MBEDTLS_PK_ECKEY, + * #MBEDTLS_PK_ECKEY_DH, or #MBEDTLS_PK_ECDSA. + * Ensuring that is the caller's responsibility. + * Alternatively, you can check whether this function returns NULL. + * + * \return The internal EC context held by the PK context, or NULL. + */ +static inline mbedtls_ecp_keypair *mbedtls_pk_ec(const mbedtls_pk_context pk) +{ + switch (mbedtls_pk_get_type(&pk)) { + case MBEDTLS_PK_ECKEY: + case MBEDTLS_PK_ECKEY_DH: + case MBEDTLS_PK_ECDSA: + return (mbedtls_ecp_keypair *) (pk).MBEDTLS_PRIVATE(pk_ctx); + default: + return NULL; + } +} +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_PK_PARSE_C) +/** \ingroup pk_module */ +/** + * \brief Parse a private key in PEM or DER format + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param key Input buffer to parse. + * The buffer must contain the input exactly, with no + * extra trailing material. For PEM, the buffer must + * contain a null-terminated string. + * \param keylen Size of \b key in bytes. + * For PEM data, this includes the terminating null byte, + * so \p keylen must be equal to `strlen(key) + 1`. + * \param pwd Optional password for decryption. + * Pass \c NULL if expecting a non-encrypted key. + * Pass a string of \p pwdlen bytes if expecting an encrypted + * key; a non-encrypted key will also be accepted. + * The empty password is not supported. + * \param pwdlen Size of the password in bytes. + * Ignored if \p pwd is \c NULL. + * \param f_rng RNG function, must not be \c NULL. Used for blinding. + * \param p_rng RNG parameter + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If you need a + * specific key type, check the result with mbedtls_pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_key(mbedtls_pk_context *ctx, + const unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +/** \ingroup pk_module */ +/** + * \brief Parse a public key in PEM or DER format + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param key Input buffer to parse. + * The buffer must contain the input exactly, with no + * extra trailing material. For PEM, the buffer must + * contain a null-terminated string. + * \param keylen Size of \b key in bytes. + * For PEM data, this includes the terminating null byte, + * so \p keylen must be equal to `strlen(key) + 1`. + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If you need a + * specific key type, check the result with mbedtls_pk_can_do(). + * + * \note For compressed points, see #MBEDTLS_ECP_PF_COMPRESSED for + * limitations. + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_public_key(mbedtls_pk_context *ctx, + const unsigned char *key, size_t keylen); + +#if defined(MBEDTLS_FS_IO) +/** \ingroup pk_module */ +/** + * \brief Load and parse a private key + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param path filename to read the private key from + * \param password Optional password to decrypt the file. + * Pass \c NULL if expecting a non-encrypted key. + * Pass a null-terminated string if expecting an encrypted + * key; a non-encrypted key will also be accepted. + * The empty password is not supported. + * \param f_rng RNG function, must not be \c NULL. Used for blinding. + * \param p_rng RNG parameter + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If you need a + * specific key type, check the result with mbedtls_pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_keyfile(mbedtls_pk_context *ctx, + const char *path, const char *password, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng); + +/** \ingroup pk_module */ +/** + * \brief Load and parse a public key + * + * \param ctx The PK context to fill. It must have been initialized + * but not set up. + * \param path filename to read the public key from + * + * \note On entry, ctx must be empty, either freshly initialised + * with mbedtls_pk_init() or reset with mbedtls_pk_free(). If + * you need a specific key type, check the result with + * mbedtls_pk_can_do(). + * + * \note The key is also checked for correctness. + * + * \return 0 if successful, or a specific PK or PEM error code + */ +int mbedtls_pk_parse_public_keyfile(mbedtls_pk_context *ctx, const char *path); +#endif /* MBEDTLS_FS_IO */ +#endif /* MBEDTLS_PK_PARSE_C */ + +#if defined(MBEDTLS_PK_WRITE_C) +/** + * \brief Write a private key to a PKCS#1 or SEC1 DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx PK context which must contain a valid private key. + * \param buf buffer to write to + * \param size size of the buffer + * + * \return length of data written if successful, or a specific + * error code + */ +int mbedtls_pk_write_key_der(const mbedtls_pk_context *ctx, unsigned char *buf, size_t size); + +/** + * \brief Write a public key to a SubjectPublicKeyInfo DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx PK context which must contain a valid public or private key. + * \param buf buffer to write to + * \param size size of the buffer + * + * \return length of data written if successful, or a specific + * error code + */ +int mbedtls_pk_write_pubkey_der(const mbedtls_pk_context *ctx, unsigned char *buf, size_t size); + +#if defined(MBEDTLS_PEM_WRITE_C) +/** + * \brief Write a public key to a PEM string + * + * \param ctx PK context which must contain a valid public or private key. + * \param buf Buffer to write to. The output includes a + * terminating null byte. + * \param size Size of the buffer in bytes. + * + * \return 0 if successful, or a specific error code + */ +int mbedtls_pk_write_pubkey_pem(const mbedtls_pk_context *ctx, unsigned char *buf, size_t size); + +/** + * \brief Write a private key to a PKCS#1 or SEC1 PEM string + * + * \param ctx PK context which must contain a valid private key. + * \param buf Buffer to write to. The output includes a + * terminating null byte. + * \param size Size of the buffer in bytes. + * + * \return 0 if successful, or a specific error code + */ +int mbedtls_pk_write_key_pem(const mbedtls_pk_context *ctx, unsigned char *buf, size_t size); +#endif /* MBEDTLS_PEM_WRITE_C */ +#endif /* MBEDTLS_PK_WRITE_C */ + +/* + * WARNING: Low-level functions. You probably do not want to use these unless + * you are certain you do ;) + */ + +#if defined(MBEDTLS_PK_PARSE_C) +/** + * \brief Parse a SubjectPublicKeyInfo DER structure + * + * \param p the position in the ASN.1 data + * \param end end of the buffer + * \param pk The PK context to fill. It must have been initialized + * but not set up. + * + * \return 0 if successful, or a specific PK error code + */ +int mbedtls_pk_parse_subpubkey(unsigned char **p, const unsigned char *end, + mbedtls_pk_context *pk); +#endif /* MBEDTLS_PK_PARSE_C */ + +#if defined(MBEDTLS_PK_WRITE_C) +/** + * \brief Write a subjectPublicKey to ASN.1 data + * Note: function works backwards in data buffer + * + * \param p reference to current position pointer + * \param start start of the buffer (for bounds-checking) + * \param key PK context which must contain a valid public or private key. + * + * \return the length written or a negative error code + */ +int mbedtls_pk_write_pubkey(unsigned char **p, unsigned char *start, + const mbedtls_pk_context *key); +#endif /* MBEDTLS_PK_WRITE_C */ + +/* + * Internal module functions. You probably do not want to use these unless you + * know you do. + */ +#if defined(MBEDTLS_FS_IO) +int mbedtls_pk_load_file(const char *path, unsigned char **buf, size_t *n); +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * \brief Turn an EC or RSA key into an opaque one. + * + * \warning This is a temporary utility function for tests. It might + * change or be removed at any time without notice. + * + * \param pk Input: the EC or RSA key to import to a PSA key. + * Output: a PK context wrapping that PSA key. + * \param key Output: a PSA key identifier. + * It's the caller's responsibility to call + * psa_destroy_key() on that key identifier after calling + * mbedtls_pk_free() on the PK context. + * \param alg The algorithm to allow for use with that key. + * \param usage The usage to allow for use with that key. + * \param alg2 The secondary algorithm to allow for use with that key. + * + * \return \c 0 if successful. + * \return An Mbed TLS error code otherwise. + */ +int mbedtls_pk_wrap_as_opaque(mbedtls_pk_context *pk, + mbedtls_svc_key_id_t *key, + psa_algorithm_t alg, + psa_key_usage_t usage, + psa_algorithm_t alg2); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_PK_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs12.h b/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs12.h new file mode 100644 index 00000000..eb9e2d9d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs12.h @@ -0,0 +1,117 @@ +/** + * \file pkcs12.h + * + * \brief PKCS#12 Personal Information Exchange Syntax + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PKCS12_H +#define MBEDTLS_PKCS12_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/md.h" +#include "mbedtls/cipher.h" +#include "mbedtls/asn1.h" + +#include + +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA -0x1F80 +/** Feature not available, e.g. unsupported encryption scheme. */ +#define MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE -0x1F00 +/** PBE ASN.1 data not as expected. */ +#define MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT -0x1E80 +/** Given private key password does not allow for correct decryption. */ +#define MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH -0x1E00 + +#define MBEDTLS_PKCS12_DERIVE_KEY 1 /**< encryption/decryption key */ +#define MBEDTLS_PKCS12_DERIVE_IV 2 /**< initialization vector */ +#define MBEDTLS_PKCS12_DERIVE_MAC_KEY 3 /**< integrity / MAC key */ + +#define MBEDTLS_PKCS12_PBE_DECRYPT 0 +#define MBEDTLS_PKCS12_PBE_ENCRYPT 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MBEDTLS_ASN1_PARSE_C) + +/** + * \brief PKCS12 Password Based function (encryption / decryption) + * for cipher-based and mbedtls_md-based PBE's + * + * \param pbe_params an ASN1 buffer containing the pkcs-12 PbeParams structure + * \param mode either #MBEDTLS_PKCS12_PBE_ENCRYPT or + * #MBEDTLS_PKCS12_PBE_DECRYPT + * \param cipher_type the cipher used + * \param md_type the mbedtls_md used + * \param pwd Latin1-encoded password used. This may only be \c NULL when + * \p pwdlen is 0. No null terminator should be used. + * \param pwdlen length of the password (may be 0) + * \param input the input data + * \param len data length + * \param output the output buffer + * + * \return 0 if successful, or a MBEDTLS_ERR_XXX code + */ +int mbedtls_pkcs12_pbe(mbedtls_asn1_buf *pbe_params, int mode, + mbedtls_cipher_type_t cipher_type, mbedtls_md_type_t md_type, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *input, size_t len, + unsigned char *output); + +#endif /* MBEDTLS_ASN1_PARSE_C */ + +/** + * \brief The PKCS#12 derivation function uses a password and a salt + * to produce pseudo-random bits for a particular "purpose". + * + * Depending on the given id, this function can produce an + * encryption/decryption key, an initialization vector or an + * integrity key. + * + * \param data buffer to store the derived data in + * \param datalen length of buffer to fill + * \param pwd The password to use. For compliance with PKCS#12 §B.1, this + * should be a BMPString, i.e. a Unicode string where each + * character is encoded as 2 bytes in big-endian order, with + * no byte order mark and with a null terminator (i.e. the + * last two bytes should be 0x00 0x00). + * \param pwdlen length of the password (may be 0). + * \param salt Salt buffer to use. This may only be \c NULL when + * \p saltlen is 0. + * \param saltlen length of the salt (may be zero) + * \param mbedtls_md mbedtls_md type to use during the derivation + * \param id id that describes the purpose (can be + * #MBEDTLS_PKCS12_DERIVE_KEY, #MBEDTLS_PKCS12_DERIVE_IV or + * #MBEDTLS_PKCS12_DERIVE_MAC_KEY) + * \param iterations number of iterations + * + * \return 0 if successful, or a MD, BIGNUM type error. + */ +int mbedtls_pkcs12_derivation(unsigned char *data, size_t datalen, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *salt, size_t saltlen, + mbedtls_md_type_t mbedtls_md, int id, int iterations); + +#ifdef __cplusplus +} +#endif + +#endif /* pkcs12.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs5.h b/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs5.h new file mode 100644 index 00000000..152b45fb --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs5.h @@ -0,0 +1,136 @@ +/** + * \file pkcs5.h + * + * \brief PKCS#5 functions + * + * \author Mathias Olsson + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PKCS5_H +#define MBEDTLS_PKCS5_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/asn1.h" +#include "mbedtls/md.h" + +#include +#include + +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA -0x2f80 +/** Unexpected ASN.1 data. */ +#define MBEDTLS_ERR_PKCS5_INVALID_FORMAT -0x2f00 +/** Requested encryption or digest alg not available. */ +#define MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE -0x2e80 +/** Given private key password does not allow for correct decryption. */ +#define MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH -0x2e00 + +#define MBEDTLS_PKCS5_DECRYPT 0 +#define MBEDTLS_PKCS5_ENCRYPT 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(MBEDTLS_ASN1_PARSE_C) + +/** + * \brief PKCS#5 PBES2 function + * + * \param pbe_params the ASN.1 algorithm parameters + * \param mode either MBEDTLS_PKCS5_DECRYPT or MBEDTLS_PKCS5_ENCRYPT + * \param pwd password to use when generating key + * \param pwdlen length of password + * \param data data to process + * \param datalen length of data + * \param output output buffer + * + * \returns 0 on success, or a MBEDTLS_ERR_XXX code if verification fails. + */ +int mbedtls_pkcs5_pbes2(const mbedtls_asn1_buf *pbe_params, int mode, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *data, size_t datalen, + unsigned char *output); + +#endif /* MBEDTLS_ASN1_PARSE_C */ + +/** + * \brief PKCS#5 PBKDF2 using HMAC without using the HMAC context + * + * \param md_type Hash algorithm used + * \param password Password to use when generating key + * \param plen Length of password + * \param salt Salt to use when generating key + * \param slen Length of salt + * \param iteration_count Iteration count + * \param key_length Length of generated key in bytes + * \param output Generated key. Must be at least as big as key_length + * + * \returns 0 on success, or a MBEDTLS_ERR_XXX code if verification fails. + */ +int mbedtls_pkcs5_pbkdf2_hmac_ext(mbedtls_md_type_t md_type, + const unsigned char *password, + size_t plen, const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output); + +#if defined(MBEDTLS_MD_C) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief PKCS#5 PBKDF2 using HMAC + * + * \deprecated Superseded by mbedtls_pkcs5_pbkdf2_hmac_ext(). + * + * \param ctx Generic HMAC context + * \param password Password to use when generating key + * \param plen Length of password + * \param salt Salt to use when generating key + * \param slen Length of salt + * \param iteration_count Iteration count + * \param key_length Length of generated key in bytes + * \param output Generated key. Must be at least as big as key_length + * + * \returns 0 on success, or a MBEDTLS_ERR_XXX code if verification fails. + */ +int MBEDTLS_DEPRECATED mbedtls_pkcs5_pbkdf2_hmac(mbedtls_md_context_t *ctx, + const unsigned char *password, + size_t plen, + const unsigned char *salt, + size_t slen, + unsigned int iteration_count, + uint32_t key_length, + unsigned char *output); +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_MD_C */ +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_pkcs5_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* pkcs5.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs7.h b/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs7.h new file mode 100644 index 00000000..1231e340 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/pkcs7.h @@ -0,0 +1,253 @@ +/** + * \file pkcs7.h + * + * \brief PKCS #7 generic defines and structures + * https://tools.ietf.org/html/rfc2315 + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Note: For the time being, this implementation of the PKCS #7 cryptographic + * message syntax is a partial implementation of RFC 2315. + * Differences include: + * - The RFC specifies 6 different content types. The only type currently + * supported in Mbed TLS is the signed-data content type. + * - The only supported PKCS #7 Signed Data syntax version is version 1 + * - The RFC specifies support for BER. This implementation is limited to + * DER only. + * - The RFC specifies that multiple digest algorithms can be specified + * in the Signed Data type. Only one digest algorithm is supported in Mbed TLS. + * - The RFC specifies the Signed Data type can contain multiple X.509 or PKCS #6 extended + * certificates. In Mbed TLS, this list can only contain 0 or 1 certificates + * and they must be in X.509 format. + * - The RFC specifies the Signed Data type can contain + * certificate-revocation lists (CRLs). This implementation has no support + * for CRLs so it is assumed to be an empty list. + * - The RFC allows for SignerInfo structure to optionally contain + * unauthenticatedAttributes and authenticatedAttributes. In Mbed TLS it is + * assumed these fields are empty. + * - The RFC allows for the signed Data type to contain contentInfo. This + * implementation assumes the type is DATA and the content is empty. + */ + +#ifndef MBEDTLS_PKCS7_H +#define MBEDTLS_PKCS7_H + +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/asn1.h" +#include "mbedtls/x509.h" +#include "mbedtls/x509_crt.h" + +/** + * \name PKCS #7 Module Error codes + * \{ + */ +#define MBEDTLS_ERR_PKCS7_INVALID_FORMAT -0x5300 /**< The format is invalid, e.g. different type expected. */ +#define MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE -0x5380 /**< Unavailable feature, e.g. anything other than signed data. */ +#define MBEDTLS_ERR_PKCS7_INVALID_VERSION -0x5400 /**< The PKCS #7 version element is invalid or cannot be parsed. */ +#define MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO -0x5480 /**< The PKCS #7 content info is invalid or cannot be parsed. */ +#define MBEDTLS_ERR_PKCS7_INVALID_ALG -0x5500 /**< The algorithm tag or value is invalid or cannot be parsed. */ +#define MBEDTLS_ERR_PKCS7_INVALID_CERT -0x5580 /**< The certificate tag or value is invalid or cannot be parsed. */ +#define MBEDTLS_ERR_PKCS7_INVALID_SIGNATURE -0x5600 /**< Error parsing the signature */ +#define MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO -0x5680 /**< Error parsing the signer's info */ +#define MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA -0x5700 /**< Input invalid. */ +#define MBEDTLS_ERR_PKCS7_ALLOC_FAILED -0x5780 /**< Allocation of memory failed. */ +#define MBEDTLS_ERR_PKCS7_VERIFY_FAIL -0x5800 /**< Verification Failed */ +#define MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID -0x5880 /**< The PKCS #7 date issued/expired dates are invalid */ +/* \} name */ + +/** + * \name PKCS #7 Supported Version + * \{ + */ +#define MBEDTLS_PKCS7_SUPPORTED_VERSION 0x01 +/* \} name */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Type-length-value structure that allows for ASN.1 using DER. + */ +typedef mbedtls_asn1_buf mbedtls_pkcs7_buf; + +/** + * Container for ASN.1 named information objects. + * It allows for Relative Distinguished Names (e.g. cn=localhost,ou=code,etc.). + */ +typedef mbedtls_asn1_named_data mbedtls_pkcs7_name; + +/** + * Container for a sequence of ASN.1 items + */ +typedef mbedtls_asn1_sequence mbedtls_pkcs7_sequence; + +/** + * PKCS #7 types + */ +typedef enum { + MBEDTLS_PKCS7_NONE=0, + MBEDTLS_PKCS7_DATA, + MBEDTLS_PKCS7_SIGNED_DATA, + MBEDTLS_PKCS7_ENVELOPED_DATA, + MBEDTLS_PKCS7_SIGNED_AND_ENVELOPED_DATA, + MBEDTLS_PKCS7_DIGESTED_DATA, + MBEDTLS_PKCS7_ENCRYPTED_DATA, +} +mbedtls_pkcs7_type; + +/** + * Structure holding PKCS #7 signer info + */ +typedef struct mbedtls_pkcs7_signer_info { + int MBEDTLS_PRIVATE(version); + mbedtls_x509_buf MBEDTLS_PRIVATE(serial); + mbedtls_x509_name MBEDTLS_PRIVATE(issuer); + mbedtls_x509_buf MBEDTLS_PRIVATE(issuer_raw); + mbedtls_x509_buf MBEDTLS_PRIVATE(alg_identifier); + mbedtls_x509_buf MBEDTLS_PRIVATE(sig_alg_identifier); + mbedtls_x509_buf MBEDTLS_PRIVATE(sig); + struct mbedtls_pkcs7_signer_info *MBEDTLS_PRIVATE(next); +} +mbedtls_pkcs7_signer_info; + +/** + * Structure holding the signed data section + */ +typedef struct mbedtls_pkcs7_signed_data { + int MBEDTLS_PRIVATE(version); + mbedtls_pkcs7_buf MBEDTLS_PRIVATE(digest_alg_identifiers); + int MBEDTLS_PRIVATE(no_of_certs); + mbedtls_x509_crt MBEDTLS_PRIVATE(certs); + int MBEDTLS_PRIVATE(no_of_crls); + mbedtls_x509_crl MBEDTLS_PRIVATE(crl); + int MBEDTLS_PRIVATE(no_of_signers); + mbedtls_pkcs7_signer_info MBEDTLS_PRIVATE(signers); +} +mbedtls_pkcs7_signed_data; + +/** + * Structure holding PKCS #7 structure, only signed data for now + */ +typedef struct mbedtls_pkcs7 { + mbedtls_pkcs7_buf MBEDTLS_PRIVATE(raw); + mbedtls_pkcs7_signed_data MBEDTLS_PRIVATE(signed_data); +} +mbedtls_pkcs7; + +/** + * \brief Initialize mbedtls_pkcs7 structure. + * + * \param pkcs7 mbedtls_pkcs7 structure. + */ +void mbedtls_pkcs7_init(mbedtls_pkcs7 *pkcs7); + +/** + * \brief Parse a single DER formatted PKCS #7 detached signature. + * + * \param pkcs7 The mbedtls_pkcs7 structure to be filled by the parser. + * \param buf The buffer holding only the DER encoded PKCS #7 content. + * \param buflen The size in bytes of \p buf. The size must be exactly the + * length of the DER encoded PKCS #7 content. + * + * \note This function makes an internal copy of the PKCS #7 buffer + * \p buf. In particular, \p buf may be destroyed or reused + * after this call returns. + * \note Signatures with internal data are not supported. + * + * \return The \c mbedtls_pkcs7_type of \p buf, if successful. + * \return A negative error code on failure. + */ +int mbedtls_pkcs7_parse_der(mbedtls_pkcs7 *pkcs7, const unsigned char *buf, + const size_t buflen); + +/** + * \brief Verification of PKCS #7 signature against a caller-supplied + * certificate. + * + * For each signer in the PKCS structure, this function computes + * a signature over the supplied data, using the supplied + * certificate and the same digest algorithm as specified by the + * signer. It then compares this signature against the + * signer's signature; verification succeeds if any comparison + * matches. + * + * This function does not use the certificates held within the + * PKCS #7 structure itself, and does not check that the + * certificate is signed by a trusted certification authority. + * + * \param pkcs7 mbedtls_pkcs7 structure containing signature. + * \param cert Certificate containing key to verify signature. + * \param data Plain data on which signature has to be verified. + * \param datalen Length of the data. + * + * \note This function internally calculates the hash on the supplied + * plain data for signature verification. + * + * \return 0 if the signature verifies, or a negative error code on failure. + */ +int mbedtls_pkcs7_signed_data_verify(mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *cert, + const unsigned char *data, + size_t datalen); + +/** + * \brief Verification of PKCS #7 signature against a caller-supplied + * certificate. + * + * For each signer in the PKCS structure, this function + * validates a signature over the supplied hash, using the + * supplied certificate and the same digest algorithm as + * specified by the signer. Verification succeeds if any + * signature is good. + * + * This function does not use the certificates held within the + * PKCS #7 structure itself, and does not check that the + * certificate is signed by a trusted certification authority. + * + * \param pkcs7 PKCS #7 structure containing signature. + * \param cert Certificate containing key to verify signature. + * \param hash Hash of the plain data on which signature has to be verified. + * \param hashlen Length of the hash. + * + * \note This function is different from mbedtls_pkcs7_signed_data_verify() + * in that it is directly passed the hash of the data. + * + * \return 0 if the signature verifies, or a negative error code on failure. + */ +int mbedtls_pkcs7_signed_hash_verify(mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *cert, + const unsigned char *hash, size_t hashlen); + +/** + * \brief Unallocate all PKCS #7 data and zeroize the memory. + * It doesn't free \p pkcs7 itself. This should be done by the caller. + * + * \param pkcs7 mbedtls_pkcs7 structure to free. + */ +void mbedtls_pkcs7_free(mbedtls_pkcs7 *pkcs7); + +#ifdef __cplusplus +} +#endif + +#endif /* pkcs7.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/platform.h b/r5dev/thirdparty/mbedtls/include/mbedtls/platform.h new file mode 100644 index 00000000..f6515871 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/platform.h @@ -0,0 +1,472 @@ +/** + * \file platform.h + * + * \brief This file contains the definitions and functions of the + * Mbed TLS platform abstraction layer. + * + * The platform abstraction layer removes the need for the library + * to directly link to standard C library functions or operating + * system services, making the library easier to port and embed. + * Application developers and users of the library can provide their own + * implementations of these functions, or implementations specific to + * their platform, which can be statically linked to the library or + * dynamically configured at runtime. + * + * When all compilation options related to platform abstraction are + * disabled, this header just defines `mbedtls_xxx` function names + * as aliases to the standard `xxx` function. + * + * Most modules in the library and example programs are expected to + * include this header. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PLATFORM_H +#define MBEDTLS_PLATFORM_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ + +/* The older Microsoft Windows common runtime provides non-conforming + * implementations of some standard library functions, including snprintf + * and vsnprintf. This affects MSVC and MinGW builds. + */ +#if defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER <= 1900) +#define MBEDTLS_PLATFORM_HAS_NON_CONFORMING_SNPRINTF +#define MBEDTLS_PLATFORM_HAS_NON_CONFORMING_VSNPRINTF +#endif + +#if !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) +#include +#include +#if defined(MBEDTLS_HAVE_TIME) +#include +#endif +#if !defined(MBEDTLS_PLATFORM_STD_SNPRINTF) +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_SNPRINTF) +#define MBEDTLS_PLATFORM_STD_SNPRINTF mbedtls_platform_win32_snprintf /**< The default \c snprintf function to use. */ +#else +#define MBEDTLS_PLATFORM_STD_SNPRINTF snprintf /**< The default \c snprintf function to use. */ +#endif +#endif +#if !defined(MBEDTLS_PLATFORM_STD_VSNPRINTF) +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_VSNPRINTF) +#define MBEDTLS_PLATFORM_STD_VSNPRINTF mbedtls_platform_win32_vsnprintf /**< The default \c vsnprintf function to use. */ +#else +#define MBEDTLS_PLATFORM_STD_VSNPRINTF vsnprintf /**< The default \c vsnprintf function to use. */ +#endif +#endif +#if !defined(MBEDTLS_PLATFORM_STD_PRINTF) +#define MBEDTLS_PLATFORM_STD_PRINTF printf /**< The default \c printf function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_FPRINTF) +#define MBEDTLS_PLATFORM_STD_FPRINTF fprintf /**< The default \c fprintf function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_CALLOC) +#define MBEDTLS_PLATFORM_STD_CALLOC calloc /**< The default \c calloc function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_FREE) +#define MBEDTLS_PLATFORM_STD_FREE free /**< The default \c free function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_SETBUF) +#define MBEDTLS_PLATFORM_STD_SETBUF setbuf /**< The default \c setbuf function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_EXIT) +#define MBEDTLS_PLATFORM_STD_EXIT exit /**< The default \c exit function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_TIME) +#define MBEDTLS_PLATFORM_STD_TIME time /**< The default \c time function to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_EXIT_SUCCESS) +#define MBEDTLS_PLATFORM_STD_EXIT_SUCCESS EXIT_SUCCESS /**< The default exit value to use. */ +#endif +#if !defined(MBEDTLS_PLATFORM_STD_EXIT_FAILURE) +#define MBEDTLS_PLATFORM_STD_EXIT_FAILURE EXIT_FAILURE /**< The default exit value to use. */ +#endif +#if defined(MBEDTLS_FS_IO) +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_READ) +#define MBEDTLS_PLATFORM_STD_NV_SEED_READ mbedtls_platform_std_nv_seed_read +#endif +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_WRITE) +#define MBEDTLS_PLATFORM_STD_NV_SEED_WRITE mbedtls_platform_std_nv_seed_write +#endif +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_FILE) +#define MBEDTLS_PLATFORM_STD_NV_SEED_FILE "seedfile" +#endif +#endif /* MBEDTLS_FS_IO */ +#else /* MBEDTLS_PLATFORM_NO_STD_FUNCTIONS */ +#if defined(MBEDTLS_PLATFORM_STD_MEM_HDR) +#include MBEDTLS_PLATFORM_STD_MEM_HDR +#endif +#endif /* MBEDTLS_PLATFORM_NO_STD_FUNCTIONS */ + + +/** \} name SECTION: Module settings */ + +/* + * The function pointers for calloc and free. + */ +#if defined(MBEDTLS_PLATFORM_MEMORY) +#if defined(MBEDTLS_PLATFORM_FREE_MACRO) && \ + defined(MBEDTLS_PLATFORM_CALLOC_MACRO) +#define mbedtls_free MBEDTLS_PLATFORM_FREE_MACRO +#define mbedtls_calloc MBEDTLS_PLATFORM_CALLOC_MACRO +#else +/* For size_t */ +#include +extern void *mbedtls_calloc(size_t n, size_t size); +extern void mbedtls_free(void *ptr); + +/** + * \brief This function dynamically sets the memory-management + * functions used by the library, during runtime. + * + * \param calloc_func The \c calloc function implementation. + * \param free_func The \c free function implementation. + * + * \return \c 0. + */ +int mbedtls_platform_set_calloc_free(void *(*calloc_func)(size_t, size_t), + void (*free_func)(void *)); +#endif /* MBEDTLS_PLATFORM_FREE_MACRO && MBEDTLS_PLATFORM_CALLOC_MACRO */ +#else /* !MBEDTLS_PLATFORM_MEMORY */ +#define mbedtls_free free +#define mbedtls_calloc calloc +#endif /* MBEDTLS_PLATFORM_MEMORY && !MBEDTLS_PLATFORM_{FREE,CALLOC}_MACRO */ + +/* + * The function pointers for fprintf + */ +#if defined(MBEDTLS_PLATFORM_FPRINTF_ALT) +/* We need FILE * */ +#include +extern int (*mbedtls_fprintf)(FILE *stream, const char *format, ...); + +/** + * \brief This function dynamically configures the fprintf + * function that is called when the + * mbedtls_fprintf() function is invoked by the library. + * + * \param fprintf_func The \c fprintf function implementation. + * + * \return \c 0. + */ +int mbedtls_platform_set_fprintf(int (*fprintf_func)(FILE *stream, const char *, + ...)); +#else +#if defined(MBEDTLS_PLATFORM_FPRINTF_MACRO) +#define mbedtls_fprintf MBEDTLS_PLATFORM_FPRINTF_MACRO +#else +#define mbedtls_fprintf fprintf +#endif /* MBEDTLS_PLATFORM_FPRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_FPRINTF_ALT */ + +/* + * The function pointers for printf + */ +#if defined(MBEDTLS_PLATFORM_PRINTF_ALT) +extern int (*mbedtls_printf)(const char *format, ...); + +/** + * \brief This function dynamically configures the snprintf + * function that is called when the mbedtls_snprintf() + * function is invoked by the library. + * + * \param printf_func The \c printf function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_printf(int (*printf_func)(const char *, ...)); +#else /* !MBEDTLS_PLATFORM_PRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_PRINTF_MACRO) +#define mbedtls_printf MBEDTLS_PLATFORM_PRINTF_MACRO +#else +#define mbedtls_printf printf +#endif /* MBEDTLS_PLATFORM_PRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_PRINTF_ALT */ + +/* + * The function pointers for snprintf + * + * The snprintf implementation should conform to C99: + * - it *must* always correctly zero-terminate the buffer + * (except when n == 0, then it must leave the buffer untouched) + * - however it is acceptable to return -1 instead of the required length when + * the destination buffer is too short. + */ +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_SNPRINTF) +/* For Windows (inc. MSYS2), we provide our own fixed implementation */ +int mbedtls_platform_win32_snprintf(char *s, size_t n, const char *fmt, ...); +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) +extern int (*mbedtls_snprintf)(char *s, size_t n, const char *format, ...); + +/** + * \brief This function allows configuring a custom + * \c snprintf function pointer. + * + * \param snprintf_func The \c snprintf function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_snprintf(int (*snprintf_func)(char *s, size_t n, + const char *format, ...)); +#else /* MBEDTLS_PLATFORM_SNPRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_SNPRINTF_MACRO) +#define mbedtls_snprintf MBEDTLS_PLATFORM_SNPRINTF_MACRO +#else +#define mbedtls_snprintf MBEDTLS_PLATFORM_STD_SNPRINTF +#endif /* MBEDTLS_PLATFORM_SNPRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_SNPRINTF_ALT */ + +/* + * The function pointers for vsnprintf + * + * The vsnprintf implementation should conform to C99: + * - it *must* always correctly zero-terminate the buffer + * (except when n == 0, then it must leave the buffer untouched) + * - however it is acceptable to return -1 instead of the required length when + * the destination buffer is too short. + */ +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_VSNPRINTF) +#include +/* For Older Windows (inc. MSYS2), we provide our own fixed implementation */ +int mbedtls_platform_win32_vsnprintf(char *s, size_t n, const char *fmt, va_list arg); +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) +#include +extern int (*mbedtls_vsnprintf)(char *s, size_t n, const char *format, va_list arg); + +/** + * \brief Set your own snprintf function pointer + * + * \param vsnprintf_func The \c vsnprintf function implementation + * + * \return \c 0 + */ +int mbedtls_platform_set_vsnprintf(int (*vsnprintf_func)(char *s, size_t n, + const char *format, va_list arg)); +#else /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_MACRO) +#define mbedtls_vsnprintf MBEDTLS_PLATFORM_VSNPRINTF_MACRO +#else +#define mbedtls_vsnprintf vsnprintf +#endif /* MBEDTLS_PLATFORM_VSNPRINTF_MACRO */ +#endif /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */ + +/* + * The function pointers for setbuf + */ +#if defined(MBEDTLS_PLATFORM_SETBUF_ALT) +#include +/** + * \brief Function pointer to call for `setbuf()` functionality + * (changing the internal buffering on stdio calls). + * + * \note The library calls this function to disable + * buffering when reading or writing sensitive data, + * to avoid having extra copies of sensitive data + * remaining in stdio buffers after the file is + * closed. If this is not a concern, for example if + * your platform's stdio doesn't have any buffering, + * you can set mbedtls_setbuf to a function that + * does nothing. + * + * The library always calls this function with + * `buf` equal to `NULL`. + */ +extern void (*mbedtls_setbuf)(FILE *stream, char *buf); + +/** + * \brief Dynamically configure the function that is called + * when the mbedtls_setbuf() function is called by the + * library. + * + * \param setbuf_func The \c setbuf function implementation + * + * \return \c 0 + */ +int mbedtls_platform_set_setbuf(void (*setbuf_func)( + FILE *stream, char *buf)); +#elif defined(MBEDTLS_PLATFORM_SETBUF_MACRO) +/** + * \brief Macro defining the function for the library to + * call for `setbuf` functionality (changing the + * internal buffering on stdio calls). + * + * \note See extra comments on the mbedtls_setbuf() function + * pointer above. + * + * \return \c 0 on success, negative on error. + */ +#define mbedtls_setbuf MBEDTLS_PLATFORM_SETBUF_MACRO +#else +#define mbedtls_setbuf setbuf +#endif /* MBEDTLS_PLATFORM_SETBUF_ALT / MBEDTLS_PLATFORM_SETBUF_MACRO */ + +/* + * The function pointers for exit + */ +#if defined(MBEDTLS_PLATFORM_EXIT_ALT) +extern void (*mbedtls_exit)(int status); + +/** + * \brief This function dynamically configures the exit + * function that is called when the mbedtls_exit() + * function is invoked by the library. + * + * \param exit_func The \c exit function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_exit(void (*exit_func)(int status)); +#else +#if defined(MBEDTLS_PLATFORM_EXIT_MACRO) +#define mbedtls_exit MBEDTLS_PLATFORM_EXIT_MACRO +#else +#define mbedtls_exit exit +#endif /* MBEDTLS_PLATFORM_EXIT_MACRO */ +#endif /* MBEDTLS_PLATFORM_EXIT_ALT */ + +/* + * The default exit values + */ +#if defined(MBEDTLS_PLATFORM_STD_EXIT_SUCCESS) +#define MBEDTLS_EXIT_SUCCESS MBEDTLS_PLATFORM_STD_EXIT_SUCCESS +#else +#define MBEDTLS_EXIT_SUCCESS 0 +#endif +#if defined(MBEDTLS_PLATFORM_STD_EXIT_FAILURE) +#define MBEDTLS_EXIT_FAILURE MBEDTLS_PLATFORM_STD_EXIT_FAILURE +#else +#define MBEDTLS_EXIT_FAILURE 1 +#endif + +/* + * The function pointers for reading from and writing a seed file to + * Non-Volatile storage (NV) in a platform-independent way + * + * Only enabled when the NV seed entropy source is enabled + */ +#if defined(MBEDTLS_ENTROPY_NV_SEED) +#if !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) && defined(MBEDTLS_FS_IO) +/* Internal standard platform definitions */ +int mbedtls_platform_std_nv_seed_read(unsigned char *buf, size_t buf_len); +int mbedtls_platform_std_nv_seed_write(unsigned char *buf, size_t buf_len); +#endif + +#if defined(MBEDTLS_PLATFORM_NV_SEED_ALT) +extern int (*mbedtls_nv_seed_read)(unsigned char *buf, size_t buf_len); +extern int (*mbedtls_nv_seed_write)(unsigned char *buf, size_t buf_len); + +/** + * \brief This function allows configuring custom seed file writing and + * reading functions. + * + * \param nv_seed_read_func The seed reading function implementation. + * \param nv_seed_write_func The seed writing function implementation. + * + * \return \c 0 on success. + */ +int mbedtls_platform_set_nv_seed( + int (*nv_seed_read_func)(unsigned char *buf, size_t buf_len), + int (*nv_seed_write_func)(unsigned char *buf, size_t buf_len) + ); +#else +#if defined(MBEDTLS_PLATFORM_NV_SEED_READ_MACRO) && \ + defined(MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO) +#define mbedtls_nv_seed_read MBEDTLS_PLATFORM_NV_SEED_READ_MACRO +#define mbedtls_nv_seed_write MBEDTLS_PLATFORM_NV_SEED_WRITE_MACRO +#else +#define mbedtls_nv_seed_read mbedtls_platform_std_nv_seed_read +#define mbedtls_nv_seed_write mbedtls_platform_std_nv_seed_write +#endif +#endif /* MBEDTLS_PLATFORM_NV_SEED_ALT */ +#endif /* MBEDTLS_ENTROPY_NV_SEED */ + +#if !defined(MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT) + +/** + * \brief The platform context structure. + * + * \note This structure may be used to assist platform-specific + * setup or teardown operations. + */ +typedef struct mbedtls_platform_context { + char MBEDTLS_PRIVATE(dummy); /**< A placeholder member, as empty structs are not portable. */ +} +mbedtls_platform_context; + +#else +#include "platform_alt.h" +#endif /* !MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT */ + +/** + * \brief This function performs any platform-specific initialization + * operations. + * + * \note This function should be called before any other library functions. + * + * Its implementation is platform-specific, and unless + * platform-specific code is provided, it does nothing. + * + * \note The usage and necessity of this function is dependent on the platform. + * + * \param ctx The platform context. + * + * \return \c 0 on success. + */ +int mbedtls_platform_setup(mbedtls_platform_context *ctx); +/** + * \brief This function performs any platform teardown operations. + * + * \note This function should be called after every other Mbed TLS module + * has been correctly freed using the appropriate free function. + * + * Its implementation is platform-specific, and unless + * platform-specific code is provided, it does nothing. + * + * \note The usage and necessity of this function is dependent on the platform. + * + * \param ctx The platform context. + * + */ +void mbedtls_platform_teardown(mbedtls_platform_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* platform.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/platform_time.h b/r5dev/thirdparty/mbedtls/include/mbedtls/platform_time.h new file mode 100644 index 00000000..eae6f5f8 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/platform_time.h @@ -0,0 +1,68 @@ +/** + * \file platform_time.h + * + * \brief mbed TLS Platform time abstraction + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PLATFORM_TIME_H +#define MBEDTLS_PLATFORM_TIME_H + +#include "mbedtls/build_info.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The time_t datatype + */ +#if defined(MBEDTLS_PLATFORM_TIME_TYPE_MACRO) +typedef MBEDTLS_PLATFORM_TIME_TYPE_MACRO mbedtls_time_t; +#else +/* For time_t */ +#include +typedef time_t mbedtls_time_t; +#endif /* MBEDTLS_PLATFORM_TIME_TYPE_MACRO */ + +/* + * The function pointers for time + */ +#if defined(MBEDTLS_PLATFORM_TIME_ALT) +extern mbedtls_time_t (*mbedtls_time)(mbedtls_time_t *time); + +/** + * \brief Set your own time function pointer + * + * \param time_func the time function implementation + * + * \return 0 + */ +int mbedtls_platform_set_time(mbedtls_time_t (*time_func)(mbedtls_time_t *time)); +#else +#if defined(MBEDTLS_PLATFORM_TIME_MACRO) +#define mbedtls_time MBEDTLS_PLATFORM_TIME_MACRO +#else +#define mbedtls_time time +#endif /* MBEDTLS_PLATFORM_TIME_MACRO */ +#endif /* MBEDTLS_PLATFORM_TIME_ALT */ + +#ifdef __cplusplus +} +#endif + +#endif /* platform_time.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/platform_util.h b/r5dev/thirdparty/mbedtls/include/mbedtls/platform_util.h new file mode 100644 index 00000000..edbde944 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/platform_util.h @@ -0,0 +1,207 @@ +/** + * \file platform_util.h + * + * \brief Common and shared functions used by multiple modules in the Mbed TLS + * library. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PLATFORM_UTIL_H +#define MBEDTLS_PLATFORM_UTIL_H + +#include "mbedtls/build_info.h" + +#include +#if defined(MBEDTLS_HAVE_TIME_DATE) +#include "mbedtls/platform_time.h" +#include +#endif /* MBEDTLS_HAVE_TIME_DATE */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Internal macros meant to be called only from within the library. */ +#define MBEDTLS_INTERNAL_VALIDATE_RET(cond, ret) do { } while (0) +#define MBEDTLS_INTERNAL_VALIDATE(cond) do { } while (0) + +/* Internal helper macros for deprecating API constants. */ +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#if defined(MBEDTLS_DEPRECATED_WARNING) +#define MBEDTLS_DEPRECATED __attribute__((deprecated)) +MBEDTLS_DEPRECATED typedef char const *mbedtls_deprecated_string_constant_t; +#define MBEDTLS_DEPRECATED_STRING_CONSTANT(VAL) \ + ((mbedtls_deprecated_string_constant_t) (VAL)) +MBEDTLS_DEPRECATED typedef int mbedtls_deprecated_numeric_constant_t; +#define MBEDTLS_DEPRECATED_NUMERIC_CONSTANT(VAL) \ + ((mbedtls_deprecated_numeric_constant_t) (VAL)) +#else /* MBEDTLS_DEPRECATED_WARNING */ +#define MBEDTLS_DEPRECATED +#define MBEDTLS_DEPRECATED_STRING_CONSTANT(VAL) VAL +#define MBEDTLS_DEPRECATED_NUMERIC_CONSTANT(VAL) VAL +#endif /* MBEDTLS_DEPRECATED_WARNING */ +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +/* Implementation of the check-return facility. + * See the user documentation in mbedtls_config.h. + * + * Do not use this macro directly to annotate function: instead, + * use one of MBEDTLS_CHECK_RETURN_CRITICAL or MBEDTLS_CHECK_RETURN_TYPICAL + * depending on how important it is to check the return value. + */ +#if !defined(MBEDTLS_CHECK_RETURN) +#if defined(__GNUC__) +#define MBEDTLS_CHECK_RETURN __attribute__((__warn_unused_result__)) +#elif defined(_MSC_VER) && _MSC_VER >= 1700 +#include +#define MBEDTLS_CHECK_RETURN _Check_return_ +#else +#define MBEDTLS_CHECK_RETURN +#endif +#endif + +/** Critical-failure function + * + * This macro appearing at the beginning of the declaration of a function + * indicates that its return value should be checked in all applications. + * Omitting the check is very likely to indicate a bug in the application + * and will result in a compile-time warning if #MBEDTLS_CHECK_RETURN + * is implemented for the compiler in use. + * + * \note The use of this macro is a work in progress. + * This macro may be added to more functions in the future. + * Such an extension is not considered an API break, provided that + * there are near-unavoidable circumstances under which the function + * can fail. For example, signature/MAC/AEAD verification functions, + * and functions that require a random generator, are considered + * return-check-critical. + */ +#define MBEDTLS_CHECK_RETURN_CRITICAL MBEDTLS_CHECK_RETURN + +/** Ordinary-failure function + * + * This macro appearing at the beginning of the declaration of a function + * indicates that its return value should be generally be checked in portable + * applications. Omitting the check will result in a compile-time warning if + * #MBEDTLS_CHECK_RETURN is implemented for the compiler in use and + * #MBEDTLS_CHECK_RETURN_WARNING is enabled in the compile-time configuration. + * + * You can use #MBEDTLS_IGNORE_RETURN to explicitly ignore the return value + * of a function that is annotated with #MBEDTLS_CHECK_RETURN. + * + * \note The use of this macro is a work in progress. + * This macro will be added to more functions in the future. + * Eventually this should appear before most functions returning + * an error code (as \c int in the \c mbedtls_xxx API or + * as ::psa_status_t in the \c psa_xxx API). + */ +#if defined(MBEDTLS_CHECK_RETURN_WARNING) +#define MBEDTLS_CHECK_RETURN_TYPICAL MBEDTLS_CHECK_RETURN +#else +#define MBEDTLS_CHECK_RETURN_TYPICAL +#endif + +/** Benign-failure function + * + * This macro appearing at the beginning of the declaration of a function + * indicates that it is rarely useful to check its return value. + * + * This macro has an empty expansion. It exists for documentation purposes: + * a #MBEDTLS_CHECK_RETURN_OPTIONAL annotation indicates that the function + * has been analyzed for return-check usefulness, whereas the lack of + * an annotation indicates that the function has not been analyzed and its + * return-check usefulness is unknown. + */ +#define MBEDTLS_CHECK_RETURN_OPTIONAL + +/** \def MBEDTLS_IGNORE_RETURN + * + * Call this macro with one argument, a function call, to suppress a warning + * from #MBEDTLS_CHECK_RETURN due to that function call. + */ +#if !defined(MBEDTLS_IGNORE_RETURN) +/* GCC doesn't silence the warning with just (void)(result). + * (void)!(result) is known to work up at least up to GCC 10, as well + * as with Clang and MSVC. + * + * https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Non_002dbugs.html + * https://stackoverflow.com/questions/40576003/ignoring-warning-wunused-result + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425#c34 + */ +#define MBEDTLS_IGNORE_RETURN(result) ((void) !(result)) +#endif + +/** + * \brief Securely zeroize a buffer + * + * The function is meant to wipe the data contained in a buffer so + * that it can no longer be recovered even if the program memory + * is later compromised. Call this function on sensitive data + * stored on the stack before returning from a function, and on + * sensitive data stored on the heap before freeing the heap + * object. + * + * It is extremely difficult to guarantee that calls to + * mbedtls_platform_zeroize() are not removed by aggressive + * compiler optimizations in a portable way. For this reason, Mbed + * TLS provides the configuration option + * MBEDTLS_PLATFORM_ZEROIZE_ALT, which allows users to configure + * mbedtls_platform_zeroize() to use a suitable implementation for + * their platform and needs + * + * \param buf Buffer to be zeroized + * \param len Length of the buffer in bytes + * + */ +void mbedtls_platform_zeroize(void *buf, size_t len); + +#if defined(MBEDTLS_HAVE_TIME_DATE) +/** + * \brief Platform-specific implementation of gmtime_r() + * + * The function is a thread-safe abstraction that behaves + * similarly to the gmtime_r() function from Unix/POSIX. + * + * Mbed TLS will try to identify the underlying platform and + * make use of an appropriate underlying implementation (e.g. + * gmtime_r() for POSIX and gmtime_s() for Windows). If this is + * not possible, then gmtime() will be used. In this case, calls + * from the library to gmtime() will be guarded by the mutex + * mbedtls_threading_gmtime_mutex if MBEDTLS_THREADING_C is + * enabled. It is recommended that calls from outside the library + * are also guarded by this mutex. + * + * If MBEDTLS_PLATFORM_GMTIME_R_ALT is defined, then Mbed TLS will + * unconditionally use the alternative implementation for + * mbedtls_platform_gmtime_r() supplied by the user at compile time. + * + * \param tt Pointer to an object containing time (in seconds) since the + * epoch to be converted + * \param tm_buf Pointer to an object where the results will be stored + * + * \return Pointer to an object of type struct tm on success, otherwise + * NULL + */ +struct tm *mbedtls_platform_gmtime_r(const mbedtls_time_t *tt, + struct tm *tm_buf); +#endif /* MBEDTLS_HAVE_TIME_DATE */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_PLATFORM_UTIL_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/poly1305.h b/r5dev/thirdparty/mbedtls/include/mbedtls/poly1305.h new file mode 100644 index 00000000..3025ef1f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/poly1305.h @@ -0,0 +1,180 @@ +/** + * \file poly1305.h + * + * \brief This file contains Poly1305 definitions and functions. + * + * Poly1305 is a one-time message authenticator that can be used to + * authenticate messages. Poly1305-AES was created by Daniel + * Bernstein https://cr.yp.to/mac/poly1305-20050329.pdf The generic + * Poly1305 algorithm (not tied to AES) was also standardized in RFC + * 7539. + * + * \author Daniel King + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_POLY1305_H +#define MBEDTLS_POLY1305_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +/** Invalid input parameter(s). */ +#define MBEDTLS_ERR_POLY1305_BAD_INPUT_DATA -0x0057 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_POLY1305_ALT) + +typedef struct mbedtls_poly1305_context { + uint32_t MBEDTLS_PRIVATE(r)[4]; /** The value for 'r' (low 128 bits of the key). */ + uint32_t MBEDTLS_PRIVATE(s)[4]; /** The value for 's' (high 128 bits of the key). */ + uint32_t MBEDTLS_PRIVATE(acc)[5]; /** The accumulator number. */ + uint8_t MBEDTLS_PRIVATE(queue)[16]; /** The current partial block of data. */ + size_t MBEDTLS_PRIVATE(queue_len); /** The number of bytes stored in 'queue'. */ +} +mbedtls_poly1305_context; + +#else /* MBEDTLS_POLY1305_ALT */ +#include "poly1305_alt.h" +#endif /* MBEDTLS_POLY1305_ALT */ + +/** + * \brief This function initializes the specified Poly1305 context. + * + * It must be the first API called before using + * the context. + * + * It is usually followed by a call to + * \c mbedtls_poly1305_starts(), then one or more calls to + * \c mbedtls_poly1305_update(), then one call to + * \c mbedtls_poly1305_finish(), then finally + * \c mbedtls_poly1305_free(). + * + * \param ctx The Poly1305 context to initialize. This must + * not be \c NULL. + */ +void mbedtls_poly1305_init(mbedtls_poly1305_context *ctx); + +/** + * \brief This function releases and clears the specified + * Poly1305 context. + * + * \param ctx The Poly1305 context to clear. This may be \c NULL, in which + * case this function is a no-op. If it is not \c NULL, it must + * point to an initialized Poly1305 context. + */ +void mbedtls_poly1305_free(mbedtls_poly1305_context *ctx); + +/** + * \brief This function sets the one-time authentication key. + * + * \warning The key must be unique and unpredictable for each + * invocation of Poly1305. + * + * \param ctx The Poly1305 context to which the key should be bound. + * This must be initialized. + * \param key The buffer containing the \c 32 Byte (\c 256 Bit) key. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_poly1305_starts(mbedtls_poly1305_context *ctx, + const unsigned char key[32]); + +/** + * \brief This functions feeds an input buffer into an ongoing + * Poly1305 computation. + * + * It is called between \c mbedtls_cipher_poly1305_starts() and + * \c mbedtls_cipher_poly1305_finish(). + * It can be called repeatedly to process a stream of data. + * + * \param ctx The Poly1305 context to use for the Poly1305 operation. + * This must be initialized and bound to a key. + * \param ilen The length of the input data in Bytes. + * Any value is accepted. + * \param input The buffer holding the input data. + * This pointer can be \c NULL if `ilen == 0`. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_poly1305_update(mbedtls_poly1305_context *ctx, + const unsigned char *input, + size_t ilen); + +/** + * \brief This function generates the Poly1305 Message + * Authentication Code (MAC). + * + * \param ctx The Poly1305 context to use for the Poly1305 operation. + * This must be initialized and bound to a key. + * \param mac The buffer to where the MAC is written. This must + * be a writable buffer of length \c 16 Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_poly1305_finish(mbedtls_poly1305_context *ctx, + unsigned char mac[16]); + +/** + * \brief This function calculates the Poly1305 MAC of the input + * buffer with the provided key. + * + * \warning The key must be unique and unpredictable for each + * invocation of Poly1305. + * + * \param key The buffer containing the \c 32 Byte (\c 256 Bit) key. + * \param ilen The length of the input data in Bytes. + * Any value is accepted. + * \param input The buffer holding the input data. + * This pointer can be \c NULL if `ilen == 0`. + * \param mac The buffer to where the MAC is written. This must be + * a writable buffer of length \c 16 Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_poly1305_mac(const unsigned char key[32], + const unsigned char *input, + size_t ilen, + unsigned char mac[16]); + +#if defined(MBEDTLS_SELF_TEST) +/** + * \brief The Poly1305 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_poly1305_self_test(int verbose); +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_POLY1305_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/private_access.h b/r5dev/thirdparty/mbedtls/include/mbedtls/private_access.h new file mode 100644 index 00000000..61fa8777 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/private_access.h @@ -0,0 +1,32 @@ +/** + * \file private_access.h + * + * \brief Macro wrapper for struct's members. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_PRIVATE_ACCESS_H +#define MBEDTLS_PRIVATE_ACCESS_H + +#ifndef MBEDTLS_ALLOW_PRIVATE_ACCESS +#define MBEDTLS_PRIVATE(member) private_##member +#else +#define MBEDTLS_PRIVATE(member) member +#endif + +#endif /* MBEDTLS_PRIVATE_ACCESS_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/psa_util.h b/r5dev/thirdparty/mbedtls/include/mbedtls/psa_util.h new file mode 100644 index 00000000..b750716a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/psa_util.h @@ -0,0 +1,397 @@ +/** + * \file psa_util.h + * + * \brief Utility functions for the use of the PSA Crypto library. + * + * \warning This function is not part of the public API and may + * change at any time. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_PSA_UTIL_H +#define MBEDTLS_PSA_UTIL_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include "psa/crypto.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/md.h" +#include "mbedtls/pk.h" +#include "mbedtls/oid.h" +#include "mbedtls/error.h" + +#include + +/* Translations for symmetric crypto. */ + +static inline psa_key_type_t mbedtls_psa_translate_cipher_type( + mbedtls_cipher_type_t cipher) +{ + switch (cipher) { + case MBEDTLS_CIPHER_AES_128_CCM: + case MBEDTLS_CIPHER_AES_192_CCM: + case MBEDTLS_CIPHER_AES_256_CCM: + case MBEDTLS_CIPHER_AES_128_CCM_STAR_NO_TAG: + case MBEDTLS_CIPHER_AES_192_CCM_STAR_NO_TAG: + case MBEDTLS_CIPHER_AES_256_CCM_STAR_NO_TAG: + case MBEDTLS_CIPHER_AES_128_GCM: + case MBEDTLS_CIPHER_AES_192_GCM: + case MBEDTLS_CIPHER_AES_256_GCM: + case MBEDTLS_CIPHER_AES_128_CBC: + case MBEDTLS_CIPHER_AES_192_CBC: + case MBEDTLS_CIPHER_AES_256_CBC: + case MBEDTLS_CIPHER_AES_128_ECB: + case MBEDTLS_CIPHER_AES_192_ECB: + case MBEDTLS_CIPHER_AES_256_ECB: + return PSA_KEY_TYPE_AES; + + /* ARIA not yet supported in PSA. */ + /* case MBEDTLS_CIPHER_ARIA_128_CCM: + case MBEDTLS_CIPHER_ARIA_192_CCM: + case MBEDTLS_CIPHER_ARIA_256_CCM: + case MBEDTLS_CIPHER_ARIA_128_CCM_STAR_NO_TAG: + case MBEDTLS_CIPHER_ARIA_192_CCM_STAR_NO_TAG: + case MBEDTLS_CIPHER_ARIA_256_CCM_STAR_NO_TAG: + case MBEDTLS_CIPHER_ARIA_128_GCM: + case MBEDTLS_CIPHER_ARIA_192_GCM: + case MBEDTLS_CIPHER_ARIA_256_GCM: + case MBEDTLS_CIPHER_ARIA_128_CBC: + case MBEDTLS_CIPHER_ARIA_192_CBC: + case MBEDTLS_CIPHER_ARIA_256_CBC: + return( PSA_KEY_TYPE_ARIA ); */ + + default: + return 0; + } +} + +static inline psa_algorithm_t mbedtls_psa_translate_cipher_mode( + mbedtls_cipher_mode_t mode, size_t taglen) +{ + switch (mode) { + case MBEDTLS_MODE_ECB: + return PSA_ALG_ECB_NO_PADDING; + case MBEDTLS_MODE_GCM: + return PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, taglen); + case MBEDTLS_MODE_CCM: + return PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen); + case MBEDTLS_MODE_CCM_STAR_NO_TAG: + return PSA_ALG_CCM_STAR_NO_TAG; + case MBEDTLS_MODE_CBC: + if (taglen == 0) { + return PSA_ALG_CBC_NO_PADDING; + } else { + return 0; + } + default: + return 0; + } +} + +static inline psa_key_usage_t mbedtls_psa_translate_cipher_operation( + mbedtls_operation_t op) +{ + switch (op) { + case MBEDTLS_ENCRYPT: + return PSA_KEY_USAGE_ENCRYPT; + case MBEDTLS_DECRYPT: + return PSA_KEY_USAGE_DECRYPT; + default: + return 0; + } +} + +/* Translations for hashing. */ + +/* Note: this function should not be used from inside the library, use + * mbedtls_hash_info_psa_from_md() from the internal hash_info.h instead. + * It is kept only for compatibility in case applications were using it. */ +static inline psa_algorithm_t mbedtls_psa_translate_md(mbedtls_md_type_t md_alg) +{ + switch (md_alg) { +#if defined(MBEDTLS_MD5_C) || defined(PSA_WANT_ALG_MD5) + case MBEDTLS_MD_MD5: + return PSA_ALG_MD5; +#endif +#if defined(MBEDTLS_SHA1_C) || defined(PSA_WANT_ALG_SHA_1) + case MBEDTLS_MD_SHA1: + return PSA_ALG_SHA_1; +#endif +#if defined(MBEDTLS_SHA224_C) || defined(PSA_WANT_ALG_SHA_224) + case MBEDTLS_MD_SHA224: + return PSA_ALG_SHA_224; +#endif +#if defined(MBEDTLS_SHA256_C) || defined(PSA_WANT_ALG_SHA_256) + case MBEDTLS_MD_SHA256: + return PSA_ALG_SHA_256; +#endif +#if defined(MBEDTLS_SHA384_C) || defined(PSA_WANT_ALG_SHA_384) + case MBEDTLS_MD_SHA384: + return PSA_ALG_SHA_384; +#endif +#if defined(MBEDTLS_SHA512_C) || defined(PSA_WANT_ALG_SHA_512) + case MBEDTLS_MD_SHA512: + return PSA_ALG_SHA_512; +#endif +#if defined(MBEDTLS_RIPEMD160_C) || defined(PSA_WANT_ALG_RIPEMD160) + case MBEDTLS_MD_RIPEMD160: + return PSA_ALG_RIPEMD160; +#endif + case MBEDTLS_MD_NONE: + return 0; + default: + return 0; + } +} + +/* Translations for ECC. */ + +static inline int mbedtls_psa_get_ecc_oid_from_id( + psa_ecc_family_t curve, size_t bits, + char const **oid, size_t *oid_len) +{ + switch (curve) { + case PSA_ECC_FAMILY_SECP_R1: + switch (bits) { +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + case 192: + *oid = MBEDTLS_OID_EC_GRP_SECP192R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP192R1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + case 224: + *oid = MBEDTLS_OID_EC_GRP_SECP224R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP224R1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + case 256: + *oid = MBEDTLS_OID_EC_GRP_SECP256R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP256R1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + case 384: + *oid = MBEDTLS_OID_EC_GRP_SECP384R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP384R1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + case 521: + *oid = MBEDTLS_OID_EC_GRP_SECP521R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP521R1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */ + } + break; + case PSA_ECC_FAMILY_SECP_K1: + switch (bits) { +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) + case 192: + *oid = MBEDTLS_OID_EC_GRP_SECP192K1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP192K1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) + case 224: + *oid = MBEDTLS_OID_EC_GRP_SECP224K1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP224K1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) + case 256: + *oid = MBEDTLS_OID_EC_GRP_SECP256K1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_SECP256K1); + return 0; +#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */ + } + break; + case PSA_ECC_FAMILY_BRAINPOOL_P_R1: + switch (bits) { +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) + case 256: + *oid = MBEDTLS_OID_EC_GRP_BP256R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_BP256R1); + return 0; +#endif /* MBEDTLS_ECP_DP_BP256R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) + case 384: + *oid = MBEDTLS_OID_EC_GRP_BP384R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_BP384R1); + return 0; +#endif /* MBEDTLS_ECP_DP_BP384R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) + case 512: + *oid = MBEDTLS_OID_EC_GRP_BP512R1; + *oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_GRP_BP512R1); + return 0; +#endif /* MBEDTLS_ECP_DP_BP512R1_ENABLED */ + } + break; + } + (void) oid; + (void) oid_len; + return -1; +} + +#define MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH \ + PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) + +#define MBEDTLS_PSA_MAX_EC_KEY_PAIR_LENGTH \ + PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) + +/* Expose whatever RNG the PSA subsystem uses to applications using the + * mbedtls_xxx API. The declarations and definitions here need to be + * consistent with the implementation in library/psa_crypto_random_impl.h. + * See that file for implementation documentation. */ + + +/* The type of a `f_rng` random generator function that many library functions + * take. + * + * This type name is not part of the Mbed TLS stable API. It may be renamed + * or moved without warning. + */ +typedef int mbedtls_f_rng_t(void *p_rng, unsigned char *output, size_t output_size); + +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + +/** The random generator function for the PSA subsystem. + * + * This function is suitable as the `f_rng` random generator function + * parameter of many `mbedtls_xxx` functions. Use #MBEDTLS_PSA_RANDOM_STATE + * to obtain the \p p_rng parameter. + * + * The implementation of this function depends on the configuration of the + * library. + * + * \note Depending on the configuration, this may be a function or + * a pointer to a function. + * + * \note This function may only be used if the PSA crypto subsystem is active. + * This means that you must call psa_crypto_init() before any call to + * this function, and you must not call this function after calling + * mbedtls_psa_crypto_free(). + * + * \param p_rng The random generator context. This must be + * #MBEDTLS_PSA_RANDOM_STATE. No other state is + * supported. + * \param output The buffer to fill. It must have room for + * \c output_size bytes. + * \param output_size The number of bytes to write to \p output. + * This function may fail if \p output_size is too + * large. It is guaranteed to accept any output size + * requested by Mbed TLS library functions. The + * maximum request size depends on the library + * configuration. + * + * \return \c 0 on success. + * \return An `MBEDTLS_ERR_ENTROPY_xxx`, + * `MBEDTLS_ERR_PLATFORM_xxx, + * `MBEDTLS_ERR_CTR_DRBG_xxx` or + * `MBEDTLS_ERR_HMAC_DRBG_xxx` on error. + */ +int mbedtls_psa_get_random(void *p_rng, + unsigned char *output, + size_t output_size); + +/** The random generator state for the PSA subsystem. + * + * This macro expands to an expression which is suitable as the `p_rng` + * random generator state parameter of many `mbedtls_xxx` functions. + * It must be used in combination with the random generator function + * mbedtls_psa_get_random(). + * + * The implementation of this macro depends on the configuration of the + * library. Do not make any assumption on its nature. + */ +#define MBEDTLS_PSA_RANDOM_STATE NULL + +#else /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ + +#if defined(MBEDTLS_CTR_DRBG_C) +#include "mbedtls/ctr_drbg.h" +typedef mbedtls_ctr_drbg_context mbedtls_psa_drbg_context_t; +static mbedtls_f_rng_t *const mbedtls_psa_get_random = mbedtls_ctr_drbg_random; +#elif defined(MBEDTLS_HMAC_DRBG_C) +#include "mbedtls/hmac_drbg.h" +typedef mbedtls_hmac_drbg_context mbedtls_psa_drbg_context_t; +static mbedtls_f_rng_t *const mbedtls_psa_get_random = mbedtls_hmac_drbg_random; +#endif +extern mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state; + +#define MBEDTLS_PSA_RANDOM_STATE mbedtls_psa_random_state + +#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ + +typedef struct { + psa_status_t psa_status; + int16_t mbedtls_error; +} mbedtls_error_pair_t; + +#if !defined(MBEDTLS_MD_C) || !defined(MBEDTLS_MD5_C) || defined(MBEDTLS_USE_PSA_CRYPTO) +extern const mbedtls_error_pair_t psa_to_md_errors[4]; +#endif + +#if defined(MBEDTLS_LMS_C) +extern const mbedtls_error_pair_t psa_to_lms_errors[3]; +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) +extern const mbedtls_error_pair_t psa_to_ssl_errors[7]; +#endif + +#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) || \ + defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) +extern const mbedtls_error_pair_t psa_to_pk_rsa_errors[8]; +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +extern const mbedtls_error_pair_t psa_to_pk_ecdsa_errors[7]; +#endif + +/* Generic fallback function for error translation, + * when the received state was not module-specific. */ +int psa_generic_status_to_mbedtls(psa_status_t status); + +/* This function iterates over provided local error translations, + * and if no match was found - calls the fallback error translation function. */ +int psa_status_to_mbedtls(psa_status_t status, + const mbedtls_error_pair_t *local_translations, + size_t local_errors_num, + int (*fallback_f)(psa_status_t)); + +/* The second out of three-stage error handling functions of the pk module, + * acts as a fallback after RSA / ECDSA error translation, and if no match + * is found, it itself calls psa_generic_status_to_mbedtls. */ +int psa_pk_status_to_mbedtls(psa_status_t status); + +/* Utility macro to shorten the defines of error translator in modules. */ +#define PSA_TO_MBEDTLS_ERR_LIST(status, error_list, fallback_f) \ + psa_status_to_mbedtls(status, error_list, \ + sizeof(error_list)/sizeof(error_list[0]), \ + fallback_f) + +#endif /* MBEDTLS_PSA_CRYPTO_C */ +#endif /* MBEDTLS_PSA_UTIL_H */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ripemd160.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ripemd160.h new file mode 100644 index 00000000..acec3c52 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ripemd160.h @@ -0,0 +1,148 @@ +/** + * \file ripemd160.h + * + * \brief RIPE MD-160 message digest + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_RIPEMD160_H +#define MBEDTLS_RIPEMD160_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_RIPEMD160_ALT) +// Regular implementation +// + +/** + * \brief RIPEMD-160 context structure + */ +typedef struct mbedtls_ripemd160_context { + uint32_t MBEDTLS_PRIVATE(total)[2]; /*!< number of bytes processed */ + uint32_t MBEDTLS_PRIVATE(state)[5]; /*!< intermediate digest state */ + unsigned char MBEDTLS_PRIVATE(buffer)[64]; /*!< data block being processed */ +} +mbedtls_ripemd160_context; + +#else /* MBEDTLS_RIPEMD160_ALT */ +#include "ripemd160_alt.h" +#endif /* MBEDTLS_RIPEMD160_ALT */ + +/** + * \brief Initialize RIPEMD-160 context + * + * \param ctx RIPEMD-160 context to be initialized + */ +void mbedtls_ripemd160_init(mbedtls_ripemd160_context *ctx); + +/** + * \brief Clear RIPEMD-160 context + * + * \param ctx RIPEMD-160 context to be cleared + */ +void mbedtls_ripemd160_free(mbedtls_ripemd160_context *ctx); + +/** + * \brief Clone (the state of) a RIPEMD-160 context + * + * \param dst The destination context + * \param src The context to be cloned + */ +void mbedtls_ripemd160_clone(mbedtls_ripemd160_context *dst, + const mbedtls_ripemd160_context *src); + +/** + * \brief RIPEMD-160 context setup + * + * \param ctx context to be initialized + * + * \return 0 if successful + */ +int mbedtls_ripemd160_starts(mbedtls_ripemd160_context *ctx); + +/** + * \brief RIPEMD-160 process buffer + * + * \param ctx RIPEMD-160 context + * \param input buffer holding the data + * \param ilen length of the input data + * + * \return 0 if successful + */ +int mbedtls_ripemd160_update(mbedtls_ripemd160_context *ctx, + const unsigned char *input, + size_t ilen); + +/** + * \brief RIPEMD-160 final digest + * + * \param ctx RIPEMD-160 context + * \param output RIPEMD-160 checksum result + * + * \return 0 if successful + */ +int mbedtls_ripemd160_finish(mbedtls_ripemd160_context *ctx, + unsigned char output[20]); + +/** + * \brief RIPEMD-160 process data block (internal use only) + * + * \param ctx RIPEMD-160 context + * \param data buffer holding one block of data + * + * \return 0 if successful + */ +int mbedtls_internal_ripemd160_process(mbedtls_ripemd160_context *ctx, + const unsigned char data[64]); + +/** + * \brief Output = RIPEMD-160( input buffer ) + * + * \param input buffer holding the data + * \param ilen length of the input data + * \param output RIPEMD-160 checksum result + * + * \return 0 if successful + */ +int mbedtls_ripemd160(const unsigned char *input, + size_t ilen, + unsigned char output[20]); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief Checkup routine + * + * \return 0 if successful, or 1 if the test failed + */ +int mbedtls_ripemd160_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_ripemd160.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/rsa.h b/r5dev/thirdparty/mbedtls/include/mbedtls/rsa.h new file mode 100644 index 00000000..da8639b3 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/rsa.h @@ -0,0 +1,1149 @@ +/** + * \file rsa.h + * + * \brief This file provides an API for the RSA public-key cryptosystem. + * + * The RSA public-key cryptosystem is defined in Public-Key + * Cryptography Standards (PKCS) #1 v1.5: RSA Encryption + * and Public-Key Cryptography Standards (PKCS) #1 v2.1: + * RSA Cryptography Specifications. + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_RSA_H +#define MBEDTLS_RSA_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/bignum.h" +#include "mbedtls/md.h" + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +/* + * RSA Error codes + */ +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_RSA_BAD_INPUT_DATA -0x4080 +/** Input data contains invalid padding and is rejected. */ +#define MBEDTLS_ERR_RSA_INVALID_PADDING -0x4100 +/** Something failed during generation of a key. */ +#define MBEDTLS_ERR_RSA_KEY_GEN_FAILED -0x4180 +/** Key failed to pass the validity check of the library. */ +#define MBEDTLS_ERR_RSA_KEY_CHECK_FAILED -0x4200 +/** The public key operation failed. */ +#define MBEDTLS_ERR_RSA_PUBLIC_FAILED -0x4280 +/** The private key operation failed. */ +#define MBEDTLS_ERR_RSA_PRIVATE_FAILED -0x4300 +/** The PKCS#1 verification failed. */ +#define MBEDTLS_ERR_RSA_VERIFY_FAILED -0x4380 +/** The output buffer for decryption is not large enough. */ +#define MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE -0x4400 +/** The random generator failed to generate non-zeros. */ +#define MBEDTLS_ERR_RSA_RNG_FAILED -0x4480 + +/* + * RSA constants + */ + +#define MBEDTLS_RSA_PKCS_V15 0 /**< Use PKCS#1 v1.5 encoding. */ +#define MBEDTLS_RSA_PKCS_V21 1 /**< Use PKCS#1 v2.1 encoding. */ + +#define MBEDTLS_RSA_SIGN 1 /**< Identifier for RSA signature operations. */ +#define MBEDTLS_RSA_CRYPT 2 /**< Identifier for RSA encryption and decryption operations. */ + +#define MBEDTLS_RSA_SALT_LEN_ANY -1 + +/* + * The above constants may be used even if the RSA module is compile out, + * eg for alternative (PKCS#11) RSA implementations in the PK layers. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_RSA_ALT) +// Regular implementation +// + +/** + * \brief The RSA context structure. + */ +typedef struct mbedtls_rsa_context { + int MBEDTLS_PRIVATE(ver); /*!< Reserved for internal purposes. + * Do not set this field in application + * code. Its meaning might change without + * notice. */ + size_t MBEDTLS_PRIVATE(len); /*!< The size of \p N in Bytes. */ + + mbedtls_mpi MBEDTLS_PRIVATE(N); /*!< The public modulus. */ + mbedtls_mpi MBEDTLS_PRIVATE(E); /*!< The public exponent. */ + + mbedtls_mpi MBEDTLS_PRIVATE(D); /*!< The private exponent. */ + mbedtls_mpi MBEDTLS_PRIVATE(P); /*!< The first prime factor. */ + mbedtls_mpi MBEDTLS_PRIVATE(Q); /*!< The second prime factor. */ + + mbedtls_mpi MBEDTLS_PRIVATE(DP); /*!< D % (P - 1). */ + mbedtls_mpi MBEDTLS_PRIVATE(DQ); /*!< D % (Q - 1). */ + mbedtls_mpi MBEDTLS_PRIVATE(QP); /*!< 1 / (Q % P). */ + + mbedtls_mpi MBEDTLS_PRIVATE(RN); /*!< cached R^2 mod N. */ + + mbedtls_mpi MBEDTLS_PRIVATE(RP); /*!< cached R^2 mod P. */ + mbedtls_mpi MBEDTLS_PRIVATE(RQ); /*!< cached R^2 mod Q. */ + + mbedtls_mpi MBEDTLS_PRIVATE(Vi); /*!< The cached blinding value. */ + mbedtls_mpi MBEDTLS_PRIVATE(Vf); /*!< The cached un-blinding value. */ + + int MBEDTLS_PRIVATE(padding); /*!< Selects padding mode: + #MBEDTLS_RSA_PKCS_V15 for 1.5 padding and + #MBEDTLS_RSA_PKCS_V21 for OAEP or PSS. */ + int MBEDTLS_PRIVATE(hash_id); /*!< Hash identifier of mbedtls_md_type_t type, + as specified in md.h for use in the MGF + mask generating function used in the + EME-OAEP and EMSA-PSS encodings. */ +#if defined(MBEDTLS_THREADING_C) + /* Invariant: the mutex is initialized iff ver != 0. */ + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); /*!< Thread-safety mutex. */ +#endif +} +mbedtls_rsa_context; + +#else /* MBEDTLS_RSA_ALT */ +#include "rsa_alt.h" +#endif /* MBEDTLS_RSA_ALT */ + +/** + * \brief This function initializes an RSA context. + * + * \note This function initializes the padding and the hash + * identifier to respectively #MBEDTLS_RSA_PKCS_V15 and + * #MBEDTLS_MD_NONE. See mbedtls_rsa_set_padding() for more + * information about those parameters. + * + * \param ctx The RSA context to initialize. This must not be \c NULL. + */ +void mbedtls_rsa_init(mbedtls_rsa_context *ctx); + +/** + * \brief This function sets padding for an already initialized RSA + * context. + * + * \note Set padding to #MBEDTLS_RSA_PKCS_V21 for the RSAES-OAEP + * encryption scheme and the RSASSA-PSS signature scheme. + * + * \note The \p hash_id parameter is ignored when using + * #MBEDTLS_RSA_PKCS_V15 padding. + * + * \note The choice of padding mode is strictly enforced for private + * key operations, since there might be security concerns in + * mixing padding modes. For public key operations it is + * a default value, which can be overridden by calling specific + * \c mbedtls_rsa_rsaes_xxx or \c mbedtls_rsa_rsassa_xxx + * functions. + * + * \note The hash selected in \p hash_id is always used for OEAP + * encryption. For PSS signatures, it is always used for + * making signatures, but can be overridden for verifying them. + * If set to #MBEDTLS_MD_NONE, it is always overridden. + * + * \param ctx The initialized RSA context to be configured. + * \param padding The padding mode to use. This must be either + * #MBEDTLS_RSA_PKCS_V15 or #MBEDTLS_RSA_PKCS_V21. + * \param hash_id The hash identifier for PSS or OAEP, if \p padding is + * #MBEDTLS_RSA_PKCS_V21. #MBEDTLS_MD_NONE is accepted by this + * function but may be not suitable for some operations. + * Ignored if \p padding is #MBEDTLS_RSA_PKCS_V15. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_RSA_INVALID_PADDING failure: + * \p padding or \p hash_id is invalid. + */ +int mbedtls_rsa_set_padding(mbedtls_rsa_context *ctx, int padding, + mbedtls_md_type_t hash_id); + +/** + * \brief This function retrieves padding mode of initialized + * RSA context. + * + * \param ctx The initialized RSA context. + * + * \return RSA padding mode. + * + */ +int mbedtls_rsa_get_padding_mode(const mbedtls_rsa_context *ctx); + +/** + * \brief This function retrieves hash identifier of mbedtls_md_type_t + * type. + * + * \param ctx The initialized RSA context. + * + * \return Hash identifier of mbedtls_md_type_t type. + * + */ +int mbedtls_rsa_get_md_alg(const mbedtls_rsa_context *ctx); + +/** + * \brief This function imports a set of core parameters into an + * RSA context. + * + * \note This function can be called multiple times for successive + * imports, if the parameters are not simultaneously present. + * + * Any sequence of calls to this function should be followed + * by a call to mbedtls_rsa_complete(), which checks and + * completes the provided information to a ready-for-use + * public or private RSA key. + * + * \note See mbedtls_rsa_complete() for more information on which + * parameters are necessary to set up a private or public + * RSA key. + * + * \note The imported parameters are copied and need not be preserved + * for the lifetime of the RSA context being set up. + * + * \param ctx The initialized RSA context to store the parameters in. + * \param N The RSA modulus. This may be \c NULL. + * \param P The first prime factor of \p N. This may be \c NULL. + * \param Q The second prime factor of \p N. This may be \c NULL. + * \param D The private exponent. This may be \c NULL. + * \param E The public exponent. This may be \c NULL. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_rsa_import(mbedtls_rsa_context *ctx, + const mbedtls_mpi *N, + const mbedtls_mpi *P, const mbedtls_mpi *Q, + const mbedtls_mpi *D, const mbedtls_mpi *E); + +/** + * \brief This function imports core RSA parameters, in raw big-endian + * binary format, into an RSA context. + * + * \note This function can be called multiple times for successive + * imports, if the parameters are not simultaneously present. + * + * Any sequence of calls to this function should be followed + * by a call to mbedtls_rsa_complete(), which checks and + * completes the provided information to a ready-for-use + * public or private RSA key. + * + * \note See mbedtls_rsa_complete() for more information on which + * parameters are necessary to set up a private or public + * RSA key. + * + * \note The imported parameters are copied and need not be preserved + * for the lifetime of the RSA context being set up. + * + * \param ctx The initialized RSA context to store the parameters in. + * \param N The RSA modulus. This may be \c NULL. + * \param N_len The Byte length of \p N; it is ignored if \p N == NULL. + * \param P The first prime factor of \p N. This may be \c NULL. + * \param P_len The Byte length of \p P; it is ignored if \p P == NULL. + * \param Q The second prime factor of \p N. This may be \c NULL. + * \param Q_len The Byte length of \p Q; it is ignored if \p Q == NULL. + * \param D The private exponent. This may be \c NULL. + * \param D_len The Byte length of \p D; it is ignored if \p D == NULL. + * \param E The public exponent. This may be \c NULL. + * \param E_len The Byte length of \p E; it is ignored if \p E == NULL. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_rsa_import_raw(mbedtls_rsa_context *ctx, + unsigned char const *N, size_t N_len, + unsigned char const *P, size_t P_len, + unsigned char const *Q, size_t Q_len, + unsigned char const *D, size_t D_len, + unsigned char const *E, size_t E_len); + +/** + * \brief This function completes an RSA context from + * a set of imported core parameters. + * + * To setup an RSA public key, precisely \p N and \p E + * must have been imported. + * + * To setup an RSA private key, sufficient information must + * be present for the other parameters to be derivable. + * + * The default implementation supports the following: + *
  • Derive \p P, \p Q from \p N, \p D, \p E.
  • + *
  • Derive \p N, \p D from \p P, \p Q, \p E.
+ * Alternative implementations need not support these. + * + * If this function runs successfully, it guarantees that + * the RSA context can be used for RSA operations without + * the risk of failure or crash. + * + * \warning This function need not perform consistency checks + * for the imported parameters. In particular, parameters that + * are not needed by the implementation might be silently + * discarded and left unchecked. To check the consistency + * of the key material, see mbedtls_rsa_check_privkey(). + * + * \param ctx The initialized RSA context holding imported parameters. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_RSA_BAD_INPUT_DATA if the attempted derivations + * failed. + * + */ +int mbedtls_rsa_complete(mbedtls_rsa_context *ctx); + +/** + * \brief This function exports the core parameters of an RSA key. + * + * If this function runs successfully, the non-NULL buffers + * pointed to by \p N, \p P, \p Q, \p D, and \p E are fully + * written, with additional unused space filled leading by + * zero Bytes. + * + * Possible reasons for returning + * #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED:
    + *
  • An alternative RSA implementation is in use, which + * stores the key externally, and either cannot or should + * not export it into RAM.
  • + *
  • A SW or HW implementation might not support a certain + * deduction. For example, \p P, \p Q from \p N, \p D, + * and \p E if the former are not part of the + * implementation.
+ * + * If the function fails due to an unsupported operation, + * the RSA context stays intact and remains usable. + * + * \param ctx The initialized RSA context. + * \param N The MPI to hold the RSA modulus. + * This may be \c NULL if this field need not be exported. + * \param P The MPI to hold the first prime factor of \p N. + * This may be \c NULL if this field need not be exported. + * \param Q The MPI to hold the second prime factor of \p N. + * This may be \c NULL if this field need not be exported. + * \param D The MPI to hold the private exponent. + * This may be \c NULL if this field need not be exported. + * \param E The MPI to hold the public exponent. + * This may be \c NULL if this field need not be exported. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED if exporting the + * requested parameters cannot be done due to missing + * functionality or because of security policies. + * \return A non-zero return code on any other failure. + * + */ +int mbedtls_rsa_export(const mbedtls_rsa_context *ctx, + mbedtls_mpi *N, mbedtls_mpi *P, mbedtls_mpi *Q, + mbedtls_mpi *D, mbedtls_mpi *E); + +/** + * \brief This function exports core parameters of an RSA key + * in raw big-endian binary format. + * + * If this function runs successfully, the non-NULL buffers + * pointed to by \p N, \p P, \p Q, \p D, and \p E are fully + * written, with additional unused space filled leading by + * zero Bytes. + * + * Possible reasons for returning + * #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED:
    + *
  • An alternative RSA implementation is in use, which + * stores the key externally, and either cannot or should + * not export it into RAM.
  • + *
  • A SW or HW implementation might not support a certain + * deduction. For example, \p P, \p Q from \p N, \p D, + * and \p E if the former are not part of the + * implementation.
+ * If the function fails due to an unsupported operation, + * the RSA context stays intact and remains usable. + * + * \note The length parameters are ignored if the corresponding + * buffer pointers are NULL. + * + * \param ctx The initialized RSA context. + * \param N The Byte array to store the RSA modulus, + * or \c NULL if this field need not be exported. + * \param N_len The size of the buffer for the modulus. + * \param P The Byte array to hold the first prime factor of \p N, + * or \c NULL if this field need not be exported. + * \param P_len The size of the buffer for the first prime factor. + * \param Q The Byte array to hold the second prime factor of \p N, + * or \c NULL if this field need not be exported. + * \param Q_len The size of the buffer for the second prime factor. + * \param D The Byte array to hold the private exponent, + * or \c NULL if this field need not be exported. + * \param D_len The size of the buffer for the private exponent. + * \param E The Byte array to hold the public exponent, + * or \c NULL if this field need not be exported. + * \param E_len The size of the buffer for the public exponent. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED if exporting the + * requested parameters cannot be done due to missing + * functionality or because of security policies. + * \return A non-zero return code on any other failure. + */ +int mbedtls_rsa_export_raw(const mbedtls_rsa_context *ctx, + unsigned char *N, size_t N_len, + unsigned char *P, size_t P_len, + unsigned char *Q, size_t Q_len, + unsigned char *D, size_t D_len, + unsigned char *E, size_t E_len); + +/** + * \brief This function exports CRT parameters of a private RSA key. + * + * \note Alternative RSA implementations not using CRT-parameters + * internally can implement this function based on + * mbedtls_rsa_deduce_opt(). + * + * \param ctx The initialized RSA context. + * \param DP The MPI to hold \c D modulo `P-1`, + * or \c NULL if it need not be exported. + * \param DQ The MPI to hold \c D modulo `Q-1`, + * or \c NULL if it need not be exported. + * \param QP The MPI to hold modular inverse of \c Q modulo \c P, + * or \c NULL if it need not be exported. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + * + */ +int mbedtls_rsa_export_crt(const mbedtls_rsa_context *ctx, + mbedtls_mpi *DP, mbedtls_mpi *DQ, mbedtls_mpi *QP); + +/** + * \brief This function retrieves the length of RSA modulus in Bytes. + * + * \param ctx The initialized RSA context. + * + * \return The length of the RSA modulus in Bytes. + * + */ +size_t mbedtls_rsa_get_len(const mbedtls_rsa_context *ctx); + +/** + * \brief This function generates an RSA keypair. + * + * \note mbedtls_rsa_init() must be called before this function, + * to set up the RSA context. + * + * \param ctx The initialized RSA context used to hold the key. + * \param f_rng The RNG function to be used for key generation. + * This is mandatory and must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. + * This may be \c NULL if \p f_rng doesn't need a context. + * \param nbits The size of the public key in bits. + * \param exponent The public exponent to use. For example, \c 65537. + * This must be odd and greater than \c 1. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent); + +/** + * \brief This function checks if a context contains at least an RSA + * public key. + * + * If the function runs successfully, it is guaranteed that + * enough information is present to perform an RSA public key + * operation using mbedtls_rsa_public(). + * + * \param ctx The initialized RSA context to check. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + * + */ +int mbedtls_rsa_check_pubkey(const mbedtls_rsa_context *ctx); + +/** + * \brief This function checks if a context contains an RSA private key + * and perform basic consistency checks. + * + * \note The consistency checks performed by this function not only + * ensure that mbedtls_rsa_private() can be called successfully + * on the given context, but that the various parameters are + * mutually consistent with high probability, in the sense that + * mbedtls_rsa_public() and mbedtls_rsa_private() are inverses. + * + * \warning This function should catch accidental misconfigurations + * like swapping of parameters, but it cannot establish full + * trust in neither the quality nor the consistency of the key + * material that was used to setup the given RSA context: + *
  • Consistency: Imported parameters that are irrelevant + * for the implementation might be silently dropped. If dropped, + * the current function does not have access to them, + * and therefore cannot check them. See mbedtls_rsa_complete(). + * If you want to check the consistency of the entire + * content of a PKCS1-encoded RSA private key, for example, you + * should use mbedtls_rsa_validate_params() before setting + * up the RSA context. + * Additionally, if the implementation performs empirical checks, + * these checks substantiate but do not guarantee consistency.
  • + *
  • Quality: This function is not expected to perform + * extended quality assessments like checking that the prime + * factors are safe. Additionally, it is the responsibility of the + * user to ensure the trustworthiness of the source of his RSA + * parameters, which goes beyond what is effectively checkable + * by the library.
+ * + * \param ctx The initialized RSA context to check. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_check_privkey(const mbedtls_rsa_context *ctx); + +/** + * \brief This function checks a public-private RSA key pair. + * + * It checks each of the contexts, and makes sure they match. + * + * \param pub The initialized RSA context holding the public key. + * \param prv The initialized RSA context holding the private key. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_check_pub_priv(const mbedtls_rsa_context *pub, + const mbedtls_rsa_context *prv); + +/** + * \brief This function performs an RSA public key operation. + * + * \param ctx The initialized RSA context to use. + * \param input The input buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \note This function does not handle message padding. + * + * \note Make sure to set \p input[0] = 0 or ensure that + * input is smaller than \p N. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_public(mbedtls_rsa_context *ctx, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function performs an RSA private key operation. + * + * \note Blinding is used if and only if a PRNG is provided. + * + * \note If blinding is used, both the base of exponentiation + * and the exponent are blinded, providing protection + * against some side-channel attacks. + * + * \warning It is deprecated and a security risk to not provide + * a PRNG here and thereby prevent the use of blinding. + * Future versions of the library may enforce the presence + * of a PRNG. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function, used for blinding. It is mandatory. + * \param p_rng The RNG context to pass to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context. + * \param input The input buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + * + */ +int mbedtls_rsa_private(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function adds the message padding, then performs an RSA + * operation. + * + * It is the generic wrapper for performing a PKCS#1 encryption + * operation. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG to use. It is used for padding generation + * and it is mandatory. + * \param p_rng The RNG context to be passed to \p f_rng. May be + * \c NULL if \p f_rng doesn't need a context argument. + * \param ilen The length of the plaintext in Bytes. + * \param input The input data to encrypt. This must be a readable + * buffer of size \p ilen Bytes. It may be \c NULL if + * `ilen == 0`. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_encrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t ilen, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function performs a PKCS#1 v1.5 encryption operation + * (RSAES-PKCS1-v1_5-ENCRYPT). + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function to use. It is mandatory and used for + * padding generation. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * \param ilen The length of the plaintext in Bytes. + * \param input The input data to encrypt. This must be a readable + * buffer of size \p ilen Bytes. It may be \c NULL if + * `ilen == 0`. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsaes_pkcs1_v15_encrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t ilen, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function performs a PKCS#1 v2.1 OAEP encryption + * operation (RSAES-OAEP-ENCRYPT). + * + * \note The output buffer must be as large as the size + * of ctx->N. For example, 128 Bytes if RSA-1024 is used. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function to use. This is needed for padding + * generation and is mandatory. + * \param p_rng The RNG context to be passed to \p f_rng. This may + * be \c NULL if \p f_rng doesn't need a context argument. + * \param label The buffer holding the custom label to use. + * This must be a readable buffer of length \p label_len + * Bytes. It may be \c NULL if \p label_len is \c 0. + * \param label_len The length of the label in Bytes. + * \param ilen The length of the plaintext buffer \p input in Bytes. + * \param input The input data to encrypt. This must be a readable + * buffer of size \p ilen Bytes. It may be \c NULL if + * `ilen == 0`. + * \param output The output buffer. This must be a writable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsaes_oaep_encrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output); + +/** + * \brief This function performs an RSA operation, then removes the + * message padding. + * + * It is the generic wrapper for performing a PKCS#1 decryption + * operation. + * + * \note The output buffer length \c output_max_len should be + * as large as the size \p ctx->len of \p ctx->N (for example, + * 128 Bytes if RSA-1024 is used) to be able to hold an + * arbitrary decrypted message. If it is not large enough to + * hold the decryption of the particular ciphertext provided, + * the function returns \c MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory; see mbedtls_rsa_private() for more. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context. + * \param olen The address at which to store the length of + * the plaintext. This must not be \c NULL. + * \param input The ciphertext buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The buffer used to hold the plaintext. This must + * be a writable buffer of length \p output_max_len Bytes. + * \param output_max_len The length in Bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_decrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len); + +/** + * \brief This function performs a PKCS#1 v1.5 decryption + * operation (RSAES-PKCS1-v1_5-DECRYPT). + * + * \note The output buffer length \c output_max_len should be + * as large as the size \p ctx->len of \p ctx->N, for example, + * 128 Bytes if RSA-1024 is used, to be able to hold an + * arbitrary decrypted message. If it is not large enough to + * hold the decryption of the particular ciphertext provided, + * the function returns #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory; see mbedtls_rsa_private() for more. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context. + * \param olen The address at which to store the length of + * the plaintext. This must not be \c NULL. + * \param input The ciphertext buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The buffer used to hold the plaintext. This must + * be a writable buffer of length \p output_max_len Bytes. + * \param output_max_len The length in Bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + * + */ +int mbedtls_rsa_rsaes_pkcs1_v15_decrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len); + +/** + * \brief This function performs a PKCS#1 v2.1 OAEP decryption + * operation (RSAES-OAEP-DECRYPT). + * + * \note The output buffer length \c output_max_len should be + * as large as the size \p ctx->len of \p ctx->N, for + * example, 128 Bytes if RSA-1024 is used, to be able to + * hold an arbitrary decrypted message. If it is not + * large enough to hold the decryption of the particular + * ciphertext provided, the function returns + * #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory. + * \param p_rng The RNG context to be passed to \p f_rng. This may be + * \c NULL if \p f_rng doesn't need a context. + * \param label The buffer holding the custom label to use. + * This must be a readable buffer of length \p label_len + * Bytes. It may be \c NULL if \p label_len is \c 0. + * \param label_len The length of the label in Bytes. + * \param olen The address at which to store the length of + * the plaintext. This must not be \c NULL. + * \param input The ciphertext buffer. This must be a readable buffer + * of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * \param output The buffer used to hold the plaintext. This must + * be a writable buffer of length \p output_max_len Bytes. + * \param output_max_len The length in Bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsaes_oaep_decrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len); + +/** + * \brief This function performs a private RSA operation to sign + * a message digest using PKCS#1. + * + * It is the generic wrapper for performing a PKCS#1 + * signature. + * + * \note The \p sig buffer must be as large as the size + * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used. + * + * \note For PKCS#1 v2.1 encoding, see comments on + * mbedtls_rsa_rsassa_pss_sign() for details on + * \p md_alg and \p hash_id. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function to use. This is mandatory and + * must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_sign(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig); + +/** + * \brief This function performs a PKCS#1 v1.5 signature + * operation (RSASSA-PKCS1-v1_5-SIGN). + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. This is used for blinding and is + * mandatory; see mbedtls_rsa_private() for more. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS signature + * operation (RSASSA-PSS-SIGN). + * + * \note The \c hash_id set in \p ctx by calling + * mbedtls_rsa_set_padding() selects the hash used for the + * encoding operation and for the mask generation function + * (MGF1). For more details on the encoding operation and the + * mask generation function, consult RFC-3447: Public-Key + * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography + * Specifications. + * + * \note This function enforces that the provided salt length complies + * with FIPS 186-4 §5.5 (e) and RFC 8017 (PKCS#1 v2.2) §9.1.1 + * step 3. The constraint is that the hash length plus the salt + * length plus 2 bytes must be at most the key length. If this + * constraint is not met, this function returns + * #MBEDTLS_ERR_RSA_BAD_INPUT_DATA. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. It is mandatory and must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param saltlen The length of the salt that should be used. + * If passed #MBEDTLS_RSA_SALT_LEN_ANY, the function will use + * the largest possible salt length up to the hash length, + * which is the largest permitted by some standards including + * FIPS 186-4 §5.5. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_sign_ext(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + int saltlen, + unsigned char *sig); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS signature + * operation (RSASSA-PSS-SIGN). + * + * \note The \c hash_id set in \p ctx by calling + * mbedtls_rsa_set_padding() selects the hash used for the + * encoding operation and for the mask generation function + * (MGF1). For more details on the encoding operation and the + * mask generation function, consult RFC-3447: Public-Key + * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography + * Specifications. + * + * \note This function always uses the maximum possible salt size, + * up to the length of the payload hash. This choice of salt + * size complies with FIPS 186-4 §5.5 (e) and RFC 8017 (PKCS#1 + * v2.2) §9.1.1 step 3. Furthermore this function enforces a + * minimum salt size which is the hash size minus 2 bytes. If + * this minimum size is too large given the key size (the salt + * size, plus the hash size, plus 2 bytes must be no more than + * the key size in bytes), this function returns + * #MBEDTLS_ERR_RSA_BAD_INPUT_DATA. + * + * \param ctx The initialized RSA context to use. + * \param f_rng The RNG function. It is mandatory and must not be \c NULL. + * \param p_rng The RNG context to be passed to \p f_rng. This may be \c NULL + * if \p f_rng doesn't need a context argument. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer to hold the signature. This must be a writable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. A buffer length of + * #MBEDTLS_MPI_MAX_SIZE is always safe. + * + * \return \c 0 if the signing operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_sign(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig); + +/** + * \brief This function performs a public RSA operation and checks + * the message digest. + * + * This is the generic wrapper for performing a PKCS#1 + * verification. + * + * \note For PKCS#1 v2.1 encoding, see comments on + * mbedtls_rsa_rsassa_pss_verify() about \p md_alg and + * \p hash_id. + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_pkcs1_verify(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig); + +/** + * \brief This function performs a PKCS#1 v1.5 verification + * operation (RSASSA-PKCS1-v1_5-VERIFY). + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pkcs1_v15_verify(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS verification + * operation (RSASSA-PSS-VERIFY). + * + * \note The \c hash_id set in \p ctx by calling + * mbedtls_rsa_set_padding() selects the hash used for the + * encoding operation and for the mask generation function + * (MGF1). For more details on the encoding operation and the + * mask generation function, consult RFC-3447: Public-Key + * Cryptography Standards (PKCS) #1 v2.1: RSA Cryptography + * Specifications. If the \c hash_id set in \p ctx by + * mbedtls_rsa_set_padding() is #MBEDTLS_MD_NONE, the \p md_alg + * parameter is used. + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_verify(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig); + +/** + * \brief This function performs a PKCS#1 v2.1 PSS verification + * operation (RSASSA-PSS-VERIFY). + * + * \note The \p sig buffer must be as large as the size + * of \p ctx->N. For example, 128 Bytes if RSA-1024 is used. + * + * \note The \c hash_id set in \p ctx by mbedtls_rsa_set_padding() is + * ignored. + * + * \param ctx The initialized RSA public key context to use. + * \param md_alg The message-digest algorithm used to hash the original data. + * Use #MBEDTLS_MD_NONE for signing raw data. + * \param hashlen The length of the message digest or raw data in Bytes. + * If \p md_alg is not #MBEDTLS_MD_NONE, this must match the + * output length of the corresponding hash algorithm. + * \param hash The buffer holding the message digest or raw data. + * This must be a readable buffer of at least \p hashlen Bytes. + * \param mgf1_hash_id The message digest algorithm used for the + * verification operation and the mask generation + * function (MGF1). For more details on the encoding + * operation and the mask generation function, consult + * RFC-3447: Public-Key Cryptography Standards + * (PKCS) #1 v2.1: RSA Cryptography + * Specifications. + * \param expected_salt_len The length of the salt used in padding. Use + * #MBEDTLS_RSA_SALT_LEN_ANY to accept any salt length. + * \param sig The buffer holding the signature. This must be a readable + * buffer of length \c ctx->len Bytes. For example, \c 256 Bytes + * for an 2048-bit RSA modulus. + * + * \return \c 0 if the verify operation was successful. + * \return An \c MBEDTLS_ERR_RSA_XXX error code on failure. + */ +int mbedtls_rsa_rsassa_pss_verify_ext(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + mbedtls_md_type_t mgf1_hash_id, + int expected_salt_len, + const unsigned char *sig); + +/** + * \brief This function copies the components of an RSA context. + * + * \param dst The destination context. This must be initialized. + * \param src The source context. This must be initialized. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_MPI_ALLOC_FAILED on memory allocation failure. + */ +int mbedtls_rsa_copy(mbedtls_rsa_context *dst, const mbedtls_rsa_context *src); + +/** + * \brief This function frees the components of an RSA key. + * + * \param ctx The RSA context to free. May be \c NULL, in which case + * this function is a no-op. If it is not \c NULL, it must + * point to an initialized RSA context. + */ +void mbedtls_rsa_free(mbedtls_rsa_context *ctx); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The RSA checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_rsa_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* rsa.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/sha1.h b/r5dev/thirdparty/mbedtls/include/mbedtls/sha1.h new file mode 100644 index 00000000..18bde93d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/sha1.h @@ -0,0 +1,231 @@ +/** + * \file sha1.h + * + * \brief This file contains SHA-1 definitions and functions. + * + * The Secure Hash Algorithm 1 (SHA-1) cryptographic hash function is defined in + * FIPS 180-4: Secure Hash Standard (SHS). + * + * \warning SHA-1 is considered a weak message digest and its use constitutes + * a security risk. We recommend considering stronger message + * digests instead. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SHA1_H +#define MBEDTLS_SHA1_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +/** SHA-1 input data was malformed. */ +#define MBEDTLS_ERR_SHA1_BAD_INPUT_DATA -0x0073 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_SHA1_ALT) +// Regular implementation +// + +/** + * \brief The SHA-1 context structure. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + */ +typedef struct mbedtls_sha1_context { + uint32_t MBEDTLS_PRIVATE(total)[2]; /*!< The number of Bytes processed. */ + uint32_t MBEDTLS_PRIVATE(state)[5]; /*!< The intermediate digest state. */ + unsigned char MBEDTLS_PRIVATE(buffer)[64]; /*!< The data block being processed. */ +} +mbedtls_sha1_context; + +#else /* MBEDTLS_SHA1_ALT */ +#include "sha1_alt.h" +#endif /* MBEDTLS_SHA1_ALT */ + +/** + * \brief This function initializes a SHA-1 context. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param ctx The SHA-1 context to initialize. + * This must not be \c NULL. + * + */ +void mbedtls_sha1_init(mbedtls_sha1_context *ctx); + +/** + * \brief This function clears a SHA-1 context. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param ctx The SHA-1 context to clear. This may be \c NULL, + * in which case this function does nothing. If it is + * not \c NULL, it must point to an initialized + * SHA-1 context. + * + */ +void mbedtls_sha1_free(mbedtls_sha1_context *ctx); + +/** + * \brief This function clones the state of a SHA-1 context. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param dst The SHA-1 context to clone to. This must be initialized. + * \param src The SHA-1 context to clone from. This must be initialized. + * + */ +void mbedtls_sha1_clone(mbedtls_sha1_context *dst, + const mbedtls_sha1_context *src); + +/** + * \brief This function starts a SHA-1 checksum calculation. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param ctx The SHA-1 context to initialize. This must be initialized. + * + * \return \c 0 on success. + * \return A negative error code on failure. + * + */ +int mbedtls_sha1_starts(mbedtls_sha1_context *ctx); + +/** + * \brief This function feeds an input buffer into an ongoing SHA-1 + * checksum calculation. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param ctx The SHA-1 context. This must be initialized + * and have a hash operation started. + * \param input The buffer holding the input data. + * This must be a readable buffer of length \p ilen Bytes. + * \param ilen The length of the input data \p input in Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha1_update(mbedtls_sha1_context *ctx, + const unsigned char *input, + size_t ilen); + +/** + * \brief This function finishes the SHA-1 operation, and writes + * the result to the output buffer. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param ctx The SHA-1 context to use. This must be initialized and + * have a hash operation started. + * \param output The SHA-1 checksum result. This must be a writable + * buffer of length \c 20 Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha1_finish(mbedtls_sha1_context *ctx, + unsigned char output[20]); + +/** + * \brief SHA-1 process data block (internal use only). + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param ctx The SHA-1 context to use. This must be initialized. + * \param data The data block being processed. This must be a + * readable buffer of length \c 64 Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + * + */ +int mbedtls_internal_sha1_process(mbedtls_sha1_context *ctx, + const unsigned char data[64]); + +/** + * \brief This function calculates the SHA-1 checksum of a buffer. + * + * The function allocates the context, performs the + * calculation, and frees the context. + * + * The SHA-1 result is calculated as + * output = SHA-1(input buffer). + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \param input The buffer holding the input data. + * This must be a readable buffer of length \p ilen Bytes. + * \param ilen The length of the input data \p input in Bytes. + * \param output The SHA-1 checksum result. + * This must be a writable buffer of length \c 20 Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + * + */ +int mbedtls_sha1(const unsigned char *input, + size_t ilen, + unsigned char output[20]); + +#if defined(MBEDTLS_SELF_TEST) + +/** + * \brief The SHA-1 checkup routine. + * + * \warning SHA-1 is considered a weak message digest and its use + * constitutes a security risk. We recommend considering + * stronger message digests instead. + * + * \return \c 0 on success. + * \return \c 1 on failure. + * + */ +int mbedtls_sha1_self_test(int verbose); + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_sha1.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/sha256.h b/r5dev/thirdparty/mbedtls/include/mbedtls/sha256.h new file mode 100644 index 00000000..87e5cc61 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/sha256.h @@ -0,0 +1,210 @@ +/** + * \file sha256.h + * + * \brief This file contains SHA-224 and SHA-256 definitions and functions. + * + * The Secure Hash Algorithms 224 and 256 (SHA-224 and SHA-256) cryptographic + * hash functions are defined in FIPS 180-4: Secure Hash Standard (SHS). + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SHA256_H +#define MBEDTLS_SHA256_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +/** SHA-256 input data was malformed. */ +#define MBEDTLS_ERR_SHA256_BAD_INPUT_DATA -0x0074 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_SHA256_ALT) +// Regular implementation +// + +/** + * \brief The SHA-256 context structure. + * + * The structure is used both for SHA-256 and for SHA-224 + * checksum calculations. The choice between these two is + * made in the call to mbedtls_sha256_starts(). + */ +typedef struct mbedtls_sha256_context { + uint32_t MBEDTLS_PRIVATE(total)[2]; /*!< The number of Bytes processed. */ + uint32_t MBEDTLS_PRIVATE(state)[8]; /*!< The intermediate digest state. */ + unsigned char MBEDTLS_PRIVATE(buffer)[64]; /*!< The data block being processed. */ + int MBEDTLS_PRIVATE(is224); /*!< Determines which function to use: + 0: Use SHA-256, or 1: Use SHA-224. */ +} +mbedtls_sha256_context; + +#else /* MBEDTLS_SHA256_ALT */ +#include "sha256_alt.h" +#endif /* MBEDTLS_SHA256_ALT */ + +/** + * \brief This function initializes a SHA-256 context. + * + * \param ctx The SHA-256 context to initialize. This must not be \c NULL. + */ +void mbedtls_sha256_init(mbedtls_sha256_context *ctx); + +/** + * \brief This function clears a SHA-256 context. + * + * \param ctx The SHA-256 context to clear. This may be \c NULL, in which + * case this function returns immediately. If it is not \c NULL, + * it must point to an initialized SHA-256 context. + */ +void mbedtls_sha256_free(mbedtls_sha256_context *ctx); + +/** + * \brief This function clones the state of a SHA-256 context. + * + * \param dst The destination context. This must be initialized. + * \param src The context to clone. This must be initialized. + */ +void mbedtls_sha256_clone(mbedtls_sha256_context *dst, + const mbedtls_sha256_context *src); + +/** + * \brief This function starts a SHA-224 or SHA-256 checksum + * calculation. + * + * \param ctx The context to use. This must be initialized. + * \param is224 This determines which function to use. This must be + * either \c 0 for SHA-256, or \c 1 for SHA-224. + * + * \note is224 must be defined accordingly to the enabled + * MBEDTLS_SHA224_C/MBEDTLS_SHA256_C symbols otherwise the + * function will return #MBEDTLS_ERR_SHA512_BAD_INPUT_DATA. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha256_starts(mbedtls_sha256_context *ctx, int is224); + +/** + * \brief This function feeds an input buffer into an ongoing + * SHA-256 checksum calculation. + * + * \param ctx The SHA-256 context. This must be initialized + * and have a hash operation started. + * \param input The buffer holding the data. This must be a readable + * buffer of length \p ilen Bytes. + * \param ilen The length of the input data in Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha256_update(mbedtls_sha256_context *ctx, + const unsigned char *input, + size_t ilen); + +/** + * \brief This function finishes the SHA-256 operation, and writes + * the result to the output buffer. + * + * \param ctx The SHA-256 context. This must be initialized + * and have a hash operation started. + * \param output The SHA-224 or SHA-256 checksum result. + * This must be a writable buffer of length \c 32 bytes + * for SHA-256, \c 28 bytes for SHA-224. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha256_finish(mbedtls_sha256_context *ctx, + unsigned char *output); + +/** + * \brief This function processes a single data block within + * the ongoing SHA-256 computation. This function is for + * internal use only. + * + * \param ctx The SHA-256 context. This must be initialized. + * \param data The buffer holding one block of data. This must + * be a readable buffer of length \c 64 Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_internal_sha256_process(mbedtls_sha256_context *ctx, + const unsigned char data[64]); + +/** + * \brief This function calculates the SHA-224 or SHA-256 + * checksum of a buffer. + * + * The function allocates the context, performs the + * calculation, and frees the context. + * + * The SHA-256 result is calculated as + * output = SHA-256(input buffer). + * + * \param input The buffer holding the data. This must be a readable + * buffer of length \p ilen Bytes. + * \param ilen The length of the input data in Bytes. + * \param output The SHA-224 or SHA-256 checksum result. + * This must be a writable buffer of length \c 32 bytes + * for SHA-256, \c 28 bytes for SHA-224. + * \param is224 Determines which function to use. This must be + * either \c 0 for SHA-256, or \c 1 for SHA-224. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha256(const unsigned char *input, + size_t ilen, + unsigned char *output, + int is224); + +#if defined(MBEDTLS_SELF_TEST) + +#if defined(MBEDTLS_SHA224_C) +/** + * \brief The SHA-224 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_sha224_self_test(int verbose); +#endif /* MBEDTLS_SHA224_C */ + +#if defined(MBEDTLS_SHA256_C) +/** + * \brief The SHA-256 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_sha256_self_test(int verbose); +#endif /* MBEDTLS_SHA256_C */ + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_sha256.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/sha512.h b/r5dev/thirdparty/mbedtls/include/mbedtls/sha512.h new file mode 100644 index 00000000..ea546782 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/sha512.h @@ -0,0 +1,220 @@ +/** + * \file sha512.h + * \brief This file contains SHA-384 and SHA-512 definitions and functions. + * + * The Secure Hash Algorithms 384 and 512 (SHA-384 and SHA-512) cryptographic + * hash functions are defined in FIPS 180-4: Secure Hash Standard (SHS). + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SHA512_H +#define MBEDTLS_SHA512_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include +#include + +/** SHA-512 input data was malformed. */ +#define MBEDTLS_ERR_SHA512_BAD_INPUT_DATA -0x0075 + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_SHA512_ALT) +// Regular implementation +// + +/** + * \brief The SHA-512 context structure. + * + * The structure is used both for SHA-384 and for SHA-512 + * checksum calculations. The choice between these two is + * made in the call to mbedtls_sha512_starts(). + */ +typedef struct mbedtls_sha512_context { + uint64_t MBEDTLS_PRIVATE(total)[2]; /*!< The number of Bytes processed. */ + uint64_t MBEDTLS_PRIVATE(state)[8]; /*!< The intermediate digest state. */ + unsigned char MBEDTLS_PRIVATE(buffer)[128]; /*!< The data block being processed. */ +#if defined(MBEDTLS_SHA384_C) + int MBEDTLS_PRIVATE(is384); /*!< Determines which function to use: + 0: Use SHA-512, or 1: Use SHA-384. */ +#endif +} +mbedtls_sha512_context; + +#else /* MBEDTLS_SHA512_ALT */ +#include "sha512_alt.h" +#endif /* MBEDTLS_SHA512_ALT */ + +/** + * \brief This function initializes a SHA-512 context. + * + * \param ctx The SHA-512 context to initialize. This must + * not be \c NULL. + */ +void mbedtls_sha512_init(mbedtls_sha512_context *ctx); + +/** + * \brief This function clears a SHA-512 context. + * + * \param ctx The SHA-512 context to clear. This may be \c NULL, + * in which case this function does nothing. If it + * is not \c NULL, it must point to an initialized + * SHA-512 context. + */ +void mbedtls_sha512_free(mbedtls_sha512_context *ctx); + +/** + * \brief This function clones the state of a SHA-512 context. + * + * \param dst The destination context. This must be initialized. + * \param src The context to clone. This must be initialized. + */ +void mbedtls_sha512_clone(mbedtls_sha512_context *dst, + const mbedtls_sha512_context *src); + +/** + * \brief This function starts a SHA-384 or SHA-512 checksum + * calculation. + * + * \param ctx The SHA-512 context to use. This must be initialized. + * \param is384 Determines which function to use. This must be + * either \c 0 for SHA-512, or \c 1 for SHA-384. + * + * \note is384 must be defined accordingly to the enabled + * MBEDTLS_SHA384_C/MBEDTLS_SHA512_C symbols otherwise the + * function will return #MBEDTLS_ERR_SHA512_BAD_INPUT_DATA. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha512_starts(mbedtls_sha512_context *ctx, int is384); + +/** + * \brief This function feeds an input buffer into an ongoing + * SHA-512 checksum calculation. + * + * \param ctx The SHA-512 context. This must be initialized + * and have a hash operation started. + * \param input The buffer holding the input data. This must + * be a readable buffer of length \p ilen Bytes. + * \param ilen The length of the input data in Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha512_update(mbedtls_sha512_context *ctx, + const unsigned char *input, + size_t ilen); + +/** + * \brief This function finishes the SHA-512 operation, and writes + * the result to the output buffer. + * + * \param ctx The SHA-512 context. This must be initialized + * and have a hash operation started. + * \param output The SHA-384 or SHA-512 checksum result. + * This must be a writable buffer of length \c 64 bytes + * for SHA-512, \c 48 bytes for SHA-384. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha512_finish(mbedtls_sha512_context *ctx, + unsigned char *output); + +/** + * \brief This function processes a single data block within + * the ongoing SHA-512 computation. + * This function is for internal use only. + * + * \param ctx The SHA-512 context. This must be initialized. + * \param data The buffer holding one block of data. This + * must be a readable buffer of length \c 128 Bytes. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_internal_sha512_process(mbedtls_sha512_context *ctx, + const unsigned char data[128]); + +/** + * \brief This function calculates the SHA-512 or SHA-384 + * checksum of a buffer. + * + * The function allocates the context, performs the + * calculation, and frees the context. + * + * The SHA-512 result is calculated as + * output = SHA-512(input buffer). + * + * \param input The buffer holding the input data. This must be + * a readable buffer of length \p ilen Bytes. + * \param ilen The length of the input data in Bytes. + * \param output The SHA-384 or SHA-512 checksum result. + * This must be a writable buffer of length \c 64 bytes + * for SHA-512, \c 48 bytes for SHA-384. + * \param is384 Determines which function to use. This must be either + * \c 0 for SHA-512, or \c 1 for SHA-384. + * + * \note is384 must be defined accordingly with the supported + * symbols in the config file. If: + * - is384 is 0, but \c MBEDTLS_SHA384_C is not defined, or + * - is384 is 1, but \c MBEDTLS_SHA512_C is not defined + * then the function will return + * #MBEDTLS_ERR_SHA512_BAD_INPUT_DATA. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_sha512(const unsigned char *input, + size_t ilen, + unsigned char *output, + int is384); + +#if defined(MBEDTLS_SELF_TEST) + +#if defined(MBEDTLS_SHA384_C) +/** + * \brief The SHA-384 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_sha384_self_test(int verbose); +#endif /* MBEDTLS_SHA384_C */ + +#if defined(MBEDTLS_SHA512_C) +/** + * \brief The SHA-512 checkup routine. + * + * \return \c 0 on success. + * \return \c 1 on failure. + */ +int mbedtls_sha512_self_test(int verbose); +#endif /* MBEDTLS_SHA512_C */ + +#endif /* MBEDTLS_SELF_TEST */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_sha512.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ssl.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl.h new file mode 100644 index 00000000..68e8d040 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl.h @@ -0,0 +1,5335 @@ +/** + * \file ssl.h + * + * \brief SSL/TLS functions. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SSL_H +#define MBEDTLS_SSL_H +#include "mbedtls/platform_util.h" +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/bignum.h" +#include "mbedtls/ecp.h" + +#include "mbedtls/ssl_ciphersuites.h" + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#include "mbedtls/x509_crt.h" +#include "mbedtls/x509_crl.h" +#endif + +#if defined(MBEDTLS_DHM_C) +#include "mbedtls/dhm.h" +#endif + +/* Adding guard for MBEDTLS_ECDSA_C to ensure no compile errors due + * to guards in TLS code. There is a gap in functionality that access to + * ecdh_ctx structure is needed for MBEDTLS_ECDSA_C which does not seem correct. + */ +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) +#include "mbedtls/ecdh.h" +#endif + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif + +#include "psa/crypto.h" + +/* + * SSL Error codes + */ +/** A cryptographic operation is in progress. Try again later. */ +#define MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS -0x7000 +/** The requested feature is not available. */ +#define MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE -0x7080 +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_SSL_BAD_INPUT_DATA -0x7100 +/** Verification of the message MAC failed. */ +#define MBEDTLS_ERR_SSL_INVALID_MAC -0x7180 +/** An invalid SSL record was received. */ +#define MBEDTLS_ERR_SSL_INVALID_RECORD -0x7200 +/** The connection indicated an EOF. */ +#define MBEDTLS_ERR_SSL_CONN_EOF -0x7280 +/** A message could not be parsed due to a syntactic error. */ +#define MBEDTLS_ERR_SSL_DECODE_ERROR -0x7300 +/* Error space gap */ +/** No RNG was provided to the SSL module. */ +#define MBEDTLS_ERR_SSL_NO_RNG -0x7400 +/** No client certification received from the client, but required by the authentication mode. */ +#define MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE -0x7480 +/** Client received an extended server hello containing an unsupported extension */ +#define MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION -0x7500 +/** No ALPN protocols supported that the client advertises */ +#define MBEDTLS_ERR_SSL_NO_APPLICATION_PROTOCOL -0x7580 +/** The own private key or pre-shared key is not set, but needed. */ +#define MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED -0x7600 +/** No CA Chain is set, but required to operate. */ +#define MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED -0x7680 +/** An unexpected message was received from our peer. */ +#define MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE -0x7700 +/** A fatal alert message was received from our peer. */ +#define MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE -0x7780 +/** No server could be identified matching the client's SNI. */ +#define MBEDTLS_ERR_SSL_UNRECOGNIZED_NAME -0x7800 +/** The peer notified us that the connection is going to be closed. */ +#define MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY -0x7880 +/* Error space gap */ +/* Error space gap */ +/** Processing of the Certificate handshake message failed. */ +#define MBEDTLS_ERR_SSL_BAD_CERTIFICATE -0x7A00 +/* Error space gap */ +/** + * Received NewSessionTicket Post Handshake Message. + * This error code is experimental and may be changed or removed without notice. + */ +#define MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET -0x7B00 +/** Not possible to read early data */ +#define MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA -0x7B80 +/** Not possible to write early data */ +#define MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA -0x7C00 +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/** Memory allocation failed */ +#define MBEDTLS_ERR_SSL_ALLOC_FAILED -0x7F00 +/** Hardware acceleration function returned with error */ +#define MBEDTLS_ERR_SSL_HW_ACCEL_FAILED -0x7F80 +/** Hardware acceleration function skipped / left alone data */ +#define MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH -0x6F80 +/** Handshake protocol not within min/max boundaries */ +#define MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION -0x6E80 +/** The handshake negotiation failed. */ +#define MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE -0x6E00 +/** Session ticket has expired. */ +#define MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED -0x6D80 +/** Public key type mismatch (eg, asked for RSA key exchange and presented EC key) */ +#define MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH -0x6D00 +/** Unknown identity received (eg, PSK identity) */ +#define MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY -0x6C80 +/** Internal error (eg, unexpected failure in lower-level module) */ +#define MBEDTLS_ERR_SSL_INTERNAL_ERROR -0x6C00 +/** A counter would wrap (eg, too many messages exchanged). */ +#define MBEDTLS_ERR_SSL_COUNTER_WRAPPING -0x6B80 +/** Unexpected message at ServerHello in renegotiation. */ +#define MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO -0x6B00 +/** DTLS client must retry for hello verification */ +#define MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED -0x6A80 +/** A buffer is too small to receive or write a message */ +#define MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL -0x6A00 +/* Error space gap */ +/** No data of requested type currently available on underlying transport. */ +#define MBEDTLS_ERR_SSL_WANT_READ -0x6900 +/** Connection requires a write call. */ +#define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 +/** The operation timed out. */ +#define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 +/** The client initiated a reconnect from the same port. */ +#define MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780 +/** Record header looks valid but is not expected. */ +#define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD -0x6700 +/** The alert message received indicates a non-fatal error. */ +#define MBEDTLS_ERR_SSL_NON_FATAL -0x6680 +/** A field in a message was incorrect or inconsistent with other fields. */ +#define MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER -0x6600 +/** Internal-only message signaling that further message-processing should be done */ +#define MBEDTLS_ERR_SSL_CONTINUE_PROCESSING -0x6580 +/** The asynchronous operation is not completed yet. */ +#define MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS -0x6500 +/** Internal-only message signaling that a message arrived early. */ +#define MBEDTLS_ERR_SSL_EARLY_MESSAGE -0x6480 +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/* Error space gap */ +/** An encrypted DTLS-frame with an unexpected CID was received. */ +#define MBEDTLS_ERR_SSL_UNEXPECTED_CID -0x6000 +/** An operation failed due to an unexpected version or configuration. */ +#define MBEDTLS_ERR_SSL_VERSION_MISMATCH -0x5F00 +/** Invalid value in SSL config */ +#define MBEDTLS_ERR_SSL_BAD_CONFIG -0x5E80 + +/* + * Constants from RFC 8446 for TLS 1.3 PSK modes + * + * Those are used in the Pre-Shared Key Exchange Modes extension. + * See Section 4.2.9 in RFC 8446. + */ +#define MBEDTLS_SSL_TLS1_3_PSK_MODE_PURE 0 /* Pure PSK-based exchange */ +#define MBEDTLS_SSL_TLS1_3_PSK_MODE_ECDHE 1 /* PSK+ECDHE-based exchange */ + +/* + * TLS 1.3 NamedGroup values + * + * From RF 8446 + * enum { + * // Elliptic Curve Groups (ECDHE) + * secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019), + * x25519(0x001D), x448(0x001E), + * // Finite Field Groups (DHE) + * ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102), + * ffdhe6144(0x0103), ffdhe8192(0x0104), + * // Reserved Code Points + * ffdhe_private_use(0x01FC..0x01FF), + * ecdhe_private_use(0xFE00..0xFEFF), + * (0xFFFF) + * } NamedGroup; + * + */ + +/* Elliptic Curve Groups (ECDHE) */ +#define MBEDTLS_SSL_IANA_TLS_GROUP_NONE 0 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP192K1 0x0012 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP192R1 0x0013 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP224K1 0x0014 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP224R1 0x0015 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP256K1 0x0016 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1 0x0017 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1 0x0018 +#define MBEDTLS_SSL_IANA_TLS_GROUP_SECP521R1 0x0019 +#define MBEDTLS_SSL_IANA_TLS_GROUP_BP256R1 0x001A +#define MBEDTLS_SSL_IANA_TLS_GROUP_BP384R1 0x001B +#define MBEDTLS_SSL_IANA_TLS_GROUP_BP512R1 0x001C +#define MBEDTLS_SSL_IANA_TLS_GROUP_X25519 0x001D +#define MBEDTLS_SSL_IANA_TLS_GROUP_X448 0x001E +/* Finite Field Groups (DHE) */ +#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE2048 0x0100 +#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE3072 0x0101 +#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE4096 0x0102 +#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE6144 0x0103 +#define MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE8192 0x0104 + +/* + * TLS 1.3 Key Exchange Modes + * + * Mbed TLS internal identifiers for use with the SSL configuration API + * mbedtls_ssl_conf_tls13_key_exchange_modes(). + */ + +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK (1u << 0) /*!< Pure-PSK TLS 1.3 key exchange, + * encompassing both externally agreed PSKs + * as well as resumption PSKs. */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL (1u << 1) /*!< Pure-Ephemeral TLS 1.3 key exchanges, + * including for example ECDHE and DHE + * key exchanges. */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL (1u << 2) /*!< PSK-Ephemeral TLS 1.3 key exchanges, + * using both a PSK and an ephemeral + * key exchange. */ + +/* Convenience macros for sets of key exchanges. */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_ALL \ + (MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK | \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL | \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL) /*!< All TLS 1.3 key exchanges */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL \ + (MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK | \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL) /*!< All PSK-based TLS 1.3 key exchanges */ +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ALL \ + (MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL | \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL) /*!< All ephemeral TLS 1.3 key exchanges */ + +#define MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_NONE (0) + +/* + * Various constants + */ + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/* These are the high and low bytes of ProtocolVersion as defined by: + * - RFC 5246: ProtocolVersion version = { 3, 3 }; // TLS v1.2 + * - RFC 8446: see section 4.2.1 + */ +#define MBEDTLS_SSL_MAJOR_VERSION_3 3 +#define MBEDTLS_SSL_MINOR_VERSION_3 3 /*!< TLS v1.2 */ +#define MBEDTLS_SSL_MINOR_VERSION_4 4 /*!< TLS v1.3 */ +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +#define MBEDTLS_SSL_TRANSPORT_STREAM 0 /*!< TLS */ +#define MBEDTLS_SSL_TRANSPORT_DATAGRAM 1 /*!< DTLS */ + +#define MBEDTLS_SSL_MAX_HOST_NAME_LEN 255 /*!< Maximum host name defined in RFC 1035 */ +#define MBEDTLS_SSL_MAX_ALPN_NAME_LEN 255 /*!< Maximum size in bytes of a protocol name in alpn ext., RFC 7301 */ + +#define MBEDTLS_SSL_MAX_ALPN_LIST_LEN 65535 /*!< Maximum size in bytes of list in alpn ext., RFC 7301 */ + +/* RFC 6066 section 4, see also mfl_code_to_length in ssl_tls.c + * NONE must be zero so that memset()ing structure to zero works */ +#define MBEDTLS_SSL_MAX_FRAG_LEN_NONE 0 /*!< don't use this extension */ +#define MBEDTLS_SSL_MAX_FRAG_LEN_512 1 /*!< MaxFragmentLength 2^9 */ +#define MBEDTLS_SSL_MAX_FRAG_LEN_1024 2 /*!< MaxFragmentLength 2^10 */ +#define MBEDTLS_SSL_MAX_FRAG_LEN_2048 3 /*!< MaxFragmentLength 2^11 */ +#define MBEDTLS_SSL_MAX_FRAG_LEN_4096 4 /*!< MaxFragmentLength 2^12 */ +#define MBEDTLS_SSL_MAX_FRAG_LEN_INVALID 5 /*!< first invalid value */ + +#define MBEDTLS_SSL_IS_CLIENT 0 +#define MBEDTLS_SSL_IS_SERVER 1 + +#define MBEDTLS_SSL_EXTENDED_MS_DISABLED 0 +#define MBEDTLS_SSL_EXTENDED_MS_ENABLED 1 + +#define MBEDTLS_SSL_CID_DISABLED 0 +#define MBEDTLS_SSL_CID_ENABLED 1 + +#define MBEDTLS_SSL_ETM_DISABLED 0 +#define MBEDTLS_SSL_ETM_ENABLED 1 + +#define MBEDTLS_SSL_COMPRESS_NULL 0 + +#define MBEDTLS_SSL_VERIFY_NONE 0 +#define MBEDTLS_SSL_VERIFY_OPTIONAL 1 +#define MBEDTLS_SSL_VERIFY_REQUIRED 2 +#define MBEDTLS_SSL_VERIFY_UNSET 3 /* Used only for sni_authmode */ + +#define MBEDTLS_SSL_LEGACY_RENEGOTIATION 0 +#define MBEDTLS_SSL_SECURE_RENEGOTIATION 1 + +#define MBEDTLS_SSL_RENEGOTIATION_DISABLED 0 +#define MBEDTLS_SSL_RENEGOTIATION_ENABLED 1 + +#define MBEDTLS_SSL_ANTI_REPLAY_DISABLED 0 +#define MBEDTLS_SSL_ANTI_REPLAY_ENABLED 1 + +#define MBEDTLS_SSL_RENEGOTIATION_NOT_ENFORCED -1 +#define MBEDTLS_SSL_RENEGO_MAX_RECORDS_DEFAULT 16 + +#define MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION 0 +#define MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION 1 +#define MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE 2 + +#define MBEDTLS_SSL_TRUNC_HMAC_DISABLED 0 +#define MBEDTLS_SSL_TRUNC_HMAC_ENABLED 1 +#define MBEDTLS_SSL_TRUNCATED_HMAC_LEN 10 /* 80 bits, rfc 6066 section 7 */ + +#define MBEDTLS_SSL_SESSION_TICKETS_DISABLED 0 +#define MBEDTLS_SSL_SESSION_TICKETS_ENABLED 1 + +#define MBEDTLS_SSL_PRESET_DEFAULT 0 +#define MBEDTLS_SSL_PRESET_SUITEB 2 + +#define MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED 1 +#define MBEDTLS_SSL_CERT_REQ_CA_LIST_DISABLED 0 + +#define MBEDTLS_SSL_EARLY_DATA_DISABLED 0 +#define MBEDTLS_SSL_EARLY_DATA_ENABLED 1 + +#define MBEDTLS_SSL_DTLS_SRTP_MKI_UNSUPPORTED 0 +#define MBEDTLS_SSL_DTLS_SRTP_MKI_SUPPORTED 1 + +#define MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_CLIENT 1 +#define MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_SERVER 0 + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#if defined(PSA_WANT_ALG_SHA_384) +#define MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN 48 +#elif defined(PSA_WANT_ALG_SHA_256) +#define MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN 32 +#endif +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS */ +/* + * Default range for DTLS retransmission timer value, in milliseconds. + * RFC 6347 4.2.4.1 says from 1 second to 60 seconds. + */ +#define MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MIN 1000 +#define MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MAX 60000 + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ + +/* + * Maximum fragment length in bytes, + * determines the size of each of the two internal I/O buffers. + * + * Note: the RFC defines the default size of SSL / TLS messages. If you + * change the value here, other clients / servers may not be able to + * communicate with you anymore. Only change this value if you control + * both sides of the connection and have it reduced at both sides, or + * if you're using the Max Fragment Length extension and you know all your + * peers are using it too! + */ +#if !defined(MBEDTLS_SSL_IN_CONTENT_LEN) +#define MBEDTLS_SSL_IN_CONTENT_LEN 16384 +#endif + +#if !defined(MBEDTLS_SSL_OUT_CONTENT_LEN) +#define MBEDTLS_SSL_OUT_CONTENT_LEN 16384 +#endif + +/* + * Maximum number of heap-allocated bytes for the purpose of + * DTLS handshake message reassembly and future message buffering. + */ +#if !defined(MBEDTLS_SSL_DTLS_MAX_BUFFERING) +#define MBEDTLS_SSL_DTLS_MAX_BUFFERING 32768 +#endif + +/* + * Maximum length of CIDs for incoming and outgoing messages. + */ +#if !defined(MBEDTLS_SSL_CID_IN_LEN_MAX) +#define MBEDTLS_SSL_CID_IN_LEN_MAX 32 +#endif + +#if !defined(MBEDTLS_SSL_CID_OUT_LEN_MAX) +#define MBEDTLS_SSL_CID_OUT_LEN_MAX 32 +#endif + +#if !defined(MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY) +#define MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY 16 +#endif + +/** \} name SECTION: Module settings */ + +/* + * Default to standard CID mode + */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + !defined(MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT) +#define MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT 0 +#endif + +/* + * Length of the verify data for secure renegotiation + */ +#define MBEDTLS_SSL_VERIFY_DATA_MAX_LEN 12 + +/* + * Signaling ciphersuite values (SCSV) + */ +#define MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO 0xFF /**< renegotiation info ext */ + +/* + * Supported Signature and Hash algorithms (For TLS 1.2) + * RFC 5246 section 7.4.1.4.1 + */ +#define MBEDTLS_SSL_HASH_NONE 0 +#define MBEDTLS_SSL_HASH_MD5 1 +#define MBEDTLS_SSL_HASH_SHA1 2 +#define MBEDTLS_SSL_HASH_SHA224 3 +#define MBEDTLS_SSL_HASH_SHA256 4 +#define MBEDTLS_SSL_HASH_SHA384 5 +#define MBEDTLS_SSL_HASH_SHA512 6 + +#define MBEDTLS_SSL_SIG_ANON 0 +#define MBEDTLS_SSL_SIG_RSA 1 +#define MBEDTLS_SSL_SIG_ECDSA 3 + +/* + * TLS 1.3 signature algorithms + * RFC 8446, Section 4.2.2 + */ + +/* RSASSA-PKCS1-v1_5 algorithms */ +#define MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256 0x0401 +#define MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA384 0x0501 +#define MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA512 0x0601 + +/* ECDSA algorithms */ +#define MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256 0x0403 +#define MBEDTLS_TLS1_3_SIG_ECDSA_SECP384R1_SHA384 0x0503 +#define MBEDTLS_TLS1_3_SIG_ECDSA_SECP521R1_SHA512 0x0603 + +/* RSASSA-PSS algorithms with public key OID rsaEncryption */ +#define MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256 0x0804 +#define MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384 0x0805 +#define MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512 0x0806 + +/* EdDSA algorithms */ +#define MBEDTLS_TLS1_3_SIG_ED25519 0x0807 +#define MBEDTLS_TLS1_3_SIG_ED448 0x0808 + +/* RSASSA-PSS algorithms with public key OID RSASSA-PSS */ +#define MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA256 0x0809 +#define MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA384 0x080A +#define MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA512 0x080B + +/* LEGACY ALGORITHMS */ +#define MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA1 0x0201 +#define MBEDTLS_TLS1_3_SIG_ECDSA_SHA1 0x0203 + +#define MBEDTLS_TLS1_3_SIG_NONE 0x0 + +/* + * Client Certificate Types + * RFC 5246 section 7.4.4 plus RFC 4492 section 5.5 + */ +#define MBEDTLS_SSL_CERT_TYPE_RSA_SIGN 1 +#define MBEDTLS_SSL_CERT_TYPE_ECDSA_SIGN 64 + +/* + * Message, alert and handshake types + */ +#define MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC 20 +#define MBEDTLS_SSL_MSG_ALERT 21 +#define MBEDTLS_SSL_MSG_HANDSHAKE 22 +#define MBEDTLS_SSL_MSG_APPLICATION_DATA 23 +#define MBEDTLS_SSL_MSG_CID 25 + +#define MBEDTLS_SSL_ALERT_LEVEL_WARNING 1 +#define MBEDTLS_SSL_ALERT_LEVEL_FATAL 2 + +#define MBEDTLS_SSL_ALERT_MSG_CLOSE_NOTIFY 0 /* 0x00 */ +#define MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE 10 /* 0x0A */ +#define MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC 20 /* 0x14 */ +#define MBEDTLS_SSL_ALERT_MSG_DECRYPTION_FAILED 21 /* 0x15 */ +#define MBEDTLS_SSL_ALERT_MSG_RECORD_OVERFLOW 22 /* 0x16 */ +#define MBEDTLS_SSL_ALERT_MSG_DECOMPRESSION_FAILURE 30 /* 0x1E */ +#define MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE 40 /* 0x28 */ +#define MBEDTLS_SSL_ALERT_MSG_NO_CERT 41 /* 0x29 */ +#define MBEDTLS_SSL_ALERT_MSG_BAD_CERT 42 /* 0x2A */ +#define MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT 43 /* 0x2B */ +#define MBEDTLS_SSL_ALERT_MSG_CERT_REVOKED 44 /* 0x2C */ +#define MBEDTLS_SSL_ALERT_MSG_CERT_EXPIRED 45 /* 0x2D */ +#define MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN 46 /* 0x2E */ +#define MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER 47 /* 0x2F */ +#define MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA 48 /* 0x30 */ +#define MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED 49 /* 0x31 */ +#define MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR 50 /* 0x32 */ +#define MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR 51 /* 0x33 */ +#define MBEDTLS_SSL_ALERT_MSG_EXPORT_RESTRICTION 60 /* 0x3C */ +#define MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION 70 /* 0x46 */ +#define MBEDTLS_SSL_ALERT_MSG_INSUFFICIENT_SECURITY 71 /* 0x47 */ +#define MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR 80 /* 0x50 */ +#define MBEDTLS_SSL_ALERT_MSG_INAPROPRIATE_FALLBACK 86 /* 0x56 */ +#define MBEDTLS_SSL_ALERT_MSG_USER_CANCELED 90 /* 0x5A */ +#define MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION 100 /* 0x64 */ +#define MBEDTLS_SSL_ALERT_MSG_MISSING_EXTENSION 109 /* 0x6d -- new in TLS 1.3 */ +#define MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT 110 /* 0x6E */ +#define MBEDTLS_SSL_ALERT_MSG_UNRECOGNIZED_NAME 112 /* 0x70 */ +#define MBEDTLS_SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY 115 /* 0x73 */ +#define MBEDTLS_SSL_ALERT_MSG_CERT_REQUIRED 116 /* 0x74 */ +#define MBEDTLS_SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL 120 /* 0x78 */ + +#define MBEDTLS_SSL_HS_HELLO_REQUEST 0 +#define MBEDTLS_SSL_HS_CLIENT_HELLO 1 +#define MBEDTLS_SSL_HS_SERVER_HELLO 2 +#define MBEDTLS_SSL_HS_HELLO_VERIFY_REQUEST 3 +#define MBEDTLS_SSL_HS_NEW_SESSION_TICKET 4 +#define MBEDTLS_SSL_HS_END_OF_EARLY_DATA 5 +#define MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS 8 +#define MBEDTLS_SSL_HS_CERTIFICATE 11 +#define MBEDTLS_SSL_HS_SERVER_KEY_EXCHANGE 12 +#define MBEDTLS_SSL_HS_CERTIFICATE_REQUEST 13 +#define MBEDTLS_SSL_HS_SERVER_HELLO_DONE 14 +#define MBEDTLS_SSL_HS_CERTIFICATE_VERIFY 15 +#define MBEDTLS_SSL_HS_CLIENT_KEY_EXCHANGE 16 +#define MBEDTLS_SSL_HS_FINISHED 20 +#define MBEDTLS_SSL_HS_MESSAGE_HASH 254 + +/* + * TLS extensions + */ +#define MBEDTLS_TLS_EXT_SERVERNAME 0 +#define MBEDTLS_TLS_EXT_SERVERNAME_HOSTNAME 0 + +#define MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH 1 + +#define MBEDTLS_TLS_EXT_TRUNCATED_HMAC 4 +#define MBEDTLS_TLS_EXT_STATUS_REQUEST 5 /* RFC 6066 TLS 1.2 and 1.3 */ + +#define MBEDTLS_TLS_EXT_SUPPORTED_ELLIPTIC_CURVES 10 +#define MBEDTLS_TLS_EXT_SUPPORTED_GROUPS 10 /* RFC 8422,7919 TLS 1.2 and 1.3 */ +#define MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS 11 + +#define MBEDTLS_TLS_EXT_SIG_ALG 13 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_USE_SRTP 14 +#define MBEDTLS_TLS_EXT_HEARTBEAT 15 /* RFC 6520 TLS 1.2 and 1.3 */ +#define MBEDTLS_TLS_EXT_ALPN 16 + +#define MBEDTLS_TLS_EXT_SCT 18 /* RFC 6962 TLS 1.2 and 1.3 */ +#define MBEDTLS_TLS_EXT_CLI_CERT_TYPE 19 /* RFC 7250 TLS 1.2 and 1.3 */ +#define MBEDTLS_TLS_EXT_SERV_CERT_TYPE 20 /* RFC 7250 TLS 1.2 and 1.3 */ +#define MBEDTLS_TLS_EXT_PADDING 21 /* RFC 7685 TLS 1.2 and 1.3 */ +#define MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC 22 /* 0x16 */ +#define MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET 0x0017 /* 23 */ + +#define MBEDTLS_TLS_EXT_RECORD_SIZE_LIMIT 28 /* RFC 8449 (implemented for TLS 1.3 only) */ + +#define MBEDTLS_TLS_EXT_SESSION_TICKET 35 + +#define MBEDTLS_TLS_EXT_PRE_SHARED_KEY 41 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_EARLY_DATA 42 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS 43 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_COOKIE 44 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_PSK_KEY_EXCHANGE_MODES 45 /* RFC 8446 TLS 1.3 */ + +#define MBEDTLS_TLS_EXT_CERT_AUTH 47 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_OID_FILTERS 48 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_POST_HANDSHAKE_AUTH 49 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_SIG_ALG_CERT 50 /* RFC 8446 TLS 1.3 */ +#define MBEDTLS_TLS_EXT_KEY_SHARE 51 /* RFC 8446 TLS 1.3 */ + +#if MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT == 0 +#define MBEDTLS_TLS_EXT_CID 54 /* RFC 9146 DTLS 1.2 CID */ +#else +#define MBEDTLS_TLS_EXT_CID 254 /* Pre-RFC 9146 DTLS 1.2 CID */ +#endif + +#define MBEDTLS_TLS_EXT_ECJPAKE_KKPP 256 /* experimental */ + +#define MBEDTLS_TLS_EXT_RENEGOTIATION_INFO 0xFF01 + +/* + * Size defines + */ +#if !defined(MBEDTLS_PSK_MAX_LEN) +/* + * If the library supports TLS 1.3 tickets and the cipher suite + * TLS1-3-AES-256-GCM-SHA384, set the PSK maximum length to 48 instead of 32. + * That way, the TLS 1.3 client and server are able to resume sessions where + * the cipher suite is TLS1-3-AES-256-GCM-SHA384 (pre-shared keys are 48 + * bytes long in that case). + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_AES_C) && defined(MBEDTLS_GCM_C) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#define MBEDTLS_PSK_MAX_LEN 48 /* 384 bits */ +#else +#define MBEDTLS_PSK_MAX_LEN 32 /* 256 bits */ +#endif +#endif /* !MBEDTLS_PSK_MAX_LEN */ + +/* Dummy type used only for its size */ +union mbedtls_ssl_premaster_secret { +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) + unsigned char _pms_rsa[48]; /* RFC 5246 8.1.1 */ +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) + unsigned char _pms_dhm[MBEDTLS_MPI_MAX_SIZE]; /* RFC 5246 8.1.2 */ +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + unsigned char _pms_ecdh[MBEDTLS_ECP_MAX_BYTES]; /* RFC 4492 5.10 */ +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + unsigned char _pms_psk[4 + 2 * MBEDTLS_PSK_MAX_LEN]; /* RFC 4279 2 */ +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + unsigned char _pms_dhe_psk[4 + MBEDTLS_MPI_MAX_SIZE + + MBEDTLS_PSK_MAX_LEN]; /* RFC 4279 3 */ +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + unsigned char _pms_rsa_psk[52 + MBEDTLS_PSK_MAX_LEN]; /* RFC 4279 4 */ +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + unsigned char _pms_ecdhe_psk[4 + MBEDTLS_ECP_MAX_BYTES + + MBEDTLS_PSK_MAX_LEN]; /* RFC 5489 2 */ +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + unsigned char _pms_ecjpake[32]; /* Thread spec: SHA-256 output */ +#endif +}; + +#define MBEDTLS_PREMASTER_SIZE sizeof(union mbedtls_ssl_premaster_secret) + +#define MBEDTLS_TLS1_3_MD_MAX_SIZE PSA_HASH_MAX_SIZE + + +/* Length in number of bytes of the TLS sequence number */ +#define MBEDTLS_SSL_SEQUENCE_NUMBER_LEN 8 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SSL state machine + */ +typedef enum { + MBEDTLS_SSL_HELLO_REQUEST, + MBEDTLS_SSL_CLIENT_HELLO, + MBEDTLS_SSL_SERVER_HELLO, + MBEDTLS_SSL_SERVER_CERTIFICATE, + MBEDTLS_SSL_SERVER_KEY_EXCHANGE, + MBEDTLS_SSL_CERTIFICATE_REQUEST, + MBEDTLS_SSL_SERVER_HELLO_DONE, + MBEDTLS_SSL_CLIENT_CERTIFICATE, + MBEDTLS_SSL_CLIENT_KEY_EXCHANGE, + MBEDTLS_SSL_CERTIFICATE_VERIFY, + MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC, + MBEDTLS_SSL_CLIENT_FINISHED, + MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC, + MBEDTLS_SSL_SERVER_FINISHED, + MBEDTLS_SSL_FLUSH_BUFFERS, + MBEDTLS_SSL_HANDSHAKE_WRAPUP, + + MBEDTLS_SSL_NEW_SESSION_TICKET, + MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT, + MBEDTLS_SSL_HELLO_RETRY_REQUEST, + MBEDTLS_SSL_ENCRYPTED_EXTENSIONS, + MBEDTLS_SSL_END_OF_EARLY_DATA, + MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY, + MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED, + MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO, + MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO, + MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO, + MBEDTLS_SSL_SERVER_CCS_AFTER_HELLO_RETRY_REQUEST, + MBEDTLS_SSL_HANDSHAKE_OVER, + MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET, + MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET_FLUSH, +} +mbedtls_ssl_states; + +/** + * \brief Callback type: send data on the network. + * + * \note That callback may be either blocking or non-blocking. + * + * \param ctx Context for the send callback (typically a file descriptor) + * \param buf Buffer holding the data to send + * \param len Length of the data to send + * + * \return The callback must return the number of bytes sent if any, + * or a non-zero error code. + * If performing non-blocking I/O, \c MBEDTLS_ERR_SSL_WANT_WRITE + * must be returned when the operation would block. + * + * \note The callback is allowed to send fewer bytes than requested. + * It must always return the number of bytes actually sent. + */ +typedef int mbedtls_ssl_send_t(void *ctx, + const unsigned char *buf, + size_t len); + +/** + * \brief Callback type: receive data from the network. + * + * \note That callback may be either blocking or non-blocking. + * + * \param ctx Context for the receive callback (typically a file + * descriptor) + * \param buf Buffer to write the received data to + * \param len Length of the receive buffer + * + * \returns If data has been received, the positive number of bytes received. + * \returns \c 0 if the connection has been closed. + * \returns If performing non-blocking I/O, \c MBEDTLS_ERR_SSL_WANT_READ + * must be returned when the operation would block. + * \returns Another negative error code on other kinds of failures. + * + * \note The callback may receive fewer bytes than the length of the + * buffer. It must always return the number of bytes actually + * received and written to the buffer. + */ +typedef int mbedtls_ssl_recv_t(void *ctx, + unsigned char *buf, + size_t len); + +/** + * \brief Callback type: receive data from the network, with timeout + * + * \note That callback must block until data is received, or the + * timeout delay expires, or the operation is interrupted by a + * signal. + * + * \param ctx Context for the receive callback (typically a file descriptor) + * \param buf Buffer to write the received data to + * \param len Length of the receive buffer + * \param timeout Maximum number of milliseconds to wait for data + * 0 means no timeout (potentially waiting forever) + * + * \return The callback must return the number of bytes received, + * or a non-zero error code: + * \c MBEDTLS_ERR_SSL_TIMEOUT if the operation timed out, + * \c MBEDTLS_ERR_SSL_WANT_READ if interrupted by a signal. + * + * \note The callback may receive fewer bytes than the length of the + * buffer. It must always return the number of bytes actually + * received and written to the buffer. + */ +typedef int mbedtls_ssl_recv_timeout_t(void *ctx, + unsigned char *buf, + size_t len, + uint32_t timeout); +/** + * \brief Callback type: set a pair of timers/delays to watch + * + * \param ctx Context pointer + * \param int_ms Intermediate delay in milliseconds + * \param fin_ms Final delay in milliseconds + * 0 cancels the current timer. + * + * \note This callback must at least store the necessary information + * for the associated \c mbedtls_ssl_get_timer_t callback to + * return correct information. + * + * \note If using an event-driven style of programming, an event must + * be generated when the final delay is passed. The event must + * cause a call to \c mbedtls_ssl_handshake() with the proper + * SSL context to be scheduled. Care must be taken to ensure + * that at most one such call happens at a time. + * + * \note Only one timer at a time must be running. Calling this + * function while a timer is running must cancel it. Cancelled + * timers must not generate any event. + */ +typedef void mbedtls_ssl_set_timer_t(void *ctx, + uint32_t int_ms, + uint32_t fin_ms); + +/** + * \brief Callback type: get status of timers/delays + * + * \param ctx Context pointer + * + * \return This callback must return: + * -1 if cancelled (fin_ms == 0), + * 0 if none of the delays have passed, + * 1 if only the intermediate delay has passed, + * 2 if the final delay has passed. + */ +typedef int mbedtls_ssl_get_timer_t(void *ctx); + +/* Defined below */ +typedef struct mbedtls_ssl_session mbedtls_ssl_session; +typedef struct mbedtls_ssl_context mbedtls_ssl_context; +typedef struct mbedtls_ssl_config mbedtls_ssl_config; + +/* Defined in library/ssl_misc.h */ +typedef struct mbedtls_ssl_transform mbedtls_ssl_transform; +typedef struct mbedtls_ssl_handshake_params mbedtls_ssl_handshake_params; +typedef struct mbedtls_ssl_sig_hash_set_t mbedtls_ssl_sig_hash_set_t; +#if defined(MBEDTLS_X509_CRT_PARSE_C) +typedef struct mbedtls_ssl_key_cert mbedtls_ssl_key_cert; +#endif +#if defined(MBEDTLS_SSL_PROTO_DTLS) +typedef struct mbedtls_ssl_flight_item mbedtls_ssl_flight_item; +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_PSK_RESUMPTION \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK /* 1U << 0 */ +#define MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_PSK_EPHEMERAL_RESUMPTION \ + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL /* 1U << 2 */ +#define MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_EARLY_DATA (1U << 3) + +#define MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK \ + (MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_PSK_RESUMPTION | \ + MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_PSK_EPHEMERAL_RESUMPTION | \ + MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_EARLY_DATA) +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS */ + +/** + * \brief Callback type: server-side session cache getter + * + * The session cache is logically a key value store, with + * keys being session IDs and values being instances of + * mbedtls_ssl_session. + * + * This callback retrieves an entry in this key-value store. + * + * \param data The address of the session cache structure to query. + * \param session_id The buffer holding the session ID to query. + * \param session_id_len The length of \p session_id in Bytes. + * \param session The address of the session structure to populate. + * It is initialized with mbdtls_ssl_session_init(), + * and the callback must always leave it in a state + * where it can safely be freed via + * mbedtls_ssl_session_free() independent of the + * return code of this function. + * + * \return \c 0 on success + * \return A non-zero return value on failure. + * + */ +typedef int mbedtls_ssl_cache_get_t(void *data, + unsigned char const *session_id, + size_t session_id_len, + mbedtls_ssl_session *session); +/** + * \brief Callback type: server-side session cache setter + * + * The session cache is logically a key value store, with + * keys being session IDs and values being instances of + * mbedtls_ssl_session. + * + * This callback sets an entry in this key-value store. + * + * \param data The address of the session cache structure to modify. + * \param session_id The buffer holding the session ID to query. + * \param session_id_len The length of \p session_id in Bytes. + * \param session The address of the session to be stored in the + * session cache. + * + * \return \c 0 on success + * \return A non-zero return value on failure. + */ +typedef int mbedtls_ssl_cache_set_t(void *data, + unsigned char const *session_id, + size_t session_id_len, + const mbedtls_ssl_session *session); + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * \brief Callback type: start external signature operation. + * + * This callback is called during an SSL handshake to start + * a signature decryption operation using an + * external processor. The parameter \p cert contains + * the public key; it is up to the callback function to + * determine how to access the associated private key. + * + * This function typically sends or enqueues a request, and + * does not wait for the operation to complete. This allows + * the handshake step to be non-blocking. + * + * The parameters \p ssl and \p cert are guaranteed to remain + * valid throughout the handshake. On the other hand, this + * function must save the contents of \p hash if the value + * is needed for later processing, because the \p hash buffer + * is no longer valid after this function returns. + * + * This function may call mbedtls_ssl_set_async_operation_data() + * to store an operation context for later retrieval + * by the resume or cancel callback. + * + * \note For RSA signatures, this function must produce output + * that is consistent with PKCS#1 v1.5 in the same way as + * mbedtls_rsa_pkcs1_sign(). Before the private key operation, + * apply the padding steps described in RFC 8017, section 9.2 + * "EMSA-PKCS1-v1_5" as follows. + * - If \p md_alg is #MBEDTLS_MD_NONE, apply the PKCS#1 v1.5 + * encoding, treating \p hash as the DigestInfo to be + * padded. In other words, apply EMSA-PKCS1-v1_5 starting + * from step 3, with `T = hash` and `tLen = hash_len`. + * - If `md_alg != MBEDTLS_MD_NONE`, apply the PKCS#1 v1.5 + * encoding, treating \p hash as the hash to be encoded and + * padded. In other words, apply EMSA-PKCS1-v1_5 starting + * from step 2, with `digestAlgorithm` obtained by calling + * mbedtls_oid_get_oid_by_md() on \p md_alg. + * + * \note For ECDSA signatures, the output format is the DER encoding + * `Ecdsa-Sig-Value` defined in + * [RFC 4492 section 5.4](https://tools.ietf.org/html/rfc4492#section-5.4). + * + * \param ssl The SSL connection instance. It should not be + * modified other than via + * mbedtls_ssl_set_async_operation_data(). + * \param cert Certificate containing the public key. + * In simple cases, this is one of the pointers passed to + * mbedtls_ssl_conf_own_cert() when configuring the SSL + * connection. However, if other callbacks are used, this + * property may not hold. For example, if an SNI callback + * is registered with mbedtls_ssl_conf_sni(), then + * this callback determines what certificate is used. + * \param md_alg Hash algorithm. + * \param hash Buffer containing the hash. This buffer is + * no longer valid when the function returns. + * \param hash_len Size of the \c hash buffer in bytes. + * + * \return 0 if the operation was started successfully and the SSL + * stack should call the resume callback immediately. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if the operation + * was started successfully and the SSL stack should return + * immediately without calling the resume callback yet. + * \return #MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH if the external + * processor does not support this key. The SSL stack will + * use the private key object instead. + * \return Any other error indicates a fatal failure and is + * propagated up the call chain. The callback should + * use \c MBEDTLS_ERR_PK_xxx error codes, and must not + * use \c MBEDTLS_ERR_SSL_xxx error codes except as + * directed in the documentation of this callback. + */ +typedef int mbedtls_ssl_async_sign_t(mbedtls_ssl_context *ssl, + mbedtls_x509_crt *cert, + mbedtls_md_type_t md_alg, + const unsigned char *hash, + size_t hash_len); + +/** + * \brief Callback type: start external decryption operation. + * + * This callback is called during an SSL handshake to start + * an RSA decryption operation using an + * external processor. The parameter \p cert contains + * the public key; it is up to the callback function to + * determine how to access the associated private key. + * + * This function typically sends or enqueues a request, and + * does not wait for the operation to complete. This allows + * the handshake step to be non-blocking. + * + * The parameters \p ssl and \p cert are guaranteed to remain + * valid throughout the handshake. On the other hand, this + * function must save the contents of \p input if the value + * is needed for later processing, because the \p input buffer + * is no longer valid after this function returns. + * + * This function may call mbedtls_ssl_set_async_operation_data() + * to store an operation context for later retrieval + * by the resume or cancel callback. + * + * \warning RSA decryption as used in TLS is subject to a potential + * timing side channel attack first discovered by Bleichenbacher + * in 1998. This attack can be remotely exploitable + * in practice. To avoid this attack, you must ensure that + * if the callback performs an RSA decryption, the time it + * takes to execute and return the result does not depend + * on whether the RSA decryption succeeded or reported + * invalid padding. + * + * \param ssl The SSL connection instance. It should not be + * modified other than via + * mbedtls_ssl_set_async_operation_data(). + * \param cert Certificate containing the public key. + * In simple cases, this is one of the pointers passed to + * mbedtls_ssl_conf_own_cert() when configuring the SSL + * connection. However, if other callbacks are used, this + * property may not hold. For example, if an SNI callback + * is registered with mbedtls_ssl_conf_sni(), then + * this callback determines what certificate is used. + * \param input Buffer containing the input ciphertext. This buffer + * is no longer valid when the function returns. + * \param input_len Size of the \p input buffer in bytes. + * + * \return 0 if the operation was started successfully and the SSL + * stack should call the resume callback immediately. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if the operation + * was started successfully and the SSL stack should return + * immediately without calling the resume callback yet. + * \return #MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH if the external + * processor does not support this key. The SSL stack will + * use the private key object instead. + * \return Any other error indicates a fatal failure and is + * propagated up the call chain. The callback should + * use \c MBEDTLS_ERR_PK_xxx error codes, and must not + * use \c MBEDTLS_ERR_SSL_xxx error codes except as + * directed in the documentation of this callback. + */ +typedef int mbedtls_ssl_async_decrypt_t(mbedtls_ssl_context *ssl, + mbedtls_x509_crt *cert, + const unsigned char *input, + size_t input_len); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +/** + * \brief Callback type: resume external operation. + * + * This callback is called during an SSL handshake to resume + * an external operation started by the + * ::mbedtls_ssl_async_sign_t or + * ::mbedtls_ssl_async_decrypt_t callback. + * + * This function typically checks the status of a pending + * request or causes the request queue to make progress, and + * does not wait for the operation to complete. This allows + * the handshake step to be non-blocking. + * + * This function may call mbedtls_ssl_get_async_operation_data() + * to retrieve an operation context set by the start callback. + * It may call mbedtls_ssl_set_async_operation_data() to modify + * this context. + * + * Note that when this function returns a status other than + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS, it must free any + * resources associated with the operation. + * + * \param ssl The SSL connection instance. It should not be + * modified other than via + * mbedtls_ssl_set_async_operation_data(). + * \param output Buffer containing the output (signature or decrypted + * data) on success. + * \param output_len On success, number of bytes written to \p output. + * \param output_size Size of the \p output buffer in bytes. + * + * \return 0 if output of the operation is available in the + * \p output buffer. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if the operation + * is still in progress. Subsequent requests for progress + * on the SSL connection will call the resume callback + * again. + * \return Any other error means that the operation is aborted. + * The SSL handshake is aborted. The callback should + * use \c MBEDTLS_ERR_PK_xxx error codes, and must not + * use \c MBEDTLS_ERR_SSL_xxx error codes except as + * directed in the documentation of this callback. + */ +typedef int mbedtls_ssl_async_resume_t(mbedtls_ssl_context *ssl, + unsigned char *output, + size_t *output_len, + size_t output_size); + +/** + * \brief Callback type: cancel external operation. + * + * This callback is called if an SSL connection is closed + * while an asynchronous operation is in progress. Note that + * this callback is not called if the + * ::mbedtls_ssl_async_resume_t callback has run and has + * returned a value other than + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS, since in that case + * the asynchronous operation has already completed. + * + * This function may call mbedtls_ssl_get_async_operation_data() + * to retrieve an operation context set by the start callback. + * + * \param ssl The SSL connection instance. It should not be + * modified. + */ +typedef void mbedtls_ssl_async_cancel_t(mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) && \ + !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +#define MBEDTLS_SSL_PEER_CERT_DIGEST_MAX_LEN 48 +#if defined(MBEDTLS_SHA256_C) +#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE MBEDTLS_MD_SHA256 +#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN 32 +#elif defined(MBEDTLS_SHA384_C) +#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE MBEDTLS_MD_SHA384 +#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN 48 +#elif defined(MBEDTLS_SHA1_C) +#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE MBEDTLS_MD_SHA1 +#define MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN 20 +#else +/* This is already checked in check_config.h, but be sure. */ +#error "Bad configuration - need SHA-1, SHA-256 or SHA-512 enabled to compute digest of peer CRT." +#endif +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED && + !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + +typedef struct { + unsigned char client_application_traffic_secret_N[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char server_application_traffic_secret_N[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char exporter_master_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char resumption_master_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; +} mbedtls_ssl_tls13_application_secrets; + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + +#define MBEDTLS_TLS_SRTP_MAX_MKI_LENGTH 255 +#define MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH 4 +/* + * For code readability use a typedef for DTLS-SRTP profiles + * + * Use_srtp extension protection profiles values as defined in + * http://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml + * + * Reminder: if this list is expanded mbedtls_ssl_check_srtp_profile_value + * must be updated too. + */ +#define MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80 ((uint16_t) 0x0001) +#define MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_32 ((uint16_t) 0x0002) +#define MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_80 ((uint16_t) 0x0005) +#define MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_32 ((uint16_t) 0x0006) +/* This one is not iana defined, but for code readability. */ +#define MBEDTLS_TLS_SRTP_UNSET ((uint16_t) 0x0000) + +typedef uint16_t mbedtls_ssl_srtp_profile; + +typedef struct mbedtls_dtls_srtp_info_t { + /*! The SRTP profile that was negotiated. */ + mbedtls_ssl_srtp_profile MBEDTLS_PRIVATE(chosen_dtls_srtp_profile); + /*! The length of mki_value. */ + uint16_t MBEDTLS_PRIVATE(mki_len); + /*! The mki_value used, with max size of 256 bytes. */ + unsigned char MBEDTLS_PRIVATE(mki_value)[MBEDTLS_TLS_SRTP_MAX_MKI_LENGTH]; +} +mbedtls_dtls_srtp_info; + +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + +/** Human-friendly representation of the (D)TLS protocol version. */ +typedef enum { + MBEDTLS_SSL_VERSION_UNKNOWN, /*!< Context not in use or version not yet negotiated. */ + MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303, /*!< (D)TLS 1.2 */ + MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304, /*!< (D)TLS 1.3 */ +} mbedtls_ssl_protocol_version; + +/* + * This structure is used for storing current session data. + * + * Note: when changing this definition, we need to check and update: + * - in tests/suites/test_suite_ssl.function: + * ssl_populate_session() and ssl_serialize_session_save_load() + * - in library/ssl_tls.c: + * mbedtls_ssl_session_init() and mbedtls_ssl_session_free() + * mbedtls_ssl_session_save() and ssl_session_load() + * ssl_session_copy() + */ +struct mbedtls_ssl_session { +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + unsigned char MBEDTLS_PRIVATE(mfl_code); /*!< MaxFragmentLength negotiated by peer */ +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + + unsigned char MBEDTLS_PRIVATE(exported); + + /** TLS version negotiated in the session. Used if and when renegotiating + * or resuming a session instead of the configured minor TLS version. + */ + mbedtls_ssl_protocol_version MBEDTLS_PRIVATE(tls_version); + +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t MBEDTLS_PRIVATE(start); /*!< starting time */ +#endif + int MBEDTLS_PRIVATE(ciphersuite); /*!< chosen ciphersuite */ + size_t MBEDTLS_PRIVATE(id_len); /*!< session id length */ + unsigned char MBEDTLS_PRIVATE(id)[32]; /*!< session identifier */ + unsigned char MBEDTLS_PRIVATE(master)[48]; /*!< the master secret */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + mbedtls_x509_crt *MBEDTLS_PRIVATE(peer_cert); /*!< peer X.509 cert chain */ +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + /*! The digest of the peer's end-CRT. This must be kept to detect CRT + * changes during renegotiation, mitigating the triple handshake attack. */ + unsigned char *MBEDTLS_PRIVATE(peer_cert_digest); + size_t MBEDTLS_PRIVATE(peer_cert_digest_len); + mbedtls_md_type_t MBEDTLS_PRIVATE(peer_cert_digest_type); +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + uint32_t MBEDTLS_PRIVATE(verify_result); /*!< verification result */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + unsigned char *MBEDTLS_PRIVATE(ticket); /*!< RFC 5077 session ticket */ + size_t MBEDTLS_PRIVATE(ticket_len); /*!< session ticket length */ + uint32_t MBEDTLS_PRIVATE(ticket_lifetime); /*!< ticket lifetime hint */ +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) + uint8_t MBEDTLS_PRIVATE(endpoint); /*!< 0: client, 1: server */ + uint8_t MBEDTLS_PRIVATE(ticket_flags); /*!< Ticket flags */ + uint32_t MBEDTLS_PRIVATE(ticket_age_add); /*!< Randomly generated value used to obscure the age of the ticket */ + uint8_t MBEDTLS_PRIVATE(resumption_key_len); /*!< resumption_key length */ + unsigned char MBEDTLS_PRIVATE(resumption_key)[MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN]; + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && defined(MBEDTLS_SSL_CLI_C) + char *MBEDTLS_PRIVATE(hostname); /*!< host name binded with tickets */ +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION && MBEDTLS_SSL_CLI_C */ + +#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_CLI_C) + mbedtls_time_t MBEDTLS_PRIVATE(ticket_received); /*!< time ticket was received */ +#endif /* MBEDTLS_HAVE_TIME && MBEDTLS_SSL_CLI_C */ + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + int MBEDTLS_PRIVATE(encrypt_then_mac); /*!< flag for EtM activation */ +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_tls13_application_secrets MBEDTLS_PRIVATE(app_secrets); +#endif +}; + +/* + * Identifiers for PRFs used in various versions of TLS. + */ +typedef enum { + MBEDTLS_SSL_TLS_PRF_NONE, + MBEDTLS_SSL_TLS_PRF_SHA384, + MBEDTLS_SSL_TLS_PRF_SHA256, + MBEDTLS_SSL_HKDF_EXPAND_SHA384, + MBEDTLS_SSL_HKDF_EXPAND_SHA256 +} +mbedtls_tls_prf_types; + +typedef enum { + MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET = 0, +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_EARLY_SECRET, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_EARLY_EXPORTER_SECRET, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_HANDSHAKE_TRAFFIC_SECRET, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_HANDSHAKE_TRAFFIC_SECRET, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_APPLICATION_TRAFFIC_SECRET, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_APPLICATION_TRAFFIC_SECRET, +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ +} mbedtls_ssl_key_export_type; + +/** + * \brief Callback type: Export key alongside random values for + * session identification, and PRF for + * implementation of TLS key exporters. + * + * \param p_expkey Context for the callback. + * \param type The type of the key that is being exported. + * \param secret The address of the buffer holding the secret + * that's being exporterd. + * \param secret_len The length of \p secret in bytes. + * \param client_random The client random bytes. + * \param server_random The server random bytes. + * \param tls_prf_type The identifier for the PRF used in the handshake + * to which the key belongs. + */ +typedef void mbedtls_ssl_export_keys_t(void *p_expkey, + mbedtls_ssl_key_export_type type, + const unsigned char *secret, + size_t secret_len, + const unsigned char client_random[32], + const unsigned char server_random[32], + mbedtls_tls_prf_types tls_prf_type); + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Callback type: generic handshake callback + * + * \note Callbacks may use user_data funcs to set/get app user data. + * See \c mbedtls_ssl_get_user_data_p() + * \c mbedtls_ssl_get_user_data_n() + * \c mbedtls_ssl_conf_get_user_data_p() + * \c mbedtls_ssl_conf_get_user_data_n() + * + * \param ssl \c mbedtls_ssl_context on which the callback is run + * + * \return The return value of the callback is 0 if successful, + * or a specific MBEDTLS_ERR_XXX code, which will cause + * the handshake to be aborted. + */ +typedef int (*mbedtls_ssl_hs_cb_t)(mbedtls_ssl_context *ssl); +#endif + +/* A type for storing user data in a library structure. + * + * The representation of type may change in future versions of the library. + * Only the behaviors guaranteed by documented accessor functions are + * guaranteed to remain stable. + */ +typedef union { + uintptr_t n; /* typically a handle to an associated object */ + void *p; /* typically a pointer to extra data */ +} mbedtls_ssl_user_data_t; + +/** + * SSL/TLS configuration to be shared between mbedtls_ssl_context structures. + */ +struct mbedtls_ssl_config { + /* Group items mostly by size. This helps to reduce memory wasted to + * padding. It also helps to keep smaller fields early in the structure, + * so that elements tend to be in the 128-element direct access window + * on Arm Thumb, which reduces the code size. */ + + mbedtls_ssl_protocol_version MBEDTLS_PRIVATE(max_tls_version); /*!< max. TLS version used */ + mbedtls_ssl_protocol_version MBEDTLS_PRIVATE(min_tls_version); /*!< min. TLS version used */ + + /* + * Flags (could be bit-fields to save RAM, but separate bytes make + * the code smaller on architectures with an instruction for direct + * byte access). + */ + + uint8_t MBEDTLS_PRIVATE(endpoint); /*!< 0: client, 1: server */ + uint8_t MBEDTLS_PRIVATE(transport); /*!< 0: stream (TLS), 1: datagram (DTLS) */ + uint8_t MBEDTLS_PRIVATE(authmode); /*!< MBEDTLS_SSL_VERIFY_XXX */ + /* needed even with renego disabled for LEGACY_BREAK_HANDSHAKE */ + uint8_t MBEDTLS_PRIVATE(allow_legacy_renegotiation); /*!< MBEDTLS_LEGACY_XXX */ +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + uint8_t MBEDTLS_PRIVATE(mfl_code); /*!< desired fragment length indicator + (MBEDTLS_SSL_MAX_FRAG_LEN_XXX) */ +#endif +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + uint8_t MBEDTLS_PRIVATE(encrypt_then_mac); /*!< negotiate encrypt-then-mac? */ +#endif +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + uint8_t MBEDTLS_PRIVATE(extended_ms); /*!< negotiate extended master secret? */ +#endif +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + uint8_t MBEDTLS_PRIVATE(anti_replay); /*!< detect and prevent replay? */ +#endif +#if defined(MBEDTLS_SSL_RENEGOTIATION) + uint8_t MBEDTLS_PRIVATE(disable_renegotiation); /*!< disable renegotiation? */ +#endif +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_CLI_C) + uint8_t MBEDTLS_PRIVATE(session_tickets); /*!< use session tickets? */ +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SRV_C) && \ + defined(MBEDTLS_SSL_PROTO_TLS1_3) + uint16_t MBEDTLS_PRIVATE(new_session_tickets_count); /*!< number of NewSessionTicket */ +#endif + +#if defined(MBEDTLS_SSL_SRV_C) + uint8_t MBEDTLS_PRIVATE(cert_req_ca_list); /*!< enable sending CA list in + Certificate Request messages? */ + uint8_t MBEDTLS_PRIVATE(respect_cli_pref); /*!< pick the ciphersuite according to + the client's preferences rather + than ours? */ +#endif +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + uint8_t MBEDTLS_PRIVATE(ignore_unexpected_cid); /*!< Should DTLS record with + * unexpected CID + * lead to failure? */ +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ +#if defined(MBEDTLS_SSL_DTLS_SRTP) + uint8_t MBEDTLS_PRIVATE(dtls_srtp_mki_support); /* support having mki_value + in the use_srtp extension? */ +#endif + + /* + * Pointers + */ + + /** Allowed ciphersuites for (D)TLS 1.2 (0-terminated) */ + const int *MBEDTLS_PRIVATE(ciphersuite_list); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /** Allowed TLS 1.3 key exchange modes. */ + int MBEDTLS_PRIVATE(tls13_kex_modes); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + /** Callback for printing debug output */ + void(*MBEDTLS_PRIVATE(f_dbg))(void *, int, const char *, int, const char *); + void *MBEDTLS_PRIVATE(p_dbg); /*!< context for the debug function */ + + /** Callback for getting (pseudo-)random numbers */ + int(*MBEDTLS_PRIVATE(f_rng))(void *, unsigned char *, size_t); + void *MBEDTLS_PRIVATE(p_rng); /*!< context for the RNG function */ + + /** Callback to retrieve a session from the cache */ + mbedtls_ssl_cache_get_t *MBEDTLS_PRIVATE(f_get_cache); + /** Callback to store a session into the cache */ + mbedtls_ssl_cache_set_t *MBEDTLS_PRIVATE(f_set_cache); + void *MBEDTLS_PRIVATE(p_cache); /*!< context for cache callbacks */ + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + /** Callback for setting cert according to SNI extension */ + int(*MBEDTLS_PRIVATE(f_sni))(void *, mbedtls_ssl_context *, const unsigned char *, size_t); + void *MBEDTLS_PRIVATE(p_sni); /*!< context for SNI callback */ +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + /** Callback to customize X.509 certificate chain verification */ + int(*MBEDTLS_PRIVATE(f_vrfy))(void *, mbedtls_x509_crt *, int, uint32_t *); + void *MBEDTLS_PRIVATE(p_vrfy); /*!< context for X.509 verify calllback */ +#endif + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) +#if defined(MBEDTLS_SSL_SRV_C) + /** Callback to retrieve PSK key from identity */ + int(*MBEDTLS_PRIVATE(f_psk))(void *, mbedtls_ssl_context *, const unsigned char *, size_t); + void *MBEDTLS_PRIVATE(p_psk); /*!< context for PSK callback */ +#endif +#endif + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) + /** Callback to create & write a cookie for ClientHello verification */ + int(*MBEDTLS_PRIVATE(f_cookie_write))(void *, unsigned char **, unsigned char *, + const unsigned char *, size_t); + /** Callback to verify validity of a ClientHello cookie */ + int(*MBEDTLS_PRIVATE(f_cookie_check))(void *, const unsigned char *, size_t, + const unsigned char *, size_t); + void *MBEDTLS_PRIVATE(p_cookie); /*!< context for the cookie callbacks */ +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_SRV_C) + /** Callback to create & write a session ticket */ + int(*MBEDTLS_PRIVATE(f_ticket_write))(void *, const mbedtls_ssl_session *, + unsigned char *, const unsigned char *, size_t *, + uint32_t *); + /** Callback to parse a session ticket into a session structure */ + int(*MBEDTLS_PRIVATE(f_ticket_parse))(void *, mbedtls_ssl_session *, unsigned char *, size_t); + void *MBEDTLS_PRIVATE(p_ticket); /*!< context for the ticket callbacks */ +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_SRV_C */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + size_t MBEDTLS_PRIVATE(cid_len); /*!< The length of CIDs for incoming DTLS records. */ +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + const mbedtls_x509_crt_profile *MBEDTLS_PRIVATE(cert_profile); /*!< verification profile */ + mbedtls_ssl_key_cert *MBEDTLS_PRIVATE(key_cert); /*!< own certificate/key pair(s) */ + mbedtls_x509_crt *MBEDTLS_PRIVATE(ca_chain); /*!< trusted CAs */ + mbedtls_x509_crl *MBEDTLS_PRIVATE(ca_crl); /*!< trusted CAs CRLs */ +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + mbedtls_x509_crt_ca_cb_t MBEDTLS_PRIVATE(f_ca_cb); + void *MBEDTLS_PRIVATE(p_ca_cb); +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +#if defined(MBEDTLS_X509_CRT_PARSE_C) + mbedtls_ssl_async_sign_t *MBEDTLS_PRIVATE(f_async_sign_start); /*!< start asynchronous signature operation */ + mbedtls_ssl_async_decrypt_t *MBEDTLS_PRIVATE(f_async_decrypt_start); /*!< start asynchronous decryption operation */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + mbedtls_ssl_async_resume_t *MBEDTLS_PRIVATE(f_async_resume); /*!< resume asynchronous operation */ + mbedtls_ssl_async_cancel_t *MBEDTLS_PRIVATE(f_async_cancel); /*!< cancel asynchronous operation */ + void *MBEDTLS_PRIVATE(p_async_config_data); /*!< Configuration data set by mbedtls_ssl_conf_async_private_cb(). */ +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + const int *MBEDTLS_PRIVATE(sig_hashes); /*!< allowed signature hashes */ +#endif + const uint16_t *MBEDTLS_PRIVATE(sig_algs); /*!< allowed signature algorithms */ +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_ECP_C) && !defined(MBEDTLS_DEPRECATED_REMOVED) + const mbedtls_ecp_group_id *MBEDTLS_PRIVATE(curve_list); /*!< allowed curves */ +#endif + + const uint16_t *MBEDTLS_PRIVATE(group_list); /*!< allowed IANA NamedGroups */ + +#if defined(MBEDTLS_DHM_C) + mbedtls_mpi MBEDTLS_PRIVATE(dhm_P); /*!< prime modulus for DHM */ + mbedtls_mpi MBEDTLS_PRIVATE(dhm_G); /*!< generator for DHM */ +#endif + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_svc_key_id_t MBEDTLS_PRIVATE(psk_opaque); /*!< PSA key slot holding opaque PSK. This field + * should only be set via + * mbedtls_ssl_conf_psk_opaque(). + * If either no PSK or a raw PSK have been + * configured, this has value \c 0. + */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + unsigned char *MBEDTLS_PRIVATE(psk); /*!< The raw pre-shared key. This field should + * only be set via mbedtls_ssl_conf_psk(). + * If either no PSK or an opaque PSK + * have been configured, this has value NULL. */ + size_t MBEDTLS_PRIVATE(psk_len); /*!< The length of the raw pre-shared key. + * This field should only be set via + * mbedtls_ssl_conf_psk(). + * Its value is non-zero if and only if + * \c psk is not \c NULL. */ + + unsigned char *MBEDTLS_PRIVATE(psk_identity); /*!< The PSK identity for PSK negotiation. + * This field should only be set via + * mbedtls_ssl_conf_psk(). + * This is set if and only if either + * \c psk or \c psk_opaque are set. */ + size_t MBEDTLS_PRIVATE(psk_identity_len);/*!< The length of PSK identity. + * This field should only be set via + * mbedtls_ssl_conf_psk(). + * Its value is non-zero if and only if + * \c psk is not \c NULL or \c psk_opaque + * is not \c 0. */ +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ + +#if defined(MBEDTLS_SSL_EARLY_DATA) + int MBEDTLS_PRIVATE(early_data_enabled); /*!< Early data enablement: + * - MBEDTLS_SSL_EARLY_DATA_DISABLED, + * - MBEDTLS_SSL_EARLY_DATA_ENABLED */ + +#if defined(MBEDTLS_SSL_SRV_C) + /* The maximum amount of 0-RTT data. RFC 8446 section 4.6.1 */ + uint32_t MBEDTLS_PRIVATE(max_early_data_size); +#endif /* MBEDTLS_SSL_SRV_C */ + +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +#if defined(MBEDTLS_SSL_ALPN) + const char **MBEDTLS_PRIVATE(alpn_list); /*!< ordered list of protocols */ +#endif + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + /*! ordered list of supported srtp profile */ + const mbedtls_ssl_srtp_profile *MBEDTLS_PRIVATE(dtls_srtp_profile_list); + /*! number of supported profiles */ + size_t MBEDTLS_PRIVATE(dtls_srtp_profile_list_len); +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + + /* + * Numerical settings (int) + */ + + uint32_t MBEDTLS_PRIVATE(read_timeout); /*!< timeout for mbedtls_ssl_read (ms) */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + uint32_t MBEDTLS_PRIVATE(hs_timeout_min); /*!< initial value of the handshake + retransmission timeout (ms) */ + uint32_t MBEDTLS_PRIVATE(hs_timeout_max); /*!< maximum value of the handshake + retransmission timeout (ms) */ +#endif + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + int MBEDTLS_PRIVATE(renego_max_records); /*!< grace period for renegotiation */ + unsigned char MBEDTLS_PRIVATE(renego_period)[8]; /*!< value of the record counters + that triggers renegotiation */ +#endif + + unsigned int MBEDTLS_PRIVATE(badmac_limit); /*!< limit of records with a bad MAC */ + +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_CLI_C) + unsigned int MBEDTLS_PRIVATE(dhm_min_bitlen); /*!< min. bit length of the DHM prime */ +#endif + + /** User data pointer or handle. + * + * The library sets this to \p 0 when creating a context and does not + * access it afterwards. + */ + mbedtls_ssl_user_data_t MBEDTLS_PRIVATE(user_data); + +#if defined(MBEDTLS_SSL_SRV_C) + mbedtls_ssl_hs_cb_t MBEDTLS_PRIVATE(f_cert_cb); /*!< certificate selection callback */ +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) + const mbedtls_x509_crt *MBEDTLS_PRIVATE(dn_hints);/*!< acceptable client cert issuers */ +#endif +}; + +struct mbedtls_ssl_context { + const mbedtls_ssl_config *MBEDTLS_PRIVATE(conf); /*!< configuration information */ + + /* + * Miscellaneous + */ + int MBEDTLS_PRIVATE(state); /*!< SSL handshake: current state */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + int MBEDTLS_PRIVATE(renego_status); /*!< Initial, in progress, pending? */ + int MBEDTLS_PRIVATE(renego_records_seen); /*!< Records since renego request, or with DTLS, + number of retransmissions of request if + renego_max_records is < 0 */ +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + + /** Server: Negotiated TLS protocol version. + * Client: Maximum TLS version to be negotiated, then negotiated TLS + * version. + * + * It is initialized as the maximum TLS version to be negotiated in the + * ClientHello writing preparation stage and used throughout the + * ClientHello writing. For a fresh handshake not linked to any previous + * handshake, it is initialized to the configured maximum TLS version + * to be negotiated. When renegotiating or resuming a session, it is + * initialized to the previously negotiated TLS version. + * + * Updated to the negotiated TLS version as soon as the ServerHello is + * received. + */ + mbedtls_ssl_protocol_version MBEDTLS_PRIVATE(tls_version); + + unsigned MBEDTLS_PRIVATE(badmac_seen); /*!< records with a bad MAC received */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + /** Callback to customize X.509 certificate chain verification */ + int(*MBEDTLS_PRIVATE(f_vrfy))(void *, mbedtls_x509_crt *, int, uint32_t *); + void *MBEDTLS_PRIVATE(p_vrfy); /*!< context for X.509 verify callback */ +#endif + + mbedtls_ssl_send_t *MBEDTLS_PRIVATE(f_send); /*!< Callback for network send */ + mbedtls_ssl_recv_t *MBEDTLS_PRIVATE(f_recv); /*!< Callback for network receive */ + mbedtls_ssl_recv_timeout_t *MBEDTLS_PRIVATE(f_recv_timeout); + /*!< Callback for network receive with timeout */ + + void *MBEDTLS_PRIVATE(p_bio); /*!< context for I/O operations */ + + /* + * Session layer + */ + mbedtls_ssl_session *MBEDTLS_PRIVATE(session_in); /*!< current session data (in) */ + mbedtls_ssl_session *MBEDTLS_PRIVATE(session_out); /*!< current session data (out) */ + mbedtls_ssl_session *MBEDTLS_PRIVATE(session); /*!< negotiated session data */ + mbedtls_ssl_session *MBEDTLS_PRIVATE(session_negotiate); /*!< session data in negotiation */ + + mbedtls_ssl_handshake_params *MBEDTLS_PRIVATE(handshake); /*!< params required only during + the handshake process */ + + /* + * Record layer transformations + */ + mbedtls_ssl_transform *MBEDTLS_PRIVATE(transform_in); /*!< current transform params (in) + * This is always a reference, + * never an owning pointer. */ + mbedtls_ssl_transform *MBEDTLS_PRIVATE(transform_out); /*!< current transform params (out) + * This is always a reference, + * never an owning pointer. */ + mbedtls_ssl_transform *MBEDTLS_PRIVATE(transform); /*!< negotiated transform params + * This pointer owns the transform + * it references. */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + mbedtls_ssl_transform *MBEDTLS_PRIVATE(transform_negotiate); /*!< transform params in negotiation + * This pointer owns the transform + * it references. */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /*! The application data transform in TLS 1.3. + * This pointer owns the transform it references. */ + mbedtls_ssl_transform *MBEDTLS_PRIVATE(transform_application); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + /* + * Timers + */ + void *MBEDTLS_PRIVATE(p_timer); /*!< context for the timer callbacks */ + + mbedtls_ssl_set_timer_t *MBEDTLS_PRIVATE(f_set_timer); /*!< set timer callback */ + mbedtls_ssl_get_timer_t *MBEDTLS_PRIVATE(f_get_timer); /*!< get timer callback */ + + /* + * Record layer (incoming data) + */ + unsigned char *MBEDTLS_PRIVATE(in_buf); /*!< input buffer */ + unsigned char *MBEDTLS_PRIVATE(in_ctr); /*!< 64-bit incoming message counter + TLS: maintained by us + DTLS: read from peer */ + unsigned char *MBEDTLS_PRIVATE(in_hdr); /*!< start of record header */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + unsigned char *MBEDTLS_PRIVATE(in_cid); /*!< The start of the CID; + * (the end is marked by in_len). */ +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + unsigned char *MBEDTLS_PRIVATE(in_len); /*!< two-bytes message length field */ + unsigned char *MBEDTLS_PRIVATE(in_iv); /*!< ivlen-byte IV */ + unsigned char *MBEDTLS_PRIVATE(in_msg); /*!< message contents (in_iv+ivlen) */ + unsigned char *MBEDTLS_PRIVATE(in_offt); /*!< read offset in application data */ + + int MBEDTLS_PRIVATE(in_msgtype); /*!< record header: message type */ + size_t MBEDTLS_PRIVATE(in_msglen); /*!< record header: message length */ + size_t MBEDTLS_PRIVATE(in_left); /*!< amount of data read so far */ +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t MBEDTLS_PRIVATE(in_buf_len); /*!< length of input buffer */ +#endif +#if defined(MBEDTLS_SSL_PROTO_DTLS) + uint16_t MBEDTLS_PRIVATE(in_epoch); /*!< DTLS epoch for incoming records */ + size_t MBEDTLS_PRIVATE(next_record_offset); /*!< offset of the next record in datagram + (equal to in_left if none) */ +#endif /* MBEDTLS_SSL_PROTO_DTLS */ +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + uint64_t MBEDTLS_PRIVATE(in_window_top); /*!< last validated record seq_num */ + uint64_t MBEDTLS_PRIVATE(in_window); /*!< bitmask for replay detection */ +#endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ + + size_t MBEDTLS_PRIVATE(in_hslen); /*!< current handshake message length, + including the handshake header */ + int MBEDTLS_PRIVATE(nb_zero); /*!< # of 0-length encrypted messages */ + + int MBEDTLS_PRIVATE(keep_current_message); /*!< drop or reuse current message + on next call to record layer? */ + + /* The following three variables indicate if and, if yes, + * what kind of alert is pending to be sent. + */ + unsigned char MBEDTLS_PRIVATE(send_alert); /*!< Determines if a fatal alert + should be sent. Values: + - \c 0 , no alert is to be sent. + - \c 1 , alert is to be sent. */ + unsigned char MBEDTLS_PRIVATE(alert_type); /*!< Type of alert if send_alert + != 0 */ + int MBEDTLS_PRIVATE(alert_reason); /*!< The error code to be returned + to the user once the fatal alert + has been sent. */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + uint8_t MBEDTLS_PRIVATE(disable_datagram_packing); /*!< Disable packing multiple records + * within a single datagram. */ +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + /* + * Record layer (outgoing data) + */ + unsigned char *MBEDTLS_PRIVATE(out_buf); /*!< output buffer */ + unsigned char *MBEDTLS_PRIVATE(out_ctr); /*!< 64-bit outgoing message counter */ + unsigned char *MBEDTLS_PRIVATE(out_hdr); /*!< start of record header */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + unsigned char *MBEDTLS_PRIVATE(out_cid); /*!< The start of the CID; + * (the end is marked by in_len). */ +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + unsigned char *MBEDTLS_PRIVATE(out_len); /*!< two-bytes message length field */ + unsigned char *MBEDTLS_PRIVATE(out_iv); /*!< ivlen-byte IV */ + unsigned char *MBEDTLS_PRIVATE(out_msg); /*!< message contents (out_iv+ivlen) */ + + int MBEDTLS_PRIVATE(out_msgtype); /*!< record header: message type */ + size_t MBEDTLS_PRIVATE(out_msglen); /*!< record header: message length */ + size_t MBEDTLS_PRIVATE(out_left); /*!< amount of data not yet written */ +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t MBEDTLS_PRIVATE(out_buf_len); /*!< length of output buffer */ +#endif + + unsigned char MBEDTLS_PRIVATE(cur_out_ctr)[MBEDTLS_SSL_SEQUENCE_NUMBER_LEN]; /*!< Outgoing record sequence number. */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + uint16_t MBEDTLS_PRIVATE(mtu); /*!< path mtu, used to fragment outgoing messages */ +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + /* + * User settings + */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + char *MBEDTLS_PRIVATE(hostname); /*!< expected peer CN for verification + (and SNI if available) */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_ALPN) + const char *MBEDTLS_PRIVATE(alpn_chosen); /*!< negotiated protocol */ +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + /* + * use_srtp extension + */ + mbedtls_dtls_srtp_info MBEDTLS_PRIVATE(dtls_srtp_info); +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + + /* + * Information for DTLS hello verify + */ +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) + unsigned char *MBEDTLS_PRIVATE(cli_id); /*!< transport-level ID of the client */ + size_t MBEDTLS_PRIVATE(cli_id_len); /*!< length of cli_id */ +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY && MBEDTLS_SSL_SRV_C */ + + /* + * Secure renegotiation + */ + /* needed to know when to send extension on server */ + int MBEDTLS_PRIVATE(secure_renegotiation); /*!< does peer support legacy or + secure renegotiation */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + size_t MBEDTLS_PRIVATE(verify_data_len); /*!< length of verify data stored */ + char MBEDTLS_PRIVATE(own_verify_data)[MBEDTLS_SSL_VERIFY_DATA_MAX_LEN]; /*!< previous handshake verify data */ + char MBEDTLS_PRIVATE(peer_verify_data)[MBEDTLS_SSL_VERIFY_DATA_MAX_LEN]; /*!< previous handshake verify data */ +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* CID configuration to use in subsequent handshakes. */ + + /*! The next incoming CID, chosen by the user and applying to + * all subsequent handshakes. This may be different from the + * CID currently used in case the user has re-configured the CID + * after an initial handshake. */ + unsigned char MBEDTLS_PRIVATE(own_cid)[MBEDTLS_SSL_CID_IN_LEN_MAX]; + uint8_t MBEDTLS_PRIVATE(own_cid_len); /*!< The length of \c own_cid. */ + uint8_t MBEDTLS_PRIVATE(negotiate_cid); /*!< This indicates whether the CID extension should + * be negotiated in the next handshake or not. + * Possible values are #MBEDTLS_SSL_CID_ENABLED + * and #MBEDTLS_SSL_CID_DISABLED. */ +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_EARLY_DATA) && defined(MBEDTLS_SSL_CLI_C) + int MBEDTLS_PRIVATE(early_data_status); +#endif /* MBEDTLS_SSL_EARLY_DATA && MBEDTLS_SSL_CLI_C */ + + /** Callback to export key block and master secret */ + mbedtls_ssl_export_keys_t *MBEDTLS_PRIVATE(f_export_keys); + void *MBEDTLS_PRIVATE(p_export_keys); /*!< context for key export callback */ + + /** User data pointer or handle. + * + * The library sets this to \p 0 when creating a context and does not + * access it afterwards. + * + * \warning Serializing and restoring an SSL context with + * mbedtls_ssl_context_save() and mbedtls_ssl_context_load() + * does not currently restore the user data. + */ + mbedtls_ssl_user_data_t MBEDTLS_PRIVATE(user_data); +}; + +/** + * \brief Return the name of the ciphersuite associated with the + * given ID + * + * \param ciphersuite_id SSL ciphersuite ID + * + * \return a string containing the ciphersuite name + */ +const char *mbedtls_ssl_get_ciphersuite_name(const int ciphersuite_id); + +/** + * \brief Return the ID of the ciphersuite associated with the + * given name + * + * \param ciphersuite_name SSL ciphersuite name + * + * \return the ID with the ciphersuite or 0 if not found + */ +int mbedtls_ssl_get_ciphersuite_id(const char *ciphersuite_name); + +/** + * \brief Initialize an SSL context + * Just makes the context ready for mbedtls_ssl_setup() or + * mbedtls_ssl_free() + * + * \param ssl SSL context + */ +void mbedtls_ssl_init(mbedtls_ssl_context *ssl); + +/** + * \brief Set up an SSL context for use + * + * \note No copy of the configuration context is made, it can be + * shared by many mbedtls_ssl_context structures. + * + * \warning The conf structure will be accessed during the session. + * It must not be modified or freed as long as the session + * is active. + * + * \warning This function must be called exactly once per context. + * Calling mbedtls_ssl_setup again is not supported, even + * if no session is active. + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param ssl SSL context + * \param conf SSL configuration to use + * + * \return 0 if successful, or MBEDTLS_ERR_SSL_ALLOC_FAILED if + * memory allocation failed + */ +int mbedtls_ssl_setup(mbedtls_ssl_context *ssl, + const mbedtls_ssl_config *conf); + +/** + * \brief Reset an already initialized SSL context for re-use + * while retaining application-set variables, function + * pointers and data. + * + * \param ssl SSL context + * \return 0 if successful, or MBEDTLS_ERR_SSL_ALLOC_FAILED or + MBEDTLS_ERR_SSL_HW_ACCEL_FAILED + */ +int mbedtls_ssl_session_reset(mbedtls_ssl_context *ssl); + +/** + * \brief Set the current endpoint type + * + * \param conf SSL configuration + * \param endpoint must be MBEDTLS_SSL_IS_CLIENT or MBEDTLS_SSL_IS_SERVER + */ +void mbedtls_ssl_conf_endpoint(mbedtls_ssl_config *conf, int endpoint); + +/** + * \brief Set the transport type (TLS or DTLS). + * Default: TLS + * + * \note For DTLS, you must either provide a recv callback that + * doesn't block, or one that handles timeouts, see + * \c mbedtls_ssl_set_bio(). You also need to provide timer + * callbacks with \c mbedtls_ssl_set_timer_cb(). + * + * \param conf SSL configuration + * \param transport transport type: + * MBEDTLS_SSL_TRANSPORT_STREAM for TLS, + * MBEDTLS_SSL_TRANSPORT_DATAGRAM for DTLS. + */ +void mbedtls_ssl_conf_transport(mbedtls_ssl_config *conf, int transport); + +/** + * \brief Set the certificate verification mode + * Default: NONE on server, REQUIRED on client + * + * \param conf SSL configuration + * \param authmode can be: + * + * MBEDTLS_SSL_VERIFY_NONE: peer certificate is not checked + * (default on server) + * (insecure on client) + * + * MBEDTLS_SSL_VERIFY_OPTIONAL: peer certificate is checked, however the + * handshake continues even if verification failed; + * mbedtls_ssl_get_verify_result() can be called after the + * handshake is complete. + * + * MBEDTLS_SSL_VERIFY_REQUIRED: peer *must* present a valid certificate, + * handshake is aborted if verification failed. + * (default on client) + * + * \note On client, MBEDTLS_SSL_VERIFY_REQUIRED is the recommended mode. + * With MBEDTLS_SSL_VERIFY_OPTIONAL, the user needs to call mbedtls_ssl_get_verify_result() at + * the right time(s), which may not be obvious, while REQUIRED always perform + * the verification as soon as possible. For example, REQUIRED was protecting + * against the "triple handshake" attack even before it was found. + */ +void mbedtls_ssl_conf_authmode(mbedtls_ssl_config *conf, int authmode); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_EARLY_DATA) +/** + * \brief Set the early data mode + * Default: disabled on server and client + * + * \param conf The SSL configuration to use. + * \param early_data_enabled can be: + * + * MBEDTLS_SSL_EARLY_DATA_DISABLED: early data functionality is disabled + * This is the default on client and server. + * + * MBEDTLS_SSL_EARLY_DATA_ENABLED: early data functionality is enabled and + * may be negotiated in the handshake. Application using + * early data functionality needs to be aware of the + * lack of replay protection of the early data application + * payloads. + * + * \warning This interface is experimental and may change without notice. + * + */ +void mbedtls_ssl_tls13_conf_early_data(mbedtls_ssl_config *conf, + int early_data_enabled); + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Set the maximum amount of 0-RTT data in bytes + * Default: #MBEDTLS_SSL_MAX_EARLY_DATA_SIZE + * + * This function sets the value of the max_early_data_size + * field of the early data indication extension included in + * the NewSessionTicket messages that the server may send. + * + * The value defines the maximum amount of 0-RTT data + * in bytes that a client will be allowed to send when using + * one of the tickets defined by the NewSessionTicket messages. + * + * \note When resuming a session using a ticket, if the server receives more + * early data than allowed for the ticket, it terminates the connection. + * The maximum amount of 0-RTT data should thus be large enough + * to allow a minimum of early data to be exchanged. + * + * \param[in] conf The SSL configuration to use. + * \param[in] max_early_data_size The maximum amount of 0-RTT data. + * + * \warning This interface is experimental and may change without notice. + * + */ +void mbedtls_ssl_tls13_conf_max_early_data_size( + mbedtls_ssl_config *conf, uint32_t max_early_data_size); +#endif /* MBEDTLS_SSL_SRV_C */ + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_EARLY_DATA */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * \brief Set the verification callback (Optional). + * + * If set, the provided verify callback is called for each + * certificate in the peer's CRT chain, including the trusted + * root. For more information, please see the documentation of + * \c mbedtls_x509_crt_verify(). + * + * \note For per context callbacks and contexts, please use + * mbedtls_ssl_set_verify() instead. + * + * \param conf The SSL configuration to use. + * \param f_vrfy The verification callback to use during CRT verification. + * \param p_vrfy The opaque context to be passed to the callback. + */ +void mbedtls_ssl_conf_verify(mbedtls_ssl_config *conf, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +/** + * \brief Set the random number generator callback + * + * \param conf SSL configuration + * \param f_rng RNG function (mandatory) + * \param p_rng RNG parameter + */ +void mbedtls_ssl_conf_rng(mbedtls_ssl_config *conf, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Set the debug callback + * + * The callback has the following argument: + * void * opaque context for the callback + * int debug level + * const char * file name + * int line number + * const char * message + * + * \param conf SSL configuration + * \param f_dbg debug function + * \param p_dbg debug parameter + */ +void mbedtls_ssl_conf_dbg(mbedtls_ssl_config *conf, + void (*f_dbg)(void *, int, const char *, int, const char *), + void *p_dbg); + +/** + * \brief Return the SSL configuration structure associated + * with the given SSL context. + * + * \note The pointer returned by this function is guaranteed to + * remain valid until the context is freed. + * + * \param ssl The SSL context to query. + * \return Pointer to the SSL configuration associated with \p ssl. + */ +static inline const mbedtls_ssl_config *mbedtls_ssl_context_get_config( + const mbedtls_ssl_context *ssl) +{ + return ssl->MBEDTLS_PRIVATE(conf); +} + +/** + * \brief Set the underlying BIO callbacks for write, read and + * read-with-timeout. + * + * \param ssl SSL context + * \param p_bio parameter (context) shared by BIO callbacks + * \param f_send write callback + * \param f_recv read callback + * \param f_recv_timeout blocking read callback with timeout. + * + * \note One of f_recv or f_recv_timeout can be NULL, in which case + * the other is used. If both are non-NULL, f_recv_timeout is + * used and f_recv is ignored (as if it were NULL). + * + * \note The two most common use cases are: + * - non-blocking I/O, f_recv != NULL, f_recv_timeout == NULL + * - blocking I/O, f_recv == NULL, f_recv_timeout != NULL + * + * \note For DTLS, you need to provide either a non-NULL + * f_recv_timeout callback, or a f_recv that doesn't block. + * + * \note See the documentations of \c mbedtls_ssl_send_t, + * \c mbedtls_ssl_recv_t and \c mbedtls_ssl_recv_timeout_t for + * the conventions those callbacks must follow. + * + * \note On some platforms, net_sockets.c provides + * \c mbedtls_net_send(), \c mbedtls_net_recv() and + * \c mbedtls_net_recv_timeout() that are suitable to be used + * here. + */ +void mbedtls_ssl_set_bio(mbedtls_ssl_context *ssl, + void *p_bio, + mbedtls_ssl_send_t *f_send, + mbedtls_ssl_recv_t *f_recv, + mbedtls_ssl_recv_timeout_t *f_recv_timeout); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + + +/** + * \brief Configure the use of the Connection ID (CID) + * extension in the next handshake. + * + * Reference: RFC 9146 (or draft-ietf-tls-dtls-connection-id-05 + * https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05 + * for legacy version) + * + * The DTLS CID extension allows the reliable association of + * DTLS records to DTLS connections across changes in the + * underlying transport (changed IP and Port metadata) by + * adding explicit connection identifiers (CIDs) to the + * headers of encrypted DTLS records. The desired CIDs are + * configured by the application layer and are exchanged in + * new `ClientHello` / `ServerHello` extensions during the + * handshake, where each side indicates the CID it wants the + * peer to use when writing encrypted messages. The CIDs are + * put to use once records get encrypted: the stack discards + * any incoming records that don't include the configured CID + * in their header, and adds the peer's requested CID to the + * headers of outgoing messages. + * + * This API enables or disables the use of the CID extension + * in the next handshake and sets the value of the CID to + * be used for incoming messages. + * + * \param ssl The SSL context to configure. This must be initialized. + * \param enable This value determines whether the CID extension should + * be used or not. Possible values are: + * - MBEDTLS_SSL_CID_ENABLED to enable the use of the CID. + * - MBEDTLS_SSL_CID_DISABLED (default) to disable the use + * of the CID. + * \param own_cid The address of the readable buffer holding the CID we want + * the peer to use when sending encrypted messages to us. + * This may be \c NULL if \p own_cid_len is \c 0. + * This parameter is unused if \p enabled is set to + * MBEDTLS_SSL_CID_DISABLED. + * \param own_cid_len The length of \p own_cid. + * This parameter is unused if \p enabled is set to + * MBEDTLS_SSL_CID_DISABLED. + * + * \note The value of \p own_cid_len must match the value of the + * \c len parameter passed to mbedtls_ssl_conf_cid() + * when configuring the ::mbedtls_ssl_config that \p ssl + * is bound to. + * + * \note This CID configuration applies to subsequent handshakes + * performed on the SSL context \p ssl, but does not trigger + * one. You still have to call `mbedtls_ssl_handshake()` + * (for the initial handshake) or `mbedtls_ssl_renegotiate()` + * (for a renegotiation handshake) explicitly after a + * successful call to this function to run the handshake. + * + * \note This call cannot guarantee that the use of the CID + * will be successfully negotiated in the next handshake, + * because the peer might not support it. Specifically: + * - On the Client, enabling the use of the CID through + * this call implies that the `ClientHello` in the next + * handshake will include the CID extension, thereby + * offering the use of the CID to the server. Only if + * the `ServerHello` contains the CID extension, too, + * the CID extension will actually be put to use. + * - On the Server, enabling the use of the CID through + * this call implies that the server will look for + * the CID extension in a `ClientHello` from the client, + * and, if present, reply with a CID extension in its + * `ServerHello`. + * + * \note To check whether the use of the CID was negotiated + * after the subsequent handshake has completed, please + * use the API mbedtls_ssl_get_peer_cid(). + * + * \warning If the use of the CID extension is enabled in this call + * and the subsequent handshake negotiates its use, Mbed TLS + * will silently drop every packet whose CID does not match + * the CID configured in \p own_cid. It is the responsibility + * of the user to adapt the underlying transport to take care + * of CID-based demultiplexing before handing datagrams to + * Mbed TLS. + * + * \return \c 0 on success. In this case, the CID configuration + * applies to the next handshake. + * \return A negative error code on failure. + */ +int mbedtls_ssl_set_cid(mbedtls_ssl_context *ssl, + int enable, + unsigned char const *own_cid, + size_t own_cid_len); + +/** + * \brief Get information about our request for usage of the CID + * extension in the current connection. + * + * \param ssl The SSL context to query. + * \param enabled The address at which to store whether the CID extension + * is requested to be used or not. If the CID is + * requested, `*enabled` is set to + * MBEDTLS_SSL_CID_ENABLED; otherwise, it is set to + * MBEDTLS_SSL_CID_DISABLED. + * \param own_cid The address of the buffer in which to store our own + * CID (if the CID extension is requested). This may be + * \c NULL in case the value of our CID isn't needed. If + * it is not \c NULL, \p own_cid_len must not be \c NULL. + * \param own_cid_len The address at which to store the size of our own CID + * (if the CID extension is requested). This is also the + * number of Bytes in \p own_cid that have been written. + * This may be \c NULL in case the length of our own CID + * isn't needed. If it is \c NULL, \p own_cid must be + * \c NULL, too. + * + *\note If we are requesting an empty CID this function sets + * `*enabled` to #MBEDTLS_SSL_CID_DISABLED (the rationale + * for this is that the resulting outcome is the + * same as if the CID extensions wasn't requested). + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_ssl_get_own_cid(mbedtls_ssl_context *ssl, + int *enabled, + unsigned char own_cid[MBEDTLS_SSL_CID_OUT_LEN_MAX], + size_t *own_cid_len); + +/** + * \brief Get information about the use of the CID extension + * in the current connection. + * + * \param ssl The SSL context to query. + * \param enabled The address at which to store whether the CID extension + * is currently in use or not. If the CID is in use, + * `*enabled` is set to MBEDTLS_SSL_CID_ENABLED; + * otherwise, it is set to MBEDTLS_SSL_CID_DISABLED. + * \param peer_cid The address of the buffer in which to store the CID + * chosen by the peer (if the CID extension is used). + * This may be \c NULL in case the value of peer CID + * isn't needed. If it is not \c NULL, \p peer_cid_len + * must not be \c NULL. + * \param peer_cid_len The address at which to store the size of the CID + * chosen by the peer (if the CID extension is used). + * This is also the number of Bytes in \p peer_cid that + * have been written. + * This may be \c NULL in case the length of the peer CID + * isn't needed. If it is \c NULL, \p peer_cid must be + * \c NULL, too. + * + * \note This applies to the state of the CID negotiated in + * the last complete handshake. If a handshake is in + * progress, this function will attempt to complete + * the handshake first. + * + * \note If CID extensions have been exchanged but both client + * and server chose to use an empty CID, this function + * sets `*enabled` to #MBEDTLS_SSL_CID_DISABLED + * (the rationale for this is that the resulting + * communication is the same as if the CID extensions + * hadn't been used). + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +int mbedtls_ssl_get_peer_cid(mbedtls_ssl_context *ssl, + int *enabled, + unsigned char peer_cid[MBEDTLS_SSL_CID_OUT_LEN_MAX], + size_t *peer_cid_len); + +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +/** + * \brief Set the Maximum Transport Unit (MTU). + * Special value: 0 means unset (no limit). + * This represents the maximum size of a datagram payload + * handled by the transport layer (usually UDP) as determined + * by the network link and stack. In practice, this controls + * the maximum size datagram the DTLS layer will pass to the + * \c f_send() callback set using \c mbedtls_ssl_set_bio(). + * + * \note The limit on datagram size is converted to a limit on + * record payload by subtracting the current overhead of + * encapsulation and encryption/authentication if any. + * + * \note This can be called at any point during the connection, for + * example when a Path Maximum Transfer Unit (PMTU) + * estimate becomes available from other sources, + * such as lower (or higher) protocol layers. + * + * \note This setting only controls the size of the packets we send, + * and does not restrict the size of the datagrams we're + * willing to receive. Client-side, you can request the + * server to use smaller records with \c + * mbedtls_ssl_conf_max_frag_len(). + * + * \note If both a MTU and a maximum fragment length have been + * configured (or negotiated with the peer), the resulting + * lower limit on record payload (see first note) is used. + * + * \note This can only be used to decrease the maximum size + * of datagrams (hence records, see first note) sent. It + * cannot be used to increase the maximum size of records over + * the limit set by #MBEDTLS_SSL_OUT_CONTENT_LEN. + * + * \note Values lower than the current record layer expansion will + * result in an error when trying to send data. + * + * \param ssl SSL context + * \param mtu Value of the path MTU in bytes + */ +void mbedtls_ssl_set_mtu(mbedtls_ssl_context *ssl, uint16_t mtu); +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * \brief Set a connection-specific verification callback (optional). + * + * If set, the provided verify callback is called for each + * certificate in the peer's CRT chain, including the trusted + * root. For more information, please see the documentation of + * \c mbedtls_x509_crt_verify(). + * + * \note This call is analogous to mbedtls_ssl_conf_verify() but + * binds the verification callback and context to an SSL context + * as opposed to an SSL configuration. + * If mbedtls_ssl_conf_verify() and mbedtls_ssl_set_verify() + * are both used, mbedtls_ssl_set_verify() takes precedence. + * + * \param ssl The SSL context to use. + * \param f_vrfy The verification callback to use during CRT verification. + * \param p_vrfy The opaque context to be passed to the callback. + */ +void mbedtls_ssl_set_verify(mbedtls_ssl_context *ssl, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +/** + * \brief Set the timeout period for mbedtls_ssl_read() + * (Default: no timeout.) + * + * \param conf SSL configuration context + * \param timeout Timeout value in milliseconds. + * Use 0 for no timeout (default). + * + * \note With blocking I/O, this will only work if a non-NULL + * \c f_recv_timeout was set with \c mbedtls_ssl_set_bio(). + * With non-blocking I/O, this will only work if timer + * callbacks were set with \c mbedtls_ssl_set_timer_cb(). + * + * \note With non-blocking I/O, you may also skip this function + * altogether and handle timeouts at the application layer. + */ +void mbedtls_ssl_conf_read_timeout(mbedtls_ssl_config *conf, uint32_t timeout); + +/** + * \brief Check whether a buffer contains a valid and authentic record + * that has not been seen before. (DTLS only). + * + * This function does not change the user-visible state + * of the SSL context. Its sole purpose is to provide + * an indication of the legitimacy of an incoming record. + * + * This can be useful e.g. in distributed server environments + * using the DTLS Connection ID feature, in which connections + * might need to be passed between service instances on a change + * of peer address, but where such disruptive operations should + * only happen after the validity of incoming records has been + * confirmed. + * + * \param ssl The SSL context to use. + * \param buf The address of the buffer holding the record to be checked. + * This must be a read/write buffer of length \p buflen Bytes. + * \param buflen The length of \p buf in Bytes. + * + * \note This routine only checks whether the provided buffer begins + * with a valid and authentic record that has not been seen + * before, but does not check potential data following the + * initial record. In particular, it is possible to pass DTLS + * datagrams containing multiple records, in which case only + * the first record is checked. + * + * \note This function modifies the input buffer \p buf. If you need + * to preserve the original record, you have to maintain a copy. + * + * \return \c 0 if the record is valid and authentic and has not been + * seen before. + * \return MBEDTLS_ERR_SSL_INVALID_MAC if the check completed + * successfully but the record was found to be not authentic. + * \return MBEDTLS_ERR_SSL_INVALID_RECORD if the check completed + * successfully but the record was found to be invalid for + * a reason different from authenticity checking. + * \return MBEDTLS_ERR_SSL_UNEXPECTED_RECORD if the check completed + * successfully but the record was found to be unexpected + * in the state of the SSL context, including replayed records. + * \return Another negative error code on different kinds of failure. + * In this case, the SSL context becomes unusable and needs + * to be freed or reset before reuse. + */ +int mbedtls_ssl_check_record(mbedtls_ssl_context const *ssl, + unsigned char *buf, + size_t buflen); + +/** + * \brief Set the timer callbacks (Mandatory for DTLS.) + * + * \param ssl SSL context + * \param p_timer parameter (context) shared by timer callbacks + * \param f_set_timer set timer callback + * \param f_get_timer get timer callback. Must return: + * + * \note See the documentation of \c mbedtls_ssl_set_timer_t and + * \c mbedtls_ssl_get_timer_t for the conventions this pair of + * callbacks must follow. + * + * \note On some platforms, timing.c provides + * \c mbedtls_timing_set_delay() and + * \c mbedtls_timing_get_delay() that are suitable for using + * here, except if using an event-driven style. + * + * \note See also the "DTLS tutorial" article in our knowledge base. + * https://mbed-tls.readthedocs.io/en/latest/kb/how-to/dtls-tutorial + */ +void mbedtls_ssl_set_timer_cb(mbedtls_ssl_context *ssl, + void *p_timer, + mbedtls_ssl_set_timer_t *f_set_timer, + mbedtls_ssl_get_timer_t *f_get_timer); + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Set the certificate selection callback (server-side only). + * + * If set, the callback is always called for each handshake, + * after `ClientHello` processing has finished. + * + * \param conf The SSL configuration to register the callback with. + * \param f_cert_cb The callback for selecting server certificate after + * `ClientHello` processing has finished. + */ +static inline void mbedtls_ssl_conf_cert_cb(mbedtls_ssl_config *conf, + mbedtls_ssl_hs_cb_t f_cert_cb) +{ + conf->MBEDTLS_PRIVATE(f_cert_cb) = f_cert_cb; +} +#endif /* MBEDTLS_SSL_SRV_C */ + +/** + * \brief Callback type: generate and write session ticket + * + * \note This describes what a callback implementation should do. + * This callback should generate an encrypted and + * authenticated ticket for the session and write it to the + * output buffer. Here, ticket means the opaque ticket part + * of the NewSessionTicket structure of RFC 5077. + * + * \param p_ticket Context for the callback + * \param session SSL session to be written in the ticket + * \param start Start of the output buffer + * \param end End of the output buffer + * \param tlen On exit, holds the length written + * \param lifetime On exit, holds the lifetime of the ticket in seconds + * + * \return 0 if successful, or + * a specific MBEDTLS_ERR_XXX code. + */ +typedef int mbedtls_ssl_ticket_write_t(void *p_ticket, + const mbedtls_ssl_session *session, + unsigned char *start, + const unsigned char *end, + size_t *tlen, + uint32_t *lifetime); + +/** + * \brief Callback type: parse and load session ticket + * + * \note This describes what a callback implementation should do. + * This callback should parse a session ticket as generated + * by the corresponding mbedtls_ssl_ticket_write_t function, + * and, if the ticket is authentic and valid, load the + * session. + * + * \note The implementation is allowed to modify the first len + * bytes of the input buffer, eg to use it as a temporary + * area for the decrypted ticket contents. + * + * \param p_ticket Context for the callback + * \param session SSL session to be loaded + * \param buf Start of the buffer containing the ticket + * \param len Length of the ticket. + * + * \return 0 if successful, or + * MBEDTLS_ERR_SSL_INVALID_MAC if not authentic, or + * MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED if expired, or + * any other non-zero code for other failures. + */ +typedef int mbedtls_ssl_ticket_parse_t(void *p_ticket, + mbedtls_ssl_session *session, + unsigned char *buf, + size_t len); + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Configure SSL session ticket callbacks (server only). + * (Default: none.) + * + * \note On server, session tickets are enabled by providing + * non-NULL callbacks. + * + * \note On client, use \c mbedtls_ssl_conf_session_tickets(). + * + * \param conf SSL configuration context + * \param f_ticket_write Callback for writing a ticket + * \param f_ticket_parse Callback for parsing a ticket + * \param p_ticket Context shared by the two callbacks + */ +void mbedtls_ssl_conf_session_tickets_cb(mbedtls_ssl_config *conf, + mbedtls_ssl_ticket_write_t *f_ticket_write, + mbedtls_ssl_ticket_parse_t *f_ticket_parse, + void *p_ticket); +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_SRV_C */ + +/** + * \brief Configure a key export callback. + * (Default: none.) + * + * This API can be used for two purposes: + * - Debugging: Use this API to e.g. generate an NSSKeylog + * file and use it to inspect encrypted traffic in tools + * such as Wireshark. + * - Application-specific export: Use this API to implement + * key exporters, e.g. for EAP-TLS or DTLS-SRTP. + * + * + * \param ssl The SSL context to which the export + * callback should be attached. + * \param f_export_keys The callback for the key export. + * \param p_export_keys The opaque context pointer to be passed to the + * callback \p f_export_keys. + */ +void mbedtls_ssl_set_export_keys_cb(mbedtls_ssl_context *ssl, + mbedtls_ssl_export_keys_t *f_export_keys, + void *p_export_keys); + +/** \brief Set the user data in an SSL configuration to a pointer. + * + * You can retrieve this value later with mbedtls_ssl_conf_get_user_data_p(). + * + * \note The library stores \c p without accessing it. It is the responsibility + * of the caller to ensure that the pointer remains valid. + * + * \param conf The SSL configuration context to modify. + * \param p The new value of the user data. + */ +static inline void mbedtls_ssl_conf_set_user_data_p( + mbedtls_ssl_config *conf, + void *p) +{ + conf->MBEDTLS_PRIVATE(user_data).p = p; +} + +/** \brief Set the user data in an SSL configuration to an integer. + * + * You can retrieve this value later with mbedtls_ssl_conf_get_user_data_n(). + * + * \param conf The SSL configuration context to modify. + * \param n The new value of the user data. + */ +static inline void mbedtls_ssl_conf_set_user_data_n( + mbedtls_ssl_config *conf, + uintptr_t n) +{ + conf->MBEDTLS_PRIVATE(user_data).n = n; +} + +/** \brief Retrieve the user data in an SSL configuration as a pointer. + * + * This is the value last set with mbedtls_ssl_conf_set_user_data_p(), or + * \c NULL if mbedtls_ssl_conf_set_user_data_p() has not previously been + * called. The value is undefined if mbedtls_ssl_conf_set_user_data_n() has + * been called without a subsequent call to mbedtls_ssl_conf_set_user_data_p(). + * + * \param conf The SSL configuration context to modify. + * \return The current value of the user data. + */ +static inline void *mbedtls_ssl_conf_get_user_data_p( + mbedtls_ssl_config *conf) +{ + return conf->MBEDTLS_PRIVATE(user_data).p; +} + +/** \brief Retrieve the user data in an SSL configuration as an integer. + * + * This is the value last set with mbedtls_ssl_conf_set_user_data_n(), or + * \c 0 if mbedtls_ssl_conf_set_user_data_n() has not previously been + * called. The value is undefined if mbedtls_ssl_conf_set_user_data_p() has + * been called without a subsequent call to mbedtls_ssl_conf_set_user_data_n(). + * + * \param conf The SSL configuration context to modify. + * \return The current value of the user data. + */ +static inline uintptr_t mbedtls_ssl_conf_get_user_data_n( + mbedtls_ssl_config *conf) +{ + return conf->MBEDTLS_PRIVATE(user_data).n; +} + +/** \brief Set the user data in an SSL context to a pointer. + * + * You can retrieve this value later with mbedtls_ssl_get_user_data_p(). + * + * \note The library stores \c p without accessing it. It is the responsibility + * of the caller to ensure that the pointer remains valid. + * + * \param ssl The SSL context to modify. + * \param p The new value of the user data. + */ +static inline void mbedtls_ssl_set_user_data_p( + mbedtls_ssl_context *ssl, + void *p) +{ + ssl->MBEDTLS_PRIVATE(user_data).p = p; +} + +/** \brief Set the user data in an SSL context to an integer. + * + * You can retrieve this value later with mbedtls_ssl_get_user_data_n(). + * + * \param ssl The SSL context to modify. + * \param n The new value of the user data. + */ +static inline void mbedtls_ssl_set_user_data_n( + mbedtls_ssl_context *ssl, + uintptr_t n) +{ + ssl->MBEDTLS_PRIVATE(user_data).n = n; +} + +/** \brief Retrieve the user data in an SSL context as a pointer. + * + * This is the value last set with mbedtls_ssl_set_user_data_p(), or + * \c NULL if mbedtls_ssl_set_user_data_p() has not previously been + * called. The value is undefined if mbedtls_ssl_set_user_data_n() has + * been called without a subsequent call to mbedtls_ssl_set_user_data_p(). + * + * \param ssl The SSL context to modify. + * \return The current value of the user data. + */ +static inline void *mbedtls_ssl_get_user_data_p( + mbedtls_ssl_context *ssl) +{ + return ssl->MBEDTLS_PRIVATE(user_data).p; +} + +/** \brief Retrieve the user data in an SSL context as an integer. + * + * This is the value last set with mbedtls_ssl_set_user_data_n(), or + * \c 0 if mbedtls_ssl_set_user_data_n() has not previously been + * called. The value is undefined if mbedtls_ssl_set_user_data_p() has + * been called without a subsequent call to mbedtls_ssl_set_user_data_n(). + * + * \param ssl The SSL context to modify. + * \return The current value of the user data. + */ +static inline uintptr_t mbedtls_ssl_get_user_data_n( + mbedtls_ssl_context *ssl) +{ + return ssl->MBEDTLS_PRIVATE(user_data).n; +} + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +/** + * \brief Configure asynchronous private key operation callbacks. + * + * \param conf SSL configuration context + * \param f_async_sign Callback to start a signature operation. See + * the description of ::mbedtls_ssl_async_sign_t + * for more information. This may be \c NULL if the + * external processor does not support any signature + * operation; in this case the private key object + * associated with the certificate will be used. + * \param f_async_decrypt Callback to start a decryption operation. See + * the description of ::mbedtls_ssl_async_decrypt_t + * for more information. This may be \c NULL if the + * external processor does not support any decryption + * operation; in this case the private key object + * associated with the certificate will be used. + * \param f_async_resume Callback to resume an asynchronous operation. See + * the description of ::mbedtls_ssl_async_resume_t + * for more information. This may not be \c NULL unless + * \p f_async_sign and \p f_async_decrypt are both + * \c NULL. + * \param f_async_cancel Callback to cancel an asynchronous operation. See + * the description of ::mbedtls_ssl_async_cancel_t + * for more information. This may be \c NULL if + * no cleanup is needed. + * \param config_data A pointer to configuration data which can be + * retrieved with + * mbedtls_ssl_conf_get_async_config_data(). The + * library stores this value without dereferencing it. + */ +void mbedtls_ssl_conf_async_private_cb(mbedtls_ssl_config *conf, + mbedtls_ssl_async_sign_t *f_async_sign, + mbedtls_ssl_async_decrypt_t *f_async_decrypt, + mbedtls_ssl_async_resume_t *f_async_resume, + mbedtls_ssl_async_cancel_t *f_async_cancel, + void *config_data); + +/** + * \brief Retrieve the configuration data set by + * mbedtls_ssl_conf_async_private_cb(). + * + * \param conf SSL configuration context + * \return The configuration data set by + * mbedtls_ssl_conf_async_private_cb(). + */ +void *mbedtls_ssl_conf_get_async_config_data(const mbedtls_ssl_config *conf); + +/** + * \brief Retrieve the asynchronous operation user context. + * + * \note This function may only be called while a handshake + * is in progress. + * + * \param ssl The SSL context to access. + * + * \return The asynchronous operation user context that was last + * set during the current handshake. If + * mbedtls_ssl_set_async_operation_data() has not yet been + * called during the current handshake, this function returns + * \c NULL. + */ +void *mbedtls_ssl_get_async_operation_data(const mbedtls_ssl_context *ssl); + +/** + * \brief Retrieve the asynchronous operation user context. + * + * \note This function may only be called while a handshake + * is in progress. + * + * \param ssl The SSL context to access. + * \param ctx The new value of the asynchronous operation user context. + * Call mbedtls_ssl_get_async_operation_data() later during the + * same handshake to retrieve this value. + */ +void mbedtls_ssl_set_async_operation_data(mbedtls_ssl_context *ssl, + void *ctx); +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +/** + * \brief Callback type: generate a cookie + * + * \param ctx Context for the callback + * \param p Buffer to write to, + * must be updated to point right after the cookie + * \param end Pointer to one past the end of the output buffer + * \param info Client ID info that was passed to + * \c mbedtls_ssl_set_client_transport_id() + * \param ilen Length of info in bytes + * + * \return The callback must return 0 on success, + * or a negative error code. + */ +typedef int mbedtls_ssl_cookie_write_t(void *ctx, + unsigned char **p, unsigned char *end, + const unsigned char *info, size_t ilen); + +/** + * \brief Callback type: verify a cookie + * + * \param ctx Context for the callback + * \param cookie Cookie to verify + * \param clen Length of cookie + * \param info Client ID info that was passed to + * \c mbedtls_ssl_set_client_transport_id() + * \param ilen Length of info in bytes + * + * \return The callback must return 0 if cookie is valid, + * or a negative error code. + */ +typedef int mbedtls_ssl_cookie_check_t(void *ctx, + const unsigned char *cookie, size_t clen, + const unsigned char *info, size_t ilen); + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Register callbacks for DTLS cookies + * (Server only. DTLS only.) + * + * Default: dummy callbacks that fail, in order to force you to + * register working callbacks (and initialize their context). + * + * To disable HelloVerifyRequest, register NULL callbacks. + * + * \warning Disabling hello verification allows your server to be used + * for amplification in DoS attacks against other hosts. + * Only disable if you known this can't happen in your + * particular environment. + * + * \note See comments on \c mbedtls_ssl_handshake() about handling + * the MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED that is expected + * on the first handshake attempt when this is enabled. + * + * \note This is also necessary to handle client reconnection from + * the same port as described in RFC 6347 section 4.2.8 (only + * the variant with cookies is supported currently). See + * comments on \c mbedtls_ssl_read() for details. + * + * \param conf SSL configuration + * \param f_cookie_write Cookie write callback + * \param f_cookie_check Cookie check callback + * \param p_cookie Context for both callbacks + */ +void mbedtls_ssl_conf_dtls_cookies(mbedtls_ssl_config *conf, + mbedtls_ssl_cookie_write_t *f_cookie_write, + mbedtls_ssl_cookie_check_t *f_cookie_check, + void *p_cookie); + +/** + * \brief Set client's transport-level identification info. + * (Server only. DTLS only.) + * + * This is usually the IP address (and port), but could be + * anything identify the client depending on the underlying + * network stack. Used for HelloVerifyRequest with DTLS. + * This is *not* used to route the actual packets. + * + * \param ssl SSL context + * \param info Transport-level info identifying the client (eg IP + port) + * \param ilen Length of info in bytes + * + * \note An internal copy is made, so the info buffer can be reused. + * + * \return 0 on success, + * MBEDTLS_ERR_SSL_BAD_INPUT_DATA if used on client, + * MBEDTLS_ERR_SSL_ALLOC_FAILED if out of memory. + */ +int mbedtls_ssl_set_client_transport_id(mbedtls_ssl_context *ssl, + const unsigned char *info, + size_t ilen); + +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY && MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) +/** + * \brief Enable or disable anti-replay protection for DTLS. + * (DTLS only, no effect on TLS.) + * Default: enabled. + * + * \param conf SSL configuration + * \param mode MBEDTLS_SSL_ANTI_REPLAY_ENABLED or MBEDTLS_SSL_ANTI_REPLAY_DISABLED. + * + * \warning Disabling this is a security risk unless the application + * protocol handles duplicated packets in a safe way. You + * should not disable this without careful consideration. + * However, if your application already detects duplicated + * packets and needs information about them to adjust its + * transmission strategy, then you'll want to disable this. + */ +void mbedtls_ssl_conf_dtls_anti_replay(mbedtls_ssl_config *conf, char mode); +#endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ + +/** + * \brief Set a limit on the number of records with a bad MAC + * before terminating the connection. + * (DTLS only, no effect on TLS.) + * Default: 0 (disabled). + * + * \param conf SSL configuration + * \param limit Limit, or 0 to disable. + * + * \note If the limit is N, then the connection is terminated when + * the Nth non-authentic record is seen. + * + * \note Records with an invalid header are not counted, only the + * ones going through the authentication-decryption phase. + * + * \note This is a security trade-off related to the fact that it's + * often relatively easy for an active attacker to inject UDP + * datagrams. On one hand, setting a low limit here makes it + * easier for such an attacker to forcibly terminated a + * connection. On the other hand, a high limit or no limit + * might make us waste resources checking authentication on + * many bogus packets. + */ +void mbedtls_ssl_conf_dtls_badmac_limit(mbedtls_ssl_config *conf, unsigned limit); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +/** + * \brief Allow or disallow packing of multiple handshake records + * within a single datagram. + * + * \param ssl The SSL context to configure. + * \param allow_packing This determines whether datagram packing may + * be used or not. A value of \c 0 means that every + * record will be sent in a separate datagram; a + * value of \c 1 means that, if space permits, + * multiple handshake messages (including CCS) belonging to + * a single flight may be packed within a single datagram. + * + * \note This is enabled by default and should only be disabled + * for test purposes, or if datagram packing causes + * interoperability issues with peers that don't support it. + * + * \note Allowing datagram packing reduces the network load since + * there's less overhead if multiple messages share the same + * datagram. Also, it increases the handshake efficiency + * since messages belonging to a single datagram will not + * be reordered in transit, and so future message buffering + * or flight retransmission (if no buffering is used) as + * means to deal with reordering are needed less frequently. + * + * \note Application records are not affected by this option and + * are currently always sent in separate datagrams. + * + */ +void mbedtls_ssl_set_datagram_packing(mbedtls_ssl_context *ssl, + unsigned allow_packing); + +/** + * \brief Set retransmit timeout values for the DTLS handshake. + * (DTLS only, no effect on TLS.) + * + * \param conf SSL configuration + * \param min Initial timeout value in milliseconds. + * Default: 1000 (1 second). + * \param max Maximum timeout value in milliseconds. + * Default: 60000 (60 seconds). + * + * \note Default values are from RFC 6347 section 4.2.4.1. + * + * \note The 'min' value should typically be slightly above the + * expected round-trip time to your peer, plus whatever time + * it takes for the peer to process the message. For example, + * if your RTT is about 600ms and you peer needs up to 1s to + * do the cryptographic operations in the handshake, then you + * should set 'min' slightly above 1600. Lower values of 'min' + * might cause spurious resends which waste network resources, + * while larger value of 'min' will increase overall latency + * on unreliable network links. + * + * \note The more unreliable your network connection is, the larger + * your max / min ratio needs to be in order to achieve + * reliable handshakes. + * + * \note Messages are retransmitted up to log2(ceil(max/min)) times. + * For example, if min = 1s and max = 5s, the retransmit plan + * goes: send ... 1s -> resend ... 2s -> resend ... 4s -> + * resend ... 5s -> give up and return a timeout error. + */ +void mbedtls_ssl_conf_handshake_timeout(mbedtls_ssl_config *conf, uint32_t min, uint32_t max); +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Set the session cache callbacks (server-side only) + * If not set, no session resuming is done (except if session + * tickets are enabled too). + * + * The session cache has the responsibility to check for stale + * entries based on timeout. See RFC 5246 for recommendations. + * + * Warning: session.peer_cert is cleared by the SSL/TLS layer on + * connection shutdown, so do not cache the pointer! Either set + * it to NULL or make a full copy of the certificate. + * + * The get callback is called once during the initial handshake + * to enable session resuming. The get function has the + * following parameters: (void *parameter, mbedtls_ssl_session *session) + * If a valid entry is found, it should fill the master of + * the session object with the cached values and return 0, + * return 1 otherwise. Optionally peer_cert can be set as well + * if it is properly present in cache entry. + * + * The set callback is called once during the initial handshake + * to enable session resuming after the entire handshake has + * been finished. The set function has the following parameters: + * (void *parameter, const mbedtls_ssl_session *session). The function + * should create a cache entry for future retrieval based on + * the data in the session structure and should keep in mind + * that the mbedtls_ssl_session object presented (and all its referenced + * data) is cleared by the SSL/TLS layer when the connection is + * terminated. It is recommended to add metadata to determine if + * an entry is still valid in the future. Return 0 if + * successfully cached, return 1 otherwise. + * + * \param conf SSL configuration + * \param p_cache parameter (context) for both callbacks + * \param f_get_cache session get callback + * \param f_set_cache session set callback + */ +void mbedtls_ssl_conf_session_cache(mbedtls_ssl_config *conf, + void *p_cache, + mbedtls_ssl_cache_get_t *f_get_cache, + mbedtls_ssl_cache_set_t *f_set_cache); +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_CLI_C) +/** + * \brief Load a session for session resumption. + * + * Sessions loaded through this call will be considered + * for session resumption in the next handshake. + * + * \note Even if this call succeeds, it is not guaranteed that + * the next handshake will indeed be shortened through the + * use of session resumption: The server is always free + * to reject any attempt for resumption and fall back to + * a full handshake. + * + * \note This function can handle a variety of mechanisms for session + * resumption: For TLS 1.2, both session ID-based resumption and + * ticket-based resumption will be considered. For TLS 1.3, + * once implemented, sessions equate to tickets, and loading + * one or more sessions via this call will lead to their + * corresponding tickets being advertised as resumption PSKs + * by the client. + * + * \note Calling this function multiple times will only be useful + * once TLS 1.3 is supported. For TLS 1.2 connections, this + * function should be called at most once. + * + * \param ssl The SSL context representing the connection which should + * be attempted to be setup using session resumption. This + * must be initialized via mbedtls_ssl_init() and bound to + * an SSL configuration via mbedtls_ssl_setup(), but + * the handshake must not yet have been started. + * \param session The session to be considered for session resumption. + * This must be a session previously exported via + * mbedtls_ssl_get_session(), and potentially serialized and + * deserialized through mbedtls_ssl_session_save() and + * mbedtls_ssl_session_load() in the meantime. + * + * \return \c 0 if successful. + * \return \c MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE if the session + * could not be loaded because of an implementation limitation. + * This error is non-fatal, and has no observable effect on + * the SSL context or the session that was attempted to be loaded. + * \return Another negative error code on other kinds of failure. + * + * \sa mbedtls_ssl_get_session() + * \sa mbedtls_ssl_session_load() + */ +int mbedtls_ssl_set_session(mbedtls_ssl_context *ssl, const mbedtls_ssl_session *session); +#endif /* MBEDTLS_SSL_CLI_C */ + +/** + * \brief Load serialized session data into a session structure. + * On client, this can be used for loading saved sessions + * before resuming them with mbedtls_ssl_set_session(). + * On server, this can be used for alternative implementations + * of session cache or session tickets. + * + * \warning If a peer certificate chain is associated with the session, + * the serialized state will only contain the peer's + * end-entity certificate and the result of the chain + * verification (unless verification was disabled), but not + * the rest of the chain. + * + * \see mbedtls_ssl_session_save() + * \see mbedtls_ssl_set_session() + * + * \param session The session structure to be populated. It must have been + * initialised with mbedtls_ssl_session_init() but not + * populated yet. + * \param buf The buffer holding the serialized session data. It must be a + * readable buffer of at least \p len bytes. + * \param len The size of the serialized data in bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if input data is invalid. + * \return #MBEDTLS_ERR_SSL_VERSION_MISMATCH if the serialized data + * was generated in a different version or configuration of + * Mbed TLS. + * \return Another negative value for other kinds of errors (for + * example, unsupported features in the embedded certificate). + */ +int mbedtls_ssl_session_load(mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len); + +/** + * \brief Save session structure as serialized data in a buffer. + * On client, this can be used for saving session data, + * potentially in non-volatile storage, for resuming later. + * On server, this can be used for alternative implementations + * of session cache or session tickets. + * + * \see mbedtls_ssl_session_load() + * + * \param session The session structure to be saved. + * \param buf The buffer to write the serialized data to. It must be a + * writeable buffer of at least \p len bytes, or may be \c + * NULL if \p len is \c 0. + * \param buf_len The number of bytes available for writing in \p buf. + * \param olen The size in bytes of the data that has been or would have + * been written. It must point to a valid \c size_t. + * + * \note \p olen is updated to the correct value regardless of + * whether \p buf_len was large enough. This makes it possible + * to determine the necessary size by calling this function + * with \p buf set to \c NULL and \p buf_len to \c 0. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL if \p buf is too small. + */ +int mbedtls_ssl_session_save(const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len, + size_t *olen); + +/** + * \brief Set the list of allowed ciphersuites and the preference + * order. First in the list has the highest preference. + * + * For TLS 1.2, the notion of ciphersuite determines both + * the key exchange mechanism and the suite of symmetric + * algorithms to be used during and after the handshake. + * + * For TLS 1.3 (in development), the notion of ciphersuite + * only determines the suite of symmetric algorithms to be + * used during and after the handshake, while key exchange + * mechanisms are configured separately. + * + * In Mbed TLS, ciphersuites for both TLS 1.2 and TLS 1.3 + * are configured via this function. For users of TLS 1.3, + * there will be separate API for the configuration of key + * exchange mechanisms. + * + * The list of ciphersuites passed to this function may + * contain a mixture of TLS 1.2 and TLS 1.3 ciphersuite + * identifiers. This is useful if negotiation of TLS 1.3 + * should be attempted, but a fallback to TLS 1.2 would + * be tolerated. + * + * \note By default, the server chooses its preferred + * ciphersuite among those that the client supports. If + * mbedtls_ssl_conf_preference_order() is called to prefer + * the client's preferences, the server instead chooses + * the client's preferred ciphersuite among those that + * the server supports. + * + * \warning The ciphersuites array \p ciphersuites is not copied. + * It must remain valid for the lifetime of the SSL + * configuration \p conf. + * + * \param conf The SSL configuration to modify. + * \param ciphersuites A 0-terminated list of IANA identifiers of supported + * ciphersuites, accessible through \c MBEDTLS_TLS_XXX + * and \c MBEDTLS_TLS1_3_XXX macros defined in + * ssl_ciphersuites.h. + */ +void mbedtls_ssl_conf_ciphersuites(mbedtls_ssl_config *conf, + const int *ciphersuites); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +/** + * \brief Set the supported key exchange modes for TLS 1.3 connections. + * + * In contrast to TLS 1.2, the ciphersuite concept in TLS 1.3 does not + * include the choice of key exchange mechanism. It is therefore not + * covered by the API mbedtls_ssl_conf_ciphersuites(). See the + * documentation of mbedtls_ssl_conf_ciphersuites() for more + * information on the ciphersuite concept in TLS 1.2 and TLS 1.3. + * + * The present function is specific to TLS 1.3 and allows users to + * configure the set of supported key exchange mechanisms in TLS 1.3. + * + * \param conf The SSL configuration the change should apply to. + * \param kex_modes A bitwise combination of one or more of the following: + * - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK + * This flag enables pure-PSK key exchanges. + * - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL + * This flag enables combined PSK-ephemeral key exchanges. + * - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL + * This flag enables pure-ephemeral key exchanges. + * For convenience, the following pre-defined macros are + * available for combinations of the above: + * - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_ALL + * Includes all of pure-PSK, PSK-ephemeral and pure-ephemeral. + * - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL + * Includes both pure-PSK and combined PSK-ephemeral + * key exchanges, but excludes pure-ephemeral key exchanges. + * - MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ALL + * Includes both pure-ephemeral and combined PSK-ephemeral + * key exchanges. + * + * \note If a PSK-based key exchange mode shall be supported, applications + * must also use the APIs mbedtls_ssl_conf_psk() or + * mbedtls_ssl_conf_psk_cb() or mbedtls_ssl_conf_psk_opaque() + * to configure the PSKs to be used. + * + * \note If a pure-ephemeral key exchange mode shall be supported, + * server-side applications must also provide a certificate via + * mbedtls_ssl_conf_own_cert(). + * + */ + +void mbedtls_ssl_conf_tls13_key_exchange_modes(mbedtls_ssl_config *conf, + const int kex_modes); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +#define MBEDTLS_SSL_UNEXPECTED_CID_IGNORE 0 +#define MBEDTLS_SSL_UNEXPECTED_CID_FAIL 1 +/** + * \brief Specify the length of Connection IDs for incoming + * encrypted DTLS records, as well as the behaviour + * on unexpected CIDs. + * + * By default, the CID length is set to \c 0, + * and unexpected CIDs are silently ignored. + * + * \param conf The SSL configuration to modify. + * \param len The length in Bytes of the CID fields in encrypted + * DTLS records using the CID mechanism. This must + * not be larger than #MBEDTLS_SSL_CID_OUT_LEN_MAX. + * \param ignore_other_cids This determines the stack's behaviour when + * receiving a record with an unexpected CID. + * Possible values are: + * - #MBEDTLS_SSL_UNEXPECTED_CID_IGNORE + * In this case, the record is silently ignored. + * - #MBEDTLS_SSL_UNEXPECTED_CID_FAIL + * In this case, the stack fails with the specific + * error code #MBEDTLS_ERR_SSL_UNEXPECTED_CID. + * + * \note The CID specification allows implementations to either + * use a common length for all incoming connection IDs or + * allow variable-length incoming IDs. Mbed TLS currently + * requires a common length for all connections sharing the + * same SSL configuration; this allows simpler parsing of + * record headers. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if \p own_cid_len + * is too large. + */ +int mbedtls_ssl_conf_cid(mbedtls_ssl_config *conf, size_t len, + int ignore_other_cids); +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * \brief Set the X.509 security profile used for verification + * + * \note The restrictions are enforced for all certificates in the + * chain. However, signatures in the handshake are not covered + * by this setting but by \b mbedtls_ssl_conf_sig_hashes(). + * + * \param conf SSL configuration + * \param profile Profile to use + */ +void mbedtls_ssl_conf_cert_profile(mbedtls_ssl_config *conf, + const mbedtls_x509_crt_profile *profile); + +/** + * \brief Set the data required to verify peer certificate + * + * \note See \c mbedtls_x509_crt_verify() for notes regarding the + * parameters ca_chain (maps to trust_ca for that function) + * and ca_crl. + * + * \param conf SSL configuration + * \param ca_chain trusted CA chain (meaning all fully trusted top-level CAs) + * \param ca_crl trusted CA CRLs + */ +void mbedtls_ssl_conf_ca_chain(mbedtls_ssl_config *conf, + mbedtls_x509_crt *ca_chain, + mbedtls_x509_crl *ca_crl); + +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +/** + * \brief Set DN hints sent to client in CertificateRequest message + * + * \note If not set, subject distinguished names (DNs) are taken + * from \c mbedtls_ssl_conf_ca_chain() + * or \c mbedtls_ssl_set_hs_ca_chain()) + * + * \param conf SSL configuration + * \param crt crt chain whose subject DNs are issuer DNs of client certs + * from which the client should select client peer certificate. + */ +static inline +void mbedtls_ssl_conf_dn_hints(mbedtls_ssl_config *conf, + const mbedtls_x509_crt *crt) +{ + conf->MBEDTLS_PRIVATE(dn_hints) = crt; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) +/** + * \brief Set the trusted certificate callback. + * + * This API allows to register the set of trusted certificates + * through a callback, instead of a linked list as configured + * by mbedtls_ssl_conf_ca_chain(). + * + * This is useful for example in contexts where a large number + * of CAs are used, and the inefficiency of maintaining them + * in a linked list cannot be tolerated. It is also useful when + * the set of trusted CAs needs to be modified frequently. + * + * See the documentation of `mbedtls_x509_crt_ca_cb_t` for + * more information. + * + * \param conf The SSL configuration to register the callback with. + * \param f_ca_cb The trusted certificate callback to use when verifying + * certificate chains. + * \param p_ca_cb The context to be passed to \p f_ca_cb (for example, + * a reference to a trusted CA database). + * + * \note This API is incompatible with mbedtls_ssl_conf_ca_chain(): + * Any call to this function overwrites the values set through + * earlier calls to mbedtls_ssl_conf_ca_chain() or + * mbedtls_ssl_conf_ca_cb(). + * + * \note This API is incompatible with CA indication in + * CertificateRequest messages: A server-side SSL context which + * is bound to an SSL configuration that uses a CA callback + * configured via mbedtls_ssl_conf_ca_cb(), and which requires + * client authentication, will send an empty CA list in the + * corresponding CertificateRequest message. + * + * \note This API is incompatible with mbedtls_ssl_set_hs_ca_chain(): + * If an SSL context is bound to an SSL configuration which uses + * CA callbacks configured via mbedtls_ssl_conf_ca_cb(), then + * calls to mbedtls_ssl_set_hs_ca_chain() have no effect. + * + * \note The use of this API disables the use of restartable ECC + * during X.509 CRT signature verification (but doesn't affect + * other uses). + * + * \warning This API is incompatible with the use of CRLs. Any call to + * mbedtls_ssl_conf_ca_cb() unsets CRLs configured through + * earlier calls to mbedtls_ssl_conf_ca_chain(). + * + * \warning In multi-threaded environments, the callback \p f_ca_cb + * must be thread-safe, and it is the user's responsibility + * to guarantee this (for example through a mutex + * contained in the callback context pointed to by \p p_ca_cb). + */ +void mbedtls_ssl_conf_ca_cb(mbedtls_ssl_config *conf, + mbedtls_x509_crt_ca_cb_t f_ca_cb, + void *p_ca_cb); +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ + +/** + * \brief Set own certificate chain and private key + * + * \note own_cert should contain in order from the bottom up your + * certificate chain. The top certificate (self-signed) + * can be omitted. + * + * \note On server, this function can be called multiple times to + * provision more than one cert/key pair (eg one ECDSA, one + * RSA with SHA-256, one RSA with SHA-1). An adequate + * certificate will be selected according to the client's + * advertised capabilities. In case multiple certificates are + * adequate, preference is given to the one set by the first + * call to this function, then second, etc. + * + * \note On client, only the first call has any effect. That is, + * only one client certificate can be provisioned. The + * server's preferences in its CertificateRequest message will + * be ignored and our only cert will be sent regardless of + * whether it matches those preferences - the server can then + * decide what it wants to do with it. + * + * \note The provided \p pk_key needs to match the public key in the + * first certificate in \p own_cert, or all handshakes using + * that certificate will fail. It is your responsibility + * to ensure that; this function will not perform any check. + * You may use mbedtls_pk_check_pair() in order to perform + * this check yourself, but be aware that this function can + * be computationally expensive on some key types. + * + * \param conf SSL configuration + * \param own_cert own public certificate chain + * \param pk_key own private key + * + * \return 0 on success or MBEDTLS_ERR_SSL_ALLOC_FAILED + */ +int mbedtls_ssl_conf_own_cert(mbedtls_ssl_config *conf, + mbedtls_x509_crt *own_cert, + mbedtls_pk_context *pk_key); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) +/** + * \brief Configure pre-shared keys (PSKs) and their + * identities to be used in PSK-based ciphersuites. + * + * Only one PSK can be registered, through either + * mbedtls_ssl_conf_psk() or mbedtls_ssl_conf_psk_opaque(). + * If you attempt to register more than one PSK, this function + * fails, though this may change in future versions, which + * may add support for multiple PSKs. + * + * \note This is mainly useful for clients. Servers will usually + * want to use \c mbedtls_ssl_conf_psk_cb() instead. + * + * \note A PSK set by \c mbedtls_ssl_set_hs_psk() in the PSK callback + * takes precedence over a PSK configured by this function. + * + * \param conf The SSL configuration to register the PSK with. + * \param psk The pointer to the pre-shared key to use. + * \param psk_len The length of the pre-shared key in bytes. + * \param psk_identity The pointer to the pre-shared key identity. + * \param psk_identity_len The length of the pre-shared key identity + * in bytes. + * + * \note The PSK and its identity are copied internally and + * hence need not be preserved by the caller for the lifetime + * of the SSL configuration. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE if no more PSKs + * can be configured. In this case, the old PSK(s) remain intact. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ssl_conf_psk(mbedtls_ssl_config *conf, + const unsigned char *psk, size_t psk_len, + const unsigned char *psk_identity, size_t psk_identity_len); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * \brief Configure one or more opaque pre-shared keys (PSKs) and + * their identities to be used in PSK-based ciphersuites. + * + * Only one PSK can be registered, through either + * mbedtls_ssl_conf_psk() or mbedtls_ssl_conf_psk_opaque(). + * If you attempt to register more than one PSK, this function + * fails, though this may change in future versions, which + * may add support for multiple PSKs. + * + * \note This is mainly useful for clients. Servers will usually + * want to use \c mbedtls_ssl_conf_psk_cb() instead. + * + * \note An opaque PSK set by \c mbedtls_ssl_set_hs_psk_opaque() in + * the PSK callback takes precedence over an opaque PSK + * configured by this function. + * + * \param conf The SSL configuration to register the PSK with. + * \param psk The identifier of the key slot holding the PSK. + * Until \p conf is destroyed or this function is successfully + * called again, the key slot \p psk must be populated with a + * key of type PSA_ALG_CATEGORY_KEY_DERIVATION whose policy + * allows its use for the key derivation algorithm applied + * in the handshake. + * \param psk_identity The pointer to the pre-shared key identity. + * \param psk_identity_len The length of the pre-shared key identity + * in bytes. + * + * \note The PSK identity hint is copied internally and hence need + * not be preserved by the caller for the lifetime of the + * SSL configuration. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE if no more PSKs + * can be configured. In this case, the old PSK(s) remain intact. + * \return Another negative error code on other kinds of failure. + */ +int mbedtls_ssl_conf_psk_opaque(mbedtls_ssl_config *conf, + mbedtls_svc_key_id_t psk, + const unsigned char *psk_identity, + size_t psk_identity_len); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +/** + * \brief Set the pre-shared Key (PSK) for the current handshake. + * + * \note This should only be called inside the PSK callback, + * i.e. the function passed to \c mbedtls_ssl_conf_psk_cb(). + * + * \note A PSK set by this function takes precedence over a PSK + * configured by \c mbedtls_ssl_conf_psk(). + * + * \param ssl The SSL context to configure a PSK for. + * \param psk The pointer to the pre-shared key. + * \param psk_len The length of the pre-shared key in bytes. + * + * \return \c 0 if successful. + * \return An \c MBEDTLS_ERR_SSL_XXX error code on failure. + */ +int mbedtls_ssl_set_hs_psk(mbedtls_ssl_context *ssl, + const unsigned char *psk, size_t psk_len); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * \brief Set an opaque pre-shared Key (PSK) for the current handshake. + * + * \note This should only be called inside the PSK callback, + * i.e. the function passed to \c mbedtls_ssl_conf_psk_cb(). + * + * \note An opaque PSK set by this function takes precedence over an + * opaque PSK configured by \c mbedtls_ssl_conf_psk_opaque(). + * + * \param ssl The SSL context to configure a PSK for. + * \param psk The identifier of the key slot holding the PSK. + * For the duration of the current handshake, the key slot + * must be populated with a key of type + * PSA_ALG_CATEGORY_KEY_DERIVATION whose policy allows its + * use for the key derivation algorithm + * applied in the handshake. + * + * \return \c 0 if successful. + * \return An \c MBEDTLS_ERR_SSL_XXX error code on failure. + */ +int mbedtls_ssl_set_hs_psk_opaque(mbedtls_ssl_context *ssl, + mbedtls_svc_key_id_t psk); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Set the PSK callback (server-side only). + * + * If set, the PSK callback is called for each + * handshake where a PSK-based ciphersuite was negotiated. + * The caller provides the identity received and wants to + * receive the actual PSK data and length. + * + * The callback has the following parameters: + * - \c void*: The opaque pointer \p p_psk. + * - \c mbedtls_ssl_context*: The SSL context to which + * the operation applies. + * - \c const unsigned char*: The PSK identity + * selected by the client. + * - \c size_t: The length of the PSK identity + * selected by the client. + * + * If a valid PSK identity is found, the callback should use + * \c mbedtls_ssl_set_hs_psk() or + * \c mbedtls_ssl_set_hs_psk_opaque() + * on the SSL context to set the correct PSK and return \c 0. + * Any other return value will result in a denied PSK identity. + * + * \note A dynamic PSK (i.e. set by the PSK callback) takes + * precedence over a static PSK (i.e. set by + * \c mbedtls_ssl_conf_psk() or + * \c mbedtls_ssl_conf_psk_opaque()). + * This means that if you set a PSK callback using this + * function, you don't need to set a PSK using + * \c mbedtls_ssl_conf_psk() or + * \c mbedtls_ssl_conf_psk_opaque()). + * + * \param conf The SSL configuration to register the callback with. + * \param f_psk The callback for selecting and setting the PSK based + * in the PSK identity chosen by the client. + * \param p_psk A pointer to an opaque structure to be passed to + * the callback, for example a PSK store. + */ +void mbedtls_ssl_conf_psk_cb(mbedtls_ssl_config *conf, + int (*f_psk)(void *, mbedtls_ssl_context *, const unsigned char *, + size_t), + void *p_psk); +#endif /* MBEDTLS_SSL_SRV_C */ +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ + +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Set the Diffie-Hellman public P and G values + * from big-endian binary presentations. + * (Default values: MBEDTLS_DHM_RFC3526_MODP_2048_[PG]_BIN) + * + * \param conf SSL configuration + * \param dhm_P Diffie-Hellman-Merkle modulus in big-endian binary form + * \param P_len Length of DHM modulus + * \param dhm_G Diffie-Hellman-Merkle generator in big-endian binary form + * \param G_len Length of DHM generator + * + * \return 0 if successful + */ +int mbedtls_ssl_conf_dh_param_bin(mbedtls_ssl_config *conf, + const unsigned char *dhm_P, size_t P_len, + const unsigned char *dhm_G, size_t G_len); + +/** + * \brief Set the Diffie-Hellman public P and G values, + * read from existing context (server-side only) + * + * \param conf SSL configuration + * \param dhm_ctx Diffie-Hellman-Merkle context + * + * \return 0 if successful + */ +int mbedtls_ssl_conf_dh_param_ctx(mbedtls_ssl_config *conf, mbedtls_dhm_context *dhm_ctx); +#endif /* MBEDTLS_DHM_C && defined(MBEDTLS_SSL_SRV_C) */ + +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_CLI_C) +/** + * \brief Set the minimum length for Diffie-Hellman parameters. + * (Client-side only.) + * (Default: 1024 bits.) + * + * \param conf SSL configuration + * \param bitlen Minimum bit length of the DHM prime + */ +void mbedtls_ssl_conf_dhm_min_bitlen(mbedtls_ssl_config *conf, + unsigned int bitlen); +#endif /* MBEDTLS_DHM_C && MBEDTLS_SSL_CLI_C */ + +#if defined(MBEDTLS_ECP_C) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief Set the allowed curves in order of preference. + * + * On server: this only affects selection of the ECDHE curve; + * the curves used for ECDH and ECDSA are determined by the + * list of available certificates instead. + * + * On client: this affects the list of curves offered for any + * use. The server can override our preference order. + * + * Both sides: limits the set of curves accepted for use in + * ECDHE and in the peer's end-entity certificate. + * + * \deprecated Superseded by mbedtls_ssl_conf_groups(). + * + * \note This has no influence on which curves are allowed inside the + * certificate chains, see \c mbedtls_ssl_conf_cert_profile() + * for that. For the end-entity certificate however, the key + * will be accepted only if it is allowed both by this list + * and by the cert profile. + * + * \note This list should be ordered by decreasing preference + * (preferred curve first). + * + * \note The default list is the same set of curves that + * #mbedtls_x509_crt_profile_default allows, plus + * ECDHE-only curves selected according to the same criteria. + * The order favors curves with the lowest resource usage. + * + * \note New minor versions of Mbed TLS may extend this list, + * for example if new curves are added to the library. + * New minor versions of Mbed TLS will not remove items + * from this list unless serious security concerns require it. + * New minor versions of Mbed TLS may change the order in + * keeping with the general principle of favoring the lowest + * resource usage. + * + * \param conf SSL configuration + * \param curves Ordered list of allowed curves, + * terminated by MBEDTLS_ECP_DP_NONE. + */ +void MBEDTLS_DEPRECATED mbedtls_ssl_conf_curves(mbedtls_ssl_config *conf, + const mbedtls_ecp_group_id *curves); +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_ECP_C */ + +/** + * \brief Set the allowed groups in order of preference. + * + * On server: This only affects the choice of key agreement mechanism + * + * On client: this affects the list of groups offered for any + * use. The server can override our preference order. + * + * Both sides: limits the set of groups accepted for use in + * key sharing. + * + * \note This function replaces the deprecated mbedtls_ssl_conf_curves(), + * which only allows ECP curves to be configured. + * + * \note The most recent invocation of either mbedtls_ssl_conf_curves() + * or mbedtls_ssl_conf_groups() nullifies all previous invocations + * of both. + * + * \note This list should be ordered by decreasing preference + * (preferred group first). + * + * \note When this function is not called, a default list is used, + * consisting of all supported curves at 255 bits and above, + * and all supported finite fields at 2048 bits and above. + * The order favors groups with the lowest resource usage. + * + * \note New minor versions of Mbed TLS will not remove items + * from the default list unless serious security concerns require it. + * New minor versions of Mbed TLS may change the order in + * keeping with the general principle of favoring the lowest + * resource usage. + * + * \param conf SSL configuration + * \param groups List of allowed groups ordered by preference, terminated by 0. + * Must contain valid IANA NamedGroup IDs (provided via either an integer + * or using MBEDTLS_TLS1_3_NAMED_GROUP_XXX macros). + */ +void mbedtls_ssl_conf_groups(mbedtls_ssl_config *conf, + const uint16_t *groups); + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) && defined(MBEDTLS_SSL_PROTO_TLS1_2) +/** + * \brief Set the allowed hashes for signatures during the handshake. + * + * \note This only affects which hashes are offered and can be used + * for signatures during the handshake. Hashes for message + * authentication and the TLS PRF are controlled by the + * ciphersuite, see \c mbedtls_ssl_conf_ciphersuites(). Hashes + * used for certificate signature are controlled by the + * verification profile, see \c mbedtls_ssl_conf_cert_profile(). + * + * \note This list should be ordered by decreasing preference + * (preferred hash first). + * + * \note By default, all supported hashes whose length is at least + * 256 bits are allowed. This is the same set as the default + * for certificate verification + * (#mbedtls_x509_crt_profile_default). + * The preference order is currently unspecified and may + * change in future versions. + * + * \note New minor versions of Mbed TLS may extend this list, + * for example if new curves are added to the library. + * New minor versions of Mbed TLS will not remove items + * from this list unless serious security concerns require it. + * + * \param conf SSL configuration + * \param hashes Ordered list of allowed signature hashes, + * terminated by \c MBEDTLS_MD_NONE. + */ +void MBEDTLS_DEPRECATED mbedtls_ssl_conf_sig_hashes(mbedtls_ssl_config *conf, + const int *hashes); +#endif /* !MBEDTLS_DEPRECATED_REMOVED && MBEDTLS_SSL_PROTO_TLS1_2 */ + +/** + * \brief Configure allowed signature algorithms for use in TLS 1.3 + * + * \param conf The SSL configuration to use. + * \param sig_algs List of allowed IANA values for TLS 1.3 signature algorithms, + * terminated by \c MBEDTLS_TLS1_3_SIG_NONE. The list must remain + * available throughout the lifetime of the conf object. Supported + * values are available as \c MBEDTLS_TLS1_3_SIG_XXXX + */ +void mbedtls_ssl_conf_sig_algs(mbedtls_ssl_config *conf, + const uint16_t *sig_algs); +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * \brief Set or reset the hostname to check against the received + * server certificate. It sets the ServerName TLS extension, + * too, if that extension is enabled. (client-side only) + * + * \param ssl SSL context + * \param hostname the server hostname, may be NULL to clear hostname + + * \note Maximum hostname length MBEDTLS_SSL_MAX_HOST_NAME_LEN. + * + * \return 0 if successful, MBEDTLS_ERR_SSL_ALLOC_FAILED on + * allocation failure, MBEDTLS_ERR_SSL_BAD_INPUT_DATA on + * too long input hostname. + * + * Hostname set to the one provided on success (cleared + * when NULL). On allocation failure hostname is cleared. + * On too long input failure, old hostname is unchanged. + */ +int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +/** + * \brief Retrieve SNI extension value for the current handshake. + * Available in \p f_cert_cb of \c mbedtls_ssl_conf_cert_cb(), + * this is the same value passed to \p f_sni callback of + * \c mbedtls_ssl_conf_sni() and may be used instead of + * \c mbedtls_ssl_conf_sni(). + * + * \param ssl SSL context + * \param name_len pointer into which to store length of returned value. + * 0 if SNI extension is not present or not yet processed. + * + * \return const pointer to SNI extension value. + * - value is valid only when called in \p f_cert_cb + * registered with \c mbedtls_ssl_conf_cert_cb(). + * - value is NULL if SNI extension is not present. + * - value is not '\0'-terminated. Use \c name_len for len. + * - value must not be freed. + */ +const unsigned char *mbedtls_ssl_get_hs_sni(mbedtls_ssl_context *ssl, + size_t *name_len); + +/** + * \brief Set own certificate and key for the current handshake + * + * \note Same as \c mbedtls_ssl_conf_own_cert() but for use within + * the SNI callback or the certificate selection callback. + * + * \note Passing null \c own_cert clears the certificate list for + * the current handshake. + * + * \param ssl SSL context + * \param own_cert own public certificate chain + * \param pk_key own private key + * + * \return 0 on success or MBEDTLS_ERR_SSL_ALLOC_FAILED + */ +int mbedtls_ssl_set_hs_own_cert(mbedtls_ssl_context *ssl, + mbedtls_x509_crt *own_cert, + mbedtls_pk_context *pk_key); + +/** + * \brief Set the data required to verify peer certificate for the + * current handshake + * + * \note Same as \c mbedtls_ssl_conf_ca_chain() but for use within + * the SNI callback or the certificate selection callback. + * + * \param ssl SSL context + * \param ca_chain trusted CA chain (meaning all fully trusted top-level CAs) + * \param ca_crl trusted CA CRLs + */ +void mbedtls_ssl_set_hs_ca_chain(mbedtls_ssl_context *ssl, + mbedtls_x509_crt *ca_chain, + mbedtls_x509_crl *ca_crl); + +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +/** + * \brief Set DN hints sent to client in CertificateRequest message + * + * \note Same as \c mbedtls_ssl_conf_dn_hints() but for use within + * the SNI callback or the certificate selection callback. + * + * \param ssl SSL context + * \param crt crt chain whose subject DNs are issuer DNs of client certs + * from which the client should select client peer certificate. + */ +void mbedtls_ssl_set_hs_dn_hints(mbedtls_ssl_context *ssl, + const mbedtls_x509_crt *crt); +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + +/** + * \brief Set authmode for the current handshake. + * + * \note Same as \c mbedtls_ssl_conf_authmode() but for use within + * the SNI callback or the certificate selection callback. + * + * \param ssl SSL context + * \param authmode MBEDTLS_SSL_VERIFY_NONE, MBEDTLS_SSL_VERIFY_OPTIONAL or + * MBEDTLS_SSL_VERIFY_REQUIRED + */ +void mbedtls_ssl_set_hs_authmode(mbedtls_ssl_context *ssl, + int authmode); + +/** + * \brief Set server side ServerName TLS extension callback + * (optional, server-side only). + * + * If set, the ServerName callback is called whenever the + * server receives a ServerName TLS extension from the client + * during a handshake. The ServerName callback has the + * following parameters: (void *parameter, mbedtls_ssl_context *ssl, + * const unsigned char *hostname, size_t len). If a suitable + * certificate is found, the callback must set the + * certificate(s) and key(s) to use with \c + * mbedtls_ssl_set_hs_own_cert() (can be called repeatedly), + * and may optionally adjust the CA and associated CRL with \c + * mbedtls_ssl_set_hs_ca_chain() as well as the client + * authentication mode with \c mbedtls_ssl_set_hs_authmode(), + * then must return 0. If no matching name is found, the + * callback may return non-zero to abort the handshake. + * + * \param conf SSL configuration + * \param f_sni verification function + * \param p_sni verification parameter + */ +void mbedtls_ssl_conf_sni(mbedtls_ssl_config *conf, + int (*f_sni)(void *, mbedtls_ssl_context *, const unsigned char *, + size_t), + void *p_sni); +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +/** + * \brief Set the EC J-PAKE password for current handshake. + * + * \note An internal copy is made, and destroyed as soon as the + * handshake is completed, or when the SSL context is reset or + * freed. + * + * \note The SSL context needs to be already set up. The right place + * to call this function is between \c mbedtls_ssl_setup() or + * \c mbedtls_ssl_reset() and \c mbedtls_ssl_handshake(). + * Password cannot be empty (see RFC 8236). + * + * \param ssl SSL context + * \param pw EC J-PAKE password (pre-shared secret). It cannot be empty + * \param pw_len length of pw in bytes + * + * \return 0 on success, or a negative error code. + */ +int mbedtls_ssl_set_hs_ecjpake_password(mbedtls_ssl_context *ssl, + const unsigned char *pw, + size_t pw_len); + +/** + * \brief Set the EC J-PAKE opaque password for current handshake. + * + * \note The key must remain valid until the handshake is over. + * + * \note The SSL context needs to be already set up. The right place + * to call this function is between \c mbedtls_ssl_setup() or + * \c mbedtls_ssl_reset() and \c mbedtls_ssl_handshake(). + * + * \param ssl SSL context + * \param pwd EC J-PAKE opaque password + * + * \return 0 on success, or a negative error code. + */ +int mbedtls_ssl_set_hs_ecjpake_password_opaque(mbedtls_ssl_context *ssl, + mbedtls_svc_key_id_t pwd); +#endif /*MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_ALPN) +/** + * \brief Set the supported Application Layer Protocols. + * + * \param conf SSL configuration + * \param protos Pointer to a NULL-terminated list of supported protocols, + * in decreasing preference order. The pointer to the list is + * recorded by the library for later reference as required, so + * the lifetime of the table must be at least as long as the + * lifetime of the SSL configuration structure. + * + * \return 0 on success, or MBEDTLS_ERR_SSL_BAD_INPUT_DATA. + */ +int mbedtls_ssl_conf_alpn_protocols(mbedtls_ssl_config *conf, const char **protos); + +/** + * \brief Get the name of the negotiated Application Layer Protocol. + * This function should be called after the handshake is + * completed. + * + * \param ssl SSL context + * + * \return Protocol name, or NULL if no protocol was negotiated. + */ +const char *mbedtls_ssl_get_alpn_protocol(const mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) +#if defined(MBEDTLS_DEBUG_C) +static inline const char *mbedtls_ssl_get_srtp_profile_as_string(mbedtls_ssl_srtp_profile profile) +{ + switch (profile) { + case MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80: + return "MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80"; + case MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_32: + return "MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_32"; + case MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_80: + return "MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_80"; + case MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_32: + return "MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_32"; + default: break; + } + return ""; +} +#endif /* MBEDTLS_DEBUG_C */ +/** + * \brief Manage support for mki(master key id) value + * in use_srtp extension. + * MKI is an optional part of SRTP used for key management + * and re-keying. See RFC3711 section 3.1 for details. + * The default value is + * #MBEDTLS_SSL_DTLS_SRTP_MKI_UNSUPPORTED. + * + * \param conf The SSL configuration to manage mki support. + * \param support_mki_value Enable or disable mki usage. Values are + * #MBEDTLS_SSL_DTLS_SRTP_MKI_UNSUPPORTED + * or #MBEDTLS_SSL_DTLS_SRTP_MKI_SUPPORTED. + */ +void mbedtls_ssl_conf_srtp_mki_value_supported(mbedtls_ssl_config *conf, + int support_mki_value); + +/** + * \brief Set the supported DTLS-SRTP protection profiles. + * + * \param conf SSL configuration + * \param profiles Pointer to a List of MBEDTLS_TLS_SRTP_UNSET terminated + * supported protection profiles + * in decreasing preference order. + * The pointer to the list is recorded by the library + * for later reference as required, so the lifetime + * of the table must be at least as long as the lifetime + * of the SSL configuration structure. + * The list must not hold more than + * MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH elements + * (excluding the terminating MBEDTLS_TLS_SRTP_UNSET). + * + * \return 0 on success + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA when the list of + * protection profiles is incorrect. + */ +int mbedtls_ssl_conf_dtls_srtp_protection_profiles + (mbedtls_ssl_config *conf, + const mbedtls_ssl_srtp_profile *profiles); + +/** + * \brief Set the mki_value for the current DTLS-SRTP session. + * + * \param ssl SSL context to use. + * \param mki_value The MKI value to set. + * \param mki_len The length of the MKI value. + * + * \note This function is relevant on client side only. + * The server discovers the mki value during handshake. + * A mki value set on server side using this function + * is ignored. + * + * \return 0 on success + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA + * \return #MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE + */ +int mbedtls_ssl_dtls_srtp_set_mki_value(mbedtls_ssl_context *ssl, + unsigned char *mki_value, + uint16_t mki_len); +/** + * \brief Get the negotiated DTLS-SRTP information: + * Protection profile and MKI value. + * + * \warning This function must be called after the handshake is + * completed. The value returned by this function must + * not be trusted or acted upon before the handshake completes. + * + * \param ssl The SSL context to query. + * \param dtls_srtp_info The negotiated DTLS-SRTP information: + * - Protection profile in use. + * A direct mapping of the iana defined value for protection + * profile on an uint16_t. + http://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml + * #MBEDTLS_TLS_SRTP_UNSET if the use of SRTP was not negotiated + * or peer's Hello packet was not parsed yet. + * - mki size and value( if size is > 0 ). + */ +void mbedtls_ssl_get_dtls_srtp_negotiation_result(const mbedtls_ssl_context *ssl, + mbedtls_dtls_srtp_info *dtls_srtp_info); +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief Set the maximum supported version sent from the client side + * and/or accepted at the server side. + * + * See also the documentation of mbedtls_ssl_conf_min_version(). + * + * \note This ignores ciphersuites from higher versions. + * + * \note This function is deprecated and has been replaced by + * \c mbedtls_ssl_conf_max_tls_version(). + * + * \param conf SSL configuration + * \param major Major version number (#MBEDTLS_SSL_MAJOR_VERSION_3) + * \param minor Minor version number + * (#MBEDTLS_SSL_MINOR_VERSION_3 for (D)TLS 1.2, + * #MBEDTLS_SSL_MINOR_VERSION_4 for TLS 1.3) + */ +void MBEDTLS_DEPRECATED mbedtls_ssl_conf_max_version(mbedtls_ssl_config *conf, int major, + int minor); +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +/** + * \brief Set the maximum supported version sent from the client side + * and/or accepted at the server side. + * + * \note After the handshake, you can call + * mbedtls_ssl_get_version_number() to see what version was + * negotiated. + * + * \param conf SSL configuration + * \param tls_version TLS protocol version number (\p mbedtls_ssl_protocol_version) + * (#MBEDTLS_SSL_VERSION_UNKNOWN is not valid) + */ +static inline void mbedtls_ssl_conf_max_tls_version(mbedtls_ssl_config *conf, + mbedtls_ssl_protocol_version tls_version) +{ + conf->MBEDTLS_PRIVATE(max_tls_version) = tls_version; +} + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief Set the minimum accepted SSL/TLS protocol version + * + * \note By default, all supported versions are accepted. + * Future versions of the library may disable older + * protocol versions by default if they become deprecated. + * + * \note The following versions are supported (if enabled at + * compile time): + * - (D)TLS 1.2: \p major = #MBEDTLS_SSL_MAJOR_VERSION_3, + * \p minor = #MBEDTLS_SSL_MINOR_VERSION_3 + * - TLS 1.3: \p major = #MBEDTLS_SSL_MAJOR_VERSION_3, + * \p minor = #MBEDTLS_SSL_MINOR_VERSION_4 + * + * Note that the numbers in the constant names are the + * TLS internal protocol numbers, and the minor versions + * differ by one from the human-readable versions! + * + * \note Input outside of the SSL_MAX_XXXXX_VERSION and + * SSL_MIN_XXXXX_VERSION range is ignored. + * + * \note After the handshake, you can call + * mbedtls_ssl_get_version_number() to see what version was + * negotiated. + * + * \note This function is deprecated and has been replaced by + * \c mbedtls_ssl_conf_min_tls_version(). + * + * \param conf SSL configuration + * \param major Major version number (#MBEDTLS_SSL_MAJOR_VERSION_3) + * \param minor Minor version number + * (#MBEDTLS_SSL_MINOR_VERSION_3 for (D)TLS 1.2, + * #MBEDTLS_SSL_MINOR_VERSION_4 for TLS 1.3) + */ +void MBEDTLS_DEPRECATED mbedtls_ssl_conf_min_version(mbedtls_ssl_config *conf, int major, + int minor); +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +/** + * \brief Set the minimum supported version sent from the client side + * and/or accepted at the server side. + * + * \note After the handshake, you can call + * mbedtls_ssl_get_version_number() to see what version was + * negotiated. + * + * \param conf SSL configuration + * \param tls_version TLS protocol version number (\p mbedtls_ssl_protocol_version) + * (#MBEDTLS_SSL_VERSION_UNKNOWN is not valid) + */ +static inline void mbedtls_ssl_conf_min_tls_version(mbedtls_ssl_config *conf, + mbedtls_ssl_protocol_version tls_version) +{ + conf->MBEDTLS_PRIVATE(min_tls_version) = tls_version; +} + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +/** + * \brief Enable or disable Encrypt-then-MAC + * (Default: MBEDTLS_SSL_ETM_ENABLED) + * + * \note This should always be enabled, it is a security + * improvement, and should not cause any interoperability + * issue (used only if the peer supports it too). + * + * \param conf SSL configuration + * \param etm MBEDTLS_SSL_ETM_ENABLED or MBEDTLS_SSL_ETM_DISABLED + */ +void mbedtls_ssl_conf_encrypt_then_mac(mbedtls_ssl_config *conf, char etm); +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) +/** + * \brief Enable or disable Extended Master Secret negotiation. + * (Default: MBEDTLS_SSL_EXTENDED_MS_ENABLED) + * + * \note This should always be enabled, it is a security fix to the + * protocol, and should not cause any interoperability issue + * (used only if the peer supports it too). + * + * \param conf SSL configuration + * \param ems MBEDTLS_SSL_EXTENDED_MS_ENABLED or MBEDTLS_SSL_EXTENDED_MS_DISABLED + */ +void mbedtls_ssl_conf_extended_master_secret(mbedtls_ssl_config *conf, char ems); +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Whether to send a list of acceptable CAs in + * CertificateRequest messages. + * (Default: do send) + * + * \param conf SSL configuration + * \param cert_req_ca_list MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED or + * MBEDTLS_SSL_CERT_REQ_CA_LIST_DISABLED + */ +void mbedtls_ssl_conf_cert_req_ca_list(mbedtls_ssl_config *conf, + char cert_req_ca_list); +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +/** + * \brief Set the maximum fragment length to emit and/or negotiate. + * (Typical: the smaller of #MBEDTLS_SSL_IN_CONTENT_LEN and + * #MBEDTLS_SSL_OUT_CONTENT_LEN, usually `2^14` bytes) + * (Server: set maximum fragment length to emit, + * usually negotiated by the client during handshake) + * (Client: set maximum fragment length to emit *and* + * negotiate with the server during handshake) + * (Default: #MBEDTLS_SSL_MAX_FRAG_LEN_NONE) + * + * \note On the client side, the maximum fragment length extension + * *will not* be used, unless the maximum fragment length has + * been set via this function to a value different than + * #MBEDTLS_SSL_MAX_FRAG_LEN_NONE. + * + * \note With TLS, this currently only affects ApplicationData (sent + * with \c mbedtls_ssl_read()), not handshake messages. + * With DTLS, this affects both ApplicationData and handshake. + * + * \note This sets the maximum length for a record's payload, + * excluding record overhead that will be added to it, see + * \c mbedtls_ssl_get_record_expansion(). + * + * \note For DTLS, it is also possible to set a limit for the total + * size of datagrams passed to the transport layer, including + * record overhead, see \c mbedtls_ssl_set_mtu(). + * + * \param conf SSL configuration + * \param mfl_code Code for maximum fragment length (allowed values: + * MBEDTLS_SSL_MAX_FRAG_LEN_512, MBEDTLS_SSL_MAX_FRAG_LEN_1024, + * MBEDTLS_SSL_MAX_FRAG_LEN_2048, MBEDTLS_SSL_MAX_FRAG_LEN_4096) + * + * \return 0 if successful or MBEDTLS_ERR_SSL_BAD_INPUT_DATA + */ +int mbedtls_ssl_conf_max_frag_len(mbedtls_ssl_config *conf, unsigned char mfl_code); +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Pick the ciphersuites order according to the second parameter + * in the SSL Server module (MBEDTLS_SSL_SRV_C). + * (Default, if never called: MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_SERVER) + * + * \param conf SSL configuration + * \param order Server or client (MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_SERVER + * or MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_CLIENT) + */ +void mbedtls_ssl_conf_preference_order(mbedtls_ssl_config *conf, int order); +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_CLI_C) +/** + * \brief Enable / Disable session tickets (client only). + * (Default: MBEDTLS_SSL_SESSION_TICKETS_ENABLED.) + * + * \note On server, use \c mbedtls_ssl_conf_session_tickets_cb(). + * + * \param conf SSL configuration + * \param use_tickets Enable or disable (MBEDTLS_SSL_SESSION_TICKETS_ENABLED or + * MBEDTLS_SSL_SESSION_TICKETS_DISABLED) + */ +void mbedtls_ssl_conf_session_tickets(mbedtls_ssl_config *conf, int use_tickets); +#endif /* MBEDTLS_SSL_SESSION_TICKETS && + MBEDTLS_SSL_CLI_C */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SRV_C) && \ + defined(MBEDTLS_SSL_PROTO_TLS1_3) +/** + * \brief Number of NewSessionTicket messages for the server to send + * after handshake completion. + * + * \note The default value is + * \c MBEDTLS_SSL_TLS1_3_DEFAULT_NEW_SESSION_TICKETS. + * + * \note In case of a session resumption, this setting only partially apply. + * At most one ticket is sent in that case to just renew the pool of + * tickets of the client. The rationale is to avoid the number of + * tickets on the server to become rapidly out of control when the + * server has the same configuration for all its connection instances. + * + * \param conf SSL configuration + * \param num_tickets Number of NewSessionTicket. + * + */ +void mbedtls_ssl_conf_new_session_tickets(mbedtls_ssl_config *conf, + uint16_t num_tickets); +#endif /* MBEDTLS_SSL_SESSION_TICKETS && + MBEDTLS_SSL_SRV_C && + MBEDTLS_SSL_PROTO_TLS1_3*/ + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +/** + * \brief Enable / Disable renegotiation support for connection when + * initiated by peer + * (Default: MBEDTLS_SSL_RENEGOTIATION_DISABLED) + * + * \warning It is recommended to always disable renegotiation unless you + * know you need it and you know what you're doing. In the + * past, there have been several issues associated with + * renegotiation or a poor understanding of its properties. + * + * \note Server-side, enabling renegotiation also makes the server + * susceptible to a resource DoS by a malicious client. + * + * \param conf SSL configuration + * \param renegotiation Enable or disable (MBEDTLS_SSL_RENEGOTIATION_ENABLED or + * MBEDTLS_SSL_RENEGOTIATION_DISABLED) + */ +void mbedtls_ssl_conf_renegotiation(mbedtls_ssl_config *conf, int renegotiation); +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +/** + * \brief Prevent or allow legacy renegotiation. + * (Default: MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION) + * + * MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION allows connections to + * be established even if the peer does not support + * secure renegotiation, but does not allow renegotiation + * to take place if not secure. + * (Interoperable and secure option) + * + * MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION allows renegotiations + * with non-upgraded peers. Allowing legacy renegotiation + * makes the connection vulnerable to specific man in the + * middle attacks. (See RFC 5746) + * (Most interoperable and least secure option) + * + * MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE breaks off connections + * if peer does not support secure renegotiation. Results + * in interoperability issues with non-upgraded peers + * that do not support renegotiation altogether. + * (Most secure option, interoperability issues) + * + * \param conf SSL configuration + * \param allow_legacy Prevent or allow (SSL_NO_LEGACY_RENEGOTIATION, + * SSL_ALLOW_LEGACY_RENEGOTIATION or + * MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE) + */ +void mbedtls_ssl_conf_legacy_renegotiation(mbedtls_ssl_config *conf, int allow_legacy); + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +/** + * \brief Enforce renegotiation requests. + * (Default: enforced, max_records = 16) + * + * When we request a renegotiation, the peer can comply or + * ignore the request. This function allows us to decide + * whether to enforce our renegotiation requests by closing + * the connection if the peer doesn't comply. + * + * However, records could already be in transit from the peer + * when the request is emitted. In order to increase + * reliability, we can accept a number of records before the + * expected handshake records. + * + * The optimal value is highly dependent on the specific usage + * scenario. + * + * \note With DTLS and server-initiated renegotiation, the + * HelloRequest is retransmitted every time mbedtls_ssl_read() times + * out or receives Application Data, until: + * - max_records records have beens seen, if it is >= 0, or + * - the number of retransmits that would happen during an + * actual handshake has been reached. + * Please remember the request might be lost a few times + * if you consider setting max_records to a really low value. + * + * \warning On client, the grace period can only happen during + * mbedtls_ssl_read(), as opposed to mbedtls_ssl_write() and mbedtls_ssl_renegotiate() + * which always behave as if max_record was 0. The reason is, + * if we receive application data from the server, we need a + * place to write it, which only happens during mbedtls_ssl_read(). + * + * \param conf SSL configuration + * \param max_records Use MBEDTLS_SSL_RENEGOTIATION_NOT_ENFORCED if you don't want to + * enforce renegotiation, or a non-negative value to enforce + * it but allow for a grace period of max_records records. + */ +void mbedtls_ssl_conf_renegotiation_enforced(mbedtls_ssl_config *conf, int max_records); + +/** + * \brief Set record counter threshold for periodic renegotiation. + * (Default: 2^48 - 1) + * + * Renegotiation is automatically triggered when a record + * counter (outgoing or incoming) crosses the defined + * threshold. The default value is meant to prevent the + * connection from being closed when the counter is about to + * reached its maximal value (it is not allowed to wrap). + * + * Lower values can be used to enforce policies such as "keys + * must be refreshed every N packets with cipher X". + * + * The renegotiation period can be disabled by setting + * conf->disable_renegotiation to + * MBEDTLS_SSL_RENEGOTIATION_DISABLED. + * + * \note When the configured transport is + * MBEDTLS_SSL_TRANSPORT_DATAGRAM the maximum renegotiation + * period is 2^48 - 1, and for MBEDTLS_SSL_TRANSPORT_STREAM, + * the maximum renegotiation period is 2^64 - 1. + * + * \param conf SSL configuration + * \param period The threshold value: a big-endian 64-bit number. + */ +void mbedtls_ssl_conf_renegotiation_period(mbedtls_ssl_config *conf, + const unsigned char period[8]); +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +/** + * \brief Check if there is data already read from the + * underlying transport but not yet processed. + * + * \param ssl SSL context + * + * \return 0 if nothing's pending, 1 otherwise. + * + * \note This is different in purpose and behaviour from + * \c mbedtls_ssl_get_bytes_avail in that it considers + * any kind of unprocessed data, not only unread + * application data. If \c mbedtls_ssl_get_bytes + * returns a non-zero value, this function will + * also signal pending data, but the converse does + * not hold. For example, in DTLS there might be + * further records waiting to be processed from + * the current underlying transport's datagram. + * + * \note If this function returns 1 (data pending), this + * does not imply that a subsequent call to + * \c mbedtls_ssl_read will provide any data; + * e.g., the unprocessed data might turn out + * to be an alert or a handshake message. + * + * \note This function is useful in the following situation: + * If the SSL/TLS module successfully returns from an + * operation - e.g. a handshake or an application record + * read - and you're awaiting incoming data next, you + * must not immediately idle on the underlying transport + * to have data ready, but you need to check the value + * of this function first. The reason is that the desired + * data might already be read but not yet processed. + * If, in contrast, a previous call to the SSL/TLS module + * returned MBEDTLS_ERR_SSL_WANT_READ, it is not necessary + * to call this function, as the latter error code entails + * that all internal data has been processed. + * + */ +int mbedtls_ssl_check_pending(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the number of application data bytes + * remaining to be read from the current record. + * + * \param ssl SSL context + * + * \return How many bytes are available in the application + * data record read buffer. + * + * \note When working over a datagram transport, this is + * useful to detect the current datagram's boundary + * in case \c mbedtls_ssl_read has written the maximal + * amount of data fitting into the input buffer. + * + */ +size_t mbedtls_ssl_get_bytes_avail(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the result of the certificate verification + * + * \param ssl The SSL context to use. + * + * \return \c 0 if the certificate verification was successful. + * \return \c -1u if the result is not available. This may happen + * e.g. if the handshake aborts early, or a verification + * callback returned a fatal error. + * \return A bitwise combination of \c MBEDTLS_X509_BADCERT_XXX + * and \c MBEDTLS_X509_BADCRL_XXX failure flags; see x509.h. + */ +uint32_t mbedtls_ssl_get_verify_result(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the id of the current ciphersuite + * + * \param ssl SSL context + * + * \return a ciphersuite id + */ +int mbedtls_ssl_get_ciphersuite_id_from_ssl(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the name of the current ciphersuite + * + * \param ssl SSL context + * + * \return a string containing the ciphersuite name + */ +const char *mbedtls_ssl_get_ciphersuite(const mbedtls_ssl_context *ssl); + + +/** + * \brief Return the (D)TLS protocol version negotiated in the + * given connection. + * + * \note If you call this function too early during the initial + * handshake, before the two sides have agreed on a version, + * this function returns #MBEDTLS_SSL_VERSION_UNKNOWN. + * + * \param ssl The SSL context to query. + * \return The negotiated protocol version. + */ +static inline mbedtls_ssl_protocol_version mbedtls_ssl_get_version_number( + const mbedtls_ssl_context *ssl) +{ + return ssl->MBEDTLS_PRIVATE(tls_version); +} + +/** + * \brief Return the current TLS version + * + * \param ssl SSL context + * + * \return a string containing the TLS version + */ +const char *mbedtls_ssl_get_version(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the (maximum) number of bytes added by the record + * layer: header + encryption/MAC overhead (inc. padding) + * + * \param ssl SSL context + * + * \return Current maximum record expansion in bytes + */ +int mbedtls_ssl_get_record_expansion(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the current maximum outgoing record payload in bytes. + * + * \note The logic to determine the maximum outgoing record payload is + * version-specific. It takes into account various factors, such as + * the mbedtls_config.h setting \c MBEDTLS_SSL_OUT_CONTENT_LEN, extensions + * such as the max fragment length or record size limit extension if + * used, and for DTLS the path MTU as configured and current + * record expansion. + * + * \note With DTLS, \c mbedtls_ssl_write() will return an error if + * called with a larger length value. + * With TLS, \c mbedtls_ssl_write() will fragment the input if + * necessary and return the number of bytes written; it is up + * to the caller to call \c mbedtls_ssl_write() again in + * order to send the remaining bytes if any. + * + * \sa mbedtls_ssl_get_max_out_record_payload() + * \sa mbedtls_ssl_get_record_expansion() + * + * \param ssl SSL context + * + * \return Current maximum payload for an outgoing record, + * or a negative error code. + */ +int mbedtls_ssl_get_max_out_record_payload(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the current maximum incoming record payload in bytes. + * + * \note The logic to determine the maximum incoming record payload is + * version-specific. It takes into account various factors, such as + * the mbedtls_config.h setting \c MBEDTLS_SSL_IN_CONTENT_LEN, extensions + * such as the max fragment length extension or record size limit + * extension if used, and the current record expansion. + * + * \sa mbedtls_ssl_set_mtu() + * \sa mbedtls_ssl_get_max_in_record_payload() + * \sa mbedtls_ssl_get_record_expansion() + * + * \param ssl SSL context + * + * \return Current maximum payload for an incoming record, + * or a negative error code. + */ +int mbedtls_ssl_get_max_in_record_payload(const mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * \brief Return the peer certificate from the current connection. + * + * \param ssl The SSL context to use. This must be initialized and setup. + * + * \return The current peer certificate, if available. + * The returned certificate is owned by the SSL context and + * is valid only until the next call to the SSL API. + * \return \c NULL if no peer certificate is available. This might + * be because the chosen ciphersuite doesn't use CRTs + * (PSK-based ciphersuites, for example), or because + * #MBEDTLS_SSL_KEEP_PEER_CERTIFICATE has been disabled, + * allowing the stack to free the peer's CRT to save memory. + * + * \note For one-time inspection of the peer's certificate during + * the handshake, consider registering an X.509 CRT verification + * callback through mbedtls_ssl_conf_verify() instead of calling + * this function. Using mbedtls_ssl_conf_verify() also comes at + * the benefit of allowing you to influence the verification + * process, for example by masking expected and tolerated + * verification failures. + * + * \warning You must not use the pointer returned by this function + * after any further call to the SSL API, including + * mbedtls_ssl_read() and mbedtls_ssl_write(); this is + * because the pointer might change during renegotiation, + * which happens transparently to the user. + * If you want to use the certificate across API calls, + * you must make a copy. + */ +const mbedtls_x509_crt *mbedtls_ssl_get_peer_cert(const mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_CLI_C) +/** + * \brief Export a session in order to resume it later. + * + * \param ssl The SSL context representing the connection for which to + * to export a session structure for later resumption. + * \param session The target structure in which to store the exported session. + * This must have been initialized with mbedtls_ssl_init_session() + * but otherwise be unused. + * + * \note This function can handle a variety of mechanisms for session + * resumption: For TLS 1.2, both session ID-based resumption and + * ticket-based resumption will be considered. For TLS 1.3, + * once implemented, sessions equate to tickets, and calling + * this function multiple times will export the available + * tickets one a time until no further tickets are available, + * in which case MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE will + * be returned. + * + * \note Calling this function multiple times will only be useful + * once TLS 1.3 is supported. For TLS 1.2 connections, this + * function should be called at most once. + * + * \return \c 0 if successful. In this case, \p session can be used for + * session resumption by passing it to mbedtls_ssl_set_session(), + * and serialized for storage via mbedtls_ssl_session_save(). + * \return #MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE if no further session + * is available for export. + * This error is a non-fatal, and has no observable effect on + * the SSL context or the destination session. + * \return Another negative error code on other kinds of failure. + * + * \sa mbedtls_ssl_set_session() + * \sa mbedtls_ssl_session_save() + */ +int mbedtls_ssl_get_session(const mbedtls_ssl_context *ssl, + mbedtls_ssl_session *session); +#endif /* MBEDTLS_SSL_CLI_C */ + +/** + * \brief Perform the SSL handshake + * + * \param ssl SSL context + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_WANT_READ or #MBEDTLS_ERR_SSL_WANT_WRITE + * if the handshake is incomplete and waiting for data to + * be available for reading from or writing to the underlying + * transport - in this case you must call this function again + * when the underlying transport is ready for the operation. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if an asynchronous + * operation is in progress (see + * mbedtls_ssl_conf_async_private_cb()) - in this case you + * must call this function again when the operation is ready. + * \return #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS if a cryptographic + * operation is in progress (see mbedtls_ecp_set_max_ops()) - + * in this case you must call this function again to complete + * the handshake when you're done attending other tasks. + * \return #MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED if DTLS is in use + * and the client did not demonstrate reachability yet - in + * this case you must stop using the context (see below). + * \return Another SSL error code - in this case you must stop using + * the context (see below). + * + * \warning If this function returns something other than + * \c 0, + * #MBEDTLS_ERR_SSL_WANT_READ, + * #MBEDTLS_ERR_SSL_WANT_WRITE, + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS or + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS, + * you must stop using the SSL context for reading or writing, + * and either free it or call \c mbedtls_ssl_session_reset() + * on it before re-using it for a new connection; the current + * connection must be closed. + * + * \note If DTLS is in use, then you may choose to handle + * #MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED specially for logging + * purposes, as it is an expected return value rather than an + * actual error, but you still need to reset/free the context. + * + * \note Remarks regarding event-driven DTLS: + * If the function returns #MBEDTLS_ERR_SSL_WANT_READ, no datagram + * from the underlying transport layer is currently being processed, + * and it is safe to idle until the timer or the underlying transport + * signal a new event. This is not true for a successful handshake, + * in which case the datagram of the underlying transport that is + * currently being processed might or might not contain further + * DTLS records. + * + * \note If the context is configured to allow TLS 1.3, or if + * #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + */ +int mbedtls_ssl_handshake(mbedtls_ssl_context *ssl); + +/** + * \brief After calling mbedtls_ssl_handshake() to start the SSL + * handshake you can call this function to check whether the + * handshake is over for a given SSL context. This function + * should be also used to determine when to stop calling + * mbedtls_handshake_step() for that context. + * + * \param ssl SSL context + * + * \return \c 1 if handshake is over, \c 0 if it is still ongoing. + */ +static inline int mbedtls_ssl_is_handshake_over(mbedtls_ssl_context *ssl) +{ + return ssl->MBEDTLS_PRIVATE(state) >= MBEDTLS_SSL_HANDSHAKE_OVER; +} + +/** + * \brief Perform a single step of the SSL handshake + * + * \note The state of the context (ssl->state) will be at + * the next state after this function returns \c 0. Do not + * call this function if mbedtls_ssl_is_handshake_over() + * returns \c 1. + * + * \warning Whilst in the past you may have used direct access to the + * context state (ssl->state) in order to ascertain when to + * stop calling this function and although you can still do + * so with something like ssl->MBEDTLS_PRIVATE(state) or by + * defining MBEDTLS_ALLOW_PRIVATE_ACCESS, this is now + * considered deprecated and could be broken in any future + * release. If you still find you have good reason for such + * direct access, then please do contact the team to explain + * this (raise an issue or post to the mailing list), so that + * we can add a solution to your problem that will be + * guaranteed to work in the future. + * + * \param ssl SSL context + * + * \return See mbedtls_ssl_handshake(). + * + * \warning If this function returns something other than \c 0, + * #MBEDTLS_ERR_SSL_WANT_READ, #MBEDTLS_ERR_SSL_WANT_WRITE, + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS or + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS, you must stop using + * the SSL context for reading or writing, and either free it + * or call \c mbedtls_ssl_session_reset() on it before + * re-using it for a new connection; the current connection + * must be closed. + */ +int mbedtls_ssl_handshake_step(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +/** + * \brief Initiate an SSL renegotiation on the running connection. + * Client: perform the renegotiation right now. + * Server: request renegotiation, which will be performed + * during the next call to mbedtls_ssl_read() if honored by + * client. + * + * \param ssl SSL context + * + * \return 0 if successful, or any mbedtls_ssl_handshake() return + * value except #MBEDTLS_ERR_SSL_CLIENT_RECONNECT that can't + * happen during a renegotiation. + * + * \warning If this function returns something other than \c 0, + * #MBEDTLS_ERR_SSL_WANT_READ, #MBEDTLS_ERR_SSL_WANT_WRITE, + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS or + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS, you must stop using + * the SSL context for reading or writing, and either free it + * or call \c mbedtls_ssl_session_reset() on it before + * re-using it for a new connection; the current connection + * must be closed. + * + */ +int mbedtls_ssl_renegotiate(mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +/** + * \brief Read at most 'len' application data bytes + * + * \param ssl SSL context + * \param buf buffer that will hold the data + * \param len maximum number of bytes to read + * + * \return The (positive) number of bytes read if successful. + * \return \c 0 if the read end of the underlying transport was closed + * without sending a CloseNotify beforehand, which might happen + * because of various reasons (internal error of an underlying + * stack, non-conformant peer not sending a CloseNotify and + * such) - in this case you must stop using the context + * (see below). + * \return #MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY if the underlying + * transport is still functional, but the peer has + * acknowledged to not send anything anymore. + * \return #MBEDTLS_ERR_SSL_WANT_READ or #MBEDTLS_ERR_SSL_WANT_WRITE + * if the handshake is incomplete and waiting for data to + * be available for reading from or writing to the underlying + * transport - in this case you must call this function again + * when the underlying transport is ready for the operation. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if an asynchronous + * operation is in progress (see + * mbedtls_ssl_conf_async_private_cb()) - in this case you + * must call this function again when the operation is ready. + * \return #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS if a cryptographic + * operation is in progress (see mbedtls_ecp_set_max_ops()) - + * in this case you must call this function again to complete + * the handshake when you're done attending other tasks. + * \return #MBEDTLS_ERR_SSL_CLIENT_RECONNECT if we're at the server + * side of a DTLS connection and the client is initiating a + * new connection using the same source port. See below. + * \return Another SSL error code - in this case you must stop using + * the context (see below). + * + * \warning If this function returns something other than + * a positive value, + * #MBEDTLS_ERR_SSL_WANT_READ, + * #MBEDTLS_ERR_SSL_WANT_WRITE, + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS, + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS or + * #MBEDTLS_ERR_SSL_CLIENT_RECONNECT, + * you must stop using the SSL context for reading or writing, + * and either free it or call \c mbedtls_ssl_session_reset() + * on it before re-using it for a new connection; the current + * connection must be closed. + * + * \note When this function returns #MBEDTLS_ERR_SSL_CLIENT_RECONNECT + * (which can only happen server-side), it means that a client + * is initiating a new connection using the same source port. + * You can either treat that as a connection close and wait + * for the client to resend a ClientHello, or directly + * continue with \c mbedtls_ssl_handshake() with the same + * context (as it has been reset internally). Either way, you + * must make sure this is seen by the application as a new + * connection: application state, if any, should be reset, and + * most importantly the identity of the client must be checked + * again. WARNING: not validating the identity of the client + * again, or not transmitting the new identity to the + * application layer, would allow authentication bypass! + * + * \note Remarks regarding event-driven DTLS: + * - If the function returns #MBEDTLS_ERR_SSL_WANT_READ, no datagram + * from the underlying transport layer is currently being processed, + * and it is safe to idle until the timer or the underlying transport + * signal a new event. + * - This function may return MBEDTLS_ERR_SSL_WANT_READ even if data was + * initially available on the underlying transport, as this data may have + * been only e.g. duplicated messages or a renegotiation request. + * Therefore, you must be prepared to receive MBEDTLS_ERR_SSL_WANT_READ even + * when reacting to an incoming-data event from the underlying transport. + * - On success, the datagram of the underlying transport that is currently + * being processed may contain further DTLS records. You should call + * \c mbedtls_ssl_check_pending to check for remaining records. + * + */ +int mbedtls_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len); + +/** + * \brief Try to write exactly 'len' application data bytes + * + * \warning This function will do partial writes in some cases. If the + * return value is non-negative but less than length, the + * function must be called again with updated arguments: + * buf + ret, len - ret (if ret is the return value) until + * it returns a value equal to the last 'len' argument. + * + * \param ssl SSL context + * \param buf buffer holding the data + * \param len how many bytes must be written + * + * \return The (non-negative) number of bytes actually written if + * successful (may be less than \p len). + * \return #MBEDTLS_ERR_SSL_WANT_READ or #MBEDTLS_ERR_SSL_WANT_WRITE + * if the handshake is incomplete and waiting for data to + * be available for reading from or writing to the underlying + * transport - in this case you must call this function again + * when the underlying transport is ready for the operation. + * \return #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS if an asynchronous + * operation is in progress (see + * mbedtls_ssl_conf_async_private_cb()) - in this case you + * must call this function again when the operation is ready. + * \return #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS if a cryptographic + * operation is in progress (see mbedtls_ecp_set_max_ops()) - + * in this case you must call this function again to complete + * the handshake when you're done attending other tasks. + * \return Another SSL error code - in this case you must stop using + * the context (see below). + * + * \warning If this function returns something other than + * a non-negative value, + * #MBEDTLS_ERR_SSL_WANT_READ, + * #MBEDTLS_ERR_SSL_WANT_WRITE, + * #MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS or + * #MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS, + * you must stop using the SSL context for reading or writing, + * and either free it or call \c mbedtls_ssl_session_reset() + * on it before re-using it for a new connection; the current + * connection must be closed. + * + * \note When this function returns #MBEDTLS_ERR_SSL_WANT_WRITE/READ, + * it must be called later with the *same* arguments, + * until it returns a value greater than or equal to 0. When + * the function returns #MBEDTLS_ERR_SSL_WANT_WRITE there may be + * some partial data in the output buffer, however this is not + * yet sent. + * + * \note If the requested length is greater than the maximum + * fragment length (either the built-in limit or the one set + * or negotiated with the peer), then: + * - with TLS, less bytes than requested are written. + * - with DTLS, MBEDTLS_ERR_SSL_BAD_INPUT_DATA is returned. + * \c mbedtls_ssl_get_max_out_record_payload() may be used to + * query the active maximum fragment length. + * + * \note Attempting to write 0 bytes will result in an empty TLS + * application record being sent. + */ +int mbedtls_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len); + +/** + * \brief Send an alert message + * + * \param ssl SSL context + * \param level The alert level of the message + * (MBEDTLS_SSL_ALERT_LEVEL_WARNING or MBEDTLS_SSL_ALERT_LEVEL_FATAL) + * \param message The alert message (SSL_ALERT_MSG_*) + * + * \return 0 if successful, or a specific SSL error code. + * + * \note If this function returns something other than 0 or + * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using + * the SSL context for reading or writing, and either free it or + * call \c mbedtls_ssl_session_reset() on it before re-using it + * for a new connection; the current connection must be closed. + */ +int mbedtls_ssl_send_alert_message(mbedtls_ssl_context *ssl, + unsigned char level, + unsigned char message); +/** + * \brief Notify the peer that the connection is being closed + * + * \param ssl SSL context + * + * \return 0 if successful, or a specific SSL error code. + * + * \note If this function returns something other than 0 or + * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using + * the SSL context for reading or writing, and either free it or + * call \c mbedtls_ssl_session_reset() on it before re-using it + * for a new connection; the current connection must be closed. + */ +int mbedtls_ssl_close_notify(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_EARLY_DATA) + +#if defined(MBEDTLS_SSL_SRV_C) +/** + * \brief Read at most 'len' application data bytes while performing + * the handshake (early data). + * + * \note This function behaves mainly as mbedtls_ssl_read(). The + * specification of mbedtls_ssl_read() relevant to TLS 1.3 + * (thus not the parts specific to (D)TLS 1.2) applies to this + * function and the present documentation is restricted to the + * differences with mbedtls_ssl_read(). + * + * \param ssl SSL context + * \param buf buffer that will hold the data + * \param len maximum number of bytes to read + * + * \return One additional specific return value: + * #MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA. + * + * #MBEDTLS_ERR_SSL_CANNOT_READ_EARLY_DATA is returned when it + * is not possible to read early data for the SSL context + * \p ssl. + * + * It may have been possible and it is not possible + * anymore because the server received the End of Early Data + * message or the maximum number of allowed early data for the + * PSK in use has been reached. + * + * It may never have been possible and will never be possible + * for the SSL context \p ssl because the use of early data + * is disabled for that context or more generally the context + * is not suitably configured to enable early data or the + * client does not use early data or the first call to the + * function was done while the handshake was already too + * advanced to gather and accept early data. + * + * It is not possible to read early data for the SSL context + * \p ssl but this does not preclude for using it with + * mbedtls_ssl_write(), mbedtls_ssl_read() or + * mbedtls_ssl_handshake(). + * + * \note When a server wants to retrieve early data, it is expected + * that this function starts the handshake for the SSL context + * \p ssl. But this is not mandatory. + * + */ +int mbedtls_ssl_read_early_data(mbedtls_ssl_context *ssl, + unsigned char *buf, size_t len); +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_CLI_C) +/** + * \brief Try to write exactly 'len' application data bytes while + * performing the handshake (early data). + * + * \note This function behaves mainly as mbedtls_ssl_write(). The + * specification of mbedtls_ssl_write() relevant to TLS 1.3 + * (thus not the parts specific to (D)TLS1.2) applies to this + * function and the present documentation is restricted to the + * differences with mbedtls_ssl_write(). + * + * \param ssl SSL context + * \param buf buffer holding the data + * \param len how many bytes must be written + * + * \return One additional specific return value: + * #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA. + * + * #MBEDTLS_ERR_SSL_CANNOT_WRITE_EARLY_DATA is returned when it + * is not possible to write early data for the SSL context + * \p ssl. + * + * It may have been possible and it is not possible + * anymore because the client received the server Finished + * message, the server rejected early data or the maximum + * number of allowed early data for the PSK in use has been + * reached. + * + * It may never have been possible and will never be possible + * for the SSL context \p ssl because the use of early data + * is disabled for that context or more generally the context + * is not suitably configured to enable early data or the first + * call to the function was done while the handshake was + * already completed. + * + * It is not possible to write early data for the SSL context + * \p ssl but this does not preclude for using it with + * mbedtls_ssl_write(), mbedtls_ssl_read() or + * mbedtls_ssl_handshake(). + * + * \note This function may write early data only if the SSL context + * has been configured for the handshake with a PSK for which + * early data is allowed. + * + * \note To maximize the number of early data that can be written in + * the course of the handshake, it is expected that this + * function starts the handshake for the SSL context \p ssl. + * But this is not mandatory. + * + * \note This function does not provide any information on whether + * the server has accepted or will accept early data or not. + * When it returns a positive value, it just means that it + * has written early data to the server. To know whether the + * server has accepted early data or not, you should call + * mbedtls_ssl_get_early_data_status() with the handshake + * completed. + */ +int mbedtls_ssl_write_early_data(mbedtls_ssl_context *ssl, + const unsigned char *buf, size_t len); + +#define MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT 0 +#define MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED 1 +#define MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED 2 +/** + * \brief Get the status of the negotiation of the use of early data. + * + * \param ssl The SSL context to query + * + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if this function is called + * from the server-side. + * + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if this function is called + * prior to completion of the handshake. + * + * \return #MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT if the client has + * not indicated the use of early data to the server. + * + * \return #MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED if the client has + * indicated the use of early data and the server has accepted + * it. + * + * \return #MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED if the client has + * indicated the use of early data but the server has rejected + * it. In this situation, the client may want to re-send the + * early data it may have tried to send by calling + * mbedtls_ssl_write_early_data() as ordinary post-handshake + * application data by calling mbedtls_ssl_write(). + * + */ +int mbedtls_ssl_get_early_data_status(mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_CLI_C */ + +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +/** + * \brief Free referenced items in an SSL context and clear memory + * + * \param ssl SSL context + */ +void mbedtls_ssl_free(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) +/** + * \brief Save an active connection as serialized data in a buffer. + * This allows the freeing or re-using of the SSL context + * while still picking up the connection later in a way that + * it entirely transparent to the peer. + * + * \see mbedtls_ssl_context_load() + * + * \note The serialized data only contains the data that is + * necessary to resume the connection: negotiated protocol + * options, session identifier, keys, etc. + * Loading a saved SSL context does not restore settings and + * state related to how the application accesses the context, + * such as configured callback functions, user data, pending + * incoming or outgoing data, etc. + * + * \note This feature is currently only available under certain + * conditions, see the documentation of the return value + * #MBEDTLS_ERR_SSL_BAD_INPUT_DATA for details. + * + * \note When this function succeeds, it calls + * mbedtls_ssl_session_reset() on \p ssl which as a result is + * no longer associated with the connection that has been + * serialized. This avoids creating copies of the connection + * state. You're then free to either re-use the context + * structure for a different connection, or call + * mbedtls_ssl_free() on it. See the documentation of + * mbedtls_ssl_session_reset() for more details. + * + * \param ssl The SSL context to save. On success, it is no longer + * associated with the connection that has been serialized. + * \param buf The buffer to write the serialized data to. It must be a + * writeable buffer of at least \p buf_len bytes, or may be \c + * NULL if \p buf_len is \c 0. + * \param buf_len The number of bytes available for writing in \p buf. + * \param olen The size in bytes of the data that has been or would have + * been written. It must point to a valid \c size_t. + * + * \note \p olen is updated to the correct value regardless of + * whether \p buf_len was large enough. This makes it possible + * to determine the necessary size by calling this function + * with \p buf set to \c NULL and \p buf_len to \c 0. However, + * the value of \p olen is only guaranteed to be correct when + * the function returns #MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL or + * \c 0. If the return value is different, then the value of + * \p olen is undefined. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL if \p buf is too small. + * \return #MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed + * while resetting the context. + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if a handshake is in + * progress, or there is pending data for reading or sending, + * or the connection does not use DTLS 1.2 with an AEAD + * ciphersuite, or renegotiation is enabled. + */ +int mbedtls_ssl_context_save(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t buf_len, + size_t *olen); + +/** + * \brief Load serialized connection data to an SSL context. + * + * \see mbedtls_ssl_context_save() + * + * \warning The same serialized data must never be loaded into more + * that one context. In order to ensure that, after + * successfully loading serialized data to an SSL context, you + * should immediately destroy or invalidate all copies of the + * serialized data that was loaded. Loading the same data in + * more than one context would cause severe security failures + * including but not limited to loss of confidentiality. + * + * \note Before calling this function, the SSL context must be + * prepared in one of the two following ways. The first way is + * to take a context freshly initialised with + * mbedtls_ssl_init() and call mbedtls_ssl_setup() on it with + * the same ::mbedtls_ssl_config structure that was used in + * the original connection. The second way is to + * call mbedtls_ssl_session_reset() on a context that was + * previously prepared as above but used in the meantime. + * Either way, you must not use the context to perform a + * handshake between calling mbedtls_ssl_setup() or + * mbedtls_ssl_session_reset() and calling this function. You + * may however call other setter functions in that time frame + * as indicated in the note below. + * + * \note Before or after calling this function successfully, you + * also need to configure some connection-specific callbacks + * and settings before you can use the connection again + * (unless they were already set before calling + * mbedtls_ssl_session_reset() and the values are suitable for + * the present connection). Specifically, you want to call + * at least mbedtls_ssl_set_bio(), + * mbedtls_ssl_set_timer_cb(), and + * mbedtls_ssl_set_user_data_n() or + * mbedtls_ssl_set_user_data_p() if they were set originally. + * All other SSL setter functions + * are not necessary to call, either because they're only used + * in handshakes, or because the setting is already saved. You + * might choose to call them anyway, for example in order to + * share code between the cases of establishing a new + * connection and the case of loading an already-established + * connection. + * + * \note If you have new information about the path MTU, you want to + * call mbedtls_ssl_set_mtu() after calling this function, as + * otherwise this function would overwrite your + * newly-configured value with the value that was active when + * the context was saved. + * + * \note When this function returns an error code, it calls + * mbedtls_ssl_free() on \p ssl. In this case, you need to + * prepare the context with the usual sequence starting with a + * call to mbedtls_ssl_init() if you want to use it again. + * + * \param ssl The SSL context structure to be populated. It must have + * been prepared as described in the note above. + * \param buf The buffer holding the serialized connection data. It must + * be a readable buffer of at least \p len bytes. + * \param len The size of the serialized data in bytes. + * + * \return \c 0 if successful. + * \return #MBEDTLS_ERR_SSL_ALLOC_FAILED if memory allocation failed. + * \return #MBEDTLS_ERR_SSL_VERSION_MISMATCH if the serialized data + * comes from a different Mbed TLS version or build. + * \return #MBEDTLS_ERR_SSL_BAD_INPUT_DATA if input data is invalid. + */ +int mbedtls_ssl_context_load(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len); +#endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ + +/** + * \brief Initialize an SSL configuration context + * Just makes the context ready for + * mbedtls_ssl_config_defaults() or mbedtls_ssl_config_free(). + * + * \note You need to call mbedtls_ssl_config_defaults() unless you + * manually set all of the relevant fields yourself. + * + * \param conf SSL configuration context + */ +void mbedtls_ssl_config_init(mbedtls_ssl_config *conf); + +/** + * \brief Load reasonable default SSL configuration values. + * (You need to call mbedtls_ssl_config_init() first.) + * + * \param conf SSL configuration context + * \param endpoint MBEDTLS_SSL_IS_CLIENT or MBEDTLS_SSL_IS_SERVER + * \param transport MBEDTLS_SSL_TRANSPORT_STREAM for TLS, or + * MBEDTLS_SSL_TRANSPORT_DATAGRAM for DTLS + * \param preset a MBEDTLS_SSL_PRESET_XXX value + * + * \note See \c mbedtls_ssl_conf_transport() for notes on DTLS. + * + * \return 0 if successful, or + * MBEDTLS_ERR_XXX_ALLOC_FAILED on memory allocation error. + */ +int mbedtls_ssl_config_defaults(mbedtls_ssl_config *conf, + int endpoint, int transport, int preset); + +/** + * \brief Free an SSL configuration context + * + * \param conf SSL configuration context + */ +void mbedtls_ssl_config_free(mbedtls_ssl_config *conf); + +/** + * \brief Initialize SSL session structure + * + * \param session SSL session + */ +void mbedtls_ssl_session_init(mbedtls_ssl_session *session); + +/** + * \brief Free referenced items in an SSL session including the + * peer certificate and clear memory + * + * \note A session object can be freed even if the SSL context + * that was used to retrieve the session is still in use. + * + * \param session SSL session + */ +void mbedtls_ssl_session_free(mbedtls_ssl_session *session); + +/** + * \brief TLS-PRF function for key derivation. + * + * \param prf The tls_prf type function type to be used. + * \param secret Secret for the key derivation function. + * \param slen Length of the secret. + * \param label String label for the key derivation function, + * terminated with null character. + * \param random Random bytes. + * \param rlen Length of the random bytes buffer. + * \param dstbuf The buffer holding the derived key. + * \param dlen Length of the output buffer. + * + * \return 0 on success. An SSL specific error on failure. + */ +int mbedtls_ssl_tls_prf(const mbedtls_tls_prf_types prf, + const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen); + +#ifdef __cplusplus +} +#endif + +#endif /* ssl.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_cache.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_cache.h new file mode 100644 index 00000000..55dcf77c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_cache.h @@ -0,0 +1,176 @@ +/** + * \file ssl_cache.h + * + * \brief SSL session cache implementation + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SSL_CACHE_H +#define MBEDTLS_SSL_CACHE_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/ssl.h" + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ + +#if !defined(MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT) +#define MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT 86400 /*!< 1 day */ +#endif + +#if !defined(MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES) +#define MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES 50 /*!< Maximum entries in cache */ +#endif + +/** \} name SECTION: Module settings */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mbedtls_ssl_cache_context mbedtls_ssl_cache_context; +typedef struct mbedtls_ssl_cache_entry mbedtls_ssl_cache_entry; + +/** + * \brief This structure is used for storing cache entries + */ +struct mbedtls_ssl_cache_entry { +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t MBEDTLS_PRIVATE(timestamp); /*!< entry timestamp */ +#endif + + unsigned char MBEDTLS_PRIVATE(session_id)[32]; /*!< session ID */ + size_t MBEDTLS_PRIVATE(session_id_len); + + unsigned char *MBEDTLS_PRIVATE(session); /*!< serialized session */ + size_t MBEDTLS_PRIVATE(session_len); + + mbedtls_ssl_cache_entry *MBEDTLS_PRIVATE(next); /*!< chain pointer */ +}; + +/** + * \brief Cache context + */ +struct mbedtls_ssl_cache_context { + mbedtls_ssl_cache_entry *MBEDTLS_PRIVATE(chain); /*!< start of the chain */ + int MBEDTLS_PRIVATE(timeout); /*!< cache entry timeout */ + int MBEDTLS_PRIVATE(max_entries); /*!< maximum entries */ +#if defined(MBEDTLS_THREADING_C) + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); /*!< mutex */ +#endif +}; + +/** + * \brief Initialize an SSL cache context + * + * \param cache SSL cache context + */ +void mbedtls_ssl_cache_init(mbedtls_ssl_cache_context *cache); + +/** + * \brief Cache get callback implementation + * (Thread-safe if MBEDTLS_THREADING_C is enabled) + * + * \param data The SSL cache context to use. + * \param session_id The pointer to the buffer holding the session ID + * for the session to load. + * \param session_id_len The length of \p session_id in bytes. + * \param session The address at which to store the session + * associated with \p session_id, if present. + */ +int mbedtls_ssl_cache_get(void *data, + unsigned char const *session_id, + size_t session_id_len, + mbedtls_ssl_session *session); + +/** + * \brief Cache set callback implementation + * (Thread-safe if MBEDTLS_THREADING_C is enabled) + * + * \param data The SSL cache context to use. + * \param session_id The pointer to the buffer holding the session ID + * associated to \p session. + * \param session_id_len The length of \p session_id in bytes. + * \param session The session to store. + */ +int mbedtls_ssl_cache_set(void *data, + unsigned char const *session_id, + size_t session_id_len, + const mbedtls_ssl_session *session); + +/** + * \brief Remove the cache entry by the session ID + * (Thread-safe if MBEDTLS_THREADING_C is enabled) + * + * \param data The SSL cache context to use. + * \param session_id The pointer to the buffer holding the session ID + * associated to \p session. + * \param session_id_len The length of \p session_id in bytes. + * + * \return 0: The cache entry for session with provided ID + * is removed or does not exist. + * Otherwise: fail. + */ +int mbedtls_ssl_cache_remove(void *data, + unsigned char const *session_id, + size_t session_id_len); + +#if defined(MBEDTLS_HAVE_TIME) +/** + * \brief Set the cache timeout + * (Default: MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT (1 day)) + * + * A timeout of 0 indicates no timeout. + * + * \param cache SSL cache context + * \param timeout cache entry timeout in seconds + */ +void mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context *cache, int timeout); +#endif /* MBEDTLS_HAVE_TIME */ + +/** + * \brief Set the maximum number of cache entries + * (Default: MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES (50)) + * + * \param cache SSL cache context + * \param max cache entry maximum + */ +void mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context *cache, int max); + +/** + * \brief Free referenced items in a cache context and clear memory + * + * \param cache SSL cache context + */ +void mbedtls_ssl_cache_free(mbedtls_ssl_cache_context *cache); + +#ifdef __cplusplus +} +#endif + +#endif /* ssl_cache.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h new file mode 100644 index 00000000..17b8ff78 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_ciphersuites.h @@ -0,0 +1,549 @@ +/** + * \file ssl_ciphersuites.h + * + * \brief SSL Ciphersuites for mbed TLS + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SSL_CIPHERSUITES_H +#define MBEDTLS_SSL_CIPHERSUITES_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/pk.h" +#include "mbedtls/cipher.h" +#include "mbedtls/md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Supported ciphersuites (Official IANA names) + */ +#define MBEDTLS_TLS_RSA_WITH_NULL_MD5 0x01 /**< Weak! */ +#define MBEDTLS_TLS_RSA_WITH_NULL_SHA 0x02 /**< Weak! */ + +#define MBEDTLS_TLS_PSK_WITH_NULL_SHA 0x2C /**< Weak! */ +#define MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA 0x2D /**< Weak! */ +#define MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA 0x2E /**< Weak! */ +#define MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA 0x2F + +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x33 +#define MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA 0x35 +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x39 + +#define MBEDTLS_TLS_RSA_WITH_NULL_SHA256 0x3B /**< Weak! */ +#define MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256 0x3C /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256 0x3D /**< TLS 1.2 */ + +#define MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA 0x41 +#define MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA 0x45 + +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x67 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x6B /**< TLS 1.2 */ + +#define MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA 0x84 +#define MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x88 + +#define MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA 0x8C +#define MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA 0x8D + +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA 0x90 +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA 0x91 + +#define MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA 0x94 +#define MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA 0x95 + +#define MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256 0x9C /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384 0x9D /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x9E /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x9F /**< TLS 1.2 */ + +#define MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256 0xA8 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384 0xA9 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 0xAA /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 0xAB /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 0xAC /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 0xAD /**< TLS 1.2 */ + +#define MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256 0xAE +#define MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384 0xAF +#define MBEDTLS_TLS_PSK_WITH_NULL_SHA256 0xB0 /**< Weak! */ +#define MBEDTLS_TLS_PSK_WITH_NULL_SHA384 0xB1 /**< Weak! */ + +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 0xB2 +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 0xB3 +#define MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256 0xB4 /**< Weak! */ +#define MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384 0xB5 /**< Weak! */ + +#define MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 0xB6 +#define MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 0xB7 +#define MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256 0xB8 /**< Weak! */ +#define MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384 0xB9 /**< Weak! */ + +#define MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBA /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xBE /**< TLS 1.2 */ + +#define MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC0 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 0xC4 /**< TLS 1.2 */ + +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001 /**< Weak! */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004 +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005 + +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA 0xC006 /**< Weak! */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A + +#define MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA 0xC00B /**< Weak! */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA 0xC00E +#define MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA 0xC00F + +#define MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA 0xC010 /**< Weak! */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 +#define MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 + +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 0xC025 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 0xC026 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 0xC028 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 0xC029 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 0xC02A /**< TLS 1.2 */ + +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 0xC02E /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 0xC032 /**< TLS 1.2 */ + +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA 0xC035 +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA 0xC036 +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 0xC037 +#define MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 0xC038 +#define MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA 0xC039 +#define MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256 0xC03A +#define MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384 0xC03B + +#define MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256 0xC03C /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384 0xC03D /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC044 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC045 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC048 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC049 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 0xC04A /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 0xC04B /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 0xC04C /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 0xC04D /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 0xC04E /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 0xC04F /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256 0xC050 /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384 0xC051 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC052 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC053 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05C /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05D /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 0xC05E /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 0xC05F /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 0xC060 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 0xC061 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 0xC062 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 0xC063 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256 0xC064 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384 0xC065 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC066 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC067 /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 0xC068 /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 0xC069 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256 0xC06A /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384 0xC06B /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 0xC06C /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 0xC06D /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 0xC06E /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 0xC06F /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 0xC070 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 0xC071 /**< TLS 1.2 */ + +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC072 +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC073 +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 0xC074 +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 0xC075 +#define MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC076 +#define MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC077 +#define MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 0xC078 +#define MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 0xC079 + +#define MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07A /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07B /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC07C /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC07D /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC086 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC087 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 0xC088 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 0xC089 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC08A /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC08B /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 0xC08C /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 0xC08D /**< TLS 1.2 */ + +#define MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC08E /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC08F /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC090 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC091 /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 0xC092 /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 0xC093 /**< TLS 1.2 */ + +#define MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC094 +#define MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC095 +#define MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC096 +#define MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC097 +#define MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC098 +#define MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC099 +#define MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 0xC09A +#define MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 0xC09B + +#define MBEDTLS_TLS_RSA_WITH_AES_128_CCM 0xC09C /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_AES_256_CCM 0xC09D /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM 0xC09E /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM 0xC09F /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8 0xC0A0 /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8 0xC0A1 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8 0xC0A2 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8 0xC0A3 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_AES_128_CCM 0xC0A4 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_AES_256_CCM 0xC0A5 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM 0xC0A6 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM 0xC0A7 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8 0xC0A8 /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8 0xC0A9 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8 0xC0AA /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8 0xC0AB /**< TLS 1.2 */ +/* The last two are named with PSK_DHE in the RFC, which looks like a typo */ + +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM 0xC0AC /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM 0xC0AD /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 0xC0AE /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 0xC0AF /**< TLS 1.2 */ + +#define MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 0xC0FF /**< experimental */ + +/* RFC 7905 */ +#define MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA /**< TLS 1.2 */ +#define MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAB /**< TLS 1.2 */ +#define MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAC /**< TLS 1.2 */ +#define MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAD /**< TLS 1.2 */ +#define MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAE /**< TLS 1.2 */ + +/* RFC 8446, Appendix B.4 */ +#define MBEDTLS_TLS1_3_AES_128_GCM_SHA256 0x1301 /**< TLS 1.3 */ +#define MBEDTLS_TLS1_3_AES_256_GCM_SHA384 0x1302 /**< TLS 1.3 */ +#define MBEDTLS_TLS1_3_CHACHA20_POLY1305_SHA256 0x1303 /**< TLS 1.3 */ +#define MBEDTLS_TLS1_3_AES_128_CCM_SHA256 0x1304 /**< TLS 1.3 */ +#define MBEDTLS_TLS1_3_AES_128_CCM_8_SHA256 0x1305 /**< TLS 1.3 */ + +/* Reminder: update mbedtls_ssl_premaster_secret when adding a new key exchange. + * Reminder: update MBEDTLS_KEY_EXCHANGE__xxx below + */ +typedef enum { + MBEDTLS_KEY_EXCHANGE_NONE = 0, + MBEDTLS_KEY_EXCHANGE_RSA, + MBEDTLS_KEY_EXCHANGE_DHE_RSA, + MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + MBEDTLS_KEY_EXCHANGE_PSK, + MBEDTLS_KEY_EXCHANGE_DHE_PSK, + MBEDTLS_KEY_EXCHANGE_RSA_PSK, + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + MBEDTLS_KEY_EXCHANGE_ECJPAKE, +} mbedtls_key_exchange_type_t; + +/* Key exchanges using a certificate */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) || \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +#define MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED +#endif + +/* Key exchanges allowing client certificate requests */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED +#endif + +/* Key exchanges involving server signature in ServerKeyExchange */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED +#endif + +/* Key exchanges using ECDH */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED +#endif + +/* Key exchanges that don't involve ephemeral keys */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED +#endif + +/* Key exchanges that involve ephemeral keys */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED +#endif + +/* Key exchanges using a PSK */ +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) || \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +#define MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED +#endif + +/* Key exchanges using DHE */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_SOME_DHE_ENABLED +#endif + +/* Key exchanges using ECDHE */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#define MBEDTLS_KEY_EXCHANGE_SOME_ECDHE_ENABLED +#endif + +typedef struct mbedtls_ssl_ciphersuite_t mbedtls_ssl_ciphersuite_t; + +#define MBEDTLS_CIPHERSUITE_WEAK 0x01 /**< Weak ciphersuite flag */ +#define MBEDTLS_CIPHERSUITE_SHORT_TAG 0x02 /**< Short authentication tag, + eg for CCM_8 */ +#define MBEDTLS_CIPHERSUITE_NODTLS 0x04 /**< Can't be used with DTLS */ + +/** + * \brief This structure is used for storing ciphersuite information + * + * \note members are defined using integral types instead of enums + * in order to pack structure and reduce memory usage by internal + * \c ciphersuite_definitions[] + */ +struct mbedtls_ssl_ciphersuite_t { + int MBEDTLS_PRIVATE(id); + const char *MBEDTLS_PRIVATE(name); + + uint8_t MBEDTLS_PRIVATE(cipher); /* mbedtls_cipher_type_t */ + uint8_t MBEDTLS_PRIVATE(mac); /* mbedtls_md_type_t */ + uint8_t MBEDTLS_PRIVATE(key_exchange); /* mbedtls_key_exchange_type_t */ + uint8_t MBEDTLS_PRIVATE(flags); + + uint16_t MBEDTLS_PRIVATE(min_tls_version); /* mbedtls_ssl_protocol_version */ + uint16_t MBEDTLS_PRIVATE(max_tls_version); /* mbedtls_ssl_protocol_version */ +}; + +const int *mbedtls_ssl_list_ciphersuites(void); + +const mbedtls_ssl_ciphersuite_t *mbedtls_ssl_ciphersuite_from_string(const char *ciphersuite_name); +const mbedtls_ssl_ciphersuite_t *mbedtls_ssl_ciphersuite_from_id(int ciphersuite_id); + +#if defined(MBEDTLS_PK_C) +mbedtls_pk_type_t mbedtls_ssl_get_ciphersuite_sig_pk_alg(const mbedtls_ssl_ciphersuite_t *info); +#if defined(MBEDTLS_USE_PSA_CRYPTO) +psa_algorithm_t mbedtls_ssl_get_ciphersuite_sig_pk_psa_alg(const mbedtls_ssl_ciphersuite_t *info); +psa_key_usage_t mbedtls_ssl_get_ciphersuite_sig_pk_psa_usage(const mbedtls_ssl_ciphersuite_t *info); +#endif +mbedtls_pk_type_t mbedtls_ssl_get_ciphersuite_sig_alg(const mbedtls_ssl_ciphersuite_t *info); +#endif + +int mbedtls_ssl_ciphersuite_uses_ec(const mbedtls_ssl_ciphersuite_t *info); +int mbedtls_ssl_ciphersuite_uses_psk(const mbedtls_ssl_ciphersuite_t *info); + +static inline const char *mbedtls_ssl_ciphersuite_get_name(const mbedtls_ssl_ciphersuite_t *info) +{ + return info->MBEDTLS_PRIVATE(name); +} + +size_t mbedtls_ssl_ciphersuite_get_cipher_key_bitlen(const mbedtls_ssl_ciphersuite_t *info); + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED) +static inline int mbedtls_ssl_ciphersuite_has_pfs(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_DHE_PSK: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK: + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + case MBEDTLS_KEY_EXCHANGE_ECJPAKE: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED) +static inline int mbedtls_ssl_ciphersuite_no_pfs(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + case MBEDTLS_KEY_EXCHANGE_RSA: + case MBEDTLS_KEY_EXCHANGE_PSK: + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED) +static inline int mbedtls_ssl_ciphersuite_uses_ecdh(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED */ + +static inline int mbedtls_ssl_ciphersuite_cert_req_allowed(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_RSA: + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + return 1; + + default: + return 0; + } +} + +static inline int mbedtls_ssl_ciphersuite_uses_srv_cert(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_RSA: + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + return 1; + + default: + return 0; + } +} + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_DHE_ENABLED) +static inline int mbedtls_ssl_ciphersuite_uses_dhe(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_DHE_PSK: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_DHE_ENABLED) */ + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDHE_ENABLED) +static inline int mbedtls_ssl_ciphersuite_uses_ecdhe(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_ECDHE_ENABLED) */ + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) +static inline int mbedtls_ssl_ciphersuite_uses_server_signature( + const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->MBEDTLS_PRIVATE(key_exchange)) { + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED */ + +#ifdef __cplusplus +} +#endif + +#endif /* ssl_ciphersuites.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_cookie.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_cookie.h new file mode 100644 index 00000000..5cd1847d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_cookie.h @@ -0,0 +1,118 @@ +/** + * \file ssl_cookie.h + * + * \brief DTLS cookie callbacks implementation + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SSL_COOKIE_H +#define MBEDTLS_SSL_COOKIE_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/ssl.h" + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + +/** + * \name SECTION: Module settings + * + * The configuration options you can set for this module are in this section. + * Either change them in mbedtls_config.h or define them on the compiler command line. + * \{ + */ +#ifndef MBEDTLS_SSL_COOKIE_TIMEOUT +#define MBEDTLS_SSL_COOKIE_TIMEOUT 60 /**< Default expiration delay of DTLS cookies, in seconds if HAVE_TIME, or in number of cookies issued */ +#endif + +/** \} name SECTION: Module settings */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Context for the default cookie functions. + */ +typedef struct mbedtls_ssl_cookie_ctx { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_svc_key_id_t MBEDTLS_PRIVATE(psa_hmac_key); /*!< key id for the HMAC portion */ + psa_algorithm_t MBEDTLS_PRIVATE(psa_hmac_alg); /*!< key algorithm for the HMAC portion */ +#else + mbedtls_md_context_t MBEDTLS_PRIVATE(hmac_ctx); /*!< context for the HMAC portion */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#if !defined(MBEDTLS_HAVE_TIME) + unsigned long MBEDTLS_PRIVATE(serial); /*!< serial number for expiration */ +#endif + unsigned long MBEDTLS_PRIVATE(timeout); /*!< timeout delay, in seconds if HAVE_TIME, + or in number of tickets issued */ + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(MBEDTLS_THREADING_C) + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); +#endif +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ +} mbedtls_ssl_cookie_ctx; + +/** + * \brief Initialize cookie context + */ +void mbedtls_ssl_cookie_init(mbedtls_ssl_cookie_ctx *ctx); + +/** + * \brief Setup cookie context (generate keys) + */ +int mbedtls_ssl_cookie_setup(mbedtls_ssl_cookie_ctx *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Set expiration delay for cookies + * (Default MBEDTLS_SSL_COOKIE_TIMEOUT) + * + * \param ctx Cookie context + * \param delay Delay, in seconds if HAVE_TIME, or in number of cookies + * issued in the meantime. + * 0 to disable expiration (NOT recommended) + */ +void mbedtls_ssl_cookie_set_timeout(mbedtls_ssl_cookie_ctx *ctx, unsigned long delay); + +/** + * \brief Free cookie context + */ +void mbedtls_ssl_cookie_free(mbedtls_ssl_cookie_ctx *ctx); + +/** + * \brief Generate cookie, see \c mbedtls_ssl_cookie_write_t + */ +mbedtls_ssl_cookie_write_t mbedtls_ssl_cookie_write; + +/** + * \brief Verify cookie, see \c mbedtls_ssl_cookie_write_t + */ +mbedtls_ssl_cookie_check_t mbedtls_ssl_cookie_check; + +#ifdef __cplusplus +} +#endif + +#endif /* ssl_cookie.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_ticket.h b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_ticket.h new file mode 100644 index 00000000..0cefe43a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/ssl_ticket.h @@ -0,0 +1,193 @@ +/** + * \file ssl_ticket.h + * + * \brief TLS server ticket callbacks implementation + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SSL_TICKET_H +#define MBEDTLS_SSL_TICKET_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +/* + * This implementation of the session ticket callbacks includes key + * management, rotating the keys periodically in order to preserve forward + * secrecy, when MBEDTLS_HAVE_TIME is defined. + */ + +#include "mbedtls/ssl.h" +#include "mbedtls/cipher.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#endif + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define MBEDTLS_SSL_TICKET_MAX_KEY_BYTES 32 /*!< Max supported key length in bytes */ +#define MBEDTLS_SSL_TICKET_KEY_NAME_BYTES 4 /*!< key name length in bytes */ + +/** + * \brief Information for session ticket protection + */ +typedef struct mbedtls_ssl_ticket_key { + unsigned char MBEDTLS_PRIVATE(name)[MBEDTLS_SSL_TICKET_KEY_NAME_BYTES]; + /*!< random key identifier */ +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t MBEDTLS_PRIVATE(generation_time); /*!< key generation timestamp (seconds) */ +#endif +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_cipher_context_t MBEDTLS_PRIVATE(ctx); /*!< context for auth enc/decryption */ +#else + mbedtls_svc_key_id_t MBEDTLS_PRIVATE(key); /*!< key used for auth enc/decryption */ + psa_algorithm_t MBEDTLS_PRIVATE(alg); /*!< algorithm of auth enc/decryption */ + psa_key_type_t MBEDTLS_PRIVATE(key_type); /*!< key type */ + size_t MBEDTLS_PRIVATE(key_bits); /*!< key length in bits */ +#endif +} +mbedtls_ssl_ticket_key; + +/** + * \brief Context for session ticket handling functions + */ +typedef struct mbedtls_ssl_ticket_context { + mbedtls_ssl_ticket_key MBEDTLS_PRIVATE(keys)[2]; /*!< ticket protection keys */ + unsigned char MBEDTLS_PRIVATE(active); /*!< index of the currently active key */ + + uint32_t MBEDTLS_PRIVATE(ticket_lifetime); /*!< lifetime of tickets in seconds */ + + /** Callback for getting (pseudo-)random numbers */ + int(*MBEDTLS_PRIVATE(f_rng))(void *, unsigned char *, size_t); + void *MBEDTLS_PRIVATE(p_rng); /*!< context for the RNG function */ + +#if defined(MBEDTLS_THREADING_C) + mbedtls_threading_mutex_t MBEDTLS_PRIVATE(mutex); +#endif +} +mbedtls_ssl_ticket_context; + +/** + * \brief Initialize a ticket context. + * (Just make it ready for mbedtls_ssl_ticket_setup() + * or mbedtls_ssl_ticket_free().) + * + * \param ctx Context to be initialized + */ +void mbedtls_ssl_ticket_init(mbedtls_ssl_ticket_context *ctx); + +/** + * \brief Prepare context to be actually used + * + * \param ctx Context to be set up + * \param f_rng RNG callback function (mandatory) + * \param p_rng RNG callback context + * \param cipher AEAD cipher to use for ticket protection. + * Recommended value: MBEDTLS_CIPHER_AES_256_GCM. + * \param lifetime Tickets lifetime in seconds + * Recommended value: 86400 (one day). + * + * \note It is highly recommended to select a cipher that is at + * least as strong as the strongest ciphersuite + * supported. Usually that means a 256-bit key. + * + * \note The lifetime of the keys is twice the lifetime of tickets. + * It is recommended to pick a reasonable lifetime so as not + * to negate the benefits of forward secrecy. + * + * \return 0 if successful, + * or a specific MBEDTLS_ERR_XXX error code + */ +int mbedtls_ssl_ticket_setup(mbedtls_ssl_ticket_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_cipher_type_t cipher, + uint32_t lifetime); + +/** + * \brief Rotate session ticket encryption key to new specified key. + * Provides for external control of session ticket encryption + * key rotation, e.g. for synchronization between different + * machines. If this function is not used, or if not called + * before ticket lifetime expires, then a new session ticket + * encryption key is generated internally in order to avoid + * unbounded session ticket encryption key lifetimes. + * + * \param ctx Context to be set up + * \param name Session ticket encryption key name + * \param nlength Session ticket encryption key name length in bytes + * \param k Session ticket encryption key + * \param klength Session ticket encryption key length in bytes + * \param lifetime Tickets lifetime in seconds + * Recommended value: 86400 (one day). + * + * \note \c name and \c k are recommended to be cryptographically + * random data. + * + * \note \c nlength must match sizeof( ctx->name ) + * + * \note \c klength must be sufficient for use by cipher specified + * to \c mbedtls_ssl_ticket_setup + * + * \note The lifetime of the keys is twice the lifetime of tickets. + * It is recommended to pick a reasonable lifetime so as not + * to negate the benefits of forward secrecy. + * + * \return 0 if successful, + * or a specific MBEDTLS_ERR_XXX error code + */ +int mbedtls_ssl_ticket_rotate(mbedtls_ssl_ticket_context *ctx, + const unsigned char *name, size_t nlength, + const unsigned char *k, size_t klength, + uint32_t lifetime); + +/** + * \brief Implementation of the ticket write callback + * + * \note See \c mbedtls_ssl_ticket_write_t for description + */ +mbedtls_ssl_ticket_write_t mbedtls_ssl_ticket_write; + +/** + * \brief Implementation of the ticket parse callback + * + * \note See \c mbedtls_ssl_ticket_parse_t for description + */ +mbedtls_ssl_ticket_parse_t mbedtls_ssl_ticket_parse; + +/** + * \brief Free a context's content and zeroize it. + * + * \param ctx Context to be cleaned up + */ +void mbedtls_ssl_ticket_free(mbedtls_ssl_ticket_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* ssl_ticket.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/threading.h b/r5dev/thirdparty/mbedtls/include/mbedtls/threading.h new file mode 100644 index 00000000..1b9c7ced --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/threading.h @@ -0,0 +1,117 @@ +/** + * \file threading.h + * + * \brief Threading abstraction layer + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_THREADING_H +#define MBEDTLS_THREADING_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Bad input parameters to function. */ +#define MBEDTLS_ERR_THREADING_BAD_INPUT_DATA -0x001C +/** Locking / unlocking / free failed with error code. */ +#define MBEDTLS_ERR_THREADING_MUTEX_ERROR -0x001E + +#if defined(MBEDTLS_THREADING_PTHREAD) +#include +typedef struct mbedtls_threading_mutex_t { + pthread_mutex_t MBEDTLS_PRIVATE(mutex); + /* is_valid is 0 after a failed init or a free, and nonzero after a + * successful init. This field is not considered part of the public + * API of Mbed TLS and may change without notice. */ + char MBEDTLS_PRIVATE(is_valid); +} mbedtls_threading_mutex_t; +#endif + +#if defined(MBEDTLS_THREADING_ALT) +/* You should define the mbedtls_threading_mutex_t type in your header */ +#include "threading_alt.h" + +/** + * \brief Set your alternate threading implementation function + * pointers and initialize global mutexes. If used, this + * function must be called once in the main thread before any + * other mbed TLS function is called, and + * 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. + * + * \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 + */ +void mbedtls_threading_set_alt(void (*mutex_init)(mbedtls_threading_mutex_t *), + void (*mutex_free)(mbedtls_threading_mutex_t *), + int (*mutex_lock)(mbedtls_threading_mutex_t *), + int (*mutex_unlock)(mbedtls_threading_mutex_t *)); + +/** + * \brief Free global mutexes. + */ +void mbedtls_threading_free_alt(void); +#endif /* MBEDTLS_THREADING_ALT */ + +#if defined(MBEDTLS_THREADING_C) +/* + * The function pointers for mutex_init, mutex_free, mutex_ and mutex_unlock + * + * All these functions are expected to work or the result will be undefined. + */ +extern void (*mbedtls_mutex_init)(mbedtls_threading_mutex_t *mutex); +extern void (*mbedtls_mutex_free)(mbedtls_threading_mutex_t *mutex); +extern int (*mbedtls_mutex_lock)(mbedtls_threading_mutex_t *mutex); +extern int (*mbedtls_mutex_unlock)(mbedtls_threading_mutex_t *mutex); + +/* + * Global mutexes + */ +#if defined(MBEDTLS_FS_IO) +extern mbedtls_threading_mutex_t mbedtls_threading_readdir_mutex; +#endif + +#if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_PLATFORM_GMTIME_R_ALT) +/* This mutex may or may not be used in the default definition of + * mbedtls_platform_gmtime_r(), but in order to determine that, + * we need to check POSIX features, hence modify _POSIX_C_SOURCE. + * With the current approach, this declaration is orphaned, lacking + * an accompanying definition, in case mbedtls_platform_gmtime_r() + * doesn't need it, but that's not a problem. */ +extern mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex; +#endif /* MBEDTLS_HAVE_TIME_DATE && !MBEDTLS_PLATFORM_GMTIME_R_ALT */ + +#endif /* MBEDTLS_THREADING_C */ + +#ifdef __cplusplus +} +#endif + +#endif /* threading.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/timing.h b/r5dev/thirdparty/mbedtls/include/mbedtls/timing.h new file mode 100644 index 00000000..2d4a19c0 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/timing.h @@ -0,0 +1,106 @@ +/** + * \file timing.h + * + * \brief Portable interface to timeouts and to the CPU cycle counter + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_TIMING_H +#define MBEDTLS_TIMING_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MBEDTLS_TIMING_ALT) +// Regular implementation +// + +/** + * \brief timer structure + */ +struct mbedtls_timing_hr_time { + unsigned char MBEDTLS_PRIVATE(opaque)[32]; +}; + +/** + * \brief Context for mbedtls_timing_set/get_delay() + */ +typedef struct mbedtls_timing_delay_context { + struct mbedtls_timing_hr_time MBEDTLS_PRIVATE(timer); + uint32_t MBEDTLS_PRIVATE(int_ms); + uint32_t MBEDTLS_PRIVATE(fin_ms); +} mbedtls_timing_delay_context; + +#else /* MBEDTLS_TIMING_ALT */ +#include "timing_alt.h" +#endif /* MBEDTLS_TIMING_ALT */ + +/* Internal use */ +unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset); + +/** + * \brief Set a pair of delays to watch + * (See \c mbedtls_timing_get_delay().) + * + * \param data Pointer to timing data. + * Must point to a valid \c mbedtls_timing_delay_context struct. + * \param int_ms First (intermediate) delay in milliseconds. + * The effect if int_ms > fin_ms is unspecified. + * \param fin_ms Second (final) delay in milliseconds. + * Pass 0 to cancel the current delay. + * + * \note To set a single delay, either use \c mbedtls_timing_set_timer + * directly or use this function with int_ms == fin_ms. + */ +void mbedtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms); + +/** + * \brief Get the status of delays + * (Memory helper: number of delays passed.) + * + * \param data Pointer to timing data + * Must point to a valid \c mbedtls_timing_delay_context struct. + * + * \return -1 if cancelled (fin_ms = 0), + * 0 if none of the delays are passed, + * 1 if only the intermediate delay is passed, + * 2 if the final delay is passed. + */ +int mbedtls_timing_get_delay(void *data); + +/** + * \brief Get the final timing delay + * + * \param data Pointer to timing data + * Must point to a valid \c mbedtls_timing_delay_context struct. + * + * \return Final timing delay in milliseconds. + */ +uint32_t mbedtls_timing_get_final_delay( + const mbedtls_timing_delay_context *data); + +#ifdef __cplusplus +} +#endif + +#endif /* timing.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/version.h b/r5dev/thirdparty/mbedtls/include/mbedtls/version.h new file mode 100644 index 00000000..bb1a4c3c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/version.h @@ -0,0 +1,90 @@ +/** + * \file version.h + * + * \brief Run-time version information + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This set of run-time variables can be used to determine the version number of + * the Mbed TLS library used. Compile-time version defines for the same can be + * found in build_info.h + */ +#ifndef MBEDTLS_VERSION_H +#define MBEDTLS_VERSION_H + +#include "mbedtls/build_info.h" + +#if defined(MBEDTLS_VERSION_C) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get the version number. + * + * \return The constructed version number in the format + * MMNNPP00 (Major, Minor, Patch). + */ +unsigned int mbedtls_version_get_number(void); + +/** + * Get the version string ("x.y.z"). + * + * \param string The string that will receive the value. + * (Should be at least 9 bytes in size) + */ +void mbedtls_version_get_string(char *string); + +/** + * Get the full version string ("mbed TLS x.y.z"). + * + * \param string The string that will receive the value. The mbed TLS version + * string will use 18 bytes AT MOST including a terminating + * null byte. + * (So the buffer should be at least 18 bytes to receive this + * version string). + */ +void mbedtls_version_get_string_full(char *string); + +/** + * \brief Check if support for a feature was compiled into this + * mbed TLS binary. This allows you to see at runtime if the + * library was for instance compiled with or without + * Multi-threading support. + * + * \note only checks against defines in the sections "System + * support", "mbed TLS modules" and "mbed TLS feature + * support" in mbedtls_config.h + * + * \param feature The string for the define to check (e.g. "MBEDTLS_AES_C") + * + * \return 0 if the feature is present, + * -1 if the feature is not present and + * -2 if support for feature checking as a whole was not + * compiled in. + */ +int mbedtls_version_check_feature(const char *feature); + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_VERSION_C */ + +#endif /* version.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/x509.h b/r5dev/thirdparty/mbedtls/include/mbedtls/x509.h new file mode 100644 index 00000000..bd1947e4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/x509.h @@ -0,0 +1,485 @@ +/** + * \file x509.h + * + * \brief X.509 generic defines and structures + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_X509_H +#define MBEDTLS_X509_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/asn1.h" +#include "mbedtls/pk.h" + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif + +/** + * \addtogroup x509_module + * \{ + */ + +#if !defined(MBEDTLS_X509_MAX_INTERMEDIATE_CA) +/** + * Maximum number of intermediate CAs in a verification chain. + * That is, maximum length of the chain, excluding the end-entity certificate + * and the trusted root certificate. + * + * Set this to a low value to prevent an adversary from making you waste + * resources verifying an overlong certificate chain. + */ +#define MBEDTLS_X509_MAX_INTERMEDIATE_CA 8 +#endif + +/** + * \name X509 Error codes + * \{ + */ +/** Unavailable feature, e.g. RSA hashing/encryption combination. */ +#define MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE -0x2080 +/** Requested OID is unknown. */ +#define MBEDTLS_ERR_X509_UNKNOWN_OID -0x2100 +/** The CRT/CRL/CSR format is invalid, e.g. different type expected. */ +#define MBEDTLS_ERR_X509_INVALID_FORMAT -0x2180 +/** The CRT/CRL/CSR version element is invalid. */ +#define MBEDTLS_ERR_X509_INVALID_VERSION -0x2200 +/** The serial tag or value is invalid. */ +#define MBEDTLS_ERR_X509_INVALID_SERIAL -0x2280 +/** The algorithm tag or value is invalid. */ +#define MBEDTLS_ERR_X509_INVALID_ALG -0x2300 +/** The name tag or value is invalid. */ +#define MBEDTLS_ERR_X509_INVALID_NAME -0x2380 +/** The date tag or value is invalid. */ +#define MBEDTLS_ERR_X509_INVALID_DATE -0x2400 +/** The signature tag or value invalid. */ +#define MBEDTLS_ERR_X509_INVALID_SIGNATURE -0x2480 +/** The extension tag or value is invalid. */ +#define MBEDTLS_ERR_X509_INVALID_EXTENSIONS -0x2500 +/** CRT/CRL/CSR has an unsupported version number. */ +#define MBEDTLS_ERR_X509_UNKNOWN_VERSION -0x2580 +/** Signature algorithm (oid) is unsupported. */ +#define MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG -0x2600 +/** Signature algorithms do not match. (see \c ::mbedtls_x509_crt sig_oid) */ +#define MBEDTLS_ERR_X509_SIG_MISMATCH -0x2680 +/** Certificate verification failed, e.g. CRL, CA or signature check failed. */ +#define MBEDTLS_ERR_X509_CERT_VERIFY_FAILED -0x2700 +/** Format not recognized as DER or PEM. */ +#define MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT -0x2780 +/** Input invalid. */ +#define MBEDTLS_ERR_X509_BAD_INPUT_DATA -0x2800 +/** Allocation of memory failed. */ +#define MBEDTLS_ERR_X509_ALLOC_FAILED -0x2880 +/** Read/write of file failed. */ +#define MBEDTLS_ERR_X509_FILE_IO_ERROR -0x2900 +/** Destination buffer is too small. */ +#define MBEDTLS_ERR_X509_BUFFER_TOO_SMALL -0x2980 +/** A fatal error occurred, eg the chain is too long or the vrfy callback failed. */ +#define MBEDTLS_ERR_X509_FATAL_ERROR -0x3000 +/** \} name X509 Error codes */ + +/** + * \name X509 Verify codes + * \{ + */ +/* Reminder: update x509_crt_verify_strings[] in library/x509_crt.c */ +#define MBEDTLS_X509_BADCERT_EXPIRED 0x01 /**< The certificate validity has expired. */ +#define MBEDTLS_X509_BADCERT_REVOKED 0x02 /**< The certificate has been revoked (is on a CRL). */ +#define MBEDTLS_X509_BADCERT_CN_MISMATCH 0x04 /**< The certificate Common Name (CN) does not match with the expected CN. */ +#define MBEDTLS_X509_BADCERT_NOT_TRUSTED 0x08 /**< The certificate is not correctly signed by the trusted CA. */ +#define MBEDTLS_X509_BADCRL_NOT_TRUSTED 0x10 /**< The CRL is not correctly signed by the trusted CA. */ +#define MBEDTLS_X509_BADCRL_EXPIRED 0x20 /**< The CRL is expired. */ +#define MBEDTLS_X509_BADCERT_MISSING 0x40 /**< Certificate was missing. */ +#define MBEDTLS_X509_BADCERT_SKIP_VERIFY 0x80 /**< Certificate verification was skipped. */ +#define MBEDTLS_X509_BADCERT_OTHER 0x0100 /**< Other reason (can be used by verify callback) */ +#define MBEDTLS_X509_BADCERT_FUTURE 0x0200 /**< The certificate validity starts in the future. */ +#define MBEDTLS_X509_BADCRL_FUTURE 0x0400 /**< The CRL is from the future */ +#define MBEDTLS_X509_BADCERT_KEY_USAGE 0x0800 /**< Usage does not match the keyUsage extension. */ +#define MBEDTLS_X509_BADCERT_EXT_KEY_USAGE 0x1000 /**< Usage does not match the extendedKeyUsage extension. */ +#define MBEDTLS_X509_BADCERT_NS_CERT_TYPE 0x2000 /**< Usage does not match the nsCertType extension. */ +#define MBEDTLS_X509_BADCERT_BAD_MD 0x4000 /**< The certificate is signed with an unacceptable hash. */ +#define MBEDTLS_X509_BADCERT_BAD_PK 0x8000 /**< The certificate is signed with an unacceptable PK alg (eg RSA vs ECDSA). */ +#define MBEDTLS_X509_BADCERT_BAD_KEY 0x010000 /**< The certificate is signed with an unacceptable key (eg bad curve, RSA too short). */ +#define MBEDTLS_X509_BADCRL_BAD_MD 0x020000 /**< The CRL is signed with an unacceptable hash. */ +#define MBEDTLS_X509_BADCRL_BAD_PK 0x040000 /**< The CRL is signed with an unacceptable PK alg (eg RSA vs ECDSA). */ +#define MBEDTLS_X509_BADCRL_BAD_KEY 0x080000 /**< The CRL is signed with an unacceptable key (eg bad curve, RSA too short). */ + +/** \} name X509 Verify codes */ +/** \} addtogroup x509_module */ + +/* + * X.509 v3 Subject Alternative Name types. + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER + */ +#define MBEDTLS_X509_SAN_OTHER_NAME 0 +#define MBEDTLS_X509_SAN_RFC822_NAME 1 +#define MBEDTLS_X509_SAN_DNS_NAME 2 +#define MBEDTLS_X509_SAN_X400_ADDRESS_NAME 3 +#define MBEDTLS_X509_SAN_DIRECTORY_NAME 4 +#define MBEDTLS_X509_SAN_EDI_PARTY_NAME 5 +#define MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER 6 +#define MBEDTLS_X509_SAN_IP_ADDRESS 7 +#define MBEDTLS_X509_SAN_REGISTERED_ID 8 + +/* + * X.509 v3 Key Usage Extension flags + * Reminder: update mbedtls_x509_info_key_usage() when adding new flags. + */ +#define MBEDTLS_X509_KU_DIGITAL_SIGNATURE (0x80) /* bit 0 */ +#define MBEDTLS_X509_KU_NON_REPUDIATION (0x40) /* bit 1 */ +#define MBEDTLS_X509_KU_KEY_ENCIPHERMENT (0x20) /* bit 2 */ +#define MBEDTLS_X509_KU_DATA_ENCIPHERMENT (0x10) /* bit 3 */ +#define MBEDTLS_X509_KU_KEY_AGREEMENT (0x08) /* bit 4 */ +#define MBEDTLS_X509_KU_KEY_CERT_SIGN (0x04) /* bit 5 */ +#define MBEDTLS_X509_KU_CRL_SIGN (0x02) /* bit 6 */ +#define MBEDTLS_X509_KU_ENCIPHER_ONLY (0x01) /* bit 7 */ +#define MBEDTLS_X509_KU_DECIPHER_ONLY (0x8000) /* bit 8 */ + +/* + * Netscape certificate types + * (http://www.mozilla.org/projects/security/pki/nss/tech-notes/tn3.html) + */ + +#define MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT (0x80) /* bit 0 */ +#define MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER (0x40) /* bit 1 */ +#define MBEDTLS_X509_NS_CERT_TYPE_EMAIL (0x20) /* bit 2 */ +#define MBEDTLS_X509_NS_CERT_TYPE_OBJECT_SIGNING (0x10) /* bit 3 */ +#define MBEDTLS_X509_NS_CERT_TYPE_RESERVED (0x08) /* bit 4 */ +#define MBEDTLS_X509_NS_CERT_TYPE_SSL_CA (0x04) /* bit 5 */ +#define MBEDTLS_X509_NS_CERT_TYPE_EMAIL_CA (0x02) /* bit 6 */ +#define MBEDTLS_X509_NS_CERT_TYPE_OBJECT_SIGNING_CA (0x01) /* bit 7 */ + +/* + * X.509 extension types + * + * Comments refer to the status for using certificates. Status can be + * different for writing certificates or reading CRLs or CSRs. + * + * Those are defined in oid.h as oid.c needs them in a data structure. Since + * these were previously defined here, let's have aliases for compatibility. + */ +#define MBEDTLS_X509_EXT_AUTHORITY_KEY_IDENTIFIER MBEDTLS_OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER +#define MBEDTLS_X509_EXT_SUBJECT_KEY_IDENTIFIER MBEDTLS_OID_X509_EXT_SUBJECT_KEY_IDENTIFIER +#define MBEDTLS_X509_EXT_KEY_USAGE MBEDTLS_OID_X509_EXT_KEY_USAGE +#define MBEDTLS_X509_EXT_CERTIFICATE_POLICIES MBEDTLS_OID_X509_EXT_CERTIFICATE_POLICIES +#define MBEDTLS_X509_EXT_POLICY_MAPPINGS MBEDTLS_OID_X509_EXT_POLICY_MAPPINGS +#define MBEDTLS_X509_EXT_SUBJECT_ALT_NAME MBEDTLS_OID_X509_EXT_SUBJECT_ALT_NAME /* Supported (DNS) */ +#define MBEDTLS_X509_EXT_ISSUER_ALT_NAME MBEDTLS_OID_X509_EXT_ISSUER_ALT_NAME +#define MBEDTLS_X509_EXT_SUBJECT_DIRECTORY_ATTRS MBEDTLS_OID_X509_EXT_SUBJECT_DIRECTORY_ATTRS +#define MBEDTLS_X509_EXT_BASIC_CONSTRAINTS MBEDTLS_OID_X509_EXT_BASIC_CONSTRAINTS /* Supported */ +#define MBEDTLS_X509_EXT_NAME_CONSTRAINTS MBEDTLS_OID_X509_EXT_NAME_CONSTRAINTS +#define MBEDTLS_X509_EXT_POLICY_CONSTRAINTS MBEDTLS_OID_X509_EXT_POLICY_CONSTRAINTS +#define MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE MBEDTLS_OID_X509_EXT_EXTENDED_KEY_USAGE +#define MBEDTLS_X509_EXT_CRL_DISTRIBUTION_POINTS MBEDTLS_OID_X509_EXT_CRL_DISTRIBUTION_POINTS +#define MBEDTLS_X509_EXT_INIHIBIT_ANYPOLICY MBEDTLS_OID_X509_EXT_INIHIBIT_ANYPOLICY +#define MBEDTLS_X509_EXT_FRESHEST_CRL MBEDTLS_OID_X509_EXT_FRESHEST_CRL +#define MBEDTLS_X509_EXT_NS_CERT_TYPE MBEDTLS_OID_X509_EXT_NS_CERT_TYPE + +/* + * Storage format identifiers + * Recognized formats: PEM and DER + */ +#define MBEDTLS_X509_FORMAT_DER 1 +#define MBEDTLS_X509_FORMAT_PEM 2 + +#define MBEDTLS_X509_MAX_DN_NAME_SIZE 256 /**< Maximum value size of a DN entry */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup x509_module + * \{ */ + +/** + * \name Structures for parsing X.509 certificates, CRLs and CSRs + * \{ + */ + +/** + * Type-length-value structure that allows for ASN1 using DER. + */ +typedef mbedtls_asn1_buf mbedtls_x509_buf; + +/** + * Container for ASN1 bit strings. + */ +typedef mbedtls_asn1_bitstring mbedtls_x509_bitstring; + +/** + * Container for ASN1 named information objects. + * It allows for Relative Distinguished Names (e.g. cn=localhost,ou=code,etc.). + */ +typedef mbedtls_asn1_named_data mbedtls_x509_name; + +/** + * Container for a sequence of ASN.1 items + */ +typedef mbedtls_asn1_sequence mbedtls_x509_sequence; + +/** Container for date and time (precision in seconds). */ +typedef struct mbedtls_x509_time { + int year, mon, day; /**< Date. */ + int hour, min, sec; /**< Time. */ +} +mbedtls_x509_time; + +/** + * From RFC 5280 section 4.2.1.6: + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * Future versions of the library may add new fields to this structure or + * to its embedded union and structure. + */ +typedef struct mbedtls_x509_san_other_name { + /** + * The type_id is an OID as defined in RFC 5280. + * To check the value of the type id, you should use + * \p MBEDTLS_OID_CMP with a known OID mbedtls_x509_buf. + */ + mbedtls_x509_buf type_id; /**< The type id. */ + union { + /** + * From RFC 4108 section 5: + * HardwareModuleName ::= SEQUENCE { + * hwType OBJECT IDENTIFIER, + * hwSerialNum OCTET STRING } + */ + struct { + mbedtls_x509_buf oid; /**< The object identifier. */ + mbedtls_x509_buf val; /**< The named value. */ + } + hardware_module_name; + } + value; +} +mbedtls_x509_san_other_name; + +/** + * A structure for holding the parsed Subject Alternative Name, + * according to type. + * + * Future versions of the library may add new fields to this structure or + * to its embedded union and structure. + */ +typedef struct mbedtls_x509_subject_alternative_name { + int type; /**< The SAN type, value of MBEDTLS_X509_SAN_XXX. */ + union { + mbedtls_x509_san_other_name other_name; /**< The otherName supported type. */ + mbedtls_x509_buf unstructured_name; /**< The buffer for the unconstructed types. Only rfc822Name, dnsName and uniformResourceIdentifier are currently supported */ + } + san; /**< A union of the supported SAN types */ +} +mbedtls_x509_subject_alternative_name; + +/** \} name Structures for parsing X.509 certificates, CRLs and CSRs */ + +/** + * \brief Store the certificate DN in printable form into buf; + * no more than size characters will be written. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param dn The X509 name to represent + * + * \return The length of the string written (not including the + * terminated nul byte), or a negative error code. + */ +int mbedtls_x509_dn_gets(char *buf, size_t size, const mbedtls_x509_name *dn); + +/** + * \brief Return the next relative DN in an X509 name. + * + * \note Intended use is to compare function result to dn->next + * in order to detect boundaries of multi-valued RDNs. + * + * \param dn Current node in the X509 name + * + * \return Pointer to the first attribute-value pair of the + * next RDN in sequence, or NULL if end is reached. + */ +static inline mbedtls_x509_name *mbedtls_x509_dn_get_next( + mbedtls_x509_name *dn) +{ + while (dn->MBEDTLS_PRIVATE(next_merged) && dn->next != NULL) { + dn = dn->next; + } + return dn->next; +} + +/** + * \brief Store the certificate serial in printable form into buf; + * no more than size characters will be written. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param serial The X509 serial to represent + * + * \return The length of the string written (not including the + * terminated nul byte), or a negative error code. + */ +int mbedtls_x509_serial_gets(char *buf, size_t size, const mbedtls_x509_buf *serial); + +/** + * \brief Check a given mbedtls_x509_time against the system time + * and tell if it's in the past. + * + * \note Intended usage is "if( is_past( valid_to ) ) ERROR". + * Hence the return value of 1 if on internal errors. + * + * \param to mbedtls_x509_time to check + * + * \return 1 if the given time is in the past or an error occurred, + * 0 otherwise. + */ +int mbedtls_x509_time_is_past(const mbedtls_x509_time *to); + +/** + * \brief Check a given mbedtls_x509_time against the system time + * and tell if it's in the future. + * + * \note Intended usage is "if( is_future( valid_from ) ) ERROR". + * Hence the return value of 1 if on internal errors. + * + * \param from mbedtls_x509_time to check + * + * \return 1 if the given time is in the future or an error occurred, + * 0 otherwise. + */ +int mbedtls_x509_time_is_future(const mbedtls_x509_time *from); + +/** + * \brief This function parses an item in the SubjectAlternativeNames + * extension. + * + * \param san_buf The buffer holding the raw data item of the subject + * alternative name. + * \param san The target structure to populate with the parsed presentation + * of the subject alternative name encoded in \p san_raw. + * + * \note Supported GeneralName types, as defined in RFC 5280: + * "rfc822Name", "dnsName", "uniformResourceIdentifier" and "hardware_module_name" + * of type "otherName", as defined in RFC 4108. + * + * \note This function should be called on a single raw data of + * subject alternative name. For example, after successful + * certificate parsing, one must iterate on every item in the + * \p crt->subject_alt_names sequence, and pass it to + * this function. + * + * \warning The target structure contains pointers to the raw data of the + * parsed certificate, and its lifetime is restricted by the + * lifetime of the certificate. + * + * \return \c 0 on success + * \return #MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE for an unsupported + * SAN type. + * \return Another negative value for any other failure. + */ +int mbedtls_x509_parse_subject_alt_name(const mbedtls_x509_buf *san_buf, + mbedtls_x509_subject_alternative_name *san); + +/** \} addtogroup x509_module */ + +/* + * Internal module functions. You probably do not want to use these unless you + * know you do. + */ +int mbedtls_x509_get_name(unsigned char **p, const unsigned char *end, + mbedtls_x509_name *cur); +int mbedtls_x509_get_alg_null(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *alg); +int mbedtls_x509_get_alg(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *alg, mbedtls_x509_buf *params); +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) +int mbedtls_x509_get_rsassa_pss_params(const mbedtls_x509_buf *params, + mbedtls_md_type_t *md_alg, mbedtls_md_type_t *mgf_md, + int *salt_len); +#endif +int mbedtls_x509_get_sig(unsigned char **p, const unsigned char *end, mbedtls_x509_buf *sig); +int mbedtls_x509_get_sig_alg(const mbedtls_x509_buf *sig_oid, const mbedtls_x509_buf *sig_params, + mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg, + void **sig_opts); +int mbedtls_x509_get_time(unsigned char **p, const unsigned char *end, + mbedtls_x509_time *t); +int mbedtls_x509_get_serial(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *serial); +int mbedtls_x509_get_ext(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *ext, int tag); +#if !defined(MBEDTLS_X509_REMOVE_INFO) +int mbedtls_x509_sig_alg_gets(char *buf, size_t size, const mbedtls_x509_buf *sig_oid, + mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg, + const void *sig_opts); +#endif +int mbedtls_x509_key_size_helper(char *buf, size_t buf_size, const char *name); +int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *name); +int mbedtls_x509_set_extension(mbedtls_asn1_named_data **head, const char *oid, size_t oid_len, + int critical, const unsigned char *val, + size_t val_len); +int mbedtls_x509_write_extensions(unsigned char **p, unsigned char *start, + mbedtls_asn1_named_data *first); +int mbedtls_x509_write_names(unsigned char **p, unsigned char *start, + mbedtls_asn1_named_data *first); +int mbedtls_x509_write_sig(unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + unsigned char *sig, size_t size); +int mbedtls_x509_get_ns_cert_type(unsigned char **p, + const unsigned char *end, + unsigned char *ns_cert_type); +int mbedtls_x509_get_key_usage(unsigned char **p, + const unsigned char *end, + unsigned int *key_usage); +int mbedtls_x509_get_subject_alt_name(unsigned char **p, + const unsigned char *end, + mbedtls_x509_sequence *subject_alt_name); +int mbedtls_x509_info_subject_alt_name(char **buf, size_t *size, + const mbedtls_x509_sequence + *subject_alt_name, + const char *prefix); +int mbedtls_x509_info_cert_type(char **buf, size_t *size, + unsigned char ns_cert_type); +int mbedtls_x509_info_key_usage(char **buf, size_t *size, + unsigned int key_usage); + +#define MBEDTLS_X509_SAFE_SNPRINTF \ + do { \ + if (ret < 0 || (size_t) ret >= n) \ + return MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; \ + \ + n -= (size_t) ret; \ + p += (size_t) ret; \ + } while (0) + +#ifdef __cplusplus +} +#endif + +#endif /* x509.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/x509_crl.h b/r5dev/thirdparty/mbedtls/include/mbedtls/x509_crl.h new file mode 100644 index 00000000..62694ae7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/x509_crl.h @@ -0,0 +1,196 @@ +/** + * \file x509_crl.h + * + * \brief X.509 certificate revocation list parsing + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_X509_CRL_H +#define MBEDTLS_X509_CRL_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/x509.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup x509_module + * \{ */ + +/** + * \name Structures and functions for parsing CRLs + * \{ + */ + +/** + * Certificate revocation list entry. + * Contains the CA-specific serial numbers and revocation dates. + * + * Some fields of this structure are publicly readable. Do not modify + * them except via Mbed TLS library functions: the effect of modifying + * those fields or the data that those fields points to is unspecified. + */ +typedef struct mbedtls_x509_crl_entry { + /** Direct access to the whole entry inside the containing buffer. */ + mbedtls_x509_buf raw; + /** The serial number of the revoked certificate. */ + mbedtls_x509_buf serial; + /** The revocation date of this entry. */ + mbedtls_x509_time revocation_date; + /** Direct access to the list of CRL entry extensions + * (an ASN.1 constructed sequence). + * + * If there are no extensions, `entry_ext.len == 0` and + * `entry_ext.p == NULL`. */ + mbedtls_x509_buf entry_ext; + + /** Next element in the linked list of entries. + * \p NULL indicates the end of the list. + * Do not modify this field directly. */ + struct mbedtls_x509_crl_entry *next; +} +mbedtls_x509_crl_entry; + +/** + * Certificate revocation list structure. + * Every CRL may have multiple entries. + */ +typedef struct mbedtls_x509_crl { + mbedtls_x509_buf raw; /**< The raw certificate data (DER). */ + mbedtls_x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ + + int version; /**< CRL version (1=v1, 2=v2) */ + mbedtls_x509_buf sig_oid; /**< CRL signature type identifier */ + + mbedtls_x509_buf issuer_raw; /**< The raw issuer data (DER). */ + + mbedtls_x509_name issuer; /**< The parsed issuer data (named information object). */ + + mbedtls_x509_time this_update; + mbedtls_x509_time next_update; + + mbedtls_x509_crl_entry entry; /**< The CRL entries containing the certificate revocation times for this CA. */ + + mbedtls_x509_buf crl_ext; + + mbedtls_x509_buf MBEDTLS_PRIVATE(sig_oid2); + mbedtls_x509_buf MBEDTLS_PRIVATE(sig); + mbedtls_md_type_t MBEDTLS_PRIVATE(sig_md); /**< Internal representation of the MD algorithm of the signature algorithm, e.g. MBEDTLS_MD_SHA256 */ + mbedtls_pk_type_t MBEDTLS_PRIVATE(sig_pk); /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. MBEDTLS_PK_RSA */ + void *MBEDTLS_PRIVATE(sig_opts); /**< Signature options to be passed to mbedtls_pk_verify_ext(), e.g. for RSASSA-PSS */ + + /** Next element in the linked list of CRL. + * \p NULL indicates the end of the list. + * Do not modify this field directly. */ + struct mbedtls_x509_crl *next; +} +mbedtls_x509_crl; + +/** + * \brief Parse a DER-encoded CRL and append it to the chained list + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain points to the start of the chain + * \param buf buffer holding the CRL data in DER format + * \param buflen size of the buffer + * (including the terminating null byte for PEM data) + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int mbedtls_x509_crl_parse_der(mbedtls_x509_crl *chain, + const unsigned char *buf, size_t buflen); +/** + * \brief Parse one or more CRLs and append them to the chained list + * + * \note Multiple CRLs are accepted only if using PEM format + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain points to the start of the chain + * \param buf buffer holding the CRL data in PEM or DER format + * \param buflen size of the buffer + * (including the terminating null byte for PEM data) + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int mbedtls_x509_crl_parse(mbedtls_x509_crl *chain, const unsigned char *buf, size_t buflen); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief Load one or more CRLs and append them to the chained list + * + * \note Multiple CRLs are accepted only if using PEM format + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain points to the start of the chain + * \param path filename to read the CRLs from (in PEM or DER encoding) + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int mbedtls_x509_crl_parse_file(mbedtls_x509_crl *chain, const char *path); +#endif /* MBEDTLS_FS_IO */ + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/** + * \brief Returns an informational string about the CRL. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param prefix A line prefix + * \param crl The X509 CRL to represent + * + * \return The length of the string written (not including the + * terminated nul byte), or a negative error code. + */ +int mbedtls_x509_crl_info(char *buf, size_t size, const char *prefix, + const mbedtls_x509_crl *crl); +#endif /* !MBEDTLS_X509_REMOVE_INFO */ + +/** + * \brief Initialize a CRL (chain) + * + * \param crl CRL chain to initialize + */ +void mbedtls_x509_crl_init(mbedtls_x509_crl *crl); + +/** + * \brief Unallocate all CRL data + * + * \param crl CRL chain to free + */ +void mbedtls_x509_crl_free(mbedtls_x509_crl *crl); + +/** \} name Structures and functions for parsing CRLs */ +/** \} addtogroup x509_module */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_x509_crl.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/x509_crt.h b/r5dev/thirdparty/mbedtls/include/mbedtls/x509_crt.h new file mode 100644 index 00000000..11e5951f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/x509_crt.h @@ -0,0 +1,1187 @@ +/** + * \file x509_crt.h + * + * \brief X.509 certificate parsing and writing + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_X509_CRT_H +#define MBEDTLS_X509_CRT_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" +#include "mbedtls/legacy_or_psa.h" + +#include "mbedtls/x509.h" +#include "mbedtls/x509_crl.h" +#include "mbedtls/bignum.h" + +/** + * \addtogroup x509_module + * \{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \name Structures and functions for parsing and writing X.509 certificates + * \{ + */ + +/** + * Container for an X.509 certificate. The certificate may be chained. + * + * Some fields of this structure are publicly readable. Do not modify + * them except via Mbed TLS library functions: the effect of modifying + * those fields or the data that those fields points to is unspecified. + */ +typedef struct mbedtls_x509_crt { + int MBEDTLS_PRIVATE(own_buffer); /**< Indicates if \c raw is owned + * by the structure or not. */ + mbedtls_x509_buf raw; /**< The raw certificate data (DER). */ + mbedtls_x509_buf tbs; /**< The raw certificate body (DER). The part that is To Be Signed. */ + + int version; /**< The X.509 version. (1=v1, 2=v2, 3=v3) */ + mbedtls_x509_buf serial; /**< Unique id for certificate issued by a specific CA. */ + mbedtls_x509_buf sig_oid; /**< Signature algorithm, e.g. sha1RSA */ + + mbedtls_x509_buf issuer_raw; /**< The raw issuer data (DER). Used for quick comparison. */ + mbedtls_x509_buf subject_raw; /**< The raw subject data (DER). Used for quick comparison. */ + + mbedtls_x509_name issuer; /**< The parsed issuer data (named information object). */ + mbedtls_x509_name subject; /**< The parsed subject data (named information object). */ + + mbedtls_x509_time valid_from; /**< Start time of certificate validity. */ + mbedtls_x509_time valid_to; /**< End time of certificate validity. */ + + mbedtls_x509_buf pk_raw; + mbedtls_pk_context pk; /**< Container for the public key context. */ + + mbedtls_x509_buf issuer_id; /**< Optional X.509 v2/v3 issuer unique identifier. */ + mbedtls_x509_buf subject_id; /**< Optional X.509 v2/v3 subject unique identifier. */ + mbedtls_x509_buf v3_ext; /**< Optional X.509 v3 extensions. */ + mbedtls_x509_sequence subject_alt_names; /**< Optional list of raw entries of Subject Alternative Names extension (currently only dNSName, uniformResourceIdentifier and OtherName are listed). */ + + mbedtls_x509_sequence certificate_policies; /**< Optional list of certificate policies (Only anyPolicy is printed and enforced, however the rest of the policies are still listed). */ + + int MBEDTLS_PRIVATE(ext_types); /**< Bit string containing detected and parsed extensions */ + int MBEDTLS_PRIVATE(ca_istrue); /**< Optional Basic Constraint extension value: 1 if this certificate belongs to a CA, 0 otherwise. */ + int MBEDTLS_PRIVATE(max_pathlen); /**< Optional Basic Constraint extension value: The maximum path length to the root certificate. Path length is 1 higher than RFC 5280 'meaning', so 1+ */ + + unsigned int MBEDTLS_PRIVATE(key_usage); /**< Optional key usage extension value: See the values in x509.h */ + + mbedtls_x509_sequence ext_key_usage; /**< Optional list of extended key usage OIDs. */ + + unsigned char MBEDTLS_PRIVATE(ns_cert_type); /**< Optional Netscape certificate type extension value: See the values in x509.h */ + + mbedtls_x509_buf MBEDTLS_PRIVATE(sig); /**< Signature: hash of the tbs part signed with the private key. */ + mbedtls_md_type_t MBEDTLS_PRIVATE(sig_md); /**< Internal representation of the MD algorithm of the signature algorithm, e.g. MBEDTLS_MD_SHA256 */ + mbedtls_pk_type_t MBEDTLS_PRIVATE(sig_pk); /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. MBEDTLS_PK_RSA */ + void *MBEDTLS_PRIVATE(sig_opts); /**< Signature options to be passed to mbedtls_pk_verify_ext(), e.g. for RSASSA-PSS */ + + /** Next certificate in the linked list that constitutes the CA chain. + * \p NULL indicates the end of the list. + * Do not modify this field directly. */ + struct mbedtls_x509_crt *next; +} +mbedtls_x509_crt; + +/** + * Build flag from an algorithm/curve identifier (pk, md, ecp) + * Since 0 is always XXX_NONE, ignore it. + */ +#define MBEDTLS_X509_ID_FLAG(id) (1 << ((id) - 1)) + +/** + * Security profile for certificate verification. + * + * All lists are bitfields, built by ORing flags from MBEDTLS_X509_ID_FLAG(). + * + * The fields of this structure are part of the public API and can be + * manipulated directly by applications. Future versions of the library may + * add extra fields or reorder existing fields. + * + * You can create custom profiles by starting from a copy of + * an existing profile, such as mbedtls_x509_crt_profile_default or + * mbedtls_x509_ctr_profile_none and then tune it to your needs. + * + * For example to allow SHA-224 in addition to the default: + * + * mbedtls_x509_crt_profile my_profile = mbedtls_x509_crt_profile_default; + * my_profile.allowed_mds |= MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ); + * + * Or to allow only RSA-3072+ with SHA-256: + * + * mbedtls_x509_crt_profile my_profile = mbedtls_x509_crt_profile_none; + * my_profile.allowed_mds = MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ); + * my_profile.allowed_pks = MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_RSA ); + * my_profile.rsa_min_bitlen = 3072; + */ +typedef struct mbedtls_x509_crt_profile { + uint32_t allowed_mds; /**< MDs for signatures */ + uint32_t allowed_pks; /**< PK algs for public keys; + * this applies to all certificates + * in the provided chain. */ + uint32_t allowed_curves; /**< Elliptic curves for ECDSA */ + uint32_t rsa_min_bitlen; /**< Minimum size for RSA keys */ +} +mbedtls_x509_crt_profile; + +#define MBEDTLS_X509_CRT_VERSION_1 0 +#define MBEDTLS_X509_CRT_VERSION_2 1 +#define MBEDTLS_X509_CRT_VERSION_3 2 + +#define MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN 20 +#define MBEDTLS_X509_RFC5280_UTC_TIME_LEN 15 + +#if !defined(MBEDTLS_X509_MAX_FILE_PATH_LEN) +#define MBEDTLS_X509_MAX_FILE_PATH_LEN 512 +#endif + +/* This macro unfolds to the concatenation of macro invocations + * X509_CRT_ERROR_INFO( error code, + * error code as string, + * human readable description ) + * where X509_CRT_ERROR_INFO is defined by the user. + * See x509_crt.c for an example of how to use this. */ +#define MBEDTLS_X509_CRT_ERROR_INFO_LIST \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_EXPIRED, \ + "MBEDTLS_X509_BADCERT_EXPIRED", \ + "The certificate validity has expired") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_REVOKED, \ + "MBEDTLS_X509_BADCERT_REVOKED", \ + "The certificate has been revoked (is on a CRL)") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_CN_MISMATCH, \ + "MBEDTLS_X509_BADCERT_CN_MISMATCH", \ + "The certificate Common Name (CN) does not match with the expected CN") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_NOT_TRUSTED, \ + "MBEDTLS_X509_BADCERT_NOT_TRUSTED", \ + "The certificate is not correctly signed by the trusted CA") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_NOT_TRUSTED, \ + "MBEDTLS_X509_BADCRL_NOT_TRUSTED", \ + "The CRL is not correctly signed by the trusted CA") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_EXPIRED, \ + "MBEDTLS_X509_BADCRL_EXPIRED", \ + "The CRL is expired") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_MISSING, \ + "MBEDTLS_X509_BADCERT_MISSING", \ + "Certificate was missing") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_SKIP_VERIFY, \ + "MBEDTLS_X509_BADCERT_SKIP_VERIFY", \ + "Certificate verification was skipped") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_OTHER, \ + "MBEDTLS_X509_BADCERT_OTHER", \ + "Other reason (can be used by verify callback)") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_FUTURE, \ + "MBEDTLS_X509_BADCERT_FUTURE", \ + "The certificate validity starts in the future") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_FUTURE, \ + "MBEDTLS_X509_BADCRL_FUTURE", \ + "The CRL is from the future") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_KEY_USAGE, \ + "MBEDTLS_X509_BADCERT_KEY_USAGE", \ + "Usage does not match the keyUsage extension") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_EXT_KEY_USAGE, \ + "MBEDTLS_X509_BADCERT_EXT_KEY_USAGE", \ + "Usage does not match the extendedKeyUsage extension") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_NS_CERT_TYPE, \ + "MBEDTLS_X509_BADCERT_NS_CERT_TYPE", \ + "Usage does not match the nsCertType extension") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_BAD_MD, \ + "MBEDTLS_X509_BADCERT_BAD_MD", \ + "The certificate is signed with an unacceptable hash.") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_BAD_PK, \ + "MBEDTLS_X509_BADCERT_BAD_PK", \ + "The certificate is signed with an unacceptable PK alg (eg RSA vs ECDSA).") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCERT_BAD_KEY, \ + "MBEDTLS_X509_BADCERT_BAD_KEY", \ + "The certificate is signed with an unacceptable key (eg bad curve, RSA too short).") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_BAD_MD, \ + "MBEDTLS_X509_BADCRL_BAD_MD", \ + "The CRL is signed with an unacceptable hash.") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_BAD_PK, \ + "MBEDTLS_X509_BADCRL_BAD_PK", \ + "The CRL is signed with an unacceptable PK alg (eg RSA vs ECDSA).") \ + X509_CRT_ERROR_INFO(MBEDTLS_X509_BADCRL_BAD_KEY, \ + "MBEDTLS_X509_BADCRL_BAD_KEY", \ + "The CRL is signed with an unacceptable key (eg bad curve, RSA too short).") + +/** + * Container for writing a certificate (CRT) + */ +typedef struct mbedtls_x509write_cert { + int MBEDTLS_PRIVATE(version); + unsigned char MBEDTLS_PRIVATE(serial)[MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN]; + size_t MBEDTLS_PRIVATE(serial_len); + mbedtls_pk_context *MBEDTLS_PRIVATE(subject_key); + mbedtls_pk_context *MBEDTLS_PRIVATE(issuer_key); + mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject); + mbedtls_asn1_named_data *MBEDTLS_PRIVATE(issuer); + mbedtls_md_type_t MBEDTLS_PRIVATE(md_alg); + char MBEDTLS_PRIVATE(not_before)[MBEDTLS_X509_RFC5280_UTC_TIME_LEN + 1]; + char MBEDTLS_PRIVATE(not_after)[MBEDTLS_X509_RFC5280_UTC_TIME_LEN + 1]; + mbedtls_asn1_named_data *MBEDTLS_PRIVATE(extensions); +} +mbedtls_x509write_cert; + +/** + * Item in a verification chain: cert and flags for it + */ +typedef struct { + mbedtls_x509_crt *MBEDTLS_PRIVATE(crt); + uint32_t MBEDTLS_PRIVATE(flags); +} mbedtls_x509_crt_verify_chain_item; + +/** + * Max size of verification chain: end-entity + intermediates + trusted root + */ +#define MBEDTLS_X509_MAX_VERIFY_CHAIN_SIZE (MBEDTLS_X509_MAX_INTERMEDIATE_CA + 2) + +/** + * Verification chain as built by \c mbedtls_crt_verify_chain() + */ +typedef struct { + mbedtls_x509_crt_verify_chain_item MBEDTLS_PRIVATE(items)[MBEDTLS_X509_MAX_VERIFY_CHAIN_SIZE]; + unsigned MBEDTLS_PRIVATE(len); + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + /* This stores the list of potential trusted signers obtained from + * the CA callback used for the CRT verification, if configured. + * We must track it somewhere because the callback passes its + * ownership to the caller. */ + mbedtls_x509_crt *MBEDTLS_PRIVATE(trust_ca_cb_result); +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ +} mbedtls_x509_crt_verify_chain; + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + +/** + * \brief Context for resuming X.509 verify operations + */ +typedef struct { + /* for check_signature() */ + mbedtls_pk_restart_ctx MBEDTLS_PRIVATE(pk); + + /* for find_parent_in() */ + mbedtls_x509_crt *MBEDTLS_PRIVATE(parent); /* non-null iff parent_in in progress */ + mbedtls_x509_crt *MBEDTLS_PRIVATE(fallback_parent); + int MBEDTLS_PRIVATE(fallback_signature_is_good); + + /* for find_parent() */ + int MBEDTLS_PRIVATE(parent_is_trusted); /* -1 if find_parent is not in progress */ + + /* for verify_chain() */ + enum { + x509_crt_rs_none, + x509_crt_rs_find_parent, + } MBEDTLS_PRIVATE(in_progress); /* none if no operation is in progress */ + int MBEDTLS_PRIVATE(self_cnt); + mbedtls_x509_crt_verify_chain MBEDTLS_PRIVATE(ver_chain); + +} mbedtls_x509_crt_restart_ctx; + +#else /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +/* Now we can declare functions that take a pointer to that */ +typedef void mbedtls_x509_crt_restart_ctx; + +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/** + * Default security profile. Should provide a good balance between security + * and compatibility with current deployments. + * + * This profile permits: + * - SHA2 hashes with at least 256 bits: SHA-256, SHA-384, SHA-512. + * - Elliptic curves with 255 bits and above except secp256k1. + * - RSA with 2048 bits and above. + * + * New minor versions of Mbed TLS may extend this profile, for example if + * new algorithms are added to the library. New minor versions of Mbed TLS will + * not reduce this profile unless serious security concerns require it. + */ +extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_default; + +/** + * Expected next default profile. Recommended for new deployments. + * Currently targets a 128-bit security level, except for allowing RSA-2048. + * This profile may change at any time. + */ +extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_next; + +/** + * NSA Suite B profile. + */ +extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb; + +/** + * Empty profile that allows nothing. Useful as a basis for constructing + * custom profiles. + */ +extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_none; + +/** + * \brief Parse a single DER formatted certificate and add it + * to the end of the provided chained list. + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain The pointer to the start of the CRT chain to attach to. + * When parsing the first CRT in a chain, this should point + * to an instance of ::mbedtls_x509_crt initialized through + * mbedtls_x509_crt_init(). + * \param buf The buffer holding the DER encoded certificate. + * \param buflen The size in Bytes of \p buf. + * + * \note This function makes an internal copy of the CRT buffer + * \p buf. In particular, \p buf may be destroyed or reused + * after this call returns. To avoid duplicating the CRT + * buffer (at the cost of stricter lifetime constraints), + * use mbedtls_x509_crt_parse_der_nocopy() instead. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_parse_der(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen); + +/** + * \brief The type of certificate extension callbacks. + * + * Callbacks of this type are passed to and used by the + * mbedtls_x509_crt_parse_der_with_ext_cb() routine when + * it encounters either an unsupported extension or a + * "certificate policies" extension containing any + * unsupported certificate policies. + * Future versions of the library may invoke the callback + * in other cases, if and when the need arises. + * + * \param p_ctx An opaque context passed to the callback. + * \param crt The certificate being parsed. + * \param oid The OID of the extension. + * \param critical Whether the extension is critical. + * \param p Pointer to the start of the extension value + * (the content of the OCTET STRING). + * \param end End of extension value. + * + * \note The callback must fail and return a negative error code + * if it can not parse or does not support the extension. + * When the callback fails to parse a critical extension + * mbedtls_x509_crt_parse_der_with_ext_cb() also fails. + * When the callback fails to parse a non critical extension + * mbedtls_x509_crt_parse_der_with_ext_cb() simply skips + * the extension and continues parsing. + * + * \return \c 0 on success. + * \return A negative error code on failure. + */ +typedef int (*mbedtls_x509_crt_ext_cb_t)(void *p_ctx, + mbedtls_x509_crt const *crt, + mbedtls_x509_buf const *oid, + int critical, + const unsigned char *p, + const unsigned char *end); + +/** + * \brief Parse a single DER formatted certificate and add it + * to the end of the provided chained list. + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain The pointer to the start of the CRT chain to attach to. + * When parsing the first CRT in a chain, this should point + * to an instance of ::mbedtls_x509_crt initialized through + * mbedtls_x509_crt_init(). + * \param buf The buffer holding the DER encoded certificate. + * \param buflen The size in Bytes of \p buf. + * \param make_copy When not zero this function makes an internal copy of the + * CRT buffer \p buf. In particular, \p buf may be destroyed + * or reused after this call returns. + * When zero this function avoids duplicating the CRT buffer + * by taking temporary ownership thereof until the CRT + * is destroyed (like mbedtls_x509_crt_parse_der_nocopy()) + * \param cb A callback invoked for every unsupported certificate + * extension. + * \param p_ctx An opaque context passed to the callback. + * + * \note This call is functionally equivalent to + * mbedtls_x509_crt_parse_der(), and/or + * mbedtls_x509_crt_parse_der_nocopy() + * but it calls the callback with every unsupported + * certificate extension and additionally the + * "certificate policies" extension if it contains any + * unsupported certificate policies. + * The callback must return a negative error code if it + * does not know how to handle such an extension. + * When the callback fails to parse a critical extension + * mbedtls_x509_crt_parse_der_with_ext_cb() also fails. + * When the callback fails to parse a non critical extension + * mbedtls_x509_crt_parse_der_with_ext_cb() simply skips + * the extension and continues parsing. + * Future versions of the library may invoke the callback + * in other cases, if and when the need arises. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_parse_der_with_ext_cb(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen, + int make_copy, + mbedtls_x509_crt_ext_cb_t cb, + void *p_ctx); + +/** + * \brief Parse a single DER formatted certificate and add it + * to the end of the provided chained list. This is a + * variant of mbedtls_x509_crt_parse_der() which takes + * temporary ownership of the CRT buffer until the CRT + * is destroyed. + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain The pointer to the start of the CRT chain to attach to. + * When parsing the first CRT in a chain, this should point + * to an instance of ::mbedtls_x509_crt initialized through + * mbedtls_x509_crt_init(). + * \param buf The address of the readable buffer holding the DER encoded + * certificate to use. On success, this buffer must be + * retained and not be changed for the lifetime of the + * CRT chain \p chain, that is, until \p chain is destroyed + * through a call to mbedtls_x509_crt_free(). + * \param buflen The size in Bytes of \p buf. + * + * \note This call is functionally equivalent to + * mbedtls_x509_crt_parse_der(), but it avoids creating a + * copy of the input buffer at the cost of stronger lifetime + * constraints. This is useful in constrained environments + * where duplication of the CRT cannot be tolerated. + * + * \return \c 0 if successful. + * \return A negative error code on failure. + */ +int mbedtls_x509_crt_parse_der_nocopy(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen); + +/** + * \brief Parse one DER-encoded or one or more concatenated PEM-encoded + * certificates and add them to the chained list. + * + * For CRTs in PEM encoding, the function parses permissively: + * if at least one certificate can be parsed, the function + * returns the number of certificates for which parsing failed + * (hence \c 0 if all certificates were parsed successfully). + * If no certificate could be parsed, the function returns + * the first (negative) error encountered during parsing. + * + * PEM encoded certificates may be interleaved by other data + * such as human readable descriptions of their content, as + * long as the certificates are enclosed in the PEM specific + * '-----{BEGIN/END} CERTIFICATE-----' delimiters. + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain The chain to which to add the parsed certificates. + * \param buf The buffer holding the certificate data in PEM or DER format. + * For certificates in PEM encoding, this may be a concatenation + * of multiple certificates; for DER encoding, the buffer must + * comprise exactly one certificate. + * \param buflen The size of \p buf, including the terminating \c NULL byte + * in case of PEM encoded data. + * + * \return \c 0 if all certificates were parsed successfully. + * \return The (positive) number of certificates that couldn't + * be parsed if parsing was partly successful (see above). + * \return A negative X509 or PEM error code otherwise. + * + */ +int mbedtls_x509_crt_parse(mbedtls_x509_crt *chain, const unsigned char *buf, size_t buflen); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief Load one or more certificates and add them + * to the chained list. Parses permissively. If some + * certificates can be parsed, the result is the number + * of failed certificates it encountered. If none complete + * correctly, the first error is returned. + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param chain points to the start of the chain + * \param path filename to read the certificates from + * + * \return 0 if all certificates parsed successfully, a positive number + * if partly successful or a specific X509 or PEM error code + */ +int mbedtls_x509_crt_parse_file(mbedtls_x509_crt *chain, const char *path); + +/** + * \brief Load one or more certificate files from a path and add them + * to the chained list. Parses permissively. If some + * certificates can be parsed, the result is the number + * of failed certificates it encountered. If none complete + * correctly, the first error is returned. + * + * \param chain points to the start of the chain + * \param path directory / folder to read the certificate files from + * + * \return 0 if all certificates parsed successfully, a positive number + * if partly successful or a specific X509 or PEM error code + */ +int mbedtls_x509_crt_parse_path(mbedtls_x509_crt *chain, const char *path); + +#endif /* MBEDTLS_FS_IO */ +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/** + * \brief Returns an informational string about the + * certificate. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param prefix A line prefix + * \param crt The X509 certificate to represent + * + * \return The length of the string written (not including the + * terminated nul byte), or a negative error code. + */ +int mbedtls_x509_crt_info(char *buf, size_t size, const char *prefix, + const mbedtls_x509_crt *crt); + +/** + * \brief Returns an informational string about the + * verification status of a certificate. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param prefix A line prefix + * \param flags Verification flags created by mbedtls_x509_crt_verify() + * + * \return The length of the string written (not including the + * terminated nul byte), or a negative error code. + */ +int mbedtls_x509_crt_verify_info(char *buf, size_t size, const char *prefix, + uint32_t flags); +#endif /* !MBEDTLS_X509_REMOVE_INFO */ + +/** + * \brief Verify a chain of certificates. + * + * The verify callback is a user-supplied callback that + * can clear / modify / add flags for a certificate. If set, + * the verification callback is called for each + * certificate in the chain (from the trust-ca down to the + * presented crt). The parameters for the callback are: + * (void *parameter, mbedtls_x509_crt *crt, int certificate_depth, + * int *flags). With the flags representing current flags for + * that specific certificate and the certificate depth from + * the bottom (Peer cert depth = 0). + * + * All flags left after returning from the callback + * are also returned to the application. The function should + * return 0 for anything (including invalid certificates) + * other than fatal error, as a non-zero return code + * immediately aborts the verification process. For fatal + * errors, a specific error code should be used (different + * from MBEDTLS_ERR_X509_CERT_VERIFY_FAILED which should not + * be returned at this point), or MBEDTLS_ERR_X509_FATAL_ERROR + * can be used if no better code is available. + * + * \note In case verification failed, the results can be displayed + * using \c mbedtls_x509_crt_verify_info() + * + * \note Same as \c mbedtls_x509_crt_verify_with_profile() with the + * default security profile. + * + * \note It is your responsibility to provide up-to-date CRLs for + * all trusted CAs. If no CRL is provided for the CA that was + * used to sign the certificate, CRL verification is skipped + * silently, that is *without* setting any flag. + * + * \note The \c trust_ca list can contain two types of certificates: + * (1) those of trusted root CAs, so that certificates + * chaining up to those CAs will be trusted, and (2) + * self-signed end-entity certificates to be trusted (for + * specific peers you know) - in that case, the self-signed + * certificate doesn't need to have the CA bit set. + * + * \param crt The certificate chain to be verified. + * \param trust_ca The list of trusted CAs. + * \param ca_crl The list of CRLs for trusted CAs. + * \param cn The expected Common Name. This will be checked to be + * present in the certificate's subjectAltNames extension or, + * if this extension is absent, as a CN component in its + * Subject name. Currently only DNS names are supported. This + * may be \c NULL if the CN need not be verified. + * \param flags The address at which to store the result of the verification. + * If the verification couldn't be completed, the flag value is + * set to (uint32_t) -1. + * \param f_vrfy The verification callback to use. See the documentation + * of mbedtls_x509_crt_verify() for more information. + * \param p_vrfy The context to be passed to \p f_vrfy. + * + * \return \c 0 if the chain is valid with respect to the + * passed CN, CAs, CRLs and security profile. + * \return #MBEDTLS_ERR_X509_CERT_VERIFY_FAILED in case the + * certificate chain verification failed. In this case, + * \c *flags will have one or more + * \c MBEDTLS_X509_BADCERT_XXX or \c MBEDTLS_X509_BADCRL_XXX + * flags set. + * \return Another negative error code in case of a fatal error + * encountered during the verification process. + */ +int mbedtls_x509_crt_verify(mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy); + +/** + * \brief Verify a chain of certificates with respect to + * a configurable security profile. + * + * \note Same as \c mbedtls_x509_crt_verify(), but with explicit + * security profile. + * + * \note The restrictions on keys (RSA minimum size, allowed curves + * for ECDSA) apply to all certificates: trusted root, + * intermediate CAs if any, and end entity certificate. + * + * \param crt The certificate chain to be verified. + * \param trust_ca The list of trusted CAs. + * \param ca_crl The list of CRLs for trusted CAs. + * \param profile The security profile to use for the verification. + * \param cn The expected Common Name. This may be \c NULL if the + * CN need not be verified. + * \param flags The address at which to store the result of the verification. + * If the verification couldn't be completed, the flag value is + * set to (uint32_t) -1. + * \param f_vrfy The verification callback to use. See the documentation + * of mbedtls_x509_crt_verify() for more information. + * \param p_vrfy The context to be passed to \p f_vrfy. + * + * \return \c 0 if the chain is valid with respect to the + * passed CN, CAs, CRLs and security profile. + * \return #MBEDTLS_ERR_X509_CERT_VERIFY_FAILED in case the + * certificate chain verification failed. In this case, + * \c *flags will have one or more + * \c MBEDTLS_X509_BADCERT_XXX or \c MBEDTLS_X509_BADCRL_XXX + * flags set. + * \return Another negative error code in case of a fatal error + * encountered during the verification process. + */ +int mbedtls_x509_crt_verify_with_profile(mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + const mbedtls_x509_crt_profile *profile, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy); + +/** + * \brief Restartable version of \c mbedtls_crt_verify_with_profile() + * + * \note Performs the same job as \c mbedtls_crt_verify_with_profile() + * but can return early and restart according to the limit + * set with \c mbedtls_ecp_set_max_ops() to reduce blocking. + * + * \param crt The certificate chain to be verified. + * \param trust_ca The list of trusted CAs. + * \param ca_crl The list of CRLs for trusted CAs. + * \param profile The security profile to use for the verification. + * \param cn The expected Common Name. This may be \c NULL if the + * CN need not be verified. + * \param flags The address at which to store the result of the verification. + * If the verification couldn't be completed, the flag value is + * set to (uint32_t) -1. + * \param f_vrfy The verification callback to use. See the documentation + * of mbedtls_x509_crt_verify() for more information. + * \param p_vrfy The context to be passed to \p f_vrfy. + * \param rs_ctx The restart context to use. This may be set to \c NULL + * to disable restartable ECC. + * + * \return See \c mbedtls_crt_verify_with_profile(), or + * \return #MBEDTLS_ERR_ECP_IN_PROGRESS if maximum number of + * operations was reached: see \c mbedtls_ecp_set_max_ops(). + */ +int mbedtls_x509_crt_verify_restartable(mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + const mbedtls_x509_crt_profile *profile, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy, + mbedtls_x509_crt_restart_ctx *rs_ctx); + +/** + * \brief The type of trusted certificate callbacks. + * + * Callbacks of this type are passed to and used by the CRT + * verification routine mbedtls_x509_crt_verify_with_ca_cb() + * when looking for trusted signers of a given certificate. + * + * On success, the callback returns a list of trusted + * certificates to be considered as potential signers + * for the input certificate. + * + * \param p_ctx An opaque context passed to the callback. + * \param child The certificate for which to search a potential signer. + * This will point to a readable certificate. + * \param candidate_cas The address at which to store the address of the first + * entry in the generated linked list of candidate signers. + * This will not be \c NULL. + * + * \note The callback must only return a non-zero value on a + * fatal error. If, in contrast, the search for a potential + * signer completes without a single candidate, the + * callback must return \c 0 and set \c *candidate_cas + * to \c NULL. + * + * \return \c 0 on success. In this case, \c *candidate_cas points + * to a heap-allocated linked list of instances of + * ::mbedtls_x509_crt, and ownership of this list is passed + * to the caller. + * \return A negative error code on failure. + */ +typedef int (*mbedtls_x509_crt_ca_cb_t)(void *p_ctx, + mbedtls_x509_crt const *child, + mbedtls_x509_crt **candidate_cas); + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) +/** + * \brief Version of \c mbedtls_x509_crt_verify_with_profile() which + * uses a callback to acquire the list of trusted CA + * certificates. + * + * \param crt The certificate chain to be verified. + * \param f_ca_cb The callback to be used to query for potential signers + * of a given child certificate. See the documentation of + * ::mbedtls_x509_crt_ca_cb_t for more information. + * \param p_ca_cb The opaque context to be passed to \p f_ca_cb. + * \param profile The security profile for the verification. + * \param cn The expected Common Name. This may be \c NULL if the + * CN need not be verified. + * \param flags The address at which to store the result of the verification. + * If the verification couldn't be completed, the flag value is + * set to (uint32_t) -1. + * \param f_vrfy The verification callback to use. See the documentation + * of mbedtls_x509_crt_verify() for more information. + * \param p_vrfy The context to be passed to \p f_vrfy. + * + * \return See \c mbedtls_crt_verify_with_profile(). + */ +int mbedtls_x509_crt_verify_with_ca_cb(mbedtls_x509_crt *crt, + mbedtls_x509_crt_ca_cb_t f_ca_cb, + void *p_ca_cb, + const mbedtls_x509_crt_profile *profile, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy); + +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ + +/** + * \brief Check usage of certificate against keyUsage extension. + * + * \param crt Leaf certificate used. + * \param usage Intended usage(s) (eg MBEDTLS_X509_KU_KEY_ENCIPHERMENT + * before using the certificate to perform an RSA key + * exchange). + * + * \note Except for decipherOnly and encipherOnly, a bit set in the + * usage argument means this bit MUST be set in the + * certificate. For decipherOnly and encipherOnly, it means + * that bit MAY be set. + * + * \return 0 is these uses of the certificate are allowed, + * MBEDTLS_ERR_X509_BAD_INPUT_DATA if the keyUsage extension + * is present but does not match the usage argument. + * + * \note You should only call this function on leaf certificates, on + * (intermediate) CAs the keyUsage extension is automatically + * checked by \c mbedtls_x509_crt_verify(). + */ +int mbedtls_x509_crt_check_key_usage(const mbedtls_x509_crt *crt, + unsigned int usage); + +/** + * \brief Check usage of certificate against extendedKeyUsage. + * + * \param crt Leaf certificate used. + * \param usage_oid Intended usage (eg MBEDTLS_OID_SERVER_AUTH or + * MBEDTLS_OID_CLIENT_AUTH). + * \param usage_len Length of usage_oid (eg given by MBEDTLS_OID_SIZE()). + * + * \return 0 if this use of the certificate is allowed, + * MBEDTLS_ERR_X509_BAD_INPUT_DATA if not. + * + * \note Usually only makes sense on leaf certificates. + */ +int mbedtls_x509_crt_check_extended_key_usage(const mbedtls_x509_crt *crt, + const char *usage_oid, + size_t usage_len); + +#if defined(MBEDTLS_X509_CRL_PARSE_C) +/** + * \brief Verify the certificate revocation status + * + * \param crt a certificate to be verified + * \param crl the CRL to verify against + * + * \return 1 if the certificate is revoked, 0 otherwise + * + */ +int mbedtls_x509_crt_is_revoked(const mbedtls_x509_crt *crt, const mbedtls_x509_crl *crl); +#endif /* MBEDTLS_X509_CRL_PARSE_C */ + +/** + * \brief Initialize a certificate (chain) + * + * \param crt Certificate chain to initialize + */ +void mbedtls_x509_crt_init(mbedtls_x509_crt *crt); + +/** + * \brief Unallocate all certificate data + * + * \param crt Certificate chain to free + */ +void mbedtls_x509_crt_free(mbedtls_x509_crt *crt); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/** + * \brief Initialize a restart context + */ +void mbedtls_x509_crt_restart_init(mbedtls_x509_crt_restart_ctx *ctx); + +/** + * \brief Free the components of a restart context + */ +void mbedtls_x509_crt_restart_free(mbedtls_x509_crt_restart_ctx *ctx); +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +/** + * \brief Query certificate for given extension type + * + * \param[in] ctx Certificate context to be queried, must not be \c NULL + * \param ext_type Extension type being queried for, must be a valid + * extension type. Must be one of the MBEDTLS_X509_EXT_XXX + * values + * + * \return 0 if the given extension type is not present, + * non-zero otherwise + */ +static inline int mbedtls_x509_crt_has_ext_type(const mbedtls_x509_crt *ctx, + int ext_type) +{ + return ctx->MBEDTLS_PRIVATE(ext_types) & ext_type; +} + +/** \} name Structures and functions for parsing and writing X.509 certificates */ + +#if defined(MBEDTLS_X509_CRT_WRITE_C) +/** + * \brief Initialize a CRT writing context + * + * \param ctx CRT context to initialize + */ +void mbedtls_x509write_crt_init(mbedtls_x509write_cert *ctx); + +/** + * \brief Set the version for a Certificate + * Default: MBEDTLS_X509_CRT_VERSION_3 + * + * \param ctx CRT context to use + * \param version version to set (MBEDTLS_X509_CRT_VERSION_1, MBEDTLS_X509_CRT_VERSION_2 or + * MBEDTLS_X509_CRT_VERSION_3) + */ +void mbedtls_x509write_crt_set_version(mbedtls_x509write_cert *ctx, int version); + +#if defined(MBEDTLS_BIGNUM_C) && !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief Set the serial number for a Certificate. + * + * \deprecated This function is deprecated and will be removed in a + * future version of the library. Please use + * mbedtls_x509write_crt_set_serial_raw() instead. + * + * \note Even though the MBEDTLS_BIGNUM_C guard looks redundant since + * X509 depends on PK and PK depends on BIGNUM, this emphasizes + * a direct dependency between X509 and BIGNUM which is going + * to be deprecated in the future. + * + * \param ctx CRT context to use + * \param serial serial number to set + * + * \return 0 if successful + */ +int MBEDTLS_DEPRECATED mbedtls_x509write_crt_set_serial( + mbedtls_x509write_cert *ctx, const mbedtls_mpi *serial); +#endif // MBEDTLS_BIGNUM_C && !MBEDTLS_DEPRECATED_REMOVED + +/** + * \brief Set the serial number for a Certificate. + * + * \param ctx CRT context to use + * \param serial A raw array of bytes containing the serial number in big + * endian format + * \param serial_len Length of valid bytes (expressed in bytes) in \p serial + * input buffer + * + * \return 0 if successful, or + * MBEDTLS_ERR_X509_BAD_INPUT_DATA if the provided input buffer + * is too big (longer than MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN) + */ +int mbedtls_x509write_crt_set_serial_raw(mbedtls_x509write_cert *ctx, + unsigned char *serial, size_t serial_len); + +/** + * \brief Set the validity period for a Certificate + * Timestamps should be in string format for UTC timezone + * i.e. "YYYYMMDDhhmmss" + * e.g. "20131231235959" for December 31st 2013 + * at 23:59:59 + * + * \param ctx CRT context to use + * \param not_before not_before timestamp + * \param not_after not_after timestamp + * + * \return 0 if timestamp was parsed successfully, or + * a specific error code + */ +int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx, const char *not_before, + const char *not_after); + +/** + * \brief Set the issuer name for a Certificate + * Issuer names should contain a comma-separated list + * of OID types and values: + * e.g. "C=UK,O=ARM,CN=mbed TLS CA" + * + * \param ctx CRT context to use + * \param issuer_name issuer name to set + * + * \return 0 if issuer name was parsed successfully, or + * a specific error code + */ +int mbedtls_x509write_crt_set_issuer_name(mbedtls_x509write_cert *ctx, + const char *issuer_name); + +/** + * \brief Set the subject name for a Certificate + * Subject names should contain a comma-separated list + * of OID types and values: + * e.g. "C=UK,O=ARM,CN=mbed TLS Server 1" + * + * \param ctx CRT context to use + * \param subject_name subject name to set + * + * \return 0 if subject name was parsed successfully, or + * a specific error code + */ +int mbedtls_x509write_crt_set_subject_name(mbedtls_x509write_cert *ctx, + const char *subject_name); + +/** + * \brief Set the subject public key for the certificate + * + * \param ctx CRT context to use + * \param key public key to include + */ +void mbedtls_x509write_crt_set_subject_key(mbedtls_x509write_cert *ctx, mbedtls_pk_context *key); + +/** + * \brief Set the issuer key used for signing the certificate + * + * \param ctx CRT context to use + * \param key private key to sign with + */ +void mbedtls_x509write_crt_set_issuer_key(mbedtls_x509write_cert *ctx, mbedtls_pk_context *key); + +/** + * \brief Set the MD algorithm to use for the signature + * (e.g. MBEDTLS_MD_SHA1) + * + * \param ctx CRT context to use + * \param md_alg MD algorithm to use + */ +void mbedtls_x509write_crt_set_md_alg(mbedtls_x509write_cert *ctx, mbedtls_md_type_t md_alg); + +/** + * \brief Generic function to add to or replace an extension in the + * CRT + * + * \param ctx CRT context to use + * \param oid OID of the extension + * \param oid_len length of the OID + * \param critical if the extension is critical (per the RFC's definition) + * \param val value of the extension OCTET STRING + * \param val_len length of the value data + * + * \return 0 if successful, or a MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_crt_set_extension(mbedtls_x509write_cert *ctx, + const char *oid, size_t oid_len, + int critical, + const unsigned char *val, size_t val_len); + +/** + * \brief Set the basicConstraints extension for a CRT + * + * \param ctx CRT context to use + * \param is_ca is this a CA certificate + * \param max_pathlen maximum length of certificate chains below this + * certificate (only for CA certificates, -1 is + * unlimited) + * + * \return 0 if successful, or a MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_crt_set_basic_constraints(mbedtls_x509write_cert *ctx, + int is_ca, int max_pathlen); + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA) +/** + * \brief Set the subjectKeyIdentifier extension for a CRT + * Requires that mbedtls_x509write_crt_set_subject_key() has been + * called before + * + * \param ctx CRT context to use + * + * \return 0 if successful, or a MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_crt_set_subject_key_identifier(mbedtls_x509write_cert *ctx); + +/** + * \brief Set the authorityKeyIdentifier extension for a CRT + * Requires that mbedtls_x509write_crt_set_issuer_key() has been + * called before + * + * \param ctx CRT context to use + * + * \return 0 if successful, or a MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_crt_set_authority_key_identifier(mbedtls_x509write_cert *ctx); +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA */ + +/** + * \brief Set the Key Usage Extension flags + * (e.g. MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN) + * + * \param ctx CRT context to use + * \param key_usage key usage flags to set + * + * \return 0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_crt_set_key_usage(mbedtls_x509write_cert *ctx, + unsigned int key_usage); + +/** + * \brief Set the Extended Key Usage Extension + * (e.g. MBEDTLS_OID_SERVER_AUTH) + * + * \param ctx CRT context to use + * \param exts extended key usage extensions to set, a sequence of + * MBEDTLS_ASN1_OID objects + * + * \return 0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, + const mbedtls_asn1_sequence *exts); + +/** + * \brief Set the Netscape Cert Type flags + * (e.g. MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT | MBEDTLS_X509_NS_CERT_TYPE_EMAIL) + * + * \param ctx CRT context to use + * \param ns_cert_type Netscape Cert Type flags to set + * + * \return 0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_crt_set_ns_cert_type(mbedtls_x509write_cert *ctx, + unsigned char ns_cert_type); + +/** + * \brief Free the contents of a CRT write context + * + * \param ctx CRT context to free + */ +void mbedtls_x509write_crt_free(mbedtls_x509write_cert *ctx); + +/** + * \brief Write a built up certificate to a X509 DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx certificate to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function. This must not be \c NULL. + * \param p_rng RNG parameter + * + * \return length of data written if successful, or a specific + * error code + * + * \note \p f_rng is used for the signature operation. + */ +int mbedtls_x509write_crt_der(mbedtls_x509write_cert *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +#if defined(MBEDTLS_PEM_WRITE_C) +/** + * \brief Write a built up certificate to a X509 PEM string + * + * \param ctx certificate to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function. This must not be \c NULL. + * \param p_rng RNG parameter + * + * \return 0 if successful, or a specific error code + * + * \note \p f_rng is used for the signature operation. + */ +int mbedtls_x509write_crt_pem(mbedtls_x509write_cert *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); +#endif /* MBEDTLS_PEM_WRITE_C */ +#endif /* MBEDTLS_X509_CRT_WRITE_C */ + +/** \} addtogroup x509_module */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_x509_crt.h */ diff --git a/r5dev/thirdparty/mbedtls/include/mbedtls/x509_csr.h b/r5dev/thirdparty/mbedtls/include/mbedtls/x509_csr.h new file mode 100644 index 00000000..f3f9e13a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/mbedtls/x509_csr.h @@ -0,0 +1,337 @@ +/** + * \file x509_csr.h + * + * \brief X.509 certificate signing request parsing and writing + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_X509_CSR_H +#define MBEDTLS_X509_CSR_H +#include "mbedtls/private_access.h" + +#include "mbedtls/build_info.h" + +#include "mbedtls/x509.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup x509_module + * \{ */ + +/** + * \name Structures and functions for X.509 Certificate Signing Requests (CSR) + * \{ + */ + +/** + * Certificate Signing Request (CSR) structure. + * + * Some fields of this structure are publicly readable. Do not modify + * them except via Mbed TLS library functions: the effect of modifying + * those fields or the data that those fields point to is unspecified. + */ +typedef struct mbedtls_x509_csr { + mbedtls_x509_buf raw; /**< The raw CSR data (DER). */ + mbedtls_x509_buf cri; /**< The raw CertificateRequestInfo body (DER). */ + + int version; /**< CSR version (1=v1). */ + + mbedtls_x509_buf subject_raw; /**< The raw subject data (DER). */ + mbedtls_x509_name subject; /**< The parsed subject data (named information object). */ + + mbedtls_pk_context pk; /**< Container for the public key context. */ + + unsigned int key_usage; /**< Optional key usage extension value: See the values in x509.h */ + unsigned char ns_cert_type; /**< Optional Netscape certificate type extension value: See the values in x509.h */ + mbedtls_x509_sequence subject_alt_names; /**< Optional list of raw entries of Subject Alternative Names extension (currently only dNSName and OtherName are listed). */ + + int MBEDTLS_PRIVATE(ext_types); /**< Bit string containing detected and parsed extensions */ + + mbedtls_x509_buf sig_oid; + mbedtls_x509_buf MBEDTLS_PRIVATE(sig); + mbedtls_md_type_t MBEDTLS_PRIVATE(sig_md); /**< Internal representation of the MD algorithm of the signature algorithm, e.g. MBEDTLS_MD_SHA256 */ + mbedtls_pk_type_t MBEDTLS_PRIVATE(sig_pk); /**< Internal representation of the Public Key algorithm of the signature algorithm, e.g. MBEDTLS_PK_RSA */ + void *MBEDTLS_PRIVATE(sig_opts); /**< Signature options to be passed to mbedtls_pk_verify_ext(), e.g. for RSASSA-PSS */ +} +mbedtls_x509_csr; + +/** + * Container for writing a CSR + */ +typedef struct mbedtls_x509write_csr { + mbedtls_pk_context *MBEDTLS_PRIVATE(key); + mbedtls_asn1_named_data *MBEDTLS_PRIVATE(subject); + mbedtls_md_type_t MBEDTLS_PRIVATE(md_alg); + mbedtls_asn1_named_data *MBEDTLS_PRIVATE(extensions); +} +mbedtls_x509write_csr; + +typedef struct mbedtls_x509_san_list { + mbedtls_x509_subject_alternative_name node; + struct mbedtls_x509_san_list *next; +} +mbedtls_x509_san_list; + +#if defined(MBEDTLS_X509_CSR_PARSE_C) +/** + * \brief Load a Certificate Signing Request (CSR) in DER format + * + * \note CSR attributes (if any) are currently silently ignored. + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param csr CSR context to fill + * \param buf buffer holding the CRL data + * \param buflen size of the buffer + * + * \return 0 if successful, or a specific X509 error code + */ +int mbedtls_x509_csr_parse_der(mbedtls_x509_csr *csr, + const unsigned char *buf, size_t buflen); + +/** + * \brief Load a Certificate Signing Request (CSR), DER or PEM format + * + * \note See notes for \c mbedtls_x509_csr_parse_der() + * + * \note If #MBEDTLS_USE_PSA_CRYPTO is enabled, the PSA crypto + * subsystem must have been initialized by calling + * psa_crypto_init() before calling this function. + * + * \param csr CSR context to fill + * \param buf buffer holding the CRL data + * \param buflen size of the buffer + * (including the terminating null byte for PEM data) + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int mbedtls_x509_csr_parse(mbedtls_x509_csr *csr, const unsigned char *buf, size_t buflen); + +#if defined(MBEDTLS_FS_IO) +/** + * \brief Load a Certificate Signing Request (CSR) + * + * \note See notes for \c mbedtls_x509_csr_parse() + * + * \param csr CSR context to fill + * \param path filename to read the CSR from + * + * \return 0 if successful, or a specific X509 or PEM error code + */ +int mbedtls_x509_csr_parse_file(mbedtls_x509_csr *csr, const char *path); +#endif /* MBEDTLS_FS_IO */ + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/** + * \brief Returns an informational string about the + * CSR. + * + * \param buf Buffer to write to + * \param size Maximum size of buffer + * \param prefix A line prefix + * \param csr The X509 CSR to represent + * + * \return The length of the string written (not including the + * terminated nul byte), or a negative error code. + */ +int mbedtls_x509_csr_info(char *buf, size_t size, const char *prefix, + const mbedtls_x509_csr *csr); +#endif /* !MBEDTLS_X509_REMOVE_INFO */ + +/** + * \brief Initialize a CSR + * + * \param csr CSR to initialize + */ +void mbedtls_x509_csr_init(mbedtls_x509_csr *csr); + +/** + * \brief Unallocate all CSR data + * + * \param csr CSR to free + */ +void mbedtls_x509_csr_free(mbedtls_x509_csr *csr); +#endif /* MBEDTLS_X509_CSR_PARSE_C */ + +/** \} name Structures and functions for X.509 Certificate Signing Requests (CSR) */ + +#if defined(MBEDTLS_X509_CSR_WRITE_C) +/** + * \brief Initialize a CSR context + * + * \param ctx CSR context to initialize + */ +void mbedtls_x509write_csr_init(mbedtls_x509write_csr *ctx); + +/** + * \brief Set the subject name for a CSR + * Subject names should contain a comma-separated list + * of OID types and values: + * e.g. "C=UK,O=ARM,CN=mbed TLS Server 1" + * + * \param ctx CSR context to use + * \param subject_name subject name to set + * + * \return 0 if subject name was parsed successfully, or + * a specific error code + */ +int mbedtls_x509write_csr_set_subject_name(mbedtls_x509write_csr *ctx, + const char *subject_name); + +/** + * \brief Set the key for a CSR (public key will be included, + * private key used to sign the CSR when writing it) + * + * \param ctx CSR context to use + * \param key Asymmetric key to include + */ +void mbedtls_x509write_csr_set_key(mbedtls_x509write_csr *ctx, mbedtls_pk_context *key); + +/** + * \brief Set the MD algorithm to use for the signature + * (e.g. MBEDTLS_MD_SHA1) + * + * \param ctx CSR context to use + * \param md_alg MD algorithm to use + */ +void mbedtls_x509write_csr_set_md_alg(mbedtls_x509write_csr *ctx, mbedtls_md_type_t md_alg); + +/** + * \brief Set the Key Usage Extension flags + * (e.g. MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN) + * + * \param ctx CSR context to use + * \param key_usage key usage flags to set + * + * \return 0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED + * + * \note The decipherOnly flag from the Key Usage + * extension is represented by bit 8 (i.e. + * 0x8000), which cannot typically be represented + * in an unsigned char. Therefore, the flag + * decipherOnly (i.e. + * #MBEDTLS_X509_KU_DECIPHER_ONLY) cannot be set using this + * function. + */ +int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage); + +/** + * \brief Set Subject Alternative Name + * + * \param ctx CSR context to use + * \param san_list List of SAN values + * + * \return 0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED + * + * \note Only "dnsName", "uniformResourceIdentifier" and "otherName", + * as defined in RFC 5280, are supported. + */ +int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx, + const mbedtls_x509_san_list *san_list); + +/** + * \brief Set the Netscape Cert Type flags + * (e.g. MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT | MBEDTLS_X509_NS_CERT_TYPE_EMAIL) + * + * \param ctx CSR context to use + * \param ns_cert_type Netscape Cert Type flags to set + * + * \return 0 if successful, or MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_csr_set_ns_cert_type(mbedtls_x509write_csr *ctx, + unsigned char ns_cert_type); + +/** + * \brief Generic function to add to or replace an extension in the + * CSR + * + * \param ctx CSR context to use + * \param oid OID of the extension + * \param oid_len length of the OID + * \param critical Set to 1 to mark the extension as critical, 0 otherwise. + * \param val value of the extension OCTET STRING + * \param val_len length of the value data + * + * \return 0 if successful, or a MBEDTLS_ERR_X509_ALLOC_FAILED + */ +int mbedtls_x509write_csr_set_extension(mbedtls_x509write_csr *ctx, + const char *oid, size_t oid_len, + int critical, + const unsigned char *val, size_t val_len); + +/** + * \brief Free the contents of a CSR context + * + * \param ctx CSR context to free + */ +void mbedtls_x509write_csr_free(mbedtls_x509write_csr *ctx); + +/** + * \brief Write a CSR (Certificate Signing Request) to a + * DER structure + * Note: data is written at the end of the buffer! Use the + * return value to determine where you should start + * using the buffer + * + * \param ctx CSR to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function. This must not be \c NULL. + * \param p_rng RNG parameter + * + * \return length of data written if successful, or a specific + * error code + * + * \note \p f_rng is used for the signature operation. + */ +int mbedtls_x509write_csr_der(mbedtls_x509write_csr *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +#if defined(MBEDTLS_PEM_WRITE_C) +/** + * \brief Write a CSR (Certificate Signing Request) to a + * PEM string + * + * \param ctx CSR to write away + * \param buf buffer to write to + * \param size size of the buffer + * \param f_rng RNG function. This must not be \c NULL. + * \param p_rng RNG parameter + * + * \return 0 if successful, or a specific error code + * + * \note \p f_rng is used for the signature operation. + */ +int mbedtls_x509write_csr_pem(mbedtls_x509write_csr *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); +#endif /* MBEDTLS_PEM_WRITE_C */ +#endif /* MBEDTLS_X509_CSR_WRITE_C */ + +/** \} addtogroup x509_module */ + +#ifdef __cplusplus +} +#endif + +#endif /* mbedtls_x509_csr.h */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto.h b/r5dev/thirdparty/mbedtls/include/psa/crypto.h new file mode 100644 index 00000000..8a05efd8 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto.h @@ -0,0 +1,4697 @@ +/** + * \file psa/crypto.h + * \brief Platform Security Architecture cryptography module + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_H +#define PSA_CRYPTO_H + +#if defined(MBEDTLS_PSA_CRYPTO_PLATFORM_FILE) +#include MBEDTLS_PSA_CRYPTO_PLATFORM_FILE +#else +#include "crypto_platform.h" +#endif + +#include + +#ifdef __DOXYGEN_ONLY__ +/* This __DOXYGEN_ONLY__ block contains mock definitions for things that + * must be defined in the crypto_platform.h header. These mock definitions + * are present in this file as a convenience to generate pretty-printed + * documentation that includes those definitions. */ + +/** \defgroup platform Implementation-specific definitions + * @{ + */ + +/**@}*/ +#endif /* __DOXYGEN_ONLY__ */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* The file "crypto_types.h" declares types that encode errors, + * algorithms, key types, policies, etc. */ +#include "crypto_types.h" + +/** \defgroup version API version + * @{ + */ + +/** + * The major version of this implementation of the PSA Crypto API + */ +#define PSA_CRYPTO_API_VERSION_MAJOR 1 + +/** + * The minor version of this implementation of the PSA Crypto API + */ +#define PSA_CRYPTO_API_VERSION_MINOR 0 + +/**@}*/ + +/* The file "crypto_values.h" declares macros to build and analyze values + * of integral types defined in "crypto_types.h". */ +#include "crypto_values.h" + +/** \defgroup initialization Library initialization + * @{ + */ + +/** + * \brief Library initialization. + * + * Applications must call this function before calling any other + * function in this module. + * + * Applications may call this function more than once. Once a call + * succeeds, subsequent calls are guaranteed to succeed. + * + * If the application calls other functions before calling psa_crypto_init(), + * the behavior is undefined. Implementations are encouraged to either perform + * the operation as if the library had been initialized or to return + * #PSA_ERROR_BAD_STATE or some other applicable error. In particular, + * implementations should not return a success status if the lack of + * initialization may have security implications, for example due to improper + * seeding of the random number generator. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + */ +psa_status_t psa_crypto_init(void); + +/**@}*/ + +/** \addtogroup attributes + * @{ + */ + +/** \def PSA_KEY_ATTRIBUTES_INIT + * + * This macro returns a suitable initializer for a key attribute structure + * of type #psa_key_attributes_t. + */ + +/** Return an initial value for a key attributes structure. + */ +static psa_key_attributes_t psa_key_attributes_init(void); + +/** Declare a key as persistent and set its key identifier. + * + * If the attribute structure currently declares the key as volatile (which + * is the default content of an attribute structure), this function sets + * the lifetime attribute to #PSA_KEY_LIFETIME_PERSISTENT. + * + * This function does not access storage, it merely stores the given + * value in the structure. + * The persistent key will be written to storage when the attribute + * structure is passed to a key creation function such as + * psa_import_key(), psa_generate_key(), + * psa_key_derivation_output_key() or psa_copy_key(). + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate each of its arguments exactly once. + * + * \param[out] attributes The attribute structure to write to. + * \param key The persistent identifier for the key. + */ +static void psa_set_key_id(psa_key_attributes_t *attributes, + mbedtls_svc_key_id_t key); + +#ifdef MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER +/** Set the owner identifier of a key. + * + * When key identifiers encode key owner identifiers, psa_set_key_id() does + * not allow to define in key attributes the owner of volatile keys as + * psa_set_key_id() enforces the key to be persistent. + * + * This function allows to set in key attributes the owner identifier of a + * key. It is intended to be used for volatile keys. For persistent keys, + * it is recommended to use the PSA Cryptography API psa_set_key_id() to define + * the owner of a key. + * + * \param[out] attributes The attribute structure to write to. + * \param owner The key owner identifier. + */ +static void mbedtls_set_key_owner_id(psa_key_attributes_t *attributes, + mbedtls_key_owner_id_t owner); +#endif + +/** Set the location of a persistent key. + * + * To make a key persistent, you must give it a persistent key identifier + * with psa_set_key_id(). By default, a key that has a persistent identifier + * is stored in the default storage area identifier by + * #PSA_KEY_LIFETIME_PERSISTENT. Call this function to choose a storage + * area, or to explicitly declare the key as volatile. + * + * This function does not access storage, it merely stores the given + * value in the structure. + * The persistent key will be written to storage when the attribute + * structure is passed to a key creation function such as + * psa_import_key(), psa_generate_key(), + * psa_key_derivation_output_key() or psa_copy_key(). + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate each of its arguments exactly once. + * + * \param[out] attributes The attribute structure to write to. + * \param lifetime The lifetime for the key. + * If this is #PSA_KEY_LIFETIME_VOLATILE, the + * key will be volatile, and the key identifier + * attribute is reset to 0. + */ +static void psa_set_key_lifetime(psa_key_attributes_t *attributes, + psa_key_lifetime_t lifetime); + +/** Retrieve the key identifier from key attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate its argument exactly once. + * + * \param[in] attributes The key attribute structure to query. + * + * \return The persistent identifier stored in the attribute structure. + * This value is unspecified if the attribute structure declares + * the key as volatile. + */ +static mbedtls_svc_key_id_t psa_get_key_id( + const psa_key_attributes_t *attributes); + +/** Retrieve the lifetime from key attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate its argument exactly once. + * + * \param[in] attributes The key attribute structure to query. + * + * \return The lifetime value stored in the attribute structure. + */ +static psa_key_lifetime_t psa_get_key_lifetime( + const psa_key_attributes_t *attributes); + +/** Declare usage flags for a key. + * + * Usage flags are part of a key's usage policy. They encode what + * kind of operations are permitted on the key. For more details, + * refer to the documentation of the type #psa_key_usage_t. + * + * This function overwrites any usage flags + * previously set in \p attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate each of its arguments exactly once. + * + * \param[out] attributes The attribute structure to write to. + * \param usage_flags The usage flags to write. + */ +static void psa_set_key_usage_flags(psa_key_attributes_t *attributes, + psa_key_usage_t usage_flags); + +/** Retrieve the usage flags from key attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate its argument exactly once. + * + * \param[in] attributes The key attribute structure to query. + * + * \return The usage flags stored in the attribute structure. + */ +static psa_key_usage_t psa_get_key_usage_flags( + const psa_key_attributes_t *attributes); + +/** Declare the permitted algorithm policy for a key. + * + * The permitted algorithm policy of a key encodes which algorithm or + * algorithms are permitted to be used with this key. The following + * algorithm policies are supported: + * - 0 does not allow any cryptographic operation with the key. The key + * may be used for non-cryptographic actions such as exporting (if + * permitted by the usage flags). + * - An algorithm value permits this particular algorithm. + * - An algorithm wildcard built from #PSA_ALG_ANY_HASH allows the specified + * signature scheme with any hash algorithm. + * - An algorithm built from #PSA_ALG_AT_LEAST_THIS_LENGTH_MAC allows + * any MAC algorithm from the same base class (e.g. CMAC) which + * generates/verifies a MAC length greater than or equal to the length + * encoded in the wildcard algorithm. + * - An algorithm built from #PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG + * allows any AEAD algorithm from the same base class (e.g. CCM) which + * generates/verifies a tag length greater than or equal to the length + * encoded in the wildcard algorithm. + * + * This function overwrites any algorithm policy + * previously set in \p attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate each of its arguments exactly once. + * + * \param[out] attributes The attribute structure to write to. + * \param alg The permitted algorithm policy to write. + */ +static void psa_set_key_algorithm(psa_key_attributes_t *attributes, + psa_algorithm_t alg); + + +/** Retrieve the algorithm policy from key attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate its argument exactly once. + * + * \param[in] attributes The key attribute structure to query. + * + * \return The algorithm stored in the attribute structure. + */ +static psa_algorithm_t psa_get_key_algorithm( + const psa_key_attributes_t *attributes); + +/** Declare the type of a key. + * + * This function overwrites any key type + * previously set in \p attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate each of its arguments exactly once. + * + * \param[out] attributes The attribute structure to write to. + * \param type The key type to write. + * If this is 0, the key type in \p attributes + * becomes unspecified. + */ +static void psa_set_key_type(psa_key_attributes_t *attributes, + psa_key_type_t type); + + +/** Declare the size of a key. + * + * This function overwrites any key size previously set in \p attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate each of its arguments exactly once. + * + * \param[out] attributes The attribute structure to write to. + * \param bits The key size in bits. + * If this is 0, the key size in \p attributes + * becomes unspecified. Keys of size 0 are + * not supported. + */ +static void psa_set_key_bits(psa_key_attributes_t *attributes, + size_t bits); + +/** Retrieve the key type from key attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate its argument exactly once. + * + * \param[in] attributes The key attribute structure to query. + * + * \return The key type stored in the attribute structure. + */ +static psa_key_type_t psa_get_key_type(const psa_key_attributes_t *attributes); + +/** Retrieve the key size from key attributes. + * + * This function may be declared as `static` (i.e. without external + * linkage). This function may be provided as a function-like macro, + * but in this case it must evaluate its argument exactly once. + * + * \param[in] attributes The key attribute structure to query. + * + * \return The key size stored in the attribute structure, in bits. + */ +static size_t psa_get_key_bits(const psa_key_attributes_t *attributes); + +/** Retrieve the attributes of a key. + * + * This function first resets the attribute structure as with + * psa_reset_key_attributes(). It then copies the attributes of + * the given key into the given attribute structure. + * + * \note This function may allocate memory or other resources. + * Once you have called this function on an attribute structure, + * you must call psa_reset_key_attributes() to free these resources. + * + * \param[in] key Identifier of the key to query. + * \param[in,out] attributes On success, the attributes of the key. + * On failure, equivalent to a + * freshly-initialized structure. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_get_key_attributes(mbedtls_svc_key_id_t key, + psa_key_attributes_t *attributes); + +/** Reset a key attribute structure to a freshly initialized state. + * + * You must initialize the attribute structure as described in the + * documentation of the type #psa_key_attributes_t before calling this + * function. Once the structure has been initialized, you may call this + * function at any time. + * + * This function frees any auxiliary resources that the structure + * may contain. + * + * \param[in,out] attributes The attribute structure to reset. + */ +void psa_reset_key_attributes(psa_key_attributes_t *attributes); + +/**@}*/ + +/** \defgroup key_management Key management + * @{ + */ + +/** Remove non-essential copies of key material from memory. + * + * If the key identifier designates a volatile key, this functions does not do + * anything and returns successfully. + * + * If the key identifier designates a persistent key, then this function will + * free all resources associated with the key in volatile memory. The key + * data in persistent storage is not affected and the key can still be used. + * + * \param key Identifier of the key to purge. + * + * \retval #PSA_SUCCESS + * The key material will have been removed from memory if it is not + * currently required. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not a valid key identifier. + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_purge_key(mbedtls_svc_key_id_t key); + +/** Make a copy of a key. + * + * Copy key material from one location to another. + * + * This function is primarily useful to copy a key from one location + * to another, since it populates a key using the material from + * another key which may have a different lifetime. + * + * This function may be used to share a key with a different party, + * subject to implementation-defined restrictions on key sharing. + * + * The policy on the source key must have the usage flag + * #PSA_KEY_USAGE_COPY set. + * This flag is sufficient to permit the copy if the key has the lifetime + * #PSA_KEY_LIFETIME_VOLATILE or #PSA_KEY_LIFETIME_PERSISTENT. + * Some secure elements do not provide a way to copy a key without + * making it extractable from the secure element. If a key is located + * in such a secure element, then the key must have both usage flags + * #PSA_KEY_USAGE_COPY and #PSA_KEY_USAGE_EXPORT in order to make + * a copy of the key outside the secure element. + * + * The resulting key may only be used in a way that conforms to + * both the policy of the original key and the policy specified in + * the \p attributes parameter: + * - The usage flags on the resulting key are the bitwise-and of the + * usage flags on the source policy and the usage flags in \p attributes. + * - If both allow the same algorithm or wildcard-based + * algorithm policy, the resulting key has the same algorithm policy. + * - If either of the policies allows an algorithm and the other policy + * allows a wildcard-based algorithm policy that includes this algorithm, + * the resulting key allows the same algorithm. + * - If the policies do not allow any algorithm in common, this function + * fails with the status #PSA_ERROR_INVALID_ARGUMENT. + * + * The effect of this function on implementation-defined attributes is + * implementation-defined. + * + * \param source_key The key to copy. It must allow the usage + * #PSA_KEY_USAGE_COPY. If a private or secret key is + * being copied outside of a secure element it must + * also allow #PSA_KEY_USAGE_EXPORT. + * \param[in] attributes The attributes for the new key. + * They are used as follows: + * - The key type and size may be 0. If either is + * nonzero, it must match the corresponding + * attribute of the source key. + * - The key location (the lifetime and, for + * persistent keys, the key identifier) is + * used directly. + * - The policy constraints (usage flags and + * algorithm policy) are combined from + * the source key and \p attributes so that + * both sets of restrictions apply, as + * described in the documentation of this function. + * \param[out] target_key On success, an identifier for the newly created + * key. For persistent keys, this is the key + * identifier defined in \p attributes. + * \c 0 on failure. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE + * \p source_key is invalid. + * \retval #PSA_ERROR_ALREADY_EXISTS + * This is an attempt to create a persistent key, and there is + * already a persistent key with the given identifier. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The lifetime or identifier in \p attributes are invalid, or + * the policy constraints on the source and specified in + * \p attributes are incompatible, or + * \p attributes specifies a key type or key size + * which does not match the attributes of the source key. + * \retval #PSA_ERROR_NOT_PERMITTED + * The source key does not have the #PSA_KEY_USAGE_COPY usage flag, or + * the source key is not exportable and its lifetime does not + * allow copying it to the target's lifetime. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_copy_key(mbedtls_svc_key_id_t source_key, + const psa_key_attributes_t *attributes, + mbedtls_svc_key_id_t *target_key); + + +/** + * \brief Destroy a key. + * + * This function destroys a key from both volatile + * memory and, if applicable, non-volatile storage. Implementations shall + * make a best effort to ensure that the key material cannot be recovered. + * + * This function also erases any metadata such as policies and frees + * resources associated with the key. + * + * If a key is currently in use in a multipart operation, then destroying the + * key will cause the multipart operation to fail. + * + * \param key Identifier of the key to erase. If this is \c 0, do nothing and + * return #PSA_SUCCESS. + * + * \retval #PSA_SUCCESS + * \p key was a valid identifier and the key material that it + * referred to has been erased. Alternatively, \p key is \c 0. + * \retval #PSA_ERROR_NOT_PERMITTED + * The key cannot be erased because it is + * read-only, either due to a policy or due to physical restrictions. + * \retval #PSA_ERROR_INVALID_HANDLE + * \p key is not a valid identifier nor \c 0. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE + * There was a failure in communication with the cryptoprocessor. + * The key material may still be present in the cryptoprocessor. + * \retval #PSA_ERROR_DATA_INVALID + * This error is typically a result of either storage corruption on a + * cleartext storage backend, or an attempt to read data that was + * written by an incompatible version of the library. + * \retval #PSA_ERROR_STORAGE_FAILURE + * The storage is corrupted. Implementations shall make a best effort + * to erase key material even in this stage, however applications + * should be aware that it may be impossible to guarantee that the + * key material is not recoverable in such cases. + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * An unexpected condition which is not a storage corruption or + * a communication failure occurred. The cryptoprocessor may have + * been compromised. + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_destroy_key(mbedtls_svc_key_id_t key); + +/**@}*/ + +/** \defgroup import_export Key import and export + * @{ + */ + +/** + * \brief Import a key in binary format. + * + * This function supports any output from psa_export_key(). Refer to the + * documentation of psa_export_public_key() for the format of public keys + * and to the documentation of psa_export_key() for the format for + * other key types. + * + * The key data determines the key size. The attributes may optionally + * specify a key size; in this case it must match the size determined + * from the key data. A key size of 0 in \p attributes indicates that + * the key size is solely determined by the key data. + * + * Implementations must reject an attempt to import a key of size 0. + * + * This specification supports a single format for each key type. + * Implementations may support other formats as long as the standard + * format is supported. Implementations that support other formats + * should ensure that the formats are clearly unambiguous so as to + * minimize the risk that an invalid input is accidentally interpreted + * according to a different format. + * + * \param[in] attributes The attributes for the new key. + * The key size is always determined from the + * \p data buffer. + * If the key size in \p attributes is nonzero, + * it must be equal to the size from \p data. + * \param[out] key On success, an identifier to the newly created key. + * For persistent keys, this is the key identifier + * defined in \p attributes. + * \c 0 on failure. + * \param[in] data Buffer containing the key data. The content of this + * buffer is interpreted according to the type declared + * in \p attributes. + * All implementations must support at least the format + * described in the documentation + * of psa_export_key() or psa_export_public_key() for + * the chosen type. Implementations may allow other + * formats, but should be conservative: implementations + * should err on the side of rejecting content if it + * may be erroneous (e.g. wrong type or truncated data). + * \param data_length Size of the \p data buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * If the key is persistent, the key material and the key's metadata + * have been saved to persistent storage. + * \retval #PSA_ERROR_ALREADY_EXISTS + * This is an attempt to create a persistent key, and there is + * already a persistent key with the given identifier. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The key type or key size is not supported, either by the + * implementation in general or in this particular persistent location. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key attributes, as a whole, are invalid, or + * the key data is not correctly formatted, or + * the size in \p attributes is nonzero and does not match the size + * of the key data. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_import_key(const psa_key_attributes_t *attributes, + const uint8_t *data, + size_t data_length, + mbedtls_svc_key_id_t *key); + + + +/** + * \brief Export a key in binary format. + * + * The output of this function can be passed to psa_import_key() to + * create an equivalent object. + * + * If the implementation of psa_import_key() supports other formats + * beyond the format specified here, the output from psa_export_key() + * must use the representation specified here, not the original + * representation. + * + * For standard key types, the output format is as follows: + * + * - For symmetric keys (including MAC keys), the format is the + * raw bytes of the key. + * - For DES, the key data consists of 8 bytes. The parity bits must be + * correct. + * - For Triple-DES, the format is the concatenation of the + * two or three DES keys. + * - For RSA key pairs (#PSA_KEY_TYPE_RSA_KEY_PAIR), the format + * is the non-encrypted DER encoding of the representation defined by + * PKCS\#1 (RFC 8017) as `RSAPrivateKey`, version 0. + * ``` + * RSAPrivateKey ::= SEQUENCE { + * version INTEGER, -- must be 0 + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * } + * ``` + * - For elliptic curve key pairs (key types for which + * #PSA_KEY_TYPE_IS_ECC_KEY_PAIR is true), the format is + * a representation of the private value as a `ceiling(m/8)`-byte string + * where `m` is the bit size associated with the curve, i.e. the bit size + * of the order of the curve's coordinate field. This byte string is + * in little-endian order for Montgomery curves (curve types + * `PSA_ECC_FAMILY_CURVEXXX`), and in big-endian order for Weierstrass + * curves (curve types `PSA_ECC_FAMILY_SECTXXX`, `PSA_ECC_FAMILY_SECPXXX` + * and `PSA_ECC_FAMILY_BRAINPOOL_PXXX`). + * For Weierstrass curves, this is the content of the `privateKey` field of + * the `ECPrivateKey` format defined by RFC 5915. For Montgomery curves, + * the format is defined by RFC 7748, and output is masked according to §5. + * For twisted Edwards curves, the private key is as defined by RFC 8032 + * (a 32-byte string for Edwards25519, a 57-byte string for Edwards448). + * - For Diffie-Hellman key exchange key pairs (key types for which + * #PSA_KEY_TYPE_IS_DH_KEY_PAIR is true), the + * format is the representation of the private key `x` as a big-endian byte + * string. The length of the byte string is the private key size in bytes + * (leading zeroes are not stripped). + * - For public keys (key types for which #PSA_KEY_TYPE_IS_PUBLIC_KEY is + * true), the format is the same as for psa_export_public_key(). + * + * The policy on the key must have the usage flag #PSA_KEY_USAGE_EXPORT set. + * + * \param key Identifier of the key to export. It must allow the + * usage #PSA_KEY_USAGE_EXPORT, unless it is a public + * key. + * \param[out] data Buffer where the key data is to be written. + * \param data_size Size of the \p data buffer in bytes. + * \param[out] data_length On success, the number of bytes + * that make up the key data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_EXPORT flag. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p data buffer is too small. You can determine a + * sufficient buffer size by calling + * #PSA_EXPORT_KEY_OUTPUT_SIZE(\c type, \c bits) + * where \c type is the key type + * and \c bits is the key size in bits. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_export_key(mbedtls_svc_key_id_t key, + uint8_t *data, + size_t data_size, + size_t *data_length); + +/** + * \brief Export a public key or the public part of a key pair in binary format. + * + * The output of this function can be passed to psa_import_key() to + * create an object that is equivalent to the public key. + * + * This specification supports a single format for each key type. + * Implementations may support other formats as long as the standard + * format is supported. Implementations that support other formats + * should ensure that the formats are clearly unambiguous so as to + * minimize the risk that an invalid input is accidentally interpreted + * according to a different format. + * + * For standard key types, the output format is as follows: + * - For RSA public keys (#PSA_KEY_TYPE_RSA_PUBLIC_KEY), the DER encoding of + * the representation defined by RFC 3279 §2.3.1 as `RSAPublicKey`. + * ``` + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER } -- e + * ``` + * - For elliptic curve keys on a twisted Edwards curve (key types for which + * #PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY is true and #PSA_KEY_TYPE_ECC_GET_FAMILY + * returns #PSA_ECC_FAMILY_TWISTED_EDWARDS), the public key is as defined + * by RFC 8032 + * (a 32-byte string for Edwards25519, a 57-byte string for Edwards448). + * - For other elliptic curve public keys (key types for which + * #PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY is true), the format is the uncompressed + * representation defined by SEC1 §2.3.3 as the content of an ECPoint. + * Let `m` be the bit size associated with the curve, i.e. the bit size of + * `q` for a curve over `F_q`. The representation consists of: + * - The byte 0x04; + * - `x_P` as a `ceiling(m/8)`-byte string, big-endian; + * - `y_P` as a `ceiling(m/8)`-byte string, big-endian. + * - For Diffie-Hellman key exchange public keys (key types for which + * #PSA_KEY_TYPE_IS_DH_PUBLIC_KEY is true), + * the format is the representation of the public key `y = g^x mod p` as a + * big-endian byte string. The length of the byte string is the length of the + * base prime `p` in bytes. + * + * Exporting a public key object or the public part of a key pair is + * always permitted, regardless of the key's usage flags. + * + * \param key Identifier of the key to export. + * \param[out] data Buffer where the key data is to be written. + * \param data_size Size of the \p data buffer in bytes. + * \param[out] data_length On success, the number of bytes + * that make up the key data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key is neither a public key nor a key pair. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p data buffer is too small. You can determine a + * sufficient buffer size by calling + * #PSA_EXPORT_KEY_OUTPUT_SIZE(#PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(\c type), \c bits) + * where \c type is the key type + * and \c bits is the key size in bits. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_export_public_key(mbedtls_svc_key_id_t key, + uint8_t *data, + size_t data_size, + size_t *data_length); + + + +/**@}*/ + +/** \defgroup hash Message digests + * @{ + */ + +/** Calculate the hash (digest) of a message. + * + * \note To verify the hash of a message against an + * expected value, use psa_hash_compare() instead. + * + * \param alg The hash algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_HASH(\p alg) is true). + * \param[in] input Buffer containing the message to hash. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] hash Buffer where the hash is to be written. + * \param hash_size Size of the \p hash buffer in bytes. + * \param[out] hash_length On success, the number of bytes + * that make up the hash value. This is always + * #PSA_HASH_LENGTH(\p alg). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a hash algorithm. + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p hash_size is too small + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_compute(psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); + +/** Calculate the hash (digest) of a message and compare it with a + * reference value. + * + * \param alg The hash algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_HASH(\p alg) is true). + * \param[in] input Buffer containing the message to hash. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] hash Buffer containing the expected hash value. + * \param hash_length Size of the \p hash buffer in bytes. + * + * \retval #PSA_SUCCESS + * The expected hash is identical to the actual hash of the input. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The hash of the message was calculated successfully, but it + * differs from the expected hash. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a hash algorithm. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p input_length or \p hash_length do not match the hash size for \p alg + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_compare(psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *hash, + size_t hash_length); + +/** The type of the state data structure for multipart hash operations. + * + * Before calling any function on a hash operation object, the application must + * initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_hash_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_hash_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_HASH_OPERATION_INIT, + * for example: + * \code + * psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + * \endcode + * - Assign the result of the function psa_hash_operation_init() + * to the structure, for example: + * \code + * psa_hash_operation_t operation; + * operation = psa_hash_operation_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. */ +typedef struct psa_hash_operation_s psa_hash_operation_t; + +/** \def PSA_HASH_OPERATION_INIT + * + * This macro returns a suitable initializer for a hash operation object + * of type #psa_hash_operation_t. + */ + +/** Return an initial value for a hash operation object. + */ +static psa_hash_operation_t psa_hash_operation_init(void); + +/** Set up a multipart hash operation. + * + * The sequence of operations to calculate a hash (message digest) + * is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_hash_operation_t, e.g. #PSA_HASH_OPERATION_INIT. + * -# Call psa_hash_setup() to specify the algorithm. + * -# Call psa_hash_update() zero, one or more times, passing a fragment + * of the message each time. The hash that is calculated is the hash + * of the concatenation of these messages in order. + * -# To calculate the hash, call psa_hash_finish(). + * To compare the hash with an expected value, call psa_hash_verify(). + * + * If an error occurs at any step after a call to psa_hash_setup(), the + * operation will need to be reset by a call to psa_hash_abort(). The + * application may call psa_hash_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_hash_setup(), the application must + * eventually terminate the operation. The following events terminate an + * operation: + * - A successful call to psa_hash_finish() or psa_hash_verify(). + * - A call to psa_hash_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #psa_hash_operation_t and not yet in use. + * \param alg The hash algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_HASH(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not a supported hash algorithm. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p alg is not a hash algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_setup(psa_hash_operation_t *operation, + psa_algorithm_t alg); + +/** Add a message fragment to a multipart hash operation. + * + * The application must call psa_hash_setup() before calling this function. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_hash_abort(). + * + * \param[in,out] operation Active hash operation. + * \param[in] input Buffer containing the message fragment to hash. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_update(psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length); + +/** Finish the calculation of the hash of a message. + * + * The application must call psa_hash_setup() before calling this function. + * This function calculates the hash of the message formed by concatenating + * the inputs passed to preceding calls to psa_hash_update(). + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_hash_abort(). + * + * \warning Applications should not call this function if they expect + * a specific value for the hash. Call psa_hash_verify() instead. + * Beware that comparing integrity or authenticity data such as + * hash values with a function such as \c memcmp is risky + * because the time taken by the comparison may leak information + * about the hashed data which could allow an attacker to guess + * a valid hash and thereby bypass security controls. + * + * \param[in,out] operation Active hash operation. + * \param[out] hash Buffer where the hash is to be written. + * \param hash_size Size of the \p hash buffer in bytes. + * \param[out] hash_length On success, the number of bytes + * that make up the hash value. This is always + * #PSA_HASH_LENGTH(\c alg) where \c alg is the + * hash algorithm that is calculated. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p hash buffer is too small. You can determine a + * sufficient buffer size by calling #PSA_HASH_LENGTH(\c alg) + * where \c alg is the hash algorithm that is calculated. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_finish(psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); + +/** Finish the calculation of the hash of a message and compare it with + * an expected value. + * + * The application must call psa_hash_setup() before calling this function. + * This function calculates the hash of the message formed by concatenating + * the inputs passed to preceding calls to psa_hash_update(). It then + * compares the calculated hash with the expected hash passed as a + * parameter to this function. + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_hash_abort(). + * + * \note Implementations shall make the best effort to ensure that the + * comparison between the actual hash and the expected hash is performed + * in constant time. + * + * \param[in,out] operation Active hash operation. + * \param[in] hash Buffer containing the expected hash value. + * \param hash_length Size of the \p hash buffer in bytes. + * + * \retval #PSA_SUCCESS + * The expected hash is identical to the actual hash of the message. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The hash of the message was calculated successfully, but it + * differs from the expected hash. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_verify(psa_hash_operation_t *operation, + const uint8_t *hash, + size_t hash_length); + +/** Abort a hash operation. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation by calling + * psa_hash_setup() again. + * + * You may call this function any time after the operation object has + * been initialized by one of the methods described in #psa_hash_operation_t. + * + * In particular, calling psa_hash_abort() after the operation has been + * terminated by a call to psa_hash_abort(), psa_hash_finish() or + * psa_hash_verify() is safe and has no effect. + * + * \param[in,out] operation Initialized hash operation. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_abort(psa_hash_operation_t *operation); + +/** Clone a hash operation. + * + * This function copies the state of an ongoing hash operation to + * a new operation object. In other words, this function is equivalent + * to calling psa_hash_setup() on \p target_operation with the same + * algorithm that \p source_operation was set up for, then + * psa_hash_update() on \p target_operation with the same input that + * that was passed to \p source_operation. After this function returns, the + * two objects are independent, i.e. subsequent calls involving one of + * the objects do not affect the other object. + * + * \param[in] source_operation The active hash operation to clone. + * \param[in,out] target_operation The operation object to set up. + * It must be initialized but not active. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The \p source_operation state is not valid (it must be active), or + * the \p target_operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation, + psa_hash_operation_t *target_operation); + +/**@}*/ + +/** \defgroup MAC Message authentication codes + * @{ + */ + +/** Calculate the MAC (message authentication code) of a message. + * + * \note To verify the MAC of a message against an + * expected value, use psa_mac_verify() instead. + * Beware that comparing integrity or authenticity data such as + * MAC values with a function such as \c memcmp is risky + * because the time taken by the comparison may leak information + * about the MAC value which could allow an attacker to guess + * a valid MAC and thereby bypass security controls. + * + * \param key Identifier of the key to use for the operation. It + * must allow the usage PSA_KEY_USAGE_SIGN_MESSAGE. + * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_MAC(\p alg) is true). + * \param[in] input Buffer containing the input message. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] mac Buffer where the MAC value is to be written. + * \param mac_size Size of the \p mac buffer in bytes. + * \param[out] mac_length On success, the number of bytes + * that make up the MAC value. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a MAC algorithm. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p mac_size is too small + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE + * The key could not be retrieved from storage. + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_compute(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length); + +/** Calculate the MAC of a message and compare it with a reference value. + * + * \param key Identifier of the key to use for the operation. It + * must allow the usage PSA_KEY_USAGE_VERIFY_MESSAGE. + * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_MAC(\p alg) is true). + * \param[in] input Buffer containing the input message. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] mac Buffer containing the expected MAC value. + * \param mac_length Size of the \p mac buffer in bytes. + * + * \retval #PSA_SUCCESS + * The expected MAC is identical to the actual MAC of the input. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The MAC of the message was calculated successfully, but it + * differs from the expected value. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a MAC algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE + * The key could not be retrieved from storage. + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_verify(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *mac, + size_t mac_length); + +/** The type of the state data structure for multipart MAC operations. + * + * Before calling any function on a MAC operation object, the application must + * initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_mac_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_mac_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_MAC_OPERATION_INIT, + * for example: + * \code + * psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + * \endcode + * - Assign the result of the function psa_mac_operation_init() + * to the structure, for example: + * \code + * psa_mac_operation_t operation; + * operation = psa_mac_operation_init(); + * \endcode + * + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. */ +typedef struct psa_mac_operation_s psa_mac_operation_t; + +/** \def PSA_MAC_OPERATION_INIT + * + * This macro returns a suitable initializer for a MAC operation object of type + * #psa_mac_operation_t. + */ + +/** Return an initial value for a MAC operation object. + */ +static psa_mac_operation_t psa_mac_operation_init(void); + +/** Set up a multipart MAC calculation operation. + * + * This function sets up the calculation of the MAC + * (message authentication code) of a byte string. + * To verify the MAC of a message against an + * expected value, use psa_mac_verify_setup() instead. + * + * The sequence of operations to calculate a MAC is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_mac_operation_t, e.g. #PSA_MAC_OPERATION_INIT. + * -# Call psa_mac_sign_setup() to specify the algorithm and key. + * -# Call psa_mac_update() zero, one or more times, passing a fragment + * of the message each time. The MAC that is calculated is the MAC + * of the concatenation of these messages in order. + * -# At the end of the message, call psa_mac_sign_finish() to finish + * calculating the MAC value and retrieve it. + * + * If an error occurs at any step after a call to psa_mac_sign_setup(), the + * operation will need to be reset by a call to psa_mac_abort(). The + * application may call psa_mac_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_mac_sign_setup(), the application must + * eventually terminate the operation through one of the following methods: + * - A successful call to psa_mac_sign_finish(). + * - A call to psa_mac_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #psa_mac_operation_t and not yet in use. + * \param key Identifier of the key to use for the operation. It + * must remain valid until the operation terminates. + * It must allow the usage PSA_KEY_USAGE_SIGN_MESSAGE. + * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_MAC(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a MAC algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE + * The key could not be retrieved from storage. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg); + +/** Set up a multipart MAC verification operation. + * + * This function sets up the verification of the MAC + * (message authentication code) of a byte string against an expected value. + * + * The sequence of operations to verify a MAC is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_mac_operation_t, e.g. #PSA_MAC_OPERATION_INIT. + * -# Call psa_mac_verify_setup() to specify the algorithm and key. + * -# Call psa_mac_update() zero, one or more times, passing a fragment + * of the message each time. The MAC that is calculated is the MAC + * of the concatenation of these messages in order. + * -# At the end of the message, call psa_mac_verify_finish() to finish + * calculating the actual MAC of the message and verify it against + * the expected value. + * + * If an error occurs at any step after a call to psa_mac_verify_setup(), the + * operation will need to be reset by a call to psa_mac_abort(). The + * application may call psa_mac_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_mac_verify_setup(), the application must + * eventually terminate the operation through one of the following methods: + * - A successful call to psa_mac_verify_finish(). + * - A call to psa_mac_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #psa_mac_operation_t and not yet in use. + * \param key Identifier of the key to use for the operation. It + * must remain valid until the operation terminates. + * It must allow the usage + * PSA_KEY_USAGE_VERIFY_MESSAGE. + * \param alg The MAC algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_MAC(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c key is not compatible with \c alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \c alg is not supported or is not a MAC algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE + * The key could not be retrieved from storage. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_verify_setup(psa_mac_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg); + +/** Add a message fragment to a multipart MAC operation. + * + * The application must call psa_mac_sign_setup() or psa_mac_verify_setup() + * before calling this function. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_mac_abort(). + * + * \param[in,out] operation Active MAC operation. + * \param[in] input Buffer containing the message fragment to add to + * the MAC calculation. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_update(psa_mac_operation_t *operation, + const uint8_t *input, + size_t input_length); + +/** Finish the calculation of the MAC of a message. + * + * The application must call psa_mac_sign_setup() before calling this function. + * This function calculates the MAC of the message formed by concatenating + * the inputs passed to preceding calls to psa_mac_update(). + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_mac_abort(). + * + * \warning Applications should not call this function if they expect + * a specific value for the MAC. Call psa_mac_verify_finish() instead. + * Beware that comparing integrity or authenticity data such as + * MAC values with a function such as \c memcmp is risky + * because the time taken by the comparison may leak information + * about the MAC value which could allow an attacker to guess + * a valid MAC and thereby bypass security controls. + * + * \param[in,out] operation Active MAC operation. + * \param[out] mac Buffer where the MAC value is to be written. + * \param mac_size Size of the \p mac buffer in bytes. + * \param[out] mac_length On success, the number of bytes + * that make up the MAC value. This is always + * #PSA_MAC_LENGTH(\c key_type, \c key_bits, \c alg) + * where \c key_type and \c key_bits are the type and + * bit-size respectively of the key and \c alg is the + * MAC algorithm that is calculated. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p mac buffer is too small. You can determine a + * sufficient buffer size by calling PSA_MAC_LENGTH(). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active mac sign + * operation), or the library has not been previously initialized + * by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation, + uint8_t *mac, + size_t mac_size, + size_t *mac_length); + +/** Finish the calculation of the MAC of a message and compare it with + * an expected value. + * + * The application must call psa_mac_verify_setup() before calling this function. + * This function calculates the MAC of the message formed by concatenating + * the inputs passed to preceding calls to psa_mac_update(). It then + * compares the calculated MAC with the expected MAC passed as a + * parameter to this function. + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_mac_abort(). + * + * \note Implementations shall make the best effort to ensure that the + * comparison between the actual MAC and the expected MAC is performed + * in constant time. + * + * \param[in,out] operation Active MAC operation. + * \param[in] mac Buffer containing the expected MAC value. + * \param mac_length Size of the \p mac buffer in bytes. + * + * \retval #PSA_SUCCESS + * The expected MAC is identical to the actual MAC of the message. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The MAC of the message was calculated successfully, but it + * differs from the expected MAC. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active mac verify + * operation), or the library has not been previously initialized + * by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation, + const uint8_t *mac, + size_t mac_length); + +/** Abort a MAC operation. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation by calling + * psa_mac_sign_setup() or psa_mac_verify_setup() again. + * + * You may call this function any time after the operation object has + * been initialized by one of the methods described in #psa_mac_operation_t. + * + * In particular, calling psa_mac_abort() after the operation has been + * terminated by a call to psa_mac_abort(), psa_mac_sign_finish() or + * psa_mac_verify_finish() is safe and has no effect. + * + * \param[in,out] operation Initialized MAC operation. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_mac_abort(psa_mac_operation_t *operation); + +/**@}*/ + +/** \defgroup cipher Symmetric ciphers + * @{ + */ + +/** Encrypt a message using a symmetric cipher. + * + * This function encrypts a message with a random IV (initialization + * vector). Use the multipart operation interface with a + * #psa_cipher_operation_t object to provide other forms of IV. + * + * \param key Identifier of the key to use for the operation. + * It must allow the usage #PSA_KEY_USAGE_ENCRYPT. + * \param alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \param[in] input Buffer containing the message to encrypt. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] output Buffer where the output is to be written. + * The output contains the IV followed by + * the ciphertext proper. + * \param output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a cipher algorithm. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Decrypt a message using a symmetric cipher. + * + * This function decrypts a message encrypted with a symmetric cipher. + * + * \param key Identifier of the key to use for the operation. + * It must remain valid until the operation + * terminates. It must allow the usage + * #PSA_KEY_USAGE_DECRYPT. + * \param alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \param[in] input Buffer containing the message to decrypt. + * This consists of the IV followed by the + * ciphertext proper. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] output Buffer where the plaintext is to be written. + * \param output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a cipher algorithm. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_decrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** The type of the state data structure for multipart cipher operations. + * + * Before calling any function on a cipher operation object, the application + * must initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_cipher_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_cipher_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_CIPHER_OPERATION_INIT, + * for example: + * \code + * psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT; + * \endcode + * - Assign the result of the function psa_cipher_operation_init() + * to the structure, for example: + * \code + * psa_cipher_operation_t operation; + * operation = psa_cipher_operation_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. */ +typedef struct psa_cipher_operation_s psa_cipher_operation_t; + +/** \def PSA_CIPHER_OPERATION_INIT + * + * This macro returns a suitable initializer for a cipher operation object of + * type #psa_cipher_operation_t. + */ + +/** Return an initial value for a cipher operation object. + */ +static psa_cipher_operation_t psa_cipher_operation_init(void); + +/** Set the key for a multipart symmetric encryption operation. + * + * The sequence of operations to encrypt a message with a symmetric cipher + * is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_cipher_operation_t, e.g. + * #PSA_CIPHER_OPERATION_INIT. + * -# Call psa_cipher_encrypt_setup() to specify the algorithm and key. + * -# Call either psa_cipher_generate_iv() or psa_cipher_set_iv() to + * generate or set the IV (initialization vector). You should use + * psa_cipher_generate_iv() unless the protocol you are implementing + * requires a specific IV value. + * -# Call psa_cipher_update() zero, one or more times, passing a fragment + * of the message each time. + * -# Call psa_cipher_finish(). + * + * If an error occurs at any step after a call to psa_cipher_encrypt_setup(), + * the operation will need to be reset by a call to psa_cipher_abort(). The + * application may call psa_cipher_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_cipher_encrypt_setup(), the application must + * eventually terminate the operation. The following events terminate an + * operation: + * - A successful call to psa_cipher_finish(). + * - A call to psa_cipher_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #psa_cipher_operation_t and not yet in use. + * \param key Identifier of the key to use for the operation. + * It must remain valid until the operation + * terminates. It must allow the usage + * #PSA_KEY_USAGE_ENCRYPT. + * \param alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a cipher algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg); + +/** Set the key for a multipart symmetric decryption operation. + * + * The sequence of operations to decrypt a message with a symmetric cipher + * is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_cipher_operation_t, e.g. + * #PSA_CIPHER_OPERATION_INIT. + * -# Call psa_cipher_decrypt_setup() to specify the algorithm and key. + * -# Call psa_cipher_set_iv() with the IV (initialization vector) for the + * decryption. If the IV is prepended to the ciphertext, you can call + * psa_cipher_update() on a buffer containing the IV followed by the + * beginning of the message. + * -# Call psa_cipher_update() zero, one or more times, passing a fragment + * of the message each time. + * -# Call psa_cipher_finish(). + * + * If an error occurs at any step after a call to psa_cipher_decrypt_setup(), + * the operation will need to be reset by a call to psa_cipher_abort(). The + * application may call psa_cipher_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_cipher_decrypt_setup(), the application must + * eventually terminate the operation. The following events terminate an + * operation: + * - A successful call to psa_cipher_finish(). + * - A call to psa_cipher_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #psa_cipher_operation_t and not yet in use. + * \param key Identifier of the key to use for the operation. + * It must remain valid until the operation + * terminates. It must allow the usage + * #PSA_KEY_USAGE_DECRYPT. + * \param alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not a cipher algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg); + +/** Generate an IV for a symmetric encryption operation. + * + * This function generates a random IV (initialization vector), nonce + * or initial counter value for the encryption operation as appropriate + * for the chosen algorithm, key type and key size. + * + * The application must call psa_cipher_encrypt_setup() before + * calling this function. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_cipher_abort(). + * + * \param[in,out] operation Active cipher operation. + * \param[out] iv Buffer where the generated IV is to be written. + * \param iv_size Size of the \p iv buffer in bytes. + * \param[out] iv_length On success, the number of bytes of the + * generated IV. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p iv buffer is too small. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, with no IV set), + * or the library has not been previously initialized + * by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, + uint8_t *iv, + size_t iv_size, + size_t *iv_length); + +/** Set the IV for a symmetric encryption or decryption operation. + * + * This function sets the IV (initialization vector), nonce + * or initial counter value for the encryption or decryption operation. + * + * The application must call psa_cipher_encrypt_setup() before + * calling this function. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_cipher_abort(). + * + * \note When encrypting, applications should use psa_cipher_generate_iv() + * instead of this function, unless implementing a protocol that requires + * a non-random IV. + * + * \param[in,out] operation Active cipher operation. + * \param[in] iv Buffer containing the IV to use. + * \param iv_length Size of the IV in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size of \p iv is not acceptable for the chosen algorithm, + * or the chosen algorithm does not use an IV. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active cipher + * encrypt operation, with no IV set), or the library has not been + * previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation, + const uint8_t *iv, + size_t iv_length); + +/** Encrypt or decrypt a message fragment in an active cipher operation. + * + * Before calling this function, you must: + * 1. Call either psa_cipher_encrypt_setup() or psa_cipher_decrypt_setup(). + * The choice of setup function determines whether this function + * encrypts or decrypts its input. + * 2. If the algorithm requires an IV, call psa_cipher_generate_iv() + * (recommended when encrypting) or psa_cipher_set_iv(). + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_cipher_abort(). + * + * \param[in,out] operation Active cipher operation. + * \param[in] input Buffer containing the message fragment to + * encrypt or decrypt. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] output Buffer where the output is to be written. + * \param output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, with an IV set + * if required for the algorithm), or the library has not been + * previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Finish encrypting or decrypting a message in a cipher operation. + * + * The application must call psa_cipher_encrypt_setup() or + * psa_cipher_decrypt_setup() before calling this function. The choice + * of setup function determines whether this function encrypts or + * decrypts its input. + * + * This function finishes the encryption or decryption of the message + * formed by concatenating the inputs passed to preceding calls to + * psa_cipher_update(). + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_cipher_abort(). + * + * \param[in,out] operation Active cipher operation. + * \param[out] output Buffer where the output is to be written. + * \param output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The total input size passed to this operation is not valid for + * this particular algorithm. For example, the algorithm is a based + * on block cipher and requires a whole number of blocks, but the + * total input size is not a multiple of the block size. + * \retval #PSA_ERROR_INVALID_PADDING + * This is a decryption operation for an algorithm that includes + * padding, and the ciphertext does not contain valid padding. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, with an IV set + * if required for the algorithm), or the library has not been + * previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Abort a cipher operation. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation by calling + * psa_cipher_encrypt_setup() or psa_cipher_decrypt_setup() again. + * + * You may call this function any time after the operation object has + * been initialized as described in #psa_cipher_operation_t. + * + * In particular, calling psa_cipher_abort() after the operation has been + * terminated by a call to psa_cipher_abort() or psa_cipher_finish() + * is safe and has no effect. + * + * \param[in,out] operation Initialized cipher operation. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation); + +/**@}*/ + +/** \defgroup aead Authenticated encryption with associated data (AEAD) + * @{ + */ + +/** Process an authenticated encryption operation. + * + * \param key Identifier of the key to use for the + * operation. It must allow the usage + * #PSA_KEY_USAGE_ENCRYPT. + * \param alg The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * \param[in] nonce Nonce or IV to use. + * \param nonce_length Size of the \p nonce buffer in bytes. + * \param[in] additional_data Additional data that will be authenticated + * but not encrypted. + * \param additional_data_length Size of \p additional_data in bytes. + * \param[in] plaintext Data that will be authenticated and + * encrypted. + * \param plaintext_length Size of \p plaintext in bytes. + * \param[out] ciphertext Output buffer for the authenticated and + * encrypted data. The additional data is not + * part of this output. For algorithms where the + * encrypted data and the authentication tag + * are defined as separate outputs, the + * authentication tag is appended to the + * encrypted data. + * \param ciphertext_size Size of the \p ciphertext buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - A sufficient output size is + * #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(\c key_type, + * \p alg, \p plaintext_length) where + * \c key_type is the type of \p key. + * - #PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(\p + * plaintext_length) evaluates to the maximum + * ciphertext size of any supported AEAD + * encryption. + * \param[out] ciphertext_length On success, the size of the output + * in the \p ciphertext buffer. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not an AEAD algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p ciphertext_size is too small. + * #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(\c key_type, \p alg, + * \p plaintext_length) or + * #PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(\p plaintext_length) can be used to + * determine the required buffer size. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *nonce, + size_t nonce_length, + const uint8_t *additional_data, + size_t additional_data_length, + const uint8_t *plaintext, + size_t plaintext_length, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length); + +/** Process an authenticated decryption operation. + * + * \param key Identifier of the key to use for the + * operation. It must allow the usage + * #PSA_KEY_USAGE_DECRYPT. + * \param alg The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * \param[in] nonce Nonce or IV to use. + * \param nonce_length Size of the \p nonce buffer in bytes. + * \param[in] additional_data Additional data that has been authenticated + * but not encrypted. + * \param additional_data_length Size of \p additional_data in bytes. + * \param[in] ciphertext Data that has been authenticated and + * encrypted. For algorithms where the + * encrypted data and the authentication tag + * are defined as separate inputs, the buffer + * must contain the encrypted data followed + * by the authentication tag. + * \param ciphertext_length Size of \p ciphertext in bytes. + * \param[out] plaintext Output buffer for the decrypted data. + * \param plaintext_size Size of the \p plaintext buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - A sufficient output size is + * #PSA_AEAD_DECRYPT_OUTPUT_SIZE(\c key_type, + * \p alg, \p ciphertext_length) where + * \c key_type is the type of \p key. + * - #PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(\p + * ciphertext_length) evaluates to the maximum + * plaintext size of any supported AEAD + * decryption. + * \param[out] plaintext_length On success, the size of the output + * in the \p plaintext buffer. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The ciphertext is not authentic. + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not an AEAD algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p plaintext_size is too small. + * #PSA_AEAD_DECRYPT_OUTPUT_SIZE(\c key_type, \p alg, + * \p ciphertext_length) or + * #PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(\p ciphertext_length) can be used + * to determine the required buffer size. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_decrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *nonce, + size_t nonce_length, + const uint8_t *additional_data, + size_t additional_data_length, + const uint8_t *ciphertext, + size_t ciphertext_length, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length); + +/** The type of the state data structure for multipart AEAD operations. + * + * Before calling any function on an AEAD operation object, the application + * must initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_aead_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_aead_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_AEAD_OPERATION_INIT, + * for example: + * \code + * psa_aead_operation_t operation = PSA_AEAD_OPERATION_INIT; + * \endcode + * - Assign the result of the function psa_aead_operation_init() + * to the structure, for example: + * \code + * psa_aead_operation_t operation; + * operation = psa_aead_operation_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. */ +typedef struct psa_aead_operation_s psa_aead_operation_t; + +/** \def PSA_AEAD_OPERATION_INIT + * + * This macro returns a suitable initializer for an AEAD operation object of + * type #psa_aead_operation_t. + */ + +/** Return an initial value for an AEAD operation object. + */ +static psa_aead_operation_t psa_aead_operation_init(void); + +/** Set the key for a multipart authenticated encryption operation. + * + * The sequence of operations to encrypt a message with authentication + * is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_aead_operation_t, e.g. + * #PSA_AEAD_OPERATION_INIT. + * -# Call psa_aead_encrypt_setup() to specify the algorithm and key. + * -# If needed, call psa_aead_set_lengths() to specify the length of the + * inputs to the subsequent calls to psa_aead_update_ad() and + * psa_aead_update(). See the documentation of psa_aead_set_lengths() + * for details. + * -# Call either psa_aead_generate_nonce() or psa_aead_set_nonce() to + * generate or set the nonce. You should use + * psa_aead_generate_nonce() unless the protocol you are implementing + * requires a specific nonce value. + * -# Call psa_aead_update_ad() zero, one or more times, passing a fragment + * of the non-encrypted additional authenticated data each time. + * -# Call psa_aead_update() zero, one or more times, passing a fragment + * of the message to encrypt each time. + * -# Call psa_aead_finish(). + * + * If an error occurs at any step after a call to psa_aead_encrypt_setup(), + * the operation will need to be reset by a call to psa_aead_abort(). The + * application may call psa_aead_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_aead_encrypt_setup(), the application must + * eventually terminate the operation. The following events terminate an + * operation: + * - A successful call to psa_aead_finish(). + * - A call to psa_aead_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #psa_aead_operation_t and not yet in use. + * \param key Identifier of the key to use for the operation. + * It must remain valid until the operation + * terminates. It must allow the usage + * #PSA_KEY_USAGE_ENCRYPT. + * \param alg The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not an AEAD algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_encrypt_setup(psa_aead_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg); + +/** Set the key for a multipart authenticated decryption operation. + * + * The sequence of operations to decrypt a message with authentication + * is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_aead_operation_t, e.g. + * #PSA_AEAD_OPERATION_INIT. + * -# Call psa_aead_decrypt_setup() to specify the algorithm and key. + * -# If needed, call psa_aead_set_lengths() to specify the length of the + * inputs to the subsequent calls to psa_aead_update_ad() and + * psa_aead_update(). See the documentation of psa_aead_set_lengths() + * for details. + * -# Call psa_aead_set_nonce() with the nonce for the decryption. + * -# Call psa_aead_update_ad() zero, one or more times, passing a fragment + * of the non-encrypted additional authenticated data each time. + * -# Call psa_aead_update() zero, one or more times, passing a fragment + * of the ciphertext to decrypt each time. + * -# Call psa_aead_verify(). + * + * If an error occurs at any step after a call to psa_aead_decrypt_setup(), + * the operation will need to be reset by a call to psa_aead_abort(). The + * application may call psa_aead_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_aead_decrypt_setup(), the application must + * eventually terminate the operation. The following events terminate an + * operation: + * - A successful call to psa_aead_verify(). + * - A call to psa_aead_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #psa_aead_operation_t and not yet in use. + * \param key Identifier of the key to use for the operation. + * It must remain valid until the operation + * terminates. It must allow the usage + * #PSA_KEY_USAGE_DECRYPT. + * \param alg The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not compatible with \p alg. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported or is not an AEAD algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or the + * library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_decrypt_setup(psa_aead_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg); + +/** Generate a random nonce for an authenticated encryption operation. + * + * This function generates a random nonce for the authenticated encryption + * operation with an appropriate size for the chosen algorithm, key type + * and key size. + * + * The application must call psa_aead_encrypt_setup() before + * calling this function. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_aead_abort(). + * + * \param[in,out] operation Active AEAD operation. + * \param[out] nonce Buffer where the generated nonce is to be + * written. + * \param nonce_size Size of the \p nonce buffer in bytes. + * \param[out] nonce_length On success, the number of bytes of the + * generated nonce. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p nonce buffer is too small. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active aead encrypt + * operation, with no nonce set), or the library has not been + * previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_generate_nonce(psa_aead_operation_t *operation, + uint8_t *nonce, + size_t nonce_size, + size_t *nonce_length); + +/** Set the nonce for an authenticated encryption or decryption operation. + * + * This function sets the nonce for the authenticated + * encryption or decryption operation. + * + * The application must call psa_aead_encrypt_setup() or + * psa_aead_decrypt_setup() before calling this function. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_aead_abort(). + * + * \note When encrypting, applications should use psa_aead_generate_nonce() + * instead of this function, unless implementing a protocol that requires + * a non-random IV. + * + * \param[in,out] operation Active AEAD operation. + * \param[in] nonce Buffer containing the nonce to use. + * \param nonce_length Size of the nonce in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size of \p nonce is not acceptable for the chosen algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, with no nonce + * set), or the library has not been previously initialized + * by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation, + const uint8_t *nonce, + size_t nonce_length); + +/** Declare the lengths of the message and additional data for AEAD. + * + * The application must call this function before calling + * psa_aead_update_ad() or psa_aead_update() if the algorithm for + * the operation requires it. If the algorithm does not require it, + * calling this function is optional, but if this function is called + * then the implementation must enforce the lengths. + * + * You may call this function before or after setting the nonce with + * psa_aead_set_nonce() or psa_aead_generate_nonce(). + * + * - For #PSA_ALG_CCM, calling this function is required. + * - For the other AEAD algorithms defined in this specification, calling + * this function is not required. + * - For vendor-defined algorithm, refer to the vendor documentation. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_aead_abort(). + * + * \param[in,out] operation Active AEAD operation. + * \param ad_length Size of the non-encrypted additional + * authenticated data in bytes. + * \param plaintext_length Size of the plaintext to encrypt in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * At least one of the lengths is not acceptable for the chosen + * algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, and + * psa_aead_update_ad() and psa_aead_update() must not have been + * called yet), or the library has not been previously initialized + * by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_set_lengths(psa_aead_operation_t *operation, + size_t ad_length, + size_t plaintext_length); + +/** Pass additional data to an active AEAD operation. + * + * Additional data is authenticated, but not encrypted. + * + * You may call this function multiple times to pass successive fragments + * of the additional data. You may not call this function after passing + * data to encrypt or decrypt with psa_aead_update(). + * + * Before calling this function, you must: + * 1. Call either psa_aead_encrypt_setup() or psa_aead_decrypt_setup(). + * 2. Set the nonce with psa_aead_generate_nonce() or psa_aead_set_nonce(). + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_aead_abort(). + * + * \warning When decrypting, until psa_aead_verify() has returned #PSA_SUCCESS, + * there is no guarantee that the input is valid. Therefore, until + * you have called psa_aead_verify() and it has returned #PSA_SUCCESS, + * treat the input as untrusted and prepare to undo any action that + * depends on the input if psa_aead_verify() returns an error status. + * + * \param[in,out] operation Active AEAD operation. + * \param[in] input Buffer containing the fragment of + * additional data. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The total input length overflows the additional data length that + * was previously specified with psa_aead_set_lengths(). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, have a nonce + * set, have lengths set if required by the algorithm, and + * psa_aead_update() must not have been called yet), or the library + * has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length); + +/** Encrypt or decrypt a message fragment in an active AEAD operation. + * + * Before calling this function, you must: + * 1. Call either psa_aead_encrypt_setup() or psa_aead_decrypt_setup(). + * The choice of setup function determines whether this function + * encrypts or decrypts its input. + * 2. Set the nonce with psa_aead_generate_nonce() or psa_aead_set_nonce(). + * 3. Call psa_aead_update_ad() to pass all the additional data. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_aead_abort(). + * + * \warning When decrypting, until psa_aead_verify() has returned #PSA_SUCCESS, + * there is no guarantee that the input is valid. Therefore, until + * you have called psa_aead_verify() and it has returned #PSA_SUCCESS: + * - Do not use the output in any way other than storing it in a + * confidential location. If you take any action that depends + * on the tentative decrypted data, this action will need to be + * undone if the input turns out not to be valid. Furthermore, + * if an adversary can observe that this action took place + * (for example through timing), they may be able to use this + * fact as an oracle to decrypt any message encrypted with the + * same key. + * - In particular, do not copy the output anywhere but to a + * memory or storage space that you have exclusive access to. + * + * This function does not require the input to be aligned to any + * particular block boundary. If the implementation can only process + * a whole block at a time, it must consume all the input provided, but + * it may delay the end of the corresponding output until a subsequent + * call to psa_aead_update(), psa_aead_finish() or psa_aead_verify() + * provides sufficient input. The amount of data that can be delayed + * in this way is bounded by #PSA_AEAD_UPDATE_OUTPUT_SIZE. + * + * \param[in,out] operation Active AEAD operation. + * \param[in] input Buffer containing the message fragment to + * encrypt or decrypt. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] output Buffer where the output is to be written. + * \param output_size Size of the \p output buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - A sufficient output size is + * #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type, + * \c alg, \p input_length) where + * \c key_type is the type of key and \c alg is + * the algorithm that were used to set up the + * operation. + * - #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p + * input_length) evaluates to the maximum + * output size of any supported AEAD + * algorithm. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type, \c alg, \p input_length) or + * #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p input_length) can be used to + * determine the required buffer size. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The total length of input to psa_aead_update_ad() so far is + * less than the additional data length that was previously + * specified with psa_aead_set_lengths(), or + * the total input length overflows the plaintext length that + * was previously specified with psa_aead_set_lengths(). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, have a nonce + * set, and have lengths set if required by the algorithm), or the + * library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_update(psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Finish encrypting a message in an AEAD operation. + * + * The operation must have been set up with psa_aead_encrypt_setup(). + * + * This function finishes the authentication of the additional data + * formed by concatenating the inputs passed to preceding calls to + * psa_aead_update_ad() with the plaintext formed by concatenating the + * inputs passed to preceding calls to psa_aead_update(). + * + * This function has two output buffers: + * - \p ciphertext contains trailing ciphertext that was buffered from + * preceding calls to psa_aead_update(). + * - \p tag contains the authentication tag. + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_aead_abort(). + * + * \param[in,out] operation Active AEAD operation. + * \param[out] ciphertext Buffer where the last part of the ciphertext + * is to be written. + * \param ciphertext_size Size of the \p ciphertext buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - A sufficient output size is + * #PSA_AEAD_FINISH_OUTPUT_SIZE(\c key_type, + * \c alg) where \c key_type is the type of key + * and \c alg is the algorithm that were used to + * set up the operation. + * - #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE evaluates to + * the maximum output size of any supported AEAD + * algorithm. + * \param[out] ciphertext_length On success, the number of bytes of + * returned ciphertext. + * \param[out] tag Buffer where the authentication tag is + * to be written. + * \param tag_size Size of the \p tag buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - The exact tag size is #PSA_AEAD_TAG_LENGTH(\c + * key_type, \c key_bits, \c alg) where + * \c key_type and \c key_bits are the type and + * bit-size of the key, and \c alg is the + * algorithm that were used in the call to + * psa_aead_encrypt_setup(). + * - #PSA_AEAD_TAG_MAX_SIZE evaluates to the + * maximum tag size of any supported AEAD + * algorithm. + * \param[out] tag_length On success, the number of bytes + * that make up the returned tag. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p ciphertext or \p tag buffer is too small. + * #PSA_AEAD_FINISH_OUTPUT_SIZE(\c key_type, \c alg) or + * #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE can be used to determine the + * required \p ciphertext buffer size. #PSA_AEAD_TAG_LENGTH(\c key_type, + * \c key_bits, \c alg) or #PSA_AEAD_TAG_MAX_SIZE can be used to + * determine the required \p tag buffer size. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The total length of input to psa_aead_update_ad() so far is + * less than the additional data length that was previously + * specified with psa_aead_set_lengths(), or + * the total length of input to psa_aead_update() so far is + * less than the plaintext length that was previously + * specified with psa_aead_set_lengths(). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active encryption + * operation with a nonce set), or the library has not been previously + * initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_finish(psa_aead_operation_t *operation, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length, + uint8_t *tag, + size_t tag_size, + size_t *tag_length); + +/** Finish authenticating and decrypting a message in an AEAD operation. + * + * The operation must have been set up with psa_aead_decrypt_setup(). + * + * This function finishes the authenticated decryption of the message + * components: + * + * - The additional data consisting of the concatenation of the inputs + * passed to preceding calls to psa_aead_update_ad(). + * - The ciphertext consisting of the concatenation of the inputs passed to + * preceding calls to psa_aead_update(). + * - The tag passed to this function call. + * + * If the authentication tag is correct, this function outputs any remaining + * plaintext and reports success. If the authentication tag is not correct, + * this function returns #PSA_ERROR_INVALID_SIGNATURE. + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_aead_abort(). + * + * \note Implementations shall make the best effort to ensure that the + * comparison between the actual tag and the expected tag is performed + * in constant time. + * + * \param[in,out] operation Active AEAD operation. + * \param[out] plaintext Buffer where the last part of the plaintext + * is to be written. This is the remaining data + * from previous calls to psa_aead_update() + * that could not be processed until the end + * of the input. + * \param plaintext_size Size of the \p plaintext buffer in bytes. + * This must be appropriate for the selected algorithm and key: + * - A sufficient output size is + * #PSA_AEAD_VERIFY_OUTPUT_SIZE(\c key_type, + * \c alg) where \c key_type is the type of key + * and \c alg is the algorithm that were used to + * set up the operation. + * - #PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE evaluates to + * the maximum output size of any supported AEAD + * algorithm. + * \param[out] plaintext_length On success, the number of bytes of + * returned plaintext. + * \param[in] tag Buffer containing the authentication tag. + * \param tag_length Size of the \p tag buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculations were successful, but the authentication tag is + * not correct. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p plaintext buffer is too small. + * #PSA_AEAD_VERIFY_OUTPUT_SIZE(\c key_type, \c alg) or + * #PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE can be used to determine the + * required buffer size. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The total length of input to psa_aead_update_ad() so far is + * less than the additional data length that was previously + * specified with psa_aead_set_lengths(), or + * the total length of input to psa_aead_update() so far is + * less than the plaintext length that was previously + * specified with psa_aead_set_lengths(). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active decryption + * operation with a nonce set), or the library has not been previously + * initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_verify(psa_aead_operation_t *operation, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length, + const uint8_t *tag, + size_t tag_length); + +/** Abort an AEAD operation. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation by calling + * psa_aead_encrypt_setup() or psa_aead_decrypt_setup() again. + * + * You may call this function any time after the operation object has + * been initialized as described in #psa_aead_operation_t. + * + * In particular, calling psa_aead_abort() after the operation has been + * terminated by a call to psa_aead_abort(), psa_aead_finish() or + * psa_aead_verify() is safe and has no effect. + * + * \param[in,out] operation Initialized AEAD operation. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_aead_abort(psa_aead_operation_t *operation); + +/**@}*/ + +/** \defgroup asymmetric Asymmetric cryptography + * @{ + */ + +/** + * \brief Sign a message with a private key. For hash-and-sign algorithms, + * this includes the hashing step. + * + * \note To perform a multi-part hash-and-sign signature algorithm, first use + * a multi-part hash operation and then pass the resulting hash to + * psa_sign_hash(). PSA_ALG_GET_HASH(\p alg) can be used to determine the + * hash algorithm to use. + * + * \param[in] key Identifier of the key to use for the operation. + * It must be an asymmetric key pair. The key must + * allow the usage #PSA_KEY_USAGE_SIGN_MESSAGE. + * \param[in] alg An asymmetric signature algorithm (PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_MESSAGE(\p alg) + * is true), that is compatible with the type of + * \p key. + * \param[in] input The input message to sign. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[out] signature Buffer where the signature is to be written. + * \param[in] signature_size Size of the \p signature buffer in bytes. This + * must be appropriate for the selected + * algorithm and key: + * - The required signature size is + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and + * bit-size respectively of key. + * - #PSA_SIGNATURE_MAX_SIZE evaluates to the + * maximum signature size of any supported + * signature algorithm. + * \param[out] signature_length On success, the number of bytes that make up + * the returned signature value. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_SIGN_MESSAGE flag, + * or it does not permit the requested algorithm. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_sign_message(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length); + +/** \brief Verify the signature of a message with a public key, using + * a hash-and-sign verification algorithm. + * + * \note To perform a multi-part hash-and-sign signature verification + * algorithm, first use a multi-part hash operation to hash the message + * and then pass the resulting hash to psa_verify_hash(). + * PSA_ALG_GET_HASH(\p alg) can be used to determine the hash algorithm + * to use. + * + * \param[in] key Identifier of the key to use for the operation. + * It must be a public key or an asymmetric key + * pair. The key must allow the usage + * #PSA_KEY_USAGE_VERIFY_MESSAGE. + * \param[in] alg An asymmetric signature algorithm (PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_MESSAGE(\p alg) + * is true), that is compatible with the type of + * \p key. + * \param[in] input The message whose signature is to be verified. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[out] signature Buffer containing the signature to verify. + * \param[in] signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_SIGN_MESSAGE flag, + * or it does not permit the requested algorithm. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed signature + * is not a valid signature. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_verify_message(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *signature, + size_t signature_length); + +/** + * \brief Sign a hash or short message with a private key. + * + * Note that to perform a hash-and-sign signature algorithm, you must + * first calculate the hash by calling psa_hash_setup(), psa_hash_update() + * and psa_hash_finish(), or alternatively by calling psa_hash_compute(). + * Then pass the resulting hash as the \p hash + * parameter to this function. You can use #PSA_ALG_SIGN_GET_HASH(\p alg) + * to determine the hash algorithm to use. + * + * \param key Identifier of the key to use for the operation. + * It must be an asymmetric key pair. The key must + * allow the usage #PSA_KEY_USAGE_SIGN_HASH. + * \param alg A signature algorithm (PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_HASH(\p alg) + * is true), that is compatible with + * the type of \p key. + * \param[in] hash The hash or message to sign. + * \param hash_length Size of the \p hash buffer in bytes. + * \param[out] signature Buffer where the signature is to be written. + * \param signature_size Size of the \p signature buffer in bytes. + * \param[out] signature_length On success, the number of bytes + * that make up the returned signature value. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_sign_hash(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *hash, + size_t hash_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length); + +/** + * \brief Verify the signature of a hash or short message using a public key. + * + * Note that to perform a hash-and-sign signature algorithm, you must + * first calculate the hash by calling psa_hash_setup(), psa_hash_update() + * and psa_hash_finish(), or alternatively by calling psa_hash_compute(). + * Then pass the resulting hash as the \p hash + * parameter to this function. You can use #PSA_ALG_SIGN_GET_HASH(\p alg) + * to determine the hash algorithm to use. + * + * \param key Identifier of the key to use for the operation. It + * must be a public key or an asymmetric key pair. The + * key must allow the usage + * #PSA_KEY_USAGE_VERIFY_HASH. + * \param alg A signature algorithm (PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_HASH(\p alg) + * is true), that is compatible with + * the type of \p key. + * \param[in] hash The hash or message whose signature is to be + * verified. + * \param hash_length Size of the \p hash buffer in bytes. + * \param[in] signature Buffer containing the signature to verify. + * \param signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * The signature is valid. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed + * signature is not a valid signature. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_verify_hash(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *hash, + size_t hash_length, + const uint8_t *signature, + size_t signature_length); + +/** + * \brief Encrypt a short message with a public key. + * + * \param key Identifier of the key to use for the operation. + * It must be a public key or an asymmetric key + * pair. It must allow the usage + * #PSA_KEY_USAGE_ENCRYPT. + * \param alg An asymmetric encryption algorithm that is + * compatible with the type of \p key. + * \param[in] input The message to encrypt. + * \param input_length Size of the \p input buffer in bytes. + * \param[in] salt A salt or label, if supported by the + * encryption algorithm. + * If the algorithm does not support a + * salt, pass \c NULL. + * If the algorithm supports an optional + * salt and you do not want to pass a salt, + * pass \c NULL. + * + * - For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is + * supported. + * \param salt_length Size of the \p salt buffer in bytes. + * If \p salt is \c NULL, pass 0. + * \param[out] output Buffer where the encrypted message is to + * be written. + * \param output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_asymmetric_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** + * \brief Decrypt a short message with a private key. + * + * \param key Identifier of the key to use for the operation. + * It must be an asymmetric key pair. It must + * allow the usage #PSA_KEY_USAGE_DECRYPT. + * \param alg An asymmetric encryption algorithm that is + * compatible with the type of \p key. + * \param[in] input The message to decrypt. + * \param input_length Size of the \p input buffer in bytes. + * \param[in] salt A salt or label, if supported by the + * encryption algorithm. + * If the algorithm does not support a + * salt, pass \c NULL. + * If the algorithm supports an optional + * salt and you do not want to pass a salt, + * pass \c NULL. + * + * - For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is + * supported. + * \param salt_length Size of the \p salt buffer in bytes. + * If \p salt is \c NULL, pass 0. + * \param[out] output Buffer where the decrypted message is to + * be written. + * \param output_size Size of the \c output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_INVALID_PADDING \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_asymmetric_decrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/**@}*/ + +/** \defgroup key_derivation Key derivation and pseudorandom generation + * @{ + */ + +/** The type of the state data structure for key derivation operations. + * + * Before calling any function on a key derivation operation object, the + * application must initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_key_derivation_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_key_derivation_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_KEY_DERIVATION_OPERATION_INIT, + * for example: + * \code + * psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; + * \endcode + * - Assign the result of the function psa_key_derivation_operation_init() + * to the structure, for example: + * \code + * psa_key_derivation_operation_t operation; + * operation = psa_key_derivation_operation_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. + */ +typedef struct psa_key_derivation_s psa_key_derivation_operation_t; + +/** \def PSA_KEY_DERIVATION_OPERATION_INIT + * + * This macro returns a suitable initializer for a key derivation operation + * object of type #psa_key_derivation_operation_t. + */ + +/** Return an initial value for a key derivation operation object. + */ +static psa_key_derivation_operation_t psa_key_derivation_operation_init(void); + +/** Set up a key derivation operation. + * + * A key derivation algorithm takes some inputs and uses them to generate + * a byte stream in a deterministic way. + * This byte stream can be used to produce keys and other + * cryptographic material. + * + * To derive a key: + * -# Start with an initialized object of type #psa_key_derivation_operation_t. + * -# Call psa_key_derivation_setup() to select the algorithm. + * -# Provide the inputs for the key derivation by calling + * psa_key_derivation_input_bytes() or psa_key_derivation_input_key() + * as appropriate. Which inputs are needed, in what order, and whether + * they may be keys and if so of what type depends on the algorithm. + * -# Optionally set the operation's maximum capacity with + * psa_key_derivation_set_capacity(). You may do this before, in the middle + * of or after providing inputs. For some algorithms, this step is mandatory + * because the output depends on the maximum capacity. + * -# To derive a key, call psa_key_derivation_output_key(). + * To derive a byte string for a different purpose, call + * psa_key_derivation_output_bytes(). + * Successive calls to these functions use successive output bytes + * calculated by the key derivation algorithm. + * -# Clean up the key derivation operation object with + * psa_key_derivation_abort(). + * + * If this function returns an error, the key derivation operation object is + * not changed. + * + * If an error occurs at any step after a call to psa_key_derivation_setup(), + * the operation will need to be reset by a call to psa_key_derivation_abort(). + * + * Implementations must reject an attempt to derive a key of size 0. + * + * \param[in,out] operation The key derivation operation object + * to set up. It must + * have been initialized but not set up yet. + * \param alg The key derivation algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_KEY_DERIVATION(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c alg is not a key derivation algorithm. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \c alg is not supported or is not a key derivation algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_setup( + psa_key_derivation_operation_t *operation, + psa_algorithm_t alg); + +/** Retrieve the current capacity of a key derivation operation. + * + * The capacity of a key derivation is the maximum number of bytes that it can + * return. When you get *N* bytes of output from a key derivation operation, + * this reduces its capacity by *N*. + * + * \param[in] operation The operation to query. + * \param[out] capacity On success, the capacity of the operation. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_get_capacity( + const psa_key_derivation_operation_t *operation, + size_t *capacity); + +/** Set the maximum capacity of a key derivation operation. + * + * The capacity of a key derivation operation is the maximum number of bytes + * that the key derivation operation can return from this point onwards. + * + * \param[in,out] operation The key derivation operation object to modify. + * \param capacity The new capacity of the operation. + * It must be less or equal to the operation's + * current capacity. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p capacity is larger than the operation's current capacity. + * In this case, the operation object remains valid and its capacity + * remains unchanged. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active), or the + * library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_set_capacity( + psa_key_derivation_operation_t *operation, + size_t capacity); + +/** Use the maximum possible capacity for a key derivation operation. + * + * Use this value as the capacity argument when setting up a key derivation + * to indicate that the operation should have the maximum possible capacity. + * The value of the maximum possible capacity depends on the key derivation + * algorithm. + */ +#define PSA_KEY_DERIVATION_UNLIMITED_CAPACITY ((size_t) (-1)) + +/** Provide an input for key derivation or key agreement. + * + * Which inputs are required and in what order depends on the algorithm. + * Refer to the documentation of each key derivation or key agreement + * algorithm for information. + * + * This function passes direct inputs, which is usually correct for + * non-secret inputs. To pass a secret input, which should be in a key + * object, call psa_key_derivation_input_key() instead of this function. + * Refer to the documentation of individual step types + * (`PSA_KEY_DERIVATION_INPUT_xxx` values of type ::psa_key_derivation_step_t) + * for more information. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_key_derivation_abort(). + * + * \param[in,out] operation The key derivation operation object to use. + * It must have been set up with + * psa_key_derivation_setup() and must not + * have produced any output yet. + * \param step Which step the input data is for. + * \param[in] data Input data to use. + * \param data_length Size of the \p data buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c step is not compatible with the operation's algorithm, or + * \c step does not allow direct inputs. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid for this input \p step, or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_input_bytes( + psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + const uint8_t *data, + size_t data_length); + +/** Provide a numeric input for key derivation or key agreement. + * + * Which inputs are required and in what order depends on the algorithm. + * However, when an algorithm requires a particular order, numeric inputs + * usually come first as they tend to be configuration parameters. + * Refer to the documentation of each key derivation or key agreement + * algorithm for information. + * + * This function is used for inputs which are fixed-size non-negative + * integers. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_key_derivation_abort(). + * + * \param[in,out] operation The key derivation operation object to use. + * It must have been set up with + * psa_key_derivation_setup() and must not + * have produced any output yet. + * \param step Which step the input data is for. + * \param[in] value The value of the numeric input. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c step is not compatible with the operation's algorithm, or + * \c step does not allow numeric inputs. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid for this input \p step, or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_input_integer( + psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + uint64_t value); + +/** Provide an input for key derivation in the form of a key. + * + * Which inputs are required and in what order depends on the algorithm. + * Refer to the documentation of each key derivation or key agreement + * algorithm for information. + * + * This function obtains input from a key object, which is usually correct for + * secret inputs or for non-secret personalization strings kept in the key + * store. To pass a non-secret parameter which is not in the key store, + * call psa_key_derivation_input_bytes() instead of this function. + * Refer to the documentation of individual step types + * (`PSA_KEY_DERIVATION_INPUT_xxx` values of type ::psa_key_derivation_step_t) + * for more information. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_key_derivation_abort(). + * + * \param[in,out] operation The key derivation operation object to use. + * It must have been set up with + * psa_key_derivation_setup() and must not + * have produced any output yet. + * \param step Which step the input data is for. + * \param key Identifier of the key. It must have an + * appropriate type for step and must allow the + * usage #PSA_KEY_USAGE_DERIVE or + * #PSA_KEY_USAGE_VERIFY_DERIVATION (see note) + * and the algorithm used by the operation. + * + * \note Once all inputs steps are completed, the operations will allow: + * - psa_key_derivation_output_bytes() if each input was either a direct input + * or a key with #PSA_KEY_USAGE_DERIVE set; + * - psa_key_derivation_output_key() if the input for step + * #PSA_KEY_DERIVATION_INPUT_SECRET or #PSA_KEY_DERIVATION_INPUT_PASSWORD + * was from a key slot with #PSA_KEY_USAGE_DERIVE and each other input was + * either a direct input or a key with #PSA_KEY_USAGE_DERIVE set; + * - psa_key_derivation_verify_bytes() if each input was either a direct input + * or a key with #PSA_KEY_USAGE_VERIFY_DERIVATION set; + * - psa_key_derivation_verify_key() under the same conditions as + * psa_key_derivation_verify_bytes(). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED + * The key allows neither #PSA_KEY_USAGE_DERIVE nor + * #PSA_KEY_USAGE_VERIFY_DERIVATION, or it doesn't allow this + * algorithm. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c step is not compatible with the operation's algorithm, or + * \c step does not allow key inputs of the given type + * or does not allow key inputs at all. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid for this input \p step, or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_input_key( + psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + mbedtls_svc_key_id_t key); + +/** Perform a key agreement and use the shared secret as input to a key + * derivation. + * + * A key agreement algorithm takes two inputs: a private key \p private_key + * a public key \p peer_key. + * The result of this function is passed as input to a key derivation. + * The output of this key derivation can be extracted by reading from the + * resulting operation to produce keys and other cryptographic material. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_key_derivation_abort(). + * + * \param[in,out] operation The key derivation operation object to use. + * It must have been set up with + * psa_key_derivation_setup() with a + * key agreement and derivation algorithm + * \c alg (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_KEY_AGREEMENT(\c alg) is true + * and #PSA_ALG_IS_RAW_KEY_AGREEMENT(\c alg) + * is false). + * The operation must be ready for an + * input of the type given by \p step. + * \param step Which step the input data is for. + * \param private_key Identifier of the private key to use. It must + * allow the usage #PSA_KEY_USAGE_DERIVE. + * \param[in] peer_key Public key of the peer. The peer key must be in the + * same format that psa_import_key() accepts for the + * public key type corresponding to the type of + * private_key. That is, this function performs the + * equivalent of + * #psa_import_key(..., + * `peer_key`, `peer_key_length`) where + * with key attributes indicating the public key + * type corresponding to the type of `private_key`. + * For example, for EC keys, this means that peer_key + * is interpreted as a point on the curve that the + * private key is on. The standard formats for public + * keys are documented in the documentation of + * psa_export_public_key(). + * \param peer_key_length Size of \p peer_key in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c private_key is not compatible with \c alg, + * or \p peer_key is not valid for \c alg or not compatible with + * \c private_key, or \c step does not allow an input resulting + * from a key agreement. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \c alg is not supported or is not a key derivation algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid for this key agreement \p step, + * or the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_key_agreement( + psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + mbedtls_svc_key_id_t private_key, + const uint8_t *peer_key, + size_t peer_key_length); + +/** Read some data from a key derivation operation. + * + * This function calculates output bytes from a key derivation algorithm and + * return those bytes. + * If you view the key derivation's output as a stream of bytes, this + * function destructively reads the requested number of bytes from the + * stream. + * The operation's capacity decreases by the number of bytes read. + * + * If this function returns an error status other than + * #PSA_ERROR_INSUFFICIENT_DATA, the operation enters an error + * state and must be aborted by calling psa_key_derivation_abort(). + * + * \param[in,out] operation The key derivation operation object to read from. + * \param[out] output Buffer where the output will be written. + * \param output_length Number of bytes to output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED + * One of the inputs was a key whose policy didn't allow + * #PSA_KEY_USAGE_DERIVE. + * \retval #PSA_ERROR_INSUFFICIENT_DATA + * The operation's capacity was less than + * \p output_length bytes. Note that in this case, + * no output is written to the output buffer. + * The operation's capacity is set to 0, thus + * subsequent calls to this function will not + * succeed, even with a smaller output buffer. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active and completed + * all required input steps), or the library has not been previously + * initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_output_bytes( + psa_key_derivation_operation_t *operation, + uint8_t *output, + size_t output_length); + +/** Derive a key from an ongoing key derivation operation. + * + * This function calculates output bytes from a key derivation algorithm + * and uses those bytes to generate a key deterministically. + * The key's location, usage policy, type and size are taken from + * \p attributes. + * + * If you view the key derivation's output as a stream of bytes, this + * function destructively reads as many bytes as required from the + * stream. + * The operation's capacity decreases by the number of bytes read. + * + * If this function returns an error status other than + * #PSA_ERROR_INSUFFICIENT_DATA, the operation enters an error + * state and must be aborted by calling psa_key_derivation_abort(). + * + * How much output is produced and consumed from the operation, and how + * the key is derived, depends on the key type and on the key size + * (denoted \c bits below): + * + * - For key types for which the key is an arbitrary sequence of bytes + * of a given size, this function is functionally equivalent to + * calling #psa_key_derivation_output_bytes + * and passing the resulting output to #psa_import_key. + * However, this function has a security benefit: + * if the implementation provides an isolation boundary then + * the key material is not exposed outside the isolation boundary. + * As a consequence, for these key types, this function always consumes + * exactly (\c bits / 8) bytes from the operation. + * The following key types defined in this specification follow this scheme: + * + * - #PSA_KEY_TYPE_AES; + * - #PSA_KEY_TYPE_ARIA; + * - #PSA_KEY_TYPE_CAMELLIA; + * - #PSA_KEY_TYPE_DERIVE; + * - #PSA_KEY_TYPE_HMAC; + * - #PSA_KEY_TYPE_PASSWORD_HASH. + * + * - For ECC keys on a Montgomery elliptic curve + * (#PSA_KEY_TYPE_ECC_KEY_PAIR(\c curve) where \c curve designates a + * Montgomery curve), this function always draws a byte string whose + * length is determined by the curve, and sets the mandatory bits + * accordingly. That is: + * + * - Curve25519 (#PSA_ECC_FAMILY_MONTGOMERY, 255 bits): draw a 32-byte + * string and process it as specified in RFC 7748 §5. + * - Curve448 (#PSA_ECC_FAMILY_MONTGOMERY, 448 bits): draw a 56-byte + * string and process it as specified in RFC 7748 §5. + * + * - For key types for which the key is represented by a single sequence of + * \c bits bits with constraints as to which bit sequences are acceptable, + * this function draws a byte string of length (\c bits / 8) bytes rounded + * up to the nearest whole number of bytes. If the resulting byte string + * is acceptable, it becomes the key, otherwise the drawn bytes are discarded. + * This process is repeated until an acceptable byte string is drawn. + * The byte string drawn from the operation is interpreted as specified + * for the output produced by psa_export_key(). + * The following key types defined in this specification follow this scheme: + * + * - #PSA_KEY_TYPE_DES. + * Force-set the parity bits, but discard forbidden weak keys. + * For 2-key and 3-key triple-DES, the three keys are generated + * successively (for example, for 3-key triple-DES, + * if the first 8 bytes specify a weak key and the next 8 bytes do not, + * discard the first 8 bytes, use the next 8 bytes as the first key, + * and continue reading output from the operation to derive the other + * two keys). + * - Finite-field Diffie-Hellman keys (#PSA_KEY_TYPE_DH_KEY_PAIR(\c group) + * where \c group designates any Diffie-Hellman group) and + * ECC keys on a Weierstrass elliptic curve + * (#PSA_KEY_TYPE_ECC_KEY_PAIR(\c curve) where \c curve designates a + * Weierstrass curve). + * For these key types, interpret the byte string as integer + * in big-endian order. Discard it if it is not in the range + * [0, *N* - 2] where *N* is the boundary of the private key domain + * (the prime *p* for Diffie-Hellman, the subprime *q* for DSA, + * or the order of the curve's base point for ECC). + * Add 1 to the resulting integer and use this as the private key *x*. + * This method allows compliance to NIST standards, specifically + * the methods titled "key-pair generation by testing candidates" + * in NIST SP 800-56A §5.6.1.1.4 for Diffie-Hellman, + * in FIPS 186-4 §B.1.2 for DSA, and + * in NIST SP 800-56A §5.6.1.2.2 or + * FIPS 186-4 §B.4.2 for elliptic curve keys. + * + * - For other key types, including #PSA_KEY_TYPE_RSA_KEY_PAIR, + * the way in which the operation output is consumed is + * implementation-defined. + * + * In all cases, the data that is read is discarded from the operation. + * The operation's capacity is decreased by the number of bytes read. + * + * For algorithms that take an input step #PSA_KEY_DERIVATION_INPUT_SECRET, + * the input to that step must be provided with psa_key_derivation_input_key(). + * Future versions of this specification may include additional restrictions + * on the derived key based on the attributes and strength of the secret key. + * + * \param[in] attributes The attributes for the new key. + * If the key type to be created is + * #PSA_KEY_TYPE_PASSWORD_HASH then the algorithm in + * the policy must be the same as in the current + * operation. + * \param[in,out] operation The key derivation operation object to read from. + * \param[out] key On success, an identifier for the newly created + * key. For persistent keys, this is the key + * identifier defined in \p attributes. + * \c 0 on failure. + * + * \retval #PSA_SUCCESS + * Success. + * If the key is persistent, the key material and the key's metadata + * have been saved to persistent storage. + * \retval #PSA_ERROR_ALREADY_EXISTS + * This is an attempt to create a persistent key, and there is + * already a persistent key with the given identifier. + * \retval #PSA_ERROR_INSUFFICIENT_DATA + * There was not enough data to create the desired key. + * Note that in this case, no output is written to the output buffer. + * The operation's capacity is set to 0, thus subsequent calls to + * this function will not succeed, even with a smaller output buffer. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The key type or key size is not supported, either by the + * implementation in general or in this particular location. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The provided key attributes are not valid for the operation. + * \retval #PSA_ERROR_NOT_PERMITTED + * The #PSA_KEY_DERIVATION_INPUT_SECRET or + * #PSA_KEY_DERIVATION_INPUT_PASSWORD input was not provided through a + * key; or one of the inputs was a key whose policy didn't allow + * #PSA_KEY_USAGE_DERIVE. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active and completed + * all required input steps), or the library has not been previously + * initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_output_key( + const psa_key_attributes_t *attributes, + psa_key_derivation_operation_t *operation, + mbedtls_svc_key_id_t *key); + +/** Compare output data from a key derivation operation to an expected value. + * + * This function calculates output bytes from a key derivation algorithm and + * compares those bytes to an expected value in constant time. + * If you view the key derivation's output as a stream of bytes, this + * function destructively reads the expected number of bytes from the + * stream before comparing them. + * The operation's capacity decreases by the number of bytes read. + * + * This is functionally equivalent to the following code: + * \code + * psa_key_derivation_output_bytes(operation, tmp, output_length); + * if (memcmp(output, tmp, output_length) != 0) + * return PSA_ERROR_INVALID_SIGNATURE; + * \endcode + * except (1) it works even if the key's policy does not allow outputting the + * bytes, and (2) the comparison will be done in constant time. + * + * If this function returns an error status other than + * #PSA_ERROR_INSUFFICIENT_DATA or #PSA_ERROR_INVALID_SIGNATURE, + * the operation enters an error state and must be aborted by calling + * psa_key_derivation_abort(). + * + * \param[in,out] operation The key derivation operation object to read from. + * \param[in] expected_output Buffer containing the expected derivation output. + * \param output_length Length of the expected output; this is also the + * number of bytes that will be read. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The output was read successfully, but it differs from the expected + * output. + * \retval #PSA_ERROR_NOT_PERMITTED + * One of the inputs was a key whose policy didn't allow + * #PSA_KEY_USAGE_VERIFY_DERIVATION. + * \retval #PSA_ERROR_INSUFFICIENT_DATA + * The operation's capacity was less than + * \p output_length bytes. Note that in this case, + * the operation's capacity is set to 0, thus + * subsequent calls to this function will not + * succeed, even with a smaller expected output. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active and completed + * all required input steps), or the library has not been previously + * initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_verify_bytes( + psa_key_derivation_operation_t *operation, + const uint8_t *expected_output, + size_t output_length); + +/** Compare output data from a key derivation operation to an expected value + * stored in a key object. + * + * This function calculates output bytes from a key derivation algorithm and + * compares those bytes to an expected value, provided as key of type + * #PSA_KEY_TYPE_PASSWORD_HASH. + * If you view the key derivation's output as a stream of bytes, this + * function destructively reads the number of bytes corresponding to the + * length of the expected value from the stream before comparing them. + * The operation's capacity decreases by the number of bytes read. + * + * This is functionally equivalent to exporting the key and calling + * psa_key_derivation_verify_bytes() on the result, except that it + * works even if the key cannot be exported. + * + * If this function returns an error status other than + * #PSA_ERROR_INSUFFICIENT_DATA or #PSA_ERROR_INVALID_SIGNATURE, + * the operation enters an error state and must be aborted by calling + * psa_key_derivation_abort(). + * + * \param[in,out] operation The key derivation operation object to read from. + * \param[in] expected A key of type #PSA_KEY_TYPE_PASSWORD_HASH + * containing the expected output. Its policy must + * include the #PSA_KEY_USAGE_VERIFY_DERIVATION flag + * and the permitted algorithm must match the + * operation. The value of this key was likely + * computed by a previous call to + * psa_key_derivation_output_key(). + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The output was read successfully, but if differs from the expected + * output. + * \retval #PSA_ERROR_INVALID_HANDLE + * The key passed as the expected value does not exist. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key passed as the expected value has an invalid type. + * \retval #PSA_ERROR_NOT_PERMITTED + * The key passed as the expected value does not allow this usage or + * this algorithm; or one of the inputs was a key whose policy didn't + * allow #PSA_KEY_USAGE_VERIFY_DERIVATION. + * \retval #PSA_ERROR_INSUFFICIENT_DATA + * The operation's capacity was less than + * the length of the expected value. In this case, + * the operation's capacity is set to 0, thus + * subsequent calls to this function will not + * succeed, even with a smaller expected output. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active and completed + * all required input steps), or the library has not been previously + * initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_verify_key( + psa_key_derivation_operation_t *operation, + psa_key_id_t expected); + +/** Abort a key derivation operation. + * + * Aborting an operation frees all associated resources except for the \c + * operation structure itself. Once aborted, the operation object can be reused + * for another operation by calling psa_key_derivation_setup() again. + * + * This function may be called at any time after the operation + * object has been initialized as described in #psa_key_derivation_operation_t. + * + * In particular, it is valid to call psa_key_derivation_abort() twice, or to + * call psa_key_derivation_abort() on an operation that has not been set up. + * + * \param[in,out] operation The operation to abort. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_key_derivation_abort( + psa_key_derivation_operation_t *operation); + +/** Perform a key agreement and return the raw shared secret. + * + * \warning The raw result of a key agreement algorithm such as finite-field + * Diffie-Hellman or elliptic curve Diffie-Hellman has biases and should + * not be used directly as key material. It should instead be passed as + * input to a key derivation algorithm. To chain a key agreement with + * a key derivation, use psa_key_derivation_key_agreement() and other + * functions from the key derivation interface. + * + * \param alg The key agreement algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_RAW_KEY_AGREEMENT(\p alg) + * is true). + * \param private_key Identifier of the private key to use. It must + * allow the usage #PSA_KEY_USAGE_DERIVE. + * \param[in] peer_key Public key of the peer. It must be + * in the same format that psa_import_key() + * accepts. The standard formats for public + * keys are documented in the documentation + * of psa_export_public_key(). + * \param peer_key_length Size of \p peer_key in bytes. + * \param[out] output Buffer where the decrypted message is to + * be written. + * \param output_size Size of the \c output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p alg is not a key agreement algorithm, or + * \p private_key is not compatible with \p alg, + * or \p peer_key is not valid for \p alg or not compatible with + * \p private_key. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p output_size is too small + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not a supported key agreement algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_raw_key_agreement(psa_algorithm_t alg, + mbedtls_svc_key_id_t private_key, + const uint8_t *peer_key, + size_t peer_key_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/**@}*/ + +/** \defgroup random Random generation + * @{ + */ + +/** + * \brief Generate random bytes. + * + * \warning This function **can** fail! Callers MUST check the return status + * and MUST NOT use the content of the output buffer if the return + * status is not #PSA_SUCCESS. + * + * \note To generate a key, use psa_generate_key() instead. + * + * \param[out] output Output buffer for the generated data. + * \param output_size Number of bytes to generate and output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_generate_random(uint8_t *output, + size_t output_size); + +/** + * \brief Generate a key or key pair. + * + * The key is generated randomly. + * Its location, usage policy, type and size are taken from \p attributes. + * + * Implementations must reject an attempt to generate a key of size 0. + * + * The following type-specific considerations apply: + * - For RSA keys (#PSA_KEY_TYPE_RSA_KEY_PAIR), + * the public exponent is 65537. + * The modulus is a product of two probabilistic primes + * between 2^{n-1} and 2^n where n is the bit size specified in the + * attributes. + * + * \param[in] attributes The attributes for the new key. + * \param[out] key On success, an identifier for the newly created + * key. For persistent keys, this is the key + * identifier defined in \p attributes. + * \c 0 on failure. + * + * \retval #PSA_SUCCESS + * Success. + * If the key is persistent, the key material and the key's metadata + * have been saved to persistent storage. + * \retval #PSA_ERROR_ALREADY_EXISTS + * This is an attempt to create a persistent key, and there is + * already a persistent key with the given identifier. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_generate_key(const psa_key_attributes_t *attributes, + mbedtls_svc_key_id_t *key); + +/**@}*/ + +/** \defgroup interruptible_hash Interruptible sign/verify hash + * @{ + */ + +/** The type of the state data structure for interruptible hash + * signing operations. + * + * Before calling any function on a sign hash operation object, the + * application must initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_sign_hash_interruptible_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_sign_hash_interruptible_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer + * #PSA_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT, for example: + * \code + * psa_sign_hash_interruptible_operation_t operation = + * PSA_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT; + * \endcode + * - Assign the result of the function + * psa_sign_hash_interruptible_operation_init() to the structure, for + * example: + * \code + * psa_sign_hash_interruptible_operation_t operation; + * operation = psa_sign_hash_interruptible_operation_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. */ +typedef struct psa_sign_hash_interruptible_operation_s psa_sign_hash_interruptible_operation_t; + +/** The type of the state data structure for interruptible hash + * verification operations. + * + * Before calling any function on a sign hash operation object, the + * application must initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_verify_hash_interruptible_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_verify_hash_interruptible_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer + * #PSA_VERIFY_HASH_INTERRUPTIBLE_OPERATION_INIT, for example: + * \code + * psa_verify_hash_interruptible_operation_t operation = + * PSA_VERIFY_HASH_INTERRUPTIBLE_OPERATION_INIT; + * \endcode + * - Assign the result of the function + * psa_verify_hash_interruptible_operation_init() to the structure, for + * example: + * \code + * psa_verify_hash_interruptible_operation_t operation; + * operation = psa_verify_hash_interruptible_operation_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. */ +typedef struct psa_verify_hash_interruptible_operation_s psa_verify_hash_interruptible_operation_t; + +/** + * \brief Set the maximum number of ops allowed to be + * executed by an interruptible function in a + * single call. + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * \note The time taken to execute a single op is + * implementation specific and depends on + * software, hardware, the algorithm, key type and + * curve chosen. Even within a single operation, + * successive ops can take differing amounts of + * time. The only guarantee is that lower values + * for \p max_ops means functions will block for a + * lesser maximum amount of time. The functions + * \c psa_sign_interruptible_get_num_ops() and + * \c psa_verify_interruptible_get_num_ops() are + * provided to help with tuning this value. + * + * \note This value defaults to + * #PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED, which + * means the whole operation will be done in one + * go, regardless of the number of ops required. + * + * \note If more ops are needed to complete a + * computation, #PSA_OPERATION_INCOMPLETE will be + * returned by the function performing the + * computation. It is then the caller's + * responsibility to either call again with the + * same operation context until it returns 0 or an + * error code; or to call the relevant abort + * function if the answer is no longer required. + * + * \note The interpretation of \p max_ops is also + * implementation defined. On a hard real time + * system, this can indicate a hard deadline, as a + * real-time system needs a guarantee of not + * spending more than X time, however care must be + * taken in such an implementation to avoid the + * situation whereby calls just return, not being + * able to do any actual work within the allotted + * time. On a non-real-time system, the + * implementation can be more relaxed, but again + * whether this number should be interpreted as as + * hard or soft limit or even whether a less than + * or equals as regards to ops executed in a + * single call is implementation defined. + * + * \note For keys in local storage when no accelerator + * driver applies, please see also the + * documentation for \c mbedtls_ecp_set_max_ops(), + * which is the internal implementation in these + * cases. + * + * \warning With implementations that interpret this number + * as a hard limit, setting this number too small + * may result in an infinite loop, whereby each + * call results in immediate return with no ops + * done (as there is not enough time to execute + * any), and thus no result will ever be achieved. + * + * \note This only applies to functions whose + * documentation mentions they may return + * #PSA_OPERATION_INCOMPLETE. + * + * \param max_ops The maximum number of ops to be executed in a + * single call. This can be a number from 0 to + * #PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED, where 0 + * is the least amount of work done per call. + */ +void psa_interruptible_set_max_ops(uint32_t max_ops); + +/** + * \brief Get the maximum number of ops allowed to be + * executed by an interruptible function in a + * single call. This will return the last + * value set by + * \c psa_interruptible_set_max_ops() or + * #PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED if + * that function has never been called. + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * \return Maximum number of ops allowed to be + * executed by an interruptible function in a + * single call. + */ +uint32_t psa_interruptible_get_max_ops(void); + +/** + * \brief Get the number of ops that a hash signing + * operation has taken so far. If the operation + * has completed, then this will represent the + * number of ops required for the entire + * operation. After initialization or calling + * \c psa_sign_hash_interruptible_abort() on + * the operation, a value of 0 will be returned. + * + * \note This interface is guaranteed re-entrant and + * thus may be called from driver code. + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * This is a helper provided to help you tune the + * value passed to \c + * psa_interruptible_set_max_ops(). + * + * \param operation The \c psa_sign_hash_interruptible_operation_t + * to use. This must be initialized first. + * + * \return Number of ops that the operation has taken so + * far. + */ +uint32_t psa_sign_hash_get_num_ops( + const psa_sign_hash_interruptible_operation_t *operation); + +/** + * \brief Get the number of ops that a hash verification + * operation has taken so far. If the operation + * has completed, then this will represent the + * number of ops required for the entire + * operation. After initialization or calling \c + * psa_verify_hash_interruptible_abort() on the + * operation, a value of 0 will be returned. + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * This is a helper provided to help you tune the + * value passed to \c + * psa_interruptible_set_max_ops(). + * + * \param operation The \c + * psa_verify_hash_interruptible_operation_t to + * use. This must be initialized first. + * + * \return Number of ops that the operation has taken so + * far. + */ +uint32_t psa_verify_hash_get_num_ops( + const psa_verify_hash_interruptible_operation_t *operation); + +/** + * \brief Start signing a hash or short message with a + * private key, in an interruptible manner. + * + * \see \c psa_sign_hash_complete() + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * \note This function combined with \c + * psa_sign_hash_complete() is equivalent to + * \c psa_sign_hash() but + * \c psa_sign_hash_complete() can return early and + * resume according to the limit set with \c + * psa_interruptible_set_max_ops() to reduce the + * maximum time spent in a function call. + * + * \note Users should call \c psa_sign_hash_complete() + * repeatedly on the same context after a + * successful call to this function until \c + * psa_sign_hash_complete() either returns 0 or an + * error. \c psa_sign_hash_complete() will return + * #PSA_OPERATION_INCOMPLETE if there is more work + * to do. Alternatively users can call + * \c psa_sign_hash_abort() at any point if they no + * longer want the result. + * + * \note If this function returns an error status, the + * operation enters an error state and must be + * aborted by calling \c psa_sign_hash_abort(). + * + * \param[in, out] operation The \c psa_sign_hash_interruptible_operation_t + * to use. This must be initialized first. + * + * \param key Identifier of the key to use for the operation. + * It must be an asymmetric key pair. The key must + * allow the usage #PSA_KEY_USAGE_SIGN_HASH. + * \param alg A signature algorithm (\c PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_HASH(\p alg) + * is true), that is compatible with + * the type of \p key. + * \param[in] hash The hash or message to sign. + * \param hash_length Size of the \p hash buffer in bytes. + * + * \retval #PSA_SUCCESS + * The operation started successfully - call \c psa_sign_hash_complete() + * with the same context to complete the operation + * + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_SIGN_HASH flag, or it does + * not permit the requested algorithm. + * \retval #PSA_ERROR_BAD_STATE + * An operation has previously been started on this context, and is + * still in progress. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_sign_hash_start( + psa_sign_hash_interruptible_operation_t *operation, + mbedtls_svc_key_id_t key, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length); + +/** + * \brief Continue and eventually complete the action of + * signing a hash or short message with a private + * key, in an interruptible manner. + * + * \see \c psa_sign_hash_start() + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * \note This function combined with \c + * psa_sign_hash_start() is equivalent to + * \c psa_sign_hash() but this function can return + * early and resume according to the limit set with + * \c psa_interruptible_set_max_ops() to reduce the + * maximum time spent in a function call. + * + * \note Users should call this function on the same + * operation object repeatedly until it either + * returns 0 or an error. This function will return + * #PSA_OPERATION_INCOMPLETE if there is more work + * to do. Alternatively users can call + * \c psa_sign_hash_abort() at any point if they no + * longer want the result. + * + * \note When this function returns successfully, the + * operation becomes inactive. If this function + * returns an error status, the operation enters an + * error state and must be aborted by calling + * \c psa_sign_hash_abort(). + * + * \param[in, out] operation The \c psa_sign_hash_interruptible_operation_t + * to use. This must be initialized first, and have + * had \c psa_sign_hash_start() called with it + * first. + * + * \param[out] signature Buffer where the signature is to be written. + * \param signature_size Size of the \p signature buffer in bytes. This + * must be appropriate for the selected + * algorithm and key: + * - The required signature size is + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c + * key_bits, \c alg) where \c key_type and \c + * key_bits are the type and bit-size + * respectively of key. + * - #PSA_SIGNATURE_MAX_SIZE evaluates to the + * maximum signature size of any supported + * signature algorithm. + * \param[out] signature_length On success, the number of bytes that make up + * the returned signature value. + * + * \retval #PSA_SUCCESS + * Operation completed successfully + * + * \retval #PSA_OPERATION_INCOMPLETE + * Operation was interrupted due to the setting of \c + * psa_interruptible_set_max_ops(). There is still work to be done. + * Call this function again with the same operation object. + * + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * + * \retval #PSA_ERROR_BAD_STATE + * An operation was not previously started on this context via + * \c psa_sign_hash_start(). + * + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has either not been previously initialized by + * psa_crypto_init() or you did not previously call + * psa_sign_hash_start() with this operation object. It is + * implementation-dependent whether a failure to initialize results in + * this error code. + */ +psa_status_t psa_sign_hash_complete( + psa_sign_hash_interruptible_operation_t *operation, + uint8_t *signature, size_t signature_size, + size_t *signature_length); + +/** + * \brief Abort a sign hash operation. + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * \note This function is the only function that clears + * the number of ops completed as part of the + * operation. Please ensure you copy this value via + * \c psa_sign_hash_get_num_ops() if required + * before calling. + * + * \note Aborting an operation frees all associated + * resources except for the \p operation structure + * itself. Once aborted, the operation object can + * be reused for another operation by calling \c + * psa_sign_hash_start() again. + * + * \note You may call this function any time after the + * operation object has been initialized. In + * particular, calling \c psa_sign_hash_abort() + * after the operation has already been terminated + * by a call to \c psa_sign_hash_abort() or + * psa_sign_hash_complete() is safe. + * + * \param[in,out] operation Initialized sign hash operation. + * + * \retval #PSA_SUCCESS + * The operation was aborted successfully. + * + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_sign_hash_abort( + psa_sign_hash_interruptible_operation_t *operation); + +/** + * \brief Start reading and verifying a hash or short + * message, in an interruptible manner. + * + * \see \c psa_verify_hash_complete() + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * \note This function combined with \c + * psa_verify_hash_complete() is equivalent to + * \c psa_verify_hash() but \c + * psa_verify_hash_complete() can return early and + * resume according to the limit set with \c + * psa_interruptible_set_max_ops() to reduce the + * maximum time spent in a function. + * + * \note Users should call \c psa_verify_hash_complete() + * repeatedly on the same operation object after a + * successful call to this function until \c + * psa_verify_hash_complete() either returns 0 or + * an error. \c psa_verify_hash_complete() will + * return #PSA_OPERATION_INCOMPLETE if there is + * more work to do. Alternatively users can call + * \c psa_verify_hash_abort() at any point if they + * no longer want the result. + * + * \note If this function returns an error status, the + * operation enters an error state and must be + * aborted by calling \c psa_verify_hash_abort(). + * + * \param[in, out] operation The \c psa_verify_hash_interruptible_operation_t + * to use. This must be initialized first. + * + * \param key Identifier of the key to use for the operation. + * The key must allow the usage + * #PSA_KEY_USAGE_VERIFY_HASH. + * \param alg A signature algorithm (\c PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_HASH(\p alg) + * is true), that is compatible with + * the type of \p key. + * \param[in] hash The hash whose signature is to be verified. + * \param hash_length Size of the \p hash buffer in bytes. + * \param[in] signature Buffer containing the signature to verify. + * \param signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * The operation started successfully - please call \c + * psa_verify_hash_complete() with the same context to complete the + * operation. + * + * \retval #PSA_ERROR_BAD_STATE + * Another operation has already been started on this context, and is + * still in progress. + * + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_VERIFY_HASH flag, or it does + * not permit the requested algorithm. + * + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_verify_hash_start( + psa_verify_hash_interruptible_operation_t *operation, + mbedtls_svc_key_id_t key, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length); + +/** + * \brief Continue and eventually complete the action of + * reading and verifying a hash or short message + * signed with a private key, in an interruptible + * manner. + * + * \see \c psa_verify_hash_start() + * + * \warning This is a beta API, and thus subject to change + * at any point. It is not bound by the usual + * interface stability promises. + * + * \note This function combined with \c + * psa_verify_hash_start() is equivalent to + * \c psa_verify_hash() but this function can + * return early and resume according to the limit + * set with \c psa_interruptible_set_max_ops() to + * reduce the maximum time spent in a function + * call. + * + * \note Users should call this function on the same + * operation object repeatedly until it either + * returns 0 or an error. This function will return + * #PSA_OPERATION_INCOMPLETE if there is more work + * to do. Alternatively users can call + * \c psa_verify_hash_abort() at any point if they + * no longer want the result. + * + * \note When this function returns successfully, the + * operation becomes inactive. If this function + * returns an error status, the operation enters an + * error state and must be aborted by calling + * \c psa_verify_hash_abort(). + * + * \param[in, out] operation The \c psa_verify_hash_interruptible_operation_t + * to use. This must be initialized first, and have + * had \c psa_verify_hash_start() called with it + * first. + * + * \retval #PSA_SUCCESS + * Operation completed successfully, and the passed signature is valid. + * + * \retval #PSA_OPERATION_INCOMPLETE + * Operation was interrupted due to the setting of \c + * psa_interruptible_set_max_ops(). There is still work to be done. + * Call this function again with the same operation object. + * + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed + * signature is not a valid signature. + * \retval #PSA_ERROR_BAD_STATE + * An operation was not previously started on this context via + * \c psa_verify_hash_start(). + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has either not been previously initialized by + * psa_crypto_init() or you did not previously call + * psa_verify_hash_start() on this object. It is + * implementation-dependent whether a failure to initialize results in + * this error code. + */ +psa_status_t psa_verify_hash_complete( + psa_verify_hash_interruptible_operation_t *operation); + +/** + * \brief Abort a verify hash operation. + * + * \warning This is a beta API, and thus subject to change at + * any point. It is not bound by the usual interface + * stability promises. + * + * \note This function is the only function that clears the + * number of ops completed as part of the operation. + * Please ensure you copy this value via + * \c psa_verify_hash_get_num_ops() if required + * before calling. + * + * \note Aborting an operation frees all associated + * resources except for the operation structure + * itself. Once aborted, the operation object can be + * reused for another operation by calling \c + * psa_verify_hash_start() again. + * + * \note You may call this function any time after the + * operation object has been initialized. + * In particular, calling \c psa_verify_hash_abort() + * after the operation has already been terminated by + * a call to \c psa_verify_hash_abort() or + * psa_verify_hash_complete() is safe. + * + * \param[in,out] operation Initialized verify hash operation. + * + * \retval #PSA_SUCCESS + * The operation was aborted successfully. + * + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_verify_hash_abort( + psa_verify_hash_interruptible_operation_t *operation); + + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +/* The file "crypto_sizes.h" contains definitions for size calculation + * macros whose definitions are implementation-specific. */ +#include "crypto_sizes.h" + +/* The file "crypto_struct.h" contains definitions for + * implementation-specific structs that are declared above. */ +#if defined(MBEDTLS_PSA_CRYPTO_STRUCT_FILE) +#include MBEDTLS_PSA_CRYPTO_STRUCT_FILE +#else +#include "crypto_struct.h" +#endif + +/* The file "crypto_extra.h" contains vendor-specific definitions. This + * can include vendor-defined algorithms, extra functions, etc. */ +#include "crypto_extra.h" + +#endif /* PSA_CRYPTO_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_builtin_composites.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_builtin_composites.h new file mode 100644 index 00000000..932c5036 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_builtin_composites.h @@ -0,0 +1,219 @@ +/* + * Context structure declaration of the Mbed TLS software-based PSA drivers + * called through the PSA Crypto driver dispatch layer. + * This file contains the context structures of those algorithms which need to + * rely on other algorithms, i.e. are 'composite' algorithms. + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * \note This header and its content is not part of the Mbed TLS API and + * applications must not depend on it. Its main purpose is to define the + * multi-part state objects of the Mbed TLS software-based PSA drivers. The + * definition of these objects are then used by crypto_struct.h to define the + * implementation-defined types of PSA multi-part state objects. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_BUILTIN_COMPOSITES_H +#define PSA_CRYPTO_BUILTIN_COMPOSITES_H +#include "mbedtls/private_access.h" + +#include + +/* + * MAC multi-part operation definitions. + */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) +#define MBEDTLS_PSA_BUILTIN_MAC +#endif + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) || defined(PSA_CRYPTO_DRIVER_TEST) +typedef struct { + /** The HMAC algorithm in use */ + psa_algorithm_t MBEDTLS_PRIVATE(alg); + /** The hash context. */ + struct psa_hash_operation_s hash_ctx; + /** The HMAC part of the context. */ + uint8_t MBEDTLS_PRIVATE(opad)[PSA_HMAC_MAX_HASH_BLOCK_SIZE]; +} mbedtls_psa_hmac_operation_t; + +#define MBEDTLS_PSA_HMAC_OPERATION_INIT { 0, PSA_HASH_OPERATION_INIT, { 0 } } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ + +#include "mbedtls/cmac.h" + +typedef struct { + psa_algorithm_t MBEDTLS_PRIVATE(alg); + union { + unsigned MBEDTLS_PRIVATE(dummy); /* Make the union non-empty even with no supported algorithms. */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) || defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_psa_hmac_operation_t MBEDTLS_PRIVATE(hmac); +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) || defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_cipher_context_t MBEDTLS_PRIVATE(cmac); +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */ + } MBEDTLS_PRIVATE(ctx); +} mbedtls_psa_mac_operation_t; + +#define MBEDTLS_PSA_MAC_OPERATION_INIT { 0, { 0 } } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) +#define MBEDTLS_PSA_BUILTIN_AEAD 1 +#endif + +/* Context structure for the Mbed TLS AEAD implementation. */ +typedef struct { + psa_algorithm_t MBEDTLS_PRIVATE(alg); + psa_key_type_t MBEDTLS_PRIVATE(key_type); + + unsigned int MBEDTLS_PRIVATE(is_encrypt) : 1; + + uint8_t MBEDTLS_PRIVATE(tag_length); + + union { + unsigned dummy; /* Enable easier initializing of the union. */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + mbedtls_ccm_context MBEDTLS_PRIVATE(ccm); +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + mbedtls_gcm_context MBEDTLS_PRIVATE(gcm); +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + mbedtls_chachapoly_context MBEDTLS_PRIVATE(chachapoly); +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + + } ctx; + +} mbedtls_psa_aead_operation_t; + +#define MBEDTLS_PSA_AEAD_OPERATION_INIT { 0, 0, 0, 0, { 0 } } + +#include "mbedtls/ecdsa.h" + +/* Context structure for the Mbed TLS interruptible sign hash implementation. */ +typedef struct { +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + mbedtls_ecdsa_context *MBEDTLS_PRIVATE(ctx); + mbedtls_ecdsa_restart_ctx MBEDTLS_PRIVATE(restart_ctx); + + uint32_t MBEDTLS_PRIVATE(num_ops); + + size_t MBEDTLS_PRIVATE(coordinate_bytes); + psa_algorithm_t MBEDTLS_PRIVATE(alg); + mbedtls_md_type_t MBEDTLS_PRIVATE(md_alg); + uint8_t MBEDTLS_PRIVATE(hash)[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)]; + size_t MBEDTLS_PRIVATE(hash_length); + +#else + /* Make the struct non-empty if algs not supported. */ + unsigned MBEDTLS_PRIVATE(dummy); + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} mbedtls_psa_sign_hash_interruptible_operation_t; + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) +#define MBEDTLS_PSA_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT { { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 } +#else +#define MBEDTLS_PSA_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT { 0 } +#endif + +/* Context structure for the Mbed TLS interruptible verify hash + * implementation.*/ +typedef struct { +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + mbedtls_ecdsa_context *MBEDTLS_PRIVATE(ctx); + mbedtls_ecdsa_restart_ctx MBEDTLS_PRIVATE(restart_ctx); + + uint32_t MBEDTLS_PRIVATE(num_ops); + + uint8_t MBEDTLS_PRIVATE(hash)[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)]; + size_t MBEDTLS_PRIVATE(hash_length); + + mbedtls_mpi MBEDTLS_PRIVATE(r); + mbedtls_mpi MBEDTLS_PRIVATE(s); + +#else + /* Make the struct non-empty if algs not supported. */ + unsigned MBEDTLS_PRIVATE(dummy); + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ + +} mbedtls_psa_verify_hash_interruptible_operation_t; + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) +#define MBEDTLS_VERIFY_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT { { 0 }, { 0 }, 0, 0, 0, 0, { 0 }, \ + { 0 } } +#else +#define MBEDTLS_VERIFY_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT { 0 } +#endif + + +/* EC-JPAKE operation definitions */ + +#include "mbedtls/ecjpake.h" + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) +#define MBEDTLS_PSA_BUILTIN_PAKE 1 +#endif + +/* Note: the format for mbedtls_ecjpake_read/write function has an extra + * length byte for each step, plus an extra 3 bytes for ECParameters in the + * server's 2nd round. */ +#define MBEDTLS_PSA_JPAKE_BUFFER_SIZE ((3 + 1 + 65 + 1 + 65 + 1 + 32) * 2) + +typedef struct { + psa_algorithm_t MBEDTLS_PRIVATE(alg); + + uint8_t *MBEDTLS_PRIVATE(password); + size_t MBEDTLS_PRIVATE(password_len); +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + uint8_t MBEDTLS_PRIVATE(role); + uint8_t MBEDTLS_PRIVATE(buffer[MBEDTLS_PSA_JPAKE_BUFFER_SIZE]); + size_t MBEDTLS_PRIVATE(buffer_length); + size_t MBEDTLS_PRIVATE(buffer_offset); +#endif + /* Context structure for the Mbed TLS EC-JPAKE implementation. */ + union { + unsigned int MBEDTLS_PRIVATE(dummy); +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + mbedtls_ecjpake_context MBEDTLS_PRIVATE(jpake); +#endif + } MBEDTLS_PRIVATE(ctx); + +} mbedtls_psa_pake_operation_t; + +#define MBEDTLS_PSA_PAKE_OPERATION_INIT { { 0 } } + +#endif /* PSA_CRYPTO_BUILTIN_COMPOSITES_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_builtin_primitives.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_builtin_primitives.h new file mode 100644 index 00000000..c76bc781 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_builtin_primitives.h @@ -0,0 +1,114 @@ +/* + * Context structure declaration of the Mbed TLS software-based PSA drivers + * called through the PSA Crypto driver dispatch layer. + * This file contains the context structures of those algorithms which do not + * rely on other algorithms, i.e. are 'primitive' algorithms. + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * \note This header and its content is not part of the Mbed TLS API and + * applications must not depend on it. Its main purpose is to define the + * multi-part state objects of the Mbed TLS software-based PSA drivers. The + * definition of these objects are then used by crypto_struct.h to define the + * implementation-defined types of PSA multi-part state objects. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_BUILTIN_PRIMITIVES_H +#define PSA_CRYPTO_BUILTIN_PRIMITIVES_H +#include "mbedtls/private_access.h" + +#include + +/* + * Hash multi-part operation definitions. + */ + +#include "mbedtls/md5.h" +#include "mbedtls/ripemd160.h" +#include "mbedtls/sha1.h" +#include "mbedtls/sha256.h" +#include "mbedtls/sha512.h" + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) +#define MBEDTLS_PSA_BUILTIN_HASH +#endif + +typedef struct { + psa_algorithm_t MBEDTLS_PRIVATE(alg); + union { + unsigned dummy; /* Make the union non-empty even with no supported algorithms. */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) + mbedtls_md5_context md5; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) + mbedtls_ripemd160_context ripemd160; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) + mbedtls_sha1_context sha1; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) + mbedtls_sha256_context sha256; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) + mbedtls_sha512_context sha512; +#endif + } MBEDTLS_PRIVATE(ctx); +} mbedtls_psa_hash_operation_t; + +#define MBEDTLS_PSA_HASH_OPERATION_INIT { 0, { 0 } } + +/* + * Cipher multi-part operation definitions. + */ + +#include "mbedtls/cipher.h" + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CTR) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CFB) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_OFB) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7) +#define MBEDTLS_PSA_BUILTIN_CIPHER 1 +#endif + +typedef struct { + /* Context structure for the Mbed TLS cipher implementation. */ + psa_algorithm_t MBEDTLS_PRIVATE(alg); + uint8_t MBEDTLS_PRIVATE(iv_length); + uint8_t MBEDTLS_PRIVATE(block_length); + union { + unsigned int MBEDTLS_PRIVATE(dummy); + mbedtls_cipher_context_t MBEDTLS_PRIVATE(cipher); + } MBEDTLS_PRIVATE(ctx); +} mbedtls_psa_cipher_operation_t; + +#define MBEDTLS_PSA_CIPHER_OPERATION_INIT { 0, 0, 0, { 0 } } + +#endif /* PSA_CRYPTO_BUILTIN_PRIMITIVES_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_compat.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_compat.h new file mode 100644 index 00000000..3544f963 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_compat.h @@ -0,0 +1,165 @@ +/** + * \file psa/crypto_compat.h + * + * \brief PSA cryptography module: Backward compatibility aliases + * + * This header declares alternative names for macro and functions. + * New application code should not use these names. + * These names may be removed in a future version of Mbed Crypto. + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_COMPAT_H +#define PSA_CRYPTO_COMPAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * To support both openless APIs and psa_open_key() temporarily, define + * psa_key_handle_t to be equal to mbedtls_svc_key_id_t. Do not mark the + * type and its utility macros and functions deprecated yet. This will be done + * in a subsequent phase. + */ +typedef mbedtls_svc_key_id_t psa_key_handle_t; + +#define PSA_KEY_HANDLE_INIT MBEDTLS_SVC_KEY_ID_INIT + +/** Check whether a handle is null. + * + * \param handle Handle + * + * \return Non-zero if the handle is null, zero otherwise. + */ +static inline int psa_key_handle_is_null(psa_key_handle_t handle) +{ + return mbedtls_svc_key_id_is_null(handle); +} + +/** Open a handle to an existing persistent key. + * + * Open a handle to a persistent key. A key is persistent if it was created + * with a lifetime other than #PSA_KEY_LIFETIME_VOLATILE. A persistent key + * always has a nonzero key identifier, set with psa_set_key_id() when + * creating the key. Implementations may provide additional pre-provisioned + * keys that can be opened with psa_open_key(). Such keys have an application + * key identifier in the vendor range, as documented in the description of + * #psa_key_id_t. + * + * The application must eventually close the handle with psa_close_key() or + * psa_destroy_key() to release associated resources. If the application dies + * without calling one of these functions, the implementation should perform + * the equivalent of a call to psa_close_key(). + * + * Some implementations permit an application to open the same key multiple + * times. If this is successful, each call to psa_open_key() will return a + * different key handle. + * + * \note This API is not part of the PSA Cryptography API Release 1.0.0 + * specification. It was defined in the 1.0 Beta 3 version of the + * specification but was removed in the 1.0.0 released version. This API is + * kept for the time being to not break applications relying on it. It is not + * deprecated yet but will be in the near future. + * + * \note Applications that rely on opening a key multiple times will not be + * portable to implementations that only permit a single key handle to be + * opened. See also :ref:\`key-handles\`. + * + * + * \param key The persistent identifier of the key. + * \param[out] handle On success, a handle to the key. + * + * \retval #PSA_SUCCESS + * Success. The application can now use the value of `*handle` + * to access the key. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * The implementation does not have sufficient resources to open the + * key. This can be due to reaching an implementation limit on the + * number of open keys, the number of open key handles, or available + * memory. + * \retval #PSA_ERROR_DOES_NOT_EXIST + * There is no persistent key with key identifier \p key. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p key is not a valid persistent key identifier. + * \retval #PSA_ERROR_NOT_PERMITTED + * The specified key exists, but the application does not have the + * permission to access it. Note that this specification does not + * define any way to create such a key, but it may be possible + * through implementation-specific means. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_open_key(mbedtls_svc_key_id_t key, + psa_key_handle_t *handle); + +/** Close a key handle. + * + * If the handle designates a volatile key, this will destroy the key material + * and free all associated resources, just like psa_destroy_key(). + * + * If this is the last open handle to a persistent key, then closing the handle + * will free all resources associated with the key in volatile memory. The key + * data in persistent storage is not affected and can be opened again later + * with a call to psa_open_key(). + * + * Closing the key handle makes the handle invalid, and the key handle + * must not be used again by the application. + * + * \note This API is not part of the PSA Cryptography API Release 1.0.0 + * specification. It was defined in the 1.0 Beta 3 version of the + * specification but was removed in the 1.0.0 released version. This API is + * kept for the time being to not break applications relying on it. It is not + * deprecated yet but will be in the near future. + * + * \note If the key handle was used to set up an active + * :ref:\`multipart operation \`, then closing the + * key handle can cause the multipart operation to fail. Applications should + * maintain the key handle until after the multipart operation has finished. + * + * \param handle The key handle to close. + * If this is \c 0, do nothing and return \c PSA_SUCCESS. + * + * \retval #PSA_SUCCESS + * \p handle was a valid handle or \c 0. It is now closed. + * \retval #PSA_ERROR_INVALID_HANDLE + * \p handle is not a valid handle nor \c 0. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_close_key(psa_key_handle_t handle); + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_CRYPTO_COMPAT_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_config.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_config.h new file mode 100644 index 00000000..e68fac8b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_config.h @@ -0,0 +1,133 @@ +/** + * \file psa/crypto_config.h + * \brief PSA crypto configuration options (set of defines) + * + */ +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) +/** + * When #MBEDTLS_PSA_CRYPTO_CONFIG is enabled in mbedtls_config.h, + * this file determines which cryptographic mechanisms are enabled + * through the PSA Cryptography API (\c psa_xxx() functions). + * + * To enable a cryptographic mechanism, uncomment the definition of + * the corresponding \c PSA_WANT_xxx preprocessor symbol. + * To disable a cryptographic mechanism, comment out the definition of + * the corresponding \c PSA_WANT_xxx preprocessor symbol. + * The names of cryptographic mechanisms correspond to values + * defined in psa/crypto_values.h, with the prefix \c PSA_WANT_ instead + * of \c PSA_. + * + * Note that many cryptographic mechanisms involve two symbols: one for + * the key type (\c PSA_WANT_KEY_TYPE_xxx) and one for the algorithm + * (\c PSA_WANT_ALG_xxx). Mechanisms with additional parameters may involve + * additional symbols. + */ +#else +/** + * When \c MBEDTLS_PSA_CRYPTO_CONFIG is disabled in mbedtls_config.h, + * this file is not used, and cryptographic mechanisms are supported + * through the PSA API if and only if they are supported through the + * mbedtls_xxx API. + */ +#endif +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_CONFIG_H +#define PSA_CRYPTO_CONFIG_H + +/* + * CBC-MAC is not yet supported via the PSA API in Mbed TLS. + */ +//#define PSA_WANT_ALG_CBC_MAC 1 +#define PSA_WANT_ALG_CBC_NO_PADDING 1 +#define PSA_WANT_ALG_CBC_PKCS7 1 +#define PSA_WANT_ALG_CCM 1 +#define PSA_WANT_ALG_CCM_STAR_NO_TAG 1 +#define PSA_WANT_ALG_CMAC 1 +#define PSA_WANT_ALG_CFB 1 +#define PSA_WANT_ALG_CHACHA20_POLY1305 1 +#define PSA_WANT_ALG_CTR 1 +#define PSA_WANT_ALG_DETERMINISTIC_ECDSA 1 +#define PSA_WANT_ALG_ECB_NO_PADDING 1 +#define PSA_WANT_ALG_ECDH 1 +#define PSA_WANT_ALG_ECDSA 1 +#define PSA_WANT_ALG_JPAKE 1 +#define PSA_WANT_ALG_GCM 1 +#define PSA_WANT_ALG_HKDF 1 +#define PSA_WANT_ALG_HKDF_EXTRACT 1 +#define PSA_WANT_ALG_HKDF_EXPAND 1 +#define PSA_WANT_ALG_HMAC 1 +#define PSA_WANT_ALG_MD5 1 +#define PSA_WANT_ALG_OFB 1 +/* PBKDF2-HMAC is not yet supported via the PSA API in Mbed TLS. + * Note: when adding support, also adjust include/mbedtls/config_psa.h */ +//#define PSA_WANT_ALG_PBKDF2_HMAC 1 +#define PSA_WANT_ALG_RIPEMD160 1 +#define PSA_WANT_ALG_RSA_OAEP 1 +#define PSA_WANT_ALG_RSA_PKCS1V15_CRYPT 1 +#define PSA_WANT_ALG_RSA_PKCS1V15_SIGN 1 +#define PSA_WANT_ALG_RSA_PSS 1 +#define PSA_WANT_ALG_SHA_1 1 +#define PSA_WANT_ALG_SHA_224 1 +#define PSA_WANT_ALG_SHA_256 1 +#define PSA_WANT_ALG_SHA_384 1 +#define PSA_WANT_ALG_SHA_512 1 +#define PSA_WANT_ALG_STREAM_CIPHER 1 +#define PSA_WANT_ALG_TLS12_PRF 1 +#define PSA_WANT_ALG_TLS12_PSK_TO_MS 1 +#define PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS 1 + +/* PBKDF2-HMAC is not yet supported via the PSA API in Mbed TLS. + * Note: when adding support, also adjust include/mbedtls/config_psa.h */ +//#define PSA_WANT_ALG_XTS 1 + +#define PSA_WANT_ECC_BRAINPOOL_P_R1_256 1 +#define PSA_WANT_ECC_BRAINPOOL_P_R1_384 1 +#define PSA_WANT_ECC_BRAINPOOL_P_R1_512 1 +#define PSA_WANT_ECC_MONTGOMERY_255 1 +#define PSA_WANT_ECC_MONTGOMERY_448 1 +#define PSA_WANT_ECC_SECP_K1_192 1 +/* + * SECP224K1 is buggy via the PSA API in Mbed TLS + * (https://github.com/Mbed-TLS/mbedtls/issues/3541). Thus, do not enable it by + * default. + */ +//#define PSA_WANT_ECC_SECP_K1_224 1 +#define PSA_WANT_ECC_SECP_K1_256 1 +#define PSA_WANT_ECC_SECP_R1_192 1 +#define PSA_WANT_ECC_SECP_R1_224 1 +#define PSA_WANT_ECC_SECP_R1_256 1 +#define PSA_WANT_ECC_SECP_R1_384 1 +#define PSA_WANT_ECC_SECP_R1_521 1 + +#define PSA_WANT_KEY_TYPE_DERIVE 1 +#define PSA_WANT_KEY_TYPE_PASSWORD 1 +#define PSA_WANT_KEY_TYPE_PASSWORD_HASH 1 +#define PSA_WANT_KEY_TYPE_HMAC 1 +#define PSA_WANT_KEY_TYPE_AES 1 +#define PSA_WANT_KEY_TYPE_ARIA 1 +#define PSA_WANT_KEY_TYPE_CAMELLIA 1 +#define PSA_WANT_KEY_TYPE_CHACHA20 1 +#define PSA_WANT_KEY_TYPE_DES 1 +#define PSA_WANT_KEY_TYPE_ECC_KEY_PAIR 1 +#define PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY 1 +#define PSA_WANT_KEY_TYPE_RAW_DATA 1 +#define PSA_WANT_KEY_TYPE_RSA_KEY_PAIR 1 +#define PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY 1 + +#endif /* PSA_CRYPTO_CONFIG_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_common.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_common.h new file mode 100644 index 00000000..26363c6b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_common.h @@ -0,0 +1,56 @@ +/** + * \file psa/crypto_driver_common.h + * \brief Definitions for all PSA crypto drivers + * + * This file contains common definitions shared by all PSA crypto drivers. + * Do not include it directly: instead, include the header file(s) for + * the type(s) of driver that you are implementing. For example, if + * you are writing a dynamically registered driver for a secure element, + * include `psa/crypto_se_driver.h`. + * + * This file is part of the PSA Crypto Driver Model, containing functions for + * driver developers to implement to enable hardware to be called in a + * standardized way by a PSA Cryptographic API implementation. The functions + * comprising the driver model, which driver authors implement, are not + * intended to be called by application developers. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PSA_CRYPTO_DRIVER_COMMON_H +#define PSA_CRYPTO_DRIVER_COMMON_H + +#include +#include + +/* Include type definitions (psa_status_t, psa_algorithm_t, + * psa_key_type_t, etc.) and macros to build and analyze values + * of these types. */ +#include "crypto_types.h" +#include "crypto_values.h" +/* Include size definitions which are used to size some arrays in operation + * structures. */ +#include + +/** For encrypt-decrypt functions, whether the operation is an encryption + * or a decryption. */ +typedef enum { + PSA_CRYPTO_DRIVER_DECRYPT, + PSA_CRYPTO_DRIVER_ENCRYPT +} psa_encrypt_or_decrypt_t; + +#endif /* PSA_CRYPTO_DRIVER_COMMON_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_contexts_composites.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_contexts_composites.h new file mode 100644 index 00000000..6c56a51d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_contexts_composites.h @@ -0,0 +1,163 @@ +/* + * Declaration of context structures for use with the PSA driver wrapper + * interface. This file contains the context structures for 'composite' + * operations, i.e. those operations which need to make use of other operations + * from the primitives (crypto_driver_contexts_primitives.h) + * + * Warning: This file will be auto-generated in the future. + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * \note This header and its content is not part of the Mbed TLS API and + * applications must not depend on it. Its main purpose is to define the + * multi-part state objects of the PSA drivers included in the cryptographic + * library. The definition of these objects are then used by crypto_struct.h + * to define the implementation-defined types of PSA multi-part state objects. + */ +/* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_DRIVER_CONTEXTS_COMPOSITES_H +#define PSA_CRYPTO_DRIVER_CONTEXTS_COMPOSITES_H + +#include "psa/crypto_driver_common.h" + +/* Include the context structure definitions for the Mbed TLS software drivers */ +#include "psa/crypto_builtin_composites.h" + +/* Include the context structure definitions for those drivers that were + * declared during the autogeneration process. */ + +#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) +#include +#endif + +#if defined(PSA_CRYPTO_DRIVER_TEST) +#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \ + defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_MAC) +typedef libtestdriver1_mbedtls_psa_mac_operation_t + mbedtls_transparent_test_driver_mac_operation_t; +typedef libtestdriver1_mbedtls_psa_mac_operation_t + mbedtls_opaque_test_driver_mac_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_MAC_OPERATION_INIT \ + LIBTESTDRIVER1_MBEDTLS_PSA_MAC_OPERATION_INIT +#define MBEDTLS_OPAQUE_TEST_DRIVER_MAC_OPERATION_INIT \ + LIBTESTDRIVER1_MBEDTLS_PSA_MAC_OPERATION_INIT + +#else +typedef mbedtls_psa_mac_operation_t + mbedtls_transparent_test_driver_mac_operation_t; +typedef mbedtls_psa_mac_operation_t + mbedtls_opaque_test_driver_mac_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_MAC_OPERATION_INIT \ + MBEDTLS_PSA_MAC_OPERATION_INIT +#define MBEDTLS_OPAQUE_TEST_DRIVER_MAC_OPERATION_INIT \ + MBEDTLS_PSA_MAC_OPERATION_INIT + +#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 && LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_MAC */ + +#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \ + defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD) +typedef libtestdriver1_mbedtls_psa_aead_operation_t + mbedtls_transparent_test_driver_aead_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_AEAD_OPERATION_INIT \ + LIBTESTDRIVER1_MBEDTLS_PSA_AEAD_OPERATION_INIT +#else +typedef mbedtls_psa_aead_operation_t + mbedtls_transparent_test_driver_aead_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_AEAD_OPERATION_INIT \ + MBEDTLS_PSA_AEAD_OPERATION_INIT + +#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 && LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \ + defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_PAKE) + +typedef libtestdriver1_mbedtls_psa_pake_operation_t + mbedtls_transparent_test_driver_pake_operation_t; +typedef libtestdriver1_mbedtls_psa_pake_operation_t + mbedtls_opaque_test_driver_pake_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_PAKE_OPERATION_INIT \ + LIBTESTDRIVER1_MBEDTLS_PSA_PAKE_OPERATION_INIT +#define MBEDTLS_OPAQUE_TEST_DRIVER_PAKE_OPERATION_INIT \ + LIBTESTDRIVER1_MBEDTLS_PSA_PAKE_OPERATION_INIT + +#else +typedef mbedtls_psa_pake_operation_t + mbedtls_transparent_test_driver_pake_operation_t; +typedef mbedtls_psa_pake_operation_t + mbedtls_opaque_test_driver_pake_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_PAKE_OPERATION_INIT \ + MBEDTLS_PSA_PAKE_OPERATION_INIT +#define MBEDTLS_OPAQUE_TEST_DRIVER_PAKE_OPERATION_INIT \ + MBEDTLS_PSA_PAKE_OPERATION_INIT + +#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 && LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_PAKE */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ + +/* Define the context to be used for an operation that is executed through the + * PSA Driver wrapper layer as the union of all possible driver's contexts. + * + * The union members are the driver's context structures, and the member names + * are formatted as `'drivername'_ctx`. This allows for procedural generation + * of both this file and the content of psa_crypto_driver_wrappers.c */ + +typedef union { + unsigned dummy; /* Make sure this union is always non-empty */ + mbedtls_psa_mac_operation_t mbedtls_ctx; +#if defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_transparent_test_driver_mac_operation_t transparent_test_driver_ctx; + mbedtls_opaque_test_driver_mac_operation_t opaque_test_driver_ctx; +#endif +} psa_driver_mac_context_t; + +typedef union { + unsigned dummy; /* Make sure this union is always non-empty */ + mbedtls_psa_aead_operation_t mbedtls_ctx; +#if defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_transparent_test_driver_aead_operation_t transparent_test_driver_ctx; +#endif +} psa_driver_aead_context_t; + +typedef union { + unsigned dummy; /* Make sure this union is always non-empty */ + mbedtls_psa_sign_hash_interruptible_operation_t mbedtls_ctx; +} psa_driver_sign_hash_interruptible_context_t; + +typedef union { + unsigned dummy; /* Make sure this union is always non-empty */ + mbedtls_psa_verify_hash_interruptible_operation_t mbedtls_ctx; +} psa_driver_verify_hash_interruptible_context_t; + +typedef union { + unsigned dummy; /* Make sure this union is always non-empty */ + mbedtls_psa_pake_operation_t mbedtls_ctx; +#if defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_transparent_test_driver_pake_operation_t transparent_test_driver_ctx; + mbedtls_opaque_test_driver_pake_operation_t opaque_test_driver_ctx; +#endif +} psa_driver_pake_context_t; + +#endif /* PSA_CRYPTO_DRIVER_CONTEXTS_COMPOSITES_H */ +/* End of automatically generated file. */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_contexts_primitives.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_contexts_primitives.h new file mode 100644 index 00000000..620a4b3a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_driver_contexts_primitives.h @@ -0,0 +1,117 @@ +/* + * Declaration of context structures for use with the PSA driver wrapper + * interface. This file contains the context structures for 'primitive' + * operations, i.e. those operations which do not rely on other contexts. + * + * Warning: This file will be auto-generated in the future. + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * \note This header and its content is not part of the Mbed TLS API and + * applications must not depend on it. Its main purpose is to define the + * multi-part state objects of the PSA drivers included in the cryptographic + * library. The definition of these objects are then used by crypto_struct.h + * to define the implementation-defined types of PSA multi-part state objects. + */ +/* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_DRIVER_CONTEXTS_PRIMITIVES_H +#define PSA_CRYPTO_DRIVER_CONTEXTS_PRIMITIVES_H + +#include "psa/crypto_driver_common.h" + +/* Include the context structure definitions for the Mbed TLS software drivers */ +#include "psa/crypto_builtin_primitives.h" + +/* Include the context structure definitions for those drivers that were + * declared during the autogeneration process. */ + +#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) +#include +#endif + +#if defined(PSA_CRYPTO_DRIVER_TEST) + +#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \ + defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_CIPHER) +typedef libtestdriver1_mbedtls_psa_cipher_operation_t + mbedtls_transparent_test_driver_cipher_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_CIPHER_OPERATION_INIT \ + LIBTESTDRIVER1_MBEDTLS_PSA_CIPHER_OPERATION_INIT +#else +typedef mbedtls_psa_cipher_operation_t + mbedtls_transparent_test_driver_cipher_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_CIPHER_OPERATION_INIT \ + MBEDTLS_PSA_CIPHER_OPERATION_INIT +#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 && + LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_CIPHER */ + +#if defined(MBEDTLS_TEST_LIBTESTDRIVER1) && \ + defined(LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_HASH) +typedef libtestdriver1_mbedtls_psa_hash_operation_t + mbedtls_transparent_test_driver_hash_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_HASH_OPERATION_INIT \ + LIBTESTDRIVER1_MBEDTLS_PSA_HASH_OPERATION_INIT +#else +typedef mbedtls_psa_hash_operation_t + mbedtls_transparent_test_driver_hash_operation_t; + +#define MBEDTLS_TRANSPARENT_TEST_DRIVER_HASH_OPERATION_INIT \ + MBEDTLS_PSA_HASH_OPERATION_INIT +#endif /* MBEDTLS_TEST_LIBTESTDRIVER1 && + LIBTESTDRIVER1_MBEDTLS_PSA_BUILTIN_HASH */ + +typedef struct { + unsigned int initialised : 1; + mbedtls_transparent_test_driver_cipher_operation_t ctx; +} mbedtls_opaque_test_driver_cipher_operation_t; + +#define MBEDTLS_OPAQUE_TEST_DRIVER_CIPHER_OPERATION_INIT \ + { 0, MBEDTLS_TRANSPARENT_TEST_DRIVER_CIPHER_OPERATION_INIT } + +#endif /* PSA_CRYPTO_DRIVER_TEST */ + +/* Define the context to be used for an operation that is executed through the + * PSA Driver wrapper layer as the union of all possible driver's contexts. + * + * The union members are the driver's context structures, and the member names + * are formatted as `'drivername'_ctx`. This allows for procedural generation + * of both this file and the content of psa_crypto_driver_wrappers.c */ + +typedef union { + unsigned dummy; /* Make sure this union is always non-empty */ + mbedtls_psa_hash_operation_t mbedtls_ctx; +#if defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_transparent_test_driver_hash_operation_t test_driver_ctx; +#endif +} psa_driver_hash_context_t; + +typedef union { + unsigned dummy; /* Make sure this union is always non-empty */ + mbedtls_psa_cipher_operation_t mbedtls_ctx; +#if defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_transparent_test_driver_cipher_operation_t transparent_test_driver_ctx; + mbedtls_opaque_test_driver_cipher_operation_t opaque_test_driver_ctx; +#endif +} psa_driver_cipher_context_t; + +#endif /* PSA_CRYPTO_DRIVER_CONTEXTS_PRIMITIVES_H */ +/* End of automatically generated file. */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_extra.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_extra.h new file mode 100644 index 00000000..4920508d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_extra.h @@ -0,0 +1,2141 @@ +/** + * \file psa/crypto_extra.h + * + * \brief PSA cryptography module: Mbed TLS vendor extensions + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * This file is reserved for vendor-specific definitions. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_EXTRA_H +#define PSA_CRYPTO_EXTRA_H +#include "mbedtls/private_access.h" + +#include "mbedtls/platform_util.h" + +#include "crypto_types.h" +#include "crypto_compat.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* UID for secure storage seed */ +#define PSA_CRYPTO_ITS_RANDOM_SEED_UID 0xFFFFFF52 + +/* See mbedtls_config.h for definition */ +#if !defined(MBEDTLS_PSA_KEY_SLOT_COUNT) +#define MBEDTLS_PSA_KEY_SLOT_COUNT 32 +#endif + +/** \addtogroup attributes + * @{ + */ + +/** \brief Declare the enrollment algorithm for a key. + * + * An operation on a key may indifferently use the algorithm set with + * psa_set_key_algorithm() or with this function. + * + * \param[out] attributes The attribute structure to write to. + * \param alg2 A second algorithm that the key may be used + * for, in addition to the algorithm set with + * psa_set_key_algorithm(). + * + * \warning Setting an enrollment algorithm is not recommended, because + * using the same key with different algorithms can allow some + * attacks based on arithmetic relations between different + * computations made with the same key, or can escalate harmless + * side channels into exploitable ones. Use this function only + * if it is necessary to support a protocol for which it has been + * verified that the usage of the key with multiple algorithms + * is safe. + */ +static inline void psa_set_key_enrollment_algorithm( + psa_key_attributes_t *attributes, + psa_algorithm_t alg2) +{ + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(policy).MBEDTLS_PRIVATE(alg2) = alg2; +} + +/** Retrieve the enrollment algorithm policy from key attributes. + * + * \param[in] attributes The key attribute structure to query. + * + * \return The enrollment algorithm stored in the attribute structure. + */ +static inline psa_algorithm_t psa_get_key_enrollment_algorithm( + const psa_key_attributes_t *attributes) +{ + return attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(policy).MBEDTLS_PRIVATE(alg2); +} + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + +/** Retrieve the slot number where a key is stored. + * + * A slot number is only defined for keys that are stored in a secure + * element. + * + * This information is only useful if the secure element is not entirely + * managed through the PSA Cryptography API. It is up to the secure + * element driver to decide how PSA slot numbers map to any other interface + * that the secure element may have. + * + * \param[in] attributes The key attribute structure to query. + * \param[out] slot_number On success, the slot number containing the key. + * + * \retval #PSA_SUCCESS + * The key is located in a secure element, and \p *slot_number + * indicates the slot number that contains it. + * \retval #PSA_ERROR_NOT_PERMITTED + * The caller is not permitted to query the slot number. + * Mbed Crypto currently does not return this error. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key is not located in a secure element. + */ +psa_status_t psa_get_key_slot_number( + const psa_key_attributes_t *attributes, + psa_key_slot_number_t *slot_number); + +/** Choose the slot number where a key is stored. + * + * This function declares a slot number in the specified attribute + * structure. + * + * A slot number is only meaningful for keys that are stored in a secure + * element. It is up to the secure element driver to decide how PSA slot + * numbers map to any other interface that the secure element may have. + * + * \note Setting a slot number in key attributes for a key creation can + * cause the following errors when creating the key: + * - #PSA_ERROR_NOT_SUPPORTED if the selected secure element does + * not support choosing a specific slot number. + * - #PSA_ERROR_NOT_PERMITTED if the caller is not permitted to + * choose slot numbers in general or to choose this specific slot. + * - #PSA_ERROR_INVALID_ARGUMENT if the chosen slot number is not + * valid in general or not valid for this specific key. + * - #PSA_ERROR_ALREADY_EXISTS if there is already a key in the + * selected slot. + * + * \param[out] attributes The attribute structure to write to. + * \param slot_number The slot number to set. + */ +static inline void psa_set_key_slot_number( + psa_key_attributes_t *attributes, + psa_key_slot_number_t slot_number) +{ + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(flags) |= MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER; + attributes->MBEDTLS_PRIVATE(slot_number) = slot_number; +} + +/** Remove the slot number attribute from a key attribute structure. + * + * This function undoes the action of psa_set_key_slot_number(). + * + * \param[out] attributes The attribute structure to write to. + */ +static inline void psa_clear_key_slot_number( + psa_key_attributes_t *attributes) +{ + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(flags) &= + ~MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER; +} + +/** Register a key that is already present in a secure element. + * + * The key must be located in a secure element designated by the + * lifetime field in \p attributes, in the slot set with + * psa_set_key_slot_number() in the attribute structure. + * This function makes the key available through the key identifier + * specified in \p attributes. + * + * \param[in] attributes The attributes of the existing key. + * + * \retval #PSA_SUCCESS + * The key was successfully registered. + * Note that depending on the design of the driver, this may or may + * not guarantee that a key actually exists in the designated slot + * and is compatible with the specified attributes. + * \retval #PSA_ERROR_ALREADY_EXISTS + * There is already a key with the identifier specified in + * \p attributes. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The secure element driver for the specified lifetime does not + * support registering a key. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The identifier in \p attributes is invalid, namely the identifier is + * not in the user range, or + * \p attributes specifies a lifetime which is not located + * in a secure element, or no slot number is specified in \p attributes, + * or the specified slot number is not valid. + * \retval #PSA_ERROR_NOT_PERMITTED + * The caller is not authorized to register the specified key slot. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t mbedtls_psa_register_se_key( + const psa_key_attributes_t *attributes); + +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +/**@}*/ + +/** + * \brief Library deinitialization. + * + * This function clears all data associated with the PSA layer, + * including the whole key store. + * + * This is an Mbed TLS extension. + */ +void mbedtls_psa_crypto_free(void); + +/** \brief Statistics about + * resource consumption related to the PSA keystore. + * + * \note The content of this structure is not part of the stable API and ABI + * of Mbed Crypto and may change arbitrarily from version to version. + */ +typedef struct mbedtls_psa_stats_s { + /** Number of slots containing key material for a volatile key. */ + size_t MBEDTLS_PRIVATE(volatile_slots); + /** Number of slots containing key material for a key which is in + * internal persistent storage. */ + size_t MBEDTLS_PRIVATE(persistent_slots); + /** Number of slots containing a reference to a key in a + * secure element. */ + size_t MBEDTLS_PRIVATE(external_slots); + /** Number of slots which are occupied, but do not contain + * key material yet. */ + size_t MBEDTLS_PRIVATE(half_filled_slots); + /** Number of slots that contain cache data. */ + size_t MBEDTLS_PRIVATE(cache_slots); + /** Number of slots that are not used for anything. */ + size_t MBEDTLS_PRIVATE(empty_slots); + /** Number of slots that are locked. */ + size_t MBEDTLS_PRIVATE(locked_slots); + /** Largest key id value among open keys in internal persistent storage. */ + psa_key_id_t MBEDTLS_PRIVATE(max_open_internal_key_id); + /** Largest key id value among open keys in secure elements. */ + psa_key_id_t MBEDTLS_PRIVATE(max_open_external_key_id); +} mbedtls_psa_stats_t; + +/** \brief Get statistics about + * resource consumption related to the PSA keystore. + * + * \note When Mbed Crypto is built as part of a service, with isolation + * between the application and the keystore, the service may or + * may not expose this function. + */ +void mbedtls_psa_get_stats(mbedtls_psa_stats_t *stats); + +/** + * \brief Inject an initial entropy seed for the random generator into + * secure storage. + * + * This function injects data to be used as a seed for the random generator + * used by the PSA Crypto implementation. On devices that lack a trusted + * entropy source (preferably a hardware random number generator), + * the Mbed PSA Crypto implementation uses this value to seed its + * random generator. + * + * On devices without a trusted entropy source, this function must be + * called exactly once in the lifetime of the device. On devices with + * a trusted entropy source, calling this function is optional. + * In all cases, this function may only be called before calling any + * other function in the PSA Crypto API, including psa_crypto_init(). + * + * When this function returns successfully, it populates a file in + * persistent storage. Once the file has been created, this function + * can no longer succeed. + * + * If any error occurs, this function does not change the system state. + * You can call this function again after correcting the reason for the + * error if possible. + * + * \warning This function **can** fail! Callers MUST check the return status. + * + * \warning If you use this function, you should use it as part of a + * factory provisioning process. The value of the injected seed + * is critical to the security of the device. It must be + * *secret*, *unpredictable* and (statistically) *unique per device*. + * You should be generate it randomly using a cryptographically + * secure random generator seeded from trusted entropy sources. + * You should transmit it securely to the device and ensure + * that its value is not leaked or stored anywhere beyond the + * needs of transmitting it from the point of generation to + * the call of this function, and erase all copies of the value + * once this function returns. + * + * This is an Mbed TLS extension. + * + * \note This function is only available on the following platforms: + * * If the compile-time option MBEDTLS_PSA_INJECT_ENTROPY is enabled. + * Note that you must provide compatible implementations of + * mbedtls_nv_seed_read and mbedtls_nv_seed_write. + * * In a client-server integration of PSA Cryptography, on the client side, + * if the server supports this feature. + * \param[in] seed Buffer containing the seed value to inject. + * \param[in] seed_size Size of the \p seed buffer. + * The size of the seed in bytes must be greater + * or equal to both #MBEDTLS_ENTROPY_BLOCK_SIZE + * and the value of \c MBEDTLS_ENTROPY_MIN_PLATFORM + * in `library/entropy_poll.h` in the Mbed TLS source + * code. + * It must be less or equal to + * #MBEDTLS_ENTROPY_MAX_SEED_SIZE. + * + * \retval #PSA_SUCCESS + * The seed value was injected successfully. The random generator + * of the PSA Crypto implementation is now ready for use. + * You may now call psa_crypto_init() and use the PSA Crypto + * implementation. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p seed_size is out of range. + * \retval #PSA_ERROR_STORAGE_FAILURE + * There was a failure reading or writing from storage. + * \retval #PSA_ERROR_NOT_PERMITTED + * The library has already been initialized. It is no longer + * possible to call this function. + */ +psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed, + size_t seed_size); + +/** \addtogroup crypto_types + * @{ + */ + +/** DSA public key. + * + * The import and export format is the + * representation of the public key `y = g^x mod p` as a big-endian byte + * string. The length of the byte string is the length of the base prime `p` + * in bytes. + */ +#define PSA_KEY_TYPE_DSA_PUBLIC_KEY ((psa_key_type_t) 0x4002) + +/** DSA key pair (private and public key). + * + * The import and export format is the + * representation of the private key `x` as a big-endian byte string. The + * length of the byte string is the private key size in bytes (leading zeroes + * are not stripped). + * + * Deterministic DSA key derivation with psa_generate_derived_key follows + * FIPS 186-4 §B.1.2: interpret the byte string as integer + * in big-endian order. Discard it if it is not in the range + * [0, *N* - 2] where *N* is the boundary of the private key domain + * (the prime *p* for Diffie-Hellman, the subprime *q* for DSA, + * or the order of the curve's base point for ECC). + * Add 1 to the resulting integer and use this as the private key *x*. + * + */ +#define PSA_KEY_TYPE_DSA_KEY_PAIR ((psa_key_type_t) 0x7002) + +/** Whether a key type is a DSA key (pair or public-only). */ +#define PSA_KEY_TYPE_IS_DSA(type) \ + (PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) == PSA_KEY_TYPE_DSA_PUBLIC_KEY) + +#define PSA_ALG_DSA_BASE ((psa_algorithm_t) 0x06000400) +/** DSA signature with hashing. + * + * This is the signature scheme defined by FIPS 186-4, + * with a random per-message secret number (*k*). + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * This includes #PSA_ALG_ANY_HASH + * when specifying the algorithm in a usage policy. + * + * \return The corresponding DSA signature algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_DSA(hash_alg) \ + (PSA_ALG_DSA_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +#define PSA_ALG_DETERMINISTIC_DSA_BASE ((psa_algorithm_t) 0x06000500) +#define PSA_ALG_DSA_DETERMINISTIC_FLAG PSA_ALG_ECDSA_DETERMINISTIC_FLAG +/** Deterministic DSA signature with hashing. + * + * This is the deterministic variant defined by RFC 6979 of + * the signature scheme defined by FIPS 186-4. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * This includes #PSA_ALG_ANY_HASH + * when specifying the algorithm in a usage policy. + * + * \return The corresponding DSA signature algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_DETERMINISTIC_DSA(hash_alg) \ + (PSA_ALG_DETERMINISTIC_DSA_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +#define PSA_ALG_IS_DSA(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK & ~PSA_ALG_DSA_DETERMINISTIC_FLAG) == \ + PSA_ALG_DSA_BASE) +#define PSA_ALG_DSA_IS_DETERMINISTIC(alg) \ + (((alg) & PSA_ALG_DSA_DETERMINISTIC_FLAG) != 0) +#define PSA_ALG_IS_DETERMINISTIC_DSA(alg) \ + (PSA_ALG_IS_DSA(alg) && PSA_ALG_DSA_IS_DETERMINISTIC(alg)) +#define PSA_ALG_IS_RANDOMIZED_DSA(alg) \ + (PSA_ALG_IS_DSA(alg) && !PSA_ALG_DSA_IS_DETERMINISTIC(alg)) + + +/* We need to expand the sample definition of this macro from + * the API definition. */ +#undef PSA_ALG_IS_VENDOR_HASH_AND_SIGN +#define PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg) \ + PSA_ALG_IS_DSA(alg) + +/**@}*/ + +/** \addtogroup attributes + * @{ + */ + +/** Custom Diffie-Hellman group. + * + * For keys of type #PSA_KEY_TYPE_DH_PUBLIC_KEY(#PSA_DH_FAMILY_CUSTOM) or + * #PSA_KEY_TYPE_DH_KEY_PAIR(#PSA_DH_FAMILY_CUSTOM), the group data comes + * from domain parameters set by psa_set_key_domain_parameters(). + */ +#define PSA_DH_FAMILY_CUSTOM ((psa_dh_family_t) 0x7e) + +/** PAKE operation stages. */ +#define PSA_PAKE_OPERATION_STAGE_SETUP 0 +#define PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS 1 +#define PSA_PAKE_OPERATION_STAGE_COMPUTATION 2 + +/** + * \brief Set domain parameters for a key. + * + * Some key types require additional domain parameters in addition to + * the key type identifier and the key size. Use this function instead + * of psa_set_key_type() when you need to specify domain parameters. + * + * The format for the required domain parameters varies based on the key type. + * + * - For RSA keys (#PSA_KEY_TYPE_RSA_PUBLIC_KEY or #PSA_KEY_TYPE_RSA_KEY_PAIR), + * the domain parameter data consists of the public exponent, + * represented as a big-endian integer with no leading zeros. + * This information is used when generating an RSA key pair. + * When importing a key, the public exponent is read from the imported + * key data and the exponent recorded in the attribute structure is ignored. + * As an exception, the public exponent 65537 is represented by an empty + * byte string. + * - For DSA keys (#PSA_KEY_TYPE_DSA_PUBLIC_KEY or #PSA_KEY_TYPE_DSA_KEY_PAIR), + * the `Dss-Params` format as defined by RFC 3279 §2.3.2. + * ``` + * Dss-Params ::= SEQUENCE { + * p INTEGER, + * q INTEGER, + * g INTEGER + * } + * ``` + * - For Diffie-Hellman key exchange keys + * (#PSA_KEY_TYPE_DH_PUBLIC_KEY(#PSA_DH_FAMILY_CUSTOM) or + * #PSA_KEY_TYPE_DH_KEY_PAIR(#PSA_DH_FAMILY_CUSTOM)), the + * `DomainParameters` format as defined by RFC 3279 §2.3.3. + * ``` + * DomainParameters ::= SEQUENCE { + * p INTEGER, -- odd prime, p=jq +1 + * g INTEGER, -- generator, g + * q INTEGER, -- factor of p-1 + * j INTEGER OPTIONAL, -- subgroup factor + * validationParams ValidationParams OPTIONAL + * } + * ValidationParams ::= SEQUENCE { + * seed BIT STRING, + * pgenCounter INTEGER + * } + * ``` + * + * \note This function may allocate memory or other resources. + * Once you have called this function on an attribute structure, + * you must call psa_reset_key_attributes() to free these resources. + * + * \note This is an experimental extension to the interface. It may change + * in future versions of the library. + * + * \param[in,out] attributes Attribute structure where the specified domain + * parameters will be stored. + * If this function fails, the content of + * \p attributes is not modified. + * \param type Key type (a \c PSA_KEY_TYPE_XXX value). + * \param[in] data Buffer containing the key domain parameters. + * The content of this buffer is interpreted + * according to \p type as described above. + * \param data_length Size of the \p data buffer in bytes. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t psa_set_key_domain_parameters(psa_key_attributes_t *attributes, + psa_key_type_t type, + const uint8_t *data, + size_t data_length); + +/** + * \brief Get domain parameters for a key. + * + * Get the domain parameters for a key with this function, if any. The format + * of the domain parameters written to \p data is specified in the + * documentation for psa_set_key_domain_parameters(). + * + * \note This is an experimental extension to the interface. It may change + * in future versions of the library. + * + * \param[in] attributes The key attribute structure to query. + * \param[out] data On success, the key domain parameters. + * \param data_size Size of the \p data buffer in bytes. + * The buffer is guaranteed to be large + * enough if its size in bytes is at least + * the value given by + * PSA_KEY_DOMAIN_PARAMETERS_SIZE(). + * \param[out] data_length On success, the number of bytes + * that make up the key domain parameters data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL \emptydescription + */ +psa_status_t psa_get_key_domain_parameters( + const psa_key_attributes_t *attributes, + uint8_t *data, + size_t data_size, + size_t *data_length); + +/** Safe output buffer size for psa_get_key_domain_parameters(). + * + * This macro returns a compile-time constant if its arguments are + * compile-time constants. + * + * \warning This function may call its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \note This is an experimental extension to the interface. It may change + * in future versions of the library. + * + * \param key_type A supported key type. + * \param key_bits The size of the key in bits. + * + * \return If the parameters are valid and supported, return + * a buffer size in bytes that guarantees that + * psa_get_key_domain_parameters() will not fail with + * #PSA_ERROR_BUFFER_TOO_SMALL. + * If the parameters are a valid combination that is not supported + * by the implementation, this macro shall return either a + * sensible size or 0. + * If the parameters are not valid, the + * return value is unspecified. + */ +#define PSA_KEY_DOMAIN_PARAMETERS_SIZE(key_type, key_bits) \ + (PSA_KEY_TYPE_IS_RSA(key_type) ? sizeof(int) : \ + PSA_KEY_TYPE_IS_DH(key_type) ? PSA_DH_KEY_DOMAIN_PARAMETERS_SIZE(key_bits) : \ + PSA_KEY_TYPE_IS_DSA(key_type) ? PSA_DSA_KEY_DOMAIN_PARAMETERS_SIZE(key_bits) : \ + 0) +#define PSA_DH_KEY_DOMAIN_PARAMETERS_SIZE(key_bits) \ + (4 + (PSA_BITS_TO_BYTES(key_bits) + 5) * 3 /*without optional parts*/) +#define PSA_DSA_KEY_DOMAIN_PARAMETERS_SIZE(key_bits) \ + (4 + (PSA_BITS_TO_BYTES(key_bits) + 5) * 2 /*p, g*/ + 34 /*q*/) + +/**@}*/ + +/** \defgroup psa_tls_helpers TLS helper functions + * @{ + */ + +#if defined(MBEDTLS_ECP_C) +#include + +/** Convert an ECC curve identifier from the Mbed TLS encoding to PSA. + * + * \note This function is provided solely for the convenience of + * Mbed TLS and may be removed at any time without notice. + * + * \param grpid An Mbed TLS elliptic curve identifier + * (`MBEDTLS_ECP_DP_xxx`). + * \param[out] bits On success, the bit size of the curve. + * + * \return The corresponding PSA elliptic curve identifier + * (`PSA_ECC_FAMILY_xxx`). + * \return \c 0 on failure (\p grpid is not recognized). + */ +static inline psa_ecc_family_t mbedtls_ecc_group_to_psa(mbedtls_ecp_group_id grpid, + size_t *bits) +{ + switch (grpid) { + case MBEDTLS_ECP_DP_SECP192R1: + *bits = 192; + return PSA_ECC_FAMILY_SECP_R1; + case MBEDTLS_ECP_DP_SECP224R1: + *bits = 224; + return PSA_ECC_FAMILY_SECP_R1; + case MBEDTLS_ECP_DP_SECP256R1: + *bits = 256; + return PSA_ECC_FAMILY_SECP_R1; + case MBEDTLS_ECP_DP_SECP384R1: + *bits = 384; + return PSA_ECC_FAMILY_SECP_R1; + case MBEDTLS_ECP_DP_SECP521R1: + *bits = 521; + return PSA_ECC_FAMILY_SECP_R1; + case MBEDTLS_ECP_DP_BP256R1: + *bits = 256; + return PSA_ECC_FAMILY_BRAINPOOL_P_R1; + case MBEDTLS_ECP_DP_BP384R1: + *bits = 384; + return PSA_ECC_FAMILY_BRAINPOOL_P_R1; + case MBEDTLS_ECP_DP_BP512R1: + *bits = 512; + return PSA_ECC_FAMILY_BRAINPOOL_P_R1; + case MBEDTLS_ECP_DP_CURVE25519: + *bits = 255; + return PSA_ECC_FAMILY_MONTGOMERY; + case MBEDTLS_ECP_DP_SECP192K1: + *bits = 192; + return PSA_ECC_FAMILY_SECP_K1; + case MBEDTLS_ECP_DP_SECP224K1: + *bits = 224; + return PSA_ECC_FAMILY_SECP_K1; + case MBEDTLS_ECP_DP_SECP256K1: + *bits = 256; + return PSA_ECC_FAMILY_SECP_K1; + case MBEDTLS_ECP_DP_CURVE448: + *bits = 448; + return PSA_ECC_FAMILY_MONTGOMERY; + default: + *bits = 0; + return 0; + } +} + +/** Convert an ECC curve identifier from the PSA encoding to Mbed TLS. + * + * \note This function is provided solely for the convenience of + * Mbed TLS and may be removed at any time without notice. + * + * \param curve A PSA elliptic curve identifier + * (`PSA_ECC_FAMILY_xxx`). + * \param bits The bit-length of a private key on \p curve. + * \param bits_is_sloppy If true, \p bits may be the bit-length rounded up + * to the nearest multiple of 8. This allows the caller + * to infer the exact curve from the length of a key + * which is supplied as a byte string. + * + * \return The corresponding Mbed TLS elliptic curve identifier + * (`MBEDTLS_ECP_DP_xxx`). + * \return #MBEDTLS_ECP_DP_NONE if \c curve is not recognized. + * \return #MBEDTLS_ECP_DP_NONE if \p bits is not + * correct for \p curve. + */ +mbedtls_ecp_group_id mbedtls_ecc_group_of_psa(psa_ecc_family_t curve, + size_t bits, + int bits_is_sloppy); +#endif /* MBEDTLS_ECP_C */ + +/**@}*/ + +/** \defgroup psa_external_rng External random generator + * @{ + */ + +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +/** External random generator function, implemented by the platform. + * + * When the compile-time option #MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG is enabled, + * this function replaces Mbed TLS's entropy and DRBG modules for all + * random generation triggered via PSA crypto interfaces. + * + * \note This random generator must deliver random numbers with cryptographic + * quality and high performance. It must supply unpredictable numbers + * with a uniform distribution. The implementation of this function + * is responsible for ensuring that the random generator is seeded + * with sufficient entropy. If you have a hardware TRNG which is slow + * or delivers non-uniform output, declare it as an entropy source + * with mbedtls_entropy_add_source() instead of enabling this option. + * + * \param[in,out] context Pointer to the random generator context. + * This is all-bits-zero on the first call + * and preserved between successive calls. + * \param[out] output Output buffer. On success, this buffer + * contains random data with a uniform + * distribution. + * \param output_size The size of the \p output buffer in bytes. + * \param[out] output_length On success, set this value to \p output_size. + * + * \retval #PSA_SUCCESS + * Success. The output buffer contains \p output_size bytes of + * cryptographic-quality random data, and \c *output_length is + * set to \p output_size. + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY + * The random generator requires extra entropy and there is no + * way to obtain entropy under current environment conditions. + * This error should not happen under normal circumstances since + * this function is responsible for obtaining as much entropy as + * it needs. However implementations of this function may return + * #PSA_ERROR_INSUFFICIENT_ENTROPY if there is no way to obtain + * entropy without blocking indefinitely. + * \retval #PSA_ERROR_HARDWARE_FAILURE + * A failure of the random generator hardware that isn't covered + * by #PSA_ERROR_INSUFFICIENT_ENTROPY. + */ +psa_status_t mbedtls_psa_external_get_random( + mbedtls_psa_external_random_context_t *context, + uint8_t *output, size_t output_size, size_t *output_length); +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + +/**@}*/ + +/** \defgroup psa_builtin_keys Built-in keys + * @{ + */ + +/** The minimum value for a key identifier that is built into the + * implementation. + * + * The range of key identifiers from #MBEDTLS_PSA_KEY_ID_BUILTIN_MIN + * to #MBEDTLS_PSA_KEY_ID_BUILTIN_MAX within the range from + * #PSA_KEY_ID_VENDOR_MIN and #PSA_KEY_ID_VENDOR_MAX and must not intersect + * with any other set of implementation-chosen key identifiers. + * + * This value is part of the library's ABI since changing it would invalidate + * the values of built-in key identifiers in applications. + */ +#define MBEDTLS_PSA_KEY_ID_BUILTIN_MIN ((psa_key_id_t) 0x7fff0000) + +/** The maximum value for a key identifier that is built into the + * implementation. + * + * See #MBEDTLS_PSA_KEY_ID_BUILTIN_MIN for more information. + */ +#define MBEDTLS_PSA_KEY_ID_BUILTIN_MAX ((psa_key_id_t) 0x7fffefff) + +/** A slot number identifying a key in a driver. + * + * Values of this type are used to identify built-in keys. + */ +typedef uint64_t psa_drv_slot_number_t; + +#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) +/** Test whether a key identifier belongs to the builtin key range. + * + * \param key_id Key identifier to test. + * + * \retval 1 + * The key identifier is a builtin key identifier. + * \retval 0 + * The key identifier is not a builtin key identifier. + */ +static inline int psa_key_id_is_builtin(psa_key_id_t key_id) +{ + return (key_id >= MBEDTLS_PSA_KEY_ID_BUILTIN_MIN) && + (key_id <= MBEDTLS_PSA_KEY_ID_BUILTIN_MAX); +} + +/** Platform function to obtain the location and slot number of a built-in key. + * + * An application-specific implementation of this function must be provided if + * #MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS is enabled. This would typically be provided + * as part of a platform's system image. + * + * #MBEDTLS_SVC_KEY_ID_GET_KEY_ID(\p key_id) needs to be in the range from + * #MBEDTLS_PSA_KEY_ID_BUILTIN_MIN to #MBEDTLS_PSA_KEY_ID_BUILTIN_MAX. + * + * In a multi-application configuration + * (\c MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER is defined), + * this function should check that #MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(\p key_id) + * is allowed to use the given key. + * + * \param key_id The key ID for which to retrieve the + * location and slot attributes. + * \param[out] lifetime On success, the lifetime associated with the key + * corresponding to \p key_id. Lifetime is a + * combination of which driver contains the key, + * and with what persistence level the key is + * intended to be used. If the platform + * implementation does not contain specific + * information about the intended key persistence + * level, the persistence level may be reported as + * #PSA_KEY_PERSISTENCE_DEFAULT. + * \param[out] slot_number On success, the slot number known to the driver + * registered at the lifetime location reported + * through \p lifetime which corresponds to the + * requested built-in key. + * + * \retval #PSA_SUCCESS + * The requested key identifier designates a built-in key. + * In a multi-application configuration, the requested owner + * is allowed to access it. + * \retval #PSA_ERROR_DOES_NOT_EXIST + * The requested key identifier is not a built-in key which is known + * to this function. If a key exists in the key storage with this + * identifier, the data from the storage will be used. + * \return (any other error) + * Any other error is propagated to the function that requested the key. + * Common errors include: + * - #PSA_ERROR_NOT_PERMITTED: the key exists but the requested owner + * is not allowed to access it. + */ +psa_status_t mbedtls_psa_platform_get_builtin_key( + mbedtls_svc_key_id_t key_id, + psa_key_lifetime_t *lifetime, + psa_drv_slot_number_t *slot_number); +#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ + +/** @} */ + +/** \addtogroup crypto_types + * @{ + */ + +#define PSA_ALG_CATEGORY_PAKE ((psa_algorithm_t) 0x0a000000) + +/** Whether the specified algorithm is a password-authenticated key exchange. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a password-authenticated key exchange (PAKE) + * algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_PAKE(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_PAKE) + +/** The Password-authenticated key exchange by juggling (J-PAKE) algorithm. + * + * This is J-PAKE as defined by RFC 8236, instantiated with the following + * parameters: + * + * - The group can be either an elliptic curve or defined over a finite field. + * - Schnorr NIZK proof as defined by RFC 8235 and using the same group as the + * J-PAKE algorithm. + * - A cryptographic hash function. + * + * To select these parameters and set up the cipher suite, call these functions + * in any order: + * + * \code + * psa_pake_cs_set_algorithm(cipher_suite, PSA_ALG_JPAKE); + * psa_pake_cs_set_primitive(cipher_suite, + * PSA_PAKE_PRIMITIVE(type, family, bits)); + * psa_pake_cs_set_hash(cipher_suite, hash); + * \endcode + * + * For more information on how to set a specific curve or field, refer to the + * documentation of the individual \c PSA_PAKE_PRIMITIVE_TYPE_XXX constants. + * + * After initializing a J-PAKE operation, call + * + * \code + * psa_pake_setup(operation, cipher_suite); + * psa_pake_set_user(operation, ...); + * psa_pake_set_peer(operation, ...); + * psa_pake_set_password_key(operation, ...); + * \endcode + * + * The password is provided as a key. This can be the password text itself, + * in an agreed character encoding, or some value derived from the password + * as required by a higher level protocol. + * + * (The implementation converts the key material to a number as described in + * Section 2.3.8 of _SEC 1: Elliptic Curve Cryptography_ + * (https://www.secg.org/sec1-v2.pdf), before reducing it modulo \c q. Here + * \c q is order of the group defined by the primitive set in the cipher suite. + * The \c psa_pake_set_password_key() function returns an error if the result + * of the reduction is 0.) + * + * The key exchange flow for J-PAKE is as follows: + * -# To get the first round data that needs to be sent to the peer, call + * \code + * // Get g1 + * psa_pake_output(operation, #PSA_PAKE_STEP_KEY_SHARE, ...); + * // Get the ZKP public key for x1 + * psa_pake_output(operation, #PSA_PAKE_STEP_ZK_PUBLIC, ...); + * // Get the ZKP proof for x1 + * psa_pake_output(operation, #PSA_PAKE_STEP_ZK_PROOF, ...); + * // Get g2 + * psa_pake_output(operation, #PSA_PAKE_STEP_KEY_SHARE, ...); + * // Get the ZKP public key for x2 + * psa_pake_output(operation, #PSA_PAKE_STEP_ZK_PUBLIC, ...); + * // Get the ZKP proof for x2 + * psa_pake_output(operation, #PSA_PAKE_STEP_ZK_PROOF, ...); + * \endcode + * -# To provide the first round data received from the peer to the operation, + * call + * \code + * // Set g3 + * psa_pake_input(operation, #PSA_PAKE_STEP_KEY_SHARE, ...); + * // Set the ZKP public key for x3 + * psa_pake_input(operation, #PSA_PAKE_STEP_ZK_PUBLIC, ...); + * // Set the ZKP proof for x3 + * psa_pake_input(operation, #PSA_PAKE_STEP_ZK_PROOF, ...); + * // Set g4 + * psa_pake_input(operation, #PSA_PAKE_STEP_KEY_SHARE, ...); + * // Set the ZKP public key for x4 + * psa_pake_input(operation, #PSA_PAKE_STEP_ZK_PUBLIC, ...); + * // Set the ZKP proof for x4 + * psa_pake_input(operation, #PSA_PAKE_STEP_ZK_PROOF, ...); + * \endcode + * -# To get the second round data that needs to be sent to the peer, call + * \code + * // Get A + * psa_pake_output(operation, #PSA_PAKE_STEP_KEY_SHARE, ...); + * // Get ZKP public key for x2*s + * psa_pake_output(operation, #PSA_PAKE_STEP_ZK_PUBLIC, ...); + * // Get ZKP proof for x2*s + * psa_pake_output(operation, #PSA_PAKE_STEP_ZK_PROOF, ...); + * \endcode + * -# To provide the second round data received from the peer to the operation, + * call + * \code + * // Set B + * psa_pake_input(operation, #PSA_PAKE_STEP_KEY_SHARE, ...); + * // Set ZKP public key for x4*s + * psa_pake_input(operation, #PSA_PAKE_STEP_ZK_PUBLIC, ...); + * // Set ZKP proof for x4*s + * psa_pake_input(operation, #PSA_PAKE_STEP_ZK_PROOF, ...); + * \endcode + * -# To access the shared secret call + * \code + * // Get Ka=Kb=K + * psa_pake_get_implicit_key() + * \endcode + * + * For more information consult the documentation of the individual + * \c PSA_PAKE_STEP_XXX constants. + * + * At this point there is a cryptographic guarantee that only the authenticated + * party who used the same password is able to compute the key. But there is no + * guarantee that the peer is the party it claims to be and was able to do so. + * + * That is, the authentication is only implicit (the peer is not authenticated + * at this point, and no action should be taken that assume that they are - like + * for example accessing restricted files). + * + * To make the authentication explicit there are various methods, see Section 5 + * of RFC 8236 for two examples. + * + */ +#define PSA_ALG_JPAKE ((psa_algorithm_t) 0x0a000100) + +/** @} */ + +/** \defgroup pake Password-authenticated key exchange (PAKE) + * + * This is a proposed PAKE interface for the PSA Crypto API. It is not part of + * the official PSA Crypto API yet. + * + * \note The content of this section is not part of the stable API and ABI + * of Mbed Crypto and may change arbitrarily from version to version. + * Same holds for the corresponding macros #PSA_ALG_CATEGORY_PAKE and + * #PSA_ALG_JPAKE. + * @{ + */ + +/** \brief Encoding of the application role of PAKE + * + * Encodes the application's role in the algorithm is being executed. For more + * information see the documentation of individual \c PSA_PAKE_ROLE_XXX + * constants. + */ +typedef uint8_t psa_pake_role_t; + +/** Encoding of input and output indicators for PAKE. + * + * Some PAKE algorithms need to exchange more data than just a single key share. + * This type is for encoding additional input and output data for such + * algorithms. + */ +typedef uint8_t psa_pake_step_t; + +/** Encoding of the type of the PAKE's primitive. + * + * Values defined by this standard will never be in the range 0x80-0xff. + * Vendors who define additional types must use an encoding in this range. + * + * For more information see the documentation of individual + * \c PSA_PAKE_PRIMITIVE_TYPE_XXX constants. + */ +typedef uint8_t psa_pake_primitive_type_t; + +/** \brief Encoding of the family of the primitive associated with the PAKE. + * + * For more information see the documentation of individual + * \c PSA_PAKE_PRIMITIVE_TYPE_XXX constants. + */ +typedef uint8_t psa_pake_family_t; + +/** \brief Encoding of the primitive associated with the PAKE. + * + * For more information see the documentation of the #PSA_PAKE_PRIMITIVE macro. + */ +typedef uint32_t psa_pake_primitive_t; + +/** A value to indicate no role in a PAKE algorithm. + * This value can be used in a call to psa_pake_set_role() for symmetric PAKE + * algorithms which do not assign roles. + */ +#define PSA_PAKE_ROLE_NONE ((psa_pake_role_t) 0x00) + +/** The first peer in a balanced PAKE. + * + * Although balanced PAKE algorithms are symmetric, some of them needs an + * ordering of peers for the transcript calculations. If the algorithm does not + * need this, both #PSA_PAKE_ROLE_FIRST and #PSA_PAKE_ROLE_SECOND are + * accepted. + */ +#define PSA_PAKE_ROLE_FIRST ((psa_pake_role_t) 0x01) + +/** The second peer in a balanced PAKE. + * + * Although balanced PAKE algorithms are symmetric, some of them needs an + * ordering of peers for the transcript calculations. If the algorithm does not + * need this, either #PSA_PAKE_ROLE_FIRST or #PSA_PAKE_ROLE_SECOND are + * accepted. + */ +#define PSA_PAKE_ROLE_SECOND ((psa_pake_role_t) 0x02) + +/** The client in an augmented PAKE. + * + * Augmented PAKE algorithms need to differentiate between client and server. + */ +#define PSA_PAKE_ROLE_CLIENT ((psa_pake_role_t) 0x11) + +/** The server in an augmented PAKE. + * + * Augmented PAKE algorithms need to differentiate between client and server. + */ +#define PSA_PAKE_ROLE_SERVER ((psa_pake_role_t) 0x12) + +/** The PAKE primitive type indicating the use of elliptic curves. + * + * The values of the \c family and \c bits fields of the cipher suite identify a + * specific elliptic curve, using the same mapping that is used for ECC + * (::psa_ecc_family_t) keys. + * + * (Here \c family means the value returned by psa_pake_cs_get_family() and + * \c bits means the value returned by psa_pake_cs_get_bits().) + * + * Input and output during the operation can involve group elements and scalar + * values: + * -# The format for group elements is the same as for public keys on the + * specific curve would be. For more information, consult the documentation of + * psa_export_public_key(). + * -# The format for scalars is the same as for private keys on the specific + * curve would be. For more information, consult the documentation of + * psa_export_key(). + */ +#define PSA_PAKE_PRIMITIVE_TYPE_ECC ((psa_pake_primitive_type_t) 0x01) + +/** The PAKE primitive type indicating the use of Diffie-Hellman groups. + * + * The values of the \c family and \c bits fields of the cipher suite identify + * a specific Diffie-Hellman group, using the same mapping that is used for + * Diffie-Hellman (::psa_dh_family_t) keys. + * + * (Here \c family means the value returned by psa_pake_cs_get_family() and + * \c bits means the value returned by psa_pake_cs_get_bits().) + * + * Input and output during the operation can involve group elements and scalar + * values: + * -# The format for group elements is the same as for public keys on the + * specific group would be. For more information, consult the documentation of + * psa_export_public_key(). + * -# The format for scalars is the same as for private keys on the specific + * group would be. For more information, consult the documentation of + * psa_export_key(). + */ +#define PSA_PAKE_PRIMITIVE_TYPE_DH ((psa_pake_primitive_type_t) 0x02) + +/** Construct a PAKE primitive from type, family and bit-size. + * + * \param pake_type The type of the primitive + * (value of type ::psa_pake_primitive_type_t). + * \param pake_family The family of the primitive + * (the type and interpretation of this parameter depends + * on \p type, for more information consult the + * documentation of individual ::psa_pake_primitive_type_t + * constants). + * \param pake_bits The bit-size of the primitive + * (Value of type \c size_t. The interpretation + * of this parameter depends on \p family, for more + * information consult the documentation of individual + * ::psa_pake_primitive_type_t constants). + * + * \return The constructed primitive value of type ::psa_pake_primitive_t. + * Return 0 if the requested primitive can't be encoded as + * ::psa_pake_primitive_t. + */ +#define PSA_PAKE_PRIMITIVE(pake_type, pake_family, pake_bits) \ + ((pake_bits & 0xFFFF) != pake_bits) ? 0 : \ + ((psa_pake_primitive_t) (((pake_type) << 24 | \ + (pake_family) << 16) | (pake_bits))) + +/** The key share being sent to or received from the peer. + * + * The format for both input and output at this step is the same as for public + * keys on the group determined by the primitive (::psa_pake_primitive_t) would + * be. + * + * For more information on the format, consult the documentation of + * psa_export_public_key(). + * + * For information regarding how the group is determined, consult the + * documentation #PSA_PAKE_PRIMITIVE. + */ +#define PSA_PAKE_STEP_KEY_SHARE ((psa_pake_step_t) 0x01) + +/** A Schnorr NIZKP public key. + * + * This is the ephemeral public key in the Schnorr Non-Interactive + * Zero-Knowledge Proof (the value denoted by the letter 'V' in RFC 8235). + * + * The format for both input and output at this step is the same as for public + * keys on the group determined by the primitive (::psa_pake_primitive_t) would + * be. + * + * For more information on the format, consult the documentation of + * psa_export_public_key(). + * + * For information regarding how the group is determined, consult the + * documentation #PSA_PAKE_PRIMITIVE. + */ +#define PSA_PAKE_STEP_ZK_PUBLIC ((psa_pake_step_t) 0x02) + +/** A Schnorr NIZKP proof. + * + * This is the proof in the Schnorr Non-Interactive Zero-Knowledge Proof (the + * value denoted by the letter 'r' in RFC 8235). + * + * Both for input and output, the value at this step is an integer less than + * the order of the group selected in the cipher suite. The format depends on + * the group as well: + * + * - For Montgomery curves, the encoding is little endian. + * - For everything else the encoding is big endian (see Section 2.3.8 of + * _SEC 1: Elliptic Curve Cryptography_ at https://www.secg.org/sec1-v2.pdf). + * + * In both cases leading zeroes are allowed as long as the length in bytes does + * not exceed the byte length of the group order. + * + * For information regarding how the group is determined, consult the + * documentation #PSA_PAKE_PRIMITIVE. + */ +#define PSA_PAKE_STEP_ZK_PROOF ((psa_pake_step_t) 0x03) + +/** The type of the data structure for PAKE cipher suites. + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. + */ +typedef struct psa_pake_cipher_suite_s psa_pake_cipher_suite_t; + +/** Return an initial value for a PAKE cipher suite object. + */ +static psa_pake_cipher_suite_t psa_pake_cipher_suite_init(void); + +/** Retrieve the PAKE algorithm from a PAKE cipher suite. + * + * \param[in] cipher_suite The cipher suite structure to query. + * + * \return The PAKE algorithm stored in the cipher suite structure. + */ +static psa_algorithm_t psa_pake_cs_get_algorithm( + const psa_pake_cipher_suite_t *cipher_suite); + +/** Declare the PAKE algorithm for the cipher suite. + * + * This function overwrites any PAKE algorithm + * previously set in \p cipher_suite. + * + * \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 + * such that #PSA_ALG_IS_PAKE(\c alg) is true.) + * If this is 0, the PAKE algorithm in + * \p cipher_suite becomes unspecified. + */ +static void psa_pake_cs_set_algorithm(psa_pake_cipher_suite_t *cipher_suite, + psa_algorithm_t algorithm); + +/** Retrieve the primitive from a PAKE cipher suite. + * + * \param[in] cipher_suite The cipher suite structure to query. + * + * \return The primitive stored in the cipher suite structure. + */ +static psa_pake_primitive_t psa_pake_cs_get_primitive( + const psa_pake_cipher_suite_t *cipher_suite); + +/** Declare the primitive for a PAKE cipher suite. + * + * This function overwrites any primitive previously set in \p cipher_suite. + * + * \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 + * unspecified. + */ +static void psa_pake_cs_set_primitive(psa_pake_cipher_suite_t *cipher_suite, + psa_pake_primitive_t primitive); + +/** Retrieve the PAKE family from a PAKE cipher suite. + * + * \param[in] cipher_suite The cipher suite structure to query. + * + * \return The PAKE family stored in the cipher suite structure. + */ +static psa_pake_family_t psa_pake_cs_get_family( + const psa_pake_cipher_suite_t *cipher_suite); + +/** Retrieve the PAKE primitive bit-size from a PAKE cipher suite. + * + * \param[in] cipher_suite The cipher suite structure to query. + * + * \return The PAKE primitive bit-size stored in the cipher suite structure. + */ +static uint16_t psa_pake_cs_get_bits( + const psa_pake_cipher_suite_t *cipher_suite); + +/** Retrieve the hash algorithm from a PAKE cipher suite. + * + * \param[in] cipher_suite The cipher suite structure to query. + * + * \return The hash algorithm stored in the cipher suite structure. The return + * value is 0 if the PAKE is not parametrised by a hash algorithm or if + * the hash algorithm is not set. + */ +static psa_algorithm_t psa_pake_cs_get_hash( + const psa_pake_cipher_suite_t *cipher_suite); + +/** Declare the hash algorithm for a PAKE cipher suite. + * + * This function overwrites any hash algorithm + * previously set in \p cipher_suite. + * + * Refer to the documentation of individual PAKE algorithm types (`PSA_ALG_XXX` + * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) + * for more information. + * + * \param[out] cipher_suite The cipher suite structure to write to. + * \param hash The hash involved in the cipher suite. + * (`PSA_ALG_XXX` values of type ::psa_algorithm_t + * such that #PSA_ALG_IS_HASH(\c alg) is true.) + * If this is 0, the hash algorithm in + * \p cipher_suite becomes unspecified. + */ +static void psa_pake_cs_set_hash(psa_pake_cipher_suite_t *cipher_suite, + psa_algorithm_t hash); + +/** The type of the state data structure for PAKE operations. + * + * Before calling any function on a PAKE operation object, the application + * must initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_pake_operation_t operation; + * memset(&operation, 0, sizeof(operation)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_pake_operation_t operation = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_PAKE_OPERATION_INIT, + * for example: + * \code + * psa_pake_operation_t operation = PSA_PAKE_OPERATION_INIT; + * \endcode + * - Assign the result of the function psa_pake_operation_init() + * to the structure, for example: + * \code + * psa_pake_operation_t operation; + * operation = psa_pake_operation_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure. + * Implementation details can change in future versions without notice. */ +typedef struct psa_pake_operation_s psa_pake_operation_t; + +/** The type of input values for PAKE operations. */ +typedef struct psa_crypto_driver_pake_inputs_s psa_crypto_driver_pake_inputs_t; + +/** The type of computation stage for J-PAKE operations. */ +typedef struct psa_jpake_computation_stage_s psa_jpake_computation_stage_t; + +/** Return an initial value for a PAKE operation object. + */ +static psa_pake_operation_t psa_pake_operation_init(void); + +/** Get the length of the password in bytes from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] password_len Password length. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * Password hasn't been set yet. + */ +psa_status_t psa_crypto_driver_pake_get_password_len( + const psa_crypto_driver_pake_inputs_t *inputs, + size_t *password_len); + +/** Get the password from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] buffer Return buffer for password. + * \param buffer_size Size of the return buffer in bytes. + * \param[out] buffer_length Actual size of the password in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * Password hasn't been set yet. + */ +psa_status_t psa_crypto_driver_pake_get_password( + const psa_crypto_driver_pake_inputs_t *inputs, + uint8_t *buffer, size_t buffer_size, size_t *buffer_length); + +/** Get the role from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] role Return buffer for role. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * Role hasn't been set yet. + */ +psa_status_t psa_crypto_driver_pake_get_role( + const psa_crypto_driver_pake_inputs_t *inputs, + psa_pake_role_t *role); + +/** Get the length of the user id in bytes from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] user_len User id length. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * User id hasn't been set yet. + */ +psa_status_t psa_crypto_driver_pake_get_user_len( + const psa_crypto_driver_pake_inputs_t *inputs, + size_t *user_len); + +/** Get the length of the peer id in bytes from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] peer_len Peer id length. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * Peer id hasn't been set yet. + */ +psa_status_t psa_crypto_driver_pake_get_peer_len( + const psa_crypto_driver_pake_inputs_t *inputs, + size_t *peer_len); + +/** Get the user id from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] user_id User id. + * \param user_id_size Size of \p user_id in bytes. + * \param[out] user_id_len Size of the user id in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * User id hasn't been set yet. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p user_id is too small. + */ +psa_status_t psa_crypto_driver_pake_get_user( + const psa_crypto_driver_pake_inputs_t *inputs, + uint8_t *user_id, size_t user_id_size, size_t *user_id_len); + +/** Get the peer id from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] peer_id Peer id. + * \param peer_id_size Size of \p peer_id in bytes. + * \param[out] peer_id_length Size of the peer id in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * Peer id hasn't been set yet. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p peer_id is too small. + */ +psa_status_t psa_crypto_driver_pake_get_peer( + const psa_crypto_driver_pake_inputs_t *inputs, + uint8_t *peer_id, size_t peer_id_size, size_t *peer_id_length); + +/** Get the cipher suite from given inputs. + * + * \param[in] inputs Operation inputs. + * \param[out] cipher_suite Return buffer for role. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * Cipher_suite hasn't been set yet. + */ +psa_status_t psa_crypto_driver_pake_get_cipher_suite( + const psa_crypto_driver_pake_inputs_t *inputs, + psa_pake_cipher_suite_t *cipher_suite); + +/** Set the session information for a password-authenticated key exchange. + * + * The sequence of operations to set up a password-authenticated key exchange + * is as follows: + * -# Allocate an operation object which will be passed to all the functions + * listed here. + * -# Initialize the operation object with one of the methods described in the + * documentation for #psa_pake_operation_t, e.g. + * #PSA_PAKE_OPERATION_INIT. + * -# Call psa_pake_setup() to specify the cipher suite. + * -# Call \c psa_pake_set_xxx() functions on the operation to complete the + * setup. The exact sequence of \c psa_pake_set_xxx() functions that needs + * to be called depends on the algorithm in use. + * + * Refer to the documentation of individual PAKE algorithm types (`PSA_ALG_XXX` + * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) + * for more information. + * + * A typical sequence of calls to perform a password-authenticated key + * exchange: + * -# Call psa_pake_output(operation, #PSA_PAKE_STEP_KEY_SHARE, ...) to get the + * key share that needs to be sent to the peer. + * -# Call psa_pake_input(operation, #PSA_PAKE_STEP_KEY_SHARE, ...) to provide + * the key share that was received from the peer. + * -# Depending on the algorithm additional calls to psa_pake_output() and + * psa_pake_input() might be necessary. + * -# Call psa_pake_get_implicit_key() for accessing the shared secret. + * + * Refer to the documentation of individual PAKE algorithm types (`PSA_ALG_XXX` + * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) + * for more information. + * + * If an error occurs at any step after a call to psa_pake_setup(), + * the operation will need to be reset by a call to psa_pake_abort(). The + * application may call psa_pake_abort() at any time after the operation + * has been initialized. + * + * After a successful call to psa_pake_setup(), the application must + * eventually terminate the operation. The following events terminate an + * operation: + * - A call to psa_pake_abort(). + * - A successful call to psa_pake_get_implicit_key(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized but not set up yet. + * \param[in] cipher_suite The cipher suite to use. (A cipher suite fully + * characterizes a PAKE algorithm and determines + * the algorithm as well.) + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The algorithm in \p cipher_suite is not a PAKE algorithm, or the + * PAKE primitive in \p cipher_suite is not compatible with the + * PAKE algorithm, or the hash algorithm in \p cipher_suite is invalid + * or not compatible with the PAKE algorithm and primitive. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The algorithm in \p cipher_suite is not a supported PAKE algorithm, + * or the PAKE primitive in \p cipher_suite is not supported or not + * compatible with the PAKE algorithm, or the hash algorithm in + * \p cipher_suite is not supported or not compatible with the PAKE + * algorithm and primitive. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid, or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_setup(psa_pake_operation_t *operation, + const psa_pake_cipher_suite_t *cipher_suite); + +/** Set the password for a password-authenticated key exchange from key ID. + * + * Call this function when the password, or a value derived from the password, + * is already present in the key store. + * + * \param[in,out] operation The operation object to set the password for. It + * must have been set up by psa_pake_setup() and + * not yet in use (neither psa_pake_output() nor + * psa_pake_input() has been called yet). It must + * be on operation for which the password hasn't + * been set yet (psa_pake_set_password_key() + * hasn't been called yet). + * \param password Identifier of the key holding the password or a + * value derived from the password (eg. by a + * memory-hard function). It must remain valid + * until the operation terminates. It must be of + * type #PSA_KEY_TYPE_PASSWORD or + * #PSA_KEY_TYPE_PASSWORD_HASH. It has to allow + * the usage #PSA_KEY_USAGE_DERIVE. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_HANDLE + * \p password is not a valid key identifier. + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_DERIVE flag, or it does not + * permit the \p operation's algorithm. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key type for \p password is not #PSA_KEY_TYPE_PASSWORD or + * #PSA_KEY_TYPE_PASSWORD_HASH, or \p password is not compatible with + * the \p operation's cipher suite. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The key type or key size of \p password is not supported with the + * \p operation's cipher suite. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must have been set up.), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_set_password_key(psa_pake_operation_t *operation, + mbedtls_svc_key_id_t password); + +/** Set the user ID for a password-authenticated key exchange. + * + * Call this function to set the user ID. For PAKE algorithms that associate a + * user identifier with each side of the session you need to call + * psa_pake_set_peer() as well. For PAKE algorithms that associate a single + * user identifier with the session, call psa_pake_set_user() only. + * + * Refer to the documentation of individual PAKE algorithm types (`PSA_ALG_XXX` + * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) + * for more information. + * + * \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 + * psa_pake_input() has been called yet). It must + * be on operation for which the user ID hasn't + * been set (psa_pake_set_user() hasn't been + * called yet). + * \param[in] user_id The user ID to authenticate with. + * (temporary limitation: "client" or "server" only) + * \param user_id_len Size of the \p user_id buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p user_id is not valid for the \p operation's algorithm and cipher + * suite. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The value of \p user_id is not supported by the implementation. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid, or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_set_user(psa_pake_operation_t *operation, + const uint8_t *user_id, + size_t user_id_len); + +/** Set the peer ID for a password-authenticated key exchange. + * + * Call this function in addition to psa_pake_set_user() for PAKE algorithms + * that associate a user identifier with each side of the session. For PAKE + * algorithms that associate a single user identifier with the session, call + * psa_pake_set_user() only. + * + * Refer to the documentation of individual PAKE algorithm types (`PSA_ALG_XXX` + * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) + * for more information. + * + * \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 + * psa_pake_input() has been called yet). It must + * be on operation for which the peer ID hasn't + * been set (psa_pake_set_peer() hasn't been + * called yet). + * \param[in] peer_id The peer's ID to authenticate. + * (temporary limitation: "client" or "server" only) + * \param peer_id_len Size of the \p peer_id buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p user_id is not valid for the \p operation's algorithm and cipher + * suite. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The algorithm doesn't associate a second identity with the session. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * Calling psa_pake_set_peer() is invalid with the \p operation's + * algorithm, the operation state is not valid, or the library has not + * been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_set_peer(psa_pake_operation_t *operation, + const uint8_t *peer_id, + size_t peer_id_len); + +/** Set the application role for a password-authenticated key exchange. + * + * Not all PAKE algorithms need to differentiate the communicating entities. + * It is optional to call this function for PAKEs that don't require a role + * to be specified. For such PAKEs the application role parameter is ignored, + * or #PSA_PAKE_ROLE_NONE can be passed as \c role. + * + * Refer to the documentation of individual PAKE algorithm types (`PSA_ALG_XXX` + * values of type ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) + * for more information. + * + * \param[in,out] operation The operation object to specify the + * application's role for. It must have been set up + * by psa_pake_setup() and not yet in use (neither + * psa_pake_output() nor psa_pake_input() has been + * called yet). It must be on operation for which + * the application's role hasn't been specified + * (psa_pake_set_role() hasn't been called yet). + * \param role A value of type ::psa_pake_role_t indicating the + * application's role in the PAKE the algorithm + * that is being set up. For more information see + * the documentation of \c PSA_PAKE_ROLE_XXX + * constants. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The \p role is not a valid PAKE role in the \p operation’s algorithm. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The \p role for this algorithm is not supported or is not valid. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid, or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_set_role(psa_pake_operation_t *operation, + psa_pake_role_t role); + +/** Get output for a step of a password-authenticated key exchange. + * + * Depending on the algorithm being executed, you might need to call this + * function several times or you might not need to call this at all. + * + * The exact sequence of calls to perform a password-authenticated key + * exchange depends on the algorithm in use. Refer to the documentation of + * individual PAKE algorithm types (`PSA_ALG_XXX` values of type + * ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) for more + * information. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_pake_abort(). + * + * \param[in,out] operation Active PAKE operation. + * \param step The step of the algorithm for which the output is + * requested. + * \param[out] output Buffer where the output is to be written in the + * format appropriate for this \p step. Refer to + * the documentation of the individual + * \c PSA_PAKE_STEP_XXX constants for more + * information. + * \param output_size Size of the \p output buffer in bytes. This must + * be at least #PSA_PAKE_OUTPUT_SIZE(\p alg, \p + * primitive, \p step) where \p alg and + * \p primitive are the PAKE algorithm and primitive + * in the operation's cipher suite, and \p step is + * the output step. + * + * \param[out] output_length On success, the number of bytes of the returned + * output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p step is not compatible with the operation's algorithm. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p step is not supported with the operation's algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, and fully set + * up, and this call must conform to the algorithm's requirements + * for ordering of input and output steps), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_output(psa_pake_operation_t *operation, + psa_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Provide input for a step of a password-authenticated key exchange. + * + * Depending on the algorithm being executed, you might need to call this + * function several times or you might not need to call this at all. + * + * The exact sequence of calls to perform a password-authenticated key + * exchange depends on the algorithm in use. Refer to the documentation of + * individual PAKE algorithm types (`PSA_ALG_XXX` values of type + * ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) for more + * information. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling psa_pake_abort(). + * + * \param[in,out] operation Active PAKE operation. + * \param step The step for which the input is provided. + * \param[in] input Buffer containing the input in the format + * appropriate for this \p step. Refer to the + * documentation of the individual + * \c PSA_PAKE_STEP_XXX constants for more + * information. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The verification fails for a #PSA_PAKE_STEP_ZK_PROOF input step. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p is not compatible with the \p operation’s algorithm, or the + * \p input is not valid for the \p operation's algorithm, cipher suite + * or \p step. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p step p is not supported with the \p operation's algorithm, or the + * \p input is not supported for the \p operation's algorithm, cipher + * suite or \p step. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active, and fully set + * up, and this call must conform to the algorithm's requirements + * for ordering of input and output steps), or + * the library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_input(psa_pake_operation_t *operation, + psa_pake_step_t step, + const uint8_t *input, + size_t input_length); + +/** Get implicitly confirmed shared secret from a PAKE. + * + * At this point there is a cryptographic guarantee that only the authenticated + * party who used the same password is able to compute the key. But there is no + * guarantee that the peer is the party it claims to be and was able to do so. + * + * That is, the authentication is only implicit. Since the peer is not + * authenticated yet, no action should be taken yet that assumes that the peer + * is who it claims to be. For example, do not access restricted files on the + * peer's behalf until an explicit authentication has succeeded. + * + * This function can be called after the key exchange phase of the operation + * has completed. It imports the shared secret output of the PAKE into the + * provided derivation operation. The input step + * #PSA_KEY_DERIVATION_INPUT_SECRET is used when placing the shared key + * material in the key derivation operation. + * + * The exact sequence of calls to perform a password-authenticated key + * exchange depends on the algorithm in use. Refer to the documentation of + * individual PAKE algorithm types (`PSA_ALG_XXX` values of type + * ::psa_algorithm_t such that #PSA_ALG_IS_PAKE(\c alg) is true) for more + * information. + * + * When this function returns successfully, \p operation becomes inactive. + * If this function returns an error status, both \p operation + * and \p key_derivation operations enter an error state and must be aborted by + * calling psa_pake_abort() and psa_key_derivation_abort() respectively. + * + * \param[in,out] operation Active PAKE operation. + * \param[out] output A key derivation operation that is ready + * for an input step of type + * #PSA_KEY_DERIVATION_INPUT_SECRET. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * #PSA_KEY_DERIVATION_INPUT_SECRET is not compatible with the + * algorithm in the \p output key derivation operation. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Input from a PAKE is not supported by the algorithm in the \p output + * key derivation operation. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The PAKE operation state is not valid (it must be active, but beyond + * that validity is specific to the algorithm), or + * the library has not been previously initialized by psa_crypto_init(), + * or the state of \p output is not valid for + * the #PSA_KEY_DERIVATION_INPUT_SECRET step. This can happen if the + * step is out of order or the application has done this step already + * and it may not be repeated. + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_get_implicit_key(psa_pake_operation_t *operation, + psa_key_derivation_operation_t *output); + +/** Abort a PAKE operation. + * + * Aborting an operation frees all associated resources except for the \c + * operation structure itself. Once aborted, the operation object can be reused + * for another operation by calling psa_pake_setup() again. + * + * This function may be called at any time after the operation + * object has been initialized as described in #psa_pake_operation_t. + * + * In particular, calling psa_pake_abort() after the operation has been + * terminated by a call to psa_pake_abort() or psa_pake_get_implicit_key() + * is safe and has no effect. + * + * \param[in,out] operation The operation to abort. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t psa_pake_abort(psa_pake_operation_t *operation); + +/**@}*/ + +/** A sufficient output buffer size for psa_pake_output(). + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_pake_output() will not fail due to an insufficient output buffer + * size. The actual size of the output might be smaller in any given call. + * + * See also #PSA_PAKE_OUTPUT_MAX_SIZE + * + * \param alg A PAKE algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_PAKE(\p alg) is true). + * \param primitive A primitive of type ::psa_pake_primitive_t that is + * compatible with algorithm \p alg. + * \param output_step A value of type ::psa_pake_step_t that is valid for the + * algorithm \p alg. + * \return A sufficient output buffer size for the specified + * PAKE algorithm, primitive, and output step. If the + * PAKE algorithm, primitive, or output step is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_PAKE_OUTPUT_SIZE(alg, primitive, output_step) \ + (alg == PSA_ALG_JPAKE && \ + primitive == PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, \ + PSA_ECC_FAMILY_SECP_R1, 256) ? \ + ( \ + output_step == PSA_PAKE_STEP_KEY_SHARE ? 65 : \ + output_step == PSA_PAKE_STEP_ZK_PUBLIC ? 65 : \ + 32 \ + ) : \ + 0) + +/** A sufficient input buffer size for psa_pake_input(). + * + * The value returned by this macro is guaranteed to be large enough for any + * valid input to psa_pake_input() in an operation with the specified + * parameters. + * + * See also #PSA_PAKE_INPUT_MAX_SIZE + * + * \param alg A PAKE algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_PAKE(\p alg) is true). + * \param primitive A primitive of type ::psa_pake_primitive_t that is + * compatible with algorithm \p alg. + * \param input_step A value of type ::psa_pake_step_t that is valid for the + * algorithm \p alg. + * \return A sufficient input buffer size for the specified + * input, cipher suite and algorithm. If the cipher suite, + * the input type or PAKE algorithm is not recognized, or + * the parameters are incompatible, return 0. + */ +#define PSA_PAKE_INPUT_SIZE(alg, primitive, input_step) \ + (alg == PSA_ALG_JPAKE && \ + primitive == PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, \ + PSA_ECC_FAMILY_SECP_R1, 256) ? \ + ( \ + input_step == PSA_PAKE_STEP_KEY_SHARE ? 65 : \ + input_step == PSA_PAKE_STEP_ZK_PUBLIC ? 65 : \ + 32 \ + ) : \ + 0) + +/** Output buffer size for psa_pake_output() for any of the supported PAKE + * algorithm and primitive suites and output step. + * + * This macro must expand to a compile-time constant integer. + * + * See also #PSA_PAKE_OUTPUT_SIZE(\p alg, \p primitive, \p step). + */ +#define PSA_PAKE_OUTPUT_MAX_SIZE 65 + +/** Input buffer size for psa_pake_input() for any of the supported PAKE + * algorithm and primitive suites and input step. + * + * This macro must expand to a compile-time constant integer. + * + * See also #PSA_PAKE_INPUT_SIZE(\p alg, \p primitive, \p step). + */ +#define PSA_PAKE_INPUT_MAX_SIZE 65 + +/** Returns a suitable initializer for a PAKE cipher suite object of type + * psa_pake_cipher_suite_t. + */ +#define PSA_PAKE_CIPHER_SUITE_INIT { PSA_ALG_NONE, 0, 0, 0, PSA_ALG_NONE } + +/** Returns a suitable initializer for a PAKE operation object of type + * psa_pake_operation_t. + */ +#define PSA_PAKE_OPERATION_INIT { 0, PSA_ALG_NONE, PSA_PAKE_OPERATION_STAGE_SETUP, \ + { 0 }, { { 0 } } } + +struct psa_pake_cipher_suite_s { + psa_algorithm_t algorithm; + psa_pake_primitive_type_t type; + psa_pake_family_t family; + uint16_t bits; + psa_algorithm_t hash; +}; + +static inline psa_algorithm_t psa_pake_cs_get_algorithm( + const psa_pake_cipher_suite_t *cipher_suite) +{ + return cipher_suite->algorithm; +} + +static inline void psa_pake_cs_set_algorithm( + psa_pake_cipher_suite_t *cipher_suite, + psa_algorithm_t algorithm) +{ + if (!PSA_ALG_IS_PAKE(algorithm)) { + cipher_suite->algorithm = 0; + } else { + cipher_suite->algorithm = algorithm; + } +} + +static inline psa_pake_primitive_t psa_pake_cs_get_primitive( + const psa_pake_cipher_suite_t *cipher_suite) +{ + return PSA_PAKE_PRIMITIVE(cipher_suite->type, cipher_suite->family, + cipher_suite->bits); +} + +static inline void psa_pake_cs_set_primitive( + psa_pake_cipher_suite_t *cipher_suite, + psa_pake_primitive_t primitive) +{ + cipher_suite->type = (psa_pake_primitive_type_t) (primitive >> 24); + cipher_suite->family = (psa_pake_family_t) (0xFF & (primitive >> 16)); + cipher_suite->bits = (uint16_t) (0xFFFF & primitive); +} + +static inline psa_pake_family_t psa_pake_cs_get_family( + const psa_pake_cipher_suite_t *cipher_suite) +{ + return cipher_suite->family; +} + +static inline uint16_t psa_pake_cs_get_bits( + const psa_pake_cipher_suite_t *cipher_suite) +{ + return cipher_suite->bits; +} + +static inline psa_algorithm_t psa_pake_cs_get_hash( + const psa_pake_cipher_suite_t *cipher_suite) +{ + return cipher_suite->hash; +} + +static inline void psa_pake_cs_set_hash(psa_pake_cipher_suite_t *cipher_suite, + psa_algorithm_t hash) +{ + if (!PSA_ALG_IS_HASH(hash)) { + cipher_suite->hash = 0; + } else { + cipher_suite->hash = hash; + } +} + +struct psa_crypto_driver_pake_inputs_s { + uint8_t *MBEDTLS_PRIVATE(password); + size_t MBEDTLS_PRIVATE(password_len); + psa_pake_role_t MBEDTLS_PRIVATE(role); + uint8_t *MBEDTLS_PRIVATE(user); + size_t MBEDTLS_PRIVATE(user_len); + uint8_t *MBEDTLS_PRIVATE(peer); + size_t MBEDTLS_PRIVATE(peer_len); + psa_key_attributes_t MBEDTLS_PRIVATE(attributes); + psa_pake_cipher_suite_t MBEDTLS_PRIVATE(cipher_suite); +}; + +typedef enum psa_jpake_step { + PSA_PAKE_STEP_INVALID = 0, + PSA_PAKE_STEP_X1_X2 = 1, + PSA_PAKE_STEP_X2S = 2, + PSA_PAKE_STEP_DERIVE = 3, +} psa_jpake_step_t; + +typedef enum psa_jpake_state { + PSA_PAKE_STATE_INVALID = 0, + PSA_PAKE_STATE_SETUP = 1, + PSA_PAKE_STATE_READY = 2, + PSA_PAKE_OUTPUT_X1_X2 = 3, + PSA_PAKE_OUTPUT_X2S = 4, + PSA_PAKE_INPUT_X1_X2 = 5, + PSA_PAKE_INPUT_X4S = 6, +} psa_jpake_state_t; + +typedef enum psa_jpake_sequence { + PSA_PAKE_SEQ_INVALID = 0, + PSA_PAKE_X1_STEP_KEY_SHARE = 1, /* also X2S & X4S KEY_SHARE */ + PSA_PAKE_X1_STEP_ZK_PUBLIC = 2, /* also X2S & X4S ZK_PUBLIC */ + PSA_PAKE_X1_STEP_ZK_PROOF = 3, /* also X2S & X4S ZK_PROOF */ + PSA_PAKE_X2_STEP_KEY_SHARE = 4, + PSA_PAKE_X2_STEP_ZK_PUBLIC = 5, + PSA_PAKE_X2_STEP_ZK_PROOF = 6, + PSA_PAKE_SEQ_END = 7, +} psa_jpake_sequence_t; + +typedef enum psa_crypto_driver_pake_step { + PSA_JPAKE_STEP_INVALID = 0, /* Invalid step */ + PSA_JPAKE_X1_STEP_KEY_SHARE = 1, /* Round 1: input/output key share (for ephemeral private key X1).*/ + PSA_JPAKE_X1_STEP_ZK_PUBLIC = 2, /* Round 1: input/output Schnorr NIZKP public key for the X1 key */ + PSA_JPAKE_X1_STEP_ZK_PROOF = 3, /* Round 1: input/output Schnorr NIZKP proof for the X1 key */ + PSA_JPAKE_X2_STEP_KEY_SHARE = 4, /* Round 1: input/output key share (for ephemeral private key X2).*/ + PSA_JPAKE_X2_STEP_ZK_PUBLIC = 5, /* Round 1: input/output Schnorr NIZKP public key for the X2 key */ + PSA_JPAKE_X2_STEP_ZK_PROOF = 6, /* Round 1: input/output Schnorr NIZKP proof for the X2 key */ + PSA_JPAKE_X2S_STEP_KEY_SHARE = 7, /* Round 2: output X2S key (our key) */ + PSA_JPAKE_X2S_STEP_ZK_PUBLIC = 8, /* Round 2: output Schnorr NIZKP public key for the X2S key (our key) */ + PSA_JPAKE_X2S_STEP_ZK_PROOF = 9, /* Round 2: output Schnorr NIZKP proof for the X2S key (our key) */ + PSA_JPAKE_X4S_STEP_KEY_SHARE = 10, /* Round 2: input X4S key (from peer) */ + PSA_JPAKE_X4S_STEP_ZK_PUBLIC = 11, /* Round 2: input Schnorr NIZKP public key for the X4S key (from peer) */ + PSA_JPAKE_X4S_STEP_ZK_PROOF = 12 /* Round 2: input Schnorr NIZKP proof for the X4S key (from peer) */ +} psa_crypto_driver_pake_step_t; + + +struct psa_jpake_computation_stage_s { + psa_jpake_state_t MBEDTLS_PRIVATE(state); + psa_jpake_sequence_t MBEDTLS_PRIVATE(sequence); + psa_jpake_step_t MBEDTLS_PRIVATE(input_step); + psa_jpake_step_t MBEDTLS_PRIVATE(output_step); +}; + +struct psa_pake_operation_s { + /** Unique ID indicating which driver got assigned to do the + * operation. Since driver contexts are driver-specific, swapping + * drivers halfway through the operation is not supported. + * ID values are auto-generated in psa_crypto_driver_wrappers.h + * ID value zero means the context is not valid or not assigned to + * any driver (i.e. none of the driver contexts are active). */ + unsigned int MBEDTLS_PRIVATE(id); + /* Algorithm of the PAKE operation */ + psa_algorithm_t MBEDTLS_PRIVATE(alg); + /* Stage of the PAKE operation: waiting for the setup, collecting inputs + * or computing. */ + uint8_t MBEDTLS_PRIVATE(stage); + /* Holds computation stage of the PAKE algorithms. */ + union { + uint8_t MBEDTLS_PRIVATE(dummy); +#if defined(PSA_WANT_ALG_JPAKE) + psa_jpake_computation_stage_t MBEDTLS_PRIVATE(jpake); +#endif + } MBEDTLS_PRIVATE(computation_stage); + union { + psa_driver_pake_context_t MBEDTLS_PRIVATE(ctx); + psa_crypto_driver_pake_inputs_t MBEDTLS_PRIVATE(inputs); + } MBEDTLS_PRIVATE(data); +}; + +static inline struct psa_pake_cipher_suite_s psa_pake_cipher_suite_init(void) +{ + const struct psa_pake_cipher_suite_s v = PSA_PAKE_CIPHER_SUITE_INIT; + return v; +} + +static inline struct psa_pake_operation_s psa_pake_operation_init(void) +{ + const struct psa_pake_operation_s v = PSA_PAKE_OPERATION_INIT; + return v; +} + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_CRYPTO_EXTRA_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_platform.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_platform.h new file mode 100644 index 00000000..e8d241bc --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_platform.h @@ -0,0 +1,103 @@ +/** + * \file psa/crypto_platform.h + * + * \brief PSA cryptography module: Mbed TLS platform definitions + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * This file contains platform-dependent type definitions. + * + * In implementations with isolation between the application and the + * cryptography module, implementers should take care to ensure that + * the definitions that are exposed to applications match what the + * module implements. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_PLATFORM_H +#define PSA_CRYPTO_PLATFORM_H +#include "mbedtls/private_access.h" + +/* Include the Mbed TLS configuration file, the way Mbed TLS does it + * in each of its header files. */ +#include "mbedtls/build_info.h" + +/* Translate between classic MBEDTLS_xxx feature symbols and PSA_xxx + * feature symbols. */ +#include "mbedtls/config_psa.h" + +/* PSA requires several types which C99 provides in stdint.h. */ +#include + +#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) + +/* Building for the PSA Crypto service on a PSA platform, a key owner is a PSA + * partition identifier. + * + * The function psa_its_identifier_of_slot() in psa_crypto_storage.c that + * translates a key identifier to a key storage file name assumes that + * mbedtls_key_owner_id_t is a 32-bit integer. This function thus needs + * reworking if mbedtls_key_owner_id_t is not defined as a 32-bit integer + * here anymore. + */ +typedef int32_t mbedtls_key_owner_id_t; + +/** Compare two key owner identifiers. + * + * \param id1 First key owner identifier. + * \param id2 Second key owner identifier. + * + * \return Non-zero if the two key owner identifiers are equal, zero otherwise. + */ +static inline int mbedtls_key_owner_id_equal(mbedtls_key_owner_id_t id1, + mbedtls_key_owner_id_t id2) +{ + return id1 == id2; +} + +#endif /* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */ + +/* + * When MBEDTLS_PSA_CRYPTO_SPM is defined, the code is being built for SPM + * (Secure Partition Manager) integration which separates the code into two + * parts: NSPE (Non-Secure Processing Environment) and SPE (Secure Processing + * Environment). When building for the SPE, an additional header file should be + * included. + */ +#if defined(MBEDTLS_PSA_CRYPTO_SPM) +#define PSA_CRYPTO_SECURE 1 +#include "crypto_spe.h" +#endif // MBEDTLS_PSA_CRYPTO_SPM + +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +/** The type of the context passed to mbedtls_psa_external_get_random(). + * + * Mbed TLS initializes the context to all-bits-zero before calling + * mbedtls_psa_external_get_random() for the first time. + * + * The definition of this type in the Mbed TLS source code is for + * demonstration purposes. Implementers of mbedtls_psa_external_get_random() + * are expected to replace it with a custom definition. + */ +typedef struct { + uintptr_t MBEDTLS_PRIVATE(opaque)[2]; +} mbedtls_psa_external_random_context_t; +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + +#endif /* PSA_CRYPTO_PLATFORM_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_se_driver.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_se_driver.h new file mode 100644 index 00000000..9ae631ff --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_se_driver.h @@ -0,0 +1,1395 @@ +/** + * \file psa/crypto_se_driver.h + * \brief PSA external cryptoprocessor driver module + * + * This header declares types and function signatures for cryptography + * drivers that access key material via opaque references. + * This is meant for cryptoprocessors that have a separate key storage from the + * space in which the PSA Crypto implementation runs, typically secure + * elements (SEs). + * + * This file is part of the PSA Crypto Driver HAL (hardware abstraction layer), + * containing functions for driver developers to implement to enable hardware + * to be called in a standardized way by a PSA Cryptography API + * implementation. The functions comprising the driver HAL, which driver + * authors implement, are not intended to be called by application developers. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PSA_CRYPTO_SE_DRIVER_H +#define PSA_CRYPTO_SE_DRIVER_H +#include "mbedtls/private_access.h" + +#include "crypto_driver_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup se_init Secure element driver initialization + */ +/**@{*/ + +/** \brief Driver context structure + * + * Driver functions receive a pointer to this structure. + * Each registered driver has one instance of this structure. + * + * Implementations must include the fields specified here and + * may include other fields. + */ +typedef struct { + /** A read-only pointer to the driver's persistent data. + * + * Drivers typically use this persistent data to keep track of + * which slot numbers are available. This is only a guideline: + * drivers may use the persistent data for any purpose, keeping + * in mind the restrictions on when the persistent data is saved + * to storage: the persistent data is only saved after calling + * certain functions that receive a writable pointer to the + * persistent data. + * + * The core allocates a memory buffer for the persistent data. + * The pointer is guaranteed to be suitably aligned for any data type, + * like a pointer returned by `malloc` (but the core can use any + * method to allocate the buffer, not necessarily `malloc`). + * + * The size of this buffer is in the \c persistent_data_size field of + * this structure. + * + * Before the driver is initialized for the first time, the content of + * the persistent data is all-bits-zero. After a driver upgrade, if the + * size of the persistent data has increased, the original data is padded + * on the right with zeros; if the size has decreased, the original data + * is truncated to the new size. + * + * This pointer is to read-only data. Only a few driver functions are + * allowed to modify the persistent data. These functions receive a + * writable pointer. These functions are: + * - psa_drv_se_t::p_init + * - psa_drv_se_key_management_t::p_allocate + * - psa_drv_se_key_management_t::p_destroy + * + * The PSA Cryptography core saves the persistent data from one + * session to the next. It does this before returning from API functions + * that call a driver method that is allowed to modify the persistent + * data, specifically: + * - psa_crypto_init() causes a call to psa_drv_se_t::p_init, and may call + * psa_drv_se_key_management_t::p_destroy to complete an action + * that was interrupted by a power failure. + * - Key creation functions cause a call to + * psa_drv_se_key_management_t::p_allocate, and may cause a call to + * psa_drv_se_key_management_t::p_destroy in case an error occurs. + * - psa_destroy_key() causes a call to + * psa_drv_se_key_management_t::p_destroy. + */ + const void *const MBEDTLS_PRIVATE(persistent_data); + + /** The size of \c persistent_data in bytes. + * + * This is always equal to the value of the `persistent_data_size` field + * of the ::psa_drv_se_t structure when the driver is registered. + */ + const size_t MBEDTLS_PRIVATE(persistent_data_size); + + /** Driver transient data. + * + * The core initializes this value to 0 and does not read or modify it + * afterwards. The driver may store whatever it wants in this field. + */ + uintptr_t MBEDTLS_PRIVATE(transient_data); +} psa_drv_se_context_t; + +/** \brief A driver initialization function. + * + * \param[in,out] drv_context The driver context structure. + * \param[in,out] persistent_data A pointer to the persistent data + * that allows writing. + * \param location The location value for which this driver + * is registered. The driver will be invoked + * for all keys whose lifetime is in this + * location. + * + * \retval #PSA_SUCCESS + * The driver is operational. + * The core will update the persistent data in storage. + * \return + * Any other return value prevents the driver from being used in + * this session. + * The core will NOT update the persistent data in storage. + */ +typedef psa_status_t (*psa_drv_se_init_t)(psa_drv_se_context_t *drv_context, + void *persistent_data, + psa_key_location_t location); + +#if defined(__DOXYGEN_ONLY__) || !defined(MBEDTLS_PSA_CRYPTO_SE_C) +/* Mbed Crypto with secure element support enabled defines this type in + * crypto_types.h because it is also visible to applications through an + * implementation-specific extension. + * For the PSA Cryptography specification, this type is only visible + * via crypto_se_driver.h. */ +/** An internal designation of a key slot between the core part of the + * PSA Crypto implementation and the driver. The meaning of this value + * is driver-dependent. */ +typedef uint64_t psa_key_slot_number_t; +#endif /* __DOXYGEN_ONLY__ || !MBEDTLS_PSA_CRYPTO_SE_C */ + +/**@}*/ + +/** \defgroup se_mac Secure Element Message Authentication Codes + * Generation and authentication of Message Authentication Codes (MACs) using + * a secure element can be done either as a single function call (via the + * `psa_drv_se_mac_generate_t` or `psa_drv_se_mac_verify_t` functions), or in + * parts using the following sequence: + * - `psa_drv_se_mac_setup_t` + * - `psa_drv_se_mac_update_t` + * - `psa_drv_se_mac_update_t` + * - ... + * - `psa_drv_se_mac_finish_t` or `psa_drv_se_mac_finish_verify_t` + * + * If a previously started secure element MAC operation needs to be terminated, + * it should be done so by the `psa_drv_se_mac_abort_t`. Failure to do so may + * result in allocated resources not being freed or in other undefined + * behavior. + */ +/**@{*/ +/** \brief A function that starts a secure element MAC operation for a PSA + * Crypto Driver implementation + * + * \param[in,out] drv_context The driver context structure. + * \param[in,out] op_context A structure that will contain the + * hardware-specific MAC context + * \param[in] key_slot The slot of the key to be used for the + * operation + * \param[in] algorithm The algorithm to be used to underly the MAC + * operation + * + * \retval #PSA_SUCCESS + * Success. + */ +typedef psa_status_t (*psa_drv_se_mac_setup_t)(psa_drv_se_context_t *drv_context, + void *op_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t algorithm); + +/** \brief A function that continues a previously started secure element MAC + * operation + * + * \param[in,out] op_context A hardware-specific structure for the + * previously-established MAC operation to be + * updated + * \param[in] p_input A buffer containing the message to be appended + * to the MAC operation + * \param[in] input_length The size in bytes of the input message buffer + */ +typedef psa_status_t (*psa_drv_se_mac_update_t)(void *op_context, + const uint8_t *p_input, + size_t input_length); + +/** \brief a function that completes a previously started secure element MAC + * operation by returning the resulting MAC. + * + * \param[in,out] op_context A hardware-specific structure for the + * previously started MAC operation to be + * finished + * \param[out] p_mac A buffer where the generated MAC will be + * placed + * \param[in] mac_size The size in bytes of the buffer that has been + * allocated for the `output` buffer + * \param[out] p_mac_length After completion, will contain the number of + * bytes placed in the `p_mac` buffer + * + * \retval #PSA_SUCCESS + * Success. + */ +typedef psa_status_t (*psa_drv_se_mac_finish_t)(void *op_context, + uint8_t *p_mac, + size_t mac_size, + size_t *p_mac_length); + +/** \brief A function that completes a previously started secure element MAC + * operation by comparing the resulting MAC against a provided value + * + * \param[in,out] op_context A hardware-specific structure for the previously + * started MAC operation to be finished + * \param[in] p_mac The MAC value against which the resulting MAC + * will be compared against + * \param[in] mac_length The size in bytes of the value stored in `p_mac` + * + * \retval #PSA_SUCCESS + * The operation completed successfully and the MACs matched each + * other + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The operation completed successfully, but the calculated MAC did + * not match the provided MAC + */ +typedef psa_status_t (*psa_drv_se_mac_finish_verify_t)(void *op_context, + const uint8_t *p_mac, + size_t mac_length); + +/** \brief A function that aborts a previous started secure element MAC + * operation + * + * \param[in,out] op_context A hardware-specific structure for the previously + * started MAC operation to be aborted + */ +typedef psa_status_t (*psa_drv_se_mac_abort_t)(void *op_context); + +/** \brief A function that performs a secure element MAC operation in one + * command and returns the calculated MAC + * + * \param[in,out] drv_context The driver context structure. + * \param[in] p_input A buffer containing the message to be MACed + * \param[in] input_length The size in bytes of `p_input` + * \param[in] key_slot The slot of the key to be used + * \param[in] alg The algorithm to be used to underlie the MAC + * operation + * \param[out] p_mac A buffer where the generated MAC will be + * placed + * \param[in] mac_size The size in bytes of the `p_mac` buffer + * \param[out] p_mac_length After completion, will contain the number of + * bytes placed in the `output` buffer + * + * \retval #PSA_SUCCESS + * Success. + */ +typedef psa_status_t (*psa_drv_se_mac_generate_t)(psa_drv_se_context_t *drv_context, + const uint8_t *p_input, + size_t input_length, + psa_key_slot_number_t key_slot, + psa_algorithm_t alg, + uint8_t *p_mac, + size_t mac_size, + size_t *p_mac_length); + +/** \brief A function that performs a secure element MAC operation in one + * command and compares the resulting MAC against a provided value + * + * \param[in,out] drv_context The driver context structure. + * \param[in] p_input A buffer containing the message to be MACed + * \param[in] input_length The size in bytes of `input` + * \param[in] key_slot The slot of the key to be used + * \param[in] alg The algorithm to be used to underlie the MAC + * operation + * \param[in] p_mac The MAC value against which the resulting MAC will + * be compared against + * \param[in] mac_length The size in bytes of `mac` + * + * \retval #PSA_SUCCESS + * The operation completed successfully and the MACs matched each + * other + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The operation completed successfully, but the calculated MAC did + * not match the provided MAC + */ +typedef psa_status_t (*psa_drv_se_mac_verify_t)(psa_drv_se_context_t *drv_context, + const uint8_t *p_input, + size_t input_length, + psa_key_slot_number_t key_slot, + psa_algorithm_t alg, + const uint8_t *p_mac, + size_t mac_length); + +/** \brief A struct containing all of the function pointers needed to + * perform secure element MAC operations + * + * PSA Crypto API implementations should populate the table as appropriate + * upon startup. + * + * If one of the functions is not implemented (such as + * `psa_drv_se_mac_generate_t`), it should be set to NULL. + * + * Driver implementers should ensure that they implement all of the functions + * that make sense for their hardware, and that they provide a full solution + * (for example, if they support `p_setup`, they should also support + * `p_update` and at least one of `p_finish` or `p_finish_verify`). + * + */ +typedef struct { + /**The size in bytes of the hardware-specific secure element MAC context + * structure + */ + size_t MBEDTLS_PRIVATE(context_size); + /** Function that performs a MAC setup operation + */ + psa_drv_se_mac_setup_t MBEDTLS_PRIVATE(p_setup); + /** Function that performs a MAC update operation + */ + psa_drv_se_mac_update_t MBEDTLS_PRIVATE(p_update); + /** Function that completes a MAC operation + */ + psa_drv_se_mac_finish_t MBEDTLS_PRIVATE(p_finish); + /** Function that completes a MAC operation with a verify check + */ + psa_drv_se_mac_finish_verify_t MBEDTLS_PRIVATE(p_finish_verify); + /** Function that aborts a previously started MAC operation + */ + psa_drv_se_mac_abort_t MBEDTLS_PRIVATE(p_abort); + /** Function that performs a MAC operation in one call + */ + psa_drv_se_mac_generate_t MBEDTLS_PRIVATE(p_mac); + /** Function that performs a MAC and verify operation in one call + */ + psa_drv_se_mac_verify_t MBEDTLS_PRIVATE(p_mac_verify); +} psa_drv_se_mac_t; +/**@}*/ + +/** \defgroup se_cipher Secure Element Symmetric Ciphers + * + * Encryption and Decryption using secure element keys in block modes other + * than ECB must be done in multiple parts, using the following flow: + * - `psa_drv_se_cipher_setup_t` + * - `psa_drv_se_cipher_set_iv_t` (optional depending upon block mode) + * - `psa_drv_se_cipher_update_t` + * - `psa_drv_se_cipher_update_t` + * - ... + * - `psa_drv_se_cipher_finish_t` + * + * If a previously started secure element Cipher operation needs to be + * terminated, it should be done so by the `psa_drv_se_cipher_abort_t`. Failure + * to do so may result in allocated resources not being freed or in other + * undefined behavior. + * + * In situations where a PSA Cryptographic API implementation is using a block + * mode not-supported by the underlying hardware or driver, it can construct + * the block mode itself, while calling the `psa_drv_se_cipher_ecb_t` function + * for the cipher operations. + */ +/**@{*/ + +/** \brief A function that provides the cipher setup function for a + * secure element driver + * + * \param[in,out] drv_context The driver context structure. + * \param[in,out] op_context A structure that will contain the + * hardware-specific cipher context. + * \param[in] key_slot The slot of the key to be used for the + * operation + * \param[in] algorithm The algorithm to be used in the cipher + * operation + * \param[in] direction Indicates whether the operation is an encrypt + * or decrypt + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + */ +typedef psa_status_t (*psa_drv_se_cipher_setup_t)(psa_drv_se_context_t *drv_context, + void *op_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t algorithm, + psa_encrypt_or_decrypt_t direction); + +/** \brief A function that sets the initialization vector (if + * necessary) for a secure element cipher operation + * + * Rationale: The `psa_se_cipher_*` operation in the PSA Cryptographic API has + * two IV functions: one to set the IV, and one to generate it internally. The + * generate function is not necessary for the drivers to implement as the PSA + * Crypto implementation can do the generation using its RNG features. + * + * \param[in,out] op_context A structure that contains the previously set up + * hardware-specific cipher context + * \param[in] p_iv A buffer containing the initialization vector + * \param[in] iv_length The size (in bytes) of the `p_iv` buffer + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_cipher_set_iv_t)(void *op_context, + const uint8_t *p_iv, + size_t iv_length); + +/** \brief A function that continues a previously started secure element cipher + * operation + * + * \param[in,out] op_context A hardware-specific structure for the + * previously started cipher operation + * \param[in] p_input A buffer containing the data to be + * encrypted/decrypted + * \param[in] input_size The size in bytes of the buffer pointed to + * by `p_input` + * \param[out] p_output The caller-allocated buffer where the + * output will be placed + * \param[in] output_size The allocated size in bytes of the + * `p_output` buffer + * \param[out] p_output_length After completion, will contain the number + * of bytes placed in the `p_output` buffer + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_cipher_update_t)(void *op_context, + const uint8_t *p_input, + size_t input_size, + uint8_t *p_output, + size_t output_size, + size_t *p_output_length); + +/** \brief A function that completes a previously started secure element cipher + * operation + * + * \param[in,out] op_context A hardware-specific structure for the + * previously started cipher operation + * \param[out] p_output The caller-allocated buffer where the output + * will be placed + * \param[in] output_size The allocated size in bytes of the `p_output` + * buffer + * \param[out] p_output_length After completion, will contain the number of + * bytes placed in the `p_output` buffer + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_cipher_finish_t)(void *op_context, + uint8_t *p_output, + size_t output_size, + size_t *p_output_length); + +/** \brief A function that aborts a previously started secure element cipher + * operation + * + * \param[in,out] op_context A hardware-specific structure for the + * previously started cipher operation + */ +typedef psa_status_t (*psa_drv_se_cipher_abort_t)(void *op_context); + +/** \brief A function that performs the ECB block mode for secure element + * cipher operations + * + * Note: this function should only be used with implementations that do not + * provide a needed higher-level operation. + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key_slot The slot of the key to be used for the operation + * \param[in] algorithm The algorithm to be used in the cipher operation + * \param[in] direction Indicates whether the operation is an encrypt or + * decrypt + * \param[in] p_input A buffer containing the data to be + * encrypted/decrypted + * \param[in] input_size The size in bytes of the buffer pointed to by + * `p_input` + * \param[out] p_output The caller-allocated buffer where the output + * will be placed + * \param[in] output_size The allocated size in bytes of the `p_output` + * buffer + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + */ +typedef psa_status_t (*psa_drv_se_cipher_ecb_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t algorithm, + psa_encrypt_or_decrypt_t direction, + const uint8_t *p_input, + size_t input_size, + uint8_t *p_output, + size_t output_size); + +/** + * \brief A struct containing all of the function pointers needed to implement + * cipher operations using secure elements. + * + * PSA Crypto API implementations should populate instances of the table as + * appropriate upon startup or at build time. + * + * If one of the functions is not implemented (such as + * `psa_drv_se_cipher_ecb_t`), it should be set to NULL. + */ +typedef struct { + /** The size in bytes of the hardware-specific secure element cipher + * context structure + */ + size_t MBEDTLS_PRIVATE(context_size); + /** Function that performs a cipher setup operation */ + psa_drv_se_cipher_setup_t MBEDTLS_PRIVATE(p_setup); + /** Function that sets a cipher IV (if necessary) */ + psa_drv_se_cipher_set_iv_t MBEDTLS_PRIVATE(p_set_iv); + /** Function that performs a cipher update operation */ + psa_drv_se_cipher_update_t MBEDTLS_PRIVATE(p_update); + /** Function that completes a cipher operation */ + psa_drv_se_cipher_finish_t MBEDTLS_PRIVATE(p_finish); + /** Function that aborts a cipher operation */ + psa_drv_se_cipher_abort_t MBEDTLS_PRIVATE(p_abort); + /** Function that performs ECB mode for a cipher operation + * (Danger: ECB mode should not be used directly by clients of the PSA + * Crypto Client API) + */ + psa_drv_se_cipher_ecb_t MBEDTLS_PRIVATE(p_ecb); +} psa_drv_se_cipher_t; + +/**@}*/ + +/** \defgroup se_asymmetric Secure Element Asymmetric Cryptography + * + * Since the amount of data that can (or should) be encrypted or signed using + * asymmetric keys is limited by the key size, asymmetric key operations using + * keys in a secure element must be done in single function calls. + */ +/**@{*/ + +/** + * \brief A function that signs a hash or short message with a private key in + * a secure element + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key_slot Key slot of an asymmetric key pair + * \param[in] alg A signature algorithm that is compatible + * with the type of `key` + * \param[in] p_hash The hash to sign + * \param[in] hash_length Size of the `p_hash` buffer in bytes + * \param[out] p_signature Buffer where the signature is to be written + * \param[in] signature_size Size of the `p_signature` buffer in bytes + * \param[out] p_signature_length On success, the number of bytes + * that make up the returned signature value + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_asymmetric_sign_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t alg, + const uint8_t *p_hash, + size_t hash_length, + uint8_t *p_signature, + size_t signature_size, + size_t *p_signature_length); + +/** + * \brief A function that verifies the signature a hash or short message using + * an asymmetric public key in a secure element + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key_slot Key slot of a public key or an asymmetric key + * pair + * \param[in] alg A signature algorithm that is compatible with + * the type of `key` + * \param[in] p_hash The hash whose signature is to be verified + * \param[in] hash_length Size of the `p_hash` buffer in bytes + * \param[in] p_signature Buffer containing the signature to verify + * \param[in] signature_length Size of the `p_signature` buffer in bytes + * + * \retval #PSA_SUCCESS + * The signature is valid. + */ +typedef psa_status_t (*psa_drv_se_asymmetric_verify_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t alg, + const uint8_t *p_hash, + size_t hash_length, + const uint8_t *p_signature, + size_t signature_length); + +/** + * \brief A function that encrypts a short message with an asymmetric public + * key in a secure element + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key_slot Key slot of a public key or an asymmetric key + * pair + * \param[in] alg An asymmetric encryption algorithm that is + * compatible with the type of `key` + * \param[in] p_input The message to encrypt + * \param[in] input_length Size of the `p_input` buffer in bytes + * \param[in] p_salt A salt or label, if supported by the + * encryption algorithm + * If the algorithm does not support a + * salt, pass `NULL`. + * If the algorithm supports an optional + * salt and you do not want to pass a salt, + * pass `NULL`. + * For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is + * supported. + * \param[in] salt_length Size of the `p_salt` buffer in bytes + * If `p_salt` is `NULL`, pass 0. + * \param[out] p_output Buffer where the encrypted message is to + * be written + * \param[in] output_size Size of the `p_output` buffer in bytes + * \param[out] p_output_length On success, the number of bytes that make up + * the returned output + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_asymmetric_encrypt_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t alg, + const uint8_t *p_input, + size_t input_length, + const uint8_t *p_salt, + size_t salt_length, + uint8_t *p_output, + size_t output_size, + size_t *p_output_length); + +/** + * \brief A function that decrypts a short message with an asymmetric private + * key in a secure element. + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key_slot Key slot of an asymmetric key pair + * \param[in] alg An asymmetric encryption algorithm that is + * compatible with the type of `key` + * \param[in] p_input The message to decrypt + * \param[in] input_length Size of the `p_input` buffer in bytes + * \param[in] p_salt A salt or label, if supported by the + * encryption algorithm + * If the algorithm does not support a + * salt, pass `NULL`. + * If the algorithm supports an optional + * salt and you do not want to pass a salt, + * pass `NULL`. + * For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is + * supported. + * \param[in] salt_length Size of the `p_salt` buffer in bytes + * If `p_salt` is `NULL`, pass 0. + * \param[out] p_output Buffer where the decrypted message is to + * be written + * \param[in] output_size Size of the `p_output` buffer in bytes + * \param[out] p_output_length On success, the number of bytes + * that make up the returned output + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_asymmetric_decrypt_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t alg, + const uint8_t *p_input, + size_t input_length, + const uint8_t *p_salt, + size_t salt_length, + uint8_t *p_output, + size_t output_size, + size_t *p_output_length); + +/** + * \brief A struct containing all of the function pointers needed to implement + * asymmetric cryptographic operations using secure elements. + * + * PSA Crypto API implementations should populate instances of the table as + * appropriate upon startup or at build time. + * + * If one of the functions is not implemented, it should be set to NULL. + */ +typedef struct { + /** Function that performs an asymmetric sign operation */ + psa_drv_se_asymmetric_sign_t MBEDTLS_PRIVATE(p_sign); + /** Function that performs an asymmetric verify operation */ + psa_drv_se_asymmetric_verify_t MBEDTLS_PRIVATE(p_verify); + /** Function that performs an asymmetric encrypt operation */ + psa_drv_se_asymmetric_encrypt_t MBEDTLS_PRIVATE(p_encrypt); + /** Function that performs an asymmetric decrypt operation */ + psa_drv_se_asymmetric_decrypt_t MBEDTLS_PRIVATE(p_decrypt); +} psa_drv_se_asymmetric_t; + +/**@}*/ + +/** \defgroup se_aead Secure Element Authenticated Encryption with Additional Data + * Authenticated Encryption with Additional Data (AEAD) operations with secure + * elements must be done in one function call. While this creates a burden for + * implementers as there must be sufficient space in memory for the entire + * message, it prevents decrypted data from being made available before the + * authentication operation is complete and the data is known to be authentic. + */ +/**@{*/ + +/** \brief A function that performs a secure element authenticated encryption + * operation + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key_slot Slot containing the key to use. + * \param[in] algorithm The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(`alg`) is true) + * \param[in] p_nonce Nonce or IV to use + * \param[in] nonce_length Size of the `p_nonce` buffer in bytes + * \param[in] p_additional_data Additional data that will be + * authenticated but not encrypted + * \param[in] additional_data_length Size of `p_additional_data` in bytes + * \param[in] p_plaintext Data that will be authenticated and + * encrypted + * \param[in] plaintext_length Size of `p_plaintext` in bytes + * \param[out] p_ciphertext Output buffer for the authenticated and + * encrypted data. The additional data is + * not part of this output. For algorithms + * where the encrypted data and the + * authentication tag are defined as + * separate outputs, the authentication + * tag is appended to the encrypted data. + * \param[in] ciphertext_size Size of the `p_ciphertext` buffer in + * bytes + * \param[out] p_ciphertext_length On success, the size of the output in + * the `p_ciphertext` buffer + * + * \retval #PSA_SUCCESS + * Success. + */ +typedef psa_status_t (*psa_drv_se_aead_encrypt_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t algorithm, + const uint8_t *p_nonce, + size_t nonce_length, + const uint8_t *p_additional_data, + size_t additional_data_length, + const uint8_t *p_plaintext, + size_t plaintext_length, + uint8_t *p_ciphertext, + size_t ciphertext_size, + size_t *p_ciphertext_length); + +/** A function that performs a secure element authenticated decryption operation + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key_slot Slot containing the key to use + * \param[in] algorithm The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(`alg`) is true) + * \param[in] p_nonce Nonce or IV to use + * \param[in] nonce_length Size of the `p_nonce` buffer in bytes + * \param[in] p_additional_data Additional data that has been + * authenticated but not encrypted + * \param[in] additional_data_length Size of `p_additional_data` in bytes + * \param[in] p_ciphertext Data that has been authenticated and + * encrypted. + * For algorithms where the encrypted data + * and the authentication tag are defined + * as separate inputs, the buffer must + * contain the encrypted data followed by + * the authentication tag. + * \param[in] ciphertext_length Size of `p_ciphertext` in bytes + * \param[out] p_plaintext Output buffer for the decrypted data + * \param[in] plaintext_size Size of the `p_plaintext` buffer in + * bytes + * \param[out] p_plaintext_length On success, the size of the output in + * the `p_plaintext` buffer + * + * \retval #PSA_SUCCESS + * Success. + */ +typedef psa_status_t (*psa_drv_se_aead_decrypt_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + psa_algorithm_t algorithm, + const uint8_t *p_nonce, + size_t nonce_length, + const uint8_t *p_additional_data, + size_t additional_data_length, + const uint8_t *p_ciphertext, + size_t ciphertext_length, + uint8_t *p_plaintext, + size_t plaintext_size, + size_t *p_plaintext_length); + +/** + * \brief A struct containing all of the function pointers needed to implement + * secure element Authenticated Encryption with Additional Data operations + * + * PSA Crypto API implementations should populate instances of the table as + * appropriate upon startup. + * + * If one of the functions is not implemented, it should be set to NULL. + */ +typedef struct { + /** Function that performs the AEAD encrypt operation */ + psa_drv_se_aead_encrypt_t MBEDTLS_PRIVATE(p_encrypt); + /** Function that performs the AEAD decrypt operation */ + psa_drv_se_aead_decrypt_t MBEDTLS_PRIVATE(p_decrypt); +} psa_drv_se_aead_t; +/**@}*/ + +/** \defgroup se_key_management Secure Element Key Management + * Currently, key management is limited to importing keys in the clear, + * destroying keys, and exporting keys in the clear. + * Whether a key may be exported is determined by the key policies in place + * on the key slot. + */ +/**@{*/ + +/** An enumeration indicating how a key is created. + */ +typedef enum { + PSA_KEY_CREATION_IMPORT, /**< During psa_import_key() */ + PSA_KEY_CREATION_GENERATE, /**< During psa_generate_key() */ + PSA_KEY_CREATION_DERIVE, /**< During psa_key_derivation_output_key() */ + PSA_KEY_CREATION_COPY, /**< During psa_copy_key() */ + +#ifndef __DOXYGEN_ONLY__ + /** A key is being registered with mbedtls_psa_register_se_key(). + * + * The core only passes this value to + * psa_drv_se_key_management_t::p_validate_slot_number, not to + * psa_drv_se_key_management_t::p_allocate. The call to + * `p_validate_slot_number` is not followed by any other call to the + * driver: the key is considered successfully registered if the call to + * `p_validate_slot_number` succeeds, or if `p_validate_slot_number` is + * null. + * + * With this creation method, the driver must return #PSA_SUCCESS if + * the given attributes are compatible with the existing key in the slot, + * and #PSA_ERROR_DOES_NOT_EXIST if the driver can determine that there + * is no key with the specified slot number. + * + * This is an Mbed Crypto extension. + */ + PSA_KEY_CREATION_REGISTER, +#endif +} psa_key_creation_method_t; + +/** \brief A function that allocates a slot for a key. + * + * To create a key in a specific slot in a secure element, the core + * first calls this function to determine a valid slot number, + * then calls a function to create the key material in that slot. + * In nominal conditions (that is, if no error occurs), + * the effect of a call to a key creation function in the PSA Cryptography + * API with a lifetime that places the key in a secure element is the + * following: + * -# The core calls psa_drv_se_key_management_t::p_allocate + * (or in some implementations + * psa_drv_se_key_management_t::p_validate_slot_number). The driver + * selects (or validates) a suitable slot number given the key attributes + * and the state of the secure element. + * -# The core calls a key creation function in the driver. + * + * The key creation functions in the PSA Cryptography API are: + * - psa_import_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_IMPORT + * then a call to psa_drv_se_key_management_t::p_import. + * - psa_generate_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_GENERATE + * then a call to psa_drv_se_key_management_t::p_import. + * - psa_key_derivation_output_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_DERIVE + * then a call to psa_drv_se_key_derivation_t::p_derive. + * - psa_copy_key(), which causes + * a call to `p_allocate` with \p method = #PSA_KEY_CREATION_COPY + * then a call to psa_drv_se_key_management_t::p_export. + * + * In case of errors, other behaviors are possible. + * - If the PSA Cryptography subsystem dies after the first step, + * for example because the device has lost power abruptly, + * the second step may never happen, or may happen after a reset + * and re-initialization. Alternatively, after a reset and + * re-initialization, the core may call + * psa_drv_se_key_management_t::p_destroy on the slot number that + * was allocated (or validated) instead of calling a key creation function. + * - If an error occurs, the core may call + * psa_drv_se_key_management_t::p_destroy on the slot number that + * was allocated (or validated) instead of calling a key creation function. + * + * Errors and system resets also have an impact on the driver's persistent + * data. If a reset happens before the overall key creation process is + * completed (before or after the second step above), it is unspecified + * whether the persistent data after the reset is identical to what it + * was before or after the call to `p_allocate` (or `p_validate_slot_number`). + * + * \param[in,out] drv_context The driver context structure. + * \param[in,out] persistent_data A pointer to the persistent data + * that allows writing. + * \param[in] attributes Attributes of the key. + * \param method The way in which the key is being created. + * \param[out] key_slot Slot where the key will be stored. + * This must be a valid slot for a key of the + * chosen type. It must be unoccupied. + * + * \retval #PSA_SUCCESS + * Success. + * The core will record \c *key_slot as the key slot where the key + * is stored and will update the persistent data in storage. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + */ +typedef psa_status_t (*psa_drv_se_allocate_key_t)( + psa_drv_se_context_t *drv_context, + void *persistent_data, + const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, + psa_key_slot_number_t *key_slot); + +/** \brief A function that determines whether a slot number is valid + * for a key. + * + * To create a key in a specific slot in a secure element, the core + * first calls this function to validate the choice of slot number, + * then calls a function to create the key material in that slot. + * See the documentation of #psa_drv_se_allocate_key_t for more details. + * + * As of the PSA Cryptography API specification version 1.0, there is no way + * for applications to trigger a call to this function. However some + * implementations offer the capability to create or declare a key in + * a specific slot via implementation-specific means, generally for the + * sake of initial device provisioning or onboarding. Such a mechanism may + * be added to a future version of the PSA Cryptography API specification. + * + * This function may update the driver's persistent data through + * \p persistent_data. The core will save the updated persistent data at the + * end of the key creation process. See the description of + * ::psa_drv_se_allocate_key_t for more information. + * + * \param[in,out] drv_context The driver context structure. + * \param[in,out] persistent_data A pointer to the persistent data + * that allows writing. + * \param[in] attributes Attributes of the key. + * \param method The way in which the key is being created. + * \param[in] key_slot Slot where the key is to be stored. + * + * \retval #PSA_SUCCESS + * The given slot number is valid for a key with the given + * attributes. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The given slot number is not valid for a key with the + * given attributes. This includes the case where the slot + * number is not valid at all. + * \retval #PSA_ERROR_ALREADY_EXISTS + * There is already a key with the specified slot number. + * Drivers may choose to return this error from the key + * creation function instead. + */ +typedef psa_status_t (*psa_drv_se_validate_slot_number_t)( + psa_drv_se_context_t *drv_context, + void *persistent_data, + const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, + psa_key_slot_number_t key_slot); + +/** \brief A function that imports a key into a secure element in binary format + * + * This function can support any output from psa_export_key(). Refer to the + * documentation of psa_export_key() for the format for each key type. + * + * \param[in,out] drv_context The driver context structure. + * \param key_slot Slot where the key will be stored. + * This must be a valid slot for a key of the + * chosen type. It must be unoccupied. + * \param[in] attributes The key attributes, including the lifetime, + * the key type and the usage policy. + * Drivers should not access the key size stored + * in the attributes: it may not match the + * data passed in \p data. + * Drivers can call psa_get_key_lifetime(), + * psa_get_key_type(), + * psa_get_key_usage_flags() and + * psa_get_key_algorithm() to access this + * information. + * \param[in] data Buffer containing the key data. + * \param[in] data_length Size of the \p data buffer in bytes. + * \param[out] bits On success, the key size in bits. The driver + * must determine this value after parsing the + * key according to the key type. + * This value is not used if the function fails. + * + * \retval #PSA_SUCCESS + * Success. + */ +typedef psa_status_t (*psa_drv_se_import_key_t)( + psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + const psa_key_attributes_t *attributes, + const uint8_t *data, + size_t data_length, + size_t *bits); + +/** + * \brief A function that destroys a secure element key and restore the slot to + * its default state + * + * This function destroys the content of the key from a secure element. + * Implementations shall make a best effort to ensure that any previous content + * of the slot is unrecoverable. + * + * This function returns the specified slot to its default state. + * + * \param[in,out] drv_context The driver context structure. + * \param[in,out] persistent_data A pointer to the persistent data + * that allows writing. + * \param key_slot The key slot to erase. + * + * \retval #PSA_SUCCESS + * The slot's content, if any, has been erased. + */ +typedef psa_status_t (*psa_drv_se_destroy_key_t)( + psa_drv_se_context_t *drv_context, + void *persistent_data, + psa_key_slot_number_t key_slot); + +/** + * \brief A function that exports a secure element key in binary format + * + * The output of this function can be passed to psa_import_key() to + * create an equivalent object. + * + * If a key is created with `psa_import_key()` and then exported with + * this function, it is not guaranteed that the resulting data is + * identical: the implementation may choose a different representation + * of the same key if the format permits it. + * + * This function should generate output in the same format that + * `psa_export_key()` does. Refer to the + * documentation of `psa_export_key()` for the format for each key type. + * + * \param[in,out] drv_context The driver context structure. + * \param[in] key Slot whose content is to be exported. This must + * be an occupied key slot. + * \param[out] p_data Buffer where the key data is to be written. + * \param[in] data_size Size of the `p_data` buffer in bytes. + * \param[out] p_data_length On success, the number of bytes + * that make up the key data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +typedef psa_status_t (*psa_drv_se_export_key_t)(psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key, + uint8_t *p_data, + size_t data_size, + size_t *p_data_length); + +/** + * \brief A function that generates a symmetric or asymmetric key on a secure + * element + * + * If the key type \c type recorded in \p attributes + * is asymmetric (#PSA_KEY_TYPE_IS_ASYMMETRIC(\c type) = 1), + * the driver may export the public key at the time of generation, + * in the format documented for psa_export_public_key() by writing it + * to the \p pubkey buffer. + * This is optional, intended for secure elements that output the + * public key at generation time and that cannot export the public key + * later. Drivers that do not need this feature should leave + * \p *pubkey_length set to 0 and should + * implement the psa_drv_key_management_t::p_export_public function. + * Some implementations do not support this feature, in which case + * \p pubkey is \c NULL and \p pubkey_size is 0. + * + * \param[in,out] drv_context The driver context structure. + * \param key_slot Slot where the key will be stored. + * This must be a valid slot for a key of the + * chosen type. It must be unoccupied. + * \param[in] attributes The key attributes, including the lifetime, + * the key type and size, and the usage policy. + * Drivers can call psa_get_key_lifetime(), + * psa_get_key_type(), psa_get_key_bits(), + * psa_get_key_usage_flags() and + * psa_get_key_algorithm() to access this + * information. + * \param[out] pubkey A buffer where the driver can write the + * public key, when generating an asymmetric + * key pair. + * This is \c NULL when generating a symmetric + * key or if the core does not support + * exporting the public key at generation time. + * \param pubkey_size The size of the `pubkey` buffer in bytes. + * This is 0 when generating a symmetric + * key or if the core does not support + * exporting the public key at generation time. + * \param[out] pubkey_length On entry, this is always 0. + * On success, the number of bytes written to + * \p pubkey. If this is 0 or unchanged on return, + * the core will not read the \p pubkey buffer, + * and will instead call the driver's + * psa_drv_key_management_t::p_export_public + * function to export the public key when needed. + */ +typedef psa_status_t (*psa_drv_se_generate_key_t)( + psa_drv_se_context_t *drv_context, + psa_key_slot_number_t key_slot, + const psa_key_attributes_t *attributes, + uint8_t *pubkey, size_t pubkey_size, size_t *pubkey_length); + +/** + * \brief A struct containing all of the function pointers needed to for secure + * element key management + * + * PSA Crypto API implementations should populate instances of the table as + * appropriate upon startup or at build time. + * + * If one of the functions is not implemented, it should be set to NULL. + */ +typedef struct { + /** Function that allocates a slot for a key. */ + psa_drv_se_allocate_key_t MBEDTLS_PRIVATE(p_allocate); + /** Function that checks the validity of a slot for a key. */ + psa_drv_se_validate_slot_number_t MBEDTLS_PRIVATE(p_validate_slot_number); + /** Function that performs a key import operation */ + psa_drv_se_import_key_t MBEDTLS_PRIVATE(p_import); + /** Function that performs a generation */ + psa_drv_se_generate_key_t MBEDTLS_PRIVATE(p_generate); + /** Function that performs a key destroy operation */ + psa_drv_se_destroy_key_t MBEDTLS_PRIVATE(p_destroy); + /** Function that performs a key export operation */ + psa_drv_se_export_key_t MBEDTLS_PRIVATE(p_export); + /** Function that performs a public key export operation */ + psa_drv_se_export_key_t MBEDTLS_PRIVATE(p_export_public); +} psa_drv_se_key_management_t; + +/**@}*/ + +/** \defgroup driver_derivation Secure Element Key Derivation and Agreement + * Key derivation is the process of generating new key material using an + * existing key and additional parameters, iterating through a basic + * cryptographic function, such as a hash. + * Key agreement is a part of cryptographic protocols that allows two parties + * to agree on the same key value, but starting from different original key + * material. + * The flows are similar, and the PSA Crypto Driver Model uses the same functions + * for both of the flows. + * + * There are two different final functions for the flows, + * `psa_drv_se_key_derivation_derive` and `psa_drv_se_key_derivation_export`. + * `psa_drv_se_key_derivation_derive` is used when the key material should be + * placed in a slot on the hardware and not exposed to the caller. + * `psa_drv_se_key_derivation_export` is used when the key material should be + * returned to the PSA Cryptographic API implementation. + * + * Different key derivation algorithms require a different number of inputs. + * Instead of having an API that takes as input variable length arrays, which + * can be problematic to manage on embedded platforms, the inputs are passed + * to the driver via a function, `psa_drv_se_key_derivation_collateral`, that + * is called multiple times with different `collateral_id`s. Thus, for a key + * derivation algorithm that required 3 parameter inputs, the flow would look + * something like: + * ~~~~~~~~~~~~~{.c} + * psa_drv_se_key_derivation_setup(kdf_algorithm, source_key, dest_key_size_bytes); + * psa_drv_se_key_derivation_collateral(kdf_algorithm_collateral_id_0, + * p_collateral_0, + * collateral_0_size); + * psa_drv_se_key_derivation_collateral(kdf_algorithm_collateral_id_1, + * p_collateral_1, + * collateral_1_size); + * psa_drv_se_key_derivation_collateral(kdf_algorithm_collateral_id_2, + * p_collateral_2, + * collateral_2_size); + * psa_drv_se_key_derivation_derive(); + * ~~~~~~~~~~~~~ + * + * key agreement example: + * ~~~~~~~~~~~~~{.c} + * psa_drv_se_key_derivation_setup(alg, source_key. dest_key_size_bytes); + * psa_drv_se_key_derivation_collateral(DHE_PUBKEY, p_pubkey, pubkey_size); + * psa_drv_se_key_derivation_export(p_session_key, + * session_key_size, + * &session_key_length); + * ~~~~~~~~~~~~~ + */ +/**@{*/ + +/** \brief A function that Sets up a secure element key derivation operation by + * specifying the algorithm and the source key sot + * + * \param[in,out] drv_context The driver context structure. + * \param[in,out] op_context A hardware-specific structure containing any + * context information for the implementation + * \param[in] kdf_alg The algorithm to be used for the key derivation + * \param[in] source_key The key to be used as the source material for + * the key derivation + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_key_derivation_setup_t)(psa_drv_se_context_t *drv_context, + void *op_context, + psa_algorithm_t kdf_alg, + psa_key_slot_number_t source_key); + +/** \brief A function that provides collateral (parameters) needed for a secure + * element key derivation or key agreement operation + * + * Since many key derivation algorithms require multiple parameters, it is + * expected that this function may be called multiple times for the same + * operation, each with a different algorithm-specific `collateral_id` + * + * \param[in,out] op_context A hardware-specific structure containing any + * context information for the implementation + * \param[in] collateral_id An ID for the collateral being provided + * \param[in] p_collateral A buffer containing the collateral data + * \param[in] collateral_size The size in bytes of the collateral + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_key_derivation_collateral_t)(void *op_context, + uint32_t collateral_id, + const uint8_t *p_collateral, + size_t collateral_size); + +/** \brief A function that performs the final secure element key derivation + * step and place the generated key material in a slot + * + * \param[in,out] op_context A hardware-specific structure containing any + * context information for the implementation + * \param[in] dest_key The slot where the generated key material + * should be placed + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_key_derivation_derive_t)(void *op_context, + psa_key_slot_number_t dest_key); + +/** \brief A function that performs the final step of a secure element key + * agreement and place the generated key material in a buffer + * + * \param[out] p_output Buffer in which to place the generated key + * material + * \param[in] output_size The size in bytes of `p_output` + * \param[out] p_output_length Upon success, contains the number of bytes of + * key material placed in `p_output` + * + * \retval #PSA_SUCCESS \emptydescription + */ +typedef psa_status_t (*psa_drv_se_key_derivation_export_t)(void *op_context, + uint8_t *p_output, + size_t output_size, + size_t *p_output_length); + +/** + * \brief A struct containing all of the function pointers needed to for secure + * element key derivation and agreement + * + * PSA Crypto API implementations should populate instances of the table as + * appropriate upon startup. + * + * If one of the functions is not implemented, it should be set to NULL. + */ +typedef struct { + /** The driver-specific size of the key derivation context */ + size_t MBEDTLS_PRIVATE(context_size); + /** Function that performs a key derivation setup */ + psa_drv_se_key_derivation_setup_t MBEDTLS_PRIVATE(p_setup); + /** Function that sets key derivation collateral */ + psa_drv_se_key_derivation_collateral_t MBEDTLS_PRIVATE(p_collateral); + /** Function that performs a final key derivation step */ + psa_drv_se_key_derivation_derive_t MBEDTLS_PRIVATE(p_derive); + /** Function that performs a final key derivation or agreement and + * exports the key */ + psa_drv_se_key_derivation_export_t MBEDTLS_PRIVATE(p_export); +} psa_drv_se_key_derivation_t; + +/**@}*/ + +/** \defgroup se_registration Secure element driver registration + */ +/**@{*/ + +/** A structure containing pointers to all the entry points of a + * secure element driver. + * + * Future versions of this specification may add extra substructures at + * the end of this structure. + */ +typedef struct { + /** The version of the driver HAL that this driver implements. + * This is a protection against loading driver binaries built against + * a different version of this specification. + * Use #PSA_DRV_SE_HAL_VERSION. + */ + uint32_t MBEDTLS_PRIVATE(hal_version); + + /** The size of the driver's persistent data in bytes. + * + * This can be 0 if the driver does not need persistent data. + * + * See the documentation of psa_drv_se_context_t::persistent_data + * for more information about why and how a driver can use + * persistent data. + */ + size_t MBEDTLS_PRIVATE(persistent_data_size); + + /** The driver initialization function. + * + * This function is called once during the initialization of the + * PSA Cryptography subsystem, before any other function of the + * driver is called. If this function returns a failure status, + * the driver will be unusable, at least until the next system reset. + * + * If this field is \c NULL, it is equivalent to a function that does + * nothing and returns #PSA_SUCCESS. + */ + psa_drv_se_init_t MBEDTLS_PRIVATE(p_init); + + const psa_drv_se_key_management_t *MBEDTLS_PRIVATE(key_management); + const psa_drv_se_mac_t *MBEDTLS_PRIVATE(mac); + const psa_drv_se_cipher_t *MBEDTLS_PRIVATE(cipher); + const psa_drv_se_aead_t *MBEDTLS_PRIVATE(aead); + const psa_drv_se_asymmetric_t *MBEDTLS_PRIVATE(asymmetric); + const psa_drv_se_key_derivation_t *MBEDTLS_PRIVATE(derivation); +} psa_drv_se_t; + +/** The current version of the secure element driver HAL. + */ +/* 0.0.0 patchlevel 5 */ +#define PSA_DRV_SE_HAL_VERSION 0x00000005 + +/** Register an external cryptoprocessor (secure element) driver. + * + * This function is only intended to be used by driver code, not by + * application code. In implementations with separation between the + * PSA cryptography module and applications, this function should + * only be available to callers that run in the same memory space as + * the cryptography module, and should not be exposed to applications + * running in a different memory space. + * + * This function may be called before psa_crypto_init(). It is + * implementation-defined whether this function may be called + * after psa_crypto_init(). + * + * \note Implementations store metadata about keys including the lifetime + * value, which contains the driver's location indicator. Therefore, + * from one instantiation of the PSA Cryptography + * library to the next one, if there is a key in storage with a certain + * lifetime value, you must always register the same driver (or an + * updated version that communicates with the same secure element) + * with the same location value. + * + * \param location The location value through which this driver will + * be exposed to applications. + * This driver will be used for all keys such that + * `location == #PSA_KEY_LIFETIME_GET_LOCATION( lifetime )`. + * The value #PSA_KEY_LOCATION_LOCAL_STORAGE is reserved + * and may not be used for drivers. Implementations + * may reserve other values. + * \param[in] methods The method table of the driver. This structure must + * remain valid for as long as the cryptography + * module keeps running. It is typically a global + * constant. + * + * \return #PSA_SUCCESS + * The driver was successfully registered. Applications can now + * use \p location to access keys through the methods passed to + * this function. + * \return #PSA_ERROR_BAD_STATE + * This function was called after the initialization of the + * cryptography module, and this implementation does not support + * driver registration at this stage. + * \return #PSA_ERROR_ALREADY_EXISTS + * There is already a registered driver for this value of \p location. + * \return #PSA_ERROR_INVALID_ARGUMENT + * \p location is a reserved value. + * \return #PSA_ERROR_NOT_SUPPORTED + * `methods->hal_version` is not supported by this implementation. + * \return #PSA_ERROR_INSUFFICIENT_MEMORY + * \return #PSA_ERROR_NOT_PERMITTED + * \return #PSA_ERROR_STORAGE_FAILURE + * \return #PSA_ERROR_DATA_CORRUPT + */ +psa_status_t psa_register_se_driver( + psa_key_location_t location, + const psa_drv_se_t *methods); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_CRYPTO_SE_DRIVER_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_sizes.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_sizes.h new file mode 100644 index 00000000..3d6bb2c1 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_sizes.h @@ -0,0 +1,1173 @@ +/** + * \file psa/crypto_sizes.h + * + * \brief PSA cryptography module: Mbed TLS buffer size macros + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * This file contains the definitions of macros that are useful to + * compute buffer sizes. The signatures and semantics of these macros + * are standardized, but the definitions are not, because they depend on + * the available algorithms and, in some cases, on permitted tolerances + * on buffer sizes. + * + * In implementations with isolation between the application and the + * cryptography module, implementers should take care to ensure that + * the definitions that are exposed to applications match what the + * module implements. + * + * Macros that compute sizes whose values do not depend on the + * implementation are in crypto.h. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_SIZES_H +#define PSA_CRYPTO_SIZES_H + +/* Include the Mbed TLS configuration file, the way Mbed TLS does it + * in each of its header files. */ +#include "mbedtls/build_info.h" + +#define PSA_BITS_TO_BYTES(bits) (((bits) + 7) / 8) +#define PSA_BYTES_TO_BITS(bytes) ((bytes) * 8) + +#define PSA_ROUND_UP_TO_MULTIPLE(block_size, length) \ + (((length) + (block_size) - 1) / (block_size) * (block_size)) + +/** The size of the output of psa_hash_finish(), in bytes. + * + * This is also the hash size that psa_hash_verify() expects. + * + * \param alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p alg) is true), or an HMAC algorithm + * (#PSA_ALG_HMAC(\c hash_alg) where \c hash_alg is a + * hash algorithm). + * + * \return The hash size for the specified hash algorithm. + * If the hash algorithm is not recognized, return 0. + */ +#define PSA_HASH_LENGTH(alg) \ + ( \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD5 ? 16 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_RIPEMD160 ? 20 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_1 ? 20 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_224 ? 28 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_256 ? 32 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_384 ? 48 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512 ? 64 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512_224 ? 28 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512_256 ? 32 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_224 ? 28 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_256 ? 32 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_384 ? 48 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_512 ? 64 : \ + 0) + +/** The input block size of a hash algorithm, in bytes. + * + * Hash algorithms process their input data in blocks. Hash operations will + * retain any partial blocks until they have enough input to fill the block or + * until the operation is finished. + * This affects the output from psa_hash_suspend(). + * + * \param alg A hash algorithm (\c PSA_ALG_XXX value such that + * PSA_ALG_IS_HASH(\p alg) is true). + * + * \return The block size in bytes for the specified hash algorithm. + * If the hash algorithm is not recognized, return 0. + * An implementation can return either 0 or the correct size for a + * hash algorithm that it recognizes, but does not support. + */ +#define PSA_HASH_BLOCK_LENGTH(alg) \ + ( \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_MD5 ? 64 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_RIPEMD160 ? 64 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_1 ? 64 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_224 ? 64 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_256 ? 64 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_384 ? 128 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512 ? 128 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512_224 ? 128 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA_512_256 ? 128 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_224 ? 144 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_256 ? 136 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_384 ? 104 : \ + PSA_ALG_HMAC_GET_HASH(alg) == PSA_ALG_SHA3_512 ? 72 : \ + 0) + +/** \def PSA_HASH_MAX_SIZE + * + * Maximum size of a hash. + * + * This macro expands to a compile-time constant integer. This value + * is the maximum size of a hash in bytes. + */ +/* Note: for HMAC-SHA-3, the block size is 144 bytes for HMAC-SHA3-226, + * 136 bytes for HMAC-SHA3-256, 104 bytes for SHA3-384, 72 bytes for + * HMAC-SHA3-512. */ +#if defined(PSA_WANT_ALG_SHA_512) || defined(PSA_WANT_ALG_SHA_384) +#define PSA_HASH_MAX_SIZE 64 +#define PSA_HMAC_MAX_HASH_BLOCK_SIZE 128 +#else +#define PSA_HASH_MAX_SIZE 32 +#define PSA_HMAC_MAX_HASH_BLOCK_SIZE 64 +#endif + +/** \def PSA_MAC_MAX_SIZE + * + * Maximum size of a MAC. + * + * This macro expands to a compile-time constant integer. This value + * is the maximum size of a MAC in bytes. + */ +/* All non-HMAC MACs have a maximum size that's smaller than the + * minimum possible value of PSA_HASH_MAX_SIZE in this implementation. */ +/* Note that the encoding of truncated MAC algorithms limits this value + * to 64 bytes. + */ +#define PSA_MAC_MAX_SIZE PSA_HASH_MAX_SIZE + +/** The length of a tag for an AEAD algorithm, in bytes. + * + * This macro can be used to allocate a buffer of sufficient size to store the + * tag output from psa_aead_finish(). + * + * See also #PSA_AEAD_TAG_MAX_SIZE. + * + * \param key_type The type of the AEAD key. + * \param key_bits The size of the AEAD key in bits. + * \param alg An AEAD algorithm + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \return The tag length for the specified algorithm and key. + * If the AEAD algorithm does not have an identified + * tag that can be distinguished from the rest of + * the ciphertext, return 0. + * If the key type or AEAD algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_AEAD_TAG_LENGTH(key_type, key_bits, alg) \ + (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 ? \ + PSA_ALG_AEAD_GET_TAG_LENGTH(alg) : \ + ((void) (key_bits), 0)) + +/** The maximum tag size for all supported AEAD algorithms, in bytes. + * + * See also #PSA_AEAD_TAG_LENGTH(\p key_type, \p key_bits, \p alg). + */ +#define PSA_AEAD_TAG_MAX_SIZE 16 + +/* The maximum size of an RSA key on this implementation, in bits. + * This is a vendor-specific macro. + * + * Mbed TLS does not set a hard limit on the size of RSA keys: any key + * whose parameters fit in a bignum is accepted. However large keys can + * induce a large memory usage and long computation times. Unlike other + * auxiliary macros in this file and in crypto.h, which reflect how the + * library is configured, this macro defines how the library is + * configured. This implementation refuses to import or generate an + * RSA key whose size is larger than the value defined here. + * + * Note that an implementation may set different size limits for different + * operations, and does not need to accept all key sizes up to the limit. */ +#define PSA_VENDOR_RSA_MAX_KEY_BITS 4096 + +/* The maximum size of an ECC key on this implementation, in bits. + * This is a vendor-specific macro. */ +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 521 +#elif defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 512 +#elif defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 448 +#elif defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 384 +#elif defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 384 +#elif defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 256 +#elif defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 256 +#elif defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 256 +#elif defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 255 +#elif defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 224 +#elif defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 224 +#elif defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 192 +#elif defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 192 +#else +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 0 +#endif + +/** This macro returns the maximum supported length of the PSK for the + * TLS-1.2 PSK-to-MS key derivation + * (#PSA_ALG_TLS12_PSK_TO_MS(\c hash_alg)). + * + * The maximum supported length does not depend on the chosen hash algorithm. + * + * Quoting RFC 4279, Sect 5.3: + * TLS implementations supporting these ciphersuites MUST support + * arbitrary PSK identities up to 128 octets in length, and arbitrary + * PSKs up to 64 octets in length. Supporting longer identities and + * keys is RECOMMENDED. + * + * Therefore, no implementation should define a value smaller than 64 + * for #PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE. + */ +#define PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE 128 + +/* The expected size of input passed to psa_tls12_ecjpake_to_pms_input, + * which is expected to work with P-256 curve only. */ +#define PSA_TLS12_ECJPAKE_TO_PMS_INPUT_SIZE 65 + +/* The size of a serialized K.X coordinate to be used in + * psa_tls12_ecjpake_to_pms_input. This function only accepts the P-256 + * curve. */ +#define PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE 32 + +/** The maximum size of a block cipher. */ +#define PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE 16 + +/** The size of the output of psa_mac_sign_finish(), in bytes. + * + * This is also the MAC size that psa_mac_verify_finish() expects. + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type The type of the MAC key. + * \param key_bits The size of the MAC key in bits. + * \param alg A MAC algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_MAC(\p alg) is true). + * + * \return The MAC size for the specified algorithm with + * the specified key parameters. + * \return 0 if the MAC algorithm is not recognized. + * \return Either 0 or the correct size for a MAC algorithm that + * the implementation recognizes, but does not support. + * \return Unspecified if the key parameters are not consistent + * with the algorithm. + */ +#define PSA_MAC_LENGTH(key_type, key_bits, alg) \ + ((alg) & PSA_ALG_MAC_TRUNCATION_MASK ? PSA_MAC_TRUNCATED_LENGTH(alg) : \ + PSA_ALG_IS_HMAC(alg) ? PSA_HASH_LENGTH(PSA_ALG_HMAC_GET_HASH(alg)) : \ + PSA_ALG_IS_BLOCK_CIPHER_MAC(alg) ? PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \ + ((void) (key_type), (void) (key_bits), 0)) + +/** The maximum size of the output of psa_aead_encrypt(), in bytes. + * + * If the size of the ciphertext buffer is at least this large, it is + * guaranteed that psa_aead_encrypt() will not fail due to an + * insufficient buffer size. Depending on the algorithm, the actual size of + * the ciphertext may be smaller. + * + * See also #PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(\p plaintext_length). + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type A symmetric key type that is + * compatible with algorithm \p alg. + * \param alg An AEAD algorithm + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * \param plaintext_length Size of the plaintext in bytes. + * + * \return The AEAD ciphertext size for the specified + * algorithm. + * If the key type or AEAD algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_AEAD_ENCRYPT_OUTPUT_SIZE(key_type, alg, plaintext_length) \ + (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 ? \ + (plaintext_length) + PSA_ALG_AEAD_GET_TAG_LENGTH(alg) : \ + 0) + +/** A sufficient output buffer size for psa_aead_encrypt(), for any of the + * supported key types and AEAD algorithms. + * + * If the size of the ciphertext buffer is at least this large, it is guaranteed + * that psa_aead_encrypt() will not fail due to an insufficient buffer size. + * + * \note This macro returns a compile-time constant if its arguments are + * compile-time constants. + * + * See also #PSA_AEAD_ENCRYPT_OUTPUT_SIZE(\p key_type, \p alg, + * \p plaintext_length). + * + * \param plaintext_length Size of the plaintext in bytes. + * + * \return A sufficient output buffer size for any of the + * supported key types and AEAD algorithms. + * + */ +#define PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(plaintext_length) \ + ((plaintext_length) + PSA_AEAD_TAG_MAX_SIZE) + + +/** The maximum size of the output of psa_aead_decrypt(), in bytes. + * + * If the size of the plaintext buffer is at least this large, it is + * guaranteed that psa_aead_decrypt() will not fail due to an + * insufficient buffer size. Depending on the algorithm, the actual size of + * the plaintext may be smaller. + * + * See also #PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(\p ciphertext_length). + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type A symmetric key type that is + * compatible with algorithm \p alg. + * \param alg An AEAD algorithm + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * \param ciphertext_length Size of the plaintext in bytes. + * + * \return The AEAD ciphertext size for the specified + * algorithm. + * If the key type or AEAD algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_AEAD_DECRYPT_OUTPUT_SIZE(key_type, alg, ciphertext_length) \ + (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 && \ + (ciphertext_length) > PSA_ALG_AEAD_GET_TAG_LENGTH(alg) ? \ + (ciphertext_length) - PSA_ALG_AEAD_GET_TAG_LENGTH(alg) : \ + 0) + +/** A sufficient output buffer size for psa_aead_decrypt(), for any of the + * supported key types and AEAD algorithms. + * + * If the size of the plaintext buffer is at least this large, it is guaranteed + * that psa_aead_decrypt() will not fail due to an insufficient buffer size. + * + * \note This macro returns a compile-time constant if its arguments are + * compile-time constants. + * + * See also #PSA_AEAD_DECRYPT_OUTPUT_SIZE(\p key_type, \p alg, + * \p ciphertext_length). + * + * \param ciphertext_length Size of the ciphertext in bytes. + * + * \return A sufficient output buffer size for any of the + * supported key types and AEAD algorithms. + * + */ +#define PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(ciphertext_length) \ + (ciphertext_length) + +/** The default nonce size for an AEAD algorithm, in bytes. + * + * This macro can be used to allocate a buffer of sufficient size to + * store the nonce output from #psa_aead_generate_nonce(). + * + * See also #PSA_AEAD_NONCE_MAX_SIZE. + * + * \note This is not the maximum size of nonce supported as input to + * #psa_aead_set_nonce(), #psa_aead_encrypt() or #psa_aead_decrypt(), + * just the default size that is generated by #psa_aead_generate_nonce(). + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type A symmetric key type that is compatible with + * algorithm \p alg. + * + * \param alg An AEAD algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \return The default nonce size for the specified key type and algorithm. + * If the key type or AEAD algorithm is not recognized, + * or the parameters are incompatible, return 0. + */ +#define PSA_AEAD_NONCE_LENGTH(key_type, alg) \ + (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) == 16 ? \ + MBEDTLS_PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_CCM) ? 13 : \ + MBEDTLS_PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_GCM) ? 12 : \ + 0 : \ + (key_type) == PSA_KEY_TYPE_CHACHA20 && \ + MBEDTLS_PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_CHACHA20_POLY1305) ? 12 : \ + 0) + +/** The maximum default nonce size among all supported pairs of key types and + * AEAD algorithms, in bytes. + * + * This is equal to or greater than any value that #PSA_AEAD_NONCE_LENGTH() + * may return. + * + * \note This is not the maximum size of nonce supported as input to + * #psa_aead_set_nonce(), #psa_aead_encrypt() or #psa_aead_decrypt(), + * just the largest size that may be generated by + * #psa_aead_generate_nonce(). + */ +#define PSA_AEAD_NONCE_MAX_SIZE 13 + +/** A sufficient output buffer size for psa_aead_update(). + * + * If the size of the output buffer is at least this large, it is + * guaranteed that psa_aead_update() will not fail due to an + * insufficient buffer size. The actual size of the output may be smaller + * in any given call. + * + * See also #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p input_length). + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type A symmetric key type that is + * compatible with algorithm \p alg. + * \param alg An AEAD algorithm + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * \param input_length Size of the input in bytes. + * + * \return A sufficient output buffer size for the specified + * algorithm. + * If the key type or AEAD algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +/* For all the AEAD modes defined in this specification, it is possible + * to emit output without delay. However, hardware may not always be + * capable of this. So for modes based on a block cipher, allow the + * implementation to delay the output until it has a full block. */ +#define PSA_AEAD_UPDATE_OUTPUT_SIZE(key_type, alg, input_length) \ + (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 ? \ + PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ? \ + PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type), (input_length)) : \ + (input_length) : \ + 0) + +/** A sufficient output buffer size for psa_aead_update(), for any of the + * supported key types and AEAD algorithms. + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_aead_update() will not fail due to an insufficient buffer size. + * + * See also #PSA_AEAD_UPDATE_OUTPUT_SIZE(\p key_type, \p alg, \p input_length). + * + * \param input_length Size of the input in bytes. + */ +#define PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(input_length) \ + (PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE, (input_length))) + +/** A sufficient ciphertext buffer size for psa_aead_finish(). + * + * If the size of the ciphertext buffer is at least this large, it is + * guaranteed that psa_aead_finish() will not fail due to an + * insufficient ciphertext buffer size. The actual size of the output may + * be smaller in any given call. + * + * See also #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE. + * + * \param key_type A symmetric key type that is + compatible with algorithm \p alg. + * \param alg An AEAD algorithm + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \return A sufficient ciphertext buffer size for the + * specified algorithm. + * If the key type or AEAD algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_AEAD_FINISH_OUTPUT_SIZE(key_type, alg) \ + (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 && \ + PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ? \ + PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \ + 0) + +/** A sufficient ciphertext buffer size for psa_aead_finish(), for any of the + * supported key types and AEAD algorithms. + * + * See also #PSA_AEAD_FINISH_OUTPUT_SIZE(\p key_type, \p alg). + */ +#define PSA_AEAD_FINISH_OUTPUT_MAX_SIZE (PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE) + +/** A sufficient plaintext buffer size for psa_aead_verify(). + * + * If the size of the plaintext buffer is at least this large, it is + * guaranteed that psa_aead_verify() will not fail due to an + * insufficient plaintext buffer size. The actual size of the output may + * be smaller in any given call. + * + * See also #PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE. + * + * \param key_type A symmetric key type that is + * compatible with algorithm \p alg. + * \param alg An AEAD algorithm + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \return A sufficient plaintext buffer size for the + * specified algorithm. + * If the key type or AEAD algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_AEAD_VERIFY_OUTPUT_SIZE(key_type, alg) \ + (PSA_AEAD_NONCE_LENGTH(key_type, alg) != 0 && \ + PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) ? \ + PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \ + 0) + +/** A sufficient plaintext buffer size for psa_aead_verify(), for any of the + * supported key types and AEAD algorithms. + * + * See also #PSA_AEAD_VERIFY_OUTPUT_SIZE(\p key_type, \p alg). + */ +#define PSA_AEAD_VERIFY_OUTPUT_MAX_SIZE (PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE) + +#define PSA_RSA_MINIMUM_PADDING_SIZE(alg) \ + (PSA_ALG_IS_RSA_OAEP(alg) ? \ + 2 * PSA_HASH_LENGTH(PSA_ALG_RSA_OAEP_GET_HASH(alg)) + 1 : \ + 11 /*PKCS#1v1.5*/) + +/** + * \brief ECDSA signature size for a given curve bit size + * + * \param curve_bits Curve size in bits. + * \return Signature size in bytes. + * + * \note This macro returns a compile-time constant if its argument is one. + */ +#define PSA_ECDSA_SIGNATURE_SIZE(curve_bits) \ + (PSA_BITS_TO_BYTES(curve_bits) * 2) + +/** Sufficient signature buffer size for psa_sign_hash(). + * + * This macro returns a sufficient buffer size for a signature using a key + * of the specified type and size, with the specified algorithm. + * Note that the actual size of the signature may be smaller + * (some algorithms produce a variable-size signature). + * + * \warning This function may call its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type An asymmetric key type (this may indifferently be a + * key pair type or a public key type). + * \param key_bits The size of the key in bits. + * \param alg The signature algorithm. + * + * \return If the parameters are valid and supported, return + * a buffer size in bytes that guarantees that + * psa_sign_hash() will not fail with + * #PSA_ERROR_BUFFER_TOO_SMALL. + * If the parameters are a valid combination that is not supported, + * return either a sensible size or 0. + * If the parameters are not valid, the + * return value is unspecified. + */ +#define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ + (PSA_KEY_TYPE_IS_RSA(key_type) ? ((void) alg, PSA_BITS_TO_BYTES(key_bits)) : \ + PSA_KEY_TYPE_IS_ECC(key_type) ? PSA_ECDSA_SIGNATURE_SIZE(key_bits) : \ + ((void) alg, 0)) + +#define PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE \ + PSA_ECDSA_SIGNATURE_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) + +/** \def PSA_SIGNATURE_MAX_SIZE + * + * Maximum size of an asymmetric signature. + * + * This macro expands to a compile-time constant integer. This value + * is the maximum size of a signature in bytes. + */ +#define PSA_SIGNATURE_MAX_SIZE \ + (PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS) > PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE ? \ + PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS) : \ + PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE) + +/** Sufficient output buffer size for psa_asymmetric_encrypt(). + * + * This macro returns a sufficient buffer size for a ciphertext produced using + * a key of the specified type and size, with the specified algorithm. + * Note that the actual size of the ciphertext may be smaller, depending + * on the algorithm. + * + * \warning This function may call its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type An asymmetric key type (this may indifferently be a + * key pair type or a public key type). + * \param key_bits The size of the key in bits. + * \param alg The asymmetric encryption algorithm. + * + * \return If the parameters are valid and supported, return + * a buffer size in bytes that guarantees that + * psa_asymmetric_encrypt() will not fail with + * #PSA_ERROR_BUFFER_TOO_SMALL. + * If the parameters are a valid combination that is not supported, + * return either a sensible size or 0. + * If the parameters are not valid, the + * return value is unspecified. + */ +#define PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(key_type, key_bits, alg) \ + (PSA_KEY_TYPE_IS_RSA(key_type) ? \ + ((void) alg, PSA_BITS_TO_BYTES(key_bits)) : \ + 0) + +/** A sufficient output buffer size for psa_asymmetric_encrypt(), for any + * supported asymmetric encryption. + * + * See also #PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(\p key_type, \p key_bits, \p alg). + */ +/* This macro assumes that RSA is the only supported asymmetric encryption. */ +#define PSA_ASYMMETRIC_ENCRYPT_OUTPUT_MAX_SIZE \ + (PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS)) + +/** Sufficient output buffer size for psa_asymmetric_decrypt(). + * + * This macro returns a sufficient buffer size for a plaintext produced using + * a key of the specified type and size, with the specified algorithm. + * Note that the actual size of the plaintext may be smaller, depending + * on the algorithm. + * + * \warning This function may call its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type An asymmetric key type (this may indifferently be a + * key pair type or a public key type). + * \param key_bits The size of the key in bits. + * \param alg The asymmetric encryption algorithm. + * + * \return If the parameters are valid and supported, return + * a buffer size in bytes that guarantees that + * psa_asymmetric_decrypt() will not fail with + * #PSA_ERROR_BUFFER_TOO_SMALL. + * If the parameters are a valid combination that is not supported, + * return either a sensible size or 0. + * If the parameters are not valid, the + * return value is unspecified. + */ +#define PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(key_type, key_bits, alg) \ + (PSA_KEY_TYPE_IS_RSA(key_type) ? \ + PSA_BITS_TO_BYTES(key_bits) - PSA_RSA_MINIMUM_PADDING_SIZE(alg) : \ + 0) + +/** A sufficient output buffer size for psa_asymmetric_decrypt(), for any + * supported asymmetric decryption. + * + * This macro assumes that RSA is the only supported asymmetric encryption. + * + * See also #PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(\p key_type, \p key_bits, \p alg). + */ +#define PSA_ASYMMETRIC_DECRYPT_OUTPUT_MAX_SIZE \ + (PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS)) + +/* Maximum size of the ASN.1 encoding of an INTEGER with the specified + * number of bits. + * + * This definition assumes that bits <= 2^19 - 9 so that the length field + * is at most 3 bytes. The length of the encoding is the length of the + * bit string padded to a whole number of bytes plus: + * - 1 type byte; + * - 1 to 3 length bytes; + * - 0 to 1 bytes of leading 0 due to the sign bit. + */ +#define PSA_KEY_EXPORT_ASN1_INTEGER_MAX_SIZE(bits) \ + ((bits) / 8 + 5) + +/* Maximum size of the export encoding of an RSA public key. + * Assumes that the public exponent is less than 2^32. + * + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER } -- e + * + * - 4 bytes of SEQUENCE overhead; + * - n : INTEGER; + * - 7 bytes for the public exponent. + */ +#define PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) \ + (PSA_KEY_EXPORT_ASN1_INTEGER_MAX_SIZE(key_bits) + 11) + +/* Maximum size of the export encoding of an RSA key pair. + * Assumes that the public exponent is less than 2^32 and that the size + * difference between the two primes is at most 1 bit. + * + * RSAPrivateKey ::= SEQUENCE { + * version Version, -- 0 + * modulus INTEGER, -- N-bit + * publicExponent INTEGER, -- 32-bit + * privateExponent INTEGER, -- N-bit + * prime1 INTEGER, -- N/2-bit + * prime2 INTEGER, -- N/2-bit + * exponent1 INTEGER, -- N/2-bit + * exponent2 INTEGER, -- N/2-bit + * coefficient INTEGER, -- N/2-bit + * } + * + * - 4 bytes of SEQUENCE overhead; + * - 3 bytes of version; + * - 7 half-size INTEGERs plus 2 full-size INTEGERs, + * overapproximated as 9 half-size INTEGERS; + * - 7 bytes for the public exponent. + */ +#define PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(key_bits) \ + (9 * PSA_KEY_EXPORT_ASN1_INTEGER_MAX_SIZE((key_bits) / 2 + 1) + 14) + +/* Maximum size of the export encoding of a DSA public key. + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } -- contains DSAPublicKey + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters Dss-Params } -- SEQUENCE of 3 INTEGERs + * DSAPublicKey ::= INTEGER -- public key, Y + * + * - 3 * 4 bytes of SEQUENCE overhead; + * - 1 + 1 + 7 bytes of algorithm (DSA OID); + * - 4 bytes of BIT STRING overhead; + * - 3 full-size INTEGERs (p, g, y); + * - 1 + 1 + 32 bytes for 1 sub-size INTEGER (q <= 256 bits). + */ +#define PSA_KEY_EXPORT_DSA_PUBLIC_KEY_MAX_SIZE(key_bits) \ + (PSA_KEY_EXPORT_ASN1_INTEGER_MAX_SIZE(key_bits) * 3 + 59) + +/* Maximum size of the export encoding of a DSA key pair. + * + * DSAPrivateKey ::= SEQUENCE { + * version Version, -- 0 + * prime INTEGER, -- p + * subprime INTEGER, -- q + * generator INTEGER, -- g + * public INTEGER, -- y + * private INTEGER, -- x + * } + * + * - 4 bytes of SEQUENCE overhead; + * - 3 bytes of version; + * - 3 full-size INTEGERs (p, g, y); + * - 2 * (1 + 1 + 32) bytes for 2 sub-size INTEGERs (q, x <= 256 bits). + */ +#define PSA_KEY_EXPORT_DSA_KEY_PAIR_MAX_SIZE(key_bits) \ + (PSA_KEY_EXPORT_ASN1_INTEGER_MAX_SIZE(key_bits) * 3 + 75) + +/* Maximum size of the export encoding of an ECC public key. + * + * The representation of an ECC public key is: + * - The byte 0x04; + * - `x_P` as a `ceiling(m/8)`-byte string, big-endian; + * - `y_P` as a `ceiling(m/8)`-byte string, big-endian; + * - where m is the bit size associated with the curve. + * + * - 1 byte + 2 * point size. + */ +#define PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) \ + (2 * PSA_BITS_TO_BYTES(key_bits) + 1) + +/* Maximum size of the export encoding of an ECC key pair. + * + * An ECC key pair is represented by the secret value. + */ +#define PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits) \ + (PSA_BITS_TO_BYTES(key_bits)) + +/** Sufficient output buffer size for psa_export_key() or + * psa_export_public_key(). + * + * This macro returns a compile-time constant if its arguments are + * compile-time constants. + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * The following code illustrates how to allocate enough memory to export + * a key by querying the key type and size at runtime. + * \code{c} + * psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + * psa_status_t status; + * status = psa_get_key_attributes(key, &attributes); + * if (status != PSA_SUCCESS) handle_error(...); + * psa_key_type_t key_type = psa_get_key_type(&attributes); + * size_t key_bits = psa_get_key_bits(&attributes); + * size_t buffer_size = PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits); + * psa_reset_key_attributes(&attributes); + * uint8_t *buffer = malloc(buffer_size); + * if (buffer == NULL) handle_error(...); + * size_t buffer_length; + * status = psa_export_key(key, buffer, buffer_size, &buffer_length); + * if (status != PSA_SUCCESS) handle_error(...); + * \endcode + * + * \param key_type A supported key type. + * \param key_bits The size of the key in bits. + * + * \return If the parameters are valid and supported, return + * a buffer size in bytes that guarantees that + * psa_export_key() or psa_export_public_key() will not fail with + * #PSA_ERROR_BUFFER_TOO_SMALL. + * If the parameters are a valid combination that is not supported, + * return either a sensible size or 0. + * If the parameters are not valid, the return value is unspecified. + */ +#define PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits) \ + (PSA_KEY_TYPE_IS_UNSTRUCTURED(key_type) ? PSA_BITS_TO_BYTES(key_bits) : \ + (key_type) == PSA_KEY_TYPE_RSA_KEY_PAIR ? PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(key_bits) : \ + (key_type) == PSA_KEY_TYPE_RSA_PUBLIC_KEY ? PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \ + (key_type) == PSA_KEY_TYPE_DSA_KEY_PAIR ? PSA_KEY_EXPORT_DSA_KEY_PAIR_MAX_SIZE(key_bits) : \ + (key_type) == PSA_KEY_TYPE_DSA_PUBLIC_KEY ? PSA_KEY_EXPORT_DSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \ + PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type) ? PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits) : \ + PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(key_type) ? PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) : \ + 0) + +/** Sufficient output buffer size for psa_export_public_key(). + * + * This macro returns a compile-time constant if its arguments are + * compile-time constants. + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * The following code illustrates how to allocate enough memory to export + * a public key by querying the key type and size at runtime. + * \code{c} + * psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + * psa_status_t status; + * status = psa_get_key_attributes(key, &attributes); + * if (status != PSA_SUCCESS) handle_error(...); + * psa_key_type_t key_type = psa_get_key_type(&attributes); + * size_t key_bits = psa_get_key_bits(&attributes); + * size_t buffer_size = PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits); + * psa_reset_key_attributes(&attributes); + * uint8_t *buffer = malloc(buffer_size); + * if (buffer == NULL) handle_error(...); + * size_t buffer_length; + * status = psa_export_public_key(key, buffer, buffer_size, &buffer_length); + * if (status != PSA_SUCCESS) handle_error(...); + * \endcode + * + * \param key_type A public key or key pair key type. + * \param key_bits The size of the key in bits. + * + * \return If the parameters are valid and supported, return + * a buffer size in bytes that guarantees that + * psa_export_public_key() will not fail with + * #PSA_ERROR_BUFFER_TOO_SMALL. + * If the parameters are a valid combination that is not + * supported, return either a sensible size or 0. + * If the parameters are not valid, + * the return value is unspecified. + * + * If the parameters are valid and supported, + * return the same result as + * #PSA_EXPORT_KEY_OUTPUT_SIZE( + * \p #PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(\p key_type), + * \p key_bits). + */ +#define PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits) \ + (PSA_KEY_TYPE_IS_RSA(key_type) ? PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \ + PSA_KEY_TYPE_IS_ECC(key_type) ? PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) : \ + 0) + +/** Sufficient buffer size for exporting any asymmetric key pair. + * + * This macro expands to a compile-time constant integer. This value is + * a sufficient buffer size when calling psa_export_key() to export any + * asymmetric key pair, regardless of the exact key type and key size. + * + * See also #PSA_EXPORT_KEY_OUTPUT_SIZE(\p key_type, \p key_bits). + */ +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE \ + (PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) > \ + PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) ? \ + PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) : \ + PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS)) + +/** Sufficient buffer size for exporting any asymmetric public key. + * + * This macro expands to a compile-time constant integer. This value is + * a sufficient buffer size when calling psa_export_key() or + * psa_export_public_key() to export any asymmetric public key, + * regardless of the exact key type and key size. + * + * See also #PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(\p key_type, \p key_bits). + */ +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE \ + (PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) > \ + PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) ? \ + PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) : \ + PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS)) + +/** Sufficient output buffer size for psa_raw_key_agreement(). + * + * This macro returns a compile-time constant if its arguments are + * compile-time constants. + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * See also #PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE. + * + * \param key_type A supported key type. + * \param key_bits The size of the key in bits. + * + * \return If the parameters are valid and supported, return + * a buffer size in bytes that guarantees that + * psa_raw_key_agreement() will not fail with + * #PSA_ERROR_BUFFER_TOO_SMALL. + * If the parameters are a valid combination that + * is not supported, return either a sensible size or 0. + * If the parameters are not valid, + * the return value is unspecified. + */ +/* FFDH is not yet supported in PSA. */ +#define PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(key_type, key_bits) \ + (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type) ? \ + PSA_BITS_TO_BYTES(key_bits) : \ + 0) + +/** Maximum size of the output from psa_raw_key_agreement(). + * + * This macro expands to a compile-time constant integer. This value is the + * maximum size of the output any raw key agreement algorithm, in bytes. + * + * See also #PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(\p key_type, \p key_bits). + */ +#define PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE \ + (PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)) + +/** The default IV size for a cipher algorithm, in bytes. + * + * The IV that is generated as part of a call to #psa_cipher_encrypt() is always + * the default IV length for the algorithm. + * + * This macro can be used to allocate a buffer of sufficient size to + * store the IV output from #psa_cipher_generate_iv() when using + * a multi-part cipher operation. + * + * See also #PSA_CIPHER_IV_MAX_SIZE. + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type A symmetric key type that is compatible with algorithm \p alg. + * + * \param alg A cipher algorithm (\c PSA_ALG_XXX value such that #PSA_ALG_IS_CIPHER(\p alg) is true). + * + * \return The default IV size for the specified key type and algorithm. + * If the algorithm does not use an IV, return 0. + * If the key type or cipher algorithm is not recognized, + * or the parameters are incompatible, return 0. + */ +#define PSA_CIPHER_IV_LENGTH(key_type, alg) \ + (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) > 1 && \ + ((alg) == PSA_ALG_CTR || \ + (alg) == PSA_ALG_CFB || \ + (alg) == PSA_ALG_OFB || \ + (alg) == PSA_ALG_XTS || \ + (alg) == PSA_ALG_CBC_NO_PADDING || \ + (alg) == PSA_ALG_CBC_PKCS7) ? PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \ + (key_type) == PSA_KEY_TYPE_CHACHA20 && \ + (alg) == PSA_ALG_STREAM_CIPHER ? 12 : \ + (alg) == PSA_ALG_CCM_STAR_NO_TAG ? 13 : \ + 0) + +/** The maximum IV size for all supported cipher algorithms, in bytes. + * + * See also #PSA_CIPHER_IV_LENGTH(). + */ +#define PSA_CIPHER_IV_MAX_SIZE 16 + +/** The maximum size of the output of psa_cipher_encrypt(), in bytes. + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_cipher_encrypt() will not fail due to an insufficient buffer size. + * Depending on the algorithm, the actual size of the output might be smaller. + * + * See also #PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(\p input_length). + * + * \warning This macro may evaluate its arguments multiple times or + * zero times, so you should not pass arguments that contain + * side effects. + * + * \param key_type A symmetric key type that is compatible with algorithm + * alg. + * \param alg A cipher algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \param input_length Size of the input in bytes. + * + * \return A sufficient output size for the specified key type and + * algorithm. If the key type or cipher algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(key_type, alg, input_length) \ + (alg == PSA_ALG_CBC_PKCS7 ? \ + (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) != 0 ? \ + PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type), \ + (input_length) + 1) + \ + PSA_CIPHER_IV_LENGTH((key_type), (alg)) : 0) : \ + (PSA_ALG_IS_CIPHER(alg) ? \ + (input_length) + PSA_CIPHER_IV_LENGTH((key_type), (alg)) : \ + 0)) + +/** A sufficient output buffer size for psa_cipher_encrypt(), for any of the + * supported key types and cipher algorithms. + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_cipher_encrypt() will not fail due to an insufficient buffer size. + * + * See also #PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(\p key_type, \p alg, \p input_length). + * + * \param input_length Size of the input in bytes. + * + */ +#define PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(input_length) \ + (PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE, \ + (input_length) + 1) + \ + PSA_CIPHER_IV_MAX_SIZE) + +/** The maximum size of the output of psa_cipher_decrypt(), in bytes. + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_cipher_decrypt() will not fail due to an insufficient buffer size. + * Depending on the algorithm, the actual size of the output might be smaller. + * + * See also #PSA_CIPHER_DECRYPT_OUTPUT_MAX_SIZE(\p input_length). + * + * \param key_type A symmetric key type that is compatible with algorithm + * alg. + * \param alg A cipher algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \param input_length Size of the input in bytes. + * + * \return A sufficient output size for the specified key type and + * algorithm. If the key type or cipher algorithm is not + * recognized, or the parameters are incompatible, + * return 0. + */ +#define PSA_CIPHER_DECRYPT_OUTPUT_SIZE(key_type, alg, input_length) \ + (PSA_ALG_IS_CIPHER(alg) && \ + ((key_type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_SYMMETRIC ? \ + (input_length) : \ + 0) + +/** A sufficient output buffer size for psa_cipher_decrypt(), for any of the + * supported key types and cipher algorithms. + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_cipher_decrypt() will not fail due to an insufficient buffer size. + * + * See also #PSA_CIPHER_DECRYPT_OUTPUT_SIZE(\p key_type, \p alg, \p input_length). + * + * \param input_length Size of the input in bytes. + */ +#define PSA_CIPHER_DECRYPT_OUTPUT_MAX_SIZE(input_length) \ + (input_length) + +/** A sufficient output buffer size for psa_cipher_update(). + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_cipher_update() will not fail due to an insufficient buffer size. + * The actual size of the output might be smaller in any given call. + * + * See also #PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(\p input_length). + * + * \param key_type A symmetric key type that is compatible with algorithm + * alg. + * \param alg A cipher algorithm (PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \param input_length Size of the input in bytes. + * + * \return A sufficient output size for the specified key type and + * algorithm. If the key type or cipher algorithm is not + * recognized, or the parameters are incompatible, return 0. + */ +#define PSA_CIPHER_UPDATE_OUTPUT_SIZE(key_type, alg, input_length) \ + (PSA_ALG_IS_CIPHER(alg) ? \ + (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) != 0 ? \ + (((alg) == PSA_ALG_CBC_PKCS7 || \ + (alg) == PSA_ALG_CBC_NO_PADDING || \ + (alg) == PSA_ALG_ECB_NO_PADDING) ? \ + PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type), \ + input_length) : \ + (input_length)) : 0) : \ + 0) + +/** A sufficient output buffer size for psa_cipher_update(), for any of the + * supported key types and cipher algorithms. + * + * If the size of the output buffer is at least this large, it is guaranteed + * that psa_cipher_update() will not fail due to an insufficient buffer size. + * + * See also #PSA_CIPHER_UPDATE_OUTPUT_SIZE(\p key_type, \p alg, \p input_length). + * + * \param input_length Size of the input in bytes. + */ +#define PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(input_length) \ + (PSA_ROUND_UP_TO_MULTIPLE(PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE, input_length)) + +/** A sufficient ciphertext buffer size for psa_cipher_finish(). + * + * If the size of the ciphertext buffer is at least this large, it is + * guaranteed that psa_cipher_finish() will not fail due to an insufficient + * ciphertext buffer size. The actual size of the output might be smaller in + * any given call. + * + * See also #PSA_CIPHER_FINISH_OUTPUT_MAX_SIZE(). + * + * \param key_type A symmetric key type that is compatible with algorithm + * alg. + * \param alg A cipher algorithm (PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \return A sufficient output size for the specified key type and + * algorithm. If the key type or cipher algorithm is not + * recognized, or the parameters are incompatible, return 0. + */ +#define PSA_CIPHER_FINISH_OUTPUT_SIZE(key_type, alg) \ + (PSA_ALG_IS_CIPHER(alg) ? \ + (alg == PSA_ALG_CBC_PKCS7 ? \ + PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) : \ + 0) : \ + 0) + +/** A sufficient ciphertext buffer size for psa_cipher_finish(), for any of the + * supported key types and cipher algorithms. + * + * See also #PSA_CIPHER_FINISH_OUTPUT_SIZE(\p key_type, \p alg). + */ +#define PSA_CIPHER_FINISH_OUTPUT_MAX_SIZE \ + (PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE) + +#endif /* PSA_CRYPTO_SIZES_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_struct.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_struct.h new file mode 100644 index 00000000..934bc176 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_struct.h @@ -0,0 +1,558 @@ +/** + * \file psa/crypto_struct.h + * + * \brief PSA cryptography module: Mbed TLS structured type implementations + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. + * + * This file contains the definitions of some data structures with + * implementation-specific definitions. + * + * In implementations with isolation between the application and the + * cryptography module, it is expected that the front-end and the back-end + * would have different versions of this file. + * + *

Design notes about multipart operation structures

+ * + * For multipart operations without driver delegation support, each multipart + * operation structure contains a `psa_algorithm_t alg` field which indicates + * which specific algorithm the structure is for. When the structure is not in + * use, `alg` is 0. Most of the structure consists of a union which is + * discriminated by `alg`. + * + * For multipart operations with driver delegation support, each multipart + * operation structure contains an `unsigned int id` field indicating which + * driver got assigned to do the operation. When the structure is not in use, + * 'id' is 0. The structure contains also a driver context which is the union + * of the contexts of all drivers able to handle the type of multipart + * operation. + * + * Note that when `alg` or `id` is 0, the content of other fields is undefined. + * In particular, it is not guaranteed that a freshly-initialized structure + * is all-zero: we initialize structures to something like `{0, 0}`, which + * is only guaranteed to initializes the first member of the union; + * GCC and Clang initialize the whole structure to 0 (at the time of writing), + * but MSVC and CompCert don't. + * + * In Mbed Crypto, multipart operation structures live independently from + * the key. This allows Mbed Crypto to free the key objects when destroying + * a key slot. If a multipart operation needs to remember the key after + * the setup function returns, the operation structure needs to contain a + * copy of the key. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_STRUCT_H +#define PSA_CRYPTO_STRUCT_H +#include "mbedtls/private_access.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Include the Mbed TLS configuration file, the way Mbed TLS does it + * in each of its header files. */ +#include "mbedtls/build_info.h" + +#include "mbedtls/cmac.h" +#include "mbedtls/gcm.h" +#include "mbedtls/ccm.h" +#include "mbedtls/chachapoly.h" + +/* Include the context definition for the compiled-in drivers for the primitive + * algorithms. */ +#include "psa/crypto_driver_contexts_primitives.h" + +struct psa_hash_operation_s { + /** Unique ID indicating which driver got assigned to do the + * operation. Since driver contexts are driver-specific, swapping + * drivers halfway through the operation is not supported. + * ID values are auto-generated in psa_driver_wrappers.h. + * ID value zero means the context is not valid or not assigned to + * any driver (i.e. the driver context is not active, in use). */ + unsigned int MBEDTLS_PRIVATE(id); + psa_driver_hash_context_t MBEDTLS_PRIVATE(ctx); +}; + +#define PSA_HASH_OPERATION_INIT { 0, { 0 } } +static inline struct psa_hash_operation_s psa_hash_operation_init(void) +{ + const struct psa_hash_operation_s v = PSA_HASH_OPERATION_INIT; + return v; +} + +struct psa_cipher_operation_s { + /** Unique ID indicating which driver got assigned to do the + * operation. Since driver contexts are driver-specific, swapping + * drivers halfway through the operation is not supported. + * ID values are auto-generated in psa_crypto_driver_wrappers.h + * ID value zero means the context is not valid or not assigned to + * any driver (i.e. none of the driver contexts are active). */ + unsigned int MBEDTLS_PRIVATE(id); + + unsigned int MBEDTLS_PRIVATE(iv_required) : 1; + unsigned int MBEDTLS_PRIVATE(iv_set) : 1; + + uint8_t MBEDTLS_PRIVATE(default_iv_length); + + psa_driver_cipher_context_t MBEDTLS_PRIVATE(ctx); +}; + +#define PSA_CIPHER_OPERATION_INIT { 0, 0, 0, 0, { 0 } } +static inline struct psa_cipher_operation_s psa_cipher_operation_init(void) +{ + const struct psa_cipher_operation_s v = PSA_CIPHER_OPERATION_INIT; + return v; +} + +/* Include the context definition for the compiled-in drivers for the composite + * algorithms. */ +#include "psa/crypto_driver_contexts_composites.h" + +struct psa_mac_operation_s { + /** Unique ID indicating which driver got assigned to do the + * operation. Since driver contexts are driver-specific, swapping + * drivers halfway through the operation is not supported. + * ID values are auto-generated in psa_driver_wrappers.h + * ID value zero means the context is not valid or not assigned to + * any driver (i.e. none of the driver contexts are active). */ + unsigned int MBEDTLS_PRIVATE(id); + uint8_t MBEDTLS_PRIVATE(mac_size); + unsigned int MBEDTLS_PRIVATE(is_sign) : 1; + psa_driver_mac_context_t MBEDTLS_PRIVATE(ctx); +}; + +#define PSA_MAC_OPERATION_INIT { 0, 0, 0, { 0 } } +static inline struct psa_mac_operation_s psa_mac_operation_init(void) +{ + const struct psa_mac_operation_s v = PSA_MAC_OPERATION_INIT; + return v; +} + +struct psa_aead_operation_s { + + /** Unique ID indicating which driver got assigned to do the + * operation. Since driver contexts are driver-specific, swapping + * drivers halfway through the operation is not supported. + * ID values are auto-generated in psa_crypto_driver_wrappers.h + * ID value zero means the context is not valid or not assigned to + * any driver (i.e. none of the driver contexts are active). */ + unsigned int MBEDTLS_PRIVATE(id); + + psa_algorithm_t MBEDTLS_PRIVATE(alg); + psa_key_type_t MBEDTLS_PRIVATE(key_type); + + size_t MBEDTLS_PRIVATE(ad_remaining); + size_t MBEDTLS_PRIVATE(body_remaining); + + unsigned int MBEDTLS_PRIVATE(nonce_set) : 1; + unsigned int MBEDTLS_PRIVATE(lengths_set) : 1; + unsigned int MBEDTLS_PRIVATE(ad_started) : 1; + unsigned int MBEDTLS_PRIVATE(body_started) : 1; + unsigned int MBEDTLS_PRIVATE(is_encrypt) : 1; + + psa_driver_aead_context_t MBEDTLS_PRIVATE(ctx); +}; + +#define PSA_AEAD_OPERATION_INIT { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0 } } +static inline struct psa_aead_operation_s psa_aead_operation_init(void) +{ + const struct psa_aead_operation_s v = PSA_AEAD_OPERATION_INIT; + return v; +} + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) +typedef struct { + uint8_t *MBEDTLS_PRIVATE(info); + size_t MBEDTLS_PRIVATE(info_length); +#if PSA_HASH_MAX_SIZE > 0xff +#error "PSA_HASH_MAX_SIZE does not fit in uint8_t" +#endif + uint8_t MBEDTLS_PRIVATE(offset_in_block); + uint8_t MBEDTLS_PRIVATE(block_number); + unsigned int MBEDTLS_PRIVATE(state) : 2; + unsigned int MBEDTLS_PRIVATE(info_set) : 1; + uint8_t MBEDTLS_PRIVATE(output_block)[PSA_HASH_MAX_SIZE]; + uint8_t MBEDTLS_PRIVATE(prk)[PSA_HASH_MAX_SIZE]; + struct psa_mac_operation_s MBEDTLS_PRIVATE(hmac); +} psa_hkdf_key_derivation_t; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF || + MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT || + MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) +typedef struct { + uint8_t MBEDTLS_PRIVATE(data)[PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE]; +} psa_tls12_ecjpake_to_pms_t; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) +typedef enum { + PSA_TLS12_PRF_STATE_INIT, /* no input provided */ + PSA_TLS12_PRF_STATE_SEED_SET, /* seed has been set */ + PSA_TLS12_PRF_STATE_OTHER_KEY_SET, /* other key has been set - optional */ + PSA_TLS12_PRF_STATE_KEY_SET, /* key has been set */ + PSA_TLS12_PRF_STATE_LABEL_SET, /* label has been set */ + PSA_TLS12_PRF_STATE_OUTPUT /* output has been started */ +} psa_tls12_prf_key_derivation_state_t; + +typedef struct psa_tls12_prf_key_derivation_s { +#if PSA_HASH_MAX_SIZE > 0xff +#error "PSA_HASH_MAX_SIZE does not fit in uint8_t" +#endif + + /* Indicates how many bytes in the current HMAC block have + * not yet been read by the user. */ + uint8_t MBEDTLS_PRIVATE(left_in_block); + + /* The 1-based number of the block. */ + uint8_t MBEDTLS_PRIVATE(block_number); + + psa_tls12_prf_key_derivation_state_t MBEDTLS_PRIVATE(state); + + uint8_t *MBEDTLS_PRIVATE(secret); + size_t MBEDTLS_PRIVATE(secret_length); + uint8_t *MBEDTLS_PRIVATE(seed); + size_t MBEDTLS_PRIVATE(seed_length); + uint8_t *MBEDTLS_PRIVATE(label); + size_t MBEDTLS_PRIVATE(label_length); +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) + uint8_t *MBEDTLS_PRIVATE(other_secret); + size_t MBEDTLS_PRIVATE(other_secret_length); +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ + + uint8_t MBEDTLS_PRIVATE(Ai)[PSA_HASH_MAX_SIZE]; + + /* `HMAC_hash( prk, A( i ) + seed )` in the notation of RFC 5246, Sect. 5. */ + uint8_t MBEDTLS_PRIVATE(output_block)[PSA_HASH_MAX_SIZE]; +} psa_tls12_prf_key_derivation_t; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || + * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ + +struct psa_key_derivation_s { + psa_algorithm_t MBEDTLS_PRIVATE(alg); + unsigned int MBEDTLS_PRIVATE(can_output_key) : 1; + size_t MBEDTLS_PRIVATE(capacity); + union { + /* Make the union non-empty even with no supported algorithms. */ + uint8_t MBEDTLS_PRIVATE(dummy); +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) + psa_hkdf_key_derivation_t MBEDTLS_PRIVATE(hkdf); +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) + psa_tls12_prf_key_derivation_t MBEDTLS_PRIVATE(tls12_prf); +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) + psa_tls12_ecjpake_to_pms_t MBEDTLS_PRIVATE(tls12_ecjpake_to_pms); +#endif + } MBEDTLS_PRIVATE(ctx); +}; + +/* This only zeroes out the first byte in the union, the rest is unspecified. */ +#define PSA_KEY_DERIVATION_OPERATION_INIT { 0, 0, 0, { 0 } } +static inline struct psa_key_derivation_s psa_key_derivation_operation_init( + void) +{ + const struct psa_key_derivation_s v = PSA_KEY_DERIVATION_OPERATION_INIT; + return v; +} + +struct psa_key_policy_s { + psa_key_usage_t MBEDTLS_PRIVATE(usage); + psa_algorithm_t MBEDTLS_PRIVATE(alg); + psa_algorithm_t MBEDTLS_PRIVATE(alg2); +}; +typedef struct psa_key_policy_s psa_key_policy_t; + +#define PSA_KEY_POLICY_INIT { 0, 0, 0 } +static inline struct psa_key_policy_s psa_key_policy_init(void) +{ + const struct psa_key_policy_s v = PSA_KEY_POLICY_INIT; + return v; +} + +/* The type used internally for key sizes. + * Public interfaces use size_t, but internally we use a smaller type. */ +typedef uint16_t psa_key_bits_t; +/* The maximum value of the type used to represent bit-sizes. + * This is used to mark an invalid key size. */ +#define PSA_KEY_BITS_TOO_LARGE ((psa_key_bits_t) -1) +/* The maximum size of a key in bits. + * Currently defined as the maximum that can be represented, rounded down + * to a whole number of bytes. + * This is an uncast value so that it can be used in preprocessor + * conditionals. */ +#define PSA_MAX_KEY_BITS 0xfff8 + +/** A mask of flags that can be stored in key attributes. + * + * This type is also used internally to store flags in slots. Internal + * flags are defined in library/psa_crypto_core.h. Internal flags may have + * the same value as external flags if they are properly handled during + * key creation and in psa_get_key_attributes. + */ +typedef uint16_t psa_key_attributes_flag_t; + +#define MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER \ + ((psa_key_attributes_flag_t) 0x0001) + +/* A mask of key attribute flags used externally only. + * Only meant for internal checks inside the library. */ +#define MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY ( \ + MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER | \ + 0) + +/* A mask of key attribute flags used both internally and externally. + * Currently there aren't any. */ +#define MBEDTLS_PSA_KA_MASK_DUAL_USE ( \ + 0) + +typedef struct { + psa_key_type_t MBEDTLS_PRIVATE(type); + psa_key_bits_t MBEDTLS_PRIVATE(bits); + psa_key_lifetime_t MBEDTLS_PRIVATE(lifetime); + mbedtls_svc_key_id_t MBEDTLS_PRIVATE(id); + psa_key_policy_t MBEDTLS_PRIVATE(policy); + psa_key_attributes_flag_t MBEDTLS_PRIVATE(flags); +} psa_core_key_attributes_t; + +#define PSA_CORE_KEY_ATTRIBUTES_INIT { PSA_KEY_TYPE_NONE, 0, \ + PSA_KEY_LIFETIME_VOLATILE, \ + MBEDTLS_SVC_KEY_ID_INIT, \ + PSA_KEY_POLICY_INIT, 0 } + +struct psa_key_attributes_s { + psa_core_key_attributes_t MBEDTLS_PRIVATE(core); +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + psa_key_slot_number_t MBEDTLS_PRIVATE(slot_number); +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + void *MBEDTLS_PRIVATE(domain_parameters); + size_t MBEDTLS_PRIVATE(domain_parameters_size); +}; + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +#define PSA_KEY_ATTRIBUTES_INIT { PSA_CORE_KEY_ATTRIBUTES_INIT, 0, NULL, 0 } +#else +#define PSA_KEY_ATTRIBUTES_INIT { PSA_CORE_KEY_ATTRIBUTES_INIT, NULL, 0 } +#endif + +static inline struct psa_key_attributes_s psa_key_attributes_init(void) +{ + const struct psa_key_attributes_s v = PSA_KEY_ATTRIBUTES_INIT; + return v; +} + +static inline void psa_set_key_id(psa_key_attributes_t *attributes, + mbedtls_svc_key_id_t key) +{ + psa_key_lifetime_t lifetime = attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(lifetime); + + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(id) = key; + + if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) { + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(lifetime) = + PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( + PSA_KEY_LIFETIME_PERSISTENT, + PSA_KEY_LIFETIME_GET_LOCATION(lifetime)); + } +} + +static inline mbedtls_svc_key_id_t psa_get_key_id( + const psa_key_attributes_t *attributes) +{ + return attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(id); +} + +#ifdef MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER +static inline void mbedtls_set_key_owner_id(psa_key_attributes_t *attributes, + mbedtls_key_owner_id_t owner) +{ + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(id).MBEDTLS_PRIVATE(owner) = owner; +} +#endif + +static inline void psa_set_key_lifetime(psa_key_attributes_t *attributes, + psa_key_lifetime_t lifetime) +{ + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(lifetime) = lifetime; + if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) { +#ifdef MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(id).MBEDTLS_PRIVATE(key_id) = 0; +#else + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(id) = 0; +#endif + } +} + +static inline psa_key_lifetime_t psa_get_key_lifetime( + const psa_key_attributes_t *attributes) +{ + return attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(lifetime); +} + +static inline void psa_extend_key_usage_flags(psa_key_usage_t *usage_flags) +{ + if (*usage_flags & PSA_KEY_USAGE_SIGN_HASH) { + *usage_flags |= PSA_KEY_USAGE_SIGN_MESSAGE; + } + + if (*usage_flags & PSA_KEY_USAGE_VERIFY_HASH) { + *usage_flags |= PSA_KEY_USAGE_VERIFY_MESSAGE; + } +} + +static inline void psa_set_key_usage_flags(psa_key_attributes_t *attributes, + psa_key_usage_t usage_flags) +{ + psa_extend_key_usage_flags(&usage_flags); + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(policy).MBEDTLS_PRIVATE(usage) = usage_flags; +} + +static inline psa_key_usage_t psa_get_key_usage_flags( + const psa_key_attributes_t *attributes) +{ + return attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(policy).MBEDTLS_PRIVATE(usage); +} + +static inline void psa_set_key_algorithm(psa_key_attributes_t *attributes, + psa_algorithm_t alg) +{ + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(policy).MBEDTLS_PRIVATE(alg) = alg; +} + +static inline psa_algorithm_t psa_get_key_algorithm( + const psa_key_attributes_t *attributes) +{ + return attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(policy).MBEDTLS_PRIVATE(alg); +} + +/* This function is declared in crypto_extra.h, which comes after this + * header file, but we need the function here, so repeat the declaration. */ +psa_status_t psa_set_key_domain_parameters(psa_key_attributes_t *attributes, + psa_key_type_t type, + const uint8_t *data, + size_t data_length); + +static inline void psa_set_key_type(psa_key_attributes_t *attributes, + psa_key_type_t type) +{ + if (attributes->MBEDTLS_PRIVATE(domain_parameters) == NULL) { + /* Common case: quick path */ + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(type) = type; + } else { + /* Call the bigger function to free the old domain parameters. + * Ignore any errors which may arise due to type requiring + * non-default domain parameters, since this function can't + * report errors. */ + (void) psa_set_key_domain_parameters(attributes, type, NULL, 0); + } +} + +static inline psa_key_type_t psa_get_key_type( + const psa_key_attributes_t *attributes) +{ + return attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(type); +} + +static inline void psa_set_key_bits(psa_key_attributes_t *attributes, + size_t bits) +{ + if (bits > PSA_MAX_KEY_BITS) { + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(bits) = PSA_KEY_BITS_TOO_LARGE; + } else { + attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(bits) = (psa_key_bits_t) bits; + } +} + +static inline size_t psa_get_key_bits( + const psa_key_attributes_t *attributes) +{ + return attributes->MBEDTLS_PRIVATE(core).MBEDTLS_PRIVATE(bits); +} + +/** + * \brief The context for PSA interruptible hash signing. + */ +struct psa_sign_hash_interruptible_operation_s { + /** Unique ID indicating which driver got assigned to do the + * operation. Since driver contexts are driver-specific, swapping + * drivers halfway through the operation is not supported. + * ID values are auto-generated in psa_crypto_driver_wrappers.h + * ID value zero means the context is not valid or not assigned to + * any driver (i.e. none of the driver contexts are active). */ + unsigned int MBEDTLS_PRIVATE(id); + + psa_driver_sign_hash_interruptible_context_t MBEDTLS_PRIVATE(ctx); + + unsigned int MBEDTLS_PRIVATE(error_occurred) : 1; + + uint32_t MBEDTLS_PRIVATE(num_ops); +}; + +#define PSA_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT { 0, { 0 }, 0, 0 } + +static inline struct psa_sign_hash_interruptible_operation_s +psa_sign_hash_interruptible_operation_init(void) +{ + const struct psa_sign_hash_interruptible_operation_s v = + PSA_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT; + + return v; +} + +/** + * \brief The context for PSA interruptible hash verification. + */ +struct psa_verify_hash_interruptible_operation_s { + /** Unique ID indicating which driver got assigned to do the + * operation. Since driver contexts are driver-specific, swapping + * drivers halfway through the operation is not supported. + * ID values are auto-generated in psa_crypto_driver_wrappers.h + * ID value zero means the context is not valid or not assigned to + * any driver (i.e. none of the driver contexts are active). */ + unsigned int MBEDTLS_PRIVATE(id); + + psa_driver_verify_hash_interruptible_context_t MBEDTLS_PRIVATE(ctx); + + unsigned int MBEDTLS_PRIVATE(error_occurred) : 1; + + uint32_t MBEDTLS_PRIVATE(num_ops); +}; + +#define PSA_VERIFY_HASH_INTERRUPTIBLE_OPERATION_INIT { 0, { 0 }, 0, 0 } + +static inline struct psa_verify_hash_interruptible_operation_s +psa_verify_hash_interruptible_operation_init(void) +{ + const struct psa_verify_hash_interruptible_operation_s v = + PSA_VERIFY_HASH_INTERRUPTIBLE_OPERATION_INIT; + + return v; +} + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_CRYPTO_STRUCT_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_types.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_types.h new file mode 100644 index 00000000..a5154fcd --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_types.h @@ -0,0 +1,465 @@ +/** + * \file psa/crypto_types.h + * + * \brief PSA cryptography module: type aliases. + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. Drivers must include the appropriate driver + * header file. + * + * This file contains portable definitions of integral types for properties + * of cryptographic keys, designations of cryptographic algorithms, and + * error codes returned by the library. + * + * This header file does not declare any function. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_TYPES_H +#define PSA_CRYPTO_TYPES_H + +/* Make sure the Mbed TLS configuration is visible. */ +#include "mbedtls/build_info.h" +/* Define the MBEDTLS_PRIVATE macro. */ +#include "mbedtls/private_access.h" + +#if defined(MBEDTLS_PSA_CRYPTO_PLATFORM_FILE) +#include MBEDTLS_PSA_CRYPTO_PLATFORM_FILE +#else +#include "crypto_platform.h" +#endif + +#include + +/** \defgroup error Error codes + * @{ + */ + +/** + * \brief Function return status. + * + * This is either #PSA_SUCCESS (which is zero), indicating success, + * or a small negative value indicating that an error occurred. Errors are + * encoded as one of the \c PSA_ERROR_xxx values defined here. */ +/* If #PSA_SUCCESS is already defined, it means that #psa_status_t + * is also defined in an external header, so prevent its multiple + * definition. + */ +#ifndef PSA_SUCCESS +typedef int32_t psa_status_t; +#endif + +/**@}*/ + +/** \defgroup crypto_types Key and algorithm types + * @{ + */ + +/** \brief Encoding of a key type. + * + * Values of this type are generally constructed by macros called + * `PSA_KEY_TYPE_xxx`. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint16_t psa_key_type_t; + +/** The type of PSA elliptic curve family identifiers. + * + * Values of this type are generally constructed by macros called + * `PSA_ECC_FAMILY_xxx`. + * + * The curve identifier is required to create an ECC key using the + * PSA_KEY_TYPE_ECC_KEY_PAIR() or PSA_KEY_TYPE_ECC_PUBLIC_KEY() + * macros. + * + * Values defined by this standard will never be in the range 0x80-0xff. + * Vendors who define additional families must use an encoding in this range. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint8_t psa_ecc_family_t; + +/** The type of PSA Diffie-Hellman group family identifiers. + * + * Values of this type are generally constructed by macros called + * `PSA_DH_FAMILY_xxx`. + * + * The group identifier is required to create a Diffie-Hellman key using the + * PSA_KEY_TYPE_DH_KEY_PAIR() or PSA_KEY_TYPE_DH_PUBLIC_KEY() + * macros. + * + * Values defined by this standard will never be in the range 0x80-0xff. + * Vendors who define additional families must use an encoding in this range. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint8_t psa_dh_family_t; + +/** \brief Encoding of a cryptographic algorithm. + * + * Values of this type are generally constructed by macros called + * `PSA_ALG_xxx`. + * + * For algorithms that can be applied to multiple key types, this type + * does not encode the key type. For example, for symmetric ciphers + * based on a block cipher, #psa_algorithm_t encodes the block cipher + * mode and the padding mode while the block cipher itself is encoded + * via #psa_key_type_t. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint32_t psa_algorithm_t; + +/**@}*/ + +/** \defgroup key_lifetimes Key lifetimes + * @{ + */ + +/** Encoding of key lifetimes. + * + * The lifetime of a key indicates where it is stored and what system actions + * may create and destroy it. + * + * Lifetime values have the following structure: + * - Bits 0-7 (#PSA_KEY_LIFETIME_GET_PERSISTENCE(\c lifetime)): + * persistence level. This value indicates what device management + * actions can cause it to be destroyed. In particular, it indicates + * whether the key is _volatile_ or _persistent_. + * See ::psa_key_persistence_t for more information. + * - Bits 8-31 (#PSA_KEY_LIFETIME_GET_LOCATION(\c lifetime)): + * location indicator. This value indicates which part of the system + * has access to the key material and can perform operations using the key. + * See ::psa_key_location_t for more information. + * + * Volatile keys are automatically destroyed when the application instance + * terminates or on a power reset of the device. Persistent keys are + * preserved until the application explicitly destroys them or until an + * integration-specific device management event occurs (for example, + * a factory reset). + * + * Persistent keys have a key identifier of type #mbedtls_svc_key_id_t. + * This identifier remains valid throughout the lifetime of the key, + * even if the application instance that created the key terminates. + * The application can call psa_open_key() to open a persistent key that + * it created previously. + * + * The default lifetime of a key is #PSA_KEY_LIFETIME_VOLATILE. The lifetime + * #PSA_KEY_LIFETIME_PERSISTENT is supported if persistent storage is + * available. Other lifetime values may be supported depending on the + * library configuration. + * + * Values of this type are generally constructed by macros called + * `PSA_KEY_LIFETIME_xxx`. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint32_t psa_key_lifetime_t; + +/** Encoding of key persistence levels. + * + * What distinguishes different persistence levels is what device management + * events may cause keys to be destroyed. _Volatile_ keys are destroyed + * by a power reset. Persistent keys may be destroyed by events such as + * a transfer of ownership or a factory reset. What management events + * actually affect persistent keys at different levels is outside the + * scope of the PSA Cryptography specification. + * + * The PSA Cryptography specification defines the following values of + * persistence levels: + * - \c 0 = #PSA_KEY_PERSISTENCE_VOLATILE: volatile key. + * A volatile key is automatically destroyed by the implementation when + * the application instance terminates. In particular, a volatile key + * is automatically destroyed on a power reset of the device. + * - \c 1 = #PSA_KEY_PERSISTENCE_DEFAULT: + * persistent key with a default lifetime. + * - \c 2-254: currently not supported by Mbed TLS. + * - \c 255 = #PSA_KEY_PERSISTENCE_READ_ONLY: + * read-only or write-once key. + * A key with this persistence level cannot be destroyed. + * Mbed TLS does not currently offer a way to create such keys, but + * integrations of Mbed TLS can use it for built-in keys that the + * application cannot modify (for example, a hardware unique key (HUK)). + * + * \note Key persistence levels are 8-bit values. Key management + * interfaces operate on lifetimes (type ::psa_key_lifetime_t) which + * encode the persistence as the lower 8 bits of a 32-bit value. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint8_t psa_key_persistence_t; + +/** Encoding of key location indicators. + * + * If an integration of Mbed TLS can make calls to external + * cryptoprocessors such as secure elements, the location of a key + * indicates which secure element performs the operations on the key. + * Depending on the design of the secure element, the key + * material may be stored either in the secure element, or + * in wrapped (encrypted) form alongside the key metadata in the + * primary local storage. + * + * The PSA Cryptography API specification defines the following values of + * location indicators: + * - \c 0: primary local storage. + * This location is always available. + * The primary local storage is typically the same storage area that + * contains the key metadata. + * - \c 1: primary secure element. + * Integrations of Mbed TLS should support this value if there is a secure + * element attached to the operating environment. + * As a guideline, secure elements may provide higher resistance against + * side channel and physical attacks than the primary local storage, but may + * have restrictions on supported key types, sizes, policies and operations + * and may have different performance characteristics. + * - \c 2-0x7fffff: other locations defined by a PSA specification. + * The PSA Cryptography API does not currently assign any meaning to these + * locations, but future versions of that specification or other PSA + * specifications may do so. + * - \c 0x800000-0xffffff: vendor-defined locations. + * No PSA specification will assign a meaning to locations in this range. + * + * \note Key location indicators are 24-bit values. Key management + * interfaces operate on lifetimes (type ::psa_key_lifetime_t) which + * encode the location as the upper 24 bits of a 32-bit value. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint32_t psa_key_location_t; + +/** Encoding of identifiers of persistent keys. + * + * - Applications may freely choose key identifiers in the range + * #PSA_KEY_ID_USER_MIN to #PSA_KEY_ID_USER_MAX. + * - The implementation may define additional key identifiers in the range + * #PSA_KEY_ID_VENDOR_MIN to #PSA_KEY_ID_VENDOR_MAX. + * - 0 is reserved as an invalid key identifier. + * - Key identifiers outside these ranges are reserved for future use. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to how values are allocated must require careful + * consideration to allow backward compatibility. + */ +typedef uint32_t psa_key_id_t; + +/** Encoding of key identifiers as seen inside the PSA Crypto implementation. + * + * When PSA Crypto is built as a library inside an application, this type + * is identical to #psa_key_id_t. When PSA Crypto is built as a service + * that can store keys on behalf of multiple clients, this type + * encodes the #psa_key_id_t value seen by each client application as + * well as extra information that identifies the client that owns + * the key. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +#if !defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) +typedef psa_key_id_t mbedtls_svc_key_id_t; + +#else /* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */ +/* Implementation-specific: The Mbed Cryptography library can be built as + * part of a multi-client service that exposes the PSA Cryptography API in each + * client and encodes the client identity in the key identifier argument of + * functions such as psa_open_key(). + */ +typedef struct { + psa_key_id_t MBEDTLS_PRIVATE(key_id); + mbedtls_key_owner_id_t MBEDTLS_PRIVATE(owner); +} mbedtls_svc_key_id_t; + +#endif /* !MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */ + +/**@}*/ + +/** \defgroup policy Key policies + * @{ + */ + +/** \brief Encoding of permitted usage on a key. + * + * Values of this type are generally constructed as bitwise-ors of macros + * called `PSA_KEY_USAGE_xxx`. + * + * \note Values of this type are encoded in the persistent key store. + * Any changes to existing values will require bumping the storage + * format version and providing a translation when reading the old + * format. + */ +typedef uint32_t psa_key_usage_t; + +/**@}*/ + +/** \defgroup attributes Key attributes + * @{ + */ + +/** The type of a structure containing key attributes. + * + * This is an opaque structure that can represent the metadata of a key + * object. Metadata that can be stored in attributes includes: + * - The location of the key in storage, indicated by its key identifier + * and its lifetime. + * - The key's policy, comprising usage flags and a specification of + * the permitted algorithm(s). + * - Information about the key itself: the key type and its size. + * - Additional implementation-defined attributes. + * + * The actual key material is not considered an attribute of a key. + * Key attributes do not contain information that is generally considered + * highly confidential. + * + * An attribute structure works like a simple data structure where each function + * `psa_set_key_xxx` sets a field and the corresponding function + * `psa_get_key_xxx` retrieves the value of the corresponding field. + * However, a future version of the library may report values that are + * equivalent to the original one, but have a different encoding. Invalid + * values may be mapped to different, also invalid values. + * + * An attribute structure may contain references to auxiliary resources, + * for example pointers to allocated memory or indirect references to + * pre-calculated values. In order to free such resources, the application + * must call psa_reset_key_attributes(). As an exception, calling + * psa_reset_key_attributes() on an attribute structure is optional if + * the structure has only been modified by the following functions + * since it was initialized or last reset with psa_reset_key_attributes(): + * - psa_set_key_id() + * - psa_set_key_lifetime() + * - psa_set_key_type() + * - psa_set_key_bits() + * - psa_set_key_usage_flags() + * - psa_set_key_algorithm() + * + * Before calling any function on a key attribute structure, the application + * must initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_key_attributes_t attributes; + * memset(&attributes, 0, sizeof(attributes)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_key_attributes_t attributes = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_KEY_ATTRIBUTES_INIT, + * for example: + * \code + * psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + * \endcode + * - Assign the result of the function psa_key_attributes_init() + * to the structure, for example: + * \code + * psa_key_attributes_t attributes; + * attributes = psa_key_attributes_init(); + * \endcode + * + * A freshly initialized attribute structure contains the following + * values: + * + * - lifetime: #PSA_KEY_LIFETIME_VOLATILE. + * - key identifier: 0 (which is not a valid key identifier). + * - type: \c 0 (meaning that the type is unspecified). + * - key size: \c 0 (meaning that the size is unspecified). + * - usage flags: \c 0 (which allows no usage except exporting a public key). + * - algorithm: \c 0 (which allows no cryptographic usage, but allows + * exporting). + * + * A typical sequence to create a key is as follows: + * -# Create and initialize an attribute structure. + * -# If the key is persistent, call psa_set_key_id(). + * Also call psa_set_key_lifetime() to place the key in a non-default + * location. + * -# Set the key policy with psa_set_key_usage_flags() and + * psa_set_key_algorithm(). + * -# Set the key type with psa_set_key_type(). + * Skip this step if copying an existing key with psa_copy_key(). + * -# When generating a random key with psa_generate_key() or deriving a key + * with psa_key_derivation_output_key(), set the desired key size with + * psa_set_key_bits(). + * -# Call a key creation function: psa_import_key(), psa_generate_key(), + * psa_key_derivation_output_key() or psa_copy_key(). This function reads + * the attribute structure, creates a key with these attributes, and + * outputs a key identifier to the newly created key. + * -# The attribute structure is now no longer necessary. + * You may call psa_reset_key_attributes(), although this is optional + * with the workflow presented here because the attributes currently + * defined in this specification do not require any additional resources + * beyond the structure itself. + * + * A typical sequence to query a key's attributes is as follows: + * -# Call psa_get_key_attributes(). + * -# Call `psa_get_key_xxx` functions to retrieve the attribute(s) that + * you are interested in. + * -# Call psa_reset_key_attributes() to free any resources that may be + * used by the attribute structure. + * + * Once a key has been created, it is impossible to change its attributes. + */ +typedef struct psa_key_attributes_s psa_key_attributes_t; + + +#ifndef __DOXYGEN_ONLY__ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +/* Mbed Crypto defines this type in crypto_types.h because it is also + * visible to applications through an implementation-specific extension. + * For the PSA Cryptography specification, this type is only visible + * via crypto_se_driver.h. */ +typedef uint64_t psa_key_slot_number_t; +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ +#endif /* !__DOXYGEN_ONLY__ */ + +/**@}*/ + +/** \defgroup derivation Key derivation + * @{ + */ + +/** \brief Encoding of the step of a key derivation. + * + * Values of this type are generally constructed by macros called + * `PSA_KEY_DERIVATION_INPUT_xxx`. + */ +typedef uint16_t psa_key_derivation_step_t; + +/**@}*/ + +#endif /* PSA_CRYPTO_TYPES_H */ diff --git a/r5dev/thirdparty/mbedtls/include/psa/crypto_values.h b/r5dev/thirdparty/mbedtls/include/psa/crypto_values.h new file mode 100644 index 00000000..39acd96c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/include/psa/crypto_values.h @@ -0,0 +1,2763 @@ +/** + * \file psa/crypto_values.h + * + * \brief PSA cryptography module: macros to build and analyze integer values. + * + * \note This file may not be included directly. Applications must + * include psa/crypto.h. Drivers must include the appropriate driver + * header file. + * + * This file contains portable definitions of macros to build and analyze + * values of integral types that encode properties of cryptographic keys, + * designations of cryptographic algorithms, and error codes returned by + * the library. + * + * Note that many of the constants defined in this file are embedded in + * the persistent key store, as part of key metadata (including usage + * policies). As a consequence, they must not be changed (unless the storage + * format version changes). + * + * This header file only defines preprocessor macros. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_VALUES_H +#define PSA_CRYPTO_VALUES_H +#include "mbedtls/private_access.h" + +/** \defgroup error Error codes + * @{ + */ + +/* PSA error codes */ + +/* Error codes are standardized across PSA domains (framework, crypto, storage, + * etc.). Do not change the values in this section or even the expansions + * of each macro: it must be possible to `#include` both this header + * and some other PSA component's headers in the same C source, + * which will lead to duplicate definitions of the `PSA_SUCCESS` and + * `PSA_ERROR_xxx` macros, which is ok if and only if the macros expand + * to the same sequence of tokens. + * + * If you must add a new + * value, check with the Arm PSA framework group to pick one that other + * domains aren't already using. */ + +/* Tell uncrustify not to touch the constant definitions, otherwise + * it might change the spacing to something that is not PSA-compliant + * (e.g. adding a space after casts). + * + * *INDENT-OFF* + */ + +/** The action was completed successfully. */ +#define PSA_SUCCESS ((psa_status_t)0) + +/** An error occurred that does not correspond to any defined + * failure cause. + * + * Implementations may use this error code if none of the other standard + * error codes are applicable. */ +#define PSA_ERROR_GENERIC_ERROR ((psa_status_t)-132) + +/** The requested operation or a parameter is not supported + * by this implementation. + * + * Implementations should return this error code when an enumeration + * parameter such as a key type, algorithm, etc. is not recognized. + * If a combination of parameters is recognized and identified as + * not valid, return #PSA_ERROR_INVALID_ARGUMENT instead. */ +#define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)-134) + +/** The requested action is denied by a policy. + * + * Implementations should return this error code when the parameters + * are recognized as valid and supported, and a policy explicitly + * denies the requested operation. + * + * If a subset of the parameters of a function call identify a + * forbidden operation, and another subset of the parameters are + * not valid or not supported, it is unspecified whether the function + * returns #PSA_ERROR_NOT_PERMITTED, #PSA_ERROR_NOT_SUPPORTED or + * #PSA_ERROR_INVALID_ARGUMENT. */ +#define PSA_ERROR_NOT_PERMITTED ((psa_status_t)-133) + +/** An output buffer is too small. + * + * Applications can call the \c PSA_xxx_SIZE macro listed in the function + * description to determine a sufficient buffer size. + * + * Implementations should preferably return this error code only + * in cases when performing the operation with a larger output + * buffer would succeed. However implementations may return this + * error if a function has invalid or unsupported parameters in addition + * to the parameters that determine the necessary output buffer size. */ +#define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)-138) + +/** Asking for an item that already exists + * + * Implementations should return this error, when attempting + * to write an item (like a key) that already exists. */ +#define PSA_ERROR_ALREADY_EXISTS ((psa_status_t)-139) + +/** Asking for an item that doesn't exist + * + * Implementations should return this error, if a requested item (like + * a key) does not exist. */ +#define PSA_ERROR_DOES_NOT_EXIST ((psa_status_t)-140) + +/** The requested action cannot be performed in the current state. + * + * Multipart operations return this error when one of the + * functions is called out of sequence. Refer to the function + * descriptions for permitted sequencing of functions. + * + * Implementations shall not return this error code to indicate + * that a key either exists or not, + * but shall instead return #PSA_ERROR_ALREADY_EXISTS or #PSA_ERROR_DOES_NOT_EXIST + * as applicable. + * + * Implementations shall not return this error code to indicate that a + * key identifier is invalid, but shall return #PSA_ERROR_INVALID_HANDLE + * instead. */ +#define PSA_ERROR_BAD_STATE ((psa_status_t)-137) + +/** The parameters passed to the function are invalid. + * + * Implementations may return this error any time a parameter or + * combination of parameters are recognized as invalid. + * + * Implementations shall not return this error code to indicate that a + * key identifier is invalid, but shall return #PSA_ERROR_INVALID_HANDLE + * instead. + */ +#define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)-135) + +/** There is not enough runtime memory. + * + * If the action is carried out across multiple security realms, this + * error can refer to available memory in any of the security realms. */ +#define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)-141) + +/** There is not enough persistent storage. + * + * Functions that modify the key storage return this error code if + * there is insufficient storage space on the host media. In addition, + * many functions that do not otherwise access storage may return this + * error code if the implementation requires a mandatory log entry for + * the requested action and the log storage space is full. */ +#define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)-142) + +/** There was a communication failure inside the implementation. + * + * This can indicate a communication failure between the application + * and an external cryptoprocessor or between the cryptoprocessor and + * an external volatile or persistent memory. A communication failure + * may be transient or permanent depending on the cause. + * + * \warning If a function returns this error, it is undetermined + * whether the requested action has completed or not. Implementations + * should return #PSA_SUCCESS on successful completion whenever + * possible, however functions may return #PSA_ERROR_COMMUNICATION_FAILURE + * if the requested action was completed successfully in an external + * cryptoprocessor but there was a breakdown of communication before + * the cryptoprocessor could report the status to the application. + */ +#define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)-145) + +/** There was a storage failure that may have led to data loss. + * + * This error indicates that some persistent storage is corrupted. + * It should not be used for a corruption of volatile memory + * (use #PSA_ERROR_CORRUPTION_DETECTED), for a communication error + * between the cryptoprocessor and its external storage (use + * #PSA_ERROR_COMMUNICATION_FAILURE), or when the storage is + * in a valid state but is full (use #PSA_ERROR_INSUFFICIENT_STORAGE). + * + * Note that a storage failure does not indicate that any data that was + * previously read is invalid. However this previously read data may no + * longer be readable from storage. + * + * When a storage failure occurs, it is no longer possible to ensure + * the global integrity of the keystore. Depending on the global + * integrity guarantees offered by the implementation, access to other + * data may or may not fail even if the data is still readable but + * its integrity cannot be guaranteed. + * + * Implementations should only use this error code to report a + * permanent storage corruption. However application writers should + * keep in mind that transient errors while reading the storage may be + * reported using this error code. */ +#define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)-146) + +/** A hardware failure was detected. + * + * A hardware failure may be transient or permanent depending on the + * cause. */ +#define PSA_ERROR_HARDWARE_FAILURE ((psa_status_t)-147) + +/** A tampering attempt was detected. + * + * If an application receives this error code, there is no guarantee + * that previously accessed or computed data was correct and remains + * confidential. Applications should not perform any security function + * and should enter a safe failure state. + * + * Implementations may return this error code if they detect an invalid + * state that cannot happen during normal operation and that indicates + * that the implementation's security guarantees no longer hold. Depending + * on the implementation architecture and on its security and safety goals, + * the implementation may forcibly terminate the application. + * + * This error code is intended as a last resort when a security breach + * is detected and it is unsure whether the keystore data is still + * protected. Implementations shall only return this error code + * to report an alarm from a tampering detector, to indicate that + * the confidentiality of stored data can no longer be guaranteed, + * or to indicate that the integrity of previously returned data is now + * considered compromised. Implementations shall not use this error code + * to indicate a hardware failure that merely makes it impossible to + * perform the requested operation (use #PSA_ERROR_COMMUNICATION_FAILURE, + * #PSA_ERROR_STORAGE_FAILURE, #PSA_ERROR_HARDWARE_FAILURE, + * #PSA_ERROR_INSUFFICIENT_ENTROPY or other applicable error code + * instead). + * + * This error indicates an attack against the application. Implementations + * shall not return this error code as a consequence of the behavior of + * the application itself. */ +#define PSA_ERROR_CORRUPTION_DETECTED ((psa_status_t)-151) + +/** There is not enough entropy to generate random data needed + * for the requested action. + * + * This error indicates a failure of a hardware random generator. + * Application writers should note that this error can be returned not + * only by functions whose purpose is to generate random data, such + * as key, IV or nonce generation, but also by functions that execute + * an algorithm with a randomized result, as well as functions that + * use randomization of intermediate computations as a countermeasure + * to certain attacks. + * + * Implementations should avoid returning this error after psa_crypto_init() + * has succeeded. Implementations should generate sufficient + * entropy during initialization and subsequently use a cryptographically + * secure pseudorandom generator (PRNG). However implementations may return + * this error at any time if a policy requires the PRNG to be reseeded + * during normal operation. */ +#define PSA_ERROR_INSUFFICIENT_ENTROPY ((psa_status_t)-148) + +/** The signature, MAC or hash is incorrect. + * + * Verification functions return this error if the verification + * calculations completed successfully, and the value to be verified + * was determined to be incorrect. + * + * If the value to verify has an invalid size, implementations may return + * either #PSA_ERROR_INVALID_ARGUMENT or #PSA_ERROR_INVALID_SIGNATURE. */ +#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149) + +/** The decrypted padding is incorrect. + * + * \warning In some protocols, when decrypting data, it is essential that + * the behavior of the application does not depend on whether the padding + * is correct, down to precise timing. Applications should prefer + * protocols that use authenticated encryption rather than plain + * encryption. If the application must perform a decryption of + * unauthenticated data, the application writer should take care not + * to reveal whether the padding is invalid. + * + * Implementations should strive to make valid and invalid padding + * as close as possible to indistinguishable to an external observer. + * In particular, the timing of a decryption operation should not + * depend on the validity of the padding. */ +#define PSA_ERROR_INVALID_PADDING ((psa_status_t)-150) + +/** Return this error when there's insufficient data when attempting + * to read from a resource. */ +#define PSA_ERROR_INSUFFICIENT_DATA ((psa_status_t)-143) + +/** The key identifier is not valid. See also :ref:\`key-handles\`. + */ +#define PSA_ERROR_INVALID_HANDLE ((psa_status_t)-136) + +/** Stored data has been corrupted. + * + * This error indicates that some persistent storage has suffered corruption. + * It does not indicate the following situations, which have specific error + * codes: + * + * - A corruption of volatile memory - use #PSA_ERROR_CORRUPTION_DETECTED. + * - A communication error between the cryptoprocessor and its external + * storage - use #PSA_ERROR_COMMUNICATION_FAILURE. + * - When the storage is in a valid state but is full - use + * #PSA_ERROR_INSUFFICIENT_STORAGE. + * - When the storage fails for other reasons - use + * #PSA_ERROR_STORAGE_FAILURE. + * - When the stored data is not valid - use #PSA_ERROR_DATA_INVALID. + * + * \note A storage corruption does not indicate that any data that was + * previously read is invalid. However this previously read data might no + * longer be readable from storage. + * + * When a storage failure occurs, it is no longer possible to ensure the + * global integrity of the keystore. + */ +#define PSA_ERROR_DATA_CORRUPT ((psa_status_t)-152) + +/** Data read from storage is not valid for the implementation. + * + * This error indicates that some data read from storage does not have a valid + * format. It does not indicate the following situations, which have specific + * error codes: + * + * - When the storage or stored data is corrupted - use #PSA_ERROR_DATA_CORRUPT + * - When the storage fails for other reasons - use #PSA_ERROR_STORAGE_FAILURE + * - An invalid argument to the API - use #PSA_ERROR_INVALID_ARGUMENT + * + * This error is typically a result of either storage corruption on a + * cleartext storage backend, or an attempt to read data that was + * written by an incompatible version of the library. + */ +#define PSA_ERROR_DATA_INVALID ((psa_status_t)-153) + +/** The function that returns this status is defined as interruptible and + * still has work to do, thus the user should call the function again with the + * same operation context until it either returns #PSA_SUCCESS or any other + * error. This is not an error per se, more a notification of status. + */ +#define PSA_OPERATION_INCOMPLETE ((psa_status_t)-248) + +/* *INDENT-ON* */ + +/**@}*/ + +/** \defgroup crypto_types Key and algorithm types + * @{ + */ + +/* Note that key type values, including ECC family and DH group values, are + * embedded in the persistent key store, as part of key metadata. As a + * consequence, they must not be changed (unless the storage format version + * changes). + */ + +/** An invalid key type value. + * + * Zero is not the encoding of any key type. + */ +#define PSA_KEY_TYPE_NONE ((psa_key_type_t) 0x0000) + +/** Vendor-defined key type flag. + * + * Key types defined by this standard will never have the + * #PSA_KEY_TYPE_VENDOR_FLAG bit set. Vendors who define additional key types + * must use an encoding with the #PSA_KEY_TYPE_VENDOR_FLAG bit set and should + * respect the bitwise structure used by standard encodings whenever practical. + */ +#define PSA_KEY_TYPE_VENDOR_FLAG ((psa_key_type_t) 0x8000) + +#define PSA_KEY_TYPE_CATEGORY_MASK ((psa_key_type_t) 0x7000) +#define PSA_KEY_TYPE_CATEGORY_RAW ((psa_key_type_t) 0x1000) +#define PSA_KEY_TYPE_CATEGORY_SYMMETRIC ((psa_key_type_t) 0x2000) +#define PSA_KEY_TYPE_CATEGORY_PUBLIC_KEY ((psa_key_type_t) 0x4000) +#define PSA_KEY_TYPE_CATEGORY_KEY_PAIR ((psa_key_type_t) 0x7000) + +#define PSA_KEY_TYPE_CATEGORY_FLAG_PAIR ((psa_key_type_t) 0x3000) + +/** Whether a key type is vendor-defined. + * + * See also #PSA_KEY_TYPE_VENDOR_FLAG. + */ +#define PSA_KEY_TYPE_IS_VENDOR_DEFINED(type) \ + (((type) & PSA_KEY_TYPE_VENDOR_FLAG) != 0) + +/** Whether a key type is an unstructured array of bytes. + * + * This encompasses both symmetric keys and non-key data. + */ +#define PSA_KEY_TYPE_IS_UNSTRUCTURED(type) \ + (((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_RAW || \ + ((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_SYMMETRIC) + +/** Whether a key type is asymmetric: either a key pair or a public key. */ +#define PSA_KEY_TYPE_IS_ASYMMETRIC(type) \ + (((type) & PSA_KEY_TYPE_CATEGORY_MASK \ + & ~PSA_KEY_TYPE_CATEGORY_FLAG_PAIR) == \ + PSA_KEY_TYPE_CATEGORY_PUBLIC_KEY) +/** Whether a key type is the public part of a key pair. */ +#define PSA_KEY_TYPE_IS_PUBLIC_KEY(type) \ + (((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_PUBLIC_KEY) +/** Whether a key type is a key pair containing a private part and a public + * part. */ +#define PSA_KEY_TYPE_IS_KEY_PAIR(type) \ + (((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_KEY_PAIR) +/** The key pair type corresponding to a public key type. + * + * You may also pass a key pair type as \p type, it will be left unchanged. + * + * \param type A public key type or key pair type. + * + * \return The corresponding key pair type. + * If \p type is not a public key or a key pair, + * the return value is undefined. + */ +#define PSA_KEY_TYPE_KEY_PAIR_OF_PUBLIC_KEY(type) \ + ((type) | PSA_KEY_TYPE_CATEGORY_FLAG_PAIR) +/** The public key type corresponding to a key pair type. + * + * You may also pass a key pair type as \p type, it will be left unchanged. + * + * \param type A public key type or key pair type. + * + * \return The corresponding public key type. + * If \p type is not a public key or a key pair, + * the return value is undefined. + */ +#define PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) \ + ((type) & ~PSA_KEY_TYPE_CATEGORY_FLAG_PAIR) + +/** Raw data. + * + * A "key" of this type cannot be used for any cryptographic operation. + * Applications may use this type to store arbitrary data in the keystore. */ +#define PSA_KEY_TYPE_RAW_DATA ((psa_key_type_t) 0x1001) + +/** HMAC key. + * + * The key policy determines which underlying hash algorithm the key can be + * used for. + * + * HMAC keys should generally have the same size as the underlying hash. + * This size can be calculated with #PSA_HASH_LENGTH(\c alg) where + * \c alg is the HMAC algorithm or the underlying hash algorithm. */ +#define PSA_KEY_TYPE_HMAC ((psa_key_type_t) 0x1100) + +/** A secret for key derivation. + * + * This key type is for high-entropy secrets only. For low-entropy secrets, + * #PSA_KEY_TYPE_PASSWORD should be used instead. + * + * These keys can be used as the #PSA_KEY_DERIVATION_INPUT_SECRET or + * #PSA_KEY_DERIVATION_INPUT_PASSWORD input of key derivation algorithms. + * + * The key policy determines which key derivation algorithm the key + * can be used for. + */ +#define PSA_KEY_TYPE_DERIVE ((psa_key_type_t) 0x1200) + +/** A low-entropy secret for password hashing or key derivation. + * + * This key type is suitable for passwords and passphrases which are typically + * intended to be memorizable by humans, and have a low entropy relative to + * their size. It can be used for randomly generated or derived keys with + * maximum or near-maximum entropy, but #PSA_KEY_TYPE_DERIVE is more suitable + * for such keys. It is not suitable for passwords with extremely low entropy, + * such as numerical PINs. + * + * These keys can be used as the #PSA_KEY_DERIVATION_INPUT_PASSWORD input of + * key derivation algorithms. Algorithms that accept such an input were + * designed to accept low-entropy secret and are known as password hashing or + * key stretching algorithms. + * + * These keys cannot be used as the #PSA_KEY_DERIVATION_INPUT_SECRET input of + * key derivation algorithms, as the algorithms that take such an input expect + * it to be high-entropy. + * + * The key policy determines which key derivation algorithm the key can be + * used for, among the permissible subset defined above. + */ +#define PSA_KEY_TYPE_PASSWORD ((psa_key_type_t) 0x1203) + +/** A secret value that can be used to verify a password hash. + * + * The key policy determines which key derivation algorithm the key + * can be used for, among the same permissible subset as for + * #PSA_KEY_TYPE_PASSWORD. + */ +#define PSA_KEY_TYPE_PASSWORD_HASH ((psa_key_type_t) 0x1205) + +/** A secret value that can be used in when computing a password hash. + * + * The key policy determines which key derivation algorithm the key + * can be used for, among the subset of algorithms that can use pepper. + */ +#define PSA_KEY_TYPE_PEPPER ((psa_key_type_t) 0x1206) + +/** Key for a cipher, AEAD or MAC algorithm based on the AES block cipher. + * + * The size of the key can be 16 bytes (AES-128), 24 bytes (AES-192) or + * 32 bytes (AES-256). + */ +#define PSA_KEY_TYPE_AES ((psa_key_type_t) 0x2400) + +/** Key for a cipher, AEAD or MAC algorithm based on the + * ARIA block cipher. */ +#define PSA_KEY_TYPE_ARIA ((psa_key_type_t) 0x2406) + +/** Key for a cipher or MAC algorithm based on DES or 3DES (Triple-DES). + * + * The size of the key can be 64 bits (single DES), 128 bits (2-key 3DES) or + * 192 bits (3-key 3DES). + * + * Note that single DES and 2-key 3DES are weak and strongly + * deprecated and should only be used to decrypt legacy data. 3-key 3DES + * is weak and deprecated and should only be used in legacy protocols. + */ +#define PSA_KEY_TYPE_DES ((psa_key_type_t) 0x2301) + +/** Key for a cipher, AEAD or MAC algorithm based on the + * Camellia block cipher. */ +#define PSA_KEY_TYPE_CAMELLIA ((psa_key_type_t) 0x2403) + +/** Key for the ChaCha20 stream cipher or the Chacha20-Poly1305 AEAD algorithm. + * + * ChaCha20 and the ChaCha20_Poly1305 construction are defined in RFC 7539. + * + * \note For ChaCha20 and ChaCha20_Poly1305, Mbed TLS only supports + * 12-byte nonces. + * + * \note For ChaCha20, the initial counter value is 0. To encrypt or decrypt + * with the initial counter value 1, you can process and discard a + * 64-byte block before the real data. + */ +#define PSA_KEY_TYPE_CHACHA20 ((psa_key_type_t) 0x2004) + +/** RSA public key. + * + * The size of an RSA key is the bit size of the modulus. + */ +#define PSA_KEY_TYPE_RSA_PUBLIC_KEY ((psa_key_type_t) 0x4001) +/** RSA key pair (private and public key). + * + * The size of an RSA key is the bit size of the modulus. + */ +#define PSA_KEY_TYPE_RSA_KEY_PAIR ((psa_key_type_t) 0x7001) +/** Whether a key type is an RSA key (pair or public-only). */ +#define PSA_KEY_TYPE_IS_RSA(type) \ + (PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) == PSA_KEY_TYPE_RSA_PUBLIC_KEY) + +#define PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE ((psa_key_type_t) 0x4100) +#define PSA_KEY_TYPE_ECC_KEY_PAIR_BASE ((psa_key_type_t) 0x7100) +#define PSA_KEY_TYPE_ECC_CURVE_MASK ((psa_key_type_t) 0x00ff) +/** Elliptic curve key pair. + * + * The size of an elliptic curve key is the bit size associated with the curve, + * i.e. the bit size of *q* for a curve over a field *Fq*. + * See the documentation of `PSA_ECC_FAMILY_xxx` curve families for details. + * + * \param curve A value of type ::psa_ecc_family_t that + * identifies the ECC curve to be used. + */ +#define PSA_KEY_TYPE_ECC_KEY_PAIR(curve) \ + (PSA_KEY_TYPE_ECC_KEY_PAIR_BASE | (curve)) +/** Elliptic curve public key. + * + * The size of an elliptic curve public key is the same as the corresponding + * private key (see #PSA_KEY_TYPE_ECC_KEY_PAIR and the documentation of + * `PSA_ECC_FAMILY_xxx` curve families). + * + * \param curve A value of type ::psa_ecc_family_t that + * identifies the ECC curve to be used. + */ +#define PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve) \ + (PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | (curve)) + +/** Whether a key type is an elliptic curve key (pair or public-only). */ +#define PSA_KEY_TYPE_IS_ECC(type) \ + ((PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) & \ + ~PSA_KEY_TYPE_ECC_CURVE_MASK) == PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE) +/** Whether a key type is an elliptic curve key pair. */ +#define PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type) \ + (((type) & ~PSA_KEY_TYPE_ECC_CURVE_MASK) == \ + PSA_KEY_TYPE_ECC_KEY_PAIR_BASE) +/** Whether a key type is an elliptic curve public key. */ +#define PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(type) \ + (((type) & ~PSA_KEY_TYPE_ECC_CURVE_MASK) == \ + PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE) + +/** Extract the curve from an elliptic curve key type. */ +#define PSA_KEY_TYPE_ECC_GET_FAMILY(type) \ + ((psa_ecc_family_t) (PSA_KEY_TYPE_IS_ECC(type) ? \ + ((type) & PSA_KEY_TYPE_ECC_CURVE_MASK) : \ + 0)) + +/** Check if the curve of given family is Weierstrass elliptic curve. */ +#define PSA_ECC_FAMILY_IS_WEIERSTRASS(family) ((family & 0xc0) == 0) + +/** SEC Koblitz curves over prime fields. + * + * This family comprises the following curves: + * secp192k1, secp224k1, secp256k1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECP_K1 ((psa_ecc_family_t) 0x17) + +/** SEC random curves over prime fields. + * + * This family comprises the following curves: + * secp192k1, secp224r1, secp256r1, secp384r1, secp521r1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECP_R1 ((psa_ecc_family_t) 0x12) +/* SECP160R2 (SEC2 v1, obsolete) */ +#define PSA_ECC_FAMILY_SECP_R2 ((psa_ecc_family_t) 0x1b) + +/** SEC Koblitz curves over binary fields. + * + * This family comprises the following curves: + * sect163k1, sect233k1, sect239k1, sect283k1, sect409k1, sect571k1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECT_K1 ((psa_ecc_family_t) 0x27) + +/** SEC random curves over binary fields. + * + * This family comprises the following curves: + * sect163r1, sect233r1, sect283r1, sect409r1, sect571r1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECT_R1 ((psa_ecc_family_t) 0x22) + +/** SEC additional random curves over binary fields. + * + * This family comprises the following curve: + * sect163r2. + * It is defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECT_R2 ((psa_ecc_family_t) 0x2b) + +/** Brainpool P random curves. + * + * This family comprises the following curves: + * brainpoolP160r1, brainpoolP192r1, brainpoolP224r1, brainpoolP256r1, + * brainpoolP320r1, brainpoolP384r1, brainpoolP512r1. + * It is defined in RFC 5639. + */ +#define PSA_ECC_FAMILY_BRAINPOOL_P_R1 ((psa_ecc_family_t) 0x30) + +/** Curve25519 and Curve448. + * + * This family comprises the following Montgomery curves: + * - 255-bit: Bernstein et al., + * _Curve25519: new Diffie-Hellman speed records_, LNCS 3958, 2006. + * The algorithm #PSA_ALG_ECDH performs X25519 when used with this curve. + * - 448-bit: Hamburg, + * _Ed448-Goldilocks, a new elliptic curve_, NIST ECC Workshop, 2015. + * The algorithm #PSA_ALG_ECDH performs X448 when used with this curve. + */ +#define PSA_ECC_FAMILY_MONTGOMERY ((psa_ecc_family_t) 0x41) + +/** The twisted Edwards curves Ed25519 and Ed448. + * + * These curves are suitable for EdDSA (#PSA_ALG_PURE_EDDSA for both curves, + * #PSA_ALG_ED25519PH for the 255-bit curve, + * #PSA_ALG_ED448PH for the 448-bit curve). + * + * This family comprises the following twisted Edwards curves: + * - 255-bit: Edwards25519, the twisted Edwards curve birationally equivalent + * to Curve25519. + * Bernstein et al., _Twisted Edwards curves_, Africacrypt 2008. + * - 448-bit: Edwards448, the twisted Edwards curve birationally equivalent + * to Curve448. + * Hamburg, _Ed448-Goldilocks, a new elliptic curve_, NIST ECC Workshop, 2015. + */ +#define PSA_ECC_FAMILY_TWISTED_EDWARDS ((psa_ecc_family_t) 0x42) + +#define PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE ((psa_key_type_t) 0x4200) +#define PSA_KEY_TYPE_DH_KEY_PAIR_BASE ((psa_key_type_t) 0x7200) +#define PSA_KEY_TYPE_DH_GROUP_MASK ((psa_key_type_t) 0x00ff) +/** Diffie-Hellman key pair. + * + * \param group A value of type ::psa_dh_family_t that identifies the + * Diffie-Hellman group to be used. + */ +#define PSA_KEY_TYPE_DH_KEY_PAIR(group) \ + (PSA_KEY_TYPE_DH_KEY_PAIR_BASE | (group)) +/** Diffie-Hellman public key. + * + * \param group A value of type ::psa_dh_family_t that identifies the + * Diffie-Hellman group to be used. + */ +#define PSA_KEY_TYPE_DH_PUBLIC_KEY(group) \ + (PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE | (group)) + +/** Whether a key type is a Diffie-Hellman key (pair or public-only). */ +#define PSA_KEY_TYPE_IS_DH(type) \ + ((PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) & \ + ~PSA_KEY_TYPE_DH_GROUP_MASK) == PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE) +/** Whether a key type is a Diffie-Hellman key pair. */ +#define PSA_KEY_TYPE_IS_DH_KEY_PAIR(type) \ + (((type) & ~PSA_KEY_TYPE_DH_GROUP_MASK) == \ + PSA_KEY_TYPE_DH_KEY_PAIR_BASE) +/** Whether a key type is a Diffie-Hellman public key. */ +#define PSA_KEY_TYPE_IS_DH_PUBLIC_KEY(type) \ + (((type) & ~PSA_KEY_TYPE_DH_GROUP_MASK) == \ + PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE) + +/** Extract the group from a Diffie-Hellman key type. */ +#define PSA_KEY_TYPE_DH_GET_FAMILY(type) \ + ((psa_dh_family_t) (PSA_KEY_TYPE_IS_DH(type) ? \ + ((type) & PSA_KEY_TYPE_DH_GROUP_MASK) : \ + 0)) + +/** Diffie-Hellman groups defined in RFC 7919 Appendix A. + * + * This family includes groups with the following key sizes (in bits): + * 2048, 3072, 4096, 6144, 8192. A given implementation may support + * all of these sizes or only a subset. + */ +#define PSA_DH_FAMILY_RFC7919 ((psa_dh_family_t) 0x03) + +#define PSA_GET_KEY_TYPE_BLOCK_SIZE_EXPONENT(type) \ + (((type) >> 8) & 7) +/** The block size of a block cipher. + * + * \param type A cipher key type (value of type #psa_key_type_t). + * + * \return The block size for a block cipher, or 1 for a stream cipher. + * The return value is undefined if \p type is not a supported + * cipher key type. + * + * \note It is possible to build stream cipher algorithms on top of a block + * cipher, for example CTR mode (#PSA_ALG_CTR). + * This macro only takes the key type into account, so it cannot be + * used to determine the size of the data that #psa_cipher_update() + * might buffer for future processing in general. + * + * \note This macro returns a compile-time constant if its argument is one. + * + * \warning This macro may evaluate its argument multiple times. + */ +#define PSA_BLOCK_CIPHER_BLOCK_LENGTH(type) \ + (((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_SYMMETRIC ? \ + 1u << PSA_GET_KEY_TYPE_BLOCK_SIZE_EXPONENT(type) : \ + 0u) + +/* Note that algorithm values are embedded in the persistent key store, + * as part of key metadata. As a consequence, they must not be changed + * (unless the storage format version changes). + */ + +/** Vendor-defined algorithm flag. + * + * Algorithms defined by this standard will never have the #PSA_ALG_VENDOR_FLAG + * bit set. Vendors who define additional algorithms must use an encoding with + * the #PSA_ALG_VENDOR_FLAG bit set and should respect the bitwise structure + * used by standard encodings whenever practical. + */ +#define PSA_ALG_VENDOR_FLAG ((psa_algorithm_t) 0x80000000) + +#define PSA_ALG_CATEGORY_MASK ((psa_algorithm_t) 0x7f000000) +#define PSA_ALG_CATEGORY_HASH ((psa_algorithm_t) 0x02000000) +#define PSA_ALG_CATEGORY_MAC ((psa_algorithm_t) 0x03000000) +#define PSA_ALG_CATEGORY_CIPHER ((psa_algorithm_t) 0x04000000) +#define PSA_ALG_CATEGORY_AEAD ((psa_algorithm_t) 0x05000000) +#define PSA_ALG_CATEGORY_SIGN ((psa_algorithm_t) 0x06000000) +#define PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION ((psa_algorithm_t) 0x07000000) +#define PSA_ALG_CATEGORY_KEY_DERIVATION ((psa_algorithm_t) 0x08000000) +#define PSA_ALG_CATEGORY_KEY_AGREEMENT ((psa_algorithm_t) 0x09000000) + +/** Whether an algorithm is vendor-defined. + * + * See also #PSA_ALG_VENDOR_FLAG. + */ +#define PSA_ALG_IS_VENDOR_DEFINED(alg) \ + (((alg) & PSA_ALG_VENDOR_FLAG) != 0) + +/** Whether the specified algorithm is a hash algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a hash algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_HASH(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_HASH) + +/** Whether the specified algorithm is a MAC algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a MAC algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_MAC(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_MAC) + +/** Whether the specified algorithm is a symmetric cipher algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a symmetric cipher algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_CIPHER(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_CIPHER) + +/** Whether the specified algorithm is an authenticated encryption + * with associated data (AEAD) algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is an AEAD algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_AEAD(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_AEAD) + +/** Whether the specified algorithm is an asymmetric signature algorithm, + * also known as public-key signature algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is an asymmetric signature algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_SIGN(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_SIGN) + +/** Whether the specified algorithm is an asymmetric encryption algorithm, + * also known as public-key encryption algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is an asymmetric encryption algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_ASYMMETRIC_ENCRYPTION(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_ASYMMETRIC_ENCRYPTION) + +/** Whether the specified algorithm is a key agreement algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a key agreement algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_KEY_AGREEMENT(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_KEY_AGREEMENT) + +/** Whether the specified algorithm is a key derivation algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a key derivation algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_KEY_DERIVATION(alg) \ + (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_KEY_DERIVATION) + +/** Whether the specified algorithm is a key stretching / password hashing + * algorithm. + * + * A key stretching / password hashing algorithm is a key derivation algorithm + * that is suitable for use with a low-entropy secret such as a password. + * Equivalently, it's a key derivation algorithm that uses a + * #PSA_KEY_DERIVATION_INPUT_PASSWORD input step. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a key stretching / password hashing algorithm, 0 + * otherwise. This macro may return either 0 or 1 if \p alg is not a + * supported algorithm identifier. + */ +#define PSA_ALG_IS_KEY_DERIVATION_STRETCHING(alg) \ + (PSA_ALG_IS_KEY_DERIVATION(alg) && \ + (alg) & PSA_ALG_KEY_DERIVATION_STRETCHING_FLAG) + +/** An invalid algorithm identifier value. */ +/* *INDENT-OFF* (https://github.com/ARM-software/psa-arch-tests/issues/337) */ +#define PSA_ALG_NONE ((psa_algorithm_t)0) +/* *INDENT-ON* */ + +#define PSA_ALG_HASH_MASK ((psa_algorithm_t) 0x000000ff) +/** MD5 */ +#define PSA_ALG_MD5 ((psa_algorithm_t) 0x02000003) +/** PSA_ALG_RIPEMD160 */ +#define PSA_ALG_RIPEMD160 ((psa_algorithm_t) 0x02000004) +/** SHA1 */ +#define PSA_ALG_SHA_1 ((psa_algorithm_t) 0x02000005) +/** SHA2-224 */ +#define PSA_ALG_SHA_224 ((psa_algorithm_t) 0x02000008) +/** SHA2-256 */ +#define PSA_ALG_SHA_256 ((psa_algorithm_t) 0x02000009) +/** SHA2-384 */ +#define PSA_ALG_SHA_384 ((psa_algorithm_t) 0x0200000a) +/** SHA2-512 */ +#define PSA_ALG_SHA_512 ((psa_algorithm_t) 0x0200000b) +/** SHA2-512/224 */ +#define PSA_ALG_SHA_512_224 ((psa_algorithm_t) 0x0200000c) +/** SHA2-512/256 */ +#define PSA_ALG_SHA_512_256 ((psa_algorithm_t) 0x0200000d) +/** SHA3-224 */ +#define PSA_ALG_SHA3_224 ((psa_algorithm_t) 0x02000010) +/** SHA3-256 */ +#define PSA_ALG_SHA3_256 ((psa_algorithm_t) 0x02000011) +/** SHA3-384 */ +#define PSA_ALG_SHA3_384 ((psa_algorithm_t) 0x02000012) +/** SHA3-512 */ +#define PSA_ALG_SHA3_512 ((psa_algorithm_t) 0x02000013) +/** The first 512 bits (64 bytes) of the SHAKE256 output. + * + * This is the prehashing for Ed448ph (see #PSA_ALG_ED448PH). For other + * scenarios where a hash function based on SHA3/SHAKE is desired, SHA3-512 + * has the same output size and a (theoretically) higher security strength. + */ +#define PSA_ALG_SHAKE256_512 ((psa_algorithm_t) 0x02000015) + +/** In a hash-and-sign algorithm policy, allow any hash algorithm. + * + * This value may be used to form the algorithm usage field of a policy + * for a signature algorithm that is parametrized by a hash. The key + * may then be used to perform operations using the same signature + * algorithm parametrized with any supported hash. + * + * That is, suppose that `PSA_xxx_SIGNATURE` is one of the following macros: + * - #PSA_ALG_RSA_PKCS1V15_SIGN, #PSA_ALG_RSA_PSS, #PSA_ALG_RSA_PSS_ANY_SALT, + * - #PSA_ALG_ECDSA, #PSA_ALG_DETERMINISTIC_ECDSA. + * Then you may create and use a key as follows: + * - Set the key usage field using #PSA_ALG_ANY_HASH, for example: + * ``` + * psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH); // or VERIFY + * psa_set_key_algorithm(&attributes, PSA_xxx_SIGNATURE(PSA_ALG_ANY_HASH)); + * ``` + * - Import or generate key material. + * - Call psa_sign_hash() or psa_verify_hash(), passing + * an algorithm built from `PSA_xxx_SIGNATURE` and a specific hash. Each + * call to sign or verify a message may use a different hash. + * ``` + * psa_sign_hash(key, PSA_xxx_SIGNATURE(PSA_ALG_SHA_256), ...); + * psa_sign_hash(key, PSA_xxx_SIGNATURE(PSA_ALG_SHA_512), ...); + * psa_sign_hash(key, PSA_xxx_SIGNATURE(PSA_ALG_SHA3_256), ...); + * ``` + * + * This value may not be used to build other algorithms that are + * parametrized over a hash. For any valid use of this macro to build + * an algorithm \c alg, #PSA_ALG_IS_HASH_AND_SIGN(\c alg) is true. + * + * This value may not be used to build an algorithm specification to + * perform an operation. It is only valid to build policies. + */ +#define PSA_ALG_ANY_HASH ((psa_algorithm_t) 0x020000ff) + +#define PSA_ALG_MAC_SUBCATEGORY_MASK ((psa_algorithm_t) 0x00c00000) +#define PSA_ALG_HMAC_BASE ((psa_algorithm_t) 0x03800000) +/** Macro to build an HMAC algorithm. + * + * For example, #PSA_ALG_HMAC(#PSA_ALG_SHA_256) is HMAC-SHA-256. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding HMAC algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_HMAC(hash_alg) \ + (PSA_ALG_HMAC_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) + +#define PSA_ALG_HMAC_GET_HASH(hmac_alg) \ + (PSA_ALG_CATEGORY_HASH | ((hmac_alg) & PSA_ALG_HASH_MASK)) + +/** Whether the specified algorithm is an HMAC algorithm. + * + * HMAC is a family of MAC algorithms that are based on a hash function. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is an HMAC algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_HMAC(alg) \ + (((alg) & (PSA_ALG_CATEGORY_MASK | PSA_ALG_MAC_SUBCATEGORY_MASK)) == \ + PSA_ALG_HMAC_BASE) + +/* In the encoding of a MAC algorithm, the bits corresponding to + * PSA_ALG_MAC_TRUNCATION_MASK encode the length to which the MAC is + * truncated. As an exception, the value 0 means the untruncated algorithm, + * whatever its length is. The length is encoded in 6 bits, so it can + * reach up to 63; the largest MAC is 64 bytes so its trivial truncation + * to full length is correctly encoded as 0 and any non-trivial truncation + * is correctly encoded as a value between 1 and 63. */ +#define PSA_ALG_MAC_TRUNCATION_MASK ((psa_algorithm_t) 0x003f0000) +#define PSA_MAC_TRUNCATION_OFFSET 16 + +/* In the encoding of a MAC algorithm, the bit corresponding to + * #PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG encodes the fact that the algorithm + * is a wildcard algorithm. A key with such wildcard algorithm as permitted + * algorithm policy can be used with any algorithm corresponding to the + * same base class and having a (potentially truncated) MAC length greater or + * equal than the one encoded in #PSA_ALG_MAC_TRUNCATION_MASK. */ +#define PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG ((psa_algorithm_t) 0x00008000) + +/** Macro to build a truncated MAC algorithm. + * + * A truncated MAC algorithm is identical to the corresponding MAC + * algorithm except that the MAC value for the truncated algorithm + * consists of only the first \p mac_length bytes of the MAC value + * for the untruncated algorithm. + * + * \note This macro may allow constructing algorithm identifiers that + * are not valid, either because the specified length is larger + * than the untruncated MAC or because the specified length is + * smaller than permitted by the implementation. + * + * \note It is implementation-defined whether a truncated MAC that + * is truncated to the same length as the MAC of the untruncated + * algorithm is considered identical to the untruncated algorithm + * for policy comparison purposes. + * + * \param mac_alg A MAC algorithm identifier (value of type + * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg) + * is true). This may be a truncated or untruncated + * MAC algorithm. + * \param mac_length Desired length of the truncated MAC in bytes. + * This must be at most the full length of the MAC + * and must be at least an implementation-specified + * minimum. The implementation-specified minimum + * shall not be zero. + * + * \return The corresponding MAC algorithm with the specified + * length. + * \return Unspecified if \p mac_alg is not a supported + * MAC algorithm or if \p mac_length is too small or + * too large for the specified MAC algorithm. + */ +#define PSA_ALG_TRUNCATED_MAC(mac_alg, mac_length) \ + (((mac_alg) & ~(PSA_ALG_MAC_TRUNCATION_MASK | \ + PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG)) | \ + ((mac_length) << PSA_MAC_TRUNCATION_OFFSET & PSA_ALG_MAC_TRUNCATION_MASK)) + +/** Macro to build the base MAC algorithm corresponding to a truncated + * MAC algorithm. + * + * \param mac_alg A MAC algorithm identifier (value of type + * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg) + * is true). This may be a truncated or untruncated + * MAC algorithm. + * + * \return The corresponding base MAC algorithm. + * \return Unspecified if \p mac_alg is not a supported + * MAC algorithm. + */ +#define PSA_ALG_FULL_LENGTH_MAC(mac_alg) \ + ((mac_alg) & ~(PSA_ALG_MAC_TRUNCATION_MASK | \ + PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG)) + +/** Length to which a MAC algorithm is truncated. + * + * \param mac_alg A MAC algorithm identifier (value of type + * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg) + * is true). + * + * \return Length of the truncated MAC in bytes. + * \return 0 if \p mac_alg is a non-truncated MAC algorithm. + * \return Unspecified if \p mac_alg is not a supported + * MAC algorithm. + */ +#define PSA_MAC_TRUNCATED_LENGTH(mac_alg) \ + (((mac_alg) & PSA_ALG_MAC_TRUNCATION_MASK) >> PSA_MAC_TRUNCATION_OFFSET) + +/** Macro to build a MAC minimum-MAC-length wildcard algorithm. + * + * A minimum-MAC-length MAC wildcard algorithm permits all MAC algorithms + * sharing the same base algorithm, and where the (potentially truncated) MAC + * length of the specific algorithm is equal to or larger then the wildcard + * algorithm's minimum MAC length. + * + * \note When setting the minimum required MAC length to less than the + * smallest MAC length allowed by the base algorithm, this effectively + * becomes an 'any-MAC-length-allowed' policy for that base algorithm. + * + * \param mac_alg A MAC algorithm identifier (value of type + * #psa_algorithm_t such that #PSA_ALG_IS_MAC(\p mac_alg) + * is true). + * \param min_mac_length Desired minimum length of the message authentication + * code in bytes. This must be at most the untruncated + * length of the MAC and must be at least 1. + * + * \return The corresponding MAC wildcard algorithm with the + * specified minimum length. + * \return Unspecified if \p mac_alg is not a supported MAC + * algorithm or if \p min_mac_length is less than 1 or + * too large for the specified MAC algorithm. + */ +#define PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(mac_alg, min_mac_length) \ + (PSA_ALG_TRUNCATED_MAC(mac_alg, min_mac_length) | \ + PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) + +#define PSA_ALG_CIPHER_MAC_BASE ((psa_algorithm_t) 0x03c00000) +/** The CBC-MAC construction over a block cipher + * + * \warning CBC-MAC is insecure in many cases. + * A more secure mode, such as #PSA_ALG_CMAC, is recommended. + */ +#define PSA_ALG_CBC_MAC ((psa_algorithm_t) 0x03c00100) +/** The CMAC construction over a block cipher */ +#define PSA_ALG_CMAC ((psa_algorithm_t) 0x03c00200) + +/** Whether the specified algorithm is a MAC algorithm based on a block cipher. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a MAC algorithm based on a block cipher, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_BLOCK_CIPHER_MAC(alg) \ + (((alg) & (PSA_ALG_CATEGORY_MASK | PSA_ALG_MAC_SUBCATEGORY_MASK)) == \ + PSA_ALG_CIPHER_MAC_BASE) + +#define PSA_ALG_CIPHER_STREAM_FLAG ((psa_algorithm_t) 0x00800000) +#define PSA_ALG_CIPHER_FROM_BLOCK_FLAG ((psa_algorithm_t) 0x00400000) + +/** Whether the specified algorithm is a stream cipher. + * + * A stream cipher is a symmetric cipher that encrypts or decrypts messages + * by applying a bitwise-xor with a stream of bytes that is generated + * from a key. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a stream cipher algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier or if it is not a symmetric cipher algorithm. + */ +#define PSA_ALG_IS_STREAM_CIPHER(alg) \ + (((alg) & (PSA_ALG_CATEGORY_MASK | PSA_ALG_CIPHER_STREAM_FLAG)) == \ + (PSA_ALG_CATEGORY_CIPHER | PSA_ALG_CIPHER_STREAM_FLAG)) + +/** The stream cipher mode of a stream cipher algorithm. + * + * The underlying stream cipher is determined by the key type. + * - To use ChaCha20, use a key type of #PSA_KEY_TYPE_CHACHA20. + */ +#define PSA_ALG_STREAM_CIPHER ((psa_algorithm_t) 0x04800100) + +/** The CTR stream cipher mode. + * + * CTR is a stream cipher which is built from a block cipher. + * The underlying block cipher is determined by the key type. + * For example, to use AES-128-CTR, use this algorithm with + * a key of type #PSA_KEY_TYPE_AES and a length of 128 bits (16 bytes). + */ +#define PSA_ALG_CTR ((psa_algorithm_t) 0x04c01000) + +/** The CFB stream cipher mode. + * + * The underlying block cipher is determined by the key type. + */ +#define PSA_ALG_CFB ((psa_algorithm_t) 0x04c01100) + +/** The OFB stream cipher mode. + * + * The underlying block cipher is determined by the key type. + */ +#define PSA_ALG_OFB ((psa_algorithm_t) 0x04c01200) + +/** The XTS cipher mode. + * + * XTS is a cipher mode which is built from a block cipher. It requires at + * least one full block of input, but beyond this minimum the input + * does not need to be a whole number of blocks. + */ +#define PSA_ALG_XTS ((psa_algorithm_t) 0x0440ff00) + +/** The Electronic Code Book (ECB) mode of a block cipher, with no padding. + * + * \warning ECB mode does not protect the confidentiality of the encrypted data + * except in extremely narrow circumstances. It is recommended that applications + * only use ECB if they need to construct an operating mode that the + * implementation does not provide. Implementations are encouraged to provide + * the modes that applications need in preference to supporting direct access + * to ECB. + * + * The underlying block cipher is determined by the key type. + * + * This symmetric cipher mode can only be used with messages whose lengths are a + * multiple of the block size of the chosen block cipher. + * + * ECB mode does not accept an initialization vector (IV). When using a + * multi-part cipher operation with this algorithm, psa_cipher_generate_iv() + * and psa_cipher_set_iv() must not be called. + */ +#define PSA_ALG_ECB_NO_PADDING ((psa_algorithm_t) 0x04404400) + +/** The CBC block cipher chaining mode, with no padding. + * + * The underlying block cipher is determined by the key type. + * + * This symmetric cipher mode can only be used with messages whose lengths + * are whole number of blocks for the chosen block cipher. + */ +#define PSA_ALG_CBC_NO_PADDING ((psa_algorithm_t) 0x04404000) + +/** The CBC block cipher chaining mode with PKCS#7 padding. + * + * The underlying block cipher is determined by the key type. + * + * This is the padding method defined by PKCS#7 (RFC 2315) §10.3. + */ +#define PSA_ALG_CBC_PKCS7 ((psa_algorithm_t) 0x04404100) + +#define PSA_ALG_AEAD_FROM_BLOCK_FLAG ((psa_algorithm_t) 0x00400000) + +/** Whether the specified algorithm is an AEAD mode on a block cipher. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is an AEAD algorithm which is an AEAD mode based on + * a block cipher, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_AEAD_ON_BLOCK_CIPHER(alg) \ + (((alg) & (PSA_ALG_CATEGORY_MASK | PSA_ALG_AEAD_FROM_BLOCK_FLAG)) == \ + (PSA_ALG_CATEGORY_AEAD | PSA_ALG_AEAD_FROM_BLOCK_FLAG)) + +/** The CCM authenticated encryption algorithm. + * + * The underlying block cipher is determined by the key type. + */ +#define PSA_ALG_CCM ((psa_algorithm_t) 0x05500100) + +/** The CCM* cipher mode without authentication. + * + * This is CCM* as specified in IEEE 802.15.4 §7, with a tag length of 0. + * For CCM* with a nonzero tag length, use the AEAD algorithm #PSA_ALG_CCM. + * + * The underlying block cipher is determined by the key type. + * + * Currently only 13-byte long IV's are supported. + */ +#define PSA_ALG_CCM_STAR_NO_TAG ((psa_algorithm_t) 0x04c01300) + +/** The GCM authenticated encryption algorithm. + * + * The underlying block cipher is determined by the key type. + */ +#define PSA_ALG_GCM ((psa_algorithm_t) 0x05500200) + +/** The Chacha20-Poly1305 AEAD algorithm. + * + * The ChaCha20_Poly1305 construction is defined in RFC 7539. + * + * Implementations must support 12-byte nonces, may support 8-byte nonces, + * and should reject other sizes. + * + * Implementations must support 16-byte tags and should reject other sizes. + */ +#define PSA_ALG_CHACHA20_POLY1305 ((psa_algorithm_t) 0x05100500) + +/* In the encoding of an AEAD algorithm, the bits corresponding to + * PSA_ALG_AEAD_TAG_LENGTH_MASK encode the length of the AEAD tag. + * The constants for default lengths follow this encoding. + */ +#define PSA_ALG_AEAD_TAG_LENGTH_MASK ((psa_algorithm_t) 0x003f0000) +#define PSA_AEAD_TAG_LENGTH_OFFSET 16 + +/* In the encoding of an AEAD algorithm, the bit corresponding to + * #PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG encodes the fact that the algorithm + * is a wildcard algorithm. A key with such wildcard algorithm as permitted + * algorithm policy can be used with any algorithm corresponding to the + * same base class and having a tag length greater than or equal to the one + * encoded in #PSA_ALG_AEAD_TAG_LENGTH_MASK. */ +#define PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG ((psa_algorithm_t) 0x00008000) + +/** Macro to build a shortened AEAD algorithm. + * + * A shortened AEAD algorithm is similar to the corresponding AEAD + * algorithm, but has an authentication tag that consists of fewer bytes. + * Depending on the algorithm, the tag length may affect the calculation + * of the ciphertext. + * + * \param aead_alg An AEAD algorithm identifier (value of type + * #psa_algorithm_t such that #PSA_ALG_IS_AEAD(\p aead_alg) + * is true). + * \param tag_length Desired length of the authentication tag in bytes. + * + * \return The corresponding AEAD algorithm with the specified + * length. + * \return Unspecified if \p aead_alg is not a supported + * AEAD algorithm or if \p tag_length is not valid + * for the specified AEAD algorithm. + */ +#define PSA_ALG_AEAD_WITH_SHORTENED_TAG(aead_alg, tag_length) \ + (((aead_alg) & ~(PSA_ALG_AEAD_TAG_LENGTH_MASK | \ + PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG)) | \ + ((tag_length) << PSA_AEAD_TAG_LENGTH_OFFSET & \ + PSA_ALG_AEAD_TAG_LENGTH_MASK)) + +/** Retrieve the tag length of a specified AEAD algorithm + * + * \param aead_alg An AEAD algorithm identifier (value of type + * #psa_algorithm_t such that #PSA_ALG_IS_AEAD(\p aead_alg) + * is true). + * + * \return The tag length specified by the input algorithm. + * \return Unspecified if \p aead_alg is not a supported + * AEAD algorithm. + */ +#define PSA_ALG_AEAD_GET_TAG_LENGTH(aead_alg) \ + (((aead_alg) & PSA_ALG_AEAD_TAG_LENGTH_MASK) >> \ + PSA_AEAD_TAG_LENGTH_OFFSET) + +/** Calculate the corresponding AEAD algorithm with the default tag length. + * + * \param aead_alg An AEAD algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p aead_alg) is true). + * + * \return The corresponding AEAD algorithm with the default + * tag length for that algorithm. + */ +#define PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(aead_alg) \ + ( \ + PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, PSA_ALG_CCM) \ + PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, PSA_ALG_GCM) \ + PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, PSA_ALG_CHACHA20_POLY1305) \ + 0) +#define PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG_CASE(aead_alg, ref) \ + PSA_ALG_AEAD_WITH_SHORTENED_TAG(aead_alg, 0) == \ + PSA_ALG_AEAD_WITH_SHORTENED_TAG(ref, 0) ? \ + ref : + +/** Macro to build an AEAD minimum-tag-length wildcard algorithm. + * + * A minimum-tag-length AEAD wildcard algorithm permits all AEAD algorithms + * sharing the same base algorithm, and where the tag length of the specific + * algorithm is equal to or larger then the minimum tag length specified by the + * wildcard algorithm. + * + * \note When setting the minimum required tag length to less than the + * smallest tag length allowed by the base algorithm, this effectively + * becomes an 'any-tag-length-allowed' policy for that base algorithm. + * + * \param aead_alg An AEAD algorithm identifier (value of type + * #psa_algorithm_t such that + * #PSA_ALG_IS_AEAD(\p aead_alg) is true). + * \param min_tag_length Desired minimum length of the authentication tag in + * bytes. This must be at least 1 and at most the largest + * allowed tag length of the algorithm. + * + * \return The corresponding AEAD wildcard algorithm with the + * specified minimum length. + * \return Unspecified if \p aead_alg is not a supported + * AEAD algorithm or if \p min_tag_length is less than 1 + * or too large for the specified AEAD algorithm. + */ +#define PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(aead_alg, min_tag_length) \ + (PSA_ALG_AEAD_WITH_SHORTENED_TAG(aead_alg, min_tag_length) | \ + PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) + +#define PSA_ALG_RSA_PKCS1V15_SIGN_BASE ((psa_algorithm_t) 0x06000200) +/** RSA PKCS#1 v1.5 signature with hashing. + * + * This is the signature scheme defined by RFC 8017 + * (PKCS#1: RSA Cryptography Specifications) under the name + * RSASSA-PKCS1-v1_5. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * This includes #PSA_ALG_ANY_HASH + * when specifying the algorithm in a usage policy. + * + * \return The corresponding RSA PKCS#1 v1.5 signature algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_RSA_PKCS1V15_SIGN(hash_alg) \ + (PSA_ALG_RSA_PKCS1V15_SIGN_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +/** Raw PKCS#1 v1.5 signature. + * + * The input to this algorithm is the DigestInfo structure used by + * RFC 8017 (PKCS#1: RSA Cryptography Specifications), §9.2 + * steps 3–6. + */ +#define PSA_ALG_RSA_PKCS1V15_SIGN_RAW PSA_ALG_RSA_PKCS1V15_SIGN_BASE +#define PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_PKCS1V15_SIGN_BASE) + +#define PSA_ALG_RSA_PSS_BASE ((psa_algorithm_t) 0x06000300) +#define PSA_ALG_RSA_PSS_ANY_SALT_BASE ((psa_algorithm_t) 0x06001300) +/** RSA PSS signature with hashing. + * + * This is the signature scheme defined by RFC 8017 + * (PKCS#1: RSA Cryptography Specifications) under the name + * RSASSA-PSS, with the message generation function MGF1, and with + * a salt length equal to the length of the hash, or the largest + * possible salt length for the algorithm and key size if that is + * smaller than the hash length. The specified hash algorithm is + * used to hash the input message, to create the salted hash, and + * for the mask generation. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * This includes #PSA_ALG_ANY_HASH + * when specifying the algorithm in a usage policy. + * + * \return The corresponding RSA PSS signature algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_RSA_PSS(hash_alg) \ + (PSA_ALG_RSA_PSS_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) + +/** RSA PSS signature with hashing with relaxed verification. + * + * This algorithm has the same behavior as #PSA_ALG_RSA_PSS when signing, + * but allows an arbitrary salt length (including \c 0) when verifying a + * signature. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * This includes #PSA_ALG_ANY_HASH + * when specifying the algorithm in a usage policy. + * + * \return The corresponding RSA PSS signature algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_RSA_PSS_ANY_SALT(hash_alg) \ + (PSA_ALG_RSA_PSS_ANY_SALT_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) + +/** Whether the specified algorithm is RSA PSS with standard salt. + * + * \param alg An algorithm value or an algorithm policy wildcard. + * + * \return 1 if \p alg is of the form + * #PSA_ALG_RSA_PSS(\c hash_alg), + * where \c hash_alg is a hash algorithm or + * #PSA_ALG_ANY_HASH. 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not + * a supported algorithm identifier or policy. + */ +#define PSA_ALG_IS_RSA_PSS_STANDARD_SALT(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_PSS_BASE) + +/** Whether the specified algorithm is RSA PSS with any salt. + * + * \param alg An algorithm value or an algorithm policy wildcard. + * + * \return 1 if \p alg is of the form + * #PSA_ALG_RSA_PSS_ANY_SALT_BASE(\c hash_alg), + * where \c hash_alg is a hash algorithm or + * #PSA_ALG_ANY_HASH. 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not + * a supported algorithm identifier or policy. + */ +#define PSA_ALG_IS_RSA_PSS_ANY_SALT(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_PSS_ANY_SALT_BASE) + +/** Whether the specified algorithm is RSA PSS. + * + * This includes any of the RSA PSS algorithm variants, regardless of the + * constraints on salt length. + * + * \param alg An algorithm value or an algorithm policy wildcard. + * + * \return 1 if \p alg is of the form + * #PSA_ALG_RSA_PSS(\c hash_alg) or + * #PSA_ALG_RSA_PSS_ANY_SALT_BASE(\c hash_alg), + * where \c hash_alg is a hash algorithm or + * #PSA_ALG_ANY_HASH. 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not + * a supported algorithm identifier or policy. + */ +#define PSA_ALG_IS_RSA_PSS(alg) \ + (PSA_ALG_IS_RSA_PSS_STANDARD_SALT(alg) || \ + PSA_ALG_IS_RSA_PSS_ANY_SALT(alg)) + +#define PSA_ALG_ECDSA_BASE ((psa_algorithm_t) 0x06000600) +/** ECDSA signature with hashing. + * + * This is the ECDSA signature scheme defined by ANSI X9.62, + * with a random per-message secret number (*k*). + * + * The representation of the signature as a byte string consists of + * the concatenation of the signature values *r* and *s*. Each of + * *r* and *s* is encoded as an *N*-octet string, where *N* is the length + * of the base point of the curve in octets. Each value is represented + * in big-endian order (most significant octet first). + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * This includes #PSA_ALG_ANY_HASH + * when specifying the algorithm in a usage policy. + * + * \return The corresponding ECDSA signature algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_ECDSA(hash_alg) \ + (PSA_ALG_ECDSA_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +/** ECDSA signature without hashing. + * + * This is the same signature scheme as #PSA_ALG_ECDSA(), but + * without specifying a hash algorithm. This algorithm may only be + * used to sign or verify a sequence of bytes that should be an + * already-calculated hash. Note that the input is padded with + * zeros on the left or truncated on the left as required to fit + * the curve size. + */ +#define PSA_ALG_ECDSA_ANY PSA_ALG_ECDSA_BASE +#define PSA_ALG_DETERMINISTIC_ECDSA_BASE ((psa_algorithm_t) 0x06000700) +/** Deterministic ECDSA signature with hashing. + * + * This is the deterministic ECDSA signature scheme defined by RFC 6979. + * + * The representation of a signature is the same as with #PSA_ALG_ECDSA(). + * + * Note that when this algorithm is used for verification, signatures + * made with randomized ECDSA (#PSA_ALG_ECDSA(\p hash_alg)) with the + * same private key are accepted. In other words, + * #PSA_ALG_DETERMINISTIC_ECDSA(\p hash_alg) differs from + * #PSA_ALG_ECDSA(\p hash_alg) only for signature, not for verification. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * This includes #PSA_ALG_ANY_HASH + * when specifying the algorithm in a usage policy. + * + * \return The corresponding deterministic ECDSA signature + * algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_DETERMINISTIC_ECDSA(hash_alg) \ + (PSA_ALG_DETERMINISTIC_ECDSA_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +#define PSA_ALG_ECDSA_DETERMINISTIC_FLAG ((psa_algorithm_t) 0x00000100) +#define PSA_ALG_IS_ECDSA(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK & ~PSA_ALG_ECDSA_DETERMINISTIC_FLAG) == \ + PSA_ALG_ECDSA_BASE) +#define PSA_ALG_ECDSA_IS_DETERMINISTIC(alg) \ + (((alg) & PSA_ALG_ECDSA_DETERMINISTIC_FLAG) != 0) +#define PSA_ALG_IS_DETERMINISTIC_ECDSA(alg) \ + (PSA_ALG_IS_ECDSA(alg) && PSA_ALG_ECDSA_IS_DETERMINISTIC(alg)) +#define PSA_ALG_IS_RANDOMIZED_ECDSA(alg) \ + (PSA_ALG_IS_ECDSA(alg) && !PSA_ALG_ECDSA_IS_DETERMINISTIC(alg)) + +/** Edwards-curve digital signature algorithm without prehashing (PureEdDSA), + * using standard parameters. + * + * Contexts are not supported in the current version of this specification + * because there is no suitable signature interface that can take the + * context as a parameter. A future version of this specification may add + * suitable functions and extend this algorithm to support contexts. + * + * PureEdDSA requires an elliptic curve key on a twisted Edwards curve. + * In this specification, the following curves are supported: + * - #PSA_ECC_FAMILY_TWISTED_EDWARDS, 255-bit: Ed25519 as specified + * in RFC 8032. + * The curve is Edwards25519. + * The hash function used internally is SHA-512. + * - #PSA_ECC_FAMILY_TWISTED_EDWARDS, 448-bit: Ed448 as specified + * in RFC 8032. + * The curve is Edwards448. + * The hash function used internally is the first 114 bytes of the + * SHAKE256 output. + * + * This algorithm can be used with psa_sign_message() and + * psa_verify_message(). Since there is no prehashing, it cannot be used + * with psa_sign_hash() or psa_verify_hash(). + * + * The signature format is the concatenation of R and S as defined by + * RFC 8032 §5.1.6 and §5.2.6 (a 64-byte string for Ed25519, a 114-byte + * string for Ed448). + */ +#define PSA_ALG_PURE_EDDSA ((psa_algorithm_t) 0x06000800) + +#define PSA_ALG_HASH_EDDSA_BASE ((psa_algorithm_t) 0x06000900) +#define PSA_ALG_IS_HASH_EDDSA(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HASH_EDDSA_BASE) + +/** Edwards-curve digital signature algorithm with prehashing (HashEdDSA), + * using SHA-512 and the Edwards25519 curve. + * + * See #PSA_ALG_PURE_EDDSA regarding context support and the signature format. + * + * This algorithm is Ed25519 as specified in RFC 8032. + * The curve is Edwards25519. + * The prehash is SHA-512. + * The hash function used internally is SHA-512. + * + * This is a hash-and-sign algorithm: to calculate a signature, + * you can either: + * - call psa_sign_message() on the message; + * - or calculate the SHA-512 hash of the message + * with psa_hash_compute() + * or with a multi-part hash operation started with psa_hash_setup(), + * using the hash algorithm #PSA_ALG_SHA_512, + * then sign the calculated hash with psa_sign_hash(). + * Verifying a signature is similar, using psa_verify_message() or + * psa_verify_hash() instead of the signature function. + */ +#define PSA_ALG_ED25519PH \ + (PSA_ALG_HASH_EDDSA_BASE | (PSA_ALG_SHA_512 & PSA_ALG_HASH_MASK)) + +/** Edwards-curve digital signature algorithm with prehashing (HashEdDSA), + * using SHAKE256 and the Edwards448 curve. + * + * See #PSA_ALG_PURE_EDDSA regarding context support and the signature format. + * + * This algorithm is Ed448 as specified in RFC 8032. + * The curve is Edwards448. + * The prehash is the first 64 bytes of the SHAKE256 output. + * The hash function used internally is the first 114 bytes of the + * SHAKE256 output. + * + * This is a hash-and-sign algorithm: to calculate a signature, + * you can either: + * - call psa_sign_message() on the message; + * - or calculate the first 64 bytes of the SHAKE256 output of the message + * with psa_hash_compute() + * or with a multi-part hash operation started with psa_hash_setup(), + * using the hash algorithm #PSA_ALG_SHAKE256_512, + * then sign the calculated hash with psa_sign_hash(). + * Verifying a signature is similar, using psa_verify_message() or + * psa_verify_hash() instead of the signature function. + */ +#define PSA_ALG_ED448PH \ + (PSA_ALG_HASH_EDDSA_BASE | (PSA_ALG_SHAKE256_512 & PSA_ALG_HASH_MASK)) + +/* Default definition, to be overridden if the library is extended with + * more hash-and-sign algorithms that we want to keep out of this header + * file. */ +#define PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg) 0 + +/** Whether the specified algorithm is a signature algorithm that can be used + * with psa_sign_hash() and psa_verify_hash(). + * + * This encompasses all strict hash-and-sign algorithms categorized by + * PSA_ALG_IS_HASH_AND_SIGN(), as well as algorithms that follow the + * paradigm more loosely: + * - #PSA_ALG_RSA_PKCS1V15_SIGN_RAW (expects its input to be an encoded hash) + * - #PSA_ALG_ECDSA_ANY (doesn't specify what kind of hash the input is) + * + * \param alg An algorithm identifier (value of type psa_algorithm_t). + * + * \return 1 if alg is a signature algorithm that can be used to sign a + * hash. 0 if alg is a signature algorithm that can only be used + * to sign a message. 0 if alg is not a signature algorithm. + * This macro can return either 0 or 1 if alg is not a + * supported algorithm identifier. + */ +#define PSA_ALG_IS_SIGN_HASH(alg) \ + (PSA_ALG_IS_RSA_PSS(alg) || PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) || \ + PSA_ALG_IS_ECDSA(alg) || PSA_ALG_IS_HASH_EDDSA(alg) || \ + PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg)) + +/** Whether the specified algorithm is a signature algorithm that can be used + * with psa_sign_message() and psa_verify_message(). + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if alg is a signature algorithm that can be used to sign a + * message. 0 if \p alg is a signature algorithm that can only be used + * to sign an already-calculated hash. 0 if \p alg is not a signature + * algorithm. This macro can return either 0 or 1 if \p alg is not a + * supported algorithm identifier. + */ +#define PSA_ALG_IS_SIGN_MESSAGE(alg) \ + (PSA_ALG_IS_SIGN_HASH(alg) || (alg) == PSA_ALG_PURE_EDDSA) + +/** Whether the specified algorithm is a hash-and-sign algorithm. + * + * Hash-and-sign algorithms are asymmetric (public-key) signature algorithms + * structured in two parts: first the calculation of a hash in a way that + * does not depend on the key, then the calculation of a signature from the + * hash value and the key. Hash-and-sign algorithms encode the hash + * used for the hashing step, and you can call #PSA_ALG_SIGN_GET_HASH + * to extract this algorithm. + * + * Thus, for a hash-and-sign algorithm, + * `psa_sign_message(key, alg, input, ...)` is equivalent to + * ``` + * psa_hash_compute(PSA_ALG_SIGN_GET_HASH(alg), input, ..., hash, ...); + * psa_sign_hash(key, alg, hash, ..., signature, ...); + * ``` + * Most usefully, separating the hash from the signature allows the hash + * to be calculated in multiple steps with psa_hash_setup(), psa_hash_update() + * and psa_hash_finish(). Likewise psa_verify_message() is equivalent to + * calculating the hash and then calling psa_verify_hash(). + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a hash-and-sign algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_HASH_AND_SIGN(alg) \ + (PSA_ALG_IS_SIGN_HASH(alg) && \ + ((alg) & PSA_ALG_HASH_MASK) != 0) + +/** Get the hash used by a hash-and-sign signature algorithm. + * + * A hash-and-sign algorithm is a signature algorithm which is + * composed of two phases: first a hashing phase which does not use + * the key and produces a hash of the input message, then a signing + * phase which only uses the hash and the key and not the message + * itself. + * + * \param alg A signature algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_SIGN(\p alg) is true). + * + * \return The underlying hash algorithm if \p alg is a hash-and-sign + * algorithm. + * \return 0 if \p alg is a signature algorithm that does not + * follow the hash-and-sign structure. + * \return Unspecified if \p alg is not a signature algorithm or + * if it is not supported by the implementation. + */ +#define PSA_ALG_SIGN_GET_HASH(alg) \ + (PSA_ALG_IS_HASH_AND_SIGN(alg) ? \ + ((alg) & PSA_ALG_HASH_MASK) | PSA_ALG_CATEGORY_HASH : \ + 0) + +/** RSA PKCS#1 v1.5 encryption. + */ +#define PSA_ALG_RSA_PKCS1V15_CRYPT ((psa_algorithm_t) 0x07000200) + +#define PSA_ALG_RSA_OAEP_BASE ((psa_algorithm_t) 0x07000300) +/** RSA OAEP encryption. + * + * This is the encryption scheme defined by RFC 8017 + * (PKCS#1: RSA Cryptography Specifications) under the name + * RSAES-OAEP, with the message generation function MGF1. + * + * \param hash_alg The hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true) to use + * for MGF1. + * + * \return The corresponding RSA OAEP encryption algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_RSA_OAEP(hash_alg) \ + (PSA_ALG_RSA_OAEP_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +#define PSA_ALG_IS_RSA_OAEP(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_OAEP_BASE) +#define PSA_ALG_RSA_OAEP_GET_HASH(alg) \ + (PSA_ALG_IS_RSA_OAEP(alg) ? \ + ((alg) & PSA_ALG_HASH_MASK) | PSA_ALG_CATEGORY_HASH : \ + 0) + +#define PSA_ALG_HKDF_BASE ((psa_algorithm_t) 0x08000100) +/** Macro to build an HKDF algorithm. + * + * For example, `PSA_ALG_HKDF(PSA_ALG_SHA_256)` is HKDF using HMAC-SHA-256. + * + * This key derivation algorithm uses the following inputs: + * - #PSA_KEY_DERIVATION_INPUT_SALT is the salt used in the "extract" step. + * It is optional; if omitted, the derivation uses an empty salt. + * - #PSA_KEY_DERIVATION_INPUT_SECRET is the secret key used in the "extract" step. + * - #PSA_KEY_DERIVATION_INPUT_INFO is the info string used in the "expand" step. + * You must pass #PSA_KEY_DERIVATION_INPUT_SALT before #PSA_KEY_DERIVATION_INPUT_SECRET. + * You may pass #PSA_KEY_DERIVATION_INPUT_INFO at any time after steup and before + * starting to generate output. + * + * \warning HKDF processes the salt as follows: first hash it with hash_alg + * if the salt is longer than the block size of the hash algorithm; then + * pad with null bytes up to the block size. As a result, it is possible + * for distinct salt inputs to result in the same outputs. To ensure + * unique outputs, it is recommended to use a fixed length for salt values. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding HKDF algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_HKDF(hash_alg) \ + (PSA_ALG_HKDF_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +/** Whether the specified algorithm is an HKDF algorithm. + * + * HKDF is a family of key derivation algorithms that are based on a hash + * function and the HMAC construction. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is an HKDF algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_HKDF(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HKDF_BASE) +#define PSA_ALG_HKDF_GET_HASH(hkdf_alg) \ + (PSA_ALG_CATEGORY_HASH | ((hkdf_alg) & PSA_ALG_HASH_MASK)) + +#define PSA_ALG_HKDF_EXTRACT_BASE ((psa_algorithm_t) 0x08000400) +/** Macro to build an HKDF-Extract algorithm. + * + * For example, `PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256)` is + * HKDF-Extract using HMAC-SHA-256. + * + * This key derivation algorithm uses the following inputs: + * - PSA_KEY_DERIVATION_INPUT_SALT is the salt. + * - PSA_KEY_DERIVATION_INPUT_SECRET is the input keying material used in the + * "extract" step. + * The inputs are mandatory and must be passed in the order above. + * Each input may only be passed once. + * + * \warning HKDF-Extract is not meant to be used on its own. PSA_ALG_HKDF + * should be used instead if possible. PSA_ALG_HKDF_EXTRACT is provided + * as a separate algorithm for the sake of protocols that use it as a + * building block. It may also be a slight performance optimization + * in applications that use HKDF with the same salt and key but many + * different info strings. + * + * \warning HKDF processes the salt as follows: first hash it with hash_alg + * if the salt is longer than the block size of the hash algorithm; then + * pad with null bytes up to the block size. As a result, it is possible + * for distinct salt inputs to result in the same outputs. To ensure + * unique outputs, it is recommended to use a fixed length for salt values. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding HKDF-Extract algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_HKDF_EXTRACT(hash_alg) \ + (PSA_ALG_HKDF_EXTRACT_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +/** Whether the specified algorithm is an HKDF-Extract algorithm. + * + * HKDF-Extract is a family of key derivation algorithms that are based + * on a hash function and the HMAC construction. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is an HKDF-Extract algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_HKDF_EXTRACT(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HKDF_EXTRACT_BASE) + +#define PSA_ALG_HKDF_EXPAND_BASE ((psa_algorithm_t) 0x08000500) +/** Macro to build an HKDF-Expand algorithm. + * + * For example, `PSA_ALG_HKDF_EXPAND(PSA_ALG_SHA_256)` is + * HKDF-Expand using HMAC-SHA-256. + * + * This key derivation algorithm uses the following inputs: + * - PSA_KEY_DERIVATION_INPUT_SECRET is the pseudorandom key (PRK). + * - PSA_KEY_DERIVATION_INPUT_INFO is the info string. + * + * The inputs are mandatory and must be passed in the order above. + * Each input may only be passed once. + * + * \warning HKDF-Expand is not meant to be used on its own. `PSA_ALG_HKDF` + * should be used instead if possible. `PSA_ALG_HKDF_EXPAND` is provided as + * a separate algorithm for the sake of protocols that use it as a building + * block. It may also be a slight performance optimization in applications + * that use HKDF with the same salt and key but many different info strings. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding HKDF-Expand algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_HKDF_EXPAND(hash_alg) \ + (PSA_ALG_HKDF_EXPAND_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +/** Whether the specified algorithm is an HKDF-Expand algorithm. + * + * HKDF-Expand is a family of key derivation algorithms that are based + * on a hash function and the HMAC construction. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is an HKDF-Expand algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_HKDF_EXPAND(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HKDF_EXPAND_BASE) + +/** Whether the specified algorithm is an HKDF or HKDF-Extract or + * HKDF-Expand algorithm. + * + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is any HKDF type algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_ANY_HKDF(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HKDF_BASE || \ + ((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HKDF_EXTRACT_BASE || \ + ((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HKDF_EXPAND_BASE) + +#define PSA_ALG_TLS12_PRF_BASE ((psa_algorithm_t) 0x08000200) +/** Macro to build a TLS-1.2 PRF algorithm. + * + * TLS 1.2 uses a custom pseudorandom function (PRF) for key schedule, + * specified in Section 5 of RFC 5246. It is based on HMAC and can be + * used with either SHA-256 or SHA-384. + * + * This key derivation algorithm uses the following inputs, which must be + * passed in the order given here: + * - #PSA_KEY_DERIVATION_INPUT_SEED is the seed. + * - #PSA_KEY_DERIVATION_INPUT_SECRET is the secret key. + * - #PSA_KEY_DERIVATION_INPUT_LABEL is the label. + * + * For the application to TLS-1.2 key expansion, the seed is the + * concatenation of ServerHello.Random + ClientHello.Random, + * and the label is "key expansion". + * + * For example, `PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256)` represents the + * TLS 1.2 PRF using HMAC-SHA-256. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding TLS-1.2 PRF algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_TLS12_PRF(hash_alg) \ + (PSA_ALG_TLS12_PRF_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) + +/** Whether the specified algorithm is a TLS-1.2 PRF algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is a TLS-1.2 PRF algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_TLS12_PRF(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_TLS12_PRF_BASE) +#define PSA_ALG_TLS12_PRF_GET_HASH(hkdf_alg) \ + (PSA_ALG_CATEGORY_HASH | ((hkdf_alg) & PSA_ALG_HASH_MASK)) + +#define PSA_ALG_TLS12_PSK_TO_MS_BASE ((psa_algorithm_t) 0x08000300) +/** Macro to build a TLS-1.2 PSK-to-MasterSecret algorithm. + * + * In a pure-PSK handshake in TLS 1.2, the master secret is derived + * from the PreSharedKey (PSK) through the application of padding + * (RFC 4279, Section 2) and the TLS-1.2 PRF (RFC 5246, Section 5). + * The latter is based on HMAC and can be used with either SHA-256 + * or SHA-384. + * + * This key derivation algorithm uses the following inputs, which must be + * passed in the order given here: + * - #PSA_KEY_DERIVATION_INPUT_SEED is the seed. + * - #PSA_KEY_DERIVATION_INPUT_OTHER_SECRET is the other secret for the + * computation of the premaster secret. This input is optional; + * if omitted, it defaults to a string of null bytes with the same length + * as the secret (PSK) input. + * - #PSA_KEY_DERIVATION_INPUT_SECRET is the secret key. + * - #PSA_KEY_DERIVATION_INPUT_LABEL is the label. + * + * For the application to TLS-1.2, the seed (which is + * forwarded to the TLS-1.2 PRF) is the concatenation of the + * ClientHello.Random + ServerHello.Random, + * the label is "master secret" or "extended master secret" and + * the other secret depends on the key exchange specified in the cipher suite: + * - for a plain PSK cipher suite (RFC 4279, Section 2), omit + * PSA_KEY_DERIVATION_INPUT_OTHER_SECRET + * - for a DHE-PSK (RFC 4279, Section 3) or ECDHE-PSK cipher suite + * (RFC 5489, Section 2), the other secret should be the output of the + * PSA_ALG_FFDH or PSA_ALG_ECDH key agreement performed with the peer. + * The recommended way to pass this input is to use a key derivation + * algorithm constructed as + * PSA_ALG_KEY_AGREEMENT(ka_alg, PSA_ALG_TLS12_PSK_TO_MS(hash_alg)) + * and to call psa_key_derivation_key_agreement(). Alternatively, + * this input may be an output of `psa_raw_key_agreement()` passed with + * psa_key_derivation_input_bytes(), or an equivalent input passed with + * psa_key_derivation_input_bytes() or psa_key_derivation_input_key(). + * - for a RSA-PSK cipher suite (RFC 4279, Section 4), the other secret + * should be the 48-byte client challenge (the PreMasterSecret of + * (RFC 5246, Section 7.4.7.1)) concatenation of the TLS version and + * a 46-byte random string chosen by the client. On the server, this is + * typically an output of psa_asymmetric_decrypt() using + * PSA_ALG_RSA_PKCS1V15_CRYPT, passed to the key derivation operation + * with `psa_key_derivation_input_bytes()`. + * + * For example, `PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256)` represents the + * TLS-1.2 PSK to MasterSecret derivation PRF using HMAC-SHA-256. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding TLS-1.2 PSK to MS algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_TLS12_PSK_TO_MS(hash_alg) \ + (PSA_ALG_TLS12_PSK_TO_MS_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) + +/** Whether the specified algorithm is a TLS-1.2 PSK to MS algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is a TLS-1.2 PSK to MS algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_TLS12_PSK_TO_MS(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_TLS12_PSK_TO_MS_BASE) +#define PSA_ALG_TLS12_PSK_TO_MS_GET_HASH(hkdf_alg) \ + (PSA_ALG_CATEGORY_HASH | ((hkdf_alg) & PSA_ALG_HASH_MASK)) + +/* The TLS 1.2 ECJPAKE-to-PMS KDF. It takes the shared secret K (an EC point + * in case of EC J-PAKE) and calculates SHA256(K.X) that the rest of TLS 1.2 + * will use to derive the session secret, as defined by step 2 of + * https://datatracker.ietf.org/doc/html/draft-cragie-tls-ecjpake-01#section-8.7. + * Uses PSA_ALG_SHA_256. + * This function takes a single input: + * #PSA_KEY_DERIVATION_INPUT_SECRET is the shared secret K from EC J-PAKE. + * The only supported curve is secp256r1 (the 256-bit curve in + * #PSA_ECC_FAMILY_SECP_R1), so the input must be exactly 65 bytes. + * The output has to be read as a single chunk of 32 bytes, defined as + * PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE. + */ +#define PSA_ALG_TLS12_ECJPAKE_TO_PMS ((psa_algorithm_t) 0x08000609) + +/* This flag indicates whether the key derivation algorithm is suitable for + * use on low-entropy secrets such as password - these algorithms are also + * known as key stretching or password hashing schemes. These are also the + * algorithms that accepts inputs of type #PSA_KEY_DERIVATION_INPUT_PASSWORD. + * + * Those algorithms cannot be combined with a key agreement algorithm. + */ +#define PSA_ALG_KEY_DERIVATION_STRETCHING_FLAG ((psa_algorithm_t) 0x00800000) + +#define PSA_ALG_PBKDF2_HMAC_BASE ((psa_algorithm_t) 0x08800100) +/** Macro to build a PBKDF2-HMAC password hashing / key stretching algorithm. + * + * PBKDF2 is defined by PKCS#5, republished as RFC 8018 (section 5.2). + * This macro specifies the PBKDF2 algorithm constructed using a PRF based on + * HMAC with the specified hash. + * For example, `PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)` specifies PBKDF2 + * using the PRF HMAC-SHA-256. + * + * This key derivation algorithm uses the following inputs, which must be + * provided in the following order: + * - #PSA_KEY_DERIVATION_INPUT_COST is the iteration count. + * This input step must be used exactly once. + * - #PSA_KEY_DERIVATION_INPUT_SALT is the salt. + * This input step must be used one or more times; if used several times, the + * inputs will be concatenated. This can be used to build the final salt + * from multiple sources, both public and secret (also known as pepper). + * - #PSA_KEY_DERIVATION_INPUT_PASSWORD is the password to be hashed. + * This input step must be used exactly once. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding PBKDF2-HMAC-XXX algorithm. + * \return Unspecified if \p hash_alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_PBKDF2_HMAC(hash_alg) \ + (PSA_ALG_PBKDF2_HMAC_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) + +/** Whether the specified algorithm is a PBKDF2-HMAC algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is a PBKDF2-HMAC algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_PBKDF2_HMAC(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_PBKDF2_HMAC_BASE) + +/** The PBKDF2-AES-CMAC-PRF-128 password hashing / key stretching algorithm. + * + * PBKDF2 is defined by PKCS#5, republished as RFC 8018 (section 5.2). + * This macro specifies the PBKDF2 algorithm constructed using the + * AES-CMAC-PRF-128 PRF specified by RFC 4615. + * + * This key derivation algorithm uses the same inputs as + * #PSA_ALG_PBKDF2_HMAC() with the same constraints. + */ +#define PSA_ALG_PBKDF2_AES_CMAC_PRF_128 ((psa_algorithm_t) 0x08800200) + +#define PSA_ALG_KEY_DERIVATION_MASK ((psa_algorithm_t) 0xfe00ffff) +#define PSA_ALG_KEY_AGREEMENT_MASK ((psa_algorithm_t) 0xffff0000) + +/** Macro to build a combined algorithm that chains a key agreement with + * a key derivation. + * + * \param ka_alg A key agreement algorithm (\c PSA_ALG_XXX value such + * that #PSA_ALG_IS_KEY_AGREEMENT(\p ka_alg) is true). + * \param kdf_alg A key derivation algorithm (\c PSA_ALG_XXX value such + * that #PSA_ALG_IS_KEY_DERIVATION(\p kdf_alg) is true). + * + * \return The corresponding key agreement and derivation + * algorithm. + * \return Unspecified if \p ka_alg is not a supported + * key agreement algorithm or \p kdf_alg is not a + * supported key derivation algorithm. + */ +#define PSA_ALG_KEY_AGREEMENT(ka_alg, kdf_alg) \ + ((ka_alg) | (kdf_alg)) + +#define PSA_ALG_KEY_AGREEMENT_GET_KDF(alg) \ + (((alg) & PSA_ALG_KEY_DERIVATION_MASK) | PSA_ALG_CATEGORY_KEY_DERIVATION) + +#define PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) \ + (((alg) & PSA_ALG_KEY_AGREEMENT_MASK) | PSA_ALG_CATEGORY_KEY_AGREEMENT) + +/** Whether the specified algorithm is a raw key agreement algorithm. + * + * A raw key agreement algorithm is one that does not specify + * a key derivation function. + * Usually, raw key agreement algorithms are constructed directly with + * a \c PSA_ALG_xxx macro while non-raw key agreement algorithms are + * constructed with #PSA_ALG_KEY_AGREEMENT(). + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \p alg is a raw key agreement algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \p alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_RAW_KEY_AGREEMENT(alg) \ + (PSA_ALG_IS_KEY_AGREEMENT(alg) && \ + PSA_ALG_KEY_AGREEMENT_GET_KDF(alg) == PSA_ALG_CATEGORY_KEY_DERIVATION) + +#define PSA_ALG_IS_KEY_DERIVATION_OR_AGREEMENT(alg) \ + ((PSA_ALG_IS_KEY_DERIVATION(alg) || PSA_ALG_IS_KEY_AGREEMENT(alg))) + +/** The finite-field Diffie-Hellman (DH) key agreement algorithm. + * + * The shared secret produced by key agreement is + * `g^{ab}` in big-endian format. + * It is `ceiling(m / 8)` bytes long where `m` is the size of the prime `p` + * in bits. + */ +#define PSA_ALG_FFDH ((psa_algorithm_t) 0x09010000) + +/** Whether the specified algorithm is a finite field Diffie-Hellman algorithm. + * + * This includes the raw finite field Diffie-Hellman algorithm as well as + * finite-field Diffie-Hellman followed by any supporter key derivation + * algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is a finite field Diffie-Hellman algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key agreement algorithm identifier. + */ +#define PSA_ALG_IS_FFDH(alg) \ + (PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) == PSA_ALG_FFDH) + +/** The elliptic curve Diffie-Hellman (ECDH) key agreement algorithm. + * + * The shared secret produced by key agreement is the x-coordinate of + * the shared secret point. It is always `ceiling(m / 8)` bytes long where + * `m` is the bit size associated with the curve, i.e. the bit size of the + * order of the curve's coordinate field. When `m` is not a multiple of 8, + * the byte containing the most significant bit of the shared secret + * is padded with zero bits. The byte order is either little-endian + * or big-endian depending on the curve type. + * + * - For Montgomery curves (curve types `PSA_ECC_FAMILY_CURVEXXX`), + * the shared secret is the x-coordinate of `d_A Q_B = d_B Q_A` + * in little-endian byte order. + * The bit size is 448 for Curve448 and 255 for Curve25519. + * - For Weierstrass curves over prime fields (curve types + * `PSA_ECC_FAMILY_SECPXXX` and `PSA_ECC_FAMILY_BRAINPOOL_PXXX`), + * the shared secret is the x-coordinate of `d_A Q_B = d_B Q_A` + * in big-endian byte order. + * The bit size is `m = ceiling(log_2(p))` for the field `F_p`. + * - For Weierstrass curves over binary fields (curve types + * `PSA_ECC_FAMILY_SECTXXX`), + * the shared secret is the x-coordinate of `d_A Q_B = d_B Q_A` + * in big-endian byte order. + * The bit size is `m` for the field `F_{2^m}`. + */ +#define PSA_ALG_ECDH ((psa_algorithm_t) 0x09020000) + +/** Whether the specified algorithm is an elliptic curve Diffie-Hellman + * algorithm. + * + * This includes the raw elliptic curve Diffie-Hellman algorithm as well as + * elliptic curve Diffie-Hellman followed by any supporter key derivation + * algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is an elliptic curve Diffie-Hellman algorithm, + * 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key agreement algorithm identifier. + */ +#define PSA_ALG_IS_ECDH(alg) \ + (PSA_ALG_KEY_AGREEMENT_GET_BASE(alg) == PSA_ALG_ECDH) + +/** Whether the specified algorithm encoding is a wildcard. + * + * Wildcard values may only be used to set the usage algorithm field in + * a policy, not to perform an operation. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is a wildcard algorithm encoding. + * \return 0 if \c alg is a non-wildcard algorithm encoding (suitable for + * an operation). + * \return This macro may return either 0 or 1 if \c alg is not a supported + * algorithm identifier. + */ +#define PSA_ALG_IS_WILDCARD(alg) \ + (PSA_ALG_IS_HASH_AND_SIGN(alg) ? \ + PSA_ALG_SIGN_GET_HASH(alg) == PSA_ALG_ANY_HASH : \ + PSA_ALG_IS_MAC(alg) ? \ + (alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0 : \ + PSA_ALG_IS_AEAD(alg) ? \ + (alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0 : \ + (alg) == PSA_ALG_ANY_HASH) + +/** Get the hash used by a composite algorithm. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return The underlying hash algorithm if alg is a composite algorithm that + * uses a hash algorithm. + * + * \return \c 0 if alg is not a composite algorithm that uses a hash. + */ +#define PSA_ALG_GET_HASH(alg) \ + (((alg) & 0x000000ff) == 0 ? ((psa_algorithm_t) 0) : 0x02000000 | ((alg) & 0x000000ff)) + +/**@}*/ + +/** \defgroup key_lifetimes Key lifetimes + * @{ + */ + +/* Note that location and persistence level values are embedded in the + * persistent key store, as part of key metadata. As a consequence, they + * must not be changed (unless the storage format version changes). + */ + +/** The default lifetime for volatile keys. + * + * A volatile key only exists as long as the identifier to it is not destroyed. + * The key material is guaranteed to be erased on a power reset. + * + * A key with this lifetime is typically stored in the RAM area of the + * PSA Crypto subsystem. However this is an implementation choice. + * If an implementation stores data about the key in a non-volatile memory, + * it must release all the resources associated with the key and erase the + * key material if the calling application terminates. + */ +#define PSA_KEY_LIFETIME_VOLATILE ((psa_key_lifetime_t) 0x00000000) + +/** The default lifetime for persistent keys. + * + * A persistent key remains in storage until it is explicitly destroyed or + * until the corresponding storage area is wiped. This specification does + * not define any mechanism to wipe a storage area, but integrations may + * provide their own mechanism (for example to perform a factory reset, + * to prepare for device refurbishment, or to uninstall an application). + * + * This lifetime value is the default storage area for the calling + * application. Integrations of Mbed TLS may support other persistent lifetimes. + * See ::psa_key_lifetime_t for more information. + */ +#define PSA_KEY_LIFETIME_PERSISTENT ((psa_key_lifetime_t) 0x00000001) + +/** The persistence level of volatile keys. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_VOLATILE ((psa_key_persistence_t) 0x00) + +/** The default persistence level for persistent keys. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_DEFAULT ((psa_key_persistence_t) 0x01) + +/** A persistence level indicating that a key is never destroyed. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_READ_ONLY ((psa_key_persistence_t) 0xff) + +#define PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) \ + ((psa_key_persistence_t) ((lifetime) & 0x000000ff)) + +#define PSA_KEY_LIFETIME_GET_LOCATION(lifetime) \ + ((psa_key_location_t) ((lifetime) >> 8)) + +/** Whether a key lifetime indicates that the key is volatile. + * + * A volatile key is automatically destroyed by the implementation when + * the application instance terminates. In particular, a volatile key + * is automatically destroyed on a power reset of the device. + * + * A key that is not volatile is persistent. Persistent keys are + * preserved until the application explicitly destroys them or until an + * implementation-specific device management event occurs (for example, + * a factory reset). + * + * \param lifetime The lifetime value to query (value of type + * ::psa_key_lifetime_t). + * + * \return \c 1 if the key is volatile, otherwise \c 0. + */ +#define PSA_KEY_LIFETIME_IS_VOLATILE(lifetime) \ + (PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) == \ + PSA_KEY_PERSISTENCE_VOLATILE) + +/** Whether a key lifetime indicates that the key is read-only. + * + * Read-only keys cannot be created or destroyed through the PSA Crypto API. + * They must be created through platform-specific means that bypass the API. + * + * Some platforms may offer ways to destroy read-only keys. For example, + * consider a platform with multiple levels of privilege, where a + * low-privilege application can use a key but is not allowed to destroy + * it, and the platform exposes the key to the application with a read-only + * lifetime. High-privilege code can destroy the key even though the + * application sees the key as read-only. + * + * \param lifetime The lifetime value to query (value of type + * ::psa_key_lifetime_t). + * + * \return \c 1 if the key is read-only, otherwise \c 0. + */ +#define PSA_KEY_LIFETIME_IS_READ_ONLY(lifetime) \ + (PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) == \ + PSA_KEY_PERSISTENCE_READ_ONLY) + +/** Construct a lifetime from a persistence level and a location. + * + * \param persistence The persistence level + * (value of type ::psa_key_persistence_t). + * \param location The location indicator + * (value of type ::psa_key_location_t). + * + * \return The constructed lifetime value. + */ +#define PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(persistence, location) \ + ((location) << 8 | (persistence)) + +/** The local storage area for persistent keys. + * + * This storage area is available on all systems that can store persistent + * keys without delegating the storage to a third-party cryptoprocessor. + * + * See ::psa_key_location_t for more information. + */ +#define PSA_KEY_LOCATION_LOCAL_STORAGE ((psa_key_location_t) 0x000000) + +#define PSA_KEY_LOCATION_VENDOR_FLAG ((psa_key_location_t) 0x800000) + +/* Note that key identifier values are embedded in the + * persistent key store, as part of key metadata. As a consequence, they + * must not be changed (unless the storage format version changes). + */ + +/** The null key identifier. + */ +/* *INDENT-OFF* (https://github.com/ARM-software/psa-arch-tests/issues/337) */ +#define PSA_KEY_ID_NULL ((psa_key_id_t)0) +/* *INDENT-ON* */ +/** The minimum value for a key identifier chosen by the application. + */ +#define PSA_KEY_ID_USER_MIN ((psa_key_id_t) 0x00000001) +/** The maximum value for a key identifier chosen by the application. + */ +#define PSA_KEY_ID_USER_MAX ((psa_key_id_t) 0x3fffffff) +/** The minimum value for a key identifier chosen by the implementation. + */ +#define PSA_KEY_ID_VENDOR_MIN ((psa_key_id_t) 0x40000000) +/** The maximum value for a key identifier chosen by the implementation. + */ +#define PSA_KEY_ID_VENDOR_MAX ((psa_key_id_t) 0x7fffffff) + + +#if !defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) + +#define MBEDTLS_SVC_KEY_ID_INIT ((psa_key_id_t) 0) +#define MBEDTLS_SVC_KEY_ID_GET_KEY_ID(id) (id) +#define MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(id) (0) + +/** Utility to initialize a key identifier at runtime. + * + * \param unused Unused parameter. + * \param key_id Identifier of the key. + */ +static inline mbedtls_svc_key_id_t mbedtls_svc_key_id_make( + unsigned int unused, psa_key_id_t key_id) +{ + (void) unused; + + return key_id; +} + +/** Compare two key identifiers. + * + * \param id1 First key identifier. + * \param id2 Second key identifier. + * + * \return Non-zero if the two key identifier are equal, zero otherwise. + */ +static inline int mbedtls_svc_key_id_equal(mbedtls_svc_key_id_t id1, + mbedtls_svc_key_id_t id2) +{ + return id1 == id2; +} + +/** Check whether a key identifier is null. + * + * \param key Key identifier. + * + * \return Non-zero if the key identifier is null, zero otherwise. + */ +static inline int mbedtls_svc_key_id_is_null(mbedtls_svc_key_id_t key) +{ + return key == 0; +} + +#else /* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */ + +#define MBEDTLS_SVC_KEY_ID_INIT ((mbedtls_svc_key_id_t){ 0, 0 }) +#define MBEDTLS_SVC_KEY_ID_GET_KEY_ID(id) ((id).MBEDTLS_PRIVATE(key_id)) +#define MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(id) ((id).MBEDTLS_PRIVATE(owner)) + +/** Utility to initialize a key identifier at runtime. + * + * \param owner_id Identifier of the key owner. + * \param key_id Identifier of the key. + */ +static inline mbedtls_svc_key_id_t mbedtls_svc_key_id_make( + mbedtls_key_owner_id_t owner_id, psa_key_id_t key_id) +{ + return (mbedtls_svc_key_id_t){ .MBEDTLS_PRIVATE(key_id) = key_id, + .MBEDTLS_PRIVATE(owner) = owner_id }; +} + +/** Compare two key identifiers. + * + * \param id1 First key identifier. + * \param id2 Second key identifier. + * + * \return Non-zero if the two key identifier are equal, zero otherwise. + */ +static inline int mbedtls_svc_key_id_equal(mbedtls_svc_key_id_t id1, + mbedtls_svc_key_id_t id2) +{ + return (id1.MBEDTLS_PRIVATE(key_id) == id2.MBEDTLS_PRIVATE(key_id)) && + mbedtls_key_owner_id_equal(id1.MBEDTLS_PRIVATE(owner), id2.MBEDTLS_PRIVATE(owner)); +} + +/** Check whether a key identifier is null. + * + * \param key Key identifier. + * + * \return Non-zero if the key identifier is null, zero otherwise. + */ +static inline int mbedtls_svc_key_id_is_null(mbedtls_svc_key_id_t key) +{ + return key.MBEDTLS_PRIVATE(key_id) == 0; +} + +#endif /* !MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */ + +/**@}*/ + +/** \defgroup policy Key policies + * @{ + */ + +/* Note that key usage flags are embedded in the + * persistent key store, as part of key metadata. As a consequence, they + * must not be changed (unless the storage format version changes). + */ + +/** Whether the key may be exported. + * + * A public key or the public part of a key pair may always be exported + * regardless of the value of this permission flag. + * + * If a key does not have export permission, implementations shall not + * allow the key to be exported in plain form from the cryptoprocessor, + * whether through psa_export_key() or through a proprietary interface. + * The key may however be exportable in a wrapped form, i.e. in a form + * where it is encrypted by another key. + */ +#define PSA_KEY_USAGE_EXPORT ((psa_key_usage_t) 0x00000001) + +/** Whether the key may be copied. + * + * This flag allows the use of psa_copy_key() to make a copy of the key + * with the same policy or a more restrictive policy. + * + * For lifetimes for which the key is located in a secure element which + * enforce the non-exportability of keys, copying a key outside the secure + * element also requires the usage flag #PSA_KEY_USAGE_EXPORT. + * Copying the key inside the secure element is permitted with just + * #PSA_KEY_USAGE_COPY if the secure element supports it. + * For keys with the lifetime #PSA_KEY_LIFETIME_VOLATILE or + * #PSA_KEY_LIFETIME_PERSISTENT, the usage flag #PSA_KEY_USAGE_COPY + * is sufficient to permit the copy. + */ +#define PSA_KEY_USAGE_COPY ((psa_key_usage_t) 0x00000002) + +/** Whether the key may be used to encrypt a message. + * + * This flag allows the key to be used for a symmetric encryption operation, + * for an AEAD encryption-and-authentication operation, + * or for an asymmetric encryption operation, + * if otherwise permitted by the key's type and policy. + * + * For a key pair, this concerns the public key. + */ +#define PSA_KEY_USAGE_ENCRYPT ((psa_key_usage_t) 0x00000100) + +/** Whether the key may be used to decrypt a message. + * + * This flag allows the key to be used for a symmetric decryption operation, + * for an AEAD decryption-and-verification operation, + * or for an asymmetric decryption operation, + * if otherwise permitted by the key's type and policy. + * + * For a key pair, this concerns the private key. + */ +#define PSA_KEY_USAGE_DECRYPT ((psa_key_usage_t) 0x00000200) + +/** Whether the key may be used to sign a message. + * + * This flag allows the key to be used for a MAC calculation operation or for + * an asymmetric message signature operation, if otherwise permitted by the + * key’s type and policy. + * + * For a key pair, this concerns the private key. + */ +#define PSA_KEY_USAGE_SIGN_MESSAGE ((psa_key_usage_t) 0x00000400) + +/** Whether the key may be used to verify a message. + * + * This flag allows the key to be used for a MAC verification operation or for + * an asymmetric message signature verification operation, if otherwise + * permitted by the key’s type and policy. + * + * For a key pair, this concerns the public key. + */ +#define PSA_KEY_USAGE_VERIFY_MESSAGE ((psa_key_usage_t) 0x00000800) + +/** Whether the key may be used to sign a message. + * + * This flag allows the key to be used for a MAC calculation operation + * or for an asymmetric signature operation, + * if otherwise permitted by the key's type and policy. + * + * For a key pair, this concerns the private key. + */ +#define PSA_KEY_USAGE_SIGN_HASH ((psa_key_usage_t) 0x00001000) + +/** Whether the key may be used to verify a message signature. + * + * This flag allows the key to be used for a MAC verification operation + * or for an asymmetric signature verification operation, + * if otherwise permitted by the key's type and policy. + * + * For a key pair, this concerns the public key. + */ +#define PSA_KEY_USAGE_VERIFY_HASH ((psa_key_usage_t) 0x00002000) + +/** Whether the key may be used to derive other keys or produce a password + * hash. + * + * This flag allows the key to be used for a key derivation operation or for + * a key agreement operation, if otherwise permitted by the key's type and + * policy. + * + * If this flag is present on all keys used in calls to + * psa_key_derivation_input_key() for a key derivation operation, then it + * permits calling psa_key_derivation_output_bytes() or + * psa_key_derivation_output_key() at the end of the operation. + */ +#define PSA_KEY_USAGE_DERIVE ((psa_key_usage_t) 0x00004000) + +/** Whether the key may be used to verify the result of a key derivation, + * including password hashing. + * + * This flag allows the key to be used: + * + * This flag allows the key to be used in a key derivation operation, if + * otherwise permitted by the key's type and policy. + * + * If this flag is present on all keys used in calls to + * psa_key_derivation_input_key() for a key derivation operation, then it + * permits calling psa_key_derivation_verify_bytes() or + * psa_key_derivation_verify_key() at the end of the operation. + */ +#define PSA_KEY_USAGE_VERIFY_DERIVATION ((psa_key_usage_t) 0x00008000) + +/**@}*/ + +/** \defgroup derivation Key derivation + * @{ + */ + +/* Key input steps are not embedded in the persistent storage, so you can + * change them if needed: it's only an ABI change. */ + +/** A secret input for key derivation. + * + * This should be a key of type #PSA_KEY_TYPE_DERIVE + * (passed to psa_key_derivation_input_key()) + * or the shared secret resulting from a key agreement + * (obtained via psa_key_derivation_key_agreement()). + * + * The secret can also be a direct input (passed to + * key_derivation_input_bytes()). In this case, the derivation operation + * may not be used to derive keys: the operation will only allow + * psa_key_derivation_output_bytes(), + * psa_key_derivation_verify_bytes(), or + * psa_key_derivation_verify_key(), but not + * psa_key_derivation_output_key(). + */ +#define PSA_KEY_DERIVATION_INPUT_SECRET ((psa_key_derivation_step_t) 0x0101) + +/** A low-entropy secret input for password hashing / key stretching. + * + * This is usually a key of type #PSA_KEY_TYPE_PASSWORD (passed to + * psa_key_derivation_input_key()) or a direct input (passed to + * psa_key_derivation_input_bytes()) that is a password or passphrase. It can + * also be high-entropy secret such as a key of type #PSA_KEY_TYPE_DERIVE or + * the shared secret resulting from a key agreement. + * + * The secret can also be a direct input (passed to + * key_derivation_input_bytes()). In this case, the derivation operation + * may not be used to derive keys: the operation will only allow + * psa_key_derivation_output_bytes(), + * psa_key_derivation_verify_bytes(), or + * psa_key_derivation_verify_key(), but not + * psa_key_derivation_output_key(). + */ +#define PSA_KEY_DERIVATION_INPUT_PASSWORD ((psa_key_derivation_step_t) 0x0102) + +/** A high-entropy additional secret input for key derivation. + * + * This is typically the shared secret resulting from a key agreement obtained + * via `psa_key_derivation_key_agreement()`. It may alternatively be a key of + * type `PSA_KEY_TYPE_DERIVE` passed to `psa_key_derivation_input_key()`, or + * a direct input passed to `psa_key_derivation_input_bytes()`. + */ +#define PSA_KEY_DERIVATION_INPUT_OTHER_SECRET \ + ((psa_key_derivation_step_t) 0x0103) + +/** A label for key derivation. + * + * This should be a direct input. + * It can also be a key of type #PSA_KEY_TYPE_RAW_DATA. + */ +#define PSA_KEY_DERIVATION_INPUT_LABEL ((psa_key_derivation_step_t) 0x0201) + +/** A salt for key derivation. + * + * This should be a direct input. + * It can also be a key of type #PSA_KEY_TYPE_RAW_DATA or + * #PSA_KEY_TYPE_PEPPER. + */ +#define PSA_KEY_DERIVATION_INPUT_SALT ((psa_key_derivation_step_t) 0x0202) + +/** An information string for key derivation. + * + * This should be a direct input. + * It can also be a key of type #PSA_KEY_TYPE_RAW_DATA. + */ +#define PSA_KEY_DERIVATION_INPUT_INFO ((psa_key_derivation_step_t) 0x0203) + +/** A seed for key derivation. + * + * This should be a direct input. + * It can also be a key of type #PSA_KEY_TYPE_RAW_DATA. + */ +#define PSA_KEY_DERIVATION_INPUT_SEED ((psa_key_derivation_step_t) 0x0204) + +/** A cost parameter for password hashing / key stretching. + * + * This must be a direct input, passed to psa_key_derivation_input_integer(). + */ +#define PSA_KEY_DERIVATION_INPUT_COST ((psa_key_derivation_step_t) 0x0205) + +/**@}*/ + +/** \defgroup helper_macros Helper macros + * @{ + */ + +/* Helper macros */ + +/** Check if two AEAD algorithm identifiers refer to the same AEAD algorithm + * regardless of the tag length they encode. + * + * \param aead_alg_1 An AEAD algorithm identifier. + * \param aead_alg_2 An AEAD algorithm identifier. + * + * \return 1 if both identifiers refer to the same AEAD algorithm, + * 0 otherwise. + * Unspecified if neither \p aead_alg_1 nor \p aead_alg_2 are + * a supported AEAD algorithm. + */ +#define MBEDTLS_PSA_ALG_AEAD_EQUAL(aead_alg_1, aead_alg_2) \ + (!(((aead_alg_1) ^ (aead_alg_2)) & \ + ~(PSA_ALG_AEAD_TAG_LENGTH_MASK | PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG))) + +/**@}*/ + +/**@}*/ + +/** \defgroup interruptible Interruptible operations + * @{ + */ + +/** Maximum value for use with \c psa_interruptible_set_max_ops() to determine + * the maximum number of ops allowed to be executed by an interruptible + * function in a single call. + */ +#define PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED UINT32_MAX + +/**@}*/ + +#endif /* PSA_CRYPTO_VALUES_H */ diff --git a/r5dev/thirdparty/mbedtls/lmots.c b/r5dev/thirdparty/mbedtls/lmots.c new file mode 100644 index 00000000..4061edde --- /dev/null +++ b/r5dev/thirdparty/mbedtls/lmots.c @@ -0,0 +1,825 @@ +/* + * The LM-OTS one-time public-key signature scheme + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The following sources were referenced in the design of this implementation + * of the LM-OTS algorithm: + * + * [1] IETF RFC8554 + * D. McGrew, M. Curcio, S.Fluhrer + * https://datatracker.ietf.org/doc/html/rfc8554 + * + * [2] NIST Special Publication 800-208 + * David A. Cooper et. al. + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_LMS_C) + +#include + +#include "lmots.h" + +#include "mbedtls/lms.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "mbedtls/psa_util.h" + +#include "psa/crypto.h" + +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_lms_errors, \ + psa_generic_status_to_mbedtls) + +#define PUBLIC_KEY_TYPE_OFFSET (0) +#define PUBLIC_KEY_I_KEY_ID_OFFSET (PUBLIC_KEY_TYPE_OFFSET + \ + MBEDTLS_LMOTS_TYPE_LEN) +#define PUBLIC_KEY_Q_LEAF_ID_OFFSET (PUBLIC_KEY_I_KEY_ID_OFFSET + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN) +#define PUBLIC_KEY_KEY_HASH_OFFSET (PUBLIC_KEY_Q_LEAF_ID_OFFSET + \ + MBEDTLS_LMOTS_Q_LEAF_ID_LEN) + +/* We only support parameter sets that use 8-bit digits, as it does not require + * translation logic between digits and bytes */ +#define W_WINTERNITZ_PARAMETER (8u) +#define CHECKSUM_LEN (2) +#define I_DIGIT_IDX_LEN (2) +#define J_HASH_IDX_LEN (1) +#define D_CONST_LEN (2) + +#define DIGIT_MAX_VALUE ((1u << W_WINTERNITZ_PARAMETER) - 1u) + +#define D_CONST_LEN (2) +static const unsigned char D_PUBLIC_CONSTANT_BYTES[D_CONST_LEN] = { 0x80, 0x80 }; +static const unsigned char D_MESSAGE_CONSTANT_BYTES[D_CONST_LEN] = { 0x81, 0x81 }; + +#if defined(MBEDTLS_TEST_HOOKS) +int (*mbedtls_lmots_sign_private_key_invalidated_hook)(unsigned char *) = NULL; +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + +void mbedtls_lms_unsigned_int_to_network_bytes(unsigned int val, size_t len, + unsigned char *bytes) +{ + size_t idx; + + for (idx = 0; idx < len; idx++) { + bytes[idx] = (val >> ((len - 1 - idx) * 8)) & 0xFF; + } +} + +unsigned int mbedtls_lms_network_bytes_to_unsigned_int(size_t len, + const unsigned char *bytes) +{ + size_t idx; + unsigned int val = 0; + + for (idx = 0; idx < len; idx++) { + val |= ((unsigned int) bytes[idx]) << (8 * (len - 1 - idx)); + } + + return val; +} + +/* Calculate the checksum digits that are appended to the end of the LMOTS digit + * string. See NIST SP800-208 section 3.1 or RFC8554 Algorithm 2 for details of + * the checksum algorithm. + * + * params The LMOTS parameter set, I and q values which + * describe the key being used. + * + * digest The digit string to create the digest from. As + * this does not contain a checksum, it is the same + * size as a hash output. + */ +static unsigned short lmots_checksum_calculate(const mbedtls_lmots_parameters_t *params, + const unsigned char *digest) +{ + size_t idx; + unsigned sum = 0; + + for (idx = 0; idx < MBEDTLS_LMOTS_N_HASH_LEN(params->type); idx++) { + sum += DIGIT_MAX_VALUE - digest[idx]; + } + + return sum; +} + +/* Create the string of digest digits (in the base determined by the Winternitz + * parameter with the checksum appended to the end (Q || cksm(Q)). See NIST + * SP800-208 section 3.1 or RFC8554 Algorithm 3 step 5 (also used in Algorithm + * 4b step 3) for details. + * + * params The LMOTS parameter set, I and q values which + * describe the key being used. + * + * msg The message that will be hashed to create the + * digest. + * + * msg_size The size of the message. + * + * C_random_value The random value that will be combined with the + * message digest. This is always the same size as a + * hash output for whichever hash algorithm is + * determined by the parameter set. + * + * output An output containing the digit string (+ + * checksum) of length P digits (in the case of + * MBEDTLS_LMOTS_SHA256_N32_W8, this means it is of + * size P bytes). + */ +static int create_digit_array_with_checksum(const mbedtls_lmots_parameters_t *params, + const unsigned char *msg, + size_t msg_len, + const unsigned char *C_random_value, + unsigned char *out) +{ + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned short checksum; + + status = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, params->q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, D_MESSAGE_CONSTANT_BYTES, D_CONST_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, C_random_value, + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(params->type)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, msg, msg_len); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&op, out, + MBEDTLS_LMOTS_N_HASH_LEN(params->type), + &output_hash_len); + if (status != PSA_SUCCESS) { + goto exit; + } + + checksum = lmots_checksum_calculate(params, out); + mbedtls_lms_unsigned_int_to_network_bytes(checksum, CHECKSUM_LEN, + out + MBEDTLS_LMOTS_N_HASH_LEN(params->type)); + +exit: + psa_hash_abort(&op); + + return PSA_TO_MBEDTLS_ERR(status); +} + +/* Hash each element of the string of digits (+ checksum), producing a hash + * output for each element. This is used in several places (by varying the + * hash_idx_min/max_values) in order to calculate a public key from a private + * key (RFC8554 Algorithm 1 step 4), in order to sign a message (RFC8554 + * Algorithm 3 step 5), and to calculate a public key candidate from a + * signature and message (RFC8554 Algorithm 4b step 3). + * + * params The LMOTS parameter set, I and q values which + * describe the key being used. + * + * x_digit_array The array of digits (of size P, 34 in the case of + * MBEDTLS_LMOTS_SHA256_N32_W8). + * + * hash_idx_min_values An array of the starting values of the j iterator + * for each of the members of the digit array. If + * this value in NULL, then all iterators will start + * at 0. + * + * hash_idx_max_values An array of the upper bound values of the j + * iterator for each of the members of the digit + * array. If this value in NULL, then iterator is + * bounded to be less than 2^w - 1 (255 in the case + * of MBEDTLS_LMOTS_SHA256_N32_W8) + * + * output An array containing a hash output for each member + * of the digit string P. In the case of + * MBEDTLS_LMOTS_SHA256_N32_W8, this is of size 32 * + * 34. + */ +static int hash_digit_array(const mbedtls_lmots_parameters_t *params, + const unsigned char *x_digit_array, + const unsigned char *hash_idx_min_values, + const unsigned char *hash_idx_max_values, + unsigned char *output) +{ + unsigned int i_digit_idx; + unsigned char i_digit_idx_bytes[I_DIGIT_IDX_LEN]; + unsigned int j_hash_idx; + unsigned char j_hash_idx_bytes[J_HASH_IDX_LEN]; + unsigned int j_hash_idx_min; + unsigned int j_hash_idx_max; + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned char tmp_hash[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + + for (i_digit_idx = 0; + i_digit_idx < MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(params->type); + i_digit_idx++) { + + memcpy(tmp_hash, + &x_digit_array[i_digit_idx * MBEDTLS_LMOTS_N_HASH_LEN(params->type)], + MBEDTLS_LMOTS_N_HASH_LEN(params->type)); + + j_hash_idx_min = hash_idx_min_values != NULL ? + hash_idx_min_values[i_digit_idx] : 0; + j_hash_idx_max = hash_idx_max_values != NULL ? + hash_idx_max_values[i_digit_idx] : DIGIT_MAX_VALUE; + + for (j_hash_idx = j_hash_idx_min; + j_hash_idx < j_hash_idx_max; + j_hash_idx++) { + status = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, + params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, + params->q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + mbedtls_lms_unsigned_int_to_network_bytes(i_digit_idx, + I_DIGIT_IDX_LEN, + i_digit_idx_bytes); + status = psa_hash_update(&op, i_digit_idx_bytes, I_DIGIT_IDX_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + mbedtls_lms_unsigned_int_to_network_bytes(j_hash_idx, + J_HASH_IDX_LEN, + j_hash_idx_bytes); + status = psa_hash_update(&op, j_hash_idx_bytes, J_HASH_IDX_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, tmp_hash, + MBEDTLS_LMOTS_N_HASH_LEN(params->type)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&op, tmp_hash, sizeof(tmp_hash), + &output_hash_len); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_hash_abort(&op); + } + + memcpy(&output[i_digit_idx * MBEDTLS_LMOTS_N_HASH_LEN(params->type)], + tmp_hash, MBEDTLS_LMOTS_N_HASH_LEN(params->type)); + } + +exit: + psa_hash_abort(&op); + mbedtls_platform_zeroize(tmp_hash, sizeof(tmp_hash)); + + return PSA_TO_MBEDTLS_ERR(status); +} + +/* Combine the hashes of the digit array into a public key. This is used in + * in order to calculate a public key from a private key (RFC8554 Algorithm 1 + * step 4), and to calculate a public key candidate from a signature and message + * (RFC8554 Algorithm 4b step 3). + * + * params The LMOTS parameter set, I and q values which describe + * the key being used. + * y_hashed_digits The array of hashes, one hash for each digit of the + * symbol array (which is of size P, 34 in the case of + * MBEDTLS_LMOTS_SHA256_N32_W8) + * + * pub_key The output public key (or candidate public key in + * case this is being run as part of signature + * verification), in the form of a hash output. + */ +static int public_key_from_hashed_digit_array(const mbedtls_lmots_parameters_t *params, + const unsigned char *y_hashed_digits, + unsigned char *pub_key) +{ + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + + status = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, + params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, params->q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, D_PUBLIC_CONSTANT_BYTES, D_CONST_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, y_hashed_digits, + MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(params->type) * + MBEDTLS_LMOTS_N_HASH_LEN(params->type)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&op, pub_key, + MBEDTLS_LMOTS_N_HASH_LEN(params->type), + &output_hash_len); + if (status != PSA_SUCCESS) { + +exit: + psa_hash_abort(&op); + } + + return PSA_TO_MBEDTLS_ERR(status); +} + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +int mbedtls_lms_error_from_psa(psa_status_t status) +{ + switch (status) { + case PSA_SUCCESS: + return 0; + case PSA_ERROR_HARDWARE_FAILURE: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED; + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL; + case PSA_ERROR_INVALID_ARGUMENT: + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + default: + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + } +} +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ + +void mbedtls_lmots_public_init(mbedtls_lmots_public_t *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +void mbedtls_lmots_public_free(mbedtls_lmots_public_t *ctx) +{ + mbedtls_platform_zeroize(ctx, sizeof(*ctx)); +} + +int mbedtls_lmots_import_public_key(mbedtls_lmots_public_t *ctx, + const unsigned char *key, size_t key_len) +{ + if (key_len < MBEDTLS_LMOTS_SIG_TYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + ctx->params.type = + mbedtls_lms_network_bytes_to_unsigned_int(MBEDTLS_LMOTS_TYPE_LEN, + key + MBEDTLS_LMOTS_SIG_TYPE_OFFSET); + + if (key_len != MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type)) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + memcpy(ctx->params.I_key_identifier, + key + PUBLIC_KEY_I_KEY_ID_OFFSET, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + + memcpy(ctx->params.q_leaf_identifier, + key + PUBLIC_KEY_Q_LEAF_ID_OFFSET, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN); + + memcpy(ctx->public_key, + key + PUBLIC_KEY_KEY_HASH_OFFSET, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type)); + + ctx->have_public_key = 1; + + return 0; +} + +int mbedtls_lmots_export_public_key(const mbedtls_lmots_public_t *ctx, + unsigned char *key, size_t key_size, + size_t *key_len) +{ + if (key_size < MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type)) { + return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL; + } + + if (!ctx->have_public_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + mbedtls_lms_unsigned_int_to_network_bytes(ctx->params.type, + MBEDTLS_LMOTS_TYPE_LEN, + key + MBEDTLS_LMOTS_SIG_TYPE_OFFSET); + + memcpy(key + PUBLIC_KEY_I_KEY_ID_OFFSET, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + + memcpy(key + PUBLIC_KEY_Q_LEAF_ID_OFFSET, + ctx->params.q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN); + + memcpy(key + PUBLIC_KEY_KEY_HASH_OFFSET, ctx->public_key, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type)); + + if (key_len != NULL) { + *key_len = MBEDTLS_LMOTS_PUBLIC_KEY_LEN(ctx->params.type); + } + + return 0; +} + +int mbedtls_lmots_calculate_public_key_candidate(const mbedtls_lmots_parameters_t *params, + const unsigned char *msg, + size_t msg_size, + const unsigned char *sig, + size_t sig_size, + unsigned char *out, + size_t out_size, + size_t *out_len) +{ + unsigned char tmp_digit_array[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX]; + unsigned char y_hashed_digits[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (msg == NULL && msg_size != 0) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (sig_size != MBEDTLS_LMOTS_SIG_LEN(params->type) || + out_size < MBEDTLS_LMOTS_N_HASH_LEN(params->type)) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + ret = create_digit_array_with_checksum(params, msg, msg_size, + sig + MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET, + tmp_digit_array); + if (ret) { + return ret; + } + + ret = hash_digit_array(params, + sig + MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(params->type), + tmp_digit_array, NULL, (unsigned char *) y_hashed_digits); + if (ret) { + return ret; + } + + ret = public_key_from_hashed_digit_array(params, + (unsigned char *) y_hashed_digits, + out); + if (ret) { + return ret; + } + + if (out_len != NULL) { + *out_len = MBEDTLS_LMOTS_N_HASH_LEN(params->type); + } + + return 0; +} + +int mbedtls_lmots_verify(const mbedtls_lmots_public_t *ctx, + const unsigned char *msg, size_t msg_size, + const unsigned char *sig, size_t sig_size) +{ + unsigned char Kc_public_key_candidate[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (msg == NULL && msg_size != 0) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (!ctx->have_public_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (ctx->params.type != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (sig_size < MBEDTLS_LMOTS_SIG_TYPE_OFFSET + MBEDTLS_LMOTS_TYPE_LEN) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + if (mbedtls_lms_network_bytes_to_unsigned_int(MBEDTLS_LMOTS_TYPE_LEN, + sig + MBEDTLS_LMOTS_SIG_TYPE_OFFSET) != + MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + ret = mbedtls_lmots_calculate_public_key_candidate(&ctx->params, + msg, msg_size, sig, sig_size, + Kc_public_key_candidate, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type), + NULL); + if (ret) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + if (memcmp(&Kc_public_key_candidate, ctx->public_key, + sizeof(ctx->public_key))) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + return 0; +} + +#if defined(MBEDTLS_LMS_PRIVATE) + +void mbedtls_lmots_private_init(mbedtls_lmots_private_t *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +void mbedtls_lmots_private_free(mbedtls_lmots_private_t *ctx) +{ + mbedtls_platform_zeroize(ctx, + sizeof(*ctx)); +} + +int mbedtls_lmots_generate_private_key(mbedtls_lmots_private_t *ctx, + mbedtls_lmots_algorithm_type_t type, + const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN], + uint32_t q_leaf_identifier, + const unsigned char *seed, + size_t seed_size) +{ + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned int i_digit_idx; + unsigned char i_digit_idx_bytes[2]; + unsigned char const_bytes[1]; + + if (ctx->have_private_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (type != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + ctx->params.type = type; + + memcpy(ctx->params.I_key_identifier, + I_key_identifier, + sizeof(ctx->params.I_key_identifier)); + + mbedtls_lms_unsigned_int_to_network_bytes(q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, + ctx->params.q_leaf_identifier); + + mbedtls_lms_unsigned_int_to_network_bytes(0xFF, sizeof(const_bytes), + const_bytes); + + for (i_digit_idx = 0; + i_digit_idx < MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(ctx->params.type); + i_digit_idx++) { + status = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, + ctx->params.I_key_identifier, + sizeof(ctx->params.I_key_identifier)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, + ctx->params.q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + mbedtls_lms_unsigned_int_to_network_bytes(i_digit_idx, I_DIGIT_IDX_LEN, + i_digit_idx_bytes); + status = psa_hash_update(&op, i_digit_idx_bytes, I_DIGIT_IDX_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, const_bytes, sizeof(const_bytes)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, seed, seed_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&op, + ctx->private_key[i_digit_idx], + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type), + &output_hash_len); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_hash_abort(&op); + } + + ctx->have_private_key = 1; + +exit: + psa_hash_abort(&op); + + return PSA_TO_MBEDTLS_ERR(status); +} + +int mbedtls_lmots_calculate_public_key(mbedtls_lmots_public_t *ctx, + const mbedtls_lmots_private_t *priv_ctx) +{ + unsigned char y_hashed_digits[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Check that a private key is loaded */ + if (!priv_ctx->have_private_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + ret = hash_digit_array(&priv_ctx->params, + (unsigned char *) priv_ctx->private_key, NULL, + NULL, (unsigned char *) y_hashed_digits); + if (ret) { + goto exit; + } + + ret = public_key_from_hashed_digit_array(&priv_ctx->params, + (unsigned char *) y_hashed_digits, + ctx->public_key); + if (ret) { + goto exit; + } + + memcpy(&ctx->params, &priv_ctx->params, + sizeof(ctx->params)); + + ctx->have_public_key = 1; + +exit: + mbedtls_platform_zeroize(y_hashed_digits, sizeof(y_hashed_digits)); + + return ret; +} + +int mbedtls_lmots_sign(mbedtls_lmots_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *msg, size_t msg_size, + unsigned char *sig, size_t sig_size, size_t *sig_len) +{ + unsigned char tmp_digit_array[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX]; + /* Create a temporary buffer to prepare the signature in. This allows us to + * finish creating a signature (ensuring the process doesn't fail), and then + * erase the private key **before** writing any data into the sig parameter + * buffer. If data were directly written into the sig buffer, it might leak + * a partial signature on failure, which effectively compromises the private + * key. + */ + unsigned char tmp_sig[MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT_MAX][MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char tmp_c_random[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (msg == NULL && msg_size != 0) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (sig_size < MBEDTLS_LMOTS_SIG_LEN(ctx->params.type)) { + return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL; + } + + /* Check that a private key is loaded */ + if (!ctx->have_private_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + ret = f_rng(p_rng, tmp_c_random, + MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type)); + if (ret) { + return ret; + } + + ret = create_digit_array_with_checksum(&ctx->params, + msg, msg_size, + tmp_c_random, + tmp_digit_array); + if (ret) { + goto exit; + } + + ret = hash_digit_array(&ctx->params, (unsigned char *) ctx->private_key, + NULL, tmp_digit_array, (unsigned char *) tmp_sig); + if (ret) { + goto exit; + } + + mbedtls_lms_unsigned_int_to_network_bytes(ctx->params.type, + MBEDTLS_LMOTS_TYPE_LEN, + sig + MBEDTLS_LMOTS_SIG_TYPE_OFFSET); + + /* Test hook to check if sig is being written to before we invalidate the + * private key. + */ +#if defined(MBEDTLS_TEST_HOOKS) + if (mbedtls_lmots_sign_private_key_invalidated_hook != NULL) { + ret = (*mbedtls_lmots_sign_private_key_invalidated_hook)(sig); + if (ret != 0) { + return ret; + } + } +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + + /* We've got a valid signature now, so it's time to make sure the private + * key can't be reused. + */ + ctx->have_private_key = 0; + mbedtls_platform_zeroize(ctx->private_key, + sizeof(ctx->private_key)); + + memcpy(sig + MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET, tmp_c_random, + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(ctx->params.type)); + + memcpy(sig + MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(ctx->params.type), tmp_sig, + MBEDTLS_LMOTS_P_SIG_DIGIT_COUNT(ctx->params.type) + * MBEDTLS_LMOTS_N_HASH_LEN(ctx->params.type)); + + if (sig_len != NULL) { + *sig_len = MBEDTLS_LMOTS_SIG_LEN(ctx->params.type); + } + + ret = 0; + +exit: + mbedtls_platform_zeroize(tmp_digit_array, sizeof(tmp_digit_array)); + mbedtls_platform_zeroize(tmp_sig, sizeof(tmp_sig)); + + return ret; +} + +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ +#endif /* defined(MBEDTLS_LMS_C) */ diff --git a/r5dev/thirdparty/mbedtls/lmots.h b/r5dev/thirdparty/mbedtls/lmots.h new file mode 100644 index 00000000..98d1941d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/lmots.h @@ -0,0 +1,323 @@ +/** + * \file lmots.h + * + * \brief This file provides an API for the LM-OTS post-quantum-safe one-time + * public-key signature scheme as defined in RFC8554 and NIST.SP.200-208. + * This implementation currently only supports a single parameter set + * MBEDTLS_LMOTS_SHA256_N32_W8 in order to reduce complexity. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_LMOTS_H +#define MBEDTLS_LMOTS_H + +#include "mbedtls/build_info.h" + +#include "psa/crypto.h" + +#include "mbedtls/lms.h" + +#include +#include + + +#define MBEDTLS_LMOTS_PUBLIC_KEY_LEN(type) (MBEDTLS_LMOTS_TYPE_LEN + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN + \ + MBEDTLS_LMOTS_Q_LEAF_ID_LEN + \ + MBEDTLS_LMOTS_N_HASH_LEN(type)) + +#define MBEDTLS_LMOTS_SIG_TYPE_OFFSET (0) +#define MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET (MBEDTLS_LMOTS_SIG_TYPE_OFFSET + \ + MBEDTLS_LMOTS_TYPE_LEN) +#define MBEDTLS_LMOTS_SIG_SIGNATURE_OFFSET(type) (MBEDTLS_LMOTS_SIG_C_RANDOM_OFFSET + \ + MBEDTLS_LMOTS_C_RANDOM_VALUE_LEN(type)) + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(MBEDTLS_TEST_HOOKS) +extern int (*mbedtls_lmots_sign_private_key_invalidated_hook)(unsigned char *); +#endif /* defined(MBEDTLS_TEST_HOOKS) */ + +/** + * \brief This function converts an unsigned int into a + * network-byte-order (big endian) string. + * + * \param val The unsigned integer value + * \param len The length of the string. + * \param bytes The string to output into. + */ +void mbedtls_lms_unsigned_int_to_network_bytes(unsigned int val, size_t len, + unsigned char *bytes); + +/** + * \brief This function converts a network-byte-order + * (big endian) string into an unsigned integer. + * + * \param len The length of the string. + * \param bytes The string. + * + * \return The corresponding LMS error code. + */ +unsigned int mbedtls_lms_network_bytes_to_unsigned_int(size_t len, + const unsigned char *bytes); + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief This function converts a \ref psa_status_t to a + * low-level LMS error code. + * + * \param status The psa_status_t to convert + * + * \return The corresponding LMS error code. + */ +int MBEDTLS_DEPRECATED mbedtls_lms_error_from_psa(psa_status_t status); +#endif + +/** + * \brief This function initializes a public LMOTS context + * + * \param ctx The uninitialized LMOTS context that will then be + * initialized. + */ +void mbedtls_lmots_public_init(mbedtls_lmots_public_t *ctx); + +/** + * \brief This function uninitializes a public LMOTS context + * + * \param ctx The initialized LMOTS context that will then be + * uninitialized. + */ +void mbedtls_lmots_public_free(mbedtls_lmots_public_t *ctx); + +/** + * \brief This function imports an LMOTS public key into a + * LMOTS context. + * + * \note Before this function is called, the context must + * have been initialized. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMOTS context store the key in. + * \param key The buffer from which the key will be read. + * #MBEDTLS_LMOTS_PUBLIC_KEY_LEN bytes will be read + * from this. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_import_public_key(mbedtls_lmots_public_t *ctx, + const unsigned char *key, size_t key_size); + +/** + * \brief This function exports an LMOTS public key from a + * LMOTS context that already contains a public key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a public key. + * + * \note See IETF RFC8554 for details of the encoding of + * this public key. + * + * \param ctx The initialized LMOTS context that contains the + * public key. + * \param key The buffer into which the key will be output. Must + * be at least #MBEDTLS_LMOTS_PUBLIC_KEY_LEN in size. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_export_public_key(const mbedtls_lmots_public_t *ctx, + unsigned char *key, size_t key_size, + size_t *key_len); + +/** + * \brief This function creates a candidate public key from + * an LMOTS signature. This can then be compared to + * the real public key to determine the validity of + * the signature. + * + * \note This function is exposed publicly to be used in LMS + * signature verification, it is expected that + * mbedtls_lmots_verify will be used for LMOTS + * signature verification. + * + * \param params The LMOTS parameter set, q and I values as an + * mbedtls_lmots_parameters_t struct. + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buffer from which the signature will be read. + * #MBEDTLS_LMOTS_SIG_LEN bytes will be read from + * this. + * \param out The buffer where the candidate public key will be + * stored. Must be at least #MBEDTLS_LMOTS_N_HASH_LEN + * bytes in size. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_calculate_public_key_candidate(const mbedtls_lmots_parameters_t *params, + const unsigned char *msg, + size_t msg_size, + const unsigned char *sig, + size_t sig_size, + unsigned char *out, + size_t out_size, + size_t *out_len); + +/** + * \brief This function verifies a LMOTS signature, using a + * LMOTS context that contains a public key. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note Before this function is called, the context must + * have been initialized and must contain a public key + * (either by import or calculation from a private + * key). + * + * \param ctx The initialized LMOTS context from which the public + * key will be read. + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf from which the signature will be read. + * #MBEDTLS_LMOTS_SIG_LEN bytes will be read from + * this. + * + * \return \c 0 on successful verification. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_verify(const mbedtls_lmots_public_t *ctx, + const unsigned char *msg, + size_t msg_size, const unsigned char *sig, + size_t sig_size); + +#if defined(MBEDTLS_LMS_PRIVATE) + +/** + * \brief This function initializes a private LMOTS context + * + * \param ctx The uninitialized LMOTS context that will then be + * initialized. + */ +void mbedtls_lmots_private_init(mbedtls_lmots_private_t *ctx); + +/** + * \brief This function uninitializes a private LMOTS context + * + * \param ctx The initialized LMOTS context that will then be + * uninitialized. + */ +void mbedtls_lmots_private_free(mbedtls_lmots_private_t *ctx); + +/** + * \brief This function calculates an LMOTS private key, and + * stores in into an LMOTS context. + * + * \warning This function is **not intended for use in + * production**, due to as-yet unsolved problems with + * handling stateful keys. The API for this function + * may change considerably in future versions. + * + * \note The seed must have at least 256 bits of entropy. + * + * \param ctx The initialized LMOTS context to generate the key + * into. + * \param I_key_identifier The key identifier of the key, as a 16-byte string. + * \param q_leaf_identifier The leaf identifier of key. If this LMOTS key is + * not being used as part of an LMS key, this should + * be set to 0. + * \param seed The seed used to deterministically generate the + * key. + * \param seed_size The length of the seed. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_generate_private_key(mbedtls_lmots_private_t *ctx, + mbedtls_lmots_algorithm_type_t type, + const unsigned char I_key_identifier[MBEDTLS_LMOTS_I_KEY_ID_LEN], + uint32_t q_leaf_identifier, + const unsigned char *seed, + size_t seed_size); + +/** + * \brief This function generates an LMOTS public key from a + * LMOTS context that already contains a private key. + * + * \note Before this function is called, the context must + * have been initialized and the context must contain + * a private key. + * + * \param ctx The initialized LMOTS context to generate the key + * from and store it into. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_calculate_public_key(mbedtls_lmots_public_t *ctx, + const mbedtls_lmots_private_t *priv_ctx); + +/** + * \brief This function creates a LMOTS signature, using a + * LMOTS context that contains a private key. + * + * \note Before this function is called, the context must + * have been initialized and must contain a private + * key. + * + * \note LMOTS private keys can only be used once, otherwise + * attackers may be able to create forged signatures. + * If the signing operation is successful, the private + * key in the context will be erased, and no further + * signing will be possible until another private key + * is loaded + * + * \param ctx The initialized LMOTS context from which the + * private key will be read. + * \param f_rng The RNG function to be used for signature + * generation. + * \param p_rng The RNG context to be passed to f_rng + * \param msg The buffer from which the message will be read. + * \param msg_size The size of the message that will be read. + * \param sig The buf into which the signature will be stored. + * Must be at least #MBEDTLS_LMOTS_SIG_LEN in size. + * + * \return \c 0 on success. + * \return A non-zero error code on failure. + */ +int mbedtls_lmots_sign(mbedtls_lmots_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *msg, size_t msg_size, + unsigned char *sig, size_t sig_size, size_t *sig_len); + +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_LMOTS_H */ diff --git a/r5dev/thirdparty/mbedtls/lms.c b/r5dev/thirdparty/mbedtls/lms.c new file mode 100644 index 00000000..acc35233 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/lms.c @@ -0,0 +1,783 @@ +/* + * The LMS stateful-hash public-key signature scheme + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The following sources were referenced in the design of this implementation + * of the LMS algorithm: + * + * [1] IETF RFC8554 + * D. McGrew, M. Curcio, S.Fluhrer + * https://datatracker.ietf.org/doc/html/rfc8554 + * + * [2] NIST Special Publication 800-208 + * David A. Cooper et. al. + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-208.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_LMS_C) + +#include + +#include "lmots.h" + +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#include "mbedtls/lms.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" + +#include "mbedtls/platform.h" + +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_lms_errors, \ + psa_generic_status_to_mbedtls) + +#define SIG_Q_LEAF_ID_OFFSET (0) +#define SIG_OTS_SIG_OFFSET (SIG_Q_LEAF_ID_OFFSET + \ + MBEDTLS_LMOTS_Q_LEAF_ID_LEN) +#define SIG_TYPE_OFFSET(otstype) (SIG_OTS_SIG_OFFSET + \ + MBEDTLS_LMOTS_SIG_LEN(otstype)) +#define SIG_PATH_OFFSET(otstype) (SIG_TYPE_OFFSET(otstype) + \ + MBEDTLS_LMS_TYPE_LEN) + +#define PUBLIC_KEY_TYPE_OFFSET (0) +#define PUBLIC_KEY_OTSTYPE_OFFSET (PUBLIC_KEY_TYPE_OFFSET + \ + MBEDTLS_LMS_TYPE_LEN) +#define PUBLIC_KEY_I_KEY_ID_OFFSET (PUBLIC_KEY_OTSTYPE_OFFSET + \ + MBEDTLS_LMOTS_TYPE_LEN) +#define PUBLIC_KEY_ROOT_NODE_OFFSET (PUBLIC_KEY_I_KEY_ID_OFFSET + \ + MBEDTLS_LMOTS_I_KEY_ID_LEN) + + +/* Currently only support H=10 */ +#define H_TREE_HEIGHT_MAX 10 +#define MERKLE_TREE_NODE_AM(type) ((size_t) 1 << (MBEDTLS_LMS_H_TREE_HEIGHT(type) + 1u)) +#define MERKLE_TREE_LEAF_NODE_AM(type) ((size_t) 1 << MBEDTLS_LMS_H_TREE_HEIGHT(type)) +#define MERKLE_TREE_INTERNAL_NODE_AM(type) ((size_t) 1 << MBEDTLS_LMS_H_TREE_HEIGHT(type)) + +#define D_CONST_LEN (2) +static const unsigned char D_LEAF_CONSTANT_BYTES[D_CONST_LEN] = { 0x82, 0x82 }; +static const unsigned char D_INTR_CONSTANT_BYTES[D_CONST_LEN] = { 0x83, 0x83 }; + + +/* Calculate the value of a leaf node of the Merkle tree (which is a hash of a + * public key and some other parameters like the leaf index). This function + * implements RFC8554 section 5.3, in the case where r >= 2^h. + * + * params The LMS parameter set, the underlying LMOTS + * parameter set, and I value which describe the key + * being used. + * + * pub_key The public key of the private whose index + * corresponds to the index of this leaf node. This + * is a hash output. + * + * r_node_idx The index of this node in the Merkle tree. Note + * that the root node of the Merkle tree is + * 1-indexed. + * + * out The output node value, which is a hash output. + */ +static int create_merkle_leaf_value(const mbedtls_lms_parameters_t *params, + unsigned char *pub_key, + unsigned int r_node_idx, + unsigned char *out) +{ + psa_hash_operation_t op; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned char r_node_idx_bytes[4]; + + op = psa_hash_operation_init(); + status = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + mbedtls_lms_unsigned_int_to_network_bytes(r_node_idx, 4, r_node_idx_bytes); + status = psa_hash_update(&op, r_node_idx_bytes, 4); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, D_LEAF_CONSTANT_BYTES, D_CONST_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, pub_key, + MBEDTLS_LMOTS_N_HASH_LEN(params->otstype)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&op, out, MBEDTLS_LMS_M_NODE_BYTES(params->type), + &output_hash_len); + if (status != PSA_SUCCESS) { + goto exit; + } + +exit: + psa_hash_abort(&op); + + return PSA_TO_MBEDTLS_ERR(status); +} + +/* Calculate the value of an internal node of the Merkle tree (which is a hash + * of a public key and some other parameters like the node index). This function + * implements RFC8554 section 5.3, in the case where r < 2^h. + * + * params The LMS parameter set, the underlying LMOTS + * parameter set, and I value which describe the key + * being used. + * + * left_node The value of the child of this node which is on + * the left-hand side. As with all nodes on the + * Merkle tree, this is a hash output. + * + * right_node The value of the child of this node which is on + * the right-hand side. As with all nodes on the + * Merkle tree, this is a hash output. + * + * r_node_idx The index of this node in the Merkle tree. Note + * that the root node of the Merkle tree is + * 1-indexed. + * + * out The output node value, which is a hash output. + */ +static int create_merkle_internal_value(const mbedtls_lms_parameters_t *params, + const unsigned char *left_node, + const unsigned char *right_node, + unsigned int r_node_idx, + unsigned char *out) +{ + psa_hash_operation_t op; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_hash_len; + unsigned char r_node_idx_bytes[4]; + + op = psa_hash_operation_init(); + status = psa_hash_setup(&op, PSA_ALG_SHA_256); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, params->I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + mbedtls_lms_unsigned_int_to_network_bytes(r_node_idx, 4, r_node_idx_bytes); + status = psa_hash_update(&op, r_node_idx_bytes, 4); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, D_INTR_CONSTANT_BYTES, D_CONST_LEN); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, left_node, + MBEDTLS_LMS_M_NODE_BYTES(params->type)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&op, right_node, + MBEDTLS_LMS_M_NODE_BYTES(params->type)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&op, out, MBEDTLS_LMS_M_NODE_BYTES(params->type), + &output_hash_len); + if (status != PSA_SUCCESS) { + goto exit; + } + +exit: + psa_hash_abort(&op); + + return PSA_TO_MBEDTLS_ERR(status); +} + +void mbedtls_lms_public_init(mbedtls_lms_public_t *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +void mbedtls_lms_public_free(mbedtls_lms_public_t *ctx) +{ + mbedtls_platform_zeroize(ctx, sizeof(*ctx)); +} + +int mbedtls_lms_import_public_key(mbedtls_lms_public_t *ctx, + const unsigned char *key, size_t key_size) +{ + mbedtls_lms_algorithm_type_t type; + mbedtls_lmots_algorithm_type_t otstype; + + type = mbedtls_lms_network_bytes_to_unsigned_int(MBEDTLS_LMS_TYPE_LEN, + key + PUBLIC_KEY_TYPE_OFFSET); + if (type != MBEDTLS_LMS_SHA256_M32_H10) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + ctx->params.type = type; + + if (key_size != MBEDTLS_LMS_PUBLIC_KEY_LEN(ctx->params.type)) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + otstype = mbedtls_lms_network_bytes_to_unsigned_int(MBEDTLS_LMOTS_TYPE_LEN, + key + PUBLIC_KEY_OTSTYPE_OFFSET); + if (otstype != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + ctx->params.otstype = otstype; + + memcpy(ctx->params.I_key_identifier, + key + PUBLIC_KEY_I_KEY_ID_OFFSET, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + memcpy(ctx->T_1_pub_key, key + PUBLIC_KEY_ROOT_NODE_OFFSET, + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)); + + ctx->have_public_key = 1; + + return 0; +} + +int mbedtls_lms_export_public_key(const mbedtls_lms_public_t *ctx, + unsigned char *key, + size_t key_size, size_t *key_len) +{ + if (key_size < MBEDTLS_LMS_PUBLIC_KEY_LEN(ctx->params.type)) { + return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL; + } + + if (!ctx->have_public_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + mbedtls_lms_unsigned_int_to_network_bytes( + ctx->params.type, + MBEDTLS_LMS_TYPE_LEN, key + PUBLIC_KEY_TYPE_OFFSET); + mbedtls_lms_unsigned_int_to_network_bytes(ctx->params.otstype, + MBEDTLS_LMOTS_TYPE_LEN, + key + PUBLIC_KEY_OTSTYPE_OFFSET); + memcpy(key + PUBLIC_KEY_I_KEY_ID_OFFSET, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + memcpy(key +PUBLIC_KEY_ROOT_NODE_OFFSET, + ctx->T_1_pub_key, + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)); + + if (key_len != NULL) { + *key_len = MBEDTLS_LMS_PUBLIC_KEY_LEN(ctx->params.type); + } + + return 0; +} + +int mbedtls_lms_verify(const mbedtls_lms_public_t *ctx, + const unsigned char *msg, size_t msg_size, + const unsigned char *sig, size_t sig_size) +{ + unsigned int q_leaf_identifier; + unsigned char Kc_candidate_ots_pub_key[MBEDTLS_LMOTS_N_HASH_LEN_MAX]; + unsigned char Tc_candidate_root_node[MBEDTLS_LMS_M_NODE_BYTES_MAX]; + unsigned int height; + unsigned int curr_node_id; + unsigned int parent_node_id; + const unsigned char *left_node; + const unsigned char *right_node; + mbedtls_lmots_parameters_t ots_params; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (!ctx->have_public_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (ctx->params.type + != MBEDTLS_LMS_SHA256_M32_H10) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (ctx->params.otstype + != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (sig_size != MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype)) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + if (sig_size < SIG_OTS_SIG_OFFSET + MBEDTLS_LMOTS_TYPE_LEN) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + if (mbedtls_lms_network_bytes_to_unsigned_int(MBEDTLS_LMOTS_TYPE_LEN, + sig + SIG_OTS_SIG_OFFSET + + MBEDTLS_LMOTS_SIG_TYPE_OFFSET) + != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + if (sig_size < SIG_TYPE_OFFSET(ctx->params.otstype) + MBEDTLS_LMS_TYPE_LEN) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + if (mbedtls_lms_network_bytes_to_unsigned_int(MBEDTLS_LMS_TYPE_LEN, + sig + SIG_TYPE_OFFSET(ctx->params.otstype)) + != MBEDTLS_LMS_SHA256_M32_H10) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + + q_leaf_identifier = mbedtls_lms_network_bytes_to_unsigned_int( + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, sig + SIG_Q_LEAF_ID_OFFSET); + + if (q_leaf_identifier >= MERKLE_TREE_LEAF_NODE_AM(ctx->params.type)) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + memcpy(ots_params.I_key_identifier, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + mbedtls_lms_unsigned_int_to_network_bytes(q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, + ots_params.q_leaf_identifier); + ots_params.type = ctx->params.otstype; + + ret = mbedtls_lmots_calculate_public_key_candidate(&ots_params, + msg, + msg_size, + sig + SIG_OTS_SIG_OFFSET, + MBEDTLS_LMOTS_SIG_LEN(ctx->params.otstype), + Kc_candidate_ots_pub_key, + sizeof(Kc_candidate_ots_pub_key), + NULL); + if (ret != 0) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + create_merkle_leaf_value( + &ctx->params, + Kc_candidate_ots_pub_key, + MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + q_leaf_identifier, + Tc_candidate_root_node); + + curr_node_id = MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + + q_leaf_identifier; + + for (height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT(ctx->params.type); + height++) { + parent_node_id = curr_node_id / 2; + + /* Left/right node ordering matters for the hash */ + if (curr_node_id & 1) { + left_node = sig + SIG_PATH_OFFSET(ctx->params.otstype) + + height * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type); + right_node = Tc_candidate_root_node; + } else { + left_node = Tc_candidate_root_node; + right_node = sig + SIG_PATH_OFFSET(ctx->params.otstype) + + height * MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type); + } + + create_merkle_internal_value(&ctx->params, left_node, right_node, + parent_node_id, Tc_candidate_root_node); + + curr_node_id /= 2; + } + + if (memcmp(Tc_candidate_root_node, ctx->T_1_pub_key, + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type))) { + return MBEDTLS_ERR_LMS_VERIFY_FAILED; + } + + return 0; +} + +#if defined(MBEDTLS_LMS_PRIVATE) + +/* Calculate a full Merkle tree based on a private key. This function + * implements RFC8554 section 5.3, and is used to generate a public key (as the + * public key is the root node of the Merkle tree). + * + * ctx The LMS private context, containing a parameter + * set and private key material consisting of both + * public and private OTS. + * + * tree The output tree, which is 2^(H + 1) hash outputs. + * In the case of H=10 we have 2048 tree nodes (of + * which 1024 of them are leaf nodes). Note that + * because the Merkle tree root is 1-indexed, the 0 + * index tree node is never used. + */ +static int calculate_merkle_tree(const mbedtls_lms_private_t *ctx, + unsigned char *tree) +{ + unsigned int priv_key_idx; + unsigned int r_node_idx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* First create the leaf nodes, in ascending order */ + for (priv_key_idx = 0; + priv_key_idx < MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type); + priv_key_idx++) { + r_node_idx = MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + priv_key_idx; + + ret = create_merkle_leaf_value(&ctx->params, + ctx->ots_public_keys[priv_key_idx].public_key, + r_node_idx, + &tree[r_node_idx * MBEDTLS_LMS_M_NODE_BYTES( + ctx->params.type)]); + if (ret != 0) { + return ret; + } + } + + /* Then the internal nodes, in reverse order so that we can guarantee the + * parent has been created */ + for (r_node_idx = MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) - 1; + r_node_idx > 0; + r_node_idx--) { + ret = create_merkle_internal_value(&ctx->params, + &tree[(r_node_idx * 2) * + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)], + &tree[(r_node_idx * 2 + 1) * + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)], + r_node_idx, + &tree[r_node_idx * + MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type)]); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +/* Calculate a path from a leaf node of the Merkle tree to the root of the tree, + * and return the full path. This function implements RFC8554 section 5.4.1, as + * the Merkle path is the main component of an LMS signature. + * + * ctx The LMS private context, containing a parameter + * set and private key material consisting of both + * public and private OTS. + * + * leaf_node_id Which leaf node to calculate the path from. + * + * path The output path, which is H hash outputs. + */ +static int get_merkle_path(mbedtls_lms_private_t *ctx, + unsigned int leaf_node_id, + unsigned char *path) +{ + const size_t node_bytes = MBEDTLS_LMS_M_NODE_BYTES(ctx->params.type); + unsigned int curr_node_id = leaf_node_id; + unsigned int adjacent_node_id; + unsigned char *tree = NULL; + unsigned int height; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + tree = mbedtls_calloc(MERKLE_TREE_NODE_AM(ctx->params.type), + node_bytes); + if (tree == NULL) { + return MBEDTLS_ERR_LMS_ALLOC_FAILED; + } + + ret = calculate_merkle_tree(ctx, tree); + if (ret != 0) { + goto exit; + } + + for (height = 0; height < MBEDTLS_LMS_H_TREE_HEIGHT(ctx->params.type); + height++) { + adjacent_node_id = curr_node_id ^ 1; + + memcpy(&path[height * node_bytes], + &tree[adjacent_node_id * node_bytes], node_bytes); + + curr_node_id >>= 1; + } + + ret = 0; + +exit: + mbedtls_platform_zeroize(tree, node_bytes * + MERKLE_TREE_NODE_AM(ctx->params.type)); + mbedtls_free(tree); + + return ret; +} + +void mbedtls_lms_private_init(mbedtls_lms_private_t *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +void mbedtls_lms_private_free(mbedtls_lms_private_t *ctx) +{ + unsigned int idx; + + if (ctx->have_private_key) { + if (ctx->ots_private_keys != NULL) { + for (idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++) { + mbedtls_lmots_private_free(&ctx->ots_private_keys[idx]); + } + } + + if (ctx->ots_public_keys != NULL) { + for (idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++) { + mbedtls_lmots_public_free(&ctx->ots_public_keys[idx]); + } + } + + mbedtls_free(ctx->ots_private_keys); + mbedtls_free(ctx->ots_public_keys); + } + + mbedtls_platform_zeroize(ctx, sizeof(*ctx)); +} + + +int mbedtls_lms_generate_private_key(mbedtls_lms_private_t *ctx, + mbedtls_lms_algorithm_type_t type, + mbedtls_lmots_algorithm_type_t otstype, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *seed, + size_t seed_size) +{ + unsigned int idx = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (type != MBEDTLS_LMS_SHA256_M32_H10) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (otstype != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (ctx->have_private_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + ctx->params.type = type; + ctx->params.otstype = otstype; + ctx->have_private_key = 1; + + ret = f_rng(p_rng, + ctx->params.I_key_identifier, + MBEDTLS_LMOTS_I_KEY_ID_LEN); + if (ret != 0) { + goto exit; + } + + /* Requires a cast to size_t to avoid an implicit cast warning on certain + * platforms (particularly Windows) */ + ctx->ots_private_keys = mbedtls_calloc((size_t) MERKLE_TREE_LEAF_NODE_AM(ctx->params.type), + sizeof(*ctx->ots_private_keys)); + if (ctx->ots_private_keys == NULL) { + ret = MBEDTLS_ERR_LMS_ALLOC_FAILED; + goto exit; + } + + /* Requires a cast to size_t to avoid an implicit cast warning on certain + * platforms (particularly Windows) */ + ctx->ots_public_keys = mbedtls_calloc((size_t) MERKLE_TREE_LEAF_NODE_AM(ctx->params.type), + sizeof(*ctx->ots_public_keys)); + if (ctx->ots_public_keys == NULL) { + ret = MBEDTLS_ERR_LMS_ALLOC_FAILED; + goto exit; + } + + for (idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++) { + mbedtls_lmots_private_init(&ctx->ots_private_keys[idx]); + mbedtls_lmots_public_init(&ctx->ots_public_keys[idx]); + } + + + for (idx = 0; idx < MERKLE_TREE_LEAF_NODE_AM(ctx->params.type); idx++) { + ret = mbedtls_lmots_generate_private_key(&ctx->ots_private_keys[idx], + otstype, + ctx->params.I_key_identifier, + idx, seed, seed_size); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_lmots_calculate_public_key(&ctx->ots_public_keys[idx], + &ctx->ots_private_keys[idx]); + if (ret != 0) { + goto exit; + } + } + + ctx->q_next_usable_key = 0; + +exit: + if (ret != 0) { + mbedtls_lms_private_free(ctx); + } + + return ret; +} + +int mbedtls_lms_calculate_public_key(mbedtls_lms_public_t *ctx, + const mbedtls_lms_private_t *priv_ctx) +{ + const size_t node_bytes = MBEDTLS_LMS_M_NODE_BYTES(priv_ctx->params.type); + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *tree = NULL; + + if (!priv_ctx->have_private_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (priv_ctx->params.type + != MBEDTLS_LMS_SHA256_M32_H10) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (priv_ctx->params.otstype + != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + tree = mbedtls_calloc(MERKLE_TREE_NODE_AM(priv_ctx->params.type), + node_bytes); + if (tree == NULL) { + return MBEDTLS_ERR_LMS_ALLOC_FAILED; + } + + memcpy(&ctx->params, &priv_ctx->params, + sizeof(mbedtls_lmots_parameters_t)); + + ret = calculate_merkle_tree(priv_ctx, tree); + if (ret != 0) { + goto exit; + } + + /* Root node is always at position 1, due to 1-based indexing */ + memcpy(ctx->T_1_pub_key, &tree[node_bytes], node_bytes); + + ctx->have_public_key = 1; + + ret = 0; + +exit: + mbedtls_platform_zeroize(tree, node_bytes * + MERKLE_TREE_NODE_AM(priv_ctx->params.type)); + mbedtls_free(tree); + + return ret; +} + + +int mbedtls_lms_sign(mbedtls_lms_private_t *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, const unsigned char *msg, + unsigned int msg_size, unsigned char *sig, size_t sig_size, + size_t *sig_len) +{ + uint32_t q_leaf_identifier; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (!ctx->have_private_key) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (sig_size < MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype)) { + return MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL; + } + + if (ctx->params.type != MBEDTLS_LMS_SHA256_M32_H10) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (ctx->params.otstype + != MBEDTLS_LMOTS_SHA256_N32_W8) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + if (ctx->q_next_usable_key >= MERKLE_TREE_LEAF_NODE_AM(ctx->params.type)) { + return MBEDTLS_ERR_LMS_OUT_OF_PRIVATE_KEYS; + } + + + q_leaf_identifier = ctx->q_next_usable_key; + /* This new value must _always_ be written back to the disk before the + * signature is returned. + */ + ctx->q_next_usable_key += 1; + + if (MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype) + < SIG_OTS_SIG_OFFSET) { + return MBEDTLS_ERR_LMS_BAD_INPUT_DATA; + } + + ret = mbedtls_lmots_sign(&ctx->ots_private_keys[q_leaf_identifier], + f_rng, + p_rng, + msg, + msg_size, + sig + SIG_OTS_SIG_OFFSET, + MBEDTLS_LMS_SIG_LEN(ctx->params.type, + ctx->params.otstype) - SIG_OTS_SIG_OFFSET, + NULL); + if (ret != 0) { + return ret; + } + + mbedtls_lms_unsigned_int_to_network_bytes(ctx->params.type, + MBEDTLS_LMS_TYPE_LEN, + sig + SIG_TYPE_OFFSET(ctx->params.otstype)); + mbedtls_lms_unsigned_int_to_network_bytes(q_leaf_identifier, + MBEDTLS_LMOTS_Q_LEAF_ID_LEN, + sig + SIG_Q_LEAF_ID_OFFSET); + + ret = get_merkle_path(ctx, + MERKLE_TREE_INTERNAL_NODE_AM(ctx->params.type) + q_leaf_identifier, + sig + SIG_PATH_OFFSET(ctx->params.otstype)); + if (ret != 0) { + return ret; + } + + if (sig_len != NULL) { + *sig_len = MBEDTLS_LMS_SIG_LEN(ctx->params.type, ctx->params.otstype); + } + + + return 0; +} + +#endif /* defined(MBEDTLS_LMS_PRIVATE) */ +#endif /* defined(MBEDTLS_LMS_C) */ diff --git a/r5dev/thirdparty/mbedtls/md.c b/r5dev/thirdparty/mbedtls/md.c new file mode 100644 index 00000000..bebe3580 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/md.c @@ -0,0 +1,970 @@ +/** + * \file md.c + * + * \brief Generic message digest wrapper for mbed TLS + * + * \author Adriaan de Jong + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +/* + * Availability of functions in this module is controlled by two + * feature macros: + * - MBEDTLS_MD_C enables the whole module; + * - MBEDTLS_MD_LIGHT enables only functions for hashing and accessing + * most hash metadata (everything except string names); is it + * automatically set whenever MBEDTLS_MD_C is defined. + * + * In this file, functions from MD_LIGHT are at the top, MD_C at the end. + * + * In the future we may want to change the contract of some functions + * (behaviour with NULL arguments) depending on whether MD_C is defined or + * only MD_LIGHT. Also, the exact scope of MD_LIGHT might vary. + * + * For these reasons, we're keeping MD_LIGHT internal for now. + */ +#if defined(MBEDTLS_MD_LIGHT) + +#include "mbedtls/md.h" +#include "md_wrap.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include "mbedtls/md5.h" +#include "mbedtls/ripemd160.h" +#include "mbedtls/sha1.h" +#include "mbedtls/sha256.h" +#include "mbedtls/sha512.h" + +#if defined(MBEDTLS_MD_SOME_PSA) +#include +#include "psa_crypto_core.h" +#endif + +#include "mbedtls/platform.h" + +#include + +#if defined(MBEDTLS_FS_IO) +#include +#endif + +#if defined(MBEDTLS_MD_CAN_MD5) +const mbedtls_md_info_t mbedtls_md5_info = { + "MD5", + MBEDTLS_MD_MD5, + 16, + 64, +}; +#endif + +#if defined(MBEDTLS_MD_CAN_RIPEMD160) +const mbedtls_md_info_t mbedtls_ripemd160_info = { + "RIPEMD160", + MBEDTLS_MD_RIPEMD160, + 20, + 64, +}; +#endif + +#if defined(MBEDTLS_MD_CAN_SHA1) +const mbedtls_md_info_t mbedtls_sha1_info = { + "SHA1", + MBEDTLS_MD_SHA1, + 20, + 64, +}; +#endif + +#if defined(MBEDTLS_MD_CAN_SHA224) +const mbedtls_md_info_t mbedtls_sha224_info = { + "SHA224", + MBEDTLS_MD_SHA224, + 28, + 64, +}; +#endif + +#if defined(MBEDTLS_MD_CAN_SHA256) +const mbedtls_md_info_t mbedtls_sha256_info = { + "SHA256", + MBEDTLS_MD_SHA256, + 32, + 64, +}; +#endif + +#if defined(MBEDTLS_MD_CAN_SHA384) +const mbedtls_md_info_t mbedtls_sha384_info = { + "SHA384", + MBEDTLS_MD_SHA384, + 48, + 128, +}; +#endif + +#if defined(MBEDTLS_MD_CAN_SHA512) +const mbedtls_md_info_t mbedtls_sha512_info = { + "SHA512", + MBEDTLS_MD_SHA512, + 64, + 128, +}; +#endif + +const mbedtls_md_info_t *mbedtls_md_info_from_type(mbedtls_md_type_t md_type) +{ + switch (md_type) { +#if defined(MBEDTLS_MD_CAN_MD5) + case MBEDTLS_MD_MD5: + return &mbedtls_md5_info; +#endif +#if defined(MBEDTLS_MD_CAN_RIPEMD160) + case MBEDTLS_MD_RIPEMD160: + return &mbedtls_ripemd160_info; +#endif +#if defined(MBEDTLS_MD_CAN_SHA1) + case MBEDTLS_MD_SHA1: + return &mbedtls_sha1_info; +#endif +#if defined(MBEDTLS_MD_CAN_SHA224) + case MBEDTLS_MD_SHA224: + return &mbedtls_sha224_info; +#endif +#if defined(MBEDTLS_MD_CAN_SHA256) + case MBEDTLS_MD_SHA256: + return &mbedtls_sha256_info; +#endif +#if defined(MBEDTLS_MD_CAN_SHA384) + case MBEDTLS_MD_SHA384: + return &mbedtls_sha384_info; +#endif +#if defined(MBEDTLS_MD_CAN_SHA512) + case MBEDTLS_MD_SHA512: + return &mbedtls_sha512_info; +#endif + default: + return NULL; + } +} + +#if defined(MBEDTLS_MD_SOME_PSA) +static psa_algorithm_t psa_alg_of_md(const mbedtls_md_info_t *info) +{ + switch (info->type) { +#if defined(MBEDTLS_MD_MD5_VIA_PSA) + case MBEDTLS_MD_MD5: + return PSA_ALG_MD5; +#endif +#if defined(MBEDTLS_MD_RIPEMD160_VIA_PSA) + case MBEDTLS_MD_RIPEMD160: + return PSA_ALG_RIPEMD160; +#endif +#if defined(MBEDTLS_MD_SHA1_VIA_PSA) + case MBEDTLS_MD_SHA1: + return PSA_ALG_SHA_1; +#endif +#if defined(MBEDTLS_MD_SHA224_VIA_PSA) + case MBEDTLS_MD_SHA224: + return PSA_ALG_SHA_224; +#endif +#if defined(MBEDTLS_MD_SHA256_VIA_PSA) + case MBEDTLS_MD_SHA256: + return PSA_ALG_SHA_256; +#endif +#if defined(MBEDTLS_MD_SHA384_VIA_PSA) + case MBEDTLS_MD_SHA384: + return PSA_ALG_SHA_384; +#endif +#if defined(MBEDTLS_MD_SHA512_VIA_PSA) + case MBEDTLS_MD_SHA512: + return PSA_ALG_SHA_512; +#endif + default: + return PSA_ALG_NONE; + } +} + +static int md_can_use_psa(const mbedtls_md_info_t *info) +{ + psa_algorithm_t alg = psa_alg_of_md(info); + if (alg == PSA_ALG_NONE) { + return 0; + } + + return psa_can_do_hash(alg); +} + +static int mbedtls_md_error_from_psa(psa_status_t status) +{ + switch (status) { + case PSA_SUCCESS: + return 0; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_MD_ALLOC_FAILED; + default: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } +} +#endif /* MBEDTLS_MD_SOME_PSA */ + +void mbedtls_md_init(mbedtls_md_context_t *ctx) +{ + /* Note: this sets engine (if present) to MBEDTLS_MD_ENGINE_LEGACY */ + memset(ctx, 0, sizeof(mbedtls_md_context_t)); +} + +void mbedtls_md_free(mbedtls_md_context_t *ctx) +{ + if (ctx == NULL || ctx->md_info == NULL) { + return; + } + + if (ctx->md_ctx != NULL) { +#if defined(MBEDTLS_MD_SOME_PSA) + if (ctx->engine == MBEDTLS_MD_ENGINE_PSA) { + psa_hash_abort(ctx->md_ctx); + } else +#endif + switch (ctx->md_info->type) { +#if defined(MBEDTLS_MD5_C) + case MBEDTLS_MD_MD5: + mbedtls_md5_free(ctx->md_ctx); + break; +#endif +#if defined(MBEDTLS_RIPEMD160_C) + case MBEDTLS_MD_RIPEMD160: + mbedtls_ripemd160_free(ctx->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA1_C) + case MBEDTLS_MD_SHA1: + mbedtls_sha1_free(ctx->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA224_C) + case MBEDTLS_MD_SHA224: + mbedtls_sha256_free(ctx->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_SHA256: + mbedtls_sha256_free(ctx->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA384_C) + case MBEDTLS_MD_SHA384: + mbedtls_sha512_free(ctx->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA512: + mbedtls_sha512_free(ctx->md_ctx); + break; +#endif + default: + /* Shouldn't happen */ + break; + } + mbedtls_free(ctx->md_ctx); + } + +#if defined(MBEDTLS_MD_C) + if (ctx->hmac_ctx != NULL) { + mbedtls_platform_zeroize(ctx->hmac_ctx, + 2 * ctx->md_info->block_size); + mbedtls_free(ctx->hmac_ctx); + } +#endif + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_md_context_t)); +} + +int mbedtls_md_clone(mbedtls_md_context_t *dst, + const mbedtls_md_context_t *src) +{ + if (dst == NULL || dst->md_info == NULL || + src == NULL || src->md_info == NULL || + dst->md_info != src->md_info) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_MD_SOME_PSA) + if (src->engine != dst->engine) { + /* This can happen with src set to legacy because PSA wasn't ready + * yet, and dst to PSA because it became ready in the meantime. + * We currently don't support that case (we'd need to re-allocate + * md_ctx to the size of the appropriate MD context). */ + return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; + } + + if (src->engine == MBEDTLS_MD_ENGINE_PSA) { + psa_status_t status = psa_hash_clone(src->md_ctx, dst->md_ctx); + return mbedtls_md_error_from_psa(status); + } +#endif + + switch (src->md_info->type) { +#if defined(MBEDTLS_MD5_C) + case MBEDTLS_MD_MD5: + mbedtls_md5_clone(dst->md_ctx, src->md_ctx); + break; +#endif +#if defined(MBEDTLS_RIPEMD160_C) + case MBEDTLS_MD_RIPEMD160: + mbedtls_ripemd160_clone(dst->md_ctx, src->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA1_C) + case MBEDTLS_MD_SHA1: + mbedtls_sha1_clone(dst->md_ctx, src->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA224_C) + case MBEDTLS_MD_SHA224: + mbedtls_sha256_clone(dst->md_ctx, src->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_SHA256: + mbedtls_sha256_clone(dst->md_ctx, src->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA384_C) + case MBEDTLS_MD_SHA384: + mbedtls_sha512_clone(dst->md_ctx, src->md_ctx); + break; +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA512: + mbedtls_sha512_clone(dst->md_ctx, src->md_ctx); + break; +#endif + default: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + return 0; +} + +#define ALLOC(type) \ + do { \ + ctx->md_ctx = mbedtls_calloc(1, sizeof(mbedtls_##type##_context)); \ + if (ctx->md_ctx == NULL) \ + return MBEDTLS_ERR_MD_ALLOC_FAILED; \ + mbedtls_##type##_init(ctx->md_ctx); \ + } \ + while (0) + +int mbedtls_md_setup(mbedtls_md_context_t *ctx, const mbedtls_md_info_t *md_info, int hmac) +{ + if (md_info == NULL || ctx == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + ctx->md_info = md_info; + ctx->md_ctx = NULL; +#if defined(MBEDTLS_MD_C) + ctx->hmac_ctx = NULL; +#else + if (hmac != 0) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } +#endif + +#if defined(MBEDTLS_MD_SOME_PSA) + if (md_can_use_psa(ctx->md_info)) { + ctx->md_ctx = mbedtls_calloc(1, sizeof(psa_hash_operation_t)); + if (ctx->md_ctx == NULL) { + return MBEDTLS_ERR_MD_ALLOC_FAILED; + } + ctx->engine = MBEDTLS_MD_ENGINE_PSA; + } else +#endif + switch (md_info->type) { +#if defined(MBEDTLS_MD5_C) + case MBEDTLS_MD_MD5: + ALLOC(md5); + break; +#endif +#if defined(MBEDTLS_RIPEMD160_C) + case MBEDTLS_MD_RIPEMD160: + ALLOC(ripemd160); + break; +#endif +#if defined(MBEDTLS_SHA1_C) + case MBEDTLS_MD_SHA1: + ALLOC(sha1); + break; +#endif +#if defined(MBEDTLS_SHA224_C) + case MBEDTLS_MD_SHA224: + ALLOC(sha256); + break; +#endif +#if defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_SHA256: + ALLOC(sha256); + break; +#endif +#if defined(MBEDTLS_SHA384_C) + case MBEDTLS_MD_SHA384: + ALLOC(sha512); + break; +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA512: + ALLOC(sha512); + break; +#endif + default: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_MD_C) + if (hmac != 0) { + ctx->hmac_ctx = mbedtls_calloc(2, md_info->block_size); + if (ctx->hmac_ctx == NULL) { + mbedtls_md_free(ctx); + return MBEDTLS_ERR_MD_ALLOC_FAILED; + } + } +#endif + + return 0; +} +#undef ALLOC + +int mbedtls_md_starts(mbedtls_md_context_t *ctx) +{ + if (ctx == NULL || ctx->md_info == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_MD_SOME_PSA) + if (ctx->engine == MBEDTLS_MD_ENGINE_PSA) { + psa_algorithm_t alg = psa_alg_of_md(ctx->md_info); + psa_hash_abort(ctx->md_ctx); + psa_status_t status = psa_hash_setup(ctx->md_ctx, alg); + return mbedtls_md_error_from_psa(status); + } +#endif + + switch (ctx->md_info->type) { +#if defined(MBEDTLS_MD5_C) + case MBEDTLS_MD_MD5: + return mbedtls_md5_starts(ctx->md_ctx); +#endif +#if defined(MBEDTLS_RIPEMD160_C) + case MBEDTLS_MD_RIPEMD160: + return mbedtls_ripemd160_starts(ctx->md_ctx); +#endif +#if defined(MBEDTLS_SHA1_C) + case MBEDTLS_MD_SHA1: + return mbedtls_sha1_starts(ctx->md_ctx); +#endif +#if defined(MBEDTLS_SHA224_C) + case MBEDTLS_MD_SHA224: + return mbedtls_sha256_starts(ctx->md_ctx, 1); +#endif +#if defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_SHA256: + return mbedtls_sha256_starts(ctx->md_ctx, 0); +#endif +#if defined(MBEDTLS_SHA384_C) + case MBEDTLS_MD_SHA384: + return mbedtls_sha512_starts(ctx->md_ctx, 1); +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA512: + return mbedtls_sha512_starts(ctx->md_ctx, 0); +#endif + default: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } +} + +int mbedtls_md_update(mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen) +{ + if (ctx == NULL || ctx->md_info == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_MD_SOME_PSA) + if (ctx->engine == MBEDTLS_MD_ENGINE_PSA) { + psa_status_t status = psa_hash_update(ctx->md_ctx, input, ilen); + return mbedtls_md_error_from_psa(status); + } +#endif + + switch (ctx->md_info->type) { +#if defined(MBEDTLS_MD5_C) + case MBEDTLS_MD_MD5: + return mbedtls_md5_update(ctx->md_ctx, input, ilen); +#endif +#if defined(MBEDTLS_RIPEMD160_C) + case MBEDTLS_MD_RIPEMD160: + return mbedtls_ripemd160_update(ctx->md_ctx, input, ilen); +#endif +#if defined(MBEDTLS_SHA1_C) + case MBEDTLS_MD_SHA1: + return mbedtls_sha1_update(ctx->md_ctx, input, ilen); +#endif +#if defined(MBEDTLS_SHA224_C) + case MBEDTLS_MD_SHA224: + return mbedtls_sha256_update(ctx->md_ctx, input, ilen); +#endif +#if defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_SHA256: + return mbedtls_sha256_update(ctx->md_ctx, input, ilen); +#endif +#if defined(MBEDTLS_SHA384_C) + case MBEDTLS_MD_SHA384: + return mbedtls_sha512_update(ctx->md_ctx, input, ilen); +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA512: + return mbedtls_sha512_update(ctx->md_ctx, input, ilen); +#endif + default: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } +} + +int mbedtls_md_finish(mbedtls_md_context_t *ctx, unsigned char *output) +{ + if (ctx == NULL || ctx->md_info == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_MD_SOME_PSA) + if (ctx->engine == MBEDTLS_MD_ENGINE_PSA) { + size_t size = ctx->md_info->size; + psa_status_t status = psa_hash_finish(ctx->md_ctx, + output, size, &size); + return mbedtls_md_error_from_psa(status); + } +#endif + + switch (ctx->md_info->type) { +#if defined(MBEDTLS_MD5_C) + case MBEDTLS_MD_MD5: + return mbedtls_md5_finish(ctx->md_ctx, output); +#endif +#if defined(MBEDTLS_RIPEMD160_C) + case MBEDTLS_MD_RIPEMD160: + return mbedtls_ripemd160_finish(ctx->md_ctx, output); +#endif +#if defined(MBEDTLS_SHA1_C) + case MBEDTLS_MD_SHA1: + return mbedtls_sha1_finish(ctx->md_ctx, output); +#endif +#if defined(MBEDTLS_SHA224_C) + case MBEDTLS_MD_SHA224: + return mbedtls_sha256_finish(ctx->md_ctx, output); +#endif +#if defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_SHA256: + return mbedtls_sha256_finish(ctx->md_ctx, output); +#endif +#if defined(MBEDTLS_SHA384_C) + case MBEDTLS_MD_SHA384: + return mbedtls_sha512_finish(ctx->md_ctx, output); +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA512: + return mbedtls_sha512_finish(ctx->md_ctx, output); +#endif + default: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } +} + +int mbedtls_md(const mbedtls_md_info_t *md_info, const unsigned char *input, size_t ilen, + unsigned char *output) +{ + if (md_info == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_MD_SOME_PSA) + if (md_can_use_psa(md_info)) { + size_t size = md_info->size; + psa_status_t status = psa_hash_compute(psa_alg_of_md(md_info), + input, ilen, + output, size, &size); + return mbedtls_md_error_from_psa(status); + } +#endif + + switch (md_info->type) { +#if defined(MBEDTLS_MD5_C) + case MBEDTLS_MD_MD5: + return mbedtls_md5(input, ilen, output); +#endif +#if defined(MBEDTLS_RIPEMD160_C) + case MBEDTLS_MD_RIPEMD160: + return mbedtls_ripemd160(input, ilen, output); +#endif +#if defined(MBEDTLS_SHA1_C) + case MBEDTLS_MD_SHA1: + return mbedtls_sha1(input, ilen, output); +#endif +#if defined(MBEDTLS_SHA224_C) + case MBEDTLS_MD_SHA224: + return mbedtls_sha256(input, ilen, output, 1); +#endif +#if defined(MBEDTLS_SHA256_C) + case MBEDTLS_MD_SHA256: + return mbedtls_sha256(input, ilen, output, 0); +#endif +#if defined(MBEDTLS_SHA384_C) + case MBEDTLS_MD_SHA384: + return mbedtls_sha512(input, ilen, output, 1); +#endif +#if defined(MBEDTLS_SHA512_C) + case MBEDTLS_MD_SHA512: + return mbedtls_sha512(input, ilen, output, 0); +#endif + default: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } +} + +unsigned char mbedtls_md_get_size(const mbedtls_md_info_t *md_info) +{ + if (md_info == NULL) { + return 0; + } + + return md_info->size; +} + +mbedtls_md_type_t mbedtls_md_get_type(const mbedtls_md_info_t *md_info) +{ + if (md_info == NULL) { + return MBEDTLS_MD_NONE; + } + + return md_info->type; +} + +/************************************************************************ + * Functions above this separator are part of MBEDTLS_MD_LIGHT, * + * functions below are only available when MBEDTLS_MD_C is set. * + ************************************************************************/ +#if defined(MBEDTLS_MD_C) + +/* + * Reminder: update profiles in x509_crt.c when adding a new hash! + */ +static const int supported_digests[] = { + +#if defined(MBEDTLS_MD_CAN_SHA512) + MBEDTLS_MD_SHA512, +#endif + +#if defined(MBEDTLS_MD_CAN_SHA384) + MBEDTLS_MD_SHA384, +#endif + +#if defined(MBEDTLS_MD_CAN_SHA256) + MBEDTLS_MD_SHA256, +#endif +#if defined(MBEDTLS_MD_CAN_SHA224) + MBEDTLS_MD_SHA224, +#endif + +#if defined(MBEDTLS_MD_CAN_SHA1) + MBEDTLS_MD_SHA1, +#endif + +#if defined(MBEDTLS_MD_CAN_RIPEMD160) + MBEDTLS_MD_RIPEMD160, +#endif + +#if defined(MBEDTLS_MD_CAN_MD5) + MBEDTLS_MD_MD5, +#endif + + MBEDTLS_MD_NONE +}; + +const int *mbedtls_md_list(void) +{ + return supported_digests; +} + +const mbedtls_md_info_t *mbedtls_md_info_from_string(const char *md_name) +{ + if (NULL == md_name) { + return NULL; + } + + /* Get the appropriate digest information */ +#if defined(MBEDTLS_MD_CAN_MD5) + if (!strcmp("MD5", md_name)) { + return mbedtls_md_info_from_type(MBEDTLS_MD_MD5); + } +#endif +#if defined(MBEDTLS_MD_CAN_RIPEMD160) + if (!strcmp("RIPEMD160", md_name)) { + return mbedtls_md_info_from_type(MBEDTLS_MD_RIPEMD160); + } +#endif +#if defined(MBEDTLS_MD_CAN_SHA1) + if (!strcmp("SHA1", md_name) || !strcmp("SHA", md_name)) { + return mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); + } +#endif +#if defined(MBEDTLS_MD_CAN_SHA224) + if (!strcmp("SHA224", md_name)) { + return mbedtls_md_info_from_type(MBEDTLS_MD_SHA224); + } +#endif +#if defined(MBEDTLS_MD_CAN_SHA256) + if (!strcmp("SHA256", md_name)) { + return mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + } +#endif +#if defined(MBEDTLS_MD_CAN_SHA384) + if (!strcmp("SHA384", md_name)) { + return mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); + } +#endif +#if defined(MBEDTLS_MD_CAN_SHA512) + if (!strcmp("SHA512", md_name)) { + return mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); + } +#endif + return NULL; +} + +const mbedtls_md_info_t *mbedtls_md_info_from_ctx( + const mbedtls_md_context_t *ctx) +{ + if (ctx == NULL) { + return NULL; + } + + return ctx->MBEDTLS_PRIVATE(md_info); +} + +#if defined(MBEDTLS_FS_IO) +int mbedtls_md_file(const mbedtls_md_info_t *md_info, const char *path, unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + FILE *f; + size_t n; + mbedtls_md_context_t ctx; + unsigned char buf[1024]; + + if (md_info == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + if ((f = fopen(path, "rb")) == NULL) { + return MBEDTLS_ERR_MD_FILE_IO_ERROR; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + mbedtls_md_init(&ctx); + + if ((ret = mbedtls_md_setup(&ctx, md_info, 0)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_md_starts(&ctx)) != 0) { + goto cleanup; + } + + while ((n = fread(buf, 1, sizeof(buf), f)) > 0) { + if ((ret = mbedtls_md_update(&ctx, buf, n)) != 0) { + goto cleanup; + } + } + + if (ferror(f) != 0) { + ret = MBEDTLS_ERR_MD_FILE_IO_ERROR; + } else { + ret = mbedtls_md_finish(&ctx, output); + } + +cleanup: + mbedtls_platform_zeroize(buf, sizeof(buf)); + fclose(f); + mbedtls_md_free(&ctx); + + return ret; +} +#endif /* MBEDTLS_FS_IO */ + +int mbedtls_md_hmac_starts(mbedtls_md_context_t *ctx, const unsigned char *key, size_t keylen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char sum[MBEDTLS_MD_MAX_SIZE]; + unsigned char *ipad, *opad; + + if (ctx == NULL || ctx->md_info == NULL || ctx->hmac_ctx == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + if (keylen > (size_t) ctx->md_info->block_size) { + if ((ret = mbedtls_md_starts(ctx)) != 0) { + goto cleanup; + } + if ((ret = mbedtls_md_update(ctx, key, keylen)) != 0) { + goto cleanup; + } + if ((ret = mbedtls_md_finish(ctx, sum)) != 0) { + goto cleanup; + } + + keylen = ctx->md_info->size; + key = sum; + } + + ipad = (unsigned char *) ctx->hmac_ctx; + opad = (unsigned char *) ctx->hmac_ctx + ctx->md_info->block_size; + + memset(ipad, 0x36, ctx->md_info->block_size); + memset(opad, 0x5C, ctx->md_info->block_size); + + mbedtls_xor(ipad, ipad, key, keylen); + mbedtls_xor(opad, opad, key, keylen); + + if ((ret = mbedtls_md_starts(ctx)) != 0) { + goto cleanup; + } + if ((ret = mbedtls_md_update(ctx, ipad, + ctx->md_info->block_size)) != 0) { + goto cleanup; + } + +cleanup: + mbedtls_platform_zeroize(sum, sizeof(sum)); + + return ret; +} + +int mbedtls_md_hmac_update(mbedtls_md_context_t *ctx, const unsigned char *input, size_t ilen) +{ + if (ctx == NULL || ctx->md_info == NULL || ctx->hmac_ctx == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + return mbedtls_md_update(ctx, input, ilen); +} + +int mbedtls_md_hmac_finish(mbedtls_md_context_t *ctx, unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char tmp[MBEDTLS_MD_MAX_SIZE]; + unsigned char *opad; + + if (ctx == NULL || ctx->md_info == NULL || ctx->hmac_ctx == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + opad = (unsigned char *) ctx->hmac_ctx + ctx->md_info->block_size; + + if ((ret = mbedtls_md_finish(ctx, tmp)) != 0) { + return ret; + } + if ((ret = mbedtls_md_starts(ctx)) != 0) { + return ret; + } + if ((ret = mbedtls_md_update(ctx, opad, + ctx->md_info->block_size)) != 0) { + return ret; + } + if ((ret = mbedtls_md_update(ctx, tmp, + ctx->md_info->size)) != 0) { + return ret; + } + return mbedtls_md_finish(ctx, output); +} + +int mbedtls_md_hmac_reset(mbedtls_md_context_t *ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *ipad; + + if (ctx == NULL || ctx->md_info == NULL || ctx->hmac_ctx == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + ipad = (unsigned char *) ctx->hmac_ctx; + + if ((ret = mbedtls_md_starts(ctx)) != 0) { + return ret; + } + return mbedtls_md_update(ctx, ipad, ctx->md_info->block_size); +} + +int mbedtls_md_hmac(const mbedtls_md_info_t *md_info, + const unsigned char *key, size_t keylen, + const unsigned char *input, size_t ilen, + unsigned char *output) +{ + mbedtls_md_context_t ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (md_info == NULL) { + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + } + + mbedtls_md_init(&ctx); + + if ((ret = mbedtls_md_setup(&ctx, md_info, 1)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_md_hmac_starts(&ctx, key, keylen)) != 0) { + goto cleanup; + } + if ((ret = mbedtls_md_hmac_update(&ctx, input, ilen)) != 0) { + goto cleanup; + } + if ((ret = mbedtls_md_hmac_finish(&ctx, output)) != 0) { + goto cleanup; + } + +cleanup: + mbedtls_md_free(&ctx); + + return ret; +} + +const char *mbedtls_md_get_name(const mbedtls_md_info_t *md_info) +{ + if (md_info == NULL) { + return NULL; + } + + return md_info->name; +} + +#endif /* MBEDTLS_MD_C */ + +#endif /* MBEDTLS_MD_LIGHT */ diff --git a/r5dev/thirdparty/mbedtls/md5.c b/r5dev/thirdparty/mbedtls/md5.c new file mode 100644 index 00000000..138a3205 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/md5.c @@ -0,0 +1,434 @@ +/* + * RFC 1321 compliant MD5 implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The MD5 algorithm was designed by Ron Rivest in 1991. + * + * http://www.ietf.org/rfc/rfc1321.txt + */ + +#include "common.h" + +#if defined(MBEDTLS_MD5_C) + +#include "mbedtls/md5.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_MD5_ALT) + +void mbedtls_md5_init(mbedtls_md5_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_md5_context)); +} + +void mbedtls_md5_free(mbedtls_md5_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_md5_context)); +} + +void mbedtls_md5_clone(mbedtls_md5_context *dst, + const mbedtls_md5_context *src) +{ + *dst = *src; +} + +/* + * MD5 context setup + */ +int mbedtls_md5_starts(mbedtls_md5_context *ctx) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + + return 0; +} + +#if !defined(MBEDTLS_MD5_PROCESS_ALT) +int mbedtls_internal_md5_process(mbedtls_md5_context *ctx, + const unsigned char data[64]) +{ + struct { + uint32_t X[16], A, B, C, D; + } local; + + local.X[0] = MBEDTLS_GET_UINT32_LE(data, 0); + local.X[1] = MBEDTLS_GET_UINT32_LE(data, 4); + local.X[2] = MBEDTLS_GET_UINT32_LE(data, 8); + local.X[3] = MBEDTLS_GET_UINT32_LE(data, 12); + local.X[4] = MBEDTLS_GET_UINT32_LE(data, 16); + local.X[5] = MBEDTLS_GET_UINT32_LE(data, 20); + local.X[6] = MBEDTLS_GET_UINT32_LE(data, 24); + local.X[7] = MBEDTLS_GET_UINT32_LE(data, 28); + local.X[8] = MBEDTLS_GET_UINT32_LE(data, 32); + local.X[9] = MBEDTLS_GET_UINT32_LE(data, 36); + local.X[10] = MBEDTLS_GET_UINT32_LE(data, 40); + local.X[11] = MBEDTLS_GET_UINT32_LE(data, 44); + local.X[12] = MBEDTLS_GET_UINT32_LE(data, 48); + local.X[13] = MBEDTLS_GET_UINT32_LE(data, 52); + local.X[14] = MBEDTLS_GET_UINT32_LE(data, 56); + local.X[15] = MBEDTLS_GET_UINT32_LE(data, 60); + +#define S(x, n) \ + (((x) << (n)) | (((x) & 0xFFFFFFFF) >> (32 - (n)))) + +#define P(a, b, c, d, k, s, t) \ + do \ + { \ + (a) += F((b), (c), (d)) + local.X[(k)] + (t); \ + (a) = S((a), (s)) + (b); \ + } while (0) + + local.A = ctx->state[0]; + local.B = ctx->state[1]; + local.C = ctx->state[2]; + local.D = ctx->state[3]; + +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) + + P(local.A, local.B, local.C, local.D, 0, 7, 0xD76AA478); + P(local.D, local.A, local.B, local.C, 1, 12, 0xE8C7B756); + P(local.C, local.D, local.A, local.B, 2, 17, 0x242070DB); + P(local.B, local.C, local.D, local.A, 3, 22, 0xC1BDCEEE); + P(local.A, local.B, local.C, local.D, 4, 7, 0xF57C0FAF); + P(local.D, local.A, local.B, local.C, 5, 12, 0x4787C62A); + P(local.C, local.D, local.A, local.B, 6, 17, 0xA8304613); + P(local.B, local.C, local.D, local.A, 7, 22, 0xFD469501); + P(local.A, local.B, local.C, local.D, 8, 7, 0x698098D8); + P(local.D, local.A, local.B, local.C, 9, 12, 0x8B44F7AF); + P(local.C, local.D, local.A, local.B, 10, 17, 0xFFFF5BB1); + P(local.B, local.C, local.D, local.A, 11, 22, 0x895CD7BE); + P(local.A, local.B, local.C, local.D, 12, 7, 0x6B901122); + P(local.D, local.A, local.B, local.C, 13, 12, 0xFD987193); + P(local.C, local.D, local.A, local.B, 14, 17, 0xA679438E); + P(local.B, local.C, local.D, local.A, 15, 22, 0x49B40821); + +#undef F + +#define F(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) + + P(local.A, local.B, local.C, local.D, 1, 5, 0xF61E2562); + P(local.D, local.A, local.B, local.C, 6, 9, 0xC040B340); + P(local.C, local.D, local.A, local.B, 11, 14, 0x265E5A51); + P(local.B, local.C, local.D, local.A, 0, 20, 0xE9B6C7AA); + P(local.A, local.B, local.C, local.D, 5, 5, 0xD62F105D); + P(local.D, local.A, local.B, local.C, 10, 9, 0x02441453); + P(local.C, local.D, local.A, local.B, 15, 14, 0xD8A1E681); + P(local.B, local.C, local.D, local.A, 4, 20, 0xE7D3FBC8); + P(local.A, local.B, local.C, local.D, 9, 5, 0x21E1CDE6); + P(local.D, local.A, local.B, local.C, 14, 9, 0xC33707D6); + P(local.C, local.D, local.A, local.B, 3, 14, 0xF4D50D87); + P(local.B, local.C, local.D, local.A, 8, 20, 0x455A14ED); + P(local.A, local.B, local.C, local.D, 13, 5, 0xA9E3E905); + P(local.D, local.A, local.B, local.C, 2, 9, 0xFCEFA3F8); + P(local.C, local.D, local.A, local.B, 7, 14, 0x676F02D9); + P(local.B, local.C, local.D, local.A, 12, 20, 0x8D2A4C8A); + +#undef F + +#define F(x, y, z) ((x) ^ (y) ^ (z)) + + P(local.A, local.B, local.C, local.D, 5, 4, 0xFFFA3942); + P(local.D, local.A, local.B, local.C, 8, 11, 0x8771F681); + P(local.C, local.D, local.A, local.B, 11, 16, 0x6D9D6122); + P(local.B, local.C, local.D, local.A, 14, 23, 0xFDE5380C); + P(local.A, local.B, local.C, local.D, 1, 4, 0xA4BEEA44); + P(local.D, local.A, local.B, local.C, 4, 11, 0x4BDECFA9); + P(local.C, local.D, local.A, local.B, 7, 16, 0xF6BB4B60); + P(local.B, local.C, local.D, local.A, 10, 23, 0xBEBFBC70); + P(local.A, local.B, local.C, local.D, 13, 4, 0x289B7EC6); + P(local.D, local.A, local.B, local.C, 0, 11, 0xEAA127FA); + P(local.C, local.D, local.A, local.B, 3, 16, 0xD4EF3085); + P(local.B, local.C, local.D, local.A, 6, 23, 0x04881D05); + P(local.A, local.B, local.C, local.D, 9, 4, 0xD9D4D039); + P(local.D, local.A, local.B, local.C, 12, 11, 0xE6DB99E5); + P(local.C, local.D, local.A, local.B, 15, 16, 0x1FA27CF8); + P(local.B, local.C, local.D, local.A, 2, 23, 0xC4AC5665); + +#undef F + +#define F(x, y, z) ((y) ^ ((x) | ~(z))) + + P(local.A, local.B, local.C, local.D, 0, 6, 0xF4292244); + P(local.D, local.A, local.B, local.C, 7, 10, 0x432AFF97); + P(local.C, local.D, local.A, local.B, 14, 15, 0xAB9423A7); + P(local.B, local.C, local.D, local.A, 5, 21, 0xFC93A039); + P(local.A, local.B, local.C, local.D, 12, 6, 0x655B59C3); + P(local.D, local.A, local.B, local.C, 3, 10, 0x8F0CCC92); + P(local.C, local.D, local.A, local.B, 10, 15, 0xFFEFF47D); + P(local.B, local.C, local.D, local.A, 1, 21, 0x85845DD1); + P(local.A, local.B, local.C, local.D, 8, 6, 0x6FA87E4F); + P(local.D, local.A, local.B, local.C, 15, 10, 0xFE2CE6E0); + P(local.C, local.D, local.A, local.B, 6, 15, 0xA3014314); + P(local.B, local.C, local.D, local.A, 13, 21, 0x4E0811A1); + P(local.A, local.B, local.C, local.D, 4, 6, 0xF7537E82); + P(local.D, local.A, local.B, local.C, 11, 10, 0xBD3AF235); + P(local.C, local.D, local.A, local.B, 2, 15, 0x2AD7D2BB); + P(local.B, local.C, local.D, local.A, 9, 21, 0xEB86D391); + +#undef F + + ctx->state[0] += local.A; + ctx->state[1] += local.B; + ctx->state[2] += local.C; + ctx->state[3] += local.D; + + /* Zeroise variables to clear sensitive data from memory. */ + mbedtls_platform_zeroize(&local, sizeof(local)); + + return 0; +} + +#endif /* !MBEDTLS_MD5_PROCESS_ALT */ + +/* + * MD5 process buffer + */ +int mbedtls_md5_update(mbedtls_md5_context *ctx, + const unsigned char *input, + size_t ilen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t fill; + uint32_t left; + + if (ilen == 0) { + return 0; + } + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if (ctx->total[0] < (uint32_t) ilen) { + ctx->total[1]++; + } + + if (left && ilen >= fill) { + memcpy((void *) (ctx->buffer + left), input, fill); + if ((ret = mbedtls_internal_md5_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= 64) { + if ((ret = mbedtls_internal_md5_process(ctx, input)) != 0) { + return ret; + } + + input += 64; + ilen -= 64; + } + + if (ilen > 0) { + memcpy((void *) (ctx->buffer + left), input, ilen); + } + + return 0; +} + +/* + * MD5 final digest + */ +int mbedtls_md5_finish(mbedtls_md5_context *ctx, + unsigned char output[16]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + uint32_t used; + uint32_t high, low; + + /* + * Add padding: 0x80 then 0x00 until 8 bytes remain for the length + */ + used = ctx->total[0] & 0x3F; + + ctx->buffer[used++] = 0x80; + + if (used <= 56) { + /* Enough room for padding + length in current block */ + memset(ctx->buffer + used, 0, 56 - used); + } else { + /* We'll need an extra block */ + memset(ctx->buffer + used, 0, 64 - used); + + if ((ret = mbedtls_internal_md5_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + memset(ctx->buffer, 0, 56); + } + + /* + * Add message length + */ + high = (ctx->total[0] >> 29) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + MBEDTLS_PUT_UINT32_LE(low, ctx->buffer, 56); + MBEDTLS_PUT_UINT32_LE(high, ctx->buffer, 60); + + if ((ret = mbedtls_internal_md5_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + /* + * Output final state + */ + MBEDTLS_PUT_UINT32_LE(ctx->state[0], output, 0); + MBEDTLS_PUT_UINT32_LE(ctx->state[1], output, 4); + MBEDTLS_PUT_UINT32_LE(ctx->state[2], output, 8); + MBEDTLS_PUT_UINT32_LE(ctx->state[3], output, 12); + + return 0; +} + +#endif /* !MBEDTLS_MD5_ALT */ + +/* + * output = MD5( input buffer ) + */ +int mbedtls_md5(const unsigned char *input, + size_t ilen, + unsigned char output[16]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md5_context ctx; + + mbedtls_md5_init(&ctx); + + if ((ret = mbedtls_md5_starts(&ctx)) != 0) { + goto exit; + } + + if ((ret = mbedtls_md5_update(&ctx, input, ilen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_md5_finish(&ctx, output)) != 0) { + goto exit; + } + +exit: + mbedtls_md5_free(&ctx); + + return ret; +} + +#if defined(MBEDTLS_SELF_TEST) +/* + * RFC 1321 test vectors + */ +static const unsigned char md5_test_buf[7][81] = +{ + { "" }, + { "a" }, + { "abc" }, + { "message digest" }, + { "abcdefghijklmnopqrstuvwxyz" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, + { "12345678901234567890123456789012345678901234567890123456789012345678901234567890" } +}; + +static const size_t md5_test_buflen[7] = +{ + 0, 1, 3, 14, 26, 62, 80 +}; + +static const unsigned char md5_test_sum[7][16] = +{ + { 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, + 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E }, + { 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8, + 0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 }, + { 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0, + 0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 }, + { 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D, + 0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 }, + { 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00, + 0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B }, + { 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5, + 0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F }, + { 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55, + 0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A } +}; + +/* + * Checkup routine + */ +int mbedtls_md5_self_test(int verbose) +{ + int i, ret = 0; + unsigned char md5sum[16]; + + for (i = 0; i < 7; i++) { + if (verbose != 0) { + mbedtls_printf(" MD5 test #%d: ", i + 1); + } + + ret = mbedtls_md5(md5_test_buf[i], md5_test_buflen[i], md5sum); + if (ret != 0) { + goto fail; + } + + if (memcmp(md5sum, md5_test_sum[i], 16) != 0) { + ret = 1; + goto fail; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; + +fail: + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_MD5_C */ diff --git a/r5dev/thirdparty/mbedtls/md_wrap.h b/r5dev/thirdparty/mbedtls/md_wrap.h new file mode 100644 index 00000000..cd539b54 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/md_wrap.h @@ -0,0 +1,81 @@ +/** + * \file md_wrap.h + * + * \brief Message digest wrappers. + * + * \warning This in an internal header. Do not include directly. + * + * \author Adriaan de Jong + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_MD_WRAP_H +#define MBEDTLS_MD_WRAP_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Message digest information. + * Allows message digest functions to be called in a generic way. + */ +struct mbedtls_md_info_t { + /** Name of the message digest */ + const char *name; + + /** Digest identifier */ + mbedtls_md_type_t type; + + /** Output length of the digest function in bytes */ + unsigned char size; + + /** Block length of the digest function in bytes */ + unsigned char block_size; +}; + +#if defined(MBEDTLS_MD5_C) +extern const mbedtls_md_info_t mbedtls_md5_info; +#endif +#if defined(MBEDTLS_RIPEMD160_C) +extern const mbedtls_md_info_t mbedtls_ripemd160_info; +#endif +#if defined(MBEDTLS_SHA1_C) +extern const mbedtls_md_info_t mbedtls_sha1_info; +#endif +#if defined(MBEDTLS_SHA224_C) +extern const mbedtls_md_info_t mbedtls_sha224_info; +#endif +#if defined(MBEDTLS_SHA256_C) +extern const mbedtls_md_info_t mbedtls_sha256_info; +#endif +#if defined(MBEDTLS_SHA384_C) +extern const mbedtls_md_info_t mbedtls_sha384_info; +#endif +#if defined(MBEDTLS_SHA512_C) +extern const mbedtls_md_info_t mbedtls_sha512_info; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* MBEDTLS_MD_WRAP_H */ diff --git a/r5dev/thirdparty/mbedtls/memory_buffer_alloc.c b/r5dev/thirdparty/mbedtls/memory_buffer_alloc.c new file mode 100644 index 00000000..e5052ce5 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/memory_buffer_alloc.c @@ -0,0 +1,757 @@ +/* + * Buffer-based memory allocator + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) +#include "mbedtls/memory_buffer_alloc.h" + +/* No need for the header guard as MBEDTLS_MEMORY_BUFFER_ALLOC_C + is dependent upon MBEDTLS_PLATFORM_C */ +#include "mbedtls/platform.h" +#include "mbedtls/platform_util.h" + +#include + +#if defined(MBEDTLS_MEMORY_BACKTRACE) +#include +#endif + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +#define MAGIC1 0xFF00AA55 +#define MAGIC2 0xEE119966 +#define MAX_BT 20 + +typedef struct _memory_header memory_header; +struct _memory_header { + size_t magic1; + size_t size; + size_t alloc; + memory_header *prev; + memory_header *next; + memory_header *prev_free; + memory_header *next_free; +#if defined(MBEDTLS_MEMORY_BACKTRACE) + char **trace; + size_t trace_count; +#endif + size_t magic2; +}; + +typedef struct { + unsigned char *buf; + size_t len; + memory_header *first; + memory_header *first_free; + int verify; +#if defined(MBEDTLS_MEMORY_DEBUG) + size_t alloc_count; + size_t free_count; + size_t total_used; + size_t maximum_used; + size_t header_count; + size_t maximum_header_count; +#endif +#if defined(MBEDTLS_THREADING_C) + mbedtls_threading_mutex_t mutex; +#endif +} +buffer_alloc_ctx; + +static buffer_alloc_ctx heap; + +#if defined(MBEDTLS_MEMORY_DEBUG) +static void debug_header(memory_header *hdr) +{ +#if defined(MBEDTLS_MEMORY_BACKTRACE) + size_t i; +#endif + + mbedtls_fprintf(stderr, "HDR: PTR(%10zu), PREV(%10zu), NEXT(%10zu), " + "ALLOC(%zu), SIZE(%10zu)\n", + (size_t) hdr, (size_t) hdr->prev, (size_t) hdr->next, + hdr->alloc, hdr->size); + mbedtls_fprintf(stderr, " FPREV(%10zu), FNEXT(%10zu)\n", + (size_t) hdr->prev_free, (size_t) hdr->next_free); + +#if defined(MBEDTLS_MEMORY_BACKTRACE) + mbedtls_fprintf(stderr, "TRACE: \n"); + for (i = 0; i < hdr->trace_count; i++) { + mbedtls_fprintf(stderr, "%s\n", hdr->trace[i]); + } + mbedtls_fprintf(stderr, "\n"); +#endif +} + +static void debug_chain(void) +{ + memory_header *cur = heap.first; + + mbedtls_fprintf(stderr, "\nBlock list\n"); + while (cur != NULL) { + debug_header(cur); + cur = cur->next; + } + + mbedtls_fprintf(stderr, "Free list\n"); + cur = heap.first_free; + + while (cur != NULL) { + debug_header(cur); + cur = cur->next_free; + } +} +#endif /* MBEDTLS_MEMORY_DEBUG */ + +static int verify_header(memory_header *hdr) +{ + if (hdr->magic1 != MAGIC1) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: MAGIC1 mismatch\n"); +#endif + return 1; + } + + if (hdr->magic2 != MAGIC2) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: MAGIC2 mismatch\n"); +#endif + return 1; + } + + if (hdr->alloc > 1) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: alloc has illegal value\n"); +#endif + return 1; + } + + if (hdr->prev != NULL && hdr->prev == hdr->next) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: prev == next\n"); +#endif + return 1; + } + + if (hdr->prev_free != NULL && hdr->prev_free == hdr->next_free) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: prev_free == next_free\n"); +#endif + return 1; + } + + return 0; +} + +static int verify_chain(void) +{ + memory_header *prv = heap.first, *cur; + + if (prv == NULL || verify_header(prv) != 0) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: verification of first header " + "failed\n"); +#endif + return 1; + } + + if (heap.first->prev != NULL) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: verification failed: " + "first->prev != NULL\n"); +#endif + return 1; + } + + cur = heap.first->next; + + while (cur != NULL) { + if (verify_header(cur) != 0) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: verification of header " + "failed\n"); +#endif + return 1; + } + + if (cur->prev != prv) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: verification failed: " + "cur->prev != prv\n"); +#endif + return 1; + } + + prv = cur; + cur = cur->next; + } + + return 0; +} + +static void *buffer_alloc_calloc(size_t n, size_t size) +{ + memory_header *new, *cur = heap.first_free; + unsigned char *p; + void *ret; + size_t original_len, len; +#if defined(MBEDTLS_MEMORY_BACKTRACE) + void *trace_buffer[MAX_BT]; + size_t trace_cnt; +#endif + + if (heap.buf == NULL || heap.first == NULL) { + return NULL; + } + + original_len = len = n * size; + + if (n == 0 || size == 0 || len / n != size) { + return NULL; + } else if (len > (size_t) -MBEDTLS_MEMORY_ALIGN_MULTIPLE) { + return NULL; + } + + if (len % MBEDTLS_MEMORY_ALIGN_MULTIPLE) { + len -= len % MBEDTLS_MEMORY_ALIGN_MULTIPLE; + len += MBEDTLS_MEMORY_ALIGN_MULTIPLE; + } + + // Find block that fits + // + while (cur != NULL) { + if (cur->size >= len) { + break; + } + + cur = cur->next_free; + } + + if (cur == NULL) { + return NULL; + } + + if (cur->alloc != 0) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: block in free_list but allocated " + "data\n"); +#endif + mbedtls_exit(1); + } + +#if defined(MBEDTLS_MEMORY_DEBUG) + heap.alloc_count++; +#endif + + // Found location, split block if > memory_header + 4 room left + // + if (cur->size - len < sizeof(memory_header) + + MBEDTLS_MEMORY_ALIGN_MULTIPLE) { + cur->alloc = 1; + + // Remove from free_list + // + if (cur->prev_free != NULL) { + cur->prev_free->next_free = cur->next_free; + } else { + heap.first_free = cur->next_free; + } + + if (cur->next_free != NULL) { + cur->next_free->prev_free = cur->prev_free; + } + + cur->prev_free = NULL; + cur->next_free = NULL; + +#if defined(MBEDTLS_MEMORY_DEBUG) + heap.total_used += cur->size; + if (heap.total_used > heap.maximum_used) { + heap.maximum_used = heap.total_used; + } +#endif +#if defined(MBEDTLS_MEMORY_BACKTRACE) + trace_cnt = backtrace(trace_buffer, MAX_BT); + cur->trace = backtrace_symbols(trace_buffer, trace_cnt); + cur->trace_count = trace_cnt; +#endif + + if ((heap.verify & MBEDTLS_MEMORY_VERIFY_ALLOC) && verify_chain() != 0) { + mbedtls_exit(1); + } + + ret = (unsigned char *) cur + sizeof(memory_header); + memset(ret, 0, original_len); + + return ret; + } + + p = ((unsigned char *) cur) + sizeof(memory_header) + len; + new = (memory_header *) p; + + new->size = cur->size - len - sizeof(memory_header); + new->alloc = 0; + new->prev = cur; + new->next = cur->next; +#if defined(MBEDTLS_MEMORY_BACKTRACE) + new->trace = NULL; + new->trace_count = 0; +#endif + new->magic1 = MAGIC1; + new->magic2 = MAGIC2; + + if (new->next != NULL) { + new->next->prev = new; + } + + // Replace cur with new in free_list + // + new->prev_free = cur->prev_free; + new->next_free = cur->next_free; + if (new->prev_free != NULL) { + new->prev_free->next_free = new; + } else { + heap.first_free = new; + } + + if (new->next_free != NULL) { + new->next_free->prev_free = new; + } + + cur->alloc = 1; + cur->size = len; + cur->next = new; + cur->prev_free = NULL; + cur->next_free = NULL; + +#if defined(MBEDTLS_MEMORY_DEBUG) + heap.header_count++; + if (heap.header_count > heap.maximum_header_count) { + heap.maximum_header_count = heap.header_count; + } + heap.total_used += cur->size; + if (heap.total_used > heap.maximum_used) { + heap.maximum_used = heap.total_used; + } +#endif +#if defined(MBEDTLS_MEMORY_BACKTRACE) + trace_cnt = backtrace(trace_buffer, MAX_BT); + cur->trace = backtrace_symbols(trace_buffer, trace_cnt); + cur->trace_count = trace_cnt; +#endif + + if ((heap.verify & MBEDTLS_MEMORY_VERIFY_ALLOC) && verify_chain() != 0) { + mbedtls_exit(1); + } + + ret = (unsigned char *) cur + sizeof(memory_header); + memset(ret, 0, original_len); + + return ret; +} + +static void buffer_alloc_free(void *ptr) +{ + memory_header *hdr, *old = NULL; + unsigned char *p = (unsigned char *) ptr; + + if (ptr == NULL || heap.buf == NULL || heap.first == NULL) { + return; + } + + if (p < heap.buf || p >= heap.buf + heap.len) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: mbedtls_free() outside of managed " + "space\n"); +#endif + mbedtls_exit(1); + } + + p -= sizeof(memory_header); + hdr = (memory_header *) p; + + if (verify_header(hdr) != 0) { + mbedtls_exit(1); + } + + if (hdr->alloc != 1) { +#if defined(MBEDTLS_MEMORY_DEBUG) + mbedtls_fprintf(stderr, "FATAL: mbedtls_free() on unallocated " + "data\n"); +#endif + mbedtls_exit(1); + } + + hdr->alloc = 0; + +#if defined(MBEDTLS_MEMORY_DEBUG) + heap.free_count++; + heap.total_used -= hdr->size; +#endif + +#if defined(MBEDTLS_MEMORY_BACKTRACE) + free(hdr->trace); + hdr->trace = NULL; + hdr->trace_count = 0; +#endif + + // Regroup with block before + // + if (hdr->prev != NULL && hdr->prev->alloc == 0) { +#if defined(MBEDTLS_MEMORY_DEBUG) + heap.header_count--; +#endif + hdr->prev->size += sizeof(memory_header) + hdr->size; + hdr->prev->next = hdr->next; + old = hdr; + hdr = hdr->prev; + + if (hdr->next != NULL) { + hdr->next->prev = hdr; + } + + memset(old, 0, sizeof(memory_header)); + } + + // Regroup with block after + // + if (hdr->next != NULL && hdr->next->alloc == 0) { +#if defined(MBEDTLS_MEMORY_DEBUG) + heap.header_count--; +#endif + hdr->size += sizeof(memory_header) + hdr->next->size; + old = hdr->next; + hdr->next = hdr->next->next; + + if (hdr->prev_free != NULL || hdr->next_free != NULL) { + if (hdr->prev_free != NULL) { + hdr->prev_free->next_free = hdr->next_free; + } else { + heap.first_free = hdr->next_free; + } + + if (hdr->next_free != NULL) { + hdr->next_free->prev_free = hdr->prev_free; + } + } + + hdr->prev_free = old->prev_free; + hdr->next_free = old->next_free; + + if (hdr->prev_free != NULL) { + hdr->prev_free->next_free = hdr; + } else { + heap.first_free = hdr; + } + + if (hdr->next_free != NULL) { + hdr->next_free->prev_free = hdr; + } + + if (hdr->next != NULL) { + hdr->next->prev = hdr; + } + + memset(old, 0, sizeof(memory_header)); + } + + // Prepend to free_list if we have not merged + // (Does not have to stay in same order as prev / next list) + // + if (old == NULL) { + hdr->next_free = heap.first_free; + if (heap.first_free != NULL) { + heap.first_free->prev_free = hdr; + } + heap.first_free = hdr; + } + + if ((heap.verify & MBEDTLS_MEMORY_VERIFY_FREE) && verify_chain() != 0) { + mbedtls_exit(1); + } +} + +void mbedtls_memory_buffer_set_verify(int verify) +{ + heap.verify = verify; +} + +int mbedtls_memory_buffer_alloc_verify(void) +{ + return verify_chain(); +} + +#if defined(MBEDTLS_MEMORY_DEBUG) +void mbedtls_memory_buffer_alloc_status(void) +{ + mbedtls_fprintf(stderr, + "Current use: %zu blocks / %zu bytes, max: %zu blocks / " + "%zu bytes (total %zu bytes), alloc / free: %zu / %zu\n", + heap.header_count, heap.total_used, + heap.maximum_header_count, heap.maximum_used, + heap.maximum_header_count * sizeof(memory_header) + + heap.maximum_used, + heap.alloc_count, heap.free_count); + + if (heap.first->next == NULL) { + mbedtls_fprintf(stderr, "All memory de-allocated in stack buffer\n"); + } else { + mbedtls_fprintf(stderr, "Memory currently allocated:\n"); + debug_chain(); + } +} + +void mbedtls_memory_buffer_alloc_count_get(size_t *alloc_count, size_t *free_count) +{ + *alloc_count = heap.alloc_count; + *free_count = heap.free_count; +} + +void mbedtls_memory_buffer_alloc_max_get(size_t *max_used, size_t *max_blocks) +{ + *max_used = heap.maximum_used; + *max_blocks = heap.maximum_header_count; +} + +void mbedtls_memory_buffer_alloc_max_reset(void) +{ + heap.maximum_used = 0; + heap.maximum_header_count = 0; +} + +void mbedtls_memory_buffer_alloc_cur_get(size_t *cur_used, size_t *cur_blocks) +{ + *cur_used = heap.total_used; + *cur_blocks = heap.header_count; +} +#endif /* MBEDTLS_MEMORY_DEBUG */ + +#if defined(MBEDTLS_THREADING_C) +static void *buffer_alloc_calloc_mutexed(size_t n, size_t size) +{ + void *buf; + if (mbedtls_mutex_lock(&heap.mutex) != 0) { + return NULL; + } + buf = buffer_alloc_calloc(n, size); + if (mbedtls_mutex_unlock(&heap.mutex)) { + return NULL; + } + return buf; +} + +static void buffer_alloc_free_mutexed(void *ptr) +{ + /* We have no good option here, but corrupting the heap seems + * worse than losing memory. */ + if (mbedtls_mutex_lock(&heap.mutex)) { + return; + } + buffer_alloc_free(ptr); + (void) mbedtls_mutex_unlock(&heap.mutex); +} +#endif /* MBEDTLS_THREADING_C */ + +void mbedtls_memory_buffer_alloc_init(unsigned char *buf, size_t len) +{ + memset(&heap, 0, sizeof(buffer_alloc_ctx)); + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&heap.mutex); + mbedtls_platform_set_calloc_free(buffer_alloc_calloc_mutexed, + buffer_alloc_free_mutexed); +#else + mbedtls_platform_set_calloc_free(buffer_alloc_calloc, buffer_alloc_free); +#endif + + if (len < sizeof(memory_header) + MBEDTLS_MEMORY_ALIGN_MULTIPLE) { + return; + } else if ((size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE) { + /* Adjust len first since buf is used in the computation */ + len -= MBEDTLS_MEMORY_ALIGN_MULTIPLE + - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; + buf += MBEDTLS_MEMORY_ALIGN_MULTIPLE + - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE; + } + + memset(buf, 0, len); + + heap.buf = buf; + heap.len = len; + + heap.first = (memory_header *) buf; + heap.first->size = len - sizeof(memory_header); + heap.first->magic1 = MAGIC1; + heap.first->magic2 = MAGIC2; + heap.first_free = heap.first; +} + +void mbedtls_memory_buffer_alloc_free(void) +{ +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_free(&heap.mutex); +#endif + mbedtls_platform_zeroize(&heap, sizeof(buffer_alloc_ctx)); +} + +#if defined(MBEDTLS_SELF_TEST) +static int check_pointer(void *p) +{ + if (p == NULL) { + return -1; + } + + if ((size_t) p % MBEDTLS_MEMORY_ALIGN_MULTIPLE != 0) { + return -1; + } + + return 0; +} + +static int check_all_free(void) +{ + if ( +#if defined(MBEDTLS_MEMORY_DEBUG) + heap.total_used != 0 || +#endif + heap.first != heap.first_free || + (void *) heap.first != (void *) heap.buf) { + return -1; + } + + return 0; +} + +#define TEST_ASSERT(condition) \ + if (!(condition)) \ + { \ + if (verbose != 0) \ + mbedtls_printf("failed\n"); \ + \ + ret = 1; \ + goto cleanup; \ + } + +int mbedtls_memory_buffer_alloc_self_test(int verbose) +{ + unsigned char buf[1024]; + unsigned char *p, *q, *r, *end; + int ret = 0; + + if (verbose != 0) { + mbedtls_printf(" MBA test #1 (basic alloc-free cycle): "); + } + + mbedtls_memory_buffer_alloc_init(buf, sizeof(buf)); + + p = mbedtls_calloc(1, 1); + q = mbedtls_calloc(1, 128); + r = mbedtls_calloc(1, 16); + + TEST_ASSERT(check_pointer(p) == 0 && + check_pointer(q) == 0 && + check_pointer(r) == 0); + + mbedtls_free(r); + mbedtls_free(q); + mbedtls_free(p); + + TEST_ASSERT(check_all_free() == 0); + + /* Memorize end to compare with the next test */ + end = heap.buf + heap.len; + + mbedtls_memory_buffer_alloc_free(); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + if (verbose != 0) { + mbedtls_printf(" MBA test #2 (buf not aligned): "); + } + + mbedtls_memory_buffer_alloc_init(buf + 1, sizeof(buf) - 1); + + TEST_ASSERT(heap.buf + heap.len == end); + + p = mbedtls_calloc(1, 1); + q = mbedtls_calloc(1, 128); + r = mbedtls_calloc(1, 16); + + TEST_ASSERT(check_pointer(p) == 0 && + check_pointer(q) == 0 && + check_pointer(r) == 0); + + mbedtls_free(r); + mbedtls_free(q); + mbedtls_free(p); + + TEST_ASSERT(check_all_free() == 0); + + mbedtls_memory_buffer_alloc_free(); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + + if (verbose != 0) { + mbedtls_printf(" MBA test #3 (full): "); + } + + mbedtls_memory_buffer_alloc_init(buf, sizeof(buf)); + + p = mbedtls_calloc(1, sizeof(buf) - sizeof(memory_header)); + + TEST_ASSERT(check_pointer(p) == 0); + TEST_ASSERT(mbedtls_calloc(1, 1) == NULL); + + mbedtls_free(p); + + p = mbedtls_calloc(1, sizeof(buf) - 2 * sizeof(memory_header) - 16); + q = mbedtls_calloc(1, 16); + + TEST_ASSERT(check_pointer(p) == 0 && check_pointer(q) == 0); + TEST_ASSERT(mbedtls_calloc(1, 1) == NULL); + + mbedtls_free(q); + + TEST_ASSERT(mbedtls_calloc(1, 17) == NULL); + + mbedtls_free(p); + + TEST_ASSERT(check_all_free() == 0); + + mbedtls_memory_buffer_alloc_free(); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + +cleanup: + mbedtls_memory_buffer_alloc_free(); + + return ret; +} +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_MEMORY_BUFFER_ALLOC_C */ diff --git a/r5dev/thirdparty/mbedtls/mps_common.h b/r5dev/thirdparty/mbedtls/mps_common.h new file mode 100644 index 00000000..4a10176b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/mps_common.h @@ -0,0 +1,195 @@ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/** + * \file mps_common.h + * + * \brief Common functions and macros used by MPS + */ + +#ifndef MBEDTLS_MPS_COMMON_H +#define MBEDTLS_MPS_COMMON_H + +#include "mps_error.h" + +#include + +/** + * \name SECTION: MPS Configuration + * + * \{ + */ + +/*! This flag controls whether the MPS-internal components + * (reader, writer, Layer 1-3) perform validation of the + * expected abstract state at the entry of API calls. + * + * Context: All MPS API functions impose assumptions/preconditions on the + * context on which they operate. For example, every structure has a notion of + * state integrity which is established by `xxx_init()` and preserved by any + * calls to the MPS API which satisfy their preconditions and either succeed, + * or fail with an error code which is explicitly documented to not corrupt + * structure integrity (such as WANT_READ and WANT_WRITE); + * apart from `xxx_init()` any function assumes state integrity as a + * precondition (but usually more). If any of the preconditions is violated, + * the function's behavior is entirely undefined. + * In addition to state integrity, all MPS structures have a more refined + * notion of abstract state that the API operates on. For example, all layers + * have a notion of 'abstract read state' which indicates if incoming data has + * been passed to the user, e.g. through mps_l2_read_start() for Layer 2 + * or mps_l3_read() in Layer 3. After such a call, it doesn't make sense to + * call these reading functions again until the incoming data has been + * explicitly 'consumed', e.g. through mps_l2_read_consume() for Layer 2 or + * mps_l3_read_consume() on Layer 3. However, even if it doesn't make sense, + * it's a design choice whether the API should fail gracefully on such + * non-sensical calls or not, and that's what this option is about: + * + * This option determines whether the expected abstract state + * is part of the API preconditions or not: If the option is set, + * then the abstract state is not part of the precondition and is + * thus required to be validated by the implementation. If an unexpected + * abstract state is encountered, the implementation must fail gracefully + * with error #MBEDTLS_ERR_MPS_OPERATION_UNEXPECTED. + * Conversely, if this option is not set, then the expected abstract state + * is included in the preconditions of the respective API calls, and + * an implementation's behaviour is undefined if the abstract state is + * not as expected. + * + * For example: Enabling this makes mps_l2_read_done() fail if + * no incoming record is currently open; disabling this would + * lead to undefined behavior in this case. + * + * Comment this to remove state validation. + */ +#define MBEDTLS_MPS_STATE_VALIDATION + +/*! This flag enables/disables assertions on the internal state of MPS. + * + * Assertions are sanity checks that should never trigger when MPS + * is used within the bounds of its API and preconditions. + * + * Enabling this increases security by limiting the scope of + * potential bugs, but comes at the cost of increased code size. + * + * Note: So far, there is no guiding principle as to what + * expected conditions merit an assertion, and which don't. + * + * Comment this to disable assertions. + */ +#define MBEDTLS_MPS_ENABLE_ASSERTIONS + +/*! This flag controls whether tracing for MPS should be enabled. */ +//#define MBEDTLS_MPS_ENABLE_TRACE + +#if defined(MBEDTLS_MPS_STATE_VALIDATION) + +#define MBEDTLS_MPS_STATE_VALIDATE_RAW(cond, string) \ + do \ + { \ + if (!(cond)) \ + { \ + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_ERROR, string); \ + MBEDTLS_MPS_TRACE_RETURN(MBEDTLS_ERR_MPS_OPERATION_UNEXPECTED); \ + } \ + } while (0) + +#else /* MBEDTLS_MPS_STATE_VALIDATION */ + +#define MBEDTLS_MPS_STATE_VALIDATE_RAW(cond, string) \ + do \ + { \ + (cond); \ + } while (0) + +#endif /* MBEDTLS_MPS_STATE_VALIDATION */ + +#if defined(MBEDTLS_MPS_ENABLE_ASSERTIONS) + +#define MBEDTLS_MPS_ASSERT_RAW(cond, string) \ + do \ + { \ + if (!(cond)) \ + { \ + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_ERROR, string); \ + MBEDTLS_MPS_TRACE_RETURN(MBEDTLS_ERR_MPS_INTERNAL_ERROR); \ + } \ + } while (0) + +#else /* MBEDTLS_MPS_ENABLE_ASSERTIONS */ + +#define MBEDTLS_MPS_ASSERT_RAW(cond, string) do {} while (0) + +#endif /* MBEDTLS_MPS_ENABLE_ASSERTIONS */ + + +/* \} name SECTION: MPS Configuration */ + +/** + * \name SECTION: Common types + * + * Various common types used throughout MPS. + * \{ + */ + +/** \brief The type of buffer sizes and offsets used in MPS structures. + * + * This is an unsigned integer type that should be large enough to + * hold the length of any buffer or message processed by MPS. + * + * The reason to pick a value as small as possible here is + * to reduce the size of MPS structures. + * + * \warning Care has to be taken when using a narrower type + * than ::mbedtls_mps_size_t here because of + * potential truncation during conversion. + * + * \warning Handshake messages in TLS may be up to 2^24 ~ 16Mb in size. + * If mbedtls_mps_[opt_]stored_size_t is smaller than that, the + * maximum handshake message is restricted accordingly. + * + * For now, we use the default type of size_t throughout, and the use of + * smaller types or different types for ::mbedtls_mps_size_t and + * ::mbedtls_mps_stored_size_t is not yet supported. + * + */ +typedef size_t mbedtls_mps_stored_size_t; +#define MBEDTLS_MPS_STORED_SIZE_MAX ((mbedtls_mps_stored_size_t) -1) + +/** \brief The type of buffer sizes and offsets used in the MPS API + * and implementation. + * + * This must be at least as wide as ::mbedtls_stored_size_t but + * may be chosen to be strictly larger if more suitable for the + * target architecture. + * + * For example, in a test build for ARM Thumb, using uint_fast16_t + * instead of uint16_t reduced the code size from 1060 Byte to 962 Byte, + * so almost 10%. + */ +typedef size_t mbedtls_mps_size_t; +#define MBEDTLS_MPS_SIZE_MAX ((mbedtls_mps_size_t) -1) + +#if MBEDTLS_MPS_STORED_SIZE_MAX > MBEDTLS_MPS_SIZE_MAX +#error "Misconfiguration of mbedtls_mps_size_t and mbedtls_mps_stored_size_t." +#endif + +/* \} SECTION: Common types */ + + +#endif /* MBEDTLS_MPS_COMMON_H */ diff --git a/r5dev/thirdparty/mbedtls/mps_error.h b/r5dev/thirdparty/mbedtls/mps_error.h new file mode 100644 index 00000000..15570d23 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/mps_error.h @@ -0,0 +1,103 @@ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/** + * \file mps_error.h + * + * \brief Error codes used by MPS + */ + +#ifndef MBEDTLS_MPS_ERROR_H +#define MBEDTLS_MPS_ERROR_H + + +/* TODO: The error code allocation needs to be revisited: + * + * - Should we make (some of) the MPS Reader error codes public? + * If so, we need to adjust MBEDTLS_MPS_READER_MAKE_ERROR() to hit + * a gap in the Mbed TLS public error space. + * If not, we have to make sure we don't forward those errors + * at the level of the public API -- no risk at the moment as + * long as MPS is an experimental component not accessible from + * public API. + */ + +/** + * \name SECTION: MPS general error codes + * + * \{ + */ + +#ifndef MBEDTLS_MPS_ERR_BASE +#define MBEDTLS_MPS_ERR_BASE (0) +#endif + +#define MBEDTLS_MPS_MAKE_ERROR(code) \ + (-(MBEDTLS_MPS_ERR_BASE | (code))) + +#define MBEDTLS_ERR_MPS_OPERATION_UNEXPECTED MBEDTLS_MPS_MAKE_ERROR(0x1) +#define MBEDTLS_ERR_MPS_INTERNAL_ERROR MBEDTLS_MPS_MAKE_ERROR(0x2) + +/* \} name SECTION: MPS general error codes */ + +/** + * \name SECTION: MPS Reader error codes + * + * \{ + */ + +#ifndef MBEDTLS_MPS_READER_ERR_BASE +#define MBEDTLS_MPS_READER_ERR_BASE (1 << 8) +#endif + +#define MBEDTLS_MPS_READER_MAKE_ERROR(code) \ + (-(MBEDTLS_MPS_READER_ERR_BASE | (code))) + +/*! An attempt to reclaim the data buffer from a reader failed because + * the user hasn't yet read and committed all of it. */ +#define MBEDTLS_ERR_MPS_READER_DATA_LEFT MBEDTLS_MPS_READER_MAKE_ERROR(0x1) + +/*! An invalid argument was passed to the reader. */ +#define MBEDTLS_ERR_MPS_READER_INVALID_ARG MBEDTLS_MPS_READER_MAKE_ERROR(0x2) + +/*! An attempt to move a reader to consuming mode through mbedtls_mps_reader_feed() + * after pausing failed because the provided data is not sufficient to serve the + * read requests that led to the pausing. */ +#define MBEDTLS_ERR_MPS_READER_NEED_MORE MBEDTLS_MPS_READER_MAKE_ERROR(0x3) + +/*! A get request failed because not enough data is available in the reader. */ +#define MBEDTLS_ERR_MPS_READER_OUT_OF_DATA MBEDTLS_MPS_READER_MAKE_ERROR(0x4) + +/*!< A get request after pausing and reactivating the reader failed because + * the request is not in line with the request made prior to pausing. The user + * must not change it's 'strategy' after pausing and reactivating a reader. */ +#define MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS MBEDTLS_MPS_READER_MAKE_ERROR(0x5) + +/*! An attempt to reclaim the data buffer from a reader failed because the reader + * has no accumulator it can use to backup the data that hasn't been processed. */ +#define MBEDTLS_ERR_MPS_READER_NEED_ACCUMULATOR MBEDTLS_MPS_READER_MAKE_ERROR(0x6) + +/*! An attempt to reclaim the data buffer from a reader failed because the + * accumulator passed to the reader is not large enough to hold both the + * data that hasn't been processed and the excess of the last read-request. */ +#define MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL MBEDTLS_MPS_READER_MAKE_ERROR(0x7) + +/* \} name SECTION: MPS Reader error codes */ + +#endif /* MBEDTLS_MPS_ERROR_H */ diff --git a/r5dev/thirdparty/mbedtls/mps_reader.c b/r5dev/thirdparty/mbedtls/mps_reader.c new file mode 100644 index 00000000..dc2a91cb --- /dev/null +++ b/r5dev/thirdparty/mbedtls/mps_reader.c @@ -0,0 +1,552 @@ +/* + * Message Processing Stack, Reader implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#include "mps_reader.h" +#include "mps_common.h" +#include "mps_trace.h" + +#include + +#if defined(MBEDTLS_MPS_ENABLE_TRACE) +static int mbedtls_mps_trace_id = MBEDTLS_MPS_TRACE_BIT_READER; +#endif /* MBEDTLS_MPS_ENABLE_TRACE */ + +/* + * GENERAL NOTE ON CODING STYLE + * + * The following code intentionally separates memory loads + * and stores from other operations (arithmetic or branches). + * This leads to the introduction of many local variables + * and significantly increases the C-code line count, but + * should not increase the size of generated assembly. + * + * The reason for this is twofold: + * (1) It will ease verification efforts using the VST + * (Verified Software Toolchain) + * whose program logic cannot directly reason + * about instructions containing a load or store in + * addition to other operations (e.g. *p = *q or + * tmp = *p + 42). + * (2) Operating on local variables and writing the results + * back to the target contexts on success only + * allows to maintain structure invariants even + * on failure - this in turn has two benefits: + * (2.a) If for some reason an error code is not caught + * and operation continues, functions are nonetheless + * called with sane contexts, reducing the risk + * of dangerous behavior. + * (2.b) Randomized testing is easier if structures + * remain intact even in the face of failing + * and/or non-sensical calls. + * Moreover, it might even reduce code-size because + * the compiler need not write back temporary results + * to memory in case of failure. + * + */ + +static inline int mps_reader_is_accumulating( + mbedtls_mps_reader const *rd) +{ + mbedtls_mps_size_t acc_remaining; + if (rd->acc == NULL) { + return 0; + } + + acc_remaining = rd->acc_share.acc_remaining; + return acc_remaining > 0; +} + +static inline int mps_reader_is_producing( + mbedtls_mps_reader const *rd) +{ + unsigned char *frag = rd->frag; + return frag == NULL; +} + +static inline int mps_reader_is_consuming( + mbedtls_mps_reader const *rd) +{ + return !mps_reader_is_producing(rd); +} + +static inline mbedtls_mps_size_t mps_reader_get_fragment_offset( + mbedtls_mps_reader const *rd) +{ + unsigned char *acc = rd->acc; + mbedtls_mps_size_t frag_offset; + + if (acc == NULL) { + return 0; + } + + frag_offset = rd->acc_share.frag_offset; + return frag_offset; +} + +static inline mbedtls_mps_size_t mps_reader_serving_from_accumulator( + mbedtls_mps_reader const *rd) +{ + mbedtls_mps_size_t frag_offset, end; + + frag_offset = mps_reader_get_fragment_offset(rd); + end = rd->end; + + return end < frag_offset; +} + +static inline void mps_reader_zero(mbedtls_mps_reader *rd) +{ + /* A plain memset() would likely be more efficient, + * but the current way of zeroing makes it harder + * to overlook fields which should not be zero-initialized. + * It's also more suitable for FV efforts since it + * doesn't require reasoning about structs being + * interpreted as unstructured binary blobs. */ + static mbedtls_mps_reader const zero = + { .frag = NULL, + .frag_len = 0, + .commit = 0, + .end = 0, + .pending = 0, + .acc = NULL, + .acc_len = 0, + .acc_available = 0, + .acc_share = { .acc_remaining = 0 } }; + *rd = zero; +} + +int mbedtls_mps_reader_init(mbedtls_mps_reader *rd, + unsigned char *acc, + mbedtls_mps_size_t acc_len) +{ + MBEDTLS_MPS_TRACE_INIT("mbedtls_mps_reader_init"); + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "* Accumulator size: %u bytes", (unsigned) acc_len); + mps_reader_zero(rd); + rd->acc = acc; + rd->acc_len = acc_len; + MBEDTLS_MPS_TRACE_RETURN(0); +} + +int mbedtls_mps_reader_free(mbedtls_mps_reader *rd) +{ + MBEDTLS_MPS_TRACE_INIT("mbedtls_mps_reader_free"); + mps_reader_zero(rd); + MBEDTLS_MPS_TRACE_RETURN(0); +} + +int mbedtls_mps_reader_feed(mbedtls_mps_reader *rd, + unsigned char *new_frag, + mbedtls_mps_size_t new_frag_len) +{ + mbedtls_mps_size_t copy_to_acc; + MBEDTLS_MPS_TRACE_INIT("mbedtls_mps_reader_feed"); + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "* Fragment length: %u bytes", (unsigned) new_frag_len); + + if (new_frag == NULL) { + MBEDTLS_MPS_TRACE_RETURN(MBEDTLS_ERR_MPS_READER_INVALID_ARG); + } + + MBEDTLS_MPS_STATE_VALIDATE_RAW(mps_reader_is_producing( + rd), + "mbedtls_mps_reader_feed() requires reader to be in producing mode"); + + if (mps_reader_is_accumulating(rd)) { + unsigned char *acc = rd->acc; + mbedtls_mps_size_t acc_remaining = rd->acc_share.acc_remaining; + mbedtls_mps_size_t acc_available = rd->acc_available; + + /* Skip over parts of the accumulator that have already been filled. */ + acc += acc_available; + + copy_to_acc = acc_remaining; + if (copy_to_acc > new_frag_len) { + copy_to_acc = new_frag_len; + } + + /* Copy new contents to accumulator. */ + memcpy(acc, new_frag, copy_to_acc); + + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Copy new data of size %u of %u into accumulator at offset %u", + (unsigned) copy_to_acc, (unsigned) new_frag_len, + (unsigned) acc_available); + + /* Check if, with the new fragment, we have enough data. */ + acc_remaining -= copy_to_acc; + if (acc_remaining > 0) { + /* We need to accumulate more data. Stay in producing mode. */ + acc_available += copy_to_acc; + rd->acc_share.acc_remaining = acc_remaining; + rd->acc_available = acc_available; + MBEDTLS_MPS_TRACE_RETURN(MBEDTLS_ERR_MPS_READER_NEED_MORE); + } + + /* We have filled the accumulator: Move to consuming mode. */ + + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Enough data available to serve user request"); + + /* Remember overlap of accumulator and fragment. */ + rd->acc_share.frag_offset = acc_available; + acc_available += copy_to_acc; + rd->acc_available = acc_available; + } else { /* Not accumulating */ + rd->acc_share.frag_offset = 0; + } + + rd->frag = new_frag; + rd->frag_len = new_frag_len; + rd->commit = 0; + rd->end = 0; + MBEDTLS_MPS_TRACE_RETURN(0); +} + + +int mbedtls_mps_reader_get(mbedtls_mps_reader *rd, + mbedtls_mps_size_t desired, + unsigned char **buffer, + mbedtls_mps_size_t *buflen) +{ + unsigned char *frag; + mbedtls_mps_size_t frag_len, frag_offset, end, frag_fetched, frag_remaining; + MBEDTLS_MPS_TRACE_INIT("mbedtls_mps_reader_get"); + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "* Bytes requested: %u", (unsigned) desired); + + MBEDTLS_MPS_STATE_VALIDATE_RAW(mps_reader_is_consuming( + rd), + "mbedtls_mps_reader_get() requires reader to be in consuming mode"); + + end = rd->end; + frag_offset = mps_reader_get_fragment_offset(rd); + + /* Check if we're still serving from the accumulator. */ + if (mps_reader_serving_from_accumulator(rd)) { + /* Illustration of supported and unsupported cases: + * + * - Allowed #1 + * + * +-----------------------------------+ + * | frag | + * +-----------------------------------+ + * + * end end+desired + * | | + * +-----v-------v-------------+ + * | acc | + * +---------------------------+ + * | | + * frag_offset acc_available + * + * - Allowed #2 + * + * +-----------------------------------+ + * | frag | + * +-----------------------------------+ + * + * end end+desired + * | | + * +----------v----------------v + * | acc | + * +---------------------------+ + * | | + * frag_offset acc_available + * + * - Not allowed #1 (could be served, but we don't actually use it): + * + * +-----------------------------------+ + * | frag | + * +-----------------------------------+ + * + * end end+desired + * | | + * +------v-------------v------+ + * | acc | + * +---------------------------+ + * | | + * frag_offset acc_available + * + * + * - Not allowed #2 (can't be served with a contiguous buffer): + * + * +-----------------------------------+ + * | frag | + * +-----------------------------------+ + * + * end end + desired + * | | + * +------v--------------------+ v + * | acc | + * +---------------------------+ + * | | + * frag_offset acc_available + * + * In case of Allowed #2 we're switching to serve from + * `frag` starting from the next call to mbedtls_mps_reader_get(). + */ + + unsigned char *acc; + + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Serve the request from the accumulator"); + if (frag_offset - end < desired) { + mbedtls_mps_size_t acc_available; + acc_available = rd->acc_available; + if (acc_available - end != desired) { + /* It might be possible to serve some of these situations by + * making additional space in the accumulator, removing those + * parts that have already been committed. + * On the other hand, this brings additional complexity and + * enlarges the code size, while there doesn't seem to be a use + * case where we don't attempt exactly the same `get` calls when + * resuming on a reader than what we tried before pausing it. + * If we believe we adhere to this restricted usage throughout + * the library, this check is a good opportunity to + * validate this. */ + MBEDTLS_MPS_TRACE_RETURN( + MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS); + } + } + + acc = rd->acc; + acc += end; + + *buffer = acc; + if (buflen != NULL) { + *buflen = desired; + } + + end += desired; + rd->end = end; + rd->pending = 0; + + MBEDTLS_MPS_TRACE_RETURN(0); + } + + /* Attempt to serve the request from the current fragment */ + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Serve the request from the current fragment."); + + frag_len = rd->frag_len; + frag_fetched = end - frag_offset; /* The amount of data from the current + * fragment that has already been passed + * to the user. */ + frag_remaining = frag_len - frag_fetched; /* Remaining data in fragment */ + + /* Check if we can serve the read request from the fragment. */ + if (frag_remaining < desired) { + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "There's not enough data in the current fragment " + "to serve the request."); + /* There's not enough data in the current fragment, + * so either just RETURN what we have or fail. */ + if (buflen == NULL) { + if (frag_remaining > 0) { + rd->pending = desired - frag_remaining; + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Remember to collect %u bytes before re-opening", + (unsigned) rd->pending); + } + MBEDTLS_MPS_TRACE_RETURN(MBEDTLS_ERR_MPS_READER_OUT_OF_DATA); + } + + desired = frag_remaining; + } + + /* There's enough data in the current fragment to serve the + * (potentially modified) read request. */ + + frag = rd->frag; + frag += frag_fetched; + + *buffer = frag; + if (buflen != NULL) { + *buflen = desired; + } + + end += desired; + rd->end = end; + rd->pending = 0; + MBEDTLS_MPS_TRACE_RETURN(0); +} + +int mbedtls_mps_reader_commit(mbedtls_mps_reader *rd) +{ + mbedtls_mps_size_t end; + MBEDTLS_MPS_TRACE_INIT("mbedtls_mps_reader_commit"); + MBEDTLS_MPS_STATE_VALIDATE_RAW(mps_reader_is_consuming( + rd), + "mbedtls_mps_reader_commit() requires reader to be in consuming mode"); + + end = rd->end; + rd->commit = end; + + MBEDTLS_MPS_TRACE_RETURN(0); +} + +int mbedtls_mps_reader_reclaim(mbedtls_mps_reader *rd, + int *paused) +{ + unsigned char *frag, *acc; + mbedtls_mps_size_t pending, commit; + mbedtls_mps_size_t acc_len, frag_offset, frag_len; + MBEDTLS_MPS_TRACE_INIT("mbedtls_mps_reader_reclaim"); + + if (paused != NULL) { + *paused = 0; + } + + MBEDTLS_MPS_STATE_VALIDATE_RAW(mps_reader_is_consuming( + rd), + "mbedtls_mps_reader_reclaim() requires reader to be in consuming mode"); + + frag = rd->frag; + acc = rd->acc; + pending = rd->pending; + commit = rd->commit; + frag_len = rd->frag_len; + + frag_offset = mps_reader_get_fragment_offset(rd); + + if (pending == 0) { + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "No unsatisfied read-request has been logged."); + + /* Check if there's data left to be consumed. */ + if (commit < frag_offset || commit - frag_offset < frag_len) { + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "There is data left to be consumed."); + rd->end = commit; + MBEDTLS_MPS_TRACE_RETURN(MBEDTLS_ERR_MPS_READER_DATA_LEFT); + } + + rd->acc_available = 0; + rd->acc_share.acc_remaining = 0; + + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Fragment has been fully processed and committed."); + } else { + int overflow; + + mbedtls_mps_size_t acc_backup_offset; + mbedtls_mps_size_t acc_backup_len; + mbedtls_mps_size_t frag_backup_offset; + mbedtls_mps_size_t frag_backup_len; + + mbedtls_mps_size_t backup_len; + mbedtls_mps_size_t acc_len_needed; + + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "There has been an unsatisfied read with %u bytes overhead.", + (unsigned) pending); + + if (acc == NULL) { + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "No accumulator present"); + MBEDTLS_MPS_TRACE_RETURN( + MBEDTLS_ERR_MPS_READER_NEED_ACCUMULATOR); + } + acc_len = rd->acc_len; + + /* Check if the upper layer has already fetched + * and committed the contents of the accumulator. */ + if (commit < frag_offset) { + /* No, accumulator is still being processed. */ + frag_backup_offset = 0; + frag_backup_len = frag_len; + acc_backup_offset = commit; + acc_backup_len = frag_offset - commit; + } else { + /* Yes, the accumulator is already processed. */ + frag_backup_offset = commit - frag_offset; + frag_backup_len = frag_len - frag_backup_offset; + acc_backup_offset = 0; + acc_backup_len = 0; + } + + backup_len = acc_backup_len + frag_backup_len; + acc_len_needed = backup_len + pending; + + overflow = 0; + overflow |= (backup_len < acc_backup_len); + overflow |= (acc_len_needed < backup_len); + + if (overflow || acc_len < acc_len_needed) { + /* Except for the different return code, we behave as if + * there hadn't been a call to mbedtls_mps_reader_get() + * since the last commit. */ + rd->end = commit; + rd->pending = 0; + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_ERROR, + "The accumulator is too small to handle the backup."); + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_ERROR, + "* Size: %u", (unsigned) acc_len); + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_ERROR, + "* Needed: %u (%u + %u)", + (unsigned) acc_len_needed, + (unsigned) backup_len, (unsigned) pending); + MBEDTLS_MPS_TRACE_RETURN( + MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL); + } + + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Fragment backup: %u", (unsigned) frag_backup_len); + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Accumulator backup: %u", (unsigned) acc_backup_len); + + /* Move uncommitted parts from the accumulator to the front + * of the accumulator. */ + memmove(acc, acc + acc_backup_offset, acc_backup_len); + + /* Copy uncommitted parts of the current fragment to the + * accumulator. */ + memcpy(acc + acc_backup_len, + frag + frag_backup_offset, frag_backup_len); + + rd->acc_available = backup_len; + rd->acc_share.acc_remaining = pending; + + if (paused != NULL) { + *paused = 1; + } + } + + rd->frag = NULL; + rd->frag_len = 0; + + rd->commit = 0; + rd->end = 0; + rd->pending = 0; + + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_COMMENT, + "Final state: aa %u, al %u, ar %u", + (unsigned) rd->acc_available, (unsigned) rd->acc_len, + (unsigned) rd->acc_share.acc_remaining); + MBEDTLS_MPS_TRACE_RETURN(0); +} + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/r5dev/thirdparty/mbedtls/mps_reader.h b/r5dev/thirdparty/mbedtls/mps_reader.h new file mode 100644 index 00000000..bff67050 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/mps_reader.h @@ -0,0 +1,380 @@ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/** + * \file mps_reader.h + * + * \brief This file defines reader objects, which together with their + * sibling writer objects form the basis for the communication + * between the various layers of the Mbed TLS messaging stack, + * as well as the communication between the messaging stack and + * the (D)TLS handshake protocol implementation. + * + * Readers provide a means of transferring incoming data from + * a 'producer' providing it in chunks of arbitrary size, to + * a 'consumer' which fetches and processes it in chunks of + * again arbitrary, and potentially different, size. + * + * Readers can thus be seen as datagram-to-stream converters, + * and they abstract away the following two tasks from the user: + * 1. The pointer arithmetic of stepping through a producer- + * provided chunk in smaller chunks. + * 2. The merging of incoming data chunks in case the + * consumer requests data in larger chunks than what the + * producer provides. + * + * The basic abstract flow of operation is the following: + * - Initially, the reader is in 'producing mode'. + * - The producer hands an incoming data buffer to the reader, + * moving it from 'producing' to 'consuming' mode. + * - The consumer subsequently fetches and processes the buffer + * content. Once that's done -- or partially done and a consumer's + * request can't be fulfilled -- the producer revokes the reader's + * access to the incoming data buffer, putting the reader back to + * producing mode. + * - The producer subsequently gathers more incoming data and hands + * it to the reader until it switches back to consuming mode + * if enough data is available for the last consumer request to + * be satisfiable. + * - Repeat the above. + * + * The abstract states of the reader from the producer's and + * consumer's perspective are as follows: + * + * - From the perspective of the consumer, the state of the + * reader consists of the following: + * - A byte stream representing (concatenation of) the data + * received through calls to mbedtls_mps_reader_get(), + * - A marker within that byte stream indicating which data + * can be considered processed, and hence need not be retained, + * when the reader is passed back to the producer via + * mbedtls_mps_reader_reclaim(). + * The marker is set via mbedtls_mps_reader_commit() + * which places it at the end of the current byte stream. + * The consumer need not be aware of the distinction between consumer + * and producer mode, because it only interfaces with the reader + * when the latter is in consuming mode. + * + * - From the perspective of the producer, the reader's state is one of: + * - Attached: The reader is in consuming mode. + * - Unset: No incoming data buffer is currently managed by the reader, + * and all previously handed incoming data buffers have been + * fully processed. More data needs to be fed into the reader + * via mbedtls_mps_reader_feed(). + * + * - Accumulating: No incoming data buffer is currently managed by the + * reader, but some data from the previous incoming data + * buffer hasn't been processed yet and is internally + * held back. + * The Attached state belongs to consuming mode, while the Unset and + * Accumulating states belong to producing mode. + * + * Transitioning from the Unset or Accumulating state to Attached is + * done via successful calls to mbedtls_mps_reader_feed(), while + * transitioning from Attached to either Unset or Accumulating (depending + * on what has been processed) is done via mbedtls_mps_reader_reclaim(). + * + * The following diagram depicts the producer-state progression: + * + * +------------------+ reclaim + * | Unset +<-------------------------------------+ get + * +--------|---------+ | +------+ + * | | | | + * | | | | + * | feed +---------+---+--+ | + * +--------------------------------------> <---+ + * | Attached | + * +--------------------------------------> <---+ + * | feed, enough data available +---------+---+--+ | + * | to serve previous consumer request | | | + * | | | | + * +--------+---------+ | +------+ + * +----> Accumulating |<-------------------------------------+ commit + * | +---+--------------+ reclaim, previous read request + * | | couldn't be fulfilled + * | | + * +--------+ + * feed, need more data to serve + * previous consumer request + * | + * | + * producing mode | consuming mode + * | + * + */ + +#ifndef MBEDTLS_READER_H +#define MBEDTLS_READER_H + +#include + +#include "mps_common.h" +#include "mps_error.h" + +struct mbedtls_mps_reader; +typedef struct mbedtls_mps_reader mbedtls_mps_reader; + +/* + * Structure definitions + */ + +struct mbedtls_mps_reader { + unsigned char *frag; /*!< The fragment of incoming data managed by + * the reader; it is provided to the reader + * through mbedtls_mps_reader_feed(). The reader + * does not own the fragment and does not + * perform any allocation operations on it, + * but does have read and write access to it. + * + * The reader is in consuming mode if + * and only if \c frag is not \c NULL. */ + mbedtls_mps_stored_size_t frag_len; + /*!< The length of the current fragment. + * Must be 0 if \c frag == \c NULL. */ + mbedtls_mps_stored_size_t commit; + /*!< The offset of the last commit, relative + * to the first byte in the fragment, if + * no accumulator is present. If an accumulator + * is present, it is viewed as a prefix to the + * current fragment, and this variable contains + * an offset from the beginning of the accumulator. + * + * This is only used when the reader is in + * consuming mode, i.e. \c frag != \c NULL; + * otherwise, its value is \c 0. */ + mbedtls_mps_stored_size_t end; + /*!< The offset of the end of the last chunk + * passed to the user through a call to + * mbedtls_mps_reader_get(), relative to the first + * byte in the fragment, if no accumulator is + * present. If an accumulator is present, it is + * viewed as a prefix to the current fragment, and + * this variable contains an offset from the + * beginning of the accumulator. + * + * This is only used when the reader is in + * consuming mode, i.e. \c frag != \c NULL; + * otherwise, its value is \c 0. */ + mbedtls_mps_stored_size_t pending; + /*!< The amount of incoming data missing on the + * last call to mbedtls_mps_reader_get(). + * In particular, it is \c 0 if the last call + * was successful. + * If a reader is reclaimed after an + * unsuccessful call to mbedtls_mps_reader_get(), + * this variable is used to have the reader + * remember how much data should be accumulated + * so that the call to mbedtls_mps_reader_get() + * succeeds next time. + * This is only used when the reader is in + * consuming mode, i.e. \c frag != \c NULL; + * otherwise, its value is \c 0. */ + + /* The accumulator is only needed if we need to be able to pause + * the reader. A few bytes could be saved by moving this to a + * separate struct and using a pointer here. */ + + unsigned char *acc; /*!< The accumulator is used to gather incoming + * data if a read-request via mbedtls_mps_reader_get() + * cannot be served from the current fragment. */ + mbedtls_mps_stored_size_t acc_len; + /*!< The total size of the accumulator. */ + mbedtls_mps_stored_size_t acc_available; + /*!< The number of bytes currently gathered in + * the accumulator. This is both used in + * producing and in consuming mode: + * While producing, it is increased until + * it reaches the value of \c acc_remaining below. + * While consuming, it is used to judge if a + * get request can be served from the + * accumulator or not. + * Must not be larger than \c acc_len. */ + union { + mbedtls_mps_stored_size_t acc_remaining; + /*!< This indicates the amount of data still + * to be gathered in the accumulator. It is + * only used in producing mode. + * Must be at most acc_len - acc_available. */ + mbedtls_mps_stored_size_t frag_offset; + /*!< If an accumulator is present and in use, this + * field indicates the offset of the current + * fragment from the beginning of the + * accumulator. If no accumulator is present + * or the accumulator is not in use, this is \c 0. + * It is only used in consuming mode. + * Must not be larger than \c acc_available. */ + } acc_share; +}; + +/* + * API organization: + * A reader object is usually prepared and maintained + * by some lower layer and passed for usage to an upper + * layer, and the API naturally splits according to which + * layer is supposed to use the respective functions. + */ + +/* + * Maintenance API (Lower layer) + */ + +/** + * \brief Initialize a reader object + * + * \param reader The reader to be initialized. + * \param acc The buffer to be used as a temporary accumulator + * in case get requests through mbedtls_mps_reader_get() + * exceed the buffer provided by mbedtls_mps_reader_feed(). + * This buffer is owned by the caller and exclusive use + * for reading and writing is given to the reader for the + * duration of the reader's lifetime. It is thus the caller's + * responsibility to maintain (and not touch) the buffer for + * the lifetime of the reader, and to properly zeroize and + * free the memory after the reader has been destroyed. + * \param acc_len The size in Bytes of \p acc. + * + * \return \c 0 on success. + * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure. + */ +int mbedtls_mps_reader_init(mbedtls_mps_reader *reader, + unsigned char *acc, + mbedtls_mps_size_t acc_len); + +/** + * \brief Free a reader object + * + * \param reader The reader to be freed. + * + * \return \c 0 on success. + * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure. + */ +int mbedtls_mps_reader_free(mbedtls_mps_reader *reader); + +/** + * \brief Pass chunk of data for the reader to manage. + * + * \param reader The reader context to use. The reader must be + * in producing mode. + * \param buf The buffer to be managed by the reader. + * \param buflen The size in Bytes of \p buffer. + * + * \return \c 0 on success. In this case, the reader will be + * moved to consuming mode and obtains read access + * of \p buf until mbedtls_mps_reader_reclaim() + * is called. It is the responsibility of the caller + * to ensure that the \p buf persists and is not changed + * between successful calls to mbedtls_mps_reader_feed() + * and mbedtls_mps_reader_reclaim(). + * \return \c MBEDTLS_ERR_MPS_READER_NEED_MORE if more input data is + * required to fulfill a previous request to mbedtls_mps_reader_get(). + * In this case, the reader remains in producing mode and + * takes no ownership of the provided buffer (an internal copy + * is made instead). + * \return Another negative \c MBEDTLS_ERR_READER_XXX error code on + * different kinds of failures. + */ +int mbedtls_mps_reader_feed(mbedtls_mps_reader *reader, + unsigned char *buf, + mbedtls_mps_size_t buflen); + +/** + * \brief Reclaim reader's access to the current input buffer. + * + * \param reader The reader context to use. The reader must be + * in consuming mode. + * \param paused If not \c NULL, the integer at address \p paused will be + * modified to indicate whether the reader has been paused + * (value \c 1) or not (value \c 0). Pausing happens if there + * is uncommitted data and a previous request to + * mbedtls_mps_reader_get() has exceeded the bounds of the + * input buffer. + * + * \return \c 0 on success. + * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure. + */ +int mbedtls_mps_reader_reclaim(mbedtls_mps_reader *reader, + int *paused); + +/* + * Usage API (Upper layer) + */ + +/** + * \brief Request data from the reader. + * + * \param reader The reader context to use. The reader must + * be in consuming mode. + * \param desired The desired amount of data to be read, in Bytes. + * \param buffer The address to store the buffer pointer in. + * This must not be \c NULL. + * \param buflen The address to store the actual buffer + * length in, or \c NULL. + * + * \return \c 0 on success. In this case, \c *buf holds the + * address of a buffer of size \c *buflen + * (if \c buflen != \c NULL) or \c desired + * (if \c buflen == \c NULL). The user has read access + * to the buffer and guarantee of stability of the data + * until the next call to mbedtls_mps_reader_reclaim(). + * \return #MBEDTLS_ERR_MPS_READER_OUT_OF_DATA if there is not enough + * data available to serve the get request. In this case, the + * reader remains intact and in consuming mode, and the consumer + * should retry the call after a successful cycle of + * mbedtls_mps_reader_reclaim() and mbedtls_mps_reader_feed(). + * If, after such a cycle, the consumer requests a different + * amount of data, the result is implementation-defined; + * progress is guaranteed only if the same amount of data + * is requested after a mbedtls_mps_reader_reclaim() and + * mbedtls_mps_reader_feed() cycle. + * \return Another negative \c MBEDTLS_ERR_READER_XXX error + * code for different kinds of failure. + * + * \note Passing \c NULL as \p buflen is a convenient way to + * indicate that fragmentation is not tolerated. + * It's functionally equivalent to passing a valid + * address as buflen and checking \c *buflen == \c desired + * afterwards. + */ +int mbedtls_mps_reader_get(mbedtls_mps_reader *reader, + mbedtls_mps_size_t desired, + unsigned char **buffer, + mbedtls_mps_size_t *buflen); + +/** + * \brief Mark data obtained from mbedtls_mps_reader_get() as processed. + * + * This call indicates that all data received from prior calls to + * mbedtls_mps_reader_get() has been or will have been + * processed when mbedtls_mps_reader_reclaim() is called, + * and thus need not be backed up. + * + * This function has no user observable effect until + * mbedtls_mps_reader_reclaim() is called. In particular, + * buffers received from mbedtls_mps_reader_get() remain + * valid until mbedtls_mps_reader_reclaim() is called. + * + * \param reader The reader context to use. + * + * \return \c 0 on success. + * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure. + * + */ +int mbedtls_mps_reader_commit(mbedtls_mps_reader *reader); + +#endif /* MBEDTLS_READER_H */ diff --git a/r5dev/thirdparty/mbedtls/mps_trace.c b/r5dev/thirdparty/mbedtls/mps_trace.c new file mode 100644 index 00000000..9ba1f85e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/mps_trace.c @@ -0,0 +1,126 @@ +/* + * Message Processing Stack, Trace module + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of Mbed TLS (https://tls.mbed.org) + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#include "mps_common.h" + +#if defined(MBEDTLS_MPS_ENABLE_TRACE) + +#include "mps_trace.h" +#include + +static int trace_depth = 0; + +#define color_default "\x1B[0m" +#define color_red "\x1B[1;31m" +#define color_green "\x1B[1;32m" +#define color_yellow "\x1B[1;33m" +#define color_blue "\x1B[1;34m" +#define color_magenta "\x1B[1;35m" +#define color_cyan "\x1B[1;36m" +#define color_white "\x1B[1;37m" + +static char const *colors[] = +{ + color_default, + color_green, + color_yellow, + color_magenta, + color_cyan, + color_blue, + color_white +}; + +#define MPS_TRACE_BUF_SIZE 100 + +void mbedtls_mps_trace_print_msg(int id, int line, const char *format, ...) +{ + int ret; + char str[MPS_TRACE_BUF_SIZE]; + va_list argp; + va_start(argp, format); + ret = mbedtls_vsnprintf(str, MPS_TRACE_BUF_SIZE, format, argp); + va_end(argp); + + if (ret >= 0 && ret < MPS_TRACE_BUF_SIZE) { + str[ret] = '\0'; + mbedtls_printf("[%d|L%d]: %s\n", id, line, str); + } +} + +int mbedtls_mps_trace_get_depth() +{ + return trace_depth; +} +void mbedtls_mps_trace_dec_depth() +{ + trace_depth--; +} +void mbedtls_mps_trace_inc_depth() +{ + trace_depth++; +} + +void mbedtls_mps_trace_color(int id) +{ + if (id > (int) (sizeof(colors) / sizeof(*colors))) { + return; + } + printf("%s", colors[id]); +} + +void mbedtls_mps_trace_indent(int level, mbedtls_mps_trace_type ty) +{ + if (level > 0) { + while (--level) { + printf("| "); + } + + printf("| "); + } + + switch (ty) { + case MBEDTLS_MPS_TRACE_TYPE_COMMENT: + mbedtls_printf("@ "); + break; + + case MBEDTLS_MPS_TRACE_TYPE_CALL: + mbedtls_printf("+--> "); + break; + + case MBEDTLS_MPS_TRACE_TYPE_ERROR: + mbedtls_printf("E "); + break; + + case MBEDTLS_MPS_TRACE_TYPE_RETURN: + mbedtls_printf("< "); + break; + + default: + break; + } +} + +#endif /* MBEDTLS_MPS_ENABLE_TRACE */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/r5dev/thirdparty/mbedtls/mps_trace.h b/r5dev/thirdparty/mbedtls/mps_trace.h new file mode 100644 index 00000000..6f0455f0 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/mps_trace.h @@ -0,0 +1,168 @@ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/** + * \file mps_trace.h + * + * \brief Tracing module for MPS + */ + +#ifndef MBEDTLS_MPS_MBEDTLS_MPS_TRACE_H +#define MBEDTLS_MPS_MBEDTLS_MPS_TRACE_H + +#include "common.h" +#include "mps_common.h" +#include "mps_trace.h" + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_MPS_ENABLE_TRACE) + +/* + * Adapt this to enable/disable tracing output + * from the various layers of the MPS. + */ + +#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_1 +#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_2 +#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_3 +#define MBEDTLS_MPS_TRACE_ENABLE_LAYER_4 +#define MBEDTLS_MPS_TRACE_ENABLE_READER +#define MBEDTLS_MPS_TRACE_ENABLE_WRITER + +/* + * To use the existing trace module, only change + * MBEDTLS_MPS_TRACE_ENABLE_XXX above, but don't modify the + * rest of this file. + */ + +typedef enum { + MBEDTLS_MPS_TRACE_TYPE_COMMENT, + MBEDTLS_MPS_TRACE_TYPE_CALL, + MBEDTLS_MPS_TRACE_TYPE_ERROR, + MBEDTLS_MPS_TRACE_TYPE_RETURN +} mbedtls_mps_trace_type; + +#define MBEDTLS_MPS_TRACE_BIT_LAYER_1 1 +#define MBEDTLS_MPS_TRACE_BIT_LAYER_2 2 +#define MBEDTLS_MPS_TRACE_BIT_LAYER_3 3 +#define MBEDTLS_MPS_TRACE_BIT_LAYER_4 4 +#define MBEDTLS_MPS_TRACE_BIT_WRITER 5 +#define MBEDTLS_MPS_TRACE_BIT_READER 6 + +#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_1) +#define MBEDTLS_MPS_TRACE_MASK_LAYER_1 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_1) +#else +#define MBEDTLS_MPS_TRACE_MASK_LAYER_1 0 +#endif + +#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_2) +#define MBEDTLS_MPS_TRACE_MASK_LAYER_2 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_2) +#else +#define MBEDTLS_MPS_TRACE_MASK_LAYER_2 0 +#endif + +#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_3) +#define MBEDTLS_MPS_TRACE_MASK_LAYER_3 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_3) +#else +#define MBEDTLS_MPS_TRACE_MASK_LAYER_3 0 +#endif + +#if defined(MBEDTLS_MPS_TRACE_ENABLE_LAYER_4) +#define MBEDTLS_MPS_TRACE_MASK_LAYER_4 (1u << MBEDTLS_MPS_TRACE_BIT_LAYER_4) +#else +#define MBEDTLS_MPS_TRACE_MASK_LAYER_4 0 +#endif + +#if defined(MBEDTLS_MPS_TRACE_ENABLE_READER) +#define MBEDTLS_MPS_TRACE_MASK_READER (1u << MBEDTLS_MPS_TRACE_BIT_READER) +#else +#define MBEDTLS_MPS_TRACE_MASK_READER 0 +#endif + +#if defined(MBEDTLS_MPS_TRACE_ENABLE_WRITER) +#define MBEDTLS_MPS_TRACE_MASK_WRITER (1u << MBEDTLS_MPS_TRACE_BIT_WRITER) +#else +#define MBEDTLS_MPS_TRACE_MASK_WRITER 0 +#endif + +#define MBEDTLS_MPS_TRACE_MASK (MBEDTLS_MPS_TRACE_MASK_LAYER_1 | \ + MBEDTLS_MPS_TRACE_MASK_LAYER_2 | \ + MBEDTLS_MPS_TRACE_MASK_LAYER_3 | \ + MBEDTLS_MPS_TRACE_MASK_LAYER_4 | \ + MBEDTLS_MPS_TRACE_MASK_READER | \ + MBEDTLS_MPS_TRACE_MASK_WRITER) + +/* We have to avoid globals because E-ACSL chokes on them... + * Wrap everything in stub functions. */ +int mbedtls_mps_trace_get_depth(void); +void mbedtls_mps_trace_inc_depth(void); +void mbedtls_mps_trace_dec_depth(void); + +void mbedtls_mps_trace_color(int id); +void mbedtls_mps_trace_indent(int level, mbedtls_mps_trace_type ty); + +void mbedtls_mps_trace_print_msg(int id, int line, const char *format, ...); + +#define MBEDTLS_MPS_TRACE(type, ...) \ + do { \ + if (!(MBEDTLS_MPS_TRACE_MASK & (1u << mbedtls_mps_trace_id))) \ + break; \ + mbedtls_mps_trace_indent(mbedtls_mps_trace_get_depth(), type); \ + mbedtls_mps_trace_color(mbedtls_mps_trace_id); \ + mbedtls_mps_trace_print_msg(mbedtls_mps_trace_id, __LINE__, __VA_ARGS__); \ + mbedtls_mps_trace_color(0); \ + } while (0) + +#define MBEDTLS_MPS_TRACE_INIT(...) \ + do { \ + if (!(MBEDTLS_MPS_TRACE_MASK & (1u << mbedtls_mps_trace_id))) \ + break; \ + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_CALL, __VA_ARGS__); \ + mbedtls_mps_trace_inc_depth(); \ + } while (0) + +#define MBEDTLS_MPS_TRACE_END(val) \ + do { \ + if (!(MBEDTLS_MPS_TRACE_MASK & (1u << mbedtls_mps_trace_id))) \ + break; \ + MBEDTLS_MPS_TRACE(MBEDTLS_MPS_TRACE_TYPE_RETURN, "%d (-%#04x)", \ + (int) (val), -((unsigned) (val))); \ + mbedtls_mps_trace_dec_depth(); \ + } while (0) + +#define MBEDTLS_MPS_TRACE_RETURN(val) \ + do { \ + /* Breaks tail recursion. */ \ + int ret__ = val; \ + MBEDTLS_MPS_TRACE_END(ret__); \ + return ret__; \ + } while (0) + +#else /* MBEDTLS_MPS_TRACE */ + +#define MBEDTLS_MPS_TRACE(type, ...) do { } while (0) +#define MBEDTLS_MPS_TRACE_INIT(...) do { } while (0) +#define MBEDTLS_MPS_TRACE_END do { } while (0) + +#define MBEDTLS_MPS_TRACE_RETURN(val) return val; + +#endif /* MBEDTLS_MPS_TRACE */ + +#endif /* MBEDTLS_MPS_MBEDTLS_MPS_TRACE_H */ diff --git a/r5dev/thirdparty/mbedtls/net_sockets.c b/r5dev/thirdparty/mbedtls/net_sockets.c new file mode 100644 index 00000000..e63d08b1 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/net_sockets.c @@ -0,0 +1,712 @@ +/* + * TCP/IP or UDP/IP networking functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Enable definition of getaddrinfo() even when compiling with -std=c99. Must + * be set before mbedtls_config.h, which pulls in glibc's features.h indirectly. + * Harmless on other platforms. */ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L +#endif +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 /* sockaddr_storage */ +#endif + +#include "common.h" + +#if defined(MBEDTLS_NET_C) + +#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) && !defined(__midipix__) +#error "This module only works on Unix and Windows, see MBEDTLS_NET_C in mbedtls_config.h" +#endif + +#include "mbedtls/platform.h" + +#include "mbedtls/net_sockets.h" +#include "mbedtls/error.h" + +#include + +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + +#define IS_EINTR(ret) ((ret) == WSAEINTR) + +#if !defined(_WIN32_WINNT) +/* Enables getaddrinfo() & Co */ +#define _WIN32_WINNT 0x0501 +#endif + +#include + +#include +#include +#if (_WIN32_WINNT < 0x0501) +#include +#endif + +#if defined(_MSC_VER) +#if defined(_WIN32_WCE) +#pragma comment( lib, "ws2.lib" ) +#else +#pragma comment( lib, "ws2_32.lib" ) +#endif +#endif /* _MSC_VER */ + +#define read(fd, buf, len) recv(fd, (char *) (buf), (int) (len), 0) +#define write(fd, buf, len) send(fd, (char *) (buf), (int) (len), 0) +#define close(fd) closesocket(fd) + +static int wsa_init_done = 0; + +#else /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_EINTR(ret) ((ret) == EINTR) + +#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ + +/* Some MS functions want int and MSVC warns if we pass size_t, + * but the standard functions use socklen_t, so cast only for MSVC */ +#if defined(_MSC_VER) +#define MSVC_INT_CAST (int) +#else +#define MSVC_INT_CAST +#endif + +#include + +#if defined(MBEDTLS_HAVE_TIME) +#include +#endif + +#include + +/* + * Prepare for using the sockets interface + */ +static int net_prepare(void) +{ +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + WSADATA wsaData; + + if (wsa_init_done == 0) { + if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { + return MBEDTLS_ERR_NET_SOCKET_FAILED; + } + + wsa_init_done = 1; + } +#else +#if !defined(EFIX64) && !defined(EFI32) + signal(SIGPIPE, SIG_IGN); +#endif +#endif + return 0; +} + +/* + * Return 0 if the file descriptor is valid, an error otherwise. + * If for_select != 0, check whether the file descriptor is within the range + * allowed for fd_set used for the FD_xxx macros and the select() function. + */ +static int check_fd(int fd, int for_select) +{ + if (fd < 0) { + return MBEDTLS_ERR_NET_INVALID_CONTEXT; + } + +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + (void) for_select; +#else + /* A limitation of select() is that it only works with file descriptors + * that are strictly less than FD_SETSIZE. This is a limitation of the + * fd_set type. Error out early, because attempting to call FD_SET on a + * large file descriptor is a buffer overflow on typical platforms. */ + if (for_select && fd >= FD_SETSIZE) { + return MBEDTLS_ERR_NET_POLL_FAILED; + } +#endif + + return 0; +} + +/* + * Initialize a context + */ +void mbedtls_net_init(mbedtls_net_context *ctx) +{ + ctx->fd = -1; +} + +/* + * Initiate a TCP connection with host:port and the given protocol + */ +int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, + const char *port, int proto) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + struct addrinfo hints, *addr_list, *cur; + + if ((ret = net_prepare()) != 0) { + return ret; + } + + /* Do name resolution with both IPv6 and IPv4 */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = proto == MBEDTLS_NET_PROTO_UDP ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = proto == MBEDTLS_NET_PROTO_UDP ? IPPROTO_UDP : IPPROTO_TCP; + + if (getaddrinfo(host, port, &hints, &addr_list) != 0) { + return MBEDTLS_ERR_NET_UNKNOWN_HOST; + } + + /* Try the sockaddrs until a connection succeeds */ + ret = MBEDTLS_ERR_NET_UNKNOWN_HOST; + for (cur = addr_list; cur != NULL; cur = cur->ai_next) { + ctx->fd = (int) socket(cur->ai_family, cur->ai_socktype, + cur->ai_protocol); + if (ctx->fd < 0) { + ret = MBEDTLS_ERR_NET_SOCKET_FAILED; + continue; + } + + if (connect(ctx->fd, cur->ai_addr, MSVC_INT_CAST cur->ai_addrlen) == 0) { + ret = 0; + break; + } + + close(ctx->fd); + ret = MBEDTLS_ERR_NET_CONNECT_FAILED; + } + + freeaddrinfo(addr_list); + + return ret; +} + +/* + * Create a listening socket on bind_ip:port + */ +int mbedtls_net_bind(mbedtls_net_context *ctx, const char *bind_ip, const char *port, int proto) +{ + int n, ret; + struct addrinfo hints, *addr_list, *cur; + + if ((ret = net_prepare()) != 0) { + return ret; + } + + /* Bind to IPv6 and/or IPv4, but only in the desired protocol */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = proto == MBEDTLS_NET_PROTO_UDP ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = proto == MBEDTLS_NET_PROTO_UDP ? IPPROTO_UDP : IPPROTO_TCP; + if (bind_ip == NULL) { + hints.ai_flags = AI_PASSIVE; + } + + if (getaddrinfo(bind_ip, port, &hints, &addr_list) != 0) { + return MBEDTLS_ERR_NET_UNKNOWN_HOST; + } + + /* Try the sockaddrs until a binding succeeds */ + ret = MBEDTLS_ERR_NET_UNKNOWN_HOST; + for (cur = addr_list; cur != NULL; cur = cur->ai_next) { + ctx->fd = (int) socket(cur->ai_family, cur->ai_socktype, + cur->ai_protocol); + if (ctx->fd < 0) { + ret = MBEDTLS_ERR_NET_SOCKET_FAILED; + continue; + } + + n = 1; + if (setsockopt(ctx->fd, SOL_SOCKET, SO_REUSEADDR, + (const char *) &n, sizeof(n)) != 0) { + close(ctx->fd); + ret = MBEDTLS_ERR_NET_SOCKET_FAILED; + continue; + } + + if (bind(ctx->fd, cur->ai_addr, MSVC_INT_CAST cur->ai_addrlen) != 0) { + close(ctx->fd); + ret = MBEDTLS_ERR_NET_BIND_FAILED; + continue; + } + + /* Listen only makes sense for TCP */ + if (proto == MBEDTLS_NET_PROTO_TCP) { + if (listen(ctx->fd, MBEDTLS_NET_LISTEN_BACKLOG) != 0) { + close(ctx->fd); + ret = MBEDTLS_ERR_NET_LISTEN_FAILED; + continue; + } + } + + /* Bind was successful */ + ret = 0; + break; + } + + freeaddrinfo(addr_list); + + return ret; + +} + +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) +/* + * Check if the requested operation would be blocking on a non-blocking socket + * and thus 'failed' with a negative return value. + */ +static int net_would_block(const mbedtls_net_context *ctx) +{ + ((void) ctx); + return WSAGetLastError() == WSAEWOULDBLOCK; +} +#else +/* + * Check if the requested operation would be blocking on a non-blocking socket + * and thus 'failed' with a negative return value. + * + * Note: on a blocking socket this function always returns 0! + */ +static int net_would_block(const mbedtls_net_context *ctx) +{ + int err = errno; + + /* + * Never return 'WOULD BLOCK' on a blocking socket + */ + if ((fcntl(ctx->fd, F_GETFL) & O_NONBLOCK) != O_NONBLOCK) { + errno = err; + return 0; + } + + switch (errno = err) { +#if defined EAGAIN + case EAGAIN: +#endif +#if defined EWOULDBLOCK && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + return 1; + } + return 0; +} +#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */ + +/* + * Accept a connection from a remote client + */ +int mbedtls_net_accept(mbedtls_net_context *bind_ctx, + mbedtls_net_context *client_ctx, + void *client_ip, size_t buf_size, size_t *ip_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int type; + + struct sockaddr_storage client_addr; + +#if defined(__socklen_t_defined) || defined(_SOCKLEN_T) || \ + defined(_SOCKLEN_T_DECLARED) || defined(__DEFINED_socklen_t) || \ + defined(socklen_t) || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) + socklen_t n = (socklen_t) sizeof(client_addr); + socklen_t type_len = (socklen_t) sizeof(type); +#else + int n = (int) sizeof(client_addr); + int type_len = (int) sizeof(type); +#endif + + /* Is this a TCP or UDP socket? */ + if (getsockopt(bind_ctx->fd, SOL_SOCKET, SO_TYPE, + (void *) &type, &type_len) != 0 || + (type != SOCK_STREAM && type != SOCK_DGRAM)) { + return MBEDTLS_ERR_NET_ACCEPT_FAILED; + } + + if (type == SOCK_STREAM) { + /* TCP: actual accept() */ + ret = client_ctx->fd = (int) accept(bind_ctx->fd, + (struct sockaddr *) &client_addr, &n); + } else { + /* UDP: wait for a message, but keep it in the queue */ + char buf[1] = { 0 }; + + ret = (int) recvfrom(bind_ctx->fd, buf, sizeof(buf), MSG_PEEK, + (struct sockaddr *) &client_addr, &n); + +#if defined(_WIN32) + if (ret == SOCKET_ERROR && + WSAGetLastError() == WSAEMSGSIZE) { + /* We know buf is too small, thanks, just peeking here */ + ret = 0; + } +#endif + } + + if (ret < 0) { + if (net_would_block(bind_ctx) != 0) { + return MBEDTLS_ERR_SSL_WANT_READ; + } + + return MBEDTLS_ERR_NET_ACCEPT_FAILED; + } + + /* UDP: hijack the listening socket to communicate with the client, + * then bind a new socket to accept new connections */ + if (type != SOCK_STREAM) { + struct sockaddr_storage local_addr; + int one = 1; + + if (connect(bind_ctx->fd, (struct sockaddr *) &client_addr, n) != 0) { + return MBEDTLS_ERR_NET_ACCEPT_FAILED; + } + + client_ctx->fd = bind_ctx->fd; + bind_ctx->fd = -1; /* In case we exit early */ + + n = sizeof(struct sockaddr_storage); + if (getsockname(client_ctx->fd, + (struct sockaddr *) &local_addr, &n) != 0 || + (bind_ctx->fd = (int) socket(local_addr.ss_family, + SOCK_DGRAM, IPPROTO_UDP)) < 0 || + setsockopt(bind_ctx->fd, SOL_SOCKET, SO_REUSEADDR, + (const char *) &one, sizeof(one)) != 0) { + return MBEDTLS_ERR_NET_SOCKET_FAILED; + } + + if (bind(bind_ctx->fd, (struct sockaddr *) &local_addr, n) != 0) { + return MBEDTLS_ERR_NET_BIND_FAILED; + } + } + + if (client_ip != NULL) { + if (client_addr.ss_family == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *) &client_addr; + *ip_len = sizeof(addr4->sin_addr.s_addr); + + if (buf_size < *ip_len) { + return MBEDTLS_ERR_NET_BUFFER_TOO_SMALL; + } + + memcpy(client_ip, &addr4->sin_addr.s_addr, *ip_len); + } else { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &client_addr; + *ip_len = sizeof(addr6->sin6_addr.s6_addr); + + if (buf_size < *ip_len) { + return MBEDTLS_ERR_NET_BUFFER_TOO_SMALL; + } + + memcpy(client_ip, &addr6->sin6_addr.s6_addr, *ip_len); + } + } + + return 0; +} + +/* + * Set the socket blocking or non-blocking + */ +int mbedtls_net_set_block(mbedtls_net_context *ctx) +{ +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + u_long n = 0; + return ioctlsocket(ctx->fd, FIONBIO, &n); +#else + return fcntl(ctx->fd, F_SETFL, fcntl(ctx->fd, F_GETFL) & ~O_NONBLOCK); +#endif +} + +int mbedtls_net_set_nonblock(mbedtls_net_context *ctx) +{ +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + u_long n = 1; + return ioctlsocket(ctx->fd, FIONBIO, &n); +#else + return fcntl(ctx->fd, F_SETFL, fcntl(ctx->fd, F_GETFL) | O_NONBLOCK); +#endif +} + +/* + * Check if data is available on the socket + */ + +int mbedtls_net_poll(mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + struct timeval tv; + + fd_set read_fds; + fd_set write_fds; + + int fd = ctx->fd; + + ret = check_fd(fd, 1); + if (ret != 0) { + return ret; + } + +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + /* Ensure that memory sanitizers consider read_fds and write_fds as + * initialized even on platforms such as Glibc/x86_64 where FD_ZERO + * is implemented in assembly. */ + memset(&read_fds, 0, sizeof(read_fds)); + memset(&write_fds, 0, sizeof(write_fds)); +#endif +#endif + + FD_ZERO(&read_fds); + if (rw & MBEDTLS_NET_POLL_READ) { + rw &= ~MBEDTLS_NET_POLL_READ; + FD_SET(fd, &read_fds); + } + + FD_ZERO(&write_fds); + if (rw & MBEDTLS_NET_POLL_WRITE) { + rw &= ~MBEDTLS_NET_POLL_WRITE; + FD_SET(fd, &write_fds); + } + + if (rw != 0) { + return MBEDTLS_ERR_NET_BAD_INPUT_DATA; + } + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + do { + ret = select(fd + 1, &read_fds, &write_fds, NULL, + timeout == (uint32_t) -1 ? NULL : &tv); + } while (IS_EINTR(ret)); + + if (ret < 0) { + return MBEDTLS_ERR_NET_POLL_FAILED; + } + + ret = 0; + if (FD_ISSET(fd, &read_fds)) { + ret |= MBEDTLS_NET_POLL_READ; + } + if (FD_ISSET(fd, &write_fds)) { + ret |= MBEDTLS_NET_POLL_WRITE; + } + + return ret; +} + +/* + * Portable usleep helper + */ +void mbedtls_net_usleep(unsigned long usec) +{ +#if defined(_WIN32) + Sleep((usec + 999) / 1000); +#else + struct timeval tv; + tv.tv_sec = usec / 1000000; +#if defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__)) + tv.tv_usec = (suseconds_t) usec % 1000000; +#else + tv.tv_usec = usec % 1000000; +#endif + select(0, NULL, NULL, NULL, &tv); +#endif +} + +/* + * Read at most 'len' characters + */ +int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int fd = ((mbedtls_net_context *) ctx)->fd; + + ret = check_fd(fd, 0); + if (ret != 0) { + return ret; + } + + ret = (int) read(fd, buf, len); + + if (ret < 0) { + if (net_would_block(ctx) != 0) { + return MBEDTLS_ERR_SSL_WANT_READ; + } + +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + if (WSAGetLastError() == WSAECONNRESET) { + return MBEDTLS_ERR_NET_CONN_RESET; + } +#else + if (errno == EPIPE || errno == ECONNRESET) { + return MBEDTLS_ERR_NET_CONN_RESET; + } + + if (errno == EINTR) { + return MBEDTLS_ERR_SSL_WANT_READ; + } +#endif + + return MBEDTLS_ERR_NET_RECV_FAILED; + } + + return ret; +} + +/* + * Read at most 'len' characters, blocking for at most 'timeout' ms + */ +int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf, + size_t len, uint32_t timeout) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + struct timeval tv; + fd_set read_fds; + int fd = ((mbedtls_net_context *) ctx)->fd; + + ret = check_fd(fd, 1); + if (ret != 0) { + return ret; + } + + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + ret = select(fd + 1, &read_fds, NULL, NULL, timeout == 0 ? NULL : &tv); + + /* Zero fds ready means we timed out */ + if (ret == 0) { + return MBEDTLS_ERR_SSL_TIMEOUT; + } + + if (ret < 0) { +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + if (WSAGetLastError() == WSAEINTR) { + return MBEDTLS_ERR_SSL_WANT_READ; + } +#else + if (errno == EINTR) { + return MBEDTLS_ERR_SSL_WANT_READ; + } +#endif + + return MBEDTLS_ERR_NET_RECV_FAILED; + } + + /* This call will not block */ + return mbedtls_net_recv(ctx, buf, len); +} + +/* + * Write at most 'len' characters + */ +int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int fd = ((mbedtls_net_context *) ctx)->fd; + + ret = check_fd(fd, 0); + if (ret != 0) { + return ret; + } + + ret = (int) write(fd, buf, len); + + if (ret < 0) { + if (net_would_block(ctx) != 0) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + +#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \ + !defined(EFI32) + if (WSAGetLastError() == WSAECONNRESET) { + return MBEDTLS_ERR_NET_CONN_RESET; + } +#else + if (errno == EPIPE || errno == ECONNRESET) { + return MBEDTLS_ERR_NET_CONN_RESET; + } + + if (errno == EINTR) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } +#endif + + return MBEDTLS_ERR_NET_SEND_FAILED; + } + + return ret; +} + +/* + * Close the connection + */ +void mbedtls_net_close(mbedtls_net_context *ctx) +{ + if (ctx->fd == -1) { + return; + } + + close(ctx->fd); + + ctx->fd = -1; +} + +/* + * Gracefully close the connection + */ +void mbedtls_net_free(mbedtls_net_context *ctx) +{ + if (ctx->fd == -1) { + return; + } + + shutdown(ctx->fd, 2); + close(ctx->fd); + + ctx->fd = -1; +} + +#endif /* MBEDTLS_NET_C */ diff --git a/r5dev/thirdparty/mbedtls/nist_kw.c b/r5dev/thirdparty/mbedtls/nist_kw.c new file mode 100644 index 00000000..5817bf4f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/nist_kw.c @@ -0,0 +1,687 @@ +/* + * Implementation of NIST SP 800-38F key wrapping, supporting KW and KWP modes + * only + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Definition of Key Wrapping: + * https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38F.pdf + * RFC 3394 "Advanced Encryption Standard (AES) Key Wrap Algorithm" + * RFC 5649 "Advanced Encryption Standard (AES) Key Wrap with Padding Algorithm" + * + * Note: RFC 3394 defines different methodology for intermediate operations for + * the wrapping and unwrapping operation than the definition in NIST SP 800-38F. + */ + +#include "common.h" + +#if defined(MBEDTLS_NIST_KW_C) + +#include "mbedtls/nist_kw.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "mbedtls/constant_time.h" + +#include +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_NIST_KW_ALT) + +#define KW_SEMIBLOCK_LENGTH 8 +#define MIN_SEMIBLOCKS_COUNT 3 + +/*! The 64-bit default integrity check value (ICV) for KW mode. */ +static const unsigned char NIST_KW_ICV1[] = { 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6 }; +/*! The 32-bit default integrity check value (ICV) for KWP mode. */ +static const unsigned char NIST_KW_ICV2[] = { 0xA6, 0x59, 0x59, 0xA6 }; + +/* + * Initialize context + */ +void mbedtls_nist_kw_init(mbedtls_nist_kw_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_nist_kw_context)); +} + +int mbedtls_nist_kw_setkey(mbedtls_nist_kw_context *ctx, + mbedtls_cipher_id_t cipher, + const unsigned char *key, + unsigned int keybits, + const int is_wrap) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_cipher_info_t *cipher_info; + + cipher_info = mbedtls_cipher_info_from_values(cipher, + keybits, + MBEDTLS_MODE_ECB); + if (cipher_info == NULL) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (cipher_info->block_size != 16) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + /* + * SP 800-38F currently defines AES cipher as the only block cipher allowed: + * "For KW and KWP, the underlying block cipher shall be approved, and the + * block size shall be 128 bits. Currently, the AES block cipher, with key + * lengths of 128, 192, or 256 bits, is the only block cipher that fits + * this profile." + * Currently we don't support other 128 bit block ciphers for key wrapping, + * such as Camellia and Aria. + */ + if (cipher != MBEDTLS_CIPHER_ID_AES) { + return MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + } + + mbedtls_cipher_free(&ctx->cipher_ctx); + + if ((ret = mbedtls_cipher_setup(&ctx->cipher_ctx, cipher_info)) != 0) { + return ret; + } + + if ((ret = mbedtls_cipher_setkey(&ctx->cipher_ctx, key, keybits, + is_wrap ? MBEDTLS_ENCRYPT : + MBEDTLS_DECRYPT) + ) != 0) { + return ret; + } + + return 0; +} + +/* + * Free context + */ +void mbedtls_nist_kw_free(mbedtls_nist_kw_context *ctx) +{ + mbedtls_cipher_free(&ctx->cipher_ctx); + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_nist_kw_context)); +} + +/* + * Helper function for Xoring the uint64_t "t" with the encrypted A. + * Defined in NIST SP 800-38F section 6.1 + */ +static void calc_a_xor_t(unsigned char A[KW_SEMIBLOCK_LENGTH], uint64_t t) +{ + size_t i = 0; + for (i = 0; i < sizeof(t); i++) { + A[i] ^= (t >> ((sizeof(t) - 1 - i) * 8)) & 0xff; + } +} + +/* + * KW-AE as defined in SP 800-38F section 6.2 + * KWP-AE as defined in SP 800-38F section 6.3 + */ +int mbedtls_nist_kw_wrap(mbedtls_nist_kw_context *ctx, + mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t *out_len, size_t out_size) +{ + int ret = 0; + size_t semiblocks = 0; + size_t s; + size_t olen, padlen = 0; + uint64_t t = 0; + unsigned char outbuff[KW_SEMIBLOCK_LENGTH * 2]; + unsigned char inbuff[KW_SEMIBLOCK_LENGTH * 2]; + + *out_len = 0; + /* + * Generate the String to work on + */ + if (mode == MBEDTLS_KW_MODE_KW) { + if (out_size < in_len + KW_SEMIBLOCK_LENGTH) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + /* + * According to SP 800-38F Table 1, the plaintext length for KW + * must be between 2 to 2^54-1 semiblocks inclusive. + */ + if (in_len < 16 || +#if SIZE_MAX > 0x1FFFFFFFFFFFFF8 + in_len > 0x1FFFFFFFFFFFFF8 || +#endif + in_len % KW_SEMIBLOCK_LENGTH != 0) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + memcpy(output, NIST_KW_ICV1, KW_SEMIBLOCK_LENGTH); + memmove(output + KW_SEMIBLOCK_LENGTH, input, in_len); + } else { + if (in_len % 8 != 0) { + padlen = (8 - (in_len % 8)); + } + + if (out_size < in_len + KW_SEMIBLOCK_LENGTH + padlen) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + /* + * According to SP 800-38F Table 1, the plaintext length for KWP + * must be between 1 and 2^32-1 octets inclusive. + */ + if (in_len < 1 +#if SIZE_MAX > 0xFFFFFFFF + || in_len > 0xFFFFFFFF +#endif + ) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + memcpy(output, NIST_KW_ICV2, KW_SEMIBLOCK_LENGTH / 2); + MBEDTLS_PUT_UINT32_BE((in_len & 0xffffffff), output, + KW_SEMIBLOCK_LENGTH / 2); + + memcpy(output + KW_SEMIBLOCK_LENGTH, input, in_len); + memset(output + KW_SEMIBLOCK_LENGTH + in_len, 0, padlen); + } + semiblocks = ((in_len + padlen) / KW_SEMIBLOCK_LENGTH) + 1; + + s = 6 * (semiblocks - 1); + + if (mode == MBEDTLS_KW_MODE_KWP + && in_len <= KW_SEMIBLOCK_LENGTH) { + memcpy(inbuff, output, 16); + ret = mbedtls_cipher_update(&ctx->cipher_ctx, + inbuff, 16, output, &olen); + if (ret != 0) { + goto cleanup; + } + } else { + unsigned char *R2 = output + KW_SEMIBLOCK_LENGTH; + unsigned char *A = output; + + /* + * Do the wrapping function W, as defined in RFC 3394 section 2.2.1 + */ + if (semiblocks < MIN_SEMIBLOCKS_COUNT) { + ret = MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + goto cleanup; + } + + /* Calculate intermediate values */ + for (t = 1; t <= s; t++) { + memcpy(inbuff, A, KW_SEMIBLOCK_LENGTH); + memcpy(inbuff + KW_SEMIBLOCK_LENGTH, R2, KW_SEMIBLOCK_LENGTH); + + ret = mbedtls_cipher_update(&ctx->cipher_ctx, + inbuff, 16, outbuff, &olen); + if (ret != 0) { + goto cleanup; + } + + memcpy(A, outbuff, KW_SEMIBLOCK_LENGTH); + calc_a_xor_t(A, t); + + memcpy(R2, outbuff + KW_SEMIBLOCK_LENGTH, KW_SEMIBLOCK_LENGTH); + R2 += KW_SEMIBLOCK_LENGTH; + if (R2 >= output + (semiblocks * KW_SEMIBLOCK_LENGTH)) { + R2 = output + KW_SEMIBLOCK_LENGTH; + } + } + } + + *out_len = semiblocks * KW_SEMIBLOCK_LENGTH; + +cleanup: + + if (ret != 0) { + memset(output, 0, semiblocks * KW_SEMIBLOCK_LENGTH); + } + mbedtls_platform_zeroize(inbuff, KW_SEMIBLOCK_LENGTH * 2); + mbedtls_platform_zeroize(outbuff, KW_SEMIBLOCK_LENGTH * 2); + + return ret; +} + +/* + * W-1 function as defined in RFC 3394 section 2.2.2 + * This function assumes the following: + * 1. Output buffer is at least of size ( semiblocks - 1 ) * KW_SEMIBLOCK_LENGTH. + * 2. The input buffer is of size semiblocks * KW_SEMIBLOCK_LENGTH. + * 3. Minimal number of semiblocks is 3. + * 4. A is a buffer to hold the first semiblock of the input buffer. + */ +static int unwrap(mbedtls_nist_kw_context *ctx, + const unsigned char *input, size_t semiblocks, + unsigned char A[KW_SEMIBLOCK_LENGTH], + unsigned char *output, size_t *out_len) +{ + int ret = 0; + const size_t s = 6 * (semiblocks - 1); + size_t olen; + uint64_t t = 0; + unsigned char outbuff[KW_SEMIBLOCK_LENGTH * 2]; + unsigned char inbuff[KW_SEMIBLOCK_LENGTH * 2]; + unsigned char *R = NULL; + *out_len = 0; + + if (semiblocks < MIN_SEMIBLOCKS_COUNT) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + memcpy(A, input, KW_SEMIBLOCK_LENGTH); + memmove(output, input + KW_SEMIBLOCK_LENGTH, (semiblocks - 1) * KW_SEMIBLOCK_LENGTH); + R = output + (semiblocks - 2) * KW_SEMIBLOCK_LENGTH; + + /* Calculate intermediate values */ + for (t = s; t >= 1; t--) { + calc_a_xor_t(A, t); + + memcpy(inbuff, A, KW_SEMIBLOCK_LENGTH); + memcpy(inbuff + KW_SEMIBLOCK_LENGTH, R, KW_SEMIBLOCK_LENGTH); + + ret = mbedtls_cipher_update(&ctx->cipher_ctx, + inbuff, 16, outbuff, &olen); + if (ret != 0) { + goto cleanup; + } + + memcpy(A, outbuff, KW_SEMIBLOCK_LENGTH); + + /* Set R as LSB64 of outbuff */ + memcpy(R, outbuff + KW_SEMIBLOCK_LENGTH, KW_SEMIBLOCK_LENGTH); + + if (R == output) { + R = output + (semiblocks - 2) * KW_SEMIBLOCK_LENGTH; + } else { + R -= KW_SEMIBLOCK_LENGTH; + } + } + + *out_len = (semiblocks - 1) * KW_SEMIBLOCK_LENGTH; + +cleanup: + if (ret != 0) { + memset(output, 0, (semiblocks - 1) * KW_SEMIBLOCK_LENGTH); + } + mbedtls_platform_zeroize(inbuff, sizeof(inbuff)); + mbedtls_platform_zeroize(outbuff, sizeof(outbuff)); + + return ret; +} + +/* + * KW-AD as defined in SP 800-38F section 6.2 + * KWP-AD as defined in SP 800-38F section 6.3 + */ +int mbedtls_nist_kw_unwrap(mbedtls_nist_kw_context *ctx, + mbedtls_nist_kw_mode_t mode, + const unsigned char *input, size_t in_len, + unsigned char *output, size_t *out_len, size_t out_size) +{ + int ret = 0; + size_t i, olen; + unsigned char A[KW_SEMIBLOCK_LENGTH]; + unsigned char diff, bad_padding = 0; + + *out_len = 0; + if (out_size < in_len - KW_SEMIBLOCK_LENGTH) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (mode == MBEDTLS_KW_MODE_KW) { + /* + * According to SP 800-38F Table 1, the ciphertext length for KW + * must be between 3 to 2^54 semiblocks inclusive. + */ + if (in_len < 24 || +#if SIZE_MAX > 0x200000000000000 + in_len > 0x200000000000000 || +#endif + in_len % KW_SEMIBLOCK_LENGTH != 0) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + ret = unwrap(ctx, input, in_len / KW_SEMIBLOCK_LENGTH, + A, output, out_len); + if (ret != 0) { + goto cleanup; + } + + /* Check ICV in "constant-time" */ + diff = mbedtls_ct_memcmp(NIST_KW_ICV1, A, KW_SEMIBLOCK_LENGTH); + + if (diff != 0) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + goto cleanup; + } + + } else if (mode == MBEDTLS_KW_MODE_KWP) { + size_t padlen = 0; + uint32_t Plen; + /* + * According to SP 800-38F Table 1, the ciphertext length for KWP + * must be between 2 to 2^29 semiblocks inclusive. + */ + if (in_len < KW_SEMIBLOCK_LENGTH * 2 || +#if SIZE_MAX > 0x100000000 + in_len > 0x100000000 || +#endif + in_len % KW_SEMIBLOCK_LENGTH != 0) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + + if (in_len == KW_SEMIBLOCK_LENGTH * 2) { + unsigned char outbuff[KW_SEMIBLOCK_LENGTH * 2]; + ret = mbedtls_cipher_update(&ctx->cipher_ctx, + input, 16, outbuff, &olen); + if (ret != 0) { + goto cleanup; + } + + memcpy(A, outbuff, KW_SEMIBLOCK_LENGTH); + memcpy(output, outbuff + KW_SEMIBLOCK_LENGTH, KW_SEMIBLOCK_LENGTH); + mbedtls_platform_zeroize(outbuff, sizeof(outbuff)); + *out_len = KW_SEMIBLOCK_LENGTH; + } else { + /* in_len >= KW_SEMIBLOCK_LENGTH * 3 */ + ret = unwrap(ctx, input, in_len / KW_SEMIBLOCK_LENGTH, + A, output, out_len); + if (ret != 0) { + goto cleanup; + } + } + + /* Check ICV in "constant-time" */ + diff = mbedtls_ct_memcmp(NIST_KW_ICV2, A, KW_SEMIBLOCK_LENGTH / 2); + + if (diff != 0) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + Plen = MBEDTLS_GET_UINT32_BE(A, KW_SEMIBLOCK_LENGTH / 2); + + /* + * Plen is the length of the plaintext, when the input is valid. + * If Plen is larger than the plaintext and padding, padlen will be + * larger than 8, because of the type wrap around. + */ + padlen = in_len - KW_SEMIBLOCK_LENGTH - Plen; + if (padlen > 7) { + padlen &= 7; + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + /* Check padding in "constant-time" */ + for (diff = 0, i = 0; i < KW_SEMIBLOCK_LENGTH; i++) { + if (i >= KW_SEMIBLOCK_LENGTH - padlen) { + diff |= output[*out_len - KW_SEMIBLOCK_LENGTH + i]; + } else { + bad_padding |= output[*out_len - KW_SEMIBLOCK_LENGTH + i]; + } + } + + if (diff != 0) { + ret = MBEDTLS_ERR_CIPHER_AUTH_FAILED; + } + + if (ret != 0) { + goto cleanup; + } + memset(output + Plen, 0, padlen); + *out_len = Plen; + } else { + ret = MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE; + goto cleanup; + } + +cleanup: + if (ret != 0) { + memset(output, 0, *out_len); + *out_len = 0; + } + + mbedtls_platform_zeroize(&bad_padding, sizeof(bad_padding)); + mbedtls_platform_zeroize(&diff, sizeof(diff)); + mbedtls_platform_zeroize(A, sizeof(A)); + + return ret; +} + +#endif /* !MBEDTLS_NIST_KW_ALT */ + +#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_AES_C) + +#define KW_TESTS 3 + +/* + * Test vectors taken from NIST + * https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/CAVP-TESTING-BLOCK-CIPHER-MODES#KW + */ +static const unsigned int key_len[KW_TESTS] = { 16, 24, 32 }; + +static const unsigned char kw_key[KW_TESTS][32] = { + { 0x75, 0x75, 0xda, 0x3a, 0x93, 0x60, 0x7c, 0xc2, + 0xbf, 0xd8, 0xce, 0xc7, 0xaa, 0xdf, 0xd9, 0xa6 }, + { 0x2d, 0x85, 0x26, 0x08, 0x1d, 0x02, 0xfb, 0x5b, + 0x85, 0xf6, 0x9a, 0xc2, 0x86, 0xec, 0xd5, 0x7d, + 0x40, 0xdf, 0x5d, 0xf3, 0x49, 0x47, 0x44, 0xd3 }, + { 0x11, 0x2a, 0xd4, 0x1b, 0x48, 0x56, 0xc7, 0x25, + 0x4a, 0x98, 0x48, 0xd3, 0x0f, 0xdd, 0x78, 0x33, + 0x5b, 0x03, 0x9a, 0x48, 0xa8, 0x96, 0x2c, 0x4d, + 0x1c, 0xb7, 0x8e, 0xab, 0xd5, 0xda, 0xd7, 0x88 } +}; + +static const unsigned char kw_msg[KW_TESTS][40] = { + { 0x42, 0x13, 0x6d, 0x3c, 0x38, 0x4a, 0x3e, 0xea, + 0xc9, 0x5a, 0x06, 0x6f, 0xd2, 0x8f, 0xed, 0x3f }, + { 0x95, 0xc1, 0x1b, 0xf5, 0x35, 0x3a, 0xfe, 0xdb, + 0x98, 0xfd, 0xd6, 0xc8, 0xca, 0x6f, 0xdb, 0x6d, + 0xa5, 0x4b, 0x74, 0xb4, 0x99, 0x0f, 0xdc, 0x45, + 0xc0, 0x9d, 0x15, 0x8f, 0x51, 0xce, 0x62, 0x9d, + 0xe2, 0xaf, 0x26, 0xe3, 0x25, 0x0e, 0x6b, 0x4c }, + { 0x1b, 0x20, 0xbf, 0x19, 0x90, 0xb0, 0x65, 0xd7, + 0x98, 0xe1, 0xb3, 0x22, 0x64, 0xad, 0x50, 0xa8, + 0x74, 0x74, 0x92, 0xba, 0x09, 0xa0, 0x4d, 0xd1 } +}; + +static const size_t kw_msg_len[KW_TESTS] = { 16, 40, 24 }; +static const size_t kw_out_len[KW_TESTS] = { 24, 48, 32 }; +static const unsigned char kw_res[KW_TESTS][48] = { + { 0x03, 0x1f, 0x6b, 0xd7, 0xe6, 0x1e, 0x64, 0x3d, + 0xf6, 0x85, 0x94, 0x81, 0x6f, 0x64, 0xca, 0xa3, + 0xf5, 0x6f, 0xab, 0xea, 0x25, 0x48, 0xf5, 0xfb }, + { 0x44, 0x3c, 0x6f, 0x15, 0x09, 0x83, 0x71, 0x91, + 0x3e, 0x5c, 0x81, 0x4c, 0xa1, 0xa0, 0x42, 0xec, + 0x68, 0x2f, 0x7b, 0x13, 0x6d, 0x24, 0x3a, 0x4d, + 0x6c, 0x42, 0x6f, 0xc6, 0x97, 0x15, 0x63, 0xe8, + 0xa1, 0x4a, 0x55, 0x8e, 0x09, 0x64, 0x16, 0x19, + 0xbf, 0x03, 0xfc, 0xaf, 0x90, 0xb1, 0xfc, 0x2d }, + { 0xba, 0x8a, 0x25, 0x9a, 0x47, 0x1b, 0x78, 0x7d, + 0xd5, 0xd5, 0x40, 0xec, 0x25, 0xd4, 0x3d, 0x87, + 0x20, 0x0f, 0xda, 0xdc, 0x6d, 0x1f, 0x05, 0xd9, + 0x16, 0x58, 0x4f, 0xa9, 0xf6, 0xcb, 0xf5, 0x12 } +}; + +static const unsigned char kwp_key[KW_TESTS][32] = { + { 0x78, 0x65, 0xe2, 0x0f, 0x3c, 0x21, 0x65, 0x9a, + 0xb4, 0x69, 0x0b, 0x62, 0x9c, 0xdf, 0x3c, 0xc4 }, + { 0xf5, 0xf8, 0x96, 0xa3, 0xbd, 0x2f, 0x4a, 0x98, + 0x23, 0xef, 0x16, 0x2b, 0x00, 0xb8, 0x05, 0xd7, + 0xde, 0x1e, 0xa4, 0x66, 0x26, 0x96, 0xa2, 0x58 }, + { 0x95, 0xda, 0x27, 0x00, 0xca, 0x6f, 0xd9, 0xa5, + 0x25, 0x54, 0xee, 0x2a, 0x8d, 0xf1, 0x38, 0x6f, + 0x5b, 0x94, 0xa1, 0xa6, 0x0e, 0xd8, 0xa4, 0xae, + 0xf6, 0x0a, 0x8d, 0x61, 0xab, 0x5f, 0x22, 0x5a } +}; + +static const unsigned char kwp_msg[KW_TESTS][31] = { + { 0xbd, 0x68, 0x43, 0xd4, 0x20, 0x37, 0x8d, 0xc8, + 0x96 }, + { 0x6c, 0xcd, 0xd5, 0x85, 0x18, 0x40, 0x97, 0xeb, + 0xd5, 0xc3, 0xaf, 0x3e, 0x47, 0xd0, 0x2c, 0x19, + 0x14, 0x7b, 0x4d, 0x99, 0x5f, 0x96, 0x43, 0x66, + 0x91, 0x56, 0x75, 0x8c, 0x13, 0x16, 0x8f }, + { 0xd1 } +}; +static const size_t kwp_msg_len[KW_TESTS] = { 9, 31, 1 }; + +static const unsigned char kwp_res[KW_TESTS][48] = { + { 0x41, 0xec, 0xa9, 0x56, 0xd4, 0xaa, 0x04, 0x7e, + 0xb5, 0xcf, 0x4e, 0xfe, 0x65, 0x96, 0x61, 0xe7, + 0x4d, 0xb6, 0xf8, 0xc5, 0x64, 0xe2, 0x35, 0x00 }, + { 0x4e, 0x9b, 0xc2, 0xbc, 0xbc, 0x6c, 0x1e, 0x13, + 0xd3, 0x35, 0xbc, 0xc0, 0xf7, 0x73, 0x6a, 0x88, + 0xfa, 0x87, 0x53, 0x66, 0x15, 0xbb, 0x8e, 0x63, + 0x8b, 0xcc, 0x81, 0x66, 0x84, 0x68, 0x17, 0x90, + 0x67, 0xcf, 0xa9, 0x8a, 0x9d, 0x0e, 0x33, 0x26 }, + { 0x06, 0xba, 0x7a, 0xe6, 0xf3, 0x24, 0x8c, 0xfd, + 0xcf, 0x26, 0x75, 0x07, 0xfa, 0x00, 0x1b, 0xc4 } +}; +static const size_t kwp_out_len[KW_TESTS] = { 24, 40, 16 }; + +int mbedtls_nist_kw_self_test(int verbose) +{ + mbedtls_nist_kw_context ctx; + unsigned char out[48]; + size_t olen; + int i; + int ret = 0; + mbedtls_nist_kw_init(&ctx); + + for (i = 0; i < KW_TESTS; i++) { + if (verbose != 0) { + mbedtls_printf(" KW-AES-%u ", (unsigned int) key_len[i] * 8); + } + + ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, + kw_key[i], key_len[i] * 8, 1); + if (ret != 0) { + if (verbose != 0) { + mbedtls_printf(" KW: setup failed "); + } + + goto end; + } + + ret = mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KW, kw_msg[i], + kw_msg_len[i], out, &olen, sizeof(out)); + if (ret != 0 || kw_out_len[i] != olen || + memcmp(out, kw_res[i], kw_out_len[i]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed. "); + } + + ret = 1; + goto end; + } + + if ((ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, + kw_key[i], key_len[i] * 8, 0)) + != 0) { + if (verbose != 0) { + mbedtls_printf(" KW: setup failed "); + } + + goto end; + } + + ret = mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KW, + out, olen, out, &olen, sizeof(out)); + + if (ret != 0 || olen != kw_msg_len[i] || + memcmp(out, kw_msg[i], kw_msg_len[i]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto end; + } + + if (verbose != 0) { + mbedtls_printf(" passed\n"); + } + } + + for (i = 0; i < KW_TESTS; i++) { + olen = sizeof(out); + if (verbose != 0) { + mbedtls_printf(" KWP-AES-%u ", (unsigned int) key_len[i] * 8); + } + + ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, kwp_key[i], + key_len[i] * 8, 1); + if (ret != 0) { + if (verbose != 0) { + mbedtls_printf(" KWP: setup failed "); + } + + goto end; + } + ret = mbedtls_nist_kw_wrap(&ctx, MBEDTLS_KW_MODE_KWP, kwp_msg[i], + kwp_msg_len[i], out, &olen, sizeof(out)); + + if (ret != 0 || kwp_out_len[i] != olen || + memcmp(out, kwp_res[i], kwp_out_len[i]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed. "); + } + + ret = 1; + goto end; + } + + if ((ret = mbedtls_nist_kw_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, + kwp_key[i], key_len[i] * 8, 0)) + != 0) { + if (verbose != 0) { + mbedtls_printf(" KWP: setup failed "); + } + + goto end; + } + + ret = mbedtls_nist_kw_unwrap(&ctx, MBEDTLS_KW_MODE_KWP, out, + olen, out, &olen, sizeof(out)); + + if (ret != 0 || olen != kwp_msg_len[i] || + memcmp(out, kwp_msg[i], kwp_msg_len[i]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed. "); + } + + ret = 1; + goto end; + } + + if (verbose != 0) { + mbedtls_printf(" passed\n"); + } + } +end: + mbedtls_nist_kw_free(&ctx); + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST && MBEDTLS_AES_C */ + +#endif /* MBEDTLS_NIST_KW_C */ diff --git a/r5dev/thirdparty/mbedtls/oid.c b/r5dev/thirdparty/mbedtls/oid.c new file mode 100644 index 00000000..63b3df38 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/oid.c @@ -0,0 +1,885 @@ +/** + * \file oid.c + * + * \brief Object Identifier (OID) database + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_OID_C) + +#include "mbedtls/oid.h" +#include "mbedtls/rsa.h" +#include "mbedtls/error.h" +#include "mbedtls/pk.h" + +#include "mbedtls/legacy_or_psa.h" + +#include +#include + +#include "mbedtls/platform.h" + +/* + * Macro to automatically add the size of #define'd OIDs + */ +#define ADD_LEN(s) s, MBEDTLS_OID_SIZE(s) + +/* + * Macro to generate mbedtls_oid_descriptor_t + */ +#if !defined(MBEDTLS_X509_REMOVE_INFO) +#define OID_DESCRIPTOR(s, name, description) { ADD_LEN(s), name, description } +#define NULL_OID_DESCRIPTOR { NULL, 0, NULL, NULL } +#else +#define OID_DESCRIPTOR(s, name, description) { ADD_LEN(s) } +#define NULL_OID_DESCRIPTOR { NULL, 0 } +#endif + +/* + * Macro to generate an internal function for oid_XXX_from_asn1() (used by + * the other functions) + */ +#define FN_OID_TYPED_FROM_ASN1(TYPE_T, NAME, LIST) \ + static const TYPE_T *oid_ ## NAME ## _from_asn1( \ + const mbedtls_asn1_buf *oid) \ + { \ + const TYPE_T *p = (LIST); \ + const mbedtls_oid_descriptor_t *cur = \ + (const mbedtls_oid_descriptor_t *) p; \ + if (p == NULL || oid == NULL) return NULL; \ + while (cur->asn1 != NULL) { \ + if (cur->asn1_len == oid->len && \ + memcmp(cur->asn1, oid->p, oid->len) == 0) { \ + return p; \ + } \ + p++; \ + cur = (const mbedtls_oid_descriptor_t *) p; \ + } \ + return NULL; \ + } + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/* + * Macro to generate a function for retrieving a single attribute from the + * descriptor of an mbedtls_oid_descriptor_t wrapper. + */ +#define FN_OID_GET_DESCRIPTOR_ATTR1(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1) \ + int FN_NAME(const mbedtls_asn1_buf *oid, ATTR1_TYPE * ATTR1) \ + { \ + const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1(oid); \ + if (data == NULL) return MBEDTLS_ERR_OID_NOT_FOUND; \ + *ATTR1 = data->descriptor.ATTR1; \ + return 0; \ + } +#endif /* MBEDTLS_X509_REMOVE_INFO */ + +/* + * Macro to generate a function for retrieving a single attribute from an + * mbedtls_oid_descriptor_t wrapper. + */ +#define FN_OID_GET_ATTR1(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1) \ + int FN_NAME(const mbedtls_asn1_buf *oid, ATTR1_TYPE * ATTR1) \ + { \ + const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1(oid); \ + if (data == NULL) return MBEDTLS_ERR_OID_NOT_FOUND; \ + *ATTR1 = data->ATTR1; \ + return 0; \ + } + +/* + * Macro to generate a function for retrieving two attributes from an + * mbedtls_oid_descriptor_t wrapper. + */ +#define FN_OID_GET_ATTR2(FN_NAME, TYPE_T, TYPE_NAME, ATTR1_TYPE, ATTR1, \ + ATTR2_TYPE, ATTR2) \ + int FN_NAME(const mbedtls_asn1_buf *oid, ATTR1_TYPE * ATTR1, \ + ATTR2_TYPE * ATTR2) \ + { \ + const TYPE_T *data = oid_ ## TYPE_NAME ## _from_asn1(oid); \ + if (data == NULL) return MBEDTLS_ERR_OID_NOT_FOUND; \ + *(ATTR1) = data->ATTR1; \ + *(ATTR2) = data->ATTR2; \ + return 0; \ + } + +/* + * Macro to generate a function for retrieving the OID based on a single + * attribute from a mbedtls_oid_descriptor_t wrapper. + */ +#define FN_OID_GET_OID_BY_ATTR1(FN_NAME, TYPE_T, LIST, ATTR1_TYPE, ATTR1) \ + int FN_NAME(ATTR1_TYPE ATTR1, const char **oid, size_t *olen) \ + { \ + const TYPE_T *cur = (LIST); \ + while (cur->descriptor.asn1 != NULL) { \ + if (cur->ATTR1 == (ATTR1)) { \ + *oid = cur->descriptor.asn1; \ + *olen = cur->descriptor.asn1_len; \ + return 0; \ + } \ + cur++; \ + } \ + return MBEDTLS_ERR_OID_NOT_FOUND; \ + } + +/* + * Macro to generate a function for retrieving the OID based on two + * attributes from a mbedtls_oid_descriptor_t wrapper. + */ +#define FN_OID_GET_OID_BY_ATTR2(FN_NAME, TYPE_T, LIST, ATTR1_TYPE, ATTR1, \ + ATTR2_TYPE, ATTR2) \ + int FN_NAME(ATTR1_TYPE ATTR1, ATTR2_TYPE ATTR2, const char **oid, \ + size_t *olen) \ + { \ + const TYPE_T *cur = (LIST); \ + while (cur->descriptor.asn1 != NULL) { \ + if (cur->ATTR1 == (ATTR1) && cur->ATTR2 == (ATTR2)) { \ + *oid = cur->descriptor.asn1; \ + *olen = cur->descriptor.asn1_len; \ + return 0; \ + } \ + cur++; \ + } \ + return MBEDTLS_ERR_OID_NOT_FOUND; \ + } + +/* + * For X520 attribute types + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + const char *short_name; +} oid_x520_attr_t; + +static const oid_x520_attr_t oid_x520_attr_type[] = +{ + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_CN, "id-at-commonName", "Common Name"), + "CN", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_COUNTRY, "id-at-countryName", "Country"), + "C", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_LOCALITY, "id-at-locality", "Locality"), + "L", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_STATE, "id-at-state", "State"), + "ST", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_ORGANIZATION, "id-at-organizationName", + "Organization"), + "O", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_ORG_UNIT, "id-at-organizationalUnitName", "Org Unit"), + "OU", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS9_EMAIL, + "emailAddress", + "E-mail address"), + "emailAddress", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_SERIAL_NUMBER, + "id-at-serialNumber", + "Serial number"), + "serialNumber", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_POSTAL_ADDRESS, + "id-at-postalAddress", + "Postal address"), + "postalAddress", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_POSTAL_CODE, "id-at-postalCode", "Postal code"), + "postalCode", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_SUR_NAME, "id-at-surName", "Surname"), + "SN", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_GIVEN_NAME, "id-at-givenName", "Given name"), + "GN", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_INITIALS, "id-at-initials", "Initials"), + "initials", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_GENERATION_QUALIFIER, + "id-at-generationQualifier", + "Generation qualifier"), + "generationQualifier", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_TITLE, "id-at-title", "Title"), + "title", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_DN_QUALIFIER, + "id-at-dnQualifier", + "Distinguished Name qualifier"), + "dnQualifier", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_PSEUDONYM, "id-at-pseudonym", "Pseudonym"), + "pseudonym", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_UID, "id-uid", "User Id"), + "uid", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_DOMAIN_COMPONENT, + "id-domainComponent", + "Domain component"), + "DC", + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_AT_UNIQUE_IDENTIFIER, + "id-at-uniqueIdentifier", + "Unique Identifier"), + "uniqueIdentifier", + }, + { + NULL_OID_DESCRIPTOR, + NULL, + } +}; + +FN_OID_TYPED_FROM_ASN1(oid_x520_attr_t, x520_attr, oid_x520_attr_type) +FN_OID_GET_ATTR1(mbedtls_oid_get_attr_short_name, + oid_x520_attr_t, + x520_attr, + const char *, + short_name) + +/* + * For X509 extensions + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + int ext_type; +} oid_x509_ext_t; + +static const oid_x509_ext_t oid_x509_ext[] = +{ + { + OID_DESCRIPTOR(MBEDTLS_OID_BASIC_CONSTRAINTS, + "id-ce-basicConstraints", + "Basic Constraints"), + MBEDTLS_OID_X509_EXT_BASIC_CONSTRAINTS, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_KEY_USAGE, "id-ce-keyUsage", "Key Usage"), + MBEDTLS_OID_X509_EXT_KEY_USAGE, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_EXTENDED_KEY_USAGE, + "id-ce-extKeyUsage", + "Extended Key Usage"), + MBEDTLS_OID_X509_EXT_EXTENDED_KEY_USAGE, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_SUBJECT_ALT_NAME, + "id-ce-subjectAltName", + "Subject Alt Name"), + MBEDTLS_OID_X509_EXT_SUBJECT_ALT_NAME, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_NS_CERT_TYPE, + "id-netscape-certtype", + "Netscape Certificate Type"), + MBEDTLS_OID_X509_EXT_NS_CERT_TYPE, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_CERTIFICATE_POLICIES, + "id-ce-certificatePolicies", + "Certificate Policies"), + MBEDTLS_OID_X509_EXT_CERTIFICATE_POLICIES, + }, + { + NULL_OID_DESCRIPTOR, + 0, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_x509_ext_t, x509_ext, oid_x509_ext) +FN_OID_GET_ATTR1(mbedtls_oid_get_x509_ext_type, oid_x509_ext_t, x509_ext, int, ext_type) + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +static const mbedtls_oid_descriptor_t oid_ext_key_usage[] = +{ + OID_DESCRIPTOR(MBEDTLS_OID_SERVER_AUTH, + "id-kp-serverAuth", + "TLS Web Server Authentication"), + OID_DESCRIPTOR(MBEDTLS_OID_CLIENT_AUTH, + "id-kp-clientAuth", + "TLS Web Client Authentication"), + OID_DESCRIPTOR(MBEDTLS_OID_CODE_SIGNING, "id-kp-codeSigning", "Code Signing"), + OID_DESCRIPTOR(MBEDTLS_OID_EMAIL_PROTECTION, "id-kp-emailProtection", "E-mail Protection"), + OID_DESCRIPTOR(MBEDTLS_OID_TIME_STAMPING, "id-kp-timeStamping", "Time Stamping"), + OID_DESCRIPTOR(MBEDTLS_OID_OCSP_SIGNING, "id-kp-OCSPSigning", "OCSP Signing"), + OID_DESCRIPTOR(MBEDTLS_OID_WISUN_FAN, + "id-kp-wisun-fan-device", + "Wi-SUN Alliance Field Area Network (FAN)"), + NULL_OID_DESCRIPTOR, +}; + +FN_OID_TYPED_FROM_ASN1(mbedtls_oid_descriptor_t, ext_key_usage, oid_ext_key_usage) +FN_OID_GET_ATTR1(mbedtls_oid_get_extended_key_usage, + mbedtls_oid_descriptor_t, + ext_key_usage, + const char *, + description) + +static const mbedtls_oid_descriptor_t oid_certificate_policies[] = +{ + OID_DESCRIPTOR(MBEDTLS_OID_ANY_POLICY, "anyPolicy", "Any Policy"), + NULL_OID_DESCRIPTOR, +}; + +FN_OID_TYPED_FROM_ASN1(mbedtls_oid_descriptor_t, certificate_policies, oid_certificate_policies) +FN_OID_GET_ATTR1(mbedtls_oid_get_certificate_policies, + mbedtls_oid_descriptor_t, + certificate_policies, + const char *, + description) +#endif /* MBEDTLS_X509_REMOVE_INFO */ + +/* + * For SignatureAlgorithmIdentifier + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_md_type_t md_alg; + mbedtls_pk_type_t pk_alg; +} oid_sig_alg_t; + +static const oid_sig_alg_t oid_sig_alg[] = +{ +#if defined(MBEDTLS_RSA_C) +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_MD5, "md5WithRSAEncryption", "RSA with MD5"), + MBEDTLS_MD_MD5, MBEDTLS_PK_RSA, + }, +#endif /* MBEDTLS_HAS_ALG_MD5_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_SHA1, "sha-1WithRSAEncryption", "RSA with SHA1"), + MBEDTLS_MD_SHA1, MBEDTLS_PK_RSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_SHA224, "sha224WithRSAEncryption", + "RSA with SHA-224"), + MBEDTLS_MD_SHA224, MBEDTLS_PK_RSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_224_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_SHA256, "sha256WithRSAEncryption", + "RSA with SHA-256"), + MBEDTLS_MD_SHA256, MBEDTLS_PK_RSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_SHA384, "sha384WithRSAEncryption", + "RSA with SHA-384"), + MBEDTLS_MD_SHA384, MBEDTLS_PK_RSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_SHA512, "sha512WithRSAEncryption", + "RSA with SHA-512"), + MBEDTLS_MD_SHA512, MBEDTLS_PK_RSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_RSA_SHA_OBS, "sha-1WithRSAEncryption", "RSA with SHA1"), + MBEDTLS_MD_SHA1, MBEDTLS_PK_RSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA */ +#endif /* MBEDTLS_RSA_C */ +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA1, "ecdsa-with-SHA1", "ECDSA with SHA1"), + MBEDTLS_MD_SHA1, MBEDTLS_PK_ECDSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA224, "ecdsa-with-SHA224", "ECDSA with SHA224"), + MBEDTLS_MD_SHA224, MBEDTLS_PK_ECDSA, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA256, "ecdsa-with-SHA256", "ECDSA with SHA256"), + MBEDTLS_MD_SHA256, MBEDTLS_PK_ECDSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA384, "ecdsa-with-SHA384", "ECDSA with SHA384"), + MBEDTLS_MD_SHA384, MBEDTLS_PK_ECDSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA512, "ecdsa-with-SHA512", "ECDSA with SHA512"), + MBEDTLS_MD_SHA512, MBEDTLS_PK_ECDSA, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA */ +#endif /* MBEDTLS_PK_CAN_ECDSA_SOME */ +#if defined(MBEDTLS_RSA_C) + { + OID_DESCRIPTOR(MBEDTLS_OID_RSASSA_PSS, "RSASSA-PSS", "RSASSA-PSS"), + MBEDTLS_MD_NONE, MBEDTLS_PK_RSASSA_PSS, + }, +#endif /* MBEDTLS_RSA_C */ + { + NULL_OID_DESCRIPTOR, + MBEDTLS_MD_NONE, MBEDTLS_PK_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_sig_alg_t, sig_alg, oid_sig_alg) + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +FN_OID_GET_DESCRIPTOR_ATTR1(mbedtls_oid_get_sig_alg_desc, + oid_sig_alg_t, + sig_alg, + const char *, + description) +#endif + +FN_OID_GET_ATTR2(mbedtls_oid_get_sig_alg, + oid_sig_alg_t, + sig_alg, + mbedtls_md_type_t, + md_alg, + mbedtls_pk_type_t, + pk_alg) +FN_OID_GET_OID_BY_ATTR2(mbedtls_oid_get_oid_by_sig_alg, + oid_sig_alg_t, + oid_sig_alg, + mbedtls_pk_type_t, + pk_alg, + mbedtls_md_type_t, + md_alg) + +/* + * For PublicKeyInfo (PKCS1, RFC 5480) + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_pk_type_t pk_alg; +} oid_pk_alg_t; + +static const oid_pk_alg_t oid_pk_alg[] = +{ + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_RSA, "rsaEncryption", "RSA"), + MBEDTLS_PK_RSA, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_ALG_UNRESTRICTED, "id-ecPublicKey", "Generic EC key"), + MBEDTLS_PK_ECKEY, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_ALG_ECDH, "id-ecDH", "EC key for ECDH"), + MBEDTLS_PK_ECKEY_DH, + }, + { + NULL_OID_DESCRIPTOR, + MBEDTLS_PK_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_pk_alg_t, pk_alg, oid_pk_alg) +FN_OID_GET_ATTR1(mbedtls_oid_get_pk_alg, oid_pk_alg_t, pk_alg, mbedtls_pk_type_t, pk_alg) +FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_pk_alg, + oid_pk_alg_t, + oid_pk_alg, + mbedtls_pk_type_t, + pk_alg) + +#if defined(MBEDTLS_ECP_C) +/* + * For namedCurve (RFC 5480) + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_ecp_group_id grp_id; +} oid_ecp_grp_t; + +static const oid_ecp_grp_t oid_ecp_grp[] = +{ +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP192R1, "secp192r1", "secp192r1"), + MBEDTLS_ECP_DP_SECP192R1, + }, +#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP224R1, "secp224r1", "secp224r1"), + MBEDTLS_ECP_DP_SECP224R1, + }, +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP256R1, "secp256r1", "secp256r1"), + MBEDTLS_ECP_DP_SECP256R1, + }, +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP384R1, "secp384r1", "secp384r1"), + MBEDTLS_ECP_DP_SECP384R1, + }, +#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP521R1, "secp521r1", "secp521r1"), + MBEDTLS_ECP_DP_SECP521R1, + }, +#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP192K1, "secp192k1", "secp192k1"), + MBEDTLS_ECP_DP_SECP192K1, + }, +#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP224K1, "secp224k1", "secp224k1"), + MBEDTLS_ECP_DP_SECP224K1, + }, +#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_SECP256K1, "secp256k1", "secp256k1"), + MBEDTLS_ECP_DP_SECP256K1, + }, +#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_BP256R1, "brainpoolP256r1", "brainpool256r1"), + MBEDTLS_ECP_DP_BP256R1, + }, +#endif /* MBEDTLS_ECP_DP_BP256R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_BP384R1, "brainpoolP384r1", "brainpool384r1"), + MBEDTLS_ECP_DP_BP384R1, + }, +#endif /* MBEDTLS_ECP_DP_BP384R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) + { + OID_DESCRIPTOR(MBEDTLS_OID_EC_GRP_BP512R1, "brainpoolP512r1", "brainpool512r1"), + MBEDTLS_ECP_DP_BP512R1, + }, +#endif /* MBEDTLS_ECP_DP_BP512R1_ENABLED */ + { + NULL_OID_DESCRIPTOR, + MBEDTLS_ECP_DP_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_ecp_grp_t, grp_id, oid_ecp_grp) +FN_OID_GET_ATTR1(mbedtls_oid_get_ec_grp, oid_ecp_grp_t, grp_id, mbedtls_ecp_group_id, grp_id) +FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_ec_grp, + oid_ecp_grp_t, + oid_ecp_grp, + mbedtls_ecp_group_id, + grp_id) +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_CIPHER_C) +/* + * For PKCS#5 PBES2 encryption algorithm + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_cipher_type_t cipher_alg; +} oid_cipher_alg_t; + +static const oid_cipher_alg_t oid_cipher_alg[] = +{ + { + OID_DESCRIPTOR(MBEDTLS_OID_DES_CBC, "desCBC", "DES-CBC"), + MBEDTLS_CIPHER_DES_CBC, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_DES_EDE3_CBC, "des-ede3-cbc", "DES-EDE3-CBC"), + MBEDTLS_CIPHER_DES_EDE3_CBC, + }, + { + NULL_OID_DESCRIPTOR, + MBEDTLS_CIPHER_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_cipher_alg_t, cipher_alg, oid_cipher_alg) +FN_OID_GET_ATTR1(mbedtls_oid_get_cipher_alg, + oid_cipher_alg_t, + cipher_alg, + mbedtls_cipher_type_t, + cipher_alg) +#endif /* MBEDTLS_CIPHER_C */ + +/* + * For digestAlgorithm + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_md_type_t md_alg; +} oid_md_alg_t; + +static const oid_md_alg_t oid_md_alg[] = +{ +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_MD5, "id-md5", "MD5"), + MBEDTLS_MD_MD5, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_SHA1, "id-sha1", "SHA-1"), + MBEDTLS_MD_SHA1, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_SHA224, "id-sha224", "SHA-224"), + MBEDTLS_MD_SHA224, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_SHA256, "id-sha256", "SHA-256"), + MBEDTLS_MD_SHA256, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_SHA384, "id-sha384", "SHA-384"), + MBEDTLS_MD_SHA384, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_SHA512, "id-sha512", "SHA-512"), + MBEDTLS_MD_SHA512, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_RIPEMD160_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_RIPEMD160, "id-ripemd160", "RIPEMD-160"), + MBEDTLS_MD_RIPEMD160, + }, +#endif + { + NULL_OID_DESCRIPTOR, + MBEDTLS_MD_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_md_alg_t, md_alg, oid_md_alg) +FN_OID_GET_ATTR1(mbedtls_oid_get_md_alg, oid_md_alg_t, md_alg, mbedtls_md_type_t, md_alg) +FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_md, + oid_md_alg_t, + oid_md_alg, + mbedtls_md_type_t, + md_alg) + +/* + * For HMAC digestAlgorithm + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_md_type_t md_hmac; +} oid_md_hmac_t; + +static const oid_md_hmac_t oid_md_hmac[] = +{ +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_HMAC_SHA1, "hmacSHA1", "HMAC-SHA-1"), + MBEDTLS_MD_SHA1, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_HMAC_SHA224, "hmacSHA224", "HMAC-SHA-224"), + MBEDTLS_MD_SHA224, + }, +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_HMAC_SHA256, "hmacSHA256", "HMAC-SHA-256"), + MBEDTLS_MD_SHA256, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_HMAC_SHA384, "hmacSHA384", "HMAC-SHA-384"), + MBEDTLS_MD_SHA384, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA) + { + OID_DESCRIPTOR(MBEDTLS_OID_HMAC_SHA512, "hmacSHA512", "HMAC-SHA-512"), + MBEDTLS_MD_SHA512, + }, +#endif /* MBEDTLS_HAS_ALG_SHA_512_VIA_LOWLEVEL_OR_PSA */ + { + NULL_OID_DESCRIPTOR, + MBEDTLS_MD_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_md_hmac_t, md_hmac, oid_md_hmac) +FN_OID_GET_ATTR1(mbedtls_oid_get_md_hmac, oid_md_hmac_t, md_hmac, mbedtls_md_type_t, md_hmac) + +#if defined(MBEDTLS_PKCS12_C) +/* + * For PKCS#12 PBEs + */ +typedef struct { + mbedtls_oid_descriptor_t descriptor; + mbedtls_md_type_t md_alg; + mbedtls_cipher_type_t cipher_alg; +} oid_pkcs12_pbe_alg_t; + +static const oid_pkcs12_pbe_alg_t oid_pkcs12_pbe_alg[] = +{ + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS12_PBE_SHA1_DES3_EDE_CBC, + "pbeWithSHAAnd3-KeyTripleDES-CBC", + "PBE with SHA1 and 3-Key 3DES"), + MBEDTLS_MD_SHA1, MBEDTLS_CIPHER_DES_EDE3_CBC, + }, + { + OID_DESCRIPTOR(MBEDTLS_OID_PKCS12_PBE_SHA1_DES2_EDE_CBC, + "pbeWithSHAAnd2-KeyTripleDES-CBC", + "PBE with SHA1 and 2-Key 3DES"), + MBEDTLS_MD_SHA1, MBEDTLS_CIPHER_DES_EDE_CBC, + }, + { + NULL_OID_DESCRIPTOR, + MBEDTLS_MD_NONE, MBEDTLS_CIPHER_NONE, + }, +}; + +FN_OID_TYPED_FROM_ASN1(oid_pkcs12_pbe_alg_t, pkcs12_pbe_alg, oid_pkcs12_pbe_alg) +FN_OID_GET_ATTR2(mbedtls_oid_get_pkcs12_pbe_alg, + oid_pkcs12_pbe_alg_t, + pkcs12_pbe_alg, + mbedtls_md_type_t, + md_alg, + mbedtls_cipher_type_t, + cipher_alg) +#endif /* MBEDTLS_PKCS12_C */ + +/* Return the x.y.z.... style numeric string for the given OID */ +int mbedtls_oid_get_numeric_string(char *buf, size_t size, + const mbedtls_asn1_buf *oid) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + char *p = buf; + size_t n = size; + unsigned int value = 0; + + if (size > INT_MAX) { + /* Avoid overflow computing return value */ + return MBEDTLS_ERR_ASN1_INVALID_LENGTH; + } + + if (oid->len <= 0) { + /* OID must not be empty */ + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + for (size_t i = 0; i < oid->len; i++) { + /* Prevent overflow in value. */ + if (value > (UINT_MAX >> 7)) { + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + if ((value == 0) && ((oid->p[i]) == 0x80)) { + /* Overlong encoding is not allowed */ + return MBEDTLS_ERR_ASN1_INVALID_DATA; + } + + value <<= 7; + value |= oid->p[i] & 0x7F; + + if (!(oid->p[i] & 0x80)) { + /* Last byte */ + if (n == size) { + int component1; + unsigned int component2; + /* First subidentifier contains first two OID components */ + if (value >= 80) { + component1 = '2'; + component2 = value - 80; + } else if (value >= 40) { + component1 = '1'; + component2 = value - 40; + } else { + component1 = '0'; + component2 = value; + } + ret = mbedtls_snprintf(p, n, "%c.%u", component1, component2); + } else { + ret = mbedtls_snprintf(p, n, ".%u", value); + } + if (ret < 2 || (size_t) ret >= n) { + return MBEDTLS_ERR_OID_BUF_TOO_SMALL; + } + n -= (size_t) ret; + p += ret; + value = 0; + } + } + + if (value != 0) { + /* Unterminated subidentifier */ + return MBEDTLS_ERR_ASN1_OUT_OF_DATA; + } + + return (int) (size - n); +} + +#endif /* MBEDTLS_OID_C */ diff --git a/r5dev/thirdparty/mbedtls/padlock.c b/r5dev/thirdparty/mbedtls/padlock.c new file mode 100644 index 00000000..f42c40ff --- /dev/null +++ b/r5dev/thirdparty/mbedtls/padlock.c @@ -0,0 +1,167 @@ +/* + * VIA PadLock support functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * This implementation is based on the VIA PadLock Programming Guide: + * + * http://www.via.com.tw/en/downloads/whitepapers/initiatives/padlock/ + * programming_guide.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_PADLOCK_C) + +#include "padlock.h" + +#include + +#if defined(MBEDTLS_HAVE_X86) + +/* + * PadLock detection routine + */ +int mbedtls_padlock_has_support(int feature) +{ + static int flags = -1; + int ebx = 0, edx = 0; + + if (flags == -1) { + asm ("movl %%ebx, %0 \n\t" + "movl $0xC0000000, %%eax \n\t" + "cpuid \n\t" + "cmpl $0xC0000001, %%eax \n\t" + "movl $0, %%edx \n\t" + "jb 1f \n\t" + "movl $0xC0000001, %%eax \n\t" + "cpuid \n\t" + "1: \n\t" + "movl %%edx, %1 \n\t" + "movl %2, %%ebx \n\t" + : "=m" (ebx), "=m" (edx) + : "m" (ebx) + : "eax", "ecx", "edx"); + + flags = edx; + } + + return flags & feature; +} + +/* + * PadLock AES-ECB block en(de)cryption + */ +int mbedtls_padlock_xcryptecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]) +{ + int ebx = 0; + uint32_t *rk; + uint32_t *blk; + uint32_t *ctrl; + unsigned char buf[256]; + + rk = ctx->buf + ctx->rk_offset; + + if (((long) rk & 15) != 0) { + return MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED; + } + + blk = MBEDTLS_PADLOCK_ALIGN16(buf); + memcpy(blk, input, 16); + + ctrl = blk + 4; + *ctrl = 0x80 | ctx->nr | ((ctx->nr + (mode^1) - 10) << 9); + + asm ("pushfl \n\t" + "popfl \n\t" + "movl %%ebx, %0 \n\t" + "movl $1, %%ecx \n\t" + "movl %2, %%edx \n\t" + "movl %3, %%ebx \n\t" + "movl %4, %%esi \n\t" + "movl %4, %%edi \n\t" + ".byte 0xf3,0x0f,0xa7,0xc8 \n\t" + "movl %1, %%ebx \n\t" + : "=m" (ebx) + : "m" (ebx), "m" (ctrl), "m" (rk), "m" (blk) + : "memory", "ecx", "edx", "esi", "edi"); + + memcpy(output, blk, 16); + + return 0; +} + +/* + * PadLock AES-CBC buffer en(de)cryption + */ +int mbedtls_padlock_xcryptcbc(mbedtls_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output) +{ + int ebx = 0; + size_t count; + uint32_t *rk; + uint32_t *iw; + uint32_t *ctrl; + unsigned char buf[256]; + + rk = ctx->buf + ctx->rk_offset; + + if (((long) input & 15) != 0 || + ((long) output & 15) != 0 || + ((long) rk & 15) != 0) { + return MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED; + } + + iw = MBEDTLS_PADLOCK_ALIGN16(buf); + memcpy(iw, iv, 16); + + ctrl = iw + 4; + *ctrl = 0x80 | ctx->nr | ((ctx->nr + (mode ^ 1) - 10) << 9); + + count = (length + 15) >> 4; + + asm ("pushfl \n\t" + "popfl \n\t" + "movl %%ebx, %0 \n\t" + "movl %2, %%ecx \n\t" + "movl %3, %%edx \n\t" + "movl %4, %%ebx \n\t" + "movl %5, %%esi \n\t" + "movl %6, %%edi \n\t" + "movl %7, %%eax \n\t" + ".byte 0xf3,0x0f,0xa7,0xd0 \n\t" + "movl %1, %%ebx \n\t" + : "=m" (ebx) + : "m" (ebx), "m" (count), "m" (ctrl), + "m" (rk), "m" (input), "m" (output), "m" (iw) + : "memory", "eax", "ecx", "edx", "esi", "edi"); + + memcpy(iv, iw, 16); + + return 0; +} + +#endif /* MBEDTLS_HAVE_X86 */ + +#endif /* MBEDTLS_PADLOCK_C */ diff --git a/r5dev/thirdparty/mbedtls/padlock.h b/r5dev/thirdparty/mbedtls/padlock.h new file mode 100644 index 00000000..b5f0d7d7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/padlock.h @@ -0,0 +1,120 @@ +/** + * \file padlock.h + * + * \brief VIA PadLock ACE for HW encryption/decryption supported by some + * processors + * + * \warning These functions are only for internal use by other library + * functions; you must not call them directly. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_PADLOCK_H +#define MBEDTLS_PADLOCK_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/aes.h" + +#define MBEDTLS_ERR_PADLOCK_DATA_MISALIGNED -0x0030 /**< Input data should be aligned. */ + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define MBEDTLS_HAVE_ASAN +#endif +#endif + +/* Some versions of ASan result in errors about not enough registers */ +#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) && defined(__i386__) && \ + !defined(MBEDTLS_HAVE_ASAN) + +#ifndef MBEDTLS_HAVE_X86 +#define MBEDTLS_HAVE_X86 +#endif + +#include + +#define MBEDTLS_PADLOCK_RNG 0x000C +#define MBEDTLS_PADLOCK_ACE 0x00C0 +#define MBEDTLS_PADLOCK_PHE 0x0C00 +#define MBEDTLS_PADLOCK_PMM 0x3000 + +#define MBEDTLS_PADLOCK_ALIGN16(x) (uint32_t *) (16 + ((int32_t) (x) & ~15)) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Internal PadLock detection routine + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param feature The feature to detect + * + * \return non-zero if CPU has support for the feature, 0 otherwise + */ +int mbedtls_padlock_has_support(int feature); + +/** + * \brief Internal PadLock AES-ECB block en(de)cryption + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param ctx AES context + * \param mode MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT + * \param input 16-byte input block + * \param output 16-byte output block + * + * \return 0 if success, 1 if operation failed + */ +int mbedtls_padlock_xcryptecb(mbedtls_aes_context *ctx, + int mode, + const unsigned char input[16], + unsigned char output[16]); + +/** + * \brief Internal PadLock AES-CBC buffer en(de)cryption + * + * \note This function is only for internal use by other library + * functions; you must not call it directly. + * + * \param ctx AES context + * \param mode MBEDTLS_AES_ENCRYPT or MBEDTLS_AES_DECRYPT + * \param length length of the input data + * \param iv initialization vector (updated after use) + * \param input buffer holding the input data + * \param output buffer holding the output data + * + * \return 0 if success, 1 if operation failed + */ +int mbedtls_padlock_xcryptcbc(mbedtls_aes_context *ctx, + int mode, + size_t length, + unsigned char iv[16], + const unsigned char *input, + unsigned char *output); + +#ifdef __cplusplus +} +#endif + +#endif /* HAVE_X86 */ + +#endif /* padlock.h */ diff --git a/r5dev/thirdparty/mbedtls/pem.c b/r5dev/thirdparty/mbedtls/pem.c new file mode 100644 index 00000000..84bbb3df --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pem.c @@ -0,0 +1,633 @@ +/* + * Privacy Enhanced Mail (PEM) decoding + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PEM_PARSE_C) || defined(MBEDTLS_PEM_WRITE_C) + +#include "mbedtls/pem.h" +#include "mbedtls/base64.h" +#include "mbedtls/des.h" +#include "mbedtls/aes.h" +#include "mbedtls/md.h" +#include "mbedtls/cipher.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "hash_info.h" + +#include + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#endif + +#if !defined(MBEDTLS_MD5_C) +#include "mbedtls/psa_util.h" +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_md_errors, \ + psa_generic_status_to_mbedtls) +#endif + +#include "mbedtls/legacy_or_psa.h" + +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_CIPHER_MODE_CBC) && \ + (defined(MBEDTLS_DES_C) || defined(MBEDTLS_AES_C)) +#define PEM_RFC1421 +#endif /* MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA && + MBEDTLS_CIPHER_MODE_CBC && + ( MBEDTLS_AES_C || MBEDTLS_DES_C ) */ + +#if defined(MBEDTLS_PEM_PARSE_C) +void mbedtls_pem_init(mbedtls_pem_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_pem_context)); +} + +#if defined(PEM_RFC1421) +/* + * Read a 16-byte hex string and convert it to binary + */ +static int pem_get_iv(const unsigned char *s, unsigned char *iv, + size_t iv_len) +{ + size_t i, j, k; + + memset(iv, 0, iv_len); + + for (i = 0; i < iv_len * 2; i++, s++) { + if (*s >= '0' && *s <= '9') { + j = *s - '0'; + } else + if (*s >= 'A' && *s <= 'F') { + j = *s - '7'; + } else + if (*s >= 'a' && *s <= 'f') { + j = *s - 'W'; + } else { + return MBEDTLS_ERR_PEM_INVALID_ENC_IV; + } + + k = ((i & 1) != 0) ? j : j << 4; + + iv[i >> 1] = (unsigned char) (iv[i >> 1] | k); + } + + return 0; +} + +#if defined(MBEDTLS_MD5_C) +static int pem_pbkdf1(unsigned char *key, size_t keylen, + unsigned char *iv, + const unsigned char *pwd, size_t pwdlen) +{ + mbedtls_md_context_t md5_ctx; + const mbedtls_md_info_t *md5_info; + unsigned char md5sum[16]; + size_t use_len; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_md_init(&md5_ctx); + + /* Prepare the context. (setup() errors gracefully on NULL info.) */ + md5_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); + if ((ret = mbedtls_md_setup(&md5_ctx, md5_info, 0)) != 0) { + goto exit; + } + + /* + * key[ 0..15] = MD5(pwd || IV) + */ + if ((ret = mbedtls_md_starts(&md5_ctx)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md5_ctx, pwd, pwdlen)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md5_ctx, iv, 8)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_finish(&md5_ctx, md5sum)) != 0) { + goto exit; + } + + if (keylen <= 16) { + memcpy(key, md5sum, keylen); + goto exit; + } + + memcpy(key, md5sum, 16); + + /* + * key[16..23] = MD5(key[ 0..15] || pwd || IV]) + */ + if ((ret = mbedtls_md_starts(&md5_ctx)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md5_ctx, md5sum, 16)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md5_ctx, pwd, pwdlen)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md5_ctx, iv, 8)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_finish(&md5_ctx, md5sum)) != 0) { + goto exit; + } + + use_len = 16; + if (keylen < 32) { + use_len = keylen - 16; + } + + memcpy(key + 16, md5sum, use_len); + +exit: + mbedtls_md_free(&md5_ctx); + mbedtls_platform_zeroize(md5sum, 16); + + return ret; +} +#else +static int pem_pbkdf1(unsigned char *key, size_t keylen, + unsigned char *iv, + const unsigned char *pwd, size_t pwdlen) +{ + unsigned char md5sum[16]; + psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + size_t output_length = 0; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + + if ((status = psa_hash_setup(&operation, PSA_ALG_MD5)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_update(&operation, pwd, pwdlen)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_update(&operation, iv, 8)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_finish(&operation, md5sum, + PSA_HASH_LENGTH(PSA_ALG_MD5), + &output_length)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_abort(&operation)) != PSA_SUCCESS) { + goto exit; + } + + /* + * key[ 0..15] = MD5(pwd || IV) + */ + if (keylen <= 16) { + memcpy(key, md5sum, keylen); + goto exit; + } + + memcpy(key, md5sum, 16); + + /* + * key[16..23] = MD5(key[ 0..15] || pwd || IV]) + */ + if ((status = psa_hash_setup(&operation, PSA_ALG_MD5)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_update(&operation, md5sum, 16)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_update(&operation, pwd, pwdlen)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_update(&operation, iv, 8)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_finish(&operation, md5sum, + PSA_HASH_LENGTH(PSA_ALG_MD5), + &output_length)) != PSA_SUCCESS) { + goto exit; + } + + if ((status = psa_hash_abort(&operation)) != PSA_SUCCESS) { + goto exit; + } + + size_t use_len = 16; + if (keylen < 32) { + use_len = keylen - 16; + } + + memcpy(key + 16, md5sum, use_len); + +exit: + mbedtls_platform_zeroize(md5sum, 16); + + return PSA_TO_MBEDTLS_ERR(status); +} +#endif /* MBEDTLS_MD5_C */ + +#if defined(MBEDTLS_DES_C) +/* + * Decrypt with DES-CBC, using PBKDF1 for key derivation + */ +static int pem_des_decrypt(unsigned char des_iv[8], + unsigned char *buf, size_t buflen, + const unsigned char *pwd, size_t pwdlen) +{ + mbedtls_des_context des_ctx; + unsigned char des_key[8]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_des_init(&des_ctx); + + if ((ret = pem_pbkdf1(des_key, 8, des_iv, pwd, pwdlen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_des_setkey_dec(&des_ctx, des_key)) != 0) { + goto exit; + } + ret = mbedtls_des_crypt_cbc(&des_ctx, MBEDTLS_DES_DECRYPT, buflen, + des_iv, buf, buf); + +exit: + mbedtls_des_free(&des_ctx); + mbedtls_platform_zeroize(des_key, 8); + + return ret; +} + +/* + * Decrypt with 3DES-CBC, using PBKDF1 for key derivation + */ +static int pem_des3_decrypt(unsigned char des3_iv[8], + unsigned char *buf, size_t buflen, + const unsigned char *pwd, size_t pwdlen) +{ + mbedtls_des3_context des3_ctx; + unsigned char des3_key[24]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_des3_init(&des3_ctx); + + if ((ret = pem_pbkdf1(des3_key, 24, des3_iv, pwd, pwdlen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_des3_set3key_dec(&des3_ctx, des3_key)) != 0) { + goto exit; + } + ret = mbedtls_des3_crypt_cbc(&des3_ctx, MBEDTLS_DES_DECRYPT, buflen, + des3_iv, buf, buf); + +exit: + mbedtls_des3_free(&des3_ctx); + mbedtls_platform_zeroize(des3_key, 24); + + return ret; +} +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_AES_C) +/* + * Decrypt with AES-XXX-CBC, using PBKDF1 for key derivation + */ +static int pem_aes_decrypt(unsigned char aes_iv[16], unsigned int keylen, + unsigned char *buf, size_t buflen, + const unsigned char *pwd, size_t pwdlen) +{ + mbedtls_aes_context aes_ctx; + unsigned char aes_key[32]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_aes_init(&aes_ctx); + + if ((ret = pem_pbkdf1(aes_key, keylen, aes_iv, pwd, pwdlen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_aes_setkey_dec(&aes_ctx, aes_key, keylen * 8)) != 0) { + goto exit; + } + ret = mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, buflen, + aes_iv, buf, buf); + +exit: + mbedtls_aes_free(&aes_ctx); + mbedtls_platform_zeroize(aes_key, keylen); + + return ret; +} +#endif /* MBEDTLS_AES_C */ + +#endif /* PEM_RFC1421 */ + +int mbedtls_pem_read_buffer(mbedtls_pem_context *ctx, const char *header, const char *footer, + const unsigned char *data, const unsigned char *pwd, + size_t pwdlen, size_t *use_len) +{ + int ret, enc; + size_t len; + unsigned char *buf; + const unsigned char *s1, *s2, *end; +#if defined(PEM_RFC1421) + unsigned char pem_iv[16]; + mbedtls_cipher_type_t enc_alg = MBEDTLS_CIPHER_NONE; +#else + ((void) pwd); + ((void) pwdlen); +#endif /* PEM_RFC1421 */ + + if (ctx == NULL) { + return MBEDTLS_ERR_PEM_BAD_INPUT_DATA; + } + + s1 = (unsigned char *) strstr((const char *) data, header); + + if (s1 == NULL) { + return MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } + + s2 = (unsigned char *) strstr((const char *) data, footer); + + if (s2 == NULL || s2 <= s1) { + return MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } + + s1 += strlen(header); + if (*s1 == ' ') { + s1++; + } + if (*s1 == '\r') { + s1++; + } + if (*s1 == '\n') { + s1++; + } else { + return MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } + + end = s2; + end += strlen(footer); + if (*end == ' ') { + end++; + } + if (*end == '\r') { + end++; + } + if (*end == '\n') { + end++; + } + *use_len = end - data; + + enc = 0; + + if (s2 - s1 >= 22 && memcmp(s1, "Proc-Type: 4,ENCRYPTED", 22) == 0) { +#if defined(PEM_RFC1421) + enc++; + + s1 += 22; + if (*s1 == '\r') { + s1++; + } + if (*s1 == '\n') { + s1++; + } else { + return MBEDTLS_ERR_PEM_INVALID_DATA; + } + + +#if defined(MBEDTLS_DES_C) + if (s2 - s1 >= 23 && memcmp(s1, "DEK-Info: DES-EDE3-CBC,", 23) == 0) { + enc_alg = MBEDTLS_CIPHER_DES_EDE3_CBC; + + s1 += 23; + if (s2 - s1 < 16 || pem_get_iv(s1, pem_iv, 8) != 0) { + return MBEDTLS_ERR_PEM_INVALID_ENC_IV; + } + + s1 += 16; + } else if (s2 - s1 >= 18 && memcmp(s1, "DEK-Info: DES-CBC,", 18) == 0) { + enc_alg = MBEDTLS_CIPHER_DES_CBC; + + s1 += 18; + if (s2 - s1 < 16 || pem_get_iv(s1, pem_iv, 8) != 0) { + return MBEDTLS_ERR_PEM_INVALID_ENC_IV; + } + + s1 += 16; + } +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_AES_C) + if (s2 - s1 >= 14 && memcmp(s1, "DEK-Info: AES-", 14) == 0) { + if (s2 - s1 < 22) { + return MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG; + } else if (memcmp(s1, "DEK-Info: AES-128-CBC,", 22) == 0) { + enc_alg = MBEDTLS_CIPHER_AES_128_CBC; + } else if (memcmp(s1, "DEK-Info: AES-192-CBC,", 22) == 0) { + enc_alg = MBEDTLS_CIPHER_AES_192_CBC; + } else if (memcmp(s1, "DEK-Info: AES-256-CBC,", 22) == 0) { + enc_alg = MBEDTLS_CIPHER_AES_256_CBC; + } else { + return MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG; + } + + s1 += 22; + if (s2 - s1 < 32 || pem_get_iv(s1, pem_iv, 16) != 0) { + return MBEDTLS_ERR_PEM_INVALID_ENC_IV; + } + + s1 += 32; + } +#endif /* MBEDTLS_AES_C */ + + if (enc_alg == MBEDTLS_CIPHER_NONE) { + return MBEDTLS_ERR_PEM_UNKNOWN_ENC_ALG; + } + + if (*s1 == '\r') { + s1++; + } + if (*s1 == '\n') { + s1++; + } else { + return MBEDTLS_ERR_PEM_INVALID_DATA; + } +#else + return MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE; +#endif /* PEM_RFC1421 */ + } + + if (s1 >= s2) { + return MBEDTLS_ERR_PEM_INVALID_DATA; + } + + ret = mbedtls_base64_decode(NULL, 0, &len, s1, s2 - s1); + + if (ret == MBEDTLS_ERR_BASE64_INVALID_CHARACTER) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PEM_INVALID_DATA, ret); + } + + if ((buf = mbedtls_calloc(1, len)) == NULL) { + return MBEDTLS_ERR_PEM_ALLOC_FAILED; + } + + if ((ret = mbedtls_base64_decode(buf, len, &len, s1, s2 - s1)) != 0) { + mbedtls_platform_zeroize(buf, len); + mbedtls_free(buf); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PEM_INVALID_DATA, ret); + } + + if (enc != 0) { +#if defined(PEM_RFC1421) + if (pwd == NULL) { + mbedtls_platform_zeroize(buf, len); + mbedtls_free(buf); + return MBEDTLS_ERR_PEM_PASSWORD_REQUIRED; + } + + ret = 0; + +#if defined(MBEDTLS_DES_C) + if (enc_alg == MBEDTLS_CIPHER_DES_EDE3_CBC) { + ret = pem_des3_decrypt(pem_iv, buf, len, pwd, pwdlen); + } else if (enc_alg == MBEDTLS_CIPHER_DES_CBC) { + ret = pem_des_decrypt(pem_iv, buf, len, pwd, pwdlen); + } +#endif /* MBEDTLS_DES_C */ + +#if defined(MBEDTLS_AES_C) + if (enc_alg == MBEDTLS_CIPHER_AES_128_CBC) { + ret = pem_aes_decrypt(pem_iv, 16, buf, len, pwd, pwdlen); + } else if (enc_alg == MBEDTLS_CIPHER_AES_192_CBC) { + ret = pem_aes_decrypt(pem_iv, 24, buf, len, pwd, pwdlen); + } else if (enc_alg == MBEDTLS_CIPHER_AES_256_CBC) { + ret = pem_aes_decrypt(pem_iv, 32, buf, len, pwd, pwdlen); + } +#endif /* MBEDTLS_AES_C */ + + if (ret != 0) { + mbedtls_free(buf); + return ret; + } + + /* + * The result will be ASN.1 starting with a SEQUENCE tag, with 1 to 3 + * length bytes (allow 4 to be sure) in all known use cases. + * + * Use that as a heuristic to try to detect password mismatches. + */ + if (len <= 2 || buf[0] != 0x30 || buf[1] > 0x83) { + mbedtls_platform_zeroize(buf, len); + mbedtls_free(buf); + return MBEDTLS_ERR_PEM_PASSWORD_MISMATCH; + } +#else + mbedtls_platform_zeroize(buf, len); + mbedtls_free(buf); + return MBEDTLS_ERR_PEM_FEATURE_UNAVAILABLE; +#endif /* PEM_RFC1421 */ + } + + ctx->buf = buf; + ctx->buflen = len; + + return 0; +} + +void mbedtls_pem_free(mbedtls_pem_context *ctx) +{ + if (ctx->buf != NULL) { + mbedtls_platform_zeroize(ctx->buf, ctx->buflen); + mbedtls_free(ctx->buf); + } + mbedtls_free(ctx->info); + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_pem_context)); +} +#endif /* MBEDTLS_PEM_PARSE_C */ + +#if defined(MBEDTLS_PEM_WRITE_C) +int mbedtls_pem_write_buffer(const char *header, const char *footer, + const unsigned char *der_data, size_t der_len, + unsigned char *buf, size_t buf_len, size_t *olen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *encode_buf = NULL, *c, *p = buf; + size_t len = 0, use_len, add_len = 0; + + mbedtls_base64_encode(NULL, 0, &use_len, der_data, der_len); + add_len = strlen(header) + strlen(footer) + (use_len / 64) + 1; + + if (use_len + add_len > buf_len) { + *olen = use_len + add_len; + return MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL; + } + + if (use_len != 0 && + ((encode_buf = mbedtls_calloc(1, use_len)) == NULL)) { + return MBEDTLS_ERR_PEM_ALLOC_FAILED; + } + + if ((ret = mbedtls_base64_encode(encode_buf, use_len, &use_len, der_data, + der_len)) != 0) { + mbedtls_free(encode_buf); + return ret; + } + + memcpy(p, header, strlen(header)); + p += strlen(header); + c = encode_buf; + + while (use_len) { + len = (use_len > 64) ? 64 : use_len; + memcpy(p, c, len); + use_len -= len; + p += len; + c += len; + *p++ = '\n'; + } + + memcpy(p, footer, strlen(footer)); + p += strlen(footer); + + *p++ = '\0'; + *olen = p - buf; + + /* Clean any remaining data previously written to the buffer */ + memset(buf + *olen, 0, buf_len - *olen); + + mbedtls_free(encode_buf); + return 0; +} +#endif /* MBEDTLS_PEM_WRITE_C */ +#endif /* MBEDTLS_PEM_PARSE_C || MBEDTLS_PEM_WRITE_C */ diff --git a/r5dev/thirdparty/mbedtls/pk.c b/r5dev/thirdparty/mbedtls/pk.c new file mode 100644 index 00000000..5e18ad29 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pk.c @@ -0,0 +1,960 @@ +/* + * Public Key abstraction layer + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PK_C) +#include "mbedtls/pk.h" +#include "pk_wrap.h" +#include "pkwrite.h" + +#include "hash_info.h" + +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif +#if defined(MBEDTLS_ECDSA_C) +#include "mbedtls/ecdsa.h" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_C) +#include "mbedtls/psa_util.h" +#define PSA_PK_TO_MBEDTLS_ERR(status) psa_pk_status_to_mbedtls(status) +#define PSA_PK_RSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_pk_rsa_errors, \ + psa_pk_status_to_mbedtls) +#define PSA_PK_ECDSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_pk_ecdsa_errors, \ + psa_pk_status_to_mbedtls) +#endif + +#include +#include + +/* + * Initialise a mbedtls_pk_context + */ +void mbedtls_pk_init(mbedtls_pk_context *ctx) +{ + ctx->pk_info = NULL; + ctx->pk_ctx = NULL; +} + +/* + * Free (the components of) a mbedtls_pk_context + */ +void mbedtls_pk_free(mbedtls_pk_context *ctx) +{ + if (ctx == NULL) { + return; + } + + if (ctx->pk_info != NULL) { + ctx->pk_info->ctx_free_func(ctx->pk_ctx); + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_pk_context)); +} + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/* + * Initialize a restart context + */ +void mbedtls_pk_restart_init(mbedtls_pk_restart_ctx *ctx) +{ + ctx->pk_info = NULL; + ctx->rs_ctx = NULL; +} + +/* + * Free the components of a restart context + */ +void mbedtls_pk_restart_free(mbedtls_pk_restart_ctx *ctx) +{ + if (ctx == NULL || ctx->pk_info == NULL || + ctx->pk_info->rs_free_func == NULL) { + return; + } + + ctx->pk_info->rs_free_func(ctx->rs_ctx); + + ctx->pk_info = NULL; + ctx->rs_ctx = NULL; +} +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +/* + * Get pk_info structure from type + */ +const mbedtls_pk_info_t *mbedtls_pk_info_from_type(mbedtls_pk_type_t pk_type) +{ + switch (pk_type) { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_PK_RSA: + return &mbedtls_rsa_info; +#endif +#if defined(MBEDTLS_ECP_C) + case MBEDTLS_PK_ECKEY: + return &mbedtls_eckey_info; + case MBEDTLS_PK_ECKEY_DH: + return &mbedtls_eckeydh_info; +#endif +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + case MBEDTLS_PK_ECDSA: + return &mbedtls_ecdsa_info; +#endif + /* MBEDTLS_PK_RSA_ALT omitted on purpose */ + default: + return NULL; + } +} + +/* + * Initialise context + */ +int mbedtls_pk_setup(mbedtls_pk_context *ctx, const mbedtls_pk_info_t *info) +{ + if (info == NULL || ctx->pk_info != NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if ((ctx->pk_ctx = info->ctx_alloc_func()) == NULL) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } + + ctx->pk_info = info; + + return 0; +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/* + * Initialise a PSA-wrapping context + */ +int mbedtls_pk_setup_opaque(mbedtls_pk_context *ctx, + const mbedtls_svc_key_id_t key) +{ + const mbedtls_pk_info_t *info = NULL; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t *pk_ctx; + psa_key_type_t type; + + if (ctx == NULL || ctx->pk_info != NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (PSA_SUCCESS != psa_get_key_attributes(key, &attributes)) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + type = psa_get_key_type(&attributes); + psa_reset_key_attributes(&attributes); + + if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type)) { + info = &mbedtls_pk_ecdsa_opaque_info; + } else if (type == PSA_KEY_TYPE_RSA_KEY_PAIR) { + info = &mbedtls_pk_rsa_opaque_info; + } else { + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + } + + if ((ctx->pk_ctx = info->ctx_alloc_func()) == NULL) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } + + ctx->pk_info = info; + + pk_ctx = (mbedtls_svc_key_id_t *) ctx->pk_ctx; + *pk_ctx = key; + + return 0; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +/* + * Initialize an RSA-alt context + */ +int mbedtls_pk_setup_rsa_alt(mbedtls_pk_context *ctx, void *key, + mbedtls_pk_rsa_alt_decrypt_func decrypt_func, + mbedtls_pk_rsa_alt_sign_func sign_func, + mbedtls_pk_rsa_alt_key_len_func key_len_func) +{ + mbedtls_rsa_alt_context *rsa_alt; + const mbedtls_pk_info_t *info = &mbedtls_rsa_alt_info; + + if (ctx->pk_info != NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if ((ctx->pk_ctx = info->ctx_alloc_func()) == NULL) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } + + ctx->pk_info = info; + + rsa_alt = (mbedtls_rsa_alt_context *) ctx->pk_ctx; + + rsa_alt->key = key; + rsa_alt->decrypt_func = decrypt_func; + rsa_alt->sign_func = sign_func; + rsa_alt->key_len_func = key_len_func; + + return 0; +} +#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */ + +/* + * Tell if a PK can do the operations of the given type + */ +int mbedtls_pk_can_do(const mbedtls_pk_context *ctx, mbedtls_pk_type_t type) +{ + /* A context with null pk_info is not set up yet and can't do anything. + * For backward compatibility, also accept NULL instead of a context + * pointer. */ + if (ctx == NULL || ctx->pk_info == NULL) { + return 0; + } + + return ctx->pk_info->can_do(type); +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/* + * Tell if a PK can do the operations of the given PSA algorithm + */ +int mbedtls_pk_can_do_ext(const mbedtls_pk_context *ctx, psa_algorithm_t alg, + psa_key_usage_t usage) +{ + psa_key_usage_t key_usage; + + /* A context with null pk_info is not set up yet and can't do anything. + * For backward compatibility, also accept NULL instead of a context + * pointer. */ + if (ctx == NULL || ctx->pk_info == NULL) { + return 0; + } + + /* Filter out non allowed algorithms */ + if (PSA_ALG_IS_ECDSA(alg) == 0 && + PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) == 0 && + PSA_ALG_IS_RSA_PSS(alg) == 0 && + alg != PSA_ALG_RSA_PKCS1V15_CRYPT && + PSA_ALG_IS_ECDH(alg) == 0) { + return 0; + } + + /* Filter out non allowed usage flags */ + if (usage == 0 || + (usage & ~(PSA_KEY_USAGE_SIGN_HASH | + PSA_KEY_USAGE_DECRYPT | + PSA_KEY_USAGE_DERIVE)) != 0) { + return 0; + } + + /* Wildcard hash is not allowed */ + if (PSA_ALG_IS_SIGN_HASH(alg) && + PSA_ALG_SIGN_GET_HASH(alg) == PSA_ALG_ANY_HASH) { + return 0; + } + + if (mbedtls_pk_get_type(ctx) != MBEDTLS_PK_OPAQUE) { + mbedtls_pk_type_t type; + + if (PSA_ALG_IS_ECDSA(alg) || PSA_ALG_IS_ECDH(alg)) { + type = MBEDTLS_PK_ECKEY; + } else if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) || + alg == PSA_ALG_RSA_PKCS1V15_CRYPT) { + type = MBEDTLS_PK_RSA; + } else if (PSA_ALG_IS_RSA_PSS(alg)) { + type = MBEDTLS_PK_RSASSA_PSS; + } else { + return 0; + } + + if (ctx->pk_info->can_do(type) == 0) { + return 0; + } + + switch (type) { + case MBEDTLS_PK_ECKEY: + key_usage = PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_DERIVE; + break; + case MBEDTLS_PK_RSA: + case MBEDTLS_PK_RSASSA_PSS: + key_usage = PSA_KEY_USAGE_SIGN_HASH | + PSA_KEY_USAGE_SIGN_MESSAGE | + PSA_KEY_USAGE_DECRYPT; + break; + default: + /* Should never happen */ + return 0; + } + + return (key_usage & usage) == usage; + } + + const mbedtls_svc_key_id_t *key = (const mbedtls_svc_key_id_t *) ctx->pk_ctx; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t key_alg, key_alg2; + psa_status_t status; + + status = psa_get_key_attributes(*key, &attributes); + if (status != PSA_SUCCESS) { + return 0; + } + + key_alg = psa_get_key_algorithm(&attributes); + key_alg2 = psa_get_key_enrollment_algorithm(&attributes); + key_usage = psa_get_key_usage_flags(&attributes); + psa_reset_key_attributes(&attributes); + + if ((key_usage & usage) != usage) { + return 0; + } + + /* + * Common case: the key alg or alg2 only allows alg. + * This will match PSA_ALG_RSA_PKCS1V15_CRYPT & PSA_ALG_IS_ECDH + * directly. + * This would also match ECDSA/RSA_PKCS1V15_SIGN/RSA_PSS with + * a fixed hash on key_alg/key_alg2. + */ + if (alg == key_alg || alg == key_alg2) { + return 1; + } + + /* + * If key_alg or key_alg2 is a hash-and-sign with a wildcard for the hash, + * and alg is the same hash-and-sign family with any hash, + * then alg is compliant with this key alg + */ + if (PSA_ALG_IS_SIGN_HASH(alg)) { + + if (PSA_ALG_IS_SIGN_HASH(key_alg) && + PSA_ALG_SIGN_GET_HASH(key_alg) == PSA_ALG_ANY_HASH && + (alg & ~PSA_ALG_HASH_MASK) == (key_alg & ~PSA_ALG_HASH_MASK)) { + return 1; + } + + if (PSA_ALG_IS_SIGN_HASH(key_alg2) && + PSA_ALG_SIGN_GET_HASH(key_alg2) == PSA_ALG_ANY_HASH && + (alg & ~PSA_ALG_HASH_MASK) == (key_alg2 & ~PSA_ALG_HASH_MASK)) { + return 1; + } + } + + return 0; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +/* + * Helper for mbedtls_pk_sign and mbedtls_pk_verify + */ +static inline int pk_hashlen_helper(mbedtls_md_type_t md_alg, size_t *hash_len) +{ + if (*hash_len != 0) { + return 0; + } + + *hash_len = mbedtls_hash_info_get_size(md_alg); + + if (*hash_len == 0) { + return -1; + } + + return 0; +} + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/* + * Helper to set up a restart context if needed + */ +static int pk_restart_setup(mbedtls_pk_restart_ctx *ctx, + const mbedtls_pk_info_t *info) +{ + /* Don't do anything if already set up or invalid */ + if (ctx == NULL || ctx->pk_info != NULL) { + return 0; + } + + /* Should never happen when we're called */ + if (info->rs_alloc_func == NULL || info->rs_free_func == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if ((ctx->rs_ctx = info->rs_alloc_func()) == NULL) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } + + ctx->pk_info = info; + + return 0; +} +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +/* + * Verify a signature (restartable) + */ +int mbedtls_pk_verify_restartable(mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len, + mbedtls_pk_restart_ctx *rs_ctx) +{ + if ((md_alg != MBEDTLS_MD_NONE || hash_len != 0) && hash == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (ctx->pk_info == NULL || + pk_hashlen_helper(md_alg, &hash_len) != 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + /* optimization: use non-restartable version if restart disabled */ + if (rs_ctx != NULL && + mbedtls_ecp_restart_is_enabled() && + ctx->pk_info->verify_rs_func != NULL) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = pk_restart_setup(rs_ctx, ctx->pk_info)) != 0) { + return ret; + } + + ret = ctx->pk_info->verify_rs_func(ctx->pk_ctx, + md_alg, hash, hash_len, sig, sig_len, rs_ctx->rs_ctx); + + if (ret != MBEDTLS_ERR_ECP_IN_PROGRESS) { + mbedtls_pk_restart_free(rs_ctx); + } + + return ret; + } +#else /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + (void) rs_ctx; +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + + if (ctx->pk_info->verify_func == NULL) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + return ctx->pk_info->verify_func(ctx->pk_ctx, md_alg, hash, hash_len, + sig, sig_len); +} + +/* + * Verify a signature + */ +int mbedtls_pk_verify(mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len) +{ + return mbedtls_pk_verify_restartable(ctx, md_alg, hash, hash_len, + sig, sig_len, NULL); +} + +/* + * Verify a signature with options + */ +int mbedtls_pk_verify_ext(mbedtls_pk_type_t type, const void *options, + mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len) +{ + if ((md_alg != MBEDTLS_MD_NONE || hash_len != 0) && hash == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (ctx->pk_info == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (!mbedtls_pk_can_do(ctx, type)) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + if (type != MBEDTLS_PK_RSASSA_PSS) { + /* General case: no options */ + if (options != NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + return mbedtls_pk_verify(ctx, md_alg, hash, hash_len, sig, sig_len); + } + +#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_PKCS1_V21) + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_pk_rsassa_pss_options *pss_opts; + + if (md_alg == MBEDTLS_MD_NONE && UINT_MAX < hash_len) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (options == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + pss_opts = (const mbedtls_pk_rsassa_pss_options *) options; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (pss_opts->mgf1_hash_id == md_alg) { + unsigned char buf[MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES]; + unsigned char *p; + int key_len; + size_t signature_length; + psa_status_t status = PSA_ERROR_DATA_CORRUPT; + psa_status_t destruction_status = PSA_ERROR_DATA_CORRUPT; + + psa_algorithm_t psa_md_alg = mbedtls_hash_info_psa_from_md(md_alg); + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t psa_sig_alg = PSA_ALG_RSA_PSS_ANY_SALT(psa_md_alg); + p = buf + sizeof(buf); + key_len = mbedtls_pk_write_pubkey(&p, buf, ctx); + + if (key_len < 0) { + return key_len; + } + + psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attributes, psa_sig_alg); + + status = psa_import_key(&attributes, + buf + sizeof(buf) - key_len, key_len, + &key_id); + if (status != PSA_SUCCESS) { + psa_destroy_key(key_id); + return PSA_PK_TO_MBEDTLS_ERR(status); + } + + /* This function requires returning MBEDTLS_ERR_PK_SIG_LEN_MISMATCH + * on a valid signature with trailing data in a buffer, but + * mbedtls_psa_rsa_verify_hash requires the sig_len to be exact, + * so for this reason the passed sig_len is overwritten. Smaller + * signature lengths should not be accepted for verification. */ + signature_length = sig_len > mbedtls_pk_get_len(ctx) ? + mbedtls_pk_get_len(ctx) : sig_len; + status = psa_verify_hash(key_id, psa_sig_alg, hash, + hash_len, sig, signature_length); + destruction_status = psa_destroy_key(key_id); + + if (status == PSA_SUCCESS && sig_len > mbedtls_pk_get_len(ctx)) { + return MBEDTLS_ERR_PK_SIG_LEN_MISMATCH; + } + + if (status == PSA_SUCCESS) { + status = destruction_status; + } + + return PSA_PK_RSA_TO_MBEDTLS_ERR(status); + } else +#endif + { + if (sig_len < mbedtls_pk_get_len(ctx)) { + return MBEDTLS_ERR_RSA_VERIFY_FAILED; + } + + ret = mbedtls_rsa_rsassa_pss_verify_ext(mbedtls_pk_rsa(*ctx), + md_alg, (unsigned int) hash_len, hash, + pss_opts->mgf1_hash_id, + pss_opts->expected_salt_len, + sig); + if (ret != 0) { + return ret; + } + + if (sig_len > mbedtls_pk_get_len(ctx)) { + return MBEDTLS_ERR_PK_SIG_LEN_MISMATCH; + } + + return 0; + } +#else + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; +#endif /* MBEDTLS_RSA_C && MBEDTLS_PKCS1_V21 */ +} + +/* + * Make a signature (restartable) + */ +int mbedtls_pk_sign_restartable(mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_pk_restart_ctx *rs_ctx) +{ + if ((md_alg != MBEDTLS_MD_NONE || hash_len != 0) && hash == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (ctx->pk_info == NULL || pk_hashlen_helper(md_alg, &hash_len) != 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + /* optimization: use non-restartable version if restart disabled */ + if (rs_ctx != NULL && + mbedtls_ecp_restart_is_enabled() && + ctx->pk_info->sign_rs_func != NULL) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = pk_restart_setup(rs_ctx, ctx->pk_info)) != 0) { + return ret; + } + + ret = ctx->pk_info->sign_rs_func(ctx->pk_ctx, md_alg, + hash, hash_len, + sig, sig_size, sig_len, + f_rng, p_rng, rs_ctx->rs_ctx); + + if (ret != MBEDTLS_ERR_ECP_IN_PROGRESS) { + mbedtls_pk_restart_free(rs_ctx); + } + + return ret; + } +#else /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + (void) rs_ctx; +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + + if (ctx->pk_info->sign_func == NULL) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + return ctx->pk_info->sign_func(ctx->pk_ctx, md_alg, + hash, hash_len, + sig, sig_size, sig_len, + f_rng, p_rng); +} + +/* + * Make a signature + */ +int mbedtls_pk_sign(mbedtls_pk_context *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + return mbedtls_pk_sign_restartable(ctx, md_alg, hash, hash_len, + sig, sig_size, sig_len, + f_rng, p_rng, NULL); +} + +#if defined(MBEDTLS_PSA_CRYPTO_C) +/* + * Make a signature given a signature type. + */ +int mbedtls_pk_sign_ext(mbedtls_pk_type_t pk_type, + mbedtls_pk_context *ctx, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ +#if defined(MBEDTLS_RSA_C) + psa_algorithm_t psa_md_alg; +#endif /* MBEDTLS_RSA_C */ + *sig_len = 0; + + if (ctx->pk_info == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (!mbedtls_pk_can_do(ctx, pk_type)) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + if (pk_type != MBEDTLS_PK_RSASSA_PSS) { + return mbedtls_pk_sign(ctx, md_alg, hash, hash_len, + sig, sig_size, sig_len, f_rng, p_rng); + } + +#if defined(MBEDTLS_RSA_C) + psa_md_alg = mbedtls_hash_info_psa_from_md(md_alg); + if (psa_md_alg == 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (mbedtls_pk_get_type(ctx) == MBEDTLS_PK_OPAQUE) { + const mbedtls_svc_key_id_t *key = (const mbedtls_svc_key_id_t *) ctx->pk_ctx; + psa_status_t status; + + status = psa_sign_hash(*key, PSA_ALG_RSA_PSS(psa_md_alg), + hash, hash_len, + sig, sig_size, sig_len); + return PSA_PK_RSA_TO_MBEDTLS_ERR(status); + } + + return mbedtls_pk_psa_rsa_sign_ext(PSA_ALG_RSA_PSS(psa_md_alg), + ctx->pk_ctx, hash, hash_len, + sig, sig_size, sig_len); +#else /* MBEDTLS_RSA_C */ + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; +#endif /* !MBEDTLS_RSA_C */ + +} +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +/* + * Decrypt message + */ +int mbedtls_pk_decrypt(mbedtls_pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + if (ctx->pk_info == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (ctx->pk_info->decrypt_func == NULL) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + return ctx->pk_info->decrypt_func(ctx->pk_ctx, input, ilen, + output, olen, osize, f_rng, p_rng); +} + +/* + * Encrypt message + */ +int mbedtls_pk_encrypt(mbedtls_pk_context *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + if (ctx->pk_info == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (ctx->pk_info->encrypt_func == NULL) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + return ctx->pk_info->encrypt_func(ctx->pk_ctx, input, ilen, + output, olen, osize, f_rng, p_rng); +} + +/* + * Check public-private key pair + */ +int mbedtls_pk_check_pair(const mbedtls_pk_context *pub, + const mbedtls_pk_context *prv, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + if (pub->pk_info == NULL || + prv->pk_info == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (f_rng == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (prv->pk_info->check_pair_func == NULL) { + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + } + + if (prv->pk_info->type == MBEDTLS_PK_RSA_ALT) { + if (pub->pk_info->type != MBEDTLS_PK_RSA) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + } else { + if (pub->pk_info != prv->pk_info) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + } + + return prv->pk_info->check_pair_func(pub->pk_ctx, prv->pk_ctx, f_rng, p_rng); +} + +/* + * Get key size in bits + */ +size_t mbedtls_pk_get_bitlen(const mbedtls_pk_context *ctx) +{ + /* For backward compatibility, accept NULL or a context that + * isn't set up yet, and return a fake value that should be safe. */ + if (ctx == NULL || ctx->pk_info == NULL) { + return 0; + } + + return ctx->pk_info->get_bitlen(ctx->pk_ctx); +} + +/* + * Export debug information + */ +int mbedtls_pk_debug(const mbedtls_pk_context *ctx, mbedtls_pk_debug_item *items) +{ + if (ctx->pk_info == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (ctx->pk_info->debug_func == NULL) { + return MBEDTLS_ERR_PK_TYPE_MISMATCH; + } + + ctx->pk_info->debug_func(ctx->pk_ctx, items); + return 0; +} + +/* + * Access the PK type name + */ +const char *mbedtls_pk_get_name(const mbedtls_pk_context *ctx) +{ + if (ctx == NULL || ctx->pk_info == NULL) { + return "invalid PK"; + } + + return ctx->pk_info->name; +} + +/* + * Access the PK type + */ +mbedtls_pk_type_t mbedtls_pk_get_type(const mbedtls_pk_context *ctx) +{ + if (ctx == NULL || ctx->pk_info == NULL) { + return MBEDTLS_PK_NONE; + } + + return ctx->pk_info->type; +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/* + * Load the key to a PSA key slot, + * then turn the PK context into a wrapper for that key slot. + * + * Currently only works for EC & RSA private keys. + */ +int mbedtls_pk_wrap_as_opaque(mbedtls_pk_context *pk, + mbedtls_svc_key_id_t *key, + psa_algorithm_t alg, + psa_key_usage_t usage, + psa_algorithm_t alg2) +{ +#if !defined(MBEDTLS_ECP_C) && !defined(MBEDTLS_RSA_C) + ((void) pk); + ((void) key); + ((void) alg); + ((void) usage); + ((void) alg2); +#else +#if defined(MBEDTLS_ECP_C) + if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_ECKEY) { + const mbedtls_ecp_keypair *ec; + unsigned char d[MBEDTLS_ECP_MAX_BYTES]; + size_t d_len; + psa_ecc_family_t curve_id; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_type_t key_type; + size_t bits; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status; + + /* export the private key material in the format PSA wants */ + ec = mbedtls_pk_ec(*pk); + d_len = PSA_BITS_TO_BYTES(ec->grp.nbits); + if ((ret = mbedtls_mpi_write_binary(&ec->d, d, d_len)) != 0) { + return ret; + } + + curve_id = mbedtls_ecc_group_to_psa(ec->grp.id, &bits); + key_type = PSA_KEY_TYPE_ECC_KEY_PAIR(curve_id); + + /* prepare the key attributes */ + psa_set_key_type(&attributes, key_type); + psa_set_key_bits(&attributes, bits); + psa_set_key_usage_flags(&attributes, usage); + psa_set_key_algorithm(&attributes, alg); + if (alg2 != PSA_ALG_NONE) { + psa_set_key_enrollment_algorithm(&attributes, alg2); + } + + /* import private key into PSA */ + status = psa_import_key(&attributes, d, d_len, key); + if (status != PSA_SUCCESS) { + return PSA_PK_TO_MBEDTLS_ERR(status); + } + + /* make PK context wrap the key slot */ + mbedtls_pk_free(pk); + mbedtls_pk_init(pk); + + return mbedtls_pk_setup_opaque(pk, *key); + } else +#endif /* MBEDTLS_ECP_C */ +#if defined(MBEDTLS_RSA_C) + if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_RSA) { + unsigned char buf[MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES]; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + int key_len; + psa_status_t status; + + /* export the private key material in the format PSA wants */ + key_len = mbedtls_pk_write_key_der(pk, buf, sizeof(buf)); + if (key_len <= 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + /* prepare the key attributes */ + psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_bits(&attributes, mbedtls_pk_get_bitlen(pk)); + psa_set_key_usage_flags(&attributes, usage); + psa_set_key_algorithm(&attributes, alg); + if (alg2 != PSA_ALG_NONE) { + psa_set_key_enrollment_algorithm(&attributes, alg2); + } + + /* import private key into PSA */ + status = psa_import_key(&attributes, + buf + sizeof(buf) - key_len, + key_len, key); + + mbedtls_platform_zeroize(buf, sizeof(buf)); + + if (status != PSA_SUCCESS) { + return PSA_PK_TO_MBEDTLS_ERR(status); + } + + /* make PK context wrap the key slot */ + mbedtls_pk_free(pk); + mbedtls_pk_init(pk); + + return mbedtls_pk_setup_opaque(pk, *key); + } else +#endif /* MBEDTLS_RSA_C */ +#endif /* !MBEDTLS_ECP_C && !MBEDTLS_RSA_C */ + return MBEDTLS_ERR_PK_TYPE_MISMATCH; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#endif /* MBEDTLS_PK_C */ diff --git a/r5dev/thirdparty/mbedtls/pk_wrap.c b/r5dev/thirdparty/mbedtls/pk_wrap.c new file mode 100644 index 00000000..4d91f22b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pk_wrap.c @@ -0,0 +1,1617 @@ +/* + * Public Key abstraction layer: wrapper functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#include "mbedtls/platform_util.h" + +#if defined(MBEDTLS_PK_C) +#include "pk_wrap.h" +#include "mbedtls/error.h" + +/* Even if RSA not activated, for the sake of RSA-alt */ +#include "mbedtls/rsa.h" + +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif + +#if defined(MBEDTLS_ECDSA_C) +#include "mbedtls/ecdsa.h" +#endif + +#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_PSA_CRYPTO_C) +#include "pkwrite.h" +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_C) +#include "mbedtls/psa_util.h" +#define PSA_PK_TO_MBEDTLS_ERR(status) psa_pk_status_to_mbedtls(status) +#define PSA_PK_RSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_pk_rsa_errors, \ + psa_pk_status_to_mbedtls) +#define PSA_PK_ECDSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_pk_ecdsa_errors, \ + psa_pk_status_to_mbedtls) +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#include "hash_info.h" + +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) +#include "mbedtls/asn1write.h" +#include "mbedtls/asn1.h" +#endif +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#include "mbedtls/platform.h" + +#include +#include +#include + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#if defined(MBEDTLS_PSA_CRYPTO_C) +int mbedtls_pk_error_from_psa(psa_status_t status) +{ + switch (status) { + case PSA_SUCCESS: + return 0; + case PSA_ERROR_INVALID_HANDLE: + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + case PSA_ERROR_NOT_PERMITTED: + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + case PSA_ERROR_INVALID_ARGUMENT: + return MBEDTLS_ERR_PK_INVALID_ALG; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_PK_ALLOC_FAILED; + case PSA_ERROR_BAD_STATE: + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + case PSA_ERROR_COMMUNICATION_FAILURE: + case PSA_ERROR_HARDWARE_FAILURE: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + case PSA_ERROR_DATA_CORRUPT: + case PSA_ERROR_DATA_INVALID: + case PSA_ERROR_STORAGE_FAILURE: + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + case PSA_ERROR_CORRUPTION_DETECTED: + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + default: + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + } +} + +#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) || \ + defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) +int mbedtls_pk_error_from_psa_rsa(psa_status_t status) +{ + switch (status) { + case PSA_ERROR_NOT_PERMITTED: + case PSA_ERROR_INVALID_ARGUMENT: + case PSA_ERROR_INVALID_HANDLE: + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE; + case PSA_ERROR_INSUFFICIENT_ENTROPY: + return MBEDTLS_ERR_RSA_RNG_FAILED; + case PSA_ERROR_INVALID_SIGNATURE: + return MBEDTLS_ERR_RSA_VERIFY_FAILED; + case PSA_ERROR_INVALID_PADDING: + return MBEDTLS_ERR_RSA_INVALID_PADDING; + case PSA_SUCCESS: + return 0; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_PK_ALLOC_FAILED; + case PSA_ERROR_BAD_STATE: + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + case PSA_ERROR_COMMUNICATION_FAILURE: + case PSA_ERROR_HARDWARE_FAILURE: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + case PSA_ERROR_DATA_CORRUPT: + case PSA_ERROR_DATA_INVALID: + case PSA_ERROR_STORAGE_FAILURE: + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + case PSA_ERROR_CORRUPTION_DETECTED: + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + default: + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + } +} +#endif /* PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY || PSA_WANT_KEY_TYPE_RSA_KEY_PAIR */ +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +int mbedtls_pk_error_from_psa_ecdsa(psa_status_t status) +{ + switch (status) { + case PSA_ERROR_NOT_PERMITTED: + case PSA_ERROR_INVALID_ARGUMENT: + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + case PSA_ERROR_INVALID_HANDLE: + return MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + case PSA_ERROR_INSUFFICIENT_ENTROPY: + return MBEDTLS_ERR_ECP_RANDOM_FAILED; + case PSA_ERROR_INVALID_SIGNATURE: + return MBEDTLS_ERR_ECP_VERIFY_FAILED; + case PSA_SUCCESS: + return 0; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_PK_ALLOC_FAILED; + case PSA_ERROR_BAD_STATE: + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + case PSA_ERROR_COMMUNICATION_FAILURE: + case PSA_ERROR_HARDWARE_FAILURE: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + case PSA_ERROR_DATA_CORRUPT: + case PSA_ERROR_DATA_INVALID: + case PSA_ERROR_STORAGE_FAILURE: + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + case PSA_ERROR_CORRUPTION_DETECTED: + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + default: + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + } +} +#endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ + +#if defined(MBEDTLS_RSA_C) +static int rsa_can_do(mbedtls_pk_type_t type) +{ + return type == MBEDTLS_PK_RSA || + type == MBEDTLS_PK_RSASSA_PSS; +} + +static size_t rsa_get_bitlen(const void *ctx) +{ + const mbedtls_rsa_context *rsa = (const mbedtls_rsa_context *) ctx; + return 8 * mbedtls_rsa_get_len(rsa); +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +static int rsa_verify_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len) +{ + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *) ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_status_t status; + mbedtls_pk_context key; + int key_len; + unsigned char buf[MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES]; + psa_algorithm_t psa_alg_md = + PSA_ALG_RSA_PKCS1V15_SIGN(mbedtls_hash_info_psa_from_md(md_alg)); + size_t rsa_len = mbedtls_rsa_get_len(rsa); + + if (md_alg == MBEDTLS_MD_NONE && UINT_MAX < hash_len) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (sig_len < rsa_len) { + return MBEDTLS_ERR_RSA_VERIFY_FAILED; + } + + /* mbedtls_pk_write_pubkey_der() expects a full PK context; + * re-construct one to make it happy */ + key.pk_info = &mbedtls_rsa_info; + key.pk_ctx = ctx; + key_len = mbedtls_pk_write_pubkey_der(&key, buf, sizeof(buf)); + if (key_len <= 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attributes, psa_alg_md); + psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + + status = psa_import_key(&attributes, + buf + sizeof(buf) - key_len, key_len, + &key_id); + if (status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + status = psa_verify_hash(key_id, psa_alg_md, hash, hash_len, + sig, sig_len); + if (status != PSA_SUCCESS) { + ret = PSA_PK_RSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } + ret = 0; + +cleanup: + status = psa_destroy_key(key_id); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + } + + return ret; +} +#else +static int rsa_verify_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *) ctx; + size_t rsa_len = mbedtls_rsa_get_len(rsa); + + if (md_alg == MBEDTLS_MD_NONE && UINT_MAX < hash_len) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (sig_len < rsa_len) { + return MBEDTLS_ERR_RSA_VERIFY_FAILED; + } + + if ((ret = mbedtls_rsa_pkcs1_verify(rsa, md_alg, + (unsigned int) hash_len, + hash, sig)) != 0) { + return ret; + } + + /* The buffer contains a valid signature followed by extra data. + * We have a special error code for that so that so that callers can + * use mbedtls_pk_verify() to check "Does the buffer start with a + * valid signature?" and not just "Does the buffer contain a valid + * signature?". */ + if (sig_len > rsa_len) { + return MBEDTLS_ERR_PK_SIG_LEN_MISMATCH; + } + + return 0; +} +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_C) +int mbedtls_pk_psa_rsa_sign_ext(psa_algorithm_t alg, + mbedtls_rsa_context *rsa_ctx, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, + size_t *sig_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_status_t status; + mbedtls_pk_context key; + int key_len; + unsigned char buf[MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES]; + mbedtls_pk_info_t pk_info = mbedtls_rsa_info; + + *sig_len = mbedtls_rsa_get_len(rsa_ctx); + if (sig_size < *sig_len) { + return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL; + } + + /* mbedtls_pk_write_key_der() expects a full PK context; + * re-construct one to make it happy */ + key.pk_info = &pk_info; + key.pk_ctx = rsa_ctx; + key_len = mbedtls_pk_write_key_der(&key, buf, sizeof(buf)); + if (key_len <= 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attributes, alg); + psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_KEY_PAIR); + + status = psa_import_key(&attributes, + buf + sizeof(buf) - key_len, key_len, + &key_id); + if (status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + goto cleanup; + } + status = psa_sign_hash(key_id, alg, hash, hash_len, + sig, sig_size, sig_len); + if (status != PSA_SUCCESS) { + ret = PSA_PK_RSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + ret = 0; + +cleanup: + status = psa_destroy_key(key_id); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + } + return ret; +} +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +static int rsa_sign_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + ((void) f_rng); + ((void) p_rng); + + psa_algorithm_t psa_md_alg; + psa_md_alg = mbedtls_hash_info_psa_from_md(md_alg); + if (psa_md_alg == 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + return mbedtls_pk_psa_rsa_sign_ext(PSA_ALG_RSA_PKCS1V15_SIGN( + psa_md_alg), + ctx, hash, hash_len, + sig, sig_size, sig_len); +} +#else +static int rsa_sign_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *) ctx; + + if (md_alg == MBEDTLS_MD_NONE && UINT_MAX < hash_len) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + *sig_len = mbedtls_rsa_get_len(rsa); + if (sig_size < *sig_len) { + return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL; + } + + return mbedtls_rsa_pkcs1_sign(rsa, f_rng, p_rng, + md_alg, (unsigned int) hash_len, + hash, sig); +} +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +static int rsa_decrypt_wrap(void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *) ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_status_t status; + mbedtls_pk_context key; + int key_len; + unsigned char buf[MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES]; + + ((void) f_rng); + ((void) p_rng); + +#if !defined(MBEDTLS_RSA_ALT) + if (rsa->padding != MBEDTLS_RSA_PKCS_V15) { + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } +#endif /* !MBEDTLS_RSA_ALT */ + + if (ilen != mbedtls_rsa_get_len(rsa)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* mbedtls_pk_write_key_der() expects a full PK context; + * re-construct one to make it happy */ + key.pk_info = &mbedtls_rsa_info; + key.pk_ctx = ctx; + key_len = mbedtls_pk_write_key_der(&key, buf, sizeof(buf)); + if (key_len <= 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, PSA_ALG_RSA_PKCS1V15_CRYPT); + + status = psa_import_key(&attributes, + buf + sizeof(buf) - key_len, key_len, + &key_id); + if (status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + status = psa_asymmetric_decrypt(key_id, PSA_ALG_RSA_PKCS1V15_CRYPT, + input, ilen, + NULL, 0, + output, osize, olen); + if (status != PSA_SUCCESS) { + ret = PSA_PK_RSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + ret = 0; + +cleanup: + mbedtls_platform_zeroize(buf, sizeof(buf)); + status = psa_destroy_key(key_id); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + } + + return ret; +} +#else +static int rsa_decrypt_wrap(void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *) ctx; + + if (ilen != mbedtls_rsa_get_len(rsa)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + return mbedtls_rsa_pkcs1_decrypt(rsa, f_rng, p_rng, + olen, input, output, osize); +} +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +static int rsa_encrypt_wrap(void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *) ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_status_t status; + mbedtls_pk_context key; + int key_len; + unsigned char buf[MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES]; + + ((void) f_rng); + ((void) p_rng); + +#if !defined(MBEDTLS_RSA_ALT) + if (rsa->padding != MBEDTLS_RSA_PKCS_V15) { + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } +#endif + + if (mbedtls_rsa_get_len(rsa) > osize) { + return MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE; + } + + /* mbedtls_pk_write_pubkey_der() expects a full PK context; + * re-construct one to make it happy */ + key.pk_info = &mbedtls_rsa_info; + key.pk_ctx = ctx; + key_len = mbedtls_pk_write_pubkey_der(&key, buf, sizeof(buf)); + if (key_len <= 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attributes, PSA_ALG_RSA_PKCS1V15_CRYPT); + psa_set_key_type(&attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY); + + status = psa_import_key(&attributes, + buf + sizeof(buf) - key_len, key_len, + &key_id); + if (status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + status = psa_asymmetric_encrypt(key_id, PSA_ALG_RSA_PKCS1V15_CRYPT, + input, ilen, + NULL, 0, + output, osize, olen); + if (status != PSA_SUCCESS) { + ret = PSA_PK_RSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + ret = 0; + +cleanup: + status = psa_destroy_key(key_id); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + } + + return ret; +} +#else +static int rsa_encrypt_wrap(void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *) ctx; + *olen = mbedtls_rsa_get_len(rsa); + + if (*olen > osize) { + return MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE; + } + + return mbedtls_rsa_pkcs1_encrypt(rsa, f_rng, p_rng, + ilen, input, output); +} +#endif + +static int rsa_check_pair_wrap(const void *pub, const void *prv, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + (void) f_rng; + (void) p_rng; + return mbedtls_rsa_check_pub_priv((const mbedtls_rsa_context *) pub, + (const mbedtls_rsa_context *) prv); +} + +static void *rsa_alloc_wrap(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_rsa_context)); + + if (ctx != NULL) { + mbedtls_rsa_init((mbedtls_rsa_context *) ctx); + } + + return ctx; +} + +static void rsa_free_wrap(void *ctx) +{ + mbedtls_rsa_free((mbedtls_rsa_context *) ctx); + mbedtls_free(ctx); +} + +static void rsa_debug(const void *ctx, mbedtls_pk_debug_item *items) +{ +#if defined(MBEDTLS_RSA_ALT) + /* Not supported */ + (void) ctx; + (void) items; +#else + items->type = MBEDTLS_PK_DEBUG_MPI; + items->name = "rsa.N"; + items->value = &(((mbedtls_rsa_context *) ctx)->N); + + items++; + + items->type = MBEDTLS_PK_DEBUG_MPI; + items->name = "rsa.E"; + items->value = &(((mbedtls_rsa_context *) ctx)->E); +#endif +} + +const mbedtls_pk_info_t mbedtls_rsa_info = { + MBEDTLS_PK_RSA, + "RSA", + rsa_get_bitlen, + rsa_can_do, + rsa_verify_wrap, + rsa_sign_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, + NULL, +#endif + rsa_decrypt_wrap, + rsa_encrypt_wrap, + rsa_check_pair_wrap, + rsa_alloc_wrap, + rsa_free_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, + NULL, +#endif + rsa_debug, +}; +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) +/* + * Generic EC key + */ +static int eckey_can_do(mbedtls_pk_type_t type) +{ + return type == MBEDTLS_PK_ECKEY || + type == MBEDTLS_PK_ECKEY_DH || + type == MBEDTLS_PK_ECDSA; +} + +static size_t eckey_get_bitlen(const void *ctx) +{ + return ((mbedtls_ecp_keypair *) ctx)->grp.pbits; +} + +#if defined(MBEDTLS_PK_CAN_ECDSA_VERIFY) +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/* + * An ASN.1 encoded signature is a sequence of two ASN.1 integers. Parse one of + * those integers and convert it to the fixed-length encoding expected by PSA. + */ +static int extract_ecdsa_sig_int(unsigned char **from, const unsigned char *end, + unsigned char *to, size_t to_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t unpadded_len, padding_len; + + if ((ret = mbedtls_asn1_get_tag(from, end, &unpadded_len, + MBEDTLS_ASN1_INTEGER)) != 0) { + return ret; + } + + while (unpadded_len > 0 && **from == 0x00) { + (*from)++; + unpadded_len--; + } + + if (unpadded_len > to_len || unpadded_len == 0) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + padding_len = to_len - unpadded_len; + memset(to, 0x00, padding_len); + memcpy(to + padding_len, *from, unpadded_len); + (*from) += unpadded_len; + + return 0; +} + +/* + * Convert a signature from an ASN.1 sequence of two integers + * to a raw {r,s} buffer. Note: the provided sig buffer must be at least + * twice as big as int_size. + */ +static int extract_ecdsa_sig(unsigned char **p, const unsigned char *end, + unsigned char *sig, size_t int_size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t tmp_size; + + if ((ret = mbedtls_asn1_get_tag(p, end, &tmp_size, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + /* Extract r */ + if ((ret = extract_ecdsa_sig_int(p, end, sig, int_size)) != 0) { + return ret; + } + /* Extract s */ + if ((ret = extract_ecdsa_sig_int(p, end, sig + int_size, int_size)) != 0) { + return ret; + } + + return 0; +} + +static int ecdsa_verify_wrap(void *ctx_arg, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len) +{ + mbedtls_ecp_keypair *ctx = ctx_arg; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_status_t status; + size_t key_len; + /* This buffer will initially contain the public key and then the signature + * but at different points in time. For all curves except secp224k1, which + * is not currently supported in PSA, the public key is one byte longer + * (header byte + 2 numbers, while the signature is only 2 numbers), + * so use that as the buffer size. */ + unsigned char buf[MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH]; + unsigned char *p; + psa_algorithm_t psa_sig_md = PSA_ALG_ECDSA_ANY; + size_t curve_bits; + psa_ecc_family_t curve = + mbedtls_ecc_group_to_psa(ctx->grp.id, &curve_bits); + const size_t signature_part_size = (ctx->grp.nbits + 7) / 8; + ((void) md_alg); + + if (curve == 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve)); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attributes, psa_sig_md); + + ret = mbedtls_ecp_point_write_binary(&ctx->grp, &ctx->Q, + MBEDTLS_ECP_PF_UNCOMPRESSED, + &key_len, buf, sizeof(buf)); + if (ret != 0) { + goto cleanup; + } + + status = psa_import_key(&attributes, + buf, key_len, + &key_id); + if (status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + /* We don't need the exported key anymore and can + * reuse its buffer for signature extraction. */ + if (2 * signature_part_size > sizeof(buf)) { + ret = MBEDTLS_ERR_PK_BAD_INPUT_DATA; + goto cleanup; + } + + p = (unsigned char *) sig; + if ((ret = extract_ecdsa_sig(&p, sig + sig_len, buf, + signature_part_size)) != 0) { + goto cleanup; + } + + status = psa_verify_hash(key_id, psa_sig_md, + hash, hash_len, + buf, 2 * signature_part_size); + if (status != PSA_SUCCESS) { + ret = PSA_PK_ECDSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + if (p != sig + sig_len) { + ret = MBEDTLS_ERR_PK_SIG_LEN_MISMATCH; + goto cleanup; + } + ret = 0; + +cleanup: + status = psa_destroy_key(key_id); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + } + + return ret; +} +#else /* MBEDTLS_USE_PSA_CRYPTO */ +static int ecdsa_verify_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + ((void) md_alg); + + ret = mbedtls_ecdsa_read_signature((mbedtls_ecdsa_context *) ctx, + hash, hash_len, sig, sig_len); + + if (ret == MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH) { + return MBEDTLS_ERR_PK_SIG_LEN_MISMATCH; + } + + return ret; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#endif /* MBEDTLS_PK_CAN_ECDSA_VERIFY */ + +#if defined(MBEDTLS_PK_CAN_ECDSA_SIGN) +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/* + * Simultaneously convert and move raw MPI from the beginning of a buffer + * to an ASN.1 MPI at the end of the buffer. + * See also mbedtls_asn1_write_mpi(). + * + * p: pointer to the end of the output buffer + * start: start of the output buffer, and also of the mpi to write at the end + * n_len: length of the mpi to read from start + */ +static int asn1_write_mpibuf(unsigned char **p, unsigned char *start, + size_t n_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + if ((size_t) (*p - start) < n_len) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + len = n_len; + *p -= len; + memmove(*p, start, len); + + /* ASN.1 DER encoding requires minimal length, so skip leading 0s. + * Neither r nor s should be 0, but as a failsafe measure, still detect + * that rather than overflowing the buffer in case of a PSA error. */ + while (len > 0 && **p == 0x00) { + ++(*p); + --len; + } + + /* this is only reached if the signature was invalid */ + if (len == 0) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + + /* if the msb is 1, ASN.1 requires that we prepend a 0. + * Neither r nor s can be 0, so we can assume len > 0 at all times. */ + if (**p & 0x80) { + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = 0x00; + len += 1; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, + MBEDTLS_ASN1_INTEGER)); + + return (int) len; +} + +/* Transcode signature from PSA format to ASN.1 sequence. + * See ecdsa_signature_to_asn1 in ecdsa.c, but with byte buffers instead of + * MPIs, and in-place. + * + * [in/out] sig: the signature pre- and post-transcoding + * [in/out] sig_len: signature length pre- and post-transcoding + * [int] buf_len: the available size the in/out buffer + */ +static int pk_ecdsa_sig_asn1_from_psa(unsigned char *sig, size_t *sig_len, + size_t buf_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + const size_t rs_len = *sig_len / 2; + unsigned char *p = sig + buf_len; + + MBEDTLS_ASN1_CHK_ADD(len, asn1_write_mpibuf(&p, sig + rs_len, rs_len)); + MBEDTLS_ASN1_CHK_ADD(len, asn1_write_mpibuf(&p, sig, rs_len)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, sig, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, sig, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + memmove(sig, p, len); + *sig_len = len; + + return 0; +} + +static int ecdsa_sign_wrap(void *ctx_arg, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_ecp_keypair *ctx = ctx_arg; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + mbedtls_svc_key_id_t key_id = MBEDTLS_SVC_KEY_ID_INIT; + psa_status_t status; + unsigned char buf[MBEDTLS_PSA_MAX_EC_KEY_PAIR_LENGTH]; +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + psa_algorithm_t psa_sig_md = + PSA_ALG_DETERMINISTIC_ECDSA(mbedtls_hash_info_psa_from_md(md_alg)); +#else + psa_algorithm_t psa_sig_md = + PSA_ALG_ECDSA(mbedtls_hash_info_psa_from_md(md_alg)); +#endif + size_t curve_bits; + psa_ecc_family_t curve = + mbedtls_ecc_group_to_psa(ctx->grp.id, &curve_bits); + size_t key_len = PSA_BITS_TO_BYTES(curve_bits); + + /* PSA has its own RNG */ + ((void) f_rng); + ((void) p_rng); + + if (curve == 0) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + if (key_len > sizeof(buf)) { + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } + ret = mbedtls_mpi_write_binary(&ctx->d, buf, key_len); + if (ret != 0) { + goto cleanup; + } + + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(curve)); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attributes, psa_sig_md); + + status = psa_import_key(&attributes, + buf, key_len, + &key_id); + if (status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + status = psa_sign_hash(key_id, psa_sig_md, hash, hash_len, + sig, sig_size, sig_len); + if (status != PSA_SUCCESS) { + ret = PSA_PK_ECDSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + ret = pk_ecdsa_sig_asn1_from_psa(sig, sig_len, sig_size); + +cleanup: + mbedtls_platform_zeroize(buf, sizeof(buf)); + status = psa_destroy_key(key_id); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_PK_TO_MBEDTLS_ERR(status); + } + + return ret; +} +#else /* MBEDTLS_USE_PSA_CRYPTO */ +static int ecdsa_sign_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + return mbedtls_ecdsa_write_signature((mbedtls_ecdsa_context *) ctx, + md_alg, hash, hash_len, + sig, sig_size, sig_len, + f_rng, p_rng); +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#endif /* MBEDTLS_PK_CAN_ECDSA_SIGN */ + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/* Forward declarations */ +static int ecdsa_verify_rs_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len, + void *rs_ctx); + +static int ecdsa_sign_rs_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + void *rs_ctx); + +/* + * Restart context for ECDSA operations with ECKEY context + * + * We need to store an actual ECDSA context, as we need to pass the same to + * the underlying ecdsa function, so we can't create it on the fly every time. + */ +typedef struct { + mbedtls_ecdsa_restart_ctx ecdsa_rs; + mbedtls_ecdsa_context ecdsa_ctx; +} eckey_restart_ctx; + +static void *eckey_rs_alloc(void) +{ + eckey_restart_ctx *rs_ctx; + + void *ctx = mbedtls_calloc(1, sizeof(eckey_restart_ctx)); + + if (ctx != NULL) { + rs_ctx = ctx; + mbedtls_ecdsa_restart_init(&rs_ctx->ecdsa_rs); + mbedtls_ecdsa_init(&rs_ctx->ecdsa_ctx); + } + + return ctx; +} + +static void eckey_rs_free(void *ctx) +{ + eckey_restart_ctx *rs_ctx; + + if (ctx == NULL) { + return; + } + + rs_ctx = ctx; + mbedtls_ecdsa_restart_free(&rs_ctx->ecdsa_rs); + mbedtls_ecdsa_free(&rs_ctx->ecdsa_ctx); + + mbedtls_free(ctx); +} + +static int eckey_verify_rs_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len, + void *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + eckey_restart_ctx *rs = rs_ctx; + + /* Should never happen */ + if (rs == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + /* set up our own sub-context if needed (that is, on first run) */ + if (rs->ecdsa_ctx.grp.pbits == 0) { + MBEDTLS_MPI_CHK(mbedtls_ecdsa_from_keypair(&rs->ecdsa_ctx, ctx)); + } + + MBEDTLS_MPI_CHK(ecdsa_verify_rs_wrap(&rs->ecdsa_ctx, + md_alg, hash, hash_len, + sig, sig_len, &rs->ecdsa_rs)); + +cleanup: + return ret; +} + +static int eckey_sign_rs_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + void *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + eckey_restart_ctx *rs = rs_ctx; + + /* Should never happen */ + if (rs == NULL) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + /* set up our own sub-context if needed (that is, on first run) */ + if (rs->ecdsa_ctx.grp.pbits == 0) { + MBEDTLS_MPI_CHK(mbedtls_ecdsa_from_keypair(&rs->ecdsa_ctx, ctx)); + } + + MBEDTLS_MPI_CHK(ecdsa_sign_rs_wrap(&rs->ecdsa_ctx, md_alg, + hash, hash_len, sig, sig_size, sig_len, + f_rng, p_rng, &rs->ecdsa_rs)); + +cleanup: + return ret; +} +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +static int eckey_check_pair(const void *pub, const void *prv, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + return mbedtls_ecp_check_pub_priv((const mbedtls_ecp_keypair *) pub, + (const mbedtls_ecp_keypair *) prv, + f_rng, p_rng); +} + +static void *eckey_alloc_wrap(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_ecp_keypair)); + + if (ctx != NULL) { + mbedtls_ecp_keypair_init(ctx); + } + + return ctx; +} + +static void eckey_free_wrap(void *ctx) +{ + mbedtls_ecp_keypair_free((mbedtls_ecp_keypair *) ctx); + mbedtls_free(ctx); +} + +static void eckey_debug(const void *ctx, mbedtls_pk_debug_item *items) +{ + items->type = MBEDTLS_PK_DEBUG_ECP; + items->name = "eckey.Q"; + items->value = &(((mbedtls_ecp_keypair *) ctx)->Q); +} + +const mbedtls_pk_info_t mbedtls_eckey_info = { + MBEDTLS_PK_ECKEY, + "EC", + eckey_get_bitlen, + eckey_can_do, +#if defined(MBEDTLS_PK_CAN_ECDSA_VERIFY) + ecdsa_verify_wrap, /* Compatible key structures */ +#else + NULL, +#endif +#if defined(MBEDTLS_PK_CAN_ECDSA_SIGN) + ecdsa_sign_wrap, /* Compatible key structures */ +#else + NULL, +#endif +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + eckey_verify_rs_wrap, + eckey_sign_rs_wrap, +#endif + NULL, + NULL, + eckey_check_pair, + eckey_alloc_wrap, + eckey_free_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + eckey_rs_alloc, + eckey_rs_free, +#endif + eckey_debug, +}; + +/* + * EC key restricted to ECDH + */ +static int eckeydh_can_do(mbedtls_pk_type_t type) +{ + return type == MBEDTLS_PK_ECKEY || + type == MBEDTLS_PK_ECKEY_DH; +} + +const mbedtls_pk_info_t mbedtls_eckeydh_info = { + MBEDTLS_PK_ECKEY_DH, + "EC_DH", + eckey_get_bitlen, /* Same underlying key structure */ + eckeydh_can_do, + NULL, + NULL, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, + NULL, +#endif + NULL, + NULL, + eckey_check_pair, + eckey_alloc_wrap, /* Same underlying key structure */ + eckey_free_wrap, /* Same underlying key structure */ +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, + NULL, +#endif + eckey_debug, /* Same underlying key structure */ +}; +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) +static int ecdsa_can_do(mbedtls_pk_type_t type) +{ + return type == MBEDTLS_PK_ECDSA; +} + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +static int ecdsa_verify_rs_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len, + void *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + ((void) md_alg); + + ret = mbedtls_ecdsa_read_signature_restartable( + (mbedtls_ecdsa_context *) ctx, + hash, hash_len, sig, sig_len, + (mbedtls_ecdsa_restart_ctx *) rs_ctx); + + if (ret == MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH) { + return MBEDTLS_ERR_PK_SIG_LEN_MISMATCH; + } + + return ret; +} + +static int ecdsa_sign_rs_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + void *rs_ctx) +{ + return mbedtls_ecdsa_write_signature_restartable( + (mbedtls_ecdsa_context *) ctx, + md_alg, hash, hash_len, sig, sig_size, sig_len, f_rng, p_rng, + (mbedtls_ecdsa_restart_ctx *) rs_ctx); + +} + +static void *ecdsa_rs_alloc(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_ecdsa_restart_ctx)); + + if (ctx != NULL) { + mbedtls_ecdsa_restart_init(ctx); + } + + return ctx; +} + +static void ecdsa_rs_free(void *ctx) +{ + mbedtls_ecdsa_restart_free(ctx); + mbedtls_free(ctx); +} +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +const mbedtls_pk_info_t mbedtls_ecdsa_info = { + MBEDTLS_PK_ECDSA, + "ECDSA", + eckey_get_bitlen, /* Compatible key structures */ + ecdsa_can_do, +#if defined(MBEDTLS_PK_CAN_ECDSA_VERIFY) + ecdsa_verify_wrap, /* Compatible key structures */ +#else + NULL, +#endif +#if defined(MBEDTLS_PK_CAN_ECDSA_SIGN) + ecdsa_sign_wrap, /* Compatible key structures */ +#else + NULL, +#endif +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + ecdsa_verify_rs_wrap, + ecdsa_sign_rs_wrap, +#endif + NULL, + NULL, + eckey_check_pair, /* Compatible key structures */ + eckey_alloc_wrap, /* Compatible key structures */ + eckey_free_wrap, /* Compatible key structures */ +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + ecdsa_rs_alloc, + ecdsa_rs_free, +#endif + eckey_debug, /* Compatible key structures */ +}; +#endif /* MBEDTLS_PK_CAN_ECDSA_SOME */ + +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +/* + * Support for alternative RSA-private implementations + */ + +static int rsa_alt_can_do(mbedtls_pk_type_t type) +{ + return type == MBEDTLS_PK_RSA; +} + +static size_t rsa_alt_get_bitlen(const void *ctx) +{ + const mbedtls_rsa_alt_context *rsa_alt = (const mbedtls_rsa_alt_context *) ctx; + + return 8 * rsa_alt->key_len_func(rsa_alt->key); +} + +static int rsa_alt_sign_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_rsa_alt_context *rsa_alt = (mbedtls_rsa_alt_context *) ctx; + + if (UINT_MAX < hash_len) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + *sig_len = rsa_alt->key_len_func(rsa_alt->key); + if (*sig_len > MBEDTLS_PK_SIGNATURE_MAX_SIZE) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + if (*sig_len > sig_size) { + return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL; + } + + return rsa_alt->sign_func(rsa_alt->key, f_rng, p_rng, + md_alg, (unsigned int) hash_len, hash, sig); +} + +static int rsa_alt_decrypt_wrap(void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + mbedtls_rsa_alt_context *rsa_alt = (mbedtls_rsa_alt_context *) ctx; + + ((void) f_rng); + ((void) p_rng); + + if (ilen != rsa_alt->key_len_func(rsa_alt->key)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + return rsa_alt->decrypt_func(rsa_alt->key, + olen, input, output, osize); +} + +#if defined(MBEDTLS_RSA_C) +static int rsa_alt_check_pair(const void *pub, const void *prv, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + unsigned char sig[MBEDTLS_MPI_MAX_SIZE]; + unsigned char hash[32]; + size_t sig_len = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (rsa_alt_get_bitlen(prv) != rsa_get_bitlen(pub)) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + memset(hash, 0x2a, sizeof(hash)); + + if ((ret = rsa_alt_sign_wrap((void *) prv, MBEDTLS_MD_NONE, + hash, sizeof(hash), + sig, sizeof(sig), &sig_len, + f_rng, p_rng)) != 0) { + return ret; + } + + if (rsa_verify_wrap((void *) pub, MBEDTLS_MD_NONE, + hash, sizeof(hash), sig, sig_len) != 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + return 0; +} +#endif /* MBEDTLS_RSA_C */ + +static void *rsa_alt_alloc_wrap(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_rsa_alt_context)); + + if (ctx != NULL) { + memset(ctx, 0, sizeof(mbedtls_rsa_alt_context)); + } + + return ctx; +} + +static void rsa_alt_free_wrap(void *ctx) +{ + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_rsa_alt_context)); + mbedtls_free(ctx); +} + +const mbedtls_pk_info_t mbedtls_rsa_alt_info = { + MBEDTLS_PK_RSA_ALT, + "RSA-alt", + rsa_alt_get_bitlen, + rsa_alt_can_do, + NULL, + rsa_alt_sign_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, + NULL, +#endif + rsa_alt_decrypt_wrap, + NULL, +#if defined(MBEDTLS_RSA_C) + rsa_alt_check_pair, +#else + NULL, +#endif + rsa_alt_alloc_wrap, + rsa_alt_free_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, + NULL, +#endif + NULL, +}; + +#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + +static void *pk_opaque_alloc_wrap(void) +{ + void *ctx = mbedtls_calloc(1, sizeof(mbedtls_svc_key_id_t)); + + /* no _init() function to call, as calloc() already zeroized */ + + return ctx; +} + +static void pk_opaque_free_wrap(void *ctx) +{ + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_svc_key_id_t)); + mbedtls_free(ctx); +} + +static size_t pk_opaque_get_bitlen(const void *ctx) +{ + const mbedtls_svc_key_id_t *key = (const mbedtls_svc_key_id_t *) ctx; + size_t bits; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + + if (PSA_SUCCESS != psa_get_key_attributes(*key, &attributes)) { + return 0; + } + + bits = psa_get_key_bits(&attributes); + psa_reset_key_attributes(&attributes); + return bits; +} + +static int pk_opaque_ecdsa_can_do(mbedtls_pk_type_t type) +{ + return type == MBEDTLS_PK_ECKEY || + type == MBEDTLS_PK_ECDSA; +} + +static int pk_opaque_rsa_can_do(mbedtls_pk_type_t type) +{ + return type == MBEDTLS_PK_RSA || + type == MBEDTLS_PK_RSASSA_PSS; +} + +static int pk_opaque_sign_wrap(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ +#if !defined(MBEDTLS_PK_CAN_ECDSA_SIGN) && !defined(MBEDTLS_RSA_C) + ((void) ctx); + ((void) md_alg); + ((void) hash); + ((void) hash_len); + ((void) sig); + ((void) sig_size); + ((void) sig_len); + ((void) f_rng); + ((void) p_rng); + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; +#else /* !MBEDTLS_PK_CAN_ECDSA_SIGN && !MBEDTLS_RSA_C */ + const mbedtls_svc_key_id_t *key = (const mbedtls_svc_key_id_t *) ctx; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t alg; + psa_key_type_t type; + psa_status_t status; + + /* PSA has its own RNG */ + (void) f_rng; + (void) p_rng; + + status = psa_get_key_attributes(*key, &attributes); + if (status != PSA_SUCCESS) { + return PSA_PK_TO_MBEDTLS_ERR(status); + } + + type = psa_get_key_type(&attributes); + psa_reset_key_attributes(&attributes); + +#if defined(MBEDTLS_PK_CAN_ECDSA_SIGN) + if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type)) { + alg = PSA_ALG_ECDSA(mbedtls_hash_info_psa_from_md(md_alg)); + } else +#endif /* MBEDTLS_PK_CAN_ECDSA_SIGN */ +#if defined(MBEDTLS_RSA_C) + if (PSA_KEY_TYPE_IS_RSA(type)) { + alg = PSA_ALG_RSA_PKCS1V15_SIGN(mbedtls_hash_info_psa_from_md(md_alg)); + } else +#endif /* MBEDTLS_RSA_C */ + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + + /* make the signature */ + status = psa_sign_hash(*key, alg, hash, hash_len, + sig, sig_size, sig_len); + if (status != PSA_SUCCESS) { +#if defined(MBEDTLS_PK_CAN_ECDSA_SIGN) + if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type)) { + return PSA_PK_ECDSA_TO_MBEDTLS_ERR(status); + } else +#endif /* MBEDTLS_PK_CAN_ECDSA_SIGN */ +#if defined(MBEDTLS_RSA_C) + if (PSA_KEY_TYPE_IS_RSA(type)) { + return PSA_PK_RSA_TO_MBEDTLS_ERR(status); + } else +#endif /* MBEDTLS_RSA_C */ + return PSA_PK_TO_MBEDTLS_ERR(status); + } + +#if defined(MBEDTLS_PK_CAN_ECDSA_SIGN) + if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(type)) { + /* transcode it to ASN.1 sequence */ + return pk_ecdsa_sig_asn1_from_psa(sig, sig_len, sig_size); + } +#endif /* MBEDTLS_PK_CAN_ECDSA_SIGN */ + + return 0; +#endif /* !MBEDTLS_PK_CAN_ECDSA_SIGN && !MBEDTLS_RSA_C */ +} + +const mbedtls_pk_info_t mbedtls_pk_ecdsa_opaque_info = { + MBEDTLS_PK_OPAQUE, + "Opaque", + pk_opaque_get_bitlen, + pk_opaque_ecdsa_can_do, + NULL, /* verify - will be done later */ + pk_opaque_sign_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, /* restartable verify - not relevant */ + NULL, /* restartable sign - not relevant */ +#endif + NULL, /* decrypt - not relevant */ + NULL, /* encrypt - not relevant */ + NULL, /* check_pair - could be done later or left NULL */ + pk_opaque_alloc_wrap, + pk_opaque_free_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, /* restart alloc - not relevant */ + NULL, /* restart free - not relevant */ +#endif + NULL, /* debug - could be done later, or even left NULL */ +}; + +#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) +static int pk_opaque_rsa_decrypt(void *ctx, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + const mbedtls_svc_key_id_t *key = (const mbedtls_svc_key_id_t *) ctx; + psa_status_t status; + + /* PSA has its own RNG */ + (void) f_rng; + (void) p_rng; + + status = psa_asymmetric_decrypt(*key, PSA_ALG_RSA_PKCS1V15_CRYPT, + input, ilen, + NULL, 0, + output, osize, olen); + if (status != PSA_SUCCESS) { + return PSA_PK_RSA_TO_MBEDTLS_ERR(status); + } + + return 0; +} +#endif /* PSA_WANT_KEY_TYPE_RSA_KEY_PAIR */ + +const mbedtls_pk_info_t mbedtls_pk_rsa_opaque_info = { + MBEDTLS_PK_OPAQUE, + "Opaque", + pk_opaque_get_bitlen, + pk_opaque_rsa_can_do, + NULL, /* verify - will be done later */ + pk_opaque_sign_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, /* restartable verify - not relevant */ + NULL, /* restartable sign - not relevant */ +#endif +#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) + pk_opaque_rsa_decrypt, +#else + NULL, /* decrypt - not available */ +#endif /* PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY */ + NULL, /* encrypt - will be done later */ + NULL, /* check_pair - could be done later or left NULL */ + pk_opaque_alloc_wrap, + pk_opaque_free_wrap, +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + NULL, /* restart alloc - not relevant */ + NULL, /* restart free - not relevant */ +#endif + NULL, /* debug - could be done later, or even left NULL */ +}; + +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#endif /* MBEDTLS_PK_C */ diff --git a/r5dev/thirdparty/mbedtls/pk_wrap.h b/r5dev/thirdparty/mbedtls/pk_wrap.h new file mode 100644 index 00000000..c5cd4df1 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pk_wrap.h @@ -0,0 +1,168 @@ +/** + * \file pk_wrap.h + * + * \brief Public Key abstraction layer: wrapper functions + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_PK_WRAP_H +#define MBEDTLS_PK_WRAP_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/pk.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) +#include "psa/crypto.h" +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +struct mbedtls_pk_info_t { + /** Public key type */ + mbedtls_pk_type_t type; + + /** Type name */ + const char *name; + + /** Get key size in bits */ + size_t (*get_bitlen)(const void *); + + /** Tell if the context implements this type (e.g. ECKEY can do ECDSA) */ + int (*can_do)(mbedtls_pk_type_t type); + + /** Verify signature */ + int (*verify_func)(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len); + + /** Make signature */ + int (*sign_func)(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + /** Verify signature (restartable) */ + int (*verify_rs_func)(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len, + void *rs_ctx); + + /** Make signature (restartable) */ + int (*sign_rs_func)(void *ctx, mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, size_t *sig_len, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, void *rs_ctx); +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + + /** Decrypt message */ + int (*decrypt_func)(void *ctx, const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + + /** Encrypt message */ + int (*encrypt_func)(void *ctx, const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + + /** Check public-private key pair */ + int (*check_pair_func)(const void *pub, const void *prv, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + + /** Allocate a new context */ + void * (*ctx_alloc_func)(void); + + /** Free the given context */ + void (*ctx_free_func)(void *ctx); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + /** Allocate the restart context */ + void *(*rs_alloc_func)(void); + + /** Free the restart context */ + void (*rs_free_func)(void *rs_ctx); +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + + /** Interface with the debug module */ + void (*debug_func)(const void *ctx, mbedtls_pk_debug_item *items); + +}; +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +/* Container for RSA-alt */ +typedef struct { + void *key; + mbedtls_pk_rsa_alt_decrypt_func decrypt_func; + mbedtls_pk_rsa_alt_sign_func sign_func; + mbedtls_pk_rsa_alt_key_len_func key_len_func; +} mbedtls_rsa_alt_context; +#endif + +#if defined(MBEDTLS_RSA_C) +extern const mbedtls_pk_info_t mbedtls_rsa_info; +#endif + +#if defined(MBEDTLS_ECP_C) +extern const mbedtls_pk_info_t mbedtls_eckey_info; +extern const mbedtls_pk_info_t mbedtls_eckeydh_info; +#endif + +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) +extern const mbedtls_pk_info_t mbedtls_ecdsa_info; +#endif + +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) +extern const mbedtls_pk_info_t mbedtls_rsa_alt_info; +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +extern const mbedtls_pk_info_t mbedtls_pk_ecdsa_opaque_info; +extern const mbedtls_pk_info_t mbedtls_pk_rsa_opaque_info; + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#if defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +int MBEDTLS_DEPRECATED mbedtls_pk_error_from_psa_ecdsa(psa_status_t status); +#endif +#endif + +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_PSA_CRYPTO_C) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +int MBEDTLS_DEPRECATED mbedtls_pk_error_from_psa(psa_status_t status); + +#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) || \ + defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) +int MBEDTLS_DEPRECATED mbedtls_pk_error_from_psa_rsa(psa_status_t status); +#endif /* PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY || PSA_WANT_KEY_TYPE_RSA_KEY_PAIR */ +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ + +#if defined(MBEDTLS_RSA_C) +int mbedtls_pk_psa_rsa_sign_ext(psa_algorithm_t psa_alg_md, + mbedtls_rsa_context *rsa_ctx, + const unsigned char *hash, size_t hash_len, + unsigned char *sig, size_t sig_size, + size_t *sig_len); +#endif /* MBEDTLS_RSA_C */ + +#endif /* MBEDTLS_PSA_CRYPTO_C */ + +#endif /* MBEDTLS_PK_WRAP_H */ diff --git a/r5dev/thirdparty/mbedtls/pkcs12.c b/r5dev/thirdparty/mbedtls/pkcs12.c new file mode 100644 index 00000000..85214839 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pkcs12.c @@ -0,0 +1,454 @@ +/* + * PKCS#12 Personal Information Exchange Syntax + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The PKCS #12 Personal Information Exchange Syntax Standard v1.1 + * + * http://www.rsa.com/rsalabs/pkcs/files/h11301-wp-pkcs-12v1-1-personal-information-exchange-syntax.pdf + * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12v1-1.asn + */ + +#include "common.h" + +#if defined(MBEDTLS_PKCS12_C) + +#include "mbedtls/pkcs12.h" +#include "mbedtls/asn1.h" +#include "mbedtls/cipher.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if !defined(MBEDTLS_MD_C) +#include "mbedtls/psa_util.h" +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_md_errors, \ + psa_generic_status_to_mbedtls) +#endif + +#if defined(MBEDTLS_DES_C) +#include "mbedtls/des.h" +#endif + +#include "hash_info.h" +#include "mbedtls/psa_util.h" + +#if defined(MBEDTLS_ASN1_PARSE_C) + +static int pkcs12_parse_pbe_params(mbedtls_asn1_buf *params, + mbedtls_asn1_buf *salt, int *iterations) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char **p = ¶ms->p; + const unsigned char *end = params->p + params->len; + + /* + * pkcs-12PbeParams ::= SEQUENCE { + * salt OCTET STRING, + * iterations INTEGER + * } + * + */ + if (params->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + if ((ret = mbedtls_asn1_get_tag(p, end, &salt->len, MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT, ret); + } + + salt->p = *p; + *p += salt->len; + + if ((ret = mbedtls_asn1_get_int(p, end, iterations)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT, ret); + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS12_PBE_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +#define PKCS12_MAX_PWDLEN 128 + +static int pkcs12_pbe_derive_key_iv(mbedtls_asn1_buf *pbe_params, mbedtls_md_type_t md_type, + const unsigned char *pwd, size_t pwdlen, + unsigned char *key, size_t keylen, + unsigned char *iv, size_t ivlen) +{ + int ret, iterations = 0; + mbedtls_asn1_buf salt; + size_t i; + unsigned char unipwd[PKCS12_MAX_PWDLEN * 2 + 2]; + + if (pwdlen > PKCS12_MAX_PWDLEN) { + return MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA; + } + + memset(&salt, 0, sizeof(mbedtls_asn1_buf)); + memset(&unipwd, 0, sizeof(unipwd)); + + if ((ret = pkcs12_parse_pbe_params(pbe_params, &salt, + &iterations)) != 0) { + return ret; + } + + for (i = 0; i < pwdlen; i++) { + unipwd[i * 2 + 1] = pwd[i]; + } + + if ((ret = mbedtls_pkcs12_derivation(key, keylen, unipwd, pwdlen * 2 + 2, + salt.p, salt.len, md_type, + MBEDTLS_PKCS12_DERIVE_KEY, iterations)) != 0) { + return ret; + } + + if (iv == NULL || ivlen == 0) { + return 0; + } + + if ((ret = mbedtls_pkcs12_derivation(iv, ivlen, unipwd, pwdlen * 2 + 2, + salt.p, salt.len, md_type, + MBEDTLS_PKCS12_DERIVE_IV, iterations)) != 0) { + return ret; + } + return 0; +} + +#undef PKCS12_MAX_PWDLEN + +int mbedtls_pkcs12_pbe(mbedtls_asn1_buf *pbe_params, int mode, + mbedtls_cipher_type_t cipher_type, mbedtls_md_type_t md_type, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *data, size_t len, + unsigned char *output) +{ + int ret, keylen = 0; + unsigned char key[32]; + unsigned char iv[16]; + const mbedtls_cipher_info_t *cipher_info; + mbedtls_cipher_context_t cipher_ctx; + size_t olen = 0; + + if (pwd == NULL && pwdlen != 0) { + return MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA; + } + + cipher_info = mbedtls_cipher_info_from_type(cipher_type); + if (cipher_info == NULL) { + return MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE; + } + + keylen = cipher_info->key_bitlen / 8; + + if ((ret = pkcs12_pbe_derive_key_iv(pbe_params, md_type, pwd, pwdlen, + key, keylen, + iv, cipher_info->iv_size)) != 0) { + return ret; + } + + mbedtls_cipher_init(&cipher_ctx); + + if ((ret = mbedtls_cipher_setup(&cipher_ctx, cipher_info)) != 0) { + goto exit; + } + + if ((ret = + mbedtls_cipher_setkey(&cipher_ctx, key, 8 * keylen, + (mbedtls_operation_t) mode)) != 0) { + goto exit; + } + + if ((ret = mbedtls_cipher_set_iv(&cipher_ctx, iv, cipher_info->iv_size)) != 0) { + goto exit; + } + + if ((ret = mbedtls_cipher_reset(&cipher_ctx)) != 0) { + goto exit; + } + + if ((ret = mbedtls_cipher_update(&cipher_ctx, data, len, + output, &olen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_cipher_finish(&cipher_ctx, output + olen, &olen)) != 0) { + ret = MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH; + } + +exit: + mbedtls_platform_zeroize(key, sizeof(key)); + mbedtls_platform_zeroize(iv, sizeof(iv)); + mbedtls_cipher_free(&cipher_ctx); + + return ret; +} + +#endif /* MBEDTLS_ASN1_PARSE_C */ + +static void pkcs12_fill_buffer(unsigned char *data, size_t data_len, + const unsigned char *filler, size_t fill_len) +{ + unsigned char *p = data; + size_t use_len; + + if (filler != NULL && fill_len != 0) { + while (data_len > 0) { + use_len = (data_len > fill_len) ? fill_len : data_len; + memcpy(p, filler, use_len); + p += use_len; + data_len -= use_len; + } + } else { + /* If either of the above are not true then clearly there is nothing + * that this function can do. The function should *not* be called + * under either of those circumstances, as you could end up with an + * incorrect output but for safety's sake, leaving the check in as + * otherwise we could end up with memory corruption.*/ + } +} + + +static int calculate_hashes(mbedtls_md_type_t md_type, int iterations, + unsigned char *diversifier, unsigned char *salt_block, + unsigned char *pwd_block, unsigned char *hash_output, int use_salt, + int use_password, size_t hlen, size_t v) +{ +#if defined(MBEDTLS_MD_C) + int ret = -1; + size_t i; + const mbedtls_md_info_t *md_info; + mbedtls_md_context_t md_ctx; + md_info = mbedtls_md_info_from_type(md_type); + if (md_info == NULL) { + return MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE; + } + + mbedtls_md_init(&md_ctx); + + if ((ret = mbedtls_md_setup(&md_ctx, md_info, 0)) != 0) { + return ret; + } + // Calculate hash( diversifier || salt_block || pwd_block ) + if ((ret = mbedtls_md_starts(&md_ctx)) != 0) { + goto exit; + } + + if ((ret = mbedtls_md_update(&md_ctx, diversifier, v)) != 0) { + goto exit; + } + + if (use_salt != 0) { + if ((ret = mbedtls_md_update(&md_ctx, salt_block, v)) != 0) { + goto exit; + } + } + + if (use_password != 0) { + if ((ret = mbedtls_md_update(&md_ctx, pwd_block, v)) != 0) { + goto exit; + } + } + + if ((ret = mbedtls_md_finish(&md_ctx, hash_output)) != 0) { + goto exit; + } + + // Perform remaining ( iterations - 1 ) recursive hash calculations + for (i = 1; i < (size_t) iterations; i++) { + if ((ret = mbedtls_md(md_info, hash_output, hlen, hash_output)) + != 0) { + goto exit; + } + } + +exit: + mbedtls_md_free(&md_ctx); + return ret; +#else + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_algorithm_t alg = mbedtls_psa_translate_md(md_type); + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t status_abort = PSA_ERROR_CORRUPTION_DETECTED; + size_t i, out_len, out_size = PSA_HASH_LENGTH(alg); + + if (alg == PSA_ALG_NONE) { + return MBEDTLS_ERR_PKCS12_FEATURE_UNAVAILABLE; + } + + if ((status = psa_hash_setup(&op, alg)) != PSA_SUCCESS) { + goto exit; + } + + // Calculate hash( diversifier || salt_block || pwd_block ) + if ((status = psa_hash_update(&op, diversifier, v)) != PSA_SUCCESS) { + goto exit; + } + + if (use_salt != 0) { + if ((status = psa_hash_update(&op, salt_block, v)) != PSA_SUCCESS) { + goto exit; + } + } + + if (use_password != 0) { + if ((status = psa_hash_update(&op, pwd_block, v)) != PSA_SUCCESS) { + goto exit; + } + } + + if ((status = psa_hash_finish(&op, hash_output, out_size, &out_len)) + != PSA_SUCCESS) { + goto exit; + } + + // Perform remaining ( iterations - 1 ) recursive hash calculations + for (i = 1; i < (size_t) iterations; i++) { + if ((status = psa_hash_compute(alg, hash_output, hlen, hash_output, + out_size, &out_len)) != PSA_SUCCESS) { + goto exit; + } + } + +exit: + status_abort = psa_hash_abort(&op); + if (status == PSA_SUCCESS) { + status = status_abort; + } + return PSA_TO_MBEDTLS_ERR(status); +#endif /* !MBEDTLS_MD_C */ +} + + +int mbedtls_pkcs12_derivation(unsigned char *data, size_t datalen, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *salt, size_t saltlen, + mbedtls_md_type_t md_type, int id, int iterations) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned int j; + + unsigned char diversifier[128]; + unsigned char salt_block[128], pwd_block[128], hash_block[128] = { 0 }; + unsigned char hash_output[MBEDTLS_HASH_MAX_SIZE]; + unsigned char *p; + unsigned char c; + int use_password = 0; + int use_salt = 0; + + size_t hlen, use_len, v, i; + + // This version only allows max of 64 bytes of password or salt + if (datalen > 128 || pwdlen > 64 || saltlen > 64) { + return MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA; + } + + if (pwd == NULL && pwdlen != 0) { + return MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA; + } + + if (salt == NULL && saltlen != 0) { + return MBEDTLS_ERR_PKCS12_BAD_INPUT_DATA; + } + + use_password = (pwd && pwdlen != 0); + use_salt = (salt && saltlen != 0); + + hlen = mbedtls_hash_info_get_size(md_type); + + if (hlen <= 32) { + v = 64; + } else { + v = 128; + } + + memset(diversifier, (unsigned char) id, v); + + if (use_salt != 0) { + pkcs12_fill_buffer(salt_block, v, salt, saltlen); + } + + if (use_password != 0) { + pkcs12_fill_buffer(pwd_block, v, pwd, pwdlen); + } + + p = data; + while (datalen > 0) { + if (calculate_hashes(md_type, iterations, diversifier, salt_block, + pwd_block, hash_output, use_salt, use_password, hlen, + v) != 0) { + goto exit; + } + + use_len = (datalen > hlen) ? hlen : datalen; + memcpy(p, hash_output, use_len); + datalen -= use_len; + p += use_len; + + if (datalen == 0) { + break; + } + + // Concatenating copies of hash_output into hash_block (B) + pkcs12_fill_buffer(hash_block, v, hash_output, hlen); + + // B += 1 + for (i = v; i > 0; i--) { + if (++hash_block[i - 1] != 0) { + break; + } + } + + if (use_salt != 0) { + // salt_block += B + c = 0; + for (i = v; i > 0; i--) { + j = salt_block[i - 1] + hash_block[i - 1] + c; + c = MBEDTLS_BYTE_1(j); + salt_block[i - 1] = MBEDTLS_BYTE_0(j); + } + } + + if (use_password != 0) { + // pwd_block += B + c = 0; + for (i = v; i > 0; i--) { + j = pwd_block[i - 1] + hash_block[i - 1] + c; + c = MBEDTLS_BYTE_1(j); + pwd_block[i - 1] = MBEDTLS_BYTE_0(j); + } + } + } + + ret = 0; + +exit: + mbedtls_platform_zeroize(salt_block, sizeof(salt_block)); + mbedtls_platform_zeroize(pwd_block, sizeof(pwd_block)); + mbedtls_platform_zeroize(hash_block, sizeof(hash_block)); + mbedtls_platform_zeroize(hash_output, sizeof(hash_output)); + + return ret; +} + +#endif /* MBEDTLS_PKCS12_C */ diff --git a/r5dev/thirdparty/mbedtls/pkcs5.c b/r5dev/thirdparty/mbedtls/pkcs5.c new file mode 100644 index 00000000..f471b637 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pkcs5.c @@ -0,0 +1,569 @@ +/** + * \file pkcs5.c + * + * \brief PKCS#5 functions + * + * \author Mathias Olsson + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * PKCS#5 includes PBKDF2 and more + * + * http://tools.ietf.org/html/rfc2898 (Specification) + * http://tools.ietf.org/html/rfc6070 (Test vectors) + */ + +#include "common.h" + +#if defined(MBEDTLS_PKCS5_C) + +#include "mbedtls/pkcs5.h" +#include "mbedtls/error.h" + +#if defined(MBEDTLS_ASN1_PARSE_C) +#include "mbedtls/asn1.h" +#include "mbedtls/cipher.h" +#include "mbedtls/oid.h" +#endif /* MBEDTLS_ASN1_PARSE_C */ + +#include + +#include "mbedtls/platform.h" + +#include "hash_info.h" +#include "mbedtls/psa_util.h" + +#if !defined(MBEDTLS_MD_C) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_md_errors, \ + psa_generic_status_to_mbedtls) +#endif + +#if defined(MBEDTLS_ASN1_PARSE_C) +static int pkcs5_parse_pbkdf2_params(const mbedtls_asn1_buf *params, + mbedtls_asn1_buf *salt, int *iterations, + int *keylen, mbedtls_md_type_t *md_type) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_asn1_buf prf_alg_oid; + unsigned char *p = params->p; + const unsigned char *end = params->p + params->len; + + if (params->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + /* + * PBKDF2-params ::= SEQUENCE { + * salt OCTET STRING, + * iterationCount INTEGER, + * keyLength INTEGER OPTIONAL + * prf AlgorithmIdentifier DEFAULT algid-hmacWithSHA1 + * } + * + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &salt->len, + MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, ret); + } + + salt->p = p; + p += salt->len; + + if ((ret = mbedtls_asn1_get_int(&p, end, iterations)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, ret); + } + + if (p == end) { + return 0; + } + + if ((ret = mbedtls_asn1_get_int(&p, end, keylen)) != 0) { + if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, ret); + } + } + + if (p == end) { + return 0; + } + + if ((ret = mbedtls_asn1_get_alg_null(&p, end, &prf_alg_oid)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, ret); + } + + if (mbedtls_oid_get_md_hmac(&prf_alg_oid, md_type) != 0) { + return MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE; + } + + if (p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +int mbedtls_pkcs5_pbes2(const mbedtls_asn1_buf *pbe_params, int mode, + const unsigned char *pwd, size_t pwdlen, + const unsigned char *data, size_t datalen, + unsigned char *output) +{ + int ret, iterations = 0, keylen = 0; + unsigned char *p, *end; + mbedtls_asn1_buf kdf_alg_oid, enc_scheme_oid, kdf_alg_params, enc_scheme_params; + mbedtls_asn1_buf salt; + mbedtls_md_type_t md_type = MBEDTLS_MD_SHA1; + unsigned char key[32], iv[32]; + size_t olen = 0; + const mbedtls_cipher_info_t *cipher_info; + mbedtls_cipher_type_t cipher_alg; + mbedtls_cipher_context_t cipher_ctx; + + p = pbe_params->p; + end = p + pbe_params->len; + + /* + * PBES2-params ::= SEQUENCE { + * keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}}, + * encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} + * } + */ + if (pbe_params->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + if ((ret = mbedtls_asn1_get_alg(&p, end, &kdf_alg_oid, + &kdf_alg_params)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, ret); + } + + // Only PBKDF2 supported at the moment + // + if (MBEDTLS_OID_CMP(MBEDTLS_OID_PKCS5_PBKDF2, &kdf_alg_oid) != 0) { + return MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE; + } + + if ((ret = pkcs5_parse_pbkdf2_params(&kdf_alg_params, + &salt, &iterations, &keylen, + &md_type)) != 0) { + return ret; + } + + if ((ret = mbedtls_asn1_get_alg(&p, end, &enc_scheme_oid, + &enc_scheme_params)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS5_INVALID_FORMAT, ret); + } + + if (mbedtls_oid_get_cipher_alg(&enc_scheme_oid, &cipher_alg) != 0) { + return MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE; + } + + cipher_info = mbedtls_cipher_info_from_type(cipher_alg); + if (cipher_info == NULL) { + return MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE; + } + + /* + * The value of keylen from pkcs5_parse_pbkdf2_params() is ignored + * since it is optional and we don't know if it was set or not + */ + keylen = cipher_info->key_bitlen / 8; + + if (enc_scheme_params.tag != MBEDTLS_ASN1_OCTET_STRING || + enc_scheme_params.len != cipher_info->iv_size) { + return MBEDTLS_ERR_PKCS5_INVALID_FORMAT; + } + + mbedtls_cipher_init(&cipher_ctx); + + memcpy(iv, enc_scheme_params.p, enc_scheme_params.len); + + if ((ret = mbedtls_pkcs5_pbkdf2_hmac_ext(md_type, pwd, pwdlen, salt.p, + salt.len, iterations, keylen, + key)) != 0) { + goto exit; + } + + if ((ret = mbedtls_cipher_setup(&cipher_ctx, cipher_info)) != 0) { + goto exit; + } + + if ((ret = mbedtls_cipher_setkey(&cipher_ctx, key, 8 * keylen, + (mbedtls_operation_t) mode)) != 0) { + goto exit; + } + + if ((ret = mbedtls_cipher_crypt(&cipher_ctx, iv, enc_scheme_params.len, + data, datalen, output, &olen)) != 0) { + ret = MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH; + } + +exit: + mbedtls_cipher_free(&cipher_ctx); + + return ret; +} +#endif /* MBEDTLS_ASN1_PARSE_C */ + +#if defined(MBEDTLS_MD_C) +static int pkcs5_pbkdf2_hmac(mbedtls_md_context_t *ctx, + const unsigned char *password, + size_t plen, const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned int i; + unsigned char md1[MBEDTLS_MD_MAX_SIZE]; + unsigned char work[MBEDTLS_MD_MAX_SIZE]; + unsigned char md_size = mbedtls_md_get_size(ctx->md_info); + size_t use_len; + unsigned char *out_p = output; + unsigned char counter[4]; + + memset(counter, 0, 4); + counter[3] = 1; + +#if UINT_MAX > 0xFFFFFFFF + if (iteration_count > 0xFFFFFFFF) { + return MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA; + } +#endif + + if ((ret = mbedtls_md_hmac_starts(ctx, password, plen)) != 0) { + return ret; + } + while (key_length) { + // U1 ends up in work + // + if ((ret = mbedtls_md_hmac_update(ctx, salt, slen)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_md_hmac_update(ctx, counter, 4)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_md_hmac_finish(ctx, work)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_md_hmac_reset(ctx)) != 0) { + goto cleanup; + } + + memcpy(md1, work, md_size); + + for (i = 1; i < iteration_count; i++) { + // U2 ends up in md1 + // + if ((ret = mbedtls_md_hmac_update(ctx, md1, md_size)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_md_hmac_finish(ctx, md1)) != 0) { + goto cleanup; + } + + if ((ret = mbedtls_md_hmac_reset(ctx)) != 0) { + goto cleanup; + } + + // U1 xor U2 + // + mbedtls_xor(work, work, md1, md_size); + } + + use_len = (key_length < md_size) ? key_length : md_size; + memcpy(out_p, work, use_len); + + key_length -= (uint32_t) use_len; + out_p += use_len; + + for (i = 4; i > 0; i--) { + if (++counter[i - 1] != 0) { + break; + } + } + } + +cleanup: + /* Zeroise buffers to clear sensitive data from memory. */ + mbedtls_platform_zeroize(work, MBEDTLS_MD_MAX_SIZE); + mbedtls_platform_zeroize(md1, MBEDTLS_MD_MAX_SIZE); + + return ret; +} + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +int mbedtls_pkcs5_pbkdf2_hmac(mbedtls_md_context_t *ctx, + const unsigned char *password, + size_t plen, const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output) +{ + return pkcs5_pbkdf2_hmac(ctx, password, plen, salt, slen, iteration_count, + key_length, output); +} +#endif +#endif /* MBEDTLS_MD_C */ + +int mbedtls_pkcs5_pbkdf2_hmac_ext(mbedtls_md_type_t md_alg, + const unsigned char *password, + size_t plen, const unsigned char *salt, size_t slen, + unsigned int iteration_count, + uint32_t key_length, unsigned char *output) +{ +#if defined(MBEDTLS_MD_C) + mbedtls_md_context_t md_ctx; + const mbedtls_md_info_t *md_info = NULL; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + md_info = mbedtls_md_info_from_type(md_alg); + if (md_info == NULL) { + return MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE; + } + + mbedtls_md_init(&md_ctx); + + if ((ret = mbedtls_md_setup(&md_ctx, md_info, 1)) != 0) { + goto exit; + } + ret = pkcs5_pbkdf2_hmac(&md_ctx, password, plen, salt, slen, + iteration_count, key_length, output); +exit: + mbedtls_md_free(&md_ctx); + return ret; +#else + unsigned int i; + unsigned char md1[PSA_HASH_MAX_SIZE]; + unsigned char work[PSA_HASH_MAX_SIZE]; + const unsigned char md_size = mbedtls_hash_info_get_size(md_alg); + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t status_destruction = PSA_ERROR_CORRUPTION_DETECTED; + size_t use_len, out_len; + unsigned char *out_p = output; + unsigned char counter[4]; + mbedtls_svc_key_id_t psa_hmac_key = MBEDTLS_SVC_KEY_ID_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + const psa_algorithm_t alg = PSA_ALG_HMAC(mbedtls_hash_info_psa_from_md(md_alg)); + const size_t out_size = PSA_MAC_LENGTH(PSA_KEY_TYPE_HMAC, 0, alg); + + memset(counter, 0, sizeof(counter)); + counter[3] = 1; + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attributes, alg); + psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC); + + if (key_length == 0) { + return 0; + } + if ((status = psa_import_key(&attributes, + password, plen, + &psa_hmac_key)) != PSA_SUCCESS) { + return MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA; + } + +#if UINT_MAX > 0xFFFFFFFF + if (iteration_count > 0xFFFFFFFF) { + return MBEDTLS_ERR_PKCS5_BAD_INPUT_DATA; + } +#endif + + while (key_length) { + status = psa_mac_sign_setup(&operation, psa_hmac_key, + PSA_ALG_HMAC(alg)); + if (status != PSA_SUCCESS) { + goto cleanup; + } + // U1 ends up in work + if ((status = psa_mac_update(&operation, salt, slen)) != PSA_SUCCESS) { + goto cleanup; + } + + if ((status = psa_mac_update(&operation, counter, sizeof(counter))) != PSA_SUCCESS) { + goto cleanup; + } + + if ((status = psa_mac_sign_finish(&operation, work, out_size, &out_len)) + != PSA_SUCCESS) { + goto cleanup; + } + + memcpy(md1, work, out_len); + + for (i = 1; i < iteration_count; i++) { + // U2 ends up in md1 + // + status = psa_mac_sign_setup(&operation, psa_hmac_key, + PSA_ALG_HMAC(alg)); + if (status != PSA_SUCCESS) { + goto cleanup; + } + if ((status = psa_mac_update(&operation, md1, md_size)) != PSA_SUCCESS) { + goto cleanup; + } + if ((status = + psa_mac_sign_finish(&operation, md1, out_size, &out_len)) != PSA_SUCCESS) { + goto cleanup; + } + + // U1 xor U2 + // + mbedtls_xor(work, work, md1, md_size); + } + + use_len = (key_length < md_size) ? key_length : md_size; + memcpy(out_p, work, use_len); + + key_length -= (uint32_t) use_len; + out_p += use_len; + + for (i = 4; i > 0; i--) { + if (++counter[i - 1] != 0) { + break; + } + } + } + +cleanup: + /* Zeroise buffers to clear sensitive data from memory. */ + mbedtls_platform_zeroize(work, PSA_HASH_MAX_SIZE); + mbedtls_platform_zeroize(md1, PSA_HASH_MAX_SIZE); + status_destruction = psa_destroy_key(psa_hmac_key); + if (status == PSA_SUCCESS && status_destruction != PSA_SUCCESS) { + status = status_destruction; + } + status_destruction = psa_mac_abort(&operation); + if (status == PSA_SUCCESS && status_destruction != PSA_SUCCESS) { + status = status_destruction; + } + + return PSA_TO_MBEDTLS_ERR(status); +#endif /* !MBEDTLS_MD_C */ +} + +#if defined(MBEDTLS_SELF_TEST) + +#if !defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA) +int mbedtls_pkcs5_self_test(int verbose) +{ + if (verbose != 0) { + mbedtls_printf(" PBKDF2 (SHA1): skipped\n\n"); + } + + return 0; +} +#else + +#define MAX_TESTS 6 + +static const size_t plen_test_data[MAX_TESTS] = +{ 8, 8, 8, 24, 9 }; + +static const unsigned char password_test_data[MAX_TESTS][32] = +{ + "password", + "password", + "password", + "passwordPASSWORDpassword", + "pass\0word", +}; + +static const size_t slen_test_data[MAX_TESTS] = +{ 4, 4, 4, 36, 5 }; + +static const unsigned char salt_test_data[MAX_TESTS][40] = +{ + "salt", + "salt", + "salt", + "saltSALTsaltSALTsaltSALTsaltSALTsalt", + "sa\0lt", +}; + +static const uint32_t it_cnt_test_data[MAX_TESTS] = +{ 1, 2, 4096, 4096, 4096 }; + +static const uint32_t key_len_test_data[MAX_TESTS] = +{ 20, 20, 20, 25, 16 }; + +static const unsigned char result_key_test_data[MAX_TESTS][32] = +{ + { 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, + 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, + 0x2f, 0xe0, 0x37, 0xa6 }, + { 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, + 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, + 0xd8, 0xde, 0x89, 0x57 }, + { 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, + 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, + 0x65, 0xa4, 0x29, 0xc1 }, + { 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, + 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, + 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, + 0x38 }, + { 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, + 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 }, +}; + +int mbedtls_pkcs5_self_test(int verbose) +{ + int ret, i; + unsigned char key[64]; + + for (i = 0; i < MAX_TESTS; i++) { + if (verbose != 0) { + mbedtls_printf(" PBKDF2 (SHA1) #%d: ", i); + } + + ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA1, password_test_data[i], + plen_test_data[i], salt_test_data[i], + slen_test_data[i], it_cnt_test_data[i], + key_len_test_data[i], key); + if (ret != 0 || + memcmp(result_key_test_data[i], key, key_len_test_data[i]) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto exit; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + +exit: + return ret; +} +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA */ + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_PKCS5_C */ diff --git a/r5dev/thirdparty/mbedtls/pkcs7.c b/r5dev/thirdparty/mbedtls/pkcs7.c new file mode 100644 index 00000000..cf05afd2 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pkcs7.c @@ -0,0 +1,785 @@ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "common.h" + +#include "mbedtls/build_info.h" +#if defined(MBEDTLS_PKCS7_C) +#include "mbedtls/pkcs7.h" +#include "mbedtls/x509.h" +#include "mbedtls/asn1.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/x509_crl.h" +#include "mbedtls/oid.h" +#include "mbedtls/error.h" + +#if defined(MBEDTLS_FS_IO) +#include +#include +#endif + +#include "mbedtls/platform.h" +#include "mbedtls/platform_util.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif +#if defined(MBEDTLS_HAVE_TIME_DATE) +#include +#endif + +/** + * Initializes the mbedtls_pkcs7 structure. + */ +void mbedtls_pkcs7_init(mbedtls_pkcs7 *pkcs7) +{ + memset(pkcs7, 0, sizeof(*pkcs7)); +} + +static int pkcs7_get_next_content_len(unsigned char **p, unsigned char *end, + size_t *len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_asn1_get_tag(p, end, len, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_CONTEXT_SPECIFIC); + if (ret != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret); + } else if ((size_t) (end - *p) != *len) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return ret; +} + +/** + * version Version + * Version ::= INTEGER + **/ +static int pkcs7_get_version(unsigned char **p, unsigned char *end, int *ver) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_asn1_get_int(p, end, ver); + if (ret != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_VERSION, ret); + } + + /* If version != 1, return invalid version */ + if (*ver != MBEDTLS_PKCS7_SUPPORTED_VERSION) { + ret = MBEDTLS_ERR_PKCS7_INVALID_VERSION; + } + + return ret; +} + +/** + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content + * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + **/ +static int pkcs7_get_content_info_type(unsigned char **p, unsigned char *end, + unsigned char **seq_end, + mbedtls_pkcs7_buf *pkcs7) +{ + size_t len = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *start = *p; + + ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + *p = start; + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret); + } + *seq_end = *p + len; + ret = mbedtls_asn1_get_tag(p, *seq_end, &len, MBEDTLS_ASN1_OID); + if (ret != 0) { + *p = start; + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret); + } + + pkcs7->tag = MBEDTLS_ASN1_OID; + pkcs7->len = len; + pkcs7->p = *p; + *p += len; + + return ret; +} + +/** + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * + * This is from x509.h + **/ +static int pkcs7_get_digest_algorithm(unsigned char **p, unsigned char *end, + mbedtls_x509_buf *alg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_alg_null(p, end, alg)) != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_ALG, ret); + } + + return ret; +} + +/** + * DigestAlgorithmIdentifiers :: SET of DigestAlgorithmIdentifier + **/ +static int pkcs7_get_digest_algorithm_set(unsigned char **p, + unsigned char *end, + mbedtls_x509_buf *alg) +{ + size_t len = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_SET); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_ALG, ret); + } + + end = *p + len; + + ret = mbedtls_asn1_get_alg_null(p, end, alg); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_ALG, ret); + } + + /** For now, it assumes there is only one digest algorithm specified **/ + if (*p != end) { + return MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE; + } + + return 0; +} + +/** + * certificates :: SET OF ExtendedCertificateOrCertificate, + * ExtendedCertificateOrCertificate ::= CHOICE { + * certificate Certificate -- x509, + * extendedCertificate[0] IMPLICIT ExtendedCertificate } + * Return number of certificates added to the signed data, + * 0 or higher is valid. + * Return negative error code for failure. + **/ +static int pkcs7_get_certificates(unsigned char **p, unsigned char *end, + mbedtls_x509_crt *certs) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len1 = 0; + size_t len2 = 0; + unsigned char *end_set, *end_cert, *start; + + ret = mbedtls_asn1_get_tag(p, end, &len1, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_CONTEXT_SPECIFIC); + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return 0; + } + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret); + } + start = *p; + end_set = *p + len1; + + ret = mbedtls_asn1_get_tag(p, end_set, &len2, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CERT, ret); + } + + end_cert = *p + len2; + + /* + * This is to verify that there is only one signer certificate. It seems it is + * not easy to differentiate between the chain vs different signer's certificate. + * So, we support only the root certificate and the single signer. + * The behaviour would be improved with addition of multiple signer support. + */ + if (end_cert != end_set) { + return MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE; + } + + if ((ret = mbedtls_x509_crt_parse_der(certs, start, len1)) < 0) { + return MBEDTLS_ERR_PKCS7_INVALID_CERT; + } + + *p = end_cert; + + /* + * Since in this version we strictly support single certificate, and reaching + * here implies we have parsed successfully, we return 1. + */ + return 1; +} + +/** + * EncryptedDigest ::= OCTET STRING + **/ +static int pkcs7_get_signature(unsigned char **p, unsigned char *end, + mbedtls_pkcs7_buf *signature) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + if (ret != 0) { + return ret; + } + + signature->tag = MBEDTLS_ASN1_OCTET_STRING; + signature->len = len; + signature->p = *p; + + *p = *p + len; + + return 0; +} + +static void pkcs7_free_signer_info(mbedtls_pkcs7_signer_info *signer) +{ + mbedtls_x509_name *name_cur; + mbedtls_x509_name *name_prv; + + if (signer == NULL) { + return; + } + + name_cur = signer->issuer.next; + while (name_cur != NULL) { + name_prv = name_cur; + name_cur = name_cur->next; + mbedtls_free(name_prv); + } + signer->issuer.next = NULL; +} + +/** + * SignerInfo ::= SEQUENCE { + * version Version; + * issuerAndSerialNumber IssuerAndSerialNumber, + * digestAlgorithm DigestAlgorithmIdentifier, + * authenticatedAttributes + * [0] IMPLICIT Attributes OPTIONAL, + * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, + * encryptedDigest EncryptedDigest, + * unauthenticatedAttributes + * [1] IMPLICIT Attributes OPTIONAL, + * Returns 0 if the signerInfo is valid. + * Return negative error code for failure. + * Structure must not contain vales for authenticatedAttributes + * and unauthenticatedAttributes. + **/ +static int pkcs7_get_signer_info(unsigned char **p, unsigned char *end, + mbedtls_pkcs7_signer_info *signer, + mbedtls_x509_buf *alg) +{ + unsigned char *end_signer, *end_issuer_and_sn; + int asn1_ret = 0, ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + asn1_ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_SEQUENCE); + if (asn1_ret != 0) { + goto out; + } + + end_signer = *p + len; + + ret = pkcs7_get_version(p, end_signer, &signer->version); + if (ret != 0) { + goto out; + } + + asn1_ret = mbedtls_asn1_get_tag(p, end_signer, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (asn1_ret != 0) { + goto out; + } + + end_issuer_and_sn = *p + len; + /* Parsing IssuerAndSerialNumber */ + signer->issuer_raw.p = *p; + + asn1_ret = mbedtls_asn1_get_tag(p, end_issuer_and_sn, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (asn1_ret != 0) { + goto out; + } + + ret = mbedtls_x509_get_name(p, *p + len, &signer->issuer); + if (ret != 0) { + goto out; + } + + signer->issuer_raw.len = *p - signer->issuer_raw.p; + + ret = mbedtls_x509_get_serial(p, end_issuer_and_sn, &signer->serial); + if (ret != 0) { + goto out; + } + + /* ensure no extra or missing bytes */ + if (*p != end_issuer_and_sn) { + ret = MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO; + goto out; + } + + ret = pkcs7_get_digest_algorithm(p, end_signer, &signer->alg_identifier); + if (ret != 0) { + goto out; + } + + /* Check that the digest algorithm used matches the one provided earlier */ + if (signer->alg_identifier.tag != alg->tag || + signer->alg_identifier.len != alg->len || + memcmp(signer->alg_identifier.p, alg->p, alg->len) != 0) { + ret = MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO; + goto out; + } + + /* Assume authenticatedAttributes is nonexistent */ + ret = pkcs7_get_digest_algorithm(p, end_signer, &signer->sig_alg_identifier); + if (ret != 0) { + goto out; + } + + ret = pkcs7_get_signature(p, end_signer, &signer->sig); + if (ret != 0) { + goto out; + } + + /* Do not permit any unauthenticated attributes */ + if (*p != end_signer) { + ret = MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO; + } + +out: + if (asn1_ret != 0 || ret != 0) { + pkcs7_free_signer_info(signer); + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO, + asn1_ret); + } + + return ret; +} + +/** + * SignerInfos ::= SET of SignerInfo + * Return number of signers added to the signed data, + * 0 or higher is valid. + * Return negative error code for failure. + **/ +static int pkcs7_get_signers_info_set(unsigned char **p, unsigned char *end, + mbedtls_pkcs7_signer_info *signers_set, + mbedtls_x509_buf *digest_alg) +{ + unsigned char *end_set; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int count = 0; + size_t len = 0; + + ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_SET); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_SIGNER_INFO, ret); + } + + /* Detect zero signers */ + if (len == 0) { + return 0; + } + + end_set = *p + len; + + ret = pkcs7_get_signer_info(p, end_set, signers_set, digest_alg); + if (ret != 0) { + return ret; + } + count++; + + mbedtls_pkcs7_signer_info *prev = signers_set; + while (*p != end_set) { + mbedtls_pkcs7_signer_info *signer = + mbedtls_calloc(1, sizeof(mbedtls_pkcs7_signer_info)); + if (!signer) { + ret = MBEDTLS_ERR_PKCS7_ALLOC_FAILED; + goto cleanup; + } + + ret = pkcs7_get_signer_info(p, end_set, signer, digest_alg); + if (ret != 0) { + mbedtls_free(signer); + goto cleanup; + } + prev->next = signer; + prev = signer; + count++; + } + + return count; + +cleanup: + pkcs7_free_signer_info(signers_set); + mbedtls_pkcs7_signer_info *signer = signers_set->next; + while (signer != NULL) { + prev = signer; + signer = signer->next; + pkcs7_free_signer_info(prev); + mbedtls_free(prev); + } + signers_set->next = NULL; + return ret; +} + +/** + * SignedData ::= SEQUENCE { + * version Version, + * digestAlgorithms DigestAlgorithmIdentifiers, + * contentInfo ContentInfo, + * certificates + * [0] IMPLICIT ExtendedCertificatesAndCertificates + * OPTIONAL, + * crls + * [0] IMPLICIT CertificateRevocationLists OPTIONAL, + * signerInfos SignerInfos } + */ +static int pkcs7_get_signed_data(unsigned char *buf, size_t buflen, + mbedtls_pkcs7_signed_data *signed_data) +{ + unsigned char *p = buf; + unsigned char *end = buf + buflen; + unsigned char *end_content_info = NULL; + size_t len = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_type_t md_alg; + + ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret); + } + + if (p + len != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* Get version of signed data */ + ret = pkcs7_get_version(&p, end, &signed_data->version); + if (ret != 0) { + return ret; + } + + /* Get digest algorithm */ + ret = pkcs7_get_digest_algorithm_set(&p, end, + &signed_data->digest_alg_identifiers); + if (ret != 0) { + return ret; + } + + ret = mbedtls_oid_get_md_alg(&signed_data->digest_alg_identifiers, &md_alg); + if (ret != 0) { + return MBEDTLS_ERR_PKCS7_INVALID_ALG; + } + + mbedtls_pkcs7_buf content_type; + memset(&content_type, 0, sizeof(content_type)); + ret = pkcs7_get_content_info_type(&p, end, &end_content_info, &content_type); + if (ret != 0) { + return ret; + } + if (MBEDTLS_OID_CMP(MBEDTLS_OID_PKCS7_DATA, &content_type)) { + return MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO; + } + + if (p != end_content_info) { + /* Determine if valid content is present */ + ret = mbedtls_asn1_get_tag(&p, + end_content_info, + &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret); + } + p += len; + if (p != end_content_info) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_CONTENT_INFO, ret); + } + /* Valid content is present - this is not supported */ + return MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE; + } + + /* Look for certificates, there may or may not be any */ + mbedtls_x509_crt_init(&signed_data->certs); + ret = pkcs7_get_certificates(&p, end, &signed_data->certs); + if (ret < 0) { + return ret; + } + + signed_data->no_of_certs = ret; + + /* + * Currently CRLs are not supported. If CRL exist, the parsing will fail + * at next step of getting signers info and return error as invalid + * signer info. + */ + + signed_data->no_of_crls = 0; + + /* Get signers info */ + ret = pkcs7_get_signers_info_set(&p, + end, + &signed_data->signers, + &signed_data->digest_alg_identifiers); + if (ret < 0) { + return ret; + } + + signed_data->no_of_signers = ret; + + /* Don't permit trailing data */ + if (p != end) { + return MBEDTLS_ERR_PKCS7_INVALID_FORMAT; + } + + return 0; +} + +int mbedtls_pkcs7_parse_der(mbedtls_pkcs7 *pkcs7, const unsigned char *buf, + const size_t buflen) +{ + unsigned char *p; + unsigned char *end; + size_t len = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (pkcs7 == NULL) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + + /* make an internal copy of the buffer for parsing */ + pkcs7->raw.p = p = mbedtls_calloc(1, buflen); + if (pkcs7->raw.p == NULL) { + ret = MBEDTLS_ERR_PKCS7_ALLOC_FAILED; + goto out; + } + memcpy(p, buf, buflen); + pkcs7->raw.len = buflen; + end = p + buflen; + + ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED + | MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, ret); + goto out; + } + + if ((size_t) (end - p) != len) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PKCS7_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + goto out; + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID)) != 0) { + if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + goto out; + } + p = pkcs7->raw.p; + len = buflen; + goto try_data; + } + + if (MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_SIGNED_DATA, p, len)) { + /* OID is not MBEDTLS_OID_PKCS7_SIGNED_DATA, which is the only supported feature */ + if (!MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_DATA, p, len) + || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_ENCRYPTED_DATA, p, len) + || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_ENVELOPED_DATA, p, len) + || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_SIGNED_AND_ENVELOPED_DATA, p, len) + || !MBEDTLS_OID_CMP_RAW(MBEDTLS_OID_PKCS7_DIGESTED_DATA, p, len)) { + /* OID is valid according to the spec, but unsupported */ + ret = MBEDTLS_ERR_PKCS7_FEATURE_UNAVAILABLE; + } else { + /* OID is invalid according to the spec */ + ret = MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + goto out; + } + + p += len; + + ret = pkcs7_get_next_content_len(&p, end, &len); + if (ret != 0) { + goto out; + } + + /* ensure no extra/missing data */ + if (p + len != end) { + ret = MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + goto out; + } + +try_data: + ret = pkcs7_get_signed_data(p, len, &pkcs7->signed_data); + if (ret != 0) { + goto out; + } + + ret = MBEDTLS_PKCS7_SIGNED_DATA; + +out: + if (ret < 0) { + mbedtls_pkcs7_free(pkcs7); + } + + return ret; +} + +static int mbedtls_pkcs7_data_or_hash_verify(mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *cert, + const unsigned char *data, + size_t datalen, + const int is_data_hash) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *hash; + mbedtls_pk_context pk_cxt = cert->pk; + const mbedtls_md_info_t *md_info; + mbedtls_md_type_t md_alg; + mbedtls_pkcs7_signer_info *signer; + + if (pkcs7->signed_data.no_of_signers == 0) { + return MBEDTLS_ERR_PKCS7_INVALID_CERT; + } + + if (mbedtls_x509_time_is_past(&cert->valid_to) || + mbedtls_x509_time_is_future(&cert->valid_from)) { + return MBEDTLS_ERR_PKCS7_CERT_DATE_INVALID; + } + + ret = mbedtls_oid_get_md_alg(&pkcs7->signed_data.digest_alg_identifiers, &md_alg); + if (ret != 0) { + return ret; + } + + md_info = mbedtls_md_info_from_type(md_alg); + if (md_info == NULL) { + return MBEDTLS_ERR_PKCS7_VERIFY_FAIL; + } + + hash = mbedtls_calloc(mbedtls_md_get_size(md_info), 1); + if (hash == NULL) { + return MBEDTLS_ERR_PKCS7_ALLOC_FAILED; + } + + /* BEGIN must free hash before jumping out */ + if (is_data_hash) { + if (datalen != mbedtls_md_get_size(md_info)) { + ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL; + } else { + memcpy(hash, data, datalen); + } + } else { + ret = mbedtls_md(md_info, data, datalen, hash); + } + if (ret != 0) { + mbedtls_free(hash); + return MBEDTLS_ERR_PKCS7_VERIFY_FAIL; + } + + /* assume failure */ + ret = MBEDTLS_ERR_PKCS7_VERIFY_FAIL; + + /* + * Potential TODOs + * Currently we iterate over all signers and return success if any of them + * verify. + * + * However, we could make this better by checking against the certificate's + * identification and SignerIdentifier fields first. That would also allow + * us to distinguish between 'no signature for key' and 'signature for key + * failed to validate'. + */ + for (signer = &pkcs7->signed_data.signers; signer; signer = signer->next) { + ret = mbedtls_pk_verify(&pk_cxt, md_alg, hash, + mbedtls_md_get_size(md_info), + signer->sig.p, signer->sig.len); + + if (ret == 0) { + break; + } + } + + mbedtls_free(hash); + /* END must free hash before jumping out */ + return ret; +} + +int mbedtls_pkcs7_signed_data_verify(mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *cert, + const unsigned char *data, + size_t datalen) +{ + if (data == NULL) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, data, datalen, 0); +} + +int mbedtls_pkcs7_signed_hash_verify(mbedtls_pkcs7 *pkcs7, + const mbedtls_x509_crt *cert, + const unsigned char *hash, + size_t hashlen) +{ + if (hash == NULL) { + return MBEDTLS_ERR_PKCS7_BAD_INPUT_DATA; + } + return mbedtls_pkcs7_data_or_hash_verify(pkcs7, cert, hash, hashlen, 1); +} + +/* + * Unallocate all pkcs7 data + */ +void mbedtls_pkcs7_free(mbedtls_pkcs7 *pkcs7) +{ + mbedtls_pkcs7_signer_info *signer_cur; + mbedtls_pkcs7_signer_info *signer_prev; + + if (pkcs7 == NULL || pkcs7->raw.p == NULL) { + return; + } + + mbedtls_free(pkcs7->raw.p); + + mbedtls_x509_crt_free(&pkcs7->signed_data.certs); + mbedtls_x509_crl_free(&pkcs7->signed_data.crl); + + signer_cur = pkcs7->signed_data.signers.next; + pkcs7_free_signer_info(&pkcs7->signed_data.signers); + while (signer_cur != NULL) { + signer_prev = signer_cur; + signer_cur = signer_prev->next; + pkcs7_free_signer_info(signer_prev); + mbedtls_free(signer_prev); + } + + pkcs7->raw.p = NULL; +} + +#endif diff --git a/r5dev/thirdparty/mbedtls/pkparse.c b/r5dev/thirdparty/mbedtls/pkparse.c new file mode 100644 index 00000000..ccca692b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pkparse.c @@ -0,0 +1,1528 @@ +/* + * Public Key layer for parsing key files and structures + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PK_PARSE_C) + +#include "mbedtls/pk.h" +#include "mbedtls/asn1.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif +#if defined(MBEDTLS_ECDSA_C) +#include "mbedtls/ecdsa.h" +#endif +#if defined(MBEDTLS_PEM_PARSE_C) +#include "mbedtls/pem.h" +#endif +#if defined(MBEDTLS_PKCS5_C) +#include "mbedtls/pkcs5.h" +#endif +#if defined(MBEDTLS_PKCS12_C) +#include "mbedtls/pkcs12.h" +#endif + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_FS_IO) +/* + * Load all data from a file into a given buffer. + * + * The file is expected to contain either PEM or DER encoded data. + * A terminating null byte is always appended. It is included in the announced + * length only if the data looks like it is PEM encoded. + */ +int mbedtls_pk_load_file(const char *path, unsigned char **buf, size_t *n) +{ + FILE *f; + long size; + + if ((f = fopen(path, "rb")) == NULL) { + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(f, NULL); + + fseek(f, 0, SEEK_END); + if ((size = ftell(f)) == -1) { + fclose(f); + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + } + fseek(f, 0, SEEK_SET); + + *n = (size_t) size; + + if (*n + 1 == 0 || + (*buf = mbedtls_calloc(1, *n + 1)) == NULL) { + fclose(f); + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } + + if (fread(*buf, 1, *n, f) != *n) { + fclose(f); + + mbedtls_platform_zeroize(*buf, *n); + mbedtls_free(*buf); + + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + } + + fclose(f); + + (*buf)[*n] = '\0'; + + if (strstr((const char *) *buf, "-----BEGIN ") != NULL) { + ++*n; + } + + return 0; +} + +/* + * Load and parse a private key + */ +int mbedtls_pk_parse_keyfile(mbedtls_pk_context *ctx, + const char *path, const char *pwd, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + unsigned char *buf; + + if ((ret = mbedtls_pk_load_file(path, &buf, &n)) != 0) { + return ret; + } + + if (pwd == NULL) { + ret = mbedtls_pk_parse_key(ctx, buf, n, NULL, 0, f_rng, p_rng); + } else { + ret = mbedtls_pk_parse_key(ctx, buf, n, + (const unsigned char *) pwd, strlen(pwd), f_rng, p_rng); + } + + mbedtls_platform_zeroize(buf, n); + mbedtls_free(buf); + + return ret; +} + +/* + * Load and parse a public key + */ +int mbedtls_pk_parse_public_keyfile(mbedtls_pk_context *ctx, const char *path) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + unsigned char *buf; + + if ((ret = mbedtls_pk_load_file(path, &buf, &n)) != 0) { + return ret; + } + + ret = mbedtls_pk_parse_public_key(ctx, buf, n); + + mbedtls_platform_zeroize(buf, n); + mbedtls_free(buf); + + return ret; +} +#endif /* MBEDTLS_FS_IO */ + +#if defined(MBEDTLS_ECP_C) +/* Minimally parse an ECParameters buffer to and mbedtls_asn1_buf + * + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * specifiedCurve SpecifiedECDomain -- = SEQUENCE { ... } + * -- implicitCurve NULL + * } + */ +static int pk_get_ecparams(unsigned char **p, const unsigned char *end, + mbedtls_asn1_buf *params) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (end - *p < 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + /* Tag may be either OID or SEQUENCE */ + params->tag = **p; + if (params->tag != MBEDTLS_ASN1_OID +#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) + && params->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) +#endif + ) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + if ((ret = mbedtls_asn1_get_tag(p, end, ¶ms->len, params->tag)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + params->p = *p; + *p += params->len; + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) +/* + * Parse a SpecifiedECDomain (SEC 1 C.2) and (mostly) fill the group with it. + * WARNING: the resulting group should only be used with + * pk_group_id_from_specified(), since its base point may not be set correctly + * if it was encoded compressed. + * + * SpecifiedECDomain ::= SEQUENCE { + * version SpecifiedECDomainVersion(ecdpVer1 | ecdpVer2 | ecdpVer3, ...), + * fieldID FieldID {{FieldTypes}}, + * curve Curve, + * base ECPoint, + * order INTEGER, + * cofactor INTEGER OPTIONAL, + * hash HashAlgorithm OPTIONAL, + * ... + * } + * + * We only support prime-field as field type, and ignore hash and cofactor. + */ +static int pk_group_from_specified(const mbedtls_asn1_buf *params, mbedtls_ecp_group *grp) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = params->p; + const unsigned char * const end = params->p + params->len; + const unsigned char *end_field, *end_curve; + size_t len; + int ver; + + /* SpecifiedECDomainVersion ::= INTEGER { 1, 2, 3 } */ + if ((ret = mbedtls_asn1_get_int(&p, end, &ver)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if (ver < 1 || ver > 3) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + + /* + * FieldID { FIELD-ID:IOSet } ::= SEQUENCE { -- Finite field + * fieldType FIELD-ID.&id({IOSet}), + * parameters FIELD-ID.&Type({IOSet}{@fieldType}) + * } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + end_field = p + len; + + /* + * FIELD-ID ::= TYPE-IDENTIFIER + * FieldTypes FIELD-ID ::= { + * { Prime-p IDENTIFIED BY prime-field } | + * { Characteristic-two IDENTIFIED BY characteristic-two-field } + * } + * prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end_field, &len, MBEDTLS_ASN1_OID)) != 0) { + return ret; + } + + if (len != MBEDTLS_OID_SIZE(MBEDTLS_OID_ANSI_X9_62_PRIME_FIELD) || + memcmp(p, MBEDTLS_OID_ANSI_X9_62_PRIME_FIELD, len) != 0) { + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + } + + p += len; + + /* Prime-p ::= INTEGER -- Field of size p. */ + if ((ret = mbedtls_asn1_get_mpi(&p, end_field, &grp->P)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + grp->pbits = mbedtls_mpi_bitlen(&grp->P); + + if (p != end_field) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* + * Curve ::= SEQUENCE { + * a FieldElement, + * b FieldElement, + * seed BIT STRING OPTIONAL + * -- Shall be present if used in SpecifiedECDomain + * -- with version equal to ecdpVer2 or ecdpVer3 + * } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + end_curve = p + len; + + /* + * FieldElement ::= OCTET STRING + * containing an integer in the case of a prime field + */ + if ((ret = mbedtls_asn1_get_tag(&p, end_curve, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0 || + (ret = mbedtls_mpi_read_binary(&grp->A, p, len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + p += len; + + if ((ret = mbedtls_asn1_get_tag(&p, end_curve, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0 || + (ret = mbedtls_mpi_read_binary(&grp->B, p, len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + p += len; + + /* Ignore seed BIT STRING OPTIONAL */ + if ((ret = mbedtls_asn1_get_tag(&p, end_curve, &len, MBEDTLS_ASN1_BIT_STRING)) == 0) { + p += len; + } + + if (p != end_curve) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* + * ECPoint ::= OCTET STRING + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if ((ret = mbedtls_ecp_point_read_binary(grp, &grp->G, + (const unsigned char *) p, len)) != 0) { + /* + * If we can't read the point because it's compressed, cheat by + * reading only the X coordinate and the parity bit of Y. + */ + if (ret != MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE || + (p[0] != 0x02 && p[0] != 0x03) || + len != mbedtls_mpi_size(&grp->P) + 1 || + mbedtls_mpi_read_binary(&grp->G.X, p + 1, len - 1) != 0 || + mbedtls_mpi_lset(&grp->G.Y, p[0] - 2) != 0 || + mbedtls_mpi_lset(&grp->G.Z, 1) != 0) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + } + + p += len; + + /* + * order INTEGER + */ + if ((ret = mbedtls_asn1_get_mpi(&p, end, &grp->N)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + grp->nbits = mbedtls_mpi_bitlen(&grp->N); + + /* + * Allow optional elements by purposefully not enforcing p == end here. + */ + + return 0; +} + +/* + * Find the group id associated with an (almost filled) group as generated by + * pk_group_from_specified(), or return an error if unknown. + */ +static int pk_group_id_from_group(const mbedtls_ecp_group *grp, mbedtls_ecp_group_id *grp_id) +{ + int ret = 0; + mbedtls_ecp_group ref; + const mbedtls_ecp_group_id *id; + + mbedtls_ecp_group_init(&ref); + + for (id = mbedtls_ecp_grp_id_list(); *id != MBEDTLS_ECP_DP_NONE; id++) { + /* Load the group associated to that id */ + mbedtls_ecp_group_free(&ref); + MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&ref, *id)); + + /* Compare to the group we were given, starting with easy tests */ + if (grp->pbits == ref.pbits && grp->nbits == ref.nbits && + mbedtls_mpi_cmp_mpi(&grp->P, &ref.P) == 0 && + mbedtls_mpi_cmp_mpi(&grp->A, &ref.A) == 0 && + mbedtls_mpi_cmp_mpi(&grp->B, &ref.B) == 0 && + mbedtls_mpi_cmp_mpi(&grp->N, &ref.N) == 0 && + mbedtls_mpi_cmp_mpi(&grp->G.X, &ref.G.X) == 0 && + mbedtls_mpi_cmp_mpi(&grp->G.Z, &ref.G.Z) == 0 && + /* For Y we may only know the parity bit, so compare only that */ + mbedtls_mpi_get_bit(&grp->G.Y, 0) == mbedtls_mpi_get_bit(&ref.G.Y, 0)) { + break; + } + + } + +cleanup: + mbedtls_ecp_group_free(&ref); + + *grp_id = *id; + + if (ret == 0 && *id == MBEDTLS_ECP_DP_NONE) { + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + } + + return ret; +} + +/* + * Parse a SpecifiedECDomain (SEC 1 C.2) and find the associated group ID + */ +static int pk_group_id_from_specified(const mbedtls_asn1_buf *params, + mbedtls_ecp_group_id *grp_id) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_group grp; + + mbedtls_ecp_group_init(&grp); + + if ((ret = pk_group_from_specified(params, &grp)) != 0) { + goto cleanup; + } + + ret = pk_group_id_from_group(&grp, grp_id); + +cleanup: + /* The API respecting lifecycle for mbedtls_ecp_group struct is + * _init(), _load() and _free(). In pk_group_id_from_specified() the + * temporary grp breaks that flow and it's members are populated + * by pk_group_id_from_group(). As such mbedtls_ecp_group_free() + * which is assuming a group populated by _setup() may not clean-up + * properly -> Manually free it's members. + */ + mbedtls_mpi_free(&grp.N); + mbedtls_mpi_free(&grp.P); + mbedtls_mpi_free(&grp.A); + mbedtls_mpi_free(&grp.B); + mbedtls_ecp_point_free(&grp.G); + + return ret; +} +#endif /* MBEDTLS_PK_PARSE_EC_EXTENDED */ + +/* + * Use EC parameters to initialise an EC group + * + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * specifiedCurve SpecifiedECDomain -- = SEQUENCE { ... } + * -- implicitCurve NULL + */ +static int pk_use_ecparams(const mbedtls_asn1_buf *params, mbedtls_ecp_group *grp) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_group_id grp_id; + + if (params->tag == MBEDTLS_ASN1_OID) { + if (mbedtls_oid_get_ec_grp(params, &grp_id) != 0) { + return MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE; + } + } else { +#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) + if ((ret = pk_group_id_from_specified(params, &grp_id)) != 0) { + return ret; + } +#else + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; +#endif + } + + /* + * grp may already be initialized; if so, make sure IDs match + */ + if (grp->id != MBEDTLS_ECP_DP_NONE && grp->id != grp_id) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + + if ((ret = mbedtls_ecp_group_load(grp, grp_id)) != 0) { + return ret; + } + + return 0; +} + +/* + * EC public key is an EC point + * + * The caller is responsible for clearing the structure upon failure if + * desired. Take care to pass along the possible ECP_FEATURE_UNAVAILABLE + * return code of mbedtls_ecp_point_read_binary() and leave p in a usable state. + */ +static int pk_get_ecpubkey(unsigned char **p, const unsigned char *end, + mbedtls_ecp_keypair *key) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_ecp_point_read_binary(&key->grp, &key->Q, + (const unsigned char *) *p, end - *p)) == 0) { + ret = mbedtls_ecp_check_pubkey(&key->grp, &key->Q); + } + + /* + * We know mbedtls_ecp_point_read_binary consumed all bytes or failed + */ + *p = (unsigned char *) end; + + return ret; +} +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_RSA_C) +/* + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ +static int pk_get_rsapubkey(unsigned char **p, + const unsigned char *end, + mbedtls_rsa_context *rsa) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret); + } + + if (*p + len != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* Import N */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret); + } + + if ((ret = mbedtls_rsa_import_raw(rsa, *p, len, NULL, 0, NULL, 0, + NULL, 0, NULL, 0)) != 0) { + return MBEDTLS_ERR_PK_INVALID_PUBKEY; + } + + *p += len; + + /* Import E */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_INTEGER)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret); + } + + if ((ret = mbedtls_rsa_import_raw(rsa, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, *p, len)) != 0) { + return MBEDTLS_ERR_PK_INVALID_PUBKEY; + } + + *p += len; + + if (mbedtls_rsa_complete(rsa) != 0 || + mbedtls_rsa_check_pubkey(rsa) != 0) { + return MBEDTLS_ERR_PK_INVALID_PUBKEY; + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} +#endif /* MBEDTLS_RSA_C */ + +/* Get a PK algorithm identifier + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + */ +static int pk_get_pk_alg(unsigned char **p, + const unsigned char *end, + mbedtls_pk_type_t *pk_alg, mbedtls_asn1_buf *params) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_asn1_buf alg_oid; + + memset(params, 0, sizeof(mbedtls_asn1_buf)); + + if ((ret = mbedtls_asn1_get_alg(p, end, &alg_oid, params)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_ALG, ret); + } + + if (mbedtls_oid_get_pk_alg(&alg_oid, pk_alg) != 0) { + return MBEDTLS_ERR_PK_UNKNOWN_PK_ALG; + } + + /* + * No parameters with RSA (only for EC) + */ + if (*pk_alg == MBEDTLS_PK_RSA && + ((params->tag != MBEDTLS_ASN1_NULL && params->tag != 0) || + params->len != 0)) { + return MBEDTLS_ERR_PK_INVALID_ALG; + } + + return 0; +} + +/* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ +int mbedtls_pk_parse_subpubkey(unsigned char **p, const unsigned char *end, + mbedtls_pk_context *pk) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + mbedtls_asn1_buf alg_params; + mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE; + const mbedtls_pk_info_t *pk_info; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + end = *p + len; + + if ((ret = pk_get_pk_alg(p, end, &pk_alg, &alg_params)) != 0) { + return ret; + } + + if ((ret = mbedtls_asn1_get_bitstring_null(p, end, &len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, ret); + } + + if (*p + len != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if ((pk_info = mbedtls_pk_info_from_type(pk_alg)) == NULL) { + return MBEDTLS_ERR_PK_UNKNOWN_PK_ALG; + } + + if ((ret = mbedtls_pk_setup(pk, pk_info)) != 0) { + return ret; + } + +#if defined(MBEDTLS_RSA_C) + if (pk_alg == MBEDTLS_PK_RSA) { + ret = pk_get_rsapubkey(p, end, mbedtls_pk_rsa(*pk)); + } else +#endif /* MBEDTLS_RSA_C */ +#if defined(MBEDTLS_ECP_C) + if (pk_alg == MBEDTLS_PK_ECKEY_DH || pk_alg == MBEDTLS_PK_ECKEY) { + ret = pk_use_ecparams(&alg_params, &mbedtls_pk_ec(*pk)->grp); + if (ret == 0) { + ret = pk_get_ecpubkey(p, end, mbedtls_pk_ec(*pk)); + } + } else +#endif /* MBEDTLS_ECP_C */ + ret = MBEDTLS_ERR_PK_UNKNOWN_PK_ALG; + + if (ret == 0 && *p != end) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if (ret != 0) { + mbedtls_pk_free(pk); + } + + return ret; +} + +#if defined(MBEDTLS_RSA_C) +/* + * Wrapper around mbedtls_asn1_get_mpi() that rejects zero. + * + * The value zero is: + * - never a valid value for an RSA parameter + * - interpreted as "omitted, please reconstruct" by mbedtls_rsa_complete(). + * + * Since values can't be omitted in PKCS#1, passing a zero value to + * rsa_complete() would be incorrect, so reject zero values early. + */ +static int asn1_get_nonzero_mpi(unsigned char **p, + const unsigned char *end, + mbedtls_mpi *X) +{ + int ret; + + ret = mbedtls_asn1_get_mpi(p, end, X); + if (ret != 0) { + return ret; + } + + if (mbedtls_mpi_cmp_int(X, 0) == 0) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + + return 0; +} + +/* + * Parse a PKCS#1 encoded private RSA key + */ +static int pk_parse_key_pkcs1_der(mbedtls_rsa_context *rsa, + const unsigned char *key, + size_t keylen) +{ + int ret, version; + size_t len; + unsigned char *p, *end; + + mbedtls_mpi T; + mbedtls_mpi_init(&T); + + p = (unsigned char *) key; + end = p + keylen; + + /* + * This function parses the RSAPrivateKey (PKCS#1) + * + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + end = p + len; + + if ((ret = mbedtls_asn1_get_int(&p, end, &version)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if (version != 0) { + return MBEDTLS_ERR_PK_KEY_INVALID_VERSION; + } + + /* Import N */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_rsa_import(rsa, &T, NULL, NULL, + NULL, NULL)) != 0) { + goto cleanup; + } + + /* Import E */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_rsa_import(rsa, NULL, NULL, NULL, + NULL, &T)) != 0) { + goto cleanup; + } + + /* Import D */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_rsa_import(rsa, NULL, NULL, NULL, + &T, NULL)) != 0) { + goto cleanup; + } + + /* Import P */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_rsa_import(rsa, NULL, &T, NULL, + NULL, NULL)) != 0) { + goto cleanup; + } + + /* Import Q */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_rsa_import(rsa, NULL, NULL, &T, + NULL, NULL)) != 0) { + goto cleanup; + } + +#if !defined(MBEDTLS_RSA_NO_CRT) && !defined(MBEDTLS_RSA_ALT) + /* + * The RSA CRT parameters DP, DQ and QP are nominally redundant, in + * that they can be easily recomputed from D, P and Q. However by + * parsing them from the PKCS1 structure it is possible to avoid + * recalculating them which both reduces the overhead of loading + * RSA private keys into memory and also avoids side channels which + * can arise when computing those values, since all of D, P, and Q + * are secret. See https://eprint.iacr.org/2020/055 for a + * description of one such attack. + */ + + /* Import DP */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_mpi_copy(&rsa->DP, &T)) != 0) { + goto cleanup; + } + + /* Import DQ */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_mpi_copy(&rsa->DQ, &T)) != 0) { + goto cleanup; + } + + /* Import QP */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = mbedtls_mpi_copy(&rsa->QP, &T)) != 0) { + goto cleanup; + } + +#else + /* Verify existence of the CRT params */ + if ((ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0 || + (ret = asn1_get_nonzero_mpi(&p, end, &T)) != 0) { + goto cleanup; + } +#endif + + /* rsa_complete() doesn't complete anything with the default + * implementation but is still called: + * - for the benefit of alternative implementation that may want to + * pre-compute stuff beyond what's provided (eg Montgomery factors) + * - as is also sanity-checks the key + * + * Furthermore, we also check the public part for consistency with + * mbedtls_pk_parse_pubkey(), as it includes size minima for example. + */ + if ((ret = mbedtls_rsa_complete(rsa)) != 0 || + (ret = mbedtls_rsa_check_pubkey(rsa)) != 0) { + goto cleanup; + } + + if (p != end) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + +cleanup: + + mbedtls_mpi_free(&T); + + if (ret != 0) { + /* Wrap error code if it's coming from a lower level */ + if ((ret & 0xff80) == 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } else { + ret = MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + + mbedtls_rsa_free(rsa); + } + + return ret; +} +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) +/* + * Parse a SEC1 encoded private EC key + */ +static int pk_parse_key_sec1_der(mbedtls_ecp_keypair *eck, + const unsigned char *key, size_t keylen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int version, pubkey_done; + size_t len; + mbedtls_asn1_buf params = { 0, 0, NULL }; + unsigned char *p = (unsigned char *) key; + unsigned char *end = p + keylen; + unsigned char *end2; + + /* + * RFC 5915, or SEC1 Appendix C.4 + * + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL + * } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + end = p + len; + + if ((ret = mbedtls_asn1_get_int(&p, end, &version)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if (version != 1) { + return MBEDTLS_ERR_PK_KEY_INVALID_VERSION; + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if ((ret = mbedtls_mpi_read_binary(&eck->d, p, len)) != 0) { + mbedtls_ecp_keypair_free(eck); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + p += len; + + pubkey_done = 0; + if (p != end) { + /* + * Is 'parameters' present? + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + 0)) == 0) { + if ((ret = pk_get_ecparams(&p, p + len, ¶ms)) != 0 || + (ret = pk_use_ecparams(¶ms, &eck->grp)) != 0) { + mbedtls_ecp_keypair_free(eck); + return ret; + } + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + mbedtls_ecp_keypair_free(eck); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + } + + if (p != end) { + /* + * Is 'publickey' present? If not, or if we can't read it (eg because it + * is compressed), create it from the private key. + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + 1)) == 0) { + end2 = p + len; + + if ((ret = mbedtls_asn1_get_bitstring_null(&p, end2, &len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if (p + len != end2) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if ((ret = pk_get_ecpubkey(&p, end2, eck)) == 0) { + pubkey_done = 1; + } else { + /* + * The only acceptable failure mode of pk_get_ecpubkey() above + * is if the point format is not recognized. + */ + if (ret != MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + } + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + mbedtls_ecp_keypair_free(eck); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + } + + if (!pubkey_done && + (ret = mbedtls_ecp_mul(&eck->grp, &eck->Q, &eck->d, &eck->grp.G, + f_rng, p_rng)) != 0) { + mbedtls_ecp_keypair_free(eck); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if ((ret = mbedtls_ecp_check_privkey(&eck->grp, &eck->d)) != 0) { + mbedtls_ecp_keypair_free(eck); + return ret; + } + + return 0; +} +#endif /* MBEDTLS_ECP_C */ + +/* + * Parse an unencrypted PKCS#8 encoded private key + * + * Notes: + * + * - This function does not own the key buffer. It is the + * responsibility of the caller to take care of zeroizing + * and freeing it after use. + * + * - The function is responsible for freeing the provided + * PK context on failure. + * + */ +static int pk_parse_key_pkcs8_unencrypted_der( + mbedtls_pk_context *pk, + const unsigned char *key, size_t keylen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret, version; + size_t len; + mbedtls_asn1_buf params; + unsigned char *p = (unsigned char *) key; + unsigned char *end = p + keylen; + mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE; + const mbedtls_pk_info_t *pk_info; + +#if !defined(MBEDTLS_ECP_C) + (void) f_rng; + (void) p_rng; +#endif + + /* + * This function parses the PrivateKeyInfo object (PKCS#8 v1.2 = RFC 5208) + * + * PrivateKeyInfo ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] IMPLICIT Attributes OPTIONAL } + * + * Version ::= INTEGER + * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + * PrivateKey ::= OCTET STRING + * + * The PrivateKey OCTET STRING is a SEC1 ECPrivateKey + */ + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + end = p + len; + + if ((ret = mbedtls_asn1_get_int(&p, end, &version)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if (version != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_VERSION, ret); + } + + if ((ret = pk_get_pk_alg(&p, end, &pk_alg, ¶ms)) != 0) { + return ret; + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if (len < 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + if ((pk_info = mbedtls_pk_info_from_type(pk_alg)) == NULL) { + return MBEDTLS_ERR_PK_UNKNOWN_PK_ALG; + } + + if ((ret = mbedtls_pk_setup(pk, pk_info)) != 0) { + return ret; + } + +#if defined(MBEDTLS_RSA_C) + if (pk_alg == MBEDTLS_PK_RSA) { + if ((ret = pk_parse_key_pkcs1_der(mbedtls_pk_rsa(*pk), p, len)) != 0) { + mbedtls_pk_free(pk); + return ret; + } + } else +#endif /* MBEDTLS_RSA_C */ +#if defined(MBEDTLS_ECP_C) + if (pk_alg == MBEDTLS_PK_ECKEY || pk_alg == MBEDTLS_PK_ECKEY_DH) { + if ((ret = pk_use_ecparams(¶ms, &mbedtls_pk_ec(*pk)->grp)) != 0 || + (ret = pk_parse_key_sec1_der(mbedtls_pk_ec(*pk), p, len, f_rng, p_rng)) != 0) { + mbedtls_pk_free(pk); + return ret; + } + } else +#endif /* MBEDTLS_ECP_C */ + return MBEDTLS_ERR_PK_UNKNOWN_PK_ALG; + + return 0; +} + +/* + * Parse an encrypted PKCS#8 encoded private key + * + * To save space, the decryption happens in-place on the given key buffer. + * Also, while this function may modify the keybuffer, it doesn't own it, + * and instead it is the responsibility of the caller to zeroize and properly + * free it after use. + * + */ +#if defined(MBEDTLS_PKCS12_C) || defined(MBEDTLS_PKCS5_C) +static int pk_parse_key_pkcs8_encrypted_der( + mbedtls_pk_context *pk, + unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret, decrypted = 0; + size_t len; + unsigned char *buf; + unsigned char *p, *end; + mbedtls_asn1_buf pbe_alg_oid, pbe_params; +#if defined(MBEDTLS_PKCS12_C) + mbedtls_cipher_type_t cipher_alg; + mbedtls_md_type_t md_alg; +#endif + + p = key; + end = p + keylen; + + if (pwdlen == 0) { + return MBEDTLS_ERR_PK_PASSWORD_REQUIRED; + } + + /* + * This function parses the EncryptedPrivateKeyInfo object (PKCS#8) + * + * EncryptedPrivateKeyInfo ::= SEQUENCE { + * encryptionAlgorithm EncryptionAlgorithmIdentifier, + * encryptedData EncryptedData + * } + * + * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * + * EncryptedData ::= OCTET STRING + * + * The EncryptedData OCTET STRING is a PKCS#8 PrivateKeyInfo + * + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + end = p + len; + + if ((ret = mbedtls_asn1_get_alg(&p, end, &pbe_alg_oid, &pbe_params)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_KEY_INVALID_FORMAT, ret); + } + + buf = p; + + /* + * Decrypt EncryptedData with appropriate PBE + */ +#if defined(MBEDTLS_PKCS12_C) + if (mbedtls_oid_get_pkcs12_pbe_alg(&pbe_alg_oid, &md_alg, &cipher_alg) == 0) { + if ((ret = mbedtls_pkcs12_pbe(&pbe_params, MBEDTLS_PKCS12_PBE_DECRYPT, + cipher_alg, md_alg, + pwd, pwdlen, p, len, buf)) != 0) { + if (ret == MBEDTLS_ERR_PKCS12_PASSWORD_MISMATCH) { + return MBEDTLS_ERR_PK_PASSWORD_MISMATCH; + } + + return ret; + } + + decrypted = 1; + } else +#endif /* MBEDTLS_PKCS12_C */ +#if defined(MBEDTLS_PKCS5_C) + if (MBEDTLS_OID_CMP(MBEDTLS_OID_PKCS5_PBES2, &pbe_alg_oid) == 0) { + if ((ret = mbedtls_pkcs5_pbes2(&pbe_params, MBEDTLS_PKCS5_DECRYPT, pwd, pwdlen, + p, len, buf)) != 0) { + if (ret == MBEDTLS_ERR_PKCS5_PASSWORD_MISMATCH) { + return MBEDTLS_ERR_PK_PASSWORD_MISMATCH; + } + + return ret; + } + + decrypted = 1; + } else +#endif /* MBEDTLS_PKCS5_C */ + { + ((void) pwd); + } + + if (decrypted == 0) { + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + } + + return pk_parse_key_pkcs8_unencrypted_der(pk, buf, len, f_rng, p_rng); +} +#endif /* MBEDTLS_PKCS12_C || MBEDTLS_PKCS5_C */ + +/* + * Parse a private key + */ +int mbedtls_pk_parse_key(mbedtls_pk_context *pk, + const unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_pk_info_t *pk_info; +#if defined(MBEDTLS_PEM_PARSE_C) + size_t len; + mbedtls_pem_context pem; +#endif + + if (keylen == 0) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + +#if defined(MBEDTLS_PEM_PARSE_C) + mbedtls_pem_init(&pem); + +#if defined(MBEDTLS_RSA_C) + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (key[keylen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN RSA PRIVATE KEY-----", + "-----END RSA PRIVATE KEY-----", + key, pwd, pwdlen, &len); + } + + if (ret == 0) { + pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA); + if ((ret = mbedtls_pk_setup(pk, pk_info)) != 0 || + (ret = pk_parse_key_pkcs1_der(mbedtls_pk_rsa(*pk), + pem.buf, pem.buflen)) != 0) { + mbedtls_pk_free(pk); + } + + mbedtls_pem_free(&pem); + return ret; + } else if (ret == MBEDTLS_ERR_PEM_PASSWORD_MISMATCH) { + return MBEDTLS_ERR_PK_PASSWORD_MISMATCH; + } else if (ret == MBEDTLS_ERR_PEM_PASSWORD_REQUIRED) { + return MBEDTLS_ERR_PK_PASSWORD_REQUIRED; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + return ret; + } +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (key[keylen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN EC PRIVATE KEY-----", + "-----END EC PRIVATE KEY-----", + key, pwd, pwdlen, &len); + } + if (ret == 0) { + pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); + + if ((ret = mbedtls_pk_setup(pk, pk_info)) != 0 || + (ret = pk_parse_key_sec1_der(mbedtls_pk_ec(*pk), + pem.buf, pem.buflen, + f_rng, p_rng)) != 0) { + mbedtls_pk_free(pk); + } + + mbedtls_pem_free(&pem); + return ret; + } else if (ret == MBEDTLS_ERR_PEM_PASSWORD_MISMATCH) { + return MBEDTLS_ERR_PK_PASSWORD_MISMATCH; + } else if (ret == MBEDTLS_ERR_PEM_PASSWORD_REQUIRED) { + return MBEDTLS_ERR_PK_PASSWORD_REQUIRED; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + return ret; + } +#endif /* MBEDTLS_ECP_C */ + + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (key[keylen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN PRIVATE KEY-----", + "-----END PRIVATE KEY-----", + key, NULL, 0, &len); + } + if (ret == 0) { + if ((ret = pk_parse_key_pkcs8_unencrypted_der(pk, + pem.buf, pem.buflen, f_rng, p_rng)) != 0) { + mbedtls_pk_free(pk); + } + + mbedtls_pem_free(&pem); + return ret; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + return ret; + } + +#if defined(MBEDTLS_PKCS12_C) || defined(MBEDTLS_PKCS5_C) + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (key[keylen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN ENCRYPTED PRIVATE KEY-----", + "-----END ENCRYPTED PRIVATE KEY-----", + key, NULL, 0, &len); + } + if (ret == 0) { + if ((ret = pk_parse_key_pkcs8_encrypted_der(pk, pem.buf, pem.buflen, + pwd, pwdlen, f_rng, p_rng)) != 0) { + mbedtls_pk_free(pk); + } + + mbedtls_pem_free(&pem); + return ret; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + return ret; + } +#endif /* MBEDTLS_PKCS12_C || MBEDTLS_PKCS5_C */ +#else + ((void) pwd); + ((void) pwdlen); +#endif /* MBEDTLS_PEM_PARSE_C */ + + /* + * At this point we only know it's not a PEM formatted key. Could be any + * of the known DER encoded private key formats + * + * We try the different DER format parsers to see if one passes without + * error + */ +#if defined(MBEDTLS_PKCS12_C) || defined(MBEDTLS_PKCS5_C) + if (pwdlen != 0) { + unsigned char *key_copy; + + if ((key_copy = mbedtls_calloc(1, keylen)) == NULL) { + return MBEDTLS_ERR_PK_ALLOC_FAILED; + } + + memcpy(key_copy, key, keylen); + + ret = pk_parse_key_pkcs8_encrypted_der(pk, key_copy, keylen, + pwd, pwdlen, f_rng, p_rng); + + mbedtls_platform_zeroize(key_copy, keylen); + mbedtls_free(key_copy); + } + + if (ret == 0) { + return 0; + } + + mbedtls_pk_free(pk); + mbedtls_pk_init(pk); + + if (ret == MBEDTLS_ERR_PK_PASSWORD_MISMATCH) { + return ret; + } +#endif /* MBEDTLS_PKCS12_C || MBEDTLS_PKCS5_C */ + + ret = pk_parse_key_pkcs8_unencrypted_der(pk, key, keylen, f_rng, p_rng); + if (ret == 0) { + return 0; + } + + mbedtls_pk_free(pk); + mbedtls_pk_init(pk); + +#if defined(MBEDTLS_RSA_C) + + pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA); + if (mbedtls_pk_setup(pk, pk_info) == 0 && + pk_parse_key_pkcs1_der(mbedtls_pk_rsa(*pk), key, keylen) == 0) { + return 0; + } + + mbedtls_pk_free(pk); + mbedtls_pk_init(pk); +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) + pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); + if (mbedtls_pk_setup(pk, pk_info) == 0 && + pk_parse_key_sec1_der(mbedtls_pk_ec(*pk), + key, keylen, f_rng, p_rng) == 0) { + return 0; + } + mbedtls_pk_free(pk); +#endif /* MBEDTLS_ECP_C */ + + /* If MBEDTLS_RSA_C is defined but MBEDTLS_ECP_C isn't, + * it is ok to leave the PK context initialized but not + * freed: It is the caller's responsibility to call pk_init() + * before calling this function, and to call pk_free() + * when it fails. If MBEDTLS_ECP_C is defined but MBEDTLS_RSA_C + * isn't, this leads to mbedtls_pk_free() being called + * twice, once here and once by the caller, but this is + * also ok and in line with the mbedtls_pk_free() calls + * on failed PEM parsing attempts. */ + + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; +} + +/* + * Parse a public key + */ +int mbedtls_pk_parse_public_key(mbedtls_pk_context *ctx, + const unsigned char *key, size_t keylen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p; +#if defined(MBEDTLS_RSA_C) + const mbedtls_pk_info_t *pk_info; +#endif +#if defined(MBEDTLS_PEM_PARSE_C) + size_t len; + mbedtls_pem_context pem; +#endif + + if (keylen == 0) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + +#if defined(MBEDTLS_PEM_PARSE_C) + mbedtls_pem_init(&pem); +#if defined(MBEDTLS_RSA_C) + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (key[keylen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN RSA PUBLIC KEY-----", + "-----END RSA PUBLIC KEY-----", + key, NULL, 0, &len); + } + + if (ret == 0) { + p = pem.buf; + if ((pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)) == NULL) { + mbedtls_pem_free(&pem); + return MBEDTLS_ERR_PK_UNKNOWN_PK_ALG; + } + + if ((ret = mbedtls_pk_setup(ctx, pk_info)) != 0) { + mbedtls_pem_free(&pem); + return ret; + } + + if ((ret = pk_get_rsapubkey(&p, p + pem.buflen, mbedtls_pk_rsa(*ctx))) != 0) { + mbedtls_pk_free(ctx); + } + + mbedtls_pem_free(&pem); + return ret; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + mbedtls_pem_free(&pem); + return ret; + } +#endif /* MBEDTLS_RSA_C */ + + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (key[keylen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN PUBLIC KEY-----", + "-----END PUBLIC KEY-----", + key, NULL, 0, &len); + } + + if (ret == 0) { + /* + * Was PEM encoded + */ + p = pem.buf; + + ret = mbedtls_pk_parse_subpubkey(&p, p + pem.buflen, ctx); + mbedtls_pem_free(&pem); + return ret; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + mbedtls_pem_free(&pem); + return ret; + } + mbedtls_pem_free(&pem); +#endif /* MBEDTLS_PEM_PARSE_C */ + +#if defined(MBEDTLS_RSA_C) + if ((pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)) == NULL) { + return MBEDTLS_ERR_PK_UNKNOWN_PK_ALG; + } + + if ((ret = mbedtls_pk_setup(ctx, pk_info)) != 0) { + return ret; + } + + p = (unsigned char *) key; + ret = pk_get_rsapubkey(&p, p + keylen, mbedtls_pk_rsa(*ctx)); + if (ret == 0) { + return ret; + } + mbedtls_pk_free(ctx); + if (ret != (MBEDTLS_ERROR_ADD(MBEDTLS_ERR_PK_INVALID_PUBKEY, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG))) { + return ret; + } +#endif /* MBEDTLS_RSA_C */ + p = (unsigned char *) key; + + ret = mbedtls_pk_parse_subpubkey(&p, p + keylen, ctx); + + return ret; +} + +#endif /* MBEDTLS_PK_PARSE_C */ diff --git a/r5dev/thirdparty/mbedtls/pkwrite.c b/r5dev/thirdparty/mbedtls/pkwrite.c new file mode 100644 index 00000000..2194c97e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pkwrite.c @@ -0,0 +1,538 @@ +/* + * Public Key layer for writing key files and structures + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PK_WRITE_C) + +#include "mbedtls/pk.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#if defined(MBEDTLS_RSA_C) +#include "mbedtls/rsa.h" +#endif +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/bignum.h" +#include "mbedtls/ecp.h" +#include "mbedtls/platform_util.h" +#endif +#if defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECP_C) +#include "pkwrite.h" +#endif +#if defined(MBEDTLS_ECDSA_C) +#include "mbedtls/ecdsa.h" +#endif +#if defined(MBEDTLS_PEM_WRITE_C) +#include "mbedtls/pem.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#endif +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_RSA_C) +/* + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ +static int pk_write_rsa_pubkey(unsigned char **p, unsigned char *start, + mbedtls_rsa_context *rsa) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + mbedtls_mpi T; + + mbedtls_mpi_init(&T); + + /* Export E */ + if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, NULL, &T)) != 0 || + (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export N */ + if ((ret = mbedtls_rsa_export(rsa, &T, NULL, NULL, NULL, NULL)) != 0 || + (ret = mbedtls_asn1_write_mpi(p, start, &T)) < 0) { + goto end_of_export; + } + len += ret; + +end_of_export: + + mbedtls_mpi_free(&T); + if (ret < 0) { + return ret; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + return (int) len; +} +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) +/* + * EC public key is an EC point + */ +static int pk_write_ec_pubkey(unsigned char **p, unsigned char *start, + mbedtls_ecp_keypair *ec) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + unsigned char buf[MBEDTLS_ECP_MAX_PT_LEN]; + + if ((ret = mbedtls_ecp_point_write_binary(&ec->grp, &ec->Q, + MBEDTLS_ECP_PF_UNCOMPRESSED, + &len, buf, sizeof(buf))) != 0) { + return ret; + } + + if (*p < start || (size_t) (*p - start) < len) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *p -= len; + memcpy(*p, buf, len); + + return (int) len; +} + +/* + * ECParameters ::= CHOICE { + * namedCurve OBJECT IDENTIFIER + * } + */ +static int pk_write_ec_param(unsigned char **p, unsigned char *start, + mbedtls_ecp_keypair *ec) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + const char *oid; + size_t oid_len; + + if ((ret = mbedtls_oid_get_oid_by_ec_grp(ec->grp.id, &oid, &oid_len)) != 0) { + return ret; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid, oid_len)); + + return (int) len; +} + +/* + * privateKey OCTET STRING -- always of length ceil(log2(n)/8) + */ +static int pk_write_ec_private(unsigned char **p, unsigned char *start, + mbedtls_ecp_keypair *ec) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t byte_length = (ec->grp.pbits + 7) / 8; + unsigned char tmp[MBEDTLS_ECP_MAX_BYTES]; + + ret = mbedtls_ecp_write_key(ec, tmp, byte_length); + if (ret != 0) { + goto exit; + } + ret = mbedtls_asn1_write_octet_string(p, start, tmp, byte_length); + +exit: + mbedtls_platform_zeroize(tmp, byte_length); + return ret; +} +#endif /* MBEDTLS_ECP_C */ + +int mbedtls_pk_write_pubkey(unsigned char **p, unsigned char *start, + const mbedtls_pk_context *key) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + +#if defined(MBEDTLS_RSA_C) + if (mbedtls_pk_get_type(key) == MBEDTLS_PK_RSA) { + MBEDTLS_ASN1_CHK_ADD(len, pk_write_rsa_pubkey(p, start, mbedtls_pk_rsa(*key))); + } else +#endif +#if defined(MBEDTLS_ECP_C) + if (mbedtls_pk_get_type(key) == MBEDTLS_PK_ECKEY) { + MBEDTLS_ASN1_CHK_ADD(len, pk_write_ec_pubkey(p, start, mbedtls_pk_ec(*key))); + } else +#endif +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (mbedtls_pk_get_type(key) == MBEDTLS_PK_OPAQUE) { + size_t buffer_size; + mbedtls_svc_key_id_t *key_id = (mbedtls_svc_key_id_t *) key->pk_ctx; + + if (*p < start) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } + + buffer_size = (size_t) (*p - start); + if (psa_export_public_key(*key_id, start, buffer_size, &len) + != PSA_SUCCESS) { + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + } else { + *p -= len; + memmove(*p, start, len); + } + } else +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + + return (int) len; +} + +int mbedtls_pk_write_pubkey_der(const mbedtls_pk_context *key, unsigned char *buf, size_t size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *c; + size_t len = 0, par_len = 0, oid_len; + mbedtls_pk_type_t pk_type; + const char *oid; + + if (size == 0) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + c = buf + size; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_pk_write_pubkey(&c, buf, key)); + + if (c - buf < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + /* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ + *--c = 0; + len += 1; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_BIT_STRING)); + + pk_type = mbedtls_pk_get_type(key); +#if defined(MBEDTLS_ECP_C) + if (pk_type == MBEDTLS_PK_ECKEY) { + MBEDTLS_ASN1_CHK_ADD(par_len, pk_write_ec_param(&c, buf, mbedtls_pk_ec(*key))); + } +#endif +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (pk_type == MBEDTLS_PK_OPAQUE) { + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_type_t key_type; + mbedtls_svc_key_id_t key_id; + psa_ecc_family_t curve; + size_t bits; + + key_id = *((mbedtls_svc_key_id_t *) key->pk_ctx); + if (PSA_SUCCESS != psa_get_key_attributes(key_id, &attributes)) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + key_type = psa_get_key_type(&attributes); + bits = psa_get_key_bits(&attributes); + psa_reset_key_attributes(&attributes); + + if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type)) { + curve = PSA_KEY_TYPE_ECC_GET_FAMILY(key_type); + if (curve == 0) { + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + } + + ret = mbedtls_psa_get_ecc_oid_from_id(curve, bits, + &oid, &oid_len); + if (ret != 0) { + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + } + + /* Write EC algorithm parameters; that's akin + * to pk_write_ec_param() above. */ + MBEDTLS_ASN1_CHK_ADD(par_len, mbedtls_asn1_write_oid(&c, buf, + oid, + oid_len)); + + /* The rest of the function works as for legacy EC contexts. */ + pk_type = MBEDTLS_PK_ECKEY; + } else if (PSA_KEY_TYPE_IS_RSA(key_type)) { + /* The rest of the function works as for legacy RSA contexts. */ + pk_type = MBEDTLS_PK_RSA; + } else { + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + } + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if ((ret = mbedtls_oid_get_oid_by_pk_alg(pk_type, &oid, + &oid_len)) != 0) { + return ret; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_algorithm_identifier(&c, buf, oid, oid_len, + par_len)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + return (int) len; +} + +int mbedtls_pk_write_key_der(const mbedtls_pk_context *key, unsigned char *buf, size_t size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *c; + size_t len = 0; + + if (size == 0) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + c = buf + size; + +#if defined(MBEDTLS_RSA_C) + if (mbedtls_pk_get_type(key) == MBEDTLS_PK_RSA) { + mbedtls_mpi T; /* Temporary holding the exported parameters */ + mbedtls_rsa_context *rsa = mbedtls_pk_rsa(*key); + + /* + * Export the parameters one after another to avoid simultaneous copies. + */ + + mbedtls_mpi_init(&T); + + /* Export QP */ + if ((ret = mbedtls_rsa_export_crt(rsa, NULL, NULL, &T)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export DQ */ + if ((ret = mbedtls_rsa_export_crt(rsa, NULL, &T, NULL)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export DP */ + if ((ret = mbedtls_rsa_export_crt(rsa, &T, NULL, NULL)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export Q */ + if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, + &T, NULL, NULL)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export P */ + if ((ret = mbedtls_rsa_export(rsa, NULL, &T, + NULL, NULL, NULL)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export D */ + if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, + NULL, &T, NULL)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export E */ + if ((ret = mbedtls_rsa_export(rsa, NULL, NULL, + NULL, NULL, &T)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + + /* Export N */ + if ((ret = mbedtls_rsa_export(rsa, &T, NULL, + NULL, NULL, NULL)) != 0 || + (ret = mbedtls_asn1_write_mpi(&c, buf, &T)) < 0) { + goto end_of_export; + } + len += ret; + +end_of_export: + + mbedtls_mpi_free(&T); + if (ret < 0) { + return ret; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(&c, buf, 0)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, + buf, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + } else +#endif /* MBEDTLS_RSA_C */ +#if defined(MBEDTLS_ECP_C) + if (mbedtls_pk_get_type(key) == MBEDTLS_PK_ECKEY) { + mbedtls_ecp_keypair *ec = mbedtls_pk_ec(*key); + size_t pub_len = 0, par_len = 0; + + /* + * RFC 5915, or SEC1 Appendix C.4 + * + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL + * } + */ + + /* publicKey */ + MBEDTLS_ASN1_CHK_ADD(pub_len, pk_write_ec_pubkey(&c, buf, ec)); + + if (c - buf < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + *--c = 0; + pub_len += 1; + + MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_len(&c, buf, pub_len)); + MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_BIT_STRING)); + + MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_len(&c, buf, pub_len)); + MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | 1)); + len += pub_len; + + /* parameters */ + MBEDTLS_ASN1_CHK_ADD(par_len, pk_write_ec_param(&c, buf, ec)); + + MBEDTLS_ASN1_CHK_ADD(par_len, mbedtls_asn1_write_len(&c, buf, par_len)); + MBEDTLS_ASN1_CHK_ADD(par_len, mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | 0)); + len += par_len; + + /* privateKey */ + MBEDTLS_ASN1_CHK_ADD(len, pk_write_ec_private(&c, buf, ec)); + + /* version */ + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(&c, buf, 1)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + } else +#endif /* MBEDTLS_ECP_C */ + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + + return (int) len; +} + +#if defined(MBEDTLS_PEM_WRITE_C) + +#define PEM_BEGIN_PUBLIC_KEY "-----BEGIN PUBLIC KEY-----\n" +#define PEM_END_PUBLIC_KEY "-----END PUBLIC KEY-----\n" + +#define PEM_BEGIN_PRIVATE_KEY_RSA "-----BEGIN RSA PRIVATE KEY-----\n" +#define PEM_END_PRIVATE_KEY_RSA "-----END RSA PRIVATE KEY-----\n" +#define PEM_BEGIN_PRIVATE_KEY_EC "-----BEGIN EC PRIVATE KEY-----\n" +#define PEM_END_PRIVATE_KEY_EC "-----END EC PRIVATE KEY-----\n" + +#define PUB_DER_MAX_BYTES \ + (MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES > MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES ? \ + MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES : MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES) +#define PRV_DER_MAX_BYTES \ + (MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES > MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES ? \ + MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES : MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES) + +int mbedtls_pk_write_pubkey_pem(const mbedtls_pk_context *key, unsigned char *buf, size_t size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char output_buf[PUB_DER_MAX_BYTES]; + size_t olen = 0; + + if ((ret = mbedtls_pk_write_pubkey_der(key, output_buf, + sizeof(output_buf))) < 0) { + return ret; + } + + if ((ret = mbedtls_pem_write_buffer(PEM_BEGIN_PUBLIC_KEY, PEM_END_PUBLIC_KEY, + output_buf + sizeof(output_buf) - ret, + ret, buf, size, &olen)) != 0) { + return ret; + } + + return 0; +} + +int mbedtls_pk_write_key_pem(const mbedtls_pk_context *key, unsigned char *buf, size_t size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char output_buf[PRV_DER_MAX_BYTES]; + const char *begin, *end; + size_t olen = 0; + + if ((ret = mbedtls_pk_write_key_der(key, output_buf, sizeof(output_buf))) < 0) { + return ret; + } + +#if defined(MBEDTLS_RSA_C) + if (mbedtls_pk_get_type(key) == MBEDTLS_PK_RSA) { + begin = PEM_BEGIN_PRIVATE_KEY_RSA; + end = PEM_END_PRIVATE_KEY_RSA; + } else +#endif +#if defined(MBEDTLS_ECP_C) + if (mbedtls_pk_get_type(key) == MBEDTLS_PK_ECKEY) { + begin = PEM_BEGIN_PRIVATE_KEY_EC; + end = PEM_END_PRIVATE_KEY_EC; + } else +#endif + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + + if ((ret = mbedtls_pem_write_buffer(begin, end, + output_buf + sizeof(output_buf) - ret, + ret, buf, size, &olen)) != 0) { + return ret; + } + + return 0; +} +#endif /* MBEDTLS_PEM_WRITE_C */ + +#endif /* MBEDTLS_PK_WRITE_C */ diff --git a/r5dev/thirdparty/mbedtls/pkwrite.h b/r5dev/thirdparty/mbedtls/pkwrite.h new file mode 100644 index 00000000..8aebd0c2 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/pkwrite.h @@ -0,0 +1,108 @@ +/** + * \file pkwrite.h + * + * \brief Internal defines shared by the PK write module + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_PK_WRITE_H +#define MBEDTLS_PK_WRITE_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/pk.h" + +/* + * Max sizes of key per types. Shown as tag + len (+ content). + */ + +#if defined(MBEDTLS_RSA_C) +/* + * RSA public keys: + * SubjectPublicKeyInfo ::= SEQUENCE { 1 + 3 + * algorithm AlgorithmIdentifier, 1 + 1 (sequence) + * + 1 + 1 + 9 (rsa oid) + * + 1 + 1 (params null) + * subjectPublicKey BIT STRING } 1 + 3 + (1 + below) + * RSAPublicKey ::= SEQUENCE { 1 + 3 + * modulus INTEGER, -- n 1 + 3 + MPI_MAX + 1 + * publicExponent INTEGER -- e 1 + 3 + MPI_MAX + 1 + * } + */ +#define MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE) + +/* + * RSA private keys: + * RSAPrivateKey ::= SEQUENCE { 1 + 3 + * version Version, 1 + 1 + 1 + * modulus INTEGER, 1 + 3 + MPI_MAX + 1 + * publicExponent INTEGER, 1 + 3 + MPI_MAX + 1 + * privateExponent INTEGER, 1 + 3 + MPI_MAX + 1 + * prime1 INTEGER, 1 + 3 + MPI_MAX / 2 + 1 + * prime2 INTEGER, 1 + 3 + MPI_MAX / 2 + 1 + * exponent1 INTEGER, 1 + 3 + MPI_MAX / 2 + 1 + * exponent2 INTEGER, 1 + 3 + MPI_MAX / 2 + 1 + * coefficient INTEGER, 1 + 3 + MPI_MAX / 2 + 1 + * otherPrimeInfos OtherPrimeInfos OPTIONAL 0 (not supported) + * } + */ +#define MBEDTLS_MPI_MAX_SIZE_2 (MBEDTLS_MPI_MAX_SIZE / 2 + \ + MBEDTLS_MPI_MAX_SIZE % 2) +#define MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES (47 + 3 * MBEDTLS_MPI_MAX_SIZE \ + + 5 * MBEDTLS_MPI_MAX_SIZE_2) + +#else /* MBEDTLS_RSA_C */ + +#define MBEDTLS_PK_RSA_PUB_DER_MAX_BYTES 0 +#define MBEDTLS_PK_RSA_PRV_DER_MAX_BYTES 0 + +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECP_C) +/* + * EC public keys: + * SubjectPublicKeyInfo ::= SEQUENCE { 1 + 2 + * algorithm AlgorithmIdentifier, 1 + 1 (sequence) + * + 1 + 1 + 7 (ec oid) + * + 1 + 1 + 9 (namedCurve oid) + * subjectPublicKey BIT STRING 1 + 2 + 1 [1] + * + 1 (point format) [1] + * + 2 * ECP_MAX (coords) [1] + * } + */ +#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES) + +/* + * EC private keys: + * ECPrivateKey ::= SEQUENCE { 1 + 2 + * version INTEGER , 1 + 1 + 1 + * privateKey OCTET STRING, 1 + 1 + ECP_MAX + * parameters [0] ECParameters OPTIONAL, 1 + 1 + (1 + 1 + 9) + * publicKey [1] BIT STRING OPTIONAL 1 + 2 + [1] above + * } + */ +#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES (29 + 3 * MBEDTLS_ECP_MAX_BYTES) + +#else /* MBEDTLS_ECP_C */ + +#define MBEDTLS_PK_ECP_PUB_DER_MAX_BYTES 0 +#define MBEDTLS_PK_ECP_PRV_DER_MAX_BYTES 0 + +#endif /* MBEDTLS_ECP_C */ + +#endif /* MBEDTLS_PK_WRITE_H */ diff --git a/r5dev/thirdparty/mbedtls/platform.c b/r5dev/thirdparty/mbedtls/platform.c new file mode 100644 index 00000000..b15b7b29 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/platform.c @@ -0,0 +1,414 @@ +/* + * Platform abstraction layer + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PLATFORM_C) + +#include "mbedtls/platform.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +/* The compile time configuration of memory allocation via the macros + * MBEDTLS_PLATFORM_{FREE/CALLOC}_MACRO takes precedence over the runtime + * configuration via mbedtls_platform_set_calloc_free(). So, omit everything + * related to the latter if MBEDTLS_PLATFORM_{FREE/CALLOC}_MACRO are defined. */ +#if defined(MBEDTLS_PLATFORM_MEMORY) && \ + !(defined(MBEDTLS_PLATFORM_CALLOC_MACRO) && \ + defined(MBEDTLS_PLATFORM_FREE_MACRO)) + +#if !defined(MBEDTLS_PLATFORM_STD_CALLOC) +static void *platform_calloc_uninit(size_t n, size_t size) +{ + ((void) n); + ((void) size); + return NULL; +} + +#define MBEDTLS_PLATFORM_STD_CALLOC platform_calloc_uninit +#endif /* !MBEDTLS_PLATFORM_STD_CALLOC */ + +#if !defined(MBEDTLS_PLATFORM_STD_FREE) +static void platform_free_uninit(void *ptr) +{ + ((void) ptr); +} + +#define MBEDTLS_PLATFORM_STD_FREE platform_free_uninit +#endif /* !MBEDTLS_PLATFORM_STD_FREE */ + +static void * (*mbedtls_calloc_func)(size_t, size_t) = MBEDTLS_PLATFORM_STD_CALLOC; +static void (*mbedtls_free_func)(void *) = MBEDTLS_PLATFORM_STD_FREE; + +void *mbedtls_calloc(size_t nmemb, size_t size) +{ + return (*mbedtls_calloc_func)(nmemb, size); +} + +void mbedtls_free(void *ptr) +{ + (*mbedtls_free_func)(ptr); +} + +int mbedtls_platform_set_calloc_free(void *(*calloc_func)(size_t, size_t), + void (*free_func)(void *)) +{ + mbedtls_calloc_func = calloc_func; + mbedtls_free_func = free_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_MEMORY && + !( defined(MBEDTLS_PLATFORM_CALLOC_MACRO) && + defined(MBEDTLS_PLATFORM_FREE_MACRO) ) */ + +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_SNPRINTF) +#include +int mbedtls_platform_win32_snprintf(char *s, size_t n, const char *fmt, ...) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + va_list argp; + + va_start(argp, fmt); + ret = mbedtls_vsnprintf(s, n, fmt, argp); + va_end(argp); + + return ret; +} +#endif + +#if defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_SNPRINTF) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_snprintf_uninit(char *s, size_t n, + const char *format, ...) +{ + ((void) s); + ((void) n); + ((void) format); + return 0; +} + +#define MBEDTLS_PLATFORM_STD_SNPRINTF platform_snprintf_uninit +#endif /* !MBEDTLS_PLATFORM_STD_SNPRINTF */ + +int (*mbedtls_snprintf)(char *s, size_t n, + const char *format, + ...) = MBEDTLS_PLATFORM_STD_SNPRINTF; + +int mbedtls_platform_set_snprintf(int (*snprintf_func)(char *s, size_t n, + const char *format, + ...)) +{ + mbedtls_snprintf = snprintf_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_SNPRINTF_ALT */ + +#if defined(MBEDTLS_PLATFORM_HAS_NON_CONFORMING_VSNPRINTF) +#include +int mbedtls_platform_win32_vsnprintf(char *s, size_t n, const char *fmt, va_list arg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Avoid calling the invalid parameter handler by checking ourselves */ + if (s == NULL || n == 0 || fmt == NULL) { + return -1; + } + +#if defined(_TRUNCATE) + ret = vsnprintf_s(s, n, _TRUNCATE, fmt, arg); +#else + ret = vsnprintf(s, n, fmt, arg); + if (ret < 0 || (size_t) ret == n) { + s[n-1] = '\0'; + ret = -1; + } +#endif + + return ret; +} +#endif + +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_VSNPRINTF) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_vsnprintf_uninit(char *s, size_t n, + const char *format, va_list arg) +{ + ((void) s); + ((void) n); + ((void) format); + ((void) arg); + return -1; +} + +#define MBEDTLS_PLATFORM_STD_VSNPRINTF platform_vsnprintf_uninit +#endif /* !MBEDTLS_PLATFORM_STD_VSNPRINTF */ + +int (*mbedtls_vsnprintf)(char *s, size_t n, + const char *format, + va_list arg) = MBEDTLS_PLATFORM_STD_VSNPRINTF; + +int mbedtls_platform_set_vsnprintf(int (*vsnprintf_func)(char *s, size_t n, + const char *format, + va_list arg)) +{ + mbedtls_vsnprintf = vsnprintf_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */ + +#if defined(MBEDTLS_PLATFORM_PRINTF_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_PRINTF) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_printf_uninit(const char *format, ...) +{ + ((void) format); + return 0; +} + +#define MBEDTLS_PLATFORM_STD_PRINTF platform_printf_uninit +#endif /* !MBEDTLS_PLATFORM_STD_PRINTF */ + +int (*mbedtls_printf)(const char *, ...) = MBEDTLS_PLATFORM_STD_PRINTF; + +int mbedtls_platform_set_printf(int (*printf_func)(const char *, ...)) +{ + mbedtls_printf = printf_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_PRINTF_ALT */ + +#if defined(MBEDTLS_PLATFORM_FPRINTF_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_FPRINTF) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_fprintf_uninit(FILE *stream, const char *format, ...) +{ + ((void) stream); + ((void) format); + return 0; +} + +#define MBEDTLS_PLATFORM_STD_FPRINTF platform_fprintf_uninit +#endif /* !MBEDTLS_PLATFORM_STD_FPRINTF */ + +int (*mbedtls_fprintf)(FILE *, const char *, ...) = + MBEDTLS_PLATFORM_STD_FPRINTF; + +int mbedtls_platform_set_fprintf(int (*fprintf_func)(FILE *, const char *, ...)) +{ + mbedtls_fprintf = fprintf_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_FPRINTF_ALT */ + +#if defined(MBEDTLS_PLATFORM_SETBUF_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_SETBUF) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static void platform_setbuf_uninit(FILE *stream, char *buf) +{ + ((void) stream); + ((void) buf); +} + +#define MBEDTLS_PLATFORM_STD_SETBUF platform_setbuf_uninit +#endif /* !MBEDTLS_PLATFORM_STD_SETBUF */ +void (*mbedtls_setbuf)(FILE *stream, char *buf) = MBEDTLS_PLATFORM_STD_SETBUF; + +int mbedtls_platform_set_setbuf(void (*setbuf_func)(FILE *stream, char *buf)) +{ + mbedtls_setbuf = setbuf_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_SETBUF_ALT */ + +#if defined(MBEDTLS_PLATFORM_EXIT_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_EXIT) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static void platform_exit_uninit(int status) +{ + ((void) status); +} + +#define MBEDTLS_PLATFORM_STD_EXIT platform_exit_uninit +#endif /* !MBEDTLS_PLATFORM_STD_EXIT */ + +void (*mbedtls_exit)(int status) = MBEDTLS_PLATFORM_STD_EXIT; + +int mbedtls_platform_set_exit(void (*exit_func)(int status)) +{ + mbedtls_exit = exit_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_EXIT_ALT */ + +#if defined(MBEDTLS_HAVE_TIME) + +#if defined(MBEDTLS_PLATFORM_TIME_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_TIME) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static mbedtls_time_t platform_time_uninit(mbedtls_time_t *timer) +{ + ((void) timer); + return 0; +} + +#define MBEDTLS_PLATFORM_STD_TIME platform_time_uninit +#endif /* !MBEDTLS_PLATFORM_STD_TIME */ + +mbedtls_time_t (*mbedtls_time)(mbedtls_time_t *timer) = MBEDTLS_PLATFORM_STD_TIME; + +int mbedtls_platform_set_time(mbedtls_time_t (*time_func)(mbedtls_time_t *timer)) +{ + mbedtls_time = time_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_TIME_ALT */ + +#endif /* MBEDTLS_HAVE_TIME */ + +#if defined(MBEDTLS_ENTROPY_NV_SEED) +#if !defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) && defined(MBEDTLS_FS_IO) +/* Default implementations for the platform independent seed functions use + * standard libc file functions to read from and write to a pre-defined filename + */ +int mbedtls_platform_std_nv_seed_read(unsigned char *buf, size_t buf_len) +{ + FILE *file; + size_t n; + + if ((file = fopen(MBEDTLS_PLATFORM_STD_NV_SEED_FILE, "rb")) == NULL) { + return -1; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(file, NULL); + + if ((n = fread(buf, 1, buf_len, file)) != buf_len) { + fclose(file); + mbedtls_platform_zeroize(buf, buf_len); + return -1; + } + + fclose(file); + return (int) n; +} + +int mbedtls_platform_std_nv_seed_write(unsigned char *buf, size_t buf_len) +{ + FILE *file; + size_t n; + + if ((file = fopen(MBEDTLS_PLATFORM_STD_NV_SEED_FILE, "w")) == NULL) { + return -1; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(file, NULL); + + if ((n = fwrite(buf, 1, buf_len, file)) != buf_len) { + fclose(file); + return -1; + } + + fclose(file); + return (int) n; +} +#endif /* MBEDTLS_PLATFORM_NO_STD_FUNCTIONS */ + +#if defined(MBEDTLS_PLATFORM_NV_SEED_ALT) +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_READ) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_nv_seed_read_uninit(unsigned char *buf, size_t buf_len) +{ + ((void) buf); + ((void) buf_len); + return -1; +} + +#define MBEDTLS_PLATFORM_STD_NV_SEED_READ platform_nv_seed_read_uninit +#endif /* !MBEDTLS_PLATFORM_STD_NV_SEED_READ */ + +#if !defined(MBEDTLS_PLATFORM_STD_NV_SEED_WRITE) +/* + * Make dummy function to prevent NULL pointer dereferences + */ +static int platform_nv_seed_write_uninit(unsigned char *buf, size_t buf_len) +{ + ((void) buf); + ((void) buf_len); + return -1; +} + +#define MBEDTLS_PLATFORM_STD_NV_SEED_WRITE platform_nv_seed_write_uninit +#endif /* !MBEDTLS_PLATFORM_STD_NV_SEED_WRITE */ + +int (*mbedtls_nv_seed_read)(unsigned char *buf, size_t buf_len) = + MBEDTLS_PLATFORM_STD_NV_SEED_READ; +int (*mbedtls_nv_seed_write)(unsigned char *buf, size_t buf_len) = + MBEDTLS_PLATFORM_STD_NV_SEED_WRITE; + +int mbedtls_platform_set_nv_seed( + int (*nv_seed_read_func)(unsigned char *buf, size_t buf_len), + int (*nv_seed_write_func)(unsigned char *buf, size_t buf_len)) +{ + mbedtls_nv_seed_read = nv_seed_read_func; + mbedtls_nv_seed_write = nv_seed_write_func; + return 0; +} +#endif /* MBEDTLS_PLATFORM_NV_SEED_ALT */ +#endif /* MBEDTLS_ENTROPY_NV_SEED */ + +#if !defined(MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT) +/* + * Placeholder platform setup that does nothing by default + */ +int mbedtls_platform_setup(mbedtls_platform_context *ctx) +{ + (void) ctx; + + return 0; +} + +/* + * Placeholder platform teardown that does nothing by default + */ +void mbedtls_platform_teardown(mbedtls_platform_context *ctx) +{ + (void) ctx; +} +#endif /* MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT */ + +#endif /* MBEDTLS_PLATFORM_C */ diff --git a/r5dev/thirdparty/mbedtls/platform_util.c b/r5dev/thirdparty/mbedtls/platform_util.c new file mode 100644 index 00000000..f891cd48 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/platform_util.c @@ -0,0 +1,221 @@ +/* + * Common and shared functions used by multiple modules in the Mbed TLS + * library. + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Ensure gmtime_r is available even with -std=c99; must be defined before + * mbedtls_config.h, which pulls in glibc's features.h. Harmless on other platforms + * except OpenBSD, where it stops us accessing explicit_bzero. + */ +#if !defined(_POSIX_C_SOURCE) && !defined(__OpenBSD__) +#define _POSIX_C_SOURCE 200112L +#endif + +#if !defined(_GNU_SOURCE) +/* Clang requires this to get support for explicit_bzero */ +#define _GNU_SOURCE +#endif + +#include "common.h" + +#include "mbedtls/platform_util.h" +#include "mbedtls/platform.h" +#include "mbedtls/threading.h" + +#include + +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 /* Ask for the C11 gmtime_s() and memset_s() if available */ +#endif +#include + +#if defined(_WIN32) +#include +#endif + +// Detect platforms known to support explicit_bzero() +#if defined(__GLIBC__) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 25) +#define MBEDTLS_PLATFORM_HAS_EXPLICIT_BZERO 1 +#elif (defined(__FreeBSD__) && (__FreeBSD_version >= 1100037)) || defined(__OpenBSD__) +#define MBEDTLS_PLATFORM_HAS_EXPLICIT_BZERO 1 +#endif + +#if !defined(MBEDTLS_PLATFORM_ZEROIZE_ALT) + +#undef HAVE_MEMORY_SANITIZER +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#include +#define HAVE_MEMORY_SANITIZER +#endif +#endif + +/* + * Where possible, we try to detect the presence of a platform-provided + * secure memset, such as explicit_bzero(), that is safe against being optimized + * out, and use that. + * + * For other platforms, we provide an implementation that aims not to be + * optimized out by the compiler. + * + * This implementation for mbedtls_platform_zeroize() was inspired from Colin + * Percival's blog article at: + * + * http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html + * + * It uses a volatile function pointer to the standard memset(). Because the + * pointer is volatile the compiler expects it to change at + * any time and will not optimize out the call that could potentially perform + * other operations on the input buffer instead of just setting it to 0. + * Nevertheless, as pointed out by davidtgoldblatt on Hacker News + * (refer to http://www.daemonology.net/blog/2014-09-05-erratum.html for + * details), optimizations of the following form are still possible: + * + * if (memset_func != memset) + * memset_func(buf, 0, len); + * + * Note that it is extremely difficult to guarantee that + * the memset() call will not be optimized out by aggressive compilers + * in a portable way. For this reason, Mbed TLS also provides the configuration + * option MBEDTLS_PLATFORM_ZEROIZE_ALT, which allows users to configure + * mbedtls_platform_zeroize() to use a suitable implementation for their + * platform and needs. + */ +#if !defined(MBEDTLS_PLATFORM_HAS_EXPLICIT_BZERO) && !defined(__STDC_LIB_EXT1__) \ + && !defined(_WIN32) +static void *(*const volatile memset_func)(void *, int, size_t) = memset; +#endif + +void mbedtls_platform_zeroize(void *buf, size_t len) +{ + MBEDTLS_INTERNAL_VALIDATE(len == 0 || buf != NULL); + + if (len > 0) { +#if defined(MBEDTLS_PLATFORM_HAS_EXPLICIT_BZERO) + explicit_bzero(buf, len); +#if defined(HAVE_MEMORY_SANITIZER) + /* You'd think that Msan would recognize explicit_bzero() as + * equivalent to bzero(), but it actually doesn't on several + * platforms, including Linux (Ubuntu 20.04). + * https://github.com/google/sanitizers/issues/1507 + * https://github.com/openssh/openssh-portable/commit/74433a19bb6f4cef607680fa4d1d7d81ca3826aa + */ + __msan_unpoison(buf, len); +#endif +#elif defined(__STDC_LIB_EXT1__) + memset_s(buf, len, 0, len); +#elif defined(_WIN32) + SecureZeroMemory(buf, len); +#else + memset_func(buf, 0, len); +#endif + } +} +#endif /* MBEDTLS_PLATFORM_ZEROIZE_ALT */ + +#if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_PLATFORM_GMTIME_R_ALT) +#include +#if !defined(_WIN32) && (defined(unix) || \ + defined(__unix) || defined(__unix__) || (defined(__APPLE__) && \ + defined(__MACH__))) +#include +#endif /* !_WIN32 && (unix || __unix || __unix__ || + * (__APPLE__ && __MACH__)) */ + +#if !((defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L) || \ + (defined(_POSIX_THREAD_SAFE_FUNCTIONS) && \ + _POSIX_THREAD_SAFE_FUNCTIONS >= 200112L)) +/* + * This is a convenience shorthand macro to avoid checking the long + * preprocessor conditions above. Ideally, we could expose this macro in + * platform_util.h and simply use it in platform_util.c, threading.c and + * threading.h. However, this macro is not part of the Mbed TLS public API, so + * we keep it private by only defining it in this file + */ +#if !(defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)) || \ + (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) +#define PLATFORM_UTIL_USE_GMTIME +#endif + +#endif /* !( ( defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L ) || \ + ( defined(_POSIX_THREAD_SAFE_FUNCTIONS ) && \ + _POSIX_THREAD_SAFE_FUNCTIONS >= 200112L ) ) */ + +struct tm *mbedtls_platform_gmtime_r(const mbedtls_time_t *tt, + struct tm *tm_buf) +{ +#if defined(_WIN32) && !defined(PLATFORM_UTIL_USE_GMTIME) +#if defined(__STDC_LIB_EXT1__) + return (gmtime_s(tt, tm_buf) == 0) ? NULL : tm_buf; +#else + /* MSVC and mingw64 argument order and return value are inconsistent with the C11 standard */ + return (gmtime_s(tm_buf, tt) == 0) ? tm_buf : NULL; +#endif +#elif !defined(PLATFORM_UTIL_USE_GMTIME) + return gmtime_r(tt, tm_buf); +#else + struct tm *lt; + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_lock(&mbedtls_threading_gmtime_mutex) != 0) { + return NULL; + } +#endif /* MBEDTLS_THREADING_C */ + + lt = gmtime(tt); + + if (lt != NULL) { + memcpy(tm_buf, lt, sizeof(struct tm)); + } + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&mbedtls_threading_gmtime_mutex) != 0) { + return NULL; + } +#endif /* MBEDTLS_THREADING_C */ + + return (lt == NULL) ? NULL : tm_buf; +#endif /* _WIN32 && !EFIX64 && !EFI32 */ +} +#endif /* MBEDTLS_HAVE_TIME_DATE && MBEDTLS_PLATFORM_GMTIME_R_ALT */ + +#if defined(MBEDTLS_TEST_HOOKS) +void (*mbedtls_test_hook_test_fail)(const char *, int, const char *); +#endif /* MBEDTLS_TEST_HOOKS */ + +/* + * Provide external definitions of some inline functions so that the compiler + * has the option to not inline them + */ +extern inline void mbedtls_xor(unsigned char *r, + const unsigned char *a, + const unsigned char *b, + size_t n); + +extern inline uint16_t mbedtls_get_unaligned_uint16(const void *p); + +extern inline void mbedtls_put_unaligned_uint16(void *p, uint16_t x); + +extern inline uint32_t mbedtls_get_unaligned_uint32(const void *p); + +extern inline void mbedtls_put_unaligned_uint32(void *p, uint32_t x); + +extern inline uint64_t mbedtls_get_unaligned_uint64(const void *p); + +extern inline void mbedtls_put_unaligned_uint64(void *p, uint64_t x); diff --git a/r5dev/thirdparty/mbedtls/poly1305.c b/r5dev/thirdparty/mbedtls/poly1305.c new file mode 100644 index 00000000..f4e1d3f8 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/poly1305.c @@ -0,0 +1,504 @@ +/** + * \file poly1305.c + * + * \brief Poly1305 authentication algorithm. + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "common.h" + +#if defined(MBEDTLS_POLY1305_C) + +#include "mbedtls/poly1305.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_POLY1305_ALT) + +#define POLY1305_BLOCK_SIZE_BYTES (16U) + +/* + * Our implementation is tuned for 32-bit platforms with a 64-bit multiplier. + * However we provided an alternative for platforms without such a multiplier. + */ +#if defined(MBEDTLS_NO_64BIT_MULTIPLICATION) +static uint64_t mul64(uint32_t a, uint32_t b) +{ + /* a = al + 2**16 ah, b = bl + 2**16 bh */ + const uint16_t al = (uint16_t) a; + const uint16_t bl = (uint16_t) b; + const uint16_t ah = a >> 16; + const uint16_t bh = b >> 16; + + /* ab = al*bl + 2**16 (ah*bl + bl*bh) + 2**32 ah*bh */ + const uint32_t lo = (uint32_t) al * bl; + const uint64_t me = (uint64_t) ((uint32_t) ah * bl) + (uint32_t) al * bh; + const uint32_t hi = (uint32_t) ah * bh; + + return lo + (me << 16) + ((uint64_t) hi << 32); +} +#else +static inline uint64_t mul64(uint32_t a, uint32_t b) +{ + return (uint64_t) a * b; +} +#endif + + +/** + * \brief Process blocks with Poly1305. + * + * \param ctx The Poly1305 context. + * \param nblocks Number of blocks to process. Note that this + * function only processes full blocks. + * \param input Buffer containing the input block(s). + * \param needs_padding Set to 0 if the padding bit has already been + * applied to the input data before calling this + * function. Otherwise, set this parameter to 1. + */ +static void poly1305_process(mbedtls_poly1305_context *ctx, + size_t nblocks, + const unsigned char *input, + uint32_t needs_padding) +{ + uint64_t d0, d1, d2, d3; + uint32_t acc0, acc1, acc2, acc3, acc4; + uint32_t r0, r1, r2, r3; + uint32_t rs1, rs2, rs3; + size_t offset = 0U; + size_t i; + + r0 = ctx->r[0]; + r1 = ctx->r[1]; + r2 = ctx->r[2]; + r3 = ctx->r[3]; + + rs1 = r1 + (r1 >> 2U); + rs2 = r2 + (r2 >> 2U); + rs3 = r3 + (r3 >> 2U); + + acc0 = ctx->acc[0]; + acc1 = ctx->acc[1]; + acc2 = ctx->acc[2]; + acc3 = ctx->acc[3]; + acc4 = ctx->acc[4]; + + /* Process full blocks */ + for (i = 0U; i < nblocks; i++) { + /* The input block is treated as a 128-bit little-endian integer */ + d0 = MBEDTLS_GET_UINT32_LE(input, offset + 0); + d1 = MBEDTLS_GET_UINT32_LE(input, offset + 4); + d2 = MBEDTLS_GET_UINT32_LE(input, offset + 8); + d3 = MBEDTLS_GET_UINT32_LE(input, offset + 12); + + /* Compute: acc += (padded) block as a 130-bit integer */ + d0 += (uint64_t) acc0; + d1 += (uint64_t) acc1 + (d0 >> 32U); + d2 += (uint64_t) acc2 + (d1 >> 32U); + d3 += (uint64_t) acc3 + (d2 >> 32U); + acc0 = (uint32_t) d0; + acc1 = (uint32_t) d1; + acc2 = (uint32_t) d2; + acc3 = (uint32_t) d3; + acc4 += (uint32_t) (d3 >> 32U) + needs_padding; + + /* Compute: acc *= r */ + d0 = mul64(acc0, r0) + + mul64(acc1, rs3) + + mul64(acc2, rs2) + + mul64(acc3, rs1); + d1 = mul64(acc0, r1) + + mul64(acc1, r0) + + mul64(acc2, rs3) + + mul64(acc3, rs2) + + mul64(acc4, rs1); + d2 = mul64(acc0, r2) + + mul64(acc1, r1) + + mul64(acc2, r0) + + mul64(acc3, rs3) + + mul64(acc4, rs2); + d3 = mul64(acc0, r3) + + mul64(acc1, r2) + + mul64(acc2, r1) + + mul64(acc3, r0) + + mul64(acc4, rs3); + acc4 *= r0; + + /* Compute: acc %= (2^130 - 5) (partial remainder) */ + d1 += (d0 >> 32); + d2 += (d1 >> 32); + d3 += (d2 >> 32); + acc0 = (uint32_t) d0; + acc1 = (uint32_t) d1; + acc2 = (uint32_t) d2; + acc3 = (uint32_t) d3; + acc4 = (uint32_t) (d3 >> 32) + acc4; + + d0 = (uint64_t) acc0 + (acc4 >> 2) + (acc4 & 0xFFFFFFFCU); + acc4 &= 3U; + acc0 = (uint32_t) d0; + d0 = (uint64_t) acc1 + (d0 >> 32U); + acc1 = (uint32_t) d0; + d0 = (uint64_t) acc2 + (d0 >> 32U); + acc2 = (uint32_t) d0; + d0 = (uint64_t) acc3 + (d0 >> 32U); + acc3 = (uint32_t) d0; + d0 = (uint64_t) acc4 + (d0 >> 32U); + acc4 = (uint32_t) d0; + + offset += POLY1305_BLOCK_SIZE_BYTES; + } + + ctx->acc[0] = acc0; + ctx->acc[1] = acc1; + ctx->acc[2] = acc2; + ctx->acc[3] = acc3; + ctx->acc[4] = acc4; +} + +/** + * \brief Compute the Poly1305 MAC + * + * \param ctx The Poly1305 context. + * \param mac The buffer to where the MAC is written. Must be + * big enough to contain the 16-byte MAC. + */ +static void poly1305_compute_mac(const mbedtls_poly1305_context *ctx, + unsigned char mac[16]) +{ + uint64_t d; + uint32_t g0, g1, g2, g3, g4; + uint32_t acc0, acc1, acc2, acc3, acc4; + uint32_t mask; + uint32_t mask_inv; + + acc0 = ctx->acc[0]; + acc1 = ctx->acc[1]; + acc2 = ctx->acc[2]; + acc3 = ctx->acc[3]; + acc4 = ctx->acc[4]; + + /* Before adding 's' we ensure that the accumulator is mod 2^130 - 5. + * We do this by calculating acc - (2^130 - 5), then checking if + * the 131st bit is set. If it is, then reduce: acc -= (2^130 - 5) + */ + + /* Calculate acc + -(2^130 - 5) */ + d = ((uint64_t) acc0 + 5U); + g0 = (uint32_t) d; + d = ((uint64_t) acc1 + (d >> 32)); + g1 = (uint32_t) d; + d = ((uint64_t) acc2 + (d >> 32)); + g2 = (uint32_t) d; + d = ((uint64_t) acc3 + (d >> 32)); + g3 = (uint32_t) d; + g4 = acc4 + (uint32_t) (d >> 32U); + + /* mask == 0xFFFFFFFF if 131st bit is set, otherwise mask == 0 */ + mask = (uint32_t) 0U - (g4 >> 2U); + mask_inv = ~mask; + + /* If 131st bit is set then acc=g, otherwise, acc is unmodified */ + acc0 = (acc0 & mask_inv) | (g0 & mask); + acc1 = (acc1 & mask_inv) | (g1 & mask); + acc2 = (acc2 & mask_inv) | (g2 & mask); + acc3 = (acc3 & mask_inv) | (g3 & mask); + + /* Add 's' */ + d = (uint64_t) acc0 + ctx->s[0]; + acc0 = (uint32_t) d; + d = (uint64_t) acc1 + ctx->s[1] + (d >> 32U); + acc1 = (uint32_t) d; + d = (uint64_t) acc2 + ctx->s[2] + (d >> 32U); + acc2 = (uint32_t) d; + acc3 += ctx->s[3] + (uint32_t) (d >> 32U); + + /* Compute MAC (128 least significant bits of the accumulator) */ + MBEDTLS_PUT_UINT32_LE(acc0, mac, 0); + MBEDTLS_PUT_UINT32_LE(acc1, mac, 4); + MBEDTLS_PUT_UINT32_LE(acc2, mac, 8); + MBEDTLS_PUT_UINT32_LE(acc3, mac, 12); +} + +void mbedtls_poly1305_init(mbedtls_poly1305_context *ctx) +{ + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_poly1305_context)); +} + +void mbedtls_poly1305_free(mbedtls_poly1305_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_poly1305_context)); +} + +int mbedtls_poly1305_starts(mbedtls_poly1305_context *ctx, + const unsigned char key[32]) +{ + /* r &= 0x0ffffffc0ffffffc0ffffffc0fffffff */ + ctx->r[0] = MBEDTLS_GET_UINT32_LE(key, 0) & 0x0FFFFFFFU; + ctx->r[1] = MBEDTLS_GET_UINT32_LE(key, 4) & 0x0FFFFFFCU; + ctx->r[2] = MBEDTLS_GET_UINT32_LE(key, 8) & 0x0FFFFFFCU; + ctx->r[3] = MBEDTLS_GET_UINT32_LE(key, 12) & 0x0FFFFFFCU; + + ctx->s[0] = MBEDTLS_GET_UINT32_LE(key, 16); + ctx->s[1] = MBEDTLS_GET_UINT32_LE(key, 20); + ctx->s[2] = MBEDTLS_GET_UINT32_LE(key, 24); + ctx->s[3] = MBEDTLS_GET_UINT32_LE(key, 28); + + /* Initial accumulator state */ + ctx->acc[0] = 0U; + ctx->acc[1] = 0U; + ctx->acc[2] = 0U; + ctx->acc[3] = 0U; + ctx->acc[4] = 0U; + + /* Queue initially empty */ + mbedtls_platform_zeroize(ctx->queue, sizeof(ctx->queue)); + ctx->queue_len = 0U; + + return 0; +} + +int mbedtls_poly1305_update(mbedtls_poly1305_context *ctx, + const unsigned char *input, + size_t ilen) +{ + size_t offset = 0U; + size_t remaining = ilen; + size_t queue_free_len; + size_t nblocks; + + if ((remaining > 0U) && (ctx->queue_len > 0U)) { + queue_free_len = (POLY1305_BLOCK_SIZE_BYTES - ctx->queue_len); + + if (ilen < queue_free_len) { + /* Not enough data to complete the block. + * Store this data with the other leftovers. + */ + memcpy(&ctx->queue[ctx->queue_len], + input, + ilen); + + ctx->queue_len += ilen; + + remaining = 0U; + } else { + /* Enough data to produce a complete block */ + memcpy(&ctx->queue[ctx->queue_len], + input, + queue_free_len); + + ctx->queue_len = 0U; + + poly1305_process(ctx, 1U, ctx->queue, 1U); /* add padding bit */ + + offset += queue_free_len; + remaining -= queue_free_len; + } + } + + if (remaining >= POLY1305_BLOCK_SIZE_BYTES) { + nblocks = remaining / POLY1305_BLOCK_SIZE_BYTES; + + poly1305_process(ctx, nblocks, &input[offset], 1U); + + offset += nblocks * POLY1305_BLOCK_SIZE_BYTES; + remaining %= POLY1305_BLOCK_SIZE_BYTES; + } + + if (remaining > 0U) { + /* Store partial block */ + ctx->queue_len = remaining; + memcpy(ctx->queue, &input[offset], remaining); + } + + return 0; +} + +int mbedtls_poly1305_finish(mbedtls_poly1305_context *ctx, + unsigned char mac[16]) +{ + /* Process any leftover data */ + if (ctx->queue_len > 0U) { + /* Add padding bit */ + ctx->queue[ctx->queue_len] = 1U; + ctx->queue_len++; + + /* Pad with zeroes */ + memset(&ctx->queue[ctx->queue_len], + 0, + POLY1305_BLOCK_SIZE_BYTES - ctx->queue_len); + + poly1305_process(ctx, 1U, /* Process 1 block */ + ctx->queue, 0U); /* Already padded above */ + } + + poly1305_compute_mac(ctx, mac); + + return 0; +} + +int mbedtls_poly1305_mac(const unsigned char key[32], + const unsigned char *input, + size_t ilen, + unsigned char mac[16]) +{ + mbedtls_poly1305_context ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_poly1305_init(&ctx); + + ret = mbedtls_poly1305_starts(&ctx, key); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_poly1305_update(&ctx, input, ilen); + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_poly1305_finish(&ctx, mac); + +cleanup: + mbedtls_poly1305_free(&ctx); + return ret; +} + +#endif /* MBEDTLS_POLY1305_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +static const unsigned char test_keys[2][32] = +{ + { + 0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33, + 0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 0x06, 0xa8, + 0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd, + 0x4a, 0xbf, 0xf6, 0xaf, 0x41, 0x49, 0xf5, 0x1b + }, + { + 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, + 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 0xb5, 0xf0, + 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, + 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0 + } +}; + +static const unsigned char test_data[2][127] = +{ + { + 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x67, 0x72, + 0x61, 0x70, 0x68, 0x69, 0x63, 0x20, 0x46, 0x6f, + 0x72, 0x75, 0x6d, 0x20, 0x52, 0x65, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6f, + 0x75, 0x70 + }, + { + 0x27, 0x54, 0x77, 0x61, 0x73, 0x20, 0x62, 0x72, + 0x69, 0x6c, 0x6c, 0x69, 0x67, 0x2c, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, + 0x6c, 0x69, 0x74, 0x68, 0x79, 0x20, 0x74, 0x6f, + 0x76, 0x65, 0x73, 0x0a, 0x44, 0x69, 0x64, 0x20, + 0x67, 0x79, 0x72, 0x65, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x67, 0x69, 0x6d, 0x62, 0x6c, 0x65, 0x20, + 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, + 0x61, 0x62, 0x65, 0x3a, 0x0a, 0x41, 0x6c, 0x6c, + 0x20, 0x6d, 0x69, 0x6d, 0x73, 0x79, 0x20, 0x77, + 0x65, 0x72, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x62, 0x6f, 0x72, 0x6f, 0x67, 0x6f, 0x76, 0x65, + 0x73, 0x2c, 0x0a, 0x41, 0x6e, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x6d, 0x6f, 0x6d, 0x65, 0x20, + 0x72, 0x61, 0x74, 0x68, 0x73, 0x20, 0x6f, 0x75, + 0x74, 0x67, 0x72, 0x61, 0x62, 0x65, 0x2e + } +}; + +static const size_t test_data_len[2] = +{ + 34U, + 127U +}; + +static const unsigned char test_mac[2][16] = +{ + { + 0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6, + 0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 0x27, 0xa9 + }, + { + 0x45, 0x41, 0x66, 0x9a, 0x7e, 0xaa, 0xee, 0x61, + 0xe7, 0x08, 0xdc, 0x7c, 0xbc, 0xc5, 0xeb, 0x62 + } +}; + +/* Make sure no other definition is already present. */ +#undef ASSERT + +#define ASSERT(cond, args) \ + do \ + { \ + if (!(cond)) \ + { \ + if (verbose != 0) \ + mbedtls_printf args; \ + \ + return -1; \ + } \ + } \ + while (0) + +int mbedtls_poly1305_self_test(int verbose) +{ + unsigned char mac[16]; + unsigned i; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + for (i = 0U; i < 2U; i++) { + if (verbose != 0) { + mbedtls_printf(" Poly1305 test %u ", i); + } + + ret = mbedtls_poly1305_mac(test_keys[i], + test_data[i], + test_data_len[i], + mac); + ASSERT(0 == ret, ("error code: %i\n", ret)); + + ASSERT(0 == memcmp(mac, test_mac[i], 16U), ("failed (mac)\n")); + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_POLY1305_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto.c b/r5dev/thirdparty/mbedtls/psa_crypto.c new file mode 100644 index 00000000..bc19ed07 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto.c @@ -0,0 +1,8049 @@ +/* + * PSA crypto layer on top of Mbed TLS crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) +#include "check_crypto_config.h" +#endif + +#include "psa/crypto.h" +#include "psa/crypto_values.h" + +#include "psa_crypto_cipher.h" +#include "psa_crypto_core.h" +#include "psa_crypto_invasive.h" +#include "psa_crypto_driver_wrappers.h" +#include "psa_crypto_ecp.h" +#include "psa_crypto_hash.h" +#include "psa_crypto_mac.h" +#include "psa_crypto_rsa.h" +#include "psa_crypto_ecp.h" +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +#include "psa_crypto_se.h" +#endif +#include "psa_crypto_slot_management.h" +/* Include internal declarations that are useful for implementing persistently + * stored keys. */ +#include "psa_crypto_storage.h" + +#include "psa_crypto_random_impl.h" + +#include +#include +#include "mbedtls/platform.h" + +#include "mbedtls/aes.h" +#include "mbedtls/asn1.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/bignum.h" +#include "mbedtls/camellia.h" +#include "mbedtls/chacha20.h" +#include "mbedtls/chachapoly.h" +#include "mbedtls/cipher.h" +#include "mbedtls/ccm.h" +#include "mbedtls/cmac.h" +#include "mbedtls/des.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecp.h" +#include "mbedtls/entropy.h" +#include "mbedtls/error.h" +#include "mbedtls/gcm.h" +#include "mbedtls/md5.h" +#include "mbedtls/md.h" +#include "md_wrap.h" +#include "mbedtls/pk.h" +#include "pk_wrap.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "mbedtls/ripemd160.h" +#include "mbedtls/rsa.h" +#include "mbedtls/sha1.h" +#include "mbedtls/sha256.h" +#include "mbedtls/sha512.h" +#include "hash_info.h" + +#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(*(array))) + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) +#define BUILTIN_ALG_ANY_HKDF 1 +#endif + +/* The only two JPAKE user/peer identifiers supported for the time being. */ +static const uint8_t jpake_server_id[] = { 's', 'e', 'r', 'v', 'e', 'r' }; +static const uint8_t jpake_client_id[] = { 'c', 'l', 'i', 'e', 'n', 't' }; + +/****************************************************************/ +/* Global data, support functions and library management */ +/****************************************************************/ + +static int key_type_is_raw_bytes(psa_key_type_t type) +{ + return PSA_KEY_TYPE_IS_UNSTRUCTURED(type); +} + +/* Values for psa_global_data_t::rng_state */ +#define RNG_NOT_INITIALIZED 0 +#define RNG_INITIALIZED 1 +#define RNG_SEEDED 2 + +typedef struct { + unsigned initialized : 1; + unsigned rng_state : 2; + unsigned drivers_initialized : 1; + mbedtls_psa_random_context_t rng; +} psa_global_data_t; + +static psa_global_data_t global_data; + +#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = + &global_data.rng.drbg; +#endif + +#define GUARD_MODULE_INITIALIZED \ + if (global_data.initialized == 0) \ + return PSA_ERROR_BAD_STATE; + +int psa_can_do_hash(psa_algorithm_t hash_alg) +{ + (void) hash_alg; + return global_data.drivers_initialized; +} + +psa_status_t mbedtls_to_psa_error(int ret) +{ + /* Mbed TLS error codes can combine a high-level error code and a + * low-level error code. The low-level error usually reflects the + * root cause better, so dispatch on that preferably. */ + int low_level_ret = -(-ret & 0x007f); + switch (low_level_ret != 0 ? low_level_ret : ret) { + case 0: + return PSA_SUCCESS; + + case MBEDTLS_ERR_AES_INVALID_KEY_LENGTH: + case MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_ASN1_OUT_OF_DATA: + case MBEDTLS_ERR_ASN1_UNEXPECTED_TAG: + case MBEDTLS_ERR_ASN1_INVALID_LENGTH: + case MBEDTLS_ERR_ASN1_LENGTH_MISMATCH: + case MBEDTLS_ERR_ASN1_INVALID_DATA: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_ASN1_ALLOC_FAILED: + return PSA_ERROR_INSUFFICIENT_MEMORY; + case MBEDTLS_ERR_ASN1_BUF_TOO_SMALL: + return PSA_ERROR_BUFFER_TOO_SMALL; + +#if defined(MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA) + case MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA: +#endif + case MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH: + return PSA_ERROR_NOT_SUPPORTED; + + case MBEDTLS_ERR_CCM_BAD_INPUT: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_CCM_AUTH_FAILED: + return PSA_ERROR_INVALID_SIGNATURE; + + case MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA: + return PSA_ERROR_INVALID_ARGUMENT; + + case MBEDTLS_ERR_CHACHAPOLY_BAD_STATE: + return PSA_ERROR_BAD_STATE; + case MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED: + return PSA_ERROR_INVALID_SIGNATURE; + + case MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_CIPHER_ALLOC_FAILED: + return PSA_ERROR_INSUFFICIENT_MEMORY; + case MBEDTLS_ERR_CIPHER_INVALID_PADDING: + return PSA_ERROR_INVALID_PADDING; + case MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_CIPHER_AUTH_FAILED: + return PSA_ERROR_INVALID_SIGNATURE; + case MBEDTLS_ERR_CIPHER_INVALID_CONTEXT: + return PSA_ERROR_CORRUPTION_DETECTED; + +#if !(defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) || \ + defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE)) + /* Only check CTR_DRBG error codes if underlying mbedtls_xxx + * functions are passed a CTR_DRBG instance. */ + case MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED: + return PSA_ERROR_INSUFFICIENT_ENTROPY; + case MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG: + case MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR: + return PSA_ERROR_INSUFFICIENT_ENTROPY; +#endif + + case MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH: + return PSA_ERROR_NOT_SUPPORTED; + + case MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED: + case MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE: + case MBEDTLS_ERR_ENTROPY_SOURCE_FAILED: + return PSA_ERROR_INSUFFICIENT_ENTROPY; + + case MBEDTLS_ERR_GCM_AUTH_FAILED: + return PSA_ERROR_INVALID_SIGNATURE; + case MBEDTLS_ERR_GCM_BUFFER_TOO_SMALL: + return PSA_ERROR_BUFFER_TOO_SMALL; + case MBEDTLS_ERR_GCM_BAD_INPUT: + return PSA_ERROR_INVALID_ARGUMENT; + +#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) && \ + defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE) + /* Only check HMAC_DRBG error codes if underlying mbedtls_xxx + * functions are passed a HMAC_DRBG instance. */ + case MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED: + return PSA_ERROR_INSUFFICIENT_ENTROPY; + case MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG: + case MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR: + return PSA_ERROR_INSUFFICIENT_ENTROPY; +#endif + + case MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_MD_BAD_INPUT_DATA: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_MD_ALLOC_FAILED: + return PSA_ERROR_INSUFFICIENT_MEMORY; + case MBEDTLS_ERR_MD_FILE_IO_ERROR: + return PSA_ERROR_STORAGE_FAILURE; + + case MBEDTLS_ERR_MPI_FILE_IO_ERROR: + return PSA_ERROR_STORAGE_FAILURE; + case MBEDTLS_ERR_MPI_BAD_INPUT_DATA: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_MPI_INVALID_CHARACTER: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL: + return PSA_ERROR_BUFFER_TOO_SMALL; + case MBEDTLS_ERR_MPI_NEGATIVE_VALUE: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_MPI_DIVISION_BY_ZERO: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_MPI_NOT_ACCEPTABLE: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_MPI_ALLOC_FAILED: + return PSA_ERROR_INSUFFICIENT_MEMORY; + + case MBEDTLS_ERR_PK_ALLOC_FAILED: + return PSA_ERROR_INSUFFICIENT_MEMORY; + case MBEDTLS_ERR_PK_TYPE_MISMATCH: + case MBEDTLS_ERR_PK_BAD_INPUT_DATA: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_PK_FILE_IO_ERROR: + return PSA_ERROR_STORAGE_FAILURE; + case MBEDTLS_ERR_PK_KEY_INVALID_VERSION: + case MBEDTLS_ERR_PK_KEY_INVALID_FORMAT: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_PK_UNKNOWN_PK_ALG: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_PK_PASSWORD_REQUIRED: + case MBEDTLS_ERR_PK_PASSWORD_MISMATCH: + return PSA_ERROR_NOT_PERMITTED; + case MBEDTLS_ERR_PK_INVALID_PUBKEY: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_PK_INVALID_ALG: + case MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE: + case MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_PK_SIG_LEN_MISMATCH: + return PSA_ERROR_INVALID_SIGNATURE; + case MBEDTLS_ERR_PK_BUFFER_TOO_SMALL: + return PSA_ERROR_BUFFER_TOO_SMALL; + + case MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED: + return PSA_ERROR_HARDWARE_FAILURE; + case MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED: + return PSA_ERROR_NOT_SUPPORTED; + + case MBEDTLS_ERR_RSA_BAD_INPUT_DATA: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_RSA_INVALID_PADDING: + return PSA_ERROR_INVALID_PADDING; + case MBEDTLS_ERR_RSA_KEY_GEN_FAILED: + return PSA_ERROR_HARDWARE_FAILURE; + case MBEDTLS_ERR_RSA_KEY_CHECK_FAILED: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_RSA_PUBLIC_FAILED: + case MBEDTLS_ERR_RSA_PRIVATE_FAILED: + return PSA_ERROR_CORRUPTION_DETECTED; + case MBEDTLS_ERR_RSA_VERIFY_FAILED: + return PSA_ERROR_INVALID_SIGNATURE; + case MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE: + return PSA_ERROR_BUFFER_TOO_SMALL; + case MBEDTLS_ERR_RSA_RNG_FAILED: + return PSA_ERROR_INSUFFICIENT_ENTROPY; + + case MBEDTLS_ERR_ECP_BAD_INPUT_DATA: + case MBEDTLS_ERR_ECP_INVALID_KEY: + return PSA_ERROR_INVALID_ARGUMENT; + case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL: + return PSA_ERROR_BUFFER_TOO_SMALL; + case MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH: + case MBEDTLS_ERR_ECP_VERIFY_FAILED: + return PSA_ERROR_INVALID_SIGNATURE; + case MBEDTLS_ERR_ECP_ALLOC_FAILED: + return PSA_ERROR_INSUFFICIENT_MEMORY; + case MBEDTLS_ERR_ECP_RANDOM_FAILED: + return PSA_ERROR_INSUFFICIENT_ENTROPY; + + case MBEDTLS_ERR_ECP_IN_PROGRESS: + return PSA_OPERATION_INCOMPLETE; + + case MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED: + return PSA_ERROR_CORRUPTION_DETECTED; + + default: + return PSA_ERROR_GENERIC_ERROR; + } +} + +/** + * \brief For output buffers which contain "tags" + * (outputs that may be checked for validity like + * hashes, MACs and signatures), fill the unused + * part of the output buffer (the whole buffer on + * error, the trailing part on success) with + * something that isn't a valid tag (barring an + * attack on the tag and deliberately-crafted + * input), in case the caller doesn't check the + * return status properly. + * + * \param output_buffer Pointer to buffer to wipe. May not be NULL + * unless \p output_buffer_size is zero. + * \param status Status of function called to generate + * output_buffer originally + * \param output_buffer_size Size of output buffer. If zero, \p output_buffer + * could be NULL. + * \param output_buffer_length Length of data written to output_buffer, must be + * less than \p output_buffer_size + */ +static void psa_wipe_tag_output_buffer(uint8_t *output_buffer, psa_status_t status, + size_t output_buffer_size, size_t output_buffer_length) +{ + size_t offset = 0; + + if (output_buffer_size == 0) { + /* If output_buffer_size is 0 then we have nothing to do. We must not + call memset because output_buffer may be NULL in this case */ + return; + } + + if (status == PSA_SUCCESS) { + offset = output_buffer_length; + } + + memset(output_buffer + offset, '!', output_buffer_size - offset); +} + + + + +/****************************************************************/ +/* Key management */ +/****************************************************************/ + +#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) +mbedtls_ecp_group_id mbedtls_ecc_group_of_psa(psa_ecc_family_t curve, + size_t bits, + int bits_is_sloppy) +{ + switch (curve) { + case PSA_ECC_FAMILY_SECP_R1: + switch (bits) { +#if defined(PSA_WANT_ECC_SECP_R1_192) + case 192: + return MBEDTLS_ECP_DP_SECP192R1; +#endif +#if defined(PSA_WANT_ECC_SECP_R1_224) + case 224: + return MBEDTLS_ECP_DP_SECP224R1; +#endif +#if defined(PSA_WANT_ECC_SECP_R1_256) + case 256: + return MBEDTLS_ECP_DP_SECP256R1; +#endif +#if defined(PSA_WANT_ECC_SECP_R1_384) + case 384: + return MBEDTLS_ECP_DP_SECP384R1; +#endif +#if defined(PSA_WANT_ECC_SECP_R1_521) + case 521: + return MBEDTLS_ECP_DP_SECP521R1; + case 528: + if (bits_is_sloppy) { + return MBEDTLS_ECP_DP_SECP521R1; + } + break; +#endif + } + break; + + case PSA_ECC_FAMILY_BRAINPOOL_P_R1: + switch (bits) { +#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_256) + case 256: + return MBEDTLS_ECP_DP_BP256R1; +#endif +#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384) + case 384: + return MBEDTLS_ECP_DP_BP384R1; +#endif +#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_512) + case 512: + return MBEDTLS_ECP_DP_BP512R1; +#endif + } + break; + + case PSA_ECC_FAMILY_MONTGOMERY: + switch (bits) { +#if defined(PSA_WANT_ECC_MONTGOMERY_255) + case 255: + return MBEDTLS_ECP_DP_CURVE25519; + case 256: + if (bits_is_sloppy) { + return MBEDTLS_ECP_DP_CURVE25519; + } + break; +#endif +#if defined(PSA_WANT_ECC_MONTGOMERY_448) + case 448: + return MBEDTLS_ECP_DP_CURVE448; +#endif + } + break; + + case PSA_ECC_FAMILY_SECP_K1: + switch (bits) { +#if defined(PSA_WANT_ECC_SECP_K1_192) + case 192: + return MBEDTLS_ECP_DP_SECP192K1; +#endif +#if defined(PSA_WANT_ECC_SECP_K1_224) + case 224: + return MBEDTLS_ECP_DP_SECP224K1; +#endif +#if defined(PSA_WANT_ECC_SECP_K1_256) + case 256: + return MBEDTLS_ECP_DP_SECP256K1; +#endif + } + break; + } + + (void) bits_is_sloppy; + return MBEDTLS_ECP_DP_NONE; +} +#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) || + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) */ + +psa_status_t psa_validate_unstructured_key_bit_size(psa_key_type_t type, + size_t bits) +{ + /* Check that the bit size is acceptable for the key type */ + switch (type) { + case PSA_KEY_TYPE_RAW_DATA: + case PSA_KEY_TYPE_HMAC: + case PSA_KEY_TYPE_DERIVE: + case PSA_KEY_TYPE_PASSWORD: + case PSA_KEY_TYPE_PASSWORD_HASH: + break; +#if defined(PSA_WANT_KEY_TYPE_AES) + case PSA_KEY_TYPE_AES: + if (bits != 128 && bits != 192 && bits != 256) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif +#if defined(PSA_WANT_KEY_TYPE_ARIA) + case PSA_KEY_TYPE_ARIA: + if (bits != 128 && bits != 192 && bits != 256) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif +#if defined(PSA_WANT_KEY_TYPE_CAMELLIA) + case PSA_KEY_TYPE_CAMELLIA: + if (bits != 128 && bits != 192 && bits != 256) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif +#if defined(PSA_WANT_KEY_TYPE_DES) + case PSA_KEY_TYPE_DES: + if (bits != 64 && bits != 128 && bits != 192) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif +#if defined(PSA_WANT_KEY_TYPE_CHACHA20) + case PSA_KEY_TYPE_CHACHA20: + if (bits != 256) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif + default: + return PSA_ERROR_NOT_SUPPORTED; + } + if (bits % 8 != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return PSA_SUCCESS; +} + +/** Check whether a given key type is valid for use with a given MAC algorithm + * + * Upon successful return of this function, the behavior of #PSA_MAC_LENGTH + * when called with the validated \p algorithm and \p key_type is well-defined. + * + * \param[in] algorithm The specific MAC algorithm (can be wildcard). + * \param[in] key_type The key type of the key to be used with the + * \p algorithm. + * + * \retval #PSA_SUCCESS + * The \p key_type is valid for use with the \p algorithm + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The \p key_type is not valid for use with the \p algorithm + */ +MBEDTLS_STATIC_TESTABLE psa_status_t psa_mac_key_can_do( + psa_algorithm_t algorithm, + psa_key_type_t key_type) +{ + if (PSA_ALG_IS_HMAC(algorithm)) { + if (key_type == PSA_KEY_TYPE_HMAC) { + return PSA_SUCCESS; + } + } + + if (PSA_ALG_IS_BLOCK_CIPHER_MAC(algorithm)) { + /* Check that we're calling PSA_BLOCK_CIPHER_BLOCK_LENGTH with a cipher + * key. */ + if ((key_type & PSA_KEY_TYPE_CATEGORY_MASK) == + PSA_KEY_TYPE_CATEGORY_SYMMETRIC) { + /* PSA_BLOCK_CIPHER_BLOCK_LENGTH returns 1 for stream ciphers and + * the block length (larger than 1) for block ciphers. */ + if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) > 1) { + return PSA_SUCCESS; + } + } + } + + return PSA_ERROR_INVALID_ARGUMENT; +} + +psa_status_t psa_allocate_buffer_to_slot(psa_key_slot_t *slot, + size_t buffer_length) +{ + if (slot->key.data != NULL) { + return PSA_ERROR_ALREADY_EXISTS; + } + + slot->key.data = mbedtls_calloc(1, buffer_length); + if (slot->key.data == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + slot->key.bytes = buffer_length; + return PSA_SUCCESS; +} + +psa_status_t psa_copy_key_material_into_slot(psa_key_slot_t *slot, + const uint8_t *data, + size_t data_length) +{ + psa_status_t status = psa_allocate_buffer_to_slot(slot, + data_length); + if (status != PSA_SUCCESS) { + return status; + } + + memcpy(slot->key.data, data, data_length); + return PSA_SUCCESS; +} + +psa_status_t psa_import_key_into_slot( + const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + uint8_t *key_buffer, size_t key_buffer_size, + size_t *key_buffer_length, size_t *bits) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_type_t type = attributes->core.type; + + /* zero-length keys are never supported. */ + if (data_length == 0) { + return PSA_ERROR_NOT_SUPPORTED; + } + + if (key_type_is_raw_bytes(type)) { + *bits = PSA_BYTES_TO_BITS(data_length); + + status = psa_validate_unstructured_key_bit_size(attributes->core.type, + *bits); + if (status != PSA_SUCCESS) { + return status; + } + + /* Copy the key material. */ + memcpy(key_buffer, data, data_length); + *key_buffer_length = data_length; + (void) key_buffer_size; + + return PSA_SUCCESS; + } else if (PSA_KEY_TYPE_IS_ASYMMETRIC(type)) { +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) + if (PSA_KEY_TYPE_IS_ECC(type)) { + return mbedtls_psa_ecp_import_key(attributes, + data, data_length, + key_buffer, key_buffer_size, + key_buffer_length, + bits); + } +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */ +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) + if (PSA_KEY_TYPE_IS_RSA(type)) { + return mbedtls_psa_rsa_import_key(attributes, + data, data_length, + key_buffer, key_buffer_size, + key_buffer_length, + bits); + } +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */ + } + + return PSA_ERROR_NOT_SUPPORTED; +} + +/** Calculate the intersection of two algorithm usage policies. + * + * Return 0 (which allows no operation) on incompatibility. + */ +static psa_algorithm_t psa_key_policy_algorithm_intersection( + psa_key_type_t key_type, + psa_algorithm_t alg1, + psa_algorithm_t alg2) +{ + /* Common case: both sides actually specify the same policy. */ + if (alg1 == alg2) { + return alg1; + } + /* If the policies are from the same hash-and-sign family, check + * if one is a wildcard. If so the other has the specific algorithm. */ + if (PSA_ALG_IS_SIGN_HASH(alg1) && + PSA_ALG_IS_SIGN_HASH(alg2) && + (alg1 & ~PSA_ALG_HASH_MASK) == (alg2 & ~PSA_ALG_HASH_MASK)) { + if (PSA_ALG_SIGN_GET_HASH(alg1) == PSA_ALG_ANY_HASH) { + return alg2; + } + if (PSA_ALG_SIGN_GET_HASH(alg2) == PSA_ALG_ANY_HASH) { + return alg1; + } + } + /* If the policies are from the same AEAD family, check whether + * one of them is a minimum-tag-length wildcard. Calculate the most + * restrictive tag length. */ + if (PSA_ALG_IS_AEAD(alg1) && PSA_ALG_IS_AEAD(alg2) && + (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg1, 0) == + PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg2, 0))) { + size_t alg1_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg1); + size_t alg2_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg2); + size_t restricted_len = alg1_len > alg2_len ? alg1_len : alg2_len; + + /* If both are wildcards, return most restrictive wildcard */ + if (((alg1 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) && + ((alg2 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0)) { + return PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG( + alg1, restricted_len); + } + /* If only one is a wildcard, return specific algorithm if compatible. */ + if (((alg1 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) && + (alg1_len <= alg2_len)) { + return alg2; + } + if (((alg2 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) && + (alg2_len <= alg1_len)) { + return alg1; + } + } + /* If the policies are from the same MAC family, check whether one + * of them is a minimum-MAC-length policy. Calculate the most + * restrictive tag length. */ + if (PSA_ALG_IS_MAC(alg1) && PSA_ALG_IS_MAC(alg2) && + (PSA_ALG_FULL_LENGTH_MAC(alg1) == + PSA_ALG_FULL_LENGTH_MAC(alg2))) { + /* Validate the combination of key type and algorithm. Since the base + * algorithm of alg1 and alg2 are the same, we only need this once. */ + if (PSA_SUCCESS != psa_mac_key_can_do(alg1, key_type)) { + return 0; + } + + /* Get the (exact or at-least) output lengths for both sides of the + * requested intersection. None of the currently supported algorithms + * have an output length dependent on the actual key size, so setting it + * to a bogus value of 0 is currently OK. + * + * Note that for at-least-this-length wildcard algorithms, the output + * length is set to the shortest allowed length, which allows us to + * calculate the most restrictive tag length for the intersection. */ + size_t alg1_len = PSA_MAC_LENGTH(key_type, 0, alg1); + size_t alg2_len = PSA_MAC_LENGTH(key_type, 0, alg2); + size_t restricted_len = alg1_len > alg2_len ? alg1_len : alg2_len; + + /* If both are wildcards, return most restrictive wildcard */ + if (((alg1 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) && + ((alg2 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0)) { + return PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(alg1, restricted_len); + } + + /* If only one is an at-least-this-length policy, the intersection would + * be the other (fixed-length) policy as long as said fixed length is + * equal to or larger than the shortest allowed length. */ + if ((alg1 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + return (alg1_len <= alg2_len) ? alg2 : 0; + } + if ((alg2 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + return (alg2_len <= alg1_len) ? alg1 : 0; + } + + /* If none of them are wildcards, check whether they define the same tag + * length. This is still possible here when one is default-length and + * the other specific-length. Ensure to always return the + * specific-length version for the intersection. */ + if (alg1_len == alg2_len) { + return PSA_ALG_TRUNCATED_MAC(alg1, alg1_len); + } + } + /* If the policies are incompatible, allow nothing. */ + return 0; +} + +static int psa_key_algorithm_permits(psa_key_type_t key_type, + psa_algorithm_t policy_alg, + psa_algorithm_t requested_alg) +{ + /* Common case: the policy only allows requested_alg. */ + if (requested_alg == policy_alg) { + return 1; + } + /* If policy_alg is a hash-and-sign with a wildcard for the hash, + * and requested_alg is the same hash-and-sign family with any hash, + * then requested_alg is compliant with policy_alg. */ + if (PSA_ALG_IS_SIGN_HASH(requested_alg) && + PSA_ALG_SIGN_GET_HASH(policy_alg) == PSA_ALG_ANY_HASH) { + return (policy_alg & ~PSA_ALG_HASH_MASK) == + (requested_alg & ~PSA_ALG_HASH_MASK); + } + /* If policy_alg is a wildcard AEAD algorithm of the same base as + * the requested algorithm, check the requested tag length to be + * equal-length or longer than the wildcard-specified length. */ + if (PSA_ALG_IS_AEAD(policy_alg) && + PSA_ALG_IS_AEAD(requested_alg) && + (PSA_ALG_AEAD_WITH_SHORTENED_TAG(policy_alg, 0) == + PSA_ALG_AEAD_WITH_SHORTENED_TAG(requested_alg, 0)) && + ((policy_alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0)) { + return PSA_ALG_AEAD_GET_TAG_LENGTH(policy_alg) <= + PSA_ALG_AEAD_GET_TAG_LENGTH(requested_alg); + } + /* If policy_alg is a MAC algorithm of the same base as the requested + * algorithm, check whether their MAC lengths are compatible. */ + if (PSA_ALG_IS_MAC(policy_alg) && + PSA_ALG_IS_MAC(requested_alg) && + (PSA_ALG_FULL_LENGTH_MAC(policy_alg) == + PSA_ALG_FULL_LENGTH_MAC(requested_alg))) { + /* Validate the combination of key type and algorithm. Since the policy + * and requested algorithms are the same, we only need this once. */ + if (PSA_SUCCESS != psa_mac_key_can_do(policy_alg, key_type)) { + return 0; + } + + /* Get both the requested output length for the algorithm which is to be + * verified, and the default output length for the base algorithm. + * Note that none of the currently supported algorithms have an output + * length dependent on actual key size, so setting it to a bogus value + * of 0 is currently OK. */ + size_t requested_output_length = PSA_MAC_LENGTH( + key_type, 0, requested_alg); + size_t default_output_length = PSA_MAC_LENGTH( + key_type, 0, + PSA_ALG_FULL_LENGTH_MAC(requested_alg)); + + /* If the policy is default-length, only allow an algorithm with + * a declared exact-length matching the default. */ + if (PSA_MAC_TRUNCATED_LENGTH(policy_alg) == 0) { + return requested_output_length == default_output_length; + } + + /* If the requested algorithm is default-length, allow it if the policy + * length exactly matches the default length. */ + if (PSA_MAC_TRUNCATED_LENGTH(requested_alg) == 0 && + PSA_MAC_TRUNCATED_LENGTH(policy_alg) == default_output_length) { + return 1; + } + + /* If policy_alg is an at-least-this-length wildcard MAC algorithm, + * check for the requested MAC length to be equal to or longer than the + * minimum allowed length. */ + if ((policy_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + return PSA_MAC_TRUNCATED_LENGTH(policy_alg) <= + requested_output_length; + } + } + /* If policy_alg is a generic key agreement operation, then using it for + * a key derivation with that key agreement should also be allowed. This + * behaviour is expected to be defined in a future specification version. */ + if (PSA_ALG_IS_RAW_KEY_AGREEMENT(policy_alg) && + PSA_ALG_IS_KEY_AGREEMENT(requested_alg)) { + return PSA_ALG_KEY_AGREEMENT_GET_BASE(requested_alg) == + policy_alg; + } + /* If it isn't explicitly permitted, it's forbidden. */ + return 0; +} + +/** Test whether a policy permits an algorithm. + * + * The caller must test usage flags separately. + * + * \note This function requires providing the key type for which the policy is + * being validated, since some algorithm policy definitions (e.g. MAC) + * have different properties depending on what kind of cipher it is + * combined with. + * + * \retval PSA_SUCCESS When \p alg is a specific algorithm + * allowed by the \p policy. + * \retval PSA_ERROR_INVALID_ARGUMENT When \p alg is not a specific algorithm + * \retval PSA_ERROR_NOT_PERMITTED When \p alg is a specific algorithm, but + * the \p policy does not allow it. + */ +static psa_status_t psa_key_policy_permits(const psa_key_policy_t *policy, + psa_key_type_t key_type, + psa_algorithm_t alg) +{ + /* '0' is not a valid algorithm */ + if (alg == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* A requested algorithm cannot be a wildcard. */ + if (PSA_ALG_IS_WILDCARD(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (psa_key_algorithm_permits(key_type, policy->alg, alg) || + psa_key_algorithm_permits(key_type, policy->alg2, alg)) { + return PSA_SUCCESS; + } else { + return PSA_ERROR_NOT_PERMITTED; + } +} + +/** Restrict a key policy based on a constraint. + * + * \note This function requires providing the key type for which the policy is + * being restricted, since some algorithm policy definitions (e.g. MAC) + * have different properties depending on what kind of cipher it is + * combined with. + * + * \param[in] key_type The key type for which to restrict the policy + * \param[in,out] policy The policy to restrict. + * \param[in] constraint The policy constraint to apply. + * + * \retval #PSA_SUCCESS + * \c *policy contains the intersection of the original value of + * \c *policy and \c *constraint. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c key_type, \c *policy and \c *constraint are incompatible. + * \c *policy is unchanged. + */ +static psa_status_t psa_restrict_key_policy( + psa_key_type_t key_type, + psa_key_policy_t *policy, + const psa_key_policy_t *constraint) +{ + psa_algorithm_t intersection_alg = + psa_key_policy_algorithm_intersection(key_type, policy->alg, + constraint->alg); + psa_algorithm_t intersection_alg2 = + psa_key_policy_algorithm_intersection(key_type, policy->alg2, + constraint->alg2); + if (intersection_alg == 0 && policy->alg != 0 && constraint->alg != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (intersection_alg2 == 0 && policy->alg2 != 0 && constraint->alg2 != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + policy->usage &= constraint->usage; + policy->alg = intersection_alg; + policy->alg2 = intersection_alg2; + return PSA_SUCCESS; +} + +/** Get the description of a key given its identifier and policy constraints + * and lock it. + * + * The key must have allow all the usage flags set in \p usage. If \p alg is + * nonzero, the key must allow operations with this algorithm. If \p alg is + * zero, the algorithm is not checked. + * + * In case of a persistent key, the function loads the description of the key + * into a key slot if not already done. + * + * On success, the returned key slot is locked. It is the responsibility of + * the caller to unlock the key slot when it does not access it anymore. + */ +static psa_status_t psa_get_and_lock_key_slot_with_policy( + mbedtls_svc_key_id_t key, + psa_key_slot_t **p_slot, + psa_key_usage_t usage, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + + status = psa_get_and_lock_key_slot(key, p_slot); + if (status != PSA_SUCCESS) { + return status; + } + slot = *p_slot; + + /* Enforce that usage policy for the key slot contains all the flags + * required by the usage parameter. There is one exception: public + * keys can always be exported, so we treat public key objects as + * if they had the export flag. */ + if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) { + usage &= ~PSA_KEY_USAGE_EXPORT; + } + + if ((slot->attr.policy.usage & usage) != usage) { + status = PSA_ERROR_NOT_PERMITTED; + goto error; + } + + /* Enforce that the usage policy permits the requested algorithm. */ + if (alg != 0) { + status = psa_key_policy_permits(&slot->attr.policy, + slot->attr.type, + alg); + if (status != PSA_SUCCESS) { + goto error; + } + } + + return PSA_SUCCESS; + +error: + *p_slot = NULL; + psa_unlock_key_slot(slot); + + return status; +} + +/** Get a key slot containing a transparent key and lock it. + * + * A transparent key is a key for which the key material is directly + * available, as opposed to a key in a secure element and/or to be used + * by a secure element. + * + * This is a temporary function that may be used instead of + * psa_get_and_lock_key_slot_with_policy() when there is no opaque key support + * for a cryptographic operation. + * + * On success, the returned key slot is locked. It is the responsibility of the + * caller to unlock the key slot when it does not access it anymore. + */ +static psa_status_t psa_get_and_lock_transparent_key_slot_with_policy( + mbedtls_svc_key_id_t key, + psa_key_slot_t **p_slot, + psa_key_usage_t usage, + psa_algorithm_t alg) +{ + psa_status_t status = psa_get_and_lock_key_slot_with_policy(key, p_slot, + usage, alg); + if (status != PSA_SUCCESS) { + return status; + } + + if (psa_key_lifetime_is_external((*p_slot)->attr.lifetime)) { + psa_unlock_key_slot(*p_slot); + *p_slot = NULL; + return PSA_ERROR_NOT_SUPPORTED; + } + + return PSA_SUCCESS; +} + +psa_status_t psa_remove_key_data_from_memory(psa_key_slot_t *slot) +{ + /* Data pointer will always be either a valid pointer or NULL in an + * initialized slot, so we can just free it. */ + if (slot->key.data != NULL) { + mbedtls_platform_zeroize(slot->key.data, slot->key.bytes); + } + + mbedtls_free(slot->key.data); + slot->key.data = NULL; + slot->key.bytes = 0; + + return PSA_SUCCESS; +} + +/** Completely wipe a slot in memory, including its policy. + * Persistent storage is not affected. */ +psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot) +{ + psa_status_t status = psa_remove_key_data_from_memory(slot); + + /* + * As the return error code may not be handled in case of multiple errors, + * do our best to report an unexpected lock counter. Assert with + * MBEDTLS_TEST_HOOK_TEST_ASSERT that the lock counter is equal to one: + * if the MBEDTLS_TEST_HOOKS configuration option is enabled and the + * function is called as part of the execution of a test suite, the + * execution of the test suite is stopped in error if the assertion fails. + */ + if (slot->lock_count != 1) { + MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->lock_count == 1); + status = PSA_ERROR_CORRUPTION_DETECTED; + } + + /* Multipart operations may still be using the key. This is safe + * because all multipart operation objects are independent from + * the key slot: if they need to access the key after the setup + * phase, they have a copy of the key. Note that this means that + * key material can linger until all operations are completed. */ + /* At this point, key material and other type-specific content has + * been wiped. Clear remaining metadata. We can call memset and not + * zeroize because the metadata is not particularly sensitive. */ + memset(slot, 0, sizeof(*slot)); + return status; +} + +psa_status_t psa_destroy_key(mbedtls_svc_key_id_t key) +{ + psa_key_slot_t *slot; + psa_status_t status; /* status of the last operation */ + psa_status_t overall_status = PSA_SUCCESS; +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + psa_se_drv_table_entry_t *driver; +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + if (mbedtls_svc_key_id_is_null(key)) { + return PSA_SUCCESS; + } + + /* + * Get the description of the key in a key slot. In case of a persistent + * key, this will load the key description from persistent memory if not + * done yet. We cannot avoid this loading as without it we don't know if + * the key is operated by an SE or not and this information is needed by + * the current implementation. + */ + status = psa_get_and_lock_key_slot(key, &slot); + if (status != PSA_SUCCESS) { + return status; + } + + /* + * If the key slot containing the key description is under access by the + * library (apart from the present access), the key cannot be destroyed + * yet. For the time being, just return in error. Eventually (to be + * implemented), the key should be destroyed when all accesses have + * stopped. + */ + if (slot->lock_count > 1) { + psa_unlock_key_slot(slot); + return PSA_ERROR_GENERIC_ERROR; + } + + if (PSA_KEY_LIFETIME_IS_READ_ONLY(slot->attr.lifetime)) { + /* Refuse the destruction of a read-only key (which may or may not work + * if we attempt it, depending on whether the key is merely read-only + * by policy or actually physically read-only). + * Just do the best we can, which is to wipe the copy in memory + * (done in this function's cleanup code). */ + overall_status = PSA_ERROR_NOT_PERMITTED; + goto exit; + } + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + driver = psa_get_se_driver_entry(slot->attr.lifetime); + if (driver != NULL) { + /* For a key in a secure element, we need to do three things: + * remove the key file in internal storage, destroy the + * key inside the secure element, and update the driver's + * persistent data. Start a transaction that will encompass these + * three actions. */ + psa_crypto_prepare_transaction(PSA_CRYPTO_TRANSACTION_DESTROY_KEY); + psa_crypto_transaction.key.lifetime = slot->attr.lifetime; + psa_crypto_transaction.key.slot = psa_key_slot_get_slot_number(slot); + psa_crypto_transaction.key.id = slot->attr.id; + status = psa_crypto_save_transaction(); + if (status != PSA_SUCCESS) { + (void) psa_crypto_stop_transaction(); + /* We should still try to destroy the key in the secure + * element and the key metadata in storage. This is especially + * important if the error is that the storage is full. + * But how to do it exactly without risking an inconsistent + * state after a reset? + * https://github.com/ARMmbed/mbed-crypto/issues/215 + */ + overall_status = status; + goto exit; + } + + status = psa_destroy_se_key(driver, + psa_key_slot_get_slot_number(slot)); + if (overall_status == PSA_SUCCESS) { + overall_status = status; + } + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) + if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) { + status = psa_destroy_persistent_key(slot->attr.id); + if (overall_status == PSA_SUCCESS) { + overall_status = status; + } + + /* TODO: other slots may have a copy of the same key. We should + * invalidate them. + * https://github.com/ARMmbed/mbed-crypto/issues/214 + */ + } +#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */ + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + if (driver != NULL) { + status = psa_save_se_persistent_data(driver); + if (overall_status == PSA_SUCCESS) { + overall_status = status; + } + status = psa_crypto_stop_transaction(); + if (overall_status == PSA_SUCCESS) { + overall_status = status; + } + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +exit: + status = psa_wipe_key_slot(slot); + /* Prioritize CORRUPTION_DETECTED from wiping over a storage error */ + if (status != PSA_SUCCESS) { + overall_status = status; + } + return overall_status; +} + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) +static psa_status_t psa_get_rsa_public_exponent( + const mbedtls_rsa_context *rsa, + psa_key_attributes_t *attributes) +{ + mbedtls_mpi mpi; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + uint8_t *buffer = NULL; + size_t buflen; + mbedtls_mpi_init(&mpi); + + ret = mbedtls_rsa_export(rsa, NULL, NULL, NULL, NULL, &mpi); + if (ret != 0) { + goto exit; + } + if (mbedtls_mpi_cmp_int(&mpi, 65537) == 0) { + /* It's the default value, which is reported as an empty string, + * so there's nothing to do. */ + goto exit; + } + + buflen = mbedtls_mpi_size(&mpi); + buffer = mbedtls_calloc(1, buflen); + if (buffer == NULL) { + ret = MBEDTLS_ERR_MPI_ALLOC_FAILED; + goto exit; + } + ret = mbedtls_mpi_write_binary(&mpi, buffer, buflen); + if (ret != 0) { + goto exit; + } + attributes->domain_parameters = buffer; + attributes->domain_parameters_size = buflen; + +exit: + mbedtls_mpi_free(&mpi); + if (ret != 0) { + mbedtls_free(buffer); + } + return mbedtls_to_psa_error(ret); +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */ + +/** Retrieve all the publicly-accessible attributes of a key. + */ +psa_status_t psa_get_key_attributes(mbedtls_svc_key_id_t key, + psa_key_attributes_t *attributes) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + psa_reset_key_attributes(attributes); + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, 0, 0); + if (status != PSA_SUCCESS) { + return status; + } + + attributes->core = slot->attr; + attributes->core.flags &= (MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY | + MBEDTLS_PSA_KA_MASK_DUAL_USE); + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + if (psa_get_se_driver_entry(slot->attr.lifetime) != NULL) { + psa_set_key_slot_number(attributes, + psa_key_slot_get_slot_number(slot)); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + switch (slot->attr.type) { +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) + case PSA_KEY_TYPE_RSA_KEY_PAIR: + case PSA_KEY_TYPE_RSA_PUBLIC_KEY: + /* TODO: reporting the public exponent for opaque keys + * is not yet implemented. + * https://github.com/ARMmbed/mbed-crypto/issues/216 + */ + if (!psa_key_lifetime_is_external(slot->attr.lifetime)) { + mbedtls_rsa_context *rsa = NULL; + + status = mbedtls_psa_rsa_load_representation( + slot->attr.type, + slot->key.data, + slot->key.bytes, + &rsa); + if (status != PSA_SUCCESS) { + break; + } + + status = psa_get_rsa_public_exponent(rsa, + attributes); + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); + } + break; +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */ + default: + /* Nothing else to do. */ + break; + } + + if (status != PSA_SUCCESS) { + psa_reset_key_attributes(attributes); + } + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +psa_status_t psa_get_key_slot_number( + const psa_key_attributes_t *attributes, + psa_key_slot_number_t *slot_number) +{ + if (attributes->core.flags & MBEDTLS_PSA_KA_FLAG_HAS_SLOT_NUMBER) { + *slot_number = attributes->slot_number; + return PSA_SUCCESS; + } else { + return PSA_ERROR_INVALID_ARGUMENT; + } +} +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +static psa_status_t psa_export_key_buffer_internal(const uint8_t *key_buffer, + size_t key_buffer_size, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + if (key_buffer_size > data_size) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + memcpy(data, key_buffer, key_buffer_size); + memset(data + key_buffer_size, 0, + data_size - key_buffer_size); + *data_length = key_buffer_size; + return PSA_SUCCESS; +} + +psa_status_t psa_export_key_internal( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length) +{ + psa_key_type_t type = attributes->core.type; + + if (key_type_is_raw_bytes(type) || + PSA_KEY_TYPE_IS_RSA(type) || + PSA_KEY_TYPE_IS_ECC(type)) { + return psa_export_key_buffer_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 + support for exporting certain key types. */ + return PSA_ERROR_NOT_SUPPORTED; + } +} + +psa_status_t psa_export_key(mbedtls_svc_key_id_t key, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + /* Reject a zero-length output buffer now, since this can never be a + * valid key representation. This way we know that data must be a valid + * pointer and we can do things like memset(data, ..., data_size). */ + if (data_size == 0) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + /* Set the key to empty now, so that even when there are errors, we always + * set data_length to a value between 0 and data_size. On error, setting + * the key to empty is a good choice because an empty key representation is + * unlikely to be accepted anywhere. */ + *data_length = 0; + + /* Export requires the EXPORT flag. There is an exception for public keys, + * which don't require any flag, but + * psa_get_and_lock_key_slot_with_policy() takes care of this. + */ + status = psa_get_and_lock_key_slot_with_policy(key, &slot, + PSA_KEY_USAGE_EXPORT, 0); + if (status != PSA_SUCCESS) { + return status; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + status = psa_driver_wrapper_export_key(&attributes, + slot->key.data, slot->key.bytes, + data, data_size, data_length); + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_export_public_key_internal( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + psa_key_type_t type = attributes->core.type; + + if (PSA_KEY_TYPE_IS_RSA(type) || PSA_KEY_TYPE_IS_ECC(type)) { + if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) { + /* Exporting public -> public */ + return psa_export_key_buffer_internal( + key_buffer, key_buffer_size, + data, data_size, data_length); + } + + if (PSA_KEY_TYPE_IS_RSA(type)) { +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) + return mbedtls_psa_rsa_export_public_key(attributes, + key_buffer, + key_buffer_size, + data, + data_size, + data_length); +#else + /* We don't know how to convert a private RSA key to public. */ + return PSA_ERROR_NOT_SUPPORTED; +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */ + } else { +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) + return mbedtls_psa_ecp_export_public_key(attributes, + key_buffer, + key_buffer_size, + data, + data_size, + data_length); +#else + /* We don't know how to convert a private ECC key to public */ + return PSA_ERROR_NOT_SUPPORTED; +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */ + } + } else { + /* This shouldn't happen in the reference implementation, but + it is valid for a special-purpose implementation to omit + support for exporting certain key types. */ + return PSA_ERROR_NOT_SUPPORTED; + } +} + +psa_status_t psa_export_public_key(mbedtls_svc_key_id_t key, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + /* Reject a zero-length output buffer now, since this can never be a + * valid key representation. This way we know that data must be a valid + * pointer and we can do things like memset(data, ..., data_size). */ + if (data_size == 0) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + /* Set the key to empty now, so that even when there are errors, we always + * set data_length to a value between 0 and data_size. On error, setting + * the key to empty is a good choice because an empty key representation is + * unlikely to be accepted anywhere. */ + *data_length = 0; + + /* Exporting a public key doesn't require a usage flag. */ + status = psa_get_and_lock_key_slot_with_policy(key, &slot, 0, 0); + if (status != PSA_SUCCESS) { + return status; + } + + if (!PSA_KEY_TYPE_IS_ASYMMETRIC(slot->attr.type)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + status = psa_driver_wrapper_export_public_key( + &attributes, slot->key.data, slot->key.bytes, + data, data_size, data_length); + +exit: + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +MBEDTLS_STATIC_ASSERT( + (MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY & MBEDTLS_PSA_KA_MASK_DUAL_USE) == 0, + "One or more key attribute flag is listed as both external-only and dual-use") +MBEDTLS_STATIC_ASSERT( + (PSA_KA_MASK_INTERNAL_ONLY & MBEDTLS_PSA_KA_MASK_DUAL_USE) == 0, + "One or more key attribute flag is listed as both internal-only and dual-use") +MBEDTLS_STATIC_ASSERT( + (PSA_KA_MASK_INTERNAL_ONLY & MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY) == 0, + "One or more key attribute flag is listed as both internal-only and external-only") + +/** Validate that a key policy is internally well-formed. + * + * This function only rejects invalid policies. It does not validate the + * consistency of the policy with respect to other attributes of the key + * such as the key type. + */ +static psa_status_t psa_validate_key_policy(const psa_key_policy_t *policy) +{ + if ((policy->usage & ~(PSA_KEY_USAGE_EXPORT | + PSA_KEY_USAGE_COPY | + PSA_KEY_USAGE_ENCRYPT | + PSA_KEY_USAGE_DECRYPT | + PSA_KEY_USAGE_SIGN_MESSAGE | + PSA_KEY_USAGE_VERIFY_MESSAGE | + PSA_KEY_USAGE_SIGN_HASH | + PSA_KEY_USAGE_VERIFY_HASH | + PSA_KEY_USAGE_VERIFY_DERIVATION | + PSA_KEY_USAGE_DERIVE)) != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return PSA_SUCCESS; +} + +/** Validate the internal consistency of key attributes. + * + * This function only rejects invalid attribute values. If does not + * validate the consistency of the attributes with any key data that may + * be involved in the creation of the key. + * + * Call this function early in the key creation process. + * + * \param[in] attributes Key attributes for the new key. + * \param[out] p_drv On any return, the driver for the key, if any. + * NULL for a transparent key. + * + */ +static psa_status_t psa_validate_key_attributes( + const psa_key_attributes_t *attributes, + psa_se_drv_table_entry_t **p_drv) +{ + psa_status_t status = PSA_ERROR_INVALID_ARGUMENT; + psa_key_lifetime_t lifetime = psa_get_key_lifetime(attributes); + mbedtls_svc_key_id_t key = psa_get_key_id(attributes); + + status = psa_validate_key_location(lifetime, p_drv); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_validate_key_persistence(lifetime); + if (status != PSA_SUCCESS) { + return status; + } + + if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) { + if (MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key) != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } else { + if (!psa_is_valid_key_id(psa_get_key_id(attributes), 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + status = psa_validate_key_policy(&attributes->core.policy); + if (status != PSA_SUCCESS) { + return status; + } + + /* Refuse to create overly large keys. + * Note that this doesn't trigger on import if the attributes don't + * explicitly specify a size (so psa_get_key_bits returns 0), so + * psa_import_key() needs its own checks. */ + if (psa_get_key_bits(attributes) > PSA_MAX_KEY_BITS) { + return PSA_ERROR_NOT_SUPPORTED; + } + + /* Reject invalid flags. These should not be reachable through the API. */ + if (attributes->core.flags & ~(MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY | + MBEDTLS_PSA_KA_MASK_DUAL_USE)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return PSA_SUCCESS; +} + +/** Prepare a key slot to receive key material. + * + * This function allocates a key slot and sets its metadata. + * + * If this function fails, call psa_fail_key_creation(). + * + * This function is intended to be used as follows: + * -# Call psa_start_key_creation() to allocate a key slot, prepare + * it with the specified attributes, and in case of a volatile key assign it + * a volatile key identifier. + * -# Populate the slot with the key material. + * -# Call psa_finish_key_creation() to finalize the creation of the slot. + * In case of failure at any step, stop the sequence and call + * psa_fail_key_creation(). + * + * On success, the key slot is locked. It is the responsibility of the caller + * to unlock the key slot when it does not access it anymore. + * + * \param method An identification of the calling function. + * \param[in] attributes Key attributes for the new key. + * \param[out] p_slot On success, a pointer to the prepared slot. + * \param[out] p_drv On any return, the driver for the key, if any. + * NULL for a transparent key. + * + * \retval #PSA_SUCCESS + * The key slot is ready to receive key material. + * \return If this function fails, the key slot is an invalid state. + * You must call psa_fail_key_creation() to wipe and free the slot. + */ +static psa_status_t psa_start_key_creation( + psa_key_creation_method_t method, + const psa_key_attributes_t *attributes, + psa_key_slot_t **p_slot, + psa_se_drv_table_entry_t **p_drv) +{ + psa_status_t status; + psa_key_id_t volatile_key_id; + psa_key_slot_t *slot; + + (void) method; + *p_drv = NULL; + + status = psa_validate_key_attributes(attributes, p_drv); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_get_empty_key_slot(&volatile_key_id, p_slot); + if (status != PSA_SUCCESS) { + return status; + } + slot = *p_slot; + + /* We're storing the declared bit-size of the key. It's up to each + * creation mechanism to verify that this information is correct. + * It's automatically correct for mechanisms that use the bit-size as + * an input (generate, device) but not for those where the bit-size + * is optional (import, copy). In case of a volatile key, assign it the + * volatile key identifier associated to the slot returned to contain its + * definition. */ + + slot->attr = attributes->core; + if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) { +#if !defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) + slot->attr.id = volatile_key_id; +#else + slot->attr.id.key_id = volatile_key_id; +#endif + } + + /* Erase external-only flags from the internal copy. To access + * external-only flags, query `attributes`. Thanks to the check + * in psa_validate_key_attributes(), this leaves the dual-use + * flags and any internal flag that psa_get_empty_key_slot() + * may have set. */ + slot->attr.flags &= ~MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY; + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + /* For a key in a secure element, we need to do three things + * when creating or registering a persistent key: + * create the key file in internal storage, create the + * key inside the secure element, and update the driver's + * persistent data. This is done by starting a transaction that will + * encompass these three actions. + * For registering a volatile key, we just need to find an appropriate + * slot number inside the SE. Since the key is designated volatile, creating + * a transaction is not required. */ + /* The first thing to do is to find a slot number for the new key. + * We save the slot number in persistent storage as part of the + * transaction data. It will be needed to recover if the power + * fails during the key creation process, to clean up on the secure + * element side after restarting. Obtaining a slot number from the + * secure element driver updates its persistent state, but we do not yet + * save the driver's persistent state, so that if the power fails, + * we can roll back to a state where the key doesn't exist. */ + if (*p_drv != NULL) { + psa_key_slot_number_t slot_number; + status = psa_find_se_slot_for_key(attributes, method, *p_drv, + &slot_number); + if (status != PSA_SUCCESS) { + return status; + } + + if (!PSA_KEY_LIFETIME_IS_VOLATILE(attributes->core.lifetime)) { + psa_crypto_prepare_transaction(PSA_CRYPTO_TRANSACTION_CREATE_KEY); + psa_crypto_transaction.key.lifetime = slot->attr.lifetime; + psa_crypto_transaction.key.slot = slot_number; + psa_crypto_transaction.key.id = slot->attr.id; + status = psa_crypto_save_transaction(); + if (status != PSA_SUCCESS) { + (void) psa_crypto_stop_transaction(); + return status; + } + } + + status = psa_copy_key_material_into_slot( + slot, (uint8_t *) (&slot_number), sizeof(slot_number)); + } + + if (*p_drv == NULL && method == PSA_KEY_CREATION_REGISTER) { + /* Key registration only makes sense with a secure element. */ + return PSA_ERROR_INVALID_ARGUMENT; + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + return PSA_SUCCESS; +} + +/** Finalize the creation of a key once its key material has been set. + * + * This entails writing the key to persistent storage. + * + * If this function fails, call psa_fail_key_creation(). + * See the documentation of psa_start_key_creation() for the intended use + * of this function. + * + * If the finalization succeeds, the function unlocks the key slot (it was + * locked by psa_start_key_creation()) and the key slot cannot be accessed + * anymore as part of the key creation process. + * + * \param[in,out] slot Pointer to the slot with key material. + * \param[in] driver The secure element driver for the key, + * or NULL for a transparent key. + * \param[out] key On success, identifier of the key. Note that the + * key identifier is also stored in the key slot. + * + * \retval #PSA_SUCCESS + * The key was successfully created. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * + * \return If this function fails, the key slot is an invalid state. + * You must call psa_fail_key_creation() to wipe and free the slot. + */ +static psa_status_t psa_finish_key_creation( + psa_key_slot_t *slot, + psa_se_drv_table_entry_t *driver, + mbedtls_svc_key_id_t *key) +{ + psa_status_t status = PSA_SUCCESS; + (void) slot; + (void) driver; + +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) + if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) { +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + if (driver != NULL) { + psa_se_key_data_storage_t data; + psa_key_slot_number_t slot_number = + psa_key_slot_get_slot_number(slot); + + MBEDTLS_STATIC_ASSERT(sizeof(slot_number) == + sizeof(data.slot_number), + "Slot number size does not match psa_se_key_data_storage_t"); + + memcpy(&data.slot_number, &slot_number, sizeof(slot_number)); + status = psa_save_persistent_key(&slot->attr, + (uint8_t *) &data, + sizeof(data)); + } else +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + { + /* Key material is saved in export representation in the slot, so + * just pass the slot buffer for storage. */ + status = psa_save_persistent_key(&slot->attr, + slot->key.data, + slot->key.bytes); + } + } +#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */ + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + /* Finish the transaction for a key creation. This does not + * happen when registering an existing key. Detect this case + * by checking whether a transaction is in progress (actual + * creation of a persistent key in a secure element requires a transaction, + * but registration or volatile key creation doesn't use one). */ + if (driver != NULL && + psa_crypto_transaction.unknown.type == PSA_CRYPTO_TRANSACTION_CREATE_KEY) { + status = psa_save_se_persistent_data(driver); + if (status != PSA_SUCCESS) { + psa_destroy_persistent_key(slot->attr.id); + return status; + } + status = psa_crypto_stop_transaction(); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + if (status == PSA_SUCCESS) { + *key = slot->attr.id; + status = psa_unlock_key_slot(slot); + if (status != PSA_SUCCESS) { + *key = MBEDTLS_SVC_KEY_ID_INIT; + } + } + + return status; +} + +/** Abort the creation of a key. + * + * You may call this function after calling psa_start_key_creation(), + * or after psa_finish_key_creation() fails. In other circumstances, this + * function may not clean up persistent storage. + * See the documentation of psa_start_key_creation() for the intended use + * of this function. + * + * \param[in,out] slot Pointer to the slot with key material. + * \param[in] driver The secure element driver for the key, + * or NULL for a transparent key. + */ +static void psa_fail_key_creation(psa_key_slot_t *slot, + psa_se_drv_table_entry_t *driver) +{ + (void) driver; + + if (slot == NULL) { + return; + } + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + /* TODO: If the key has already been created in the secure + * element, and the failure happened later (when saving metadata + * to internal storage), we need to destroy the key in the secure + * element. + * https://github.com/ARMmbed/mbed-crypto/issues/217 + */ + + /* Abort the ongoing transaction if any (there may not be one if + * the creation process failed before starting one, or if the + * key creation is a registration of a key in a secure element). + * Earlier functions must already have done what it takes to undo any + * partial creation. All that's left is to update the transaction data + * itself. */ + (void) psa_crypto_stop_transaction(); +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + psa_wipe_key_slot(slot); +} + +/** Validate optional attributes during key creation. + * + * Some key attributes are optional during key creation. If they are + * specified in the attributes structure, check that they are consistent + * with the data in the slot. + * + * This function should be called near the end of key creation, after + * the slot in memory is fully populated but before saving persistent data. + */ +static psa_status_t psa_validate_optional_attributes( + const psa_key_slot_t *slot, + const psa_key_attributes_t *attributes) +{ + if (attributes->core.type != 0) { + if (attributes->core.type != slot->attr.type) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + if (attributes->domain_parameters_size != 0) { +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) + if (PSA_KEY_TYPE_IS_RSA(slot->attr.type)) { + mbedtls_rsa_context *rsa = NULL; + mbedtls_mpi actual, required; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + psa_status_t status = mbedtls_psa_rsa_load_representation( + slot->attr.type, + slot->key.data, + slot->key.bytes, + &rsa); + if (status != PSA_SUCCESS) { + return status; + } + + mbedtls_mpi_init(&actual); + mbedtls_mpi_init(&required); + ret = mbedtls_rsa_export(rsa, + NULL, NULL, NULL, NULL, &actual); + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); + if (ret != 0) { + goto rsa_exit; + } + ret = mbedtls_mpi_read_binary(&required, + attributes->domain_parameters, + attributes->domain_parameters_size); + if (ret != 0) { + goto rsa_exit; + } + if (mbedtls_mpi_cmp_mpi(&actual, &required) != 0) { + ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } +rsa_exit: + mbedtls_mpi_free(&actual); + mbedtls_mpi_free(&required); + if (ret != 0) { + return mbedtls_to_psa_error(ret); + } + } else +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */ + { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + if (attributes->core.bits != 0) { + if (attributes->core.bits != slot->attr.bits) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + return PSA_SUCCESS; +} + +psa_status_t psa_import_key(const psa_key_attributes_t *attributes, + const uint8_t *data, + size_t data_length, + mbedtls_svc_key_id_t *key) +{ + psa_status_t status; + psa_key_slot_t *slot = NULL; + psa_se_drv_table_entry_t *driver = NULL; + size_t bits; + size_t storage_size = data_length; + + *key = MBEDTLS_SVC_KEY_ID_INIT; + + /* Reject zero-length symmetric keys (including raw data key objects). + * This also rejects any key which might be encoded as an empty string, + * which is never valid. */ + if (data_length == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* Ensure that the bytes-to-bits conversion cannot overflow. */ + if (data_length > SIZE_MAX / 8) { + return PSA_ERROR_NOT_SUPPORTED; + } + + status = psa_start_key_creation(PSA_KEY_CREATION_IMPORT, attributes, + &slot, &driver); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* In the case of a transparent key or an opaque key stored in local + * storage ( thus not in the case of importing a key in a secure element + * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a + * buffer to hold the imported key material. */ + if (slot->key.data == NULL) { + if (psa_key_lifetime_is_external(attributes->core.lifetime)) { + status = psa_driver_wrapper_get_key_buffer_size_from_key_data( + attributes, data, data_length, &storage_size); + if (status != PSA_SUCCESS) { + goto exit; + } + } + status = psa_allocate_buffer_to_slot(slot, storage_size); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + bits = slot->attr.bits; + status = psa_driver_wrapper_import_key(attributes, + data, data_length, + slot->key.data, + slot->key.bytes, + &slot->key.bytes, &bits); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (slot->attr.bits == 0) { + slot->attr.bits = (psa_key_bits_t) bits; + } else if (bits != slot->attr.bits) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + /* Enforce a size limit, and in particular ensure that the bit + * size fits in its representation type.*/ + if (bits > PSA_MAX_KEY_BITS) { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + status = psa_validate_optional_attributes(slot, attributes); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_finish_key_creation(slot, driver, key); +exit: + if (status != PSA_SUCCESS) { + psa_fail_key_creation(slot, driver); + } + + return status; +} + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +psa_status_t mbedtls_psa_register_se_key( + const psa_key_attributes_t *attributes) +{ + psa_status_t status; + psa_key_slot_t *slot = NULL; + psa_se_drv_table_entry_t *driver = NULL; + mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT; + + /* Leaving attributes unspecified is not currently supported. + * It could make sense to query the key type and size from the + * secure element, but not all secure elements support this + * and the driver HAL doesn't currently support it. */ + if (psa_get_key_type(attributes) == PSA_KEY_TYPE_NONE) { + return PSA_ERROR_NOT_SUPPORTED; + } + if (psa_get_key_bits(attributes) == 0) { + return PSA_ERROR_NOT_SUPPORTED; + } + + status = psa_start_key_creation(PSA_KEY_CREATION_REGISTER, attributes, + &slot, &driver); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_finish_key_creation(slot, driver, &key); + +exit: + if (status != PSA_SUCCESS) { + psa_fail_key_creation(slot, driver); + } + + /* Registration doesn't keep the key in RAM. */ + psa_close_key(key); + return status; +} +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +psa_status_t psa_copy_key(mbedtls_svc_key_id_t source_key, + const psa_key_attributes_t *specified_attributes, + mbedtls_svc_key_id_t *target_key) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *source_slot = NULL; + psa_key_slot_t *target_slot = NULL; + psa_key_attributes_t actual_attributes = *specified_attributes; + psa_se_drv_table_entry_t *driver = NULL; + size_t storage_size = 0; + + *target_key = MBEDTLS_SVC_KEY_ID_INIT; + + status = psa_get_and_lock_key_slot_with_policy( + source_key, &source_slot, PSA_KEY_USAGE_COPY, 0); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_validate_optional_attributes(source_slot, + specified_attributes); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* The target key type and number of bits have been validated by + * psa_validate_optional_attributes() to be either equal to zero or + * equal to the ones of the source key. So it is safe to inherit + * them from the source key now." + * */ + actual_attributes.core.bits = source_slot->attr.bits; + actual_attributes.core.type = source_slot->attr.type; + + + status = psa_restrict_key_policy(source_slot->attr.type, + &actual_attributes.core.policy, + &source_slot->attr.policy); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_start_key_creation(PSA_KEY_CREATION_COPY, &actual_attributes, + &target_slot, &driver); + if (status != PSA_SUCCESS) { + goto exit; + } + if (PSA_KEY_LIFETIME_GET_LOCATION(target_slot->attr.lifetime) != + PSA_KEY_LIFETIME_GET_LOCATION(source_slot->attr.lifetime)) { + /* + * If the source and target keys are stored in different locations, + * the source key would need to be exported as plaintext and re-imported + * in the other location. This has security implications which have not + * been fully mapped. For now, this can be achieved through + * appropriate API invocations from the application, if needed. + * */ + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + /* + * When the source and target keys are within the same location, + * - For transparent keys it is a blind copy without any driver invocation, + * - For opaque keys this translates to an invocation of the drivers' + * copy_key entry point through the dispatch layer. + * */ + if (psa_key_lifetime_is_external(actual_attributes.core.lifetime)) { + status = psa_driver_wrapper_get_key_buffer_size(&actual_attributes, + &storage_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_allocate_buffer_to_slot(target_slot, storage_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_driver_wrapper_copy_key(&actual_attributes, + source_slot->key.data, + source_slot->key.bytes, + target_slot->key.data, + target_slot->key.bytes, + &target_slot->key.bytes); + if (status != PSA_SUCCESS) { + goto exit; + } + } else { + status = psa_copy_key_material_into_slot(target_slot, + source_slot->key.data, + source_slot->key.bytes); + if (status != PSA_SUCCESS) { + goto exit; + } + } + status = psa_finish_key_creation(target_slot, driver, target_key); +exit: + if (status != PSA_SUCCESS) { + psa_fail_key_creation(target_slot, driver); + } + + unlock_status = psa_unlock_key_slot(source_slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + + + +/****************************************************************/ +/* Message digests */ +/****************************************************************/ + +psa_status_t psa_hash_abort(psa_hash_operation_t *operation) +{ + /* Aborting a non-active operation is allowed */ + if (operation->id == 0) { + return PSA_SUCCESS; + } + + psa_status_t status = psa_driver_wrapper_hash_abort(operation); + operation->id = 0; + + return status; +} + +psa_status_t psa_hash_setup(psa_hash_operation_t *operation, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* A context must be freshly initialized before it can be set up. */ + if (operation->id != 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (!PSA_ALG_IS_HASH(alg)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + /* Ensure all of the context is zeroized, since PSA_HASH_OPERATION_INIT only + * directly zeroes the int-sized dummy member of the context union. */ + memset(&operation->ctx, 0, sizeof(operation->ctx)); + + status = psa_driver_wrapper_hash_setup(operation, alg); + +exit: + if (status != PSA_SUCCESS) { + psa_hash_abort(operation); + } + + return status; +} + +psa_status_t psa_hash_update(psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + /* Don't require hash implementations to behave correctly on a + * zero-length input, which may have an invalid pointer. */ + if (input_length == 0) { + return PSA_SUCCESS; + } + + status = psa_driver_wrapper_hash_update(operation, input, input_length); + +exit: + if (status != PSA_SUCCESS) { + psa_hash_abort(operation); + } + + return status; +} + +psa_status_t psa_hash_finish(psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length) +{ + *hash_length = 0; + if (operation->id == 0) { + return PSA_ERROR_BAD_STATE; + } + + psa_status_t status = psa_driver_wrapper_hash_finish( + operation, hash, hash_size, hash_length); + psa_hash_abort(operation); + return status; +} + +psa_status_t psa_hash_verify(psa_hash_operation_t *operation, + const uint8_t *hash, + size_t hash_length) +{ + uint8_t actual_hash[PSA_HASH_MAX_SIZE]; + size_t actual_hash_length; + psa_status_t status = psa_hash_finish( + operation, + actual_hash, sizeof(actual_hash), + &actual_hash_length); + + if (status != PSA_SUCCESS) { + goto exit; + } + + if (actual_hash_length != hash_length) { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + + if (mbedtls_psa_safer_memcmp(hash, actual_hash, actual_hash_length) != 0) { + status = PSA_ERROR_INVALID_SIGNATURE; + } + +exit: + mbedtls_platform_zeroize(actual_hash, sizeof(actual_hash)); + if (status != PSA_SUCCESS) { + psa_hash_abort(operation); + } + + return status; +} + +psa_status_t psa_hash_compute(psa_algorithm_t alg, + const uint8_t *input, size_t input_length, + uint8_t *hash, size_t hash_size, + size_t *hash_length) +{ + *hash_length = 0; + if (!PSA_ALG_IS_HASH(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return psa_driver_wrapper_hash_compute(alg, input, input_length, + hash, hash_size, hash_length); +} + +psa_status_t psa_hash_compare(psa_algorithm_t alg, + const uint8_t *input, size_t input_length, + const uint8_t *hash, size_t hash_length) +{ + uint8_t actual_hash[PSA_HASH_MAX_SIZE]; + size_t actual_hash_length; + + if (!PSA_ALG_IS_HASH(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + psa_status_t status = psa_driver_wrapper_hash_compute( + alg, input, input_length, + actual_hash, sizeof(actual_hash), + &actual_hash_length); + if (status != PSA_SUCCESS) { + goto exit; + } + if (actual_hash_length != hash_length) { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + if (mbedtls_psa_safer_memcmp(hash, actual_hash, actual_hash_length) != 0) { + status = PSA_ERROR_INVALID_SIGNATURE; + } + +exit: + mbedtls_platform_zeroize(actual_hash, sizeof(actual_hash)); + return status; +} + +psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation, + psa_hash_operation_t *target_operation) +{ + if (source_operation->id == 0 || + target_operation->id != 0) { + return PSA_ERROR_BAD_STATE; + } + + psa_status_t status = psa_driver_wrapper_hash_clone(source_operation, + target_operation); + if (status != PSA_SUCCESS) { + psa_hash_abort(target_operation); + } + + return status; +} + + +/****************************************************************/ +/* MAC */ +/****************************************************************/ + +psa_status_t psa_mac_abort(psa_mac_operation_t *operation) +{ + /* Aborting a non-active operation is allowed */ + if (operation->id == 0) { + return PSA_SUCCESS; + } + + psa_status_t status = psa_driver_wrapper_mac_abort(operation); + operation->mac_size = 0; + operation->is_sign = 0; + operation->id = 0; + + return status; +} + +static psa_status_t psa_mac_finalize_alg_and_key_validation( + psa_algorithm_t alg, + const psa_key_attributes_t *attributes, + uint8_t *mac_size) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_type_t key_type = psa_get_key_type(attributes); + size_t key_bits = psa_get_key_bits(attributes); + + if (!PSA_ALG_IS_MAC(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* Validate the combination of key type and algorithm */ + status = psa_mac_key_can_do(alg, key_type); + if (status != PSA_SUCCESS) { + return status; + } + + /* Get the output length for the algorithm and key combination */ + *mac_size = PSA_MAC_LENGTH(key_type, key_bits, alg); + + if (*mac_size < 4) { + /* A very short MAC is too short for security since it can be + * brute-forced. Ancient protocols with 32-bit MACs do exist, + * so we make this our minimum, even though 32 bits is still + * too small for security. */ + return PSA_ERROR_NOT_SUPPORTED; + } + + if (*mac_size > PSA_MAC_LENGTH(key_type, key_bits, + PSA_ALG_FULL_LENGTH_MAC(alg))) { + /* It's impossible to "truncate" to a larger length than the full length + * of the algorithm. */ + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (*mac_size > PSA_MAC_MAX_SIZE) { + /* PSA_MAC_LENGTH returns the correct length even for a MAC algorithm + * that is disabled in the compile-time configuration. The result can + * therefore be larger than PSA_MAC_MAX_SIZE, which does take the + * configuration into account. In this case, force a return of + * PSA_ERROR_NOT_SUPPORTED here. Otherwise psa_mac_verify(), or + * psa_mac_compute(mac_size=PSA_MAC_MAX_SIZE), would return + * PSA_ERROR_BUFFER_TOO_SMALL for an unsupported algorithm whose MAC size + * is larger than PSA_MAC_MAX_SIZE, which is misleading and which breaks + * systematically generated tests. */ + return PSA_ERROR_NOT_SUPPORTED; + } + + return PSA_SUCCESS; +} + +static psa_status_t psa_mac_setup(psa_mac_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + int is_sign) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + + /* A context must be freshly initialized before it can be set up. */ + if (operation->id != 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_get_and_lock_key_slot_with_policy( + key, + &slot, + is_sign ? PSA_KEY_USAGE_SIGN_MESSAGE : PSA_KEY_USAGE_VERIFY_MESSAGE, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_mac_finalize_alg_and_key_validation(alg, &attributes, + &operation->mac_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + operation->is_sign = is_sign; + /* Dispatch the MAC setup call with validated input */ + if (is_sign) { + status = psa_driver_wrapper_mac_sign_setup(operation, + &attributes, + slot->key.data, + slot->key.bytes, + alg); + } else { + status = psa_driver_wrapper_mac_verify_setup(operation, + &attributes, + slot->key.data, + slot->key.bytes, + alg); + } + +exit: + if (status != PSA_SUCCESS) { + psa_mac_abort(operation); + } + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg) +{ + return psa_mac_setup(operation, key, alg, 1); +} + +psa_status_t psa_mac_verify_setup(psa_mac_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg) +{ + return psa_mac_setup(operation, key, alg, 0); +} + +psa_status_t psa_mac_update(psa_mac_operation_t *operation, + const uint8_t *input, + size_t input_length) +{ + if (operation->id == 0) { + return PSA_ERROR_BAD_STATE; + } + + /* Don't require hash implementations to behave correctly on a + * zero-length input, which may have an invalid pointer. */ + if (input_length == 0) { + return PSA_SUCCESS; + } + + psa_status_t status = psa_driver_wrapper_mac_update(operation, + input, input_length); + if (status != PSA_SUCCESS) { + psa_mac_abort(operation); + } + + return status; +} + +psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation, + uint8_t *mac, + size_t mac_size, + size_t *mac_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (!operation->is_sign) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + /* Sanity check. This will guarantee that mac_size != 0 (and so mac != NULL) + * once all the error checks are done. */ + if (operation->mac_size == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (mac_size < operation->mac_size) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + status = psa_driver_wrapper_mac_sign_finish(operation, + mac, operation->mac_size, + mac_length); + +exit: + /* In case of success, set the potential excess room in the output buffer + * to an invalid value, to avoid potentially leaking a longer MAC. + * In case of error, set the output length and content to a safe default, + * such that in case the caller misses an error check, the output would be + * an unachievable MAC. + */ + if (status != PSA_SUCCESS) { + *mac_length = mac_size; + operation->mac_size = 0; + } + + psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length); + + abort_status = psa_mac_abort(operation); + + return status == PSA_SUCCESS ? abort_status : status; +} + +psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation, + const uint8_t *mac, + size_t mac_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->is_sign) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->mac_size != mac_length) { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + + status = psa_driver_wrapper_mac_verify_finish(operation, + mac, mac_length); + +exit: + abort_status = psa_mac_abort(operation); + + return status == PSA_SUCCESS ? abort_status : status; +} + +static psa_status_t psa_mac_compute_internal(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length, + int is_sign) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + uint8_t operation_mac_size = 0; + + status = psa_get_and_lock_key_slot_with_policy( + key, + &slot, + is_sign ? PSA_KEY_USAGE_SIGN_MESSAGE : PSA_KEY_USAGE_VERIFY_MESSAGE, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_mac_finalize_alg_and_key_validation(alg, &attributes, + &operation_mac_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (mac_size < operation_mac_size) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + status = psa_driver_wrapper_mac_compute( + &attributes, + slot->key.data, slot->key.bytes, + alg, + input, input_length, + mac, operation_mac_size, mac_length); + +exit: + /* In case of success, set the potential excess room in the output buffer + * to an invalid value, to avoid potentially leaking a longer MAC. + * In case of error, set the output length and content to a safe default, + * such that in case the caller misses an error check, the output would be + * an unachievable MAC. + */ + if (status != PSA_SUCCESS) { + *mac_length = mac_size; + operation_mac_size = 0; + } + + psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length); + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_mac_compute(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length) +{ + return psa_mac_compute_internal(key, alg, + input, input_length, + mac, mac_size, mac_length, 1); +} + +psa_status_t psa_mac_verify(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *mac, + size_t mac_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + uint8_t actual_mac[PSA_MAC_MAX_SIZE]; + size_t actual_mac_length; + + status = psa_mac_compute_internal(key, alg, + input, input_length, + actual_mac, sizeof(actual_mac), + &actual_mac_length, 0); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (mac_length != actual_mac_length) { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + if (mbedtls_psa_safer_memcmp(mac, actual_mac, actual_mac_length) != 0) { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + +exit: + mbedtls_platform_zeroize(actual_mac, sizeof(actual_mac)); + + return status; +} + +/****************************************************************/ +/* Asymmetric cryptography */ +/****************************************************************/ + +static psa_status_t psa_sign_verify_check_alg(int input_is_message, + psa_algorithm_t alg) +{ + if (input_is_message) { + if (!PSA_ALG_IS_SIGN_MESSAGE(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (PSA_ALG_IS_SIGN_HASH(alg)) { + if (!PSA_ALG_IS_HASH(PSA_ALG_SIGN_GET_HASH(alg))) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + } else { + if (!PSA_ALG_IS_SIGN_HASH(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + return PSA_SUCCESS; +} + +static psa_status_t psa_sign_internal(mbedtls_svc_key_id_t key, + int input_is_message, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + *signature_length = 0; + + status = psa_sign_verify_check_alg(input_is_message, alg); + if (status != PSA_SUCCESS) { + return status; + } + + /* Immediately reject a zero-length signature buffer. This guarantees + * that signature must be a valid pointer. (On the other hand, the input + * buffer can in principle be empty since it doesn't actually have + * to be a hash.) */ + if (signature_size == 0) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + status = psa_get_and_lock_key_slot_with_policy( + key, &slot, + input_is_message ? PSA_KEY_USAGE_SIGN_MESSAGE : + PSA_KEY_USAGE_SIGN_HASH, + alg); + + if (status != PSA_SUCCESS) { + goto exit; + } + + if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + if (input_is_message) { + status = psa_driver_wrapper_sign_message( + &attributes, slot->key.data, slot->key.bytes, + alg, input, input_length, + signature, signature_size, signature_length); + } else { + + status = psa_driver_wrapper_sign_hash( + &attributes, slot->key.data, slot->key.bytes, + alg, input, input_length, + signature, signature_size, signature_length); + } + + +exit: + psa_wipe_tag_output_buffer(signature, status, signature_size, + *signature_length); + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +static psa_status_t psa_verify_internal(mbedtls_svc_key_id_t key, + int input_is_message, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *signature, + size_t signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + status = psa_sign_verify_check_alg(input_is_message, alg); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_get_and_lock_key_slot_with_policy( + key, &slot, + input_is_message ? PSA_KEY_USAGE_VERIFY_MESSAGE : + PSA_KEY_USAGE_VERIFY_HASH, + alg); + + if (status != PSA_SUCCESS) { + return status; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + if (input_is_message) { + status = psa_driver_wrapper_verify_message( + &attributes, slot->key.data, slot->key.bytes, + alg, input, input_length, + signature, signature_length); + } else { + status = psa_driver_wrapper_verify_hash( + &attributes, slot->key.data, slot->key.bytes, + alg, input, input_length, + signature, signature_length); + } + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; + +} + +psa_status_t psa_sign_message_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (PSA_ALG_IS_SIGN_HASH(alg)) { + size_t hash_length; + uint8_t hash[PSA_HASH_MAX_SIZE]; + + status = psa_driver_wrapper_hash_compute( + PSA_ALG_SIGN_GET_HASH(alg), + input, input_length, + hash, sizeof(hash), &hash_length); + + if (status != PSA_SUCCESS) { + return status; + } + + return psa_driver_wrapper_sign_hash( + attributes, key_buffer, key_buffer_size, + alg, hash, hash_length, + signature, signature_size, signature_length); + } + + return PSA_ERROR_NOT_SUPPORTED; +} + +psa_status_t psa_sign_message(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length) +{ + return psa_sign_internal( + key, 1, alg, input, input_length, + signature, signature_size, signature_length); +} + +psa_status_t psa_verify_message_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *signature, + size_t signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (PSA_ALG_IS_SIGN_HASH(alg)) { + size_t hash_length; + uint8_t hash[PSA_HASH_MAX_SIZE]; + + status = psa_driver_wrapper_hash_compute( + PSA_ALG_SIGN_GET_HASH(alg), + input, input_length, + hash, sizeof(hash), &hash_length); + + if (status != PSA_SUCCESS) { + return status; + } + + return psa_driver_wrapper_verify_hash( + attributes, key_buffer, key_buffer_size, + alg, hash, hash_length, + signature, signature_length); + } + + return PSA_ERROR_NOT_SUPPORTED; +} + +psa_status_t psa_verify_message(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *signature, + size_t signature_length) +{ + return psa_verify_internal( + key, 1, alg, input, input_length, + signature, signature_length); +} + +psa_status_t psa_sign_hash_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length) +{ + if (attributes->core.type == PSA_KEY_TYPE_RSA_KEY_PAIR) { + if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) || + PSA_ALG_IS_RSA_PSS(alg)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) + return mbedtls_psa_rsa_sign_hash( + attributes, + key_buffer, key_buffer_size, + alg, hash, hash_length, + signature, signature_size, signature_length); +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */ + } else { + return PSA_ERROR_INVALID_ARGUMENT; + } + } else if (PSA_KEY_TYPE_IS_ECC(attributes->core.type)) { + if (PSA_ALG_IS_ECDSA(alg)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) + return mbedtls_psa_ecdsa_sign_hash( + attributes, + key_buffer, key_buffer_size, + alg, hash, hash_length, + signature, signature_size, signature_length); +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */ + } else { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + (void) key_buffer; + (void) key_buffer_size; + (void) hash; + (void) hash_length; + (void) signature; + (void) signature_size; + (void) signature_length; + + return PSA_ERROR_NOT_SUPPORTED; +} + +psa_status_t psa_sign_hash(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *hash, + size_t hash_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length) +{ + return psa_sign_internal( + key, 0, alg, hash, hash_length, + signature, signature_size, signature_length); +} + +psa_status_t psa_verify_hash_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length) +{ + if (PSA_KEY_TYPE_IS_RSA(attributes->core.type)) { + if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) || + PSA_ALG_IS_RSA_PSS(alg)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) + return mbedtls_psa_rsa_verify_hash( + attributes, + key_buffer, key_buffer_size, + alg, hash, hash_length, + signature, signature_length); +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */ + } else { + return PSA_ERROR_INVALID_ARGUMENT; + } + } else if (PSA_KEY_TYPE_IS_ECC(attributes->core.type)) { + if (PSA_ALG_IS_ECDSA(alg)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) + return mbedtls_psa_ecdsa_verify_hash( + attributes, + key_buffer, key_buffer_size, + alg, hash, hash_length, + signature, signature_length); +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */ + } else { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + (void) key_buffer; + (void) key_buffer_size; + (void) hash; + (void) hash_length; + (void) signature; + (void) signature_length; + + return PSA_ERROR_NOT_SUPPORTED; +} + +psa_status_t psa_verify_hash(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *hash, + size_t hash_length, + const uint8_t *signature, + size_t signature_length) +{ + return psa_verify_internal( + key, 0, alg, hash, hash_length, + signature, signature_length); +} + +psa_status_t psa_asymmetric_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + (void) input; + (void) input_length; + (void) salt; + (void) output; + (void) output_size; + + *output_length = 0; + + if (!PSA_ALG_IS_RSA_OAEP(alg) && salt_length != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_get_and_lock_transparent_key_slot_with_policy( + key, &slot, PSA_KEY_USAGE_ENCRYPT, alg); + if (status != PSA_SUCCESS) { + return status; + } + if (!(PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type) || + PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type))) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_driver_wrapper_asymmetric_encrypt( + &attributes, slot->key.data, slot->key.bytes, + alg, input, input_length, salt, salt_length, + output, output_size, output_length); +exit: + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_asymmetric_decrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + (void) input; + (void) input_length; + (void) salt; + (void) output; + (void) output_size; + + *output_length = 0; + + if (!PSA_ALG_IS_RSA_OAEP(alg) && salt_length != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_get_and_lock_transparent_key_slot_with_policy( + key, &slot, PSA_KEY_USAGE_DECRYPT, alg); + if (status != PSA_SUCCESS) { + return status; + } + if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_driver_wrapper_asymmetric_decrypt( + &attributes, slot->key.data, slot->key.bytes, + alg, input, input_length, salt, salt_length, + output, output_size, output_length); + +exit: + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +/****************************************************************/ +/* Asymmetric interruptible cryptography */ +/****************************************************************/ + +static uint32_t psa_interruptible_max_ops = PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED; + +void psa_interruptible_set_max_ops(uint32_t max_ops) +{ + psa_interruptible_max_ops = max_ops; +} + +uint32_t psa_interruptible_get_max_ops(void) +{ + return psa_interruptible_max_ops; +} + +uint32_t psa_sign_hash_get_num_ops( + const psa_sign_hash_interruptible_operation_t *operation) +{ + return operation->num_ops; +} + +uint32_t psa_verify_hash_get_num_ops( + const psa_verify_hash_interruptible_operation_t *operation) +{ + return operation->num_ops; +} + +static psa_status_t psa_sign_hash_abort_internal( + psa_sign_hash_interruptible_operation_t *operation) +{ + if (operation->id == 0) { + /* The object has (apparently) been initialized but it is not (yet) + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + return PSA_SUCCESS; + } + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_driver_wrapper_sign_hash_abort(operation); + + operation->id = 0; + + /* Do not clear either the error_occurred or num_ops elements here as they + * only want to be cleared by the application calling abort, not by abort + * being called at completion of an operation. */ + + return status; +} + +psa_status_t psa_sign_hash_start( + psa_sign_hash_interruptible_operation_t *operation, + mbedtls_svc_key_id_t key, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + /* Check that start has not been previously called, or operation has not + * previously errored. */ + if (operation->id != 0 || operation->error_occurred) { + return PSA_ERROR_BAD_STATE; + } + + status = psa_sign_verify_check_alg(0, alg); + if (status != PSA_SUCCESS) { + operation->error_occurred = 1; + return status; + } + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, + PSA_KEY_USAGE_SIGN_HASH, + alg); + + if (status != PSA_SUCCESS) { + goto exit; + } + + if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + /* Ensure ops count gets reset, in case of operation re-use. */ + operation->num_ops = 0; + + status = psa_driver_wrapper_sign_hash_start(operation, &attributes, + slot->key.data, + slot->key.bytes, alg, + hash, hash_length); +exit: + + if (status != PSA_SUCCESS) { + operation->error_occurred = 1; + psa_sign_hash_abort_internal(operation); + } + + unlock_status = psa_unlock_key_slot(slot); + + if (unlock_status != PSA_SUCCESS) { + operation->error_occurred = 1; + } + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + + +psa_status_t psa_sign_hash_complete( + psa_sign_hash_interruptible_operation_t *operation, + uint8_t *signature, size_t signature_size, + size_t *signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + *signature_length = 0; + + /* Check that start has been called first, and that operation has not + * previously errored. */ + if (operation->id == 0 || operation->error_occurred) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + /* Immediately reject a zero-length signature buffer. This guarantees that + * signature must be a valid pointer. */ + if (signature_size == 0) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + status = psa_driver_wrapper_sign_hash_complete(operation, signature, + signature_size, + signature_length); + + /* Update ops count with work done. */ + operation->num_ops = psa_driver_wrapper_sign_hash_get_num_ops(operation); + +exit: + + psa_wipe_tag_output_buffer(signature, status, signature_size, + *signature_length); + + if (status != PSA_OPERATION_INCOMPLETE) { + if (status != PSA_SUCCESS) { + operation->error_occurred = 1; + } + + psa_sign_hash_abort_internal(operation); + } + + return status; +} + +psa_status_t psa_sign_hash_abort( + psa_sign_hash_interruptible_operation_t *operation) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_sign_hash_abort_internal(operation); + + /* We clear the number of ops done here, so that it is not cleared when + * the operation fails or succeeds, only on manual abort. */ + operation->num_ops = 0; + + /* Likewise, failure state. */ + operation->error_occurred = 0; + + return status; +} + +static psa_status_t psa_verify_hash_abort_internal( + psa_verify_hash_interruptible_operation_t *operation) +{ + if (operation->id == 0) { + /* The object has (apparently) been initialized but it is not (yet) + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + return PSA_SUCCESS; + } + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_driver_wrapper_verify_hash_abort(operation); + + operation->id = 0; + + /* Do not clear either the error_occurred or num_ops elements here as they + * only want to be cleared by the application calling abort, not by abort + * being called at completion of an operation. */ + + return status; +} + +psa_status_t psa_verify_hash_start( + psa_verify_hash_interruptible_operation_t *operation, + mbedtls_svc_key_id_t key, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + /* Check that start has not been previously called, or operation has not + * previously errored. */ + if (operation->id != 0 || operation->error_occurred) { + return PSA_ERROR_BAD_STATE; + } + + status = psa_sign_verify_check_alg(0, alg); + if (status != PSA_SUCCESS) { + operation->error_occurred = 1; + return status; + } + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, + PSA_KEY_USAGE_VERIFY_HASH, + alg); + + if (status != PSA_SUCCESS) { + operation->error_occurred = 1; + return status; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + /* Ensure ops count gets reset, in case of operation re-use. */ + operation->num_ops = 0; + + status = psa_driver_wrapper_verify_hash_start(operation, &attributes, + slot->key.data, + slot->key.bytes, + alg, hash, hash_length, + signature, signature_length); + + if (status != PSA_SUCCESS) { + operation->error_occurred = 1; + psa_verify_hash_abort_internal(operation); + } + + unlock_status = psa_unlock_key_slot(slot); + + if (unlock_status != PSA_SUCCESS) { + operation->error_occurred = 1; + } + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_verify_hash_complete( + psa_verify_hash_interruptible_operation_t *operation) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* Check that start has been called first, and that operation has not + * previously errored. */ + if (operation->id == 0 || operation->error_occurred) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_driver_wrapper_verify_hash_complete(operation); + + /* Update ops count with work done. */ + operation->num_ops = psa_driver_wrapper_verify_hash_get_num_ops( + operation); + +exit: + + if (status != PSA_OPERATION_INCOMPLETE) { + if (status != PSA_SUCCESS) { + operation->error_occurred = 1; + } + + psa_verify_hash_abort_internal(operation); + } + + return status; +} + +psa_status_t psa_verify_hash_abort( + psa_verify_hash_interruptible_operation_t *operation) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_verify_hash_abort_internal(operation); + + /* We clear the number of ops done here, so that it is not cleared when + * the operation fails or succeeds, only on manual abort. */ + operation->num_ops = 0; + + /* Likewise, failure state. */ + operation->error_occurred = 0; + + return status; +} + +/****************************************************************/ +/* Asymmetric interruptible cryptography internal */ +/* implementations */ +/****************************************************************/ + +void mbedtls_psa_interruptible_set_max_ops(uint32_t max_ops) +{ + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + /* Internal implementation uses zero to indicate infinite number max ops, + * therefore avoid this value, and set to minimum possible. */ + if (max_ops == 0) { + max_ops = 1; + } + + mbedtls_ecp_set_max_ops(max_ops); +#else + (void) max_ops; +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +uint32_t mbedtls_psa_sign_hash_get_num_ops( + const mbedtls_psa_sign_hash_interruptible_operation_t *operation) +{ +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + return operation->num_ops; +#else + (void) operation; + return 0; +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +uint32_t mbedtls_psa_verify_hash_get_num_ops( + const mbedtls_psa_verify_hash_interruptible_operation_t *operation) +{ + #if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + return operation->num_ops; +#else + (void) operation; + return 0; +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +psa_status_t mbedtls_psa_sign_hash_start( + mbedtls_psa_sign_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t required_hash_length; + + if (!PSA_KEY_TYPE_IS_ECC(attributes->core.type)) { + return PSA_ERROR_NOT_SUPPORTED; + } + + if (!PSA_ALG_IS_ECDSA(alg)) { + return PSA_ERROR_NOT_SUPPORTED; + } + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + mbedtls_ecdsa_restart_init(&operation->restart_ctx); + + /* Ensure num_ops is zero'ed in case of context re-use. */ + operation->num_ops = 0; + + status = mbedtls_psa_ecp_load_representation(attributes->core.type, + attributes->core.bits, + key_buffer, + key_buffer_size, + &operation->ctx); + + if (status != PSA_SUCCESS) { + return status; + } + + operation->coordinate_bytes = PSA_BITS_TO_BYTES( + operation->ctx->grp.nbits); + + psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg); + operation->md_alg = mbedtls_hash_info_md_from_psa(hash_alg); + operation->alg = alg; + + /* We only need to store the same length of hash as the private key size + * here, it would be truncated by the internal implementation anyway. */ + required_hash_length = (hash_length < operation->coordinate_bytes ? + hash_length : operation->coordinate_bytes); + + if (required_hash_length > sizeof(operation->hash)) { + /* Shouldn't happen, but better safe than sorry. */ + return PSA_ERROR_CORRUPTION_DETECTED; + } + + memcpy(operation->hash, hash, required_hash_length); + operation->hash_length = required_hash_length; + + return PSA_SUCCESS; + +#else + (void) operation; + (void) key_buffer; + (void) key_buffer_size; + (void) alg; + (void) hash; + (void) hash_length; + (void) status; + (void) required_hash_length; + + return PSA_ERROR_NOT_SUPPORTED; +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +psa_status_t mbedtls_psa_sign_hash_complete( + mbedtls_psa_sign_hash_interruptible_operation_t *operation, + uint8_t *signature, size_t signature_size, + size_t *signature_length) +{ +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi r; + mbedtls_mpi s; + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + /* Ensure max_ops is set to the current value (or default). */ + mbedtls_psa_interruptible_set_max_ops(psa_interruptible_get_max_ops()); + + if (signature_size < 2 * operation->coordinate_bytes) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + if (PSA_ALG_ECDSA_IS_DETERMINISTIC(operation->alg)) { + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) + status = mbedtls_to_psa_error( + mbedtls_ecdsa_sign_det_restartable(&operation->ctx->grp, + &r, + &s, + &operation->ctx->d, + operation->hash, + operation->hash_length, + operation->md_alg, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + &operation->restart_ctx)); +#else /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */ + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */ + } else { + status = mbedtls_to_psa_error( + mbedtls_ecdsa_sign_restartable(&operation->ctx->grp, + &r, + &s, + &operation->ctx->d, + operation->hash, + operation->hash_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + &operation->restart_ctx)); + } + + /* Hide the fact that the restart context only holds a delta of number of + * ops done during the last operation, not an absolute value. */ + operation->num_ops += operation->restart_ctx.ecp.ops_done; + + if (status == PSA_SUCCESS) { + status = mbedtls_to_psa_error( + mbedtls_mpi_write_binary(&r, + signature, + operation->coordinate_bytes) + ); + + if (status != PSA_SUCCESS) { + goto exit; + } + + status = mbedtls_to_psa_error( + mbedtls_mpi_write_binary(&s, + signature + + operation->coordinate_bytes, + operation->coordinate_bytes) + ); + + if (status != PSA_SUCCESS) { + goto exit; + } + + *signature_length = operation->coordinate_bytes * 2; + + status = PSA_SUCCESS; + } + +exit: + + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + return status; + + #else + + (void) operation; + (void) signature; + (void) signature_size; + (void) signature_length; + + return PSA_ERROR_NOT_SUPPORTED; + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +psa_status_t mbedtls_psa_sign_hash_abort( + mbedtls_psa_sign_hash_interruptible_operation_t *operation) +{ + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + if (operation->ctx) { + mbedtls_ecdsa_free(operation->ctx); + mbedtls_free(operation->ctx); + operation->ctx = NULL; + } + + mbedtls_ecdsa_restart_free(&operation->restart_ctx); + + operation->num_ops = 0; + + return PSA_SUCCESS; + +#else + + (void) operation; + + return PSA_ERROR_NOT_SUPPORTED; + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +psa_status_t mbedtls_psa_verify_hash_start( + mbedtls_psa_verify_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t coordinate_bytes = 0; + size_t required_hash_length = 0; + + if (!PSA_KEY_TYPE_IS_ECC(attributes->core.type)) { + return PSA_ERROR_NOT_SUPPORTED; + } + + if (!PSA_ALG_IS_ECDSA(alg)) { + return PSA_ERROR_NOT_SUPPORTED; + } + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + mbedtls_ecdsa_restart_init(&operation->restart_ctx); + mbedtls_mpi_init(&operation->r); + mbedtls_mpi_init(&operation->s); + + /* Ensure num_ops is zero'ed in case of context re-use. */ + operation->num_ops = 0; + + status = mbedtls_psa_ecp_load_representation(attributes->core.type, + attributes->core.bits, + key_buffer, + key_buffer_size, + &operation->ctx); + + if (status != PSA_SUCCESS) { + return status; + } + + coordinate_bytes = PSA_BITS_TO_BYTES(operation->ctx->grp.nbits); + + if (signature_length != 2 * coordinate_bytes) { + return PSA_ERROR_INVALID_SIGNATURE; + } + + status = mbedtls_to_psa_error( + mbedtls_mpi_read_binary(&operation->r, + signature, + coordinate_bytes)); + + if (status != PSA_SUCCESS) { + return status; + } + + status = mbedtls_to_psa_error( + mbedtls_mpi_read_binary(&operation->s, + signature + + coordinate_bytes, + coordinate_bytes)); + + if (status != PSA_SUCCESS) { + return status; + } + + status = mbedtls_psa_ecp_load_public_part(operation->ctx); + + if (status != PSA_SUCCESS) { + return status; + } + + /* We only need to store the same length of hash as the private key size + * here, it would be truncated by the internal implementation anyway. */ + required_hash_length = (hash_length < coordinate_bytes ? hash_length : + coordinate_bytes); + + if (required_hash_length > sizeof(operation->hash)) { + /* Shouldn't happen, but better safe than sorry. */ + return PSA_ERROR_CORRUPTION_DETECTED; + } + + memcpy(operation->hash, hash, required_hash_length); + operation->hash_length = required_hash_length; + + return PSA_SUCCESS; +#else + (void) operation; + (void) key_buffer; + (void) key_buffer_size; + (void) alg; + (void) hash; + (void) hash_length; + (void) signature; + (void) signature_length; + (void) status; + (void) coordinate_bytes; + (void) required_hash_length; + + return PSA_ERROR_NOT_SUPPORTED; +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +psa_status_t mbedtls_psa_verify_hash_complete( + mbedtls_psa_verify_hash_interruptible_operation_t *operation) +{ + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* Ensure max_ops is set to the current value (or default). */ + mbedtls_psa_interruptible_set_max_ops(psa_interruptible_get_max_ops()); + + status = mbedtls_to_psa_error( + mbedtls_ecdsa_verify_restartable(&operation->ctx->grp, + operation->hash, + operation->hash_length, + &operation->ctx->Q, + &operation->r, + &operation->s, + &operation->restart_ctx)); + + /* Hide the fact that the restart context only holds a delta of number of + * ops done during the last operation, not an absolute value. */ + operation->num_ops += operation->restart_ctx.ecp.ops_done; + + return status; +#else + (void) operation; + + return PSA_ERROR_NOT_SUPPORTED; + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +psa_status_t mbedtls_psa_verify_hash_abort( + mbedtls_psa_verify_hash_interruptible_operation_t *operation) +{ + +#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \ + defined(MBEDTLS_ECP_RESTARTABLE) + + if (operation->ctx) { + mbedtls_ecdsa_free(operation->ctx); + mbedtls_free(operation->ctx); + operation->ctx = NULL; + } + + mbedtls_ecdsa_restart_free(&operation->restart_ctx); + + operation->num_ops = 0; + + mbedtls_mpi_free(&operation->r); + mbedtls_mpi_free(&operation->s); + + return PSA_SUCCESS; + +#else + (void) operation; + + return PSA_ERROR_NOT_SUPPORTED; + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) && + * defined( MBEDTLS_ECP_RESTARTABLE ) */ +} + +/****************************************************************/ +/* Symmetric cryptography */ +/****************************************************************/ + +static psa_status_t psa_cipher_setup(psa_cipher_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + mbedtls_operation_t cipher_operation) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + psa_key_usage_t usage = (cipher_operation == MBEDTLS_ENCRYPT ? + PSA_KEY_USAGE_ENCRYPT : + PSA_KEY_USAGE_DECRYPT); + + /* A context must be freshly initialized before it can be set up. */ + if (operation->id != 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (!PSA_ALG_IS_CIPHER(alg)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, usage, alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* Initialize the operation struct members, except for id. The id member + * is used to indicate to psa_cipher_abort that there are resources to free, + * so we only set it (in the driver wrapper) after resources have been + * allocated/initialized. */ + operation->iv_set = 0; + if (alg == PSA_ALG_ECB_NO_PADDING) { + operation->iv_required = 0; + } else { + operation->iv_required = 1; + } + operation->default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg); + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + /* Try doing the operation through a driver before using software fallback. */ + if (cipher_operation == MBEDTLS_ENCRYPT) { + status = psa_driver_wrapper_cipher_encrypt_setup(operation, + &attributes, + slot->key.data, + slot->key.bytes, + alg); + } else { + status = psa_driver_wrapper_cipher_decrypt_setup(operation, + &attributes, + slot->key.data, + slot->key.bytes, + alg); + } + +exit: + if (status != PSA_SUCCESS) { + psa_cipher_abort(operation); + } + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg) +{ + return psa_cipher_setup(operation, key, alg, MBEDTLS_ENCRYPT); +} + +psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg) +{ + return psa_cipher_setup(operation, key, alg, MBEDTLS_DECRYPT); +} + +psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, + uint8_t *iv, + size_t iv_size, + size_t *iv_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + uint8_t local_iv[PSA_CIPHER_IV_MAX_SIZE]; + size_t default_iv_length; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->iv_set || !operation->iv_required) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + default_iv_length = operation->default_iv_length; + if (iv_size < default_iv_length) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + if (default_iv_length > PSA_CIPHER_IV_MAX_SIZE) { + status = PSA_ERROR_GENERIC_ERROR; + goto exit; + } + + status = psa_generate_random(local_iv, default_iv_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_driver_wrapper_cipher_set_iv(operation, + local_iv, default_iv_length); + +exit: + if (status == PSA_SUCCESS) { + memcpy(iv, local_iv, default_iv_length); + *iv_length = default_iv_length; + operation->iv_set = 1; + } else { + *iv_length = 0; + psa_cipher_abort(operation); + } + + return status; +} + +psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation, + const uint8_t *iv, + size_t iv_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->iv_set || !operation->iv_required) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (iv_length > PSA_CIPHER_IV_MAX_SIZE) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + status = psa_driver_wrapper_cipher_set_iv(operation, + iv, + iv_length); + +exit: + if (status == PSA_SUCCESS) { + operation->iv_set = 1; + } else { + psa_cipher_abort(operation); + } + return status; +} + +psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->iv_required && !operation->iv_set) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_driver_wrapper_cipher_update(operation, + input, + input_length, + output, + output_size, + output_length); + +exit: + if (status != PSA_SUCCESS) { + psa_cipher_abort(operation); + } + + return status; +} + +psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_GENERIC_ERROR; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->iv_required && !operation->iv_set) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_driver_wrapper_cipher_finish(operation, + output, + output_size, + output_length); + +exit: + if (status == PSA_SUCCESS) { + return psa_cipher_abort(operation); + } else { + *output_length = 0; + (void) psa_cipher_abort(operation); + + return status; + } +} + +psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation) +{ + if (operation->id == 0) { + /* The object has (apparently) been initialized but it is not (yet) + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + return PSA_SUCCESS; + } + + psa_driver_wrapper_cipher_abort(operation); + + operation->id = 0; + operation->iv_set = 0; + operation->iv_required = 0; + + return PSA_SUCCESS; +} + +psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + uint8_t local_iv[PSA_CIPHER_IV_MAX_SIZE]; + size_t default_iv_length = 0; + + if (!PSA_ALG_IS_CIPHER(alg)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, + PSA_KEY_USAGE_ENCRYPT, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg); + if (default_iv_length > PSA_CIPHER_IV_MAX_SIZE) { + status = PSA_ERROR_GENERIC_ERROR; + goto exit; + } + + if (default_iv_length > 0) { + if (output_size < default_iv_length) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + status = psa_generate_random(local_iv, default_iv_length); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + status = psa_driver_wrapper_cipher_encrypt( + &attributes, slot->key.data, slot->key.bytes, + alg, local_iv, default_iv_length, input, input_length, + mbedtls_buffer_offset(output, default_iv_length), + output_size - default_iv_length, output_length); + +exit: + unlock_status = psa_unlock_key_slot(slot); + if (status == PSA_SUCCESS) { + status = unlock_status; + } + + if (status == PSA_SUCCESS) { + if (default_iv_length > 0) { + memcpy(output, local_iv, default_iv_length); + } + *output_length += default_iv_length; + } else { + *output_length = 0; + } + + return status; +} + +psa_status_t psa_cipher_decrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + + if (!PSA_ALG_IS_CIPHER(alg)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, + PSA_KEY_USAGE_DECRYPT, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + if (alg == PSA_ALG_CCM_STAR_NO_TAG && + input_length < PSA_BLOCK_CIPHER_BLOCK_LENGTH(slot->attr.type)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } else if (input_length < PSA_CIPHER_IV_LENGTH(slot->attr.type, alg)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + status = psa_driver_wrapper_cipher_decrypt( + &attributes, slot->key.data, slot->key.bytes, + alg, input, input_length, + output, output_size, output_length); + +exit: + unlock_status = psa_unlock_key_slot(slot); + if (status == PSA_SUCCESS) { + status = unlock_status; + } + + if (status != PSA_SUCCESS) { + *output_length = 0; + } + + return status; +} + + +/****************************************************************/ +/* AEAD */ +/****************************************************************/ + +/* Helper function to get the base algorithm from its variants. */ +static psa_algorithm_t psa_aead_get_base_algorithm(psa_algorithm_t alg) +{ + return PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg); +} + +/* Helper function to perform common nonce length checks. */ +static psa_status_t psa_aead_check_nonce_length(psa_algorithm_t alg, + size_t nonce_length) +{ + psa_algorithm_t base_alg = psa_aead_get_base_algorithm(alg); + + switch (base_alg) { +#if defined(PSA_WANT_ALG_GCM) + case PSA_ALG_GCM: + /* Not checking max nonce size here as GCM spec allows almost + * arbitrarily large nonces. Please note that we do not generally + * recommend the usage of nonces of greater length than + * PSA_AEAD_NONCE_MAX_SIZE, as large nonces are hashed to a shorter + * size, which can then lead to collisions if you encrypt a very + * large number of messages.*/ + if (nonce_length != 0) { + return PSA_SUCCESS; + } + break; +#endif /* PSA_WANT_ALG_GCM */ +#if defined(PSA_WANT_ALG_CCM) + case PSA_ALG_CCM: + if (nonce_length >= 7 && nonce_length <= 13) { + return PSA_SUCCESS; + } + break; +#endif /* PSA_WANT_ALG_CCM */ +#if defined(PSA_WANT_ALG_CHACHA20_POLY1305) + case PSA_ALG_CHACHA20_POLY1305: + if (nonce_length == 12) { + return PSA_SUCCESS; + } else if (nonce_length == 8) { + return PSA_ERROR_NOT_SUPPORTED; + } + break; +#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */ + default: + (void) nonce_length; + return PSA_ERROR_NOT_SUPPORTED; + } + + return PSA_ERROR_INVALID_ARGUMENT; +} + +static psa_status_t psa_aead_check_algorithm(psa_algorithm_t alg) +{ + if (!PSA_ALG_IS_AEAD(alg) || PSA_ALG_IS_WILDCARD(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return PSA_SUCCESS; +} + +psa_status_t psa_aead_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *nonce, + size_t nonce_length, + const uint8_t *additional_data, + size_t additional_data_length, + const uint8_t *plaintext, + size_t plaintext_length, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + *ciphertext_length = 0; + + status = psa_aead_check_algorithm(alg); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_get_and_lock_key_slot_with_policy( + key, &slot, PSA_KEY_USAGE_ENCRYPT, alg); + if (status != PSA_SUCCESS) { + return status; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_aead_check_nonce_length(alg, nonce_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_driver_wrapper_aead_encrypt( + &attributes, slot->key.data, slot->key.bytes, + alg, + nonce, nonce_length, + additional_data, additional_data_length, + plaintext, plaintext_length, + ciphertext, ciphertext_size, ciphertext_length); + + if (status != PSA_SUCCESS && ciphertext_size != 0) { + memset(ciphertext, 0, ciphertext_size); + } + +exit: + psa_unlock_key_slot(slot); + + return status; +} + +psa_status_t psa_aead_decrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *nonce, + size_t nonce_length, + const uint8_t *additional_data, + size_t additional_data_length, + const uint8_t *ciphertext, + size_t ciphertext_length, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + *plaintext_length = 0; + + status = psa_aead_check_algorithm(alg); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_get_and_lock_key_slot_with_policy( + key, &slot, PSA_KEY_USAGE_DECRYPT, alg); + if (status != PSA_SUCCESS) { + return status; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_aead_check_nonce_length(alg, nonce_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_driver_wrapper_aead_decrypt( + &attributes, slot->key.data, slot->key.bytes, + alg, + nonce, nonce_length, + additional_data, additional_data_length, + ciphertext, ciphertext_length, + plaintext, plaintext_size, plaintext_length); + + if (status != PSA_SUCCESS && plaintext_size != 0) { + memset(plaintext, 0, plaintext_size); + } + +exit: + psa_unlock_key_slot(slot); + + return status; +} + +static psa_status_t psa_validate_tag_length(psa_algorithm_t alg) +{ + const uint8_t tag_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg); + + switch (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0)) { +#if defined(PSA_WANT_ALG_CCM) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0): + /* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.*/ + if (tag_len < 4 || tag_len > 16 || tag_len % 2) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif /* PSA_WANT_ALG_CCM */ + +#if defined(PSA_WANT_ALG_GCM) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0): + /* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16. */ + if (tag_len != 4 && tag_len != 8 && (tag_len < 12 || tag_len > 16)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif /* PSA_WANT_ALG_GCM */ + +#if defined(PSA_WANT_ALG_CHACHA20_POLY1305) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0): + /* We only support the default tag length. */ + if (tag_len != 16) { + return PSA_ERROR_INVALID_ARGUMENT; + } + break; +#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */ + + default: + (void) tag_len; + return PSA_ERROR_NOT_SUPPORTED; + } + return PSA_SUCCESS; +} + +/* Set the key for a multipart authenticated operation. */ +static psa_status_t psa_aead_setup(psa_aead_operation_t *operation, + int is_encrypt, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + psa_key_usage_t key_usage = 0; + + status = psa_aead_check_algorithm(alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (operation->id != 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->nonce_set || operation->lengths_set || + operation->ad_started || operation->body_started) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (is_encrypt) { + key_usage = PSA_KEY_USAGE_ENCRYPT; + } else { + key_usage = PSA_KEY_USAGE_DECRYPT; + } + + status = psa_get_and_lock_key_slot_with_policy(key, &slot, key_usage, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + if ((status = psa_validate_tag_length(alg)) != PSA_SUCCESS) { + goto exit; + } + + if (is_encrypt) { + status = psa_driver_wrapper_aead_encrypt_setup(operation, + &attributes, + slot->key.data, + slot->key.bytes, + alg); + } else { + status = psa_driver_wrapper_aead_decrypt_setup(operation, + &attributes, + slot->key.data, + slot->key.bytes, + alg); + } + if (status != PSA_SUCCESS) { + goto exit; + } + + operation->key_type = psa_get_key_type(&attributes); + +exit: + unlock_status = psa_unlock_key_slot(slot); + + if (status == PSA_SUCCESS) { + status = unlock_status; + operation->alg = psa_aead_get_base_algorithm(alg); + operation->is_encrypt = is_encrypt; + } else { + psa_aead_abort(operation); + } + + return status; +} + +/* Set the key for a multipart authenticated encryption operation. */ +psa_status_t psa_aead_encrypt_setup(psa_aead_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg) +{ + return psa_aead_setup(operation, 1, key, alg); +} + +/* Set the key for a multipart authenticated decryption operation. */ +psa_status_t psa_aead_decrypt_setup(psa_aead_operation_t *operation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg) +{ + return psa_aead_setup(operation, 0, key, alg); +} + +/* Generate a random nonce / IV for multipart AEAD operation */ +psa_status_t psa_aead_generate_nonce(psa_aead_operation_t *operation, + uint8_t *nonce, + size_t nonce_size, + size_t *nonce_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + uint8_t local_nonce[PSA_AEAD_NONCE_MAX_SIZE]; + size_t required_nonce_size; + + *nonce_length = 0; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->nonce_set || !operation->is_encrypt) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + /* For CCM, this size may not be correct according to the PSA + * specification. The PSA Crypto 1.0.1 specification states: + * + * CCM encodes the plaintext length pLen in L octets, with L the smallest + * integer >= 2 where pLen < 2^(8L). The nonce length is then 15 - L bytes. + * + * However this restriction that L has to be the smallest integer is not + * applied in practice, and it is not implementable here since the + * plaintext length may or may not be known at this time. */ + required_nonce_size = PSA_AEAD_NONCE_LENGTH(operation->key_type, + operation->alg); + if (nonce_size < required_nonce_size) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + status = psa_generate_random(local_nonce, required_nonce_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_aead_set_nonce(operation, local_nonce, required_nonce_size); + +exit: + if (status == PSA_SUCCESS) { + memcpy(nonce, local_nonce, required_nonce_size); + *nonce_length = required_nonce_size; + } else { + psa_aead_abort(operation); + } + + return status; +} + +/* Set the nonce for a multipart authenticated encryption or decryption + operation.*/ +psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation, + const uint8_t *nonce, + size_t nonce_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->nonce_set) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_aead_check_nonce_length(operation->alg, nonce_length); + if (status != PSA_SUCCESS) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + status = psa_driver_wrapper_aead_set_nonce(operation, nonce, + nonce_length); + +exit: + if (status == PSA_SUCCESS) { + operation->nonce_set = 1; + } else { + psa_aead_abort(operation); + } + + return status; +} + +/* Declare the lengths of the message and additional data for multipart AEAD. */ +psa_status_t psa_aead_set_lengths(psa_aead_operation_t *operation, + size_t ad_length, + size_t plaintext_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->lengths_set || operation->ad_started || + operation->body_started) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + switch (operation->alg) { +#if defined(PSA_WANT_ALG_GCM) + case PSA_ALG_GCM: + /* Lengths can only be too large for GCM if size_t is bigger than 32 + * bits. Without the guard this code will generate warnings on 32bit + * builds. */ +#if SIZE_MAX > UINT32_MAX + if (((uint64_t) ad_length) >> 61 != 0 || + ((uint64_t) plaintext_length) > 0xFFFFFFFE0ull) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } +#endif + break; +#endif /* PSA_WANT_ALG_GCM */ +#if defined(PSA_WANT_ALG_CCM) + case PSA_ALG_CCM: + if (ad_length > 0xFF00) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + break; +#endif /* PSA_WANT_ALG_CCM */ +#if defined(PSA_WANT_ALG_CHACHA20_POLY1305) + case PSA_ALG_CHACHA20_POLY1305: + /* No length restrictions for ChaChaPoly. */ + break; +#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */ + default: + break; + } + + status = psa_driver_wrapper_aead_set_lengths(operation, ad_length, + plaintext_length); + +exit: + if (status == PSA_SUCCESS) { + operation->ad_remaining = ad_length; + operation->body_remaining = plaintext_length; + operation->lengths_set = 1; + } else { + psa_aead_abort(operation); + } + + return status; +} + +/* Pass additional data to an active multipart AEAD operation. */ +psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (!operation->nonce_set || operation->body_started) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->lengths_set) { + if (operation->ad_remaining < input_length) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + operation->ad_remaining -= input_length; + } +#if defined(PSA_WANT_ALG_CCM) + else if (operation->alg == PSA_ALG_CCM) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } +#endif /* PSA_WANT_ALG_CCM */ + + status = psa_driver_wrapper_aead_update_ad(operation, input, + input_length); + +exit: + if (status == PSA_SUCCESS) { + operation->ad_started = 1; + } else { + psa_aead_abort(operation); + } + + return status; +} + +/* Encrypt or decrypt a message fragment in an active multipart AEAD + operation.*/ +psa_status_t psa_aead_update(psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + *output_length = 0; + + if (operation->id == 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (!operation->nonce_set) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (operation->lengths_set) { + /* Additional data length was supplied, but not all the additional + data was supplied.*/ + if (operation->ad_remaining != 0) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + /* Too much data provided. */ + if (operation->body_remaining < input_length) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + operation->body_remaining -= input_length; + } +#if defined(PSA_WANT_ALG_CCM) + else if (operation->alg == PSA_ALG_CCM) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } +#endif /* PSA_WANT_ALG_CCM */ + + status = psa_driver_wrapper_aead_update(operation, input, input_length, + output, output_size, + output_length); + +exit: + if (status == PSA_SUCCESS) { + operation->body_started = 1; + } else { + psa_aead_abort(operation); + } + + return status; +} + +static psa_status_t psa_aead_final_checks(const psa_aead_operation_t *operation) +{ + if (operation->id == 0 || !operation->nonce_set) { + return PSA_ERROR_BAD_STATE; + } + + if (operation->lengths_set && (operation->ad_remaining != 0 || + operation->body_remaining != 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return PSA_SUCCESS; +} + +/* Finish encrypting a message in a multipart AEAD operation. */ +psa_status_t psa_aead_finish(psa_aead_operation_t *operation, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length, + uint8_t *tag, + size_t tag_size, + size_t *tag_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + *ciphertext_length = 0; + *tag_length = tag_size; + + status = psa_aead_final_checks(operation); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (!operation->is_encrypt) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_driver_wrapper_aead_finish(operation, ciphertext, + ciphertext_size, + ciphertext_length, + tag, tag_size, tag_length); + +exit: + + + /* In case the operation fails and the user fails to check for failure or + * the zero tag size, make sure the tag is set to something implausible. + * Even if the operation succeeds, make sure we clear the rest of the + * buffer to prevent potential leakage of anything previously placed in + * the same buffer.*/ + psa_wipe_tag_output_buffer(tag, status, tag_size, *tag_length); + + psa_aead_abort(operation); + + return status; +} + +/* Finish authenticating and decrypting a message in a multipart AEAD + operation.*/ +psa_status_t psa_aead_verify(psa_aead_operation_t *operation, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length, + const uint8_t *tag, + size_t tag_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + *plaintext_length = 0; + + status = psa_aead_final_checks(operation); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (operation->is_encrypt) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_driver_wrapper_aead_verify(operation, plaintext, + plaintext_size, + plaintext_length, + tag, tag_length); + +exit: + psa_aead_abort(operation); + + return status; +} + +/* Abort an AEAD operation. */ +psa_status_t psa_aead_abort(psa_aead_operation_t *operation) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->id == 0) { + /* The object has (apparently) been initialized but it is not (yet) + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + return PSA_SUCCESS; + } + + status = psa_driver_wrapper_aead_abort(operation); + + memset(operation, 0, sizeof(*operation)); + + return status; +} + +/****************************************************************/ +/* Generators */ +/****************************************************************/ + +#if defined(BUILTIN_ALG_ANY_HKDF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) +#define AT_LEAST_ONE_BUILTIN_KDF +#endif /* At least one builtin KDF */ + +#if defined(BUILTIN_ALG_ANY_HKDF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) +static psa_status_t psa_key_derivation_start_hmac( + psa_mac_operation_t *operation, + psa_algorithm_t hash_alg, + const uint8_t *hmac_key, + size_t hmac_key_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(hmac_key_length)); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH); + + operation->is_sign = 1; + operation->mac_size = PSA_HASH_LENGTH(hash_alg); + + status = psa_driver_wrapper_mac_sign_setup(operation, + &attributes, + hmac_key, hmac_key_length, + PSA_ALG_HMAC(hash_alg)); + + psa_reset_key_attributes(&attributes); + return status; +} +#endif /* KDF algorithms reliant on HMAC */ + +#define HKDF_STATE_INIT 0 /* no input yet */ +#define HKDF_STATE_STARTED 1 /* got salt */ +#define HKDF_STATE_KEYED 2 /* got key */ +#define HKDF_STATE_OUTPUT 3 /* output started */ + +static psa_algorithm_t psa_key_derivation_get_kdf_alg( + const psa_key_derivation_operation_t *operation) +{ + if (PSA_ALG_IS_KEY_AGREEMENT(operation->alg)) { + return PSA_ALG_KEY_AGREEMENT_GET_KDF(operation->alg); + } else { + return operation->alg; + } +} + +psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t *operation) +{ + psa_status_t status = PSA_SUCCESS; + psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation); + if (kdf_alg == 0) { + /* The object has (apparently) been initialized but it is not + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + } else +#if defined(BUILTIN_ALG_ANY_HKDF) + if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) { + mbedtls_free(operation->ctx.hkdf.info); + status = psa_mac_abort(&operation->ctx.hkdf.hmac); + } else +#endif /* BUILTIN_ALG_ANY_HKDF */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) + if (PSA_ALG_IS_TLS12_PRF(kdf_alg) || + /* TLS-1.2 PSK-to-MS KDF uses the same core as TLS-1.2 PRF */ + PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) { + if (operation->ctx.tls12_prf.secret != NULL) { + mbedtls_platform_zeroize(operation->ctx.tls12_prf.secret, + operation->ctx.tls12_prf.secret_length); + mbedtls_free(operation->ctx.tls12_prf.secret); + } + + if (operation->ctx.tls12_prf.seed != NULL) { + mbedtls_platform_zeroize(operation->ctx.tls12_prf.seed, + operation->ctx.tls12_prf.seed_length); + mbedtls_free(operation->ctx.tls12_prf.seed); + } + + if (operation->ctx.tls12_prf.label != NULL) { + mbedtls_platform_zeroize(operation->ctx.tls12_prf.label, + operation->ctx.tls12_prf.label_length); + mbedtls_free(operation->ctx.tls12_prf.label); + } +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) + if (operation->ctx.tls12_prf.other_secret != NULL) { + mbedtls_platform_zeroize(operation->ctx.tls12_prf.other_secret, + operation->ctx.tls12_prf.other_secret_length); + mbedtls_free(operation->ctx.tls12_prf.other_secret); + } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ + status = PSA_SUCCESS; + + /* We leave the fields Ai and output_block to be erased safely by the + * mbedtls_platform_zeroize() in the end of this function. */ + } else +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) + if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) { + mbedtls_platform_zeroize(operation->ctx.tls12_ecjpake_to_pms.data, + sizeof(operation->ctx.tls12_ecjpake_to_pms.data)); + } else +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) */ + { + status = PSA_ERROR_BAD_STATE; + } + mbedtls_platform_zeroize(operation, sizeof(*operation)); + return status; +} + +psa_status_t psa_key_derivation_get_capacity(const psa_key_derivation_operation_t *operation, + size_t *capacity) +{ + if (operation->alg == 0) { + /* This is a blank key derivation operation. */ + return PSA_ERROR_BAD_STATE; + } + + *capacity = operation->capacity; + return PSA_SUCCESS; +} + +psa_status_t psa_key_derivation_set_capacity(psa_key_derivation_operation_t *operation, + size_t capacity) +{ + if (operation->alg == 0) { + return PSA_ERROR_BAD_STATE; + } + if (capacity > operation->capacity) { + return PSA_ERROR_INVALID_ARGUMENT; + } + operation->capacity = capacity; + return PSA_SUCCESS; +} + +#if defined(BUILTIN_ALG_ANY_HKDF) +/* Read some bytes from an HKDF-based operation. */ +static psa_status_t psa_key_derivation_hkdf_read(psa_hkdf_key_derivation_t *hkdf, + psa_algorithm_t kdf_alg, + uint8_t *output, + size_t output_length) +{ + psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg); + uint8_t hash_length = PSA_HASH_LENGTH(hash_alg); + size_t hmac_output_length; + psa_status_t status; +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) + const uint8_t last_block = PSA_ALG_IS_HKDF_EXTRACT(kdf_alg) ? 0 : 0xff; +#else + const uint8_t last_block = 0xff; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */ + + if (hkdf->state < HKDF_STATE_KEYED || + (!hkdf->info_set +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) + && !PSA_ALG_IS_HKDF_EXTRACT(kdf_alg) +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */ + )) { + return PSA_ERROR_BAD_STATE; + } + hkdf->state = HKDF_STATE_OUTPUT; + + while (output_length != 0) { + /* Copy what remains of the current block */ + uint8_t n = hash_length - hkdf->offset_in_block; + if (n > output_length) { + n = (uint8_t) output_length; + } + memcpy(output, hkdf->output_block + hkdf->offset_in_block, n); + output += n; + output_length -= n; + hkdf->offset_in_block += n; + if (output_length == 0) { + break; + } + /* We can't be wanting more output after the last block, otherwise + * the capacity check in psa_key_derivation_output_bytes() would have + * prevented this call. It could happen only if the operation + * object was corrupted or if this function is called directly + * inside the library. */ + if (hkdf->block_number == last_block) { + return PSA_ERROR_BAD_STATE; + } + + /* We need a new block */ + ++hkdf->block_number; + hkdf->offset_in_block = 0; + + status = psa_key_derivation_start_hmac(&hkdf->hmac, + hash_alg, + hkdf->prk, + hash_length); + if (status != PSA_SUCCESS) { + return status; + } + + if (hkdf->block_number != 1) { + status = psa_mac_update(&hkdf->hmac, + hkdf->output_block, + hash_length); + if (status != PSA_SUCCESS) { + return status; + } + } + status = psa_mac_update(&hkdf->hmac, + hkdf->info, + hkdf->info_length); + if (status != PSA_SUCCESS) { + return status; + } + status = psa_mac_update(&hkdf->hmac, + &hkdf->block_number, 1); + if (status != PSA_SUCCESS) { + return status; + } + status = psa_mac_sign_finish(&hkdf->hmac, + hkdf->output_block, + sizeof(hkdf->output_block), + &hmac_output_length); + if (status != PSA_SUCCESS) { + return status; + } + } + + return PSA_SUCCESS; +} +#endif /* BUILTIN_ALG_ANY_HKDF */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) +static psa_status_t psa_key_derivation_tls12_prf_generate_next_block( + psa_tls12_prf_key_derivation_t *tls12_prf, + psa_algorithm_t alg) +{ + psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(alg); + uint8_t hash_length = PSA_HASH_LENGTH(hash_alg); + psa_mac_operation_t hmac = PSA_MAC_OPERATION_INIT; + size_t hmac_output_length; + psa_status_t status, cleanup_status; + + /* We can't be wanting more output after block 0xff, otherwise + * the capacity check in psa_key_derivation_output_bytes() would have + * prevented this call. It could happen only if the operation + * object was corrupted or if this function is called directly + * inside the library. */ + if (tls12_prf->block_number == 0xff) { + return PSA_ERROR_CORRUPTION_DETECTED; + } + + /* We need a new block */ + ++tls12_prf->block_number; + tls12_prf->left_in_block = hash_length; + + /* Recall the definition of the TLS-1.2-PRF from RFC 5246: + * + * PRF(secret, label, seed) = P_(secret, label + seed) + * + * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + + * HMAC_hash(secret, A(2) + seed) + + * HMAC_hash(secret, A(3) + seed) + ... + * + * A(0) = seed + * A(i) = HMAC_hash(secret, A(i-1)) + * + * The `psa_tls12_prf_key_derivation` structure saves the block + * `HMAC_hash(secret, A(i) + seed)` from which the output + * is currently extracted as `output_block` and where i is + * `block_number`. + */ + + status = psa_key_derivation_start_hmac(&hmac, + hash_alg, + tls12_prf->secret, + tls12_prf->secret_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + + /* Calculate A(i) where i = tls12_prf->block_number. */ + if (tls12_prf->block_number == 1) { + /* A(1) = HMAC_hash(secret, A(0)), where A(0) = seed. (The RFC overloads + * the variable seed and in this instance means it in the context of the + * P_hash function, where seed = label + seed.) */ + status = psa_mac_update(&hmac, + tls12_prf->label, + tls12_prf->label_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + status = psa_mac_update(&hmac, + tls12_prf->seed, + tls12_prf->seed_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + } else { + /* A(i) = HMAC_hash(secret, A(i-1)) */ + status = psa_mac_update(&hmac, tls12_prf->Ai, hash_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + } + + status = psa_mac_sign_finish(&hmac, + tls12_prf->Ai, hash_length, + &hmac_output_length); + if (hmac_output_length != hash_length) { + status = PSA_ERROR_CORRUPTION_DETECTED; + } + if (status != PSA_SUCCESS) { + goto cleanup; + } + + /* Calculate HMAC_hash(secret, A(i) + label + seed). */ + status = psa_key_derivation_start_hmac(&hmac, + hash_alg, + tls12_prf->secret, + tls12_prf->secret_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + status = psa_mac_update(&hmac, tls12_prf->Ai, hash_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + status = psa_mac_update(&hmac, tls12_prf->label, tls12_prf->label_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + status = psa_mac_update(&hmac, tls12_prf->seed, tls12_prf->seed_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + status = psa_mac_sign_finish(&hmac, + tls12_prf->output_block, hash_length, + &hmac_output_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + + +cleanup: + cleanup_status = psa_mac_abort(&hmac); + if (status == PSA_SUCCESS && cleanup_status != PSA_SUCCESS) { + status = cleanup_status; + } + + return status; +} + +static psa_status_t psa_key_derivation_tls12_prf_read( + psa_tls12_prf_key_derivation_t *tls12_prf, + psa_algorithm_t alg, + uint8_t *output, + size_t output_length) +{ + psa_algorithm_t hash_alg = PSA_ALG_TLS12_PRF_GET_HASH(alg); + uint8_t hash_length = PSA_HASH_LENGTH(hash_alg); + psa_status_t status; + uint8_t offset, length; + + switch (tls12_prf->state) { + case PSA_TLS12_PRF_STATE_LABEL_SET: + tls12_prf->state = PSA_TLS12_PRF_STATE_OUTPUT; + break; + case PSA_TLS12_PRF_STATE_OUTPUT: + break; + default: + return PSA_ERROR_BAD_STATE; + } + + while (output_length != 0) { + /* Check if we have fully processed the current block. */ + if (tls12_prf->left_in_block == 0) { + status = psa_key_derivation_tls12_prf_generate_next_block(tls12_prf, + alg); + if (status != PSA_SUCCESS) { + return status; + } + + continue; + } + + if (tls12_prf->left_in_block > output_length) { + length = (uint8_t) output_length; + } else { + length = tls12_prf->left_in_block; + } + + offset = hash_length - tls12_prf->left_in_block; + memcpy(output, tls12_prf->output_block + offset, length); + output += length; + output_length -= length; + tls12_prf->left_in_block -= length; + } + + return PSA_SUCCESS; +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF || + * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) +static psa_status_t psa_key_derivation_tls12_ecjpake_to_pms_read( + psa_tls12_ecjpake_to_pms_t *ecjpake, + uint8_t *output, + size_t output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t output_size = 0; + + if (output_length != 32) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_hash_compute(PSA_ALG_SHA_256, ecjpake->data, + PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE, output, output_length, + &output_size); + if (status != PSA_SUCCESS) { + return status; + } + + if (output_size != output_length) { + return PSA_ERROR_GENERIC_ERROR; + } + + return PSA_SUCCESS; +} +#endif + +psa_status_t psa_key_derivation_output_bytes( + psa_key_derivation_operation_t *operation, + uint8_t *output, + size_t output_length) +{ + psa_status_t status; + psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation); + + if (operation->alg == 0) { + /* This is a blank operation. */ + return PSA_ERROR_BAD_STATE; + } + + if (output_length > operation->capacity) { + operation->capacity = 0; + /* Go through the error path to wipe all confidential data now + * that the operation object is useless. */ + status = PSA_ERROR_INSUFFICIENT_DATA; + goto exit; + } + if (output_length == 0 && operation->capacity == 0) { + /* Edge case: this is a finished operation, and 0 bytes + * were requested. The right error in this case could + * be either INSUFFICIENT_CAPACITY or BAD_STATE. Return + * INSUFFICIENT_CAPACITY, which is right for a finished + * operation, for consistency with the case when + * output_length > 0. */ + return PSA_ERROR_INSUFFICIENT_DATA; + } + operation->capacity -= output_length; + +#if defined(BUILTIN_ALG_ANY_HKDF) + if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) { + status = psa_key_derivation_hkdf_read(&operation->ctx.hkdf, kdf_alg, + output, output_length); + } else +#endif /* BUILTIN_ALG_ANY_HKDF */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) + if (PSA_ALG_IS_TLS12_PRF(kdf_alg) || + PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) { + status = psa_key_derivation_tls12_prf_read(&operation->ctx.tls12_prf, + kdf_alg, output, + output_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF || + * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) + if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) { + status = psa_key_derivation_tls12_ecjpake_to_pms_read( + &operation->ctx.tls12_ecjpake_to_pms, output, output_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */ + + { + (void) kdf_alg; + return PSA_ERROR_BAD_STATE; + } + +exit: + if (status != PSA_SUCCESS) { + /* Preserve the algorithm upon errors, but clear all sensitive state. + * This allows us to differentiate between exhausted operations and + * blank operations, so we can return PSA_ERROR_BAD_STATE on blank + * operations. */ + psa_algorithm_t alg = operation->alg; + psa_key_derivation_abort(operation); + operation->alg = alg; + memset(output, '!', output_length); + } + return status; +} + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) +static void psa_des_set_key_parity(uint8_t *data, size_t data_size) +{ + if (data_size >= 8) { + mbedtls_des_key_set_parity(data); + } + if (data_size >= 16) { + mbedtls_des_key_set_parity(data + 8); + } + if (data_size >= 24) { + mbedtls_des_key_set_parity(data + 16); + } +} +#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES */ + +/* + * ECC keys on a Weierstrass elliptic curve require the generation + * of a private key which is an integer + * in the range [1, N - 1], where N is the boundary of the private key domain: + * N is the prime p for Diffie-Hellman, or the order of the + * curve’s base point for ECC. + * + * Let m be the bit size of N, such that 2^m > N >= 2^(m-1). + * This function generates the private key using the following process: + * + * 1. Draw a byte string of length ceiling(m/8) bytes. + * 2. If m is not a multiple of 8, set the most significant + * (8 * ceiling(m/8) - m) bits of the first byte in the string to zero. + * 3. Convert the string to integer k by decoding it as a big-endian byte string. + * 4. If k > N - 2, discard the result and return to step 1. + * 5. Output k + 1 as the private key. + * + * This method allows compliance to NIST standards, specifically the methods titled + * Key-Pair Generation by Testing Candidates in the following publications: + * - NIST Special Publication 800-56A: Recommendation for Pair-Wise Key-Establishment + * Schemes Using Discrete Logarithm Cryptography [SP800-56A] §5.6.1.1.4 for + * Diffie-Hellman keys. + * + * - [SP800-56A] §5.6.1.2.2 or FIPS Publication 186-4: Digital Signature + * Standard (DSS) [FIPS186-4] §B.4.2 for elliptic curve keys. + * + * Note: Function allocates memory for *data buffer, so given *data should be + * always NULL. + */ +#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) +static psa_status_t psa_generate_derived_ecc_key_weierstrass_helper( + psa_key_slot_t *slot, + size_t bits, + psa_key_derivation_operation_t *operation, + uint8_t **data + ) +{ +#if defined(MBEDTLS_ECP_C) + unsigned key_out_of_range = 1; + mbedtls_mpi k; + mbedtls_mpi diff_N_2; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + mbedtls_mpi_init(&k); + mbedtls_mpi_init(&diff_N_2); + + psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY( + slot->attr.type); + mbedtls_ecp_group_id grp_id = + mbedtls_ecc_group_of_psa(curve, bits, 0); + + if (grp_id == MBEDTLS_ECP_DP_NONE) { + ret = MBEDTLS_ERR_ASN1_INVALID_DATA; + goto cleanup; + } + + mbedtls_ecp_group ecp_group; + mbedtls_ecp_group_init(&ecp_group); + + MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&ecp_group, grp_id)); + + /* N is the boundary of the private key domain (ecp_group.N). */ + /* Let m be the bit size of N. */ + size_t m = ecp_group.nbits; + + size_t m_bytes = PSA_BITS_TO_BYTES(m); + + /* Calculate N - 2 - it will be needed later. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&diff_N_2, &ecp_group.N, 2)); + + /* Note: This function is always called with *data == NULL and it + * allocates memory for the data buffer. */ + *data = mbedtls_calloc(1, m_bytes); + if (*data == NULL) { + ret = MBEDTLS_ERR_ASN1_ALLOC_FAILED; + goto cleanup; + } + + while (key_out_of_range) { + /* 1. Draw a byte string of length ceiling(m/8) bytes. */ + if ((status = psa_key_derivation_output_bytes(operation, *data, m_bytes)) != 0) { + goto cleanup; + } + + /* 2. If m is not a multiple of 8 */ + if (m % 8 != 0) { + /* Set the most significant + * (8 * ceiling(m/8) - m) bits of the first byte in + * the string to zero. + */ + uint8_t clear_bit_mask = (1 << (m % 8)) - 1; + (*data)[0] &= clear_bit_mask; + } + + /* 3. Convert the string to integer k by decoding it as a + * big-endian byte string. + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&k, *data, m_bytes)); + + /* 4. If k > N - 2, discard the result and return to step 1. + * Result of comparison is returned. When it indicates error + * then this function is called again. + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_lt_mpi_ct(&diff_N_2, &k, &key_out_of_range)); + } + + /* 5. Output k + 1 as the private key. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&k, &k, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&k, *data, m_bytes)); +cleanup: + if (ret != 0) { + status = mbedtls_to_psa_error(ret); + } + if (status != PSA_SUCCESS) { + mbedtls_free(*data); + *data = NULL; + } + mbedtls_mpi_free(&k); + mbedtls_mpi_free(&diff_N_2); + return status; +#else /* MBEDTLS_ECP_C */ + (void) slot; + (void) bits; + (void) operation; + (void) data; + return PSA_ERROR_NOT_SUPPORTED; +#endif /* MBEDTLS_ECP_C */ +} + +/* ECC keys on a Montgomery elliptic curve draws a byte string whose length + * is determined by the curve, and sets the mandatory bits accordingly. That is: + * + * - Curve25519 (PSA_ECC_FAMILY_MONTGOMERY, 255 bits): + * draw a 32-byte string and process it as specified in + * Elliptic Curves for Security [RFC7748] §5. + * + * - Curve448 (PSA_ECC_FAMILY_MONTGOMERY, 448 bits): + * draw a 56-byte string and process it as specified in [RFC7748] §5. + * + * Note: Function allocates memory for *data buffer, so given *data should be + * always NULL. + */ + +static psa_status_t psa_generate_derived_ecc_key_montgomery_helper( + size_t bits, + psa_key_derivation_operation_t *operation, + uint8_t **data + ) +{ + size_t output_length; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + switch (bits) { + case 255: + output_length = 32; + break; + case 448: + output_length = 56; + break; + default: + return PSA_ERROR_INVALID_ARGUMENT; + break; + } + + *data = mbedtls_calloc(1, output_length); + + if (*data == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + status = psa_key_derivation_output_bytes(operation, *data, output_length); + + if (status != PSA_SUCCESS) { + return status; + } + + switch (bits) { + case 255: + (*data)[0] &= 248; + (*data)[31] &= 127; + (*data)[31] |= 64; + break; + case 448: + (*data)[0] &= 252; + (*data)[55] |= 128; + break; + default: + return PSA_ERROR_CORRUPTION_DETECTED; + break; + } + + return status; +} +#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) || + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) */ + +static psa_status_t psa_generate_derived_key_internal( + psa_key_slot_t *slot, + size_t bits, + psa_key_derivation_operation_t *operation) +{ + uint8_t *data = NULL; + size_t bytes = PSA_BITS_TO_BYTES(bits); + size_t storage_size = bytes; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + +#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) + if (PSA_KEY_TYPE_IS_ECC(slot->attr.type)) { + psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(slot->attr.type); + if (PSA_ECC_FAMILY_IS_WEIERSTRASS(curve)) { + /* Weierstrass elliptic curve */ + status = psa_generate_derived_ecc_key_weierstrass_helper(slot, bits, operation, &data); + if (status != PSA_SUCCESS) { + goto exit; + } + } else { + /* Montgomery elliptic curve */ + status = psa_generate_derived_ecc_key_montgomery_helper(bits, operation, &data); + if (status != PSA_SUCCESS) { + goto exit; + } + } + } else +#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) || + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) || + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) */ + if (key_type_is_raw_bytes(slot->attr.type)) { + if (bits % 8 != 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + data = mbedtls_calloc(1, bytes); + if (data == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + status = psa_key_derivation_output_bytes(operation, data, bytes); + if (status != PSA_SUCCESS) { + goto exit; + } +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) + if (slot->attr.type == PSA_KEY_TYPE_DES) { + psa_des_set_key_parity(data, bytes); + } +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) */ + } else { + return PSA_ERROR_NOT_SUPPORTED; + } + + slot->attr.bits = (psa_key_bits_t) bits; + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + if (psa_key_lifetime_is_external(attributes.core.lifetime)) { + status = psa_driver_wrapper_get_key_buffer_size(&attributes, + &storage_size); + if (status != PSA_SUCCESS) { + goto exit; + } + } + status = psa_allocate_buffer_to_slot(slot, storage_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_driver_wrapper_import_key(&attributes, + data, bytes, + slot->key.data, + slot->key.bytes, + &slot->key.bytes, &bits); + if (bits != slot->attr.bits) { + status = PSA_ERROR_INVALID_ARGUMENT; + } + +exit: + mbedtls_free(data); + return status; +} + +psa_status_t psa_key_derivation_output_key(const psa_key_attributes_t *attributes, + psa_key_derivation_operation_t *operation, + mbedtls_svc_key_id_t *key) +{ + psa_status_t status; + psa_key_slot_t *slot = NULL; + psa_se_drv_table_entry_t *driver = NULL; + + *key = MBEDTLS_SVC_KEY_ID_INIT; + + /* Reject any attempt to create a zero-length key so that we don't + * risk tripping up later, e.g. on a malloc(0) that returns NULL. */ + if (psa_get_key_bits(attributes) == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (operation->alg == PSA_ALG_NONE) { + return PSA_ERROR_BAD_STATE; + } + + if (!operation->can_output_key) { + return PSA_ERROR_NOT_PERMITTED; + } + + status = psa_start_key_creation(PSA_KEY_CREATION_DERIVE, attributes, + &slot, &driver); +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + if (driver != NULL) { + /* Deriving a key in a secure element is not implemented yet. */ + status = PSA_ERROR_NOT_SUPPORTED; + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + if (status == PSA_SUCCESS) { + status = psa_generate_derived_key_internal(slot, + attributes->core.bits, + operation); + } + if (status == PSA_SUCCESS) { + status = psa_finish_key_creation(slot, driver, key); + } + if (status != PSA_SUCCESS) { + psa_fail_key_creation(slot, driver); + } + + return status; +} + + + +/****************************************************************/ +/* Key derivation */ +/****************************************************************/ + +#if defined(AT_LEAST_ONE_BUILTIN_KDF) +static int is_kdf_alg_supported(psa_algorithm_t kdf_alg) +{ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) + if (PSA_ALG_IS_HKDF(kdf_alg)) { + return 1; + } +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) + if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) { + return 1; + } +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) + if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) { + return 1; + } +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) + if (PSA_ALG_IS_TLS12_PRF(kdf_alg)) { + return 1; + } +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) + if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) { + return 1; + } +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) + if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) { + return 1; + } +#endif + return 0; +} + +static psa_status_t psa_hash_try_support(psa_algorithm_t alg) +{ + psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; + psa_status_t status = psa_hash_setup(&operation, alg); + psa_hash_abort(&operation); + return status; +} + +static psa_status_t psa_key_derivation_setup_kdf( + psa_key_derivation_operation_t *operation, + psa_algorithm_t kdf_alg) +{ + /* Make sure that operation->ctx is properly zero-initialised. (Macro + * initialisers for this union leave some bytes unspecified.) */ + memset(&operation->ctx, 0, sizeof(operation->ctx)); + + /* Make sure that kdf_alg is a supported key derivation algorithm. */ + if (!is_kdf_alg_supported(kdf_alg)) { + return PSA_ERROR_NOT_SUPPORTED; + } + + /* All currently supported key derivation algorithms (apart from + * ecjpake to pms) are based on a hash algorithm. */ + psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg); + size_t hash_size = PSA_HASH_LENGTH(hash_alg); + if (kdf_alg != PSA_ALG_TLS12_ECJPAKE_TO_PMS) { + if (hash_size == 0) { + return PSA_ERROR_NOT_SUPPORTED; + } + + /* Make sure that hash_alg is a supported hash algorithm. Otherwise + * we might fail later, which is somewhat unfriendly and potentially + * risk-prone. */ + psa_status_t status = psa_hash_try_support(hash_alg); + if (status != PSA_SUCCESS) { + return status; + } + } else { + hash_size = PSA_HASH_LENGTH(PSA_ALG_SHA_256); + } + + if ((PSA_ALG_IS_TLS12_PRF(kdf_alg) || + PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) && + !(hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) { + return PSA_ERROR_NOT_SUPPORTED; + } +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) + if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg) || + (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS)) { + operation->capacity = hash_size; + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT || + MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */ + operation->capacity = 255 * hash_size; + return PSA_SUCCESS; +} + +static psa_status_t psa_key_agreement_try_support(psa_algorithm_t alg) +{ +#if defined(PSA_WANT_ALG_ECDH) + if (alg == PSA_ALG_ECDH) { + return PSA_SUCCESS; + } +#endif + (void) alg; + return PSA_ERROR_NOT_SUPPORTED; +} + +static int psa_key_derivation_allows_free_form_secret_input( + psa_algorithm_t kdf_alg) +{ +#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS) + if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) { + return 0; + } +#endif + (void) kdf_alg; + return 1; +} +#endif /* AT_LEAST_ONE_BUILTIN_KDF */ + +psa_status_t psa_key_derivation_setup(psa_key_derivation_operation_t *operation, + psa_algorithm_t alg) +{ + psa_status_t status; + + if (operation->alg != 0) { + return PSA_ERROR_BAD_STATE; + } + + if (PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } else if (PSA_ALG_IS_KEY_AGREEMENT(alg)) { +#if defined(AT_LEAST_ONE_BUILTIN_KDF) + psa_algorithm_t kdf_alg = PSA_ALG_KEY_AGREEMENT_GET_KDF(alg); + psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE(alg); + status = psa_key_agreement_try_support(ka_alg); + if (status != PSA_SUCCESS) { + return status; + } + if (!psa_key_derivation_allows_free_form_secret_input(kdf_alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + status = psa_key_derivation_setup_kdf(operation, kdf_alg); +#else + return PSA_ERROR_NOT_SUPPORTED; +#endif /* AT_LEAST_ONE_BUILTIN_KDF */ + } else if (PSA_ALG_IS_KEY_DERIVATION(alg)) { +#if defined(AT_LEAST_ONE_BUILTIN_KDF) + status = psa_key_derivation_setup_kdf(operation, alg); +#else + return PSA_ERROR_NOT_SUPPORTED; +#endif /* AT_LEAST_ONE_BUILTIN_KDF */ + } else { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (status == PSA_SUCCESS) { + operation->alg = alg; + } + return status; +} + +#if defined(BUILTIN_ALG_ANY_HKDF) +static psa_status_t psa_hkdf_input(psa_hkdf_key_derivation_t *hkdf, + psa_algorithm_t kdf_alg, + psa_key_derivation_step_t step, + const uint8_t *data, + size_t data_length) +{ + psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg); + psa_status_t status; + switch (step) { + case PSA_KEY_DERIVATION_INPUT_SALT: +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) + if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND */ + if (hkdf->state != HKDF_STATE_INIT) { + return PSA_ERROR_BAD_STATE; + } else { + status = psa_key_derivation_start_hmac(&hkdf->hmac, + hash_alg, + data, data_length); + if (status != PSA_SUCCESS) { + return status; + } + hkdf->state = HKDF_STATE_STARTED; + return PSA_SUCCESS; + } + case PSA_KEY_DERIVATION_INPUT_SECRET: +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) + if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) { + /* We shouldn't be in different state as HKDF_EXPAND only allows + * two inputs: SECRET (this case) and INFO which does not modify + * the state. It could happen only if the hkdf + * object was corrupted. */ + if (hkdf->state != HKDF_STATE_INIT) { + return PSA_ERROR_BAD_STATE; + } + + /* Allow only input that fits expected prk size */ + if (data_length != PSA_HASH_LENGTH(hash_alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + memcpy(hkdf->prk, data, data_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND */ + { + /* HKDF: If no salt was provided, use an empty salt. + * HKDF-EXTRACT: salt is mandatory. */ + if (hkdf->state == HKDF_STATE_INIT) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) + if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) { + return PSA_ERROR_BAD_STATE; + } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */ + status = psa_key_derivation_start_hmac(&hkdf->hmac, + hash_alg, + NULL, 0); + if (status != PSA_SUCCESS) { + return status; + } + hkdf->state = HKDF_STATE_STARTED; + } + if (hkdf->state != HKDF_STATE_STARTED) { + return PSA_ERROR_BAD_STATE; + } + status = psa_mac_update(&hkdf->hmac, + data, data_length); + if (status != PSA_SUCCESS) { + return status; + } + status = psa_mac_sign_finish(&hkdf->hmac, + hkdf->prk, + sizeof(hkdf->prk), + &data_length); + if (status != PSA_SUCCESS) { + return status; + } + } + + hkdf->state = HKDF_STATE_KEYED; + hkdf->block_number = 0; +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) + if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) { + /* The only block of output is the PRK. */ + memcpy(hkdf->output_block, hkdf->prk, PSA_HASH_LENGTH(hash_alg)); + hkdf->offset_in_block = 0; + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */ + { + /* Block 0 is empty, and the next block will be + * generated by psa_key_derivation_hkdf_read(). */ + hkdf->offset_in_block = PSA_HASH_LENGTH(hash_alg); + } + + return PSA_SUCCESS; + case PSA_KEY_DERIVATION_INPUT_INFO: +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) + if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) + if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg) && + hkdf->state == HKDF_STATE_INIT) { + return PSA_ERROR_BAD_STATE; + } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */ + if (hkdf->state == HKDF_STATE_OUTPUT) { + return PSA_ERROR_BAD_STATE; + } + if (hkdf->info_set) { + return PSA_ERROR_BAD_STATE; + } + hkdf->info_length = data_length; + if (data_length != 0) { + hkdf->info = mbedtls_calloc(1, data_length); + if (hkdf->info == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + memcpy(hkdf->info, data, data_length); + } + hkdf->info_set = 1; + return PSA_SUCCESS; + default: + return PSA_ERROR_INVALID_ARGUMENT; + } +} +#endif /* BUILTIN_ALG_ANY_HKDF */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) +static psa_status_t psa_tls12_prf_set_seed(psa_tls12_prf_key_derivation_t *prf, + const uint8_t *data, + size_t data_length) +{ + if (prf->state != PSA_TLS12_PRF_STATE_INIT) { + return PSA_ERROR_BAD_STATE; + } + + if (data_length != 0) { + prf->seed = mbedtls_calloc(1, data_length); + if (prf->seed == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + memcpy(prf->seed, data, data_length); + prf->seed_length = data_length; + } + + prf->state = PSA_TLS12_PRF_STATE_SEED_SET; + + return PSA_SUCCESS; +} + +static psa_status_t psa_tls12_prf_set_key(psa_tls12_prf_key_derivation_t *prf, + const uint8_t *data, + size_t data_length) +{ + if (prf->state != PSA_TLS12_PRF_STATE_SEED_SET && + prf->state != PSA_TLS12_PRF_STATE_OTHER_KEY_SET) { + return PSA_ERROR_BAD_STATE; + } + + if (data_length != 0) { + prf->secret = mbedtls_calloc(1, data_length); + if (prf->secret == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + memcpy(prf->secret, data, data_length); + prf->secret_length = data_length; + } + + prf->state = PSA_TLS12_PRF_STATE_KEY_SET; + + return PSA_SUCCESS; +} + +static psa_status_t psa_tls12_prf_set_label(psa_tls12_prf_key_derivation_t *prf, + const uint8_t *data, + size_t data_length) +{ + if (prf->state != PSA_TLS12_PRF_STATE_KEY_SET) { + return PSA_ERROR_BAD_STATE; + } + + if (data_length != 0) { + prf->label = mbedtls_calloc(1, data_length); + if (prf->label == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + memcpy(prf->label, data, data_length); + prf->label_length = data_length; + } + + prf->state = PSA_TLS12_PRF_STATE_LABEL_SET; + + return PSA_SUCCESS; +} + +static psa_status_t psa_tls12_prf_input(psa_tls12_prf_key_derivation_t *prf, + psa_key_derivation_step_t step, + const uint8_t *data, + size_t data_length) +{ + switch (step) { + case PSA_KEY_DERIVATION_INPUT_SEED: + return psa_tls12_prf_set_seed(prf, data, data_length); + case PSA_KEY_DERIVATION_INPUT_SECRET: + return psa_tls12_prf_set_key(prf, data, data_length); + case PSA_KEY_DERIVATION_INPUT_LABEL: + return psa_tls12_prf_set_label(prf, data, data_length); + default: + return PSA_ERROR_INVALID_ARGUMENT; + } +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || + * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) +static psa_status_t psa_tls12_prf_psk_to_ms_set_key( + psa_tls12_prf_key_derivation_t *prf, + const uint8_t *data, + size_t data_length) +{ + psa_status_t status; + const size_t pms_len = (prf->state == PSA_TLS12_PRF_STATE_OTHER_KEY_SET ? + 4 + data_length + prf->other_secret_length : + 4 + 2 * data_length); + + if (data_length > PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + uint8_t *pms = mbedtls_calloc(1, pms_len); + if (pms == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + uint8_t *cur = pms; + + /* pure-PSK: + * Quoting RFC 4279, Section 2: + * + * The premaster secret is formed as follows: if the PSK is N octets + * long, concatenate a uint16 with the value N, N zero octets, a second + * uint16 with the value N, and the PSK itself. + * + * mixed-PSK: + * In a DHE-PSK, RSA-PSK, ECDHE-PSK the premaster secret is formed as + * follows: concatenate a uint16 with the length of the other secret, + * the other secret itself, uint16 with the length of PSK, and the + * PSK itself. + * For details please check: + * - RFC 4279, Section 4 for the definition of RSA-PSK, + * - RFC 4279, Section 3 for the definition of DHE-PSK, + * - RFC 5489 for the definition of ECDHE-PSK. + */ + + if (prf->state == PSA_TLS12_PRF_STATE_OTHER_KEY_SET) { + *cur++ = MBEDTLS_BYTE_1(prf->other_secret_length); + *cur++ = MBEDTLS_BYTE_0(prf->other_secret_length); + if (prf->other_secret_length != 0) { + memcpy(cur, prf->other_secret, prf->other_secret_length); + mbedtls_platform_zeroize(prf->other_secret, prf->other_secret_length); + cur += prf->other_secret_length; + } + } else { + *cur++ = MBEDTLS_BYTE_1(data_length); + *cur++ = MBEDTLS_BYTE_0(data_length); + memset(cur, 0, data_length); + cur += data_length; + } + + *cur++ = MBEDTLS_BYTE_1(data_length); + *cur++ = MBEDTLS_BYTE_0(data_length); + memcpy(cur, data, data_length); + cur += data_length; + + status = psa_tls12_prf_set_key(prf, pms, cur - pms); + + mbedtls_platform_zeroize(pms, pms_len); + mbedtls_free(pms); + return status; +} + +static psa_status_t psa_tls12_prf_psk_to_ms_set_other_key( + psa_tls12_prf_key_derivation_t *prf, + const uint8_t *data, + size_t data_length) +{ + if (prf->state != PSA_TLS12_PRF_STATE_SEED_SET) { + return PSA_ERROR_BAD_STATE; + } + + if (data_length != 0) { + prf->other_secret = mbedtls_calloc(1, data_length); + if (prf->other_secret == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + memcpy(prf->other_secret, data, data_length); + prf->other_secret_length = data_length; + } else { + prf->other_secret_length = 0; + } + + prf->state = PSA_TLS12_PRF_STATE_OTHER_KEY_SET; + + return PSA_SUCCESS; +} + +static psa_status_t psa_tls12_prf_psk_to_ms_input( + psa_tls12_prf_key_derivation_t *prf, + psa_key_derivation_step_t step, + const uint8_t *data, + size_t data_length) +{ + switch (step) { + case PSA_KEY_DERIVATION_INPUT_SECRET: + return psa_tls12_prf_psk_to_ms_set_key(prf, + data, data_length); + break; + case PSA_KEY_DERIVATION_INPUT_OTHER_SECRET: + return psa_tls12_prf_psk_to_ms_set_other_key(prf, + data, + data_length); + break; + default: + return psa_tls12_prf_input(prf, step, data, data_length); + break; + + } +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) +static psa_status_t psa_tls12_ecjpake_to_pms_input( + psa_tls12_ecjpake_to_pms_t *ecjpake, + psa_key_derivation_step_t step, + const uint8_t *data, + size_t data_length) +{ + if (data_length != PSA_TLS12_ECJPAKE_TO_PMS_INPUT_SIZE || + step != PSA_KEY_DERIVATION_INPUT_SECRET) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* Check if the passed point is in an uncompressed form */ + if (data[0] != 0x04) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* Only K.X has to be extracted - bytes 1 to 32 inclusive. */ + memcpy(ecjpake->data, data + 1, PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE); + + return PSA_SUCCESS; +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */ +/** Check whether the given key type is acceptable for the given + * input step of a key derivation. + * + * Secret inputs must have the type #PSA_KEY_TYPE_DERIVE. + * Non-secret inputs must have the type #PSA_KEY_TYPE_RAW_DATA. + * Both secret and non-secret inputs can alternatively have the type + * #PSA_KEY_TYPE_NONE, which is never the type of a key object, meaning + * that the input was passed as a buffer rather than via a key object. + */ +static int psa_key_derivation_check_input_type( + psa_key_derivation_step_t step, + psa_key_type_t key_type) +{ + switch (step) { + case PSA_KEY_DERIVATION_INPUT_SECRET: + if (key_type == PSA_KEY_TYPE_DERIVE) { + return PSA_SUCCESS; + } + if (key_type == PSA_KEY_TYPE_NONE) { + return PSA_SUCCESS; + } + break; + case PSA_KEY_DERIVATION_INPUT_OTHER_SECRET: + if (key_type == PSA_KEY_TYPE_DERIVE) { + return PSA_SUCCESS; + } + if (key_type == PSA_KEY_TYPE_NONE) { + return PSA_SUCCESS; + } + break; + case PSA_KEY_DERIVATION_INPUT_LABEL: + case PSA_KEY_DERIVATION_INPUT_SALT: + case PSA_KEY_DERIVATION_INPUT_INFO: + case PSA_KEY_DERIVATION_INPUT_SEED: + if (key_type == PSA_KEY_TYPE_RAW_DATA) { + return PSA_SUCCESS; + } + if (key_type == PSA_KEY_TYPE_NONE) { + return PSA_SUCCESS; + } + break; + } + return PSA_ERROR_INVALID_ARGUMENT; +} + +static psa_status_t psa_key_derivation_input_internal( + psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + psa_key_type_t key_type, + const uint8_t *data, + size_t data_length) +{ + psa_status_t status; + psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation); + + status = psa_key_derivation_check_input_type(step, key_type); + if (status != PSA_SUCCESS) { + goto exit; + } + +#if defined(BUILTIN_ALG_ANY_HKDF) + if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) { + status = psa_hkdf_input(&operation->ctx.hkdf, kdf_alg, + step, data, data_length); + } else +#endif /* BUILTIN_ALG_ANY_HKDF */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) + if (PSA_ALG_IS_TLS12_PRF(kdf_alg)) { + status = psa_tls12_prf_input(&operation->ctx.tls12_prf, + step, data, data_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) + if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) { + status = psa_tls12_prf_psk_to_ms_input(&operation->ctx.tls12_prf, + step, data, data_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) + if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) { + status = psa_tls12_ecjpake_to_pms_input( + &operation->ctx.tls12_ecjpake_to_pms, step, data, data_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */ + { + /* This can't happen unless the operation object was not initialized */ + (void) data; + (void) data_length; + (void) kdf_alg; + return PSA_ERROR_BAD_STATE; + } + +exit: + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(operation); + } + return status; +} + +psa_status_t psa_key_derivation_input_bytes( + psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + const uint8_t *data, + size_t data_length) +{ + return psa_key_derivation_input_internal(operation, step, + PSA_KEY_TYPE_NONE, + data, data_length); +} + +psa_status_t psa_key_derivation_input_key( + psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + mbedtls_svc_key_id_t key) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + status = psa_get_and_lock_transparent_key_slot_with_policy( + key, &slot, PSA_KEY_USAGE_DERIVE, operation->alg); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(operation); + return status; + } + + /* Passing a key object as a SECRET input unlocks the permission + * to output to a key object. */ + if (step == PSA_KEY_DERIVATION_INPUT_SECRET) { + operation->can_output_key = 1; + } + + status = psa_key_derivation_input_internal(operation, + step, slot->attr.type, + slot->key.data, + slot->key.bytes); + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + + + +/****************************************************************/ +/* Key agreement */ +/****************************************************************/ + +psa_status_t psa_key_agreement_raw_builtin(const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *peer_key, + size_t peer_key_length, + uint8_t *shared_secret, + size_t shared_secret_size, + size_t *shared_secret_length) +{ + switch (alg) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) + case PSA_ALG_ECDH: + return mbedtls_psa_key_agreement_ecdh(attributes, key_buffer, + key_buffer_size, alg, + peer_key, peer_key_length, + shared_secret, + shared_secret_size, + shared_secret_length); +#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECDH */ + default: + (void) attributes; + (void) key_buffer; + (void) key_buffer_size; + (void) peer_key; + (void) peer_key_length; + (void) shared_secret; + (void) shared_secret_size; + (void) shared_secret_length; + return PSA_ERROR_NOT_SUPPORTED; + } +} + +/** Internal function for raw key agreement + * Calls the driver wrapper which will hand off key agreement task + * to the driver's implementation if a driver is present. + * Fallback specified in the driver wrapper is built-in raw key agreement + * (psa_key_agreement_raw_builtin). + */ +static psa_status_t psa_key_agreement_raw_internal(psa_algorithm_t alg, + psa_key_slot_t *private_key, + const uint8_t *peer_key, + size_t peer_key_length, + uint8_t *shared_secret, + size_t shared_secret_size, + size_t *shared_secret_length) +{ + if (!PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) { + return PSA_ERROR_NOT_SUPPORTED; + } + + psa_key_attributes_t attributes = { + .core = private_key->attr + }; + + return psa_driver_wrapper_key_agreement(&attributes, + private_key->key.data, + private_key->key.bytes, alg, + peer_key, peer_key_length, + shared_secret, + shared_secret_size, + shared_secret_length); +} + +/* Note that if this function fails, you must call psa_key_derivation_abort() + * to potentially free embedded data structures and wipe confidential data. + */ +static psa_status_t psa_key_agreement_internal(psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + psa_key_slot_t *private_key, + const uint8_t *peer_key, + size_t peer_key_length) +{ + psa_status_t status; + uint8_t shared_secret[PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE]; + size_t shared_secret_length = 0; + psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE(operation->alg); + + /* Step 1: run the secret agreement algorithm to generate the shared + * secret. */ + status = psa_key_agreement_raw_internal(ka_alg, + private_key, + peer_key, peer_key_length, + shared_secret, + sizeof(shared_secret), + &shared_secret_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* Step 2: set up the key derivation to generate key material from + * the shared secret. A shared secret is permitted wherever a key + * of type DERIVE is permitted. */ + status = psa_key_derivation_input_internal(operation, step, + PSA_KEY_TYPE_DERIVE, + shared_secret, + shared_secret_length); +exit: + mbedtls_platform_zeroize(shared_secret, shared_secret_length); + return status; +} + +psa_status_t psa_key_derivation_key_agreement(psa_key_derivation_operation_t *operation, + psa_key_derivation_step_t step, + mbedtls_svc_key_id_t private_key, + const uint8_t *peer_key, + size_t peer_key_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + + if (!PSA_ALG_IS_KEY_AGREEMENT(operation->alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + status = psa_get_and_lock_transparent_key_slot_with_policy( + private_key, &slot, PSA_KEY_USAGE_DERIVE, operation->alg); + if (status != PSA_SUCCESS) { + return status; + } + status = psa_key_agreement_internal(operation, step, + slot, + peer_key, peer_key_length); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(operation); + } else { + /* If a private key has been added as SECRET, we allow the derived + * key material to be used as a key in PSA Crypto. */ + if (step == PSA_KEY_DERIVATION_INPUT_SECRET) { + operation->can_output_key = 1; + } + } + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_raw_key_agreement(psa_algorithm_t alg, + mbedtls_svc_key_id_t private_key, + const uint8_t *peer_key, + size_t peer_key_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + + if (!PSA_ALG_IS_KEY_AGREEMENT(alg)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + status = psa_get_and_lock_transparent_key_slot_with_policy( + private_key, &slot, PSA_KEY_USAGE_DERIVE, alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() is in general an upper bound + * for the output size. The PSA specification only guarantees that this + * function works if output_size >= PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(...), + * but it might be nice to allow smaller buffers if the output fits. + * At the time of writing this comment, with only ECDH implemented, + * PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() is exact so the point is moot. + * If FFDH is implemented, PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() can easily + * be exact for it as well. */ + size_t expected_length = + PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(slot->attr.type, slot->attr.bits); + if (output_size < expected_length) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + status = psa_key_agreement_raw_internal(alg, slot, + peer_key, peer_key_length, + output, output_size, + output_length); + +exit: + if (status != PSA_SUCCESS) { + /* If an error happens and is not handled properly, the output + * may be used as a key to protect sensitive data. Arrange for such + * a key to be random, which is likely to result in decryption or + * verification errors. This is better than filling the buffer with + * some constant data such as zeros, which would result in the data + * being protected with a reproducible, easily knowable key. + */ + psa_generate_random(output, output_size); + *output_length = output_size; + } + + unlock_status = psa_unlock_key_slot(slot); + + return (status == PSA_SUCCESS) ? unlock_status : status; +} + + + +/****************************************************************/ +/* Random generation */ +/****************************************************************/ + +/** Initialize the PSA random generator. + */ +static void mbedtls_psa_random_init(mbedtls_psa_random_context_t *rng) +{ +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + memset(rng, 0, sizeof(*rng)); +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + + /* Set default configuration if + * mbedtls_psa_crypto_configure_entropy_sources() hasn't been called. */ + if (rng->entropy_init == NULL) { + rng->entropy_init = mbedtls_entropy_init; + } + if (rng->entropy_free == NULL) { + rng->entropy_free = mbedtls_entropy_free; + } + + rng->entropy_init(&rng->entropy); +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \ + defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) + /* The PSA entropy injection feature depends on using NV seed as an entropy + * source. Add NV seed as an entropy source for PSA entropy injection. */ + mbedtls_entropy_add_source(&rng->entropy, + mbedtls_nv_seed_poll, NULL, + MBEDTLS_ENTROPY_BLOCK_SIZE, + MBEDTLS_ENTROPY_SOURCE_STRONG); +#endif + + mbedtls_psa_drbg_init(MBEDTLS_PSA_RANDOM_STATE); +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +} + +/** Deinitialize the PSA random generator. + */ +static void mbedtls_psa_random_free(mbedtls_psa_random_context_t *rng) +{ +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + memset(rng, 0, sizeof(*rng)); +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + mbedtls_psa_drbg_free(MBEDTLS_PSA_RANDOM_STATE); + rng->entropy_free(&rng->entropy); +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +} + +/** Seed the PSA random generator. + */ +static psa_status_t mbedtls_psa_random_seed(mbedtls_psa_random_context_t *rng) +{ +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + /* Do nothing: the external RNG seeds itself. */ + (void) rng; + return PSA_SUCCESS; +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + const unsigned char drbg_seed[] = "PSA"; + int ret = mbedtls_psa_drbg_seed(&rng->entropy, + drbg_seed, sizeof(drbg_seed) - 1); + return mbedtls_to_psa_error(ret); +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +} + +psa_status_t psa_generate_random(uint8_t *output, + size_t output_size) +{ + GUARD_MODULE_INITIALIZED; + +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + + size_t output_length = 0; + psa_status_t status = mbedtls_psa_external_get_random(&global_data.rng, + output, output_size, + &output_length); + if (status != PSA_SUCCESS) { + return status; + } + /* Breaking up a request into smaller chunks is currently not supported + * for the external RNG interface. */ + if (output_length != output_size) { + return PSA_ERROR_INSUFFICIENT_ENTROPY; + } + return PSA_SUCCESS; + +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + + while (output_size > 0) { + size_t request_size = + (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ? + MBEDTLS_PSA_RANDOM_MAX_REQUEST : + output_size); + int ret = mbedtls_psa_get_random(MBEDTLS_PSA_RANDOM_STATE, + output, request_size); + if (ret != 0) { + return mbedtls_to_psa_error(ret); + } + output_size -= request_size; + output += request_size; + } + return PSA_SUCCESS; +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +} + +/* Wrapper function allowing the classic API to use the PSA RNG. + * + * `mbedtls_psa_get_random(MBEDTLS_PSA_RANDOM_STATE, ...)` calls + * `psa_generate_random(...)`. The state parameter is ignored since the + * PSA API doesn't support passing an explicit state. + * + * In the non-external case, psa_generate_random() calls an + * `mbedtls_xxx_drbg_random` function which has exactly the same signature + * and semantics as mbedtls_psa_get_random(). As an optimization, + * instead of doing this back-and-forth between the PSA API and the + * classic API, psa_crypto_random_impl.h defines `mbedtls_psa_get_random` + * as a constant function pointer to `mbedtls_xxx_drbg_random`. + */ +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +int mbedtls_psa_get_random(void *p_rng, + unsigned char *output, + size_t output_size) +{ + /* This function takes a pointer to the RNG state because that's what + * classic mbedtls functions using an RNG expect. The PSA RNG manages + * its own state internally and doesn't let the caller access that state. + * So we just ignore the state parameter, and in practice we'll pass + * NULL. */ + (void) p_rng; + psa_status_t status = psa_generate_random(output, output_size); + if (status == PSA_SUCCESS) { + return 0; + } else { + return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; + } +} +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) +#include "entropy_poll.h" + +psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed, + size_t seed_size) +{ + if (global_data.initialized) { + return PSA_ERROR_NOT_PERMITTED; + } + + if (((seed_size < MBEDTLS_ENTROPY_MIN_PLATFORM) || + (seed_size < MBEDTLS_ENTROPY_BLOCK_SIZE)) || + (seed_size > MBEDTLS_ENTROPY_MAX_SEED_SIZE)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return mbedtls_psa_storage_inject_entropy(seed, seed_size); +} +#endif /* MBEDTLS_PSA_INJECT_ENTROPY */ + +/** Validate the key type and size for key generation + * + * \param type The key type + * \param bits The number of bits of the key + * + * \retval #PSA_SUCCESS + * The key type and size are valid. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size in bits of the key is not valid. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The type and/or the size in bits of the key or the combination of + * the two is not supported. + */ +static psa_status_t psa_validate_key_type_and_size_for_key_generation( + psa_key_type_t type, size_t bits) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (key_type_is_raw_bytes(type)) { + status = psa_validate_unstructured_key_bit_size(type, bits); + if (status != PSA_SUCCESS) { + return status; + } + } else +#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) + if (PSA_KEY_TYPE_IS_RSA(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) { + if (bits > PSA_VENDOR_RSA_MAX_KEY_BITS) { + return PSA_ERROR_NOT_SUPPORTED; + } + + /* Accept only byte-aligned keys, for the same reasons as + * in psa_import_rsa_key(). */ + if (bits % 8 != 0) { + return PSA_ERROR_NOT_SUPPORTED; + } + } else +#endif /* defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) */ + +#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) + if (PSA_KEY_TYPE_IS_ECC(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) { + /* To avoid empty block, return successfully here. */ + return PSA_SUCCESS; + } else +#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR) */ + { + return PSA_ERROR_NOT_SUPPORTED; + } + + return PSA_SUCCESS; +} + +psa_status_t psa_generate_key_internal( + const psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_type_t type = attributes->core.type; + + if ((attributes->domain_parameters == NULL) && + (attributes->domain_parameters_size != 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (key_type_is_raw_bytes(type)) { + status = psa_generate_random(key_buffer, key_buffer_size); + if (status != PSA_SUCCESS) { + return status; + } + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) + if (type == PSA_KEY_TYPE_DES) { + psa_des_set_key_parity(key_buffer, key_buffer_size); + } +#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES */ + } else + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) && \ + defined(MBEDTLS_GENPRIME) + if (type == PSA_KEY_TYPE_RSA_KEY_PAIR) { + return mbedtls_psa_rsa_generate_key(attributes, + key_buffer, + key_buffer_size, + key_buffer_length); + } else +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) + * defined(MBEDTLS_GENPRIME) */ + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) + if (PSA_KEY_TYPE_IS_ECC(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) { + return mbedtls_psa_ecp_generate_key(attributes, + key_buffer, + key_buffer_size, + key_buffer_length); + } else +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) */ + { + (void) key_buffer_length; + return PSA_ERROR_NOT_SUPPORTED; + } + + return PSA_SUCCESS; +} + +psa_status_t psa_generate_key(const psa_key_attributes_t *attributes, + mbedtls_svc_key_id_t *key) +{ + psa_status_t status; + psa_key_slot_t *slot = NULL; + psa_se_drv_table_entry_t *driver = NULL; + size_t key_buffer_size; + + *key = MBEDTLS_SVC_KEY_ID_INIT; + + /* Reject any attempt to create a zero-length key so that we don't + * risk tripping up later, e.g. on a malloc(0) that returns NULL. */ + if (psa_get_key_bits(attributes) == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* Reject any attempt to create a public key. */ + if (PSA_KEY_TYPE_IS_PUBLIC_KEY(attributes->core.type)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_start_key_creation(PSA_KEY_CREATION_GENERATE, attributes, + &slot, &driver); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* In the case of a transparent key or an opaque key stored in local + * storage ( thus not in the case of generating a key in a secure element + * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a + * buffer to hold the generated key material. */ + if (slot->key.data == NULL) { + if (PSA_KEY_LIFETIME_GET_LOCATION(attributes->core.lifetime) == + PSA_KEY_LOCATION_LOCAL_STORAGE) { + status = psa_validate_key_type_and_size_for_key_generation( + attributes->core.type, attributes->core.bits); + if (status != PSA_SUCCESS) { + goto exit; + } + + key_buffer_size = PSA_EXPORT_KEY_OUTPUT_SIZE( + attributes->core.type, + attributes->core.bits); + } else { + status = psa_driver_wrapper_get_key_buffer_size( + attributes, &key_buffer_size); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + status = psa_allocate_buffer_to_slot(slot, key_buffer_size); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + status = psa_driver_wrapper_generate_key(attributes, + slot->key.data, slot->key.bytes, &slot->key.bytes); + + if (status != PSA_SUCCESS) { + psa_remove_key_data_from_memory(slot); + } + +exit: + if (status == PSA_SUCCESS) { + status = psa_finish_key_creation(slot, driver, key); + } + if (status != PSA_SUCCESS) { + psa_fail_key_creation(slot, driver); + } + + return status; +} + +/****************************************************************/ +/* Module setup */ +/****************************************************************/ + +#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +psa_status_t mbedtls_psa_crypto_configure_entropy_sources( + void (* entropy_init)(mbedtls_entropy_context *ctx), + void (* entropy_free)(mbedtls_entropy_context *ctx)) +{ + if (global_data.rng_state != RNG_NOT_INITIALIZED) { + return PSA_ERROR_BAD_STATE; + } + global_data.rng.entropy_init = entropy_init; + global_data.rng.entropy_free = entropy_free; + return PSA_SUCCESS; +} +#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ + +void mbedtls_psa_crypto_free(void) +{ + psa_wipe_all_key_slots(); + if (global_data.rng_state != RNG_NOT_INITIALIZED) { + mbedtls_psa_random_free(&global_data.rng); + } + /* Wipe all remaining data, including configuration. + * In particular, this sets all state indicator to the value + * indicating "uninitialized". */ + mbedtls_platform_zeroize(&global_data, sizeof(global_data)); + + /* Terminate drivers */ + psa_driver_wrapper_free(); +} + +#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) +/** Recover a transaction that was interrupted by a power failure. + * + * This function is called during initialization, before psa_crypto_init() + * returns. If this function returns a failure status, the initialization + * fails. + */ +static psa_status_t psa_crypto_recover_transaction( + const psa_crypto_transaction_t *transaction) +{ + switch (transaction->unknown.type) { + case PSA_CRYPTO_TRANSACTION_CREATE_KEY: + case PSA_CRYPTO_TRANSACTION_DESTROY_KEY: + /* TODO - fall through to the failure case until this + * is implemented. + * https://github.com/ARMmbed/mbed-crypto/issues/218 + */ + default: + /* We found an unsupported transaction in the storage. + * We don't know what state the storage is in. Give up. */ + return PSA_ERROR_DATA_INVALID; + } +} +#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */ + +psa_status_t psa_crypto_init(void) +{ + psa_status_t status; + + /* Double initialization is explicitly allowed. */ + if (global_data.initialized != 0) { + return PSA_SUCCESS; + } + + /* Init drivers */ + status = psa_driver_wrapper_init(); + if (status != PSA_SUCCESS) { + goto exit; + } + global_data.drivers_initialized = 1; + + /* Initialize and seed the random generator. */ + mbedtls_psa_random_init(&global_data.rng); + global_data.rng_state = RNG_INITIALIZED; + status = mbedtls_psa_random_seed(&global_data.rng); + if (status != PSA_SUCCESS) { + goto exit; + } + global_data.rng_state = RNG_SEEDED; + + status = psa_initialize_key_slots(); + if (status != PSA_SUCCESS) { + goto exit; + } + +#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) + status = psa_crypto_load_transaction(); + if (status == PSA_SUCCESS) { + status = psa_crypto_recover_transaction(&psa_crypto_transaction); + if (status != PSA_SUCCESS) { + goto exit; + } + status = psa_crypto_stop_transaction(); + } else if (status == PSA_ERROR_DOES_NOT_EXIST) { + /* There's no transaction to complete. It's all good. */ + status = PSA_SUCCESS; + } +#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */ + + /* All done. */ + global_data.initialized = 1; + +exit: + if (status != PSA_SUCCESS) { + mbedtls_psa_crypto_free(); + } + return status; +} + +psa_status_t psa_crypto_driver_pake_get_password_len( + const psa_crypto_driver_pake_inputs_t *inputs, + size_t *password_len) +{ + if (inputs->password_len == 0) { + return PSA_ERROR_BAD_STATE; + } + + *password_len = inputs->password_len; + + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_driver_pake_get_password( + const psa_crypto_driver_pake_inputs_t *inputs, + uint8_t *buffer, size_t buffer_size, size_t *buffer_length) +{ + if (inputs->password_len == 0) { + return PSA_ERROR_BAD_STATE; + } + + if (buffer_size < inputs->password_len) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + memcpy(buffer, inputs->password, inputs->password_len); + *buffer_length = inputs->password_len; + + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_driver_pake_get_role( + const psa_crypto_driver_pake_inputs_t *inputs, + psa_pake_role_t *role) +{ + if (inputs->role == PSA_PAKE_ROLE_NONE) { + return PSA_ERROR_BAD_STATE; + } + + *role = inputs->role; + + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_driver_pake_get_user_len( + const psa_crypto_driver_pake_inputs_t *inputs, + size_t *user_len) +{ + if (inputs->user_len == 0) { + return PSA_ERROR_BAD_STATE; + } + + *user_len = inputs->user_len; + + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_driver_pake_get_user( + const psa_crypto_driver_pake_inputs_t *inputs, + uint8_t *user_id, size_t user_id_size, size_t *user_id_len) +{ + if (inputs->user_len == 0) { + return PSA_ERROR_BAD_STATE; + } + + if (user_id_size < inputs->user_len) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + memcpy(user_id, inputs->user, inputs->user_len); + *user_id_len = inputs->user_len; + + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_driver_pake_get_peer_len( + const psa_crypto_driver_pake_inputs_t *inputs, + size_t *peer_len) +{ + if (inputs->peer_len == 0) { + return PSA_ERROR_BAD_STATE; + } + + *peer_len = inputs->peer_len; + + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_driver_pake_get_peer( + const psa_crypto_driver_pake_inputs_t *inputs, + uint8_t *peer_id, size_t peer_id_size, size_t *peer_id_length) +{ + if (inputs->peer_len == 0) { + return PSA_ERROR_BAD_STATE; + } + + if (peer_id_size < inputs->peer_len) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + memcpy(peer_id, inputs->peer, inputs->peer_len); + *peer_id_length = inputs->peer_len; + + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_driver_pake_get_cipher_suite( + const psa_crypto_driver_pake_inputs_t *inputs, + psa_pake_cipher_suite_t *cipher_suite) +{ + if (inputs->cipher_suite.algorithm == PSA_ALG_NONE) { + return PSA_ERROR_BAD_STATE; + } + + *cipher_suite = inputs->cipher_suite; + + return PSA_SUCCESS; +} + +psa_status_t psa_pake_setup( + psa_pake_operation_t *operation, + const psa_pake_cipher_suite_t *cipher_suite) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_SETUP) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (PSA_ALG_IS_PAKE(cipher_suite->algorithm) == 0 || + PSA_ALG_IS_HASH(cipher_suite->hash) == 0) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + memset(&operation->data.inputs, 0, sizeof(operation->data.inputs)); + + operation->alg = cipher_suite->algorithm; + operation->data.inputs.cipher_suite = *cipher_suite; + +#if defined(PSA_WANT_ALG_JPAKE) + if (operation->alg == PSA_ALG_JPAKE) { + psa_jpake_computation_stage_t *computation_stage = + &operation->computation_stage.jpake; + + computation_stage->state = PSA_PAKE_STATE_SETUP; + computation_stage->sequence = PSA_PAKE_SEQ_INVALID; + computation_stage->input_step = PSA_PAKE_STEP_X1_X2; + computation_stage->output_step = PSA_PAKE_STEP_X1_X2; + } else +#endif /* PSA_WANT_ALG_JPAKE */ + { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + operation->stage = PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS; + + return PSA_SUCCESS; +exit: + psa_pake_abort(operation); + return status; +} + +psa_status_t psa_pake_set_password_key( + psa_pake_operation_t *operation, + mbedtls_svc_key_id_t password) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot = NULL; + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + status = psa_get_and_lock_key_slot_with_policy(password, &slot, + PSA_KEY_USAGE_DERIVE, + operation->alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + psa_key_type_t type = psa_get_key_type(&attributes); + + if (type != PSA_KEY_TYPE_PASSWORD && + type != PSA_KEY_TYPE_PASSWORD_HASH) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + operation->data.inputs.password = mbedtls_calloc(1, slot->key.bytes); + if (operation->data.inputs.password == NULL) { + status = PSA_ERROR_INSUFFICIENT_MEMORY; + goto exit; + } + + memcpy(operation->data.inputs.password, slot->key.data, slot->key.bytes); + operation->data.inputs.password_len = slot->key.bytes; + operation->data.inputs.attributes = attributes; +exit: + if (status != PSA_SUCCESS) { + psa_pake_abort(operation); + } + unlock_status = psa_unlock_key_slot(slot); + return (status == PSA_SUCCESS) ? unlock_status : status; +} + +psa_status_t psa_pake_set_user( + psa_pake_operation_t *operation, + const uint8_t *user_id, + size_t user_id_len) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (user_id_len == 0) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + if (operation->data.inputs.user_len != 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + /* Allow only "client" or "server" values (temporary restriction). */ + if ((user_id_len != sizeof(jpake_server_id) || + memcmp(user_id, jpake_server_id, user_id_len) != 0) && + (user_id_len != sizeof(jpake_client_id) || + memcmp(user_id, jpake_client_id, user_id_len) != 0)) { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + operation->data.inputs.user = mbedtls_calloc(1, user_id_len); + if (operation->data.inputs.user == NULL) { + status = PSA_ERROR_INSUFFICIENT_MEMORY; + goto exit; + } + + memcpy(operation->data.inputs.user, user_id, user_id_len); + operation->data.inputs.user_len = user_id_len; + + return PSA_SUCCESS; +exit: + psa_pake_abort(operation); + return status; +} + +psa_status_t psa_pake_set_peer( + psa_pake_operation_t *operation, + const uint8_t *peer_id, + size_t peer_id_len) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (peer_id_len == 0) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + if (operation->data.inputs.peer_len != 0) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + /* Allow only "client" or "server" values (temporary restriction). */ + if ((peer_id_len != sizeof(jpake_server_id) || + memcmp(peer_id, jpake_server_id, peer_id_len) != 0) && + (peer_id_len != sizeof(jpake_client_id) || + memcmp(peer_id, jpake_client_id, peer_id_len) != 0)) { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + operation->data.inputs.peer = mbedtls_calloc(1, peer_id_len); + if (operation->data.inputs.peer == NULL) { + status = PSA_ERROR_INSUFFICIENT_MEMORY; + goto exit; + } + + memcpy(operation->data.inputs.peer, peer_id, peer_id_len); + operation->data.inputs.peer_len = peer_id_len; + + return PSA_SUCCESS; +exit: + psa_pake_abort(operation); + return status; +} + +psa_status_t psa_pake_set_role( + psa_pake_operation_t *operation, + psa_pake_role_t role) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + switch (operation->alg) { +#if defined(PSA_WANT_ALG_JPAKE) + case PSA_ALG_JPAKE: + if (role == PSA_PAKE_ROLE_NONE) { + return PSA_SUCCESS; + } + status = PSA_ERROR_INVALID_ARGUMENT; + break; +#endif + default: + (void) role; + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } +exit: + psa_pake_abort(operation); + return status; +} + +/* Auxiliary function to convert core computation stage(step, sequence, state) to single driver step. */ +#if defined(PSA_WANT_ALG_JPAKE) +static psa_crypto_driver_pake_step_t convert_jpake_computation_stage_to_driver_step( + psa_jpake_computation_stage_t *stage) +{ + switch (stage->state) { + case PSA_PAKE_OUTPUT_X1_X2: + case PSA_PAKE_INPUT_X1_X2: + switch (stage->sequence) { + case PSA_PAKE_X1_STEP_KEY_SHARE: + return PSA_JPAKE_X1_STEP_KEY_SHARE; + case PSA_PAKE_X1_STEP_ZK_PUBLIC: + return PSA_JPAKE_X1_STEP_ZK_PUBLIC; + case PSA_PAKE_X1_STEP_ZK_PROOF: + return PSA_JPAKE_X1_STEP_ZK_PROOF; + case PSA_PAKE_X2_STEP_KEY_SHARE: + return PSA_JPAKE_X2_STEP_KEY_SHARE; + case PSA_PAKE_X2_STEP_ZK_PUBLIC: + return PSA_JPAKE_X2_STEP_ZK_PUBLIC; + case PSA_PAKE_X2_STEP_ZK_PROOF: + return PSA_JPAKE_X2_STEP_ZK_PROOF; + default: + return PSA_JPAKE_STEP_INVALID; + } + break; + case PSA_PAKE_OUTPUT_X2S: + switch (stage->sequence) { + case PSA_PAKE_X1_STEP_KEY_SHARE: + return PSA_JPAKE_X2S_STEP_KEY_SHARE; + case PSA_PAKE_X1_STEP_ZK_PUBLIC: + return PSA_JPAKE_X2S_STEP_ZK_PUBLIC; + case PSA_PAKE_X1_STEP_ZK_PROOF: + return PSA_JPAKE_X2S_STEP_ZK_PROOF; + default: + return PSA_JPAKE_STEP_INVALID; + } + break; + case PSA_PAKE_INPUT_X4S: + switch (stage->sequence) { + case PSA_PAKE_X1_STEP_KEY_SHARE: + return PSA_JPAKE_X4S_STEP_KEY_SHARE; + case PSA_PAKE_X1_STEP_ZK_PUBLIC: + return PSA_JPAKE_X4S_STEP_ZK_PUBLIC; + case PSA_PAKE_X1_STEP_ZK_PROOF: + return PSA_JPAKE_X4S_STEP_ZK_PROOF; + default: + return PSA_JPAKE_STEP_INVALID; + } + break; + default: + return PSA_JPAKE_STEP_INVALID; + } + return PSA_JPAKE_STEP_INVALID; +} +#endif /* PSA_WANT_ALG_JPAKE */ + +static psa_status_t psa_pake_complete_inputs( + psa_pake_operation_t *operation) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + /* Create copy of the inputs on stack as inputs share memory + with the driver context which will be setup by the driver. */ + psa_crypto_driver_pake_inputs_t inputs = operation->data.inputs; + + if (inputs.password_len == 0) { + return PSA_ERROR_BAD_STATE; + } + + if (operation->alg == PSA_ALG_JPAKE) { + if (inputs.user_len == 0 || inputs.peer_len == 0) { + return PSA_ERROR_BAD_STATE; + } + if (memcmp(inputs.user, jpake_client_id, inputs.user_len) == 0 && + memcmp(inputs.peer, jpake_server_id, inputs.peer_len) == 0) { + inputs.role = PSA_PAKE_ROLE_CLIENT; + } else + if (memcmp(inputs.user, jpake_server_id, inputs.user_len) == 0 && + memcmp(inputs.peer, jpake_client_id, inputs.peer_len) == 0) { + inputs.role = PSA_PAKE_ROLE_SERVER; + } + + if (inputs.role != PSA_PAKE_ROLE_CLIENT && + inputs.role != PSA_PAKE_ROLE_SERVER) { + return PSA_ERROR_NOT_SUPPORTED; + } + } + + /* Clear driver context */ + mbedtls_platform_zeroize(&operation->data, sizeof(operation->data)); + + status = psa_driver_wrapper_pake_setup(operation, &inputs); + + /* Driver is responsible for creating its own copy of the password. */ + mbedtls_platform_zeroize(inputs.password, inputs.password_len); + mbedtls_free(inputs.password); + + /* User and peer are translated to role. */ + mbedtls_free(inputs.user); + mbedtls_free(inputs.peer); + + if (status == PSA_SUCCESS) { +#if defined(PSA_WANT_ALG_JPAKE) + if (operation->alg == PSA_ALG_JPAKE) { + operation->stage = PSA_PAKE_OPERATION_STAGE_COMPUTATION; + psa_jpake_computation_stage_t *computation_stage = + &operation->computation_stage.jpake; + computation_stage->state = PSA_PAKE_STATE_READY; + computation_stage->sequence = PSA_PAKE_SEQ_INVALID; + computation_stage->input_step = PSA_PAKE_STEP_X1_X2; + computation_stage->output_step = PSA_PAKE_STEP_X1_X2; + } else +#endif /* PSA_WANT_ALG_JPAKE */ + { + status = PSA_ERROR_NOT_SUPPORTED; + } + } + return status; +} + +#if defined(PSA_WANT_ALG_JPAKE) +static psa_status_t psa_jpake_output_prologue( + psa_pake_operation_t *operation, + psa_pake_step_t step) +{ + if (step != PSA_PAKE_STEP_KEY_SHARE && + step != PSA_PAKE_STEP_ZK_PUBLIC && + step != PSA_PAKE_STEP_ZK_PROOF) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + psa_jpake_computation_stage_t *computation_stage = + &operation->computation_stage.jpake; + + if (computation_stage->state == PSA_PAKE_STATE_INVALID) { + return PSA_ERROR_BAD_STATE; + } + + if (computation_stage->state != PSA_PAKE_STATE_READY && + computation_stage->state != PSA_PAKE_OUTPUT_X1_X2 && + computation_stage->state != PSA_PAKE_OUTPUT_X2S) { + return PSA_ERROR_BAD_STATE; + } + + if (computation_stage->state == PSA_PAKE_STATE_READY) { + if (step != PSA_PAKE_STEP_KEY_SHARE) { + return PSA_ERROR_BAD_STATE; + } + + switch (computation_stage->output_step) { + case PSA_PAKE_STEP_X1_X2: + computation_stage->state = PSA_PAKE_OUTPUT_X1_X2; + break; + case PSA_PAKE_STEP_X2S: + computation_stage->state = PSA_PAKE_OUTPUT_X2S; + break; + default: + return PSA_ERROR_BAD_STATE; + } + + computation_stage->sequence = PSA_PAKE_X1_STEP_KEY_SHARE; + } + + /* Check if step matches current sequence */ + switch (computation_stage->sequence) { + case PSA_PAKE_X1_STEP_KEY_SHARE: + case PSA_PAKE_X2_STEP_KEY_SHARE: + if (step != PSA_PAKE_STEP_KEY_SHARE) { + return PSA_ERROR_BAD_STATE; + } + break; + + case PSA_PAKE_X1_STEP_ZK_PUBLIC: + case PSA_PAKE_X2_STEP_ZK_PUBLIC: + if (step != PSA_PAKE_STEP_ZK_PUBLIC) { + return PSA_ERROR_BAD_STATE; + } + break; + + case PSA_PAKE_X1_STEP_ZK_PROOF: + case PSA_PAKE_X2_STEP_ZK_PROOF: + if (step != PSA_PAKE_STEP_ZK_PROOF) { + return PSA_ERROR_BAD_STATE; + } + break; + + default: + return PSA_ERROR_BAD_STATE; + } + + return PSA_SUCCESS; +} + +static psa_status_t psa_jpake_output_epilogue( + psa_pake_operation_t *operation) +{ + psa_jpake_computation_stage_t *computation_stage = + &operation->computation_stage.jpake; + + if ((computation_stage->state == PSA_PAKE_OUTPUT_X1_X2 && + computation_stage->sequence == PSA_PAKE_X2_STEP_ZK_PROOF) || + (computation_stage->state == PSA_PAKE_OUTPUT_X2S && + computation_stage->sequence == PSA_PAKE_X1_STEP_ZK_PROOF)) { + computation_stage->state = PSA_PAKE_STATE_READY; + computation_stage->output_step++; + computation_stage->sequence = PSA_PAKE_SEQ_INVALID; + } else { + computation_stage->sequence++; + } + + return PSA_SUCCESS; +} +#endif /* PSA_WANT_ALG_JPAKE */ + +psa_status_t psa_pake_output( + psa_pake_operation_t *operation, + psa_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_crypto_driver_pake_step_t driver_step = PSA_JPAKE_STEP_INVALID; + *output_length = 0; + + if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) { + status = psa_pake_complete_inputs(operation); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (output_size == 0) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + switch (operation->alg) { +#if defined(PSA_WANT_ALG_JPAKE) + case PSA_ALG_JPAKE: + status = psa_jpake_output_prologue(operation, step); + if (status != PSA_SUCCESS) { + goto exit; + } + driver_step = convert_jpake_computation_stage_to_driver_step( + &operation->computation_stage.jpake); + break; +#endif /* PSA_WANT_ALG_JPAKE */ + default: + (void) step; + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + status = psa_driver_wrapper_pake_output(operation, driver_step, + output, output_size, output_length); + + if (status != PSA_SUCCESS) { + goto exit; + } + + switch (operation->alg) { +#if defined(PSA_WANT_ALG_JPAKE) + case PSA_ALG_JPAKE: + status = psa_jpake_output_epilogue(operation); + if (status != PSA_SUCCESS) { + goto exit; + } + break; +#endif /* PSA_WANT_ALG_JPAKE */ + default: + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + return PSA_SUCCESS; +exit: + psa_pake_abort(operation); + return status; +} + +#if defined(PSA_WANT_ALG_JPAKE) +static psa_status_t psa_jpake_input_prologue( + psa_pake_operation_t *operation, + psa_pake_step_t step) +{ + if (step != PSA_PAKE_STEP_KEY_SHARE && + step != PSA_PAKE_STEP_ZK_PUBLIC && + step != PSA_PAKE_STEP_ZK_PROOF) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + psa_jpake_computation_stage_t *computation_stage = + &operation->computation_stage.jpake; + + if (computation_stage->state == PSA_PAKE_STATE_INVALID) { + return PSA_ERROR_BAD_STATE; + } + + if (computation_stage->state != PSA_PAKE_STATE_READY && + computation_stage->state != PSA_PAKE_INPUT_X1_X2 && + computation_stage->state != PSA_PAKE_INPUT_X4S) { + return PSA_ERROR_BAD_STATE; + } + + if (computation_stage->state == PSA_PAKE_STATE_READY) { + if (step != PSA_PAKE_STEP_KEY_SHARE) { + return PSA_ERROR_BAD_STATE; + } + + switch (computation_stage->input_step) { + case PSA_PAKE_STEP_X1_X2: + computation_stage->state = PSA_PAKE_INPUT_X1_X2; + break; + case PSA_PAKE_STEP_X2S: + computation_stage->state = PSA_PAKE_INPUT_X4S; + break; + default: + return PSA_ERROR_BAD_STATE; + } + + computation_stage->sequence = PSA_PAKE_X1_STEP_KEY_SHARE; + } + + /* Check if step matches current sequence */ + switch (computation_stage->sequence) { + case PSA_PAKE_X1_STEP_KEY_SHARE: + case PSA_PAKE_X2_STEP_KEY_SHARE: + if (step != PSA_PAKE_STEP_KEY_SHARE) { + return PSA_ERROR_BAD_STATE; + } + break; + + case PSA_PAKE_X1_STEP_ZK_PUBLIC: + case PSA_PAKE_X2_STEP_ZK_PUBLIC: + if (step != PSA_PAKE_STEP_ZK_PUBLIC) { + return PSA_ERROR_BAD_STATE; + } + break; + + case PSA_PAKE_X1_STEP_ZK_PROOF: + case PSA_PAKE_X2_STEP_ZK_PROOF: + if (step != PSA_PAKE_STEP_ZK_PROOF) { + return PSA_ERROR_BAD_STATE; + } + break; + + default: + return PSA_ERROR_BAD_STATE; + } + + return PSA_SUCCESS; +} + +static psa_status_t psa_jpake_input_epilogue( + psa_pake_operation_t *operation) +{ + psa_jpake_computation_stage_t *computation_stage = + &operation->computation_stage.jpake; + + if ((computation_stage->state == PSA_PAKE_INPUT_X1_X2 && + computation_stage->sequence == PSA_PAKE_X2_STEP_ZK_PROOF) || + (computation_stage->state == PSA_PAKE_INPUT_X4S && + computation_stage->sequence == PSA_PAKE_X1_STEP_ZK_PROOF)) { + computation_stage->state = PSA_PAKE_STATE_READY; + computation_stage->input_step++; + computation_stage->sequence = PSA_PAKE_SEQ_INVALID; + } else { + computation_stage->sequence++; + } + + return PSA_SUCCESS; +} +#endif /* PSA_WANT_ALG_JPAKE */ + +psa_status_t psa_pake_input( + psa_pake_operation_t *operation, + psa_pake_step_t step, + const uint8_t *input, + size_t input_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_crypto_driver_pake_step_t driver_step = PSA_JPAKE_STEP_INVALID; + + if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) { + status = psa_pake_complete_inputs(operation); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + + if (input_length == 0 || input_length > PSA_PAKE_INPUT_MAX_SIZE) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + switch (operation->alg) { +#if defined(PSA_WANT_ALG_JPAKE) + case PSA_ALG_JPAKE: + status = psa_jpake_input_prologue(operation, step); + if (status != PSA_SUCCESS) { + goto exit; + } + driver_step = convert_jpake_computation_stage_to_driver_step( + &operation->computation_stage.jpake); + break; +#endif /* PSA_WANT_ALG_JPAKE */ + default: + (void) step; + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + status = psa_driver_wrapper_pake_input(operation, driver_step, + input, input_length); + + if (status != PSA_SUCCESS) { + goto exit; + } + + switch (operation->alg) { +#if defined(PSA_WANT_ALG_JPAKE) + case PSA_ALG_JPAKE: + status = psa_jpake_input_epilogue(operation); + if (status != PSA_SUCCESS) { + goto exit; + } + break; +#endif /* PSA_WANT_ALG_JPAKE */ + default: + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + return PSA_SUCCESS; +exit: + psa_pake_abort(operation); + return status; +} + +psa_status_t psa_pake_get_implicit_key( + psa_pake_operation_t *operation, + psa_key_derivation_operation_t *output) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED; + uint8_t shared_key[MBEDTLS_PSA_JPAKE_BUFFER_SIZE]; + size_t shared_key_len = 0; + + if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + +#if defined(PSA_WANT_ALG_JPAKE) + if (operation->alg == PSA_ALG_JPAKE) { + psa_jpake_computation_stage_t *computation_stage = + &operation->computation_stage.jpake; + if (computation_stage->input_step != PSA_PAKE_STEP_DERIVE || + computation_stage->output_step != PSA_PAKE_STEP_DERIVE) { + status = PSA_ERROR_BAD_STATE; + goto exit; + } + } else +#endif /* PSA_WANT_ALG_JPAKE */ + { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + status = psa_driver_wrapper_pake_get_implicit_key(operation, + shared_key, + sizeof(shared_key), + &shared_key_len); + + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_key_derivation_input_bytes(output, + PSA_KEY_DERIVATION_INPUT_SECRET, + shared_key, + shared_key_len); + + mbedtls_platform_zeroize(shared_key, sizeof(shared_key)); +exit: + abort_status = psa_pake_abort(operation); + return status == PSA_SUCCESS ? abort_status : status; +} + +psa_status_t psa_pake_abort( + psa_pake_operation_t *operation) +{ + psa_status_t status = PSA_SUCCESS; + + if (operation->stage == PSA_PAKE_OPERATION_STAGE_COMPUTATION) { + status = psa_driver_wrapper_pake_abort(operation); + } + + if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) { + if (operation->data.inputs.password != NULL) { + mbedtls_platform_zeroize(operation->data.inputs.password, + operation->data.inputs.password_len); + mbedtls_free(operation->data.inputs.password); + } + if (operation->data.inputs.user != NULL) { + mbedtls_free(operation->data.inputs.user); + } + if (operation->data.inputs.peer != NULL) { + mbedtls_free(operation->data.inputs.peer); + } + } + memset(operation, 0, sizeof(psa_pake_operation_t)); + + return status; +} + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_aead.c b/r5dev/thirdparty/mbedtls/psa_crypto_aead.c new file mode 100644 index 00000000..85d1f39b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_aead.c @@ -0,0 +1,665 @@ +/* + * PSA AEAD entry points + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include "psa_crypto_aead.h" +#include "psa_crypto_core.h" +#include "psa_crypto_cipher.h" + +#include +#include "mbedtls/platform.h" + +#include "mbedtls/ccm.h" +#include "mbedtls/chachapoly.h" +#include "mbedtls/cipher.h" +#include "mbedtls/gcm.h" +#include "mbedtls/error.h" + +static psa_status_t psa_aead_setup( + mbedtls_psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t key_bits; + const mbedtls_cipher_info_t *cipher_info; + mbedtls_cipher_id_t cipher_id; + + (void) key_buffer_size; + + key_bits = attributes->core.bits; + + cipher_info = mbedtls_cipher_info_from_psa(alg, + attributes->core.type, key_bits, + &cipher_id); + if (cipher_info == NULL) { + return PSA_ERROR_NOT_SUPPORTED; + } + + switch (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0): + operation->alg = PSA_ALG_CCM; + /* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16. + * The call to mbedtls_ccm_encrypt_and_tag or + * mbedtls_ccm_auth_decrypt will validate the tag length. */ + if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->core.type) != 16) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + mbedtls_ccm_init(&operation->ctx.ccm); + status = mbedtls_to_psa_error( + mbedtls_ccm_setkey(&operation->ctx.ccm, cipher_id, + key_buffer, (unsigned int) key_bits)); + if (status != PSA_SUCCESS) { + return status; + } + break; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0): + operation->alg = PSA_ALG_GCM; + /* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16. + * The call to mbedtls_gcm_crypt_and_tag or + * mbedtls_gcm_auth_decrypt will validate the tag length. */ + if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->core.type) != 16) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + mbedtls_gcm_init(&operation->ctx.gcm); + status = mbedtls_to_psa_error( + mbedtls_gcm_setkey(&operation->ctx.gcm, cipher_id, + key_buffer, (unsigned int) key_bits)); + if (status != PSA_SUCCESS) { + return status; + } + break; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0): + operation->alg = PSA_ALG_CHACHA20_POLY1305; + /* We only support the default tag length. */ + if (alg != PSA_ALG_CHACHA20_POLY1305) { + return PSA_ERROR_NOT_SUPPORTED; + } + + mbedtls_chachapoly_init(&operation->ctx.chachapoly); + status = mbedtls_to_psa_error( + mbedtls_chachapoly_setkey(&operation->ctx.chachapoly, + key_buffer)); + if (status != PSA_SUCCESS) { + return status; + } + break; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + + default: + (void) status; + (void) key_buffer; + return PSA_ERROR_NOT_SUPPORTED; + } + + operation->key_type = psa_get_key_type(attributes); + + operation->tag_length = PSA_ALG_AEAD_GET_TAG_LENGTH(alg); + + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_aead_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *plaintext, size_t plaintext_length, + uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_psa_aead_operation_t operation = MBEDTLS_PSA_AEAD_OPERATION_INIT; + uint8_t *tag; + + status = psa_aead_setup(&operation, attributes, key_buffer, + key_buffer_size, alg); + + if (status != PSA_SUCCESS) { + goto exit; + } + + /* For all currently supported modes, the tag is at the end of the + * ciphertext. */ + if (ciphertext_size < (plaintext_length + operation.tag_length)) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + tag = ciphertext + plaintext_length; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + if (operation.alg == PSA_ALG_CCM) { + status = mbedtls_to_psa_error( + mbedtls_ccm_encrypt_and_tag(&operation.ctx.ccm, + plaintext_length, + nonce, nonce_length, + additional_data, + additional_data_length, + plaintext, ciphertext, + tag, operation.tag_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + if (operation.alg == PSA_ALG_GCM) { + status = mbedtls_to_psa_error( + mbedtls_gcm_crypt_and_tag(&operation.ctx.gcm, + MBEDTLS_GCM_ENCRYPT, + plaintext_length, + nonce, nonce_length, + additional_data, additional_data_length, + plaintext, ciphertext, + operation.tag_length, tag)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + if (operation.alg == PSA_ALG_CHACHA20_POLY1305) { + if (operation.tag_length != 16) { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + status = mbedtls_to_psa_error( + mbedtls_chachapoly_encrypt_and_tag(&operation.ctx.chachapoly, + plaintext_length, + nonce, + additional_data, + additional_data_length, + plaintext, + ciphertext, + tag)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + { + (void) tag; + (void) nonce; + (void) nonce_length; + (void) additional_data; + (void) additional_data_length; + (void) plaintext; + return PSA_ERROR_NOT_SUPPORTED; + } + + if (status == PSA_SUCCESS) { + *ciphertext_length = plaintext_length + operation.tag_length; + } + +exit: + mbedtls_psa_aead_abort(&operation); + + return status; +} + +/* Locate the tag in a ciphertext buffer containing the encrypted data + * followed by the tag. Return the length of the part preceding the tag in + * *plaintext_length. This is the size of the plaintext in modes where + * the encrypted data has the same size as the plaintext, such as + * CCM and GCM. */ +static psa_status_t psa_aead_unpadded_locate_tag(size_t tag_length, + const uint8_t *ciphertext, + size_t ciphertext_length, + size_t plaintext_size, + const uint8_t **p_tag) +{ + size_t payload_length; + if (tag_length > ciphertext_length) { + return PSA_ERROR_INVALID_ARGUMENT; + } + payload_length = ciphertext_length - tag_length; + if (payload_length > plaintext_size) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + *p_tag = ciphertext + payload_length; + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_aead_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *ciphertext, size_t ciphertext_length, + uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_psa_aead_operation_t operation = MBEDTLS_PSA_AEAD_OPERATION_INIT; + const uint8_t *tag = NULL; + + status = psa_aead_setup(&operation, attributes, key_buffer, + key_buffer_size, alg); + + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_aead_unpadded_locate_tag(operation.tag_length, + ciphertext, ciphertext_length, + plaintext_size, &tag); + if (status != PSA_SUCCESS) { + goto exit; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + if (operation.alg == PSA_ALG_CCM) { + status = mbedtls_to_psa_error( + mbedtls_ccm_auth_decrypt(&operation.ctx.ccm, + ciphertext_length - operation.tag_length, + nonce, nonce_length, + additional_data, + additional_data_length, + ciphertext, plaintext, + tag, operation.tag_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + if (operation.alg == PSA_ALG_GCM) { + status = mbedtls_to_psa_error( + mbedtls_gcm_auth_decrypt(&operation.ctx.gcm, + ciphertext_length - operation.tag_length, + nonce, nonce_length, + additional_data, + additional_data_length, + tag, operation.tag_length, + ciphertext, plaintext)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + if (operation.alg == PSA_ALG_CHACHA20_POLY1305) { + if (operation.tag_length != 16) { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + status = mbedtls_to_psa_error( + mbedtls_chachapoly_auth_decrypt(&operation.ctx.chachapoly, + ciphertext_length - operation.tag_length, + nonce, + additional_data, + additional_data_length, + tag, + ciphertext, + plaintext)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + { + (void) nonce; + (void) nonce_length; + (void) additional_data; + (void) additional_data_length; + (void) plaintext; + return PSA_ERROR_NOT_SUPPORTED; + } + + if (status == PSA_SUCCESS) { + *plaintext_length = ciphertext_length - operation.tag_length; + } + +exit: + mbedtls_psa_aead_abort(&operation); + + if (status == PSA_SUCCESS) { + *plaintext_length = ciphertext_length - operation.tag_length; + } + return status; +} + +/* Set the key and algorithm for a multipart authenticated encryption + * operation. */ +psa_status_t mbedtls_psa_aead_encrypt_setup( + mbedtls_psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_aead_setup(operation, attributes, key_buffer, + key_buffer_size, alg); + + if (status == PSA_SUCCESS) { + operation->is_encrypt = 1; + } + + return status; +} + +/* Set the key and algorithm for a multipart authenticated decryption + * operation. */ +psa_status_t mbedtls_psa_aead_decrypt_setup( + mbedtls_psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_aead_setup(operation, attributes, key_buffer, + key_buffer_size, alg); + + if (status == PSA_SUCCESS) { + operation->is_encrypt = 0; + } + + return status; +} + +/* Set a nonce for the multipart AEAD operation*/ +psa_status_t mbedtls_psa_aead_set_nonce( + mbedtls_psa_aead_operation_t *operation, + const uint8_t *nonce, + size_t nonce_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + if (operation->alg == PSA_ALG_GCM) { + status = mbedtls_to_psa_error( + mbedtls_gcm_starts(&operation->ctx.gcm, + operation->is_encrypt ? + MBEDTLS_GCM_ENCRYPT : MBEDTLS_GCM_DECRYPT, + nonce, + nonce_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + if (operation->alg == PSA_ALG_CCM) { + status = mbedtls_to_psa_error( + mbedtls_ccm_starts(&operation->ctx.ccm, + operation->is_encrypt ? + MBEDTLS_CCM_ENCRYPT : MBEDTLS_CCM_DECRYPT, + nonce, + nonce_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + if (operation->alg == PSA_ALG_CHACHA20_POLY1305) { + /* Note - ChaChaPoly allows an 8 byte nonce, but we would have to + * allocate a buffer in the operation, copy the nonce to it and pad + * it, so for now check the nonce is 12 bytes, as + * mbedtls_chachapoly_starts() assumes it can read 12 bytes from the + * passed in buffer. */ + if (nonce_length != 12) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = mbedtls_to_psa_error( + mbedtls_chachapoly_starts(&operation->ctx.chachapoly, + nonce, + operation->is_encrypt ? + MBEDTLS_CHACHAPOLY_ENCRYPT : + MBEDTLS_CHACHAPOLY_DECRYPT)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + { + (void) operation; + (void) nonce; + (void) nonce_length; + + return PSA_ERROR_NOT_SUPPORTED; + } + + return status; +} + +/* Declare the lengths of the message and additional data for AEAD. */ +psa_status_t mbedtls_psa_aead_set_lengths( + mbedtls_psa_aead_operation_t *operation, + size_t ad_length, + size_t plaintext_length) +{ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + if (operation->alg == PSA_ALG_CCM) { + return mbedtls_to_psa_error( + mbedtls_ccm_set_lengths(&operation->ctx.ccm, + ad_length, + plaintext_length, + operation->tag_length)); + + } +#else /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ + (void) operation; + (void) ad_length; + (void) plaintext_length; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ + + return PSA_SUCCESS; +} + +/* Pass additional data to an active multipart AEAD operation. */ +psa_status_t mbedtls_psa_aead_update_ad( + mbedtls_psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + if (operation->alg == PSA_ALG_GCM) { + status = mbedtls_to_psa_error( + mbedtls_gcm_update_ad(&operation->ctx.gcm, input, input_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + if (operation->alg == PSA_ALG_CCM) { + status = mbedtls_to_psa_error( + mbedtls_ccm_update_ad(&operation->ctx.ccm, input, input_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + if (operation->alg == PSA_ALG_CHACHA20_POLY1305) { + status = mbedtls_to_psa_error( + mbedtls_chachapoly_update_aad(&operation->ctx.chachapoly, + input, + input_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + { + (void) operation; + (void) input; + (void) input_length; + + return PSA_ERROR_NOT_SUPPORTED; + } + + return status; +} + +/* Encrypt or decrypt a message fragment in an active multipart AEAD + * operation.*/ +psa_status_t mbedtls_psa_aead_update( + mbedtls_psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + size_t update_output_length; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + update_output_length = input_length; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + if (operation->alg == PSA_ALG_GCM) { + status = mbedtls_to_psa_error( + mbedtls_gcm_update(&operation->ctx.gcm, + input, input_length, + output, output_size, + &update_output_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + if (operation->alg == PSA_ALG_CCM) { + if (output_size < input_length) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + status = mbedtls_to_psa_error( + mbedtls_ccm_update(&operation->ctx.ccm, + input, input_length, + output, output_size, + &update_output_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + if (operation->alg == PSA_ALG_CHACHA20_POLY1305) { + if (output_size < input_length) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + status = mbedtls_to_psa_error( + mbedtls_chachapoly_update(&operation->ctx.chachapoly, + input_length, + input, + output)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + { + (void) operation; + (void) input; + (void) output; + (void) output_size; + + return PSA_ERROR_NOT_SUPPORTED; + } + + if (status == PSA_SUCCESS) { + *output_length = update_output_length; + } + + return status; +} + +/* Finish encrypting a message in a multipart AEAD operation. */ +psa_status_t mbedtls_psa_aead_finish( + mbedtls_psa_aead_operation_t *operation, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length, + uint8_t *tag, + size_t tag_size, + size_t *tag_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t finish_output_size = 0; + + if (tag_size < operation->tag_length) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + if (operation->alg == PSA_ALG_GCM) { + status = mbedtls_to_psa_error( + mbedtls_gcm_finish(&operation->ctx.gcm, + ciphertext, ciphertext_size, ciphertext_length, + tag, operation->tag_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + if (operation->alg == PSA_ALG_CCM) { + /* tag must be big enough to store a tag of size passed into set + * lengths. */ + if (tag_size < operation->tag_length) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + status = mbedtls_to_psa_error( + mbedtls_ccm_finish(&operation->ctx.ccm, + tag, operation->tag_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + if (operation->alg == PSA_ALG_CHACHA20_POLY1305) { + /* Belt and braces. Although the above tag_size check should have + * already done this, if we later start supporting smaller tag sizes + * for chachapoly, then passing a tag buffer smaller than 16 into here + * could cause a buffer overflow, so better safe than sorry. */ + if (tag_size < 16) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + status = mbedtls_to_psa_error( + mbedtls_chachapoly_finish(&operation->ctx.chachapoly, + tag)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + { + (void) ciphertext; + (void) ciphertext_size; + (void) ciphertext_length; + (void) tag; + (void) tag_size; + (void) tag_length; + + return PSA_ERROR_NOT_SUPPORTED; + } + + if (status == PSA_SUCCESS) { + /* This will be zero for all supported algorithms currently, but left + * here for future support. */ + *ciphertext_length = finish_output_size; + *tag_length = operation->tag_length; + } + + return status; +} + +/* Abort an AEAD operation */ +psa_status_t mbedtls_psa_aead_abort( + mbedtls_psa_aead_operation_t *operation) +{ + switch (operation->alg) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + case PSA_ALG_CCM: + mbedtls_ccm_free(&operation->ctx.ccm); + break; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + case PSA_ALG_GCM: + mbedtls_gcm_free(&operation->ctx.gcm); + break; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + case PSA_ALG_CHACHA20_POLY1305: + mbedtls_chachapoly_free(&operation->ctx.chachapoly); + break; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */ + } + + operation->is_encrypt = 0; + + return PSA_SUCCESS; +} + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_aead.h b/r5dev/thirdparty/mbedtls/psa_crypto_aead.h new file mode 100644 index 00000000..4b24b0f6 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_aead.h @@ -0,0 +1,511 @@ +/* + * PSA AEAD driver entry points + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_AEAD_H +#define PSA_CRYPTO_AEAD_H + +#include + +/** + * \brief Process an authenticated encryption operation. + * + * \note The signature of this function is that of a PSA driver + * aead_encrypt entry point. This function behaves as an aead_encrypt + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param alg The AEAD algorithm to compute. + * \param[in] nonce Nonce or IV to use. + * \param nonce_length Size of the nonce buffer in bytes. This must + * be appropriate for the selected algorithm. + * The default nonce size is + * PSA_AEAD_NONCE_LENGTH(key_type, alg) where + * key_type is the type of key. + * \param[in] additional_data Additional data that will be authenticated + * but not encrypted. + * \param additional_data_length Size of additional_data in bytes. + * \param[in] plaintext Data that will be authenticated and encrypted. + * \param plaintext_length Size of plaintext in bytes. + * \param[out] ciphertext Output buffer for the authenticated and + * encrypted data. The additional data is not + * part of this output. For algorithms where the + * encrypted data and the authentication tag are + * defined as separate outputs, the + * authentication tag is appended to the + * encrypted data. + * \param ciphertext_size Size of the ciphertext buffer in bytes. This + * must be appropriate for the selected algorithm + * and key: + * - A sufficient output size is + * PSA_AEAD_ENCRYPT_OUTPUT_SIZE(key_type, alg, + * plaintext_length) where key_type is the type + * of key. + * - PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE( + * plaintext_length) evaluates to the maximum + * ciphertext size of any supported AEAD + * encryption. + * \param[out] ciphertext_length On success, the size of the output in the + * ciphertext buffer. + * + * \retval #PSA_SUCCESS Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * ciphertext_size is too small. + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_aead_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *plaintext, size_t plaintext_length, + uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length); + +/** + * \brief Process an authenticated decryption operation. + * + * \note The signature of this function is that of a PSA driver + * aead_decrypt entry point. This function behaves as an aead_decrypt + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param alg The AEAD algorithm to compute. + * \param[in] nonce Nonce or IV to use. + * \param nonce_length Size of the nonce buffer in bytes. This must + * be appropriate for the selected algorithm. + * The default nonce size is + * PSA_AEAD_NONCE_LENGTH(key_type, alg) where + * key_type is the type of key. + * \param[in] additional_data Additional data that has been authenticated + * but not encrypted. + * \param additional_data_length Size of additional_data in bytes. + * \param[in] ciphertext Data that has been authenticated and + * encrypted. For algorithms where the encrypted + * data and the authentication tag are defined + * as separate inputs, the buffer contains + * encrypted data followed by the authentication + * tag. + * \param ciphertext_length Size of ciphertext in bytes. + * \param[out] plaintext Output buffer for the decrypted data. + * \param plaintext_size Size of the plaintext buffer in bytes. This + * must be appropriate for the selected algorithm + * and key: + * - A sufficient output size is + * PSA_AEAD_DECRYPT_OUTPUT_SIZE(key_type, alg, + * ciphertext_length) where key_type is the + * type of key. + * - PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE( + * ciphertext_length) evaluates to the maximum + * plaintext size of any supported AEAD + * decryption. + * \param[out] plaintext_length On success, the size of the output in the + * plaintext buffer. + * + * \retval #PSA_SUCCESS Success. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The cipher is not authentic. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * plaintext_size is too small. + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_aead_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *ciphertext, size_t ciphertext_length, + uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length); + +/** Set the key for a multipart authenticated encryption operation. + * + * \note The signature of this function is that of a PSA driver + * aead_encrypt_setup entry point. This function behaves as an + * aead_encrypt_setup entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * If an error occurs at any step after a call to + * mbedtls_psa_aead_encrypt_setup(), the operation is reset by the PSA core by a + * call to mbedtls_psa_aead_abort(). The PSA core may call + * mbedtls_psa_aead_abort() at any time after the operation has been + * initialized, and is required to when the operation is no longer needed. + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #mbedtls_psa_aead_operation_t and not yet in + * use. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + It must be consistent with the size in bits + recorded in \p attributes. + * \param alg The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * An invalid block length was supplied. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * Failed to allocate memory for key material + */ +psa_status_t mbedtls_psa_aead_encrypt_setup( + mbedtls_psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg); + +/** Set the key for a multipart authenticated decryption operation. + * + * \note The signature of this function is that of a PSA driver + * aead_decrypt_setup entry point. This function behaves as an + * aead_decrypt_setup entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * If an error occurs at any step after a call to + * mbedtls_psa_aead_decrypt_setup(), the PSA core resets the operation by a + * call to mbedtls_psa_aead_abort(). The PSA core may call + * mbedtls_psa_aead_abort() at any time after the operation has been + * initialized, and is required to when the operation is no longer needed. + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized as per the documentation for + * #mbedtls_psa_aead_operation_t and not yet in + * use. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + It must be consistent with the size in bits + recorded in \p attributes. + * \param alg The AEAD algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_AEAD(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * An invalid block length was supplied. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * Failed to allocate memory for key material + */ +psa_status_t mbedtls_psa_aead_decrypt_setup( + mbedtls_psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg); + +/** Set the nonce for an authenticated encryption or decryption operation. + * + * \note The signature of this function is that of a PSA driver aead_set_nonce + * entry point. This function behaves as an aead_set_nonce entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * This function sets the nonce for the authenticated + * encryption or decryption operation. + * + * The PSA core calls mbedtls_psa_aead_encrypt_setup() or + * mbedtls_psa_aead_decrypt_setup() before calling this function. + * + * If this function returns an error status, the PSA core will call + * mbedtls_psa_aead_abort(). + * + * \param[in,out] operation Active AEAD operation. + * \param[in] nonce Buffer containing the nonce to use. + * \param nonce_length Size of the nonce in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size of \p nonce is not acceptable for the chosen algorithm. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Algorithm previously set is not supported in this configuration of + * the library. + */ +psa_status_t mbedtls_psa_aead_set_nonce( + mbedtls_psa_aead_operation_t *operation, + const uint8_t *nonce, + size_t nonce_length); + +/** Declare the lengths of the message and additional data for AEAD. + * + * \note The signature of this function is that of a PSA driver aead_set_lengths + * entry point. This function behaves as an aead_set_lengths entry point + * as defined in the PSA driver interface specification for transparent + * drivers. + * + * The PSA core calls this function before calling mbedtls_psa_aead_update_ad() + * or mbedtls_psa_aead_update() if the algorithm for the operation requires it. + * If the algorithm does not require it, calling this function is optional, but + * if this function is called then the implementation must enforce the lengths. + * + * The PSA core may call this function before or after setting the nonce with + * mbedtls_psa_aead_set_nonce(). + * + * - For #PSA_ALG_CCM, calling this function is required. + * - For the other AEAD algorithms defined in this specification, calling + * this function is not required. + * + * If this function returns an error status, the PSA core calls + * mbedtls_psa_aead_abort(). + * + * \param[in,out] operation Active AEAD operation. + * \param ad_length Size of the non-encrypted additional + * authenticated data in bytes. + * \param plaintext_length Size of the plaintext to encrypt in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * At least one of the lengths is not acceptable for the chosen + * algorithm. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Algorithm previously set is not supported in this configuration of + * the library. + */ +psa_status_t mbedtls_psa_aead_set_lengths( + mbedtls_psa_aead_operation_t *operation, + size_t ad_length, + size_t plaintext_length); + +/** Pass additional data to an active AEAD operation. + * + * \note The signature of this function is that of a PSA driver + * aead_update_ad entry point. This function behaves as an aead_update_ad + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * Additional data is authenticated, but not encrypted. + * + * The PSA core can call this function multiple times to pass successive + * fragments of the additional data. It will not call this function after + * passing data to encrypt or decrypt with mbedtls_psa_aead_update(). + * + * Before calling this function, the PSA core will: + * 1. Call either mbedtls_psa_aead_encrypt_setup() or + * mbedtls_psa_aead_decrypt_setup(). + * 2. Set the nonce with mbedtls_psa_aead_set_nonce(). + * + * If this function returns an error status, the PSA core will call + * mbedtls_psa_aead_abort(). + * + * \param[in,out] operation Active AEAD operation. + * \param[in] input Buffer containing the fragment of + * additional data. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Algorithm previously set is not supported in this configuration of + * the library. + */ +psa_status_t mbedtls_psa_aead_update_ad( + mbedtls_psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length); + +/** Encrypt or decrypt a message fragment in an active AEAD operation. + * + * \note The signature of this function is that of a PSA driver + * aead_update entry point. This function behaves as an aead_update entry + * point as defined in the PSA driver interface specification for + * transparent drivers. + * + * Before calling this function, the PSA core will: + * 1. Call either mbedtls_psa_aead_encrypt_setup() or + * mbedtls_psa_aead_decrypt_setup(). The choice of setup function + * determines whether this function encrypts or decrypts its input. + * 2. Set the nonce with mbedtls_psa_aead_set_nonce(). + * 3. Call mbedtls_psa_aead_update_ad() to pass all the additional data. + * + * If this function returns an error status, the PSA core will call + * mbedtls_psa_aead_abort(). + * + * This function does not require the input to be aligned to any + * particular block boundary. If the implementation can only process + * a whole block at a time, it must consume all the input provided, but + * it may delay the end of the corresponding output until a subsequent + * call to mbedtls_psa_aead_update(), mbedtls_psa_aead_finish() provides + * sufficient input. The amount of data that can be delayed in this way is + * bounded by #PSA_AEAD_UPDATE_OUTPUT_SIZE. + * + * \param[in,out] operation Active AEAD operation. + * \param[in] input Buffer containing the message fragment to + * encrypt or decrypt. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] output Buffer where the output is to be written. + * \param output_size Size of the \p output buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - A sufficient output size is + * #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type, + * \c alg, \p input_length) where + * \c key_type is the type of key and \c alg is + * the algorithm that were used to set up the + * operation. + * - #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p + * input_length) evaluates to the maximum + * output size of any supported AEAD + * algorithm. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS + * Success. + * + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * #PSA_AEAD_UPDATE_OUTPUT_SIZE(\c key_type, \c alg, \p input_length) or + * #PSA_AEAD_UPDATE_OUTPUT_MAX_SIZE(\p input_length) can be used to + * determine the required buffer size. + */ +psa_status_t mbedtls_psa_aead_update( + mbedtls_psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Finish encrypting a message in an AEAD operation. + * + * \note The signature of this function is that of a PSA driver + * aead_finish entry point. This function behaves as an aead_finish entry + * point as defined in the PSA driver interface specification for + * transparent drivers. + * + * The operation must have been set up by the PSA core with + * mbedtls_psa_aead_encrypt_setup(). + * + * This function finishes the authentication of the additional data + * formed by concatenating the inputs passed to preceding calls to + * mbedtls_psa_aead_update_ad() with the plaintext formed by concatenating the + * inputs passed to preceding calls to mbedtls_psa_aead_update(). + * + * This function has two output buffers: + * - \p ciphertext contains trailing ciphertext that was buffered from + * preceding calls to mbedtls_psa_aead_update(). + * - \p tag contains the authentication tag. + * + * Whether or not this function returns successfully, the PSA core subsequently + * calls mbedtls_psa_aead_abort() to deactivate the operation. + * + * \param[in,out] operation Active AEAD operation. + * \param[out] ciphertext Buffer where the last part of the ciphertext + * is to be written. + * \param ciphertext_size Size of the \p ciphertext buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - A sufficient output size is + * #PSA_AEAD_FINISH_OUTPUT_SIZE(\c key_type, + * \c alg) where \c key_type is the type of key + * and \c alg is the algorithm that were used to + * set up the operation. + * - #PSA_AEAD_FINISH_OUTPUT_MAX_SIZE evaluates to + * the maximum output size of any supported AEAD + * algorithm. + * \param[out] ciphertext_length On success, the number of bytes of + * returned ciphertext. + * \param[out] tag Buffer where the authentication tag is + * to be written. + * \param tag_size Size of the \p tag buffer in bytes. + * This must be appropriate for the selected + * algorithm and key: + * - The exact tag size is #PSA_AEAD_TAG_LENGTH(\c + * key_type, \c key_bits, \c alg) where + * \c key_type and \c key_bits are the type and + * bit-size of the key, and \c alg are the + * algorithm that were used in the call to + * mbedtls_psa_aead_encrypt_setup(). + * - #PSA_AEAD_TAG_MAX_SIZE evaluates to the + * maximum tag size of any supported AEAD + * algorithm. + * \param[out] tag_length On success, the number of bytes + * that make up the returned tag. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p tag buffer is too small. + * #PSA_AEAD_TAG_LENGTH(\c key_type, key_bits, \c alg) or + * #PSA_AEAD_TAG_MAX_SIZE can be used to determine the required \p tag + * buffer size. + */ +psa_status_t mbedtls_psa_aead_finish( + mbedtls_psa_aead_operation_t *operation, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length, + uint8_t *tag, + size_t tag_size, + size_t *tag_length); + +/** Abort an AEAD operation. + * + * \note The signature of this function is that of a PSA driver + * aead_abort entry point. This function behaves as an aead_abort entry + * point as defined in the PSA driver interface specification for + * transparent drivers. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation by the PSA core by it calling + * mbedtls_psa_aead_encrypt_setup() or mbedtls_psa_aead_decrypt_setup() again. + * + * The PSA core may call this function any time after the operation object has + * been initialized as described in #mbedtls_psa_aead_operation_t. + * + * In particular, calling mbedtls_psa_aead_abort() after the operation has been + * terminated by a call to mbedtls_psa_aead_abort() or + * mbedtls_psa_aead_finish() is safe and has no effect. + * + * \param[in,out] operation Initialized AEAD operation. + * + * \retval #PSA_SUCCESS + * Success. + */ +psa_status_t mbedtls_psa_aead_abort( + mbedtls_psa_aead_operation_t *operation); + +#endif /* PSA_CRYPTO_AEAD_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_cipher.c b/r5dev/thirdparty/mbedtls/psa_crypto_cipher.c new file mode 100644 index 00000000..c501144e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_cipher.c @@ -0,0 +1,602 @@ +/* + * PSA cipher driver entry points + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include "psa_crypto_cipher.h" +#include "psa_crypto_core.h" +#include "psa_crypto_random_impl.h" + +#include "mbedtls/cipher.h" +#include "mbedtls/error.h" + +#include + +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa( + psa_algorithm_t alg, + psa_key_type_t key_type, + size_t key_bits, + mbedtls_cipher_id_t *cipher_id) +{ + mbedtls_cipher_mode_t mode; + mbedtls_cipher_id_t cipher_id_tmp; + + if (PSA_ALG_IS_AEAD(alg)) { + alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0); + } + + if (PSA_ALG_IS_CIPHER(alg) || PSA_ALG_IS_AEAD(alg)) { + switch (alg) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER) + case PSA_ALG_STREAM_CIPHER: + mode = MBEDTLS_MODE_STREAM; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CTR) + case PSA_ALG_CTR: + mode = MBEDTLS_MODE_CTR; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CFB) + case PSA_ALG_CFB: + mode = MBEDTLS_MODE_CFB; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_OFB) + case PSA_ALG_OFB: + mode = MBEDTLS_MODE_OFB; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) + case PSA_ALG_ECB_NO_PADDING: + mode = MBEDTLS_MODE_ECB; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING) + case PSA_ALG_CBC_NO_PADDING: + mode = MBEDTLS_MODE_CBC; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7) + case PSA_ALG_CBC_PKCS7: + mode = MBEDTLS_MODE_CBC; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG) + case PSA_ALG_CCM_STAR_NO_TAG: + mode = MBEDTLS_MODE_CCM_STAR_NO_TAG; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0): + mode = MBEDTLS_MODE_CCM; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0): + mode = MBEDTLS_MODE_GCM; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305) + case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0): + mode = MBEDTLS_MODE_CHACHAPOLY; + break; +#endif + default: + return NULL; + } + } else if (alg == PSA_ALG_CMAC) { + mode = MBEDTLS_MODE_ECB; + } else { + return NULL; + } + + switch (key_type) { +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES) + case PSA_KEY_TYPE_AES: + cipher_id_tmp = MBEDTLS_CIPHER_ID_AES; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA) + case PSA_KEY_TYPE_ARIA: + cipher_id_tmp = MBEDTLS_CIPHER_ID_ARIA; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) + case PSA_KEY_TYPE_DES: + /* key_bits is 64 for Single-DES, 128 for two-key Triple-DES, + * and 192 for three-key Triple-DES. */ + if (key_bits == 64) { + cipher_id_tmp = MBEDTLS_CIPHER_ID_DES; + } else { + cipher_id_tmp = MBEDTLS_CIPHER_ID_3DES; + } + /* mbedtls doesn't recognize two-key Triple-DES as an algorithm, + * but two-key Triple-DES is functionally three-key Triple-DES + * with K1=K3, so that's how we present it to mbedtls. */ + if (key_bits == 128) { + key_bits = 192; + } + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA) + case PSA_KEY_TYPE_CAMELLIA: + cipher_id_tmp = MBEDTLS_CIPHER_ID_CAMELLIA; + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20) + case PSA_KEY_TYPE_CHACHA20: + cipher_id_tmp = MBEDTLS_CIPHER_ID_CHACHA20; + break; +#endif + default: + return NULL; + } + if (cipher_id != NULL) { + *cipher_id = cipher_id_tmp; + } + + return mbedtls_cipher_info_from_values(cipher_id_tmp, + (int) key_bits, mode); +} + +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + +static psa_status_t psa_cipher_setup( + mbedtls_psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + mbedtls_operation_t cipher_operation) +{ + int ret = 0; + size_t key_bits; + const mbedtls_cipher_info_t *cipher_info = NULL; + psa_key_type_t key_type = attributes->core.type; + + (void) key_buffer_size; + + mbedtls_cipher_init(&operation->ctx.cipher); + + operation->alg = alg; + key_bits = attributes->core.bits; + cipher_info = mbedtls_cipher_info_from_psa(alg, key_type, + key_bits, NULL); + if (cipher_info == NULL) { + return PSA_ERROR_NOT_SUPPORTED; + } + + ret = mbedtls_cipher_setup(&operation->ctx.cipher, cipher_info); + if (ret != 0) { + goto exit; + } + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) + if (key_type == PSA_KEY_TYPE_DES && key_bits == 128) { + /* Two-key Triple-DES is 3-key Triple-DES with K1=K3 */ + uint8_t keys[24]; + memcpy(keys, key_buffer, 16); + memcpy(keys + 16, key_buffer, 8); + ret = mbedtls_cipher_setkey(&operation->ctx.cipher, + keys, + 192, cipher_operation); + } else +#endif + { + ret = mbedtls_cipher_setkey(&operation->ctx.cipher, key_buffer, + (int) key_bits, cipher_operation); + } + if (ret != 0) { + goto exit; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7) + switch (alg) { + case PSA_ALG_CBC_NO_PADDING: + ret = mbedtls_cipher_set_padding_mode(&operation->ctx.cipher, + MBEDTLS_PADDING_NONE); + break; + case PSA_ALG_CBC_PKCS7: + ret = mbedtls_cipher_set_padding_mode(&operation->ctx.cipher, + MBEDTLS_PADDING_PKCS7); + break; + default: + /* The algorithm doesn't involve padding. */ + ret = 0; + break; + } + if (ret != 0) { + goto exit; + } +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING || + MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7 */ + + operation->block_length = (PSA_ALG_IS_STREAM_CIPHER(alg) ? 1 : + PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type)); + operation->iv_length = PSA_CIPHER_IV_LENGTH(key_type, alg); + +exit: + return mbedtls_to_psa_error(ret); +} + +psa_status_t mbedtls_psa_cipher_encrypt_setup( + mbedtls_psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg) +{ + return psa_cipher_setup(operation, attributes, + key_buffer, key_buffer_size, + alg, MBEDTLS_ENCRYPT); +} + +psa_status_t mbedtls_psa_cipher_decrypt_setup( + mbedtls_psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg) +{ + return psa_cipher_setup(operation, attributes, + key_buffer, key_buffer_size, + alg, MBEDTLS_DECRYPT); +} + +psa_status_t mbedtls_psa_cipher_set_iv( + mbedtls_psa_cipher_operation_t *operation, + const uint8_t *iv, size_t iv_length) +{ + if (iv_length != operation->iv_length) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return mbedtls_to_psa_error( + mbedtls_cipher_set_iv(&operation->ctx.cipher, + iv, iv_length)); +} + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) +/** Process input for which the algorithm is set to ECB mode. + * + * This requires manual processing, since the PSA API is defined as being + * able to process arbitrary-length calls to psa_cipher_update() with ECB mode, + * but the underlying mbedtls_cipher_update only takes full blocks. + * + * \param ctx The mbedtls cipher context to use. It must have been + * set up for ECB. + * \param[in] input The input plaintext or ciphertext to process. + * \param input_length The number of bytes to process from \p input. + * This does not need to be aligned to a block boundary. + * If there is a partial block at the end of the input, + * it is stored in \p ctx for future processing. + * \param output The buffer where the output is written. It must be + * at least `BS * floor((p + input_length) / BS)` bytes + * long, where `p` is the number of bytes in the + * unprocessed partial block in \p ctx (with + * `0 <= p <= BS - 1`) and `BS` is the block size. + * \param output_length On success, the number of bytes written to \p output. + * \c 0 on error. + * + * \return #PSA_SUCCESS or an error from a hardware accelerator + */ +static psa_status_t psa_cipher_update_ecb( + mbedtls_cipher_context_t *ctx, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t block_size = ctx->cipher_info->block_size; + size_t internal_output_length = 0; + *output_length = 0; + + if (input_length == 0) { + status = PSA_SUCCESS; + goto exit; + } + + if (ctx->unprocessed_len > 0) { + /* Fill up to block size, and run the block if there's a full one. */ + size_t bytes_to_copy = block_size - ctx->unprocessed_len; + + if (input_length < bytes_to_copy) { + bytes_to_copy = input_length; + } + + memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]), + input, bytes_to_copy); + input_length -= bytes_to_copy; + input += bytes_to_copy; + ctx->unprocessed_len += bytes_to_copy; + + if (ctx->unprocessed_len == block_size) { + status = mbedtls_to_psa_error( + mbedtls_cipher_update(ctx, + ctx->unprocessed_data, + block_size, + output, &internal_output_length)); + + if (status != PSA_SUCCESS) { + goto exit; + } + + output += internal_output_length; + *output_length += internal_output_length; + ctx->unprocessed_len = 0; + } + } + + while (input_length >= block_size) { + /* Run all full blocks we have, one by one */ + status = mbedtls_to_psa_error( + mbedtls_cipher_update(ctx, input, + block_size, + output, &internal_output_length)); + + if (status != PSA_SUCCESS) { + goto exit; + } + + input_length -= block_size; + input += block_size; + + output += internal_output_length; + *output_length += internal_output_length; + } + + if (input_length > 0) { + /* Save unprocessed bytes for later processing */ + memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]), + input, input_length); + ctx->unprocessed_len += input_length; + } + + status = PSA_SUCCESS; + +exit: + return status; +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */ + +psa_status_t mbedtls_psa_cipher_update( + mbedtls_psa_cipher_operation_t *operation, + const uint8_t *input, size_t input_length, + uint8_t *output, size_t output_size, size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t expected_output_size; + + if (!PSA_ALG_IS_STREAM_CIPHER(operation->alg)) { + /* Take the unprocessed partial block left over from previous + * update calls, if any, plus the input to this call. Remove + * the last partial block, if any. You get the data that will be + * output in this call. */ + expected_output_size = + (operation->ctx.cipher.unprocessed_len + input_length) + / operation->block_length * operation->block_length; + } else { + expected_output_size = input_length; + } + + if (output_size < expected_output_size) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING) + if (operation->alg == PSA_ALG_ECB_NO_PADDING) { + /* mbedtls_cipher_update has an API inconsistency: it will only + * process a single block at a time in ECB mode. Abstract away that + * inconsistency here to match the PSA API behaviour. */ + status = psa_cipher_update_ecb(&operation->ctx.cipher, + input, + input_length, + output, + output_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */ + { + status = mbedtls_to_psa_error( + mbedtls_cipher_update(&operation->ctx.cipher, input, + input_length, output, output_length)); + + if (*output_length > output_size) { + return PSA_ERROR_CORRUPTION_DETECTED; + } + } + + return status; +} + +psa_status_t mbedtls_psa_cipher_finish( + mbedtls_psa_cipher_operation_t *operation, + 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]; + + if (operation->ctx.cipher.unprocessed_len != 0) { + if (operation->alg == PSA_ALG_ECB_NO_PADDING || + operation->alg == PSA_ALG_CBC_NO_PADDING) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + } + + status = mbedtls_to_psa_error( + mbedtls_cipher_finish(&operation->ctx.cipher, + temp_output_buffer, + output_length)); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (*output_length == 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; + } + +exit: + mbedtls_platform_zeroize(temp_output_buffer, + sizeof(temp_output_buffer)); + + return status; +} + +psa_status_t mbedtls_psa_cipher_abort( + mbedtls_psa_cipher_operation_t *operation) +{ + /* Sanity check (shouldn't happen: operation->alg should + * always have been initialized to a valid value). */ + if (!PSA_ALG_IS_CIPHER(operation->alg)) { + return PSA_ERROR_BAD_STATE; + } + + mbedtls_cipher_free(&operation->ctx.cipher); + + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_cipher_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *iv, + size_t iv_length, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_psa_cipher_operation_t operation = MBEDTLS_PSA_CIPHER_OPERATION_INIT; + size_t update_output_length, finish_output_length; + + status = mbedtls_psa_cipher_encrypt_setup(&operation, attributes, + key_buffer, key_buffer_size, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (iv_length > 0) { + status = mbedtls_psa_cipher_set_iv(&operation, iv, iv_length); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + status = mbedtls_psa_cipher_update(&operation, input, input_length, + output, output_size, + &update_output_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = mbedtls_psa_cipher_finish( + &operation, + mbedtls_buffer_offset(output, update_output_length), + output_size - update_output_length, &finish_output_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + *output_length = update_output_length + finish_output_length; + +exit: + if (status == PSA_SUCCESS) { + status = mbedtls_psa_cipher_abort(&operation); + } else { + mbedtls_psa_cipher_abort(&operation); + } + + return status; +} + +psa_status_t mbedtls_psa_cipher_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_psa_cipher_operation_t operation = MBEDTLS_PSA_CIPHER_OPERATION_INIT; + size_t olength, accumulated_length; + + status = mbedtls_psa_cipher_decrypt_setup(&operation, attributes, + key_buffer, key_buffer_size, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (operation.iv_length > 0) { + status = mbedtls_psa_cipher_set_iv(&operation, + input, operation.iv_length); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + status = mbedtls_psa_cipher_update( + &operation, + mbedtls_buffer_offset_const(input, operation.iv_length), + input_length - operation.iv_length, + output, output_size, &olength); + if (status != PSA_SUCCESS) { + goto exit; + } + + accumulated_length = olength; + + status = mbedtls_psa_cipher_finish( + &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); + } + + return status; +} +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_cipher.h b/r5dev/thirdparty/mbedtls/psa_crypto_cipher.h new file mode 100644 index 00000000..bf43ff08 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_cipher.h @@ -0,0 +1,305 @@ +/* + * PSA cipher driver entry points and associated auxiliary functions + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_CIPHER_H +#define PSA_CRYPTO_CIPHER_H + +#include +#include + +/** Get Mbed TLS cipher information given the cipher algorithm PSA identifier + * as well as the PSA type and size of the key to be used with the cipher + * algorithm. + * + * \param alg PSA cipher algorithm identifier + * \param key_type PSA key type + * \param key_bits Size of the key in bits + * \param[out] cipher_id Mbed TLS cipher algorithm identifier + * + * \return The Mbed TLS cipher information of the cipher algorithm. + * \c NULL if the PSA cipher algorithm is not supported. + */ +const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa( + psa_algorithm_t alg, psa_key_type_t key_type, size_t key_bits, + mbedtls_cipher_id_t *cipher_id); + +/** + * \brief Set the key for a multipart symmetric encryption operation. + * + * \note The signature of this function is that of a PSA driver + * cipher_encrypt_setup entry point. This function behaves as a + * cipher_encrypt_setup entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in,out] operation The operation object to set up. It has been + * initialized as per the documentation for + * #psa_cipher_operation_t and not yet in use. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_cipher_encrypt_setup( + mbedtls_psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg); + +/** + * \brief Set the key for a multipart symmetric decryption operation. + * + * \note The signature of this function is that of a PSA driver + * cipher_decrypt_setup entry point. This function behaves as a + * cipher_decrypt_setup entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in,out] operation The operation object to set up. It has been + * initialized as per the documentation for + * #psa_cipher_operation_t and not yet in use. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_cipher_decrypt_setup( + mbedtls_psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg); + +/** Set the IV for a symmetric encryption or decryption operation. + * + * This function sets the IV (initialization vector), nonce + * or initial counter value for the encryption or decryption operation. + * + * \note The signature of this function is that of a PSA driver + * cipher_set_iv entry point. This function behaves as a + * cipher_set_iv entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in,out] operation Active cipher operation. + * \param[in] iv Buffer containing the IV to use. + * \param[in] iv_length Size of the IV in bytes. It is guaranteed by + * the core to be less or equal to + * PSA_CIPHER_IV_MAX_SIZE. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size of \p iv is not acceptable for the chosen algorithm, + * or the chosen algorithm does not use an IV. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_cipher_set_iv( + mbedtls_psa_cipher_operation_t *operation, + const uint8_t *iv, size_t iv_length); + +/** Encrypt or decrypt a message fragment in an active cipher operation. + * + * \note The signature of this function is that of a PSA driver + * cipher_update entry point. This function behaves as a + * cipher_update entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in,out] operation Active cipher operation. + * \param[in] input Buffer containing the message fragment to + * encrypt or decrypt. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[out] output Buffer where the output is to be written. + * \param[in] output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_cipher_update( + mbedtls_psa_cipher_operation_t *operation, + const uint8_t *input, size_t input_length, + uint8_t *output, size_t output_size, size_t *output_length); + +/** Finish encrypting or decrypting a message in a cipher operation. + * + * \note The signature of this function is that of a PSA driver + * cipher_finish entry point. This function behaves as a + * cipher_finish entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in,out] operation Active cipher operation. + * \param[out] output Buffer where the output is to be written. + * \param[in] output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The total input size passed to this operation is not valid for + * this particular algorithm. For example, the algorithm is a based + * on block cipher and requires a whole number of blocks, but the + * total input size is not a multiple of the block size. + * \retval #PSA_ERROR_INVALID_PADDING + * This is a decryption operation for an algorithm that includes + * padding, and the ciphertext does not contain valid padding. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_cipher_finish( + mbedtls_psa_cipher_operation_t *operation, + uint8_t *output, size_t output_size, size_t *output_length); + +/** Abort a cipher operation. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation. + * + * \note The signature of this function is that of a PSA driver + * cipher_abort entry point. This function behaves as a + * cipher_abort entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in,out] operation Initialized cipher operation. + * + * \retval #PSA_SUCCESS \emptydescription + */ +psa_status_t mbedtls_psa_cipher_abort(mbedtls_psa_cipher_operation_t *operation); + +/** Encrypt a message using a symmetric cipher. + * + * \note The signature of this function is that of a PSA driver + * cipher_encrypt entry point. This function behaves as a + * cipher_encrypt entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \param[in] iv Buffer containing the IV for encryption. The + * IV has been generated by the core. + * \param[in] iv_length Size of the \p iv in bytes. + * \param[in] input Buffer containing the message to encrypt. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[in,out] output Buffer where the output is to be written. + * \param[in] output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes that make up + * the returned output. Initialized to zero + * by the core. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size \p iv_length is not acceptable for the chosen algorithm, + * or the chosen algorithm does not use an IV. + * The total input size passed to this operation is not valid for + * this particular algorithm. For example, the algorithm is a based + * on block cipher and requires a whole number of blocks, but the + * total input size is not a multiple of the block size. + * \retval #PSA_ERROR_INVALID_PADDING + * This is a decryption operation for an algorithm that includes + * padding, and the ciphertext does not contain valid padding. + */ +psa_status_t mbedtls_psa_cipher_encrypt(const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *iv, + size_t iv_length, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Decrypt a message using a symmetric cipher. + * + * \note The signature of this function is that of a PSA driver + * cipher_decrypt entry point. This function behaves as a + * cipher_decrypt entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg The cipher algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_CIPHER(\p alg) is true). + * \param[in] input Buffer containing the iv and the ciphertext. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[out] output Buffer where the output is to be written. + * \param[in] output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes that make up + * the returned output. Initialized to zero + * by the core. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size of \p iv is not acceptable for the chosen algorithm, + * or the chosen algorithm does not use an IV. + * The total input size passed to this operation is not valid for + * this particular algorithm. For example, the algorithm is a based + * on block cipher and requires a whole number of blocks, but the + * total input size is not a multiple of the block size. + * \retval #PSA_ERROR_INVALID_PADDING + * This is a decryption operation for an algorithm that includes + * padding, and the ciphertext does not contain valid padding. + */ +psa_status_t mbedtls_psa_cipher_decrypt(const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +#endif /* PSA_CRYPTO_CIPHER_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_client.c b/r5dev/thirdparty/mbedtls/psa_crypto_client.c new file mode 100644 index 00000000..c3234275 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_client.c @@ -0,0 +1,79 @@ +/* + * PSA crypto client code + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" +#include "psa/crypto.h" + +#if defined(MBEDTLS_PSA_CRYPTO_CLIENT) + +#include +#include "mbedtls/platform.h" + +void psa_reset_key_attributes(psa_key_attributes_t *attributes) +{ + mbedtls_free(attributes->domain_parameters); + memset(attributes, 0, sizeof(*attributes)); +} + +psa_status_t psa_set_key_domain_parameters(psa_key_attributes_t *attributes, + psa_key_type_t type, + const uint8_t *data, + size_t data_length) +{ + uint8_t *copy = NULL; + + if (data_length != 0) { + copy = mbedtls_calloc(1, data_length); + if (copy == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + memcpy(copy, data, data_length); + } + /* After this point, this function is guaranteed to succeed, so it + * can start modifying `*attributes`. */ + + if (attributes->domain_parameters != NULL) { + mbedtls_free(attributes->domain_parameters); + attributes->domain_parameters = NULL; + attributes->domain_parameters_size = 0; + } + + attributes->domain_parameters = copy; + attributes->domain_parameters_size = data_length; + attributes->core.type = type; + return PSA_SUCCESS; +} + +psa_status_t psa_get_key_domain_parameters( + const psa_key_attributes_t *attributes, + uint8_t *data, size_t data_size, size_t *data_length) +{ + if (attributes->domain_parameters_size > data_size) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + *data_length = attributes->domain_parameters_size; + if (attributes->domain_parameters_size != 0) { + memcpy(data, attributes->domain_parameters, + attributes->domain_parameters_size); + } + return PSA_SUCCESS; +} + +#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_core.h b/r5dev/thirdparty/mbedtls/psa_crypto_core.h new file mode 100644 index 00000000..8bc1b647 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_core.h @@ -0,0 +1,871 @@ +/* + * PSA crypto core internal interfaces + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_CORE_H +#define PSA_CRYPTO_CORE_H + +#include "mbedtls/build_info.h" + +#include "psa/crypto.h" +#include "psa/crypto_se_driver.h" + +/** + * Tell if PSA is ready for this hash. + * + * \note For now, only checks the state of the driver subsystem, + * not the algorithm. Might do more in the future. + * + * \param hash_alg The hash algorithm (ignored for now). + * + * \return 1 if the driver subsytem is ready, 0 otherwise. + */ +int psa_can_do_hash(psa_algorithm_t hash_alg); + +/** Constant-time buffer comparison + * + * \param[in] a Left-hand buffer for comparison. + * \param[in] b Right-hand buffer for comparison. + * \param n Amount of bytes to compare. + * + * \return 0 if the buffer contents are equal, non-zero otherwise + */ +static inline int mbedtls_psa_safer_memcmp( + const uint8_t *a, const uint8_t *b, size_t n) +{ + size_t i; + unsigned char diff = 0; + + for (i = 0; i < n; i++) { + diff |= a[i] ^ b[i]; + } + + return diff; +} + +/** The data structure representing a key slot, containing key material + * and metadata for one key. + */ +typedef struct { + psa_core_key_attributes_t attr; + + /* + * Number of locks on the key slot held by the library. + * + * This counter is incremented by one each time a library function + * retrieves through one of the dedicated internal API a pointer to the + * key slot. + * + * This counter is decremented by one each time a library function stops + * accessing the key slot and states it by calling the + * psa_unlock_key_slot() API. + * + * This counter is used to prevent resetting the key slot while the library + * may access it. For example, such control is needed in the following + * scenarios: + * . In case of key slot starvation, all key slots contain the description + * of a key, and the library asks for the description of a persistent + * key not present in the key slots, the key slots currently accessed by + * the library cannot be reclaimed to free a key slot to load the + * persistent key. + * . In case of a multi-threaded application where one thread asks to close + * or purge or destroy a key while it is in used by the library through + * another thread. + */ + size_t lock_count; + + /* Dynamically allocated key data buffer. + * Format as specified in psa_export_key(). */ + struct key_data { + uint8_t *data; + size_t bytes; + } key; +} psa_key_slot_t; + +/* A mask of key attribute flags used only internally. + * Currently there aren't any. */ +#define PSA_KA_MASK_INTERNAL_ONLY ( \ + 0) + +/** Test whether a key slot is occupied. + * + * A key slot is occupied iff the key type is nonzero. This works because + * no valid key can have 0 as its key type. + * + * \param[in] slot The key slot to test. + * + * \return 1 if the slot is occupied, 0 otherwise. + */ +static inline int psa_is_key_slot_occupied(const psa_key_slot_t *slot) +{ + return slot->attr.type != 0; +} + +/** Test whether a key slot is locked. + * + * A key slot is locked iff its lock counter is strictly greater than 0. + * + * \param[in] slot The key slot to test. + * + * \return 1 if the slot is locked, 0 otherwise. + */ +static inline int psa_is_key_slot_locked(const psa_key_slot_t *slot) +{ + return slot->lock_count > 0; +} + +/** Retrieve flags from psa_key_slot_t::attr::core::flags. + * + * \param[in] slot The key slot to query. + * \param mask The mask of bits to extract. + * + * \return The key attribute flags in the given slot, + * bitwise-anded with \p mask. + */ +static inline uint16_t psa_key_slot_get_flags(const psa_key_slot_t *slot, + uint16_t mask) +{ + return slot->attr.flags & mask; +} + +/** Set flags in psa_key_slot_t::attr::core::flags. + * + * \param[in,out] slot The key slot to modify. + * \param mask The mask of bits to modify. + * \param value The new value of the selected bits. + */ +static inline void psa_key_slot_set_flags(psa_key_slot_t *slot, + uint16_t mask, + uint16_t value) +{ + slot->attr.flags = ((~mask & slot->attr.flags) | + (mask & value)); +} + +/** Turn on flags in psa_key_slot_t::attr::core::flags. + * + * \param[in,out] slot The key slot to modify. + * \param mask The mask of bits to set. + */ +static inline void psa_key_slot_set_bits_in_flags(psa_key_slot_t *slot, + uint16_t mask) +{ + slot->attr.flags |= mask; +} + +/** Turn off flags in psa_key_slot_t::attr::core::flags. + * + * \param[in,out] slot The key slot to modify. + * \param mask The mask of bits to clear. + */ +static inline void psa_key_slot_clear_bits(psa_key_slot_t *slot, + uint16_t mask) +{ + slot->attr.flags &= ~mask; +} + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +/** Get the SE slot number of a key from the key slot storing its description. + * + * \param[in] slot The key slot to query. This must be a key slot storing + * the description of a key of a dynamically registered + * secure element, otherwise the behaviour is undefined. + */ +static inline psa_key_slot_number_t psa_key_slot_get_slot_number( + const psa_key_slot_t *slot) +{ + return *((psa_key_slot_number_t *) (slot->key.data)); +} +#endif + +/** Completely wipe a slot in memory, including its policy. + * + * Persistent storage is not affected. + * + * \param[in,out] slot The key slot to wipe. + * + * \retval #PSA_SUCCESS + * Success. This includes the case of a key slot that was + * already fully wiped. + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot); + +/** Try to allocate a buffer to an empty key slot. + * + * \param[in,out] slot Key slot to attach buffer to. + * \param[in] buffer_length Requested size of the buffer. + * + * \retval #PSA_SUCCESS + * The buffer has been successfully allocated. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * Not enough memory was available for allocation. + * \retval #PSA_ERROR_ALREADY_EXISTS + * Trying to allocate a buffer to a non-empty key slot. + */ +psa_status_t psa_allocate_buffer_to_slot(psa_key_slot_t *slot, + size_t buffer_length); + +/** Wipe key data from a slot. Preserves metadata such as the policy. */ +psa_status_t psa_remove_key_data_from_memory(psa_key_slot_t *slot); + +/** Copy key data (in export format) into an empty key slot. + * + * This function assumes that the slot does not contain + * any key material yet. On failure, the slot content is unchanged. + * + * \param[in,out] slot Key slot to copy the key into. + * \param[in] data Buffer containing the key material. + * \param data_length Size of the key buffer. + * + * \retval #PSA_SUCCESS + * The key has been copied successfully. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * Not enough memory was available for allocation of the + * copy buffer. + * \retval #PSA_ERROR_ALREADY_EXISTS + * There was other key material already present in the slot. + */ +psa_status_t psa_copy_key_material_into_slot(psa_key_slot_t *slot, + const uint8_t *data, + size_t data_length); + +/** Convert an mbed TLS error code to a PSA error code + * + * \note This function is provided solely for the convenience of + * Mbed TLS and may be removed at any time without notice. + * + * \param ret An mbed TLS-thrown error code + * + * \return The corresponding PSA error code + */ +psa_status_t mbedtls_to_psa_error(int ret); + +/** Import a key in binary format. + * + * \note The signature of this function is that of a PSA driver + * import_key entry point. This function behaves as an import_key + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes for the key to import. + * \param[in] data The buffer containing the key data in import + * format. + * \param[in] data_length Size of the \p data buffer in bytes. + * \param[out] key_buffer The buffer to contain the key data in output + * format upon successful return. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. This + * size is greater or equal to \p data_length. + * \param[out] key_buffer_length The length of the data written in \p + * key_buffer in bytes. + * \param[out] bits The key size in number of bits. + * + * \retval #PSA_SUCCESS The key was imported successfully. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key data is not correctly formatted. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t psa_import_key_into_slot( + const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + uint8_t *key_buffer, size_t key_buffer_size, + size_t *key_buffer_length, size_t *bits); + +/** Export a key in binary format + * + * \note The signature of this function is that of a PSA driver export_key + * entry point. This function behaves as an export_key entry point as + * defined in the PSA driver interface specification. + * + * \param[in] attributes The attributes for the key to export. + * \param[in] key_buffer Material or context of the key to export. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[out] data Buffer where the key data is to be written. + * \param[in] data_size Size of the \p data buffer in bytes. + * \param[out] data_length On success, the number of bytes written in + * \p data + * + * \retval #PSA_SUCCESS The key was exported successfully. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t psa_export_key_internal( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length); + +/** Export a public key or the public part of a key pair in binary format. + * + * \note The signature of this function is that of a PSA driver + * export_public_key entry point. This function behaves as an + * export_public_key entry point as defined in the PSA driver interface + * specification. + * + * \param[in] attributes The attributes for the key to export. + * \param[in] key_buffer Material or context of the key to export. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[out] data Buffer where the key data is to be written. + * \param[in] data_size Size of the \p data buffer in bytes. + * \param[out] data_length On success, the number of bytes written in + * \p data + * + * \retval #PSA_SUCCESS The public key was exported successfully. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t psa_export_public_key_internal( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length); + +/** + * \brief Generate a key. + * + * \note The signature of the function is that of a PSA driver generate_key + * entry point. + * + * \param[in] attributes The attributes for the key to generate. + * \param[out] key_buffer Buffer where the key data is to be written. + * \param[in] key_buffer_size Size of \p key_buffer in bytes. + * \param[out] key_buffer_length On success, the number of bytes written in + * \p key_buffer. + * + * \retval #PSA_SUCCESS + * The key was generated successfully. + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED + * Key size in bits or type not supported. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of \p key_buffer is too small. + */ +psa_status_t psa_generate_key_internal(const psa_key_attributes_t *attributes, + uint8_t *key_buffer, + size_t key_buffer_size, + size_t *key_buffer_length); + +/** Sign a message with a private key. For hash-and-sign algorithms, + * this includes the hashing step. + * + * \note The signature of this function is that of a PSA driver + * sign_message entry point. This function behaves as a sign_message + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \note This function will call the driver for psa_sign_hash + * and go through driver dispatch again. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * the type of the key. + * \param[in] input The input message to sign. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[out] signature Buffer where the signature is to be written. + * \param[in] signature_size Size of the \p signature buffer in bytes. + * \param[out] signature_length On success, the number of bytes + * that make up the returned signature value. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of the key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + */ +psa_status_t psa_sign_message_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *input, size_t input_length, + uint8_t *signature, size_t signature_size, size_t *signature_length); + +/** Verify the signature of a message with a public key, using + * a hash-and-sign verification algorithm. + * + * \note The signature of this function is that of a PSA driver + * verify_message entry point. This function behaves as a verify_message + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \note This function will call the driver for psa_verify_hash + * and go through driver dispatch again. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * the type of the key. + * \param[in] input The message whose signature is to be verified. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[in] signature Buffer containing the signature to verify. + * \param[in] signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * The signature is valid. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed + * signature is not a valid signature. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t psa_verify_message_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *input, size_t input_length, + const uint8_t *signature, size_t signature_length); + +/** Sign an already-calculated hash with a private key. + * + * \note The signature of this function is that of a PSA driver + * sign_hash entry point. This function behaves as a sign_hash + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * the type of the key. + * \param[in] hash The hash or message to sign. + * \param[in] hash_length Size of the \p hash buffer in bytes. + * \param[out] signature Buffer where the signature is to be written. + * \param[in] signature_size Size of the \p signature buffer in bytes. + * \param[out] signature_length On success, the number of bytes + * that make up the returned signature value. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of the key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + */ +psa_status_t psa_sign_hash_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length); + +/** + * \brief Verify the signature a hash or short message using a public key. + * + * \note The signature of this function is that of a PSA driver + * verify_hash entry point. This function behaves as a verify_hash + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * the type of the key. + * \param[in] hash The hash or message whose signature is to be + * verified. + * \param[in] hash_length Size of the \p hash buffer in bytes. + * \param[in] signature Buffer containing the signature to verify. + * \param[in] signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * The signature is valid. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed + * signature is not a valid signature. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t psa_verify_hash_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length); + +/** + * \brief Validate the key bit size for unstructured keys. + * + * \note Check that the bit size is acceptable for a given key type for + * unstructured keys. + * + * \param[in] type The key type + * \param[in] bits The number of bits of the key + * + * \retval #PSA_SUCCESS + * The key type and size are valid. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The size in bits of the key is not valid. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The type and/or the size in bits of the key or the combination of + * the two is not supported. + */ +psa_status_t psa_validate_unstructured_key_bit_size(psa_key_type_t type, + size_t bits); + +/** Perform a key agreement and return the raw shared secret, using + built-in raw key agreement functions. + * + * \note The signature of this function is that of a PSA driver + * key_agreement entry point. This function behaves as a key_agreement + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the private key + * context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in + * bytes. + * \param[in] alg A key agreement algorithm that is + * compatible with the type of the key. + * \param[in] peer_key The buffer containing the key context + * of the peer's public key. + * \param[in] peer_key_length Size of the \p peer_key buffer in + * bytes. + * \param[out] shared_secret The buffer to which the shared secret + * is to be written. + * \param[in] shared_secret_size Size of the \p shared_secret buffer in + * bytes. + * \param[out] shared_secret_length On success, the number of bytes that make + * up the returned shared secret. + * \retval #PSA_SUCCESS + * Success. Shared secret successfully calculated. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p alg is not a key agreement algorithm, or + * \p private_key is not compatible with \p alg, + * or \p peer_key is not valid for \p alg or not compatible with + * \p private_key. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p shared_secret_size is too small + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not a supported key agreement algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_BAD_STATE \emptydescription + */ +psa_status_t psa_key_agreement_raw_builtin( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *peer_key, + size_t peer_key_length, + uint8_t *shared_secret, + size_t shared_secret_size, + size_t *shared_secret_length); + +/** + * \brief Set the maximum number of ops allowed to be executed by an + * interruptible function in a single call. + * + * \note The signature of this function is that of a PSA driver + * interruptible_set_max_ops entry point. This function behaves as an + * interruptible_set_max_ops entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in] max_ops The maximum number of ops to be executed in a + * single call, this can be a number from 0 to + * #PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED, where 0 + * is obviously the least amount of work done per + * call. + */ +void mbedtls_psa_interruptible_set_max_ops(uint32_t max_ops); + +/** + * \brief Get the maximum number of ops allowed to be executed by an + * interruptible function in a single call. + * + * \note The signature of this function is that of a PSA driver + * interruptible_get_max_ops entry point. This function behaves as an + * interruptible_get_max_ops entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \return Maximum number of ops allowed to be executed + * by an interruptible function in a single call. + */ +uint32_t mbedtls_psa_interruptible_get_max_ops(void); + +/** + * \brief Get the number of ops that a hash signing operation has taken for the + * previous call. If no call or work has taken place, this will return + * zero. + * + * \note The signature of this function is that of a PSA driver + * sign_hash_get_num_ops entry point. This function behaves as an + * sign_hash_get_num_ops entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param operation The \c + * mbedtls_psa_sign_hash_interruptible_operation_t + * to use. This must be initialized first. + * + * \return Number of ops that were completed + * in the last call to \c + * mbedtls_psa_sign_hash_complete(). + */ +uint32_t mbedtls_psa_sign_hash_get_num_ops( + const mbedtls_psa_sign_hash_interruptible_operation_t *operation); + +/** + * \brief Get the number of ops that a hash verification operation has taken for + * the previous call. If no call or work has taken place, this will + * return zero. + * + * \note The signature of this function is that of a PSA driver + * verify_hash_get_num_ops entry point. This function behaves as an + * verify_hash_get_num_ops entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param operation The \c + * mbedtls_psa_verify_hash_interruptible_operation_t + * to use. This must be initialized first. + * + * \return Number of ops that were completed + * in the last call to \c + * mbedtls_psa_verify_hash_complete(). + */ +uint32_t mbedtls_psa_verify_hash_get_num_ops( + const mbedtls_psa_verify_hash_interruptible_operation_t *operation); + +/** + * \brief Start signing a hash or short message with a private key, in an + * interruptible manner. + * + * \note The signature of this function is that of a PSA driver + * sign_hash_start entry point. This function behaves as a + * sign_hash_start entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * \param[in] operation The \c + * mbedtls_psa_sign_hash_interruptible_operation_t + * to use. This must be initialized first. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * the type of the key. + * \param[in] hash The hash or message to sign. + * \param hash_length Size of the \p hash buffer in bytes. + * + * \retval #PSA_SUCCESS + * The operation started successfully - call \c psa_sign_hash_complete() + * with the same context to complete the operation + * \retval #PSA_ERROR_INVALID_ARGUMENT + * An unsupported, incorrectly formatted or incorrect type of key was + * used. + * \retval #PSA_ERROR_NOT_SUPPORTED Either no internal interruptible operations + * are currently supported, or the key type is currently unsupported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * There was insufficient memory to load the key representation. + */ +psa_status_t mbedtls_psa_sign_hash_start( + mbedtls_psa_sign_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length); + +/** + * \brief Continue and eventually complete the action of signing a hash or + * short message with a private key, in an interruptible manner. + * + * \note The signature of this function is that of a PSA driver + * sign_hash_complete entry point. This function behaves as a + * sign_hash_complete entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * \param[in] operation The \c + * mbedtls_psa_sign_hash_interruptible_operation_t + * to use. This must be initialized first. + * + * \param[out] signature Buffer where the signature is to be written. + * \param signature_size Size of the \p signature buffer in bytes. This + * must be appropriate for the selected + * algorithm and key. + * \param[out] signature_length On success, the number of bytes that make up + * the returned signature value. + * + * \retval #PSA_SUCCESS + * Operation completed successfully + * + * \retval #PSA_OPERATION_INCOMPLETE + * Operation was interrupted due to the setting of \c + * psa_interruptible_set_max_ops(), there is still work to be done, + * please call this function again with the same operation object. + * + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + */ +psa_status_t mbedtls_psa_sign_hash_complete( + mbedtls_psa_sign_hash_interruptible_operation_t *operation, + uint8_t *signature, size_t signature_size, + size_t *signature_length); + +/** + * \brief Abort a sign hash operation. + * + * \note The signature of this function is that of a PSA driver sign_hash_abort + * entry point. This function behaves as a sign_hash_abort entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * \param[in] operation The \c + * mbedtls_psa_sign_hash_interruptible_operation_t + * to abort. + * + * \retval #PSA_SUCCESS + * The operation was aborted successfully. + */ +psa_status_t mbedtls_psa_sign_hash_abort( + mbedtls_psa_sign_hash_interruptible_operation_t *operation); + +/** + * \brief Start reading and verifying a hash or short message, in an + * interruptible manner. + * + * \note The signature of this function is that of a PSA driver + * verify_hash_start entry point. This function behaves as a + * verify_hash_start entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * \param[in] operation The \c + * mbedtls_psa_verify_hash_interruptible_operation_t + * to use. This must be initialized first. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * the type of the key. + * \param[in] hash The hash whose signature is to be verified. + * \param hash_length Size of the \p hash buffer in bytes. + * \param[in] signature Buffer containing the signature to verify. + * \param signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * The operation started successfully - call \c psa_sign_hash_complete() + * with the same context to complete the operation + * \retval #PSA_ERROR_INVALID_ARGUMENT + * An unsupported or incorrect type of key was used. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Either no internal interruptible operations are currently supported, + * or the key type is currently unsupported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * There was insufficient memory either to load the key representation, + * or to prepare the operation. + */ +psa_status_t mbedtls_psa_verify_hash_start( + mbedtls_psa_verify_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length); + +/** + * \brief Continue and eventually complete the action of signing a hash or + * short message with a private key, in an interruptible manner. + * + * \note The signature of this function is that of a PSA driver + * sign_hash_complete entry point. This function behaves as a + * sign_hash_complete entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * \param[in] operation The \c + * mbedtls_psa_sign_hash_interruptible_operation_t + * to use. This must be initialized first. + * + * \retval #PSA_SUCCESS + * Operation completed successfully, and the passed signature is valid. + * + * \retval #PSA_OPERATION_INCOMPLETE + * Operation was interrupted due to the setting of \c + * psa_interruptible_set_max_ops(), there is still work to be done, + * please call this function again with the same operation object. + * + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed + * signature is not a valid signature. + * + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_verify_hash_complete( + mbedtls_psa_verify_hash_interruptible_operation_t *operation); + +/** + * \brief Abort a verify signed hash operation. + * + * \note The signature of this function is that of a PSA driver + * verify_hash_abort entry point. This function behaves as a + * verify_hash_abort entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * \param[in] operation The \c + * mbedtls_psa_verify_hash_interruptible_operation_t + * to abort. + * + * \retval #PSA_SUCCESS + * The operation was aborted successfully. + */ +psa_status_t mbedtls_psa_verify_hash_abort( + mbedtls_psa_verify_hash_interruptible_operation_t *operation); + +#endif /* PSA_CRYPTO_CORE_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_driver_wrappers.c b/r5dev/thirdparty/mbedtls/psa_crypto_driver_wrappers.c new file mode 100644 index 00000000..9e946e36 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_driver_wrappers.c @@ -0,0 +1,2987 @@ +/* + * Functions to delegate cryptographic operations to an available + * and appropriate accelerator. + * Warning: This file is now auto-generated. + */ +/* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* BEGIN-common headers */ +#include "common.h" +#include "psa_crypto_aead.h" +#include "psa_crypto_cipher.h" +#include "psa_crypto_core.h" +#include "psa_crypto_driver_wrappers.h" +#include "psa_crypto_hash.h" +#include "psa_crypto_mac.h" +#include "psa_crypto_pake.h" +#include "psa_crypto_rsa.h" + +#include "mbedtls/platform.h" +/* END-common headers */ + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +/* BEGIN-driver headers */ +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) +/* Headers for mbedtls_test opaque driver */ +#if defined(PSA_CRYPTO_DRIVER_TEST) +#include "test/drivers/test_driver.h" + +#endif +/* Headers for mbedtls_test transparent driver */ +#if defined(PSA_CRYPTO_DRIVER_TEST) +#include "test/drivers/test_driver.h" + +#endif + +#endif /* MBEDTLS_PSA_CRYPTO_DRIVERS */ +/* END-driver headers */ + +/* Auto-generated values depending on which drivers are registered. + * ID 0 is reserved for unallocated operations. + * ID 1 is reserved for the Mbed TLS software driver. */ +/* BEGIN-driver id definition */ +#define PSA_CRYPTO_MBED_TLS_DRIVER_ID (1) +#define MBEDTLS_TEST_OPAQUE_DRIVER_ID (2) +#define MBEDTLS_TEST_TRANSPARENT_DRIVER_ID (3) + +/* END-driver id */ + +/* BEGIN-Common Macro definitions */ + +/* END-Common Macro definitions */ + +/* Support the 'old' SE interface when asked to */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +/* PSA_CRYPTO_DRIVER_PRESENT is defined when either a new-style or old-style + * SE driver is present, to avoid unused argument errors at compile time. */ +#ifndef PSA_CRYPTO_DRIVER_PRESENT +#define PSA_CRYPTO_DRIVER_PRESENT +#endif +#include "psa_crypto_se.h" +#endif + +psa_status_t psa_driver_wrapper_init( void ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + status = psa_init_all_se_drivers( ); + if( status != PSA_SUCCESS ) + return( status ); +#endif + +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_init( ); + if( status != PSA_SUCCESS ) + return( status ); + + status = mbedtls_test_opaque_init( ); + if( status != PSA_SUCCESS ) + return( status ); +#endif + + (void) status; + return( PSA_SUCCESS ); +} + +void psa_driver_wrapper_free( void ) +{ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + /* Unregister all secure element drivers, so that we restart from + * a pristine state. */ + psa_unregister_all_se_drivers( ); +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +#if defined(PSA_CRYPTO_DRIVER_TEST) + mbedtls_test_transparent_free( ); + mbedtls_test_opaque_free( ); +#endif +} + +/* Start delegation functions */ +psa_status_t psa_driver_wrapper_sign_message( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_signature_sign_message( + attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + signature, + signature_size, + signature_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + break; + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + status = mbedtls_test_opaque_signature_sign_message( + attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + signature, + signature_size, + signature_length ); + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); + break; +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + break; + } + + return( psa_sign_message_builtin( attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + signature, + signature_size, + signature_length ) ); +} + +psa_status_t psa_driver_wrapper_verify_message( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *signature, + size_t signature_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_signature_verify_message( + attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + signature, + signature_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + break; + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_signature_verify_message( + attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + signature, + signature_length ) ); + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); + break; +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + break; + } + + return( psa_verify_message_builtin( attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + signature, + signature_length ) ); +} + +psa_status_t psa_driver_wrapper_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length ) +{ + /* Try dynamically-registered SE interface first */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + + if( psa_get_se_driver( attributes->core.lifetime, &drv, &drv_context ) ) + { + if( drv->asymmetric == NULL || + drv->asymmetric->p_sign == NULL ) + { + /* Key is defined in SE, but we have no way to exercise it */ + return( PSA_ERROR_NOT_SUPPORTED ); + } + return( drv->asymmetric->p_sign( + drv_context, *( (psa_key_slot_number_t *)key_buffer ), + alg, hash, hash_length, + signature, signature_size, signature_length ) ); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_signature_sign_hash( attributes, + key_buffer, + key_buffer_size, + alg, + hash, + hash_length, + signature, + signature_size, + signature_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + /* Fell through, meaning no accelerator supports this operation */ + return( psa_sign_hash_builtin( attributes, + key_buffer, + key_buffer_size, + alg, + hash, + hash_length, + signature, + signature_size, + signature_length ) ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_signature_sign_hash( attributes, + key_buffer, + key_buffer_size, + alg, + hash, + hash_length, + signature, + signature_size, + signature_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_verify_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length ) +{ + /* Try dynamically-registered SE interface first */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + + if( psa_get_se_driver( attributes->core.lifetime, &drv, &drv_context ) ) + { + if( drv->asymmetric == NULL || + drv->asymmetric->p_verify == NULL ) + { + /* Key is defined in SE, but we have no way to exercise it */ + return( PSA_ERROR_NOT_SUPPORTED ); + } + return( drv->asymmetric->p_verify( + drv_context, *( (psa_key_slot_number_t *)key_buffer ), + alg, hash, hash_length, + signature, signature_length ) ); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_signature_verify_hash( + attributes, + key_buffer, + key_buffer_size, + alg, + hash, + hash_length, + signature, + signature_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + return( psa_verify_hash_builtin( attributes, + key_buffer, + key_buffer_size, + alg, + hash, + hash_length, + signature, + signature_length ) ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_signature_verify_hash( attributes, + key_buffer, + key_buffer_size, + alg, + hash, + hash_length, + signature, + signature_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +uint32_t psa_driver_wrapper_sign_hash_get_num_ops( + psa_sign_hash_interruptible_operation_t *operation ) +{ + switch( operation->id ) + { + /* If uninitialised, return 0, as no work can have been done. */ + case 0: + return 0; + + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return(mbedtls_psa_sign_hash_get_num_ops(&operation->ctx.mbedtls_ctx)); + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + /* Add test driver tests here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +uint32_t psa_driver_wrapper_verify_hash_get_num_ops( + psa_verify_hash_interruptible_operation_t *operation ) +{ + switch( operation->id ) + { + /* If uninitialised, return 0, as no work can have been done. */ + case 0: + return 0; + + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return (mbedtls_psa_verify_hash_get_num_ops(&operation->ctx.mbedtls_ctx)); + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + /* Add test driver tests here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + } + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_sign_hash_start( + psa_sign_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( + attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + + /* Add test driver tests here */ + + /* Declared with fallback == true */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Fell through, meaning no accelerator supports this operation */ + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + return( mbedtls_psa_sign_hash_start( &operation->ctx.mbedtls_ctx, + attributes, + key_buffer, key_buffer_size, + alg, hash, hash_length ) ); + break; + + /* Add cases for opaque driver here */ + + default: + /* Key is declared with a lifetime not known to us */ + ( void ) status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } + + ( void ) operation; + ( void ) key_buffer; + ( void ) key_buffer_size; + ( void ) alg; + ( void ) hash; + ( void ) hash_length; + + return( status ); +} + +psa_status_t psa_driver_wrapper_sign_hash_complete( + psa_sign_hash_interruptible_operation_t *operation, + uint8_t *signature, size_t signature_size, + size_t *signature_length ) +{ + switch( operation->id ) + { + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_sign_hash_complete( &operation->ctx.mbedtls_ctx, + signature, signature_size, + signature_length ) ); + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + /* Add test driver tests here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + ( void ) signature; + ( void ) signature_size; + ( void ) signature_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_sign_hash_abort( + psa_sign_hash_interruptible_operation_t *operation ) +{ + switch( operation->id ) + { + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_sign_hash_abort( &operation->ctx.mbedtls_ctx ) ); + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + /* Add test driver tests here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_verify_hash_start( + psa_verify_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length ) +{ + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( + attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + + /* Add test driver tests here */ + + /* Declared with fallback == true */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Fell through, meaning no accelerator supports this operation */ + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + return( mbedtls_psa_verify_hash_start( &operation->ctx.mbedtls_ctx, + attributes, + key_buffer, key_buffer_size, + alg, hash, hash_length, + signature, signature_length + ) ); + break; + + /* Add cases for opaque driver here */ + + default: + /* Key is declared with a lifetime not known to us */ + ( void ) status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } + + ( void ) operation; + ( void ) key_buffer; + ( void ) key_buffer_size; + ( void ) alg; + ( void ) hash; + ( void ) hash_length; + ( void ) signature; + ( void ) signature_length; + + return( status ); +} + +psa_status_t psa_driver_wrapper_verify_hash_complete( + psa_verify_hash_interruptible_operation_t *operation ) +{ + switch( operation->id ) + { + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_verify_hash_complete( + &operation->ctx.mbedtls_ctx + ) ); + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + /* Add test driver tests here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_verify_hash_abort( + psa_verify_hash_interruptible_operation_t *operation ) +{ + switch( operation->id ) + { + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_verify_hash_abort( &operation->ctx.mbedtls_ctx + ) ); + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + /* Add test driver tests here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +/** Calculate the key buffer size required to store the key material of a key + * associated with an opaque driver from input key data. + * + * \param[in] attributes The key attributes + * \param[in] data The input key data. + * \param[in] data_length The input data length. + * \param[out] key_buffer_size Minimum buffer size to contain the key material. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + */ +psa_status_t psa_driver_wrapper_get_key_buffer_size_from_key_data( + const psa_key_attributes_t *attributes, + const uint8_t *data, + size_t data_length, + size_t *key_buffer_size ) +{ + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + psa_key_type_t key_type = attributes->core.type; + + *key_buffer_size = 0; + switch( location ) + { +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + *key_buffer_size = mbedtls_test_opaque_size_function( key_type, + PSA_BYTES_TO_BITS( data_length ) ); + return( ( *key_buffer_size != 0 ) ? + PSA_SUCCESS : PSA_ERROR_NOT_SUPPORTED ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ + + default: + (void)key_type; + (void)data; + (void)data_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +/** Get the key buffer size required to store the key material of a key + * associated with an opaque driver. + * + * \param[in] attributes The key attributes. + * \param[out] key_buffer_size Minimum buffer size to contain the key material + * + * \retval #PSA_SUCCESS + * The minimum size for a buffer to contain the key material has been + * returned successfully. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The type and/or the size in bits of the key or the combination of + * the two is not supported. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key is declared with a lifetime not known to us. + */ +psa_status_t psa_driver_wrapper_get_key_buffer_size( + const psa_key_attributes_t *attributes, + size_t *key_buffer_size ) +{ + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + psa_key_type_t key_type = attributes->core.type; + size_t key_bits = attributes->core.bits; + + *key_buffer_size = 0; + switch( location ) + { +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: +#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) + /* Emulate property 'builtin_key_size' */ + if( psa_key_id_is_builtin( + MBEDTLS_SVC_KEY_ID_GET_KEY_ID( + psa_get_key_id( attributes ) ) ) ) + { + *key_buffer_size = sizeof( psa_drv_slot_number_t ); + return( PSA_SUCCESS ); + } +#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ + *key_buffer_size = mbedtls_test_opaque_size_function( key_type, + key_bits ); + return( ( *key_buffer_size != 0 ) ? + PSA_SUCCESS : PSA_ERROR_NOT_SUPPORTED ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ + + default: + (void)key_type; + (void)key_bits; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_generate_key( + const psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION(attributes->core.lifetime); + + /* Try dynamically-registered SE interface first */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + + if( psa_get_se_driver( attributes->core.lifetime, &drv, &drv_context ) ) + { + size_t pubkey_length = 0; /* We don't support this feature yet */ + if( drv->key_management == NULL || + drv->key_management->p_generate == NULL ) + { + /* Key is defined as being in SE, but we have no way to generate it */ + return( PSA_ERROR_NOT_SUPPORTED ); + } + return( drv->key_management->p_generate( + drv_context, + *( (psa_key_slot_number_t *)key_buffer ), + attributes, NULL, 0, &pubkey_length ) ); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) + /* Transparent drivers are limited to generating asymmetric keys */ + if( PSA_KEY_TYPE_IS_ASYMMETRIC( attributes->core.type ) ) + { + /* Cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_generate_key( + attributes, key_buffer, key_buffer_size, + key_buffer_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + break; +#endif /* PSA_CRYPTO_DRIVER_TEST */ + } +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Software fallback */ + status = psa_generate_key_internal( + attributes, key_buffer, key_buffer_size, key_buffer_length ); + break; + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + status = mbedtls_test_opaque_generate_key( + attributes, key_buffer, key_buffer_size, key_buffer_length ); + break; +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + default: + /* Key is declared with a lifetime not known to us */ + status = PSA_ERROR_INVALID_ARGUMENT; + break; + } + + return( status ); +} + +psa_status_t psa_driver_wrapper_import_key( + const psa_key_attributes_t *attributes, + const uint8_t *data, + size_t data_length, + uint8_t *key_buffer, + size_t key_buffer_size, + size_t *key_buffer_length, + size_t *bits ) +{ + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( + psa_get_key_lifetime( attributes ) ); + + /* Try dynamically-registered SE interface first */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + + if( psa_get_se_driver( attributes->core.lifetime, &drv, &drv_context ) ) + { + if( drv->key_management == NULL || + drv->key_management->p_import == NULL ) + return( PSA_ERROR_NOT_SUPPORTED ); + + /* The driver should set the number of key bits, however in + * case it doesn't, we initialize bits to an invalid value. */ + *bits = PSA_MAX_KEY_BITS + 1; + status = drv->key_management->p_import( + drv_context, + *( (psa_key_slot_number_t *)key_buffer ), + attributes, data, data_length, bits ); + + if( status != PSA_SUCCESS ) + return( status ); + + if( (*bits) > PSA_MAX_KEY_BITS ) + return( PSA_ERROR_NOT_SUPPORTED ); + + return( PSA_SUCCESS ); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) + +#if (defined(PSA_CRYPTO_DRIVER_TEST) ) + status = mbedtls_test_transparent_import_key + (attributes, + data, + data_length, + key_buffer, + key_buffer_size, + key_buffer_length, + bits + ); + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif + + +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Fell through, meaning no accelerator supports this operation */ + return( psa_import_key_into_slot( attributes, + data, data_length, + key_buffer, key_buffer_size, + key_buffer_length, bits ) ); + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) + +#if (defined(PSA_CRYPTO_DRIVER_TEST) ) + case 0x7fffff: + return( mbedtls_test_opaque_import_key + (attributes, + data, + data_length, + key_buffer, + key_buffer_size, + key_buffer_length, + bits + )); +#endif + + +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } + +} + +psa_status_t psa_driver_wrapper_export_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length ) + +{ + + psa_status_t status = PSA_ERROR_INVALID_ARGUMENT; + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( + psa_get_key_lifetime( attributes ) ); + + /* Try dynamically-registered SE interface first */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + + if( psa_get_se_driver( attributes->core.lifetime, &drv, &drv_context ) ) + { + if( ( drv->key_management == NULL ) || + ( drv->key_management->p_export == NULL ) ) + { + return( PSA_ERROR_NOT_SUPPORTED ); + } + + return( drv->key_management->p_export( + drv_context, + *( (psa_key_slot_number_t *)key_buffer ), + data, data_size, data_length ) ); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + return( psa_export_key_internal( attributes, + key_buffer, + key_buffer_size, + data, + data_size, + data_length ) ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) + +#if (defined(PSA_CRYPTO_DRIVER_TEST) ) + case 0x7fffff: + return( mbedtls_test_opaque_export_key + (attributes, + key_buffer, + key_buffer_size, + data, + data_size, + data_length + )); +#endif + + +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + return( status ); + } + +} + +psa_status_t psa_driver_wrapper_export_public_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length ) + +{ + + psa_status_t status = PSA_ERROR_INVALID_ARGUMENT; + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( + psa_get_key_lifetime( attributes ) ); + + /* Try dynamically-registered SE interface first */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + + if( psa_get_se_driver( attributes->core.lifetime, &drv, &drv_context ) ) + { + if( ( drv->key_management == NULL ) || + ( drv->key_management->p_export_public == NULL ) ) + { + return( PSA_ERROR_NOT_SUPPORTED ); + } + + return( drv->key_management->p_export_public( + drv_context, + *( (psa_key_slot_number_t *)key_buffer ), + data, data_size, data_length ) ); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) + +#if (defined(PSA_CRYPTO_DRIVER_TEST) ) + status = mbedtls_test_transparent_export_public_key + (attributes, + key_buffer, + key_buffer_size, + data, + data_size, + data_length + ); + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif + + +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + /* Fell through, meaning no accelerator supports this operation */ + return( psa_export_public_key_internal( attributes, + key_buffer, + key_buffer_size, + data, + data_size, + data_length ) ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) + +#if (defined(PSA_CRYPTO_DRIVER_TEST) ) + case 0x7fffff: + return( mbedtls_test_opaque_export_public_key + (attributes, + key_buffer, + key_buffer_size, + data, + data_size, + data_length + )); +#endif + + +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + return( status ); + } + +} + +psa_status_t psa_driver_wrapper_get_builtin_key( + psa_drv_slot_number_t slot_number, + psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length ) +{ + + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + switch( location ) + { +#if defined(PSA_CRYPTO_DRIVER_TEST) + +#if (defined(PSA_CRYPTO_DRIVER_TEST) ) + case 0x7fffff: + return( mbedtls_test_opaque_get_builtin_key + (slot_number, + attributes, + key_buffer, + key_buffer_size, + key_buffer_length + )); +#endif + + +#endif /* PSA_CRYPTO_DRIVER_TEST */ + default: + (void) slot_number; + (void) key_buffer; + (void) key_buffer_size; + (void) key_buffer_length; + return( PSA_ERROR_DOES_NOT_EXIST ); + } + +} + +psa_status_t psa_driver_wrapper_copy_key( + psa_key_attributes_t *attributes, + const uint8_t *source_key, size_t source_key_length, + uint8_t *target_key_buffer, size_t target_key_buffer_size, + size_t *target_key_buffer_length ) +{ + + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + + if( psa_get_se_driver( attributes->core.lifetime, &drv, &drv_context ) ) + { + /* Copying to a secure element is not implemented yet. */ + return( PSA_ERROR_NOT_SUPPORTED ); + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + switch( location ) + { +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) + +#if (defined(PSA_CRYPTO_DRIVER_TEST) ) + case 0x7fffff: + return( mbedtls_test_opaque_copy_key + (attributes, + source_key, + source_key_length, + target_key_buffer, + target_key_buffer_size, + target_key_buffer_length + )); +#endif + + +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void)source_key; + (void)source_key_length; + (void)target_key_buffer; + (void)target_key_buffer_size; + (void)target_key_buffer_length; + status = PSA_ERROR_INVALID_ARGUMENT; + } + return( status ); + +} + +/* + * Cipher functions + */ +psa_status_t psa_driver_wrapper_cipher_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *iv, + size_t iv_length, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_cipher_encrypt( attributes, + key_buffer, + key_buffer_size, + alg, + iv, + iv_length, + input, + input_length, + output, + output_size, + output_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + return( mbedtls_psa_cipher_encrypt( attributes, + key_buffer, + key_buffer_size, + alg, + iv, + iv_length, + input, + input_length, + output, + output_size, + output_length ) ); +#else + return( PSA_ERROR_NOT_SUPPORTED ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_cipher_encrypt( attributes, + key_buffer, + key_buffer_size, + alg, + iv, + iv_length, + input, + input_length, + output, + output_size, + output_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + (void)key_buffer; + (void)key_buffer_size; + (void)alg; + (void)iv; + (void)iv_length; + (void)input; + (void)input_length; + (void)output; + (void)output_size; + (void)output_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_cipher_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_cipher_decrypt( attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + output, + output_size, + output_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + return( mbedtls_psa_cipher_decrypt( attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + output, + output_size, + output_length ) ); +#else + return( PSA_ERROR_NOT_SUPPORTED ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_cipher_decrypt( attributes, + key_buffer, + key_buffer_size, + alg, + input, + input_length, + output, + output_size, + output_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + (void)key_buffer; + (void)key_buffer_size; + (void)alg; + (void)input; + (void)input_length; + (void)output; + (void)output_size; + (void)output_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_cipher_encrypt_setup( + psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_cipher_encrypt_setup( + &operation->ctx.transparent_test_driver_ctx, + attributes, + key_buffer, + key_buffer_size, + alg ); + /* Declared with fallback == true */ + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + /* Fell through, meaning no accelerator supports this operation */ + status = mbedtls_psa_cipher_encrypt_setup( &operation->ctx.mbedtls_ctx, + attributes, + key_buffer, + key_buffer_size, + alg ); + if( status == PSA_SUCCESS ) + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + return( PSA_ERROR_NOT_SUPPORTED ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + status = mbedtls_test_opaque_cipher_encrypt_setup( + &operation->ctx.opaque_test_driver_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_OPAQUE_DRIVER_ID; + + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + (void)operation; + (void)key_buffer; + (void)key_buffer_size; + (void)alg; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_cipher_decrypt_setup( + psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg ) +{ + psa_status_t status = PSA_ERROR_INVALID_ARGUMENT; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_cipher_decrypt_setup( + &operation->ctx.transparent_test_driver_ctx, + attributes, + key_buffer, + key_buffer_size, + alg ); + /* Declared with fallback == true */ + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + /* Fell through, meaning no accelerator supports this operation */ + status = mbedtls_psa_cipher_decrypt_setup( &operation->ctx.mbedtls_ctx, + attributes, + key_buffer, + key_buffer_size, + alg ); + if( status == PSA_SUCCESS ) + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + + return( status ); +#else /* MBEDTLS_PSA_BUILTIN_CIPHER */ + return( PSA_ERROR_NOT_SUPPORTED ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + status = mbedtls_test_opaque_cipher_decrypt_setup( + &operation->ctx.opaque_test_driver_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_OPAQUE_DRIVER_ID; + + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + (void)operation; + (void)key_buffer; + (void)key_buffer_size; + (void)alg; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_cipher_set_iv( + psa_cipher_operation_t *operation, + const uint8_t *iv, + size_t iv_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_cipher_set_iv( &operation->ctx.mbedtls_ctx, + iv, + iv_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_cipher_set_iv( + &operation->ctx.transparent_test_driver_ctx, + iv, iv_length ) ); + + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + return( mbedtls_test_opaque_cipher_set_iv( + &operation->ctx.opaque_test_driver_ctx, + iv, iv_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)iv; + (void)iv_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_cipher_update( + psa_cipher_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_cipher_update( &operation->ctx.mbedtls_ctx, + input, + input_length, + output, + output_size, + output_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_cipher_update( + &operation->ctx.transparent_test_driver_ctx, + input, input_length, + output, output_size, output_length ) ); + + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + return( mbedtls_test_opaque_cipher_update( + &operation->ctx.opaque_test_driver_ctx, + input, input_length, + output, output_size, output_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)input; + (void)input_length; + (void)output; + (void)output_size; + (void)output_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_cipher_finish( + psa_cipher_operation_t *operation, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_cipher_finish( &operation->ctx.mbedtls_ctx, + output, + output_size, + output_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_cipher_finish( + &operation->ctx.transparent_test_driver_ctx, + output, output_size, output_length ) ); + + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + return( mbedtls_test_opaque_cipher_finish( + &operation->ctx.opaque_test_driver_ctx, + output, output_size, output_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)output; + (void)output_size; + (void)output_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_cipher_abort( + psa_cipher_operation_t *operation ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_CIPHER) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_cipher_abort( &operation->ctx.mbedtls_ctx ) ); +#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + status = mbedtls_test_transparent_cipher_abort( + &operation->ctx.transparent_test_driver_ctx ); + mbedtls_platform_zeroize( + &operation->ctx.transparent_test_driver_ctx, + sizeof( operation->ctx.transparent_test_driver_ctx ) ); + return( status ); + + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + status = mbedtls_test_opaque_cipher_abort( + &operation->ctx.opaque_test_driver_ctx ); + mbedtls_platform_zeroize( + &operation->ctx.opaque_test_driver_ctx, + sizeof( operation->ctx.opaque_test_driver_ctx ) ); + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +/* + * Hashing functions + */ +psa_status_t psa_driver_wrapper_hash_compute( + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *hash, + size_t hash_size, + size_t *hash_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* Try accelerators first */ +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_hash_compute( + alg, input, input_length, hash, hash_size, hash_length ); + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif + + /* If software fallback is compiled in, try fallback */ +#if defined(MBEDTLS_PSA_BUILTIN_HASH) + status = mbedtls_psa_hash_compute( alg, input, input_length, + hash, hash_size, hash_length ); + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif + (void) status; + (void) alg; + (void) input; + (void) input_length; + (void) hash; + (void) hash_size; + (void) hash_length; + + return( PSA_ERROR_NOT_SUPPORTED ); +} + +psa_status_t psa_driver_wrapper_hash_setup( + psa_hash_operation_t *operation, + psa_algorithm_t alg ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* Try setup on accelerators first */ +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_hash_setup( + &operation->ctx.test_driver_ctx, alg ); + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif + + /* If software fallback is compiled in, try fallback */ +#if defined(MBEDTLS_PSA_BUILTIN_HASH) + status = mbedtls_psa_hash_setup( &operation->ctx.mbedtls_ctx, alg ); + if( status == PSA_SUCCESS ) + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif + /* Nothing left to try if we fall through here */ + (void) status; + (void) operation; + (void) alg; + return( PSA_ERROR_NOT_SUPPORTED ); +} + +psa_status_t psa_driver_wrapper_hash_clone( + const psa_hash_operation_t *source_operation, + psa_hash_operation_t *target_operation ) +{ + switch( source_operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_HASH) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + target_operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + return( mbedtls_psa_hash_clone( &source_operation->ctx.mbedtls_ctx, + &target_operation->ctx.mbedtls_ctx ) ); +#endif +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + target_operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + return( mbedtls_test_transparent_hash_clone( + &source_operation->ctx.test_driver_ctx, + &target_operation->ctx.test_driver_ctx ) ); +#endif + default: + (void) target_operation; + return( PSA_ERROR_BAD_STATE ); + } +} + +psa_status_t psa_driver_wrapper_hash_update( + psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_HASH) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_hash_update( &operation->ctx.mbedtls_ctx, + input, input_length ) ); +#endif +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_hash_update( + &operation->ctx.test_driver_ctx, + input, input_length ) ); +#endif + default: + (void) input; + (void) input_length; + return( PSA_ERROR_BAD_STATE ); + } +} + +psa_status_t psa_driver_wrapper_hash_finish( + psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_HASH) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_hash_finish( &operation->ctx.mbedtls_ctx, + hash, hash_size, hash_length ) ); +#endif +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_hash_finish( + &operation->ctx.test_driver_ctx, + hash, hash_size, hash_length ) ); +#endif + default: + (void) hash; + (void) hash_size; + (void) hash_length; + return( PSA_ERROR_BAD_STATE ); + } +} + +psa_status_t psa_driver_wrapper_hash_abort( + psa_hash_operation_t *operation ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_HASH) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_hash_abort( &operation->ctx.mbedtls_ctx ) ); +#endif +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_hash_abort( + &operation->ctx.test_driver_ctx ) ); +#endif + default: + return( PSA_ERROR_BAD_STATE ); + } +} + +psa_status_t psa_driver_wrapper_aead_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *plaintext, size_t plaintext_length, + uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_aead_encrypt( + attributes, key_buffer, key_buffer_size, + alg, + nonce, nonce_length, + additional_data, additional_data_length, + plaintext, plaintext_length, + ciphertext, ciphertext_size, ciphertext_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Fell through, meaning no accelerator supports this operation */ + return( mbedtls_psa_aead_encrypt( + attributes, key_buffer, key_buffer_size, + alg, + nonce, nonce_length, + additional_data, additional_data_length, + plaintext, plaintext_length, + ciphertext, ciphertext_size, ciphertext_length ) ); + + /* Add cases for opaque driver here */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_aead_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *ciphertext, size_t ciphertext_length, + uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_aead_decrypt( + attributes, key_buffer, key_buffer_size, + alg, + nonce, nonce_length, + additional_data, additional_data_length, + ciphertext, ciphertext_length, + plaintext, plaintext_size, plaintext_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Fell through, meaning no accelerator supports this operation */ + return( mbedtls_psa_aead_decrypt( + attributes, key_buffer, key_buffer_size, + alg, + nonce, nonce_length, + additional_data, additional_data_length, + ciphertext, ciphertext_length, + plaintext, plaintext_size, plaintext_length ) ); + + /* Add cases for opaque driver here */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_aead_encrypt_setup( + psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + status = mbedtls_test_transparent_aead_encrypt_setup( + &operation->ctx.transparent_test_driver_ctx, + attributes, key_buffer, key_buffer_size, + alg ); + + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Fell through, meaning no accelerator supports this operation */ + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + status = mbedtls_psa_aead_encrypt_setup( + &operation->ctx.mbedtls_ctx, attributes, + key_buffer, key_buffer_size, + alg ); + + return( status ); + + /* Add cases for opaque driver here */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_aead_decrypt_setup( + psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + status = mbedtls_test_transparent_aead_decrypt_setup( + &operation->ctx.transparent_test_driver_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Fell through, meaning no accelerator supports this operation */ + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + status = mbedtls_psa_aead_decrypt_setup( + &operation->ctx.mbedtls_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + + return( status ); + + /* Add cases for opaque driver here */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_aead_set_nonce( + psa_aead_operation_t *operation, + const uint8_t *nonce, + size_t nonce_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_AEAD) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_aead_set_nonce( &operation->ctx.mbedtls_ctx, + nonce, + nonce_length ) ); + +#endif /* MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_aead_set_nonce( + &operation->ctx.transparent_test_driver_ctx, + nonce, nonce_length ) ); + + /* Add cases for opaque driver here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)nonce; + (void)nonce_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_aead_set_lengths( + psa_aead_operation_t *operation, + size_t ad_length, + size_t plaintext_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_AEAD) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_aead_set_lengths( &operation->ctx.mbedtls_ctx, + ad_length, + plaintext_length ) ); + +#endif /* MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_aead_set_lengths( + &operation->ctx.transparent_test_driver_ctx, + ad_length, plaintext_length ) ); + + /* Add cases for opaque driver here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)ad_length; + (void)plaintext_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_aead_update_ad( + psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_AEAD) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_aead_update_ad( &operation->ctx.mbedtls_ctx, + input, + input_length ) ); + +#endif /* MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_aead_update_ad( + &operation->ctx.transparent_test_driver_ctx, + input, input_length ) ); + + /* Add cases for opaque driver here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)input; + (void)input_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_aead_update( + psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_AEAD) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_aead_update( &operation->ctx.mbedtls_ctx, + input, input_length, + output, output_size, + output_length ) ); + +#endif /* MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_aead_update( + &operation->ctx.transparent_test_driver_ctx, + input, input_length, output, output_size, + output_length ) ); + + /* Add cases for opaque driver here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)input; + (void)input_length; + (void)output; + (void)output_size; + (void)output_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_aead_finish( + psa_aead_operation_t *operation, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length, + uint8_t *tag, + size_t tag_size, + size_t *tag_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_AEAD) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_aead_finish( &operation->ctx.mbedtls_ctx, + ciphertext, + ciphertext_size, + ciphertext_length, tag, + tag_size, tag_length ) ); + +#endif /* MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_aead_finish( + &operation->ctx.transparent_test_driver_ctx, + ciphertext, ciphertext_size, + ciphertext_length, tag, tag_size, tag_length ) ); + + /* Add cases for opaque driver here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)ciphertext; + (void)ciphertext_size; + (void)ciphertext_length; + (void)tag; + (void)tag_size; + (void)tag_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_aead_verify( + psa_aead_operation_t *operation, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length, + const uint8_t *tag, + size_t tag_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_AEAD) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + { + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + uint8_t check_tag[PSA_AEAD_TAG_MAX_SIZE]; + size_t check_tag_length; + + status = mbedtls_psa_aead_finish( &operation->ctx.mbedtls_ctx, + plaintext, + plaintext_size, + plaintext_length, + check_tag, + sizeof( check_tag ), + &check_tag_length ); + + if( status == PSA_SUCCESS ) + { + if( tag_length != check_tag_length || + mbedtls_psa_safer_memcmp( tag, check_tag, tag_length ) + != 0 ) + status = PSA_ERROR_INVALID_SIGNATURE; + } + + mbedtls_platform_zeroize( check_tag, sizeof( check_tag ) ); + + return( status ); + } + +#endif /* MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_aead_verify( + &operation->ctx.transparent_test_driver_ctx, + plaintext, plaintext_size, + plaintext_length, tag, tag_length ) ); + + /* Add cases for opaque driver here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + (void)plaintext; + (void)plaintext_size; + (void)plaintext_length; + (void)tag; + (void)tag_length; + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +psa_status_t psa_driver_wrapper_aead_abort( + psa_aead_operation_t *operation ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_AEAD) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_aead_abort( &operation->ctx.mbedtls_ctx ) ); + +#endif /* MBEDTLS_PSA_BUILTIN_AEAD */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_aead_abort( + &operation->ctx.transparent_test_driver_ctx ) ); + + /* Add cases for opaque driver here */ + +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + } + + return( PSA_ERROR_INVALID_ARGUMENT ); +} + +/* + * MAC functions + */ +psa_status_t psa_driver_wrapper_mac_compute( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_mac_compute( + attributes, key_buffer, key_buffer_size, alg, + input, input_length, + mac, mac_size, mac_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ +#if defined(MBEDTLS_PSA_BUILTIN_MAC) + /* Fell through, meaning no accelerator supports this operation */ + status = mbedtls_psa_mac_compute( + attributes, key_buffer, key_buffer_size, alg, + input, input_length, + mac, mac_size, mac_length ); + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* MBEDTLS_PSA_BUILTIN_MAC */ + return( PSA_ERROR_NOT_SUPPORTED ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + status = mbedtls_test_opaque_mac_compute( + attributes, key_buffer, key_buffer_size, alg, + input, input_length, + mac, mac_size, mac_length ); + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void) key_buffer; + (void) key_buffer_size; + (void) alg; + (void) input; + (void) input_length; + (void) mac; + (void) mac_size; + (void) mac_length; + (void) status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_mac_sign_setup( + psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_mac_sign_setup( + &operation->ctx.transparent_test_driver_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + /* Declared with fallback == true */ + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ +#if defined(MBEDTLS_PSA_BUILTIN_MAC) + /* Fell through, meaning no accelerator supports this operation */ + status = mbedtls_psa_mac_sign_setup( &operation->ctx.mbedtls_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + if( status == PSA_SUCCESS ) + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* MBEDTLS_PSA_BUILTIN_MAC */ + return( PSA_ERROR_NOT_SUPPORTED ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + status = mbedtls_test_opaque_mac_sign_setup( + &operation->ctx.opaque_test_driver_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_OPAQUE_DRIVER_ID; + + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void) status; + (void) operation; + (void) key_buffer; + (void) key_buffer_size; + (void) alg; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_mac_verify_setup( + psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_mac_verify_setup( + &operation->ctx.transparent_test_driver_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + /* Declared with fallback == true */ + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ +#if defined(MBEDTLS_PSA_BUILTIN_MAC) + /* Fell through, meaning no accelerator supports this operation */ + status = mbedtls_psa_mac_verify_setup( &operation->ctx.mbedtls_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + if( status == PSA_SUCCESS ) + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* MBEDTLS_PSA_BUILTIN_MAC */ + return( PSA_ERROR_NOT_SUPPORTED ); + + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + status = mbedtls_test_opaque_mac_verify_setup( + &operation->ctx.opaque_test_driver_ctx, + attributes, + key_buffer, key_buffer_size, + alg ); + + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_OPAQUE_DRIVER_ID; + + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + /* Key is declared with a lifetime not known to us */ + (void) status; + (void) operation; + (void) key_buffer; + (void) key_buffer_size; + (void) alg; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_mac_update( + psa_mac_operation_t *operation, + const uint8_t *input, + size_t input_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_MAC) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_mac_update( &operation->ctx.mbedtls_ctx, + input, input_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_MAC */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_mac_update( + &operation->ctx.transparent_test_driver_ctx, + input, input_length ) ); + + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + return( mbedtls_test_opaque_mac_update( + &operation->ctx.opaque_test_driver_ctx, + input, input_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void) input; + (void) input_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_mac_sign_finish( + psa_mac_operation_t *operation, + uint8_t *mac, + size_t mac_size, + size_t *mac_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_MAC) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_mac_sign_finish( &operation->ctx.mbedtls_ctx, + mac, mac_size, mac_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_MAC */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_mac_sign_finish( + &operation->ctx.transparent_test_driver_ctx, + mac, mac_size, mac_length ) ); + + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + return( mbedtls_test_opaque_mac_sign_finish( + &operation->ctx.opaque_test_driver_ctx, + mac, mac_size, mac_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void) mac; + (void) mac_size; + (void) mac_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_mac_verify_finish( + psa_mac_operation_t *operation, + const uint8_t *mac, + size_t mac_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_MAC) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_mac_verify_finish( &operation->ctx.mbedtls_ctx, + mac, mac_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_MAC */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_mac_verify_finish( + &operation->ctx.transparent_test_driver_ctx, + mac, mac_length ) ); + + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + return( mbedtls_test_opaque_mac_verify_finish( + &operation->ctx.opaque_test_driver_ctx, + mac, mac_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void) mac; + (void) mac_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_mac_abort( + psa_mac_operation_t *operation ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_MAC) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_mac_abort( &operation->ctx.mbedtls_ctx ) ); +#endif /* MBEDTLS_PSA_BUILTIN_MAC */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_mac_abort( + &operation->ctx.transparent_test_driver_ctx ) ); + case MBEDTLS_TEST_OPAQUE_DRIVER_ID: + return( mbedtls_test_opaque_mac_abort( + &operation->ctx.opaque_test_driver_ctx ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +/* + * Asymmetric cryptography + */ +psa_status_t psa_driver_wrapper_asymmetric_encrypt( + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, const uint8_t *input, + size_t input_length, const uint8_t *salt, size_t salt_length, + uint8_t *output, size_t output_size, size_t *output_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_asymmetric_encrypt( attributes, + key_buffer, key_buffer_size, alg, input, input_length, + salt, salt_length, output, output_size, + output_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + return( mbedtls_psa_asymmetric_encrypt( attributes, + key_buffer, key_buffer_size, alg, input, input_length, + salt, salt_length, output, output_size, output_length ) + ); + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_asymmetric_encrypt( attributes, + key_buffer, key_buffer_size, alg, input, input_length, + salt, salt_length, output, output_size, output_length ) + ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + (void)key_buffer; + (void)key_buffer_size; + (void)alg; + (void)input; + (void)input_length; + (void)salt; + (void)salt_length; + (void)output; + (void)output_size; + (void)output_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_asymmetric_decrypt( + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, const uint8_t *input, + size_t input_length, const uint8_t *salt, size_t salt_length, + uint8_t *output, size_t output_size, size_t *output_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_asymmetric_decrypt( attributes, + key_buffer, key_buffer_size, alg, input, input_length, + salt, salt_length, output, output_size, + output_length ); + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + return( mbedtls_psa_asymmetric_decrypt( attributes, + key_buffer, key_buffer_size, alg,input, input_length, + salt, salt_length, output, output_size, + output_length ) ); + /* Add cases for opaque driver here */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_asymmetric_decrypt( attributes, + key_buffer, key_buffer_size, alg, input, input_length, + salt, salt_length, output, output_size, + output_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + default: + /* Key is declared with a lifetime not known to us */ + (void)status; + (void)key_buffer; + (void)key_buffer_size; + (void)alg; + (void)input; + (void)input_length; + (void)salt; + (void)salt_length; + (void)output; + (void)output_size; + (void)output_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_key_agreement( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *peer_key, + size_t peer_key_length, + uint8_t *shared_secret, + size_t shared_secret_size, + size_t *shared_secret_length + ) + { + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( attributes->core.lifetime ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = + mbedtls_test_transparent_key_agreement( attributes, + key_buffer, key_buffer_size, alg, peer_key, + peer_key_length, shared_secret, shared_secret_size, + shared_secret_length ); + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + /* Software Fallback */ + status = psa_key_agreement_raw_builtin( attributes, + key_buffer, + key_buffer_size, + alg, + peer_key, + peer_key_length, + shared_secret, + shared_secret_size, + shared_secret_length ); + return( status ); +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case PSA_CRYPTO_TEST_DRIVER_LOCATION: + return( mbedtls_test_opaque_key_agreement( attributes, + key_buffer, key_buffer_size, alg, peer_key, + peer_key_length, shared_secret, shared_secret_size, + shared_secret_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + + default: + (void) attributes; + (void) key_buffer; + (void) key_buffer_size; + (void) peer_key; + (void) peer_key_length; + (void) shared_secret; + (void) shared_secret_size; + (void) shared_secret_length; + return( PSA_ERROR_NOT_SUPPORTED ); + + } + } + +psa_status_t psa_driver_wrapper_pake_setup( + psa_pake_operation_t *operation, + const psa_crypto_driver_pake_inputs_t *inputs ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + psa_key_location_t location = + PSA_KEY_LIFETIME_GET_LOCATION( psa_get_key_lifetime( &inputs->attributes ) ); + + switch( location ) + { + case PSA_KEY_LOCATION_LOCAL_STORAGE: + /* Key is stored in the slot in export representation, so + * cycle through all known transparent accelerators */ +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + status = mbedtls_test_transparent_pake_setup( + &operation->data.ctx.transparent_test_driver_ctx, + inputs ); + if( status == PSA_SUCCESS ) + operation->id = MBEDTLS_TEST_TRANSPARENT_DRIVER_ID; + /* Declared with fallback == true */ + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ +#if defined(MBEDTLS_PSA_BUILTIN_PAKE) + status = mbedtls_psa_pake_setup( &operation->data.ctx.mbedtls_ctx, + inputs ); + if( status == PSA_SUCCESS ) + operation->id = PSA_CRYPTO_MBED_TLS_DRIVER_ID; + return status; +#endif + return( PSA_ERROR_NOT_SUPPORTED ); + /* Add cases for opaque driver here */ + default: + /* Key is declared with a lifetime not known to us */ + (void)operation; + (void)inputs; + (void)status; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} +psa_status_t psa_driver_wrapper_pake_output( + psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_PAKE) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_pake_output( &operation->data.ctx.mbedtls_ctx, step, + output, output_size, output_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_PAKE */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_pake_output( + &operation->data.ctx.transparent_test_driver_ctx, + step, output, output_size, output_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void) step; + (void) output; + (void) output_size; + (void) output_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_pake_input( + psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + const uint8_t *input, + size_t input_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_PAKE) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_pake_input( &operation->data.ctx.mbedtls_ctx, + step, input, + input_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_PAKE */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_pake_input( + &operation->data.ctx.transparent_test_driver_ctx, + step, + input, input_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void) step; + (void) input; + (void) input_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_pake_get_implicit_key( + psa_pake_operation_t *operation, + uint8_t *output, size_t output_size, + size_t *output_length ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_PAKE) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_pake_get_implicit_key( &operation->data.ctx.mbedtls_ctx, + output, output_size, output_length ) ); +#endif /* MBEDTLS_PSA_BUILTIN_PAKE */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_pake_get_implicit_key( + &operation->data.ctx.transparent_test_driver_ctx, + output, output_size, output_length ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + (void) output; + (void) output_size; + (void) output_length; + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +psa_status_t psa_driver_wrapper_pake_abort( + psa_pake_operation_t * operation ) +{ + switch( operation->id ) + { +#if defined(MBEDTLS_PSA_BUILTIN_PAKE) + case PSA_CRYPTO_MBED_TLS_DRIVER_ID: + return( mbedtls_psa_pake_abort( &operation->data.ctx.mbedtls_ctx ) ); +#endif /* MBEDTLS_PSA_BUILTIN_PAKE */ + +#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT) +#if defined(PSA_CRYPTO_DRIVER_TEST) + case MBEDTLS_TEST_TRANSPARENT_DRIVER_ID: + return( mbedtls_test_transparent_pake_abort( + &operation->data.ctx.transparent_test_driver_ctx ) ); +#endif /* PSA_CRYPTO_DRIVER_TEST */ +#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */ + default: + return( PSA_ERROR_INVALID_ARGUMENT ); + } +} + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_driver_wrappers.h b/r5dev/thirdparty/mbedtls/psa_crypto_driver_wrappers.h new file mode 100644 index 00000000..7d672d64 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_driver_wrappers.h @@ -0,0 +1,441 @@ +/* + * Function signatures for functionality that can be provided by + * cryptographic accelerators. + */ +/* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_DRIVER_WRAPPERS_H +#define PSA_CRYPTO_DRIVER_WRAPPERS_H + +#include "psa/crypto.h" +#include "psa/crypto_driver_common.h" + +/* + * Initialization and termination functions + */ +psa_status_t psa_driver_wrapper_init(void); +void psa_driver_wrapper_free(void); + +/* + * Signature functions + */ +psa_status_t psa_driver_wrapper_sign_message( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length); + +psa_status_t psa_driver_wrapper_verify_message( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *signature, + size_t signature_length); + +psa_status_t psa_driver_wrapper_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length); + +psa_status_t psa_driver_wrapper_verify_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length); + +/* + * Interruptible Signature functions + */ + +uint32_t psa_driver_wrapper_sign_hash_get_num_ops( + psa_sign_hash_interruptible_operation_t *operation); + +uint32_t psa_driver_wrapper_verify_hash_get_num_ops( + psa_verify_hash_interruptible_operation_t *operation); + +psa_status_t psa_driver_wrapper_sign_hash_start( + psa_sign_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length); + +psa_status_t psa_driver_wrapper_sign_hash_complete( + psa_sign_hash_interruptible_operation_t *operation, + uint8_t *signature, size_t signature_size, + size_t *signature_length); + +psa_status_t psa_driver_wrapper_sign_hash_abort( + psa_sign_hash_interruptible_operation_t *operation); + +psa_status_t psa_driver_wrapper_verify_hash_start( + psa_verify_hash_interruptible_operation_t *operation, + const psa_key_attributes_t *attributes, const uint8_t *key_buffer, + size_t key_buffer_size, psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length); + +psa_status_t psa_driver_wrapper_verify_hash_complete( + psa_verify_hash_interruptible_operation_t *operation); + +psa_status_t psa_driver_wrapper_verify_hash_abort( + psa_verify_hash_interruptible_operation_t *operation); + +/* + * Key handling functions + */ + +psa_status_t psa_driver_wrapper_import_key( + const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + uint8_t *key_buffer, size_t key_buffer_size, + size_t *key_buffer_length, size_t *bits); + +psa_status_t psa_driver_wrapper_export_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length); + +psa_status_t psa_driver_wrapper_export_public_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length); + +psa_status_t psa_driver_wrapper_get_key_buffer_size( + const psa_key_attributes_t *attributes, + size_t *key_buffer_size); + +psa_status_t psa_driver_wrapper_get_key_buffer_size_from_key_data( + const psa_key_attributes_t *attributes, + const uint8_t *data, + size_t data_length, + size_t *key_buffer_size); + +psa_status_t psa_driver_wrapper_generate_key( + const psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length); + +psa_status_t psa_driver_wrapper_get_builtin_key( + psa_drv_slot_number_t slot_number, + psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length); + +psa_status_t psa_driver_wrapper_copy_key( + psa_key_attributes_t *attributes, + const uint8_t *source_key, size_t source_key_length, + uint8_t *target_key_buffer, size_t target_key_buffer_size, + size_t *target_key_buffer_length); +/* + * Cipher functions + */ +psa_status_t psa_driver_wrapper_cipher_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *iv, + size_t iv_length, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_cipher_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_cipher_encrypt_setup( + psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg); + +psa_status_t psa_driver_wrapper_cipher_decrypt_setup( + psa_cipher_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg); + +psa_status_t psa_driver_wrapper_cipher_set_iv( + psa_cipher_operation_t *operation, + const uint8_t *iv, + size_t iv_length); + +psa_status_t psa_driver_wrapper_cipher_update( + psa_cipher_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_cipher_finish( + psa_cipher_operation_t *operation, + uint8_t *output, + size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_cipher_abort( + psa_cipher_operation_t *operation); + +/* + * Hashing functions + */ +psa_status_t psa_driver_wrapper_hash_compute( + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); + +psa_status_t psa_driver_wrapper_hash_setup( + psa_hash_operation_t *operation, + psa_algorithm_t alg); + +psa_status_t psa_driver_wrapper_hash_clone( + const psa_hash_operation_t *source_operation, + psa_hash_operation_t *target_operation); + +psa_status_t psa_driver_wrapper_hash_update( + psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length); + +psa_status_t psa_driver_wrapper_hash_finish( + psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); + +psa_status_t psa_driver_wrapper_hash_abort( + psa_hash_operation_t *operation); + +/* + * AEAD functions + */ + +psa_status_t psa_driver_wrapper_aead_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *plaintext, size_t plaintext_length, + uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length); + +psa_status_t psa_driver_wrapper_aead_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *nonce, size_t nonce_length, + const uint8_t *additional_data, size_t additional_data_length, + const uint8_t *ciphertext, size_t ciphertext_length, + uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length); + +psa_status_t psa_driver_wrapper_aead_encrypt_setup( + psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg); + +psa_status_t psa_driver_wrapper_aead_decrypt_setup( + psa_aead_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg); + +psa_status_t psa_driver_wrapper_aead_set_nonce( + psa_aead_operation_t *operation, + const uint8_t *nonce, + size_t nonce_length); + +psa_status_t psa_driver_wrapper_aead_set_lengths( + psa_aead_operation_t *operation, + size_t ad_length, + size_t plaintext_length); + +psa_status_t psa_driver_wrapper_aead_update_ad( + psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length); + +psa_status_t psa_driver_wrapper_aead_update( + psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_aead_finish( + psa_aead_operation_t *operation, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length, + uint8_t *tag, + size_t tag_size, + size_t *tag_length); + +psa_status_t psa_driver_wrapper_aead_verify( + psa_aead_operation_t *operation, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length, + const uint8_t *tag, + size_t tag_length); + +psa_status_t psa_driver_wrapper_aead_abort( + psa_aead_operation_t *operation); + +/* + * MAC functions + */ +psa_status_t psa_driver_wrapper_mac_compute( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length); + +psa_status_t psa_driver_wrapper_mac_sign_setup( + psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg); + +psa_status_t psa_driver_wrapper_mac_verify_setup( + psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg); + +psa_status_t psa_driver_wrapper_mac_update( + psa_mac_operation_t *operation, + const uint8_t *input, + size_t input_length); + +psa_status_t psa_driver_wrapper_mac_sign_finish( + psa_mac_operation_t *operation, + uint8_t *mac, + size_t mac_size, + size_t *mac_length); + +psa_status_t psa_driver_wrapper_mac_verify_finish( + psa_mac_operation_t *operation, + const uint8_t *mac, + size_t mac_length); + +psa_status_t psa_driver_wrapper_mac_abort( + psa_mac_operation_t *operation); + +/* + * Asymmetric cryptography + */ +psa_status_t psa_driver_wrapper_asymmetric_encrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_asymmetric_decrypt( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/* + * Raw Key Agreement + */ +psa_status_t psa_driver_wrapper_key_agreement( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *peer_key, + size_t peer_key_length, + uint8_t *shared_secret, + size_t shared_secret_size, + size_t *shared_secret_length); + +/* + * PAKE functions. + */ +psa_status_t psa_driver_wrapper_pake_setup( + psa_pake_operation_t *operation, + const psa_crypto_driver_pake_inputs_t *inputs); + +psa_status_t psa_driver_wrapper_pake_output( + psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_pake_input( + psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + const uint8_t *input, + size_t input_length); + +psa_status_t psa_driver_wrapper_pake_get_implicit_key( + psa_pake_operation_t *operation, + uint8_t *output, size_t output_size, + size_t *output_length); + +psa_status_t psa_driver_wrapper_pake_abort( + psa_pake_operation_t *operation); + +#endif /* PSA_CRYPTO_DRIVER_WRAPPERS_H */ + +/* End of automatically generated file. */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_ecp.c b/r5dev/thirdparty/mbedtls/psa_crypto_ecp.c new file mode 100644 index 00000000..f70d804b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_ecp.c @@ -0,0 +1,567 @@ +/* + * PSA ECP layer on top of Mbed TLS crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include +#include "psa_crypto_core.h" +#include "psa_crypto_ecp.h" +#include "psa_crypto_random_impl.h" +#include "hash_info.h" + +#include +#include +#include "mbedtls/platform.h" + +#include +#include +#include +#include + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) +psa_status_t mbedtls_psa_ecp_load_representation( + psa_key_type_t type, size_t curve_bits, + const uint8_t *data, size_t data_length, + mbedtls_ecp_keypair **p_ecp) +{ + mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE; + psa_status_t status; + mbedtls_ecp_keypair *ecp = NULL; + size_t curve_bytes = data_length; + int explicit_bits = (curve_bits != 0); + + if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type) && + PSA_KEY_TYPE_ECC_GET_FAMILY(type) != PSA_ECC_FAMILY_MONTGOMERY) { + /* A Weierstrass public key is represented as: + * - The byte 0x04; + * - `x_P` as a `ceiling(m/8)`-byte string, big-endian; + * - `y_P` as a `ceiling(m/8)`-byte string, big-endian. + * So its data length is 2m+1 where m is the curve size in bits. + */ + if ((data_length & 1) == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + curve_bytes = data_length / 2; + + /* Montgomery public keys are represented in compressed format, meaning + * their curve_bytes is equal to the amount of input. */ + + /* Private keys are represented in uncompressed private random integer + * format, meaning their curve_bytes is equal to the amount of input. */ + } + + if (explicit_bits) { + /* With an explicit bit-size, the data must have the matching length. */ + if (curve_bytes != PSA_BITS_TO_BYTES(curve_bits)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } else { + /* We need to infer the bit-size from the data. Since the only + * information we have is the length in bytes, the value of curve_bits + * at this stage is rounded up to the nearest multiple of 8. */ + curve_bits = PSA_BYTES_TO_BITS(curve_bytes); + } + + /* Allocate and initialize a key representation. */ + ecp = mbedtls_calloc(1, sizeof(mbedtls_ecp_keypair)); + if (ecp == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + mbedtls_ecp_keypair_init(ecp); + + /* Load the group. */ + grp_id = mbedtls_ecc_group_of_psa(PSA_KEY_TYPE_ECC_GET_FAMILY(type), + curve_bits, !explicit_bits); + if (grp_id == MBEDTLS_ECP_DP_NONE) { + /* We can't distinguish between a nonsensical family/size combination + * (which would warrant PSA_ERROR_INVALID_ARGUMENT) and a + * well-regarded curve that Mbed TLS just doesn't know about (which + * would warrant PSA_ERROR_NOT_SUPPORTED). For uniformity with how + * curves that Mbed TLS knows about but for which support is disabled + * at build time, return NOT_SUPPORTED. */ + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + + status = mbedtls_to_psa_error( + mbedtls_ecp_group_load(&ecp->grp, grp_id)); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* Load the key material. */ + if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) { + /* Load the public value. */ + status = mbedtls_to_psa_error( + mbedtls_ecp_point_read_binary(&ecp->grp, &ecp->Q, + data, + data_length)); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* Check that the point is on the curve. */ + status = mbedtls_to_psa_error( + mbedtls_ecp_check_pubkey(&ecp->grp, &ecp->Q)); + if (status != PSA_SUCCESS) { + goto exit; + } + } else { + /* Load and validate the secret value. */ + status = mbedtls_to_psa_error( + mbedtls_ecp_read_key(ecp->grp.id, + ecp, + data, + data_length)); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + *p_ecp = ecp; +exit: + if (status != PSA_SUCCESS) { + mbedtls_ecp_keypair_free(ecp); + mbedtls_free(ecp); + } + + return status; +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) */ + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) + +psa_status_t mbedtls_psa_ecp_import_key( + const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + uint8_t *key_buffer, size_t key_buffer_size, + size_t *key_buffer_length, size_t *bits) +{ + psa_status_t status; + mbedtls_ecp_keypair *ecp = NULL; + + /* Parse input */ + status = mbedtls_psa_ecp_load_representation(attributes->core.type, + attributes->core.bits, + data, + data_length, + &ecp); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->core.type) == + PSA_ECC_FAMILY_MONTGOMERY) { + *bits = ecp->grp.nbits + 1; + } else { + *bits = ecp->grp.nbits; + } + + /* Re-export the data to PSA export format. There is currently no support + * for other input formats then the export format, so this is a 1-1 + * copy operation. */ + status = mbedtls_psa_ecp_export_key(attributes->core.type, + ecp, + key_buffer, + key_buffer_size, + key_buffer_length); +exit: + /* Always free the PK object (will also free contained ECP context) */ + mbedtls_ecp_keypair_free(ecp); + mbedtls_free(ecp); + + return status; +} + +psa_status_t mbedtls_psa_ecp_export_key(psa_key_type_t type, + mbedtls_ecp_keypair *ecp, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + psa_status_t status; + + if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) { + /* Check whether the public part is loaded */ + if (mbedtls_ecp_is_zero(&ecp->Q)) { + /* Calculate the public key */ + status = mbedtls_to_psa_error( + mbedtls_ecp_mul(&ecp->grp, &ecp->Q, &ecp->d, &ecp->grp.G, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE)); + if (status != PSA_SUCCESS) { + return status; + } + } + + status = mbedtls_to_psa_error( + mbedtls_ecp_point_write_binary(&ecp->grp, &ecp->Q, + MBEDTLS_ECP_PF_UNCOMPRESSED, + data_length, + data, + data_size)); + if (status != PSA_SUCCESS) { + memset(data, 0, data_size); + } + + return status; + } else { + if (data_size < PSA_BITS_TO_BYTES(ecp->grp.nbits)) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + status = mbedtls_to_psa_error( + mbedtls_ecp_write_key(ecp, + data, + PSA_BITS_TO_BYTES(ecp->grp.nbits))); + if (status == PSA_SUCCESS) { + *data_length = PSA_BITS_TO_BYTES(ecp->grp.nbits); + } else { + memset(data, 0, data_size); + } + + return status; + } +} + +psa_status_t mbedtls_psa_ecp_export_public_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_keypair *ecp = NULL; + + status = mbedtls_psa_ecp_load_representation( + attributes->core.type, attributes->core.bits, + key_buffer, key_buffer_size, &ecp); + if (status != PSA_SUCCESS) { + return status; + } + + status = mbedtls_psa_ecp_export_key( + PSA_KEY_TYPE_ECC_PUBLIC_KEY( + PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->core.type)), + ecp, data, data_size, data_length); + + mbedtls_ecp_keypair_free(ecp); + mbedtls_free(ecp); + + return status; +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */ + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) +psa_status_t mbedtls_psa_ecp_generate_key( + const psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY( + attributes->core.type); + mbedtls_ecp_group_id grp_id = + mbedtls_ecc_group_of_psa(curve, attributes->core.bits, 0); + + const mbedtls_ecp_curve_info *curve_info = + mbedtls_ecp_curve_info_from_grp_id(grp_id); + mbedtls_ecp_keypair ecp; + + if (attributes->domain_parameters_size != 0) { + return PSA_ERROR_NOT_SUPPORTED; + } + + if (grp_id == MBEDTLS_ECP_DP_NONE || curve_info == NULL) { + return PSA_ERROR_NOT_SUPPORTED; + } + + mbedtls_ecp_keypair_init(&ecp); + ret = mbedtls_ecp_gen_key(grp_id, &ecp, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE); + if (ret != 0) { + mbedtls_ecp_keypair_free(&ecp); + return mbedtls_to_psa_error(ret); + } + + status = mbedtls_to_psa_error( + mbedtls_ecp_write_key(&ecp, key_buffer, key_buffer_size)); + + mbedtls_ecp_keypair_free(&ecp); + + if (status == PSA_SUCCESS) { + *key_buffer_length = key_buffer_size; + } + + return status; +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR) */ + +/****************************************************************/ +/* ECDSA sign/verify */ +/****************************************************************/ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) +psa_status_t mbedtls_psa_ecdsa_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_keypair *ecp = NULL; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t curve_bytes; + mbedtls_mpi r, s; + + status = mbedtls_psa_ecp_load_representation(attributes->core.type, + attributes->core.bits, + key_buffer, + key_buffer_size, + &ecp); + if (status != PSA_SUCCESS) { + return status; + } + + curve_bytes = PSA_BITS_TO_BYTES(ecp->grp.pbits); + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + if (signature_size < 2 * curve_bytes) { + ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + goto cleanup; + } + + if (PSA_ALG_ECDSA_IS_DETERMINISTIC(alg)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) + psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg); + mbedtls_md_type_t md_alg = mbedtls_hash_info_md_from_psa(hash_alg); + MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign_det_ext( + &ecp->grp, &r, &s, + &ecp->d, hash, + hash_length, md_alg, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE)); +#else + ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE; + goto cleanup; +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */ + } else { + (void) alg; + MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign(&ecp->grp, &r, &s, &ecp->d, + hash, hash_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE)); + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&r, + signature, + curve_bytes)); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&s, + signature + curve_bytes, + curve_bytes)); +cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + if (ret == 0) { + *signature_length = 2 * curve_bytes; + } + + mbedtls_ecp_keypair_free(ecp); + mbedtls_free(ecp); + + return mbedtls_to_psa_error(ret); +} + +psa_status_t mbedtls_psa_ecp_load_public_part(mbedtls_ecp_keypair *ecp) +{ + int ret = 0; + + /* Check whether the public part is loaded. If not, load it. */ + if (mbedtls_ecp_is_zero(&ecp->Q)) { + ret = mbedtls_ecp_mul(&ecp->grp, &ecp->Q, + &ecp->d, &ecp->grp.G, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE); + } + + return mbedtls_to_psa_error(ret); +} + +psa_status_t mbedtls_psa_ecdsa_verify_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_ecp_keypair *ecp = NULL; + size_t curve_bytes; + mbedtls_mpi r, s; + + (void) alg; + + status = mbedtls_psa_ecp_load_representation(attributes->core.type, + attributes->core.bits, + key_buffer, + key_buffer_size, + &ecp); + if (status != PSA_SUCCESS) { + return status; + } + + curve_bytes = PSA_BITS_TO_BYTES(ecp->grp.pbits); + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + if (signature_length != 2 * curve_bytes) { + status = PSA_ERROR_INVALID_SIGNATURE; + goto cleanup; + } + + status = mbedtls_to_psa_error(mbedtls_mpi_read_binary(&r, + signature, + curve_bytes)); + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = mbedtls_to_psa_error(mbedtls_mpi_read_binary(&s, + signature + curve_bytes, + curve_bytes)); + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = mbedtls_psa_ecp_load_public_part(ecp); + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = mbedtls_to_psa_error(mbedtls_ecdsa_verify(&ecp->grp, hash, + hash_length, &ecp->Q, + &r, &s)); +cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + mbedtls_ecp_keypair_free(ecp); + mbedtls_free(ecp); + + return status; +} + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \ + * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */ + +/****************************************************************/ +/* ECDH Key Agreement */ +/****************************************************************/ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) +psa_status_t mbedtls_psa_key_agreement_ecdh( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *peer_key, size_t peer_key_length, + uint8_t *shared_secret, size_t shared_secret_size, + size_t *shared_secret_length) +{ + psa_status_t status; + if (!PSA_KEY_TYPE_IS_ECC_KEY_PAIR(attributes->core.type) || + !PSA_ALG_IS_ECDH(alg)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + mbedtls_ecp_keypair *ecp = NULL; + status = mbedtls_psa_ecp_load_representation( + attributes->core.type, + attributes->core.bits, + key_buffer, + key_buffer_size, + &ecp); + if (status != PSA_SUCCESS) { + return status; + } + mbedtls_ecp_keypair *their_key = NULL; + mbedtls_ecdh_context ecdh; + size_t bits = 0; + psa_ecc_family_t curve = mbedtls_ecc_group_to_psa(ecp->grp.id, &bits); + mbedtls_ecdh_init(&ecdh); + + status = mbedtls_psa_ecp_load_representation( + PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve), + bits, + peer_key, + peer_key_length, + &their_key); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = mbedtls_to_psa_error( + mbedtls_ecdh_get_params(&ecdh, their_key, MBEDTLS_ECDH_THEIRS)); + if (status != PSA_SUCCESS) { + goto exit; + } + status = mbedtls_to_psa_error( + mbedtls_ecdh_get_params(&ecdh, ecp, MBEDTLS_ECDH_OURS)); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = mbedtls_to_psa_error( + mbedtls_ecdh_calc_secret(&ecdh, + shared_secret_length, + shared_secret, shared_secret_size, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE)); + if (status != PSA_SUCCESS) { + goto exit; + } + if (PSA_BITS_TO_BYTES(bits) != *shared_secret_length) { + status = PSA_ERROR_CORRUPTION_DETECTED; + } +exit: + if (status != PSA_SUCCESS) { + mbedtls_platform_zeroize(shared_secret, shared_secret_size); + } + mbedtls_ecdh_free(&ecdh); + mbedtls_ecp_keypair_free(their_key); + mbedtls_free(their_key); + mbedtls_ecp_keypair_free(ecp); + mbedtls_free(ecp); + return status; +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECDH */ + + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_ecp.h b/r5dev/thirdparty/mbedtls/psa_crypto_ecp.h new file mode 100644 index 00000000..f4ad3d27 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_ecp.h @@ -0,0 +1,279 @@ +/* + * PSA ECP layer on top of Mbed TLS crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_ECP_H +#define PSA_CRYPTO_ECP_H + +#include +#include + +/** Load the contents of a key buffer into an internal ECP representation + * + * \param[in] type The type of key contained in \p data. + * \param[in] curve_bits The nominal bit-size of the curve. + * It must be consistent with the representation + * passed in \p data. + * This can be 0, in which case the bit-size + * is inferred from \p data_length (which is possible + * for all key types and representation formats + * formats that are currently supported or will + * be in the foreseeable future). + * \param[in] data The buffer from which to load the representation. + * \param[in] data_length The size in bytes of \p data. + * \param[out] p_ecp Returns a pointer to an ECP context on success. + * The caller is responsible for freeing both the + * contents of the context and the context itself + * when done. + */ +psa_status_t mbedtls_psa_ecp_load_representation(psa_key_type_t type, + size_t curve_bits, + const uint8_t *data, + size_t data_length, + mbedtls_ecp_keypair **p_ecp); + +/** Load the public part of an internal ECP, if required. + * + * \param ecp The ECP context to load the public part for. + * + * \return PSA_SUCCESS on success, otherwise an MPI error. + */ + +psa_status_t mbedtls_psa_ecp_load_public_part(mbedtls_ecp_keypair *ecp); + +/** Import an ECP key in binary format. + * + * \note The signature of this function is that of a PSA driver + * import_key entry point. This function behaves as an import_key + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes for the key to import. + * \param[in] data The buffer containing the key data in import + * format. + * \param[in] data_length Size of the \p data buffer in bytes. + * \param[out] key_buffer The buffer containing the key data in output + * format. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. This + * size is greater or equal to \p data_length. + * \param[out] key_buffer_length The length of the data written in \p + * key_buffer in bytes. + * \param[out] bits The key size in number of bits. + * + * \retval #PSA_SUCCESS The ECP key was imported successfully. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key data is not correctly formatted. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_ecp_import_key( + const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + uint8_t *key_buffer, size_t key_buffer_size, + size_t *key_buffer_length, size_t *bits); + +/** Export an ECP key to export representation + * + * \param[in] type The type of key (public/private) to export + * \param[in] ecp The internal ECP representation from which to export + * \param[out] data The buffer to export to + * \param[in] data_size The length of the buffer to export to + * \param[out] data_length The amount of bytes written to \p data + */ +psa_status_t mbedtls_psa_ecp_export_key(psa_key_type_t type, + mbedtls_ecp_keypair *ecp, + uint8_t *data, + size_t data_size, + size_t *data_length); + +/** Export an ECP public key or the public part of an ECP key pair in binary + * format. + * + * \note The signature of this function is that of a PSA driver + * export_public_key entry point. This function behaves as an + * export_public_key entry point as defined in the PSA driver interface + * specification. + * + * \param[in] attributes The attributes for the key to export. + * \param[in] key_buffer Material or context of the key to export. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[out] data Buffer where the key data is to be written. + * \param[in] data_size Size of the \p data buffer in bytes. + * \param[out] data_length On success, the number of bytes written in + * \p data + * + * \retval #PSA_SUCCESS The ECP public key was exported successfully. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_ecp_export_public_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length); + +/** + * \brief Generate an ECP key. + * + * \note The signature of the function is that of a PSA driver generate_key + * entry point. + * + * \param[in] attributes The attributes for the ECP key to generate. + * \param[out] key_buffer Buffer where the key data is to be written. + * \param[in] key_buffer_size Size of \p key_buffer in bytes. + * \param[out] key_buffer_length On success, the number of bytes written in + * \p key_buffer. + * + * \retval #PSA_SUCCESS + * The key was successfully generated. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Key length or type not supported. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of \p key_buffer is too small. + */ +psa_status_t mbedtls_psa_ecp_generate_key( + const psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length); + +/** Sign an already-calculated hash with ECDSA. + * + * \note The signature of this function is that of a PSA driver + * sign_hash entry point. This function behaves as a sign_hash + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the ECC key to use for the + * operation. + * \param[in] key_buffer The buffer containing the ECC key context. + * format. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg Randomized or deterministic ECDSA algorithm. + * \param[in] hash The hash or message to sign. + * \param[in] hash_length Size of the \p hash buffer in bytes. + * \param[out] signature Buffer where the signature is to be written. + * \param[in] signature_size Size of the \p signature buffer in bytes. + * \param[out] signature_length On success, the number of bytes + * that make up the returned signature value. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c PSA_KEY_TYPE_ECC_KEY_PAIR, \c key_bits, + * \p alg) where \c key_bits is the bit-size of the ECC key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + */ +psa_status_t mbedtls_psa_ecdsa_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length); + +/** + * \brief Verify an ECDSA hash or short message signature. + * + * \note The signature of this function is that of a PSA driver + * verify_hash entry point. This function behaves as a verify_hash + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the ECC key to use for the + * operation. + * \param[in] key_buffer The buffer containing the ECC key context. + * format. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg Randomized or deterministic ECDSA algorithm. + * \param[in] hash The hash or message whose signature is to be + * verified. + * \param[in] hash_length Size of the \p hash buffer in bytes. + * \param[in] signature Buffer containing the signature to verify. + * \param[in] signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * The signature is valid. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed + * signature is not a valid signature. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_ecdsa_verify_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length); + + +/** Perform a key agreement and return the raw ECDH shared secret. + * + * \note The signature of this function is that of a PSA driver + * key_agreement entry point. This function behaves as a key_agreement + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the private key + * context. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in + * bytes. + * \param[in] alg A key agreement algorithm that is + * compatible with the type of the key. + * \param[in] peer_key The buffer containing the key context + * of the peer's public key. + * \param[in] peer_key_length Size of the \p peer_key buffer in + * bytes. + * \param[out] shared_secret The buffer to which the shared secret + * is to be written. + * \param[in] shared_secret_size Size of the \p shared_secret buffer in + * bytes. + * \param[out] shared_secret_length On success, the number of bytes that make + * up the returned shared secret. + * \retval #PSA_SUCCESS + * Success. Shared secret successfully calculated. + * \retval #PSA_ERROR_INVALID_HANDLE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \p alg is not a key agreement algorithm, or + * \p private_key is not compatible with \p alg, + * or \p peer_key is not valid for \p alg or not compatible with + * \p private_key. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p shared_secret_size is too small + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not a supported key agreement algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_key_agreement_ecdh( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *peer_key, size_t peer_key_length, + uint8_t *shared_secret, size_t shared_secret_size, + size_t *shared_secret_length); +#endif /* PSA_CRYPTO_ECP_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_hash.c b/r5dev/thirdparty/mbedtls/psa_crypto_hash.c new file mode 100644 index 00000000..ddf70949 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_hash.c @@ -0,0 +1,380 @@ +/* + * PSA hashing layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include +#include "psa_crypto_core.h" +#include "psa_crypto_hash.h" + +#include +#include + +#if defined(MBEDTLS_PSA_BUILTIN_HASH) +psa_status_t mbedtls_psa_hash_abort( + mbedtls_psa_hash_operation_t *operation) +{ + switch (operation->alg) { + case 0: + /* The object has (apparently) been initialized but it is not + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + break; +#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) + case PSA_ALG_MD5: + mbedtls_md5_free(&operation->ctx.md5); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) + case PSA_ALG_RIPEMD160: + mbedtls_ripemd160_free(&operation->ctx.ripemd160); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) + case PSA_ALG_SHA_1: + mbedtls_sha1_free(&operation->ctx.sha1); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) + case PSA_ALG_SHA_224: + mbedtls_sha256_free(&operation->ctx.sha256); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) + case PSA_ALG_SHA_256: + mbedtls_sha256_free(&operation->ctx.sha256); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) + case PSA_ALG_SHA_384: + mbedtls_sha512_free(&operation->ctx.sha512); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) + case PSA_ALG_SHA_512: + mbedtls_sha512_free(&operation->ctx.sha512); + break; +#endif + default: + return PSA_ERROR_BAD_STATE; + } + operation->alg = 0; + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_hash_setup( + mbedtls_psa_hash_operation_t *operation, + psa_algorithm_t alg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* A context must be freshly initialized before it can be set up. */ + if (operation->alg != 0) { + return PSA_ERROR_BAD_STATE; + } + + switch (alg) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) + case PSA_ALG_MD5: + mbedtls_md5_init(&operation->ctx.md5); + ret = mbedtls_md5_starts(&operation->ctx.md5); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) + case PSA_ALG_RIPEMD160: + mbedtls_ripemd160_init(&operation->ctx.ripemd160); + ret = mbedtls_ripemd160_starts(&operation->ctx.ripemd160); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) + case PSA_ALG_SHA_1: + mbedtls_sha1_init(&operation->ctx.sha1); + ret = mbedtls_sha1_starts(&operation->ctx.sha1); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) + case PSA_ALG_SHA_224: + mbedtls_sha256_init(&operation->ctx.sha256); + ret = mbedtls_sha256_starts(&operation->ctx.sha256, 1); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) + case PSA_ALG_SHA_256: + mbedtls_sha256_init(&operation->ctx.sha256); + ret = mbedtls_sha256_starts(&operation->ctx.sha256, 0); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) + case PSA_ALG_SHA_384: + mbedtls_sha512_init(&operation->ctx.sha512); + ret = mbedtls_sha512_starts(&operation->ctx.sha512, 1); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) + case PSA_ALG_SHA_512: + mbedtls_sha512_init(&operation->ctx.sha512); + ret = mbedtls_sha512_starts(&operation->ctx.sha512, 0); + break; +#endif + default: + return PSA_ALG_IS_HASH(alg) ? + PSA_ERROR_NOT_SUPPORTED : + PSA_ERROR_INVALID_ARGUMENT; + } + if (ret == 0) { + operation->alg = alg; + } else { + mbedtls_psa_hash_abort(operation); + } + return mbedtls_to_psa_error(ret); +} + +psa_status_t mbedtls_psa_hash_clone( + const mbedtls_psa_hash_operation_t *source_operation, + mbedtls_psa_hash_operation_t *target_operation) +{ + switch (source_operation->alg) { + case 0: + return PSA_ERROR_BAD_STATE; +#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) + case PSA_ALG_MD5: + mbedtls_md5_clone(&target_operation->ctx.md5, + &source_operation->ctx.md5); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) + case PSA_ALG_RIPEMD160: + mbedtls_ripemd160_clone(&target_operation->ctx.ripemd160, + &source_operation->ctx.ripemd160); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) + case PSA_ALG_SHA_1: + mbedtls_sha1_clone(&target_operation->ctx.sha1, + &source_operation->ctx.sha1); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) + case PSA_ALG_SHA_224: + mbedtls_sha256_clone(&target_operation->ctx.sha256, + &source_operation->ctx.sha256); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) + case PSA_ALG_SHA_256: + mbedtls_sha256_clone(&target_operation->ctx.sha256, + &source_operation->ctx.sha256); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) + case PSA_ALG_SHA_384: + mbedtls_sha512_clone(&target_operation->ctx.sha512, + &source_operation->ctx.sha512); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) + case PSA_ALG_SHA_512: + mbedtls_sha512_clone(&target_operation->ctx.sha512, + &source_operation->ctx.sha512); + break; +#endif + default: + (void) source_operation; + (void) target_operation; + return PSA_ERROR_NOT_SUPPORTED; + } + + target_operation->alg = source_operation->alg; + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_hash_update( + mbedtls_psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + switch (operation->alg) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) + case PSA_ALG_MD5: + ret = mbedtls_md5_update(&operation->ctx.md5, + input, input_length); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) + case PSA_ALG_RIPEMD160: + ret = mbedtls_ripemd160_update(&operation->ctx.ripemd160, + input, input_length); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) + case PSA_ALG_SHA_1: + ret = mbedtls_sha1_update(&operation->ctx.sha1, + input, input_length); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) + case PSA_ALG_SHA_224: + ret = mbedtls_sha256_update(&operation->ctx.sha256, + input, input_length); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) + case PSA_ALG_SHA_256: + ret = mbedtls_sha256_update(&operation->ctx.sha256, + input, input_length); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) + case PSA_ALG_SHA_384: + ret = mbedtls_sha512_update(&operation->ctx.sha512, + input, input_length); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) + case PSA_ALG_SHA_512: + ret = mbedtls_sha512_update(&operation->ctx.sha512, + input, input_length); + break; +#endif + default: + (void) input; + (void) input_length; + return PSA_ERROR_BAD_STATE; + } + + return mbedtls_to_psa_error(ret); +} + +psa_status_t mbedtls_psa_hash_finish( + mbedtls_psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length) +{ + psa_status_t status; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t actual_hash_length = PSA_HASH_LENGTH(operation->alg); + + /* Fill the output buffer with something that isn't a valid hash + * (barring an attack on the hash and deliberately-crafted input), + * in case the caller doesn't check the return status properly. */ + *hash_length = hash_size; + /* If hash_size is 0 then hash may be NULL and then the + * call to memset would have undefined behavior. */ + if (hash_size != 0) { + memset(hash, '!', hash_size); + } + + if (hash_size < actual_hash_length) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + + switch (operation->alg) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5) + case PSA_ALG_MD5: + ret = mbedtls_md5_finish(&operation->ctx.md5, hash); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160) + case PSA_ALG_RIPEMD160: + ret = mbedtls_ripemd160_finish(&operation->ctx.ripemd160, hash); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1) + case PSA_ALG_SHA_1: + ret = mbedtls_sha1_finish(&operation->ctx.sha1, hash); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224) + case PSA_ALG_SHA_224: + ret = mbedtls_sha256_finish(&operation->ctx.sha256, hash); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256) + case PSA_ALG_SHA_256: + ret = mbedtls_sha256_finish(&operation->ctx.sha256, hash); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384) + case PSA_ALG_SHA_384: + ret = mbedtls_sha512_finish(&operation->ctx.sha512, hash); + break; +#endif +#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512) + case PSA_ALG_SHA_512: + ret = mbedtls_sha512_finish(&operation->ctx.sha512, hash); + break; +#endif + default: + (void) hash; + return PSA_ERROR_BAD_STATE; + } + status = mbedtls_to_psa_error(ret); + +exit: + if (status == PSA_SUCCESS) { + *hash_length = actual_hash_length; + } + return status; +} + +psa_status_t mbedtls_psa_hash_compute( + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *hash, + size_t hash_size, + size_t *hash_length) +{ + mbedtls_psa_hash_operation_t operation = MBEDTLS_PSA_HASH_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED; + + *hash_length = hash_size; + status = mbedtls_psa_hash_setup(&operation, alg); + if (status != PSA_SUCCESS) { + goto exit; + } + status = mbedtls_psa_hash_update(&operation, input, input_length); + if (status != PSA_SUCCESS) { + goto exit; + } + status = mbedtls_psa_hash_finish(&operation, hash, hash_size, hash_length); + if (status != PSA_SUCCESS) { + goto exit; + } + +exit: + abort_status = mbedtls_psa_hash_abort(&operation); + if (status == PSA_SUCCESS) { + return abort_status; + } else { + return status; + } + +} +#endif /* MBEDTLS_PSA_BUILTIN_HASH */ + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_hash.h b/r5dev/thirdparty/mbedtls/psa_crypto_hash.h new file mode 100644 index 00000000..d6bbd3fe --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_hash.h @@ -0,0 +1,225 @@ +/* + * PSA hashing layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_HASH_H +#define PSA_CRYPTO_HASH_H + +#include + +#include "md_wrap.h" + +/** Calculate the hash (digest) of a message using Mbed TLS routines. + * + * \note The signature of this function is that of a PSA driver hash_compute + * entry point. This function behaves as a hash_compute entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * \param alg The hash algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_HASH(\p alg) is true). + * \param[in] input Buffer containing the message to hash. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] hash Buffer where the hash is to be written. + * \param hash_size Size of the \p hash buffer in bytes. + * \param[out] hash_length On success, the number of bytes + * that make up the hash value. This is always + * #PSA_HASH_LENGTH(\p alg). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p hash_size is too small + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_hash_compute( + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); + +/** Set up a multipart hash operation using Mbed TLS routines. + * + * \note The signature of this function is that of a PSA driver hash_setup + * entry point. This function behaves as a hash_setup entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * If an error occurs at any step after a call to mbedtls_psa_hash_setup(), the + * operation will need to be reset by a call to mbedtls_psa_hash_abort(). The + * core may call mbedtls_psa_hash_abort() at any time after the operation + * has been initialized. + * + * After a successful call to mbedtls_psa_hash_setup(), the core must + * eventually terminate the operation. The following events terminate an + * operation: + * - A successful call to mbedtls_psa_hash_finish() or mbedtls_psa_hash_verify(). + * - A call to mbedtls_psa_hash_abort(). + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized to all-zero and not yet be in use. + * \param alg The hash algorithm to compute (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_HASH(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_hash_setup( + mbedtls_psa_hash_operation_t *operation, + psa_algorithm_t alg); + +/** Clone an Mbed TLS hash operation. + * + * \note The signature of this function is that of a PSA driver hash_clone + * entry point. This function behaves as a hash_clone entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * This function copies the state of an ongoing hash operation to + * a new operation object. In other words, this function is equivalent + * to calling mbedtls_psa_hash_setup() on \p target_operation with the same + * algorithm that \p source_operation was set up for, then + * mbedtls_psa_hash_update() on \p target_operation with the same input that + * that was passed to \p source_operation. After this function returns, the + * two objects are independent, i.e. subsequent calls involving one of + * the objects do not affect the other object. + * + * \param[in] source_operation The active hash operation to clone. + * \param[in,out] target_operation The operation object to set up. + * It must be initialized but not active. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The \p source_operation state is not valid (it must be active). + * \retval #PSA_ERROR_BAD_STATE + * The \p target_operation state is not valid (it must be inactive). + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_hash_clone( + const mbedtls_psa_hash_operation_t *source_operation, + mbedtls_psa_hash_operation_t *target_operation); + +/** Add a message fragment to a multipart Mbed TLS hash operation. + * + * \note The signature of this function is that of a PSA driver hash_update + * entry point. This function behaves as a hash_update entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * The application must call mbedtls_psa_hash_setup() before calling this function. + * + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling mbedtls_psa_hash_abort(). + * + * \param[in,out] operation Active hash operation. + * \param[in] input Buffer containing the message fragment to hash. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_hash_update( + mbedtls_psa_hash_operation_t *operation, + const uint8_t *input, + size_t input_length); + +/** Finish the calculation of the Mbed TLS-calculated hash of a message. + * + * \note The signature of this function is that of a PSA driver hash_finish + * entry point. This function behaves as a hash_finish entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * The application must call mbedtls_psa_hash_setup() before calling this function. + * This function calculates the hash of the message formed by concatenating + * the inputs passed to preceding calls to mbedtls_psa_hash_update(). + * + * When this function returns successfully, the operation becomes inactive. + * If this function returns an error status, the operation enters an error + * state and must be aborted by calling mbedtls_psa_hash_abort(). + * + * \param[in,out] operation Active hash operation. + * \param[out] hash Buffer where the hash is to be written. + * \param hash_size Size of the \p hash buffer in bytes. + * \param[out] hash_length On success, the number of bytes + * that make up the hash value. This is always + * #PSA_HASH_LENGTH(\c alg) where \c alg is the + * hash algorithm that is calculated. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active). + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p hash buffer is too small. You can determine a + * sufficient buffer size by calling #PSA_HASH_LENGTH(\c alg) + * where \c alg is the hash algorithm that is calculated. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_hash_finish( + mbedtls_psa_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); + +/** Abort an Mbed TLS hash operation. + * + * \note The signature of this function is that of a PSA driver hash_abort + * entry point. This function behaves as a hash_abort entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation by calling + * mbedtls_psa_hash_setup() again. + * + * You may call this function any time after the operation object has + * been initialized by one of the methods described in #psa_hash_operation_t. + * + * In particular, calling mbedtls_psa_hash_abort() after the operation has been + * terminated by a call to mbedtls_psa_hash_abort(), mbedtls_psa_hash_finish() or + * mbedtls_psa_hash_verify() is safe and has no effect. + * + * \param[in,out] operation Initialized hash operation. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_hash_abort( + mbedtls_psa_hash_operation_t *operation); + +#endif /* PSA_CRYPTO_HASH_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_invasive.h b/r5dev/thirdparty/mbedtls/psa_crypto_invasive.h new file mode 100644 index 00000000..a900dd8f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_invasive.h @@ -0,0 +1,82 @@ +/** + * \file psa_crypto_invasive.h + * + * \brief PSA cryptography module: invasive interfaces for test only. + * + * The interfaces in this file are intended for testing purposes only. + * They MUST NOT be made available to clients over IPC in integrations + * with isolation, and they SHOULD NOT be made available in library + * integrations except when building the library for testing. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_INVASIVE_H +#define PSA_CRYPTO_INVASIVE_H + +#include "mbedtls/build_info.h" + +#include "psa/crypto.h" +#include "common.h" + +#include "mbedtls/entropy.h" + +#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) +/** \brief Configure entropy sources. + * + * This function may only be called before a call to psa_crypto_init(), + * or after a call to mbedtls_psa_crypto_free() and before any + * subsequent call to psa_crypto_init(). + * + * This function is only intended for test purposes. The functionality + * it provides is also useful for system integrators, but + * system integrators should configure entropy drivers instead of + * breaking through to the Mbed TLS API. + * + * \param entropy_init Function to initialize the entropy context + * and set up the desired entropy sources. + * It is called by psa_crypto_init(). + * By default this is mbedtls_entropy_init(). + * This function cannot report failures directly. + * To indicate a failure, set the entropy context + * to a state where mbedtls_entropy_func() will + * return an error. + * \param entropy_free Function to free the entropy context + * and associated resources. + * It is called by mbedtls_psa_crypto_free(). + * By default this is mbedtls_entropy_free(). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_PERMITTED + * The caller does not have the permission to configure + * entropy sources. + * \retval #PSA_ERROR_BAD_STATE + * The library has already been initialized. + */ +psa_status_t mbedtls_psa_crypto_configure_entropy_sources( + void (* entropy_init)(mbedtls_entropy_context *ctx), + void (* entropy_free)(mbedtls_entropy_context *ctx)); +#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */ + +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) +psa_status_t psa_mac_key_can_do( + psa_algorithm_t algorithm, + psa_key_type_t key_type); +#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C */ + +#endif /* PSA_CRYPTO_INVASIVE_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_its.h b/r5dev/thirdparty/mbedtls/psa_crypto_its.h new file mode 100644 index 00000000..3ceee49b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_its.h @@ -0,0 +1,143 @@ +/** \file psa_crypto_its.h + * \brief Interface of trusted storage that crypto is built on. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_ITS_H +#define PSA_CRYPTO_ITS_H + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \brief Flags used when creating a data entry + */ +typedef uint32_t psa_storage_create_flags_t; + +/** \brief A type for UIDs used for identifying data + */ +typedef uint64_t psa_storage_uid_t; + +#define PSA_STORAGE_FLAG_NONE 0 /**< No flags to pass */ +#define PSA_STORAGE_FLAG_WRITE_ONCE (1 << 0) /**< The data associated with the uid will not be able to be modified or deleted. Intended to be used to set bits in `psa_storage_create_flags_t`*/ + +/** + * \brief A container for metadata associated with a specific uid + */ +struct psa_storage_info_t { + uint32_t size; /**< The size of the data associated with a uid **/ + psa_storage_create_flags_t flags; /**< The flags set when the uid was created **/ +}; + +/** Flag indicating that \ref psa_storage_create and \ref psa_storage_set_extended are supported */ +#define PSA_STORAGE_SUPPORT_SET_EXTENDED (1 << 0) + +#define PSA_ITS_API_VERSION_MAJOR 1 /**< The major version number of the PSA ITS API. It will be incremented on significant updates that may include breaking changes */ +#define PSA_ITS_API_VERSION_MINOR 1 /**< The minor version number of the PSA ITS API. It will be incremented in small updates that are unlikely to include breaking changes */ + +/** + * \brief create a new or modify an existing uid/value pair + * + * \param[in] uid the identifier for the data + * \param[in] data_length The size in bytes of the data in `p_data` + * \param[in] p_data A buffer containing the data + * \param[in] create_flags The flags that the data will be stored with + * + * \return A status indicating the success/failure of the operation + * + * \retval #PSA_SUCCESS The operation completed successfully + * \retval #PSA_ERROR_NOT_PERMITTED The operation failed because the provided `uid` value was already created with PSA_STORAGE_FLAG_WRITE_ONCE + * \retval #PSA_ERROR_NOT_SUPPORTED The operation failed because one or more of the flags provided in `create_flags` is not supported or is not valid + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE The operation failed because there was insufficient space on the storage medium + * \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error) + * \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`) + * is invalid, for example is `NULL` or references memory the caller cannot access + */ +psa_status_t psa_its_set(psa_storage_uid_t uid, + uint32_t data_length, + const void *p_data, + psa_storage_create_flags_t create_flags); + +/** + * \brief Retrieve the value associated with a provided uid + * + * \param[in] uid The uid value + * \param[in] data_offset The starting offset of the data requested + * \param[in] data_length the amount of data requested (and the minimum allocated size of the `p_data` buffer) + * \param[out] p_data The buffer where the data will be placed upon successful completion + * \param[out] p_data_length The amount of data returned in the p_data buffer + * + * + * \return A status indicating the success/failure of the operation + * + * \retval #PSA_SUCCESS The operation completed successfully + * \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided `uid` value was not found in the storage + * \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error) + * \retval #PSA_ERROR_DATA_CORRUPT The operation failed because stored data has been corrupted + * \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_data`, `p_data_length`) + * is invalid. For example is `NULL` or references memory the caller cannot access. + * In addition, this can also happen if an invalid offset was provided. + */ +psa_status_t psa_its_get(psa_storage_uid_t uid, + uint32_t data_offset, + uint32_t data_length, + void *p_data, + size_t *p_data_length); + +/** + * \brief Retrieve the metadata about the provided uid + * + * \param[in] uid The uid value + * \param[out] p_info A pointer to the `psa_storage_info_t` struct that will be populated with the metadata + * + * \return A status indicating the success/failure of the operation + * + * \retval #PSA_SUCCESS The operation completed successfully + * \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided uid value was not found in the storage + * \retval #PSA_ERROR_DATA_CORRUPT The operation failed because stored data has been corrupted + * \retval #PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the provided pointers(`p_info`) + * is invalid, for example is `NULL` or references memory the caller cannot access + */ +psa_status_t psa_its_get_info(psa_storage_uid_t uid, + struct psa_storage_info_t *p_info); + +/** + * \brief Remove the provided key and its associated data from the storage + * + * \param[in] uid The uid value + * + * \return A status indicating the success/failure of the operation + * + * \retval #PSA_SUCCESS The operation completed successfully + * \retval #PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided key value was not found in the storage + * \retval #PSA_ERROR_NOT_PERMITTED The operation failed because the provided key value was created with PSA_STORAGE_FLAG_WRITE_ONCE + * \retval #PSA_ERROR_STORAGE_FAILURE The operation failed because the physical storage has failed (Fatal error) + */ +psa_status_t psa_its_remove(psa_storage_uid_t uid); + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_CRYPTO_ITS_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_mac.c b/r5dev/thirdparty/mbedtls/psa_crypto_mac.c new file mode 100644 index 00000000..07f123ee --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_mac.c @@ -0,0 +1,507 @@ +/* + * PSA MAC layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include +#include "psa_crypto_core.h" +#include "psa_crypto_cipher.h" +#include "psa_crypto_mac.h" +#include + +#include +#include + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) +static psa_status_t psa_hmac_abort_internal( + mbedtls_psa_hmac_operation_t *hmac) +{ + mbedtls_platform_zeroize(hmac->opad, sizeof(hmac->opad)); + return psa_hash_abort(&hmac->hash_ctx); +} + +static psa_status_t psa_hmac_setup_internal( + mbedtls_psa_hmac_operation_t *hmac, + const uint8_t *key, + size_t key_length, + psa_algorithm_t hash_alg) +{ + uint8_t ipad[PSA_HMAC_MAX_HASH_BLOCK_SIZE]; + size_t i; + size_t hash_size = PSA_HASH_LENGTH(hash_alg); + size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg); + psa_status_t status; + + hmac->alg = hash_alg; + + /* Sanity checks on block_size, to guarantee that there won't be a buffer + * overflow below. This should never trigger if the hash algorithm + * is implemented correctly. */ + /* The size checks against the ipad and opad buffers cannot be written + * `block_size > sizeof( ipad ) || block_size > sizeof( hmac->opad )` + * because that triggers -Wlogical-op on GCC 7.3. */ + if (block_size > sizeof(ipad)) { + return PSA_ERROR_NOT_SUPPORTED; + } + if (block_size > sizeof(hmac->opad)) { + return PSA_ERROR_NOT_SUPPORTED; + } + if (block_size < hash_size) { + return PSA_ERROR_NOT_SUPPORTED; + } + + if (key_length > block_size) { + status = psa_hash_compute(hash_alg, key, key_length, + ipad, sizeof(ipad), &key_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + } + /* A 0-length key is not commonly used in HMAC when used as a MAC, + * but it is permitted. It is common when HMAC is used in HKDF, for + * example. Don't call `memcpy` in the 0-length because `key` could be + * an invalid pointer which would make the behavior undefined. */ + else if (key_length != 0) { + memcpy(ipad, key, key_length); + } + + /* ipad contains the key followed by garbage. Xor and fill with 0x36 + * to create the ipad value. */ + for (i = 0; i < key_length; i++) { + ipad[i] ^= 0x36; + } + memset(ipad + key_length, 0x36, block_size - key_length); + + /* Copy the key material from ipad to opad, flipping the requisite bits, + * and filling the rest of opad with the requisite constant. */ + for (i = 0; i < key_length; i++) { + hmac->opad[i] = ipad[i] ^ 0x36 ^ 0x5C; + } + memset(hmac->opad + key_length, 0x5C, block_size - key_length); + + status = psa_hash_setup(&hmac->hash_ctx, hash_alg); + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = psa_hash_update(&hmac->hash_ctx, ipad, block_size); + +cleanup: + mbedtls_platform_zeroize(ipad, sizeof(ipad)); + + return status; +} + +static psa_status_t psa_hmac_update_internal( + mbedtls_psa_hmac_operation_t *hmac, + const uint8_t *data, + size_t data_length) +{ + return psa_hash_update(&hmac->hash_ctx, data, data_length); +} + +static psa_status_t psa_hmac_finish_internal( + mbedtls_psa_hmac_operation_t *hmac, + uint8_t *mac, + size_t mac_size) +{ + uint8_t tmp[PSA_HASH_MAX_SIZE]; + psa_algorithm_t hash_alg = hmac->alg; + size_t hash_size = 0; + size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg); + psa_status_t status; + + status = psa_hash_finish(&hmac->hash_ctx, tmp, sizeof(tmp), &hash_size); + if (status != PSA_SUCCESS) { + return status; + } + /* From here on, tmp needs to be wiped. */ + + status = psa_hash_setup(&hmac->hash_ctx, hash_alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&hmac->hash_ctx, hmac->opad, block_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_update(&hmac->hash_ctx, tmp, hash_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&hmac->hash_ctx, tmp, sizeof(tmp), &hash_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + memcpy(mac, tmp, mac_size); + +exit: + mbedtls_platform_zeroize(tmp, hash_size); + return status; +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) +static psa_status_t cmac_setup(mbedtls_psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(PSA_WANT_KEY_TYPE_DES) + /* Mbed TLS CMAC does not accept 3DES with only two keys, nor does it accept + * to do CMAC with pure DES, so return NOT_SUPPORTED here. */ + if (psa_get_key_type(attributes) == PSA_KEY_TYPE_DES && + (psa_get_key_bits(attributes) == 64 || + psa_get_key_bits(attributes) == 128)) { + return PSA_ERROR_NOT_SUPPORTED; + } +#endif + + const mbedtls_cipher_info_t *cipher_info = + mbedtls_cipher_info_from_psa( + PSA_ALG_CMAC, + psa_get_key_type(attributes), + psa_get_key_bits(attributes), + NULL); + + if (cipher_info == NULL) { + return PSA_ERROR_NOT_SUPPORTED; + } + + ret = mbedtls_cipher_setup(&operation->ctx.cmac, cipher_info); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_cipher_cmac_starts(&operation->ctx.cmac, + key_buffer, + psa_get_key_bits(attributes)); +exit: + return mbedtls_to_psa_error(ret); +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) + +/* Initialize this driver's MAC operation structure. Once this function has been + * called, mbedtls_psa_mac_abort can run and will do the right thing. */ +static psa_status_t mac_init( + mbedtls_psa_mac_operation_t *operation, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + operation->alg = alg; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) + if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) { + mbedtls_cipher_init(&operation->ctx.cmac); + status = PSA_SUCCESS; + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) + if (PSA_ALG_IS_HMAC(operation->alg)) { + /* We'll set up the hash operation later in psa_hmac_setup_internal. */ + operation->ctx.hmac.alg = 0; + status = PSA_SUCCESS; + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ + { + (void) operation; + status = PSA_ERROR_NOT_SUPPORTED; + } + + if (status != PSA_SUCCESS) { + memset(operation, 0, sizeof(*operation)); + } + return status; +} + +psa_status_t mbedtls_psa_mac_abort(mbedtls_psa_mac_operation_t *operation) +{ + if (operation->alg == 0) { + /* The object has (apparently) been initialized but it is not + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + return PSA_SUCCESS; + } else +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) + if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) { + mbedtls_cipher_free(&operation->ctx.cmac); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) + if (PSA_ALG_IS_HMAC(operation->alg)) { + psa_hmac_abort_internal(&operation->ctx.hmac); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ + { + /* Sanity check (shouldn't happen: operation->alg should + * always have been initialized to a valid value). */ + goto bad_state; + } + + operation->alg = 0; + + return PSA_SUCCESS; + +bad_state: + /* If abort is called on an uninitialized object, we can't trust + * anything. Wipe the object in case it contains confidential data. + * This may result in a memory leak if a pointer gets overwritten, + * but it's too late to do anything about this. */ + memset(operation, 0, sizeof(*operation)); + return PSA_ERROR_BAD_STATE; +} + +static psa_status_t psa_mac_setup(mbedtls_psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* A context must be freshly initialized before it can be set up. */ + if (operation->alg != 0) { + return PSA_ERROR_BAD_STATE; + } + + status = mac_init(operation, alg); + if (status != PSA_SUCCESS) { + return status; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) + if (PSA_ALG_FULL_LENGTH_MAC(alg) == PSA_ALG_CMAC) { + /* Key buffer size for CMAC is dictated by the key bits set on the + * attributes, and previously validated by the core on key import. */ + (void) key_buffer_size; + status = cmac_setup(operation, attributes, key_buffer); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) + if (PSA_ALG_IS_HMAC(alg)) { + status = psa_hmac_setup_internal(&operation->ctx.hmac, + key_buffer, + key_buffer_size, + PSA_ALG_HMAC_GET_HASH(alg)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ + { + (void) attributes; + (void) key_buffer; + (void) key_buffer_size; + status = PSA_ERROR_NOT_SUPPORTED; + } + + if (status != PSA_SUCCESS) { + mbedtls_psa_mac_abort(operation); + } + + return status; +} + +psa_status_t mbedtls_psa_mac_sign_setup( + mbedtls_psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg) +{ + return psa_mac_setup(operation, attributes, + key_buffer, key_buffer_size, alg); +} + +psa_status_t mbedtls_psa_mac_verify_setup( + mbedtls_psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg) +{ + return psa_mac_setup(operation, attributes, + key_buffer, key_buffer_size, alg); +} + +psa_status_t mbedtls_psa_mac_update( + mbedtls_psa_mac_operation_t *operation, + const uint8_t *input, + size_t input_length) +{ + if (operation->alg == 0) { + return PSA_ERROR_BAD_STATE; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) + if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) { + return mbedtls_to_psa_error( + mbedtls_cipher_cmac_update(&operation->ctx.cmac, + input, input_length)); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) + if (PSA_ALG_IS_HMAC(operation->alg)) { + return psa_hmac_update_internal(&operation->ctx.hmac, + input, input_length); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ + { + /* This shouldn't happen if `operation` was initialized by + * a setup function. */ + (void) input; + (void) input_length; + return PSA_ERROR_BAD_STATE; + } +} + +static psa_status_t psa_mac_finish_internal( + mbedtls_psa_mac_operation_t *operation, + uint8_t *mac, size_t mac_size) +{ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC) + if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) { + uint8_t tmp[PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE]; + int ret = mbedtls_cipher_cmac_finish(&operation->ctx.cmac, tmp); + if (ret == 0) { + memcpy(mac, tmp, mac_size); + } + mbedtls_platform_zeroize(tmp, sizeof(tmp)); + return mbedtls_to_psa_error(ret); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) + if (PSA_ALG_IS_HMAC(operation->alg)) { + return psa_hmac_finish_internal(&operation->ctx.hmac, + mac, mac_size); + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */ + { + /* This shouldn't happen if `operation` was initialized by + * a setup function. */ + (void) operation; + (void) mac; + (void) mac_size; + return PSA_ERROR_BAD_STATE; + } +} + +psa_status_t mbedtls_psa_mac_sign_finish( + mbedtls_psa_mac_operation_t *operation, + uint8_t *mac, + size_t mac_size, + size_t *mac_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->alg == 0) { + return PSA_ERROR_BAD_STATE; + } + + status = psa_mac_finish_internal(operation, mac, mac_size); + if (status == PSA_SUCCESS) { + *mac_length = mac_size; + } + + return status; +} + +psa_status_t mbedtls_psa_mac_verify_finish( + mbedtls_psa_mac_operation_t *operation, + const uint8_t *mac, + size_t mac_length) +{ + uint8_t actual_mac[PSA_MAC_MAX_SIZE]; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (operation->alg == 0) { + return PSA_ERROR_BAD_STATE; + } + + /* Consistency check: requested MAC length fits our local buffer */ + if (mac_length > sizeof(actual_mac)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = psa_mac_finish_internal(operation, actual_mac, mac_length); + if (status != PSA_SUCCESS) { + goto cleanup; + } + + if (mbedtls_psa_safer_memcmp(mac, actual_mac, mac_length) != 0) { + status = PSA_ERROR_INVALID_SIGNATURE; + } + +cleanup: + mbedtls_platform_zeroize(actual_mac, sizeof(actual_mac)); + + return status; +} + +psa_status_t mbedtls_psa_mac_compute( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_psa_mac_operation_t operation = MBEDTLS_PSA_MAC_OPERATION_INIT; + + status = psa_mac_setup(&operation, + attributes, key_buffer, key_buffer_size, + alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (input_length > 0) { + status = mbedtls_psa_mac_update(&operation, input, input_length); + if (status != PSA_SUCCESS) { + goto exit; + } + } + + status = psa_mac_finish_internal(&operation, mac, mac_size); + if (status == PSA_SUCCESS) { + *mac_length = mac_size; + } + +exit: + mbedtls_psa_mac_abort(&operation); + + return status; +} + +#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC || MBEDTLS_PSA_BUILTIN_ALG_CMAC */ + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_mac.h b/r5dev/thirdparty/mbedtls/psa_crypto_mac.h new file mode 100644 index 00000000..4f8024a9 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_mac.h @@ -0,0 +1,276 @@ +/* + * PSA MAC layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_MAC_H +#define PSA_CRYPTO_MAC_H + +#include + +/** Calculate the MAC (message authentication code) of a message using Mbed TLS. + * + * \note The signature of this function is that of a PSA driver mac_compute + * entry point. This function behaves as a mac_compute entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key to use for + * computing the MAC. This buffer contains the key + * in export representation as defined by + * psa_export_key() (i.e. the raw key bytes). + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param alg The MAC algorithm to use (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_MAC(\p alg) is true). + * \param[in] input Buffer containing the input message. + * \param input_length Size of the \p input buffer in bytes. + * \param[out] mac Buffer where the MAC value is to be written. + * \param mac_size Size of the \p mac buffer in bytes. + * \param[out] mac_length On success, the number of bytes + * that make up the MAC value. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * \p mac_size is too small + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_mac_compute( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *mac, + size_t mac_size, + size_t *mac_length); + +/** Set up a multipart MAC calculation operation using Mbed TLS. + * + * \note The signature of this function is that of a PSA driver mac_sign_setup + * entry point. This function behaves as a mac_sign_setup entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized and not yet in use. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key to use for + * computing the MAC. This buffer contains the key + * in export representation as defined by + * psa_export_key() (i.e. the raw key bytes). + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param alg The MAC algorithm to use (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_MAC(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive). + */ +psa_status_t mbedtls_psa_mac_sign_setup( + mbedtls_psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg); + +/** Set up a multipart MAC verification operation using Mbed TLS. + * + * \note The signature of this function is that of a PSA driver mac_verify_setup + * entry point. This function behaves as a mac_verify_setup entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized and not yet in use. + * \param[in] attributes The attributes of the key to use for the + * operation. + * \param[in] key_buffer The buffer containing the key to use for + * computing the MAC. This buffer contains the key + * in export representation as defined by + * psa_export_key() (i.e. the raw key bytes). + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param alg The MAC algorithm to use (\c PSA_ALG_XXX value + * such that #PSA_ALG_IS_MAC(\p alg) is true). + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \p alg is not supported. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be inactive). + */ +psa_status_t mbedtls_psa_mac_verify_setup( + mbedtls_psa_mac_operation_t *operation, + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg); + +/** Add a message fragment to a multipart MAC operation using Mbed TLS. + * + * \note The signature of this function is that of a PSA driver mac_update + * entry point. This function behaves as a mac_update entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * The PSA core calls mbedtls_psa_mac_sign_setup() or + * mbedtls_psa_mac_verify_setup() before calling this function. + * + * If this function returns an error status, the PSA core aborts the + * operation by calling mbedtls_psa_mac_abort(). + * + * \param[in,out] operation Active MAC operation. + * \param[in] input Buffer containing the message fragment to add to + * the MAC calculation. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be active). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_mac_update( + mbedtls_psa_mac_operation_t *operation, + const uint8_t *input, + size_t input_length); + +/** Finish the calculation of the MAC of a message using Mbed TLS. + * + * \note The signature of this function is that of a PSA driver mac_sign_finish + * entry point. This function behaves as a mac_sign_finish entry point as + * defined in the PSA driver interface specification for transparent + * drivers. + * + * The PSA core calls mbedtls_psa_mac_sign_setup() before calling this function. + * This function calculates the MAC of the message formed by concatenating + * the inputs passed to preceding calls to mbedtls_psa_mac_update(). + * + * Whether this function returns successfully or not, the PSA core subsequently + * aborts the operation by calling mbedtls_psa_mac_abort(). + * + * \param[in,out] operation Active MAC operation. + * \param[out] mac Buffer where the MAC value is to be written. + * \param mac_size Output size requested for the MAC algorithm. The PSA + * core guarantees this is a valid MAC length for the + * algorithm and key combination passed to + * mbedtls_psa_mac_sign_setup(). It also guarantees the + * \p mac buffer is large enough to contain the + * requested output size. + * \param[out] mac_length On success, the number of bytes output to buffer + * \p mac, which will be equal to the requested length + * \p mac_size. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active mac sign + * operation). + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p mac buffer is too small. A sufficient buffer size + * can be determined by calling PSA_MAC_LENGTH(). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_mac_sign_finish( + mbedtls_psa_mac_operation_t *operation, + uint8_t *mac, + size_t mac_size, + size_t *mac_length); + +/** Finish the calculation of the MAC of a message and compare it with + * an expected value using Mbed TLS. + * + * \note The signature of this function is that of a PSA driver + * mac_verify_finish entry point. This function behaves as a + * mac_verify_finish entry point as defined in the PSA driver interface + * specification for transparent drivers. + * + * The PSA core calls mbedtls_psa_mac_verify_setup() before calling this + * function. This function calculates the MAC of the message formed by + * concatenating the inputs passed to preceding calls to + * mbedtls_psa_mac_update(). It then compares the calculated MAC with the + * expected MAC passed as a parameter to this function. + * + * Whether this function returns successfully or not, the PSA core subsequently + * aborts the operation by calling mbedtls_psa_mac_abort(). + * + * \param[in,out] operation Active MAC operation. + * \param[in] mac Buffer containing the expected MAC value. + * \param mac_length Length in bytes of the expected MAC value. The PSA + * core guarantees that this length is a valid MAC + * length for the algorithm and key combination passed + * to mbedtls_psa_mac_verify_setup(). + * + * \retval #PSA_SUCCESS + * The expected MAC is identical to the actual MAC of the message. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The MAC of the message was calculated successfully, but it + * differs from the expected MAC. + * \retval #PSA_ERROR_BAD_STATE + * The operation state is not valid (it must be an active mac verify + * operation). + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_mac_verify_finish( + mbedtls_psa_mac_operation_t *operation, + const uint8_t *mac, + size_t mac_length); + +/** Abort a MAC operation using Mbed TLS. + * + * Aborting an operation frees all associated resources except for the + * \p operation structure itself. Once aborted, the operation object + * can be reused for another operation by calling + * mbedtls_psa_mac_sign_setup() or mbedtls_psa_mac_verify_setup() again. + * + * The PSA core may call this function any time after the operation object has + * been initialized by one of the methods described in + * #mbedtls_psa_mac_operation_t. + * + * In particular, calling mbedtls_psa_mac_abort() after the operation has been + * terminated by a call to mbedtls_psa_mac_abort(), + * mbedtls_psa_mac_sign_finish() or mbedtls_psa_mac_verify_finish() is safe and + * has no effect. + * + * \param[in,out] operation Initialized MAC operation. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_mac_abort( + mbedtls_psa_mac_operation_t *operation); + +#endif /* PSA_CRYPTO_MAC_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_pake.c b/r5dev/thirdparty/mbedtls/psa_crypto_pake.c new file mode 100644 index 00000000..a5371849 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_pake.c @@ -0,0 +1,559 @@ +/* + * PSA PAKE layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include +#include "psa_crypto_core.h" +#include "psa_crypto_pake.h" +#include "psa_crypto_slot_management.h" + +#include +#include + +#include +#include +#include + +/* + * State sequence: + * + * psa_pake_setup() + * | + * |-- In any order: + * | | psa_pake_set_password_key() + * | | psa_pake_set_user() + * | | psa_pake_set_peer() + * | | psa_pake_set_role() + * | + * |--- In any order: (First round input before or after first round output) + * | | + * | |------ In Order + * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF) + * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF) + * | | + * | |------ In Order: + * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF) + * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF) + * | + * |--- In any order: (Second round input before or after second round output) + * | | + * | |------ In Order + * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC) + * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF) + * | | + * | |------ In Order: + * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC) + * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF) + * | + * psa_pake_get_implicit_key() + * psa_pake_abort() + */ + +/* + * The first PAKE step shares the same sequences of the second PAKE step + * but with a second set of KEY_SHARE/ZK_PUBLIC/ZK_PROOF outputs/inputs. + * It's simpler to share the same sequences numbers of the first + * set of KEY_SHARE/ZK_PUBLIC/ZK_PROOF outputs/inputs in both PAKE steps. + * + * State sequence with step, state & sequence enums: + * => Input & Output Step = PSA_PAKE_STEP_INVALID + * => state = PSA_PAKE_STATE_INVALID + * psa_pake_setup() + * => Input & Output Step = PSA_PAKE_STEP_X1_X2 + * => state = PSA_PAKE_STATE_SETUP + * => sequence = PSA_PAKE_SEQ_INVALID + * | + * |--- In any order: (First round input before or after first round output) + * | | First call of psa_pake_output() or psa_pake_input() sets + * | | state = PSA_PAKE_STATE_READY + * | | + * | |------ In Order: => state = PSA_PAKE_OUTPUT_X1_X2 + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | psa_pake_output() => sequence = PSA_PAKE_X2_STEP_KEY_SHARE + * | | | psa_pake_output() => sequence = PSA_PAKE_X2_STEP_ZK_PUBLIC + * | | | psa_pake_output() => sequence = PSA_PAKE_X2_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_STEP_X2S + * | | + * | |------ In Order: => state = PSA_PAKE_INPUT_X1_X2 + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | psa_pake_input() => sequence = PSA_PAKE_X2_STEP_KEY_SHARE + * | | | psa_pake_input() => sequence = PSA_PAKE_X2_STEP_ZK_PUBLIC + * | | | psa_pake_input() => sequence = PSA_PAKE_X2_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_INPUT_X4S + * | + * |--- In any order: (Second round input before or after second round output) + * | | + * | |------ In Order: => state = PSA_PAKE_OUTPUT_X2S + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_output() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_STEP_DERIVE + * | | + * | |------ In Order: => state = PSA_PAKE_INPUT_X4S + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_KEY_SHARE + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PUBLIC + * | | | psa_pake_input() => sequence = PSA_PAKE_X1_STEP_ZK_PROOF + * | | | => state = PSA_PAKE_STATE_READY + * | | | => sequence = PSA_PAKE_SEQ_INVALID + * | | | => Output Step = PSA_PAKE_STEP_DERIVE + * | + * psa_pake_get_implicit_key() + * => Input & Output Step = PSA_PAKE_STEP_INVALID + */ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) +static psa_status_t mbedtls_ecjpake_to_psa_error(int ret) +{ + switch (ret) { + case MBEDTLS_ERR_MPI_BAD_INPUT_DATA: + case MBEDTLS_ERR_ECP_BAD_INPUT_DATA: + case MBEDTLS_ERR_ECP_INVALID_KEY: + case MBEDTLS_ERR_ECP_VERIFY_FAILED: + return PSA_ERROR_DATA_INVALID; + case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL: + case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL: + return PSA_ERROR_BUFFER_TOO_SMALL; + case MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE: + return PSA_ERROR_NOT_SUPPORTED; + case MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED: + return PSA_ERROR_CORRUPTION_DETECTED; + default: + return PSA_ERROR_GENERIC_ERROR; + } +} +#endif + +#if defined(MBEDTLS_PSA_BUILTIN_PAKE) +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) +static psa_status_t psa_pake_ecjpake_setup(mbedtls_psa_pake_operation_t *operation) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ecjpake_role role = (operation->role == PSA_PAKE_ROLE_CLIENT) ? + MBEDTLS_ECJPAKE_CLIENT : MBEDTLS_ECJPAKE_SERVER; + + mbedtls_ecjpake_init(&operation->ctx.jpake); + + ret = mbedtls_ecjpake_setup(&operation->ctx.jpake, + role, + MBEDTLS_MD_SHA256, + MBEDTLS_ECP_DP_SECP256R1, + operation->password, + operation->password_len); + + mbedtls_platform_zeroize(operation->password, operation->password_len); + + if (ret != 0) { + return mbedtls_ecjpake_to_psa_error(ret); + } + + return PSA_SUCCESS; +} +#endif + +psa_status_t mbedtls_psa_pake_setup(mbedtls_psa_pake_operation_t *operation, + const psa_crypto_driver_pake_inputs_t *inputs) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t password_len = 0; + psa_pake_role_t role = PSA_PAKE_ROLE_NONE; + psa_pake_cipher_suite_t cipher_suite = psa_pake_cipher_suite_init(); + size_t actual_password_len = 0; + + status = psa_crypto_driver_pake_get_password_len(inputs, &password_len); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_crypto_driver_pake_get_role(inputs, &role); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_crypto_driver_pake_get_cipher_suite(inputs, &cipher_suite); + if (status != PSA_SUCCESS) { + return status; + } + + operation->password = mbedtls_calloc(1, password_len); + if (operation->password == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + status = psa_crypto_driver_pake_get_password(inputs, operation->password, + password_len, &actual_password_len); + if (status != PSA_SUCCESS) { + goto error; + } + + operation->password_len = actual_password_len; + operation->alg = cipher_suite.algorithm; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + if (cipher_suite.algorithm == PSA_ALG_JPAKE) { + if (cipher_suite.type != PSA_PAKE_PRIMITIVE_TYPE_ECC || + cipher_suite.family != PSA_ECC_FAMILY_SECP_R1 || + cipher_suite.bits != 256 || + cipher_suite.hash != PSA_ALG_SHA_256) { + status = PSA_ERROR_NOT_SUPPORTED; + goto error; + } + + operation->role = role; + + operation->buffer_length = 0; + operation->buffer_offset = 0; + + status = psa_pake_ecjpake_setup(operation); + if (status != PSA_SUCCESS) { + goto error; + } + + return PSA_SUCCESS; + } else +#else + (void) operation; + (void) inputs; +#endif + { status = PSA_ERROR_NOT_SUPPORTED; } + +error: + /* In case of failure of the setup of a multipart operation, the PSA driver interface + * specifies that the core does not call any other driver entry point thus does not + * call mbedtls_psa_pake_abort(). Therefore call it here to do the needed clean + * up like freeing the memory that may have been allocated to store the password. + */ + mbedtls_psa_pake_abort(operation); + return status; +} + +static psa_status_t mbedtls_psa_pake_output_internal( + mbedtls_psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t length; + (void) step; // Unused parameter + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + /* + * The PSA CRYPTO PAKE and MbedTLS JPAKE API have a different + * handling of output sequencing. + * + * The MbedTLS JPAKE API outputs the whole X1+X2 and X2S steps data + * at once, on the other side the PSA CRYPTO PAKE api requires + * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X2S to be + * retrieved in sequence. + * + * In order to achieve API compatibility, the whole X1+X2 or X2S steps + * data is stored in an intermediate buffer at first step output call, + * and data is sliced down by parsing the ECPoint records in order + * to return the right parts on each step. + */ + if (operation->alg == PSA_ALG_JPAKE) { + /* Initialize & write round on KEY_SHARE sequences */ + if (step == PSA_JPAKE_X1_STEP_KEY_SHARE) { + ret = mbedtls_ecjpake_write_round_one(&operation->ctx.jpake, + operation->buffer, + sizeof(operation->buffer), + &operation->buffer_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE); + if (ret != 0) { + return mbedtls_ecjpake_to_psa_error(ret); + } + + operation->buffer_offset = 0; + } else if (step == PSA_JPAKE_X2S_STEP_KEY_SHARE) { + ret = mbedtls_ecjpake_write_round_two(&operation->ctx.jpake, + operation->buffer, + sizeof(operation->buffer), + &operation->buffer_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE); + if (ret != 0) { + return mbedtls_ecjpake_to_psa_error(ret); + } + + operation->buffer_offset = 0; + } + + /* + * mbedtls_ecjpake_write_round_xxx() outputs thing in the format + * defined by draft-cragie-tls-ecjpake-01 section 7. The summary is + * that the data for each step is prepended with a length byte, and + * then they're concatenated. Additionally, the server's second round + * output is prepended with a 3-bytes ECParameters structure. + * + * In PSA, we output each step separately, and don't prepend the + * output with a length byte, even less a curve identifier, as that + * information is already available. + */ + if (step == PSA_JPAKE_X2S_STEP_KEY_SHARE && + operation->role == PSA_PAKE_ROLE_SERVER) { + /* Skip ECParameters, with is 3 bytes (RFC 8422) */ + operation->buffer_offset += 3; + } + + /* Read the length byte then move past it to the data */ + length = operation->buffer[operation->buffer_offset]; + operation->buffer_offset += 1; + + if (operation->buffer_offset + length > operation->buffer_length) { + return PSA_ERROR_DATA_CORRUPT; + } + + if (output_size < length) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + memcpy(output, + operation->buffer + operation->buffer_offset, + length); + *output_length = length; + + operation->buffer_offset += length; + + /* Reset buffer after ZK_PROOF sequence */ + if ((step == PSA_JPAKE_X2_STEP_ZK_PROOF) || + (step == PSA_JPAKE_X2S_STEP_ZK_PROOF)) { + mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer)); + operation->buffer_length = 0; + operation->buffer_offset = 0; + } + + return PSA_SUCCESS; + } else +#else + (void) step; + (void) output; + (void) output_size; + (void) output_length; +#endif + { return PSA_ERROR_NOT_SUPPORTED; } +} + +psa_status_t mbedtls_psa_pake_output(mbedtls_psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = mbedtls_psa_pake_output_internal( + operation, step, output, output_size, output_length); + + return status; +} + +static psa_status_t mbedtls_psa_pake_input_internal( + mbedtls_psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + const uint8_t *input, + size_t input_length) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + (void) step; // Unused parameter + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + /* + * The PSA CRYPTO PAKE and MbedTLS JPAKE API have a different + * handling of input sequencing. + * + * The MbedTLS JPAKE API takes the whole X1+X2 or X4S steps data + * at once as input, on the other side the PSA CRYPTO PAKE api requires + * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X4S to be + * given in sequence. + * + * In order to achieve API compatibility, each X1+X2 or X4S step data + * is stored sequentially in an intermediate buffer and given to the + * MbedTLS JPAKE API on the last step. + * + * This causes any input error to be only detected on the last step. + */ + if (operation->alg == PSA_ALG_JPAKE) { + /* + * Copy input to local buffer and format it as the Mbed TLS API + * expects, i.e. as defined by draft-cragie-tls-ecjpake-01 section 7. + * The summary is that the data for each step is prepended with a + * length byte, and then they're concatenated. Additionally, the + * server's second round output is prepended with a 3-bytes + * ECParameters structure - which means we have to prepend that when + * we're a client. + */ + if (step == PSA_JPAKE_X4S_STEP_KEY_SHARE && + operation->role == PSA_PAKE_ROLE_CLIENT) { + /* We only support secp256r1. */ + /* This is the ECParameters structure defined by RFC 8422. */ + unsigned char ecparameters[3] = { + 3, /* named_curve */ + 0, 23 /* secp256r1 */ + }; + + if (operation->buffer_length + sizeof(ecparameters) > + sizeof(operation->buffer)) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + memcpy(operation->buffer + operation->buffer_length, + ecparameters, sizeof(ecparameters)); + operation->buffer_length += sizeof(ecparameters); + } + + /* + * The core checks that input_length is smaller than + * PSA_PAKE_INPUT_MAX_SIZE. + * Thus no risk of integer overflow here. + */ + if (operation->buffer_length + input_length + 1 > sizeof(operation->buffer)) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + /* Write the length byte */ + operation->buffer[operation->buffer_length] = (uint8_t) input_length; + operation->buffer_length += 1; + + /* Finally copy the data */ + memcpy(operation->buffer + operation->buffer_length, + input, input_length); + operation->buffer_length += input_length; + + /* Load buffer at each last round ZK_PROOF */ + if (step == PSA_JPAKE_X2_STEP_ZK_PROOF) { + ret = mbedtls_ecjpake_read_round_one(&operation->ctx.jpake, + operation->buffer, + operation->buffer_length); + + mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer)); + operation->buffer_length = 0; + + if (ret != 0) { + return mbedtls_ecjpake_to_psa_error(ret); + } + } else if (step == PSA_JPAKE_X4S_STEP_ZK_PROOF) { + ret = mbedtls_ecjpake_read_round_two(&operation->ctx.jpake, + operation->buffer, + operation->buffer_length); + + mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer)); + operation->buffer_length = 0; + + if (ret != 0) { + return mbedtls_ecjpake_to_psa_error(ret); + } + } + + return PSA_SUCCESS; + } else +#else + (void) step; + (void) input; + (void) input_length; +#endif + { return PSA_ERROR_NOT_SUPPORTED; } +} + +psa_status_t mbedtls_psa_pake_input(mbedtls_psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + const uint8_t *input, + size_t input_length) +{ + psa_status_t status = mbedtls_psa_pake_input_internal( + operation, step, input, input_length); + + return status; +} + +psa_status_t mbedtls_psa_pake_get_implicit_key( + mbedtls_psa_pake_operation_t *operation, + uint8_t *output, size_t output_size, + size_t *output_length) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + if (operation->alg == PSA_ALG_JPAKE) { + ret = mbedtls_ecjpake_write_shared_key(&operation->ctx.jpake, + output, + output_size, + output_length, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE); + if (ret != 0) { + return mbedtls_ecjpake_to_psa_error(ret); + } + + return PSA_SUCCESS; + } else +#else + (void) output; +#endif + { return PSA_ERROR_NOT_SUPPORTED; } +} + +psa_status_t mbedtls_psa_pake_abort(mbedtls_psa_pake_operation_t *operation) +{ + mbedtls_platform_zeroize(operation->password, operation->password_len); + mbedtls_free(operation->password); + operation->password = NULL; + operation->password_len = 0; + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE) + if (operation->alg == PSA_ALG_JPAKE) { + operation->role = PSA_PAKE_ROLE_NONE; + mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer)); + operation->buffer_length = 0; + operation->buffer_offset = 0; + mbedtls_ecjpake_free(&operation->ctx.jpake); + } +#endif + + operation->alg = PSA_ALG_NONE; + + return PSA_SUCCESS; +} + +#endif /* MBEDTLS_PSA_BUILTIN_PAKE */ + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_pake.h b/r5dev/thirdparty/mbedtls/psa_crypto_pake.h new file mode 100644 index 00000000..001c987a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_pake.h @@ -0,0 +1,171 @@ +/* + * PSA PAKE layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_PAKE_H +#define PSA_CRYPTO_PAKE_H + +#include + +/** Set the session information for a password-authenticated key exchange. + * + * \note The signature of this function is that of a PSA driver + * pake_setup entry point. This function behaves as a pake_setup + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in,out] operation The operation object to set up. It must have + * been initialized but not set up yet. + * \param[in] inputs Inputs required for PAKE operation (role, password, + * key lifetime, cipher suite) + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * The algorithm in \p cipher_suite is not a supported PAKE algorithm, + * or the PAKE primitive in \p cipher_suite is not supported or not + * compatible with the PAKE algorithm, or the hash algorithm in + * \p cipher_suite is not supported or not compatible with the PAKE + * algorithm and primitive. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_CORRUPTION_DETECTED + */ +psa_status_t mbedtls_psa_pake_setup(mbedtls_psa_pake_operation_t *operation, + const psa_crypto_driver_pake_inputs_t *inputs); + + +/** Get output for a step of a password-authenticated key exchange. + * + * \note The signature of this function is that of a PSA driver + * pake_output entry point. This function behaves as a pake_output + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in,out] operation Active PAKE operation. + * \param step The step of the algorithm for which the output is + * requested. + * \param[out] output Buffer where the output is to be written in the + * format appropriate for this driver \p step. Refer to + * the documentation of psa_crypto_driver_pake_step_t for + * more information. + * \param output_size Size of the \p output buffer in bytes. This must + * be at least #PSA_PAKE_OUTPUT_SIZE(\p alg, \p + * primitive, \p step) where \p alg and + * \p primitive are the PAKE algorithm and primitive + * in the operation's cipher suite, and \p step is + * the output step. + * + * \param[out] output_length On success, the number of bytes of the returned + * output. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * \retval #PSA_ERROR_DATA_CORRUPT + * \retval #PSA_ERROR_DATA_INVALID + */ +psa_status_t mbedtls_psa_pake_output(mbedtls_psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** Provide input for a step of a password-authenticated key exchange. + * + * \note The signature of this function is that of a PSA driver + * pake_input entry point. This function behaves as a pake_input + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \note The core checks that input_length is smaller than PSA_PAKE_INPUT_MAX_SIZE. + * + * \param[in,out] operation Active PAKE operation. + * \param step The driver step for which the input is provided. + * \param[in] input Buffer containing the input in the format + * appropriate for this \p step. Refer to the + * documentation of psa_crypto_driver_pake_step_t + * for more information. + * \param input_length Size of the \p input buffer in bytes. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The verification fails for a zero-knowledge input step. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * the \p input is not valid for the \p operation's algorithm, cipher suite + * or \p step. + * \retval #PSA_ERROR_NOT_SUPPORTED + * the \p input is not supported for the \p operation's algorithm, cipher + * suite or \p step. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * \retval #PSA_ERROR_DATA_CORRUPT + * \retval #PSA_ERROR_DATA_INVALID + */ +psa_status_t mbedtls_psa_pake_input(mbedtls_psa_pake_operation_t *operation, + psa_crypto_driver_pake_step_t step, + const uint8_t *input, + size_t input_length); + +/** Get implicitly confirmed shared secret from a PAKE. + * + * \note The signature of this function is that of a PSA driver + * pake_get_implicit_key entry point. This function behaves as a + * pake_get_implicit_key entry point as defined in the PSA driver + * interface specification for transparent drivers. + * + * \param[in,out] operation Active PAKE operation. + * \param[out] output Output buffer for implicit key. + * \param output_size Size of the output buffer in bytes. + * \param[out] output_length On success, the number of bytes of the implicit key. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Input from a PAKE is not supported by the algorithm in the \p output + * key derivation operation. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * \retval #PSA_ERROR_DATA_CORRUPT + * \retval #PSA_ERROR_DATA_INVALID + */ +psa_status_t mbedtls_psa_pake_get_implicit_key( + mbedtls_psa_pake_operation_t *operation, + uint8_t *output, size_t output_size, + size_t *output_length); + +/** Abort a PAKE operation. + * + * \note The signature of this function is that of a PSA driver + * pake_abort entry point. This function behaves as a pake_abort + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in,out] operation The operation to abort. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_CORRUPTION_DETECTED + */ +psa_status_t mbedtls_psa_pake_abort(mbedtls_psa_pake_operation_t *operation); + +#endif /* PSA_CRYPTO_PAKE_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_random_impl.h b/r5dev/thirdparty/mbedtls/psa_crypto_random_impl.h new file mode 100644 index 00000000..f1a2af11 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_random_impl.h @@ -0,0 +1,204 @@ +/** \file psa_crypto_random_impl.h + * + * \brief PSA crypto random generator implementation abstraction. + * + * The definitions here need to be consistent with the declarations + * in include/mbedtls/psa_util.h. This file contains some redundant + * declarations to increase the chance that a compiler will detect + * inconsistencies if one file is changed without updating the other, + * but not all potential inconsistencies can be enforced, so make sure + * to check the public declarations and contracts in + * include/mbedtls/psa_util.h if you modify this file. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_RANDOM_IMPL_H +#define PSA_CRYPTO_RANDOM_IMPL_H + +#include + +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + +#include +#include // only for error codes +#include + +typedef mbedtls_psa_external_random_context_t mbedtls_psa_random_context_t; + +/* Trivial wrapper around psa_generate_random(). */ +int mbedtls_psa_get_random(void *p_rng, + unsigned char *output, + size_t output_size); + +/* The PSA RNG API doesn't need any externally maintained state. */ +#define MBEDTLS_PSA_RANDOM_STATE NULL + +#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + +/* Choose a DRBG based on configuration and availability */ +#if defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE) + +#include "mbedtls/hmac_drbg.h" + +#elif defined(MBEDTLS_CTR_DRBG_C) + +#include "mbedtls/ctr_drbg.h" + +#elif defined(MBEDTLS_HMAC_DRBG_C) + +#include "mbedtls/hmac_drbg.h" +#if defined(MBEDTLS_SHA512_C) && defined(MBEDTLS_SHA256_C) +#include +#if SIZE_MAX > 0xffffffff +/* Looks like a 64-bit system, so prefer SHA-512. */ +#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA512 +#else +/* Looks like a 32-bit system, so prefer SHA-256. */ +#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA256 +#endif +#elif defined(MBEDTLS_SHA512_C) +#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA512 +#elif defined(MBEDTLS_SHA256_C) +#define MBEDTLS_PSA_HMAC_DRBG_MD_TYPE MBEDTLS_MD_SHA256 +#else +#error "No hash algorithm available for HMAC_DBRG." +#endif + +#else +#error "No DRBG module available for the psa_crypto module." +#endif + +#include "mbedtls/entropy.h" + +/** Initialize the PSA DRBG. + * + * \param p_rng Pointer to the Mbed TLS DRBG state. + */ +static inline void mbedtls_psa_drbg_init(mbedtls_psa_drbg_context_t *p_rng) +{ +#if defined(MBEDTLS_CTR_DRBG_C) + mbedtls_ctr_drbg_init(p_rng); +#elif defined(MBEDTLS_HMAC_DRBG_C) + mbedtls_hmac_drbg_init(p_rng); +#endif +} + +/** Deinitialize the PSA DRBG. + * + * \param p_rng Pointer to the Mbed TLS DRBG state. + */ +static inline void mbedtls_psa_drbg_free(mbedtls_psa_drbg_context_t *p_rng) +{ +#if defined(MBEDTLS_CTR_DRBG_C) + mbedtls_ctr_drbg_free(p_rng); +#elif defined(MBEDTLS_HMAC_DRBG_C) + mbedtls_hmac_drbg_free(p_rng); +#endif +} + +/** The type of the PSA random generator context. + * + * The random generator context is composed of an entropy context and + * a DRBG context. + */ +typedef struct { + void (* entropy_init)(mbedtls_entropy_context *ctx); + void (* entropy_free)(mbedtls_entropy_context *ctx); + mbedtls_entropy_context entropy; + mbedtls_psa_drbg_context_t drbg; +} mbedtls_psa_random_context_t; + +/* Defined in include/mbedtls/psa_util.h so that it's visible to + * application code. The declaration here is redundant, but included + * as a safety net to make it more likely that a future change that + * accidentally causes the implementation to diverge from the interface + * will be noticed. */ +/* Do not include the declaration under MSVC because it doesn't accept it + * ("error C2370: 'mbedtls_psa_get_random' : redefinition; different storage class"). + * Observed with Visual Studio 2013. A known bug apparently: + * https://stackoverflow.com/questions/8146541/duplicate-external-static-declarations-not-allowed-in-visual-studio + */ +#if !defined(_MSC_VER) +static mbedtls_f_rng_t *const mbedtls_psa_get_random; +#endif + +/** The maximum number of bytes that mbedtls_psa_get_random() is expected to + * return. + */ +#if defined(MBEDTLS_CTR_DRBG_C) +#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_CTR_DRBG_MAX_REQUEST +#elif defined(MBEDTLS_HMAC_DRBG_C) +#define MBEDTLS_PSA_RANDOM_MAX_REQUEST MBEDTLS_HMAC_DRBG_MAX_REQUEST +#endif + +/** A pointer to the PSA DRBG state. + * + * This variable is only intended to be used through the macro + * #MBEDTLS_PSA_RANDOM_STATE. + */ +/* psa_crypto.c sets this variable to a pointer to the DRBG state in the + * global PSA crypto state. */ +/* The type `mbedtls_psa_drbg_context_t` is defined in + * include/mbedtls/psa_util.h so that `mbedtls_psa_random_state` can be + * declared there and be visible to application code. */ +extern mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state; + +/** A pointer to the PSA DRBG state. + * + * This macro expands to an expression that is suitable as the \c p_rng + * parameter to pass to mbedtls_psa_get_random(). + * + * This macro exists in all configurations where the psa_crypto module is + * enabled. Its expansion depends on the configuration. + */ +#define MBEDTLS_PSA_RANDOM_STATE mbedtls_psa_random_state + +/** Seed the PSA DRBG. + * + * \param entropy An entropy context to read the seed from. + * \param custom The personalization string. + * This can be \c NULL, in which case the personalization + * string is empty regardless of the value of \p len. + * \param len The length of the personalization string. + * + * \return \c 0 on success. + * \return An Mbed TLS error code (\c MBEDTLS_ERR_xxx) on failure. + */ +static inline int mbedtls_psa_drbg_seed( + mbedtls_entropy_context *entropy, + const unsigned char *custom, size_t len) +{ +#if defined(MBEDTLS_CTR_DRBG_C) + return mbedtls_ctr_drbg_seed(MBEDTLS_PSA_RANDOM_STATE, + mbedtls_entropy_func, + entropy, + custom, len); +#elif defined(MBEDTLS_HMAC_DRBG_C) + const mbedtls_md_info_t *md_info = + mbedtls_md_info_from_type(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE); + return mbedtls_hmac_drbg_seed(MBEDTLS_PSA_RANDOM_STATE, + md_info, + mbedtls_entropy_func, + entropy, + custom, len); +#endif +} + +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ + +#endif /* PSA_CRYPTO_RANDOM_IMPL_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_rsa.c b/r5dev/thirdparty/mbedtls/psa_crypto_rsa.c new file mode 100644 index 00000000..3ff589dc --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_rsa.c @@ -0,0 +1,726 @@ +/* + * PSA RSA layer on top of Mbed TLS crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include +#include "psa/crypto_values.h" +#include "psa_crypto_core.h" +#include "psa_crypto_random_impl.h" +#include "psa_crypto_rsa.h" +#include "psa_crypto_hash.h" + +#include +#include +#include "mbedtls/platform.h" + +#include +#include +#include +#include "pk_wrap.h" +#include "hash_info.h" + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) + +/* Mbed TLS doesn't support non-byte-aligned key sizes (i.e. key sizes + * that are not a multiple of 8) well. For example, there is only + * mbedtls_rsa_get_len(), which returns a number of bytes, and no + * way to return the exact bit size of a key. + * To keep things simple, reject non-byte-aligned key sizes. */ +static psa_status_t psa_check_rsa_key_byte_aligned( + const mbedtls_rsa_context *rsa) +{ + mbedtls_mpi n; + psa_status_t status; + mbedtls_mpi_init(&n); + status = mbedtls_to_psa_error( + mbedtls_rsa_export(rsa, &n, NULL, NULL, NULL, NULL)); + if (status == PSA_SUCCESS) { + if (mbedtls_mpi_bitlen(&n) % 8 != 0) { + status = PSA_ERROR_NOT_SUPPORTED; + } + } + mbedtls_mpi_free(&n); + return status; +} + +psa_status_t mbedtls_psa_rsa_load_representation( + psa_key_type_t type, const uint8_t *data, size_t data_length, + mbedtls_rsa_context **p_rsa) +{ + psa_status_t status; + mbedtls_pk_context ctx; + size_t bits; + mbedtls_pk_init(&ctx); + + /* Parse the data. */ + if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) { + status = mbedtls_to_psa_error( + mbedtls_pk_parse_key(&ctx, data, data_length, NULL, 0, + mbedtls_psa_get_random, MBEDTLS_PSA_RANDOM_STATE)); + } else { + status = mbedtls_to_psa_error( + mbedtls_pk_parse_public_key(&ctx, data, data_length)); + } + if (status != PSA_SUCCESS) { + goto exit; + } + + /* We have something that the pkparse module recognizes. If it is a + * valid RSA key, store it. */ + if (mbedtls_pk_get_type(&ctx) != MBEDTLS_PK_RSA) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + /* The size of an RSA key doesn't have to be a multiple of 8. Mbed TLS + * supports non-byte-aligned key sizes, but not well. For example, + * mbedtls_rsa_get_len() returns the key size in bytes, not in bits. */ + bits = PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(mbedtls_pk_rsa(ctx))); + if (bits > PSA_VENDOR_RSA_MAX_KEY_BITS) { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + status = psa_check_rsa_key_byte_aligned(mbedtls_pk_rsa(ctx)); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* Copy out the pointer to the RSA context, and reset the PK context + * such that pk_free doesn't free the RSA context we just grabbed. */ + *p_rsa = mbedtls_pk_rsa(ctx); + ctx.pk_info = NULL; + +exit: + mbedtls_pk_free(&ctx); + return status; +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */ + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || \ + defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) + +psa_status_t mbedtls_psa_rsa_import_key( + const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + uint8_t *key_buffer, size_t key_buffer_size, + size_t *key_buffer_length, size_t *bits) +{ + psa_status_t status; + mbedtls_rsa_context *rsa = NULL; + + /* Parse input */ + status = mbedtls_psa_rsa_load_representation(attributes->core.type, + data, + data_length, + &rsa); + if (status != PSA_SUCCESS) { + goto exit; + } + + *bits = (psa_key_bits_t) PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(rsa)); + + /* Re-export the data to PSA export format, such that we can store export + * representation in the key slot. Export representation in case of RSA is + * the smallest representation that's allowed as input, so a straight-up + * allocation of the same size as the input buffer will be large enough. */ + status = mbedtls_psa_rsa_export_key(attributes->core.type, + rsa, + key_buffer, + key_buffer_size, + key_buffer_length); +exit: + /* Always free the RSA object */ + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); + + return status; +} + +psa_status_t mbedtls_psa_rsa_export_key(psa_key_type_t type, + mbedtls_rsa_context *rsa, + uint8_t *data, + size_t data_size, + size_t *data_length) +{ + int ret; + mbedtls_pk_context pk; + uint8_t *pos = data + data_size; + + mbedtls_pk_init(&pk); + pk.pk_info = &mbedtls_rsa_info; + pk.pk_ctx = rsa; + + /* PSA Crypto API defines the format of an RSA key as a DER-encoded + * representation of the non-encrypted PKCS#1 RSAPrivateKey for a + * private key and of the RFC3279 RSAPublicKey for a public key. */ + if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) { + ret = mbedtls_pk_write_key_der(&pk, data, data_size); + } else { + ret = mbedtls_pk_write_pubkey(&pos, data, &pk); + } + + if (ret < 0) { + /* Clean up in case pk_write failed halfway through. */ + memset(data, 0, data_size); + return mbedtls_to_psa_error(ret); + } + + /* The mbedtls_pk_xxx functions write to the end of the buffer. + * Move the data to the beginning and erase remaining data + * at the original location. */ + if (2 * (size_t) ret <= data_size) { + memcpy(data, data + data_size - ret, ret); + memset(data + data_size - ret, 0, ret); + } else if ((size_t) ret < data_size) { + memmove(data, data + data_size - ret, ret); + memset(data + ret, 0, data_size - ret); + } + + *data_length = ret; + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_rsa_export_public_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_rsa_context *rsa = NULL; + + status = mbedtls_psa_rsa_load_representation( + attributes->core.type, key_buffer, key_buffer_size, &rsa); + if (status != PSA_SUCCESS) { + return status; + } + + status = mbedtls_psa_rsa_export_key(PSA_KEY_TYPE_RSA_PUBLIC_KEY, + rsa, + data, + data_size, + data_length); + + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); + + return status; +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) || + * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */ + +#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) && \ + defined(MBEDTLS_GENPRIME) +static psa_status_t psa_rsa_read_exponent(const uint8_t *domain_parameters, + size_t domain_parameters_size, + int *exponent) +{ + size_t i; + uint32_t acc = 0; + + if (domain_parameters_size == 0) { + *exponent = 65537; + return PSA_SUCCESS; + } + + /* Mbed TLS encodes the public exponent as an int. For simplicity, only + * support values that fit in a 32-bit integer, which is larger than + * int on just about every platform anyway. */ + if (domain_parameters_size > sizeof(acc)) { + return PSA_ERROR_NOT_SUPPORTED; + } + for (i = 0; i < domain_parameters_size; i++) { + acc = (acc << 8) | domain_parameters[i]; + } + if (acc > INT_MAX) { + return PSA_ERROR_NOT_SUPPORTED; + } + *exponent = acc; + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_rsa_generate_key( + const psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length) +{ + psa_status_t status; + mbedtls_rsa_context rsa; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int exponent; + + status = psa_rsa_read_exponent(attributes->domain_parameters, + attributes->domain_parameters_size, + &exponent); + if (status != PSA_SUCCESS) { + return status; + } + + mbedtls_rsa_init(&rsa); + ret = mbedtls_rsa_gen_key(&rsa, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + (unsigned int) attributes->core.bits, + exponent); + if (ret != 0) { + return mbedtls_to_psa_error(ret); + } + + status = mbedtls_psa_rsa_export_key(attributes->core.type, + &rsa, key_buffer, key_buffer_size, + key_buffer_length); + mbedtls_rsa_free(&rsa); + + return status; +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR) + * defined(MBEDTLS_GENPRIME) */ + +/****************************************************************/ +/* Sign/verify hashes */ +/****************************************************************/ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) + +/* Decode the hash algorithm from alg and store the mbedtls encoding in + * md_alg. Verify that the hash length is acceptable. */ +static psa_status_t psa_rsa_decode_md_type(psa_algorithm_t alg, + size_t hash_length, + mbedtls_md_type_t *md_alg) +{ + psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg); + *md_alg = mbedtls_hash_info_md_from_psa(hash_alg); + + /* The Mbed TLS RSA module uses an unsigned int for hash length + * parameters. Validate that it fits so that we don't risk an + * overflow later. */ + if (hash_length > UINT_MAX) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + /* For signatures using a hash, the hash length must be correct. */ + if (alg != PSA_ALG_RSA_PKCS1V15_SIGN_RAW) { + if (*md_alg == MBEDTLS_MD_NONE) { + return PSA_ERROR_NOT_SUPPORTED; + } + if (mbedtls_hash_info_get_size(*md_alg) != hash_length) { + return PSA_ERROR_INVALID_ARGUMENT; + } + } + + return PSA_SUCCESS; +} + +psa_status_t mbedtls_psa_rsa_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_rsa_context *rsa = NULL; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_type_t md_alg; + + status = mbedtls_psa_rsa_load_representation(attributes->core.type, + key_buffer, + key_buffer_size, + &rsa); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_rsa_decode_md_type(alg, hash_length, &md_alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (signature_size < mbedtls_rsa_get_len(rsa)) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto exit; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) + if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) { + ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V15, + MBEDTLS_MD_NONE); + if (ret == 0) { + ret = mbedtls_rsa_pkcs1_sign(rsa, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + md_alg, + (unsigned int) hash_length, + hash, + signature); + } + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) + if (PSA_ALG_IS_RSA_PSS(alg)) { + ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg); + + if (ret == 0) { + ret = mbedtls_rsa_rsassa_pss_sign(rsa, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + MBEDTLS_MD_NONE, + (unsigned int) hash_length, + hash, + signature); + } + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */ + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + if (ret == 0) { + *signature_length = mbedtls_rsa_get_len(rsa); + } + status = mbedtls_to_psa_error(ret); + +exit: + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); + + return status; +} + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) +static int rsa_pss_expected_salt_len(psa_algorithm_t alg, + const mbedtls_rsa_context *rsa, + size_t hash_length) +{ + if (PSA_ALG_IS_RSA_PSS_ANY_SALT(alg)) { + return MBEDTLS_RSA_SALT_LEN_ANY; + } + /* Otherwise: standard salt length, i.e. largest possible salt length + * up to the hash length. */ + int klen = (int) mbedtls_rsa_get_len(rsa); // known to fit + int hlen = (int) hash_length; // known to fit + int room = klen - 2 - hlen; + if (room < 0) { + return 0; // there is no valid signature in this case anyway + } else if (room > hlen) { + return hlen; + } else { + return room; + } +} +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */ + +psa_status_t mbedtls_psa_rsa_verify_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_rsa_context *rsa = NULL; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_type_t md_alg; + + status = mbedtls_psa_rsa_load_representation(attributes->core.type, + key_buffer, + key_buffer_size, + &rsa); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_rsa_decode_md_type(alg, hash_length, &md_alg); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (signature_length != mbedtls_rsa_get_len(rsa)) { + status = PSA_ERROR_INVALID_SIGNATURE; + goto exit; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) + if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) { + ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V15, + MBEDTLS_MD_NONE); + if (ret == 0) { + ret = mbedtls_rsa_pkcs1_verify(rsa, + md_alg, + (unsigned int) hash_length, + hash, + signature); + } + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN */ +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) + if (PSA_ALG_IS_RSA_PSS(alg)) { + ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg); + if (ret == 0) { + int slen = rsa_pss_expected_salt_len(alg, rsa, hash_length); + ret = mbedtls_rsa_rsassa_pss_verify_ext(rsa, + md_alg, + (unsigned) hash_length, + hash, + md_alg, + slen, + signature); + } + } else +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */ + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + /* Mbed TLS distinguishes "invalid padding" from "valid padding but + * the rest of the signature is invalid". This has little use in + * practice and PSA doesn't report this distinction. */ + status = (ret == MBEDTLS_ERR_RSA_INVALID_PADDING) ? + PSA_ERROR_INVALID_SIGNATURE : + mbedtls_to_psa_error(ret); + +exit: + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); + + return status; +} + +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */ + +/****************************************************************/ +/* Asymmetric cryptography */ +/****************************************************************/ + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) +static int psa_rsa_oaep_set_padding_mode(psa_algorithm_t alg, + mbedtls_rsa_context *rsa) +{ + psa_algorithm_t hash_alg = PSA_ALG_RSA_OAEP_GET_HASH(alg); + mbedtls_md_type_t md_alg = mbedtls_hash_info_md_from_psa(hash_alg); + + return mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg); +} +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */ + +psa_status_t mbedtls_psa_asymmetric_encrypt(const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + (void) key_buffer; + (void) key_buffer_size; + (void) input; + (void) input_length; + (void) salt; + (void) salt_length; + (void) output; + (void) output_size; + (void) output_length; + + if (PSA_KEY_TYPE_IS_RSA(attributes->core.type)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) + mbedtls_rsa_context *rsa = NULL; + status = mbedtls_psa_rsa_load_representation(attributes->core.type, + key_buffer, + key_buffer_size, + &rsa); + if (status != PSA_SUCCESS) { + goto rsa_exit; + } + + if (output_size < mbedtls_rsa_get_len(rsa)) { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto rsa_exit; + } +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */ + if (alg == PSA_ALG_RSA_PKCS1V15_CRYPT) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) + status = mbedtls_to_psa_error( + mbedtls_rsa_pkcs1_encrypt(rsa, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + input_length, + input, + output)); +#else + status = PSA_ERROR_NOT_SUPPORTED; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT */ + } else + if (PSA_ALG_IS_RSA_OAEP(alg)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) + status = mbedtls_to_psa_error( + psa_rsa_oaep_set_padding_mode(alg, rsa)); + if (status != PSA_SUCCESS) { + goto rsa_exit; + } + + status = mbedtls_to_psa_error( + mbedtls_rsa_rsaes_oaep_encrypt(rsa, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + salt, salt_length, + input_length, + input, + output)); +#else + status = PSA_ERROR_NOT_SUPPORTED; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP */ + } else { + status = PSA_ERROR_INVALID_ARGUMENT; + } +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) +rsa_exit: + if (status == PSA_SUCCESS) { + *output_length = mbedtls_rsa_get_len(rsa); + } + + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */ + } else { + status = PSA_ERROR_NOT_SUPPORTED; + } + + return status; +} + +psa_status_t mbedtls_psa_asymmetric_decrypt(const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + (void) key_buffer; + (void) key_buffer_size; + (void) input; + (void) input_length; + (void) salt; + (void) salt_length; + (void) output; + (void) output_size; + (void) output_length; + + *output_length = 0; + + if (attributes->core.type == PSA_KEY_TYPE_RSA_KEY_PAIR) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) + mbedtls_rsa_context *rsa = NULL; + status = mbedtls_psa_rsa_load_representation(attributes->core.type, + key_buffer, + key_buffer_size, + &rsa); + if (status != PSA_SUCCESS) { + goto rsa_exit; + } + + if (input_length != mbedtls_rsa_get_len(rsa)) { + status = PSA_ERROR_INVALID_ARGUMENT; + goto rsa_exit; + } +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */ + + if (alg == PSA_ALG_RSA_PKCS1V15_CRYPT) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) + status = mbedtls_to_psa_error( + mbedtls_rsa_pkcs1_decrypt(rsa, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + output_length, + input, + output, + output_size)); +#else + status = PSA_ERROR_NOT_SUPPORTED; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT */ + } else + if (PSA_ALG_IS_RSA_OAEP(alg)) { +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) + status = mbedtls_to_psa_error( + psa_rsa_oaep_set_padding_mode(alg, rsa)); + if (status != PSA_SUCCESS) { + goto rsa_exit; + } + + status = mbedtls_to_psa_error( + mbedtls_rsa_rsaes_oaep_decrypt(rsa, + mbedtls_psa_get_random, + MBEDTLS_PSA_RANDOM_STATE, + salt, salt_length, + output_length, + input, + output, + output_size)); +#else + status = PSA_ERROR_NOT_SUPPORTED; +#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP */ + } else { + status = PSA_ERROR_INVALID_ARGUMENT; + } + +#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \ + defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) +rsa_exit: + mbedtls_rsa_free(rsa); + mbedtls_free(rsa); +#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || + * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */ + } else { + status = PSA_ERROR_NOT_SUPPORTED; + } + + return status; +} + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_rsa.h b/r5dev/thirdparty/mbedtls/psa_crypto_rsa.h new file mode 100644 index 00000000..bc24ef5d --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_rsa.h @@ -0,0 +1,329 @@ +/* + * PSA RSA layer on top of Mbed TLS crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_RSA_H +#define PSA_CRYPTO_RSA_H + +#include +#include + +/** Load the contents of a key buffer into an internal RSA representation + * + * \param[in] type The type of key contained in \p data. + * \param[in] data The buffer from which to load the representation. + * \param[in] data_length The size in bytes of \p data. + * \param[out] p_rsa Returns a pointer to an RSA context on success. + * The caller is responsible for freeing both the + * contents of the context and the context itself + * when done. + */ +psa_status_t mbedtls_psa_rsa_load_representation(psa_key_type_t type, + const uint8_t *data, + size_t data_length, + mbedtls_rsa_context **p_rsa); + +/** Import an RSA key in binary format. + * + * \note The signature of this function is that of a PSA driver + * import_key entry point. This function behaves as an import_key + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes for the key to import. + * \param[in] data The buffer containing the key data in import + * format. + * \param[in] data_length Size of the \p data buffer in bytes. + * \param[out] key_buffer The buffer containing the key data in output + * format. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. This + * size is greater or equal to \p data_length. + * \param[out] key_buffer_length The length of the data written in \p + * key_buffer in bytes. + * \param[out] bits The key size in number of bits. + * + * \retval #PSA_SUCCESS The RSA key was imported successfully. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * The key data is not correctly formatted. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + */ +psa_status_t mbedtls_psa_rsa_import_key( + const psa_key_attributes_t *attributes, + const uint8_t *data, size_t data_length, + uint8_t *key_buffer, size_t key_buffer_size, + size_t *key_buffer_length, size_t *bits); + +/** Export an RSA key to export representation + * + * \param[in] type The type of key (public/private) to export + * \param[in] rsa The internal RSA representation from which to export + * \param[out] data The buffer to export to + * \param[in] data_size The length of the buffer to export to + * \param[out] data_length The amount of bytes written to \p data + */ +psa_status_t mbedtls_psa_rsa_export_key(psa_key_type_t type, + mbedtls_rsa_context *rsa, + uint8_t *data, + size_t data_size, + size_t *data_length); + +/** Export a public RSA key or the public part of an RSA key pair in binary + * format. + * + * \note The signature of this function is that of a PSA driver + * export_public_key entry point. This function behaves as an + * export_public_key entry point as defined in the PSA driver interface + * specification. + * + * \param[in] attributes The attributes for the key to export. + * \param[in] key_buffer Material or context of the key to export. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[out] data Buffer where the key data is to be written. + * \param[in] data_size Size of the \p data buffer in bytes. + * \param[out] data_length On success, the number of bytes written in + * \p data. + * + * \retval #PSA_SUCCESS The RSA public key was exported successfully. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_rsa_export_public_key( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + uint8_t *data, size_t data_size, size_t *data_length); + +/** + * \brief Generate an RSA key. + * + * \note The signature of the function is that of a PSA driver generate_key + * entry point. + * + * \param[in] attributes The attributes for the RSA key to generate. + * \param[out] key_buffer Buffer where the key data is to be written. + * \param[in] key_buffer_size Size of \p key_buffer in bytes. + * \param[out] key_buffer_length On success, the number of bytes written in + * \p key_buffer. + * + * \retval #PSA_SUCCESS + * The key was successfully generated. + * \retval #PSA_ERROR_NOT_SUPPORTED + * Key length or type not supported. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of \p key_buffer is too small. + */ +psa_status_t mbedtls_psa_rsa_generate_key( + const psa_key_attributes_t *attributes, + uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length); + +/** Sign an already-calculated hash with an RSA private key. + * + * \note The signature of this function is that of a PSA driver + * sign_hash entry point. This function behaves as a sign_hash + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the RSA key to use for the + * operation. + * \param[in] key_buffer The buffer containing the RSA key context. + * format. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * an RSA key. + * \param[in] hash The hash or message to sign. + * \param[in] hash_length Size of the \p hash buffer in bytes. + * \param[out] signature Buffer where the signature is to be written. + * \param[in] signature_size Size of the \p signature buffer in bytes. + * \param[out] signature_length On success, the number of bytes + * that make up the returned signature value. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c PSA_KEY_TYPE_RSA_KEY_PAIR, \c key_bits, + * \p alg) where \c key_bits is the bit-size of the RSA key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + */ +psa_status_t mbedtls_psa_rsa_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length); + +/** + * \brief Verify the signature a hash or short message using a public RSA key. + * + * \note The signature of this function is that of a PSA driver + * verify_hash entry point. This function behaves as a verify_hash + * entry point as defined in the PSA driver interface specification for + * transparent drivers. + * + * \param[in] attributes The attributes of the RSA key to use for the + * operation. + * \param[in] key_buffer The buffer containing the RSA key context. + * format. + * \param[in] key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] alg A signature algorithm that is compatible with + * an RSA key. + * \param[in] hash The hash or message whose signature is to be + * verified. + * \param[in] hash_length Size of the \p hash buffer in bytes. + * \param[in] signature Buffer containing the signature to verify. + * \param[in] signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * The signature is valid. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed + * signature is not a valid signature. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + */ +psa_status_t mbedtls_psa_rsa_verify_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, size_t key_buffer_size, + psa_algorithm_t alg, const uint8_t *hash, size_t hash_length, + const uint8_t *signature, size_t signature_length); + +/** + * \brief Encrypt a short message with a public key. + * + * \param attributes The attributes for the key to import. + * \param key_buffer Buffer where the key data is to be written. + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param input_length Size of the \p input buffer in bytes. + * \param[in] salt A salt or label, if supported by the + * encryption algorithm. + * If the algorithm does not support a + * salt, pass \c NULL. + * If the algorithm supports an optional + * salt and you do not want to pass a salt, + * pass \c NULL. + * + * - For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is + * supported. + * \param salt_length Size of the \p salt buffer in bytes. + * If \p salt is \c NULL, pass 0. + * \param[out] output Buffer where the encrypted message is to + * be written. + * \param output_size Size of the \p output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t mbedtls_psa_asymmetric_encrypt(const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +/** + * \brief Decrypt a short message with a private key. + * + * \param attributes The attributes for the key to import. + * \param key_buffer Buffer where the key data is to be written. + * \param key_buffer_size Size of the \p key_buffer buffer in bytes. + * \param[in] input The message to decrypt. + * \param input_length Size of the \p input buffer in bytes. + * \param[in] salt A salt or label, if supported by the + * encryption algorithm. + * If the algorithm does not support a + * salt, pass \c NULL. + * If the algorithm supports an optional + * salt and you do not want to pass a salt, + * pass \c NULL. + * + * - For #PSA_ALG_RSA_PKCS1V15_CRYPT, no salt is + * supported. + * \param salt_length Size of the \p salt buffer in bytes. + * If \p salt is \c NULL, pass 0. + * \param[out] output Buffer where the decrypted message is to + * be written. + * \param output_size Size of the \c output buffer in bytes. + * \param[out] output_length On success, the number of bytes + * that make up the returned output. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p output buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_ASYMMETRIC_DECRYPT_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * \retval #PSA_ERROR_NOT_SUPPORTED \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_COMMUNICATION_FAILURE \emptydescription + * \retval #PSA_ERROR_HARDWARE_FAILURE \emptydescription + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY \emptydescription + * \retval #PSA_ERROR_INVALID_PADDING \emptydescription + * \retval #PSA_ERROR_BAD_STATE + * The library has not been previously initialized by psa_crypto_init(). + * It is implementation-dependent whether a failure to initialize + * results in this error code. + */ +psa_status_t mbedtls_psa_asymmetric_decrypt(const psa_key_attributes_t *attributes, + const uint8_t *key_buffer, + size_t key_buffer_size, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *salt, + size_t salt_length, + uint8_t *output, + size_t output_size, + size_t *output_length); + +#endif /* PSA_CRYPTO_RSA_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_se.c b/r5dev/thirdparty/mbedtls/psa_crypto_se.c new file mode 100644 index 00000000..9db3dedc --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_se.c @@ -0,0 +1,385 @@ +/* + * PSA crypto support for secure element drivers + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + +#include +#include + +#include "psa/crypto_se_driver.h" + +#include "psa_crypto_se.h" + +#if defined(MBEDTLS_PSA_ITS_FILE_C) +#include "psa_crypto_its.h" +#else /* Native ITS implementation */ +#include "psa/error.h" +#include "psa/internal_trusted_storage.h" +#endif + +#include "mbedtls/platform.h" + + + +/****************************************************************/ +/* Driver lookup */ +/****************************************************************/ + +/* This structure is identical to psa_drv_se_context_t declared in + * `crypto_se_driver.h`, except that some parts are writable here + * (non-const, or pointer to non-const). */ +typedef struct { + void *persistent_data; + size_t persistent_data_size; + uintptr_t transient_data; +} psa_drv_se_internal_context_t; + +struct psa_se_drv_table_entry_s { + psa_key_location_t location; + const psa_drv_se_t *methods; + union { + psa_drv_se_internal_context_t internal; + psa_drv_se_context_t context; + } u; +}; + +static psa_se_drv_table_entry_t driver_table[PSA_MAX_SE_DRIVERS]; + +psa_se_drv_table_entry_t *psa_get_se_driver_entry( + psa_key_lifetime_t lifetime) +{ + size_t i; + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime); + /* In the driver table, location=0 means an entry that isn't used. + * No driver has a location of 0 because it's a reserved value + * (which designates transparent keys). Make sure we never return + * a driver entry for location 0. */ + if (location == 0) { + return NULL; + } + for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) { + if (driver_table[i].location == location) { + return &driver_table[i]; + } + } + return NULL; +} + +const psa_drv_se_t *psa_get_se_driver_methods( + const psa_se_drv_table_entry_t *driver) +{ + return driver->methods; +} + +psa_drv_se_context_t *psa_get_se_driver_context( + psa_se_drv_table_entry_t *driver) +{ + return &driver->u.context; +} + +int psa_get_se_driver(psa_key_lifetime_t lifetime, + const psa_drv_se_t **p_methods, + psa_drv_se_context_t **p_drv_context) +{ + psa_se_drv_table_entry_t *driver = psa_get_se_driver_entry(lifetime); + if (p_methods != NULL) { + *p_methods = (driver ? driver->methods : NULL); + } + if (p_drv_context != NULL) { + *p_drv_context = (driver ? &driver->u.context : NULL); + } + return driver != NULL; +} + + + +/****************************************************************/ +/* Persistent data management */ +/****************************************************************/ + +static psa_status_t psa_get_se_driver_its_file_uid( + const psa_se_drv_table_entry_t *driver, + psa_storage_uid_t *uid) +{ + if (driver->location > PSA_MAX_SE_LOCATION) { + return PSA_ERROR_NOT_SUPPORTED; + } + + /* ITS file sizes are limited to 32 bits. */ + if (driver->u.internal.persistent_data_size > UINT32_MAX) { + return PSA_ERROR_NOT_SUPPORTED; + } + + /* See the documentation of PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE. */ + *uid = PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + driver->location; + return PSA_SUCCESS; +} + +psa_status_t psa_load_se_persistent_data( + const psa_se_drv_table_entry_t *driver) +{ + psa_status_t status; + psa_storage_uid_t uid; + size_t length; + + status = psa_get_se_driver_its_file_uid(driver, &uid); + if (status != PSA_SUCCESS) { + return status; + } + + /* Read the amount of persistent data that the driver requests. + * If the data in storage is larger, it is truncated. If the data + * in storage is smaller, silently keep what is already at the end + * of the output buffer. */ + /* psa_get_se_driver_its_file_uid ensures that the size_t + * persistent_data_size is in range, but compilers don't know that, + * so cast to reassure them. */ + return psa_its_get(uid, 0, + (uint32_t) driver->u.internal.persistent_data_size, + driver->u.internal.persistent_data, + &length); +} + +psa_status_t psa_save_se_persistent_data( + const psa_se_drv_table_entry_t *driver) +{ + psa_status_t status; + psa_storage_uid_t uid; + + status = psa_get_se_driver_its_file_uid(driver, &uid); + if (status != PSA_SUCCESS) { + return status; + } + + /* psa_get_se_driver_its_file_uid ensures that the size_t + * persistent_data_size is in range, but compilers don't know that, + * so cast to reassure them. */ + return psa_its_set(uid, + (uint32_t) driver->u.internal.persistent_data_size, + driver->u.internal.persistent_data, + 0); +} + +psa_status_t psa_destroy_se_persistent_data(psa_key_location_t location) +{ + psa_storage_uid_t uid; + if (location > PSA_MAX_SE_LOCATION) { + return PSA_ERROR_NOT_SUPPORTED; + } + uid = PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + location; + return psa_its_remove(uid); +} + +psa_status_t psa_find_se_slot_for_key( + const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, + psa_se_drv_table_entry_t *driver, + psa_key_slot_number_t *slot_number) +{ + psa_status_t status; + psa_key_location_t key_location = + PSA_KEY_LIFETIME_GET_LOCATION(psa_get_key_lifetime(attributes)); + + /* If the location is wrong, it's a bug in the library. */ + if (driver->location != key_location) { + return PSA_ERROR_CORRUPTION_DETECTED; + } + + /* If the driver doesn't support key creation in any way, give up now. */ + if (driver->methods->key_management == NULL) { + return PSA_ERROR_NOT_SUPPORTED; + } + + if (psa_get_key_slot_number(attributes, slot_number) == PSA_SUCCESS) { + /* The application wants to use a specific slot. Allow it if + * the driver supports it. On a system with isolation, + * the crypto service must check that the application is + * permitted to request this slot. */ + psa_drv_se_validate_slot_number_t p_validate_slot_number = + driver->methods->key_management->p_validate_slot_number; + if (p_validate_slot_number == NULL) { + return PSA_ERROR_NOT_SUPPORTED; + } + status = p_validate_slot_number(&driver->u.context, + driver->u.internal.persistent_data, + attributes, method, + *slot_number); + } else if (method == PSA_KEY_CREATION_REGISTER) { + /* The application didn't specify a slot number. This doesn't + * make sense when registering a slot. */ + return PSA_ERROR_INVALID_ARGUMENT; + } else { + /* The application didn't tell us which slot to use. Let the driver + * choose. This is the normal case. */ + psa_drv_se_allocate_key_t p_allocate = + driver->methods->key_management->p_allocate; + if (p_allocate == NULL) { + return PSA_ERROR_NOT_SUPPORTED; + } + status = p_allocate(&driver->u.context, + driver->u.internal.persistent_data, + attributes, method, + slot_number); + } + return status; +} + +psa_status_t psa_destroy_se_key(psa_se_drv_table_entry_t *driver, + psa_key_slot_number_t slot_number) +{ + psa_status_t status; + psa_status_t storage_status; + /* Normally a missing method would mean that the action is not + * supported. But psa_destroy_key() is not supposed to return + * PSA_ERROR_NOT_SUPPORTED: if you can create a key, you should + * be able to destroy it. The only use case for a driver that + * does not have a way to destroy keys at all is if the keys are + * locked in a read-only state: we can use the keys but not + * destroy them. Hence, if the driver doesn't support destroying + * keys, it's really a lack of permission. */ + if (driver->methods->key_management == NULL || + driver->methods->key_management->p_destroy == NULL) { + return PSA_ERROR_NOT_PERMITTED; + } + status = driver->methods->key_management->p_destroy( + &driver->u.context, + driver->u.internal.persistent_data, + slot_number); + storage_status = psa_save_se_persistent_data(driver); + return status == PSA_SUCCESS ? storage_status : status; +} + +psa_status_t psa_init_all_se_drivers(void) +{ + size_t i; + for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) { + psa_se_drv_table_entry_t *driver = &driver_table[i]; + if (driver->location == 0) { + continue; /* skipping unused entry */ + } + const psa_drv_se_t *methods = psa_get_se_driver_methods(driver); + if (methods->p_init != NULL) { + psa_status_t status = methods->p_init( + &driver->u.context, + driver->u.internal.persistent_data, + driver->location); + if (status != PSA_SUCCESS) { + return status; + } + status = psa_save_se_persistent_data(driver); + if (status != PSA_SUCCESS) { + return status; + } + } + } + return PSA_SUCCESS; +} + + + +/****************************************************************/ +/* Driver registration */ +/****************************************************************/ + +psa_status_t psa_register_se_driver( + psa_key_location_t location, + const psa_drv_se_t *methods) +{ + size_t i; + psa_status_t status; + + if (methods->hal_version != PSA_DRV_SE_HAL_VERSION) { + return PSA_ERROR_NOT_SUPPORTED; + } + /* Driver table entries are 0-initialized. 0 is not a valid driver + * location because it means a transparent key. */ + MBEDTLS_STATIC_ASSERT(PSA_KEY_LOCATION_LOCAL_STORAGE == 0, + "Secure element support requires 0 to mean a local key"); + + if (location == PSA_KEY_LOCATION_LOCAL_STORAGE) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (location > PSA_MAX_SE_LOCATION) { + return PSA_ERROR_NOT_SUPPORTED; + } + + for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) { + if (driver_table[i].location == 0) { + break; + } + /* Check that location isn't already in use up to the first free + * entry. Since entries are created in order and never deleted, + * there can't be a used entry after the first free entry. */ + if (driver_table[i].location == location) { + return PSA_ERROR_ALREADY_EXISTS; + } + } + if (i == PSA_MAX_SE_DRIVERS) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + driver_table[i].location = location; + driver_table[i].methods = methods; + driver_table[i].u.internal.persistent_data_size = + methods->persistent_data_size; + + if (methods->persistent_data_size != 0) { + driver_table[i].u.internal.persistent_data = + mbedtls_calloc(1, methods->persistent_data_size); + if (driver_table[i].u.internal.persistent_data == NULL) { + status = PSA_ERROR_INSUFFICIENT_MEMORY; + goto error; + } + /* Load the driver's persistent data. On first use, the persistent + * data does not exist in storage, and is initialized to + * all-bits-zero by the calloc call just above. */ + status = psa_load_se_persistent_data(&driver_table[i]); + if (status != PSA_SUCCESS && status != PSA_ERROR_DOES_NOT_EXIST) { + goto error; + } + } + + return PSA_SUCCESS; + +error: + memset(&driver_table[i], 0, sizeof(driver_table[i])); + return status; +} + +void psa_unregister_all_se_drivers(void) +{ + size_t i; + for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) { + if (driver_table[i].u.internal.persistent_data != NULL) { + mbedtls_free(driver_table[i].u.internal.persistent_data); + } + } + memset(driver_table, 0, sizeof(driver_table)); +} + + + +/****************************************************************/ +/* The end */ +/****************************************************************/ + +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_se.h b/r5dev/thirdparty/mbedtls/psa_crypto_se.h new file mode 100644 index 00000000..a1e5e092 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_se.h @@ -0,0 +1,197 @@ +/* + * PSA crypto support for secure element drivers + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_SE_H +#define PSA_CRYPTO_SE_H + +#include "mbedtls/build_info.h" + +#include "psa/crypto.h" +#include "psa/crypto_se_driver.h" + +/** The maximum location value that this implementation supports + * for a secure element. + * + * This is not a characteristic that each PSA implementation has, but a + * limitation of the current implementation due to the constraints imposed + * by storage. See #PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE. + * + * The minimum location value for a secure element is 1, like on any + * PSA implementation (0 means a transparent key). + */ +#define PSA_MAX_SE_LOCATION 255 + +/** The base of the range of ITS file identifiers for secure element + * driver persistent data. + * + * We use a slice of the implementation reserved range 0xffff0000..0xffffffff, + * specifically the range 0xfffffe00..0xfffffeff. The length of this range + * drives the value of #PSA_MAX_SE_LOCATION. The identifier 0xfffffe00 is + * actually not used since it corresponds to #PSA_KEY_LOCATION_LOCAL_STORAGE + * which doesn't have a driver. + */ +#define PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE ((psa_key_id_t) 0xfffffe00) + +/** The maximum number of registered secure element driver locations. */ +#define PSA_MAX_SE_DRIVERS 4 + +/** Unregister all secure element drivers. + * + * \warning Do not call this function while the library is in the initialized + * state. This function is only intended to be called at the end + * of mbedtls_psa_crypto_free(). + */ +void psa_unregister_all_se_drivers(void); + +/** Initialize all secure element drivers. + * + * Called from psa_crypto_init(). + */ +psa_status_t psa_init_all_se_drivers(void); + +/** A structure that describes a registered secure element driver. + * + * A secure element driver table entry contains a pointer to the + * driver's method table as well as the driver context structure. + */ +typedef struct psa_se_drv_table_entry_s psa_se_drv_table_entry_t; + +/** Return the secure element driver information for a lifetime value. + * + * \param lifetime The lifetime value to query. + * \param[out] p_methods On output, if there is a driver, + * \c *methods points to its method table. + * Otherwise \c *methods is \c NULL. + * \param[out] p_drv_context On output, if there is a driver, + * \c *drv_context points to its context + * structure. + * Otherwise \c *drv_context is \c NULL. + * + * \retval 1 + * \p lifetime corresponds to a registered driver. + * \retval 0 + * \p lifetime does not correspond to a registered driver. + */ +int psa_get_se_driver(psa_key_lifetime_t lifetime, + const psa_drv_se_t **p_methods, + psa_drv_se_context_t **p_drv_context); + +/** Return the secure element driver table entry for a lifetime value. + * + * \param lifetime The lifetime value to query. + * + * \return The driver table entry for \p lifetime, or + * \p NULL if \p lifetime does not correspond to a registered driver. + */ +psa_se_drv_table_entry_t *psa_get_se_driver_entry( + psa_key_lifetime_t lifetime); + +/** Return the method table for a secure element driver. + * + * \param[in] driver The driver table entry to access, or \c NULL. + * + * \return The driver's method table. + * \c NULL if \p driver is \c NULL. + */ +const psa_drv_se_t *psa_get_se_driver_methods( + const psa_se_drv_table_entry_t *driver); + +/** Return the context of a secure element driver. + * + * \param[in] driver The driver table entry to access, or \c NULL. + * + * \return A pointer to the driver context. + * \c NULL if \p driver is \c NULL. + */ +psa_drv_se_context_t *psa_get_se_driver_context( + psa_se_drv_table_entry_t *driver); + +/** Find a free slot for a key that is to be created. + * + * This function calls the relevant method in the driver to find a suitable + * slot for a key with the given attributes. + * + * \param[in] attributes Metadata about the key that is about to be created. + * \param[in] driver The driver table entry to query. + * \param[out] slot_number On success, a slot number that is free in this + * secure element. + */ +psa_status_t psa_find_se_slot_for_key( + const psa_key_attributes_t *attributes, + psa_key_creation_method_t method, + psa_se_drv_table_entry_t *driver, + psa_key_slot_number_t *slot_number); + +/** Destroy a key in a secure element. + * + * This function calls the relevant driver method to destroy a key + * and updates the driver's persistent data. + */ +psa_status_t psa_destroy_se_key(psa_se_drv_table_entry_t *driver, + psa_key_slot_number_t slot_number); + +/** Load the persistent data of a secure element driver. + * + * \param driver The driver table entry containing the persistent + * data to load from storage. + * + * \return #PSA_SUCCESS + * \return #PSA_ERROR_NOT_SUPPORTED + * \return #PSA_ERROR_DOES_NOT_EXIST + * \return #PSA_ERROR_STORAGE_FAILURE + * \return #PSA_ERROR_DATA_CORRUPT + * \return #PSA_ERROR_INVALID_ARGUMENT + */ +psa_status_t psa_load_se_persistent_data( + const psa_se_drv_table_entry_t *driver); + +/** Save the persistent data of a secure element driver. + * + * \param[in] driver The driver table entry containing the persistent + * data to save to storage. + * + * \return #PSA_SUCCESS + * \return #PSA_ERROR_NOT_SUPPORTED + * \return #PSA_ERROR_NOT_PERMITTED + * \return #PSA_ERROR_NOT_SUPPORTED + * \return #PSA_ERROR_INSUFFICIENT_STORAGE + * \return #PSA_ERROR_STORAGE_FAILURE + * \return #PSA_ERROR_INVALID_ARGUMENT + */ +psa_status_t psa_save_se_persistent_data( + const psa_se_drv_table_entry_t *driver); + +/** Destroy the persistent data of a secure element driver. + * + * This is currently only used for testing. + * + * \param[in] location The location identifier for the driver whose + * persistent data is to be erased. + */ +psa_status_t psa_destroy_se_persistent_data(psa_key_location_t location); + + +/** The storage representation of a key whose data is in a secure element. + */ +typedef struct { + uint8_t slot_number[sizeof(psa_key_slot_number_t)]; +} psa_se_key_data_storage_t; + +#endif /* PSA_CRYPTO_SE_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_slot_management.c b/r5dev/thirdparty/mbedtls/psa_crypto_slot_management.c new file mode 100644 index 00000000..cb7fa59c --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_slot_management.c @@ -0,0 +1,579 @@ +/* + * PSA crypto layer on top of Mbed TLS crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include "psa/crypto.h" + +#include "psa_crypto_core.h" +#include "psa_crypto_driver_wrappers.h" +#include "psa_crypto_slot_management.h" +#include "psa_crypto_storage.h" +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +#include "psa_crypto_se.h" +#endif + +#include +#include +#include "mbedtls/platform.h" + +#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(*(array))) + +typedef struct { + psa_key_slot_t key_slots[MBEDTLS_PSA_KEY_SLOT_COUNT]; + unsigned key_slots_initialized : 1; +} psa_global_data_t; + +static psa_global_data_t global_data; + +int psa_is_valid_key_id(mbedtls_svc_key_id_t key, int vendor_ok) +{ + psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key); + + if ((PSA_KEY_ID_USER_MIN <= key_id) && + (key_id <= PSA_KEY_ID_USER_MAX)) { + return 1; + } + + if (vendor_ok && + (PSA_KEY_ID_VENDOR_MIN <= key_id) && + (key_id <= PSA_KEY_ID_VENDOR_MAX)) { + return 1; + } + + return 0; +} + +/** Get the description in memory of a key given its identifier and lock it. + * + * The descriptions of volatile keys and loaded persistent keys are + * stored in key slots. This function returns a pointer to the key slot + * containing the description of a key given its identifier. + * + * The function searches the key slots containing the description of the key + * with \p key identifier. The function does only read accesses to the key + * slots. The function does not load any persistent key thus does not access + * any storage. + * + * For volatile key identifiers, only one key slot is queried as a volatile + * key with identifier key_id can only be stored in slot of index + * ( key_id - #PSA_KEY_ID_VOLATILE_MIN ). + * + * On success, the function locks the key slot. It is the responsibility of + * the caller to unlock the key slot when it does not access it anymore. + * + * \param key Key identifier to query. + * \param[out] p_slot On success, `*p_slot` contains a pointer to the + * key slot containing the description of the key + * identified by \p key. + * + * \retval #PSA_SUCCESS + * The pointer to the key slot containing the description of the key + * identified by \p key was returned. + * \retval #PSA_ERROR_INVALID_HANDLE + * \p key is not a valid key identifier. + * \retval #PSA_ERROR_DOES_NOT_EXIST + * There is no key with key identifier \p key in the key slots. + */ +static psa_status_t psa_get_and_lock_key_slot_in_memory( + mbedtls_svc_key_id_t key, psa_key_slot_t **p_slot) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key); + size_t slot_idx; + psa_key_slot_t *slot = NULL; + + if (psa_key_id_is_volatile(key_id)) { + slot = &global_data.key_slots[key_id - PSA_KEY_ID_VOLATILE_MIN]; + + /* + * Check if both the PSA key identifier key_id and the owner + * identifier of key match those of the key slot. + * + * Note that, if the key slot is not occupied, its PSA key identifier + * is equal to zero. This is an invalid value for a PSA key identifier + * and thus cannot be equal to the valid PSA key identifier key_id. + */ + status = mbedtls_svc_key_id_equal(key, slot->attr.id) ? + PSA_SUCCESS : PSA_ERROR_DOES_NOT_EXIST; + } else { + if (!psa_is_valid_key_id(key, 1)) { + return PSA_ERROR_INVALID_HANDLE; + } + + for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) { + slot = &global_data.key_slots[slot_idx]; + if (mbedtls_svc_key_id_equal(key, slot->attr.id)) { + break; + } + } + status = (slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT) ? + PSA_SUCCESS : PSA_ERROR_DOES_NOT_EXIST; + } + + if (status == PSA_SUCCESS) { + status = psa_lock_key_slot(slot); + if (status == PSA_SUCCESS) { + *p_slot = slot; + } + } + + return status; +} + +psa_status_t psa_initialize_key_slots(void) +{ + /* Nothing to do: program startup and psa_wipe_all_key_slots() both + * guarantee that the key slots are initialized to all-zero, which + * means that all the key slots are in a valid, empty state. */ + global_data.key_slots_initialized = 1; + return PSA_SUCCESS; +} + +void psa_wipe_all_key_slots(void) +{ + size_t slot_idx; + + for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) { + psa_key_slot_t *slot = &global_data.key_slots[slot_idx]; + slot->lock_count = 1; + (void) psa_wipe_key_slot(slot); + } + global_data.key_slots_initialized = 0; +} + +psa_status_t psa_get_empty_key_slot(psa_key_id_t *volatile_key_id, + psa_key_slot_t **p_slot) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t slot_idx; + psa_key_slot_t *selected_slot, *unlocked_persistent_key_slot; + + if (!global_data.key_slots_initialized) { + status = PSA_ERROR_BAD_STATE; + goto error; + } + + selected_slot = unlocked_persistent_key_slot = NULL; + for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) { + psa_key_slot_t *slot = &global_data.key_slots[slot_idx]; + if (!psa_is_key_slot_occupied(slot)) { + selected_slot = slot; + break; + } + + if ((unlocked_persistent_key_slot == NULL) && + (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) && + (!psa_is_key_slot_locked(slot))) { + unlocked_persistent_key_slot = slot; + } + } + + /* + * If there is no unused key slot and there is at least one unlocked key + * slot containing the description of a persistent key, recycle the first + * such key slot we encountered. If we later need to operate on the + * persistent key we are evicting now, we will reload its description from + * storage. + */ + if ((selected_slot == NULL) && + (unlocked_persistent_key_slot != NULL)) { + selected_slot = unlocked_persistent_key_slot; + selected_slot->lock_count = 1; + psa_wipe_key_slot(selected_slot); + } + + if (selected_slot != NULL) { + status = psa_lock_key_slot(selected_slot); + if (status != PSA_SUCCESS) { + goto error; + } + + *volatile_key_id = PSA_KEY_ID_VOLATILE_MIN + + ((psa_key_id_t) (selected_slot - global_data.key_slots)); + *p_slot = selected_slot; + + return PSA_SUCCESS; + } + status = PSA_ERROR_INSUFFICIENT_MEMORY; + +error: + *p_slot = NULL; + *volatile_key_id = 0; + + return status; +} + +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) +static psa_status_t psa_load_persistent_key_into_slot(psa_key_slot_t *slot) +{ + psa_status_t status = PSA_SUCCESS; + uint8_t *key_data = NULL; + size_t key_data_length = 0; + + status = psa_load_persistent_key(&slot->attr, + &key_data, &key_data_length); + if (status != PSA_SUCCESS) { + goto exit; + } + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + /* Special handling is required for loading keys associated with a + * dynamically registered SE interface. */ + const psa_drv_se_t *drv; + psa_drv_se_context_t *drv_context; + if (psa_get_se_driver(slot->attr.lifetime, &drv, &drv_context)) { + psa_se_key_data_storage_t *data; + + if (key_data_length != sizeof(*data)) { + status = PSA_ERROR_DATA_INVALID; + goto exit; + } + data = (psa_se_key_data_storage_t *) key_data; + status = psa_copy_key_material_into_slot( + slot, data->slot_number, sizeof(data->slot_number)); + goto exit; + } +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + + status = psa_copy_key_material_into_slot(slot, key_data, key_data_length); + +exit: + psa_free_persistent_key_data(key_data, key_data_length); + return status; +} +#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C */ + +#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) + +static psa_status_t psa_load_builtin_key_into_slot(psa_key_slot_t *slot) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_VOLATILE; + psa_drv_slot_number_t slot_number = 0; + size_t key_buffer_size = 0; + size_t key_buffer_length = 0; + + if (!psa_key_id_is_builtin( + MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id))) { + return PSA_ERROR_DOES_NOT_EXIST; + } + + /* Check the platform function to see whether this key actually exists */ + status = mbedtls_psa_platform_get_builtin_key( + slot->attr.id, &lifetime, &slot_number); + if (status != PSA_SUCCESS) { + return status; + } + + /* Set required key attributes to ensure get_builtin_key can retrieve the + * full attributes. */ + psa_set_key_id(&attributes, slot->attr.id); + psa_set_key_lifetime(&attributes, lifetime); + + /* Get the full key attributes from the driver in order to be able to + * calculate the required buffer size. */ + status = psa_driver_wrapper_get_builtin_key( + slot_number, &attributes, + NULL, 0, NULL); + if (status != PSA_ERROR_BUFFER_TOO_SMALL) { + /* Builtin keys cannot be defined by the attributes alone */ + if (status == PSA_SUCCESS) { + status = PSA_ERROR_CORRUPTION_DETECTED; + } + return status; + } + + /* If the key should exist according to the platform, then ask the driver + * what its expected size is. */ + status = psa_driver_wrapper_get_key_buffer_size(&attributes, + &key_buffer_size); + if (status != PSA_SUCCESS) { + return status; + } + + /* Allocate a buffer of the required size and load the builtin key directly + * into the (now properly sized) slot buffer. */ + status = psa_allocate_buffer_to_slot(slot, key_buffer_size); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_driver_wrapper_get_builtin_key( + slot_number, &attributes, + slot->key.data, slot->key.bytes, &key_buffer_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + /* Copy actual key length and core attributes into the slot on success */ + slot->key.bytes = key_buffer_length; + slot->attr = attributes.core; + +exit: + if (status != PSA_SUCCESS) { + psa_remove_key_data_from_memory(slot); + } + return status; +} +#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ + +psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key, + psa_key_slot_t **p_slot) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + *p_slot = NULL; + if (!global_data.key_slots_initialized) { + return PSA_ERROR_BAD_STATE; + } + + /* + * On success, the pointer to the slot is passed directly to the caller + * thus no need to unlock the key slot here. + */ + status = psa_get_and_lock_key_slot_in_memory(key, p_slot); + if (status != PSA_ERROR_DOES_NOT_EXIST) { + return status; + } + + /* Loading keys from storage requires support for such a mechanism */ +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || \ + defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) + psa_key_id_t volatile_key_id; + + status = psa_get_empty_key_slot(&volatile_key_id, p_slot); + if (status != PSA_SUCCESS) { + return status; + } + + (*p_slot)->attr.id = key; + (*p_slot)->attr.lifetime = PSA_KEY_LIFETIME_PERSISTENT; + + status = PSA_ERROR_DOES_NOT_EXIST; +#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) + /* Load keys in the 'builtin' range through their own interface */ + status = psa_load_builtin_key_into_slot(*p_slot); +#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ + +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) + if (status == PSA_ERROR_DOES_NOT_EXIST) { + status = psa_load_persistent_key_into_slot(*p_slot); + } +#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */ + + if (status != PSA_SUCCESS) { + psa_wipe_key_slot(*p_slot); + if (status == PSA_ERROR_DOES_NOT_EXIST) { + status = PSA_ERROR_INVALID_HANDLE; + } + } else { + /* Add implicit usage flags. */ + psa_extend_key_usage_flags(&(*p_slot)->attr.policy.usage); + } + + return status; +#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ + return PSA_ERROR_INVALID_HANDLE; +#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ +} + +psa_status_t psa_unlock_key_slot(psa_key_slot_t *slot) +{ + if (slot == NULL) { + return PSA_SUCCESS; + } + + if (slot->lock_count > 0) { + slot->lock_count--; + return PSA_SUCCESS; + } + + /* + * As the return error code may not be handled in case of multiple errors, + * do our best to report if the lock counter is equal to zero. Assert with + * MBEDTLS_TEST_HOOK_TEST_ASSERT that the lock counter is strictly greater + * than zero: if the MBEDTLS_TEST_HOOKS configuration option is enabled and + * the function is called as part of the execution of a test suite, the + * execution of the test suite is stopped in error if the assertion fails. + */ + MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->lock_count > 0); + return PSA_ERROR_CORRUPTION_DETECTED; +} + +psa_status_t psa_validate_key_location(psa_key_lifetime_t lifetime, + psa_se_drv_table_entry_t **p_drv) +{ + if (psa_key_lifetime_is_external(lifetime)) { +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + /* Check whether a driver is registered against this lifetime */ + psa_se_drv_table_entry_t *driver = psa_get_se_driver_entry(lifetime); + if (driver != NULL) { + if (p_drv != NULL) { + *p_drv = driver; + } + return PSA_SUCCESS; + } +#else /* MBEDTLS_PSA_CRYPTO_SE_C */ + (void) p_drv; +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ + +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) + /* Key location for external keys gets checked by the wrapper */ + return PSA_SUCCESS; +#else /* MBEDTLS_PSA_CRYPTO_DRIVERS */ + /* No support for external lifetimes at all, or dynamic interface + * did not find driver for requested lifetime. */ + return PSA_ERROR_INVALID_ARGUMENT; +#endif /* MBEDTLS_PSA_CRYPTO_DRIVERS */ + } else { + /* Local/internal keys are always valid */ + return PSA_SUCCESS; + } +} + +psa_status_t psa_validate_key_persistence(psa_key_lifetime_t lifetime) +{ + if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) { + /* Volatile keys are always supported */ + return PSA_SUCCESS; + } else { + /* Persistent keys require storage support */ +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) + if (PSA_KEY_LIFETIME_IS_READ_ONLY(lifetime)) { + return PSA_ERROR_INVALID_ARGUMENT; + } else { + return PSA_SUCCESS; + } +#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C */ + return PSA_ERROR_NOT_SUPPORTED; +#endif /* !MBEDTLS_PSA_CRYPTO_STORAGE_C */ + } +} + +psa_status_t psa_open_key(mbedtls_svc_key_id_t key, psa_key_handle_t *handle) +{ +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || \ + defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) + psa_status_t status; + psa_key_slot_t *slot; + + status = psa_get_and_lock_key_slot(key, &slot); + if (status != PSA_SUCCESS) { + *handle = PSA_KEY_HANDLE_INIT; + if (status == PSA_ERROR_INVALID_HANDLE) { + status = PSA_ERROR_DOES_NOT_EXIST; + } + + return status; + } + + *handle = key; + + return psa_unlock_key_slot(slot); + +#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ + (void) key; + *handle = PSA_KEY_HANDLE_INIT; + return PSA_ERROR_NOT_SUPPORTED; +#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ +} + +psa_status_t psa_close_key(psa_key_handle_t handle) +{ + psa_status_t status; + psa_key_slot_t *slot; + + if (psa_key_handle_is_null(handle)) { + return PSA_SUCCESS; + } + + status = psa_get_and_lock_key_slot_in_memory(handle, &slot); + if (status != PSA_SUCCESS) { + if (status == PSA_ERROR_DOES_NOT_EXIST) { + status = PSA_ERROR_INVALID_HANDLE; + } + + return status; + } + if (slot->lock_count <= 1) { + return psa_wipe_key_slot(slot); + } else { + return psa_unlock_key_slot(slot); + } +} + +psa_status_t psa_purge_key(mbedtls_svc_key_id_t key) +{ + psa_status_t status; + psa_key_slot_t *slot; + + status = psa_get_and_lock_key_slot_in_memory(key, &slot); + if (status != PSA_SUCCESS) { + return status; + } + + if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) && + (slot->lock_count <= 1)) { + return psa_wipe_key_slot(slot); + } else { + return psa_unlock_key_slot(slot); + } +} + +void mbedtls_psa_get_stats(mbedtls_psa_stats_t *stats) +{ + size_t slot_idx; + + memset(stats, 0, sizeof(*stats)); + + for (slot_idx = 0; slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT; slot_idx++) { + const psa_key_slot_t *slot = &global_data.key_slots[slot_idx]; + if (psa_is_key_slot_locked(slot)) { + ++stats->locked_slots; + } + if (!psa_is_key_slot_occupied(slot)) { + ++stats->empty_slots; + continue; + } + if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) { + ++stats->volatile_slots; + } else { + psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id); + ++stats->persistent_slots; + if (id > stats->max_open_internal_key_id) { + stats->max_open_internal_key_id = id; + } + } + if (PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime) != + PSA_KEY_LOCATION_LOCAL_STORAGE) { + psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id); + ++stats->external_slots; + if (id > stats->max_open_external_key_id) { + stats->max_open_external_key_id = id; + } + } + } +} + +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_slot_management.h b/r5dev/thirdparty/mbedtls/psa_crypto_slot_management.h new file mode 100644 index 00000000..c8366abe --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_slot_management.h @@ -0,0 +1,225 @@ +/* + * PSA crypto layer on top of Mbed TLS crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_SLOT_MANAGEMENT_H +#define PSA_CRYPTO_SLOT_MANAGEMENT_H + +#include "psa/crypto.h" +#include "psa_crypto_core.h" +#include "psa_crypto_se.h" + +/** Range of volatile key identifiers. + * + * The last #MBEDTLS_PSA_KEY_SLOT_COUNT identifiers of the implementation + * range of key identifiers are reserved for volatile key identifiers. + * A volatile key identifier is equal to #PSA_KEY_ID_VOLATILE_MIN plus the + * index of the key slot containing the volatile key definition. + */ + +/** The minimum value for a volatile key identifier. + */ +#define PSA_KEY_ID_VOLATILE_MIN (PSA_KEY_ID_VENDOR_MAX - \ + MBEDTLS_PSA_KEY_SLOT_COUNT + 1) + +/** The maximum value for a volatile key identifier. + */ +#define PSA_KEY_ID_VOLATILE_MAX PSA_KEY_ID_VENDOR_MAX + +/** Test whether a key identifier is a volatile key identifier. + * + * \param key_id Key identifier to test. + * + * \retval 1 + * The key identifier is a volatile key identifier. + * \retval 0 + * The key identifier is not a volatile key identifier. + */ +static inline int psa_key_id_is_volatile(psa_key_id_t key_id) +{ + return (key_id >= PSA_KEY_ID_VOLATILE_MIN) && + (key_id <= PSA_KEY_ID_VOLATILE_MAX); +} + +/** Get the description of a key given its identifier and lock it. + * + * The descriptions of volatile keys and loaded persistent keys are stored in + * key slots. This function returns a pointer to the key slot containing the + * description of a key given its identifier. + * + * In case of a persistent key, the function loads the description of the key + * into a key slot if not already done. + * + * On success, the returned key slot is locked. It is the responsibility of + * the caller to unlock the key slot when it does not access it anymore. + * + * \param key Key identifier to query. + * \param[out] p_slot On success, `*p_slot` contains a pointer to the + * key slot containing the description of the key + * identified by \p key. + * + * \retval #PSA_SUCCESS + * \p *p_slot contains a pointer to the key slot containing the + * description of the key identified by \p key. + * The key slot counter has been incremented. + * \retval #PSA_ERROR_BAD_STATE + * The library has not been initialized. + * \retval #PSA_ERROR_INVALID_HANDLE + * \p key is not a valid key identifier. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \p key is a persistent key identifier. The implementation does not + * have sufficient resources to load the persistent key. This can be + * due to a lack of empty key slot, or available memory. + * \retval #PSA_ERROR_DOES_NOT_EXIST + * There is no key with key identifier \p key. + * \retval #PSA_ERROR_CORRUPTION_DETECTED \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + */ +psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key, + psa_key_slot_t **p_slot); + +/** Initialize the key slot structures. + * + * \retval #PSA_SUCCESS + * Currently this function always succeeds. + */ +psa_status_t psa_initialize_key_slots(void); + +/** Delete all data from key slots in memory. + * + * This does not affect persistent storage. */ +void psa_wipe_all_key_slots(void); + +/** Find a free key slot. + * + * This function returns a key slot that is available for use and is in its + * ground state (all-bits-zero). On success, the key slot is locked. It is + * the responsibility of the caller to unlock the key slot when it does not + * access it anymore. + * + * \param[out] volatile_key_id On success, volatile key identifier + * associated to the returned slot. + * \param[out] p_slot On success, a pointer to the slot. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_BAD_STATE \emptydescription + */ +psa_status_t psa_get_empty_key_slot(psa_key_id_t *volatile_key_id, + psa_key_slot_t **p_slot); + +/** Lock a key slot. + * + * This function increments the key slot lock counter by one. + * + * \param[in] slot The key slot. + * + * \retval #PSA_SUCCESS + The key slot lock counter was incremented. + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * The lock counter already reached its maximum value and was not + * increased. + */ +static inline psa_status_t psa_lock_key_slot(psa_key_slot_t *slot) +{ + if (slot->lock_count >= SIZE_MAX) { + return PSA_ERROR_CORRUPTION_DETECTED; + } + + slot->lock_count++; + + return PSA_SUCCESS; +} + +/** Unlock a key slot. + * + * This function decrements the key slot lock counter by one. + * + * \note To ease the handling of errors in retrieving a key slot + * a NULL input pointer is valid, and the function returns + * successfully without doing anything in that case. + * + * \param[in] slot The key slot. + * \retval #PSA_SUCCESS + * \p slot is NULL or the key slot lock counter has been + * decremented successfully. + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * The lock counter was equal to 0. + * + */ +psa_status_t psa_unlock_key_slot(psa_key_slot_t *slot); + +/** Test whether a lifetime designates a key in an external cryptoprocessor. + * + * \param lifetime The lifetime to test. + * + * \retval 1 + * The lifetime designates an external key. There should be a + * registered driver for this lifetime, otherwise the key cannot + * be created or manipulated. + * \retval 0 + * The lifetime designates a key that is volatile or in internal + * storage. + */ +static inline int psa_key_lifetime_is_external(psa_key_lifetime_t lifetime) +{ + return PSA_KEY_LIFETIME_GET_LOCATION(lifetime) + != PSA_KEY_LOCATION_LOCAL_STORAGE; +} + +/** Validate a key's location. + * + * This function checks whether the key's attributes point to a location that + * is known to the PSA Core, and returns the driver function table if the key + * is to be found in an external location. + * + * \param[in] lifetime The key lifetime attribute. + * \param[out] p_drv On success, when a key is located in external + * storage, returns a pointer to the driver table + * associated with the key's storage location. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + */ +psa_status_t psa_validate_key_location(psa_key_lifetime_t lifetime, + psa_se_drv_table_entry_t **p_drv); + +/** Validate the persistence of a key. + * + * \param[in] lifetime The key lifetime attribute. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_NOT_SUPPORTED The key is persistent but persistent keys + * are not supported. + */ +psa_status_t psa_validate_key_persistence(psa_key_lifetime_t lifetime); + +/** Validate a key identifier. + * + * \param[in] key The key identifier. + * \param[in] vendor_ok Non-zero to indicate that key identifiers in the + * vendor range are allowed, volatile key identifiers + * excepted \c 0 otherwise. + * + * \retval <> 0 if the key identifier is valid, 0 otherwise. + */ +int psa_is_valid_key_id(mbedtls_svc_key_id_t key, int vendor_ok); + +#endif /* PSA_CRYPTO_SLOT_MANAGEMENT_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_storage.c b/r5dev/thirdparty/mbedtls/psa_crypto_storage.c new file mode 100644 index 00000000..a8ed9375 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_storage.c @@ -0,0 +1,498 @@ +/* + * PSA persistent key storage + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) + +#include +#include + +#include "psa/crypto.h" +#include "psa_crypto_storage.h" +#include "mbedtls/platform_util.h" + +#if defined(MBEDTLS_PSA_ITS_FILE_C) +#include "psa_crypto_its.h" +#else /* Native ITS implementation */ +#include "psa/error.h" +#include "psa/internal_trusted_storage.h" +#endif + +#include "mbedtls/platform.h" + + + +/****************************************************************/ +/* Key storage */ +/****************************************************************/ + +/* Determine a file name (ITS file identifier) for the given key identifier. + * The file name must be distinct from any file that is used for a purpose + * other than storing a key. Currently, the only such file is the random seed + * file whose name is PSA_CRYPTO_ITS_RANDOM_SEED_UID and whose value is + * 0xFFFFFF52. */ +static psa_storage_uid_t psa_its_identifier_of_slot(mbedtls_svc_key_id_t key) +{ +#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) + /* Encode the owner in the upper 32 bits. This means that if + * owner values are nonzero (as they are on a PSA platform), + * no key file will ever have a value less than 0x100000000, so + * the whole range 0..0xffffffff is available for non-key files. */ + uint32_t unsigned_owner_id = MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(key); + return ((uint64_t) unsigned_owner_id << 32) | + MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key); +#else + /* Use the key id directly as a file name. + * psa_is_key_id_valid() in psa_crypto_slot_management.c + * is responsible for ensuring that key identifiers do not have a + * value that is reserved for non-key files. */ + return key; +#endif +} + +/** + * \brief Load persistent data for the given key slot number. + * + * This function reads data from a storage backend and returns the data in a + * buffer. + * + * \param key Persistent identifier of the key to be loaded. This + * should be an occupied storage location. + * \param[out] data Buffer where the data is to be written. + * \param data_size Size of the \c data buffer in bytes. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription + */ +static psa_status_t psa_crypto_storage_load( + const mbedtls_svc_key_id_t key, uint8_t *data, size_t data_size) +{ + psa_status_t status; + psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key); + struct psa_storage_info_t data_identifier_info; + size_t data_length = 0; + + status = psa_its_get_info(data_identifier, &data_identifier_info); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_its_get(data_identifier, 0, (uint32_t) data_size, data, &data_length); + if (data_size != data_length) { + return PSA_ERROR_DATA_INVALID; + } + + return status; +} + +int psa_is_key_present_in_storage(const mbedtls_svc_key_id_t key) +{ + psa_status_t ret; + psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key); + struct psa_storage_info_t data_identifier_info; + + ret = psa_its_get_info(data_identifier, &data_identifier_info); + + if (ret == PSA_ERROR_DOES_NOT_EXIST) { + return 0; + } + return 1; +} + +/** + * \brief Store persistent data for the given key slot number. + * + * This function stores the given data buffer to a persistent storage. + * + * \param key Persistent identifier of the key to be stored. This + * should be an unoccupied storage location. + * \param[in] data Buffer containing the data to be stored. + * \param data_length The number of bytes + * that make up the data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + */ +static psa_status_t psa_crypto_storage_store(const mbedtls_svc_key_id_t key, + const uint8_t *data, + size_t data_length) +{ + psa_status_t status; + psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key); + struct psa_storage_info_t data_identifier_info; + + if (psa_is_key_present_in_storage(key) == 1) { + return PSA_ERROR_ALREADY_EXISTS; + } + + status = psa_its_set(data_identifier, (uint32_t) data_length, data, 0); + if (status != PSA_SUCCESS) { + return PSA_ERROR_DATA_INVALID; + } + + status = psa_its_get_info(data_identifier, &data_identifier_info); + if (status != PSA_SUCCESS) { + goto exit; + } + + if (data_identifier_info.size != data_length) { + status = PSA_ERROR_DATA_INVALID; + goto exit; + } + +exit: + if (status != PSA_SUCCESS) { + /* Remove the file in case we managed to create it but something + * went wrong. It's ok if the file doesn't exist. If the file exists + * but the removal fails, we're already reporting an error so there's + * nothing else we can do. */ + (void) psa_its_remove(data_identifier); + } + return status; +} + +psa_status_t psa_destroy_persistent_key(const mbedtls_svc_key_id_t key) +{ + psa_status_t ret; + psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key); + struct psa_storage_info_t data_identifier_info; + + ret = psa_its_get_info(data_identifier, &data_identifier_info); + if (ret == PSA_ERROR_DOES_NOT_EXIST) { + return PSA_SUCCESS; + } + + if (psa_its_remove(data_identifier) != PSA_SUCCESS) { + return PSA_ERROR_DATA_INVALID; + } + + ret = psa_its_get_info(data_identifier, &data_identifier_info); + if (ret != PSA_ERROR_DOES_NOT_EXIST) { + return PSA_ERROR_DATA_INVALID; + } + + return PSA_SUCCESS; +} + +/** + * \brief Get data length for given key slot number. + * + * \param key Persistent identifier whose stored data length + * is to be obtained. + * \param[out] data_length The number of bytes that make up the data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + */ +static psa_status_t psa_crypto_storage_get_data_length( + const mbedtls_svc_key_id_t key, + size_t *data_length) +{ + psa_status_t status; + psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key); + struct psa_storage_info_t data_identifier_info; + + status = psa_its_get_info(data_identifier, &data_identifier_info); + if (status != PSA_SUCCESS) { + return status; + } + + *data_length = (size_t) data_identifier_info.size; + + return PSA_SUCCESS; +} + +/** + * Persistent key storage magic header. + */ +#define PSA_KEY_STORAGE_MAGIC_HEADER "PSA\0KEY" +#define PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH (sizeof(PSA_KEY_STORAGE_MAGIC_HEADER)) + +typedef struct { + uint8_t magic[PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH]; + uint8_t version[4]; + uint8_t lifetime[sizeof(psa_key_lifetime_t)]; + uint8_t type[2]; + uint8_t bits[2]; + uint8_t policy[sizeof(psa_key_policy_t)]; + uint8_t data_len[4]; + uint8_t key_data[]; +} psa_persistent_key_storage_format; + +void psa_format_key_data_for_storage(const uint8_t *data, + const size_t data_length, + const psa_core_key_attributes_t *attr, + uint8_t *storage_data) +{ + psa_persistent_key_storage_format *storage_format = + (psa_persistent_key_storage_format *) storage_data; + + memcpy(storage_format->magic, PSA_KEY_STORAGE_MAGIC_HEADER, + PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH); + MBEDTLS_PUT_UINT32_LE(0, storage_format->version, 0); + MBEDTLS_PUT_UINT32_LE(attr->lifetime, storage_format->lifetime, 0); + MBEDTLS_PUT_UINT16_LE((uint16_t) attr->type, storage_format->type, 0); + MBEDTLS_PUT_UINT16_LE((uint16_t) attr->bits, storage_format->bits, 0); + MBEDTLS_PUT_UINT32_LE(attr->policy.usage, storage_format->policy, 0); + MBEDTLS_PUT_UINT32_LE(attr->policy.alg, storage_format->policy, sizeof(uint32_t)); + MBEDTLS_PUT_UINT32_LE(attr->policy.alg2, storage_format->policy, 2 * sizeof(uint32_t)); + MBEDTLS_PUT_UINT32_LE(data_length, storage_format->data_len, 0); + memcpy(storage_format->key_data, data, data_length); +} + +static psa_status_t check_magic_header(const uint8_t *data) +{ + if (memcmp(data, PSA_KEY_STORAGE_MAGIC_HEADER, + PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH) != 0) { + return PSA_ERROR_DATA_INVALID; + } + return PSA_SUCCESS; +} + +psa_status_t psa_parse_key_data_from_storage(const uint8_t *storage_data, + size_t storage_data_length, + uint8_t **key_data, + size_t *key_data_length, + psa_core_key_attributes_t *attr) +{ + psa_status_t status; + const psa_persistent_key_storage_format *storage_format = + (const psa_persistent_key_storage_format *) storage_data; + uint32_t version; + + if (storage_data_length < sizeof(*storage_format)) { + return PSA_ERROR_DATA_INVALID; + } + + status = check_magic_header(storage_data); + if (status != PSA_SUCCESS) { + return status; + } + + version = MBEDTLS_GET_UINT32_LE(storage_format->version, 0); + if (version != 0) { + return PSA_ERROR_DATA_INVALID; + } + + *key_data_length = MBEDTLS_GET_UINT32_LE(storage_format->data_len, 0); + if (*key_data_length > (storage_data_length - sizeof(*storage_format)) || + *key_data_length > PSA_CRYPTO_MAX_STORAGE_SIZE) { + return PSA_ERROR_DATA_INVALID; + } + + if (*key_data_length == 0) { + *key_data = NULL; + } else { + *key_data = mbedtls_calloc(1, *key_data_length); + if (*key_data == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + memcpy(*key_data, storage_format->key_data, *key_data_length); + } + + attr->lifetime = MBEDTLS_GET_UINT32_LE(storage_format->lifetime, 0); + attr->type = MBEDTLS_GET_UINT16_LE(storage_format->type, 0); + attr->bits = MBEDTLS_GET_UINT16_LE(storage_format->bits, 0); + attr->policy.usage = MBEDTLS_GET_UINT32_LE(storage_format->policy, 0); + attr->policy.alg = MBEDTLS_GET_UINT32_LE(storage_format->policy, sizeof(uint32_t)); + attr->policy.alg2 = MBEDTLS_GET_UINT32_LE(storage_format->policy, 2 * sizeof(uint32_t)); + + return PSA_SUCCESS; +} + +psa_status_t psa_save_persistent_key(const psa_core_key_attributes_t *attr, + const uint8_t *data, + const size_t data_length) +{ + size_t storage_data_length; + uint8_t *storage_data; + psa_status_t status; + + /* All keys saved to persistent storage always have a key context */ + if (data == NULL || data_length == 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + if (data_length > PSA_CRYPTO_MAX_STORAGE_SIZE) { + return PSA_ERROR_INSUFFICIENT_STORAGE; + } + storage_data_length = data_length + sizeof(psa_persistent_key_storage_format); + + storage_data = mbedtls_calloc(1, storage_data_length); + if (storage_data == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + psa_format_key_data_for_storage(data, data_length, attr, storage_data); + + status = psa_crypto_storage_store(attr->id, + storage_data, storage_data_length); + + mbedtls_platform_zeroize(storage_data, storage_data_length); + mbedtls_free(storage_data); + + return status; +} + +void psa_free_persistent_key_data(uint8_t *key_data, size_t key_data_length) +{ + if (key_data != NULL) { + mbedtls_platform_zeroize(key_data, key_data_length); + } + mbedtls_free(key_data); +} + +psa_status_t psa_load_persistent_key(psa_core_key_attributes_t *attr, + uint8_t **data, + size_t *data_length) +{ + psa_status_t status = PSA_SUCCESS; + uint8_t *loaded_data; + size_t storage_data_length = 0; + mbedtls_svc_key_id_t key = attr->id; + + status = psa_crypto_storage_get_data_length(key, &storage_data_length); + if (status != PSA_SUCCESS) { + return status; + } + + loaded_data = mbedtls_calloc(1, storage_data_length); + + if (loaded_data == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + + status = psa_crypto_storage_load(key, loaded_data, storage_data_length); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_parse_key_data_from_storage(loaded_data, storage_data_length, + data, data_length, attr); + + /* All keys saved to persistent storage always have a key context */ + if (status == PSA_SUCCESS && + (*data == NULL || *data_length == 0)) { + status = PSA_ERROR_STORAGE_FAILURE; + } + +exit: + mbedtls_platform_zeroize(loaded_data, storage_data_length); + mbedtls_free(loaded_data); + return status; +} + + + +/****************************************************************/ +/* Transactions */ +/****************************************************************/ + +#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) + +psa_crypto_transaction_t psa_crypto_transaction; + +psa_status_t psa_crypto_save_transaction(void) +{ + struct psa_storage_info_t p_info; + psa_status_t status; + status = psa_its_get_info(PSA_CRYPTO_ITS_TRANSACTION_UID, &p_info); + if (status == PSA_SUCCESS) { + /* This shouldn't happen: we're trying to start a transaction while + * there is still a transaction that hasn't been replayed. */ + return PSA_ERROR_CORRUPTION_DETECTED; + } else if (status != PSA_ERROR_DOES_NOT_EXIST) { + return status; + } + return psa_its_set(PSA_CRYPTO_ITS_TRANSACTION_UID, + sizeof(psa_crypto_transaction), + &psa_crypto_transaction, + 0); +} + +psa_status_t psa_crypto_load_transaction(void) +{ + psa_status_t status; + size_t length; + status = psa_its_get(PSA_CRYPTO_ITS_TRANSACTION_UID, 0, + sizeof(psa_crypto_transaction), + &psa_crypto_transaction, &length); + if (status != PSA_SUCCESS) { + return status; + } + if (length != sizeof(psa_crypto_transaction)) { + return PSA_ERROR_DATA_INVALID; + } + return PSA_SUCCESS; +} + +psa_status_t psa_crypto_stop_transaction(void) +{ + psa_status_t status = psa_its_remove(PSA_CRYPTO_ITS_TRANSACTION_UID); + /* Whether or not updating the storage succeeded, the transaction is + * finished now. It's too late to go back, so zero out the in-memory + * data. */ + memset(&psa_crypto_transaction, 0, sizeof(psa_crypto_transaction)); + return status; +} + +#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */ + + + +/****************************************************************/ +/* Random generator state */ +/****************************************************************/ + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) +psa_status_t mbedtls_psa_storage_inject_entropy(const unsigned char *seed, + size_t seed_size) +{ + psa_status_t status; + struct psa_storage_info_t p_info; + + status = psa_its_get_info(PSA_CRYPTO_ITS_RANDOM_SEED_UID, &p_info); + + if (PSA_ERROR_DOES_NOT_EXIST == status) { /* No seed exists */ + status = psa_its_set(PSA_CRYPTO_ITS_RANDOM_SEED_UID, seed_size, seed, 0); + } else if (PSA_SUCCESS == status) { + /* You should not be here. Seed needs to be injected only once */ + status = PSA_ERROR_NOT_PERMITTED; + } + return status; +} +#endif /* MBEDTLS_PSA_INJECT_ENTROPY */ + + + +/****************************************************************/ +/* The end */ +/****************************************************************/ + +#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_crypto_storage.h b/r5dev/thirdparty/mbedtls/psa_crypto_storage.h new file mode 100644 index 00000000..04768f8a --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_crypto_storage.h @@ -0,0 +1,396 @@ +/** + * \file psa_crypto_storage.h + * + * \brief PSA cryptography module: Mbed TLS key storage + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PSA_CRYPTO_STORAGE_H +#define PSA_CRYPTO_STORAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "psa/crypto.h" +#include "psa/crypto_se_driver.h" + +#include +#include + +/* Limit the maximum key size in storage. This should have no effect + * since the key size is limited in memory. */ +#define PSA_CRYPTO_MAX_STORAGE_SIZE (PSA_BITS_TO_BYTES(PSA_MAX_KEY_BITS)) +/* Sanity check: a file size must fit in 32 bits. Allow a generous + * 64kB of metadata. */ +#if PSA_CRYPTO_MAX_STORAGE_SIZE > 0xffff0000 +#error PSA_CRYPTO_MAX_STORAGE_SIZE > 0xffff0000 +#endif + +/** The maximum permitted persistent slot number. + * + * In Mbed Crypto 0.1.0b: + * - Using the file backend, all key ids are ok except 0. + * - Using the ITS backend, all key ids are ok except 0xFFFFFF52 + * (#PSA_CRYPTO_ITS_RANDOM_SEED_UID) for which the file contains the + * device's random seed (if this feature is enabled). + * - Only key ids from 1 to #MBEDTLS_PSA_KEY_SLOT_COUNT are actually used. + * + * Since we need to preserve the random seed, avoid using that key slot. + * Reserve a whole range of key slots just in case something else comes up. + * + * This limitation will probably become moot when we implement client + * separation for key storage. + */ +#define PSA_MAX_PERSISTENT_KEY_IDENTIFIER PSA_KEY_ID_VENDOR_MAX + +/** + * \brief Checks if persistent data is stored for the given key slot number + * + * This function checks if any key data or metadata exists for the key slot in + * the persistent storage. + * + * \param key Persistent identifier to check. + * + * \retval 0 + * No persistent data present for slot number + * \retval 1 + * Persistent data present for slot number + */ +int psa_is_key_present_in_storage(const mbedtls_svc_key_id_t key); + +/** + * \brief Format key data and metadata and save to a location for given key + * slot. + * + * This function formats the key data and metadata and saves it to a + * persistent storage backend. The storage location corresponding to the + * key slot must be empty, otherwise this function will fail. This function + * should be called after loading the key into an internal slot to ensure the + * persistent key is not saved into a storage location corresponding to an + * already occupied non-persistent key, as well as ensuring the key data is + * validated. + * + * Note: This function will only succeed for key buffers which are not + * empty. If passed a NULL pointer or zero-length, the function will fail + * with #PSA_ERROR_INVALID_ARGUMENT. + * + * \param[in] attr The attributes of the key to save. + * The key identifier field in the attributes + * determines the key's location. + * \param[in] data Buffer containing the key data. + * \param data_length The number of bytes that make up the key data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INVALID_ARGUMENT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + */ +psa_status_t psa_save_persistent_key(const psa_core_key_attributes_t *attr, + const uint8_t *data, + const size_t data_length); + +/** + * \brief Parses key data and metadata and load persistent key for given + * key slot number. + * + * This function reads from a storage backend, parses the key data and + * metadata and writes them to the appropriate output parameters. + * + * Note: This function allocates a buffer and returns a pointer to it through + * the data parameter. On successful return, the pointer is guaranteed to be + * valid and the buffer contains at least one byte of data. + * psa_free_persistent_key_data() must be called on the data buffer + * afterwards to zeroize and free this buffer. + * + * \param[in,out] attr On input, the key identifier field identifies + * the key to load. Other fields are ignored. + * On success, the attribute structure contains + * the key metadata that was loaded from storage. + * \param[out] data Pointer to an allocated key data buffer on return. + * \param[out] data_length The number of bytes that make up the key data. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription + */ +psa_status_t psa_load_persistent_key(psa_core_key_attributes_t *attr, + uint8_t **data, + size_t *data_length); + +/** + * \brief Remove persistent data for the given key slot number. + * + * \param key Persistent identifier of the key to remove + * from persistent storage. + * + * \retval #PSA_SUCCESS + * The key was successfully removed, + * or the key did not exist. + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + */ +psa_status_t psa_destroy_persistent_key(const mbedtls_svc_key_id_t key); + +/** + * \brief Free the temporary buffer allocated by psa_load_persistent_key(). + * + * This function must be called at some point after psa_load_persistent_key() + * to zeroize and free the memory allocated to the buffer in that function. + * + * \param key_data Buffer for the key data. + * \param key_data_length Size of the key data buffer. + * + */ +void psa_free_persistent_key_data(uint8_t *key_data, size_t key_data_length); + +/** + * \brief Formats key data and metadata for persistent storage + * + * \param[in] data Buffer containing the key data. + * \param data_length Length of the key data buffer. + * \param[in] attr The core attributes of the key. + * \param[out] storage_data Output buffer for the formatted data. + * + */ +void psa_format_key_data_for_storage(const uint8_t *data, + const size_t data_length, + const psa_core_key_attributes_t *attr, + uint8_t *storage_data); + +/** + * \brief Parses persistent storage data into key data and metadata + * + * \param[in] storage_data Buffer for the storage data. + * \param storage_data_length Length of the storage data buffer + * \param[out] key_data On output, pointer to a newly allocated buffer + * containing the key data. This must be freed + * using psa_free_persistent_key_data() + * \param[out] key_data_length Length of the key data buffer + * \param[out] attr On success, the attribute structure is filled + * with the loaded key metadata. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + */ +psa_status_t psa_parse_key_data_from_storage(const uint8_t *storage_data, + size_t storage_data_length, + uint8_t **key_data, + size_t *key_data_length, + psa_core_key_attributes_t *attr); + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +/** This symbol is defined if transaction support is required. */ +#define PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS +#endif + +#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) + +/** The type of transaction that is in progress. + */ +/* This is an integer type rather than an enum for two reasons: to support + * unknown values when loading a transaction file, and to ensure that the + * type has a known size. + */ +typedef uint16_t psa_crypto_transaction_type_t; + +/** No transaction is in progress. + * + * This has the value 0, so zero-initialization sets a transaction's type to + * this value. + */ +#define PSA_CRYPTO_TRANSACTION_NONE ((psa_crypto_transaction_type_t) 0x0000) + +/** A key creation transaction. + * + * This is only used for keys in an external cryptoprocessor (secure element). + * Keys in RAM or in internal storage are created atomically in storage + * (simple file creation), so they do not need a transaction mechanism. + */ +#define PSA_CRYPTO_TRANSACTION_CREATE_KEY ((psa_crypto_transaction_type_t) 0x0001) + +/** A key destruction transaction. + * + * This is only used for keys in an external cryptoprocessor (secure element). + * Keys in RAM or in internal storage are destroyed atomically in storage + * (simple file deletion), so they do not need a transaction mechanism. + */ +#define PSA_CRYPTO_TRANSACTION_DESTROY_KEY ((psa_crypto_transaction_type_t) 0x0002) + +/** Transaction data. + * + * This type is designed to be serialized by writing the memory representation + * and reading it back on the same device. + * + * \note The transaction mechanism is designed for a single active transaction + * at a time. The transaction object is #psa_crypto_transaction. + * + * \note If an API call starts a transaction, it must complete this transaction + * before returning to the application. + * + * The lifetime of a transaction is the following (note that only one + * transaction may be active at a time): + * + * -# Call psa_crypto_prepare_transaction() to initialize the transaction + * object in memory and declare the type of transaction that is starting. + * -# Fill in the type-specific fields of #psa_crypto_transaction. + * -# Call psa_crypto_save_transaction() to start the transaction. This + * saves the transaction data to internal storage. + * -# Perform the work of the transaction by modifying files, contacting + * external entities, or whatever needs doing. Note that the transaction + * may be interrupted by a power failure, so you need to have a way + * recover from interruptions either by undoing what has been done + * so far or by resuming where you left off. + * -# If there are intermediate stages in the transaction, update + * the fields of #psa_crypto_transaction and call + * psa_crypto_save_transaction() again when each stage is reached. + * -# When the transaction is over, call psa_crypto_stop_transaction() to + * remove the transaction data in storage and in memory. + * + * If the system crashes while a transaction is in progress, psa_crypto_init() + * calls psa_crypto_load_transaction() and takes care of completing or + * rewinding the transaction. This is done in psa_crypto_recover_transaction() + * in psa_crypto.c. If you add a new type of transaction, be + * sure to add code for it in psa_crypto_recover_transaction(). + */ +typedef union { + /* Each element of this union must have the following properties + * to facilitate serialization and deserialization: + * + * - The element is a struct. + * - The first field of the struct is `psa_crypto_transaction_type_t type`. + * - Elements of the struct are arranged such a way that there is + * no padding. + */ + struct psa_crypto_transaction_unknown_s { + psa_crypto_transaction_type_t type; + uint16_t unused1; + uint32_t unused2; + uint64_t unused3; + uint64_t unused4; + } unknown; + /* ::type is #PSA_CRYPTO_TRANSACTION_CREATE_KEY or + * #PSA_CRYPTO_TRANSACTION_DESTROY_KEY. */ + struct psa_crypto_transaction_key_s { + psa_crypto_transaction_type_t type; + uint16_t unused1; + psa_key_lifetime_t lifetime; + psa_key_slot_number_t slot; + mbedtls_svc_key_id_t id; + } key; +} psa_crypto_transaction_t; + +/** The single active transaction. + */ +extern psa_crypto_transaction_t psa_crypto_transaction; + +/** Prepare for a transaction. + * + * There must not be an ongoing transaction. + * + * \param type The type of transaction to start. + */ +static inline void psa_crypto_prepare_transaction( + psa_crypto_transaction_type_t type) +{ + psa_crypto_transaction.unknown.type = type; +} + +/** Save the transaction data to storage. + * + * You may call this function multiple times during a transaction to + * atomically update the transaction state. + * + * \retval #PSA_SUCCESS \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + */ +psa_status_t psa_crypto_save_transaction(void); + +/** Load the transaction data from storage, if any. + * + * This function is meant to be called from psa_crypto_init() to recover + * in case a transaction was interrupted by a system crash. + * + * \retval #PSA_SUCCESS + * The data about the ongoing transaction has been loaded to + * #psa_crypto_transaction. + * \retval #PSA_ERROR_DOES_NOT_EXIST + * There is no ongoing transaction. + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_DATA_INVALID \emptydescription + * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription + */ +psa_status_t psa_crypto_load_transaction(void); + +/** Indicate that the current transaction is finished. + * + * Call this function at the very end of transaction processing. + * This function does not "commit" or "abort" the transaction: the storage + * subsystem has no concept of "commit" and "abort", just saving and + * removing the transaction information in storage. + * + * This function erases the transaction data in storage (if any) and + * resets the transaction data in memory. + * + * \retval #PSA_SUCCESS + * There was transaction data in storage. + * \retval #PSA_ERROR_DOES_NOT_EXIST + * There was no transaction data in storage. + * \retval #PSA_ERROR_STORAGE_FAILURE + * It was impossible to determine whether there was transaction data + * in storage, or the transaction data could not be erased. + */ +psa_status_t psa_crypto_stop_transaction(void); + +/** The ITS file identifier for the transaction data. + * + * 0xffffffNN = special file; 0x74 = 't' for transaction. + */ +#define PSA_CRYPTO_ITS_TRANSACTION_UID ((psa_key_id_t) 0xffffff74) + +#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */ + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) +/** Backend side of mbedtls_psa_inject_entropy(). + * + * This function stores the supplied data into the entropy seed file. + * + * \retval #PSA_SUCCESS + * Success + * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription + * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription + * \retval #PSA_ERROR_NOT_PERMITTED + * The entropy seed file already exists. + */ +psa_status_t mbedtls_psa_storage_inject_entropy(const unsigned char *seed, + size_t seed_size); +#endif /* MBEDTLS_PSA_INJECT_ENTROPY */ + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_CRYPTO_STORAGE_H */ diff --git a/r5dev/thirdparty/mbedtls/psa_its_file.c b/r5dev/thirdparty/mbedtls/psa_its_file.c new file mode 100644 index 00000000..97486165 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_its_file.c @@ -0,0 +1,271 @@ +/* + * PSA ITS simulator over stdio files. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_ITS_FILE_C) + +#include "mbedtls/platform.h" + +#if defined(_WIN32) +#include +#endif + +#include "psa_crypto_its.h" + +#include +#include +#include +#include + +#if !defined(PSA_ITS_STORAGE_PREFIX) +#define PSA_ITS_STORAGE_PREFIX "" +#endif + +#define PSA_ITS_STORAGE_FILENAME_PATTERN "%08x%08x" +#define PSA_ITS_STORAGE_SUFFIX ".psa_its" +#define PSA_ITS_STORAGE_FILENAME_LENGTH \ + (sizeof(PSA_ITS_STORAGE_PREFIX) - 1 + /*prefix without terminating 0*/ \ + 16 + /*UID (64-bit number in hex)*/ \ + sizeof(PSA_ITS_STORAGE_SUFFIX) - 1 + /*suffix without terminating 0*/ \ + 1 /*terminating null byte*/) +#define PSA_ITS_STORAGE_TEMP \ + PSA_ITS_STORAGE_PREFIX "tempfile" PSA_ITS_STORAGE_SUFFIX + +/* The maximum value of psa_storage_info_t.size */ +#define PSA_ITS_MAX_SIZE 0xffffffff + +#define PSA_ITS_MAGIC_STRING "PSA\0ITS\0" +#define PSA_ITS_MAGIC_LENGTH 8 + +/* As rename fails on Windows if the new filepath already exists, + * use MoveFileExA with the MOVEFILE_REPLACE_EXISTING flag instead. + * Returns 0 on success, nonzero on failure. */ +#if defined(_WIN32) +#define rename_replace_existing(oldpath, newpath) \ + (!MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) +#else +#define rename_replace_existing(oldpath, newpath) rename(oldpath, newpath) +#endif + +typedef struct { + uint8_t magic[PSA_ITS_MAGIC_LENGTH]; + uint8_t size[sizeof(uint32_t)]; + uint8_t flags[sizeof(psa_storage_create_flags_t)]; +} psa_its_file_header_t; + +static void psa_its_fill_filename(psa_storage_uid_t uid, char *filename) +{ + /* Break up the UID into two 32-bit pieces so as not to rely on + * long long support in snprintf. */ + mbedtls_snprintf(filename, PSA_ITS_STORAGE_FILENAME_LENGTH, + "%s" PSA_ITS_STORAGE_FILENAME_PATTERN "%s", + PSA_ITS_STORAGE_PREFIX, + (unsigned) (uid >> 32), + (unsigned) (uid & 0xffffffff), + PSA_ITS_STORAGE_SUFFIX); +} + +static psa_status_t psa_its_read_file(psa_storage_uid_t uid, + struct psa_storage_info_t *p_info, + FILE **p_stream) +{ + char filename[PSA_ITS_STORAGE_FILENAME_LENGTH]; + psa_its_file_header_t header; + size_t n; + + *p_stream = NULL; + psa_its_fill_filename(uid, filename); + *p_stream = fopen(filename, "rb"); + if (*p_stream == NULL) { + return PSA_ERROR_DOES_NOT_EXIST; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(*p_stream, NULL); + + n = fread(&header, 1, sizeof(header), *p_stream); + if (n != sizeof(header)) { + return PSA_ERROR_DATA_CORRUPT; + } + if (memcmp(header.magic, PSA_ITS_MAGIC_STRING, + PSA_ITS_MAGIC_LENGTH) != 0) { + return PSA_ERROR_DATA_CORRUPT; + } + + p_info->size = (header.size[0] | + header.size[1] << 8 | + header.size[2] << 16 | + header.size[3] << 24); + p_info->flags = (header.flags[0] | + header.flags[1] << 8 | + header.flags[2] << 16 | + header.flags[3] << 24); + return PSA_SUCCESS; +} + +psa_status_t psa_its_get_info(psa_storage_uid_t uid, + struct psa_storage_info_t *p_info) +{ + psa_status_t status; + FILE *stream = NULL; + status = psa_its_read_file(uid, p_info, &stream); + if (stream != NULL) { + fclose(stream); + } + return status; +} + +psa_status_t psa_its_get(psa_storage_uid_t uid, + uint32_t data_offset, + uint32_t data_length, + void *p_data, + size_t *p_data_length) +{ + psa_status_t status; + FILE *stream = NULL; + size_t n; + struct psa_storage_info_t info; + + status = psa_its_read_file(uid, &info, &stream); + if (status != PSA_SUCCESS) { + goto exit; + } + status = PSA_ERROR_INVALID_ARGUMENT; + if (data_offset + data_length < data_offset) { + goto exit; + } +#if SIZE_MAX < 0xffffffff + if (data_offset + data_length > SIZE_MAX) { + goto exit; + } +#endif + if (data_offset + data_length > info.size) { + goto exit; + } + + status = PSA_ERROR_STORAGE_FAILURE; +#if LONG_MAX < 0xffffffff + while (data_offset > LONG_MAX) { + if (fseek(stream, LONG_MAX, SEEK_CUR) != 0) { + goto exit; + } + data_offset -= LONG_MAX; + } +#endif + if (fseek(stream, data_offset, SEEK_CUR) != 0) { + goto exit; + } + n = fread(p_data, 1, data_length, stream); + if (n != data_length) { + goto exit; + } + status = PSA_SUCCESS; + if (p_data_length != NULL) { + *p_data_length = n; + } + +exit: + if (stream != NULL) { + fclose(stream); + } + return status; +} + +psa_status_t psa_its_set(psa_storage_uid_t uid, + uint32_t data_length, + const void *p_data, + psa_storage_create_flags_t create_flags) +{ + if (uid == 0) { + return PSA_ERROR_INVALID_HANDLE; + } + + psa_status_t status = PSA_ERROR_STORAGE_FAILURE; + char filename[PSA_ITS_STORAGE_FILENAME_LENGTH]; + FILE *stream = NULL; + psa_its_file_header_t header; + size_t n; + + memcpy(header.magic, PSA_ITS_MAGIC_STRING, PSA_ITS_MAGIC_LENGTH); + MBEDTLS_PUT_UINT32_LE(data_length, header.size, 0); + MBEDTLS_PUT_UINT32_LE(create_flags, header.flags, 0); + + psa_its_fill_filename(uid, filename); + stream = fopen(PSA_ITS_STORAGE_TEMP, "wb"); + + if (stream == NULL) { + goto exit; + } + + /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ + mbedtls_setbuf(stream, NULL); + + status = PSA_ERROR_INSUFFICIENT_STORAGE; + n = fwrite(&header, 1, sizeof(header), stream); + if (n != sizeof(header)) { + goto exit; + } + if (data_length != 0) { + n = fwrite(p_data, 1, data_length, stream); + if (n != data_length) { + goto exit; + } + } + status = PSA_SUCCESS; + +exit: + if (stream != NULL) { + int ret = fclose(stream); + if (status == PSA_SUCCESS && ret != 0) { + status = PSA_ERROR_INSUFFICIENT_STORAGE; + } + } + if (status == PSA_SUCCESS) { + if (rename_replace_existing(PSA_ITS_STORAGE_TEMP, filename) != 0) { + status = PSA_ERROR_STORAGE_FAILURE; + } + } + /* The temporary file may still exist, but only in failure cases where + * we're already reporting an error. So there's nothing we can do on + * failure. If the function succeeded, and in some error cases, the + * temporary file doesn't exist and so remove() is expected to fail. + * Thus we just ignore the return status of remove(). */ + (void) remove(PSA_ITS_STORAGE_TEMP); + return status; +} + +psa_status_t psa_its_remove(psa_storage_uid_t uid) +{ + char filename[PSA_ITS_STORAGE_FILENAME_LENGTH]; + FILE *stream; + psa_its_fill_filename(uid, filename); + stream = fopen(filename, "rb"); + if (stream == NULL) { + return PSA_ERROR_DOES_NOT_EXIST; + } + fclose(stream); + if (remove(filename) != 0) { + return PSA_ERROR_STORAGE_FAILURE; + } + return PSA_SUCCESS; +} + +#endif /* MBEDTLS_PSA_ITS_FILE_C */ diff --git a/r5dev/thirdparty/mbedtls/psa_util.c b/r5dev/thirdparty/mbedtls/psa_util.c new file mode 100644 index 00000000..43a10a32 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/psa_util.c @@ -0,0 +1,149 @@ +/* + * PSA hashing layer on top of Mbed TLS software crypto + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_PSA_CRYPTO_C) + +#include + +#include "psa_crypto_core.h" +#include +#include +#include +#include +#include + +/* PSA_SUCCESS is kept at the top of each error table since + * it's the most common status when everything functions properly. */ +#if !defined(MBEDTLS_MD_C) || !defined(MBEDTLS_MD5_C) || defined(MBEDTLS_USE_PSA_CRYPTO) +const mbedtls_error_pair_t psa_to_md_errors[] = +{ + { PSA_SUCCESS, 0 }, + { PSA_ERROR_NOT_SUPPORTED, MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE }, + { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_MD_BAD_INPUT_DATA }, + { PSA_ERROR_INSUFFICIENT_MEMORY, MBEDTLS_ERR_MD_ALLOC_FAILED } +}; +#endif +#if defined(MBEDTLS_LMS_C) +const mbedtls_error_pair_t psa_to_lms_errors[] = +{ + { PSA_SUCCESS, 0 }, + { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL }, + { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_LMS_BAD_INPUT_DATA } +}; +#endif +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) +const mbedtls_error_pair_t psa_to_ssl_errors[] = +{ + { PSA_SUCCESS, 0 }, + { PSA_ERROR_INSUFFICIENT_MEMORY, MBEDTLS_ERR_SSL_ALLOC_FAILED }, + { PSA_ERROR_NOT_SUPPORTED, MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE }, + { PSA_ERROR_INVALID_SIGNATURE, MBEDTLS_ERR_SSL_INVALID_MAC }, + { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_SSL_BAD_INPUT_DATA }, + { PSA_ERROR_BAD_STATE, MBEDTLS_ERR_SSL_INTERNAL_ERROR }, + { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL } +}; +#endif + +#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) || \ + defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR) +const mbedtls_error_pair_t psa_to_pk_rsa_errors[] = +{ + { PSA_SUCCESS, 0 }, + { PSA_ERROR_NOT_PERMITTED, MBEDTLS_ERR_RSA_BAD_INPUT_DATA }, + { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_RSA_BAD_INPUT_DATA }, + { PSA_ERROR_INVALID_HANDLE, MBEDTLS_ERR_RSA_BAD_INPUT_DATA }, + { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE }, + { PSA_ERROR_INSUFFICIENT_ENTROPY, MBEDTLS_ERR_RSA_RNG_FAILED }, + { PSA_ERROR_INVALID_SIGNATURE, MBEDTLS_ERR_RSA_VERIFY_FAILED }, + { PSA_ERROR_INVALID_PADDING, MBEDTLS_ERR_RSA_INVALID_PADDING } +}; +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY) +const mbedtls_error_pair_t psa_to_pk_ecdsa_errors[] = +{ + { PSA_SUCCESS, 0 }, + { PSA_ERROR_NOT_PERMITTED, MBEDTLS_ERR_ECP_BAD_INPUT_DATA }, + { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_ECP_BAD_INPUT_DATA }, + { PSA_ERROR_INVALID_HANDLE, MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE }, + { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL }, + { PSA_ERROR_INSUFFICIENT_ENTROPY, MBEDTLS_ERR_ECP_RANDOM_FAILED }, + { PSA_ERROR_INVALID_SIGNATURE, MBEDTLS_ERR_ECP_VERIFY_FAILED } +}; +#endif + +int psa_generic_status_to_mbedtls(psa_status_t status) +{ + switch (status) { + case PSA_SUCCESS: + return 0; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED; + case PSA_ERROR_CORRUPTION_DETECTED: + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + case PSA_ERROR_COMMUNICATION_FAILURE: + case PSA_ERROR_HARDWARE_FAILURE: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + case PSA_ERROR_NOT_PERMITTED: + default: + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + } +} + +int psa_status_to_mbedtls(psa_status_t status, + const mbedtls_error_pair_t *local_translations, + size_t local_errors_num, + int (*fallback_f)(psa_status_t)) +{ + for (size_t i = 0; i < local_errors_num; i++) { + if (status == local_translations[i].psa_status) { + return local_translations[i].mbedtls_error; + } + } + return fallback_f(status); +} + +int psa_pk_status_to_mbedtls(psa_status_t status) +{ + switch (status) { + case PSA_ERROR_INVALID_HANDLE: + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE; + case PSA_ERROR_INVALID_ARGUMENT: + return MBEDTLS_ERR_PK_INVALID_ALG; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_PK_ALLOC_FAILED; + case PSA_ERROR_BAD_STATE: + return MBEDTLS_ERR_PK_BAD_INPUT_DATA; + case PSA_ERROR_DATA_CORRUPT: + case PSA_ERROR_DATA_INVALID: + case PSA_ERROR_STORAGE_FAILURE: + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + default: + return psa_generic_status_to_mbedtls(status); + } +} +#endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/r5dev/thirdparty/mbedtls/ripemd160.c b/r5dev/thirdparty/mbedtls/ripemd160.c new file mode 100644 index 00000000..ba97c1f3 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ripemd160.c @@ -0,0 +1,498 @@ +/* + * RIPE MD-160 implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The RIPEMD-160 algorithm was designed by RIPE in 1996 + * http://homes.esat.kuleuven.be/~bosselae/mbedtls_ripemd160.html + * http://ehash.iaik.tugraz.at/wiki/RIPEMD-160 + */ + +#include "common.h" + +#if defined(MBEDTLS_RIPEMD160_C) + +#include "mbedtls/ripemd160.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_RIPEMD160_ALT) + +void mbedtls_ripemd160_init(mbedtls_ripemd160_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_ripemd160_context)); +} + +void mbedtls_ripemd160_free(mbedtls_ripemd160_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_ripemd160_context)); +} + +void mbedtls_ripemd160_clone(mbedtls_ripemd160_context *dst, + const mbedtls_ripemd160_context *src) +{ + *dst = *src; +} + +/* + * RIPEMD-160 context setup + */ +int mbedtls_ripemd160_starts(mbedtls_ripemd160_context *ctx) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + + return 0; +} + +#if !defined(MBEDTLS_RIPEMD160_PROCESS_ALT) +/* + * Process one block + */ +int mbedtls_internal_ripemd160_process(mbedtls_ripemd160_context *ctx, + const unsigned char data[64]) +{ + struct { + uint32_t A, B, C, D, E, Ap, Bp, Cp, Dp, Ep, X[16]; + } local; + + local.X[0] = MBEDTLS_GET_UINT32_LE(data, 0); + local.X[1] = MBEDTLS_GET_UINT32_LE(data, 4); + local.X[2] = MBEDTLS_GET_UINT32_LE(data, 8); + local.X[3] = MBEDTLS_GET_UINT32_LE(data, 12); + local.X[4] = MBEDTLS_GET_UINT32_LE(data, 16); + local.X[5] = MBEDTLS_GET_UINT32_LE(data, 20); + local.X[6] = MBEDTLS_GET_UINT32_LE(data, 24); + local.X[7] = MBEDTLS_GET_UINT32_LE(data, 28); + local.X[8] = MBEDTLS_GET_UINT32_LE(data, 32); + local.X[9] = MBEDTLS_GET_UINT32_LE(data, 36); + local.X[10] = MBEDTLS_GET_UINT32_LE(data, 40); + local.X[11] = MBEDTLS_GET_UINT32_LE(data, 44); + local.X[12] = MBEDTLS_GET_UINT32_LE(data, 48); + local.X[13] = MBEDTLS_GET_UINT32_LE(data, 52); + local.X[14] = MBEDTLS_GET_UINT32_LE(data, 56); + local.X[15] = MBEDTLS_GET_UINT32_LE(data, 60); + + local.A = local.Ap = ctx->state[0]; + local.B = local.Bp = ctx->state[1]; + local.C = local.Cp = ctx->state[2]; + local.D = local.Dp = ctx->state[3]; + local.E = local.Ep = ctx->state[4]; + +#define F1(x, y, z) ((x) ^ (y) ^ (z)) +#define F2(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define F3(x, y, z) (((x) | ~(y)) ^ (z)) +#define F4(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define F5(x, y, z) ((x) ^ ((y) | ~(z))) + +#define S(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +#define P(a, b, c, d, e, r, s, f, k) \ + do \ + { \ + (a) += f((b), (c), (d)) + local.X[r] + (k); \ + (a) = S((a), (s)) + (e); \ + (c) = S((c), 10); \ + } while (0) + +#define P2(a, b, c, d, e, r, s, rp, sp) \ + do \ + { \ + P((a), (b), (c), (d), (e), (r), (s), F, K); \ + P(a ## p, b ## p, c ## p, d ## p, e ## p, \ + (rp), (sp), Fp, Kp); \ + } while (0) + +#define F F1 +#define K 0x00000000 +#define Fp F5 +#define Kp 0x50A28BE6 + P2(local.A, local.B, local.C, local.D, local.E, 0, 11, 5, 8); + P2(local.E, local.A, local.B, local.C, local.D, 1, 14, 14, 9); + P2(local.D, local.E, local.A, local.B, local.C, 2, 15, 7, 9); + P2(local.C, local.D, local.E, local.A, local.B, 3, 12, 0, 11); + P2(local.B, local.C, local.D, local.E, local.A, 4, 5, 9, 13); + P2(local.A, local.B, local.C, local.D, local.E, 5, 8, 2, 15); + P2(local.E, local.A, local.B, local.C, local.D, 6, 7, 11, 15); + P2(local.D, local.E, local.A, local.B, local.C, 7, 9, 4, 5); + P2(local.C, local.D, local.E, local.A, local.B, 8, 11, 13, 7); + P2(local.B, local.C, local.D, local.E, local.A, 9, 13, 6, 7); + P2(local.A, local.B, local.C, local.D, local.E, 10, 14, 15, 8); + P2(local.E, local.A, local.B, local.C, local.D, 11, 15, 8, 11); + P2(local.D, local.E, local.A, local.B, local.C, 12, 6, 1, 14); + P2(local.C, local.D, local.E, local.A, local.B, 13, 7, 10, 14); + P2(local.B, local.C, local.D, local.E, local.A, 14, 9, 3, 12); + P2(local.A, local.B, local.C, local.D, local.E, 15, 8, 12, 6); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F2 +#define K 0x5A827999 +#define Fp F4 +#define Kp 0x5C4DD124 + P2(local.E, local.A, local.B, local.C, local.D, 7, 7, 6, 9); + P2(local.D, local.E, local.A, local.B, local.C, 4, 6, 11, 13); + P2(local.C, local.D, local.E, local.A, local.B, 13, 8, 3, 15); + P2(local.B, local.C, local.D, local.E, local.A, 1, 13, 7, 7); + P2(local.A, local.B, local.C, local.D, local.E, 10, 11, 0, 12); + P2(local.E, local.A, local.B, local.C, local.D, 6, 9, 13, 8); + P2(local.D, local.E, local.A, local.B, local.C, 15, 7, 5, 9); + P2(local.C, local.D, local.E, local.A, local.B, 3, 15, 10, 11); + P2(local.B, local.C, local.D, local.E, local.A, 12, 7, 14, 7); + P2(local.A, local.B, local.C, local.D, local.E, 0, 12, 15, 7); + P2(local.E, local.A, local.B, local.C, local.D, 9, 15, 8, 12); + P2(local.D, local.E, local.A, local.B, local.C, 5, 9, 12, 7); + P2(local.C, local.D, local.E, local.A, local.B, 2, 11, 4, 6); + P2(local.B, local.C, local.D, local.E, local.A, 14, 7, 9, 15); + P2(local.A, local.B, local.C, local.D, local.E, 11, 13, 1, 13); + P2(local.E, local.A, local.B, local.C, local.D, 8, 12, 2, 11); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F3 +#define K 0x6ED9EBA1 +#define Fp F3 +#define Kp 0x6D703EF3 + P2(local.D, local.E, local.A, local.B, local.C, 3, 11, 15, 9); + P2(local.C, local.D, local.E, local.A, local.B, 10, 13, 5, 7); + P2(local.B, local.C, local.D, local.E, local.A, 14, 6, 1, 15); + P2(local.A, local.B, local.C, local.D, local.E, 4, 7, 3, 11); + P2(local.E, local.A, local.B, local.C, local.D, 9, 14, 7, 8); + P2(local.D, local.E, local.A, local.B, local.C, 15, 9, 14, 6); + P2(local.C, local.D, local.E, local.A, local.B, 8, 13, 6, 6); + P2(local.B, local.C, local.D, local.E, local.A, 1, 15, 9, 14); + P2(local.A, local.B, local.C, local.D, local.E, 2, 14, 11, 12); + P2(local.E, local.A, local.B, local.C, local.D, 7, 8, 8, 13); + P2(local.D, local.E, local.A, local.B, local.C, 0, 13, 12, 5); + P2(local.C, local.D, local.E, local.A, local.B, 6, 6, 2, 14); + P2(local.B, local.C, local.D, local.E, local.A, 13, 5, 10, 13); + P2(local.A, local.B, local.C, local.D, local.E, 11, 12, 0, 13); + P2(local.E, local.A, local.B, local.C, local.D, 5, 7, 4, 7); + P2(local.D, local.E, local.A, local.B, local.C, 12, 5, 13, 5); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F4 +#define K 0x8F1BBCDC +#define Fp F2 +#define Kp 0x7A6D76E9 + P2(local.C, local.D, local.E, local.A, local.B, 1, 11, 8, 15); + P2(local.B, local.C, local.D, local.E, local.A, 9, 12, 6, 5); + P2(local.A, local.B, local.C, local.D, local.E, 11, 14, 4, 8); + P2(local.E, local.A, local.B, local.C, local.D, 10, 15, 1, 11); + P2(local.D, local.E, local.A, local.B, local.C, 0, 14, 3, 14); + P2(local.C, local.D, local.E, local.A, local.B, 8, 15, 11, 14); + P2(local.B, local.C, local.D, local.E, local.A, 12, 9, 15, 6); + P2(local.A, local.B, local.C, local.D, local.E, 4, 8, 0, 14); + P2(local.E, local.A, local.B, local.C, local.D, 13, 9, 5, 6); + P2(local.D, local.E, local.A, local.B, local.C, 3, 14, 12, 9); + P2(local.C, local.D, local.E, local.A, local.B, 7, 5, 2, 12); + P2(local.B, local.C, local.D, local.E, local.A, 15, 6, 13, 9); + P2(local.A, local.B, local.C, local.D, local.E, 14, 8, 9, 12); + P2(local.E, local.A, local.B, local.C, local.D, 5, 6, 7, 5); + P2(local.D, local.E, local.A, local.B, local.C, 6, 5, 10, 15); + P2(local.C, local.D, local.E, local.A, local.B, 2, 12, 14, 8); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F5 +#define K 0xA953FD4E +#define Fp F1 +#define Kp 0x00000000 + P2(local.B, local.C, local.D, local.E, local.A, 4, 9, 12, 8); + P2(local.A, local.B, local.C, local.D, local.E, 0, 15, 15, 5); + P2(local.E, local.A, local.B, local.C, local.D, 5, 5, 10, 12); + P2(local.D, local.E, local.A, local.B, local.C, 9, 11, 4, 9); + P2(local.C, local.D, local.E, local.A, local.B, 7, 6, 1, 12); + P2(local.B, local.C, local.D, local.E, local.A, 12, 8, 5, 5); + P2(local.A, local.B, local.C, local.D, local.E, 2, 13, 8, 14); + P2(local.E, local.A, local.B, local.C, local.D, 10, 12, 7, 6); + P2(local.D, local.E, local.A, local.B, local.C, 14, 5, 6, 8); + P2(local.C, local.D, local.E, local.A, local.B, 1, 12, 2, 13); + P2(local.B, local.C, local.D, local.E, local.A, 3, 13, 13, 6); + P2(local.A, local.B, local.C, local.D, local.E, 8, 14, 14, 5); + P2(local.E, local.A, local.B, local.C, local.D, 11, 11, 0, 15); + P2(local.D, local.E, local.A, local.B, local.C, 6, 8, 3, 13); + P2(local.C, local.D, local.E, local.A, local.B, 15, 5, 9, 11); + P2(local.B, local.C, local.D, local.E, local.A, 13, 6, 11, 11); +#undef F +#undef K +#undef Fp +#undef Kp + + local.C = ctx->state[1] + local.C + local.Dp; + ctx->state[1] = ctx->state[2] + local.D + local.Ep; + ctx->state[2] = ctx->state[3] + local.E + local.Ap; + ctx->state[3] = ctx->state[4] + local.A + local.Bp; + ctx->state[4] = ctx->state[0] + local.B + local.Cp; + ctx->state[0] = local.C; + + /* Zeroise variables to clear sensitive data from memory. */ + mbedtls_platform_zeroize(&local, sizeof(local)); + + return 0; +} + +#endif /* !MBEDTLS_RIPEMD160_PROCESS_ALT */ + +/* + * RIPEMD-160 process buffer + */ +int mbedtls_ripemd160_update(mbedtls_ripemd160_context *ctx, + const unsigned char *input, + size_t ilen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t fill; + uint32_t left; + + if (ilen == 0) { + return 0; + } + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if (ctx->total[0] < (uint32_t) ilen) { + ctx->total[1]++; + } + + if (left && ilen >= fill) { + memcpy((void *) (ctx->buffer + left), input, fill); + + if ((ret = mbedtls_internal_ripemd160_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= 64) { + if ((ret = mbedtls_internal_ripemd160_process(ctx, input)) != 0) { + return ret; + } + + input += 64; + ilen -= 64; + } + + if (ilen > 0) { + memcpy((void *) (ctx->buffer + left), input, ilen); + } + + return 0; +} + +static const unsigned char ripemd160_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * RIPEMD-160 final digest + */ +int mbedtls_ripemd160_finish(mbedtls_ripemd160_context *ctx, + unsigned char output[20]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = (ctx->total[0] >> 29) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + MBEDTLS_PUT_UINT32_LE(low, msglen, 0); + MBEDTLS_PUT_UINT32_LE(high, msglen, 4); + + last = ctx->total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + ret = mbedtls_ripemd160_update(ctx, ripemd160_padding, padn); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ripemd160_update(ctx, msglen, 8); + if (ret != 0) { + return ret; + } + + MBEDTLS_PUT_UINT32_LE(ctx->state[0], output, 0); + MBEDTLS_PUT_UINT32_LE(ctx->state[1], output, 4); + MBEDTLS_PUT_UINT32_LE(ctx->state[2], output, 8); + MBEDTLS_PUT_UINT32_LE(ctx->state[3], output, 12); + MBEDTLS_PUT_UINT32_LE(ctx->state[4], output, 16); + + return 0; +} + +#endif /* ! MBEDTLS_RIPEMD160_ALT */ + +/* + * output = RIPEMD-160( input buffer ) + */ +int mbedtls_ripemd160(const unsigned char *input, + size_t ilen, + unsigned char output[20]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ripemd160_context ctx; + + mbedtls_ripemd160_init(&ctx); + + if ((ret = mbedtls_ripemd160_starts(&ctx)) != 0) { + goto exit; + } + + if ((ret = mbedtls_ripemd160_update(&ctx, input, ilen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_ripemd160_finish(&ctx, output)) != 0) { + goto exit; + } + +exit: + mbedtls_ripemd160_free(&ctx); + + return ret; +} + +#if defined(MBEDTLS_SELF_TEST) +/* + * Test vectors from the RIPEMD-160 paper and + * http://homes.esat.kuleuven.be/~bosselae/mbedtls_ripemd160.html#HMAC + */ +#define TESTS 8 +static const unsigned char ripemd160_test_str[TESTS][81] = +{ + { "" }, + { "a" }, + { "abc" }, + { "message digest" }, + { "abcdefghijklmnopqrstuvwxyz" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" }, + { "12345678901234567890123456789012345678901234567890123456789012345678901234567890" }, +}; + +static const size_t ripemd160_test_strlen[TESTS] = +{ + 0, 1, 3, 14, 26, 56, 62, 80 +}; + +static const unsigned char ripemd160_test_md[TESTS][20] = +{ + { 0x9c, 0x11, 0x85, 0xa5, 0xc5, 0xe9, 0xfc, 0x54, 0x61, 0x28, + 0x08, 0x97, 0x7e, 0xe8, 0xf5, 0x48, 0xb2, 0x25, 0x8d, 0x31 }, + { 0x0b, 0xdc, 0x9d, 0x2d, 0x25, 0x6b, 0x3e, 0xe9, 0xda, 0xae, + 0x34, 0x7b, 0xe6, 0xf4, 0xdc, 0x83, 0x5a, 0x46, 0x7f, 0xfe }, + { 0x8e, 0xb2, 0x08, 0xf7, 0xe0, 0x5d, 0x98, 0x7a, 0x9b, 0x04, + 0x4a, 0x8e, 0x98, 0xc6, 0xb0, 0x87, 0xf1, 0x5a, 0x0b, 0xfc }, + { 0x5d, 0x06, 0x89, 0xef, 0x49, 0xd2, 0xfa, 0xe5, 0x72, 0xb8, + 0x81, 0xb1, 0x23, 0xa8, 0x5f, 0xfa, 0x21, 0x59, 0x5f, 0x36 }, + { 0xf7, 0x1c, 0x27, 0x10, 0x9c, 0x69, 0x2c, 0x1b, 0x56, 0xbb, + 0xdc, 0xeb, 0x5b, 0x9d, 0x28, 0x65, 0xb3, 0x70, 0x8d, 0xbc }, + { 0x12, 0xa0, 0x53, 0x38, 0x4a, 0x9c, 0x0c, 0x88, 0xe4, 0x05, + 0xa0, 0x6c, 0x27, 0xdc, 0xf4, 0x9a, 0xda, 0x62, 0xeb, 0x2b }, + { 0xb0, 0xe2, 0x0b, 0x6e, 0x31, 0x16, 0x64, 0x02, 0x86, 0xed, + 0x3a, 0x87, 0xa5, 0x71, 0x30, 0x79, 0xb2, 0x1f, 0x51, 0x89 }, + { 0x9b, 0x75, 0x2e, 0x45, 0x57, 0x3d, 0x4b, 0x39, 0xf4, 0xdb, + 0xd3, 0x32, 0x3c, 0xab, 0x82, 0xbf, 0x63, 0x32, 0x6b, 0xfb }, +}; + +/* + * Checkup routine + */ +int mbedtls_ripemd160_self_test(int verbose) +{ + int i, ret = 0; + unsigned char output[20]; + + memset(output, 0, sizeof(output)); + + for (i = 0; i < TESTS; i++) { + if (verbose != 0) { + mbedtls_printf(" RIPEMD-160 test #%d: ", i + 1); + } + + ret = mbedtls_ripemd160(ripemd160_test_str[i], + ripemd160_test_strlen[i], output); + if (ret != 0) { + goto fail; + } + + if (memcmp(output, ripemd160_test_md[i], 20) != 0) { + ret = 1; + goto fail; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + return 0; + +fail: + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_RIPEMD160_C */ diff --git a/r5dev/thirdparty/mbedtls/rsa.c b/r5dev/thirdparty/mbedtls/rsa.c new file mode 100644 index 00000000..01159dfa --- /dev/null +++ b/r5dev/thirdparty/mbedtls/rsa.c @@ -0,0 +1,2580 @@ +/* + * The RSA public-key cryptosystem + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The following sources were referenced in the design of this implementation + * of the RSA algorithm: + * + * [1] A method for obtaining digital signatures and public-key cryptosystems + * R Rivest, A Shamir, and L Adleman + * http://people.csail.mit.edu/rivest/pubs.html#RSA78 + * + * [2] Handbook of Applied Cryptography - 1997, Chapter 8 + * Menezes, van Oorschot and Vanstone + * + * [3] Malware Guard Extension: Using SGX to Conceal Cache Attacks + * Michael Schwarz, Samuel Weiser, Daniel Gruss, Clémentine Maurice and + * Stefan Mangard + * https://arxiv.org/abs/1702.08719v2 + * + */ + +#include "common.h" + +#if defined(MBEDTLS_RSA_C) + +#include "mbedtls/rsa.h" +#include "rsa_alt_helpers.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" +#include "constant_time_internal.h" +#include "mbedtls/constant_time.h" +#include "hash_info.h" + +#include + +#if defined(MBEDTLS_PKCS1_V15) && !defined(__OpenBSD__) && !defined(__NetBSD__) +#include +#endif + +/* We use MD first if it's available (for compatibility reasons) + * and "fall back" to PSA otherwise (which needs psa_crypto_init()). */ +#if defined(MBEDTLS_PKCS1_V21) +#if !defined(MBEDTLS_MD_C) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_md_errors, \ + psa_generic_status_to_mbedtls) +#endif /* !MBEDTLS_MD_C */ +#endif /* MBEDTLS_PKCS1_V21 */ + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_RSA_ALT) + +int mbedtls_rsa_import(mbedtls_rsa_context *ctx, + const mbedtls_mpi *N, + const mbedtls_mpi *P, const mbedtls_mpi *Q, + const mbedtls_mpi *D, const mbedtls_mpi *E) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((N != NULL && (ret = mbedtls_mpi_copy(&ctx->N, N)) != 0) || + (P != NULL && (ret = mbedtls_mpi_copy(&ctx->P, P)) != 0) || + (Q != NULL && (ret = mbedtls_mpi_copy(&ctx->Q, Q)) != 0) || + (D != NULL && (ret = mbedtls_mpi_copy(&ctx->D, D)) != 0) || + (E != NULL && (ret = mbedtls_mpi_copy(&ctx->E, E)) != 0)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } + + if (N != NULL) { + ctx->len = mbedtls_mpi_size(&ctx->N); + } + + return 0; +} + +int mbedtls_rsa_import_raw(mbedtls_rsa_context *ctx, + unsigned char const *N, size_t N_len, + unsigned char const *P, size_t P_len, + unsigned char const *Q, size_t Q_len, + unsigned char const *D, size_t D_len, + unsigned char const *E, size_t E_len) +{ + int ret = 0; + + if (N != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->N, N, N_len)); + ctx->len = mbedtls_mpi_size(&ctx->N); + } + + if (P != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->P, P, P_len)); + } + + if (Q != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->Q, Q, Q_len)); + } + + if (D != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->D, D, D_len)); + } + + if (E != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&ctx->E, E, E_len)); + } + +cleanup: + + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } + + return 0; +} + +/* + * Checks whether the context fields are set in such a way + * that the RSA primitives will be able to execute without error. + * It does *not* make guarantees for consistency of the parameters. + */ +static int rsa_check_context(mbedtls_rsa_context const *ctx, int is_priv, + int blinding_needed) +{ +#if !defined(MBEDTLS_RSA_NO_CRT) + /* blinding_needed is only used for NO_CRT to decide whether + * P,Q need to be present or not. */ + ((void) blinding_needed); +#endif + + if (ctx->len != mbedtls_mpi_size(&ctx->N) || + ctx->len > MBEDTLS_MPI_MAX_SIZE) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* + * 1. Modular exponentiation needs positive, odd moduli. + */ + + /* Modular exponentiation wrt. N is always used for + * RSA public key operations. */ + if (mbedtls_mpi_cmp_int(&ctx->N, 0) <= 0 || + mbedtls_mpi_get_bit(&ctx->N, 0) == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + +#if !defined(MBEDTLS_RSA_NO_CRT) + /* Modular exponentiation for P and Q is only + * used for private key operations and if CRT + * is used. */ + if (is_priv && + (mbedtls_mpi_cmp_int(&ctx->P, 0) <= 0 || + mbedtls_mpi_get_bit(&ctx->P, 0) == 0 || + mbedtls_mpi_cmp_int(&ctx->Q, 0) <= 0 || + mbedtls_mpi_get_bit(&ctx->Q, 0) == 0)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } +#endif /* !MBEDTLS_RSA_NO_CRT */ + + /* + * 2. Exponents must be positive + */ + + /* Always need E for public key operations */ + if (mbedtls_mpi_cmp_int(&ctx->E, 0) <= 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_RSA_NO_CRT) + /* For private key operations, use D or DP & DQ + * as (unblinded) exponents. */ + if (is_priv && mbedtls_mpi_cmp_int(&ctx->D, 0) <= 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } +#else + if (is_priv && + (mbedtls_mpi_cmp_int(&ctx->DP, 0) <= 0 || + mbedtls_mpi_cmp_int(&ctx->DQ, 0) <= 0)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } +#endif /* MBEDTLS_RSA_NO_CRT */ + + /* Blinding shouldn't make exponents negative either, + * so check that P, Q >= 1 if that hasn't yet been + * done as part of 1. */ +#if defined(MBEDTLS_RSA_NO_CRT) + if (is_priv && blinding_needed && + (mbedtls_mpi_cmp_int(&ctx->P, 0) <= 0 || + mbedtls_mpi_cmp_int(&ctx->Q, 0) <= 0)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } +#endif + + /* It wouldn't lead to an error if it wasn't satisfied, + * but check for QP >= 1 nonetheless. */ +#if !defined(MBEDTLS_RSA_NO_CRT) + if (is_priv && + mbedtls_mpi_cmp_int(&ctx->QP, 0) <= 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } +#endif + + return 0; +} + +int mbedtls_rsa_complete(mbedtls_rsa_context *ctx) +{ + int ret = 0; + int have_N, have_P, have_Q, have_D, have_E; +#if !defined(MBEDTLS_RSA_NO_CRT) + int have_DP, have_DQ, have_QP; +#endif + int n_missing, pq_missing, d_missing, is_pub, is_priv; + + have_N = (mbedtls_mpi_cmp_int(&ctx->N, 0) != 0); + have_P = (mbedtls_mpi_cmp_int(&ctx->P, 0) != 0); + have_Q = (mbedtls_mpi_cmp_int(&ctx->Q, 0) != 0); + have_D = (mbedtls_mpi_cmp_int(&ctx->D, 0) != 0); + have_E = (mbedtls_mpi_cmp_int(&ctx->E, 0) != 0); + +#if !defined(MBEDTLS_RSA_NO_CRT) + have_DP = (mbedtls_mpi_cmp_int(&ctx->DP, 0) != 0); + have_DQ = (mbedtls_mpi_cmp_int(&ctx->DQ, 0) != 0); + have_QP = (mbedtls_mpi_cmp_int(&ctx->QP, 0) != 0); +#endif + + /* + * Check whether provided parameters are enough + * to deduce all others. The following incomplete + * parameter sets for private keys are supported: + * + * (1) P, Q missing. + * (2) D and potentially N missing. + * + */ + + n_missing = have_P && have_Q && have_D && have_E; + pq_missing = have_N && !have_P && !have_Q && have_D && have_E; + d_missing = have_P && have_Q && !have_D && have_E; + is_pub = have_N && !have_P && !have_Q && !have_D && have_E; + + /* These three alternatives are mutually exclusive */ + is_priv = n_missing || pq_missing || d_missing; + + if (!is_priv && !is_pub) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* + * Step 1: Deduce N if P, Q are provided. + */ + + if (!have_N && have_P && have_Q) { + if ((ret = mbedtls_mpi_mul_mpi(&ctx->N, &ctx->P, + &ctx->Q)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } + + ctx->len = mbedtls_mpi_size(&ctx->N); + } + + /* + * Step 2: Deduce and verify all remaining core parameters. + */ + + if (pq_missing) { + ret = mbedtls_rsa_deduce_primes(&ctx->N, &ctx->E, &ctx->D, + &ctx->P, &ctx->Q); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } + + } else if (d_missing) { + if ((ret = mbedtls_rsa_deduce_private_exponent(&ctx->P, + &ctx->Q, + &ctx->E, + &ctx->D)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } + } + + /* + * Step 3: Deduce all additional parameters specific + * to our current RSA implementation. + */ + +#if !defined(MBEDTLS_RSA_NO_CRT) + if (is_priv && !(have_DP && have_DQ && have_QP)) { + ret = mbedtls_rsa_deduce_crt(&ctx->P, &ctx->Q, &ctx->D, + &ctx->DP, &ctx->DQ, &ctx->QP); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } + } +#endif /* MBEDTLS_RSA_NO_CRT */ + + /* + * Step 3: Basic sanity checks + */ + + return rsa_check_context(ctx, is_priv, 1); +} + +int mbedtls_rsa_export_raw(const mbedtls_rsa_context *ctx, + unsigned char *N, size_t N_len, + unsigned char *P, size_t P_len, + unsigned char *Q, size_t Q_len, + unsigned char *D, size_t D_len, + unsigned char *E, size_t E_len) +{ + int ret = 0; + int is_priv; + + /* Check if key is private or public */ + is_priv = + mbedtls_mpi_cmp_int(&ctx->N, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->P, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->Q, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->D, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->E, 0) != 0; + + if (!is_priv) { + /* If we're trying to export private parameters for a public key, + * something must be wrong. */ + if (P != NULL || Q != NULL || D != NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + } + + if (N != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->N, N, N_len)); + } + + if (P != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->P, P, P_len)); + } + + if (Q != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->Q, Q, Q_len)); + } + + if (D != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->D, D, D_len)); + } + + if (E != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&ctx->E, E, E_len)); + } + +cleanup: + + return ret; +} + +int mbedtls_rsa_export(const mbedtls_rsa_context *ctx, + mbedtls_mpi *N, mbedtls_mpi *P, mbedtls_mpi *Q, + mbedtls_mpi *D, mbedtls_mpi *E) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int is_priv; + + /* Check if key is private or public */ + is_priv = + mbedtls_mpi_cmp_int(&ctx->N, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->P, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->Q, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->D, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->E, 0) != 0; + + if (!is_priv) { + /* If we're trying to export private parameters for a public key, + * something must be wrong. */ + if (P != NULL || Q != NULL || D != NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + } + + /* Export all requested core parameters. */ + + if ((N != NULL && (ret = mbedtls_mpi_copy(N, &ctx->N)) != 0) || + (P != NULL && (ret = mbedtls_mpi_copy(P, &ctx->P)) != 0) || + (Q != NULL && (ret = mbedtls_mpi_copy(Q, &ctx->Q)) != 0) || + (D != NULL && (ret = mbedtls_mpi_copy(D, &ctx->D)) != 0) || + (E != NULL && (ret = mbedtls_mpi_copy(E, &ctx->E)) != 0)) { + return ret; + } + + return 0; +} + +/* + * Export CRT parameters + * This must also be implemented if CRT is not used, for being able to + * write DER encoded RSA keys. The helper function mbedtls_rsa_deduce_crt + * can be used in this case. + */ +int mbedtls_rsa_export_crt(const mbedtls_rsa_context *ctx, + mbedtls_mpi *DP, mbedtls_mpi *DQ, mbedtls_mpi *QP) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int is_priv; + + /* Check if key is private or public */ + is_priv = + mbedtls_mpi_cmp_int(&ctx->N, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->P, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->Q, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->D, 0) != 0 && + mbedtls_mpi_cmp_int(&ctx->E, 0) != 0; + + if (!is_priv) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + +#if !defined(MBEDTLS_RSA_NO_CRT) + /* Export all requested blinding parameters. */ + if ((DP != NULL && (ret = mbedtls_mpi_copy(DP, &ctx->DP)) != 0) || + (DQ != NULL && (ret = mbedtls_mpi_copy(DQ, &ctx->DQ)) != 0) || + (QP != NULL && (ret = mbedtls_mpi_copy(QP, &ctx->QP)) != 0)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } +#else + if ((ret = mbedtls_rsa_deduce_crt(&ctx->P, &ctx->Q, &ctx->D, + DP, DQ, QP)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_BAD_INPUT_DATA, ret); + } +#endif + + return 0; +} + +/* + * Initialize an RSA context + */ +void mbedtls_rsa_init(mbedtls_rsa_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_rsa_context)); + + ctx->padding = MBEDTLS_RSA_PKCS_V15; + ctx->hash_id = MBEDTLS_MD_NONE; + +#if defined(MBEDTLS_THREADING_C) + /* Set ctx->ver to nonzero to indicate that the mutex has been + * initialized and will need to be freed. */ + ctx->ver = 1; + mbedtls_mutex_init(&ctx->mutex); +#endif +} + +/* + * Set padding for an existing RSA context + */ +int mbedtls_rsa_set_padding(mbedtls_rsa_context *ctx, int padding, + mbedtls_md_type_t hash_id) +{ + switch (padding) { +#if defined(MBEDTLS_PKCS1_V15) + case MBEDTLS_RSA_PKCS_V15: + break; +#endif + +#if defined(MBEDTLS_PKCS1_V21) + case MBEDTLS_RSA_PKCS_V21: + break; +#endif + default: + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } + +#if defined(MBEDTLS_PKCS1_V21) + if ((padding == MBEDTLS_RSA_PKCS_V21) && + (hash_id != MBEDTLS_MD_NONE)) { + /* Just make sure this hash is supported in this build. */ + if (mbedtls_hash_info_psa_from_md(hash_id) == PSA_ALG_NONE) { + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } + } +#endif /* MBEDTLS_PKCS1_V21 */ + + ctx->padding = padding; + ctx->hash_id = hash_id; + + return 0; +} + +/* + * Get padding mode of initialized RSA context + */ +int mbedtls_rsa_get_padding_mode(const mbedtls_rsa_context *ctx) +{ + return ctx->padding; +} + +/* + * Get hash identifier of mbedtls_md_type_t type + */ +int mbedtls_rsa_get_md_alg(const mbedtls_rsa_context *ctx) +{ + return ctx->hash_id; +} + +/* + * Get length in bytes of RSA modulus + */ +size_t mbedtls_rsa_get_len(const mbedtls_rsa_context *ctx) +{ + return ctx->len; +} + + +#if defined(MBEDTLS_GENPRIME) + +/* + * Generate an RSA keypair + * + * This generation method follows the RSA key pair generation procedure of + * FIPS 186-4 if 2^16 < exponent < 2^256 and nbits = 2048 or nbits = 3072. + */ +int mbedtls_rsa_gen_key(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + unsigned int nbits, int exponent) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_mpi H, G, L; + int prime_quality = 0; + + /* + * If the modulus is 1024 bit long or shorter, then the security strength of + * the RSA algorithm is less than or equal to 80 bits and therefore an error + * rate of 2^-80 is sufficient. + */ + if (nbits > 1024) { + prime_quality = MBEDTLS_MPI_GEN_PRIME_FLAG_LOW_ERR; + } + + mbedtls_mpi_init(&H); + mbedtls_mpi_init(&G); + mbedtls_mpi_init(&L); + + if (nbits < 128 || exponent < 3 || nbits % 2 != 0) { + ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + goto cleanup; + } + + /* + * find primes P and Q with Q < P so that: + * 1. |P-Q| > 2^( nbits / 2 - 100 ) + * 2. GCD( E, (P-1)*(Q-1) ) == 1 + * 3. E^-1 mod LCM(P-1, Q-1) > 2^( nbits / 2 ) + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&ctx->E, exponent)); + + do { + MBEDTLS_MPI_CHK(mbedtls_mpi_gen_prime(&ctx->P, nbits >> 1, + prime_quality, f_rng, p_rng)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_gen_prime(&ctx->Q, nbits >> 1, + prime_quality, f_rng, p_rng)); + + /* make sure the difference between p and q is not too small (FIPS 186-4 §B.3.3 step 5.4) */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&H, &ctx->P, &ctx->Q)); + if (mbedtls_mpi_bitlen(&H) <= ((nbits >= 200) ? ((nbits >> 1) - 99) : 0)) { + continue; + } + + /* not required by any standards, but some users rely on the fact that P > Q */ + if (H.s < 0) { + 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) { + continue; + } + + /* 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)) + 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)); + + 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) + /* + * DP = D mod (P - 1) + * DQ = D mod (Q - 1) + * QP = Q^-1 mod P + */ + MBEDTLS_MPI_CHK(mbedtls_rsa_deduce_crt(&ctx->P, &ctx->Q, &ctx->D, + &ctx->DP, &ctx->DQ, &ctx->QP)); +#endif /* MBEDTLS_RSA_NO_CRT */ + + /* Double-check */ + MBEDTLS_MPI_CHK(mbedtls_rsa_check_privkey(ctx)); + +cleanup: + + mbedtls_mpi_free(&H); + mbedtls_mpi_free(&G); + mbedtls_mpi_free(&L); + + if (ret != 0) { + mbedtls_rsa_free(ctx); + + if ((-ret & ~0x7f) == 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_KEY_GEN_FAILED, ret); + } + return ret; + } + + return 0; +} + +#endif /* MBEDTLS_GENPRIME */ + +/* + * Check a public RSA key + */ +int mbedtls_rsa_check_pubkey(const mbedtls_rsa_context *ctx) +{ + if (rsa_check_context(ctx, 0 /* public */, 0 /* no blinding */) != 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + if (mbedtls_mpi_bitlen(&ctx->N) < 128) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + if (mbedtls_mpi_get_bit(&ctx->E, 0) == 0 || + mbedtls_mpi_bitlen(&ctx->E) < 2 || + mbedtls_mpi_cmp_mpi(&ctx->E, &ctx->N) >= 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + return 0; +} + +/* + * Check for the consistency of all fields in an RSA private key context + */ +int mbedtls_rsa_check_privkey(const mbedtls_rsa_context *ctx) +{ + if (mbedtls_rsa_check_pubkey(ctx) != 0 || + rsa_check_context(ctx, 1 /* private */, 1 /* blinding */) != 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + if (mbedtls_rsa_validate_params(&ctx->N, &ctx->P, &ctx->Q, + &ctx->D, &ctx->E, NULL, NULL) != 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + +#if !defined(MBEDTLS_RSA_NO_CRT) + else if (mbedtls_rsa_validate_crt(&ctx->P, &ctx->Q, &ctx->D, + &ctx->DP, &ctx->DQ, &ctx->QP) != 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } +#endif + + return 0; +} + +/* + * Check if contexts holding a public and private key match + */ +int mbedtls_rsa_check_pub_priv(const mbedtls_rsa_context *pub, + const mbedtls_rsa_context *prv) +{ + if (mbedtls_rsa_check_pubkey(pub) != 0 || + mbedtls_rsa_check_privkey(prv) != 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + if (mbedtls_mpi_cmp_mpi(&pub->N, &prv->N) != 0 || + mbedtls_mpi_cmp_mpi(&pub->E, &prv->E) != 0) { + return MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + return 0; +} + +/* + * Do an RSA public key operation + */ +int mbedtls_rsa_public(mbedtls_rsa_context *ctx, + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen; + mbedtls_mpi T; + + if (rsa_check_context(ctx, 0 /* public */, 0 /* no blinding */)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + mbedtls_mpi_init(&T); + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&T, input, ctx->len)); + + if (mbedtls_mpi_cmp_mpi(&T, &ctx->N) >= 0) { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + + olen = ctx->len; + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&T, &T, &ctx->E, &ctx->N, &ctx->RN)); + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&T, output, olen)); + +cleanup: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + mbedtls_mpi_free(&T); + + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_PUBLIC_FAILED, ret); + } + + return 0; +} + +/* + * Generate or update blinding values, see section 10 of: + * KOCHER, Paul C. Timing attacks on implementations of Diffie-Hellman, RSA, + * DSS, and other systems. In : Advances in Cryptology-CRYPTO'96. Springer + * Berlin Heidelberg, 1996. p. 104-113. + */ +static int rsa_prepare_blinding(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng) +{ + int ret, count = 0; + mbedtls_mpi R; + + mbedtls_mpi_init(&R); + + if (ctx->Vf.p != NULL) { + /* We already have blinding values, just update them by squaring */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vi, &ctx->Vi, &ctx->Vi)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vi, &ctx->Vi, &ctx->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&ctx->Vf, &ctx->Vf, &ctx->Vf)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&ctx->Vf, &ctx->Vf, &ctx->N)); + + goto cleanup; + } + + /* Unblinding value: Vf = random number, invertible mod N */ + 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)); + + /* Blinding value: Vi = Vf^(-e) mod N + * (Vi already contains Vf^-1 at this point) */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&ctx->Vi, &ctx->Vi, &ctx->E, &ctx->N, &ctx->RN)); + + +cleanup: + mbedtls_mpi_free(&R); + + return ret; +} + +/* + * Exponent blinding supposed to prevent side-channel attacks using multiple + * traces of measurements to recover the RSA key. The more collisions are there, + * the more bits of the key can be recovered. See [3]. + * + * Collecting n collisions with m bit long blinding value requires 2^(m-m/n) + * observations on average. + * + * For example with 28 byte blinding to achieve 2 collisions the adversary has + * to make 2^112 observations on average. + * + * (With the currently (as of 2017 April) known best algorithms breaking 2048 + * bit RSA requires approximately as much time as trying out 2^112 random keys. + * Thus in this sense with 28 byte blinding the security is not reduced by + * side-channel attacks like the one in [3]) + * + * This countermeasure does not help if the key recovery is possible with a + * single trace. + */ +#define RSA_EXPONENT_BLINDING 28 + +/* + * Do an RSA private key operation + */ +int mbedtls_rsa_private(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *input, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen; + + /* Temporary holding the result */ + mbedtls_mpi T; + + /* Temporaries holding P-1, Q-1 and the + * exponent blinding factor, respectively. */ + mbedtls_mpi P1, Q1, R; + +#if !defined(MBEDTLS_RSA_NO_CRT) + /* Temporaries holding the results mod p resp. mod q. */ + mbedtls_mpi TP, TQ; + + /* Temporaries holding the blinded exponents for + * the mod p resp. mod q computation (if used). */ + mbedtls_mpi DP_blind, DQ_blind; + + /* Pointers to actual exponents to be used - either the unblinded + * or the blinded ones, depending on the presence of a PRNG. */ + mbedtls_mpi *DP = &ctx->DP; + mbedtls_mpi *DQ = &ctx->DQ; +#else + /* Temporary holding the blinded exponent (if used). */ + mbedtls_mpi D_blind; + + /* Pointer to actual exponent to be used - either the unblinded + * or the blinded one, depending on the presence of a PRNG. */ + mbedtls_mpi *D = &ctx->D; +#endif /* MBEDTLS_RSA_NO_CRT */ + + /* Temporaries holding the initial input and the double + * checked result; should be the same in the end. */ + mbedtls_mpi I, C; + + if (f_rng == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (rsa_check_context(ctx, 1 /* private key checks */, + 1 /* blinding on */) != 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + /* MPI Initialization */ + mbedtls_mpi_init(&T); + + mbedtls_mpi_init(&P1); + mbedtls_mpi_init(&Q1); + mbedtls_mpi_init(&R); + +#if defined(MBEDTLS_RSA_NO_CRT) + mbedtls_mpi_init(&D_blind); +#else + mbedtls_mpi_init(&DP_blind); + mbedtls_mpi_init(&DQ_blind); +#endif + +#if !defined(MBEDTLS_RSA_NO_CRT) + mbedtls_mpi_init(&TP); mbedtls_mpi_init(&TQ); +#endif + + mbedtls_mpi_init(&I); + mbedtls_mpi_init(&C); + + /* End of MPI initialization */ + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&T, input, ctx->len)); + if (mbedtls_mpi_cmp_mpi(&T, &ctx->N) >= 0) { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&I, &T)); + + /* + * Blinding + * T = T * Vi mod N + */ + MBEDTLS_MPI_CHK(rsa_prepare_blinding(ctx, f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&T, &T, &ctx->Vi)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&T, &T, &ctx->N)); + + /* + * Exponent blinding + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&P1, &ctx->P, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&Q1, &ctx->Q, 1)); + +#if defined(MBEDTLS_RSA_NO_CRT) + /* + * D_blind = ( P - 1 ) * ( Q - 1 ) * R + D + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&R, RSA_EXPONENT_BLINDING, + f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&D_blind, &P1, &Q1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&D_blind, &D_blind, &R)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&D_blind, &D_blind, &ctx->D)); + + D = &D_blind; +#else + /* + * DP_blind = ( P - 1 ) * R + DP + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&R, RSA_EXPONENT_BLINDING, + f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&DP_blind, &P1, &R)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&DP_blind, &DP_blind, + &ctx->DP)); + + DP = &DP_blind; + + /* + * DQ_blind = ( Q - 1 ) * R + DQ + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_fill_random(&R, RSA_EXPONENT_BLINDING, + f_rng, p_rng)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&DQ_blind, &Q1, &R)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&DQ_blind, &DQ_blind, + &ctx->DQ)); + + DQ = &DQ_blind; +#endif /* MBEDTLS_RSA_NO_CRT */ + +#if defined(MBEDTLS_RSA_NO_CRT) + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&T, &T, D, &ctx->N, &ctx->RN)); +#else + /* + * Faster decryption using the CRT + * + * TP = input ^ dP mod P + * TQ = input ^ dQ mod Q + */ + + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&TP, &T, DP, &ctx->P, &ctx->RP)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&TQ, &T, DQ, &ctx->Q, &ctx->RQ)); + + /* + * T = (TP - TQ) * (Q^-1 mod P) mod P + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&T, &TP, &TQ)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&TP, &T, &ctx->QP)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&T, &TP, &ctx->P)); + + /* + * T = TQ + T * Q + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&TP, &T, &ctx->Q)); + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&T, &TQ, &TP)); +#endif /* MBEDTLS_RSA_NO_CRT */ + + /* + * Unblind + * T = T * Vf mod N + */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&T, &T, &ctx->Vf)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&T, &T, &ctx->N)); + + /* Verify the result to prevent glitching attacks. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&C, &T, &ctx->E, + &ctx->N, &ctx->RN)); + if (mbedtls_mpi_cmp_mpi(&C, &I) != 0) { + ret = MBEDTLS_ERR_RSA_VERIFY_FAILED; + goto cleanup; + } + + olen = ctx->len; + MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&T, output, olen)); + +cleanup: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + mbedtls_mpi_free(&P1); + mbedtls_mpi_free(&Q1); + mbedtls_mpi_free(&R); + +#if defined(MBEDTLS_RSA_NO_CRT) + mbedtls_mpi_free(&D_blind); +#else + mbedtls_mpi_free(&DP_blind); + mbedtls_mpi_free(&DQ_blind); +#endif + + mbedtls_mpi_free(&T); + +#if !defined(MBEDTLS_RSA_NO_CRT) + mbedtls_mpi_free(&TP); mbedtls_mpi_free(&TQ); +#endif + + mbedtls_mpi_free(&C); + mbedtls_mpi_free(&I); + + if (ret != 0 && ret >= -0x007f) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_PRIVATE_FAILED, ret); + } + + return ret; +} + +#if defined(MBEDTLS_PKCS1_V21) +/** + * Generate and apply the MGF1 operation (from PKCS#1 v2.1) to a buffer. + * + * \param dst buffer to mask + * \param dlen length of destination buffer + * \param src source of the mask generation + * \param slen length of the source buffer + * \param md_alg message digest to use + */ +static int mgf_mask(unsigned char *dst, size_t dlen, unsigned char *src, + size_t slen, mbedtls_md_type_t md_alg) +{ + unsigned char counter[4]; + unsigned char *p; + unsigned int hlen; + size_t i, use_len; + unsigned char mask[MBEDTLS_HASH_MAX_SIZE]; +#if defined(MBEDTLS_MD_C) + int ret = 0; + const mbedtls_md_info_t *md_info; + mbedtls_md_context_t md_ctx; + + mbedtls_md_init(&md_ctx); + md_info = mbedtls_md_info_from_type(md_alg); + if (md_info == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + mbedtls_md_init(&md_ctx); + if ((ret = mbedtls_md_setup(&md_ctx, md_info, 0)) != 0) { + goto exit; + } + + hlen = mbedtls_md_get_size(md_info); +#else + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_algorithm_t alg = mbedtls_psa_translate_md(md_alg); + psa_status_t status = PSA_SUCCESS; + size_t out_len; + + hlen = PSA_HASH_LENGTH(alg); +#endif + + memset(mask, 0, sizeof(mask)); + memset(counter, 0, 4); + + /* Generate and apply dbMask */ + p = dst; + + while (dlen > 0) { + use_len = hlen; + if (dlen < hlen) { + use_len = dlen; + } + +#if defined(MBEDTLS_MD_C) + if ((ret = mbedtls_md_starts(&md_ctx)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md_ctx, src, slen)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md_ctx, counter, 4)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_finish(&md_ctx, mask)) != 0) { + goto exit; + } +#else + if ((status = psa_hash_setup(&op, alg)) != PSA_SUCCESS) { + goto exit; + } + if ((status = psa_hash_update(&op, src, slen)) != PSA_SUCCESS) { + goto exit; + } + if ((status = psa_hash_update(&op, counter, 4)) != PSA_SUCCESS) { + goto exit; + } + status = psa_hash_finish(&op, mask, sizeof(mask), &out_len); + if (status != PSA_SUCCESS) { + goto exit; + } +#endif + + for (i = 0; i < use_len; ++i) { + *p++ ^= mask[i]; + } + + counter[3]++; + + dlen -= use_len; + } + +exit: + mbedtls_platform_zeroize(mask, sizeof(mask)); +#if defined(MBEDTLS_MD_C) + mbedtls_md_free(&md_ctx); + + return ret; +#else + psa_hash_abort(&op); + + return PSA_TO_MBEDTLS_ERR(status); +#endif +} + +/** + * Generate Hash(M') as in RFC 8017 page 43 points 5 and 6. + * + * \param hash the input hash + * \param hlen length of the input hash + * \param salt the input salt + * \param slen length of the input salt + * \param out the output buffer - must be large enough for \p md_alg + * \param md_alg message digest to use + */ +static int hash_mprime(const unsigned char *hash, size_t hlen, + const unsigned char *salt, size_t slen, + unsigned char *out, mbedtls_md_type_t md_alg) +{ + const unsigned char zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +#if defined(MBEDTLS_MD_C) + mbedtls_md_context_t md_ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_alg); + if (md_info == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + mbedtls_md_init(&md_ctx); + if ((ret = mbedtls_md_setup(&md_ctx, md_info, 0)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_starts(&md_ctx)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md_ctx, zeros, sizeof(zeros))) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md_ctx, hash, hlen)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_update(&md_ctx, salt, slen)) != 0) { + goto exit; + } + if ((ret = mbedtls_md_finish(&md_ctx, out)) != 0) { + goto exit; + } + +exit: + mbedtls_md_free(&md_ctx); + + return ret; +#else + psa_hash_operation_t op = PSA_HASH_OPERATION_INIT; + psa_algorithm_t alg = mbedtls_psa_translate_md(md_alg); + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t out_size = PSA_HASH_LENGTH(alg); + size_t out_len; + + if ((status = psa_hash_setup(&op, alg)) != PSA_SUCCESS) { + goto exit; + } + if ((status = psa_hash_update(&op, zeros, sizeof(zeros))) != PSA_SUCCESS) { + goto exit; + } + if ((status = psa_hash_update(&op, hash, hlen)) != PSA_SUCCESS) { + goto exit; + } + if ((status = psa_hash_update(&op, salt, slen)) != PSA_SUCCESS) { + goto exit; + } + status = psa_hash_finish(&op, out, out_size, &out_len); + if (status != PSA_SUCCESS) { + goto exit; + } + +exit: + psa_hash_abort(&op); + + return PSA_TO_MBEDTLS_ERR(status); +#endif /* !MBEDTLS_MD_C */ +} + +/** + * Compute a hash. + * + * \param md_alg algorithm to use + * \param input input message to hash + * \param ilen input length + * \param output the output buffer - must be large enough for \p md_alg + */ +static int compute_hash(mbedtls_md_type_t md_alg, + const unsigned char *input, size_t ilen, + unsigned char *output) +{ +#if defined(MBEDTLS_MD_C) + const mbedtls_md_info_t *md_info; + + md_info = mbedtls_md_info_from_type(md_alg); + if (md_info == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + return mbedtls_md(md_info, input, ilen, output); +#else + psa_algorithm_t alg = mbedtls_psa_translate_md(md_alg); + psa_status_t status; + size_t out_size = PSA_HASH_LENGTH(alg); + size_t out_len; + + status = psa_hash_compute(alg, input, ilen, output, out_size, &out_len); + + return PSA_TO_MBEDTLS_ERR(status); +#endif /* !MBEDTLS_MD_C */ +} +#endif /* MBEDTLS_PKCS1_V21 */ + +#if defined(MBEDTLS_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-ENCRYPT function + */ +int mbedtls_rsa_rsaes_oaep_encrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *label, size_t label_len, + size_t ilen, + const unsigned char *input, + unsigned char *output) +{ + size_t olen; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = output; + unsigned int hlen; + + if (f_rng == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + hlen = mbedtls_hash_info_get_size((mbedtls_md_type_t) ctx->hash_id); + if (hlen == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + olen = ctx->len; + + /* first comparison checks for overflow */ + if (ilen + 2 * hlen + 2 < ilen || olen < ilen + 2 * hlen + 2) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + memset(output, 0, olen); + + *p++ = 0; + + /* Generate a random octet string seed */ + if ((ret = f_rng(p_rng, p, hlen)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_RNG_FAILED, ret); + } + + p += hlen; + + /* Construct DB */ + ret = compute_hash((mbedtls_md_type_t) ctx->hash_id, label, label_len, p); + if (ret != 0) { + return ret; + } + p += hlen; + p += olen - 2 * hlen - 2 - ilen; + *p++ = 1; + if (ilen != 0) { + memcpy(p, input, ilen); + } + + /* maskedDB: Apply dbMask to DB */ + if ((ret = mgf_mask(output + hlen + 1, olen - hlen - 1, output + 1, hlen, + ctx->hash_id)) != 0) { + return ret; + } + + /* maskedSeed: Apply seedMask to seed */ + if ((ret = mgf_mask(output + 1, hlen, output + hlen + 1, olen - hlen - 1, + ctx->hash_id)) != 0) { + return ret; + } + + return mbedtls_rsa_public(ctx, output, output); +} +#endif /* MBEDTLS_PKCS1_V21 */ + +#if defined(MBEDTLS_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-ENCRYPT function + */ +int mbedtls_rsa_rsaes_pkcs1_v15_encrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, size_t ilen, + const unsigned char *input, + unsigned char *output) +{ + size_t nb_pad, olen; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = output; + + olen = ctx->len; + + /* first comparison checks for overflow */ + if (ilen + 11 < ilen || olen < ilen + 11) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + nb_pad = olen - 3 - ilen; + + *p++ = 0; + + if (f_rng == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + *p++ = MBEDTLS_RSA_CRYPT; + + while (nb_pad-- > 0) { + int rng_dl = 100; + + do { + ret = f_rng(p_rng, p, 1); + } while (*p == 0 && --rng_dl && ret == 0); + + /* Check if RNG failed to generate data */ + if (rng_dl == 0 || ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_RNG_FAILED, ret); + } + + p++; + } + + *p++ = 0; + if (ilen != 0) { + memcpy(p, input, ilen); + } + + return mbedtls_rsa_public(ctx, output, output); +} +#endif /* MBEDTLS_PKCS1_V15 */ + +/* + * Add the message padding, then do an RSA operation + */ +int mbedtls_rsa_pkcs1_encrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t ilen, + const unsigned char *input, + unsigned char *output) +{ + switch (ctx->padding) { +#if defined(MBEDTLS_PKCS1_V15) + case MBEDTLS_RSA_PKCS_V15: + return mbedtls_rsa_rsaes_pkcs1_v15_encrypt(ctx, f_rng, p_rng, + ilen, input, output); +#endif + +#if defined(MBEDTLS_PKCS1_V21) + case MBEDTLS_RSA_PKCS_V21: + return mbedtls_rsa_rsaes_oaep_encrypt(ctx, f_rng, p_rng, NULL, 0, + ilen, input, output); +#endif + + default: + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } +} + +#if defined(MBEDTLS_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSAES-OAEP-DECRYPT function + */ +int mbedtls_rsa_rsaes_oaep_decrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + const unsigned char *label, size_t label_len, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t ilen, i, pad_len; + unsigned char *p, bad, pad_done; + unsigned char buf[MBEDTLS_MPI_MAX_SIZE]; + unsigned char lhash[MBEDTLS_HASH_MAX_SIZE]; + unsigned int hlen; + + /* + * Parameters sanity checks + */ + if (ctx->padding != MBEDTLS_RSA_PKCS_V21) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + ilen = ctx->len; + + if (ilen < 16 || ilen > sizeof(buf)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + hlen = mbedtls_hash_info_get_size((mbedtls_md_type_t) ctx->hash_id); + if (hlen == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + // checking for integer underflow + if (2 * hlen + 2 > ilen) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* + * RSA operation + */ + ret = mbedtls_rsa_private(ctx, f_rng, p_rng, input, buf); + + if (ret != 0) { + goto cleanup; + } + + /* + * Unmask data and generate lHash + */ + /* seed: Apply seedMask to maskedSeed */ + if ((ret = mgf_mask(buf + 1, hlen, buf + hlen + 1, ilen - hlen - 1, + ctx->hash_id)) != 0 || + /* DB: Apply dbMask to maskedDB */ + (ret = mgf_mask(buf + hlen + 1, ilen - hlen - 1, buf + 1, hlen, + ctx->hash_id)) != 0) { + goto cleanup; + } + + /* Generate lHash */ + ret = compute_hash((mbedtls_md_type_t) ctx->hash_id, + label, label_len, lhash); + if (ret != 0) { + goto cleanup; + } + + /* + * Check contents, in "constant-time" + */ + p = buf; + bad = 0; + + bad |= *p++; /* First byte must be 0 */ + + p += hlen; /* Skip seed */ + + /* Check lHash */ + for (i = 0; i < hlen; i++) { + bad |= lhash[i] ^ *p++; + } + + /* Get zero-padding len, but always read till end of buffer + * (minus one, for the 01 byte) */ + pad_len = 0; + pad_done = 0; + for (i = 0; i < ilen - 2 * hlen - 2; i++) { + pad_done |= p[i]; + pad_len += ((pad_done | (unsigned char) -pad_done) >> 7) ^ 1; + } + + p += pad_len; + bad |= *p++ ^ 0x01; + + /* + * The only information "leaked" is whether the padding was correct or not + * (eg, no data is copied if it was not correct). This meets the + * recommendations in PKCS#1 v2.2: an opponent cannot distinguish between + * the different error conditions. + */ + if (bad != 0) { + ret = MBEDTLS_ERR_RSA_INVALID_PADDING; + goto cleanup; + } + + if (ilen - (p - buf) > output_max_len) { + ret = MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE; + goto cleanup; + } + + *olen = ilen - (p - buf); + if (*olen != 0) { + memcpy(output, p, *olen); + } + ret = 0; + +cleanup: + mbedtls_platform_zeroize(buf, sizeof(buf)); + mbedtls_platform_zeroize(lhash, sizeof(lhash)); + + return ret; +} +#endif /* MBEDTLS_PKCS1_V21 */ + +#if defined(MBEDTLS_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function + */ +int mbedtls_rsa_rsaes_pkcs1_v15_decrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t ilen; + unsigned char buf[MBEDTLS_MPI_MAX_SIZE]; + + ilen = ctx->len; + + if (ctx->padding != MBEDTLS_RSA_PKCS_V15) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (ilen < 16 || ilen > sizeof(buf)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + ret = mbedtls_rsa_private(ctx, f_rng, p_rng, input, buf); + + if (ret != 0) { + goto cleanup; + } + + ret = mbedtls_ct_rsaes_pkcs1_v15_unpadding(buf, ilen, + output, output_max_len, olen); + +cleanup: + mbedtls_platform_zeroize(buf, sizeof(buf)); + + return ret; +} +#endif /* MBEDTLS_PKCS1_V15 */ + +/* + * Do an RSA operation, then remove the message padding + */ +int mbedtls_rsa_pkcs1_decrypt(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + size_t *olen, + const unsigned char *input, + unsigned char *output, + size_t output_max_len) +{ + switch (ctx->padding) { +#if defined(MBEDTLS_PKCS1_V15) + case MBEDTLS_RSA_PKCS_V15: + return mbedtls_rsa_rsaes_pkcs1_v15_decrypt(ctx, f_rng, p_rng, olen, + input, output, output_max_len); +#endif + +#if defined(MBEDTLS_PKCS1_V21) + case MBEDTLS_RSA_PKCS_V21: + return mbedtls_rsa_rsaes_oaep_decrypt(ctx, f_rng, p_rng, NULL, 0, + olen, input, output, + output_max_len); +#endif + + default: + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } +} + +#if defined(MBEDTLS_PKCS1_V21) +static int rsa_rsassa_pss_sign(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + int saltlen, + unsigned char *sig) +{ + size_t olen; + unsigned char *p = sig; + unsigned char *salt = NULL; + size_t slen, min_slen, hlen, offset = 0; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t msb; + + if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (ctx->padding != MBEDTLS_RSA_PKCS_V21) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (f_rng == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + olen = ctx->len; + + if (md_alg != MBEDTLS_MD_NONE) { + /* Gather length of hash to sign */ + size_t exp_hashlen = mbedtls_hash_info_get_size(md_alg); + if (exp_hashlen == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (hashlen != exp_hashlen) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + } + + hlen = mbedtls_hash_info_get_size((mbedtls_md_type_t) ctx->hash_id); + if (hlen == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (saltlen == MBEDTLS_RSA_SALT_LEN_ANY) { + /* Calculate the largest possible salt length, up to the hash size. + * Normally this is the hash length, which is the maximum salt length + * according to FIPS 185-4 §5.5 (e) and common practice. If there is not + * enough room, use the maximum salt length that fits. The constraint is + * that the hash length plus the salt length plus 2 bytes must be at most + * the key length. This complies with FIPS 186-4 §5.5 (e) and RFC 8017 + * (PKCS#1 v2.2) §9.1.1 step 3. */ + min_slen = hlen - 2; + if (olen < hlen + min_slen + 2) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } else if (olen >= hlen + hlen + 2) { + slen = hlen; + } else { + slen = olen - hlen - 2; + } + } else if ((saltlen < 0) || (saltlen + hlen + 2 > olen)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } else { + slen = (size_t) saltlen; + } + + memset(sig, 0, olen); + + /* Note: EMSA-PSS encoding is over the length of N - 1 bits */ + msb = mbedtls_mpi_bitlen(&ctx->N) - 1; + p += olen - hlen - slen - 2; + *p++ = 0x01; + + /* Generate salt of length slen in place in the encoded message */ + salt = p; + if ((ret = f_rng(p_rng, salt, slen)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_RNG_FAILED, ret); + } + + p += slen; + + /* Generate H = Hash( M' ) */ + ret = hash_mprime(hash, hashlen, salt, slen, p, ctx->hash_id); + if (ret != 0) { + return ret; + } + + /* Compensate for boundary condition when applying mask */ + if (msb % 8 == 0) { + offset = 1; + } + + /* maskedDB: Apply dbMask to DB */ + ret = mgf_mask(sig + offset, olen - hlen - 1 - offset, p, hlen, + ctx->hash_id); + if (ret != 0) { + return ret; + } + + msb = mbedtls_mpi_bitlen(&ctx->N) - 1; + sig[0] &= 0xFF >> (olen * 8 - msb); + + p += hlen; + *p++ = 0xBC; + + return mbedtls_rsa_private(ctx, f_rng, p_rng, sig, sig); +} + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-SIGN function with + * the option to pass in the salt length. + */ +int mbedtls_rsa_rsassa_pss_sign_ext(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + int saltlen, + unsigned char *sig) +{ + return rsa_rsassa_pss_sign(ctx, f_rng, p_rng, md_alg, + hashlen, hash, saltlen, sig); +} + + +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-SIGN function + */ +int mbedtls_rsa_rsassa_pss_sign(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig) +{ + return rsa_rsassa_pss_sign(ctx, f_rng, p_rng, md_alg, + hashlen, hash, MBEDTLS_RSA_SALT_LEN_ANY, sig); +} +#endif /* MBEDTLS_PKCS1_V21 */ + +#if defined(MBEDTLS_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-V1_5-SIGN function + */ + +/* Construct a PKCS v1.5 encoding of a hashed message + * + * This is used both for signature generation and verification. + * + * Parameters: + * - md_alg: Identifies the hash algorithm used to generate the given hash; + * MBEDTLS_MD_NONE if raw data is signed. + * - hashlen: Length of hash. Must match md_alg if that's not NONE. + * - hash: Buffer containing the hashed message or the raw data. + * - dst_len: Length of the encoded message. + * - dst: Buffer to hold the encoded message. + * + * Assumptions: + * - hash has size hashlen. + * - dst points to a buffer of size at least dst_len. + * + */ +static int rsa_rsassa_pkcs1_v15_encode(mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + size_t dst_len, + unsigned char *dst) +{ + size_t oid_size = 0; + size_t nb_pad = dst_len; + unsigned char *p = dst; + const char *oid = NULL; + + /* Are we signing hashed or raw data? */ + if (md_alg != MBEDTLS_MD_NONE) { + unsigned char md_size = mbedtls_hash_info_get_size(md_alg); + if (md_size == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (mbedtls_oid_get_oid_by_md(md_alg, &oid, &oid_size) != 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (hashlen != md_size) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* Double-check that 8 + hashlen + oid_size can be used as a + * 1-byte ASN.1 length encoding and that there's no overflow. */ + if (8 + hashlen + oid_size >= 0x80 || + 10 + hashlen < hashlen || + 10 + hashlen + oid_size < 10 + hashlen) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* + * Static bounds check: + * - Need 10 bytes for five tag-length pairs. + * (Insist on 1-byte length encodings to protect against variants of + * Bleichenbacher's forgery attack against lax PKCS#1v1.5 verification) + * - Need hashlen bytes for hash + * - Need oid_size bytes for hash alg OID. + */ + if (nb_pad < 10 + hashlen + oid_size) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + nb_pad -= 10 + hashlen + oid_size; + } else { + if (nb_pad < hashlen) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + nb_pad -= hashlen; + } + + /* Need space for signature header and padding delimiter (3 bytes), + * and 8 bytes for the minimal padding */ + if (nb_pad < 3 + 8) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + nb_pad -= 3; + + /* Now nb_pad is the amount of memory to be filled + * with padding, and at least 8 bytes long. */ + + /* Write signature header and padding */ + *p++ = 0; + *p++ = MBEDTLS_RSA_SIGN; + memset(p, 0xFF, nb_pad); + p += nb_pad; + *p++ = 0; + + /* Are we signing raw data? */ + if (md_alg == MBEDTLS_MD_NONE) { + memcpy(p, hash, hashlen); + return 0; + } + + /* Signing hashed data, add corresponding ASN.1 structure + * + * DigestInfo ::= SEQUENCE { + * digestAlgorithm DigestAlgorithmIdentifier, + * digest Digest } + * DigestAlgorithmIdentifier ::= AlgorithmIdentifier + * Digest ::= OCTET STRING + * + * Schematic: + * TAG-SEQ + LEN [ TAG-SEQ + LEN [ TAG-OID + LEN [ OID ] + * TAG-NULL + LEN [ NULL ] ] + * TAG-OCTET + LEN [ HASH ] ] + */ + *p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED; + *p++ = (unsigned char) (0x08 + oid_size + hashlen); + *p++ = MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED; + *p++ = (unsigned char) (0x04 + oid_size); + *p++ = MBEDTLS_ASN1_OID; + *p++ = (unsigned char) oid_size; + memcpy(p, oid, oid_size); + p += oid_size; + *p++ = MBEDTLS_ASN1_NULL; + *p++ = 0x00; + *p++ = MBEDTLS_ASN1_OCTET_STRING; + *p++ = (unsigned char) hashlen; + memcpy(p, hash, hashlen); + p += hashlen; + + /* Just a sanity-check, should be automatic + * after the initial bounds check. */ + if (p != dst + dst_len) { + mbedtls_platform_zeroize(dst, dst_len); + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + return 0; +} + +/* + * Do an RSA operation to sign the message digest + */ +int mbedtls_rsa_rsassa_pkcs1_v15_sign(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *sig_try = NULL, *verif = NULL; + + if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (ctx->padding != MBEDTLS_RSA_PKCS_V15) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* + * Prepare PKCS1-v1.5 encoding (padding and hash identifier) + */ + + if ((ret = rsa_rsassa_pkcs1_v15_encode(md_alg, hashlen, hash, + ctx->len, sig)) != 0) { + return ret; + } + + /* Private key operation + * + * In order to prevent Lenstra's attack, make the signature in a + * temporary buffer and check it before returning it. + */ + + sig_try = mbedtls_calloc(1, ctx->len); + if (sig_try == NULL) { + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + verif = mbedtls_calloc(1, ctx->len); + if (verif == NULL) { + mbedtls_free(sig_try); + return MBEDTLS_ERR_MPI_ALLOC_FAILED; + } + + MBEDTLS_MPI_CHK(mbedtls_rsa_private(ctx, f_rng, p_rng, sig, sig_try)); + MBEDTLS_MPI_CHK(mbedtls_rsa_public(ctx, sig_try, verif)); + + if (mbedtls_ct_memcmp(verif, sig, ctx->len) != 0) { + ret = MBEDTLS_ERR_RSA_PRIVATE_FAILED; + goto cleanup; + } + + memcpy(sig, sig_try, ctx->len); + +cleanup: + mbedtls_platform_zeroize(sig_try, ctx->len); + mbedtls_platform_zeroize(verif, ctx->len); + mbedtls_free(sig_try); + mbedtls_free(verif); + + if (ret != 0) { + memset(sig, '!', ctx->len); + } + return ret; +} +#endif /* MBEDTLS_PKCS1_V15 */ + +/* + * Do an RSA operation to sign the message digest + */ +int mbedtls_rsa_pkcs1_sign(mbedtls_rsa_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + unsigned char *sig) +{ + if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + switch (ctx->padding) { +#if defined(MBEDTLS_PKCS1_V15) + case MBEDTLS_RSA_PKCS_V15: + return mbedtls_rsa_rsassa_pkcs1_v15_sign(ctx, f_rng, p_rng, + md_alg, hashlen, hash, sig); +#endif + +#if defined(MBEDTLS_PKCS1_V21) + case MBEDTLS_RSA_PKCS_V21: + return mbedtls_rsa_rsassa_pss_sign(ctx, f_rng, p_rng, md_alg, + hashlen, hash, sig); +#endif + + default: + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } +} + +#if defined(MBEDTLS_PKCS1_V21) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int mbedtls_rsa_rsassa_pss_verify_ext(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + mbedtls_md_type_t mgf1_hash_id, + int expected_salt_len, + const unsigned char *sig) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t siglen; + unsigned char *p; + unsigned char *hash_start; + unsigned char result[MBEDTLS_HASH_MAX_SIZE]; + unsigned int hlen; + size_t observed_salt_len, msb; + unsigned char buf[MBEDTLS_MPI_MAX_SIZE] = { 0 }; + + if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + siglen = ctx->len; + + if (siglen < 16 || siglen > sizeof(buf)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + ret = mbedtls_rsa_public(ctx, sig, buf); + + if (ret != 0) { + return ret; + } + + p = buf; + + if (buf[siglen - 1] != 0xBC) { + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } + + if (md_alg != MBEDTLS_MD_NONE) { + /* Gather length of hash to sign */ + size_t exp_hashlen = mbedtls_hash_info_get_size(md_alg); + if (exp_hashlen == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + if (hashlen != exp_hashlen) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + } + + hlen = mbedtls_hash_info_get_size(mgf1_hash_id); + if (hlen == 0) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* + * Note: EMSA-PSS verification is over the length of N - 1 bits + */ + msb = mbedtls_mpi_bitlen(&ctx->N) - 1; + + if (buf[0] >> (8 - siglen * 8 + msb)) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + /* Compensate for boundary condition when applying mask */ + if (msb % 8 == 0) { + p++; + siglen -= 1; + } + + if (siglen < hlen + 2) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + hash_start = p + siglen - hlen - 1; + + ret = mgf_mask(p, siglen - hlen - 1, hash_start, hlen, mgf1_hash_id); + if (ret != 0) { + return ret; + } + + buf[0] &= 0xFF >> (siglen * 8 - msb); + + while (p < hash_start - 1 && *p == 0) { + p++; + } + + if (*p++ != 0x01) { + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } + + observed_salt_len = hash_start - p; + + if (expected_salt_len != MBEDTLS_RSA_SALT_LEN_ANY && + observed_salt_len != (size_t) expected_salt_len) { + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } + + /* + * Generate H = Hash( M' ) + */ + ret = hash_mprime(hash, hashlen, p, observed_salt_len, + result, mgf1_hash_id); + if (ret != 0) { + return ret; + } + + if (memcmp(hash_start, result, hlen) != 0) { + return MBEDTLS_ERR_RSA_VERIFY_FAILED; + } + + return 0; +} + +/* + * Simplified PKCS#1 v2.1 RSASSA-PSS-VERIFY function + */ +int mbedtls_rsa_rsassa_pss_verify(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig) +{ + mbedtls_md_type_t mgf1_hash_id; + if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + mgf1_hash_id = (ctx->hash_id != MBEDTLS_MD_NONE) + ? (mbedtls_md_type_t) ctx->hash_id + : md_alg; + + return mbedtls_rsa_rsassa_pss_verify_ext(ctx, + md_alg, hashlen, hash, + mgf1_hash_id, + MBEDTLS_RSA_SALT_LEN_ANY, + sig); + +} +#endif /* MBEDTLS_PKCS1_V21 */ + +#if defined(MBEDTLS_PKCS1_V15) +/* + * Implementation of the PKCS#1 v2.1 RSASSA-PKCS1-v1_5-VERIFY function + */ +int mbedtls_rsa_rsassa_pkcs1_v15_verify(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig) +{ + int ret = 0; + size_t sig_len; + unsigned char *encoded = NULL, *encoded_expected = NULL; + + if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + sig_len = ctx->len; + + /* + * Prepare expected PKCS1 v1.5 encoding of hash. + */ + + if ((encoded = mbedtls_calloc(1, sig_len)) == NULL || + (encoded_expected = mbedtls_calloc(1, sig_len)) == NULL) { + ret = MBEDTLS_ERR_MPI_ALLOC_FAILED; + goto cleanup; + } + + if ((ret = rsa_rsassa_pkcs1_v15_encode(md_alg, hashlen, hash, sig_len, + encoded_expected)) != 0) { + goto cleanup; + } + + /* + * Apply RSA primitive to get what should be PKCS1 encoded hash. + */ + + ret = mbedtls_rsa_public(ctx, sig, encoded); + if (ret != 0) { + goto cleanup; + } + + /* + * Compare + */ + + if ((ret = mbedtls_ct_memcmp(encoded, encoded_expected, + sig_len)) != 0) { + ret = MBEDTLS_ERR_RSA_VERIFY_FAILED; + goto cleanup; + } + +cleanup: + + if (encoded != NULL) { + mbedtls_platform_zeroize(encoded, sig_len); + mbedtls_free(encoded); + } + + if (encoded_expected != NULL) { + mbedtls_platform_zeroize(encoded_expected, sig_len); + mbedtls_free(encoded_expected); + } + + return ret; +} +#endif /* MBEDTLS_PKCS1_V15 */ + +/* + * Do an RSA operation and check the message digest + */ +int mbedtls_rsa_pkcs1_verify(mbedtls_rsa_context *ctx, + mbedtls_md_type_t md_alg, + unsigned int hashlen, + const unsigned char *hash, + const unsigned char *sig) +{ + if ((md_alg != MBEDTLS_MD_NONE || hashlen != 0) && hash == NULL) { + return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + } + + switch (ctx->padding) { +#if defined(MBEDTLS_PKCS1_V15) + case MBEDTLS_RSA_PKCS_V15: + return mbedtls_rsa_rsassa_pkcs1_v15_verify(ctx, md_alg, + hashlen, hash, sig); +#endif + +#if defined(MBEDTLS_PKCS1_V21) + case MBEDTLS_RSA_PKCS_V21: + return mbedtls_rsa_rsassa_pss_verify(ctx, md_alg, + hashlen, hash, sig); +#endif + + default: + return MBEDTLS_ERR_RSA_INVALID_PADDING; + } +} + +/* + * Copy the components of an RSA key + */ +int mbedtls_rsa_copy(mbedtls_rsa_context *dst, const mbedtls_rsa_context *src) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + dst->len = src->len; + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->N, &src->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->E, &src->E)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->D, &src->D)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->P, &src->P)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->Q, &src->Q)); + +#if !defined(MBEDTLS_RSA_NO_CRT) + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->DP, &src->DP)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->DQ, &src->DQ)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->QP, &src->QP)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->RP, &src->RP)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->RQ, &src->RQ)); +#endif + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->RN, &src->RN)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->Vi, &src->Vi)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&dst->Vf, &src->Vf)); + + dst->padding = src->padding; + dst->hash_id = src->hash_id; + +cleanup: + if (ret != 0) { + mbedtls_rsa_free(dst); + } + + return ret; +} + +/* + * Free the components of an RSA key + */ +void mbedtls_rsa_free(mbedtls_rsa_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_mpi_free(&ctx->Vi); + mbedtls_mpi_free(&ctx->Vf); + mbedtls_mpi_free(&ctx->RN); + mbedtls_mpi_free(&ctx->D); + mbedtls_mpi_free(&ctx->Q); + mbedtls_mpi_free(&ctx->P); + mbedtls_mpi_free(&ctx->E); + mbedtls_mpi_free(&ctx->N); + +#if !defined(MBEDTLS_RSA_NO_CRT) + mbedtls_mpi_free(&ctx->RQ); + mbedtls_mpi_free(&ctx->RP); + mbedtls_mpi_free(&ctx->QP); + mbedtls_mpi_free(&ctx->DQ); + mbedtls_mpi_free(&ctx->DP); +#endif /* MBEDTLS_RSA_NO_CRT */ + +#if defined(MBEDTLS_THREADING_C) + /* Free the mutex, but only if it hasn't been freed already. */ + if (ctx->ver != 0) { + mbedtls_mutex_free(&ctx->mutex); + ctx->ver = 0; + } +#endif +} + +#endif /* !MBEDTLS_RSA_ALT */ + +#if defined(MBEDTLS_SELF_TEST) + +#include "mbedtls/md.h" + +/* + * Example RSA-1024 keypair, for test purposes + */ +#define KEY_LEN 128 + +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +#define RSA_E "10001" + +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define PT_LEN 24 +#define RSA_PT "\xAA\xBB\xCC\x03\x02\x01\x00\xFF\xFF\xFF\xFF\xFF" \ + "\x11\x22\x33\x0A\x0B\x0C\xCC\xDD\xDD\xDD\xDD\xDD" + +#if defined(MBEDTLS_PKCS1_V15) +static int myrand(void *rng_state, unsigned char *output, size_t len) +{ +#if !defined(__OpenBSD__) && !defined(__NetBSD__) + size_t i; + + if (rng_state != NULL) { + rng_state = NULL; + } + + for (i = 0; i < len; ++i) { + output[i] = rand(); + } +#else + if (rng_state != NULL) { + rng_state = NULL; + } + + arc4random_buf(output, len); +#endif /* !OpenBSD && !NetBSD */ + + return 0; +} +#endif /* MBEDTLS_PKCS1_V15 */ + +/* + * Checkup routine + */ +int mbedtls_rsa_self_test(int verbose) +{ + int ret = 0; +#if defined(MBEDTLS_PKCS1_V15) + size_t len; + mbedtls_rsa_context rsa; + unsigned char rsa_plaintext[PT_LEN]; + unsigned char rsa_decrypted[PT_LEN]; + unsigned char rsa_ciphertext[KEY_LEN]; +#if defined(MBEDTLS_SHA1_C) + unsigned char sha1sum[20]; +#endif + + mbedtls_mpi K; + + mbedtls_mpi_init(&K); + mbedtls_rsa_init(&rsa); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&K, 16, RSA_N)); + MBEDTLS_MPI_CHK(mbedtls_rsa_import(&rsa, &K, NULL, NULL, NULL, NULL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&K, 16, RSA_P)); + MBEDTLS_MPI_CHK(mbedtls_rsa_import(&rsa, NULL, &K, NULL, NULL, NULL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&K, 16, RSA_Q)); + MBEDTLS_MPI_CHK(mbedtls_rsa_import(&rsa, NULL, NULL, &K, NULL, NULL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&K, 16, RSA_D)); + MBEDTLS_MPI_CHK(mbedtls_rsa_import(&rsa, NULL, NULL, NULL, &K, NULL)); + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&K, 16, RSA_E)); + MBEDTLS_MPI_CHK(mbedtls_rsa_import(&rsa, NULL, NULL, NULL, NULL, &K)); + + MBEDTLS_MPI_CHK(mbedtls_rsa_complete(&rsa)); + + if (verbose != 0) { + mbedtls_printf(" RSA key validation: "); + } + + if (mbedtls_rsa_check_pubkey(&rsa) != 0 || + mbedtls_rsa_check_privkey(&rsa) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n PKCS#1 encryption : "); + } + + memcpy(rsa_plaintext, RSA_PT, PT_LEN); + + if (mbedtls_rsa_pkcs1_encrypt(&rsa, myrand, NULL, + PT_LEN, rsa_plaintext, + rsa_ciphertext) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n PKCS#1 decryption : "); + } + + if (mbedtls_rsa_pkcs1_decrypt(&rsa, myrand, NULL, + &len, rsa_ciphertext, rsa_decrypted, + sizeof(rsa_decrypted)) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (memcmp(rsa_decrypted, rsa_plaintext, len) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + +#if defined(MBEDTLS_SHA1_C) + if (verbose != 0) { + mbedtls_printf(" PKCS#1 data sign : "); + } + + if (mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), + rsa_plaintext, PT_LEN, sha1sum) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + return 1; + } + + if (mbedtls_rsa_pkcs1_sign(&rsa, myrand, NULL, + MBEDTLS_MD_SHA1, 20, + sha1sum, rsa_ciphertext) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n PKCS#1 sig. verify: "); + } + + if (mbedtls_rsa_pkcs1_verify(&rsa, MBEDTLS_MD_SHA1, 20, + sha1sum, rsa_ciphertext) != 0) { + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + + ret = 1; + goto cleanup; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } +#endif /* MBEDTLS_SHA1_C */ + + if (verbose != 0) { + mbedtls_printf("\n"); + } + +cleanup: + mbedtls_mpi_free(&K); + mbedtls_rsa_free(&rsa); +#else /* MBEDTLS_PKCS1_V15 */ + ((void) verbose); +#endif /* MBEDTLS_PKCS1_V15 */ + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_RSA_C */ diff --git a/r5dev/thirdparty/mbedtls/rsa_alt_helpers.c b/r5dev/thirdparty/mbedtls/rsa_alt_helpers.c new file mode 100644 index 00000000..3451469b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/rsa_alt_helpers.c @@ -0,0 +1,459 @@ +/* + * Helper functions for the RSA module + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "common.h" + +#if defined(MBEDTLS_RSA_C) + +#include "mbedtls/rsa.h" +#include "mbedtls/bignum.h" +#include "rsa_alt_helpers.h" + +/* + * Compute RSA prime factors from public and private exponents + * + * Summary of algorithm: + * Setting F := lcm(P-1,Q-1), the idea is as follows: + * + * (a) For any 1 <= X < N with gcd(X,N)=1, we have X^F = 1 modulo N, so X^(F/2) + * is a square root of 1 in Z/NZ. Since Z/NZ ~= Z/PZ x Z/QZ by CRT and the + * square roots of 1 in Z/PZ and Z/QZ are +1 and -1, this leaves the four + * possibilities X^(F/2) = (+-1, +-1). If it happens that X^(F/2) = (-1,+1) + * or (+1,-1), then gcd(X^(F/2) + 1, N) will be equal to one of the prime + * factors of N. + * + * (b) If we don't know F/2 but (F/2) * K for some odd (!) K, then the same + * construction still applies since (-)^K is the identity on the set of + * roots of 1 in Z/NZ. + * + * The public and private key primitives (-)^E and (-)^D are mutually inverse + * bijections on Z/NZ if and only if (-)^(DE) is the identity on Z/NZ, i.e. + * if and only if DE - 1 is a multiple of F, say DE - 1 = F * L. + * Splitting L = 2^t * K with K odd, we have + * + * DE - 1 = FL = (F/2) * (2^(t+1)) * K, + * + * so (F / 2) * K is among the numbers + * + * (DE - 1) >> 1, (DE - 1) >> 2, ..., (DE - 1) >> ord + * + * where ord is the order of 2 in (DE - 1). + * We can therefore iterate through these numbers apply the construction + * of (a) and (b) above to attempt to factor N. + * + */ +int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, + mbedtls_mpi const *E, mbedtls_mpi const *D, + mbedtls_mpi *P, mbedtls_mpi *Q) +{ + int ret = 0; + + uint16_t attempt; /* Number of current attempt */ + uint16_t iter; /* Number of squares computed in the current attempt */ + + uint16_t order; /* Order of 2 in DE - 1 */ + + mbedtls_mpi T; /* Holds largest odd divisor of DE - 1 */ + mbedtls_mpi K; /* Temporary holding the current candidate */ + + const unsigned char primes[] = { 2, + 3, 5, 7, 11, 13, 17, 19, 23, + 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, + 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, + 181, 191, 193, 197, 199, 211, 223, 227, + 229, 233, 239, 241, 251 }; + + const size_t num_primes = sizeof(primes) / sizeof(*primes); + + if (P == NULL || Q == NULL || P->p != NULL || Q->p != NULL) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (mbedtls_mpi_cmp_int(N, 0) <= 0 || + mbedtls_mpi_cmp_int(D, 1) <= 0 || + mbedtls_mpi_cmp_mpi(D, N) >= 0 || + mbedtls_mpi_cmp_int(E, 1) <= 0 || + mbedtls_mpi_cmp_mpi(E, N) >= 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + /* + * Initializations and temporary changes + */ + + mbedtls_mpi_init(&K); + mbedtls_mpi_init(&T); + + /* T := DE - 1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&T, D, E)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&T, &T, 1)); + + if ((order = (uint16_t) mbedtls_mpi_lsb(&T)) == 0) { + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + goto cleanup; + } + + /* After this operation, T holds the largest odd divisor of DE - 1. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&T, order)); + + /* + * Actual work + */ + + /* Skip trying 2 if N == 1 mod 8 */ + attempt = 0; + if (N->p[0] % 8 == 1) { + attempt = 1; + } + + for (; attempt < num_primes; ++attempt) { + mbedtls_mpi_lset(&K, primes[attempt]); + + /* Check if gcd(K,N) = 1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(P, &K, N)); + if (mbedtls_mpi_cmp_int(P, 1) != 0) { + continue; + } + + /* Go through K^T + 1, K^(2T) + 1, K^(4T) + 1, ... + * and check whether they have nontrivial GCD with N. */ + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&K, &K, &T, N, + Q /* temporarily use Q for storing Montgomery + * multiplication helper values */)); + + for (iter = 1; iter <= order; ++iter) { + /* If we reach 1 prematurely, there's no point + * in continuing to square K */ + if (mbedtls_mpi_cmp_int(&K, 1) == 0) { + break; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&K, &K, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(P, &K, N)); + + if (mbedtls_mpi_cmp_int(P, 1) == 1 && + mbedtls_mpi_cmp_mpi(P, N) == -1) { + /* + * Have found a nontrivial divisor P of N. + * Set Q := N / P. + */ + + MBEDTLS_MPI_CHK(mbedtls_mpi_div_mpi(Q, NULL, N, P)); + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, &K, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&K, &K, &K)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&K, &K, N)); + } + + /* + * If we get here, then either we prematurely aborted the loop because + * we reached 1, or K holds primes[attempt]^(DE - 1) mod N, which must + * be 1 if D,E,N were consistent. + * Check if that's the case and abort if not, to avoid very long, + * yet eventually failing, computations if N,D,E were not sane. + */ + if (mbedtls_mpi_cmp_int(&K, 1) != 0) { + break; + } + } + + ret = MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + +cleanup: + + mbedtls_mpi_free(&K); + mbedtls_mpi_free(&T); + return ret; +} + +/* + * Given P, Q and the public exponent E, deduce D. + * This is essentially a modular inversion. + */ +int mbedtls_rsa_deduce_private_exponent(mbedtls_mpi const *P, + mbedtls_mpi const *Q, + mbedtls_mpi const *E, + mbedtls_mpi *D) +{ + int ret = 0; + mbedtls_mpi K, L; + + if (D == NULL || mbedtls_mpi_cmp_int(D, 0) != 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + if (mbedtls_mpi_cmp_int(P, 1) <= 0 || + mbedtls_mpi_cmp_int(Q, 1) <= 0 || + mbedtls_mpi_cmp_int(E, 0) == 0) { + return MBEDTLS_ERR_MPI_BAD_INPUT_DATA; + } + + mbedtls_mpi_init(&K); + mbedtls_mpi_init(&L); + + /* Temporarily put K := P-1 and L := Q-1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, P, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&L, Q, 1)); + + /* Temporarily put D := gcd(P-1, Q-1) */ + MBEDTLS_MPI_CHK(mbedtls_mpi_gcd(D, &K, &L)); + + /* K := LCM(P-1, Q-1) */ + 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)); + +cleanup: + + mbedtls_mpi_free(&K); + mbedtls_mpi_free(&L); + + return ret; +} + +int mbedtls_rsa_deduce_crt(const mbedtls_mpi *P, const mbedtls_mpi *Q, + const mbedtls_mpi *D, mbedtls_mpi *DP, + mbedtls_mpi *DQ, mbedtls_mpi *QP) +{ + int ret = 0; + mbedtls_mpi K; + mbedtls_mpi_init(&K); + + /* DP = D mod P-1 */ + if (DP != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, P, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(DP, D, &K)); + } + + /* DQ = D mod Q-1 */ + if (DQ != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, Q, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(DQ, D, &K)); + } + + /* QP = Q^{-1} mod P */ + if (QP != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(QP, Q, P)); + } + +cleanup: + mbedtls_mpi_free(&K); + + return ret; +} + +/* + * Check that core RSA parameters are sane. + */ +int mbedtls_rsa_validate_params(const mbedtls_mpi *N, const mbedtls_mpi *P, + const mbedtls_mpi *Q, const mbedtls_mpi *D, + const mbedtls_mpi *E, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = 0; + mbedtls_mpi K, L; + + mbedtls_mpi_init(&K); + mbedtls_mpi_init(&L); + + /* + * Step 1: If PRNG provided, check that P and Q are prime + */ + +#if defined(MBEDTLS_GENPRIME) + /* + * When generating keys, the strongest security we support aims for an error + * rate of at most 2^-100 and we are aiming for the same certainty here as + * well. + */ + if (f_rng != NULL && P != NULL && + (ret = mbedtls_mpi_is_prime_ext(P, 50, f_rng, p_rng)) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + + if (f_rng != NULL && Q != NULL && + (ret = mbedtls_mpi_is_prime_ext(Q, 50, f_rng, p_rng)) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } +#else + ((void) f_rng); + ((void) p_rng); +#endif /* MBEDTLS_GENPRIME */ + + /* + * Step 2: Check that 1 < N = P * Q + */ + + if (P != NULL && Q != NULL && N != NULL) { + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&K, P, Q)); + if (mbedtls_mpi_cmp_int(N, 1) <= 0 || + mbedtls_mpi_cmp_mpi(&K, N) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + } + + /* + * Step 3: Check and 1 < D, E < N if present. + */ + + if (N != NULL && D != NULL && E != NULL) { + if (mbedtls_mpi_cmp_int(D, 1) <= 0 || + mbedtls_mpi_cmp_int(E, 1) <= 0 || + mbedtls_mpi_cmp_mpi(D, N) >= 0 || + mbedtls_mpi_cmp_mpi(E, N) >= 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + } + + /* + * Step 4: Check that D, E are inverse modulo P-1 and Q-1 + */ + + if (P != NULL && Q != NULL && D != NULL && E != NULL) { + if (mbedtls_mpi_cmp_int(P, 1) <= 0 || + mbedtls_mpi_cmp_int(Q, 1) <= 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + + /* Compute DE-1 mod P-1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&K, D, E)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, &K, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&L, P, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&K, &K, &L)); + if (mbedtls_mpi_cmp_int(&K, 0) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + + /* Compute DE-1 mod Q-1 */ + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&K, D, E)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, &K, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&L, Q, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&K, &K, &L)); + if (mbedtls_mpi_cmp_int(&K, 0) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + } + +cleanup: + + mbedtls_mpi_free(&K); + mbedtls_mpi_free(&L); + + /* Wrap MPI error codes by RSA check failure error code */ + if (ret != 0 && ret != MBEDTLS_ERR_RSA_KEY_CHECK_FAILED) { + ret += MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + return ret; +} + +/* + * Check that RSA CRT parameters are in accordance with core parameters. + */ +int mbedtls_rsa_validate_crt(const mbedtls_mpi *P, const mbedtls_mpi *Q, + const mbedtls_mpi *D, const mbedtls_mpi *DP, + const mbedtls_mpi *DQ, const mbedtls_mpi *QP) +{ + int ret = 0; + + mbedtls_mpi K, L; + mbedtls_mpi_init(&K); + mbedtls_mpi_init(&L); + + /* Check that DP - D == 0 mod P - 1 */ + if (DP != NULL) { + if (P == NULL) { + ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, P, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&L, DP, D)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&L, &L, &K)); + + if (mbedtls_mpi_cmp_int(&L, 0) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + } + + /* Check that DQ - D == 0 mod Q - 1 */ + if (DQ != NULL) { + if (Q == NULL) { + ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, Q, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&L, DQ, D)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&L, &L, &K)); + + if (mbedtls_mpi_cmp_int(&L, 0) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + } + + /* Check that QP * Q - 1 == 0 mod P */ + if (QP != NULL) { + if (P == NULL || Q == NULL) { + ret = MBEDTLS_ERR_RSA_BAD_INPUT_DATA; + goto cleanup; + } + + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&K, QP, Q)); + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&K, &K, 1)); + MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&K, &K, P)); + if (mbedtls_mpi_cmp_int(&K, 0) != 0) { + ret = MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + goto cleanup; + } + } + +cleanup: + + /* Wrap MPI error codes by RSA check failure error code */ + if (ret != 0 && + ret != MBEDTLS_ERR_RSA_KEY_CHECK_FAILED && + ret != MBEDTLS_ERR_RSA_BAD_INPUT_DATA) { + ret += MBEDTLS_ERR_RSA_KEY_CHECK_FAILED; + } + + mbedtls_mpi_free(&K); + mbedtls_mpi_free(&L); + + return ret; +} + +#endif /* MBEDTLS_RSA_C */ diff --git a/r5dev/thirdparty/mbedtls/rsa_alt_helpers.h b/r5dev/thirdparty/mbedtls/rsa_alt_helpers.h new file mode 100644 index 00000000..3b22ba85 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/rsa_alt_helpers.h @@ -0,0 +1,220 @@ +/** + * \file rsa_alt_helpers.h + * + * \brief Context-independent RSA helper functions + * + * This module declares some RSA-related helper functions useful when + * implementing the RSA interface. These functions are provided in a separate + * compilation unit in order to make it easy for designers of alternative RSA + * implementations to use them in their own code, as it is conceived that the + * functionality they provide will be necessary for most complete + * implementations. + * + * End-users of Mbed TLS who are not providing their own alternative RSA + * implementations should not use these functions directly, and should instead + * use only the functions declared in rsa.h. + * + * The interface provided by this module will be maintained through LTS (Long + * Term Support) branches of Mbed TLS, but may otherwise be subject to change, + * and must be considered an internal interface of the library. + * + * There are two classes of helper functions: + * + * (1) Parameter-generating helpers. These are: + * - mbedtls_rsa_deduce_primes + * - mbedtls_rsa_deduce_private_exponent + * - mbedtls_rsa_deduce_crt + * Each of these functions takes a set of core RSA parameters and + * generates some other, or CRT related parameters. + * + * (2) Parameter-checking helpers. These are: + * - mbedtls_rsa_validate_params + * - mbedtls_rsa_validate_crt + * They take a set of core or CRT related RSA parameters and check their + * validity. + * + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef MBEDTLS_RSA_INTERNAL_H +#define MBEDTLS_RSA_INTERNAL_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/bignum.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * \brief Compute RSA prime moduli P, Q from public modulus N=PQ + * and a pair of private and public key. + * + * \note This is a 'static' helper function not operating on + * an RSA context. Alternative implementations need not + * overwrite it. + * + * \param N RSA modulus N = PQ, with P, Q to be found + * \param E RSA public exponent + * \param D RSA private exponent + * \param P Pointer to MPI holding first prime factor of N on success + * \param Q Pointer to MPI holding second prime factor of N on success + * + * \return + * - 0 if successful. In this case, P and Q constitute a + * factorization of N. + * - A non-zero error code otherwise. + * + * \note It is neither checked that P, Q are prime nor that + * D, E are modular inverses wrt. P-1 and Q-1. For that, + * use the helper function \c mbedtls_rsa_validate_params. + * + */ +int mbedtls_rsa_deduce_primes(mbedtls_mpi const *N, mbedtls_mpi const *E, + mbedtls_mpi const *D, + mbedtls_mpi *P, mbedtls_mpi *Q); + +/** + * \brief Compute RSA private exponent from + * prime moduli and public key. + * + * \note This is a 'static' helper function not operating on + * an RSA context. Alternative implementations need not + * overwrite it. + * + * \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. + * + * \note This function does not check whether P and Q are primes. + * + */ +int mbedtls_rsa_deduce_private_exponent(mbedtls_mpi const *P, + mbedtls_mpi const *Q, + mbedtls_mpi const *E, + mbedtls_mpi *D); + + +/** + * \brief Generate RSA-CRT parameters + * + * \note This is a 'static' helper function not operating on + * an RSA context. Alternative implementations need not + * overwrite it. + * + * \param P First prime factor of N + * \param Q Second prime factor of N + * \param D RSA private exponent + * \param DP Output variable for D modulo P-1 + * \param DQ Output variable for D modulo Q-1 + * \param QP Output variable for the modular inverse of Q modulo P. + * + * \return 0 on success, non-zero error code otherwise. + * + * \note This function does not check whether P, Q are + * prime and whether D is a valid private exponent. + * + */ +int mbedtls_rsa_deduce_crt(const mbedtls_mpi *P, const mbedtls_mpi *Q, + const mbedtls_mpi *D, mbedtls_mpi *DP, + mbedtls_mpi *DQ, mbedtls_mpi *QP); + + +/** + * \brief Check validity of core RSA parameters + * + * \note This is a 'static' helper function not operating on + * an RSA context. Alternative implementations need not + * overwrite it. + * + * \param N RSA modulus N = PQ + * \param P First prime factor of N + * \param Q Second prime factor of N + * \param D RSA private exponent + * \param E RSA public exponent + * \param f_rng PRNG to be used for primality check, or NULL + * \param p_rng PRNG context for f_rng, or NULL + * + * \return + * - 0 if the following conditions are satisfied + * if all relevant parameters are provided: + * - P prime if f_rng != NULL (%) + * - Q prime if f_rng != NULL (%) + * - 1 < N = P * Q + * - 1 < D, E < N + * - D and E are modular inverses modulo P-1 and Q-1 + * (%) This is only done if MBEDTLS_GENPRIME is defined. + * - A non-zero error code otherwise. + * + * \note The function can be used with a restricted set of arguments + * to perform specific checks only. E.g., calling it with + * (-,P,-,-,-) and a PRNG amounts to a primality check for P. + */ +int mbedtls_rsa_validate_params(const mbedtls_mpi *N, const mbedtls_mpi *P, + const mbedtls_mpi *Q, const mbedtls_mpi *D, + const mbedtls_mpi *E, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); + +/** + * \brief Check validity of RSA CRT parameters + * + * \note This is a 'static' helper function not operating on + * an RSA context. Alternative implementations need not + * overwrite it. + * + * \param P First prime factor of RSA modulus + * \param Q Second prime factor of RSA modulus + * \param D RSA private exponent + * \param DP MPI to check for D modulo P-1 + * \param DQ MPI to check for D modulo P-1 + * \param QP MPI to check for the modular inverse of Q modulo P. + * + * \return + * - 0 if the following conditions are satisfied: + * - D = DP mod P-1 if P, D, DP != NULL + * - Q = DQ mod P-1 if P, D, DQ != NULL + * - QP = Q^-1 mod P if P, Q, QP != NULL + * - \c MBEDTLS_ERR_RSA_KEY_CHECK_FAILED if check failed, + * potentially including \c MBEDTLS_ERR_MPI_XXX if some + * MPI calculations failed. + * - \c MBEDTLS_ERR_RSA_BAD_INPUT_DATA if insufficient + * data was provided to check DP, DQ or QP. + * + * \note The function can be used with a restricted set of arguments + * to perform specific checks only. E.g., calling it with the + * parameters (P, -, D, DP, -, -) will check DP = D mod P-1. + */ +int mbedtls_rsa_validate_crt(const mbedtls_mpi *P, const mbedtls_mpi *Q, + const mbedtls_mpi *D, const mbedtls_mpi *DP, + const mbedtls_mpi *DQ, const mbedtls_mpi *QP); + +#ifdef __cplusplus +} +#endif + +#endif /* rsa_alt_helpers.h */ diff --git a/r5dev/thirdparty/mbedtls/sha1.c b/r5dev/thirdparty/mbedtls/sha1.c new file mode 100644 index 00000000..4c9cbf5e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/sha1.c @@ -0,0 +1,489 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#include "common.h" + +#if defined(MBEDTLS_SHA1_C) + +#include "mbedtls/sha1.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#include "mbedtls/platform.h" + +#if !defined(MBEDTLS_SHA1_ALT) + +void mbedtls_sha1_init(mbedtls_sha1_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_sha1_context)); +} + +void mbedtls_sha1_free(mbedtls_sha1_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_sha1_context)); +} + +void mbedtls_sha1_clone(mbedtls_sha1_context *dst, + const mbedtls_sha1_context *src) +{ + *dst = *src; +} + +/* + * SHA-1 context setup + */ +int mbedtls_sha1_starts(mbedtls_sha1_context *ctx) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + + return 0; +} + +#if !defined(MBEDTLS_SHA1_PROCESS_ALT) +int mbedtls_internal_sha1_process(mbedtls_sha1_context *ctx, + const unsigned char data[64]) +{ + struct { + uint32_t temp, W[16], A, B, C, D, E; + } local; + + local.W[0] = MBEDTLS_GET_UINT32_BE(data, 0); + local.W[1] = MBEDTLS_GET_UINT32_BE(data, 4); + local.W[2] = MBEDTLS_GET_UINT32_BE(data, 8); + local.W[3] = MBEDTLS_GET_UINT32_BE(data, 12); + local.W[4] = MBEDTLS_GET_UINT32_BE(data, 16); + local.W[5] = MBEDTLS_GET_UINT32_BE(data, 20); + local.W[6] = MBEDTLS_GET_UINT32_BE(data, 24); + local.W[7] = MBEDTLS_GET_UINT32_BE(data, 28); + local.W[8] = MBEDTLS_GET_UINT32_BE(data, 32); + local.W[9] = MBEDTLS_GET_UINT32_BE(data, 36); + local.W[10] = MBEDTLS_GET_UINT32_BE(data, 40); + local.W[11] = MBEDTLS_GET_UINT32_BE(data, 44); + local.W[12] = MBEDTLS_GET_UINT32_BE(data, 48); + local.W[13] = MBEDTLS_GET_UINT32_BE(data, 52); + local.W[14] = MBEDTLS_GET_UINT32_BE(data, 56); + local.W[15] = MBEDTLS_GET_UINT32_BE(data, 60); + +#define S(x, n) (((x) << (n)) | (((x) & 0xFFFFFFFF) >> (32 - (n)))) + +#define R(t) \ + ( \ + local.temp = local.W[((t) - 3) & 0x0F] ^ \ + local.W[((t) - 8) & 0x0F] ^ \ + local.W[((t) - 14) & 0x0F] ^ \ + local.W[(t) & 0x0F], \ + (local.W[(t) & 0x0F] = S(local.temp, 1)) \ + ) + +#define P(a, b, c, d, e, x) \ + do \ + { \ + (e) += S((a), 5) + F((b), (c), (d)) + K + (x); \ + (b) = S((b), 30); \ + } while (0) + + local.A = ctx->state[0]; + local.B = ctx->state[1]; + local.C = ctx->state[2]; + local.D = ctx->state[3]; + local.E = ctx->state[4]; + +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define K 0x5A827999 + + P(local.A, local.B, local.C, local.D, local.E, local.W[0]); + P(local.E, local.A, local.B, local.C, local.D, local.W[1]); + P(local.D, local.E, local.A, local.B, local.C, local.W[2]); + P(local.C, local.D, local.E, local.A, local.B, local.W[3]); + P(local.B, local.C, local.D, local.E, local.A, local.W[4]); + P(local.A, local.B, local.C, local.D, local.E, local.W[5]); + P(local.E, local.A, local.B, local.C, local.D, local.W[6]); + P(local.D, local.E, local.A, local.B, local.C, local.W[7]); + P(local.C, local.D, local.E, local.A, local.B, local.W[8]); + P(local.B, local.C, local.D, local.E, local.A, local.W[9]); + P(local.A, local.B, local.C, local.D, local.E, local.W[10]); + P(local.E, local.A, local.B, local.C, local.D, local.W[11]); + P(local.D, local.E, local.A, local.B, local.C, local.W[12]); + P(local.C, local.D, local.E, local.A, local.B, local.W[13]); + P(local.B, local.C, local.D, local.E, local.A, local.W[14]); + P(local.A, local.B, local.C, local.D, local.E, local.W[15]); + P(local.E, local.A, local.B, local.C, local.D, R(16)); + P(local.D, local.E, local.A, local.B, local.C, R(17)); + P(local.C, local.D, local.E, local.A, local.B, R(18)); + P(local.B, local.C, local.D, local.E, local.A, R(19)); + +#undef K +#undef F + +#define F(x, y, z) ((x) ^ (y) ^ (z)) +#define K 0x6ED9EBA1 + + P(local.A, local.B, local.C, local.D, local.E, R(20)); + P(local.E, local.A, local.B, local.C, local.D, R(21)); + P(local.D, local.E, local.A, local.B, local.C, R(22)); + P(local.C, local.D, local.E, local.A, local.B, R(23)); + P(local.B, local.C, local.D, local.E, local.A, R(24)); + P(local.A, local.B, local.C, local.D, local.E, R(25)); + P(local.E, local.A, local.B, local.C, local.D, R(26)); + P(local.D, local.E, local.A, local.B, local.C, R(27)); + P(local.C, local.D, local.E, local.A, local.B, R(28)); + P(local.B, local.C, local.D, local.E, local.A, R(29)); + P(local.A, local.B, local.C, local.D, local.E, R(30)); + P(local.E, local.A, local.B, local.C, local.D, R(31)); + P(local.D, local.E, local.A, local.B, local.C, R(32)); + P(local.C, local.D, local.E, local.A, local.B, R(33)); + P(local.B, local.C, local.D, local.E, local.A, R(34)); + P(local.A, local.B, local.C, local.D, local.E, R(35)); + P(local.E, local.A, local.B, local.C, local.D, R(36)); + P(local.D, local.E, local.A, local.B, local.C, R(37)); + P(local.C, local.D, local.E, local.A, local.B, R(38)); + P(local.B, local.C, local.D, local.E, local.A, R(39)); + +#undef K +#undef F + +#define F(x, y, z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define K 0x8F1BBCDC + + P(local.A, local.B, local.C, local.D, local.E, R(40)); + P(local.E, local.A, local.B, local.C, local.D, R(41)); + P(local.D, local.E, local.A, local.B, local.C, R(42)); + P(local.C, local.D, local.E, local.A, local.B, R(43)); + P(local.B, local.C, local.D, local.E, local.A, R(44)); + P(local.A, local.B, local.C, local.D, local.E, R(45)); + P(local.E, local.A, local.B, local.C, local.D, R(46)); + P(local.D, local.E, local.A, local.B, local.C, R(47)); + P(local.C, local.D, local.E, local.A, local.B, R(48)); + P(local.B, local.C, local.D, local.E, local.A, R(49)); + P(local.A, local.B, local.C, local.D, local.E, R(50)); + P(local.E, local.A, local.B, local.C, local.D, R(51)); + P(local.D, local.E, local.A, local.B, local.C, R(52)); + P(local.C, local.D, local.E, local.A, local.B, R(53)); + P(local.B, local.C, local.D, local.E, local.A, R(54)); + P(local.A, local.B, local.C, local.D, local.E, R(55)); + P(local.E, local.A, local.B, local.C, local.D, R(56)); + P(local.D, local.E, local.A, local.B, local.C, R(57)); + P(local.C, local.D, local.E, local.A, local.B, R(58)); + P(local.B, local.C, local.D, local.E, local.A, R(59)); + +#undef K +#undef F + +#define F(x, y, z) ((x) ^ (y) ^ (z)) +#define K 0xCA62C1D6 + + P(local.A, local.B, local.C, local.D, local.E, R(60)); + P(local.E, local.A, local.B, local.C, local.D, R(61)); + P(local.D, local.E, local.A, local.B, local.C, R(62)); + P(local.C, local.D, local.E, local.A, local.B, R(63)); + P(local.B, local.C, local.D, local.E, local.A, R(64)); + P(local.A, local.B, local.C, local.D, local.E, R(65)); + P(local.E, local.A, local.B, local.C, local.D, R(66)); + P(local.D, local.E, local.A, local.B, local.C, R(67)); + P(local.C, local.D, local.E, local.A, local.B, R(68)); + P(local.B, local.C, local.D, local.E, local.A, R(69)); + P(local.A, local.B, local.C, local.D, local.E, R(70)); + P(local.E, local.A, local.B, local.C, local.D, R(71)); + P(local.D, local.E, local.A, local.B, local.C, R(72)); + P(local.C, local.D, local.E, local.A, local.B, R(73)); + P(local.B, local.C, local.D, local.E, local.A, R(74)); + P(local.A, local.B, local.C, local.D, local.E, R(75)); + P(local.E, local.A, local.B, local.C, local.D, R(76)); + P(local.D, local.E, local.A, local.B, local.C, R(77)); + P(local.C, local.D, local.E, local.A, local.B, R(78)); + P(local.B, local.C, local.D, local.E, local.A, R(79)); + +#undef K +#undef F + + ctx->state[0] += local.A; + ctx->state[1] += local.B; + ctx->state[2] += local.C; + ctx->state[3] += local.D; + ctx->state[4] += local.E; + + /* Zeroise buffers and variables to clear sensitive data from memory. */ + mbedtls_platform_zeroize(&local, sizeof(local)); + + return 0; +} + +#endif /* !MBEDTLS_SHA1_PROCESS_ALT */ + +/* + * SHA-1 process buffer + */ +int mbedtls_sha1_update(mbedtls_sha1_context *ctx, + const unsigned char *input, + size_t ilen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t fill; + uint32_t left; + + if (ilen == 0) { + return 0; + } + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if (ctx->total[0] < (uint32_t) ilen) { + ctx->total[1]++; + } + + if (left && ilen >= fill) { + memcpy((void *) (ctx->buffer + left), input, fill); + + if ((ret = mbedtls_internal_sha1_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= 64) { + if ((ret = mbedtls_internal_sha1_process(ctx, input)) != 0) { + return ret; + } + + input += 64; + ilen -= 64; + } + + if (ilen > 0) { + memcpy((void *) (ctx->buffer + left), input, ilen); + } + + return 0; +} + +/* + * SHA-1 final digest + */ +int mbedtls_sha1_finish(mbedtls_sha1_context *ctx, + unsigned char output[20]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + uint32_t used; + uint32_t high, low; + + /* + * Add padding: 0x80 then 0x00 until 8 bytes remain for the length + */ + used = ctx->total[0] & 0x3F; + + ctx->buffer[used++] = 0x80; + + if (used <= 56) { + /* Enough room for padding + length in current block */ + memset(ctx->buffer + used, 0, 56 - used); + } else { + /* We'll need an extra block */ + memset(ctx->buffer + used, 0, 64 - used); + + if ((ret = mbedtls_internal_sha1_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + memset(ctx->buffer, 0, 56); + } + + /* + * Add message length + */ + high = (ctx->total[0] >> 29) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + MBEDTLS_PUT_UINT32_BE(high, ctx->buffer, 56); + MBEDTLS_PUT_UINT32_BE(low, ctx->buffer, 60); + + if ((ret = mbedtls_internal_sha1_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + /* + * Output final state + */ + MBEDTLS_PUT_UINT32_BE(ctx->state[0], output, 0); + MBEDTLS_PUT_UINT32_BE(ctx->state[1], output, 4); + MBEDTLS_PUT_UINT32_BE(ctx->state[2], output, 8); + MBEDTLS_PUT_UINT32_BE(ctx->state[3], output, 12); + MBEDTLS_PUT_UINT32_BE(ctx->state[4], output, 16); + + return 0; +} + +#endif /* !MBEDTLS_SHA1_ALT */ + +/* + * output = SHA-1( input buffer ) + */ +int mbedtls_sha1(const unsigned char *input, + size_t ilen, + unsigned char output[20]) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_sha1_context ctx; + + mbedtls_sha1_init(&ctx); + + if ((ret = mbedtls_sha1_starts(&ctx)) != 0) { + goto exit; + } + + if ((ret = mbedtls_sha1_update(&ctx, input, ilen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_sha1_finish(&ctx, output)) != 0) { + goto exit; + } + +exit: + mbedtls_sha1_free(&ctx); + + return ret; +} + +#if defined(MBEDTLS_SELF_TEST) +/* + * FIPS-180-1 test vectors + */ +static const unsigned char sha1_test_buf[3][57] = +{ + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const size_t sha1_test_buflen[3] = +{ + 3, 56, 1000 +}; + +static const unsigned char sha1_test_sum[3][20] = +{ + { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, + 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }, + { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, + 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, + { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, + 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } +}; + +/* + * Checkup routine + */ +int mbedtls_sha1_self_test(int verbose) +{ + int i, j, buflen, ret = 0; + unsigned char buf[1024]; + unsigned char sha1sum[20]; + mbedtls_sha1_context ctx; + + mbedtls_sha1_init(&ctx); + + /* + * SHA-1 + */ + for (i = 0; i < 3; i++) { + if (verbose != 0) { + mbedtls_printf(" SHA-1 test #%d: ", i + 1); + } + + if ((ret = mbedtls_sha1_starts(&ctx)) != 0) { + goto fail; + } + + if (i == 2) { + memset(buf, 'a', buflen = 1000); + + for (j = 0; j < 1000; j++) { + ret = mbedtls_sha1_update(&ctx, buf, buflen); + if (ret != 0) { + goto fail; + } + } + } else { + ret = mbedtls_sha1_update(&ctx, sha1_test_buf[i], + sha1_test_buflen[i]); + if (ret != 0) { + goto fail; + } + } + + if ((ret = mbedtls_sha1_finish(&ctx, sha1sum)) != 0) { + goto fail; + } + + if (memcmp(sha1sum, sha1_test_sum[i], 20) != 0) { + ret = 1; + goto fail; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + goto exit; + +fail: + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + +exit: + mbedtls_sha1_free(&ctx); + + return ret; +} + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_SHA1_C */ diff --git a/r5dev/thirdparty/mbedtls/sha256.c b/r5dev/thirdparty/mbedtls/sha256.c new file mode 100644 index 00000000..08822f44 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/sha256.c @@ -0,0 +1,939 @@ +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The SHA-256 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +#if defined(__aarch64__) && !defined(__ARM_FEATURE_CRYPTO) && \ + defined(__clang__) && __clang_major__ >= 4 +/* TODO: Re-consider above after https://reviews.llvm.org/D131064 merged. + * + * The intrinsic declaration are guarded by predefined ACLE macros in clang: + * these are normally only enabled by the -march option on the command line. + * By defining the macros ourselves we gain access to those declarations without + * requiring -march on the command line. + * + * `arm_neon.h` could be included by any header file, so we put these defines + * at the top of this file, before any includes. + */ +#define __ARM_FEATURE_CRYPTO 1 +/* See: https://arm-software.github.io/acle/main/acle.html#cryptographic-extensions + * + * `__ARM_FEATURE_CRYPTO` is deprecated, but we need to continue to specify it + * for older compilers. + */ +#define __ARM_FEATURE_SHA2 1 +#define MBEDTLS_ENABLE_ARM_CRYPTO_EXTENSIONS_COMPILER_FLAG +#endif + +#include "common.h" + +#if defined(MBEDTLS_SHA256_C) || defined(MBEDTLS_SHA224_C) + +#include "mbedtls/sha256.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#include + +#include "mbedtls/platform.h" + +#if defined(__aarch64__) +# if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) +/* *INDENT-OFF* */ +# if !defined(__ARM_FEATURE_CRYPTO) || defined(MBEDTLS_ENABLE_ARM_CRYPTO_EXTENSIONS_COMPILER_FLAG) +# if defined(__clang__) +# if __clang_major__ < 4 +# error "A more recent Clang is required for MBEDTLS_SHA256_USE_A64_CRYPTO_*" +# endif +# pragma clang attribute push (__attribute__((target("crypto"))), apply_to=function) +# define MBEDTLS_POP_TARGET_PRAGMA +# elif defined(__GNUC__) + /* FIXME: GCC 5 claims to support Armv8 Crypto Extensions, but some + * intrinsics are missing. Missing intrinsics could be worked around. + */ +# if __GNUC__ < 6 +# error "A more recent GCC is required for MBEDTLS_SHA256_USE_A64_CRYPTO_*" +# else +# pragma GCC push_options +# pragma GCC target ("arch=armv8-a+crypto") +# define MBEDTLS_POP_TARGET_PRAGMA +# endif +# else +# error "Only GCC and Clang supported for MBEDTLS_SHA256_USE_A64_CRYPTO_*" +# endif +# endif +/* *INDENT-ON* */ +# include +# endif +# if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) +# if defined(__unix__) +# if defined(__linux__) +/* Our preferred method of detection is getauxval() */ +# include +# endif +/* Use SIGILL on Unix, and fall back to it on Linux */ +# include +# endif +# endif +#elif defined(_M_ARM64) +# if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) +# include +# endif +#else +# undef MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY +# undef MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT +#endif + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) +/* + * Capability detection code comes early, so we can disable + * MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT if no detection mechanism found + */ +#if defined(HWCAP_SHA2) +static int mbedtls_a64_crypto_sha256_determine_support(void) +{ + return (getauxval(AT_HWCAP) & HWCAP_SHA2) ? 1 : 0; +} +#elif defined(__APPLE__) +static int mbedtls_a64_crypto_sha256_determine_support(void) +{ + return 1; +} +#elif defined(_M_ARM64) +#define WIN32_LEAN_AND_MEAN +#include +#include + +static int mbedtls_a64_crypto_sha256_determine_support(void) +{ + return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE) ? + 1 : 0; +} +#elif defined(__unix__) && defined(SIG_SETMASK) +/* Detection with SIGILL, setjmp() and longjmp() */ +#include +#include + +static jmp_buf return_from_sigill; + +/* + * A64 SHA256 support detection via SIGILL + */ +static void sigill_handler(int signal) +{ + (void) signal; + longjmp(return_from_sigill, 1); +} + +static int mbedtls_a64_crypto_sha256_determine_support(void) +{ + struct sigaction old_action, new_action; + + sigset_t old_mask; + if (sigprocmask(0, NULL, &old_mask)) { + return 0; + } + + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + new_action.sa_handler = sigill_handler; + + sigaction(SIGILL, &new_action, &old_action); + + static int ret = 0; + + if (setjmp(return_from_sigill) == 0) { /* First return only */ + /* If this traps, we will return a second time from setjmp() with 1 */ + asm ("sha256h q0, q0, v0.4s" : : : "v0"); + ret = 1; + } + + sigaction(SIGILL, &old_action, NULL); + sigprocmask(SIG_SETMASK, &old_mask, NULL); + + return ret; +} +#else +#warning "No mechanism to detect A64_CRYPTO found, using C code only" +#undef MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT +#endif /* HWCAP_SHA2, __APPLE__, __unix__ && SIG_SETMASK */ + +#endif /* MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT */ + +#if !defined(MBEDTLS_SHA256_ALT) + +#define SHA256_BLOCK_SIZE 64 + +void mbedtls_sha256_init(mbedtls_sha256_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_sha256_context)); +} + +void mbedtls_sha256_free(mbedtls_sha256_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_sha256_context)); +} + +void mbedtls_sha256_clone(mbedtls_sha256_context *dst, + const mbedtls_sha256_context *src) +{ + *dst = *src; +} + +/* + * SHA-256 context setup + */ +int mbedtls_sha256_starts(mbedtls_sha256_context *ctx, int is224) +{ +#if defined(MBEDTLS_SHA224_C) && defined(MBEDTLS_SHA256_C) + if (is224 != 0 && is224 != 1) { + return MBEDTLS_ERR_SHA256_BAD_INPUT_DATA; + } +#elif defined(MBEDTLS_SHA256_C) + if (is224 != 0) { + return MBEDTLS_ERR_SHA256_BAD_INPUT_DATA; + } +#else /* defined MBEDTLS_SHA224_C only */ + if (is224 == 0) { + return MBEDTLS_ERR_SHA256_BAD_INPUT_DATA; + } +#endif + + ctx->total[0] = 0; + ctx->total[1] = 0; + + if (is224 == 0) { +#if defined(MBEDTLS_SHA256_C) + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +#endif + } else { +#if defined(MBEDTLS_SHA224_C) + ctx->state[0] = 0xC1059ED8; + ctx->state[1] = 0x367CD507; + ctx->state[2] = 0x3070DD17; + ctx->state[3] = 0xF70E5939; + ctx->state[4] = 0xFFC00B31; + ctx->state[5] = 0x68581511; + ctx->state[6] = 0x64F98FA7; + ctx->state[7] = 0xBEFA4FA4; +#endif + } + +#if defined(MBEDTLS_SHA224_C) + ctx->is224 = is224; +#endif + + return 0; +} + +#if !defined(MBEDTLS_SHA256_PROCESS_ALT) +static const uint32_t K[] = +{ + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2, +}; + +#endif + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) +# define mbedtls_internal_sha256_process_many_a64_crypto mbedtls_internal_sha256_process_many +# define mbedtls_internal_sha256_process_a64_crypto mbedtls_internal_sha256_process +#endif + +static size_t mbedtls_internal_sha256_process_many_a64_crypto( + mbedtls_sha256_context *ctx, const uint8_t *msg, size_t len) +{ + uint32x4_t abcd = vld1q_u32(&ctx->state[0]); + uint32x4_t efgh = vld1q_u32(&ctx->state[4]); + + size_t processed = 0; + + for (; + len >= SHA256_BLOCK_SIZE; + processed += SHA256_BLOCK_SIZE, + msg += SHA256_BLOCK_SIZE, + len -= SHA256_BLOCK_SIZE) { + uint32x4_t tmp, abcd_prev; + + uint32x4_t abcd_orig = abcd; + uint32x4_t efgh_orig = efgh; + + uint32x4_t sched0 = (uint32x4_t) vld1q_u8(msg + 16 * 0); + uint32x4_t sched1 = (uint32x4_t) vld1q_u8(msg + 16 * 1); + uint32x4_t sched2 = (uint32x4_t) vld1q_u8(msg + 16 * 2); + uint32x4_t sched3 = (uint32x4_t) vld1q_u8(msg + 16 * 3); + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ /* Will be true if not defined */ + /* Untested on BE */ + sched0 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched0))); + sched1 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched1))); + sched2 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched2))); + sched3 = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(sched3))); +#endif + + /* Rounds 0 to 3 */ + tmp = vaddq_u32(sched0, vld1q_u32(&K[0])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + + /* Rounds 4 to 7 */ + tmp = vaddq_u32(sched1, vld1q_u32(&K[4])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + + /* Rounds 8 to 11 */ + tmp = vaddq_u32(sched2, vld1q_u32(&K[8])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + + /* Rounds 12 to 15 */ + tmp = vaddq_u32(sched3, vld1q_u32(&K[12])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + + for (int t = 16; t < 64; t += 16) { + /* Rounds t to t + 3 */ + sched0 = vsha256su1q_u32(vsha256su0q_u32(sched0, sched1), sched2, sched3); + tmp = vaddq_u32(sched0, vld1q_u32(&K[t])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + + /* Rounds t + 4 to t + 7 */ + sched1 = vsha256su1q_u32(vsha256su0q_u32(sched1, sched2), sched3, sched0); + tmp = vaddq_u32(sched1, vld1q_u32(&K[t + 4])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + + /* Rounds t + 8 to t + 11 */ + sched2 = vsha256su1q_u32(vsha256su0q_u32(sched2, sched3), sched0, sched1); + tmp = vaddq_u32(sched2, vld1q_u32(&K[t + 8])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + + /* Rounds t + 12 to t + 15 */ + sched3 = vsha256su1q_u32(vsha256su0q_u32(sched3, sched0), sched1, sched2); + tmp = vaddq_u32(sched3, vld1q_u32(&K[t + 12])); + abcd_prev = abcd; + abcd = vsha256hq_u32(abcd_prev, efgh, tmp); + efgh = vsha256h2q_u32(efgh, abcd_prev, tmp); + } + + abcd = vaddq_u32(abcd, abcd_orig); + efgh = vaddq_u32(efgh, efgh_orig); + } + + vst1q_u32(&ctx->state[0], abcd); + vst1q_u32(&ctx->state[4], efgh); + + return processed; +} + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) +/* + * This function is for internal use only if we are building both C and A64 + * versions, otherwise it is renamed to be the public mbedtls_internal_sha256_process() + */ +static +#endif +int mbedtls_internal_sha256_process_a64_crypto(mbedtls_sha256_context *ctx, + const unsigned char data[SHA256_BLOCK_SIZE]) +{ + return (mbedtls_internal_sha256_process_many_a64_crypto(ctx, data, + SHA256_BLOCK_SIZE) == + SHA256_BLOCK_SIZE) ? 0 : -1; +} + +#if defined(MBEDTLS_POP_TARGET_PRAGMA) +#if defined(__clang__) +#pragma clang attribute pop +#elif defined(__GNUC__) +#pragma GCC pop_options +#endif +#undef MBEDTLS_POP_TARGET_PRAGMA +#endif + +#endif /* MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT || MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY */ + +#if !defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) +#define mbedtls_internal_sha256_process_many_c mbedtls_internal_sha256_process_many +#define mbedtls_internal_sha256_process_c mbedtls_internal_sha256_process +#endif + + +#if !defined(MBEDTLS_SHA256_PROCESS_ALT) && \ + !defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) + +#define SHR(x, n) (((x) & 0xFFFFFFFF) >> (n)) +#define ROTR(x, n) (SHR(x, n) | ((x) << (32 - (n)))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) + +#define F0(x, y, z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) + +#define R(t) \ + ( \ + local.W[t] = S1(local.W[(t) - 2]) + local.W[(t) - 7] + \ + S0(local.W[(t) - 15]) + local.W[(t) - 16] \ + ) + +#define P(a, b, c, d, e, f, g, h, x, K) \ + do \ + { \ + local.temp1 = (h) + S3(e) + F1((e), (f), (g)) + (K) + (x); \ + local.temp2 = S2(a) + F0((a), (b), (c)); \ + (d) += local.temp1; (h) = local.temp1 + local.temp2; \ + } while (0) + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) +/* + * This function is for internal use only if we are building both C and A64 + * versions, otherwise it is renamed to be the public mbedtls_internal_sha256_process() + */ +static +#endif +int mbedtls_internal_sha256_process_c(mbedtls_sha256_context *ctx, + const unsigned char data[SHA256_BLOCK_SIZE]) +{ + struct { + uint32_t temp1, temp2, W[64]; + uint32_t A[8]; + } local; + + unsigned int i; + + for (i = 0; i < 8; i++) { + local.A[i] = ctx->state[i]; + } + +#if defined(MBEDTLS_SHA256_SMALLER) + for (i = 0; i < 64; i++) { + if (i < 16) { + local.W[i] = MBEDTLS_GET_UINT32_BE(data, 4 * i); + } else { + R(i); + } + + P(local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], + local.A[5], local.A[6], local.A[7], local.W[i], K[i]); + + local.temp1 = local.A[7]; local.A[7] = local.A[6]; + local.A[6] = local.A[5]; local.A[5] = local.A[4]; + local.A[4] = local.A[3]; local.A[3] = local.A[2]; + local.A[2] = local.A[1]; local.A[1] = local.A[0]; + local.A[0] = local.temp1; + } +#else /* MBEDTLS_SHA256_SMALLER */ + for (i = 0; i < 16; i++) { + local.W[i] = MBEDTLS_GET_UINT32_BE(data, 4 * i); + } + + for (i = 0; i < 16; i += 8) { + P(local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], + local.A[5], local.A[6], local.A[7], local.W[i+0], K[i+0]); + P(local.A[7], local.A[0], local.A[1], local.A[2], local.A[3], + local.A[4], local.A[5], local.A[6], local.W[i+1], K[i+1]); + P(local.A[6], local.A[7], local.A[0], local.A[1], local.A[2], + local.A[3], local.A[4], local.A[5], local.W[i+2], K[i+2]); + P(local.A[5], local.A[6], local.A[7], local.A[0], local.A[1], + local.A[2], local.A[3], local.A[4], local.W[i+3], K[i+3]); + P(local.A[4], local.A[5], local.A[6], local.A[7], local.A[0], + local.A[1], local.A[2], local.A[3], local.W[i+4], K[i+4]); + P(local.A[3], local.A[4], local.A[5], local.A[6], local.A[7], + local.A[0], local.A[1], local.A[2], local.W[i+5], K[i+5]); + P(local.A[2], local.A[3], local.A[4], local.A[5], local.A[6], + local.A[7], local.A[0], local.A[1], local.W[i+6], K[i+6]); + P(local.A[1], local.A[2], local.A[3], local.A[4], local.A[5], + local.A[6], local.A[7], local.A[0], local.W[i+7], K[i+7]); + } + + for (i = 16; i < 64; i += 8) { + P(local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], + local.A[5], local.A[6], local.A[7], R(i+0), K[i+0]); + P(local.A[7], local.A[0], local.A[1], local.A[2], local.A[3], + local.A[4], local.A[5], local.A[6], R(i+1), K[i+1]); + P(local.A[6], local.A[7], local.A[0], local.A[1], local.A[2], + local.A[3], local.A[4], local.A[5], R(i+2), K[i+2]); + P(local.A[5], local.A[6], local.A[7], local.A[0], local.A[1], + local.A[2], local.A[3], local.A[4], R(i+3), K[i+3]); + P(local.A[4], local.A[5], local.A[6], local.A[7], local.A[0], + local.A[1], local.A[2], local.A[3], R(i+4), K[i+4]); + P(local.A[3], local.A[4], local.A[5], local.A[6], local.A[7], + local.A[0], local.A[1], local.A[2], R(i+5), K[i+5]); + P(local.A[2], local.A[3], local.A[4], local.A[5], local.A[6], + local.A[7], local.A[0], local.A[1], R(i+6), K[i+6]); + P(local.A[1], local.A[2], local.A[3], local.A[4], local.A[5], + local.A[6], local.A[7], local.A[0], R(i+7), K[i+7]); + } +#endif /* MBEDTLS_SHA256_SMALLER */ + + for (i = 0; i < 8; i++) { + ctx->state[i] += local.A[i]; + } + + /* Zeroise buffers and variables to clear sensitive data from memory. */ + mbedtls_platform_zeroize(&local, sizeof(local)); + + return 0; +} + +#endif /* !MBEDTLS_SHA256_PROCESS_ALT && !MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY */ + + +#if !defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) + +static size_t mbedtls_internal_sha256_process_many_c( + mbedtls_sha256_context *ctx, const uint8_t *data, size_t len) +{ + size_t processed = 0; + + while (len >= SHA256_BLOCK_SIZE) { + if (mbedtls_internal_sha256_process_c(ctx, data) != 0) { + return 0; + } + + data += SHA256_BLOCK_SIZE; + len -= SHA256_BLOCK_SIZE; + + processed += SHA256_BLOCK_SIZE; + } + + return processed; +} + +#endif /* !MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY */ + + +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) + +static int mbedtls_a64_crypto_sha256_has_support(void) +{ + static int done = 0; + static int supported = 0; + + if (!done) { + supported = mbedtls_a64_crypto_sha256_determine_support(); + done = 1; + } + + return supported; +} + +static size_t mbedtls_internal_sha256_process_many(mbedtls_sha256_context *ctx, + const uint8_t *msg, size_t len) +{ + if (mbedtls_a64_crypto_sha256_has_support()) { + return mbedtls_internal_sha256_process_many_a64_crypto(ctx, msg, len); + } else { + return mbedtls_internal_sha256_process_many_c(ctx, msg, len); + } +} + +int mbedtls_internal_sha256_process(mbedtls_sha256_context *ctx, + const unsigned char data[SHA256_BLOCK_SIZE]) +{ + if (mbedtls_a64_crypto_sha256_has_support()) { + return mbedtls_internal_sha256_process_a64_crypto(ctx, data); + } else { + return mbedtls_internal_sha256_process_c(ctx, data); + } +} + +#endif /* MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT */ + + +/* + * SHA-256 process buffer + */ +int mbedtls_sha256_update(mbedtls_sha256_context *ctx, + const unsigned char *input, + size_t ilen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t fill; + uint32_t left; + + if (ilen == 0) { + return 0; + } + + left = ctx->total[0] & 0x3F; + fill = SHA256_BLOCK_SIZE - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if (ctx->total[0] < (uint32_t) ilen) { + ctx->total[1]++; + } + + if (left && ilen >= fill) { + memcpy((void *) (ctx->buffer + left), input, fill); + + if ((ret = mbedtls_internal_sha256_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= SHA256_BLOCK_SIZE) { + size_t processed = + mbedtls_internal_sha256_process_many(ctx, input, ilen); + if (processed < SHA256_BLOCK_SIZE) { + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + } + + input += processed; + ilen -= processed; + } + + if (ilen > 0) { + memcpy((void *) (ctx->buffer + left), input, ilen); + } + + return 0; +} + +/* + * SHA-256 final digest + */ +int mbedtls_sha256_finish(mbedtls_sha256_context *ctx, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + uint32_t used; + uint32_t high, low; + + /* + * Add padding: 0x80 then 0x00 until 8 bytes remain for the length + */ + used = ctx->total[0] & 0x3F; + + ctx->buffer[used++] = 0x80; + + if (used <= 56) { + /* Enough room for padding + length in current block */ + memset(ctx->buffer + used, 0, 56 - used); + } else { + /* We'll need an extra block */ + memset(ctx->buffer + used, 0, SHA256_BLOCK_SIZE - used); + + if ((ret = mbedtls_internal_sha256_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + memset(ctx->buffer, 0, 56); + } + + /* + * Add message length + */ + high = (ctx->total[0] >> 29) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + MBEDTLS_PUT_UINT32_BE(high, ctx->buffer, 56); + MBEDTLS_PUT_UINT32_BE(low, ctx->buffer, 60); + + if ((ret = mbedtls_internal_sha256_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + /* + * Output final state + */ + MBEDTLS_PUT_UINT32_BE(ctx->state[0], output, 0); + MBEDTLS_PUT_UINT32_BE(ctx->state[1], output, 4); + MBEDTLS_PUT_UINT32_BE(ctx->state[2], output, 8); + MBEDTLS_PUT_UINT32_BE(ctx->state[3], output, 12); + MBEDTLS_PUT_UINT32_BE(ctx->state[4], output, 16); + MBEDTLS_PUT_UINT32_BE(ctx->state[5], output, 20); + MBEDTLS_PUT_UINT32_BE(ctx->state[6], output, 24); + + int truncated = 0; +#if defined(MBEDTLS_SHA224_C) + truncated = ctx->is224; +#endif + if (!truncated) { + MBEDTLS_PUT_UINT32_BE(ctx->state[7], output, 28); + } + + return 0; +} + +#endif /* !MBEDTLS_SHA256_ALT */ + +/* + * output = SHA-256( input buffer ) + */ +int mbedtls_sha256(const unsigned char *input, + size_t ilen, + unsigned char *output, + int is224) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_sha256_context ctx; + +#if defined(MBEDTLS_SHA224_C) && defined(MBEDTLS_SHA256_C) + if (is224 != 0 && is224 != 1) { + return MBEDTLS_ERR_SHA256_BAD_INPUT_DATA; + } +#elif defined(MBEDTLS_SHA256_C) + if (is224 != 0) { + return MBEDTLS_ERR_SHA256_BAD_INPUT_DATA; + } +#else /* defined MBEDTLS_SHA224_C only */ + if (is224 == 0) { + return MBEDTLS_ERR_SHA256_BAD_INPUT_DATA; + } +#endif + + mbedtls_sha256_init(&ctx); + + if ((ret = mbedtls_sha256_starts(&ctx, is224)) != 0) { + goto exit; + } + + if ((ret = mbedtls_sha256_update(&ctx, input, ilen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_sha256_finish(&ctx, output)) != 0) { + goto exit; + } + +exit: + mbedtls_sha256_free(&ctx); + + return ret; +} + +#if defined(MBEDTLS_SELF_TEST) +/* + * FIPS-180-2 test vectors + */ +static const unsigned char sha_test_buf[3][57] = +{ + { "abc" }, + { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, + { "" } +}; + +static const size_t sha_test_buflen[3] = +{ + 3, 56, 1000 +}; + +typedef const unsigned char (sha_test_sum_t)[32]; + +/* + * SHA-224 test vectors + */ +#if defined(MBEDTLS_SHA224_C) +static sha_test_sum_t sha224_test_sum[] = +{ + { 0x23, 0x09, 0x7D, 0x22, 0x34, 0x05, 0xD8, 0x22, + 0x86, 0x42, 0xA4, 0x77, 0xBD, 0xA2, 0x55, 0xB3, + 0x2A, 0xAD, 0xBC, 0xE4, 0xBD, 0xA0, 0xB3, 0xF7, + 0xE3, 0x6C, 0x9D, 0xA7 }, + { 0x75, 0x38, 0x8B, 0x16, 0x51, 0x27, 0x76, 0xCC, + 0x5D, 0xBA, 0x5D, 0xA1, 0xFD, 0x89, 0x01, 0x50, + 0xB0, 0xC6, 0x45, 0x5C, 0xB4, 0xF5, 0x8B, 0x19, + 0x52, 0x52, 0x25, 0x25 }, + { 0x20, 0x79, 0x46, 0x55, 0x98, 0x0C, 0x91, 0xD8, + 0xBB, 0xB4, 0xC1, 0xEA, 0x97, 0x61, 0x8A, 0x4B, + 0xF0, 0x3F, 0x42, 0x58, 0x19, 0x48, 0xB2, 0xEE, + 0x4E, 0xE7, 0xAD, 0x67 } +}; +#endif + +/* + * SHA-256 test vectors + */ +#if defined(MBEDTLS_SHA256_C) +static sha_test_sum_t sha256_test_sum[] = +{ + { 0xBA, 0x78, 0x16, 0xBF, 0x8F, 0x01, 0xCF, 0xEA, + 0x41, 0x41, 0x40, 0xDE, 0x5D, 0xAE, 0x22, 0x23, + 0xB0, 0x03, 0x61, 0xA3, 0x96, 0x17, 0x7A, 0x9C, + 0xB4, 0x10, 0xFF, 0x61, 0xF2, 0x00, 0x15, 0xAD }, + { 0x24, 0x8D, 0x6A, 0x61, 0xD2, 0x06, 0x38, 0xB8, + 0xE5, 0xC0, 0x26, 0x93, 0x0C, 0x3E, 0x60, 0x39, + 0xA3, 0x3C, 0xE4, 0x59, 0x64, 0xFF, 0x21, 0x67, + 0xF6, 0xEC, 0xED, 0xD4, 0x19, 0xDB, 0x06, 0xC1 }, + { 0xCD, 0xC7, 0x6E, 0x5C, 0x99, 0x14, 0xFB, 0x92, + 0x81, 0xA1, 0xC7, 0xE2, 0x84, 0xD7, 0x3E, 0x67, + 0xF1, 0x80, 0x9A, 0x48, 0xA4, 0x97, 0x20, 0x0E, + 0x04, 0x6D, 0x39, 0xCC, 0xC7, 0x11, 0x2C, 0xD0 } +}; +#endif + +/* + * Checkup routine + */ +static int mbedtls_sha256_common_self_test(int verbose, int is224) +{ + int i, buflen, ret = 0; + unsigned char *buf; + unsigned char sha256sum[32]; + mbedtls_sha256_context ctx; + +#if defined(MBEDTLS_SHA224_C) && defined(MBEDTLS_SHA256_C) + sha_test_sum_t *sha_test_sum = (is224) ? sha224_test_sum : sha256_test_sum; +#elif defined(MBEDTLS_SHA256_C) + sha_test_sum_t *sha_test_sum = sha256_test_sum; +#else + sha_test_sum_t *sha_test_sum = sha224_test_sum; +#endif + + buf = mbedtls_calloc(1024, sizeof(unsigned char)); + if (NULL == buf) { + if (verbose != 0) { + mbedtls_printf("Buffer allocation failed\n"); + } + + return 1; + } + + mbedtls_sha256_init(&ctx); + + for (i = 0; i < 3; i++) { + if (verbose != 0) { + mbedtls_printf(" SHA-%d test #%d: ", 256 - is224 * 32, i + 1); + } + + if ((ret = mbedtls_sha256_starts(&ctx, is224)) != 0) { + goto fail; + } + + if (i == 2) { + memset(buf, 'a', buflen = 1000); + + for (int j = 0; j < 1000; j++) { + ret = mbedtls_sha256_update(&ctx, buf, buflen); + if (ret != 0) { + goto fail; + } + } + + } else { + ret = mbedtls_sha256_update(&ctx, sha_test_buf[i], + sha_test_buflen[i]); + if (ret != 0) { + goto fail; + } + } + + if ((ret = mbedtls_sha256_finish(&ctx, sha256sum)) != 0) { + goto fail; + } + + + if (memcmp(sha256sum, sha_test_sum[i], 32 - is224 * 4) != 0) { + ret = 1; + goto fail; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + goto exit; + +fail: + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + +exit: + mbedtls_sha256_free(&ctx); + mbedtls_free(buf); + + return ret; +} + +#if defined(MBEDTLS_SHA256_C) +int mbedtls_sha256_self_test(int verbose) +{ + return mbedtls_sha256_common_self_test(verbose, 0); +} +#endif /* MBEDTLS_SHA256_C */ + +#if defined(MBEDTLS_SHA224_C) +int mbedtls_sha224_self_test(int verbose) +{ + return mbedtls_sha256_common_self_test(verbose, 1); +} +#endif /* MBEDTLS_SHA224_C */ + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_SHA256_C || MBEDTLS_SHA224_C */ diff --git a/r5dev/thirdparty/mbedtls/sha512.c b/r5dev/thirdparty/mbedtls/sha512.c new file mode 100644 index 00000000..67acfee4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/sha512.c @@ -0,0 +1,1108 @@ +/* + * FIPS-180-2 compliant SHA-384/512 implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The SHA-512 Secure Hash Standard was published by NIST in 2002. + * + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + */ + +#if defined(__aarch64__) && !defined(__ARM_FEATURE_SHA512) && \ + defined(__clang__) && __clang_major__ >= 7 +/* TODO: Re-consider above after https://reviews.llvm.org/D131064 merged. + * + * The intrinsic declaration are guarded by predefined ACLE macros in clang: + * these are normally only enabled by the -march option on the command line. + * By defining the macros ourselves we gain access to those declarations without + * requiring -march on the command line. + * + * `arm_neon.h` could be included by any header file, so we put these defines + * at the top of this file, before any includes. + */ +#define __ARM_FEATURE_SHA512 1 +#define MBEDTLS_ENABLE_ARM_SHA3_EXTENSIONS_COMPILER_FLAG +#endif + +#include "common.h" + +#if defined(MBEDTLS_SHA512_C) || defined(MBEDTLS_SHA384_C) + +#include "mbedtls/sha512.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/error.h" + +#if defined(_MSC_VER) || defined(__WATCOMC__) + #define UL64(x) x##ui64 +#else + #define UL64(x) x##ULL +#endif + +#include + +#include "mbedtls/platform.h" + +#if defined(__aarch64__) +# if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) +/* *INDENT-OFF* */ +/* + * Best performance comes from most recent compilers, with intrinsics and -O3. + * Must compile with -march=armv8.2-a+sha3, but we can't detect armv8.2-a, and + * can't always detect __ARM_FEATURE_SHA512 (notably clang 7-12). + * + * GCC < 8 won't work at all (lacks the sha512 instructions) + * GCC >= 8 uses intrinsics, sets __ARM_FEATURE_SHA512 + * + * Clang < 7 won't work at all (lacks the sha512 instructions) + * Clang 7-12 don't have intrinsics (but we work around that with inline + * assembler) or __ARM_FEATURE_SHA512 + * Clang == 13.0.0 same as clang 12 (only seen on macOS) + * Clang >= 13.0.1 has __ARM_FEATURE_SHA512 and intrinsics + */ +# if !defined(__ARM_FEATURE_SHA512) || defined(MBEDTLS_ENABLE_ARM_SHA3_EXTENSIONS_COMPILER_FLAG) + /* Test Clang first, as it defines __GNUC__ */ +# if defined(__clang__) +# if __clang_major__ < 7 +# error "A more recent Clang is required for MBEDTLS_SHA512_USE_A64_CRYPTO_*" +# else +# pragma clang attribute push (__attribute__((target("sha3"))), apply_to=function) +# define MBEDTLS_POP_TARGET_PRAGMA +# endif +# elif defined(__GNUC__) +# if __GNUC__ < 8 +# error "A more recent GCC is required for MBEDTLS_SHA512_USE_A64_CRYPTO_*" +# else +# pragma GCC push_options +# pragma GCC target ("arch=armv8.2-a+sha3") +# define MBEDTLS_POP_TARGET_PRAGMA +# endif +# else +# error "Only GCC and Clang supported for MBEDTLS_SHA512_USE_A64_CRYPTO_*" +# endif +# endif +/* *INDENT-ON* */ +# include +# endif +# if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) +# if defined(__unix__) +# if defined(__linux__) +/* Our preferred method of detection is getauxval() */ +# include +# endif +/* Use SIGILL on Unix, and fall back to it on Linux */ +# include +# endif +# endif +#elif defined(_M_ARM64) +# if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) +# include +# endif +#else +# undef MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY +# undef MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT +#endif + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) +/* + * Capability detection code comes early, so we can disable + * MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT if no detection mechanism found + */ +#if defined(HWCAP_SHA512) +static int mbedtls_a64_crypto_sha512_determine_support(void) +{ + return (getauxval(AT_HWCAP) & HWCAP_SHA512) ? 1 : 0; +} +#elif defined(__APPLE__) +#include +#include + +static int mbedtls_a64_crypto_sha512_determine_support(void) +{ + int value = 0; + size_t value_len = sizeof(value); + + int ret = sysctlbyname("hw.optional.armv8_2_sha512", &value, &value_len, + NULL, 0); + return ret == 0 && value != 0; +} +#elif defined(_M_ARM64) +/* + * As of March 2022, there don't appear to be any PF_ARM_V8_* flags + * available to pass to IsProcessorFeaturePresent() to check for + * SHA-512 support. So we fall back to the C code only. + */ +#if defined(_MSC_VER) +#pragma message "No mechanism to detect A64_CRYPTO found, using C code only" +#else +#warning "No mechanism to detect A64_CRYPTO found, using C code only" +#endif +#elif defined(__unix__) && defined(SIG_SETMASK) +/* Detection with SIGILL, setjmp() and longjmp() */ +#include +#include + +static jmp_buf return_from_sigill; + +/* + * A64 SHA512 support detection via SIGILL + */ +static void sigill_handler(int signal) +{ + (void) signal; + longjmp(return_from_sigill, 1); +} + +static int mbedtls_a64_crypto_sha512_determine_support(void) +{ + struct sigaction old_action, new_action; + + sigset_t old_mask; + if (sigprocmask(0, NULL, &old_mask)) { + return 0; + } + + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + new_action.sa_handler = sigill_handler; + + sigaction(SIGILL, &new_action, &old_action); + + static int ret = 0; + + if (setjmp(return_from_sigill) == 0) { /* First return only */ + /* If this traps, we will return a second time from setjmp() with 1 */ + asm ("sha512h q0, q0, v0.2d" : : : "v0"); + ret = 1; + } + + sigaction(SIGILL, &old_action, NULL); + sigprocmask(SIG_SETMASK, &old_mask, NULL); + + return ret; +} +#else +#warning "No mechanism to detect A64_CRYPTO found, using C code only" +#undef MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT +#endif /* HWCAP_SHA512, __APPLE__, __unix__ && SIG_SETMASK */ + +#endif /* MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT */ + +#if !defined(MBEDTLS_SHA512_ALT) + +#define SHA512_BLOCK_SIZE 128 + +#if defined(MBEDTLS_SHA512_SMALLER) +static void sha512_put_uint64_be(uint64_t n, unsigned char *b, uint8_t i) +{ + MBEDTLS_PUT_UINT64_BE(n, b, i); +} +#else +#define sha512_put_uint64_be MBEDTLS_PUT_UINT64_BE +#endif /* MBEDTLS_SHA512_SMALLER */ + +void mbedtls_sha512_init(mbedtls_sha512_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_sha512_context)); +} + +void mbedtls_sha512_free(mbedtls_sha512_context *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_sha512_context)); +} + +void mbedtls_sha512_clone(mbedtls_sha512_context *dst, + const mbedtls_sha512_context *src) +{ + *dst = *src; +} + +/* + * SHA-512 context setup + */ +int mbedtls_sha512_starts(mbedtls_sha512_context *ctx, int is384) +{ +#if defined(MBEDTLS_SHA384_C) && defined(MBEDTLS_SHA512_C) + if (is384 != 0 && is384 != 1) { + return MBEDTLS_ERR_SHA512_BAD_INPUT_DATA; + } +#elif defined(MBEDTLS_SHA512_C) + if (is384 != 0) { + return MBEDTLS_ERR_SHA512_BAD_INPUT_DATA; + } +#else /* defined MBEDTLS_SHA384_C only */ + if (is384 == 0) { + return MBEDTLS_ERR_SHA512_BAD_INPUT_DATA; + } +#endif + + ctx->total[0] = 0; + ctx->total[1] = 0; + + if (is384 == 0) { +#if defined(MBEDTLS_SHA512_C) + ctx->state[0] = UL64(0x6A09E667F3BCC908); + ctx->state[1] = UL64(0xBB67AE8584CAA73B); + ctx->state[2] = UL64(0x3C6EF372FE94F82B); + ctx->state[3] = UL64(0xA54FF53A5F1D36F1); + ctx->state[4] = UL64(0x510E527FADE682D1); + ctx->state[5] = UL64(0x9B05688C2B3E6C1F); + ctx->state[6] = UL64(0x1F83D9ABFB41BD6B); + ctx->state[7] = UL64(0x5BE0CD19137E2179); +#endif /* MBEDTLS_SHA512_C */ + } else { +#if defined(MBEDTLS_SHA384_C) + ctx->state[0] = UL64(0xCBBB9D5DC1059ED8); + ctx->state[1] = UL64(0x629A292A367CD507); + ctx->state[2] = UL64(0x9159015A3070DD17); + ctx->state[3] = UL64(0x152FECD8F70E5939); + ctx->state[4] = UL64(0x67332667FFC00B31); + ctx->state[5] = UL64(0x8EB44A8768581511); + ctx->state[6] = UL64(0xDB0C2E0D64F98FA7); + ctx->state[7] = UL64(0x47B5481DBEFA4FA4); +#endif /* MBEDTLS_SHA384_C */ + } + +#if defined(MBEDTLS_SHA384_C) + ctx->is384 = is384; +#endif + + return 0; +} + +#if !defined(MBEDTLS_SHA512_PROCESS_ALT) + +/* + * Round constants + */ +static const uint64_t K[80] = +{ + UL64(0x428A2F98D728AE22), UL64(0x7137449123EF65CD), + UL64(0xB5C0FBCFEC4D3B2F), UL64(0xE9B5DBA58189DBBC), + UL64(0x3956C25BF348B538), UL64(0x59F111F1B605D019), + UL64(0x923F82A4AF194F9B), UL64(0xAB1C5ED5DA6D8118), + UL64(0xD807AA98A3030242), UL64(0x12835B0145706FBE), + UL64(0x243185BE4EE4B28C), UL64(0x550C7DC3D5FFB4E2), + UL64(0x72BE5D74F27B896F), UL64(0x80DEB1FE3B1696B1), + UL64(0x9BDC06A725C71235), UL64(0xC19BF174CF692694), + UL64(0xE49B69C19EF14AD2), UL64(0xEFBE4786384F25E3), + UL64(0x0FC19DC68B8CD5B5), UL64(0x240CA1CC77AC9C65), + UL64(0x2DE92C6F592B0275), UL64(0x4A7484AA6EA6E483), + UL64(0x5CB0A9DCBD41FBD4), UL64(0x76F988DA831153B5), + UL64(0x983E5152EE66DFAB), UL64(0xA831C66D2DB43210), + UL64(0xB00327C898FB213F), UL64(0xBF597FC7BEEF0EE4), + UL64(0xC6E00BF33DA88FC2), UL64(0xD5A79147930AA725), + UL64(0x06CA6351E003826F), UL64(0x142929670A0E6E70), + UL64(0x27B70A8546D22FFC), UL64(0x2E1B21385C26C926), + UL64(0x4D2C6DFC5AC42AED), UL64(0x53380D139D95B3DF), + UL64(0x650A73548BAF63DE), UL64(0x766A0ABB3C77B2A8), + UL64(0x81C2C92E47EDAEE6), UL64(0x92722C851482353B), + UL64(0xA2BFE8A14CF10364), UL64(0xA81A664BBC423001), + UL64(0xC24B8B70D0F89791), UL64(0xC76C51A30654BE30), + UL64(0xD192E819D6EF5218), UL64(0xD69906245565A910), + UL64(0xF40E35855771202A), UL64(0x106AA07032BBD1B8), + UL64(0x19A4C116B8D2D0C8), UL64(0x1E376C085141AB53), + UL64(0x2748774CDF8EEB99), UL64(0x34B0BCB5E19B48A8), + UL64(0x391C0CB3C5C95A63), UL64(0x4ED8AA4AE3418ACB), + UL64(0x5B9CCA4F7763E373), UL64(0x682E6FF3D6B2B8A3), + UL64(0x748F82EE5DEFB2FC), UL64(0x78A5636F43172F60), + UL64(0x84C87814A1F0AB72), UL64(0x8CC702081A6439EC), + UL64(0x90BEFFFA23631E28), UL64(0xA4506CEBDE82BDE9), + UL64(0xBEF9A3F7B2C67915), UL64(0xC67178F2E372532B), + UL64(0xCA273ECEEA26619C), UL64(0xD186B8C721C0C207), + UL64(0xEADA7DD6CDE0EB1E), UL64(0xF57D4F7FEE6ED178), + UL64(0x06F067AA72176FBA), UL64(0x0A637DC5A2C898A6), + UL64(0x113F9804BEF90DAE), UL64(0x1B710B35131C471B), + UL64(0x28DB77F523047D84), UL64(0x32CAAB7B40C72493), + UL64(0x3C9EBE0A15C9BEBC), UL64(0x431D67C49C100D4C), + UL64(0x4CC5D4BECB3E42B6), UL64(0x597F299CFC657E2A), + UL64(0x5FCB6FAB3AD6FAEC), UL64(0x6C44198C4A475817) +}; +#endif + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) || \ + defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) +# define mbedtls_internal_sha512_process_many_a64_crypto mbedtls_internal_sha512_process_many +# define mbedtls_internal_sha512_process_a64_crypto mbedtls_internal_sha512_process +#endif + +/* Accelerated SHA-512 implementation originally written by Simon Tatham for PuTTY, + * under the MIT licence; dual-licensed as Apache 2 with his kind permission. + */ + +#if defined(__clang__) && \ + (__clang_major__ < 13 || \ + (__clang_major__ == 13 && __clang_minor__ == 0 && __clang_patchlevel__ == 0)) +static inline uint64x2_t vsha512su0q_u64(uint64x2_t x, uint64x2_t y) +{ + asm ("sha512su0 %0.2D,%1.2D" : "+w" (x) : "w" (y)); + return x; +} +static inline uint64x2_t vsha512su1q_u64(uint64x2_t x, uint64x2_t y, uint64x2_t z) +{ + asm ("sha512su1 %0.2D,%1.2D,%2.2D" : "+w" (x) : "w" (y), "w" (z)); + return x; +} +static inline uint64x2_t vsha512hq_u64(uint64x2_t x, uint64x2_t y, uint64x2_t z) +{ + asm ("sha512h %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z)); + return x; +} +static inline uint64x2_t vsha512h2q_u64(uint64x2_t x, uint64x2_t y, uint64x2_t z) +{ + asm ("sha512h2 %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z)); + return x; +} +#endif /* __clang__ etc */ + +static size_t mbedtls_internal_sha512_process_many_a64_crypto( + mbedtls_sha512_context *ctx, const uint8_t *msg, size_t len) +{ + uint64x2_t ab = vld1q_u64(&ctx->state[0]); + uint64x2_t cd = vld1q_u64(&ctx->state[2]); + uint64x2_t ef = vld1q_u64(&ctx->state[4]); + uint64x2_t gh = vld1q_u64(&ctx->state[6]); + + size_t processed = 0; + + for (; + len >= SHA512_BLOCK_SIZE; + processed += SHA512_BLOCK_SIZE, + msg += SHA512_BLOCK_SIZE, + len -= SHA512_BLOCK_SIZE) { + uint64x2_t initial_sum, sum, intermed; + + uint64x2_t ab_orig = ab; + uint64x2_t cd_orig = cd; + uint64x2_t ef_orig = ef; + uint64x2_t gh_orig = gh; + + uint64x2_t s0 = (uint64x2_t) vld1q_u8(msg + 16 * 0); + uint64x2_t s1 = (uint64x2_t) vld1q_u8(msg + 16 * 1); + uint64x2_t s2 = (uint64x2_t) vld1q_u8(msg + 16 * 2); + uint64x2_t s3 = (uint64x2_t) vld1q_u8(msg + 16 * 3); + uint64x2_t s4 = (uint64x2_t) vld1q_u8(msg + 16 * 4); + uint64x2_t s5 = (uint64x2_t) vld1q_u8(msg + 16 * 5); + uint64x2_t s6 = (uint64x2_t) vld1q_u8(msg + 16 * 6); + uint64x2_t s7 = (uint64x2_t) vld1q_u8(msg + 16 * 7); + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ /* assume LE if these not defined; untested on BE */ + s0 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s0))); + s1 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s1))); + s2 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s2))); + s3 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s3))); + s4 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s4))); + s5 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s5))); + s6 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s6))); + s7 = vreinterpretq_u64_u8(vrev64q_u8(vreinterpretq_u8_u64(s7))); +#endif + + /* Rounds 0 and 1 */ + initial_sum = vaddq_u64(s0, vld1q_u64(&K[0])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), gh); + intermed = vsha512hq_u64(sum, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(intermed, cd, ab); + cd = vaddq_u64(cd, intermed); + + /* Rounds 2 and 3 */ + initial_sum = vaddq_u64(s1, vld1q_u64(&K[2])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ef); + intermed = vsha512hq_u64(sum, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(intermed, ab, gh); + ab = vaddq_u64(ab, intermed); + + /* Rounds 4 and 5 */ + initial_sum = vaddq_u64(s2, vld1q_u64(&K[4])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), cd); + intermed = vsha512hq_u64(sum, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(intermed, gh, ef); + gh = vaddq_u64(gh, intermed); + + /* Rounds 6 and 7 */ + initial_sum = vaddq_u64(s3, vld1q_u64(&K[6])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ab); + intermed = vsha512hq_u64(sum, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(intermed, ef, cd); + ef = vaddq_u64(ef, intermed); + + /* Rounds 8 and 9 */ + initial_sum = vaddq_u64(s4, vld1q_u64(&K[8])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), gh); + intermed = vsha512hq_u64(sum, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(intermed, cd, ab); + cd = vaddq_u64(cd, intermed); + + /* Rounds 10 and 11 */ + initial_sum = vaddq_u64(s5, vld1q_u64(&K[10])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ef); + intermed = vsha512hq_u64(sum, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(intermed, ab, gh); + ab = vaddq_u64(ab, intermed); + + /* Rounds 12 and 13 */ + initial_sum = vaddq_u64(s6, vld1q_u64(&K[12])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), cd); + intermed = vsha512hq_u64(sum, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(intermed, gh, ef); + gh = vaddq_u64(gh, intermed); + + /* Rounds 14 and 15 */ + initial_sum = vaddq_u64(s7, vld1q_u64(&K[14])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ab); + intermed = vsha512hq_u64(sum, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(intermed, ef, cd); + ef = vaddq_u64(ef, intermed); + + for (unsigned int t = 16; t < 80; t += 16) { + /* Rounds t and t + 1 */ + s0 = vsha512su1q_u64(vsha512su0q_u64(s0, s1), s7, vextq_u64(s4, s5, 1)); + initial_sum = vaddq_u64(s0, vld1q_u64(&K[t])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), gh); + intermed = vsha512hq_u64(sum, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(intermed, cd, ab); + cd = vaddq_u64(cd, intermed); + + /* Rounds t + 2 and t + 3 */ + s1 = vsha512su1q_u64(vsha512su0q_u64(s1, s2), s0, vextq_u64(s5, s6, 1)); + initial_sum = vaddq_u64(s1, vld1q_u64(&K[t + 2])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ef); + intermed = vsha512hq_u64(sum, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(intermed, ab, gh); + ab = vaddq_u64(ab, intermed); + + /* Rounds t + 4 and t + 5 */ + s2 = vsha512su1q_u64(vsha512su0q_u64(s2, s3), s1, vextq_u64(s6, s7, 1)); + initial_sum = vaddq_u64(s2, vld1q_u64(&K[t + 4])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), cd); + intermed = vsha512hq_u64(sum, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(intermed, gh, ef); + gh = vaddq_u64(gh, intermed); + + /* Rounds t + 6 and t + 7 */ + s3 = vsha512su1q_u64(vsha512su0q_u64(s3, s4), s2, vextq_u64(s7, s0, 1)); + initial_sum = vaddq_u64(s3, vld1q_u64(&K[t + 6])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ab); + intermed = vsha512hq_u64(sum, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(intermed, ef, cd); + ef = vaddq_u64(ef, intermed); + + /* Rounds t + 8 and t + 9 */ + s4 = vsha512su1q_u64(vsha512su0q_u64(s4, s5), s3, vextq_u64(s0, s1, 1)); + initial_sum = vaddq_u64(s4, vld1q_u64(&K[t + 8])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), gh); + intermed = vsha512hq_u64(sum, vextq_u64(ef, gh, 1), vextq_u64(cd, ef, 1)); + gh = vsha512h2q_u64(intermed, cd, ab); + cd = vaddq_u64(cd, intermed); + + /* Rounds t + 10 and t + 11 */ + s5 = vsha512su1q_u64(vsha512su0q_u64(s5, s6), s4, vextq_u64(s1, s2, 1)); + initial_sum = vaddq_u64(s5, vld1q_u64(&K[t + 10])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ef); + intermed = vsha512hq_u64(sum, vextq_u64(cd, ef, 1), vextq_u64(ab, cd, 1)); + ef = vsha512h2q_u64(intermed, ab, gh); + ab = vaddq_u64(ab, intermed); + + /* Rounds t + 12 and t + 13 */ + s6 = vsha512su1q_u64(vsha512su0q_u64(s6, s7), s5, vextq_u64(s2, s3, 1)); + initial_sum = vaddq_u64(s6, vld1q_u64(&K[t + 12])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), cd); + intermed = vsha512hq_u64(sum, vextq_u64(ab, cd, 1), vextq_u64(gh, ab, 1)); + cd = vsha512h2q_u64(intermed, gh, ef); + gh = vaddq_u64(gh, intermed); + + /* Rounds t + 14 and t + 15 */ + s7 = vsha512su1q_u64(vsha512su0q_u64(s7, s0), s6, vextq_u64(s3, s4, 1)); + initial_sum = vaddq_u64(s7, vld1q_u64(&K[t + 14])); + sum = vaddq_u64(vextq_u64(initial_sum, initial_sum, 1), ab); + intermed = vsha512hq_u64(sum, vextq_u64(gh, ab, 1), vextq_u64(ef, gh, 1)); + ab = vsha512h2q_u64(intermed, ef, cd); + ef = vaddq_u64(ef, intermed); + } + + ab = vaddq_u64(ab, ab_orig); + cd = vaddq_u64(cd, cd_orig); + ef = vaddq_u64(ef, ef_orig); + gh = vaddq_u64(gh, gh_orig); + } + + vst1q_u64(&ctx->state[0], ab); + vst1q_u64(&ctx->state[2], cd); + vst1q_u64(&ctx->state[4], ef); + vst1q_u64(&ctx->state[6], gh); + + return processed; +} + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) +/* + * This function is for internal use only if we are building both C and A64 + * versions, otherwise it is renamed to be the public mbedtls_internal_sha512_process() + */ +static +#endif +int mbedtls_internal_sha512_process_a64_crypto(mbedtls_sha512_context *ctx, + const unsigned char data[SHA512_BLOCK_SIZE]) +{ + return (mbedtls_internal_sha512_process_many_a64_crypto(ctx, data, + SHA512_BLOCK_SIZE) == + SHA512_BLOCK_SIZE) ? 0 : -1; +} + +#if defined(MBEDTLS_POP_TARGET_PRAGMA) +#if defined(__clang__) +#pragma clang attribute pop +#elif defined(__GNUC__) +#pragma GCC pop_options +#endif +#undef MBEDTLS_POP_TARGET_PRAGMA +#endif + +#endif /* MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT || MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY */ + + +#if !defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) +#define mbedtls_internal_sha512_process_many_c mbedtls_internal_sha512_process_many +#define mbedtls_internal_sha512_process_c mbedtls_internal_sha512_process +#endif + + +#if !defined(MBEDTLS_SHA512_PROCESS_ALT) && !defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) +/* + * This function is for internal use only if we are building both C and A64 + * versions, otherwise it is renamed to be the public mbedtls_internal_sha512_process() + */ +static +#endif +int mbedtls_internal_sha512_process_c(mbedtls_sha512_context *ctx, + const unsigned char data[SHA512_BLOCK_SIZE]) +{ + int i; + struct { + uint64_t temp1, temp2, W[80]; + uint64_t A[8]; + } local; + +#define SHR(x, n) ((x) >> (n)) +#define ROTR(x, n) (SHR((x), (n)) | ((x) << (64 - (n)))) + +#define S0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x, 7)) +#define S1(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHR(x, 6)) + +#define S2(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define S3(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) + +#define F0(x, y, z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define F1(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) + +#define P(a, b, c, d, e, f, g, h, x, K) \ + do \ + { \ + local.temp1 = (h) + S3(e) + F1((e), (f), (g)) + (K) + (x); \ + local.temp2 = S2(a) + F0((a), (b), (c)); \ + (d) += local.temp1; (h) = local.temp1 + local.temp2; \ + } while (0) + + for (i = 0; i < 8; i++) { + local.A[i] = ctx->state[i]; + } + +#if defined(MBEDTLS_SHA512_SMALLER) + for (i = 0; i < 80; i++) { + if (i < 16) { + local.W[i] = MBEDTLS_GET_UINT64_BE(data, i << 3); + } else { + local.W[i] = S1(local.W[i - 2]) + local.W[i - 7] + + S0(local.W[i - 15]) + local.W[i - 16]; + } + + P(local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], + local.A[5], local.A[6], local.A[7], local.W[i], K[i]); + + local.temp1 = local.A[7]; local.A[7] = local.A[6]; + local.A[6] = local.A[5]; local.A[5] = local.A[4]; + local.A[4] = local.A[3]; local.A[3] = local.A[2]; + local.A[2] = local.A[1]; local.A[1] = local.A[0]; + local.A[0] = local.temp1; + } +#else /* MBEDTLS_SHA512_SMALLER */ + for (i = 0; i < 16; i++) { + local.W[i] = MBEDTLS_GET_UINT64_BE(data, i << 3); + } + + for (; i < 80; i++) { + local.W[i] = S1(local.W[i - 2]) + local.W[i - 7] + + S0(local.W[i - 15]) + local.W[i - 16]; + } + + i = 0; + do { + P(local.A[0], local.A[1], local.A[2], local.A[3], local.A[4], + local.A[5], local.A[6], local.A[7], local.W[i], K[i]); i++; + P(local.A[7], local.A[0], local.A[1], local.A[2], local.A[3], + local.A[4], local.A[5], local.A[6], local.W[i], K[i]); i++; + P(local.A[6], local.A[7], local.A[0], local.A[1], local.A[2], + local.A[3], local.A[4], local.A[5], local.W[i], K[i]); i++; + P(local.A[5], local.A[6], local.A[7], local.A[0], local.A[1], + local.A[2], local.A[3], local.A[4], local.W[i], K[i]); i++; + P(local.A[4], local.A[5], local.A[6], local.A[7], local.A[0], + local.A[1], local.A[2], local.A[3], local.W[i], K[i]); i++; + P(local.A[3], local.A[4], local.A[5], local.A[6], local.A[7], + local.A[0], local.A[1], local.A[2], local.W[i], K[i]); i++; + P(local.A[2], local.A[3], local.A[4], local.A[5], local.A[6], + local.A[7], local.A[0], local.A[1], local.W[i], K[i]); i++; + P(local.A[1], local.A[2], local.A[3], local.A[4], local.A[5], + local.A[6], local.A[7], local.A[0], local.W[i], K[i]); i++; + } while (i < 80); +#endif /* MBEDTLS_SHA512_SMALLER */ + + for (i = 0; i < 8; i++) { + ctx->state[i] += local.A[i]; + } + + /* Zeroise buffers and variables to clear sensitive data from memory. */ + mbedtls_platform_zeroize(&local, sizeof(local)); + + return 0; +} + +#endif /* !MBEDTLS_SHA512_PROCESS_ALT && !MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY */ + + +#if !defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) + +static size_t mbedtls_internal_sha512_process_many_c( + mbedtls_sha512_context *ctx, const uint8_t *data, size_t len) +{ + size_t processed = 0; + + while (len >= SHA512_BLOCK_SIZE) { + if (mbedtls_internal_sha512_process_c(ctx, data) != 0) { + return 0; + } + + data += SHA512_BLOCK_SIZE; + len -= SHA512_BLOCK_SIZE; + + processed += SHA512_BLOCK_SIZE; + } + + return processed; +} + +#endif /* !MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY */ + + +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) + +static int mbedtls_a64_crypto_sha512_has_support(void) +{ + static int done = 0; + static int supported = 0; + + if (!done) { + supported = mbedtls_a64_crypto_sha512_determine_support(); + done = 1; + } + + return supported; +} + +static size_t mbedtls_internal_sha512_process_many(mbedtls_sha512_context *ctx, + const uint8_t *msg, size_t len) +{ + if (mbedtls_a64_crypto_sha512_has_support()) { + return mbedtls_internal_sha512_process_many_a64_crypto(ctx, msg, len); + } else { + return mbedtls_internal_sha512_process_many_c(ctx, msg, len); + } +} + +int mbedtls_internal_sha512_process(mbedtls_sha512_context *ctx, + const unsigned char data[SHA512_BLOCK_SIZE]) +{ + if (mbedtls_a64_crypto_sha512_has_support()) { + return mbedtls_internal_sha512_process_a64_crypto(ctx, data); + } else { + return mbedtls_internal_sha512_process_c(ctx, data); + } +} + +#endif /* MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT */ + +/* + * SHA-512 process buffer + */ +int mbedtls_sha512_update(mbedtls_sha512_context *ctx, + const unsigned char *input, + size_t ilen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t fill; + unsigned int left; + + if (ilen == 0) { + return 0; + } + + left = (unsigned int) (ctx->total[0] & 0x7F); + fill = SHA512_BLOCK_SIZE - left; + + ctx->total[0] += (uint64_t) ilen; + + if (ctx->total[0] < (uint64_t) ilen) { + ctx->total[1]++; + } + + if (left && ilen >= fill) { + memcpy((void *) (ctx->buffer + left), input, fill); + + if ((ret = mbedtls_internal_sha512_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + input += fill; + ilen -= fill; + left = 0; + } + + while (ilen >= SHA512_BLOCK_SIZE) { + size_t processed = + mbedtls_internal_sha512_process_many(ctx, input, ilen); + if (processed < SHA512_BLOCK_SIZE) { + return MBEDTLS_ERR_ERROR_GENERIC_ERROR; + } + + input += processed; + ilen -= processed; + } + + if (ilen > 0) { + memcpy((void *) (ctx->buffer + left), input, ilen); + } + + return 0; +} + +/* + * SHA-512 final digest + */ +int mbedtls_sha512_finish(mbedtls_sha512_context *ctx, + unsigned char *output) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned used; + uint64_t high, low; + + /* + * Add padding: 0x80 then 0x00 until 16 bytes remain for the length + */ + used = ctx->total[0] & 0x7F; + + ctx->buffer[used++] = 0x80; + + if (used <= 112) { + /* Enough room for padding + length in current block */ + memset(ctx->buffer + used, 0, 112 - used); + } else { + /* We'll need an extra block */ + memset(ctx->buffer + used, 0, SHA512_BLOCK_SIZE - used); + + if ((ret = mbedtls_internal_sha512_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + memset(ctx->buffer, 0, 112); + } + + /* + * Add message length + */ + high = (ctx->total[0] >> 61) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + sha512_put_uint64_be(high, ctx->buffer, 112); + sha512_put_uint64_be(low, ctx->buffer, 120); + + if ((ret = mbedtls_internal_sha512_process(ctx, ctx->buffer)) != 0) { + return ret; + } + + /* + * Output final state + */ + sha512_put_uint64_be(ctx->state[0], output, 0); + sha512_put_uint64_be(ctx->state[1], output, 8); + sha512_put_uint64_be(ctx->state[2], output, 16); + sha512_put_uint64_be(ctx->state[3], output, 24); + sha512_put_uint64_be(ctx->state[4], output, 32); + sha512_put_uint64_be(ctx->state[5], output, 40); + + int truncated = 0; +#if defined(MBEDTLS_SHA384_C) + truncated = ctx->is384; +#endif + if (!truncated) { + sha512_put_uint64_be(ctx->state[6], output, 48); + sha512_put_uint64_be(ctx->state[7], output, 56); + } + + return 0; +} + +#endif /* !MBEDTLS_SHA512_ALT */ + +/* + * output = SHA-512( input buffer ) + */ +int mbedtls_sha512(const unsigned char *input, + size_t ilen, + unsigned char *output, + int is384) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_sha512_context ctx; + +#if defined(MBEDTLS_SHA384_C) && defined(MBEDTLS_SHA512_C) + if (is384 != 0 && is384 != 1) { + return MBEDTLS_ERR_SHA512_BAD_INPUT_DATA; + } +#elif defined(MBEDTLS_SHA512_C) + if (is384 != 0) { + return MBEDTLS_ERR_SHA512_BAD_INPUT_DATA; + } +#else /* defined MBEDTLS_SHA384_C only */ + if (is384 == 0) { + return MBEDTLS_ERR_SHA512_BAD_INPUT_DATA; + } +#endif + + mbedtls_sha512_init(&ctx); + + if ((ret = mbedtls_sha512_starts(&ctx, is384)) != 0) { + goto exit; + } + + if ((ret = mbedtls_sha512_update(&ctx, input, ilen)) != 0) { + goto exit; + } + + if ((ret = mbedtls_sha512_finish(&ctx, output)) != 0) { + goto exit; + } + +exit: + mbedtls_sha512_free(&ctx); + + return ret; +} + +#if defined(MBEDTLS_SELF_TEST) + +/* + * FIPS-180-2 test vectors + */ +static const unsigned char sha_test_buf[3][113] = +{ + { "abc" }, + { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + }, + { "" } +}; + +static const size_t sha_test_buflen[3] = +{ + 3, 112, 1000 +}; + +typedef const unsigned char (sha_test_sum_t)[64]; + +/* + * SHA-384 test vectors + */ +#if defined(MBEDTLS_SHA384_C) +static sha_test_sum_t sha384_test_sum[] = +{ + { 0xCB, 0x00, 0x75, 0x3F, 0x45, 0xA3, 0x5E, 0x8B, + 0xB5, 0xA0, 0x3D, 0x69, 0x9A, 0xC6, 0x50, 0x07, + 0x27, 0x2C, 0x32, 0xAB, 0x0E, 0xDE, 0xD1, 0x63, + 0x1A, 0x8B, 0x60, 0x5A, 0x43, 0xFF, 0x5B, 0xED, + 0x80, 0x86, 0x07, 0x2B, 0xA1, 0xE7, 0xCC, 0x23, + 0x58, 0xBA, 0xEC, 0xA1, 0x34, 0xC8, 0x25, 0xA7 }, + { 0x09, 0x33, 0x0C, 0x33, 0xF7, 0x11, 0x47, 0xE8, + 0x3D, 0x19, 0x2F, 0xC7, 0x82, 0xCD, 0x1B, 0x47, + 0x53, 0x11, 0x1B, 0x17, 0x3B, 0x3B, 0x05, 0xD2, + 0x2F, 0xA0, 0x80, 0x86, 0xE3, 0xB0, 0xF7, 0x12, + 0xFC, 0xC7, 0xC7, 0x1A, 0x55, 0x7E, 0x2D, 0xB9, + 0x66, 0xC3, 0xE9, 0xFA, 0x91, 0x74, 0x60, 0x39 }, + { 0x9D, 0x0E, 0x18, 0x09, 0x71, 0x64, 0x74, 0xCB, + 0x08, 0x6E, 0x83, 0x4E, 0x31, 0x0A, 0x4A, 0x1C, + 0xED, 0x14, 0x9E, 0x9C, 0x00, 0xF2, 0x48, 0x52, + 0x79, 0x72, 0xCE, 0xC5, 0x70, 0x4C, 0x2A, 0x5B, + 0x07, 0xB8, 0xB3, 0xDC, 0x38, 0xEC, 0xC4, 0xEB, + 0xAE, 0x97, 0xDD, 0xD8, 0x7F, 0x3D, 0x89, 0x85 } +}; +#endif /* MBEDTLS_SHA384_C */ + +/* + * SHA-512 test vectors + */ +#if defined(MBEDTLS_SHA512_C) +static sha_test_sum_t sha512_test_sum[] = +{ + { 0xDD, 0xAF, 0x35, 0xA1, 0x93, 0x61, 0x7A, 0xBA, + 0xCC, 0x41, 0x73, 0x49, 0xAE, 0x20, 0x41, 0x31, + 0x12, 0xE6, 0xFA, 0x4E, 0x89, 0xA9, 0x7E, 0xA2, + 0x0A, 0x9E, 0xEE, 0xE6, 0x4B, 0x55, 0xD3, 0x9A, + 0x21, 0x92, 0x99, 0x2A, 0x27, 0x4F, 0xC1, 0xA8, + 0x36, 0xBA, 0x3C, 0x23, 0xA3, 0xFE, 0xEB, 0xBD, + 0x45, 0x4D, 0x44, 0x23, 0x64, 0x3C, 0xE8, 0x0E, + 0x2A, 0x9A, 0xC9, 0x4F, 0xA5, 0x4C, 0xA4, 0x9F }, + { 0x8E, 0x95, 0x9B, 0x75, 0xDA, 0xE3, 0x13, 0xDA, + 0x8C, 0xF4, 0xF7, 0x28, 0x14, 0xFC, 0x14, 0x3F, + 0x8F, 0x77, 0x79, 0xC6, 0xEB, 0x9F, 0x7F, 0xA1, + 0x72, 0x99, 0xAE, 0xAD, 0xB6, 0x88, 0x90, 0x18, + 0x50, 0x1D, 0x28, 0x9E, 0x49, 0x00, 0xF7, 0xE4, + 0x33, 0x1B, 0x99, 0xDE, 0xC4, 0xB5, 0x43, 0x3A, + 0xC7, 0xD3, 0x29, 0xEE, 0xB6, 0xDD, 0x26, 0x54, + 0x5E, 0x96, 0xE5, 0x5B, 0x87, 0x4B, 0xE9, 0x09 }, + { 0xE7, 0x18, 0x48, 0x3D, 0x0C, 0xE7, 0x69, 0x64, + 0x4E, 0x2E, 0x42, 0xC7, 0xBC, 0x15, 0xB4, 0x63, + 0x8E, 0x1F, 0x98, 0xB1, 0x3B, 0x20, 0x44, 0x28, + 0x56, 0x32, 0xA8, 0x03, 0xAF, 0xA9, 0x73, 0xEB, + 0xDE, 0x0F, 0xF2, 0x44, 0x87, 0x7E, 0xA6, 0x0A, + 0x4C, 0xB0, 0x43, 0x2C, 0xE5, 0x77, 0xC3, 0x1B, + 0xEB, 0x00, 0x9C, 0x5C, 0x2C, 0x49, 0xAA, 0x2E, + 0x4E, 0xAD, 0xB2, 0x17, 0xAD, 0x8C, 0xC0, 0x9B } +}; +#endif /* MBEDTLS_SHA512_C */ + +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0])) + +static int mbedtls_sha512_common_self_test(int verbose, int is384) +{ + int i, buflen, ret = 0; + unsigned char *buf; + unsigned char sha512sum[64]; + mbedtls_sha512_context ctx; + +#if defined(MBEDTLS_SHA384_C) && defined(MBEDTLS_SHA512_C) + sha_test_sum_t *sha_test_sum = (is384) ? sha384_test_sum : sha512_test_sum; +#elif defined(MBEDTLS_SHA512_C) + sha_test_sum_t *sha_test_sum = sha512_test_sum; +#else + sha_test_sum_t *sha_test_sum = sha384_test_sum; +#endif + + buf = mbedtls_calloc(1024, sizeof(unsigned char)); + if (NULL == buf) { + if (verbose != 0) { + mbedtls_printf("Buffer allocation failed\n"); + } + + return 1; + } + + mbedtls_sha512_init(&ctx); + + for (i = 0; i < 3; i++) { + if (verbose != 0) { + mbedtls_printf(" SHA-%d test #%d: ", 512 - is384 * 128, i + 1); + } + + if ((ret = mbedtls_sha512_starts(&ctx, is384)) != 0) { + goto fail; + } + + if (i == 2) { + memset(buf, 'a', buflen = 1000); + + for (int j = 0; j < 1000; j++) { + ret = mbedtls_sha512_update(&ctx, buf, buflen); + if (ret != 0) { + goto fail; + } + } + } else { + ret = mbedtls_sha512_update(&ctx, sha_test_buf[i], + sha_test_buflen[i]); + if (ret != 0) { + goto fail; + } + } + + if ((ret = mbedtls_sha512_finish(&ctx, sha512sum)) != 0) { + goto fail; + } + + if (memcmp(sha512sum, sha_test_sum[i], 64 - is384 * 16) != 0) { + ret = 1; + goto fail; + } + + if (verbose != 0) { + mbedtls_printf("passed\n"); + } + } + + if (verbose != 0) { + mbedtls_printf("\n"); + } + + goto exit; + +fail: + if (verbose != 0) { + mbedtls_printf("failed\n"); + } + +exit: + mbedtls_sha512_free(&ctx); + mbedtls_free(buf); + + return ret; +} + +#if defined(MBEDTLS_SHA512_C) +int mbedtls_sha512_self_test(int verbose) +{ + return mbedtls_sha512_common_self_test(verbose, 0); +} +#endif /* MBEDTLS_SHA512_C */ + +#if defined(MBEDTLS_SHA384_C) +int mbedtls_sha384_self_test(int verbose) +{ + return mbedtls_sha512_common_self_test(verbose, 1); +} +#endif /* MBEDTLS_SHA384_C */ + +#undef ARRAY_LENGTH + +#endif /* MBEDTLS_SELF_TEST */ + +#endif /* MBEDTLS_SHA512_C || MBEDTLS_SHA384_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_cache.c b/r5dev/thirdparty/mbedtls/ssl_cache.c new file mode 100644 index 00000000..048c21d4 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_cache.c @@ -0,0 +1,423 @@ +/* + * SSL session cache implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * These session callbacks use a simple chained list + * to store and retrieve the session information. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_CACHE_C) + +#include "mbedtls/platform.h" + +#include "mbedtls/ssl_cache.h" +#include "ssl_misc.h" + +#include + +void mbedtls_ssl_cache_init(mbedtls_ssl_cache_context *cache) +{ + memset(cache, 0, sizeof(mbedtls_ssl_cache_context)); + + cache->timeout = MBEDTLS_SSL_CACHE_DEFAULT_TIMEOUT; + cache->max_entries = MBEDTLS_SSL_CACHE_DEFAULT_MAX_ENTRIES; + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&cache->mutex); +#endif +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_cache_find_entry(mbedtls_ssl_cache_context *cache, + unsigned char const *session_id, + size_t session_id_len, + mbedtls_ssl_cache_entry **dst) +{ + int ret = 1; +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t t = mbedtls_time(NULL); +#endif + mbedtls_ssl_cache_entry *cur; + + for (cur = cache->chain; cur != NULL; cur = cur->next) { +#if defined(MBEDTLS_HAVE_TIME) + if (cache->timeout != 0 && + (int) (t - cur->timestamp) > cache->timeout) { + continue; + } +#endif + + if (session_id_len != cur->session_id_len || + memcmp(session_id, cur->session_id, + cur->session_id_len) != 0) { + continue; + } + + break; + } + + if (cur != NULL) { + *dst = cur; + ret = 0; + } + + return ret; +} + + +int mbedtls_ssl_cache_get(void *data, + unsigned char const *session_id, + size_t session_id_len, + mbedtls_ssl_session *session) +{ + int ret = 1; + mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data; + mbedtls_ssl_cache_entry *entry; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) { + return ret; + } +#endif + + ret = ssl_cache_find_entry(cache, session_id, session_id_len, &entry); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_ssl_session_load(session, + entry->session, + entry->session_len); + if (ret != 0) { + goto exit; + } + + ret = 0; + +exit: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&cache->mutex) != 0) { + ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +/* zeroize a cache entry */ +static void ssl_cache_entry_zeroize(mbedtls_ssl_cache_entry *entry) +{ + if (entry == NULL) { + return; + } + + /* zeroize and free session structure */ + if (entry->session != NULL) { + mbedtls_platform_zeroize(entry->session, entry->session_len); + mbedtls_free(entry->session); + } + + /* zeroize the whole entry structure */ + mbedtls_platform_zeroize(entry, sizeof(mbedtls_ssl_cache_entry)); +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_cache_pick_writing_slot(mbedtls_ssl_cache_context *cache, + unsigned char const *session_id, + size_t session_id_len, + mbedtls_ssl_cache_entry **dst) +{ +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t t = mbedtls_time(NULL), oldest = 0; +#endif /* MBEDTLS_HAVE_TIME */ + + mbedtls_ssl_cache_entry *old = NULL; + int count = 0; + mbedtls_ssl_cache_entry *cur, *last; + + /* Check 1: Is there already an entry with the given session ID? + * + * If yes, overwrite it. + * + * If not, `count` will hold the size of the session cache + * at the end of this loop, and `last` will point to the last + * entry, both of which will be used later. */ + + last = NULL; + for (cur = cache->chain; cur != NULL; cur = cur->next) { + count++; + if (session_id_len == cur->session_id_len && + memcmp(session_id, cur->session_id, cur->session_id_len) == 0) { + goto found; + } + last = cur; + } + + /* Check 2: Is there an outdated entry in the cache? + * + * If so, overwrite it. + * + * If not, remember the oldest entry in `old` for later. + */ + +#if defined(MBEDTLS_HAVE_TIME) + for (cur = cache->chain; cur != NULL; cur = cur->next) { + if (cache->timeout != 0 && + (int) (t - cur->timestamp) > cache->timeout) { + goto found; + } + + if (oldest == 0 || cur->timestamp < oldest) { + oldest = cur->timestamp; + old = cur; + } + } +#endif /* MBEDTLS_HAVE_TIME */ + + /* Check 3: Is there free space in the cache? */ + + if (count < cache->max_entries) { + /* Create new entry */ + cur = mbedtls_calloc(1, sizeof(mbedtls_ssl_cache_entry)); + if (cur == NULL) { + return 1; + } + + /* Append to the end of the linked list. */ + if (last == NULL) { + cache->chain = cur; + } else { + last->next = cur; + } + + goto found; + } + + /* Last resort: The cache is full and doesn't contain any outdated + * elements. In this case, we evict the oldest one, judged by timestamp + * (if present) or cache-order. */ + +#if defined(MBEDTLS_HAVE_TIME) + if (old == NULL) { + /* This should only happen on an ill-configured cache + * with max_entries == 0. */ + return 1; + } +#else /* MBEDTLS_HAVE_TIME */ + /* Reuse first entry in chain, but move to last place. */ + if (cache->chain == NULL) { + return 1; + } + + old = cache->chain; + cache->chain = old->next; + old->next = NULL; + last->next = old; +#endif /* MBEDTLS_HAVE_TIME */ + + /* Now `old` points to the oldest entry to be overwritten. */ + cur = old; + +found: + + /* If we're reusing an entry, free it first. */ + if (cur->session != NULL) { + /* `ssl_cache_entry_zeroize` would break the chain, + * so we reuse `old` to record `next` temporarily. */ + old = cur->next; + ssl_cache_entry_zeroize(cur); + cur->next = old; + } + +#if defined(MBEDTLS_HAVE_TIME) + cur->timestamp = t; +#endif + + *dst = cur; + return 0; +} + +int mbedtls_ssl_cache_set(void *data, + unsigned char const *session_id, + size_t session_id_len, + const mbedtls_ssl_session *session) +{ + int ret = 1; + mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data; + mbedtls_ssl_cache_entry *cur; + + size_t session_serialized_len; + unsigned char *session_serialized = NULL; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) { + return ret; + } +#endif + + ret = ssl_cache_pick_writing_slot(cache, + session_id, session_id_len, + &cur); + if (ret != 0) { + goto exit; + } + + /* Check how much space we need to serialize the session + * and allocate a sufficiently large buffer. */ + ret = mbedtls_ssl_session_save(session, NULL, 0, &session_serialized_len); + if (ret != MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL) { + ret = 1; + goto exit; + } + + session_serialized = mbedtls_calloc(1, session_serialized_len); + if (session_serialized == NULL) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto exit; + } + + /* Now serialize the session into the allocated buffer. */ + ret = mbedtls_ssl_session_save(session, + session_serialized, + session_serialized_len, + &session_serialized_len); + if (ret != 0) { + goto exit; + } + + if (session_id_len > sizeof(cur->session_id)) { + ret = 1; + goto exit; + } + cur->session_id_len = session_id_len; + memcpy(cur->session_id, session_id, session_id_len); + + cur->session = session_serialized; + cur->session_len = session_serialized_len; + session_serialized = NULL; + + ret = 0; + +exit: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&cache->mutex) != 0) { + ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + if (session_serialized != NULL) { + mbedtls_platform_zeroize(session_serialized, session_serialized_len); + mbedtls_free(session_serialized); + session_serialized = NULL; + } + + return ret; +} + +int mbedtls_ssl_cache_remove(void *data, + unsigned char const *session_id, + size_t session_id_len) +{ + int ret = 1; + mbedtls_ssl_cache_context *cache = (mbedtls_ssl_cache_context *) data; + mbedtls_ssl_cache_entry *entry; + mbedtls_ssl_cache_entry *prev; + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&cache->mutex)) != 0) { + return ret; + } +#endif + + ret = ssl_cache_find_entry(cache, session_id, session_id_len, &entry); + /* No valid entry found, exit with success */ + if (ret != 0) { + ret = 0; + goto exit; + } + + /* Now we remove the entry from the chain */ + if (entry == cache->chain) { + cache->chain = entry->next; + goto free; + } + for (prev = cache->chain; prev->next != NULL; prev = prev->next) { + if (prev->next == entry) { + prev->next = entry->next; + break; + } + } + +free: + ssl_cache_entry_zeroize(entry); + mbedtls_free(entry); + ret = 0; + +exit: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&cache->mutex) != 0) { + ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +#if defined(MBEDTLS_HAVE_TIME) +void mbedtls_ssl_cache_set_timeout(mbedtls_ssl_cache_context *cache, int timeout) +{ + if (timeout < 0) { + timeout = 0; + } + + cache->timeout = timeout; +} +#endif /* MBEDTLS_HAVE_TIME */ + +void mbedtls_ssl_cache_set_max_entries(mbedtls_ssl_cache_context *cache, int max) +{ + if (max < 0) { + max = 0; + } + + cache->max_entries = max; +} + +void mbedtls_ssl_cache_free(mbedtls_ssl_cache_context *cache) +{ + mbedtls_ssl_cache_entry *cur, *prv; + + cur = cache->chain; + + while (cur != NULL) { + prv = cur; + cur = cur->next; + + ssl_cache_entry_zeroize(prv); + mbedtls_free(prv); + } + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_free(&cache->mutex); +#endif + cache->chain = NULL; +} + +#endif /* MBEDTLS_SSL_CACHE_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_ciphersuites.c b/r5dev/thirdparty/mbedtls/ssl_ciphersuites.c new file mode 100644 index 00000000..501608a5 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_ciphersuites.c @@ -0,0 +1,2058 @@ +/** + * \file ssl_ciphersuites.c + * + * \brief SSL ciphersuites for mbed TLS + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_TLS_C) + +#include "mbedtls/platform.h" + +#include "mbedtls/ssl_ciphersuites.h" +#include "mbedtls/ssl.h" +#include "ssl_misc.h" + +#include "mbedtls/legacy_or_psa.h" + +#include + +/* + * Ordered from most preferred to least preferred in terms of security. + * + * Current rule (except weak and null which come last): + * 1. By key exchange: + * Forward-secure non-PSK > forward-secure PSK > ECJPAKE > other non-PSK > other PSK + * 2. By key length and cipher: + * ChaCha > AES-256 > Camellia-256 > ARIA-256 > AES-128 > Camellia-128 > ARIA-128 + * 3. By cipher mode when relevant GCM > CCM > CBC > CCM_8 + * 4. By hash function used when relevant + * 5. By key exchange/auth again: EC > non-EC + */ +static const int ciphersuite_preference[] = +{ +#if defined(MBEDTLS_SSL_CIPHERSUITES) + MBEDTLS_SSL_CIPHERSUITES, +#else +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /* TLS 1.3 ciphersuites */ + MBEDTLS_TLS1_3_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS1_3_AES_256_GCM_SHA384, + MBEDTLS_TLS1_3_AES_128_GCM_SHA256, + MBEDTLS_TLS1_3_AES_128_CCM_SHA256, + MBEDTLS_TLS1_3_AES_128_CCM_8_SHA256, +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + /* Chacha-Poly ephemeral suites */ + MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + + /* All AES-256 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8, + + /* All CAMELLIA-256 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, + + /* All ARIA-256 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, + + /* All AES-128 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8, + + /* All CAMELLIA-128 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, + + /* All ARIA-128 ephemeral suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, + + /* The PSK ephemeral suites */ + MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, + + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, + + /* The ECJPAKE suite */ + MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8, + + /* All AES-256 suites */ + MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8, + + /* All CAMELLIA-256 suites */ + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + + /* All ARIA-256 suites */ + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384, + + /* All AES-128 suites */ + MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8, + + /* All CAMELLIA-128 suites */ + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + + /* All ARIA-128 suites */ + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256, + + /* The RSA PSK suites */ + MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, + + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, + + /* The PSK suites */ + MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, + MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384, + MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384, + + MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, + MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8, + MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256, + MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256, + + /* NULL suites */ + MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA, + + MBEDTLS_TLS_RSA_WITH_NULL_SHA256, + MBEDTLS_TLS_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_RSA_WITH_NULL_MD5, + MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA, + MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA, + MBEDTLS_TLS_PSK_WITH_NULL_SHA384, + MBEDTLS_TLS_PSK_WITH_NULL_SHA256, + MBEDTLS_TLS_PSK_WITH_NULL_SHA, + +#endif /* MBEDTLS_SSL_CIPHERSUITES */ + 0 +}; + +static const mbedtls_ssl_ciphersuite_t ciphersuite_definitions[] = +{ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS1_3_AES_256_GCM_SHA384, "TLS1-3-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, + MBEDTLS_KEY_EXCHANGE_NONE, /* Key exchange not part of ciphersuite in TLS 1.3 */ + 0, + MBEDTLS_SSL_VERSION_TLS1_3, MBEDTLS_SSL_VERSION_TLS1_3 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS1_3_AES_128_GCM_SHA256, "TLS1-3-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_NONE, /* Key exchange not part of ciphersuite in TLS 1.3 */ + 0, + MBEDTLS_SSL_VERSION_TLS1_3, MBEDTLS_SSL_VERSION_TLS1_3 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#if defined(MBEDTLS_CCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS1_3_AES_128_CCM_SHA256, "TLS1-3-AES-128-CCM-SHA256", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_NONE, /* Key exchange not part of ciphersuite in TLS 1.3 */ + 0, + MBEDTLS_SSL_VERSION_TLS1_3, MBEDTLS_SSL_VERSION_TLS1_3 }, + { MBEDTLS_TLS1_3_AES_128_CCM_8_SHA256, "TLS1-3-AES-128-CCM-8-SHA256", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_NONE, /* Key exchange not part of ciphersuite in TLS 1.3 */ + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_3, MBEDTLS_SSL_VERSION_TLS1_3 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA && MBEDTLS_CCM_C */ +#endif /* MBEDTLS_AES_C */ +#if defined(MBEDTLS_CHACHAPOLY_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS1_3_CHACHA20_POLY1305_SHA256, + "TLS1-3-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_NONE, /* Key exchange not part of ciphersuite in TLS 1.3 */ + 0, + MBEDTLS_SSL_VERSION_TLS1_3, MBEDTLS_SSL_VERSION_TLS1_3 }, +#endif /* MBEDTLS_CHACHAPOLY_C && MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_CHACHAPOLY_C) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_SSL_PROTO_TLS1_2) +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) + { MBEDTLS_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) + { MBEDTLS_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + { MBEDTLS_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + { MBEDTLS_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-ECDHE-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + { MBEDTLS_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-DHE-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + { MBEDTLS_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256, + "TLS-RSA-PSK-WITH-CHACHA20-POLY1305-SHA256", + MBEDTLS_CIPHER_CHACHA20_POLY1305, MBEDTLS_MD_SHA256, + MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#endif /* MBEDTLS_CHACHAPOLY_C && + MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA && + MBEDTLS_SSL_PROTO_TLS1_2 */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, "TLS-ECDHE-ECDSA-WITH-AES-256-CCM", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, "TLS-ECDHE-ECDSA-WITH-AES-256-CCM-8", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, "TLS-ECDHE-ECDSA-WITH-AES-128-CCM", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, "TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CCM_C */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_NULL_SHA, "TLS-ECDHE-ECDSA-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_NULL_CIPHER */ +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, + "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, + "TLS-ECDHE-RSA-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, + "TLS-ECDHE-RSA-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_RSA_WITH_NULL_SHA, "TLS-ECDHE-RSA-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_NULL_CIPHER */ +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA && MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS-DHE-RSA-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS-DHE-RSA-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM, "TLS-DHE-RSA-WITH-AES-256-CCM", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_DHE_RSA_WITH_AES_256_CCM_8, "TLS-DHE-RSA-WITH-AES-256-CCM-8", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM, "TLS-DHE-RSA-WITH-AES-128-CCM", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_DHE_RSA_WITH_AES_128_CCM_8, "TLS-DHE-RSA-WITH-AES-128-CCM-8", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CCM_C */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-DHE-RSA-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-DHE-RSA-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS-RSA-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA && MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS-RSA-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS-RSA-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS-RSA-WITH-AES-256-CBC-SHA256", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_RSA_WITH_AES_128_CBC_SHA, "TLS-RSA-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_RSA_WITH_AES_256_CBC_SHA, "TLS-RSA-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_TLS_RSA_WITH_AES_256_CCM, "TLS-RSA-WITH-AES-256-CCM", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_RSA_WITH_AES_256_CCM_8, "TLS-RSA-WITH-AES-256-CCM-8", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_RSA_WITH_AES_128_CCM, "TLS-RSA-WITH-AES-128-CCM", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_RSA_WITH_AES_128_CCM_8, "TLS-RSA-WITH-AES-128-CCM-8", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CCM_C */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, "TLS-RSA-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, "TLS-RSA-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, "TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, "TLS-ECDH-RSA-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, + "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, + "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, + "TLS-ECDH-RSA-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, + "TLS-ECDH-RSA-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_RSA_WITH_NULL_SHA, "TLS-ECDH-RSA-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_NULL_CIPHER */ +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_CIPHER_MODE_CBC) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_GCM_C) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, "TLS-ECDH-ECDSA-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, + "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, + "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, + "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, + "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_NULL_SHA, "TLS-ECDH-ECDSA-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_NULL_CIPHER */ +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256, "TLS-PSK-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_AES_256_GCM_SHA384, "TLS-PSK-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256, "TLS-PSK-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA384, "TLS-PSK-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, "TLS-PSK-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, "TLS-PSK-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_TLS_PSK_WITH_AES_256_CCM, "TLS-PSK-WITH-AES-256-CCM", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_PSK_WITH_AES_256_CCM_8, "TLS-PSK-WITH-AES-256-CCM-8", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_PSK_WITH_AES_128_CCM, "TLS-PSK-WITH-AES-128-CCM", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_PSK_WITH_AES_128_CCM_8, "TLS-PSK-WITH-AES-128-CCM-8", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CCM_C */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, "TLS-PSK-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, "TLS-PSK-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, "TLS-PSK-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, "TLS-PSK-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256, "TLS-DHE-PSK-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, "TLS-DHE-PSK-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, "TLS-DHE-PSK-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, "TLS-DHE-PSK-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, "TLS-DHE-PSK-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, "TLS-DHE-PSK-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM, "TLS-DHE-PSK-WITH-AES-256-CCM", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_DHE_PSK_WITH_AES_256_CCM_8, "TLS-DHE-PSK-WITH-AES-256-CCM-8", + MBEDTLS_CIPHER_AES_256_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM, "TLS-DHE-PSK-WITH-AES-128-CCM", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + { MBEDTLS_TLS_DHE_PSK_WITH_AES_128_CCM_8, "TLS-DHE-PSK-WITH-AES-128-CCM-8", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CCM_C */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, "TLS-DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, "TLS-DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256, "TLS-DHE-PSK-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384, "TLS-DHE-PSK-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#if defined(MBEDTLS_AES_C) + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, + "TLS-ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, + "TLS-ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, "TLS-RSA-PSK-WITH-AES-128-GCM-SHA256", + MBEDTLS_CIPHER_AES_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, "TLS-RSA-PSK-WITH-AES-256-GCM-SHA384", + MBEDTLS_CIPHER_AES_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, "TLS-RSA-PSK-WITH-AES-128-CBC-SHA256", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, "TLS-RSA-PSK-WITH-AES-256-CBC-SHA384", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, "TLS-RSA-PSK-WITH-AES-128-CBC-SHA", + MBEDTLS_CIPHER_AES_128_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, + + { MBEDTLS_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, "TLS-RSA-PSK-WITH-AES-256-CBC-SHA", + MBEDTLS_CIPHER_AES_256_CBC, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_AES_C */ + +#if defined(MBEDTLS_CAMELLIA_C) +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, "TLS-RSA-PSK-WITH-CAMELLIA-128-CBC-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, "TLS-RSA-PSK-WITH-CAMELLIA-256-CBC-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_CIPHER_MODE_CBC */ + +#if defined(MBEDTLS_GCM_C) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, "TLS-RSA-PSK-WITH-CAMELLIA-128-GCM-SHA256", + MBEDTLS_CIPHER_CAMELLIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, "TLS-RSA-PSK-WITH-CAMELLIA-256-GCM-SHA384", + MBEDTLS_CIPHER_CAMELLIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_GCM_C */ +#endif /* MBEDTLS_CAMELLIA_C */ + +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +#if defined(MBEDTLS_AES_C) +#if defined(MBEDTLS_CCM_C) + { MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8, "TLS-ECJPAKE-WITH-AES-128-CCM-8", + MBEDTLS_CIPHER_AES_128_CCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECJPAKE, + MBEDTLS_CIPHERSUITE_SHORT_TAG, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_CCM_C */ +#endif /* MBEDTLS_AES_C */ +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_WITH_NULL_MD5, "TLS-RSA-WITH-NULL-MD5", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_MD5, MBEDTLS_KEY_EXCHANGE_RSA, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_WITH_NULL_SHA, "TLS-RSA-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_WITH_NULL_SHA256, "TLS-RSA-WITH-NULL-SHA256", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_NULL_SHA, "TLS-PSK-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_NULL_SHA256, "TLS-PSK-WITH-NULL-SHA256", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_PSK_WITH_NULL_SHA384, "TLS-PSK-WITH-NULL-SHA384", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA, "TLS-DHE-PSK-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA256, "TLS-DHE-PSK-WITH-NULL-SHA256", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_DHE_PSK_WITH_NULL_SHA384, "TLS-DHE-PSK-WITH-NULL-SHA384", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA, "TLS-ECDHE-PSK-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA256, "TLS-ECDHE-PSK-WITH-NULL-SHA256", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_ECDHE_PSK_WITH_NULL_SHA384, "TLS-ECDHE-PSK-WITH-NULL-SHA384", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA, "TLS-RSA-PSK-WITH-NULL-SHA", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA1, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA256, "TLS-RSA-PSK-WITH-NULL-SHA256", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { MBEDTLS_TLS_RSA_PSK_WITH_NULL_SHA384, "TLS-RSA-PSK-WITH-NULL-SHA384", + MBEDTLS_CIPHER_NULL, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + MBEDTLS_CIPHERSUITE_WEAK, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#endif /* MBEDTLS_CIPHER_NULL_CIPHER */ + +#if defined(MBEDTLS_ARIA_C) + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_WITH_ARIA_256_GCM_SHA384, + "TLS-RSA-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_WITH_ARIA_256_CBC_SHA384, + "TLS-RSA-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_WITH_ARIA_128_GCM_SHA256, + "TLS-RSA-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_WITH_ARIA_128_CBC_SHA256, + "TLS-RSA-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, + "TLS-RSA-PSK-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, + "TLS-RSA-PSK-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, + "TLS-RSA-PSK-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, + "TLS-RSA-PSK-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_RSA_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_PSK_WITH_ARIA_256_GCM_SHA384, + "TLS-PSK-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_PSK_WITH_ARIA_256_CBC_SHA384, + "TLS-PSK-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_PSK_WITH_ARIA_128_GCM_SHA256, + "TLS-PSK-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_PSK_WITH_ARIA_128_CBC_SHA256, + "TLS-PSK-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, + "TLS-ECDH-RSA-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, + "TLS-ECDH-RSA-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, + "TLS-ECDH-RSA-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, + "TLS-ECDH-RSA-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384, + "TLS-ECDHE-RSA-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, + "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256, + "TLS-ECDHE-RSA-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, + "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, + "TLS-ECDHE-PSK-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, + "TLS-ECDHE-PSK-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, + "TLS-ECDHE-ECDSA-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, + "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, + "TLS-ECDHE-ECDSA-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, + "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, + "TLS-ECDH-ECDSA-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, + "TLS-ECDH-ECDSA-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, + "TLS-ECDH-ECDSA-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, + "TLS-ECDH-ECDSA-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384, + "TLS-DHE-RSA-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, + "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256, + "TLS-DHE-RSA-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, + "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_RSA, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384, + "TLS-DHE-PSK-WITH-ARIA-256-GCM-SHA384", + MBEDTLS_CIPHER_ARIA_256_GCM, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, + "TLS-DHE-PSK-WITH-ARIA-256-CBC-SHA384", + MBEDTLS_CIPHER_ARIA_256_CBC, MBEDTLS_MD_SHA384, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_GCM_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256, + "TLS-DHE-PSK-WITH-ARIA-128-GCM-SHA256", + MBEDTLS_CIPHER_ARIA_128_GCM, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif +#if (defined(MBEDTLS_CIPHER_MODE_CBC) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA)) + { MBEDTLS_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, + "TLS-DHE-PSK-WITH-ARIA-128-CBC-SHA256", + MBEDTLS_CIPHER_ARIA_128_CBC, MBEDTLS_MD_SHA256, MBEDTLS_KEY_EXCHANGE_DHE_PSK, + 0, + MBEDTLS_SSL_VERSION_TLS1_2, MBEDTLS_SSL_VERSION_TLS1_2 }, +#endif + +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#endif /* MBEDTLS_ARIA_C */ + + + { 0, "", + MBEDTLS_CIPHER_NONE, MBEDTLS_MD_NONE, MBEDTLS_KEY_EXCHANGE_NONE, + 0, 0, 0 } +}; + +#if defined(MBEDTLS_SSL_CIPHERSUITES) +const int *mbedtls_ssl_list_ciphersuites(void) +{ + return ciphersuite_preference; +} +#else +#define MAX_CIPHERSUITES sizeof(ciphersuite_definitions) / \ + sizeof(ciphersuite_definitions[0]) +static int supported_ciphersuites[MAX_CIPHERSUITES]; +static int supported_init = 0; + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ciphersuite_is_removed(const mbedtls_ssl_ciphersuite_t *cs_info) +{ + (void) cs_info; + + return 0; +} + +const int *mbedtls_ssl_list_ciphersuites(void) +{ + /* + * On initial call filter out all ciphersuites not supported by current + * build based on presence in the ciphersuite_definitions. + */ + if (supported_init == 0) { + const int *p; + int *q; + + for (p = ciphersuite_preference, q = supported_ciphersuites; + *p != 0 && q < supported_ciphersuites + MAX_CIPHERSUITES - 1; + p++) { + const mbedtls_ssl_ciphersuite_t *cs_info; + if ((cs_info = mbedtls_ssl_ciphersuite_from_id(*p)) != NULL && + !ciphersuite_is_removed(cs_info)) { + *(q++) = *p; + } + } + *q = 0; + + supported_init = 1; + } + + return supported_ciphersuites; +} +#endif /* MBEDTLS_SSL_CIPHERSUITES */ + +const mbedtls_ssl_ciphersuite_t *mbedtls_ssl_ciphersuite_from_string( + const char *ciphersuite_name) +{ + const mbedtls_ssl_ciphersuite_t *cur = ciphersuite_definitions; + + if (NULL == ciphersuite_name) { + return NULL; + } + + while (cur->id != 0) { + if (0 == strcmp(cur->name, ciphersuite_name)) { + return cur; + } + + cur++; + } + + return NULL; +} + +const mbedtls_ssl_ciphersuite_t *mbedtls_ssl_ciphersuite_from_id(int ciphersuite) +{ + const mbedtls_ssl_ciphersuite_t *cur = ciphersuite_definitions; + + while (cur->id != 0) { + if (cur->id == ciphersuite) { + return cur; + } + + cur++; + } + + return NULL; +} + +const char *mbedtls_ssl_get_ciphersuite_name(const int ciphersuite_id) +{ + const mbedtls_ssl_ciphersuite_t *cur; + + cur = mbedtls_ssl_ciphersuite_from_id(ciphersuite_id); + + if (cur == NULL) { + return "unknown"; + } + + return cur->name; +} + +int mbedtls_ssl_get_ciphersuite_id(const char *ciphersuite_name) +{ + const mbedtls_ssl_ciphersuite_t *cur; + + cur = mbedtls_ssl_ciphersuite_from_string(ciphersuite_name); + + if (cur == NULL) { + return 0; + } + + return cur->id; +} + +size_t mbedtls_ssl_ciphersuite_get_cipher_key_bitlen(const mbedtls_ssl_ciphersuite_t *info) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_type_t key_type; + psa_algorithm_t alg; + size_t key_bits; + + status = mbedtls_ssl_cipher_to_psa(info->cipher, + info->flags & MBEDTLS_CIPHERSUITE_SHORT_TAG ? 8 : 16, + &alg, &key_type, &key_bits); + + if (status != PSA_SUCCESS) { + return 0; + } + + return key_bits; +#else + const mbedtls_cipher_info_t * const cipher_info = + mbedtls_cipher_info_from_type(info->cipher); + + return mbedtls_cipher_info_get_key_bitlen(cipher_info); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} + +#if defined(MBEDTLS_PK_C) +mbedtls_pk_type_t mbedtls_ssl_get_ciphersuite_sig_pk_alg(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->key_exchange) { + case MBEDTLS_KEY_EXCHANGE_RSA: + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + return MBEDTLS_PK_RSA; + + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + return MBEDTLS_PK_ECDSA; + + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + return MBEDTLS_PK_ECKEY; + + default: + return MBEDTLS_PK_NONE; + } +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +psa_algorithm_t mbedtls_ssl_get_ciphersuite_sig_pk_psa_alg(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->key_exchange) { + case MBEDTLS_KEY_EXCHANGE_RSA: + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + return PSA_ALG_RSA_PKCS1V15_CRYPT; + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + return PSA_ALG_RSA_PKCS1V15_SIGN( + mbedtls_hash_info_psa_from_md(info->mac)); + + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + return PSA_ALG_ECDSA(mbedtls_hash_info_psa_from_md(info->mac)); + + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + return PSA_ALG_ECDH; + + default: + return PSA_ALG_NONE; + } +} + +psa_key_usage_t mbedtls_ssl_get_ciphersuite_sig_pk_psa_usage(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->key_exchange) { + case MBEDTLS_KEY_EXCHANGE_RSA: + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + return PSA_KEY_USAGE_DECRYPT; + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + return PSA_KEY_USAGE_SIGN_HASH; + + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + return PSA_KEY_USAGE_DERIVE; + + default: + return 0; + } +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +mbedtls_pk_type_t mbedtls_ssl_get_ciphersuite_sig_alg(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->key_exchange) { + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + return MBEDTLS_PK_RSA; + + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + return MBEDTLS_PK_ECDSA; + + default: + return MBEDTLS_PK_NONE; + } +} + +#endif /* MBEDTLS_PK_C */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +int mbedtls_ssl_ciphersuite_uses_ec(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->key_exchange) { + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK: + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + case MBEDTLS_KEY_EXCHANGE_ECJPAKE: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED*/ + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) +int mbedtls_ssl_ciphersuite_uses_psk(const mbedtls_ssl_ciphersuite_t *info) +{ + switch (info->key_exchange) { + case MBEDTLS_KEY_EXCHANGE_PSK: + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + case MBEDTLS_KEY_EXCHANGE_DHE_PSK: + case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK: + return 1; + + default: + return 0; + } +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ + +#endif /* MBEDTLS_SSL_TLS_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_client.c b/r5dev/thirdparty/mbedtls/ssl_client.c new file mode 100644 index 00000000..ea64b216 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_client.c @@ -0,0 +1,999 @@ +/* + * TLS 1.2 and 1.3 client-side functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS ( https://tls.mbed.org ) + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_CLI_C) +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) || defined(MBEDTLS_SSL_PROTO_TLS1_2) + +#include + +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/platform.h" + +#include "ssl_client.h" +#include "ssl_misc.h" +#include "ssl_tls13_keys.h" +#include "ssl_debug_helpers.h" + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_hostname_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + size_t hostname_len; + + *olen = 0; + + if (ssl->hostname == NULL) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding server name extension: %s", + ssl->hostname)); + + hostname_len = strlen(ssl->hostname); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, hostname_len + 9); + + /* + * Sect. 3, RFC 6066 (TLS Extensions Definitions) + * + * In order to provide any of the server names, clients MAY include an + * extension of type "server_name" in the (extended) client hello. The + * "extension_data" field of this extension SHALL contain + * "ServerNameList" where: + * + * struct { + * NameType name_type; + * select (name_type) { + * case host_name: HostName; + * } name; + * } ServerName; + * + * enum { + * host_name(0), (255) + * } NameType; + * + * opaque HostName<1..2^16-1>; + * + * struct { + * ServerName server_name_list<1..2^16-1> + * } ServerNameList; + * + */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SERVERNAME, p, 0); + p += 2; + + MBEDTLS_PUT_UINT16_BE(hostname_len + 5, p, 0); + p += 2; + + MBEDTLS_PUT_UINT16_BE(hostname_len + 3, p, 0); + p += 2; + + *p++ = MBEDTLS_BYTE_0(MBEDTLS_TLS_EXT_SERVERNAME_HOSTNAME); + + MBEDTLS_PUT_UINT16_BE(hostname_len, p, 0); + p += 2; + + memcpy(p, ssl->hostname, hostname_len); + + *olen = hostname_len + 9; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_SERVERNAME); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + return 0; +} +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_SSL_ALPN) +/* + * ssl_write_alpn_ext() + * + * Structure of the application_layer_protocol_negotiation extension in + * ClientHello: + * + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + * + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_alpn_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + + *out_len = 0; + + if (ssl->conf->alpn_list == NULL) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding alpn extension")); + + + /* Check we have enough space for the extension type (2 bytes), the + * extension length (2 bytes) and the protocol_name_list length (2 bytes). + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6); + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_ALPN, p, 0); + /* Skip writing extension and list length for now */ + p += 6; + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + */ + for (const char **cur = ssl->conf->alpn_list; *cur != NULL; cur++) { + /* + * mbedtls_ssl_conf_set_alpn_protocols() checked that the length of + * protocol names is less than 255. + */ + size_t protocol_name_len = strlen(*cur); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 1 + protocol_name_len); + *p++ = (unsigned char) protocol_name_len; + memcpy(p, *cur, protocol_name_len); + p += protocol_name_len; + } + + *out_len = p - buf; + + /* List length = *out_len - 2 (ext_type) - 2 (ext_len) - 2 (list_len) */ + MBEDTLS_PUT_UINT16_BE(*out_len - 6, buf, 4); + + /* Extension length = *out_len - 2 (ext_type) - 2 (ext_len) */ + MBEDTLS_PUT_UINT16_BE(*out_len - 4, buf, 2); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_ALPN); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + return 0; +} +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +/* + * Function for writing a supported groups (TLS 1.3) or supported elliptic + * curves (TLS 1.2) extension. + * + * The "extension_data" field of a supported groups extension contains a + * "NamedGroupList" value (TLS 1.3 RFC8446): + * enum { + * secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019), + * x25519(0x001D), x448(0x001E), + * ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102), + * ffdhe6144(0x0103), ffdhe8192(0x0104), + * ffdhe_private_use(0x01FC..0x01FF), + * ecdhe_private_use(0xFE00..0xFEFF), + * (0xFFFF) + * } NamedGroup; + * struct { + * NamedGroup named_group_list<2..2^16-1>; + * } NamedGroupList; + * + * The "extension_data" field of a supported elliptic curves extension contains + * a "NamedCurveList" value (TLS 1.2 RFC 8422): + * enum { + * deprecated(1..22), + * secp256r1 (23), secp384r1 (24), secp521r1 (25), + * x25519(29), x448(30), + * reserved (0xFE00..0xFEFF), + * deprecated(0xFF01..0xFF02), + * (0xFFFF) + * } NamedCurve; + * struct { + * NamedCurve named_curve_list<2..2^16-1> + * } NamedCurveList; + * + * The TLS 1.3 supported groups extension was defined to be a compatible + * generalization of the TLS 1.2 supported elliptic curves extension. They both + * share the same extension identifier. + * + * DHE groups are not supported yet. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_supported_groups_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + unsigned char *named_group_list; /* Start of named_group_list */ + size_t named_group_list_len; /* Length of named_group_list */ + const uint16_t *group_list = mbedtls_ssl_get_groups(ssl); + + *out_len = 0; + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding supported_groups extension")); + + /* Check if we have space for header and length fields: + * - extension_type (2 bytes) + * - extension_data_length (2 bytes) + * - named_group_list_length (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6); + p += 6; + + named_group_list = p; + + if (group_list == NULL) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + + for (; *group_list != 0; group_list++) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got supported group(%04x)", *group_list)); + +#if defined(MBEDTLS_ECP_C) + if ((mbedtls_ssl_conf_is_tls13_enabled(ssl->conf) && + mbedtls_ssl_tls13_named_group_is_ecdhe(*group_list)) || + (mbedtls_ssl_conf_is_tls12_enabled(ssl->conf) && + mbedtls_ssl_tls12_named_group_is_ecdhe(*group_list))) { + if (mbedtls_ssl_get_ecp_group_id_from_tls_id(*group_list) == + MBEDTLS_ECP_DP_NONE) { + continue; + } + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + MBEDTLS_PUT_UINT16_BE(*group_list, p, 0); + p += 2; + MBEDTLS_SSL_DEBUG_MSG(3, ("NamedGroup: %s ( %x )", + mbedtls_ssl_get_curve_name_from_tls_id(*group_list), + *group_list)); + } +#endif /* MBEDTLS_ECP_C */ + /* Add DHE groups here */ + + } + + /* Length of named_group_list */ + named_group_list_len = p - named_group_list; + if (named_group_list_len == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("No group available.")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Write extension_type */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SUPPORTED_GROUPS, buf, 0); + /* Write extension_data_length */ + MBEDTLS_PUT_UINT16_BE(named_group_list_len + 2, buf, 2); + /* Write length of named_group_list */ + MBEDTLS_PUT_UINT16_BE(named_group_list_len, buf, 4); + + MBEDTLS_SSL_DEBUG_BUF(3, "Supported groups extension", + buf + 4, named_group_list_len + 2); + + *out_len = p - buf; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_tls13_set_hs_sent_ext_mask( + ssl, MBEDTLS_TLS_EXT_SUPPORTED_GROUPS); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + return 0; +} + +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || + MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_client_hello_cipher_suites( + mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + int *tls12_uses_ec, + size_t *out_len) +{ + unsigned char *p = buf; + const int *ciphersuite_list; + unsigned char *cipher_suites; /* Start of the cipher_suites list */ + size_t cipher_suites_len; + + *tls12_uses_ec = 0; + *out_len = 0; + + /* + * Ciphersuite list + * + * This is a list of the symmetric cipher options supported by + * the client, specifically the record protection algorithm + * ( including secret key length ) and a hash to be used with + * HKDF, in descending order of client preference. + */ + ciphersuite_list = ssl->conf->ciphersuite_list; + + /* Check there is space for the cipher suite list length (2 bytes). */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + p += 2; + + /* Write cipher_suites + * CipherSuite cipher_suites<2..2^16-2>; + */ + cipher_suites = p; + for (size_t i = 0; ciphersuite_list[i] != 0; i++) { + int cipher_suite = ciphersuite_list[i]; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(cipher_suite); + + if (mbedtls_ssl_validate_ciphersuite(ssl, ciphersuite_info, + ssl->handshake->min_tls_version, + ssl->tls_version) != 0) { + continue; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \ + (defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED)) + *tls12_uses_ec |= mbedtls_ssl_ciphersuite_uses_ec(ciphersuite_info); +#endif + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, add ciphersuite: %04x, %s", + (unsigned int) cipher_suite, + ciphersuite_info->name)); + + /* Check there is space for the cipher suite identifier (2 bytes). */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + MBEDTLS_PUT_UINT16_BE(cipher_suite, p, 0); + p += 2; + } + + /* + * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV + */ + int renegotiating = 0; +#if defined(MBEDTLS_SSL_RENEGOTIATION) + renegotiating = (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE); +#endif + if (!renegotiating) { + MBEDTLS_SSL_DEBUG_MSG(3, ("adding EMPTY_RENEGOTIATION_INFO_SCSV")); + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + MBEDTLS_PUT_UINT16_BE(MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO, p, 0); + p += 2; + } + + /* Write the cipher_suites length in number of bytes */ + cipher_suites_len = p - cipher_suites; + MBEDTLS_PUT_UINT16_BE(cipher_suites_len, buf, 0); + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, got %" MBEDTLS_PRINTF_SIZET " cipher suites", + cipher_suites_len/2)); + + /* Output the total length of cipher_suites field. */ + *out_len = p - buf; + + return 0; +} + +/* + * Structure of the TLS 1.3 ClientHello message: + * + * struct { + * ProtocolVersion legacy_version = 0x0303; // TLS v1.2 + * Random random; + * opaque legacy_session_id<0..32>; + * CipherSuite cipher_suites<2..2^16-2>; + * opaque legacy_compression_methods<1..2^8-1>; + * Extension extensions<8..2^16-1>; + * } ClientHello; + * + * Structure of the (D)TLS 1.2 ClientHello message: + * + * struct { + * ProtocolVersion client_version; + * Random random; + * SessionID session_id; + * opaque cookie<0..2^8-1>; // DTLS 1.2 ONLY + * CipherSuite cipher_suites<2..2^16-2>; + * CompressionMethod compression_methods<1..2^8-1>; + * select (extensions_present) { + * case false: + * struct {}; + * case true: + * Extension extensions<0..2^16-1>; + * }; + * } ClientHello; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_client_hello_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len, + size_t *binders_len) +{ + int ret; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + unsigned char *p = buf; + unsigned char *p_extensions_len; /* Pointer to extensions length */ + size_t output_len; /* Length of buffer used by function */ + size_t extensions_len; /* Length of the list of extensions*/ + int tls12_uses_ec = 0; + + *out_len = 0; + *binders_len = 0; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + unsigned char propose_tls12 = + (handshake->min_tls_version <= MBEDTLS_SSL_VERSION_TLS1_2) + && + (MBEDTLS_SSL_VERSION_TLS1_2 <= ssl->tls_version); +#endif +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + unsigned char propose_tls13 = + (handshake->min_tls_version <= MBEDTLS_SSL_VERSION_TLS1_3) + && + (MBEDTLS_SSL_VERSION_TLS1_3 <= ssl->tls_version); +#endif + + /* + * Write client_version (TLS 1.2) or legacy_version (TLS 1.3) + * + * In all cases this is the TLS 1.2 version. + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + mbedtls_ssl_write_version(p, ssl->conf->transport, + MBEDTLS_SSL_VERSION_TLS1_2); + p += 2; + + /* ... + * Random random; + * ... + * + * The random bytes have been prepared by ssl_prepare_client_hello() into + * the handshake->randbytes buffer and are copied here into the output + * buffer. + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, MBEDTLS_CLIENT_HELLO_RANDOM_LEN); + memcpy(p, handshake->randbytes, MBEDTLS_CLIENT_HELLO_RANDOM_LEN); + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, random bytes", + p, MBEDTLS_CLIENT_HELLO_RANDOM_LEN); + p += MBEDTLS_CLIENT_HELLO_RANDOM_LEN; + + /* TLS 1.2: + * ... + * SessionID session_id; + * ... + * with + * opaque SessionID<0..32>; + * + * TLS 1.3: + * ... + * opaque legacy_session_id<0..32>; + * ... + * + * The (legacy) session identifier bytes have been prepared by + * ssl_prepare_client_hello() into the ssl->session_negotiate->id buffer + * and are copied here into the output buffer. + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, ssl->session_negotiate->id_len + 1); + *p++ = (unsigned char) ssl->session_negotiate->id_len; + memcpy(p, ssl->session_negotiate->id, ssl->session_negotiate->id_len); + p += ssl->session_negotiate->id_len; + + MBEDTLS_SSL_DEBUG_BUF(3, "session id", ssl->session_negotiate->id, + ssl->session_negotiate->id_len); + + /* DTLS 1.2 ONLY + * ... + * opaque cookie<0..2^8-1>; + * ... + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { +#if !defined(MBEDTLS_SSL_PROTO_TLS1_3) + uint8_t cookie_len = 0; +#else + uint16_t cookie_len = 0; +#endif /* !MBEDTLS_SSL_PROTO_TLS1_3 */ + + if (handshake->cookie != NULL) { + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, cookie", + handshake->cookie, + handshake->cookie_len); + cookie_len = handshake->cookie_len; + } + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, cookie_len + 1); + *p++ = (unsigned char) cookie_len; + if (cookie_len > 0) { + memcpy(p, handshake->cookie, cookie_len); + p += cookie_len; + } + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 && MBEDTLS_SSL_PROTO_DTLS */ + + /* Write cipher_suites */ + ret = ssl_write_client_hello_cipher_suites(ssl, p, end, + &tls12_uses_ec, + &output_len); + if (ret != 0) { + return ret; + } + p += output_len; + + /* Write legacy_compression_methods (TLS 1.3) or + * compression_methods (TLS 1.2) + * + * For every TLS 1.3 ClientHello, this vector MUST contain exactly + * one byte set to zero, which corresponds to the 'null' compression + * method in prior versions of TLS. + * + * For TLS 1.2 ClientHello, for security reasons we do not support + * compression anymore, thus also just the 'null' compression method. + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + *p++ = 1; + *p++ = MBEDTLS_SSL_COMPRESS_NULL; + + /* Write extensions */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /* Keeping track of the included extensions */ + handshake->sent_extensions = MBEDTLS_SSL_EXT_MASK_NONE; +#endif + + /* First write extensions, then the total length */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + p_extensions_len = p; + p += 2; + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + /* Write server name extension */ + ret = ssl_write_hostname_ext(ssl, p, end, &output_len); + if (ret != 0) { + return ret; + } + p += output_len; +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_SSL_ALPN) + ret = ssl_write_alpn_ext(ssl, p, end, &output_len); + if (ret != 0) { + return ret; + } + p += output_len; +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (propose_tls13) { + ret = mbedtls_ssl_tls13_write_client_hello_exts(ssl, p, end, + &output_len); + if (ret != 0) { + return ret; + } + p += output_len; + } +#endif + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if ( +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + (propose_tls13 && + mbedtls_ssl_conf_tls13_some_ephemeral_enabled(ssl)) || +#endif +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + (propose_tls12 && tls12_uses_ec) || +#endif + 0) { + ret = ssl_write_supported_groups_ext(ssl, p, end, &output_len); + if (ret != 0) { + return ret; + } + p += output_len; + } +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + if ( +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + (propose_tls13 && mbedtls_ssl_conf_tls13_ephemeral_enabled(ssl)) || +#endif +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + propose_tls12 || +#endif + 0) { + ret = mbedtls_ssl_write_sig_alg_ext(ssl, p, end, &output_len); + if (ret != 0) { + return ret; + } + p += output_len; + } +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (propose_tls12) { + ret = mbedtls_ssl_tls12_write_client_hello_exts(ssl, p, end, + tls12_uses_ec, + &output_len); + if (ret != 0) { + return ret; + } + p += output_len; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + /* The "pre_shared_key" extension (RFC 8446 Section 4.2.11) + * MUST be the last extension in the ClientHello. + */ + if (propose_tls13 && mbedtls_ssl_conf_tls13_some_psk_enabled(ssl)) { + ret = mbedtls_ssl_tls13_write_identities_of_pre_shared_key_ext( + ssl, p, end, &output_len, binders_len); + if (ret != 0) { + return ret; + } + p += output_len; + } +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + + /* Write the length of the list of extensions. */ + extensions_len = p - p_extensions_len - 2; + + if (extensions_len == 0) { + p = p_extensions_len; + } else { + MBEDTLS_PUT_UINT16_BE(extensions_len, p_extensions_len, 0); + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, total extension length: %" \ + MBEDTLS_PRINTF_SIZET, extensions_len)); + MBEDTLS_SSL_DEBUG_BUF(3, "client hello extensions", + p_extensions_len, extensions_len); + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + MBEDTLS_SSL_PRINT_EXTS( + 3, MBEDTLS_SSL_HS_CLIENT_HELLO, handshake->sent_extensions); +#endif + + *out_len = p - buf; + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_generate_random(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *randbytes = ssl->handshake->randbytes; + size_t gmt_unix_time_len = 0; + + /* + * Generate the random bytes + * + * TLS 1.2 case: + * struct { + * uint32 gmt_unix_time; + * opaque random_bytes[28]; + * } Random; + * + * TLS 1.3 case: + * opaque Random[32]; + */ + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2) { +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t gmt_unix_time = mbedtls_time(NULL); + MBEDTLS_PUT_UINT32_BE(gmt_unix_time, randbytes, 0); + gmt_unix_time_len = 4; + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, current time: %" MBEDTLS_PRINTF_LONGLONG, + (long long) gmt_unix_time)); +#endif /* MBEDTLS_HAVE_TIME */ + } + + ret = ssl->conf->f_rng(ssl->conf->p_rng, + randbytes + gmt_unix_time_len, + MBEDTLS_CLIENT_HELLO_RANDOM_LEN - gmt_unix_time_len); + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_prepare_client_hello(mbedtls_ssl_context *ssl) +{ + int ret; + size_t session_id_len; + mbedtls_ssl_session *session_negotiate = ssl->session_negotiate; + + if (session_negotiate == NULL) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_HAVE_TIME) + + /* Check if a tls13 ticket has been configured. */ + if (ssl->handshake->resume != 0 && + session_negotiate->tls_version == MBEDTLS_SSL_VERSION_TLS1_3 && + session_negotiate->ticket != NULL) { + mbedtls_time_t now = mbedtls_time(NULL); + uint64_t age = (uint64_t) (now - session_negotiate->ticket_received); + if (session_negotiate->ticket_received > now || + age > session_negotiate->ticket_lifetime) { + /* Without valid ticket, disable session resumption.*/ + MBEDTLS_SSL_DEBUG_MSG( + 3, ("Ticket expired, disable session resumption")); + ssl->handshake->resume = 0; + } + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && + MBEDTLS_SSL_SESSION_TICKETS && + MBEDTLS_HAVE_TIME */ + + if (ssl->conf->f_rng == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no RNG provided")); + return MBEDTLS_ERR_SSL_NO_RNG; + } + + /* Bet on the highest configured version if we are not in a TLS 1.2 + * renegotiation or session resumption. + */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + ssl->handshake->min_tls_version = ssl->tls_version; + } else +#endif + { + if (ssl->handshake->resume) { + ssl->tls_version = session_negotiate->tls_version; + ssl->handshake->min_tls_version = ssl->tls_version; + } else { + ssl->tls_version = ssl->conf->max_tls_version; + ssl->handshake->min_tls_version = ssl->conf->min_tls_version; + } + } + + /* + * Generate the random bytes, except when responding to a verify request + * where we MUST reuse the previously generated random bytes + * (RFC 6347 4.2.1). + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if ((ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) || + (ssl->handshake->cookie == NULL)) +#endif + { + ret = ssl_generate_random(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "Random bytes generation failed", ret); + return ret; + } + } + + /* + * Prepare session identifier. At that point, the length of the session + * identifier in the SSL context `ssl->session_negotiate->id_len` is equal + * to zero, except in the case of a TLS 1.2 session renegotiation or + * session resumption. + */ + session_id_len = session_negotiate->id_len; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2) { + if (session_id_len < 16 || session_id_len > 32 || +#if defined(MBEDTLS_SSL_RENEGOTIATION) + ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE || +#endif + ssl->handshake->resume == 0) { + session_id_len = 0; + } + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + /* + * RFC 5077 section 3.4: "When presenting a ticket, the client MAY + * generate and include a Session ID in the TLS ClientHello." + */ + int renegotiating = 0; +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + renegotiating = 1; + } +#endif + if (!renegotiating) { + if ((session_negotiate->ticket != NULL) && + (session_negotiate->ticket_len != 0)) { + session_id_len = 32; + } + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + /* + * Create a legacy session identifier for the purpose of middlebox + * compatibility only if one has not been created already, which is + * the case if we are here for the TLS 1.3 second ClientHello. + * + * Versions of TLS before TLS 1.3 supported a "session resumption" + * feature which has been merged with pre-shared keys in TLS 1.3 + * version. A client which has a cached session ID set by a pre-TLS 1.3 + * server SHOULD set this field to that value. In compatibility mode, + * this field MUST be non-empty, so a client not offering a pre-TLS 1.3 + * session MUST generate a new 32-byte value. This value need not be + * random but SHOULD be unpredictable to avoid implementations fixating + * on a specific value (also known as ossification). Otherwise, it MUST + * be set as a zero-length vector ( i.e., a zero-valued single byte + * length field ). + */ + session_id_len = 32; + } +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + + if (session_id_len != session_negotiate->id_len) { + session_negotiate->id_len = session_id_len; + if (session_id_len > 0) { + ret = ssl->conf->f_rng(ssl->conf->p_rng, + session_negotiate->id, + session_id_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "creating session id failed", ret); + return ret; + } + } + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3 && + ssl->handshake->resume) { + int hostname_mismatch = ssl->hostname != NULL || + session_negotiate->hostname != NULL; + if (ssl->hostname != NULL && session_negotiate->hostname != NULL) { + hostname_mismatch = strcmp( + ssl->hostname, session_negotiate->hostname) != 0; + } + + if (hostname_mismatch) { + MBEDTLS_SSL_DEBUG_MSG( + 1, ("Hostname mismatch the session ticket, " + "disable session resumption.")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + } else { + return mbedtls_ssl_session_set_hostname(session_negotiate, + ssl->hostname); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && + MBEDTLS_SSL_SESSION_TICKETS && + MBEDTLS_SSL_SERVER_NAME_INDICATION */ + + return 0; +} +/* + * Write ClientHello handshake message. + * Handler for MBEDTLS_SSL_CLIENT_HELLO + */ +int mbedtls_ssl_write_client_hello(mbedtls_ssl_context *ssl) +{ + int ret = 0; + unsigned char *buf; + size_t buf_len, msg_len, binders_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write client hello")); + + MBEDTLS_SSL_PROC_CHK(ssl_prepare_client_hello(ssl)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg( + ssl, MBEDTLS_SSL_HS_CLIENT_HELLO, + &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_write_client_hello_body(ssl, buf, + buf + buf_len, + &msg_len, + &binders_len)); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ssl->out_msglen = msg_len + 4; + mbedtls_ssl_send_flight_completed(ssl); + + /* + * The two functions below may try to send data on the network and + * can return with the MBEDTLS_ERR_SSL_WANT_READ error code when they + * fail to do so and the transmission has to be retried later. In that + * case as in fatal error cases, we return immediately. But we must have + * set the handshake state to the next state at that point to ensure + * that we will not write and send again a ClientHello when we + * eventually succeed in sending the pending data. + */ + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_HELLO); + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + if ((ret = mbedtls_ssl_flight_transmit(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flight_transmit", ret); + return ret; + } + } else +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 && MBEDTLS_SSL_PROTO_DTLS */ + { + + ret = mbedtls_ssl_add_hs_hdr_to_checksum(ssl, + MBEDTLS_SSL_HS_CLIENT_HELLO, + msg_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_add_hs_hdr_to_checksum", ret); + return ret; + } + ret = ssl->handshake->update_checksum(ssl, buf, msg_len - binders_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "update_checksum", ret); + return ret; + } +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + if (binders_len > 0) { + MBEDTLS_SSL_PROC_CHK( + mbedtls_ssl_tls13_write_binders_of_pre_shared_key_ext( + ssl, buf + msg_len - binders_len, buf + msg_len)); + ret = ssl->handshake->update_checksum(ssl, buf + msg_len - binders_len, + binders_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "update_checksum", ret); + return ret; + } + } +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg(ssl, + buf_len, + msg_len)); + + /* + * Set next state. Note that if TLS 1.3 is proposed, this may be + * overwritten by mbedtls_ssl_tls13_finalize_client_hello(). + */ + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_HELLO); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ssl->handshake->min_tls_version <= MBEDTLS_SSL_VERSION_TLS1_3 && + MBEDTLS_SSL_VERSION_TLS1_3 <= ssl->tls_version) { + ret = mbedtls_ssl_tls13_finalize_client_hello(ssl); + } +#endif + } + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write client hello")); + return ret; +} + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 || MBEDTLS_SSL_PROTO_TLS1_2 */ +#endif /* MBEDTLS_SSL_CLI_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_client.h b/r5dev/thirdparty/mbedtls/ssl_client.h new file mode 100644 index 00000000..f57bea33 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_client.h @@ -0,0 +1,34 @@ +/** + * TLS 1.2 and 1.3 client-side functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_SSL_CLIENT_H +#define MBEDTLS_SSL_CLIENT_H + +#include "common.h" + +#if defined(MBEDTLS_SSL_TLS_C) +#include "ssl_misc.h" +#endif + +#include + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_client_hello(mbedtls_ssl_context *ssl); + +#endif /* MBEDTLS_SSL_CLIENT_H */ diff --git a/r5dev/thirdparty/mbedtls/ssl_cookie.c b/r5dev/thirdparty/mbedtls/ssl_cookie.c new file mode 100644 index 00000000..ef4d1886 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_cookie.c @@ -0,0 +1,390 @@ +/* + * DTLS cookie callbacks implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * These session callbacks use a simple chained list + * to store and retrieve the session information. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_COOKIE_C) + +#include "mbedtls/platform.h" + +#include "mbedtls/ssl_cookie.h" +#include "ssl_misc.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/constant_time.h" + +#include "mbedtls/legacy_or_psa.h" + +#include + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) +#endif + +/* + * If DTLS is in use, then at least one of SHA-256 or SHA-384 is + * available. Try SHA-256 first as 384 wastes resources + */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_LOWLEVEL_OR_PSA) +#define COOKIE_MD MBEDTLS_MD_SHA256 +#define COOKIE_MD_OUTLEN 32 +#define COOKIE_HMAC_LEN 28 +#elif defined(MBEDTLS_HAS_ALG_SHA_384_VIA_LOWLEVEL_OR_PSA) +#define COOKIE_MD MBEDTLS_MD_SHA384 +#define COOKIE_MD_OUTLEN 48 +#define COOKIE_HMAC_LEN 28 +#else +#error "DTLS hello verify needs SHA-256 or SHA-384" +#endif + +/* + * Cookies are formed of a 4-bytes timestamp (or serial number) and + * an HMAC of timestamp and client ID. + */ +#define COOKIE_LEN (4 + COOKIE_HMAC_LEN) + +void mbedtls_ssl_cookie_init(mbedtls_ssl_cookie_ctx *ctx) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ctx->psa_hmac_key = MBEDTLS_SVC_KEY_ID_INIT; +#else + mbedtls_md_init(&ctx->hmac_ctx); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#if !defined(MBEDTLS_HAVE_TIME) + ctx->serial = 0; +#endif + ctx->timeout = MBEDTLS_SSL_COOKIE_TIMEOUT; + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&ctx->mutex); +#endif +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ +} + +void mbedtls_ssl_cookie_set_timeout(mbedtls_ssl_cookie_ctx *ctx, unsigned long delay) +{ + ctx->timeout = delay; +} + +void mbedtls_ssl_cookie_free(mbedtls_ssl_cookie_ctx *ctx) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_destroy_key(ctx->psa_hmac_key); +#else + mbedtls_md_free(&ctx->hmac_ctx); + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_free(&ctx->mutex); +#endif +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_ssl_cookie_ctx)); +} + +int mbedtls_ssl_cookie_setup(mbedtls_ssl_cookie_ctx *ctx, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_algorithm_t alg; + + (void) f_rng; + (void) p_rng; + + alg = mbedtls_hash_info_psa_from_md(COOKIE_MD); + if (alg == 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ctx->psa_hmac_alg = PSA_ALG_TRUNCATED_MAC(PSA_ALG_HMAC(alg), + COOKIE_HMAC_LEN); + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_MESSAGE | + PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attributes, ctx->psa_hmac_alg); + psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(COOKIE_MD_OUTLEN)); + + if ((status = psa_generate_key(&attributes, + &ctx->psa_hmac_key)) != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char key[COOKIE_MD_OUTLEN]; + + if ((ret = f_rng(p_rng, key, sizeof(key))) != 0) { + return ret; + } + + ret = mbedtls_md_setup(&ctx->hmac_ctx, mbedtls_md_info_from_type(COOKIE_MD), 1); + if (ret != 0) { + return ret; + } + + ret = mbedtls_md_hmac_starts(&ctx->hmac_ctx, key, sizeof(key)); + if (ret != 0) { + return ret; + } + + mbedtls_platform_zeroize(key, sizeof(key)); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + return 0; +} + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) +/* + * Generate the HMAC part of a cookie + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_cookie_hmac(mbedtls_md_context_t *hmac_ctx, + const unsigned char time[4], + unsigned char **p, unsigned char *end, + const unsigned char *cli_id, size_t cli_id_len) +{ + unsigned char hmac_out[COOKIE_MD_OUTLEN]; + + MBEDTLS_SSL_CHK_BUF_PTR(*p, end, COOKIE_HMAC_LEN); + + if (mbedtls_md_hmac_reset(hmac_ctx) != 0 || + mbedtls_md_hmac_update(hmac_ctx, time, 4) != 0 || + mbedtls_md_hmac_update(hmac_ctx, cli_id, cli_id_len) != 0 || + mbedtls_md_hmac_finish(hmac_ctx, hmac_out) != 0) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + memcpy(*p, hmac_out, COOKIE_HMAC_LEN); + *p += COOKIE_HMAC_LEN; + + return 0; +} +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + +/* + * Generate cookie for DTLS ClientHello verification + */ +int mbedtls_ssl_cookie_write(void *p_ctx, + unsigned char **p, unsigned char *end, + const unsigned char *cli_id, size_t cli_id_len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t sign_mac_length = 0; +#endif + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_cookie_ctx *ctx = (mbedtls_ssl_cookie_ctx *) p_ctx; + unsigned long t; + + if (ctx == NULL || cli_id == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + MBEDTLS_SSL_CHK_BUF_PTR(*p, end, COOKIE_LEN); + +#if defined(MBEDTLS_HAVE_TIME) + t = (unsigned long) mbedtls_time(NULL); +#else + t = ctx->serial++; +#endif + + MBEDTLS_PUT_UINT32_BE(t, *p, 0); + *p += 4; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_mac_sign_setup(&operation, ctx->psa_hmac_key, + ctx->psa_hmac_alg); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + status = psa_mac_update(&operation, *p - 4, 4); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + status = psa_mac_update(&operation, cli_id, cli_id_len); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + status = psa_mac_sign_finish(&operation, *p, COOKIE_MD_OUTLEN, + &sign_mac_length); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + *p += COOKIE_HMAC_LEN; + + ret = 0; +#else +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_SSL_INTERNAL_ERROR, ret); + } +#endif + + ret = ssl_cookie_hmac(&ctx->hmac_ctx, *p - 4, + p, end, cli_id, cli_id_len); + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_SSL_INTERNAL_ERROR, + MBEDTLS_ERR_THREADING_MUTEX_ERROR); + } +#endif +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +exit: + status = psa_mac_abort(&operation); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + return ret; +} + +/* + * Check a cookie + */ +int mbedtls_ssl_cookie_check(void *p_ctx, + const unsigned char *cookie, size_t cookie_len, + const unsigned char *cli_id, size_t cli_id_len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; +#else + unsigned char ref_hmac[COOKIE_HMAC_LEN]; + unsigned char *p = ref_hmac; +#endif + int ret = 0; + mbedtls_ssl_cookie_ctx *ctx = (mbedtls_ssl_cookie_ctx *) p_ctx; + unsigned long cur_time, cookie_time; + + if (ctx == NULL || cli_id == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (cookie_len != COOKIE_LEN) { + return -1; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_mac_verify_setup(&operation, ctx->psa_hmac_key, + ctx->psa_hmac_alg); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + status = psa_mac_update(&operation, cookie, 4); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + status = psa_mac_update(&operation, cli_id, + cli_id_len); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + status = psa_mac_verify_finish(&operation, cookie + 4, + COOKIE_HMAC_LEN); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + ret = 0; +#else +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_SSL_INTERNAL_ERROR, ret); + } +#endif + + if (ssl_cookie_hmac(&ctx->hmac_ctx, cookie, + &p, p + sizeof(ref_hmac), + cli_id, cli_id_len) != 0) { + ret = -1; + } + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_SSL_INTERNAL_ERROR, + MBEDTLS_ERR_THREADING_MUTEX_ERROR); + } +#endif + + if (ret != 0) { + goto exit; + } + + if (mbedtls_ct_memcmp(cookie + 4, ref_hmac, sizeof(ref_hmac)) != 0) { + ret = -1; + goto exit; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_HAVE_TIME) + cur_time = (unsigned long) mbedtls_time(NULL); +#else + cur_time = ctx->serial; +#endif + + cookie_time = ((unsigned long) cookie[0] << 24) | + ((unsigned long) cookie[1] << 16) | + ((unsigned long) cookie[2] << 8) | + ((unsigned long) cookie[3]); + + if (ctx->timeout != 0 && cur_time - cookie_time > ctx->timeout) { + ret = -1; + goto exit; + } + +exit: +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_mac_abort(&operation); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + } +#else + mbedtls_platform_zeroize(ref_hmac, sizeof(ref_hmac)); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + return ret; +} +#endif /* MBEDTLS_SSL_COOKIE_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_debug_helpers.h b/r5dev/thirdparty/mbedtls/ssl_debug_helpers.h new file mode 100644 index 00000000..5c22ed22 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_debug_helpers.h @@ -0,0 +1,90 @@ +/** + * \file ssl_debug_helpers.h + * + * \brief Automatically generated helper functions for debugging + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_SSL_DEBUG_HELPERS_H +#define MBEDTLS_SSL_DEBUG_HELPERS_H + +#include "common.h" + +#if defined(MBEDTLS_DEBUG_C) + +#include "mbedtls/ssl.h" +#include "ssl_misc.h" + + +const char *mbedtls_ssl_states_str(mbedtls_ssl_states in); + +const char *mbedtls_ssl_protocol_version_str(mbedtls_ssl_protocol_version in); + +const char *mbedtls_tls_prf_types_str(mbedtls_tls_prf_types in); + +const char *mbedtls_ssl_key_export_type_str(mbedtls_ssl_key_export_type in); + +const char *mbedtls_ssl_sig_alg_to_str(uint16_t in); + +const char *mbedtls_ssl_named_group_to_str(uint16_t in); + +const char *mbedtls_ssl_get_extension_name(unsigned int extension_type); + +void mbedtls_ssl_print_extensions(const mbedtls_ssl_context *ssl, + int level, const char *file, int line, + int hs_msg_type, uint32_t extensions_mask, + const char *extra); + +void mbedtls_ssl_print_extension(const mbedtls_ssl_context *ssl, + int level, const char *file, int line, + int hs_msg_type, unsigned int extension_type, + const char *extra_msg0, const char *extra_msg1); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +void mbedtls_ssl_print_ticket_flags(const mbedtls_ssl_context *ssl, + int level, const char *file, int line, + unsigned int flags); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS */ + +#define MBEDTLS_SSL_PRINT_EXTS(level, hs_msg_type, extensions_mask) \ + mbedtls_ssl_print_extensions(ssl, level, __FILE__, __LINE__, \ + hs_msg_type, extensions_mask, NULL) + +#define MBEDTLS_SSL_PRINT_EXT(level, hs_msg_type, extension_type, extra) \ + mbedtls_ssl_print_extension(ssl, level, __FILE__, __LINE__, \ + hs_msg_type, extension_type, \ + extra, NULL) + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define MBEDTLS_SSL_PRINT_TICKET_FLAGS(level, flags) \ + mbedtls_ssl_print_ticket_flags(ssl, level, __FILE__, __LINE__, flags) +#endif + +#else + +#define MBEDTLS_SSL_PRINT_EXTS(level, hs_msg_type, extension_mask) + +#define MBEDTLS_SSL_PRINT_EXT(level, hs_msg_type, extension_type, extra) + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define MBEDTLS_SSL_PRINT_TICKET_FLAGS(level, flags) +#endif + +#endif /* MBEDTLS_DEBUG_C */ + +#endif /* MBEDTLS_SSL_DEBUG_HELPERS_H */ diff --git a/r5dev/thirdparty/mbedtls/ssl_debug_helpers_generated.c b/r5dev/thirdparty/mbedtls/ssl_debug_helpers_generated.c new file mode 100644 index 00000000..9add9db2 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_debug_helpers_generated.c @@ -0,0 +1,225 @@ +/* Automatically generated by generate_ssl_debug_helpers.py. DO NOT EDIT. */ + +/** + * \file ssl_debug_helpers_generated.c + * + * \brief Automatically generated helper functions for debugging + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_DEBUG_C) + +#include "ssl_debug_helpers.h" + + +const char *mbedtls_ssl_named_group_to_str( uint16_t in ) +{ + switch( in ) + { + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP192K1: + return "secp192k1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP192R1: + return "secp192r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP224K1: + return "secp224k1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP224R1: + return "secp224r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP256K1: + return "secp256k1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1: + return "secp256r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1: + return "secp384r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_SECP521R1: + return "secp521r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_BP256R1: + return "bp256r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_BP384R1: + return "bp384r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_BP512R1: + return "bp512r1"; + case MBEDTLS_SSL_IANA_TLS_GROUP_X25519: + return "x25519"; + case MBEDTLS_SSL_IANA_TLS_GROUP_X448: + return "x448"; + case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE2048: + return "ffdhe2048"; + case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE3072: + return "ffdhe3072"; + case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE4096: + return "ffdhe4096"; + case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE6144: + return "ffdhe6144"; + case MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE8192: + return "ffdhe8192"; + }; + + return "UNKOWN"; +} +const char *mbedtls_ssl_sig_alg_to_str( uint16_t in ) +{ + switch( in ) + { + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256: + return "rsa_pkcs1_sha256"; + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA384: + return "rsa_pkcs1_sha384"; + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA512: + return "rsa_pkcs1_sha512"; + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256: + return "ecdsa_secp256r1_sha256"; + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP384R1_SHA384: + return "ecdsa_secp384r1_sha384"; + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP521R1_SHA512: + return "ecdsa_secp521r1_sha512"; + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256: + return "rsa_pss_rsae_sha256"; + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384: + return "rsa_pss_rsae_sha384"; + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512: + return "rsa_pss_rsae_sha512"; + case MBEDTLS_TLS1_3_SIG_ED25519: + return "ed25519"; + case MBEDTLS_TLS1_3_SIG_ED448: + return "ed448"; + case MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA256: + return "rsa_pss_pss_sha256"; + case MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA384: + return "rsa_pss_pss_sha384"; + case MBEDTLS_TLS1_3_SIG_RSA_PSS_PSS_SHA512: + return "rsa_pss_pss_sha512"; + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA1: + return "rsa_pkcs1_sha1"; + case MBEDTLS_TLS1_3_SIG_ECDSA_SHA1: + return "ecdsa_sha1"; + case MBEDTLS_TLS1_3_SIG_NONE: + return "none"; + }; + + return "UNKNOWN"; +} +const char *mbedtls_ssl_states_str( mbedtls_ssl_states in ) +{ + const char * in_to_str[]= + { + [MBEDTLS_SSL_HELLO_REQUEST] = "MBEDTLS_SSL_HELLO_REQUEST", + [MBEDTLS_SSL_CLIENT_HELLO] = "MBEDTLS_SSL_CLIENT_HELLO", + [MBEDTLS_SSL_SERVER_HELLO] = "MBEDTLS_SSL_SERVER_HELLO", + [MBEDTLS_SSL_SERVER_CERTIFICATE] = "MBEDTLS_SSL_SERVER_CERTIFICATE", + [MBEDTLS_SSL_SERVER_KEY_EXCHANGE] = "MBEDTLS_SSL_SERVER_KEY_EXCHANGE", + [MBEDTLS_SSL_CERTIFICATE_REQUEST] = "MBEDTLS_SSL_CERTIFICATE_REQUEST", + [MBEDTLS_SSL_SERVER_HELLO_DONE] = "MBEDTLS_SSL_SERVER_HELLO_DONE", + [MBEDTLS_SSL_CLIENT_CERTIFICATE] = "MBEDTLS_SSL_CLIENT_CERTIFICATE", + [MBEDTLS_SSL_CLIENT_KEY_EXCHANGE] = "MBEDTLS_SSL_CLIENT_KEY_EXCHANGE", + [MBEDTLS_SSL_CERTIFICATE_VERIFY] = "MBEDTLS_SSL_CERTIFICATE_VERIFY", + [MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC] = "MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC", + [MBEDTLS_SSL_CLIENT_FINISHED] = "MBEDTLS_SSL_CLIENT_FINISHED", + [MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC] = "MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC", + [MBEDTLS_SSL_SERVER_FINISHED] = "MBEDTLS_SSL_SERVER_FINISHED", + [MBEDTLS_SSL_FLUSH_BUFFERS] = "MBEDTLS_SSL_FLUSH_BUFFERS", + [MBEDTLS_SSL_HANDSHAKE_WRAPUP] = "MBEDTLS_SSL_HANDSHAKE_WRAPUP", + [MBEDTLS_SSL_NEW_SESSION_TICKET] = "MBEDTLS_SSL_NEW_SESSION_TICKET", + [MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT] = "MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT", + [MBEDTLS_SSL_HELLO_RETRY_REQUEST] = "MBEDTLS_SSL_HELLO_RETRY_REQUEST", + [MBEDTLS_SSL_ENCRYPTED_EXTENSIONS] = "MBEDTLS_SSL_ENCRYPTED_EXTENSIONS", + [MBEDTLS_SSL_END_OF_EARLY_DATA] = "MBEDTLS_SSL_END_OF_EARLY_DATA", + [MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY] = "MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY", + [MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED] = "MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED", + [MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO] = "MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO", + [MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO] = "MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO", + [MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO] = "MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO", + [MBEDTLS_SSL_SERVER_CCS_AFTER_HELLO_RETRY_REQUEST] = "MBEDTLS_SSL_SERVER_CCS_AFTER_HELLO_RETRY_REQUEST", + [MBEDTLS_SSL_HANDSHAKE_OVER] = "MBEDTLS_SSL_HANDSHAKE_OVER", + [MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET] = "MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET", + [MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET_FLUSH] = "MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET_FLUSH", + }; + + if( in > ( sizeof( in_to_str )/sizeof( in_to_str[0]) - 1 ) || + in_to_str[ in ] == NULL ) + { + return "UNKNOWN_VALUE"; + } + return in_to_str[ in ]; +} + +const char *mbedtls_ssl_protocol_version_str( mbedtls_ssl_protocol_version in ) +{ + const char * in_to_str[]= + { + [MBEDTLS_SSL_VERSION_UNKNOWN] = "MBEDTLS_SSL_VERSION_UNKNOWN", + [MBEDTLS_SSL_VERSION_TLS1_2] = "MBEDTLS_SSL_VERSION_TLS1_2", + [MBEDTLS_SSL_VERSION_TLS1_3] = "MBEDTLS_SSL_VERSION_TLS1_3", + }; + + if( in > ( sizeof( in_to_str )/sizeof( in_to_str[0]) - 1 ) || + in_to_str[ in ] == NULL ) + { + return "UNKNOWN_VALUE"; + } + return in_to_str[ in ]; +} + +const char *mbedtls_tls_prf_types_str( mbedtls_tls_prf_types in ) +{ + const char * in_to_str[]= + { + [MBEDTLS_SSL_TLS_PRF_NONE] = "MBEDTLS_SSL_TLS_PRF_NONE", + [MBEDTLS_SSL_TLS_PRF_SHA384] = "MBEDTLS_SSL_TLS_PRF_SHA384", + [MBEDTLS_SSL_TLS_PRF_SHA256] = "MBEDTLS_SSL_TLS_PRF_SHA256", + [MBEDTLS_SSL_HKDF_EXPAND_SHA384] = "MBEDTLS_SSL_HKDF_EXPAND_SHA384", + [MBEDTLS_SSL_HKDF_EXPAND_SHA256] = "MBEDTLS_SSL_HKDF_EXPAND_SHA256", + }; + + if( in > ( sizeof( in_to_str )/sizeof( in_to_str[0]) - 1 ) || + in_to_str[ in ] == NULL ) + { + return "UNKNOWN_VALUE"; + } + return in_to_str[ in ]; +} + +const char *mbedtls_ssl_key_export_type_str( mbedtls_ssl_key_export_type in ) +{ + const char * in_to_str[]= + { + [MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET] = "MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET", +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + [MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_EARLY_SECRET] = "MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_EARLY_SECRET", + [MBEDTLS_SSL_KEY_EXPORT_TLS1_3_EARLY_EXPORTER_SECRET] = "MBEDTLS_SSL_KEY_EXPORT_TLS1_3_EARLY_EXPORTER_SECRET", + [MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_HANDSHAKE_TRAFFIC_SECRET] = "MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_HANDSHAKE_TRAFFIC_SECRET", + [MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_HANDSHAKE_TRAFFIC_SECRET] = "MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_HANDSHAKE_TRAFFIC_SECRET", + [MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_APPLICATION_TRAFFIC_SECRET] = "MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_APPLICATION_TRAFFIC_SECRET", + [MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_APPLICATION_TRAFFIC_SECRET] = "MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_APPLICATION_TRAFFIC_SECRET", +#endif + }; + + if( in > ( sizeof( in_to_str )/sizeof( in_to_str[0]) - 1 ) || + in_to_str[ in ] == NULL ) + { + return "UNKNOWN_VALUE"; + } + return in_to_str[ in ]; +} + + + +#endif /* MBEDTLS_DEBUG_C */ +/* End of automatically generated file. */ + diff --git a/r5dev/thirdparty/mbedtls/ssl_misc.h b/r5dev/thirdparty/mbedtls/ssl_misc.h new file mode 100644 index 00000000..6fe0414f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_misc.h @@ -0,0 +1,2769 @@ +/** + * \file ssl_misc.h + * + * \brief Internal functions shared by the SSL modules + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBEDTLS_SSL_MISC_H +#define MBEDTLS_SSL_MISC_H + +#include "mbedtls/build_info.h" + +#include "mbedtls/ssl.h" +#include "mbedtls/cipher.h" + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#include "hash_info.h" +#endif +#include "mbedtls/legacy_or_psa.h" + +#if defined(MBEDTLS_MD5_C) +#include "mbedtls/md5.h" +#endif + +#if defined(MBEDTLS_SHA1_C) +#include "mbedtls/sha1.h" +#endif + +#if defined(MBEDTLS_SHA256_C) +#include "mbedtls/sha256.h" +#endif + +#if defined(MBEDTLS_SHA512_C) +#include "mbedtls/sha512.h" +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \ + !defined(MBEDTLS_USE_PSA_CRYPTO) +#include "mbedtls/ecjpake.h" +#endif + +#include "mbedtls/pk.h" +#include "common.h" + +/* Shorthand for restartable ECC */ +#if defined(MBEDTLS_ECP_RESTARTABLE) && \ + defined(MBEDTLS_SSL_CLI_C) && \ + defined(MBEDTLS_SSL_PROTO_TLS1_2) && \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +#define MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED +#endif + +#define MBEDTLS_SSL_INITIAL_HANDSHAKE 0 +#define MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS 1 /* In progress */ +#define MBEDTLS_SSL_RENEGOTIATION_DONE 2 /* Done or aborted */ +#define MBEDTLS_SSL_RENEGOTIATION_PENDING 3 /* Requested (server only) */ + +/* Faked handshake message identity for HelloRetryRequest. */ +#define MBEDTLS_SSL_TLS1_3_HS_HELLO_RETRY_REQUEST (-MBEDTLS_SSL_HS_SERVER_HELLO) + +/* + * Internal identity of handshake extensions + */ +#define MBEDTLS_SSL_EXT_ID_UNRECOGNIZED 0 +#define MBEDTLS_SSL_EXT_ID_SERVERNAME 1 +#define MBEDTLS_SSL_EXT_ID_SERVERNAME_HOSTNAME 1 +#define MBEDTLS_SSL_EXT_ID_MAX_FRAGMENT_LENGTH 2 +#define MBEDTLS_SSL_EXT_ID_STATUS_REQUEST 3 +#define MBEDTLS_SSL_EXT_ID_SUPPORTED_GROUPS 4 +#define MBEDTLS_SSL_EXT_ID_SUPPORTED_ELLIPTIC_CURVES 4 +#define MBEDTLS_SSL_EXT_ID_SIG_ALG 5 +#define MBEDTLS_SSL_EXT_ID_USE_SRTP 6 +#define MBEDTLS_SSL_EXT_ID_HEARTBEAT 7 +#define MBEDTLS_SSL_EXT_ID_ALPN 8 +#define MBEDTLS_SSL_EXT_ID_SCT 9 +#define MBEDTLS_SSL_EXT_ID_CLI_CERT_TYPE 10 +#define MBEDTLS_SSL_EXT_ID_SERV_CERT_TYPE 11 +#define MBEDTLS_SSL_EXT_ID_PADDING 12 +#define MBEDTLS_SSL_EXT_ID_PRE_SHARED_KEY 13 +#define MBEDTLS_SSL_EXT_ID_EARLY_DATA 14 +#define MBEDTLS_SSL_EXT_ID_SUPPORTED_VERSIONS 15 +#define MBEDTLS_SSL_EXT_ID_COOKIE 16 +#define MBEDTLS_SSL_EXT_ID_PSK_KEY_EXCHANGE_MODES 17 +#define MBEDTLS_SSL_EXT_ID_CERT_AUTH 18 +#define MBEDTLS_SSL_EXT_ID_OID_FILTERS 19 +#define MBEDTLS_SSL_EXT_ID_POST_HANDSHAKE_AUTH 20 +#define MBEDTLS_SSL_EXT_ID_SIG_ALG_CERT 21 +#define MBEDTLS_SSL_EXT_ID_KEY_SHARE 22 +#define MBEDTLS_SSL_EXT_ID_TRUNCATED_HMAC 23 +#define MBEDTLS_SSL_EXT_ID_SUPPORTED_POINT_FORMATS 24 +#define MBEDTLS_SSL_EXT_ID_ENCRYPT_THEN_MAC 25 +#define MBEDTLS_SSL_EXT_ID_EXTENDED_MASTER_SECRET 26 +#define MBEDTLS_SSL_EXT_ID_SESSION_TICKET 27 +#define MBEDTLS_SSL_EXT_ID_RECORD_SIZE_LIMIT 28 + +/* Utility for translating IANA extension type. */ +uint32_t mbedtls_ssl_get_extension_id(unsigned int extension_type); +uint32_t mbedtls_ssl_get_extension_mask(unsigned int extension_type); +/* Macros used to define mask constants */ +#define MBEDTLS_SSL_EXT_MASK(id) (1ULL << (MBEDTLS_SSL_EXT_ID_##id)) +/* Reset value of extension mask */ +#define MBEDTLS_SSL_EXT_MASK_NONE 0 + +/* In messages containing extension requests, we should ignore unrecognized + * extensions. In messages containing extension responses, unrecognized + * extensions should result in handshake abortion. Messages containing + * extension requests include ClientHello, CertificateRequest and + * NewSessionTicket. Messages containing extension responses include + * ServerHello, HelloRetryRequest, EncryptedExtensions and Certificate. + * + * RFC 8446 section 4.1.3 + * + * The ServerHello MUST only include extensions which are required to establish + * the cryptographic context and negotiate the protocol version. + * + * RFC 8446 section 4.2 + * + * If an implementation receives an extension which it recognizes and which is + * not specified for the message in which it appears, it MUST abort the handshake + * with an "illegal_parameter" alert. + */ + +/* Extensions that are not recognized by TLS 1.3 */ +#define MBEDTLS_SSL_TLS1_3_EXT_MASK_UNRECOGNIZED \ + (MBEDTLS_SSL_EXT_MASK(SUPPORTED_POINT_FORMATS) | \ + MBEDTLS_SSL_EXT_MASK(ENCRYPT_THEN_MAC) | \ + MBEDTLS_SSL_EXT_MASK(EXTENDED_MASTER_SECRET) | \ + MBEDTLS_SSL_EXT_MASK(SESSION_TICKET) | \ + MBEDTLS_SSL_EXT_MASK(TRUNCATED_HMAC) | \ + MBEDTLS_SSL_EXT_MASK(UNRECOGNIZED)) + +/* RFC 8446 section 4.2. Allowed extensions for ClientHello */ +#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CH \ + (MBEDTLS_SSL_EXT_MASK(SERVERNAME) | \ + MBEDTLS_SSL_EXT_MASK(MAX_FRAGMENT_LENGTH) | \ + MBEDTLS_SSL_EXT_MASK(STATUS_REQUEST) | \ + MBEDTLS_SSL_EXT_MASK(SUPPORTED_GROUPS) | \ + MBEDTLS_SSL_EXT_MASK(SIG_ALG) | \ + MBEDTLS_SSL_EXT_MASK(USE_SRTP) | \ + MBEDTLS_SSL_EXT_MASK(HEARTBEAT) | \ + MBEDTLS_SSL_EXT_MASK(ALPN) | \ + MBEDTLS_SSL_EXT_MASK(SCT) | \ + MBEDTLS_SSL_EXT_MASK(CLI_CERT_TYPE) | \ + MBEDTLS_SSL_EXT_MASK(SERV_CERT_TYPE) | \ + MBEDTLS_SSL_EXT_MASK(PADDING) | \ + MBEDTLS_SSL_EXT_MASK(KEY_SHARE) | \ + MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY) | \ + MBEDTLS_SSL_EXT_MASK(PSK_KEY_EXCHANGE_MODES) | \ + MBEDTLS_SSL_EXT_MASK(EARLY_DATA) | \ + MBEDTLS_SSL_EXT_MASK(COOKIE) | \ + MBEDTLS_SSL_EXT_MASK(SUPPORTED_VERSIONS) | \ + MBEDTLS_SSL_EXT_MASK(CERT_AUTH) | \ + MBEDTLS_SSL_EXT_MASK(POST_HANDSHAKE_AUTH) | \ + MBEDTLS_SSL_EXT_MASK(SIG_ALG_CERT) | \ + MBEDTLS_SSL_EXT_MASK(RECORD_SIZE_LIMIT) | \ + MBEDTLS_SSL_TLS1_3_EXT_MASK_UNRECOGNIZED) + +/* RFC 8446 section 4.2. Allowed extensions for EncryptedExtensions */ +#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_EE \ + (MBEDTLS_SSL_EXT_MASK(SERVERNAME) | \ + MBEDTLS_SSL_EXT_MASK(MAX_FRAGMENT_LENGTH) | \ + MBEDTLS_SSL_EXT_MASK(SUPPORTED_GROUPS) | \ + MBEDTLS_SSL_EXT_MASK(USE_SRTP) | \ + MBEDTLS_SSL_EXT_MASK(HEARTBEAT) | \ + MBEDTLS_SSL_EXT_MASK(ALPN) | \ + MBEDTLS_SSL_EXT_MASK(CLI_CERT_TYPE) | \ + MBEDTLS_SSL_EXT_MASK(SERV_CERT_TYPE) | \ + MBEDTLS_SSL_EXT_MASK(EARLY_DATA) | \ + MBEDTLS_SSL_EXT_MASK(RECORD_SIZE_LIMIT)) + +/* RFC 8446 section 4.2. Allowed extensions for CertificateRequest */ +#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CR \ + (MBEDTLS_SSL_EXT_MASK(STATUS_REQUEST) | \ + MBEDTLS_SSL_EXT_MASK(SIG_ALG) | \ + MBEDTLS_SSL_EXT_MASK(SCT) | \ + MBEDTLS_SSL_EXT_MASK(CERT_AUTH) | \ + MBEDTLS_SSL_EXT_MASK(OID_FILTERS) | \ + MBEDTLS_SSL_EXT_MASK(SIG_ALG_CERT) | \ + MBEDTLS_SSL_TLS1_3_EXT_MASK_UNRECOGNIZED) + +/* RFC 8446 section 4.2. Allowed extensions for Certificate */ +#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CT \ + (MBEDTLS_SSL_EXT_MASK(STATUS_REQUEST) | \ + MBEDTLS_SSL_EXT_MASK(SCT)) + +/* RFC 8446 section 4.2. Allowed extensions for ServerHello */ +#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_SH \ + (MBEDTLS_SSL_EXT_MASK(KEY_SHARE) | \ + MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY) | \ + MBEDTLS_SSL_EXT_MASK(SUPPORTED_VERSIONS)) + +/* RFC 8446 section 4.2. Allowed extensions for HelloRetryRequest */ +#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_HRR \ + (MBEDTLS_SSL_EXT_MASK(KEY_SHARE) | \ + MBEDTLS_SSL_EXT_MASK(COOKIE) | \ + MBEDTLS_SSL_EXT_MASK(SUPPORTED_VERSIONS)) + +/* RFC 8446 section 4.2. Allowed extensions for NewSessionTicket */ +#define MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_NST \ + (MBEDTLS_SSL_EXT_MASK(EARLY_DATA) | \ + MBEDTLS_SSL_TLS1_3_EXT_MASK_UNRECOGNIZED) + +/* + * Helper macros for function call with return check. + */ +/* + * Exit when return non-zero value + */ +#define MBEDTLS_SSL_PROC_CHK(f) \ + do { \ + ret = (f); \ + if (ret != 0) \ + { \ + goto cleanup; \ + } \ + } while (0) +/* + * Exit when return negative value + */ +#define MBEDTLS_SSL_PROC_CHK_NEG(f) \ + do { \ + ret = (f); \ + if (ret < 0) \ + { \ + goto cleanup; \ + } \ + } while (0) + +/* + * DTLS retransmission states, see RFC 6347 4.2.4 + * + * The SENDING state is merged in PREPARING for initial sends, + * but is distinct for resends. + * + * Note: initial state is wrong for server, but is not used anyway. + */ +#define MBEDTLS_SSL_RETRANS_PREPARING 0 +#define MBEDTLS_SSL_RETRANS_SENDING 1 +#define MBEDTLS_SSL_RETRANS_WAITING 2 +#define MBEDTLS_SSL_RETRANS_FINISHED 3 + +/* + * Allow extra bytes for record, authentication and encryption overhead: + * counter (8) + header (5) + IV(16) + MAC (16-48) + padding (0-256). + */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + +/* This macro determines whether CBC is supported. */ +#if defined(MBEDTLS_CIPHER_MODE_CBC) && \ + (defined(MBEDTLS_AES_C) || \ + defined(MBEDTLS_CAMELLIA_C) || \ + defined(MBEDTLS_ARIA_C) || \ + defined(MBEDTLS_DES_C)) +#define MBEDTLS_SSL_SOME_SUITES_USE_CBC +#endif + +/* This macro determines whether a ciphersuite using a + * stream cipher can be used. */ +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) +#define MBEDTLS_SSL_SOME_SUITES_USE_STREAM +#endif + +/* This macro determines whether the CBC construct used in TLS 1.2 is supported. */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) && \ + defined(MBEDTLS_SSL_PROTO_TLS1_2) +#define MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC +#endif + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_STREAM) || \ + defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) +#define MBEDTLS_SSL_SOME_SUITES_USE_MAC +#endif + +/* This macro determines whether a ciphersuite uses Encrypt-then-MAC with CBC */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) && \ + defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +#define MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM +#endif + +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) +/* Ciphersuites using HMAC */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#define MBEDTLS_SSL_MAC_ADD 48 /* SHA-384 used for HMAC */ +#elif defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#define MBEDTLS_SSL_MAC_ADD 32 /* SHA-256 used for HMAC */ +#else +#define MBEDTLS_SSL_MAC_ADD 20 /* SHA-1 used for HMAC */ +#endif +#else /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ +/* AEAD ciphersuites: GCM and CCM use a 128 bits tag */ +#define MBEDTLS_SSL_MAC_ADD 16 +#endif + +#if defined(MBEDTLS_CIPHER_MODE_CBC) +#define MBEDTLS_SSL_PADDING_ADD 256 +#else +#define MBEDTLS_SSL_PADDING_ADD 0 +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +#define MBEDTLS_SSL_MAX_CID_EXPANSION MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY +#else +#define MBEDTLS_SSL_MAX_CID_EXPANSION 0 +#endif + +#define MBEDTLS_SSL_PAYLOAD_OVERHEAD (MBEDTLS_MAX_IV_LENGTH + \ + MBEDTLS_SSL_MAC_ADD + \ + MBEDTLS_SSL_PADDING_ADD + \ + MBEDTLS_SSL_MAX_CID_EXPANSION \ + ) + +#define MBEDTLS_SSL_IN_PAYLOAD_LEN (MBEDTLS_SSL_PAYLOAD_OVERHEAD + \ + (MBEDTLS_SSL_IN_CONTENT_LEN)) + +#define MBEDTLS_SSL_OUT_PAYLOAD_LEN (MBEDTLS_SSL_PAYLOAD_OVERHEAD + \ + (MBEDTLS_SSL_OUT_CONTENT_LEN)) + +/* The maximum number of buffered handshake messages. */ +#define MBEDTLS_SSL_MAX_BUFFERED_HS 4 + +/* Maximum length we can advertise as our max content length for + RFC 6066 max_fragment_length extension negotiation purposes + (the lesser of both sizes, if they are unequal.) + */ +#define MBEDTLS_TLS_EXT_ADV_CONTENT_LEN ( \ + (MBEDTLS_SSL_IN_CONTENT_LEN > MBEDTLS_SSL_OUT_CONTENT_LEN) \ + ? (MBEDTLS_SSL_OUT_CONTENT_LEN) \ + : (MBEDTLS_SSL_IN_CONTENT_LEN) \ + ) + +/* Maximum size in bytes of list in signature algorithms ext., RFC 5246/8446 */ +#define MBEDTLS_SSL_MAX_SIG_ALG_LIST_LEN 65534 + +/* Minimum size in bytes of list in signature algorithms ext., RFC 5246/8446 */ +#define MBEDTLS_SSL_MIN_SIG_ALG_LIST_LEN 2 + +/* Maximum size in bytes of list in supported elliptic curve ext., RFC 4492 */ +#define MBEDTLS_SSL_MAX_CURVE_LIST_LEN 65535 + +#define MBEDTLS_RECEIVED_SIG_ALGS_SIZE 20 + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + +#define MBEDTLS_TLS_SIG_NONE MBEDTLS_TLS1_3_SIG_NONE + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +#define MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(sig, hash) ((hash << 8) | sig) +#define MBEDTLS_SSL_TLS12_SIG_ALG_FROM_SIG_AND_HASH_ALG(alg) (alg & 0xFF) +#define MBEDTLS_SSL_TLS12_HASH_ALG_FROM_SIG_AND_HASH_ALG(alg) (alg >> 8) +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +/* + * Check that we obey the standard's message size bounds + */ + +#if MBEDTLS_SSL_IN_CONTENT_LEN > 16384 +#error "Bad configuration - incoming record content too large." +#endif + +#if MBEDTLS_SSL_OUT_CONTENT_LEN > 16384 +#error "Bad configuration - outgoing record content too large." +#endif + +#if MBEDTLS_SSL_IN_PAYLOAD_LEN > MBEDTLS_SSL_IN_CONTENT_LEN + 2048 +#error "Bad configuration - incoming protected record payload too large." +#endif + +#if MBEDTLS_SSL_OUT_PAYLOAD_LEN > MBEDTLS_SSL_OUT_CONTENT_LEN + 2048 +#error "Bad configuration - outgoing protected record payload too large." +#endif + +/* Calculate buffer sizes */ + +/* Note: Even though the TLS record header is only 5 bytes + long, we're internally using 8 bytes to store the + implicit sequence number. */ +#define MBEDTLS_SSL_HEADER_LEN 13 + +#if !defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +#define MBEDTLS_SSL_IN_BUFFER_LEN \ + ((MBEDTLS_SSL_HEADER_LEN) + (MBEDTLS_SSL_IN_PAYLOAD_LEN)) +#else +#define MBEDTLS_SSL_IN_BUFFER_LEN \ + ((MBEDTLS_SSL_HEADER_LEN) + (MBEDTLS_SSL_IN_PAYLOAD_LEN) \ + + (MBEDTLS_SSL_CID_IN_LEN_MAX)) +#endif + +#if !defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +#define MBEDTLS_SSL_OUT_BUFFER_LEN \ + ((MBEDTLS_SSL_HEADER_LEN) + (MBEDTLS_SSL_OUT_PAYLOAD_LEN)) +#else +#define MBEDTLS_SSL_OUT_BUFFER_LEN \ + ((MBEDTLS_SSL_HEADER_LEN) + (MBEDTLS_SSL_OUT_PAYLOAD_LEN) \ + + (MBEDTLS_SSL_CID_OUT_LEN_MAX)) +#endif + +#define MBEDTLS_CLIENT_HELLO_RANDOM_LEN 32 +#define MBEDTLS_SERVER_HELLO_RANDOM_LEN 32 + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +/** + * \brief Return the maximum fragment length (payload, in bytes) for + * the output buffer. For the client, this is the configured + * value. For the server, it is the minimum of two - the + * configured value and the negotiated one. + * + * \sa mbedtls_ssl_conf_max_frag_len() + * \sa mbedtls_ssl_get_max_out_record_payload() + * + * \param ssl SSL context + * + * \return Current maximum fragment length for the output buffer. + */ +size_t mbedtls_ssl_get_output_max_frag_len(const mbedtls_ssl_context *ssl); + +/** + * \brief Return the maximum fragment length (payload, in bytes) for + * the input buffer. This is the negotiated maximum fragment + * length, or, if there is none, MBEDTLS_SSL_IN_CONTENT_LEN. + * If it is not defined either, the value is 2^14. This function + * works as its predecessor, \c mbedtls_ssl_get_max_frag_len(). + * + * \sa mbedtls_ssl_conf_max_frag_len() + * \sa mbedtls_ssl_get_max_in_record_payload() + * + * \param ssl SSL context + * + * \return Current maximum fragment length for the output buffer. + */ +size_t mbedtls_ssl_get_input_max_frag_len(const mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) +static inline size_t mbedtls_ssl_get_output_buflen(const mbedtls_ssl_context *ctx) +{ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + return mbedtls_ssl_get_output_max_frag_len(ctx) + + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD + + MBEDTLS_SSL_CID_OUT_LEN_MAX; +#else + return mbedtls_ssl_get_output_max_frag_len(ctx) + + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD; +#endif +} + +static inline size_t mbedtls_ssl_get_input_buflen(const mbedtls_ssl_context *ctx) +{ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + return mbedtls_ssl_get_input_max_frag_len(ctx) + + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD + + MBEDTLS_SSL_CID_IN_LEN_MAX; +#else + return mbedtls_ssl_get_input_max_frag_len(ctx) + + MBEDTLS_SSL_HEADER_LEN + MBEDTLS_SSL_PAYLOAD_OVERHEAD; +#endif +} +#endif + +/* + * TLS extension flags (for extensions with outgoing ServerHello content + * that need it (e.g. for RENEGOTIATION_INFO the server already knows because + * of state of the renegotiation flag, so no indicator is required) + */ +#define MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT (1 << 0) +#define MBEDTLS_TLS_EXT_ECJPAKE_KKPP_OK (1 << 1) + +/** + * \brief This function checks if the remaining size in a buffer is + * greater or equal than a needed space. + * + * \param cur Pointer to the current position in the buffer. + * \param end Pointer to one past the end of the buffer. + * \param need Needed space in bytes. + * + * \return Zero if the needed space is available in the buffer, non-zero + * otherwise. + */ +#if !defined(MBEDTLS_TEST_HOOKS) +static inline int mbedtls_ssl_chk_buf_ptr(const uint8_t *cur, + const uint8_t *end, size_t need) +{ + return (cur > end) || (need > (size_t) (end - cur)); +} +#else +typedef struct { + const uint8_t *cur; + const uint8_t *end; + size_t need; +} mbedtls_ssl_chk_buf_ptr_args; + +void mbedtls_ssl_set_chk_buf_ptr_fail_args( + const uint8_t *cur, const uint8_t *end, size_t need); +void mbedtls_ssl_reset_chk_buf_ptr_fail_args(void); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_cmp_chk_buf_ptr_fail_args(mbedtls_ssl_chk_buf_ptr_args *args); + +static inline int mbedtls_ssl_chk_buf_ptr(const uint8_t *cur, + const uint8_t *end, size_t need) +{ + if ((cur > end) || (need > (size_t) (end - cur))) { + mbedtls_ssl_set_chk_buf_ptr_fail_args(cur, end, need); + return 1; + } + return 0; +} +#endif /* MBEDTLS_TEST_HOOKS */ + +/** + * \brief This macro checks if the remaining size in a buffer is + * greater or equal than a needed space. If it is not the case, + * it returns an SSL_BUFFER_TOO_SMALL error. + * + * \param cur Pointer to the current position in the buffer. + * \param end Pointer to one past the end of the buffer. + * \param need Needed space in bytes. + * + */ +#define MBEDTLS_SSL_CHK_BUF_PTR(cur, end, need) \ + do { \ + if (mbedtls_ssl_chk_buf_ptr((cur), (end), (need)) != 0) \ + { \ + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; \ + } \ + } while (0) + +/** + * \brief This macro checks if the remaining length in an input buffer is + * greater or equal than a needed length. If it is not the case, it + * returns #MBEDTLS_ERR_SSL_DECODE_ERROR error and pends a + * #MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR alert message. + * + * This is a function-like macro. It is guaranteed to evaluate each + * argument exactly once. + * + * \param cur Pointer to the current position in the buffer. + * \param end Pointer to one past the end of the buffer. + * \param need Needed length in bytes. + * + */ +#define MBEDTLS_SSL_CHK_BUF_READ_PTR(cur, end, need) \ + do { \ + if (mbedtls_ssl_chk_buf_ptr((cur), (end), (need)) != 0) \ + { \ + MBEDTLS_SSL_DEBUG_MSG(1, \ + ("missing input data in %s", __func__)); \ + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, \ + MBEDTLS_ERR_SSL_DECODE_ERROR); \ + return MBEDTLS_ERR_SSL_DECODE_ERROR; \ + } \ + } while (0) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int mbedtls_ssl_tls_prf_cb(const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen); + +/* cipher.h exports the maximum IV, key and block length from + * all ciphers enabled in the config, regardless of whether those + * ciphers are actually usable in SSL/TLS. Notably, XTS is enabled + * in the default configuration and uses 64 Byte keys, but it is + * not used for record protection in SSL/TLS. + * + * In order to prevent unnecessary inflation of key structures, + * we introduce SSL-specific variants of the max-{key,block,IV} + * macros here which are meant to only take those ciphers into + * account which can be negotiated in SSL/TLS. + * + * Since the current definitions of MBEDTLS_MAX_{KEY|BLOCK|IV}_LENGTH + * in cipher.h are rough overapproximations of the real maxima, here + * we content ourselves with replicating those overapproximations + * for the maximum block and IV length, and excluding XTS from the + * computation of the maximum key length. */ +#define MBEDTLS_SSL_MAX_BLOCK_LENGTH 16 +#define MBEDTLS_SSL_MAX_IV_LENGTH 16 +#define MBEDTLS_SSL_MAX_KEY_LENGTH 32 + +/** + * \brief The data structure holding the cryptographic material (key and IV) + * used for record protection in TLS 1.3. + */ +struct mbedtls_ssl_key_set { + /*! The key for client->server records. */ + unsigned char client_write_key[MBEDTLS_SSL_MAX_KEY_LENGTH]; + /*! The key for server->client records. */ + unsigned char server_write_key[MBEDTLS_SSL_MAX_KEY_LENGTH]; + /*! The IV for client->server records. */ + unsigned char client_write_iv[MBEDTLS_SSL_MAX_IV_LENGTH]; + /*! The IV for server->client records. */ + unsigned char server_write_iv[MBEDTLS_SSL_MAX_IV_LENGTH]; + + size_t key_len; /*!< The length of client_write_key and + * server_write_key, in Bytes. */ + size_t iv_len; /*!< The length of client_write_iv and + * server_write_iv, in Bytes. */ +}; +typedef struct mbedtls_ssl_key_set mbedtls_ssl_key_set; + +typedef struct { + unsigned char binder_key[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char client_early_traffic_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char early_exporter_master_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; +} mbedtls_ssl_tls13_early_secrets; + +typedef struct { + unsigned char client_handshake_traffic_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char server_handshake_traffic_secret[MBEDTLS_TLS1_3_MD_MAX_SIZE]; +} mbedtls_ssl_tls13_handshake_secrets; + +/* + * This structure contains the parameters only needed during handshake. + */ +struct mbedtls_ssl_handshake_params { + /* Frequently-used boolean or byte fields (placed early to take + * advantage of smaller code size for indirect access on Arm Thumb) */ + uint8_t resume; /*!< session resume indicator*/ + uint8_t cli_exts; /*!< client extension presence*/ + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + uint8_t sni_authmode; /*!< authmode from SNI callback */ +#endif + +#if defined(MBEDTLS_SSL_SRV_C) + /* Flag indicating if a CertificateRequest message has been sent + * to the client or not. */ + uint8_t certificate_request_sent; +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + uint8_t new_session_ticket; /*!< use NewSessionTicket? */ +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_CLI_C) + /** Minimum TLS version to be negotiated. + * + * It is set up in the ClientHello writing preparation stage and used + * throughout the ClientHello writing. Not relevant anymore as soon as + * the protocol version has been negotiated thus as soon as the + * ServerHello is received. + * For a fresh handshake not linked to any previous handshake, it is + * equal to the configured minimum minor version to be negotiated. When + * renegotiating or resuming a session, it is equal to the previously + * negotiated minor version. + * + * There is no maximum TLS version field in this handshake context. + * From the start of the handshake, we need to define a current protocol + * version for the record layer which we define as the maximum TLS + * version to be negotiated. The `tls_version` field of the SSL context is + * used to store this maximum value until it contains the actual + * negotiated value. + */ + mbedtls_ssl_protocol_version min_tls_version; +#endif + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + uint8_t extended_ms; /*!< use Extended Master Secret? */ +#endif + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + uint8_t async_in_progress; /*!< an asynchronous operation is in progress */ +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + unsigned char retransmit_state; /*!< Retransmission state */ +#endif + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + unsigned char group_list_heap_allocated; + unsigned char sig_algs_heap_allocated; +#endif + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + uint8_t ecrs_enabled; /*!< Handshake supports EC restart? */ + enum { /* this complements ssl->state with info on intra-state operations */ + ssl_ecrs_none = 0, /*!< nothing going on (yet) */ + ssl_ecrs_crt_verify, /*!< Certificate: crt_verify() */ + ssl_ecrs_ske_start_processing, /*!< ServerKeyExchange: pk_verify() */ + ssl_ecrs_cke_ecdh_calc_secret, /*!< ClientKeyExchange: ECDH step 2 */ + ssl_ecrs_crt_vrfy_sign, /*!< CertificateVerify: pk_sign() */ + } ecrs_state; /*!< current (or last) operation */ + mbedtls_x509_crt *ecrs_peer_cert; /*!< The peer's CRT chain. */ + size_t ecrs_n; /*!< place for saving a length */ +#endif + + mbedtls_ssl_ciphersuite_t const *ciphersuite_info; + + MBEDTLS_CHECK_RETURN_CRITICAL + int (*update_checksum)(mbedtls_ssl_context *, const unsigned char *, size_t); + MBEDTLS_CHECK_RETURN_CRITICAL + int (*calc_verify)(const mbedtls_ssl_context *, unsigned char *, size_t *); + MBEDTLS_CHECK_RETURN_CRITICAL + int (*calc_finished)(mbedtls_ssl_context *, unsigned char *, int); + mbedtls_ssl_tls_prf_cb *tls_prf; + + /* + * Handshake specific crypto variables + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + uint8_t key_exchange_mode; /*!< Selected key exchange mode */ + + /** Number of HelloRetryRequest messages received/sent from/to the server. */ + int hello_retry_request_count; + +#if defined(MBEDTLS_SSL_SRV_C) + /** selected_group of key_share extension in HelloRetryRequest message. */ + uint16_t hrr_selected_group; +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + uint8_t tls13_kex_modes; /*!< Key exchange modes supported by the client */ +#endif +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + uint16_t new_session_tickets_count; /*!< number of session tickets */ +#endif +#endif /* MBEDTLS_SSL_SRV_C */ + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + uint16_t received_sig_algs[MBEDTLS_RECEIVED_SIG_ALGS_SIZE]; +#endif + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + const uint16_t *group_list; + const uint16_t *sig_algs; +#endif + +#if defined(MBEDTLS_DHM_C) + mbedtls_dhm_context dhm_ctx; /*!< DHM key exchange */ +#endif + +/* Adding guard for MBEDTLS_ECDSA_C to ensure no compile errors due + * to guards in client and server code. There is a gap in functionality that + * access to ecdh_ctx structure is needed for MBEDTLS_ECDSA_C which does not + * seem correct. + */ +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_ecdh_context ecdh_ctx; /*!< ECDH key exchange */ +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) + psa_key_type_t ecdh_psa_type; + size_t ecdh_bits; + mbedtls_svc_key_id_t ecdh_psa_privkey; + uint8_t ecdh_psa_privkey_is_external; + unsigned char ecdh_psa_peerkey[MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH]; + size_t ecdh_psa_peerkey_len; +#endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */ +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_pake_operation_t psa_pake_ctx; /*!< EC J-PAKE key exchange */ + mbedtls_svc_key_id_t psa_pake_password; + uint8_t psa_pake_ctx_is_ok; +#else + mbedtls_ecjpake_context ecjpake_ctx; /*!< EC J-PAKE key exchange */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#if defined(MBEDTLS_SSL_CLI_C) + unsigned char *ecjpake_cache; /*!< Cache for ClientHello ext */ + size_t ecjpake_cache_len; /*!< Length of cached data */ +#endif +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + uint16_t *curves_tls_id; /*!< List of TLS IDs of supported elliptic curves */ +#endif + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_svc_key_id_t psk_opaque; /*!< Opaque PSK from the callback */ + uint8_t psk_opaque_is_internal; +#else + unsigned char *psk; /*!< PSK from the callback */ + size_t psk_len; /*!< Length of PSK from callback */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + uint16_t selected_identity; +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + mbedtls_x509_crt_restart_ctx ecrs_ctx; /*!< restart context */ +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + mbedtls_ssl_key_cert *key_cert; /*!< chosen key/cert pair (server) */ +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + mbedtls_ssl_key_cert *sni_key_cert; /*!< key/cert list from SNI */ + mbedtls_x509_crt *sni_ca_chain; /*!< trusted CAs from SNI callback */ + mbedtls_x509_crl *sni_ca_crl; /*!< trusted CAs CRLs from SNI */ +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && \ + !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + mbedtls_pk_context peer_pubkey; /*!< The public key from the peer. */ +#endif /* MBEDTLS_X509_CRT_PARSE_C && !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + struct { + size_t total_bytes_buffered; /*!< Cumulative size of heap allocated + * buffers used for message buffering. */ + + uint8_t seen_ccs; /*!< Indicates if a CCS message has + * been seen in the current flight. */ + + struct mbedtls_ssl_hs_buffer { + unsigned is_valid : 1; + unsigned is_fragmented : 1; + unsigned is_complete : 1; + unsigned char *data; + size_t data_len; + } hs[MBEDTLS_SSL_MAX_BUFFERED_HS]; + + struct { + unsigned char *data; + size_t len; + unsigned epoch; + } future_record; + + } buffering; + +#if defined(MBEDTLS_SSL_CLI_C) && \ + (defined(MBEDTLS_SSL_PROTO_DTLS) || \ + defined(MBEDTLS_SSL_PROTO_TLS1_3)) + unsigned char *cookie; /*!< HelloVerifyRequest cookie for DTLS + * HelloRetryRequest cookie for TLS 1.3 */ +#if !defined(MBEDTLS_SSL_PROTO_TLS1_3) + /* RFC 6347 page 15 + ... + opaque cookie<0..2^8-1>; + ... + */ + uint8_t cookie_len; +#else + /* RFC 8446 page 39 + ... + opaque cookie<0..2^16-1>; + ... + If TLS1_3 is enabled, the max length is 2^16 - 1 + */ + uint16_t cookie_len; /*!< DTLS: HelloVerifyRequest cookie length + * TLS1_3: HelloRetryRequest cookie length */ +#endif +#endif /* MBEDTLS_SSL_CLI_C && + ( MBEDTLS_SSL_PROTO_DTLS || + MBEDTLS_SSL_PROTO_TLS1_3 ) */ +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_PROTO_DTLS) + unsigned char cookie_verify_result; /*!< Srv: flag for sending a cookie */ +#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + unsigned int out_msg_seq; /*!< Outgoing handshake sequence number */ + unsigned int in_msg_seq; /*!< Incoming handshake sequence number */ + + uint32_t retransmit_timeout; /*!< Current value of timeout */ + mbedtls_ssl_flight_item *flight; /*!< Current outgoing flight */ + mbedtls_ssl_flight_item *cur_msg; /*!< Current message in flight */ + unsigned char *cur_msg_p; /*!< Position in current message */ + unsigned int in_flight_start_seq; /*!< Minimum message sequence in the + flight being received */ + mbedtls_ssl_transform *alt_transform_out; /*!< Alternative transform for + resending messages */ + unsigned char alt_out_ctr[MBEDTLS_SSL_SEQUENCE_NUMBER_LEN]; /*!< Alternative record epoch/counter + for resending messages */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* The state of CID configuration in this handshake. */ + + uint8_t cid_in_use; /*!< This indicates whether the use of the CID extension + * has been negotiated. Possible values are + * #MBEDTLS_SSL_CID_ENABLED and + * #MBEDTLS_SSL_CID_DISABLED. */ + unsigned char peer_cid[MBEDTLS_SSL_CID_OUT_LEN_MAX]; /*! The peer's CID */ + uint8_t peer_cid_len; /*!< The length of + * \c peer_cid. */ +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + uint16_t mtu; /*!< Handshake mtu, used to fragment outgoing messages */ +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + /* + * Checksum contexts + */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_hash_operation_t fin_sha256_psa; +#else + mbedtls_md_context_t fin_sha256; +#endif +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_hash_operation_t fin_sha384_psa; +#else + mbedtls_md_context_t fin_sha384; +#endif +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + uint16_t offered_group_id; /* The NamedGroup value for the group + * that is being used for ephemeral + * key exchange. + * + * On the client: Defaults to the first + * entry in the client's group list, + * but can be overwritten by the HRR. */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_CLI_C) + uint8_t client_auth; /*!< used to check if CertificateRequest has been + received from server side. If CertificateRequest + has been received, Certificate and CertificateVerify + should be sent to server */ +#endif /* MBEDTLS_SSL_CLI_C */ + /* + * State-local variables used during the processing + * of a specific handshake state. + */ + union { + /* Outgoing Finished message */ + struct { + uint8_t preparation_done; + + /* Buffer holding digest of the handshake up to + * but excluding the outgoing finished message. */ + unsigned char digest[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t digest_len; + } finished_out; + + /* Incoming Finished message */ + struct { + uint8_t preparation_done; + + /* Buffer holding digest of the handshake up to but + * excluding the peer's incoming finished message. */ + unsigned char digest[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t digest_len; + } finished_in; + + } state_local; + + /* End of state-local variables. */ + + unsigned char randbytes[MBEDTLS_CLIENT_HELLO_RANDOM_LEN + + MBEDTLS_SERVER_HELLO_RANDOM_LEN]; + /*!< random bytes */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + unsigned char premaster[MBEDTLS_PREMASTER_SIZE]; + /*!< premaster secret */ + size_t pmslen; /*!< premaster length */ +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + uint32_t sent_extensions; /*!< extensions sent by endpoint */ + uint32_t received_extensions; /*!< extensions received by endpoint */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + unsigned char certificate_request_context_len; + unsigned char *certificate_request_context; +#endif + + /** TLS 1.3 transform for encrypted handshake messages. */ + mbedtls_ssl_transform *transform_handshake; + union { + unsigned char early[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char handshake[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + unsigned char app[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + } tls13_master_secrets; + + mbedtls_ssl_tls13_handshake_secrets tls13_hs_secrets; +#if defined(MBEDTLS_SSL_EARLY_DATA) + /** TLS 1.3 transform for early data and handshake messages. */ + mbedtls_ssl_transform *transform_earlydata; +#endif +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + /** Asynchronous operation context. This field is meant for use by the + * asynchronous operation callbacks (mbedtls_ssl_config::f_async_sign_start, + * mbedtls_ssl_config::f_async_decrypt_start, + * mbedtls_ssl_config::f_async_resume, mbedtls_ssl_config::f_async_cancel). + * The library does not use it internally. */ + void *user_async_ctx; +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + const unsigned char *sni_name; /*!< raw SNI */ + size_t sni_name_len; /*!< raw SNI len */ +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) + const mbedtls_x509_crt *dn_hints; /*!< acceptable client cert issuers */ +#endif +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ +}; + +typedef struct mbedtls_ssl_hs_buffer mbedtls_ssl_hs_buffer; + +/* + * Representation of decryption/encryption transformations on records + * + * There are the following general types of record transformations: + * - Stream transformations (TLS versions == 1.2 only) + * Transformation adding a MAC and applying a stream-cipher + * to the authenticated message. + * - CBC block cipher transformations ([D]TLS versions == 1.2 only) + * For TLS 1.2, no IV is generated at key extraction time, but every + * encrypted record is explicitly prefixed by the IV with which it was + * encrypted. + * - AEAD transformations ([D]TLS versions == 1.2 only) + * These come in two fundamentally different versions, the first one + * used in TLS 1.2, excluding ChaChaPoly ciphersuites, and the second + * one used for ChaChaPoly ciphersuites in TLS 1.2 as well as for TLS 1.3. + * In the first transformation, the IV to be used for a record is obtained + * as the concatenation of an explicit, static 4-byte IV and the 8-byte + * record sequence number, and explicitly prepending this sequence number + * to the encrypted record. In contrast, in the second transformation + * the IV is obtained by XOR'ing a static IV obtained at key extraction + * time with the 8-byte record sequence number, without prepending the + * latter to the encrypted record. + * + * Additionally, DTLS 1.2 + CID as well as TLS 1.3 use an inner plaintext + * which allows to add flexible length padding and to hide a record's true + * content type. + * + * In addition to type and version, the following parameters are relevant: + * - The symmetric cipher algorithm to be used. + * - The (static) encryption/decryption keys for the cipher. + * - For stream/CBC, the type of message digest to be used. + * - For stream/CBC, (static) encryption/decryption keys for the digest. + * - For AEAD transformations, the size (potentially 0) of an explicit, + * random initialization vector placed in encrypted records. + * - For some transformations (currently AEAD) an implicit IV. It is static + * and (if present) is combined with the explicit IV in a transformation- + * -dependent way (e.g. appending in TLS 1.2 and XOR'ing in TLS 1.3). + * - For stream/CBC, a flag determining the order of encryption and MAC. + * - The details of the transformation depend on the SSL/TLS version. + * - The length of the authentication tag. + * + * The struct below refines this abstract view as follows: + * - The cipher underlying the transformation is managed in + * cipher contexts cipher_ctx_{enc/dec}, which must have the + * same cipher type. The mode of these cipher contexts determines + * the type of the transformation in the sense above: e.g., if + * the type is MBEDTLS_CIPHER_AES_256_CBC resp. MBEDTLS_CIPHER_AES_192_GCM + * then the transformation has type CBC resp. AEAD. + * - The cipher keys are never stored explicitly but + * are maintained within cipher_ctx_{enc/dec}. + * - For stream/CBC transformations, the message digest contexts + * used for the MAC's are stored in md_ctx_{enc/dec}. These contexts + * are unused for AEAD transformations. + * - For stream/CBC transformations, the MAC keys are not stored explicitly + * but maintained within md_ctx_{enc/dec}. + * - The mac_enc and mac_dec fields are unused for EAD transformations. + * - For transformations using an implicit IV maintained within + * the transformation context, its contents are stored within + * iv_{enc/dec}. + * - The value of ivlen indicates the length of the IV. + * This is redundant in case of stream/CBC transformations + * which always use 0 resp. the cipher's block length as the + * IV length, but is needed for AEAD ciphers and may be + * different from the underlying cipher's block length + * in this case. + * - The field fixed_ivlen is nonzero for AEAD transformations only + * and indicates the length of the static part of the IV which is + * constant throughout the communication, and which is stored in + * the first fixed_ivlen bytes of the iv_{enc/dec} arrays. + * - tls_version denotes the 2-byte TLS version + * - For stream/CBC transformations, maclen denotes the length of the + * authentication tag, while taglen is unused and 0. + * - For AEAD transformations, taglen denotes the length of the + * authentication tag, while maclen is unused and 0. + * - For CBC transformations, encrypt_then_mac determines the + * order of encryption and authentication. This field is unused + * in other transformations. + * + */ +struct mbedtls_ssl_transform { + /* + * Session specific crypto layer + */ + size_t minlen; /*!< min. ciphertext length */ + size_t ivlen; /*!< IV length */ + size_t fixed_ivlen; /*!< Fixed part of IV (AEAD) */ + size_t maclen; /*!< MAC(CBC) len */ + size_t taglen; /*!< TAG(AEAD) len */ + + unsigned char iv_enc[16]; /*!< IV (encryption) */ + unsigned char iv_dec[16]; /*!< IV (decryption) */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_svc_key_id_t psa_mac_enc; /*!< MAC (encryption) */ + mbedtls_svc_key_id_t psa_mac_dec; /*!< MAC (decryption) */ + psa_algorithm_t psa_mac_alg; /*!< psa MAC algorithm */ +#else + mbedtls_md_context_t md_ctx_enc; /*!< MAC (encryption) */ + mbedtls_md_context_t md_ctx_dec; /*!< MAC (decryption) */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + int encrypt_then_mac; /*!< flag for EtM activation */ +#endif + +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + + mbedtls_ssl_protocol_version tls_version; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_svc_key_id_t psa_key_enc; /*!< psa encryption key */ + mbedtls_svc_key_id_t psa_key_dec; /*!< psa decryption key */ + psa_algorithm_t psa_alg; /*!< psa algorithm */ +#else + mbedtls_cipher_context_t cipher_ctx_enc; /*!< encryption context */ + mbedtls_cipher_context_t cipher_ctx_dec; /*!< decryption context */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + uint8_t in_cid_len; + uint8_t out_cid_len; + unsigned char in_cid[MBEDTLS_SSL_CID_IN_LEN_MAX]; + unsigned char out_cid[MBEDTLS_SSL_CID_OUT_LEN_MAX]; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) + /* We need the Hello random bytes in order to re-derive keys from the + * Master Secret and other session info, + * see ssl_tls12_populate_transform() */ + unsigned char randbytes[MBEDTLS_SERVER_HELLO_RANDOM_LEN + + MBEDTLS_CLIENT_HELLO_RANDOM_LEN]; + /*!< ServerHello.random+ClientHello.random */ +#endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ +}; + +/* + * Return 1 if the transform uses an AEAD cipher, 0 otherwise. + * Equivalently, return 0 if a separate MAC is used, 1 otherwise. + */ +static inline int mbedtls_ssl_transform_uses_aead( + const mbedtls_ssl_transform *transform) +{ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + return transform->maclen == 0 && transform->taglen != 0; +#else + (void) transform; + return 1; +#endif +} + +/* + * Internal representation of record frames + * + * Instances come in two flavors: + * (1) Encrypted + * These always have data_offset = 0 + * (2) Unencrypted + * These have data_offset set to the amount of + * pre-expansion during record protection. Concretely, + * this is the length of the fixed part of the explicit IV + * used for encryption, or 0 if no explicit IV is used + * (e.g. for stream ciphers). + * + * The reason for the data_offset in the unencrypted case + * is to allow for in-place conversion of an unencrypted to + * an encrypted record. If the offset wasn't included, the + * encrypted content would need to be shifted afterwards to + * make space for the fixed IV. + * + */ +#if MBEDTLS_SSL_CID_OUT_LEN_MAX > MBEDTLS_SSL_CID_IN_LEN_MAX +#define MBEDTLS_SSL_CID_LEN_MAX MBEDTLS_SSL_CID_OUT_LEN_MAX +#else +#define MBEDTLS_SSL_CID_LEN_MAX MBEDTLS_SSL_CID_IN_LEN_MAX +#endif + +typedef struct { + uint8_t ctr[MBEDTLS_SSL_SEQUENCE_NUMBER_LEN]; /* In TLS: The implicit record sequence number. + * In DTLS: The 2-byte epoch followed by + * the 6-byte sequence number. + * This is stored as a raw big endian byte array + * as opposed to a uint64_t because we rarely + * need to perform arithmetic on this, but do + * need it as a Byte array for the purpose of + * MAC computations. */ + uint8_t type; /* The record content type. */ + uint8_t ver[2]; /* SSL/TLS version as present on the wire. + * Convert to internal presentation of versions + * using mbedtls_ssl_read_version() and + * mbedtls_ssl_write_version(). + * Keep wire-format for MAC computations. */ + + unsigned char *buf; /* Memory buffer enclosing the record content */ + size_t buf_len; /* Buffer length */ + size_t data_offset; /* Offset of record content */ + size_t data_len; /* Length of record content */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + uint8_t cid_len; /* Length of the CID (0 if not present) */ + unsigned char cid[MBEDTLS_SSL_CID_LEN_MAX]; /* The CID */ +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ +} mbedtls_record; + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/* + * List of certificate + private key pairs + */ +struct mbedtls_ssl_key_cert { + mbedtls_x509_crt *cert; /*!< cert */ + mbedtls_pk_context *key; /*!< private key */ + mbedtls_ssl_key_cert *next; /*!< next key/cert pair */ +}; +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +/* + * List of handshake messages kept around for resending + */ +struct mbedtls_ssl_flight_item { + unsigned char *p; /*!< message, including handshake headers */ + size_t len; /*!< length of p */ + unsigned char type; /*!< type of the message: handshake or CCS */ + mbedtls_ssl_flight_item *next; /*!< next handshake message(s) */ +}; +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +/** + * \brief Given an SSL context and its associated configuration, write the TLS + * 1.2 specific extensions of the ClientHello message. + * + * \param[in] ssl SSL context + * \param[in] buf Base address of the buffer where to write the extensions + * \param[in] end End address of the buffer where to write the extensions + * \param uses_ec Whether one proposed ciphersuite uses an elliptic curve + * (<> 0) or not ( 0 ). + * \param[out] out_len Length of the data written into the buffer \p buf + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls12_write_client_hello_exts(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + int uses_ec, + size_t *out_len); +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \ + defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + +/** + * \brief Find the preferred hash for a given signature algorithm. + * + * \param[in] ssl SSL context + * \param[in] sig_alg A signature algorithm identifier as defined in the + * TLS 1.2 SignatureAlgorithm enumeration. + * + * \return The preferred hash algorithm for \p sig_alg. It is a hash algorithm + * identifier as defined in the TLS 1.2 HashAlgorithm enumeration. + */ +unsigned int mbedtls_ssl_tls12_get_preferred_hash_for_sig_alg( + mbedtls_ssl_context *ssl, + unsigned int sig_alg); + +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 && + MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ + +/** + * \brief Free referenced items in an SSL transform context and clear + * memory + * + * \param transform SSL transform context + */ +void mbedtls_ssl_transform_free(mbedtls_ssl_transform *transform); + +/** + * \brief Free referenced items in an SSL handshake context and clear + * memory + * + * \param ssl SSL context + */ +void mbedtls_ssl_handshake_free(mbedtls_ssl_context *ssl); + +/* set inbound transform of ssl context */ +void mbedtls_ssl_set_inbound_transform(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform); + +/* set outbound transform of ssl context */ +void mbedtls_ssl_set_outbound_transform(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_handshake_client_step(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_handshake_server_step(mbedtls_ssl_context *ssl); +void mbedtls_ssl_handshake_wrapup(mbedtls_ssl_context *ssl); +static inline void mbedtls_ssl_handshake_set_state(mbedtls_ssl_context *ssl, + mbedtls_ssl_states state) +{ + ssl->state = (int) state; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_send_fatal_handshake_failure(mbedtls_ssl_context *ssl); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_reset_checksum(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_derive_keys(mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_handle_message_type(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_update_handshake_status(mbedtls_ssl_context *ssl); + +/** + * \brief Update record layer + * + * This function roughly separates the implementation + * of the logic of (D)TLS from the implementation + * of the secure transport. + * + * \param ssl The SSL context to use. + * \param update_hs_digest This indicates if the handshake digest + * should be automatically updated in case + * a handshake message is found. + * + * \return 0 or non-zero error code. + * + * \note A clarification on what is called 'record layer' here + * is in order, as many sensible definitions are possible: + * + * The record layer takes as input an untrusted underlying + * transport (stream or datagram) and transforms it into + * a serially multiplexed, secure transport, which + * conceptually provides the following: + * + * (1) Three datagram based, content-agnostic transports + * for handshake, alert and CCS messages. + * (2) One stream- or datagram-based transport + * for application data. + * (3) Functionality for changing the underlying transform + * securing the contents. + * + * The interface to this functionality is given as follows: + * + * a Updating + * [Currently implemented by mbedtls_ssl_read_record] + * + * Check if and on which of the four 'ports' data is pending: + * Nothing, a controlling datagram of type (1), or application + * data (2). In any case data is present, internal buffers + * provide access to the data for the user to process it. + * Consumption of type (1) datagrams is done automatically + * on the next update, invalidating that the internal buffers + * for previous datagrams, while consumption of application + * data (2) is user-controlled. + * + * b Reading of application data + * [Currently manual adaption of ssl->in_offt pointer] + * + * As mentioned in the last paragraph, consumption of data + * is different from the automatic consumption of control + * datagrams (1) because application data is treated as a stream. + * + * c Tracking availability of application data + * [Currently manually through decreasing ssl->in_msglen] + * + * For efficiency and to retain datagram semantics for + * application data in case of DTLS, the record layer + * provides functionality for checking how much application + * data is still available in the internal buffer. + * + * d Changing the transformation securing the communication. + * + * Given an opaque implementation of the record layer in the + * above sense, it should be possible to implement the logic + * of (D)TLS on top of it without the need to know anything + * about the record layer's internals. This is done e.g. + * in all the handshake handling functions, and in the + * application data reading function mbedtls_ssl_read. + * + * \note The above tries to give a conceptual picture of the + * record layer, but the current implementation deviates + * from it in some places. For example, our implementation of + * the update functionality through mbedtls_ssl_read_record + * discards datagrams depending on the current state, which + * wouldn't fall under the record layer's responsibility + * following the above definition. + * + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_read_record(mbedtls_ssl_context *ssl, + unsigned update_hs_digest); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_fetch_input(mbedtls_ssl_context *ssl, size_t nb_want); + +/* + * Write handshake message header + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_start_handshake_msg(mbedtls_ssl_context *ssl, unsigned hs_type, + unsigned char **buf, size_t *buf_len); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_handshake_msg_ext(mbedtls_ssl_context *ssl, + int update_checksum, + int force_flush); +static inline int mbedtls_ssl_write_handshake_msg(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_write_handshake_msg_ext(ssl, 1 /* update checksum */, 1 /* force flush */); +} + +/* + * Write handshake message tail + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_finish_handshake_msg(mbedtls_ssl_context *ssl, + size_t buf_len, size_t msg_len); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_record(mbedtls_ssl_context *ssl, int force_flush); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_flush_output(mbedtls_ssl_context *ssl); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_certificate(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_certificate(mbedtls_ssl_context *ssl); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_change_cipher_spec(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_change_cipher_spec(mbedtls_ssl_context *ssl); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_finished(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_finished(mbedtls_ssl_context *ssl); + +void mbedtls_ssl_optimize_checksum(mbedtls_ssl_context *ssl, + const mbedtls_ssl_ciphersuite_t *ciphersuite_info); + +/* + * Update checksum of handshake messages. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_add_hs_msg_to_checksum(mbedtls_ssl_context *ssl, + unsigned hs_type, + unsigned char const *msg, + size_t msg_len); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_add_hs_hdr_to_checksum(mbedtls_ssl_context *ssl, + unsigned hs_type, + size_t total_hs_len); + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) +#if !defined(MBEDTLS_USE_PSA_CRYPTO) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_psk_derive_premaster(mbedtls_ssl_context *ssl, + mbedtls_key_exchange_type_t key_ex); +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) +#if defined(MBEDTLS_SSL_CLI_C) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_conf_has_static_psk(mbedtls_ssl_config const *conf); +#endif +#if defined(MBEDTLS_USE_PSA_CRYPTO) +/** + * Get the first defined opaque PSK by order of precedence: + * 1. handshake PSK set by \c mbedtls_ssl_set_hs_psk_opaque() in the PSK + * callback + * 2. static PSK configured by \c mbedtls_ssl_conf_psk_opaque() + * Return an opaque PSK + */ +static inline mbedtls_svc_key_id_t mbedtls_ssl_get_opaque_psk( + const mbedtls_ssl_context *ssl) +{ + if (!mbedtls_svc_key_id_is_null(ssl->handshake->psk_opaque)) { + return ssl->handshake->psk_opaque; + } + + if (!mbedtls_svc_key_id_is_null(ssl->conf->psk_opaque)) { + return ssl->conf->psk_opaque; + } + + return MBEDTLS_SVC_KEY_ID_INIT; +} +#else +/** + * Get the first defined PSK by order of precedence: + * 1. handshake PSK set by \c mbedtls_ssl_set_hs_psk() in the PSK callback + * 2. static PSK configured by \c mbedtls_ssl_conf_psk() + * Return a code and update the pair (PSK, PSK length) passed to this function + */ +static inline int mbedtls_ssl_get_psk(const mbedtls_ssl_context *ssl, + const unsigned char **psk, size_t *psk_len) +{ + if (ssl->handshake->psk != NULL && ssl->handshake->psk_len > 0) { + *psk = ssl->handshake->psk; + *psk_len = ssl->handshake->psk_len; + } else if (ssl->conf->psk != NULL && ssl->conf->psk_len > 0) { + *psk = ssl->conf->psk; + *psk_len = ssl->conf->psk_len; + } else { + *psk = NULL; + *psk_len = 0; + return MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED; + } + + return 0; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ + +#if defined(MBEDTLS_PK_C) +unsigned char mbedtls_ssl_sig_from_pk(mbedtls_pk_context *pk); +unsigned char mbedtls_ssl_sig_from_pk_alg(mbedtls_pk_type_t type); +mbedtls_pk_type_t mbedtls_ssl_pk_alg_from_sig(unsigned char sig); +#endif + +mbedtls_md_type_t mbedtls_ssl_md_alg_from_hash(unsigned char hash); +unsigned char mbedtls_ssl_hash_from_md_alg(int md); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_set_calc_verify_md(mbedtls_ssl_context *ssl, int md); +#endif + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_check_curve_tls_id(const mbedtls_ssl_context *ssl, uint16_t tls_id); +#if defined(MBEDTLS_ECP_C) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_check_curve(const mbedtls_ssl_context *ssl, mbedtls_ecp_group_id grp_id); +#endif + +/** + * \brief Return PSA EC info for the specified TLS ID. + * + * \param tls_id The TLS ID to look for + * \param family If the TLD ID is supported, then proper \c psa_ecc_family_t + * value is returned here. Can be NULL. + * \param bits If the TLD ID is supported, then proper bit size is returned + * here. Can be NULL. + * \return PSA_SUCCESS if the TLS ID is supported, + * PSA_ERROR_NOT_SUPPORTED otherwise + * + * \note If either \c family or \c bits parameters are NULL, then + * the corresponding value is not returned. + * The function can be called with both parameters as NULL + * simply to check if a specific TLS ID is supported. + */ +int mbedtls_ssl_get_psa_curve_info_from_tls_id(uint16_t tls_id, + psa_ecc_family_t *family, + size_t *bits); + +/** + * \brief Return \c mbedtls_ecp_group_id for the specified TLS ID. + * + * \param tls_id The TLS ID to look for + * \return Proper \c mbedtls_ecp_group_id if the TLS ID is supported, + * or MBEDTLS_ECP_DP_NONE otherwise + */ +mbedtls_ecp_group_id mbedtls_ssl_get_ecp_group_id_from_tls_id(uint16_t tls_id); + +/** + * \brief Return TLS ID for the specified \c mbedtls_ecp_group_id. + * + * \param grp_id The \c mbedtls_ecp_group_id ID to look for + * \return Proper TLS ID if the \c mbedtls_ecp_group_id is supported, + * or 0 otherwise + */ +uint16_t mbedtls_ssl_get_tls_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id); + +#if defined(MBEDTLS_DEBUG_C) +/** + * \brief Return EC's name for the specified TLS ID. + * + * \param tls_id The TLS ID to look for + * \return A pointer to a const string with the proper name. If TLS + * ID is not supported, a NULL pointer is returned instead. + */ +const char *mbedtls_ssl_get_curve_name_from_tls_id(uint16_t tls_id); +#endif + +#if defined(MBEDTLS_SSL_DTLS_SRTP) +static inline mbedtls_ssl_srtp_profile mbedtls_ssl_check_srtp_profile_value + (const uint16_t srtp_profile_value) +{ + switch (srtp_profile_value) { + case MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_80: + case MBEDTLS_TLS_SRTP_AES128_CM_HMAC_SHA1_32: + case MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_80: + case MBEDTLS_TLS_SRTP_NULL_HMAC_SHA1_32: + return srtp_profile_value; + default: break; + } + return MBEDTLS_TLS_SRTP_UNSET; +} +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +static inline mbedtls_pk_context *mbedtls_ssl_own_key(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_key_cert *key_cert; + + if (ssl->handshake != NULL && ssl->handshake->key_cert != NULL) { + key_cert = ssl->handshake->key_cert; + } else { + key_cert = ssl->conf->key_cert; + } + + return key_cert == NULL ? NULL : key_cert->key; +} + +static inline mbedtls_x509_crt *mbedtls_ssl_own_cert(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_key_cert *key_cert; + + if (ssl->handshake != NULL && ssl->handshake->key_cert != NULL) { + key_cert = ssl->handshake->key_cert; + } else { + key_cert = ssl->conf->key_cert; + } + + return key_cert == NULL ? NULL : key_cert->cert; +} + +/* + * Check usage of a certificate wrt extensions: + * keyUsage, extendedKeyUsage (later), and nSCertType (later). + * + * Warning: cert_endpoint is the endpoint of the cert (ie, of our peer when we + * check a cert we received from them)! + * + * Return 0 if everything is OK, -1 if not. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_check_cert_usage(const mbedtls_x509_crt *cert, + const mbedtls_ssl_ciphersuite_t *ciphersuite, + int cert_endpoint, + uint32_t *flags); +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +void mbedtls_ssl_write_version(unsigned char version[2], int transport, + mbedtls_ssl_protocol_version tls_version); +uint16_t mbedtls_ssl_read_version(const unsigned char version[2], + int transport); + +static inline size_t mbedtls_ssl_in_hdr_len(const mbedtls_ssl_context *ssl) +{ +#if !defined(MBEDTLS_SSL_PROTO_DTLS) + ((void) ssl); +#endif + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return 13; + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + { + return 5; + } +} + +static inline size_t mbedtls_ssl_out_hdr_len(const mbedtls_ssl_context *ssl) +{ + return (size_t) (ssl->out_iv - ssl->out_hdr); +} + +static inline size_t mbedtls_ssl_hs_hdr_len(const mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return 12; + } +#else + ((void) ssl); +#endif + return 4; +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +void mbedtls_ssl_send_flight_completed(mbedtls_ssl_context *ssl); +void mbedtls_ssl_recv_flight_completed(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_resend(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_flight_transmit(mbedtls_ssl_context *ssl); +#endif + +/* Visible for testing purposes only */ +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_dtls_replay_check(mbedtls_ssl_context const *ssl); +void mbedtls_ssl_dtls_replay_update(mbedtls_ssl_context *ssl); +#endif + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_session_copy(mbedtls_ssl_session *dst, + const mbedtls_ssl_session *src); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +/* The hash buffer must have at least MBEDTLS_MD_MAX_SIZE bytes of length. */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_get_key_exchange_md_tls1_2(mbedtls_ssl_context *ssl, + unsigned char *hash, size_t *hashlen, + unsigned char *data, size_t data_len, + mbedtls_md_type_t md_alg); +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#ifdef __cplusplus +} +#endif + +void mbedtls_ssl_transform_init(mbedtls_ssl_transform *transform); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_encrypt_buf(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform, + mbedtls_record *rec, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_decrypt_buf(mbedtls_ssl_context const *ssl, + mbedtls_ssl_transform *transform, + mbedtls_record *rec); + +/* Length of the "epoch" field in the record header */ +static inline size_t mbedtls_ssl_ep_len(const mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return 2; + } +#else + ((void) ssl); +#endif + return 0; +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_resend_hello_request(mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +void mbedtls_ssl_set_timer(mbedtls_ssl_context *ssl, uint32_t millisecs); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_check_timer(mbedtls_ssl_context *ssl); + +void mbedtls_ssl_reset_in_out_pointers(mbedtls_ssl_context *ssl); +void mbedtls_ssl_update_out_pointers(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform); +void mbedtls_ssl_update_in_pointers(mbedtls_ssl_context *ssl); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_session_reset_int(mbedtls_ssl_context *ssl, int partial); +void mbedtls_ssl_session_reset_msg_layer(mbedtls_ssl_context *ssl, + int partial); + +/* + * Send pending alert + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_handle_pending_alert(mbedtls_ssl_context *ssl); + +/* + * Set pending fatal alert flag. + */ +void mbedtls_ssl_pend_fatal_alert(mbedtls_ssl_context *ssl, + unsigned char alert_type, + int alert_reason); + +/* Alias of mbedtls_ssl_pend_fatal_alert */ +#define MBEDTLS_SSL_PEND_FATAL_ALERT(type, user_return_value) \ + mbedtls_ssl_pend_fatal_alert(ssl, type, user_return_value) + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) +void mbedtls_ssl_dtls_replay_reset(mbedtls_ssl_context *ssl); +#endif + +void mbedtls_ssl_handshake_wrapup_free_hs_transform(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_start_renegotiation(mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +size_t mbedtls_ssl_get_current_mtu(const mbedtls_ssl_context *ssl); +void mbedtls_ssl_buffering_free(mbedtls_ssl_context *ssl); +void mbedtls_ssl_flight_free(mbedtls_ssl_flight_item *flight); +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +/** + * ssl utils functions for checking configuration. + */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +static inline int mbedtls_ssl_conf_is_tls13_only(const mbedtls_ssl_config *conf) +{ + return conf->min_tls_version == MBEDTLS_SSL_VERSION_TLS1_3 && + conf->max_tls_version == MBEDTLS_SSL_VERSION_TLS1_3; +} + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +static inline int mbedtls_ssl_conf_is_tls12_only(const mbedtls_ssl_config *conf) +{ + return conf->min_tls_version == MBEDTLS_SSL_VERSION_TLS1_2 && + conf->max_tls_version == MBEDTLS_SSL_VERSION_TLS1_2; +} + +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +static inline int mbedtls_ssl_conf_is_tls13_enabled(const mbedtls_ssl_config *conf) +{ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + return conf->min_tls_version <= MBEDTLS_SSL_VERSION_TLS1_3 && + conf->max_tls_version >= MBEDTLS_SSL_VERSION_TLS1_3; +#else + ((void) conf); + return 0; +#endif +} + +static inline int mbedtls_ssl_conf_is_tls12_enabled(const mbedtls_ssl_config *conf) +{ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + return conf->min_tls_version <= MBEDTLS_SSL_VERSION_TLS1_2 && + conf->max_tls_version >= MBEDTLS_SSL_VERSION_TLS1_2; +#else + ((void) conf); + return 0; +#endif +} + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_SSL_PROTO_TLS1_3) +static inline int mbedtls_ssl_conf_is_hybrid_tls12_tls13(const mbedtls_ssl_config *conf) +{ + return conf->min_tls_version == MBEDTLS_SSL_VERSION_TLS1_2 && + conf->max_tls_version == MBEDTLS_SSL_VERSION_TLS1_3; +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 && MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +extern const uint8_t mbedtls_ssl_tls13_hello_retry_request_magic[ + MBEDTLS_SERVER_HELLO_RANDOM_LEN]; +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_process_finished_message(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_write_finished_message(mbedtls_ssl_context *ssl); +void mbedtls_ssl_tls13_handshake_wrapup(mbedtls_ssl_context *ssl); + +/** + * \brief Given an SSL context and its associated configuration, write the TLS + * 1.3 specific extensions of the ClientHello message. + * + * \param[in] ssl SSL context + * \param[in] buf Base address of the buffer where to write the extensions + * \param[in] end End address of the buffer where to write the extensions + * \param[out] out_len Length of the data written into the buffer \p buf + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_write_client_hello_exts(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len); + +/** + * \brief TLS 1.3 client side state machine entry + * + * \param ssl SSL context + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl); + +/** + * \brief TLS 1.3 server side state machine entry + * + * \param ssl SSL context + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_handshake_server_step(mbedtls_ssl_context *ssl); + + +/* + * Helper functions around key exchange modes. + */ +static inline unsigned mbedtls_ssl_conf_tls13_check_kex_modes(mbedtls_ssl_context *ssl, + int kex_mode_mask) +{ + return (ssl->conf->tls13_kex_modes & kex_mode_mask) != 0; +} + +static inline int mbedtls_ssl_conf_tls13_psk_enabled(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_conf_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK); +} + +static inline int mbedtls_ssl_conf_tls13_psk_ephemeral_enabled(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_conf_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL); +} + +static inline int mbedtls_ssl_conf_tls13_ephemeral_enabled(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_conf_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL); +} + +static inline int mbedtls_ssl_conf_tls13_some_ephemeral_enabled(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_conf_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ALL); +} + +static inline int mbedtls_ssl_conf_tls13_some_psk_enabled(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_conf_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL); +} + +#if defined(MBEDTLS_SSL_SRV_C) && \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +/** + * Given a list of key exchange modes, check if at least one of them is + * supported. + * + * \param[in] ssl SSL context + * \param kex_modes_mask Mask of the key exchange modes to check + * + * \return 0 if at least one of the key exchange modes is supported, + * !=0 otherwise. + */ +static inline unsigned mbedtls_ssl_tls13_check_kex_modes(mbedtls_ssl_context *ssl, + int kex_modes_mask) +{ + return (ssl->handshake->tls13_kex_modes & kex_modes_mask) == 0; +} + +static inline int mbedtls_ssl_tls13_psk_enabled(mbedtls_ssl_context *ssl) +{ + return !mbedtls_ssl_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK); +} + +static inline int mbedtls_ssl_tls13_psk_ephemeral_enabled( + mbedtls_ssl_context *ssl) +{ + return !mbedtls_ssl_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL); +} + +static inline int mbedtls_ssl_tls13_ephemeral_enabled(mbedtls_ssl_context *ssl) +{ + return !mbedtls_ssl_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL); +} + +static inline int mbedtls_ssl_tls13_some_ephemeral_enabled(mbedtls_ssl_context *ssl) +{ + return !mbedtls_ssl_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ALL); +} + +static inline int mbedtls_ssl_tls13_some_psk_enabled(mbedtls_ssl_context *ssl) +{ + return !mbedtls_ssl_tls13_check_kex_modes(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL); +} +#endif /* MBEDTLS_SSL_SRV_C && + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + +/* + * Helper functions for extensions checking. + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_check_received_extension( + mbedtls_ssl_context *ssl, + int hs_msg_type, + unsigned int received_extension_type, + uint32_t hs_msg_allowed_extensions_mask); + +static inline void mbedtls_ssl_tls13_set_hs_sent_ext_mask( + mbedtls_ssl_context *ssl, unsigned int extension_type) +{ + ssl->handshake->sent_extensions |= + mbedtls_ssl_get_extension_mask(extension_type); +} + +/* + * Helper functions to check the selected key exchange mode. + */ +static inline int mbedtls_ssl_tls13_key_exchange_mode_check( + mbedtls_ssl_context *ssl, int kex_mask) +{ + return (ssl->handshake->key_exchange_mode & kex_mask) != 0; +} + +static inline int mbedtls_ssl_tls13_key_exchange_mode_with_psk( + mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_tls13_key_exchange_mode_check(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL); +} + +static inline int mbedtls_ssl_tls13_key_exchange_mode_with_ephemeral( + mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_tls13_key_exchange_mode_check(ssl, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ALL); +} + +/* + * Fetch TLS 1.3 handshake message header + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_fetch_handshake_msg(mbedtls_ssl_context *ssl, + unsigned hs_type, + unsigned char **buf, + size_t *buf_len); + +/* + * Handler of TLS 1.3 server certificate message + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_process_certificate(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +/* + * Handler of TLS 1.3 write Certificate message + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_write_certificate(mbedtls_ssl_context *ssl); + +/* + * Handler of TLS 1.3 write Certificate Verify message + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_write_certificate_verify(mbedtls_ssl_context *ssl); + +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +/* + * Generic handler of Certificate Verify + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_process_certificate_verify(mbedtls_ssl_context *ssl); + +/* + * Write of dummy-CCS's for middlebox compatibility + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_write_change_cipher_spec(mbedtls_ssl_context *ssl); + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_reset_transcript_for_hrr(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_ECDH_C) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_generate_and_write_ecdh_key_exchange( + mbedtls_ssl_context *ssl, + uint16_t named_group, + unsigned char *buf, + unsigned char *end, + size_t *out_len); +#endif /* MBEDTLS_ECDH_C */ + +#if defined(MBEDTLS_SSL_EARLY_DATA) +int mbedtls_ssl_tls13_write_early_data_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *out_len); +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +/* + * Write Signature Algorithm extension + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_sig_alg_ext(mbedtls_ssl_context *ssl, unsigned char *buf, + const unsigned char *end, size_t *out_len); +/* + * Parse TLS Signature Algorithm extension + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_sig_alg_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end); +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +/* Get handshake transcript */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_get_handshake_transcript(mbedtls_ssl_context *ssl, + const mbedtls_md_type_t md, + unsigned char *dst, + size_t dst_len, + size_t *olen); + +/* + * Return supported groups. + * + * In future, invocations can be changed to ssl->conf->group_list + * when mbedtls_ssl_conf_curves() is deleted. + * + * ssl->handshake->group_list is either a translation of curve_list to IANA TLS group + * identifiers when mbedtls_ssl_conf_curves() has been used, or a pointer to + * ssl->conf->group_list when mbedtls_ssl_conf_groups() has been more recently invoked. + * + */ +static inline const void *mbedtls_ssl_get_groups(const mbedtls_ssl_context *ssl) +{ + #if defined(MBEDTLS_DEPRECATED_REMOVED) || !defined(MBEDTLS_ECP_C) + return ssl->conf->group_list; + #else + if ((ssl->handshake != NULL) && (ssl->handshake->group_list != NULL)) { + return ssl->handshake->group_list; + } else { + return ssl->conf->group_list; + } + #endif +} + +/* + * Helper functions for NamedGroup. + */ +static inline int mbedtls_ssl_tls12_named_group_is_ecdhe(uint16_t named_group) +{ + /* + * RFC 8422 section 5.1.1 + */ + return named_group == MBEDTLS_SSL_IANA_TLS_GROUP_X25519 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_BP256R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_BP384R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_BP512R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_X448 || + /* Below deprecated curves should be removed with notice to users */ + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP192K1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP192R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP224K1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP224R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP256K1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP521R1; +} + +static inline int mbedtls_ssl_tls13_named_group_is_ecdhe(uint16_t named_group) +{ + return named_group == MBEDTLS_SSL_IANA_TLS_GROUP_X25519 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_SECP521R1 || + named_group == MBEDTLS_SSL_IANA_TLS_GROUP_X448; +} + +static inline int mbedtls_ssl_tls13_named_group_is_dhe(uint16_t named_group) +{ + return named_group >= MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE2048 && + named_group <= MBEDTLS_SSL_IANA_TLS_GROUP_FFDHE8192; +} + +static inline int mbedtls_ssl_named_group_is_offered( + const mbedtls_ssl_context *ssl, uint16_t named_group) +{ + const uint16_t *group_list = mbedtls_ssl_get_groups(ssl); + + if (group_list == NULL) { + return 0; + } + + for (; *group_list != 0; group_list++) { + if (*group_list == named_group) { + return 1; + } + } + + return 0; +} + +static inline int mbedtls_ssl_named_group_is_supported(uint16_t named_group) +{ +#if defined(MBEDTLS_ECDH_C) + if (mbedtls_ssl_tls13_named_group_is_ecdhe(named_group)) { + if (mbedtls_ssl_get_ecp_group_id_from_tls_id(named_group) != + MBEDTLS_ECP_DP_NONE) { + return 1; + } + } +#else + ((void) named_group); +#endif /* MBEDTLS_ECDH_C */ + return 0; +} + +/* + * Return supported signature algorithms. + * + * In future, invocations can be changed to ssl->conf->sig_algs when + * mbedtls_ssl_conf_sig_hashes() is deleted. + * + * ssl->handshake->sig_algs is either a translation of sig_hashes to IANA TLS + * signature algorithm identifiers when mbedtls_ssl_conf_sig_hashes() has been + * used, or a pointer to ssl->conf->sig_algs when mbedtls_ssl_conf_sig_algs() has + * been more recently invoked. + * + */ +static inline const void *mbedtls_ssl_get_sig_algs( + const mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + if (ssl->handshake != NULL && + ssl->handshake->sig_algs_heap_allocated == 1 && + ssl->handshake->sig_algs != NULL) { + return ssl->handshake->sig_algs; + } +#endif + return ssl->conf->sig_algs; + +#else /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + + ((void) ssl); + return NULL; +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +static inline int mbedtls_ssl_sig_alg_is_received(const mbedtls_ssl_context *ssl, + uint16_t own_sig_alg) +{ + const uint16_t *sig_alg = ssl->handshake->received_sig_algs; + if (sig_alg == NULL) { + return 0; + } + + for (; *sig_alg != MBEDTLS_TLS_SIG_NONE; sig_alg++) { + if (*sig_alg == own_sig_alg) { + return 1; + } + } + return 0; +} + +static inline int mbedtls_ssl_tls13_sig_alg_for_cert_verify_is_supported( + const uint16_t sig_alg) +{ + switch (sig_alg) { +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) +#if defined(PSA_WANT_ALG_SHA_256) && defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256: + break; +#endif /* PSA_WANT_ALG_SHA_256 && MBEDTLS_ECP_DP_SECP256R1_ENABLED */ +#if defined(PSA_WANT_ALG_SHA_384) && defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP384R1_SHA384: + break; +#endif /* PSA_WANT_ALG_SHA_384 && MBEDTLS_ECP_DP_SECP384R1_ENABLED */ +#if defined(PSA_WANT_ALG_SHA_512) && defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP521R1_SHA512: + break; +#endif /* PSA_WANT_ALG_SHA_512 && MBEDTLS_ECP_DP_SECP521R1_ENABLED */ +#endif /* MBEDTLS_PK_CAN_ECDSA_SOME */ + +#if defined(MBEDTLS_PKCS1_V21) +#if defined(PSA_WANT_ALG_SHA_256) + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256: + break; +#endif /* PSA_WANT_ALG_SHA_256 */ +#if defined(PSA_WANT_ALG_SHA_384) + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384: + break; +#endif /* PSA_WANT_ALG_SHA_384 */ +#if defined(PSA_WANT_ALG_SHA_512) + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512: + break; +#endif /* PSA_WANT_ALG_SHA_512 */ +#endif /* MBEDTLS_PKCS1_V21 */ + default: + return 0; + } + return 1; + +} + +static inline int mbedtls_ssl_tls13_sig_alg_is_supported( + const uint16_t sig_alg) +{ + switch (sig_alg) { +#if defined(MBEDTLS_PKCS1_V15) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256: + break; +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA384: + break; +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA512: + break; +#endif /* MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_PKCS1_V15 */ + default: + return mbedtls_ssl_tls13_sig_alg_for_cert_verify_is_supported( + sig_alg); + } + return 1; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_check_sig_alg_cert_key_match(uint16_t sig_alg, + mbedtls_pk_context *key); +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +static inline int mbedtls_ssl_sig_alg_is_offered(const mbedtls_ssl_context *ssl, + uint16_t proposed_sig_alg) +{ + const uint16_t *sig_alg = mbedtls_ssl_get_sig_algs(ssl); + if (sig_alg == NULL) { + return 0; + } + + for (; *sig_alg != MBEDTLS_TLS_SIG_NONE; sig_alg++) { + if (*sig_alg == proposed_sig_alg) { + return 1; + } + } + return 0; +} + +static inline int mbedtls_ssl_get_pk_type_and_md_alg_from_sig_alg( + uint16_t sig_alg, mbedtls_pk_type_t *pk_type, mbedtls_md_type_t *md_alg) +{ + *pk_type = mbedtls_ssl_pk_alg_from_sig(sig_alg & 0xff); + *md_alg = mbedtls_ssl_md_alg_from_hash((sig_alg >> 8) & 0xff); + + if (*pk_type != MBEDTLS_PK_NONE && *md_alg != MBEDTLS_MD_NONE) { + return 0; + } + + switch (sig_alg) { +#if defined(MBEDTLS_PKCS1_V21) +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256: + *md_alg = MBEDTLS_MD_SHA256; + *pk_type = MBEDTLS_PK_RSASSA_PSS; + break; +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384: + *md_alg = MBEDTLS_MD_SHA384; + *pk_type = MBEDTLS_PK_RSASSA_PSS; + break; +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512: + *md_alg = MBEDTLS_MD_SHA512; + *pk_type = MBEDTLS_PK_RSASSA_PSS; + break; +#endif /* MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ +#endif /* MBEDTLS_PKCS1_V21 */ + default: + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + return 0; +} + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +static inline int mbedtls_ssl_tls12_sig_alg_is_supported( + const uint16_t sig_alg) +{ + /* High byte is hash */ + unsigned char hash = MBEDTLS_BYTE_1(sig_alg); + unsigned char sig = MBEDTLS_BYTE_0(sig_alg); + + switch (hash) { +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_MD5: + break; +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA1: + break; +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA224: + break; +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA256: + break; +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA384: + break; +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA512: + break; +#endif + + default: + return 0; + } + + switch (sig) { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_SSL_SIG_RSA: + break; +#endif + +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + case MBEDTLS_SSL_SIG_ECDSA: + break; +#endif + + default: + return 0; + } + + return 1; +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +static inline int mbedtls_ssl_sig_alg_is_supported( + const mbedtls_ssl_context *ssl, + const uint16_t sig_alg) +{ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2) { + return mbedtls_ssl_tls12_sig_alg_is_supported(sig_alg); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + return mbedtls_ssl_tls13_sig_alg_is_supported(sig_alg); + } +#endif + ((void) ssl); + ((void) sig_alg); + return 0; +} +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) +/* Corresponding PSA algorithm for MBEDTLS_CIPHER_NULL. + * Same value is used for PSA_ALG_CATEGORY_CIPHER, hence it is + * guaranteed to not be a valid PSA algorithm identifier. + */ +#define MBEDTLS_SSL_NULL_CIPHER 0x04000000 + +/** + * \brief Translate mbedtls cipher type/taglen pair to psa: + * algorithm, key type and key size. + * + * \param mbedtls_cipher_type [in] given mbedtls cipher type + * \param taglen [in] given tag length + * 0 - default tag length + * \param alg [out] corresponding PSA alg + * There is no corresponding PSA + * alg for MBEDTLS_CIPHER_NULL, so + * in this case MBEDTLS_SSL_NULL_CIPHER + * is returned via this parameter + * \param key_type [out] corresponding PSA key type + * \param key_size [out] corresponding PSA key size + * + * \return PSA_SUCCESS on success or PSA_ERROR_NOT_SUPPORTED if + * conversion is not supported. + */ +psa_status_t mbedtls_ssl_cipher_to_psa(mbedtls_cipher_type_t mbedtls_cipher_type, + size_t taglen, + psa_algorithm_t *alg, + psa_key_type_t *key_type, + size_t *key_size); + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/** + * \brief Convert given PSA status to mbedtls error code. + * + * \param status [in] given PSA status + * + * \return corresponding mbedtls error code + */ +static inline MBEDTLS_DEPRECATED int psa_ssl_status_to_mbedtls(psa_status_t status) +{ + switch (status) { + case PSA_SUCCESS: + return 0; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + case PSA_ERROR_INVALID_SIGNATURE: + return MBEDTLS_ERR_SSL_INVALID_MAC; + case PSA_ERROR_INVALID_ARGUMENT: + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + case PSA_ERROR_BAD_STATE: + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + default: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } +} +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \ + defined(MBEDTLS_USE_PSA_CRYPTO) + +typedef enum { + MBEDTLS_ECJPAKE_ROUND_ONE, + MBEDTLS_ECJPAKE_ROUND_TWO +} mbedtls_ecjpake_rounds_t; + +/** + * \brief Parse the provided input buffer for getting the first round + * of key exchange. This code is common between server and client + * + * \param pake_ctx [in] the PAKE's operation/context structure + * \param buf [in] input buffer to parse + * \param len [in] length of the input buffer + * \param round [in] either MBEDTLS_ECJPAKE_ROUND_ONE or + * MBEDTLS_ECJPAKE_ROUND_TWO + * + * \return 0 on success or a negative error code in case of failure + */ +int mbedtls_psa_ecjpake_read_round( + psa_pake_operation_t *pake_ctx, + const unsigned char *buf, + size_t len, mbedtls_ecjpake_rounds_t round); + +/** + * \brief Write the first round of key exchange into the provided output + * buffer. This code is common between server and client + * + * \param pake_ctx [in] the PAKE's operation/context structure + * \param buf [out] the output buffer in which data will be written to + * \param len [in] length of the output buffer + * \param olen [out] the length of the data really written on the buffer + * \param round [in] either MBEDTLS_ECJPAKE_ROUND_ONE or + * MBEDTLS_ECJPAKE_ROUND_TWO + * + * \return 0 on success or a negative error code in case of failure + */ +int mbedtls_psa_ecjpake_write_round( + psa_pake_operation_t *pake_ctx, + unsigned char *buf, + size_t len, size_t *olen, + mbedtls_ecjpake_rounds_t round); + +#endif //MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED && MBEDTLS_USE_PSA_CRYPTO + +/** + * \brief TLS record protection modes + */ +typedef enum { + MBEDTLS_SSL_MODE_STREAM = 0, + MBEDTLS_SSL_MODE_CBC, + MBEDTLS_SSL_MODE_CBC_ETM, + MBEDTLS_SSL_MODE_AEAD +} mbedtls_ssl_mode_t; + +mbedtls_ssl_mode_t mbedtls_ssl_get_mode_from_transform( + const mbedtls_ssl_transform *transform); + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) +mbedtls_ssl_mode_t mbedtls_ssl_get_mode_from_ciphersuite( + int encrypt_then_mac, + const mbedtls_ssl_ciphersuite_t *suite); +#else +mbedtls_ssl_mode_t mbedtls_ssl_get_mode_from_ciphersuite( + const mbedtls_ssl_ciphersuite_t *suite); +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + +#if defined(MBEDTLS_ECDH_C) + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_read_public_ecdhe_share(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t buf_len); + +#endif /* MBEDTLS_ECDH_C */ + +static inline int mbedtls_ssl_tls13_cipher_suite_is_offered( + mbedtls_ssl_context *ssl, int cipher_suite) +{ + const int *ciphersuite_list = ssl->conf->ciphersuite_list; + + /* Check whether we have offered this ciphersuite */ + for (size_t i = 0; ciphersuite_list[i] != 0; i++) { + if (ciphersuite_list[i] == cipher_suite) { + return 1; + } + } + return 0; +} + +/** + * \brief Validate cipher suite against config in SSL context. + * + * \param ssl SSL context + * \param suite_info Cipher suite to validate + * \param min_tls_version Minimal TLS version to accept a cipher suite + * \param max_tls_version Maximal TLS version to accept a cipher suite + * + * \return 0 if valid, negative value otherwise. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_validate_ciphersuite( + const mbedtls_ssl_context *ssl, + const mbedtls_ssl_ciphersuite_t *suite_info, + mbedtls_ssl_protocol_version min_tls_version, + mbedtls_ssl_protocol_version max_tls_version); + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_server_name_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end); +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) +#define MBEDTLS_SSL_RECORD_SIZE_LIMIT_EXTENSION_DATA_LENGTH (2) +#define MBEDTLS_SSL_RECORD_SIZE_LIMIT_MIN (64) + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_parse_record_size_limit_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end); +#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */ + +#if defined(MBEDTLS_SSL_ALPN) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_alpn_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end); + + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_write_alpn_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len); +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_TEST_HOOKS) +int mbedtls_ssl_check_dtls_clihlo_cookie( + mbedtls_ssl_context *ssl, + const unsigned char *cli_id, size_t cli_id_len, + const unsigned char *in, size_t in_len, + unsigned char *obuf, size_t buf_len, size_t *olen); +#endif + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +/** + * \brief Given an SSL context and its associated configuration, write the TLS + * 1.3 specific Pre-Shared key extension. + * + * \param[in] ssl SSL context + * \param[in] buf Base address of the buffer where to write the extension + * \param[in] end End address of the buffer where to write the extension + * \param[out] out_len Length in bytes of the Pre-Shared key extension: data + * written into the buffer \p buf by this function plus + * the length of the binders to be written. + * \param[out] binders_len Length of the binders to be written at the end of + * the extension. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_write_identities_of_pre_shared_key_ext( + mbedtls_ssl_context *ssl, + unsigned char *buf, unsigned char *end, + size_t *out_len, size_t *binders_len); + +/** + * \brief Given an SSL context and its associated configuration, write the TLS + * 1.3 specific Pre-Shared key extension binders at the end of the + * ClientHello. + * + * \param[in] ssl SSL context + * \param[in] buf Base address of the buffer where to write the binders + * \param[in] end End address of the buffer where to write the binders + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_write_binders_of_pre_shared_key_ext( + mbedtls_ssl_context *ssl, + unsigned char *buf, unsigned char *end); +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + defined(MBEDTLS_SSL_CLI_C) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_session_set_hostname(mbedtls_ssl_session *session, + const char *hostname); +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +static inline unsigned int mbedtls_ssl_session_get_ticket_flags( + mbedtls_ssl_session *session, unsigned int flags) +{ + return session->ticket_flags & + (flags & MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK); +} + +static inline void mbedtls_ssl_session_set_ticket_flags( + mbedtls_ssl_session *session, unsigned int flags) +{ + session->ticket_flags |= (flags & MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK); +} + +static inline void mbedtls_ssl_session_clear_ticket_flags( + mbedtls_ssl_session *session, unsigned int flags) +{ + session->ticket_flags &= ~(flags & MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK); +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_PROTO_TLS1_3) +int mbedtls_ssl_tls13_finalize_client_hello(mbedtls_ssl_context *ssl); +#endif + +#endif /* ssl_misc.h */ diff --git a/r5dev/thirdparty/mbedtls/ssl_msg.c b/r5dev/thirdparty/mbedtls/ssl_msg.c new file mode 100644 index 00000000..18c19f93 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_msg.c @@ -0,0 +1,5917 @@ +/* + * Generic SSL/TLS messaging layer functions + * (record layer + retransmission state machine) + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * http://www.ietf.org/rfc/rfc2246.txt + * http://www.ietf.org/rfc/rfc4346.txt + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_TLS_C) + +#include "mbedtls/platform.h" + +#include "mbedtls/ssl.h" +#include "ssl_misc.h" +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/version.h" +#include "constant_time_internal.h" +#include "mbedtls/constant_time.h" + +#include + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "mbedtls/psa_util.h" +#include "psa/crypto.h" +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#include "mbedtls/oid.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) +#endif + +static uint32_t ssl_get_hs_total_len(mbedtls_ssl_context const *ssl); + +/* + * Start a timer. + * Passing millisecs = 0 cancels a running timer. + */ +void mbedtls_ssl_set_timer(mbedtls_ssl_context *ssl, uint32_t millisecs) +{ + if (ssl->f_set_timer == NULL) { + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("set_timer to %d ms", (int) millisecs)); + ssl->f_set_timer(ssl->p_timer, millisecs / 4, millisecs); +} + +/* + * Return -1 is timer is expired, 0 if it isn't. + */ +int mbedtls_ssl_check_timer(mbedtls_ssl_context *ssl) +{ + if (ssl->f_get_timer == NULL) { + return 0; + } + + if (ssl->f_get_timer(ssl->p_timer) == 2) { + MBEDTLS_SSL_DEBUG_MSG(3, ("timer expired")); + return -1; + } + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_record_header(mbedtls_ssl_context const *ssl, + unsigned char *buf, + size_t len, + mbedtls_record *rec); + +int mbedtls_ssl_check_record(mbedtls_ssl_context const *ssl, + unsigned char *buf, + size_t buflen) +{ + int ret = 0; + MBEDTLS_SSL_DEBUG_MSG(1, ("=> mbedtls_ssl_check_record")); + MBEDTLS_SSL_DEBUG_BUF(3, "record buffer", buf, buflen); + + /* We don't support record checking in TLS because + * there doesn't seem to be a usecase for it. + */ + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_STREAM) { + ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + goto exit; + } +#if defined(MBEDTLS_SSL_PROTO_DTLS) + else { + mbedtls_record rec; + + ret = ssl_parse_record_header(ssl, buf, buflen, &rec); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(3, "ssl_parse_record_header", ret); + goto exit; + } + + if (ssl->transform_in != NULL) { + ret = mbedtls_ssl_decrypt_buf(ssl, ssl->transform_in, &rec); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(3, "mbedtls_ssl_decrypt_buf", ret); + goto exit; + } + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +exit: + /* On success, we have decrypted the buffer in-place, so make + * sure we don't leak any plaintext data. */ + mbedtls_platform_zeroize(buf, buflen); + + /* For the purpose of this API, treat messages with unexpected CID + * as well as such from future epochs as unexpected. */ + if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_CID || + ret == MBEDTLS_ERR_SSL_EARLY_MESSAGE) { + ret = MBEDTLS_ERR_SSL_UNEXPECTED_RECORD; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("<= mbedtls_ssl_check_record")); + return ret; +} + +#define SSL_DONT_FORCE_FLUSH 0 +#define SSL_FORCE_FLUSH 1 + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +/* Forward declarations for functions related to message buffering. */ +static void ssl_buffering_free_slot(mbedtls_ssl_context *ssl, + uint8_t slot); +static void ssl_free_buffered_record(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_load_buffered_message(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_load_buffered_record(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_buffer_message(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_buffer_future_record(mbedtls_ssl_context *ssl, + mbedtls_record const *rec); +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_next_record_is_in_datagram(mbedtls_ssl_context *ssl); + +static size_t ssl_get_maximum_datagram_size(mbedtls_ssl_context const *ssl) +{ + size_t mtu = mbedtls_ssl_get_current_mtu(ssl); +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t out_buf_len = ssl->out_buf_len; +#else + size_t out_buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN; +#endif + + if (mtu != 0 && mtu < out_buf_len) { + return mtu; + } + + return out_buf_len; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_remaining_space_in_datagram(mbedtls_ssl_context const *ssl) +{ + size_t const bytes_written = ssl->out_left; + size_t const mtu = ssl_get_maximum_datagram_size(ssl); + + /* Double-check that the write-index hasn't gone + * past what we can transmit in a single datagram. */ + if (bytes_written > mtu) { + /* Should never happen... */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + return (int) (mtu - bytes_written); +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_remaining_payload_in_datagram(mbedtls_ssl_context const *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t remaining, expansion; + size_t max_len = MBEDTLS_SSL_OUT_CONTENT_LEN; + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + const size_t mfl = mbedtls_ssl_get_output_max_frag_len(ssl); + + if (max_len > mfl) { + max_len = mfl; + } + + /* By the standard (RFC 6066 Sect. 4), the MFL extension + * only limits the maximum record payload size, so in theory + * we would be allowed to pack multiple records of payload size + * MFL into a single datagram. However, this would mean that there's + * no way to explicitly communicate MTU restrictions to the peer. + * + * The following reduction of max_len makes sure that we never + * write datagrams larger than MFL + Record Expansion Overhead. + */ + if (max_len <= ssl->out_left) { + return 0; + } + + max_len -= ssl->out_left; +#endif + + ret = ssl_get_remaining_space_in_datagram(ssl); + if (ret < 0) { + return ret; + } + remaining = (size_t) ret; + + ret = mbedtls_ssl_get_record_expansion(ssl); + if (ret < 0) { + return ret; + } + expansion = (size_t) ret; + + if (remaining <= expansion) { + return 0; + } + + remaining -= expansion; + if (remaining >= max_len) { + remaining = max_len; + } + + return (int) remaining; +} + +/* + * Double the retransmit timeout value, within the allowed range, + * returning -1 if the maximum value has already been reached. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_double_retransmit_timeout(mbedtls_ssl_context *ssl) +{ + uint32_t new_timeout; + + if (ssl->handshake->retransmit_timeout >= ssl->conf->hs_timeout_max) { + return -1; + } + + /* Implement the final paragraph of RFC 6347 section 4.1.1.1 + * in the following way: after the initial transmission and a first + * retransmission, back off to a temporary estimated MTU of 508 bytes. + * This value is guaranteed to be deliverable (if not guaranteed to be + * delivered) of any compliant IPv4 (and IPv6) network, and should work + * on most non-IP stacks too. */ + if (ssl->handshake->retransmit_timeout != ssl->conf->hs_timeout_min) { + ssl->handshake->mtu = 508; + MBEDTLS_SSL_DEBUG_MSG(2, ("mtu autoreduction to %d bytes", ssl->handshake->mtu)); + } + + new_timeout = 2 * ssl->handshake->retransmit_timeout; + + /* Avoid arithmetic overflow and range overflow */ + if (new_timeout < ssl->handshake->retransmit_timeout || + new_timeout > ssl->conf->hs_timeout_max) { + new_timeout = ssl->conf->hs_timeout_max; + } + + ssl->handshake->retransmit_timeout = new_timeout; + MBEDTLS_SSL_DEBUG_MSG(3, ("update timeout value to %lu millisecs", + (unsigned long) ssl->handshake->retransmit_timeout)); + + return 0; +} + +static void ssl_reset_retransmit_timeout(mbedtls_ssl_context *ssl) +{ + ssl->handshake->retransmit_timeout = ssl->conf->hs_timeout_min; + MBEDTLS_SSL_DEBUG_MSG(3, ("update timeout value to %lu millisecs", + (unsigned long) ssl->handshake->retransmit_timeout)); +} +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +/* + * Encryption/decryption functions + */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) || defined(MBEDTLS_SSL_PROTO_TLS1_3) + +static size_t ssl_compute_padding_length(size_t len, + size_t granularity) +{ + return (granularity - (len + 1) % granularity) % granularity; +} + +/* This functions transforms a (D)TLS plaintext fragment and a record content + * type into an instance of the (D)TLSInnerPlaintext structure. This is used + * in DTLS 1.2 + CID and within TLS 1.3 to allow flexible padding and to protect + * a record's content type. + * + * struct { + * opaque content[DTLSPlaintext.length]; + * ContentType real_type; + * uint8 zeros[length_of_padding]; + * } (D)TLSInnerPlaintext; + * + * Input: + * - `content`: The beginning of the buffer holding the + * plaintext to be wrapped. + * - `*content_size`: The length of the plaintext in Bytes. + * - `max_len`: The number of Bytes available starting from + * `content`. This must be `>= *content_size`. + * - `rec_type`: The desired record content type. + * + * Output: + * - `content`: The beginning of the resulting (D)TLSInnerPlaintext structure. + * - `*content_size`: The length of the resulting (D)TLSInnerPlaintext structure. + * + * Returns: + * - `0` on success. + * - A negative error code if `max_len` didn't offer enough space + * for the expansion. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_build_inner_plaintext(unsigned char *content, + size_t *content_size, + size_t remaining, + uint8_t rec_type, + size_t pad) +{ + size_t len = *content_size; + + /* Write real content type */ + if (remaining == 0) { + return -1; + } + content[len] = rec_type; + len++; + remaining--; + + if (remaining < pad) { + return -1; + } + memset(content + len, 0, pad); + len += pad; + remaining -= pad; + + *content_size = len; + return 0; +} + +/* This function parses a (D)TLSInnerPlaintext structure. + * See ssl_build_inner_plaintext() for details. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_inner_plaintext(unsigned char const *content, + size_t *content_size, + uint8_t *rec_type) +{ + size_t remaining = *content_size; + + /* Determine length of padding by skipping zeroes from the back. */ + do { + if (remaining == 0) { + return -1; + } + remaining--; + } while (content[remaining] == 0); + + *content_size = remaining; + *rec_type = content[remaining]; + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID || MBEDTLS_SSL_PROTO_TLS1_3 */ + +/* The size of the `add_data` structure depends on various + * factors, namely + * + * 1) CID functionality disabled + * + * additional_data = + * 8: seq_num + + * 1: type + + * 2: version + + * 2: length of inner plaintext + + * + * size = 13 bytes + * + * 2) CID functionality based on RFC 9146 enabled + * + * size = 8 + 1 + 1 + 1 + 2 + 2 + 6 + 2 + CID-length + * = 23 + CID-length + * + * 3) CID functionality based on legacy CID version + according to draft-ietf-tls-dtls-connection-id-05 + * https://tools.ietf.org/html/draft-ietf-tls-dtls-connection-id-05 + * + * size = 13 + 1 + CID-length + * + * More information about the CID usage: + * + * Per Section 5.3 of draft-ietf-tls-dtls-connection-id-05 the + * size of the additional data structure is calculated as: + * + * additional_data = + * 8: seq_num + + * 1: tls12_cid + + * 2: DTLSCipherText.version + + * n: cid + + * 1: cid_length + + * 2: length_of_DTLSInnerPlaintext + * + * Per RFC 9146 the size of the add_data structure is calculated as: + * + * additional_data = + * 8: seq_num_placeholder + + * 1: tls12_cid + + * 1: cid_length + + * 1: tls12_cid + + * 2: DTLSCiphertext.version + + * 2: epoch + + * 6: sequence_number + + * n: cid + + * 2: length_of_DTLSInnerPlaintext + * + */ +static void ssl_extract_add_data_from_record(unsigned char *add_data, + size_t *add_data_len, + mbedtls_record *rec, + mbedtls_ssl_protocol_version + tls_version, + size_t taglen) +{ + /* Several types of ciphers have been defined for use with TLS and DTLS, + * and the MAC calculations for those ciphers differ slightly. Further + * variants were added when the CID functionality was added with RFC 9146. + * This implementations also considers the use of a legacy version of the + * CID specification published in draft-ietf-tls-dtls-connection-id-05, + * which is used in deployments. + * + * We will distinguish between the non-CID and the CID cases below. + * + * --- Non-CID cases --- + * + * Quoting RFC 5246 (TLS 1.2): + * + * additional_data = seq_num + TLSCompressed.type + + * TLSCompressed.version + TLSCompressed.length; + * + * For TLS 1.3, the record sequence number is dropped from the AAD + * and encoded within the nonce of the AEAD operation instead. + * Moreover, the additional data involves the length of the TLS + * ciphertext, not the TLS plaintext as in earlier versions. + * Quoting RFC 8446 (TLS 1.3): + * + * additional_data = TLSCiphertext.opaque_type || + * TLSCiphertext.legacy_record_version || + * TLSCiphertext.length + * + * We pass the tag length to this function in order to compute the + * ciphertext length from the inner plaintext length rec->data_len via + * + * TLSCiphertext.length = TLSInnerPlaintext.length + taglen. + * + * --- CID cases --- + * + * RFC 9146 uses a common pattern when constructing the data + * passed into a MAC / AEAD cipher. + * + * Data concatenation for MACs used with block ciphers with + * Encrypt-then-MAC Processing (with CID): + * + * data = seq_num_placeholder + + * tls12_cid + + * cid_length + + * tls12_cid + + * DTLSCiphertext.version + + * epoch + + * sequence_number + + * cid + + * DTLSCiphertext.length + + * IV + + * ENC(content + padding + padding_length) + * + * Data concatenation for MACs used with block ciphers (with CID): + * + * data = seq_num_placeholder + + * tls12_cid + + * cid_length + + * tls12_cid + + * DTLSCiphertext.version + + * epoch + + * sequence_number + + * cid + + * length_of_DTLSInnerPlaintext + + * DTLSInnerPlaintext.content + + * DTLSInnerPlaintext.real_type + + * DTLSInnerPlaintext.zeros + * + * AEAD ciphers use the following additional data calculation (with CIDs): + * + * additional_data = seq_num_placeholder + + * tls12_cid + + * cid_length + + * tls12_cid + + * DTLSCiphertext.version + + * epoch + + * sequence_number + + * cid + + * length_of_DTLSInnerPlaintext + * + * Section 5.3 of draft-ietf-tls-dtls-connection-id-05 (for legacy CID use) + * defines the additional data calculation as follows: + * + * additional_data = seq_num + + * tls12_cid + + * DTLSCipherText.version + + * cid + + * cid_length + + * length_of_DTLSInnerPlaintext + */ + + unsigned char *cur = add_data; + size_t ad_len_field = rec->data_len; + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT == 0 + const unsigned char seq_num_placeholder[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + /* In TLS 1.3, the AAD contains the length of the TLSCiphertext, + * which differs from the length of the TLSInnerPlaintext + * by the length of the authentication tag. */ + ad_len_field += taglen; + } else +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + { + ((void) tls_version); + ((void) taglen); + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT == 0 + if (rec->cid_len != 0) { + // seq_num_placeholder + memcpy(cur, seq_num_placeholder, sizeof(seq_num_placeholder)); + cur += sizeof(seq_num_placeholder); + + // tls12_cid type + *cur = rec->type; + cur++; + + // cid_length + *cur = rec->cid_len; + cur++; + } else +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + { + // epoch + sequence number + memcpy(cur, rec->ctr, sizeof(rec->ctr)); + cur += sizeof(rec->ctr); + } + } + + // type + *cur = rec->type; + cur++; + + // version + memcpy(cur, rec->ver, sizeof(rec->ver)); + cur += sizeof(rec->ver); + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT == 1 + + if (rec->cid_len != 0) { + // CID + memcpy(cur, rec->cid, rec->cid_len); + cur += rec->cid_len; + + // cid_length + *cur = rec->cid_len; + cur++; + + // length of inner plaintext + MBEDTLS_PUT_UINT16_BE(ad_len_field, cur, 0); + cur += 2; + } else +#elif defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) && \ + MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT == 0 + + if (rec->cid_len != 0) { + // epoch + sequence number + memcpy(cur, rec->ctr, sizeof(rec->ctr)); + cur += sizeof(rec->ctr); + + // CID + memcpy(cur, rec->cid, rec->cid_len); + cur += rec->cid_len; + + // length of inner plaintext + MBEDTLS_PUT_UINT16_BE(ad_len_field, cur, 0); + cur += 2; + } else +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + { + MBEDTLS_PUT_UINT16_BE(ad_len_field, cur, 0); + cur += 2; + } + + *add_data_len = cur - add_data; +} + +#if defined(MBEDTLS_GCM_C) || \ + defined(MBEDTLS_CCM_C) || \ + defined(MBEDTLS_CHACHAPOLY_C) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_transform_aead_dynamic_iv_is_explicit( + mbedtls_ssl_transform const *transform) +{ + return transform->ivlen != transform->fixed_ivlen; +} + +/* Compute IV := ( fixed_iv || 0 ) XOR ( 0 || dynamic_IV ) + * + * Concretely, this occurs in two variants: + * + * a) Fixed and dynamic IV lengths add up to total IV length, giving + * IV = fixed_iv || dynamic_iv + * + * This variant is used in TLS 1.2 when used with GCM or CCM. + * + * b) Fixed IV lengths matches total IV length, giving + * IV = fixed_iv XOR ( 0 || dynamic_iv ) + * + * This variant occurs in TLS 1.3 and for TLS 1.2 when using ChaChaPoly. + * + * See also the documentation of mbedtls_ssl_transform. + * + * This function has the precondition that + * + * dst_iv_len >= max( fixed_iv_len, dynamic_iv_len ) + * + * which has to be ensured by the caller. If this precondition + * violated, the behavior of this function is undefined. + */ +static void ssl_build_record_nonce(unsigned char *dst_iv, + size_t dst_iv_len, + unsigned char const *fixed_iv, + size_t fixed_iv_len, + unsigned char const *dynamic_iv, + size_t dynamic_iv_len) +{ + /* Start with Fixed IV || 0 */ + memset(dst_iv, 0, dst_iv_len); + memcpy(dst_iv, fixed_iv, fixed_iv_len); + + dst_iv += dst_iv_len - dynamic_iv_len; + mbedtls_xor(dst_iv, dst_iv, dynamic_iv, dynamic_iv_len); +} +#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */ + +int mbedtls_ssl_encrypt_buf(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform, + mbedtls_record *rec, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + mbedtls_ssl_mode_t ssl_mode; + int auth_done = 0; + unsigned char *data; + /* For an explanation of the additional data length see + * the description of ssl_extract_add_data_from_record(). + */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + unsigned char add_data[23 + MBEDTLS_SSL_CID_OUT_LEN_MAX]; +#else + unsigned char add_data[13]; +#endif + size_t add_data_len; + size_t post_avail; + + /* The SSL context is only used for debugging purposes! */ +#if !defined(MBEDTLS_DEBUG_C) + ssl = NULL; /* make sure we don't use it except for debug */ + ((void) ssl); +#endif + + /* The PRNG is used for dynamic IV generation that's used + * for CBC transformations in TLS 1.2. */ +#if !(defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) && \ + defined(MBEDTLS_SSL_PROTO_TLS1_2)) + ((void) f_rng); + ((void) p_rng); +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> encrypt buf")); + + if (transform == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no transform provided to encrypt_buf")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + if (rec == NULL + || rec->buf == NULL + || rec->buf_len < rec->data_offset + || rec->buf_len - rec->data_offset < rec->data_len +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + || rec->cid_len != 0 +#endif + ) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad record structure provided to encrypt_buf")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ssl_mode = mbedtls_ssl_get_mode_from_transform(transform); + + data = rec->buf + rec->data_offset; + post_avail = rec->buf_len - (rec->data_len + rec->data_offset); + MBEDTLS_SSL_DEBUG_BUF(4, "before encrypt: output payload", + data, rec->data_len); + + if (rec->data_len > MBEDTLS_SSL_OUT_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Record content %" MBEDTLS_PRINTF_SIZET + " too large, maximum %" MBEDTLS_PRINTF_SIZET, + rec->data_len, + (size_t) MBEDTLS_SSL_OUT_CONTENT_LEN)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* The following two code paths implement the (D)TLSInnerPlaintext + * structure present in TLS 1.3 and DTLS 1.2 + CID. + * + * See ssl_build_inner_plaintext() for more information. + * + * Note that this changes `rec->data_len`, and hence + * `post_avail` needs to be recalculated afterwards. + * + * Note also that the two code paths cannot occur simultaneously + * since they apply to different versions of the protocol. There + * is hence no risk of double-addition of the inner plaintext. + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (transform->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + size_t padding = + ssl_compute_padding_length(rec->data_len, + MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY); + if (ssl_build_inner_plaintext(data, + &rec->data_len, + post_avail, + rec->type, + padding) != 0) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + rec->type = MBEDTLS_SSL_MSG_APPLICATION_DATA; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* + * Add CID information + */ + rec->cid_len = transform->out_cid_len; + memcpy(rec->cid, transform->out_cid, transform->out_cid_len); + MBEDTLS_SSL_DEBUG_BUF(3, "CID", rec->cid, rec->cid_len); + + if (rec->cid_len != 0) { + size_t padding = + ssl_compute_padding_length(rec->data_len, + MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY); + /* + * Wrap plaintext into DTLSInnerPlaintext structure. + * See ssl_build_inner_plaintext() for more information. + * + * Note that this changes `rec->data_len`, and hence + * `post_avail` needs to be recalculated afterwards. + */ + if (ssl_build_inner_plaintext(data, + &rec->data_len, + post_avail, + rec->type, + padding) != 0) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + rec->type = MBEDTLS_SSL_MSG_CID; + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + post_avail = rec->buf_len - (rec->data_len + rec->data_offset); + + /* + * Add MAC before if needed + */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + if (ssl_mode == MBEDTLS_SSL_MODE_STREAM || + ssl_mode == MBEDTLS_SSL_MODE_CBC) { + if (post_avail < transform->maclen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Buffer provided for encrypted record not large enough")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + unsigned char mac[MBEDTLS_SSL_MAC_ADD]; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t sign_mac_length = 0; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + ssl_extract_add_data_from_record(add_data, &add_data_len, rec, + transform->tls_version, + transform->taglen); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_mac_sign_setup(&operation, transform->psa_mac_enc, + transform->psa_mac_alg); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_disabled; + } + + status = psa_mac_update(&operation, add_data, add_data_len); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_disabled; + } + + status = psa_mac_update(&operation, data, rec->data_len); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_disabled; + } + + status = psa_mac_sign_finish(&operation, mac, MBEDTLS_SSL_MAC_ADD, + &sign_mac_length); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_disabled; + } +#else + ret = mbedtls_md_hmac_update(&transform->md_ctx_enc, add_data, + add_data_len); + if (ret != 0) { + goto hmac_failed_etm_disabled; + } + ret = mbedtls_md_hmac_update(&transform->md_ctx_enc, data, rec->data_len); + if (ret != 0) { + goto hmac_failed_etm_disabled; + } + ret = mbedtls_md_hmac_finish(&transform->md_ctx_enc, mac); + if (ret != 0) { + goto hmac_failed_etm_disabled; + } + ret = mbedtls_md_hmac_reset(&transform->md_ctx_enc); + if (ret != 0) { + goto hmac_failed_etm_disabled; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + memcpy(data + rec->data_len, mac, transform->maclen); +#endif + + MBEDTLS_SSL_DEBUG_BUF(4, "computed mac", data + rec->data_len, + transform->maclen); + + rec->data_len += transform->maclen; + post_avail -= transform->maclen; + auth_done++; + +hmac_failed_etm_disabled: + mbedtls_platform_zeroize(mac, transform->maclen); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ret = PSA_TO_MBEDTLS_ERR(status); + status = psa_mac_abort(&operation); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_hmac_xxx", ret); + return ret; + } + } +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + + /* + * Encrypt + */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_STREAM) + if (ssl_mode == MBEDTLS_SSL_MODE_STREAM) { + MBEDTLS_SSL_DEBUG_MSG(3, ("before encrypt: msglen = %" MBEDTLS_PRINTF_SIZET ", " + "including %d bytes of padding", + rec->data_len, 0)); + + /* The only supported stream cipher is "NULL", + * so there's nothing to do here.*/ + } else +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_STREAM */ + +#if defined(MBEDTLS_GCM_C) || \ + defined(MBEDTLS_CCM_C) || \ + defined(MBEDTLS_CHACHAPOLY_C) + if (ssl_mode == MBEDTLS_SSL_MODE_AEAD) { + unsigned char iv[12]; + unsigned char *dynamic_iv; + size_t dynamic_iv_len; + int dynamic_iv_is_explicit = + ssl_transform_aead_dynamic_iv_is_explicit(transform); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Check that there's space for the authentication tag. */ + if (post_avail < transform->taglen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Buffer provided for encrypted record not large enough")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + /* + * Build nonce for AEAD encryption. + * + * Note: In the case of CCM and GCM in TLS 1.2, the dynamic + * part of the IV is prepended to the ciphertext and + * can be chosen freely - in particular, it need not + * agree with the record sequence number. + * However, since ChaChaPoly as well as all AEAD modes + * in TLS 1.3 use the record sequence number as the + * dynamic part of the nonce, we uniformly use the + * record sequence number here in all cases. + */ + dynamic_iv = rec->ctr; + dynamic_iv_len = sizeof(rec->ctr); + + ssl_build_record_nonce(iv, sizeof(iv), + transform->iv_enc, + transform->fixed_ivlen, + dynamic_iv, + dynamic_iv_len); + + /* + * Build additional data for AEAD encryption. + * This depends on the TLS version. + */ + ssl_extract_add_data_from_record(add_data, &add_data_len, rec, + transform->tls_version, + transform->taglen); + + MBEDTLS_SSL_DEBUG_BUF(4, "IV used (internal)", + iv, transform->ivlen); + MBEDTLS_SSL_DEBUG_BUF(4, "IV used (transmitted)", + dynamic_iv, + dynamic_iv_is_explicit ? dynamic_iv_len : 0); + MBEDTLS_SSL_DEBUG_BUF(4, "additional data used for AEAD", + add_data, add_data_len); + MBEDTLS_SSL_DEBUG_MSG(3, ("before encrypt: msglen = %" MBEDTLS_PRINTF_SIZET ", " + "including 0 bytes of padding", + rec->data_len)); + + /* + * Encrypt and authenticate + */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_aead_encrypt(transform->psa_key_enc, + transform->psa_alg, + iv, transform->ivlen, + add_data, add_data_len, + data, rec->data_len, + data, rec->buf_len - (data - rec->buf), + &rec->data_len); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_encrypt_buf", ret); + return ret; + } +#else + if ((ret = mbedtls_cipher_auth_encrypt_ext(&transform->cipher_ctx_enc, + iv, transform->ivlen, + add_data, add_data_len, + data, rec->data_len, /* src */ + data, rec->buf_len - (data - rec->buf), /* dst */ + &rec->data_len, + transform->taglen)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_auth_encrypt_ext", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + MBEDTLS_SSL_DEBUG_BUF(4, "after encrypt: tag", + data + rec->data_len - transform->taglen, + transform->taglen); + /* Account for authentication tag. */ + post_avail -= transform->taglen; + + /* + * Prefix record content with dynamic IV in case it is explicit. + */ + if (dynamic_iv_is_explicit != 0) { + if (rec->data_offset < dynamic_iv_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Buffer provided for encrypted record not large enough")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + memcpy(data - dynamic_iv_len, dynamic_iv, dynamic_iv_len); + rec->data_offset -= dynamic_iv_len; + rec->data_len += dynamic_iv_len; + } + + auth_done++; + } else +#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) + if (ssl_mode == MBEDTLS_SSL_MODE_CBC || + ssl_mode == MBEDTLS_SSL_MODE_CBC_ETM) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t padlen, i; + size_t olen; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t part_len; + psa_cipher_operation_t cipher_op = PSA_CIPHER_OPERATION_INIT; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* Currently we're always using minimal padding + * (up to 255 bytes would be allowed). */ + padlen = transform->ivlen - (rec->data_len + 1) % transform->ivlen; + if (padlen == transform->ivlen) { + padlen = 0; + } + + /* Check there's enough space in the buffer for the padding. */ + if (post_avail < padlen + 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Buffer provided for encrypted record not large enough")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + for (i = 0; i <= padlen; i++) { + data[rec->data_len + i] = (unsigned char) padlen; + } + + rec->data_len += padlen + 1; + post_avail -= padlen + 1; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* + * Prepend per-record IV for block cipher in TLS v1.2 as per + * Method 1 (6.2.3.2. in RFC4346 and RFC5246) + */ + if (f_rng == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("No PRNG provided to encrypt_record routine")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (rec->data_offset < transform->ivlen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Buffer provided for encrypted record not large enough")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + /* + * Generate IV + */ + ret = f_rng(p_rng, transform->iv_enc, transform->ivlen); + if (ret != 0) { + return ret; + } + + memcpy(data - transform->ivlen, transform->iv_enc, transform->ivlen); +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + MBEDTLS_SSL_DEBUG_MSG(3, ("before encrypt: msglen = %" MBEDTLS_PRINTF_SIZET ", " + "including %" + MBEDTLS_PRINTF_SIZET + " bytes of IV and %" MBEDTLS_PRINTF_SIZET " bytes of padding", + rec->data_len, transform->ivlen, + padlen + 1)); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_cipher_encrypt_setup(&cipher_op, + transform->psa_key_enc, transform->psa_alg); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_encrypt_setup", ret); + return ret; + } + + status = psa_cipher_set_iv(&cipher_op, transform->iv_enc, transform->ivlen); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_set_iv", ret); + return ret; + + } + + status = psa_cipher_update(&cipher_op, + data, rec->data_len, + data, rec->data_len, &olen); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_update", ret); + return ret; + + } + + status = psa_cipher_finish(&cipher_op, + data + olen, rec->data_len - olen, + &part_len); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_finish", ret); + return ret; + + } + + olen += part_len; +#else + if ((ret = mbedtls_cipher_crypt(&transform->cipher_ctx_enc, + transform->iv_enc, + transform->ivlen, + data, rec->data_len, + data, &olen)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_crypt", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (rec->data_len != olen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + data -= transform->ivlen; + rec->data_offset -= transform->ivlen; + rec->data_len += transform->ivlen; + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + if (auth_done == 0) { + unsigned char mac[MBEDTLS_SSL_MAC_ADD]; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + size_t sign_mac_length = 0; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* MAC(MAC_write_key, add_data, IV, ENC(content + padding + padding_length)) + */ + + if (post_avail < transform->maclen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Buffer provided for encrypted record not large enough")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + ssl_extract_add_data_from_record(add_data, &add_data_len, + rec, transform->tls_version, + transform->taglen); + + MBEDTLS_SSL_DEBUG_MSG(3, ("using encrypt then mac")); + MBEDTLS_SSL_DEBUG_BUF(4, "MAC'd meta-data", add_data, + add_data_len); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_mac_sign_setup(&operation, transform->psa_mac_enc, + transform->psa_mac_alg); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } + + status = psa_mac_update(&operation, add_data, add_data_len); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } + + status = psa_mac_update(&operation, data, rec->data_len); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } + + status = psa_mac_sign_finish(&operation, mac, MBEDTLS_SSL_MAC_ADD, + &sign_mac_length); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } +#else + + ret = mbedtls_md_hmac_update(&transform->md_ctx_enc, add_data, + add_data_len); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } + ret = mbedtls_md_hmac_update(&transform->md_ctx_enc, + data, rec->data_len); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } + ret = mbedtls_md_hmac_finish(&transform->md_ctx_enc, mac); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } + ret = mbedtls_md_hmac_reset(&transform->md_ctx_enc); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + memcpy(data + rec->data_len, mac, transform->maclen); + + rec->data_len += transform->maclen; + post_avail -= transform->maclen; + auth_done++; + +hmac_failed_etm_enabled: + mbedtls_platform_zeroize(mac, transform->maclen); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ret = PSA_TO_MBEDTLS_ERR(status); + status = psa_mac_abort(&operation); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "HMAC calculation failed", ret); + return ret; + } + } +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + } else +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC) */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Make extra sure authentication was performed, exactly once */ + if (auth_done != 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= encrypt buf")); + + return 0; +} + +int mbedtls_ssl_decrypt_buf(mbedtls_ssl_context const *ssl, + mbedtls_ssl_transform *transform, + mbedtls_record *rec) +{ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) || defined(MBEDTLS_CIPHER_MODE_AEAD) + size_t olen; +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC || MBEDTLS_CIPHER_MODE_AEAD */ + mbedtls_ssl_mode_t ssl_mode; + int ret; + + int auth_done = 0; +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + size_t padlen = 0, correct = 1; +#endif + unsigned char *data; + /* For an explanation of the additional data length see + * the description of ssl_extract_add_data_from_record(). + */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + unsigned char add_data[23 + MBEDTLS_SSL_CID_IN_LEN_MAX]; +#else + unsigned char add_data[13]; +#endif + size_t add_data_len; + +#if !defined(MBEDTLS_DEBUG_C) + ssl = NULL; /* make sure we don't use it except for debug */ + ((void) ssl); +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> decrypt buf")); + if (rec == NULL || + rec->buf == NULL || + rec->buf_len < rec->data_offset || + rec->buf_len - rec->data_offset < rec->data_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad record structure provided to decrypt_buf")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + data = rec->buf + rec->data_offset; + ssl_mode = mbedtls_ssl_get_mode_from_transform(transform); + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* + * Match record's CID with incoming CID. + */ + if (rec->cid_len != transform->in_cid_len || + memcmp(rec->cid, transform->in_cid, rec->cid_len) != 0) { + return MBEDTLS_ERR_SSL_UNEXPECTED_CID; + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_STREAM) + if (ssl_mode == MBEDTLS_SSL_MODE_STREAM) { + /* The only supported stream cipher is "NULL", + * so there's nothing to do here.*/ + } else +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_STREAM */ +#if defined(MBEDTLS_GCM_C) || \ + defined(MBEDTLS_CCM_C) || \ + defined(MBEDTLS_CHACHAPOLY_C) + if (ssl_mode == MBEDTLS_SSL_MODE_AEAD) { + unsigned char iv[12]; + unsigned char *dynamic_iv; + size_t dynamic_iv_len; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* + * Extract dynamic part of nonce for AEAD decryption. + * + * Note: In the case of CCM and GCM in TLS 1.2, the dynamic + * part of the IV is prepended to the ciphertext and + * can be chosen freely - in particular, it need not + * agree with the record sequence number. + */ + dynamic_iv_len = sizeof(rec->ctr); + if (ssl_transform_aead_dynamic_iv_is_explicit(transform) == 1) { + if (rec->data_len < dynamic_iv_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("msglen (%" MBEDTLS_PRINTF_SIZET + " ) < explicit_iv_len (%" MBEDTLS_PRINTF_SIZET ") ", + rec->data_len, + dynamic_iv_len)); + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + dynamic_iv = data; + + data += dynamic_iv_len; + rec->data_offset += dynamic_iv_len; + rec->data_len -= dynamic_iv_len; + } else { + dynamic_iv = rec->ctr; + } + + /* Check that there's space for the authentication tag. */ + if (rec->data_len < transform->taglen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("msglen (%" MBEDTLS_PRINTF_SIZET + ") < taglen (%" MBEDTLS_PRINTF_SIZET ") ", + rec->data_len, + transform->taglen)); + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + rec->data_len -= transform->taglen; + + /* + * Prepare nonce from dynamic and static parts. + */ + ssl_build_record_nonce(iv, sizeof(iv), + transform->iv_dec, + transform->fixed_ivlen, + dynamic_iv, + dynamic_iv_len); + + /* + * Build additional data for AEAD encryption. + * This depends on the TLS version. + */ + ssl_extract_add_data_from_record(add_data, &add_data_len, rec, + transform->tls_version, + transform->taglen); + MBEDTLS_SSL_DEBUG_BUF(4, "additional data used for AEAD", + add_data, add_data_len); + + /* Because of the check above, we know that there are + * explicit_iv_len Bytes preceding data, and taglen + * bytes following data + data_len. This justifies + * the debug message and the invocation of + * mbedtls_cipher_auth_decrypt_ext() below. */ + + MBEDTLS_SSL_DEBUG_BUF(4, "IV used", iv, transform->ivlen); + MBEDTLS_SSL_DEBUG_BUF(4, "TAG used", data + rec->data_len, + transform->taglen); + + /* + * Decrypt and authenticate + */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_aead_decrypt(transform->psa_key_dec, + transform->psa_alg, + iv, transform->ivlen, + add_data, add_data_len, + data, rec->data_len + transform->taglen, + data, rec->buf_len - (data - rec->buf), + &olen); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_aead_decrypt", ret); + return ret; + } +#else + if ((ret = mbedtls_cipher_auth_decrypt_ext(&transform->cipher_ctx_dec, + iv, transform->ivlen, + add_data, add_data_len, + data, rec->data_len + transform->taglen, /* src */ + data, rec->buf_len - (data - rec->buf), &olen, /* dst */ + transform->taglen)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_auth_decrypt_ext", ret); + + if (ret == MBEDTLS_ERR_CIPHER_AUTH_FAILED) { + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + auth_done++; + + /* Double-check that AEAD decryption doesn't change content length. */ + if (olen != rec->data_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + } else +#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) + if (ssl_mode == MBEDTLS_SSL_MODE_CBC || + ssl_mode == MBEDTLS_SSL_MODE_CBC_ETM) { + size_t minlen = 0; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t part_len; + psa_cipher_operation_t cipher_op = PSA_CIPHER_OPERATION_INIT; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* + * Check immediate ciphertext sanity + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* The ciphertext is prefixed with the CBC IV. */ + minlen += transform->ivlen; +#endif + + /* Size considerations: + * + * - The CBC cipher text must not be empty and hence + * at least of size transform->ivlen. + * + * Together with the potential IV-prefix, this explains + * the first of the two checks below. + * + * - The record must contain a MAC, either in plain or + * encrypted, depending on whether Encrypt-then-MAC + * is used or not. + * - If it is, the message contains the IV-prefix, + * the CBC ciphertext, and the MAC. + * - If it is not, the padded plaintext, and hence + * the CBC ciphertext, has at least length maclen + 1 + * because there is at least the padding length byte. + * + * As the CBC ciphertext is not empty, both cases give the + * lower bound minlen + maclen + 1 on the record size, which + * we test for in the second check below. + */ + if (rec->data_len < minlen + transform->ivlen || + rec->data_len < minlen + transform->maclen + 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("msglen (%" MBEDTLS_PRINTF_SIZET + ") < max( ivlen(%" MBEDTLS_PRINTF_SIZET + "), maclen (%" MBEDTLS_PRINTF_SIZET ") " + "+ 1 ) ( + expl IV )", + rec->data_len, + transform->ivlen, + transform->maclen)); + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + + /* + * Authenticate before decrypt if enabled + */ +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + if (ssl_mode == MBEDTLS_SSL_MODE_CBC_ETM) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; +#else + unsigned char mac_expect[MBEDTLS_SSL_MAC_ADD]; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + MBEDTLS_SSL_DEBUG_MSG(3, ("using encrypt then mac")); + + /* Update data_len in tandem with add_data. + * + * The subtraction is safe because of the previous check + * data_len >= minlen + maclen + 1. + * + * Afterwards, we know that data + data_len is followed by at + * least maclen Bytes, which justifies the call to + * mbedtls_ct_memcmp() below. + * + * Further, we still know that data_len > minlen */ + rec->data_len -= transform->maclen; + ssl_extract_add_data_from_record(add_data, &add_data_len, rec, + transform->tls_version, + transform->taglen); + + /* Calculate expected MAC. */ + MBEDTLS_SSL_DEBUG_BUF(4, "MAC'd meta-data", add_data, + add_data_len); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_mac_verify_setup(&operation, transform->psa_mac_dec, + transform->psa_mac_alg); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } + + status = psa_mac_update(&operation, add_data, add_data_len); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } + + status = psa_mac_update(&operation, data, rec->data_len); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } + + /* Compare expected MAC with MAC at the end of the record. */ + status = psa_mac_verify_finish(&operation, data + rec->data_len, + transform->maclen); + if (status != PSA_SUCCESS) { + goto hmac_failed_etm_enabled; + } +#else + ret = mbedtls_md_hmac_update(&transform->md_ctx_dec, add_data, + add_data_len); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } + ret = mbedtls_md_hmac_update(&transform->md_ctx_dec, + data, rec->data_len); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } + ret = mbedtls_md_hmac_finish(&transform->md_ctx_dec, mac_expect); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } + ret = mbedtls_md_hmac_reset(&transform->md_ctx_dec); + if (ret != 0) { + goto hmac_failed_etm_enabled; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "message mac", data + rec->data_len, + transform->maclen); + MBEDTLS_SSL_DEBUG_BUF(4, "expected mac", mac_expect, + transform->maclen); + + /* Compare expected MAC with MAC at the end of the record. */ + if (mbedtls_ct_memcmp(data + rec->data_len, mac_expect, + transform->maclen) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("message mac does not match")); + ret = MBEDTLS_ERR_SSL_INVALID_MAC; + goto hmac_failed_etm_enabled; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + auth_done++; + +hmac_failed_etm_enabled: +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ret = PSA_TO_MBEDTLS_ERR(status); + status = psa_mac_abort(&operation); + if (ret == 0 && status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + } +#else + mbedtls_platform_zeroize(mac_expect, transform->maclen); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (ret != 0) { + if (ret != MBEDTLS_ERR_SSL_INVALID_MAC) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_hmac_xxx", ret); + } + return ret; + } + } +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + + /* + * Check length sanity + */ + + /* We know from above that data_len > minlen >= 0, + * so the following check in particular implies that + * data_len >= minlen + ivlen ( = minlen or 2 * minlen ). */ + if (rec->data_len % transform->ivlen != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("msglen (%" MBEDTLS_PRINTF_SIZET + ") %% ivlen (%" MBEDTLS_PRINTF_SIZET ") != 0", + rec->data_len, transform->ivlen)); + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* + * Initialize for prepended IV for block cipher in TLS v1.2 + */ + /* Safe because data_len >= minlen + ivlen = 2 * ivlen. */ + memcpy(transform->iv_dec, data, transform->ivlen); + + data += transform->ivlen; + rec->data_offset += transform->ivlen; + rec->data_len -= transform->ivlen; +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + /* We still have data_len % ivlen == 0 and data_len >= ivlen here. */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_cipher_decrypt_setup(&cipher_op, + transform->psa_key_dec, transform->psa_alg); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_decrypt_setup", ret); + return ret; + } + + status = psa_cipher_set_iv(&cipher_op, transform->iv_dec, transform->ivlen); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_set_iv", ret); + return ret; + } + + status = psa_cipher_update(&cipher_op, + data, rec->data_len, + data, rec->data_len, &olen); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_update", ret); + return ret; + } + + status = psa_cipher_finish(&cipher_op, + data + olen, rec->data_len - olen, + &part_len); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_cipher_finish", ret); + return ret; + } + + olen += part_len; +#else + + if ((ret = mbedtls_cipher_crypt(&transform->cipher_ctx_dec, + transform->iv_dec, transform->ivlen, + data, rec->data_len, data, &olen)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_crypt", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* Double-check that length hasn't changed during decryption. */ + if (rec->data_len != olen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Safe since data_len >= minlen + maclen + 1, so after having + * subtracted at most minlen and maclen up to this point, + * data_len > 0 (because of data_len % ivlen == 0, it's actually + * >= ivlen ). */ + padlen = data[rec->data_len - 1]; + + if (auth_done == 1) { + const size_t mask = mbedtls_ct_size_mask_ge( + rec->data_len, + padlen + 1); + correct &= mask; + padlen &= mask; + } else { +#if defined(MBEDTLS_SSL_DEBUG_ALL) + if (rec->data_len < transform->maclen + padlen + 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("msglen (%" MBEDTLS_PRINTF_SIZET + ") < maclen (%" MBEDTLS_PRINTF_SIZET + ") + padlen (%" MBEDTLS_PRINTF_SIZET ")", + rec->data_len, + transform->maclen, + padlen + 1)); + } +#endif + + const size_t mask = mbedtls_ct_size_mask_ge( + rec->data_len, + transform->maclen + padlen + 1); + correct &= mask; + padlen &= mask; + } + + padlen++; + + /* Regardless of the validity of the padding, + * we have data_len >= padlen here. */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* The padding check involves a series of up to 256 + * consecutive memory reads at the end of the record + * plaintext buffer. In order to hide the length and + * validity of the padding, always perform exactly + * `min(256,plaintext_len)` reads (but take into account + * only the last `padlen` bytes for the padding check). */ + size_t pad_count = 0; + volatile unsigned char * const check = data; + + /* Index of first padding byte; it has been ensured above + * that the subtraction is safe. */ + size_t const padding_idx = rec->data_len - padlen; + size_t const num_checks = rec->data_len <= 256 ? rec->data_len : 256; + size_t const start_idx = rec->data_len - num_checks; + size_t idx; + + for (idx = start_idx; idx < rec->data_len; idx++) { + /* pad_count += (idx >= padding_idx) && + * (check[idx] == padlen - 1); + */ + const size_t mask = mbedtls_ct_size_mask_ge(idx, padding_idx); + const size_t equal = mbedtls_ct_size_bool_eq(check[idx], + padlen - 1); + pad_count += mask & equal; + } + correct &= mbedtls_ct_size_bool_eq(pad_count, padlen); + +#if defined(MBEDTLS_SSL_DEBUG_ALL) + if (padlen > 0 && correct == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad padding byte detected")); + } +#endif + padlen &= mbedtls_ct_size_mask(correct); + +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + /* If the padding was found to be invalid, padlen == 0 + * and the subtraction is safe. If the padding was found valid, + * padlen hasn't been changed and the previous assertion + * data_len >= padlen still holds. */ + rec->data_len -= padlen; + } else +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + +#if defined(MBEDTLS_SSL_DEBUG_ALL) + MBEDTLS_SSL_DEBUG_BUF(4, "raw buffer after decryption", + data, rec->data_len); +#endif + + /* + * Authenticate if not done yet. + * Compute the MAC regardless of the padding result (RFC4346, CBCTIME). + */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + if (auth_done == 0) { + unsigned char mac_expect[MBEDTLS_SSL_MAC_ADD] = { 0 }; + unsigned char mac_peer[MBEDTLS_SSL_MAC_ADD] = { 0 }; + + /* If the initial value of padlen was such that + * data_len < maclen + padlen + 1, then padlen + * got reset to 1, and the initial check + * data_len >= minlen + maclen + 1 + * guarantees that at this point we still + * have at least data_len >= maclen. + * + * If the initial value of padlen was such that + * data_len >= maclen + padlen + 1, then we have + * subtracted either padlen + 1 (if the padding was correct) + * or 0 (if the padding was incorrect) since then, + * hence data_len >= maclen in any case. + */ + rec->data_len -= transform->maclen; + ssl_extract_add_data_from_record(add_data, &add_data_len, rec, + transform->tls_version, + transform->taglen); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* + * The next two sizes are the minimum and maximum values of + * data_len over all padlen values. + * + * They're independent of padlen, since we previously did + * data_len -= padlen. + * + * Note that max_len + maclen is never more than the buffer + * length, as we previously did in_msglen -= maclen too. + */ + const size_t max_len = rec->data_len + padlen; + const size_t min_len = (max_len > 256) ? max_len - 256 : 0; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ret = mbedtls_ct_hmac(transform->psa_mac_dec, + transform->psa_mac_alg, + add_data, add_data_len, + data, rec->data_len, min_len, max_len, + mac_expect); +#else + ret = mbedtls_ct_hmac(&transform->md_ctx_dec, + add_data, add_data_len, + data, rec->data_len, min_len, max_len, + mac_expect); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ct_hmac", ret); + goto hmac_failed_etm_disabled; + } + + mbedtls_ct_memcpy_offset(mac_peer, data, + rec->data_len, + min_len, max_len, + transform->maclen); +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_DEBUG_ALL) + MBEDTLS_SSL_DEBUG_BUF(4, "expected mac", mac_expect, transform->maclen); + MBEDTLS_SSL_DEBUG_BUF(4, "message mac", mac_peer, transform->maclen); +#endif + + if (mbedtls_ct_memcmp(mac_peer, mac_expect, + transform->maclen) != 0) { +#if defined(MBEDTLS_SSL_DEBUG_ALL) + MBEDTLS_SSL_DEBUG_MSG(1, ("message mac does not match")); +#endif + correct = 0; + } + auth_done++; + +hmac_failed_etm_disabled: + mbedtls_platform_zeroize(mac_peer, transform->maclen); + mbedtls_platform_zeroize(mac_expect, transform->maclen); + if (ret != 0) { + return ret; + } + } + + /* + * Finally check the correct flag + */ + if (correct == 0) { + return MBEDTLS_ERR_SSL_INVALID_MAC; + } +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + + /* Make extra sure authentication was performed, exactly once */ + if (auth_done != 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (transform->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + /* Remove inner padding and infer true content type. */ + ret = ssl_parse_inner_plaintext(data, &rec->data_len, + &rec->type); + + if (ret != 0) { + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + if (rec->cid_len != 0) { + ret = ssl_parse_inner_plaintext(data, &rec->data_len, + &rec->type); + if (ret != 0) { + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= decrypt buf")); + + return 0; +} + +#undef MAC_NONE +#undef MAC_PLAINTEXT +#undef MAC_CIPHERTEXT + +/* + * Fill the input message buffer by appending data to it. + * The amount of data already fetched is in ssl->in_left. + * + * If we return 0, is it guaranteed that (at least) nb_want bytes are + * available (from this read and/or a previous one). Otherwise, an error code + * is returned (possibly EOF or WANT_READ). + * + * With stream transport (TLS) on success ssl->in_left == nb_want, but + * with datagram transport (DTLS) on success ssl->in_left >= nb_want, + * since we always read a whole datagram at once. + * + * For DTLS, it is up to the caller to set ssl->next_record_offset when + * they're done reading a record. + */ +int mbedtls_ssl_fetch_input(mbedtls_ssl_context *ssl, size_t nb_want) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t in_buf_len = ssl->in_buf_len; +#else + size_t in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN; +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> fetch input")); + + if (ssl->f_recv == NULL && ssl->f_recv_timeout == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Bad usage of mbedtls_ssl_set_bio() ")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (nb_want > in_buf_len - (size_t) (ssl->in_hdr - ssl->in_buf)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("requesting more data than fits")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + uint32_t timeout; + + /* + * The point is, we need to always read a full datagram at once, so we + * sometimes read more then requested, and handle the additional data. + * It could be the rest of the current record (while fetching the + * header) and/or some other records in the same datagram. + */ + + /* + * Move to the next record in the already read datagram if applicable + */ + if (ssl->next_record_offset != 0) { + if (ssl->in_left < ssl->next_record_offset) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ssl->in_left -= ssl->next_record_offset; + + if (ssl->in_left != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("next record in same datagram, offset: %" + MBEDTLS_PRINTF_SIZET, + ssl->next_record_offset)); + memmove(ssl->in_hdr, + ssl->in_hdr + ssl->next_record_offset, + ssl->in_left); + } + + ssl->next_record_offset = 0; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("in_left: %" MBEDTLS_PRINTF_SIZET + ", nb_want: %" MBEDTLS_PRINTF_SIZET, + ssl->in_left, nb_want)); + + /* + * Done if we already have enough data. + */ + if (nb_want <= ssl->in_left) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= fetch input")); + return 0; + } + + /* + * A record can't be split across datagrams. If we need to read but + * are not at the beginning of a new record, the caller did something + * wrong. + */ + if (ssl->in_left != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* + * Don't even try to read if time's out already. + * This avoids by-passing the timer when repeatedly receiving messages + * that will end up being dropped. + */ + if (mbedtls_ssl_check_timer(ssl) != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("timer has expired")); + ret = MBEDTLS_ERR_SSL_TIMEOUT; + } else { + len = in_buf_len - (ssl->in_hdr - ssl->in_buf); + + if (mbedtls_ssl_is_handshake_over(ssl) == 0) { + timeout = ssl->handshake->retransmit_timeout; + } else { + timeout = ssl->conf->read_timeout; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("f_recv_timeout: %lu ms", (unsigned long) timeout)); + + if (ssl->f_recv_timeout != NULL) { + ret = ssl->f_recv_timeout(ssl->p_bio, ssl->in_hdr, len, + timeout); + } else { + ret = ssl->f_recv(ssl->p_bio, ssl->in_hdr, len); + } + + MBEDTLS_SSL_DEBUG_RET(2, "ssl->f_recv(_timeout)", ret); + + if (ret == 0) { + return MBEDTLS_ERR_SSL_CONN_EOF; + } + } + + if (ret == MBEDTLS_ERR_SSL_TIMEOUT) { + MBEDTLS_SSL_DEBUG_MSG(2, ("timeout")); + mbedtls_ssl_set_timer(ssl, 0); + + if (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { + if (ssl_double_retransmit_timeout(ssl) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("handshake timeout")); + return MBEDTLS_ERR_SSL_TIMEOUT; + } + + if ((ret = mbedtls_ssl_resend(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_resend", ret); + return ret; + } + + return MBEDTLS_ERR_SSL_WANT_READ; + } +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_RENEGOTIATION) + else if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING) { + if ((ret = mbedtls_ssl_resend_hello_request(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_resend_hello_request", + ret); + return ret; + } + + return MBEDTLS_ERR_SSL_WANT_READ; + } +#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_RENEGOTIATION */ + } + + if (ret < 0) { + return ret; + } + + ssl->in_left = ret; + } else +#endif + { + MBEDTLS_SSL_DEBUG_MSG(2, ("in_left: %" MBEDTLS_PRINTF_SIZET + ", nb_want: %" MBEDTLS_PRINTF_SIZET, + ssl->in_left, nb_want)); + + while (ssl->in_left < nb_want) { + len = nb_want - ssl->in_left; + + if (mbedtls_ssl_check_timer(ssl) != 0) { + ret = MBEDTLS_ERR_SSL_TIMEOUT; + } else { + if (ssl->f_recv_timeout != NULL) { + ret = ssl->f_recv_timeout(ssl->p_bio, + ssl->in_hdr + ssl->in_left, len, + ssl->conf->read_timeout); + } else { + ret = ssl->f_recv(ssl->p_bio, + ssl->in_hdr + ssl->in_left, len); + } + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("in_left: %" MBEDTLS_PRINTF_SIZET + ", nb_want: %" MBEDTLS_PRINTF_SIZET, + ssl->in_left, nb_want)); + MBEDTLS_SSL_DEBUG_RET(2, "ssl->f_recv(_timeout)", ret); + + if (ret == 0) { + return MBEDTLS_ERR_SSL_CONN_EOF; + } + + if (ret < 0) { + return ret; + } + + if ((size_t) ret > len) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("f_recv returned %d bytes but only %" MBEDTLS_PRINTF_SIZET + " were requested", + ret, len)); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ssl->in_left += ret; + } + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= fetch input")); + + return 0; +} + +/* + * Flush any data not yet written + */ +int mbedtls_ssl_flush_output(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> flush output")); + + if (ssl->f_send == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Bad usage of mbedtls_ssl_set_bio() ")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* Avoid incrementing counter if data is flushed */ + if (ssl->out_left == 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= flush output")); + return 0; + } + + while (ssl->out_left > 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("message length: %" MBEDTLS_PRINTF_SIZET + ", out_left: %" MBEDTLS_PRINTF_SIZET, + mbedtls_ssl_out_hdr_len(ssl) + ssl->out_msglen, ssl->out_left)); + + buf = ssl->out_hdr - ssl->out_left; + ret = ssl->f_send(ssl->p_bio, buf, ssl->out_left); + + MBEDTLS_SSL_DEBUG_RET(2, "ssl->f_send", ret); + + if (ret <= 0) { + return ret; + } + + if ((size_t) ret > ssl->out_left) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("f_send returned %d bytes but only %" MBEDTLS_PRINTF_SIZET + " bytes were sent", + ret, ssl->out_left)); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ssl->out_left -= ret; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ssl->out_hdr = ssl->out_buf; + } else +#endif + { + ssl->out_hdr = ssl->out_buf + 8; + } + mbedtls_ssl_update_out_pointers(ssl, ssl->transform_out); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= flush output")); + + return 0; +} + +/* + * Functions to handle the DTLS retransmission state machine + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) +/* + * Append current handshake message to current outgoing flight + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_flight_append(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_flight_item *msg; + MBEDTLS_SSL_DEBUG_MSG(2, ("=> ssl_flight_append")); + MBEDTLS_SSL_DEBUG_BUF(4, "message appended to flight", + ssl->out_msg, ssl->out_msglen); + + /* Allocate space for current message */ + if ((msg = mbedtls_calloc(1, sizeof(mbedtls_ssl_flight_item))) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc %" MBEDTLS_PRINTF_SIZET " bytes failed", + sizeof(mbedtls_ssl_flight_item))); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + if ((msg->p = mbedtls_calloc(1, ssl->out_msglen)) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc %" MBEDTLS_PRINTF_SIZET " bytes failed", + ssl->out_msglen)); + mbedtls_free(msg); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + /* Copy current handshake message with headers */ + memcpy(msg->p, ssl->out_msg, ssl->out_msglen); + msg->len = ssl->out_msglen; + msg->type = ssl->out_msgtype; + msg->next = NULL; + + /* Append to the current flight */ + if (ssl->handshake->flight == NULL) { + ssl->handshake->flight = msg; + } else { + mbedtls_ssl_flight_item *cur = ssl->handshake->flight; + while (cur->next != NULL) { + cur = cur->next; + } + cur->next = msg; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= ssl_flight_append")); + return 0; +} + +/* + * Free the current flight of handshake messages + */ +void mbedtls_ssl_flight_free(mbedtls_ssl_flight_item *flight) +{ + mbedtls_ssl_flight_item *cur = flight; + mbedtls_ssl_flight_item *next; + + while (cur != NULL) { + next = cur->next; + + mbedtls_free(cur->p); + mbedtls_free(cur); + + cur = next; + } +} + +/* + * Swap transform_out and out_ctr with the alternative ones + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_swap_epochs(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_transform *tmp_transform; + unsigned char tmp_out_ctr[MBEDTLS_SSL_SEQUENCE_NUMBER_LEN]; + + if (ssl->transform_out == ssl->handshake->alt_transform_out) { + MBEDTLS_SSL_DEBUG_MSG(3, ("skip swap epochs")); + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("swap epochs")); + + /* Swap transforms */ + tmp_transform = ssl->transform_out; + ssl->transform_out = ssl->handshake->alt_transform_out; + ssl->handshake->alt_transform_out = tmp_transform; + + /* Swap epoch + sequence_number */ + memcpy(tmp_out_ctr, ssl->cur_out_ctr, sizeof(tmp_out_ctr)); + memcpy(ssl->cur_out_ctr, ssl->handshake->alt_out_ctr, + sizeof(ssl->cur_out_ctr)); + memcpy(ssl->handshake->alt_out_ctr, tmp_out_ctr, + sizeof(ssl->handshake->alt_out_ctr)); + + /* Adjust to the newly activated transform */ + mbedtls_ssl_update_out_pointers(ssl, ssl->transform_out); + + return 0; +} + +/* + * Retransmit the current flight of messages. + */ +int mbedtls_ssl_resend(mbedtls_ssl_context *ssl) +{ + int ret = 0; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> mbedtls_ssl_resend")); + + ret = mbedtls_ssl_flight_transmit(ssl); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= mbedtls_ssl_resend")); + + return ret; +} + +/* + * Transmit or retransmit the current flight of messages. + * + * Need to remember the current message in case flush_output returns + * WANT_WRITE, causing us to exit this function and come back later. + * This function must be called until state is no longer SENDING. + */ +int mbedtls_ssl_flight_transmit(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_SSL_DEBUG_MSG(2, ("=> mbedtls_ssl_flight_transmit")); + + if (ssl->handshake->retransmit_state != MBEDTLS_SSL_RETRANS_SENDING) { + MBEDTLS_SSL_DEBUG_MSG(2, ("initialise flight transmission")); + + ssl->handshake->cur_msg = ssl->handshake->flight; + ssl->handshake->cur_msg_p = ssl->handshake->flight->p + 12; + ret = ssl_swap_epochs(ssl); + if (ret != 0) { + return ret; + } + + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_SENDING; + } + + while (ssl->handshake->cur_msg != NULL) { + size_t max_frag_len; + const mbedtls_ssl_flight_item * const cur = ssl->handshake->cur_msg; + + int const is_finished = + (cur->type == MBEDTLS_SSL_MSG_HANDSHAKE && + cur->p[0] == MBEDTLS_SSL_HS_FINISHED); + + int const force_flush = ssl->disable_datagram_packing == 1 ? + SSL_FORCE_FLUSH : SSL_DONT_FORCE_FLUSH; + + /* Swap epochs before sending Finished: we can't do it after + * sending ChangeCipherSpec, in case write returns WANT_READ. + * Must be done before copying, may change out_msg pointer */ + if (is_finished && ssl->handshake->cur_msg_p == (cur->p + 12)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("swap epochs to send finished message")); + ret = ssl_swap_epochs(ssl); + if (ret != 0) { + return ret; + } + } + + ret = ssl_get_remaining_payload_in_datagram(ssl); + if (ret < 0) { + return ret; + } + max_frag_len = (size_t) ret; + + /* CCS is copied as is, while HS messages may need fragmentation */ + if (cur->type == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC) { + if (max_frag_len == 0) { + if ((ret = mbedtls_ssl_flush_output(ssl)) != 0) { + return ret; + } + + continue; + } + + memcpy(ssl->out_msg, cur->p, cur->len); + ssl->out_msglen = cur->len; + ssl->out_msgtype = cur->type; + + /* Update position inside current message */ + ssl->handshake->cur_msg_p += cur->len; + } else { + const unsigned char * const p = ssl->handshake->cur_msg_p; + const size_t hs_len = cur->len - 12; + const size_t frag_off = p - (cur->p + 12); + const size_t rem_len = hs_len - frag_off; + size_t cur_hs_frag_len, max_hs_frag_len; + + if ((max_frag_len < 12) || (max_frag_len == 12 && hs_len != 0)) { + if (is_finished) { + ret = ssl_swap_epochs(ssl); + if (ret != 0) { + return ret; + } + } + + if ((ret = mbedtls_ssl_flush_output(ssl)) != 0) { + return ret; + } + + continue; + } + max_hs_frag_len = max_frag_len - 12; + + cur_hs_frag_len = rem_len > max_hs_frag_len ? + max_hs_frag_len : rem_len; + + if (frag_off == 0 && cur_hs_frag_len != hs_len) { + MBEDTLS_SSL_DEBUG_MSG(2, ("fragmenting handshake message (%u > %u)", + (unsigned) cur_hs_frag_len, + (unsigned) max_hs_frag_len)); + } + + /* Messages are stored with handshake headers as if not fragmented, + * copy beginning of headers then fill fragmentation fields. + * Handshake headers: type(1) len(3) seq(2) f_off(3) f_len(3) */ + memcpy(ssl->out_msg, cur->p, 6); + + ssl->out_msg[6] = MBEDTLS_BYTE_2(frag_off); + ssl->out_msg[7] = MBEDTLS_BYTE_1(frag_off); + ssl->out_msg[8] = MBEDTLS_BYTE_0(frag_off); + + ssl->out_msg[9] = MBEDTLS_BYTE_2(cur_hs_frag_len); + ssl->out_msg[10] = MBEDTLS_BYTE_1(cur_hs_frag_len); + ssl->out_msg[11] = MBEDTLS_BYTE_0(cur_hs_frag_len); + + MBEDTLS_SSL_DEBUG_BUF(3, "handshake header", ssl->out_msg, 12); + + /* Copy the handshake message content and set records fields */ + memcpy(ssl->out_msg + 12, p, cur_hs_frag_len); + ssl->out_msglen = cur_hs_frag_len + 12; + ssl->out_msgtype = cur->type; + + /* Update position inside current message */ + ssl->handshake->cur_msg_p += cur_hs_frag_len; + } + + /* If done with the current message move to the next one if any */ + if (ssl->handshake->cur_msg_p >= cur->p + cur->len) { + if (cur->next != NULL) { + ssl->handshake->cur_msg = cur->next; + ssl->handshake->cur_msg_p = cur->next->p + 12; + } else { + ssl->handshake->cur_msg = NULL; + ssl->handshake->cur_msg_p = NULL; + } + } + + /* Actually send the message out */ + if ((ret = mbedtls_ssl_write_record(ssl, force_flush)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_record", ret); + return ret; + } + } + + if ((ret = mbedtls_ssl_flush_output(ssl)) != 0) { + return ret; + } + + /* Update state and set timer */ + if (mbedtls_ssl_is_handshake_over(ssl) == 1) { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_FINISHED; + } else { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_WAITING; + mbedtls_ssl_set_timer(ssl, ssl->handshake->retransmit_timeout); + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= mbedtls_ssl_flight_transmit")); + + return 0; +} + +/* + * To be called when the last message of an incoming flight is received. + */ +void mbedtls_ssl_recv_flight_completed(mbedtls_ssl_context *ssl) +{ + /* We won't need to resend that one any more */ + mbedtls_ssl_flight_free(ssl->handshake->flight); + ssl->handshake->flight = NULL; + ssl->handshake->cur_msg = NULL; + + /* The next incoming flight will start with this msg_seq */ + ssl->handshake->in_flight_start_seq = ssl->handshake->in_msg_seq; + + /* We don't want to remember CCS's across flight boundaries. */ + ssl->handshake->buffering.seen_ccs = 0; + + /* Clear future message buffering structure. */ + mbedtls_ssl_buffering_free(ssl); + + /* Cancel timer */ + mbedtls_ssl_set_timer(ssl, 0); + + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + ssl->in_msg[0] == MBEDTLS_SSL_HS_FINISHED) { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_FINISHED; + } else { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_PREPARING; + } +} + +/* + * To be called when the last message of an outgoing flight is send. + */ +void mbedtls_ssl_send_flight_completed(mbedtls_ssl_context *ssl) +{ + ssl_reset_retransmit_timeout(ssl); + mbedtls_ssl_set_timer(ssl, ssl->handshake->retransmit_timeout); + + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + ssl->in_msg[0] == MBEDTLS_SSL_HS_FINISHED) { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_FINISHED; + } else { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_WAITING; + } +} +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +/* + * Handshake layer functions + */ +int mbedtls_ssl_start_handshake_msg(mbedtls_ssl_context *ssl, unsigned hs_type, + unsigned char **buf, size_t *buf_len) +{ + /* + * Reserve 4 bytes for handshake header. ( Section 4,RFC 8446 ) + * ... + * HandshakeType msg_type; + * uint24 length; + * ... + */ + *buf = ssl->out_msg + 4; + *buf_len = MBEDTLS_SSL_OUT_CONTENT_LEN - 4; + + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = hs_type; + + return 0; +} + +/* + * Write (DTLS: or queue) current handshake (including CCS) message. + * + * - fill in handshake headers + * - update handshake checksum + * - DTLS: save message for resending + * - then pass to the record layer + * + * DTLS: except for HelloRequest, messages are only queued, and will only be + * actually sent when calling flight_transmit() or resend(). + * + * Inputs: + * - ssl->out_msglen: 4 + actual handshake message len + * (4 is the size of handshake headers for TLS) + * - ssl->out_msg[0]: the handshake type (ClientHello, ServerHello, etc) + * - ssl->out_msg + 4: the handshake message body + * + * Outputs, ie state before passing to flight_append() or write_record(): + * - ssl->out_msglen: the length of the record contents + * (including handshake headers but excluding record headers) + * - ssl->out_msg: the record contents (handshake headers + content) + */ +int mbedtls_ssl_write_handshake_msg_ext(mbedtls_ssl_context *ssl, + int update_checksum, + int force_flush) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const size_t hs_len = ssl->out_msglen - 4; + const unsigned char hs_type = ssl->out_msg[0]; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write handshake message")); + + /* + * Sanity checks + */ + if (ssl->out_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE && + ssl->out_msgtype != MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Whenever we send anything different from a + * HelloRequest we should be in a handshake - double check. */ + if (!(ssl->out_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + hs_type == MBEDTLS_SSL_HS_HELLO_REQUEST) && + ssl->handshake == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->handshake != NULL && + ssl->handshake->retransmit_state == MBEDTLS_SSL_RETRANS_SENDING) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +#endif + + /* Double-check that we did not exceed the bounds + * of the outgoing record buffer. + * This should never fail as the various message + * writing functions must obey the bounds of the + * outgoing record buffer, but better be safe. + * + * Note: We deliberately do not check for the MTU or MFL here. + */ + if (ssl->out_msglen > MBEDTLS_SSL_OUT_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Record too large: " + "size %" MBEDTLS_PRINTF_SIZET + ", maximum %" MBEDTLS_PRINTF_SIZET, + ssl->out_msglen, + (size_t) MBEDTLS_SSL_OUT_CONTENT_LEN)); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* + * Fill handshake headers + */ + if (ssl->out_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE) { + ssl->out_msg[1] = MBEDTLS_BYTE_2(hs_len); + ssl->out_msg[2] = MBEDTLS_BYTE_1(hs_len); + ssl->out_msg[3] = MBEDTLS_BYTE_0(hs_len); + + /* + * DTLS has additional fields in the Handshake layer, + * between the length field and the actual payload: + * uint16 message_seq; + * uint24 fragment_offset; + * uint24 fragment_length; + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + /* Make room for the additional DTLS fields */ + if (MBEDTLS_SSL_OUT_CONTENT_LEN - ssl->out_msglen < 8) { + MBEDTLS_SSL_DEBUG_MSG(1, ("DTLS handshake message too large: " + "size %" MBEDTLS_PRINTF_SIZET ", maximum %" + MBEDTLS_PRINTF_SIZET, + hs_len, + (size_t) (MBEDTLS_SSL_OUT_CONTENT_LEN - 12))); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + memmove(ssl->out_msg + 12, ssl->out_msg + 4, hs_len); + ssl->out_msglen += 8; + + /* Write message_seq and update it, except for HelloRequest */ + if (hs_type != MBEDTLS_SSL_HS_HELLO_REQUEST) { + MBEDTLS_PUT_UINT16_BE(ssl->handshake->out_msg_seq, ssl->out_msg, 4); + ++(ssl->handshake->out_msg_seq); + } else { + ssl->out_msg[4] = 0; + ssl->out_msg[5] = 0; + } + + /* Handshake hashes are computed without fragmentation, + * so set frag_offset = 0 and frag_len = hs_len for now */ + memset(ssl->out_msg + 6, 0x00, 3); + memcpy(ssl->out_msg + 9, ssl->out_msg + 1, 3); + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + /* Update running hashes of handshake messages seen */ + if (hs_type != MBEDTLS_SSL_HS_HELLO_REQUEST && update_checksum != 0) { + ret = ssl->handshake->update_checksum(ssl, ssl->out_msg, + ssl->out_msglen); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "update_checksum", ret); + return ret; + } + } + } + + /* Either send now, or just save to be sent (and resent) later */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + !(ssl->out_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + hs_type == MBEDTLS_SSL_HS_HELLO_REQUEST)) { + if ((ret = ssl_flight_append(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_flight_append", ret); + return ret; + } + } else +#endif + { + if ((ret = mbedtls_ssl_write_record(ssl, force_flush)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_record", ret); + return ret; + } + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write handshake message")); + + return 0; +} + +int mbedtls_ssl_finish_handshake_msg(mbedtls_ssl_context *ssl, + size_t buf_len, size_t msg_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t msg_with_header_len; + ((void) buf_len); + + /* Add reserved 4 bytes for handshake header */ + msg_with_header_len = msg_len + 4; + ssl->out_msglen = msg_with_header_len; + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_write_handshake_msg_ext(ssl, 0, 0)); + +cleanup: + return ret; +} + +/* + * Record layer functions + */ + +/* + * Write current record. + * + * Uses: + * - ssl->out_msgtype: type of the message (AppData, Handshake, Alert, CCS) + * - ssl->out_msglen: length of the record content (excl headers) + * - ssl->out_msg: record content + */ +int mbedtls_ssl_write_record(mbedtls_ssl_context *ssl, int force_flush) +{ + int ret, done = 0; + size_t len = ssl->out_msglen; + int flush = force_flush; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write record")); + + if (!done) { + unsigned i; + size_t protected_record_size; +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t out_buf_len = ssl->out_buf_len; +#else + size_t out_buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN; +#endif + /* Skip writing the record content type to after the encryption, + * as it may change when using the CID extension. */ + mbedtls_ssl_protocol_version tls_ver = ssl->tls_version; +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /* TLS 1.3 still uses the TLS 1.2 version identifier + * for backwards compatibility. */ + if (tls_ver == MBEDTLS_SSL_VERSION_TLS1_3) { + tls_ver = MBEDTLS_SSL_VERSION_TLS1_2; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + mbedtls_ssl_write_version(ssl->out_hdr + 1, ssl->conf->transport, + tls_ver); + + memcpy(ssl->out_ctr, ssl->cur_out_ctr, MBEDTLS_SSL_SEQUENCE_NUMBER_LEN); + MBEDTLS_PUT_UINT16_BE(len, ssl->out_len, 0); + + if (ssl->transform_out != NULL) { + mbedtls_record rec; + + rec.buf = ssl->out_iv; + rec.buf_len = out_buf_len - (ssl->out_iv - ssl->out_buf); + rec.data_len = ssl->out_msglen; + rec.data_offset = ssl->out_msg - rec.buf; + + memcpy(&rec.ctr[0], ssl->out_ctr, sizeof(rec.ctr)); + mbedtls_ssl_write_version(rec.ver, ssl->conf->transport, tls_ver); + rec.type = ssl->out_msgtype; + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* The CID is set by mbedtls_ssl_encrypt_buf(). */ + rec.cid_len = 0; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + if ((ret = mbedtls_ssl_encrypt_buf(ssl, ssl->transform_out, &rec, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_encrypt_buf", ret); + return ret; + } + + if (rec.data_offset != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Update the record content type and CID. */ + ssl->out_msgtype = rec.type; +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + memcpy(ssl->out_cid, rec.cid, rec.cid_len); +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + ssl->out_msglen = len = rec.data_len; + MBEDTLS_PUT_UINT16_BE(rec.data_len, ssl->out_len, 0); + } + + protected_record_size = len + mbedtls_ssl_out_hdr_len(ssl); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + /* In case of DTLS, double-check that we don't exceed + * the remaining space in the datagram. */ + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ret = ssl_get_remaining_space_in_datagram(ssl); + if (ret < 0) { + return ret; + } + + if (protected_record_size > (size_t) ret) { + /* Should never happen */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + /* Now write the potentially updated record content type. */ + ssl->out_hdr[0] = (unsigned char) ssl->out_msgtype; + + MBEDTLS_SSL_DEBUG_MSG(3, ("output record: msgtype = %u, " + "version = [%u:%u], msglen = %" MBEDTLS_PRINTF_SIZET, + ssl->out_hdr[0], ssl->out_hdr[1], + ssl->out_hdr[2], len)); + + MBEDTLS_SSL_DEBUG_BUF(4, "output record sent to network", + ssl->out_hdr, protected_record_size); + + ssl->out_left += protected_record_size; + ssl->out_hdr += protected_record_size; + mbedtls_ssl_update_out_pointers(ssl, ssl->transform_out); + + for (i = 8; i > mbedtls_ssl_ep_len(ssl); i--) { + if (++ssl->cur_out_ctr[i - 1] != 0) { + break; + } + } + + /* The loop goes to its end if the counter is wrapping */ + if (i == mbedtls_ssl_ep_len(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("outgoing message counter would wrap")); + return MBEDTLS_ERR_SSL_COUNTER_WRAPPING; + } + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + flush == SSL_DONT_FORCE_FLUSH) { + size_t remaining; + ret = ssl_get_remaining_payload_in_datagram(ssl); + if (ret < 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_get_remaining_payload_in_datagram", + ret); + return ret; + } + + remaining = (size_t) ret; + if (remaining == 0) { + flush = SSL_FORCE_FLUSH; + } else { + MBEDTLS_SSL_DEBUG_MSG(2, + ("Still %u bytes available in current datagram", + (unsigned) remaining)); + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + if ((flush == SSL_FORCE_FLUSH) && + (ret = mbedtls_ssl_flush_output(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flush_output", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write record")); + + return 0; +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_hs_is_proper_fragment(mbedtls_ssl_context *ssl) +{ + if (ssl->in_msglen < ssl->in_hslen || + memcmp(ssl->in_msg + 6, "\0\0\0", 3) != 0 || + memcmp(ssl->in_msg + 9, ssl->in_msg + 1, 3) != 0) { + return 1; + } + return 0; +} + +static uint32_t ssl_get_hs_frag_len(mbedtls_ssl_context const *ssl) +{ + return (ssl->in_msg[9] << 16) | + (ssl->in_msg[10] << 8) | + ssl->in_msg[11]; +} + +static uint32_t ssl_get_hs_frag_off(mbedtls_ssl_context const *ssl) +{ + return (ssl->in_msg[6] << 16) | + (ssl->in_msg[7] << 8) | + ssl->in_msg[8]; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_hs_header(mbedtls_ssl_context const *ssl) +{ + uint32_t msg_len, frag_off, frag_len; + + msg_len = ssl_get_hs_total_len(ssl); + frag_off = ssl_get_hs_frag_off(ssl); + frag_len = ssl_get_hs_frag_len(ssl); + + if (frag_off > msg_len) { + return -1; + } + + if (frag_len > msg_len - frag_off) { + return -1; + } + + if (frag_len + 12 > ssl->in_msglen) { + return -1; + } + + return 0; +} + +/* + * Mark bits in bitmask (used for DTLS HS reassembly) + */ +static void ssl_bitmask_set(unsigned char *mask, size_t offset, size_t len) +{ + unsigned int start_bits, end_bits; + + start_bits = 8 - (offset % 8); + if (start_bits != 8) { + size_t first_byte_idx = offset / 8; + + /* Special case */ + if (len <= start_bits) { + for (; len != 0; len--) { + mask[first_byte_idx] |= 1 << (start_bits - len); + } + + /* Avoid potential issues with offset or len becoming invalid */ + return; + } + + offset += start_bits; /* Now offset % 8 == 0 */ + len -= start_bits; + + for (; start_bits != 0; start_bits--) { + mask[first_byte_idx] |= 1 << (start_bits - 1); + } + } + + end_bits = len % 8; + if (end_bits != 0) { + size_t last_byte_idx = (offset + len) / 8; + + len -= end_bits; /* Now len % 8 == 0 */ + + for (; end_bits != 0; end_bits--) { + mask[last_byte_idx] |= 1 << (8 - end_bits); + } + } + + memset(mask + offset / 8, 0xFF, len / 8); +} + +/* + * Check that bitmask is full + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_bitmask_check(unsigned char *mask, size_t len) +{ + size_t i; + + for (i = 0; i < len / 8; i++) { + if (mask[i] != 0xFF) { + return -1; + } + } + + for (i = 0; i < len % 8; i++) { + if ((mask[len / 8] & (1 << (7 - i))) == 0) { + return -1; + } + } + + return 0; +} + +/* msg_len does not include the handshake header */ +static size_t ssl_get_reassembly_buffer_size(size_t msg_len, + unsigned add_bitmap) +{ + size_t alloc_len; + + alloc_len = 12; /* Handshake header */ + alloc_len += msg_len; /* Content buffer */ + + if (add_bitmap) { + alloc_len += msg_len / 8 + (msg_len % 8 != 0); /* Bitmap */ + + } + return alloc_len; +} + +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +static uint32_t ssl_get_hs_total_len(mbedtls_ssl_context const *ssl) +{ + return (ssl->in_msg[1] << 16) | + (ssl->in_msg[2] << 8) | + ssl->in_msg[3]; +} + +int mbedtls_ssl_prepare_handshake_record(mbedtls_ssl_context *ssl) +{ + if (ssl->in_msglen < mbedtls_ssl_hs_hdr_len(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("handshake message too short: %" MBEDTLS_PRINTF_SIZET, + ssl->in_msglen)); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + ssl->in_hslen = mbedtls_ssl_hs_hdr_len(ssl) + ssl_get_hs_total_len(ssl); + + MBEDTLS_SSL_DEBUG_MSG(3, ("handshake message: msglen =" + " %" MBEDTLS_PRINTF_SIZET ", type = %u, hslen = %" + MBEDTLS_PRINTF_SIZET, + ssl->in_msglen, ssl->in_msg[0], ssl->in_hslen)); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned int recv_msg_seq = (ssl->in_msg[4] << 8) | ssl->in_msg[5]; + + if (ssl_check_hs_header(ssl) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid handshake header")); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + if (ssl->handshake != NULL && + ((mbedtls_ssl_is_handshake_over(ssl) == 0 && + recv_msg_seq != ssl->handshake->in_msg_seq) || + (mbedtls_ssl_is_handshake_over(ssl) == 1 && + ssl->in_msg[0] != MBEDTLS_SSL_HS_CLIENT_HELLO))) { + if (recv_msg_seq > ssl->handshake->in_msg_seq) { + MBEDTLS_SSL_DEBUG_MSG(2, + ( + "received future handshake message of sequence number %u (next %u)", + recv_msg_seq, + ssl->handshake->in_msg_seq)); + return MBEDTLS_ERR_SSL_EARLY_MESSAGE; + } + + /* Retransmit only on last message from previous flight, to avoid + * too many retransmissions. + * Besides, No sane server ever retransmits HelloVerifyRequest */ + if (recv_msg_seq == ssl->handshake->in_flight_start_seq - 1 && + ssl->in_msg[0] != MBEDTLS_SSL_HS_HELLO_VERIFY_REQUEST) { + MBEDTLS_SSL_DEBUG_MSG(2, ("received message from last flight, " + "message_seq = %u, start_of_flight = %u", + recv_msg_seq, + ssl->handshake->in_flight_start_seq)); + + if ((ret = mbedtls_ssl_resend(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_resend", ret); + return ret; + } + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("dropping out-of-sequence message: " + "message_seq = %u, expected = %u", + recv_msg_seq, + ssl->handshake->in_msg_seq)); + } + + return MBEDTLS_ERR_SSL_CONTINUE_PROCESSING; + } + /* Wait until message completion to increment in_msg_seq */ + + /* Message reassembly is handled alongside buffering of future + * messages; the commonality is that both handshake fragments and + * future messages cannot be forwarded immediately to the + * handshake logic layer. */ + if (ssl_hs_is_proper_fragment(ssl) == 1) { + MBEDTLS_SSL_DEBUG_MSG(2, ("found fragmented DTLS handshake message")); + return MBEDTLS_ERR_SSL_EARLY_MESSAGE; + } + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + /* With TLS we don't handle fragmentation (for now) */ + if (ssl->in_msglen < ssl->in_hslen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("TLS handshake fragmentation not supported")); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + return 0; +} + +int mbedtls_ssl_update_handshake_status(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + + if (mbedtls_ssl_is_handshake_over(ssl) == 0 && hs != NULL) { + ret = ssl->handshake->update_checksum(ssl, ssl->in_msg, ssl->in_hslen); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "update_checksum", ret); + return ret; + } + } + + /* Handshake message is complete, increment counter */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->handshake != NULL) { + unsigned offset; + mbedtls_ssl_hs_buffer *hs_buf; + + /* Increment handshake sequence number */ + hs->in_msg_seq++; + + /* + * Clear up handshake buffering and reassembly structure. + */ + + /* Free first entry */ + ssl_buffering_free_slot(ssl, 0); + + /* Shift all other entries */ + for (offset = 0, hs_buf = &hs->buffering.hs[0]; + offset + 1 < MBEDTLS_SSL_MAX_BUFFERED_HS; + offset++, hs_buf++) { + *hs_buf = *(hs_buf + 1); + } + + /* Create a fresh last entry */ + memset(hs_buf, 0, sizeof(mbedtls_ssl_hs_buffer)); + } +#endif + return 0; +} + +/* + * DTLS anti-replay: RFC 6347 4.1.2.6 + * + * in_window is a field of bits numbered from 0 (lsb) to 63 (msb). + * Bit n is set iff record number in_window_top - n has been seen. + * + * Usually, in_window_top is the last record number seen and the lsb of + * in_window is set. The only exception is the initial state (record number 0 + * not seen yet). + */ +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) +void mbedtls_ssl_dtls_replay_reset(mbedtls_ssl_context *ssl) +{ + ssl->in_window_top = 0; + ssl->in_window = 0; +} + +static inline uint64_t ssl_load_six_bytes(unsigned char *buf) +{ + return ((uint64_t) buf[0] << 40) | + ((uint64_t) buf[1] << 32) | + ((uint64_t) buf[2] << 24) | + ((uint64_t) buf[3] << 16) | + ((uint64_t) buf[4] << 8) | + ((uint64_t) buf[5]); +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int mbedtls_ssl_dtls_record_replay_check(mbedtls_ssl_context *ssl, uint8_t *record_in_ctr) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *original_in_ctr; + + // save original in_ctr + original_in_ctr = ssl->in_ctr; + + // use counter from record + ssl->in_ctr = record_in_ctr; + + ret = mbedtls_ssl_dtls_replay_check((mbedtls_ssl_context const *) ssl); + + // restore the counter + ssl->in_ctr = original_in_ctr; + + return ret; +} + +/* + * Return 0 if sequence number is acceptable, -1 otherwise + */ +int mbedtls_ssl_dtls_replay_check(mbedtls_ssl_context const *ssl) +{ + uint64_t rec_seqnum = ssl_load_six_bytes(ssl->in_ctr + 2); + uint64_t bit; + + if (ssl->conf->anti_replay == MBEDTLS_SSL_ANTI_REPLAY_DISABLED) { + return 0; + } + + if (rec_seqnum > ssl->in_window_top) { + return 0; + } + + bit = ssl->in_window_top - rec_seqnum; + + if (bit >= 64) { + return -1; + } + + if ((ssl->in_window & ((uint64_t) 1 << bit)) != 0) { + return -1; + } + + return 0; +} + +/* + * Update replay window on new validated record + */ +void mbedtls_ssl_dtls_replay_update(mbedtls_ssl_context *ssl) +{ + uint64_t rec_seqnum = ssl_load_six_bytes(ssl->in_ctr + 2); + + if (ssl->conf->anti_replay == MBEDTLS_SSL_ANTI_REPLAY_DISABLED) { + return; + } + + if (rec_seqnum > ssl->in_window_top) { + /* Update window_top and the contents of the window */ + uint64_t shift = rec_seqnum - ssl->in_window_top; + + if (shift >= 64) { + ssl->in_window = 1; + } else { + ssl->in_window <<= shift; + ssl->in_window |= 1; + } + + ssl->in_window_top = rec_seqnum; + } else { + /* Mark that number as seen in the current window */ + uint64_t bit = ssl->in_window_top - rec_seqnum; + + if (bit < 64) { /* Always true, but be extra sure */ + ssl->in_window |= (uint64_t) 1 << bit; + } + } +} +#endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ + +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && defined(MBEDTLS_SSL_SRV_C) +/* + * Check if a datagram looks like a ClientHello with a valid cookie, + * and if it doesn't, generate a HelloVerifyRequest message. + * Both input and output include full DTLS headers. + * + * - if cookie is valid, return 0 + * - if ClientHello looks superficially valid but cookie is not, + * fill obuf and set olen, then + * return MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED + * - otherwise return a specific error code + */ +MBEDTLS_CHECK_RETURN_CRITICAL +MBEDTLS_STATIC_TESTABLE +int mbedtls_ssl_check_dtls_clihlo_cookie( + mbedtls_ssl_context *ssl, + const unsigned char *cli_id, size_t cli_id_len, + const unsigned char *in, size_t in_len, + unsigned char *obuf, size_t buf_len, size_t *olen) +{ + size_t sid_len, cookie_len, epoch, fragment_offset; + unsigned char *p; + + /* + * Structure of ClientHello with record and handshake headers, + * and expected values. We don't need to check a lot, more checks will be + * done when actually parsing the ClientHello - skipping those checks + * avoids code duplication and does not make cookie forging any easier. + * + * 0-0 ContentType type; copied, must be handshake + * 1-2 ProtocolVersion version; copied + * 3-4 uint16 epoch; copied, must be 0 + * 5-10 uint48 sequence_number; copied + * 11-12 uint16 length; (ignored) + * + * 13-13 HandshakeType msg_type; (ignored) + * 14-16 uint24 length; (ignored) + * 17-18 uint16 message_seq; copied + * 19-21 uint24 fragment_offset; copied, must be 0 + * 22-24 uint24 fragment_length; (ignored) + * + * 25-26 ProtocolVersion client_version; (ignored) + * 27-58 Random random; (ignored) + * 59-xx SessionID session_id; 1 byte len + sid_len content + * 60+ opaque cookie<0..2^8-1>; 1 byte len + content + * ... + * + * Minimum length is 61 bytes. + */ + MBEDTLS_SSL_DEBUG_MSG(4, ("check cookie: in_len=%u", + (unsigned) in_len)); + MBEDTLS_SSL_DEBUG_BUF(4, "cli_id", cli_id, cli_id_len); + if (in_len < 61) { + MBEDTLS_SSL_DEBUG_MSG(4, ("check cookie: record too short")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + epoch = MBEDTLS_GET_UINT16_BE(in, 3); + fragment_offset = MBEDTLS_GET_UINT24_BE(in, 19); + + if (in[0] != MBEDTLS_SSL_MSG_HANDSHAKE || epoch != 0 || + fragment_offset != 0) { + MBEDTLS_SSL_DEBUG_MSG(4, ("check cookie: not a good ClientHello")); + MBEDTLS_SSL_DEBUG_MSG(4, (" type=%u epoch=%u fragment_offset=%u", + in[0], (unsigned) epoch, + (unsigned) fragment_offset)); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + sid_len = in[59]; + if (59 + 1 + sid_len + 1 > in_len) { + MBEDTLS_SSL_DEBUG_MSG(4, ("check cookie: sid_len=%u > %u", + (unsigned) sid_len, + (unsigned) in_len - 61)); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + MBEDTLS_SSL_DEBUG_BUF(4, "sid received from network", + in + 60, sid_len); + + cookie_len = in[60 + sid_len]; + if (59 + 1 + sid_len + 1 + cookie_len > in_len) { + MBEDTLS_SSL_DEBUG_MSG(4, ("check cookie: cookie_len=%u > %u", + (unsigned) cookie_len, + (unsigned) (in_len - sid_len - 61))); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "cookie received from network", + in + sid_len + 61, cookie_len); + if (ssl->conf->f_cookie_check(ssl->conf->p_cookie, + in + sid_len + 61, cookie_len, + cli_id, cli_id_len) == 0) { + MBEDTLS_SSL_DEBUG_MSG(4, ("check cookie: valid")); + return 0; + } + + /* + * If we get here, we've got an invalid cookie, let's prepare HVR. + * + * 0-0 ContentType type; copied + * 1-2 ProtocolVersion version; copied + * 3-4 uint16 epoch; copied + * 5-10 uint48 sequence_number; copied + * 11-12 uint16 length; olen - 13 + * + * 13-13 HandshakeType msg_type; hello_verify_request + * 14-16 uint24 length; olen - 25 + * 17-18 uint16 message_seq; copied + * 19-21 uint24 fragment_offset; copied + * 22-24 uint24 fragment_length; olen - 25 + * + * 25-26 ProtocolVersion server_version; 0xfe 0xff + * 27-27 opaque cookie<0..2^8-1>; cookie_len = olen - 27, cookie + * + * Minimum length is 28. + */ + if (buf_len < 28) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + /* Copy most fields and adapt others */ + memcpy(obuf, in, 25); + obuf[13] = MBEDTLS_SSL_HS_HELLO_VERIFY_REQUEST; + obuf[25] = 0xfe; + obuf[26] = 0xff; + + /* Generate and write actual cookie */ + p = obuf + 28; + if (ssl->conf->f_cookie_write(ssl->conf->p_cookie, + &p, obuf + buf_len, + cli_id, cli_id_len) != 0) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + *olen = p - obuf; + + /* Go back and fill length fields */ + obuf[27] = (unsigned char) (*olen - 28); + + obuf[14] = obuf[22] = MBEDTLS_BYTE_2(*olen - 25); + obuf[15] = obuf[23] = MBEDTLS_BYTE_1(*olen - 25); + obuf[16] = obuf[24] = MBEDTLS_BYTE_0(*olen - 25); + + MBEDTLS_PUT_UINT16_BE(*olen - 13, obuf, 11); + + return MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED; +} + +/* + * Handle possible client reconnect with the same UDP quadruplet + * (RFC 6347 Section 4.2.8). + * + * Called by ssl_parse_record_header() in case we receive an epoch 0 record + * that looks like a ClientHello. + * + * - if the input looks like a ClientHello without cookies, + * send back HelloVerifyRequest, then return 0 + * - if the input looks like a ClientHello with a valid cookie, + * reset the session of the current context, and + * return MBEDTLS_ERR_SSL_CLIENT_RECONNECT + * - if anything goes wrong, return a specific error code + * + * This function is called (through ssl_check_client_reconnect()) when an + * unexpected record is found in ssl_get_next_record(), which will discard the + * record if we return 0, and bubble up the return value otherwise (this + * includes the case of MBEDTLS_ERR_SSL_CLIENT_RECONNECT and of unexpected + * errors, and is the right thing to do in both cases). + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_handle_possible_reconnect(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if (ssl->conf->f_cookie_write == NULL || + ssl->conf->f_cookie_check == NULL) { + /* If we can't use cookies to verify reachability of the peer, + * drop the record. */ + MBEDTLS_SSL_DEBUG_MSG(1, ("no cookie callbacks, " + "can't check reconnect validity")); + return 0; + } + + ret = mbedtls_ssl_check_dtls_clihlo_cookie( + ssl, + ssl->cli_id, ssl->cli_id_len, + ssl->in_buf, ssl->in_left, + ssl->out_buf, MBEDTLS_SSL_OUT_CONTENT_LEN, &len); + + MBEDTLS_SSL_DEBUG_RET(2, "mbedtls_ssl_check_dtls_clihlo_cookie", ret); + + if (ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) { + int send_ret; + MBEDTLS_SSL_DEBUG_MSG(1, ("sending HelloVerifyRequest")); + MBEDTLS_SSL_DEBUG_BUF(4, "output record sent to network", + ssl->out_buf, len); + /* Don't check write errors as we can't do anything here. + * If the error is permanent we'll catch it later, + * if it's not, then hopefully it'll work next time. */ + send_ret = ssl->f_send(ssl->p_bio, ssl->out_buf, len); + MBEDTLS_SSL_DEBUG_RET(2, "ssl->f_send", send_ret); + (void) send_ret; + + return 0; + } + + if (ret == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("cookie is valid, resetting context")); + if ((ret = mbedtls_ssl_session_reset_int(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "reset", ret); + return ret; + } + + return MBEDTLS_ERR_SSL_CLIENT_RECONNECT; + } + + return ret; +} +#endif /* MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE && MBEDTLS_SSL_SRV_C */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_record_type(uint8_t record_type) +{ + if (record_type != MBEDTLS_SSL_MSG_HANDSHAKE && + record_type != MBEDTLS_SSL_MSG_ALERT && + record_type != MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC && + record_type != MBEDTLS_SSL_MSG_APPLICATION_DATA) { + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + return 0; +} + +/* + * ContentType type; + * ProtocolVersion version; + * uint16 epoch; // DTLS only + * uint48 sequence_number; // DTLS only + * uint16 length; + * + * Return 0 if header looks sane (and, for DTLS, the record is expected) + * MBEDTLS_ERR_SSL_INVALID_RECORD if the header looks bad, + * MBEDTLS_ERR_SSL_UNEXPECTED_RECORD (DTLS only) if sane but unexpected. + * + * With DTLS, mbedtls_ssl_read_record() will: + * 1. proceed with the record if this function returns 0 + * 2. drop only the current record if this function returns UNEXPECTED_RECORD + * 3. return CLIENT_RECONNECT if this function return that value + * 4. drop the whole datagram if this function returns anything else. + * Point 2 is needed when the peer is resending, and we have already received + * the first record from a datagram but are still waiting for the others. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_record_header(mbedtls_ssl_context const *ssl, + unsigned char *buf, + size_t len, + mbedtls_record *rec) +{ + mbedtls_ssl_protocol_version tls_version; + + size_t const rec_hdr_type_offset = 0; + size_t const rec_hdr_type_len = 1; + + size_t const rec_hdr_version_offset = rec_hdr_type_offset + + rec_hdr_type_len; + size_t const rec_hdr_version_len = 2; + + size_t const rec_hdr_ctr_len = 8; +#if defined(MBEDTLS_SSL_PROTO_DTLS) + uint32_t rec_epoch; + size_t const rec_hdr_ctr_offset = rec_hdr_version_offset + + rec_hdr_version_len; + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + size_t const rec_hdr_cid_offset = rec_hdr_ctr_offset + + rec_hdr_ctr_len; + size_t rec_hdr_cid_len = 0; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + size_t rec_hdr_len_offset; /* To be determined */ + size_t const rec_hdr_len_len = 2; + + /* + * Check minimum lengths for record header. + */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + rec_hdr_len_offset = rec_hdr_ctr_offset + rec_hdr_ctr_len; + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + { + rec_hdr_len_offset = rec_hdr_version_offset + rec_hdr_version_len; + } + + if (len < rec_hdr_len_offset + rec_hdr_len_len) { + MBEDTLS_SSL_DEBUG_MSG(1, + ( + "datagram of length %u too small to hold DTLS record header of length %u", + (unsigned) len, + (unsigned) (rec_hdr_len_len + rec_hdr_len_len))); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + /* + * Parse and validate record content type + */ + + rec->type = buf[rec_hdr_type_offset]; + + /* Check record content type */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + rec->cid_len = 0; + + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->conf->cid_len != 0 && + rec->type == MBEDTLS_SSL_MSG_CID) { + /* Shift pointers to account for record header including CID + * struct { + * ContentType outer_type = tls12_cid; + * ProtocolVersion version; + * uint16 epoch; + * uint48 sequence_number; + * opaque cid[cid_length]; // Additional field compared to + * // default DTLS record format + * uint16 length; + * opaque enc_content[DTLSCiphertext.length]; + * } DTLSCiphertext; + */ + + /* So far, we only support static CID lengths + * fixed in the configuration. */ + rec_hdr_cid_len = ssl->conf->cid_len; + rec_hdr_len_offset += rec_hdr_cid_len; + + if (len < rec_hdr_len_offset + rec_hdr_len_len) { + MBEDTLS_SSL_DEBUG_MSG(1, + ( + "datagram of length %u too small to hold DTLS record header including CID, length %u", + (unsigned) len, + (unsigned) (rec_hdr_len_offset + rec_hdr_len_len))); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + /* configured CID len is guaranteed at most 255, see + * MBEDTLS_SSL_CID_OUT_LEN_MAX in check_config.h */ + rec->cid_len = (uint8_t) rec_hdr_cid_len; + memcpy(rec->cid, buf + rec_hdr_cid_offset, rec_hdr_cid_len); + } else +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + { + if (ssl_check_record_type(rec->type)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("unknown record type %u", + (unsigned) rec->type)); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + } + + /* + * Parse and validate record version + */ + rec->ver[0] = buf[rec_hdr_version_offset + 0]; + rec->ver[1] = buf[rec_hdr_version_offset + 1]; + tls_version = mbedtls_ssl_read_version(buf + rec_hdr_version_offset, + ssl->conf->transport); + + if (tls_version > ssl->conf->max_tls_version) { + MBEDTLS_SSL_DEBUG_MSG(1, ("TLS version mismatch: got %u, expected max %u", + (unsigned) tls_version, + (unsigned) ssl->conf->max_tls_version)); + + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + /* + * Parse/Copy record sequence number. + */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + /* Copy explicit record sequence number from input buffer. */ + memcpy(&rec->ctr[0], buf + rec_hdr_ctr_offset, + rec_hdr_ctr_len); + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + { + /* Copy implicit record sequence number from SSL context structure. */ + memcpy(&rec->ctr[0], ssl->in_ctr, rec_hdr_ctr_len); + } + + /* + * Parse record length. + */ + + rec->data_offset = rec_hdr_len_offset + rec_hdr_len_len; + rec->data_len = ((size_t) buf[rec_hdr_len_offset + 0] << 8) | + ((size_t) buf[rec_hdr_len_offset + 1] << 0); + MBEDTLS_SSL_DEBUG_BUF(4, "input record header", buf, rec->data_offset); + + MBEDTLS_SSL_DEBUG_MSG(3, ("input record: msgtype = %u, " + "version = [0x%x], msglen = %" MBEDTLS_PRINTF_SIZET, + rec->type, (unsigned) tls_version, rec->data_len)); + + rec->buf = buf; + rec->buf_len = rec->data_offset + rec->data_len; + + if (rec->data_len == 0) { + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + /* + * DTLS-related tests. + * Check epoch before checking length constraint because + * the latter varies with the epoch. E.g., if a ChangeCipherSpec + * message gets duplicated before the corresponding Finished message, + * the second ChangeCipherSpec should be discarded because it belongs + * to an old epoch, but not because its length is shorter than + * the minimum record length for packets using the new record transform. + * Note that these two kinds of failures are handled differently, + * as an unexpected record is silently skipped but an invalid + * record leads to the entire datagram being dropped. + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + rec_epoch = (rec->ctr[0] << 8) | rec->ctr[1]; + + /* Check that the datagram is large enough to contain a record + * of the advertised length. */ + if (len < rec->data_offset + rec->data_len) { + MBEDTLS_SSL_DEBUG_MSG(1, + ( + "Datagram of length %u too small to contain record of advertised length %u.", + (unsigned) len, + (unsigned) (rec->data_offset + rec->data_len))); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + /* Records from other, non-matching epochs are silently discarded. + * (The case of same-port Client reconnects must be considered in + * the caller). */ + if (rec_epoch != ssl->in_epoch) { + MBEDTLS_SSL_DEBUG_MSG(1, ("record from another epoch: " + "expected %u, received %lu", + ssl->in_epoch, (unsigned long) rec_epoch)); + + /* Records from the next epoch are considered for buffering + * (concretely: early Finished messages). */ + if (rec_epoch == (unsigned) ssl->in_epoch + 1) { + MBEDTLS_SSL_DEBUG_MSG(2, ("Consider record for buffering")); + return MBEDTLS_ERR_SSL_EARLY_MESSAGE; + } + + return MBEDTLS_ERR_SSL_UNEXPECTED_RECORD; + } +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + /* For records from the correct epoch, check whether their + * sequence number has been seen before. */ + else if (mbedtls_ssl_dtls_record_replay_check((mbedtls_ssl_context *) ssl, + &rec->ctr[0]) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("replayed record")); + return MBEDTLS_ERR_SSL_UNEXPECTED_RECORD; + } +#endif + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + return 0; +} + + +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && defined(MBEDTLS_SSL_SRV_C) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_client_reconnect(mbedtls_ssl_context *ssl) +{ + unsigned int rec_epoch = (ssl->in_ctr[0] << 8) | ssl->in_ctr[1]; + + /* + * Check for an epoch 0 ClientHello. We can't use in_msg here to + * access the first byte of record content (handshake type), as we + * have an active transform (possibly iv_len != 0), so use the + * fact that the record header len is 13 instead. + */ + if (rec_epoch == 0 && + ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + mbedtls_ssl_is_handshake_over(ssl) == 1 && + ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + ssl->in_left > 13 && + ssl->in_buf[13] == MBEDTLS_SSL_HS_CLIENT_HELLO) { + MBEDTLS_SSL_DEBUG_MSG(1, ("possible client reconnect " + "from the same port")); + return ssl_handle_possible_reconnect(ssl); + } + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE && MBEDTLS_SSL_SRV_C */ + +/* + * If applicable, decrypt record content + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_prepare_record_content(mbedtls_ssl_context *ssl, + mbedtls_record *rec) +{ + int ret, done = 0; + + MBEDTLS_SSL_DEBUG_BUF(4, "input record from network", + rec->buf, rec->buf_len); + + /* + * In TLS 1.3, always treat ChangeCipherSpec records + * as unencrypted. The only thing we do with them is + * check the length and content and ignore them. + */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ssl->transform_in != NULL && + ssl->transform_in->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + if (rec->type == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC) { + done = 1; + } + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + if (!done && ssl->transform_in != NULL) { + unsigned char const old_msg_type = rec->type; + + if ((ret = mbedtls_ssl_decrypt_buf(ssl, ssl->transform_in, + rec)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_decrypt_buf", ret); + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_CID && + ssl->conf->ignore_unexpected_cid + == MBEDTLS_SSL_UNEXPECTED_CID_IGNORE) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ignoring unexpected CID")); + ret = MBEDTLS_ERR_SSL_CONTINUE_PROCESSING; + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + return ret; + } + + if (old_msg_type != rec->type) { + MBEDTLS_SSL_DEBUG_MSG(4, ("record type after decrypt (before %d): %d", + old_msg_type, rec->type)); + } + + MBEDTLS_SSL_DEBUG_BUF(4, "input payload after decrypt", + rec->buf + rec->data_offset, rec->data_len); + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* We have already checked the record content type + * in ssl_parse_record_header(), failing or silently + * dropping the record in the case of an unknown type. + * + * Since with the use of CIDs, the record content type + * might change during decryption, re-check the record + * content type, but treat a failure as fatal this time. */ + if (ssl_check_record_type(rec->type)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("unknown record type")); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + if (rec->data_len == 0) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2 + && rec->type != MBEDTLS_SSL_MSG_APPLICATION_DATA) { + /* TLS v1.2 explicitly disallows zero-length messages which are not application data */ + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid zero-length message type: %d", ssl->in_msgtype)); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + ssl->nb_zero++; + + /* + * Three or more empty messages may be a DoS attack + * (excessive CPU consumption). + */ + if (ssl->nb_zero > 3) { + MBEDTLS_SSL_DEBUG_MSG(1, ("received four consecutive empty " + "messages, possible DoS attack")); + /* Treat the records as if they were not properly authenticated, + * thereby failing the connection if we see more than allowed + * by the configured bad MAC threshold. */ + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + } else { + ssl->nb_zero = 0; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ; /* in_ctr read from peer, not maintained internally */ + } else +#endif + { + unsigned i; + for (i = MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; + i > mbedtls_ssl_ep_len(ssl); i--) { + if (++ssl->in_ctr[i - 1] != 0) { + break; + } + } + + /* The loop goes to its end iff the counter is wrapping */ + if (i == mbedtls_ssl_ep_len(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("incoming message counter would wrap")); + return MBEDTLS_ERR_SSL_COUNTER_WRAPPING; + } + } + + } + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ssl_dtls_replay_update(ssl); + } +#endif + + /* Check actual (decrypted) record content length against + * configured maximum. */ + if (rec->data_len > MBEDTLS_SSL_IN_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad message length")); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + return 0; +} + +/* + * Read a record. + * + * Silently ignore non-fatal alert (and for DTLS, invalid records as well, + * RFC 6347 4.1.2.7) and continue reading until a valid record is found. + * + */ + +/* Helper functions for mbedtls_ssl_read_record(). */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_consume_current_message(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_next_record(mbedtls_ssl_context *ssl); +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_record_is_in_progress(mbedtls_ssl_context *ssl); + +int mbedtls_ssl_read_record(mbedtls_ssl_context *ssl, + unsigned update_hs_digest) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> read record")); + + if (ssl->keep_current_message == 0) { + do { + + ret = ssl_consume_current_message(ssl); + if (ret != 0) { + return ret; + } + + if (ssl_record_is_in_progress(ssl) == 0) { + int dtls_have_buffered = 0; +#if defined(MBEDTLS_SSL_PROTO_DTLS) + + /* We only check for buffered messages if the + * current datagram is fully consumed. */ + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl_next_record_is_in_datagram(ssl) == 0) { + if (ssl_load_buffered_message(ssl) == 0) { + dtls_have_buffered = 1; + } + } + +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + if (dtls_have_buffered == 0) { + ret = ssl_get_next_record(ssl); + if (ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING) { + continue; + } + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_get_next_record"), ret); + return ret; + } + } + } + + ret = mbedtls_ssl_handle_message_type(ssl); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ret == MBEDTLS_ERR_SSL_EARLY_MESSAGE) { + /* Buffer future message */ + ret = ssl_buffer_message(ssl); + if (ret != 0) { + return ret; + } + + ret = MBEDTLS_ERR_SSL_CONTINUE_PROCESSING; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + } while (MBEDTLS_ERR_SSL_NON_FATAL == ret || + MBEDTLS_ERR_SSL_CONTINUE_PROCESSING == ret); + + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_handle_message_type"), ret); + return ret; + } + + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + update_hs_digest == 1) { + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; + } + } + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("reuse previously read message")); + ssl->keep_current_message = 0; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= read record")); + + return 0; +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_next_record_is_in_datagram(mbedtls_ssl_context *ssl) +{ + if (ssl->in_left > ssl->next_record_offset) { + return 1; + } + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_load_buffered_message(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + mbedtls_ssl_hs_buffer *hs_buf; + int ret = 0; + + if (hs == NULL) { + return -1; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> ssl_load_buffered_message")); + + if (ssl->state == MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC || + ssl->state == MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC) { + /* Check if we have seen a ChangeCipherSpec before. + * If yes, synthesize a CCS record. */ + if (!hs->buffering.seen_ccs) { + MBEDTLS_SSL_DEBUG_MSG(2, ("CCS not seen in the current flight")); + ret = -1; + goto exit; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("Injecting buffered CCS message")); + ssl->in_msgtype = MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC; + ssl->in_msglen = 1; + ssl->in_msg[0] = 1; + + /* As long as they are equal, the exact value doesn't matter. */ + ssl->in_left = 0; + ssl->next_record_offset = 0; + + hs->buffering.seen_ccs = 0; + goto exit; + } + +#if defined(MBEDTLS_DEBUG_C) + /* Debug only */ + { + unsigned offset; + for (offset = 1; offset < MBEDTLS_SSL_MAX_BUFFERED_HS; offset++) { + hs_buf = &hs->buffering.hs[offset]; + if (hs_buf->is_valid == 1) { + MBEDTLS_SSL_DEBUG_MSG(2, ("Future message with sequence number %u %s buffered.", + hs->in_msg_seq + offset, + hs_buf->is_complete ? "fully" : "partially")); + } + } + } +#endif /* MBEDTLS_DEBUG_C */ + + /* Check if we have buffered and/or fully reassembled the + * next handshake message. */ + hs_buf = &hs->buffering.hs[0]; + if ((hs_buf->is_valid == 1) && (hs_buf->is_complete == 1)) { + /* Synthesize a record containing the buffered HS message. */ + size_t msg_len = (hs_buf->data[1] << 16) | + (hs_buf->data[2] << 8) | + hs_buf->data[3]; + + /* Double-check that we haven't accidentally buffered + * a message that doesn't fit into the input buffer. */ + if (msg_len + 12 > MBEDTLS_SSL_IN_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("Next handshake message has been buffered - load")); + MBEDTLS_SSL_DEBUG_BUF(3, "Buffered handshake message (incl. header)", + hs_buf->data, msg_len + 12); + + ssl->in_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->in_hslen = msg_len + 12; + ssl->in_msglen = msg_len + 12; + memcpy(ssl->in_msg, hs_buf->data, ssl->in_hslen); + + ret = 0; + goto exit; + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("Next handshake message %u not or only partially bufffered", + hs->in_msg_seq)); + } + + ret = -1; + +exit: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= ssl_load_buffered_message")); + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_buffer_make_space(mbedtls_ssl_context *ssl, + size_t desired) +{ + int offset; + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + MBEDTLS_SSL_DEBUG_MSG(2, ("Attempt to free buffered messages to have %u bytes available", + (unsigned) desired)); + + /* Get rid of future records epoch first, if such exist. */ + ssl_free_buffered_record(ssl); + + /* Check if we have enough space available now. */ + if (desired <= (MBEDTLS_SSL_DTLS_MAX_BUFFERING - + hs->buffering.total_bytes_buffered)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("Enough space available after freeing future epoch record")); + return 0; + } + + /* We don't have enough space to buffer the next expected handshake + * message. Remove buffers used for future messages to gain space, + * starting with the most distant one. */ + for (offset = MBEDTLS_SSL_MAX_BUFFERED_HS - 1; + offset >= 0; offset--) { + MBEDTLS_SSL_DEBUG_MSG(2, + ( + "Free buffering slot %d to make space for reassembly of next handshake message", + offset)); + + ssl_buffering_free_slot(ssl, (uint8_t) offset); + + /* Check if we have enough space available now. */ + if (desired <= (MBEDTLS_SSL_DTLS_MAX_BUFFERING - + hs->buffering.total_bytes_buffered)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("Enough space available after freeing buffered HS messages")); + return 0; + } + } + + return -1; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_buffer_message(mbedtls_ssl_context *ssl) +{ + int ret = 0; + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + + if (hs == NULL) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> ssl_buffer_message")); + + switch (ssl->in_msgtype) { + case MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC: + MBEDTLS_SSL_DEBUG_MSG(2, ("Remember CCS message")); + + hs->buffering.seen_ccs = 1; + break; + + case MBEDTLS_SSL_MSG_HANDSHAKE: + { + unsigned recv_msg_seq_offset; + unsigned recv_msg_seq = (ssl->in_msg[4] << 8) | ssl->in_msg[5]; + mbedtls_ssl_hs_buffer *hs_buf; + size_t msg_len = ssl->in_hslen - 12; + + /* We should never receive an old handshake + * message - double-check nonetheless. */ + if (recv_msg_seq < ssl->handshake->in_msg_seq) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + recv_msg_seq_offset = recv_msg_seq - ssl->handshake->in_msg_seq; + if (recv_msg_seq_offset >= MBEDTLS_SSL_MAX_BUFFERED_HS) { + /* Silently ignore -- message too far in the future */ + MBEDTLS_SSL_DEBUG_MSG(2, + ("Ignore future HS message with sequence number %u, " + "buffering window %u - %u", + recv_msg_seq, ssl->handshake->in_msg_seq, + ssl->handshake->in_msg_seq + MBEDTLS_SSL_MAX_BUFFERED_HS - + 1)); + + goto exit; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("Buffering HS message with sequence number %u, offset %u ", + recv_msg_seq, recv_msg_seq_offset)); + + hs_buf = &hs->buffering.hs[recv_msg_seq_offset]; + + /* Check if the buffering for this seq nr has already commenced. */ + if (!hs_buf->is_valid) { + size_t reassembly_buf_sz; + + hs_buf->is_fragmented = + (ssl_hs_is_proper_fragment(ssl) == 1); + + /* We copy the message back into the input buffer + * after reassembly, so check that it's not too large. + * This is an implementation-specific limitation + * and not one from the standard, hence it is not + * checked in ssl_check_hs_header(). */ + if (msg_len + 12 > MBEDTLS_SSL_IN_CONTENT_LEN) { + /* Ignore message */ + goto exit; + } + + /* Check if we have enough space to buffer the message. */ + if (hs->buffering.total_bytes_buffered > + MBEDTLS_SSL_DTLS_MAX_BUFFERING) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + reassembly_buf_sz = ssl_get_reassembly_buffer_size(msg_len, + hs_buf->is_fragmented); + + if (reassembly_buf_sz > (MBEDTLS_SSL_DTLS_MAX_BUFFERING - + hs->buffering.total_bytes_buffered)) { + if (recv_msg_seq_offset > 0) { + /* If we can't buffer a future message because + * of space limitations -- ignore. */ + MBEDTLS_SSL_DEBUG_MSG(2, + ("Buffering of future message of size %" + MBEDTLS_PRINTF_SIZET + " would exceed the compile-time limit %" + MBEDTLS_PRINTF_SIZET + " (already %" MBEDTLS_PRINTF_SIZET + " bytes buffered) -- ignore\n", + msg_len, (size_t) MBEDTLS_SSL_DTLS_MAX_BUFFERING, + hs->buffering.total_bytes_buffered)); + goto exit; + } else { + MBEDTLS_SSL_DEBUG_MSG(2, + ("Buffering of future message of size %" + MBEDTLS_PRINTF_SIZET + " would exceed the compile-time limit %" + MBEDTLS_PRINTF_SIZET + " (already %" MBEDTLS_PRINTF_SIZET + " bytes buffered) -- attempt to make space by freeing buffered future messages\n", + msg_len, (size_t) MBEDTLS_SSL_DTLS_MAX_BUFFERING, + hs->buffering.total_bytes_buffered)); + } + + if (ssl_buffer_make_space(ssl, reassembly_buf_sz) != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, + ("Reassembly of next message of size %" + MBEDTLS_PRINTF_SIZET + " (%" MBEDTLS_PRINTF_SIZET + " with bitmap) would exceed" + " the compile-time limit %" + MBEDTLS_PRINTF_SIZET + " (already %" MBEDTLS_PRINTF_SIZET + " bytes buffered) -- fail\n", + msg_len, + reassembly_buf_sz, + (size_t) MBEDTLS_SSL_DTLS_MAX_BUFFERING, + hs->buffering.total_bytes_buffered)); + ret = MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + goto exit; + } + } + + MBEDTLS_SSL_DEBUG_MSG(2, + ("initialize reassembly, total length = %" + MBEDTLS_PRINTF_SIZET, + msg_len)); + + hs_buf->data = mbedtls_calloc(1, reassembly_buf_sz); + if (hs_buf->data == NULL) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto exit; + } + hs_buf->data_len = reassembly_buf_sz; + + /* Prepare final header: copy msg_type, length and message_seq, + * then add standardised fragment_offset and fragment_length */ + memcpy(hs_buf->data, ssl->in_msg, 6); + memset(hs_buf->data + 6, 0, 3); + memcpy(hs_buf->data + 9, hs_buf->data + 1, 3); + + hs_buf->is_valid = 1; + + hs->buffering.total_bytes_buffered += reassembly_buf_sz; + } else { + /* Make sure msg_type and length are consistent */ + if (memcmp(hs_buf->data, ssl->in_msg, 4) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Fragment header mismatch - ignore")); + /* Ignore */ + goto exit; + } + } + + if (!hs_buf->is_complete) { + size_t frag_len, frag_off; + unsigned char * const msg = hs_buf->data + 12; + + /* + * Check and copy current fragment + */ + + /* Validation of header fields already done in + * mbedtls_ssl_prepare_handshake_record(). */ + frag_off = ssl_get_hs_frag_off(ssl); + frag_len = ssl_get_hs_frag_len(ssl); + + MBEDTLS_SSL_DEBUG_MSG(2, ("adding fragment, offset = %" MBEDTLS_PRINTF_SIZET + ", length = %" MBEDTLS_PRINTF_SIZET, + frag_off, frag_len)); + memcpy(msg + frag_off, ssl->in_msg + 12, frag_len); + + if (hs_buf->is_fragmented) { + unsigned char * const bitmask = msg + msg_len; + ssl_bitmask_set(bitmask, frag_off, frag_len); + hs_buf->is_complete = (ssl_bitmask_check(bitmask, + msg_len) == 0); + } else { + hs_buf->is_complete = 1; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("message %scomplete", + hs_buf->is_complete ? "" : "not yet ")); + } + + break; + } + + default: + /* We don't buffer other types of messages. */ + break; + } + +exit: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= ssl_buffer_message")); + return ret; +} +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_consume_current_message(mbedtls_ssl_context *ssl) +{ + /* + * Consume last content-layer message and potentially + * update in_msglen which keeps track of the contents' + * consumption state. + * + * (1) Handshake messages: + * Remove last handshake message, move content + * and adapt in_msglen. + * + * (2) Alert messages: + * Consume whole record content, in_msglen = 0. + * + * (3) Change cipher spec: + * Consume whole record content, in_msglen = 0. + * + * (4) Application data: + * Don't do anything - the record layer provides + * the application data as a stream transport + * and consumes through mbedtls_ssl_read only. + * + */ + + /* Case (1): Handshake messages */ + if (ssl->in_hslen != 0) { + /* Hard assertion to be sure that no application data + * is in flight, as corrupting ssl->in_msglen during + * ssl->in_offt != NULL is fatal. */ + if (ssl->in_offt != NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* + * Get next Handshake message in the current record + */ + + /* Notes: + * (1) in_hslen is not necessarily the size of the + * current handshake content: If DTLS handshake + * fragmentation is used, that's the fragment + * size instead. Using the total handshake message + * size here is faulty and should be changed at + * some point. + * (2) While it doesn't seem to cause problems, one + * has to be very careful not to assume that in_hslen + * is always <= in_msglen in a sensible communication. + * Again, it's wrong for DTLS handshake fragmentation. + * The following check is therefore mandatory, and + * should not be treated as a silently corrected assertion. + * Additionally, ssl->in_hslen might be arbitrarily out of + * bounds after handling a DTLS message with an unexpected + * sequence number, see mbedtls_ssl_prepare_handshake_record. + */ + if (ssl->in_hslen < ssl->in_msglen) { + ssl->in_msglen -= ssl->in_hslen; + memmove(ssl->in_msg, ssl->in_msg + ssl->in_hslen, + ssl->in_msglen); + + MBEDTLS_SSL_DEBUG_BUF(4, "remaining content in record", + ssl->in_msg, ssl->in_msglen); + } else { + ssl->in_msglen = 0; + } + + ssl->in_hslen = 0; + } + /* Case (4): Application data */ + else if (ssl->in_offt != NULL) { + return 0; + } + /* Everything else (CCS & Alerts) */ + else { + ssl->in_msglen = 0; + } + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_record_is_in_progress(mbedtls_ssl_context *ssl) +{ + if (ssl->in_msglen > 0) { + return 1; + } + + return 0; +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +static void ssl_free_buffered_record(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + if (hs == NULL) { + return; + } + + if (hs->buffering.future_record.data != NULL) { + hs->buffering.total_bytes_buffered -= + hs->buffering.future_record.len; + + mbedtls_free(hs->buffering.future_record.data); + hs->buffering.future_record.data = NULL; + } +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_load_buffered_record(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + unsigned char *rec; + size_t rec_len; + unsigned rec_epoch; +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t in_buf_len = ssl->in_buf_len; +#else + size_t in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN; +#endif + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return 0; + } + + if (hs == NULL) { + return 0; + } + + rec = hs->buffering.future_record.data; + rec_len = hs->buffering.future_record.len; + rec_epoch = hs->buffering.future_record.epoch; + + if (rec == NULL) { + return 0; + } + + /* Only consider loading future records if the + * input buffer is empty. */ + if (ssl_next_record_is_in_datagram(ssl) == 1) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> ssl_load_buffered_record")); + + if (rec_epoch != ssl->in_epoch) { + MBEDTLS_SSL_DEBUG_MSG(2, ("Buffered record not from current epoch.")); + goto exit; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("Found buffered record from current epoch - load")); + + /* Double-check that the record is not too large */ + if (rec_len > in_buf_len - (size_t) (ssl->in_hdr - ssl->in_buf)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + memcpy(ssl->in_hdr, rec, rec_len); + ssl->in_left = rec_len; + ssl->next_record_offset = 0; + + ssl_free_buffered_record(ssl); + +exit: + MBEDTLS_SSL_DEBUG_MSG(2, ("<= ssl_load_buffered_record")); + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_buffer_future_record(mbedtls_ssl_context *ssl, + mbedtls_record const *rec) +{ + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + + /* Don't buffer future records outside handshakes. */ + if (hs == NULL) { + return 0; + } + + /* Only buffer handshake records (we are only interested + * in Finished messages). */ + if (rec->type != MBEDTLS_SSL_MSG_HANDSHAKE) { + return 0; + } + + /* Don't buffer more than one future epoch record. */ + if (hs->buffering.future_record.data != NULL) { + return 0; + } + + /* Don't buffer record if there's not enough buffering space remaining. */ + if (rec->buf_len > (MBEDTLS_SSL_DTLS_MAX_BUFFERING - + hs->buffering.total_bytes_buffered)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("Buffering of future epoch record of size %" MBEDTLS_PRINTF_SIZET + " would exceed the compile-time limit %" MBEDTLS_PRINTF_SIZET + " (already %" MBEDTLS_PRINTF_SIZET + " bytes buffered) -- ignore\n", + rec->buf_len, (size_t) MBEDTLS_SSL_DTLS_MAX_BUFFERING, + hs->buffering.total_bytes_buffered)); + return 0; + } + + /* Buffer record */ + MBEDTLS_SSL_DEBUG_MSG(2, ("Buffer record from epoch %u", + ssl->in_epoch + 1U)); + MBEDTLS_SSL_DEBUG_BUF(3, "Buffered record", rec->buf, rec->buf_len); + + /* ssl_parse_record_header() only considers records + * of the next epoch as candidates for buffering. */ + hs->buffering.future_record.epoch = ssl->in_epoch + 1; + hs->buffering.future_record.len = rec->buf_len; + + hs->buffering.future_record.data = + mbedtls_calloc(1, hs->buffering.future_record.len); + if (hs->buffering.future_record.data == NULL) { + /* If we run out of RAM trying to buffer a + * record from the next epoch, just ignore. */ + return 0; + } + + memcpy(hs->buffering.future_record.data, rec->buf, rec->buf_len); + + hs->buffering.total_bytes_buffered += rec->buf_len; + return 0; +} + +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_next_record(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_record rec; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + /* We might have buffered a future record; if so, + * and if the epoch matches now, load it. + * On success, this call will set ssl->in_left to + * the length of the buffered record, so that + * the calls to ssl_fetch_input() below will + * essentially be no-ops. */ + ret = ssl_load_buffered_record(ssl); + if (ret != 0) { + return ret; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + /* Ensure that we have enough space available for the default form + * of TLS / DTLS record headers (5 Bytes for TLS, 13 Bytes for DTLS, + * with no space for CIDs counted in). */ + ret = mbedtls_ssl_fetch_input(ssl, mbedtls_ssl_in_hdr_len(ssl)); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); + return ret; + } + + ret = ssl_parse_record_header(ssl, ssl->in_hdr, ssl->in_left, &rec); + if (ret != 0) { +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + if (ret == MBEDTLS_ERR_SSL_EARLY_MESSAGE) { + ret = ssl_buffer_future_record(ssl, &rec); + if (ret != 0) { + return ret; + } + + /* Fall through to handling of unexpected records */ + ret = MBEDTLS_ERR_SSL_UNEXPECTED_RECORD; + } + + if (ret == MBEDTLS_ERR_SSL_UNEXPECTED_RECORD) { +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) && defined(MBEDTLS_SSL_SRV_C) + /* Reset in pointers to default state for TLS/DTLS records, + * assuming no CID and no offset between record content and + * record plaintext. */ + mbedtls_ssl_update_in_pointers(ssl); + + /* Setup internal message pointers from record structure. */ + ssl->in_msgtype = rec.type; +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + ssl->in_len = ssl->in_cid + rec.cid_len; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + ssl->in_iv = ssl->in_msg = ssl->in_len + 2; + ssl->in_msglen = rec.data_len; + + ret = ssl_check_client_reconnect(ssl); + MBEDTLS_SSL_DEBUG_RET(2, "ssl_check_client_reconnect", ret); + if (ret != 0) { + return ret; + } +#endif + + /* Skip unexpected record (but not whole datagram) */ + ssl->next_record_offset = rec.buf_len; + + MBEDTLS_SSL_DEBUG_MSG(1, ("discarding unexpected record " + "(header)")); + } else { + /* Skip invalid record and the rest of the datagram */ + ssl->next_record_offset = 0; + ssl->in_left = 0; + + MBEDTLS_SSL_DEBUG_MSG(1, ("discarding invalid record " + "(header)")); + } + + /* Get next record */ + return MBEDTLS_ERR_SSL_CONTINUE_PROCESSING; + } else +#endif + { + return ret; + } + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + /* Remember offset of next record within datagram. */ + ssl->next_record_offset = rec.buf_len; + if (ssl->next_record_offset < ssl->in_left) { + MBEDTLS_SSL_DEBUG_MSG(3, ("more than one record within datagram")); + } + } else +#endif + { + /* + * Fetch record contents from underlying transport. + */ + ret = mbedtls_ssl_fetch_input(ssl, rec.buf_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); + return ret; + } + + ssl->in_left = 0; + } + + /* + * Decrypt record contents. + */ + + if ((ret = ssl_prepare_record_content(ssl, &rec)) != 0) { +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + /* Silently discard invalid records */ + if (ret == MBEDTLS_ERR_SSL_INVALID_MAC) { + /* Except when waiting for Finished as a bad mac here + * probably means something went wrong in the handshake + * (eg wrong psk used, mitm downgrade attempt, etc.) */ + if (ssl->state == MBEDTLS_SSL_CLIENT_FINISHED || + ssl->state == MBEDTLS_SSL_SERVER_FINISHED) { +#if defined(MBEDTLS_SSL_ALL_ALERT_MESSAGES) + if (ret == MBEDTLS_ERR_SSL_INVALID_MAC) { + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC); + } +#endif + return ret; + } + + if (ssl->conf->badmac_limit != 0 && + ++ssl->badmac_seen >= ssl->conf->badmac_limit) { + MBEDTLS_SSL_DEBUG_MSG(1, ("too many records with bad MAC")); + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + + /* As above, invalid records cause + * dismissal of the whole datagram. */ + + ssl->next_record_offset = 0; + ssl->in_left = 0; + + MBEDTLS_SSL_DEBUG_MSG(1, ("discarding invalid record (mac)")); + return MBEDTLS_ERR_SSL_CONTINUE_PROCESSING; + } + + return ret; + } else +#endif + { + /* Error out (and send alert) on invalid records */ +#if defined(MBEDTLS_SSL_ALL_ALERT_MESSAGES) + if (ret == MBEDTLS_ERR_SSL_INVALID_MAC) { + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC); + } +#endif + return ret; + } + } + + + /* Reset in pointers to default state for TLS/DTLS records, + * assuming no CID and no offset between record content and + * record plaintext. */ + mbedtls_ssl_update_in_pointers(ssl); +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + ssl->in_len = ssl->in_cid + rec.cid_len; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + ssl->in_iv = ssl->in_len + 2; + + /* The record content type may change during decryption, + * so re-read it. */ + ssl->in_msgtype = rec.type; + /* Also update the input buffer, because unfortunately + * the server-side ssl_parse_client_hello() reparses the + * record header when receiving a ClientHello initiating + * a renegotiation. */ + ssl->in_hdr[0] = rec.type; + ssl->in_msg = rec.buf + rec.data_offset; + ssl->in_msglen = rec.data_len; + MBEDTLS_PUT_UINT16_BE(rec.data_len, ssl->in_len, 0); + + return 0; +} + +int mbedtls_ssl_handle_message_type(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * Handle particular types of records + */ + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE) { + if ((ret = mbedtls_ssl_prepare_handshake_record(ssl)) != 0) { + return ret; + } + } + + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC) { + if (ssl->in_msglen != 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid CCS message, len: %" MBEDTLS_PRINTF_SIZET, + ssl->in_msglen)); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + if (ssl->in_msg[0] != 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid CCS message, content: %02x", + ssl->in_msg[0])); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC && + ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC) { + if (ssl->handshake == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("dropping ChangeCipherSpec outside handshake")); + return MBEDTLS_ERR_SSL_UNEXPECTED_RECORD; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("received out-of-order ChangeCipherSpec - remember")); + return MBEDTLS_ERR_SSL_EARLY_MESSAGE; + } +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + MBEDTLS_SSL_DEBUG_MSG(1, + ("Ignore ChangeCipherSpec in TLS 1.3 compatibility mode")); + return MBEDTLS_ERR_SSL_CONTINUE_PROCESSING; +#else + MBEDTLS_SSL_DEBUG_MSG(1, + ("ChangeCipherSpec invalid in TLS 1.3 without compatibility mode")); + return MBEDTLS_ERR_SSL_INVALID_RECORD; +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + } + + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_ALERT) { + if (ssl->in_msglen != 2) { + /* Note: Standard allows for more than one 2 byte alert + to be packed in a single message, but Mbed TLS doesn't + currently support this. */ + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid alert message, len: %" MBEDTLS_PRINTF_SIZET, + ssl->in_msglen)); + return MBEDTLS_ERR_SSL_INVALID_RECORD; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("got an alert message, type: [%u:%u]", + ssl->in_msg[0], ssl->in_msg[1])); + + /* + * Ignore non-fatal alerts, except close_notify and no_renegotiation + */ + if (ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_FATAL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("is a fatal alert message (msg %d)", + ssl->in_msg[1])); + return MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE; + } + + if (ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING && + ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_CLOSE_NOTIFY) { + MBEDTLS_SSL_DEBUG_MSG(2, ("is a close notify message")); + return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY; + } + +#if defined(MBEDTLS_SSL_RENEGOTIATION_ENABLED) + if (ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING && + ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION) { + MBEDTLS_SSL_DEBUG_MSG(2, ("is a no renegotiation alert")); + /* Will be handled when trying to parse ServerHello */ + return 0; + } +#endif + /* Silently ignore: fetch new message */ + return MBEDTLS_ERR_SSL_NON_FATAL; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + /* Drop unexpected ApplicationData records, + * except at the beginning of renegotiations */ + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA && + mbedtls_ssl_is_handshake_over(ssl) == 0 +#if defined(MBEDTLS_SSL_RENEGOTIATION) + && !(ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS && + ssl->state == MBEDTLS_SSL_SERVER_HELLO) +#endif + ) { + MBEDTLS_SSL_DEBUG_MSG(1, ("dropping unexpected ApplicationData")); + return MBEDTLS_ERR_SSL_NON_FATAL; + } + + if (ssl->handshake != NULL && + mbedtls_ssl_is_handshake_over(ssl) == 1) { + mbedtls_ssl_handshake_wrapup_free_hs_transform(ssl); + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + return 0; +} + +int mbedtls_ssl_send_fatal_handshake_failure(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); +} + +int mbedtls_ssl_send_alert_message(mbedtls_ssl_context *ssl, + unsigned char level, + unsigned char message) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ssl == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (ssl->out_left != 0) { + return mbedtls_ssl_flush_output(ssl); + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> send alert message")); + MBEDTLS_SSL_DEBUG_MSG(3, ("send alert level=%u message=%u", level, message)); + + ssl->out_msgtype = MBEDTLS_SSL_MSG_ALERT; + ssl->out_msglen = 2; + ssl->out_msg[0] = level; + ssl->out_msg[1] = message; + + if ((ret = mbedtls_ssl_write_record(ssl, SSL_FORCE_FLUSH)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_record", ret); + return ret; + } + MBEDTLS_SSL_DEBUG_MSG(2, ("<= send alert message")); + + return 0; +} + +int mbedtls_ssl_write_change_cipher_spec(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write change cipher spec")); + + ssl->out_msgtype = MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC; + ssl->out_msglen = 1; + ssl->out_msg[0] = 1; + + ssl->state++; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write change cipher spec")); + + return 0; +} + +int mbedtls_ssl_parse_change_cipher_spec(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse change cipher spec")); + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad change cipher spec message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + /* CCS records are only accepted if they have length 1 and content '1', + * so we don't need to check this here. */ + + /* + * Switch to our negotiated transform and session parameters for inbound + * data. + */ + MBEDTLS_SSL_DEBUG_MSG(3, ("switching to new transform spec for inbound data")); +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + ssl->transform_in = ssl->transform_negotiate; +#endif + ssl->session_in = ssl->session_negotiate; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + mbedtls_ssl_dtls_replay_reset(ssl); +#endif + + /* Increment epoch */ + if (++ssl->in_epoch == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("DTLS epoch would wrap")); + /* This is highly unlikely to happen for legitimate reasons, so + treat it as an attack and don't send an alert. */ + return MBEDTLS_ERR_SSL_COUNTER_WRAPPING; + } + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + memset(ssl->in_ctr, 0, MBEDTLS_SSL_SEQUENCE_NUMBER_LEN); + + mbedtls_ssl_update_in_pointers(ssl); + + ssl->state++; + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse change cipher spec")); + + return 0; +} + +/* Once ssl->out_hdr as the address of the beginning of the + * next outgoing record is set, deduce the other pointers. + * + * Note: For TLS, we save the implicit record sequence number + * (entering MAC computation) in the 8 bytes before ssl->out_hdr, + * and the caller has to make sure there's space for this. + */ + +static size_t ssl_transform_get_explicit_iv_len( + mbedtls_ssl_transform const *transform) +{ + return transform->ivlen - transform->fixed_ivlen; +} + +void mbedtls_ssl_update_out_pointers(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform) +{ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ssl->out_ctr = ssl->out_hdr + 3; +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + ssl->out_cid = ssl->out_ctr + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; + ssl->out_len = ssl->out_cid; + if (transform != NULL) { + ssl->out_len += transform->out_cid_len; + } +#else /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + ssl->out_len = ssl->out_ctr + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + ssl->out_iv = ssl->out_len + 2; + } else +#endif + { + ssl->out_len = ssl->out_hdr + 3; +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + ssl->out_cid = ssl->out_len; +#endif + ssl->out_iv = ssl->out_hdr + 5; + } + + ssl->out_msg = ssl->out_iv; + /* Adjust out_msg to make space for explicit IV, if used. */ + if (transform != NULL) { + ssl->out_msg += ssl_transform_get_explicit_iv_len(transform); + } +} + +/* Once ssl->in_hdr as the address of the beginning of the + * next incoming record is set, deduce the other pointers. + * + * Note: For TLS, we save the implicit record sequence number + * (entering MAC computation) in the 8 bytes before ssl->in_hdr, + * and the caller has to make sure there's space for this. + */ + +void mbedtls_ssl_update_in_pointers(mbedtls_ssl_context *ssl) +{ + /* This function sets the pointers to match the case + * of unprotected TLS/DTLS records, with both ssl->in_iv + * and ssl->in_msg pointing to the beginning of the record + * content. + * + * When decrypting a protected record, ssl->in_msg + * will be shifted to point to the beginning of the + * record plaintext. + */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + /* This sets the header pointers to match records + * without CID. When we receive a record containing + * a CID, the fields are shifted accordingly in + * ssl_parse_record_header(). */ + ssl->in_ctr = ssl->in_hdr + 3; +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + ssl->in_cid = ssl->in_ctr + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; + ssl->in_len = ssl->in_cid; /* Default: no CID */ +#else /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + ssl->in_len = ssl->in_ctr + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + ssl->in_iv = ssl->in_len + 2; + } else +#endif + { + ssl->in_ctr = ssl->in_hdr - MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; + ssl->in_len = ssl->in_hdr + 3; +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + ssl->in_cid = ssl->in_len; +#endif + ssl->in_iv = ssl->in_hdr + 5; + } + + /* This will be adjusted at record decryption time. */ + ssl->in_msg = ssl->in_iv; +} + +/* + * Setup an SSL context + */ + +void mbedtls_ssl_reset_in_out_pointers(mbedtls_ssl_context *ssl) +{ + /* Set the incoming and outgoing record pointers. */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ssl->out_hdr = ssl->out_buf; + ssl->in_hdr = ssl->in_buf; + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + { + ssl->out_ctr = ssl->out_buf; + ssl->out_hdr = ssl->out_buf + 8; + ssl->in_hdr = ssl->in_buf + 8; + } + + /* Derive other internal pointers. */ + mbedtls_ssl_update_out_pointers(ssl, NULL /* no transform enabled */); + mbedtls_ssl_update_in_pointers(ssl); +} + +/* + * SSL get accessors + */ +size_t mbedtls_ssl_get_bytes_avail(const mbedtls_ssl_context *ssl) +{ + return ssl->in_offt == NULL ? 0 : ssl->in_msglen; +} + +int mbedtls_ssl_check_pending(const mbedtls_ssl_context *ssl) +{ + /* + * Case A: We're currently holding back + * a message for further processing. + */ + + if (ssl->keep_current_message == 1) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ssl_check_pending: record held back for processing")); + return 1; + } + + /* + * Case B: Further records are pending in the current datagram. + */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->in_left > ssl->next_record_offset) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ssl_check_pending: more records within current datagram")); + return 1; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + /* + * Case C: A handshake message is being processed. + */ + + if (ssl->in_hslen > 0 && ssl->in_hslen < ssl->in_msglen) { + MBEDTLS_SSL_DEBUG_MSG(3, + ("ssl_check_pending: more handshake messages within current record")); + return 1; + } + + /* + * Case D: An application data message is being processed + */ + if (ssl->in_offt != NULL) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ssl_check_pending: application data record is being processed")); + return 1; + } + + /* + * In all other cases, the rest of the message can be dropped. + * As in ssl_get_next_record, this needs to be adapted if + * we implement support for multiple alerts in single records. + */ + + MBEDTLS_SSL_DEBUG_MSG(3, ("ssl_check_pending: nothing pending")); + return 0; +} + + +int mbedtls_ssl_get_record_expansion(const mbedtls_ssl_context *ssl) +{ + size_t transform_expansion = 0; + const mbedtls_ssl_transform *transform = ssl->transform_out; + unsigned block_size; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_type_t key_type; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + size_t out_hdr_len = mbedtls_ssl_out_hdr_len(ssl); + + if (transform == NULL) { + return (int) out_hdr_len; + } + + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (transform->psa_alg == PSA_ALG_GCM || + transform->psa_alg == PSA_ALG_CCM || + transform->psa_alg == PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 8) || + transform->psa_alg == PSA_ALG_CHACHA20_POLY1305 || + transform->psa_alg == MBEDTLS_SSL_NULL_CIPHER) { + transform_expansion = transform->minlen; + } else if (transform->psa_alg == PSA_ALG_CBC_NO_PADDING) { + (void) psa_get_key_attributes(transform->psa_key_enc, &attr); + key_type = psa_get_key_type(&attr); + + block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type); + + /* Expansion due to the addition of the MAC. */ + transform_expansion += transform->maclen; + + /* Expansion due to the addition of CBC padding; + * Theoretically up to 256 bytes, but we never use + * more than the block size of the underlying cipher. */ + transform_expansion += block_size; + + /* For TLS 1.2 or higher, an explicit IV is added + * after the record header. */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + transform_expansion += block_size; +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + } else { + MBEDTLS_SSL_DEBUG_MSG(1, + ("Unsupported psa_alg spotted in mbedtls_ssl_get_record_expansion()")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +#else + switch (mbedtls_cipher_get_cipher_mode(&transform->cipher_ctx_enc)) { + case MBEDTLS_MODE_GCM: + case MBEDTLS_MODE_CCM: + case MBEDTLS_MODE_CHACHAPOLY: + case MBEDTLS_MODE_STREAM: + transform_expansion = transform->minlen; + break; + + case MBEDTLS_MODE_CBC: + + block_size = mbedtls_cipher_get_block_size( + &transform->cipher_ctx_enc); + + /* Expansion due to the addition of the MAC. */ + transform_expansion += transform->maclen; + + /* Expansion due to the addition of CBC padding; + * Theoretically up to 256 bytes, but we never use + * more than the block size of the underlying cipher. */ + transform_expansion += block_size; + + /* For TLS 1.2 or higher, an explicit IV is added + * after the record header. */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + transform_expansion += block_size; +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + break; + + default: + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + if (transform->out_cid_len != 0) { + transform_expansion += MBEDTLS_SSL_MAX_CID_EXPANSION; + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + return (int) (out_hdr_len + transform_expansion); +} + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +/* + * Check record counters and renegotiate if they're above the limit. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_ctr_renegotiate(mbedtls_ssl_context *ssl) +{ + size_t ep_len = mbedtls_ssl_ep_len(ssl); + int in_ctr_cmp; + int out_ctr_cmp; + + if (mbedtls_ssl_is_handshake_over(ssl) == 0 || + ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING || + ssl->conf->disable_renegotiation == MBEDTLS_SSL_RENEGOTIATION_DISABLED) { + return 0; + } + + in_ctr_cmp = memcmp(ssl->in_ctr + ep_len, + &ssl->conf->renego_period[ep_len], + MBEDTLS_SSL_SEQUENCE_NUMBER_LEN - ep_len); + out_ctr_cmp = memcmp(&ssl->cur_out_ctr[ep_len], + &ssl->conf->renego_period[ep_len], + sizeof(ssl->cur_out_ctr) - ep_len); + + if (in_ctr_cmp <= 0 && out_ctr_cmp <= 0) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("record counter limit reached: renegotiate")); + return mbedtls_ssl_renegotiate(ssl); +} +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_check_new_session_ticket(mbedtls_ssl_context *ssl) +{ + + if ((ssl->in_hslen == mbedtls_ssl_hs_hdr_len(ssl)) || + (ssl->in_msg[0] != MBEDTLS_SSL_HS_NEW_SESSION_TICKET)) { + return 0; + } + + ssl->keep_current_message = 1; + + MBEDTLS_SSL_DEBUG_MSG(3, ("NewSessionTicket received")); + mbedtls_ssl_handshake_set_state(ssl, + MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET); + + return MBEDTLS_ERR_SSL_WANT_READ; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_handle_hs_message_post_handshake(mbedtls_ssl_context *ssl) +{ + + MBEDTLS_SSL_DEBUG_MSG(3, ("received post-handshake message")); + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + int ret = ssl_tls13_check_new_session_ticket(ssl); + if (ret != 0) { + return ret; + } + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + + /* Fail in all other cases. */ + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +/* This function is called from mbedtls_ssl_read() when a handshake message is + * received after the initial handshake. In this context, handshake messages + * may only be sent for the purpose of initiating renegotiations. + * + * This function is introduced as a separate helper since the handling + * of post-handshake handshake messages changes significantly in TLS 1.3, + * and having a helper function allows to distinguish between TLS <= 1.2 and + * TLS 1.3 in the future without bloating the logic of mbedtls_ssl_read(). + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls12_handle_hs_message_post_handshake(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * - For client-side, expect SERVER_HELLO_REQUEST. + * - For server-side, expect CLIENT_HELLO. + * - Fail (TLS) or silently drop record (DTLS) in other cases. + */ + +#if defined(MBEDTLS_SSL_CLI_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && + (ssl->in_msg[0] != MBEDTLS_SSL_HS_HELLO_REQUEST || + ssl->in_hslen != mbedtls_ssl_hs_hdr_len(ssl))) { + MBEDTLS_SSL_DEBUG_MSG(1, ("handshake received (not HelloRequest)")); + + /* With DTLS, drop the packet (probably from last handshake) */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return 0; + } +#endif + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } +#endif /* MBEDTLS_SSL_CLI_C */ + +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + ssl->in_msg[0] != MBEDTLS_SSL_HS_CLIENT_HELLO) { + MBEDTLS_SSL_DEBUG_MSG(1, ("handshake received (not ClientHello)")); + + /* With DTLS, drop the packet (probably from last handshake) */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return 0; + } +#endif + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + /* Determine whether renegotiation attempt should be accepted */ + if (!(ssl->conf->disable_renegotiation == MBEDTLS_SSL_RENEGOTIATION_DISABLED || + (ssl->secure_renegotiation == MBEDTLS_SSL_LEGACY_RENEGOTIATION && + ssl->conf->allow_legacy_renegotiation == + MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION))) { + /* + * Accept renegotiation request + */ + + /* DTLS clients need to know renego is server-initiated */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + ssl->renego_status = MBEDTLS_SSL_RENEGOTIATION_PENDING; + } +#endif + ret = mbedtls_ssl_start_renegotiation(ssl); + if (ret != MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO && + ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_start_renegotiation", + ret); + return ret; + } + } else +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + { + /* + * Refuse renegotiation + */ + + MBEDTLS_SSL_DEBUG_MSG(3, ("refusing renegotiation, sending alert")); + + if ((ret = mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_WARNING, + MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION)) != 0) { + return ret; + } + } + + return 0; +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_handle_hs_message_post_handshake(mbedtls_ssl_context *ssl) +{ + /* Check protocol version and dispatch accordingly. */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + return ssl_tls13_handle_hs_message_post_handshake(ssl); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->tls_version <= MBEDTLS_SSL_VERSION_TLS1_2) { + return ssl_tls12_handle_hs_message_post_handshake(ssl); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + /* Should never happen */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} + +/* + * Receive application data decrypted from the SSL layer + */ +int mbedtls_ssl_read(mbedtls_ssl_context *ssl, unsigned char *buf, size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + + if (ssl == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> read")); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + if ((ret = mbedtls_ssl_flush_output(ssl)) != 0) { + return ret; + } + + if (ssl->handshake != NULL && + ssl->handshake->retransmit_state == MBEDTLS_SSL_RETRANS_SENDING) { + if ((ret = mbedtls_ssl_flight_transmit(ssl)) != 0) { + return ret; + } + } + } +#endif + + /* + * Check if renegotiation is necessary and/or handshake is + * in process. If yes, perform/continue, and fall through + * if an unexpected packet is received while the client + * is waiting for the ServerHello. + * + * (There is no equivalent to the last condition on + * the server-side as it is not treated as within + * a handshake while waiting for the ClientHello + * after a renegotiation request.) + */ + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + ret = ssl_check_ctr_renegotiate(ssl); + if (ret != MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO && + ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_check_ctr_renegotiate", ret); + return ret; + } +#endif + + if (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { + ret = mbedtls_ssl_handshake(ssl); + if (ret != MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO && + ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake", ret); + return ret; + } + } + + /* Loop as long as no application data record is available */ + while (ssl->in_offt == NULL) { + /* Start timer if not already running */ + if (ssl->f_get_timer != NULL && + ssl->f_get_timer(ssl->p_timer) == -1) { + mbedtls_ssl_set_timer(ssl, ssl->conf->read_timeout); + } + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + if (ret == MBEDTLS_ERR_SSL_CONN_EOF) { + return 0; + } + + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + if (ssl->in_msglen == 0 && + ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA) { + /* + * OpenSSL sends empty messages to randomize the IV + */ + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + if (ret == MBEDTLS_ERR_SSL_CONN_EOF) { + return 0; + } + + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + } + + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE) { + ret = ssl_handle_hs_message_post_handshake(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_handle_hs_message_post_handshake", + ret); + return ret; + } + + /* At this point, we don't know whether the renegotiation triggered + * by the post-handshake message has been completed or not. The cases + * to consider are the following: + * 1) The renegotiation is complete. In this case, no new record + * has been read yet. + * 2) The renegotiation is incomplete because the client received + * an application data record while awaiting the ServerHello. + * 3) The renegotiation is incomplete because the client received + * a non-handshake, non-application data message while awaiting + * the ServerHello. + * + * In each of these cases, looping will be the proper action: + * - For 1), the next iteration will read a new record and check + * if it's application data. + * - For 2), the loop condition isn't satisfied as application data + * is present, hence continue is the same as break + * - For 3), the loop condition is satisfied and read_record + * will re-deliver the message that was held back by the client + * when expecting the ServerHello. + */ + + continue; + } +#if defined(MBEDTLS_SSL_RENEGOTIATION) + else if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING) { + if (ssl->conf->renego_max_records >= 0) { + if (++ssl->renego_records_seen > ssl->conf->renego_max_records) { + MBEDTLS_SSL_DEBUG_MSG(1, ("renegotiation requested, " + "but not honored by client")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + } + } +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + + /* Fatal and closure alerts handled by mbedtls_ssl_read_record() */ + if (ssl->in_msgtype == MBEDTLS_SSL_MSG_ALERT) { + MBEDTLS_SSL_DEBUG_MSG(2, ("ignoring non-fatal non-closure alert")); + return MBEDTLS_ERR_SSL_WANT_READ; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_APPLICATION_DATA) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad application data message")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + ssl->in_offt = ssl->in_msg; + + /* We're going to return something now, cancel timer, + * except if handshake (renegotiation) is in progress */ + if (mbedtls_ssl_is_handshake_over(ssl) == 1) { + mbedtls_ssl_set_timer(ssl, 0); + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + /* If we requested renego but received AppData, resend HelloRequest. + * Do it now, after setting in_offt, to avoid taking this branch + * again if ssl_write_hello_request() returns WANT_WRITE */ +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER && + ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING) { + if ((ret = mbedtls_ssl_resend_hello_request(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_resend_hello_request", + ret); + return ret; + } + } +#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_RENEGOTIATION */ +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + } + + n = (len < ssl->in_msglen) + ? len : ssl->in_msglen; + + if (len != 0) { + memcpy(buf, ssl->in_offt, n); + ssl->in_msglen -= n; + } + + /* Zeroising the plaintext buffer to erase unused application data + from the memory. */ + mbedtls_platform_zeroize(ssl->in_offt, n); + + if (ssl->in_msglen == 0) { + /* all bytes consumed */ + ssl->in_offt = NULL; + ssl->keep_current_message = 0; + } else { + /* more data available */ + ssl->in_offt += n; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= read")); + + return (int) n; +} + +/* + * Send application data to be encrypted by the SSL layer, taking care of max + * fragment length and buffer size. + * + * According to RFC 5246 Section 6.2.1: + * + * Zero-length fragments of Application data MAY be sent as they are + * potentially useful as a traffic analysis countermeasure. + * + * Therefore, it is possible that the input message length is 0 and the + * corresponding return code is 0 on success. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_real(mbedtls_ssl_context *ssl, + const unsigned char *buf, size_t len) +{ + int ret = mbedtls_ssl_get_max_out_record_payload(ssl); + const size_t max_len = (size_t) ret; + + if (ret < 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_get_max_out_record_payload", ret); + return ret; + } + + if (len > max_len) { +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + MBEDTLS_SSL_DEBUG_MSG(1, ("fragment larger than the (negotiated) " + "maximum fragment length: %" MBEDTLS_PRINTF_SIZET + " > %" MBEDTLS_PRINTF_SIZET, + len, max_len)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } else +#endif + len = max_len; + } + + if (ssl->out_left != 0) { + /* + * The user has previously tried to send the data and + * MBEDTLS_ERR_SSL_WANT_WRITE or the message was only partially + * written. In this case, we expect the high-level write function + * (e.g. mbedtls_ssl_write()) to be called with the same parameters + */ + if ((ret = mbedtls_ssl_flush_output(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flush_output", ret); + return ret; + } + } else { + /* + * The user is trying to send a message the first time, so we need to + * copy the data into the internal buffers and setup the data structure + * to keep track of partial writes + */ + ssl->out_msglen = len; + ssl->out_msgtype = MBEDTLS_SSL_MSG_APPLICATION_DATA; + if (len > 0) { + memcpy(ssl->out_msg, buf, len); + } + + if ((ret = mbedtls_ssl_write_record(ssl, SSL_FORCE_FLUSH)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_record", ret); + return ret; + } + } + + return (int) len; +} + +/* + * Write application data (public-facing wrapper) + */ +int mbedtls_ssl_write(mbedtls_ssl_context *ssl, const unsigned char *buf, size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write")); + + if (ssl == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if ((ret = ssl_check_ctr_renegotiate(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_check_ctr_renegotiate", ret); + return ret; + } +#endif + + if (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { + if ((ret = mbedtls_ssl_handshake(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake", ret); + return ret; + } + } + + ret = ssl_write_real(ssl, buf, len); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write")); + + return ret; +} + +/* + * Notify the peer that the connection is being closed + */ +int mbedtls_ssl_close_notify(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ssl == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write close notify")); + + if (mbedtls_ssl_is_handshake_over(ssl) == 1) { + if ((ret = mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_WARNING, + MBEDTLS_SSL_ALERT_MSG_CLOSE_NOTIFY)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_send_alert_message", ret); + return ret; + } + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write close notify")); + + return 0; +} + +void mbedtls_ssl_transform_free(mbedtls_ssl_transform *transform) +{ + if (transform == NULL) { + return; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_destroy_key(transform->psa_key_enc); + psa_destroy_key(transform->psa_key_dec); +#else + mbedtls_cipher_free(&transform->cipher_ctx_enc); + mbedtls_cipher_free(&transform->cipher_ctx_dec); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_destroy_key(transform->psa_mac_enc); + psa_destroy_key(transform->psa_mac_dec); +#else + mbedtls_md_free(&transform->md_ctx_enc); + mbedtls_md_free(&transform->md_ctx_dec); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#endif + + mbedtls_platform_zeroize(transform, sizeof(mbedtls_ssl_transform)); +} + +void mbedtls_ssl_set_inbound_transform(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform) +{ + ssl->transform_in = transform; + memset(ssl->in_ctr, 0, MBEDTLS_SSL_SEQUENCE_NUMBER_LEN); +} + +void mbedtls_ssl_set_outbound_transform(mbedtls_ssl_context *ssl, + mbedtls_ssl_transform *transform) +{ + ssl->transform_out = transform; + memset(ssl->cur_out_ctr, 0, sizeof(ssl->cur_out_ctr)); +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +void mbedtls_ssl_buffering_free(mbedtls_ssl_context *ssl) +{ + unsigned offset; + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + + if (hs == NULL) { + return; + } + + ssl_free_buffered_record(ssl); + + for (offset = 0; offset < MBEDTLS_SSL_MAX_BUFFERED_HS; offset++) { + ssl_buffering_free_slot(ssl, offset); + } +} + +static void ssl_buffering_free_slot(mbedtls_ssl_context *ssl, + uint8_t slot) +{ + mbedtls_ssl_handshake_params * const hs = ssl->handshake; + mbedtls_ssl_hs_buffer * const hs_buf = &hs->buffering.hs[slot]; + + if (slot >= MBEDTLS_SSL_MAX_BUFFERED_HS) { + return; + } + + if (hs_buf->is_valid == 1) { + hs->buffering.total_bytes_buffered -= hs_buf->data_len; + mbedtls_platform_zeroize(hs_buf->data, hs_buf->data_len); + mbedtls_free(hs_buf->data); + memset(hs_buf, 0, sizeof(mbedtls_ssl_hs_buffer)); + } +} + +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +/* + * Convert version numbers to/from wire format + * and, for DTLS, to/from TLS equivalent. + * + * For TLS this is the identity. + * For DTLS, map as follows, then use 1's complement (v -> ~v): + * 1.x <-> 3.x+1 for x != 0 (DTLS 1.2 based on TLS 1.2) + * DTLS 1.0 is stored as TLS 1.1 internally + */ +void mbedtls_ssl_write_version(unsigned char version[2], int transport, + mbedtls_ssl_protocol_version tls_version) +{ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + tls_version = + ~(tls_version - (tls_version == 0x0302 ? 0x0202 : 0x0201)); + } +#else + ((void) transport); +#endif + MBEDTLS_PUT_UINT16_BE(tls_version, version, 0); +} + +uint16_t mbedtls_ssl_read_version(const unsigned char version[2], + int transport) +{ + uint16_t tls_version = MBEDTLS_GET_UINT16_BE(version, 0); +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + tls_version = + ~(tls_version - (tls_version == 0xfeff ? 0x0202 : 0x0201)); + } +#else + ((void) transport); +#endif + return tls_version; +} + +/* + * Send pending fatal alert. + * 0, No alert message. + * !0, if mbedtls_ssl_send_alert_message() returned in error, the error code it + * returned, ssl->alert_reason otherwise. + */ +int mbedtls_ssl_handle_pending_alert(mbedtls_ssl_context *ssl) +{ + int ret; + + /* No pending alert, return success*/ + if (ssl->send_alert == 0) { + return 0; + } + + ret = mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + ssl->alert_type); + + /* If mbedtls_ssl_send_alert_message() returned with MBEDTLS_ERR_SSL_WANT_WRITE, + * do not clear the alert to be able to send it later. + */ + if (ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ssl->send_alert = 0; + } + + if (ret != 0) { + return ret; + } + + return ssl->alert_reason; +} + +/* + * Set pending fatal alert flag. + */ +void mbedtls_ssl_pend_fatal_alert(mbedtls_ssl_context *ssl, + unsigned char alert_type, + int alert_reason) +{ + ssl->send_alert = 1; + ssl->alert_type = alert_type; + ssl->alert_reason = alert_reason; +} + +#endif /* MBEDTLS_SSL_TLS_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_ticket.c b/r5dev/thirdparty/mbedtls/ssl_ticket.c new file mode 100644 index 00000000..7d07d191 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_ticket.c @@ -0,0 +1,546 @@ +/* + * TLS server tickets callbacks implementation + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_TICKET_C) + +#include "mbedtls/platform.h" + +#include "ssl_misc.h" +#include "mbedtls/ssl_ticket.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" + +#include + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) +#endif + +/* + * Initialize context + */ +void mbedtls_ssl_ticket_init(mbedtls_ssl_ticket_context *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_ssl_ticket_context)); + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_init(&ctx->mutex); +#endif +} + +#define MAX_KEY_BYTES MBEDTLS_SSL_TICKET_MAX_KEY_BYTES + +#define TICKET_KEY_NAME_BYTES MBEDTLS_SSL_TICKET_KEY_NAME_BYTES +#define TICKET_IV_BYTES 12 +#define TICKET_CRYPT_LEN_BYTES 2 +#define TICKET_AUTH_TAG_BYTES 16 + +#define TICKET_MIN_LEN (TICKET_KEY_NAME_BYTES + \ + TICKET_IV_BYTES + \ + TICKET_CRYPT_LEN_BYTES + \ + TICKET_AUTH_TAG_BYTES) +#define TICKET_ADD_DATA_LEN (TICKET_KEY_NAME_BYTES + \ + TICKET_IV_BYTES + \ + TICKET_CRYPT_LEN_BYTES) + +/* + * Generate/update a key + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_ticket_gen_key(mbedtls_ssl_ticket_context *ctx, + unsigned char index) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char buf[MAX_KEY_BYTES] = { 0 }; + mbedtls_ssl_ticket_key *key = ctx->keys + index; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; +#endif + +#if defined(MBEDTLS_HAVE_TIME) + key->generation_time = mbedtls_time(NULL); +#endif + + if ((ret = ctx->f_rng(ctx->p_rng, key->name, sizeof(key->name))) != 0) { + return ret; + } + + if ((ret = ctx->f_rng(ctx->p_rng, buf, sizeof(buf))) != 0) { + return ret; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_set_key_usage_flags(&attributes, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, key->alg); + psa_set_key_type(&attributes, key->key_type); + psa_set_key_bits(&attributes, key->key_bits); + + ret = PSA_TO_MBEDTLS_ERR( + psa_import_key(&attributes, buf, + PSA_BITS_TO_BYTES(key->key_bits), + &key->key)); +#else + /* With GCM and CCM, same context can encrypt & decrypt */ + ret = mbedtls_cipher_setkey(&key->ctx, buf, + mbedtls_cipher_get_key_bitlen(&key->ctx), + MBEDTLS_ENCRYPT); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + mbedtls_platform_zeroize(buf, sizeof(buf)); + + return ret; +} + +/* + * Rotate/generate keys if necessary + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_ticket_update_keys(mbedtls_ssl_ticket_context *ctx) +{ +#if !defined(MBEDTLS_HAVE_TIME) + ((void) ctx); +#else + if (ctx->ticket_lifetime != 0) { + mbedtls_time_t current_time = mbedtls_time(NULL); + mbedtls_time_t key_time = ctx->keys[ctx->active].generation_time; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; +#endif + + if (current_time >= key_time && + (uint64_t) (current_time - key_time) < ctx->ticket_lifetime) { + return 0; + } + + ctx->active = 1 - ctx->active; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((status = psa_destroy_key(ctx->keys[ctx->active].key)) != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + return ssl_ticket_gen_key(ctx, ctx->active); + } else +#endif /* MBEDTLS_HAVE_TIME */ + return 0; +} + +/* + * Rotate active session ticket encryption key + */ +int mbedtls_ssl_ticket_rotate(mbedtls_ssl_ticket_context *ctx, + const unsigned char *name, size_t nlength, + const unsigned char *k, size_t klength, + uint32_t lifetime) +{ + const unsigned char idx = 1 - ctx->active; + mbedtls_ssl_ticket_key * const key = ctx->keys + idx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + const size_t bitlen = key->key_bits; +#else + const int bitlen = mbedtls_cipher_get_key_bitlen(&key->ctx); +#endif + + if (nlength < TICKET_KEY_NAME_BYTES || klength * 8 < (size_t) bitlen) { + return MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((status = psa_destroy_key(key->key)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + return ret; + } + + psa_set_key_usage_flags(&attributes, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, key->alg); + psa_set_key_type(&attributes, key->key_type); + psa_set_key_bits(&attributes, key->key_bits); + + if ((status = psa_import_key(&attributes, k, + PSA_BITS_TO_BYTES(key->key_bits), + &key->key)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + return ret; + } +#else + ret = mbedtls_cipher_setkey(&key->ctx, k, bitlen, MBEDTLS_ENCRYPT); + if (ret != 0) { + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + ctx->active = idx; + ctx->ticket_lifetime = lifetime; + memcpy(key->name, name, TICKET_KEY_NAME_BYTES); +#if defined(MBEDTLS_HAVE_TIME) + key->generation_time = mbedtls_time(NULL); +#endif + return 0; +} + +/* + * Setup context for actual use + */ +int mbedtls_ssl_ticket_setup(mbedtls_ssl_ticket_context *ctx, + int (*f_rng)(void *, unsigned char *, size_t), void *p_rng, + mbedtls_cipher_type_t cipher, + uint32_t lifetime) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t key_bits; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_algorithm_t alg; + psa_key_type_t key_type; +#else + const mbedtls_cipher_info_t *cipher_info; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (mbedtls_ssl_cipher_to_psa(cipher, TICKET_AUTH_TAG_BYTES, + &alg, &key_type, &key_bits) != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (PSA_ALG_IS_AEAD(alg) == 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } +#else + cipher_info = mbedtls_cipher_info_from_type(cipher); + + if (mbedtls_cipher_info_get_mode(cipher_info) != MBEDTLS_MODE_GCM && + mbedtls_cipher_info_get_mode(cipher_info) != MBEDTLS_MODE_CCM && + mbedtls_cipher_info_get_mode(cipher_info) != MBEDTLS_MODE_CHACHAPOLY) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + key_bits = mbedtls_cipher_info_get_key_bitlen(cipher_info); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (key_bits > 8 * MAX_KEY_BYTES) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ctx->f_rng = f_rng; + ctx->p_rng = p_rng; + + ctx->ticket_lifetime = lifetime; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ctx->keys[0].alg = alg; + ctx->keys[0].key_type = key_type; + ctx->keys[0].key_bits = key_bits; + + ctx->keys[1].alg = alg; + ctx->keys[1].key_type = key_type; + ctx->keys[1].key_bits = key_bits; +#else + if ((ret = mbedtls_cipher_setup(&ctx->keys[0].ctx, cipher_info)) != 0) { + return ret; + } + + if ((ret = mbedtls_cipher_setup(&ctx->keys[1].ctx, cipher_info)) != 0) { + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if ((ret = ssl_ticket_gen_key(ctx, 0)) != 0 || + (ret = ssl_ticket_gen_key(ctx, 1)) != 0) { + return ret; + } + + return 0; +} + +/* + * Create session ticket, with the following structure: + * + * struct { + * opaque key_name[4]; + * opaque iv[12]; + * opaque encrypted_state<0..2^16-1>; + * opaque tag[16]; + * } ticket; + * + * The key_name, iv, and length of encrypted_state are the additional + * authenticated data. + */ + +int mbedtls_ssl_ticket_write(void *p_ticket, + const mbedtls_ssl_session *session, + unsigned char *start, + const unsigned char *end, + size_t *tlen, + uint32_t *ticket_lifetime) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_ticket_context *ctx = p_ticket; + mbedtls_ssl_ticket_key *key; + unsigned char *key_name = start; + unsigned char *iv = start + TICKET_KEY_NAME_BYTES; + unsigned char *state_len_bytes = iv + TICKET_IV_BYTES; + unsigned char *state = state_len_bytes + TICKET_CRYPT_LEN_BYTES; + size_t clear_len, ciph_len; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; +#endif + + *tlen = 0; + + if (ctx == NULL || ctx->f_rng == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* We need at least 4 bytes for key_name, 12 for IV, 2 for len 16 for tag, + * in addition to session itself, that will be checked when writing it. */ + MBEDTLS_SSL_CHK_BUF_PTR(start, end, TICKET_MIN_LEN); + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + if ((ret = ssl_ticket_update_keys(ctx)) != 0) { + goto cleanup; + } + + key = &ctx->keys[ctx->active]; + + *ticket_lifetime = ctx->ticket_lifetime; + + memcpy(key_name, key->name, TICKET_KEY_NAME_BYTES); + + if ((ret = ctx->f_rng(ctx->p_rng, iv, TICKET_IV_BYTES)) != 0) { + goto cleanup; + } + + /* Dump session state */ + if ((ret = mbedtls_ssl_session_save(session, + state, end - state, + &clear_len)) != 0 || + (unsigned long) clear_len > 65535) { + goto cleanup; + } + MBEDTLS_PUT_UINT16_BE(clear_len, state_len_bytes, 0); + + /* Encrypt and authenticate */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((status = psa_aead_encrypt(key->key, key->alg, iv, TICKET_IV_BYTES, + key_name, TICKET_ADD_DATA_LEN, + state, clear_len, + state, end - state, + &ciph_len)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } +#else + if ((ret = mbedtls_cipher_auth_encrypt_ext(&key->ctx, + iv, TICKET_IV_BYTES, + /* Additional data: key name, IV and length */ + key_name, TICKET_ADD_DATA_LEN, + state, clear_len, + state, end - state, &ciph_len, + TICKET_AUTH_TAG_BYTES)) != 0) { + goto cleanup; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (ciph_len != clear_len + TICKET_AUTH_TAG_BYTES) { + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto cleanup; + } + + *tlen = TICKET_MIN_LEN + ciph_len - TICKET_AUTH_TAG_BYTES; + +cleanup: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +/* + * Select key based on name + */ +static mbedtls_ssl_ticket_key *ssl_ticket_select_key( + mbedtls_ssl_ticket_context *ctx, + const unsigned char name[4]) +{ + unsigned char i; + + for (i = 0; i < sizeof(ctx->keys) / sizeof(*ctx->keys); i++) { + if (memcmp(name, ctx->keys[i].name, 4) == 0) { + return &ctx->keys[i]; + } + } + + return NULL; +} + +/* + * Load session ticket (see mbedtls_ssl_ticket_write for structure) + */ +int mbedtls_ssl_ticket_parse(void *p_ticket, + mbedtls_ssl_session *session, + unsigned char *buf, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_ticket_context *ctx = p_ticket; + mbedtls_ssl_ticket_key *key; + unsigned char *key_name = buf; + unsigned char *iv = buf + TICKET_KEY_NAME_BYTES; + unsigned char *enc_len_p = iv + TICKET_IV_BYTES; + unsigned char *ticket = enc_len_p + TICKET_CRYPT_LEN_BYTES; + size_t enc_len, clear_len; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; +#endif + + if (ctx == NULL || ctx->f_rng == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (len < TICKET_MIN_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&ctx->mutex)) != 0) { + return ret; + } +#endif + + if ((ret = ssl_ticket_update_keys(ctx)) != 0) { + goto cleanup; + } + + enc_len = (enc_len_p[0] << 8) | enc_len_p[1]; + + if (len != TICKET_MIN_LEN + enc_len) { + ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + goto cleanup; + } + + /* Select key */ + if ((key = ssl_ticket_select_key(ctx, key_name)) == NULL) { + /* We can't know for sure but this is a likely option unless we're + * under attack - this is only informative anyway */ + ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED; + goto cleanup; + } + + /* Decrypt and authenticate */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((status = psa_aead_decrypt(key->key, key->alg, iv, TICKET_IV_BYTES, + key_name, TICKET_ADD_DATA_LEN, + ticket, enc_len + TICKET_AUTH_TAG_BYTES, + ticket, enc_len, &clear_len)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } +#else + if ((ret = mbedtls_cipher_auth_decrypt_ext(&key->ctx, + iv, TICKET_IV_BYTES, + /* Additional data: key name, IV and length */ + key_name, TICKET_ADD_DATA_LEN, + ticket, enc_len + TICKET_AUTH_TAG_BYTES, + ticket, enc_len, &clear_len, + TICKET_AUTH_TAG_BYTES)) != 0) { + if (ret == MBEDTLS_ERR_CIPHER_AUTH_FAILED) { + ret = MBEDTLS_ERR_SSL_INVALID_MAC; + } + + goto cleanup; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (clear_len != enc_len) { + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto cleanup; + } + + /* Actually load session */ + if ((ret = mbedtls_ssl_session_load(session, ticket, clear_len)) != 0) { + goto cleanup; + } + +#if defined(MBEDTLS_HAVE_TIME) + { + /* Check for expiration */ + mbedtls_time_t current_time = mbedtls_time(NULL); + + if (current_time < session->start || + (uint32_t) (current_time - session->start) > ctx->ticket_lifetime) { + ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED; + goto cleanup; + } + } +#endif + +cleanup: +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&ctx->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif + + return ret; +} + +/* + * Free context + */ +void mbedtls_ssl_ticket_free(mbedtls_ssl_ticket_context *ctx) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_destroy_key(ctx->keys[0].key); + psa_destroy_key(ctx->keys[1].key); +#else + mbedtls_cipher_free(&ctx->keys[0].ctx); + mbedtls_cipher_free(&ctx->keys[1].ctx); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_THREADING_C) + mbedtls_mutex_free(&ctx->mutex); +#endif + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_ssl_ticket_context)); +} + +#endif /* MBEDTLS_SSL_TICKET_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls.c b/r5dev/thirdparty/mbedtls/ssl_tls.c new file mode 100644 index 00000000..a6129da3 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls.c @@ -0,0 +1,9695 @@ +/* + * TLS shared functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * http://www.ietf.org/rfc/rfc2246.txt + * http://www.ietf.org/rfc/rfc4346.txt + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_TLS_C) + +#include "mbedtls/platform.h" + +#include "mbedtls/ssl.h" +#include "ssl_client.h" +#include "ssl_debug_helpers.h" +#include "ssl_misc.h" + +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/version.h" +#include "mbedtls/constant_time.h" + +#include + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "mbedtls/psa_util.h" +#include "psa/crypto.h" +#endif +#include "mbedtls/legacy_or_psa.h" + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#include "mbedtls/oid.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) +#define PSA_TO_MD_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_md_errors, \ + psa_generic_status_to_mbedtls) +#endif + +#if defined(MBEDTLS_TEST_HOOKS) +static mbedtls_ssl_chk_buf_ptr_args chk_buf_ptr_fail_args; + +void mbedtls_ssl_set_chk_buf_ptr_fail_args( + const uint8_t *cur, const uint8_t *end, size_t need) +{ + chk_buf_ptr_fail_args.cur = cur; + chk_buf_ptr_fail_args.end = end; + chk_buf_ptr_fail_args.need = need; +} + +void mbedtls_ssl_reset_chk_buf_ptr_fail_args(void) +{ + memset(&chk_buf_ptr_fail_args, 0, sizeof(chk_buf_ptr_fail_args)); +} + +int mbedtls_ssl_cmp_chk_buf_ptr_fail_args(mbedtls_ssl_chk_buf_ptr_args *args) +{ + return (chk_buf_ptr_fail_args.cur != args->cur) || + (chk_buf_ptr_fail_args.end != args->end) || + (chk_buf_ptr_fail_args.need != args->need); +} +#endif /* MBEDTLS_TEST_HOOKS */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +/* Top-level Connection ID API */ + +int mbedtls_ssl_conf_cid(mbedtls_ssl_config *conf, + size_t len, + int ignore_other_cid) +{ + if (len > MBEDTLS_SSL_CID_IN_LEN_MAX) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (ignore_other_cid != MBEDTLS_SSL_UNEXPECTED_CID_FAIL && + ignore_other_cid != MBEDTLS_SSL_UNEXPECTED_CID_IGNORE) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + conf->ignore_unexpected_cid = ignore_other_cid; + conf->cid_len = len; + return 0; +} + +int mbedtls_ssl_set_cid(mbedtls_ssl_context *ssl, + int enable, + unsigned char const *own_cid, + size_t own_cid_len) +{ + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl->negotiate_cid = enable; + if (enable == MBEDTLS_SSL_CID_DISABLED) { + MBEDTLS_SSL_DEBUG_MSG(3, ("Disable use of CID extension.")); + return 0; + } + MBEDTLS_SSL_DEBUG_MSG(3, ("Enable use of CID extension.")); + MBEDTLS_SSL_DEBUG_BUF(3, "Own CID", own_cid, own_cid_len); + + if (own_cid_len != ssl->conf->cid_len) { + MBEDTLS_SSL_DEBUG_MSG(3, ("CID length %u does not match CID length %u in config", + (unsigned) own_cid_len, + (unsigned) ssl->conf->cid_len)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + memcpy(ssl->own_cid, own_cid, own_cid_len); + /* Truncation is not an issue here because + * MBEDTLS_SSL_CID_IN_LEN_MAX at most 255. */ + ssl->own_cid_len = (uint8_t) own_cid_len; + + return 0; +} + +int mbedtls_ssl_get_own_cid(mbedtls_ssl_context *ssl, + int *enabled, + unsigned char own_cid[MBEDTLS_SSL_CID_OUT_LEN_MAX], + size_t *own_cid_len) +{ + *enabled = MBEDTLS_SSL_CID_DISABLED; + + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* We report MBEDTLS_SSL_CID_DISABLED in case the CID length is + * zero as this is indistinguishable from not requesting to use + * the CID extension. */ + if (ssl->own_cid_len == 0 || ssl->negotiate_cid == MBEDTLS_SSL_CID_DISABLED) { + return 0; + } + + if (own_cid_len != NULL) { + *own_cid_len = ssl->own_cid_len; + if (own_cid != NULL) { + memcpy(own_cid, ssl->own_cid, ssl->own_cid_len); + } + } + + *enabled = MBEDTLS_SSL_CID_ENABLED; + + return 0; +} + +int mbedtls_ssl_get_peer_cid(mbedtls_ssl_context *ssl, + int *enabled, + unsigned char peer_cid[MBEDTLS_SSL_CID_OUT_LEN_MAX], + size_t *peer_cid_len) +{ + *enabled = MBEDTLS_SSL_CID_DISABLED; + + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM || + mbedtls_ssl_is_handshake_over(ssl) == 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* We report MBEDTLS_SSL_CID_DISABLED in case the CID extensions + * were used, but client and server requested the empty CID. + * This is indistinguishable from not using the CID extension + * in the first place. */ + if (ssl->transform_in->in_cid_len == 0 && + ssl->transform_in->out_cid_len == 0) { + return 0; + } + + if (peer_cid_len != NULL) { + *peer_cid_len = ssl->transform_in->out_cid_len; + if (peer_cid != NULL) { + memcpy(peer_cid, ssl->transform_in->out_cid, + ssl->transform_in->out_cid_len); + } + } + + *enabled = MBEDTLS_SSL_CID_ENABLED; + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +/* + * Convert max_fragment_length codes to length. + * RFC 6066 says: + * enum{ + * 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255) + * } MaxFragmentLength; + * and we add 0 -> extension unused + */ +static unsigned int ssl_mfl_code_to_length(int mfl) +{ + switch (mfl) { + case MBEDTLS_SSL_MAX_FRAG_LEN_NONE: + return MBEDTLS_TLS_EXT_ADV_CONTENT_LEN; + case MBEDTLS_SSL_MAX_FRAG_LEN_512: + return 512; + case MBEDTLS_SSL_MAX_FRAG_LEN_1024: + return 1024; + case MBEDTLS_SSL_MAX_FRAG_LEN_2048: + return 2048; + case MBEDTLS_SSL_MAX_FRAG_LEN_4096: + return 4096; + default: + return MBEDTLS_TLS_EXT_ADV_CONTENT_LEN; + } +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +int mbedtls_ssl_session_copy(mbedtls_ssl_session *dst, + const mbedtls_ssl_session *src) +{ + mbedtls_ssl_session_free(dst); + memcpy(dst, src, sizeof(mbedtls_ssl_session)); +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + dst->ticket = NULL; +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + dst->hostname = NULL; +#endif +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + if (src->peer_cert != NULL) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + dst->peer_cert = mbedtls_calloc(1, sizeof(mbedtls_x509_crt)); + if (dst->peer_cert == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + mbedtls_x509_crt_init(dst->peer_cert); + + if ((ret = mbedtls_x509_crt_parse_der(dst->peer_cert, src->peer_cert->raw.p, + src->peer_cert->raw.len)) != 0) { + mbedtls_free(dst->peer_cert); + dst->peer_cert = NULL; + return ret; + } + } +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (src->peer_cert_digest != NULL) { + dst->peer_cert_digest = + mbedtls_calloc(1, src->peer_cert_digest_len); + if (dst->peer_cert_digest == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(dst->peer_cert_digest, src->peer_cert_digest, + src->peer_cert_digest_len); + dst->peer_cert_digest_type = src->peer_cert_digest_type; + dst->peer_cert_digest_len = src->peer_cert_digest_len; + } +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + if (src->ticket != NULL) { + dst->ticket = mbedtls_calloc(1, src->ticket_len); + if (dst->ticket == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(dst->ticket, src->ticket, src->ticket_len); + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (src->endpoint == MBEDTLS_SSL_IS_CLIENT) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + ret = mbedtls_ssl_session_set_hostname(dst, src->hostname); + if (ret != 0) { + return ret; + } + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && + MBEDTLS_SSL_SERVER_NAME_INDICATION */ +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + + return 0; +} + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) +MBEDTLS_CHECK_RETURN_CRITICAL +static int resize_buffer(unsigned char **buffer, size_t len_new, size_t *len_old) +{ + unsigned char *resized_buffer = mbedtls_calloc(1, len_new); + if (resized_buffer == NULL) { + return -1; + } + + /* We want to copy len_new bytes when downsizing the buffer, and + * len_old bytes when upsizing, so we choose the smaller of two sizes, + * to fit one buffer into another. Size checks, ensuring that no data is + * lost, are done outside of this function. */ + memcpy(resized_buffer, *buffer, + (len_new < *len_old) ? len_new : *len_old); + mbedtls_platform_zeroize(*buffer, *len_old); + mbedtls_free(*buffer); + + *buffer = resized_buffer; + *len_old = len_new; + + return 0; +} + +static void handle_buffer_resizing(mbedtls_ssl_context *ssl, int downsizing, + size_t in_buf_new_len, + size_t out_buf_new_len) +{ + int modified = 0; + size_t written_in = 0, iv_offset_in = 0, len_offset_in = 0; + size_t written_out = 0, iv_offset_out = 0, len_offset_out = 0; + if (ssl->in_buf != NULL) { + written_in = ssl->in_msg - ssl->in_buf; + iv_offset_in = ssl->in_iv - ssl->in_buf; + len_offset_in = ssl->in_len - ssl->in_buf; + if (downsizing ? + ssl->in_buf_len > in_buf_new_len && ssl->in_left < in_buf_new_len : + ssl->in_buf_len < in_buf_new_len) { + if (resize_buffer(&ssl->in_buf, in_buf_new_len, &ssl->in_buf_len) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("input buffer resizing failed - out of memory")); + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("Reallocating in_buf to %" MBEDTLS_PRINTF_SIZET, + in_buf_new_len)); + modified = 1; + } + } + } + + if (ssl->out_buf != NULL) { + written_out = ssl->out_msg - ssl->out_buf; + iv_offset_out = ssl->out_iv - ssl->out_buf; + len_offset_out = ssl->out_len - ssl->out_buf; + if (downsizing ? + ssl->out_buf_len > out_buf_new_len && ssl->out_left < out_buf_new_len : + ssl->out_buf_len < out_buf_new_len) { + if (resize_buffer(&ssl->out_buf, out_buf_new_len, &ssl->out_buf_len) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("output buffer resizing failed - out of memory")); + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("Reallocating out_buf to %" MBEDTLS_PRINTF_SIZET, + out_buf_new_len)); + modified = 1; + } + } + } + if (modified) { + /* Update pointers here to avoid doing it twice. */ + mbedtls_ssl_reset_in_out_pointers(ssl); + /* Fields below might not be properly updated with record + * splitting or with CID, so they are manually updated here. */ + ssl->out_msg = ssl->out_buf + written_out; + ssl->out_len = ssl->out_buf + len_offset_out; + ssl->out_iv = ssl->out_buf + iv_offset_out; + + ssl->in_msg = ssl->in_buf + written_in; + ssl->in_len = ssl->in_buf + len_offset_in; + ssl->in_iv = ssl->in_buf + iv_offset_in; + } +} +#endif /* MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) +typedef int (*tls_prf_fn)(const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen); + +static tls_prf_fn ssl_tls12prf_from_cs(int ciphersuite_id); + +#endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ + +/* Type for the TLS PRF */ +typedef int ssl_tls_prf_t(const unsigned char *, size_t, const char *, + const unsigned char *, size_t, + unsigned char *, size_t); + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls12_populate_transform(mbedtls_ssl_transform *transform, + int ciphersuite, + const unsigned char master[48], +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + int encrypt_then_mac, +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + ssl_tls_prf_t tls_prf, + const unsigned char randbytes[64], + mbedtls_ssl_protocol_version tls_version, + unsigned endpoint, + const mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +MBEDTLS_CHECK_RETURN_CRITICAL +static int tls_prf_sha256(const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen); +static int ssl_calc_verify_tls_sha256(const mbedtls_ssl_context *, unsigned char *, size_t *); +static int ssl_calc_finished_tls_sha256(mbedtls_ssl_context *, unsigned char *, int); + +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +MBEDTLS_CHECK_RETURN_CRITICAL +static int tls_prf_sha384(const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen); + +static int ssl_calc_verify_tls_sha384(const mbedtls_ssl_context *, unsigned char *, size_t *); +static int ssl_calc_finished_tls_sha384(mbedtls_ssl_context *, unsigned char *, int); +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +static size_t ssl_tls12_session_save(const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len); + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls12_session_load(mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len); +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +static int ssl_update_checksum_start(mbedtls_ssl_context *, const unsigned char *, size_t); + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +static int ssl_update_checksum_sha256(mbedtls_ssl_context *, const unsigned char *, size_t); +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +static int ssl_update_checksum_sha384(mbedtls_ssl_context *, const unsigned char *, size_t); +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +int mbedtls_ssl_tls_prf(const mbedtls_tls_prf_types prf, + const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen) +{ + mbedtls_ssl_tls_prf_cb *tls_prf = NULL; + + switch (prf) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_TLS_PRF_SHA384: + tls_prf = tls_prf_sha384; + break; +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_TLS_PRF_SHA256: + tls_prf = tls_prf_sha256; + break; +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + default: + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + return tls_prf(secret, slen, label, random, rlen, dstbuf, dlen); +} + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +static void ssl_clear_peer_cert(mbedtls_ssl_session *session) +{ +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + if (session->peer_cert != NULL) { + mbedtls_x509_crt_free(session->peer_cert); + mbedtls_free(session->peer_cert); + session->peer_cert = NULL; + } +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (session->peer_cert_digest != NULL) { + /* Zeroization is not necessary. */ + mbedtls_free(session->peer_cert_digest); + session->peer_cert_digest = NULL; + session->peer_cert_digest_type = MBEDTLS_MD_NONE; + session->peer_cert_digest_len = 0; + } +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +uint32_t mbedtls_ssl_get_extension_id(unsigned int extension_type) +{ + switch (extension_type) { + case MBEDTLS_TLS_EXT_SERVERNAME: + return MBEDTLS_SSL_EXT_ID_SERVERNAME; + + case MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH: + return MBEDTLS_SSL_EXT_ID_MAX_FRAGMENT_LENGTH; + + case MBEDTLS_TLS_EXT_STATUS_REQUEST: + return MBEDTLS_SSL_EXT_ID_STATUS_REQUEST; + + case MBEDTLS_TLS_EXT_SUPPORTED_GROUPS: + return MBEDTLS_SSL_EXT_ID_SUPPORTED_GROUPS; + + case MBEDTLS_TLS_EXT_SIG_ALG: + return MBEDTLS_SSL_EXT_ID_SIG_ALG; + + case MBEDTLS_TLS_EXT_USE_SRTP: + return MBEDTLS_SSL_EXT_ID_USE_SRTP; + + case MBEDTLS_TLS_EXT_HEARTBEAT: + return MBEDTLS_SSL_EXT_ID_HEARTBEAT; + + case MBEDTLS_TLS_EXT_ALPN: + return MBEDTLS_SSL_EXT_ID_ALPN; + + case MBEDTLS_TLS_EXT_SCT: + return MBEDTLS_SSL_EXT_ID_SCT; + + case MBEDTLS_TLS_EXT_CLI_CERT_TYPE: + return MBEDTLS_SSL_EXT_ID_CLI_CERT_TYPE; + + case MBEDTLS_TLS_EXT_SERV_CERT_TYPE: + return MBEDTLS_SSL_EXT_ID_SERV_CERT_TYPE; + + case MBEDTLS_TLS_EXT_PADDING: + return MBEDTLS_SSL_EXT_ID_PADDING; + + case MBEDTLS_TLS_EXT_PRE_SHARED_KEY: + return MBEDTLS_SSL_EXT_ID_PRE_SHARED_KEY; + + case MBEDTLS_TLS_EXT_EARLY_DATA: + return MBEDTLS_SSL_EXT_ID_EARLY_DATA; + + case MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS: + return MBEDTLS_SSL_EXT_ID_SUPPORTED_VERSIONS; + + case MBEDTLS_TLS_EXT_COOKIE: + return MBEDTLS_SSL_EXT_ID_COOKIE; + + case MBEDTLS_TLS_EXT_PSK_KEY_EXCHANGE_MODES: + return MBEDTLS_SSL_EXT_ID_PSK_KEY_EXCHANGE_MODES; + + case MBEDTLS_TLS_EXT_CERT_AUTH: + return MBEDTLS_SSL_EXT_ID_CERT_AUTH; + + case MBEDTLS_TLS_EXT_OID_FILTERS: + return MBEDTLS_SSL_EXT_ID_OID_FILTERS; + + case MBEDTLS_TLS_EXT_POST_HANDSHAKE_AUTH: + return MBEDTLS_SSL_EXT_ID_POST_HANDSHAKE_AUTH; + + case MBEDTLS_TLS_EXT_SIG_ALG_CERT: + return MBEDTLS_SSL_EXT_ID_SIG_ALG_CERT; + + case MBEDTLS_TLS_EXT_KEY_SHARE: + return MBEDTLS_SSL_EXT_ID_KEY_SHARE; + + case MBEDTLS_TLS_EXT_TRUNCATED_HMAC: + return MBEDTLS_SSL_EXT_ID_TRUNCATED_HMAC; + + case MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS: + return MBEDTLS_SSL_EXT_ID_SUPPORTED_POINT_FORMATS; + + case MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC: + return MBEDTLS_SSL_EXT_ID_ENCRYPT_THEN_MAC; + + case MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET: + return MBEDTLS_SSL_EXT_ID_EXTENDED_MASTER_SECRET; + + case MBEDTLS_TLS_EXT_RECORD_SIZE_LIMIT: + return MBEDTLS_SSL_EXT_ID_RECORD_SIZE_LIMIT; + + case MBEDTLS_TLS_EXT_SESSION_TICKET: + return MBEDTLS_SSL_EXT_ID_SESSION_TICKET; + + } + + return MBEDTLS_SSL_EXT_ID_UNRECOGNIZED; +} + +uint32_t mbedtls_ssl_get_extension_mask(unsigned int extension_type) +{ + return 1 << mbedtls_ssl_get_extension_id(extension_type); +} + +#if defined(MBEDTLS_DEBUG_C) +static const char *extension_name_table[] = { + [MBEDTLS_SSL_EXT_ID_UNRECOGNIZED] = "unrecognized", + [MBEDTLS_SSL_EXT_ID_SERVERNAME] = "server_name", + [MBEDTLS_SSL_EXT_ID_MAX_FRAGMENT_LENGTH] = "max_fragment_length", + [MBEDTLS_SSL_EXT_ID_STATUS_REQUEST] = "status_request", + [MBEDTLS_SSL_EXT_ID_SUPPORTED_GROUPS] = "supported_groups", + [MBEDTLS_SSL_EXT_ID_SIG_ALG] = "signature_algorithms", + [MBEDTLS_SSL_EXT_ID_USE_SRTP] = "use_srtp", + [MBEDTLS_SSL_EXT_ID_HEARTBEAT] = "heartbeat", + [MBEDTLS_SSL_EXT_ID_ALPN] = "application_layer_protocol_negotiation", + [MBEDTLS_SSL_EXT_ID_SCT] = "signed_certificate_timestamp", + [MBEDTLS_SSL_EXT_ID_CLI_CERT_TYPE] = "client_certificate_type", + [MBEDTLS_SSL_EXT_ID_SERV_CERT_TYPE] = "server_certificate_type", + [MBEDTLS_SSL_EXT_ID_PADDING] = "padding", + [MBEDTLS_SSL_EXT_ID_PRE_SHARED_KEY] = "pre_shared_key", + [MBEDTLS_SSL_EXT_ID_EARLY_DATA] = "early_data", + [MBEDTLS_SSL_EXT_ID_SUPPORTED_VERSIONS] = "supported_versions", + [MBEDTLS_SSL_EXT_ID_COOKIE] = "cookie", + [MBEDTLS_SSL_EXT_ID_PSK_KEY_EXCHANGE_MODES] = "psk_key_exchange_modes", + [MBEDTLS_SSL_EXT_ID_CERT_AUTH] = "certificate_authorities", + [MBEDTLS_SSL_EXT_ID_OID_FILTERS] = "oid_filters", + [MBEDTLS_SSL_EXT_ID_POST_HANDSHAKE_AUTH] = "post_handshake_auth", + [MBEDTLS_SSL_EXT_ID_SIG_ALG_CERT] = "signature_algorithms_cert", + [MBEDTLS_SSL_EXT_ID_KEY_SHARE] = "key_share", + [MBEDTLS_SSL_EXT_ID_TRUNCATED_HMAC] = "truncated_hmac", + [MBEDTLS_SSL_EXT_ID_SUPPORTED_POINT_FORMATS] = "supported_point_formats", + [MBEDTLS_SSL_EXT_ID_ENCRYPT_THEN_MAC] = "encrypt_then_mac", + [MBEDTLS_SSL_EXT_ID_EXTENDED_MASTER_SECRET] = "extended_master_secret", + [MBEDTLS_SSL_EXT_ID_SESSION_TICKET] = "session_ticket", + [MBEDTLS_SSL_EXT_ID_RECORD_SIZE_LIMIT] = "record_size_limit" +}; + +static unsigned int extension_type_table[] = { + [MBEDTLS_SSL_EXT_ID_UNRECOGNIZED] = 0xff, + [MBEDTLS_SSL_EXT_ID_SERVERNAME] = MBEDTLS_TLS_EXT_SERVERNAME, + [MBEDTLS_SSL_EXT_ID_MAX_FRAGMENT_LENGTH] = MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH, + [MBEDTLS_SSL_EXT_ID_STATUS_REQUEST] = MBEDTLS_TLS_EXT_STATUS_REQUEST, + [MBEDTLS_SSL_EXT_ID_SUPPORTED_GROUPS] = MBEDTLS_TLS_EXT_SUPPORTED_GROUPS, + [MBEDTLS_SSL_EXT_ID_SIG_ALG] = MBEDTLS_TLS_EXT_SIG_ALG, + [MBEDTLS_SSL_EXT_ID_USE_SRTP] = MBEDTLS_TLS_EXT_USE_SRTP, + [MBEDTLS_SSL_EXT_ID_HEARTBEAT] = MBEDTLS_TLS_EXT_HEARTBEAT, + [MBEDTLS_SSL_EXT_ID_ALPN] = MBEDTLS_TLS_EXT_ALPN, + [MBEDTLS_SSL_EXT_ID_SCT] = MBEDTLS_TLS_EXT_SCT, + [MBEDTLS_SSL_EXT_ID_CLI_CERT_TYPE] = MBEDTLS_TLS_EXT_CLI_CERT_TYPE, + [MBEDTLS_SSL_EXT_ID_SERV_CERT_TYPE] = MBEDTLS_TLS_EXT_SERV_CERT_TYPE, + [MBEDTLS_SSL_EXT_ID_PADDING] = MBEDTLS_TLS_EXT_PADDING, + [MBEDTLS_SSL_EXT_ID_PRE_SHARED_KEY] = MBEDTLS_TLS_EXT_PRE_SHARED_KEY, + [MBEDTLS_SSL_EXT_ID_EARLY_DATA] = MBEDTLS_TLS_EXT_EARLY_DATA, + [MBEDTLS_SSL_EXT_ID_SUPPORTED_VERSIONS] = MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS, + [MBEDTLS_SSL_EXT_ID_COOKIE] = MBEDTLS_TLS_EXT_COOKIE, + [MBEDTLS_SSL_EXT_ID_PSK_KEY_EXCHANGE_MODES] = MBEDTLS_TLS_EXT_PSK_KEY_EXCHANGE_MODES, + [MBEDTLS_SSL_EXT_ID_CERT_AUTH] = MBEDTLS_TLS_EXT_CERT_AUTH, + [MBEDTLS_SSL_EXT_ID_OID_FILTERS] = MBEDTLS_TLS_EXT_OID_FILTERS, + [MBEDTLS_SSL_EXT_ID_POST_HANDSHAKE_AUTH] = MBEDTLS_TLS_EXT_POST_HANDSHAKE_AUTH, + [MBEDTLS_SSL_EXT_ID_SIG_ALG_CERT] = MBEDTLS_TLS_EXT_SIG_ALG_CERT, + [MBEDTLS_SSL_EXT_ID_KEY_SHARE] = MBEDTLS_TLS_EXT_KEY_SHARE, + [MBEDTLS_SSL_EXT_ID_TRUNCATED_HMAC] = MBEDTLS_TLS_EXT_TRUNCATED_HMAC, + [MBEDTLS_SSL_EXT_ID_SUPPORTED_POINT_FORMATS] = MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS, + [MBEDTLS_SSL_EXT_ID_ENCRYPT_THEN_MAC] = MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC, + [MBEDTLS_SSL_EXT_ID_EXTENDED_MASTER_SECRET] = MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET, + [MBEDTLS_SSL_EXT_ID_SESSION_TICKET] = MBEDTLS_TLS_EXT_SESSION_TICKET, + [MBEDTLS_SSL_EXT_ID_RECORD_SIZE_LIMIT] = MBEDTLS_TLS_EXT_RECORD_SIZE_LIMIT +}; + +const char *mbedtls_ssl_get_extension_name(unsigned int extension_type) +{ + return extension_name_table[ + mbedtls_ssl_get_extension_id(extension_type)]; +} + +static const char *ssl_tls13_get_hs_msg_name(int hs_msg_type) +{ + switch (hs_msg_type) { + case MBEDTLS_SSL_HS_CLIENT_HELLO: + return "ClientHello"; + case MBEDTLS_SSL_HS_SERVER_HELLO: + return "ServerHello"; + case MBEDTLS_SSL_TLS1_3_HS_HELLO_RETRY_REQUEST: + return "HelloRetryRequest"; + case MBEDTLS_SSL_HS_NEW_SESSION_TICKET: + return "NewSessionTicket"; + case MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS: + return "EncryptedExtensions"; + case MBEDTLS_SSL_HS_CERTIFICATE: + return "Certificate"; + case MBEDTLS_SSL_HS_CERTIFICATE_REQUEST: + return "CertificateRequest"; + } + return "Unknown"; +} + +void mbedtls_ssl_print_extension(const mbedtls_ssl_context *ssl, + int level, const char *file, int line, + int hs_msg_type, unsigned int extension_type, + const char *extra_msg0, const char *extra_msg1) +{ + const char *extra_msg; + if (extra_msg0 && extra_msg1) { + mbedtls_debug_print_msg( + ssl, level, file, line, + "%s: %s(%u) extension %s %s.", + ssl_tls13_get_hs_msg_name(hs_msg_type), + mbedtls_ssl_get_extension_name(extension_type), + extension_type, + extra_msg0, extra_msg1); + return; + } + + extra_msg = extra_msg0 ? extra_msg0 : extra_msg1; + if (extra_msg) { + mbedtls_debug_print_msg( + ssl, level, file, line, + "%s: %s(%u) extension %s.", ssl_tls13_get_hs_msg_name(hs_msg_type), + mbedtls_ssl_get_extension_name(extension_type), extension_type, + extra_msg); + return; + } + + mbedtls_debug_print_msg( + ssl, level, file, line, + "%s: %s(%u) extension.", ssl_tls13_get_hs_msg_name(hs_msg_type), + mbedtls_ssl_get_extension_name(extension_type), extension_type); +} + +void mbedtls_ssl_print_extensions(const mbedtls_ssl_context *ssl, + int level, const char *file, int line, + int hs_msg_type, uint32_t extensions_mask, + const char *extra) +{ + + for (unsigned i = 0; + i < sizeof(extension_name_table) / sizeof(extension_name_table[0]); + i++) { + mbedtls_ssl_print_extension( + ssl, level, file, line, hs_msg_type, extension_type_table[i], + extensions_mask & (1 << i) ? "exists" : "does not exist", extra); + } +} + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(*(a))) + +static const char *ticket_flag_name_table[] = +{ + [0] = "ALLOW_PSK_RESUMPTION", + [2] = "ALLOW_PSK_EPHEMERAL_RESUMPTION", + [3] = "ALLOW_EARLY_DATA", +}; + +void mbedtls_ssl_print_ticket_flags(const mbedtls_ssl_context *ssl, + int level, const char *file, int line, + unsigned int flags) +{ + size_t i; + + mbedtls_debug_print_msg(ssl, level, file, line, + "print ticket_flags (0x%02x)", flags); + + flags = flags & MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK; + + for (i = 0; i < ARRAY_LENGTH(ticket_flag_name_table); i++) { + if ((flags & (1 << i))) { + mbedtls_debug_print_msg(ssl, level, file, line, "- %s is set.", + ticket_flag_name_table[i]); + } + } +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && MBEDTLS_SSL_SESSION_TICKETS */ + +#endif /* MBEDTLS_DEBUG_C */ + +void mbedtls_ssl_optimize_checksum(mbedtls_ssl_context *ssl, + const mbedtls_ssl_ciphersuite_t *ciphersuite_info) +{ + ((void) ciphersuite_info); + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + if (ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + ssl->handshake->update_checksum = ssl_update_checksum_sha384; + } else +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + if (ciphersuite_info->mac != MBEDTLS_MD_SHA384) { + ssl->handshake->update_checksum = ssl_update_checksum_sha256; + } else +#endif + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return; + } +} + +int mbedtls_ssl_add_hs_hdr_to_checksum(mbedtls_ssl_context *ssl, + unsigned hs_type, + size_t total_hs_len) +{ + unsigned char hs_hdr[4]; + + /* Build HS header for checksum update. */ + hs_hdr[0] = MBEDTLS_BYTE_0(hs_type); + hs_hdr[1] = MBEDTLS_BYTE_2(total_hs_len); + hs_hdr[2] = MBEDTLS_BYTE_1(total_hs_len); + hs_hdr[3] = MBEDTLS_BYTE_0(total_hs_len); + + return ssl->handshake->update_checksum(ssl, hs_hdr, sizeof(hs_hdr)); +} + +int mbedtls_ssl_add_hs_msg_to_checksum(mbedtls_ssl_context *ssl, + unsigned hs_type, + unsigned char const *msg, + size_t msg_len) +{ + int ret; + ret = mbedtls_ssl_add_hs_hdr_to_checksum(ssl, hs_type, msg_len); + if (ret != 0) { + return ret; + } + return ssl->handshake->update_checksum(ssl, msg, msg_len); +} + +int mbedtls_ssl_reset_checksum(mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) || \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#endif +#else /* SHA-256 or SHA-384 */ + ((void) ssl); +#endif /* SHA-256 or SHA-384 */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_hash_abort(&ssl->handshake->fin_sha256_psa); + if (status != PSA_SUCCESS) { + return PSA_TO_MD_ERR(status); + } + status = psa_hash_setup(&ssl->handshake->fin_sha256_psa, PSA_ALG_SHA_256); + if (status != PSA_SUCCESS) { + return PSA_TO_MD_ERR(status); + } +#else + mbedtls_md_free(&ssl->handshake->fin_sha256); + mbedtls_md_init(&ssl->handshake->fin_sha256); + ret = mbedtls_md_setup(&ssl->handshake->fin_sha256, + mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), + 0); + if (ret != 0) { + return ret; + } + ret = mbedtls_md_starts(&ssl->handshake->fin_sha256); + if (ret != 0) { + return ret; + } +#endif +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_hash_abort(&ssl->handshake->fin_sha384_psa); + if (status != PSA_SUCCESS) { + return PSA_TO_MD_ERR(status); + } + status = psa_hash_setup(&ssl->handshake->fin_sha384_psa, PSA_ALG_SHA_384); + if (status != PSA_SUCCESS) { + return PSA_TO_MD_ERR(status); + } +#else + mbedtls_md_free(&ssl->handshake->fin_sha384); + mbedtls_md_init(&ssl->handshake->fin_sha384); + ret = mbedtls_md_setup(&ssl->handshake->fin_sha384, + mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), 0); + if (ret != 0) { + return ret; + } + ret = mbedtls_md_starts(&ssl->handshake->fin_sha384); + if (ret != 0) { + return ret; + } +#endif +#endif + return 0; +} + +static int ssl_update_checksum_start(mbedtls_ssl_context *ssl, + const unsigned char *buf, size_t len) +{ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) || \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#endif +#else /* SHA-256 or SHA-384 */ + ((void) ssl); + (void) buf; + (void) len; +#endif /* SHA-256 or SHA-384 */ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_hash_update(&ssl->handshake->fin_sha256_psa, buf, len); + if (status != PSA_SUCCESS) { + return PSA_TO_MD_ERR(status); + } +#else + ret = mbedtls_md_update(&ssl->handshake->fin_sha256, buf, len); + if (ret != 0) { + return ret; + } +#endif +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_hash_update(&ssl->handshake->fin_sha384_psa, buf, len); + if (status != PSA_SUCCESS) { + return PSA_TO_MD_ERR(status); + } +#else + ret = mbedtls_md_update(&ssl->handshake->fin_sha384, buf, len); + if (ret != 0) { + return ret; + } +#endif +#endif + return 0; +} + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +static int ssl_update_checksum_sha256(mbedtls_ssl_context *ssl, + const unsigned char *buf, size_t len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + return PSA_TO_MD_ERR(psa_hash_update( + &ssl->handshake->fin_sha256_psa, buf, len)); +#else + return mbedtls_md_update(&ssl->handshake->fin_sha256, buf, len); +#endif +} +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +static int ssl_update_checksum_sha384(mbedtls_ssl_context *ssl, + const unsigned char *buf, size_t len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + return PSA_TO_MD_ERR(psa_hash_update( + &ssl->handshake->fin_sha384_psa, buf, len)); +#else + return mbedtls_md_update(&ssl->handshake->fin_sha384, buf, len); +#endif +} +#endif + +static void ssl_handshake_params_init(mbedtls_ssl_handshake_params *handshake) +{ + memset(handshake, 0, sizeof(mbedtls_ssl_handshake_params)); + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + handshake->fin_sha256_psa = psa_hash_operation_init(); +#else + mbedtls_md_init(&handshake->fin_sha256); +#endif +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + handshake->fin_sha384_psa = psa_hash_operation_init(); +#else + mbedtls_md_init(&handshake->fin_sha384); +#endif +#endif + + handshake->update_checksum = ssl_update_checksum_start; + +#if defined(MBEDTLS_DHM_C) + mbedtls_dhm_init(&handshake->dhm_ctx); +#endif +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && defined(MBEDTLS_ECDH_C) + mbedtls_ecdh_init(&handshake->ecdh_ctx); +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + handshake->psa_pake_ctx = psa_pake_operation_init(); + handshake->psa_pake_password = MBEDTLS_SVC_KEY_ID_INIT; +#else + mbedtls_ecjpake_init(&handshake->ecjpake_ctx); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#if defined(MBEDTLS_SSL_CLI_C) + handshake->ecjpake_cache = NULL; + handshake->ecjpake_cache_len = 0; +#endif +#endif + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + mbedtls_x509_crt_restart_init(&handshake->ecrs_ctx); +#endif + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + handshake->sni_authmode = MBEDTLS_SSL_VERIFY_UNSET; +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && \ + !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + mbedtls_pk_init(&handshake->peer_pubkey); +#endif +} + +void mbedtls_ssl_transform_init(mbedtls_ssl_transform *transform) +{ + memset(transform, 0, sizeof(mbedtls_ssl_transform)); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + transform->psa_key_enc = MBEDTLS_SVC_KEY_ID_INIT; + transform->psa_key_dec = MBEDTLS_SVC_KEY_ID_INIT; +#else + mbedtls_cipher_init(&transform->cipher_ctx_enc); + mbedtls_cipher_init(&transform->cipher_ctx_dec); +#endif + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + transform->psa_mac_enc = MBEDTLS_SVC_KEY_ID_INIT; + transform->psa_mac_dec = MBEDTLS_SVC_KEY_ID_INIT; +#else + mbedtls_md_init(&transform->md_ctx_enc); + mbedtls_md_init(&transform->md_ctx_dec); +#endif +#endif +} + +void mbedtls_ssl_session_init(mbedtls_ssl_session *session) +{ + memset(session, 0, sizeof(mbedtls_ssl_session)); +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_handshake_init(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Clear old handshake information if present */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->transform_negotiate) { + mbedtls_ssl_transform_free(ssl->transform_negotiate); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + if (ssl->session_negotiate) { + mbedtls_ssl_session_free(ssl->session_negotiate); + } + if (ssl->handshake) { + mbedtls_ssl_handshake_free(ssl); + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* + * Either the pointers are now NULL or cleared properly and can be freed. + * Now allocate missing structures. + */ + if (ssl->transform_negotiate == NULL) { + ssl->transform_negotiate = mbedtls_calloc(1, sizeof(mbedtls_ssl_transform)); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + if (ssl->session_negotiate == NULL) { + ssl->session_negotiate = mbedtls_calloc(1, sizeof(mbedtls_ssl_session)); + } + + if (ssl->handshake == NULL) { + ssl->handshake = mbedtls_calloc(1, sizeof(mbedtls_ssl_handshake_params)); + } +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + /* If the buffers are too small - reallocate */ + + handle_buffer_resizing(ssl, 0, MBEDTLS_SSL_IN_BUFFER_LEN, + MBEDTLS_SSL_OUT_BUFFER_LEN); +#endif + + /* All pointers should exist and can be directly freed without issue */ + if (ssl->handshake == NULL || +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + ssl->transform_negotiate == NULL || +#endif + ssl->session_negotiate == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc() of ssl sub-contexts failed")); + + mbedtls_free(ssl->handshake); + ssl->handshake = NULL; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + mbedtls_free(ssl->transform_negotiate); + ssl->transform_negotiate = NULL; +#endif + + mbedtls_free(ssl->session_negotiate); + ssl->session_negotiate = NULL; + + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + /* Initialize structures */ + mbedtls_ssl_session_init(ssl->session_negotiate); + ssl_handshake_params_init(ssl->handshake); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + mbedtls_ssl_transform_init(ssl->transform_negotiate); +#endif + + /* Setup handshake checksums */ + ret = mbedtls_ssl_reset_checksum(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_reset_checksum", ret); + return ret; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SRV_C) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) + ssl->handshake->new_session_tickets_count = + ssl->conf->new_session_tickets_count; +#endif + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ssl->handshake->alt_transform_out = ssl->transform_out; + + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_PREPARING; + } else { + ssl->handshake->retransmit_state = MBEDTLS_SSL_RETRANS_WAITING; + } + + mbedtls_ssl_set_timer(ssl, 0); + } +#endif + +/* + * curve_list is translated to IANA TLS group identifiers here because + * mbedtls_ssl_conf_curves returns void and so can't return + * any error codes. + */ +#if defined(MBEDTLS_ECP_C) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + /* Heap allocate and translate curve_list from internal to IANA group ids */ + if (ssl->conf->curve_list != NULL) { + size_t length; + const mbedtls_ecp_group_id *curve_list = ssl->conf->curve_list; + + for (length = 0; (curve_list[length] != MBEDTLS_ECP_DP_NONE) && + (length < MBEDTLS_ECP_DP_MAX); length++) { + } + + /* Leave room for zero termination */ + uint16_t *group_list = mbedtls_calloc(length + 1, sizeof(uint16_t)); + if (group_list == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + for (size_t i = 0; i < length; i++) { + uint16_t tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id( + curve_list[i]); + if (tls_id == 0) { + mbedtls_free(group_list); + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + group_list[i] = tls_id; + } + + group_list[length] = 0; + + ssl->handshake->group_list = group_list; + ssl->handshake->group_list_heap_allocated = 1; + } else { + ssl->handshake->group_list = ssl->conf->group_list; + ssl->handshake->group_list_heap_allocated = 0; + } +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* Heap allocate and translate sig_hashes from internal hash identifiers to + signature algorithms IANA identifiers. */ + if (mbedtls_ssl_conf_is_tls12_only(ssl->conf) && + ssl->conf->sig_hashes != NULL) { + const int *md; + const int *sig_hashes = ssl->conf->sig_hashes; + size_t sig_algs_len = 0; + uint16_t *p; + + MBEDTLS_STATIC_ASSERT(MBEDTLS_SSL_MAX_SIG_ALG_LIST_LEN + <= (SIZE_MAX - (2 * sizeof(uint16_t))), + "MBEDTLS_SSL_MAX_SIG_ALG_LIST_LEN too big"); + + for (md = sig_hashes; *md != MBEDTLS_MD_NONE; md++) { + if (mbedtls_ssl_hash_from_md_alg(*md) == MBEDTLS_SSL_HASH_NONE) { + continue; + } +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + sig_algs_len += sizeof(uint16_t); +#endif + +#if defined(MBEDTLS_RSA_C) + sig_algs_len += sizeof(uint16_t); +#endif + if (sig_algs_len > MBEDTLS_SSL_MAX_SIG_ALG_LIST_LEN) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + } + + if (sig_algs_len < MBEDTLS_SSL_MIN_SIG_ALG_LIST_LEN) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + + ssl->handshake->sig_algs = mbedtls_calloc(1, sig_algs_len + + sizeof(uint16_t)); + if (ssl->handshake->sig_algs == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + p = (uint16_t *) ssl->handshake->sig_algs; + for (md = sig_hashes; *md != MBEDTLS_MD_NONE; md++) { + unsigned char hash = mbedtls_ssl_hash_from_md_alg(*md); + if (hash == MBEDTLS_SSL_HASH_NONE) { + continue; + } +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + *p = ((hash << 8) | MBEDTLS_SSL_SIG_ECDSA); + p++; +#endif +#if defined(MBEDTLS_RSA_C) + *p = ((hash << 8) | MBEDTLS_SSL_SIG_RSA); + p++; +#endif + } + *p = MBEDTLS_TLS_SIG_NONE; + ssl->handshake->sig_algs_heap_allocated = 1; + } else +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + { + ssl->handshake->sig_algs_heap_allocated = 0; + } +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + return 0; +} + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) +/* Dummy cookie callbacks for defaults */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_cookie_write_dummy(void *ctx, + unsigned char **p, unsigned char *end, + const unsigned char *cli_id, size_t cli_id_len) +{ + ((void) ctx); + ((void) p); + ((void) end); + ((void) cli_id); + ((void) cli_id_len); + + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_cookie_check_dummy(void *ctx, + const unsigned char *cookie, size_t cookie_len, + const unsigned char *cli_id, size_t cli_id_len) +{ + ((void) ctx); + ((void) cookie); + ((void) cookie_len); + ((void) cli_id); + ((void) cli_id_len); + + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY && MBEDTLS_SSL_SRV_C */ + +/* + * Initialize an SSL context + */ +void mbedtls_ssl_init(mbedtls_ssl_context *ssl) +{ + memset(ssl, 0, sizeof(mbedtls_ssl_context)); +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_conf_version_check(const mbedtls_ssl_context *ssl) +{ + const mbedtls_ssl_config *conf = ssl->conf; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (mbedtls_ssl_conf_is_tls13_only(conf)) { + if (conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + MBEDTLS_SSL_DEBUG_MSG(1, ("DTLS 1.3 is not yet supported.")); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + MBEDTLS_SSL_DEBUG_MSG(4, ("The SSL configuration is tls13 only.")); + return 0; + } +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (mbedtls_ssl_conf_is_tls12_only(conf)) { + MBEDTLS_SSL_DEBUG_MSG(4, ("The SSL configuration is tls12 only.")); + return 0; + } +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (mbedtls_ssl_conf_is_hybrid_tls12_tls13(conf)) { + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + MBEDTLS_SSL_DEBUG_MSG(1, ("DTLS not yet supported in Hybrid TLS 1.3 + TLS 1.2")); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + if (conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + MBEDTLS_SSL_DEBUG_MSG(1, ("TLS 1.3 server is not supported yet.")); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + + MBEDTLS_SSL_DEBUG_MSG(4, ("The SSL configuration is TLS 1.3 or TLS 1.2.")); + return 0; + } +#endif + + MBEDTLS_SSL_DEBUG_MSG(1, ("The SSL configuration is invalid.")); + return MBEDTLS_ERR_SSL_BAD_CONFIG; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_conf_check(const mbedtls_ssl_context *ssl) +{ + int ret; + ret = ssl_conf_version_check(ssl); + if (ret != 0) { + return ret; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + /* RFC 8446 section 4.4.3 + * + * If the verification fails, the receiver MUST terminate the handshake with + * a "decrypt_error" alert. + * + * If the client is configured as TLS 1.3 only with optional verify, return + * bad config. + * + */ + if (mbedtls_ssl_conf_tls13_ephemeral_enabled( + (mbedtls_ssl_context *) ssl) && + ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && + ssl->conf->max_tls_version == MBEDTLS_SSL_VERSION_TLS1_3 && + ssl->conf->min_tls_version == MBEDTLS_SSL_VERSION_TLS1_3 && + ssl->conf->authmode == MBEDTLS_SSL_VERIFY_OPTIONAL) { + MBEDTLS_SSL_DEBUG_MSG( + 1, ("Optional verify auth mode " + "is not available for TLS 1.3 client")); + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + /* Space for further checks */ + + return 0; +} + +/* + * Setup an SSL context + */ + +int mbedtls_ssl_setup(mbedtls_ssl_context *ssl, + const mbedtls_ssl_config *conf) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN; + size_t out_buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN; + + ssl->conf = conf; + + if ((ret = ssl_conf_check(ssl)) != 0) { + return ret; + } + + /* + * Prepare base structures + */ + + /* Set to NULL in case of an error condition */ + ssl->out_buf = NULL; + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + ssl->in_buf_len = in_buf_len; +#endif + ssl->in_buf = mbedtls_calloc(1, in_buf_len); + if (ssl->in_buf == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc(%" MBEDTLS_PRINTF_SIZET " bytes) failed", in_buf_len)); + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto error; + } + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + ssl->out_buf_len = out_buf_len; +#endif + ssl->out_buf = mbedtls_calloc(1, out_buf_len); + if (ssl->out_buf == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc(%" MBEDTLS_PRINTF_SIZET " bytes) failed", out_buf_len)); + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto error; + } + + mbedtls_ssl_reset_in_out_pointers(ssl); + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + memset(&ssl->dtls_srtp_info, 0, sizeof(ssl->dtls_srtp_info)); +#endif + + if ((ret = ssl_handshake_init(ssl)) != 0) { + goto error; + } + + return 0; + +error: + mbedtls_free(ssl->in_buf); + mbedtls_free(ssl->out_buf); + + ssl->conf = NULL; + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + ssl->in_buf_len = 0; + ssl->out_buf_len = 0; +#endif + ssl->in_buf = NULL; + ssl->out_buf = NULL; + + ssl->in_hdr = NULL; + ssl->in_ctr = NULL; + ssl->in_len = NULL; + ssl->in_iv = NULL; + ssl->in_msg = NULL; + + ssl->out_hdr = NULL; + ssl->out_ctr = NULL; + ssl->out_len = NULL; + ssl->out_iv = NULL; + ssl->out_msg = NULL; + + return ret; +} + +/* + * Reset an initialized and used SSL context for re-use while retaining + * all application-set variables, function pointers and data. + * + * If partial is non-zero, keep data in the input buffer and client ID. + * (Use when a DTLS client reconnects from the same port.) + */ +void mbedtls_ssl_session_reset_msg_layer(mbedtls_ssl_context *ssl, + int partial) +{ +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t in_buf_len = ssl->in_buf_len; + size_t out_buf_len = ssl->out_buf_len; +#else + size_t in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN; + size_t out_buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN; +#endif + +#if !defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) || !defined(MBEDTLS_SSL_SRV_C) + partial = 0; +#endif + + /* Cancel any possibly running timer */ + mbedtls_ssl_set_timer(ssl, 0); + + mbedtls_ssl_reset_in_out_pointers(ssl); + + /* Reset incoming message parsing */ + ssl->in_offt = NULL; + ssl->nb_zero = 0; + ssl->in_msgtype = 0; + ssl->in_msglen = 0; + ssl->in_hslen = 0; + ssl->keep_current_message = 0; + ssl->transform_in = NULL; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + ssl->next_record_offset = 0; + ssl->in_epoch = 0; +#endif + + /* Keep current datagram if partial == 1 */ + if (partial == 0) { + ssl->in_left = 0; + memset(ssl->in_buf, 0, in_buf_len); + } + + ssl->send_alert = 0; + + /* Reset outgoing message writing */ + ssl->out_msgtype = 0; + ssl->out_msglen = 0; + ssl->out_left = 0; + memset(ssl->out_buf, 0, out_buf_len); + memset(ssl->cur_out_ctr, 0, sizeof(ssl->cur_out_ctr)); + ssl->transform_out = NULL; + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + mbedtls_ssl_dtls_replay_reset(ssl); +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->transform) { + mbedtls_ssl_transform_free(ssl->transform); + mbedtls_free(ssl->transform); + ssl->transform = NULL; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_transform_free(ssl->transform_application); + mbedtls_free(ssl->transform_application); + ssl->transform_application = NULL; + + if (ssl->handshake != NULL) { +#if defined(MBEDTLS_SSL_EARLY_DATA) + mbedtls_ssl_transform_free(ssl->handshake->transform_earlydata); + mbedtls_free(ssl->handshake->transform_earlydata); + ssl->handshake->transform_earlydata = NULL; +#endif + + mbedtls_ssl_transform_free(ssl->handshake->transform_handshake); + mbedtls_free(ssl->handshake->transform_handshake); + ssl->handshake->transform_handshake = NULL; + } + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ +} + +int mbedtls_ssl_session_reset_int(mbedtls_ssl_context *ssl, int partial) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ssl->state = MBEDTLS_SSL_HELLO_REQUEST; + + mbedtls_ssl_session_reset_msg_layer(ssl, partial); + + /* Reset renegotiation state */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + ssl->renego_status = MBEDTLS_SSL_INITIAL_HANDSHAKE; + ssl->renego_records_seen = 0; + + ssl->verify_data_len = 0; + memset(ssl->own_verify_data, 0, MBEDTLS_SSL_VERIFY_DATA_MAX_LEN); + memset(ssl->peer_verify_data, 0, MBEDTLS_SSL_VERIFY_DATA_MAX_LEN); +#endif + ssl->secure_renegotiation = MBEDTLS_SSL_LEGACY_RENEGOTIATION; + + ssl->session_in = NULL; + ssl->session_out = NULL; + if (ssl->session) { + mbedtls_ssl_session_free(ssl->session); + mbedtls_free(ssl->session); + ssl->session = NULL; + } + +#if defined(MBEDTLS_SSL_ALPN) + ssl->alpn_chosen = NULL; +#endif + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) + int free_cli_id = 1; +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) + free_cli_id = (partial == 0); +#endif + if (free_cli_id) { + mbedtls_free(ssl->cli_id); + ssl->cli_id = NULL; + ssl->cli_id_len = 0; + } +#endif + + if ((ret = ssl_handshake_init(ssl)) != 0) { + return ret; + } + + return 0; +} + +/* + * Reset an initialized and used SSL context for re-use while retaining + * all application-set variables, function pointers and data. + */ +int mbedtls_ssl_session_reset(mbedtls_ssl_context *ssl) +{ + return mbedtls_ssl_session_reset_int(ssl, 0); +} + +/* + * SSL set accessors + */ +void mbedtls_ssl_conf_endpoint(mbedtls_ssl_config *conf, int endpoint) +{ + conf->endpoint = endpoint; +} + +void mbedtls_ssl_conf_transport(mbedtls_ssl_config *conf, int transport) +{ + conf->transport = transport; +} + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) +void mbedtls_ssl_conf_dtls_anti_replay(mbedtls_ssl_config *conf, char mode) +{ + conf->anti_replay = mode; +} +#endif + +void mbedtls_ssl_conf_dtls_badmac_limit(mbedtls_ssl_config *conf, unsigned limit) +{ + conf->badmac_limit = limit; +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + +void mbedtls_ssl_set_datagram_packing(mbedtls_ssl_context *ssl, + unsigned allow_packing) +{ + ssl->disable_datagram_packing = !allow_packing; +} + +void mbedtls_ssl_conf_handshake_timeout(mbedtls_ssl_config *conf, + uint32_t min, uint32_t max) +{ + conf->hs_timeout_min = min; + conf->hs_timeout_max = max; +} +#endif + +void mbedtls_ssl_conf_authmode(mbedtls_ssl_config *conf, int authmode) +{ + conf->authmode = authmode; +} + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +void mbedtls_ssl_conf_verify(mbedtls_ssl_config *conf, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy) +{ + conf->f_vrfy = f_vrfy; + conf->p_vrfy = p_vrfy; +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +void mbedtls_ssl_conf_rng(mbedtls_ssl_config *conf, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + conf->f_rng = f_rng; + conf->p_rng = p_rng; +} + +void mbedtls_ssl_conf_dbg(mbedtls_ssl_config *conf, + void (*f_dbg)(void *, int, const char *, int, const char *), + void *p_dbg) +{ + conf->f_dbg = f_dbg; + conf->p_dbg = p_dbg; +} + +void mbedtls_ssl_set_bio(mbedtls_ssl_context *ssl, + void *p_bio, + mbedtls_ssl_send_t *f_send, + mbedtls_ssl_recv_t *f_recv, + mbedtls_ssl_recv_timeout_t *f_recv_timeout) +{ + ssl->p_bio = p_bio; + ssl->f_send = f_send; + ssl->f_recv = f_recv; + ssl->f_recv_timeout = f_recv_timeout; +} + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +void mbedtls_ssl_set_mtu(mbedtls_ssl_context *ssl, uint16_t mtu) +{ + ssl->mtu = mtu; +} +#endif + +void mbedtls_ssl_conf_read_timeout(mbedtls_ssl_config *conf, uint32_t timeout) +{ + conf->read_timeout = timeout; +} + +void mbedtls_ssl_set_timer_cb(mbedtls_ssl_context *ssl, + void *p_timer, + mbedtls_ssl_set_timer_t *f_set_timer, + mbedtls_ssl_get_timer_t *f_get_timer) +{ + ssl->p_timer = p_timer; + ssl->f_set_timer = f_set_timer; + ssl->f_get_timer = f_get_timer; + + /* Make sure we start with no timer running */ + mbedtls_ssl_set_timer(ssl, 0); +} + +#if defined(MBEDTLS_SSL_SRV_C) +void mbedtls_ssl_conf_session_cache(mbedtls_ssl_config *conf, + void *p_cache, + mbedtls_ssl_cache_get_t *f_get_cache, + mbedtls_ssl_cache_set_t *f_set_cache) +{ + conf->p_cache = p_cache; + conf->f_get_cache = f_get_cache; + conf->f_set_cache = f_set_cache; +} +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_CLI_C) +int mbedtls_ssl_set_session(mbedtls_ssl_context *ssl, const mbedtls_ssl_session *session) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ssl == NULL || + session == NULL || + ssl->session_negotiate == NULL || + ssl->conf->endpoint != MBEDTLS_SSL_IS_CLIENT) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (ssl->handshake->resume == 1) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (session->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + mbedtls_ssl_ciphersuite_from_id(session->ciphersuite); + + if (mbedtls_ssl_validate_ciphersuite( + ssl, ciphersuite_info, MBEDTLS_SSL_VERSION_TLS1_3, + MBEDTLS_SSL_VERSION_TLS1_3) != 0) { + MBEDTLS_SSL_DEBUG_MSG(4, ("%d is not a valid TLS 1.3 ciphersuite.", + session->ciphersuite)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + if ((ret = mbedtls_ssl_session_copy(ssl->session_negotiate, + session)) != 0) { + return ret; + } + + ssl->handshake->resume = 1; + + return 0; +} +#endif /* MBEDTLS_SSL_CLI_C */ + +void mbedtls_ssl_conf_ciphersuites(mbedtls_ssl_config *conf, + const int *ciphersuites) +{ + conf->ciphersuite_list = ciphersuites; +} + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +void mbedtls_ssl_conf_tls13_key_exchange_modes(mbedtls_ssl_config *conf, + const int kex_modes) +{ + conf->tls13_kex_modes = kex_modes & MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_ALL; +} + +#if defined(MBEDTLS_SSL_EARLY_DATA) +void mbedtls_ssl_tls13_conf_early_data(mbedtls_ssl_config *conf, + int early_data_enabled) +{ + conf->early_data_enabled = early_data_enabled; +} + +#if defined(MBEDTLS_SSL_SRV_C) +void mbedtls_ssl_tls13_conf_max_early_data_size( + mbedtls_ssl_config *conf, uint32_t max_early_data_size) +{ + conf->max_early_data_size = max_early_data_size; +} +#endif /* MBEDTLS_SSL_SRV_C */ + +#endif /* MBEDTLS_SSL_EARLY_DATA */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +void mbedtls_ssl_conf_cert_profile(mbedtls_ssl_config *conf, + const mbedtls_x509_crt_profile *profile) +{ + conf->cert_profile = profile; +} + +static void ssl_key_cert_free(mbedtls_ssl_key_cert *key_cert) +{ + mbedtls_ssl_key_cert *cur = key_cert, *next; + + while (cur != NULL) { + next = cur->next; + mbedtls_free(cur); + cur = next; + } +} + +/* Append a new keycert entry to a (possibly empty) list */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_append_key_cert(mbedtls_ssl_key_cert **head, + mbedtls_x509_crt *cert, + mbedtls_pk_context *key) +{ + mbedtls_ssl_key_cert *new_cert; + + if (cert == NULL) { + /* Free list if cert is null */ + ssl_key_cert_free(*head); + *head = NULL; + return 0; + } + + new_cert = mbedtls_calloc(1, sizeof(mbedtls_ssl_key_cert)); + if (new_cert == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + new_cert->cert = cert; + new_cert->key = key; + new_cert->next = NULL; + + /* Update head if the list was null, else add to the end */ + if (*head == NULL) { + *head = new_cert; + } else { + mbedtls_ssl_key_cert *cur = *head; + while (cur->next != NULL) { + cur = cur->next; + } + cur->next = new_cert; + } + + return 0; +} + +int mbedtls_ssl_conf_own_cert(mbedtls_ssl_config *conf, + mbedtls_x509_crt *own_cert, + mbedtls_pk_context *pk_key) +{ + return ssl_append_key_cert(&conf->key_cert, own_cert, pk_key); +} + +void mbedtls_ssl_conf_ca_chain(mbedtls_ssl_config *conf, + mbedtls_x509_crt *ca_chain, + mbedtls_x509_crl *ca_crl) +{ + conf->ca_chain = ca_chain; + conf->ca_crl = ca_crl; + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + /* mbedtls_ssl_conf_ca_chain() and mbedtls_ssl_conf_ca_cb() + * cannot be used together. */ + conf->f_ca_cb = NULL; + conf->p_ca_cb = NULL; +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ +} + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) +void mbedtls_ssl_conf_ca_cb(mbedtls_ssl_config *conf, + mbedtls_x509_crt_ca_cb_t f_ca_cb, + void *p_ca_cb) +{ + conf->f_ca_cb = f_ca_cb; + conf->p_ca_cb = p_ca_cb; + + /* mbedtls_ssl_conf_ca_chain() and mbedtls_ssl_conf_ca_cb() + * cannot be used together. */ + conf->ca_chain = NULL; + conf->ca_crl = NULL; +} +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +const unsigned char *mbedtls_ssl_get_hs_sni(mbedtls_ssl_context *ssl, + size_t *name_len) +{ + *name_len = ssl->handshake->sni_name_len; + return ssl->handshake->sni_name; +} + +int mbedtls_ssl_set_hs_own_cert(mbedtls_ssl_context *ssl, + mbedtls_x509_crt *own_cert, + mbedtls_pk_context *pk_key) +{ + return ssl_append_key_cert(&ssl->handshake->sni_key_cert, + own_cert, pk_key); +} + +void mbedtls_ssl_set_hs_ca_chain(mbedtls_ssl_context *ssl, + mbedtls_x509_crt *ca_chain, + mbedtls_x509_crl *ca_crl) +{ + ssl->handshake->sni_ca_chain = ca_chain; + ssl->handshake->sni_ca_crl = ca_crl; +} + +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +void mbedtls_ssl_set_hs_dn_hints(mbedtls_ssl_context *ssl, + const mbedtls_x509_crt *crt) +{ + ssl->handshake->dn_hints = crt; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + +void mbedtls_ssl_set_hs_authmode(mbedtls_ssl_context *ssl, + int authmode) +{ + ssl->handshake->sni_authmode = authmode; +} +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +void mbedtls_ssl_set_verify(mbedtls_ssl_context *ssl, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy) +{ + ssl->f_vrfy = f_vrfy; + ssl->p_vrfy = p_vrfy; +} +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +static const uint8_t jpake_server_id[] = { 's', 'e', 'r', 'v', 'e', 'r' }; +static const uint8_t jpake_client_id[] = { 'c', 'l', 'i', 'e', 'n', 't' }; + +static psa_status_t mbedtls_ssl_set_hs_ecjpake_password_common( + mbedtls_ssl_context *ssl, + mbedtls_svc_key_id_t pwd) +{ + psa_status_t status; + psa_pake_cipher_suite_t cipher_suite = psa_pake_cipher_suite_init(); + const uint8_t *user = NULL; + size_t user_len = 0; + const uint8_t *peer = NULL; + size_t peer_len = 0; + psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_JPAKE); + psa_pake_cs_set_primitive(&cipher_suite, + PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, + PSA_ECC_FAMILY_SECP_R1, + 256)); + psa_pake_cs_set_hash(&cipher_suite, PSA_ALG_SHA_256); + + status = psa_pake_setup(&ssl->handshake->psa_pake_ctx, &cipher_suite); + if (status != PSA_SUCCESS) { + return status; + } + + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + user = jpake_server_id; + user_len = sizeof(jpake_server_id); + peer = jpake_client_id; + peer_len = sizeof(jpake_client_id); + } else { + user = jpake_client_id; + user_len = sizeof(jpake_client_id); + peer = jpake_server_id; + peer_len = sizeof(jpake_server_id); + } + + status = psa_pake_set_user(&ssl->handshake->psa_pake_ctx, user, user_len); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_pake_set_peer(&ssl->handshake->psa_pake_ctx, peer, peer_len); + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_pake_set_password_key(&ssl->handshake->psa_pake_ctx, pwd); + if (status != PSA_SUCCESS) { + return status; + } + + ssl->handshake->psa_pake_ctx_is_ok = 1; + + return PSA_SUCCESS; +} + +int mbedtls_ssl_set_hs_ecjpake_password(mbedtls_ssl_context *ssl, + const unsigned char *pw, + size_t pw_len) +{ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status; + + if (ssl->handshake == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* Empty password is not valid */ + if ((pw == NULL) || (pw_len == 0)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attributes, PSA_ALG_JPAKE); + psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + + status = psa_import_key(&attributes, pw, pw_len, + &ssl->handshake->psa_pake_password); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = mbedtls_ssl_set_hs_ecjpake_password_common(ssl, + ssl->handshake->psa_pake_password); + if (status != PSA_SUCCESS) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + return 0; +} + +int mbedtls_ssl_set_hs_ecjpake_password_opaque(mbedtls_ssl_context *ssl, + mbedtls_svc_key_id_t pwd) +{ + psa_status_t status; + + if (ssl->handshake == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (mbedtls_svc_key_id_is_null(pwd)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + status = mbedtls_ssl_set_hs_ecjpake_password_common(ssl, pwd); + if (status != PSA_SUCCESS) { + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + return 0; +} +#else /* MBEDTLS_USE_PSA_CRYPTO */ +int mbedtls_ssl_set_hs_ecjpake_password(mbedtls_ssl_context *ssl, + const unsigned char *pw, + size_t pw_len) +{ + mbedtls_ecjpake_role role; + + if (ssl->handshake == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* Empty password is not valid */ + if ((pw == NULL) || (pw_len == 0)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + role = MBEDTLS_ECJPAKE_SERVER; + } else { + role = MBEDTLS_ECJPAKE_CLIENT; + } + + return mbedtls_ecjpake_setup(&ssl->handshake->ecjpake_ctx, + role, + MBEDTLS_MD_SHA256, + MBEDTLS_ECP_DP_SECP256R1, + pw, pw_len); +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) +int mbedtls_ssl_conf_has_static_psk(mbedtls_ssl_config const *conf) +{ + if (conf->psk_identity == NULL || + conf->psk_identity_len == 0) { + return 0; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (!mbedtls_svc_key_id_is_null(conf->psk_opaque)) { + return 1; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (conf->psk != NULL && conf->psk_len != 0) { + return 1; + } + + return 0; +} + +static void ssl_conf_remove_psk(mbedtls_ssl_config *conf) +{ + /* Remove reference to existing PSK, if any. */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (!mbedtls_svc_key_id_is_null(conf->psk_opaque)) { + /* The maintenance of the PSK key slot is the + * user's responsibility. */ + conf->psk_opaque = MBEDTLS_SVC_KEY_ID_INIT; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (conf->psk != NULL) { + mbedtls_platform_zeroize(conf->psk, conf->psk_len); + + mbedtls_free(conf->psk); + conf->psk = NULL; + conf->psk_len = 0; + } + + /* Remove reference to PSK identity, if any. */ + if (conf->psk_identity != NULL) { + mbedtls_free(conf->psk_identity); + conf->psk_identity = NULL; + conf->psk_identity_len = 0; + } +} + +/* This function assumes that PSK identity in the SSL config is unset. + * It checks that the provided identity is well-formed and attempts + * to make a copy of it in the SSL config. + * On failure, the PSK identity in the config remains unset. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_conf_set_psk_identity(mbedtls_ssl_config *conf, + unsigned char const *psk_identity, + size_t psk_identity_len) +{ + /* Identity len will be encoded on two bytes */ + if (psk_identity == NULL || + psk_identity_len == 0 || + (psk_identity_len >> 16) != 0 || + psk_identity_len > MBEDTLS_SSL_OUT_CONTENT_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + conf->psk_identity = mbedtls_calloc(1, psk_identity_len); + if (conf->psk_identity == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + conf->psk_identity_len = psk_identity_len; + memcpy(conf->psk_identity, psk_identity, conf->psk_identity_len); + + return 0; +} + +int mbedtls_ssl_conf_psk(mbedtls_ssl_config *conf, + const unsigned char *psk, size_t psk_len, + const unsigned char *psk_identity, size_t psk_identity_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* We currently only support one PSK, raw or opaque. */ + if (mbedtls_ssl_conf_has_static_psk(conf)) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + /* Check and set raw PSK */ + if (psk == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (psk_len == 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (psk_len > MBEDTLS_PSK_MAX_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if ((conf->psk = mbedtls_calloc(1, psk_len)) == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + conf->psk_len = psk_len; + memcpy(conf->psk, psk, conf->psk_len); + + /* Check and set PSK Identity */ + ret = ssl_conf_set_psk_identity(conf, psk_identity, psk_identity_len); + if (ret != 0) { + ssl_conf_remove_psk(conf); + } + + return ret; +} + +static void ssl_remove_psk(mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (!mbedtls_svc_key_id_is_null(ssl->handshake->psk_opaque)) { + /* The maintenance of the external PSK key slot is the + * user's responsibility. */ + if (ssl->handshake->psk_opaque_is_internal) { + psa_destroy_key(ssl->handshake->psk_opaque); + ssl->handshake->psk_opaque_is_internal = 0; + } + ssl->handshake->psk_opaque = MBEDTLS_SVC_KEY_ID_INIT; + } +#else + if (ssl->handshake->psk != NULL) { + mbedtls_platform_zeroize(ssl->handshake->psk, + ssl->handshake->psk_len); + mbedtls_free(ssl->handshake->psk); + ssl->handshake->psk_len = 0; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} + +int mbedtls_ssl_set_hs_psk(mbedtls_ssl_context *ssl, + const unsigned char *psk, size_t psk_len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_key_attributes_t key_attributes = psa_key_attributes_init(); + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_algorithm_t alg = PSA_ALG_NONE; + mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (psk == NULL || ssl->handshake == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (psk_len > MBEDTLS_PSK_MAX_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl_remove_psk(ssl); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2) { + if (ssl->handshake->ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384); + } else { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256); + } + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + alg = PSA_ALG_HKDF_EXTRACT(PSA_ALG_ANY_HASH); + psa_set_key_usage_flags(&key_attributes, + PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + psa_set_key_algorithm(&key_attributes, alg); + psa_set_key_type(&key_attributes, PSA_KEY_TYPE_DERIVE); + + status = psa_import_key(&key_attributes, psk, psk_len, &key); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + /* Allow calling psa_destroy_key() on psk remove */ + ssl->handshake->psk_opaque_is_internal = 1; + return mbedtls_ssl_set_hs_psk_opaque(ssl, key); +#else + if ((ssl->handshake->psk = mbedtls_calloc(1, psk_len)) == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + ssl->handshake->psk_len = psk_len; + memcpy(ssl->handshake->psk, psk, ssl->handshake->psk_len); + + return 0; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +int mbedtls_ssl_conf_psk_opaque(mbedtls_ssl_config *conf, + mbedtls_svc_key_id_t psk, + const unsigned char *psk_identity, + size_t psk_identity_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* We currently only support one PSK, raw or opaque. */ + if (mbedtls_ssl_conf_has_static_psk(conf)) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + /* Check and set opaque PSK */ + if (mbedtls_svc_key_id_is_null(psk)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + conf->psk_opaque = psk; + + /* Check and set PSK Identity */ + ret = ssl_conf_set_psk_identity(conf, psk_identity, + psk_identity_len); + if (ret != 0) { + ssl_conf_remove_psk(conf); + } + + return ret; +} + +int mbedtls_ssl_set_hs_psk_opaque(mbedtls_ssl_context *ssl, + mbedtls_svc_key_id_t psk) +{ + if ((mbedtls_svc_key_id_is_null(psk)) || + (ssl->handshake == NULL)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl_remove_psk(ssl); + ssl->handshake->psk_opaque = psk; + return 0; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_SRV_C) +void mbedtls_ssl_conf_psk_cb(mbedtls_ssl_config *conf, + int (*f_psk)(void *, mbedtls_ssl_context *, const unsigned char *, + size_t), + void *p_psk) +{ + conf->f_psk = f_psk; + conf->p_psk = p_psk; +} +#endif /* MBEDTLS_SSL_SRV_C */ + +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +static mbedtls_ssl_mode_t mbedtls_ssl_get_base_mode( + psa_algorithm_t alg) +{ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + if (alg == PSA_ALG_CBC_NO_PADDING) { + return MBEDTLS_SSL_MODE_CBC; + } +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + if (PSA_ALG_IS_AEAD(alg)) { + return MBEDTLS_SSL_MODE_AEAD; + } + return MBEDTLS_SSL_MODE_STREAM; +} + +#else /* MBEDTLS_USE_PSA_CRYPTO */ + +static mbedtls_ssl_mode_t mbedtls_ssl_get_base_mode( + mbedtls_cipher_mode_t mode) +{ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + if (mode == MBEDTLS_MODE_CBC) { + return MBEDTLS_SSL_MODE_CBC; + } +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + +#if defined(MBEDTLS_GCM_C) || \ + defined(MBEDTLS_CCM_C) || \ + defined(MBEDTLS_CHACHAPOLY_C) + if (mode == MBEDTLS_MODE_GCM || + mode == MBEDTLS_MODE_CCM || + mode == MBEDTLS_MODE_CHACHAPOLY) { + return MBEDTLS_SSL_MODE_AEAD; + } +#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */ + + return MBEDTLS_SSL_MODE_STREAM; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +static mbedtls_ssl_mode_t mbedtls_ssl_get_actual_mode( + mbedtls_ssl_mode_t base_mode, + int encrypt_then_mac) +{ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + if (encrypt_then_mac == MBEDTLS_SSL_ETM_ENABLED && + base_mode == MBEDTLS_SSL_MODE_CBC) { + return MBEDTLS_SSL_MODE_CBC_ETM; + } +#else + (void) encrypt_then_mac; +#endif + return base_mode; +} + +mbedtls_ssl_mode_t mbedtls_ssl_get_mode_from_transform( + const mbedtls_ssl_transform *transform) +{ + mbedtls_ssl_mode_t base_mode = mbedtls_ssl_get_base_mode( +#if defined(MBEDTLS_USE_PSA_CRYPTO) + transform->psa_alg +#else + mbedtls_cipher_get_cipher_mode(&transform->cipher_ctx_enc) +#endif + ); + + int encrypt_then_mac = 0; +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + encrypt_then_mac = transform->encrypt_then_mac; +#endif + return mbedtls_ssl_get_actual_mode(base_mode, encrypt_then_mac); +} + +mbedtls_ssl_mode_t mbedtls_ssl_get_mode_from_ciphersuite( +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + int encrypt_then_mac, +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + const mbedtls_ssl_ciphersuite_t *suite) +{ + mbedtls_ssl_mode_t base_mode = MBEDTLS_SSL_MODE_STREAM; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status; + psa_algorithm_t alg; + psa_key_type_t type; + size_t size; + status = mbedtls_ssl_cipher_to_psa(suite->cipher, 0, &alg, &type, &size); + if (status == PSA_SUCCESS) { + base_mode = mbedtls_ssl_get_base_mode(alg); + } +#else + const mbedtls_cipher_info_t *cipher = + mbedtls_cipher_info_from_type(suite->cipher); + if (cipher != NULL) { + base_mode = + mbedtls_ssl_get_base_mode( + mbedtls_cipher_info_get_mode(cipher)); + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if !defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + int encrypt_then_mac = 0; +#endif + return mbedtls_ssl_get_actual_mode(base_mode, encrypt_then_mac); +} + +#if defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) +/* Serialization of TLS 1.3 sessions: + * + * struct { + * opaque hostname<0..2^16-1>; + * uint64 ticket_received; + * uint32 ticket_lifetime; + * opaque ticket<1..2^16-1>; + * } ClientOnlyData; + * + * struct { + * uint8 endpoint; + * uint8 ciphersuite[2]; + * uint32 ticket_age_add; + * uint8 ticket_flags; + * opaque resumption_key<0..255>; + * select ( endpoint ) { + * case client: ClientOnlyData; + * case server: uint64 start_time; + * }; + * } serialized_session_tls13; + * + */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_session_save(const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len, + size_t *olen) +{ + unsigned char *p = buf; +#if defined(MBEDTLS_SSL_CLI_C) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + size_t hostname_len = (session->hostname == NULL) ? + 0 : strlen(session->hostname) + 1; +#endif + size_t needed = 1 /* endpoint */ + + 2 /* ciphersuite */ + + 4 /* ticket_age_add */ + + 1 /* ticket_flags */ + + 1; /* resumption_key length */ + *olen = 0; + + if (session->resumption_key_len > MBEDTLS_SSL_TLS1_3_TICKET_RESUMPTION_KEY_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + needed += session->resumption_key_len; /* resumption_key */ + +#if defined(MBEDTLS_HAVE_TIME) + needed += 8; /* start_time or ticket_received */ +#endif + +#if defined(MBEDTLS_SSL_CLI_C) + if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + needed += 2 /* hostname_len */ + + hostname_len; /* hostname */ +#endif + + needed += 4 /* ticket_lifetime */ + + 2; /* ticket_len */ + + /* Check size_t overflow */ + if (session->ticket_len > SIZE_MAX - needed) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + needed += session->ticket_len; /* ticket */ + } +#endif /* MBEDTLS_SSL_CLI_C */ + + *olen = needed; + if (needed > buf_len) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + p[0] = session->endpoint; + MBEDTLS_PUT_UINT16_BE(session->ciphersuite, p, 1); + MBEDTLS_PUT_UINT32_BE(session->ticket_age_add, p, 3); + p[7] = session->ticket_flags; + + /* save resumption_key */ + p[8] = session->resumption_key_len; + p += 9; + memcpy(p, session->resumption_key, session->resumption_key_len); + p += session->resumption_key_len; + +#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C) + if (session->endpoint == MBEDTLS_SSL_IS_SERVER) { + MBEDTLS_PUT_UINT64_BE((uint64_t) session->start, p, 0); + p += 8; + } +#endif /* MBEDTLS_HAVE_TIME */ + +#if defined(MBEDTLS_SSL_CLI_C) + if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + MBEDTLS_PUT_UINT16_BE(hostname_len, p, 0); + p += 2; + if (hostname_len > 0) { + /* save host name */ + memcpy(p, session->hostname, hostname_len); + p += hostname_len; + } +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_HAVE_TIME) + MBEDTLS_PUT_UINT64_BE((uint64_t) session->ticket_received, p, 0); + p += 8; +#endif + MBEDTLS_PUT_UINT32_BE(session->ticket_lifetime, p, 0); + p += 4; + + MBEDTLS_PUT_UINT16_BE(session->ticket_len, p, 0); + p += 2; + + if (session->ticket != NULL && session->ticket_len > 0) { + memcpy(p, session->ticket, session->ticket_len); + p += session->ticket_len; + } + } +#endif /* MBEDTLS_SSL_CLI_C */ + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_session_load(mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len) +{ + const unsigned char *p = buf; + const unsigned char *end = buf + len; + + if (end - p < 9) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + session->endpoint = p[0]; + session->ciphersuite = MBEDTLS_GET_UINT16_BE(p, 1); + session->ticket_age_add = MBEDTLS_GET_UINT32_BE(p, 3); + session->ticket_flags = p[7]; + + /* load resumption_key */ + session->resumption_key_len = p[8]; + p += 9; + + if (end - p < session->resumption_key_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (sizeof(session->resumption_key) < session->resumption_key_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + memcpy(session->resumption_key, p, session->resumption_key_len); + p += session->resumption_key_len; + +#if defined(MBEDTLS_HAVE_TIME) && defined(MBEDTLS_SSL_SRV_C) + if (session->endpoint == MBEDTLS_SSL_IS_SERVER) { + if (end - p < 8) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + session->start = MBEDTLS_GET_UINT64_BE(p, 0); + p += 8; + } +#endif /* MBEDTLS_HAVE_TIME */ + +#if defined(MBEDTLS_SSL_CLI_C) + if (session->endpoint == MBEDTLS_SSL_IS_CLIENT) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) + size_t hostname_len; + /* load host name */ + if (end - p < 2) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + hostname_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + if (end - p < (long int) hostname_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (hostname_len > 0) { + session->hostname = mbedtls_calloc(1, hostname_len); + if (session->hostname == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + memcpy(session->hostname, p, hostname_len); + p += hostname_len; + } +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION && + MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_HAVE_TIME) + if (end - p < 8) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + session->ticket_received = MBEDTLS_GET_UINT64_BE(p, 0); + p += 8; +#endif + if (end - p < 4) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + session->ticket_lifetime = MBEDTLS_GET_UINT32_BE(p, 0); + p += 4; + + if (end - p < 2) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + session->ticket_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + if (end - p < (long int) session->ticket_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (session->ticket_len > 0) { + session->ticket = mbedtls_calloc(1, session->ticket_len); + if (session->ticket == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + memcpy(session->ticket, p, session->ticket_len); + p += session->ticket_len; + } + } +#endif /* MBEDTLS_SSL_CLI_C */ + + return 0; + +} +#else /* MBEDTLS_SSL_SESSION_TICKETS */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_session_save(const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len, + size_t *olen) +{ + ((void) session); + ((void) buf); + ((void) buf_len); + *olen = 0; + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} + +static int ssl_tls13_session_load(const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len) +{ + ((void) session); + ((void) buf); + ((void) buf_len); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} +#endif /* !MBEDTLS_SSL_SESSION_TICKETS */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +psa_status_t mbedtls_ssl_cipher_to_psa(mbedtls_cipher_type_t mbedtls_cipher_type, + size_t taglen, + psa_algorithm_t *alg, + psa_key_type_t *key_type, + size_t *key_size) +{ + switch (mbedtls_cipher_type) { + case MBEDTLS_CIPHER_AES_128_CBC: + *alg = PSA_ALG_CBC_NO_PADDING; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 128; + break; + case MBEDTLS_CIPHER_AES_128_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 128; + break; + case MBEDTLS_CIPHER_AES_128_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 128; + break; + case MBEDTLS_CIPHER_AES_192_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 192; + break; + case MBEDTLS_CIPHER_AES_192_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 192; + break; + case MBEDTLS_CIPHER_AES_256_CBC: + *alg = PSA_ALG_CBC_NO_PADDING; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 256; + break; + case MBEDTLS_CIPHER_AES_256_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 256; + break; + case MBEDTLS_CIPHER_AES_256_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_AES; + *key_size = 256; + break; + case MBEDTLS_CIPHER_ARIA_128_CBC: + *alg = PSA_ALG_CBC_NO_PADDING; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 128; + break; + case MBEDTLS_CIPHER_ARIA_128_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 128; + break; + case MBEDTLS_CIPHER_ARIA_128_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 128; + break; + case MBEDTLS_CIPHER_ARIA_192_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 192; + break; + case MBEDTLS_CIPHER_ARIA_192_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 192; + break; + case MBEDTLS_CIPHER_ARIA_256_CBC: + *alg = PSA_ALG_CBC_NO_PADDING; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 256; + break; + case MBEDTLS_CIPHER_ARIA_256_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 256; + break; + case MBEDTLS_CIPHER_ARIA_256_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_ARIA; + *key_size = 256; + break; + case MBEDTLS_CIPHER_CAMELLIA_128_CBC: + *alg = PSA_ALG_CBC_NO_PADDING; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 128; + break; + case MBEDTLS_CIPHER_CAMELLIA_128_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 128; + break; + case MBEDTLS_CIPHER_CAMELLIA_128_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 128; + break; + case MBEDTLS_CIPHER_CAMELLIA_192_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 192; + break; + case MBEDTLS_CIPHER_CAMELLIA_192_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 192; + break; + case MBEDTLS_CIPHER_CAMELLIA_256_CBC: + *alg = PSA_ALG_CBC_NO_PADDING; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 256; + break; + case MBEDTLS_CIPHER_CAMELLIA_256_CCM: + *alg = taglen ? PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, taglen) : PSA_ALG_CCM; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 256; + break; + case MBEDTLS_CIPHER_CAMELLIA_256_GCM: + *alg = PSA_ALG_GCM; + *key_type = PSA_KEY_TYPE_CAMELLIA; + *key_size = 256; + break; + case MBEDTLS_CIPHER_CHACHA20_POLY1305: + *alg = PSA_ALG_CHACHA20_POLY1305; + *key_type = PSA_KEY_TYPE_CHACHA20; + *key_size = 256; + break; + case MBEDTLS_CIPHER_NULL: + *alg = MBEDTLS_SSL_NULL_CIPHER; + *key_type = 0; + *key_size = 0; + break; + default: + return PSA_ERROR_NOT_SUPPORTED; + } + + return PSA_SUCCESS; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO || MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_SRV_C) +int mbedtls_ssl_conf_dh_param_bin(mbedtls_ssl_config *conf, + const unsigned char *dhm_P, size_t P_len, + const unsigned char *dhm_G, size_t G_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_mpi_free(&conf->dhm_P); + mbedtls_mpi_free(&conf->dhm_G); + + if ((ret = mbedtls_mpi_read_binary(&conf->dhm_P, dhm_P, P_len)) != 0 || + (ret = mbedtls_mpi_read_binary(&conf->dhm_G, dhm_G, G_len)) != 0) { + mbedtls_mpi_free(&conf->dhm_P); + mbedtls_mpi_free(&conf->dhm_G); + return ret; + } + + return 0; +} + +int mbedtls_ssl_conf_dh_param_ctx(mbedtls_ssl_config *conf, mbedtls_dhm_context *dhm_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_mpi_free(&conf->dhm_P); + mbedtls_mpi_free(&conf->dhm_G); + + if ((ret = mbedtls_dhm_get_value(dhm_ctx, MBEDTLS_DHM_PARAM_P, + &conf->dhm_P)) != 0 || + (ret = mbedtls_dhm_get_value(dhm_ctx, MBEDTLS_DHM_PARAM_G, + &conf->dhm_G)) != 0) { + mbedtls_mpi_free(&conf->dhm_P); + mbedtls_mpi_free(&conf->dhm_G); + return ret; + } + + return 0; +} +#endif /* MBEDTLS_DHM_C && MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_CLI_C) +/* + * Set the minimum length for Diffie-Hellman parameters + */ +void mbedtls_ssl_conf_dhm_min_bitlen(mbedtls_ssl_config *conf, + unsigned int bitlen) +{ + conf->dhm_min_bitlen = bitlen; +} +#endif /* MBEDTLS_DHM_C && MBEDTLS_SSL_CLI_C */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) && defined(MBEDTLS_SSL_PROTO_TLS1_2) +/* + * Set allowed/preferred hashes for handshake signatures + */ +void mbedtls_ssl_conf_sig_hashes(mbedtls_ssl_config *conf, + const int *hashes) +{ + conf->sig_hashes = hashes; +} +#endif /* !MBEDTLS_DEPRECATED_REMOVED && MBEDTLS_SSL_PROTO_TLS1_2 */ + +/* Configure allowed signature algorithms for handshake */ +void mbedtls_ssl_conf_sig_algs(mbedtls_ssl_config *conf, + const uint16_t *sig_algs) +{ +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + conf->sig_hashes = NULL; +#endif /* !MBEDTLS_DEPRECATED_REMOVED */ + conf->sig_algs = sig_algs; +} +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_ECP_C) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +/* + * Set the allowed elliptic curves + * + * mbedtls_ssl_setup() takes the provided list + * and translates it to a list of IANA TLS group identifiers, + * stored in ssl->handshake->group_list. + * + */ +void mbedtls_ssl_conf_curves(mbedtls_ssl_config *conf, + const mbedtls_ecp_group_id *curve_list) +{ + conf->curve_list = curve_list; + conf->group_list = NULL; +} +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_ECP_C */ + +/* + * Set the allowed groups + */ +void mbedtls_ssl_conf_groups(mbedtls_ssl_config *conf, + const uint16_t *group_list) +{ +#if defined(MBEDTLS_ECP_C) && !defined(MBEDTLS_DEPRECATED_REMOVED) + conf->curve_list = NULL; +#endif + conf->group_list = group_list; +} + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +int mbedtls_ssl_set_hostname(mbedtls_ssl_context *ssl, const char *hostname) +{ + /* Initialize to suppress unnecessary compiler warning */ + size_t hostname_len = 0; + + /* Check if new hostname is valid before + * making any change to current one */ + if (hostname != NULL) { + hostname_len = strlen(hostname); + + if (hostname_len > MBEDTLS_SSL_MAX_HOST_NAME_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + } + + /* Now it's clear that we will overwrite the old hostname, + * so we can free it safely */ + + if (ssl->hostname != NULL) { + mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); + mbedtls_free(ssl->hostname); + } + + /* Passing NULL as hostname shall clear the old one */ + + if (hostname == NULL) { + ssl->hostname = NULL; + } else { + ssl->hostname = mbedtls_calloc(1, hostname_len + 1); + if (ssl->hostname == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(ssl->hostname, hostname, hostname_len); + + ssl->hostname[hostname_len] = '\0'; + } + + return 0; +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +void mbedtls_ssl_conf_sni(mbedtls_ssl_config *conf, + int (*f_sni)(void *, mbedtls_ssl_context *, + const unsigned char *, size_t), + void *p_sni) +{ + conf->f_sni = f_sni; + conf->p_sni = p_sni; +} +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_SSL_ALPN) +int mbedtls_ssl_conf_alpn_protocols(mbedtls_ssl_config *conf, const char **protos) +{ + size_t cur_len, tot_len; + const char **p; + + /* + * RFC 7301 3.1: "Empty strings MUST NOT be included and byte strings + * MUST NOT be truncated." + * We check lengths now rather than later. + */ + tot_len = 0; + for (p = protos; *p != NULL; p++) { + cur_len = strlen(*p); + tot_len += cur_len; + + if ((cur_len == 0) || + (cur_len > MBEDTLS_SSL_MAX_ALPN_NAME_LEN) || + (tot_len > MBEDTLS_SSL_MAX_ALPN_LIST_LEN)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + } + + conf->alpn_list = protos; + + return 0; +} + +const char *mbedtls_ssl_get_alpn_protocol(const mbedtls_ssl_context *ssl) +{ + return ssl->alpn_chosen; +} +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) +void mbedtls_ssl_conf_srtp_mki_value_supported(mbedtls_ssl_config *conf, + int support_mki_value) +{ + conf->dtls_srtp_mki_support = support_mki_value; +} + +int mbedtls_ssl_dtls_srtp_set_mki_value(mbedtls_ssl_context *ssl, + unsigned char *mki_value, + uint16_t mki_len) +{ + if (mki_len > MBEDTLS_TLS_SRTP_MAX_MKI_LENGTH) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (ssl->conf->dtls_srtp_mki_support == MBEDTLS_SSL_DTLS_SRTP_MKI_UNSUPPORTED) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + memcpy(ssl->dtls_srtp_info.mki_value, mki_value, mki_len); + ssl->dtls_srtp_info.mki_len = mki_len; + return 0; +} + +int mbedtls_ssl_conf_dtls_srtp_protection_profiles(mbedtls_ssl_config *conf, + const mbedtls_ssl_srtp_profile *profiles) +{ + const mbedtls_ssl_srtp_profile *p; + size_t list_size = 0; + + /* check the profiles list: all entry must be valid, + * its size cannot be more than the total number of supported profiles, currently 4 */ + for (p = profiles; *p != MBEDTLS_TLS_SRTP_UNSET && + list_size <= MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH; + p++) { + if (mbedtls_ssl_check_srtp_profile_value(*p) != MBEDTLS_TLS_SRTP_UNSET) { + list_size++; + } else { + /* unsupported value, stop parsing and set the size to an error value */ + list_size = MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH + 1; + } + } + + if (list_size > MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH) { + conf->dtls_srtp_profile_list = NULL; + conf->dtls_srtp_profile_list_len = 0; + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + conf->dtls_srtp_profile_list = profiles; + conf->dtls_srtp_profile_list_len = list_size; + + return 0; +} + +void mbedtls_ssl_get_dtls_srtp_negotiation_result(const mbedtls_ssl_context *ssl, + mbedtls_dtls_srtp_info *dtls_srtp_info) +{ + dtls_srtp_info->chosen_dtls_srtp_profile = ssl->dtls_srtp_info.chosen_dtls_srtp_profile; + /* do not copy the mki value if there is no chosen profile */ + if (dtls_srtp_info->chosen_dtls_srtp_profile == MBEDTLS_TLS_SRTP_UNSET) { + dtls_srtp_info->mki_len = 0; + } else { + dtls_srtp_info->mki_len = ssl->dtls_srtp_info.mki_len; + memcpy(dtls_srtp_info->mki_value, ssl->dtls_srtp_info.mki_value, + ssl->dtls_srtp_info.mki_len); + } +} +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + +#if !defined(MBEDTLS_DEPRECATED_REMOVED) +void mbedtls_ssl_conf_max_version(mbedtls_ssl_config *conf, int major, int minor) +{ + conf->max_tls_version = (major << 8) | minor; +} + +void mbedtls_ssl_conf_min_version(mbedtls_ssl_config *conf, int major, int minor) +{ + conf->min_tls_version = (major << 8) | minor; +} +#endif /* MBEDTLS_DEPRECATED_REMOVED */ + +#if defined(MBEDTLS_SSL_SRV_C) +void mbedtls_ssl_conf_cert_req_ca_list(mbedtls_ssl_config *conf, + char cert_req_ca_list) +{ + conf->cert_req_ca_list = cert_req_ca_list; +} +#endif + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +void mbedtls_ssl_conf_encrypt_then_mac(mbedtls_ssl_config *conf, char etm) +{ + conf->encrypt_then_mac = etm; +} +#endif + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) +void mbedtls_ssl_conf_extended_master_secret(mbedtls_ssl_config *conf, char ems) +{ + conf->extended_ms = ems; +} +#endif + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +int mbedtls_ssl_conf_max_frag_len(mbedtls_ssl_config *conf, unsigned char mfl_code) +{ + if (mfl_code >= MBEDTLS_SSL_MAX_FRAG_LEN_INVALID || + ssl_mfl_code_to_length(mfl_code) > MBEDTLS_TLS_EXT_ADV_CONTENT_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + conf->mfl_code = mfl_code; + + return 0; +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +void mbedtls_ssl_conf_legacy_renegotiation(mbedtls_ssl_config *conf, int allow_legacy) +{ + conf->allow_legacy_renegotiation = allow_legacy; +} + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +void mbedtls_ssl_conf_renegotiation(mbedtls_ssl_config *conf, int renegotiation) +{ + conf->disable_renegotiation = renegotiation; +} + +void mbedtls_ssl_conf_renegotiation_enforced(mbedtls_ssl_config *conf, int max_records) +{ + conf->renego_max_records = max_records; +} + +void mbedtls_ssl_conf_renegotiation_period(mbedtls_ssl_config *conf, + const unsigned char period[8]) +{ + memcpy(conf->renego_period, period, 8); +} +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +#if defined(MBEDTLS_SSL_CLI_C) +void mbedtls_ssl_conf_session_tickets(mbedtls_ssl_config *conf, int use_tickets) +{ + conf->session_tickets = use_tickets; +} +#endif + +#if defined(MBEDTLS_SSL_SRV_C) + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && defined(MBEDTLS_SSL_SESSION_TICKETS) +void mbedtls_ssl_conf_new_session_tickets(mbedtls_ssl_config *conf, + uint16_t num_tickets) +{ + conf->new_session_tickets_count = num_tickets; +} +#endif + +void mbedtls_ssl_conf_session_tickets_cb(mbedtls_ssl_config *conf, + mbedtls_ssl_ticket_write_t *f_ticket_write, + mbedtls_ssl_ticket_parse_t *f_ticket_parse, + void *p_ticket) +{ + conf->f_ticket_write = f_ticket_write; + conf->f_ticket_parse = f_ticket_parse; + conf->p_ticket = p_ticket; +} +#endif +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +void mbedtls_ssl_set_export_keys_cb(mbedtls_ssl_context *ssl, + mbedtls_ssl_export_keys_t *f_export_keys, + void *p_export_keys) +{ + ssl->f_export_keys = f_export_keys; + ssl->p_export_keys = p_export_keys; +} + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +void mbedtls_ssl_conf_async_private_cb( + mbedtls_ssl_config *conf, + mbedtls_ssl_async_sign_t *f_async_sign, + mbedtls_ssl_async_decrypt_t *f_async_decrypt, + mbedtls_ssl_async_resume_t *f_async_resume, + mbedtls_ssl_async_cancel_t *f_async_cancel, + void *async_config_data) +{ + conf->f_async_sign_start = f_async_sign; + conf->f_async_decrypt_start = f_async_decrypt; + conf->f_async_resume = f_async_resume; + conf->f_async_cancel = f_async_cancel; + conf->p_async_config_data = async_config_data; +} + +void *mbedtls_ssl_conf_get_async_config_data(const mbedtls_ssl_config *conf) +{ + return conf->p_async_config_data; +} + +void *mbedtls_ssl_get_async_operation_data(const mbedtls_ssl_context *ssl) +{ + if (ssl->handshake == NULL) { + return NULL; + } else { + return ssl->handshake->user_async_ctx; + } +} + +void mbedtls_ssl_set_async_operation_data(mbedtls_ssl_context *ssl, + void *ctx) +{ + if (ssl->handshake != NULL) { + ssl->handshake->user_async_ctx = ctx; + } +} +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +/* + * SSL get accessors + */ +uint32_t mbedtls_ssl_get_verify_result(const mbedtls_ssl_context *ssl) +{ + if (ssl->session != NULL) { + return ssl->session->verify_result; + } + + if (ssl->session_negotiate != NULL) { + return ssl->session_negotiate->verify_result; + } + + return 0xFFFFFFFF; +} + +int mbedtls_ssl_get_ciphersuite_id_from_ssl(const mbedtls_ssl_context *ssl) +{ + if (ssl == NULL || ssl->session == NULL) { + return 0; + } + + return ssl->session->ciphersuite; +} + +const char *mbedtls_ssl_get_ciphersuite(const mbedtls_ssl_context *ssl) +{ + if (ssl == NULL || ssl->session == NULL) { + return NULL; + } + + return mbedtls_ssl_get_ciphersuite_name(ssl->session->ciphersuite); +} + +const char *mbedtls_ssl_get_version(const mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + switch (ssl->tls_version) { + case MBEDTLS_SSL_VERSION_TLS1_2: + return "DTLSv1.2"; + default: + return "unknown (DTLS)"; + } + } +#endif + + switch (ssl->tls_version) { + case MBEDTLS_SSL_VERSION_TLS1_2: + return "TLSv1.2"; + case MBEDTLS_SSL_VERSION_TLS1_3: + return "TLSv1.3"; + default: + return "unknown"; + } +} + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +size_t mbedtls_ssl_get_input_max_frag_len(const mbedtls_ssl_context *ssl) +{ + size_t max_len = MBEDTLS_SSL_IN_CONTENT_LEN; + size_t read_mfl; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + /* Use the configured MFL for the client if we're past SERVER_HELLO_DONE */ + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && + ssl->state >= MBEDTLS_SSL_SERVER_HELLO_DONE) { + return ssl_mfl_code_to_length(ssl->conf->mfl_code); + } +#endif + + /* Check if a smaller max length was negotiated */ + if (ssl->session_out != NULL) { + read_mfl = ssl_mfl_code_to_length(ssl->session_out->mfl_code); + if (read_mfl < max_len) { + max_len = read_mfl; + } + } + + /* During a handshake, use the value being negotiated */ + if (ssl->session_negotiate != NULL) { + read_mfl = ssl_mfl_code_to_length(ssl->session_negotiate->mfl_code); + if (read_mfl < max_len) { + max_len = read_mfl; + } + } + + return max_len; +} + +size_t mbedtls_ssl_get_output_max_frag_len(const mbedtls_ssl_context *ssl) +{ + size_t max_len; + + /* + * Assume mfl_code is correct since it was checked when set + */ + max_len = ssl_mfl_code_to_length(ssl->conf->mfl_code); + + /* Check if a smaller max length was negotiated */ + if (ssl->session_out != NULL && + ssl_mfl_code_to_length(ssl->session_out->mfl_code) < max_len) { + max_len = ssl_mfl_code_to_length(ssl->session_out->mfl_code); + } + + /* During a handshake, use the value being negotiated */ + if (ssl->session_negotiate != NULL && + ssl_mfl_code_to_length(ssl->session_negotiate->mfl_code) < max_len) { + max_len = ssl_mfl_code_to_length(ssl->session_negotiate->mfl_code); + } + + return max_len; +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +size_t mbedtls_ssl_get_current_mtu(const mbedtls_ssl_context *ssl) +{ + /* Return unlimited mtu for client hello messages to avoid fragmentation. */ + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && + (ssl->state == MBEDTLS_SSL_CLIENT_HELLO || + ssl->state == MBEDTLS_SSL_SERVER_HELLO)) { + return 0; + } + + if (ssl->handshake == NULL || ssl->handshake->mtu == 0) { + return ssl->mtu; + } + + if (ssl->mtu == 0) { + return ssl->handshake->mtu; + } + + return ssl->mtu < ssl->handshake->mtu ? + ssl->mtu : ssl->handshake->mtu; +} +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +int mbedtls_ssl_get_max_out_record_payload(const mbedtls_ssl_context *ssl) +{ + size_t max_len = MBEDTLS_SSL_OUT_CONTENT_LEN; + +#if !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) && \ + !defined(MBEDTLS_SSL_PROTO_DTLS) + (void) ssl; +#endif + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + const size_t mfl = mbedtls_ssl_get_output_max_frag_len(ssl); + + if (max_len > mfl) { + max_len = mfl; + } +#endif + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (mbedtls_ssl_get_current_mtu(ssl) != 0) { + const size_t mtu = mbedtls_ssl_get_current_mtu(ssl); + const int ret = mbedtls_ssl_get_record_expansion(ssl); + const size_t overhead = (size_t) ret; + + if (ret < 0) { + return ret; + } + + if (mtu <= overhead) { + MBEDTLS_SSL_DEBUG_MSG(1, ("MTU too low for record expansion")); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + if (max_len > mtu - overhead) { + max_len = mtu - overhead; + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) && \ + !defined(MBEDTLS_SSL_PROTO_DTLS) + ((void) ssl); +#endif + + return (int) max_len; +} + +int mbedtls_ssl_get_max_in_record_payload(const mbedtls_ssl_context *ssl) +{ + size_t max_len = MBEDTLS_SSL_IN_CONTENT_LEN; + +#if !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + (void) ssl; +#endif + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + const size_t mfl = mbedtls_ssl_get_input_max_frag_len(ssl); + + if (max_len > mfl) { + max_len = mfl; + } +#endif + + return (int) max_len; +} + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +const mbedtls_x509_crt *mbedtls_ssl_get_peer_cert(const mbedtls_ssl_context *ssl) +{ + if (ssl == NULL || ssl->session == NULL) { + return NULL; + } + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + return ssl->session->peer_cert; +#else + return NULL; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_CLI_C) +int mbedtls_ssl_get_session(const mbedtls_ssl_context *ssl, + mbedtls_ssl_session *dst) +{ + int ret; + + if (ssl == NULL || + dst == NULL || + ssl->session == NULL || + ssl->conf->endpoint != MBEDTLS_SSL_IS_CLIENT) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* Since Mbed TLS 3.0, mbedtls_ssl_get_session() is no longer + * idempotent: Each session can only be exported once. + * + * (This is in preparation for TLS 1.3 support where we will + * need the ability to export multiple sessions (aka tickets), + * which will be achieved by calling mbedtls_ssl_get_session() + * multiple times until it fails.) + * + * Check whether we have already exported the current session, + * and fail if so. + */ + if (ssl->session->exported == 1) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + ret = mbedtls_ssl_session_copy(dst, ssl->session); + if (ret != 0) { + return ret; + } + + /* Remember that we've exported the session. */ + ssl->session->exported = 1; + return 0; +} +#endif /* MBEDTLS_SSL_CLI_C */ + +/* + * Define ticket header determining Mbed TLS version + * and structure of the ticket. + */ + +/* + * Define bitflag determining compile-time settings influencing + * structure of serialized SSL sessions. + */ + +#if defined(MBEDTLS_HAVE_TIME) +#define SSL_SERIALIZED_SESSION_CONFIG_TIME 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_TIME 0 +#endif /* MBEDTLS_HAVE_TIME */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#define SSL_SERIALIZED_SESSION_CONFIG_CRT 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_CRT 0 +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET 0 +#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +#define SSL_SERIALIZED_SESSION_CONFIG_MFL 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_MFL 0 +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +#define SSL_SERIALIZED_SESSION_CONFIG_ETM 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_ETM 0 +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +#define SSL_SERIALIZED_SESSION_CONFIG_TICKET 1 +#else +#define SSL_SERIALIZED_SESSION_CONFIG_TICKET 0 +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#define SSL_SERIALIZED_SESSION_CONFIG_TIME_BIT 0 +#define SSL_SERIALIZED_SESSION_CONFIG_CRT_BIT 1 +#define SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT 2 +#define SSL_SERIALIZED_SESSION_CONFIG_MFL_BIT 3 +#define SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT 4 +#define SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT 5 + +#define SSL_SERIALIZED_SESSION_CONFIG_BITFLAG \ + ((uint16_t) ( \ + (SSL_SERIALIZED_SESSION_CONFIG_TIME << SSL_SERIALIZED_SESSION_CONFIG_TIME_BIT) | \ + (SSL_SERIALIZED_SESSION_CONFIG_CRT << SSL_SERIALIZED_SESSION_CONFIG_CRT_BIT) | \ + (SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET << \ + SSL_SERIALIZED_SESSION_CONFIG_CLIENT_TICKET_BIT) | \ + (SSL_SERIALIZED_SESSION_CONFIG_MFL << SSL_SERIALIZED_SESSION_CONFIG_MFL_BIT) | \ + (SSL_SERIALIZED_SESSION_CONFIG_ETM << SSL_SERIALIZED_SESSION_CONFIG_ETM_BIT) | \ + (SSL_SERIALIZED_SESSION_CONFIG_TICKET << SSL_SERIALIZED_SESSION_CONFIG_TICKET_BIT))) + +static unsigned char ssl_serialized_session_header[] = { + MBEDTLS_VERSION_MAJOR, + MBEDTLS_VERSION_MINOR, + MBEDTLS_VERSION_PATCH, + MBEDTLS_BYTE_1(SSL_SERIALIZED_SESSION_CONFIG_BITFLAG), + MBEDTLS_BYTE_0(SSL_SERIALIZED_SESSION_CONFIG_BITFLAG), +}; + +/* + * Serialize a session in the following format: + * (in the presentation language of TLS, RFC 8446 section 3) + * + * struct { + * + * opaque mbedtls_version[3]; // library version: major, minor, patch + * opaque session_format[2]; // library-version specific 16-bit field + * // determining the format of the remaining + * // serialized data. + * + * Note: When updating the format, remember to keep + * these version+format bytes. + * + * // In this version, `session_format` determines + * // the setting of those compile-time + * // configuration options which influence + * // the structure of mbedtls_ssl_session. + * + * uint8_t minor_ver; // Protocol minor version. Possible values: + * // - TLS 1.2 (0x0303) + * // - TLS 1.3 (0x0304) + * + * select (serialized_session.tls_version) { + * + * case MBEDTLS_SSL_VERSION_TLS1_2: + * serialized_session_tls12 data; + * case MBEDTLS_SSL_VERSION_TLS1_3: + * serialized_session_tls13 data; + * + * }; + * + * } serialized_session; + * + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_session_save(const mbedtls_ssl_session *session, + unsigned char omit_header, + unsigned char *buf, + size_t buf_len, + size_t *olen) +{ + unsigned char *p = buf; + size_t used = 0; + size_t remaining_len; +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + size_t out_len; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#endif + if (session == NULL) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (!omit_header) { + /* + * Add Mbed TLS version identifier + */ + used += sizeof(ssl_serialized_session_header); + + if (used <= buf_len) { + memcpy(p, ssl_serialized_session_header, + sizeof(ssl_serialized_session_header)); + p += sizeof(ssl_serialized_session_header); + } + } + + /* + * TLS version identifier + */ + used += 1; + if (used <= buf_len) { + *p++ = MBEDTLS_BYTE_0(session->tls_version); + } + + /* Forward to version-specific serialization routine. */ + remaining_len = (buf_len >= used) ? buf_len - used : 0; + switch (session->tls_version) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + case MBEDTLS_SSL_VERSION_TLS1_2: + used += ssl_tls12_session_save(session, p, remaining_len); + break; +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + case MBEDTLS_SSL_VERSION_TLS1_3: + ret = ssl_tls13_session_save(session, p, remaining_len, &out_len); + if (ret != 0 && ret != MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL) { + return ret; + } + used += out_len; + break; +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + default: + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + *olen = used; + if (used > buf_len) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + return 0; +} + +/* + * Public wrapper for ssl_session_save() + */ +int mbedtls_ssl_session_save(const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len, + size_t *olen) +{ + return ssl_session_save(session, 0, buf, buf_len, olen); +} + +/* + * Deserialize session, see mbedtls_ssl_session_save() for format. + * + * This internal version is wrapped by a public function that cleans up in + * case of error, and has an extra option omit_header. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_session_load(mbedtls_ssl_session *session, + unsigned char omit_header, + const unsigned char *buf, + size_t len) +{ + const unsigned char *p = buf; + const unsigned char * const end = buf + len; + size_t remaining_len; + + + if (session == NULL) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (!omit_header) { + /* + * Check Mbed TLS version identifier + */ + + if ((size_t) (end - p) < sizeof(ssl_serialized_session_header)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (memcmp(p, ssl_serialized_session_header, + sizeof(ssl_serialized_session_header)) != 0) { + return MBEDTLS_ERR_SSL_VERSION_MISMATCH; + } + p += sizeof(ssl_serialized_session_header); + } + + /* + * TLS version identifier + */ + if (1 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + session->tls_version = 0x0300 | *p++; + + /* Dispatch according to TLS version. */ + remaining_len = (end - p); + switch (session->tls_version) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + case MBEDTLS_SSL_VERSION_TLS1_2: + return ssl_tls12_session_load(session, p, remaining_len); +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + case MBEDTLS_SSL_VERSION_TLS1_3: + return ssl_tls13_session_load(session, p, remaining_len); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + default: + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } +} + +/* + * Deserialize session: public wrapper for error cleaning + */ +int mbedtls_ssl_session_load(mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len) +{ + int ret = ssl_session_load(session, 0, buf, len); + + if (ret != 0) { + mbedtls_ssl_session_free(session); + } + + return ret; +} + +/* + * Perform a single step of the SSL handshake + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_prepare_handshake_step(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * We may have not been able to send to the peer all the handshake data + * that were written into the output buffer by the previous handshake step, + * if the write to the network callback returned with the + * #MBEDTLS_ERR_SSL_WANT_WRITE error code. + * We proceed to the next handshake step only when all data from the + * previous one have been sent to the peer, thus we make sure that this is + * the case here by calling `mbedtls_ssl_flush_output()`. The function may + * return with the #MBEDTLS_ERR_SSL_WANT_WRITE error code in which case + * we have to wait before to go ahead. + * In the case of TLS 1.3, handshake step handlers do not send data to the + * peer. Data are only sent here and through + * `mbedtls_ssl_handle_pending_alert` in case an error that triggered an + * alert occurred. + */ + if ((ret = mbedtls_ssl_flush_output(ssl)) != 0) { + return ret; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->handshake->retransmit_state == MBEDTLS_SSL_RETRANS_SENDING) { + if ((ret = mbedtls_ssl_flight_transmit(ssl)) != 0) { + return ret; + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + return ret; +} + +int mbedtls_ssl_handshake_step(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ssl == NULL || + ssl->conf == NULL || + ssl->handshake == NULL || + ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ret = ssl_prepare_handshake_step(ssl); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_handle_pending_alert(ssl); + if (ret != 0) { + goto cleanup; + } + + /* If ssl->conf->endpoint is not one of MBEDTLS_SSL_IS_CLIENT or + * MBEDTLS_SSL_IS_SERVER, this is the return code we give */ + ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + +#if defined(MBEDTLS_SSL_CLI_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + MBEDTLS_SSL_DEBUG_MSG(2, ("client state: %s", + mbedtls_ssl_states_str(ssl->state))); + + switch (ssl->state) { + case MBEDTLS_SSL_HELLO_REQUEST: + ssl->state = MBEDTLS_SSL_CLIENT_HELLO; + ret = 0; + break; + + case MBEDTLS_SSL_CLIENT_HELLO: + ret = mbedtls_ssl_write_client_hello(ssl); + break; + + default: +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + ret = mbedtls_ssl_tls13_handshake_client_step(ssl); + } else { + ret = mbedtls_ssl_handshake_client_step(ssl); + } +#elif defined(MBEDTLS_SSL_PROTO_TLS1_2) + ret = mbedtls_ssl_handshake_client_step(ssl); +#else + ret = mbedtls_ssl_tls13_handshake_client_step(ssl); +#endif + } + } +#endif +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (mbedtls_ssl_conf_is_tls13_only(ssl->conf)) { + ret = mbedtls_ssl_tls13_handshake_server_step(ssl); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (mbedtls_ssl_conf_is_tls12_only(ssl->conf)) { + ret = mbedtls_ssl_handshake_server_step(ssl); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + } +#endif + + if (ret != 0) { + /* handshake_step return error. And it is same + * with alert_reason. + */ + if (ssl->send_alert) { + ret = mbedtls_ssl_handle_pending_alert(ssl); + goto cleanup; + } + } + +cleanup: + return ret; +} + +/* + * Perform the SSL handshake + */ +int mbedtls_ssl_handshake(mbedtls_ssl_context *ssl) +{ + int ret = 0; + + /* Sanity checks */ + + if (ssl == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + (ssl->f_set_timer == NULL || ssl->f_get_timer == NULL)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("You must use " + "mbedtls_ssl_set_timer_cb() for DTLS")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> handshake")); + + /* Main handshake loop */ + while (ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER) { + ret = mbedtls_ssl_handshake_step(ssl); + + if (ret != 0) { + break; + } + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= handshake")); + + return ret; +} + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +#if defined(MBEDTLS_SSL_SRV_C) +/* + * Write HelloRequest to request renegotiation on server + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_hello_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write hello request")); + + ssl->out_msglen = 4; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_HELLO_REQUEST; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write hello request")); + + return 0; +} +#endif /* MBEDTLS_SSL_SRV_C */ + +/* + * Actually renegotiate current connection, triggered by either: + * - any side: calling mbedtls_ssl_renegotiate(), + * - client: receiving a HelloRequest during mbedtls_ssl_read(), + * - server: receiving any handshake message on server during mbedtls_ssl_read() after + * the initial handshake is completed. + * If the handshake doesn't complete due to waiting for I/O, it will continue + * during the next calls to mbedtls_ssl_renegotiate() or mbedtls_ssl_read() respectively. + */ +int mbedtls_ssl_start_renegotiation(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> renegotiate")); + + if ((ret = ssl_handshake_init(ssl)) != 0) { + return ret; + } + + /* RFC 6347 4.2.2: "[...] the HelloRequest will have message_seq = 0 and + * the ServerHello will have message_seq = 1" */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING) { + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + ssl->handshake->out_msg_seq = 1; + } else { + ssl->handshake->in_msg_seq = 1; + } + } +#endif + + ssl->state = MBEDTLS_SSL_HELLO_REQUEST; + ssl->renego_status = MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS; + + if ((ret = mbedtls_ssl_handshake(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= renegotiate")); + + return 0; +} + +/* + * Renegotiate current connection on client, + * or request renegotiation on server + */ +int mbedtls_ssl_renegotiate(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + + if (ssl == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_SSL_SRV_C) + /* On server, just send the request */ + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + if (mbedtls_ssl_is_handshake_over(ssl) == 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl->renego_status = MBEDTLS_SSL_RENEGOTIATION_PENDING; + + /* Did we already try/start sending HelloRequest? */ + if (ssl->out_left != 0) { + return mbedtls_ssl_flush_output(ssl); + } + + return ssl_write_hello_request(ssl); + } +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_CLI_C) + /* + * On client, either start the renegotiation process or, + * if already in progress, continue the handshake + */ + if (ssl->renego_status != MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { + if (mbedtls_ssl_is_handshake_over(ssl) == 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if ((ret = mbedtls_ssl_start_renegotiation(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_start_renegotiation", ret); + return ret; + } + } else { + if ((ret = mbedtls_ssl_handshake(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_handshake", ret); + return ret; + } + } +#endif /* MBEDTLS_SSL_CLI_C */ + + return ret; +} +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +void mbedtls_ssl_handshake_free(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + if (handshake == NULL) { + return; + } + +#if defined(MBEDTLS_ECP_C) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + if (ssl->handshake->group_list_heap_allocated) { + mbedtls_free((void *) handshake->group_list); + } + handshake->group_list = NULL; +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +#if !defined(MBEDTLS_DEPRECATED_REMOVED) + if (ssl->handshake->sig_algs_heap_allocated) { + mbedtls_free((void *) handshake->sig_algs); + } + handshake->sig_algs = NULL; +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (ssl->handshake->certificate_request_context) { + mbedtls_free((void *) handshake->certificate_request_context); + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if (ssl->conf->f_async_cancel != NULL && handshake->async_in_progress != 0) { + ssl->conf->f_async_cancel(ssl); + handshake->async_in_progress = 0; + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_hash_abort(&handshake->fin_sha256_psa); +#else + mbedtls_md_free(&handshake->fin_sha256); +#endif +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_hash_abort(&handshake->fin_sha384_psa); +#else + mbedtls_md_free(&handshake->fin_sha384); +#endif +#endif + +#if defined(MBEDTLS_DHM_C) + mbedtls_dhm_free(&handshake->dhm_ctx); +#endif +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && defined(MBEDTLS_ECDH_C) + mbedtls_ecdh_free(&handshake->ecdh_ctx); +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_pake_abort(&handshake->psa_pake_ctx); + /* + * Opaque keys are not stored in the handshake's data and it's the user + * responsibility to destroy them. Clear ones, instead, are created by + * the TLS library and should be destroyed at the same level + */ + if (!mbedtls_svc_key_id_is_null(handshake->psa_pake_password)) { + psa_destroy_key(handshake->psa_pake_password); + } + handshake->psa_pake_password = MBEDTLS_SVC_KEY_ID_INIT; +#else + mbedtls_ecjpake_free(&handshake->ecjpake_ctx); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#if defined(MBEDTLS_SSL_CLI_C) + mbedtls_free(handshake->ecjpake_cache); + handshake->ecjpake_cache = NULL; + handshake->ecjpake_cache_len = 0; +#endif +#endif + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + /* explicit void pointer cast for buggy MS compiler */ + mbedtls_free((void *) handshake->curves_tls_id); +#endif + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (!mbedtls_svc_key_id_is_null(ssl->handshake->psk_opaque)) { + /* The maintenance of the external PSK key slot is the + * user's responsibility. */ + if (ssl->handshake->psk_opaque_is_internal) { + psa_destroy_key(ssl->handshake->psk_opaque); + ssl->handshake->psk_opaque_is_internal = 0; + } + ssl->handshake->psk_opaque = MBEDTLS_SVC_KEY_ID_INIT; + } +#else + if (handshake->psk != NULL) { + mbedtls_platform_zeroize(handshake->psk, handshake->psk_len); + mbedtls_free(handshake->psk); + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + /* + * Free only the linked list wrapper, not the keys themselves + * since the belong to the SNI callback + */ + ssl_key_cert_free(handshake->sni_key_cert); +#endif /* MBEDTLS_X509_CRT_PARSE_C && MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + mbedtls_x509_crt_restart_free(&handshake->ecrs_ctx); + if (handshake->ecrs_peer_cert != NULL) { + mbedtls_x509_crt_free(handshake->ecrs_peer_cert); + mbedtls_free(handshake->ecrs_peer_cert); + } +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && \ + !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + mbedtls_pk_free(&handshake->peer_pubkey); +#endif /* MBEDTLS_X509_CRT_PARSE_C && !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + +#if defined(MBEDTLS_SSL_CLI_C) && \ + (defined(MBEDTLS_SSL_PROTO_DTLS) || defined(MBEDTLS_SSL_PROTO_TLS1_3)) + mbedtls_free(handshake->cookie); +#endif /* MBEDTLS_SSL_CLI_C && + ( MBEDTLS_SSL_PROTO_DTLS || MBEDTLS_SSL_PROTO_TLS1_3 ) */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + mbedtls_ssl_flight_free(handshake->flight); + mbedtls_ssl_buffering_free(ssl); +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_ECDH_C) && \ + (defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3)) + if (handshake->ecdh_psa_privkey_is_external == 0) { + psa_destroy_key(handshake->ecdh_psa_privkey); + } +#endif /* MBEDTLS_ECDH_C && MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_transform_free(handshake->transform_handshake); + mbedtls_free(handshake->transform_handshake); +#if defined(MBEDTLS_SSL_EARLY_DATA) + mbedtls_ssl_transform_free(handshake->transform_earlydata); + mbedtls_free(handshake->transform_earlydata); +#endif +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + /* If the buffers are too big - reallocate. Because of the way Mbed TLS + * processes datagrams and the fact that a datagram is allowed to have + * several records in it, it is possible that the I/O buffers are not + * empty at this stage */ + handle_buffer_resizing(ssl, 1, mbedtls_ssl_get_input_buflen(ssl), + mbedtls_ssl_get_output_buflen(ssl)); +#endif + + /* mbedtls_platform_zeroize MUST be last one in this function */ + mbedtls_platform_zeroize(handshake, + sizeof(mbedtls_ssl_handshake_params)); +} + +void mbedtls_ssl_session_free(mbedtls_ssl_session *session) +{ + if (session == NULL) { + return; + } + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + ssl_clear_peer_cert(session); +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + mbedtls_free(session->hostname); +#endif + mbedtls_free(session->ticket); +#endif + + mbedtls_platform_zeroize(session, sizeof(mbedtls_ssl_session)); +} + +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_CONNECTION_ID 1u +#else +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_CONNECTION_ID 0u +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_BADMAC_LIMIT 1u + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_ANTI_REPLAY 1u +#else +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_ANTI_REPLAY 0u +#endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ + +#if defined(MBEDTLS_SSL_ALPN) +#define SSL_SERIALIZED_CONTEXT_CONFIG_ALPN 1u +#else +#define SSL_SERIALIZED_CONTEXT_CONFIG_ALPN 0u +#endif /* MBEDTLS_SSL_ALPN */ + +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_CONNECTION_ID_BIT 0 +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_BADMAC_LIMIT_BIT 1 +#define SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_ANTI_REPLAY_BIT 2 +#define SSL_SERIALIZED_CONTEXT_CONFIG_ALPN_BIT 3 + +#define SSL_SERIALIZED_CONTEXT_CONFIG_BITFLAG \ + ((uint32_t) ( \ + (SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_CONNECTION_ID << \ + SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_CONNECTION_ID_BIT) | \ + (SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_BADMAC_LIMIT << \ + SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_BADMAC_LIMIT_BIT) | \ + (SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_ANTI_REPLAY << \ + SSL_SERIALIZED_CONTEXT_CONFIG_DTLS_ANTI_REPLAY_BIT) | \ + (SSL_SERIALIZED_CONTEXT_CONFIG_ALPN << SSL_SERIALIZED_CONTEXT_CONFIG_ALPN_BIT) | \ + 0u)) + +static unsigned char ssl_serialized_context_header[] = { + MBEDTLS_VERSION_MAJOR, + MBEDTLS_VERSION_MINOR, + MBEDTLS_VERSION_PATCH, + MBEDTLS_BYTE_1(SSL_SERIALIZED_SESSION_CONFIG_BITFLAG), + MBEDTLS_BYTE_0(SSL_SERIALIZED_SESSION_CONFIG_BITFLAG), + MBEDTLS_BYTE_2(SSL_SERIALIZED_CONTEXT_CONFIG_BITFLAG), + MBEDTLS_BYTE_1(SSL_SERIALIZED_CONTEXT_CONFIG_BITFLAG), + MBEDTLS_BYTE_0(SSL_SERIALIZED_CONTEXT_CONFIG_BITFLAG), +}; + +/* + * Serialize a full SSL context + * + * The format of the serialized data is: + * (in the presentation language of TLS, RFC 8446 section 3) + * + * // header + * opaque mbedtls_version[3]; // major, minor, patch + * opaque context_format[5]; // version-specific field determining + * // the format of the remaining + * // serialized data. + * Note: When updating the format, remember to keep these + * version+format bytes. (We may make their size part of the API.) + * + * // session sub-structure + * opaque session<1..2^32-1>; // see mbedtls_ssl_session_save() + * // transform sub-structure + * uint8 random[64]; // ServerHello.random+ClientHello.random + * uint8 in_cid<0..2^8-1> // Connection ID: expected incoming value + * uint8 out_cid<0..2^8-1> // Connection ID: outgoing value to use + * // fields from ssl_context + * uint32 badmac_seen; // DTLS: number of records with failing MAC + * uint64 in_window_top; // DTLS: last validated record seq_num + * uint64 in_window; // DTLS: bitmask for replay protection + * uint8 disable_datagram_packing; // DTLS: only one record per datagram + * uint64 cur_out_ctr; // Record layer: outgoing sequence number + * uint16 mtu; // DTLS: path mtu (max outgoing fragment size) + * uint8 alpn_chosen<0..2^8-1> // ALPN: negotiated application protocol + * + * Note that many fields of the ssl_context or sub-structures are not + * serialized, as they fall in one of the following categories: + * + * 1. forced value (eg in_left must be 0) + * 2. pointer to dynamically-allocated memory (eg session, transform) + * 3. value can be re-derived from other data (eg session keys from MS) + * 4. value was temporary (eg content of input buffer) + * 5. value will be provided by the user again (eg I/O callbacks and context) + */ +int mbedtls_ssl_context_save(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t buf_len, + size_t *olen) +{ + unsigned char *p = buf; + size_t used = 0; + size_t session_len; + int ret = 0; + + /* + * Enforce usage restrictions, see "return BAD_INPUT_DATA" in + * this function's documentation. + * + * These are due to assumptions/limitations in the implementation. Some of + * them are likely to stay (no handshake in progress) some might go away + * (only DTLS) but are currently used to simplify the implementation. + */ + /* The initial handshake must be over */ + if (mbedtls_ssl_is_handshake_over(ssl) == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Initial handshake isn't over")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (ssl->handshake != NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Handshake isn't completed")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + /* Double-check that sub-structures are indeed ready */ + if (ssl->transform == NULL || ssl->session == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Serialised structures aren't ready")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + /* There must be no pending incoming or outgoing data */ + if (mbedtls_ssl_check_pending(ssl) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("There is pending incoming data")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (ssl->out_left != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("There is pending outgoing data")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + /* Protocol must be DTLS, not TLS */ + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Only DTLS is supported")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + /* Version must be 1.2 */ + if (ssl->tls_version != MBEDTLS_SSL_VERSION_TLS1_2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Only version 1.2 supported")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + /* We must be using an AEAD ciphersuite */ + if (mbedtls_ssl_transform_uses_aead(ssl->transform) != 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Only AEAD ciphersuites supported")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + /* Renegotiation must not be enabled */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->conf->disable_renegotiation != MBEDTLS_SSL_RENEGOTIATION_DISABLED) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Renegotiation must not be enabled")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } +#endif + + /* + * Version and format identifier + */ + used += sizeof(ssl_serialized_context_header); + + if (used <= buf_len) { + memcpy(p, ssl_serialized_context_header, + sizeof(ssl_serialized_context_header)); + p += sizeof(ssl_serialized_context_header); + } + + /* + * Session (length + data) + */ + ret = ssl_session_save(ssl->session, 1, NULL, 0, &session_len); + if (ret != MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL) { + return ret; + } + + used += 4 + session_len; + if (used <= buf_len) { + MBEDTLS_PUT_UINT32_BE(session_len, p, 0); + p += 4; + + ret = ssl_session_save(ssl->session, 1, + p, session_len, &session_len); + if (ret != 0) { + return ret; + } + + p += session_len; + } + + /* + * Transform + */ + used += sizeof(ssl->transform->randbytes); + if (used <= buf_len) { + memcpy(p, ssl->transform->randbytes, + sizeof(ssl->transform->randbytes)); + p += sizeof(ssl->transform->randbytes); + } + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + used += 2 + ssl->transform->in_cid_len + ssl->transform->out_cid_len; + if (used <= buf_len) { + *p++ = ssl->transform->in_cid_len; + memcpy(p, ssl->transform->in_cid, ssl->transform->in_cid_len); + p += ssl->transform->in_cid_len; + + *p++ = ssl->transform->out_cid_len; + memcpy(p, ssl->transform->out_cid, ssl->transform->out_cid_len); + p += ssl->transform->out_cid_len; + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + /* + * Saved fields from top-level ssl_context structure + */ + used += 4; + if (used <= buf_len) { + MBEDTLS_PUT_UINT32_BE(ssl->badmac_seen, p, 0); + p += 4; + } + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + used += 16; + if (used <= buf_len) { + MBEDTLS_PUT_UINT64_BE(ssl->in_window_top, p, 0); + p += 8; + + MBEDTLS_PUT_UINT64_BE(ssl->in_window, p, 0); + p += 8; + } +#endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + used += 1; + if (used <= buf_len) { + *p++ = ssl->disable_datagram_packing; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + used += MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; + if (used <= buf_len) { + memcpy(p, ssl->cur_out_ctr, MBEDTLS_SSL_SEQUENCE_NUMBER_LEN); + p += MBEDTLS_SSL_SEQUENCE_NUMBER_LEN; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + used += 2; + if (used <= buf_len) { + MBEDTLS_PUT_UINT16_BE(ssl->mtu, p, 0); + p += 2; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_SSL_ALPN) + { + const uint8_t alpn_len = ssl->alpn_chosen + ? (uint8_t) strlen(ssl->alpn_chosen) + : 0; + + used += 1 + alpn_len; + if (used <= buf_len) { + *p++ = alpn_len; + + if (ssl->alpn_chosen != NULL) { + memcpy(p, ssl->alpn_chosen, alpn_len); + p += alpn_len; + } + } + } +#endif /* MBEDTLS_SSL_ALPN */ + + /* + * Done + */ + *olen = used; + + if (used > buf_len) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "saved context", buf, used); + + return mbedtls_ssl_session_reset_int(ssl, 0); +} + +/* + * Deserialize context, see mbedtls_ssl_context_save() for format. + * + * This internal version is wrapped by a public function that cleans up in + * case of error. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_context_load(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + const unsigned char *p = buf; + const unsigned char * const end = buf + len; + size_t session_len; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + tls_prf_fn prf_func = NULL; +#endif + + /* + * The context should have been freshly setup or reset. + * Give the user an error in case of obvious misuse. + * (Checking session is useful because it won't be NULL if we're + * renegotiating, or if the user mistakenly loaded a session first.) + */ + if (ssl->state != MBEDTLS_SSL_HELLO_REQUEST || + ssl->session != NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* + * We can't check that the config matches the initial one, but we can at + * least check it matches the requirements for serializing. + */ + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM || + ssl->conf->max_tls_version < MBEDTLS_SSL_VERSION_TLS1_2 || + ssl->conf->min_tls_version > MBEDTLS_SSL_VERSION_TLS1_2 || +#if defined(MBEDTLS_SSL_RENEGOTIATION) + ssl->conf->disable_renegotiation != MBEDTLS_SSL_RENEGOTIATION_DISABLED || +#endif + 0) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "context to load", buf, len); + + /* + * Check version identifier + */ + if ((size_t) (end - p) < sizeof(ssl_serialized_context_header)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (memcmp(p, ssl_serialized_context_header, + sizeof(ssl_serialized_context_header)) != 0) { + return MBEDTLS_ERR_SSL_VERSION_MISMATCH; + } + p += sizeof(ssl_serialized_context_header); + + /* + * Session + */ + if ((size_t) (end - p) < 4) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session_len = ((size_t) p[0] << 24) | + ((size_t) p[1] << 16) | + ((size_t) p[2] << 8) | + ((size_t) p[3]); + p += 4; + + /* This has been allocated by ssl_handshake_init(), called by + * by either mbedtls_ssl_session_reset_int() or mbedtls_ssl_setup(). */ + ssl->session = ssl->session_negotiate; + ssl->session_in = ssl->session; + ssl->session_out = ssl->session; + ssl->session_negotiate = NULL; + + if ((size_t) (end - p) < session_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ret = ssl_session_load(ssl->session, 1, p, session_len); + if (ret != 0) { + mbedtls_ssl_session_free(ssl->session); + return ret; + } + + p += session_len; + + /* + * Transform + */ + + /* This has been allocated by ssl_handshake_init(), called by + * by either mbedtls_ssl_session_reset_int() or mbedtls_ssl_setup(). */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + ssl->transform = ssl->transform_negotiate; + ssl->transform_in = ssl->transform; + ssl->transform_out = ssl->transform; + ssl->transform_negotiate = NULL; +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + prf_func = ssl_tls12prf_from_cs(ssl->session->ciphersuite); + if (prf_func == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* Read random bytes and populate structure */ + if ((size_t) (end - p) < sizeof(ssl->transform->randbytes)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ret = ssl_tls12_populate_transform(ssl->transform, + ssl->session->ciphersuite, + ssl->session->master, +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + ssl->session->encrypt_then_mac, +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + prf_func, + p, /* currently pointing to randbytes */ + MBEDTLS_SSL_VERSION_TLS1_2, /* (D)TLS 1.2 is forced */ + ssl->conf->endpoint, + ssl); + if (ret != 0) { + return ret; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + p += sizeof(ssl->transform->randbytes); + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* Read connection IDs and store them */ + if ((size_t) (end - p) < 1) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl->transform->in_cid_len = *p++; + + if ((size_t) (end - p) < ssl->transform->in_cid_len + 1u) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + memcpy(ssl->transform->in_cid, p, ssl->transform->in_cid_len); + p += ssl->transform->in_cid_len; + + ssl->transform->out_cid_len = *p++; + + if ((size_t) (end - p) < ssl->transform->out_cid_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + memcpy(ssl->transform->out_cid, p, ssl->transform->out_cid_len); + p += ssl->transform->out_cid_len; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + /* + * Saved fields from top-level ssl_context structure + */ + if ((size_t) (end - p) < 4) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl->badmac_seen = ((uint32_t) p[0] << 24) | + ((uint32_t) p[1] << 16) | + ((uint32_t) p[2] << 8) | + ((uint32_t) p[3]); + p += 4; + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + if ((size_t) (end - p) < 16) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl->in_window_top = ((uint64_t) p[0] << 56) | + ((uint64_t) p[1] << 48) | + ((uint64_t) p[2] << 40) | + ((uint64_t) p[3] << 32) | + ((uint64_t) p[4] << 24) | + ((uint64_t) p[5] << 16) | + ((uint64_t) p[6] << 8) | + ((uint64_t) p[7]); + p += 8; + + ssl->in_window = ((uint64_t) p[0] << 56) | + ((uint64_t) p[1] << 48) | + ((uint64_t) p[2] << 40) | + ((uint64_t) p[3] << 32) | + ((uint64_t) p[4] << 24) | + ((uint64_t) p[5] << 16) | + ((uint64_t) p[6] << 8) | + ((uint64_t) p[7]); + p += 8; +#endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if ((size_t) (end - p) < 1) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl->disable_datagram_packing = *p++; +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + if ((size_t) (end - p) < sizeof(ssl->cur_out_ctr)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + memcpy(ssl->cur_out_ctr, p, sizeof(ssl->cur_out_ctr)); + p += sizeof(ssl->cur_out_ctr); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if ((size_t) (end - p) < 2) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl->mtu = (p[0] << 8) | p[1]; + p += 2; +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +#if defined(MBEDTLS_SSL_ALPN) + { + uint8_t alpn_len; + const char **cur; + + if ((size_t) (end - p) < 1) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + alpn_len = *p++; + + if (alpn_len != 0 && ssl->conf->alpn_list != NULL) { + /* alpn_chosen should point to an item in the configured list */ + for (cur = ssl->conf->alpn_list; *cur != NULL; cur++) { + if (strlen(*cur) == alpn_len && + memcmp(p, cur, alpn_len) == 0) { + ssl->alpn_chosen = *cur; + break; + } + } + } + + /* can only happen on conf mismatch */ + if (alpn_len != 0 && ssl->alpn_chosen == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + p += alpn_len; + } +#endif /* MBEDTLS_SSL_ALPN */ + + /* + * Forced fields from top-level ssl_context structure + * + * Most of them already set to the correct value by mbedtls_ssl_init() and + * mbedtls_ssl_reset(), so we only need to set the remaining ones. + */ + ssl->state = MBEDTLS_SSL_HANDSHAKE_OVER; + ssl->tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + + /* Adjust pointers for header fields of outgoing records to + * the given transform, accounting for explicit IV and CID. */ + mbedtls_ssl_update_out_pointers(ssl, ssl->transform); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + ssl->in_epoch = 1; +#endif + + /* mbedtls_ssl_reset() leaves the handshake sub-structure allocated, + * which we don't want - otherwise we'd end up freeing the wrong transform + * by calling mbedtls_ssl_handshake_wrapup_free_hs_transform() + * inappropriately. */ + if (ssl->handshake != NULL) { + mbedtls_ssl_handshake_free(ssl); + mbedtls_free(ssl->handshake); + ssl->handshake = NULL; + } + + /* + * Done - should have consumed entire buffer + */ + if (p != end) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + return 0; +} + +/* + * Deserialize context: public wrapper for error cleaning + */ +int mbedtls_ssl_context_load(mbedtls_ssl_context *context, + const unsigned char *buf, + size_t len) +{ + int ret = ssl_context_load(context, buf, len); + + if (ret != 0) { + mbedtls_ssl_free(context); + } + + return ret; +} +#endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ + +/* + * Free an SSL context + */ +void mbedtls_ssl_free(mbedtls_ssl_context *ssl) +{ + if (ssl == NULL) { + return; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> free")); + + if (ssl->out_buf != NULL) { +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t out_buf_len = ssl->out_buf_len; +#else + size_t out_buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN; +#endif + + mbedtls_platform_zeroize(ssl->out_buf, out_buf_len); + mbedtls_free(ssl->out_buf); + ssl->out_buf = NULL; + } + + if (ssl->in_buf != NULL) { +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t in_buf_len = ssl->in_buf_len; +#else + size_t in_buf_len = MBEDTLS_SSL_IN_BUFFER_LEN; +#endif + + mbedtls_platform_zeroize(ssl->in_buf, in_buf_len); + mbedtls_free(ssl->in_buf); + ssl->in_buf = NULL; + } + + if (ssl->transform) { + mbedtls_ssl_transform_free(ssl->transform); + mbedtls_free(ssl->transform); + } + + if (ssl->handshake) { + mbedtls_ssl_handshake_free(ssl); + mbedtls_free(ssl->handshake); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + mbedtls_ssl_transform_free(ssl->transform_negotiate); + mbedtls_free(ssl->transform_negotiate); +#endif + + mbedtls_ssl_session_free(ssl->session_negotiate); + mbedtls_free(ssl->session_negotiate); + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_transform_free(ssl->transform_application); + mbedtls_free(ssl->transform_application); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + if (ssl->session) { + mbedtls_ssl_session_free(ssl->session); + mbedtls_free(ssl->session); + } + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + if (ssl->hostname != NULL) { + mbedtls_platform_zeroize(ssl->hostname, strlen(ssl->hostname)); + mbedtls_free(ssl->hostname); + } +#endif + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) + mbedtls_free(ssl->cli_id); +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= free")); + + /* Actually clear after last debug message */ + mbedtls_platform_zeroize(ssl, sizeof(mbedtls_ssl_context)); +} + +/* + * Initialize mbedtls_ssl_config + */ +void mbedtls_ssl_config_init(mbedtls_ssl_config *conf) +{ + memset(conf, 0, sizeof(mbedtls_ssl_config)); +} + +/* The selection should be the same as mbedtls_x509_crt_profile_default in + * x509_crt.c, plus Montgomery curves for ECDHE. Here, the order matters: + * curves with a lower resource usage come first. + * See the documentation of mbedtls_ssl_conf_curves() for what we promise + * about this list. + */ +static uint16_t ssl_preset_default_groups[] = { +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_X25519, +#endif +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1, +#endif +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1, +#endif +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_X448, +#endif +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_SECP521R1, +#endif +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_BP256R1, +#endif +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_BP384R1, +#endif +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_BP512R1, +#endif + MBEDTLS_SSL_IANA_TLS_GROUP_NONE +}; + +static int ssl_preset_suiteb_ciphersuites[] = { + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + 0 +}; + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + +/* NOTICE: + * For ssl_preset_*_sig_algs and ssl_tls12_preset_*_sig_algs, the following + * rules SHOULD be upheld. + * - No duplicate entries. + * - But if there is a good reason, do not change the order of the algorithms. + * - ssl_tls12_preset* is for TLS 1.2 use only. + * - ssl_preset_* is for TLS 1.3 only or hybrid TLS 1.3/1.2 handshakes. + */ +static uint16_t ssl_preset_default_sig_algs[] = { + +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256, +#endif /* MBEDTLS_PK_CAN_ECDSA_SOME && MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA && + MBEDTLS_ECP_DP_SECP256R1_ENABLED */ + +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + MBEDTLS_TLS1_3_SIG_ECDSA_SECP384R1_SHA384, +#endif /* MBEDTLS_PK_CAN_ECDSA_SOME && MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA&& + MBEDTLS_ECP_DP_SECP384R1_ENABLED */ + +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) && \ + defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + MBEDTLS_TLS1_3_SIG_ECDSA_SECP521R1_SHA512, +#endif /* MBEDTLS_PK_CAN_ECDSA_SOME && MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA&& + MBEDTLS_ECP_DP_SECP521R1_ENABLED */ + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ + defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512, +#endif \ + /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ + defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384, +#endif \ + /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256, +#endif \ + /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA512, +#endif /* MBEDTLS_RSA_C && MBEDTLS_SHA512_C */ + +#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA384, +#endif /* MBEDTLS_RSA_C && MBEDTLS_SHA384_C */ + +#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256, +#endif /* MBEDTLS_RSA_C && MBEDTLS_SHA256_C */ + + MBEDTLS_TLS_SIG_NONE +}; + +/* NOTICE: see above */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +static uint16_t ssl_tls12_preset_default_sig_algs[] = { +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA512), +#endif +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512, +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ +#if defined(MBEDTLS_RSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA512), +#endif +#endif /* MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA384), +#endif +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384, +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ +#if defined(MBEDTLS_RSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA384), +#endif +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA256), +#endif +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256, +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ +#if defined(MBEDTLS_RSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA256), +#endif +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + MBEDTLS_TLS_SIG_NONE +}; +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ +/* NOTICE: see above */ +static uint16_t ssl_preset_suiteb_sig_algs[] = { + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256, +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA&& + MBEDTLS_ECP_DP_SECP256R1_ENABLED */ + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + MBEDTLS_TLS1_3_SIG_ECDSA_SECP384R1_SHA384, +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA&& + MBEDTLS_ECP_DP_SECP384R1_ENABLED */ + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ + defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256, +#endif \ + /* MBEDTLS_X509_RSASSA_PSS_SUPPORT && MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +#if defined(MBEDTLS_RSA_C) && defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256, +#endif /* MBEDTLS_RSA_C && MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + + MBEDTLS_TLS_SIG_NONE +}; + +/* NOTICE: see above */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) +static uint16_t ssl_tls12_preset_suiteb_sig_algs[] = { +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_ECDSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA256), +#endif +#if defined(MBEDTLS_RSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA256), +#endif +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +#if defined(MBEDTLS_ECDSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, MBEDTLS_SSL_HASH_SHA384), +#endif +#if defined(MBEDTLS_RSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, MBEDTLS_SSL_HASH_SHA384), +#endif +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + MBEDTLS_TLS_SIG_NONE +}; +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +static uint16_t ssl_preset_suiteb_groups[] = { +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1, +#endif +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1, +#endif + MBEDTLS_SSL_IANA_TLS_GROUP_NONE +}; + +#if defined(MBEDTLS_DEBUG_C) && defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +/* Function for checking `ssl_preset_*_sig_algs` and `ssl_tls12_preset_*_sig_algs` + * to make sure there are no duplicated signature algorithm entries. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_no_sig_alg_duplication(uint16_t *sig_algs) +{ + size_t i, j; + int ret = 0; + + for (i = 0; sig_algs[i] != MBEDTLS_TLS_SIG_NONE; i++) { + for (j = 0; j < i; j++) { + if (sig_algs[i] != sig_algs[j]) { + continue; + } + mbedtls_printf(" entry(%04x,%" MBEDTLS_PRINTF_SIZET + ") is duplicated at %" MBEDTLS_PRINTF_SIZET "\n", + sig_algs[i], j, i); + ret = -1; + } + } + return ret; +} + +#endif /* MBEDTLS_DEBUG_C && MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +/* + * Load default in mbedtls_ssl_config + */ +int mbedtls_ssl_config_defaults(mbedtls_ssl_config *conf, + int endpoint, int transport, int preset) +{ +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_SRV_C) + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#endif + +#if defined(MBEDTLS_DEBUG_C) && defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) + if (ssl_check_no_sig_alg_duplication(ssl_preset_suiteb_sig_algs)) { + mbedtls_printf("ssl_preset_suiteb_sig_algs has duplicated entries\n"); + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } + + if (ssl_check_no_sig_alg_duplication(ssl_preset_default_sig_algs)) { + mbedtls_printf("ssl_preset_default_sig_algs has duplicated entries\n"); + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl_check_no_sig_alg_duplication(ssl_tls12_preset_suiteb_sig_algs)) { + mbedtls_printf("ssl_tls12_preset_suiteb_sig_algs has duplicated entries\n"); + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } + + if (ssl_check_no_sig_alg_duplication(ssl_tls12_preset_default_sig_algs)) { + mbedtls_printf("ssl_tls12_preset_default_sig_algs has duplicated entries\n"); + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ +#endif /* MBEDTLS_DEBUG_C && MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + + /* Use the functions here so that they are covered in tests, + * but otherwise access member directly for efficiency */ + mbedtls_ssl_conf_endpoint(conf, endpoint); + mbedtls_ssl_conf_transport(conf, transport); + + /* + * Things that are common to all presets + */ +#if defined(MBEDTLS_SSL_CLI_C) + if (endpoint == MBEDTLS_SSL_IS_CLIENT) { + conf->authmode = MBEDTLS_SSL_VERIFY_REQUIRED; +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + conf->session_tickets = MBEDTLS_SSL_SESSION_TICKETS_ENABLED; +#endif + } +#endif + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + conf->encrypt_then_mac = MBEDTLS_SSL_ETM_ENABLED; +#endif + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + conf->extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED; +#endif + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) && defined(MBEDTLS_SSL_SRV_C) + conf->f_cookie_write = ssl_cookie_write_dummy; + conf->f_cookie_check = ssl_cookie_check_dummy; +#endif + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + conf->anti_replay = MBEDTLS_SSL_ANTI_REPLAY_ENABLED; +#endif + +#if defined(MBEDTLS_SSL_SRV_C) + conf->cert_req_ca_list = MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED; + conf->respect_cli_pref = MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_SERVER; +#endif + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + conf->hs_timeout_min = MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MIN; + conf->hs_timeout_max = MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MAX; +#endif + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + conf->renego_max_records = MBEDTLS_SSL_RENEGO_MAX_RECORDS_DEFAULT; + memset(conf->renego_period, 0x00, 2); + memset(conf->renego_period + 2, 0xFF, 6); +#endif + +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_SRV_C) + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + const unsigned char dhm_p[] = + MBEDTLS_DHM_RFC3526_MODP_2048_P_BIN; + const unsigned char dhm_g[] = + MBEDTLS_DHM_RFC3526_MODP_2048_G_BIN; + + if ((ret = mbedtls_ssl_conf_dh_param_bin(conf, + dhm_p, sizeof(dhm_p), + dhm_g, sizeof(dhm_g))) != 0) { + return ret; + } + } +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#if defined(MBEDTLS_SSL_EARLY_DATA) + mbedtls_ssl_tls13_conf_early_data(conf, MBEDTLS_SSL_EARLY_DATA_DISABLED); +#if defined(MBEDTLS_SSL_SRV_C) + mbedtls_ssl_tls13_conf_max_early_data_size( + conf, MBEDTLS_SSL_MAX_EARLY_DATA_SIZE); +#endif +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_conf_new_session_tickets( + conf, MBEDTLS_SSL_TLS1_3_DEFAULT_NEW_SESSION_TICKETS); +#endif + /* + * Allow all TLS 1.3 key exchange modes by default. + */ + conf->tls13_kex_modes = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_ALL; +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + if (transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + conf->min_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + conf->max_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; +#else + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +#endif + } else { +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (endpoint == MBEDTLS_SSL_IS_CLIENT) { + conf->min_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + conf->max_tls_version = MBEDTLS_SSL_VERSION_TLS1_3; + } else { + /* Hybrid TLS 1.2 / 1.3 is not supported on server side yet */ + conf->min_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + conf->max_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + } +#elif defined(MBEDTLS_SSL_PROTO_TLS1_3) + conf->min_tls_version = MBEDTLS_SSL_VERSION_TLS1_3; + conf->max_tls_version = MBEDTLS_SSL_VERSION_TLS1_3; +#elif defined(MBEDTLS_SSL_PROTO_TLS1_2) + conf->min_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + conf->max_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; +#else + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +#endif + } + + /* + * Preset-specific defaults + */ + switch (preset) { + /* + * NSA Suite B + */ + case MBEDTLS_SSL_PRESET_SUITEB: + + conf->ciphersuite_list = ssl_preset_suiteb_ciphersuites; + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + conf->cert_profile = &mbedtls_x509_crt_profile_suiteb; +#endif + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (mbedtls_ssl_conf_is_tls12_only(conf)) { + conf->sig_algs = ssl_tls12_preset_suiteb_sig_algs; + } else +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + conf->sig_algs = ssl_preset_suiteb_sig_algs; +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_ECP_C) && !defined(MBEDTLS_DEPRECATED_REMOVED) + conf->curve_list = NULL; +#endif + conf->group_list = ssl_preset_suiteb_groups; + break; + + /* + * Default + */ + default: + + conf->ciphersuite_list = mbedtls_ssl_list_ciphersuites(); + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + conf->cert_profile = &mbedtls_x509_crt_profile_default; +#endif + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (mbedtls_ssl_conf_is_tls12_only(conf)) { + conf->sig_algs = ssl_tls12_preset_default_sig_algs; + } else +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + conf->sig_algs = ssl_preset_default_sig_algs; +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_ECP_C) && !defined(MBEDTLS_DEPRECATED_REMOVED) + conf->curve_list = NULL; +#endif + conf->group_list = ssl_preset_default_groups; + +#if defined(MBEDTLS_DHM_C) && defined(MBEDTLS_SSL_CLI_C) + conf->dhm_min_bitlen = 1024; +#endif + } + + return 0; +} + +/* + * Free mbedtls_ssl_config + */ +void mbedtls_ssl_config_free(mbedtls_ssl_config *conf) +{ +#if defined(MBEDTLS_DHM_C) + mbedtls_mpi_free(&conf->dhm_P); + mbedtls_mpi_free(&conf->dhm_G); +#endif + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (!mbedtls_svc_key_id_is_null(conf->psk_opaque)) { + conf->psk_opaque = MBEDTLS_SVC_KEY_ID_INIT; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (conf->psk != NULL) { + mbedtls_platform_zeroize(conf->psk, conf->psk_len); + mbedtls_free(conf->psk); + conf->psk = NULL; + conf->psk_len = 0; + } + + if (conf->psk_identity != NULL) { + mbedtls_platform_zeroize(conf->psk_identity, conf->psk_identity_len); + mbedtls_free(conf->psk_identity); + conf->psk_identity = NULL; + conf->psk_identity_len = 0; + } +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + ssl_key_cert_free(conf->key_cert); +#endif + + mbedtls_platform_zeroize(conf, sizeof(mbedtls_ssl_config)); +} + +#if defined(MBEDTLS_PK_C) && \ + (defined(MBEDTLS_RSA_C) || defined(MBEDTLS_PK_CAN_ECDSA_SOME)) +/* + * Convert between MBEDTLS_PK_XXX and SSL_SIG_XXX + */ +unsigned char mbedtls_ssl_sig_from_pk(mbedtls_pk_context *pk) +{ +#if defined(MBEDTLS_RSA_C) + if (mbedtls_pk_can_do(pk, MBEDTLS_PK_RSA)) { + return MBEDTLS_SSL_SIG_RSA; + } +#endif +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + if (mbedtls_pk_can_do(pk, MBEDTLS_PK_ECDSA)) { + return MBEDTLS_SSL_SIG_ECDSA; + } +#endif + return MBEDTLS_SSL_SIG_ANON; +} + +unsigned char mbedtls_ssl_sig_from_pk_alg(mbedtls_pk_type_t type) +{ + switch (type) { + case MBEDTLS_PK_RSA: + return MBEDTLS_SSL_SIG_RSA; + case MBEDTLS_PK_ECDSA: + case MBEDTLS_PK_ECKEY: + return MBEDTLS_SSL_SIG_ECDSA; + default: + return MBEDTLS_SSL_SIG_ANON; + } +} + +mbedtls_pk_type_t mbedtls_ssl_pk_alg_from_sig(unsigned char sig) +{ + switch (sig) { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_SSL_SIG_RSA: + return MBEDTLS_PK_RSA; +#endif +#if defined(MBEDTLS_PK_CAN_ECDSA_SOME) + case MBEDTLS_SSL_SIG_ECDSA: + return MBEDTLS_PK_ECDSA; +#endif + default: + return MBEDTLS_PK_NONE; + } +} +#endif /* MBEDTLS_PK_C && ( MBEDTLS_RSA_C || MBEDTLS_PK_CAN_ECDSA_SOME ) */ + +/* + * Convert from MBEDTLS_SSL_HASH_XXX to MBEDTLS_MD_XXX + */ +mbedtls_md_type_t mbedtls_ssl_md_alg_from_hash(unsigned char hash) +{ + switch (hash) { +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_MD5: + return MBEDTLS_MD_MD5; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA1: + return MBEDTLS_MD_SHA1; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA224: + return MBEDTLS_MD_SHA224; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA256: + return MBEDTLS_MD_SHA256; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA384: + return MBEDTLS_MD_SHA384; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA512: + return MBEDTLS_MD_SHA512; +#endif + default: + return MBEDTLS_MD_NONE; + } +} + +/* + * Convert from MBEDTLS_MD_XXX to MBEDTLS_SSL_HASH_XXX + */ +unsigned char mbedtls_ssl_hash_from_md_alg(int md) +{ + switch (md) { +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_MD5: + return MBEDTLS_SSL_HASH_MD5; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA1: + return MBEDTLS_SSL_HASH_SHA1; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA224: + return MBEDTLS_SSL_HASH_SHA224; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA256: + return MBEDTLS_SSL_HASH_SHA256; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA384: + return MBEDTLS_SSL_HASH_SHA384; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA512: + return MBEDTLS_SSL_HASH_SHA512; +#endif + default: + return MBEDTLS_SSL_HASH_NONE; + } +} + +/* + * Check if a curve proposed by the peer is in our list. + * Return 0 if we're willing to use it, -1 otherwise. + */ +int mbedtls_ssl_check_curve_tls_id(const mbedtls_ssl_context *ssl, uint16_t tls_id) +{ + const uint16_t *group_list = mbedtls_ssl_get_groups(ssl); + + if (group_list == NULL) { + return -1; + } + + for (; *group_list != 0; group_list++) { + if (*group_list == tls_id) { + return 0; + } + } + + return -1; +} + +#if defined(MBEDTLS_ECP_C) +/* + * Same as mbedtls_ssl_check_curve_tls_id() but with a mbedtls_ecp_group_id. + */ +int mbedtls_ssl_check_curve(const mbedtls_ssl_context *ssl, mbedtls_ecp_group_id grp_id) +{ + uint16_t tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id(grp_id); + + if (tls_id == 0) { + return -1; + } + + return mbedtls_ssl_check_curve_tls_id(ssl, tls_id); +} +#endif /* MBEDTLS_ECP_C */ + +#if defined(MBEDTLS_DEBUG_C) +#define EC_NAME(_name_) _name_ +#else +#define EC_NAME(_name_) NULL +#endif + +static const struct { + uint16_t tls_id; + mbedtls_ecp_group_id ecp_group_id; + psa_ecc_family_t psa_family; + uint16_t bits; + const char *name; +} tls_id_match_table[] = +{ +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) || defined(PSA_WANT_ECC_SECP_R1_521) + { 25, MBEDTLS_ECP_DP_SECP521R1, PSA_ECC_FAMILY_SECP_R1, 521, EC_NAME("secp521r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) || defined(PSA_WANT_ECC_BRAINPOOL_P_R1_512) + { 28, MBEDTLS_ECP_DP_BP512R1, PSA_ECC_FAMILY_BRAINPOOL_P_R1, 512, EC_NAME("brainpoolP512r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) || defined(PSA_WANT_ECC_SECP_R1_384) + { 24, MBEDTLS_ECP_DP_SECP384R1, PSA_ECC_FAMILY_SECP_R1, 384, EC_NAME("secp384r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) || defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384) + { 27, MBEDTLS_ECP_DP_BP384R1, PSA_ECC_FAMILY_BRAINPOOL_P_R1, 384, EC_NAME("brainpoolP384r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) || defined(PSA_WANT_ECC_SECP_R1_256) + { 23, MBEDTLS_ECP_DP_SECP256R1, PSA_ECC_FAMILY_SECP_R1, 256, EC_NAME("secp256r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) || defined(PSA_WANT_ECC_SECP_K1_256) + { 22, MBEDTLS_ECP_DP_SECP256K1, PSA_ECC_FAMILY_SECP_K1, 256, EC_NAME("secp256k1") }, +#endif +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) || defined(PSA_WANT_ECC_BRAINPOOL_P_R1_256) + { 26, MBEDTLS_ECP_DP_BP256R1, PSA_ECC_FAMILY_BRAINPOOL_P_R1, 256, EC_NAME("brainpoolP256r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || defined(PSA_WANT_ECC_SECP_R1_224) + { 21, MBEDTLS_ECP_DP_SECP224R1, PSA_ECC_FAMILY_SECP_R1, 224, EC_NAME("secp224r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) || defined(PSA_WANT_ECC_SECP_K1_224) + { 20, MBEDTLS_ECP_DP_SECP224K1, PSA_ECC_FAMILY_SECP_K1, 224, EC_NAME("secp224k1") }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) || defined(PSA_WANT_ECC_SECP_R1_192) + { 19, MBEDTLS_ECP_DP_SECP192R1, PSA_ECC_FAMILY_SECP_R1, 192, EC_NAME("secp192r1") }, +#endif +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) || defined(PSA_WANT_ECC_SECP_K1_192) + { 18, MBEDTLS_ECP_DP_SECP192K1, PSA_ECC_FAMILY_SECP_K1, 192, EC_NAME("secp192k1") }, +#endif +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) || defined(PSA_WANT_ECC_MONTGOMERY_255) + { 29, MBEDTLS_ECP_DP_CURVE25519, PSA_ECC_FAMILY_MONTGOMERY, 255, EC_NAME("x25519") }, +#endif +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) || defined(PSA_WANT_ECC_MONTGOMERY_448) + { 30, MBEDTLS_ECP_DP_CURVE448, PSA_ECC_FAMILY_MONTGOMERY, 448, EC_NAME("x448") }, +#endif + { 0, MBEDTLS_ECP_DP_NONE, 0, 0, NULL }, +}; + +int mbedtls_ssl_get_psa_curve_info_from_tls_id(uint16_t tls_id, + psa_ecc_family_t *family, + size_t *bits) +{ + for (int i = 0; tls_id_match_table[i].tls_id != 0; i++) { + if (tls_id_match_table[i].tls_id == tls_id) { + if (family != NULL) { + *family = tls_id_match_table[i].psa_family; + } + if (bits != NULL) { + *bits = tls_id_match_table[i].bits; + } + return PSA_SUCCESS; + } + } + + return PSA_ERROR_NOT_SUPPORTED; +} + +mbedtls_ecp_group_id mbedtls_ssl_get_ecp_group_id_from_tls_id(uint16_t tls_id) +{ + for (int i = 0; tls_id_match_table[i].tls_id != 0; i++) { + if (tls_id_match_table[i].tls_id == tls_id) { + return tls_id_match_table[i].ecp_group_id; + } + } + + return MBEDTLS_ECP_DP_NONE; +} + +uint16_t mbedtls_ssl_get_tls_id_from_ecp_group_id(mbedtls_ecp_group_id grp_id) +{ + for (int i = 0; tls_id_match_table[i].ecp_group_id != MBEDTLS_ECP_DP_NONE; + i++) { + if (tls_id_match_table[i].ecp_group_id == grp_id) { + return tls_id_match_table[i].tls_id; + } + } + + return 0; +} + +#if defined(MBEDTLS_DEBUG_C) +const char *mbedtls_ssl_get_curve_name_from_tls_id(uint16_t tls_id) +{ + for (int i = 0; tls_id_match_table[i].tls_id != 0; i++) { + if (tls_id_match_table[i].tls_id == tls_id) { + return tls_id_match_table[i].name; + } + } + + return NULL; +} +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +int mbedtls_ssl_check_cert_usage(const mbedtls_x509_crt *cert, + const mbedtls_ssl_ciphersuite_t *ciphersuite, + int cert_endpoint, + uint32_t *flags) +{ + int ret = 0; + int usage = 0; + const char *ext_oid; + size_t ext_len; + + if (cert_endpoint == MBEDTLS_SSL_IS_SERVER) { + /* Server part of the key exchange */ + switch (ciphersuite->key_exchange) { + case MBEDTLS_KEY_EXCHANGE_RSA: + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + usage = MBEDTLS_X509_KU_KEY_ENCIPHERMENT; + break; + + case MBEDTLS_KEY_EXCHANGE_DHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA: + usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE; + break; + + case MBEDTLS_KEY_EXCHANGE_ECDH_RSA: + case MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA: + usage = MBEDTLS_X509_KU_KEY_AGREEMENT; + break; + + /* Don't use default: we want warnings when adding new values */ + case MBEDTLS_KEY_EXCHANGE_NONE: + case MBEDTLS_KEY_EXCHANGE_PSK: + case MBEDTLS_KEY_EXCHANGE_DHE_PSK: + case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK: + case MBEDTLS_KEY_EXCHANGE_ECJPAKE: + usage = 0; + } + } else { + /* Client auth: we only implement rsa_sign and mbedtls_ecdsa_sign for now */ + usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE; + } + + if (mbedtls_x509_crt_check_key_usage(cert, usage) != 0) { + *flags |= MBEDTLS_X509_BADCERT_KEY_USAGE; + ret = -1; + } + + if (cert_endpoint == MBEDTLS_SSL_IS_SERVER) { + ext_oid = MBEDTLS_OID_SERVER_AUTH; + ext_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH); + } else { + ext_oid = MBEDTLS_OID_CLIENT_AUTH; + ext_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH); + } + + if (mbedtls_x509_crt_check_extended_key_usage(cert, ext_oid, ext_len) != 0) { + *flags |= MBEDTLS_X509_BADCERT_EXT_KEY_USAGE; + ret = -1; + } + + return ret; +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +int mbedtls_ssl_get_handshake_transcript(mbedtls_ssl_context *ssl, + const mbedtls_md_type_t md, + unsigned char *dst, + size_t dst_len, + size_t *olen) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_hash_operation_t *hash_operation_to_clone; + psa_hash_operation_t hash_operation = psa_hash_operation_init(); + + *olen = 0; + + switch (md) { +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA384: + hash_operation_to_clone = &ssl->handshake->fin_sha384_psa; + break; +#endif + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA256: + hash_operation_to_clone = &ssl->handshake->fin_sha256_psa; + break; +#endif + + default: + goto exit; + } + + status = psa_hash_clone(hash_operation_to_clone, &hash_operation); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&hash_operation, dst, dst_len, olen); + if (status != PSA_SUCCESS) { + goto exit; + } + +exit: +#if !defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + !defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + (void) ssl; +#endif + return PSA_TO_MBEDTLS_ERR(status); +} +#else /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_handshake_transcript_sha384(mbedtls_ssl_context *ssl, + unsigned char *dst, + size_t dst_len, + size_t *olen) +{ + int ret; + mbedtls_md_context_t sha384; + + if (dst_len < 48) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + mbedtls_md_init(&sha384); + ret = mbedtls_md_setup(&sha384, mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), 0); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_clone(&sha384, &ssl->handshake->fin_sha384); + if (ret != 0) { + goto exit; + } + + if ((ret = mbedtls_md_finish(&sha384, dst)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_finish", ret); + goto exit; + } + + *olen = 48; + +exit: + + mbedtls_md_free(&sha384); + return ret; +} +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_handshake_transcript_sha256(mbedtls_ssl_context *ssl, + unsigned char *dst, + size_t dst_len, + size_t *olen) +{ + int ret; + mbedtls_md_context_t sha256; + + if (dst_len < 32) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + mbedtls_md_init(&sha256); + ret = mbedtls_md_setup(&sha256, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_clone(&sha256, &ssl->handshake->fin_sha256); + if (ret != 0) { + goto exit; + } + + if ((ret = mbedtls_md_finish(&sha256, dst)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_finish", ret); + goto exit; + } + + *olen = 32; + +exit: + + mbedtls_md_free(&sha256); + return ret; +} +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +int mbedtls_ssl_get_handshake_transcript(mbedtls_ssl_context *ssl, + const mbedtls_md_type_t md, + unsigned char *dst, + size_t dst_len, + size_t *olen) +{ + switch (md) { + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA384: + return ssl_get_handshake_transcript_sha384(ssl, dst, dst_len, olen); +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_MD_SHA256: + return ssl_get_handshake_transcript_sha256(ssl, dst, dst_len, olen); +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + + default: +#if !defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + !defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + (void) ssl; + (void) dst; + (void) dst_len; + (void) olen; +#endif + break; + } + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} + +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +/* mbedtls_ssl_parse_sig_alg_ext() + * + * The `extension_data` field of signature algorithm contains a `SignatureSchemeList` + * value (TLS 1.3 RFC8446): + * enum { + * .... + * ecdsa_secp256r1_sha256( 0x0403 ), + * ecdsa_secp384r1_sha384( 0x0503 ), + * ecdsa_secp521r1_sha512( 0x0603 ), + * .... + * } SignatureScheme; + * + * struct { + * SignatureScheme supported_signature_algorithms<2..2^16-2>; + * } SignatureSchemeList; + * + * The `extension_data` field of signature algorithm contains a `SignatureAndHashAlgorithm` + * value (TLS 1.2 RFC5246): + * enum { + * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + * sha512(6), (255) + * } HashAlgorithm; + * + * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + * SignatureAlgorithm; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2..2^16-2>; + * + * The TLS 1.3 signature algorithm extension was defined to be a compatible + * generalization of the TLS 1.2 signature algorithm extension. + * `SignatureAndHashAlgorithm` field of TLS 1.2 can be represented by + * `SignatureScheme` field of TLS 1.3 + * + */ +int mbedtls_ssl_parse_sig_alg_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + const unsigned char *p = buf; + size_t supported_sig_algs_len = 0; + const unsigned char *supported_sig_algs_end; + uint16_t sig_alg; + uint32_t common_idx = 0; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + supported_sig_algs_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + memset(ssl->handshake->received_sig_algs, 0, + sizeof(ssl->handshake->received_sig_algs)); + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, supported_sig_algs_len); + supported_sig_algs_end = p + supported_sig_algs_len; + while (p < supported_sig_algs_end) { + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, supported_sig_algs_end, 2); + sig_alg = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_DEBUG_MSG(4, ("received signature algorithm: 0x%x %s", + sig_alg, + mbedtls_ssl_sig_alg_to_str(sig_alg))); +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + if (ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2 && + (!(mbedtls_ssl_sig_alg_is_supported(ssl, sig_alg) && + mbedtls_ssl_sig_alg_is_offered(ssl, sig_alg)))) { + continue; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + MBEDTLS_SSL_DEBUG_MSG(4, ("valid signature algorithm: %s", + mbedtls_ssl_sig_alg_to_str(sig_alg))); + + if (common_idx + 1 < MBEDTLS_RECEIVED_SIG_ALGS_SIZE) { + ssl->handshake->received_sig_algs[common_idx] = sig_alg; + common_idx += 1; + } + } + /* Check that we consumed all the message. */ + if (p != end) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("Signature algorithms extension length misaligned")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if (common_idx == 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("no signature algorithm in common")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + ssl->handshake->received_sig_algs[common_idx] = MBEDTLS_TLS_SIG_NONE; + return 0; +} + +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + +static psa_status_t setup_psa_key_derivation(psa_key_derivation_operation_t *derivation, + mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const unsigned char *raw_psk, size_t raw_psk_length, + const unsigned char *seed, size_t seed_length, + const unsigned char *label, size_t label_length, + const unsigned char *other_secret, + size_t other_secret_length, + size_t capacity) +{ + psa_status_t status; + + status = psa_key_derivation_setup(derivation, alg); + if (status != PSA_SUCCESS) { + return status; + } + + if (PSA_ALG_IS_TLS12_PRF(alg) || PSA_ALG_IS_TLS12_PSK_TO_MS(alg)) { + status = psa_key_derivation_input_bytes(derivation, + PSA_KEY_DERIVATION_INPUT_SEED, + seed, seed_length); + if (status != PSA_SUCCESS) { + return status; + } + + if (other_secret != NULL) { + status = psa_key_derivation_input_bytes(derivation, + PSA_KEY_DERIVATION_INPUT_OTHER_SECRET, + other_secret, other_secret_length); + if (status != PSA_SUCCESS) { + return status; + } + } + + if (mbedtls_svc_key_id_is_null(key)) { + status = psa_key_derivation_input_bytes( + derivation, PSA_KEY_DERIVATION_INPUT_SECRET, + raw_psk, raw_psk_length); + } else { + status = psa_key_derivation_input_key( + derivation, PSA_KEY_DERIVATION_INPUT_SECRET, key); + } + if (status != PSA_SUCCESS) { + return status; + } + + status = psa_key_derivation_input_bytes(derivation, + PSA_KEY_DERIVATION_INPUT_LABEL, + label, label_length); + if (status != PSA_SUCCESS) { + return status; + } + } else { + return PSA_ERROR_NOT_SUPPORTED; + } + + status = psa_key_derivation_set_capacity(derivation, capacity); + if (status != PSA_SUCCESS) { + return status; + } + + return PSA_SUCCESS; +} + +#if defined(PSA_WANT_ALG_SHA_384) || \ + defined(PSA_WANT_ALG_SHA_256) +MBEDTLS_CHECK_RETURN_CRITICAL +static int tls_prf_generic(mbedtls_md_type_t md_type, + const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen) +{ + psa_status_t status; + psa_algorithm_t alg; + mbedtls_svc_key_id_t master_key = MBEDTLS_SVC_KEY_ID_INIT; + psa_key_derivation_operation_t derivation = + PSA_KEY_DERIVATION_OPERATION_INIT; + + if (md_type == MBEDTLS_MD_SHA384) { + alg = PSA_ALG_TLS12_PRF(PSA_ALG_SHA_384); + } else { + alg = PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256); + } + + /* Normally a "secret" should be long enough to be impossible to + * find by brute force, and in particular should not be empty. But + * this PRF is also used to derive an IV, in particular in EAP-TLS, + * and for this use case it makes sense to have a 0-length "secret". + * Since the key API doesn't allow importing a key of length 0, + * keep master_key=0, which setup_psa_key_derivation() understands + * to mean a 0-length "secret" input. */ + if (slen != 0) { + psa_key_attributes_t key_attributes = psa_key_attributes_init(); + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&key_attributes, alg); + psa_set_key_type(&key_attributes, PSA_KEY_TYPE_DERIVE); + + status = psa_import_key(&key_attributes, secret, slen, &master_key); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + } + + status = setup_psa_key_derivation(&derivation, + master_key, alg, + NULL, 0, + random, rlen, + (unsigned char const *) label, + (size_t) strlen(label), + NULL, 0, + dlen); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(&derivation); + psa_destroy_key(master_key); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_key_derivation_output_bytes(&derivation, dstbuf, dlen); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(&derivation); + psa_destroy_key(master_key); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_key_derivation_abort(&derivation); + if (status != PSA_SUCCESS) { + psa_destroy_key(master_key); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + if (!mbedtls_svc_key_id_is_null(master_key)) { + status = psa_destroy_key(master_key); + } + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + return 0; +} +#endif /* PSA_WANT_ALG_SHA_256 || PSA_WANT_ALG_SHA_384 */ +#else /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_MD_C) && \ + (defined(MBEDTLS_SHA256_C) || \ + defined(MBEDTLS_SHA384_C)) +MBEDTLS_CHECK_RETURN_CRITICAL +static int tls_prf_generic(mbedtls_md_type_t md_type, + const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen) +{ + size_t nb; + size_t i, j, k, md_len; + unsigned char *tmp; + size_t tmp_len = 0; + unsigned char h_i[MBEDTLS_MD_MAX_SIZE]; + const mbedtls_md_info_t *md_info; + mbedtls_md_context_t md_ctx; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_md_init(&md_ctx); + + if ((md_info = mbedtls_md_info_from_type(md_type)) == NULL) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + md_len = mbedtls_md_get_size(md_info); + + tmp_len = md_len + strlen(label) + rlen; + tmp = mbedtls_calloc(1, tmp_len); + if (tmp == NULL) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto exit; + } + + nb = strlen(label); + memcpy(tmp + md_len, label, nb); + memcpy(tmp + md_len + nb, random, rlen); + nb += rlen; + + /* + * Compute P_(secret, label + random)[0..dlen] + */ + if ((ret = mbedtls_md_setup(&md_ctx, md_info, 1)) != 0) { + goto exit; + } + + ret = mbedtls_md_hmac_starts(&md_ctx, secret, slen); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_hmac_update(&md_ctx, tmp + md_len, nb); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_hmac_finish(&md_ctx, tmp); + if (ret != 0) { + goto exit; + } + + for (i = 0; i < dlen; i += md_len) { + ret = mbedtls_md_hmac_reset(&md_ctx); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_hmac_update(&md_ctx, tmp, md_len + nb); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_hmac_finish(&md_ctx, h_i); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_md_hmac_reset(&md_ctx); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_hmac_update(&md_ctx, tmp, md_len); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_hmac_finish(&md_ctx, tmp); + if (ret != 0) { + goto exit; + } + + k = (i + md_len > dlen) ? dlen % md_len : md_len; + + for (j = 0; j < k; j++) { + dstbuf[i + j] = h_i[j]; + } + } + +exit: + mbedtls_md_free(&md_ctx); + + if (tmp != NULL) { + mbedtls_platform_zeroize(tmp, tmp_len); + } + + mbedtls_platform_zeroize(h_i, sizeof(h_i)); + + mbedtls_free(tmp); + + return ret; +} +#endif /* MBEDTLS_MD_C && ( MBEDTLS_SHA256_C || MBEDTLS_SHA384_C ) */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +MBEDTLS_CHECK_RETURN_CRITICAL +static int tls_prf_sha256(const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen) +{ + return tls_prf_generic(MBEDTLS_MD_SHA256, secret, slen, + label, random, rlen, dstbuf, dlen); +} +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +MBEDTLS_CHECK_RETURN_CRITICAL +static int tls_prf_sha384(const unsigned char *secret, size_t slen, + const char *label, + const unsigned char *random, size_t rlen, + unsigned char *dstbuf, size_t dlen) +{ + return tls_prf_generic(MBEDTLS_MD_SHA384, secret, slen, + label, random, rlen, dstbuf, dlen); +} +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +/* + * Set appropriate PRF function and other SSL / TLS1.2 functions + * + * Inputs: + * - hash associated with the ciphersuite (only used by TLS 1.2) + * + * Outputs: + * - the tls_prf, calc_verify and calc_finished members of handshake structure + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_set_handshake_prfs(mbedtls_ssl_handshake_params *handshake, + mbedtls_md_type_t hash) +{ +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + if (hash == MBEDTLS_MD_SHA384) { + handshake->tls_prf = tls_prf_sha384; + handshake->calc_verify = ssl_calc_verify_tls_sha384; + handshake->calc_finished = ssl_calc_finished_tls_sha384; + } else +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { + (void) hash; + handshake->tls_prf = tls_prf_sha256; + handshake->calc_verify = ssl_calc_verify_tls_sha256; + handshake->calc_finished = ssl_calc_finished_tls_sha256; + } +#else + { + (void) handshake; + (void) hash; + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +#endif + + return 0; +} + +/* + * Compute master secret if needed + * + * Parameters: + * [in/out] handshake + * [in] resume, premaster, extended_ms, calc_verify, tls_prf + * (PSA-PSK) ciphersuite_info, psk_opaque + * [out] premaster (cleared) + * [out] master + * [in] ssl: optionally used for debugging, EMS and PSA-PSK + * debug: conf->f_dbg, conf->p_dbg + * EMS: passed to calc_verify (debug + session_negotiate) + * PSA-PSA: conf + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_compute_master(mbedtls_ssl_handshake_params *handshake, + unsigned char *master, + const mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* cf. RFC 5246, Section 8.1: + * "The master secret is always exactly 48 bytes in length." */ + size_t const master_secret_len = 48; + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + unsigned char session_hash[48]; +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + + /* The label for the KDF used for key expansion. + * This is either "master secret" or "extended master secret" + * depending on whether the Extended Master Secret extension + * is used. */ + char const *lbl = "master secret"; + + /* The seed for the KDF used for key expansion. + * - If the Extended Master Secret extension is not used, + * this is ClientHello.Random + ServerHello.Random + * (see Sect. 8.1 in RFC 5246). + * - If the Extended Master Secret extension is used, + * this is the transcript of the handshake so far. + * (see Sect. 4 in RFC 7627). */ + unsigned char const *seed = handshake->randbytes; + size_t seed_len = 64; + +#if !defined(MBEDTLS_DEBUG_C) && \ + !defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) && \ + !(defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)) + ssl = NULL; /* make sure we don't use it except for those cases */ + (void) ssl; +#endif + + if (handshake->resume != 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("no premaster (session resumed)")); + return 0; + } + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + if (handshake->extended_ms == MBEDTLS_SSL_EXTENDED_MS_ENABLED) { + lbl = "extended master secret"; + seed = session_hash; + ret = handshake->calc_verify(ssl, session_hash, &seed_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "calc_verify", ret); + } + + MBEDTLS_SSL_DEBUG_BUF(3, "session hash for extended master secret", + session_hash, seed_len); + } +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_psk(handshake->ciphersuite_info) == 1) { + /* Perform PSK-to-MS expansion in a single step. */ + psa_status_t status; + psa_algorithm_t alg; + mbedtls_svc_key_id_t psk; + psa_key_derivation_operation_t derivation = + PSA_KEY_DERIVATION_OPERATION_INIT; + mbedtls_md_type_t hash_alg = handshake->ciphersuite_info->mac; + + MBEDTLS_SSL_DEBUG_MSG(2, ("perform PSA-based PSK-to-MS expansion")); + + psk = mbedtls_ssl_get_opaque_psk(ssl); + + if (hash_alg == MBEDTLS_MD_SHA384) { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_384); + } else { + alg = PSA_ALG_TLS12_PSK_TO_MS(PSA_ALG_SHA_256); + } + + size_t other_secret_len = 0; + unsigned char *other_secret = NULL; + + switch (handshake->ciphersuite_info->key_exchange) { + /* Provide other secret. + * Other secret is stored in premaster, where first 2 bytes hold the + * length of the other key. + */ + case MBEDTLS_KEY_EXCHANGE_RSA_PSK: + /* For RSA-PSK other key length is always 48 bytes. */ + other_secret_len = 48; + other_secret = handshake->premaster + 2; + break; + case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK: + case MBEDTLS_KEY_EXCHANGE_DHE_PSK: + other_secret_len = MBEDTLS_GET_UINT16_BE(handshake->premaster, 0); + other_secret = handshake->premaster + 2; + break; + default: + break; + } + + status = setup_psa_key_derivation(&derivation, psk, alg, + ssl->conf->psk, ssl->conf->psk_len, + seed, seed_len, + (unsigned char const *) lbl, + (size_t) strlen(lbl), + other_secret, other_secret_len, + master_secret_len); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(&derivation); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_key_derivation_output_bytes(&derivation, + master, + master_secret_len); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(&derivation); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_key_derivation_abort(&derivation); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + } else +#endif + { +#if defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if (handshake->ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE) { + psa_status_t status; + psa_algorithm_t alg = PSA_ALG_TLS12_ECJPAKE_TO_PMS; + psa_key_derivation_operation_t derivation = + PSA_KEY_DERIVATION_OPERATION_INIT; + + MBEDTLS_SSL_DEBUG_MSG(2, ("perform PSA-based PMS KDF for ECJPAKE")); + + handshake->pmslen = PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE; + + status = psa_key_derivation_setup(&derivation, alg); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_key_derivation_set_capacity(&derivation, + PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(&derivation); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_pake_get_implicit_key(&handshake->psa_pake_ctx, + &derivation); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(&derivation); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_key_derivation_output_bytes(&derivation, + handshake->premaster, + handshake->pmslen); + if (status != PSA_SUCCESS) { + psa_key_derivation_abort(&derivation); + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + status = psa_key_derivation_abort(&derivation); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + } +#endif + ret = handshake->tls_prf(handshake->premaster, handshake->pmslen, + lbl, seed, seed_len, + master, + master_secret_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "prf", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "premaster secret", + handshake->premaster, + handshake->pmslen); + + mbedtls_platform_zeroize(handshake->premaster, + sizeof(handshake->premaster)); + } + + return 0; +} + +int mbedtls_ssl_derive_keys(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_ssl_ciphersuite_t * const ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> derive keys")); + + /* Set PRF, calc_verify and calc_finished function pointers */ + ret = ssl_set_handshake_prfs(ssl->handshake, + ciphersuite_info->mac); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_set_handshake_prfs", ret); + return ret; + } + + /* Compute master secret if needed */ + ret = ssl_compute_master(ssl->handshake, + ssl->session_negotiate->master, + ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_compute_master", ret); + return ret; + } + + /* Swap the client and server random values: + * - MS derivation wanted client+server (RFC 5246 8.1) + * - key derivation wants server+client (RFC 5246 6.3) */ + { + unsigned char tmp[64]; + memcpy(tmp, ssl->handshake->randbytes, 64); + memcpy(ssl->handshake->randbytes, tmp + 32, 32); + memcpy(ssl->handshake->randbytes + 32, tmp, 32); + mbedtls_platform_zeroize(tmp, sizeof(tmp)); + } + + /* Populate transform structure */ + ret = ssl_tls12_populate_transform(ssl->transform_negotiate, + ssl->session_negotiate->ciphersuite, + ssl->session_negotiate->master, +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + ssl->session_negotiate->encrypt_then_mac, +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + ssl->handshake->tls_prf, + ssl->handshake->randbytes, + ssl->tls_version, + ssl->conf->endpoint, + ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls12_populate_transform", ret); + return ret; + } + + /* We no longer need Server/ClientHello.random values */ + mbedtls_platform_zeroize(ssl->handshake->randbytes, + sizeof(ssl->handshake->randbytes)); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= derive keys")); + + return 0; +} + +int mbedtls_ssl_set_calc_verify_md(mbedtls_ssl_context *ssl, int md) +{ + switch (md) { +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA384: + ssl->handshake->calc_verify = ssl_calc_verify_tls_sha384; + break; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + case MBEDTLS_SSL_HASH_SHA256: + ssl->handshake->calc_verify = ssl_calc_verify_tls_sha256; + break; +#endif + default: + return -1; + } +#if !defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + !defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + (void) ssl; +#endif + return 0; +} + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +int ssl_calc_verify_tls_sha256(const mbedtls_ssl_context *ssl, + unsigned char *hash, + size_t *hlen) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t hash_size; + psa_status_t status; + psa_hash_operation_t sha256_psa = psa_hash_operation_init(); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> PSA calc verify sha256")); + status = psa_hash_clone(&ssl->handshake->fin_sha256_psa, &sha256_psa); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&sha256_psa, hash, 32, &hash_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + *hlen = 32; + MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated verify result", hash, *hlen); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= PSA calc verify")); + +exit: + psa_hash_abort(&sha256_psa); + return PSA_TO_MD_ERR(status); +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_context_t sha256; + + mbedtls_md_init(&sha256); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> calc verify sha256")); + + ret = mbedtls_md_setup(&sha256, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_clone(&sha256, &ssl->handshake->fin_sha256); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_md_finish(&sha256, hash); + if (ret != 0) { + goto exit; + } + + *hlen = 32; + + MBEDTLS_SSL_DEBUG_BUF(3, "calculated verify result", hash, *hlen); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc verify")); + +exit: + mbedtls_md_free(&sha256); + return ret; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +int ssl_calc_verify_tls_sha384(const mbedtls_ssl_context *ssl, + unsigned char *hash, + size_t *hlen) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t hash_size; + psa_status_t status; + psa_hash_operation_t sha384_psa = psa_hash_operation_init(); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> PSA calc verify sha384")); + status = psa_hash_clone(&ssl->handshake->fin_sha384_psa, &sha384_psa); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&sha384_psa, hash, 48, &hash_size); + if (status != PSA_SUCCESS) { + goto exit; + } + + *hlen = 48; + MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated verify result", hash, *hlen); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= PSA calc verify")); + +exit: + psa_hash_abort(&sha384_psa); + return PSA_TO_MD_ERR(status); +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_context_t sha384; + + mbedtls_md_init(&sha384); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> calc verify sha384")); + + ret = mbedtls_md_setup(&sha384, mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), 0); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_clone(&sha384, &ssl->handshake->fin_sha384); + if (ret != 0) { + goto exit; + } + + ret = mbedtls_md_finish(&sha384, hash); + if (ret != 0) { + goto exit; + } + + *hlen = 48; + + MBEDTLS_SSL_DEBUG_BUF(3, "calculated verify result", hash, *hlen); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc verify")); + +exit: + mbedtls_md_free(&sha384); + return ret; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) +int mbedtls_ssl_psk_derive_premaster(mbedtls_ssl_context *ssl, mbedtls_key_exchange_type_t key_ex) +{ + unsigned char *p = ssl->handshake->premaster; + unsigned char *end = p + sizeof(ssl->handshake->premaster); + const unsigned char *psk = NULL; + size_t psk_len = 0; + int psk_ret = mbedtls_ssl_get_psk(ssl, &psk, &psk_len); + + if (psk_ret == MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED) { + /* + * This should never happen because the existence of a PSK is always + * checked before calling this function. + * + * The exception is opaque DHE-PSK. For DHE-PSK fill premaster with + * the shared secret without PSK. + */ + if (key_ex != MBEDTLS_KEY_EXCHANGE_DHE_PSK) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + } + + /* + * PMS = struct { + * opaque other_secret<0..2^16-1>; + * opaque psk<0..2^16-1>; + * }; + * with "other_secret" depending on the particular key exchange + */ +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + if (key_ex == MBEDTLS_KEY_EXCHANGE_PSK) { + if (end - p < 2) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + MBEDTLS_PUT_UINT16_BE(psk_len, p, 0); + p += 2; + + if (end < p || (size_t) (end - p) < psk_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + memset(p, 0, psk_len); + p += psk_len; + } else +#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + if (key_ex == MBEDTLS_KEY_EXCHANGE_RSA_PSK) { + /* + * other_secret already set by the ClientKeyExchange message, + * and is 48 bytes long + */ + if (end - p < 2) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + *p++ = 0; + *p++ = 48; + p += 48; + } else +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + if (key_ex == MBEDTLS_KEY_EXCHANGE_DHE_PSK) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + /* Write length only when we know the actual value */ + if ((ret = mbedtls_dhm_calc_secret(&ssl->handshake->dhm_ctx, + p + 2, end - (p + 2), &len, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_calc_secret", ret); + return ret; + } + MBEDTLS_PUT_UINT16_BE(len, p, 0); + p += 2 + len; + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: K ", &ssl->handshake->dhm_ctx.K); + } else +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if (key_ex == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t zlen; + + if ((ret = mbedtls_ecdh_calc_secret(&ssl->handshake->ecdh_ctx, &zlen, + p + 2, end - (p + 2), + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_calc_secret", ret); + return ret; + } + + MBEDTLS_PUT_UINT16_BE(zlen, p, 0); + p += 2 + zlen; + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_Z); + } else +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* opaque psk<0..2^16-1>; */ + if (end - p < 2) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + MBEDTLS_PUT_UINT16_BE(psk_len, p, 0); + p += 2; + + if (end < p || (size_t) (end - p) < psk_len) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + memcpy(p, psk, psk_len); + p += psk_len; + + ssl->handshake->pmslen = p - ssl->handshake->premaster; + + return 0; +} +#endif /* !MBEDTLS_USE_PSA_CRYPTO && MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ + +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_RENEGOTIATION) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_hello_request(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) +int mbedtls_ssl_resend_hello_request(mbedtls_ssl_context *ssl) +{ + /* If renegotiation is not enforced, retransmit until we would reach max + * timeout if we were using the usual handshake doubling scheme */ + if (ssl->conf->renego_max_records < 0) { + uint32_t ratio = ssl->conf->hs_timeout_max / ssl->conf->hs_timeout_min + 1; + unsigned char doublings = 1; + + while (ratio != 0) { + ++doublings; + ratio >>= 1; + } + + if (++ssl->renego_records_seen > doublings) { + MBEDTLS_SSL_DEBUG_MSG(2, ("no longer retransmitting hello request")); + return 0; + } + } + + return ssl_write_hello_request(ssl); +} +#endif +#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_RENEGOTIATION */ + +/* + * Handshake functions + */ +#if !defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) +/* No certificate support -> dummy functions */ +int mbedtls_ssl_write_certificate(mbedtls_ssl_context *ssl) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate")); + + if (!mbedtls_ssl_ciphersuite_uses_srv_cert(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate")); + ssl->state++; + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} + +int mbedtls_ssl_parse_certificate(mbedtls_ssl_context *ssl) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate")); + + if (!mbedtls_ssl_ciphersuite_uses_srv_cert(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate")); + ssl->state++; + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} + +#else /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ +/* Some certificate support -> implement write and parse */ + +int mbedtls_ssl_write_certificate(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + size_t i, n; + const mbedtls_x509_crt *crt; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate")); + + if (!mbedtls_ssl_ciphersuite_uses_srv_cert(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate")); + ssl->state++; + return 0; + } + +#if defined(MBEDTLS_SSL_CLI_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + if (ssl->handshake->client_auth == 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate")); + ssl->state++; + return 0; + } + } +#endif /* MBEDTLS_SSL_CLI_C */ +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + if (mbedtls_ssl_own_cert(ssl) == NULL) { + /* Should never happen because we shouldn't have picked the + * ciphersuite if we don't have a certificate. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + } +#endif + + MBEDTLS_SSL_DEBUG_CRT(3, "own certificate", mbedtls_ssl_own_cert(ssl)); + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 6 length of all certs + * 7 . 9 length of cert. 1 + * 10 . n-1 peer certificate + * n . n+2 length of cert. 2 + * n+3 . ... upper level cert, etc. + */ + i = 7; + crt = mbedtls_ssl_own_cert(ssl); + + while (crt != NULL) { + n = crt->raw.len; + if (n > MBEDTLS_SSL_OUT_CONTENT_LEN - 3 - i) { + MBEDTLS_SSL_DEBUG_MSG(1, ("certificate too large, %" MBEDTLS_PRINTF_SIZET + " > %" MBEDTLS_PRINTF_SIZET, + i + 3 + n, (size_t) MBEDTLS_SSL_OUT_CONTENT_LEN)); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + ssl->out_msg[i] = MBEDTLS_BYTE_2(n); + ssl->out_msg[i + 1] = MBEDTLS_BYTE_1(n); + ssl->out_msg[i + 2] = MBEDTLS_BYTE_0(n); + + i += 3; memcpy(ssl->out_msg + i, crt->raw.p, n); + i += n; crt = crt->next; + } + + ssl->out_msg[4] = MBEDTLS_BYTE_2(i - 7); + ssl->out_msg[5] = MBEDTLS_BYTE_1(i - 7); + ssl->out_msg[6] = MBEDTLS_BYTE_0(i - 7); + + ssl->out_msglen = i; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_CERTIFICATE; + + ssl->state++; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write certificate")); + + return ret; +} + +#if defined(MBEDTLS_SSL_RENEGOTIATION) && defined(MBEDTLS_SSL_CLI_C) + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_peer_crt_unchanged(mbedtls_ssl_context *ssl, + unsigned char *crt_buf, + size_t crt_buf_len) +{ + mbedtls_x509_crt const * const peer_crt = ssl->session->peer_cert; + + if (peer_crt == NULL) { + return -1; + } + + if (peer_crt->raw.len != crt_buf_len) { + return -1; + } + + return memcmp(peer_crt->raw.p, crt_buf, peer_crt->raw.len); +} +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_peer_crt_unchanged(mbedtls_ssl_context *ssl, + unsigned char *crt_buf, + size_t crt_buf_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char const * const peer_cert_digest = + ssl->session->peer_cert_digest; + mbedtls_md_type_t const peer_cert_digest_type = + ssl->session->peer_cert_digest_type; + mbedtls_md_info_t const * const digest_info = + mbedtls_md_info_from_type(peer_cert_digest_type); + unsigned char tmp_digest[MBEDTLS_SSL_PEER_CERT_DIGEST_MAX_LEN]; + size_t digest_len; + + if (peer_cert_digest == NULL || digest_info == NULL) { + return -1; + } + + digest_len = mbedtls_md_get_size(digest_info); + if (digest_len > MBEDTLS_SSL_PEER_CERT_DIGEST_MAX_LEN) { + return -1; + } + + ret = mbedtls_md(digest_info, crt_buf, crt_buf_len, tmp_digest); + if (ret != 0) { + return -1; + } + + return memcmp(tmp_digest, peer_cert_digest, digest_len); +} +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_SSL_RENEGOTIATION && MBEDTLS_SSL_CLI_C */ + +/* + * Once the certificate message is read, parse it into a cert chain and + * perform basic checks, but leave actual verification to the caller + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_certificate_chain(mbedtls_ssl_context *ssl, + mbedtls_x509_crt *chain) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#if defined(MBEDTLS_SSL_RENEGOTIATION) && defined(MBEDTLS_SSL_CLI_C) + int crt_cnt = 0; +#endif + size_t i, n; + uint8_t alert; + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + if (ssl->in_msg[0] != MBEDTLS_SSL_HS_CERTIFICATE) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + if (ssl->in_hslen < mbedtls_ssl_hs_hdr_len(ssl) + 3 + 3) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + i = mbedtls_ssl_hs_hdr_len(ssl); + + /* + * Same message structure as in mbedtls_ssl_write_certificate() + */ + n = (ssl->in_msg[i+1] << 8) | ssl->in_msg[i+2]; + + if (ssl->in_msg[i] != 0 || + ssl->in_hslen != n + 3 + mbedtls_ssl_hs_hdr_len(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Make &ssl->in_msg[i] point to the beginning of the CRT chain. */ + i += 3; + + /* Iterate through and parse the CRTs in the provided chain. */ + while (i < ssl->in_hslen) { + /* Check that there's room for the next CRT's length fields. */ + if (i + 3 > ssl->in_hslen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate message")); + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + /* In theory, the CRT can be up to 2**24 Bytes, but we don't support + * anything beyond 2**16 ~ 64K. */ + if (ssl->in_msg[i] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate message")); + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT); + return MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } + + /* Read length of the next CRT in the chain. */ + n = ((unsigned int) ssl->in_msg[i + 1] << 8) + | (unsigned int) ssl->in_msg[i + 2]; + i += 3; + + if (n < 128 || i + n > ssl->in_hslen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate message")); + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Check if we're handling the first CRT in the chain. */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) && defined(MBEDTLS_SSL_CLI_C) + if (crt_cnt++ == 0 && + ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && + ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { + /* During client-side renegotiation, check that the server's + * end-CRTs hasn't changed compared to the initial handshake, + * mitigating the triple handshake attack. On success, reuse + * the original end-CRT instead of parsing it again. */ + MBEDTLS_SSL_DEBUG_MSG(3, ("Check that peer CRT hasn't changed during renegotiation")); + if (ssl_check_peer_crt_unchanged(ssl, + &ssl->in_msg[i], + n) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("new server cert during renegotiation")); + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED); + return MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } + + /* Now we can safely free the original chain. */ + ssl_clear_peer_cert(ssl->session); + } +#endif /* MBEDTLS_SSL_RENEGOTIATION && MBEDTLS_SSL_CLI_C */ + + /* Parse the next certificate in the chain. */ +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + ret = mbedtls_x509_crt_parse_der(chain, ssl->in_msg + i, n); +#else + /* If we don't need to store the CRT chain permanently, parse + * it in-place from the input buffer instead of making a copy. */ + ret = mbedtls_x509_crt_parse_der_nocopy(chain, ssl->in_msg + i, n); +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + switch (ret) { + case 0: /*ok*/ + case MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG + MBEDTLS_ERR_OID_NOT_FOUND: + /* Ignore certificate with an unknown algorithm: maybe a + prior certificate was already trusted. */ + break; + + case MBEDTLS_ERR_X509_ALLOC_FAILED: + alert = MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR; + goto crt_parse_der_failed; + + case MBEDTLS_ERR_X509_UNKNOWN_VERSION: + alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT; + goto crt_parse_der_failed; + + default: + alert = MBEDTLS_SSL_ALERT_MSG_BAD_CERT; +crt_parse_der_failed: + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, alert); + MBEDTLS_SSL_DEBUG_RET(1, " mbedtls_x509_crt_parse_der", ret); + return ret; + } + + i += n; + } + + MBEDTLS_SSL_DEBUG_CRT(3, "peer certificate", chain); + return 0; +} + +#if defined(MBEDTLS_SSL_SRV_C) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_srv_check_client_no_crt_notification(mbedtls_ssl_context *ssl) +{ + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + return -1; + } + + if (ssl->in_hslen == 3 + mbedtls_ssl_hs_hdr_len(ssl) && + ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE && + ssl->in_msg[0] == MBEDTLS_SSL_HS_CERTIFICATE && + memcmp(ssl->in_msg + mbedtls_ssl_hs_hdr_len(ssl), "\0\0\0", 3) == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("peer has no certificate")); + return 0; + } + return -1; +} +#endif /* MBEDTLS_SSL_SRV_C */ + +/* Check if a certificate message is expected. + * Return either + * - SSL_CERTIFICATE_EXPECTED, or + * - SSL_CERTIFICATE_SKIP + * indicating whether a Certificate message is expected or not. + */ +#define SSL_CERTIFICATE_EXPECTED 0 +#define SSL_CERTIFICATE_SKIP 1 +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_certificate_coordinate(mbedtls_ssl_context *ssl, + int authmode) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + if (!mbedtls_ssl_ciphersuite_uses_srv_cert(ciphersuite_info)) { + return SSL_CERTIFICATE_SKIP; + } + +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK) { + return SSL_CERTIFICATE_SKIP; + } + + if (authmode == MBEDTLS_SSL_VERIFY_NONE) { + ssl->session_negotiate->verify_result = + MBEDTLS_X509_BADCERT_SKIP_VERIFY; + return SSL_CERTIFICATE_SKIP; + } + } +#else + ((void) authmode); +#endif /* MBEDTLS_SSL_SRV_C */ + + return SSL_CERTIFICATE_EXPECTED; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_certificate_verify(mbedtls_ssl_context *ssl, + int authmode, + mbedtls_x509_crt *chain, + void *rs_ctx) +{ + int ret = 0; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + int have_ca_chain = 0; + + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *); + void *p_vrfy; + + if (authmode == MBEDTLS_SSL_VERIFY_NONE) { + return 0; + } + + if (ssl->f_vrfy != NULL) { + MBEDTLS_SSL_DEBUG_MSG(3, ("Use context-specific verification callback")); + f_vrfy = ssl->f_vrfy; + p_vrfy = ssl->p_vrfy; + } else { + MBEDTLS_SSL_DEBUG_MSG(3, ("Use configuration-specific verification callback")); + f_vrfy = ssl->conf->f_vrfy; + p_vrfy = ssl->conf->p_vrfy; + } + + /* + * Main check: verify certificate + */ +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + if (ssl->conf->f_ca_cb != NULL) { + ((void) rs_ctx); + have_ca_chain = 1; + + MBEDTLS_SSL_DEBUG_MSG(3, ("use CA callback for X.509 CRT verification")); + ret = mbedtls_x509_crt_verify_with_ca_cb( + chain, + ssl->conf->f_ca_cb, + ssl->conf->p_ca_cb, + ssl->conf->cert_profile, + ssl->hostname, + &ssl->session_negotiate->verify_result, + f_vrfy, p_vrfy); + } else +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ + { + mbedtls_x509_crt *ca_chain; + mbedtls_x509_crl *ca_crl; + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_ca_chain != NULL) { + ca_chain = ssl->handshake->sni_ca_chain; + ca_crl = ssl->handshake->sni_ca_crl; + } else +#endif + { + ca_chain = ssl->conf->ca_chain; + ca_crl = ssl->conf->ca_crl; + } + + if (ca_chain != NULL) { + have_ca_chain = 1; + } + + ret = mbedtls_x509_crt_verify_restartable( + chain, + ca_chain, ca_crl, + ssl->conf->cert_profile, + ssl->hostname, + &ssl->session_negotiate->verify_result, + f_vrfy, p_vrfy, rs_ctx); + } + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "x509_verify_cert", ret); + } + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + return MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; + } +#endif + + /* + * Secondary checks: always done, but change 'ret' only if it was 0 + */ + +#if defined(MBEDTLS_ECP_C) + { + const mbedtls_pk_context *pk = &chain->pk; + + /* If certificate uses an EC key, make sure the curve is OK. + * This is a public key, so it can't be opaque, so can_do() is a good + * enough check to ensure pk_ec() is safe to use here. */ + if (mbedtls_pk_can_do(pk, MBEDTLS_PK_ECKEY)) { + /* and in the unlikely case the above assumption no longer holds + * we are making sure that pk_ec() here does not return a NULL + */ + const mbedtls_ecp_keypair *ec = mbedtls_pk_ec(*pk); + if (ec == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("mbedtls_pk_ec() returned NULL")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (mbedtls_ssl_check_curve(ssl, ec->grp.id) != 0) { + ssl->session_negotiate->verify_result |= + MBEDTLS_X509_BADCERT_BAD_KEY; + + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate (EC key curve)")); + if (ret == 0) { + ret = MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } + } + } + } +#endif /* MBEDTLS_ECP_C */ + + if (mbedtls_ssl_check_cert_usage(chain, + ciphersuite_info, + !ssl->conf->endpoint, + &ssl->session_negotiate->verify_result) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate (usage extensions)")); + if (ret == 0) { + ret = MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } + } + + /* mbedtls_x509_crt_verify_with_profile is supposed to report a + * verification failure through MBEDTLS_ERR_X509_CERT_VERIFY_FAILED, + * with details encoded in the verification flags. All other kinds + * of error codes, including those from the user provided f_vrfy + * functions, are treated as fatal and lead to a failure of + * ssl_parse_certificate even if verification was optional. */ + if (authmode == MBEDTLS_SSL_VERIFY_OPTIONAL && + (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED || + ret == MBEDTLS_ERR_SSL_BAD_CERTIFICATE)) { + ret = 0; + } + + if (have_ca_chain == 0 && authmode == MBEDTLS_SSL_VERIFY_REQUIRED) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no CA chain")); + ret = MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED; + } + + if (ret != 0) { + uint8_t alert; + + /* The certificate may have been rejected for several reasons. + Pick one and send the corresponding alert. Which alert to send + may be a subject of debate in some cases. */ + if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_OTHER) { + alert = MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_CN_MISMATCH) { + alert = MBEDTLS_SSL_ALERT_MSG_BAD_CERT; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_KEY_USAGE) { + alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_EXT_KEY_USAGE) { + alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_NS_CERT_TYPE) { + alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_BAD_PK) { + alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_BAD_KEY) { + alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_EXPIRED) { + alert = MBEDTLS_SSL_ALERT_MSG_CERT_EXPIRED; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_REVOKED) { + alert = MBEDTLS_SSL_ALERT_MSG_CERT_REVOKED; + } else if (ssl->session_negotiate->verify_result & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { + alert = MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA; + } else { + alert = MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN; + } + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + alert); + } + +#if defined(MBEDTLS_DEBUG_C) + if (ssl->session_negotiate->verify_result != 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("! Certificate verification flags %08x", + (unsigned int) ssl->session_negotiate->verify_result)); + } else { + MBEDTLS_SSL_DEBUG_MSG(3, ("Certificate verification flags clear")); + } +#endif /* MBEDTLS_DEBUG_C */ + + return ret; +} + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_remember_peer_crt_digest(mbedtls_ssl_context *ssl, + unsigned char *start, size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + /* Remember digest of the peer's end-CRT. */ + ssl->session_negotiate->peer_cert_digest = + mbedtls_calloc(1, MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN); + if (ssl->session_negotiate->peer_cert_digest == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc(%d bytes) failed", + MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN)); + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + ret = mbedtls_md(mbedtls_md_info_from_type( + MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE), + start, len, + ssl->session_negotiate->peer_cert_digest); + + ssl->session_negotiate->peer_cert_digest_type = + MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_TYPE; + ssl->session_negotiate->peer_cert_digest_len = + MBEDTLS_SSL_PEER_CERT_DIGEST_DFL_LEN; + + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_remember_peer_pubkey(mbedtls_ssl_context *ssl, + unsigned char *start, size_t len) +{ + unsigned char *end = start + len; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* Make a copy of the peer's raw public key. */ + mbedtls_pk_init(&ssl->handshake->peer_pubkey); + ret = mbedtls_pk_parse_subpubkey(&start, end, + &ssl->handshake->peer_pubkey); + if (ret != 0) { + /* We should have parsed the public key before. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + return 0; +} +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + +int mbedtls_ssl_parse_certificate(mbedtls_ssl_context *ssl) +{ + int ret = 0; + int crt_expected; +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + const int authmode = ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET + ? ssl->handshake->sni_authmode + : ssl->conf->authmode; +#else + const int authmode = ssl->conf->authmode; +#endif + void *rs_ctx = NULL; + mbedtls_x509_crt *chain = NULL; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate")); + + crt_expected = ssl_parse_certificate_coordinate(ssl, authmode); + if (crt_expected == SSL_CERTIFICATE_SKIP) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate")); + goto exit; + } + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled && + ssl->handshake->ecrs_state == ssl_ecrs_crt_verify) { + chain = ssl->handshake->ecrs_peer_cert; + ssl->handshake->ecrs_peer_cert = NULL; + goto crt_verify; + } +#endif + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + /* mbedtls_ssl_read_record may have sent an alert already. We + let it decide whether to alert. */ + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + goto exit; + } + +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl_srv_check_client_no_crt_notification(ssl) == 0) { + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING; + + if (authmode != MBEDTLS_SSL_VERIFY_OPTIONAL) { + ret = MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE; + } + + goto exit; + } +#endif /* MBEDTLS_SSL_SRV_C */ + + /* Clear existing peer CRT structure in case we tried to + * reuse a session but it failed, and allocate a new one. */ + ssl_clear_peer_cert(ssl->session_negotiate); + + chain = mbedtls_calloc(1, sizeof(mbedtls_x509_crt)); + if (chain == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc(%" MBEDTLS_PRINTF_SIZET " bytes) failed", + sizeof(mbedtls_x509_crt))); + mbedtls_ssl_send_alert_message(ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto exit; + } + mbedtls_x509_crt_init(chain); + + ret = ssl_parse_certificate_chain(ssl, chain); + if (ret != 0) { + goto exit; + } + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled) { + ssl->handshake->ecrs_state = ssl_ecrs_crt_verify; + } + +crt_verify: + if (ssl->handshake->ecrs_enabled) { + rs_ctx = &ssl->handshake->ecrs_ctx; + } +#endif + + ret = ssl_parse_certificate_verify(ssl, authmode, + chain, rs_ctx); + if (ret != 0) { + goto exit; + } + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + { + unsigned char *crt_start, *pk_start; + size_t crt_len, pk_len; + + /* We parse the CRT chain without copying, so + * these pointers point into the input buffer, + * and are hence still valid after freeing the + * CRT chain. */ + + crt_start = chain->raw.p; + crt_len = chain->raw.len; + + pk_start = chain->pk_raw.p; + pk_len = chain->pk_raw.len; + + /* Free the CRT structures before computing + * digest and copying the peer's public key. */ + mbedtls_x509_crt_free(chain); + mbedtls_free(chain); + chain = NULL; + + ret = ssl_remember_peer_crt_digest(ssl, crt_start, crt_len); + if (ret != 0) { + goto exit; + } + + ret = ssl_remember_peer_pubkey(ssl, pk_start, pk_len); + if (ret != 0) { + goto exit; + } + } +#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + /* Pass ownership to session structure. */ + ssl->session_negotiate->peer_cert = chain; + chain = NULL; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse certificate")); + +exit: + + if (ret == 0) { + ssl->state++; + } + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ret == MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS) { + ssl->handshake->ecrs_peer_cert = chain; + chain = NULL; + } +#endif + + if (chain != NULL) { + mbedtls_x509_crt_free(chain); + mbedtls_free(chain); + } + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +static int ssl_calc_finished_tls_sha256( + mbedtls_ssl_context *ssl, unsigned char *buf, int from) +{ + int len = 12; + const char *sender; + unsigned char padbuf[32]; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t hash_size; + psa_hash_operation_t sha256_psa = PSA_HASH_OPERATION_INIT; + psa_status_t status; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_context_t sha256; +#endif + + mbedtls_ssl_session *session = ssl->session_negotiate; + if (!session) { + session = ssl->session; + } + + sender = (from == MBEDTLS_SSL_IS_CLIENT) + ? "client finished" + : "server finished"; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + sha256_psa = psa_hash_operation_init(); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> calc PSA finished tls sha256")); + + status = psa_hash_clone(&ssl->handshake->fin_sha256_psa, &sha256_psa); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&sha256_psa, padbuf, sizeof(padbuf), &hash_size); + if (status != PSA_SUCCESS) { + goto exit; + } + MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated padbuf", padbuf, 32); +#else + + mbedtls_md_init(&sha256); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> calc finished tls sha256")); + + ret = mbedtls_md_setup(&sha256, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 0); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_clone(&sha256, &ssl->handshake->fin_sha256); + if (ret != 0) { + goto exit; + } + + /* + * TLSv1.2: + * hash = PRF( master, finished_label, + * Hash( handshake ) )[0.11] + */ + + ret = mbedtls_md_finish(&sha256, padbuf); + if (ret != 0) { + goto exit; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + MBEDTLS_SSL_DEBUG_BUF(4, "finished sha256 output", padbuf, 32); + + ssl->handshake->tls_prf(session->master, 48, sender, + padbuf, 32, buf, len); + + MBEDTLS_SSL_DEBUG_BUF(3, "calc finished result", buf, len); + + mbedtls_platform_zeroize(padbuf, sizeof(padbuf)); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc finished")); + +exit: +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_hash_abort(&sha256_psa); + return PSA_TO_MD_ERR(status); +#else + mbedtls_md_free(&sha256); + return ret; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} +#endif /* MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + + +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +static int ssl_calc_finished_tls_sha384( + mbedtls_ssl_context *ssl, unsigned char *buf, int from) +{ + int len = 12; + const char *sender; + unsigned char padbuf[48]; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t hash_size; + psa_hash_operation_t sha384_psa = PSA_HASH_OPERATION_INIT; + psa_status_t status; +#else + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_context_t sha384; +#endif + + mbedtls_ssl_session *session = ssl->session_negotiate; + if (!session) { + session = ssl->session; + } + + sender = (from == MBEDTLS_SSL_IS_CLIENT) + ? "client finished" + : "server finished"; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + sha384_psa = psa_hash_operation_init(); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> calc PSA finished tls sha384")); + + status = psa_hash_clone(&ssl->handshake->fin_sha384_psa, &sha384_psa); + if (status != PSA_SUCCESS) { + goto exit; + } + + status = psa_hash_finish(&sha384_psa, padbuf, sizeof(padbuf), &hash_size); + if (status != PSA_SUCCESS) { + goto exit; + } + MBEDTLS_SSL_DEBUG_BUF(3, "PSA calculated padbuf", padbuf, 48); +#else + mbedtls_md_init(&sha384); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> calc finished tls sha384")); + + ret = mbedtls_md_setup(&sha384, mbedtls_md_info_from_type(MBEDTLS_MD_SHA384), 0); + if (ret != 0) { + goto exit; + } + ret = mbedtls_md_clone(&sha384, &ssl->handshake->fin_sha384); + if (ret != 0) { + goto exit; + } + + /* + * TLSv1.2: + * hash = PRF( master, finished_label, + * Hash( handshake ) )[0.11] + */ + + ret = mbedtls_md_finish(&sha384, padbuf); + if (ret != 0) { + goto exit; + } +#endif + + MBEDTLS_SSL_DEBUG_BUF(4, "finished sha384 output", padbuf, 48); + + ssl->handshake->tls_prf(session->master, 48, sender, + padbuf, 48, buf, len); + + MBEDTLS_SSL_DEBUG_BUF(3, "calc finished result", buf, len); + + mbedtls_platform_zeroize(padbuf, sizeof(padbuf)); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= calc finished")); + +exit: +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_hash_abort(&sha384_psa); + return PSA_TO_MD_ERR(status); +#else + mbedtls_md_free(&sha384); + return ret; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} +#endif /* MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA*/ + +void mbedtls_ssl_handshake_wrapup_free_hs_transform(mbedtls_ssl_context *ssl) +{ + MBEDTLS_SSL_DEBUG_MSG(3, ("=> handshake wrapup: final free")); + + /* + * Free our handshake params + */ + mbedtls_ssl_handshake_free(ssl); + mbedtls_free(ssl->handshake); + ssl->handshake = NULL; + + /* + * Free the previous transform and switch in the current one + */ + if (ssl->transform) { + mbedtls_ssl_transform_free(ssl->transform); + mbedtls_free(ssl->transform); + } + ssl->transform = ssl->transform_negotiate; + ssl->transform_negotiate = NULL; + + MBEDTLS_SSL_DEBUG_MSG(3, ("<= handshake wrapup: final free")); +} + +void mbedtls_ssl_handshake_wrapup(mbedtls_ssl_context *ssl) +{ + int resume = ssl->handshake->resume; + + MBEDTLS_SSL_DEBUG_MSG(3, ("=> handshake wrapup")); + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { + ssl->renego_status = MBEDTLS_SSL_RENEGOTIATION_DONE; + ssl->renego_records_seen = 0; + } +#endif + + /* + * Free the previous session and switch in the current one + */ + if (ssl->session) { +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + /* RFC 7366 3.1: keep the EtM state */ + ssl->session_negotiate->encrypt_then_mac = + ssl->session->encrypt_then_mac; +#endif + + mbedtls_ssl_session_free(ssl->session); + mbedtls_free(ssl->session); + } + ssl->session = ssl->session_negotiate; + ssl->session_negotiate = NULL; + + /* + * Add cache entry + */ + if (ssl->conf->f_set_cache != NULL && + ssl->session->id_len != 0 && + resume == 0) { + if (ssl->conf->f_set_cache(ssl->conf->p_cache, + ssl->session->id, + ssl->session->id_len, + ssl->session) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("cache did not store session")); + } + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->handshake->flight != NULL) { + /* Cancel handshake timer */ + mbedtls_ssl_set_timer(ssl, 0); + + /* Keep last flight around in case we need to resend it: + * we need the handshake and transform structures for that */ + MBEDTLS_SSL_DEBUG_MSG(3, ("skip freeing handshake and transform")); + } else +#endif + mbedtls_ssl_handshake_wrapup_free_hs_transform(ssl); + + ssl->state = MBEDTLS_SSL_HANDSHAKE_OVER; + + MBEDTLS_SSL_DEBUG_MSG(3, ("<= handshake wrapup")); +} + +int mbedtls_ssl_write_finished(mbedtls_ssl_context *ssl) +{ + int ret, hash_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write finished")); + + mbedtls_ssl_update_out_pointers(ssl, ssl->transform_negotiate); + + ret = ssl->handshake->calc_finished(ssl, ssl->out_msg + 4, ssl->conf->endpoint); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "calc_finished", ret); + } + + /* + * RFC 5246 7.4.9 (Page 63) says 12 is the default length and ciphersuites + * may define some other value. Currently (early 2016), no defined + * ciphersuite does this (and this is unlikely to change as activity has + * moved to TLS 1.3 now) so we can keep the hardcoded 12 here. + */ + hash_len = 12; + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + ssl->verify_data_len = hash_len; + memcpy(ssl->own_verify_data, ssl->out_msg + 4, hash_len); +#endif + + ssl->out_msglen = 4 + hash_len; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_FINISHED; + + /* + * In case of session resuming, invert the client and server + * ChangeCipherSpec messages order. + */ + if (ssl->handshake->resume != 0) { +#if defined(MBEDTLS_SSL_CLI_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + ssl->state = MBEDTLS_SSL_HANDSHAKE_WRAPUP; + } +#endif +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + ssl->state = MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC; + } +#endif + } else { + ssl->state++; + } + + /* + * Switch to our negotiated transform and session parameters for outbound + * data. + */ + MBEDTLS_SSL_DEBUG_MSG(3, ("switching to new transform spec for outbound data")); + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + unsigned char i; + + /* Remember current epoch settings for resending */ + ssl->handshake->alt_transform_out = ssl->transform_out; + memcpy(ssl->handshake->alt_out_ctr, ssl->cur_out_ctr, + sizeof(ssl->handshake->alt_out_ctr)); + + /* Set sequence_number to zero */ + memset(&ssl->cur_out_ctr[2], 0, sizeof(ssl->cur_out_ctr) - 2); + + + /* Increment epoch */ + for (i = 2; i > 0; i--) { + if (++ssl->cur_out_ctr[i - 1] != 0) { + break; + } + } + + /* The loop goes to its end iff the counter is wrapping */ + if (i == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("DTLS epoch would wrap")); + return MBEDTLS_ERR_SSL_COUNTER_WRAPPING; + } + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + memset(ssl->cur_out_ctr, 0, sizeof(ssl->cur_out_ctr)); + + ssl->transform_out = ssl->transform_negotiate; + ssl->session_out = ssl->session_negotiate; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ssl_send_flight_completed(ssl); + } +#endif + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + (ret = mbedtls_ssl_flight_transmit(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flight_transmit", ret); + return ret; + } +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write finished")); + + return 0; +} + +#define SSL_MAX_HASH_LEN 12 + +int mbedtls_ssl_parse_finished(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned int hash_len = 12; + unsigned char buf[SSL_MAX_HASH_LEN]; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse finished")); + + ret = ssl->handshake->calc_finished(ssl, buf, ssl->conf->endpoint ^ 1); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "calc_finished", ret); + } + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + goto exit; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad finished message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + ret = MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + goto exit; + } + + if (ssl->in_msg[0] != MBEDTLS_SSL_HS_FINISHED) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + ret = MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + goto exit; + } + + if (ssl->in_hslen != mbedtls_ssl_hs_hdr_len(ssl) + hash_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad finished message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + ret = MBEDTLS_ERR_SSL_DECODE_ERROR; + goto exit; + } + + if (mbedtls_ct_memcmp(ssl->in_msg + mbedtls_ssl_hs_hdr_len(ssl), + buf, hash_len) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad finished message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR); + ret = MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + goto exit; + } + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + ssl->verify_data_len = hash_len; + memcpy(ssl->peer_verify_data, buf, hash_len); +#endif + + if (ssl->handshake->resume != 0) { +#if defined(MBEDTLS_SSL_CLI_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + ssl->state = MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC; + } +#endif +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + ssl->state = MBEDTLS_SSL_HANDSHAKE_WRAPUP; + } +#endif + } else { + ssl->state++; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ssl_recv_flight_completed(ssl); + } +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse finished")); + +exit: + mbedtls_platform_zeroize(buf, hash_len); + return ret; +} + +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) +/* + * Helper to get TLS 1.2 PRF from ciphersuite + * (Duplicates bits of logic from ssl_set_handshake_prfs().) + */ +static tls_prf_fn ssl_tls12prf_from_cs(int ciphersuite_id) +{ + const mbedtls_ssl_ciphersuite_t * const ciphersuite_info = + mbedtls_ssl_ciphersuite_from_id(ciphersuite_id); +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + if (ciphersuite_info != NULL && ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + return tls_prf_sha384; + } else +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + { + if (ciphersuite_info != NULL && ciphersuite_info->mac == MBEDTLS_MD_SHA256) { + return tls_prf_sha256; + } + } +#endif +#if !defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) && \ + !defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + (void) ciphersuite_info; +#endif + + return NULL; +} +#endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ + +static mbedtls_tls_prf_types tls_prf_get_type(mbedtls_ssl_tls_prf_cb *tls_prf) +{ + ((void) tls_prf); +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + if (tls_prf == tls_prf_sha384) { + return MBEDTLS_SSL_TLS_PRF_SHA384; + } else +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA_BASED_ON_USE_PSA) + if (tls_prf == tls_prf_sha256) { + return MBEDTLS_SSL_TLS_PRF_SHA256; + } else +#endif + return MBEDTLS_SSL_TLS_PRF_NONE; +} + +/* + * Populate a transform structure with session keys and all the other + * necessary information. + * + * Parameters: + * - [in/out]: transform: structure to populate + * [in] must be just initialised with mbedtls_ssl_transform_init() + * [out] fully populated, ready for use by mbedtls_ssl_{en,de}crypt_buf() + * - [in] ciphersuite + * - [in] master + * - [in] encrypt_then_mac + * - [in] tls_prf: pointer to PRF to use for key derivation + * - [in] randbytes: buffer holding ServerHello.random + ClientHello.random + * - [in] tls_version: TLS version + * - [in] endpoint: client or server + * - [in] ssl: used for: + * - ssl->conf->{f,p}_export_keys + * [in] optionally used for: + * - MBEDTLS_DEBUG_C: ssl->conf->{f,p}_dbg + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls12_populate_transform(mbedtls_ssl_transform *transform, + int ciphersuite, + const unsigned char master[48], +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + int encrypt_then_mac, +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + ssl_tls_prf_t tls_prf, + const unsigned char randbytes[64], + mbedtls_ssl_protocol_version tls_version, + unsigned endpoint, + const mbedtls_ssl_context *ssl) +{ + int ret = 0; + unsigned char keyblk[256]; + unsigned char *key1; + unsigned char *key2; + unsigned char *mac_enc; + unsigned char *mac_dec; + size_t mac_key_len = 0; + size_t iv_copy_len; + size_t keylen; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + mbedtls_ssl_mode_t ssl_mode; +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + const mbedtls_cipher_info_t *cipher_info; + const mbedtls_md_info_t *md_info; +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_key_type_t key_type; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t alg; + psa_algorithm_t mac_alg = 0; + size_t key_bits; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; +#endif + +#if !defined(MBEDTLS_DEBUG_C) && \ + !defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + if (ssl->f_export_keys == NULL) { + ssl = NULL; /* make sure we don't use it except for these cases */ + (void) ssl; + } +#endif + + /* + * Some data just needs copying into the structure + */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + transform->encrypt_then_mac = encrypt_then_mac; +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + transform->tls_version = tls_version; + +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) + memcpy(transform->randbytes, randbytes, sizeof(transform->randbytes)); +#endif + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + /* At the moment, we keep TLS <= 1.2 and TLS 1.3 transform + * generation separate. This should never happen. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + /* + * Get various info structures + */ + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(ciphersuite); + if (ciphersuite_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("ciphersuite info for %d not found", + ciphersuite)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl_mode = mbedtls_ssl_get_mode_from_ciphersuite( +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + encrypt_then_mac, +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + ciphersuite_info); + + if (ssl_mode == MBEDTLS_SSL_MODE_AEAD) { + transform->taglen = + ciphersuite_info->flags & MBEDTLS_CIPHERSUITE_SHORT_TAG ? 8 : 16; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((status = mbedtls_ssl_cipher_to_psa(ciphersuite_info->cipher, + transform->taglen, + &alg, + &key_type, + &key_bits)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_cipher_to_psa", ret); + goto end; + } +#else + cipher_info = mbedtls_cipher_info_from_type(ciphersuite_info->cipher); + if (cipher_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("cipher info for %u not found", + ciphersuite_info->cipher)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mac_alg = mbedtls_hash_info_psa_from_md(ciphersuite_info->mac); + if (mac_alg == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("mbedtls_hash_info_psa_from_md for %u not found", + (unsigned) ciphersuite_info->mac)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } +#else + md_info = mbedtls_md_info_from_type(ciphersuite_info->mac); + if (md_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("mbedtls_md info for %u not found", + (unsigned) ciphersuite_info->mac)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + /* Copy own and peer's CID if the use of the CID + * extension has been negotiated. */ + if (ssl->handshake->cid_in_use == MBEDTLS_SSL_CID_ENABLED) { + MBEDTLS_SSL_DEBUG_MSG(3, ("Copy CIDs into SSL transform")); + + transform->in_cid_len = ssl->own_cid_len; + memcpy(transform->in_cid, ssl->own_cid, ssl->own_cid_len); + MBEDTLS_SSL_DEBUG_BUF(3, "Incoming CID", transform->in_cid, + transform->in_cid_len); + + transform->out_cid_len = ssl->handshake->peer_cid_len; + memcpy(transform->out_cid, ssl->handshake->peer_cid, + ssl->handshake->peer_cid_len); + MBEDTLS_SSL_DEBUG_BUF(3, "Outgoing CID", transform->out_cid, + transform->out_cid_len); + } +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + + /* + * Compute key block using the PRF + */ + ret = tls_prf(master, 48, "key expansion", randbytes, 64, keyblk, 256); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "prf", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite = %s", + mbedtls_ssl_get_ciphersuite_name(ciphersuite))); + MBEDTLS_SSL_DEBUG_BUF(3, "master secret", master, 48); + MBEDTLS_SSL_DEBUG_BUF(4, "random bytes", randbytes, 64); + MBEDTLS_SSL_DEBUG_BUF(4, "key block", keyblk, 256); + + /* + * Determine the appropriate key, IV and MAC length. + */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + keylen = PSA_BITS_TO_BYTES(key_bits); +#else + keylen = mbedtls_cipher_info_get_key_bitlen(cipher_info) / 8; +#endif + +#if defined(MBEDTLS_GCM_C) || \ + defined(MBEDTLS_CCM_C) || \ + defined(MBEDTLS_CHACHAPOLY_C) + if (ssl_mode == MBEDTLS_SSL_MODE_AEAD) { + size_t explicit_ivlen; + + transform->maclen = 0; + mac_key_len = 0; + + /* All modes haves 96-bit IVs, but the length of the static parts vary + * with mode and version: + * - For GCM and CCM in TLS 1.2, there's a static IV of 4 Bytes + * (to be concatenated with a dynamically chosen IV of 8 Bytes) + * - For ChaChaPoly in TLS 1.2, and all modes in TLS 1.3, there's + * a static IV of 12 Bytes (to be XOR'ed with the 8 Byte record + * sequence number). + */ + transform->ivlen = 12; + + int is_chachapoly = 0; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + is_chachapoly = (key_type == PSA_KEY_TYPE_CHACHA20); +#else + is_chachapoly = (mbedtls_cipher_info_get_mode(cipher_info) + == MBEDTLS_MODE_CHACHAPOLY); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (is_chachapoly) { + transform->fixed_ivlen = 12; + } else { + transform->fixed_ivlen = 4; + } + + /* Minimum length of encrypted record */ + explicit_ivlen = transform->ivlen - transform->fixed_ivlen; + transform->minlen = explicit_ivlen + transform->taglen; + } else +#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */ +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + if (ssl_mode == MBEDTLS_SSL_MODE_STREAM || + ssl_mode == MBEDTLS_SSL_MODE_CBC || + ssl_mode == MBEDTLS_SSL_MODE_CBC_ETM) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type); +#else + size_t block_size = cipher_info->block_size; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + /* Get MAC length */ + mac_key_len = PSA_HASH_LENGTH(mac_alg); +#else + /* Initialize HMAC contexts */ + if ((ret = mbedtls_md_setup(&transform->md_ctx_enc, md_info, 1)) != 0 || + (ret = mbedtls_md_setup(&transform->md_ctx_dec, md_info, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_setup", ret); + goto end; + } + + /* Get MAC length */ + mac_key_len = mbedtls_md_get_size(md_info); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + transform->maclen = mac_key_len; + + /* IV length */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + transform->ivlen = PSA_CIPHER_IV_LENGTH(key_type, alg); +#else + transform->ivlen = cipher_info->iv_size; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* Minimum length */ + if (ssl_mode == MBEDTLS_SSL_MODE_STREAM) { + transform->minlen = transform->maclen; + } else { + /* + * GenericBlockCipher: + * 1. if EtM is in use: one block plus MAC + * otherwise: * first multiple of blocklen greater than maclen + * 2. IV + */ +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + if (ssl_mode == MBEDTLS_SSL_MODE_CBC_ETM) { + transform->minlen = transform->maclen + + block_size; + } else +#endif + { + transform->minlen = transform->maclen + + block_size + - transform->maclen % block_size; + } + + if (tls_version == MBEDTLS_SSL_VERSION_TLS1_2) { + transform->minlen += transform->ivlen; + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto end; + } + } + } else +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("keylen: %u, minlen: %u, ivlen: %u, maclen: %u", + (unsigned) keylen, + (unsigned) transform->minlen, + (unsigned) transform->ivlen, + (unsigned) transform->maclen)); + + /* + * Finally setup the cipher contexts, IVs and MAC secrets. + */ +#if defined(MBEDTLS_SSL_CLI_C) + if (endpoint == MBEDTLS_SSL_IS_CLIENT) { + key1 = keyblk + mac_key_len * 2; + key2 = keyblk + mac_key_len * 2 + keylen; + + mac_enc = keyblk; + mac_dec = keyblk + mac_key_len; + + iv_copy_len = (transform->fixed_ivlen) ? + transform->fixed_ivlen : transform->ivlen; + memcpy(transform->iv_enc, key2 + keylen, iv_copy_len); + memcpy(transform->iv_dec, key2 + keylen + iv_copy_len, + iv_copy_len); + } else +#endif /* MBEDTLS_SSL_CLI_C */ +#if defined(MBEDTLS_SSL_SRV_C) + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + key1 = keyblk + mac_key_len * 2 + keylen; + key2 = keyblk + mac_key_len * 2; + + mac_enc = keyblk + mac_key_len; + mac_dec = keyblk; + + iv_copy_len = (transform->fixed_ivlen) ? + transform->fixed_ivlen : transform->ivlen; + memcpy(transform->iv_dec, key1 + keylen, iv_copy_len); + memcpy(transform->iv_enc, key1 + keylen + iv_copy_len, + iv_copy_len); + } else +#endif /* MBEDTLS_SSL_SRV_C */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto end; + } + + if (ssl != NULL && ssl->f_export_keys != NULL) { + ssl->f_export_keys(ssl->p_export_keys, + MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET, + master, 48, + randbytes + 32, + randbytes, + tls_prf_get_type(tls_prf)); + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + transform->psa_alg = alg; + + if (alg != MBEDTLS_SSL_NULL_CIPHER) { + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attributes, alg); + psa_set_key_type(&attributes, key_type); + + if ((status = psa_import_key(&attributes, + key1, + PSA_BITS_TO_BYTES(key_bits), + &transform->psa_key_enc)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(3, "psa_import_key", (int) status); + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_import_key", ret); + goto end; + } + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); + + if ((status = psa_import_key(&attributes, + key2, + PSA_BITS_TO_BYTES(key_bits), + &transform->psa_key_dec)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_import_key", ret); + goto end; + } + } +#else + if ((ret = mbedtls_cipher_setup(&transform->cipher_ctx_enc, + cipher_info)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setup", ret); + goto end; + } + + if ((ret = mbedtls_cipher_setup(&transform->cipher_ctx_dec, + cipher_info)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setup", ret); + goto end; + } + + if ((ret = mbedtls_cipher_setkey(&transform->cipher_ctx_enc, key1, + (int) mbedtls_cipher_info_get_key_bitlen(cipher_info), + MBEDTLS_ENCRYPT)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setkey", ret); + goto end; + } + + if ((ret = mbedtls_cipher_setkey(&transform->cipher_ctx_dec, key2, + (int) mbedtls_cipher_info_get_key_bitlen(cipher_info), + MBEDTLS_DECRYPT)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setkey", ret); + goto end; + } + +#if defined(MBEDTLS_CIPHER_MODE_CBC) + if (mbedtls_cipher_info_get_mode(cipher_info) == MBEDTLS_MODE_CBC) { + if ((ret = mbedtls_cipher_set_padding_mode(&transform->cipher_ctx_enc, + MBEDTLS_PADDING_NONE)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_set_padding_mode", ret); + goto end; + } + + if ((ret = mbedtls_cipher_set_padding_mode(&transform->cipher_ctx_dec, + MBEDTLS_PADDING_NONE)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_set_padding_mode", ret); + goto end; + } + } +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) + /* For HMAC-based ciphersuites, initialize the HMAC transforms. + For AEAD-based ciphersuites, there is nothing to do here. */ + if (mac_key_len != 0) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + transform->psa_mac_alg = PSA_ALG_HMAC(mac_alg); + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attributes, PSA_ALG_HMAC(mac_alg)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC); + + if ((status = psa_import_key(&attributes, + mac_enc, mac_key_len, + &transform->psa_mac_enc)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_import_mac_key", ret); + goto end; + } + + if ((transform->psa_alg == MBEDTLS_SSL_NULL_CIPHER) || + ((transform->psa_alg == PSA_ALG_CBC_NO_PADDING) +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + && (transform->encrypt_then_mac == MBEDTLS_SSL_ETM_DISABLED) +#endif + )) { + /* mbedtls_ct_hmac() requires the key to be exportable */ + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT | + PSA_KEY_USAGE_VERIFY_HASH); + } else { + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH); + } + + if ((status = psa_import_key(&attributes, + mac_dec, mac_key_len, + &transform->psa_mac_dec)) != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_import_mac_key", ret); + goto end; + } +#else + ret = mbedtls_md_hmac_starts(&transform->md_ctx_enc, mac_enc, mac_key_len); + if (ret != 0) { + goto end; + } + ret = mbedtls_md_hmac_starts(&transform->md_ctx_dec, mac_dec, mac_key_len); + if (ret != 0) { + goto end; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ + + ((void) mac_dec); + ((void) mac_enc); + +end: + mbedtls_platform_zeroize(keyblk, sizeof(keyblk)); + return ret; +} + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) && \ + defined(MBEDTLS_USE_PSA_CRYPTO) +int mbedtls_psa_ecjpake_read_round( + psa_pake_operation_t *pake_ctx, + const unsigned char *buf, + size_t len, mbedtls_ecjpake_rounds_t round) +{ + psa_status_t status; + size_t input_offset = 0; + /* + * At round one repeat the KEY_SHARE, ZK_PUBLIC & ZF_PROOF twice + * At round two perform a single cycle + */ + unsigned int remaining_steps = (round == MBEDTLS_ECJPAKE_ROUND_ONE) ? 2 : 1; + + for (; remaining_steps > 0; remaining_steps--) { + for (psa_pake_step_t step = PSA_PAKE_STEP_KEY_SHARE; + step <= PSA_PAKE_STEP_ZK_PROOF; + ++step) { + /* Length is stored at the first byte */ + size_t length = buf[input_offset]; + input_offset += 1; + + if (input_offset + length > len) { + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + status = psa_pake_input(pake_ctx, step, + buf + input_offset, length); + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } + + input_offset += length; + } + } + + if (input_offset != len) { + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + return 0; +} + +int mbedtls_psa_ecjpake_write_round( + psa_pake_operation_t *pake_ctx, + unsigned char *buf, + size_t len, size_t *olen, + mbedtls_ecjpake_rounds_t round) +{ + psa_status_t status; + size_t output_offset = 0; + size_t output_len; + /* + * At round one repeat the KEY_SHARE, ZK_PUBLIC & ZF_PROOF twice + * At round two perform a single cycle + */ + unsigned int remaining_steps = (round == MBEDTLS_ECJPAKE_ROUND_ONE) ? 2 : 1; + + for (; remaining_steps > 0; remaining_steps--) { + for (psa_pake_step_t step = PSA_PAKE_STEP_KEY_SHARE; + step <= PSA_PAKE_STEP_ZK_PROOF; + ++step) { + /* + * For each step, prepend 1 byte with the length of the data as + * given by psa_pake_output(). + */ + status = psa_pake_output(pake_ctx, step, + buf + output_offset + 1, + len - output_offset - 1, + &output_len); + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } + + *(buf + output_offset) = (uint8_t) output_len; + + output_offset += output_len + 1; + } + } + + *olen = output_offset; + + return 0; +} +#endif //MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED && MBEDTLS_USE_PSA_CRYPTO + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +int mbedtls_ssl_get_key_exchange_md_tls1_2(mbedtls_ssl_context *ssl, + unsigned char *hash, size_t *hashlen, + unsigned char *data, size_t data_len, + mbedtls_md_type_t md_alg) +{ + psa_status_t status; + psa_hash_operation_t hash_operation = PSA_HASH_OPERATION_INIT; + psa_algorithm_t hash_alg = mbedtls_hash_info_psa_from_md(md_alg); + + MBEDTLS_SSL_DEBUG_MSG(3, ("Perform PSA-based computation of digest of ServerKeyExchange")); + + if ((status = psa_hash_setup(&hash_operation, + hash_alg)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "psa_hash_setup", status); + goto exit; + } + + if ((status = psa_hash_update(&hash_operation, ssl->handshake->randbytes, + 64)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "psa_hash_update", status); + goto exit; + } + + if ((status = psa_hash_update(&hash_operation, + data, data_len)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "psa_hash_update", status); + goto exit; + } + + if ((status = psa_hash_finish(&hash_operation, hash, PSA_HASH_MAX_SIZE, + hashlen)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "psa_hash_finish", status); + goto exit; + } + +exit: + if (status != PSA_SUCCESS) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + switch (status) { + case PSA_ERROR_NOT_SUPPORTED: + return MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE; + case PSA_ERROR_BAD_STATE: /* Intentional fallthrough */ + case PSA_ERROR_BUFFER_TOO_SMALL: + return MBEDTLS_ERR_MD_BAD_INPUT_DATA; + case PSA_ERROR_INSUFFICIENT_MEMORY: + return MBEDTLS_ERR_MD_ALLOC_FAILED; + default: + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + } + return 0; +} + +#else + +int mbedtls_ssl_get_key_exchange_md_tls1_2(mbedtls_ssl_context *ssl, + unsigned char *hash, size_t *hashlen, + unsigned char *data, size_t data_len, + mbedtls_md_type_t md_alg) +{ + int ret = 0; + mbedtls_md_context_t ctx; + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_alg); + *hashlen = mbedtls_md_get_size(md_info); + + MBEDTLS_SSL_DEBUG_MSG(3, ("Perform mbedtls-based computation of digest of ServerKeyExchange")); + + mbedtls_md_init(&ctx); + + /* + * digitally-signed struct { + * opaque client_random[32]; + * opaque server_random[32]; + * ServerDHParams params; + * }; + */ + if ((ret = mbedtls_md_setup(&ctx, md_info, 0)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_setup", ret); + goto exit; + } + if ((ret = mbedtls_md_starts(&ctx)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_starts", ret); + goto exit; + } + if ((ret = mbedtls_md_update(&ctx, ssl->handshake->randbytes, 64)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_update", ret); + goto exit; + } + if ((ret = mbedtls_md_update(&ctx, data, data_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_update", ret); + goto exit; + } + if ((ret = mbedtls_md_finish(&ctx, hash)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_md_finish", ret); + goto exit; + } + +exit: + mbedtls_md_free(&ctx); + + if (ret != 0) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + } + + return ret; +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + +/* Find the preferred hash for a given signature algorithm. */ +unsigned int mbedtls_ssl_tls12_get_preferred_hash_for_sig_alg( + mbedtls_ssl_context *ssl, + unsigned int sig_alg) +{ + unsigned int i; + uint16_t *received_sig_algs = ssl->handshake->received_sig_algs; + + if (sig_alg == MBEDTLS_SSL_SIG_ANON) { + return MBEDTLS_SSL_HASH_NONE; + } + + for (i = 0; received_sig_algs[i] != MBEDTLS_TLS_SIG_NONE; i++) { + unsigned int hash_alg_received = + MBEDTLS_SSL_TLS12_HASH_ALG_FROM_SIG_AND_HASH_ALG( + received_sig_algs[i]); + unsigned int sig_alg_received = + MBEDTLS_SSL_TLS12_SIG_ALG_FROM_SIG_AND_HASH_ALG( + received_sig_algs[i]); + + if (sig_alg == sig_alg_received) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ssl->handshake->key_cert && ssl->handshake->key_cert->key) { + psa_algorithm_t psa_hash_alg = + mbedtls_hash_info_psa_from_md(hash_alg_received); + + if (sig_alg_received == MBEDTLS_SSL_SIG_ECDSA && + !mbedtls_pk_can_do_ext(ssl->handshake->key_cert->key, + PSA_ALG_ECDSA(psa_hash_alg), + PSA_KEY_USAGE_SIGN_HASH)) { + continue; + } + + if (sig_alg_received == MBEDTLS_SSL_SIG_RSA && + !mbedtls_pk_can_do_ext(ssl->handshake->key_cert->key, + PSA_ALG_RSA_PKCS1V15_SIGN( + psa_hash_alg), + PSA_KEY_USAGE_SIGN_HASH)) { + continue; + } + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + return hash_alg_received; + } + } + + return MBEDTLS_SSL_HASH_NONE; +} + +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ + +/* Serialization of TLS 1.2 sessions: + * + * struct { + * uint64 start_time; + * uint8 ciphersuite[2]; // defined by the standard + * uint8 session_id_len; // at most 32 + * opaque session_id[32]; + * opaque master[48]; // fixed length in the standard + * uint32 verify_result; + * opaque peer_cert<0..2^24-1>; // length 0 means no peer cert + * opaque ticket<0..2^24-1>; // length 0 means no ticket + * uint32 ticket_lifetime; + * uint8 mfl_code; // up to 255 according to standard + * uint8 encrypt_then_mac; // 0 or 1 + * } serialized_session_tls12; + * + */ +static size_t ssl_tls12_session_save(const mbedtls_ssl_session *session, + unsigned char *buf, + size_t buf_len) +{ + unsigned char *p = buf; + size_t used = 0; + +#if defined(MBEDTLS_HAVE_TIME) + uint64_t start; +#endif +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + size_t cert_len; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + /* + * Time + */ +#if defined(MBEDTLS_HAVE_TIME) + used += 8; + + if (used <= buf_len) { + start = (uint64_t) session->start; + + MBEDTLS_PUT_UINT64_BE(start, p, 0); + p += 8; + } +#endif /* MBEDTLS_HAVE_TIME */ + + /* + * Basic mandatory fields + */ + used += 2 /* ciphersuite */ + + 1 /* id_len */ + + sizeof(session->id) + + sizeof(session->master) + + 4; /* verify_result */ + + if (used <= buf_len) { + MBEDTLS_PUT_UINT16_BE(session->ciphersuite, p, 0); + p += 2; + + *p++ = MBEDTLS_BYTE_0(session->id_len); + memcpy(p, session->id, 32); + p += 32; + + memcpy(p, session->master, 48); + p += 48; + + MBEDTLS_PUT_UINT32_BE(session->verify_result, p, 0); + p += 4; + } + + /* + * Peer's end-entity certificate + */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + if (session->peer_cert == NULL) { + cert_len = 0; + } else { + cert_len = session->peer_cert->raw.len; + } + + used += 3 + cert_len; + + if (used <= buf_len) { + *p++ = MBEDTLS_BYTE_2(cert_len); + *p++ = MBEDTLS_BYTE_1(cert_len); + *p++ = MBEDTLS_BYTE_0(cert_len); + + if (session->peer_cert != NULL) { + memcpy(p, session->peer_cert->raw.p, cert_len); + p += cert_len; + } + } +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (session->peer_cert_digest != NULL) { + used += 1 /* type */ + 1 /* length */ + session->peer_cert_digest_len; + if (used <= buf_len) { + *p++ = (unsigned char) session->peer_cert_digest_type; + *p++ = (unsigned char) session->peer_cert_digest_len; + memcpy(p, session->peer_cert_digest, + session->peer_cert_digest_len); + p += session->peer_cert_digest_len; + } + } else { + used += 2; + if (used <= buf_len) { + *p++ = (unsigned char) MBEDTLS_MD_NONE; + *p++ = 0; + } + } +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + /* + * Session ticket if any, plus associated data + */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + used += 3 + session->ticket_len + 4; /* len + ticket + lifetime */ + + if (used <= buf_len) { + *p++ = MBEDTLS_BYTE_2(session->ticket_len); + *p++ = MBEDTLS_BYTE_1(session->ticket_len); + *p++ = MBEDTLS_BYTE_0(session->ticket_len); + + if (session->ticket != NULL) { + memcpy(p, session->ticket, session->ticket_len); + p += session->ticket_len; + } + + MBEDTLS_PUT_UINT32_BE(session->ticket_lifetime, p, 0); + p += 4; + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + + /* + * Misc extension-related info + */ +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + used += 1; + + if (used <= buf_len) { + *p++ = session->mfl_code; + } +#endif + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + used += 1; + + if (used <= buf_len) { + *p++ = MBEDTLS_BYTE_0(session->encrypt_then_mac); + } +#endif + + return used; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls12_session_load(mbedtls_ssl_session *session, + const unsigned char *buf, + size_t len) +{ +#if defined(MBEDTLS_HAVE_TIME) + uint64_t start; +#endif +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + size_t cert_len; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + const unsigned char *p = buf; + const unsigned char * const end = buf + len; + + /* + * Time + */ +#if defined(MBEDTLS_HAVE_TIME) + if (8 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + start = ((uint64_t) p[0] << 56) | + ((uint64_t) p[1] << 48) | + ((uint64_t) p[2] << 40) | + ((uint64_t) p[3] << 32) | + ((uint64_t) p[4] << 24) | + ((uint64_t) p[5] << 16) | + ((uint64_t) p[6] << 8) | + ((uint64_t) p[7]); + p += 8; + + session->start = (time_t) start; +#endif /* MBEDTLS_HAVE_TIME */ + + /* + * Basic mandatory fields + */ + if (2 + 1 + 32 + 48 + 4 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->ciphersuite = (p[0] << 8) | p[1]; + p += 2; + + session->id_len = *p++; + memcpy(session->id, p, 32); + p += 32; + + memcpy(session->master, p, 48); + p += 48; + + session->verify_result = ((uint32_t) p[0] << 24) | + ((uint32_t) p[1] << 16) | + ((uint32_t) p[2] << 8) | + ((uint32_t) p[3]); + p += 4; + + /* Immediately clear invalid pointer values that have been read, in case + * we exit early before we replaced them with valid ones. */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + session->peer_cert = NULL; +#else + session->peer_cert_digest = NULL; +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + session->ticket = NULL; +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + + /* + * Peer certificate + */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /* Deserialize CRT from the end of the ticket. */ + if (3 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + cert_len = (p[0] << 16) | (p[1] << 8) | p[2]; + p += 3; + + if (cert_len != 0) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (cert_len > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->peer_cert = mbedtls_calloc(1, sizeof(mbedtls_x509_crt)); + + if (session->peer_cert == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + mbedtls_x509_crt_init(session->peer_cert); + + if ((ret = mbedtls_x509_crt_parse_der(session->peer_cert, + p, cert_len)) != 0) { + mbedtls_x509_crt_free(session->peer_cert); + mbedtls_free(session->peer_cert); + session->peer_cert = NULL; + return ret; + } + + p += cert_len; + } +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + /* Deserialize CRT digest from the end of the ticket. */ + if (2 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->peer_cert_digest_type = (mbedtls_md_type_t) *p++; + session->peer_cert_digest_len = (size_t) *p++; + + if (session->peer_cert_digest_len != 0) { + const mbedtls_md_info_t *md_info = + mbedtls_md_info_from_type(session->peer_cert_digest_type); + if (md_info == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (session->peer_cert_digest_len != mbedtls_md_get_size(md_info)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + if (session->peer_cert_digest_len > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->peer_cert_digest = + mbedtls_calloc(1, session->peer_cert_digest_len); + if (session->peer_cert_digest == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(session->peer_cert_digest, p, + session->peer_cert_digest_len); + p += session->peer_cert_digest_len; + } +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + /* + * Session ticket and associated data + */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && defined(MBEDTLS_SSL_CLI_C) + if (3 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->ticket_len = (p[0] << 16) | (p[1] << 8) | p[2]; + p += 3; + + if (session->ticket_len != 0) { + if (session->ticket_len > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->ticket = mbedtls_calloc(1, session->ticket_len); + if (session->ticket == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(session->ticket, p, session->ticket_len); + p += session->ticket_len; + } + + if (4 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->ticket_lifetime = ((uint32_t) p[0] << 24) | + ((uint32_t) p[1] << 16) | + ((uint32_t) p[2] << 8) | + ((uint32_t) p[3]); + p += 4; +#endif /* MBEDTLS_SSL_SESSION_TICKETS && MBEDTLS_SSL_CLI_C */ + + /* + * Misc extension-related info + */ +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + if (1 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->mfl_code = *p++; +#endif + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + if (1 > (size_t) (end - p)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + session->encrypt_then_mac = *p++; +#endif + + /* Done, should have consumed entire buffer */ + if (p != end) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + return 0; +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + +int mbedtls_ssl_validate_ciphersuite( + const mbedtls_ssl_context *ssl, + const mbedtls_ssl_ciphersuite_t *suite_info, + mbedtls_ssl_protocol_version min_tls_version, + mbedtls_ssl_protocol_version max_tls_version) +{ + (void) ssl; + + if (suite_info == NULL) { + return -1; + } + + if ((suite_info->min_tls_version > max_tls_version) || + (suite_info->max_tls_version < min_tls_version)) { + return -1; + } + +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && defined(MBEDTLS_SSL_CLI_C) +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (suite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE && + ssl->handshake->psa_pake_ctx_is_ok != 1) +#else + if (suite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE && + mbedtls_ecjpake_check(&ssl->handshake->ecjpake_ctx) != 0) +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + { + return -1; + } +#endif + + /* Don't suggest PSK-based ciphersuite if no PSK is available. */ +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_psk(suite_info) && + mbedtls_ssl_conf_has_static_psk(ssl->conf) == 0) { + return -1; + } +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ + + return 0; +} + +#if defined(MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED) +/* + * Function for writing a signature algorithm extension. + * + * The `extension_data` field of signature algorithm contains a `SignatureSchemeList` + * value (TLS 1.3 RFC8446): + * enum { + * .... + * ecdsa_secp256r1_sha256( 0x0403 ), + * ecdsa_secp384r1_sha384( 0x0503 ), + * ecdsa_secp521r1_sha512( 0x0603 ), + * .... + * } SignatureScheme; + * + * struct { + * SignatureScheme supported_signature_algorithms<2..2^16-2>; + * } SignatureSchemeList; + * + * The `extension_data` field of signature algorithm contains a `SignatureAndHashAlgorithm` + * value (TLS 1.2 RFC5246): + * enum { + * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + * sha512(6), (255) + * } HashAlgorithm; + * + * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + * SignatureAlgorithm; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2..2^16-2>; + * + * The TLS 1.3 signature algorithm extension was defined to be a compatible + * generalization of the TLS 1.2 signature algorithm extension. + * `SignatureAndHashAlgorithm` field of TLS 1.2 can be represented by + * `SignatureScheme` field of TLS 1.3 + * + */ +int mbedtls_ssl_write_sig_alg_ext(mbedtls_ssl_context *ssl, unsigned char *buf, + const unsigned char *end, size_t *out_len) +{ + unsigned char *p = buf; + unsigned char *supported_sig_alg; /* Start of supported_signature_algorithms */ + size_t supported_sig_alg_len = 0; /* Length of supported_signature_algorithms */ + + *out_len = 0; + + MBEDTLS_SSL_DEBUG_MSG(3, ("adding signature_algorithms extension")); + + /* Check if we have space for header and length field: + * - extension_type (2 bytes) + * - extension_data_length (2 bytes) + * - supported_signature_algorithms_length (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6); + p += 6; + + /* + * Write supported_signature_algorithms + */ + supported_sig_alg = p; + const uint16_t *sig_alg = mbedtls_ssl_get_sig_algs(ssl); + if (sig_alg == NULL) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + + for (; *sig_alg != MBEDTLS_TLS1_3_SIG_NONE; sig_alg++) { + MBEDTLS_SSL_DEBUG_MSG(3, ("got signature scheme [%x] %s", + *sig_alg, + mbedtls_ssl_sig_alg_to_str(*sig_alg))); + if (!mbedtls_ssl_sig_alg_is_supported(ssl, *sig_alg)) { + continue; + } + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + MBEDTLS_PUT_UINT16_BE(*sig_alg, p, 0); + p += 2; + MBEDTLS_SSL_DEBUG_MSG(3, ("sent signature scheme [%x] %s", + *sig_alg, + mbedtls_ssl_sig_alg_to_str(*sig_alg))); + } + + /* Length of supported_signature_algorithms */ + supported_sig_alg_len = p - supported_sig_alg; + if (supported_sig_alg_len == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("No signature algorithms defined.")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SIG_ALG, buf, 0); + MBEDTLS_PUT_UINT16_BE(supported_sig_alg_len + 2, buf, 2); + MBEDTLS_PUT_UINT16_BE(supported_sig_alg_len, buf, 4); + + *out_len = p - buf; + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_SIG_ALG); +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + + return 0; +} +#endif /* MBEDTLS_SSL_HANDSHAKE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) +/* + * mbedtls_ssl_parse_server_name_ext + * + * Structure of server_name extension: + * + * enum { + * host_name(0), (255) + * } NameType; + * opaque HostName<1..2^16-1>; + * + * struct { + * NameType name_type; + * select (name_type) { + * case host_name: HostName; + * } name; + * } ServerName; + * struct { + * ServerName server_name_list<1..2^16-1> + * } ServerNameList; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_server_name_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + size_t server_name_list_len, hostname_len; + const unsigned char *server_name_list_end; + + MBEDTLS_SSL_DEBUG_MSG(3, ("parse ServerName extension")); + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + server_name_list_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, server_name_list_len); + server_name_list_end = p + server_name_list_len; + while (p < server_name_list_end) { + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, server_name_list_end, 3); + hostname_len = MBEDTLS_GET_UINT16_BE(p, 1); + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, server_name_list_end, + hostname_len + 3); + + if (p[0] == MBEDTLS_TLS_EXT_SERVERNAME_HOSTNAME) { + /* sni_name is intended to be used only during the parsing of the + * ClientHello message (it is reset to NULL before the end of + * the message parsing). Thus it is ok to just point to the + * reception buffer and not make a copy of it. + */ + ssl->handshake->sni_name = p + 3; + ssl->handshake->sni_name_len = hostname_len; + if (ssl->conf->f_sni == NULL) { + return 0; + } + ret = ssl->conf->f_sni(ssl->conf->p_sni, + ssl, p + 3, hostname_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_sni_wrapper", ret); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_UNRECOGNIZED_NAME, + MBEDTLS_ERR_SSL_UNRECOGNIZED_NAME); + return MBEDTLS_ERR_SSL_UNRECOGNIZED_NAME; + } + return 0; + } + + p += hostname_len + 3; + } + + return 0; +} +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_SSL_ALPN) +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_parse_alpn_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + const unsigned char *p = buf; + size_t protocol_name_list_len; + const unsigned char *protocol_name_list; + const unsigned char *protocol_name_list_end; + size_t protocol_name_len; + + /* If ALPN not configured, just ignore the extension */ + if (ssl->conf->alpn_list == NULL) { + return 0; + } + + /* + * RFC7301, section 3.1 + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + */ + + /* + * protocol_name_list_len 2 bytes + * protocol_name_len 1 bytes + * protocol_name >=1 byte + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 4); + + protocol_name_list_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, protocol_name_list_len); + protocol_name_list = p; + protocol_name_list_end = p + protocol_name_list_len; + + /* Validate peer's list (lengths) */ + while (p < protocol_name_list_end) { + protocol_name_len = *p++; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, protocol_name_list_end, + protocol_name_len); + if (protocol_name_len == 0) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + p += protocol_name_len; + } + + /* Use our order of preference */ + for (const char **alpn = ssl->conf->alpn_list; *alpn != NULL; alpn++) { + size_t const alpn_len = strlen(*alpn); + p = protocol_name_list; + while (p < protocol_name_list_end) { + protocol_name_len = *p++; + if (protocol_name_len == alpn_len && + memcmp(p, *alpn, alpn_len) == 0) { + ssl->alpn_chosen = *alpn; + return 0; + } + + p += protocol_name_len; + } + } + + /* If we get here, no match was found */ + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_NO_APPLICATION_PROTOCOL, + MBEDTLS_ERR_SSL_NO_APPLICATION_PROTOCOL); + return MBEDTLS_ERR_SSL_NO_APPLICATION_PROTOCOL; +} + +int mbedtls_ssl_write_alpn_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + size_t protocol_name_len; + *out_len = 0; + + if (ssl->alpn_chosen == NULL) { + return 0; + } + + protocol_name_len = strlen(ssl->alpn_chosen); + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 7 + protocol_name_len); + + MBEDTLS_SSL_DEBUG_MSG(3, ("server side, adding alpn extension")); + /* + * 0 . 1 ext identifier + * 2 . 3 ext length + * 4 . 5 protocol list length + * 6 . 6 protocol name length + * 7 . 7+n protocol name + */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_ALPN, p, 0); + + *out_len = 7 + protocol_name_len; + + MBEDTLS_PUT_UINT16_BE(protocol_name_len + 3, p, 2); + MBEDTLS_PUT_UINT16_BE(protocol_name_len + 1, p, 4); + /* Note: the length of the chosen protocol has been checked to be less + * than 255 bytes in `mbedtls_ssl_conf_alpn_protocols`. + */ + p[6] = MBEDTLS_BYTE_0(protocol_name_len); + + memcpy(p + 7, ssl->alpn_chosen, protocol_name_len); + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_ALPN); +#endif + + return 0; +} +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ + defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) && \ + defined(MBEDTLS_SSL_CLI_C) +int mbedtls_ssl_session_set_hostname(mbedtls_ssl_session *session, + const char *hostname) +{ + /* Initialize to suppress unnecessary compiler warning */ + size_t hostname_len = 0; + + /* Check if new hostname is valid before + * making any change to current one */ + if (hostname != NULL) { + hostname_len = strlen(hostname); + + if (hostname_len > MBEDTLS_SSL_MAX_HOST_NAME_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + } + + /* Now it's clear that we will overwrite the old hostname, + * so we can free it safely */ + if (session->hostname != NULL) { + mbedtls_platform_zeroize(session->hostname, + strlen(session->hostname)); + mbedtls_free(session->hostname); + } + + /* Passing NULL as hostname shall clear the old one */ + if (hostname == NULL) { + session->hostname = NULL; + } else { + session->hostname = mbedtls_calloc(1, hostname_len + 1); + if (session->hostname == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(session->hostname, hostname, hostname_len); + } + + return 0; +} +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 && + MBEDTLS_SSL_SESSION_TICKETS && + MBEDTLS_SSL_SERVER_NAME_INDICATION && + MBEDTLS_SSL_CLI_C */ + +#endif /* MBEDTLS_SSL_TLS_C */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls12_client.c b/r5dev/thirdparty/mbedtls/ssl_tls12_client.c new file mode 100644 index 00000000..890e9a90 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls12_client.c @@ -0,0 +1,3600 @@ +/* + * TLS client-side functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_PROTO_TLS1_2) + +#include "mbedtls/platform.h" + +#include "mbedtls/ssl.h" +#include "ssl_client.h" +#include "ssl_misc.h" +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/constant_time.h" + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "mbedtls/psa_util.h" +#include "psa/crypto.h" +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#include + +#include + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +#include "mbedtls/platform_util.h" +#endif + +#include "hash_info.h" + +#if defined(MBEDTLS_SSL_RENEGOTIATION) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_renegotiation_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + + *olen = 0; + + /* We're always including a TLS_EMPTY_RENEGOTIATION_INFO_SCSV in the + * initial ClientHello, in which case also adding the renegotiation + * info extension is NOT RECOMMENDED as per RFC 5746 Section 3.4. */ + if (ssl->renego_status != MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding renegotiation extension")); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 5 + ssl->verify_data_len); + + /* + * Secure renegotiation + */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_RENEGOTIATION_INFO, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = MBEDTLS_BYTE_0(ssl->verify_data_len + 1); + *p++ = MBEDTLS_BYTE_0(ssl->verify_data_len); + + memcpy(p, ssl->own_verify_data, ssl->verify_data_len); + + *olen = 5 + ssl->verify_data_len; + + return 0; +} +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_supported_point_formats_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + (void) ssl; /* ssl used for debugging only */ + + *olen = 0; + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding supported_point_formats extension")); + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 2; + + *p++ = 1; + *p++ = MBEDTLS_ECP_PF_UNCOMPRESSED; + + *olen = 6; + + return 0; +} +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || + MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_ecjpake_kkpp_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + size_t kkpp_len = 0; + + *olen = 0; + + /* Skip costly extension if we can't use EC J-PAKE anyway */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ssl->handshake->psa_pake_ctx_is_ok != 1) { + return 0; + } +#else + if (mbedtls_ecjpake_check(&ssl->handshake->ecjpake_ctx) != 0) { + return 0; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding ecjpake_kkpp extension")); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_ECJPAKE_KKPP, p, 0); + p += 2; + + /* + * We may need to send ClientHello multiple times for Hello verification. + * We don't want to compute fresh values every time (both for performance + * and consistency reasons), so cache the extension content. + */ + if (ssl->handshake->ecjpake_cache == NULL || + ssl->handshake->ecjpake_cache_len == 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("generating new ecjpake parameters")); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ret = mbedtls_psa_ecjpake_write_round(&ssl->handshake->psa_pake_ctx, + p + 2, end - p - 2, &kkpp_len, + MBEDTLS_ECJPAKE_ROUND_ONE); + if (ret != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_output", ret); + return ret; + } +#else + ret = mbedtls_ecjpake_write_round_one(&ssl->handshake->ecjpake_ctx, + p + 2, end - p - 2, &kkpp_len, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ecjpake_write_round_one", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + ssl->handshake->ecjpake_cache = mbedtls_calloc(1, kkpp_len); + if (ssl->handshake->ecjpake_cache == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("allocation failed")); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(ssl->handshake->ecjpake_cache, p + 2, kkpp_len); + ssl->handshake->ecjpake_cache_len = kkpp_len; + } else { + MBEDTLS_SSL_DEBUG_MSG(3, ("re-using cached ecjpake parameters")); + + kkpp_len = ssl->handshake->ecjpake_cache_len; + MBEDTLS_SSL_CHK_BUF_PTR(p + 2, end, kkpp_len); + + memcpy(p + 2, ssl->handshake->ecjpake_cache, kkpp_len); + } + + MBEDTLS_PUT_UINT16_BE(kkpp_len, p, 0); + p += 2; + + *olen = kkpp_len + 4; + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_cid_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + size_t ext_len; + + /* + * struct { + * opaque cid<0..2^8-1>; + * } ConnectionId; + */ + + *olen = 0; + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM || + ssl->negotiate_cid == MBEDTLS_SSL_CID_DISABLED) { + return 0; + } + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding CID extension")); + + /* ssl->own_cid_len is at most MBEDTLS_SSL_CID_IN_LEN_MAX + * which is at most 255, so the increment cannot overflow. */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, (unsigned) (ssl->own_cid_len + 5)); + + /* Add extension ID + size */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_CID, p, 0); + p += 2; + ext_len = (size_t) ssl->own_cid_len + 1; + MBEDTLS_PUT_UINT16_BE(ext_len, p, 0); + p += 2; + + *p++ = (uint8_t) ssl->own_cid_len; + memcpy(p, ssl->own_cid, ssl->own_cid_len); + + *olen = ssl->own_cid_len + 5; + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_max_fragment_length_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + + *olen = 0; + + if (ssl->conf->mfl_code == MBEDTLS_SSL_MAX_FRAG_LEN_NONE) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding max_fragment_length extension")); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 5); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 1; + + *p++ = ssl->conf->mfl_code; + + *olen = 5; + + return 0; +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_encrypt_then_mac_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + + *olen = 0; + + if (ssl->conf->encrypt_then_mac == MBEDTLS_SSL_ETM_DISABLED) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding encrypt_then_mac extension")); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; + + return 0; +} +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_extended_ms_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + + *olen = 0; + + if (ssl->conf->extended_ms == MBEDTLS_SSL_EXTENDED_MS_DISABLED) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding extended_master_secret extension")); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; + + return 0; +} +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_session_ticket_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + size_t tlen = ssl->session_negotiate->ticket_len; + + *olen = 0; + + if (ssl->conf->session_tickets == MBEDTLS_SSL_SESSION_TICKETS_DISABLED) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding session ticket extension")); + + /* The addition is safe here since the ticket length is 16 bit. */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4 + tlen); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SESSION_TICKET, p, 0); + p += 2; + + MBEDTLS_PUT_UINT16_BE(tlen, p, 0); + p += 2; + + *olen = 4; + + if (ssl->session_negotiate->ticket == NULL || tlen == 0) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("sending session ticket of length %" MBEDTLS_PRINTF_SIZET, tlen)); + + memcpy(p, ssl->session_negotiate->ticket, tlen); + + *olen += tlen; + + return 0; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_use_srtp_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *olen) +{ + unsigned char *p = buf; + size_t protection_profiles_index = 0, ext_len = 0; + uint16_t mki_len = 0, profile_value = 0; + + *olen = 0; + + if ((ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) || + (ssl->conf->dtls_srtp_profile_list == NULL) || + (ssl->conf->dtls_srtp_profile_list_len == 0)) { + return 0; + } + + /* RFC 5764 section 4.1.1 + * uint8 SRTPProtectionProfile[2]; + * + * struct { + * SRTPProtectionProfiles SRTPProtectionProfiles; + * opaque srtp_mki<0..255>; + * } UseSRTPData; + * SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1>; + */ + if (ssl->conf->dtls_srtp_mki_support == MBEDTLS_SSL_DTLS_SRTP_MKI_SUPPORTED) { + mki_len = ssl->dtls_srtp_info.mki_len; + } + /* Extension length = 2 bytes for profiles length, + * ssl->conf->dtls_srtp_profile_list_len * 2 (each profile is 2 bytes length ), + * 1 byte for srtp_mki vector length and the mki_len value + */ + ext_len = 2 + 2 * (ssl->conf->dtls_srtp_profile_list_len) + 1 + mki_len; + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding use_srtp extension")); + + /* Check there is room in the buffer for the extension + 4 bytes + * - the extension tag (2 bytes) + * - the extension length (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, ext_len + 4); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_USE_SRTP, p, 0); + p += 2; + + MBEDTLS_PUT_UINT16_BE(ext_len, p, 0); + p += 2; + + /* protection profile length: 2*(ssl->conf->dtls_srtp_profile_list_len) */ + /* micro-optimization: + * the list size is limited to MBEDTLS_TLS_SRTP_MAX_PROFILE_LIST_LENGTH + * which is lower than 127, so the upper byte of the length is always 0 + * For the documentation, the more generic code is left in comments + * *p++ = (unsigned char)( ( ( 2 * ssl->conf->dtls_srtp_profile_list_len ) + * >> 8 ) & 0xFF ); + */ + *p++ = 0; + *p++ = MBEDTLS_BYTE_0(2 * ssl->conf->dtls_srtp_profile_list_len); + + for (protection_profiles_index = 0; + protection_profiles_index < ssl->conf->dtls_srtp_profile_list_len; + protection_profiles_index++) { + profile_value = mbedtls_ssl_check_srtp_profile_value + (ssl->conf->dtls_srtp_profile_list[protection_profiles_index]); + if (profile_value != MBEDTLS_TLS_SRTP_UNSET) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ssl_write_use_srtp_ext, add profile: %04x", + profile_value)); + MBEDTLS_PUT_UINT16_BE(profile_value, p, 0); + p += 2; + } else { + /* + * Note: we shall never arrive here as protection profiles + * is checked by mbedtls_ssl_conf_dtls_srtp_protection_profiles function + */ + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, " + "illegal DTLS-SRTP protection profile %d", + ssl->conf->dtls_srtp_profile_list[protection_profiles_index] + )); + return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + } + } + + *p++ = mki_len & 0xFF; + + if (mki_len != 0) { + memcpy(p, ssl->dtls_srtp_info.mki_value, mki_len); + /* + * Increment p to point to the current position. + */ + p += mki_len; + MBEDTLS_SSL_DEBUG_BUF(3, "sending mki", ssl->dtls_srtp_info.mki_value, + ssl->dtls_srtp_info.mki_len); + } + + /* + * total extension length: extension type (2 bytes) + * + extension length (2 bytes) + * + protection profile length (2 bytes) + * + 2 * number of protection profiles + * + srtp_mki vector length(1 byte) + * + mki value + */ + *olen = p - buf; + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + +int mbedtls_ssl_tls12_write_client_hello_exts(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + int uses_ec, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + size_t ext_len = 0; + + (void) ssl; + (void) end; + (void) uses_ec; + (void) ret; + (void) ext_len; + + *out_len = 0; + + /* Note that TLS_EMPTY_RENEGOTIATION_INFO_SCSV is always added + * even if MBEDTLS_SSL_RENEGOTIATION is not defined. */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if ((ret = ssl_write_renegotiation_ext(ssl, p, end, &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_renegotiation_ext", ret); + return ret; + } + p += ext_len; +#endif + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if (uses_ec) { + if ((ret = ssl_write_supported_point_formats_ext(ssl, p, end, + &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_supported_point_formats_ext", ret); + return ret; + } + p += ext_len; + } +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if ((ret = ssl_write_ecjpake_kkpp_ext(ssl, p, end, &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_ecjpake_kkpp_ext", ret); + return ret; + } + p += ext_len; +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + if ((ret = ssl_write_cid_ext(ssl, p, end, &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_cid_ext", ret); + return ret; + } + p += ext_len; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + if ((ret = ssl_write_max_fragment_length_ext(ssl, p, end, + &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_max_fragment_length_ext", ret); + return ret; + } + p += ext_len; +#endif + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + if ((ret = ssl_write_encrypt_then_mac_ext(ssl, p, end, &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_encrypt_then_mac_ext", ret); + return ret; + } + p += ext_len; +#endif + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + if ((ret = ssl_write_extended_ms_ext(ssl, p, end, &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_extended_ms_ext", ret); + return ret; + } + p += ext_len; +#endif + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + if ((ret = ssl_write_use_srtp_ext(ssl, p, end, &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_use_srtp_ext", ret); + return ret; + } + p += ext_len; +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if ((ret = ssl_write_session_ticket_ext(ssl, p, end, &ext_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_write_session_ticket_ext", ret); + return ret; + } + p += ext_len; +#endif + + *out_len = p - buf; + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_renegotiation_info(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + /* Check verify-data in constant-time. The length OTOH is no secret */ + if (len != 1 + ssl->verify_data_len * 2 || + buf[0] != ssl->verify_data_len * 2 || + mbedtls_ct_memcmp(buf + 1, + ssl->own_verify_data, ssl->verify_data_len) != 0 || + mbedtls_ct_memcmp(buf + 1 + ssl->verify_data_len, + ssl->peer_verify_data, ssl->verify_data_len) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("non-matching renegotiation info")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + } else +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + { + if (len != 1 || buf[0] != 0x00) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("non-zero length renegotiation info")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + ssl->secure_renegotiation = MBEDTLS_SSL_SECURE_RENEGOTIATION; + } + + return 0; +} + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_max_fragment_length_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + /* + * server should use the extension only if we did, + * and if so the server's value should match ours (and len is always 1) + */ + if (ssl->conf->mfl_code == MBEDTLS_SSL_MAX_FRAG_LEN_NONE || + len != 1 || + buf[0] != ssl->conf->mfl_code) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("non-matching max fragment length extension")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + return 0; +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_cid_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + size_t peer_cid_len; + + if ( /* CID extension only makes sense in DTLS */ + ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM || + /* The server must only send the CID extension if we have offered it. */ + ssl->negotiate_cid == MBEDTLS_SSL_CID_DISABLED) { + MBEDTLS_SSL_DEBUG_MSG(1, ("CID extension unexpected")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT); + return MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; + } + + if (len == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("CID extension invalid")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + peer_cid_len = *buf++; + len--; + + if (peer_cid_len > MBEDTLS_SSL_CID_OUT_LEN_MAX) { + MBEDTLS_SSL_DEBUG_MSG(1, ("CID extension invalid")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + if (len != peer_cid_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("CID extension invalid")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ssl->handshake->cid_in_use = MBEDTLS_SSL_CID_ENABLED; + ssl->handshake->peer_cid_len = (uint8_t) peer_cid_len; + memcpy(ssl->handshake->peer_cid, buf, peer_cid_len); + + MBEDTLS_SSL_DEBUG_MSG(3, ("Use of CID extension negotiated")); + MBEDTLS_SSL_DEBUG_BUF(3, "Server CID", buf, peer_cid_len); + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_encrypt_then_mac_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + if (ssl->conf->encrypt_then_mac == MBEDTLS_SSL_ETM_DISABLED || + len != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("non-matching encrypt-then-MAC extension")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT); + return MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; + } + + ((void) buf); + + ssl->session_negotiate->encrypt_then_mac = MBEDTLS_SSL_ETM_ENABLED; + + return 0; +} +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_extended_ms_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + if (ssl->conf->extended_ms == MBEDTLS_SSL_EXTENDED_MS_DISABLED || + len != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("non-matching extended master secret extension")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT); + return MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; + } + + ((void) buf); + + ssl->handshake->extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED; + + return 0; +} +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_session_ticket_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + if (ssl->conf->session_tickets == MBEDTLS_SSL_SESSION_TICKETS_DISABLED || + len != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("non-matching session ticket extension")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT); + return MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; + } + + ((void) buf); + + ssl->handshake->new_session_ticket = 1; + + return 0; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_supported_point_formats_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + size_t list_size; + const unsigned char *p; + + if (len == 0 || (size_t) (buf[0] + 1) != len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + list_size = buf[0]; + + p = buf + 1; + while (list_size > 0) { + if (p[0] == MBEDTLS_ECP_PF_UNCOMPRESSED || + p[0] == MBEDTLS_ECP_PF_COMPRESSED) { +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && \ + (defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)) + ssl->handshake->ecdh_ctx.point_format = p[0]; +#endif /* !MBEDTLS_USE_PSA_CRYPTO && + ( MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ) */ +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + mbedtls_ecjpake_set_point_format(&ssl->handshake->ecjpake_ctx, + p[0]); +#endif /* !MBEDTLS_USE_PSA_CRYPTO && MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + MBEDTLS_SSL_DEBUG_MSG(4, ("point format selected: %d", p[0])); + return 0; + } + + list_size--; + p++; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("no point format in common")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; +} +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || + MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_ecjpake_kkpp(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ssl->handshake->ciphersuite_info->key_exchange != + MBEDTLS_KEY_EXCHANGE_ECJPAKE) { + MBEDTLS_SSL_DEBUG_MSG(3, ("skip ecjpake kkpp extension")); + return 0; + } + + /* If we got here, we no longer need our cached extension */ + mbedtls_free(ssl->handshake->ecjpake_cache); + ssl->handshake->ecjpake_cache = NULL; + ssl->handshake->ecjpake_cache_len = 0; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((ret = mbedtls_psa_ecjpake_read_round( + &ssl->handshake->psa_pake_ctx, buf, len, + MBEDTLS_ECJPAKE_ROUND_ONE)) != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_input round one", ret); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return ret; + } + + return 0; +#else + if ((ret = mbedtls_ecjpake_read_round_one(&ssl->handshake->ecjpake_ctx, + buf, len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_read_round_one", ret); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return ret; + } + + return 0; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_ALPN) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_alpn_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, size_t len) +{ + size_t list_len, name_len; + const char **p; + + /* If we didn't send it, the server shouldn't send it */ + if (ssl->conf->alpn_list == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("non-matching ALPN extension")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT); + return MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; + } + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + * + * the "ProtocolNameList" MUST contain exactly one "ProtocolName" + */ + + /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */ + if (len < 4) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + list_len = (buf[0] << 8) | buf[1]; + if (list_len != len - 2) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + name_len = buf[2]; + if (name_len != list_len - 1) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Check that the server chosen protocol was in our list and save it */ + for (p = ssl->conf->alpn_list; *p != NULL; p++) { + if (name_len == strlen(*p) && + memcmp(buf + 3, *p, name_len) == 0) { + ssl->alpn_chosen = *p; + return 0; + } + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("ALPN extension: no matching protocol")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; +} +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_use_srtp_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + mbedtls_ssl_srtp_profile server_protection = MBEDTLS_TLS_SRTP_UNSET; + size_t i, mki_len = 0; + uint16_t server_protection_profile_value = 0; + + /* If use_srtp is not configured, just ignore the extension */ + if ((ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) || + (ssl->conf->dtls_srtp_profile_list == NULL) || + (ssl->conf->dtls_srtp_profile_list_len == 0)) { + return 0; + } + + /* RFC 5764 section 4.1.1 + * uint8 SRTPProtectionProfile[2]; + * + * struct { + * SRTPProtectionProfiles SRTPProtectionProfiles; + * opaque srtp_mki<0..255>; + * } UseSRTPData; + + * SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1>; + * + */ + if (ssl->conf->dtls_srtp_mki_support == MBEDTLS_SSL_DTLS_SRTP_MKI_SUPPORTED) { + mki_len = ssl->dtls_srtp_info.mki_len; + } + + /* + * Length is 5 + optional mki_value : one protection profile length (2 bytes) + * + protection profile (2 bytes) + * + mki_len(1 byte) + * and optional srtp_mki + */ + if ((len < 5) || (len != (buf[4] + 5u))) { + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* + * get the server protection profile + */ + + /* + * protection profile length must be 0x0002 as we must have only + * one protection profile in server Hello + */ + if ((buf[0] != 0) || (buf[1] != 2)) { + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + server_protection_profile_value = (buf[2] << 8) | buf[3]; + server_protection = mbedtls_ssl_check_srtp_profile_value( + server_protection_profile_value); + if (server_protection != MBEDTLS_TLS_SRTP_UNSET) { + MBEDTLS_SSL_DEBUG_MSG(3, ("found srtp profile: %s", + mbedtls_ssl_get_srtp_profile_as_string( + server_protection))); + } + + ssl->dtls_srtp_info.chosen_dtls_srtp_profile = MBEDTLS_TLS_SRTP_UNSET; + + /* + * Check we have the server profile in our list + */ + for (i = 0; i < ssl->conf->dtls_srtp_profile_list_len; i++) { + if (server_protection == ssl->conf->dtls_srtp_profile_list[i]) { + ssl->dtls_srtp_info.chosen_dtls_srtp_profile = ssl->conf->dtls_srtp_profile_list[i]; + MBEDTLS_SSL_DEBUG_MSG(3, ("selected srtp profile: %s", + mbedtls_ssl_get_srtp_profile_as_string( + server_protection))); + break; + } + } + + /* If no match was found : server problem, it shall never answer with incompatible profile */ + if (ssl->dtls_srtp_info.chosen_dtls_srtp_profile == MBEDTLS_TLS_SRTP_UNSET) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + /* If server does not use mki in its reply, make sure the client won't keep + * one as negotiated */ + if (len == 5) { + ssl->dtls_srtp_info.mki_len = 0; + } + + /* + * RFC5764: + * If the client detects a nonzero-length MKI in the server's response + * that is different than the one the client offered, then the client + * MUST abort the handshake and SHOULD send an invalid_parameter alert. + */ + if (len > 5 && (buf[4] != mki_len || + (memcmp(ssl->dtls_srtp_info.mki_value, &buf[5], mki_len)))) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } +#if defined(MBEDTLS_DEBUG_C) + if (len > 5) { + MBEDTLS_SSL_DEBUG_BUF(3, "received mki", ssl->dtls_srtp_info.mki_value, + ssl->dtls_srtp_info.mki_len); + } +#endif + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + +/* + * Parse HelloVerifyRequest. Only called after verifying the HS type. + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_hello_verify_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + const unsigned char *p = ssl->in_msg + mbedtls_ssl_hs_hdr_len(ssl); + uint16_t dtls_legacy_version; + +#if !defined(MBEDTLS_SSL_PROTO_TLS1_3) + uint8_t cookie_len; +#else + uint16_t cookie_len; +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse hello verify request")); + + /* Check that there is enough room for: + * - 2 bytes of version + * - 1 byte of cookie_len + */ + if (mbedtls_ssl_hs_hdr_len(ssl) + 3 > ssl->in_msglen) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("incoming HelloVerifyRequest message is too short")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* + * struct { + * ProtocolVersion server_version; + * opaque cookie<0..2^8-1>; + * } HelloVerifyRequest; + */ + MBEDTLS_SSL_DEBUG_BUF(3, "server version", p, 2); + dtls_legacy_version = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + /* + * Since the RFC is not clear on this point, accept DTLS 1.0 (0xfeff) + * The DTLS 1.3 (current draft) renames ProtocolVersion server_version to + * legacy_version and locks the value of legacy_version to 0xfefd (DTLS 1.2) + */ + if (dtls_legacy_version != 0xfefd && dtls_legacy_version != 0xfeff) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server version")); + + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION); + + return MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + } + + cookie_len = *p++; + if ((ssl->in_msg + ssl->in_msglen) - p < cookie_len) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("cookie length does not match incoming message size")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + MBEDTLS_SSL_DEBUG_BUF(3, "cookie", p, cookie_len); + + mbedtls_free(ssl->handshake->cookie); + + ssl->handshake->cookie = mbedtls_calloc(1, cookie_len); + if (ssl->handshake->cookie == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc failed (%d bytes)", cookie_len)); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(ssl->handshake->cookie, p, cookie_len); + ssl->handshake->cookie_len = cookie_len; + + /* Start over at ClientHello */ + ssl->state = MBEDTLS_SSL_CLIENT_HELLO; + ret = mbedtls_ssl_reset_checksum(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_reset_checksum"), ret); + return ret; + } + + mbedtls_ssl_recv_flight_completed(ssl); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse hello verify request")); + + return 0; +} +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_server_hello(mbedtls_ssl_context *ssl) +{ + int ret, i; + size_t n; + size_t ext_len; + unsigned char *buf, *ext; + unsigned char comp; +#if defined(MBEDTLS_SSL_RENEGOTIATION) + int renegotiation_info_seen = 0; +#endif + int handshake_failure = 0; + const mbedtls_ssl_ciphersuite_t *suite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse server hello")); + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + /* No alert on a read error. */ + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + buf = ssl->in_msg; + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { + ssl->renego_records_seen++; + + if (ssl->conf->renego_max_records >= 0 && + ssl->renego_records_seen > ssl->conf->renego_max_records) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("renegotiation requested, but not honored by server")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + MBEDTLS_SSL_DEBUG_MSG(1, + ("non-handshake message during renegotiation")); + + ssl->keep_current_message = 1; + return MBEDTLS_ERR_SSL_WAITING_SERVER_HELLO_RENEGO; + } +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + if (buf[0] == MBEDTLS_SSL_HS_HELLO_VERIFY_REQUEST) { + MBEDTLS_SSL_DEBUG_MSG(2, ("received hello verify request")); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse server hello")); + return ssl_parse_hello_verify_request(ssl); + } else { + /* We made it through the verification process */ + mbedtls_free(ssl->handshake->cookie); + ssl->handshake->cookie = NULL; + ssl->handshake->cookie_len = 0; + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + if (ssl->in_hslen < 38 + mbedtls_ssl_hs_hdr_len(ssl) || + buf[0] != MBEDTLS_SSL_HS_SERVER_HELLO) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* + * 0 . 1 server_version + * 2 . 33 random (maybe including 4 bytes of Unix time) + * 34 . 34 session_id length = n + * 35 . 34+n session_id + * 35+n . 36+n cipher_suite + * 37+n . 37+n compression_method + * + * 38+n . 39+n extensions length (optional) + * 40+n . .. extensions + */ + buf += mbedtls_ssl_hs_hdr_len(ssl); + + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, version", buf, 2); + ssl->tls_version = mbedtls_ssl_read_version(buf, ssl->conf->transport); + ssl->session_negotiate->tls_version = ssl->tls_version; + + if (ssl->tls_version < ssl->conf->min_tls_version || + ssl->tls_version > ssl->conf->max_tls_version) { + MBEDTLS_SSL_DEBUG_MSG(1, + ( + "server version out of bounds - min: [0x%x], server: [0x%x], max: [0x%x]", + (unsigned) ssl->conf->min_tls_version, + (unsigned) ssl->tls_version, + (unsigned) ssl->conf->max_tls_version)); + + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION); + + return MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, current time: %lu", + ((unsigned long) buf[2] << 24) | + ((unsigned long) buf[3] << 16) | + ((unsigned long) buf[4] << 8) | + ((unsigned long) buf[5]))); + + memcpy(ssl->handshake->randbytes + 32, buf + 2, 32); + + n = buf[34]; + + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, random bytes", buf + 2, 32); + + if (n > 32) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if (ssl->in_hslen > mbedtls_ssl_hs_hdr_len(ssl) + 39 + n) { + ext_len = ((buf[38 + n] << 8) + | (buf[39 + n])); + + if ((ext_len > 0 && ext_len < 4) || + ssl->in_hslen != mbedtls_ssl_hs_hdr_len(ssl) + 40 + n + ext_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + } else if (ssl->in_hslen == mbedtls_ssl_hs_hdr_len(ssl) + 38 + n) { + ext_len = 0; + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* ciphersuite (used later) */ + i = (buf[35 + n] << 8) | buf[36 + n]; + + /* + * Read and check compression + */ + comp = buf[37 + n]; + + if (comp != MBEDTLS_SSL_COMPRESS_NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("server hello, bad compression: %d", comp)); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + /* + * Initialize update checksum functions + */ + ssl->handshake->ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(i); + if (ssl->handshake->ciphersuite_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("ciphersuite info for %04x not found", (unsigned int) i)); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + mbedtls_ssl_optimize_checksum(ssl, ssl->handshake->ciphersuite_info); + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, session id len.: %" MBEDTLS_PRINTF_SIZET, n)); + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, session id", buf + 35, n); + + /* + * Check if the session can be resumed + */ + if (ssl->handshake->resume == 0 || n == 0 || +#if defined(MBEDTLS_SSL_RENEGOTIATION) + ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE || +#endif + ssl->session_negotiate->ciphersuite != i || + ssl->session_negotiate->id_len != n || + memcmp(ssl->session_negotiate->id, buf + 35, n) != 0) { + ssl->state++; + ssl->handshake->resume = 0; +#if defined(MBEDTLS_HAVE_TIME) + ssl->session_negotiate->start = mbedtls_time(NULL); +#endif + ssl->session_negotiate->ciphersuite = i; + ssl->session_negotiate->id_len = n; + memcpy(ssl->session_negotiate->id, buf + 35, n); + } else { + ssl->state = MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("%s session has been resumed", + ssl->handshake->resume ? "a" : "no")); + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, chosen ciphersuite: %04x", (unsigned) i)); + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, compress alg.: %d", + buf[37 + n])); + + /* + * Perform cipher suite validation in same way as in ssl_write_client_hello. + */ + i = 0; + while (1) { + if (ssl->conf->ciphersuite_list[i] == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + if (ssl->conf->ciphersuite_list[i++] == + ssl->session_negotiate->ciphersuite) { + break; + } + } + + suite_info = mbedtls_ssl_ciphersuite_from_id( + ssl->session_negotiate->ciphersuite); + if (mbedtls_ssl_validate_ciphersuite(ssl, suite_info, ssl->tls_version, + ssl->tls_version) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("server hello, chosen ciphersuite: %s", suite_info->name)); + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (suite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA && + ssl->tls_version == MBEDTLS_SSL_VERSION_TLS1_2) { + ssl->handshake->ecrs_enabled = 1; + } +#endif + + if (comp != MBEDTLS_SSL_COMPRESS_NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + ext = buf + 40 + n; + + MBEDTLS_SSL_DEBUG_MSG(2, + ("server hello, total extension length: %" MBEDTLS_PRINTF_SIZET, + ext_len)); + + while (ext_len) { + unsigned int ext_id = ((ext[0] << 8) + | (ext[1])); + unsigned int ext_size = ((ext[2] << 8) + | (ext[3])); + + if (ext_size + 4 > ext_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + mbedtls_ssl_send_alert_message( + ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + switch (ext_id) { + case MBEDTLS_TLS_EXT_RENEGOTIATION_INFO: + MBEDTLS_SSL_DEBUG_MSG(3, ("found renegotiation extension")); +#if defined(MBEDTLS_SSL_RENEGOTIATION) + renegotiation_info_seen = 1; +#endif + + if ((ret = ssl_parse_renegotiation_info(ssl, ext + 4, + ext_size)) != 0) { + return ret; + } + + break; + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + case MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH: + MBEDTLS_SSL_DEBUG_MSG(3, + ("found max_fragment_length extension")); + + if ((ret = ssl_parse_max_fragment_length_ext(ssl, + ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + case MBEDTLS_TLS_EXT_CID: + MBEDTLS_SSL_DEBUG_MSG(3, ("found CID extension")); + + if ((ret = ssl_parse_cid_ext(ssl, + ext + 4, + ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + case MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC: + MBEDTLS_SSL_DEBUG_MSG(3, ("found encrypt_then_mac extension")); + + if ((ret = ssl_parse_encrypt_then_mac_ext(ssl, + ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + case MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET: + MBEDTLS_SSL_DEBUG_MSG(3, + ("found extended_master_secret extension")); + + if ((ret = ssl_parse_extended_ms_ext(ssl, + ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + case MBEDTLS_TLS_EXT_SESSION_TICKET: + MBEDTLS_SSL_DEBUG_MSG(3, ("found session_ticket extension")); + + if ((ret = ssl_parse_session_ticket_ext(ssl, + ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + case MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS: + MBEDTLS_SSL_DEBUG_MSG(3, + ("found supported_point_formats extension")); + + if ((ret = ssl_parse_supported_point_formats_ext(ssl, + ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || + MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + case MBEDTLS_TLS_EXT_ECJPAKE_KKPP: + MBEDTLS_SSL_DEBUG_MSG(3, ("found ecjpake_kkpp extension")); + + if ((ret = ssl_parse_ecjpake_kkpp(ssl, + ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_ALPN) + case MBEDTLS_TLS_EXT_ALPN: + MBEDTLS_SSL_DEBUG_MSG(3, ("found alpn extension")); + + if ((ret = ssl_parse_alpn_ext(ssl, ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + case MBEDTLS_TLS_EXT_USE_SRTP: + MBEDTLS_SSL_DEBUG_MSG(3, ("found use_srtp extension")); + + if ((ret = ssl_parse_use_srtp_ext(ssl, ext + 4, ext_size)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + + default: + MBEDTLS_SSL_DEBUG_MSG(3, + ("unknown extension found: %u (ignoring)", ext_id)); + } + + ext_len -= 4 + ext_size; + ext += 4 + ext_size; + + if (ext_len > 0 && ext_len < 4) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + } + + /* + * mbedtls_ssl_derive_keys() has to be called after the parsing of the + * extensions. It sets the transform data for the resumed session which in + * case of DTLS includes the server CID extracted from the CID extension. + */ + if (ssl->handshake->resume) { + if ((ret = mbedtls_ssl_derive_keys(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_derive_keys", ret); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + return ret; + } + } + + /* + * Renegotiation security checks + */ + if (ssl->secure_renegotiation == MBEDTLS_SSL_LEGACY_RENEGOTIATION && + ssl->conf->allow_legacy_renegotiation == + MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("legacy renegotiation, breaking off handshake")); + handshake_failure = 1; + } +#if defined(MBEDTLS_SSL_RENEGOTIATION) + else if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS && + ssl->secure_renegotiation == MBEDTLS_SSL_SECURE_RENEGOTIATION && + renegotiation_info_seen == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("renegotiation_info extension missing (secure)")); + handshake_failure = 1; + } else if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS && + ssl->secure_renegotiation == MBEDTLS_SSL_LEGACY_RENEGOTIATION && + ssl->conf->allow_legacy_renegotiation == + MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION) { + MBEDTLS_SSL_DEBUG_MSG(1, ("legacy renegotiation not allowed")); + handshake_failure = 1; + } else if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS && + ssl->secure_renegotiation == MBEDTLS_SSL_LEGACY_RENEGOTIATION && + renegotiation_info_seen == 1) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("renegotiation_info extension present (legacy)")); + handshake_failure = 1; + } +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + + if (handshake_failure == 1) { + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse server hello")); + + return 0; +} + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_server_dh_params(mbedtls_ssl_context *ssl, + unsigned char **p, + unsigned char *end) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + size_t dhm_actual_bitlen; + + /* + * Ephemeral DH parameters: + * + * struct { + * opaque dh_p<1..2^16-1>; + * opaque dh_g<1..2^16-1>; + * opaque dh_Ys<1..2^16-1>; + * } ServerDHParams; + */ + if ((ret = mbedtls_dhm_read_params(&ssl->handshake->dhm_ctx, + p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(2, ("mbedtls_dhm_read_params"), ret); + return ret; + } + + dhm_actual_bitlen = mbedtls_dhm_get_bitlen(&ssl->handshake->dhm_ctx); + if (dhm_actual_bitlen < ssl->conf->dhm_min_bitlen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("DHM prime too short: %" MBEDTLS_PRINTF_SIZET " < %u", + dhm_actual_bitlen, + ssl->conf->dhm_min_bitlen)); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: P ", &ssl->handshake->dhm_ctx.P); + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: G ", &ssl->handshake->dhm_ctx.G); + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: GY", &ssl->handshake->dhm_ctx.GY); + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_server_ecdh_params(mbedtls_ssl_context *ssl, + unsigned char **p, + unsigned char *end) +{ + uint16_t tls_id; + uint8_t ecpoint_len; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + psa_ecc_family_t ec_psa_family = 0; + size_t ec_bits = 0; + + /* + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + * + * 1 curve_type (must be "named_curve") + * 2..3 NamedCurve + * 4 ECPoint.len + * 5+ ECPoint contents + */ + if (end - *p < 4) { + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* First byte is curve_type; only named_curve is handled */ + if (*(*p)++ != MBEDTLS_ECP_TLS_NAMED_CURVE) { + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + /* Next two bytes are the namedcurve value */ + tls_id = *(*p)++; + tls_id <<= 8; + tls_id |= *(*p)++; + + /* Check it's a curve we offered */ + if (mbedtls_ssl_check_curve_tls_id(ssl, tls_id) != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, + ("bad server key exchange message (ECDHE curve): %u", + (unsigned) tls_id)); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + /* Convert EC's TLS ID to PSA key type. */ + if (mbedtls_ssl_get_psa_curve_info_from_tls_id(tls_id, &ec_psa_family, + &ec_bits) == PSA_ERROR_NOT_SUPPORTED) { + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + handshake->ecdh_psa_type = PSA_KEY_TYPE_ECC_KEY_PAIR(ec_psa_family); + handshake->ecdh_bits = ec_bits; + + /* Keep a copy of the peer's public key */ + ecpoint_len = *(*p)++; + if ((size_t) (end - *p) < ecpoint_len) { + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if (ecpoint_len > sizeof(handshake->ecdh_psa_peerkey)) { + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + memcpy(handshake->ecdh_psa_peerkey, *p, ecpoint_len); + handshake->ecdh_psa_peerkey_len = ecpoint_len; + *p += ecpoint_len; + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ +#else +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_server_ecdh_params(const mbedtls_ssl_context *ssl) +{ + uint16_t tls_id; + mbedtls_ecp_group_id grp_id; +#if defined(MBEDTLS_ECDH_LEGACY_CONTEXT) + grp_id = ssl->handshake->ecdh_ctx.grp.id; +#else + grp_id = ssl->handshake->ecdh_ctx.grp_id; +#endif + + tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id(grp_id); + if (tls_id == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("ECDH curve: %s", + mbedtls_ssl_get_curve_name_from_tls_id(tls_id))); + + if (mbedtls_ssl_check_curve(ssl, grp_id) != 0) { + return -1; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_QP); + + return 0; +} + +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_server_ecdh_params(mbedtls_ssl_context *ssl, + unsigned char **p, + unsigned char *end) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + + /* + * Ephemeral ECDH parameters: + * + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ + if ((ret = mbedtls_ecdh_read_params(&ssl->handshake->ecdh_ctx, + (const unsigned char **) p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ecdh_read_params"), ret); +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + ret = MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; + } +#endif + return ret; + } + + if (ssl_check_server_ecdh_params(ssl) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("bad server key exchange message (ECDHE curve)")); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED || \ + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED || \ + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_server_psk_hint(mbedtls_ssl_context *ssl, + unsigned char **p, + unsigned char *end) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + uint16_t len; + ((void) ssl); + + /* + * PSK parameters: + * + * opaque psk_identity_hint<0..2^16-1>; + */ + if (end - (*p) < 2) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("bad server key exchange message (psk_identity_hint length)")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + len = (*p)[0] << 8 | (*p)[1]; + *p += 2; + + if (end - (*p) < len) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("bad server key exchange message (psk_identity_hint length)")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* + * Note: we currently ignore the PSK identity hint, as we only allow one + * PSK to be provisioned on the client. This could be changed later if + * someone needs that feature. + */ + *p += len; + ret = 0; + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) +/* + * Generate a pre-master secret and encrypt it with the server's RSA key + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_encrypted_pms(mbedtls_ssl_context *ssl, + size_t offset, size_t *olen, + size_t pms_offset) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len_bytes = 2; + unsigned char *p = ssl->handshake->premaster + pms_offset; + mbedtls_pk_context *peer_pk; + + if (offset + len_bytes > MBEDTLS_SSL_OUT_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, ("buffer too small for encrypted pms")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + /* + * Generate (part of) the pre-master as + * struct { + * ProtocolVersion client_version; + * opaque random[46]; + * } PreMasterSecret; + */ + mbedtls_ssl_write_version(p, ssl->conf->transport, + MBEDTLS_SSL_VERSION_TLS1_2); + + if ((ret = ssl->conf->f_rng(ssl->conf->p_rng, p + 2, 46)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "f_rng", ret); + return ret; + } + + ssl->handshake->pmslen = 48; + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + peer_pk = &ssl->handshake->peer_pubkey; +#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (ssl->session_negotiate->peer_cert == NULL) { + /* Should never happen */ + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + peer_pk = &ssl->session_negotiate->peer_cert->pk; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + /* + * Now write it out, encrypted + */ + if (!mbedtls_pk_can_do(peer_pk, MBEDTLS_PK_RSA)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("certificate key type mismatch")); + return MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + } + + if ((ret = mbedtls_pk_encrypt(peer_pk, + p, ssl->handshake->pmslen, + ssl->out_msg + offset + len_bytes, olen, + MBEDTLS_SSL_OUT_CONTENT_LEN - offset - len_bytes, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_rsa_pkcs1_encrypt", ret); + return ret; + } + + if (len_bytes == 2) { + MBEDTLS_PUT_UINT16_BE(*olen, ssl->out_msg, offset); + *olen += 2; + } + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /* We don't need the peer's public key anymore. Free it. */ + mbedtls_pk_free(peer_pk); +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_ecdh_params_from_cert(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_ecp_keypair *peer_key; + mbedtls_pk_context *peer_pk; + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + peer_pk = &ssl->handshake->peer_pubkey; +#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (ssl->session_negotiate->peer_cert == NULL) { + /* Should never happen */ + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + peer_pk = &ssl->session_negotiate->peer_cert->pk; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + /* This is a public key, so it can't be opaque, so can_do() is a good + * enough check to ensure pk_ec() is safe to use below. */ + if (!mbedtls_pk_can_do(peer_pk, MBEDTLS_PK_ECKEY)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("server key not ECDH capable")); + return MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + } + + peer_key = mbedtls_pk_ec(*peer_pk); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t olen = 0; + uint16_t tls_id = 0; + psa_ecc_family_t ecc_family; + + if (mbedtls_ssl_check_curve(ssl, peer_key->grp.id) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server certificate (ECDH curve)")); + return MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } + + tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id(peer_key->grp.id); + if (tls_id == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("ECC group %u not suported", + peer_key->grp.id)); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + /* If the above conversion to TLS ID was fine, then also this one will be, + so there is no need to check the return value here */ + mbedtls_ssl_get_psa_curve_info_from_tls_id(tls_id, &ecc_family, + &ssl->handshake->ecdh_bits); + + ssl->handshake->ecdh_psa_type = PSA_KEY_TYPE_ECC_KEY_PAIR(ecc_family); + + /* Store peer's public key in psa format. */ + ret = mbedtls_ecp_point_write_binary(&peer_key->grp, &peer_key->Q, + MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, + ssl->handshake->ecdh_psa_peerkey, + MBEDTLS_PSA_MAX_EC_PUBKEY_LENGTH); + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ecp_point_write_binary"), ret); + return ret; + } + + ssl->handshake->ecdh_psa_peerkey_len = olen; +#else + if ((ret = mbedtls_ecdh_get_params(&ssl->handshake->ecdh_ctx, peer_key, + MBEDTLS_ECDH_THEIRS)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ecdh_get_params"), ret); + return ret; + } + + if (ssl_check_server_ecdh_params(ssl) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server certificate (ECDH curve)")); + return MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } +#endif +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /* We don't need the peer's public key anymore. Free it, + * so that more RAM is available for upcoming expensive + * operations like ECDHE. */ + mbedtls_pk_free(peer_pk); +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || + MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_server_key_exchange(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + unsigned char *p = NULL, *end = NULL; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse server key exchange")); + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse server key exchange")); + ssl->state++; + return 0; + } + ((void) p); + ((void) end); +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_RSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA) { + if ((ret = ssl_get_ecdh_params_from_cert(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_get_ecdh_params_from_cert", ret); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse server key exchange")); + ssl->state++; + return 0; + } + ((void) p); + ((void) end); +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled && + ssl->handshake->ecrs_state == ssl_ecrs_ske_start_processing) { + goto start_processing; + } +#endif + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + /* + * ServerKeyExchange may be skipped with PSK and RSA-PSK when the server + * doesn't use a psk_identity_hint + */ + if (ssl->in_msg[0] != MBEDTLS_SSL_HS_SERVER_KEY_EXCHANGE) { + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK) { + /* Current message is probably either + * CertificateRequest or ServerHelloDone */ + ssl->keep_current_message = 1; + goto exit; + } + + MBEDTLS_SSL_DEBUG_MSG(1, + ("server key exchange message must not be skipped")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled) { + ssl->handshake->ecrs_state = ssl_ecrs_ske_start_processing; + } + +start_processing: +#endif + p = ssl->in_msg + mbedtls_ssl_hs_hdr_len(ssl); + end = ssl->in_msg + ssl->in_hslen; + MBEDTLS_SSL_DEBUG_BUF(3, "server key exchange", p, end - p); + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK) { + if (ssl_parse_server_psk_hint(ssl, &p, end) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + } /* FALLTHROUGH */ +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK) { + ; /* nothing more to do */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED || + MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_RSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK) { + if (ssl_parse_server_dh_params(ssl, &p, end) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + } else +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA) { + if (ssl_parse_server_ecdh_params(ssl, &p, end) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + } else +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + /* + * The first 3 bytes are: + * [0] MBEDTLS_ECP_TLS_NAMED_CURVE + * [1, 2] elliptic curve's TLS ID + * + * However since we only support secp256r1 for now, we check only + * that TLS ID here + */ + uint16_t read_tls_id = MBEDTLS_GET_UINT16_BE(p, 1); + uint16_t exp_tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id( + MBEDTLS_ECP_DP_SECP256R1); + + if (exp_tls_id == 0) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + if ((*p != MBEDTLS_ECP_TLS_NAMED_CURVE) || + (read_tls_id != exp_tls_id)) { + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + p += 3; + + if ((ret = mbedtls_psa_ecjpake_read_round( + &ssl->handshake->psa_pake_ctx, p, end - p, + MBEDTLS_ECJPAKE_ROUND_TWO)) != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_input round two", ret); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } +#else + ret = mbedtls_ecjpake_read_round_two(&ssl->handshake->ecjpake_ctx, + p, end - p); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_read_round_two", ret); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_server_signature(ciphersuite_info)) { + size_t sig_len, hashlen; + unsigned char hash[MBEDTLS_HASH_MAX_SIZE]; + + mbedtls_md_type_t md_alg = MBEDTLS_MD_NONE; + mbedtls_pk_type_t pk_alg = MBEDTLS_PK_NONE; + unsigned char *params = ssl->in_msg + mbedtls_ssl_hs_hdr_len(ssl); + size_t params_len = p - params; + void *rs_ctx = NULL; + uint16_t sig_alg; + + mbedtls_pk_context *peer_pk; + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + peer_pk = &ssl->handshake->peer_pubkey; +#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (ssl->session_negotiate->peer_cert == NULL) { + /* Should never happen */ + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + peer_pk = &ssl->session_negotiate->peer_cert->pk; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + /* + * Handle the digitally-signed structure + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + sig_alg = MBEDTLS_GET_UINT16_BE(p, 0); + if (mbedtls_ssl_get_pk_type_and_md_alg_from_sig_alg( + sig_alg, &pk_alg, &md_alg) != 0 && + !mbedtls_ssl_sig_alg_is_offered(ssl, sig_alg) && + !mbedtls_ssl_sig_alg_is_supported(ssl, sig_alg)) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + p += 2; + + if (!mbedtls_pk_can_do(peer_pk, pk_alg)) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + /* + * Read signature + */ + + if (p > end - 2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + sig_len = (p[0] << 8) | p[1]; + p += 2; + + if (p != end - sig_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "signature", p, sig_len); + + /* + * Compute the hash that has been signed + */ + if (md_alg != MBEDTLS_MD_NONE) { + ret = mbedtls_ssl_get_key_exchange_md_tls1_2(ssl, hash, &hashlen, + params, params_len, + md_alg); + if (ret != 0) { + return ret; + } + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "parameters hash", hash, hashlen); + + /* + * Verify signature + */ + if (!mbedtls_pk_can_do(peer_pk, pk_alg)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server key exchange message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + } + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled) { + rs_ctx = &ssl->handshake->ecrs_ctx.pk; + } +#endif + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + if (pk_alg == MBEDTLS_PK_RSASSA_PSS) { + mbedtls_pk_rsassa_pss_options rsassa_pss_options; + rsassa_pss_options.mgf1_hash_id = md_alg; + rsassa_pss_options.expected_salt_len = + mbedtls_hash_info_get_size(md_alg); + if (rsassa_pss_options.expected_salt_len == 0) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ret = mbedtls_pk_verify_ext(pk_alg, &rsassa_pss_options, + peer_pk, + md_alg, hash, hashlen, + p, sig_len); + } else +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + ret = mbedtls_pk_verify_restartable(peer_pk, + md_alg, hash, hashlen, p, sig_len, rs_ctx); + + if (ret != 0) { + int send_alert_msg = 1; +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + send_alert_msg = (ret != MBEDTLS_ERR_ECP_IN_PROGRESS); +#endif + if (send_alert_msg) { + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR); + } + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_pk_verify", ret); +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + ret = MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; + } +#endif + return ret; + } + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /* We don't need the peer's public key anymore. Free it, + * so that more RAM is available for upcoming expensive + * operations like ECDHE. */ + mbedtls_pk_free(peer_pk); +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + } +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED */ + +exit: + ssl->state++; + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse server key exchange")); + + return 0; +} + +#if !defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_certificate_request(mbedtls_ssl_context *ssl) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate request")); + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate request")); + ssl->state++; + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} +#else /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_certificate_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + size_t n = 0; + size_t cert_type_len = 0, dn_len = 0; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + size_t sig_alg_len; +#if defined(MBEDTLS_DEBUG_C) + unsigned char *sig_alg; + unsigned char *dn; +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate request")); + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate request")); + ssl->state++; + return 0; + } + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate request message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + ssl->state++; + ssl->handshake->client_auth = + (ssl->in_msg[0] == MBEDTLS_SSL_HS_CERTIFICATE_REQUEST); + + MBEDTLS_SSL_DEBUG_MSG(3, ("got %s certificate request", + ssl->handshake->client_auth ? "a" : "no")); + + if (ssl->handshake->client_auth == 0) { + /* Current message is probably the ServerHelloDone */ + ssl->keep_current_message = 1; + goto exit; + } + + /* + * struct { + * ClientCertificateType certificate_types<1..2^8-1>; + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2^16-1>; -- TLS 1.2 only + * DistinguishedName certificate_authorities<0..2^16-1>; + * } CertificateRequest; + * + * Since we only support a single certificate on clients, let's just + * ignore all the information that's supposed to help us pick a + * certificate. + * + * We could check that our certificate matches the request, and bail out + * if it doesn't, but it's simpler to just send the certificate anyway, + * and give the server the opportunity to decide if it should terminate + * the connection when it doesn't like our certificate. + * + * Same goes for the hash in TLS 1.2's signature_algorithms: at this + * point we only have one hash available (see comments in + * write_certificate_verify), so let's just use what we have. + * + * However, we still minimally parse the message to check it is at least + * superficially sane. + */ + buf = ssl->in_msg; + + /* certificate_types */ + if (ssl->in_hslen <= mbedtls_ssl_hs_hdr_len(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate request message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + cert_type_len = buf[mbedtls_ssl_hs_hdr_len(ssl)]; + n = cert_type_len; + + /* + * In the subsequent code there are two paths that read from buf: + * * the length of the signature algorithms field (if minor version of + * SSL is 3), + * * distinguished name length otherwise. + * Both reach at most the index: + * ...hdr_len + 2 + n, + * therefore the buffer length at this point must be greater than that + * regardless of the actual code path. + */ + if (ssl->in_hslen <= mbedtls_ssl_hs_hdr_len(ssl) + 2 + n) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate request message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* supported_signature_algorithms */ + sig_alg_len = ((buf[mbedtls_ssl_hs_hdr_len(ssl) + 1 + n] << 8) + | (buf[mbedtls_ssl_hs_hdr_len(ssl) + 2 + n])); + + /* + * The furthest access in buf is in the loop few lines below: + * sig_alg[i + 1], + * where: + * sig_alg = buf + ...hdr_len + 3 + n, + * max(i) = sig_alg_len - 1. + * Therefore the furthest access is: + * buf[...hdr_len + 3 + n + sig_alg_len - 1 + 1], + * which reduces to: + * buf[...hdr_len + 3 + n + sig_alg_len], + * which is one less than we need the buf to be. + */ + if (ssl->in_hslen <= mbedtls_ssl_hs_hdr_len(ssl) + 3 + n + sig_alg_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate request message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + +#if defined(MBEDTLS_DEBUG_C) + sig_alg = buf + mbedtls_ssl_hs_hdr_len(ssl) + 3 + n; + for (size_t i = 0; i < sig_alg_len; i += 2) { + MBEDTLS_SSL_DEBUG_MSG(3, + ("Supported Signature Algorithm found: %02x %02x", + sig_alg[i], sig_alg[i + 1])); + } +#endif + + n += 2 + sig_alg_len; + + /* certificate_authorities */ + dn_len = ((buf[mbedtls_ssl_hs_hdr_len(ssl) + 1 + n] << 8) + | (buf[mbedtls_ssl_hs_hdr_len(ssl) + 2 + n])); + + n += dn_len; + if (ssl->in_hslen != mbedtls_ssl_hs_hdr_len(ssl) + 3 + n) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate request message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + +#if defined(MBEDTLS_DEBUG_C) + dn = buf + mbedtls_ssl_hs_hdr_len(ssl) + 3 + n - dn_len; + for (size_t i = 0, dni_len = 0; i < dn_len; i += 2 + dni_len) { + unsigned char *p = dn + i + 2; + mbedtls_x509_name name; + size_t asn1_len; + char s[MBEDTLS_X509_MAX_DN_NAME_SIZE]; + memset(&name, 0, sizeof(name)); + dni_len = MBEDTLS_GET_UINT16_BE(dn + i, 0); + if (dni_len > dn_len - i - 2 || + mbedtls_asn1_get_tag(&p, p + dni_len, &asn1_len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0 || + mbedtls_x509_get_name(&p, p + asn1_len, &name) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate request message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + MBEDTLS_SSL_DEBUG_MSG(3, + ("DN hint: %.*s", + mbedtls_x509_dn_gets(s, sizeof(s), &name), s)); + mbedtls_asn1_free_named_data_list_shallow(name.next); + } +#endif + +exit: + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse certificate request")); + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_server_hello_done(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse server hello done")); + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello done message")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + if (ssl->in_hslen != mbedtls_ssl_hs_hdr_len(ssl) || + ssl->in_msg[0] != MBEDTLS_SSL_HS_SERVER_HELLO_DONE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad server hello done message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ssl->state++; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ssl_recv_flight_completed(ssl); + } +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse server hello done")); + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_client_key_exchange(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + size_t header_len; + size_t content_len; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write client key exchange")); + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_RSA) { + /* + * DHM key exchange -- send G^X mod P + */ + content_len = mbedtls_dhm_get_len(&ssl->handshake->dhm_ctx); + + MBEDTLS_PUT_UINT16_BE(content_len, ssl->out_msg, 4); + header_len = 6; + + ret = mbedtls_dhm_make_public(&ssl->handshake->dhm_ctx, + (int) mbedtls_dhm_get_len(&ssl->handshake->dhm_ctx), + &ssl->out_msg[header_len], content_len, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_make_public", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: X ", &ssl->handshake->dhm_ctx.X); + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: GX", &ssl->handshake->dhm_ctx.GX); + + if ((ret = mbedtls_dhm_calc_secret(&ssl->handshake->dhm_ctx, + ssl->handshake->premaster, + MBEDTLS_PREMASTER_SIZE, + &ssl->handshake->pmslen, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_calc_secret", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: K ", &ssl->handshake->dhm_ctx.K); + } else +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_RSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t destruction_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t key_attributes; + + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + header_len = 4; + + MBEDTLS_SSL_DEBUG_MSG(1, ("Perform PSA-based ECDH computation.")); + + /* + * Generate EC private key for ECDHE exchange. + */ + + /* The master secret is obtained from the shared ECDH secret by + * applying the TLS 1.2 PRF with a specific salt and label. While + * the PSA Crypto API encourages combining key agreement schemes + * such as ECDH with fixed KDFs such as TLS 1.2 PRF, it does not + * yet support the provisioning of salt + label to the KDF. + * For the time being, we therefore need to split the computation + * of the ECDH secret and the application of the TLS 1.2 PRF. */ + key_attributes = psa_key_attributes_init(); + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH); + psa_set_key_type(&key_attributes, handshake->ecdh_psa_type); + psa_set_key_bits(&key_attributes, handshake->ecdh_bits); + + /* Generate ECDH private key. */ + status = psa_generate_key(&key_attributes, + &handshake->ecdh_psa_privkey); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + /* Export the public part of the ECDH private key from PSA. + * The export format is an ECPoint structure as expected by TLS, + * but we just need to add a length byte before that. */ + unsigned char *own_pubkey = ssl->out_msg + header_len + 1; + unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; + size_t own_pubkey_max_len = (size_t) (end - own_pubkey); + size_t own_pubkey_len; + + status = psa_export_public_key(handshake->ecdh_psa_privkey, + own_pubkey, own_pubkey_max_len, + &own_pubkey_len); + if (status != PSA_SUCCESS) { + psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } + + ssl->out_msg[header_len] = (unsigned char) own_pubkey_len; + content_len = own_pubkey_len + 1; + + /* The ECDH secret is the premaster secret used for key derivation. */ + + /* Compute ECDH shared secret. */ + status = psa_raw_key_agreement(PSA_ALG_ECDH, + handshake->ecdh_psa_privkey, + handshake->ecdh_psa_peerkey, + handshake->ecdh_psa_peerkey_len, + ssl->handshake->premaster, + sizeof(ssl->handshake->premaster), + &ssl->handshake->pmslen); + + destruction_status = psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + + if (status != PSA_SUCCESS || destruction_status != PSA_SUCCESS) { + return MBEDTLS_ERR_SSL_HW_ACCEL_FAILED; + } +#else + /* + * ECDH key exchange -- send client public value + */ + header_len = 4; + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled) { + if (ssl->handshake->ecrs_state == ssl_ecrs_cke_ecdh_calc_secret) { + goto ecdh_calc_secret; + } + + mbedtls_ecdh_enable_restart(&ssl->handshake->ecdh_ctx); + } +#endif + + ret = mbedtls_ecdh_make_public(&ssl->handshake->ecdh_ctx, + &content_len, + &ssl->out_msg[header_len], 1000, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_make_public", ret); +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + ret = MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; + } +#endif + return ret; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_Q); + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled) { + ssl->handshake->ecrs_n = content_len; + ssl->handshake->ecrs_state = ssl_ecrs_cke_ecdh_calc_secret; + } + +ecdh_calc_secret: + if (ssl->handshake->ecrs_enabled) { + content_len = ssl->handshake->ecrs_n; + } +#endif + if ((ret = mbedtls_ecdh_calc_secret(&ssl->handshake->ecdh_ctx, + &ssl->handshake->pmslen, + ssl->handshake->premaster, + MBEDTLS_MPI_MAX_SIZE, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_calc_secret", ret); +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + ret = MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; + } +#endif + return ret; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_Z); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK) { + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t destruction_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_attributes_t key_attributes; + + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* + * opaque psk_identity<0..2^16-1>; + */ + if (mbedtls_ssl_conf_has_static_psk(ssl->conf) == 0) { + /* We don't offer PSK suites if we don't have a PSK, + * and we check that the server's choice is among the + * ciphersuites we offered, so this should never happen. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* uint16 to store content length */ + const size_t content_len_size = 2; + + header_len = 4; + + if (header_len + content_len_size + ssl->conf->psk_identity_len + > MBEDTLS_SSL_OUT_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("psk identity too long or SSL buffer too short")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + unsigned char *p = ssl->out_msg + header_len; + + *p++ = MBEDTLS_BYTE_1(ssl->conf->psk_identity_len); + *p++ = MBEDTLS_BYTE_0(ssl->conf->psk_identity_len); + header_len += content_len_size; + + memcpy(p, ssl->conf->psk_identity, + ssl->conf->psk_identity_len); + p += ssl->conf->psk_identity_len; + + header_len += ssl->conf->psk_identity_len; + + MBEDTLS_SSL_DEBUG_MSG(1, ("Perform PSA-based ECDH computation.")); + + /* + * Generate EC private key for ECDHE exchange. + */ + + /* The master secret is obtained from the shared ECDH secret by + * applying the TLS 1.2 PRF with a specific salt and label. While + * the PSA Crypto API encourages combining key agreement schemes + * such as ECDH with fixed KDFs such as TLS 1.2 PRF, it does not + * yet support the provisioning of salt + label to the KDF. + * For the time being, we therefore need to split the computation + * of the ECDH secret and the application of the TLS 1.2 PRF. */ + key_attributes = psa_key_attributes_init(); + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH); + psa_set_key_type(&key_attributes, handshake->ecdh_psa_type); + psa_set_key_bits(&key_attributes, handshake->ecdh_bits); + + /* Generate ECDH private key. */ + status = psa_generate_key(&key_attributes, + &handshake->ecdh_psa_privkey); + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } + + /* Export the public part of the ECDH private key from PSA. + * The export format is an ECPoint structure as expected by TLS, + * but we just need to add a length byte before that. */ + unsigned char *own_pubkey = p + 1; + unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; + size_t own_pubkey_max_len = (size_t) (end - own_pubkey); + size_t own_pubkey_len = 0; + + status = psa_export_public_key(handshake->ecdh_psa_privkey, + own_pubkey, own_pubkey_max_len, + &own_pubkey_len); + if (status != PSA_SUCCESS) { + psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return PSA_TO_MBEDTLS_ERR(status); + } + + *p = (unsigned char) own_pubkey_len; + content_len = own_pubkey_len + 1; + + /* As RFC 5489 section 2, the premaster secret is formed as follows: + * - a uint16 containing the length (in octets) of the ECDH computation + * - the octet string produced by the ECDH computation + * - a uint16 containing the length (in octets) of the PSK + * - the PSK itself + */ + unsigned char *pms = ssl->handshake->premaster; + const unsigned char * const pms_end = pms + + sizeof(ssl->handshake->premaster); + /* uint16 to store length (in octets) of the ECDH computation */ + const size_t zlen_size = 2; + size_t zlen = 0; + + /* Perform ECDH computation after the uint16 reserved for the length */ + status = psa_raw_key_agreement(PSA_ALG_ECDH, + handshake->ecdh_psa_privkey, + handshake->ecdh_psa_peerkey, + handshake->ecdh_psa_peerkey_len, + pms + zlen_size, + pms_end - (pms + zlen_size), + &zlen); + + destruction_status = psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } else if (destruction_status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(destruction_status); + } + + /* Write the ECDH computation length before the ECDH computation */ + MBEDTLS_PUT_UINT16_BE(zlen, pms, 0); + pms += zlen_size + zlen; + } else +#endif /* MBEDTLS_USE_PSA_CRYPTO && + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_psk(ciphersuite_info)) { + /* + * opaque psk_identity<0..2^16-1>; + */ + if (mbedtls_ssl_conf_has_static_psk(ssl->conf) == 0) { + /* We don't offer PSK suites if we don't have a PSK, + * and we check that the server's choice is among the + * ciphersuites we offered, so this should never happen. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + header_len = 4; + content_len = ssl->conf->psk_identity_len; + + if (header_len + 2 + content_len > MBEDTLS_SSL_OUT_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("psk identity too long or SSL buffer too short")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + ssl->out_msg[header_len++] = MBEDTLS_BYTE_1(content_len); + ssl->out_msg[header_len++] = MBEDTLS_BYTE_0(content_len); + + memcpy(ssl->out_msg + header_len, + ssl->conf->psk_identity, + ssl->conf->psk_identity_len); + header_len += ssl->conf->psk_identity_len; + +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK) { + content_len = 0; + } else +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK) { + if ((ret = ssl_write_encrypted_pms(ssl, header_len, + &content_len, 2)) != 0) { + return ret; + } + } else +#endif +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK) { + /* + * ClientDiffieHellmanPublic public (DHM send G^X mod P) + */ + content_len = mbedtls_dhm_get_len(&ssl->handshake->dhm_ctx); + + if (header_len + 2 + content_len > + MBEDTLS_SSL_OUT_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("psk identity or DHM size too long or SSL buffer too short")); + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + ssl->out_msg[header_len++] = MBEDTLS_BYTE_1(content_len); + ssl->out_msg[header_len++] = MBEDTLS_BYTE_0(content_len); + + ret = mbedtls_dhm_make_public(&ssl->handshake->dhm_ctx, + (int) mbedtls_dhm_get_len(&ssl->handshake->dhm_ctx), + &ssl->out_msg[header_len], content_len, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_make_public", ret); + return ret; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + unsigned char *pms = ssl->handshake->premaster; + unsigned char *pms_end = pms + sizeof(ssl->handshake->premaster); + size_t pms_len; + + /* Write length only when we know the actual value */ + if ((ret = mbedtls_dhm_calc_secret(&ssl->handshake->dhm_ctx, + pms + 2, pms_end - (pms + 2), &pms_len, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_calc_secret", ret); + return ret; + } + MBEDTLS_PUT_UINT16_BE(pms_len, pms, 0); + pms += 2 + pms_len; + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: K ", &ssl->handshake->dhm_ctx.K); +#endif + } else +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK) { + /* + * ClientECDiffieHellmanPublic public; + */ + ret = mbedtls_ecdh_make_public(&ssl->handshake->ecdh_ctx, + &content_len, + &ssl->out_msg[header_len], + MBEDTLS_SSL_OUT_CONTENT_LEN - header_len, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_make_public", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_Q); + } else +#endif /* !MBEDTLS_USE_PSA_CRYPTO && MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + if ((ret = mbedtls_ssl_psk_derive_premaster(ssl, + ciphersuite_info->key_exchange)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_psk_derive_premaster", ret); + return ret; + } +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA) { + header_len = 4; + if ((ret = ssl_write_encrypted_pms(ssl, header_len, + &content_len, 0)) != 0) { + return ret; + } + } else +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE) { + header_len = 4; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + unsigned char *out_p = ssl->out_msg + header_len; + unsigned char *end_p = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN - + header_len; + ret = mbedtls_psa_ecjpake_write_round(&ssl->handshake->psa_pake_ctx, + out_p, end_p - out_p, &content_len, + MBEDTLS_ECJPAKE_ROUND_TWO); + if (ret != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_output", ret); + return ret; + } +#else + ret = mbedtls_ecjpake_write_round_two(&ssl->handshake->ecjpake_ctx, + ssl->out_msg + header_len, + MBEDTLS_SSL_OUT_CONTENT_LEN - header_len, + &content_len, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_write_round_two", ret); + return ret; + } + + ret = mbedtls_ecjpake_derive_secret(&ssl->handshake->ecjpake_ctx, + ssl->handshake->premaster, 32, &ssl->handshake->pmslen, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_derive_secret", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */ + { + ((void) ciphersuite_info); + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ssl->out_msglen = header_len + content_len; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_CLIENT_KEY_EXCHANGE; + + ssl->state++; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write client key exchange")); + + return 0; +} + +#if !defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_certificate_verify(mbedtls_ssl_context *ssl) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate verify")); + + if ((ret = mbedtls_ssl_derive_keys(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_derive_keys", ret); + return ret; + } + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate verify")); + ssl->state++; + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} +#else /* !MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_certificate_verify(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + size_t n = 0, offset = 0; + unsigned char hash[48]; + unsigned char *hash_start = hash; + mbedtls_md_type_t md_alg = MBEDTLS_MD_NONE; + size_t hashlen; + void *rs_ctx = NULL; +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t out_buf_len = ssl->out_buf_len - (ssl->out_msg - ssl->out_buf); +#else + size_t out_buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN - (ssl->out_msg - ssl->out_buf); +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate verify")); + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled && + ssl->handshake->ecrs_state == ssl_ecrs_crt_vrfy_sign) { + goto sign; + } +#endif + + if ((ret = mbedtls_ssl_derive_keys(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_derive_keys", ret); + return ret; + } + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate verify")); + ssl->state++; + return 0; + } + + if (ssl->handshake->client_auth == 0 || + mbedtls_ssl_own_cert(ssl) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate verify")); + ssl->state++; + return 0; + } + + if (mbedtls_ssl_own_key(ssl) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no private key for certificate")); + return MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED; + } + + /* + * Make a signature of the handshake digests + */ +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled) { + ssl->handshake->ecrs_state = ssl_ecrs_crt_vrfy_sign; + } + +sign: +#endif + + ret = ssl->handshake->calc_verify(ssl, hash, &hashlen); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("calc_verify"), ret); + return ret; + } + + /* + * digitally-signed struct { + * opaque handshake_messages[handshake_messages_length]; + * }; + * + * Taking shortcut here. We assume that the server always allows the + * PRF Hash function and has sent it in the allowed signature + * algorithms list received in the Certificate Request message. + * + * Until we encounter a server that does not, we will take this + * shortcut. + * + * Reason: Otherwise we should have running hashes for SHA512 and + * SHA224 in order to satisfy 'weird' needs from the server + * side. + */ + if (ssl->handshake->ciphersuite_info->mac == MBEDTLS_MD_SHA384) { + md_alg = MBEDTLS_MD_SHA384; + ssl->out_msg[4] = MBEDTLS_SSL_HASH_SHA384; + } else { + md_alg = MBEDTLS_MD_SHA256; + ssl->out_msg[4] = MBEDTLS_SSL_HASH_SHA256; + } + ssl->out_msg[5] = mbedtls_ssl_sig_from_pk(mbedtls_ssl_own_key(ssl)); + + /* Info from md_alg will be used instead */ + hashlen = 0; + offset = 2; + +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ssl->handshake->ecrs_enabled) { + rs_ctx = &ssl->handshake->ecrs_ctx.pk; + } +#endif + + if ((ret = mbedtls_pk_sign_restartable(mbedtls_ssl_own_key(ssl), + md_alg, hash_start, hashlen, + ssl->out_msg + 6 + offset, + out_buf_len - 6 - offset, + &n, + ssl->conf->f_rng, ssl->conf->p_rng, rs_ctx)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_pk_sign", ret); +#if defined(MBEDTLS_SSL_ECP_RESTARTABLE_ENABLED) + if (ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + ret = MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS; + } +#endif + return ret; + } + + MBEDTLS_PUT_UINT16_BE(n, ssl->out_msg, offset + 4); + + ssl->out_msglen = 6 + n + offset; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_CERTIFICATE_VERIFY; + + ssl->state++; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write certificate verify")); + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_new_session_ticket(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + uint32_t lifetime; + size_t ticket_len; + unsigned char *ticket; + const unsigned char *msg; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse new session ticket")); + + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad new session ticket message")); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + /* + * struct { + * uint32 ticket_lifetime_hint; + * opaque ticket<0..2^16-1>; + * } NewSessionTicket; + * + * 0 . 3 ticket_lifetime_hint + * 4 . 5 ticket_len (n) + * 6 . 5+n ticket content + */ + if (ssl->in_msg[0] != MBEDTLS_SSL_HS_NEW_SESSION_TICKET || + ssl->in_hslen < 6 + mbedtls_ssl_hs_hdr_len(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad new session ticket message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + msg = ssl->in_msg + mbedtls_ssl_hs_hdr_len(ssl); + + lifetime = (((uint32_t) msg[0]) << 24) | (msg[1] << 16) | + (msg[2] << 8) | (msg[3]); + + ticket_len = (msg[4] << 8) | (msg[5]); + + if (ticket_len + 6 + mbedtls_ssl_hs_hdr_len(ssl) != ssl->in_hslen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad new session ticket message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket length: %" MBEDTLS_PRINTF_SIZET, ticket_len)); + + /* We're not waiting for a NewSessionTicket message any more */ + ssl->handshake->new_session_ticket = 0; + ssl->state = MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC; + + /* + * Zero-length ticket means the server changed his mind and doesn't want + * to send a ticket after all, so just forget it + */ + if (ticket_len == 0) { + return 0; + } + + if (ssl->session != NULL && ssl->session->ticket != NULL) { + mbedtls_platform_zeroize(ssl->session->ticket, + ssl->session->ticket_len); + mbedtls_free(ssl->session->ticket); + ssl->session->ticket = NULL; + ssl->session->ticket_len = 0; + } + + mbedtls_platform_zeroize(ssl->session_negotiate->ticket, + ssl->session_negotiate->ticket_len); + mbedtls_free(ssl->session_negotiate->ticket); + ssl->session_negotiate->ticket = NULL; + ssl->session_negotiate->ticket_len = 0; + + if ((ticket = mbedtls_calloc(1, ticket_len)) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("ticket alloc failed")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(ticket, msg + 6, ticket_len); + + ssl->session_negotiate->ticket = ticket; + ssl->session_negotiate->ticket_len = ticket_len; + ssl->session_negotiate->ticket_lifetime = lifetime; + + /* + * RFC 5077 section 3.4: + * "If the client receives a session ticket from the server, then it + * discards any Session ID that was sent in the ServerHello." + */ + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket in use, discarding session id")); + ssl->session_negotiate->id_len = 0; + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse new session ticket")); + + return 0; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +/* + * SSL handshake -- client side -- single step + */ +int mbedtls_ssl_handshake_client_step(mbedtls_ssl_context *ssl) +{ + int ret = 0; + + /* Change state now, so that it is right in mbedtls_ssl_read_record(), used + * by DTLS for dropping out-of-sequence ChangeCipherSpec records */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (ssl->state == MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC && + ssl->handshake->new_session_ticket != 0) { + ssl->state = MBEDTLS_SSL_NEW_SESSION_TICKET; + } +#endif + + switch (ssl->state) { + case MBEDTLS_SSL_HELLO_REQUEST: + ssl->state = MBEDTLS_SSL_CLIENT_HELLO; + break; + + /* + * ==> ClientHello + */ + case MBEDTLS_SSL_CLIENT_HELLO: + ret = mbedtls_ssl_write_client_hello(ssl); + break; + + /* + * <== ServerHello + * Certificate + * ( ServerKeyExchange ) + * ( CertificateRequest ) + * ServerHelloDone + */ + case MBEDTLS_SSL_SERVER_HELLO: + ret = ssl_parse_server_hello(ssl); + break; + + case MBEDTLS_SSL_SERVER_CERTIFICATE: + ret = mbedtls_ssl_parse_certificate(ssl); + break; + + case MBEDTLS_SSL_SERVER_KEY_EXCHANGE: + ret = ssl_parse_server_key_exchange(ssl); + break; + + case MBEDTLS_SSL_CERTIFICATE_REQUEST: + ret = ssl_parse_certificate_request(ssl); + break; + + case MBEDTLS_SSL_SERVER_HELLO_DONE: + ret = ssl_parse_server_hello_done(ssl); + break; + + /* + * ==> ( Certificate/Alert ) + * ClientKeyExchange + * ( CertificateVerify ) + * ChangeCipherSpec + * Finished + */ + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + ret = mbedtls_ssl_write_certificate(ssl); + break; + + case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE: + ret = ssl_write_client_key_exchange(ssl); + break; + + case MBEDTLS_SSL_CERTIFICATE_VERIFY: + ret = ssl_write_certificate_verify(ssl); + break; + + case MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC: + ret = mbedtls_ssl_write_change_cipher_spec(ssl); + break; + + case MBEDTLS_SSL_CLIENT_FINISHED: + ret = mbedtls_ssl_write_finished(ssl); + break; + + /* + * <== ( NewSessionTicket ) + * ChangeCipherSpec + * Finished + */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + case MBEDTLS_SSL_NEW_SESSION_TICKET: + ret = ssl_parse_new_session_ticket(ssl); + break; +#endif + + case MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC: + ret = mbedtls_ssl_parse_change_cipher_spec(ssl); + break; + + case MBEDTLS_SSL_SERVER_FINISHED: + ret = mbedtls_ssl_parse_finished(ssl); + break; + + case MBEDTLS_SSL_FLUSH_BUFFERS: + MBEDTLS_SSL_DEBUG_MSG(2, ("handshake: done")); + ssl->state = MBEDTLS_SSL_HANDSHAKE_WRAPUP; + break; + + case MBEDTLS_SSL_HANDSHAKE_WRAPUP: + mbedtls_ssl_handshake_wrapup(ssl); + break; + + default: + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid state %d", ssl->state)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + return ret; +} + +#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_PROTO_TLS1_2 */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls12_server.c b/r5dev/thirdparty/mbedtls/ssl_tls12_server.c new file mode 100644 index 00000000..631331d8 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls12_server.c @@ -0,0 +1,4341 @@ +/* + * TLS server-side functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_PROTO_TLS1_2) + +#include "mbedtls/platform.h" + +#include "mbedtls/ssl.h" +#include "ssl_misc.h" +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/platform_util.h" +#include "constant_time_internal.h" +#include "mbedtls/constant_time.h" +#include "hash_info.h" + +#include + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) +#endif + +#if defined(MBEDTLS_ECP_C) +#include "mbedtls/ecp.h" +#endif + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) +int mbedtls_ssl_set_client_transport_id(mbedtls_ssl_context *ssl, + const unsigned char *info, + size_t ilen) +{ + if (ssl->conf->endpoint != MBEDTLS_SSL_IS_SERVER) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + mbedtls_free(ssl->cli_id); + + if ((ssl->cli_id = mbedtls_calloc(1, ilen)) == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(ssl->cli_id, info, ilen); + ssl->cli_id_len = ilen; + + return 0; +} + +void mbedtls_ssl_conf_dtls_cookies(mbedtls_ssl_config *conf, + mbedtls_ssl_cookie_write_t *f_cookie_write, + mbedtls_ssl_cookie_check_t *f_cookie_check, + void *p_cookie) +{ + conf->f_cookie_write = f_cookie_write; + conf->f_cookie_check = f_cookie_check; + conf->p_cookie = p_cookie; +} +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */ + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_conf_has_psk_or_cb(mbedtls_ssl_config const *conf) +{ + if (conf->f_psk != NULL) { + return 1; + } + + if (conf->psk_identity_len == 0 || conf->psk_identity == NULL) { + return 0; + } + + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (!mbedtls_svc_key_id_is_null(conf->psk_opaque)) { + return 1; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (conf->psk != NULL && conf->psk_len != 0) { + return 1; + } + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_renegotiation_info(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + /* Check verify-data in constant-time. The length OTOH is no secret */ + if (len != 1 + ssl->verify_data_len || + buf[0] != ssl->verify_data_len || + mbedtls_ct_memcmp(buf + 1, ssl->peer_verify_data, + ssl->verify_data_len) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("non-matching renegotiation info")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + } else +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + { + if (len != 1 || buf[0] != 0x0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("non-zero length renegotiation info")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + ssl->secure_renegotiation = MBEDTLS_SSL_SECURE_RENEGOTIATION; + } + + return 0; +} + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +/* + * Function for parsing a supported groups (TLS 1.3) or supported elliptic + * curves (TLS 1.2) extension. + * + * The "extension_data" field of a supported groups extension contains a + * "NamedGroupList" value (TLS 1.3 RFC8446): + * enum { + * secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019), + * x25519(0x001D), x448(0x001E), + * ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102), + * ffdhe6144(0x0103), ffdhe8192(0x0104), + * ffdhe_private_use(0x01FC..0x01FF), + * ecdhe_private_use(0xFE00..0xFEFF), + * (0xFFFF) + * } NamedGroup; + * struct { + * NamedGroup named_group_list<2..2^16-1>; + * } NamedGroupList; + * + * The "extension_data" field of a supported elliptic curves extension contains + * a "NamedCurveList" value (TLS 1.2 RFC 8422): + * enum { + * deprecated(1..22), + * secp256r1 (23), secp384r1 (24), secp521r1 (25), + * x25519(29), x448(30), + * reserved (0xFE00..0xFEFF), + * deprecated(0xFF01..0xFF02), + * (0xFFFF) + * } NamedCurve; + * struct { + * NamedCurve named_curve_list<2..2^16-1> + * } NamedCurveList; + * + * The TLS 1.3 supported groups extension was defined to be a compatible + * generalization of the TLS 1.2 supported elliptic curves extension. They both + * share the same extension identifier. + * + * DHE groups are not supported yet. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_supported_groups_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + size_t list_size, our_size; + const unsigned char *p; + uint16_t *curves_tls_id; + + if (len < 2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + list_size = ((buf[0] << 8) | (buf[1])); + if (list_size + 2 != len || + list_size % 2 != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Should never happen unless client duplicates the extension */ + if (ssl->handshake->curves_tls_id != NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + /* Don't allow our peer to make us allocate too much memory, + * and leave room for a final 0 */ + our_size = list_size / 2 + 1; + if (our_size > MBEDTLS_ECP_DP_MAX) { + our_size = MBEDTLS_ECP_DP_MAX; + } + + if ((curves_tls_id = mbedtls_calloc(our_size, + sizeof(*curves_tls_id))) == NULL) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + ssl->handshake->curves_tls_id = curves_tls_id; + + p = buf + 2; + while (list_size > 0 && our_size > 1) { + uint16_t curr_tls_id = MBEDTLS_GET_UINT16_BE(p, 0); + + if (mbedtls_ssl_get_ecp_group_id_from_tls_id(curr_tls_id) != + MBEDTLS_ECP_DP_NONE) { + *curves_tls_id++ = curr_tls_id; + our_size--; + } + + list_size -= 2; + p += 2; + } + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_supported_point_formats(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + size_t list_size; + const unsigned char *p; + + if (len == 0 || (size_t) (buf[0] + 1) != len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + list_size = buf[0]; + + p = buf + 1; + while (list_size > 0) { + if (p[0] == MBEDTLS_ECP_PF_UNCOMPRESSED || + p[0] == MBEDTLS_ECP_PF_COMPRESSED) { +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && \ + (defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C)) + ssl->handshake->ecdh_ctx.point_format = p[0]; +#endif /* !MBEDTLS_USE_PSA_CRYPTO && + ( MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C ) */ +#if !defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + mbedtls_ecjpake_set_point_format(&ssl->handshake->ecjpake_ctx, + p[0]); +#endif /* !MBEDTLS_USE_PSA_CRYPTO && MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + MBEDTLS_SSL_DEBUG_MSG(4, ("point format selected: %d", p[0])); + return 0; + } + + list_size--; + p++; + } + + return 0; +} +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || + MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_ecjpake_kkpp(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (ssl->handshake->psa_pake_ctx_is_ok != 1) +#else + if (mbedtls_ecjpake_check(&ssl->handshake->ecjpake_ctx) != 0) +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + { + MBEDTLS_SSL_DEBUG_MSG(3, ("skip ecjpake kkpp extension")); + return 0; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((ret = mbedtls_psa_ecjpake_read_round( + &ssl->handshake->psa_pake_ctx, buf, len, + MBEDTLS_ECJPAKE_ROUND_ONE)) != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_input round one", ret); + mbedtls_ssl_send_alert_message( + ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + + return ret; + } +#else + if ((ret = mbedtls_ecjpake_read_round_one(&ssl->handshake->ecjpake_ctx, + buf, len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_read_round_one", ret); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* Only mark the extension as OK when we're sure it is */ + ssl->handshake->cli_exts |= MBEDTLS_TLS_EXT_ECJPAKE_KKPP_OK; + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_max_fragment_length_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + if (len != 1 || buf[0] >= MBEDTLS_SSL_MAX_FRAG_LEN_INVALID) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + ssl->session_negotiate->mfl_code = buf[0]; + + return 0; +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_cid_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + size_t peer_cid_len; + + /* CID extension only makes sense in DTLS */ + if (ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + /* + * struct { + * opaque cid<0..2^8-1>; + * } ConnectionId; + */ + + if (len < 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + peer_cid_len = *buf++; + len--; + + if (len != peer_cid_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Ignore CID if the user has disabled its use. */ + if (ssl->negotiate_cid == MBEDTLS_SSL_CID_DISABLED) { + /* Leave ssl->handshake->cid_in_use in its default + * value of MBEDTLS_SSL_CID_DISABLED. */ + MBEDTLS_SSL_DEBUG_MSG(3, ("Client sent CID extension, but CID disabled")); + return 0; + } + + if (peer_cid_len > MBEDTLS_SSL_CID_OUT_LEN_MAX) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + ssl->handshake->cid_in_use = MBEDTLS_SSL_CID_ENABLED; + ssl->handshake->peer_cid_len = (uint8_t) peer_cid_len; + memcpy(ssl->handshake->peer_cid, buf, peer_cid_len); + + MBEDTLS_SSL_DEBUG_MSG(3, ("Use of CID extension negotiated")); + MBEDTLS_SSL_DEBUG_BUF(3, "Client CID", buf, peer_cid_len); + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_encrypt_then_mac_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + if (len != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ((void) buf); + + if (ssl->conf->encrypt_then_mac == MBEDTLS_SSL_ETM_ENABLED) { + ssl->session_negotiate->encrypt_then_mac = MBEDTLS_SSL_ETM_ENABLED; + } + + return 0; +} +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_extended_ms_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + if (len != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ((void) buf); + + if (ssl->conf->extended_ms == MBEDTLS_SSL_EXTENDED_MS_ENABLED) { + ssl->handshake->extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED; + } + + return 0; +} +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_session_ticket_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_session session; + + mbedtls_ssl_session_init(&session); + + if (ssl->conf->f_ticket_parse == NULL || + ssl->conf->f_ticket_write == NULL) { + return 0; + } + + /* Remember the client asked us to send a new ticket */ + ssl->handshake->new_session_ticket = 1; + + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket length: %" MBEDTLS_PRINTF_SIZET, len)); + + if (len == 0) { + return 0; + } + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket rejected: renegotiating")); + return 0; + } +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + + /* + * Failures are ok: just ignore the ticket and proceed. + */ + if ((ret = ssl->conf->f_ticket_parse(ssl->conf->p_ticket, &session, + buf, len)) != 0) { + mbedtls_ssl_session_free(&session); + + if (ret == MBEDTLS_ERR_SSL_INVALID_MAC) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket is not authentic")); + } else if (ret == MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket is expired")); + } else { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_ticket_parse", ret); + } + + return 0; + } + + /* + * Keep the session ID sent by the client, since we MUST send it back to + * inform them we're accepting the ticket (RFC 5077 section 3.4) + */ + session.id_len = ssl->session_negotiate->id_len; + memcpy(&session.id, ssl->session_negotiate->id, session.id_len); + + mbedtls_ssl_session_free(ssl->session_negotiate); + memcpy(ssl->session_negotiate, &session, sizeof(mbedtls_ssl_session)); + + /* Zeroize instead of free as we copied the content */ + mbedtls_platform_zeroize(&session, sizeof(mbedtls_ssl_session)); + + MBEDTLS_SSL_DEBUG_MSG(3, ("session successfully restored from ticket")); + + ssl->handshake->resume = 1; + + /* Don't send a new ticket after all, this one is OK */ + ssl->handshake->new_session_ticket = 0; + + return 0; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_use_srtp_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t len) +{ + mbedtls_ssl_srtp_profile client_protection = MBEDTLS_TLS_SRTP_UNSET; + size_t i, j; + size_t profile_length; + uint16_t mki_length; + /*! 2 bytes for profile length and 1 byte for mki len */ + const size_t size_of_lengths = 3; + + /* If use_srtp is not configured, just ignore the extension */ + if ((ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) || + (ssl->conf->dtls_srtp_profile_list == NULL) || + (ssl->conf->dtls_srtp_profile_list_len == 0)) { + return 0; + } + + /* RFC5764 section 4.1.1 + * uint8 SRTPProtectionProfile[2]; + * + * struct { + * SRTPProtectionProfiles SRTPProtectionProfiles; + * opaque srtp_mki<0..255>; + * } UseSRTPData; + + * SRTPProtectionProfile SRTPProtectionProfiles<2..2^16-1>; + */ + + /* + * Min length is 5: at least one protection profile(2 bytes) + * and length(2 bytes) + srtp_mki length(1 byte) + * Check here that we have at least 2 bytes of protection profiles length + * and one of srtp_mki length + */ + if (len < size_of_lengths) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ssl->dtls_srtp_info.chosen_dtls_srtp_profile = MBEDTLS_TLS_SRTP_UNSET; + + /* first 2 bytes are protection profile length(in bytes) */ + profile_length = (buf[0] << 8) | buf[1]; + buf += 2; + + /* The profile length cannot be bigger than input buffer size - lengths fields */ + if (profile_length > len - size_of_lengths || + profile_length % 2 != 0) { /* profiles are 2 bytes long, so the length must be even */ + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + /* + * parse the extension list values are defined in + * http://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml + */ + for (j = 0; j < profile_length; j += 2) { + uint16_t protection_profile_value = buf[j] << 8 | buf[j + 1]; + client_protection = mbedtls_ssl_check_srtp_profile_value(protection_profile_value); + + if (client_protection != MBEDTLS_TLS_SRTP_UNSET) { + MBEDTLS_SSL_DEBUG_MSG(3, ("found srtp profile: %s", + mbedtls_ssl_get_srtp_profile_as_string( + client_protection))); + } else { + continue; + } + /* check if suggested profile is in our list */ + for (i = 0; i < ssl->conf->dtls_srtp_profile_list_len; i++) { + if (client_protection == ssl->conf->dtls_srtp_profile_list[i]) { + ssl->dtls_srtp_info.chosen_dtls_srtp_profile = ssl->conf->dtls_srtp_profile_list[i]; + MBEDTLS_SSL_DEBUG_MSG(3, ("selected srtp profile: %s", + mbedtls_ssl_get_srtp_profile_as_string( + client_protection))); + break; + } + } + if (ssl->dtls_srtp_info.chosen_dtls_srtp_profile != MBEDTLS_TLS_SRTP_UNSET) { + break; + } + } + buf += profile_length; /* buf points to the mki length */ + mki_length = *buf; + buf++; + + if (mki_length > MBEDTLS_TLS_SRTP_MAX_MKI_LENGTH || + mki_length + profile_length + size_of_lengths != len) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Parse the mki only if present and mki is supported locally */ + if (ssl->conf->dtls_srtp_mki_support == MBEDTLS_SSL_DTLS_SRTP_MKI_SUPPORTED && + mki_length > 0) { + ssl->dtls_srtp_info.mki_len = mki_length; + + memcpy(ssl->dtls_srtp_info.mki_value, buf, mki_length); + + MBEDTLS_SSL_DEBUG_BUF(3, "using mki", ssl->dtls_srtp_info.mki_value, + ssl->dtls_srtp_info.mki_len); + } + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + +/* + * Auxiliary functions for ServerHello parsing and related actions + */ + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +/* + * Return 0 if the given key uses one of the acceptable curves, -1 otherwise + */ +#if defined(MBEDTLS_ECDSA_C) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_check_key_curve(mbedtls_pk_context *pk, + uint16_t *curves_tls_id) +{ + uint16_t *curr_tls_id = curves_tls_id; + mbedtls_ecp_group_id grp_id = mbedtls_pk_ec(*pk)->grp.id; + mbedtls_ecp_group_id curr_grp_id; + + while (*curr_tls_id != 0) { + curr_grp_id = mbedtls_ssl_get_ecp_group_id_from_tls_id(*curr_tls_id); + if (curr_grp_id == grp_id) { + return 0; + } + curr_tls_id++; + } + + return -1; +} +#endif /* MBEDTLS_ECDSA_C */ + +/* + * Try picking a certificate for this ciphersuite, + * return 0 on success and -1 on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_pick_cert(mbedtls_ssl_context *ssl, + const mbedtls_ssl_ciphersuite_t *ciphersuite_info) +{ + mbedtls_ssl_key_cert *cur, *list; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_algorithm_t pk_alg = + mbedtls_ssl_get_ciphersuite_sig_pk_psa_alg(ciphersuite_info); + psa_key_usage_t pk_usage = + mbedtls_ssl_get_ciphersuite_sig_pk_psa_usage(ciphersuite_info); +#else + mbedtls_pk_type_t pk_alg = + mbedtls_ssl_get_ciphersuite_sig_pk_alg(ciphersuite_info); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + uint32_t flags; + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_key_cert != NULL) { + list = ssl->handshake->sni_key_cert; + } else +#endif + list = ssl->conf->key_cert; + + int pk_alg_is_none = 0; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + pk_alg_is_none = (pk_alg == PSA_ALG_NONE); +#else + pk_alg_is_none = (pk_alg == MBEDTLS_PK_NONE); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (pk_alg_is_none) { + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite requires certificate")); + + if (list == NULL) { + MBEDTLS_SSL_DEBUG_MSG(3, ("server has no certificate")); + return -1; + } + + for (cur = list; cur != NULL; cur = cur->next) { + flags = 0; + MBEDTLS_SSL_DEBUG_CRT(3, "candidate certificate chain, certificate", + cur->cert); + + int key_type_matches = 0; +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + key_type_matches = ((ssl->conf->f_async_sign_start != NULL || + ssl->conf->f_async_decrypt_start != NULL || + mbedtls_pk_can_do_ext(cur->key, pk_alg, pk_usage)) && + mbedtls_pk_can_do_ext(&cur->cert->pk, pk_alg, pk_usage)); +#else + key_type_matches = ( + mbedtls_pk_can_do_ext(cur->key, pk_alg, pk_usage)); +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ +#else + key_type_matches = mbedtls_pk_can_do(&cur->cert->pk, pk_alg); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + if (!key_type_matches) { + MBEDTLS_SSL_DEBUG_MSG(3, ("certificate mismatch: key type")); + continue; + } + + /* + * This avoids sending the client a cert it'll reject based on + * keyUsage or other extensions. + * + * It also allows the user to provision different certificates for + * different uses based on keyUsage, eg if they want to avoid signing + * and decrypting with the same RSA key. + */ + if (mbedtls_ssl_check_cert_usage(cur->cert, ciphersuite_info, + MBEDTLS_SSL_IS_SERVER, &flags) != 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("certificate mismatch: " + "(extended) key usage extension")); + continue; + } + +#if defined(MBEDTLS_ECDSA_C) + if (pk_alg == MBEDTLS_PK_ECDSA && + ssl_check_key_curve(&cur->cert->pk, + ssl->handshake->curves_tls_id) != 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("certificate mismatch: elliptic curve")); + continue; + } +#endif + + /* If we get there, we got a winner */ + break; + } + + /* Do not update ssl->handshake->key_cert unless there is a match */ + if (cur != NULL) { + ssl->handshake->key_cert = cur; + MBEDTLS_SSL_DEBUG_CRT(3, "selected certificate chain, certificate", + ssl->handshake->key_cert->cert); + return 0; + } + + return -1; +} +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + +/* + * Check if a given ciphersuite is suitable for use with our config/keys/etc + * Sets ciphersuite_info only if the suite matches. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_ciphersuite_match(mbedtls_ssl_context *ssl, int suite_id, + const mbedtls_ssl_ciphersuite_t **ciphersuite_info) +{ + const mbedtls_ssl_ciphersuite_t *suite_info; + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + mbedtls_pk_type_t sig_type; +#endif + + suite_info = mbedtls_ssl_ciphersuite_from_id(suite_id); + if (suite_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("trying ciphersuite: %#04x (%s)", + (unsigned int) suite_id, suite_info->name)); + + if (suite_info->min_tls_version > ssl->tls_version || + suite_info->max_tls_version < ssl->tls_version) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite mismatch: version")); + return 0; + } + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if (suite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE && + (ssl->handshake->cli_exts & MBEDTLS_TLS_EXT_ECJPAKE_KKPP_OK) == 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite mismatch: ecjpake " + "not configured or ext missing")); + return 0; + } +#endif + + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) + if (mbedtls_ssl_ciphersuite_uses_ec(suite_info) && + (ssl->handshake->curves_tls_id == NULL || + ssl->handshake->curves_tls_id[0] == 0)) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite mismatch: " + "no common elliptic curve")); + return 0; + } +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) + /* If the ciphersuite requires a pre-shared key and we don't + * have one, skip it now rather than failing later */ + if (mbedtls_ssl_ciphersuite_uses_psk(suite_info) && + ssl_conf_has_psk_or_cb(ssl->conf) == 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite mismatch: no pre-shared key")); + return 0; + } +#endif + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + /* + * Final check: if ciphersuite requires us to have a + * certificate/key of a particular type: + * - select the appropriate certificate if we have one, or + * - try the next ciphersuite if we don't + * This must be done last since we modify the key_cert list. + */ + if (ssl_pick_cert(ssl, suite_info) != 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite mismatch: " + "no suitable certificate")); + return 0; + } +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + /* If the ciphersuite requires signing, check whether + * a suitable hash algorithm is present. */ + sig_type = mbedtls_ssl_get_ciphersuite_sig_alg(suite_info); + if (sig_type != MBEDTLS_PK_NONE && + mbedtls_ssl_tls12_get_preferred_hash_for_sig_alg( + ssl, mbedtls_ssl_sig_from_pk_alg(sig_type)) == MBEDTLS_SSL_HASH_NONE) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ciphersuite mismatch: no suitable hash algorithm " + "for signature algorithm %u", (unsigned) sig_type)); + return 0; + } + +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ + + *ciphersuite_info = suite_info; + return 0; +} + +/* This function doesn't alert on errors that happen early during + ClientHello parsing because they might indicate that the client is + not talking SSL/TLS at all and would not understand our alert. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_client_hello(mbedtls_ssl_context *ssl) +{ + int ret, got_common_suite; + size_t i, j; + size_t ciph_offset, comp_offset, ext_offset; + size_t msg_len, ciph_len, sess_len, comp_len, ext_len; +#if defined(MBEDTLS_SSL_PROTO_DTLS) + size_t cookie_offset, cookie_len; +#endif + unsigned char *buf, *p, *ext; +#if defined(MBEDTLS_SSL_RENEGOTIATION) + int renegotiation_info_seen = 0; +#endif + int handshake_failure = 0; + const int *ciphersuites; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + + /* If there is no signature-algorithm extension present, + * we need to fall back to the default values for allowed + * signature-hash pairs. */ +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + int sig_hash_alg_ext_present = 0; +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse client hello")); + + int renegotiating; + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) +read_record_header: +#endif + /* + * If renegotiating, then the input was read with mbedtls_ssl_read_record(), + * otherwise read it ourselves manually in order to support SSLv2 + * ClientHello, which doesn't use the same record layer format. + */ + renegotiating = 0; +#if defined(MBEDTLS_SSL_RENEGOTIATION) + renegotiating = (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE); +#endif + if (!renegotiating) { + if ((ret = mbedtls_ssl_fetch_input(ssl, 5)) != 0) { + /* No alert on a read error. */ + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); + return ret; + } + } + + buf = ssl->in_hdr; + + MBEDTLS_SSL_DEBUG_BUF(4, "record header", buf, mbedtls_ssl_in_hdr_len(ssl)); + + /* + * TLS Client Hello + * + * Record layer: + * 0 . 0 message type + * 1 . 2 protocol version + * 3 . 11 DTLS: epoch + record sequence number + * 3 . 4 message length + */ + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message type: %d", + buf[0])); + + if (buf[0] != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, message len.: %d", + (ssl->in_len[0] << 8) | ssl->in_len[1])); + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, protocol version: [%d:%d]", + buf[1], buf[2])); + + /* For DTLS if this is the initial handshake, remember the client sequence + * number to use it in our next message (RFC 6347 4.2.1) */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM +#if defined(MBEDTLS_SSL_RENEGOTIATION) + && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE +#endif + ) { + /* Epoch should be 0 for initial handshakes */ + if (ssl->in_ctr[0] != 0 || ssl->in_ctr[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + memcpy(&ssl->cur_out_ctr[2], ssl->in_ctr + 2, + sizeof(ssl->cur_out_ctr) - 2); + +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + if (mbedtls_ssl_dtls_replay_check(ssl) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("replayed record, discarding")); + ssl->next_record_offset = 0; + ssl->in_left = 0; + goto read_record_header; + } + + /* No MAC to check yet, so we can update right now */ + mbedtls_ssl_dtls_replay_update(ssl); +#endif + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + msg_len = (ssl->in_len[0] << 8) | ssl->in_len[1]; + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + /* Set by mbedtls_ssl_read_record() */ + msg_len = ssl->in_hslen; + } else +#endif + { + if (msg_len > MBEDTLS_SSL_IN_CONTENT_LEN) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + if ((ret = mbedtls_ssl_fetch_input(ssl, + mbedtls_ssl_in_hdr_len(ssl) + msg_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_fetch_input", ret); + return ret; + } + + /* Done reading this record, get ready for the next one */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + ssl->next_record_offset = msg_len + mbedtls_ssl_in_hdr_len(ssl); + } else +#endif + ssl->in_left = 0; + } + + buf = ssl->in_msg; + + MBEDTLS_SSL_DEBUG_BUF(4, "record contents", buf, msg_len); + + ret = ssl->handshake->update_checksum(ssl, buf, msg_len); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret); + return ret; + } + + /* + * Handshake layer: + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 5 DTLS only: message sequence number + * 6 . 8 DTLS only: fragment offset + * 9 . 11 DTLS only: fragment length + */ + if (msg_len < mbedtls_ssl_hs_hdr_len(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello v3, handshake type: %d", buf[0])); + + if (buf[0] != MBEDTLS_SSL_HS_CLIENT_HELLO) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + { + size_t handshake_len = MBEDTLS_GET_UINT24_BE(buf, 1); + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello v3, handshake len.: %u", + (unsigned) handshake_len)); + + /* The record layer has a record size limit of 2^14 - 1 and + * fragmentation is not supported, so buf[1] should be zero. */ + if (buf[1] != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message: %u != 0", + (unsigned) buf[1])); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* We don't support fragmentation of ClientHello (yet?) */ + if (msg_len != mbedtls_ssl_hs_hdr_len(ssl) + handshake_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message: %u != %u + %u", + (unsigned) msg_len, + (unsigned) mbedtls_ssl_hs_hdr_len(ssl), + (unsigned) handshake_len)); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + /* + * Copy the client's handshake message_seq on initial handshakes, + * check sequence number on renego. + */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { + /* This couldn't be done in ssl_prepare_handshake_record() */ + unsigned int cli_msg_seq = (ssl->in_msg[4] << 8) | + ssl->in_msg[5]; + + if (cli_msg_seq != ssl->handshake->in_msg_seq) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message_seq: " + "%u (expected %u)", cli_msg_seq, + ssl->handshake->in_msg_seq)); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ssl->handshake->in_msg_seq++; + } else +#endif + { + unsigned int cli_msg_seq = (ssl->in_msg[4] << 8) | + ssl->in_msg[5]; + ssl->handshake->out_msg_seq = cli_msg_seq; + ssl->handshake->in_msg_seq = cli_msg_seq + 1; + } + { + /* + * For now we don't support fragmentation, so make sure + * fragment_offset == 0 and fragment_length == length + */ + size_t fragment_offset, fragment_length, length; + fragment_offset = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 6); + fragment_length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 9); + length = MBEDTLS_GET_UINT24_BE(ssl->in_msg, 1); + MBEDTLS_SSL_DEBUG_MSG( + 4, ("fragment_offset=%u fragment_length=%u length=%u", + (unsigned) fragment_offset, (unsigned) fragment_length, + (unsigned) length)); + if (fragment_offset != 0 || length != fragment_length) { + MBEDTLS_SSL_DEBUG_MSG(1, ("ClientHello fragmentation not supported")); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + } + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + buf += mbedtls_ssl_hs_hdr_len(ssl); + msg_len -= mbedtls_ssl_hs_hdr_len(ssl); + + /* + * ClientHello layer: + * 0 . 1 protocol version + * 2 . 33 random bytes (starting with 4 bytes of Unix time) + * 34 . 35 session id length (1 byte) + * 35 . 34+x session id + * 35+x . 35+x DTLS only: cookie length (1 byte) + * 36+x . .. DTLS only: cookie + * .. . .. ciphersuite list length (2 bytes) + * .. . .. ciphersuite list + * .. . .. compression alg. list length (1 byte) + * .. . .. compression alg. list + * .. . .. extensions length (2 bytes, optional) + * .. . .. extensions (optional) + */ + + /* + * Minimal length (with everything empty and extensions omitted) is + * 2 + 32 + 1 + 2 + 1 = 38 bytes. Check that first, so that we can + * read at least up to session id length without worrying. + */ + if (msg_len < 38) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* + * Check and save the protocol version + */ + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, version", buf, 2); + + ssl->tls_version = mbedtls_ssl_read_version(buf, ssl->conf->transport); + ssl->session_negotiate->tls_version = ssl->tls_version; + + if (ssl->tls_version != MBEDTLS_SSL_VERSION_TLS1_2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("server only supports TLS 1.2")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION); + return MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + } + + /* + * Save client random (inc. Unix time) + */ + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, random bytes", buf + 2, 32); + + memcpy(ssl->handshake->randbytes, buf + 2, 32); + + /* + * Check the session ID length and save session ID + */ + sess_len = buf[34]; + + if (sess_len > sizeof(ssl->session_negotiate->id) || + sess_len + 34 + 2 > msg_len) { /* 2 for cipherlist length field */ + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, session id", buf + 35, sess_len); + + ssl->session_negotiate->id_len = sess_len; + memset(ssl->session_negotiate->id, 0, + sizeof(ssl->session_negotiate->id)); + memcpy(ssl->session_negotiate->id, buf + 35, + ssl->session_negotiate->id_len); + + /* + * Check the cookie length and content + */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + cookie_offset = 35 + sess_len; + cookie_len = buf[cookie_offset]; + + if (cookie_offset + 1 + cookie_len + 2 > msg_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, cookie", + buf + cookie_offset + 1, cookie_len); + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) + if (ssl->conf->f_cookie_check != NULL +#if defined(MBEDTLS_SSL_RENEGOTIATION) + && ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE +#endif + ) { + if (ssl->conf->f_cookie_check(ssl->conf->p_cookie, + buf + cookie_offset + 1, cookie_len, + ssl->cli_id, ssl->cli_id_len) != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("cookie verification failed")); + ssl->handshake->cookie_verify_result = 1; + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("cookie verification passed")); + ssl->handshake->cookie_verify_result = 0; + } + } else +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */ + { + /* We know we didn't send a cookie, so it should be empty */ + if (cookie_len != 0) { + /* This may be an attacker's probe, so don't send an alert */ + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("cookie verification skipped")); + } + + /* + * Check the ciphersuitelist length (will be parsed later) + */ + ciph_offset = cookie_offset + 1 + cookie_len; + } else +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + ciph_offset = 35 + sess_len; + + ciph_len = (buf[ciph_offset + 0] << 8) + | (buf[ciph_offset + 1]); + + if (ciph_len < 2 || + ciph_len + 2 + ciph_offset + 1 > msg_len || /* 1 for comp. alg. len */ + (ciph_len % 2) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, ciphersuitelist", + buf + ciph_offset + 2, ciph_len); + + /* + * Check the compression algorithm's length. + * The list contents are ignored because implementing + * MBEDTLS_SSL_COMPRESS_NULL is mandatory and is the only + * option supported by Mbed TLS. + */ + comp_offset = ciph_offset + 2 + ciph_len; + + comp_len = buf[comp_offset]; + + if (comp_len < 1 || + comp_len > 16 || + comp_len + comp_offset + 1 > msg_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, compression", + buf + comp_offset + 1, comp_len); + + /* + * Check the extension length + */ + ext_offset = comp_offset + 1 + comp_len; + if (msg_len > ext_offset) { + if (msg_len < ext_offset + 2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ext_len = (buf[ext_offset + 0] << 8) + | (buf[ext_offset + 1]); + + if (msg_len != ext_offset + 2 + ext_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + } else { + ext_len = 0; + } + + ext = buf + ext_offset + 2; + MBEDTLS_SSL_DEBUG_BUF(3, "client hello extensions", ext, ext_len); + + while (ext_len != 0) { + unsigned int ext_id; + unsigned int ext_size; + if (ext_len < 4) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + ext_id = ((ext[0] << 8) | (ext[1])); + ext_size = ((ext[2] << 8) | (ext[3])); + + if (ext_size + 4 > ext_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + switch (ext_id) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + case MBEDTLS_TLS_EXT_SERVERNAME: + MBEDTLS_SSL_DEBUG_MSG(3, ("found ServerName extension")); + ret = mbedtls_ssl_parse_server_name_ext(ssl, ext + 4, + ext + 4 + ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + + case MBEDTLS_TLS_EXT_RENEGOTIATION_INFO: + MBEDTLS_SSL_DEBUG_MSG(3, ("found renegotiation extension")); +#if defined(MBEDTLS_SSL_RENEGOTIATION) + renegotiation_info_seen = 1; +#endif + + ret = ssl_parse_renegotiation_info(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + case MBEDTLS_TLS_EXT_SIG_ALG: + MBEDTLS_SSL_DEBUG_MSG(3, ("found signature_algorithms extension")); + + ret = mbedtls_ssl_parse_sig_alg_ext(ssl, ext + 4, ext + 4 + ext_size); + if (ret != 0) { + return ret; + } + + sig_hash_alg_ext_present = 1; + break; +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + case MBEDTLS_TLS_EXT_SUPPORTED_GROUPS: + MBEDTLS_SSL_DEBUG_MSG(3, ("found supported elliptic curves extension")); + + ret = ssl_parse_supported_groups_ext(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; + + case MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS: + MBEDTLS_SSL_DEBUG_MSG(3, ("found supported point formats extension")); + ssl->handshake->cli_exts |= MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT; + + ret = ssl_parse_supported_point_formats(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || + MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + case MBEDTLS_TLS_EXT_ECJPAKE_KKPP: + MBEDTLS_SSL_DEBUG_MSG(3, ("found ecjpake kkpp extension")); + + ret = ssl_parse_ecjpake_kkpp(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + case MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH: + MBEDTLS_SSL_DEBUG_MSG(3, ("found max fragment length extension")); + + ret = ssl_parse_max_fragment_length_ext(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + case MBEDTLS_TLS_EXT_CID: + MBEDTLS_SSL_DEBUG_MSG(3, ("found CID extension")); + + ret = ssl_parse_cid_ext(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + case MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC: + MBEDTLS_SSL_DEBUG_MSG(3, ("found encrypt then mac extension")); + + ret = ssl_parse_encrypt_then_mac_ext(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + case MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET: + MBEDTLS_SSL_DEBUG_MSG(3, ("found extended master secret extension")); + + ret = ssl_parse_extended_ms_ext(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + case MBEDTLS_TLS_EXT_SESSION_TICKET: + MBEDTLS_SSL_DEBUG_MSG(3, ("found session ticket extension")); + + ret = ssl_parse_session_ticket_ext(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_ALPN) + case MBEDTLS_TLS_EXT_ALPN: + MBEDTLS_SSL_DEBUG_MSG(3, ("found alpn extension")); + + ret = mbedtls_ssl_parse_alpn_ext(ssl, ext + 4, + ext + 4 + ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + case MBEDTLS_TLS_EXT_USE_SRTP: + MBEDTLS_SSL_DEBUG_MSG(3, ("found use_srtp extension")); + + ret = ssl_parse_use_srtp_ext(ssl, ext + 4, ext_size); + if (ret != 0) { + return ret; + } + break; +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + + default: + MBEDTLS_SSL_DEBUG_MSG(3, ("unknown extension found: %u (ignoring)", + ext_id)); + } + + ext_len -= 4 + ext_size; + ext += 4 + ext_size; + } + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + + /* + * Try to fall back to default hash SHA1 if the client + * hasn't provided any preferred signature-hash combinations. + */ + if (!sig_hash_alg_ext_present) { + uint16_t *received_sig_algs = ssl->handshake->received_sig_algs; + const uint16_t default_sig_algs[] = { +#if defined(MBEDTLS_ECDSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_ECDSA, + MBEDTLS_SSL_HASH_SHA1), +#endif +#if defined(MBEDTLS_RSA_C) + MBEDTLS_SSL_TLS12_SIG_AND_HASH_ALG(MBEDTLS_SSL_SIG_RSA, + MBEDTLS_SSL_HASH_SHA1), +#endif + MBEDTLS_TLS_SIG_NONE + }; + + MBEDTLS_STATIC_ASSERT(sizeof(default_sig_algs) / sizeof(default_sig_algs[0]) + <= MBEDTLS_RECEIVED_SIG_ALGS_SIZE, + "default_sig_algs is too big"); + + memcpy(received_sig_algs, default_sig_algs, sizeof(default_sig_algs)); + } + +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED */ + + /* + * Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV + */ + for (i = 0, p = buf + ciph_offset + 2; i < ciph_len; i += 2, p += 2) { + if (p[0] == 0 && p[1] == MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO) { + MBEDTLS_SSL_DEBUG_MSG(3, ("received TLS_EMPTY_RENEGOTIATION_INFO ")); +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS) { + MBEDTLS_SSL_DEBUG_MSG(1, ("received RENEGOTIATION SCSV " + "during renegotiation")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } +#endif + ssl->secure_renegotiation = MBEDTLS_SSL_SECURE_RENEGOTIATION; + break; + } + } + + /* + * Renegotiation security checks + */ + if (ssl->secure_renegotiation != MBEDTLS_SSL_SECURE_RENEGOTIATION && + ssl->conf->allow_legacy_renegotiation == MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("legacy renegotiation, breaking off handshake")); + handshake_failure = 1; + } +#if defined(MBEDTLS_SSL_RENEGOTIATION) + else if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS && + ssl->secure_renegotiation == MBEDTLS_SSL_SECURE_RENEGOTIATION && + renegotiation_info_seen == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("renegotiation_info extension missing (secure)")); + handshake_failure = 1; + } else if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS && + ssl->secure_renegotiation == MBEDTLS_SSL_LEGACY_RENEGOTIATION && + ssl->conf->allow_legacy_renegotiation == MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION) { + MBEDTLS_SSL_DEBUG_MSG(1, ("legacy renegotiation not allowed")); + handshake_failure = 1; + } else if (ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS && + ssl->secure_renegotiation == MBEDTLS_SSL_LEGACY_RENEGOTIATION && + renegotiation_info_seen == 1) { + MBEDTLS_SSL_DEBUG_MSG(1, ("renegotiation_info extension present (legacy)")); + handshake_failure = 1; + } +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + + if (handshake_failure == 1) { + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + /* + * Server certification selection (after processing TLS extensions) + */ + if (ssl->conf->f_cert_cb && (ret = ssl->conf->f_cert_cb(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "f_cert_cb", ret); + return ret; + } +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + ssl->handshake->sni_name = NULL; + ssl->handshake->sni_name_len = 0; +#endif + + /* + * Search for a matching ciphersuite + * (At the end because we need information from the EC-based extensions + * and certificate from the SNI callback triggered by the SNI extension + * or certificate from server certificate selection callback.) + */ + got_common_suite = 0; + ciphersuites = ssl->conf->ciphersuite_list; + ciphersuite_info = NULL; + + if (ssl->conf->respect_cli_pref == MBEDTLS_SSL_SRV_CIPHERSUITE_ORDER_CLIENT) { + for (j = 0, p = buf + ciph_offset + 2; j < ciph_len; j += 2, p += 2) { + for (i = 0; ciphersuites[i] != 0; i++) { + if (MBEDTLS_GET_UINT16_BE(p, 0) != ciphersuites[i]) { + continue; + } + + got_common_suite = 1; + + if ((ret = ssl_ciphersuite_match(ssl, ciphersuites[i], + &ciphersuite_info)) != 0) { + return ret; + } + + if (ciphersuite_info != NULL) { + goto have_ciphersuite; + } + } + } + } else { + for (i = 0; ciphersuites[i] != 0; i++) { + for (j = 0, p = buf + ciph_offset + 2; j < ciph_len; j += 2, p += 2) { + if (MBEDTLS_GET_UINT16_BE(p, 0) != ciphersuites[i]) { + continue; + } + + got_common_suite = 1; + + if ((ret = ssl_ciphersuite_match(ssl, ciphersuites[i], + &ciphersuite_info)) != 0) { + return ret; + } + + if (ciphersuite_info != NULL) { + goto have_ciphersuite; + } + } + } + } + + if (got_common_suite) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got ciphersuites in common, " + "but none of them usable")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no ciphersuites in common")); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + +have_ciphersuite: + MBEDTLS_SSL_DEBUG_MSG(2, ("selected ciphersuite: %s", ciphersuite_info->name)); + + ssl->session_negotiate->ciphersuite = ciphersuites[i]; + ssl->handshake->ciphersuite_info = ciphersuite_info; + + ssl->state++; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ssl_recv_flight_completed(ssl); + } +#endif + + /* Debugging-only output for testsuite */ +#if defined(MBEDTLS_DEBUG_C) && \ + defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) + mbedtls_pk_type_t sig_alg = mbedtls_ssl_get_ciphersuite_sig_alg(ciphersuite_info); + if (sig_alg != MBEDTLS_PK_NONE) { + unsigned int sig_hash = mbedtls_ssl_tls12_get_preferred_hash_for_sig_alg( + ssl, mbedtls_ssl_sig_from_pk_alg(sig_alg)); + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello v3, signature_algorithm ext: %u", + sig_hash)); + } else { + MBEDTLS_SSL_DEBUG_MSG(3, ("no hash algorithm for signature algorithm " + "%u - should not happen", (unsigned) sig_alg)); + } +#endif + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse client hello")); + + return 0; +} + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) +static void ssl_write_cid_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + unsigned char *p = buf; + size_t ext_len; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; + + *olen = 0; + + /* Skip writing the extension if we don't want to use it or if + * the client hasn't offered it. */ + if (ssl->handshake->cid_in_use == MBEDTLS_SSL_CID_DISABLED) { + return; + } + + /* ssl->own_cid_len is at most MBEDTLS_SSL_CID_IN_LEN_MAX + * which is at most 255, so the increment cannot overflow. */ + if (end < p || (size_t) (end - p) < (unsigned) (ssl->own_cid_len + 5)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("buffer too small")); + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, adding CID extension")); + + /* + * struct { + * opaque cid<0..2^8-1>; + * } ConnectionId; + */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_CID, p, 0); + p += 2; + ext_len = (size_t) ssl->own_cid_len + 1; + MBEDTLS_PUT_UINT16_BE(ext_len, p, 0); + p += 2; + + *p++ = (uint8_t) ssl->own_cid_len; + memcpy(p, ssl->own_cid, ssl->own_cid_len); + + *olen = ssl->own_cid_len + 5; +} +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) +static void ssl_write_encrypt_then_mac_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + unsigned char *p = buf; + const mbedtls_ssl_ciphersuite_t *suite = NULL; + + /* + * RFC 7366: "If a server receives an encrypt-then-MAC request extension + * from a client and then selects a stream or Authenticated Encryption + * with Associated Data (AEAD) ciphersuite, it MUST NOT send an + * encrypt-then-MAC response extension back to the client." + */ + suite = mbedtls_ssl_ciphersuite_from_id( + ssl->session_negotiate->ciphersuite); + if (suite == NULL) { + ssl->session_negotiate->encrypt_then_mac = MBEDTLS_SSL_ETM_DISABLED; + } else { + mbedtls_ssl_mode_t ssl_mode = + mbedtls_ssl_get_mode_from_ciphersuite( + ssl->session_negotiate->encrypt_then_mac, + suite); + + if (ssl_mode != MBEDTLS_SSL_MODE_CBC_ETM) { + ssl->session_negotiate->encrypt_then_mac = MBEDTLS_SSL_ETM_DISABLED; + } + } + + if (ssl->session_negotiate->encrypt_then_mac == MBEDTLS_SSL_ETM_DISABLED) { + *olen = 0; + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, adding encrypt then mac extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_ENCRYPT_THEN_MAC, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM */ + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) +static void ssl_write_extended_ms_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + unsigned char *p = buf; + + if (ssl->handshake->extended_ms == MBEDTLS_SSL_EXTENDED_MS_DISABLED) { + *olen = 0; + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, adding extended master secret " + "extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_EXTENDED_MASTER_SECRET, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +static void ssl_write_session_ticket_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + unsigned char *p = buf; + + if (ssl->handshake->new_session_ticket == 0) { + *olen = 0; + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, adding session ticket extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SESSION_TICKET, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 0x00; + + *olen = 4; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +static void ssl_write_renegotiation_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + unsigned char *p = buf; + + if (ssl->secure_renegotiation != MBEDTLS_SSL_SECURE_RENEGOTIATION) { + *olen = 0; + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, secure renegotiation extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_RENEGOTIATION_INFO, p, 0); + p += 2; + +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + *p++ = 0x00; + *p++ = (ssl->verify_data_len * 2 + 1) & 0xFF; + *p++ = ssl->verify_data_len * 2 & 0xFF; + + memcpy(p, ssl->peer_verify_data, ssl->verify_data_len); + p += ssl->verify_data_len; + memcpy(p, ssl->own_verify_data, ssl->verify_data_len); + p += ssl->verify_data_len; + } else +#endif /* MBEDTLS_SSL_RENEGOTIATION */ + { + *p++ = 0x00; + *p++ = 0x01; + *p++ = 0x00; + } + + *olen = p - buf; +} + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) +static void ssl_write_max_fragment_length_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + unsigned char *p = buf; + + if (ssl->session_negotiate->mfl_code == MBEDTLS_SSL_MAX_FRAG_LEN_NONE) { + *olen = 0; + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, max_fragment_length extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_MAX_FRAGMENT_LENGTH, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 1; + + *p++ = ssl->session_negotiate->mfl_code; + + *olen = 5; +} +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +static void ssl_write_supported_point_formats_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + unsigned char *p = buf; + ((void) ssl); + + if ((ssl->handshake->cli_exts & + MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS_PRESENT) == 0) { + *olen = 0; + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, supported_point_formats extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SUPPORTED_POINT_FORMATS, p, 0); + p += 2; + + *p++ = 0x00; + *p++ = 2; + + *p++ = 1; + *p++ = MBEDTLS_ECP_PF_UNCOMPRESSED; + + *olen = 6; +} +#endif /* MBEDTLS_ECDH_C || MBEDTLS_ECDSA_C || MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) +static void ssl_write_ecjpake_kkpp_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; + size_t kkpp_len; + + *olen = 0; + + /* Skip costly computation if not needed */ + if (ssl->handshake->ciphersuite_info->key_exchange != + MBEDTLS_KEY_EXCHANGE_ECJPAKE) { + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, ecjpake kkpp extension")); + + if (end - p < 4) { + MBEDTLS_SSL_DEBUG_MSG(1, ("buffer too small")); + return; + } + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_ECJPAKE_KKPP, p, 0); + p += 2; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + ret = mbedtls_psa_ecjpake_write_round(&ssl->handshake->psa_pake_ctx, + p + 2, end - p - 2, &kkpp_len, + MBEDTLS_ECJPAKE_ROUND_ONE); + if (ret != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_output", ret); + return; + } +#else + ret = mbedtls_ecjpake_write_round_one(&ssl->handshake->ecjpake_ctx, + p + 2, end - p - 2, &kkpp_len, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_write_round_one", ret); + return; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + MBEDTLS_PUT_UINT16_BE(kkpp_len, p, 0); + p += 2; + + *olen = kkpp_len + 4; +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + +#if defined(MBEDTLS_SSL_DTLS_SRTP) && defined(MBEDTLS_SSL_PROTO_DTLS) +static void ssl_write_use_srtp_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + size_t *olen) +{ + size_t mki_len = 0, ext_len = 0; + uint16_t profile_value = 0; + const unsigned char *end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; + + *olen = 0; + + if ((ssl->conf->transport != MBEDTLS_SSL_TRANSPORT_DATAGRAM) || + (ssl->dtls_srtp_info.chosen_dtls_srtp_profile == MBEDTLS_TLS_SRTP_UNSET)) { + return; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, adding use_srtp extension")); + + if (ssl->conf->dtls_srtp_mki_support == MBEDTLS_SSL_DTLS_SRTP_MKI_SUPPORTED) { + mki_len = ssl->dtls_srtp_info.mki_len; + } + + /* The extension total size is 9 bytes : + * - 2 bytes for the extension tag + * - 2 bytes for the total size + * - 2 bytes for the protection profile length + * - 2 bytes for the protection profile + * - 1 byte for the mki length + * + the actual mki length + * Check we have enough room in the output buffer */ + if ((size_t) (end - buf) < mki_len + 9) { + MBEDTLS_SSL_DEBUG_MSG(1, ("buffer too small")); + return; + } + + /* extension */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_USE_SRTP, buf, 0); + /* + * total length 5 and mki value: only one profile(2 bytes) + * and length(2 bytes) and srtp_mki ) + */ + ext_len = 5 + mki_len; + MBEDTLS_PUT_UINT16_BE(ext_len, buf, 2); + + /* protection profile length: 2 */ + buf[4] = 0x00; + buf[5] = 0x02; + profile_value = mbedtls_ssl_check_srtp_profile_value( + ssl->dtls_srtp_info.chosen_dtls_srtp_profile); + if (profile_value != MBEDTLS_TLS_SRTP_UNSET) { + MBEDTLS_PUT_UINT16_BE(profile_value, buf, 6); + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("use_srtp extension invalid profile")); + return; + } + + buf[8] = mki_len & 0xFF; + memcpy(&buf[9], ssl->dtls_srtp_info.mki_value, mki_len); + + *olen = 9 + mki_len; +} +#endif /* MBEDTLS_SSL_DTLS_SRTP */ + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_hello_verify_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = ssl->out_msg + 4; + unsigned char *cookie_len_byte; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write hello verify request")); + + /* + * struct { + * ProtocolVersion server_version; + * opaque cookie<0..2^8-1>; + * } HelloVerifyRequest; + */ + + /* The RFC is not clear on this point, but sending the actual negotiated + * version looks like the most interoperable thing to do. */ + mbedtls_ssl_write_version(p, ssl->conf->transport, ssl->tls_version); + MBEDTLS_SSL_DEBUG_BUF(3, "server version", p, 2); + p += 2; + + /* If we get here, f_cookie_check is not null */ + if (ssl->conf->f_cookie_write == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("inconsistent cookie callbacks")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Skip length byte until we know the length */ + cookie_len_byte = p++; + + if ((ret = ssl->conf->f_cookie_write(ssl->conf->p_cookie, + &p, ssl->out_buf + MBEDTLS_SSL_OUT_BUFFER_LEN, + ssl->cli_id, ssl->cli_id_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "f_cookie_write", ret); + return ret; + } + + *cookie_len_byte = (unsigned char) (p - (cookie_len_byte + 1)); + + MBEDTLS_SSL_DEBUG_BUF(3, "cookie sent", cookie_len_byte + 1, *cookie_len_byte); + + ssl->out_msglen = p - ssl->out_msg; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_HELLO_VERIFY_REQUEST; + + ssl->state = MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + (ret = mbedtls_ssl_flight_transmit(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flight_transmit", ret); + return ret; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write hello verify request")); + + return 0; +} +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */ + +static void ssl_handle_id_based_session_resumption(mbedtls_ssl_context *ssl) +{ + int ret; + mbedtls_ssl_session session_tmp; + mbedtls_ssl_session * const session = ssl->session_negotiate; + + /* Resume is 0 by default, see ssl_handshake_init(). + * It may be already set to 1 by ssl_parse_session_ticket_ext(). */ + if (ssl->handshake->resume == 1) { + return; + } + if (session->id_len == 0) { + return; + } + if (ssl->conf->f_get_cache == NULL) { + return; + } +#if defined(MBEDTLS_SSL_RENEGOTIATION) + if (ssl->renego_status != MBEDTLS_SSL_INITIAL_HANDSHAKE) { + return; + } +#endif + + mbedtls_ssl_session_init(&session_tmp); + + ret = ssl->conf->f_get_cache(ssl->conf->p_cache, + session->id, + session->id_len, + &session_tmp); + if (ret != 0) { + goto exit; + } + + if (session->ciphersuite != session_tmp.ciphersuite) { + /* Mismatch between cached and negotiated session */ + goto exit; + } + + /* Move semantics */ + mbedtls_ssl_session_free(session); + *session = session_tmp; + memset(&session_tmp, 0, sizeof(session_tmp)); + + MBEDTLS_SSL_DEBUG_MSG(3, ("session successfully restored from cache")); + ssl->handshake->resume = 1; + +exit: + + mbedtls_ssl_session_free(&session_tmp); +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_server_hello(mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t t; +#endif + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen, ext_len = 0, n; + unsigned char *buf, *p; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write server hello")); + +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + ssl->handshake->cookie_verify_result != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("client hello was not authenticated")); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write server hello")); + + return ssl_write_hello_verify_request(ssl); + } +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */ + + if (ssl->conf->f_rng == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no RNG provided")); + return MBEDTLS_ERR_SSL_NO_RNG; + } + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 5 protocol version + * 6 . 9 UNIX time() + * 10 . 37 random bytes + */ + buf = ssl->out_msg; + p = buf + 4; + + mbedtls_ssl_write_version(p, ssl->conf->transport, ssl->tls_version); + p += 2; + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, chosen version: [%d:%d]", + buf[4], buf[5])); + +#if defined(MBEDTLS_HAVE_TIME) + t = mbedtls_time(NULL); + MBEDTLS_PUT_UINT32_BE(t, p, 0); + p += 4; + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, current time: %" MBEDTLS_PRINTF_LONGLONG, + (long long) t)); +#else + if ((ret = ssl->conf->f_rng(ssl->conf->p_rng, p, 4)) != 0) { + return ret; + } + + p += 4; +#endif /* MBEDTLS_HAVE_TIME */ + + if ((ret = ssl->conf->f_rng(ssl->conf->p_rng, p, 28)) != 0) { + return ret; + } + + p += 28; + + memcpy(ssl->handshake->randbytes + 32, buf + 6, 32); + + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, random bytes", buf + 6, 32); + + ssl_handle_id_based_session_resumption(ssl); + + if (ssl->handshake->resume == 0) { + /* + * New session, create a new session id, + * unless we're about to issue a session ticket + */ + ssl->state++; + +#if defined(MBEDTLS_HAVE_TIME) + ssl->session_negotiate->start = mbedtls_time(NULL); +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (ssl->handshake->new_session_ticket != 0) { + ssl->session_negotiate->id_len = n = 0; + memset(ssl->session_negotiate->id, 0, 32); + } else +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + { + ssl->session_negotiate->id_len = n = 32; + if ((ret = ssl->conf->f_rng(ssl->conf->p_rng, ssl->session_negotiate->id, + n)) != 0) { + return ret; + } + } + } else { + /* + * Resuming a session + */ + n = ssl->session_negotiate->id_len; + ssl->state = MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC; + + if ((ret = mbedtls_ssl_derive_keys(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_derive_keys", ret); + return ret; + } + } + + /* + * 38 . 38 session id length + * 39 . 38+n session id + * 39+n . 40+n chosen ciphersuite + * 41+n . 41+n chosen compression alg. + * 42+n . 43+n extensions length + * 44+n . 43+n+m extensions + */ + *p++ = (unsigned char) ssl->session_negotiate->id_len; + memcpy(p, ssl->session_negotiate->id, ssl->session_negotiate->id_len); + p += ssl->session_negotiate->id_len; + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, session id len.: %" MBEDTLS_PRINTF_SIZET, n)); + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, session id", buf + 39, n); + MBEDTLS_SSL_DEBUG_MSG(3, ("%s session has been resumed", + ssl->handshake->resume ? "a" : "no")); + + MBEDTLS_PUT_UINT16_BE(ssl->session_negotiate->ciphersuite, p, 0); + p += 2; + *p++ = MBEDTLS_BYTE_0(MBEDTLS_SSL_COMPRESS_NULL); + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, chosen ciphersuite: %s", + mbedtls_ssl_get_ciphersuite_name(ssl->session_negotiate->ciphersuite))); + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, compress alg.: 0x%02X", + (unsigned int) MBEDTLS_SSL_COMPRESS_NULL)); + + /* + * First write extensions, then the total length + */ + ssl_write_renegotiation_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; + +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + ssl_write_max_fragment_length_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; +#endif + +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + ssl_write_cid_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; +#endif + +#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC_ETM) + ssl_write_encrypt_then_mac_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; +#endif + +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + ssl_write_extended_ms_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + ssl_write_session_ticket_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; +#endif + +#if defined(MBEDTLS_ECDH_C) || defined(MBEDTLS_ECDSA_C) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + const mbedtls_ssl_ciphersuite_t *suite = + mbedtls_ssl_ciphersuite_from_id(ssl->session_negotiate->ciphersuite); + if (suite != NULL && mbedtls_ssl_ciphersuite_uses_ec(suite)) { + ssl_write_supported_point_formats_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; + } +#endif + +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + ssl_write_ecjpake_kkpp_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; +#endif + +#if defined(MBEDTLS_SSL_ALPN) + unsigned char *end = buf + MBEDTLS_SSL_OUT_CONTENT_LEN - 4; + if ((ret = mbedtls_ssl_write_alpn_ext(ssl, p + 2 + ext_len, end, &olen)) + != 0) { + return ret; + } + + ext_len += olen; +#endif + +#if defined(MBEDTLS_SSL_DTLS_SRTP) + ssl_write_use_srtp_ext(ssl, p + 2 + ext_len, &olen); + ext_len += olen; +#endif + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, total extension length: %" MBEDTLS_PRINTF_SIZET, + ext_len)); + + if (ext_len > 0) { + MBEDTLS_PUT_UINT16_BE(ext_len, p, 0); + p += 2 + ext_len; + } + + ssl->out_msglen = p - buf; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_SERVER_HELLO; + + ret = mbedtls_ssl_write_handshake_msg(ssl); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write server hello")); + + return ret; +} + +#if !defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_certificate_request(mbedtls_ssl_context *ssl) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate request")); + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate request")); + ssl->state++; + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} +#else /* !MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_certificate_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + uint16_t dn_size, total_dn_size; /* excluding length bytes */ + size_t ct_len, sa_len; /* including length bytes */ + unsigned char *buf, *p; + const unsigned char * const end = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN; + const mbedtls_x509_crt *crt; + int authmode; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate request")); + + ssl->state++; + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET) { + authmode = ssl->handshake->sni_authmode; + } else +#endif + authmode = ssl->conf->authmode; + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info) || + authmode == MBEDTLS_SSL_VERIFY_NONE) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate request")); + return 0; + } + + /* + * 0 . 0 handshake type + * 1 . 3 handshake length + * 4 . 4 cert type count + * 5 .. m-1 cert types + * m .. m+1 sig alg length (TLS 1.2 only) + * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only) + * n .. n+1 length of all DNs + * n+2 .. n+3 length of DN 1 + * n+4 .. ... Distinguished Name #1 + * ... .. ... length of DN 2, etc. + */ + buf = ssl->out_msg; + p = buf + 4; + + /* + * Supported certificate types + * + * ClientCertificateType certificate_types<1..2^8-1>; + * enum { (255) } ClientCertificateType; + */ + ct_len = 0; + +#if defined(MBEDTLS_RSA_C) + p[1 + ct_len++] = MBEDTLS_SSL_CERT_TYPE_RSA_SIGN; +#endif +#if defined(MBEDTLS_ECDSA_C) + p[1 + ct_len++] = MBEDTLS_SSL_CERT_TYPE_ECDSA_SIGN; +#endif + + p[0] = (unsigned char) ct_len++; + p += ct_len; + + sa_len = 0; + + /* + * Add signature_algorithms for verify (TLS 1.2) + * + * SignatureAndHashAlgorithm supported_signature_algorithms<2..2^16-2>; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * enum { (255) } HashAlgorithm; + * enum { (255) } SignatureAlgorithm; + */ + const uint16_t *sig_alg = mbedtls_ssl_get_sig_algs(ssl); + if (sig_alg == NULL) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + + for (; *sig_alg != MBEDTLS_TLS_SIG_NONE; sig_alg++) { + unsigned char hash = MBEDTLS_BYTE_1(*sig_alg); + + if (mbedtls_ssl_set_calc_verify_md(ssl, hash)) { + continue; + } + if (!mbedtls_ssl_sig_alg_is_supported(ssl, *sig_alg)) { + continue; + } + + /* Write elements at offsets starting from 1 (offset 0 is for the + * length). Thus the offset of each element is the length of the + * partial list including that element. */ + sa_len += 2; + MBEDTLS_PUT_UINT16_BE(*sig_alg, p, sa_len); + + } + + /* Fill in list length. */ + MBEDTLS_PUT_UINT16_BE(sa_len, p, 0); + sa_len += 2; + p += sa_len; + + /* + * DistinguishedName certificate_authorities<0..2^16-1>; + * opaque DistinguishedName<1..2^16-1>; + */ + p += 2; + + total_dn_size = 0; + + if (ssl->conf->cert_req_ca_list == MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED) { + /* NOTE: If trusted certificates are provisioned + * via a CA callback (configured through + * `mbedtls_ssl_conf_ca_cb()`, then the + * CertificateRequest is currently left empty. */ + +#if defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->dn_hints != NULL) { + crt = ssl->handshake->dn_hints; + } else +#endif + if (ssl->conf->dn_hints != NULL) { + crt = ssl->conf->dn_hints; + } else +#endif +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_ca_chain != NULL) { + crt = ssl->handshake->sni_ca_chain; + } else +#endif + crt = ssl->conf->ca_chain; + + while (crt != NULL && crt->version != 0) { + /* It follows from RFC 5280 A.1 that this length + * can be represented in at most 11 bits. */ + dn_size = (uint16_t) crt->subject_raw.len; + + if (end < p || (size_t) (end - p) < 2 + (size_t) dn_size) { + MBEDTLS_SSL_DEBUG_MSG(1, ("skipping CAs: buffer too short")); + break; + } + + MBEDTLS_PUT_UINT16_BE(dn_size, p, 0); + p += 2; + memcpy(p, crt->subject_raw.p, dn_size); + p += dn_size; + + MBEDTLS_SSL_DEBUG_BUF(3, "requested DN", p - dn_size, dn_size); + + total_dn_size += 2 + dn_size; + crt = crt->next; + } + } + + ssl->out_msglen = p - buf; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_CERTIFICATE_REQUEST; + MBEDTLS_PUT_UINT16_BE(total_dn_size, ssl->out_msg, 4 + ct_len + sa_len); + + ret = mbedtls_ssl_write_handshake_msg(ssl); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write certificate request")); + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) && \ + (defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED)) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_ecdh_params_from_cert(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + unsigned char buf[ + PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS)]; + psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; + uint16_t tls_id = 0; + psa_ecc_family_t ecc_family; + size_t key_len; + mbedtls_pk_context *pk; + mbedtls_ecp_keypair *key; + + pk = mbedtls_ssl_own_key(ssl); + + if (pk == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + switch (mbedtls_pk_get_type(pk)) { + case MBEDTLS_PK_OPAQUE: + if (!mbedtls_pk_can_do(pk, MBEDTLS_PK_ECKEY)) { + return MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + } + + ssl->handshake->ecdh_psa_privkey = + *((mbedtls_svc_key_id_t *) pk->pk_ctx); + + /* Key should not be destroyed in the TLS library */ + ssl->handshake->ecdh_psa_privkey_is_external = 1; + + status = psa_get_key_attributes(ssl->handshake->ecdh_psa_privkey, + &key_attributes); + if (status != PSA_SUCCESS) { + ssl->handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return PSA_TO_MBEDTLS_ERR(status); + } + + ssl->handshake->ecdh_psa_type = psa_get_key_type(&key_attributes); + ssl->handshake->ecdh_bits = psa_get_key_bits(&key_attributes); + + psa_reset_key_attributes(&key_attributes); + + ret = 0; + break; + case MBEDTLS_PK_ECKEY: + case MBEDTLS_PK_ECKEY_DH: + case MBEDTLS_PK_ECDSA: + key = mbedtls_pk_ec(*pk); + if (key == NULL) { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id(key->grp.id); + if (tls_id == 0) { + /* This elliptic curve is not supported */ + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + /* If the above conversion to TLS ID was fine, then also this one will + be, so there is no need to check the return value here */ + mbedtls_ssl_get_psa_curve_info_from_tls_id(tls_id, &ecc_family, + &ssl->handshake->ecdh_bits); + + ssl->handshake->ecdh_psa_type = PSA_KEY_TYPE_ECC_KEY_PAIR(ecc_family); + + key_attributes = psa_key_attributes_init(); + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH); + psa_set_key_type(&key_attributes, + PSA_KEY_TYPE_ECC_KEY_PAIR(ssl->handshake->ecdh_psa_type)); + psa_set_key_bits(&key_attributes, ssl->handshake->ecdh_bits); + + key_len = PSA_BITS_TO_BYTES(key->grp.pbits); + ret = mbedtls_ecp_write_key(key, buf, key_len); + if (ret != 0) { + goto cleanup; + } + + status = psa_import_key(&key_attributes, buf, key_len, + &ssl->handshake->ecdh_psa_privkey); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto cleanup; + } + + ret = 0; + break; + default: + ret = MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + } + +cleanup: + mbedtls_platform_zeroize(buf, sizeof(buf)); + + return ret; +} +#elif defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_get_ecdh_params_from_cert(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + const mbedtls_pk_context *private_key = mbedtls_ssl_own_key(ssl); + if (private_key == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no server private key")); + return MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED; + } + + if (!mbedtls_pk_can_do(private_key, MBEDTLS_PK_ECKEY)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("server key not ECDH capable")); + return MBEDTLS_ERR_SSL_PK_TYPE_MISMATCH; + } + + if ((ret = mbedtls_ecdh_get_params(&ssl->handshake->ecdh_ctx, + mbedtls_pk_ec(*mbedtls_ssl_own_key(ssl)), + MBEDTLS_ECDH_OURS)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ecdh_get_params"), ret); + return ret; + } + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || + MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) && \ + defined(MBEDTLS_SSL_ASYNC_PRIVATE) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_resume_server_key_exchange(mbedtls_ssl_context *ssl, + size_t *signature_len) +{ + /* Append the signature to ssl->out_msg, leaving 2 bytes for the + * signature length which will be added in ssl_write_server_key_exchange + * after the call to ssl_prepare_server_key_exchange. + * ssl_write_server_key_exchange also takes care of incrementing + * ssl->out_msglen. */ + unsigned char *sig_start = ssl->out_msg + ssl->out_msglen + 2; + size_t sig_max_len = (ssl->out_buf + MBEDTLS_SSL_OUT_CONTENT_LEN + - sig_start); + int ret = ssl->conf->f_async_resume(ssl, + sig_start, signature_len, sig_max_len); + if (ret != MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) { + ssl->handshake->async_in_progress = 0; + mbedtls_ssl_set_async_operation_data(ssl, NULL); + } + MBEDTLS_SSL_DEBUG_RET(2, "ssl_resume_server_key_exchange", ret); + return ret; +} +#endif /* defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) && + defined(MBEDTLS_SSL_ASYNC_PRIVATE) */ + +/* Prepare the ServerKeyExchange message, up to and including + * calculating the signature if any, but excluding formatting the + * signature and sending the message. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_prepare_server_key_exchange(mbedtls_ssl_context *ssl, + size_t *signature_len) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED) +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) + unsigned char *dig_signed = NULL; +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED */ +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PFS_ENABLED */ + + (void) ciphersuite_info; /* unused in some configurations */ +#if !defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) + (void) signature_len; +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + size_t out_buf_len = ssl->out_buf_len - (ssl->out_msg - ssl->out_buf); +#else + size_t out_buf_len = MBEDTLS_SSL_OUT_BUFFER_LEN - (ssl->out_msg - ssl->out_buf); +#endif +#endif + + ssl->out_msglen = 4; /* header (type:1, length:3) to be written later */ + + /* + * + * Part 1: Provide key exchange parameters for chosen ciphersuite. + * + */ + + /* + * - ECJPAKE key exchanges + */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + unsigned char *out_p = ssl->out_msg + ssl->out_msglen; + unsigned char *end_p = ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN - + ssl->out_msglen; + size_t output_offset = 0; + size_t output_len = 0; + + /* + * The first 3 bytes are: + * [0] MBEDTLS_ECP_TLS_NAMED_CURVE + * [1, 2] elliptic curve's TLS ID + * + * However since we only support secp256r1 for now, we hardcode its + * TLS ID here + */ + uint16_t tls_id = mbedtls_ssl_get_tls_id_from_ecp_group_id( + MBEDTLS_ECP_DP_SECP256R1); + if (tls_id == 0) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + *out_p = MBEDTLS_ECP_TLS_NAMED_CURVE; + MBEDTLS_PUT_UINT16_BE(tls_id, out_p, 1); + output_offset += 3; + + ret = mbedtls_psa_ecjpake_write_round(&ssl->handshake->psa_pake_ctx, + out_p + output_offset, + end_p - out_p - output_offset, &output_len, + MBEDTLS_ECJPAKE_ROUND_TWO); + if (ret != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_output", ret); + return ret; + } + + output_offset += output_len; + ssl->out_msglen += output_offset; +#else + size_t len = 0; + + ret = mbedtls_ecjpake_write_round_two( + &ssl->handshake->ecjpake_ctx, + ssl->out_msg + ssl->out_msglen, + MBEDTLS_SSL_OUT_CONTENT_LEN - ssl->out_msglen, &len, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_write_round_two", ret); + return ret; + } + + ssl->out_msglen += len; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + + /* + * For (EC)DHE key exchanges with PSK, parameters are prefixed by support + * identity hint (RFC 4279, Sec. 3). Until someone needs this feature, + * we use empty support identity hints here. + **/ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK) { + ssl->out_msg[ssl->out_msglen++] = 0x00; + ssl->out_msg[ssl->out_msglen++] = 0x00; + } +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ + + /* + * - DHE key exchanges + */ +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_DHE_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_dhe(ciphersuite_info)) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + if (ssl->conf->dhm_P.p == NULL || ssl->conf->dhm_G.p == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no DH parameters set")); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* + * Ephemeral DH parameters: + * + * struct { + * opaque dh_p<1..2^16-1>; + * opaque dh_g<1..2^16-1>; + * opaque dh_Ys<1..2^16-1>; + * } ServerDHParams; + */ + if ((ret = mbedtls_dhm_set_group(&ssl->handshake->dhm_ctx, + &ssl->conf->dhm_P, + &ssl->conf->dhm_G)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_set_group", ret); + return ret; + } + + if ((ret = mbedtls_dhm_make_params( + &ssl->handshake->dhm_ctx, + (int) mbedtls_dhm_get_len(&ssl->handshake->dhm_ctx), + ssl->out_msg + ssl->out_msglen, &len, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_make_params", ret); + return ret; + } + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) + dig_signed = ssl->out_msg + ssl->out_msglen; +#endif + + ssl->out_msglen += len; + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: X ", &ssl->handshake->dhm_ctx.X); + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: P ", &ssl->handshake->dhm_ctx.P); + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: G ", &ssl->handshake->dhm_ctx.G); + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: GX", &ssl->handshake->dhm_ctx.GX); + } +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_DHE_ENABLED */ + + /* + * - ECDHE key exchanges + */ +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDHE_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_ecdhe(ciphersuite_info)) { + /* + * Ephemeral ECDH parameters: + * + * struct { + * ECParameters curve_params; + * ECPoint public; + * } ServerECDHParams; + */ + uint16_t *curr_tls_id = ssl->handshake->curves_tls_id; + const uint16_t *group_list = mbedtls_ssl_get_groups(ssl); + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + /* Match our preference list against the offered curves */ + if ((group_list == NULL) || (curr_tls_id == NULL)) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + for (; *group_list != 0; group_list++) { + for (curr_tls_id = ssl->handshake->curves_tls_id; + *curr_tls_id != 0; curr_tls_id++) { + if (*curr_tls_id == *group_list) { + goto curve_matching_done; + } + } + } + +curve_matching_done: + if (*curr_tls_id == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no matching curve for ECDHE")); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("ECDHE curve: %s", + mbedtls_ssl_get_curve_name_from_tls_id(*curr_tls_id))); + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_GENERIC_ERROR; + psa_key_attributes_t key_attributes; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + uint8_t *p = ssl->out_msg + ssl->out_msglen; + const size_t header_size = 4; // curve_type(1), namedcurve(2), + // data length(1) + const size_t data_length_size = 1; + psa_ecc_family_t ec_psa_family = 0; + size_t ec_bits = 0; + + MBEDTLS_SSL_DEBUG_MSG(1, ("Perform PSA-based ECDH computation.")); + + /* Convert EC's TLS ID to PSA key type. */ + if (mbedtls_ssl_get_psa_curve_info_from_tls_id(*curr_tls_id, + &ec_psa_family, + &ec_bits) == PSA_ERROR_NOT_SUPPORTED) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Invalid ecc group parse.")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + handshake->ecdh_psa_type = PSA_KEY_TYPE_ECC_KEY_PAIR(ec_psa_family); + handshake->ecdh_bits = ec_bits; + + key_attributes = psa_key_attributes_init(); + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH); + psa_set_key_type(&key_attributes, handshake->ecdh_psa_type); + psa_set_key_bits(&key_attributes, handshake->ecdh_bits); + + /* + * ECParameters curve_params + * + * First byte is curve_type, always named_curve + */ + *p++ = MBEDTLS_ECP_TLS_NAMED_CURVE; + + /* + * Next two bytes are the namedcurve value + */ + MBEDTLS_PUT_UINT16_BE(*curr_tls_id, p, 0); + p += 2; + + /* Generate ECDH private key. */ + status = psa_generate_key(&key_attributes, + &handshake->ecdh_psa_privkey); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_generate_key", ret); + return ret; + } + + /* + * ECPoint public + * + * First byte is data length. + * It will be filled later. p holds now the data length location. + */ + + /* Export the public part of the ECDH private key from PSA. + * Make one byte space for the length. + */ + unsigned char *own_pubkey = p + data_length_size; + + size_t own_pubkey_max_len = (size_t) (MBEDTLS_SSL_OUT_CONTENT_LEN + - (own_pubkey - ssl->out_msg)); + + status = psa_export_public_key(handshake->ecdh_psa_privkey, + own_pubkey, own_pubkey_max_len, + &len); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_export_public_key", ret); + (void) psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return ret; + } + + /* Store the length of the exported public key. */ + *p = (uint8_t) len; + + /* Determine full message length. */ + len += header_size; +#else + mbedtls_ecp_group_id curr_grp_id = + mbedtls_ssl_get_ecp_group_id_from_tls_id(*curr_tls_id); + + if ((ret = mbedtls_ecdh_setup(&ssl->handshake->ecdh_ctx, + curr_grp_id)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecp_group_load", ret); + return ret; + } + + if ((ret = mbedtls_ecdh_make_params( + &ssl->handshake->ecdh_ctx, &len, + ssl->out_msg + ssl->out_msglen, + MBEDTLS_SSL_OUT_CONTENT_LEN - ssl->out_msglen, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_make_params", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_Q); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) + dig_signed = ssl->out_msg + ssl->out_msglen; +#endif + + ssl->out_msglen += len; + } +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_ECDHE_ENABLED */ + + /* + * + * Part 2: For key exchanges involving the server signing the + * exchange parameters, compute and add the signature here. + * + */ +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_server_signature(ciphersuite_info)) { + if (dig_signed == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + size_t dig_signed_len = ssl->out_msg + ssl->out_msglen - dig_signed; + size_t hashlen = 0; + unsigned char hash[MBEDTLS_HASH_MAX_SIZE]; + + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * 2.1: Choose hash algorithm: + * For TLS 1.2, obey signature-hash-algorithm extension + * to choose appropriate hash. + */ + + mbedtls_pk_type_t sig_alg = + mbedtls_ssl_get_ciphersuite_sig_pk_alg(ciphersuite_info); + + unsigned int sig_hash = + mbedtls_ssl_tls12_get_preferred_hash_for_sig_alg( + ssl, mbedtls_ssl_sig_from_pk_alg(sig_alg)); + + mbedtls_md_type_t md_alg = mbedtls_ssl_md_alg_from_hash(sig_hash); + + /* For TLS 1.2, obey signature-hash-algorithm extension + * (RFC 5246, Sec. 7.4.1.4.1). */ + if (sig_alg == MBEDTLS_PK_NONE || md_alg == MBEDTLS_MD_NONE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + /* (... because we choose a cipher suite + * only if there is a matching hash.) */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("pick hash algorithm %u for signing", (unsigned) md_alg)); + + /* + * 2.2: Compute the hash to be signed + */ + if (md_alg != MBEDTLS_MD_NONE) { + ret = mbedtls_ssl_get_key_exchange_md_tls1_2(ssl, hash, &hashlen, + dig_signed, + dig_signed_len, + md_alg); + if (ret != 0) { + return ret; + } + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "parameters hash", hash, hashlen); + + /* + * 2.3: Compute and add the signature + */ + /* + * We need to specify signature and hash algorithm explicitly through + * a prefix to the signature. + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * struct { + * SignatureAndHashAlgorithm algorithm; + * opaque signature<0..2^16-1>; + * } DigitallySigned; + * + */ + + ssl->out_msg[ssl->out_msglen++] = mbedtls_ssl_hash_from_md_alg(md_alg); + ssl->out_msg[ssl->out_msglen++] = mbedtls_ssl_sig_from_pk_alg(sig_alg); + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if (ssl->conf->f_async_sign_start != NULL) { + ret = ssl->conf->f_async_sign_start(ssl, + mbedtls_ssl_own_cert(ssl), + md_alg, hash, hashlen); + switch (ret) { + case MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH: + /* act as if f_async_sign was null */ + break; + case 0: + ssl->handshake->async_in_progress = 1; + return ssl_resume_server_key_exchange(ssl, signature_len); + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + ssl->handshake->async_in_progress = 1; + return MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS; + default: + MBEDTLS_SSL_DEBUG_RET(1, "f_async_sign_start", ret); + return ret; + } + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + if (mbedtls_ssl_own_key(ssl) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no private key")); + return MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED; + } + + /* Append the signature to ssl->out_msg, leaving 2 bytes for the + * signature length which will be added in ssl_write_server_key_exchange + * after the call to ssl_prepare_server_key_exchange. + * ssl_write_server_key_exchange also takes care of incrementing + * ssl->out_msglen. */ + if ((ret = mbedtls_pk_sign(mbedtls_ssl_own_key(ssl), + md_alg, hash, hashlen, + ssl->out_msg + ssl->out_msglen + 2, + out_buf_len - ssl->out_msglen - 2, + signature_len, + ssl->conf->f_rng, + ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_pk_sign", ret); + return ret; + } + } +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED */ + + return 0; +} + +/* Prepare the ServerKeyExchange message and send it. For ciphersuites + * that do not include a ServerKeyExchange message, do nothing. Either + * way, if successful, move on to the next step in the SSL state + * machine. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_server_key_exchange(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t signature_len = 0; +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED) + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write server key exchange")); + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED) + /* Extract static ECDH parameters and abort if ServerKeyExchange + * is not needed. */ + if (mbedtls_ssl_ciphersuite_no_pfs(ciphersuite_info)) { + /* For suites involving ECDH, extract DH parameters + * from certificate at this point. */ +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED) + if (mbedtls_ssl_ciphersuite_uses_ecdh(ciphersuite_info)) { + ret = ssl_get_ecdh_params_from_cert(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_get_ecdh_params_from_cert", ret); + return ret; + } + } +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_ECDH_ENABLED */ + + /* Key exchanges not involving ephemeral keys don't use + * ServerKeyExchange, so end here. */ + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write server key exchange")); + ssl->state++; + return 0; + } +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_NON_PFS_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) && \ + defined(MBEDTLS_SSL_ASYNC_PRIVATE) + /* If we have already prepared the message and there is an ongoing + * signature operation, resume signing. */ + if (ssl->handshake->async_in_progress != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("resuming signature operation")); + ret = ssl_resume_server_key_exchange(ssl, &signature_len); + } else +#endif /* defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) && + defined(MBEDTLS_SSL_ASYNC_PRIVATE) */ + { + /* ServerKeyExchange is needed. Prepare the message. */ + ret = ssl_prepare_server_key_exchange(ssl, &signature_len); + } + + if (ret != 0) { + /* If we're starting to write a new message, set ssl->out_msglen + * to 0. But if we're resuming after an asynchronous message, + * out_msglen is the amount of data written so far and mst be + * preserved. */ + if (ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write server key exchange (pending)")); + } else { + ssl->out_msglen = 0; + } + return ret; + } + + /* If there is a signature, write its length. + * ssl_prepare_server_key_exchange already wrote the signature + * itself at its proper place in the output buffer. */ +#if defined(MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED) + if (signature_len != 0) { + ssl->out_msg[ssl->out_msglen++] = MBEDTLS_BYTE_1(signature_len); + ssl->out_msg[ssl->out_msglen++] = MBEDTLS_BYTE_0(signature_len); + + MBEDTLS_SSL_DEBUG_BUF(3, "my signature", + ssl->out_msg + ssl->out_msglen, + signature_len); + + /* Skip over the already-written signature */ + ssl->out_msglen += signature_len; + } +#endif /* MBEDTLS_KEY_EXCHANGE_WITH_SERVER_SIGNATURE_ENABLED */ + + /* Add header and send. */ + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_SERVER_KEY_EXCHANGE; + + ssl->state++; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write server key exchange")); + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_server_hello_done(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write server hello done")); + + ssl->out_msglen = 4; + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_SERVER_HELLO_DONE; + + ssl->state++; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM) { + mbedtls_ssl_send_flight_completed(ssl); + } +#endif + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + if (ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM && + (ret = mbedtls_ssl_flight_transmit(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_flight_transmit", ret); + return ret; + } +#endif /* MBEDTLS_SSL_PROTO_DTLS */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write server hello done")); + + return 0; +} + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_client_dh_public(mbedtls_ssl_context *ssl, unsigned char **p, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + size_t n; + + /* + * Receive G^Y mod P, premaster = (G^Y)^X mod P + */ + if (*p + 2 > end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + n = ((*p)[0] << 8) | (*p)[1]; + *p += 2; + + if (*p + n > end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if ((ret = mbedtls_dhm_read_public(&ssl->handshake->dhm_ctx, *p, n)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_read_public", ret); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + *p += n; + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: GY", &ssl->handshake->dhm_ctx.GY); + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_resume_decrypt_pms(mbedtls_ssl_context *ssl, + unsigned char *peer_pms, + size_t *peer_pmslen, + size_t peer_pmssize) +{ + int ret = ssl->conf->f_async_resume(ssl, + peer_pms, peer_pmslen, peer_pmssize); + if (ret != MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) { + ssl->handshake->async_in_progress = 0; + mbedtls_ssl_set_async_operation_data(ssl, NULL); + } + MBEDTLS_SSL_DEBUG_RET(2, "ssl_decrypt_encrypted_pms", ret); + return ret; +} +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_decrypt_encrypted_pms(mbedtls_ssl_context *ssl, + const unsigned char *p, + const unsigned char *end, + unsigned char *peer_pms, + size_t *peer_pmslen, + size_t peer_pmssize) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_x509_crt *own_cert = mbedtls_ssl_own_cert(ssl); + if (own_cert == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no local certificate")); + return MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE; + } + mbedtls_pk_context *public_key = &own_cert->pk; + mbedtls_pk_context *private_key = mbedtls_ssl_own_key(ssl); + size_t len = mbedtls_pk_get_len(public_key); + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + /* If we have already started decoding the message and there is an ongoing + * decryption operation, resume signing. */ + if (ssl->handshake->async_in_progress != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("resuming decryption operation")); + return ssl_resume_decrypt_pms(ssl, + peer_pms, peer_pmslen, peer_pmssize); + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + /* + * Prepare to decrypt the premaster using own private RSA key + */ + if (p + 2 > end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + if (*p++ != MBEDTLS_BYTE_1(len) || + *p++ != MBEDTLS_BYTE_0(len)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if (p + len != end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* + * Decrypt the premaster secret + */ +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if (ssl->conf->f_async_decrypt_start != NULL) { + ret = ssl->conf->f_async_decrypt_start(ssl, + mbedtls_ssl_own_cert(ssl), + p, len); + switch (ret) { + case MBEDTLS_ERR_SSL_HW_ACCEL_FALLTHROUGH: + /* act as if f_async_decrypt_start was null */ + break; + case 0: + ssl->handshake->async_in_progress = 1; + return ssl_resume_decrypt_pms(ssl, + peer_pms, + peer_pmslen, + peer_pmssize); + case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS: + ssl->handshake->async_in_progress = 1; + return MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS; + default: + MBEDTLS_SSL_DEBUG_RET(1, "f_async_decrypt_start", ret); + return ret; + } + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + if (!mbedtls_pk_can_do(private_key, MBEDTLS_PK_RSA)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no RSA private key")); + return MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED; + } + + ret = mbedtls_pk_decrypt(private_key, p, len, + peer_pms, peer_pmslen, peer_pmssize, + ssl->conf->f_rng, ssl->conf->p_rng); + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_encrypted_pms(mbedtls_ssl_context *ssl, + const unsigned char *p, + const unsigned char *end, + size_t pms_offset) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *pms = ssl->handshake->premaster + pms_offset; + unsigned char ver[2]; + unsigned char fake_pms[48], peer_pms[48]; + unsigned char mask; + size_t i, peer_pmslen; + unsigned int diff; + + /* In case of a failure in decryption, the decryption may write less than + * 2 bytes of output, but we always read the first two bytes. It doesn't + * matter in the end because diff will be nonzero in that case due to + * ret being nonzero, and we only care whether diff is 0. + * But do initialize peer_pms and peer_pmslen for robustness anyway. This + * also makes memory analyzers happy (don't access uninitialized memory, + * even if it's an unsigned char). */ + peer_pms[0] = peer_pms[1] = ~0; + peer_pmslen = 0; + + ret = ssl_decrypt_encrypted_pms(ssl, p, end, + peer_pms, + &peer_pmslen, + sizeof(peer_pms)); + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if (ret == MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS) { + return ret; + } +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + + mbedtls_ssl_write_version(ver, ssl->conf->transport, + ssl->session_negotiate->tls_version); + + /* Avoid data-dependent branches while checking for invalid + * padding, to protect against timing-based Bleichenbacher-type + * attacks. */ + diff = (unsigned int) ret; + diff |= peer_pmslen ^ 48; + diff |= peer_pms[0] ^ ver[0]; + diff |= peer_pms[1] ^ ver[1]; + + /* mask = diff ? 0xff : 0x00 using bit operations to avoid branches */ + mask = mbedtls_ct_uint_mask(diff); + + /* + * Protection against Bleichenbacher's attack: invalid PKCS#1 v1.5 padding + * must not cause the connection to end immediately; instead, send a + * bad_record_mac later in the handshake. + * To protect against timing-based variants of the attack, we must + * not have any branch that depends on whether the decryption was + * successful. In particular, always generate the fake premaster secret, + * regardless of whether it will ultimately influence the output or not. + */ + ret = ssl->conf->f_rng(ssl->conf->p_rng, fake_pms, sizeof(fake_pms)); + if (ret != 0) { + /* It's ok to abort on an RNG failure, since this does not reveal + * anything about the RSA decryption. */ + return ret; + } + +#if defined(MBEDTLS_SSL_DEBUG_ALL) + if (diff != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + } +#endif + + if (sizeof(ssl->handshake->premaster) < pms_offset || + sizeof(ssl->handshake->premaster) - pms_offset < 48) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + ssl->handshake->pmslen = 48; + + /* Set pms to either the true or the fake PMS, without + * data-dependent branches. */ + for (i = 0; i < ssl->handshake->pmslen; i++) { + pms[i] = (mask & fake_pms[i]) | ((~mask) & peer_pms[i]); + } + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ + +#if defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_client_psk_identity(mbedtls_ssl_context *ssl, unsigned char **p, + const unsigned char *end) +{ + int ret = 0; + uint16_t n; + + if (ssl_conf_has_psk_or_cb(ssl->conf) == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no pre-shared key")); + return MBEDTLS_ERR_SSL_PRIVATE_KEY_REQUIRED; + } + + /* + * Receive client pre-shared key identity name + */ + if (end - *p < 2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + n = ((*p)[0] << 8) | (*p)[1]; + *p += 2; + + if (n == 0 || n > end - *p) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if (ssl->conf->f_psk != NULL) { + if (ssl->conf->f_psk(ssl->conf->p_psk, ssl, *p, n) != 0) { + ret = MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY; + } + } else { + /* Identity is not a big secret since clients send it in the clear, + * but treat it carefully anyway, just in case */ + if (n != ssl->conf->psk_identity_len || + mbedtls_ct_memcmp(ssl->conf->psk_identity, *p, n) != 0) { + ret = MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY; + } + } + + if (ret == MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY) { + MBEDTLS_SSL_DEBUG_BUF(3, "Unknown PSK identity", *p, n); + mbedtls_ssl_send_alert_message(ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL, + MBEDTLS_SSL_ALERT_MSG_UNKNOWN_PSK_IDENTITY); + return MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY; + } + + *p += n; + + return 0; +} +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_client_key_exchange(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + unsigned char *p, *end; + + ciphersuite_info = ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse client key exchange")); + +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) && \ + (defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED)) + if ((ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA) && + (ssl->handshake->async_in_progress != 0)) { + /* We've already read a record and there is an asynchronous + * operation in progress to decrypt it. So skip reading the + * record. */ + MBEDTLS_SSL_DEBUG_MSG(3, ("will resume decryption of previously-read record")); + } else +#endif + if ((ret = mbedtls_ssl_read_record(ssl, 1)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + + p = ssl->in_msg + mbedtls_ssl_hs_hdr_len(ssl); + end = ssl->in_msg + ssl->in_hslen; + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + if (ssl->in_msg[0] != MBEDTLS_SSL_HS_CLIENT_KEY_EXCHANGE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_RSA) { + if ((ret = ssl_parse_client_dh_public(ssl, &p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_client_dh_public"), ret); + return ret; + } + + if (p != end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if ((ret = mbedtls_dhm_calc_secret(&ssl->handshake->dhm_ctx, + ssl->handshake->premaster, + MBEDTLS_PREMASTER_SIZE, + &ssl->handshake->pmslen, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_calc_secret", ret); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: K ", &ssl->handshake->dhm_ctx.K); + } else +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) || \ + defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_RSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_RSA || + ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t data_len = (size_t) (*p++); + size_t buf_len = (size_t) (end - p); + psa_status_t status = PSA_ERROR_GENERIC_ERROR; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + MBEDTLS_SSL_DEBUG_MSG(1, ("Read the peer's public key.")); + + /* + * We must have at least two bytes (1 for length, at least 1 for data) + */ + if (buf_len < 2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Invalid buffer length")); + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + if (data_len < 1 || data_len > buf_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Invalid data length")); + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + /* Store peer's ECDH public key. */ + memcpy(handshake->ecdh_psa_peerkey, p, data_len); + handshake->ecdh_psa_peerkey_len = data_len; + + /* Compute ECDH shared secret. */ + status = psa_raw_key_agreement( + PSA_ALG_ECDH, handshake->ecdh_psa_privkey, + handshake->ecdh_psa_peerkey, handshake->ecdh_psa_peerkey_len, + handshake->premaster, sizeof(handshake->premaster), + &handshake->pmslen); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_raw_key_agreement", ret); + if (handshake->ecdh_psa_privkey_is_external == 0) { + (void) psa_destroy_key(handshake->ecdh_psa_privkey); + } + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return ret; + } + + if (handshake->ecdh_psa_privkey_is_external == 0) { + status = psa_destroy_key(handshake->ecdh_psa_privkey); + + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_destroy_key", ret); + return ret; + } + } + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; +#else + if ((ret = mbedtls_ecdh_read_public(&ssl->handshake->ecdh_ctx, + p, end - p)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_read_public", ret); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_QP); + + if ((ret = mbedtls_ecdh_calc_secret(&ssl->handshake->ecdh_ctx, + &ssl->handshake->pmslen, + ssl->handshake->premaster, + MBEDTLS_MPI_MAX_SIZE, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_calc_secret", ret); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_Z); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED || + MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_PSK) { + if ((ret = ssl_parse_client_psk_identity(ssl, &p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_client_psk_identity"), ret); + return ret; + } + + if (p != end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + if ((ret = mbedtls_ssl_psk_derive_premaster(ssl, + ciphersuite_info->key_exchange)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_psk_derive_premaster", ret); + return ret; + } +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK) { +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + if (ssl->handshake->async_in_progress != 0) { + /* There is an asynchronous operation in progress to + * decrypt the encrypted premaster secret, so skip + * directly to resuming this operation. */ + MBEDTLS_SSL_DEBUG_MSG(3, ("PSK identity already parsed")); + /* Update p to skip the PSK identity. ssl_parse_encrypted_pms + * won't actually use it, but maintain p anyway for robustness. */ + p += ssl->conf->psk_identity_len + 2; + } else +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ + if ((ret = ssl_parse_client_psk_identity(ssl, &p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_client_psk_identity"), ret); + return ret; + } + + if ((ret = ssl_parse_encrypted_pms(ssl, p, end, 2)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_encrypted_pms"), ret); + return ret; + } + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + if ((ret = mbedtls_ssl_psk_derive_premaster(ssl, + ciphersuite_info->key_exchange)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_psk_derive_premaster", ret); + return ret; + } +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK) { + if ((ret = ssl_parse_client_psk_identity(ssl, &p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_client_psk_identity"), ret); + return ret; + } + if ((ret = ssl_parse_client_dh_public(ssl, &p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_client_dh_public"), ret); + return ret; + } + + if (p != end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + unsigned char *pms = ssl->handshake->premaster; + unsigned char *pms_end = pms + sizeof(ssl->handshake->premaster); + size_t pms_len; + + /* Write length only when we know the actual value */ + if ((ret = mbedtls_dhm_calc_secret(&ssl->handshake->dhm_ctx, + pms + 2, pms_end - (pms + 2), &pms_len, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_dhm_calc_secret", ret); + return ret; + } + MBEDTLS_PUT_UINT16_BE(pms_len, pms, 0); + pms += 2 + pms_len; + + MBEDTLS_SSL_DEBUG_MPI(3, "DHM: K ", &ssl->handshake->dhm_ctx.K); +#else + if ((ret = mbedtls_ssl_psk_derive_premaster(ssl, + ciphersuite_info->key_exchange)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_psk_derive_premaster", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t destruction_status = PSA_ERROR_CORRUPTION_DETECTED; + uint8_t ecpoint_len; + + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + if ((ret = ssl_parse_client_psk_identity(ssl, &p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_client_psk_identity"), ret); + psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return ret; + } + + /* Keep a copy of the peer's public key */ + if (p >= end) { + psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ecpoint_len = *(p++); + if ((size_t) (end - p) < ecpoint_len) { + psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + if (ecpoint_len > sizeof(handshake->ecdh_psa_peerkey)) { + psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + memcpy(handshake->ecdh_psa_peerkey, p, ecpoint_len); + handshake->ecdh_psa_peerkey_len = ecpoint_len; + p += ecpoint_len; + + /* As RFC 5489 section 2, the premaster secret is formed as follows: + * - a uint16 containing the length (in octets) of the ECDH computation + * - the octet string produced by the ECDH computation + * - a uint16 containing the length (in octets) of the PSK + * - the PSK itself + */ + unsigned char *psm = ssl->handshake->premaster; + const unsigned char * const psm_end = + psm + sizeof(ssl->handshake->premaster); + /* uint16 to store length (in octets) of the ECDH computation */ + const size_t zlen_size = 2; + size_t zlen = 0; + + /* Compute ECDH shared secret. */ + status = psa_raw_key_agreement(PSA_ALG_ECDH, + handshake->ecdh_psa_privkey, + handshake->ecdh_psa_peerkey, + handshake->ecdh_psa_peerkey_len, + psm + zlen_size, + psm_end - (psm + zlen_size), + &zlen); + + destruction_status = psa_destroy_key(handshake->ecdh_psa_privkey); + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } else if (destruction_status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(destruction_status); + } + + /* Write the ECDH computation length before the ECDH computation */ + MBEDTLS_PUT_UINT16_BE(zlen, psm, 0); + psm += zlen_size + zlen; + +#else /* MBEDTLS_USE_PSA_CRYPTO */ + if ((ret = ssl_parse_client_psk_identity(ssl, &p, end)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_client_psk_identity"), ret); + return ret; + } + + if ((ret = mbedtls_ecdh_read_public(&ssl->handshake->ecdh_ctx, + p, end - p)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecdh_read_public", ret); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_ECDH(3, &ssl->handshake->ecdh_ctx, + MBEDTLS_DEBUG_ECDH_QP); + + if ((ret = mbedtls_ssl_psk_derive_premaster(ssl, + ciphersuite_info->key_exchange)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_psk_derive_premaster", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA) { + if ((ret = ssl_parse_encrypted_pms(ssl, p, end, 0)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, ("ssl_parse_parse_encrypted_pms_secret"), ret); + return ret; + } + } else +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + if (ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if ((ret = mbedtls_psa_ecjpake_read_round( + &ssl->handshake->psa_pake_ctx, p, end - p, + MBEDTLS_ECJPAKE_ROUND_TWO)) != 0) { + psa_destroy_key(ssl->handshake->psa_pake_password); + psa_pake_abort(&ssl->handshake->psa_pake_ctx); + + MBEDTLS_SSL_DEBUG_RET(1, "psa_pake_input round two", ret); + return ret; + } +#else + ret = mbedtls_ecjpake_read_round_two(&ssl->handshake->ecjpake_ctx, + p, end - p); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_read_round_two", ret); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ret = mbedtls_ecjpake_derive_secret(&ssl->handshake->ecjpake_ctx, + ssl->handshake->premaster, 32, &ssl->handshake->pmslen, + ssl->conf->f_rng, ssl->conf->p_rng); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ecjpake_derive_secret", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + } else +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ + { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if ((ret = mbedtls_ssl_derive_keys(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_derive_keys", ret); + return ret; + } + + ssl->state++; + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse client key exchange")); + + return 0; +} + +#if !defined(MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_certificate_verify(mbedtls_ssl_context *ssl) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate verify")); + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate verify")); + ssl->state++; + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} +#else /* !MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_parse_certificate_verify(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + size_t i, sig_len; + unsigned char hash[48]; + unsigned char *hash_start = hash; + size_t hashlen; + mbedtls_pk_type_t pk_alg; + mbedtls_md_type_t md_alg; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + mbedtls_pk_context *peer_pk; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate verify")); + + if (!mbedtls_ssl_ciphersuite_cert_req_allowed(ciphersuite_info)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate verify")); + ssl->state++; + return 0; + } + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + if (ssl->session_negotiate->peer_cert == NULL) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate verify")); + ssl->state++; + return 0; + } +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (ssl->session_negotiate->peer_cert_digest == NULL) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip parse certificate verify")); + ssl->state++; + return 0; + } +#endif /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + /* Read the message without adding it to the checksum */ + ret = mbedtls_ssl_read_record(ssl, 0 /* no checksum update */); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_read_record"), ret); + return ret; + } + + ssl->state++; + + /* Process the message contents */ + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE || + ssl->in_msg[0] != MBEDTLS_SSL_HS_CERTIFICATE_VERIFY) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate verify message")); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + + i = mbedtls_ssl_hs_hdr_len(ssl); + +#if !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + peer_pk = &ssl->handshake->peer_pubkey; +#else /* !MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + if (ssl->session_negotiate->peer_cert == NULL) { + /* Should never happen */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + peer_pk = &ssl->session_negotiate->peer_cert->pk; +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ + + /* + * struct { + * SignatureAndHashAlgorithm algorithm; -- TLS 1.2 only + * opaque signature<0..2^16-1>; + * } DigitallySigned; + */ + if (i + 2 > ssl->in_hslen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate verify message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* + * Hash + */ + md_alg = mbedtls_ssl_md_alg_from_hash(ssl->in_msg[i]); + + if (md_alg == MBEDTLS_MD_NONE || mbedtls_ssl_set_calc_verify_md(ssl, ssl->in_msg[i])) { + MBEDTLS_SSL_DEBUG_MSG(1, ("peer not adhering to requested sig_alg" + " for verify message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + +#if !defined(MBEDTLS_MD_SHA1) + if (MBEDTLS_MD_SHA1 == md_alg) { + hash_start += 16; + } +#endif + + /* Info from md_alg will be used instead */ + hashlen = 0; + + i++; + + /* + * Signature + */ + if ((pk_alg = mbedtls_ssl_pk_alg_from_sig(ssl->in_msg[i])) + == MBEDTLS_PK_NONE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("peer not adhering to requested sig_alg" + " for verify message")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + /* + * Check the certificate's key type matches the signature alg + */ + if (!mbedtls_pk_can_do(peer_pk, pk_alg)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("sig_alg doesn't match cert key")); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + i++; + + if (i + 2 > ssl->in_hslen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate verify message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + sig_len = (ssl->in_msg[i] << 8) | ssl->in_msg[i+1]; + i += 2; + + if (i + sig_len != ssl->in_hslen) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate verify message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Calculate hash and verify signature */ + { + size_t dummy_hlen; + ret = ssl->handshake->calc_verify(ssl, hash, &dummy_hlen); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("calc_verify"), ret); + return ret; + } + } + + if ((ret = mbedtls_pk_verify(peer_pk, + md_alg, hash_start, hashlen, + ssl->in_msg + i, sig_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_pk_verify", ret); + return ret; + } + + ret = mbedtls_ssl_update_handshake_status(ssl); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_update_handshake_status"), ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse certificate verify")); + + return ret; +} +#endif /* MBEDTLS_KEY_EXCHANGE_CERT_REQ_ALLOWED_ENABLED */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_write_new_session_ticket(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t tlen; + uint32_t lifetime; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write new session ticket")); + + ssl->out_msgtype = MBEDTLS_SSL_MSG_HANDSHAKE; + ssl->out_msg[0] = MBEDTLS_SSL_HS_NEW_SESSION_TICKET; + + /* + * struct { + * uint32 ticket_lifetime_hint; + * opaque ticket<0..2^16-1>; + * } NewSessionTicket; + * + * 4 . 7 ticket_lifetime_hint (0 = unspecified) + * 8 . 9 ticket_len (n) + * 10 . 9+n ticket content + */ + + if ((ret = ssl->conf->f_ticket_write(ssl->conf->p_ticket, + ssl->session_negotiate, + ssl->out_msg + 10, + ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN, + &tlen, &lifetime)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_ticket_write", ret); + tlen = 0; + } + + MBEDTLS_PUT_UINT32_BE(lifetime, ssl->out_msg, 4); + MBEDTLS_PUT_UINT16_BE(tlen, ssl->out_msg, 8); + ssl->out_msglen = 10 + tlen; + + /* + * Morally equivalent to updating ssl->state, but NewSessionTicket and + * ChangeCipherSpec share the same state. + */ + ssl->handshake->new_session_ticket = 0; + + if ((ret = mbedtls_ssl_write_handshake_msg(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_write_handshake_msg", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write new session ticket")); + + return 0; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +/* + * SSL handshake -- server side -- single step + */ +int mbedtls_ssl_handshake_server_step(mbedtls_ssl_context *ssl) +{ + int ret = 0; + + MBEDTLS_SSL_DEBUG_MSG(2, ("server state: %d", ssl->state)); + + switch (ssl->state) { + case MBEDTLS_SSL_HELLO_REQUEST: + ssl->state = MBEDTLS_SSL_CLIENT_HELLO; + break; + + /* + * <== ClientHello + */ + case MBEDTLS_SSL_CLIENT_HELLO: + ret = ssl_parse_client_hello(ssl); + break; + +#if defined(MBEDTLS_SSL_PROTO_DTLS) + case MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT: + return MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED; +#endif + + /* + * ==> ServerHello + * Certificate + * ( ServerKeyExchange ) + * ( CertificateRequest ) + * ServerHelloDone + */ + case MBEDTLS_SSL_SERVER_HELLO: + ret = ssl_write_server_hello(ssl); + break; + + case MBEDTLS_SSL_SERVER_CERTIFICATE: + ret = mbedtls_ssl_write_certificate(ssl); + break; + + case MBEDTLS_SSL_SERVER_KEY_EXCHANGE: + ret = ssl_write_server_key_exchange(ssl); + break; + + case MBEDTLS_SSL_CERTIFICATE_REQUEST: + ret = ssl_write_certificate_request(ssl); + break; + + case MBEDTLS_SSL_SERVER_HELLO_DONE: + ret = ssl_write_server_hello_done(ssl); + break; + + /* + * <== ( Certificate/Alert ) + * ClientKeyExchange + * ( CertificateVerify ) + * ChangeCipherSpec + * Finished + */ + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + ret = mbedtls_ssl_parse_certificate(ssl); + break; + + case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE: + ret = ssl_parse_client_key_exchange(ssl); + break; + + case MBEDTLS_SSL_CERTIFICATE_VERIFY: + ret = ssl_parse_certificate_verify(ssl); + break; + + case MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC: + ret = mbedtls_ssl_parse_change_cipher_spec(ssl); + break; + + case MBEDTLS_SSL_CLIENT_FINISHED: + ret = mbedtls_ssl_parse_finished(ssl); + break; + + /* + * ==> ( NewSessionTicket ) + * ChangeCipherSpec + * Finished + */ + case MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC: +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (ssl->handshake->new_session_ticket != 0) { + ret = ssl_write_new_session_ticket(ssl); + } else +#endif + ret = mbedtls_ssl_write_change_cipher_spec(ssl); + break; + + case MBEDTLS_SSL_SERVER_FINISHED: + ret = mbedtls_ssl_write_finished(ssl); + break; + + case MBEDTLS_SSL_FLUSH_BUFFERS: + MBEDTLS_SSL_DEBUG_MSG(2, ("handshake: done")); + ssl->state = MBEDTLS_SSL_HANDSHAKE_WRAPUP; + break; + + case MBEDTLS_SSL_HANDSHAKE_WRAPUP: + mbedtls_ssl_handshake_wrapup(ssl); + break; + + default: + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid state %d", ssl->state)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + return ret; +} + +void mbedtls_ssl_conf_preference_order(mbedtls_ssl_config *conf, int order) +{ + conf->respect_cli_pref = order; +} + +#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_PROTO_TLS1_2 */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls13_client.c b/r5dev/thirdparty/mbedtls/ssl_tls13_client.c new file mode 100644 index 00000000..a7fecedf --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls13_client.c @@ -0,0 +1,3064 @@ +/* + * TLS 1.3 client-side functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS ( https://tls.mbed.org ) + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_CLI_C) && defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#include + +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/platform.h" + +#include "ssl_misc.h" +#include "ssl_client.h" +#include "ssl_tls13_keys.h" +#include "ssl_debug_helpers.h" + +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) + +/* Write extensions */ + +/* + * ssl_tls13_write_supported_versions_ext(): + * + * struct { + * ProtocolVersion versions<2..254>; + * } SupportedVersions; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_supported_versions_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + unsigned char versions_len = (ssl->handshake->min_tls_version <= + MBEDTLS_SSL_VERSION_TLS1_2) ? 4 : 2; + + *out_len = 0; + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding supported versions extension")); + + /* Check if we have space to write the extension: + * - extension_type (2 bytes) + * - extension_data_length (2 bytes) + * - versions_length (1 byte ) + * - versions (2 or 4 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 5 + versions_len); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS, p, 0); + MBEDTLS_PUT_UINT16_BE(versions_len + 1, p, 2); + p += 4; + + /* Length of versions */ + *p++ = versions_len; + + /* Write values of supported versions. + * They are defined by the configuration. + * Currently, we advertise only TLS 1.3 or both TLS 1.3 and TLS 1.2. + */ + mbedtls_ssl_write_version(p, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_VERSION_TLS1_3); + MBEDTLS_SSL_DEBUG_MSG(3, ("supported version: [3:4]")); + + + if (ssl->handshake->min_tls_version <= MBEDTLS_SSL_VERSION_TLS1_2) { + mbedtls_ssl_write_version(p + 2, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_VERSION_TLS1_2); + MBEDTLS_SSL_DEBUG_MSG(3, ("supported version: [3:3]")); + } + + *out_len = 5 + versions_len; + + mbedtls_ssl_tls13_set_hs_sent_ext_mask( + ssl, MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS); + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_supported_versions_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + ((void) ssl); + + MBEDTLS_SSL_CHK_BUF_READ_PTR(buf, end, 2); + if (mbedtls_ssl_read_version(buf, ssl->conf->transport) != + MBEDTLS_SSL_VERSION_TLS1_3) { + MBEDTLS_SSL_DEBUG_MSG(1, ("unexpected version")); + + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + if (&buf[2] != end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("supported_versions ext data length incorrect")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + return 0; +} + +#if defined(MBEDTLS_SSL_ALPN) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_alpn_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, size_t len) +{ + const unsigned char *p = buf; + const unsigned char *end = buf + len; + size_t protocol_name_list_len, protocol_name_len; + const unsigned char *protocol_name_list_end; + + /* If we didn't send it, the server shouldn't send it */ + if (ssl->conf->alpn_list == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* + * opaque ProtocolName<1..2^8-1>; + * + * struct { + * ProtocolName protocol_name_list<2..2^16-1> + * } ProtocolNameList; + * + * the "ProtocolNameList" MUST contain exactly one "ProtocolName" + */ + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + protocol_name_list_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, protocol_name_list_len); + protocol_name_list_end = p + protocol_name_list_len; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, protocol_name_list_end, 1); + protocol_name_len = *p++; + + /* Check that the server chosen protocol was in our list and save it */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, protocol_name_list_end, protocol_name_len); + for (const char **alpn = ssl->conf->alpn_list; *alpn != NULL; alpn++) { + if (protocol_name_len == strlen(*alpn) && + memcmp(p, *alpn, protocol_name_len) == 0) { + ssl->alpn_chosen = *alpn; + return 0; + } + } + + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; +} +#endif /* MBEDTLS_SSL_ALPN */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_reset_key_share(mbedtls_ssl_context *ssl) +{ + uint16_t group_id = ssl->handshake->offered_group_id; + + if (group_id == 0) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + +#if defined(MBEDTLS_ECDH_C) + if (mbedtls_ssl_tls13_named_group_is_ecdhe(group_id)) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + /* Destroy generated private key. */ + status = psa_destroy_key(ssl->handshake->ecdh_psa_privkey); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_destroy_key", ret); + return ret; + } + + ssl->handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; + return 0; + } else +#endif /* MBEDTLS_ECDH_C */ + if (0 /* other KEMs? */) { + /* Do something */ + } + + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} + +/* + * Functions for writing key_share extension. + */ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_get_default_group_id(mbedtls_ssl_context *ssl, + uint16_t *group_id) +{ + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + + +#if defined(MBEDTLS_ECDH_C) + const uint16_t *group_list = mbedtls_ssl_get_groups(ssl); + /* Pick first available ECDHE group compatible with TLS 1.3 */ + if (group_list == NULL) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + + for (; *group_list != 0; group_list++) { + if ((mbedtls_ssl_get_psa_curve_info_from_tls_id(*group_list, + NULL, NULL) == PSA_SUCCESS) && + mbedtls_ssl_tls13_named_group_is_ecdhe(*group_list)) { + *group_id = *group_list; + return 0; + } + } +#else + ((void) ssl); + ((void) group_id); +#endif /* MBEDTLS_ECDH_C */ + + /* + * Add DHE named groups here. + * Pick first available DHE group compatible with TLS 1.3 + */ + + return ret; +} + +/* + * ssl_tls13_write_key_share_ext + * + * Structure of key_share extension in ClientHello: + * + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + * struct { + * KeyShareEntry client_shares<0..2^16-1>; + * } KeyShareClientHello; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_key_share_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + unsigned char *client_shares; /* Start of client_shares */ + size_t client_shares_len; /* Length of client_shares */ + uint16_t group_id; + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + + *out_len = 0; + + /* Check if we have space for header and length fields: + * - extension_type (2 bytes) + * - extension_data_length (2 bytes) + * - client_shares_length (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6); + p += 6; + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello: adding key share extension")); + + /* HRR could already have requested something else. */ + group_id = ssl->handshake->offered_group_id; + if (!mbedtls_ssl_tls13_named_group_is_ecdhe(group_id) && + !mbedtls_ssl_tls13_named_group_is_dhe(group_id)) { + MBEDTLS_SSL_PROC_CHK(ssl_tls13_get_default_group_id(ssl, + &group_id)); + } + + /* + * Dispatch to type-specific key generation function. + * + * So far, we're only supporting ECDHE. With the introduction + * of PQC KEMs, we'll want to have multiple branches, one per + * type of KEM, and dispatch to the corresponding crypto. And + * only one key share entry is allowed. + */ + client_shares = p; +#if defined(MBEDTLS_ECDH_C) + if (mbedtls_ssl_tls13_named_group_is_ecdhe(group_id)) { + /* Pointer to group */ + unsigned char *group = p; + /* Length of key_exchange */ + size_t key_exchange_len = 0; + + /* Check there is space for header of KeyShareEntry + * - group (2 bytes) + * - key_exchange_length (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4); + p += 4; + ret = mbedtls_ssl_tls13_generate_and_write_ecdh_key_exchange( + ssl, group_id, p, end, &key_exchange_len); + p += key_exchange_len; + if (ret != 0) { + return ret; + } + + /* Write group */ + MBEDTLS_PUT_UINT16_BE(group_id, group, 0); + /* Write key_exchange_length */ + MBEDTLS_PUT_UINT16_BE(key_exchange_len, group, 2); + } else +#endif /* MBEDTLS_ECDH_C */ + if (0 /* other KEMs? */) { + /* Do something */ + } else { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Length of client_shares */ + client_shares_len = p - client_shares; + if (client_shares_len == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("No key share defined.")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + /* Write extension_type */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_KEY_SHARE, buf, 0); + /* Write extension_data_length */ + MBEDTLS_PUT_UINT16_BE(client_shares_len + 2, buf, 2); + /* Write client_shares_length */ + MBEDTLS_PUT_UINT16_BE(client_shares_len, buf, 4); + + /* Update offered_group_id field */ + ssl->handshake->offered_group_id = group_id; + + /* Output the total length of key_share extension. */ + *out_len = p - buf; + + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, key_share extension", buf, *out_len); + + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_KEY_SHARE); + +cleanup: + + return ret; +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED */ + +/* + * ssl_tls13_parse_hrr_key_share_ext() + * Parse key_share extension in Hello Retry Request + * + * struct { + * NamedGroup selected_group; + * } KeyShareHelloRetryRequest; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_hrr_key_share_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ +#if defined(MBEDTLS_ECDH_C) + const unsigned char *p = buf; + int selected_group; + int found = 0; + + const uint16_t *group_list = mbedtls_ssl_get_groups(ssl); + if (group_list == NULL) { + return MBEDTLS_ERR_SSL_BAD_CONFIG; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "key_share extension", p, end - buf); + + /* Read selected_group */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + selected_group = MBEDTLS_GET_UINT16_BE(p, 0); + MBEDTLS_SSL_DEBUG_MSG(3, ("selected_group ( %d )", selected_group)); + + /* Upon receipt of this extension in a HelloRetryRequest, the client + * MUST first verify that the selected_group field corresponds to a + * group which was provided in the "supported_groups" extension in the + * original ClientHello. + * The supported_group was based on the info in ssl->conf->group_list. + * + * If the server provided a key share that was not sent in the ClientHello + * then the client MUST abort the handshake with an "illegal_parameter" alert. + */ + for (; *group_list != 0; group_list++) { + if ((mbedtls_ssl_get_psa_curve_info_from_tls_id(*group_list, + NULL, NULL) == PSA_ERROR_NOT_SUPPORTED) || + *group_list != selected_group) { + continue; + } + + /* We found a match */ + found = 1; + break; + } + + /* Client MUST verify that the selected_group field does not + * correspond to a group which was provided in the "key_share" + * extension in the original ClientHello. If the server sent an + * HRR message with a key share already provided in the + * ClientHello then the client MUST abort the handshake with + * an "illegal_parameter" alert. + */ + if (found == 0 || selected_group == ssl->handshake->offered_group_id) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Invalid key share in HRR")); + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + /* Remember server's preference for next ClientHello */ + ssl->handshake->offered_group_id = selected_group; + + return 0; +#else + (void) ssl; + (void) buf; + (void) end; + return MBEDTLS_ERR_SSL_BAD_CONFIG; +#endif +} + +/* + * ssl_tls13_parse_key_share_ext() + * Parse key_share extension in Server Hello + * + * struct { + * KeyShareEntry server_share; + * } KeyShareServerHello; + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_key_share_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + uint16_t group, offered_group; + + /* ... + * NamedGroup group; (2 bytes) + * ... + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + group = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + /* Check that the chosen group matches the one we offered. */ + offered_group = ssl->handshake->offered_group_id; + if (offered_group != group) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("Invalid server key share, our group %u, their group %u", + (unsigned) offered_group, (unsigned) group)); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + +#if defined(MBEDTLS_ECDH_C) + if (mbedtls_ssl_tls13_named_group_is_ecdhe(group)) { + if (mbedtls_ssl_get_psa_curve_info_from_tls_id(group, NULL, NULL) + == PSA_ERROR_NOT_SUPPORTED) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Invalid TLS curve group id")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("ECDH curve: %s", + mbedtls_ssl_get_curve_name_from_tls_id(group))); + + ret = mbedtls_ssl_tls13_read_public_ecdhe_share(ssl, p, end - p); + if (ret != 0) { + return ret; + } + } else +#endif /* MBEDTLS_ECDH_C */ + if (0 /* other KEMs? */) { + /* Do something */ + } else { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + return ret; +} + +/* + * ssl_tls13_parse_cookie_ext() + * Parse cookie extension in Hello Retry Request + * + * struct { + * opaque cookie<1..2^16-1>; + * } Cookie; + * + * When sending a HelloRetryRequest, the server MAY provide a "cookie" + * extension to the client (this is an exception to the usual rule that + * the only extensions that may be sent are those that appear in the + * ClientHello). When sending the new ClientHello, the client MUST copy + * the contents of the extension received in the HelloRetryRequest into + * a "cookie" extension in the new ClientHello. Clients MUST NOT use + * cookies in their initial ClientHello in subsequent connections. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_cookie_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + uint16_t cookie_len; + const unsigned char *p = buf; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* Retrieve length field of cookie */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + cookie_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, cookie_len); + MBEDTLS_SSL_DEBUG_BUF(3, "cookie extension", p, cookie_len); + + mbedtls_free(handshake->cookie); + handshake->cookie_len = 0; + handshake->cookie = mbedtls_calloc(1, cookie_len); + if (handshake->cookie == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("alloc failed ( %ud bytes )", + cookie_len)); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + memcpy(handshake->cookie, p, cookie_len); + handshake->cookie_len = cookie_len; + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_cookie_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + *out_len = 0; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + if (handshake->cookie == NULL) { + MBEDTLS_SSL_DEBUG_MSG(3, ("no cookie to send; skip extension")); + return 0; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, cookie", + handshake->cookie, + handshake->cookie_len); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, handshake->cookie_len + 6); + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding cookie extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_COOKIE, p, 0); + MBEDTLS_PUT_UINT16_BE(handshake->cookie_len + 2, p, 2); + MBEDTLS_PUT_UINT16_BE(handshake->cookie_len, p, 4); + p += 6; + + /* Cookie */ + memcpy(p, handshake->cookie, handshake->cookie_len); + + *out_len = handshake->cookie_len + 6; + + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_COOKIE); + + return 0; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +/* + * ssl_tls13_write_psk_key_exchange_modes_ext() structure: + * + * enum { psk_ke( 0 ), psk_dhe_ke( 1 ), ( 255 ) } PskKeyExchangeMode; + * + * struct { + * PskKeyExchangeMode ke_modes<1..255>; + * } PskKeyExchangeModes; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_psk_key_exchange_modes_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + int ke_modes_len = 0; + + ((void) ke_modes_len); + *out_len = 0; + + /* Skip writing extension if no PSK key exchange mode + * is enabled in the config. + */ + if (!mbedtls_ssl_conf_tls13_some_psk_enabled(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(3, ("skip psk_key_exchange_modes extension")); + return 0; + } + + /* Require 7 bytes of data, otherwise fail, + * even if extension might be shorter. + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 7); + MBEDTLS_SSL_DEBUG_MSG( + 3, ("client hello, adding psk_key_exchange_modes extension")); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_PSK_KEY_EXCHANGE_MODES, p, 0); + + /* Skip extension length (2 bytes) and + * ke_modes length (1 byte) for now. + */ + p += 5; + + if (mbedtls_ssl_conf_tls13_psk_ephemeral_enabled(ssl)) { + *p++ = MBEDTLS_SSL_TLS1_3_PSK_MODE_ECDHE; + ke_modes_len++; + + MBEDTLS_SSL_DEBUG_MSG(4, ("Adding PSK-ECDHE key exchange mode")); + } + + if (mbedtls_ssl_conf_tls13_psk_enabled(ssl)) { + *p++ = MBEDTLS_SSL_TLS1_3_PSK_MODE_PURE; + ke_modes_len++; + + MBEDTLS_SSL_DEBUG_MSG(4, ("Adding pure PSK key exchange mode")); + } + + /* Now write the extension and ke_modes length */ + MBEDTLS_PUT_UINT16_BE(ke_modes_len + 1, buf, 2); + buf[4] = ke_modes_len; + + *out_len = p - buf; + + mbedtls_ssl_tls13_set_hs_sent_ext_mask( + ssl, MBEDTLS_TLS_EXT_PSK_KEY_EXCHANGE_MODES); + + return 0; +} + +static psa_algorithm_t ssl_tls13_get_ciphersuite_hash_alg(int ciphersuite) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = NULL; + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(ciphersuite); + + if (ciphersuite_info != NULL) { + return mbedtls_psa_translate_md(ciphersuite_info->mac); + } + + return PSA_ALG_NONE; +} + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +static int ssl_tls13_has_configured_ticket(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_session *session = ssl->session_negotiate; + return ssl->handshake->resume && + session != NULL && session->ticket != NULL && + mbedtls_ssl_conf_tls13_check_kex_modes( + ssl, mbedtls_ssl_session_get_ticket_flags( + session, MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL)); +} + +#if defined(MBEDTLS_SSL_EARLY_DATA) +static int ssl_tls13_early_data_has_valid_ticket(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_session *session = ssl->session_negotiate; + return ssl->handshake->resume && + session->tls_version == MBEDTLS_SSL_VERSION_TLS1_3 && + (session->ticket_flags & + MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_EARLY_DATA) && + mbedtls_ssl_tls13_cipher_suite_is_offered( + ssl, session->ciphersuite); +} +#endif + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_ticket_get_identity(mbedtls_ssl_context *ssl, + psa_algorithm_t *hash_alg, + const unsigned char **identity, + size_t *identity_len) +{ + mbedtls_ssl_session *session = ssl->session_negotiate; + + if (!ssl_tls13_has_configured_ticket(ssl)) { + return -1; + } + + *hash_alg = ssl_tls13_get_ciphersuite_hash_alg(session->ciphersuite); + *identity = session->ticket; + *identity_len = session->ticket_len; + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_ticket_get_psk(mbedtls_ssl_context *ssl, + psa_algorithm_t *hash_alg, + const unsigned char **psk, + size_t *psk_len) +{ + + mbedtls_ssl_session *session = ssl->session_negotiate; + + if (!ssl_tls13_has_configured_ticket(ssl)) { + return -1; + } + + *hash_alg = ssl_tls13_get_ciphersuite_hash_alg(session->ciphersuite); + *psk = session->resumption_key; + *psk_len = session->resumption_key_len; + + return 0; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_psk_get_identity(mbedtls_ssl_context *ssl, + psa_algorithm_t *hash_alg, + const unsigned char **identity, + size_t *identity_len) +{ + + if (!mbedtls_ssl_conf_has_static_psk(ssl->conf)) { + return -1; + } + + *hash_alg = PSA_ALG_SHA_256; + *identity = ssl->conf->psk_identity; + *identity_len = ssl->conf->psk_identity_len; + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_psk_get_psk(mbedtls_ssl_context *ssl, + psa_algorithm_t *hash_alg, + const unsigned char **psk, + size_t *psk_len) +{ + + if (!mbedtls_ssl_conf_has_static_psk(ssl->conf)) { + return -1; + } + + *hash_alg = PSA_ALG_SHA_256; + *psk = ssl->conf->psk; + *psk_len = ssl->conf->psk_len; + return 0; +} + +static int ssl_tls13_get_configured_psk_count(mbedtls_ssl_context *ssl) +{ + int configured_psk_count = 0; +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (ssl_tls13_has_configured_ticket(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(3, ("Ticket is configured")); + configured_psk_count++; + } +#endif + if (mbedtls_ssl_conf_has_static_psk(ssl->conf)) { + MBEDTLS_SSL_DEBUG_MSG(3, ("PSK is configured")); + configured_psk_count++; + } + return configured_psk_count; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_identity(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + const unsigned char *identity, + size_t identity_len, + uint32_t obfuscated_ticket_age, + size_t *out_len) +{ + ((void) ssl); + *out_len = 0; + + /* + * - identity_len (2 bytes) + * - identity (psk_identity_len bytes) + * - obfuscated_ticket_age (4 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(buf, end, 6 + identity_len); + + MBEDTLS_PUT_UINT16_BE(identity_len, buf, 0); + memcpy(buf + 2, identity, identity_len); + MBEDTLS_PUT_UINT32_BE(obfuscated_ticket_age, buf, 2 + identity_len); + + MBEDTLS_SSL_DEBUG_BUF(4, "write identity", buf, 6 + identity_len); + + *out_len = 6 + identity_len; + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_binder(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + int psk_type, + psa_algorithm_t hash_alg, + const unsigned char *psk, + size_t psk_len, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char binder_len; + unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t transcript_len = 0; + + *out_len = 0; + + binder_len = PSA_HASH_LENGTH(hash_alg); + + /* + * - binder_len (1 bytes) + * - binder (binder_len bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(buf, end, 1 + binder_len); + + buf[0] = binder_len; + + /* Get current state of handshake transcript. */ + ret = mbedtls_ssl_get_handshake_transcript( + ssl, mbedtls_hash_info_md_from_psa(hash_alg), + transcript, sizeof(transcript), &transcript_len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_create_psk_binder(ssl, hash_alg, + psk, psk_len, psk_type, + transcript, buf + 1); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_create_psk_binder", ret); + return ret; + } + MBEDTLS_SSL_DEBUG_BUF(4, "write binder", buf, 1 + binder_len); + + *out_len = 1 + binder_len; + + return 0; +} + +/* + * mbedtls_ssl_tls13_write_identities_of_pre_shared_key_ext() structure: + * + * struct { + * opaque identity<1..2^16-1>; + * uint32 obfuscated_ticket_age; + * } PskIdentity; + * + * opaque PskBinderEntry<32..255>; + * + * struct { + * PskIdentity identities<7..2^16-1>; + * PskBinderEntry binders<33..2^16-1>; + * } OfferedPsks; + * + * struct { + * select (Handshake.msg_type) { + * case client_hello: OfferedPsks; + * ... + * }; + * } PreSharedKeyExtension; + * + */ +int mbedtls_ssl_tls13_write_identities_of_pre_shared_key_ext( + mbedtls_ssl_context *ssl, unsigned char *buf, unsigned char *end, + size_t *out_len, size_t *binders_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int configured_psk_count = 0; + unsigned char *p = buf; + psa_algorithm_t hash_alg = PSA_ALG_NONE; + const unsigned char *identity; + size_t identity_len; + size_t l_binders_len = 0; + size_t output_len; + + *out_len = 0; + *binders_len = 0; + + /* Check if we have any PSKs to offer. If no, skip pre_shared_key */ + configured_psk_count = ssl_tls13_get_configured_psk_count(ssl); + if (configured_psk_count == 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("skip pre_shared_key extensions")); + return 0; + } + + MBEDTLS_SSL_DEBUG_MSG(4, ("Pre-configured PSK number = %d", + configured_psk_count)); + + /* Check if we have space to write the extension, binders included. + * - extension_type (2 bytes) + * - extension_data_len (2 bytes) + * - identities_len (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6); + p += 6; + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (ssl_tls13_ticket_get_identity( + ssl, &hash_alg, &identity, &identity_len) == 0) { +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t now = mbedtls_time(NULL); + mbedtls_ssl_session *session = ssl->session_negotiate; + uint32_t obfuscated_ticket_age = + (uint32_t) (now - session->ticket_received); + + /* + * The ticket timestamp is in seconds but the ticket age is in + * milliseconds. If the ticket was received at the end of a second and + * re-used here just at the beginning of the next second, the computed + * age `now - session->ticket_received` is equal to 1s thus 1000 ms + * while the actual age could be just a few milliseconds or tens of + * milliseconds. If the server has more accurate ticket timestamps + * (typically timestamps in milliseconds), as part of the processing of + * the ClientHello, it may compute a ticket lifetime smaller than the + * one computed here and potentially reject the ticket. To avoid that, + * remove one second to the ticket age if possible. + */ + if (obfuscated_ticket_age > 0) { + obfuscated_ticket_age -= 1; + } + + obfuscated_ticket_age *= 1000; + obfuscated_ticket_age += session->ticket_age_add; + + ret = ssl_tls13_write_identity(ssl, p, end, + identity, identity_len, + obfuscated_ticket_age, + &output_len); +#else + ret = ssl_tls13_write_identity(ssl, p, end, identity, identity_len, + 0, &output_len); +#endif /* MBEDTLS_HAVE_TIME */ + if (ret != 0) { + return ret; + } + + p += output_len; + l_binders_len += 1 + PSA_HASH_LENGTH(hash_alg); + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + + if (ssl_tls13_psk_get_identity( + ssl, &hash_alg, &identity, &identity_len) == 0) { + + ret = ssl_tls13_write_identity(ssl, p, end, identity, identity_len, 0, + &output_len); + if (ret != 0) { + return ret; + } + + p += output_len; + l_binders_len += 1 + PSA_HASH_LENGTH(hash_alg); + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("client hello, adding pre_shared_key extension, " + "omitting PSK binder list")); + + /* Take into account the two bytes for the length of the binders. */ + l_binders_len += 2; + /* Check if there is enough space for binders */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, l_binders_len); + + /* + * - extension_type (2 bytes) + * - extension_data_len (2 bytes) + * - identities_len (2 bytes) + */ + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_PRE_SHARED_KEY, buf, 0); + MBEDTLS_PUT_UINT16_BE(p - buf - 4 + l_binders_len, buf, 2); + MBEDTLS_PUT_UINT16_BE(p - buf - 6, buf, 4); + + *out_len = (p - buf) + l_binders_len; + *binders_len = l_binders_len; + + MBEDTLS_SSL_DEBUG_BUF(3, "pre_shared_key identities", buf, p - buf); + + return 0; +} + +int mbedtls_ssl_tls13_write_binders_of_pre_shared_key_ext( + mbedtls_ssl_context *ssl, unsigned char *buf, unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + psa_algorithm_t hash_alg = PSA_ALG_NONE; + const unsigned char *psk; + size_t psk_len; + size_t output_len; + + /* Check if we have space to write binders_len. + * - binders_len (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + p += 2; + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (ssl_tls13_ticket_get_psk(ssl, &hash_alg, &psk, &psk_len) == 0) { + + ret = ssl_tls13_write_binder(ssl, p, end, + MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION, + hash_alg, psk, psk_len, + &output_len); + if (ret != 0) { + return ret; + } + p += output_len; + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + + if (ssl_tls13_psk_get_psk(ssl, &hash_alg, &psk, &psk_len) == 0) { + + ret = ssl_tls13_write_binder(ssl, p, end, + MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL, + hash_alg, psk, psk_len, + &output_len); + if (ret != 0) { + return ret; + } + p += output_len; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("client hello, adding PSK binder list.")); + + /* + * - binders_len (2 bytes) + */ + MBEDTLS_PUT_UINT16_BE(p - buf - 2, buf, 0); + + MBEDTLS_SSL_DEBUG_BUF(3, "pre_shared_key binders", buf, p - buf); + + mbedtls_ssl_tls13_set_hs_sent_ext_mask( + ssl, MBEDTLS_TLS_EXT_PRE_SHARED_KEY); + + return 0; +} + +/* + * struct { + * opaque identity<1..2^16-1>; + * uint32 obfuscated_ticket_age; + * } PskIdentity; + * + * opaque PskBinderEntry<32..255>; + * + * struct { + * + * select (Handshake.msg_type) { + * ... + * case server_hello: uint16 selected_identity; + * }; + * + * } PreSharedKeyExtension; + * + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_server_pre_shared_key_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + int selected_identity; + const unsigned char *psk; + size_t psk_len; + psa_algorithm_t hash_alg; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(buf, end, 2); + selected_identity = MBEDTLS_GET_UINT16_BE(buf, 0); + ssl->handshake->selected_identity = (uint16_t) selected_identity; + + MBEDTLS_SSL_DEBUG_MSG(3, ("selected_identity = %d", selected_identity)); + + if (selected_identity >= ssl_tls13_get_configured_psk_count(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Invalid PSK identity.")); + + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (selected_identity == 0 && ssl_tls13_has_configured_ticket(ssl)) { + ret = ssl_tls13_ticket_get_psk(ssl, &hash_alg, &psk, &psk_len); + } else +#endif + if (mbedtls_ssl_conf_has_static_psk(ssl->conf)) { + ret = ssl_tls13_psk_get_psk(ssl, &hash_alg, &psk, &psk_len); + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + if (ret != 0) { + return ret; + } + + if (mbedtls_psa_translate_md(ssl->handshake->ciphersuite_info->mac) + != hash_alg) { + MBEDTLS_SSL_DEBUG_MSG( + 1, ("Invalid ciphersuite for external psk.")); + + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + ret = mbedtls_ssl_set_hs_psk(ssl, psk, psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); + return ret; + } + + return 0; +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + +int mbedtls_ssl_tls13_write_client_hello_exts(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + size_t ext_len; + + *out_len = 0; + + /* Write supported_versions extension + * + * Supported Versions Extension is mandatory with TLS 1.3. + */ + ret = ssl_tls13_write_supported_versions_ext(ssl, p, end, &ext_len); + if (ret != 0) { + return ret; + } + p += ext_len; + + /* Echo the cookie if the server provided one in its preceding + * HelloRetryRequest message. + */ + ret = ssl_tls13_write_cookie_ext(ssl, p, end, &ext_len); + if (ret != 0) { + return ret; + } + p += ext_len; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED) + if (mbedtls_ssl_conf_tls13_some_ephemeral_enabled(ssl)) { + ret = ssl_tls13_write_key_share_ext(ssl, p, end, &ext_len); + if (ret != 0) { + return ret; + } + p += ext_len; + } +#endif + +#if defined(MBEDTLS_SSL_EARLY_DATA) + if (mbedtls_ssl_conf_tls13_some_psk_enabled(ssl) && + ssl_tls13_early_data_has_valid_ticket(ssl) && + ssl->conf->early_data_enabled == MBEDTLS_SSL_EARLY_DATA_ENABLED) { + ret = mbedtls_ssl_tls13_write_early_data_ext(ssl, p, end, &ext_len); + if (ret != 0) { + return ret; + } + p += ext_len; + + /* Initializes the status to `rejected`. It will be updated to + * `accepted` if the EncryptedExtension message contain an early data + * indication extension. + */ + ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED; + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write early_data extension")); + ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT; + } +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + /* For PSK-based key exchange we need the pre_shared_key extension + * and the psk_key_exchange_modes extension. + * + * The pre_shared_key extension MUST be the last extension in the + * ClientHello. Servers MUST check that it is the last extension and + * otherwise fail the handshake with an "illegal_parameter" alert. + * + * Add the psk_key_exchange_modes extension. + */ + ret = ssl_tls13_write_psk_key_exchange_modes_ext(ssl, p, end, &ext_len); + if (ret != 0) { + return ret; + } + p += ext_len; +#endif + + *out_len = p - buf; + + return 0; +} + +int mbedtls_ssl_tls13_finalize_client_hello(mbedtls_ssl_context *ssl) +{ + ((void) ssl); + +#if defined(MBEDTLS_SSL_EARLY_DATA) + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_algorithm_t hash_alg = PSA_ALG_NONE; + const unsigned char *psk; + size_t psk_len; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + + if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED) { +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO); +#endif + MBEDTLS_SSL_DEBUG_MSG( + 1, ("Set hs psk for early data when writing the first psk")); + + ret = ssl_tls13_ticket_get_psk(ssl, &hash_alg, &psk, &psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "ssl_tls13_ticket_get_psk", ret); + return ret; + } + + ret = mbedtls_ssl_set_hs_psk(ssl, psk, psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); + return ret; + } + + /* + * Early data are going to be encrypted using the ciphersuite + * associated with the pre-shared key used for the handshake. + * Note that if the server rejects early data, the handshake + * based on the pre-shared key may complete successfully + * with a selected ciphersuite different from the ciphersuite + * associated with the pre-shared key. Only the hashes of the + * two ciphersuites have to be the same. In that case, the + * encrypted handshake data and application data are + * encrypted using a different ciphersuite than the one used for + * the rejected early data. + */ + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id( + ssl->session_negotiate->ciphersuite); + ssl->handshake->ciphersuite_info = ciphersuite_info; + + /* Enable psk and psk_ephemeral to make stage early happy */ + ssl->handshake->key_exchange_mode = + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL; + + /* Start the TLS 1.3 key schedule: + * Set the PSK and derive early secret. + */ + ret = mbedtls_ssl_tls13_key_schedule_stage_early(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "mbedtls_ssl_tls13_key_schedule_stage_early", ret); + return ret; + } + + /* Derive early data key material */ + ret = mbedtls_ssl_tls13_compute_early_transform(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "mbedtls_ssl_tls13_compute_early_transform", ret); + return ret; + } + + } +#endif /* MBEDTLS_SSL_EARLY_DATA */ + return 0; +} +/* + * Functions for parsing and processing Server Hello + */ + +/** + * \brief Detect if the ServerHello contains a supported_versions extension + * or not. + * + * \param[in] ssl SSL context + * \param[in] buf Buffer containing the ServerHello message + * \param[in] end End of the buffer containing the ServerHello message + * + * \return 0 if the ServerHello does not contain a supported_versions extension + * \return 1 if the ServerHello contains a supported_versions extension + * \return A negative value if an error occurred while parsing the ServerHello. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_is_supported_versions_ext_present( + mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + const unsigned char *p = buf; + size_t legacy_session_id_echo_len; + size_t extensions_len; + const unsigned char *extensions_end; + + /* + * Check there is enough data to access the legacy_session_id_echo vector + * length: + * - legacy_version 2 bytes + * - random MBEDTLS_SERVER_HELLO_RANDOM_LEN bytes + * - legacy_session_id_echo length 1 byte + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, MBEDTLS_SERVER_HELLO_RANDOM_LEN + 3); + p += MBEDTLS_SERVER_HELLO_RANDOM_LEN + 2; + legacy_session_id_echo_len = *p; + + /* + * Jump to the extensions, jumping over: + * - legacy_session_id_echo (legacy_session_id_echo_len + 1) bytes + * - cipher_suite 2 bytes + * - legacy_compression_method 1 byte + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, legacy_session_id_echo_len + 4); + p += legacy_session_id_echo_len + 4; + + /* Case of no extension */ + if (p == end) { + return 0; + } + + /* ... + * Extension extensions<6..2^16-1>; + * ... + * struct { + * ExtensionType extension_type; (2 bytes) + * opaque extension_data<0..2^16-1>; + * } Extension; + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + extensions_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + /* Check extensions do not go beyond the buffer of data. */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extensions_len); + extensions_end = p + extensions_len; + + while (p < extensions_end) { + unsigned int extension_type; + size_t extension_data_len; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, 4); + extension_type = MBEDTLS_GET_UINT16_BE(p, 0); + extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + + if (extension_type == MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS) { + return 1; + } + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, extension_data_len); + p += extension_data_len; + } + + return 0; +} + +/* Returns a negative value on failure, and otherwise + * - 1 if the last eight bytes of the ServerHello random bytes indicate that + * the server is TLS 1.3 capable but negotiating TLS 1.2 or below. + * - 0 otherwise + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_is_downgrade_negotiation(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + /* First seven bytes of the magic downgrade strings, see RFC 8446 4.1.3 */ + static const unsigned char magic_downgrade_string[] = + { 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44 }; + const unsigned char *last_eight_bytes_of_random; + unsigned char last_byte_of_random; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(buf, end, MBEDTLS_SERVER_HELLO_RANDOM_LEN + 2); + last_eight_bytes_of_random = buf + 2 + MBEDTLS_SERVER_HELLO_RANDOM_LEN - 8; + + if (memcmp(last_eight_bytes_of_random, + magic_downgrade_string, + sizeof(magic_downgrade_string)) == 0) { + last_byte_of_random = last_eight_bytes_of_random[7]; + return last_byte_of_random == 0 || + last_byte_of_random == 1; + } + + return 0; +} + +/* Returns a negative value on failure, and otherwise + * - SSL_SERVER_HELLO or + * - SSL_SERVER_HELLO_HRR + * to indicate which message is expected and to be parsed next. + */ +#define SSL_SERVER_HELLO 0 +#define SSL_SERVER_HELLO_HRR 1 +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_server_hello_is_hrr(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + + /* Check whether this message is a HelloRetryRequest ( HRR ) message. + * + * Server Hello and HRR are only distinguished by Random set to the + * special value of the SHA-256 of "HelloRetryRequest". + * + * struct { + * ProtocolVersion legacy_version = 0x0303; + * Random random; + * opaque legacy_session_id_echo<0..32>; + * CipherSuite cipher_suite; + * uint8 legacy_compression_method = 0; + * Extension extensions<6..2^16-1>; + * } ServerHello; + * + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(buf, end, + 2 + sizeof(mbedtls_ssl_tls13_hello_retry_request_magic)); + + if (memcmp(buf + 2, mbedtls_ssl_tls13_hello_retry_request_magic, + sizeof(mbedtls_ssl_tls13_hello_retry_request_magic)) == 0) { + return SSL_SERVER_HELLO_HRR; + } + + return SSL_SERVER_HELLO; +} + +/* + * Returns a negative value on failure, and otherwise + * - SSL_SERVER_HELLO or + * - SSL_SERVER_HELLO_HRR or + * - SSL_SERVER_HELLO_TLS1_2 + */ +#define SSL_SERVER_HELLO_TLS1_2 2 +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_preprocess_server_hello(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + MBEDTLS_SSL_PROC_CHK_NEG(ssl_tls13_is_supported_versions_ext_present( + ssl, buf, end)); + + if (ret == 0) { + MBEDTLS_SSL_PROC_CHK_NEG( + ssl_tls13_is_downgrade_negotiation(ssl, buf, end)); + + /* If the server is negotiating TLS 1.2 or below and: + * . we did not propose TLS 1.2 or + * . the server responded it is TLS 1.3 capable but negotiating a lower + * version of the protocol and thus we are under downgrade attack + * abort the handshake with an "illegal parameter" alert. + */ + if (handshake->min_tls_version > MBEDTLS_SSL_VERSION_TLS1_2 || ret) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + ssl->keep_current_message = 1; + ssl->tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_SERVER_HELLO, + buf, (size_t) (end - buf))); + + if (mbedtls_ssl_conf_tls13_some_ephemeral_enabled(ssl)) { + ret = ssl_tls13_reset_key_share(ssl); + if (ret != 0) { + return ret; + } + } + + return SSL_SERVER_HELLO_TLS1_2; + } + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + ssl->session_negotiate->endpoint = ssl->conf->endpoint; + ssl->session_negotiate->tls_version = ssl->tls_version; +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + + handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + ret = ssl_server_hello_is_hrr(ssl, buf, end); + switch (ret) { + case SSL_SERVER_HELLO: + MBEDTLS_SSL_DEBUG_MSG(2, ("received ServerHello message")); + break; + case SSL_SERVER_HELLO_HRR: + MBEDTLS_SSL_DEBUG_MSG(2, ("received HelloRetryRequest message")); + /* If a client receives a second + * HelloRetryRequest in the same connection (i.e., where the ClientHello + * was itself in response to a HelloRetryRequest), it MUST abort the + * handshake with an "unexpected_message" alert. + */ + if (handshake->hello_retry_request_count > 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Multiple HRRs received")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE, + MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE); + return MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + } + /* + * Clients must abort the handshake with an "illegal_parameter" + * alert if the HelloRetryRequest would not result in any change + * in the ClientHello. + * In a PSK only key exchange that what we expect. + */ + if (!mbedtls_ssl_conf_tls13_some_ephemeral_enabled(ssl)) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("Unexpected HRR in pure PSK key exchange.")); + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + handshake->hello_retry_request_count++; + + break; + } + +cleanup: + + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_check_server_hello_session_id_echo(mbedtls_ssl_context *ssl, + const unsigned char **buf, + const unsigned char *end) +{ + const unsigned char *p = *buf; + size_t legacy_session_id_echo_len; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 1); + legacy_session_id_echo_len = *p++; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, legacy_session_id_echo_len); + + /* legacy_session_id_echo */ + if (ssl->session_negotiate->id_len != legacy_session_id_echo_len || + memcmp(ssl->session_negotiate->id, p, legacy_session_id_echo_len) != 0) { + MBEDTLS_SSL_DEBUG_BUF(3, "Expected Session ID", + ssl->session_negotiate->id, + ssl->session_negotiate->id_len); + MBEDTLS_SSL_DEBUG_BUF(3, "Received Session ID", p, + legacy_session_id_echo_len); + + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + p += legacy_session_id_echo_len; + *buf = p; + + MBEDTLS_SSL_DEBUG_BUF(3, "Session ID", ssl->session_negotiate->id, + ssl->session_negotiate->id_len); + return 0; +} + +/* Parse ServerHello message and configure context + * + * struct { + * ProtocolVersion legacy_version = 0x0303; // TLS 1.2 + * Random random; + * opaque legacy_session_id_echo<0..32>; + * CipherSuite cipher_suite; + * uint8 legacy_compression_method = 0; + * Extension extensions<6..2^16-1>; + * } ServerHello; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_server_hello(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end, + int is_hrr) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + size_t extensions_len; + const unsigned char *extensions_end; + uint16_t cipher_suite; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + int fatal_alert = 0; + uint32_t allowed_extensions_mask; + int hs_msg_type = is_hrr ? MBEDTLS_SSL_TLS1_3_HS_HELLO_RETRY_REQUEST : + MBEDTLS_SSL_HS_SERVER_HELLO; + + /* + * Check there is space for minimal fields + * + * - legacy_version ( 2 bytes) + * - random (MBEDTLS_SERVER_HELLO_RANDOM_LEN bytes) + * - legacy_session_id_echo ( 1 byte ), minimum size + * - cipher_suite ( 2 bytes) + * - legacy_compression_method ( 1 byte ) + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, MBEDTLS_SERVER_HELLO_RANDOM_LEN + 6); + + MBEDTLS_SSL_DEBUG_BUF(4, "server hello", p, end - p); + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, version", p, 2); + + /* ... + * ProtocolVersion legacy_version = 0x0303; // TLS 1.2 + * ... + * with ProtocolVersion defined as: + * uint16 ProtocolVersion; + */ + if (mbedtls_ssl_read_version(p, ssl->conf->transport) != + MBEDTLS_SSL_VERSION_TLS1_2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Unsupported version of TLS.")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION, + MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION); + ret = MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + goto cleanup; + } + p += 2; + + /* ... + * Random random; + * ... + * with Random defined as: + * opaque Random[MBEDTLS_SERVER_HELLO_RANDOM_LEN]; + */ + if (!is_hrr) { + memcpy(&handshake->randbytes[MBEDTLS_CLIENT_HELLO_RANDOM_LEN], p, + MBEDTLS_SERVER_HELLO_RANDOM_LEN); + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, random bytes", + p, MBEDTLS_SERVER_HELLO_RANDOM_LEN); + } + p += MBEDTLS_SERVER_HELLO_RANDOM_LEN; + + /* ... + * opaque legacy_session_id_echo<0..32>; + * ... + */ + if (ssl_tls13_check_server_hello_session_id_echo(ssl, &p, end) != 0) { + fatal_alert = MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER; + goto cleanup; + } + + /* ... + * CipherSuite cipher_suite; + * ... + * with CipherSuite defined as: + * uint8 CipherSuite[2]; + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + cipher_suite = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(cipher_suite); + /* + * Check whether this ciphersuite is valid and offered. + */ + if ((mbedtls_ssl_validate_ciphersuite(ssl, ciphersuite_info, + ssl->tls_version, + ssl->tls_version) != 0) || + !mbedtls_ssl_tls13_cipher_suite_is_offered(ssl, cipher_suite)) { + fatal_alert = MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER; + } + /* + * If we received an HRR before and that the proposed selected + * ciphersuite in this server hello is not the same as the one + * proposed in the HRR, we abort the handshake and send an + * "illegal_parameter" alert. + */ + else if ((!is_hrr) && (handshake->hello_retry_request_count > 0) && + (cipher_suite != ssl->session_negotiate->ciphersuite)) { + fatal_alert = MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER; + } + + if (fatal_alert == MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER) { + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid ciphersuite(%04x) parameter", + cipher_suite)); + goto cleanup; + } + + /* Configure ciphersuites */ + mbedtls_ssl_optimize_checksum(ssl, ciphersuite_info); + + handshake->ciphersuite_info = ciphersuite_info; + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, chosen ciphersuite: ( %04x ) - %s", + cipher_suite, ciphersuite_info->name)); + +#if defined(MBEDTLS_HAVE_TIME) + ssl->session_negotiate->start = time(NULL); +#endif /* MBEDTLS_HAVE_TIME */ + + /* ... + * uint8 legacy_compression_method = 0; + * ... + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 1); + if (p[0] != MBEDTLS_SSL_COMPRESS_NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad legacy compression method")); + fatal_alert = MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER; + goto cleanup; + } + p++; + + /* ... + * Extension extensions<6..2^16-1>; + * ... + * struct { + * ExtensionType extension_type; (2 bytes) + * opaque extension_data<0..2^16-1>; + * } Extension; + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + extensions_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + /* Check extensions do not go beyond the buffer of data. */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extensions_len); + extensions_end = p + extensions_len; + + MBEDTLS_SSL_DEBUG_BUF(3, "server hello extensions", p, extensions_len); + + handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + allowed_extensions_mask = is_hrr ? + MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_HRR : + MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_SH; + + while (p < extensions_end) { + unsigned int extension_type; + size_t extension_data_len; + const unsigned char *extension_data_end; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, 4); + extension_type = MBEDTLS_GET_UINT16_BE(p, 0); + extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, extension_data_len); + extension_data_end = p + extension_data_len; + + ret = mbedtls_ssl_tls13_check_received_extension( + ssl, hs_msg_type, extension_type, allowed_extensions_mask); + if (ret != 0) { + return ret; + } + + switch (extension_type) { + case MBEDTLS_TLS_EXT_COOKIE: + + ret = ssl_tls13_parse_cookie_ext(ssl, + p, extension_data_end); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "ssl_tls13_parse_cookie_ext", + ret); + goto cleanup; + } + break; + + case MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS: + ret = ssl_tls13_parse_supported_versions_ext(ssl, + p, + extension_data_end); + if (ret != 0) { + goto cleanup; + } + break; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + case MBEDTLS_TLS_EXT_PRE_SHARED_KEY: + MBEDTLS_SSL_DEBUG_MSG(3, ("found pre_shared_key extension")); + + if ((ret = ssl_tls13_parse_server_pre_shared_key_ext( + ssl, p, extension_data_end)) != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, ("ssl_tls13_parse_server_pre_shared_key_ext"), ret); + return ret; + } + break; +#endif + + case MBEDTLS_TLS_EXT_KEY_SHARE: + MBEDTLS_SSL_DEBUG_MSG(3, ("found key_shares extension")); + if (!mbedtls_ssl_conf_tls13_some_ephemeral_enabled(ssl)) { + fatal_alert = MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT; + goto cleanup; + } + + if (is_hrr) { + ret = ssl_tls13_parse_hrr_key_share_ext(ssl, + p, extension_data_end); + } else { + ret = ssl_tls13_parse_key_share_ext(ssl, + p, extension_data_end); + } + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "ssl_tls13_parse_key_share_ext", + ret); + goto cleanup; + } + break; + + default: + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto cleanup; + } + + p += extension_data_len; + } + + MBEDTLS_SSL_PRINT_EXTS(3, hs_msg_type, handshake->received_extensions); + +cleanup: + + if (fatal_alert == MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT, + MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION); + ret = MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; + } else if (fatal_alert == MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + ret = MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + return ret; +} + +#if defined(MBEDTLS_DEBUG_C) +static const char *ssl_tls13_get_kex_mode_str(int mode) +{ + switch (mode) { + case MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK: + return "psk"; + case MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL: + return "ephemeral"; + case MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL: + return "psk_ephemeral"; + default: + return "unknown mode"; + } +} +#endif /* MBEDTLS_DEBUG_C */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_postprocess_server_hello(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* Determine the key exchange mode: + * 1) If both the pre_shared_key and key_share extensions were received + * then the key exchange mode is PSK with EPHEMERAL. + * 2) If only the pre_shared_key extension was received then the key + * exchange mode is PSK-only. + * 3) If only the key_share extension was received then the key + * exchange mode is EPHEMERAL-only. + */ + switch (handshake->received_extensions & + (MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY) | MBEDTLS_SSL_EXT_MASK(KEY_SHARE))) { + /* Only the pre_shared_key extension was received */ + case MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY): + handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK; + break; + + /* Only the key_share extension was received */ + case MBEDTLS_SSL_EXT_MASK(KEY_SHARE): + handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL; + break; + + /* Both the pre_shared_key and key_share extensions were received */ + case (MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY) | MBEDTLS_SSL_EXT_MASK(KEY_SHARE)): + handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL; + break; + + /* Neither pre_shared_key nor key_share extension was received */ + default: + MBEDTLS_SSL_DEBUG_MSG(1, ("Unknown key exchange.")); + ret = MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + goto cleanup; + } +#if defined(MBEDTLS_SSL_EARLY_DATA) + if (handshake->received_extensions & MBEDTLS_SSL_EXT_MASK(EARLY_DATA) && + (handshake->selected_identity != 0 || + handshake->ciphersuite_info->id != + ssl->session_negotiate->ciphersuite)) { + /* RFC8446 4.2.11 + * If the server supplies an "early_data" extension, the + * client MUST verify that the server's selected_identity + * is 0. If any other value is returned, the client MUST + * abort the handshake with an "illegal_parameter" alert. + * + * RFC 8446 4.2.10 + * In order to accept early data, the server MUST have accepted a PSK + * cipher suite and selected the first key offered in the client's + * "pre_shared_key" extension. In addition, it MUST verify that the + * following values are the same as those associated with the + * selected PSK: + * - The TLS version number + * - The selected cipher suite + * - The selected ALPN [RFC7301] protocol, if any + * + * We check here that when early data is involved the server + * selected the cipher suite associated to the pre-shared key + * as it must have. + */ + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } +#endif + + if (!mbedtls_ssl_conf_tls13_check_kex_modes( + ssl, handshake->key_exchange_mode)) { + ret = MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + MBEDTLS_SSL_DEBUG_MSG(2, + ("Key exchange mode(%s) is not supported.", + ssl_tls13_get_kex_mode_str(handshake->key_exchange_mode))); + goto cleanup; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("Selected key exchange mode: %s", + ssl_tls13_get_kex_mode_str(handshake->key_exchange_mode))); + + /* Start the TLS 1.3 key scheduling if not already done. + * + * If we proposed early data then we have already derived an + * early secret using the selected PSK and its associated hash. + * It means that if the negotiated key exchange mode is psk or + * psk_ephemeral, we have already correctly computed the + * early secret and thus we do not do it again. In all other + * cases we compute it here. + */ +#if defined(MBEDTLS_SSL_EARLY_DATA) + if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_NOT_SENT || + handshake->key_exchange_mode == + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL) +#endif + { + ret = mbedtls_ssl_tls13_key_schedule_stage_early(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "mbedtls_ssl_tls13_key_schedule_stage_early", ret); + goto cleanup; + } + } + + ret = mbedtls_ssl_tls13_compute_handshake_transform(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_tls13_compute_handshake_transform", + ret); + goto cleanup; + } + + mbedtls_ssl_set_inbound_transform(ssl, handshake->transform_handshake); + MBEDTLS_SSL_DEBUG_MSG(1, ("Switch to handshake keys for inbound traffic")); + ssl->session_negotiate->ciphersuite = handshake->ciphersuite_info->id; + ssl->session_in = ssl->session_negotiate; + +cleanup: + if (ret != 0) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + } + + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_postprocess_hrr(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + mbedtls_ssl_session_reset_msg_layer(ssl, 0); + + /* + * We are going to re-generate a shared secret corresponding to the group + * selected by the server, which is different from the group for which we + * generated a shared secret in the first client hello. + * Thus, reset the shared secret. + */ + ret = ssl_tls13_reset_key_share(ssl); + if (ret != 0) { + return ret; + } + + ssl->session_negotiate->ciphersuite = ssl->handshake->ciphersuite_info->id; + return 0; +} + +/* + * Wait and parse ServerHello handshake message. + * Handler for MBEDTLS_SSL_SERVER_HELLO + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_server_hello(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf = NULL; + size_t buf_len = 0; + int is_hrr = 0; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> %s", __func__)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_fetch_handshake_msg(ssl, + MBEDTLS_SSL_HS_SERVER_HELLO, + &buf, &buf_len)); + + ret = ssl_tls13_preprocess_server_hello(ssl, buf, buf + buf_len); + if (ret < 0) { + goto cleanup; + } else { + is_hrr = (ret == SSL_SERVER_HELLO_HRR); + } + + if (ret == SSL_SERVER_HELLO_TLS1_2) { + ret = 0; + goto cleanup; + } + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_parse_server_hello(ssl, buf, + buf + buf_len, + is_hrr)); + if (is_hrr) { + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_reset_transcript_for_hrr(ssl)); + } + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_SERVER_HELLO, buf, + buf_len)); + + if (is_hrr) { + MBEDTLS_SSL_PROC_CHK(ssl_tls13_postprocess_hrr(ssl)); +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + /* If not offering early data, the client sends a dummy CCS record + * immediately before its second flight. This may either be before + * its second ClientHello or before its encrypted handshake flight. + */ + mbedtls_ssl_handshake_set_state(ssl, + MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO); +#else + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO); +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + } else { + MBEDTLS_SSL_PROC_CHK(ssl_tls13_postprocess_server_hello(ssl)); + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS); + } + +cleanup: + MBEDTLS_SSL_DEBUG_MSG(2, ("<= %s ( %s )", __func__, + is_hrr ? "HelloRetryRequest" : "ServerHello")); + return ret; +} + +/* + * + * Handler for MBEDTLS_SSL_ENCRYPTED_EXTENSIONS + * + * The EncryptedExtensions message contains any extensions which + * should be protected, i.e., any which are not needed to establish + * the cryptographic context. + */ + +/* Parse EncryptedExtensions message + * struct { + * Extension extensions<0..2^16-1>; + * } EncryptedExtensions; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_encrypted_extensions(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = 0; + size_t extensions_len; + const unsigned char *p = buf; + const unsigned char *extensions_end; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + extensions_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extensions_len); + extensions_end = p + extensions_len; + + MBEDTLS_SSL_DEBUG_BUF(3, "encrypted extensions", p, extensions_len); + + handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + while (p < extensions_end) { + unsigned int extension_type; + size_t extension_data_len; + + /* + * struct { + * ExtensionType extension_type; (2 bytes) + * opaque extension_data<0..2^16-1>; + * } Extension; + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, 4); + extension_type = MBEDTLS_GET_UINT16_BE(p, 0); + extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, extension_data_len); + + ret = mbedtls_ssl_tls13_check_received_extension( + ssl, MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, extension_type, + MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_EE); + if (ret != 0) { + return ret; + } + + switch (extension_type) { +#if defined(MBEDTLS_SSL_ALPN) + case MBEDTLS_TLS_EXT_ALPN: + MBEDTLS_SSL_DEBUG_MSG(3, ("found alpn extension")); + + if ((ret = ssl_tls13_parse_alpn_ext(ssl, p, (size_t) extension_data_len)) != 0) { + return ret; + } + + break; +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_EARLY_DATA) + case MBEDTLS_TLS_EXT_EARLY_DATA: + + if (extension_data_len != 0) { + /* The message must be empty. */ + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + break; +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) + case MBEDTLS_TLS_EXT_RECORD_SIZE_LIMIT: + MBEDTLS_SSL_DEBUG_MSG(3, ("found record_size_limit extension")); + + ret = mbedtls_ssl_tls13_parse_record_size_limit_ext(ssl, p, p + extension_data_len); + + /* TODO: Return unconditionally here until we handle the record size limit correctly. + * Once handled correctly, only return in case of errors. */ + return ret; + + break; +#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */ + + default: + MBEDTLS_SSL_PRINT_EXT( + 3, MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, + extension_type, "( ignored )"); + break; + } + + p += extension_data_len; + } + + MBEDTLS_SSL_PRINT_EXTS(3, MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, + handshake->received_extensions); + + /* Check that we consumed all the message. */ + if (p != end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("EncryptedExtension lengths misaligned")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_encrypted_extensions(mbedtls_ssl_context *ssl) +{ + int ret; + unsigned char *buf; + size_t buf_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse encrypted extensions")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_fetch_handshake_msg(ssl, + MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, + &buf, &buf_len)); + + /* Process the message contents */ + MBEDTLS_SSL_PROC_CHK( + ssl_tls13_parse_encrypted_extensions(ssl, buf, buf + buf_len)); + +#if defined(MBEDTLS_SSL_EARLY_DATA) + if (ssl->handshake->received_extensions & + MBEDTLS_SSL_EXT_MASK(EARLY_DATA)) { + ssl->early_data_status = MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED; + } +#endif + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, + buf, buf_len)); + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + if (mbedtls_ssl_tls13_key_exchange_mode_with_psk(ssl)) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); + } else { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CERTIFICATE_REQUEST); + } +#else + ((void) ssl); + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); +#endif + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse encrypted extensions")); + return ret; + +} + +/* + * Handler for MBEDTLS_SSL_END_OF_EARLY_DATA + * + * RFC 8446 section 4.5 + * + * struct {} EndOfEarlyData; + * + * If the server sent an "early_data" extension in EncryptedExtensions, the + * client MUST send an EndOfEarlyData message after receiving the server + * Finished. Otherwise, the client MUST NOT send an EndOfEarlyData message. + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_end_of_early_data(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf = NULL; + size_t buf_len; + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write EndOfEarlyData")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg( + ssl, MBEDTLS_SSL_HS_END_OF_EARLY_DATA, + &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_hdr_to_checksum( + ssl, MBEDTLS_SSL_HS_END_OF_EARLY_DATA, 0)); + + MBEDTLS_SSL_PROC_CHK( + mbedtls_ssl_finish_handshake_msg(ssl, buf_len, 0)); + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE); + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write EndOfEarlyData")); + return ret; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +/* + * STATE HANDLING: CertificateRequest + * + */ +#define SSL_CERTIFICATE_REQUEST_EXPECT_REQUEST 0 +#define SSL_CERTIFICATE_REQUEST_SKIP 1 +/* Coordination: + * Deals with the ambiguity of not knowing if a CertificateRequest + * will be sent. Returns a negative code on failure, or + * - SSL_CERTIFICATE_REQUEST_EXPECT_REQUEST + * - SSL_CERTIFICATE_REQUEST_SKIP + * indicating if a Certificate Request is expected or not. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_certificate_request_coordinate(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + return ret; + } + ssl->keep_current_message = 1; + + if ((ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE) && + (ssl->in_msg[0] == MBEDTLS_SSL_HS_CERTIFICATE_REQUEST)) { + MBEDTLS_SSL_DEBUG_MSG(3, ("got a certificate request")); + return SSL_CERTIFICATE_REQUEST_EXPECT_REQUEST; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("got no certificate request")); + + return SSL_CERTIFICATE_REQUEST_SKIP; +} + +/* + * ssl_tls13_parse_certificate_request() + * Parse certificate request + * struct { + * opaque certificate_request_context<0..2^8-1>; + * Extension extensions<2..2^16-1>; + * } CertificateRequest; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_certificate_request(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + size_t certificate_request_context_len = 0; + size_t extensions_len = 0; + const unsigned char *extensions_end; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* ... + * opaque certificate_request_context<0..2^8-1> + * ... + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 1); + certificate_request_context_len = (size_t) p[0]; + p += 1; + + if (certificate_request_context_len > 0) { + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, certificate_request_context_len); + MBEDTLS_SSL_DEBUG_BUF(3, "Certificate Request Context", + p, certificate_request_context_len); + + handshake->certificate_request_context = + mbedtls_calloc(1, certificate_request_context_len); + if (handshake->certificate_request_context == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("buffer too small")); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + memcpy(handshake->certificate_request_context, p, + certificate_request_context_len); + p += certificate_request_context_len; + } + + /* ... + * Extension extensions<2..2^16-1>; + * ... + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + extensions_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extensions_len); + extensions_end = p + extensions_len; + + handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + while (p < extensions_end) { + unsigned int extension_type; + size_t extension_data_len; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, 4); + extension_type = MBEDTLS_GET_UINT16_BE(p, 0); + extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, extension_data_len); + + ret = mbedtls_ssl_tls13_check_received_extension( + ssl, MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, extension_type, + MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CR); + if (ret != 0) { + return ret; + } + + switch (extension_type) { + case MBEDTLS_TLS_EXT_SIG_ALG: + MBEDTLS_SSL_DEBUG_MSG(3, + ("found signature algorithms extension")); + ret = mbedtls_ssl_parse_sig_alg_ext(ssl, p, + p + extension_data_len); + if (ret != 0) { + return ret; + } + + break; + + default: + MBEDTLS_SSL_PRINT_EXT( + 3, MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, + extension_type, "( ignored )"); + break; + } + + p += extension_data_len; + } + + MBEDTLS_SSL_PRINT_EXTS(3, MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, + handshake->received_extensions); + + /* Check that we consumed all the message. */ + if (p != end) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("CertificateRequest misaligned")); + goto decode_error; + } + + /* RFC 8446 section 4.3.2 + * + * The "signature_algorithms" extension MUST be specified + */ + if ((handshake->received_extensions & MBEDTLS_SSL_EXT_MASK(SIG_ALG)) == 0) { + MBEDTLS_SSL_DEBUG_MSG(3, + ("no signature algorithms extension found")); + goto decode_error; + } + + ssl->handshake->client_auth = 1; + return 0; + +decode_error: + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; +} + +/* + * Handler for MBEDTLS_SSL_CERTIFICATE_REQUEST + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_certificate_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate request")); + + MBEDTLS_SSL_PROC_CHK_NEG(ssl_tls13_certificate_request_coordinate(ssl)); + + if (ret == SSL_CERTIFICATE_REQUEST_EXPECT_REQUEST) { + unsigned char *buf; + size_t buf_len; + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_fetch_handshake_msg(ssl, + MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, + &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_parse_certificate_request(ssl, + buf, buf + buf_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, + buf, buf_len)); + } else if (ret == SSL_CERTIFICATE_REQUEST_SKIP) { + ret = 0; + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto cleanup; + } + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_CERTIFICATE); + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse certificate request")); + return ret; +} + +/* + * Handler for MBEDTLS_SSL_SERVER_CERTIFICATE + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_server_certificate(mbedtls_ssl_context *ssl) +{ + int ret; + + ret = mbedtls_ssl_tls13_process_certificate(ssl); + if (ret != 0) { + return ret; + } + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CERTIFICATE_VERIFY); + return 0; +} + +/* + * Handler for MBEDTLS_SSL_CERTIFICATE_VERIFY + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_certificate_verify(mbedtls_ssl_context *ssl) +{ + int ret; + + ret = mbedtls_ssl_tls13_process_certificate_verify(ssl); + if (ret != 0) { + return ret; + } + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); + return 0; +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +/* + * Handler for MBEDTLS_SSL_SERVER_FINISHED + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_server_finished(mbedtls_ssl_context *ssl) +{ + int ret; + + ret = mbedtls_ssl_tls13_process_finished_message(ssl); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_compute_application_transform(ssl); + if (ret != 0) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return ret; + } + +#if defined(MBEDTLS_SSL_EARLY_DATA) + if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_ACCEPTED) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_END_OF_EARLY_DATA); + } else if (ssl->early_data_status == MBEDTLS_SSL_EARLY_DATA_STATUS_REJECTED) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE); + } else +#endif /* MBEDTLS_SSL_EARLY_DATA */ + { +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED); +#else + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE); +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + } + + return 0; +} + +/* + * Handler for MBEDTLS_SSL_CLIENT_CERTIFICATE + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_client_certificate(mbedtls_ssl_context *ssl) +{ + int non_empty_certificate_msg = 0; + + MBEDTLS_SSL_DEBUG_MSG(1, + ("Switch to handshake traffic keys for outbound traffic")); + mbedtls_ssl_set_outbound_transform(ssl, ssl->handshake->transform_handshake); + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + if (ssl->handshake->client_auth) { + int ret = mbedtls_ssl_tls13_write_certificate(ssl); + if (ret != 0) { + return ret; + } + + if (mbedtls_ssl_own_cert(ssl) != NULL) { + non_empty_certificate_msg = 1; + } + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("skip write certificate")); + } +#endif + + if (non_empty_certificate_msg) { + mbedtls_ssl_handshake_set_state(ssl, + MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY); + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("skip write certificate verify")); + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_FINISHED); + } + + return 0; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +/* + * Handler for MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_client_certificate_verify(mbedtls_ssl_context *ssl) +{ + int ret = mbedtls_ssl_tls13_write_certificate_verify(ssl); + + if (ret == 0) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_FINISHED); + } + + return ret; +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +/* + * Handler for MBEDTLS_SSL_CLIENT_FINISHED + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_client_finished(mbedtls_ssl_context *ssl) +{ + int ret; + + ret = mbedtls_ssl_tls13_write_finished_message(ssl); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_compute_resumption_master_secret(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_tls13_compute_resumption_master_secret ", ret); + return ret; + } + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_FLUSH_BUFFERS); + return 0; +} + +/* + * Handler for MBEDTLS_SSL_FLUSH_BUFFERS + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_flush_buffers(mbedtls_ssl_context *ssl) +{ + MBEDTLS_SSL_DEBUG_MSG(2, ("handshake: done")); + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_WRAPUP); + return 0; +} + +/* + * Handler for MBEDTLS_SSL_HANDSHAKE_WRAPUP + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_handshake_wrapup(mbedtls_ssl_context *ssl) +{ + + mbedtls_ssl_tls13_handshake_wrapup(ssl); + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_OVER); + return 0; +} + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_new_session_ticket_exts(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + const unsigned char *p = buf; + + + handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + while (p < end) { + unsigned int extension_type; + size_t extension_data_len; + int ret; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 4); + extension_type = MBEDTLS_GET_UINT16_BE(p, 0); + extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extension_data_len); + + ret = mbedtls_ssl_tls13_check_received_extension( + ssl, MBEDTLS_SSL_HS_NEW_SESSION_TICKET, extension_type, + MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_NST); + if (ret != 0) { + return ret; + } + + switch (extension_type) { +#if defined(MBEDTLS_SSL_EARLY_DATA) + case MBEDTLS_TLS_EXT_EARLY_DATA: + if (extension_data_len != 4) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + if (ssl->session != NULL) { + ssl->session->ticket_flags |= + MBEDTLS_SSL_TLS1_3_TICKET_ALLOW_EARLY_DATA; + } + break; +#endif /* MBEDTLS_SSL_EARLY_DATA */ + + default: + MBEDTLS_SSL_PRINT_EXT( + 3, MBEDTLS_SSL_HS_NEW_SESSION_TICKET, + extension_type, "( ignored )"); + break; + } + + p += extension_data_len; + } + + MBEDTLS_SSL_PRINT_EXTS(3, MBEDTLS_SSL_HS_NEW_SESSION_TICKET, + handshake->received_extensions); + + return 0; +} + +/* + * From RFC8446, page 74 + * + * struct { + * uint32 ticket_lifetime; + * uint32 ticket_age_add; + * opaque ticket_nonce<0..255>; + * opaque ticket<1..2^16-1>; + * Extension extensions<0..2^16-2>; + * } NewSessionTicket; + * + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_new_session_ticket(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + unsigned char **ticket_nonce, + size_t *ticket_nonce_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + mbedtls_ssl_session *session = ssl->session; + size_t ticket_len; + unsigned char *ticket; + size_t extensions_len; + + *ticket_nonce = NULL; + *ticket_nonce_len = 0; + /* + * ticket_lifetime 4 bytes + * ticket_age_add 4 bytes + * ticket_nonce_len 1 byte + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 9); + + session->ticket_lifetime = MBEDTLS_GET_UINT32_BE(p, 0); + MBEDTLS_SSL_DEBUG_MSG(3, + ("ticket_lifetime: %u", + (unsigned int) session->ticket_lifetime)); + + session->ticket_age_add = MBEDTLS_GET_UINT32_BE(p, 4); + MBEDTLS_SSL_DEBUG_MSG(3, + ("ticket_age_add: %u", + (unsigned int) session->ticket_age_add)); + + *ticket_nonce_len = p[8]; + p += 9; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, *ticket_nonce_len); + *ticket_nonce = p; + MBEDTLS_SSL_DEBUG_BUF(3, "ticket_nonce:", *ticket_nonce, *ticket_nonce_len); + p += *ticket_nonce_len; + + /* Ticket */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + ticket_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, ticket_len); + MBEDTLS_SSL_DEBUG_BUF(3, "received ticket", p, ticket_len); + + /* Check if we previously received a ticket already. */ + if (session->ticket != NULL || session->ticket_len > 0) { + mbedtls_free(session->ticket); + session->ticket = NULL; + session->ticket_len = 0; + } + + if ((ticket = mbedtls_calloc(1, ticket_len)) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("ticket alloc failed")); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + memcpy(ticket, p, ticket_len); + p += ticket_len; + session->ticket = ticket; + session->ticket_len = ticket_len; + + /* Clear all flags in ticket_flags */ + mbedtls_ssl_session_clear_ticket_flags( + session, MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK); + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + extensions_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extensions_len); + + MBEDTLS_SSL_DEBUG_BUF(3, "ticket extension", p, extensions_len); + + ret = ssl_tls13_parse_new_session_ticket_exts(ssl, p, p + extensions_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "ssl_tls13_parse_new_session_ticket_exts", + ret); + return ret; + } + + /* session has been updated, allow export */ + session->exported = 0; + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_postprocess_new_session_ticket(mbedtls_ssl_context *ssl, + unsigned char *ticket_nonce, + size_t ticket_nonce_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_session *session = ssl->session; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + psa_algorithm_t psa_hash_alg; + int hash_length; + +#if defined(MBEDTLS_HAVE_TIME) + /* Store ticket creation time */ + session->ticket_received = mbedtls_time(NULL); +#endif + + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(session->ciphersuite); + if (ciphersuite_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + psa_hash_alg = mbedtls_psa_translate_md(ciphersuite_info->mac); + hash_length = PSA_HASH_LENGTH(psa_hash_alg); + if (hash_length == -1 || + (size_t) hash_length > sizeof(session->resumption_key)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + + MBEDTLS_SSL_DEBUG_BUF(3, "resumption_master_secret", + session->app_secrets.resumption_master_secret, + hash_length); + + /* Compute resumption key + * + * HKDF-Expand-Label( resumption_master_secret, + * "resumption", ticket_nonce, Hash.length ) + */ + ret = mbedtls_ssl_tls13_hkdf_expand_label( + psa_hash_alg, + session->app_secrets.resumption_master_secret, + hash_length, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(resumption), + ticket_nonce, + ticket_nonce_len, + session->resumption_key, + hash_length); + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(2, + "Creating the ticket-resumed PSK failed", + ret); + return ret; + } + + session->resumption_key_len = hash_length; + + MBEDTLS_SSL_DEBUG_BUF(3, "Ticket-resumed PSK", + session->resumption_key, + session->resumption_key_len); + + /* Set ticket_flags depends on the selected key exchange modes */ + mbedtls_ssl_session_set_ticket_flags( + session, ssl->conf->tls13_kex_modes); + MBEDTLS_SSL_PRINT_TICKET_FLAGS(4, session->ticket_flags); + + return 0; +} + +/* + * Handler for MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_new_session_ticket(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + size_t buf_len; + unsigned char *ticket_nonce; + size_t ticket_nonce_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse new session ticket")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_fetch_handshake_msg( + ssl, MBEDTLS_SSL_HS_NEW_SESSION_TICKET, + &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_parse_new_session_ticket( + ssl, buf, buf + buf_len, + &ticket_nonce, &ticket_nonce_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_postprocess_new_session_ticket( + ssl, ticket_nonce, ticket_nonce_len)); + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_OVER); + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse new session ticket")); + return ret; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +int mbedtls_ssl_tls13_handshake_client_step(mbedtls_ssl_context *ssl) +{ + int ret = 0; + + switch (ssl->state) { + case MBEDTLS_SSL_HELLO_REQUEST: + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO); + break; + + case MBEDTLS_SSL_CLIENT_HELLO: + ret = mbedtls_ssl_write_client_hello(ssl); + break; + + case MBEDTLS_SSL_SERVER_HELLO: + ret = ssl_tls13_process_server_hello(ssl); + break; + + case MBEDTLS_SSL_ENCRYPTED_EXTENSIONS: + ret = ssl_tls13_process_encrypted_extensions(ssl); + break; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + case MBEDTLS_SSL_CERTIFICATE_REQUEST: + ret = ssl_tls13_process_certificate_request(ssl); + break; + + case MBEDTLS_SSL_SERVER_CERTIFICATE: + ret = ssl_tls13_process_server_certificate(ssl); + break; + + case MBEDTLS_SSL_CERTIFICATE_VERIFY: + ret = ssl_tls13_process_certificate_verify(ssl); + break; +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + + case MBEDTLS_SSL_SERVER_FINISHED: + ret = ssl_tls13_process_server_finished(ssl); + break; + + case MBEDTLS_SSL_END_OF_EARLY_DATA: + ret = ssl_tls13_write_end_of_early_data(ssl); + break; + + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + ret = ssl_tls13_write_client_certificate(ssl); + break; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + case MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY: + ret = ssl_tls13_write_client_certificate_verify(ssl); + break; +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + + case MBEDTLS_SSL_CLIENT_FINISHED: + ret = ssl_tls13_write_client_finished(ssl); + break; + + case MBEDTLS_SSL_FLUSH_BUFFERS: + ret = ssl_tls13_flush_buffers(ssl); + break; + + case MBEDTLS_SSL_HANDSHAKE_WRAPUP: + ret = ssl_tls13_handshake_wrapup(ssl); + break; + + /* + * Injection of dummy-CCS's for middlebox compatibility + */ +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + case MBEDTLS_SSL_CLIENT_CCS_BEFORE_2ND_CLIENT_HELLO: + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret == 0) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO); + } + break; + + case MBEDTLS_SSL_CLIENT_CCS_AFTER_SERVER_FINISHED: + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret == 0) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE); + } + break; + + case MBEDTLS_SSL_CLIENT_CCS_AFTER_CLIENT_HELLO: + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret == 0) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_HELLO); + +#if defined(MBEDTLS_SSL_EARLY_DATA) + MBEDTLS_SSL_DEBUG_MSG( + 1, ("Switch to early data keys for outbound traffic")); + mbedtls_ssl_set_outbound_transform( + ssl, ssl->handshake->transform_earlydata); +#endif + } + break; +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + case MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET: + ret = ssl_tls13_process_new_session_ticket(ssl); + if (ret != 0) { + break; + } + ret = MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET; + break; +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + + default: + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid state %d", ssl->state)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + return ret; +} + +#endif /* MBEDTLS_SSL_CLI_C && MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls13_generic.c b/r5dev/thirdparty/mbedtls/ssl_tls13_generic.c new file mode 100644 index 00000000..669a90a9 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls13_generic.c @@ -0,0 +1,1627 @@ +/* + * TLS 1.3 functionality shared between client and server + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_TLS_C) && defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#include + +#include "mbedtls/error.h" +#include "mbedtls/debug.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform.h" +#include "mbedtls/constant_time.h" +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" + +#include "ssl_misc.h" +#include "ssl_tls13_invasive.h" +#include "ssl_tls13_keys.h" +#include "ssl_debug_helpers.h" + +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" + +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) + +const uint8_t mbedtls_ssl_tls13_hello_retry_request_magic[ + MBEDTLS_SERVER_HELLO_RANDOM_LEN] = +{ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, + 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, + 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, + 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C }; + +int mbedtls_ssl_tls13_fetch_handshake_msg(mbedtls_ssl_context *ssl, + unsigned hs_type, + unsigned char **buf, + size_t *buf_len) +{ + int ret; + + if ((ret = mbedtls_ssl_read_record(ssl, 0)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_read_record", ret); + goto cleanup; + } + + if (ssl->in_msgtype != MBEDTLS_SSL_MSG_HANDSHAKE || + ssl->in_msg[0] != hs_type) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Receive unexpected handshake message.")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_UNEXPECTED_MESSAGE, + MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE); + ret = MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE; + goto cleanup; + } + + /* + * Jump handshake header (4 bytes, see Section 4 of RFC 8446). + * ... + * HandshakeType msg_type; + * uint24 length; + * ... + */ + *buf = ssl->in_msg + 4; + *buf_len = ssl->in_hslen - 4; + +cleanup: + + return ret; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +/* + * STATE HANDLING: Read CertificateVerify + */ +/* Macro to express the maximum length of the verify structure. + * + * The structure is computed per TLS 1.3 specification as: + * - 64 bytes of octet 32, + * - 33 bytes for the context string + * (which is either "TLS 1.3, client CertificateVerify" + * or "TLS 1.3, server CertificateVerify"), + * - 1 byte for the octet 0x0, which serves as a separator, + * - 32 or 48 bytes for the Transcript-Hash(Handshake Context, Certificate) + * (depending on the size of the transcript_hash) + * + * This results in a total size of + * - 130 bytes for a SHA256-based transcript hash, or + * (64 + 33 + 1 + 32 bytes) + * - 146 bytes for a SHA384-based transcript hash. + * (64 + 33 + 1 + 48 bytes) + * + */ +#define SSL_VERIFY_STRUCT_MAX_SIZE (64 + \ + 33 + \ + 1 + \ + MBEDTLS_TLS1_3_MD_MAX_SIZE \ + ) + +/* + * The ssl_tls13_create_verify_structure() creates the verify structure. + * As input, it requires the transcript hash. + * + * The caller has to ensure that the buffer has size at least + * SSL_VERIFY_STRUCT_MAX_SIZE bytes. + */ +static void ssl_tls13_create_verify_structure(const unsigned char *transcript_hash, + size_t transcript_hash_len, + unsigned char *verify_buffer, + size_t *verify_buffer_len, + int from) +{ + size_t idx; + + /* RFC 8446, Section 4.4.3: + * + * The digital signature [in the CertificateVerify message] is then + * computed over the concatenation of: + * - A string that consists of octet 32 (0x20) repeated 64 times + * - The context string + * - A single 0 byte which serves as the separator + * - The content to be signed + */ + memset(verify_buffer, 0x20, 64); + idx = 64; + + if (from == MBEDTLS_SSL_IS_CLIENT) { + memcpy(verify_buffer + idx, MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(client_cv)); + idx += MBEDTLS_SSL_TLS1_3_LBL_LEN(client_cv); + } else { /* from == MBEDTLS_SSL_IS_SERVER */ + memcpy(verify_buffer + idx, MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(server_cv)); + idx += MBEDTLS_SSL_TLS1_3_LBL_LEN(server_cv); + } + + verify_buffer[idx++] = 0x0; + + memcpy(verify_buffer + idx, transcript_hash, transcript_hash_len); + idx += transcript_hash_len; + + *verify_buffer_len = idx; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_certificate_verify(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end, + const unsigned char *verify_buffer, + size_t verify_buffer_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + uint16_t algorithm; + size_t signature_len; + mbedtls_pk_type_t sig_alg; + mbedtls_md_type_t md_alg; + psa_algorithm_t hash_alg = PSA_ALG_NONE; + unsigned char verify_hash[PSA_HASH_MAX_SIZE]; + size_t verify_hash_len; + + void const *options = NULL; +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + mbedtls_pk_rsassa_pss_options rsassa_pss_options; +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + + /* + * struct { + * SignatureScheme algorithm; + * opaque signature<0..2^16-1>; + * } CertificateVerify; + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + algorithm = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + /* RFC 8446 section 4.4.3 + * + * If the CertificateVerify message is sent by a server, the signature algorithm + * MUST be one offered in the client's "signature_algorithms" extension unless + * no valid certificate chain can be produced without unsupported algorithms + * + * RFC 8446 section 4.4.2.2 + * + * If the client cannot construct an acceptable chain using the provided + * certificates and decides to abort the handshake, then it MUST abort the handshake + * with an appropriate certificate-related alert (by default, "unsupported_certificate"). + * + * Check if algorithm is an offered signature algorithm. + */ + if (!mbedtls_ssl_sig_alg_is_offered(ssl, algorithm)) { + /* algorithm not in offered signature algorithms list */ + MBEDTLS_SSL_DEBUG_MSG(1, ("Received signature algorithm(%04x) is not " + "offered.", + (unsigned int) algorithm)); + goto error; + } + + if (mbedtls_ssl_get_pk_type_and_md_alg_from_sig_alg( + algorithm, &sig_alg, &md_alg) != 0) { + goto error; + } + + hash_alg = mbedtls_hash_info_psa_from_md(md_alg); + if (hash_alg == 0) { + goto error; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("Certificate Verify: Signature algorithm ( %04x )", + (unsigned int) algorithm)); + + /* + * Check the certificate's key type matches the signature alg + */ + if (!mbedtls_pk_can_do(&ssl->session_negotiate->peer_cert->pk, sig_alg)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("signature algorithm doesn't match cert key")); + goto error; + } + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + signature_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, signature_len); + + status = psa_hash_compute(hash_alg, + verify_buffer, + verify_buffer_len, + verify_hash, + sizeof(verify_hash), + &verify_hash_len); + if (status != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "hash computation PSA error", status); + goto error; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "verify hash", verify_hash, verify_hash_len); +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + if (sig_alg == MBEDTLS_PK_RSASSA_PSS) { + rsassa_pss_options.mgf1_hash_id = md_alg; + + rsassa_pss_options.expected_salt_len = PSA_HASH_LENGTH(hash_alg); + options = (const void *) &rsassa_pss_options; + } +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + + if ((ret = mbedtls_pk_verify_ext(sig_alg, options, + &ssl->session_negotiate->peer_cert->pk, + md_alg, verify_hash, verify_hash_len, + p, signature_len)) == 0) { + return 0; + } + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_pk_verify_ext", ret); + +error: + /* RFC 8446 section 4.4.3 + * + * If the verification fails, the receiver MUST terminate the handshake + * with a "decrypt_error" alert. + */ + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +int mbedtls_ssl_tls13_process_certificate_verify(mbedtls_ssl_context *ssl) +{ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char verify_buffer[SSL_VERIFY_STRUCT_MAX_SIZE]; + size_t verify_buffer_len; + unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t transcript_len; + unsigned char *buf; + size_t buf_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate verify")); + + MBEDTLS_SSL_PROC_CHK( + mbedtls_ssl_tls13_fetch_handshake_msg(ssl, + MBEDTLS_SSL_HS_CERTIFICATE_VERIFY, &buf, &buf_len)); + + /* Need to calculate the hash of the transcript first + * before reading the message since otherwise it gets + * included in the transcript + */ + ret = mbedtls_ssl_get_handshake_transcript(ssl, + ssl->handshake->ciphersuite_info->mac, + transcript, sizeof(transcript), + &transcript_len); + if (ret != 0) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR, + MBEDTLS_ERR_SSL_INTERNAL_ERROR); + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "handshake hash", transcript, transcript_len); + + /* Create verify structure */ + ssl_tls13_create_verify_structure(transcript, + transcript_len, + verify_buffer, + &verify_buffer_len, + (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) ? + MBEDTLS_SSL_IS_SERVER : + MBEDTLS_SSL_IS_CLIENT); + + /* Process the message contents */ + MBEDTLS_SSL_PROC_CHK(ssl_tls13_parse_certificate_verify(ssl, buf, + buf + buf_len, verify_buffer, + verify_buffer_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_CERTIFICATE_VERIFY, + buf, buf_len)); + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse certificate verify")); + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_process_certificate_verify", ret); + return ret; +#else + ((void) ssl); + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ +} + +/* + * + * STATE HANDLING: Incoming Certificate. + * + */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +/* + * Structure of Certificate message: + * + * enum { + * X509(0), + * RawPublicKey(2), + * (255) + * } CertificateType; + * + * struct { + * select (certificate_type) { + * case RawPublicKey: + * * From RFC 7250 ASN.1_subjectPublicKeyInfo * + * opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; + * case X509: + * opaque cert_data<1..2^24-1>; + * }; + * Extension extensions<0..2^16-1>; + * } CertificateEntry; + * + * struct { + * opaque certificate_request_context<0..2^8-1>; + * CertificateEntry certificate_list<0..2^24-1>; + * } Certificate; + * + */ + +/* Parse certificate chain send by the server. */ +MBEDTLS_CHECK_RETURN_CRITICAL +MBEDTLS_STATIC_TESTABLE +int mbedtls_ssl_tls13_parse_certificate(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t certificate_request_context_len = 0; + size_t certificate_list_len = 0; + const unsigned char *p = buf; + const unsigned char *certificate_list_end; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 4); + certificate_request_context_len = p[0]; + certificate_list_len = MBEDTLS_GET_UINT24_BE(p, 1); + p += 4; + + /* In theory, the certificate list can be up to 2^24 Bytes, but we don't + * support anything beyond 2^16 = 64K. + */ + if ((certificate_request_context_len != 0) || + (certificate_list_len >= 0x10000)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate message")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* In case we tried to reuse a session but it failed */ + if (ssl->session_negotiate->peer_cert != NULL) { + mbedtls_x509_crt_free(ssl->session_negotiate->peer_cert); + mbedtls_free(ssl->session_negotiate->peer_cert); + } + + if (certificate_list_len == 0) { + ssl->session_negotiate->peer_cert = NULL; + ret = 0; + goto exit; + } + + if ((ssl->session_negotiate->peer_cert = + mbedtls_calloc(1, sizeof(mbedtls_x509_crt))) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("alloc( %" MBEDTLS_PRINTF_SIZET " bytes ) failed", + sizeof(mbedtls_x509_crt))); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR, + MBEDTLS_ERR_SSL_ALLOC_FAILED); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + mbedtls_x509_crt_init(ssl->session_negotiate->peer_cert); + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, certificate_list_len); + certificate_list_end = p + certificate_list_len; + while (p < certificate_list_end) { + size_t cert_data_len, extensions_len; + const unsigned char *extensions_end; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, certificate_list_end, 3); + cert_data_len = MBEDTLS_GET_UINT24_BE(p, 0); + p += 3; + + /* In theory, the CRT can be up to 2^24 Bytes, but we don't support + * anything beyond 2^16 = 64K. Otherwise as in the TLS 1.2 code, + * check that we have a minimum of 128 bytes of data, this is not + * clear why we need that though. + */ + if ((cert_data_len < 128) || (cert_data_len >= 0x10000)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad Certificate message")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, certificate_list_end, cert_data_len); + ret = mbedtls_x509_crt_parse_der(ssl->session_negotiate->peer_cert, + p, cert_data_len); + + switch (ret) { + case 0: /*ok*/ + break; + case MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG + MBEDTLS_ERR_OID_NOT_FOUND: + /* Ignore certificate with an unknown algorithm: maybe a + prior certificate was already trusted. */ + break; + + case MBEDTLS_ERR_X509_ALLOC_FAILED: + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_INTERNAL_ERROR, + MBEDTLS_ERR_X509_ALLOC_FAILED); + MBEDTLS_SSL_DEBUG_RET(1, " mbedtls_x509_crt_parse_der", ret); + return ret; + + case MBEDTLS_ERR_X509_UNKNOWN_VERSION: + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT, + MBEDTLS_ERR_X509_UNKNOWN_VERSION); + MBEDTLS_SSL_DEBUG_RET(1, " mbedtls_x509_crt_parse_der", ret); + return ret; + + default: + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_BAD_CERT, + ret); + MBEDTLS_SSL_DEBUG_RET(1, " mbedtls_x509_crt_parse_der", ret); + return ret; + } + + p += cert_data_len; + + /* Certificate extensions length */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, certificate_list_end, 2); + extensions_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, certificate_list_end, extensions_len); + + extensions_end = p + extensions_len; + handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + while (p < extensions_end) { + unsigned int extension_type; + size_t extension_data_len; + + /* + * struct { + * ExtensionType extension_type; (2 bytes) + * opaque extension_data<0..2^16-1>; + * } Extension; + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, 4); + extension_type = MBEDTLS_GET_UINT16_BE(p, 0); + extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, extension_data_len); + + ret = mbedtls_ssl_tls13_check_received_extension( + ssl, MBEDTLS_SSL_HS_CERTIFICATE, extension_type, + MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CT); + if (ret != 0) { + return ret; + } + + switch (extension_type) { + default: + MBEDTLS_SSL_PRINT_EXT( + 3, MBEDTLS_SSL_HS_CERTIFICATE, + extension_type, "( ignored )"); + break; + } + + p += extension_data_len; + } + + MBEDTLS_SSL_PRINT_EXTS(3, MBEDTLS_SSL_HS_CERTIFICATE, + handshake->received_extensions); + } + +exit: + /* Check that all the message is consumed. */ + if (p != end) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad Certificate message")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_CRT(3, "peer certificate", ssl->session_negotiate->peer_cert); + + return ret; +} +#else +MBEDTLS_CHECK_RETURN_CRITICAL +MBEDTLS_STATIC_TESTABLE +int mbedtls_ssl_tls13_parse_certificate(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + ((void) ssl); + ((void) buf); + ((void) end); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +/* Validate certificate chain sent by the server. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_validate_certificate(mbedtls_ssl_context *ssl) +{ + int ret = 0; + int authmode = MBEDTLS_SSL_VERIFY_REQUIRED; + mbedtls_x509_crt *ca_chain; + mbedtls_x509_crl *ca_crl; + const char *ext_oid; + size_t ext_len; + uint32_t verify_result = 0; + + /* If SNI was used, overwrite authentication mode + * from the configuration. */ +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET) { + authmode = ssl->handshake->sni_authmode; + } else +#endif + authmode = ssl->conf->authmode; + } +#endif + + /* + * If the peer hasn't sent a certificate ( i.e. it sent + * an empty certificate chain ), this is reflected in the peer CRT + * structure being unset. + * Check for that and handle it depending on the + * authentication mode. + */ + if (ssl->session_negotiate->peer_cert == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("peer has no certificate")); + +#if defined(MBEDTLS_SSL_SRV_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER) { + /* The client was asked for a certificate but didn't send + * one. The client should know what's going on, so we + * don't send an alert. + */ + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_MISSING; + if (authmode == MBEDTLS_SSL_VERIFY_OPTIONAL) { + return 0; + } else { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_NO_CERT, + MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE); + return MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE; + } + } +#endif /* MBEDTLS_SSL_SRV_C */ + +#if defined(MBEDTLS_SSL_CLI_C) + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_NO_CERT, + MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE); + return MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE; + } +#endif /* MBEDTLS_SSL_CLI_C */ + } + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_ca_chain != NULL) { + ca_chain = ssl->handshake->sni_ca_chain; + ca_crl = ssl->handshake->sni_ca_crl; + } else +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + { + ca_chain = ssl->conf->ca_chain; + ca_crl = ssl->conf->ca_crl; + } + + /* + * Main check: verify certificate + */ + ret = mbedtls_x509_crt_verify_with_profile( + ssl->session_negotiate->peer_cert, + ca_chain, ca_crl, + ssl->conf->cert_profile, + ssl->hostname, + &verify_result, + ssl->conf->f_vrfy, ssl->conf->p_vrfy); + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "x509_verify_cert", ret); + } + + /* + * Secondary checks: always done, but change 'ret' only if it was 0 + */ + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT) { + ext_oid = MBEDTLS_OID_SERVER_AUTH; + ext_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH); + } else { + ext_oid = MBEDTLS_OID_CLIENT_AUTH; + ext_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH); + } + + if ((mbedtls_x509_crt_check_key_usage( + ssl->session_negotiate->peer_cert, + MBEDTLS_X509_KU_DIGITAL_SIGNATURE) != 0) || + (mbedtls_x509_crt_check_extended_key_usage( + ssl->session_negotiate->peer_cert, + ext_oid, ext_len) != 0)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad certificate (usage extensions)")); + if (ret == 0) { + ret = MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } + } + + /* mbedtls_x509_crt_verify_with_profile is supposed to report a + * verification failure through MBEDTLS_ERR_X509_CERT_VERIFY_FAILED, + * with details encoded in the verification flags. All other kinds + * of error codes, including those from the user provided f_vrfy + * functions, are treated as fatal and lead to a failure of + * mbedtls_ssl_tls13_parse_certificate even if verification was optional. + */ + if (authmode == MBEDTLS_SSL_VERIFY_OPTIONAL && + (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED || + ret == MBEDTLS_ERR_SSL_BAD_CERTIFICATE)) { + ret = 0; + } + + if (ca_chain == NULL && authmode == MBEDTLS_SSL_VERIFY_REQUIRED) { + MBEDTLS_SSL_DEBUG_MSG(1, ("got no CA chain")); + ret = MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED; + } + + if (ret != 0) { + /* The certificate may have been rejected for several reasons. + Pick one and send the corresponding alert. Which alert to send + may be a subject of debate in some cases. */ + if (verify_result & MBEDTLS_X509_BADCERT_OTHER) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED, ret); + } else if (verify_result & MBEDTLS_X509_BADCERT_CN_MISMATCH) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_BAD_CERT, ret); + } else if (verify_result & (MBEDTLS_X509_BADCERT_KEY_USAGE | + MBEDTLS_X509_BADCERT_EXT_KEY_USAGE | + MBEDTLS_X509_BADCERT_NS_CERT_TYPE | + MBEDTLS_X509_BADCERT_BAD_PK | + MBEDTLS_X509_BADCERT_BAD_KEY)) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_CERT, ret); + } else if (verify_result & MBEDTLS_X509_BADCERT_EXPIRED) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_CERT_EXPIRED, ret); + } else if (verify_result & MBEDTLS_X509_BADCERT_REVOKED) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_CERT_REVOKED, ret); + } else if (verify_result & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA, ret); + } else { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_CERT_UNKNOWN, ret); + } + } + +#if defined(MBEDTLS_DEBUG_C) + if (verify_result != 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("! Certificate verification flags %08x", + (unsigned int) verify_result)); + } else { + MBEDTLS_SSL_DEBUG_MSG(3, ("Certificate verification flags clear")); + } +#endif /* MBEDTLS_DEBUG_C */ + + ssl->session_negotiate->verify_result = verify_result; + return ret; +} +#else /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_validate_certificate(mbedtls_ssl_context *ssl) +{ + ((void) ssl); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +int mbedtls_ssl_tls13_process_certificate(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse certificate")); + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + unsigned char *buf; + size_t buf_len; + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_fetch_handshake_msg( + ssl, MBEDTLS_SSL_HS_CERTIFICATE, + &buf, &buf_len)); + + /* Parse the certificate chain sent by the peer. */ + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_parse_certificate(ssl, buf, + buf + buf_len)); + /* Validate the certificate chain and set the verification results. */ + MBEDTLS_SSL_PROC_CHK(ssl_tls13_validate_certificate(ssl)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_CERTIFICATE, buf, + buf_len)); + +cleanup: +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse certificate")); + return ret; +} +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +/* + * enum { + * X509(0), + * RawPublicKey(2), + * (255) + * } CertificateType; + * + * struct { + * select (certificate_type) { + * case RawPublicKey: + * // From RFC 7250 ASN.1_subjectPublicKeyInfo + * opaque ASN1_subjectPublicKeyInfo<1..2^24-1>; + * + * case X509: + * opaque cert_data<1..2^24-1>; + * }; + * Extension extensions<0..2^16-1>; + * } CertificateEntry; + * + * struct { + * opaque certificate_request_context<0..2^8-1>; + * CertificateEntry certificate_list<0..2^24-1>; + * } Certificate; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_certificate_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + const mbedtls_x509_crt *crt = mbedtls_ssl_own_cert(ssl); + unsigned char *p = buf; + unsigned char *certificate_request_context = + ssl->handshake->certificate_request_context; + unsigned char certificate_request_context_len = + ssl->handshake->certificate_request_context_len; + unsigned char *p_certificate_list_len; + + + /* ... + * opaque certificate_request_context<0..2^8-1>; + * ... + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, certificate_request_context_len + 1); + *p++ = certificate_request_context_len; + if (certificate_request_context_len > 0) { + memcpy(p, certificate_request_context, certificate_request_context_len); + p += certificate_request_context_len; + } + + /* ... + * CertificateEntry certificate_list<0..2^24-1>; + * ... + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 3); + p_certificate_list_len = p; + p += 3; + + MBEDTLS_SSL_DEBUG_CRT(3, "own certificate", crt); + + while (crt != NULL) { + size_t cert_data_len = crt->raw.len; + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, cert_data_len + 3 + 2); + MBEDTLS_PUT_UINT24_BE(cert_data_len, p, 0); + p += 3; + + memcpy(p, crt->raw.p, cert_data_len); + p += cert_data_len; + crt = crt->next; + + /* Currently, we don't have any certificate extensions defined. + * Hence, we are sending an empty extension with length zero. + */ + MBEDTLS_PUT_UINT16_BE(0, p, 0); + p += 2; + } + + MBEDTLS_PUT_UINT24_BE(p - p_certificate_list_len - 3, + p_certificate_list_len, 0); + + *out_len = p - buf; + + MBEDTLS_SSL_PRINT_EXTS( + 3, MBEDTLS_SSL_HS_CERTIFICATE, ssl->handshake->sent_extensions); + + return 0; +} + +int mbedtls_ssl_tls13_write_certificate(mbedtls_ssl_context *ssl) +{ + int ret; + unsigned char *buf; + size_t buf_len, msg_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg(ssl, + MBEDTLS_SSL_HS_CERTIFICATE, &buf, + &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_certificate_body(ssl, + buf, + buf + buf_len, + &msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_CERTIFICATE, buf, + msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg( + ssl, buf_len, msg_len)); +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write certificate")); + return ret; +} + +/* + * STATE HANDLING: Output Certificate Verify + */ +int mbedtls_ssl_tls13_check_sig_alg_cert_key_match(uint16_t sig_alg, + mbedtls_pk_context *key) +{ + mbedtls_pk_type_t pk_type = mbedtls_ssl_sig_from_pk(key); + size_t key_size = mbedtls_pk_get_bitlen(key); + + switch (pk_type) { + case MBEDTLS_SSL_SIG_ECDSA: + switch (key_size) { + case 256: + return + sig_alg == MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256; + + case 384: + return + sig_alg == MBEDTLS_TLS1_3_SIG_ECDSA_SECP384R1_SHA384; + + case 521: + return + sig_alg == MBEDTLS_TLS1_3_SIG_ECDSA_SECP521R1_SHA512; + default: + break; + } + break; + + case MBEDTLS_SSL_SIG_RSA: + switch (sig_alg) { + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256: /* Intentional fallthrough */ + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384: /* Intentional fallthrough */ + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512: + return 1; + + default: + break; + } + break; + + default: + break; + } + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_certificate_verify_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + mbedtls_pk_context *own_key; + + unsigned char handshake_hash[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t handshake_hash_len; + unsigned char verify_buffer[SSL_VERIFY_STRUCT_MAX_SIZE]; + size_t verify_buffer_len; + + uint16_t *sig_alg = ssl->handshake->received_sig_algs; + size_t signature_len = 0; + + *out_len = 0; + + own_key = mbedtls_ssl_own_key(ssl); + if (own_key == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ret = mbedtls_ssl_get_handshake_transcript(ssl, + ssl->handshake->ciphersuite_info->mac, + handshake_hash, + sizeof(handshake_hash), + &handshake_hash_len); + if (ret != 0) { + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "handshake hash", + handshake_hash, + handshake_hash_len); + + ssl_tls13_create_verify_structure(handshake_hash, handshake_hash_len, + verify_buffer, &verify_buffer_len, + ssl->conf->endpoint); + + /* + * struct { + * SignatureScheme algorithm; + * opaque signature<0..2^16-1>; + * } CertificateVerify; + */ + /* Check there is space for the algorithm identifier (2 bytes) and the + * signature length (2 bytes). + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4); + + for (; *sig_alg != MBEDTLS_TLS1_3_SIG_NONE; sig_alg++) { + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + mbedtls_pk_type_t pk_type = MBEDTLS_PK_NONE; + mbedtls_md_type_t md_alg = MBEDTLS_MD_NONE; + psa_algorithm_t psa_algorithm = PSA_ALG_NONE; + unsigned char verify_hash[PSA_HASH_MAX_SIZE]; + size_t verify_hash_len; + + if (!mbedtls_ssl_sig_alg_is_offered(ssl, *sig_alg)) { + continue; + } + + if (!mbedtls_ssl_tls13_sig_alg_for_cert_verify_is_supported(*sig_alg)) { + continue; + } + + if (!mbedtls_ssl_tls13_check_sig_alg_cert_key_match(*sig_alg, own_key)) { + continue; + } + + if (mbedtls_ssl_get_pk_type_and_md_alg_from_sig_alg( + *sig_alg, &pk_type, &md_alg) != 0) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Hash verify buffer with indicated hash function */ + psa_algorithm = mbedtls_hash_info_psa_from_md(md_alg); + status = psa_hash_compute(psa_algorithm, + verify_buffer, + verify_buffer_len, + verify_hash, sizeof(verify_hash), + &verify_hash_len); + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } + + MBEDTLS_SSL_DEBUG_BUF(3, "verify hash", verify_hash, verify_hash_len); + + if ((ret = mbedtls_pk_sign_ext(pk_type, own_key, + md_alg, verify_hash, verify_hash_len, + p + 4, (size_t) (end - (p + 4)), &signature_len, + ssl->conf->f_rng, ssl->conf->p_rng)) != 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("CertificateVerify signature failed with %s", + mbedtls_ssl_sig_alg_to_str(*sig_alg))); + MBEDTLS_SSL_DEBUG_RET(2, "mbedtls_pk_sign_ext", ret); + + /* The signature failed. This is possible if the private key + * was not suitable for the signature operation as purposely we + * did not check its suitability completely. Let's try with + * another signature algorithm. + */ + continue; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("CertificateVerify signature with %s", + mbedtls_ssl_sig_alg_to_str(*sig_alg))); + + break; + } + + if (*sig_alg == MBEDTLS_TLS1_3_SIG_NONE) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no suitable signature algorithm")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + MBEDTLS_PUT_UINT16_BE(*sig_alg, p, 0); + MBEDTLS_PUT_UINT16_BE(signature_len, p, 2); + + *out_len = 4 + signature_len; + + return 0; +} + +int mbedtls_ssl_tls13_write_certificate_verify(mbedtls_ssl_context *ssl) +{ + int ret = 0; + unsigned char *buf; + size_t buf_len, msg_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate verify")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg(ssl, + MBEDTLS_SSL_HS_CERTIFICATE_VERIFY, &buf, + &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_certificate_verify_body( + ssl, buf, buf + buf_len, &msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_CERTIFICATE_VERIFY, buf, + msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg( + ssl, buf_len, msg_len)); + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write certificate verify")); + return ret; +} + +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +/* + * + * STATE HANDLING: Incoming Finished message. + */ +/* + * Implementation + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_preprocess_finished_message(mbedtls_ssl_context *ssl) +{ + int ret; + + ret = mbedtls_ssl_tls13_calculate_verify_data(ssl, + ssl->handshake->state_local.finished_in.digest, + sizeof(ssl->handshake->state_local.finished_in. + digest), + &ssl->handshake->state_local.finished_in.digest_len, + ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT ? + MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_calculate_verify_data", ret); + return ret; + } + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_finished_message(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + /* + * struct { + * opaque verify_data[Hash.length]; + * } Finished; + */ + const unsigned char *expected_verify_data = + ssl->handshake->state_local.finished_in.digest; + size_t expected_verify_data_len = + ssl->handshake->state_local.finished_in.digest_len; + /* Structural validation */ + if ((size_t) (end - buf) != expected_verify_data_len) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad finished message")); + + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "verify_data (self-computed):", + expected_verify_data, + expected_verify_data_len); + MBEDTLS_SSL_DEBUG_BUF(4, "verify_data (received message):", buf, + expected_verify_data_len); + + /* Semantic validation */ + if (mbedtls_ct_memcmp(buf, + expected_verify_data, + expected_verify_data_len) != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad finished message")); + + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + return 0; +} + +int mbedtls_ssl_tls13_process_finished_message(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + size_t buf_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse finished message")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_fetch_handshake_msg(ssl, + MBEDTLS_SSL_HS_FINISHED, + &buf, &buf_len)); + + /* Preprocessing step: Compute handshake digest */ + MBEDTLS_SSL_PROC_CHK(ssl_tls13_preprocess_finished_message(ssl)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_parse_finished_message(ssl, buf, buf + buf_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_FINISHED, buf, buf_len)); + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse finished message")); + return ret; +} + +/* + * + * STATE HANDLING: Write and send Finished message. + * + */ +/* + * Implement + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_prepare_finished_message(mbedtls_ssl_context *ssl) +{ + int ret; + + /* Compute transcript of handshake up to now. */ + ret = mbedtls_ssl_tls13_calculate_verify_data(ssl, + ssl->handshake->state_local.finished_out.digest, + sizeof(ssl->handshake->state_local.finished_out. + digest), + &ssl->handshake->state_local.finished_out.digest_len, + ssl->conf->endpoint); + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "calculate_verify_data failed", ret); + return ret; + } + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_finished_message_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + size_t verify_data_len = ssl->handshake->state_local.finished_out.digest_len; + /* + * struct { + * opaque verify_data[Hash.length]; + * } Finished; + */ + MBEDTLS_SSL_CHK_BUF_PTR(buf, end, verify_data_len); + + memcpy(buf, ssl->handshake->state_local.finished_out.digest, + verify_data_len); + + *out_len = verify_data_len; + return 0; +} + +/* Main entry point: orchestrates the other functions */ +int mbedtls_ssl_tls13_write_finished_message(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + size_t buf_len, msg_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write finished message")); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_prepare_finished_message(ssl)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg(ssl, + MBEDTLS_SSL_HS_FINISHED, &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_finished_message_body( + ssl, buf, buf + buf_len, &msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum(ssl, + MBEDTLS_SSL_HS_FINISHED, buf, msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg( + ssl, buf_len, msg_len)); +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write finished message")); + return ret; +} + +void mbedtls_ssl_tls13_handshake_wrapup(mbedtls_ssl_context *ssl) +{ + + MBEDTLS_SSL_DEBUG_MSG(3, ("=> handshake wrapup")); + + MBEDTLS_SSL_DEBUG_MSG(1, ("Switch to application keys for inbound traffic")); + mbedtls_ssl_set_inbound_transform(ssl, ssl->transform_application); + + MBEDTLS_SSL_DEBUG_MSG(1, ("Switch to application keys for outbound traffic")); + mbedtls_ssl_set_outbound_transform(ssl, ssl->transform_application); + + /* + * Free the previous session and switch to the current one. + */ + if (ssl->session) { + mbedtls_ssl_session_free(ssl->session); + mbedtls_free(ssl->session); + } + ssl->session = ssl->session_negotiate; + ssl->session_negotiate = NULL; + + MBEDTLS_SSL_DEBUG_MSG(3, ("<= handshake wrapup")); +} + +/* + * + * STATE HANDLING: Write ChangeCipherSpec + * + */ +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_change_cipher_spec_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *olen) +{ + ((void) ssl); + + MBEDTLS_SSL_CHK_BUF_PTR(buf, end, 1); + buf[0] = 1; + *olen = 1; + + return 0; +} + +int mbedtls_ssl_tls13_write_change_cipher_spec(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write change cipher spec")); + + /* Write CCS message */ + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_change_cipher_spec_body( + ssl, ssl->out_msg, + ssl->out_msg + MBEDTLS_SSL_OUT_CONTENT_LEN, + &ssl->out_msglen)); + + ssl->out_msgtype = MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC; + + /* Dispatch message */ + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_write_record(ssl, 0)); + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write change cipher spec")); + return ret; +} + +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + +/* Early Data Indication Extension + * + * struct { + * select ( Handshake.msg_type ) { + * ... + * case client_hello: Empty; + * case encrypted_extensions: Empty; + * }; + * } EarlyDataIndication; + */ +#if defined(MBEDTLS_SSL_EARLY_DATA) +int mbedtls_ssl_tls13_write_early_data_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *out_len) +{ + unsigned char *p = buf; + *out_len = 0; + ((void) ssl); + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_EARLY_DATA, p, 0); + MBEDTLS_PUT_UINT16_BE(0, p, 2); + + *out_len = 4; + + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_EARLY_DATA); + + return 0; +} +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +/* Reset SSL context and update hash for handling HRR. + * + * Replace Transcript-Hash(X) by + * Transcript-Hash( message_hash || + * 00 00 Hash.length || + * X ) + * A few states of the handshake are preserved, including: + * - session ID + * - session ticket + * - negotiated ciphersuite + */ +int mbedtls_ssl_reset_transcript_for_hrr(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char hash_transcript[PSA_HASH_MAX_SIZE + 4]; + size_t hash_len; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = + ssl->handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(3, ("Reset SSL session for HRR")); + + ret = mbedtls_ssl_get_handshake_transcript(ssl, ciphersuite_info->mac, + hash_transcript + 4, + PSA_HASH_MAX_SIZE, + &hash_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_get_handshake_transcript", ret); + return ret; + } + + hash_transcript[0] = MBEDTLS_SSL_HS_MESSAGE_HASH; + hash_transcript[1] = 0; + hash_transcript[2] = 0; + hash_transcript[3] = (unsigned char) hash_len; + + hash_len += 4; + + MBEDTLS_SSL_DEBUG_BUF(4, "Truncated handshake transcript", + hash_transcript, hash_len); + + /* Reset running hash and replace it with a hash of the transcript */ + ret = mbedtls_ssl_reset_checksum(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_reset_checksum", ret); + return ret; + } + ret = ssl->handshake->update_checksum(ssl, hash_transcript, hash_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "update_checksum", ret); + return ret; + } + + return ret; +} + +#if defined(MBEDTLS_ECDH_C) + +int mbedtls_ssl_tls13_read_public_ecdhe_share(mbedtls_ssl_context *ssl, + const unsigned char *buf, + size_t buf_len) +{ + uint8_t *p = (uint8_t *) buf; + const uint8_t *end = buf + buf_len; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* Get size of the TLS opaque key_exchange field of the KeyShareEntry struct. */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + uint16_t peerkey_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + /* Check if key size is consistent with given buffer length. */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, peerkey_len); + + /* Store peer's ECDH public key. */ + memcpy(handshake->ecdh_psa_peerkey, p, peerkey_len); + handshake->ecdh_psa_peerkey_len = peerkey_len; + + return 0; +} + +int mbedtls_ssl_tls13_generate_and_write_ecdh_key_exchange( + mbedtls_ssl_context *ssl, + uint16_t named_group, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + psa_status_t status = PSA_ERROR_GENERIC_ERROR; + int ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + psa_key_attributes_t key_attributes; + size_t own_pubkey_len; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + psa_ecc_family_t ec_psa_family = 0; + size_t ec_bits = 0; + + MBEDTLS_SSL_DEBUG_MSG(1, ("Perform PSA-based ECDH computation.")); + + /* Convert EC's TLS ID to PSA key type. */ + if (mbedtls_ssl_get_psa_curve_info_from_tls_id(named_group, + &ec_psa_family, + &ec_bits) == PSA_ERROR_NOT_SUPPORTED) { + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + handshake->ecdh_psa_type = PSA_KEY_TYPE_ECC_KEY_PAIR(ec_psa_family); + ssl->handshake->ecdh_bits = ec_bits; + + key_attributes = psa_key_attributes_init(); + psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDH); + psa_set_key_type(&key_attributes, handshake->ecdh_psa_type); + psa_set_key_bits(&key_attributes, handshake->ecdh_bits); + + /* Generate ECDH private key. */ + status = psa_generate_key(&key_attributes, + &handshake->ecdh_psa_privkey); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_generate_key", ret); + return ret; + + } + + /* Export the public part of the ECDH private key from PSA. */ + status = psa_export_public_key(handshake->ecdh_psa_privkey, + buf, (size_t) (end - buf), + &own_pubkey_len); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_export_public_key", ret); + return ret; + + } + + *out_len = own_pubkey_len; + + return 0; +} +#endif /* MBEDTLS_ECDH_C */ + +/* RFC 8446 section 4.2 + * + * If an implementation receives an extension which it recognizes and which is + * not specified for the message in which it appears, it MUST abort the handshake + * with an "illegal_parameter" alert. + * + */ +int mbedtls_ssl_tls13_check_received_extension( + mbedtls_ssl_context *ssl, + int hs_msg_type, + unsigned int received_extension_type, + uint32_t hs_msg_allowed_extensions_mask) +{ + uint32_t extension_mask = mbedtls_ssl_get_extension_mask( + received_extension_type); + + MBEDTLS_SSL_PRINT_EXT( + 3, hs_msg_type, received_extension_type, "received"); + + if ((extension_mask & hs_msg_allowed_extensions_mask) == 0) { + MBEDTLS_SSL_PRINT_EXT( + 3, hs_msg_type, received_extension_type, "is illegal"); + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + ssl->handshake->received_extensions |= extension_mask; + /* + * If it is a message containing extension responses, check that we + * previously sent the extension. + */ + switch (hs_msg_type) { + case MBEDTLS_SSL_HS_SERVER_HELLO: + case MBEDTLS_SSL_TLS1_3_HS_HELLO_RETRY_REQUEST: + case MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS: + case MBEDTLS_SSL_HS_CERTIFICATE: + /* Check if the received extension is sent by peer message.*/ + if ((ssl->handshake->sent_extensions & extension_mask) != 0) { + return 0; + } + break; + default: + return 0; + } + + MBEDTLS_SSL_PRINT_EXT( + 3, hs_msg_type, received_extension_type, "is unsupported"); + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT, + MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION); + return MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; +} + +#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) +/* RFC 8449, section 4: + * + * The ExtensionData of the "record_size_limit" extension is + * RecordSizeLimit: + * uint16 RecordSizeLimit; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_parse_record_size_limit_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + const unsigned char *p = buf; + uint16_t record_size_limit; + const size_t extension_data_len = end - buf; + + if (extension_data_len != MBEDTLS_SSL_RECORD_SIZE_LIMIT_EXTENSION_DATA_LENGTH) { + MBEDTLS_SSL_DEBUG_MSG(2, + ("record_size_limit extension has invalid length: %" + MBEDTLS_PRINTF_SIZET " Bytes", + extension_data_len)); + + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + record_size_limit = MBEDTLS_GET_UINT16_BE(p, 0); + + MBEDTLS_SSL_DEBUG_MSG(2, ("RecordSizeLimit: %u Bytes", record_size_limit)); + + /* RFC 8449, section 4 + * + * Endpoints MUST NOT send a "record_size_limit" extension with a value + * smaller than 64. An endpoint MUST treat receipt of a smaller value + * as a fatal error and generate an "illegal_parameter" alert. + */ + if (record_size_limit < MBEDTLS_SSL_RECORD_SIZE_LIMIT_MIN) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + MBEDTLS_SSL_DEBUG_MSG(2, + ( + "record_size_limit extension is still in development. Aborting handshake.")); + + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_UNSUPPORTED_EXT, + MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION); + return MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION; +} +#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */ + +#endif /* MBEDTLS_SSL_TLS_C && MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls13_invasive.h b/r5dev/thirdparty/mbedtls/ssl_tls13_invasive.h new file mode 100644 index 00000000..3fb79a95 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls13_invasive.h @@ -0,0 +1,35 @@ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_SSL_TLS13_INVASIVE_H +#define MBEDTLS_SSL_TLS13_INVASIVE_H + +#include "common.h" + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#include "psa/crypto.h" + +#if defined(MBEDTLS_TEST_HOOKS) +int mbedtls_ssl_tls13_parse_certificate(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end); +#endif /* MBEDTLS_TEST_HOOKS */ + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#endif /* MBEDTLS_SSL_TLS13_INVASIVE_H */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls13_keys.c b/r5dev/thirdparty/mbedtls/ssl_tls13_keys.c new file mode 100644 index 00000000..6edce50b --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls13_keys.c @@ -0,0 +1,1861 @@ +/* + * TLS 1.3 key schedule + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 ( the "License" ); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#include +#include + +#include "mbedtls/hkdf.h" +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/platform.h" + +#include "ssl_misc.h" +#include "ssl_tls13_keys.h" +#include "ssl_tls13_invasive.h" + +#include "psa/crypto.h" + +#define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ + psa_to_ssl_errors, \ + psa_generic_status_to_mbedtls) + +#define MBEDTLS_SSL_TLS1_3_LABEL(name, string) \ + .name = string, + +struct mbedtls_ssl_tls13_labels_struct const mbedtls_ssl_tls13_labels = +{ + /* This seems to work in C, despite the string literal being one + * character too long due to the 0-termination. */ + MBEDTLS_SSL_TLS1_3_LABEL_LIST +}; + +#undef MBEDTLS_SSL_TLS1_3_LABEL + +/* + * This function creates a HkdfLabel structure used in the TLS 1.3 key schedule. + * + * The HkdfLabel is specified in RFC 8446 as follows: + * + * struct HkdfLabel { + * uint16 length; // Length of expanded key material + * opaque label<7..255>; // Always prefixed by "tls13 " + * opaque context<0..255>; // Usually a communication transcript hash + * }; + * + * Parameters: + * - desired_length: Length of expanded key material + * Even though the standard allows expansion to up to + * 2**16 Bytes, TLS 1.3 never uses expansion to more than + * 255 Bytes, so we require `desired_length` to be at most + * 255. This allows us to save a few Bytes of code by + * hardcoding the writing of the high bytes. + * - (label, label_len): label + label length, without "tls13 " prefix + * The label length MUST be less than or equal to + * MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN + * It is the caller's responsibility to ensure this. + * All (label, label length) pairs used in TLS 1.3 + * can be obtained via MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(). + * - (ctx, ctx_len): context + context length + * The context length MUST be less than or equal to + * MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_CONTEXT_LEN + * It is the caller's responsibility to ensure this. + * - dst: Target buffer for HkdfLabel structure, + * This MUST be a writable buffer of size + * at least SSL_TLS1_3_KEY_SCHEDULE_MAX_HKDF_LABEL_LEN Bytes. + * - dst_len: Pointer at which to store the actual length of + * the HkdfLabel structure on success. + */ + +static const char tls13_label_prefix[6] = "tls13 "; + +#define SSL_TLS1_3_KEY_SCHEDULE_HKDF_LABEL_LEN(label_len, context_len) \ + (2 /* expansion length */ \ + + 1 /* label length */ \ + + label_len \ + + 1 /* context length */ \ + + context_len) + +#define SSL_TLS1_3_KEY_SCHEDULE_MAX_HKDF_LABEL_LEN \ + SSL_TLS1_3_KEY_SCHEDULE_HKDF_LABEL_LEN( \ + sizeof(tls13_label_prefix) + \ + MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN, \ + MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_CONTEXT_LEN) + +static void ssl_tls13_hkdf_encode_label( + size_t desired_length, + const unsigned char *label, size_t label_len, + const unsigned char *ctx, size_t ctx_len, + unsigned char *dst, size_t *dst_len) +{ + size_t total_label_len = + sizeof(tls13_label_prefix) + label_len; + size_t total_hkdf_lbl_len = + SSL_TLS1_3_KEY_SCHEDULE_HKDF_LABEL_LEN(total_label_len, ctx_len); + + unsigned char *p = dst; + + /* Add the size of the expanded key material. + * We're hardcoding the high byte to 0 here assuming that we never use + * TLS 1.3 HKDF key expansion to more than 255 Bytes. */ +#if MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN > 255 +#error "The implementation of ssl_tls13_hkdf_encode_label() is not fit for the \ + value of MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN" +#endif + + *p++ = 0; + *p++ = MBEDTLS_BYTE_0(desired_length); + + /* Add label incl. prefix */ + *p++ = MBEDTLS_BYTE_0(total_label_len); + memcpy(p, tls13_label_prefix, sizeof(tls13_label_prefix)); + p += sizeof(tls13_label_prefix); + memcpy(p, label, label_len); + p += label_len; + + /* Add context value */ + *p++ = MBEDTLS_BYTE_0(ctx_len); + if (ctx_len != 0) { + memcpy(p, ctx, ctx_len); + } + + /* Return total length to the caller. */ + *dst_len = total_hkdf_lbl_len; +} + +int mbedtls_ssl_tls13_hkdf_expand_label( + psa_algorithm_t hash_alg, + const unsigned char *secret, size_t secret_len, + const unsigned char *label, size_t label_len, + const unsigned char *ctx, size_t ctx_len, + unsigned char *buf, size_t buf_len) +{ + unsigned char hkdf_label[SSL_TLS1_3_KEY_SCHEDULE_MAX_HKDF_LABEL_LEN]; + size_t hkdf_label_len = 0; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_derivation_operation_t operation = + PSA_KEY_DERIVATION_OPERATION_INIT; + + if (label_len > MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN) { + /* Should never happen since this is an internal + * function, and we know statically which labels + * are allowed. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (ctx_len > MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_CONTEXT_LEN) { + /* Should not happen, as above. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (buf_len > MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN) { + /* Should not happen, as above. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + ssl_tls13_hkdf_encode_label(buf_len, + label, label_len, + ctx, ctx_len, + hkdf_label, + &hkdf_label_len); + + status = psa_key_derivation_setup(&operation, PSA_ALG_HKDF_EXPAND(hash_alg)); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = psa_key_derivation_input_bytes(&operation, + PSA_KEY_DERIVATION_INPUT_SECRET, + secret, + secret_len); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = psa_key_derivation_input_bytes(&operation, + PSA_KEY_DERIVATION_INPUT_INFO, + hkdf_label, + hkdf_label_len); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = psa_key_derivation_output_bytes(&operation, + buf, + buf_len); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + +cleanup: + abort_status = psa_key_derivation_abort(&operation); + status = (status == PSA_SUCCESS ? abort_status : status); + mbedtls_platform_zeroize(hkdf_label, hkdf_label_len); + return PSA_TO_MBEDTLS_ERR(status); +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_make_traffic_key( + psa_algorithm_t hash_alg, + const unsigned char *secret, size_t secret_len, + unsigned char *key, size_t key_len, + unsigned char *iv, size_t iv_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_ssl_tls13_hkdf_expand_label( + hash_alg, + secret, secret_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(key), + NULL, 0, + key, key_len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_hkdf_expand_label( + hash_alg, + secret, secret_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(iv), + NULL, 0, + iv, iv_len); + return ret; +} + +/* + * The traffic keying material is generated from the following inputs: + * + * - One secret value per sender. + * - A purpose value indicating the specific value being generated + * - The desired lengths of key and IV. + * + * The expansion itself is based on HKDF: + * + * [sender]_write_key = HKDF-Expand-Label( Secret, "key", "", key_length ) + * [sender]_write_iv = HKDF-Expand-Label( Secret, "iv" , "", iv_length ) + * + * [sender] denotes the sending side and the Secret value is provided + * by the function caller. Note that we generate server and client side + * keys in a single function call. + */ +int mbedtls_ssl_tls13_make_traffic_keys( + psa_algorithm_t hash_alg, + const unsigned char *client_secret, + const unsigned char *server_secret, size_t secret_len, + size_t key_len, size_t iv_len, + mbedtls_ssl_key_set *keys) +{ + int ret = 0; + + ret = ssl_tls13_make_traffic_key( + hash_alg, client_secret, secret_len, + keys->client_write_key, key_len, + keys->client_write_iv, iv_len); + if (ret != 0) { + return ret; + } + + ret = ssl_tls13_make_traffic_key( + hash_alg, server_secret, secret_len, + keys->server_write_key, key_len, + keys->server_write_iv, iv_len); + if (ret != 0) { + return ret; + } + + keys->key_len = key_len; + keys->iv_len = iv_len; + + return 0; +} + +int mbedtls_ssl_tls13_derive_secret( + psa_algorithm_t hash_alg, + const unsigned char *secret, size_t secret_len, + const unsigned char *label, size_t label_len, + const unsigned char *ctx, size_t ctx_len, + int ctx_hashed, + unsigned char *dstbuf, size_t dstbuf_len) +{ + int ret; + unsigned char hashed_context[PSA_HASH_MAX_SIZE]; + if (ctx_hashed == MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED) { + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_hash_compute(hash_alg, ctx, ctx_len, hashed_context, + PSA_HASH_LENGTH(hash_alg), &ctx_len); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + return ret; + } + } else { + if (ctx_len > sizeof(hashed_context)) { + /* This should never happen since this function is internal + * and the code sets `ctx_hashed` correctly. + * Let's double-check nonetheless to not run at the risk + * of getting a stack overflow. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + memcpy(hashed_context, ctx, ctx_len); + } + + return mbedtls_ssl_tls13_hkdf_expand_label(hash_alg, + secret, secret_len, + label, label_len, + hashed_context, ctx_len, + dstbuf, dstbuf_len); + +} + +int mbedtls_ssl_tls13_evolve_secret( + psa_algorithm_t hash_alg, + const unsigned char *secret_old, + const unsigned char *input, size_t input_len, + unsigned char *secret_new) +{ + int ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED; + size_t hlen; + unsigned char tmp_secret[PSA_MAC_MAX_SIZE] = { 0 }; + const unsigned char all_zeroes_input[MBEDTLS_TLS1_3_MD_MAX_SIZE] = { 0 }; + const unsigned char *l_input = NULL; + size_t l_input_len; + + psa_key_derivation_operation_t operation = + PSA_KEY_DERIVATION_OPERATION_INIT; + + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + hlen = PSA_HASH_LENGTH(hash_alg); + + /* For non-initial runs, call Derive-Secret( ., "derived", "") + * on the old secret. */ + if (secret_old != NULL) { + ret = mbedtls_ssl_tls13_derive_secret( + hash_alg, + secret_old, hlen, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(derived), + NULL, 0, /* context */ + MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED, + tmp_secret, hlen); + if (ret != 0) { + goto cleanup; + } + } + + ret = 0; + + if (input != NULL && input_len != 0) { + l_input = input; + l_input_len = input_len; + } else { + l_input = all_zeroes_input; + l_input_len = hlen; + } + + status = psa_key_derivation_setup(&operation, + PSA_ALG_HKDF_EXTRACT(hash_alg)); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = psa_key_derivation_input_bytes(&operation, + PSA_KEY_DERIVATION_INPUT_SALT, + tmp_secret, + hlen); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = psa_key_derivation_input_bytes(&operation, + PSA_KEY_DERIVATION_INPUT_SECRET, + l_input, l_input_len); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + + status = psa_key_derivation_output_bytes(&operation, + secret_new, + PSA_HASH_LENGTH(hash_alg)); + + if (status != PSA_SUCCESS) { + goto cleanup; + } + +cleanup: + abort_status = psa_key_derivation_abort(&operation); + status = (status == PSA_SUCCESS ? abort_status : status); + ret = (ret == 0 ? PSA_TO_MBEDTLS_ERR(status) : ret); + mbedtls_platform_zeroize(tmp_secret, sizeof(tmp_secret)); + return ret; +} + +int mbedtls_ssl_tls13_derive_early_secrets( + psa_algorithm_t hash_alg, + unsigned char const *early_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_early_secrets *derived) +{ + int ret; + size_t const hash_len = PSA_HASH_LENGTH(hash_alg); + + /* We should never call this function with an unknown hash, + * but add an assertion anyway. */ + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* + * 0 + * | + * v + * PSK -> HKDF-Extract = Early Secret + * | + * +-----> Derive-Secret(., "c e traffic", ClientHello) + * | = client_early_traffic_secret + * | + * +-----> Derive-Secret(., "e exp master", ClientHello) + * | = early_exporter_master_secret + * v + */ + + /* Create client_early_traffic_secret */ + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + early_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(c_e_traffic), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->client_early_traffic_secret, + hash_len); + if (ret != 0) { + return ret; + } + + /* Create early exporter */ + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + early_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(e_exp_master), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->early_exporter_master_secret, + hash_len); + if (ret != 0) { + return ret; + } + + return 0; +} + +int mbedtls_ssl_tls13_derive_handshake_secrets( + psa_algorithm_t hash_alg, + unsigned char const *handshake_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_handshake_secrets *derived) +{ + int ret; + size_t const hash_len = PSA_HASH_LENGTH(hash_alg); + + /* We should never call this function with an unknown hash, + * but add an assertion anyway. */ + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* + * + * Handshake Secret + * | + * +-----> Derive-Secret( ., "c hs traffic", + * | ClientHello...ServerHello ) + * | = client_handshake_traffic_secret + * | + * +-----> Derive-Secret( ., "s hs traffic", + * | ClientHello...ServerHello ) + * | = server_handshake_traffic_secret + * + */ + + /* + * Compute client_handshake_traffic_secret with + * Derive-Secret( ., "c hs traffic", ClientHello...ServerHello ) + */ + + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + handshake_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(c_hs_traffic), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->client_handshake_traffic_secret, + hash_len); + if (ret != 0) { + return ret; + } + + /* + * Compute server_handshake_traffic_secret with + * Derive-Secret( ., "s hs traffic", ClientHello...ServerHello ) + */ + + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + handshake_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(s_hs_traffic), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->server_handshake_traffic_secret, + hash_len); + if (ret != 0) { + return ret; + } + + return 0; +} + +int mbedtls_ssl_tls13_derive_application_secrets( + psa_algorithm_t hash_alg, + unsigned char const *application_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_application_secrets *derived) +{ + int ret; + size_t const hash_len = PSA_HASH_LENGTH(hash_alg); + + /* We should never call this function with an unknown hash, + * but add an assertion anyway. */ + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* Generate {client,server}_application_traffic_secret_0 + * + * Master Secret + * | + * +-----> Derive-Secret( ., "c ap traffic", + * | ClientHello...server Finished ) + * | = client_application_traffic_secret_0 + * | + * +-----> Derive-Secret( ., "s ap traffic", + * | ClientHello...Server Finished ) + * | = server_application_traffic_secret_0 + * | + * +-----> Derive-Secret( ., "exp master", + * | ClientHello...server Finished) + * | = exporter_master_secret + * + */ + + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + application_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(c_ap_traffic), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->client_application_traffic_secret_N, + hash_len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + application_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(s_ap_traffic), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->server_application_traffic_secret_N, + hash_len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + application_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(exp_master), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->exporter_master_secret, + hash_len); + if (ret != 0) { + return ret; + } + + return 0; +} + +/* Generate resumption_master_secret for use with the ticket exchange. + * + * This is not integrated with mbedtls_ssl_tls13_derive_application_secrets() + * because it uses the transcript hash up to and including ClientFinished. */ +int mbedtls_ssl_tls13_derive_resumption_master_secret( + psa_algorithm_t hash_alg, + unsigned char const *application_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_application_secrets *derived) +{ + int ret; + size_t const hash_len = PSA_HASH_LENGTH(hash_alg); + + /* We should never call this function with an unknown hash, + * but add an assertion anyway. */ + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + application_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(res_master), + transcript, transcript_len, + MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED, + derived->resumption_master_secret, + hash_len); + + if (ret != 0) { + return ret; + } + + return 0; +} + +/** + * \brief Transition into application stage of TLS 1.3 key schedule. + * + * The TLS 1.3 key schedule can be viewed as a simple state machine + * with states Initial -> Early -> Handshake -> Application, and + * this function represents the Handshake -> Application transition. + * + * In the handshake stage, ssl_tls13_generate_application_keys() + * can be used to derive the handshake traffic keys. + * + * \param ssl The SSL context to operate on. This must be in key schedule + * stage \c Handshake. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_key_schedule_stage_application(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + psa_algorithm_t const hash_alg = mbedtls_hash_info_psa_from_md( + handshake->ciphersuite_info->mac); + + /* + * Compute MasterSecret + */ + ret = mbedtls_ssl_tls13_evolve_secret(hash_alg, + handshake->tls13_master_secrets.handshake, + NULL, 0, + handshake->tls13_master_secrets.app); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_evolve_secret", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "Master secret", + handshake->tls13_master_secrets.app, PSA_HASH_LENGTH(hash_alg)); + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_calc_finished_core(psa_algorithm_t hash_alg, + unsigned char const *base_key, + unsigned char const *transcript, + unsigned char *dst, + size_t *dst_len) +{ + mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t hash_len = PSA_HASH_LENGTH(hash_alg); + unsigned char finished_key[PSA_MAC_MAX_SIZE]; + int ret; + psa_algorithm_t alg; + + /* We should never call this function with an unknown hash, + * but add an assertion anyway. */ + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* TLS 1.3 Finished message + * + * struct { + * opaque verify_data[Hash.length]; + * } Finished; + * + * verify_data = + * HMAC( finished_key, + * Hash( Handshake Context + + * Certificate* + + * CertificateVerify* ) + * ) + * + * finished_key = + * HKDF-Expand-Label( BaseKey, "finished", "", Hash.length ) + */ + + ret = mbedtls_ssl_tls13_hkdf_expand_label( + hash_alg, base_key, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(finished), + NULL, 0, + finished_key, hash_len); + if (ret != 0) { + goto exit; + } + + alg = PSA_ALG_HMAC(hash_alg); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attributes, alg); + psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC); + + status = psa_import_key(&attributes, finished_key, hash_len, &key); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + goto exit; + } + + status = psa_mac_compute(key, alg, transcript, hash_len, + dst, hash_len, dst_len); + ret = PSA_TO_MBEDTLS_ERR(status); + +exit: + + status = psa_destroy_key(key); + if (ret == 0) { + ret = PSA_TO_MBEDTLS_ERR(status); + } + + mbedtls_platform_zeroize(finished_key, sizeof(finished_key)); + + return ret; +} + +int mbedtls_ssl_tls13_calculate_verify_data(mbedtls_ssl_context *ssl, + unsigned char *dst, + size_t dst_len, + size_t *actual_len, + int from) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t transcript_len; + + unsigned char *base_key = NULL; + size_t base_key_len = 0; + mbedtls_ssl_tls13_handshake_secrets *tls13_hs_secrets = + &ssl->handshake->tls13_hs_secrets; + + mbedtls_md_type_t const md_type = ssl->handshake->ciphersuite_info->mac; + + psa_algorithm_t hash_alg = mbedtls_hash_info_psa_from_md( + ssl->handshake->ciphersuite_info->mac); + size_t const hash_len = PSA_HASH_LENGTH(hash_alg); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> mbedtls_ssl_tls13_calculate_verify_data")); + + if (from == MBEDTLS_SSL_IS_CLIENT) { + base_key = tls13_hs_secrets->client_handshake_traffic_secret; + base_key_len = sizeof(tls13_hs_secrets->client_handshake_traffic_secret); + } else { + base_key = tls13_hs_secrets->server_handshake_traffic_secret; + base_key_len = sizeof(tls13_hs_secrets->server_handshake_traffic_secret); + } + + if (dst_len < hash_len) { + ret = MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + goto exit; + } + + ret = mbedtls_ssl_get_handshake_transcript(ssl, md_type, + transcript, sizeof(transcript), + &transcript_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_get_handshake_transcript", ret); + goto exit; + } + MBEDTLS_SSL_DEBUG_BUF(4, "handshake hash", transcript, transcript_len); + + ret = ssl_tls13_calc_finished_core(hash_alg, base_key, transcript, dst, actual_len); + if (ret != 0) { + goto exit; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "verify_data for finished message", dst, hash_len); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= mbedtls_ssl_tls13_calculate_verify_data")); + +exit: + /* Erase handshake secrets */ + mbedtls_platform_zeroize(base_key, base_key_len); + mbedtls_platform_zeroize(transcript, sizeof(transcript)); + return ret; +} + +int mbedtls_ssl_tls13_create_psk_binder(mbedtls_ssl_context *ssl, + const psa_algorithm_t hash_alg, + unsigned char const *psk, size_t psk_len, + int psk_type, + unsigned char const *transcript, + unsigned char *result) +{ + int ret = 0; + unsigned char binder_key[PSA_MAC_MAX_SIZE]; + unsigned char early_secret[PSA_MAC_MAX_SIZE]; + size_t const hash_len = PSA_HASH_LENGTH(hash_alg); + size_t actual_len; + +#if !defined(MBEDTLS_DEBUG_C) + ssl = NULL; /* make sure we don't use it except for debug */ + ((void) ssl); +#endif + + /* We should never call this function with an unknown hash, + * but add an assertion anyway. */ + if (!PSA_ALG_IS_HASH(hash_alg)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* + * 0 + * | + * v + * PSK -> HKDF-Extract = Early Secret + * | + * +-----> Derive-Secret(., "ext binder" | "res binder", "") + * | = binder_key + * v + */ + + ret = mbedtls_ssl_tls13_evolve_secret(hash_alg, + NULL, /* Old secret */ + psk, psk_len, /* Input */ + early_secret); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_evolve_secret", ret); + goto exit; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "mbedtls_ssl_tls13_create_psk_binder", + early_secret, hash_len); + + if (psk_type == MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION) { + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + early_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(res_binder), + NULL, 0, MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED, + binder_key, hash_len); + MBEDTLS_SSL_DEBUG_MSG(4, ("Derive Early Secret with 'res binder'")); + } else { + ret = mbedtls_ssl_tls13_derive_secret(hash_alg, + early_secret, hash_len, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(ext_binder), + NULL, 0, MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED, + binder_key, hash_len); + MBEDTLS_SSL_DEBUG_MSG(4, ("Derive Early Secret with 'ext binder'")); + } + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_derive_secret", ret); + goto exit; + } + + /* + * The binding_value is computed in the same way as the Finished message + * but with the BaseKey being the binder_key. + */ + + ret = ssl_tls13_calc_finished_core(hash_alg, binder_key, transcript, + result, &actual_len); + if (ret != 0) { + goto exit; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "psk binder", result, actual_len); + +exit: + + mbedtls_platform_zeroize(early_secret, sizeof(early_secret)); + mbedtls_platform_zeroize(binder_key, sizeof(binder_key)); + return ret; +} + +int mbedtls_ssl_tls13_populate_transform(mbedtls_ssl_transform *transform, + int endpoint, + int ciphersuite, + mbedtls_ssl_key_set const *traffic_keys, + mbedtls_ssl_context *ssl /* DEBUG ONLY */) +{ +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + int ret; + mbedtls_cipher_info_t const *cipher_info; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + unsigned char const *key_enc; + unsigned char const *iv_enc; + unsigned char const *key_dec; + unsigned char const *iv_dec; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_key_type_t key_type; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t alg; + size_t key_bits; + psa_status_t status = PSA_SUCCESS; +#endif + +#if !defined(MBEDTLS_DEBUG_C) + ssl = NULL; /* make sure we don't use it except for those cases */ + (void) ssl; +#endif + + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(ciphersuite); + if (ciphersuite_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("ciphersuite info for %d not found", + ciphersuite)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + cipher_info = mbedtls_cipher_info_from_type(ciphersuite_info->cipher); + if (cipher_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("cipher info for %u not found", + ciphersuite_info->cipher)); + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + /* + * Setup cipher contexts in target transform + */ + if ((ret = mbedtls_cipher_setup(&transform->cipher_ctx_enc, + cipher_info)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setup", ret); + return ret; + } + + if ((ret = mbedtls_cipher_setup(&transform->cipher_ctx_dec, + cipher_info)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setup", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#if defined(MBEDTLS_SSL_SRV_C) + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + key_enc = traffic_keys->server_write_key; + key_dec = traffic_keys->client_write_key; + iv_enc = traffic_keys->server_write_iv; + iv_dec = traffic_keys->client_write_iv; + } else +#endif /* MBEDTLS_SSL_SRV_C */ +#if defined(MBEDTLS_SSL_CLI_C) + if (endpoint == MBEDTLS_SSL_IS_CLIENT) { + key_enc = traffic_keys->client_write_key; + key_dec = traffic_keys->server_write_key; + iv_enc = traffic_keys->client_write_iv; + iv_dec = traffic_keys->server_write_iv; + } else +#endif /* MBEDTLS_SSL_CLI_C */ + { + /* should not happen */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + memcpy(transform->iv_enc, iv_enc, traffic_keys->iv_len); + memcpy(transform->iv_dec, iv_dec, traffic_keys->iv_len); + +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + if ((ret = mbedtls_cipher_setkey(&transform->cipher_ctx_enc, + key_enc, cipher_info->key_bitlen, + MBEDTLS_ENCRYPT)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setkey", ret); + return ret; + } + + if ((ret = mbedtls_cipher_setkey(&transform->cipher_ctx_dec, + key_dec, cipher_info->key_bitlen, + MBEDTLS_DECRYPT)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_cipher_setkey", ret); + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* + * Setup other fields in SSL transform + */ + + if ((ciphersuite_info->flags & MBEDTLS_CIPHERSUITE_SHORT_TAG) != 0) { + transform->taglen = 8; + } else { + transform->taglen = 16; + } + + transform->ivlen = traffic_keys->iv_len; + transform->maclen = 0; + transform->fixed_ivlen = transform->ivlen; + transform->tls_version = MBEDTLS_SSL_VERSION_TLS1_3; + + /* We add the true record content type (1 Byte) to the plaintext and + * then pad to the configured granularity. The minimum length of the + * type-extended and padded plaintext is therefore the padding + * granularity. */ + transform->minlen = + transform->taglen + MBEDTLS_SSL_CID_TLS1_3_PADDING_GRANULARITY; + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + /* + * Setup psa keys and alg + */ + if ((status = mbedtls_ssl_cipher_to_psa(ciphersuite_info->cipher, + transform->taglen, + &alg, + &key_type, + &key_bits)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_cipher_to_psa", PSA_TO_MBEDTLS_ERR(status)); + return PSA_TO_MBEDTLS_ERR(status); + } + + transform->psa_alg = alg; + + if (alg != MBEDTLS_SSL_NULL_CIPHER) { + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attributes, alg); + psa_set_key_type(&attributes, key_type); + + if ((status = psa_import_key(&attributes, + key_enc, + PSA_BITS_TO_BYTES(key_bits), + &transform->psa_key_enc)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "psa_import_key", PSA_TO_MBEDTLS_ERR(status)); + return PSA_TO_MBEDTLS_ERR(status); + } + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); + + if ((status = psa_import_key(&attributes, + key_dec, + PSA_BITS_TO_BYTES(key_bits), + &transform->psa_key_dec)) != PSA_SUCCESS) { + MBEDTLS_SSL_DEBUG_RET(1, "psa_import_key", PSA_TO_MBEDTLS_ERR(status)); + return PSA_TO_MBEDTLS_ERR(status); + } + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_get_cipher_key_info( + const mbedtls_ssl_ciphersuite_t *ciphersuite_info, + size_t *key_len, size_t *iv_len) +{ + psa_key_type_t key_type; + psa_algorithm_t alg; + size_t taglen; + size_t key_bits; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + if (ciphersuite_info->flags & MBEDTLS_CIPHERSUITE_SHORT_TAG) { + taglen = 8; + } else { + taglen = 16; + } + + status = mbedtls_ssl_cipher_to_psa(ciphersuite_info->cipher, taglen, + &alg, &key_type, &key_bits); + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } + + *key_len = PSA_BITS_TO_BYTES(key_bits); + + /* TLS 1.3 only have AEAD ciphers, IV length is unconditionally 12 bytes */ + *iv_len = 12; + + return 0; +} + +#if defined(MBEDTLS_SSL_EARLY_DATA) +/* + * ssl_tls13_generate_early_key() generates the key necessary for protecting + * the early application data and handshake messages as described in section 7 + * of RFC 8446. + * + * NOTE: Only one key is generated, the key for the traffic from the client to + * the server. The TLS 1.3 specification does not define a secret and thus + * a key for server early traffic. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_generate_early_key(mbedtls_ssl_context *ssl, + mbedtls_ssl_key_set *traffic_keys) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_type_t md_type; + psa_algorithm_t hash_alg; + size_t hash_len; + unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t transcript_len; + size_t key_len; + size_t iv_len; + mbedtls_ssl_tls13_early_secrets tls13_early_secrets; + + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = handshake->ciphersuite_info; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> ssl_tls13_generate_early_key")); + + ret = ssl_tls13_get_cipher_key_info(ciphersuite_info, &key_len, &iv_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_get_cipher_key_info", ret); + goto cleanup; + } + + md_type = ciphersuite_info->mac; + + hash_alg = mbedtls_hash_info_psa_from_md(ciphersuite_info->mac); + hash_len = PSA_HASH_LENGTH(hash_alg); + + ret = mbedtls_ssl_get_handshake_transcript(ssl, md_type, + transcript, + sizeof(transcript), + &transcript_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_get_handshake_transcript", + ret); + goto cleanup; + } + + ret = mbedtls_ssl_tls13_derive_early_secrets( + hash_alg, handshake->tls13_master_secrets.early, + transcript, transcript_len, &tls13_early_secrets); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "mbedtls_ssl_tls13_derive_early_secrets", ret); + goto cleanup; + } + + MBEDTLS_SSL_DEBUG_BUF( + 4, "Client early traffic secret", + tls13_early_secrets.client_early_traffic_secret, hash_len); + + /* + * Export client handshake traffic secret + */ + if (ssl->f_export_keys != NULL) { + ssl->f_export_keys( + ssl->p_export_keys, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_EARLY_SECRET, + tls13_early_secrets.client_early_traffic_secret, + hash_len, + handshake->randbytes, + handshake->randbytes + MBEDTLS_CLIENT_HELLO_RANDOM_LEN, + MBEDTLS_SSL_TLS_PRF_NONE /* TODO: FIX! */); + } + + ret = ssl_tls13_make_traffic_key( + hash_alg, + tls13_early_secrets.client_early_traffic_secret, + hash_len, traffic_keys->client_write_key, key_len, + traffic_keys->client_write_iv, iv_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_make_traffic_key", ret); + goto cleanup; + } + traffic_keys->key_len = key_len; + traffic_keys->iv_len = iv_len; + + MBEDTLS_SSL_DEBUG_BUF(4, "client early write_key", + traffic_keys->client_write_key, + traffic_keys->key_len); + + MBEDTLS_SSL_DEBUG_BUF(4, "client early write_iv", + traffic_keys->client_write_iv, + traffic_keys->iv_len); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= ssl_tls13_generate_early_key")); + +cleanup: + /* Erase early secrets and transcript */ + mbedtls_platform_zeroize( + &tls13_early_secrets, sizeof(mbedtls_ssl_tls13_early_secrets)); + mbedtls_platform_zeroize(transcript, sizeof(transcript)); + return ret; +} + +int mbedtls_ssl_tls13_compute_early_transform(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_key_set traffic_keys; + mbedtls_ssl_transform *transform_earlydata = NULL; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* Next evolution in key schedule: Establish early_data secret and + * key material. */ + ret = ssl_tls13_generate_early_key(ssl, &traffic_keys); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_generate_early_key", + ret); + goto cleanup; + } + + transform_earlydata = mbedtls_calloc(1, sizeof(mbedtls_ssl_transform)); + if (transform_earlydata == NULL) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto cleanup; + } + + ret = mbedtls_ssl_tls13_populate_transform( + transform_earlydata, + ssl->conf->endpoint, + handshake->ciphersuite_info->id, + &traffic_keys, + ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_populate_transform", ret); + goto cleanup; + } + handshake->transform_earlydata = transform_earlydata; + +cleanup: + mbedtls_platform_zeroize(&traffic_keys, sizeof(traffic_keys)); + if (ret != 0) { + mbedtls_free(transform_earlydata); + } + + return ret; +} +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +int mbedtls_ssl_tls13_key_schedule_stage_early(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + psa_algorithm_t hash_alg; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + unsigned char *psk = NULL; + size_t psk_len = 0; + + if (handshake->ciphersuite_info == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("cipher suite info not found")); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + hash_alg = mbedtls_hash_info_psa_from_md(handshake->ciphersuite_info->mac); +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + if (mbedtls_ssl_tls13_key_exchange_mode_with_psk(ssl)) { + ret = mbedtls_ssl_tls13_export_handshake_psk(ssl, &psk, &psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_export_handshake_psk", + ret); + return ret; + } + } +#endif + + ret = mbedtls_ssl_tls13_evolve_secret(hash_alg, NULL, psk, psk_len, + handshake->tls13_master_secrets.early); +#if defined(MBEDTLS_USE_PSA_CRYPTO) && \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + mbedtls_free((void *) psk); +#endif + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_evolve_secret", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "mbedtls_ssl_tls13_key_schedule_stage_early", + handshake->tls13_master_secrets.early, + PSA_HASH_LENGTH(hash_alg)); + return 0; +} + +/** + * \brief Compute TLS 1.3 handshake traffic keys. + * + * ssl_tls13_generate_handshake_keys() generates keys necessary for + * protecting the handshake messages, as described in Section 7 of + * RFC 8446. + * + * \param ssl The SSL context to operate on. This must be in + * key schedule stage \c Handshake, see + * ssl_tls13_key_schedule_stage_handshake(). + * \param traffic_keys The address at which to store the handshake traffic + * keys. This must be writable but may be uninitialized. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_generate_handshake_keys(mbedtls_ssl_context *ssl, + mbedtls_ssl_key_set *traffic_keys) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_type_t md_type; + psa_algorithm_t hash_alg; + size_t hash_len; + unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t transcript_len; + size_t key_len; + size_t iv_len; + + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info = handshake->ciphersuite_info; + mbedtls_ssl_tls13_handshake_secrets *tls13_hs_secrets = &handshake->tls13_hs_secrets; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> ssl_tls13_generate_handshake_keys")); + + ret = ssl_tls13_get_cipher_key_info(ciphersuite_info, &key_len, &iv_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_get_cipher_key_info", ret); + return ret; + } + + md_type = ciphersuite_info->mac; + + hash_alg = mbedtls_hash_info_psa_from_md(ciphersuite_info->mac); + hash_len = PSA_HASH_LENGTH(hash_alg); + + ret = mbedtls_ssl_get_handshake_transcript(ssl, md_type, + transcript, + sizeof(transcript), + &transcript_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_get_handshake_transcript", + ret); + return ret; + } + + ret = mbedtls_ssl_tls13_derive_handshake_secrets(hash_alg, + handshake->tls13_master_secrets.handshake, + transcript, transcript_len, tls13_hs_secrets); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_derive_handshake_secrets", + ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "Client handshake traffic secret", + tls13_hs_secrets->client_handshake_traffic_secret, + hash_len); + MBEDTLS_SSL_DEBUG_BUF(4, "Server handshake traffic secret", + tls13_hs_secrets->server_handshake_traffic_secret, + hash_len); + + /* + * Export client handshake traffic secret + */ + if (ssl->f_export_keys != NULL) { + ssl->f_export_keys(ssl->p_export_keys, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_HANDSHAKE_TRAFFIC_SECRET, + tls13_hs_secrets->client_handshake_traffic_secret, + hash_len, + handshake->randbytes, + handshake->randbytes + MBEDTLS_CLIENT_HELLO_RANDOM_LEN, + MBEDTLS_SSL_TLS_PRF_NONE /* TODO: FIX! */); + + ssl->f_export_keys(ssl->p_export_keys, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_HANDSHAKE_TRAFFIC_SECRET, + tls13_hs_secrets->server_handshake_traffic_secret, + hash_len, + handshake->randbytes, + handshake->randbytes + MBEDTLS_CLIENT_HELLO_RANDOM_LEN, + MBEDTLS_SSL_TLS_PRF_NONE /* TODO: FIX! */); + } + + ret = mbedtls_ssl_tls13_make_traffic_keys(hash_alg, + tls13_hs_secrets->client_handshake_traffic_secret, + tls13_hs_secrets->server_handshake_traffic_secret, + hash_len, key_len, iv_len, traffic_keys); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_make_traffic_keys", ret); + goto exit; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "client_handshake write_key", + traffic_keys->client_write_key, + traffic_keys->key_len); + + MBEDTLS_SSL_DEBUG_BUF(4, "server_handshake write_key", + traffic_keys->server_write_key, + traffic_keys->key_len); + + MBEDTLS_SSL_DEBUG_BUF(4, "client_handshake write_iv", + traffic_keys->client_write_iv, + traffic_keys->iv_len); + + MBEDTLS_SSL_DEBUG_BUF(4, "server_handshake write_iv", + traffic_keys->server_write_iv, + traffic_keys->iv_len); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= ssl_tls13_generate_handshake_keys")); + +exit: + + return ret; +} + +/** + * \brief Transition into handshake stage of TLS 1.3 key schedule. + * + * The TLS 1.3 key schedule can be viewed as a simple state machine + * with states Initial -> Early -> Handshake -> Application, and + * this function represents the Early -> Handshake transition. + * + * In the handshake stage, ssl_tls13_generate_handshake_keys() + * can be used to derive the handshake traffic keys. + * + * \param ssl The SSL context to operate on. This must be in key schedule + * stage \c Early. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_key_schedule_stage_handshake(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + psa_algorithm_t const hash_alg = mbedtls_hash_info_psa_from_md( + handshake->ciphersuite_info->mac); + unsigned char *shared_secret = NULL; + size_t shared_secret_len = 0; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED) + /* + * Compute ECDHE secret used to compute the handshake secret from which + * client_handshake_traffic_secret and server_handshake_traffic_secret + * are derived in the handshake secret derivation stage. + */ + if (mbedtls_ssl_tls13_key_exchange_mode_with_ephemeral(ssl)) { + if (mbedtls_ssl_tls13_named_group_is_ecdhe(handshake->offered_group_id)) { +#if defined(MBEDTLS_ECDH_C) + /* Compute ECDH shared secret. */ + psa_status_t status = PSA_ERROR_GENERIC_ERROR; + psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; + + status = psa_get_key_attributes(handshake->ecdh_psa_privkey, + &key_attributes); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + } + + shared_secret_len = PSA_BITS_TO_BYTES( + psa_get_key_bits(&key_attributes)); + shared_secret = mbedtls_calloc(1, shared_secret_len); + if (shared_secret == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + status = psa_raw_key_agreement( + PSA_ALG_ECDH, handshake->ecdh_psa_privkey, + handshake->ecdh_psa_peerkey, handshake->ecdh_psa_peerkey_len, + shared_secret, shared_secret_len, &shared_secret_len); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_raw_key_agreement", ret); + goto cleanup; + } + + status = psa_destroy_key(handshake->ecdh_psa_privkey); + if (status != PSA_SUCCESS) { + ret = PSA_TO_MBEDTLS_ERR(status); + MBEDTLS_SSL_DEBUG_RET(1, "psa_destroy_key", ret); + goto cleanup; + } + + handshake->ecdh_psa_privkey = MBEDTLS_SVC_KEY_ID_INIT; +#endif /* MBEDTLS_ECDH_C */ + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("Group not supported.")); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + } +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_EPHEMERAL_ENABLED */ + + /* + * Compute the Handshake Secret + */ + ret = mbedtls_ssl_tls13_evolve_secret(hash_alg, + handshake->tls13_master_secrets.early, + shared_secret, shared_secret_len, + handshake->tls13_master_secrets.handshake); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_evolve_secret", ret); + goto cleanup; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "Handshake secret", + handshake->tls13_master_secrets.handshake, + PSA_HASH_LENGTH(hash_alg)); + +cleanup: + if (shared_secret != NULL) { + mbedtls_platform_zeroize(shared_secret, shared_secret_len); + mbedtls_free(shared_secret); + } + + return ret; +} + +/** + * \brief Compute TLS 1.3 application traffic keys. + * + * ssl_tls13_generate_application_keys() generates application traffic + * keys, since any record following a 1-RTT Finished message MUST be + * encrypted under the application traffic key. + * + * \param ssl The SSL context to operate on. This must be in + * key schedule stage \c Application, see + * ssl_tls13_key_schedule_stage_application(). + * \param traffic_keys The address at which to store the application traffic + * keys. This must be writable but may be uninitialized. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_generate_application_keys( + mbedtls_ssl_context *ssl, + mbedtls_ssl_key_set *traffic_keys) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* Address at which to store the application secrets */ + mbedtls_ssl_tls13_application_secrets * const app_secrets = + &ssl->session_negotiate->app_secrets; + + /* Holding the transcript up to and including the ServerFinished */ + unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t transcript_len; + + /* Variables relating to the hash for the chosen ciphersuite. */ + mbedtls_md_type_t md_type; + + psa_algorithm_t hash_alg; + size_t hash_len; + + /* Variables relating to the cipher for the chosen ciphersuite. */ + size_t key_len, iv_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> derive application traffic keys")); + + /* Extract basic information about hash and ciphersuite */ + + ret = ssl_tls13_get_cipher_key_info(handshake->ciphersuite_info, + &key_len, &iv_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_get_cipher_key_info", ret); + goto cleanup; + } + + md_type = handshake->ciphersuite_info->mac; + + hash_alg = mbedtls_hash_info_psa_from_md(handshake->ciphersuite_info->mac); + hash_len = PSA_HASH_LENGTH(hash_alg); + + /* Compute current handshake transcript. It's the caller's responsibility + * to call this at the right time, that is, after the ServerFinished. */ + + ret = mbedtls_ssl_get_handshake_transcript(ssl, md_type, + transcript, sizeof(transcript), + &transcript_len); + if (ret != 0) { + goto cleanup; + } + + /* Compute application secrets from master secret and transcript hash. */ + + ret = mbedtls_ssl_tls13_derive_application_secrets(hash_alg, + handshake->tls13_master_secrets.app, + transcript, transcript_len, + app_secrets); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_tls13_derive_application_secrets", ret); + goto cleanup; + } + + /* Derive first epoch of IV + Key for application traffic. */ + + ret = mbedtls_ssl_tls13_make_traffic_keys(hash_alg, + app_secrets->client_application_traffic_secret_N, + app_secrets->server_application_traffic_secret_N, + hash_len, key_len, iv_len, traffic_keys); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_make_traffic_keys", ret); + goto cleanup; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "Client application traffic secret", + app_secrets->client_application_traffic_secret_N, + hash_len); + + MBEDTLS_SSL_DEBUG_BUF(4, "Server application traffic secret", + app_secrets->server_application_traffic_secret_N, + hash_len); + + /* + * Export client/server application traffic secret 0 + */ + if (ssl->f_export_keys != NULL) { + ssl->f_export_keys(ssl->p_export_keys, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_APPLICATION_TRAFFIC_SECRET, + app_secrets->client_application_traffic_secret_N, hash_len, + handshake->randbytes, + handshake->randbytes + MBEDTLS_CLIENT_HELLO_RANDOM_LEN, + MBEDTLS_SSL_TLS_PRF_NONE /* TODO: this should be replaced by + a new constant for TLS 1.3! */); + + ssl->f_export_keys(ssl->p_export_keys, + MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_APPLICATION_TRAFFIC_SECRET, + app_secrets->server_application_traffic_secret_N, hash_len, + handshake->randbytes, + handshake->randbytes + MBEDTLS_CLIENT_HELLO_RANDOM_LEN, + MBEDTLS_SSL_TLS_PRF_NONE /* TODO: this should be replaced by + a new constant for TLS 1.3! */); + } + + MBEDTLS_SSL_DEBUG_BUF(4, "client application_write_key:", + traffic_keys->client_write_key, key_len); + MBEDTLS_SSL_DEBUG_BUF(4, "server application write key", + traffic_keys->server_write_key, key_len); + MBEDTLS_SSL_DEBUG_BUF(4, "client application write IV", + traffic_keys->client_write_iv, iv_len); + MBEDTLS_SSL_DEBUG_BUF(4, "server application write IV", + traffic_keys->server_write_iv, iv_len); + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= derive application traffic keys")); + +cleanup: + /* randbytes is not used again */ + mbedtls_platform_zeroize(ssl->handshake->randbytes, + sizeof(ssl->handshake->randbytes)); + + mbedtls_platform_zeroize(transcript, sizeof(transcript)); + return ret; +} + +int mbedtls_ssl_tls13_compute_handshake_transform(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_key_set traffic_keys; + mbedtls_ssl_transform *transform_handshake = NULL; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + + /* Compute handshake secret */ + ret = ssl_tls13_key_schedule_stage_handshake(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_derive_master_secret", ret); + goto cleanup; + } + + /* Next evolution in key schedule: Establish handshake secret and + * key material. */ + ret = ssl_tls13_generate_handshake_keys(ssl, &traffic_keys); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_generate_handshake_keys", + ret); + goto cleanup; + } + + transform_handshake = mbedtls_calloc(1, sizeof(mbedtls_ssl_transform)); + if (transform_handshake == NULL) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto cleanup; + } + + ret = mbedtls_ssl_tls13_populate_transform( + transform_handshake, + ssl->conf->endpoint, + handshake->ciphersuite_info->id, + &traffic_keys, + ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_populate_transform", ret); + goto cleanup; + } + handshake->transform_handshake = transform_handshake; + +cleanup: + mbedtls_platform_zeroize(&traffic_keys, sizeof(traffic_keys)); + if (ret != 0) { + mbedtls_free(transform_handshake); + } + + return ret; +} + +int mbedtls_ssl_tls13_compute_resumption_master_secret(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_md_type_t md_type; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + unsigned char transcript[MBEDTLS_TLS1_3_MD_MAX_SIZE]; + size_t transcript_len; + + MBEDTLS_SSL_DEBUG_MSG(2, + ("=> mbedtls_ssl_tls13_compute_resumption_master_secret")); + + md_type = handshake->ciphersuite_info->mac; + + ret = mbedtls_ssl_get_handshake_transcript(ssl, md_type, + transcript, sizeof(transcript), + &transcript_len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_derive_resumption_master_secret( + mbedtls_psa_translate_md(md_type), + handshake->tls13_master_secrets.app, + transcript, transcript_len, + &ssl->session_negotiate->app_secrets); + if (ret != 0) { + return ret; + } + + /* Erase master secrets */ + mbedtls_platform_zeroize(&handshake->tls13_master_secrets, + sizeof(handshake->tls13_master_secrets)); + + MBEDTLS_SSL_DEBUG_BUF(4, "Resumption master secret", + ssl->session_negotiate->app_secrets.resumption_master_secret, + PSA_HASH_LENGTH(mbedtls_psa_translate_md(md_type))); + + MBEDTLS_SSL_DEBUG_MSG(2, + ("<= mbedtls_ssl_tls13_compute_resumption_master_secret")); + return 0; +} + +int mbedtls_ssl_tls13_compute_application_transform(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_key_set traffic_keys; + mbedtls_ssl_transform *transform_application = NULL; + + ret = ssl_tls13_key_schedule_stage_application(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "ssl_tls13_key_schedule_stage_application", ret); + goto cleanup; + } + + ret = ssl_tls13_generate_application_keys(ssl, &traffic_keys); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "ssl_tls13_generate_application_keys", ret); + goto cleanup; + } + + transform_application = + mbedtls_calloc(1, sizeof(mbedtls_ssl_transform)); + if (transform_application == NULL) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + goto cleanup; + } + + ret = mbedtls_ssl_tls13_populate_transform( + transform_application, + ssl->conf->endpoint, + ssl->handshake->ciphersuite_info->id, + &traffic_keys, + ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_tls13_populate_transform", ret); + goto cleanup; + } + + ssl->transform_application = transform_application; + +cleanup: + + mbedtls_platform_zeroize(&traffic_keys, sizeof(traffic_keys)); + if (ret != 0) { + mbedtls_free(transform_application); + } + return ret; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +int mbedtls_ssl_tls13_export_handshake_psk(mbedtls_ssl_context *ssl, + unsigned char **psk, + size_t *psk_len) +{ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + *psk_len = 0; + *psk = NULL; + + if (mbedtls_svc_key_id_is_null(ssl->handshake->psk_opaque)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + status = psa_get_key_attributes(ssl->handshake->psk_opaque, &key_attributes); + if (status != PSA_SUCCESS) { + return PSA_TO_MBEDTLS_ERR(status); + } + + *psk_len = PSA_BITS_TO_BYTES(psa_get_key_bits(&key_attributes)); + *psk = mbedtls_calloc(1, *psk_len); + if (*psk == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + + status = psa_export_key(ssl->handshake->psk_opaque, + (uint8_t *) *psk, *psk_len, psk_len); + if (status != PSA_SUCCESS) { + mbedtls_free((void *) *psk); + *psk = NULL; + return PSA_TO_MBEDTLS_ERR(status); + } + return 0; +#else + *psk = ssl->handshake->psk; + *psk_len = ssl->handshake->psk_len; + if (*psk == NULL) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + return 0; +#endif /* !MBEDTLS_USE_PSA_CRYPTO */ +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls13_keys.h b/r5dev/thirdparty/mbedtls/ssl_tls13_keys.h new file mode 100644 index 00000000..21e9b4d7 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls13_keys.h @@ -0,0 +1,663 @@ +/* + * TLS 1.3 key schedule + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 ( the "License" ); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#if !defined(MBEDTLS_SSL_TLS1_3_KEYS_H) +#define MBEDTLS_SSL_TLS1_3_KEYS_H + +/* This requires MBEDTLS_SSL_TLS1_3_LABEL( idx, name, string ) to be defined at + * the point of use. See e.g. the definition of mbedtls_ssl_tls13_labels_union + * below. */ +#define MBEDTLS_SSL_TLS1_3_LABEL_LIST \ + MBEDTLS_SSL_TLS1_3_LABEL(finished, "finished") \ + MBEDTLS_SSL_TLS1_3_LABEL(resumption, "resumption") \ + MBEDTLS_SSL_TLS1_3_LABEL(traffic_upd, "traffic upd") \ + MBEDTLS_SSL_TLS1_3_LABEL(exporter, "exporter") \ + MBEDTLS_SSL_TLS1_3_LABEL(key, "key") \ + MBEDTLS_SSL_TLS1_3_LABEL(iv, "iv") \ + MBEDTLS_SSL_TLS1_3_LABEL(c_hs_traffic, "c hs traffic") \ + MBEDTLS_SSL_TLS1_3_LABEL(c_ap_traffic, "c ap traffic") \ + MBEDTLS_SSL_TLS1_3_LABEL(c_e_traffic, "c e traffic") \ + MBEDTLS_SSL_TLS1_3_LABEL(s_hs_traffic, "s hs traffic") \ + MBEDTLS_SSL_TLS1_3_LABEL(s_ap_traffic, "s ap traffic") \ + MBEDTLS_SSL_TLS1_3_LABEL(s_e_traffic, "s e traffic") \ + MBEDTLS_SSL_TLS1_3_LABEL(e_exp_master, "e exp master") \ + MBEDTLS_SSL_TLS1_3_LABEL(res_master, "res master") \ + MBEDTLS_SSL_TLS1_3_LABEL(exp_master, "exp master") \ + MBEDTLS_SSL_TLS1_3_LABEL(ext_binder, "ext binder") \ + MBEDTLS_SSL_TLS1_3_LABEL(res_binder, "res binder") \ + MBEDTLS_SSL_TLS1_3_LABEL(derived, "derived") \ + MBEDTLS_SSL_TLS1_3_LABEL(client_cv, "TLS 1.3, client CertificateVerify") \ + MBEDTLS_SSL_TLS1_3_LABEL(server_cv, "TLS 1.3, server CertificateVerify") + +#define MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED 0 +#define MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED 1 + +#define MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL 0 +#define MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION 1 + +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#define MBEDTLS_SSL_TLS1_3_LABEL(name, string) \ + const unsigned char name [sizeof(string) - 1]; + +union mbedtls_ssl_tls13_labels_union { + MBEDTLS_SSL_TLS1_3_LABEL_LIST +}; +struct mbedtls_ssl_tls13_labels_struct { + MBEDTLS_SSL_TLS1_3_LABEL_LIST +}; +#undef MBEDTLS_SSL_TLS1_3_LABEL + +extern const struct mbedtls_ssl_tls13_labels_struct mbedtls_ssl_tls13_labels; + +#define MBEDTLS_SSL_TLS1_3_LBL_LEN(LABEL) \ + sizeof(mbedtls_ssl_tls13_labels.LABEL) + +#define MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(LABEL) \ + mbedtls_ssl_tls13_labels.LABEL, \ + MBEDTLS_SSL_TLS1_3_LBL_LEN(LABEL) + +#define MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_LABEL_LEN \ + sizeof(union mbedtls_ssl_tls13_labels_union) + +/* The maximum length of HKDF contexts used in the TLS 1.3 standard. + * Since contexts are always hashes of message transcripts, this can + * be approximated from above by the maximum hash size. */ +#define MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_CONTEXT_LEN \ + PSA_HASH_MAX_SIZE + +/* Maximum desired length for expanded key material generated + * by HKDF-Expand-Label. + * + * Warning: If this ever needs to be increased, the implementation + * ssl_tls13_hkdf_encode_label() in ssl_tls13_keys.c needs to be + * adjusted since it currently assumes that HKDF key expansion + * is never used with more than 255 Bytes of output. */ +#define MBEDTLS_SSL_TLS1_3_KEY_SCHEDULE_MAX_EXPANSION_LEN 255 + +/** + * \brief The \c HKDF-Expand-Label function from + * the TLS 1.3 standard RFC 8446. + * + * + * HKDF-Expand-Label( Secret, Label, Context, Length ) = + * HKDF-Expand( Secret, HkdfLabel, Length ) + * + * + * \param hash_alg The identifier for the hash algorithm to use. + * \param secret The \c Secret argument to \c HKDF-Expand-Label. + * This must be a readable buffer of length + * \p secret_len Bytes. + * \param secret_len The length of \p secret in Bytes. + * \param label The \c Label argument to \c HKDF-Expand-Label. + * This must be a readable buffer of length + * \p label_len Bytes. + * \param label_len The length of \p label in Bytes. + * \param ctx The \c Context argument to \c HKDF-Expand-Label. + * This must be a readable buffer of length \p ctx_len Bytes. + * \param ctx_len The length of \p context in Bytes. + * \param buf The destination buffer to hold the expanded secret. + * This must be a writable buffer of length \p buf_len Bytes. + * \param buf_len The desired size of the expanded secret in Bytes. + * + * \returns \c 0 on success. + * \return A negative error code on failure. + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_hkdf_expand_label( + psa_algorithm_t hash_alg, + const unsigned char *secret, size_t secret_len, + const unsigned char *label, size_t label_len, + const unsigned char *ctx, size_t ctx_len, + unsigned char *buf, size_t buf_len); + +/** + * \brief This function is part of the TLS 1.3 key schedule. + * It extracts key and IV for the actual client/server traffic + * from the client/server traffic secrets. + * + * From RFC 8446: + * + * + * [sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length) + * [sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)* + * + * + * \param hash_alg The identifier for the hash algorithm to be used + * for the HKDF-based expansion of the secret. + * \param client_secret The client traffic secret. + * This must be a readable buffer of size + * \p secret_len Bytes + * \param server_secret The server traffic secret. + * This must be a readable buffer of size + * \p secret_len Bytes + * \param secret_len Length of the secrets \p client_secret and + * \p server_secret in Bytes. + * \param key_len The desired length of the key to be extracted in Bytes. + * \param iv_len The desired length of the IV to be extracted in Bytes. + * \param keys The address of the structure holding the generated + * keys and IVs. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_make_traffic_keys( + psa_algorithm_t hash_alg, + const unsigned char *client_secret, + const unsigned char *server_secret, size_t secret_len, + size_t key_len, size_t iv_len, + mbedtls_ssl_key_set *keys); + +/** + * \brief The \c Derive-Secret function from the TLS 1.3 standard RFC 8446. + * + * + * Derive-Secret( Secret, Label, Messages ) = + * HKDF-Expand-Label( Secret, Label, + * Hash( Messages ), + * Hash.Length ) ) + * + * + * \param hash_alg The identifier for the hash function used for the + * applications of HKDF. + * \param secret The \c Secret argument to the \c Derive-Secret function. + * This must be a readable buffer of length + * \p secret_len Bytes. + * \param secret_len The length of \p secret in Bytes. + * \param label The \c Label argument to the \c Derive-Secret function. + * This must be a readable buffer of length + * \p label_len Bytes. + * \param label_len The length of \p label in Bytes. + * \param ctx The hash of the \c Messages argument to the + * \c Derive-Secret function, or the \c Messages argument + * itself, depending on \p ctx_hashed. + * \param ctx_len The length of \p ctx in Bytes. + * \param ctx_hashed This indicates whether the \p ctx contains the hash of + * the \c Messages argument in the application of the + * \c Derive-Secret function + * (value MBEDTLS_SSL_TLS1_3_CONTEXT_HASHED), or whether + * it is the content of \c Messages itself, in which case + * the function takes care of the hashing + * (value MBEDTLS_SSL_TLS1_3_CONTEXT_UNHASHED). + * \param dstbuf The target buffer to write the output of + * \c Derive-Secret to. This must be a writable buffer of + * size \p dtsbuf_len Bytes. + * \param dstbuf_len The length of \p dstbuf in Bytes. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_derive_secret( + psa_algorithm_t hash_alg, + const unsigned char *secret, size_t secret_len, + const unsigned char *label, size_t label_len, + const unsigned char *ctx, size_t ctx_len, + int ctx_hashed, + unsigned char *dstbuf, size_t dstbuf_len); + +/** + * \brief Derive TLS 1.3 early data key material from early secret. + * + * This is a small wrapper invoking mbedtls_ssl_tls13_derive_secret() + * with the appropriate labels. + * + * + * Early Secret + * | + * +-----> Derive-Secret(., "c e traffic", ClientHello) + * | = client_early_traffic_secret + * | + * +-----> Derive-Secret(., "e exp master", ClientHello) + * . = early_exporter_master_secret + * . + * . + * + * + * \note To obtain the actual key and IV for the early data traffic, + * the client secret derived by this function need to be + * further processed by mbedtls_ssl_tls13_make_traffic_keys(). + * + * \note The binder key, which is also generated from the early secret, + * is omitted here. Its calculation is part of the separate routine + * mbedtls_ssl_tls13_create_psk_binder(). + * + * \param hash_alg The hash algorithm associated with the PSK for which + * early data key material is being derived. + * \param early_secret The early secret from which the early data key material + * should be derived. This must be a readable buffer whose + * length is the digest size of the hash algorithm + * represented by \p md_size. + * \param transcript The transcript of the handshake so far, calculated with + * respect to \p hash_alg. This must be a readable buffer + * whose length is the digest size of the hash algorithm + * represented by \p md_size. + * \param derived The address of the structure in which to store + * the early data key material. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_derive_early_secrets( + psa_algorithm_t hash_alg, + unsigned char const *early_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_early_secrets *derived); + +/** + * \brief Derive TLS 1.3 handshake key material from the handshake secret. + * + * This is a small wrapper invoking mbedtls_ssl_tls13_derive_secret() + * with the appropriate labels from the standard. + * + * + * Handshake Secret + * | + * +-----> Derive-Secret( ., "c hs traffic", + * | ClientHello...ServerHello ) + * | = client_handshake_traffic_secret + * | + * +-----> Derive-Secret( ., "s hs traffic", + * . ClientHello...ServerHello ) + * . = server_handshake_traffic_secret + * . + * + * + * \note To obtain the actual key and IV for the encrypted handshake traffic, + * the client and server secret derived by this function need to be + * further processed by mbedtls_ssl_tls13_make_traffic_keys(). + * + * \param hash_alg The hash algorithm associated with the ciphersuite + * that's being used for the connection. + * \param handshake_secret The handshake secret from which the handshake key + * material should be derived. This must be a readable + * buffer whose length is the digest size of the hash + * algorithm represented by \p md_size. + * \param transcript The transcript of the handshake so far, calculated + * with respect to \p hash_alg. This must be a readable + * buffer whose length is the digest size of the hash + * algorithm represented by \p md_size. + * \param derived The address of the structure in which to + * store the handshake key material. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_derive_handshake_secrets( + psa_algorithm_t hash_alg, + unsigned char const *handshake_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_handshake_secrets *derived); + +/** + * \brief Derive TLS 1.3 application key material from the master secret. + * + * This is a small wrapper invoking mbedtls_ssl_tls13_derive_secret() + * with the appropriate labels from the standard. + * + * + * Master Secret + * | + * +-----> Derive-Secret( ., "c ap traffic", + * | ClientHello...server Finished ) + * | = client_application_traffic_secret_0 + * | + * +-----> Derive-Secret( ., "s ap traffic", + * | ClientHello...Server Finished ) + * | = server_application_traffic_secret_0 + * | + * +-----> Derive-Secret( ., "exp master", + * . ClientHello...server Finished) + * . = exporter_master_secret + * . + * + * + * \note To obtain the actual key and IV for the (0-th) application traffic, + * the client and server secret derived by this function need to be + * further processed by mbedtls_ssl_tls13_make_traffic_keys(). + * + * \param hash_alg The hash algorithm associated with the ciphersuite + * that's being used for the connection. + * \param master_secret The master secret from which the application key + * material should be derived. This must be a readable + * buffer whose length is the digest size of the hash + * algorithm represented by \p md_size. + * \param transcript The transcript of the handshake up to and including + * the ServerFinished message, calculated with respect + * to \p hash_alg. This must be a readable buffer whose + * length is the digest size of the hash algorithm + * represented by \p hash_alg. + * \param derived The address of the structure in which to + * store the application key material. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_derive_application_secrets( + psa_algorithm_t hash_alg, + unsigned char const *master_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_application_secrets *derived); + +/** + * \brief Derive TLS 1.3 resumption master secret from the master secret. + * + * This is a small wrapper invoking mbedtls_ssl_tls13_derive_secret() + * with the appropriate labels from the standard. + * + * \param hash_alg The hash algorithm used in the application for which + * key material is being derived. + * \param application_secret The application secret from which the resumption master + * secret should be derived. This must be a readable + * buffer whose length is the digest size of the hash + * algorithm represented by \p md_size. + * \param transcript The transcript of the handshake up to and including + * the ClientFinished message, calculated with respect + * to \p hash_alg. This must be a readable buffer whose + * length is the digest size of the hash algorithm + * represented by \p hash_alg. + * \param transcript_len The length of \p transcript in Bytes. + * \param derived The address of the structure in which to + * store the resumption master secret. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_derive_resumption_master_secret( + psa_algorithm_t hash_alg, + unsigned char const *application_secret, + unsigned char const *transcript, size_t transcript_len, + mbedtls_ssl_tls13_application_secrets *derived); + +/** + * \brief Compute the next secret in the TLS 1.3 key schedule + * + * The TLS 1.3 key schedule proceeds as follows to compute + * the three main secrets during the handshake: The early + * secret for early data, the handshake secret for all + * other encrypted handshake messages, and the master + * secret for all application traffic. + * + * + * 0 + * | + * v + * PSK -> HKDF-Extract = Early Secret + * | + * v + * Derive-Secret( ., "derived", "" ) + * | + * v + * (EC)DHE -> HKDF-Extract = Handshake Secret + * | + * v + * Derive-Secret( ., "derived", "" ) + * | + * v + * 0 -> HKDF-Extract = Master Secret + * + * + * Each of the three secrets in turn is the basis for further + * key derivations, such as the derivation of traffic keys and IVs; + * see e.g. mbedtls_ssl_tls13_make_traffic_keys(). + * + * This function implements one step in this evolution of secrets: + * + * + * old_secret + * | + * v + * Derive-Secret( ., "derived", "" ) + * | + * v + * input -> HKDF-Extract = new_secret + * + * + * \param hash_alg The identifier for the hash function used for the + * applications of HKDF. + * \param secret_old The address of the buffer holding the old secret + * on function entry. If not \c NULL, this must be a + * readable buffer whose size matches the output size + * of the hash function represented by \p hash_alg. + * If \c NULL, an all \c 0 array will be used instead. + * \param input The address of the buffer holding the additional + * input for the key derivation (e.g., the PSK or the + * ephemeral (EC)DH secret). If not \c NULL, this must be + * a readable buffer whose size \p input_len Bytes. + * If \c NULL, an all \c 0 array will be used instead. + * \param input_len The length of \p input in Bytes. + * \param secret_new The address of the buffer holding the new secret + * on function exit. This must be a writable buffer + * whose size matches the output size of the hash + * function represented by \p hash_alg. + * This may be the same as \p secret_old. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_evolve_secret( + psa_algorithm_t hash_alg, + const unsigned char *secret_old, + const unsigned char *input, size_t input_len, + unsigned char *secret_new); + +/** + * \brief Calculate a TLS 1.3 PSK binder. + * + * \param ssl The SSL context. This is used for debugging only and may + * be \c NULL if MBEDTLS_DEBUG_C is disabled. + * \param hash_alg The hash algorithm associated to the PSK \p psk. + * \param psk The buffer holding the PSK for which to create a binder. + * \param psk_len The size of \p psk in bytes. + * \param psk_type This indicates whether the PSK \p psk is externally + * provisioned (#MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL) or a + * resumption PSK (#MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION). + * \param transcript The handshake transcript up to the point where the + * PSK binder calculation happens. This must be readable, + * and its size must be equal to the digest size of + * the hash algorithm represented by \p hash_alg. + * \param result The address at which to store the PSK binder on success. + * This must be writable, and its size must be equal to the + * digest size of the hash algorithm represented by + * \p hash_alg. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_create_psk_binder(mbedtls_ssl_context *ssl, + const psa_algorithm_t hash_alg, + unsigned char const *psk, size_t psk_len, + int psk_type, + unsigned char const *transcript, + unsigned char *result); + +/** + * \bref Setup an SSL transform structure representing the + * record protection mechanism used by TLS 1.3 + * + * \param transform The SSL transform structure to be created. This must have + * been initialized through mbedtls_ssl_transform_init() and + * not used in any other way prior to calling this function. + * In particular, this function does not clean up the + * transform structure prior to installing the new keys. + * \param endpoint Indicates whether the transform is for the client + * (value #MBEDTLS_SSL_IS_CLIENT) or the server + * (value #MBEDTLS_SSL_IS_SERVER). + * \param ciphersuite The numerical identifier for the ciphersuite to use. + * This must be one of the identifiers listed in + * ssl_ciphersuites.h. + * \param traffic_keys The key material to use. No reference is stored in + * the SSL transform being generated, and the caller + * should destroy the key material afterwards. + * \param ssl (Debug-only) The SSL context to use for debug output + * in case of failure. This parameter is only needed if + * #MBEDTLS_DEBUG_C is set, and is ignored otherwise. + * + * \return \c 0 on success. In this case, \p transform is ready to + * be used with mbedtls_ssl_transform_decrypt() and + * mbedtls_ssl_transform_encrypt(). + * \return A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_populate_transform(mbedtls_ssl_transform *transform, + int endpoint, + int ciphersuite, + mbedtls_ssl_key_set const *traffic_keys, + mbedtls_ssl_context *ssl); + +/* + * TLS 1.3 key schedule evolutions + * + * Early -> Handshake -> Application + * + * Small wrappers around mbedtls_ssl_tls13_evolve_secret(). + */ + +/** + * \brief Begin TLS 1.3 key schedule by calculating early secret. + * + * The TLS 1.3 key schedule can be viewed as a simple state machine + * with states Initial -> Early -> Handshake -> Application, and + * this function represents the Initial -> Early transition. + * + * \param ssl The SSL context to operate on. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_key_schedule_stage_early(mbedtls_ssl_context *ssl); + +/** + * \brief Compute TLS 1.3 resumption master secret. + * + * \param ssl The SSL context to operate on. This must be in + * key schedule stage \c Application, see + * mbedtls_ssl_tls13_key_schedule_stage_application(). + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_compute_resumption_master_secret(mbedtls_ssl_context *ssl); + +/** + * \brief Calculate the verify_data value for the client or server TLS 1.3 + * Finished message. + * + * \param ssl The SSL context to operate on. This must be in + * key schedule stage \c Handshake, see + * mbedtls_ssl_tls13_key_schedule_stage_application(). + * \param dst The address at which to write the verify_data value. + * \param dst_len The size of \p dst in bytes. + * \param actual_len The address at which to store the amount of data + * actually written to \p dst upon success. + * \param which The message to calculate the `verify_data` for: + * - #MBEDTLS_SSL_IS_CLIENT for the Client's Finished message + * - #MBEDTLS_SSL_IS_SERVER for the Server's Finished message + * + * \note Both client and server call this function twice, once to + * generate their own Finished message, and once to verify the + * peer's Finished message. + + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_calculate_verify_data(mbedtls_ssl_context *ssl, + unsigned char *dst, + size_t dst_len, + size_t *actual_len, + int which); + +#if defined(MBEDTLS_SSL_EARLY_DATA) +/** + * \brief Compute TLS 1.3 early transform + * + * \param ssl The SSL context to operate on. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + * + * \warning The function does not compute the early master secret. Call + * mbedtls_ssl_tls13_key_schedule_stage_early() before to + * call this function to generate the early master secret. + * \note For a client/server endpoint, the function computes only the + * encryption/decryption part of the transform as the decryption/ + * encryption part is not defined by the specification (no early + * traffic from the server to the client). + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_compute_early_transform(mbedtls_ssl_context *ssl); +#endif /* MBEDTLS_SSL_EARLY_DATA */ + +/** + * \brief Compute TLS 1.3 handshake transform + * + * \param ssl The SSL context to operate on. The early secret must have been + * computed. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_compute_handshake_transform(mbedtls_ssl_context *ssl); + +/** + * \brief Compute TLS 1.3 application transform + * + * \param ssl The SSL context to operate on. The early secret must have been + * computed. + * + * \returns \c 0 on success. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_compute_application_transform(mbedtls_ssl_context *ssl); + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +/** + * \brief Export TLS 1.3 PSK from handshake context + * + * \param[in] ssl The SSL context to operate on. + * \param[out] psk PSK output pointer. + * \param[out] psk_len Length of PSK. + * + * \returns \c 0 if there is a configured PSK and it was exported + * successfully. + * \returns A negative error code on failure. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +int mbedtls_ssl_tls13_export_handshake_psk(mbedtls_ssl_context *ssl, + unsigned char **psk, + size_t *psk_len); +#endif + +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ + +#endif /* MBEDTLS_SSL_TLS1_3_KEYS_H */ diff --git a/r5dev/thirdparty/mbedtls/ssl_tls13_server.c b/r5dev/thirdparty/mbedtls/ssl_tls13_server.c new file mode 100644 index 00000000..b5a0c9e5 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/ssl_tls13_server.c @@ -0,0 +1,3084 @@ +/* + * TLS 1.3 server-side functions + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_PROTO_TLS1_3) + +#include "mbedtls/debug.h" +#include "mbedtls/error.h" +#include "mbedtls/platform.h" +#include "mbedtls/constant_time.h" + +#include "ssl_misc.h" +#include "ssl_tls13_keys.h" +#include "ssl_debug_helpers.h" + + +static const mbedtls_ssl_ciphersuite_t *ssl_tls13_validate_peer_ciphersuite( + mbedtls_ssl_context *ssl, + unsigned int cipher_suite) +{ + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + if (!mbedtls_ssl_tls13_cipher_suite_is_offered(ssl, cipher_suite)) { + return NULL; + } + + ciphersuite_info = mbedtls_ssl_ciphersuite_from_id(cipher_suite); + if ((mbedtls_ssl_validate_ciphersuite(ssl, ciphersuite_info, + ssl->tls_version, + ssl->tls_version) != 0)) { + return NULL; + } + return ciphersuite_info; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +/* From RFC 8446: + * + * enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode; + * struct { + * PskKeyExchangeMode ke_modes<1..255>; + * } PskKeyExchangeModes; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_key_exchange_modes_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + const unsigned char *p = buf; + size_t ke_modes_len; + int ke_modes = 0; + + /* Read ke_modes length (1 Byte) */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 1); + ke_modes_len = *p++; + /* Currently, there are only two PSK modes, so even without looking + * at the content, something's wrong if the list has more than 2 items. */ + if (ke_modes_len > 2) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, ke_modes_len); + + while (ke_modes_len-- != 0) { + switch (*p++) { + case MBEDTLS_SSL_TLS1_3_PSK_MODE_PURE: + ke_modes |= MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK; + MBEDTLS_SSL_DEBUG_MSG(3, ("Found PSK KEX MODE")); + break; + case MBEDTLS_SSL_TLS1_3_PSK_MODE_ECDHE: + ke_modes |= MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL; + MBEDTLS_SSL_DEBUG_MSG(3, ("Found PSK_EPHEMERAL KEX MODE")); + break; + default: + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + } + + ssl->handshake->tls13_kex_modes = ke_modes; + return 0; +} + +#define SSL_TLS1_3_OFFERED_PSK_NOT_MATCH 1 +#define SSL_TLS1_3_OFFERED_PSK_MATCH 0 + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_offered_psks_check_identity_match_ticket( + mbedtls_ssl_context *ssl, + const unsigned char *identity, + size_t identity_len, + uint32_t obfuscated_ticket_age, + mbedtls_ssl_session *session) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *ticket_buffer; +#if defined(MBEDTLS_HAVE_TIME) + mbedtls_time_t now; + uint64_t age_in_s; + int64_t age_diff_in_ms; +#endif + + ((void) obfuscated_ticket_age); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> check_identity_match_ticket")); + + /* Ticket parser is not configured, Skip */ + if (ssl->conf->f_ticket_parse == NULL || identity_len == 0) { + return 0; + } + + /* We create a copy of the encrypted ticket since the ticket parsing + * function is allowed to use its input buffer as an output buffer + * (in-place decryption). We do, however, need the original buffer for + * computing the PSK binder value. + */ + ticket_buffer = mbedtls_calloc(1, identity_len); + if (ticket_buffer == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("buffer too small")); + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + memcpy(ticket_buffer, identity, identity_len); + + if ((ret = ssl->conf->f_ticket_parse(ssl->conf->p_ticket, + session, + ticket_buffer, identity_len)) != 0) { + if (ret == MBEDTLS_ERR_SSL_INVALID_MAC) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket is not authentic")); + } else if (ret == MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED) { + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket is expired")); + } else { + MBEDTLS_SSL_DEBUG_RET(1, "ticket_parse", ret); + } + } + + /* We delete the temporary buffer */ + mbedtls_free(ticket_buffer); + + if (ret != 0) { + goto exit; + } + + /* RFC 8446 section 4.2.9 + * + * Servers SHOULD NOT send NewSessionTicket with tickets that are not + * compatible with the advertised modes; however, if a server does so, + * the impact will just be that the client's attempts at resumption fail. + * + * We regard the ticket with incompatible key exchange modes as not match. + */ + ret = MBEDTLS_ERR_ERROR_GENERIC_ERROR; + MBEDTLS_SSL_PRINT_TICKET_FLAGS(4, + session->ticket_flags); + if (mbedtls_ssl_tls13_check_kex_modes( + ssl, + mbedtls_ssl_session_get_ticket_flags( + session, + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ALL))) { + MBEDTLS_SSL_DEBUG_MSG(3, ("No suitable key exchange mode")); + goto exit; + } + + ret = MBEDTLS_ERR_SSL_SESSION_TICKET_EXPIRED; +#if defined(MBEDTLS_HAVE_TIME) + now = mbedtls_time(NULL); + + if (now < session->start) { + MBEDTLS_SSL_DEBUG_MSG( + 3, ("Invalid ticket start time ( now=%" MBEDTLS_PRINTF_LONGLONG + ", start=%" MBEDTLS_PRINTF_LONGLONG " )", + (long long) now, (long long) session->start)); + goto exit; + } + + age_in_s = (uint64_t) (now - session->start); + + /* RFC 8446 section 4.6.1 + * + * Servers MUST NOT use any value greater than 604800 seconds (7 days). + * + * RFC 8446 section 4.2.11.1 + * + * Clients MUST NOT attempt to use tickets which have ages greater than + * the "ticket_lifetime" value which was provided with the ticket. + * + * For time being, the age MUST be less than 604800 seconds (7 days). + */ + if (age_in_s > 604800) { + MBEDTLS_SSL_DEBUG_MSG( + 3, ("Ticket age exceeds limitation ticket_age=%lu", + (long unsigned int) age_in_s)); + goto exit; + } + + /* RFC 8446 section 4.2.10 + * + * For PSKs provisioned via NewSessionTicket, a server MUST validate that + * the ticket age for the selected PSK identity (computed by subtracting + * ticket_age_add from PskIdentity.obfuscated_ticket_age modulo 2^32) is + * within a small tolerance of the time since the ticket was issued. + * + * NOTE: When `now == session->start`, `age_diff_in_ms` may be negative + * as the age units are different on the server (s) and in the + * client (ms) side. Add a -1000 ms tolerance window to take this + * into account. + */ + age_diff_in_ms = age_in_s * 1000; + age_diff_in_ms -= (obfuscated_ticket_age - session->ticket_age_add); + if (age_diff_in_ms <= -1000 || + age_diff_in_ms > MBEDTLS_SSL_TLS1_3_TICKET_AGE_TOLERANCE) { + MBEDTLS_SSL_DEBUG_MSG( + 3, ("Ticket age outside tolerance window ( diff=%d )", + (int) age_diff_in_ms)); + goto exit; + } + + ret = 0; + +#endif /* MBEDTLS_HAVE_TIME */ + +exit: + if (ret != 0) { + mbedtls_ssl_session_free(session); + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= check_identity_match_ticket")); + return ret; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_offered_psks_check_identity_match( + mbedtls_ssl_context *ssl, + const unsigned char *identity, + size_t identity_len, + uint32_t obfuscated_ticket_age, + int *psk_type, + mbedtls_ssl_session *session) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ((void) session); + ((void) obfuscated_ticket_age); + *psk_type = MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL; + + MBEDTLS_SSL_DEBUG_BUF(4, "identity", identity, identity_len); + ssl->handshake->resume = 0; + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (ssl_tls13_offered_psks_check_identity_match_ticket( + ssl, identity, identity_len, obfuscated_ticket_age, + session) == SSL_TLS1_3_OFFERED_PSK_MATCH) { + ssl->handshake->resume = 1; + *psk_type = MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION; + ret = mbedtls_ssl_set_hs_psk(ssl, + session->resumption_key, + session->resumption_key_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(4, "Ticket-resumed PSK:", + session->resumption_key, + session->resumption_key_len); + MBEDTLS_SSL_DEBUG_MSG(4, ("ticket: obfuscated_ticket_age: %u", + (unsigned) obfuscated_ticket_age)); + return SSL_TLS1_3_OFFERED_PSK_MATCH; + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + + /* Check identity with external configured function */ + if (ssl->conf->f_psk != NULL) { + if (ssl->conf->f_psk( + ssl->conf->p_psk, ssl, identity, identity_len) == 0) { + return SSL_TLS1_3_OFFERED_PSK_MATCH; + } + return SSL_TLS1_3_OFFERED_PSK_NOT_MATCH; + } + + MBEDTLS_SSL_DEBUG_BUF(5, "identity", identity, identity_len); + /* Check identity with pre-configured psk */ + if (ssl->conf->psk_identity != NULL && + identity_len == ssl->conf->psk_identity_len && + mbedtls_ct_memcmp(ssl->conf->psk_identity, + identity, identity_len) == 0) { + ret = mbedtls_ssl_set_hs_psk(ssl, ssl->conf->psk, ssl->conf->psk_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_set_hs_psk", ret); + return ret; + } + return SSL_TLS1_3_OFFERED_PSK_MATCH; + } + + return SSL_TLS1_3_OFFERED_PSK_NOT_MATCH; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_offered_psks_check_binder_match(mbedtls_ssl_context *ssl, + const unsigned char *binder, + size_t binder_len, + int psk_type, + psa_algorithm_t psk_hash_alg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + unsigned char transcript[PSA_HASH_MAX_SIZE]; + size_t transcript_len; + unsigned char *psk; + size_t psk_len; + unsigned char server_computed_binder[PSA_HASH_MAX_SIZE]; + + /* Get current state of handshake transcript. */ + ret = mbedtls_ssl_get_handshake_transcript( + ssl, mbedtls_hash_info_md_from_psa(psk_hash_alg), + transcript, sizeof(transcript), &transcript_len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_export_handshake_psk(ssl, &psk, &psk_len); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_create_psk_binder(ssl, psk_hash_alg, + psk, psk_len, psk_type, + transcript, + server_computed_binder); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + mbedtls_free((void *) psk); +#endif + if (ret != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("PSK binder calculation failed.")); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "psk binder ( computed ): ", + server_computed_binder, transcript_len); + MBEDTLS_SSL_DEBUG_BUF(3, "psk binder ( received ): ", binder, binder_len); + + if (mbedtls_ct_memcmp(server_computed_binder, binder, binder_len) == 0) { + return SSL_TLS1_3_OFFERED_PSK_MATCH; + } + + mbedtls_platform_zeroize(server_computed_binder, + sizeof(server_computed_binder)); + return SSL_TLS1_3_OFFERED_PSK_NOT_MATCH; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_select_ciphersuite_for_psk( + mbedtls_ssl_context *ssl, + const unsigned char *cipher_suites, + const unsigned char *cipher_suites_end, + uint16_t *selected_ciphersuite, + const mbedtls_ssl_ciphersuite_t **selected_ciphersuite_info) +{ + psa_algorithm_t psk_hash_alg = PSA_ALG_SHA_256; + + *selected_ciphersuite = 0; + *selected_ciphersuite_info = NULL; + + /* RFC 8446, page 55. + * + * For externally established PSKs, the Hash algorithm MUST be set when the + * PSK is established or default to SHA-256 if no such algorithm is defined. + * + */ + + /* + * Search for a matching ciphersuite + */ + for (const unsigned char *p = cipher_suites; + p < cipher_suites_end; p += 2) { + uint16_t cipher_suite; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + + cipher_suite = MBEDTLS_GET_UINT16_BE(p, 0); + ciphersuite_info = ssl_tls13_validate_peer_ciphersuite(ssl, + cipher_suite); + if (ciphersuite_info == NULL) { + continue; + } + + /* MAC of selected ciphersuite MUST be same with PSK binder if exist. + * Otherwise, client should reject. + */ + if (psk_hash_alg == mbedtls_psa_translate_md(ciphersuite_info->mac)) { + *selected_ciphersuite = cipher_suite; + *selected_ciphersuite_info = ciphersuite_info; + return 0; + } + } + MBEDTLS_SSL_DEBUG_MSG(2, ("No matched ciphersuite")); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; +} + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_select_ciphersuite_for_resumption( + mbedtls_ssl_context *ssl, + const unsigned char *cipher_suites, + const unsigned char *cipher_suites_end, + mbedtls_ssl_session *session, + uint16_t *selected_ciphersuite, + const mbedtls_ssl_ciphersuite_t **selected_ciphersuite_info) +{ + + *selected_ciphersuite = 0; + *selected_ciphersuite_info = NULL; + for (const unsigned char *p = cipher_suites; p < cipher_suites_end; p += 2) { + uint16_t cipher_suite = MBEDTLS_GET_UINT16_BE(p, 0); + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + + if (cipher_suite != session->ciphersuite) { + continue; + } + + ciphersuite_info = ssl_tls13_validate_peer_ciphersuite(ssl, + cipher_suite); + if (ciphersuite_info == NULL) { + continue; + } + + *selected_ciphersuite = cipher_suite; + *selected_ciphersuite_info = ciphersuite_info; + + return 0; + } + + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_session_copy_ticket(mbedtls_ssl_session *dst, + const mbedtls_ssl_session *src) +{ + dst->ticket_age_add = src->ticket_age_add; + dst->ticket_flags = src->ticket_flags; + dst->resumption_key_len = src->resumption_key_len; + if (src->resumption_key_len == 0) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + memcpy(dst->resumption_key, src->resumption_key, src->resumption_key_len); + + return 0; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +/* Parser for pre_shared_key extension in client hello + * struct { + * opaque identity<1..2^16-1>; + * uint32 obfuscated_ticket_age; + * } PskIdentity; + * + * opaque PskBinderEntry<32..255>; + * + * struct { + * PskIdentity identities<7..2^16-1>; + * PskBinderEntry binders<33..2^16-1>; + * } OfferedPsks; + * + * struct { + * select (Handshake.msg_type) { + * case client_hello: OfferedPsks; + * .... + * }; + * } PreSharedKeyExtension; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_pre_shared_key_ext(mbedtls_ssl_context *ssl, + const unsigned char *pre_shared_key_ext, + const unsigned char *pre_shared_key_ext_end, + const unsigned char *ciphersuites, + const unsigned char *ciphersuites_end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *identities = pre_shared_key_ext; + const unsigned char *p_identity_len; + size_t identities_len; + const unsigned char *identities_end; + const unsigned char *binders; + const unsigned char *p_binder_len; + size_t binders_len; + const unsigned char *binders_end; + int matched_identity = -1; + int identity_id = -1; + + MBEDTLS_SSL_DEBUG_BUF(3, "pre_shared_key extension", + pre_shared_key_ext, + pre_shared_key_ext_end - pre_shared_key_ext); + + /* identities_len 2 bytes + * identities_data >= 7 bytes + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(identities, pre_shared_key_ext_end, 7 + 2); + identities_len = MBEDTLS_GET_UINT16_BE(identities, 0); + p_identity_len = identities + 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p_identity_len, pre_shared_key_ext_end, + identities_len); + identities_end = p_identity_len + identities_len; + + /* binders_len 2 bytes + * binders >= 33 bytes + */ + binders = identities_end; + MBEDTLS_SSL_CHK_BUF_READ_PTR(binders, pre_shared_key_ext_end, 33 + 2); + binders_len = MBEDTLS_GET_UINT16_BE(binders, 0); + p_binder_len = binders + 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p_binder_len, pre_shared_key_ext_end, binders_len); + binders_end = p_binder_len + binders_len; + + ret = ssl->handshake->update_checksum(ssl, pre_shared_key_ext, + identities_end - pre_shared_key_ext); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret); + return ret; + } + + while (p_identity_len < identities_end && p_binder_len < binders_end) { + const unsigned char *identity; + size_t identity_len; + uint32_t obfuscated_ticket_age; + const unsigned char *binder; + size_t binder_len; + int psk_type; + uint16_t cipher_suite; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_session session; + mbedtls_ssl_session_init(&session); +#endif + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p_identity_len, identities_end, 2 + 1 + 4); + identity_len = MBEDTLS_GET_UINT16_BE(p_identity_len, 0); + identity = p_identity_len + 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(identity, identities_end, identity_len + 4); + obfuscated_ticket_age = MBEDTLS_GET_UINT32_BE(identity, identity_len); + p_identity_len += identity_len + 6; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p_binder_len, binders_end, 1 + 32); + binder_len = *p_binder_len; + binder = p_binder_len + 1; + MBEDTLS_SSL_CHK_BUF_READ_PTR(binder, binders_end, binder_len); + p_binder_len += binder_len + 1; + + identity_id++; + if (matched_identity != -1) { + continue; + } + + ret = ssl_tls13_offered_psks_check_identity_match( + ssl, identity, identity_len, obfuscated_ticket_age, + &psk_type, &session); + if (ret != SSL_TLS1_3_OFFERED_PSK_MATCH) { + continue; + } + + MBEDTLS_SSL_DEBUG_MSG(4, ("found matched identity")); + switch (psk_type) { + case MBEDTLS_SSL_TLS1_3_PSK_EXTERNAL: + ret = ssl_tls13_select_ciphersuite_for_psk( + ssl, ciphersuites, ciphersuites_end, + &cipher_suite, &ciphersuite_info); + break; + case MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION: +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + ret = ssl_tls13_select_ciphersuite_for_resumption( + ssl, ciphersuites, ciphersuites_end, &session, + &cipher_suite, &ciphersuite_info); + if (ret != 0) { + mbedtls_ssl_session_free(&session); + } +#else + ret = MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +#endif + break; + default: + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + if (ret != 0) { + /* See below, no cipher_suite available, abort handshake */ + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + MBEDTLS_SSL_DEBUG_RET( + 2, "ssl_tls13_select_ciphersuite", ret); + return ret; + } + + ret = ssl_tls13_offered_psks_check_binder_match( + ssl, binder, binder_len, psk_type, + mbedtls_psa_translate_md(ciphersuite_info->mac)); + if (ret != SSL_TLS1_3_OFFERED_PSK_MATCH) { + /* For security reasons, the handshake should be aborted when we + * fail to validate a binder value. See RFC 8446 section 4.2.11.2 + * and appendix E.6. */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + mbedtls_ssl_session_free(&session); +#endif + MBEDTLS_SSL_DEBUG_MSG(3, ("Invalid binder.")); + MBEDTLS_SSL_DEBUG_RET(1, + "ssl_tls13_offered_psks_check_binder_match", ret); + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return ret; + } + + matched_identity = identity_id; + + /* Update handshake parameters */ + ssl->handshake->ciphersuite_info = ciphersuite_info; + ssl->session_negotiate->ciphersuite = cipher_suite; + MBEDTLS_SSL_DEBUG_MSG(2, ("overwrite ciphersuite: %04x - %s", + cipher_suite, ciphersuite_info->name)); +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + if (psk_type == MBEDTLS_SSL_TLS1_3_PSK_RESUMPTION) { + ret = ssl_tls13_session_copy_ticket(ssl->session_negotiate, + &session); + mbedtls_ssl_session_free(&session); + if (ret != 0) { + return ret; + } + } +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + } + + if (p_identity_len != identities_end || p_binder_len != binders_end) { + MBEDTLS_SSL_DEBUG_MSG(3, ("pre_shared_key extension decode error")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Update the handshake transcript with the binder list. */ + ret = ssl->handshake->update_checksum(ssl, + identities_end, + (size_t) (binders_end - identities_end)); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret); + return ret; + } + if (matched_identity == -1) { + MBEDTLS_SSL_DEBUG_MSG(3, ("No matched PSK or ticket.")); + return MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY; + } + + ssl->handshake->selected_identity = (uint16_t) matched_identity; + MBEDTLS_SSL_DEBUG_MSG(3, ("Pre shared key found")); + + return 0; +} + +/* + * struct { + * select ( Handshake.msg_type ) { + * .... + * case server_hello: + * uint16 selected_identity; + * } + * } PreSharedKeyExtension; + */ +static int ssl_tls13_write_server_pre_shared_key_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *olen) +{ + unsigned char *p = (unsigned char *) buf; + + *olen = 0; + + int not_using_psk = 0; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + not_using_psk = (mbedtls_svc_key_id_is_null(ssl->handshake->psk_opaque)); +#else + not_using_psk = (ssl->handshake->psk == NULL); +#endif + if (not_using_psk) { + /* We shouldn't have called this extension writer unless we've + * chosen to use a PSK. */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, adding pre_shared_key extension")); + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 6); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_PRE_SHARED_KEY, p, 0); + MBEDTLS_PUT_UINT16_BE(2, p, 2); + + MBEDTLS_PUT_UINT16_BE(ssl->handshake->selected_identity, p, 4); + + *olen = 6; + + MBEDTLS_SSL_DEBUG_MSG(4, ("sent selected_identity: %u", + ssl->handshake->selected_identity)); + + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_PRE_SHARED_KEY); + + return 0; +} + +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + +/* From RFC 8446: + * struct { + * ProtocolVersion versions<2..254>; + * } SupportedVersions; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_supported_versions_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + const unsigned char *p = buf; + size_t versions_len; + const unsigned char *versions_end; + uint16_t tls_version; + int tls13_supported = 0; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 1); + versions_len = p[0]; + p += 1; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, versions_len); + versions_end = p + versions_len; + while (p < versions_end) { + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, versions_end, 2); + tls_version = mbedtls_ssl_read_version(p, ssl->conf->transport); + p += 2; + + /* In this implementation we only support TLS 1.3 and DTLS 1.3. */ + if (tls_version == MBEDTLS_SSL_VERSION_TLS1_3) { + tls13_supported = 1; + break; + } + } + + if (!tls13_supported) { + MBEDTLS_SSL_DEBUG_MSG(1, ("TLS 1.3 is not supported by the client")); + + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION, + MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION); + return MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("Negotiated version. Supported is [%04x]", + (unsigned int) tls_version)); + + return 0; +} + +#if defined(MBEDTLS_ECDH_C) +/* + * + * From RFC 8446: + * enum { + * ... (0xFFFF) + * } NamedGroup; + * struct { + * NamedGroup named_group_list<2..2^16-1>; + * } NamedGroupList; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_supported_groups_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + const unsigned char *p = buf; + size_t named_group_list_len; + const unsigned char *named_group_list_end; + + MBEDTLS_SSL_DEBUG_BUF(3, "supported_groups extension", p, end - buf); + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + named_group_list_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, named_group_list_len); + named_group_list_end = p + named_group_list_len; + ssl->handshake->hrr_selected_group = 0; + + while (p < named_group_list_end) { + uint16_t named_group; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, named_group_list_end, 2); + named_group = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + MBEDTLS_SSL_DEBUG_MSG(2, + ("got named group: %s(%04x)", + mbedtls_ssl_named_group_to_str(named_group), + named_group)); + + if (!mbedtls_ssl_named_group_is_offered(ssl, named_group) || + !mbedtls_ssl_named_group_is_supported(named_group) || + ssl->handshake->hrr_selected_group != 0) { + continue; + } + + MBEDTLS_SSL_DEBUG_MSG(2, + ("add named group %s(%04x) into received list.", + mbedtls_ssl_named_group_to_str(named_group), + named_group)); + + ssl->handshake->hrr_selected_group = named_group; + } + + return 0; + +} +#endif /* MBEDTLS_ECDH_C */ + +#define SSL_TLS1_3_PARSE_KEY_SHARES_EXT_NO_MATCH 1 + +#if defined(MBEDTLS_ECDH_C) +/* + * ssl_tls13_parse_key_shares_ext() verifies whether the information in the + * extension is correct and stores the first acceptable key share and its associated group. + * + * Possible return values are: + * - 0: Successful processing of the client provided key share extension. + * - SSL_TLS1_3_PARSE_KEY_SHARES_EXT_NO_MATCH: The key shares provided by the client + * does not match a group supported by the server. A HelloRetryRequest will + * be needed. + * - A negative value for fatal errors. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_key_shares_ext(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char const *p = buf; + unsigned char const *client_shares_end; + size_t client_shares_len; + + /* From RFC 8446: + * + * struct { + * KeyShareEntry client_shares<0..2^16-1>; + * } KeyShareClientHello; + * + */ + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 2); + client_shares_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, client_shares_len); + + ssl->handshake->offered_group_id = 0; + client_shares_end = p + client_shares_len; + + /* We try to find a suitable key share entry and copy it to the + * handshake context. Later, we have to find out whether we can do + * something with the provided key share or whether we have to + * dismiss it and send a HelloRetryRequest message. + */ + + while (p < client_shares_end) { + uint16_t group; + size_t key_exchange_len; + const unsigned char *key_exchange; + + /* + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, client_shares_end, 4); + group = MBEDTLS_GET_UINT16_BE(p, 0); + key_exchange_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + key_exchange = p; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, client_shares_end, key_exchange_len); + p += key_exchange_len; + + /* Continue parsing even if we have already found a match, + * for input validation purposes. + */ + if (!mbedtls_ssl_named_group_is_offered(ssl, group) || + !mbedtls_ssl_named_group_is_supported(group) || + ssl->handshake->offered_group_id != 0) { + continue; + } + + /* + * For now, we only support ECDHE groups. + */ + if (mbedtls_ssl_tls13_named_group_is_ecdhe(group)) { + MBEDTLS_SSL_DEBUG_MSG(2, ("ECDH group: %s (%04x)", + mbedtls_ssl_named_group_to_str(group), + group)); + ret = mbedtls_ssl_tls13_read_public_ecdhe_share( + ssl, key_exchange - 2, key_exchange_len + 2); + if (ret != 0) { + return ret; + } + + } else { + MBEDTLS_SSL_DEBUG_MSG(4, ("Unrecognized NamedGroup %u", + (unsigned) group)); + continue; + } + + ssl->handshake->offered_group_id = group; + } + + + if (ssl->handshake->offered_group_id == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no matching key share")); + return SSL_TLS1_3_PARSE_KEY_SHARES_EXT_NO_MATCH; + } + return 0; +} +#endif /* MBEDTLS_ECDH_C */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_client_hello_has_exts(mbedtls_ssl_context *ssl, + int exts_mask) +{ + int masked = ssl->handshake->received_extensions & exts_mask; + return masked == exts_mask; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_client_hello_has_exts_for_ephemeral_key_exchange( + mbedtls_ssl_context *ssl) +{ + return ssl_tls13_client_hello_has_exts( + ssl, + MBEDTLS_SSL_EXT_MASK(SUPPORTED_GROUPS) | + MBEDTLS_SSL_EXT_MASK(KEY_SHARE) | + MBEDTLS_SSL_EXT_MASK(SIG_ALG)); +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_client_hello_has_exts_for_psk_key_exchange( + mbedtls_ssl_context *ssl) +{ + return ssl_tls13_client_hello_has_exts( + ssl, + MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY) | + MBEDTLS_SSL_EXT_MASK(PSK_KEY_EXCHANGE_MODES)); +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_client_hello_has_exts_for_psk_ephemeral_key_exchange( + mbedtls_ssl_context *ssl) +{ + return ssl_tls13_client_hello_has_exts( + ssl, + MBEDTLS_SSL_EXT_MASK(SUPPORTED_GROUPS) | + MBEDTLS_SSL_EXT_MASK(KEY_SHARE) | + MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY) | + MBEDTLS_SSL_EXT_MASK(PSK_KEY_EXCHANGE_MODES)); +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_check_ephemeral_key_exchange(mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + return mbedtls_ssl_conf_tls13_ephemeral_enabled(ssl) && + ssl_tls13_client_hello_has_exts_for_ephemeral_key_exchange(ssl); +#else + ((void) ssl); + return 0; +#endif +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_check_psk_key_exchange(mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED) + return mbedtls_ssl_conf_tls13_psk_enabled(ssl) && + mbedtls_ssl_tls13_psk_enabled(ssl) && + ssl_tls13_client_hello_has_exts_for_psk_key_exchange(ssl); +#else + ((void) ssl); + return 0; +#endif +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_check_psk_ephemeral_key_exchange(mbedtls_ssl_context *ssl) +{ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) + return mbedtls_ssl_conf_tls13_psk_ephemeral_enabled(ssl) && + mbedtls_ssl_tls13_psk_ephemeral_enabled(ssl) && + ssl_tls13_client_hello_has_exts_for_psk_ephemeral_key_exchange(ssl); +#else + ((void) ssl); + return 0; +#endif +} + +static int ssl_tls13_determine_key_exchange_mode(mbedtls_ssl_context *ssl) +{ + /* + * Determine the key exchange algorithm to use. + * There are three types of key exchanges supported in TLS 1.3: + * - (EC)DH with ECDSA, + * - (EC)DH with PSK, + * - plain PSK. + * + * The PSK-based key exchanges may additionally be used with 0-RTT. + * + * Our built-in order of preference is + * 1 ) (EC)DHE-PSK Mode ( psk_ephemeral ) + * 2 ) Certificate Mode ( ephemeral ) + * 3 ) Plain PSK Mode ( psk ) + */ + + ssl->handshake->key_exchange_mode = MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_NONE; + + if (ssl_tls13_check_psk_ephemeral_key_exchange(ssl)) { + ssl->handshake->key_exchange_mode = + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL; + MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: psk_ephemeral")); + } else + if (ssl_tls13_check_ephemeral_key_exchange(ssl)) { + ssl->handshake->key_exchange_mode = + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL; + MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: ephemeral")); + } else + if (ssl_tls13_check_psk_key_exchange(ssl)) { + ssl->handshake->key_exchange_mode = + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK; + MBEDTLS_SSL_DEBUG_MSG(2, ("key exchange mode: psk")); + } else { + MBEDTLS_SSL_DEBUG_MSG( + 1, + ("ClientHello message misses mandatory extensions.")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_MISSING_EXTENSION, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + return 0; + +} + +#if defined(MBEDTLS_X509_CRT_PARSE_C) && \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +static psa_algorithm_t ssl_tls13_iana_sig_alg_to_psa_alg(uint16_t sig_alg) +{ + switch (sig_alg) { + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256: + return PSA_ALG_ECDSA(PSA_ALG_SHA_256); + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP384R1_SHA384: + return PSA_ALG_ECDSA(PSA_ALG_SHA_384); + case MBEDTLS_TLS1_3_SIG_ECDSA_SECP521R1_SHA512: + return PSA_ALG_ECDSA(PSA_ALG_SHA_512); + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA256: + return PSA_ALG_RSA_PSS(PSA_ALG_SHA_256); + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA384: + return PSA_ALG_RSA_PSS(PSA_ALG_SHA_384); + case MBEDTLS_TLS1_3_SIG_RSA_PSS_RSAE_SHA512: + return PSA_ALG_RSA_PSS(PSA_ALG_SHA_512); + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA256: + return PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256); + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA384: + return PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_384); + case MBEDTLS_TLS1_3_SIG_RSA_PKCS1_SHA512: + return PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_512); + default: + return PSA_ALG_NONE; + } +} +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +/* + * Pick best ( private key, certificate chain ) pair based on the signature + * algorithms supported by the client. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_pick_key_cert(mbedtls_ssl_context *ssl) +{ + mbedtls_ssl_key_cert *key_cert, *key_cert_list; + const uint16_t *sig_alg = ssl->handshake->received_sig_algs; + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_key_cert != NULL) { + key_cert_list = ssl->handshake->sni_key_cert; + } else +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + key_cert_list = ssl->conf->key_cert; + + if (key_cert_list == NULL) { + MBEDTLS_SSL_DEBUG_MSG(3, ("server has no certificate")); + return -1; + } + + for (; *sig_alg != MBEDTLS_TLS1_3_SIG_NONE; sig_alg++) { + if (!mbedtls_ssl_sig_alg_is_offered(ssl, *sig_alg)) { + continue; + } + + if (!mbedtls_ssl_tls13_sig_alg_for_cert_verify_is_supported(*sig_alg)) { + continue; + } + + for (key_cert = key_cert_list; key_cert != NULL; + key_cert = key_cert->next) { +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_algorithm_t psa_alg = PSA_ALG_NONE; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + MBEDTLS_SSL_DEBUG_CRT(3, "certificate (chain) candidate", + key_cert->cert); + + /* + * This avoids sending the client a cert it'll reject based on + * keyUsage or other extensions. + */ + if (mbedtls_x509_crt_check_key_usage( + key_cert->cert, MBEDTLS_X509_KU_DIGITAL_SIGNATURE) != 0 || + mbedtls_x509_crt_check_extended_key_usage( + key_cert->cert, MBEDTLS_OID_SERVER_AUTH, + MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH)) != 0) { + MBEDTLS_SSL_DEBUG_MSG(3, ("certificate mismatch: " + "(extended) key usage extension")); + continue; + } + + MBEDTLS_SSL_DEBUG_MSG(3, + ("ssl_tls13_pick_key_cert:" + "check signature algorithm %s [%04x]", + mbedtls_ssl_sig_alg_to_str(*sig_alg), + *sig_alg)); +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_alg = ssl_tls13_iana_sig_alg_to_psa_alg(*sig_alg); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (mbedtls_ssl_tls13_check_sig_alg_cert_key_match( + *sig_alg, &key_cert->cert->pk) +#if defined(MBEDTLS_USE_PSA_CRYPTO) + && psa_alg != PSA_ALG_NONE && + mbedtls_pk_can_do_ext(&key_cert->cert->pk, psa_alg, + PSA_KEY_USAGE_SIGN_HASH) == 1 +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + ) { + ssl->handshake->key_cert = key_cert; + MBEDTLS_SSL_DEBUG_MSG(3, + ("ssl_tls13_pick_key_cert:" + "selected signature algorithm" + " %s [%04x]", + mbedtls_ssl_sig_alg_to_str(*sig_alg), + *sig_alg)); + MBEDTLS_SSL_DEBUG_CRT( + 3, "selected certificate (chain)", + ssl->handshake->key_cert->cert); + return 0; + } + } + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("ssl_tls13_pick_key_cert:" + "no suitable certificate found")); + return -1; +} +#endif /* MBEDTLS_X509_CRT_PARSE_C && + MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +/* + * + * STATE HANDLING: ClientHello + * + * There are three possible classes of outcomes when parsing the ClientHello: + * + * 1) The ClientHello was well-formed and matched the server's configuration. + * + * In this case, the server progresses to sending its ServerHello. + * + * 2) The ClientHello was well-formed but didn't match the server's + * configuration. + * + * For example, the client might not have offered a key share which + * the server supports, or the server might require a cookie. + * + * In this case, the server sends a HelloRetryRequest. + * + * 3) The ClientHello was ill-formed + * + * In this case, we abort the handshake. + * + */ + +/* + * Structure of this message: + * + * uint16 ProtocolVersion; + * opaque Random[32]; + * uint8 CipherSuite[2]; // Cryptographic suite selector + * + * struct { + * ProtocolVersion legacy_version = 0x0303; // TLS v1.2 + * Random random; + * opaque legacy_session_id<0..32>; + * CipherSuite cipher_suites<2..2^16-2>; + * opaque legacy_compression_methods<1..2^8-1>; + * Extension extensions<8..2^16-1>; + * } ClientHello; + */ + +#define SSL_CLIENT_HELLO_OK 0 +#define SSL_CLIENT_HELLO_HRR_REQUIRED 1 + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_parse_client_hello(mbedtls_ssl_context *ssl, + const unsigned char *buf, + const unsigned char *end) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned char *p = buf; + size_t legacy_session_id_len; + size_t cipher_suites_len; + const unsigned char *cipher_suites_end; + size_t extensions_len; + const unsigned char *extensions_end; + mbedtls_ssl_handshake_params *handshake = ssl->handshake; + int hrr_required = 0; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + const unsigned char *cipher_suites; + const unsigned char *pre_shared_key_ext = NULL; + const unsigned char *pre_shared_key_ext_end = NULL; +#endif + + /* + * ClientHello layout: + * 0 . 1 protocol version + * 2 . 33 random bytes + * 34 . 34 session id length ( 1 byte ) + * 35 . 34+x session id + * .. . .. ciphersuite list length ( 2 bytes ) + * .. . .. ciphersuite list + * .. . .. compression alg. list length ( 1 byte ) + * .. . .. compression alg. list + * .. . .. extensions length ( 2 bytes, optional ) + * .. . .. extensions ( optional ) + */ + + /* + * Minimal length ( with everything empty and extensions omitted ) is + * 2 + 32 + 1 + 2 + 1 = 38 bytes. Check that first, so that we can + * read at least up to session id length without worrying. + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, 38); + + /* ... + * ProtocolVersion legacy_version = 0x0303; // TLS 1.2 + * ... + * with ProtocolVersion defined as: + * uint16 ProtocolVersion; + */ + if (mbedtls_ssl_read_version(p, ssl->conf->transport) != + MBEDTLS_SSL_VERSION_TLS1_2) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Unsupported version of TLS.")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_PROTOCOL_VERSION, + MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION); + return MBEDTLS_ERR_SSL_BAD_PROTOCOL_VERSION; + } + p += 2; + + /* + * Only support TLS 1.3 currently, temporarily set the version. + */ + ssl->tls_version = MBEDTLS_SSL_VERSION_TLS1_3; + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + /* Store minor version for later use with ticket serialization. */ + ssl->session_negotiate->tls_version = MBEDTLS_SSL_VERSION_TLS1_3; + ssl->session_negotiate->endpoint = ssl->conf->endpoint; +#endif + + /* ... + * Random random; + * ... + * with Random defined as: + * opaque Random[32]; + */ + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, random bytes", + p, MBEDTLS_CLIENT_HELLO_RANDOM_LEN); + + memcpy(&handshake->randbytes[0], p, MBEDTLS_CLIENT_HELLO_RANDOM_LEN); + p += MBEDTLS_CLIENT_HELLO_RANDOM_LEN; + + /* ... + * opaque legacy_session_id<0..32>; + * ... + */ + legacy_session_id_len = p[0]; + p++; + + if (legacy_session_id_len > sizeof(ssl->session_negotiate->id)) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad client hello message")); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + ssl->session_negotiate->id_len = legacy_session_id_len; + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, session id", + p, legacy_session_id_len); + /* + * Check we have enough data for the legacy session identifier + * and the ciphersuite list length. + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, legacy_session_id_len + 2); + + memcpy(&ssl->session_negotiate->id[0], p, legacy_session_id_len); + p += legacy_session_id_len; + + cipher_suites_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + + /* + * The length of the ciphersuite list has to be even. + */ + if (cipher_suites_len & 1) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR, + MBEDTLS_ERR_SSL_DECODE_ERROR); + return MBEDTLS_ERR_SSL_DECODE_ERROR; + } + + /* Check we have enough data for the ciphersuite list, the legacy + * compression methods and the length of the extensions. + * + * cipher_suites cipher_suites_len bytes + * legacy_compression_methods 2 bytes + * extensions_len 2 bytes + */ + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, cipher_suites_len + 2 + 2); + + /* ... + * CipherSuite cipher_suites<2..2^16-2>; + * ... + * with CipherSuite defined as: + * uint8 CipherSuite[2]; + */ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + cipher_suites = p; +#endif + cipher_suites_end = p + cipher_suites_len; + MBEDTLS_SSL_DEBUG_BUF(3, "client hello, ciphersuitelist", + p, cipher_suites_len); + + /* + * Search for a matching ciphersuite + */ + for (; p < cipher_suites_end; p += 2) { + uint16_t cipher_suite; + const mbedtls_ssl_ciphersuite_t *ciphersuite_info; + + /* + * "cipher_suite_end - p is even" is an invariant of the loop. As + * cipher_suites_end - p > 0, we have cipher_suites_end - p >= 2 and + * it is thus safe to read two bytes. + */ + cipher_suite = MBEDTLS_GET_UINT16_BE(p, 0); + ciphersuite_info = ssl_tls13_validate_peer_ciphersuite( + ssl, cipher_suite); + if (ciphersuite_info == NULL) { + continue; + } + + ssl->session_negotiate->ciphersuite = cipher_suite; + handshake->ciphersuite_info = ciphersuite_info; + MBEDTLS_SSL_DEBUG_MSG(2, ("selected ciphersuite: %04x - %s", + cipher_suite, + ciphersuite_info->name)); + break; + } + + if (handshake->ciphersuite_info == NULL) { + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + p = cipher_suites_end; + + /* ... + * opaque legacy_compression_methods<1..2^8-1>; + * ... + */ + if (p[0] != 1 || p[1] != MBEDTLS_SSL_COMPRESS_NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("bad legacy compression method")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + p += 2; + + /* ... + * Extension extensions<8..2^16-1>; + * ... + * with Extension defined as: + * struct { + * ExtensionType extension_type; + * opaque extension_data<0..2^16-1>; + * } Extension; + */ + extensions_len = MBEDTLS_GET_UINT16_BE(p, 0); + p += 2; + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, end, extensions_len); + extensions_end = p + extensions_len; + + MBEDTLS_SSL_DEBUG_BUF(3, "client hello extensions", p, extensions_len); + + handshake->received_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + while (p < extensions_end) { + unsigned int extension_type; + size_t extension_data_len; + const unsigned char *extension_data_end; + + /* RFC 8446, section 4.2.11 + * + * The "pre_shared_key" extension MUST be the last extension in the + * ClientHello (this facilitates implementation as described below). + * Servers MUST check that it is the last extension and otherwise fail + * the handshake with an "illegal_parameter" alert. + */ + if (handshake->received_extensions & MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY)) { + MBEDTLS_SSL_DEBUG_MSG( + 3, ("pre_shared_key is not last extension.")); + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, 4); + extension_type = MBEDTLS_GET_UINT16_BE(p, 0); + extension_data_len = MBEDTLS_GET_UINT16_BE(p, 2); + p += 4; + + MBEDTLS_SSL_CHK_BUF_READ_PTR(p, extensions_end, extension_data_len); + extension_data_end = p + extension_data_len; + + ret = mbedtls_ssl_tls13_check_received_extension( + ssl, MBEDTLS_SSL_HS_CLIENT_HELLO, extension_type, + MBEDTLS_SSL_TLS1_3_ALLOWED_EXTS_OF_CH); + if (ret != 0) { + return ret; + } + + switch (extension_type) { +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + case MBEDTLS_TLS_EXT_SERVERNAME: + MBEDTLS_SSL_DEBUG_MSG(3, ("found ServerName extension")); + ret = mbedtls_ssl_parse_server_name_ext(ssl, p, + extension_data_end); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "mbedtls_ssl_parse_servername_ext", ret); + return ret; + } + break; +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + +#if defined(MBEDTLS_ECDH_C) + case MBEDTLS_TLS_EXT_SUPPORTED_GROUPS: + MBEDTLS_SSL_DEBUG_MSG(3, ("found supported group extension")); + + /* Supported Groups Extension + * + * When sent by the client, the "supported_groups" extension + * indicates the named groups which the client supports, + * ordered from most preferred to least preferred. + */ + ret = ssl_tls13_parse_supported_groups_ext( + ssl, p, extension_data_end); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_parse_supported_groups_ext", ret); + return ret; + } + + break; +#endif /* MBEDTLS_ECDH_C */ + +#if defined(MBEDTLS_ECDH_C) + case MBEDTLS_TLS_EXT_KEY_SHARE: + MBEDTLS_SSL_DEBUG_MSG(3, ("found key share extension")); + + /* + * Key Share Extension + * + * When sent by the client, the "key_share" extension + * contains the endpoint's cryptographic parameters for + * ECDHE/DHE key establishment methods. + */ + ret = ssl_tls13_parse_key_shares_ext( + ssl, p, extension_data_end); + if (ret == SSL_TLS1_3_PARSE_KEY_SHARES_EXT_NO_MATCH) { + MBEDTLS_SSL_DEBUG_MSG(2, ("HRR needed ")); + hrr_required = 1; + } + + if (ret < 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "ssl_tls13_parse_key_shares_ext", ret); + return ret; + } + + break; +#endif /* MBEDTLS_ECDH_C */ + + case MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS: + MBEDTLS_SSL_DEBUG_MSG(3, ("found supported versions extension")); + + ret = ssl_tls13_parse_supported_versions_ext( + ssl, p, extension_data_end); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + ("ssl_tls13_parse_supported_versions_ext"), ret); + return ret; + } + break; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + case MBEDTLS_TLS_EXT_PSK_KEY_EXCHANGE_MODES: + MBEDTLS_SSL_DEBUG_MSG(3, ("found psk key exchange modes extension")); + + ret = ssl_tls13_parse_key_exchange_modes_ext( + ssl, p, extension_data_end); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "ssl_tls13_parse_key_exchange_modes_ext", ret); + return ret; + } + + break; +#endif + + case MBEDTLS_TLS_EXT_PRE_SHARED_KEY: + MBEDTLS_SSL_DEBUG_MSG(3, ("found pre_shared_key extension")); + if ((handshake->received_extensions & + MBEDTLS_SSL_EXT_MASK(PSK_KEY_EXCHANGE_MODES)) == 0) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_ILLEGAL_PARAMETER, + MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER); + return MBEDTLS_ERR_SSL_ILLEGAL_PARAMETER; + } +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + /* Delay processing of the PSK identity once we have + * found out which algorithms to use. We keep a pointer + * to the buffer and the size for later processing. + */ + pre_shared_key_ext = p; + pre_shared_key_ext_end = extension_data_end; +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + break; + +#if defined(MBEDTLS_SSL_ALPN) + case MBEDTLS_TLS_EXT_ALPN: + MBEDTLS_SSL_DEBUG_MSG(3, ("found alpn extension")); + + ret = mbedtls_ssl_parse_alpn_ext(ssl, p, extension_data_end); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, ("mbedtls_ssl_parse_alpn_ext"), ret); + return ret; + } + break; +#endif /* MBEDTLS_SSL_ALPN */ + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + case MBEDTLS_TLS_EXT_SIG_ALG: + MBEDTLS_SSL_DEBUG_MSG(3, ("found signature_algorithms extension")); + + ret = mbedtls_ssl_parse_sig_alg_ext( + ssl, p, extension_data_end); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_MSG(1, + ( + "ssl_parse_supported_signature_algorithms_server_ext ( %d )", + ret)); + return ret; + } + break; +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) + case MBEDTLS_TLS_EXT_RECORD_SIZE_LIMIT: + MBEDTLS_SSL_DEBUG_MSG(3, ("found record_size_limit extension")); + + ret = mbedtls_ssl_tls13_parse_record_size_limit_ext(ssl, p, extension_data_end); + + /* TODO: Return unconditionally here until we handle the record size limit correctly. + * Once handled correctly, only return in case of errors. */ + return ret; + + break; +#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */ + + default: + MBEDTLS_SSL_PRINT_EXT( + 3, MBEDTLS_SSL_HS_CLIENT_HELLO, + extension_type, "( ignored )"); + break; + } + + p += extension_data_len; + } + + MBEDTLS_SSL_PRINT_EXTS(3, MBEDTLS_SSL_HS_CLIENT_HELLO, + handshake->received_extensions); + + ret = mbedtls_ssl_add_hs_hdr_to_checksum(ssl, + MBEDTLS_SSL_HS_CLIENT_HELLO, + p - buf); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("mbedtls_ssl_add_hs_hdr_to_checksum"), ret); + return ret; + } + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + /* Update checksum with either + * - The entire content of the CH message, if no PSK extension is present + * - The content up to but excluding the PSK extension, if present. + */ + /* If we've settled on a PSK-based exchange, parse PSK identity ext */ + if (mbedtls_ssl_tls13_some_psk_enabled(ssl) && + mbedtls_ssl_conf_tls13_some_psk_enabled(ssl) && + (handshake->received_extensions & MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY))) { + ret = handshake->update_checksum(ssl, buf, + pre_shared_key_ext - buf); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret); + return ret; + } + ret = ssl_tls13_parse_pre_shared_key_ext(ssl, + pre_shared_key_ext, + pre_shared_key_ext_end, + cipher_suites, + cipher_suites_end); + if (ret == MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY) { + handshake->received_extensions &= ~MBEDTLS_SSL_EXT_MASK(PRE_SHARED_KEY); + } else if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "ssl_tls13_parse_pre_shared_key_ext", ret); + return ret; + } + } else +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED */ + { + ret = handshake->update_checksum(ssl, buf, p - buf); + if (0 != ret) { + MBEDTLS_SSL_DEBUG_RET(1, ("update_checksum"), ret); + return ret; + } + } + + ret = ssl_tls13_determine_key_exchange_mode(ssl); + if (ret < 0) { + return ret; + } + + mbedtls_ssl_optimize_checksum(ssl, handshake->ciphersuite_info); + + return hrr_required ? SSL_CLIENT_HELLO_HRR_REQUIRED : SSL_CLIENT_HELLO_OK; +} + +/* Update the handshake state machine */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_postprocess_client_hello(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * Server certificate selection + */ + if (ssl->conf->f_cert_cb && (ret = ssl->conf->f_cert_cb(ssl)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "f_cert_cb", ret); + return ret; + } +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + ssl->handshake->sni_name = NULL; + ssl->handshake->sni_name_len = 0; +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ + + ret = mbedtls_ssl_tls13_key_schedule_stage_early(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_tls1_3_key_schedule_stage_early", ret); + return ret; + } + + return 0; + +} + +/* + * Main entry point from the state machine; orchestrates the otherfunctions. + */ + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_client_hello(mbedtls_ssl_context *ssl) +{ + + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf = NULL; + size_t buflen = 0; + int parse_client_hello_ret; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> parse client hello")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_tls13_fetch_handshake_msg( + ssl, MBEDTLS_SSL_HS_CLIENT_HELLO, + &buf, &buflen)); + + MBEDTLS_SSL_PROC_CHK_NEG(ssl_tls13_parse_client_hello(ssl, buf, + buf + buflen)); + parse_client_hello_ret = ret; /* Store return value of parse_client_hello, + * only SSL_CLIENT_HELLO_OK or + * SSL_CLIENT_HELLO_HRR_REQUIRED at this + * stage as negative error codes are handled + * by MBEDTLS_SSL_PROC_CHK_NEG. */ + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_postprocess_client_hello(ssl)); + + if (parse_client_hello_ret == SSL_CLIENT_HELLO_OK) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_HELLO); + } else { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HELLO_RETRY_REQUEST); + } + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= parse client hello")); + return ret; +} + +/* + * Handler for MBEDTLS_SSL_SERVER_HELLO + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_prepare_server_hello(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *server_randbytes = + ssl->handshake->randbytes + MBEDTLS_CLIENT_HELLO_RANDOM_LEN; + if (ssl->conf->f_rng == NULL) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no RNG provided")); + return MBEDTLS_ERR_SSL_NO_RNG; + } + + if ((ret = ssl->conf->f_rng(ssl->conf->p_rng, server_randbytes, + MBEDTLS_SERVER_HELLO_RANDOM_LEN)) != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "f_rng", ret); + return ret; + } + + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, random bytes", server_randbytes, + MBEDTLS_SERVER_HELLO_RANDOM_LEN); + +#if defined(MBEDTLS_HAVE_TIME) + ssl->session_negotiate->start = time(NULL); +#endif /* MBEDTLS_HAVE_TIME */ + + return ret; +} + +/* + * ssl_tls13_write_server_hello_supported_versions_ext (): + * + * struct { + * ProtocolVersion selected_version; + * } SupportedVersions; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_server_hello_supported_versions_ext( + mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + *out_len = 0; + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, write selected version")); + + /* Check if we have space to write the extension: + * - extension_type (2 bytes) + * - extension_data_length (2 bytes) + * - selected_version (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(buf, end, 6); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS, buf, 0); + + MBEDTLS_PUT_UINT16_BE(2, buf, 2); + + mbedtls_ssl_write_version(buf + 4, + ssl->conf->transport, + ssl->tls_version); + + MBEDTLS_SSL_DEBUG_MSG(3, ("supported version: [%04x]", + ssl->tls_version)); + + *out_len = 6; + + mbedtls_ssl_tls13_set_hs_sent_ext_mask( + ssl, MBEDTLS_TLS_EXT_SUPPORTED_VERSIONS); + + return 0; +} + + + +/* Generate and export a single key share. For hybrid KEMs, this can + * be called multiple times with the different components of the hybrid. */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_generate_and_write_key_share(mbedtls_ssl_context *ssl, + uint16_t named_group, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + *out_len = 0; + +#if defined(MBEDTLS_ECDH_C) + if (mbedtls_ssl_tls13_named_group_is_ecdhe(named_group)) { + ret = mbedtls_ssl_tls13_generate_and_write_ecdh_key_exchange( + ssl, named_group, buf, end, out_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "mbedtls_ssl_tls13_generate_and_write_ecdh_key_exchange", + ret); + return ret; + } + } else +#endif /* MBEDTLS_ECDH_C */ + if (0 /* Other kinds of KEMs */) { + } else { + ((void) ssl); + ((void) named_group); + ((void) buf); + ((void) end); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + return ret; +} + +/* + * ssl_tls13_write_key_share_ext + * + * Structure of key_share extension in ServerHello: + * + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + * struct { + * KeyShareEntry server_share; + * } KeyShareServerHello; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_key_share_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + uint16_t group = ssl->handshake->offered_group_id; + unsigned char *server_share = buf + 4; + size_t key_exchange_length; + + *out_len = 0; + + MBEDTLS_SSL_DEBUG_MSG(3, ("server hello, adding key share extension")); + + MBEDTLS_SSL_DEBUG_MSG(2, ("server hello, write selected_group: %s (%04x)", + mbedtls_ssl_named_group_to_str(group), + group)); + + /* Check if we have space for header and length fields: + * - extension_type (2 bytes) + * - extension_data_length (2 bytes) + * - group (2 bytes) + * - key_exchange_length (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 8); + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_KEY_SHARE, p, 0); + MBEDTLS_PUT_UINT16_BE(group, server_share, 0); + p += 8; + + /* When we introduce PQC-ECDHE hybrids, we'll want to call this + * function multiple times. */ + ret = ssl_tls13_generate_and_write_key_share( + ssl, group, server_share + 4, end, &key_exchange_length); + if (ret != 0) { + return ret; + } + p += key_exchange_length; + + MBEDTLS_PUT_UINT16_BE(key_exchange_length, server_share + 2, 0); + + MBEDTLS_PUT_UINT16_BE(p - server_share, buf, 2); + + *out_len = p - buf; + + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_KEY_SHARE); + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_hrr_key_share_ext(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + uint16_t selected_group = ssl->handshake->hrr_selected_group; + /* key_share Extension + * + * struct { + * select (Handshake.msg_type) { + * ... + * case hello_retry_request: + * NamedGroup selected_group; + * ... + * }; + * } KeyShare; + */ + + *out_len = 0; + + /* + * For a pure PSK key exchange, there is no group to agree upon. The purpose + * of the HRR is then to transmit a cookie to force the client to demonstrate + * reachability at their apparent network address (primarily useful for DTLS). + */ + if (!mbedtls_ssl_tls13_key_exchange_mode_with_ephemeral(ssl)) { + return 0; + } + + /* We should only send the key_share extension if the client's initial + * key share was not acceptable. */ + if (ssl->handshake->offered_group_id != 0) { + MBEDTLS_SSL_DEBUG_MSG(4, ("Skip key_share extension in HRR")); + return 0; + } + + if (selected_group == 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("no matching named group found")); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + /* Check if we have enough space: + * - extension_type (2 bytes) + * - extension_data_length (2 bytes) + * - selected_group (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(buf, end, 6); + + MBEDTLS_PUT_UINT16_BE(MBEDTLS_TLS_EXT_KEY_SHARE, buf, 0); + MBEDTLS_PUT_UINT16_BE(2, buf, 2); + MBEDTLS_PUT_UINT16_BE(selected_group, buf, 4); + + MBEDTLS_SSL_DEBUG_MSG(3, + ("HRR selected_group: %s (%x)", + mbedtls_ssl_named_group_to_str(selected_group), + selected_group)); + + *out_len = 6; + + mbedtls_ssl_tls13_set_hs_sent_ext_mask(ssl, MBEDTLS_TLS_EXT_KEY_SHARE); + + return 0; +} + +/* + * Structure of ServerHello message: + * + * struct { + * ProtocolVersion legacy_version = 0x0303; // TLS v1.2 + * Random random; + * opaque legacy_session_id_echo<0..32>; + * CipherSuite cipher_suite; + * uint8 legacy_compression_method = 0; + * Extension extensions<6..2^16-1>; + * } ServerHello; + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_server_hello_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len, + int is_hrr) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + unsigned char *p_extensions_len; + size_t output_len; + + *out_len = 0; + ssl->handshake->sent_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + /* ... + * ProtocolVersion legacy_version = 0x0303; // TLS 1.2 + * ... + * with ProtocolVersion defined as: + * uint16 ProtocolVersion; + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + MBEDTLS_PUT_UINT16_BE(0x0303, p, 0); + p += 2; + + /* ... + * Random random; + * ... + * with Random defined as: + * opaque Random[MBEDTLS_SERVER_HELLO_RANDOM_LEN]; + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, MBEDTLS_SERVER_HELLO_RANDOM_LEN); + if (is_hrr) { + memcpy(p, mbedtls_ssl_tls13_hello_retry_request_magic, + MBEDTLS_SERVER_HELLO_RANDOM_LEN); + } else { + memcpy(p, &ssl->handshake->randbytes[MBEDTLS_CLIENT_HELLO_RANDOM_LEN], + MBEDTLS_SERVER_HELLO_RANDOM_LEN); + } + MBEDTLS_SSL_DEBUG_BUF(3, "server hello, random bytes", + p, MBEDTLS_SERVER_HELLO_RANDOM_LEN); + p += MBEDTLS_SERVER_HELLO_RANDOM_LEN; + + /* ... + * opaque legacy_session_id_echo<0..32>; + * ... + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 1 + ssl->session_negotiate->id_len); + *p++ = (unsigned char) ssl->session_negotiate->id_len; + if (ssl->session_negotiate->id_len > 0) { + memcpy(p, &ssl->session_negotiate->id[0], + ssl->session_negotiate->id_len); + p += ssl->session_negotiate->id_len; + + MBEDTLS_SSL_DEBUG_BUF(3, "session id", ssl->session_negotiate->id, + ssl->session_negotiate->id_len); + } + + /* ... + * CipherSuite cipher_suite; + * ... + * with CipherSuite defined as: + * uint8 CipherSuite[2]; + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + MBEDTLS_PUT_UINT16_BE(ssl->session_negotiate->ciphersuite, p, 0); + p += 2; + MBEDTLS_SSL_DEBUG_MSG(3, + ("server hello, chosen ciphersuite: %s ( id=%d )", + mbedtls_ssl_get_ciphersuite_name( + ssl->session_negotiate->ciphersuite), + ssl->session_negotiate->ciphersuite)); + + /* ... + * uint8 legacy_compression_method = 0; + * ... + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 1); + *p++ = MBEDTLS_SSL_COMPRESS_NULL; + + /* ... + * Extension extensions<6..2^16-1>; + * ... + * struct { + * ExtensionType extension_type; (2 bytes) + * opaque extension_data<0..2^16-1>; + * } Extension; + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + p_extensions_len = p; + p += 2; + + if ((ret = ssl_tls13_write_server_hello_supported_versions_ext( + ssl, p, end, &output_len)) != 0) { + MBEDTLS_SSL_DEBUG_RET( + 1, "ssl_tls13_write_server_hello_supported_versions_ext", ret); + return ret; + } + p += output_len; + + if (mbedtls_ssl_tls13_key_exchange_mode_with_ephemeral(ssl)) { + if (is_hrr) { + ret = ssl_tls13_write_hrr_key_share_ext(ssl, p, end, &output_len); + } else { + ret = ssl_tls13_write_key_share_ext(ssl, p, end, &output_len); + } + if (ret != 0) { + return ret; + } + p += output_len; + } + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + if (!is_hrr && mbedtls_ssl_tls13_key_exchange_mode_with_psk(ssl)) { + ret = ssl_tls13_write_server_pre_shared_key_ext(ssl, p, end, &output_len); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_write_server_pre_shared_key_ext", + ret); + return ret; + } + p += output_len; + } +#endif + + MBEDTLS_PUT_UINT16_BE(p - p_extensions_len - 2, p_extensions_len, 0); + + MBEDTLS_SSL_DEBUG_BUF(4, "server hello extensions", + p_extensions_len, p - p_extensions_len); + + *out_len = p - buf; + + MBEDTLS_SSL_DEBUG_BUF(3, "server hello", buf, *out_len); + + MBEDTLS_SSL_PRINT_EXTS( + 3, is_hrr ? MBEDTLS_SSL_TLS1_3_HS_HELLO_RETRY_REQUEST : + MBEDTLS_SSL_HS_SERVER_HELLO, + ssl->handshake->sent_extensions); + + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_finalize_server_hello(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + ret = mbedtls_ssl_tls13_compute_handshake_transform(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_tls13_compute_handshake_transform", + ret); + return ret; + } + + return ret; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_server_hello(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + size_t buf_len, msg_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write server hello")); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_prepare_server_hello(ssl)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg(ssl, + MBEDTLS_SSL_HS_SERVER_HELLO, &buf, + &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_server_hello_body(ssl, buf, + buf + buf_len, + &msg_len, + 0)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum( + ssl, MBEDTLS_SSL_HS_SERVER_HELLO, buf, msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg( + ssl, buf_len, msg_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_finalize_server_hello(ssl)); + +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + /* The server sends a dummy change_cipher_spec record immediately + * after its first handshake message. This may either be after + * a ServerHello or a HelloRetryRequest. + */ + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO); +#else + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS); +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write server hello")); + return ret; +} + + +/* + * Handler for MBEDTLS_SSL_HELLO_RETRY_REQUEST + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_prepare_hello_retry_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + if (ssl->handshake->hello_retry_request_count > 0) { + MBEDTLS_SSL_DEBUG_MSG(1, ("Too many HRRs")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } + + /* + * Create stateless transcript hash for HRR + */ + MBEDTLS_SSL_DEBUG_MSG(4, ("Reset transcript for HRR")); + ret = mbedtls_ssl_reset_transcript_for_hrr(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "mbedtls_ssl_reset_transcript_for_hrr", ret); + return ret; + } + mbedtls_ssl_session_reset_msg_layer(ssl, 0); + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_hello_retry_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + size_t buf_len, msg_len; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write hello retry request")); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_prepare_hello_retry_request(ssl)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg( + ssl, MBEDTLS_SSL_HS_SERVER_HELLO, + &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_server_hello_body(ssl, buf, + buf + buf_len, + &msg_len, + 1)); + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum( + ssl, MBEDTLS_SSL_HS_SERVER_HELLO, buf, msg_len)); + + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg(ssl, buf_len, + msg_len)); + + ssl->handshake->hello_retry_request_count++; + +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + /* The server sends a dummy change_cipher_spec record immediately + * after its first handshake message. This may either be after + * a ServerHello or a HelloRetryRequest. + */ + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_SERVER_CCS_AFTER_HELLO_RETRY_REQUEST); +#else + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO); +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + +cleanup: + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write hello retry request")); + return ret; +} + +/* + * Handler for MBEDTLS_SSL_ENCRYPTED_EXTENSIONS + */ + +/* + * struct { + * Extension extensions<0..2 ^ 16 - 1>; + * } EncryptedExtensions; + * + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_encrypted_extensions_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + size_t extensions_len = 0; + unsigned char *p_extensions_len; + size_t output_len; + + *out_len = 0; + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + p_extensions_len = p; + p += 2; + + ((void) ssl); + ((void) ret); + ((void) output_len); + +#if defined(MBEDTLS_SSL_ALPN) + ret = mbedtls_ssl_write_alpn_ext(ssl, p, end, &output_len); + if (ret != 0) { + return ret; + } + p += output_len; +#endif /* MBEDTLS_SSL_ALPN */ + + extensions_len = (p - p_extensions_len) - 2; + MBEDTLS_PUT_UINT16_BE(extensions_len, p_extensions_len, 0); + + *out_len = p - buf; + + MBEDTLS_SSL_DEBUG_BUF(4, "encrypted extensions", buf, *out_len); + + MBEDTLS_SSL_PRINT_EXTS( + 3, MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, ssl->handshake->sent_extensions); + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_encrypted_extensions(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *buf; + size_t buf_len, msg_len; + + mbedtls_ssl_set_outbound_transform(ssl, + ssl->handshake->transform_handshake); + MBEDTLS_SSL_DEBUG_MSG( + 3, ("switching to handshake transform for outbound data")); + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write encrypted extensions")); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg(ssl, + MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, &buf, + &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_encrypted_extensions_body( + ssl, buf, buf + buf_len, &msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum( + ssl, MBEDTLS_SSL_HS_ENCRYPTED_EXTENSIONS, buf, msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg( + ssl, buf_len, msg_len)); + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + if (mbedtls_ssl_tls13_key_exchange_mode_with_psk(ssl)) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); + } else { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CERTIFICATE_REQUEST); + } +#else + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); +#endif + +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write encrypted extensions")); + return ret; +} + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) +#define SSL_CERTIFICATE_REQUEST_SEND_REQUEST 0 +#define SSL_CERTIFICATE_REQUEST_SKIP 1 +/* Coordination: + * Check whether a CertificateRequest message should be written. + * Returns a negative code on failure, or + * - SSL_CERTIFICATE_REQUEST_SEND_REQUEST + * - SSL_CERTIFICATE_REQUEST_SKIP + * indicating if the writing of the CertificateRequest + * should be skipped or not. + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_certificate_request_coordinate(mbedtls_ssl_context *ssl) +{ + int authmode; + +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + if (ssl->handshake->sni_authmode != MBEDTLS_SSL_VERIFY_UNSET) { + authmode = ssl->handshake->sni_authmode; + } else +#endif + authmode = ssl->conf->authmode; + + if (authmode == MBEDTLS_SSL_VERIFY_NONE) { + ssl->session_negotiate->verify_result = MBEDTLS_X509_BADCERT_SKIP_VERIFY; + return SSL_CERTIFICATE_REQUEST_SKIP; + } + + ssl->handshake->certificate_request_sent = 1; + + return SSL_CERTIFICATE_REQUEST_SEND_REQUEST; +} + +/* + * struct { + * opaque certificate_request_context<0..2^8-1>; + * Extension extensions<2..2^16-1>; + * } CertificateRequest; + * + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_certificate_request_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + const unsigned char *end, + size_t *out_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + size_t output_len = 0; + unsigned char *p_extensions_len; + + *out_len = 0; + + /* Check if we have enough space: + * - certificate_request_context (1 byte) + * - extensions length (2 bytes) + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 3); + + /* + * Write certificate_request_context + */ + /* + * We use a zero length context for the normal handshake + * messages. For post-authentication handshake messages + * this request context would be set to a non-zero value. + */ + *p++ = 0x0; + + /* + * Write extensions + */ + /* The extensions must contain the signature_algorithms. */ + p_extensions_len = p; + p += 2; + ret = mbedtls_ssl_write_sig_alg_ext(ssl, p, end, &output_len); + if (ret != 0) { + return ret; + } + + p += output_len; + MBEDTLS_PUT_UINT16_BE(p - p_extensions_len - 2, p_extensions_len, 0); + + *out_len = p - buf; + + MBEDTLS_SSL_PRINT_EXTS( + 3, MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, ssl->handshake->sent_extensions); + + return 0; +} + +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_certificate_request(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write certificate request")); + + MBEDTLS_SSL_PROC_CHK_NEG(ssl_tls13_certificate_request_coordinate(ssl)); + + if (ret == SSL_CERTIFICATE_REQUEST_SEND_REQUEST) { + unsigned char *buf; + size_t buf_len, msg_len; + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg(ssl, + MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, + &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_certificate_request_body( + ssl, buf, buf + buf_len, &msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_add_hs_msg_to_checksum( + ssl, MBEDTLS_SSL_HS_CERTIFICATE_REQUEST, buf, msg_len)); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg( + ssl, buf_len, msg_len)); + } else if (ret == SSL_CERTIFICATE_REQUEST_SKIP) { + MBEDTLS_SSL_DEBUG_MSG(2, ("<= skip write certificate request")); + ret = 0; + } else { + MBEDTLS_SSL_DEBUG_MSG(1, ("should never happen")); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + goto cleanup; + } + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_CERTIFICATE); +cleanup: + + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write certificate request")); + return ret; +} + +/* + * Handler for MBEDTLS_SSL_SERVER_CERTIFICATE + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_server_certificate(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + if ((ssl_tls13_pick_key_cert(ssl) != 0) || + mbedtls_ssl_own_cert(ssl) == NULL) { + MBEDTLS_SSL_DEBUG_MSG(2, ("No certificate available.")); + MBEDTLS_SSL_PEND_FATAL_ALERT(MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE; + } +#endif /* MBEDTLS_X509_CRT_PARSE_C */ + + ret = mbedtls_ssl_tls13_write_certificate(ssl); + if (ret != 0) { + return ret; + } + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CERTIFICATE_VERIFY); + return 0; +} + +/* + * Handler for MBEDTLS_SSL_CERTIFICATE_VERIFY + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_certificate_verify(mbedtls_ssl_context *ssl) +{ + int ret = mbedtls_ssl_tls13_write_certificate_verify(ssl); + if (ret != 0) { + return ret; + } + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_SERVER_FINISHED); + return 0; +} +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +/* + * Handler for MBEDTLS_SSL_SERVER_FINISHED + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_server_finished(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_ssl_tls13_write_finished_message(ssl); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_compute_application_transform(ssl); + if (ret != 0) { + MBEDTLS_SSL_PEND_FATAL_ALERT( + MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE, + MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE); + return ret; + } + + MBEDTLS_SSL_DEBUG_MSG(1, ("Switch to handshake keys for inbound traffic")); + mbedtls_ssl_set_inbound_transform(ssl, ssl->handshake->transform_handshake); + + if (ssl->handshake->certificate_request_sent) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE); + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("skip parse certificate")); + MBEDTLS_SSL_DEBUG_MSG(2, ("skip parse certificate verify")); + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_FINISHED); + } + + return 0; +} + +/* + * Handler for MBEDTLS_SSL_CLIENT_FINISHED + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_process_client_finished(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_ssl_tls13_process_finished_message(ssl); + if (ret != 0) { + return ret; + } + + ret = mbedtls_ssl_tls13_compute_resumption_master_secret(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "mbedtls_ssl_tls13_compute_resumption_master_secret", ret); + } + + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_WRAPUP); + return 0; +} + +/* + * Handler for MBEDTLS_SSL_HANDSHAKE_WRAPUP + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_handshake_wrapup(mbedtls_ssl_context *ssl) +{ + MBEDTLS_SSL_DEBUG_MSG(2, ("handshake: done")); + + mbedtls_ssl_tls13_handshake_wrapup(ssl); + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) +/* TODO: Remove the check of SOME_PSK_ENABLED since SESSION_TICKETS requires + * SOME_PSK_ENABLED to be enabled. Here is just to make CI happy. It is + * expected to be resolved with issue#6395. + */ + /* Sent NewSessionTicket message only when client supports PSK */ + if (mbedtls_ssl_tls13_some_psk_enabled(ssl)) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET); + } else +#endif + { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_OVER); + } + return 0; +} + +/* + * Handler for MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET + */ +#define SSL_NEW_SESSION_TICKET_SKIP 0 +#define SSL_NEW_SESSION_TICKET_WRITE 1 +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_new_session_ticket_coordinate(mbedtls_ssl_context *ssl) +{ + /* Check whether the use of session tickets is enabled */ + if (ssl->conf->f_ticket_write == NULL) { + MBEDTLS_SSL_DEBUG_MSG(2, ("NewSessionTicket: disabled," + " callback is not set")); + return SSL_NEW_SESSION_TICKET_SKIP; + } + if (ssl->conf->new_session_tickets_count == 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("NewSessionTicket: disabled," + " configured count is zero")); + return SSL_NEW_SESSION_TICKET_SKIP; + } + + if (ssl->handshake->new_session_tickets_count == 0) { + MBEDTLS_SSL_DEBUG_MSG(2, ("NewSessionTicket: all tickets have " + "been sent.")); + return SSL_NEW_SESSION_TICKET_SKIP; + } + + return SSL_NEW_SESSION_TICKET_WRITE; +} + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_prepare_new_session_ticket(mbedtls_ssl_context *ssl, + unsigned char *ticket_nonce, + size_t ticket_nonce_size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_ssl_session *session = ssl->session; + mbedtls_ssl_ciphersuite_t *ciphersuite_info; + psa_algorithm_t psa_hash_alg; + int hash_length; + + MBEDTLS_SSL_DEBUG_MSG(2, ("=> prepare NewSessionTicket msg")); + +#if defined(MBEDTLS_HAVE_TIME) + session->start = mbedtls_time(NULL); +#endif + + /* Set ticket_flags depends on the advertised psk key exchange mode */ + mbedtls_ssl_session_clear_ticket_flags( + session, MBEDTLS_SSL_TLS1_3_TICKET_FLAGS_MASK); +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_SOME_PSK_ENABLED) + mbedtls_ssl_session_set_ticket_flags( + session, ssl->handshake->tls13_kex_modes); +#endif + MBEDTLS_SSL_PRINT_TICKET_FLAGS(4, session->ticket_flags); + + /* Generate ticket_age_add */ + if ((ret = ssl->conf->f_rng(ssl->conf->p_rng, + (unsigned char *) &session->ticket_age_add, + sizeof(session->ticket_age_add)) != 0)) { + MBEDTLS_SSL_DEBUG_RET(1, "generate_ticket_age_add", ret); + return ret; + } + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket_age_add: %u", + (unsigned int) session->ticket_age_add)); + + /* Generate ticket_nonce */ + ret = ssl->conf->f_rng(ssl->conf->p_rng, ticket_nonce, ticket_nonce_size); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "generate_ticket_nonce", ret); + return ret; + } + MBEDTLS_SSL_DEBUG_BUF(3, "ticket_nonce:", + ticket_nonce, ticket_nonce_size); + + ciphersuite_info = + (mbedtls_ssl_ciphersuite_t *) ssl->handshake->ciphersuite_info; + psa_hash_alg = mbedtls_psa_translate_md(ciphersuite_info->mac); + hash_length = PSA_HASH_LENGTH(psa_hash_alg); + if (hash_length == -1 || + (size_t) hash_length > sizeof(session->resumption_key)) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* In this code the psk key length equals the length of the hash */ + session->resumption_key_len = hash_length; + session->ciphersuite = ciphersuite_info->id; + + /* Compute resumption key + * + * HKDF-Expand-Label( resumption_master_secret, + * "resumption", ticket_nonce, Hash.length ) + */ + ret = mbedtls_ssl_tls13_hkdf_expand_label( + psa_hash_alg, + session->app_secrets.resumption_master_secret, + hash_length, + MBEDTLS_SSL_TLS1_3_LBL_WITH_LEN(resumption), + ticket_nonce, + ticket_nonce_size, + session->resumption_key, + hash_length); + + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(2, + "Creating the ticket-resumed PSK failed", + ret); + return ret; + } + MBEDTLS_SSL_DEBUG_BUF(3, "Ticket-resumed PSK", + session->resumption_key, + session->resumption_key_len); + + MBEDTLS_SSL_DEBUG_BUF(3, "resumption_master_secret", + session->app_secrets.resumption_master_secret, + hash_length); + + return 0; +} + +/* This function creates a NewSessionTicket message in the following format: + * + * struct { + * uint32 ticket_lifetime; + * uint32 ticket_age_add; + * opaque ticket_nonce<0..255>; + * opaque ticket<1..2^16-1>; + * Extension extensions<0..2^16-2>; + * } NewSessionTicket; + * + * The ticket inside the NewSessionTicket message is an encrypted container + * carrying the necessary information so that the server is later able to + * re-start the communication. + * + * The following fields are placed inside the ticket by the + * f_ticket_write() function: + * + * - creation time (start) + * - flags (flags) + * - age add (ticket_age_add) + * - key (key) + * - key length (key_len) + * - ciphersuite (ciphersuite) + */ +MBEDTLS_CHECK_RETURN_CRITICAL +static int ssl_tls13_write_new_session_ticket_body(mbedtls_ssl_context *ssl, + unsigned char *buf, + unsigned char *end, + size_t *out_len, + unsigned char *ticket_nonce, + size_t ticket_nonce_size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p = buf; + mbedtls_ssl_session *session = ssl->session; + size_t ticket_len; + uint32_t ticket_lifetime; + + *out_len = 0; + MBEDTLS_SSL_DEBUG_MSG(2, ("=> write NewSessionTicket msg")); + + /* + * ticket_lifetime 4 bytes + * ticket_age_add 4 bytes + * ticket_nonce 1 + ticket_nonce_size bytes + * ticket >=2 bytes + */ + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 4 + 4 + 1 + ticket_nonce_size + 2); + + /* Generate ticket and ticket_lifetime */ + ret = ssl->conf->f_ticket_write(ssl->conf->p_ticket, + session, + p + 9 + ticket_nonce_size + 2, + end, + &ticket_len, + &ticket_lifetime); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "write_ticket", ret); + return ret; + } + /* RFC 8446 4.6.1 + * ticket_lifetime: Indicates the lifetime in seconds as a 32-bit + * unsigned integer in network byte order from the time of ticket + * issuance. Servers MUST NOT use any value greater than + * 604800 seconds (7 days). The value of zero indicates that the + * ticket should be discarded immediately. Clients MUST NOT cache + * tickets for longer than 7 days, regardless of the ticket_lifetime, + * and MAY delete tickets earlier based on local policy. A server + * MAY treat a ticket as valid for a shorter period of time than what + * is stated in the ticket_lifetime. + */ + if (ticket_lifetime > 604800) { + ticket_lifetime = 604800; + } + MBEDTLS_PUT_UINT32_BE(ticket_lifetime, p, 0); + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket_lifetime: %u", + (unsigned int) ticket_lifetime)); + + /* Write ticket_age_add */ + MBEDTLS_PUT_UINT32_BE(session->ticket_age_add, p, 4); + MBEDTLS_SSL_DEBUG_MSG(3, ("ticket_age_add: %u", + (unsigned int) session->ticket_age_add)); + + /* Write ticket_nonce */ + p[8] = (unsigned char) ticket_nonce_size; + if (ticket_nonce_size > 0) { + memcpy(p + 9, ticket_nonce, ticket_nonce_size); + } + p += 9 + ticket_nonce_size; + + /* Write ticket */ + MBEDTLS_PUT_UINT16_BE(ticket_len, p, 0); + p += 2; + MBEDTLS_SSL_DEBUG_BUF(4, "ticket", p, ticket_len); + p += ticket_len; + + /* Ticket Extensions + * + * Note: We currently don't have any extensions. + * Set length to zero. + */ + ssl->handshake->sent_extensions = MBEDTLS_SSL_EXT_MASK_NONE; + + MBEDTLS_SSL_CHK_BUF_PTR(p, end, 2); + MBEDTLS_PUT_UINT16_BE(0, p, 0); + p += 2; + + *out_len = p - buf; + MBEDTLS_SSL_DEBUG_BUF(4, "ticket", buf, *out_len); + MBEDTLS_SSL_DEBUG_MSG(2, ("<= write new session ticket")); + + MBEDTLS_SSL_PRINT_EXTS( + 3, MBEDTLS_SSL_HS_NEW_SESSION_TICKET, ssl->handshake->sent_extensions); + + return 0; +} + +/* + * Handler for MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET + */ +static int ssl_tls13_write_new_session_ticket(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + MBEDTLS_SSL_PROC_CHK_NEG(ssl_tls13_write_new_session_ticket_coordinate(ssl)); + + if (ret == SSL_NEW_SESSION_TICKET_WRITE) { + unsigned char ticket_nonce[MBEDTLS_SSL_TLS1_3_TICKET_NONCE_LENGTH]; + unsigned char *buf; + size_t buf_len, msg_len; + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_prepare_new_session_ticket( + ssl, ticket_nonce, sizeof(ticket_nonce))); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_start_handshake_msg(ssl, + MBEDTLS_SSL_HS_NEW_SESSION_TICKET, + &buf, &buf_len)); + + MBEDTLS_SSL_PROC_CHK(ssl_tls13_write_new_session_ticket_body( + ssl, buf, buf + buf_len, &msg_len, + ticket_nonce, sizeof(ticket_nonce))); + + MBEDTLS_SSL_PROC_CHK(mbedtls_ssl_finish_handshake_msg( + ssl, buf_len, msg_len)); + + /* Limit session tickets count to one when resumption connection. + * + * See document of mbedtls_ssl_conf_new_session_tickets. + */ + if (ssl->handshake->resume == 1) { + ssl->handshake->new_session_tickets_count = 0; + } else { + ssl->handshake->new_session_tickets_count--; + } + + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET_FLUSH); + } else { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_OVER); + } + +cleanup: + + return ret; +} +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + +/* + * TLS 1.3 State Machine -- server side + */ +int mbedtls_ssl_tls13_handshake_server_step(mbedtls_ssl_context *ssl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER || ssl->handshake == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + MBEDTLS_SSL_DEBUG_MSG(2, ("tls13 server state: %s(%d)", + mbedtls_ssl_states_str(ssl->state), + ssl->state)); + + switch (ssl->state) { + /* start state */ + case MBEDTLS_SSL_HELLO_REQUEST: + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO); + ret = 0; + break; + + case MBEDTLS_SSL_CLIENT_HELLO: + ret = ssl_tls13_process_client_hello(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_process_client_hello", ret); + } + break; + + case MBEDTLS_SSL_HELLO_RETRY_REQUEST: + ret = ssl_tls13_write_hello_retry_request(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_write_hello_retry_request", ret); + return ret; + } + break; + + case MBEDTLS_SSL_SERVER_HELLO: + ret = ssl_tls13_write_server_hello(ssl); + break; + + case MBEDTLS_SSL_ENCRYPTED_EXTENSIONS: + ret = ssl_tls13_write_encrypted_extensions(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, "ssl_tls13_write_encrypted_extensions", ret); + return ret; + } + break; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + case MBEDTLS_SSL_CERTIFICATE_REQUEST: + ret = ssl_tls13_write_certificate_request(ssl); + break; + + case MBEDTLS_SSL_SERVER_CERTIFICATE: + ret = ssl_tls13_write_server_certificate(ssl); + break; + + case MBEDTLS_SSL_CERTIFICATE_VERIFY: + ret = ssl_tls13_write_certificate_verify(ssl); + break; +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + + /* + * Injection of dummy-CCS's for middlebox compatibility + */ +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + case MBEDTLS_SSL_SERVER_CCS_AFTER_HELLO_RETRY_REQUEST: + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret == 0) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_CLIENT_HELLO); + } + break; + + case MBEDTLS_SSL_SERVER_CCS_AFTER_SERVER_HELLO: + ret = mbedtls_ssl_tls13_write_change_cipher_spec(ssl); + if (ret == 0) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_ENCRYPTED_EXTENSIONS); + } + break; +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ + + case MBEDTLS_SSL_SERVER_FINISHED: + ret = ssl_tls13_write_server_finished(ssl); + break; + + case MBEDTLS_SSL_CLIENT_FINISHED: + ret = ssl_tls13_process_client_finished(ssl); + break; + + case MBEDTLS_SSL_HANDSHAKE_WRAPUP: + ret = ssl_tls13_handshake_wrapup(ssl); + break; + +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + ret = mbedtls_ssl_tls13_process_certificate(ssl); + if (ret == 0) { + if (ssl->session_negotiate->peer_cert != NULL) { + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY); + } else { + MBEDTLS_SSL_DEBUG_MSG(2, ("skip parse certificate verify")); + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_CLIENT_FINISHED); + } + } + break; + + case MBEDTLS_SSL_CLIENT_CERTIFICATE_VERIFY: + ret = mbedtls_ssl_tls13_process_certificate_verify(ssl); + if (ret == 0) { + mbedtls_ssl_handshake_set_state( + ssl, MBEDTLS_SSL_CLIENT_FINISHED); + } + break; +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + case MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET: + ret = ssl_tls13_write_new_session_ticket(ssl); + if (ret != 0) { + MBEDTLS_SSL_DEBUG_RET(1, + "ssl_tls13_write_new_session_ticket ", + ret); + } + break; + case MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET_FLUSH: + /* This state is necessary to do the flush of the New Session + * Ticket message written in MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET + * as part of ssl_prepare_handshake_step. + */ + ret = 0; + + if (ssl->handshake->new_session_tickets_count == 0) { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_HANDSHAKE_OVER); + } else { + mbedtls_ssl_handshake_set_state(ssl, MBEDTLS_SSL_TLS1_3_NEW_SESSION_TICKET); + } + break; + +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ + + default: + MBEDTLS_SSL_DEBUG_MSG(1, ("invalid state %d", ssl->state)); + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + return ret; +} + +#endif /* MBEDTLS_SSL_SRV_C && MBEDTLS_SSL_PROTO_TLS1_3 */ diff --git a/r5dev/thirdparty/mbedtls/threading.c b/r5dev/thirdparty/mbedtls/threading.c new file mode 100644 index 00000000..130c6963 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/threading.c @@ -0,0 +1,193 @@ +/* + * Threading abstraction layer + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Ensure gmtime_r is available even with -std=c99; must be defined before + * mbedtls_config.h, which pulls in glibc's features.h. Harmless on other platforms. + */ +#if !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200112L +#endif + +#include "common.h" + +#if defined(MBEDTLS_THREADING_C) + +#include "mbedtls/threading.h" + +#if defined(MBEDTLS_HAVE_TIME_DATE) && !defined(MBEDTLS_PLATFORM_GMTIME_R_ALT) + +#if !defined(_WIN32) && (defined(unix) || \ + defined(__unix) || defined(__unix__) || (defined(__APPLE__) && \ + defined(__MACH__))) +#include +#endif /* !_WIN32 && (unix || __unix || __unix__ || + * (__APPLE__ && __MACH__)) */ + +#if !((defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L) || \ + (defined(_POSIX_THREAD_SAFE_FUNCTIONS) && \ + _POSIX_THREAD_SAFE_FUNCTIONS >= 200112L)) +/* + * This is a convenience shorthand macro to avoid checking the long + * preprocessor conditions above. Ideally, we could expose this macro in + * platform_util.h and simply use it in platform_util.c, threading.c and + * threading.h. However, this macro is not part of the Mbed TLS public API, so + * we keep it private by only defining it in this file + */ + +#if !(defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)) +#define THREADING_USE_GMTIME +#endif /* ! ( defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) ) */ + +#endif /* !( ( defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L ) || \ + ( defined(_POSIX_THREAD_SAFE_FUNCTIONS ) && \ + _POSIX_THREAD_SAFE_FUNCTIONS >= 200112L ) ) */ + +#endif /* MBEDTLS_HAVE_TIME_DATE && !MBEDTLS_PLATFORM_GMTIME_R_ALT */ + +#if defined(MBEDTLS_THREADING_PTHREAD) +static void threading_mutex_init_pthread(mbedtls_threading_mutex_t *mutex) +{ + if (mutex == NULL) { + return; + } + + /* A nonzero value of is_valid indicates a successfully initialized + * mutex. This is a workaround for not being able to return an error + * code for this function. The lock/unlock functions return an error + * if is_valid is nonzero. The Mbed TLS unit test code uses this field + * to distinguish more states of the mutex; see + * tests/src/threading_helpers for details. */ + mutex->is_valid = pthread_mutex_init(&mutex->mutex, NULL) == 0; +} + +static void threading_mutex_free_pthread(mbedtls_threading_mutex_t *mutex) +{ + if (mutex == NULL || !mutex->is_valid) { + return; + } + + (void) pthread_mutex_destroy(&mutex->mutex); + mutex->is_valid = 0; +} + +static int threading_mutex_lock_pthread(mbedtls_threading_mutex_t *mutex) +{ + if (mutex == NULL || !mutex->is_valid) { + return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA; + } + + if (pthread_mutex_lock(&mutex->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } + + return 0; +} + +static int threading_mutex_unlock_pthread(mbedtls_threading_mutex_t *mutex) +{ + if (mutex == NULL || !mutex->is_valid) { + return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA; + } + + if (pthread_mutex_unlock(&mutex->mutex) != 0) { + return MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } + + return 0; +} + +void (*mbedtls_mutex_init)(mbedtls_threading_mutex_t *) = threading_mutex_init_pthread; +void (*mbedtls_mutex_free)(mbedtls_threading_mutex_t *) = threading_mutex_free_pthread; +int (*mbedtls_mutex_lock)(mbedtls_threading_mutex_t *) = threading_mutex_lock_pthread; +int (*mbedtls_mutex_unlock)(mbedtls_threading_mutex_t *) = threading_mutex_unlock_pthread; + +/* + * With pthreads we can statically initialize mutexes + */ +#define MUTEX_INIT = { PTHREAD_MUTEX_INITIALIZER, 1 } + +#endif /* MBEDTLS_THREADING_PTHREAD */ + +#if defined(MBEDTLS_THREADING_ALT) +static int threading_mutex_fail(mbedtls_threading_mutex_t *mutex) +{ + ((void) mutex); + return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA; +} +static void threading_mutex_dummy(mbedtls_threading_mutex_t *mutex) +{ + ((void) mutex); + return; +} + +void (*mbedtls_mutex_init)(mbedtls_threading_mutex_t *) = threading_mutex_dummy; +void (*mbedtls_mutex_free)(mbedtls_threading_mutex_t *) = threading_mutex_dummy; +int (*mbedtls_mutex_lock)(mbedtls_threading_mutex_t *) = threading_mutex_fail; +int (*mbedtls_mutex_unlock)(mbedtls_threading_mutex_t *) = threading_mutex_fail; + +/* + * Set functions pointers and initialize global mutexes + */ +void mbedtls_threading_set_alt(void (*mutex_init)(mbedtls_threading_mutex_t *), + void (*mutex_free)(mbedtls_threading_mutex_t *), + int (*mutex_lock)(mbedtls_threading_mutex_t *), + int (*mutex_unlock)(mbedtls_threading_mutex_t *)) +{ + mbedtls_mutex_init = mutex_init; + mbedtls_mutex_free = mutex_free; + mbedtls_mutex_lock = mutex_lock; + mbedtls_mutex_unlock = mutex_unlock; + +#if defined(MBEDTLS_FS_IO) + mbedtls_mutex_init(&mbedtls_threading_readdir_mutex); +#endif +#if defined(THREADING_USE_GMTIME) + mbedtls_mutex_init(&mbedtls_threading_gmtime_mutex); +#endif +} + +/* + * Free global mutexes + */ +void mbedtls_threading_free_alt(void) +{ +#if defined(MBEDTLS_FS_IO) + mbedtls_mutex_free(&mbedtls_threading_readdir_mutex); +#endif +#if defined(THREADING_USE_GMTIME) + mbedtls_mutex_free(&mbedtls_threading_gmtime_mutex); +#endif +} +#endif /* MBEDTLS_THREADING_ALT */ + +/* + * Define global mutexes + */ +#ifndef MUTEX_INIT +#define MUTEX_INIT +#endif +#if defined(MBEDTLS_FS_IO) +mbedtls_threading_mutex_t mbedtls_threading_readdir_mutex MUTEX_INIT; +#endif +#if defined(THREADING_USE_GMTIME) +mbedtls_threading_mutex_t mbedtls_threading_gmtime_mutex MUTEX_INIT; +#endif + +#endif /* MBEDTLS_THREADING_C */ diff --git a/r5dev/thirdparty/mbedtls/timing.c b/r5dev/thirdparty/mbedtls/timing.c new file mode 100644 index 00000000..6852033e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/timing.c @@ -0,0 +1,166 @@ +/* + * Portable interface to the CPU cycle counter + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_TIMING_C) + +#include "mbedtls/timing.h" + +#if !defined(MBEDTLS_TIMING_ALT) + +#if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ + !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ + !defined(__HAIKU__) && !defined(__midipix__) +#error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in mbedtls_config.h" +#endif + +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +#include +#include + +struct _hr_time { + LARGE_INTEGER start; +}; + +#else + +#include +#include +#include +/* time.h should be included independently of MBEDTLS_HAVE_TIME. If the + * platform matches the ifdefs above, it will be used. */ +#include +#include +struct _hr_time { + struct timeval start; +}; +#endif /* _WIN32 && !EFIX64 && !EFI32 */ + +/** + * \brief Return the elapsed time in milliseconds + * + * \warning May change without notice + * + * \param val points to a timer structure + * \param reset If 0, query the elapsed time. Otherwise (re)start the timer. + * + * \return Elapsed time since the previous reset in ms. When + * restarting, this is always 0. + * + * \note To initialize a timer, call this function with reset=1. + * + * Determining the elapsed time and resetting the timer is not + * atomic on all platforms, so after the sequence + * `{ get_timer(1); ...; time1 = get_timer(1); ...; time2 = + * get_timer(0) }` the value time1+time2 is only approximately + * the delay since the first reset. + */ +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + +unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset) +{ + struct _hr_time *t = (struct _hr_time *) val; + + if (reset) { + QueryPerformanceCounter(&t->start); + return 0; + } else { + unsigned long delta; + LARGE_INTEGER now, hfreq; + QueryPerformanceCounter(&now); + QueryPerformanceFrequency(&hfreq); + delta = (unsigned long) ((now.QuadPart - t->start.QuadPart) * 1000ul + / hfreq.QuadPart); + return delta; + } +} + +#else /* _WIN32 && !EFIX64 && !EFI32 */ + +unsigned long mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset) +{ + struct _hr_time *t = (struct _hr_time *) val; + + if (reset) { + gettimeofday(&t->start, NULL); + return 0; + } else { + unsigned long delta; + struct timeval now; + gettimeofday(&now, NULL); + delta = (now.tv_sec - t->start.tv_sec) * 1000ul + + (now.tv_usec - t->start.tv_usec) / 1000; + return delta; + } +} + +#endif /* _WIN32 && !EFIX64 && !EFI32 */ + +/* + * Set delays to watch + */ +void mbedtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; + + ctx->int_ms = int_ms; + ctx->fin_ms = fin_ms; + + if (fin_ms != 0) { + (void) mbedtls_timing_get_timer(&ctx->timer, 1); + } +} + +/* + * Get number of delays expired + */ +int mbedtls_timing_get_delay(void *data) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; + unsigned long elapsed_ms; + + if (ctx->fin_ms == 0) { + return -1; + } + + elapsed_ms = mbedtls_timing_get_timer(&ctx->timer, 0); + + if (elapsed_ms >= ctx->fin_ms) { + return 2; + } + + if (elapsed_ms >= ctx->int_ms) { + return 1; + } + + return 0; +} + +/* + * Get the final delay. + */ +uint32_t mbedtls_timing_get_final_delay( + const mbedtls_timing_delay_context *data) +{ + return data->fin_ms; +} +#endif /* !MBEDTLS_TIMING_ALT */ +#endif /* MBEDTLS_TIMING_C */ diff --git a/r5dev/thirdparty/mbedtls/version.c b/r5dev/thirdparty/mbedtls/version.c new file mode 100644 index 00000000..4f78c9cb --- /dev/null +++ b/r5dev/thirdparty/mbedtls/version.c @@ -0,0 +1,44 @@ +/* + * Version information + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_VERSION_C) + +#include "mbedtls/version.h" +#include + +unsigned int mbedtls_version_get_number(void) +{ + return MBEDTLS_VERSION_NUMBER; +} + +void mbedtls_version_get_string(char *string) +{ + memcpy(string, MBEDTLS_VERSION_STRING, + sizeof(MBEDTLS_VERSION_STRING)); +} + +void mbedtls_version_get_string_full(char *string) +{ + memcpy(string, MBEDTLS_VERSION_STRING_FULL, + sizeof(MBEDTLS_VERSION_STRING_FULL)); +} + +#endif /* MBEDTLS_VERSION_C */ diff --git a/r5dev/thirdparty/mbedtls/version_features.c b/r5dev/thirdparty/mbedtls/version_features.c new file mode 100644 index 00000000..0a6ff220 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/version_features.c @@ -0,0 +1,808 @@ +/* + * Version feature information + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_VERSION_C) + +#include "mbedtls/version.h" + +#include + +static const char * const features[] = { +#if defined(MBEDTLS_VERSION_FEATURES) + #if defined(MBEDTLS_HAVE_ASM) + "MBEDTLS_HAVE_ASM", +#endif /* MBEDTLS_HAVE_ASM */ +#if defined(MBEDTLS_NO_UDBL_DIVISION) + "MBEDTLS_NO_UDBL_DIVISION", +#endif /* MBEDTLS_NO_UDBL_DIVISION */ +#if defined(MBEDTLS_NO_64BIT_MULTIPLICATION) + "MBEDTLS_NO_64BIT_MULTIPLICATION", +#endif /* MBEDTLS_NO_64BIT_MULTIPLICATION */ +#if defined(MBEDTLS_HAVE_SSE2) + "MBEDTLS_HAVE_SSE2", +#endif /* MBEDTLS_HAVE_SSE2 */ +#if defined(MBEDTLS_HAVE_TIME) + "MBEDTLS_HAVE_TIME", +#endif /* MBEDTLS_HAVE_TIME */ +#if defined(MBEDTLS_HAVE_TIME_DATE) + "MBEDTLS_HAVE_TIME_DATE", +#endif /* MBEDTLS_HAVE_TIME_DATE */ +#if defined(MBEDTLS_PLATFORM_MEMORY) + "MBEDTLS_PLATFORM_MEMORY", +#endif /* MBEDTLS_PLATFORM_MEMORY */ +#if defined(MBEDTLS_PLATFORM_NO_STD_FUNCTIONS) + "MBEDTLS_PLATFORM_NO_STD_FUNCTIONS", +#endif /* MBEDTLS_PLATFORM_NO_STD_FUNCTIONS */ +#if defined(MBEDTLS_PLATFORM_SETBUF_ALT) + "MBEDTLS_PLATFORM_SETBUF_ALT", +#endif /* MBEDTLS_PLATFORM_SETBUF_ALT */ +#if defined(MBEDTLS_PLATFORM_EXIT_ALT) + "MBEDTLS_PLATFORM_EXIT_ALT", +#endif /* MBEDTLS_PLATFORM_EXIT_ALT */ +#if defined(MBEDTLS_PLATFORM_TIME_ALT) + "MBEDTLS_PLATFORM_TIME_ALT", +#endif /* MBEDTLS_PLATFORM_TIME_ALT */ +#if defined(MBEDTLS_PLATFORM_FPRINTF_ALT) + "MBEDTLS_PLATFORM_FPRINTF_ALT", +#endif /* MBEDTLS_PLATFORM_FPRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_PRINTF_ALT) + "MBEDTLS_PLATFORM_PRINTF_ALT", +#endif /* MBEDTLS_PLATFORM_PRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_SNPRINTF_ALT) + "MBEDTLS_PLATFORM_SNPRINTF_ALT", +#endif /* MBEDTLS_PLATFORM_SNPRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_VSNPRINTF_ALT) + "MBEDTLS_PLATFORM_VSNPRINTF_ALT", +#endif /* MBEDTLS_PLATFORM_VSNPRINTF_ALT */ +#if defined(MBEDTLS_PLATFORM_NV_SEED_ALT) + "MBEDTLS_PLATFORM_NV_SEED_ALT", +#endif /* MBEDTLS_PLATFORM_NV_SEED_ALT */ +#if defined(MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT) + "MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT", +#endif /* MBEDTLS_PLATFORM_SETUP_TEARDOWN_ALT */ +#if defined(MBEDTLS_DEPRECATED_WARNING) + "MBEDTLS_DEPRECATED_WARNING", +#endif /* MBEDTLS_DEPRECATED_WARNING */ +#if defined(MBEDTLS_DEPRECATED_REMOVED) + "MBEDTLS_DEPRECATED_REMOVED", +#endif /* MBEDTLS_DEPRECATED_REMOVED */ +#if defined(MBEDTLS_TIMING_ALT) + "MBEDTLS_TIMING_ALT", +#endif /* MBEDTLS_TIMING_ALT */ +#if defined(MBEDTLS_AES_ALT) + "MBEDTLS_AES_ALT", +#endif /* MBEDTLS_AES_ALT */ +#if defined(MBEDTLS_ARIA_ALT) + "MBEDTLS_ARIA_ALT", +#endif /* MBEDTLS_ARIA_ALT */ +#if defined(MBEDTLS_CAMELLIA_ALT) + "MBEDTLS_CAMELLIA_ALT", +#endif /* MBEDTLS_CAMELLIA_ALT */ +#if defined(MBEDTLS_CCM_ALT) + "MBEDTLS_CCM_ALT", +#endif /* MBEDTLS_CCM_ALT */ +#if defined(MBEDTLS_CHACHA20_ALT) + "MBEDTLS_CHACHA20_ALT", +#endif /* MBEDTLS_CHACHA20_ALT */ +#if defined(MBEDTLS_CHACHAPOLY_ALT) + "MBEDTLS_CHACHAPOLY_ALT", +#endif /* MBEDTLS_CHACHAPOLY_ALT */ +#if defined(MBEDTLS_CMAC_ALT) + "MBEDTLS_CMAC_ALT", +#endif /* MBEDTLS_CMAC_ALT */ +#if defined(MBEDTLS_DES_ALT) + "MBEDTLS_DES_ALT", +#endif /* MBEDTLS_DES_ALT */ +#if defined(MBEDTLS_DHM_ALT) + "MBEDTLS_DHM_ALT", +#endif /* MBEDTLS_DHM_ALT */ +#if defined(MBEDTLS_ECJPAKE_ALT) + "MBEDTLS_ECJPAKE_ALT", +#endif /* MBEDTLS_ECJPAKE_ALT */ +#if defined(MBEDTLS_GCM_ALT) + "MBEDTLS_GCM_ALT", +#endif /* MBEDTLS_GCM_ALT */ +#if defined(MBEDTLS_NIST_KW_ALT) + "MBEDTLS_NIST_KW_ALT", +#endif /* MBEDTLS_NIST_KW_ALT */ +#if defined(MBEDTLS_MD5_ALT) + "MBEDTLS_MD5_ALT", +#endif /* MBEDTLS_MD5_ALT */ +#if defined(MBEDTLS_POLY1305_ALT) + "MBEDTLS_POLY1305_ALT", +#endif /* MBEDTLS_POLY1305_ALT */ +#if defined(MBEDTLS_RIPEMD160_ALT) + "MBEDTLS_RIPEMD160_ALT", +#endif /* MBEDTLS_RIPEMD160_ALT */ +#if defined(MBEDTLS_RSA_ALT) + "MBEDTLS_RSA_ALT", +#endif /* MBEDTLS_RSA_ALT */ +#if defined(MBEDTLS_SHA1_ALT) + "MBEDTLS_SHA1_ALT", +#endif /* MBEDTLS_SHA1_ALT */ +#if defined(MBEDTLS_SHA256_ALT) + "MBEDTLS_SHA256_ALT", +#endif /* MBEDTLS_SHA256_ALT */ +#if defined(MBEDTLS_SHA512_ALT) + "MBEDTLS_SHA512_ALT", +#endif /* MBEDTLS_SHA512_ALT */ +#if defined(MBEDTLS_ECP_ALT) + "MBEDTLS_ECP_ALT", +#endif /* MBEDTLS_ECP_ALT */ +#if defined(MBEDTLS_MD5_PROCESS_ALT) + "MBEDTLS_MD5_PROCESS_ALT", +#endif /* MBEDTLS_MD5_PROCESS_ALT */ +#if defined(MBEDTLS_RIPEMD160_PROCESS_ALT) + "MBEDTLS_RIPEMD160_PROCESS_ALT", +#endif /* MBEDTLS_RIPEMD160_PROCESS_ALT */ +#if defined(MBEDTLS_SHA1_PROCESS_ALT) + "MBEDTLS_SHA1_PROCESS_ALT", +#endif /* MBEDTLS_SHA1_PROCESS_ALT */ +#if defined(MBEDTLS_SHA256_PROCESS_ALT) + "MBEDTLS_SHA256_PROCESS_ALT", +#endif /* MBEDTLS_SHA256_PROCESS_ALT */ +#if defined(MBEDTLS_SHA512_PROCESS_ALT) + "MBEDTLS_SHA512_PROCESS_ALT", +#endif /* MBEDTLS_SHA512_PROCESS_ALT */ +#if defined(MBEDTLS_DES_SETKEY_ALT) + "MBEDTLS_DES_SETKEY_ALT", +#endif /* MBEDTLS_DES_SETKEY_ALT */ +#if defined(MBEDTLS_DES_CRYPT_ECB_ALT) + "MBEDTLS_DES_CRYPT_ECB_ALT", +#endif /* MBEDTLS_DES_CRYPT_ECB_ALT */ +#if defined(MBEDTLS_DES3_CRYPT_ECB_ALT) + "MBEDTLS_DES3_CRYPT_ECB_ALT", +#endif /* MBEDTLS_DES3_CRYPT_ECB_ALT */ +#if defined(MBEDTLS_AES_SETKEY_ENC_ALT) + "MBEDTLS_AES_SETKEY_ENC_ALT", +#endif /* MBEDTLS_AES_SETKEY_ENC_ALT */ +#if defined(MBEDTLS_AES_SETKEY_DEC_ALT) + "MBEDTLS_AES_SETKEY_DEC_ALT", +#endif /* MBEDTLS_AES_SETKEY_DEC_ALT */ +#if defined(MBEDTLS_AES_ENCRYPT_ALT) + "MBEDTLS_AES_ENCRYPT_ALT", +#endif /* MBEDTLS_AES_ENCRYPT_ALT */ +#if defined(MBEDTLS_AES_DECRYPT_ALT) + "MBEDTLS_AES_DECRYPT_ALT", +#endif /* MBEDTLS_AES_DECRYPT_ALT */ +#if defined(MBEDTLS_ECDH_GEN_PUBLIC_ALT) + "MBEDTLS_ECDH_GEN_PUBLIC_ALT", +#endif /* MBEDTLS_ECDH_GEN_PUBLIC_ALT */ +#if defined(MBEDTLS_ECDH_COMPUTE_SHARED_ALT) + "MBEDTLS_ECDH_COMPUTE_SHARED_ALT", +#endif /* MBEDTLS_ECDH_COMPUTE_SHARED_ALT */ +#if defined(MBEDTLS_ECDSA_VERIFY_ALT) + "MBEDTLS_ECDSA_VERIFY_ALT", +#endif /* MBEDTLS_ECDSA_VERIFY_ALT */ +#if defined(MBEDTLS_ECDSA_SIGN_ALT) + "MBEDTLS_ECDSA_SIGN_ALT", +#endif /* MBEDTLS_ECDSA_SIGN_ALT */ +#if defined(MBEDTLS_ECDSA_GENKEY_ALT) + "MBEDTLS_ECDSA_GENKEY_ALT", +#endif /* MBEDTLS_ECDSA_GENKEY_ALT */ +#if defined(MBEDTLS_ECP_INTERNAL_ALT) + "MBEDTLS_ECP_INTERNAL_ALT", +#endif /* MBEDTLS_ECP_INTERNAL_ALT */ +#if defined(MBEDTLS_ECP_NO_FALLBACK) + "MBEDTLS_ECP_NO_FALLBACK", +#endif /* MBEDTLS_ECP_NO_FALLBACK */ +#if defined(MBEDTLS_ECP_RANDOMIZE_JAC_ALT) + "MBEDTLS_ECP_RANDOMIZE_JAC_ALT", +#endif /* MBEDTLS_ECP_RANDOMIZE_JAC_ALT */ +#if defined(MBEDTLS_ECP_ADD_MIXED_ALT) + "MBEDTLS_ECP_ADD_MIXED_ALT", +#endif /* MBEDTLS_ECP_ADD_MIXED_ALT */ +#if defined(MBEDTLS_ECP_DOUBLE_JAC_ALT) + "MBEDTLS_ECP_DOUBLE_JAC_ALT", +#endif /* MBEDTLS_ECP_DOUBLE_JAC_ALT */ +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT) + "MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT", +#endif /* MBEDTLS_ECP_NORMALIZE_JAC_MANY_ALT */ +#if defined(MBEDTLS_ECP_NORMALIZE_JAC_ALT) + "MBEDTLS_ECP_NORMALIZE_JAC_ALT", +#endif /* MBEDTLS_ECP_NORMALIZE_JAC_ALT */ +#if defined(MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT) + "MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT", +#endif /* MBEDTLS_ECP_DOUBLE_ADD_MXZ_ALT */ +#if defined(MBEDTLS_ECP_RANDOMIZE_MXZ_ALT) + "MBEDTLS_ECP_RANDOMIZE_MXZ_ALT", +#endif /* MBEDTLS_ECP_RANDOMIZE_MXZ_ALT */ +#if defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT) + "MBEDTLS_ECP_NORMALIZE_MXZ_ALT", +#endif /* MBEDTLS_ECP_NORMALIZE_MXZ_ALT */ +#if defined(MBEDTLS_ENTROPY_HARDWARE_ALT) + "MBEDTLS_ENTROPY_HARDWARE_ALT", +#endif /* MBEDTLS_ENTROPY_HARDWARE_ALT */ +#if defined(MBEDTLS_AES_ROM_TABLES) + "MBEDTLS_AES_ROM_TABLES", +#endif /* MBEDTLS_AES_ROM_TABLES */ +#if defined(MBEDTLS_AES_FEWER_TABLES) + "MBEDTLS_AES_FEWER_TABLES", +#endif /* MBEDTLS_AES_FEWER_TABLES */ +#if defined(MBEDTLS_CAMELLIA_SMALL_MEMORY) + "MBEDTLS_CAMELLIA_SMALL_MEMORY", +#endif /* MBEDTLS_CAMELLIA_SMALL_MEMORY */ +#if defined(MBEDTLS_CHECK_RETURN_WARNING) + "MBEDTLS_CHECK_RETURN_WARNING", +#endif /* MBEDTLS_CHECK_RETURN_WARNING */ +#if defined(MBEDTLS_CIPHER_MODE_CBC) + "MBEDTLS_CIPHER_MODE_CBC", +#endif /* MBEDTLS_CIPHER_MODE_CBC */ +#if defined(MBEDTLS_CIPHER_MODE_CFB) + "MBEDTLS_CIPHER_MODE_CFB", +#endif /* MBEDTLS_CIPHER_MODE_CFB */ +#if defined(MBEDTLS_CIPHER_MODE_CTR) + "MBEDTLS_CIPHER_MODE_CTR", +#endif /* MBEDTLS_CIPHER_MODE_CTR */ +#if defined(MBEDTLS_CIPHER_MODE_OFB) + "MBEDTLS_CIPHER_MODE_OFB", +#endif /* MBEDTLS_CIPHER_MODE_OFB */ +#if defined(MBEDTLS_CIPHER_MODE_XTS) + "MBEDTLS_CIPHER_MODE_XTS", +#endif /* MBEDTLS_CIPHER_MODE_XTS */ +#if defined(MBEDTLS_CIPHER_NULL_CIPHER) + "MBEDTLS_CIPHER_NULL_CIPHER", +#endif /* MBEDTLS_CIPHER_NULL_CIPHER */ +#if defined(MBEDTLS_CIPHER_PADDING_PKCS7) + "MBEDTLS_CIPHER_PADDING_PKCS7", +#endif /* MBEDTLS_CIPHER_PADDING_PKCS7 */ +#if defined(MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS) + "MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS", +#endif /* MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS */ +#if defined(MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN) + "MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN", +#endif /* MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN */ +#if defined(MBEDTLS_CIPHER_PADDING_ZEROS) + "MBEDTLS_CIPHER_PADDING_ZEROS", +#endif /* MBEDTLS_CIPHER_PADDING_ZEROS */ +#if defined(MBEDTLS_CTR_DRBG_USE_128_BIT_KEY) + "MBEDTLS_CTR_DRBG_USE_128_BIT_KEY", +#endif /* MBEDTLS_CTR_DRBG_USE_128_BIT_KEY */ +#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED) + "MBEDTLS_ECP_DP_SECP192R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) + "MBEDTLS_ECP_DP_SECP224R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP224R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED) + "MBEDTLS_ECP_DP_SECP256R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP256R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED) + "MBEDTLS_ECP_DP_SECP384R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP384R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED) + "MBEDTLS_ECP_DP_SECP521R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP521R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP192K1_ENABLED) + "MBEDTLS_ECP_DP_SECP192K1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP192K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP224K1_ENABLED) + "MBEDTLS_ECP_DP_SECP224K1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP224K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_SECP256K1_ENABLED) + "MBEDTLS_ECP_DP_SECP256K1_ENABLED", +#endif /* MBEDTLS_ECP_DP_SECP256K1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP256R1_ENABLED) + "MBEDTLS_ECP_DP_BP256R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_BP256R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP384R1_ENABLED) + "MBEDTLS_ECP_DP_BP384R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_BP384R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_BP512R1_ENABLED) + "MBEDTLS_ECP_DP_BP512R1_ENABLED", +#endif /* MBEDTLS_ECP_DP_BP512R1_ENABLED */ +#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED) + "MBEDTLS_ECP_DP_CURVE25519_ENABLED", +#endif /* MBEDTLS_ECP_DP_CURVE25519_ENABLED */ +#if defined(MBEDTLS_ECP_DP_CURVE448_ENABLED) + "MBEDTLS_ECP_DP_CURVE448_ENABLED", +#endif /* MBEDTLS_ECP_DP_CURVE448_ENABLED */ +#if defined(MBEDTLS_ECP_NIST_OPTIM) + "MBEDTLS_ECP_NIST_OPTIM", +#endif /* MBEDTLS_ECP_NIST_OPTIM */ +#if defined(MBEDTLS_ECP_RESTARTABLE) + "MBEDTLS_ECP_RESTARTABLE", +#endif /* MBEDTLS_ECP_RESTARTABLE */ +#if defined(MBEDTLS_ECDSA_DETERMINISTIC) + "MBEDTLS_ECDSA_DETERMINISTIC", +#endif /* MBEDTLS_ECDSA_DETERMINISTIC */ +#if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED) + "MBEDTLS_KEY_EXCHANGE_PSK_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED) + "MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) + "MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED) + "MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_PSK_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_RSA_ENABLED) + "MBEDTLS_KEY_EXCHANGE_RSA_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED) + "MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) + "MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) + "MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED) + "MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) + "MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED */ +#if defined(MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED) + "MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED", +#endif /* MBEDTLS_KEY_EXCHANGE_ECJPAKE_ENABLED */ +#if defined(MBEDTLS_PK_PARSE_EC_EXTENDED) + "MBEDTLS_PK_PARSE_EC_EXTENDED", +#endif /* MBEDTLS_PK_PARSE_EC_EXTENDED */ +#if defined(MBEDTLS_ERROR_STRERROR_DUMMY) + "MBEDTLS_ERROR_STRERROR_DUMMY", +#endif /* MBEDTLS_ERROR_STRERROR_DUMMY */ +#if defined(MBEDTLS_GENPRIME) + "MBEDTLS_GENPRIME", +#endif /* MBEDTLS_GENPRIME */ +#if defined(MBEDTLS_FS_IO) + "MBEDTLS_FS_IO", +#endif /* MBEDTLS_FS_IO */ +#if defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) + "MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES", +#endif /* MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES */ +#if defined(MBEDTLS_NO_PLATFORM_ENTROPY) + "MBEDTLS_NO_PLATFORM_ENTROPY", +#endif /* MBEDTLS_NO_PLATFORM_ENTROPY */ +#if defined(MBEDTLS_ENTROPY_FORCE_SHA256) + "MBEDTLS_ENTROPY_FORCE_SHA256", +#endif /* MBEDTLS_ENTROPY_FORCE_SHA256 */ +#if defined(MBEDTLS_ENTROPY_NV_SEED) + "MBEDTLS_ENTROPY_NV_SEED", +#endif /* MBEDTLS_ENTROPY_NV_SEED */ +#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) + "MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER", +#endif /* MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER */ +#if defined(MBEDTLS_MEMORY_DEBUG) + "MBEDTLS_MEMORY_DEBUG", +#endif /* MBEDTLS_MEMORY_DEBUG */ +#if defined(MBEDTLS_MEMORY_BACKTRACE) + "MBEDTLS_MEMORY_BACKTRACE", +#endif /* MBEDTLS_MEMORY_BACKTRACE */ +#if defined(MBEDTLS_PK_RSA_ALT_SUPPORT) + "MBEDTLS_PK_RSA_ALT_SUPPORT", +#endif /* MBEDTLS_PK_RSA_ALT_SUPPORT */ +#if defined(MBEDTLS_PKCS1_V15) + "MBEDTLS_PKCS1_V15", +#endif /* MBEDTLS_PKCS1_V15 */ +#if defined(MBEDTLS_PKCS1_V21) + "MBEDTLS_PKCS1_V21", +#endif /* MBEDTLS_PKCS1_V21 */ +#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) + "MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS", +#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */ +#if defined(MBEDTLS_PSA_CRYPTO_CLIENT) + "MBEDTLS_PSA_CRYPTO_CLIENT", +#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */ +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) + "MBEDTLS_PSA_CRYPTO_DRIVERS", +#endif /* MBEDTLS_PSA_CRYPTO_DRIVERS */ +#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) + "MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG", +#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */ +#if defined(MBEDTLS_PSA_CRYPTO_SPM) + "MBEDTLS_PSA_CRYPTO_SPM", +#endif /* MBEDTLS_PSA_CRYPTO_SPM */ +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) + "MBEDTLS_PSA_INJECT_ENTROPY", +#endif /* MBEDTLS_PSA_INJECT_ENTROPY */ +#if defined(MBEDTLS_RSA_NO_CRT) + "MBEDTLS_RSA_NO_CRT", +#endif /* MBEDTLS_RSA_NO_CRT */ +#if defined(MBEDTLS_SELF_TEST) + "MBEDTLS_SELF_TEST", +#endif /* MBEDTLS_SELF_TEST */ +#if defined(MBEDTLS_SHA256_SMALLER) + "MBEDTLS_SHA256_SMALLER", +#endif /* MBEDTLS_SHA256_SMALLER */ +#if defined(MBEDTLS_SHA512_SMALLER) + "MBEDTLS_SHA512_SMALLER", +#endif /* MBEDTLS_SHA512_SMALLER */ +#if defined(MBEDTLS_SSL_ALL_ALERT_MESSAGES) + "MBEDTLS_SSL_ALL_ALERT_MESSAGES", +#endif /* MBEDTLS_SSL_ALL_ALERT_MESSAGES */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID) + "MBEDTLS_SSL_DTLS_CONNECTION_ID", +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */ +#if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT) + "MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT", +#endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID_COMPAT */ +#if defined(MBEDTLS_SSL_ASYNC_PRIVATE) + "MBEDTLS_SSL_ASYNC_PRIVATE", +#endif /* MBEDTLS_SSL_ASYNC_PRIVATE */ +#if defined(MBEDTLS_SSL_CONTEXT_SERIALIZATION) + "MBEDTLS_SSL_CONTEXT_SERIALIZATION", +#endif /* MBEDTLS_SSL_CONTEXT_SERIALIZATION */ +#if defined(MBEDTLS_SSL_DEBUG_ALL) + "MBEDTLS_SSL_DEBUG_ALL", +#endif /* MBEDTLS_SSL_DEBUG_ALL */ +#if defined(MBEDTLS_SSL_ENCRYPT_THEN_MAC) + "MBEDTLS_SSL_ENCRYPT_THEN_MAC", +#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */ +#if defined(MBEDTLS_SSL_EXTENDED_MASTER_SECRET) + "MBEDTLS_SSL_EXTENDED_MASTER_SECRET", +#endif /* MBEDTLS_SSL_EXTENDED_MASTER_SECRET */ +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + "MBEDTLS_SSL_KEEP_PEER_CERTIFICATE", +#endif /* MBEDTLS_SSL_KEEP_PEER_CERTIFICATE */ +#if defined(MBEDTLS_SSL_RENEGOTIATION) + "MBEDTLS_SSL_RENEGOTIATION", +#endif /* MBEDTLS_SSL_RENEGOTIATION */ +#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) + "MBEDTLS_SSL_MAX_FRAGMENT_LENGTH", +#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */ +#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) + "MBEDTLS_SSL_RECORD_SIZE_LIMIT", +#endif /* MBEDTLS_SSL_RECORD_SIZE_LIMIT */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_2) + "MBEDTLS_SSL_PROTO_TLS1_2", +#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */ +#if defined(MBEDTLS_SSL_PROTO_TLS1_3) + "MBEDTLS_SSL_PROTO_TLS1_3", +#endif /* MBEDTLS_SSL_PROTO_TLS1_3 */ +#if defined(MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE) + "MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE", +#endif /* MBEDTLS_SSL_TLS1_3_COMPATIBILITY_MODE */ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED) + "MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED", +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_ENABLED */ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED) + "MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED", +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_EPHEMERAL_ENABLED */ +#if defined(MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED) + "MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED", +#endif /* MBEDTLS_SSL_TLS1_3_KEY_EXCHANGE_MODE_PSK_EPHEMERAL_ENABLED */ +#if defined(MBEDTLS_SSL_EARLY_DATA) + "MBEDTLS_SSL_EARLY_DATA", +#endif /* MBEDTLS_SSL_EARLY_DATA */ +#if defined(MBEDTLS_SSL_MAX_EARLY_DATA_SIZE) + "MBEDTLS_SSL_MAX_EARLY_DATA_SIZE", +#endif /* MBEDTLS_SSL_MAX_EARLY_DATA_SIZE */ +#if defined(MBEDTLS_SSL_PROTO_DTLS) + "MBEDTLS_SSL_PROTO_DTLS", +#endif /* MBEDTLS_SSL_PROTO_DTLS */ +#if defined(MBEDTLS_SSL_ALPN) + "MBEDTLS_SSL_ALPN", +#endif /* MBEDTLS_SSL_ALPN */ +#if defined(MBEDTLS_SSL_DTLS_ANTI_REPLAY) + "MBEDTLS_SSL_DTLS_ANTI_REPLAY", +#endif /* MBEDTLS_SSL_DTLS_ANTI_REPLAY */ +#if defined(MBEDTLS_SSL_DTLS_HELLO_VERIFY) + "MBEDTLS_SSL_DTLS_HELLO_VERIFY", +#endif /* MBEDTLS_SSL_DTLS_HELLO_VERIFY */ +#if defined(MBEDTLS_SSL_DTLS_SRTP) + "MBEDTLS_SSL_DTLS_SRTP", +#endif /* MBEDTLS_SSL_DTLS_SRTP */ +#if defined(MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE) + "MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE", +#endif /* MBEDTLS_SSL_DTLS_CLIENT_PORT_REUSE */ +#if defined(MBEDTLS_SSL_SESSION_TICKETS) + "MBEDTLS_SSL_SESSION_TICKETS", +#endif /* MBEDTLS_SSL_SESSION_TICKETS */ +#if defined(MBEDTLS_SSL_SERVER_NAME_INDICATION) + "MBEDTLS_SSL_SERVER_NAME_INDICATION", +#endif /* MBEDTLS_SSL_SERVER_NAME_INDICATION */ +#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH) + "MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH", +#endif /* MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH */ +#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) + "MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN", +#endif /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN */ +#if defined(MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND) + "MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND", +#endif /* MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND */ +#if defined(MBEDTLS_TEST_HOOKS) + "MBEDTLS_TEST_HOOKS", +#endif /* MBEDTLS_TEST_HOOKS */ +#if defined(MBEDTLS_THREADING_ALT) + "MBEDTLS_THREADING_ALT", +#endif /* MBEDTLS_THREADING_ALT */ +#if defined(MBEDTLS_THREADING_PTHREAD) + "MBEDTLS_THREADING_PTHREAD", +#endif /* MBEDTLS_THREADING_PTHREAD */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + "MBEDTLS_USE_PSA_CRYPTO", +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#if defined(MBEDTLS_PSA_CRYPTO_CONFIG) + "MBEDTLS_PSA_CRYPTO_CONFIG", +#endif /* MBEDTLS_PSA_CRYPTO_CONFIG */ +#if defined(MBEDTLS_VERSION_FEATURES) + "MBEDTLS_VERSION_FEATURES", +#endif /* MBEDTLS_VERSION_FEATURES */ +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + "MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK", +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ +#if defined(MBEDTLS_X509_REMOVE_INFO) + "MBEDTLS_X509_REMOVE_INFO", +#endif /* MBEDTLS_X509_REMOVE_INFO */ +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + "MBEDTLS_X509_RSASSA_PSS_SUPPORT", +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ +#if defined(MBEDTLS_AESNI_C) + "MBEDTLS_AESNI_C", +#endif /* MBEDTLS_AESNI_C */ +#if defined(MBEDTLS_AESCE_C) + "MBEDTLS_AESCE_C", +#endif /* MBEDTLS_AESCE_C */ +#if defined(MBEDTLS_AES_C) + "MBEDTLS_AES_C", +#endif /* MBEDTLS_AES_C */ +#if defined(MBEDTLS_ASN1_PARSE_C) + "MBEDTLS_ASN1_PARSE_C", +#endif /* MBEDTLS_ASN1_PARSE_C */ +#if defined(MBEDTLS_ASN1_WRITE_C) + "MBEDTLS_ASN1_WRITE_C", +#endif /* MBEDTLS_ASN1_WRITE_C */ +#if defined(MBEDTLS_BASE64_C) + "MBEDTLS_BASE64_C", +#endif /* MBEDTLS_BASE64_C */ +#if defined(MBEDTLS_BIGNUM_C) + "MBEDTLS_BIGNUM_C", +#endif /* MBEDTLS_BIGNUM_C */ +#if defined(MBEDTLS_CAMELLIA_C) + "MBEDTLS_CAMELLIA_C", +#endif /* MBEDTLS_CAMELLIA_C */ +#if defined(MBEDTLS_ARIA_C) + "MBEDTLS_ARIA_C", +#endif /* MBEDTLS_ARIA_C */ +#if defined(MBEDTLS_CCM_C) + "MBEDTLS_CCM_C", +#endif /* MBEDTLS_CCM_C */ +#if defined(MBEDTLS_CHACHA20_C) + "MBEDTLS_CHACHA20_C", +#endif /* MBEDTLS_CHACHA20_C */ +#if defined(MBEDTLS_CHACHAPOLY_C) + "MBEDTLS_CHACHAPOLY_C", +#endif /* MBEDTLS_CHACHAPOLY_C */ +#if defined(MBEDTLS_CIPHER_C) + "MBEDTLS_CIPHER_C", +#endif /* MBEDTLS_CIPHER_C */ +#if defined(MBEDTLS_CMAC_C) + "MBEDTLS_CMAC_C", +#endif /* MBEDTLS_CMAC_C */ +#if defined(MBEDTLS_CTR_DRBG_C) + "MBEDTLS_CTR_DRBG_C", +#endif /* MBEDTLS_CTR_DRBG_C */ +#if defined(MBEDTLS_DEBUG_C) + "MBEDTLS_DEBUG_C", +#endif /* MBEDTLS_DEBUG_C */ +#if defined(MBEDTLS_DES_C) + "MBEDTLS_DES_C", +#endif /* MBEDTLS_DES_C */ +#if defined(MBEDTLS_DHM_C) + "MBEDTLS_DHM_C", +#endif /* MBEDTLS_DHM_C */ +#if defined(MBEDTLS_ECDH_C) + "MBEDTLS_ECDH_C", +#endif /* MBEDTLS_ECDH_C */ +#if defined(MBEDTLS_ECDSA_C) + "MBEDTLS_ECDSA_C", +#endif /* MBEDTLS_ECDSA_C */ +#if defined(MBEDTLS_ECJPAKE_C) + "MBEDTLS_ECJPAKE_C", +#endif /* MBEDTLS_ECJPAKE_C */ +#if defined(MBEDTLS_ECP_C) + "MBEDTLS_ECP_C", +#endif /* MBEDTLS_ECP_C */ +#if defined(MBEDTLS_ENTROPY_C) + "MBEDTLS_ENTROPY_C", +#endif /* MBEDTLS_ENTROPY_C */ +#if defined(MBEDTLS_ERROR_C) + "MBEDTLS_ERROR_C", +#endif /* MBEDTLS_ERROR_C */ +#if defined(MBEDTLS_GCM_C) + "MBEDTLS_GCM_C", +#endif /* MBEDTLS_GCM_C */ +#if defined(MBEDTLS_HKDF_C) + "MBEDTLS_HKDF_C", +#endif /* MBEDTLS_HKDF_C */ +#if defined(MBEDTLS_HMAC_DRBG_C) + "MBEDTLS_HMAC_DRBG_C", +#endif /* MBEDTLS_HMAC_DRBG_C */ +#if defined(MBEDTLS_LMS_C) + "MBEDTLS_LMS_C", +#endif /* MBEDTLS_LMS_C */ +#if defined(MBEDTLS_LMS_PRIVATE) + "MBEDTLS_LMS_PRIVATE", +#endif /* MBEDTLS_LMS_PRIVATE */ +#if defined(MBEDTLS_NIST_KW_C) + "MBEDTLS_NIST_KW_C", +#endif /* MBEDTLS_NIST_KW_C */ +#if defined(MBEDTLS_MD_C) + "MBEDTLS_MD_C", +#endif /* MBEDTLS_MD_C */ +#if defined(MBEDTLS_MD5_C) + "MBEDTLS_MD5_C", +#endif /* MBEDTLS_MD5_C */ +#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) + "MBEDTLS_MEMORY_BUFFER_ALLOC_C", +#endif /* MBEDTLS_MEMORY_BUFFER_ALLOC_C */ +#if defined(MBEDTLS_NET_C) + "MBEDTLS_NET_C", +#endif /* MBEDTLS_NET_C */ +#if defined(MBEDTLS_OID_C) + "MBEDTLS_OID_C", +#endif /* MBEDTLS_OID_C */ +#if defined(MBEDTLS_PADLOCK_C) + "MBEDTLS_PADLOCK_C", +#endif /* MBEDTLS_PADLOCK_C */ +#if defined(MBEDTLS_PEM_PARSE_C) + "MBEDTLS_PEM_PARSE_C", +#endif /* MBEDTLS_PEM_PARSE_C */ +#if defined(MBEDTLS_PEM_WRITE_C) + "MBEDTLS_PEM_WRITE_C", +#endif /* MBEDTLS_PEM_WRITE_C */ +#if defined(MBEDTLS_PK_C) + "MBEDTLS_PK_C", +#endif /* MBEDTLS_PK_C */ +#if defined(MBEDTLS_PK_PARSE_C) + "MBEDTLS_PK_PARSE_C", +#endif /* MBEDTLS_PK_PARSE_C */ +#if defined(MBEDTLS_PK_WRITE_C) + "MBEDTLS_PK_WRITE_C", +#endif /* MBEDTLS_PK_WRITE_C */ +#if defined(MBEDTLS_PKCS5_C) + "MBEDTLS_PKCS5_C", +#endif /* MBEDTLS_PKCS5_C */ +#if defined(MBEDTLS_PKCS7_C) + "MBEDTLS_PKCS7_C", +#endif /* MBEDTLS_PKCS7_C */ +#if defined(MBEDTLS_PKCS12_C) + "MBEDTLS_PKCS12_C", +#endif /* MBEDTLS_PKCS12_C */ +#if defined(MBEDTLS_PLATFORM_C) + "MBEDTLS_PLATFORM_C", +#endif /* MBEDTLS_PLATFORM_C */ +#if defined(MBEDTLS_POLY1305_C) + "MBEDTLS_POLY1305_C", +#endif /* MBEDTLS_POLY1305_C */ +#if defined(MBEDTLS_PSA_CRYPTO_C) + "MBEDTLS_PSA_CRYPTO_C", +#endif /* MBEDTLS_PSA_CRYPTO_C */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) + "MBEDTLS_PSA_CRYPTO_SE_C", +#endif /* MBEDTLS_PSA_CRYPTO_SE_C */ +#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) + "MBEDTLS_PSA_CRYPTO_STORAGE_C", +#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C */ +#if defined(MBEDTLS_PSA_ITS_FILE_C) + "MBEDTLS_PSA_ITS_FILE_C", +#endif /* MBEDTLS_PSA_ITS_FILE_C */ +#if defined(MBEDTLS_RIPEMD160_C) + "MBEDTLS_RIPEMD160_C", +#endif /* MBEDTLS_RIPEMD160_C */ +#if defined(MBEDTLS_RSA_C) + "MBEDTLS_RSA_C", +#endif /* MBEDTLS_RSA_C */ +#if defined(MBEDTLS_SHA1_C) + "MBEDTLS_SHA1_C", +#endif /* MBEDTLS_SHA1_C */ +#if defined(MBEDTLS_SHA224_C) + "MBEDTLS_SHA224_C", +#endif /* MBEDTLS_SHA224_C */ +#if defined(MBEDTLS_SHA256_C) + "MBEDTLS_SHA256_C", +#endif /* MBEDTLS_SHA256_C */ +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT) + "MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT", +#endif /* MBEDTLS_SHA256_USE_A64_CRYPTO_IF_PRESENT */ +#if defined(MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY) + "MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY", +#endif /* MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY */ +#if defined(MBEDTLS_SHA384_C) + "MBEDTLS_SHA384_C", +#endif /* MBEDTLS_SHA384_C */ +#if defined(MBEDTLS_SHA512_C) + "MBEDTLS_SHA512_C", +#endif /* MBEDTLS_SHA512_C */ +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT) + "MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT", +#endif /* MBEDTLS_SHA512_USE_A64_CRYPTO_IF_PRESENT */ +#if defined(MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY) + "MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY", +#endif /* MBEDTLS_SHA512_USE_A64_CRYPTO_ONLY */ +#if defined(MBEDTLS_SSL_CACHE_C) + "MBEDTLS_SSL_CACHE_C", +#endif /* MBEDTLS_SSL_CACHE_C */ +#if defined(MBEDTLS_SSL_COOKIE_C) + "MBEDTLS_SSL_COOKIE_C", +#endif /* MBEDTLS_SSL_COOKIE_C */ +#if defined(MBEDTLS_SSL_TICKET_C) + "MBEDTLS_SSL_TICKET_C", +#endif /* MBEDTLS_SSL_TICKET_C */ +#if defined(MBEDTLS_SSL_CLI_C) + "MBEDTLS_SSL_CLI_C", +#endif /* MBEDTLS_SSL_CLI_C */ +#if defined(MBEDTLS_SSL_SRV_C) + "MBEDTLS_SSL_SRV_C", +#endif /* MBEDTLS_SSL_SRV_C */ +#if defined(MBEDTLS_SSL_TLS_C) + "MBEDTLS_SSL_TLS_C", +#endif /* MBEDTLS_SSL_TLS_C */ +#if defined(MBEDTLS_THREADING_C) + "MBEDTLS_THREADING_C", +#endif /* MBEDTLS_THREADING_C */ +#if defined(MBEDTLS_TIMING_C) + "MBEDTLS_TIMING_C", +#endif /* MBEDTLS_TIMING_C */ +#if defined(MBEDTLS_VERSION_C) + "MBEDTLS_VERSION_C", +#endif /* MBEDTLS_VERSION_C */ +#if defined(MBEDTLS_X509_USE_C) + "MBEDTLS_X509_USE_C", +#endif /* MBEDTLS_X509_USE_C */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + "MBEDTLS_X509_CRT_PARSE_C", +#endif /* MBEDTLS_X509_CRT_PARSE_C */ +#if defined(MBEDTLS_X509_CRL_PARSE_C) + "MBEDTLS_X509_CRL_PARSE_C", +#endif /* MBEDTLS_X509_CRL_PARSE_C */ +#if defined(MBEDTLS_X509_CSR_PARSE_C) + "MBEDTLS_X509_CSR_PARSE_C", +#endif /* MBEDTLS_X509_CSR_PARSE_C */ +#if defined(MBEDTLS_X509_CREATE_C) + "MBEDTLS_X509_CREATE_C", +#endif /* MBEDTLS_X509_CREATE_C */ +#if defined(MBEDTLS_X509_CRT_WRITE_C) + "MBEDTLS_X509_CRT_WRITE_C", +#endif /* MBEDTLS_X509_CRT_WRITE_C */ +#if defined(MBEDTLS_X509_CSR_WRITE_C) + "MBEDTLS_X509_CSR_WRITE_C", +#endif /* MBEDTLS_X509_CSR_WRITE_C */ +#endif /* MBEDTLS_VERSION_FEATURES */ + NULL +}; + +int mbedtls_version_check_feature(const char *feature) +{ + const char * const *idx = features; + + if (*idx == NULL) { + return -2; + } + + if (feature == NULL) { + return -1; + } + + while (*idx != NULL) { + if (!strcmp(*idx, feature)) { + return 0; + } + idx++; + } + return -1; +} + +#endif /* MBEDTLS_VERSION_C */ diff --git a/r5dev/thirdparty/mbedtls/x509.c b/r5dev/thirdparty/mbedtls/x509.c new file mode 100644 index 00000000..fc13b921 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/x509.c @@ -0,0 +1,1641 @@ +/* + * X.509 common functions for parsing and verification + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_X509_USE_C) + +#include "mbedtls/x509.h" +#include "mbedtls/asn1.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" + +#include +#include + +#if defined(MBEDTLS_PEM_PARSE_C) +#include "mbedtls/pem.h" +#endif + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_HAVE_TIME) +#include "mbedtls/platform_time.h" +#endif +#if defined(MBEDTLS_HAVE_TIME_DATE) +#include "mbedtls/platform_util.h" +#include +#endif + +#include "mbedtls/legacy_or_psa.h" + +#define CHECK(code) if ((ret = (code)) != 0) { return ret; } +#define CHECK_RANGE(min, max, val) \ + do \ + { \ + if ((val) < (min) || (val) > (max)) \ + { \ + return ret; \ + } \ + } while (0) + +/* + * CertificateSerialNumber ::= INTEGER + */ +int mbedtls_x509_get_serial(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *serial) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((end - *p) < 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_SERIAL, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + if (**p != (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_PRIMITIVE | 2) && + **p != MBEDTLS_ASN1_INTEGER) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_SERIAL, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + serial->tag = *(*p)++; + + if ((ret = mbedtls_asn1_get_len(p, end, &serial->len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_SERIAL, ret); + } + + serial->p = *p; + *p += serial->len; + + return 0; +} + +/* Get an algorithm identifier without parameters (eg for signatures) + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + */ +int mbedtls_x509_get_alg_null(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *alg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_alg_null(p, end, alg)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + return 0; +} + +/* + * Parse an algorithm identifier with (optional) parameters + */ +int mbedtls_x509_get_alg(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *alg, mbedtls_x509_buf *params) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_alg(p, end, alg, params)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + return 0; +} + +/* + * Convert md type to string + */ +static inline const char *md_type_to_string(mbedtls_md_type_t md_alg) +{ + switch (md_alg) { +#if defined(MBEDTLS_HAS_ALG_MD5_VIA_MD_OR_PSA) + case MBEDTLS_MD_MD5: + return "MD5"; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA) + case MBEDTLS_MD_SHA1: + return "SHA1"; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_224_VIA_MD_OR_PSA) + case MBEDTLS_MD_SHA224: + return "SHA224"; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_256_VIA_MD_OR_PSA) + case MBEDTLS_MD_SHA256: + return "SHA256"; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_384_VIA_MD_OR_PSA) + case MBEDTLS_MD_SHA384: + return "SHA384"; +#endif +#if defined(MBEDTLS_HAS_ALG_SHA_512_VIA_MD_OR_PSA) + case MBEDTLS_MD_SHA512: + return "SHA512"; +#endif +#if defined(MBEDTLS_HAS_ALG_RIPEMD160_VIA_MD_OR_PSA) + case MBEDTLS_MD_RIPEMD160: + return "RIPEMD160"; +#endif + case MBEDTLS_MD_NONE: + return NULL; + default: + return NULL; + } +} + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) +/* + * HashAlgorithm ::= AlgorithmIdentifier + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * + * For HashAlgorithm, parameters MUST be NULL or absent. + */ +static int x509_get_hash_alg(const mbedtls_x509_buf *alg, mbedtls_md_type_t *md_alg) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p; + const unsigned char *end; + mbedtls_x509_buf md_oid; + size_t len; + + /* Make sure we got a SEQUENCE and setup bounds */ + if (alg->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + p = alg->p; + end = p + alg->len; + + if (p >= end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + /* Parse md_oid */ + md_oid.tag = *p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &md_oid.len, MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + md_oid.p = p; + p += md_oid.len; + + /* Get md_alg from md_oid */ + if ((ret = mbedtls_oid_get_md_alg(&md_oid, md_alg)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + /* Make sure params is absent of NULL */ + if (p == end) { + return 0; + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_NULL)) != 0 || len != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * RSASSA-PSS-params ::= SEQUENCE { + * hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, + * maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1Identifier, + * saltLength [2] INTEGER DEFAULT 20, + * trailerField [3] INTEGER DEFAULT 1 } + * -- Note that the tags in this Sequence are explicit. + * + * RFC 4055 (which defines use of RSASSA-PSS in PKIX) states that the value + * of trailerField MUST be 1, and PKCS#1 v2.2 doesn't even define any other + * option. Enforce this at parsing time. + */ +int mbedtls_x509_get_rsassa_pss_params(const mbedtls_x509_buf *params, + mbedtls_md_type_t *md_alg, mbedtls_md_type_t *mgf_md, + int *salt_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char *p; + const unsigned char *end, *end2; + size_t len; + mbedtls_x509_buf alg_id, alg_params; + + /* First set everything to defaults */ + *md_alg = MBEDTLS_MD_SHA1; + *mgf_md = MBEDTLS_MD_SHA1; + *salt_len = 20; + + /* Make sure params is a SEQUENCE and setup bounds */ + if (params->tag != (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + p = (unsigned char *) params->p; + end = p + params->len; + + if (p == end) { + return 0; + } + + /* + * HashAlgorithm + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + 0)) == 0) { + end2 = p + len; + + /* HashAlgorithm ::= AlgorithmIdentifier (without parameters) */ + if ((ret = mbedtls_x509_get_alg_null(&p, end2, &alg_id)) != 0) { + return ret; + } + + if ((ret = mbedtls_oid_get_md_alg(&alg_id, md_alg)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p != end2) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p == end) { + return 0; + } + + /* + * MaskGenAlgorithm + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + 1)) == 0) { + end2 = p + len; + + /* MaskGenAlgorithm ::= AlgorithmIdentifier (params = HashAlgorithm) */ + if ((ret = mbedtls_x509_get_alg(&p, end2, &alg_id, &alg_params)) != 0) { + return ret; + } + + /* Only MFG1 is recognised for now */ + if (MBEDTLS_OID_CMP(MBEDTLS_OID_MGF1, &alg_id) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE, + MBEDTLS_ERR_OID_NOT_FOUND); + } + + /* Parse HashAlgorithm */ + if ((ret = x509_get_hash_alg(&alg_params, mgf_md)) != 0) { + return ret; + } + + if (p != end2) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p == end) { + return 0; + } + + /* + * salt_len + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + 2)) == 0) { + end2 = p + len; + + if ((ret = mbedtls_asn1_get_int(&p, end2, salt_len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p != end2) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p == end) { + return 0; + } + + /* + * trailer_field (if present, must be 1) + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + 3)) == 0) { + int trailer_field; + + end2 = p + len; + + if ((ret = mbedtls_asn1_get_int(&p, end2, &trailer_field)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p != end2) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if (trailer_field != 1) { + return MBEDTLS_ERR_X509_INVALID_ALG; + } + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, ret); + } + + if (p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_ALG, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + +/* + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +static int x509_get_attr_type_value(unsigned char **p, + const unsigned char *end, + mbedtls_x509_name *cur) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + mbedtls_x509_buf *oid; + mbedtls_x509_buf *val; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, ret); + } + + end = *p + len; + + if ((end - *p) < 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + oid = &cur->oid; + oid->tag = **p; + + if ((ret = mbedtls_asn1_get_tag(p, end, &oid->len, MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, ret); + } + + oid->p = *p; + *p += oid->len; + + if ((end - *p) < 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + if (**p != MBEDTLS_ASN1_BMP_STRING && **p != MBEDTLS_ASN1_UTF8_STRING && + **p != MBEDTLS_ASN1_T61_STRING && **p != MBEDTLS_ASN1_PRINTABLE_STRING && + **p != MBEDTLS_ASN1_IA5_STRING && **p != MBEDTLS_ASN1_UNIVERSAL_STRING && + **p != MBEDTLS_ASN1_BIT_STRING) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + val = &cur->val; + val->tag = *(*p)++; + + if ((ret = mbedtls_asn1_get_len(p, end, &val->len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, ret); + } + + val->p = *p; + *p += val->len; + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + cur->next = NULL; + + return 0; +} + +/* + * Name ::= CHOICE { -- only one possibility for now -- + * rdnSequence RDNSequence } + * + * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + * + * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + * + * The data structure is optimized for the common case where each RDN has only + * one element, which is represented as a list of AttributeTypeAndValue. + * For the general case we still use a flat list, but we mark elements of the + * same set so that they are "merged" together in the functions that consume + * this list, eg mbedtls_x509_dn_gets(). + * + * On success, this function may allocate a linked list starting at cur->next + * that must later be free'd by the caller using mbedtls_free(). In error + * cases, this function frees all allocated memory internally and the caller + * has no freeing responsibilities. + */ +int mbedtls_x509_get_name(unsigned char **p, const unsigned char *end, + mbedtls_x509_name *cur) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t set_len; + const unsigned char *end_set; + mbedtls_x509_name *head = cur; + + /* don't use recursion, we'd risk stack overflow if not optimized */ + while (1) { + /* + * parse SET + */ + if ((ret = mbedtls_asn1_get_tag(p, end, &set_len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)) != 0) { + ret = MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_NAME, ret); + goto error; + } + + end_set = *p + set_len; + + while (1) { + if ((ret = x509_get_attr_type_value(p, end_set, cur)) != 0) { + goto error; + } + + if (*p == end_set) { + break; + } + + /* Mark this item as being no the only one in a set */ + cur->next_merged = 1; + + cur->next = mbedtls_calloc(1, sizeof(mbedtls_x509_name)); + + if (cur->next == NULL) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto error; + } + + cur = cur->next; + } + + /* + * continue until end of SEQUENCE is reached + */ + if (*p == end) { + return 0; + } + + cur->next = mbedtls_calloc(1, sizeof(mbedtls_x509_name)); + + if (cur->next == NULL) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + goto error; + } + + cur = cur->next; + } + +error: + /* Skip the first element as we did not allocate it */ + mbedtls_asn1_free_named_data_list_shallow(head->next); + head->next = NULL; + + return ret; +} + +static int x509_parse_int(unsigned char **p, size_t n, int *res) +{ + *res = 0; + + for (; n > 0; --n) { + if ((**p < '0') || (**p > '9')) { + return MBEDTLS_ERR_X509_INVALID_DATE; + } + + *res *= 10; + *res += (*(*p)++ - '0'); + } + + return 0; +} + +static int x509_date_is_valid(const mbedtls_x509_time *t) +{ + int ret = MBEDTLS_ERR_X509_INVALID_DATE; + int month_len; + + CHECK_RANGE(0, 9999, t->year); + CHECK_RANGE(0, 23, t->hour); + CHECK_RANGE(0, 59, t->min); + CHECK_RANGE(0, 59, t->sec); + + switch (t->mon) { + case 1: case 3: case 5: case 7: case 8: case 10: case 12: + month_len = 31; + break; + case 4: case 6: case 9: case 11: + month_len = 30; + break; + case 2: + if ((!(t->year % 4) && t->year % 100) || + !(t->year % 400)) { + month_len = 29; + } else { + month_len = 28; + } + break; + default: + return ret; + } + CHECK_RANGE(1, month_len, t->day); + + return 0; +} + +/* + * Parse an ASN1_UTC_TIME (yearlen=2) or ASN1_GENERALIZED_TIME (yearlen=4) + * field. + */ +static int x509_parse_time(unsigned char **p, size_t len, size_t yearlen, + mbedtls_x509_time *tm) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + /* + * Minimum length is 10 or 12 depending on yearlen + */ + if (len < yearlen + 8) { + return MBEDTLS_ERR_X509_INVALID_DATE; + } + len -= yearlen + 8; + + /* + * Parse year, month, day, hour, minute + */ + CHECK(x509_parse_int(p, yearlen, &tm->year)); + if (2 == yearlen) { + if (tm->year < 50) { + tm->year += 100; + } + + tm->year += 1900; + } + + CHECK(x509_parse_int(p, 2, &tm->mon)); + CHECK(x509_parse_int(p, 2, &tm->day)); + CHECK(x509_parse_int(p, 2, &tm->hour)); + CHECK(x509_parse_int(p, 2, &tm->min)); + + /* + * Parse seconds if present + */ + if (len >= 2) { + CHECK(x509_parse_int(p, 2, &tm->sec)); + len -= 2; + } else { + return MBEDTLS_ERR_X509_INVALID_DATE; + } + + /* + * Parse trailing 'Z' if present + */ + if (1 == len && 'Z' == **p) { + (*p)++; + len--; + } + + /* + * We should have parsed all characters at this point + */ + if (0 != len) { + return MBEDTLS_ERR_X509_INVALID_DATE; + } + + CHECK(x509_date_is_valid(tm)); + + return 0; +} + +/* + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + */ +int mbedtls_x509_get_time(unsigned char **p, const unsigned char *end, + mbedtls_x509_time *tm) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len, year_len; + unsigned char tag; + + if ((end - *p) < 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + tag = **p; + + if (tag == MBEDTLS_ASN1_UTC_TIME) { + year_len = 2; + } else if (tag == MBEDTLS_ASN1_GENERALIZED_TIME) { + year_len = 4; + } else { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + (*p)++; + ret = mbedtls_asn1_get_len(p, end, &len); + + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, ret); + } + + return x509_parse_time(p, len, year_len, tm); +} + +int mbedtls_x509_get_sig(unsigned char **p, const unsigned char *end, mbedtls_x509_buf *sig) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + int tag_type; + + if ((end - *p) < 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_SIGNATURE, + MBEDTLS_ERR_ASN1_OUT_OF_DATA); + } + + tag_type = **p; + + if ((ret = mbedtls_asn1_get_bitstring_null(p, end, &len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_SIGNATURE, ret); + } + + sig->tag = tag_type; + sig->len = len; + sig->p = *p; + + *p += len; + + return 0; +} + +/* + * Get signature algorithm from alg OID and optional parameters + */ +int mbedtls_x509_get_sig_alg(const mbedtls_x509_buf *sig_oid, const mbedtls_x509_buf *sig_params, + mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg, + void **sig_opts) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (*sig_opts != NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + if ((ret = mbedtls_oid_get_sig_alg(sig_oid, md_alg, pk_alg)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG, ret); + } + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + if (*pk_alg == MBEDTLS_PK_RSASSA_PSS) { + mbedtls_pk_rsassa_pss_options *pss_opts; + + pss_opts = mbedtls_calloc(1, sizeof(mbedtls_pk_rsassa_pss_options)); + if (pss_opts == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + ret = mbedtls_x509_get_rsassa_pss_params(sig_params, + md_alg, + &pss_opts->mgf1_hash_id, + &pss_opts->expected_salt_len); + if (ret != 0) { + mbedtls_free(pss_opts); + return ret; + } + + *sig_opts = (void *) pss_opts; + } else +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + { + /* Make sure parameters are absent or NULL */ + if ((sig_params->tag != MBEDTLS_ASN1_NULL && sig_params->tag != 0) || + sig_params->len != 0) { + return MBEDTLS_ERR_X509_INVALID_ALG; + } + } + + return 0; +} + +/* + * X.509 Extensions (No parsing of extensions, pointer should + * be either manually updated or extensions should be parsed!) + */ +int mbedtls_x509_get_ext(unsigned char **p, const unsigned char *end, + mbedtls_x509_buf *ext, int tag) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + /* Extension structure use EXPLICIT tagging. That is, the actual + * `Extensions` structure is wrapped by a tag-length pair using + * the respective context-specific tag. */ + ret = mbedtls_asn1_get_tag(p, end, &ext->len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | tag); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + ext->tag = MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | tag; + ext->p = *p; + end = *p + ext->len; + + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (end != *p + len) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Store the name in printable form into buf; no more + * than size characters will be written + */ +int mbedtls_x509_dn_gets(char *buf, size_t size, const mbedtls_x509_name *dn) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, j, n; + unsigned char c, merge = 0; + const mbedtls_x509_name *name; + const char *short_name = NULL; + char s[MBEDTLS_X509_MAX_DN_NAME_SIZE], *p; + + memset(s, 0, sizeof(s)); + + name = dn; + p = buf; + n = size; + + while (name != NULL) { + if (!name->oid.p) { + name = name->next; + continue; + } + + if (name != dn) { + ret = mbedtls_snprintf(p, n, merge ? " + " : ", "); + MBEDTLS_X509_SAFE_SNPRINTF; + } + + ret = mbedtls_oid_get_attr_short_name(&name->oid, &short_name); + + if (ret == 0) { + ret = mbedtls_snprintf(p, n, "%s=", short_name); + } else { + ret = mbedtls_snprintf(p, n, "\?\?="); + } + MBEDTLS_X509_SAFE_SNPRINTF; + + for (i = 0, j = 0; i < name->val.len; i++, j++) { + if (j >= sizeof(s) - 1) { + return MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; + } + + c = name->val.p[i]; + // Special characters requiring escaping, RFC 1779 + if (c && strchr(",=+<>#;\"\\", c)) { + if (j + 1 >= sizeof(s) - 1) { + return MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; + } + s[j++] = '\\'; + } + if (c < 32 || c >= 127) { + s[j] = '?'; + } else { + s[j] = c; + } + } + s[j] = '\0'; + ret = mbedtls_snprintf(p, n, "%s", s); + MBEDTLS_X509_SAFE_SNPRINTF; + + merge = name->next_merged; + name = name->next; + } + + return (int) (size - n); +} + +/* + * Store the serial in printable form into buf; no more + * than size characters will be written + */ +int mbedtls_x509_serial_gets(char *buf, size_t size, const mbedtls_x509_buf *serial) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, n, nr; + char *p; + + p = buf; + n = size; + + nr = (serial->len <= 32) + ? serial->len : 28; + + for (i = 0; i < nr; i++) { + if (i == 0 && nr > 1 && serial->p[i] == 0x0) { + continue; + } + + ret = mbedtls_snprintf(p, n, "%02X%s", + serial->p[i], (i < nr - 1) ? ":" : ""); + MBEDTLS_X509_SAFE_SNPRINTF; + } + + if (nr != serial->len) { + ret = mbedtls_snprintf(p, n, "...."); + MBEDTLS_X509_SAFE_SNPRINTF; + } + + return (int) (size - n); +} + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/* + * Helper for writing signature algorithms + */ +int mbedtls_x509_sig_alg_gets(char *buf, size_t size, const mbedtls_x509_buf *sig_oid, + mbedtls_pk_type_t pk_alg, mbedtls_md_type_t md_alg, + const void *sig_opts) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + char *p = buf; + size_t n = size; + const char *desc = NULL; + + ret = mbedtls_oid_get_sig_alg_desc(sig_oid, &desc); + if (ret != 0) { + ret = mbedtls_snprintf(p, n, "???"); + } else { + ret = mbedtls_snprintf(p, n, "%s", desc); + } + MBEDTLS_X509_SAFE_SNPRINTF; + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + if (pk_alg == MBEDTLS_PK_RSASSA_PSS) { + const mbedtls_pk_rsassa_pss_options *pss_opts; + + pss_opts = (const mbedtls_pk_rsassa_pss_options *) sig_opts; + + const char *name = md_type_to_string(md_alg); + const char *mgf_name = md_type_to_string(pss_opts->mgf1_hash_id); + + ret = mbedtls_snprintf(p, n, " (%s, MGF1-%s, 0x%02X)", + name ? name : "???", + mgf_name ? mgf_name : "???", + (unsigned int) pss_opts->expected_salt_len); + MBEDTLS_X509_SAFE_SNPRINTF; + } +#else + ((void) pk_alg); + ((void) md_alg); + ((void) sig_opts); +#endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ + + return (int) (size - n); +} +#endif /* MBEDTLS_X509_REMOVE_INFO */ + +/* + * Helper for writing "RSA key size", "EC key size", etc + */ +int mbedtls_x509_key_size_helper(char *buf, size_t buf_size, const char *name) +{ + char *p = buf; + size_t n = buf_size; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + ret = mbedtls_snprintf(p, n, "%s key size", name); + MBEDTLS_X509_SAFE_SNPRINTF; + + return 0; +} + +#if defined(MBEDTLS_HAVE_TIME_DATE) +/* + * Set the time structure to the current time. + * Return 0 on success, non-zero on failure. + */ +static int x509_get_current_time(mbedtls_x509_time *now) +{ + struct tm *lt, tm_buf; + mbedtls_time_t tt; + int ret = 0; + + tt = mbedtls_time(NULL); + lt = mbedtls_platform_gmtime_r(&tt, &tm_buf); + + if (lt == NULL) { + ret = -1; + } else { + now->year = lt->tm_year + 1900; + now->mon = lt->tm_mon + 1; + now->day = lt->tm_mday; + now->hour = lt->tm_hour; + now->min = lt->tm_min; + now->sec = lt->tm_sec; + } + + return ret; +} + +/* + * Return 0 if before <= after, 1 otherwise + */ +static int x509_check_time(const mbedtls_x509_time *before, const mbedtls_x509_time *after) +{ + if (before->year > after->year) { + return 1; + } + + if (before->year == after->year && + before->mon > after->mon) { + return 1; + } + + if (before->year == after->year && + before->mon == after->mon && + before->day > after->day) { + return 1; + } + + if (before->year == after->year && + before->mon == after->mon && + before->day == after->day && + before->hour > after->hour) { + return 1; + } + + if (before->year == after->year && + before->mon == after->mon && + before->day == after->day && + before->hour == after->hour && + before->min > after->min) { + return 1; + } + + if (before->year == after->year && + before->mon == after->mon && + before->day == after->day && + before->hour == after->hour && + before->min == after->min && + before->sec > after->sec) { + return 1; + } + + return 0; +} + +int mbedtls_x509_time_is_past(const mbedtls_x509_time *to) +{ + mbedtls_x509_time now; + + if (x509_get_current_time(&now) != 0) { + return 1; + } + + return x509_check_time(&now, to); +} + +int mbedtls_x509_time_is_future(const mbedtls_x509_time *from) +{ + mbedtls_x509_time now; + + if (x509_get_current_time(&now) != 0) { + return 1; + } + + return x509_check_time(from, &now); +} + +#else /* MBEDTLS_HAVE_TIME_DATE */ + +int mbedtls_x509_time_is_past(const mbedtls_x509_time *to) +{ + ((void) to); + return 0; +} + +int mbedtls_x509_time_is_future(const mbedtls_x509_time *from) +{ + ((void) from); + return 0; +} +#endif /* MBEDTLS_HAVE_TIME_DATE */ + +/* Common functions for parsing CRT and CSR. */ +#if defined(MBEDTLS_X509_CRT_PARSE_C) || defined(MBEDTLS_X509_CSR_PARSE_C) +/* + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * HardwareModuleName ::= SEQUENCE { + * hwType OBJECT IDENTIFIER, + * hwSerialNum OCTET STRING } + * + * NOTE: we currently only parse and use otherName of type HwModuleName, + * as defined in RFC 4108. + */ +static int x509_get_other_name(const mbedtls_x509_buf *subject_alt_name, + mbedtls_x509_san_other_name *other_name) +{ + int ret = 0; + size_t len; + unsigned char *p = subject_alt_name->p; + const unsigned char *end = p + subject_alt_name->len; + mbedtls_x509_buf cur_oid; + + if ((subject_alt_name->tag & + (MBEDTLS_ASN1_TAG_CLASS_MASK | MBEDTLS_ASN1_TAG_VALUE_MASK)) != + (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_OTHER_NAME)) { + /* + * The given subject alternative name is not of type "othername". + */ + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + cur_oid.tag = MBEDTLS_ASN1_OID; + cur_oid.p = p; + cur_oid.len = len; + + /* + * Only HwModuleName is currently supported. + */ + if (MBEDTLS_OID_CMP(MBEDTLS_OID_ON_HW_MODULE_NAME, &cur_oid) != 0) { + return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + } + + p += len; + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC)) != + 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (end != p + len) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (end != p + len) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + other_name->value.hardware_module_name.oid.tag = MBEDTLS_ASN1_OID; + other_name->value.hardware_module_name.oid.p = p; + other_name->value.hardware_module_name.oid.len = len; + + p += len; + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + other_name->value.hardware_module_name.val.tag = MBEDTLS_ASN1_OCTET_STRING; + other_name->value.hardware_module_name.val.p = p; + other_name->value.hardware_module_name.val.len = len; + p += len; + if (p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + return 0; +} + +/* + * SubjectAltName ::= GeneralNames + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * + * We list all types, but use the following GeneralName types from RFC 5280: + * "dnsName", "uniformResourceIdentifier" and "hardware_module_name" + * of type "otherName", as defined in RFC 4108. + */ +int mbedtls_x509_get_subject_alt_name(unsigned char **p, + const unsigned char *end, + mbedtls_x509_sequence *subject_alt_name) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len, tag_len; + mbedtls_asn1_sequence *cur = subject_alt_name; + + /* Get main sequence tag */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (*p + len != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + while (*p < end) { + mbedtls_x509_subject_alternative_name dummy_san_buf; + mbedtls_x509_buf tmp_san_buf; + memset(&dummy_san_buf, 0, sizeof(dummy_san_buf)); + + tmp_san_buf.tag = **p; + (*p)++; + + if ((ret = mbedtls_asn1_get_len(p, end, &tag_len)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + tmp_san_buf.p = *p; + tmp_san_buf.len = tag_len; + + if ((tmp_san_buf.tag & MBEDTLS_ASN1_TAG_CLASS_MASK) != + MBEDTLS_ASN1_CONTEXT_SPECIFIC) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + + /* + * Check that the SAN is structured correctly. + */ + ret = mbedtls_x509_parse_subject_alt_name(&tmp_san_buf, &dummy_san_buf); + /* + * In case the extension is malformed, return an error, + * and clear the allocated sequences. + */ + if (ret != 0 && ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE) { + mbedtls_asn1_sequence_free(subject_alt_name->next); + subject_alt_name->next = NULL; + return ret; + } + + /* Allocate and assign next pointer */ + if (cur->buf.p != NULL) { + if (cur->next != NULL) { + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + + cur->next = mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + + if (cur->next == NULL) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_ALLOC_FAILED); + } + + cur = cur->next; + } + + cur->buf = tmp_san_buf; + *p += tmp_san_buf.len; + } + + /* Set final sequence entry's next pointer to NULL */ + cur->next = NULL; + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +int mbedtls_x509_get_ns_cert_type(unsigned char **p, + const unsigned char *end, + unsigned char *ns_cert_type) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_x509_bitstring bs = { 0, 0, NULL }; + + if ((ret = mbedtls_asn1_get_bitstring(p, end, &bs)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + /* A bitstring with no flags set is still technically valid, as it will mean + that the certificate has no designated purpose at the time of creation. */ + if (bs.len == 0) { + *ns_cert_type = 0; + return 0; + } + + if (bs.len != 1) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_INVALID_LENGTH); + } + + /* Get actual bitstring */ + *ns_cert_type = *bs.p; + return 0; +} + +int mbedtls_x509_get_key_usage(unsigned char **p, + const unsigned char *end, + unsigned int *key_usage) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i; + mbedtls_x509_bitstring bs = { 0, 0, NULL }; + + if ((ret = mbedtls_asn1_get_bitstring(p, end, &bs)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + /* A bitstring with no flags set is still technically valid, as it will mean + that the certificate has no designated purpose at the time of creation. */ + if (bs.len == 0) { + *key_usage = 0; + return 0; + } + + /* Get actual bitstring */ + *key_usage = 0; + for (i = 0; i < bs.len && i < sizeof(unsigned int); i++) { + *key_usage |= (unsigned int) bs.p[i] << (8*i); + } + + return 0; +} + +int mbedtls_x509_parse_subject_alt_name(const mbedtls_x509_buf *san_buf, + mbedtls_x509_subject_alternative_name *san) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + switch (san_buf->tag & + (MBEDTLS_ASN1_TAG_CLASS_MASK | + MBEDTLS_ASN1_TAG_VALUE_MASK)) { + /* + * otherName + */ + case (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_OTHER_NAME): + { + mbedtls_x509_san_other_name other_name; + + ret = x509_get_other_name(san_buf, &other_name); + if (ret != 0) { + return ret; + } + + memset(san, 0, sizeof(mbedtls_x509_subject_alternative_name)); + san->type = MBEDTLS_X509_SAN_OTHER_NAME; + memcpy(&san->san.other_name, + &other_name, sizeof(other_name)); + + } + break; + /* + * uniformResourceIdentifier + */ + case (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER): + { + memset(san, 0, sizeof(mbedtls_x509_subject_alternative_name)); + san->type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER; + + memcpy(&san->san.unstructured_name, + san_buf, sizeof(*san_buf)); + + } + break; + /* + * dNSName + */ + case (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_DNS_NAME): + { + memset(san, 0, sizeof(mbedtls_x509_subject_alternative_name)); + san->type = MBEDTLS_X509_SAN_DNS_NAME; + + memcpy(&san->san.unstructured_name, + san_buf, sizeof(*san_buf)); + } + break; + + /* + * RFC822 Name + */ + case (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_X509_SAN_RFC822_NAME): + { + memset(san, 0, sizeof(mbedtls_x509_subject_alternative_name)); + san->type = MBEDTLS_X509_SAN_RFC822_NAME; + memcpy(&san->san.unstructured_name, san_buf, sizeof(*san_buf)); + } + break; + + /* + * Type not supported + */ + default: + return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + } + return 0; +} + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +int mbedtls_x509_info_subject_alt_name(char **buf, size_t *size, + const mbedtls_x509_sequence + *subject_alt_name, + const char *prefix) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i; + size_t n = *size; + char *p = *buf; + const mbedtls_x509_sequence *cur = subject_alt_name; + mbedtls_x509_subject_alternative_name san; + int parse_ret; + + while (cur != NULL) { + memset(&san, 0, sizeof(san)); + parse_ret = mbedtls_x509_parse_subject_alt_name(&cur->buf, &san); + if (parse_ret != 0) { + if (parse_ret == MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE) { + ret = mbedtls_snprintf(p, n, "\n%s ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + } else { + ret = mbedtls_snprintf(p, n, "\n%s ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + } + cur = cur->next; + continue; + } + + switch (san.type) { + /* + * otherName + */ + case MBEDTLS_X509_SAN_OTHER_NAME: + { + mbedtls_x509_san_other_name *other_name = &san.san.other_name; + + ret = mbedtls_snprintf(p, n, "\n%s otherName :", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if (MBEDTLS_OID_CMP(MBEDTLS_OID_ON_HW_MODULE_NAME, + &other_name->value.hardware_module_name.oid) != 0) { + ret = mbedtls_snprintf(p, n, "\n%s hardware module name :", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + ret = + mbedtls_snprintf(p, n, "\n%s hardware type : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_oid_get_numeric_string(p, + n, + &other_name->value.hardware_module_name.oid); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = + mbedtls_snprintf(p, n, "\n%s hardware serial number : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + for (i = 0; i < other_name->value.hardware_module_name.val.len; i++) { + ret = mbedtls_snprintf(p, + n, + "%02X", + other_name->value.hardware_module_name.val.p[i]); + MBEDTLS_X509_SAFE_SNPRINTF; + } + }/* MBEDTLS_OID_ON_HW_MODULE_NAME */ + } + break; + /* + * uniformResourceIdentifier + */ + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + { + ret = mbedtls_snprintf(p, n, "\n%s uniformResourceIdentifier : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + if (san.san.unstructured_name.len >= n) { + *p = '\0'; + return MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; + } + + memcpy(p, san.san.unstructured_name.p, san.san.unstructured_name.len); + p += san.san.unstructured_name.len; + n -= san.san.unstructured_name.len; + } + break; + /* + * dNSName + * RFC822 Name + */ + case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_RFC822_NAME: + { + const char *dns_name = "dNSName"; + const char *rfc822_name = "rfc822Name"; + + ret = mbedtls_snprintf(p, n, + "\n%s %s : ", + prefix, + san.type == + MBEDTLS_X509_SAN_DNS_NAME ? dns_name : rfc822_name); + MBEDTLS_X509_SAFE_SNPRINTF; + if (san.san.unstructured_name.len >= n) { + *p = '\0'; + return MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; + } + + memcpy(p, san.san.unstructured_name.p, san.san.unstructured_name.len); + p += san.san.unstructured_name.len; + n -= san.san.unstructured_name.len; + } + break; + + /* + * Type not supported, skip item. + */ + default: + ret = mbedtls_snprintf(p, n, "\n%s ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + break; + } + + cur = cur->next; + } + + *p = '\0'; + + *size = n; + *buf = p; + + return 0; +} + +#define PRINT_ITEM(i) \ + { \ + ret = mbedtls_snprintf(p, n, "%s" i, sep); \ + MBEDTLS_X509_SAFE_SNPRINTF; \ + sep = ", "; \ + } + +#define CERT_TYPE(type, name) \ + if (ns_cert_type & (type)) \ + PRINT_ITEM(name); + +int mbedtls_x509_info_cert_type(char **buf, size_t *size, + unsigned char ns_cert_type) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n = *size; + char *p = *buf; + const char *sep = ""; + + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_SSL_CLIENT, "SSL Client"); + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_SSL_SERVER, "SSL Server"); + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_EMAIL, "Email"); + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_OBJECT_SIGNING, "Object Signing"); + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_RESERVED, "Reserved"); + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_SSL_CA, "SSL CA"); + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_EMAIL_CA, "Email CA"); + CERT_TYPE(MBEDTLS_X509_NS_CERT_TYPE_OBJECT_SIGNING_CA, "Object Signing CA"); + + *size = n; + *buf = p; + + return 0; +} + +#define KEY_USAGE(code, name) \ + if (key_usage & (code)) \ + PRINT_ITEM(name); + +int mbedtls_x509_info_key_usage(char **buf, size_t *size, + unsigned int key_usage) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n = *size; + char *p = *buf; + const char *sep = ""; + + KEY_USAGE(MBEDTLS_X509_KU_DIGITAL_SIGNATURE, "Digital Signature"); + KEY_USAGE(MBEDTLS_X509_KU_NON_REPUDIATION, "Non Repudiation"); + KEY_USAGE(MBEDTLS_X509_KU_KEY_ENCIPHERMENT, "Key Encipherment"); + KEY_USAGE(MBEDTLS_X509_KU_DATA_ENCIPHERMENT, "Data Encipherment"); + KEY_USAGE(MBEDTLS_X509_KU_KEY_AGREEMENT, "Key Agreement"); + KEY_USAGE(MBEDTLS_X509_KU_KEY_CERT_SIGN, "Key Cert Sign"); + KEY_USAGE(MBEDTLS_X509_KU_CRL_SIGN, "CRL Sign"); + KEY_USAGE(MBEDTLS_X509_KU_ENCIPHER_ONLY, "Encipher Only"); + KEY_USAGE(MBEDTLS_X509_KU_DECIPHER_ONLY, "Decipher Only"); + + *size = n; + *buf = p; + + return 0; +} +#endif /* MBEDTLS_X509_REMOVE_INFO */ +#endif /* MBEDTLS_X509_CRT_PARSE_C || MBEDTLS_X509_CSR_PARSE_C */ +#endif /* MBEDTLS_X509_USE_C */ diff --git a/r5dev/thirdparty/mbedtls/x509_create.c b/r5dev/thirdparty/mbedtls/x509_create.c new file mode 100644 index 00000000..50db9568 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/x509_create.c @@ -0,0 +1,368 @@ +/* + * X.509 base functions for creating certificates / CSRs + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +#if defined(MBEDTLS_X509_CREATE_C) + +#include "mbedtls/x509.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" + +#include + +/* Structure linking OIDs for X.509 DN AttributeTypes to their + * string representations and default string encodings used by Mbed TLS. */ +typedef struct { + const char *name; /* String representation of AttributeType, e.g. + * "CN" or "emailAddress". */ + size_t name_len; /* Length of 'name', without trailing 0 byte. */ + const char *oid; /* String representation of OID of AttributeType, + * as per RFC 5280, Appendix A.1. */ + int default_tag; /* The default character encoding used for the + * given attribute type, e.g. + * MBEDTLS_ASN1_UTF8_STRING for UTF-8. */ +} x509_attr_descriptor_t; + +#define ADD_STRLEN(s) s, sizeof(s) - 1 + +/* X.509 DN attributes from RFC 5280, Appendix A.1. */ +static const x509_attr_descriptor_t x509_attrs[] = +{ + { ADD_STRLEN("CN"), + MBEDTLS_OID_AT_CN, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("commonName"), + MBEDTLS_OID_AT_CN, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("C"), + MBEDTLS_OID_AT_COUNTRY, MBEDTLS_ASN1_PRINTABLE_STRING }, + { ADD_STRLEN("countryName"), + MBEDTLS_OID_AT_COUNTRY, MBEDTLS_ASN1_PRINTABLE_STRING }, + { ADD_STRLEN("O"), + MBEDTLS_OID_AT_ORGANIZATION, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("organizationName"), + MBEDTLS_OID_AT_ORGANIZATION, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("L"), + MBEDTLS_OID_AT_LOCALITY, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("locality"), + MBEDTLS_OID_AT_LOCALITY, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("R"), + MBEDTLS_OID_PKCS9_EMAIL, MBEDTLS_ASN1_IA5_STRING }, + { ADD_STRLEN("OU"), + MBEDTLS_OID_AT_ORG_UNIT, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("organizationalUnitName"), + MBEDTLS_OID_AT_ORG_UNIT, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("ST"), + MBEDTLS_OID_AT_STATE, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("stateOrProvinceName"), + MBEDTLS_OID_AT_STATE, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("emailAddress"), + MBEDTLS_OID_PKCS9_EMAIL, MBEDTLS_ASN1_IA5_STRING }, + { ADD_STRLEN("serialNumber"), + MBEDTLS_OID_AT_SERIAL_NUMBER, MBEDTLS_ASN1_PRINTABLE_STRING }, + { ADD_STRLEN("postalAddress"), + MBEDTLS_OID_AT_POSTAL_ADDRESS, MBEDTLS_ASN1_PRINTABLE_STRING }, + { ADD_STRLEN("postalCode"), + MBEDTLS_OID_AT_POSTAL_CODE, MBEDTLS_ASN1_PRINTABLE_STRING }, + { ADD_STRLEN("dnQualifier"), + MBEDTLS_OID_AT_DN_QUALIFIER, MBEDTLS_ASN1_PRINTABLE_STRING }, + { ADD_STRLEN("title"), + MBEDTLS_OID_AT_TITLE, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("surName"), + MBEDTLS_OID_AT_SUR_NAME, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("SN"), + MBEDTLS_OID_AT_SUR_NAME, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("givenName"), + MBEDTLS_OID_AT_GIVEN_NAME, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("GN"), + MBEDTLS_OID_AT_GIVEN_NAME, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("initials"), + MBEDTLS_OID_AT_INITIALS, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("pseudonym"), + MBEDTLS_OID_AT_PSEUDONYM, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("generationQualifier"), + MBEDTLS_OID_AT_GENERATION_QUALIFIER, MBEDTLS_ASN1_UTF8_STRING }, + { ADD_STRLEN("domainComponent"), + MBEDTLS_OID_DOMAIN_COMPONENT, MBEDTLS_ASN1_IA5_STRING }, + { ADD_STRLEN("DC"), + MBEDTLS_OID_DOMAIN_COMPONENT, MBEDTLS_ASN1_IA5_STRING }, + { NULL, 0, NULL, MBEDTLS_ASN1_NULL } +}; + +static const x509_attr_descriptor_t *x509_attr_descr_from_name(const char *name, size_t name_len) +{ + const x509_attr_descriptor_t *cur; + + for (cur = x509_attrs; cur->name != NULL; cur++) { + if (cur->name_len == name_len && + strncmp(cur->name, name, name_len) == 0) { + break; + } + } + + if (cur->name == NULL) { + return NULL; + } + + return cur; +} + +int mbedtls_x509_string_to_names(mbedtls_asn1_named_data **head, const char *name) +{ + int ret = 0; + const char *s = name, *c = s; + const char *end = s + strlen(s); + const char *oid = NULL; + const x509_attr_descriptor_t *attr_descr = NULL; + int in_tag = 1; + char data[MBEDTLS_X509_MAX_DN_NAME_SIZE]; + char *d = data; + + /* Clear existing chain if present */ + mbedtls_asn1_free_named_data_list(head); + + while (c <= end) { + if (in_tag && *c == '=') { + if ((attr_descr = x509_attr_descr_from_name(s, c - s)) == NULL) { + ret = MBEDTLS_ERR_X509_UNKNOWN_OID; + goto exit; + } + + oid = attr_descr->oid; + s = c + 1; + in_tag = 0; + d = data; + } + + if (!in_tag && *c == '\\' && c != end) { + c++; + + /* Check for valid escaped characters */ + if (c == end || *c != ',') { + ret = MBEDTLS_ERR_X509_INVALID_NAME; + goto exit; + } + } else if (!in_tag && (*c == ',' || c == end)) { + mbedtls_asn1_named_data *cur = + mbedtls_asn1_store_named_data(head, oid, strlen(oid), + (unsigned char *) data, + d - data); + + if (cur == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + // set tagType + cur->val.tag = attr_descr->default_tag; + + while (c < end && *(c + 1) == ' ') { + c++; + } + + s = c + 1; + in_tag = 1; + } + + if (!in_tag && s != c + 1) { + *(d++) = *c; + + if (d - data == MBEDTLS_X509_MAX_DN_NAME_SIZE) { + ret = MBEDTLS_ERR_X509_INVALID_NAME; + goto exit; + } + } + + c++; + } + +exit: + + return ret; +} + +/* The first byte of the value in the mbedtls_asn1_named_data structure is reserved + * to store the critical boolean for us + */ +int mbedtls_x509_set_extension(mbedtls_asn1_named_data **head, const char *oid, size_t oid_len, + int critical, const unsigned char *val, size_t val_len) +{ + mbedtls_asn1_named_data *cur; + + if ((cur = mbedtls_asn1_store_named_data(head, oid, oid_len, + NULL, val_len + 1)) == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + cur->val.p[0] = critical; + memcpy(cur->val.p + 1, val, val_len); + + return 0; +} + +/* + * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +static int x509_write_name(unsigned char **p, + unsigned char *start, + mbedtls_asn1_named_data *cur_name) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + const char *oid = (const char *) cur_name->oid.p; + size_t oid_len = cur_name->oid.len; + const unsigned char *name = cur_name->val.p; + size_t name_len = cur_name->val.len; + + // Write correct string tag and value + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tagged_string(p, start, + cur_name->val.tag, + (const char *) name, + name_len)); + // Write OID + // + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_oid(p, start, oid, + oid_len)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SET)); + + return (int) len; +} + +int mbedtls_x509_write_names(unsigned char **p, unsigned char *start, + mbedtls_asn1_named_data *first) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + mbedtls_asn1_named_data *cur = first; + + while (cur != NULL) { + MBEDTLS_ASN1_CHK_ADD(len, x509_write_name(p, start, cur)); + cur = cur->next; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + return (int) len; +} + +int mbedtls_x509_write_sig(unsigned char **p, unsigned char *start, + const char *oid, size_t oid_len, + unsigned char *sig, size_t size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + if (*p < start || (size_t) (*p - start) < size) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + len = size; + (*p) -= len; + memcpy(*p, sig, len); + + if (*p - start < 1) { + return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL; + } + + *--(*p) = 0; + len += 1; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_BIT_STRING)); + + // Write OID + // + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_algorithm_identifier(p, start, oid, + oid_len, 0)); + + return (int) len; +} + +static int x509_write_extension(unsigned char **p, unsigned char *start, + mbedtls_asn1_named_data *ext) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, ext->val.p + 1, + ext->val.len - 1)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, ext->val.len - 1)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OCTET_STRING)); + + if (ext->val.p[0] != 0) { + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_bool(p, start, 1)); + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, ext->oid.p, + ext->oid.len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, ext->oid.len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_OID)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + return (int) len; +} + +/* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * -- contains the DER encoding of an ASN.1 value + * -- corresponding to the extension type identified + * -- by extnID + * } + */ +int mbedtls_x509_write_extensions(unsigned char **p, unsigned char *start, + mbedtls_asn1_named_data *first) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + mbedtls_asn1_named_data *cur_ext = first; + + while (cur_ext != NULL) { + MBEDTLS_ASN1_CHK_ADD(len, x509_write_extension(p, start, cur_ext)); + cur_ext = cur_ext->next; + } + + return (int) len; +} + +#endif /* MBEDTLS_X509_CREATE_C */ diff --git a/r5dev/thirdparty/mbedtls/x509_crl.c b/r5dev/thirdparty/mbedtls/x509_crl.c new file mode 100644 index 00000000..f6442030 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/x509_crl.c @@ -0,0 +1,727 @@ +/* + * X.509 Certificate Revocation List (CRL) parsing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_X509_CRL_PARSE_C) + +#include "mbedtls/x509_crl.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" + +#include + +#if defined(MBEDTLS_PEM_PARSE_C) +#include "mbedtls/pem.h" +#endif + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_HAVE_TIME) +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) +#include +#else +#include +#endif +#endif + +#if defined(MBEDTLS_FS_IO) || defined(EFIX64) || defined(EFI32) +#include +#endif + +/* + * Version ::= INTEGER { v1(0), v2(1) } + */ +static int x509_crl_get_version(unsigned char **p, + const unsigned char *end, + int *ver) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_int(p, end, ver)) != 0) { + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + *ver = 0; + return 0; + } + + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_VERSION, ret); + } + + return 0; +} + +/* + * X.509 CRL v2 extensions + * + * We currently don't parse any extension's content, but we do check that the + * list of extensions is well-formed and abort on critical extensions (that + * are unsupported as we don't support any extension so far) + */ +static int x509_get_crl_ext(unsigned char **p, + const unsigned char *end, + mbedtls_x509_buf *ext) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (*p == end) { + return 0; + } + + /* + * crlExtensions [0] EXPLICIT Extensions OPTIONAL + * -- if present, version MUST be v2 + */ + if ((ret = mbedtls_x509_get_ext(p, end, ext, 0)) != 0) { + return ret; + } + + end = ext->p + ext->len; + + while (*p < end) { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + int is_critical = 0; + const unsigned char *end_ext_data; + size_t len; + + /* Get enclosing sequence tag */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + end_ext_data = *p + len; + + /* Get OID (currently ignored) */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &len, + MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + *p += len; + + /* Get optional critical */ + if ((ret = mbedtls_asn1_get_bool(p, end_ext_data, + &is_critical)) != 0 && + (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + /* Data should be octet string type */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &len, + MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + /* Ignore data so far and just check its length */ + *p += len; + if (*p != end_ext_data) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* Abort on (unsupported) critical extensions */ + if (is_critical) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * X.509 CRL v2 entry extensions (no extensions parsed yet.) + */ +static int x509_get_crl_entry_ext(unsigned char **p, + const unsigned char *end, + mbedtls_x509_buf *ext) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + /* OPTIONAL */ + if (end <= *p) { + return 0; + } + + ext->tag = **p; + ext->p = *p; + + /* + * Get CRL-entry extension sequence header + * crlEntryExtensions Extensions OPTIONAL -- if present, MUST be v2 + */ + if ((ret = mbedtls_asn1_get_tag(p, end, &ext->len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + ext->p = NULL; + return 0; + } + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + end = *p + ext->len; + + if (end != *p + ext->len) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + while (*p < end) { + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + *p += len; + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * X.509 CRL Entries + */ +static int x509_get_entries(unsigned char **p, + const unsigned char *end, + mbedtls_x509_crl_entry *entry) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t entry_len; + mbedtls_x509_crl_entry *cur_entry = entry; + + if (*p == end) { + return 0; + } + + if ((ret = mbedtls_asn1_get_tag(p, end, &entry_len, + MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) { + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return 0; + } + + return ret; + } + + end = *p + entry_len; + + while (*p < end) { + size_t len2; + const unsigned char *end2; + + cur_entry->raw.tag = **p; + if ((ret = mbedtls_asn1_get_tag(p, end, &len2, + MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED)) != 0) { + return ret; + } + + cur_entry->raw.p = *p; + cur_entry->raw.len = len2; + end2 = *p + len2; + + if ((ret = mbedtls_x509_get_serial(p, end2, &cur_entry->serial)) != 0) { + return ret; + } + + if ((ret = mbedtls_x509_get_time(p, end2, + &cur_entry->revocation_date)) != 0) { + return ret; + } + + if ((ret = x509_get_crl_entry_ext(p, end2, + &cur_entry->entry_ext)) != 0) { + return ret; + } + + if (*p < end) { + cur_entry->next = mbedtls_calloc(1, sizeof(mbedtls_x509_crl_entry)); + + if (cur_entry->next == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + cur_entry = cur_entry->next; + } + } + + return 0; +} + +/* + * Parse one CRLs in DER format and append it to the chained list + */ +int mbedtls_x509_crl_parse_der(mbedtls_x509_crl *chain, + const unsigned char *buf, size_t buflen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + unsigned char *p = NULL, *end = NULL; + mbedtls_x509_buf sig_params1, sig_params2, sig_oid2; + mbedtls_x509_crl *crl = chain; + + /* + * Check for valid input + */ + if (crl == NULL || buf == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + memset(&sig_params1, 0, sizeof(mbedtls_x509_buf)); + memset(&sig_params2, 0, sizeof(mbedtls_x509_buf)); + memset(&sig_oid2, 0, sizeof(mbedtls_x509_buf)); + + /* + * Add new CRL on the end of the chain if needed. + */ + while (crl->version != 0 && crl->next != NULL) { + crl = crl->next; + } + + if (crl->version != 0 && crl->next == NULL) { + crl->next = mbedtls_calloc(1, sizeof(mbedtls_x509_crl)); + + if (crl->next == NULL) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + mbedtls_x509_crl_init(crl->next); + crl = crl->next; + } + + /* + * Copy raw DER-encoded CRL + */ + if (buflen == 0) { + return MBEDTLS_ERR_X509_INVALID_FORMAT; + } + + p = mbedtls_calloc(1, buflen); + if (p == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + memcpy(p, buf, buflen); + + crl->raw.p = p; + crl->raw.len = buflen; + + end = p + buflen; + + /* + * CertificateList ::= SEQUENCE { + * tbsCertList TBSCertList, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERR_X509_INVALID_FORMAT; + } + + if (len != (size_t) (end - p)) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* + * TBSCertList ::= SEQUENCE { + */ + crl->tbs.p = p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + end = p + len; + crl->tbs.len = end - crl->tbs.p; + + /* + * Version ::= INTEGER OPTIONAL { v1(0), v2(1) } + * -- if present, MUST be v2 + * + * signature AlgorithmIdentifier + */ + if ((ret = x509_crl_get_version(&p, end, &crl->version)) != 0 || + (ret = mbedtls_x509_get_alg(&p, end, &crl->sig_oid, &sig_params1)) != 0) { + mbedtls_x509_crl_free(crl); + return ret; + } + + if (crl->version < 0 || crl->version > 1) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERR_X509_UNKNOWN_VERSION; + } + + crl->version++; + + if ((ret = mbedtls_x509_get_sig_alg(&crl->sig_oid, &sig_params1, + &crl->sig_md, &crl->sig_pk, + &crl->sig_opts)) != 0) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG; + } + + /* + * issuer Name + */ + crl->issuer_raw.p = p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + if ((ret = mbedtls_x509_get_name(&p, p + len, &crl->issuer)) != 0) { + mbedtls_x509_crl_free(crl); + return ret; + } + + crl->issuer_raw.len = p - crl->issuer_raw.p; + + /* + * thisUpdate Time + * nextUpdate Time OPTIONAL + */ + if ((ret = mbedtls_x509_get_time(&p, end, &crl->this_update)) != 0) { + mbedtls_x509_crl_free(crl); + return ret; + } + + if ((ret = mbedtls_x509_get_time(&p, end, &crl->next_update)) != 0) { + if (ret != (MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) && + ret != (MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, + MBEDTLS_ERR_ASN1_OUT_OF_DATA))) { + mbedtls_x509_crl_free(crl); + return ret; + } + } + + /* + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate Time, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, MUST be v2 + * } OPTIONAL + */ + if ((ret = x509_get_entries(&p, end, &crl->entry)) != 0) { + mbedtls_x509_crl_free(crl); + return ret; + } + + /* + * crlExtensions EXPLICIT Extensions OPTIONAL + * -- if present, MUST be v2 + */ + if (crl->version == 2) { + ret = x509_get_crl_ext(&p, end, &crl->crl_ext); + + if (ret != 0) { + mbedtls_x509_crl_free(crl); + return ret; + } + } + + if (p != end) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + end = crl->raw.p + crl->raw.len; + + /* + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + */ + if ((ret = mbedtls_x509_get_alg(&p, end, &sig_oid2, &sig_params2)) != 0) { + mbedtls_x509_crl_free(crl); + return ret; + } + + if (crl->sig_oid.len != sig_oid2.len || + memcmp(crl->sig_oid.p, sig_oid2.p, crl->sig_oid.len) != 0 || + sig_params1.len != sig_params2.len || + (sig_params1.len != 0 && + memcmp(sig_params1.p, sig_params2.p, sig_params1.len) != 0)) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERR_X509_SIG_MISMATCH; + } + + if ((ret = mbedtls_x509_get_sig(&p, end, &crl->sig)) != 0) { + mbedtls_x509_crl_free(crl); + return ret; + } + + if (p != end) { + mbedtls_x509_crl_free(crl); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Parse one or more CRLs and add them to the chained list + */ +int mbedtls_x509_crl_parse(mbedtls_x509_crl *chain, const unsigned char *buf, size_t buflen) +{ +#if defined(MBEDTLS_PEM_PARSE_C) + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t use_len = 0; + mbedtls_pem_context pem; + int is_pem = 0; + + if (chain == NULL || buf == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + do { + mbedtls_pem_init(&pem); + + // Avoid calling mbedtls_pem_read_buffer() on non-null-terminated + // string + if (buflen == 0 || buf[buflen - 1] != '\0') { + ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT; + } else { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN X509 CRL-----", + "-----END X509 CRL-----", + buf, NULL, 0, &use_len); + } + + if (ret == 0) { + /* + * Was PEM encoded + */ + is_pem = 1; + + buflen -= use_len; + buf += use_len; + + if ((ret = mbedtls_x509_crl_parse_der(chain, + pem.buf, pem.buflen)) != 0) { + mbedtls_pem_free(&pem); + return ret; + } + } else if (is_pem) { + mbedtls_pem_free(&pem); + return ret; + } + + mbedtls_pem_free(&pem); + } + /* In the PEM case, buflen is 1 at the end, for the terminated NULL byte. + * And a valid CRL cannot be less than 1 byte anyway. */ + while (is_pem && buflen > 1); + + if (is_pem) { + return 0; + } else +#endif /* MBEDTLS_PEM_PARSE_C */ + return mbedtls_x509_crl_parse_der(chain, buf, buflen); +} + +#if defined(MBEDTLS_FS_IO) +/* + * Load one or more CRLs and add them to the chained list + */ +int mbedtls_x509_crl_parse_file(mbedtls_x509_crl *chain, const char *path) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + unsigned char *buf; + + if ((ret = mbedtls_pk_load_file(path, &buf, &n)) != 0) { + return ret; + } + + ret = mbedtls_x509_crl_parse(chain, buf, n); + + mbedtls_platform_zeroize(buf, n); + mbedtls_free(buf); + + return ret; +} +#endif /* MBEDTLS_FS_IO */ + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +/* + * Return an informational string about the certificate. + */ +#define BEFORE_COLON 14 +#define BC "14" +/* + * Return an informational string about the CRL. + */ +int mbedtls_x509_crl_info(char *buf, size_t size, const char *prefix, + const mbedtls_x509_crl *crl) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + char *p; + const mbedtls_x509_crl_entry *entry; + + p = buf; + n = size; + + ret = mbedtls_snprintf(p, n, "%sCRL version : %d", + prefix, crl->version); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%sissuer name : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_dn_gets(p, n, &crl->issuer); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%sthis update : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crl->this_update.year, crl->this_update.mon, + crl->this_update.day, crl->this_update.hour, + crl->this_update.min, crl->this_update.sec); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%snext update : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crl->next_update.year, crl->next_update.mon, + crl->next_update.day, crl->next_update.hour, + crl->next_update.min, crl->next_update.sec); + MBEDTLS_X509_SAFE_SNPRINTF; + + entry = &crl->entry; + + ret = mbedtls_snprintf(p, n, "\n%sRevoked certificates:", + prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + while (entry != NULL && entry->raw.len != 0) { + ret = mbedtls_snprintf(p, n, "\n%sserial number: ", + prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_x509_serial_gets(p, n, &entry->serial); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, " revocation date: " \ + "%04d-%02d-%02d %02d:%02d:%02d", + entry->revocation_date.year, entry->revocation_date.mon, + entry->revocation_date.day, entry->revocation_date.hour, + entry->revocation_date.min, entry->revocation_date.sec); + MBEDTLS_X509_SAFE_SNPRINTF; + + entry = entry->next; + } + + ret = mbedtls_snprintf(p, n, "\n%ssigned using : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_x509_sig_alg_gets(p, n, &crl->sig_oid, crl->sig_pk, crl->sig_md, + crl->sig_opts); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n"); + MBEDTLS_X509_SAFE_SNPRINTF; + + return (int) (size - n); +} +#endif /* MBEDTLS_X509_REMOVE_INFO */ + +/* + * Initialize a CRL chain + */ +void mbedtls_x509_crl_init(mbedtls_x509_crl *crl) +{ + memset(crl, 0, sizeof(mbedtls_x509_crl)); +} + +/* + * Unallocate all CRL data + */ +void mbedtls_x509_crl_free(mbedtls_x509_crl *crl) +{ + mbedtls_x509_crl *crl_cur = crl; + mbedtls_x509_crl *crl_prv; + mbedtls_x509_crl_entry *entry_cur; + mbedtls_x509_crl_entry *entry_prv; + + while (crl_cur != NULL) { +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + mbedtls_free(crl_cur->sig_opts); +#endif + + mbedtls_asn1_free_named_data_list_shallow(crl_cur->issuer.next); + + entry_cur = crl_cur->entry.next; + while (entry_cur != NULL) { + entry_prv = entry_cur; + entry_cur = entry_cur->next; + mbedtls_platform_zeroize(entry_prv, + sizeof(mbedtls_x509_crl_entry)); + mbedtls_free(entry_prv); + } + + if (crl_cur->raw.p != NULL) { + mbedtls_platform_zeroize(crl_cur->raw.p, crl_cur->raw.len); + mbedtls_free(crl_cur->raw.p); + } + + crl_prv = crl_cur; + crl_cur = crl_cur->next; + + mbedtls_platform_zeroize(crl_prv, sizeof(mbedtls_x509_crl)); + if (crl_prv != crl) { + mbedtls_free(crl_prv); + } + } +} + +#endif /* MBEDTLS_X509_CRL_PARSE_C */ diff --git a/r5dev/thirdparty/mbedtls/x509_crt.c b/r5dev/thirdparty/mbedtls/x509_crt.c new file mode 100644 index 00000000..cf62532f --- /dev/null +++ b/r5dev/thirdparty/mbedtls/x509_crt.c @@ -0,0 +1,2889 @@ +/* + * X.509 certificate parsing and verification + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + * + * [SIRO] https://cabforum.org/wp-content/uploads/Chunghwatelecom201503cabforumV4.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_X509_CRT_PARSE_C) + +#include "mbedtls/x509_crt.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" + +#include + +#if defined(MBEDTLS_PEM_PARSE_C) +#include "mbedtls/pem.h" +#endif + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#include "hash_info.h" + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_THREADING_C) +#include "mbedtls/threading.h" +#endif + +#if defined(MBEDTLS_HAVE_TIME) +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) +#include +#else +#include +#endif +#endif + +#if defined(MBEDTLS_FS_IO) +#include +#if !defined(_WIN32) || defined(EFIX64) || defined(EFI32) +#include +#include +#if defined(__MBED__) +#include +#else +#include +#endif /* __MBED__ */ +#include +#endif /* !_WIN32 || EFIX64 || EFI32 */ +#endif + +/* + * Item in a verification chain: cert and flags for it + */ +typedef struct { + mbedtls_x509_crt *crt; + uint32_t flags; +} x509_crt_verify_chain_item; + +/* + * Max size of verification chain: end-entity + intermediates + trusted root + */ +#define X509_MAX_VERIFY_CHAIN_SIZE (MBEDTLS_X509_MAX_INTERMEDIATE_CA + 2) + +/* Default profile. Do not remove items unless there are serious security + * concerns. */ +const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_default = +{ + /* Hashes from SHA-256 and above. Note that this selection + * should be aligned with ssl_preset_default_hashes in ssl_tls.c. */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA512), + 0xFFFFFFF, /* Any PK alg */ +#if defined(MBEDTLS_ECP_C) + /* Curves at or above 128-bit security level. Note that this selection + * should be aligned with ssl_preset_default_curves in ssl_tls.c. */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP384R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP521R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_BP256R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_BP384R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_BP512R1) | + 0, +#else + 0, +#endif + 2048, +}; + +/* Next-generation profile. Currently identical to the default, but may + * be tightened at any time. */ +const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_next = +{ + /* Hashes from SHA-256 and above. */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA512), + 0xFFFFFFF, /* Any PK alg */ +#if defined(MBEDTLS_ECP_C) + /* Curves at or above 128-bit security level. */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP384R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP521R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_BP256R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_BP384R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_BP512R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256K1), +#else + 0, +#endif + 2048, +}; + +/* + * NSA Suite B Profile + */ +const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb = +{ + /* Only SHA-256 and 384 */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384), + /* Only ECDSA */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECKEY), +#if defined(MBEDTLS_ECP_C) + /* Only NIST P-256 and P-384 */ + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP384R1), +#else + 0, +#endif + 0, +}; + +/* + * Empty / all-forbidden profile + */ +const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_none = +{ + 0, + 0, + 0, + (uint32_t) -1, +}; + +/* + * Check md_alg against profile + * Return 0 if md_alg is acceptable for this profile, -1 otherwise + */ +static int x509_profile_check_md_alg(const mbedtls_x509_crt_profile *profile, + mbedtls_md_type_t md_alg) +{ + if (md_alg == MBEDTLS_MD_NONE) { + return -1; + } + + if ((profile->allowed_mds & MBEDTLS_X509_ID_FLAG(md_alg)) != 0) { + return 0; + } + + return -1; +} + +/* + * Check pk_alg against profile + * Return 0 if pk_alg is acceptable for this profile, -1 otherwise + */ +static int x509_profile_check_pk_alg(const mbedtls_x509_crt_profile *profile, + mbedtls_pk_type_t pk_alg) +{ + if (pk_alg == MBEDTLS_PK_NONE) { + return -1; + } + + if ((profile->allowed_pks & MBEDTLS_X509_ID_FLAG(pk_alg)) != 0) { + return 0; + } + + return -1; +} + +/* + * Check key against profile + * Return 0 if pk is acceptable for this profile, -1 otherwise + */ +static int x509_profile_check_key(const mbedtls_x509_crt_profile *profile, + const mbedtls_pk_context *pk) +{ + const mbedtls_pk_type_t pk_alg = mbedtls_pk_get_type(pk); + +#if defined(MBEDTLS_RSA_C) + if (pk_alg == MBEDTLS_PK_RSA || pk_alg == MBEDTLS_PK_RSASSA_PSS) { + if (mbedtls_pk_get_bitlen(pk) >= profile->rsa_min_bitlen) { + return 0; + } + + return -1; + } +#endif + +#if defined(MBEDTLS_ECP_C) + if (pk_alg == MBEDTLS_PK_ECDSA || + pk_alg == MBEDTLS_PK_ECKEY || + pk_alg == MBEDTLS_PK_ECKEY_DH) { + const mbedtls_ecp_group_id gid = mbedtls_pk_ec(*pk)->grp.id; + + if (gid == MBEDTLS_ECP_DP_NONE) { + return -1; + } + + if ((profile->allowed_curves & MBEDTLS_X509_ID_FLAG(gid)) != 0) { + return 0; + } + + return -1; + } +#endif + + return -1; +} + +/* + * Like memcmp, but case-insensitive and always returns -1 if different + */ +static int x509_memcasecmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + unsigned char diff; + const unsigned char *n1 = s1, *n2 = s2; + + for (i = 0; i < len; i++) { + diff = n1[i] ^ n2[i]; + + if (diff == 0) { + continue; + } + + if (diff == 32 && + ((n1[i] >= 'a' && n1[i] <= 'z') || + (n1[i] >= 'A' && n1[i] <= 'Z'))) { + continue; + } + + return -1; + } + + return 0; +} + +/* + * Return 0 if name matches wildcard, -1 otherwise + */ +static int x509_check_wildcard(const char *cn, const mbedtls_x509_buf *name) +{ + size_t i; + size_t cn_idx = 0, cn_len = strlen(cn); + + /* We can't have a match if there is no wildcard to match */ + if (name->len < 3 || name->p[0] != '*' || name->p[1] != '.') { + return -1; + } + + for (i = 0; i < cn_len; ++i) { + if (cn[i] == '.') { + cn_idx = i; + break; + } + } + + if (cn_idx == 0) { + return -1; + } + + if (cn_len - cn_idx == name->len - 1 && + x509_memcasecmp(name->p + 1, cn + cn_idx, name->len - 1) == 0) { + return 0; + } + + return -1; +} + +/* + * Compare two X.509 strings, case-insensitive, and allowing for some encoding + * variations (but not all). + * + * Return 0 if equal, -1 otherwise. + */ +static int x509_string_cmp(const mbedtls_x509_buf *a, const mbedtls_x509_buf *b) +{ + if (a->tag == b->tag && + a->len == b->len && + memcmp(a->p, b->p, b->len) == 0) { + return 0; + } + + if ((a->tag == MBEDTLS_ASN1_UTF8_STRING || a->tag == MBEDTLS_ASN1_PRINTABLE_STRING) && + (b->tag == MBEDTLS_ASN1_UTF8_STRING || b->tag == MBEDTLS_ASN1_PRINTABLE_STRING) && + a->len == b->len && + x509_memcasecmp(a->p, b->p, b->len) == 0) { + return 0; + } + + return -1; +} + +/* + * Compare two X.509 Names (aka rdnSequence). + * + * See RFC 5280 section 7.1, though we don't implement the whole algorithm: + * we sometimes return unequal when the full algorithm would return equal, + * but never the other way. (In particular, we don't do Unicode normalisation + * or space folding.) + * + * Return 0 if equal, -1 otherwise. + */ +static int x509_name_cmp(const mbedtls_x509_name *a, const mbedtls_x509_name *b) +{ + /* Avoid recursion, it might not be optimised by the compiler */ + while (a != NULL || b != NULL) { + if (a == NULL || b == NULL) { + return -1; + } + + /* type */ + if (a->oid.tag != b->oid.tag || + a->oid.len != b->oid.len || + memcmp(a->oid.p, b->oid.p, b->oid.len) != 0) { + return -1; + } + + /* value */ + if (x509_string_cmp(&a->val, &b->val) != 0) { + return -1; + } + + /* structure of the list of sets */ + if (a->next_merged != b->next_merged) { + return -1; + } + + a = a->next; + b = b->next; + } + + /* a == NULL == b */ + return 0; +} + +/* + * Reset (init or clear) a verify_chain + */ +static void x509_crt_verify_chain_reset( + mbedtls_x509_crt_verify_chain *ver_chain) +{ + size_t i; + + for (i = 0; i < MBEDTLS_X509_MAX_VERIFY_CHAIN_SIZE; i++) { + ver_chain->items[i].crt = NULL; + ver_chain->items[i].flags = (uint32_t) -1; + } + + ver_chain->len = 0; + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + ver_chain->trust_ca_cb_result = NULL; +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ +} + +/* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ +static int x509_get_version(unsigned char **p, + const unsigned char *end, + int *ver) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + 0)) != 0) { + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + *ver = 0; + return 0; + } + + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + end = *p + len; + + if ((ret = mbedtls_asn1_get_int(p, end, ver)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_VERSION, ret); + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_VERSION, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + */ +static int x509_get_dates(unsigned char **p, + const unsigned char *end, + mbedtls_x509_time *from, + mbedtls_x509_time *to) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, ret); + } + + end = *p + len; + + if ((ret = mbedtls_x509_get_time(p, end, from)) != 0) { + return ret; + } + + if ((ret = mbedtls_x509_get_time(p, end, to)) != 0) { + return ret; + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_DATE, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * X.509 v2/v3 unique identifier (not parsed) + */ +static int x509_get_uid(unsigned char **p, + const unsigned char *end, + mbedtls_x509_buf *uid, int n) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if (*p == end) { + return 0; + } + + uid->tag = **p; + + if ((ret = mbedtls_asn1_get_tag(p, end, &uid->len, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | + n)) != 0) { + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + return 0; + } + + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + uid->p = *p; + *p += uid->len; + + return 0; +} + +static int x509_get_basic_constraints(unsigned char **p, + const unsigned char *end, + int *ca_istrue, + int *max_pathlen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + + /* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ + *ca_istrue = 0; /* DEFAULT FALSE */ + *max_pathlen = 0; /* endless */ + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (*p == end) { + return 0; + } + + if ((ret = mbedtls_asn1_get_bool(p, end, ca_istrue)) != 0) { + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + ret = mbedtls_asn1_get_int(p, end, ca_istrue); + } + + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (*ca_istrue != 0) { + *ca_istrue = 1; + } + } + + if (*p == end) { + return 0; + } + + if ((ret = mbedtls_asn1_get_int(p, end, max_pathlen)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* Do not accept max_pathlen equal to INT_MAX to avoid a signed integer + * overflow, which is an undefined behavior. */ + if (*max_pathlen == INT_MAX) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_INVALID_LENGTH); + } + + (*max_pathlen)++; + + return 0; +} + +/* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ +static int x509_get_ext_key_usage(unsigned char **p, + const unsigned char *end, + mbedtls_x509_sequence *ext_key_usage) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_sequence_of(p, end, ext_key_usage, MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + /* Sequence length must be >= 1 */ + if (ext_key_usage->buf.p == NULL) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_INVALID_LENGTH); + } + + return 0; +} + +/* + * id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } + * + * anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 } + * + * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + * + * PolicyInformation ::= SEQUENCE { + * policyIdentifier CertPolicyId, + * policyQualifiers SEQUENCE SIZE (1..MAX) OF + * PolicyQualifierInfo OPTIONAL } + * + * CertPolicyId ::= OBJECT IDENTIFIER + * + * PolicyQualifierInfo ::= SEQUENCE { + * policyQualifierId PolicyQualifierId, + * qualifier ANY DEFINED BY policyQualifierId } + * + * -- policyQualifierIds for Internet policy qualifiers + * + * id-qt OBJECT IDENTIFIER ::= { id-pkix 2 } + * id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 } + * id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 } + * + * PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) + * + * Qualifier ::= CHOICE { + * cPSuri CPSuri, + * userNotice UserNotice } + * + * CPSuri ::= IA5String + * + * UserNotice ::= SEQUENCE { + * noticeRef NoticeReference OPTIONAL, + * explicitText DisplayText OPTIONAL } + * + * NoticeReference ::= SEQUENCE { + * organization DisplayText, + * noticeNumbers SEQUENCE OF INTEGER } + * + * DisplayText ::= CHOICE { + * ia5String IA5String (SIZE (1..200)), + * visibleString VisibleString (SIZE (1..200)), + * bmpString BMPString (SIZE (1..200)), + * utf8String UTF8String (SIZE (1..200)) } + * + * NOTE: we only parse and use anyPolicy without qualifiers at this point + * as defined in RFC 5280. + */ +static int x509_get_certificate_policies(unsigned char **p, + const unsigned char *end, + mbedtls_x509_sequence *certificate_policies) +{ + int ret, parse_ret = 0; + size_t len; + mbedtls_asn1_buf *buf; + mbedtls_asn1_sequence *cur = certificate_policies; + + /* Get main sequence tag */ + ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (*p + len != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* + * Cannot be an empty sequence. + */ + if (len == 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + while (*p < end) { + mbedtls_x509_buf policy_oid; + const unsigned char *policy_end; + + /* + * Get the policy sequence + */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + policy_end = *p + len; + + if ((ret = mbedtls_asn1_get_tag(p, policy_end, &len, + MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + policy_oid.tag = MBEDTLS_ASN1_OID; + policy_oid.len = len; + policy_oid.p = *p; + + /* + * Only AnyPolicy is currently supported when enforcing policy. + */ + if (MBEDTLS_OID_CMP(MBEDTLS_OID_ANY_POLICY, &policy_oid) != 0) { + /* + * Set the parsing return code but continue parsing, in case this + * extension is critical. + */ + parse_ret = MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + } + + /* Allocate and assign next pointer */ + if (cur->buf.p != NULL) { + if (cur->next != NULL) { + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + + cur->next = mbedtls_calloc(1, sizeof(mbedtls_asn1_sequence)); + + if (cur->next == NULL) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_ALLOC_FAILED); + } + + cur = cur->next; + } + + buf = &(cur->buf); + buf->tag = policy_oid.tag; + buf->p = policy_oid.p; + buf->len = policy_oid.len; + + *p += len; + + /* + * If there is an optional qualifier, then *p < policy_end + * Check the Qualifier len to verify it doesn't exceed policy_end. + */ + if (*p < policy_end) { + if ((ret = mbedtls_asn1_get_tag(p, policy_end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != + 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + /* + * Skip the optional policy qualifiers. + */ + *p += len; + } + + if (*p != policy_end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + } + + /* Set final sequence entry's next pointer to NULL */ + cur->next = NULL; + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return parse_ret; +} + +/* + * X.509 v3 extensions + * + */ +static int x509_get_crt_ext(unsigned char **p, + const unsigned char *end, + mbedtls_x509_crt *crt, + mbedtls_x509_crt_ext_cb_t cb, + void *p_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + unsigned char *end_ext_data, *start_ext_octet, *end_ext_octet; + + if (*p == end) { + return 0; + } + + if ((ret = mbedtls_x509_get_ext(p, end, &crt->v3_ext, 3)) != 0) { + return ret; + } + + end = crt->v3_ext.p + crt->v3_ext.len; + while (*p < end) { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + mbedtls_x509_buf extn_oid = { 0, 0, NULL }; + int is_critical = 0; /* DEFAULT FALSE */ + int ext_type = 0; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + end_ext_data = *p + len; + + /* Get extension ID */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &extn_oid.len, + MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + extn_oid.tag = MBEDTLS_ASN1_OID; + extn_oid.p = *p; + *p += extn_oid.len; + + /* Get optional critical */ + if ((ret = mbedtls_asn1_get_bool(p, end_ext_data, &is_critical)) != 0 && + (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + /* Data should be octet string type */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &len, + MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + start_ext_octet = *p; + end_ext_octet = *p + len; + + if (end_ext_octet != end_ext_data) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* + * Detect supported extensions + */ + ret = mbedtls_oid_get_x509_ext_type(&extn_oid, &ext_type); + + if (ret != 0) { + /* Give the callback (if any) a chance to handle the extension */ + if (cb != NULL) { + ret = cb(p_ctx, crt, &extn_oid, is_critical, *p, end_ext_octet); + if (ret != 0 && is_critical) { + return ret; + } + *p = end_ext_octet; + continue; + } + + /* No parser found, skip extension */ + *p = end_ext_octet; + + if (is_critical) { + /* Data is marked as critical: fail */ + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG); + } + continue; + } + + /* Forbid repeated extensions */ + if ((crt->ext_types & ext_type) != 0) { + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + + crt->ext_types |= ext_type; + + switch (ext_type) { + case MBEDTLS_X509_EXT_BASIC_CONSTRAINTS: + /* Parse basic constraints */ + if ((ret = x509_get_basic_constraints(p, end_ext_octet, + &crt->ca_istrue, &crt->max_pathlen)) != 0) { + return ret; + } + break; + + case MBEDTLS_X509_EXT_KEY_USAGE: + /* Parse key usage */ + if ((ret = mbedtls_x509_get_key_usage(p, end_ext_octet, + &crt->key_usage)) != 0) { + return ret; + } + break; + + case MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE: + /* Parse extended key usage */ + if ((ret = x509_get_ext_key_usage(p, end_ext_octet, + &crt->ext_key_usage)) != 0) { + return ret; + } + break; + + case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME: + /* Parse subject alt name */ + if ((ret = mbedtls_x509_get_subject_alt_name(p, end_ext_octet, + &crt->subject_alt_names)) != 0) { + return ret; + } + break; + + case MBEDTLS_X509_EXT_NS_CERT_TYPE: + /* Parse netscape certificate type */ + if ((ret = mbedtls_x509_get_ns_cert_type(p, end_ext_octet, + &crt->ns_cert_type)) != 0) { + return ret; + } + break; + + case MBEDTLS_OID_X509_EXT_CERTIFICATE_POLICIES: + /* Parse certificate policies type */ + if ((ret = x509_get_certificate_policies(p, end_ext_octet, + &crt->certificate_policies)) != 0) { + /* Give the callback (if any) a chance to handle the extension + * if it contains unsupported policies */ + if (ret == MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE && cb != NULL && + cb(p_ctx, crt, &extn_oid, is_critical, + start_ext_octet, end_ext_octet) == 0) { + break; + } + + if (is_critical) { + return ret; + } else + /* + * If MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE is returned, then we + * cannot interpret or enforce the policy. However, it is up to + * the user to choose how to enforce the policies, + * unless the extension is critical. + */ + if (ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE) { + return ret; + } + } + break; + + default: + /* + * If this is a non-critical extension, which the oid layer + * supports, but there isn't an x509 parser for it, + * skip the extension. + */ + if (is_critical) { + return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + } else { + *p = end_ext_octet; + } + } + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Parse and fill a single X.509 certificate in DER format + */ +static int x509_crt_parse_der_core(mbedtls_x509_crt *crt, + const unsigned char *buf, + size_t buflen, + int make_copy, + mbedtls_x509_crt_ext_cb_t cb, + void *p_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + unsigned char *p, *end, *crt_end; + mbedtls_x509_buf sig_params1, sig_params2, sig_oid2; + + memset(&sig_params1, 0, sizeof(mbedtls_x509_buf)); + memset(&sig_params2, 0, sizeof(mbedtls_x509_buf)); + memset(&sig_oid2, 0, sizeof(mbedtls_x509_buf)); + + /* + * Check for valid input + */ + if (crt == NULL || buf == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + /* Use the original buffer until we figure out actual length. */ + p = (unsigned char *) buf; + len = buflen; + end = p + len; + + /* + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERR_X509_INVALID_FORMAT; + } + + end = crt_end = p + len; + crt->raw.len = crt_end - buf; + if (make_copy != 0) { + /* Create and populate a new buffer for the raw field. */ + crt->raw.p = p = mbedtls_calloc(1, crt->raw.len); + if (crt->raw.p == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + memcpy(crt->raw.p, buf, crt->raw.len); + crt->own_buffer = 1; + + p += crt->raw.len - len; + end = crt_end = p + len; + } else { + crt->raw.p = (unsigned char *) buf; + crt->own_buffer = 0; + } + + /* + * TBSCertificate ::= SEQUENCE { + */ + crt->tbs.p = p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + end = p + len; + crt->tbs.len = end - crt->tbs.p; + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * + * CertificateSerialNumber ::= INTEGER + * + * signature AlgorithmIdentifier + */ + if ((ret = x509_get_version(&p, end, &crt->version)) != 0 || + (ret = mbedtls_x509_get_serial(&p, end, &crt->serial)) != 0 || + (ret = mbedtls_x509_get_alg(&p, end, &crt->sig_oid, + &sig_params1)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + + if (crt->version < 0 || crt->version > 2) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERR_X509_UNKNOWN_VERSION; + } + + crt->version++; + + if ((ret = mbedtls_x509_get_sig_alg(&crt->sig_oid, &sig_params1, + &crt->sig_md, &crt->sig_pk, + &crt->sig_opts)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + + /* + * issuer Name + */ + crt->issuer_raw.p = p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + if ((ret = mbedtls_x509_get_name(&p, p + len, &crt->issuer)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + + crt->issuer_raw.len = p - crt->issuer_raw.p; + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + */ + if ((ret = x509_get_dates(&p, end, &crt->valid_from, + &crt->valid_to)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + + /* + * subject Name + */ + crt->subject_raw.p = p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + if (len && (ret = mbedtls_x509_get_name(&p, p + len, &crt->subject)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + + crt->subject_raw.len = p - crt->subject_raw.p; + + /* + * SubjectPublicKeyInfo + */ + crt->pk_raw.p = p; + if ((ret = mbedtls_pk_parse_subpubkey(&p, end, &crt->pk)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + crt->pk_raw.len = p - crt->pk_raw.p; + + /* + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version shall be v3 + */ + if (crt->version == 2 || crt->version == 3) { + ret = x509_get_uid(&p, end, &crt->issuer_id, 1); + if (ret != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + } + + if (crt->version == 2 || crt->version == 3) { + ret = x509_get_uid(&p, end, &crt->subject_id, 2); + if (ret != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + } + + if (crt->version == 3) { + ret = x509_get_crt_ext(&p, end, crt, cb, p_ctx); + if (ret != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + } + + if (p != end) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + end = crt_end; + + /* + * } + * -- end of TBSCertificate + * + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + */ + if ((ret = mbedtls_x509_get_alg(&p, end, &sig_oid2, &sig_params2)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + + if (crt->sig_oid.len != sig_oid2.len || + memcmp(crt->sig_oid.p, sig_oid2.p, crt->sig_oid.len) != 0 || + sig_params1.tag != sig_params2.tag || + sig_params1.len != sig_params2.len || + (sig_params1.len != 0 && + memcmp(sig_params1.p, sig_params2.p, sig_params1.len) != 0)) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERR_X509_SIG_MISMATCH; + } + + if ((ret = mbedtls_x509_get_sig(&p, end, &crt->sig)) != 0) { + mbedtls_x509_crt_free(crt); + return ret; + } + + if (p != end) { + mbedtls_x509_crt_free(crt); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Parse one X.509 certificate in DER format from a buffer and add them to a + * chained list + */ +static int mbedtls_x509_crt_parse_der_internal(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen, + int make_copy, + mbedtls_x509_crt_ext_cb_t cb, + void *p_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_x509_crt *crt = chain, *prev = NULL; + + /* + * Check for valid input + */ + if (crt == NULL || buf == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + while (crt->version != 0 && crt->next != NULL) { + prev = crt; + crt = crt->next; + } + + /* + * Add new certificate on the end of the chain if needed. + */ + if (crt->version != 0 && crt->next == NULL) { + crt->next = mbedtls_calloc(1, sizeof(mbedtls_x509_crt)); + + if (crt->next == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + prev = crt; + mbedtls_x509_crt_init(crt->next); + crt = crt->next; + } + + ret = x509_crt_parse_der_core(crt, buf, buflen, make_copy, cb, p_ctx); + if (ret != 0) { + if (prev) { + prev->next = NULL; + } + + if (crt != chain) { + mbedtls_free(crt); + } + + return ret; + } + + return 0; +} + +int mbedtls_x509_crt_parse_der_nocopy(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen) +{ + return mbedtls_x509_crt_parse_der_internal(chain, buf, buflen, 0, NULL, NULL); +} + +int mbedtls_x509_crt_parse_der_with_ext_cb(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen, + int make_copy, + mbedtls_x509_crt_ext_cb_t cb, + void *p_ctx) +{ + return mbedtls_x509_crt_parse_der_internal(chain, buf, buflen, make_copy, cb, p_ctx); +} + +int mbedtls_x509_crt_parse_der(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen) +{ + return mbedtls_x509_crt_parse_der_internal(chain, buf, buflen, 1, NULL, NULL); +} + +/* + * Parse one or more PEM certificates from a buffer and add them to the chained + * list + */ +int mbedtls_x509_crt_parse(mbedtls_x509_crt *chain, + const unsigned char *buf, + size_t buflen) +{ +#if defined(MBEDTLS_PEM_PARSE_C) + int success = 0, first_error = 0, total_failed = 0; + int buf_format = MBEDTLS_X509_FORMAT_DER; +#endif + + /* + * Check for valid input + */ + if (chain == NULL || buf == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + /* + * Determine buffer content. Buffer contains either one DER certificate or + * one or more PEM certificates. + */ +#if defined(MBEDTLS_PEM_PARSE_C) + if (buflen != 0 && buf[buflen - 1] == '\0' && + strstr((const char *) buf, "-----BEGIN CERTIFICATE-----") != NULL) { + buf_format = MBEDTLS_X509_FORMAT_PEM; + } + + if (buf_format == MBEDTLS_X509_FORMAT_DER) { + return mbedtls_x509_crt_parse_der(chain, buf, buflen); + } +#else + return mbedtls_x509_crt_parse_der(chain, buf, buflen); +#endif + +#if defined(MBEDTLS_PEM_PARSE_C) + if (buf_format == MBEDTLS_X509_FORMAT_PEM) { + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_pem_context pem; + + /* 1 rather than 0 since the terminating NULL byte is counted in */ + while (buflen > 1) { + size_t use_len; + mbedtls_pem_init(&pem); + + /* If we get there, we know the string is null-terminated */ + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN CERTIFICATE-----", + "-----END CERTIFICATE-----", + buf, NULL, 0, &use_len); + + if (ret == 0) { + /* + * Was PEM encoded + */ + buflen -= use_len; + buf += use_len; + } else if (ret == MBEDTLS_ERR_PEM_BAD_INPUT_DATA) { + return ret; + } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + mbedtls_pem_free(&pem); + + /* + * PEM header and footer were found + */ + buflen -= use_len; + buf += use_len; + + if (first_error == 0) { + first_error = ret; + } + + total_failed++; + continue; + } else { + break; + } + + ret = mbedtls_x509_crt_parse_der(chain, pem.buf, pem.buflen); + + mbedtls_pem_free(&pem); + + if (ret != 0) { + /* + * Quit parsing on a memory error + */ + if (ret == MBEDTLS_ERR_X509_ALLOC_FAILED) { + return ret; + } + + if (first_error == 0) { + first_error = ret; + } + + total_failed++; + continue; + } + + success = 1; + } + } + + if (success) { + return total_failed; + } else if (first_error) { + return first_error; + } else { + return MBEDTLS_ERR_X509_CERT_UNKNOWN_FORMAT; + } +#endif /* MBEDTLS_PEM_PARSE_C */ +} + +#if defined(MBEDTLS_FS_IO) +/* + * Load one or more certificates and add them to the chained list + */ +int mbedtls_x509_crt_parse_file(mbedtls_x509_crt *chain, const char *path) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + unsigned char *buf; + + if ((ret = mbedtls_pk_load_file(path, &buf, &n)) != 0) { + return ret; + } + + ret = mbedtls_x509_crt_parse(chain, buf, n); + + mbedtls_platform_zeroize(buf, n); + mbedtls_free(buf); + + return ret; +} + +int mbedtls_x509_crt_parse_path(mbedtls_x509_crt *chain, const char *path) +{ + int ret = 0; +#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) + int w_ret; + WCHAR szDir[MAX_PATH]; + char filename[MAX_PATH]; + char *p; + size_t len = strlen(path); + + WIN32_FIND_DATAW file_data; + HANDLE hFind; + + if (len > MAX_PATH - 3) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + memset(szDir, 0, sizeof(szDir)); + memset(filename, 0, MAX_PATH); + memcpy(filename, path, len); + filename[len++] = '\\'; + p = filename + len; + filename[len++] = '*'; + + w_ret = MultiByteToWideChar(CP_ACP, 0, filename, (int) len, szDir, + MAX_PATH - 3); + if (w_ret == 0) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + hFind = FindFirstFileW(szDir, &file_data); + if (hFind == INVALID_HANDLE_VALUE) { + return MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + + len = MAX_PATH - len; + do { + memset(p, 0, len); + + if (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + continue; + } + + w_ret = WideCharToMultiByte(CP_ACP, 0, file_data.cFileName, + -1, + p, (int) len, + NULL, NULL); + if (w_ret == 0) { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + goto cleanup; + } + + w_ret = mbedtls_x509_crt_parse_file(chain, filename); + if (w_ret < 0) { + ret++; + } else { + ret += w_ret; + } + } while (FindNextFileW(hFind, &file_data) != 0); + + if (GetLastError() != ERROR_NO_MORE_FILES) { + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + +cleanup: + FindClose(hFind); +#else /* _WIN32 */ + int t_ret; + int snp_ret; + struct stat sb; + struct dirent *entry; + char entry_name[MBEDTLS_X509_MAX_FILE_PATH_LEN]; + DIR *dir = opendir(path); + + if (dir == NULL) { + return MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + +#if defined(MBEDTLS_THREADING_C) + if ((ret = mbedtls_mutex_lock(&mbedtls_threading_readdir_mutex)) != 0) { + closedir(dir); + return ret; + } +#endif /* MBEDTLS_THREADING_C */ + + memset(&sb, 0, sizeof(sb)); + + while ((entry = readdir(dir)) != NULL) { + snp_ret = mbedtls_snprintf(entry_name, sizeof(entry_name), + "%s/%s", path, entry->d_name); + + if (snp_ret < 0 || (size_t) snp_ret >= sizeof(entry_name)) { + ret = MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; + goto cleanup; + } else if (stat(entry_name, &sb) == -1) { + if (errno == ENOENT) { + /* Broken symbolic link - ignore this entry. + stat(2) will return this error for either (a) a dangling + symlink or (b) a missing file. + Given that we have just obtained the filename from readdir, + assume that it does exist and therefore treat this as a + dangling symlink. */ + continue; + } else { + /* Some other file error; report the error. */ + ret = MBEDTLS_ERR_X509_FILE_IO_ERROR; + goto cleanup; + } + } + + if (!S_ISREG(sb.st_mode)) { + continue; + } + + // Ignore parse errors + // + t_ret = mbedtls_x509_crt_parse_file(chain, entry_name); + if (t_ret < 0) { + ret++; + } else { + ret += t_ret; + } + } + +cleanup: + closedir(dir); + +#if defined(MBEDTLS_THREADING_C) + if (mbedtls_mutex_unlock(&mbedtls_threading_readdir_mutex) != 0) { + ret = MBEDTLS_ERR_THREADING_MUTEX_ERROR; + } +#endif /* MBEDTLS_THREADING_C */ + +#endif /* _WIN32 */ + + return ret; +} +#endif /* MBEDTLS_FS_IO */ + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +static int x509_info_ext_key_usage(char **buf, size_t *size, + const mbedtls_x509_sequence *extended_key_usage) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const char *desc; + size_t n = *size; + char *p = *buf; + const mbedtls_x509_sequence *cur = extended_key_usage; + const char *sep = ""; + + while (cur != NULL) { + if (mbedtls_oid_get_extended_key_usage(&cur->buf, &desc) != 0) { + desc = "???"; + } + + ret = mbedtls_snprintf(p, n, "%s%s", sep, desc); + MBEDTLS_X509_SAFE_SNPRINTF; + + sep = ", "; + + cur = cur->next; + } + + *size = n; + *buf = p; + + return 0; +} + +static int x509_info_cert_policies(char **buf, size_t *size, + const mbedtls_x509_sequence *certificate_policies) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const char *desc; + size_t n = *size; + char *p = *buf; + const mbedtls_x509_sequence *cur = certificate_policies; + const char *sep = ""; + + while (cur != NULL) { + if (mbedtls_oid_get_certificate_policies(&cur->buf, &desc) != 0) { + desc = "???"; + } + + ret = mbedtls_snprintf(p, n, "%s%s", sep, desc); + MBEDTLS_X509_SAFE_SNPRINTF; + + sep = ", "; + + cur = cur->next; + } + + *size = n; + *buf = p; + + return 0; +} + +/* + * Return an informational string about the certificate. + */ +#define BEFORE_COLON 18 +#define BC "18" +int mbedtls_x509_crt_info(char *buf, size_t size, const char *prefix, + const mbedtls_x509_crt *crt) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + char *p; + char key_size_str[BEFORE_COLON]; + + p = buf; + n = size; + + if (NULL == crt) { + ret = mbedtls_snprintf(p, n, "\nCertificate is uninitialised!\n"); + MBEDTLS_X509_SAFE_SNPRINTF; + + return (int) (size - n); + } + + ret = mbedtls_snprintf(p, n, "%scert. version : %d\n", + prefix, crt->version); + MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_snprintf(p, n, "%sserial number : ", + prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_x509_serial_gets(p, n, &crt->serial); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%sissuer name : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_dn_gets(p, n, &crt->issuer); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%ssubject name : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_dn_gets(p, n, &crt->subject); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%sissued on : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crt->valid_from.year, crt->valid_from.mon, + crt->valid_from.day, crt->valid_from.hour, + crt->valid_from.min, crt->valid_from.sec); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%sexpires on : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crt->valid_to.year, crt->valid_to.mon, + crt->valid_to.day, crt->valid_to.hour, + crt->valid_to.min, crt->valid_to.sec); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%ssigned using : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_x509_sig_alg_gets(p, n, &crt->sig_oid, crt->sig_pk, + crt->sig_md, crt->sig_opts); + MBEDTLS_X509_SAFE_SNPRINTF; + + /* Key size */ + if ((ret = mbedtls_x509_key_size_helper(key_size_str, BEFORE_COLON, + mbedtls_pk_get_name(&crt->pk))) != 0) { + return ret; + } + + ret = mbedtls_snprintf(p, n, "\n%s%-" BC "s: %d bits", prefix, key_size_str, + (int) mbedtls_pk_get_bitlen(&crt->pk)); + MBEDTLS_X509_SAFE_SNPRINTF; + + /* + * Optional extensions + */ + + if (crt->ext_types & MBEDTLS_X509_EXT_BASIC_CONSTRAINTS) { + ret = mbedtls_snprintf(p, n, "\n%sbasic constraints : CA=%s", prefix, + crt->ca_istrue ? "true" : "false"); + MBEDTLS_X509_SAFE_SNPRINTF; + + if (crt->max_pathlen > 0) { + ret = mbedtls_snprintf(p, n, ", max_pathlen=%d", crt->max_pathlen - 1); + MBEDTLS_X509_SAFE_SNPRINTF; + } + } + + if (crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) { + ret = mbedtls_snprintf(p, n, "\n%ssubject alt name :", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = mbedtls_x509_info_subject_alt_name(&p, &n, + &crt->subject_alt_names, + prefix)) != 0) { + return ret; + } + } + + if (crt->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE) { + ret = mbedtls_snprintf(p, n, "\n%scert. type : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = mbedtls_x509_info_cert_type(&p, &n, crt->ns_cert_type)) != 0) { + return ret; + } + } + + if (crt->ext_types & MBEDTLS_X509_EXT_KEY_USAGE) { + ret = mbedtls_snprintf(p, n, "\n%skey usage : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = mbedtls_x509_info_key_usage(&p, &n, crt->key_usage)) != 0) { + return ret; + } + } + + if (crt->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE) { + ret = mbedtls_snprintf(p, n, "\n%sext key usage : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = x509_info_ext_key_usage(&p, &n, + &crt->ext_key_usage)) != 0) { + return ret; + } + } + + if (crt->ext_types & MBEDTLS_OID_X509_EXT_CERTIFICATE_POLICIES) { + ret = mbedtls_snprintf(p, n, "\n%scertificate policies : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = x509_info_cert_policies(&p, &n, + &crt->certificate_policies)) != 0) { + return ret; + } + } + + ret = mbedtls_snprintf(p, n, "\n"); + MBEDTLS_X509_SAFE_SNPRINTF; + + return (int) (size - n); +} + +struct x509_crt_verify_string { + int code; + const char *string; +}; + +#define X509_CRT_ERROR_INFO(err, err_str, info) { err, info }, +static const struct x509_crt_verify_string x509_crt_verify_strings[] = { + MBEDTLS_X509_CRT_ERROR_INFO_LIST + { 0, NULL } +}; +#undef X509_CRT_ERROR_INFO + +int mbedtls_x509_crt_verify_info(char *buf, size_t size, const char *prefix, + uint32_t flags) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const struct x509_crt_verify_string *cur; + char *p = buf; + size_t n = size; + + for (cur = x509_crt_verify_strings; cur->string != NULL; cur++) { + if ((flags & cur->code) == 0) { + continue; + } + + ret = mbedtls_snprintf(p, n, "%s%s\n", prefix, cur->string); + MBEDTLS_X509_SAFE_SNPRINTF; + flags ^= cur->code; + } + + if (flags != 0) { + ret = mbedtls_snprintf(p, n, "%sUnknown reason " + "(this should not happen)\n", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + } + + return (int) (size - n); +} +#endif /* MBEDTLS_X509_REMOVE_INFO */ + +int mbedtls_x509_crt_check_key_usage(const mbedtls_x509_crt *crt, + unsigned int usage) +{ + unsigned int usage_must, usage_may; + unsigned int may_mask = MBEDTLS_X509_KU_ENCIPHER_ONLY + | MBEDTLS_X509_KU_DECIPHER_ONLY; + + if ((crt->ext_types & MBEDTLS_X509_EXT_KEY_USAGE) == 0) { + return 0; + } + + usage_must = usage & ~may_mask; + + if (((crt->key_usage & ~may_mask) & usage_must) != usage_must) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + usage_may = usage & may_mask; + + if (((crt->key_usage & may_mask) | usage_may) != usage_may) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + return 0; +} + +int mbedtls_x509_crt_check_extended_key_usage(const mbedtls_x509_crt *crt, + const char *usage_oid, + size_t usage_len) +{ + const mbedtls_x509_sequence *cur; + + /* Extension is not mandatory, absent means no restriction */ + if ((crt->ext_types & MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE) == 0) { + return 0; + } + + /* + * Look for the requested usage (or wildcard ANY) in our list + */ + for (cur = &crt->ext_key_usage; cur != NULL; cur = cur->next) { + const mbedtls_x509_buf *cur_oid = &cur->buf; + + if (cur_oid->len == usage_len && + memcmp(cur_oid->p, usage_oid, usage_len) == 0) { + return 0; + } + + if (MBEDTLS_OID_CMP(MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE, cur_oid) == 0) { + return 0; + } + } + + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; +} + +#if defined(MBEDTLS_X509_CRL_PARSE_C) +/* + * Return 1 if the certificate is revoked, or 0 otherwise. + */ +int mbedtls_x509_crt_is_revoked(const mbedtls_x509_crt *crt, const mbedtls_x509_crl *crl) +{ + const mbedtls_x509_crl_entry *cur = &crl->entry; + + while (cur != NULL && cur->serial.len != 0) { + if (crt->serial.len == cur->serial.len && + memcmp(crt->serial.p, cur->serial.p, crt->serial.len) == 0) { + return 1; + } + + cur = cur->next; + } + + return 0; +} + +/* + * Check that the given certificate is not revoked according to the CRL. + * Skip validation if no CRL for the given CA is present. + */ +static int x509_crt_verifycrl(mbedtls_x509_crt *crt, mbedtls_x509_crt *ca, + mbedtls_x509_crl *crl_list, + const mbedtls_x509_crt_profile *profile) +{ + int flags = 0; + unsigned char hash[MBEDTLS_HASH_MAX_SIZE]; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_algorithm_t psa_algorithm; +#else + const mbedtls_md_info_t *md_info; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + size_t hash_length; + + if (ca == NULL) { + return flags; + } + + while (crl_list != NULL) { + if (crl_list->version == 0 || + x509_name_cmp(&crl_list->issuer, &ca->subject) != 0) { + crl_list = crl_list->next; + continue; + } + + /* + * Check if the CA is configured to sign CRLs + */ + if (mbedtls_x509_crt_check_key_usage(ca, + MBEDTLS_X509_KU_CRL_SIGN) != 0) { + flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED; + break; + } + + /* + * Check if CRL is correctly signed by the trusted CA + */ + if (x509_profile_check_md_alg(profile, crl_list->sig_md) != 0) { + flags |= MBEDTLS_X509_BADCRL_BAD_MD; + } + + if (x509_profile_check_pk_alg(profile, crl_list->sig_pk) != 0) { + flags |= MBEDTLS_X509_BADCRL_BAD_PK; + } + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_algorithm = mbedtls_hash_info_psa_from_md(crl_list->sig_md); + if (psa_hash_compute(psa_algorithm, + crl_list->tbs.p, + crl_list->tbs.len, + hash, + sizeof(hash), + &hash_length) != PSA_SUCCESS) { + /* Note: this can't happen except after an internal error */ + flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED; + break; + } +#else + md_info = mbedtls_md_info_from_type(crl_list->sig_md); + hash_length = mbedtls_md_get_size(md_info); + if (mbedtls_md(md_info, + crl_list->tbs.p, + crl_list->tbs.len, + hash) != 0) { + /* Note: this can't happen except after an internal error */ + flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED; + break; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + if (x509_profile_check_key(profile, &ca->pk) != 0) { + flags |= MBEDTLS_X509_BADCERT_BAD_KEY; + } + + if (mbedtls_pk_verify_ext(crl_list->sig_pk, crl_list->sig_opts, &ca->pk, + crl_list->sig_md, hash, hash_length, + crl_list->sig.p, crl_list->sig.len) != 0) { + flags |= MBEDTLS_X509_BADCRL_NOT_TRUSTED; + break; + } + + /* + * Check for validity of CRL (Do not drop out) + */ + if (mbedtls_x509_time_is_past(&crl_list->next_update)) { + flags |= MBEDTLS_X509_BADCRL_EXPIRED; + } + + if (mbedtls_x509_time_is_future(&crl_list->this_update)) { + flags |= MBEDTLS_X509_BADCRL_FUTURE; + } + + /* + * Check if certificate is revoked + */ + if (mbedtls_x509_crt_is_revoked(crt, crl_list)) { + flags |= MBEDTLS_X509_BADCERT_REVOKED; + break; + } + + crl_list = crl_list->next; + } + + return flags; +} +#endif /* MBEDTLS_X509_CRL_PARSE_C */ + +/* + * Check the signature of a certificate by its parent + */ +static int x509_crt_check_signature(const mbedtls_x509_crt *child, + mbedtls_x509_crt *parent, + mbedtls_x509_crt_restart_ctx *rs_ctx) +{ + size_t hash_len; + unsigned char hash[MBEDTLS_HASH_MAX_SIZE]; +#if !defined(MBEDTLS_USE_PSA_CRYPTO) + const mbedtls_md_info_t *md_info; + md_info = mbedtls_md_info_from_type(child->sig_md); + hash_len = mbedtls_md_get_size(md_info); + + /* Note: hash errors can happen only after an internal error */ + if (mbedtls_md(md_info, child->tbs.p, child->tbs.len, hash) != 0) { + return -1; + } +#else + psa_algorithm_t hash_alg = mbedtls_hash_info_psa_from_md(child->sig_md); + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + + status = psa_hash_compute(hash_alg, + child->tbs.p, + child->tbs.len, + hash, + sizeof(hash), + &hash_len); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } + +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + /* Skip expensive computation on obvious mismatch */ + if (!mbedtls_pk_can_do(&parent->pk, child->sig_pk)) { + return -1; + } + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && child->sig_pk == MBEDTLS_PK_ECDSA) { + return mbedtls_pk_verify_restartable(&parent->pk, + child->sig_md, hash, hash_len, + child->sig.p, child->sig.len, &rs_ctx->pk); + } +#else + (void) rs_ctx; +#endif + + return mbedtls_pk_verify_ext(child->sig_pk, child->sig_opts, &parent->pk, + child->sig_md, hash, hash_len, + child->sig.p, child->sig.len); +} + +/* + * Check if 'parent' is a suitable parent (signing CA) for 'child'. + * Return 0 if yes, -1 if not. + * + * top means parent is a locally-trusted certificate + */ +static int x509_crt_check_parent(const mbedtls_x509_crt *child, + const mbedtls_x509_crt *parent, + int top) +{ + int need_ca_bit; + + /* Parent must be the issuer */ + if (x509_name_cmp(&child->issuer, &parent->subject) != 0) { + return -1; + } + + /* Parent must have the basicConstraints CA bit set as a general rule */ + need_ca_bit = 1; + + /* Exception: v1/v2 certificates that are locally trusted. */ + if (top && parent->version < 3) { + need_ca_bit = 0; + } + + if (need_ca_bit && !parent->ca_istrue) { + return -1; + } + + if (need_ca_bit && + mbedtls_x509_crt_check_key_usage(parent, MBEDTLS_X509_KU_KEY_CERT_SIGN) != 0) { + return -1; + } + + return 0; +} + +/* + * Find a suitable parent for child in candidates, or return NULL. + * + * Here suitable is defined as: + * 1. subject name matches child's issuer + * 2. if necessary, the CA bit is set and key usage allows signing certs + * 3. for trusted roots, the signature is correct + * (for intermediates, the signature is checked and the result reported) + * 4. pathlen constraints are satisfied + * + * If there's a suitable candidate which is also time-valid, return the first + * such. Otherwise, return the first suitable candidate (or NULL if there is + * none). + * + * The rationale for this rule is that someone could have a list of trusted + * roots with two versions on the same root with different validity periods. + * (At least one user reported having such a list and wanted it to just work.) + * The reason we don't just require time-validity is that generally there is + * only one version, and if it's expired we want the flags to state that + * rather than NOT_TRUSTED, as would be the case if we required it here. + * + * The rationale for rule 3 (signature for trusted roots) is that users might + * have two versions of the same CA with different keys in their list, and the + * way we select the correct one is by checking the signature (as we don't + * rely on key identifier extensions). (This is one way users might choose to + * handle key rollover, another relies on self-issued certs, see [SIRO].) + * + * Arguments: + * - [in] child: certificate for which we're looking for a parent + * - [in] candidates: chained list of potential parents + * - [out] r_parent: parent found (or NULL) + * - [out] r_signature_is_good: 1 if child signature by parent is valid, or 0 + * - [in] top: 1 if candidates consists of trusted roots, ie we're at the top + * of the chain, 0 otherwise + * - [in] path_cnt: number of intermediates seen so far + * - [in] self_cnt: number of self-signed intermediates seen so far + * (will never be greater than path_cnt) + * - [in-out] rs_ctx: context for restarting operations + * + * Return value: + * - 0 on success + * - MBEDTLS_ERR_ECP_IN_PROGRESS otherwise + */ +static int x509_crt_find_parent_in( + mbedtls_x509_crt *child, + mbedtls_x509_crt *candidates, + mbedtls_x509_crt **r_parent, + int *r_signature_is_good, + int top, + unsigned path_cnt, + unsigned self_cnt, + mbedtls_x509_crt_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_x509_crt *parent, *fallback_parent; + int signature_is_good = 0, fallback_signature_is_good; + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + /* did we have something in progress? */ + if (rs_ctx != NULL && rs_ctx->parent != NULL) { + /* restore saved state */ + parent = rs_ctx->parent; + fallback_parent = rs_ctx->fallback_parent; + fallback_signature_is_good = rs_ctx->fallback_signature_is_good; + + /* clear saved state */ + rs_ctx->parent = NULL; + rs_ctx->fallback_parent = NULL; + rs_ctx->fallback_signature_is_good = 0; + + /* resume where we left */ + goto check_signature; + } +#endif + + fallback_parent = NULL; + fallback_signature_is_good = 0; + + for (parent = candidates; parent != NULL; parent = parent->next) { + /* basic parenting skills (name, CA bit, key usage) */ + if (x509_crt_check_parent(child, parent, top) != 0) { + continue; + } + + /* +1 because stored max_pathlen is 1 higher that the actual value */ + if (parent->max_pathlen > 0 && + (size_t) parent->max_pathlen < 1 + path_cnt - self_cnt) { + continue; + } + + /* Signature */ +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +check_signature: +#endif + ret = x509_crt_check_signature(child, parent, rs_ctx); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + /* save state */ + rs_ctx->parent = parent; + rs_ctx->fallback_parent = fallback_parent; + rs_ctx->fallback_signature_is_good = fallback_signature_is_good; + + return ret; + } +#else + (void) ret; +#endif + + signature_is_good = ret == 0; + if (top && !signature_is_good) { + continue; + } + + /* optional time check */ + if (mbedtls_x509_time_is_past(&parent->valid_to) || + mbedtls_x509_time_is_future(&parent->valid_from)) { + if (fallback_parent == NULL) { + fallback_parent = parent; + fallback_signature_is_good = signature_is_good; + } + + continue; + } + + *r_parent = parent; + *r_signature_is_good = signature_is_good; + + break; + } + + if (parent == NULL) { + *r_parent = fallback_parent; + *r_signature_is_good = fallback_signature_is_good; + } + + return 0; +} + +/* + * Find a parent in trusted CAs or the provided chain, or return NULL. + * + * Searches in trusted CAs first, and return the first suitable parent found + * (see find_parent_in() for definition of suitable). + * + * Arguments: + * - [in] child: certificate for which we're looking for a parent, followed + * by a chain of possible intermediates + * - [in] trust_ca: list of locally trusted certificates + * - [out] parent: parent found (or NULL) + * - [out] parent_is_trusted: 1 if returned `parent` is trusted, or 0 + * - [out] signature_is_good: 1 if child signature by parent is valid, or 0 + * - [in] path_cnt: number of links in the chain so far (EE -> ... -> child) + * - [in] self_cnt: number of self-signed certs in the chain so far + * (will always be no greater than path_cnt) + * - [in-out] rs_ctx: context for restarting operations + * + * Return value: + * - 0 on success + * - MBEDTLS_ERR_ECP_IN_PROGRESS otherwise + */ +static int x509_crt_find_parent( + mbedtls_x509_crt *child, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crt **parent, + int *parent_is_trusted, + int *signature_is_good, + unsigned path_cnt, + unsigned self_cnt, + mbedtls_x509_crt_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_x509_crt *search_list; + + *parent_is_trusted = 1; + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + /* restore then clear saved state if we have some stored */ + if (rs_ctx != NULL && rs_ctx->parent_is_trusted != -1) { + *parent_is_trusted = rs_ctx->parent_is_trusted; + rs_ctx->parent_is_trusted = -1; + } +#endif + + while (1) { + search_list = *parent_is_trusted ? trust_ca : child->next; + + ret = x509_crt_find_parent_in(child, search_list, + parent, signature_is_good, + *parent_is_trusted, + path_cnt, self_cnt, rs_ctx); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + /* save state */ + rs_ctx->parent_is_trusted = *parent_is_trusted; + return ret; + } +#else + (void) ret; +#endif + + /* stop here if found or already in second iteration */ + if (*parent != NULL || *parent_is_trusted == 0) { + break; + } + + /* prepare second iteration */ + *parent_is_trusted = 0; + } + + /* extra precaution against mistakes in the caller */ + if (*parent == NULL) { + *parent_is_trusted = 0; + *signature_is_good = 0; + } + + return 0; +} + +/* + * Check if an end-entity certificate is locally trusted + * + * Currently we require such certificates to be self-signed (actually only + * check for self-issued as self-signatures are not checked) + */ +static int x509_crt_check_ee_locally_trusted( + mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca) +{ + mbedtls_x509_crt *cur; + + /* must be self-issued */ + if (x509_name_cmp(&crt->issuer, &crt->subject) != 0) { + return -1; + } + + /* look for an exact match with trusted cert */ + for (cur = trust_ca; cur != NULL; cur = cur->next) { + if (crt->raw.len == cur->raw.len && + memcmp(crt->raw.p, cur->raw.p, crt->raw.len) == 0) { + return 0; + } + } + + /* too bad */ + return -1; +} + +/* + * Build and verify a certificate chain + * + * Given a peer-provided list of certificates EE, C1, ..., Cn and + * a list of trusted certs R1, ... Rp, try to build and verify a chain + * EE, Ci1, ... Ciq [, Rj] + * such that every cert in the chain is a child of the next one, + * jumping to a trusted root as early as possible. + * + * Verify that chain and return it with flags for all issues found. + * + * Special cases: + * - EE == Rj -> return a one-element list containing it + * - EE, Ci1, ..., Ciq cannot be continued with a trusted root + * -> return that chain with NOT_TRUSTED set on Ciq + * + * Tests for (aspects of) this function should include at least: + * - trusted EE + * - EE -> trusted root + * - EE -> intermediate CA -> trusted root + * - if relevant: EE untrusted + * - if relevant: EE -> intermediate, untrusted + * with the aspect under test checked at each relevant level (EE, int, root). + * For some aspects longer chains are required, but usually length 2 is + * enough (but length 1 is not in general). + * + * Arguments: + * - [in] crt: the cert list EE, C1, ..., Cn + * - [in] trust_ca: the trusted list R1, ..., Rp + * - [in] ca_crl, profile: as in verify_with_profile() + * - [out] ver_chain: the built and verified chain + * Only valid when return value is 0, may contain garbage otherwise! + * Restart note: need not be the same when calling again to resume. + * - [in-out] rs_ctx: context for restarting operations + * + * Return value: + * - non-zero if the chain could not be fully built and examined + * - 0 is the chain was successfully built and examined, + * even if it was found to be invalid + */ +static int x509_crt_verify_chain( + mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + mbedtls_x509_crt_ca_cb_t f_ca_cb, + void *p_ca_cb, + const mbedtls_x509_crt_profile *profile, + mbedtls_x509_crt_verify_chain *ver_chain, + mbedtls_x509_crt_restart_ctx *rs_ctx) +{ + /* Don't initialize any of those variables here, so that the compiler can + * catch potential issues with jumping ahead when restarting */ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + uint32_t *flags; + mbedtls_x509_crt_verify_chain_item *cur; + mbedtls_x509_crt *child; + mbedtls_x509_crt *parent; + int parent_is_trusted; + int child_is_trusted; + int signature_is_good; + unsigned self_cnt; + mbedtls_x509_crt *cur_trust_ca = NULL; + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + /* resume if we had an operation in progress */ + if (rs_ctx != NULL && rs_ctx->in_progress == x509_crt_rs_find_parent) { + /* restore saved state */ + *ver_chain = rs_ctx->ver_chain; /* struct copy */ + self_cnt = rs_ctx->self_cnt; + + /* restore derived state */ + cur = &ver_chain->items[ver_chain->len - 1]; + child = cur->crt; + flags = &cur->flags; + + goto find_parent; + } +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + + child = crt; + self_cnt = 0; + parent_is_trusted = 0; + child_is_trusted = 0; + + while (1) { + /* Add certificate to the verification chain */ + cur = &ver_chain->items[ver_chain->len]; + cur->crt = child; + cur->flags = 0; + ver_chain->len++; + flags = &cur->flags; + + /* Check time-validity (all certificates) */ + if (mbedtls_x509_time_is_past(&child->valid_to)) { + *flags |= MBEDTLS_X509_BADCERT_EXPIRED; + } + + if (mbedtls_x509_time_is_future(&child->valid_from)) { + *flags |= MBEDTLS_X509_BADCERT_FUTURE; + } + + /* Stop here for trusted roots (but not for trusted EE certs) */ + if (child_is_trusted) { + return 0; + } + + /* Check signature algorithm: MD & PK algs */ + if (x509_profile_check_md_alg(profile, child->sig_md) != 0) { + *flags |= MBEDTLS_X509_BADCERT_BAD_MD; + } + + if (x509_profile_check_pk_alg(profile, child->sig_pk) != 0) { + *flags |= MBEDTLS_X509_BADCERT_BAD_PK; + } + + /* Special case: EE certs that are locally trusted */ + if (ver_chain->len == 1 && + x509_crt_check_ee_locally_trusted(child, trust_ca) == 0) { + return 0; + } + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +find_parent: +#endif + + /* Obtain list of potential trusted signers from CA callback, + * or use statically provided list. */ +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + if (f_ca_cb != NULL) { + mbedtls_x509_crt_free(ver_chain->trust_ca_cb_result); + mbedtls_free(ver_chain->trust_ca_cb_result); + ver_chain->trust_ca_cb_result = NULL; + + ret = f_ca_cb(p_ca_cb, child, &ver_chain->trust_ca_cb_result); + if (ret != 0) { + return MBEDTLS_ERR_X509_FATAL_ERROR; + } + + cur_trust_ca = ver_chain->trust_ca_cb_result; + } else +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ + { + ((void) f_ca_cb); + ((void) p_ca_cb); + cur_trust_ca = trust_ca; + } + + /* Look for a parent in trusted CAs or up the chain */ + ret = x509_crt_find_parent(child, cur_trust_ca, &parent, + &parent_is_trusted, &signature_is_good, + ver_chain->len - 1, self_cnt, rs_ctx); + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && ret == MBEDTLS_ERR_ECP_IN_PROGRESS) { + /* save state */ + rs_ctx->in_progress = x509_crt_rs_find_parent; + rs_ctx->self_cnt = self_cnt; + rs_ctx->ver_chain = *ver_chain; /* struct copy */ + + return ret; + } +#else + (void) ret; +#endif + + /* No parent? We're done here */ + if (parent == NULL) { + *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED; + return 0; + } + + /* Count intermediate self-issued (not necessarily self-signed) certs. + * These can occur with some strategies for key rollover, see [SIRO], + * and should be excluded from max_pathlen checks. */ + if (ver_chain->len != 1 && + x509_name_cmp(&child->issuer, &child->subject) == 0) { + self_cnt++; + } + + /* path_cnt is 0 for the first intermediate CA, + * and if parent is trusted it's not an intermediate CA */ + if (!parent_is_trusted && + ver_chain->len > MBEDTLS_X509_MAX_INTERMEDIATE_CA) { + /* return immediately to avoid overflow the chain array */ + return MBEDTLS_ERR_X509_FATAL_ERROR; + } + + /* signature was checked while searching parent */ + if (!signature_is_good) { + *flags |= MBEDTLS_X509_BADCERT_NOT_TRUSTED; + } + + /* check size of signing key */ + if (x509_profile_check_key(profile, &parent->pk) != 0) { + *flags |= MBEDTLS_X509_BADCERT_BAD_KEY; + } + +#if defined(MBEDTLS_X509_CRL_PARSE_C) + /* Check trusted CA's CRL for the given crt */ + *flags |= x509_crt_verifycrl(child, parent, ca_crl, profile); +#else + (void) ca_crl; +#endif + + /* prepare for next iteration */ + child = parent; + parent = NULL; + child_is_trusted = parent_is_trusted; + signature_is_good = 0; + } +} + +/* + * Check for CN match + */ +static int x509_crt_check_cn(const mbedtls_x509_buf *name, + const char *cn, size_t cn_len) +{ + /* try exact match */ + if (name->len == cn_len && + x509_memcasecmp(cn, name->p, cn_len) == 0) { + return 0; + } + + /* try wildcard match */ + if (x509_check_wildcard(cn, name) == 0) { + return 0; + } + + return -1; +} + +/* + * Check for SAN match, see RFC 5280 Section 4.2.1.6 + */ +static int x509_crt_check_san(const mbedtls_x509_buf *name, + const char *cn, size_t cn_len) +{ + const unsigned char san_type = (unsigned char) name->tag & + MBEDTLS_ASN1_TAG_VALUE_MASK; + + /* dNSName */ + if (san_type == MBEDTLS_X509_SAN_DNS_NAME) { + return x509_crt_check_cn(name, cn, cn_len); + } + + /* (We may handle other types here later.) */ + + /* Unrecognized type */ + return -1; +} + +/* + * Verify the requested CN - only call this if cn is not NULL! + */ +static void x509_crt_verify_name(const mbedtls_x509_crt *crt, + const char *cn, + uint32_t *flags) +{ + const mbedtls_x509_name *name; + const mbedtls_x509_sequence *cur; + size_t cn_len = strlen(cn); + + if (crt->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) { + for (cur = &crt->subject_alt_names; cur != NULL; cur = cur->next) { + if (x509_crt_check_san(&cur->buf, cn, cn_len) == 0) { + break; + } + } + + if (cur == NULL) { + *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; + } + } else { + for (name = &crt->subject; name != NULL; name = name->next) { + if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &name->oid) == 0 && + x509_crt_check_cn(&name->val, cn, cn_len) == 0) { + break; + } + } + + if (name == NULL) { + *flags |= MBEDTLS_X509_BADCERT_CN_MISMATCH; + } + } +} + +/* + * Merge the flags for all certs in the chain, after calling callback + */ +static int x509_crt_merge_flags_with_cb( + uint32_t *flags, + const mbedtls_x509_crt_verify_chain *ver_chain, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned i; + uint32_t cur_flags; + const mbedtls_x509_crt_verify_chain_item *cur; + + for (i = ver_chain->len; i != 0; --i) { + cur = &ver_chain->items[i-1]; + cur_flags = cur->flags; + + if (NULL != f_vrfy) { + if ((ret = f_vrfy(p_vrfy, cur->crt, (int) i-1, &cur_flags)) != 0) { + return ret; + } + } + + *flags |= cur_flags; + } + + return 0; +} + +/* + * Verify the certificate validity, with profile, restartable version + * + * This function: + * - checks the requested CN (if any) + * - checks the type and size of the EE cert's key, + * as that isn't done as part of chain building/verification currently + * - builds and verifies the chain + * - then calls the callback and merges the flags + * + * The parameters pairs `trust_ca`, `ca_crl` and `f_ca_cb`, `p_ca_cb` + * are mutually exclusive: If `f_ca_cb != NULL`, it will be used by the + * verification routine to search for trusted signers, and CRLs will + * be disabled. Otherwise, `trust_ca` will be used as the static list + * of trusted signers, and `ca_crl` will be use as the static list + * of CRLs. + */ +static int x509_crt_verify_restartable_ca_cb(mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + mbedtls_x509_crt_ca_cb_t f_ca_cb, + void *p_ca_cb, + const mbedtls_x509_crt_profile *profile, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, + mbedtls_x509_crt *, + int, + uint32_t *), + void *p_vrfy, + mbedtls_x509_crt_restart_ctx *rs_ctx) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + mbedtls_pk_type_t pk_type; + mbedtls_x509_crt_verify_chain ver_chain; + uint32_t ee_flags; + + *flags = 0; + ee_flags = 0; + x509_crt_verify_chain_reset(&ver_chain); + + if (profile == NULL) { + ret = MBEDTLS_ERR_X509_BAD_INPUT_DATA; + goto exit; + } + + /* check name if requested */ + if (cn != NULL) { + x509_crt_verify_name(crt, cn, &ee_flags); + } + + /* Check the type and size of the key */ + pk_type = mbedtls_pk_get_type(&crt->pk); + + if (x509_profile_check_pk_alg(profile, pk_type) != 0) { + ee_flags |= MBEDTLS_X509_BADCERT_BAD_PK; + } + + if (x509_profile_check_key(profile, &crt->pk) != 0) { + ee_flags |= MBEDTLS_X509_BADCERT_BAD_KEY; + } + + /* Check the chain */ + ret = x509_crt_verify_chain(crt, trust_ca, ca_crl, + f_ca_cb, p_ca_cb, profile, + &ver_chain, rs_ctx); + + if (ret != 0) { + goto exit; + } + + /* Merge end-entity flags */ + ver_chain.items[0].flags |= ee_flags; + + /* Build final flags, calling callback on the way if any */ + ret = x509_crt_merge_flags_with_cb(flags, &ver_chain, f_vrfy, p_vrfy); + +exit: + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) + mbedtls_x509_crt_free(ver_chain.trust_ca_cb_result); + mbedtls_free(ver_chain.trust_ca_cb_result); + ver_chain.trust_ca_cb_result = NULL; +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) + if (rs_ctx != NULL && ret != MBEDTLS_ERR_ECP_IN_PROGRESS) { + mbedtls_x509_crt_restart_free(rs_ctx); + } +#endif + + /* prevent misuse of the vrfy callback - VERIFY_FAILED would be ignored by + * the SSL module for authmode optional, but non-zero return from the + * callback means a fatal error so it shouldn't be ignored */ + if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + } + + if (ret != 0) { + *flags = (uint32_t) -1; + return ret; + } + + if (*flags != 0) { + return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + } + + return 0; +} + + +/* + * Verify the certificate validity (default profile, not restartable) + */ +int mbedtls_x509_crt_verify(mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy) +{ + return x509_crt_verify_restartable_ca_cb(crt, trust_ca, ca_crl, + NULL, NULL, + &mbedtls_x509_crt_profile_default, + cn, flags, + f_vrfy, p_vrfy, NULL); +} + +/* + * Verify the certificate validity (user-chosen profile, not restartable) + */ +int mbedtls_x509_crt_verify_with_profile(mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + const mbedtls_x509_crt_profile *profile, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy) +{ + return x509_crt_verify_restartable_ca_cb(crt, trust_ca, ca_crl, + NULL, NULL, + profile, cn, flags, + f_vrfy, p_vrfy, NULL); +} + +#if defined(MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK) +/* + * Verify the certificate validity (user-chosen profile, CA callback, + * not restartable). + */ +int mbedtls_x509_crt_verify_with_ca_cb(mbedtls_x509_crt *crt, + mbedtls_x509_crt_ca_cb_t f_ca_cb, + void *p_ca_cb, + const mbedtls_x509_crt_profile *profile, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy) +{ + return x509_crt_verify_restartable_ca_cb(crt, NULL, NULL, + f_ca_cb, p_ca_cb, + profile, cn, flags, + f_vrfy, p_vrfy, NULL); +} +#endif /* MBEDTLS_X509_TRUSTED_CERTIFICATE_CALLBACK */ + +int mbedtls_x509_crt_verify_restartable(mbedtls_x509_crt *crt, + mbedtls_x509_crt *trust_ca, + mbedtls_x509_crl *ca_crl, + const mbedtls_x509_crt_profile *profile, + const char *cn, uint32_t *flags, + int (*f_vrfy)(void *, mbedtls_x509_crt *, int, uint32_t *), + void *p_vrfy, + mbedtls_x509_crt_restart_ctx *rs_ctx) +{ + return x509_crt_verify_restartable_ca_cb(crt, trust_ca, ca_crl, + NULL, NULL, + profile, cn, flags, + f_vrfy, p_vrfy, rs_ctx); +} + + +/* + * Initialize a certificate chain + */ +void mbedtls_x509_crt_init(mbedtls_x509_crt *crt) +{ + memset(crt, 0, sizeof(mbedtls_x509_crt)); +} + +/* + * Unallocate all certificate data + */ +void mbedtls_x509_crt_free(mbedtls_x509_crt *crt) +{ + mbedtls_x509_crt *cert_cur = crt; + mbedtls_x509_crt *cert_prv; + + while (cert_cur != NULL) { + mbedtls_pk_free(&cert_cur->pk); + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + mbedtls_free(cert_cur->sig_opts); +#endif + + mbedtls_asn1_free_named_data_list_shallow(cert_cur->issuer.next); + mbedtls_asn1_free_named_data_list_shallow(cert_cur->subject.next); + mbedtls_asn1_sequence_free(cert_cur->ext_key_usage.next); + mbedtls_asn1_sequence_free(cert_cur->subject_alt_names.next); + mbedtls_asn1_sequence_free(cert_cur->certificate_policies.next); + + if (cert_cur->raw.p != NULL && cert_cur->own_buffer) { + mbedtls_platform_zeroize(cert_cur->raw.p, cert_cur->raw.len); + mbedtls_free(cert_cur->raw.p); + } + + cert_prv = cert_cur; + cert_cur = cert_cur->next; + + mbedtls_platform_zeroize(cert_prv, sizeof(mbedtls_x509_crt)); + if (cert_prv != crt) { + mbedtls_free(cert_prv); + } + } +} + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECP_RESTARTABLE) +/* + * Initialize a restart context + */ +void mbedtls_x509_crt_restart_init(mbedtls_x509_crt_restart_ctx *ctx) +{ + mbedtls_pk_restart_init(&ctx->pk); + + ctx->parent = NULL; + ctx->fallback_parent = NULL; + ctx->fallback_signature_is_good = 0; + + ctx->parent_is_trusted = -1; + + ctx->in_progress = x509_crt_rs_none; + ctx->self_cnt = 0; + x509_crt_verify_chain_reset(&ctx->ver_chain); +} + +/* + * Free the components of a restart context + */ +void mbedtls_x509_crt_restart_free(mbedtls_x509_crt_restart_ctx *ctx) +{ + if (ctx == NULL) { + return; + } + + mbedtls_pk_restart_free(&ctx->pk); + mbedtls_x509_crt_restart_init(ctx); +} +#endif /* MBEDTLS_ECDSA_C && MBEDTLS_ECP_RESTARTABLE */ + +#endif /* MBEDTLS_X509_CRT_PARSE_C */ diff --git a/r5dev/thirdparty/mbedtls/x509_csr.c b/r5dev/thirdparty/mbedtls/x509_csr.c new file mode 100644 index 00000000..cd117cbd --- /dev/null +++ b/r5dev/thirdparty/mbedtls/x509_csr.c @@ -0,0 +1,588 @@ +/* + * X.509 Certificate Signing Request (CSR) parsing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc5280.txt (Certificates and CRLs) + * http://www.ietf.org/rfc/rfc3279.txt (Alg IDs for CRLs) + * http://www.ietf.org/rfc/rfc2986.txt (CSRs, aka PKCS#10) + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#include "common.h" + +#if defined(MBEDTLS_X509_CSR_PARSE_C) + +#include "mbedtls/x509_csr.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" + +#include + +#if defined(MBEDTLS_PEM_PARSE_C) +#include "mbedtls/pem.h" +#endif + +#include "mbedtls/platform.h" + +#if defined(MBEDTLS_FS_IO) || defined(EFIX64) || defined(EFI32) +#include +#endif + +/* + * Version ::= INTEGER { v1(0) } + */ +static int x509_csr_get_version(unsigned char **p, + const unsigned char *end, + int *ver) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + if ((ret = mbedtls_asn1_get_int(p, end, ver)) != 0) { + if (ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + *ver = 0; + return 0; + } + + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_VERSION, ret); + } + + return 0; +} + +/* + * Parse CSR extension requests in DER format + */ +static int x509_csr_parse_extensions(mbedtls_x509_csr *csr, + unsigned char **p, const unsigned char *end) +{ + int ret; + size_t len; + unsigned char *end_ext_data; + while (*p < end) { + mbedtls_x509_buf extn_oid = { 0, 0, NULL }; + int ext_type = 0; + + /* Read sequence tag */ + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + end_ext_data = *p + len; + + /* Get extension ID */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &extn_oid.len, + MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + extn_oid.tag = MBEDTLS_ASN1_OID; + extn_oid.p = *p; + *p += extn_oid.len; + + /* Data should be octet string type */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &len, + MBEDTLS_ASN1_OCTET_STRING)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if (*p + len != end_ext_data) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* + * Detect supported extensions and skip unsupported extensions + */ + ret = mbedtls_oid_get_x509_ext_type(&extn_oid, &ext_type); + + if (ret == 0) { + /* Forbid repeated extensions */ + if ((csr->ext_types & ext_type) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_INVALID_DATA); + } + + csr->ext_types |= ext_type; + + switch (ext_type) { + case MBEDTLS_X509_EXT_KEY_USAGE: + /* Parse key usage */ + if ((ret = mbedtls_x509_get_key_usage(p, end_ext_data, + &csr->key_usage)) != 0) { + return ret; + } + break; + + case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME: + /* Parse subject alt name */ + if ((ret = mbedtls_x509_get_subject_alt_name(p, end_ext_data, + &csr->subject_alt_names)) != 0) { + return ret; + } + break; + + case MBEDTLS_X509_EXT_NS_CERT_TYPE: + /* Parse netscape certificate type */ + if ((ret = mbedtls_x509_get_ns_cert_type(p, end_ext_data, + &csr->ns_cert_type)) != 0) { + return ret; + } + break; + default: + break; + } + } + *p = end_ext_data; + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Parse CSR attributes in DER format + */ +static int x509_csr_parse_attributes(mbedtls_x509_csr *csr, + const unsigned char *start, const unsigned char *end) +{ + int ret; + size_t len; + unsigned char *end_attr_data; + unsigned char **p = (unsigned char **) &start; + + while (*p < end) { + mbedtls_x509_buf attr_oid = { 0, 0, NULL }; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + end_attr_data = *p + len; + + /* Get attribute ID */ + if ((ret = mbedtls_asn1_get_tag(p, end_attr_data, &attr_oid.len, + MBEDTLS_ASN1_OID)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + attr_oid.tag = MBEDTLS_ASN1_OID; + attr_oid.p = *p; + *p += attr_oid.len; + + /* Check that this is an extension-request attribute */ + if (MBEDTLS_OID_CMP(MBEDTLS_OID_PKCS9_CSR_EXT_REQ, &attr_oid) == 0) { + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)) != 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != + 0) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, ret); + } + + if ((ret = x509_csr_parse_extensions(csr, p, *p + len)) != 0) { + return ret; + } + + if (*p != end_attr_data) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + } + + *p = end_attr_data; + } + + if (*p != end) { + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_EXTENSIONS, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Parse a CSR in DER format + */ +int mbedtls_x509_csr_parse_der(mbedtls_x509_csr *csr, + const unsigned char *buf, size_t buflen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len; + unsigned char *p, *end; + mbedtls_x509_buf sig_params; + + memset(&sig_params, 0, sizeof(mbedtls_x509_buf)); + + /* + * Check for valid input + */ + if (csr == NULL || buf == NULL || buflen == 0) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + mbedtls_x509_csr_init(csr); + + /* + * first copy the raw DER data + */ + p = mbedtls_calloc(1, len = buflen); + + if (p == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + memcpy(p, buf, buflen); + + csr->raw.p = p; + csr->raw.len = len; + end = p + len; + + /* + * CertificationRequest ::= SEQUENCE { + * certificationRequestInfo CertificationRequestInfo, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + * } + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERR_X509_INVALID_FORMAT; + } + + if (len != (size_t) (end - p)) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + /* + * CertificationRequestInfo ::= SEQUENCE { + */ + csr->cri.p = p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + end = p + len; + csr->cri.len = end - csr->cri.p; + + /* + * Version ::= INTEGER { v1(0) } + */ + if ((ret = x509_csr_get_version(&p, end, &csr->version)) != 0) { + mbedtls_x509_csr_free(csr); + return ret; + } + + if (csr->version != 0) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERR_X509_UNKNOWN_VERSION; + } + + csr->version++; + + /* + * subject Name + */ + csr->subject_raw.p = p; + + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + if ((ret = mbedtls_x509_get_name(&p, p + len, &csr->subject)) != 0) { + mbedtls_x509_csr_free(csr); + return ret; + } + + csr->subject_raw.len = p - csr->subject_raw.p; + + /* + * subjectPKInfo SubjectPublicKeyInfo + */ + if ((ret = mbedtls_pk_parse_subpubkey(&p, end, &csr->pk)) != 0) { + mbedtls_x509_csr_free(csr); + return ret; + } + + /* + * attributes [0] Attributes + * + * The list of possible attributes is open-ended, though RFC 2985 + * (PKCS#9) defines a few in section 5.4. We currently don't support any, + * so we just ignore them. This is a safe thing to do as the worst thing + * that could happen is that we issue a certificate that does not match + * the requester's expectations - this cannot cause a violation of our + * signature policies. + */ + if ((ret = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC)) != + 0) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); + } + + if ((ret = x509_csr_parse_attributes(csr, p, p + len)) != 0) { + mbedtls_x509_csr_free(csr); + return ret; + } + + p += len; + + end = csr->raw.p + csr->raw.len; + + /* + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING + */ + if ((ret = mbedtls_x509_get_alg(&p, end, &csr->sig_oid, &sig_params)) != 0) { + mbedtls_x509_csr_free(csr); + return ret; + } + + if ((ret = mbedtls_x509_get_sig_alg(&csr->sig_oid, &sig_params, + &csr->sig_md, &csr->sig_pk, + &csr->sig_opts)) != 0) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG; + } + + if ((ret = mbedtls_x509_get_sig(&p, end, &csr->sig)) != 0) { + mbedtls_x509_csr_free(csr); + return ret; + } + + if (p != end) { + mbedtls_x509_csr_free(csr); + return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + } + + return 0; +} + +/* + * Parse a CSR, allowing for PEM or raw DER encoding + */ +int mbedtls_x509_csr_parse(mbedtls_x509_csr *csr, const unsigned char *buf, size_t buflen) +{ +#if defined(MBEDTLS_PEM_PARSE_C) + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t use_len; + mbedtls_pem_context pem; +#endif + + /* + * Check for valid input + */ + if (csr == NULL || buf == NULL || buflen == 0) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + +#if defined(MBEDTLS_PEM_PARSE_C) + /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */ + if (buf[buflen - 1] == '\0') { + mbedtls_pem_init(&pem); + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN CERTIFICATE REQUEST-----", + "-----END CERTIFICATE REQUEST-----", + buf, NULL, 0, &use_len); + if (ret == MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + ret = mbedtls_pem_read_buffer(&pem, + "-----BEGIN NEW CERTIFICATE REQUEST-----", + "-----END NEW CERTIFICATE REQUEST-----", + buf, NULL, 0, &use_len); + } + + if (ret == 0) { + /* + * Was PEM encoded, parse the result + */ + ret = mbedtls_x509_csr_parse_der(csr, pem.buf, pem.buflen); + } + + mbedtls_pem_free(&pem); + if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) { + return ret; + } + } +#endif /* MBEDTLS_PEM_PARSE_C */ + return mbedtls_x509_csr_parse_der(csr, buf, buflen); +} + +#if defined(MBEDTLS_FS_IO) +/* + * Load a CSR into the structure + */ +int mbedtls_x509_csr_parse_file(mbedtls_x509_csr *csr, const char *path) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + unsigned char *buf; + + if ((ret = mbedtls_pk_load_file(path, &buf, &n)) != 0) { + return ret; + } + + ret = mbedtls_x509_csr_parse(csr, buf, n); + + mbedtls_platform_zeroize(buf, n); + mbedtls_free(buf); + + return ret; +} +#endif /* MBEDTLS_FS_IO */ + +#if !defined(MBEDTLS_X509_REMOVE_INFO) +#define BEFORE_COLON 14 +#define BC "14" +/* + * Return an informational string about the CSR. + */ +int mbedtls_x509_csr_info(char *buf, size_t size, const char *prefix, + const mbedtls_x509_csr *csr) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t n; + char *p; + char key_size_str[BEFORE_COLON]; + + p = buf; + n = size; + + ret = mbedtls_snprintf(p, n, "%sCSR version : %d", + prefix, csr->version); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%ssubject name : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + ret = mbedtls_x509_dn_gets(p, n, &csr->subject); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_snprintf(p, n, "\n%ssigned using : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + ret = mbedtls_x509_sig_alg_gets(p, n, &csr->sig_oid, csr->sig_pk, csr->sig_md, + csr->sig_opts); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = mbedtls_x509_key_size_helper(key_size_str, BEFORE_COLON, + mbedtls_pk_get_name(&csr->pk))) != 0) { + return ret; + } + + ret = mbedtls_snprintf(p, n, "\n%s%-" BC "s: %d bits\n", prefix, key_size_str, + (int) mbedtls_pk_get_bitlen(&csr->pk)); + MBEDTLS_X509_SAFE_SNPRINTF; + + /* + * Optional extensions + */ + + if (csr->ext_types & MBEDTLS_X509_EXT_SUBJECT_ALT_NAME) { + ret = mbedtls_snprintf(p, n, "\n%ssubject alt name :", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = mbedtls_x509_info_subject_alt_name(&p, &n, + &csr->subject_alt_names, + prefix)) != 0) { + return ret; + } + } + + if (csr->ext_types & MBEDTLS_X509_EXT_NS_CERT_TYPE) { + ret = mbedtls_snprintf(p, n, "\n%scert. type : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = mbedtls_x509_info_cert_type(&p, &n, csr->ns_cert_type)) != 0) { + return ret; + } + } + + if (csr->ext_types & MBEDTLS_X509_EXT_KEY_USAGE) { + ret = mbedtls_snprintf(p, n, "\n%skey usage : ", prefix); + MBEDTLS_X509_SAFE_SNPRINTF; + + if ((ret = mbedtls_x509_info_key_usage(&p, &n, csr->key_usage)) != 0) { + return ret; + } + } + + if (csr->ext_types != 0) { + ret = mbedtls_snprintf(p, n, "\n"); + MBEDTLS_X509_SAFE_SNPRINTF; + } + + return (int) (size - n); +} +#endif /* MBEDTLS_X509_REMOVE_INFO */ + +/* + * Initialize a CSR + */ +void mbedtls_x509_csr_init(mbedtls_x509_csr *csr) +{ + memset(csr, 0, sizeof(mbedtls_x509_csr)); +} + +/* + * Unallocate all CSR data + */ +void mbedtls_x509_csr_free(mbedtls_x509_csr *csr) +{ + if (csr == NULL) { + return; + } + + mbedtls_pk_free(&csr->pk); + +#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) + mbedtls_free(csr->sig_opts); +#endif + + mbedtls_asn1_free_named_data_list_shallow(csr->subject.next); + mbedtls_asn1_sequence_free(csr->subject_alt_names.next); + + if (csr->raw.p != NULL) { + mbedtls_platform_zeroize(csr->raw.p, csr->raw.len); + mbedtls_free(csr->raw.p); + } + + mbedtls_platform_zeroize(csr, sizeof(mbedtls_x509_csr)); +} + +#endif /* MBEDTLS_X509_CSR_PARSE_C */ diff --git a/r5dev/thirdparty/mbedtls/x509write_crt.c b/r5dev/thirdparty/mbedtls/x509write_crt.c new file mode 100644 index 00000000..f481155e --- /dev/null +++ b/r5dev/thirdparty/mbedtls/x509write_crt.c @@ -0,0 +1,674 @@ +/* + * X.509 certificate writing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * References: + * - certificates: RFC 5280, updated by RFC 6818 + * - CSRs: PKCS#10 v1.7 aka RFC 2986 + * - attributes: PKCS#9 v2.0 aka RFC 2985 + */ + +#include "common.h" + +#if defined(MBEDTLS_X509_CRT_WRITE_C) + +#include "mbedtls/x509_crt.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" +#include "mbedtls/md.h" + +#include + +#if defined(MBEDTLS_PEM_WRITE_C) +#include "mbedtls/pem.h" +#endif /* MBEDTLS_PEM_WRITE_C */ + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + +#include "hash_info.h" +#include "mbedtls/legacy_or_psa.h" + +void mbedtls_x509write_crt_init(mbedtls_x509write_cert *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_x509write_cert)); + + ctx->version = MBEDTLS_X509_CRT_VERSION_3; +} + +void mbedtls_x509write_crt_free(mbedtls_x509write_cert *ctx) +{ + mbedtls_asn1_free_named_data_list(&ctx->subject); + mbedtls_asn1_free_named_data_list(&ctx->issuer); + mbedtls_asn1_free_named_data_list(&ctx->extensions); + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_x509write_cert)); +} + +void mbedtls_x509write_crt_set_version(mbedtls_x509write_cert *ctx, + int version) +{ + ctx->version = version; +} + +void mbedtls_x509write_crt_set_md_alg(mbedtls_x509write_cert *ctx, + mbedtls_md_type_t md_alg) +{ + ctx->md_alg = md_alg; +} + +void mbedtls_x509write_crt_set_subject_key(mbedtls_x509write_cert *ctx, + mbedtls_pk_context *key) +{ + ctx->subject_key = key; +} + +void mbedtls_x509write_crt_set_issuer_key(mbedtls_x509write_cert *ctx, + mbedtls_pk_context *key) +{ + ctx->issuer_key = key; +} + +int mbedtls_x509write_crt_set_subject_name(mbedtls_x509write_cert *ctx, + const char *subject_name) +{ + return mbedtls_x509_string_to_names(&ctx->subject, subject_name); +} + +int mbedtls_x509write_crt_set_issuer_name(mbedtls_x509write_cert *ctx, + const char *issuer_name) +{ + return mbedtls_x509_string_to_names(&ctx->issuer, issuer_name); +} + +#if defined(MBEDTLS_BIGNUM_C) && !defined(MBEDTLS_DEPRECATED_REMOVED) +int mbedtls_x509write_crt_set_serial(mbedtls_x509write_cert *ctx, + const mbedtls_mpi *serial) +{ + int ret; + size_t tmp_len; + + /* Ensure that the MPI value fits into the buffer */ + tmp_len = mbedtls_mpi_size(serial); + if (tmp_len > MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + ctx->serial_len = tmp_len; + + ret = mbedtls_mpi_write_binary(serial, ctx->serial, tmp_len); + if (ret < 0) { + return ret; + } + + return 0; +} +#endif // MBEDTLS_BIGNUM_C && !MBEDTLS_DEPRECATED_REMOVED + +int mbedtls_x509write_crt_set_serial_raw(mbedtls_x509write_cert *ctx, + unsigned char *serial, size_t serial_len) +{ + if (serial_len > MBEDTLS_X509_RFC5280_MAX_SERIAL_LEN) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + ctx->serial_len = serial_len; + memcpy(ctx->serial, serial, serial_len); + + return 0; +} + +int mbedtls_x509write_crt_set_validity(mbedtls_x509write_cert *ctx, + const char *not_before, + const char *not_after) +{ + if (strlen(not_before) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1 || + strlen(not_after) != MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + strncpy(ctx->not_before, not_before, MBEDTLS_X509_RFC5280_UTC_TIME_LEN); + strncpy(ctx->not_after, not_after, MBEDTLS_X509_RFC5280_UTC_TIME_LEN); + ctx->not_before[MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1] = 'Z'; + ctx->not_after[MBEDTLS_X509_RFC5280_UTC_TIME_LEN - 1] = 'Z'; + + return 0; +} + +int mbedtls_x509write_crt_set_extension(mbedtls_x509write_cert *ctx, + const char *oid, size_t oid_len, + int critical, + const unsigned char *val, size_t val_len) +{ + return mbedtls_x509_set_extension(&ctx->extensions, oid, oid_len, + critical, val, val_len); +} + +int mbedtls_x509write_crt_set_basic_constraints(mbedtls_x509write_cert *ctx, + int is_ca, int max_pathlen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char buf[9]; + unsigned char *c = buf + sizeof(buf); + size_t len = 0; + + memset(buf, 0, sizeof(buf)); + + if (is_ca && max_pathlen > 127) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + if (is_ca) { + if (max_pathlen >= 0) { + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(&c, buf, + max_pathlen)); + } + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_bool(&c, buf, 1)); + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + return + mbedtls_x509write_crt_set_extension(ctx, MBEDTLS_OID_BASIC_CONSTRAINTS, + MBEDTLS_OID_SIZE(MBEDTLS_OID_BASIC_CONSTRAINTS), + is_ca, buf + sizeof(buf) - len, len); +} + +#if defined(MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA) +static int mbedtls_x509write_crt_set_key_identifier(mbedtls_x509write_cert *ctx, + int is_ca, + unsigned char tag) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char buf[MBEDTLS_MPI_MAX_SIZE * 2 + 20]; /* tag, length + 2xMPI */ + unsigned char *c = buf + sizeof(buf); + size_t len = 0; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + size_t hash_length; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + memset(buf, 0, sizeof(buf)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_pk_write_pubkey(&c, + buf, + is_ca ? + ctx->issuer_key : + ctx->subject_key)); + + +#if defined(MBEDTLS_USE_PSA_CRYPTO) + status = psa_hash_compute(PSA_ALG_SHA_1, + buf + sizeof(buf) - len, + len, + buf + sizeof(buf) - 20, + 20, + &hash_length); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } +#else + ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), + buf + sizeof(buf) - len, len, + buf + sizeof(buf) - 20); + if (ret != 0) { + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + c = buf + sizeof(buf) - 20; + len = 20; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, tag)); + + if (is_ca) { // writes AuthorityKeyIdentifier sequence + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, + buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + } + + if (is_ca) { + return mbedtls_x509write_crt_set_extension(ctx, + MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER, + MBEDTLS_OID_SIZE( + MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER), + 0, buf + sizeof(buf) - len, len); + } else { + return mbedtls_x509write_crt_set_extension(ctx, + MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER, + MBEDTLS_OID_SIZE( + MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER), + 0, buf + sizeof(buf) - len, len); + } +} + +int mbedtls_x509write_crt_set_subject_key_identifier(mbedtls_x509write_cert *ctx) +{ + return mbedtls_x509write_crt_set_key_identifier(ctx, + 0, + MBEDTLS_ASN1_OCTET_STRING); +} + +int mbedtls_x509write_crt_set_authority_key_identifier(mbedtls_x509write_cert *ctx) +{ + return mbedtls_x509write_crt_set_key_identifier(ctx, + 1, + (MBEDTLS_ASN1_CONTEXT_SPECIFIC | 0)); +} +#endif /* MBEDTLS_HAS_ALG_SHA_1_VIA_MD_OR_PSA_BASED_ON_USE_PSA */ + +int mbedtls_x509write_crt_set_key_usage(mbedtls_x509write_cert *ctx, + unsigned int key_usage) +{ + unsigned char buf[5] = { 0 }, ku[2] = { 0 }; + unsigned char *c; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const unsigned int allowed_bits = MBEDTLS_X509_KU_DIGITAL_SIGNATURE | + MBEDTLS_X509_KU_NON_REPUDIATION | + MBEDTLS_X509_KU_KEY_ENCIPHERMENT | + MBEDTLS_X509_KU_DATA_ENCIPHERMENT | + MBEDTLS_X509_KU_KEY_AGREEMENT | + MBEDTLS_X509_KU_KEY_CERT_SIGN | + MBEDTLS_X509_KU_CRL_SIGN | + MBEDTLS_X509_KU_ENCIPHER_ONLY | + MBEDTLS_X509_KU_DECIPHER_ONLY; + + /* Check that nothing other than the allowed flags is set */ + if ((key_usage & ~allowed_bits) != 0) { + return MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE; + } + + c = buf + 5; + MBEDTLS_PUT_UINT16_LE(key_usage, ku, 0); + ret = mbedtls_asn1_write_named_bitstring(&c, buf, ku, 9); + + if (ret < 0) { + return ret; + } else if (ret < 3 || ret > 5) { + return MBEDTLS_ERR_X509_INVALID_FORMAT; + } + + ret = mbedtls_x509write_crt_set_extension(ctx, MBEDTLS_OID_KEY_USAGE, + MBEDTLS_OID_SIZE(MBEDTLS_OID_KEY_USAGE), + 1, c, (size_t) ret); + if (ret != 0) { + return ret; + } + + return 0; +} + +int mbedtls_x509write_crt_set_ext_key_usage(mbedtls_x509write_cert *ctx, + const mbedtls_asn1_sequence *exts) +{ + unsigned char buf[256]; + unsigned char *c = buf + sizeof(buf); + int ret; + size_t len = 0; + const mbedtls_asn1_sequence *last_ext = NULL; + const mbedtls_asn1_sequence *ext; + + memset(buf, 0, sizeof(buf)); + + /* We need at least one extension: SEQUENCE SIZE (1..MAX) OF KeyPurposeId */ + if (exts == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + /* Iterate over exts backwards, so we write them out in the requested order */ + while (last_ext != exts) { + for (ext = exts; ext->next != last_ext; ext = ext->next) { + } + if (ext->buf.tag != MBEDTLS_ASN1_OID) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, buf, ext->buf.p, ext->buf.len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, ext->buf.len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_OID)); + last_ext = ext; + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + + return mbedtls_x509write_crt_set_extension(ctx, + MBEDTLS_OID_EXTENDED_KEY_USAGE, + MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), + 1, c, len); +} + +int mbedtls_x509write_crt_set_ns_cert_type(mbedtls_x509write_cert *ctx, + unsigned char ns_cert_type) +{ + unsigned char buf[4] = { 0 }; + unsigned char *c; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + c = buf + 4; + + ret = mbedtls_asn1_write_named_bitstring(&c, buf, &ns_cert_type, 8); + if (ret < 3 || ret > 4) { + return ret; + } + + ret = mbedtls_x509write_crt_set_extension(ctx, MBEDTLS_OID_NS_CERT_TYPE, + MBEDTLS_OID_SIZE(MBEDTLS_OID_NS_CERT_TYPE), + 0, c, (size_t) ret); + if (ret != 0) { + return ret; + } + + return 0; +} + +static int x509_write_time(unsigned char **p, unsigned char *start, + const char *t, size_t size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t len = 0; + + /* + * write MBEDTLS_ASN1_UTC_TIME if year < 2050 (2 bytes shorter) + */ + if (t[0] < '2' || (t[0] == '2' && t[1] == '0' && t[2] < '5')) { + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, + (const unsigned char *) t + 2, + size - 2)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, + MBEDTLS_ASN1_UTC_TIME)); + } else { + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(p, start, + (const unsigned char *) t, + size)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(p, start, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(p, start, + MBEDTLS_ASN1_GENERALIZED_TIME)); + } + + return (int) len; +} + +int mbedtls_x509write_crt_der(mbedtls_x509write_cert *ctx, + unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const char *sig_oid; + size_t sig_oid_len = 0; + unsigned char *c, *c2; + unsigned char sig[MBEDTLS_PK_SIGNATURE_MAX_SIZE]; + size_t hash_length = 0; + unsigned char hash[MBEDTLS_HASH_MAX_SIZE]; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_algorithm_t psa_algorithm; +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + size_t sub_len = 0, pub_len = 0, sig_and_oid_len = 0, sig_len; + size_t len = 0; + mbedtls_pk_type_t pk_alg; + + /* + * Prepare data to be signed at the end of the target buffer + */ + c = buf + size; + + /* Signature algorithm needed in TBS, and later for actual signature */ + + /* There's no direct way of extracting a signature algorithm + * (represented as an element of mbedtls_pk_type_t) from a PK instance. */ + if (mbedtls_pk_can_do(ctx->issuer_key, MBEDTLS_PK_RSA)) { + pk_alg = MBEDTLS_PK_RSA; + } else if (mbedtls_pk_can_do(ctx->issuer_key, MBEDTLS_PK_ECDSA)) { + pk_alg = MBEDTLS_PK_ECDSA; + } else { + return MBEDTLS_ERR_X509_INVALID_ALG; + } + + if ((ret = mbedtls_oid_get_oid_by_sig_alg(pk_alg, ctx->md_alg, + &sig_oid, &sig_oid_len)) != 0) { + return ret; + } + + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + */ + + /* Only for v3 */ + if (ctx->version == MBEDTLS_X509_CRT_VERSION_3) { + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_x509_write_extensions(&c, + buf, ctx->extensions)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | 3)); + } + + /* + * SubjectPublicKeyInfo + */ + MBEDTLS_ASN1_CHK_ADD(pub_len, + mbedtls_pk_write_pubkey_der(ctx->subject_key, + buf, c - buf)); + c -= pub_len; + len += pub_len; + + /* + * Subject ::= Name + */ + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_x509_write_names(&c, buf, + ctx->subject)); + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + */ + sub_len = 0; + + MBEDTLS_ASN1_CHK_ADD(sub_len, + x509_write_time(&c, buf, ctx->not_after, + MBEDTLS_X509_RFC5280_UTC_TIME_LEN)); + + MBEDTLS_ASN1_CHK_ADD(sub_len, + x509_write_time(&c, buf, ctx->not_before, + MBEDTLS_X509_RFC5280_UTC_TIME_LEN)); + + len += sub_len; + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, sub_len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + /* + * Issuer ::= Name + */ + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_x509_write_names(&c, buf, + ctx->issuer)); + + /* + * Signature ::= AlgorithmIdentifier + */ + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_algorithm_identifier(&c, buf, + sig_oid, strlen(sig_oid), 0)); + + /* + * Serial ::= INTEGER + * + * Written data is: + * - "ctx->serial_len" bytes for the raw serial buffer + * - if MSb of "serial" is 1, then prepend an extra 0x00 byte + * - 1 byte for the length + * - 1 byte for the TAG + */ + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, buf, + ctx->serial, ctx->serial_len)); + if (*c & 0x80) { + if (c - buf < 1) { + return MBEDTLS_ERR_X509_BUFFER_TOO_SMALL; + } + *(--c) = 0x0; + len++; + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, + ctx->serial_len + 1)); + } else { + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, + ctx->serial_len)); + } + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_INTEGER)); + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + + /* Can be omitted for v1 */ + if (ctx->version != MBEDTLS_X509_CRT_VERSION_1) { + sub_len = 0; + MBEDTLS_ASN1_CHK_ADD(sub_len, + mbedtls_asn1_write_int(&c, buf, ctx->version)); + len += sub_len; + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_len(&c, buf, sub_len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | + MBEDTLS_ASN1_CONSTRUCTED | 0)); + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag(&c, buf, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + /* + * Make signature + */ + + /* Compute hash of CRT. */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + psa_algorithm = mbedtls_hash_info_psa_from_md(ctx->md_alg); + + status = psa_hash_compute(psa_algorithm, + c, + len, + hash, + sizeof(hash), + &hash_length); + if (status != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } +#else + if ((ret = mbedtls_md(mbedtls_md_info_from_type(ctx->md_alg), c, + len, hash)) != 0) { + return ret; + } +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + + if ((ret = mbedtls_pk_sign(ctx->issuer_key, ctx->md_alg, + hash, hash_length, sig, sizeof(sig), &sig_len, + f_rng, p_rng)) != 0) { + return ret; + } + + /* Move CRT to the front of the buffer to have space + * for the signature. */ + memmove(buf, c, len); + c = buf + len; + + /* Add signature at the end of the buffer, + * making sure that it doesn't underflow + * into the CRT buffer. */ + c2 = buf + size; + MBEDTLS_ASN1_CHK_ADD(sig_and_oid_len, mbedtls_x509_write_sig(&c2, c, + sig_oid, sig_oid_len, sig, + sig_len)); + + /* + * Memory layout after this step: + * + * buf c=buf+len c2 buf+size + * [CRT0,...,CRTn, UNUSED, ..., UNUSED, SIG0, ..., SIGm] + */ + + /* Move raw CRT to just before the signature. */ + c = c2 - len; + memmove(c, buf, len); + + len += sig_and_oid_len; + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + return (int) len; +} + +#define PEM_BEGIN_CRT "-----BEGIN CERTIFICATE-----\n" +#define PEM_END_CRT "-----END CERTIFICATE-----\n" + +#if defined(MBEDTLS_PEM_WRITE_C) +int mbedtls_x509write_crt_pem(mbedtls_x509write_cert *crt, + unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen; + + if ((ret = mbedtls_x509write_crt_der(crt, buf, size, + f_rng, p_rng)) < 0) { + return ret; + } + + if ((ret = mbedtls_pem_write_buffer(PEM_BEGIN_CRT, PEM_END_CRT, + buf + size - ret, ret, + buf, size, &olen)) != 0) { + return ret; + } + + return 0; +} +#endif /* MBEDTLS_PEM_WRITE_C */ + +#endif /* MBEDTLS_X509_CRT_WRITE_C */ diff --git a/r5dev/thirdparty/mbedtls/x509write_csr.c b/r5dev/thirdparty/mbedtls/x509write_csr.c new file mode 100644 index 00000000..deb66174 --- /dev/null +++ b/r5dev/thirdparty/mbedtls/x509write_csr.c @@ -0,0 +1,436 @@ +/* + * X.509 Certificate Signing Request writing + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * References: + * - CSRs: PKCS#10 v1.7 aka RFC 2986 + * - attributes: PKCS#9 v2.0 aka RFC 2985 + */ + +#include "common.h" + +#if defined(MBEDTLS_X509_CSR_WRITE_C) + +#include "mbedtls/x509.h" +#include "mbedtls/x509_csr.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/error.h" +#include "mbedtls/oid.h" +#include "mbedtls/platform_util.h" + +#if defined(MBEDTLS_USE_PSA_CRYPTO) +#include "psa/crypto.h" +#include "mbedtls/psa_util.h" +#endif /* MBEDTLS_USE_PSA_CRYPTO */ +#include "hash_info.h" + +#include +#include + +#if defined(MBEDTLS_PEM_WRITE_C) +#include "mbedtls/pem.h" +#endif + +#include "mbedtls/platform.h" + +void mbedtls_x509write_csr_init(mbedtls_x509write_csr *ctx) +{ + memset(ctx, 0, sizeof(mbedtls_x509write_csr)); +} + +void mbedtls_x509write_csr_free(mbedtls_x509write_csr *ctx) +{ + mbedtls_asn1_free_named_data_list(&ctx->subject); + mbedtls_asn1_free_named_data_list(&ctx->extensions); + + mbedtls_platform_zeroize(ctx, sizeof(mbedtls_x509write_csr)); +} + +void mbedtls_x509write_csr_set_md_alg(mbedtls_x509write_csr *ctx, mbedtls_md_type_t md_alg) +{ + ctx->md_alg = md_alg; +} + +void mbedtls_x509write_csr_set_key(mbedtls_x509write_csr *ctx, mbedtls_pk_context *key) +{ + ctx->key = key; +} + +int mbedtls_x509write_csr_set_subject_name(mbedtls_x509write_csr *ctx, + const char *subject_name) +{ + return mbedtls_x509_string_to_names(&ctx->subject, subject_name); +} + +int mbedtls_x509write_csr_set_extension(mbedtls_x509write_csr *ctx, + const char *oid, size_t oid_len, + int critical, + const unsigned char *val, size_t val_len) +{ + return mbedtls_x509_set_extension(&ctx->extensions, oid, oid_len, + critical, val, val_len); +} + +int mbedtls_x509write_csr_set_subject_alternative_name(mbedtls_x509write_csr *ctx, + const mbedtls_x509_san_list *san_list) +{ + int ret = 0; + const mbedtls_x509_san_list *cur; + unsigned char *buf; + unsigned char *p; + size_t len; + size_t buflen = 0; + + /* Determine the maximum size of the SubjectAltName list */ + for (cur = san_list; cur != NULL; cur = cur->next) { + /* Calculate size of the required buffer */ + switch (cur->node.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + case MBEDTLS_X509_SAN_IP_ADDRESS: + /* length of value for each name entry, + * maximum 4 bytes for the length field, + * 1 byte for the tag/type. + */ + buflen += cur->node.san.unstructured_name.len + 4 + 1; + break; + + default: + /* Not supported - skip. */ + break; + } + } + + /* Add the extra length field and tag */ + buflen += 4 + 1; + + /* Allocate buffer */ + buf = mbedtls_calloc(1, buflen); + if (buf == NULL) { + return MBEDTLS_ERR_ASN1_ALLOC_FAILED; + } + + mbedtls_platform_zeroize(buf, buflen); + p = buf + buflen; + + /* Write ASN.1-based structure */ + cur = san_list; + len = 0; + while (cur != NULL) { + switch (cur->node.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + case MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER: + case MBEDTLS_X509_SAN_IP_ADDRESS: + { + const unsigned char *unstructured_name = + (const unsigned char *) cur->node.san.unstructured_name.p; + size_t unstructured_name_len = cur->node.san.unstructured_name.len; + + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, + mbedtls_asn1_write_raw_buffer( + &p, buf, + unstructured_name, unstructured_name_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len( + &p, buf, unstructured_name_len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, + mbedtls_asn1_write_tag( + &p, buf, + MBEDTLS_ASN1_CONTEXT_SPECIFIC | cur->node.type)); + } + break; + default: + /* Skip unsupported names. */ + break; + } + cur = cur->next; + } + + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); + MBEDTLS_ASN1_CHK_CLEANUP_ADD(len, + mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + ret = mbedtls_x509write_csr_set_extension( + ctx, + MBEDTLS_OID_SUBJECT_ALT_NAME, + MBEDTLS_OID_SIZE(MBEDTLS_OID_SUBJECT_ALT_NAME), + 0, + buf + buflen - len, + len); + + /* If we exceeded the allocated buffer it means that maximum size of the SubjectAltName list + * was incorrectly calculated and memory is corrupted. */ + if (p < buf) { + ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + +cleanup: + mbedtls_free(buf); + return ret; +} + +int mbedtls_x509write_csr_set_key_usage(mbedtls_x509write_csr *ctx, unsigned char key_usage) +{ + unsigned char buf[4] = { 0 }; + unsigned char *c; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + c = buf + 4; + + ret = mbedtls_asn1_write_named_bitstring(&c, buf, &key_usage, 8); + if (ret < 3 || ret > 4) { + return ret; + } + + ret = mbedtls_x509write_csr_set_extension(ctx, MBEDTLS_OID_KEY_USAGE, + MBEDTLS_OID_SIZE(MBEDTLS_OID_KEY_USAGE), + 0, c, (size_t) ret); + if (ret != 0) { + return ret; + } + + return 0; +} + +int mbedtls_x509write_csr_set_ns_cert_type(mbedtls_x509write_csr *ctx, + unsigned char ns_cert_type) +{ + unsigned char buf[4] = { 0 }; + unsigned char *c; + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + + c = buf + 4; + + ret = mbedtls_asn1_write_named_bitstring(&c, buf, &ns_cert_type, 8); + if (ret < 3 || ret > 4) { + return ret; + } + + ret = mbedtls_x509write_csr_set_extension(ctx, MBEDTLS_OID_NS_CERT_TYPE, + MBEDTLS_OID_SIZE(MBEDTLS_OID_NS_CERT_TYPE), + 0, c, (size_t) ret); + if (ret != 0) { + return ret; + } + + return 0; +} + +static int x509write_csr_der_internal(mbedtls_x509write_csr *ctx, + unsigned char *buf, + size_t size, + unsigned char *sig, size_t sig_size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const char *sig_oid; + size_t sig_oid_len = 0; + unsigned char *c, *c2; + unsigned char hash[MBEDTLS_HASH_MAX_SIZE]; + size_t pub_len = 0, sig_and_oid_len = 0, sig_len; + size_t len = 0; + mbedtls_pk_type_t pk_alg; +#if defined(MBEDTLS_USE_PSA_CRYPTO) + size_t hash_len; + psa_algorithm_t hash_alg = mbedtls_hash_info_psa_from_md(ctx->md_alg); +#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* Write the CSR backwards starting from the end of buf */ + c = buf + size; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_x509_write_extensions(&c, buf, + ctx->extensions)); + + if (len) { + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag( + &c, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag( + &c, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)); + + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_oid( + &c, buf, MBEDTLS_OID_PKCS9_CSR_EXT_REQ, + MBEDTLS_OID_SIZE(MBEDTLS_OID_PKCS9_CSR_EXT_REQ))); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag( + &c, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + } + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag( + &c, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC)); + + MBEDTLS_ASN1_CHK_ADD(pub_len, mbedtls_pk_write_pubkey_der(ctx->key, + buf, c - buf)); + c -= pub_len; + len += pub_len; + + /* + * Subject ::= Name + */ + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_x509_write_names(&c, buf, + ctx->subject)); + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_int(&c, buf, 0)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag( + &c, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + + /* + * Sign the written CSR data into the sig buffer + * Note: hash errors can happen only after an internal error + */ +#if defined(MBEDTLS_USE_PSA_CRYPTO) + if (psa_hash_compute(hash_alg, + c, + len, + hash, + sizeof(hash), + &hash_len) != PSA_SUCCESS) { + return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED; + } +#else /* MBEDTLS_USE_PSA_CRYPTO */ + ret = mbedtls_md(mbedtls_md_info_from_type(ctx->md_alg), c, len, hash); + if (ret != 0) { + return ret; + } +#endif + if ((ret = mbedtls_pk_sign(ctx->key, ctx->md_alg, hash, 0, + sig, sig_size, &sig_len, + f_rng, p_rng)) != 0) { + return ret; + } + + if (mbedtls_pk_can_do(ctx->key, MBEDTLS_PK_RSA)) { + pk_alg = MBEDTLS_PK_RSA; + } else if (mbedtls_pk_can_do(ctx->key, MBEDTLS_PK_ECDSA)) { + pk_alg = MBEDTLS_PK_ECDSA; + } else { + return MBEDTLS_ERR_X509_INVALID_ALG; + } + + if ((ret = mbedtls_oid_get_oid_by_sig_alg(pk_alg, ctx->md_alg, + &sig_oid, &sig_oid_len)) != 0) { + return ret; + } + + /* + * Move the written CSR data to the start of buf to create space for + * writing the signature into buf. + */ + memmove(buf, c, len); + + /* + * Write sig and its OID into buf backwards from the end of buf. + * Note: mbedtls_x509_write_sig will check for c2 - ( buf + len ) < sig_len + * and return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL if needed. + */ + c2 = buf + size; + MBEDTLS_ASN1_CHK_ADD(sig_and_oid_len, + mbedtls_x509_write_sig(&c2, buf + len, sig_oid, sig_oid_len, + sig, sig_len)); + + /* + * Compact the space between the CSR data and signature by moving the + * CSR data to the start of the signature. + */ + c2 -= len; + memmove(c2, buf, len); + + /* ASN encode the total size and tag the CSR data with it. */ + len += sig_and_oid_len; + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c2, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, + mbedtls_asn1_write_tag( + &c2, buf, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)); + + /* Zero the unused bytes at the start of buf */ + memset(buf, 0, c2 - buf); + + return (int) len; +} + +int mbedtls_x509write_csr_der(mbedtls_x509write_csr *ctx, unsigned char *buf, + size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret; + unsigned char *sig; + + if ((sig = mbedtls_calloc(1, MBEDTLS_PK_SIGNATURE_MAX_SIZE)) == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + ret = x509write_csr_der_internal(ctx, buf, size, + sig, MBEDTLS_PK_SIGNATURE_MAX_SIZE, + f_rng, p_rng); + + mbedtls_free(sig); + + return ret; +} + +#define PEM_BEGIN_CSR "-----BEGIN CERTIFICATE REQUEST-----\n" +#define PEM_END_CSR "-----END CERTIFICATE REQUEST-----\n" + +#if defined(MBEDTLS_PEM_WRITE_C) +int mbedtls_x509write_csr_pem(mbedtls_x509write_csr *ctx, unsigned char *buf, size_t size, + int (*f_rng)(void *, unsigned char *, size_t), + void *p_rng) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t olen = 0; + + if ((ret = mbedtls_x509write_csr_der(ctx, buf, size, + f_rng, p_rng)) < 0) { + return ret; + } + + if ((ret = mbedtls_pem_write_buffer(PEM_BEGIN_CSR, PEM_END_CSR, + buf + size - ret, + ret, buf, size, &olen)) != 0) { + return ret; + } + + return 0; +} +#endif /* MBEDTLS_PEM_WRITE_C */ + +#endif /* MBEDTLS_X509_CSR_WRITE_C */ diff --git a/r5dev/thirdparty/nlohmann/adl_serializer.hpp b/r5dev/thirdparty/nlohmann/adl_serializer.hpp deleted file mode 100644 index f77f9447..00000000 --- a/r5dev/thirdparty/nlohmann/adl_serializer.hpp +++ /dev/null @@ -1,55 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN - -/// @sa https://json.nlohmann.me/api/adl_serializer/ -template -struct adl_serializer -{ - /// @brief convert a JSON value to any value type - /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ - template - static auto from_json(BasicJsonType && j, TargetType& val) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), val))) - -> decltype(::nlohmann::from_json(std::forward(j), val), void()) - { - ::nlohmann::from_json(std::forward(j), val); - } - - /// @brief convert a JSON value to any value type - /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ - template - static auto from_json(BasicJsonType && j) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) - -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) - { - return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); - } - - /// @brief convert any value type to a JSON value - /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/ - template - static auto to_json(BasicJsonType& j, TargetType && val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) - -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) - { - ::nlohmann::to_json(j, std::forward(val)); - } -}; - -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/byte_container_with_subtype.hpp b/r5dev/thirdparty/nlohmann/byte_container_with_subtype.hpp deleted file mode 100644 index 1031cdcf..00000000 --- a/r5dev/thirdparty/nlohmann/byte_container_with_subtype.hpp +++ /dev/null @@ -1,103 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // uint8_t, uint64_t -#include // tie -#include // move - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN - -/// @brief an internal type for a backed binary type -/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/ -template -class byte_container_with_subtype : public BinaryType -{ - public: - using container_type = BinaryType; - using subtype_type = std::uint64_t; - - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ - byte_container_with_subtype() noexcept(noexcept(container_type())) - : container_type() - {} - - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ - byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) - : container_type(b) - {} - - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ - byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) - : container_type(std::move(b)) - {} - - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ - byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) - : container_type(b) - , m_subtype(subtype_) - , m_has_subtype(true) - {} - - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ - byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) - : container_type(std::move(b)) - , m_subtype(subtype_) - , m_has_subtype(true) - {} - - bool operator==(const byte_container_with_subtype& rhs) const - { - return std::tie(static_cast(*this), m_subtype, m_has_subtype) == - std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); - } - - bool operator!=(const byte_container_with_subtype& rhs) const - { - return !(rhs == *this); - } - - /// @brief sets the binary subtype - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/ - void set_subtype(subtype_type subtype_) noexcept - { - m_subtype = subtype_; - m_has_subtype = true; - } - - /// @brief return the binary subtype - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/ - constexpr subtype_type subtype() const noexcept - { - return m_has_subtype ? m_subtype : static_cast(-1); - } - - /// @brief return whether the value has a subtype - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/ - constexpr bool has_subtype() const noexcept - { - return m_has_subtype; - } - - /// @brief clears the binary subtype - /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/ - void clear_subtype() noexcept - { - m_subtype = 0; - m_has_subtype = false; - } - - private: - subtype_type m_subtype = 0; - bool m_has_subtype = false; -}; - -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/abi_macros.hpp b/r5dev/thirdparty/nlohmann/detail/abi_macros.hpp deleted file mode 100644 index 0d3108d1..00000000 --- a/r5dev/thirdparty/nlohmann/detail/abi_macros.hpp +++ /dev/null @@ -1,100 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -// This file contains all macro definitions affecting or depending on the ABI - -#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK - #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2 - #warning "Already included a different version of the library!" - #endif - #endif -#endif - -#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 2 // NOLINT(modernize-macro-to-enum) - -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 -#endif - -#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 -#endif - -#if JSON_DIAGNOSTICS - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag -#else - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS -#endif - -#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp -#else - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION - #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 -#endif - -// Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) - -#define NLOHMANN_JSON_ABI_TAGS \ - NLOHMANN_JSON_ABI_TAGS_CONCAT( \ - NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) - -// Construct the namespace version component -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ - _v ## major ## _ ## minor ## _ ## patch -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) - -#if NLOHMANN_JSON_NAMESPACE_NO_VERSION -#define NLOHMANN_JSON_NAMESPACE_VERSION -#else -#define NLOHMANN_JSON_NAMESPACE_VERSION \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ - NLOHMANN_JSON_VERSION_MINOR, \ - NLOHMANN_JSON_VERSION_PATCH) -#endif - -// Combine namespace components -#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b -#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ - NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) - -#ifndef NLOHMANN_JSON_NAMESPACE -#define NLOHMANN_JSON_NAMESPACE \ - nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN -#define NLOHMANN_JSON_NAMESPACE_BEGIN \ - namespace nlohmann \ - { \ - inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) \ - { -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_END -#define NLOHMANN_JSON_NAMESPACE_END \ - } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ - } // namespace nlohmann -#endif diff --git a/r5dev/thirdparty/nlohmann/detail/conversions/from_json.hpp b/r5dev/thirdparty/nlohmann/detail/conversions/from_json.hpp deleted file mode 100644 index c6299aa0..00000000 --- a/r5dev/thirdparty/nlohmann/detail/conversions/from_json.hpp +++ /dev/null @@ -1,497 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // transform -#include // array -#include // forward_list -#include // inserter, front_inserter, end -#include // map -#include // string -#include // tuple, make_tuple -#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible -#include // unordered_map -#include // pair, declval -#include // valarray - -#include -#include -#include -#include -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_null())) - { - JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j)); - } - n = nullptr; -} - -// overloads for basic_json template parameters -template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < std::is_arithmetic::value&& - !std::is_same::value, - int > = 0 > -void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) -{ - switch (static_cast(j)) - { - case value_t::number_unsigned: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_integer: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_float: - { - val = static_cast(*j.template get_ptr()); - break; - } - - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::boolean: - case value_t::binary: - case value_t::discarded: - default: - JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); - } -} - -template -inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) - { - JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j)); - } - b = *j.template get_ptr(); -} - -template -inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_string())) - { - JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); - } - s = *j.template get_ptr(); -} - -template < - typename BasicJsonType, typename StringType, - enable_if_t < - std::is_assignable::value - && is_detected_exact::value - && !std::is_same::value - && !is_json_ref::value, int > = 0 > -inline void from_json(const BasicJsonType& j, StringType& s) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_string())) - { - JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); - } - - s = *j.template get_ptr(); -} - -template -inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) -{ - get_arithmetic_value(j, val); -} - -template -inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) -{ - get_arithmetic_value(j, val); -} - -template -inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) -{ - get_arithmetic_value(j, val); -} - -#if !JSON_DISABLE_ENUM_SERIALIZATION -template::value, int> = 0> -inline void from_json(const BasicJsonType& j, EnumType& e) -{ - typename std::underlying_type::type val; - get_arithmetic_value(j, val); - e = static_cast(val); -} -#endif // JSON_DISABLE_ENUM_SERIALIZATION - -// forward_list doesn't have an insert method -template::value, int> = 0> -inline void from_json(const BasicJsonType& j, std::forward_list& l) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); - } - l.clear(); - std::transform(j.rbegin(), j.rend(), - std::front_inserter(l), [](const BasicJsonType & i) - { - return i.template get(); - }); -} - -// valarray doesn't have an insert method -template::value, int> = 0> -inline void from_json(const BasicJsonType& j, std::valarray& l) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); - } - l.resize(j.size()); - std::transform(j.begin(), j.end(), std::begin(l), - [](const BasicJsonType & elem) - { - return elem.template get(); - }); -} - -template -auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) --> decltype(j.template get(), void()) -{ - for (std::size_t i = 0; i < N; ++i) - { - arr[i] = j.at(i).template get(); - } -} - -template -inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) -{ - arr = *j.template get_ptr(); -} - -template -auto from_json_array_impl(const BasicJsonType& j, std::array& arr, - priority_tag<2> /*unused*/) --> decltype(j.template get(), void()) -{ - for (std::size_t i = 0; i < N; ++i) - { - arr[i] = j.at(i).template get(); - } -} - -template::value, - int> = 0> -auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) --> decltype( - arr.reserve(std::declval()), - j.template get(), - void()) -{ - using std::end; - - ConstructibleArrayType ret; - ret.reserve(j.size()); - std::transform(j.begin(), j.end(), - std::inserter(ret, end(ret)), [](const BasicJsonType & i) - { - // get() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get(); - }); - arr = std::move(ret); -} - -template::value, - int> = 0> -inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, - priority_tag<0> /*unused*/) -{ - using std::end; - - ConstructibleArrayType ret; - std::transform( - j.begin(), j.end(), std::inserter(ret, end(ret)), - [](const BasicJsonType & i) - { - // get() returns *this, this won't call a from_json - // method when value_type is BasicJsonType - return i.template get(); - }); - arr = std::move(ret); -} - -template < typename BasicJsonType, typename ConstructibleArrayType, - enable_if_t < - is_constructible_array_type::value&& - !is_constructible_object_type::value&& - !is_constructible_string_type::value&& - !std::is_same::value&& - !is_basic_json::value, - int > = 0 > -auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) --> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), -j.template get(), -void()) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); - } - - from_json_array_impl(j, arr, priority_tag<3> {}); -} - -template < typename BasicJsonType, typename T, std::size_t... Idx > -std::array from_json_inplace_array_impl(BasicJsonType&& j, - identity_tag> /*unused*/, index_sequence /*unused*/) -{ - return { { std::forward(j).at(Idx).template get()... } }; -} - -template < typename BasicJsonType, typename T, std::size_t N > -auto from_json(BasicJsonType&& j, identity_tag> tag) --> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); - } - - return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); -} - -template -inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) - { - JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j)); - } - - bin = *j.template get_ptr(); -} - -template::value, int> = 0> -inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_object())) - { - JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j)); - } - - ConstructibleObjectType ret; - const auto* inner_object = j.template get_ptr(); - using value_type = typename ConstructibleObjectType::value_type; - std::transform( - inner_object->begin(), inner_object->end(), - std::inserter(ret, ret.begin()), - [](typename BasicJsonType::object_t::value_type const & p) - { - return value_type(p.first, p.second.template get()); - }); - obj = std::move(ret); -} - -// overload for arithmetic types, not chosen for basic_json template arguments -// (BooleanType, etc..); note: Is it really necessary to provide explicit -// overloads for boolean_t etc. in case of a custom BooleanType which is not -// an arithmetic type? -template < typename BasicJsonType, typename ArithmeticType, - enable_if_t < - std::is_arithmetic::value&& - !std::is_same::value&& - !std::is_same::value&& - !std::is_same::value&& - !std::is_same::value, - int > = 0 > -inline void from_json(const BasicJsonType& j, ArithmeticType& val) -{ - switch (static_cast(j)) - { - case value_t::number_unsigned: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_integer: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::number_float: - { - val = static_cast(*j.template get_ptr()); - break; - } - case value_t::boolean: - { - val = static_cast(*j.template get_ptr()); - break; - } - - case value_t::null: - case value_t::object: - case value_t::array: - case value_t::string: - case value_t::binary: - case value_t::discarded: - default: - JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j)); - } -} - -template -std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) -{ - return std::make_tuple(std::forward(j).at(Idx).template get()...); -} - -template < typename BasicJsonType, class A1, class A2 > -std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) -{ - return {std::forward(j).at(0).template get(), - std::forward(j).at(1).template get()}; -} - -template -inline void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) -{ - p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); -} - -template -std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) -{ - return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); -} - -template -inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) -{ - t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); -} - -template -auto from_json(BasicJsonType&& j, TupleRelated&& t) --> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); - } - - return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); -} - -template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, - typename = enable_if_t < !std::is_constructible < - typename BasicJsonType::string_t, Key >::value >> -inline void from_json(const BasicJsonType& j, std::map& m) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); - } - m.clear(); - for (const auto& p : j) - { - if (JSON_HEDLEY_UNLIKELY(!p.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j)); - } - m.emplace(p.at(0).template get(), p.at(1).template get()); - } -} - -template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, - typename = enable_if_t < !std::is_constructible < - typename BasicJsonType::string_t, Key >::value >> -inline void from_json(const BasicJsonType& j, std::unordered_map& m) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j)); - } - m.clear(); - for (const auto& p : j) - { - if (JSON_HEDLEY_UNLIKELY(!p.is_array())) - { - JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j)); - } - m.emplace(p.at(0).template get(), p.at(1).template get()); - } -} - -#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM -template -inline void from_json(const BasicJsonType& j, std_fs::path& p) -{ - if (JSON_HEDLEY_UNLIKELY(!j.is_string())) - { - JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j)); - } - p = *j.template get_ptr(); -} -#endif - -struct from_json_fn -{ - template - auto operator()(const BasicJsonType& j, T&& val) const - noexcept(noexcept(from_json(j, std::forward(val)))) - -> decltype(from_json(j, std::forward(val))) - { - return from_json(j, std::forward(val)); - } -}; - -} // namespace detail - -#ifndef JSON_HAS_CPP_17 -/// namespace to hold default `from_json` function -/// to see why this is required: -/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html -namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) -{ -#endif -JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers) - detail::static_const::value; -#ifndef JSON_HAS_CPP_17 -} // namespace -#endif - -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/conversions/to_chars.hpp b/r5dev/thirdparty/nlohmann/detail/conversions/to_chars.hpp deleted file mode 100644 index febef932..00000000 --- a/r5dev/thirdparty/nlohmann/detail/conversions/to_chars.hpp +++ /dev/null @@ -1,1118 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2009 Florian Loitsch -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // array -#include // signbit, isfinite -#include // intN_t, uintN_t -#include // memcpy, memmove -#include // numeric_limits -#include // conditional - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/*! -@brief implements the Grisu2 algorithm for binary to decimal floating-point -conversion. - -This implementation is a slightly modified version of the reference -implementation which may be obtained from -http://florian.loitsch.com/publications (bench.tar.gz). - -The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. - -For a detailed description of the algorithm see: - -[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with - Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming - Language Design and Implementation, PLDI 2010 -[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", - Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language - Design and Implementation, PLDI 1996 -*/ -namespace dtoa_impl -{ - -template -Target reinterpret_bits(const Source source) -{ - static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); - - Target target; - std::memcpy(&target, &source, sizeof(Source)); - return target; -} - -struct diyfp // f * 2^e -{ - static constexpr int kPrecision = 64; // = q - - std::uint64_t f = 0; - int e = 0; - - constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} - - /*! - @brief returns x - y - @pre x.e == y.e and x.f >= y.f - */ - static diyfp sub(const diyfp& x, const diyfp& y) noexcept - { - JSON_ASSERT(x.e == y.e); - JSON_ASSERT(x.f >= y.f); - - return {x.f - y.f, x.e}; - } - - /*! - @brief returns x * y - @note The result is rounded. (Only the upper q bits are returned.) - */ - static diyfp mul(const diyfp& x, const diyfp& y) noexcept - { - static_assert(kPrecision == 64, "internal error"); - - // Computes: - // f = round((x.f * y.f) / 2^q) - // e = x.e + y.e + q - - // Emulate the 64-bit * 64-bit multiplication: - // - // p = u * v - // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) - // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) - // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) - // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) - // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) - // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) - // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) - // - // (Since Q might be larger than 2^32 - 1) - // - // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) - // - // (Q_hi + H does not overflow a 64-bit int) - // - // = p_lo + 2^64 p_hi - - const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; - const std::uint64_t u_hi = x.f >> 32u; - const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; - const std::uint64_t v_hi = y.f >> 32u; - - const std::uint64_t p0 = u_lo * v_lo; - const std::uint64_t p1 = u_lo * v_hi; - const std::uint64_t p2 = u_hi * v_lo; - const std::uint64_t p3 = u_hi * v_hi; - - const std::uint64_t p0_hi = p0 >> 32u; - const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; - const std::uint64_t p1_hi = p1 >> 32u; - const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; - const std::uint64_t p2_hi = p2 >> 32u; - - std::uint64_t Q = p0_hi + p1_lo + p2_lo; - - // The full product might now be computed as - // - // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) - // p_lo = p0_lo + (Q << 32) - // - // But in this particular case here, the full p_lo is not required. - // Effectively we only need to add the highest bit in p_lo to p_hi (and - // Q_hi + 1 does not overflow). - - Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up - - const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); - - return {h, x.e + y.e + 64}; - } - - /*! - @brief normalize x such that the significand is >= 2^(q-1) - @pre x.f != 0 - */ - static diyfp normalize(diyfp x) noexcept - { - JSON_ASSERT(x.f != 0); - - while ((x.f >> 63u) == 0) - { - x.f <<= 1u; - x.e--; - } - - return x; - } - - /*! - @brief normalize x such that the result has the exponent E - @pre e >= x.e and the upper e - x.e bits of x.f must be zero. - */ - static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept - { - const int delta = x.e - target_exponent; - - JSON_ASSERT(delta >= 0); - JSON_ASSERT(((x.f << delta) >> delta) == x.f); - - return {x.f << delta, target_exponent}; - } -}; - -struct boundaries -{ - diyfp w; - diyfp minus; - diyfp plus; -}; - -/*! -Compute the (normalized) diyfp representing the input number 'value' and its -boundaries. - -@pre value must be finite and positive -*/ -template -boundaries compute_boundaries(FloatType value) -{ - JSON_ASSERT(std::isfinite(value)); - JSON_ASSERT(value > 0); - - // Convert the IEEE representation into a diyfp. - // - // If v is denormal: - // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) - // If v is normalized: - // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) - - static_assert(std::numeric_limits::is_iec559, - "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); - - constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) - constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); - constexpr int kMinExp = 1 - kBias; - constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) - - using bits_type = typename std::conditional::type; - - const auto bits = static_cast(reinterpret_bits(value)); - const std::uint64_t E = bits >> (kPrecision - 1); - const std::uint64_t F = bits & (kHiddenBit - 1); - - const bool is_denormal = E == 0; - const diyfp v = is_denormal - ? diyfp(F, kMinExp) - : diyfp(F + kHiddenBit, static_cast(E) - kBias); - - // Compute the boundaries m- and m+ of the floating-point value - // v = f * 2^e. - // - // Determine v- and v+, the floating-point predecessor and successor if v, - // respectively. - // - // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) - // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) - // - // v+ = v + 2^e - // - // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ - // between m- and m+ round to v, regardless of how the input rounding - // algorithm breaks ties. - // - // ---+-------------+-------------+-------------+-------------+--- (A) - // v- m- v m+ v+ - // - // -----------------+------+------+-------------+-------------+--- (B) - // v- m- v m+ v+ - - const bool lower_boundary_is_closer = F == 0 && E > 1; - const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); - const diyfp m_minus = lower_boundary_is_closer - ? diyfp(4 * v.f - 1, v.e - 2) // (B) - : diyfp(2 * v.f - 1, v.e - 1); // (A) - - // Determine the normalized w+ = m+. - const diyfp w_plus = diyfp::normalize(m_plus); - - // Determine w- = m- such that e_(w-) = e_(w+). - const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); - - return {diyfp::normalize(v), w_minus, w_plus}; -} - -// Given normalized diyfp w, Grisu needs to find a (normalized) cached -// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies -// within a certain range [alpha, gamma] (Definition 3.2 from [1]) -// -// alpha <= e = e_c + e_w + q <= gamma -// -// or -// -// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q -// <= f_c * f_w * 2^gamma -// -// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies -// -// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma -// -// or -// -// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) -// -// The choice of (alpha,gamma) determines the size of the table and the form of -// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well -// in practice: -// -// The idea is to cut the number c * w = f * 2^e into two parts, which can be -// processed independently: An integral part p1, and a fractional part p2: -// -// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e -// = (f div 2^-e) + (f mod 2^-e) * 2^e -// = p1 + p2 * 2^e -// -// The conversion of p1 into decimal form requires a series of divisions and -// modulos by (a power of) 10. These operations are faster for 32-bit than for -// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be -// achieved by choosing -// -// -e >= 32 or e <= -32 := gamma -// -// In order to convert the fractional part -// -// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... -// -// into decimal form, the fraction is repeatedly multiplied by 10 and the digits -// d[-i] are extracted in order: -// -// (10 * p2) div 2^-e = d[-1] -// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... -// -// The multiplication by 10 must not overflow. It is sufficient to choose -// -// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. -// -// Since p2 = f mod 2^-e < 2^-e, -// -// -e <= 60 or e >= -60 := alpha - -constexpr int kAlpha = -60; -constexpr int kGamma = -32; - -struct cached_power // c = f * 2^e ~= 10^k -{ - std::uint64_t f; - int e; - int k; -}; - -/*! -For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached -power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c -satisfies (Definition 3.2 from [1]) - - alpha <= e_c + e + q <= gamma. -*/ -inline cached_power get_cached_power_for_binary_exponent(int e) -{ - // Now - // - // alpha <= e_c + e + q <= gamma (1) - // ==> f_c * 2^alpha <= c * 2^e * 2^q - // - // and since the c's are normalized, 2^(q-1) <= f_c, - // - // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) - // ==> 2^(alpha - e - 1) <= c - // - // If c were an exact power of ten, i.e. c = 10^k, one may determine k as - // - // k = ceil( log_10( 2^(alpha - e - 1) ) ) - // = ceil( (alpha - e - 1) * log_10(2) ) - // - // From the paper: - // "In theory the result of the procedure could be wrong since c is rounded, - // and the computation itself is approximated [...]. In practice, however, - // this simple function is sufficient." - // - // For IEEE double precision floating-point numbers converted into - // normalized diyfp's w = f * 2^e, with q = 64, - // - // e >= -1022 (min IEEE exponent) - // -52 (p - 1) - // -52 (p - 1, possibly normalize denormal IEEE numbers) - // -11 (normalize the diyfp) - // = -1137 - // - // and - // - // e <= +1023 (max IEEE exponent) - // -52 (p - 1) - // -11 (normalize the diyfp) - // = 960 - // - // This binary exponent range [-1137,960] results in a decimal exponent - // range [-307,324]. One does not need to store a cached power for each - // k in this range. For each such k it suffices to find a cached power - // such that the exponent of the product lies in [alpha,gamma]. - // This implies that the difference of the decimal exponents of adjacent - // table entries must be less than or equal to - // - // floor( (gamma - alpha) * log_10(2) ) = 8. - // - // (A smaller distance gamma-alpha would require a larger table.) - - // NB: - // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. - - constexpr int kCachedPowersMinDecExp = -300; - constexpr int kCachedPowersDecStep = 8; - - static constexpr std::array kCachedPowers = - { - { - { 0xAB70FE17C79AC6CA, -1060, -300 }, - { 0xFF77B1FCBEBCDC4F, -1034, -292 }, - { 0xBE5691EF416BD60C, -1007, -284 }, - { 0x8DD01FAD907FFC3C, -980, -276 }, - { 0xD3515C2831559A83, -954, -268 }, - { 0x9D71AC8FADA6C9B5, -927, -260 }, - { 0xEA9C227723EE8BCB, -901, -252 }, - { 0xAECC49914078536D, -874, -244 }, - { 0x823C12795DB6CE57, -847, -236 }, - { 0xC21094364DFB5637, -821, -228 }, - { 0x9096EA6F3848984F, -794, -220 }, - { 0xD77485CB25823AC7, -768, -212 }, - { 0xA086CFCD97BF97F4, -741, -204 }, - { 0xEF340A98172AACE5, -715, -196 }, - { 0xB23867FB2A35B28E, -688, -188 }, - { 0x84C8D4DFD2C63F3B, -661, -180 }, - { 0xC5DD44271AD3CDBA, -635, -172 }, - { 0x936B9FCEBB25C996, -608, -164 }, - { 0xDBAC6C247D62A584, -582, -156 }, - { 0xA3AB66580D5FDAF6, -555, -148 }, - { 0xF3E2F893DEC3F126, -529, -140 }, - { 0xB5B5ADA8AAFF80B8, -502, -132 }, - { 0x87625F056C7C4A8B, -475, -124 }, - { 0xC9BCFF6034C13053, -449, -116 }, - { 0x964E858C91BA2655, -422, -108 }, - { 0xDFF9772470297EBD, -396, -100 }, - { 0xA6DFBD9FB8E5B88F, -369, -92 }, - { 0xF8A95FCF88747D94, -343, -84 }, - { 0xB94470938FA89BCF, -316, -76 }, - { 0x8A08F0F8BF0F156B, -289, -68 }, - { 0xCDB02555653131B6, -263, -60 }, - { 0x993FE2C6D07B7FAC, -236, -52 }, - { 0xE45C10C42A2B3B06, -210, -44 }, - { 0xAA242499697392D3, -183, -36 }, - { 0xFD87B5F28300CA0E, -157, -28 }, - { 0xBCE5086492111AEB, -130, -20 }, - { 0x8CBCCC096F5088CC, -103, -12 }, - { 0xD1B71758E219652C, -77, -4 }, - { 0x9C40000000000000, -50, 4 }, - { 0xE8D4A51000000000, -24, 12 }, - { 0xAD78EBC5AC620000, 3, 20 }, - { 0x813F3978F8940984, 30, 28 }, - { 0xC097CE7BC90715B3, 56, 36 }, - { 0x8F7E32CE7BEA5C70, 83, 44 }, - { 0xD5D238A4ABE98068, 109, 52 }, - { 0x9F4F2726179A2245, 136, 60 }, - { 0xED63A231D4C4FB27, 162, 68 }, - { 0xB0DE65388CC8ADA8, 189, 76 }, - { 0x83C7088E1AAB65DB, 216, 84 }, - { 0xC45D1DF942711D9A, 242, 92 }, - { 0x924D692CA61BE758, 269, 100 }, - { 0xDA01EE641A708DEA, 295, 108 }, - { 0xA26DA3999AEF774A, 322, 116 }, - { 0xF209787BB47D6B85, 348, 124 }, - { 0xB454E4A179DD1877, 375, 132 }, - { 0x865B86925B9BC5C2, 402, 140 }, - { 0xC83553C5C8965D3D, 428, 148 }, - { 0x952AB45CFA97A0B3, 455, 156 }, - { 0xDE469FBD99A05FE3, 481, 164 }, - { 0xA59BC234DB398C25, 508, 172 }, - { 0xF6C69A72A3989F5C, 534, 180 }, - { 0xB7DCBF5354E9BECE, 561, 188 }, - { 0x88FCF317F22241E2, 588, 196 }, - { 0xCC20CE9BD35C78A5, 614, 204 }, - { 0x98165AF37B2153DF, 641, 212 }, - { 0xE2A0B5DC971F303A, 667, 220 }, - { 0xA8D9D1535CE3B396, 694, 228 }, - { 0xFB9B7CD9A4A7443C, 720, 236 }, - { 0xBB764C4CA7A44410, 747, 244 }, - { 0x8BAB8EEFB6409C1A, 774, 252 }, - { 0xD01FEF10A657842C, 800, 260 }, - { 0x9B10A4E5E9913129, 827, 268 }, - { 0xE7109BFBA19C0C9D, 853, 276 }, - { 0xAC2820D9623BF429, 880, 284 }, - { 0x80444B5E7AA7CF85, 907, 292 }, - { 0xBF21E44003ACDD2D, 933, 300 }, - { 0x8E679C2F5E44FF8F, 960, 308 }, - { 0xD433179D9C8CB841, 986, 316 }, - { 0x9E19DB92B4E31BA9, 1013, 324 }, - } - }; - - // This computation gives exactly the same results for k as - // k = ceil((kAlpha - e - 1) * 0.30102999566398114) - // for |e| <= 1500, but doesn't require floating-point operations. - // NB: log_10(2) ~= 78913 / 2^18 - JSON_ASSERT(e >= -1500); - JSON_ASSERT(e <= 1500); - const int f = kAlpha - e - 1; - const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); - - const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; - JSON_ASSERT(index >= 0); - JSON_ASSERT(static_cast(index) < kCachedPowers.size()); - - const cached_power cached = kCachedPowers[static_cast(index)]; - JSON_ASSERT(kAlpha <= cached.e + e + 64); - JSON_ASSERT(kGamma >= cached.e + e + 64); - - return cached; -} - -/*! -For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. -For n == 0, returns 1 and sets pow10 := 1. -*/ -inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) -{ - // LCOV_EXCL_START - if (n >= 1000000000) - { - pow10 = 1000000000; - return 10; - } - // LCOV_EXCL_STOP - if (n >= 100000000) - { - pow10 = 100000000; - return 9; - } - if (n >= 10000000) - { - pow10 = 10000000; - return 8; - } - if (n >= 1000000) - { - pow10 = 1000000; - return 7; - } - if (n >= 100000) - { - pow10 = 100000; - return 6; - } - if (n >= 10000) - { - pow10 = 10000; - return 5; - } - if (n >= 1000) - { - pow10 = 1000; - return 4; - } - if (n >= 100) - { - pow10 = 100; - return 3; - } - if (n >= 10) - { - pow10 = 10; - return 2; - } - - pow10 = 1; - return 1; -} - -inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, - std::uint64_t rest, std::uint64_t ten_k) -{ - JSON_ASSERT(len >= 1); - JSON_ASSERT(dist <= delta); - JSON_ASSERT(rest <= delta); - JSON_ASSERT(ten_k > 0); - - // <--------------------------- delta ----> - // <---- dist ---------> - // --------------[------------------+-------------------]-------------- - // M- w M+ - // - // ten_k - // <------> - // <---- rest ----> - // --------------[------------------+----+--------------]-------------- - // w V - // = buf * 10^k - // - // ten_k represents a unit-in-the-last-place in the decimal representation - // stored in buf. - // Decrement buf by ten_k while this takes buf closer to w. - - // The tests are written in this order to avoid overflow in unsigned - // integer arithmetic. - - while (rest < dist - && delta - rest >= ten_k - && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) - { - JSON_ASSERT(buf[len - 1] != '0'); - buf[len - 1]--; - rest += ten_k; - } -} - -/*! -Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. -M- and M+ must be normalized and share the same exponent -60 <= e <= -32. -*/ -inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, - diyfp M_minus, diyfp w, diyfp M_plus) -{ - static_assert(kAlpha >= -60, "internal error"); - static_assert(kGamma <= -32, "internal error"); - - // Generates the digits (and the exponent) of a decimal floating-point - // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's - // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. - // - // <--------------------------- delta ----> - // <---- dist ---------> - // --------------[------------------+-------------------]-------------- - // M- w M+ - // - // Grisu2 generates the digits of M+ from left to right and stops as soon as - // V is in [M-,M+]. - - JSON_ASSERT(M_plus.e >= kAlpha); - JSON_ASSERT(M_plus.e <= kGamma); - - std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) - std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) - - // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): - // - // M+ = f * 2^e - // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e - // = ((p1 ) * 2^-e + (p2 )) * 2^e - // = p1 + p2 * 2^e - - const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); - - auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) - std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e - - // 1) - // - // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] - - JSON_ASSERT(p1 > 0); - - std::uint32_t pow10{}; - const int k = find_largest_pow10(p1, pow10); - - // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) - // - // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) - // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) - // - // M+ = p1 + p2 * 2^e - // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e - // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e - // = d[k-1] * 10^(k-1) + ( rest) * 2^e - // - // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) - // - // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] - // - // but stop as soon as - // - // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e - - int n = k; - while (n > 0) - { - // Invariants: - // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) - // pow10 = 10^(n-1) <= p1 < 10^n - // - const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) - const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) - // - // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e - // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) - // - JSON_ASSERT(d <= 9); - buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d - // - // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) - // - p1 = r; - n--; - // - // M+ = buffer * 10^n + (p1 + p2 * 2^e) - // pow10 = 10^n - // - - // Now check if enough digits have been generated. - // Compute - // - // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e - // - // Note: - // Since rest and delta share the same exponent e, it suffices to - // compare the significands. - const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; - if (rest <= delta) - { - // V = buffer * 10^n, with M- <= V <= M+. - - decimal_exponent += n; - - // We may now just stop. But instead look if the buffer could be - // decremented to bring V closer to w. - // - // pow10 = 10^n is now 1 ulp in the decimal representation V. - // The rounding procedure works with diyfp's with an implicit - // exponent of e. - // - // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e - // - const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; - grisu2_round(buffer, length, dist, delta, rest, ten_n); - - return; - } - - pow10 /= 10; - // - // pow10 = 10^(n-1) <= p1 < 10^n - // Invariants restored. - } - - // 2) - // - // The digits of the integral part have been generated: - // - // M+ = d[k-1]...d[1]d[0] + p2 * 2^e - // = buffer + p2 * 2^e - // - // Now generate the digits of the fractional part p2 * 2^e. - // - // Note: - // No decimal point is generated: the exponent is adjusted instead. - // - // p2 actually represents the fraction - // - // p2 * 2^e - // = p2 / 2^-e - // = d[-1] / 10^1 + d[-2] / 10^2 + ... - // - // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) - // - // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m - // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) - // - // using - // - // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) - // = ( d) * 2^-e + ( r) - // - // or - // 10^m * p2 * 2^e = d + r * 2^e - // - // i.e. - // - // M+ = buffer + p2 * 2^e - // = buffer + 10^-m * (d + r * 2^e) - // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e - // - // and stop as soon as 10^-m * r * 2^e <= delta * 2^e - - JSON_ASSERT(p2 > delta); - - int m = 0; - for (;;) - { - // Invariant: - // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e - // = buffer * 10^-m + 10^-m * (p2 ) * 2^e - // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e - // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e - // - JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); - p2 *= 10; - const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e - const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e - // - // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e - // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) - // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e - // - JSON_ASSERT(d <= 9); - buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d - // - // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e - // - p2 = r; - m++; - // - // M+ = buffer * 10^-m + 10^-m * p2 * 2^e - // Invariant restored. - - // Check if enough digits have been generated. - // - // 10^-m * p2 * 2^e <= delta * 2^e - // p2 * 2^e <= 10^m * delta * 2^e - // p2 <= 10^m * delta - delta *= 10; - dist *= 10; - if (p2 <= delta) - { - break; - } - } - - // V = buffer * 10^-m, with M- <= V <= M+. - - decimal_exponent -= m; - - // 1 ulp in the decimal representation is now 10^-m. - // Since delta and dist are now scaled by 10^m, we need to do the - // same with ulp in order to keep the units in sync. - // - // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e - // - const std::uint64_t ten_m = one.f; - grisu2_round(buffer, length, dist, delta, p2, ten_m); - - // By construction this algorithm generates the shortest possible decimal - // number (Loitsch, Theorem 6.2) which rounds back to w. - // For an input number of precision p, at least - // - // N = 1 + ceil(p * log_10(2)) - // - // decimal digits are sufficient to identify all binary floating-point - // numbers (Matula, "In-and-Out conversions"). - // This implies that the algorithm does not produce more than N decimal - // digits. - // - // N = 17 for p = 53 (IEEE double precision) - // N = 9 for p = 24 (IEEE single precision) -} - -/*! -v = buf * 10^decimal_exponent -len is the length of the buffer (number of decimal digits) -The buffer must be large enough, i.e. >= max_digits10. -*/ -JSON_HEDLEY_NON_NULL(1) -inline void grisu2(char* buf, int& len, int& decimal_exponent, - diyfp m_minus, diyfp v, diyfp m_plus) -{ - JSON_ASSERT(m_plus.e == m_minus.e); - JSON_ASSERT(m_plus.e == v.e); - - // --------(-----------------------+-----------------------)-------- (A) - // m- v m+ - // - // --------------------(-----------+-----------------------)-------- (B) - // m- v m+ - // - // First scale v (and m- and m+) such that the exponent is in the range - // [alpha, gamma]. - - const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); - - const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k - - // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] - const diyfp w = diyfp::mul(v, c_minus_k); - const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); - const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); - - // ----(---+---)---------------(---+---)---------------(---+---)---- - // w- w w+ - // = c*m- = c*v = c*m+ - // - // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and - // w+ are now off by a small amount. - // In fact: - // - // w - v * 10^k < 1 ulp - // - // To account for this inaccuracy, add resp. subtract 1 ulp. - // - // --------+---[---------------(---+---)---------------]---+-------- - // w- M- w M+ w+ - // - // Now any number in [M-, M+] (bounds included) will round to w when input, - // regardless of how the input rounding algorithm breaks ties. - // - // And digit_gen generates the shortest possible such number in [M-, M+]. - // Note that this does not mean that Grisu2 always generates the shortest - // possible number in the interval (m-, m+). - const diyfp M_minus(w_minus.f + 1, w_minus.e); - const diyfp M_plus (w_plus.f - 1, w_plus.e ); - - decimal_exponent = -cached.k; // = -(-k) = k - - grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); -} - -/*! -v = buf * 10^decimal_exponent -len is the length of the buffer (number of decimal digits) -The buffer must be large enough, i.e. >= max_digits10. -*/ -template -JSON_HEDLEY_NON_NULL(1) -void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) -{ - static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, - "internal error: not enough precision"); - - JSON_ASSERT(std::isfinite(value)); - JSON_ASSERT(value > 0); - - // If the neighbors (and boundaries) of 'value' are always computed for double-precision - // numbers, all float's can be recovered using strtod (and strtof). However, the resulting - // decimal representations are not exactly "short". - // - // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) - // says "value is converted to a string as if by std::sprintf in the default ("C") locale" - // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars' - // does. - // On the other hand, the documentation for 'std::to_chars' requires that "parsing the - // representation using the corresponding std::from_chars function recovers value exactly". That - // indicates that single precision floating-point numbers should be recovered using - // 'std::strtof'. - // - // NB: If the neighbors are computed for single-precision numbers, there is a single float - // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision - // value is off by 1 ulp. -#if 0 - const boundaries w = compute_boundaries(static_cast(value)); -#else - const boundaries w = compute_boundaries(value); -#endif - - grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); -} - -/*! -@brief appends a decimal representation of e to buf -@return a pointer to the element following the exponent. -@pre -1000 < e < 1000 -*/ -JSON_HEDLEY_NON_NULL(1) -JSON_HEDLEY_RETURNS_NON_NULL -inline char* append_exponent(char* buf, int e) -{ - JSON_ASSERT(e > -1000); - JSON_ASSERT(e < 1000); - - if (e < 0) - { - e = -e; - *buf++ = '-'; - } - else - { - *buf++ = '+'; - } - - auto k = static_cast(e); - if (k < 10) - { - // Always print at least two digits in the exponent. - // This is for compatibility with printf("%g"). - *buf++ = '0'; - *buf++ = static_cast('0' + k); - } - else if (k < 100) - { - *buf++ = static_cast('0' + k / 10); - k %= 10; - *buf++ = static_cast('0' + k); - } - else - { - *buf++ = static_cast('0' + k / 100); - k %= 100; - *buf++ = static_cast('0' + k / 10); - k %= 10; - *buf++ = static_cast('0' + k); - } - - return buf; -} - -/*! -@brief prettify v = buf * 10^decimal_exponent - -If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point -notation. Otherwise it will be printed in exponential notation. - -@pre min_exp < 0 -@pre max_exp > 0 -*/ -JSON_HEDLEY_NON_NULL(1) -JSON_HEDLEY_RETURNS_NON_NULL -inline char* format_buffer(char* buf, int len, int decimal_exponent, - int min_exp, int max_exp) -{ - JSON_ASSERT(min_exp < 0); - JSON_ASSERT(max_exp > 0); - - const int k = len; - const int n = len + decimal_exponent; - - // v = buf * 10^(n-k) - // k is the length of the buffer (number of decimal digits) - // n is the position of the decimal point relative to the start of the buffer. - - if (k <= n && n <= max_exp) - { - // digits[000] - // len <= max_exp + 2 - - std::memset(buf + k, '0', static_cast(n) - static_cast(k)); - // Make it look like a floating-point number (#362, #378) - buf[n + 0] = '.'; - buf[n + 1] = '0'; - return buf + (static_cast(n) + 2); - } - - if (0 < n && n <= max_exp) - { - // dig.its - // len <= max_digits10 + 1 - - JSON_ASSERT(k > n); - - std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); - buf[n] = '.'; - return buf + (static_cast(k) + 1U); - } - - if (min_exp < n && n <= 0) - { - // 0.[000]digits - // len <= 2 + (-min_exp - 1) + max_digits10 - - std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); - buf[0] = '0'; - buf[1] = '.'; - std::memset(buf + 2, '0', static_cast(-n)); - return buf + (2U + static_cast(-n) + static_cast(k)); - } - - if (k == 1) - { - // dE+123 - // len <= 1 + 5 - - buf += 1; - } - else - { - // d.igitsE+123 - // len <= max_digits10 + 1 + 5 - - std::memmove(buf + 2, buf + 1, static_cast(k) - 1); - buf[1] = '.'; - buf += 1 + static_cast(k); - } - - *buf++ = 'e'; - return append_exponent(buf, n - 1); -} - -} // namespace dtoa_impl - -/*! -@brief generates a decimal representation of the floating-point number value in [first, last). - -The format of the resulting decimal representation is similar to printf's %g -format. Returns an iterator pointing past-the-end of the decimal representation. - -@note The input number must be finite, i.e. NaN's and Inf's are not supported. -@note The buffer must be large enough. -@note The result is NOT null-terminated. -*/ -template -JSON_HEDLEY_NON_NULL(1, 2) -JSON_HEDLEY_RETURNS_NON_NULL -char* to_chars(char* first, const char* last, FloatType value) -{ - static_cast(last); // maybe unused - fix warning - JSON_ASSERT(std::isfinite(value)); - - // Use signbit(value) instead of (value < 0) since signbit works for -0. - if (std::signbit(value)) - { - value = -value; - *first++ = '-'; - } - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - if (value == 0) // +-0 - { - *first++ = '0'; - // Make it look like a floating-point number (#362, #378) - *first++ = '.'; - *first++ = '0'; - return first; - } -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - - JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); - - // Compute v = buffer * 10^decimal_exponent. - // The decimal digits are stored in the buffer, which needs to be interpreted - // as an unsigned decimal integer. - // len is the length of the buffer, i.e. the number of decimal digits. - int len = 0; - int decimal_exponent = 0; - dtoa_impl::grisu2(first, len, decimal_exponent, value); - - JSON_ASSERT(len <= std::numeric_limits::max_digits10); - - // Format the buffer like printf("%.*g", prec, value) - constexpr int kMinExp = -4; - // Use digits10 here to increase compatibility with version 2. - constexpr int kMaxExp = std::numeric_limits::digits10; - - JSON_ASSERT(last - first >= kMaxExp + 2); - JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); - JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); - - return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/conversions/to_json.hpp b/r5dev/thirdparty/nlohmann/detail/conversions/to_json.hpp deleted file mode 100644 index b33d726b..00000000 --- a/r5dev/thirdparty/nlohmann/detail/conversions/to_json.hpp +++ /dev/null @@ -1,446 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // copy -#include // begin, end -#include // string -#include // tuple, get -#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type -#include // move, forward, declval, pair -#include // valarray -#include // vector - -#include -#include -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -////////////////// -// constructors // -////////////////// - -/* - * Note all external_constructor<>::construct functions need to call - * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an - * allocated value (e.g., a string). See bug issue - * https://github.com/nlohmann/json/issues/2865 for more information. - */ - -template struct external_constructor; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::boolean; - j.m_value = b; - j.assert_invariant(); - } -}; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::string; - j.m_value = s; - j.assert_invariant(); - } - - template - static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::string; - j.m_value = std::move(s); - j.assert_invariant(); - } - - template < typename BasicJsonType, typename CompatibleStringType, - enable_if_t < !std::is_same::value, - int > = 0 > - static void construct(BasicJsonType& j, const CompatibleStringType& str) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::string; - j.m_value.string = j.template create(str); - j.assert_invariant(); - } -}; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(b); - j.assert_invariant(); - } - - template - static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::binary; - j.m_value = typename BasicJsonType::binary_t(std::move(b)); - j.assert_invariant(); - } -}; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::number_float; - j.m_value = val; - j.assert_invariant(); - } -}; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::number_unsigned; - j.m_value = val; - j.assert_invariant(); - } -}; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::number_integer; - j.m_value = val; - j.assert_invariant(); - } -}; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::array; - j.m_value = arr; - j.set_parents(); - j.assert_invariant(); - } - - template - static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::array; - j.m_value = std::move(arr); - j.set_parents(); - j.assert_invariant(); - } - - template < typename BasicJsonType, typename CompatibleArrayType, - enable_if_t < !std::is_same::value, - int > = 0 > - static void construct(BasicJsonType& j, const CompatibleArrayType& arr) - { - using std::begin; - using std::end; - - j.m_value.destroy(j.m_type); - j.m_type = value_t::array; - j.m_value.array = j.template create(begin(arr), end(arr)); - j.set_parents(); - j.assert_invariant(); - } - - template - static void construct(BasicJsonType& j, const std::vector& arr) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::array; - j.m_value = value_t::array; - j.m_value.array->reserve(arr.size()); - for (const bool x : arr) - { - j.m_value.array->push_back(x); - j.set_parent(j.m_value.array->back()); - } - j.assert_invariant(); - } - - template::value, int> = 0> - static void construct(BasicJsonType& j, const std::valarray& arr) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::array; - j.m_value = value_t::array; - j.m_value.array->resize(arr.size()); - if (arr.size() > 0) - { - std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); - } - j.set_parents(); - j.assert_invariant(); - } -}; - -template<> -struct external_constructor -{ - template - static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::object; - j.m_value = obj; - j.set_parents(); - j.assert_invariant(); - } - - template - static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) - { - j.m_value.destroy(j.m_type); - j.m_type = value_t::object; - j.m_value = std::move(obj); - j.set_parents(); - j.assert_invariant(); - } - - template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < !std::is_same::value, int > = 0 > - static void construct(BasicJsonType& j, const CompatibleObjectType& obj) - { - using std::begin; - using std::end; - - j.m_value.destroy(j.m_type); - j.m_type = value_t::object; - j.m_value.object = j.template create(begin(obj), end(obj)); - j.set_parents(); - j.assert_invariant(); - } -}; - -///////////// -// to_json // -///////////// - -template::value, int> = 0> -inline void to_json(BasicJsonType& j, T b) noexcept -{ - external_constructor::construct(j, b); -} - -template < typename BasicJsonType, typename BoolRef, - enable_if_t < - ((std::is_same::reference, BoolRef>::value - && !std::is_same ::reference, typename BasicJsonType::boolean_t&>::value) - || (std::is_same::const_reference, BoolRef>::value - && !std::is_same ::const_reference>, - typename BasicJsonType::boolean_t >::value)) - && std::is_convertible::value, int > = 0 > -inline void to_json(BasicJsonType& j, const BoolRef& b) noexcept -{ - external_constructor::construct(j, static_cast(b)); -} - -template::value, int> = 0> -inline void to_json(BasicJsonType& j, const CompatibleString& s) -{ - external_constructor::construct(j, s); -} - -template -inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) -{ - external_constructor::construct(j, std::move(s)); -} - -template::value, int> = 0> -inline void to_json(BasicJsonType& j, FloatType val) noexcept -{ - external_constructor::construct(j, static_cast(val)); -} - -template::value, int> = 0> -inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept -{ - external_constructor::construct(j, static_cast(val)); -} - -template::value, int> = 0> -inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept -{ - external_constructor::construct(j, static_cast(val)); -} - -#if !JSON_DISABLE_ENUM_SERIALIZATION -template::value, int> = 0> -inline void to_json(BasicJsonType& j, EnumType e) noexcept -{ - using underlying_type = typename std::underlying_type::type; - external_constructor::construct(j, static_cast(e)); -} -#endif // JSON_DISABLE_ENUM_SERIALIZATION - -template -inline void to_json(BasicJsonType& j, const std::vector& e) -{ - external_constructor::construct(j, e); -} - -template < typename BasicJsonType, typename CompatibleArrayType, - enable_if_t < is_compatible_array_type::value&& - !is_compatible_object_type::value&& - !is_compatible_string_type::value&& - !std::is_same::value&& - !is_basic_json::value, - int > = 0 > -inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr) -{ - external_constructor::construct(j, arr); -} - -template -inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) -{ - external_constructor::construct(j, bin); -} - -template::value, int> = 0> -inline void to_json(BasicJsonType& j, const std::valarray& arr) -{ - external_constructor::construct(j, std::move(arr)); -} - -template -inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) -{ - external_constructor::construct(j, std::move(arr)); -} - -template < typename BasicJsonType, typename CompatibleObjectType, - enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > -inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj) -{ - external_constructor::construct(j, obj); -} - -template -inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) -{ - external_constructor::construct(j, std::move(obj)); -} - -template < - typename BasicJsonType, typename T, std::size_t N, - enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - int > = 0 > -inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) -{ - external_constructor::construct(j, arr); -} - -template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > -inline void to_json(BasicJsonType& j, const std::pair& p) -{ - j = { p.first, p.second }; -} - -// for https://github.com/nlohmann/json/pull/1134 -template>::value, int> = 0> -inline void to_json(BasicJsonType& j, const T& b) -{ - j = { {b.key(), b.value()} }; -} - -template -inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) -{ - j = { std::get(t)... }; -} - -template::value, int > = 0> -inline void to_json(BasicJsonType& j, const T& t) -{ - to_json_tuple_impl(j, t, make_index_sequence::value> {}); -} - -#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM -template -inline void to_json(BasicJsonType& j, const std_fs::path& p) -{ - j = p.string(); -} -#endif - -struct to_json_fn -{ - template - auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) - -> decltype(to_json(j, std::forward(val)), void()) - { - return to_json(j, std::forward(val)); - } -}; -} // namespace detail - -#ifndef JSON_HAS_CPP_17 -/// namespace to hold default `to_json` function -/// to see why this is required: -/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html -namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) -{ -#endif -JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers) - detail::static_const::value; -#ifndef JSON_HAS_CPP_17 -} // namespace -#endif - -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/exceptions.hpp b/r5dev/thirdparty/nlohmann/detail/exceptions.hpp deleted file mode 100644 index 96d7e010..00000000 --- a/r5dev/thirdparty/nlohmann/detail/exceptions.hpp +++ /dev/null @@ -1,255 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // nullptr_t -#include // exception -#include // runtime_error -#include // to_string -#include // vector - -#include -#include -#include -#include -#include -#include -#include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -//////////////// -// exceptions // -//////////////// - -/// @brief general exception of the @ref basic_json class -/// @sa https://json.nlohmann.me/api/basic_json/exception/ -class exception : public std::exception -{ - public: - /// returns the explanatory string - const char* what() const noexcept override - { - return m.what(); - } - - /// the id of the exception - const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) - - protected: - JSON_HEDLEY_NON_NULL(3) - exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) - - static std::string name(const std::string& ename, int id_) - { - return concat("[json.exception.", ename, '.', std::to_string(id_), "] "); - } - - static std::string diagnostics(std::nullptr_t /*leaf_element*/) - { - return ""; - } - - template - static std::string diagnostics(const BasicJsonType* leaf_element) - { -#if JSON_DIAGNOSTICS - std::vector tokens; - for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent) - { - switch (current->m_parent->type()) - { - case value_t::array: - { - for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) - { - if (¤t->m_parent->m_value.array->operator[](i) == current) - { - tokens.emplace_back(std::to_string(i)); - break; - } - } - break; - } - - case value_t::object: - { - for (const auto& element : *current->m_parent->m_value.object) - { - if (&element.second == current) - { - tokens.emplace_back(element.first.c_str()); - break; - } - } - break; - } - - case value_t::null: // LCOV_EXCL_LINE - case value_t::string: // LCOV_EXCL_LINE - case value_t::boolean: // LCOV_EXCL_LINE - case value_t::number_integer: // LCOV_EXCL_LINE - case value_t::number_unsigned: // LCOV_EXCL_LINE - case value_t::number_float: // LCOV_EXCL_LINE - case value_t::binary: // LCOV_EXCL_LINE - case value_t::discarded: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - break; // LCOV_EXCL_LINE - } - } - - if (tokens.empty()) - { - return ""; - } - - auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, - [](const std::string & a, const std::string & b) - { - return concat(a, '/', detail::escape(b)); - }); - return concat('(', str, ") "); -#else - static_cast(leaf_element); - return ""; -#endif - } - - private: - /// an exception object as storage for error messages - std::runtime_error m; -}; - -/// @brief exception indicating a parse error -/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ -class parse_error : public exception -{ - public: - /*! - @brief create a parse error exception - @param[in] id_ the id of the exception - @param[in] pos the position where the error occurred (or with - chars_read_total=0 if the position cannot be - determined) - @param[in] what_arg the explanatory string - @return parse_error object - */ - template::value, int> = 0> - static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context) - { - std::string w = concat(exception::name("parse_error", id_), "parse error", - position_string(pos), ": ", exception::diagnostics(context), what_arg); - return {id_, pos.chars_read_total, w.c_str()}; - } - - template::value, int> = 0> - static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context) - { - std::string w = concat(exception::name("parse_error", id_), "parse error", - (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""), - ": ", exception::diagnostics(context), what_arg); - return {id_, byte_, w.c_str()}; - } - - /*! - @brief byte index of the parse error - - The byte index of the last read character in the input file. - - @note For an input with n bytes, 1 is the index of the first character and - n+1 is the index of the terminating null byte or the end of file. - This also holds true when reading a byte vector (CBOR or MessagePack). - */ - const std::size_t byte; - - private: - parse_error(int id_, std::size_t byte_, const char* what_arg) - : exception(id_, what_arg), byte(byte_) {} - - static std::string position_string(const position_t& pos) - { - return concat(" at line ", std::to_string(pos.lines_read + 1), - ", column ", std::to_string(pos.chars_read_current_line)); - } -}; - -/// @brief exception indicating errors with iterators -/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ -class invalid_iterator : public exception -{ - public: - template::value, int> = 0> - static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context) - { - std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg); - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - invalid_iterator(int id_, const char* what_arg) - : exception(id_, what_arg) {} -}; - -/// @brief exception indicating executing a member function with a wrong type -/// @sa https://json.nlohmann.me/api/basic_json/type_error/ -class type_error : public exception -{ - public: - template::value, int> = 0> - static type_error create(int id_, const std::string& what_arg, BasicJsonContext context) - { - std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg); - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/// @brief exception indicating access out of the defined range -/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ -class out_of_range : public exception -{ - public: - template::value, int> = 0> - static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context) - { - std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg); - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -/// @brief exception indicating other library errors -/// @sa https://json.nlohmann.me/api/basic_json/other_error/ -class other_error : public exception -{ - public: - template::value, int> = 0> - static other_error create(int id_, const std::string& what_arg, BasicJsonContext context) - { - std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg); - return {id_, w.c_str()}; - } - - private: - JSON_HEDLEY_NON_NULL(3) - other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/hash.hpp b/r5dev/thirdparty/nlohmann/detail/hash.hpp deleted file mode 100644 index 3f05af83..00000000 --- a/r5dev/thirdparty/nlohmann/detail/hash.hpp +++ /dev/null @@ -1,129 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // uint8_t -#include // size_t -#include // hash - -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -// boost::hash_combine -inline std::size_t combine(std::size_t seed, std::size_t h) noexcept -{ - seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); - return seed; -} - -/*! -@brief hash a JSON value - -The hash function tries to rely on std::hash where possible. Furthermore, the -type of the JSON value is taken into account to have different hash values for -null, 0, 0U, and false, etc. - -@tparam BasicJsonType basic_json specialization -@param j JSON value to hash -@return hash value of j -*/ -template -std::size_t hash(const BasicJsonType& j) -{ - using string_t = typename BasicJsonType::string_t; - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - - const auto type = static_cast(j.type()); - switch (j.type()) - { - case BasicJsonType::value_t::null: - case BasicJsonType::value_t::discarded: - { - return combine(type, 0); - } - - case BasicJsonType::value_t::object: - { - auto seed = combine(type, j.size()); - for (const auto& element : j.items()) - { - const auto h = std::hash {}(element.key()); - seed = combine(seed, h); - seed = combine(seed, hash(element.value())); - } - return seed; - } - - case BasicJsonType::value_t::array: - { - auto seed = combine(type, j.size()); - for (const auto& element : j) - { - seed = combine(seed, hash(element)); - } - return seed; - } - - case BasicJsonType::value_t::string: - { - const auto h = std::hash {}(j.template get_ref()); - return combine(type, h); - } - - case BasicJsonType::value_t::boolean: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::number_integer: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::number_unsigned: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::number_float: - { - const auto h = std::hash {}(j.template get()); - return combine(type, h); - } - - case BasicJsonType::value_t::binary: - { - auto seed = combine(type, j.get_binary().size()); - const auto h = std::hash {}(j.get_binary().has_subtype()); - seed = combine(seed, h); - seed = combine(seed, static_cast(j.get_binary().subtype())); - for (const auto byte : j.get_binary()) - { - seed = combine(seed, std::hash {}(byte)); - } - return seed; - } - - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - return 0; // LCOV_EXCL_LINE - } -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/input/binary_reader.hpp b/r5dev/thirdparty/nlohmann/detail/input/binary_reader.hpp deleted file mode 100644 index 634615d3..00000000 --- a/r5dev/thirdparty/nlohmann/detail/input/binary_reader.hpp +++ /dev/null @@ -1,3010 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // generate_n -#include // array -#include // ldexp -#include // size_t -#include // uint8_t, uint16_t, uint32_t, uint64_t -#include // snprintf -#include // memcpy -#include // back_inserter -#include // numeric_limits -#include // char_traits, string -#include // make_pair, move -#include // vector - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/// how to treat CBOR tags -enum class cbor_tag_handler_t -{ - error, ///< throw a parse_error exception in case of a tag - ignore, ///< ignore tags - store ///< store tags as binary type -}; - -/*! -@brief determine system byte order - -@return true if and only if system's byte order is little endian - -@note from https://stackoverflow.com/a/1001328/266378 -*/ -static inline bool little_endianness(int num = 1) noexcept -{ - return *reinterpret_cast(&num) == 1; -} - - -/////////////////// -// binary reader // -/////////////////// - -/*! -@brief deserialization of CBOR, MessagePack, and UBJSON values -*/ -template> -class binary_reader -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - using json_sax_t = SAX; - using char_type = typename InputAdapterType::char_type; - using char_int_type = typename std::char_traits::int_type; - - public: - /*! - @brief create a binary reader - - @param[in] adapter input adapter to read from - */ - explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format) - { - (void)detail::is_sax_static_asserts {}; - } - - // make class move-only - binary_reader(const binary_reader&) = delete; - binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - binary_reader& operator=(const binary_reader&) = delete; - binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~binary_reader() = default; - - /*! - @param[in] format the binary format to parse - @param[in] sax_ a SAX event processor - @param[in] strict whether to expect the input to be consumed completed - @param[in] tag_handler how to treat CBOR tags - - @return whether parsing was successful - */ - JSON_HEDLEY_NON_NULL(3) - bool sax_parse(const input_format_t format, - json_sax_t* sax_, - const bool strict = true, - const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) - { - sax = sax_; - bool result = false; - - switch (format) - { - case input_format_t::bson: - result = parse_bson_internal(); - break; - - case input_format_t::cbor: - result = parse_cbor_internal(true, tag_handler); - break; - - case input_format_t::msgpack: - result = parse_msgpack_internal(); - break; - - case input_format_t::ubjson: - case input_format_t::bjdata: - result = parse_ubjson_internal(); - break; - - case input_format_t::json: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - } - - // strict mode: next byte must be EOF - if (result && strict) - { - if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata) - { - get_ignore_noop(); - } - else - { - get(); - } - - if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read, - exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr)); - } - } - - return result; - } - - private: - ////////// - // BSON // - ////////// - - /*! - @brief Reads in a BSON-object and passes it to the SAX-parser. - @return whether a valid BSON-value was passed to the SAX parser - */ - bool parse_bson_internal() - { - std::int32_t document_size{}; - get_number(input_format_t::bson, document_size); - - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) - { - return false; - } - - if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) - { - return false; - } - - return sax->end_object(); - } - - /*! - @brief Parses a C-style string from the BSON input. - @param[in,out] result A reference to the string variable where the read - string is to be stored. - @return `true` if the \x00-byte indicating the end of the string was - encountered before the EOF; false` indicates an unexpected EOF. - */ - bool get_bson_cstr(string_t& result) - { - auto out = std::back_inserter(result); - while (true) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) - { - return false; - } - if (current == 0x00) - { - return true; - } - *out++ = static_cast(current); - } - } - - /*! - @brief Parses a zero-terminated string of length @a len from the BSON - input. - @param[in] len The length (including the zero-byte at the end) of the - string to be read. - @param[in,out] result A reference to the string variable where the read - string is to be stored. - @tparam NumberType The type of the length @a len - @pre len >= 1 - @return `true` if the string was successfully parsed - */ - template - bool get_bson_string(const NumberType len, string_t& result) - { - if (JSON_HEDLEY_UNLIKELY(len < 1)) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr)); - } - - return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); - } - - /*! - @brief Parses a byte array input of length @a len from the BSON input. - @param[in] len The length of the byte array to be read. - @param[in,out] result A reference to the binary variable where the read - array is to be stored. - @tparam NumberType The type of the length @a len - @pre len >= 0 - @return `true` if the byte array was successfully parsed - */ - template - bool get_bson_binary(const NumberType len, binary_t& result) - { - if (JSON_HEDLEY_UNLIKELY(len < 0)) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr)); - } - - // All BSON binary values have a subtype - std::uint8_t subtype{}; - get_number(input_format_t::bson, subtype); - result.set_subtype(subtype); - - return get_binary(input_format_t::bson, len, result); - } - - /*! - @brief Read a BSON document element of the given @a element_type. - @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html - @param[in] element_type_parse_position The position in the input stream, - where the `element_type` was read. - @warning Not all BSON element types are supported yet. An unsupported - @a element_type will give rise to a parse_error.114: - Unsupported BSON record type 0x... - @return whether a valid BSON-object/array was passed to the SAX parser - */ - bool parse_bson_element_internal(const char_int_type element_type, - const std::size_t element_type_parse_position) - { - switch (element_type) - { - case 0x01: // double - { - double number{}; - return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); - } - - case 0x02: // string - { - std::int32_t len{}; - string_t value; - return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); - } - - case 0x03: // object - { - return parse_bson_internal(); - } - - case 0x04: // array - { - return parse_bson_array(); - } - - case 0x05: // binary - { - std::int32_t len{}; - binary_t value; - return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); - } - - case 0x08: // boolean - { - return sax->boolean(get() != 0); - } - - case 0x0A: // null - { - return sax->null(); - } - - case 0x10: // int32 - { - std::int32_t value{}; - return get_number(input_format_t::bson, value) && sax->number_integer(value); - } - - case 0x12: // int64 - { - std::int64_t value{}; - return get_number(input_format_t::bson, value) && sax->number_integer(value); - } - - default: // anything else not supported (yet) - { - std::array cr{{}}; - static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - std::string cr_str{cr.data()}; - return sax->parse_error(element_type_parse_position, cr_str, - parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr)); - } - } - } - - /*! - @brief Read a BSON element list (as specified in the BSON-spec) - - The same binary layout is used for objects and arrays, hence it must be - indicated with the argument @a is_array which one is expected - (true --> array, false --> object). - - @param[in] is_array Determines if the element list being read is to be - treated as an object (@a is_array == false), or as an - array (@a is_array == true). - @return whether a valid BSON-object/array was passed to the SAX parser - */ - bool parse_bson_element_list(const bool is_array) - { - string_t key; - - while (auto element_type = get()) - { - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) - { - return false; - } - - const std::size_t element_type_parse_position = chars_read; - if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) - { - return false; - } - - if (!is_array && !sax->key(key)) - { - return false; - } - - if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) - { - return false; - } - - // get_bson_cstr only appends - key.clear(); - } - - return true; - } - - /*! - @brief Reads an array from the BSON input and passes it to the SAX-parser. - @return whether a valid BSON-array was passed to the SAX parser - */ - bool parse_bson_array() - { - std::int32_t document_size{}; - get_number(input_format_t::bson, document_size); - - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) - { - return false; - } - - if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) - { - return false; - } - - return sax->end_array(); - } - - ////////// - // CBOR // - ////////// - - /*! - @param[in] get_char whether a new character should be retrieved from the - input (true) or whether the last read character should - be considered instead (false) - @param[in] tag_handler how CBOR tags should be treated - - @return whether a valid CBOR value was passed to the SAX parser - */ - bool parse_cbor_internal(const bool get_char, - const cbor_tag_handler_t tag_handler) - { - switch (get_char ? get() : current) - { - // EOF - case std::char_traits::eof(): - return unexpect_eof(input_format_t::cbor, "value"); - - // Integer 0x00..0x17 (0..23) - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0A: - case 0x0B: - case 0x0C: - case 0x0D: - case 0x0E: - case 0x0F: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - return sax->number_unsigned(static_cast(current)); - - case 0x18: // Unsigned integer (one-byte uint8_t follows) - { - std::uint8_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); - } - - case 0x19: // Unsigned integer (two-byte uint16_t follows) - { - std::uint16_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); - } - - case 0x1A: // Unsigned integer (four-byte uint32_t follows) - { - std::uint32_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); - } - - case 0x1B: // Unsigned integer (eight-byte uint64_t follows) - { - std::uint64_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); - } - - // Negative integer -1-0x00..-1-0x17 (-1..-24) - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2A: - case 0x2B: - case 0x2C: - case 0x2D: - case 0x2E: - case 0x2F: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - return sax->number_integer(static_cast(0x20 - 1 - current)); - - case 0x38: // Negative integer (one-byte uint8_t follows) - { - std::uint8_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); - } - - case 0x39: // Negative integer -1-n (two-byte uint16_t follows) - { - std::uint16_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); - } - - case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) - { - std::uint32_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); - } - - case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) - { - std::uint64_t number{}; - return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - - static_cast(number)); - } - - // Binary data (0x00..0x17 bytes follow) - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4A: - case 0x4B: - case 0x4C: - case 0x4D: - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: // Binary data (one-byte uint8_t for n follows) - case 0x59: // Binary data (two-byte uint16_t for n follow) - case 0x5A: // Binary data (four-byte uint32_t for n follow) - case 0x5B: // Binary data (eight-byte uint64_t for n follow) - case 0x5F: // Binary data (indefinite length) - { - binary_t b; - return get_cbor_binary(b) && sax->binary(b); - } - - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) - case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) - case 0x7F: // UTF-8 string (indefinite length) - { - string_t s; - return get_cbor_string(s) && sax->string(s); - } - - // array (0x00..0x17 data items follow) - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8A: - case 0x8B: - case 0x8C: - case 0x8D: - case 0x8E: - case 0x8F: - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - return get_cbor_array( - conditional_static_cast(static_cast(current) & 0x1Fu), tag_handler); - - case 0x98: // array (one-byte uint8_t for n follows) - { - std::uint8_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); - } - - case 0x99: // array (two-byte uint16_t for n follow) - { - std::uint16_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); - } - - case 0x9A: // array (four-byte uint32_t for n follow) - { - std::uint32_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast(len), tag_handler); - } - - case 0x9B: // array (eight-byte uint64_t for n follow) - { - std::uint64_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast(len), tag_handler); - } - - case 0x9F: // array (indefinite length) - return get_cbor_array(static_cast(-1), tag_handler); - - // map (0x00..0x17 pairs of data items follow) - case 0xA0: - case 0xA1: - case 0xA2: - case 0xA3: - case 0xA4: - case 0xA5: - case 0xA6: - case 0xA7: - case 0xA8: - case 0xA9: - case 0xAA: - case 0xAB: - case 0xAC: - case 0xAD: - case 0xAE: - case 0xAF: - case 0xB0: - case 0xB1: - case 0xB2: - case 0xB3: - case 0xB4: - case 0xB5: - case 0xB6: - case 0xB7: - return get_cbor_object(conditional_static_cast(static_cast(current) & 0x1Fu), tag_handler); - - case 0xB8: // map (one-byte uint8_t for n follows) - { - std::uint8_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); - } - - case 0xB9: // map (two-byte uint16_t for n follow) - { - std::uint16_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); - } - - case 0xBA: // map (four-byte uint32_t for n follow) - { - std::uint32_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast(len), tag_handler); - } - - case 0xBB: // map (eight-byte uint64_t for n follow) - { - std::uint64_t len{}; - return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast(len), tag_handler); - } - - case 0xBF: // map (indefinite length) - return get_cbor_object(static_cast(-1), tag_handler); - - case 0xC6: // tagged item - case 0xC7: - case 0xC8: - case 0xC9: - case 0xCA: - case 0xCB: - case 0xCC: - case 0xCD: - case 0xCE: - case 0xCF: - case 0xD0: - case 0xD1: - case 0xD2: - case 0xD3: - case 0xD4: - case 0xD8: // tagged item (1 bytes follow) - case 0xD9: // tagged item (2 bytes follow) - case 0xDA: // tagged item (4 bytes follow) - case 0xDB: // tagged item (8 bytes follow) - { - switch (tag_handler) - { - case cbor_tag_handler_t::error: - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); - } - - case cbor_tag_handler_t::ignore: - { - // ignore binary subtype - switch (current) - { - case 0xD8: - { - std::uint8_t subtype_to_ignore{}; - get_number(input_format_t::cbor, subtype_to_ignore); - break; - } - case 0xD9: - { - std::uint16_t subtype_to_ignore{}; - get_number(input_format_t::cbor, subtype_to_ignore); - break; - } - case 0xDA: - { - std::uint32_t subtype_to_ignore{}; - get_number(input_format_t::cbor, subtype_to_ignore); - break; - } - case 0xDB: - { - std::uint64_t subtype_to_ignore{}; - get_number(input_format_t::cbor, subtype_to_ignore); - break; - } - default: - break; - } - return parse_cbor_internal(true, tag_handler); - } - - case cbor_tag_handler_t::store: - { - binary_t b; - // use binary subtype and store in binary container - switch (current) - { - case 0xD8: - { - std::uint8_t subtype{}; - get_number(input_format_t::cbor, subtype); - b.set_subtype(detail::conditional_static_cast(subtype)); - break; - } - case 0xD9: - { - std::uint16_t subtype{}; - get_number(input_format_t::cbor, subtype); - b.set_subtype(detail::conditional_static_cast(subtype)); - break; - } - case 0xDA: - { - std::uint32_t subtype{}; - get_number(input_format_t::cbor, subtype); - b.set_subtype(detail::conditional_static_cast(subtype)); - break; - } - case 0xDB: - { - std::uint64_t subtype{}; - get_number(input_format_t::cbor, subtype); - b.set_subtype(detail::conditional_static_cast(subtype)); - break; - } - default: - return parse_cbor_internal(true, tag_handler); - } - get(); - return get_cbor_binary(b) && sax->binary(b); - } - - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - return false; // LCOV_EXCL_LINE - } - } - - case 0xF4: // false - return sax->boolean(false); - - case 0xF5: // true - return sax->boolean(true); - - case 0xF6: // null - return sax->null(); - - case 0xF9: // Half-Precision Float (two-byte IEEE 754) - { - const auto byte1_raw = get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) - { - return false; - } - const auto byte2_raw = get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) - { - return false; - } - - const auto byte1 = static_cast(byte1_raw); - const auto byte2 = static_cast(byte2_raw); - - // code from RFC 7049, Appendix D, Figure 3: - // As half-precision floating-point numbers were only added - // to IEEE 754 in 2008, today's programming platforms often - // still only have limited support for them. It is very - // easy to include at least decoding support for them even - // without such support. An example of a small decoder for - // half-precision floating-point numbers in the C language - // is shown in Fig. 3. - const auto half = static_cast((byte1 << 8u) + byte2); - const double val = [&half] - { - const int exp = (half >> 10u) & 0x1Fu; - const unsigned int mant = half & 0x3FFu; - JSON_ASSERT(0 <= exp&& exp <= 32); - JSON_ASSERT(mant <= 1024); - switch (exp) - { - case 0: - return std::ldexp(mant, -24); - case 31: - return (mant == 0) - ? std::numeric_limits::infinity() - : std::numeric_limits::quiet_NaN(); - default: - return std::ldexp(mant + 1024, exp - 25); - } - }(); - return sax->number_float((half & 0x8000u) != 0 - ? static_cast(-val) - : static_cast(val), ""); - } - - case 0xFA: // Single-Precision Float (four-byte IEEE 754) - { - float number{}; - return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); - } - - case 0xFB: // Double-Precision Float (eight-byte IEEE 754) - { - double number{}; - return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); - } - - default: // anything else (0xFF is handled inside the other types) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr)); - } - } - } - - /*! - @brief reads a CBOR string - - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - Additionally, CBOR's strings with indefinite lengths are supported. - - @param[out] result created string - - @return whether string creation completed - */ - bool get_cbor_string(string_t& result) - { - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) - { - return false; - } - - switch (current) - { - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - { - return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); - } - - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - { - std::uint8_t len{}; - return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); - } - - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - { - std::uint16_t len{}; - return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); - } - - case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) - { - std::uint32_t len{}; - return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); - } - - case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) - { - std::uint64_t len{}; - return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); - } - - case 0x7F: // UTF-8 string (indefinite length) - { - while (get() != 0xFF) - { - string_t chunk; - if (!get_cbor_string(chunk)) - { - return false; - } - result.append(chunk); - } - return true; - } - - default: - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr)); - } - } - } - - /*! - @brief reads a CBOR byte array - - This function first reads starting bytes to determine the expected - byte array length and then copies this number of bytes into the byte array. - Additionally, CBOR's byte arrays with indefinite lengths are supported. - - @param[out] result created byte array - - @return whether byte array creation completed - */ - bool get_cbor_binary(binary_t& result) - { - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) - { - return false; - } - - switch (current) - { - // Binary data (0x00..0x17 bytes follow) - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4A: - case 0x4B: - case 0x4C: - case 0x4D: - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - { - return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); - } - - case 0x58: // Binary data (one-byte uint8_t for n follows) - { - std::uint8_t len{}; - return get_number(input_format_t::cbor, len) && - get_binary(input_format_t::cbor, len, result); - } - - case 0x59: // Binary data (two-byte uint16_t for n follow) - { - std::uint16_t len{}; - return get_number(input_format_t::cbor, len) && - get_binary(input_format_t::cbor, len, result); - } - - case 0x5A: // Binary data (four-byte uint32_t for n follow) - { - std::uint32_t len{}; - return get_number(input_format_t::cbor, len) && - get_binary(input_format_t::cbor, len, result); - } - - case 0x5B: // Binary data (eight-byte uint64_t for n follow) - { - std::uint64_t len{}; - return get_number(input_format_t::cbor, len) && - get_binary(input_format_t::cbor, len, result); - } - - case 0x5F: // Binary data (indefinite length) - { - while (get() != 0xFF) - { - binary_t chunk; - if (!get_cbor_binary(chunk)) - { - return false; - } - result.insert(result.end(), chunk.begin(), chunk.end()); - } - return true; - } - - default: - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr)); - } - } - } - - /*! - @param[in] len the length of the array or static_cast(-1) for an - array of indefinite size - @param[in] tag_handler how CBOR tags should be treated - @return whether array creation completed - */ - bool get_cbor_array(const std::size_t len, - const cbor_tag_handler_t tag_handler) - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) - { - return false; - } - - if (len != static_cast(-1)) - { - for (std::size_t i = 0; i < len; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) - { - return false; - } - } - } - else - { - while (get() != 0xFF) - { - if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) - { - return false; - } - } - } - - return sax->end_array(); - } - - /*! - @param[in] len the length of the object or static_cast(-1) for an - object of indefinite size - @param[in] tag_handler how CBOR tags should be treated - @return whether object creation completed - */ - bool get_cbor_object(const std::size_t len, - const cbor_tag_handler_t tag_handler) - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) - { - return false; - } - - if (len != 0) - { - string_t key; - if (len != static_cast(-1)) - { - for (std::size_t i = 0; i < len; ++i) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) - { - return false; - } - - if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) - { - return false; - } - key.clear(); - } - } - else - { - while (get() != 0xFF) - { - if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) - { - return false; - } - - if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) - { - return false; - } - key.clear(); - } - } - } - - return sax->end_object(); - } - - ///////////// - // MsgPack // - ///////////// - - /*! - @return whether a valid MessagePack value was passed to the SAX parser - */ - bool parse_msgpack_internal() - { - switch (get()) - { - // EOF - case std::char_traits::eof(): - return unexpect_eof(input_format_t::msgpack, "value"); - - // positive fixint - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0A: - case 0x0B: - case 0x0C: - case 0x0D: - case 0x0E: - case 0x0F: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1A: - case 0x1B: - case 0x1C: - case 0x1D: - case 0x1E: - case 0x1F: - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2A: - case 0x2B: - case 0x2C: - case 0x2D: - case 0x2E: - case 0x2F: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3A: - case 0x3B: - case 0x3C: - case 0x3D: - case 0x3E: - case 0x3F: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4A: - case 0x4B: - case 0x4C: - case 0x4D: - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5A: - case 0x5B: - case 0x5C: - case 0x5D: - case 0x5E: - case 0x5F: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7A: - case 0x7B: - case 0x7C: - case 0x7D: - case 0x7E: - case 0x7F: - return sax->number_unsigned(static_cast(current)); - - // fixmap - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8A: - case 0x8B: - case 0x8C: - case 0x8D: - case 0x8E: - case 0x8F: - return get_msgpack_object(conditional_static_cast(static_cast(current) & 0x0Fu)); - - // fixarray - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9A: - case 0x9B: - case 0x9C: - case 0x9D: - case 0x9E: - case 0x9F: - return get_msgpack_array(conditional_static_cast(static_cast(current) & 0x0Fu)); - - // fixstr - case 0xA0: - case 0xA1: - case 0xA2: - case 0xA3: - case 0xA4: - case 0xA5: - case 0xA6: - case 0xA7: - case 0xA8: - case 0xA9: - case 0xAA: - case 0xAB: - case 0xAC: - case 0xAD: - case 0xAE: - case 0xAF: - case 0xB0: - case 0xB1: - case 0xB2: - case 0xB3: - case 0xB4: - case 0xB5: - case 0xB6: - case 0xB7: - case 0xB8: - case 0xB9: - case 0xBA: - case 0xBB: - case 0xBC: - case 0xBD: - case 0xBE: - case 0xBF: - case 0xD9: // str 8 - case 0xDA: // str 16 - case 0xDB: // str 32 - { - string_t s; - return get_msgpack_string(s) && sax->string(s); - } - - case 0xC0: // nil - return sax->null(); - - case 0xC2: // false - return sax->boolean(false); - - case 0xC3: // true - return sax->boolean(true); - - case 0xC4: // bin 8 - case 0xC5: // bin 16 - case 0xC6: // bin 32 - case 0xC7: // ext 8 - case 0xC8: // ext 16 - case 0xC9: // ext 32 - case 0xD4: // fixext 1 - case 0xD5: // fixext 2 - case 0xD6: // fixext 4 - case 0xD7: // fixext 8 - case 0xD8: // fixext 16 - { - binary_t b; - return get_msgpack_binary(b) && sax->binary(b); - } - - case 0xCA: // float 32 - { - float number{}; - return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); - } - - case 0xCB: // float 64 - { - double number{}; - return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); - } - - case 0xCC: // uint 8 - { - std::uint8_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); - } - - case 0xCD: // uint 16 - { - std::uint16_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); - } - - case 0xCE: // uint 32 - { - std::uint32_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); - } - - case 0xCF: // uint 64 - { - std::uint64_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); - } - - case 0xD0: // int 8 - { - std::int8_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_integer(number); - } - - case 0xD1: // int 16 - { - std::int16_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_integer(number); - } - - case 0xD2: // int 32 - { - std::int32_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_integer(number); - } - - case 0xD3: // int 64 - { - std::int64_t number{}; - return get_number(input_format_t::msgpack, number) && sax->number_integer(number); - } - - case 0xDC: // array 16 - { - std::uint16_t len{}; - return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); - } - - case 0xDD: // array 32 - { - std::uint32_t len{}; - return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast(len)); - } - - case 0xDE: // map 16 - { - std::uint16_t len{}; - return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); - } - - case 0xDF: // map 32 - { - std::uint32_t len{}; - return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast(len)); - } - - // negative fixint - case 0xE0: - case 0xE1: - case 0xE2: - case 0xE3: - case 0xE4: - case 0xE5: - case 0xE6: - case 0xE7: - case 0xE8: - case 0xE9: - case 0xEA: - case 0xEB: - case 0xEC: - case 0xED: - case 0xEE: - case 0xEF: - case 0xF0: - case 0xF1: - case 0xF2: - case 0xF3: - case 0xF4: - case 0xF5: - case 0xF6: - case 0xF7: - case 0xF8: - case 0xF9: - case 0xFA: - case 0xFB: - case 0xFC: - case 0xFD: - case 0xFE: - case 0xFF: - return sax->number_integer(static_cast(current)); - - default: // anything else - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr)); - } - } - } - - /*! - @brief reads a MessagePack string - - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - - @param[out] result created string - - @return whether string creation completed - */ - bool get_msgpack_string(string_t& result) - { - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) - { - return false; - } - - switch (current) - { - // fixstr - case 0xA0: - case 0xA1: - case 0xA2: - case 0xA3: - case 0xA4: - case 0xA5: - case 0xA6: - case 0xA7: - case 0xA8: - case 0xA9: - case 0xAA: - case 0xAB: - case 0xAC: - case 0xAD: - case 0xAE: - case 0xAF: - case 0xB0: - case 0xB1: - case 0xB2: - case 0xB3: - case 0xB4: - case 0xB5: - case 0xB6: - case 0xB7: - case 0xB8: - case 0xB9: - case 0xBA: - case 0xBB: - case 0xBC: - case 0xBD: - case 0xBE: - case 0xBF: - { - return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); - } - - case 0xD9: // str 8 - { - std::uint8_t len{}; - return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); - } - - case 0xDA: // str 16 - { - std::uint16_t len{}; - return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); - } - - case 0xDB: // str 32 - { - std::uint32_t len{}; - return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); - } - - default: - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr)); - } - } - } - - /*! - @brief reads a MessagePack byte array - - This function first reads starting bytes to determine the expected - byte array length and then copies this number of bytes into a byte array. - - @param[out] result created byte array - - @return whether byte array creation completed - */ - bool get_msgpack_binary(binary_t& result) - { - // helper function to set the subtype - auto assign_and_return_true = [&result](std::int8_t subtype) - { - result.set_subtype(static_cast(subtype)); - return true; - }; - - switch (current) - { - case 0xC4: // bin 8 - { - std::uint8_t len{}; - return get_number(input_format_t::msgpack, len) && - get_binary(input_format_t::msgpack, len, result); - } - - case 0xC5: // bin 16 - { - std::uint16_t len{}; - return get_number(input_format_t::msgpack, len) && - get_binary(input_format_t::msgpack, len, result); - } - - case 0xC6: // bin 32 - { - std::uint32_t len{}; - return get_number(input_format_t::msgpack, len) && - get_binary(input_format_t::msgpack, len, result); - } - - case 0xC7: // ext 8 - { - std::uint8_t len{}; - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, len) && - get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, len, result) && - assign_and_return_true(subtype); - } - - case 0xC8: // ext 16 - { - std::uint16_t len{}; - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, len) && - get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, len, result) && - assign_and_return_true(subtype); - } - - case 0xC9: // ext 32 - { - std::uint32_t len{}; - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, len) && - get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, len, result) && - assign_and_return_true(subtype); - } - - case 0xD4: // fixext 1 - { - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, 1, result) && - assign_and_return_true(subtype); - } - - case 0xD5: // fixext 2 - { - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, 2, result) && - assign_and_return_true(subtype); - } - - case 0xD6: // fixext 4 - { - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, 4, result) && - assign_and_return_true(subtype); - } - - case 0xD7: // fixext 8 - { - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, 8, result) && - assign_and_return_true(subtype); - } - - case 0xD8: // fixext 16 - { - std::int8_t subtype{}; - return get_number(input_format_t::msgpack, subtype) && - get_binary(input_format_t::msgpack, 16, result) && - assign_and_return_true(subtype); - } - - default: // LCOV_EXCL_LINE - return false; // LCOV_EXCL_LINE - } - } - - /*! - @param[in] len the length of the array - @return whether array creation completed - */ - bool get_msgpack_array(const std::size_t len) - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) - { - return false; - } - - for (std::size_t i = 0; i < len; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) - { - return false; - } - } - - return sax->end_array(); - } - - /*! - @param[in] len the length of the object - @return whether object creation completed - */ - bool get_msgpack_object(const std::size_t len) - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) - { - return false; - } - - string_t key; - for (std::size_t i = 0; i < len; ++i) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) - { - return false; - } - - if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) - { - return false; - } - key.clear(); - } - - return sax->end_object(); - } - - //////////// - // UBJSON // - //////////// - - /*! - @param[in] get_char whether a new character should be retrieved from the - input (true, default) or whether the last read - character should be considered instead - - @return whether a valid UBJSON value was passed to the SAX parser - */ - bool parse_ubjson_internal(const bool get_char = true) - { - return get_ubjson_value(get_char ? get_ignore_noop() : current); - } - - /*! - @brief reads a UBJSON string - - This function is either called after reading the 'S' byte explicitly - indicating a string, or in case of an object key where the 'S' byte can be - left out. - - @param[out] result created string - @param[in] get_char whether a new character should be retrieved from the - input (true, default) or whether the last read - character should be considered instead - - @return whether string creation completed - */ - bool get_ubjson_string(string_t& result, const bool get_char = true) - { - if (get_char) - { - get(); // TODO(niels): may we ignore N here? - } - - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) - { - return false; - } - - switch (current) - { - case 'U': - { - std::uint8_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - case 'i': - { - std::int8_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - case 'I': - { - std::int16_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - case 'l': - { - std::int32_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - case 'L': - { - std::int64_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - case 'u': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint16_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - case 'm': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint32_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - case 'M': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint64_t len{}; - return get_number(input_format, len) && get_string(input_format, len, result); - } - - default: - break; - } - auto last_token = get_token_string(); - std::string message; - - if (input_format != input_format_t::bjdata) - { - message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token; - } - else - { - message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token; - } - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr)); - } - - /*! - @param[out] dim an integer vector storing the ND array dimensions - @return whether reading ND array size vector is successful - */ - bool get_ubjson_ndarray_size(std::vector& dim) - { - std::pair size_and_type; - size_t dimlen = 0; - bool no_ndarray = true; - - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray))) - { - return false; - } - - if (size_and_type.first != npos) - { - if (size_and_type.second != 0) - { - if (size_and_type.second != 'N') - { - for (std::size_t i = 0; i < size_and_type.first; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second))) - { - return false; - } - dim.push_back(dimlen); - } - } - } - else - { - for (std::size_t i = 0; i < size_and_type.first; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray))) - { - return false; - } - dim.push_back(dimlen); - } - } - } - else - { - while (current != ']') - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current))) - { - return false; - } - dim.push_back(dimlen); - get_ignore_noop(); - } - } - return true; - } - - /*! - @param[out] result determined size - @param[in,out] is_ndarray for input, `true` means already inside an ndarray vector - or ndarray dimension is not allowed; `false` means ndarray - is allowed; for output, `true` means an ndarray is found; - is_ndarray can only return `true` when its initial value - is `false` - @param[in] prefix type marker if already read, otherwise set to 0 - - @return whether size determination completed - */ - bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0) - { - if (prefix == 0) - { - prefix = get_ignore_noop(); - } - - switch (prefix) - { - case 'U': - { - std::uint8_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - result = static_cast(number); - return true; - } - - case 'i': - { - std::int8_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - if (number < 0) - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, - exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); - } - result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char - return true; - } - - case 'I': - { - std::int16_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - if (number < 0) - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, - exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); - } - result = static_cast(number); - return true; - } - - case 'l': - { - std::int32_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - if (number < 0) - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, - exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); - } - result = static_cast(number); - return true; - } - - case 'L': - { - std::int64_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - if (number < 0) - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, - exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr)); - } - if (!value_in_range_of(number)) - { - return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, - exception_message(input_format, "integer value overflow", "size"), nullptr)); - } - result = static_cast(number); - return true; - } - - case 'u': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint16_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - result = static_cast(number); - return true; - } - - case 'm': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint32_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - result = conditional_static_cast(number); - return true; - } - - case 'M': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint64_t number{}; - if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number))) - { - return false; - } - if (!value_in_range_of(number)) - { - return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, - exception_message(input_format, "integer value overflow", "size"), nullptr)); - } - result = detail::conditional_static_cast(number); - return true; - } - - case '[': - { - if (input_format != input_format_t::bjdata) - { - break; - } - if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimentional vector is not allowed", "size"), nullptr)); - } - std::vector dim; - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim))) - { - return false; - } - if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector - { - result = dim.at(dim.size() - 1); - return true; - } - if (!dim.empty()) // if ndarray, convert to an object in JData annotated array format - { - for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container - { - if ( i == 0 ) - { - result = 0; - return true; - } - } - - string_t key = "_ArraySize_"; - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size()))) - { - return false; - } - result = 1; - for (auto i : dim) - { - result *= i; - if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type() - { - return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); - } - if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast(i)))) - { - return false; - } - } - is_ndarray = true; - return sax->end_array(); - } - result = 0; - return true; - } - - default: - break; - } - auto last_token = get_token_string(); - std::string message; - - if (input_format != input_format_t::bjdata) - { - message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token; - } - else - { - message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token; - } - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr)); - } - - /*! - @brief determine the type and size for a container - - In the optimized UBJSON format, a type and a size can be provided to allow - for a more compact representation. - - @param[out] result pair of the size and the type - @param[in] inside_ndarray whether the parser is parsing an ND array dimensional vector - - @return whether pair creation completed - */ - bool get_ubjson_size_type(std::pair& result, bool inside_ndarray = false) - { - result.first = npos; // size - result.second = 0; // type - bool is_ndarray = false; - - get_ignore_noop(); - - if (current == '$') - { - result.second = get(); // must not ignore 'N', because 'N' maybe the type - if (input_format == input_format_t::bjdata - && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second))) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr)); - } - - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type"))) - { - return false; - } - - get_ignore_noop(); - if (JSON_HEDLEY_UNLIKELY(current != '#')) - { - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value"))) - { - return false; - } - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr)); - } - - bool is_error = get_ubjson_size_value(result.first, is_ndarray); - if (input_format == input_format_t::bjdata && is_ndarray) - { - if (inside_ndarray) - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read, - exception_message(input_format, "ndarray can not be recursive", "size"), nullptr)); - } - result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters - } - return is_error; - } - - if (current == '#') - { - bool is_error = get_ubjson_size_value(result.first, is_ndarray); - if (input_format == input_format_t::bjdata && is_ndarray) - { - return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read, - exception_message(input_format, "ndarray requires both type and size", "size"), nullptr)); - } - return is_error; - } - - return true; - } - - /*! - @param prefix the previously read or set type prefix - @return whether value creation completed - */ - bool get_ubjson_value(const char_int_type prefix) - { - switch (prefix) - { - case std::char_traits::eof(): // EOF - return unexpect_eof(input_format, "value"); - - case 'T': // true - return sax->boolean(true); - case 'F': // false - return sax->boolean(false); - - case 'Z': // null - return sax->null(); - - case 'U': - { - std::uint8_t number{}; - return get_number(input_format, number) && sax->number_unsigned(number); - } - - case 'i': - { - std::int8_t number{}; - return get_number(input_format, number) && sax->number_integer(number); - } - - case 'I': - { - std::int16_t number{}; - return get_number(input_format, number) && sax->number_integer(number); - } - - case 'l': - { - std::int32_t number{}; - return get_number(input_format, number) && sax->number_integer(number); - } - - case 'L': - { - std::int64_t number{}; - return get_number(input_format, number) && sax->number_integer(number); - } - - case 'u': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint16_t number{}; - return get_number(input_format, number) && sax->number_unsigned(number); - } - - case 'm': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint32_t number{}; - return get_number(input_format, number) && sax->number_unsigned(number); - } - - case 'M': - { - if (input_format != input_format_t::bjdata) - { - break; - } - std::uint64_t number{}; - return get_number(input_format, number) && sax->number_unsigned(number); - } - - case 'h': - { - if (input_format != input_format_t::bjdata) - { - break; - } - const auto byte1_raw = get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) - { - return false; - } - const auto byte2_raw = get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) - { - return false; - } - - const auto byte1 = static_cast(byte1_raw); - const auto byte2 = static_cast(byte2_raw); - - // code from RFC 7049, Appendix D, Figure 3: - // As half-precision floating-point numbers were only added - // to IEEE 754 in 2008, today's programming platforms often - // still only have limited support for them. It is very - // easy to include at least decoding support for them even - // without such support. An example of a small decoder for - // half-precision floating-point numbers in the C language - // is shown in Fig. 3. - const auto half = static_cast((byte2 << 8u) + byte1); - const double val = [&half] - { - const int exp = (half >> 10u) & 0x1Fu; - const unsigned int mant = half & 0x3FFu; - JSON_ASSERT(0 <= exp&& exp <= 32); - JSON_ASSERT(mant <= 1024); - switch (exp) - { - case 0: - return std::ldexp(mant, -24); - case 31: - return (mant == 0) - ? std::numeric_limits::infinity() - : std::numeric_limits::quiet_NaN(); - default: - return std::ldexp(mant + 1024, exp - 25); - } - }(); - return sax->number_float((half & 0x8000u) != 0 - ? static_cast(-val) - : static_cast(val), ""); - } - - case 'd': - { - float number{}; - return get_number(input_format, number) && sax->number_float(static_cast(number), ""); - } - - case 'D': - { - double number{}; - return get_number(input_format, number) && sax->number_float(static_cast(number), ""); - } - - case 'H': - { - return get_ubjson_high_precision_number(); - } - - case 'C': // char - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char"))) - { - return false; - } - if (JSON_HEDLEY_UNLIKELY(current > 127)) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, - exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr)); - } - string_t s(1, static_cast(current)); - return sax->string(s); - } - - case 'S': // string - { - string_t s; - return get_ubjson_string(s) && sax->string(s); - } - - case '[': // array - return get_ubjson_array(); - - case '{': // object - return get_ubjson_object(); - - default: // anything else - break; - } - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr)); - } - - /*! - @return whether array creation completed - */ - bool get_ubjson_array() - { - std::pair size_and_type; - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) - { - return false; - } - - // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata): - // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]} - - if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0) - { - size_and_type.second &= ~(static_cast(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker - auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t) - { - return p.first < t; - }); - string_t key = "_ArrayType_"; - if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second)) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr)); - } - - string_t type = it->second; // sax->string() takes a reference - if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type))) - { - return false; - } - - if (size_and_type.second == 'C') - { - size_and_type.second = 'U'; - } - - key = "_ArrayData_"; - if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) )) - { - return false; - } - - for (std::size_t i = 0; i < size_and_type.first; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) - { - return false; - } - } - - return (sax->end_array() && sax->end_object()); - } - - if (size_and_type.first != npos) - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) - { - return false; - } - - if (size_and_type.second != 0) - { - if (size_and_type.second != 'N') - { - for (std::size_t i = 0; i < size_and_type.first; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) - { - return false; - } - } - } - } - else - { - for (std::size_t i = 0; i < size_and_type.first; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) - { - return false; - } - } - } - } - else - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) - { - return false; - } - - while (current != ']') - { - if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) - { - return false; - } - get_ignore_noop(); - } - } - - return sax->end_array(); - } - - /*! - @return whether object creation completed - */ - bool get_ubjson_object() - { - std::pair size_and_type; - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) - { - return false; - } - - // do not accept ND-array size in objects in BJData - if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0) - { - auto last_token = get_token_string(); - return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, - exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr)); - } - - string_t key; - if (size_and_type.first != npos) - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) - { - return false; - } - - if (size_and_type.second != 0) - { - for (std::size_t i = 0; i < size_and_type.first; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) - { - return false; - } - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) - { - return false; - } - key.clear(); - } - } - else - { - for (std::size_t i = 0; i < size_and_type.first; ++i) - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) - { - return false; - } - if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) - { - return false; - } - key.clear(); - } - } - } - else - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) - { - return false; - } - - while (current != '}') - { - if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) - { - return false; - } - if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) - { - return false; - } - get_ignore_noop(); - key.clear(); - } - } - - return sax->end_object(); - } - - // Note, no reader for UBJSON binary types is implemented because they do - // not exist - - bool get_ubjson_high_precision_number() - { - // get size of following number string - std::size_t size{}; - bool no_ndarray = true; - auto res = get_ubjson_size_value(size, no_ndarray); - if (JSON_HEDLEY_UNLIKELY(!res)) - { - return res; - } - - // get number string - std::vector number_vector; - for (std::size_t i = 0; i < size; ++i) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number"))) - { - return false; - } - number_vector.push_back(static_cast(current)); - } - - // parse number string - using ia_type = decltype(detail::input_adapter(number_vector)); - auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); - const auto result_number = number_lexer.scan(); - const auto number_string = number_lexer.get_token_string(); - const auto result_remainder = number_lexer.scan(); - - using token_type = typename detail::lexer_base::token_type; - - if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) - { - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, - exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); - } - - switch (result_number) - { - case token_type::value_integer: - return sax->number_integer(number_lexer.get_number_integer()); - case token_type::value_unsigned: - return sax->number_unsigned(number_lexer.get_number_unsigned()); - case token_type::value_float: - return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); - case token_type::uninitialized: - case token_type::literal_true: - case token_type::literal_false: - case token_type::literal_null: - case token_type::value_string: - case token_type::begin_array: - case token_type::begin_object: - case token_type::end_array: - case token_type::end_object: - case token_type::name_separator: - case token_type::value_separator: - case token_type::parse_error: - case token_type::end_of_input: - case token_type::literal_or_value: - default: - return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, - exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr)); - } - } - - /////////////////////// - // Utility functions // - /////////////////////// - - /*! - @brief get next character from the input - - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns a -'ve valued - `std::char_traits::eof()` in that case. - - @return character read from the input - */ - char_int_type get() - { - ++chars_read; - return current = ia.get_character(); - } - - /*! - @return character read from the input after ignoring all 'N' entries - */ - char_int_type get_ignore_noop() - { - do - { - get(); - } - while (current == 'N'); - - return current; - } - - /* - @brief read a number from the input - - @tparam NumberType the type of the number - @param[in] format the current format (for diagnostics) - @param[out] result number of type @a NumberType - - @return whether conversion completed - - @note This function needs to respect the system's endianness, because - bytes in CBOR, MessagePack, and UBJSON are stored in network order - (big endian) and therefore need reordering on little endian systems. - On the other hand, BSON and BJData use little endian and should reorder - on big endian systems. - */ - template - bool get_number(const input_format_t format, NumberType& result) - { - // step 1: read input into array with system's byte order - std::array vec{}; - for (std::size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) - { - return false; - } - - // reverse byte order prior to conversion if necessary - if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata)) - { - vec[sizeof(NumberType) - i - 1] = static_cast(current); - } - else - { - vec[i] = static_cast(current); // LCOV_EXCL_LINE - } - } - - // step 2: convert array into number of type T and return - std::memcpy(&result, vec.data(), sizeof(NumberType)); - return true; - } - - /*! - @brief create a string by reading characters from the input - - @tparam NumberType the type of the number - @param[in] format the current format (for diagnostics) - @param[in] len number of characters to read - @param[out] result string created by reading @a len bytes - - @return whether string creation completed - - @note We can not reserve @a len bytes for the result, because @a len - may be too large. Usually, @ref unexpect_eof() detects the end of - the input before we run out of string memory. - */ - template - bool get_string(const input_format_t format, - const NumberType len, - string_t& result) - { - bool success = true; - for (NumberType i = 0; i < len; i++) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) - { - success = false; - break; - } - result.push_back(static_cast(current)); - } - return success; - } - - /*! - @brief create a byte array by reading bytes from the input - - @tparam NumberType the type of the number - @param[in] format the current format (for diagnostics) - @param[in] len number of bytes to read - @param[out] result byte array created by reading @a len bytes - - @return whether byte array creation completed - - @note We can not reserve @a len bytes for the result, because @a len - may be too large. Usually, @ref unexpect_eof() detects the end of - the input before we run out of memory. - */ - template - bool get_binary(const input_format_t format, - const NumberType len, - binary_t& result) - { - bool success = true; - for (NumberType i = 0; i < len; i++) - { - get(); - if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) - { - success = false; - break; - } - result.push_back(static_cast(current)); - } - return success; - } - - /*! - @param[in] format the current format (for diagnostics) - @param[in] context further context information (for diagnostics) - @return whether the last read character is not EOF - */ - JSON_HEDLEY_NON_NULL(3) - bool unexpect_eof(const input_format_t format, const char* context) const - { - if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) - { - return sax->parse_error(chars_read, "", - parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr)); - } - return true; - } - - /*! - @return a string representation of the last read byte - */ - std::string get_token_string() const - { - std::array cr{{}}; - static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - return std::string{cr.data()}; - } - - /*! - @param[in] format the current format - @param[in] detail a detailed error message - @param[in] context further context information - @return a message string to use in the parse_error exceptions - */ - std::string exception_message(const input_format_t format, - const std::string& detail, - const std::string& context) const - { - std::string error_msg = "syntax error while parsing "; - - switch (format) - { - case input_format_t::cbor: - error_msg += "CBOR"; - break; - - case input_format_t::msgpack: - error_msg += "MessagePack"; - break; - - case input_format_t::ubjson: - error_msg += "UBJSON"; - break; - - case input_format_t::bson: - error_msg += "BSON"; - break; - - case input_format_t::bjdata: - error_msg += "BJData"; - break; - - case input_format_t::json: // LCOV_EXCL_LINE - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - } - - return concat(error_msg, ' ', context, ": ", detail); - } - - private: - static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast(-1); - - /// input adapter - InputAdapterType ia; - - /// the current character - char_int_type current = std::char_traits::eof(); - - /// the number of characters read - std::size_t chars_read = 0; - - /// whether we can assume little endianness - const bool is_little_endian = little_endianness(); - - /// input format - const input_format_t input_format = input_format_t::json; - - /// the SAX parser - json_sax_t* sax = nullptr; - - // excluded markers in bjdata optimized type -#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \ - make_array('F', 'H', 'N', 'S', 'T', 'Z', '[', '{') - -#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \ - make_array( \ - bjd_type{'C', "char"}, \ - bjd_type{'D', "double"}, \ - bjd_type{'I', "int16"}, \ - bjd_type{'L', "int64"}, \ - bjd_type{'M', "uint64"}, \ - bjd_type{'U', "uint8"}, \ - bjd_type{'d', "single"}, \ - bjd_type{'i', "int8"}, \ - bjd_type{'l', "int32"}, \ - bjd_type{'m', "uint32"}, \ - bjd_type{'u', "uint16"}) - - JSON_PRIVATE_UNLESS_TESTED: - // lookup tables - // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) - const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers = - JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_; - - using bjd_type = std::pair; - // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes) - const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map = - JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_; - -#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ -#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ -}; - -#ifndef JSON_HAS_CPP_17 - template - constexpr std::size_t binary_reader::npos; -#endif - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/input/input_adapters.hpp b/r5dev/thirdparty/nlohmann/detail/input/input_adapters.hpp deleted file mode 100644 index cf53b1d5..00000000 --- a/r5dev/thirdparty/nlohmann/detail/input/input_adapters.hpp +++ /dev/null @@ -1,494 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // array -#include // size_t -#include // strlen -#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next -#include // shared_ptr, make_shared, addressof -#include // accumulate -#include // string, char_traits -#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer -#include // pair, declval - -#ifndef JSON_NO_IO - #include // FILE * - #include // istream -#endif // JSON_NO_IO - -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/// the supported input formats -enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata }; - -//////////////////// -// input adapters // -//////////////////// - -#ifndef JSON_NO_IO -/*! -Input adapter for stdio file access. This adapter read only 1 byte and do not use any - buffer. This adapter is a very low level adapter. -*/ -class file_input_adapter -{ - public: - using char_type = char; - - JSON_HEDLEY_NON_NULL(2) - explicit file_input_adapter(std::FILE* f) noexcept - : m_file(f) - { - JSON_ASSERT(m_file != nullptr); - } - - // make class move-only - file_input_adapter(const file_input_adapter&) = delete; - file_input_adapter(file_input_adapter&&) noexcept = default; - file_input_adapter& operator=(const file_input_adapter&) = delete; - file_input_adapter& operator=(file_input_adapter&&) = delete; - ~file_input_adapter() = default; - - std::char_traits::int_type get_character() noexcept - { - return std::fgetc(m_file); - } - - private: - /// the file pointer to read from - std::FILE* m_file; -}; - - -/*! -Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at -beginning of input. Does not support changing the underlying std::streambuf -in mid-input. Maintains underlying std::istream and std::streambuf to support -subsequent use of standard std::istream operations to process any input -characters following those used in parsing the JSON input. Clears the -std::istream flags; any input errors (e.g., EOF) will be detected by the first -subsequent call for input from the std::istream. -*/ -class input_stream_adapter -{ - public: - using char_type = char; - - ~input_stream_adapter() - { - // clear stream flags; we use underlying streambuf I/O, do not - // maintain ifstream flags, except eof - if (is != nullptr) - { - is->clear(is->rdstate() & std::ios::eofbit); - } - } - - explicit input_stream_adapter(std::istream& i) - : is(&i), sb(i.rdbuf()) - {} - - // delete because of pointer members - input_stream_adapter(const input_stream_adapter&) = delete; - input_stream_adapter& operator=(input_stream_adapter&) = delete; - input_stream_adapter& operator=(input_stream_adapter&&) = delete; - - input_stream_adapter(input_stream_adapter&& rhs) noexcept - : is(rhs.is), sb(rhs.sb) - { - rhs.is = nullptr; - rhs.sb = nullptr; - } - - // std::istream/std::streambuf use std::char_traits::to_int_type, to - // ensure that std::char_traits::eof() and the character 0xFF do not - // end up as the same value, e.g. 0xFFFFFFFF. - std::char_traits::int_type get_character() - { - auto res = sb->sbumpc(); - // set eof manually, as we don't use the istream interface. - if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) - { - is->clear(is->rdstate() | std::ios::eofbit); - } - return res; - } - - private: - /// the associated input stream - std::istream* is = nullptr; - std::streambuf* sb = nullptr; -}; -#endif // JSON_NO_IO - -// General-purpose iterator-based adapter. It might not be as fast as -// theoretically possible for some containers, but it is extremely versatile. -template -class iterator_input_adapter -{ - public: - using char_type = typename std::iterator_traits::value_type; - - iterator_input_adapter(IteratorType first, IteratorType last) - : current(std::move(first)), end(std::move(last)) - {} - - typename std::char_traits::int_type get_character() - { - if (JSON_HEDLEY_LIKELY(current != end)) - { - auto result = std::char_traits::to_int_type(*current); - std::advance(current, 1); - return result; - } - - return std::char_traits::eof(); - } - - private: - IteratorType current; - IteratorType end; - - template - friend struct wide_string_input_helper; - - bool empty() const - { - return current == end; - } -}; - - -template -struct wide_string_input_helper; - -template -struct wide_string_input_helper -{ - // UTF-32 - static void fill_buffer(BaseInputAdapter& input, - std::array::int_type, 4>& utf8_bytes, - size_t& utf8_bytes_index, - size_t& utf8_bytes_filled) - { - utf8_bytes_index = 0; - - if (JSON_HEDLEY_UNLIKELY(input.empty())) - { - utf8_bytes[0] = std::char_traits::eof(); - utf8_bytes_filled = 1; - } - else - { - // get the current character - const auto wc = input.get_character(); - - // UTF-32 to UTF-8 encoding - if (wc < 0x80) - { - utf8_bytes[0] = static_cast::int_type>(wc); - utf8_bytes_filled = 1; - } - else if (wc <= 0x7FF) - { - utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); - utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 2; - } - else if (wc <= 0xFFFF) - { - utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 3; - } - else if (wc <= 0x10FFFF) - { - utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); - utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 4; - } - else - { - // unknown character - utf8_bytes[0] = static_cast::int_type>(wc); - utf8_bytes_filled = 1; - } - } - } -}; - -template -struct wide_string_input_helper -{ - // UTF-16 - static void fill_buffer(BaseInputAdapter& input, - std::array::int_type, 4>& utf8_bytes, - size_t& utf8_bytes_index, - size_t& utf8_bytes_filled) - { - utf8_bytes_index = 0; - - if (JSON_HEDLEY_UNLIKELY(input.empty())) - { - utf8_bytes[0] = std::char_traits::eof(); - utf8_bytes_filled = 1; - } - else - { - // get the current character - const auto wc = input.get_character(); - - // UTF-16 to UTF-8 encoding - if (wc < 0x80) - { - utf8_bytes[0] = static_cast::int_type>(wc); - utf8_bytes_filled = 1; - } - else if (wc <= 0x7FF) - { - utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); - utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 2; - } - else if (0xD800 > wc || wc >= 0xE000) - { - utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); - utf8_bytes_filled = 3; - } - else - { - if (JSON_HEDLEY_UNLIKELY(!input.empty())) - { - const auto wc2 = static_cast(input.get_character()); - const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); - utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); - utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); - utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); - utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); - utf8_bytes_filled = 4; - } - else - { - utf8_bytes[0] = static_cast::int_type>(wc); - utf8_bytes_filled = 1; - } - } - } - } -}; - -// Wraps another input apdater to convert wide character types into individual bytes. -template -class wide_string_input_adapter -{ - public: - using char_type = char; - - wide_string_input_adapter(BaseInputAdapter base) - : base_adapter(base) {} - - typename std::char_traits::int_type get_character() noexcept - { - // check if buffer needs to be filled - if (utf8_bytes_index == utf8_bytes_filled) - { - fill_buffer(); - - JSON_ASSERT(utf8_bytes_filled > 0); - JSON_ASSERT(utf8_bytes_index == 0); - } - - // use buffer - JSON_ASSERT(utf8_bytes_filled > 0); - JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); - return utf8_bytes[utf8_bytes_index++]; - } - - private: - BaseInputAdapter base_adapter; - - template - void fill_buffer() - { - wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); - } - - /// a buffer for UTF-8 bytes - std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; - - /// index to the utf8_codes array for the next valid byte - std::size_t utf8_bytes_index = 0; - /// number of valid bytes in the utf8_codes array - std::size_t utf8_bytes_filled = 0; -}; - - -template -struct iterator_input_adapter_factory -{ - using iterator_type = IteratorType; - using char_type = typename std::iterator_traits::value_type; - using adapter_type = iterator_input_adapter; - - static adapter_type create(IteratorType first, IteratorType last) - { - return adapter_type(std::move(first), std::move(last)); - } -}; - -template -struct is_iterator_of_multibyte -{ - using value_type = typename std::iterator_traits::value_type; - enum - { - value = sizeof(value_type) > 1 - }; -}; - -template -struct iterator_input_adapter_factory::value>> -{ - using iterator_type = IteratorType; - using char_type = typename std::iterator_traits::value_type; - using base_adapter_type = iterator_input_adapter; - using adapter_type = wide_string_input_adapter; - - static adapter_type create(IteratorType first, IteratorType last) - { - return adapter_type(base_adapter_type(std::move(first), std::move(last))); - } -}; - -// General purpose iterator-based input -template -typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) -{ - using factory_type = iterator_input_adapter_factory; - return factory_type::create(first, last); -} - -// Convenience shorthand from container to iterator -// Enables ADL on begin(container) and end(container) -// Encloses the using declarations in namespace for not to leak them to outside scope - -namespace container_input_adapter_factory_impl -{ - -using std::begin; -using std::end; - -template -struct container_input_adapter_factory {}; - -template -struct container_input_adapter_factory< ContainerType, - void_t()), end(std::declval()))>> - { - using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); - - static adapter_type create(const ContainerType& container) -{ - return input_adapter(begin(container), end(container)); -} - }; - -} // namespace container_input_adapter_factory_impl - -template -typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) -{ - return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); -} - -#ifndef JSON_NO_IO -// Special cases with fast paths -inline file_input_adapter input_adapter(std::FILE* file) -{ - return file_input_adapter(file); -} - -inline input_stream_adapter input_adapter(std::istream& stream) -{ - return input_stream_adapter(stream); -} - -inline input_stream_adapter input_adapter(std::istream&& stream) -{ - return input_stream_adapter(stream); -} -#endif // JSON_NO_IO - -using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); - -// Null-delimited strings, and the like. -template < typename CharT, - typename std::enable_if < - std::is_pointer::value&& - !std::is_array::value&& - std::is_integral::type>::value&& - sizeof(typename std::remove_pointer::type) == 1, - int >::type = 0 > -contiguous_bytes_input_adapter input_adapter(CharT b) -{ - auto length = std::strlen(reinterpret_cast(b)); - const auto* ptr = reinterpret_cast(b); - return input_adapter(ptr, ptr + length); -} - -template -auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) -{ - return input_adapter(array, array + N); -} - -// This class only handles inputs of input_buffer_adapter type. -// It's required so that expressions like {ptr, len} can be implicitly cast -// to the correct adapter. -class span_input_adapter -{ - public: - template < typename CharT, - typename std::enable_if < - std::is_pointer::value&& - std::is_integral::type>::value&& - sizeof(typename std::remove_pointer::type) == 1, - int >::type = 0 > - span_input_adapter(CharT b, std::size_t l) - : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} - - template::iterator_category, std::random_access_iterator_tag>::value, - int>::type = 0> - span_input_adapter(IteratorType first, IteratorType last) - : ia(input_adapter(first, last)) {} - - contiguous_bytes_input_adapter&& get() - { - return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) - } - - private: - contiguous_bytes_input_adapter ia; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/input/json_sax.hpp b/r5dev/thirdparty/nlohmann/detail/input/json_sax.hpp deleted file mode 100644 index 5bd5c51c..00000000 --- a/r5dev/thirdparty/nlohmann/detail/input/json_sax.hpp +++ /dev/null @@ -1,728 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include // string -#include // move -#include // vector - -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN - -/*! -@brief SAX interface - -This class describes the SAX interface used by @ref nlohmann::json::sax_parse. -Each function is called in different situations while the input is parsed. The -boolean return value informs the parser whether to continue processing the -input. -*/ -template -struct json_sax -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - - /*! - @brief a null value was read - @return whether parsing should proceed - */ - virtual bool null() = 0; - - /*! - @brief a boolean value was read - @param[in] val boolean value - @return whether parsing should proceed - */ - virtual bool boolean(bool val) = 0; - - /*! - @brief an integer number was read - @param[in] val integer value - @return whether parsing should proceed - */ - virtual bool number_integer(number_integer_t val) = 0; - - /*! - @brief an unsigned integer number was read - @param[in] val unsigned integer value - @return whether parsing should proceed - */ - virtual bool number_unsigned(number_unsigned_t val) = 0; - - /*! - @brief a floating-point number was read - @param[in] val floating-point value - @param[in] s raw token value - @return whether parsing should proceed - */ - virtual bool number_float(number_float_t val, const string_t& s) = 0; - - /*! - @brief a string value was read - @param[in] val string value - @return whether parsing should proceed - @note It is safe to move the passed string value. - */ - virtual bool string(string_t& val) = 0; - - /*! - @brief a binary value was read - @param[in] val binary value - @return whether parsing should proceed - @note It is safe to move the passed binary value. - */ - virtual bool binary(binary_t& val) = 0; - - /*! - @brief the beginning of an object was read - @param[in] elements number of object elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_object(std::size_t elements) = 0; - - /*! - @brief an object key was read - @param[in] val object key - @return whether parsing should proceed - @note It is safe to move the passed string. - */ - virtual bool key(string_t& val) = 0; - - /*! - @brief the end of an object was read - @return whether parsing should proceed - */ - virtual bool end_object() = 0; - - /*! - @brief the beginning of an array was read - @param[in] elements number of array elements or -1 if unknown - @return whether parsing should proceed - @note binary formats may report the number of elements - */ - virtual bool start_array(std::size_t elements) = 0; - - /*! - @brief the end of an array was read - @return whether parsing should proceed - */ - virtual bool end_array() = 0; - - /*! - @brief a parse error occurred - @param[in] position the position in the input where the error occurs - @param[in] last_token the last read token - @param[in] ex an exception object describing the error - @return whether parsing should proceed (must return false) - */ - virtual bool parse_error(std::size_t position, - const std::string& last_token, - const detail::exception& ex) = 0; - - json_sax() = default; - json_sax(const json_sax&) = default; - json_sax(json_sax&&) noexcept = default; - json_sax& operator=(const json_sax&) = default; - json_sax& operator=(json_sax&&) noexcept = default; - virtual ~json_sax() = default; -}; - - -namespace detail -{ -/*! -@brief SAX implementation to create a JSON value from SAX events - -This class implements the @ref json_sax interface and processes the SAX events -to create a JSON value which makes it basically a DOM parser. The structure or -hierarchy of the JSON value is managed by the stack `ref_stack` which contains -a pointer to the respective array or object for each recursion depth. - -After successful parsing, the value that is passed by reference to the -constructor contains the parsed value. - -@tparam BasicJsonType the JSON type -*/ -template -class json_sax_dom_parser -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - - /*! - @param[in,out] r reference to a JSON value that is manipulated while - parsing - @param[in] allow_exceptions_ whether parse errors yield exceptions - */ - explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) - : root(r), allow_exceptions(allow_exceptions_) - {} - - // make class move-only - json_sax_dom_parser(const json_sax_dom_parser&) = delete; - json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; - json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~json_sax_dom_parser() = default; - - bool null() - { - handle_value(nullptr); - return true; - } - - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } - - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } - - bool string(string_t& val) - { - handle_value(val); - return true; - } - - bool binary(binary_t& val) - { - handle_value(std::move(val)); - return true; - } - - bool start_object(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); - - if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); - } - - return true; - } - - bool key(string_t& val) - { - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(ref_stack.back()->is_object()); - - // add null at given key and store the reference for later - object_element = &(ref_stack.back()->m_value.object->operator[](val)); - return true; - } - - bool end_object() - { - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(ref_stack.back()->is_object()); - - ref_stack.back()->set_parents(); - ref_stack.pop_back(); - return true; - } - - bool start_array(std::size_t len) - { - ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); - - if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); - } - - return true; - } - - bool end_array() - { - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(ref_stack.back()->is_array()); - - ref_stack.back()->set_parents(); - ref_stack.pop_back(); - return true; - } - - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - - private: - /*! - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - */ - template - JSON_HEDLEY_RETURNS_NON_NULL - BasicJsonType* handle_value(Value&& v) - { - if (ref_stack.empty()) - { - root = BasicJsonType(std::forward(v)); - return &root; - } - - JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); - - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->emplace_back(std::forward(v)); - return &(ref_stack.back()->m_value.array->back()); - } - - JSON_ASSERT(ref_stack.back()->is_object()); - JSON_ASSERT(object_element); - *object_element = BasicJsonType(std::forward(v)); - return object_element; - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack {}; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; -}; - -template -class json_sax_dom_callback_parser -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - using parser_callback_t = typename BasicJsonType::parser_callback_t; - using parse_event_t = typename BasicJsonType::parse_event_t; - - json_sax_dom_callback_parser(BasicJsonType& r, - const parser_callback_t cb, - const bool allow_exceptions_ = true) - : root(r), callback(cb), allow_exceptions(allow_exceptions_) - { - keep_stack.push_back(true); - } - - // make class move-only - json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; - json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~json_sax_dom_callback_parser() = default; - - bool null() - { - handle_value(nullptr); - return true; - } - - bool boolean(bool val) - { - handle_value(val); - return true; - } - - bool number_integer(number_integer_t val) - { - handle_value(val); - return true; - } - - bool number_unsigned(number_unsigned_t val) - { - handle_value(val); - return true; - } - - bool number_float(number_float_t val, const string_t& /*unused*/) - { - handle_value(val); - return true; - } - - bool string(string_t& val) - { - handle_value(val); - return true; - } - - bool binary(binary_t& val) - { - handle_value(std::move(val)); - return true; - } - - bool start_object(std::size_t len) - { - // check callback for object start - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::object, true); - ref_stack.push_back(val.second); - - // check object limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back())); - } - - return true; - } - - bool key(string_t& val) - { - BasicJsonType k = BasicJsonType(val); - - // check callback for key - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); - key_keep_stack.push_back(keep); - - // add discarded value at given key and store the reference for later - if (keep && ref_stack.back()) - { - object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); - } - - return true; - } - - bool end_object() - { - if (ref_stack.back()) - { - if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) - { - // discard object - *ref_stack.back() = discarded; - } - else - { - ref_stack.back()->set_parents(); - } - } - - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(!keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) - { - // remove discarded value - for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) - { - if (it->is_discarded()) - { - ref_stack.back()->erase(it); - break; - } - } - } - - return true; - } - - bool start_array(std::size_t len) - { - const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); - keep_stack.push_back(keep); - - auto val = handle_value(BasicJsonType::value_t::array, true); - ref_stack.push_back(val.second); - - // check array limit - if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) - { - JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back())); - } - - return true; - } - - bool end_array() - { - bool keep = true; - - if (ref_stack.back()) - { - keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); - if (keep) - { - ref_stack.back()->set_parents(); - } - else - { - // discard array - *ref_stack.back() = discarded; - } - } - - JSON_ASSERT(!ref_stack.empty()); - JSON_ASSERT(!keep_stack.empty()); - ref_stack.pop_back(); - keep_stack.pop_back(); - - // remove discarded value - if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->pop_back(); - } - - return true; - } - - template - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, - const Exception& ex) - { - errored = true; - static_cast(ex); - if (allow_exceptions) - { - JSON_THROW(ex); - } - return false; - } - - constexpr bool is_errored() const - { - return errored; - } - - private: - /*! - @param[in] v value to add to the JSON value we build during parsing - @param[in] skip_callback whether we should skip calling the callback - function; this is required after start_array() and - start_object() SAX events, because otherwise we would call the - callback function with an empty array or object, respectively. - - @invariant If the ref stack is empty, then the passed value will be the new - root. - @invariant If the ref stack contains a value, then it is an array or an - object to which we can add elements - - @return pair of boolean (whether value should be kept) and pointer (to the - passed value in the ref_stack hierarchy; nullptr if not kept) - */ - template - std::pair handle_value(Value&& v, const bool skip_callback = false) - { - JSON_ASSERT(!keep_stack.empty()); - - // do not handle this value if we know it would be added to a discarded - // container - if (!keep_stack.back()) - { - return {false, nullptr}; - } - - // create value - auto value = BasicJsonType(std::forward(v)); - - // check callback - const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); - - // do not handle this value if we just learnt it shall be discarded - if (!keep) - { - return {false, nullptr}; - } - - if (ref_stack.empty()) - { - root = std::move(value); - return {true, &root}; - } - - // skip this value if we already decided to skip the parent - // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) - if (!ref_stack.back()) - { - return {false, nullptr}; - } - - // we now only expect arrays and objects - JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); - - // array - if (ref_stack.back()->is_array()) - { - ref_stack.back()->m_value.array->emplace_back(std::move(value)); - return {true, &(ref_stack.back()->m_value.array->back())}; - } - - // object - JSON_ASSERT(ref_stack.back()->is_object()); - // check if we should store an element for the current key - JSON_ASSERT(!key_keep_stack.empty()); - const bool store_element = key_keep_stack.back(); - key_keep_stack.pop_back(); - - if (!store_element) - { - return {false, nullptr}; - } - - JSON_ASSERT(object_element); - *object_element = std::move(value); - return {true, object_element}; - } - - /// the parsed JSON value - BasicJsonType& root; - /// stack to model hierarchy of values - std::vector ref_stack {}; - /// stack to manage which values to keep - std::vector keep_stack {}; - /// stack to manage which object keys to keep - std::vector key_keep_stack {}; - /// helper to hold the reference for the next object element - BasicJsonType* object_element = nullptr; - /// whether a syntax error occurred - bool errored = false; - /// callback function - const parser_callback_t callback = nullptr; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; - /// a discarded value for the callback - BasicJsonType discarded = BasicJsonType::value_t::discarded; -}; - -template -class json_sax_acceptor -{ - public: - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - - bool null() - { - return true; - } - - bool boolean(bool /*unused*/) - { - return true; - } - - bool number_integer(number_integer_t /*unused*/) - { - return true; - } - - bool number_unsigned(number_unsigned_t /*unused*/) - { - return true; - } - - bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) - { - return true; - } - - bool string(string_t& /*unused*/) - { - return true; - } - - bool binary(binary_t& /*unused*/) - { - return true; - } - - bool start_object(std::size_t /*unused*/ = static_cast(-1)) - { - return true; - } - - bool key(string_t& /*unused*/) - { - return true; - } - - bool end_object() - { - return true; - } - - bool start_array(std::size_t /*unused*/ = static_cast(-1)) - { - return true; - } - - bool end_array() - { - return true; - } - - bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) - { - return false; - } -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/input/lexer.hpp b/r5dev/thirdparty/nlohmann/detail/input/lexer.hpp deleted file mode 100644 index 72e99510..00000000 --- a/r5dev/thirdparty/nlohmann/detail/input/lexer.hpp +++ /dev/null @@ -1,1632 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // array -#include // localeconv -#include // size_t -#include // snprintf -#include // strtof, strtod, strtold, strtoll, strtoull -#include // initializer_list -#include // char_traits, string -#include // move -#include // vector - -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/////////// -// lexer // -/////////// - -template -class lexer_base -{ - public: - /// token types for the parser - enum class token_type - { - uninitialized, ///< indicating the scanner is uninitialized - literal_true, ///< the `true` literal - literal_false, ///< the `false` literal - literal_null, ///< the `null` literal - value_string, ///< a string -- use get_string() for actual value - value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value - value_integer, ///< a signed integer -- use get_number_integer() for actual value - value_float, ///< an floating point number -- use get_number_float() for actual value - begin_array, ///< the character for array begin `[` - begin_object, ///< the character for object begin `{` - end_array, ///< the character for array end `]` - end_object, ///< the character for object end `}` - name_separator, ///< the name separator `:` - value_separator, ///< the value separator `,` - parse_error, ///< indicating a parse error - end_of_input, ///< indicating the end of the input buffer - literal_or_value ///< a literal or the begin of a value (only for diagnostics) - }; - - /// return name of values of type token_type (only used for errors) - JSON_HEDLEY_RETURNS_NON_NULL - JSON_HEDLEY_CONST - static const char* token_type_name(const token_type t) noexcept - { - switch (t) - { - case token_type::uninitialized: - return ""; - case token_type::literal_true: - return "true literal"; - case token_type::literal_false: - return "false literal"; - case token_type::literal_null: - return "null literal"; - case token_type::value_string: - return "string literal"; - case token_type::value_unsigned: - case token_type::value_integer: - case token_type::value_float: - return "number literal"; - case token_type::begin_array: - return "'['"; - case token_type::begin_object: - return "'{'"; - case token_type::end_array: - return "']'"; - case token_type::end_object: - return "'}'"; - case token_type::name_separator: - return "':'"; - case token_type::value_separator: - return "','"; - case token_type::parse_error: - return ""; - case token_type::end_of_input: - return "end of input"; - case token_type::literal_or_value: - return "'[', '{', or a literal"; - // LCOV_EXCL_START - default: // catch non-enum values - return "unknown token"; - // LCOV_EXCL_STOP - } - } -}; -/*! -@brief lexical analysis - -This class organizes the lexical analysis during JSON deserialization. -*/ -template -class lexer : public lexer_base -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using char_type = typename InputAdapterType::char_type; - using char_int_type = typename std::char_traits::int_type; - - public: - using token_type = typename lexer_base::token_type; - - explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept - : ia(std::move(adapter)) - , ignore_comments(ignore_comments_) - , decimal_point_char(static_cast(get_decimal_point())) - {} - - // delete because of pointer members - lexer(const lexer&) = delete; - lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - lexer& operator=(lexer&) = delete; - lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) - ~lexer() = default; - - private: - ///////////////////// - // locales - ///////////////////// - - /// return the locale-dependent decimal point - JSON_HEDLEY_PURE - static char get_decimal_point() noexcept - { - const auto* loc = localeconv(); - JSON_ASSERT(loc != nullptr); - return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); - } - - ///////////////////// - // scan functions - ///////////////////// - - /*! - @brief get codepoint from 4 hex characters following `\u` - - For input "\u c1 c2 c3 c4" the codepoint is: - (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 - = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) - - Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' - must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The - conversion is done by subtracting the offset (0x30, 0x37, and 0x57) - between the ASCII value of the character and the desired integer value. - - @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or - non-hex character) - */ - int get_codepoint() - { - // this function only makes sense after reading `\u` - JSON_ASSERT(current == 'u'); - int codepoint = 0; - - const auto factors = { 12u, 8u, 4u, 0u }; - for (const auto factor : factors) - { - get(); - - if (current >= '0' && current <= '9') - { - codepoint += static_cast((static_cast(current) - 0x30u) << factor); - } - else if (current >= 'A' && current <= 'F') - { - codepoint += static_cast((static_cast(current) - 0x37u) << factor); - } - else if (current >= 'a' && current <= 'f') - { - codepoint += static_cast((static_cast(current) - 0x57u) << factor); - } - else - { - return -1; - } - } - - JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); - return codepoint; - } - - /*! - @brief check if the next byte(s) are inside a given range - - Adds the current byte and, for each passed range, reads a new byte and - checks if it is inside the range. If a violation was detected, set up an - error message and return false. Otherwise, return true. - - @param[in] ranges list of integers; interpreted as list of pairs of - inclusive lower and upper bound, respectively - - @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, - 1, 2, or 3 pairs. This precondition is enforced by an assertion. - - @return true if and only if no range violation was detected - */ - bool next_byte_in_range(std::initializer_list ranges) - { - JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); - add(current); - - for (auto range = ranges.begin(); range != ranges.end(); ++range) - { - get(); - if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) - { - add(current); - } - else - { - error_message = "invalid string: ill-formed UTF-8 byte"; - return false; - } - } - - return true; - } - - /*! - @brief scan a string literal - - This function scans a string according to Sect. 7 of RFC 8259. While - scanning, bytes are escaped and copied into buffer token_buffer. Then the - function returns successfully, token_buffer is *not* null-terminated (as it - may contain \0 bytes), and token_buffer.size() is the number of bytes in the - string. - - @return token_type::value_string if string could be successfully scanned, - token_type::parse_error otherwise - - @note In case of errors, variable error_message contains a textual - description. - */ - token_type scan_string() - { - // reset token_buffer (ignore opening quote) - reset(); - - // we entered the function by reading an open quote - JSON_ASSERT(current == '\"'); - - while (true) - { - // get next character - switch (get()) - { - // end of file while parsing string - case std::char_traits::eof(): - { - error_message = "invalid string: missing closing quote"; - return token_type::parse_error; - } - - // closing quote - case '\"': - { - return token_type::value_string; - } - - // escapes - case '\\': - { - switch (get()) - { - // quotation mark - case '\"': - add('\"'); - break; - // reverse solidus - case '\\': - add('\\'); - break; - // solidus - case '/': - add('/'); - break; - // backspace - case 'b': - add('\b'); - break; - // form feed - case 'f': - add('\f'); - break; - // line feed - case 'n': - add('\n'); - break; - // carriage return - case 'r': - add('\r'); - break; - // tab - case 't': - add('\t'); - break; - - // unicode escapes - case 'u': - { - const int codepoint1 = get_codepoint(); - int codepoint = codepoint1; // start with codepoint1 - - if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } - - // check if code point is a high surrogate - if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) - { - // expect next \uxxxx entry - if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) - { - const int codepoint2 = get_codepoint(); - - if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) - { - error_message = "invalid string: '\\u' must be followed by 4 hex digits"; - return token_type::parse_error; - } - - // check if codepoint2 is a low surrogate - if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) - { - // overwrite codepoint - codepoint = static_cast( - // high surrogate occupies the most significant 22 bits - (static_cast(codepoint1) << 10u) - // low surrogate occupies the least significant 15 bits - + static_cast(codepoint2) - // there is still the 0xD800, 0xDC00 and 0x10000 noise - // in the result, so we have to subtract with: - // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 - - 0x35FDC00u); - } - else - { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; - return token_type::parse_error; - } - } - else - { - if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) - { - error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; - return token_type::parse_error; - } - } - - // result of the above calculation yields a proper codepoint - JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); - - // translate codepoint into bytes - if (codepoint < 0x80) - { - // 1-byte characters: 0xxxxxxx (ASCII) - add(static_cast(codepoint)); - } - else if (codepoint <= 0x7FF) - { - // 2-byte characters: 110xxxxx 10xxxxxx - add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - else if (codepoint <= 0xFFFF) - { - // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx - add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - else - { - // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); - add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); - add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); - } - - break; - } - - // other characters after escape - default: - error_message = "invalid string: forbidden character after backslash"; - return token_type::parse_error; - } - - break; - } - - // invalid control characters - case 0x00: - { - error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; - return token_type::parse_error; - } - - case 0x01: - { - error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; - return token_type::parse_error; - } - - case 0x02: - { - error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; - return token_type::parse_error; - } - - case 0x03: - { - error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; - return token_type::parse_error; - } - - case 0x04: - { - error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; - return token_type::parse_error; - } - - case 0x05: - { - error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; - return token_type::parse_error; - } - - case 0x06: - { - error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; - return token_type::parse_error; - } - - case 0x07: - { - error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; - return token_type::parse_error; - } - - case 0x08: - { - error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; - return token_type::parse_error; - } - - case 0x09: - { - error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; - return token_type::parse_error; - } - - case 0x0A: - { - error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; - return token_type::parse_error; - } - - case 0x0B: - { - error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; - return token_type::parse_error; - } - - case 0x0C: - { - error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; - return token_type::parse_error; - } - - case 0x0D: - { - error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; - return token_type::parse_error; - } - - case 0x0E: - { - error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; - return token_type::parse_error; - } - - case 0x0F: - { - error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; - return token_type::parse_error; - } - - case 0x10: - { - error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; - return token_type::parse_error; - } - - case 0x11: - { - error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; - return token_type::parse_error; - } - - case 0x12: - { - error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; - return token_type::parse_error; - } - - case 0x13: - { - error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; - return token_type::parse_error; - } - - case 0x14: - { - error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; - return token_type::parse_error; - } - - case 0x15: - { - error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; - return token_type::parse_error; - } - - case 0x16: - { - error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; - return token_type::parse_error; - } - - case 0x17: - { - error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; - return token_type::parse_error; - } - - case 0x18: - { - error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; - return token_type::parse_error; - } - - case 0x19: - { - error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; - return token_type::parse_error; - } - - case 0x1A: - { - error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; - return token_type::parse_error; - } - - case 0x1B: - { - error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; - return token_type::parse_error; - } - - case 0x1C: - { - error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; - return token_type::parse_error; - } - - case 0x1D: - { - error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; - return token_type::parse_error; - } - - case 0x1E: - { - error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; - return token_type::parse_error; - } - - case 0x1F: - { - error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; - return token_type::parse_error; - } - - // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) - case 0x20: - case 0x21: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2A: - case 0x2B: - case 0x2C: - case 0x2D: - case 0x2E: - case 0x2F: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3A: - case 0x3B: - case 0x3C: - case 0x3D: - case 0x3E: - case 0x3F: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4A: - case 0x4B: - case 0x4C: - case 0x4D: - case 0x4E: - case 0x4F: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5A: - case 0x5B: - case 0x5D: - case 0x5E: - case 0x5F: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6A: - case 0x6B: - case 0x6C: - case 0x6D: - case 0x6E: - case 0x6F: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7A: - case 0x7B: - case 0x7C: - case 0x7D: - case 0x7E: - case 0x7F: - { - add(current); - break; - } - - // U+0080..U+07FF: bytes C2..DF 80..BF - case 0xC2: - case 0xC3: - case 0xC4: - case 0xC5: - case 0xC6: - case 0xC7: - case 0xC8: - case 0xC9: - case 0xCA: - case 0xCB: - case 0xCC: - case 0xCD: - case 0xCE: - case 0xCF: - case 0xD0: - case 0xD1: - case 0xD2: - case 0xD3: - case 0xD4: - case 0xD5: - case 0xD6: - case 0xD7: - case 0xD8: - case 0xD9: - case 0xDA: - case 0xDB: - case 0xDC: - case 0xDD: - case 0xDE: - case 0xDF: - { - if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) - { - return token_type::parse_error; - } - break; - } - - // U+0800..U+0FFF: bytes E0 A0..BF 80..BF - case 0xE0: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } - - // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF - // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF - case 0xE1: - case 0xE2: - case 0xE3: - case 0xE4: - case 0xE5: - case 0xE6: - case 0xE7: - case 0xE8: - case 0xE9: - case 0xEA: - case 0xEB: - case 0xEC: - case 0xEE: - case 0xEF: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } - - // U+D000..U+D7FF: bytes ED 80..9F 80..BF - case 0xED: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } - - // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF - case 0xF0: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } - - // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF - case 0xF1: - case 0xF2: - case 0xF3: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } - - // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF - case 0xF4: - { - if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) - { - return token_type::parse_error; - } - break; - } - - // remaining bytes (80..C1 and F5..FF) are ill-formed - default: - { - error_message = "invalid string: ill-formed UTF-8 byte"; - return token_type::parse_error; - } - } - } - } - - /*! - * @brief scan a comment - * @return whether comment could be scanned successfully - */ - bool scan_comment() - { - switch (get()) - { - // single-line comments skip input until a newline or EOF is read - case '/': - { - while (true) - { - switch (get()) - { - case '\n': - case '\r': - case std::char_traits::eof(): - case '\0': - return true; - - default: - break; - } - } - } - - // multi-line comments skip input until */ is read - case '*': - { - while (true) - { - switch (get()) - { - case std::char_traits::eof(): - case '\0': - { - error_message = "invalid comment; missing closing '*/'"; - return false; - } - - case '*': - { - switch (get()) - { - case '/': - return true; - - default: - { - unget(); - continue; - } - } - } - - default: - continue; - } - } - } - - // unexpected character after reading '/' - default: - { - error_message = "invalid comment; expecting '/' or '*' after '/'"; - return false; - } - } - } - - JSON_HEDLEY_NON_NULL(2) - static void strtof(float& f, const char* str, char** endptr) noexcept - { - f = std::strtof(str, endptr); - } - - JSON_HEDLEY_NON_NULL(2) - static void strtof(double& f, const char* str, char** endptr) noexcept - { - f = std::strtod(str, endptr); - } - - JSON_HEDLEY_NON_NULL(2) - static void strtof(long double& f, const char* str, char** endptr) noexcept - { - f = std::strtold(str, endptr); - } - - /*! - @brief scan a number literal - - This function scans a string according to Sect. 6 of RFC 8259. - - The function is realized with a deterministic finite state machine derived - from the grammar described in RFC 8259. Starting in state "init", the - input is read and used to determined the next state. Only state "done" - accepts the number. State "error" is a trap state to model errors. In the - table below, "anything" means any character but the ones listed before. - - state | 0 | 1-9 | e E | + | - | . | anything - ---------|----------|----------|----------|---------|---------|----------|----------- - init | zero | any1 | [error] | [error] | minus | [error] | [error] - minus | zero | any1 | [error] | [error] | [error] | [error] | [error] - zero | done | done | exponent | done | done | decimal1 | done - any1 | any1 | any1 | exponent | done | done | decimal1 | done - decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] - decimal2 | decimal2 | decimal2 | exponent | done | done | done | done - exponent | any2 | any2 | [error] | sign | sign | [error] | [error] - sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] - any2 | any2 | any2 | done | done | done | done | done - - The state machine is realized with one label per state (prefixed with - "scan_number_") and `goto` statements between them. The state machine - contains cycles, but any cycle can be left when EOF is read. Therefore, - the function is guaranteed to terminate. - - During scanning, the read bytes are stored in token_buffer. This string is - then converted to a signed integer, an unsigned integer, or a - floating-point number. - - @return token_type::value_unsigned, token_type::value_integer, or - token_type::value_float if number could be successfully scanned, - token_type::parse_error otherwise - - @note The scanner is independent of the current locale. Internally, the - locale's decimal point is used instead of `.` to work with the - locale-dependent converters. - */ - token_type scan_number() // lgtm [cpp/use-of-goto] - { - // reset token_buffer to store the number's bytes - reset(); - - // the type of the parsed number; initially set to unsigned; will be - // changed if minus sign, decimal point or exponent is read - token_type number_type = token_type::value_unsigned; - - // state (init): we just found out we need to scan a number - switch (current) - { - case '-': - { - add(current); - goto scan_number_minus; - } - - case '0': - { - add(current); - goto scan_number_zero; - } - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } - - // all other characters are rejected outside scan_number() - default: // LCOV_EXCL_LINE - JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE - } - -scan_number_minus: - // state: we just parsed a leading minus sign - number_type = token_type::value_integer; - switch (get()) - { - case '0': - { - add(current); - goto scan_number_zero; - } - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } - - default: - { - error_message = "invalid number; expected digit after '-'"; - return token_type::parse_error; - } - } - -scan_number_zero: - // state: we just parse a zero (maybe with a leading minus sign) - switch (get()) - { - case '.': - { - add(decimal_point_char); - goto scan_number_decimal1; - } - - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } - - default: - goto scan_number_done; - } - -scan_number_any1: - // state: we just parsed a number 0-9 (maybe with a leading minus sign) - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any1; - } - - case '.': - { - add(decimal_point_char); - goto scan_number_decimal1; - } - - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } - - default: - goto scan_number_done; - } - -scan_number_decimal1: - // state: we just parsed a decimal point - number_type = token_type::value_float; - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_decimal2; - } - - default: - { - error_message = "invalid number; expected digit after '.'"; - return token_type::parse_error; - } - } - -scan_number_decimal2: - // we just parsed at least one number after a decimal point - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_decimal2; - } - - case 'e': - case 'E': - { - add(current); - goto scan_number_exponent; - } - - default: - goto scan_number_done; - } - -scan_number_exponent: - // we just parsed an exponent - number_type = token_type::value_float; - switch (get()) - { - case '+': - case '-': - { - add(current); - goto scan_number_sign; - } - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } - - default: - { - error_message = - "invalid number; expected '+', '-', or digit after exponent"; - return token_type::parse_error; - } - } - -scan_number_sign: - // we just parsed an exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } - - default: - { - error_message = "invalid number; expected digit after exponent sign"; - return token_type::parse_error; - } - } - -scan_number_any2: - // we just parsed a number after the exponent or exponent sign - switch (get()) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - add(current); - goto scan_number_any2; - } - - default: - goto scan_number_done; - } - -scan_number_done: - // unget the character after the number (we only read it to know that - // we are done scanning a number) - unget(); - - char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - errno = 0; - - // try to parse integers first and fall back to floats - if (number_type == token_type::value_unsigned) - { - const auto x = std::strtoull(token_buffer.data(), &endptr, 10); - - // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - - if (errno == 0) - { - value_unsigned = static_cast(x); - if (value_unsigned == x) - { - return token_type::value_unsigned; - } - } - } - else if (number_type == token_type::value_integer) - { - const auto x = std::strtoll(token_buffer.data(), &endptr, 10); - - // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - - if (errno == 0) - { - value_integer = static_cast(x); - if (value_integer == x) - { - return token_type::value_integer; - } - } - } - - // this code is reached if we parse a floating-point number or if an - // integer conversion above failed - strtof(value_float, token_buffer.data(), &endptr); - - // we checked the number format before - JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); - - return token_type::value_float; - } - - /*! - @param[in] literal_text the literal text to expect - @param[in] length the length of the passed literal text - @param[in] return_type the token type to return on success - */ - JSON_HEDLEY_NON_NULL(2) - token_type scan_literal(const char_type* literal_text, const std::size_t length, - token_type return_type) - { - JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); - for (std::size_t i = 1; i < length; ++i) - { - if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) - { - error_message = "invalid literal"; - return token_type::parse_error; - } - } - return return_type; - } - - ///////////////////// - // input management - ///////////////////// - - /// reset token_buffer; current character is beginning of token - void reset() noexcept - { - token_buffer.clear(); - token_string.clear(); - token_string.push_back(std::char_traits::to_char_type(current)); - } - - /* - @brief get next character from the input - - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns a - `std::char_traits::eof()` in that case. Stores the scanned characters - for use in error messages. - - @return character read from the input - */ - char_int_type get() - { - ++position.chars_read_total; - ++position.chars_read_current_line; - - if (next_unget) - { - // just reset the next_unget variable and work with current - next_unget = false; - } - else - { - current = ia.get_character(); - } - - if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) - { - token_string.push_back(std::char_traits::to_char_type(current)); - } - - if (current == '\n') - { - ++position.lines_read; - position.chars_read_current_line = 0; - } - - return current; - } - - /*! - @brief unget current character (read it again on next get) - - We implement unget by setting variable next_unget to true. The input is not - changed - we just simulate ungetting by modifying chars_read_total, - chars_read_current_line, and token_string. The next call to get() will - behave as if the unget character is read again. - */ - void unget() - { - next_unget = true; - - --position.chars_read_total; - - // in case we "unget" a newline, we have to also decrement the lines_read - if (position.chars_read_current_line == 0) - { - if (position.lines_read > 0) - { - --position.lines_read; - } - } - else - { - --position.chars_read_current_line; - } - - if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) - { - JSON_ASSERT(!token_string.empty()); - token_string.pop_back(); - } - } - - /// add a character to token_buffer - void add(char_int_type c) - { - token_buffer.push_back(static_cast(c)); - } - - public: - ///////////////////// - // value getters - ///////////////////// - - /// return integer value - constexpr number_integer_t get_number_integer() const noexcept - { - return value_integer; - } - - /// return unsigned integer value - constexpr number_unsigned_t get_number_unsigned() const noexcept - { - return value_unsigned; - } - - /// return floating-point value - constexpr number_float_t get_number_float() const noexcept - { - return value_float; - } - - /// return current string value (implicitly resets the token; useful only once) - string_t& get_string() - { - return token_buffer; - } - - ///////////////////// - // diagnostics - ///////////////////// - - /// return position of last read token - constexpr position_t get_position() const noexcept - { - return position; - } - - /// return the last read token (for errors only). Will never contain EOF - /// (an arbitrary value that is not a valid char value, often -1), because - /// 255 may legitimately occur. May contain NUL, which should be escaped. - std::string get_token_string() const - { - // escape control characters - std::string result; - for (const auto c : token_string) - { - if (static_cast(c) <= '\x1F') - { - // escape control characters - std::array cs{{}}; - static_cast((std::snprintf)(cs.data(), cs.size(), "", static_cast(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) - result += cs.data(); - } - else - { - // add character as is - result.push_back(static_cast(c)); - } - } - - return result; - } - - /// return syntax error message - JSON_HEDLEY_RETURNS_NON_NULL - constexpr const char* get_error_message() const noexcept - { - return error_message; - } - - ///////////////////// - // actual scanner - ///////////////////// - - /*! - @brief skip the UTF-8 byte order mark - @return true iff there is no BOM or the correct BOM has been skipped - */ - bool skip_bom() - { - if (get() == 0xEF) - { - // check if we completely parse the BOM - return get() == 0xBB && get() == 0xBF; - } - - // the first character is not the beginning of the BOM; unget it to - // process is later - unget(); - return true; - } - - void skip_whitespace() - { - do - { - get(); - } - while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); - } - - token_type scan() - { - // initially, skip the BOM - if (position.chars_read_total == 0 && !skip_bom()) - { - error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; - return token_type::parse_error; - } - - // read next character and ignore whitespace - skip_whitespace(); - - // ignore comments - while (ignore_comments && current == '/') - { - if (!scan_comment()) - { - return token_type::parse_error; - } - - // skip following whitespace - skip_whitespace(); - } - - switch (current) - { - // structural characters - case '[': - return token_type::begin_array; - case ']': - return token_type::end_array; - case '{': - return token_type::begin_object; - case '}': - return token_type::end_object; - case ':': - return token_type::name_separator; - case ',': - return token_type::value_separator; - - // literals - case 't': - { - std::array true_literal = {{static_cast('t'), static_cast('r'), static_cast('u'), static_cast('e')}}; - return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); - } - case 'f': - { - std::array false_literal = {{static_cast('f'), static_cast('a'), static_cast('l'), static_cast('s'), static_cast('e')}}; - return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); - } - case 'n': - { - std::array null_literal = {{static_cast('n'), static_cast('u'), static_cast('l'), static_cast('l')}}; - return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); - } - - // string - case '\"': - return scan_string(); - - // number - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return scan_number(); - - // end of input (the null byte is needed when parsing from - // string literals) - case '\0': - case std::char_traits::eof(): - return token_type::end_of_input; - - // error - default: - error_message = "invalid literal"; - return token_type::parse_error; - } - } - - private: - /// input adapter - InputAdapterType ia; - - /// whether comments should be ignored (true) or signaled as errors (false) - const bool ignore_comments = false; - - /// the current character - char_int_type current = std::char_traits::eof(); - - /// whether the next get() call should just return current - bool next_unget = false; - - /// the start position of the current token - position_t position {}; - - /// raw input token string (for error messages) - std::vector token_string {}; - - /// buffer for variable-length tokens (numbers, strings) - string_t token_buffer {}; - - /// a description of occurred lexer errors - const char* error_message = ""; - - // number values - number_integer_t value_integer = 0; - number_unsigned_t value_unsigned = 0; - number_float_t value_float = 0; - - /// the decimal point - const char_int_type decimal_point_char = '.'; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/input/parser.hpp b/r5dev/thirdparty/nlohmann/detail/input/parser.hpp deleted file mode 100644 index 8acbd4fc..00000000 --- a/r5dev/thirdparty/nlohmann/detail/input/parser.hpp +++ /dev/null @@ -1,507 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // isfinite -#include // uint8_t -#include // function -#include // string -#include // move -#include // vector - -#include -#include -#include -#include -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ -//////////// -// parser // -//////////// - -enum class parse_event_t : std::uint8_t -{ - /// the parser read `{` and started to process a JSON object - object_start, - /// the parser read `}` and finished processing a JSON object - object_end, - /// the parser read `[` and started to process a JSON array - array_start, - /// the parser read `]` and finished processing a JSON array - array_end, - /// the parser read a key of a value in an object - key, - /// the parser finished reading a JSON value - value -}; - -template -using parser_callback_t = - std::function; - -/*! -@brief syntax analysis - -This class implements a recursive descent parser. -*/ -template -class parser -{ - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using lexer_t = lexer; - using token_type = typename lexer_t::token_type; - - public: - /// a parser reading from an input adapter - explicit parser(InputAdapterType&& adapter, - const parser_callback_t cb = nullptr, - const bool allow_exceptions_ = true, - const bool skip_comments = false) - : callback(cb) - , m_lexer(std::move(adapter), skip_comments) - , allow_exceptions(allow_exceptions_) - { - // read first token - get_token(); - } - - /*! - @brief public parser interface - - @param[in] strict whether to expect the last token to be EOF - @param[in,out] result parsed JSON value - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails - */ - void parse(const bool strict, BasicJsonType& result) - { - if (callback) - { - json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); - sax_parse_internal(&sdp); - - // in strict mode, input must be completely read - if (strict && (get_token() != token_type::end_of_input)) - { - sdp.parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), - exception_message(token_type::end_of_input, "value"), nullptr)); - } - - // in case of an error, return discarded value - if (sdp.is_errored()) - { - result = value_t::discarded; - return; - } - - // set top-level value to null if it was discarded by the callback - // function - if (result.is_discarded()) - { - result = nullptr; - } - } - else - { - json_sax_dom_parser sdp(result, allow_exceptions); - sax_parse_internal(&sdp); - - // in strict mode, input must be completely read - if (strict && (get_token() != token_type::end_of_input)) - { - sdp.parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); - } - - // in case of an error, return discarded value - if (sdp.is_errored()) - { - result = value_t::discarded; - return; - } - } - - result.assert_invariant(); - } - - /*! - @brief public accept interface - - @param[in] strict whether to expect the last token to be EOF - @return whether the input is a proper JSON text - */ - bool accept(const bool strict = true) - { - json_sax_acceptor sax_acceptor; - return sax_parse(&sax_acceptor, strict); - } - - template - JSON_HEDLEY_NON_NULL(2) - bool sax_parse(SAX* sax, const bool strict = true) - { - (void)detail::is_sax_static_asserts {}; - const bool result = sax_parse_internal(sax); - - // strict mode: next byte must be EOF - if (result && strict && (get_token() != token_type::end_of_input)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr)); - } - - return result; - } - - private: - template - JSON_HEDLEY_NON_NULL(2) - bool sax_parse_internal(SAX* sax) - { - // stack to remember the hierarchy of structured values we are parsing - // true = array; false = object - std::vector states; - // value to avoid a goto (see comment where set to true) - bool skip_to_state_evaluation = false; - - while (true) - { - if (!skip_to_state_evaluation) - { - // invariant: get_token() was called before each iteration - switch (last_token) - { - case token_type::begin_object: - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) - { - return false; - } - - // closing } -> we are done - if (get_token() == token_type::end_object) - { - if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) - { - return false; - } - break; - } - - // parse key - if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); - } - if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) - { - return false; - } - - // parse separator (:) - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); - } - - // remember we are now inside an object - states.push_back(false); - - // parse values - get_token(); - continue; - } - - case token_type::begin_array: - { - if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) - { - return false; - } - - // closing ] -> we are done - if (get_token() == token_type::end_array) - { - if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) - { - return false; - } - break; - } - - // remember we are now inside an array - states.push_back(true); - - // parse values (no need to call get_token) - continue; - } - - case token_type::value_float: - { - const auto res = m_lexer.get_number_float(); - - if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr)); - } - - if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) - { - return false; - } - - break; - } - - case token_type::literal_false: - { - if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) - { - return false; - } - break; - } - - case token_type::literal_null: - { - if (JSON_HEDLEY_UNLIKELY(!sax->null())) - { - return false; - } - break; - } - - case token_type::literal_true: - { - if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) - { - return false; - } - break; - } - - case token_type::value_integer: - { - if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) - { - return false; - } - break; - } - - case token_type::value_string: - { - if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) - { - return false; - } - break; - } - - case token_type::value_unsigned: - { - if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) - { - return false; - } - break; - } - - case token_type::parse_error: - { - // using "uninitialized" to avoid "expected" message - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr)); - } - - case token_type::uninitialized: - case token_type::end_array: - case token_type::end_object: - case token_type::name_separator: - case token_type::value_separator: - case token_type::end_of_input: - case token_type::literal_or_value: - default: // the last token was unexpected - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr)); - } - } - } - else - { - skip_to_state_evaluation = false; - } - - // we reached this line after we successfully parsed a value - if (states.empty()) - { - // empty stack: we reached the end of the hierarchy: done - return true; - } - - if (states.back()) // array - { - // comma -> next value - if (get_token() == token_type::value_separator) - { - // parse a new value - get_token(); - continue; - } - - // closing ] - if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) - { - if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) - { - return false; - } - - // We are done with this array. Before we can parse a - // new value, we need to evaluate the new state first. - // By setting skip_to_state_evaluation to false, we - // are effectively jumping to the beginning of this if. - JSON_ASSERT(!states.empty()); - states.pop_back(); - skip_to_state_evaluation = true; - continue; - } - - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr)); - } - - // states.back() is false -> object - - // comma -> next value - if (get_token() == token_type::value_separator) - { - // parse key - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr)); - } - - if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) - { - return false; - } - - // parse separator (:) - if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) - { - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr)); - } - - // parse values - get_token(); - continue; - } - - // closing } - if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) - { - if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) - { - return false; - } - - // We are done with this object. Before we can parse a - // new value, we need to evaluate the new state first. - // By setting skip_to_state_evaluation to false, we - // are effectively jumping to the beginning of this if. - JSON_ASSERT(!states.empty()); - states.pop_back(); - skip_to_state_evaluation = true; - continue; - } - - return sax->parse_error(m_lexer.get_position(), - m_lexer.get_token_string(), - parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr)); - } - } - - /// get next token from lexer - token_type get_token() - { - return last_token = m_lexer.scan(); - } - - std::string exception_message(const token_type expected, const std::string& context) - { - std::string error_msg = "syntax error "; - - if (!context.empty()) - { - error_msg += concat("while parsing ", context, ' '); - } - - error_msg += "- "; - - if (last_token == token_type::parse_error) - { - error_msg += concat(m_lexer.get_error_message(), "; last read: '", - m_lexer.get_token_string(), '\''); - } - else - { - error_msg += concat("unexpected ", lexer_t::token_type_name(last_token)); - } - - if (expected != token_type::uninitialized) - { - error_msg += concat("; expected ", lexer_t::token_type_name(expected)); - } - - return error_msg; - } - - private: - /// callback function - const parser_callback_t callback = nullptr; - /// the type of the last read token - token_type last_token = token_type::uninitialized; - /// the lexer - lexer_t m_lexer; - /// whether to throw exceptions in case of errors - const bool allow_exceptions = true; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/input/position_t.hpp b/r5dev/thirdparty/nlohmann/detail/input/position_t.hpp deleted file mode 100644 index 396db0e1..00000000 --- a/r5dev/thirdparty/nlohmann/detail/input/position_t.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // size_t - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/// struct to capture the start position of the current token -struct position_t -{ - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; - - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const - { - return chars_read_total; - } -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp b/r5dev/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp deleted file mode 100644 index 13a212c8..00000000 --- a/r5dev/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/*! -@brief an iterator value - -@note This structure could easily be a union, but MSVC currently does not allow -unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. -*/ -template struct internal_iterator -{ - /// iterator for JSON objects - typename BasicJsonType::object_t::iterator object_iterator {}; - /// iterator for JSON arrays - typename BasicJsonType::array_t::iterator array_iterator {}; - /// generic iterator for all other types - primitive_iterator_t primitive_iterator {}; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/iterators/iter_impl.hpp b/r5dev/thirdparty/nlohmann/detail/iterators/iter_impl.hpp deleted file mode 100644 index 3f5a9901..00000000 --- a/r5dev/thirdparty/nlohmann/detail/iterators/iter_impl.hpp +++ /dev/null @@ -1,751 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next -#include // conditional, is_const, remove_const - -#include -#include -#include -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -// forward declare, to be able to friend it later on -template class iteration_proxy; -template class iteration_proxy_value; - -/*! -@brief a template for a bidirectional iterator for the @ref basic_json class -This class implements a both iterators (iterator and const_iterator) for the -@ref basic_json class. -@note An iterator is called *initialized* when a pointer to a JSON value has - been set (e.g., by a constructor or a copy assignment). If the iterator is - default-constructed, it is *uninitialized* and most methods are undefined. - **The library uses assertions to detect calls on uninitialized iterators.** -@requirement The class satisfies the following concept requirements: -- -[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): - The iterator that can be moved can be moved in both directions (i.e. - incremented and decremented). -@since version 1.0.0, simplified in version 2.0.9, change to bidirectional - iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) -*/ -template -class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) -{ - /// the iterator with BasicJsonType of different const-ness - using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; - /// allow basic_json to access private members - friend other_iter_impl; - friend BasicJsonType; - friend iteration_proxy; - friend iteration_proxy_value; - - using object_t = typename BasicJsonType::object_t; - using array_t = typename BasicJsonType::array_t; - // make sure BasicJsonType is basic_json or const basic_json - static_assert(is_basic_json::type>::value, - "iter_impl only accepts (const) basic_json"); - // superficial check for the LegacyBidirectionalIterator named requirement - static_assert(std::is_base_of::value - && std::is_base_of::iterator_category>::value, - "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement."); - - public: - /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. - /// The C++ Standard has never required user-defined iterators to derive from std::iterator. - /// A user-defined iterator should provide publicly accessible typedefs named - /// iterator_category, value_type, difference_type, pointer, and reference. - /// Note that value_type is required to be non-const, even for constant iterators. - using iterator_category = std::bidirectional_iterator_tag; - - /// the type of the values when the iterator is dereferenced - using value_type = typename BasicJsonType::value_type; - /// a type to represent differences between iterators - using difference_type = typename BasicJsonType::difference_type; - /// defines a pointer to the type iterated over (value_type) - using pointer = typename std::conditional::value, - typename BasicJsonType::const_pointer, - typename BasicJsonType::pointer>::type; - /// defines a reference to the type iterated over (value_type) - using reference = - typename std::conditional::value, - typename BasicJsonType::const_reference, - typename BasicJsonType::reference>::type; - - iter_impl() = default; - ~iter_impl() = default; - iter_impl(iter_impl&&) noexcept = default; - iter_impl& operator=(iter_impl&&) noexcept = default; - - /*! - @brief constructor for a given JSON instance - @param[in] object pointer to a JSON object for this iterator - @pre object != nullptr - @post The iterator is initialized; i.e. `m_object != nullptr`. - */ - explicit iter_impl(pointer object) noexcept : m_object(object) - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - m_it.object_iterator = typename object_t::iterator(); - break; - } - - case value_t::array: - { - m_it.array_iterator = typename array_t::iterator(); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator = primitive_iterator_t(); - break; - } - } - } - - /*! - @note The conventional copy constructor and copy assignment are implicitly - defined. Combined with the following converting constructor and - assignment, they support: (1) copy from iterator to iterator, (2) - copy from const iterator to const iterator, and (3) conversion from - iterator to const iterator. However conversion from const iterator - to iterator is not defined. - */ - - /*! - @brief const copy constructor - @param[in] other const iterator to copy from - @note This copy constructor had to be defined explicitly to circumvent a bug - occurring on msvc v19.0 compiler (VS 2015) debug build. For more - information refer to: https://github.com/nlohmann/json/issues/1608 - */ - iter_impl(const iter_impl& other) noexcept - : m_object(other.m_object), m_it(other.m_it) - {} - - /*! - @brief converting assignment - @param[in] other const iterator to copy from - @return const/non-const iterator - @note It is not checked whether @a other is initialized. - */ - iter_impl& operator=(const iter_impl& other) noexcept - { - if (&other != this) - { - m_object = other.m_object; - m_it = other.m_it; - } - return *this; - } - - /*! - @brief converting constructor - @param[in] other non-const iterator to copy from - @note It is not checked whether @a other is initialized. - */ - iter_impl(const iter_impl::type>& other) noexcept - : m_object(other.m_object), m_it(other.m_it) - {} - - /*! - @brief converting assignment - @param[in] other non-const iterator to copy from - @return const/non-const iterator - @note It is not checked whether @a other is initialized. - */ - iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) - { - m_object = other.m_object; - m_it = other.m_it; - return *this; - } - - JSON_PRIVATE_UNLESS_TESTED: - /*! - @brief set the iterator to the first value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_begin() noexcept - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - m_it.object_iterator = m_object->m_value.object->begin(); - break; - } - - case value_t::array: - { - m_it.array_iterator = m_object->m_value.array->begin(); - break; - } - - case value_t::null: - { - // set to end so begin()==end() is true: null is empty - m_it.primitive_iterator.set_end(); - break; - } - - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator.set_begin(); - break; - } - } - } - - /*! - @brief set the iterator past the last value - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - void set_end() noexcept - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - m_it.object_iterator = m_object->m_value.object->end(); - break; - } - - case value_t::array: - { - m_it.array_iterator = m_object->m_value.array->end(); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator.set_end(); - break; - } - } - } - - public: - /*! - @brief return a reference to the value pointed to by the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator*() const - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); - return m_it.object_iterator->second; - } - - case value_t::array: - { - JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); - return *m_it.array_iterator; - } - - case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); - - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) - { - return *m_object; - } - - JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); - } - } - } - - /*! - @brief dereference the iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - pointer operator->() const - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); - return &(m_it.object_iterator->second); - } - - case value_t::array: - { - JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); - return &*m_it.array_iterator; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) - { - return m_object; - } - - JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); - } - } - } - - /*! - @brief post-increment (it++) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp) - { - auto result = *this; - ++(*this); - return result; - } - - /*! - @brief pre-increment (++it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator++() - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - std::advance(m_it.object_iterator, 1); - break; - } - - case value_t::array: - { - std::advance(m_it.array_iterator, 1); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - ++m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /*! - @brief post-decrement (it--) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp) - { - auto result = *this; - --(*this); - return result; - } - - /*! - @brief pre-decrement (--it) - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator--() - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - { - std::advance(m_it.object_iterator, -1); - break; - } - - case value_t::array: - { - std::advance(m_it.array_iterator, -1); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - --m_it.primitive_iterator; - break; - } - } - - return *this; - } - - /*! - @brief comparison: equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > - bool operator==(const IterImpl& other) const - { - // if objects are not the same, the comparison is undefined - if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); - } - - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - return (m_it.object_iterator == other.m_it.object_iterator); - - case value_t::array: - return (m_it.array_iterator == other.m_it.array_iterator); - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return (m_it.primitive_iterator == other.m_it.primitive_iterator); - } - } - - /*! - @brief comparison: not equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > - bool operator!=(const IterImpl& other) const - { - return !operator==(other); - } - - /*! - @brief comparison: smaller - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<(const iter_impl& other) const - { - // if objects are not the same, the comparison is undefined - if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) - { - JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object)); - } - - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object)); - - case value_t::array: - return (m_it.array_iterator < other.m_it.array_iterator); - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return (m_it.primitive_iterator < other.m_it.primitive_iterator); - } - } - - /*! - @brief comparison: less than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator<=(const iter_impl& other) const - { - return !other.operator < (*this); - } - - /*! - @brief comparison: greater than - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>(const iter_impl& other) const - { - return !operator<=(other); - } - - /*! - @brief comparison: greater than or equal - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - bool operator>=(const iter_impl& other) const - { - return !operator<(other); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator+=(difference_type i) - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); - - case value_t::array: - { - std::advance(m_it.array_iterator, i); - break; - } - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - m_it.primitive_iterator += i; - break; - } - } - - return *this; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl& operator-=(difference_type i) - { - return operator+=(-i); - } - - /*! - @brief add to iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator+(difference_type i) const - { - auto result = *this; - result += i; - return result; - } - - /*! - @brief addition of distance and iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - friend iter_impl operator+(difference_type i, const iter_impl& it) - { - auto result = it; - result += i; - return result; - } - - /*! - @brief subtract from iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - iter_impl operator-(difference_type i) const - { - auto result = *this; - result -= i; - return result; - } - - /*! - @brief return difference - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - difference_type operator-(const iter_impl& other) const - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object)); - - case value_t::array: - return m_it.array_iterator - other.m_it.array_iterator; - - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return m_it.primitive_iterator - other.m_it.primitive_iterator; - } - } - - /*! - @brief access to successor - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference operator[](difference_type n) const - { - JSON_ASSERT(m_object != nullptr); - - switch (m_object->m_type) - { - case value_t::object: - JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object)); - - case value_t::array: - return *std::next(m_it.array_iterator, n); - - case value_t::null: - JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); - - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - { - if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) - { - return *m_object; - } - - JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object)); - } - } - } - - /*! - @brief return the key of an object iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - const typename object_t::key_type& key() const - { - JSON_ASSERT(m_object != nullptr); - - if (JSON_HEDLEY_LIKELY(m_object->is_object())) - { - return m_it.object_iterator->first; - } - - JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object)); - } - - /*! - @brief return the value of an iterator - @pre The iterator is initialized; i.e. `m_object != nullptr`. - */ - reference value() const - { - return operator*(); - } - - JSON_PRIVATE_UNLESS_TESTED: - /// associated JSON instance - pointer m_object = nullptr; - /// the actual iterator of the associated instance - internal_iterator::type> m_it {}; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp b/r5dev/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp deleted file mode 100644 index 659cd06f..00000000 --- a/r5dev/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp +++ /dev/null @@ -1,242 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // size_t -#include // input_iterator_tag -#include // string, to_string -#include // tuple_size, get, tuple_element -#include // move - -#if JSON_HAS_RANGES - #include // enable_borrowed_range -#endif - -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -void int_to_string( string_type& target, std::size_t value ) -{ - // For ADL - using std::to_string; - target = to_string(value); -} -template class iteration_proxy_value -{ - public: - using difference_type = std::ptrdiff_t; - using value_type = iteration_proxy_value; - using pointer = value_type *; - using reference = value_type &; - using iterator_category = std::input_iterator_tag; - using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; - - private: - /// the iterator - IteratorType anchor{}; - /// an index for arrays (used to create key names) - std::size_t array_index = 0; - /// last stringified array index - mutable std::size_t array_index_last = 0; - /// a string representation of the array index - mutable string_type array_index_str = "0"; - /// an empty string (to return a reference for primitive values) - string_type empty_str{}; - - public: - explicit iteration_proxy_value() = default; - explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0) - noexcept(std::is_nothrow_move_constructible::value - && std::is_nothrow_default_constructible::value) - : anchor(std::move(it)) - , array_index(array_index_) - {} - - iteration_proxy_value(iteration_proxy_value const&) = default; - iteration_proxy_value& operator=(iteration_proxy_value const&) = default; - // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions - iteration_proxy_value(iteration_proxy_value&&) - noexcept(std::is_nothrow_move_constructible::value - && std::is_nothrow_move_constructible::value) = default; - iteration_proxy_value& operator=(iteration_proxy_value&&) - noexcept(std::is_nothrow_move_assignable::value - && std::is_nothrow_move_assignable::value) = default; - ~iteration_proxy_value() = default; - - /// dereference operator (needed for range-based for) - const iteration_proxy_value& operator*() const - { - return *this; - } - - /// increment operator (needed for range-based for) - iteration_proxy_value& operator++() - { - ++anchor; - ++array_index; - - return *this; - } - - iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp) - { - auto tmp = iteration_proxy_value(anchor, array_index); - ++anchor; - ++array_index; - return tmp; - } - - /// equality operator (needed for InputIterator) - bool operator==(const iteration_proxy_value& o) const - { - return anchor == o.anchor; - } - - /// inequality operator (needed for range-based for) - bool operator!=(const iteration_proxy_value& o) const - { - return anchor != o.anchor; - } - - /// return key of the iterator - const string_type& key() const - { - JSON_ASSERT(anchor.m_object != nullptr); - - switch (anchor.m_object->type()) - { - // use integer array index as key - case value_t::array: - { - if (array_index != array_index_last) - { - int_to_string( array_index_str, array_index ); - array_index_last = array_index; - } - return array_index_str; - } - - // use key from the object - case value_t::object: - return anchor.key(); - - // use an empty key for all primitive types - case value_t::null: - case value_t::string: - case value_t::boolean: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::number_float: - case value_t::binary: - case value_t::discarded: - default: - return empty_str; - } - } - - /// return value of the iterator - typename IteratorType::reference value() const - { - return anchor.value(); - } -}; - -/// proxy class for the items() function -template class iteration_proxy -{ - private: - /// the container to iterate - typename IteratorType::pointer container = nullptr; - - public: - explicit iteration_proxy() = default; - - /// construct iteration proxy from a container - explicit iteration_proxy(typename IteratorType::reference cont) noexcept - : container(&cont) {} - - iteration_proxy(iteration_proxy const&) = default; - iteration_proxy& operator=(iteration_proxy const&) = default; - iteration_proxy(iteration_proxy&&) noexcept = default; - iteration_proxy& operator=(iteration_proxy&&) noexcept = default; - ~iteration_proxy() = default; - - /// return iterator begin (needed for range-based for) - iteration_proxy_value begin() const noexcept - { - return iteration_proxy_value(container->begin()); - } - - /// return iterator end (needed for range-based for) - iteration_proxy_value end() const noexcept - { - return iteration_proxy_value(container->end()); - } -}; - -// Structured Bindings Support -// For further reference see https://blog.tartanllama.xyz/structured-bindings/ -// And see https://github.com/nlohmann/json/pull/1391 -template = 0> -auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) -{ - return i.key(); -} -// Structured Bindings Support -// For further reference see https://blog.tartanllama.xyz/structured-bindings/ -// And see https://github.com/nlohmann/json/pull/1391 -template = 0> -auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) -{ - return i.value(); -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// The Addition to the STD Namespace is required to add -// Structured Bindings Support to the iteration_proxy_value class -// For further reference see https://blog.tartanllama.xyz/structured-bindings/ -// And see https://github.com/nlohmann/json/pull/1391 -namespace std -{ - -#if defined(__clang__) - // Fix: https://github.com/nlohmann/json/issues/1401 - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wmismatched-tags" -#endif -template -class tuple_size<::nlohmann::detail::iteration_proxy_value> - : public std::integral_constant {}; - -template -class tuple_element> -{ - public: - using type = decltype( - get(std::declval < - ::nlohmann::detail::iteration_proxy_value> ())); -}; -#if defined(__clang__) - #pragma clang diagnostic pop -#endif - -} // namespace std - -#if JSON_HAS_RANGES - template - inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy> = true; -#endif diff --git a/r5dev/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp b/r5dev/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp deleted file mode 100644 index 34a20eee..00000000 --- a/r5dev/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // random_access_iterator_tag - -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -struct iterator_types {}; - -template -struct iterator_types < - It, - void_t> -{ - using difference_type = typename It::difference_type; - using value_type = typename It::value_type; - using pointer = typename It::pointer; - using reference = typename It::reference; - using iterator_category = typename It::iterator_category; -}; - -// This is required as some compilers implement std::iterator_traits in a way that -// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. -template -struct iterator_traits -{ -}; - -template -struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types -{ -}; - -template -struct iterator_traits::value>> -{ - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp b/r5dev/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp deleted file mode 100644 index eb450e98..00000000 --- a/r5dev/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp +++ /dev/null @@ -1,130 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // ptrdiff_t -#include // reverse_iterator -#include // declval - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -////////////////////// -// reverse_iterator // -////////////////////// - -/*! -@brief a template for a reverse iterator class - -@tparam Base the base iterator type to reverse. Valid types are @ref -iterator (to create @ref reverse_iterator) and @ref const_iterator (to -create @ref const_reverse_iterator). - -@requirement The class satisfies the following concept requirements: -- -[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): - The iterator that can be moved can be moved in both directions (i.e. - incremented and decremented). -- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): - It is possible to write to the pointed-to element (only if @a Base is - @ref iterator). - -@since version 1.0.0 -*/ -template -class json_reverse_iterator : public std::reverse_iterator -{ - public: - using difference_type = std::ptrdiff_t; - /// shortcut to the reverse iterator adapter - using base_iterator = std::reverse_iterator; - /// the reference type for the pointed-to element - using reference = typename Base::reference; - - /// create reverse iterator from iterator - explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept - : base_iterator(it) {} - - /// create reverse iterator from base class - explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} - - /// post-increment (it++) - json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp) - { - return static_cast(base_iterator::operator++(1)); - } - - /// pre-increment (++it) - json_reverse_iterator& operator++() - { - return static_cast(base_iterator::operator++()); - } - - /// post-decrement (it--) - json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp) - { - return static_cast(base_iterator::operator--(1)); - } - - /// pre-decrement (--it) - json_reverse_iterator& operator--() - { - return static_cast(base_iterator::operator--()); - } - - /// add to iterator - json_reverse_iterator& operator+=(difference_type i) - { - return static_cast(base_iterator::operator+=(i)); - } - - /// add to iterator - json_reverse_iterator operator+(difference_type i) const - { - return static_cast(base_iterator::operator+(i)); - } - - /// subtract from iterator - json_reverse_iterator operator-(difference_type i) const - { - return static_cast(base_iterator::operator-(i)); - } - - /// return difference - difference_type operator-(const json_reverse_iterator& other) const - { - return base_iterator(*this) - base_iterator(other); - } - - /// access to successor - reference operator[](difference_type n) const - { - return *(this->operator+(n)); - } - - /// return the key of an object iterator - auto key() const -> decltype(std::declval().key()) - { - auto it = --this->base(); - return it.key(); - } - - /// return the value of an iterator - reference value() const - { - auto it = --this->base(); - return it.operator * (); - } -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp b/r5dev/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp deleted file mode 100644 index 0bc3ca80..00000000 --- a/r5dev/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp +++ /dev/null @@ -1,132 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // ptrdiff_t -#include // numeric_limits - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/* -@brief an iterator for primitive JSON types - -This class models an iterator for primitive JSON types (boolean, number, -string). It's only purpose is to allow the iterator/const_iterator classes -to "iterate" over primitive values. Internally, the iterator is modeled by -a `difference_type` variable. Value begin_value (`0`) models the begin, -end_value (`1`) models past the end. -*/ -class primitive_iterator_t -{ - private: - using difference_type = std::ptrdiff_t; - static constexpr difference_type begin_value = 0; - static constexpr difference_type end_value = begin_value + 1; - - JSON_PRIVATE_UNLESS_TESTED: - /// iterator as signed integer type - difference_type m_it = (std::numeric_limits::min)(); - - public: - constexpr difference_type get_value() const noexcept - { - return m_it; - } - - /// set iterator to a defined beginning - void set_begin() noexcept - { - m_it = begin_value; - } - - /// set iterator to a defined past the end - void set_end() noexcept - { - m_it = end_value; - } - - /// return whether the iterator can be dereferenced - constexpr bool is_begin() const noexcept - { - return m_it == begin_value; - } - - /// return whether the iterator is at end - constexpr bool is_end() const noexcept - { - return m_it == end_value; - } - - friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it == rhs.m_it; - } - - friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it < rhs.m_it; - } - - primitive_iterator_t operator+(difference_type n) noexcept - { - auto result = *this; - result += n; - return result; - } - - friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept - { - return lhs.m_it - rhs.m_it; - } - - primitive_iterator_t& operator++() noexcept - { - ++m_it; - return *this; - } - - primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp) - { - auto result = *this; - ++m_it; - return result; - } - - primitive_iterator_t& operator--() noexcept - { - --m_it; - return *this; - } - - primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp) - { - auto result = *this; - --m_it; - return result; - } - - primitive_iterator_t& operator+=(difference_type n) noexcept - { - m_it += n; - return *this; - } - - primitive_iterator_t& operator-=(difference_type n) noexcept - { - m_it -= n; - return *this; - } -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/json_pointer.hpp b/r5dev/thirdparty/nlohmann/detail/json_pointer.hpp deleted file mode 100644 index 3f69bcdf..00000000 --- a/r5dev/thirdparty/nlohmann/detail/json_pointer.hpp +++ /dev/null @@ -1,988 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // all_of -#include // isdigit -#include // errno, ERANGE -#include // strtoull -#ifndef JSON_NO_IO - #include // ostream -#endif // JSON_NO_IO -#include // max -#include // accumulate -#include // string -#include // move -#include // vector - -#include -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN - -/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document -/// @sa https://json.nlohmann.me/api/json_pointer/ -template -class json_pointer -{ - // allow basic_json to access private members - NLOHMANN_BASIC_JSON_TPL_DECLARATION - friend class basic_json; - - template - friend class json_pointer; - - template - struct string_t_helper - { - using type = T; - }; - - NLOHMANN_BASIC_JSON_TPL_DECLARATION - struct string_t_helper - { - using type = StringType; - }; - - public: - // for backwards compatibility accept BasicJsonType - using string_t = typename string_t_helper::type; - - /// @brief create JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ - explicit json_pointer(const string_t& s = "") - : reference_tokens(split(s)) - {} - - /// @brief return a string representation of the JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ - string_t to_string() const - { - return std::accumulate(reference_tokens.begin(), reference_tokens.end(), - string_t{}, - [](const string_t& a, const string_t& b) - { - return detail::concat(a, '/', detail::escape(b)); - }); - } - - /// @brief return a string representation of the JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ - JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string()) - operator string_t() const - { - return to_string(); - } - -#ifndef JSON_NO_IO - /// @brief write string representation of the JSON pointer to stream - /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ - friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr) - { - o << ptr.to_string(); - return o; - } -#endif - - /// @brief append another JSON pointer at the end of this JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ - json_pointer& operator/=(const json_pointer& ptr) - { - reference_tokens.insert(reference_tokens.end(), - ptr.reference_tokens.begin(), - ptr.reference_tokens.end()); - return *this; - } - - /// @brief append an unescaped reference token at the end of this JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ - json_pointer& operator/=(string_t token) - { - push_back(std::move(token)); - return *this; - } - - /// @brief append an array index at the end of this JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ - json_pointer& operator/=(std::size_t array_idx) - { - return *this /= std::to_string(array_idx); - } - - /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ - friend json_pointer operator/(const json_pointer& lhs, - const json_pointer& rhs) - { - return json_pointer(lhs) /= rhs; - } - - /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ - friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param) - { - return json_pointer(lhs) /= std::move(token); - } - - /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ - friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) - { - return json_pointer(lhs) /= array_idx; - } - - /// @brief returns the parent of this JSON pointer - /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ - json_pointer parent_pointer() const - { - if (empty()) - { - return *this; - } - - json_pointer res = *this; - res.pop_back(); - return res; - } - - /// @brief remove last reference token - /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ - void pop_back() - { - if (JSON_HEDLEY_UNLIKELY(empty())) - { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); - } - - reference_tokens.pop_back(); - } - - /// @brief return last reference token - /// @sa https://json.nlohmann.me/api/json_pointer/back/ - const string_t& back() const - { - if (JSON_HEDLEY_UNLIKELY(empty())) - { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); - } - - return reference_tokens.back(); - } - - /// @brief append an unescaped token at the end of the reference pointer - /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ - void push_back(const string_t& token) - { - reference_tokens.push_back(token); - } - - /// @brief append an unescaped token at the end of the reference pointer - /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ - void push_back(string_t&& token) - { - reference_tokens.push_back(std::move(token)); - } - - /// @brief return whether pointer points to the root document - /// @sa https://json.nlohmann.me/api/json_pointer/empty/ - bool empty() const noexcept - { - return reference_tokens.empty(); - } - - private: - /*! - @param[in] s reference token to be converted into an array index - - @return integer representation of @a s - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index begins not with a digit - @throw out_of_range.404 if string @a s could not be converted to an integer - @throw out_of_range.410 if an array index exceeds size_type - */ - template - static typename BasicJsonType::size_type array_index(const string_t& s) - { - using size_type = typename BasicJsonType::size_type; - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) - { - JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); - } - - // error condition (cf. RFC 6901, Sect. 4) - if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) - { - JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); - } - - const char* p = s.c_str(); - char* p_end = nullptr; - errno = 0; // strtoull doesn't reset errno - unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int) - if (p == p_end // invalid input or empty string - || errno == ERANGE // out of range - || JSON_HEDLEY_UNLIKELY(static_cast(p_end - p) != s.size())) // incomplete read - { - JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr)); - } - - // only triggered on special platforms (like 32bit), see also - // https://github.com/nlohmann/json/pull/2203 - if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) - { - JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE - } - - return static_cast(res); - } - - JSON_PRIVATE_UNLESS_TESTED: - json_pointer top() const - { - if (JSON_HEDLEY_UNLIKELY(empty())) - { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr)); - } - - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; - } - - private: - /*! - @brief create and return a reference to the pointed to value - - @complexity Linear in the number of reference tokens. - - @throw parse_error.109 if array index is not a number - @throw type_error.313 if value cannot be unflattened - */ - template - BasicJsonType& get_and_create(BasicJsonType& j) const - { - auto* result = &j; - - // in case no reference tokens exist, return a reference to the JSON value - // j which will be overwritten by a primitive value - for (const auto& reference_token : reference_tokens) - { - switch (result->type()) - { - case detail::value_t::null: - { - if (reference_token == "0") - { - // start a new array if reference token is 0 - result = &result->operator[](0); - } - else - { - // start a new object otherwise - result = &result->operator[](reference_token); - } - break; - } - - case detail::value_t::object: - { - // create an entry in the object - result = &result->operator[](reference_token); - break; - } - - case detail::value_t::array: - { - // create an entry in the array - result = &result->operator[](array_index(reference_token)); - break; - } - - /* - The following code is only reached if there exists a reference - token _and_ the current value is primitive. In this case, we have - an error situation, because primitive values may only occur as - single value; that is, with an empty list of reference tokens. - */ - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j)); - } - } - - return *result; - } - - /*! - @brief return a reference to the pointed to value - - @note This version does not throw if a value is not present, but tries to - create nested values instead. For instance, calling this function - with pointer `"/this/that"` on a null value is equivalent to calling - `operator[]("this").operator[]("that")` on that value, effectively - changing the null value to an object. - - @param[in] ptr a JSON value - - @return reference to the JSON value pointed to by the JSON pointer - - @complexity Linear in the length of the JSON pointer. - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - template - BasicJsonType& get_unchecked(BasicJsonType* ptr) const - { - for (const auto& reference_token : reference_tokens) - { - // convert null values to arrays or objects before continuing - if (ptr->is_null()) - { - // check if reference token is a number - const bool nums = - std::all_of(reference_token.begin(), reference_token.end(), - [](const unsigned char x) - { - return std::isdigit(x); - }); - - // change value to array for numbers or "-" or to object otherwise - *ptr = (nums || reference_token == "-") - ? detail::value_t::array - : detail::value_t::object; - } - - switch (ptr->type()) - { - case detail::value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case detail::value_t::array: - { - if (reference_token == "-") - { - // explicitly treat "-" as index beyond the end - ptr = &ptr->operator[](ptr->m_value.array->size()); - } - else - { - // convert array index to number; unchecked access - ptr = &ptr->operator[](array_index(reference_token)); - } - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); - } - } - - return *ptr; - } - - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - template - BasicJsonType& get_checked(BasicJsonType* ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->type()) - { - case detail::value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) - { - // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, detail::concat( - "array index '-' (", std::to_string(ptr->m_value.array->size()), - ") is out of range"), ptr)); - } - - // note: at performs range check - ptr = &ptr->at(array_index(reference_token)); - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); - } - } - - return *ptr; - } - - /*! - @brief return a const reference to the pointed to value - - @param[in] ptr a JSON value - - @return const reference to the JSON value pointed to by the JSON - pointer - - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - template - const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->type()) - { - case detail::value_t::object: - { - // use unchecked object access - ptr = &ptr->operator[](reference_token); - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) - { - // "-" cannot be used for const access - JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_value.array->size()), ") is out of range"), ptr)); - } - - // use unchecked array access - ptr = &ptr->operator[](array_index(reference_token)); - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); - } - } - - return *ptr; - } - - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - template - const BasicJsonType& get_checked(const BasicJsonType* ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->type()) - { - case detail::value_t::object: - { - // note: at performs range check - ptr = &ptr->at(reference_token); - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) - { - // "-" always fails the range check - JSON_THROW(detail::out_of_range::create(402, detail::concat( - "array index '-' (", std::to_string(ptr->m_value.array->size()), - ") is out of range"), ptr)); - } - - // note: at performs range check - ptr = &ptr->at(array_index(reference_token)); - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr)); - } - } - - return *ptr; - } - - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - */ - template - bool contains(const BasicJsonType* ptr) const - { - for (const auto& reference_token : reference_tokens) - { - switch (ptr->type()) - { - case detail::value_t::object: - { - if (!ptr->contains(reference_token)) - { - // we did not find the key in the object - return false; - } - - ptr = &ptr->operator[](reference_token); - break; - } - - case detail::value_t::array: - { - if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) - { - // "-" always fails the range check - return false; - } - if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) - { - // invalid char - return false; - } - if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) - { - if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) - { - // first char should be between '1' and '9' - return false; - } - for (std::size_t i = 1; i < reference_token.size(); i++) - { - if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) - { - // other char should be between '0' and '9' - return false; - } - } - } - - const auto idx = array_index(reference_token); - if (idx >= ptr->size()) - { - // index out of range - return false; - } - - ptr = &ptr->operator[](idx); - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - { - // we do not expect primitive values if there is still a - // reference token to process - return false; - } - } - } - - // no reference token left means we found a primitive value - return true; - } - - /*! - @brief split the string input to reference tokens - - @note This function is only called by the json_pointer constructor. - All exceptions below are documented there. - - @throw parse_error.107 if the pointer is not empty or begins with '/' - @throw parse_error.108 if character '~' is not followed by '0' or '1' - */ - static std::vector split(const string_t& reference_string) - { - std::vector result; - - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { - return result; - } - - // check if nonempty reference string begins with slash - if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) - { - JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); - } - - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - std::size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == 0 (if slash == string_t::npos) - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == string_t::npos) - start = (slash == string_t::npos) ? 0 : slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); - - // check reference tokens are properly escaped - for (std::size_t pos = reference_token.find_first_of('~'); - pos != string_t::npos; - pos = reference_token.find_first_of('~', pos + 1)) - { - JSON_ASSERT(reference_token[pos] == '~'); - - // ~ must be followed by 0 or 1 - if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || - (reference_token[pos + 1] != '0' && - reference_token[pos + 1] != '1'))) - { - JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); - } - } - - // finally, store the reference token - detail::unescape(reference_token); - result.push_back(reference_token); - } - - return result; - } - - private: - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to - - @note Empty objects or arrays are flattened to `null`. - */ - template - static void flatten(const string_t& reference_string, - const BasicJsonType& value, - BasicJsonType& result) - { - switch (value.type()) - { - case detail::value_t::array: - { - if (value.m_value.array->empty()) - { - // flatten empty array as null - result[reference_string] = nullptr; - } - else - { - // iterate array and use index as reference string - for (std::size_t i = 0; i < value.m_value.array->size(); ++i) - { - flatten(detail::concat(reference_string, '/', std::to_string(i)), - value.m_value.array->operator[](i), result); - } - } - break; - } - - case detail::value_t::object: - { - if (value.m_value.object->empty()) - { - // flatten empty object as null - result[reference_string] = nullptr; - } - else - { - // iterate object and use keys as reference string - for (const auto& element : *value.m_value.object) - { - flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result); - } - } - break; - } - - case detail::value_t::null: - case detail::value_t::string: - case detail::value_t::boolean: - case detail::value_t::number_integer: - case detail::value_t::number_unsigned: - case detail::value_t::number_float: - case detail::value_t::binary: - case detail::value_t::discarded: - default: - { - // add primitive value with its reference string - result[reference_string] = value; - break; - } - } - } - - /*! - @param[in] value flattened JSON - - @return unflattened JSON - - @throw parse_error.109 if array index is not a number - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - @throw type_error.313 if value cannot be unflattened - */ - template - static BasicJsonType - unflatten(const BasicJsonType& value) - { - if (JSON_HEDLEY_UNLIKELY(!value.is_object())) - { - JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value)); - } - - BasicJsonType result; - - // iterate the JSON object values - for (const auto& element : *value.m_value.object) - { - if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) - { - JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second)); - } - - // assign value to reference pointed to by JSON pointer; Note that if - // the JSON pointer is "" (i.e., points to the whole value), function - // get_and_create returns a reference to result itself. An assignment - // will then create a primitive value. - json_pointer(element.first).get_and_create(result) = element.second; - } - - return result; - } - - // can't use conversion operator because of ambiguity - json_pointer convert() const& - { - json_pointer result; - result.reference_tokens = reference_tokens; - return result; - } - - json_pointer convert()&& - { - json_pointer result; - result.reference_tokens = std::move(reference_tokens); - return result; - } - - public: -#if JSON_HAS_THREE_WAY_COMPARISON - /// @brief compares two JSON pointers for equality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ - template - bool operator==(const json_pointer& rhs) const noexcept - { - return reference_tokens == rhs.reference_tokens; - } - - /// @brief compares JSON pointer and string for equality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ - JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer)) - bool operator==(const string_t& rhs) const - { - return *this == json_pointer(rhs); - } - - /// @brief 3-way compares two JSON pointers - template - std::strong_ordering operator<=>(const json_pointer& rhs) const noexcept // *NOPAD* - { - return reference_tokens <=> rhs.reference_tokens; // *NOPAD* - } -#else - /// @brief compares two JSON pointers for equality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ - template - // NOLINTNEXTLINE(readability-redundant-declaration) - friend bool operator==(const json_pointer& lhs, - const json_pointer& rhs) noexcept; - - /// @brief compares JSON pointer and string for equality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ - template - // NOLINTNEXTLINE(readability-redundant-declaration) - friend bool operator==(const json_pointer& lhs, - const StringType& rhs); - - /// @brief compares string and JSON pointer for equality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/ - template - // NOLINTNEXTLINE(readability-redundant-declaration) - friend bool operator==(const StringType& lhs, - const json_pointer& rhs); - - /// @brief compares two JSON pointers for inequality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ - template - // NOLINTNEXTLINE(readability-redundant-declaration) - friend bool operator!=(const json_pointer& lhs, - const json_pointer& rhs) noexcept; - - /// @brief compares JSON pointer and string for inequality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ - template - // NOLINTNEXTLINE(readability-redundant-declaration) - friend bool operator!=(const json_pointer& lhs, - const StringType& rhs); - - /// @brief compares string and JSON pointer for inequality - /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/ - template - // NOLINTNEXTLINE(readability-redundant-declaration) - friend bool operator!=(const StringType& lhs, - const json_pointer& rhs); - - /// @brief compares two JSON pointer for less-than - template - // NOLINTNEXTLINE(readability-redundant-declaration) - friend bool operator<(const json_pointer& lhs, - const json_pointer& rhs) noexcept; -#endif - - private: - /// the reference tokens - std::vector reference_tokens; -}; - -#if !JSON_HAS_THREE_WAY_COMPARISON -// functions cannot be defined inside class due to ODR violations -template -inline bool operator==(const json_pointer& lhs, - const json_pointer& rhs) noexcept -{ - return lhs.reference_tokens == rhs.reference_tokens; -} - -template::string_t> -JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) -inline bool operator==(const json_pointer& lhs, - const StringType& rhs) -{ - return lhs == json_pointer(rhs); -} - -template::string_t> -JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer)) -inline bool operator==(const StringType& lhs, - const json_pointer& rhs) -{ - return json_pointer(lhs) == rhs; -} - -template -inline bool operator!=(const json_pointer& lhs, - const json_pointer& rhs) noexcept -{ - return !(lhs == rhs); -} - -template::string_t> -JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) -inline bool operator!=(const json_pointer& lhs, - const StringType& rhs) -{ - return !(lhs == rhs); -} - -template::string_t> -JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer)) -inline bool operator!=(const StringType& lhs, - const json_pointer& rhs) -{ - return !(lhs == rhs); -} - -template -inline bool operator<(const json_pointer& lhs, - const json_pointer& rhs) noexcept -{ - return lhs.reference_tokens < rhs.reference_tokens; -} -#endif - -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/json_ref.hpp b/r5dev/thirdparty/nlohmann/detail/json_ref.hpp deleted file mode 100644 index 47911fb5..00000000 --- a/r5dev/thirdparty/nlohmann/detail/json_ref.hpp +++ /dev/null @@ -1,78 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -class json_ref -{ - public: - using value_type = BasicJsonType; - - json_ref(value_type&& value) - : owned_value(std::move(value)) - {} - - json_ref(const value_type& value) - : value_ref(&value) - {} - - json_ref(std::initializer_list init) - : owned_value(init) - {} - - template < - class... Args, - enable_if_t::value, int> = 0 > - json_ref(Args && ... args) - : owned_value(std::forward(args)...) - {} - - // class should be movable only - json_ref(json_ref&&) noexcept = default; - json_ref(const json_ref&) = delete; - json_ref& operator=(const json_ref&) = delete; - json_ref& operator=(json_ref&&) = delete; - ~json_ref() = default; - - value_type moved_or_copied() const - { - if (value_ref == nullptr) - { - return std::move(owned_value); - } - return *value_ref; - } - - value_type const& operator*() const - { - return value_ref ? *value_ref : owned_value; - } - - value_type const* operator->() const - { - return &** this; - } - - private: - mutable value_type owned_value = nullptr; - value_type const* value_ref = nullptr; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/macro_scope.hpp b/r5dev/thirdparty/nlohmann/detail/macro_scope.hpp deleted file mode 100644 index 6248bea1..00000000 --- a/r5dev/thirdparty/nlohmann/detail/macro_scope.hpp +++ /dev/null @@ -1,468 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // declval, pair -#include -#include - -// This file contains all internal macro definitions (except those affecting ABI) -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them - -#include - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) - #if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #endif -#endif - -// C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 - #endif - // the cpp 11 flag is always specified because it is the minimal required version - #define JSON_HAS_CPP_11 -#endif - -#ifdef __has_include - #if __has_include() - #include - #endif -#endif - -#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) - #ifdef JSON_HAS_CPP_17 - #if defined(__cpp_lib_filesystem) - #define JSON_HAS_FILESYSTEM 1 - #elif defined(__cpp_lib_experimental_filesystem) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif !defined(__has_include) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #endif - - // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ - #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__clang_major__) && __clang_major__ < 7 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1914 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before iOS 13 - #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before macOS Catalina - #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - #endif -#endif - -#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_FILESYSTEM - #define JSON_HAS_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_THREE_WAY_COMPARISON - #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ - && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L - #define JSON_HAS_THREE_WAY_COMPARISON 1 - #else - #define JSON_HAS_THREE_WAY_COMPARISON 0 - #endif -#endif - -#ifndef JSON_HAS_RANGES - // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error - #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 - #define JSON_HAS_RANGES 0 - #elif defined(__cpp_lib_ranges) - #define JSON_HAS_RANGES 1 - #else - #define JSON_HAS_RANGES 0 - #endif -#endif - -#ifdef JSON_HAS_CPP_17 - #define JSON_INLINE_VARIABLE inline -#else - #define JSON_INLINE_VARIABLE -#endif - -#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) - #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else - #define JSON_NO_UNIQUE_ADDRESS -#endif - -// disable documentation warnings on clang -#if defined(__clang__) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdocumentation" - #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#endif - -// allow disabling exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) - #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) - #define JSON_INTERNAL_CATCH(exception) catch(exception) -#else - #include - #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) - #define JSON_INTERNAL_CATCH(exception) if(false) -#endif - -// override exception macros -#if defined(JSON_THROW_USER) - #undef JSON_THROW - #define JSON_THROW JSON_THROW_USER -#endif -#if defined(JSON_TRY_USER) - #undef JSON_TRY - #define JSON_TRY JSON_TRY_USER -#endif -#if defined(JSON_CATCH_USER) - #undef JSON_CATCH - #define JSON_CATCH JSON_CATCH_USER - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_CATCH_USER -#endif -#if defined(JSON_INTERNAL_CATCH_USER) - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER -#endif - -// allow overriding assert -#if !defined(JSON_ASSERT) - #include // assert - #define JSON_ASSERT(x) assert(x) -#endif - -// allow to access some private functions (needed by the test suite) -#if defined(JSON_TESTS_PRIVATE) - #define JSON_PRIVATE_UNLESS_TESTED public -#else - #define JSON_PRIVATE_UNLESS_TESTED private -#endif - -/*! -@brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 -*/ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ - } - -// Ugly macros to avoid uglier copy-paste when specializing basic_json. They -// may be removed in the future once the class is split. - -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template class ObjectType, \ - template class ArrayType, \ - class StringType, class BooleanType, class NumberIntegerType, \ - class NumberUnsignedType, class NumberFloatType, \ - template class AllocatorType, \ - template class JSONSerializer, \ - class BinaryType> - -#define NLOHMANN_BASIC_JSON_TPL \ - basic_json - -// Macros to simplify conversion from/to types - -#define NLOHMANN_JSON_EXPAND( x ) x -#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME -#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ - NLOHMANN_JSON_PASTE64, \ - NLOHMANN_JSON_PASTE63, \ - NLOHMANN_JSON_PASTE62, \ - NLOHMANN_JSON_PASTE61, \ - NLOHMANN_JSON_PASTE60, \ - NLOHMANN_JSON_PASTE59, \ - NLOHMANN_JSON_PASTE58, \ - NLOHMANN_JSON_PASTE57, \ - NLOHMANN_JSON_PASTE56, \ - NLOHMANN_JSON_PASTE55, \ - NLOHMANN_JSON_PASTE54, \ - NLOHMANN_JSON_PASTE53, \ - NLOHMANN_JSON_PASTE52, \ - NLOHMANN_JSON_PASTE51, \ - NLOHMANN_JSON_PASTE50, \ - NLOHMANN_JSON_PASTE49, \ - NLOHMANN_JSON_PASTE48, \ - NLOHMANN_JSON_PASTE47, \ - NLOHMANN_JSON_PASTE46, \ - NLOHMANN_JSON_PASTE45, \ - NLOHMANN_JSON_PASTE44, \ - NLOHMANN_JSON_PASTE43, \ - NLOHMANN_JSON_PASTE42, \ - NLOHMANN_JSON_PASTE41, \ - NLOHMANN_JSON_PASTE40, \ - NLOHMANN_JSON_PASTE39, \ - NLOHMANN_JSON_PASTE38, \ - NLOHMANN_JSON_PASTE37, \ - NLOHMANN_JSON_PASTE36, \ - NLOHMANN_JSON_PASTE35, \ - NLOHMANN_JSON_PASTE34, \ - NLOHMANN_JSON_PASTE33, \ - NLOHMANN_JSON_PASTE32, \ - NLOHMANN_JSON_PASTE31, \ - NLOHMANN_JSON_PASTE30, \ - NLOHMANN_JSON_PASTE29, \ - NLOHMANN_JSON_PASTE28, \ - NLOHMANN_JSON_PASTE27, \ - NLOHMANN_JSON_PASTE26, \ - NLOHMANN_JSON_PASTE25, \ - NLOHMANN_JSON_PASTE24, \ - NLOHMANN_JSON_PASTE23, \ - NLOHMANN_JSON_PASTE22, \ - NLOHMANN_JSON_PASTE21, \ - NLOHMANN_JSON_PASTE20, \ - NLOHMANN_JSON_PASTE19, \ - NLOHMANN_JSON_PASTE18, \ - NLOHMANN_JSON_PASTE17, \ - NLOHMANN_JSON_PASTE16, \ - NLOHMANN_JSON_PASTE15, \ - NLOHMANN_JSON_PASTE14, \ - NLOHMANN_JSON_PASTE13, \ - NLOHMANN_JSON_PASTE12, \ - NLOHMANN_JSON_PASTE11, \ - NLOHMANN_JSON_PASTE10, \ - NLOHMANN_JSON_PASTE9, \ - NLOHMANN_JSON_PASTE8, \ - NLOHMANN_JSON_PASTE7, \ - NLOHMANN_JSON_PASTE6, \ - NLOHMANN_JSON_PASTE5, \ - NLOHMANN_JSON_PASTE4, \ - NLOHMANN_JSON_PASTE3, \ - NLOHMANN_JSON_PASTE2, \ - NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) -#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) -#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) -#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) -#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) -#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) -#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) -#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) -#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) -#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) -#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) -#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) -#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) -#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) -#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) -#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) -#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) -#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) -#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) -#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) -#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) -#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) -#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) -#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) -#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) -#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) -#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) -#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) -#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) -#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) -#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) -#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) -#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) -#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) -#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) -#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) -#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) -#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) -#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) -#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) -#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) -#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) -#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) -#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) -#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) -#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) -#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) -#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) -#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) -#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) -#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) -#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) -#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) -#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) -#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) -#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) -#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) -#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) -#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) -#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) -#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) -#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) -#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) - -#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; -#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_INTRUSIVE -@since version 3.9.0 -*/ -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE -@since version 3.9.0 -*/ -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - - -// inspired from https://stackoverflow.com/a/26745591 -// allows to call any std function as if (e.g. with begin): -// using std::begin; begin(x); -// -// it allows using the detected idiom to retrieve the return type -// of such an expression -#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ - namespace detail { \ - using std::std_name; \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ - } \ - \ - namespace detail2 { \ - struct std_name##_tag \ - { \ - }; \ - \ - template \ - std_name##_tag std_name(T&&...); \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ - \ - template \ - struct would_call_std_##std_name \ - { \ - static constexpr auto const value = ::nlohmann::detail:: \ - is_detected_exact::value; \ - }; \ - } /* namespace detail2 */ \ - \ - template \ - struct would_call_std_##std_name : detail2::would_call_std_##std_name \ - { \ - } - -#ifndef JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_USE_IMPLICIT_CONVERSIONS 1 -#endif - -#if JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_EXPLICIT -#else - #define JSON_EXPLICIT explicit -#endif - -#ifndef JSON_DISABLE_ENUM_SERIALIZATION - #define JSON_DISABLE_ENUM_SERIALIZATION 0 -#endif - -#ifndef JSON_USE_GLOBAL_UDLS - #define JSON_USE_GLOBAL_UDLS 1 -#endif diff --git a/r5dev/thirdparty/nlohmann/detail/macro_unscope.hpp b/r5dev/thirdparty/nlohmann/detail/macro_unscope.hpp deleted file mode 100644 index 4a871f0c..00000000 --- a/r5dev/thirdparty/nlohmann/detail/macro_unscope.hpp +++ /dev/null @@ -1,44 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -// restore clang diagnostic settings -#if defined(__clang__) - #pragma clang diagnostic pop -#endif - -// clean up -#undef JSON_ASSERT -#undef JSON_INTERNAL_CATCH -#undef JSON_THROW -#undef JSON_PRIVATE_UNLESS_TESTED -#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION -#undef NLOHMANN_BASIC_JSON_TPL -#undef JSON_EXPLICIT -#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL -#undef JSON_INLINE_VARIABLE -#undef JSON_NO_UNIQUE_ADDRESS -#undef JSON_DISABLE_ENUM_SERIALIZATION -#undef JSON_USE_GLOBAL_UDLS - -#ifndef JSON_TEST_KEEP_MACROS - #undef JSON_CATCH - #undef JSON_TRY - #undef JSON_HAS_CPP_11 - #undef JSON_HAS_CPP_14 - #undef JSON_HAS_CPP_17 - #undef JSON_HAS_CPP_20 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #undef JSON_HAS_THREE_WAY_COMPARISON - #undef JSON_HAS_RANGES - #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON -#endif - -#include diff --git a/r5dev/thirdparty/nlohmann/detail/meta/call_std/begin.hpp b/r5dev/thirdparty/nlohmann/detail/meta/call_std/begin.hpp deleted file mode 100644 index 27d36c66..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/call_std/begin.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); - -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/meta/call_std/end.hpp b/r5dev/thirdparty/nlohmann/detail/meta/call_std/end.hpp deleted file mode 100644 index d10bf833..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/call_std/end.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); - -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/meta/cpp_future.hpp b/r5dev/thirdparty/nlohmann/detail/meta/cpp_future.hpp deleted file mode 100644 index 22f25140..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/cpp_future.hpp +++ /dev/null @@ -1,171 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-FileCopyrightText: 2018 The Abseil Authors -// SPDX-License-Identifier: MIT - -#pragma once - -#include // array -#include // size_t -#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -#include // index_sequence, make_index_sequence, index_sequence_for - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -using uncvref_t = typename std::remove_cv::type>::type; - -#ifdef JSON_HAS_CPP_14 - -// the following utilities are natively available in C++14 -using std::enable_if_t; -using std::index_sequence; -using std::make_index_sequence; -using std::index_sequence_for; - -#else - -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; - -// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h -// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. - -//// START OF CODE FROM GOOGLE ABSEIL - -// integer_sequence -// -// Class template representing a compile-time integer sequence. An instantiation -// of `integer_sequence` has a sequence of integers encoded in its -// type through its template arguments (which is a common need when -// working with C++11 variadic templates). `absl::integer_sequence` is designed -// to be a drop-in replacement for C++14's `std::integer_sequence`. -// -// Example: -// -// template< class T, T... Ints > -// void user_function(integer_sequence); -// -// int main() -// { -// // user_function's `T` will be deduced to `int` and `Ints...` -// // will be deduced to `0, 1, 2, 3, 4`. -// user_function(make_integer_sequence()); -// } -template -struct integer_sequence -{ - using value_type = T; - static constexpr std::size_t size() noexcept - { - return sizeof...(Ints); - } -}; - -// index_sequence -// -// A helper template for an `integer_sequence` of `size_t`, -// `absl::index_sequence` is designed to be a drop-in replacement for C++14's -// `std::index_sequence`. -template -using index_sequence = integer_sequence; - -namespace utility_internal -{ - -template -struct Extend; - -// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. -template -struct Extend, SeqSize, 0> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; -}; - -template -struct Extend, SeqSize, 1> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; -}; - -// Recursion helper for 'make_integer_sequence'. -// 'Gen::type' is an alias for 'integer_sequence'. -template -struct Gen -{ - using type = - typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; -}; - -template -struct Gen -{ - using type = integer_sequence; -}; - -} // namespace utility_internal - -// Compile-time sequences of integers - -// make_integer_sequence -// -// This template alias is equivalent to -// `integer_sequence`, and is designed to be a drop-in -// replacement for C++14's `std::make_integer_sequence`. -template -using make_integer_sequence = typename utility_internal::Gen::type; - -// make_index_sequence -// -// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, -// and is designed to be a drop-in replacement for C++14's -// `std::make_index_sequence`. -template -using make_index_sequence = make_integer_sequence; - -// index_sequence_for -// -// Converts a typename pack into an index sequence of the same length, and -// is designed to be a drop-in replacement for C++14's -// `std::index_sequence_for()` -template -using index_sequence_for = make_index_sequence; - -//// END OF CODE FROM GOOGLE ABSEIL - -#endif - -// dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag < N - 1 > {}; -template<> struct priority_tag<0> {}; - -// taken from ranges-v3 -template -struct static_const -{ - static JSON_INLINE_VARIABLE constexpr T value{}; -}; - -#ifndef JSON_HAS_CPP_17 - template - constexpr T static_const::value; -#endif - -template -inline constexpr std::array make_array(Args&& ... args) -{ - return std::array {{static_cast(std::forward(args))...}}; -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/meta/detected.hpp b/r5dev/thirdparty/nlohmann/detail/meta/detected.hpp deleted file mode 100644 index b2f6db9f..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/detected.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -// https://en.cppreference.com/w/cpp/experimental/is_detected -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; - -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; - -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; - -template class Op, class... Args> -using is_detected = typename detector::value_t; - -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; - -template class Op, class... Args> -using detected_t = typename detector::type; - -template class Op, class... Args> -using detected_or = detector; - -template class Op, class... Args> -using detected_or_t = typename detected_or::type; - -template class Op, class... Args> -using is_detected_exact = std::is_same>; - -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/meta/identity_tag.hpp b/r5dev/thirdparty/nlohmann/detail/meta/identity_tag.hpp deleted file mode 100644 index 71164f28..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/identity_tag.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -// dispatching helper struct -template struct identity_tag {}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/meta/is_sax.hpp b/r5dev/thirdparty/nlohmann/detail/meta/is_sax.hpp deleted file mode 100644 index 21500896..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/is_sax.hpp +++ /dev/null @@ -1,159 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // size_t -#include // declval -#include // string - -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -using null_function_t = decltype(std::declval().null()); - -template -using boolean_function_t = - decltype(std::declval().boolean(std::declval())); - -template -using number_integer_function_t = - decltype(std::declval().number_integer(std::declval())); - -template -using number_unsigned_function_t = - decltype(std::declval().number_unsigned(std::declval())); - -template -using number_float_function_t = decltype(std::declval().number_float( - std::declval(), std::declval())); - -template -using string_function_t = - decltype(std::declval().string(std::declval())); - -template -using binary_function_t = - decltype(std::declval().binary(std::declval())); - -template -using start_object_function_t = - decltype(std::declval().start_object(std::declval())); - -template -using key_function_t = - decltype(std::declval().key(std::declval())); - -template -using end_object_function_t = decltype(std::declval().end_object()); - -template -using start_array_function_t = - decltype(std::declval().start_array(std::declval())); - -template -using end_array_function_t = decltype(std::declval().end_array()); - -template -using parse_error_function_t = decltype(std::declval().parse_error( - std::declval(), std::declval(), - std::declval())); - -template -struct is_sax -{ - private: - static_assert(is_basic_json::value, - "BasicJsonType must be of type basic_json<...>"); - - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - using exception_t = typename BasicJsonType::exception; - - public: - static constexpr bool value = - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value && - is_detected_exact::value; -}; - -template -struct is_sax_static_asserts -{ - private: - static_assert(is_basic_json::value, - "BasicJsonType must be of type basic_json<...>"); - - using number_integer_t = typename BasicJsonType::number_integer_t; - using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - using number_float_t = typename BasicJsonType::number_float_t; - using string_t = typename BasicJsonType::string_t; - using binary_t = typename BasicJsonType::binary_t; - using exception_t = typename BasicJsonType::exception; - - public: - static_assert(is_detected_exact::value, - "Missing/invalid function: bool null()"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool boolean(bool)"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool boolean(bool)"); - static_assert( - is_detected_exact::value, - "Missing/invalid function: bool number_integer(number_integer_t)"); - static_assert( - is_detected_exact::value, - "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); - static_assert( - is_detected_exact::value, - "Missing/invalid function: bool string(string_t&)"); - static_assert( - is_detected_exact::value, - "Missing/invalid function: bool binary(binary_t&)"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool start_object(std::size_t)"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool key(string_t&)"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool end_object()"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool start_array(std::size_t)"); - static_assert(is_detected_exact::value, - "Missing/invalid function: bool end_array()"); - static_assert( - is_detected_exact::value, - "Missing/invalid function: bool parse_error(std::size_t, const " - "std::string&, const exception&)"); -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END diff --git a/r5dev/thirdparty/nlohmann/detail/meta/std_fs.hpp b/r5dev/thirdparty/nlohmann/detail/meta/std_fs.hpp deleted file mode 100644 index c0961580..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/std_fs.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include - -#if JSON_HAS_EXPERIMENTAL_FILESYSTEM -#include -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ -namespace std_fs = std::experimental::filesystem; -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END -#elif JSON_HAS_FILESYSTEM -#include -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ -namespace std_fs = std::filesystem; -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END -#endif diff --git a/r5dev/thirdparty/nlohmann/detail/meta/type_traits.hpp b/r5dev/thirdparty/nlohmann/detail/meta/type_traits.hpp deleted file mode 100644 index cfc7e5ad..00000000 --- a/r5dev/thirdparty/nlohmann/detail/meta/type_traits.hpp +++ /dev/null @@ -1,740 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.11.2 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann -// SPDX-License-Identifier: MIT - -#pragma once - -#include // numeric_limits -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval -#include // tuple - -#include -#include -#include -#include -#include -#include -#include - -NLOHMANN_JSON_NAMESPACE_BEGIN -/*! -@brief detail namespace with internal helper functions - -This namespace collects functions that should not be exposed, -implementations of some @ref basic_json methods, and meta-programming helpers. - -@since version 2.1.0 -*/ -namespace detail -{ - -///////////// -// helpers // -///////////// - -// Note to maintainers: -// -// Every trait in this file expects a non CV-qualified type. -// The only exceptions are in the 'aliases for detected' section -// (i.e. those of the form: decltype(T::member_function(std::declval()))) -// -// In this case, T has to be properly CV-qualified to constraint the function arguments -// (e.g. to_json(BasicJsonType&, const T&)) - -template struct is_basic_json : std::false_type {}; - -NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct is_basic_json : std::true_type {}; - -// used by exceptions create() member functions -// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t -// false_type otherwise -template -struct is_basic_json_context : - std::integral_constant < bool, - is_basic_json::type>::type>::value - || std::is_same::value > -{}; - -////////////////////// -// json_ref helpers // -////////////////////// - -template -class json_ref; - -template -struct is_json_ref : std::false_type {}; - -template -struct is_json_ref> : std::true_type {}; - -////////////////////////// -// aliases for detected // -////////////////////////// - -template -using mapped_type_t = typename T::mapped_type; - -template -using key_type_t = typename T::key_type; - -template -using value_type_t = typename T::value_type; - -template -using difference_type_t = typename T::difference_type; - -template -using pointer_t = typename T::pointer; - -template -using reference_t = typename T::reference; - -template -using iterator_category_t = typename T::iterator_category; - -template -using to_json_function = decltype(T::to_json(std::declval()...)); - -template -using from_json_function = decltype(T::from_json(std::declval()...)); - -template -using get_template_function = decltype(std::declval().template get()); - -// trait checking if JSONSerializer::from_json(json const&, udt&) exists -template -struct has_from_json : std::false_type {}; - -// trait checking if j.get is valid -// use this trait instead of std::is_constructible or std::is_convertible, -// both rely on, or make use of implicit conversions, and thus fail when T -// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) -template -struct is_getable -{ - static constexpr bool value = is_detected::value; -}; - -template -struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if JSONSerializer::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template -struct has_non_default_from_json : std::false_type {}; - -template -struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if BasicJsonType::json_serializer::to_json exists -// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template -struct has_to_json : std::false_type {}; - -template -struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -template -using detect_key_compare = typename T::key_compare; - -template -struct has_key_compare : std::integral_constant::value> {}; - -// obtains the actual object key comparator -template -struct actual_object_comparator -{ - using object_t = typename BasicJsonType::object_t; - using object_comparator_t = typename BasicJsonType::default_object_comparator_t; - using type = typename std::conditional < has_key_compare::value, - typename object_t::key_compare, object_comparator_t>::type; -}; - -template -using actual_object_comparator_t = typename actual_object_comparator::type; - -/////////////////// -// is_ functions // -/////////////////// - -// https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B { }; -template -struct conjunction -: std::conditional(B::value), conjunction, B>::type {}; - -// https://en.cppreference.com/w/cpp/types/negation -template struct negation : std::integral_constant < bool, !B::value > { }; - -// Reimplementation of is_constructible and is_default_constructible, due to them being broken for -// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). -// This causes compile errors in e.g. clang 3.5 or gcc 4.9. -template -struct is_default_constructible : std::is_default_constructible {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - - -template -struct is_constructible : std::is_constructible {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - - -template -struct is_iterator_traits : std::false_type {}; - -template -struct is_iterator_traits> -{ - private: - using traits = iterator_traits; - - public: - static constexpr auto value = - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value; -}; - -template -struct is_range -{ - private: - using t_ref = typename std::add_lvalue_reference::type; - - using iterator = detected_t; - using sentinel = detected_t; - - // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator - // and https://en.cppreference.com/w/cpp/iterator/sentinel_for - // but reimplementing these would be too much work, as a lot of other concepts are used underneath - static constexpr auto is_iterator_begin = - is_iterator_traits>::value; - - public: - static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; -}; - -template -using iterator_t = enable_if_t::value, result_of_begin())>>; - -template -using range_value_t = value_type_t>>; - -// The following implementation of is_complete_type is taken from -// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ -// and is written by Xiang Fan who agreed to using it in this library. - -template -struct is_complete_type : std::false_type {}; - -template -struct is_complete_type : std::true_type {}; - -template -struct is_compatible_object_type_impl : std::false_type {}; - -template -struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - // macOS's is_constructible does not play well with nonesuch... - static constexpr bool value = - is_constructible::value && - is_constructible::value; -}; - -template -struct is_compatible_object_type - : is_compatible_object_type_impl {}; - -template -struct is_constructible_object_type_impl : std::false_type {}; - -template -struct is_constructible_object_type_impl < - BasicJsonType, ConstructibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - static constexpr bool value = - (is_default_constructible::value && - (std::is_move_assignable::value || - std::is_copy_assignable::value) && - (is_constructible::value && - std::is_same < - typename object_t::mapped_type, - typename ConstructibleObjectType::mapped_type >::value)) || - (has_from_json::value || - has_non_default_from_json < - BasicJsonType, - typename ConstructibleObjectType::mapped_type >::value); -}; - -template -struct is_constructible_object_type - : is_constructible_object_type_impl {}; - -template -struct is_compatible_string_type -{ - static constexpr auto value = - is_constructible::value; -}; - -template -struct is_constructible_string_type -{ - // launder type through decltype() to fix compilation failure on ICPC -#ifdef __INTEL_COMPILER - using laundered_type = decltype(std::declval()); -#else - using laundered_type = ConstructibleStringType; -#endif - - static constexpr auto value = - conjunction < - is_constructible, - is_detected_exact>::value; -}; - -template -struct is_compatible_array_type_impl : std::false_type {}; - -template -struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < - is_detected::value&& - is_iterator_traits>>::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 - !std::is_same>::value >> -{ - static constexpr bool value = - is_constructible>::value; -}; - -template -struct is_compatible_array_type - : is_compatible_array_type_impl {}; - -template -struct is_constructible_array_type_impl : std::false_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t::value >> - : std::true_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t < !std::is_same::value&& - !is_compatible_string_type::value&& - is_default_constructible::value&& -(std::is_move_assignable::value || - std::is_copy_assignable::value)&& -is_detected::value&& -is_iterator_traits>>::value&& -is_detected::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 -!std::is_same>::value&& - is_complete_type < - detected_t>::value >> -{ - using value_type = range_value_t; - - static constexpr bool value = - std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, - value_type >::value; -}; - -template -struct is_constructible_array_type - : is_constructible_array_type_impl {}; - -template -struct is_compatible_integer_type_impl : std::false_type {}; - -template -struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& - !std::is_same::value >> -{ - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; - - static constexpr auto value = - is_constructible::value && - CompatibleLimits::is_integer && - RealLimits::is_signed == CompatibleLimits::is_signed; -}; - -template -struct is_compatible_integer_type - : is_compatible_integer_type_impl {}; - -template -struct is_compatible_type_impl: std::false_type {}; - -template -struct is_compatible_type_impl < - BasicJsonType, CompatibleType, - enable_if_t::value >> -{ - static constexpr bool value = - has_to_json::value; -}; - -template -struct is_compatible_type - : is_compatible_type_impl {}; - -template -struct is_constructible_tuple : std::false_type {}; - -template -struct is_constructible_tuple> : conjunction...> {}; - -template -struct is_json_iterator_of : std::false_type {}; - -template -struct is_json_iterator_of : std::true_type {}; - -template -struct is_json_iterator_of : std::true_type -{}; - -// checks if a given type T is a template specialization of Primary -template